[
  {
    "path": ".clang-format",
    "content": "---\n# We'll use defaults from the Google style.\n# See http://clang.llvm.org/docs/ClangFormat.html for help.\n#\n# We include the full configuration to avoid changes with our code\n# formatting when there are changes added in clang-format or changes\n# in Google's style\nLanguage:        Cpp\n\n# BasedOnStyle:  Google\nAccessModifierOffset: -1\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Left\nAlignOperands:   true\nAlignTrailingComments: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: All\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: true\nAlwaysBreakTemplateDeclarations: true\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping:\n  AfterClass:      false\n  AfterControlStatement: false\n  AfterEnum:       false\n  AfterFunction:   false\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     false\n  AfterUnion:      false\n  BeforeCatch:     false\n  BeforeElse:      false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: Attach\nBreakBeforeInheritanceComma: false\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:     80\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDisableFormat:   false\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nForEachMacros:\n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\nIncludeCategories:\n  - Regex:           '^<.*'\n    Priority:        1\n  - Regex:           '.*'\n    Priority:        2\nIncludeIsMainRegex: '([-_](test|unittest))?$'\nIndentCaseLabels: true\nIndentWidth:     2\nIndentWrappedFunctionNames: false\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLinesAtTheStartOfBlocks: false\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBlockIndentWidth: 2\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: false\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 1\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 200\nReflowComments:  true\nSortIncludes:    true\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: false\nSpaceAfterTemplateKeyword: true\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeParens: ControlStatements\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 2\nSpacesInAngles:  false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nStandard:        Auto\nTabWidth:        8\nUseTab:          Never\n\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nPointerAlignment: Left\nDerivePointerAlignment: false\n...\n"
  },
  {
    "path": ".clang-tidy",
    "content": "# Define the checks we want to use and remove with comments as to why.\n#\n# Here is a list of the quirks with clang-tidy file format:\n# - no apostrophes in the comments\n# - every comment line in the checks must end with a comma\n# - every line in the checks must start at the beginning of the line\nChecks: '*,\n-abseil-*,\n-altera-*,\n-android-*,\n-bugprone-easily-swappable-parameters,\n-bugprone-exception-escape,\n# complains about Catch macros having infinite loops,\n-bugprone-infinite-loop,\n# false positive: Catch2 CHECK(a == b) expands to Decomposer() <= a == b,\n# which triggers the chained comparison check on macro-generated code.,\n-bugprone-chained-comparison,\n-bugprone-macro-parentheses,\n# useless as it triggers on calls to value() which already do a check,\n# and is too aggressive as it requires the check to be immediately before the\n# access.\n-bugprone-unchecked-optional-access,\n# Official justification is not public, unofficial justification has nothing,\n# specific to iterators and is basically \"never create mutable temporaries\",,\n# which is against standard practice everywhere else.,\n-cert-dcl21-cpp,\n# many static variables we use do not throw and if they do we,\n# want to terminate anyway,\n-cert-err58-cpp,\n-cert-msc51-cpp,\n-cert-msc32-c,\n# checks for incorrectly implemented self-assignment,\n# checks.  However, it is broken.,\n-cert-oop54-cpp,\n-cppcoreguidelines-avoid-c-arrays,\n-cppcoreguidelines-avoid-do-while,\n# too many inconvenient positives for us to deal with,\n-cppcoreguidelines-avoid-magic-numbers,\n# false positives,\n-cppcoreguidelines-c-copy-assignment-signature,\n# sometimes macros are the right answer.,\n-cppcoreguidelines-macro-usage,\n# public and protected member variables are fine,\n-cppcoreguidelines-non-private-member-variables-in-classes,\n# We do not use gsl::owner,\n-cppcoreguidelines-owning-memory,\n-fuchsia-*,\n# defaulting virtual functions in CoordinateMap,\n-google-default-arguments,\n# Documented as redundant with -Wold-style-cast.  Has more false positives.,\n-google-readability-casting,\n# specifying int32_t and int64_t instead of just int,\n-google-runtime-int,\n# redundant with other checks,\n-hicpp-*,\n# We use pragma once instead of include guards,\n-llvm-header-guard,\n# Makes code less portable because some implementation-defined STL,\n# types can be pointers or not.  Same as,\n# readability-qualified-auto below,\n-llvm-qualified-auto,\n# We are not developing LLVM libc,\n-llvmlibc-*,\n# anonymous namespaces are the idiomatic C++ way to restrict visibility;,\n# conflicts with misc-use-anonymous-namespace which prefers anonymous namespaces.,\n-llvm-prefer-static-over-anonymous-namespace,\n# Triggers when \"l\" is used because it is \"too similar\" to \"I\",\n-misc-confusable-identifiers,\n# thinks constexpr variables in header files cause ODR violations,\n-misc-definitions-in-headers,\n# Insufficiently configurable, does not handle system headers well,\n-misc-include-cleaner,\n# false positives,\n-misc-noexcept-move-constructor,\n-misc-non-private-member-variables-in-classes,\n# false positives,\n-misc-unconventional-assign-operator,\n-modernize-raw-string-literal,\n# should be used, but requires possibly a lot of code changes that,\n# we do not have the resources for,\n-modernize-use-nodiscard,\n# this wants everything to use trailing return type syntax, which,\n# is silly.,\n-modernize-use-trailing-return-type,\n-performance-noexcept-move-constructor,\n# complains about decltype(auto),\n-readability-const-return-type,\n# style choice, discussed in issue #145,\n-readability-else-after-return,\n-llvm-else-after-return,\n# flagged code that is not very complex,\n-readability-function-cognitive-complexity,\n# We have lots of \"x\", \"p\", & \"os\" variable names,\n-readability-identifier-length,\n-readability-magic-numbers,\n-misc-confusable-identifiers\n# Same as llvm-qualified-auto above.,\n-readability-qualified-auto,\n# Access specifiers can be useful to structure code,\n-readability-redundant-access-specifiers,\n# We can have two of: methods are static when possible, static,\n# methods are not called through instances, and methods of,\n# calling, e.g., x.size(), are consistent across classes.  We,\n# choose to lose this one.,\n-readability-static-accessed-through-instance,\n# we are okay with lower case,\n-readability-uppercase-literal-suffix,'\nCheckOptions:\n  - key: cppcoreguidelines-avoid-do-while.IgnoreMacros\n    value: true\n  - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions\n    value: true\n  - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor\n    value: true\n  - key: performance-move-const-arg.CheckTriviallyCopyableMove\n    value: false\n  # The fix for this is not supported by GCC 9.\n  - key: modernize-loop-convert.UseCxx20ReverseRanges\n    value: false\nWarningsAsErrors: '*'\n# It is unclear if the header filter actually works or how to use it so\n# just include all headers\nHeaderFilterRegex: ''\nFormatStyle:     none\n"
  },
  {
    "path": ".claude/hooks/PostFormat.sh",
    "content": "#!/usr/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Claude Code PostToolUse hook: auto-format files after Edit/Write.\n#\n# - C++ (.cpp, .hpp, .tpp):\n#     Edit  -> git-clang-format -f (diff-only formatting)\n#     Write -> clang-format -i    (whole-file formatting)\n# - Python (.py):\n#     black -q && isort -q        (single-file formatting)\n\n# Returns the path to the best available git-clang-format binary.\n# Prefers the unversioned `git-clang-format`, then falls back to the\n# highest-versioned `git-clang-format-N` found in PATH.\nfind_git_clang_format() {\n  if command -v git-clang-format &>/dev/null; then\n    echo \"git-clang-format\"\n    return\n  fi\n  local best_ver=0 best_bin=\"\"\n  IFS=: read -ra dirs <<< \"$PATH\"\n  for dir in \"${dirs[@]}\"; do\n    for bin in \"$dir\"/git-clang-format-*; do\n      [[ -x \"$bin\" ]] || continue\n      local ver=\"${bin##*-}\"\n      [[ \"$ver\" =~ ^[0-9]+$ ]] || continue\n      (( ver > best_ver )) && { best_ver=$ver; best_bin=$bin; }\n    done\n  done\n  echo \"$best_bin\"\n}\n\nINPUT=$(cat)\nFILE=$(echo \"$INPUT\" | jq -r '.tool_input.file_path // empty')\nTOOL=$(echo \"$INPUT\" | jq -r '.tool_name // empty')\n\nif [[ -z \"$FILE\" || ! -f \"$FILE\" ]]; then\n  exit 0\nfi\n\ncase \"$FILE\" in\n  *.cpp|*.hpp|*.tpp)\n    if [[ \"$TOOL\" == \"Write\" ]]; then\n      clang-format -i \"$FILE\" 2>/dev/null\n    else\n      gcf=$(find_git_clang_format)\n      if [[ -n \"$gcf\" ]]; then\n        \"$gcf\" -f -- \"$FILE\" 2>/dev/null\n      else\n        clang-format -i \"$FILE\" 2>/dev/null\n      fi\n    fi\n    ;;\n  *.py)\n    black -q \"$FILE\" 2>/dev/null\n    isort -q \"$FILE\" 2>/dev/null\n    ;;\nesac\n\nexit 0\n"
  },
  {
    "path": ".claude/rules/CMake.md",
    "content": "---\ntriggers:\n  - glob: \"**/CMakeLists.txt\"\n---\n\n- Files listed in spectre_target_sources() and spectre_target_headers() must be\n  in alphabetical order (case-insensitive). When adding or removing entries,\n  maintain the sorted order.\n- Use ${LIBRARY} variable in CMake target calls, not hardcoded library names.\n"
  },
  {
    "path": ".claude/rules/Cxx.md",
    "content": "---\ntriggers:\n  - glob: \"**/*.{hpp,cpp,tpp}\"\n---\n\n# SpECTRE Code Rules Reference\n\n## Banned Patterns\n- `#include <iostream>` -> `Parallel/Printf/Printf.hpp` with `Parallel::printf`\n- `#include <lrtslock.h>` -> `#include <converse.h>`\n- `std::enable_if` -> C++20 `requires` clause\n- `||` `&&` `!` (logical operators) -> `or` `and` `not` (C++ alternative\n  tokens). Note: `!=` is fine.\n- `.ckLocal()` -> `Parallel::local(proxy)`\n- `.ckLocalBranch()` -> `Parallel::local_branch(proxy)`\n- `return Py_None;` -> `Py_RETURN_NONE`\n- `TEST_CASE(` -> `SPECTRE_TEST_CASE(`\n- `Approx(` in tests -> `approx` from `Framework/TestingFramework.hpp`\n- `namespace _details` / `namespace details` -> `_detail` / `detail`\n- `boost::optional` / `boost/optional.hpp` -> `std::optional`\n- `mutable` member variables -> avoid (race conditions in parallel); discuss\n  with core devs\n- `struct TD;` / `class TD;` -> remove (debug artifacts)\n- `Ls` as abbreviation -> use `List`\n- `int` for container sizes/indices -> `size_t`\n- `noexcept` -> don't add unless overriding a signature that requires it\n\n## Prefer-Library Patterns\nWhen you see manual loops over Tensor indices, suggest the existing utility:\n- Dot product -> `DataStructures/Tensor/EagerMath/DotProduct.hpp`\n- Cross product -> `EagerMath/CrossProduct.hpp`\n- Trace -> `EagerMath/Trace.hpp`\n- Determinant -> `EagerMath/Determinant.hpp` or `DeterminantAndInverse.hpp`\n- Magnitude/norm -> `EagerMath/Magnitude.hpp` or `Norms.hpp`\n- Outer product -> `EagerMath/OuterProduct.hpp`\n- Raise/lower index -> `EagerMath/RaiseOrLowerIndex.hpp`\n- Gram-Schmidt -> `EagerMath/GramSchmidtOrthonormalize.hpp`\n- Frame transform -> `EagerMath/FrameTransform.hpp`\n- Cartesian<->Spherical -> `EagerMath/CartesianToSpherical.hpp`\n\nIf you spot a tensor loop not covered above, grep\n`src/DataStructures/Tensor/EagerMath/` and `src/NumericalAlgorithms/` for an\nexisting utility before suggesting the author write their own. Encourage use\nof tensor expressions `src/DataStructures/Tensor/Expressions/`.\n\nFor general relativity, generalized harmonic, CCZ4, apparent horizons, and\nGRMHD, many manipulations like shift from spacetime metric, lapse from spacetime\nmetric, derivative of shift, etc. are implemented as functions in\n`src/PointwiseFunctions/GeneralRelativity` or `src/PointwiseFunctions/Hydro`.\n\n## Style Rules\n- **Naming**: CamelCase for classes, template params, files, dirs. snake_case\n  for functions, variables. SCREAMING_SNAKE_CASE for macros. Trailing `_` on\n  private members. Unused params: `/*name*/`.\n- **Names**: Use full, descriptive names (e.g., `block_id` not `blk_id`).\n- **Almost always `auto`** except expression templates (e.g. `DataVector`)\n- **Braces on all loops and if/else** (no braceless one-liners)\n- **Return by value** preferred. Mutable out-params: `gsl::not_null<T*>` (listed\n  first in arg list).\n- **`std::move`** into members from by-value constructor parameters\n- **`get<a,b>(tensor)`** when indices are compile-time known\n- **Explicit double literals**: `2.0` or `2.`, never bare `2` in floating-point\n  context\n- **`#pragma once`** for header guards (not `#ifndef`)\n- **`override`** keyword on all virtual overrides\n- **`const`** on all immutable objects\n- **No top-level `const` on value parameters in declarations**: In `.hpp`\n  forward declarations, don't mark value parameters (including pointer-by-value)\n  as `const`. `const` is fine in the corresponding definition. Example:\n  `void foo(int x, double* ptr);` (declaration) vs\n  `void foo(const int x, double* const ptr) { ... }` (definition).\n- **Templates**: prefer definitions in `.cpp` with explicit instantiations\n  (`GENERATE_EXPLICIT_INSTANTIATIONS`)\n- **Internal namespaces**: `LibraryOrFile_detail` (e.g. `Tensor_detail`)\n- **Macros**: avoid if possible; use `constexpr` or templates. Macro vars: add\n  suffix to avoid collisions.\n- **Error messages**: descriptive, include runtime values. Bad: \"Size\n  mismatch\". Good: \"The number of grid points in matrix 'F' (N) != determinant\n  grid points (M).\"\n- **Header order**: (1) TestingFramework.hpp + blank (tests), (2) corresponding\n  .hpp + blank (.cpp files), (3) STL/external `<>` alphabetical, (4) blank, (5)\n  SpECTRE `\"\"` alphabetical\n- **Doxygen**: required for all public API in `.hpp`. Use `///` or `/*!`. NO\n  doxygen in `.cpp`. Use `\\f$...\\f$` for inline math, `align` env (not\n  `eqnarray`) for multi-line. Blank doxygen line before/after out-of-line\n  equations.\n- **CMake lists**: alphabetical. Use `${LIBRARY}` variable, not hardcoded names.\n\n## Test Requirements\n- Files: `tests/Unit/<mirrors_src>/Test_<SourceFile>.cpp`\n- Include order: `\"Framework/TestingFramework.hpp\"`, blank line,\n  system/external includes, blank line, spectre includes.\n- All helper classes/functions in anonymous `namespace {}`\n- Test macro: `SPECTRE_TEST_CASE(\"Unit.Category.Name\", \"[Unit][Category]\")`\n- Floating-point: `CHECK_ITERABLE_APPROX` (not `Approx`)\n- Completion: < 5 seconds (prefer < 0.5s)\n- Random values: `MAKE_GENERATOR(gen)`, test 10^4 times for tolerance\n- Error tests: `CHECK_THROWS_WITH` inside `#ifdef SPECTRE_DEBUG`\n- Pointwise functions: test with analytic solution AND random-value comparison\n  via `pypp::check_with_random_values()`\n- Use metamorphic tests: like `sin^2(x)+cos^2(x)=1` or that a spacetime vector\n  in general relativity that should be null is actually null. I.e, test\n  identities.\n- Name tests after the component and behavior (e.g.,\n  `Test_ApparentHorizonFinder`).\n- Prefer a single `SPECTRE_TEST_CASE` that calls several anonymous-namespace\n  helper functions over many small `SPECTRE_TEST_CASE`s.\n- Increase test timeouts sparingly. If necessary, use `// [[TimeOut, SECONDS]]`\n  on the line before `SPECTRE_TEST_CASE`. Default timeout is 2 seconds.\n"
  },
  {
    "path": ".claude/settings.json",
    "content": "{\n  \"permissions\": {\n    \"allow\": [\n      \"Skill(spectre-fix-ci)\",\n      \"Skill(spectre-fix-issue)\",\n      \"Skill(spectre-fix-pr)\",\n      \"Skill(spectre-review)\"\n    ]\n  },\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Edit|Update|Write\",\n        \"hooks\": [\n          {\n            \"type\": \"command\",\n            \"command\": \"$CLAUDE_PROJECT_DIR/.claude/hooks/PostFormat.sh\"\n          }\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": ".claude/skills/spectre-fix-ci/SKILL.md",
    "content": "---\nname: spectre-fix-ci\ndescription: >\n  Fetch and analyze a failed GitHub Actions job log. When viewing a job\n  in GitHub Actions, the job ID is the last number in the URL, e.g. in\n  \"runs/24319442407/job/71002790711\" the job ID is \"71002790711\".\nallowed-tools: [\"Bash\"]\nargument-hint: \"JOB_ID [--owner OWNER]\"\nuser-invocable: true\nmodel-invocable: false\n---\n\nRead and follow instructions in file\n`$(git rev-parse --show-toplevel)/.skills/spectre-fix-ci/SKILL.md`\n"
  },
  {
    "path": ".claude/skills/spectre-fix-issue/SKILL.md",
    "content": "---\nname: spectre-fix-issue\ndescription: >\n  Fetch a GitHub issue by number. Trigger when the user references a GitHub\n  issue (e.g. \"fix issue #1727\", \"work on #1727\").\nallowed-tools: [\"Bash\", \"Read\"]\nargument-hint: \"ISSUE_NUMBER [--repo OWNER/REPO] [--full]\"\n---\n\nRead and follow instructions in file\n`$(git rev-parse --show-toplevel)/.skills/spectre-fix-issue/SKILL.md`\n"
  },
  {
    "path": ".claude/skills/spectre-fix-pr/SKILL.md",
    "content": "---\nname: spectre-fix-pr\ndescription: >\n  Fetch PR review comments. Trigger when users reference a PR\n  (e.g. \"address comments on PR #1234\", \"work on PR #1234 reviews\").\nallowed-tools: [\"Bash\", \"Read\"]\nargument-hint: \"PR_NUMBER [--repo OWNER/REPO]\"\n---\n\nRead and follow instructions in file\n`$(git rev-parse --show-toplevel)/.skills/spectre-fix-pr/SKILL.md`\n"
  },
  {
    "path": ".claude/skills/spectre-review/SKILL.md",
    "content": "---\nname: spectre-review\ndescription: SpECTRE code review (PR or local commits)\nargument-hint: \"[PR#] [clang-tidy] [coverage]\"\nallowed-tools: [\"Bash\", \"Read\", \"Grep\", \"Glob\", \"Agent\", \"AskUserQuestion\", \"TaskCreate\", \"TaskUpdate\", \"TaskList\"]\ndisable-model-invocation: true\n---\n\nRead and follow instructions in file\n`$(git rev-parse --show-toplevel)/.skills/spectre-review/SKILL.md`\n"
  },
  {
    "path": ".codecov.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Disable PR comments\ncomment: false\n\ncoverage:\n  status:\n    # Check that PRs increase overall coverage\n    project:\n      default:\n        target: auto\n        # Account for random-value tests sometimes taking different code paths\n        threshold: 0.3%\n    # Check that PRs are fully tested\n    patch:\n      default:\n        target: 100%\n"
  },
  {
    "path": ".devcontainer/cmake-kits.json",
    "content": "[\n    {\n        \"name\": \"Default\",\n        \"compilers\": {\n            \"C\": \"clang\",\n            \"CXX\": \"clang++\",\n            \"Fortran\": \"gfortran\"\n        },\n        \"cmakeSettings\": {\n            \"BUILD_SHARED_LIBS\": \"ON\",\n            \"MEMORY_ALLOCATOR\": \"SYSTEM\"\n        },\n        \"preferredGenerator\": {\n            \"name\": \"Unix Makefiles\"\n        }\n    }\n]\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n    \"name\": \"SpECTRE development environment\",\n    \"image\": \"sxscollaboration/spectre:dev\",\n    \"workspaceMount\": \"source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind,consistency=delegated\",\n    \"workspaceFolder\": \"${localWorkspaceFolder}\",\n    \"customizations\": {\n        \"vscode\": {\n            \"extensions\": [\n                \"eamodio.gitlens\",\n                \"foxundermoon.shell-format\",\n                \"github.vscode-pull-request-github\",\n                \"gruntfuggly.format-modified\",\n                \"ms-python.black-formatter\",\n                \"ms-python.python\",\n                \"ms-vscode.cmake-tools\",\n                \"ms-vscode.cpptools\",\n                \"ms-vsliveshare.vsliveshare\",\n                \"redhat.vscode-yaml\",\n                \"stkb.rewrap\",\n                \"streetsidesoftware.code-spell-checker\",\n                \"twxs.cmake\"\n            ],\n            \"settings\": {\n                \"cmake.buildDirectory\": \"${localWorkspaceFolder}/build-${buildKit}-${buildType}\",\n                \"editor.rulers\": [80],\n                \"editor.wordWrap\": \"off\",\n                \"editor.formatOnSave\": false,\n                \"editor.formatOnPaste\": true,\n                \"editor.formatOnType\": true,\n                \"python.pythonPath\": \"/usr/bin/python3\",\n                \"[python]\": {\n                    \"editor.defaultFormatter\": \"ms-python.black-formatter\"\n                }\n            }\n        }\n    },\n    \"remoteEnv\": {\n        \"SPECTRE_HOME\": \"${localWorkspaceFolder}\"\n    },\n    \"postCreateCommand\": \"git config --global --add safe.directory ${localWorkspaceFolder} && mkdir -p /root/.local/share/CMakeTools && cp ${localWorkspaceFolder}/.devcontainer/cmake-kits.json /root/.local/share/CMakeTools/cmake-tools-kits.json\"\n}\n"
  },
  {
    "path": ".dockerignore",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Build directories\n# Ignore build dirs named similarly to the example in the installation notes;\n# in general arbitrary names are possible and cannot all be ignored. This will\n# keep the size of the context down and prevent errors in the demo image where\n# we create our own `build` directory in the container\nbuild*\ncontainers/Dockerfile.buildenv\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "# Bug reports:\n\n### Expected behavior:\n\n<!--\ndescribe the expected behavior\n-->\n\n### Current behavior:\n\n<!--\ndescribe the current behavior and how to reproduce\n-->\n\n### Environment:\n\nAdd as an attachment `$SPECTRE_BUILD_DIR/BuildInfo.txt` or\nadd its contents here.\n\n# Feature request:\n\n### Component:\n\n- [ ] Code\n- [ ] Documentation\n- [ ] Build system\n- [ ] Continuous integration\n\n### Desired feature:\n\n- [ ] Detail 1\n- [ ] Detail 2\n- [ ] Detail 3\n\n### Detailed discussion:\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "## Proposed changes\n\n<!--\nAt a high level, describe what this PR does.\n-->\n\n### Upgrade instructions\n\n<!--\nIf this PR makes changes that other people should be aware of when upgrading\ntheir code, describe what they should do between the two UPGRADE INSTRUCTIONS\nlines below.\n-->\n<!-- UPGRADE INSTRUCTIONS -->\n\n<!-- UPGRADE INSTRUCTIONS -->\n\n### Code review checklist\n\n- [ ] The code is documented and the documentation renders correctly. Run\n  `make doc` to generate the documentation locally into `BUILD_DIR/docs/html`.\n  Then open `index.html`.\n- [ ] The code follows the stylistic and code quality guidelines listed in the\n  [code review guide](https://spectre-code.org/code_review_guide.html).\n- [ ] The PR lists upgrade instructions and is labeled `bugfix` or\n  `new feature` if appropriate.\n- [ ] If a coding agent is used, have one of\n      \"Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>\",\n      \"Co-Authored-by: Codex <noreply@openai.com>\", or\n      \"Co-Authored-By: GitHub Copilot CLI <noreply@microsoft.com>\"\n      as the last line of the commit, depending on the agent.\n\n### Further comments\n\n<!--\nIf this is a relatively large or complex change, kick off the discussion by\nexplaining why you chose the solution you did and what alternatives you\nconsidered, etc...\n-->\n"
  },
  {
    "path": ".github/actions/parse-compiler/action.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nname: Parse compiler\n\ndescription: |\n  Parse the compiler name and version from the input and set the environment\n  variables CC, CXX, FC, COMPILER_ID, and COMPILER_VERSION.\n  For nvcc, it sets CC, CXX, and FC to the host compilers (gcc, g++, gfortran).\n\ninputs:\n  compiler:\n    description: Compiler to parse, e.g. 'gcc-10'\n    required: true\n\nruns:\n  using: \"composite\"\n  steps:\n    - run: |\n        if [[ ${{ inputs.compiler }} =~ (gcc|clang|nvcc)-([0-9\\.\\-]+) ]]; then\n          COMPILER_ID=${BASH_REMATCH[1]}\n          COMPILER_VERSION=${BASH_REMATCH[2]}\n          if [[ $COMPILER_ID = gcc ]]; then\n            CC=gcc-${COMPILER_VERSION};\n            CXX=g++-${COMPILER_VERSION};\n            FC=gfortran-${COMPILER_VERSION};\n          elif [[ $COMPILER_ID = clang ]]; then\n            CC=clang-${COMPILER_VERSION};\n            CXX=clang++-${COMPILER_VERSION};\n            FC=gfortran-11;\n          elif [[ $COMPILER_ID = nvcc ]]; then\n            # Set host compilers\n            CC=gcc;\n            CXX=g++;\n            FC=gfortran-11;\n          fi\n        fi\n        echo \"CC=$CC\" >> $GITHUB_ENV\n        echo \"CXX=$CXX\" >> $GITHUB_ENV\n        echo \"FC=$FC\" >> $GITHUB_ENV\n        echo \"COMPILER_ID=$COMPILER_ID\" >> $GITHUB_ENV\n        echo \"COMPILER_VERSION=$COMPILER_VERSION\" >> $GITHUB_ENV\n      shell: bash\n"
  },
  {
    "path": ".github/problem_matchers/ClangTidy.json",
    "content": "{\n  \"problemMatcher\": [\n    {\n      \"owner\": \"clang-tidy\",\n      \"pattern\": [\n        {\n          \"regexp\": \"^(?:\\\\x1b\\\\[[\\\\d;]+m)*(.+):(\\\\d+):(\\\\d+):\\\\s(?:\\\\x1b\\\\[[\\\\d;]+m)*(error):\\\\s(?:\\\\x1b\\\\[[\\\\d;]+m)*(.+)\\\\s\\\\[(.+)\\\\](?:\\\\x1b\\\\[[\\\\d;]+m)*$\",\n          \"file\": 1,\n          \"line\": 2,\n          \"column\": 3,\n          \"severity\": 4,\n          \"message\": 5,\n          \"code\": 6\n        }\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": ".github/scripts/Release.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport datetime\nimport difflib\nimport logging\nimport operator\nimport os\nimport pathlib\nimport re\nimport tempfile\nimport textwrap\nimport urllib\nfrom typing import List\n\nimport git\nimport pybtex.database\nimport requests\nimport uplink\nimport yaml\nfrom cffconvert.citation import Citation\nfrom pybtex.backends.plaintext import Backend as PlaintextBackend\nfrom pybtex.style.formatting.plain import Style as PlainStyle\n\nlogger = logging.getLogger(__name__)\n\nVERSION_PATTERN = r\"(\\d{4})\\.(\\d{2})\\.(\\d{2})(\\.\\d+)?\"\nPUBLICATION_DATE_PATTERN = r\"\\d{4}-\\d{2}-\\d{2}\"\nDOI_PATTERN = r\"10\\.\\d{4,9}/zenodo\\.\\d+\"\nZENODO_ID_PATTERN = r\"\\d+\"\n\n\ndef report_check_only(msg: str):\n    logger.info(f\"CHECK ONLY: {msg}\")\n\n\ndef new_version_id_from_response(response):\n    \"\"\"Retrieves the ID of the new version draft from the API response\n\n    The \"New version\" action of the Zenodo API returns the ID of the created\n    version draft in the 'links' section, as documented\n    [here](https://developers.zenodo.org/#new-version). This function parses the\n    ID out of the link.\n    \"\"\"\n    new_version_url = response.json()[\"links\"][\"latest_draft\"]\n    return int(\n        pathlib.PurePosixPath(\n            urllib.parse.urlparse(new_version_url).path\n        ).parts[-1]\n    )\n\n\ndef raise_for_status(response):\n    try:\n        response.raise_for_status()\n    except requests.exceptions.HTTPError as err:\n        if response.status_code >= 400 and response.status_code < 500:\n            raise requests.exceptions.HTTPError(\n                yaml.safe_dump(response.json(), allow_unicode=True)\n            ) from err\n        else:\n            raise\n    return response\n\n\n@uplink.response_handler(raise_for_status)\nclass Zenodo(uplink.Consumer):\n    \"\"\"Abstraction of the [Zenodo API](https://developers.zenodo.org)\"\"\"\n\n    @uplink.returns.json\n    @uplink.get(\"deposit/depositions/{id}\")\n    def get_deposition(self, id: uplink.Path):\n        \"\"\"Retrieves a deposition by ID.\"\"\"\n        pass\n\n    @uplink.returns.json\n    @uplink.get(\"records/{id}\")\n    def get_record(self, id: uplink.Path):\n        \"\"\"Retrieves a published record by ID.\"\"\"\n        pass\n\n    @uplink.returns.json(\n        key=(\"metadata\", \"relations\", \"version\", 0, \"is_last\"),\n        type=bool,\n    )\n    @uplink.get(\"records/{id}\")\n    def is_latest_version(self, record_id: uplink.Path(name=\"id\")):\n        \"\"\"Checks whether the record is the latest version.\"\"\"\n        pass\n\n    @uplink.response_handler(new_version_id_from_response)\n    @uplink.post(\"deposit/depositions/{id}/actions/newversion\")\n    def new_version(self, latest_version_id: uplink.Path(name=\"id\")):\n        \"\"\"Invoke the \"New version\" action on a deposition.\n\n        Returns:\n          The ID of the new version.\n        \"\"\"\n        pass\n\n    @uplink.returns.json\n    @uplink.post(\"deposit/depositions/{id}/actions/publish\")\n    def publish(self, id: uplink.Path):\n        \"\"\"Invoke the \"Publish\" action on a deposition.\"\"\"\n        pass\n\n    @uplink.json\n    @uplink.returns.json\n    @uplink.put(\"deposit/depositions/{id}\")\n    def update_deposition(self, id: uplink.Path, **body: uplink.Body):\n        \"\"\"Update the deposition with the metadata\"\"\"\n        pass\n\n    @uplink.returns.json\n    @uplink.put(\"files/{bucket_id}/{filename}\")\n    def upload_file(\n        self, bucket_id: uplink.Path, filename: uplink.Path, file: uplink.Body\n    ):\n        \"\"\"Uploads a file to the bucket.\"\"\"\n        pass\n\n\n@uplink.response_handler(raise_for_status)\nclass Github(uplink.Consumer):\n    \"\"\"Abstraction of the [GitHub REST API](https://docs.github.com/en/rest)\"\"\"\n\n    # This endpoint is not currently implemented in PyGithub, so we wrap it\n    # here manually\n    @uplink.headers({\"Content-Type\": \"text/x-markdown\"})\n    @uplink.response_handler(operator.attrgetter(\"text\"))\n    @uplink.post(\"markdown/raw\")\n    def render_markdown_raw(self, text: uplink.Body):\n        \"\"\"Render Markdown in plain format like a README.md file on GitHub\"\"\"\n        pass\n\n    @uplink.returns.json\n    @uplink.get(\"repos/{user}/{repo}/releases/tags/{tag}\")\n    def get_release_by_tag(\n        self, user: uplink.Path, repo: uplink.Path, tag: uplink.Path\n    ):\n        pass\n\n    @uplink.returns.json\n    @uplink.get(\"repos/{user}/{repo}/releases/{release_id}/assets\")\n    def get_assets(\n        self, user: uplink.Path, repo: uplink.Path, release_id: uplink.Path\n    ):\n        pass\n\n\ndef to_plaintext_reference(\n    bib_entry: pybtex.database.Entry,\n    style=PlainStyle(),\n    backend=PlaintextBackend(),\n) -> str:\n    # pybtex doesn't support 'software' bibtex types for some reason:\n    # https://bitbucket.org/pybtex-devs/pybtex/issues/157/support-for-software-and-patent-entries-in\n    if bib_entry.type == \"software\":\n        bib_entry.type = \"misc\"\n    return style.format_entry(label=bib_entry.key, entry=bib_entry).text.render(\n        backend\n    )\n\n\ndef collect_zenodo_metadata(\n    metadata: dict, references: List[pybtex.database.Entry], github: Github\n) -> dict:\n    \"\"\"Produces the metadata that we send to Zenodo\n\n    Args:\n      metadata: The project metadata read from the YAML file. This is the main\n        source of information for this function.\n      references: List of references resolved from 'metadata[\"References\"]'.\n        They will be formatted and sent to Zenodo.\n      github: The GitHub API client. We use it to render the description to\n        HTML in a way that's consistent with GitHub's rendering.\n\n    Returns:\n      Metadata in the format that the Zenodo API expects.\n    \"\"\"\n    # Generate the DOI author list from the authors in the project metadata\n    zenodo_creators = []\n    for author_tier in [\"Core\", \"Developers\", \"Contributors\"]:\n        for author in metadata[\"Authors\"][author_tier][\"List\"]:\n            zenodo_creator = dict(name=author[\"Name\"])\n            if \"Orcid\" in author:\n                zenodo_creator[\"orcid\"] = author[\"Orcid\"]\n            if \"Affiliations\" in author and len(author[\"Affiliations\"]) > 0:\n                zenodo_creator[\"affiliation\"] = \" and \".join(\n                    author[\"Affiliations\"]\n                )\n            zenodo_creators.append(zenodo_creator)\n    # Render the description to HTML\n    rendered_description = github.render_markdown_raw(metadata[\"Description\"])\n    # Format references as plain text for Zenodo\n    formatted_references = [\n        to_plaintext_reference(entry) for entry in references\n    ]\n    # Construct Zenodo metadata\n    return dict(\n        title=metadata[\"Name\"],\n        version=metadata[\"Version\"],\n        publication_date=metadata[\"PublicationDate\"].isoformat(),\n        doi=metadata[\"Doi\"],\n        description=rendered_description,\n        creators=zenodo_creators,\n        related_identifiers=[\n            dict(\n                identifier=metadata[\"Homepage\"],\n                relation=\"isDocumentedBy\",\n                scheme=\"url\",\n                resource_type=\"publication-softwaredocumentation\",\n            ),\n            dict(\n                identifier=(\"https://github.com/\" + metadata[\"GitHub\"]),\n                relation=\"isSupplementTo\",\n                scheme=\"url\",\n                resource_type=\"software\",\n            ),\n        ],\n        language=\"eng\",\n        communities=[dict(identifier=\"sxs\")],\n        keywords=metadata[\"Keywords\"],\n        license=metadata[\"License\"],\n        upload_type=\"software\",\n        access_right=\"open\",\n        references=formatted_references,\n    )\n\n\ndef to_cff_person(person: pybtex.database.Person) -> dict:\n    \"\"\"BibTeX to CFF conversion for person objects.\n\n    The format is defined here:\n    https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsperson\n    \"\"\"\n    # Map BibTeX to CFF fields\n    name_fields = {\n        \"last\": \"family-names\",\n        \"bibtex_first\": \"given-names\",\n        \"prelast\": \"name-particle\",\n        \"lineage\": \"name-suffix\",\n    }\n    result = {\n        cff_field: \" \".join(person.get_part(bibtex_field))\n        for bibtex_field, cff_field in name_fields.items()\n        if person.get_part(bibtex_field)\n    }\n    # Use CFF \"entity\" format if BibTex has no first & last names\n    if list(result.keys()) == [\"family-names\"]:\n        return {\"name\": result[\"family-names\"]}\n    return result\n\n\ndef to_cff_reference(bib_entry: pybtex.database.Entry) -> dict:\n    \"\"\"BibTeX to CFF conversion for references.\n\n    The format is defined here:\n    https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md#definitionsreference\n    \"\"\"\n\n    def _cff_transform(cff_field, bib_value):\n        if cff_field == \"type\":\n            if bib_value == \"inproceedings\":\n                return \"article\"\n            elif bib_value == \"incollection\":\n                return \"article\"\n        elif cff_field == \"publisher\":\n            return {\"name\": bib_value}\n        elif cff_field == \"month\":\n            try:\n                return int(bib_value)\n            except ValueError:\n                return {\n                    \"jan\": 1,\n                    \"feb\": 2,\n                    \"mar\": 3,\n                    \"apr\": 4,\n                    \"may\": 5,\n                    \"jun\": 6,\n                    \"jul\": 7,\n                    \"aug\": 8,\n                    \"sep\": 9,\n                    \"oct\": 10,\n                    \"nov\": 11,\n                    \"dec\": 12,\n                }[bib_value[:3].lower()]\n        return bib_value\n\n    cff_reference = {\n        \"type\": _cff_transform(cff_field=\"type\", bib_value=bib_entry.type),\n        \"authors\": [\n            to_cff_person(person) for person in bib_entry.persons[\"author\"]\n        ],\n    }\n    # Map BibTeX to CFF fields. This is just a subset of the most relevant\n    # fields.\n    fields = {\n        \"doi\": \"doi\",\n        \"edition\": \"edition\",\n        \"isbn\": \"isbn\",\n        \"license\": \"license\",\n        \"month\": \"month\",\n        \"number\": \"number\",\n        \"pages\": \"pages\",\n        \"publisher\": \"publisher\",\n        \"title\": \"title\",\n        \"url\": \"url\",\n        \"version\": \"version\",\n        \"volume\": \"volume\",\n        \"year\": \"year\",\n        \"booktitle\": \"collection-title\",\n    }\n    for bibtex_field, value in bib_entry.fields.items():\n        bibtex_field = bibtex_field.lower()\n        if bibtex_field in fields:\n            cff_field = fields[bibtex_field]\n            cff_reference[cff_field] = _cff_transform(\n                cff_field=cff_field, bib_value=value\n            )\n    return cff_reference\n\n\ndef collect_citation_metadata(\n    metadata: dict, references: List[pybtex.database.Entry]\n) -> dict:\n    \"\"\"Produces the data stored in the CITATION.cff file\n\n    Args:\n      metadata: The project metadata read from the YAML file. This is the main\n        source of information for this function.\n      references: List of references resolved from 'metadata[\"References\"]'.\n        They will be converted to CFF.\n\n    Returns:\n      Citation data in the [Citation File Format](https://github.com/citation-file-format/citation-file-format)\n    \"\"\"\n    # Author list\n    citation_authors = []\n    for author_tier in [\"Core\", \"Developers\", \"Contributors\"]:\n        for author in metadata[\"Authors\"][author_tier][\"List\"]:\n            family_names, given_names = author[\"Name\"].split(\", \")\n            citation_author = {\n                \"family-names\": family_names,\n                \"given-names\": given_names,\n            }\n            if \"Orcid\" in author:\n                citation_author[\"orcid\"] = (\n                    \"https://orcid.org/\" + author[\"Orcid\"]\n                )\n            if \"Affiliations\" in author and len(author[\"Affiliations\"]) > 0:\n                citation_author[\"affiliation\"] = \" and \".join(\n                    author[\"Affiliations\"]\n                )\n            citation_authors.append(citation_author)\n    # References in CITATION.cff format\n    citation_references = [to_cff_reference(entry) for entry in references]\n    return {\n        \"cff-version\": \"1.2.0\",\n        \"message\": (\n            \"Please cite SpECTRE in any publications that make use of its code\"\n            \" or data. Cite the latest version that you use in your\"\n            \" publication. The citation for this version is listed below.\"\n        ),\n        \"title\": metadata[\"Name\"],\n        \"url\": metadata[\"Homepage\"],\n        \"repository-code\": \"https://github.com/\" + metadata[\"GitHub\"],\n        \"version\": metadata[\"Version\"],\n        \"date-released\": metadata[\"PublicationDate\"],\n        \"doi\": metadata[\"Doi\"],\n        \"authors\": citation_authors,\n        \"keywords\": metadata[\"Keywords\"],\n        \"license\": metadata[\"License\"],\n        \"references\": citation_references,\n    }\n\n\ndef build_bibtex_entry(metadata: dict):\n    \"\"\"Builds a BibTeX entry that we suggest people cite in publications\n\n    Args:\n      metadata: The project metadata read from the YAML file\n\n    Returns:\n      A pybtex.database.Entry. Use the `to_string` member function to convert to\n      a string in BibTeX, YAML or other formats.\n    \"\"\"\n    # We truncate the author list in the BibTeX entry after 'Developers',\n    # because not all journals are happy with printing an excessively long\n    # author list, e.g. Phys. Rev. wants at most 15 authors. By truncating the\n    # author list here, the user who copies the BibTeX entry doesn't have to\n    # make the decision where to truncate.\n    authors = [\n        pybtex.database.Person(author[\"Name\"])\n        for author in (\n            metadata[\"Authors\"][\"Core\"][\"List\"]\n            + metadata[\"Authors\"][\"Developers\"][\"List\"]\n        )\n    ] + [pybtex.database.Person(\"others\")]\n    entry = pybtex.database.Entry(\n        \"software\",\n        persons=dict(author=authors),\n        fields=dict(\n            title=(\n                r\"\\texttt{\"\n                + metadata[\"Name\"]\n                + \" v\"\n                + metadata[\"Version\"]\n                + \"}\"\n            ),\n            # The 'version' field is not used by revtex4-2, so we also put the\n            # version in the title\n            version=metadata[\"Version\"],\n            publisher=\"Zenodo\",\n            doi=metadata[\"Doi\"],\n            url=metadata[\"Homepage\"],\n            howpublished=(\n                r\"\\href{https://doi.org/\"\n                + metadata[\"Doi\"]\n                + \"}{\"\n                + metadata[\"Doi\"]\n                + \"}\"\n            ),\n            license=metadata[\"License\"],\n            year=str(metadata[\"PublicationDate\"].year),\n            month=str(metadata[\"PublicationDate\"].month),\n        ),\n    )\n    entry.key = \"spectrecode\"\n    return entry\n\n\ndef prepare(\n    metadata: dict,\n    version_name: str,\n    metadata_file: str,\n    citation_file: str,\n    bib_file: str,\n    references_file: str,\n    readme_file: str,\n    zenodo: Zenodo,\n    github: Github,\n    update_only: bool,\n    check_only: bool,\n):\n    # Validate new version name\n    match_version_name = re.match(VERSION_PATTERN + \"$\", version_name)\n    if not match_version_name:\n        raise ValueError(\n            f\"Version name '{version_name}' doesn't match \"\n            f\"pattern '{VERSION_PATTERN}'.\"\n        )\n    publication_date = datetime.date(\n        year=int(match_version_name.group(1)),\n        month=int(match_version_name.group(2)),\n        day=int(match_version_name.group(3)),\n    )\n\n    if update_only:\n        # Don't try to create a new version draft on Zenodo but update the\n        # existing one. We assume that the metadata in the repository already\n        # point to the existing version draft on Zenodo that we want to update.\n        # This is the case when the user has run this script without the\n        # `--update-only` option before and has thus created the new version\n        # draft on Zenodo, and is now running it again with the `--update-only`\n        # option to push updated metadata to the draft.\n        new_version_id = metadata[\"ZenodoId\"]\n    else:\n        # Zenodo doesn't have a draft for the new version yet, or the metadata\n        # in the repository is not yet updated. Either way, we use the ID from\n        # the metadata to obtain the latest version on Zenodo and create a new\n        # draft. Zenodo doesn't create another draft if one already exists, but\n        # just returns it.\n        latest_version_id = metadata[\"ZenodoId\"]\n        try:\n            assert zenodo.is_latest_version(record_id=latest_version_id), (\n                \"The latest Zenodo version ID in the repository is\"\n                f\" {latest_version_id}, but Zenodo reports it is not the latest\"\n                \" version.\"\n            )\n        except requests.exceptions.HTTPError as err:\n            raise requests.exceptions.HTTPError(\n                f\"No published record with ID {latest_version_id} found on \"\n                \"Zenodo. Use the '--update-only' flag if you're re-running \"\n                \"the script over a repository that already has an unpublished \"\n                \"new version ID inserted into Metadata.yaml.\"\n            ) from err\n        logger.info(f\"The latest Zenodo version ID is {latest_version_id}.\")\n        # Reserve a DOI by creating a new version on Zenodo. It will remain a\n        # draft until we publish it in the `publish` subprogram.\n        if check_only:\n            report_check_only(\n                \"Would create new version on Zenodo with \"\n                f\"ID {latest_version_id}.\"\n            )\n            new_version_id = latest_version_id\n        else:\n            new_version_id = zenodo.new_version(\n                latest_version_id=latest_version_id\n            )\n    new_version_draft = zenodo.get_deposition(id=new_version_id)\n    new_version_doi = new_version_draft[\"metadata\"][\"prereserve_doi\"][\"doi\"]\n    assert new_version_doi, (\n        \"Zenodo did not return a reserved DOI for the new version draft. \"\n        \"You may want to visit {} to reserve one, save the draft and re-run \"\n        \"this script with the '--update-only' flag.\"\n    ).format(new_version_draft[\"links\"][\"html\"])\n    logger.info(\n        \"The new version draft on Zenodo has \"\n        f\"ID {new_version_id} and DOI {new_version_doi}.\"\n    )\n\n    # Insert the new version information into the metadata file. We have to\n    # resort to regex-replacements because pyyaml doesn't support\n    # format-preserving round-trips.\n    def replace_in_yaml(content, key, value, validate_value_pattern):\n        content, num_subs = re.subn(\n            r\"^{}: {}$\".format(key, validate_value_pattern),\n            r\"{}: {}\".format(key, value),\n            content,\n            flags=re.MULTILINE,\n        )\n        if num_subs == 0:\n            match = re.search(\n                r\"^{}: (.*)$\".format(key), content, flags=re.MULTILINE\n            )\n            if match:\n                raise ValueError(\n                    f\"The value of '{key}' in the file '{metadata_file}' \"\n                    f\"does not match the pattern '{validate_value_pattern}': \"\n                    f\"{match.group(1)}\"\n                )\n            else:\n                raise ValueError(\n                    f\"Could not find '{key}' in root of file '{metadata_file}'.\"\n                )\n        elif num_subs > 1:\n            raise ValueError(\n                f\"Found more than one '{key}' in file '{metadata_file}'.\"\n            )\n        return content\n\n    with open(metadata_file, \"r\" if check_only else \"r+\") as open_metadata_file:\n        content_original = open_metadata_file.read()\n        content_new = replace_in_yaml(\n            content_original, \"Version\", version_name, VERSION_PATTERN\n        )\n        content_new = replace_in_yaml(\n            content_new,\n            \"PublicationDate\",\n            publication_date.isoformat(),\n            PUBLICATION_DATE_PATTERN,\n        )\n        content_new = replace_in_yaml(\n            content_new, \"Doi\", new_version_doi, DOI_PATTERN\n        )\n        content_new = replace_in_yaml(\n            content_new, \"ZenodoId\", new_version_id, ZENODO_ID_PATTERN\n        )\n        content_diff = \"\\n\".join(\n            difflib.context_diff(\n                content_original.split(\"\\n\"),\n                content_new.split(\"\\n\"),\n                lineterm=\"\",\n                fromfile=metadata_file,\n                tofile=metadata_file,\n            )\n        )\n        if check_only:\n            report_check_only(f\"Would apply diff:\\n{content_diff}\")\n        else:\n            logger.debug(f\"Applying diff:\\n{content_diff}\")\n            open_metadata_file.seek(0)\n            open_metadata_file.write(content_new)\n            open_metadata_file.truncate()\n    logger.info(f\"Inserted new version info into '{metadata_file}'.\")\n    # Also update the the metadata dict to make sure we don't accidentally use\n    # the old values somewhere\n    metadata[\"Version\"] = version_name\n    metadata[\"PublicationDate\"] = publication_date\n    metadata[\"Doi\"] = new_version_doi\n    metadata[\"ZenodoId\"] = new_version_id\n\n    def write_file_or_check(filename, content_new):\n        with open(filename, \"r\" if check_only else \"r+\") as open_file:\n            content_original = open_file.read()\n            content_diff = \"\\n\".join(\n                difflib.context_diff(\n                    content_original.split(\"\\n\"),\n                    content_new.split(\"\\n\"),\n                    lineterm=\"\",\n                    fromfile=filename,\n                    tofile=filename,\n                )\n            )\n            if check_only:\n                report_check_only(f\"Would apply diff:\\n{content_diff}\")\n            else:\n                logger.debug(f\"Applying diff:\\n{content_diff}\")\n                open_file.seek(0)\n                open_file.write(content_new)\n                open_file.truncate()\n\n    # Write the CITATION.cff file\n    reference_keys = metadata[\"References\"][\"List\"]\n    all_references = pybtex.database.parse_file(references_file)\n    references = [all_references.entries[key] for key in reference_keys]\n    citation_data = collect_citation_metadata(metadata, references)\n    citation_file_content = \"\"\"# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This file is automatically generated. It will be overwritten at every\n# release. See .github/scripts/Release.py for details.\n\n\"\"\"\n    citation_file_content += yaml.safe_dump(citation_data, allow_unicode=True)\n    write_file_or_check(citation_file, citation_file_content)\n    # Validate the CFF file\n    Citation(citation_file_content, src=citation_file).validate()\n\n    # Get the BibTeX entry and write to file\n    bibtex_entry = build_bibtex_entry(metadata)\n    bib_file_content = bibtex_entry.to_string(\n        \"bibtex\",\n        # LaTeX-encode special characters, instead of writing unicode symbols\n        encoding=\"ASCII\",\n    )\n    # Fix an issue with encoded accents (a space in the author list is\n    # recognized as delimiter between names)\n    bib_file_content = bib_file_content.replace(r\"\\c c\", r\"\\c{c}\")\n    # Wrap long lines in BibTeX output\n    bib_file_content = \"\\n\".join(\n        [textwrap.fill(line, width=80) for line in bib_file_content.split(\"\\n\")]\n    )\n    write_file_or_check(bib_file, bib_file_content)\n\n    # Insert the new version information into the README\n    def replace_badge_in_readme(content, key, image_url, link_url):\n        content, num_subs = re.subn(\n            r\"\\[!\\[{}\\]\\(.*\\)\\]\\(.*\\)\".format(key),\n            r\"[![{}]({})]({})\".format(key, image_url, link_url),\n            content,\n            flags=re.MULTILINE,\n        )\n        assert (\n            num_subs > 0\n        ), f\"Could not find badge '{key}' in file '{readme_file}'.\"\n        return content\n\n    def replace_doi_in_readme(content, doi, doi_url):\n        content, num_subs = re.subn(\n            r\"DOI: \\[{}\\]\\(.*\\)\".format(DOI_PATTERN),\n            r\"DOI: [{}]({})\".format(doi, doi_url),\n            content,\n            flags=re.MULTILINE,\n        )\n        assert (\n            num_subs > 0\n        ), \"Could not find DOI (matching '{}') with link in file '{}'.\".format(\n            DOI_PATTERN, readme_file\n        )\n        return content\n\n    def replace_link_in_readme(content, link_text, link_url):\n        content, num_subs = re.subn(\n            r\"\\[{}\\]\\(.*\\)\".format(link_text),\n            r\"[{}]({})\".format(link_text, link_url),\n            content,\n            flags=re.MULTILINE,\n        )\n        assert num_subs > 0, (\n            f\"Could not find link with text '{link_text}' in \"\n            f\"file '{readme_file}'.\"\n        )\n        return content\n\n    def replace_bibtex_entry_in_readme(content, bibtex_entry):\n        bibtex_entry_string = bib_file_content\n        # Work around an issue with escaping LaTeX commands\n        bibtex_entry_string = bibtex_entry_string.replace(\"\\\\\", \"\\\\\\\\\")\n        FENCE_PATTERN = \"<!-- BIBTEX ENTRY -->\"\n        content, num_subs = re.subn(\n            (FENCE_PATTERN + \"(.*)\" + FENCE_PATTERN),\n            (\n                FENCE_PATTERN\n                + \"\\n```bib\\n\"\n                + bibtex_entry_string.strip()\n                + \"\\n```\\n\"\n                + FENCE_PATTERN\n            ),\n            content,\n            flags=re.DOTALL,\n        )\n        assert (\n            num_subs > 0\n        ), f\"Could not find a BibTeX entry in file '{readme_file}'.\"\n        return content\n\n    with open(readme_file, \"r\" if check_only else \"r+\") as open_readme_file:\n        content_original = open_readme_file.read()\n        content = replace_badge_in_readme(\n            content_original,\n            \"release\",\n            f\"https://img.shields.io/badge/release-v{version_name}-informational\",\n            \"https://github.com/{}/releases/tag/v{}\".format(\n                metadata[\"GitHub\"], version_name\n            ),\n        )\n        content = replace_badge_in_readme(\n            content,\n            \"DOI\",\n            f\"https://zenodo.org/badge/doi/{new_version_doi}.svg\",\n            f\"https://doi.org/{new_version_doi}\",\n        )\n        content = replace_doi_in_readme(\n            content, new_version_doi, f\"https://doi.org/{new_version_doi}\"\n        )\n        # We don't currently link to the Zenodo BibTeX entry because it isn't\n        # very good. Instead, we generate our own.\n        content = replace_bibtex_entry_in_readme(content, bibtex_entry)\n        content_diff = \"\\n\".join(\n            difflib.context_diff(\n                content_original.split(\"\\n\"),\n                content.split(\"\\n\"),\n                lineterm=\"\",\n                fromfile=readme_file,\n                tofile=readme_file,\n            )\n        )\n        if check_only:\n            report_check_only(f\"Would apply diff:\\n{content_diff}\")\n        else:\n            logger.debug(f\"Applying diff:\\n{content_diff}\")\n            open_readme_file.seek(0)\n            open_readme_file.write(content)\n            open_readme_file.truncate()\n\n    # Upload the updated metadata to Zenodo\n    zenodo_metadata = collect_zenodo_metadata(metadata, references, github)\n    logger.debug(\n        \"The metadata we'll send to Zenodo are:\\n{}\".format(\n            yaml.safe_dump(zenodo_metadata, allow_unicode=True)\n        )\n    )\n    if check_only:\n        report_check_only(\"Would upload metadata to Zenodo.\")\n    else:\n        zenodo.update_deposition(id=new_version_id, metadata=zenodo_metadata)\n    logger.debug(\n        (\n            \"New Zenodo version draft is now prepared. You can edit \"\n            \"it here:\\n{}\"\n        ).format(new_version_draft[\"links\"][\"html\"])\n    )\n\n\ndef publish(\n    metadata: dict,\n    zenodo: Zenodo,\n    github: Github,\n    auto_publish: bool,\n    check_only: bool,\n):\n    version_name = metadata[\"Version\"]\n    new_version_id = metadata[\"ZenodoId\"]\n\n    # Retrieve the Zenodo deposition for the version draft that we have\n    # prepared before\n    new_version_draft = zenodo.get_deposition(id=new_version_id)\n\n    # Retrieve the file \"bucket\" ID for uploading data\n    bucket_id = pathlib.PurePosixPath(\n        urllib.parse.urlparse(new_version_draft[\"links\"][\"bucket\"]).path\n    ).parts[-1]\n\n    # Retrieve the URL of the GitHub release archive that we want to upload\n    # to Zenodo\n    gh_user, gh_repo = metadata[\"GitHub\"].split(\"/\")\n    # Alternatively we could use the release ID that GitHub's\n    # 'actions/create-release' returns to retrieve the release\n    gh_release = github.get_release_by_tag(\n        user=gh_user, repo=gh_repo, tag=\"v\" + version_name\n    )\n    logger.debug(\n        \"The release on GitHub is:\\n{}\".format(\n            yaml.safe_dump(gh_release, allow_unicode=True)\n        )\n    )\n    zipball_url = gh_release[\"zipball_url\"]\n\n    # Stream the release archive to Zenodo.\n    # We keep the file name for the archive on Zenodo the same for each\n    # release so we can just overwrite it. Note that the _unpacked_ directory\n    # name contains the version as expected, since the unpacked directory name\n    # is determined by the GitHub release.\n    archive_filename = gh_repo + \".zip\"\n    if check_only:\n        report_check_only(\n            f\"Would stream release zipball '{zipball_url}' as \"\n            f\"filename '{archive_filename}' to bucket '{bucket_id}'.\"\n        )\n    else:\n        # Download the zipball from GitHub, then upload to Zenodo.\n        # Note: Something like this should also work to stream the file\n        # directly from GitHub to Zenodo without temporarily saving it, but\n        # Zenodo doesn't currently document their new \"bucket\" file API so it\n        # is difficult to debug:\n        # with requests.get(zipball_url, stream=True) as zipball_stream:\n        #     zipball_stream.raise_for_status()\n        #     uploaded_file = zenodo.upload_file(bucket_id=bucket_id,\n        #                                        file=zipball_stream,\n        #                                        filename=archive_filename)\n        zipball_download = requests.get(zipball_url, stream=True)\n        with tempfile.TemporaryFile() as open_tmp_file:\n            for chunk in zipball_download.iter_content():\n                open_tmp_file.write(chunk)\n            open_tmp_file.seek(0)\n            uploaded_file = zenodo.upload_file(\n                bucket_id=bucket_id,\n                file=open_tmp_file,\n                filename=archive_filename,\n            )\n        logger.debug(\n            \"Release archive upload complete:\\n{}\".format(\n                yaml.safe_dump(uploaded_file, allow_unicode=True)\n            )\n        )\n\n    # Publish!\n    if auto_publish:\n        if check_only:\n            report_check_only(\n                f\"Would publish Zenodo record {new_version_id} now!\"\n            )\n        else:\n            published_record = zenodo.publish(id=new_version_id)\n            logger.debug(\n                \"Zenodo record published:\\n{}\".format(\n                    yaml.safe_dump(published_record, allow_unicode=True)\n                )\n            )\n            logger.info(\n                (\n                    \"Zenodo record is now public! Here's the link to the \"\n                    \"record:\\n{}\"\n                ).format(published_record[\"links\"][\"record_html\"])\n            )\n    else:\n        logger.info(\n            (\n                \"Release is ready to be published on Zenodo. Go to this \"\n                \"website, make sure everything looks fine and then hit the \"\n                \"'Publish' button:\\n{}\"\n            ).format(new_version_draft[\"links\"][\"html\"])\n        )\n\n\nif __name__ == \"__main__\":\n    # Always work with the repository that contains this file\n    repo = git.Repo(__file__, search_parent_directories=True)\n\n    import argparse\n\n    parser = argparse.ArgumentParser(\n        description=(\n            \"Prepare the repository and publish releases on Zenodo as part of\"\n            \" the automatic versioning procedure. This script is not intended\"\n            \" to be run outside of GitHub actions. The 'prepare' subprogram\"\n            \" reserves a DOI on Zenodo and inserts it into the repository\"\n            \" along with the new version name. Once the release archive has\"\n            \" been created, the 'publish'subprogram uploads it to Zenodo.\"\n            f\" Repository: {repo.working_dir}.\"\n        )\n    )\n    parent_parser = argparse.ArgumentParser(add_help=False)\n    parent_parser.add_argument(\n        \"--zenodo-token\",\n        required=True,\n        help=(\n            \"Zenodo access token. Refer to the Zenodo documentation \"\n            \"for instructions on creating a personal access token.\"\n        ),\n    )\n    parent_parser.add_argument(\n        \"--zenodo-sandbox\",\n        action=\"store_true\",\n        help=\"Use the Zenodo sandbox instead of the public version of Zenodo\",\n    )\n    parent_parser.add_argument(\n        \"--github-token\",\n        required=False,\n        help=(\n            \"Access token for GitHub queries. Refer to the GitHub documentation\"\n            \" for instructions on creating a personal access token.\"\n        ),\n    )\n    parent_parser.add_argument(\n        \"-v\",\n        \"--verbose\",\n        action=\"count\",\n        default=0,\n        help=\"Verbosity (-v, -vv, ...)\",\n    )\n    parent_parser.add_argument(\n        \"--check-only\",\n        action=\"store_true\",\n        help=(\n            \"Dry mode, only check that all files are consistent. Nothing is\"\n            \" edited or uploaded to Zenodo. Used in CI tests to make sure\"\n            \" changes to the repository remain compatible with this script.\"\n        ),\n    )\n    subparsers = parser.add_subparsers()\n    parser_prepare = subparsers.add_parser(\"prepare\", parents=[parent_parser])\n    parser_prepare.set_defaults(subprogram=prepare)\n    parser_prepare.add_argument(\n        \"--update-only\",\n        action=\"store_true\",\n        help=(\n            \"Only update an existing version draft on Zenodo, not creating a \"\n            \"new one. Use this flag if the metadata in the repository already \"\n            \"reference the new version draft on Zenodo.\"\n        ),\n    )\n    parser_prepare.add_argument(\n        \"--version-name\",\n        required=False,\n        help=(\n            \"The name of the new version. Will be inserted into the \"\n            \"'--metadata-file'. Required unless '--check-only'.\"\n        ),\n    )\n    parser_publish = subparsers.add_parser(\"publish\", parents=[parent_parser])\n    parser_publish.set_defaults(subprogram=publish)\n    parser_publish.add_argument(\n        \"--auto-publish\",\n        action=\"store_true\",\n        help=(\n            \"Publish the Zenodo record once it's ready. \"\n            \"WARNING: Published records cannot be deleted and editing is \"\n            \"limited. Omit this argument to print out the link to the \"\n            \"prepared draft on Zenodo so you can do a manual sanity-check \"\n            \"before publishing it.\"\n        ),\n    )\n    args = parser.parse_args()\n\n    # Set the log level\n    logging.basicConfig(level=logging.WARNING - args.verbose * 10)\n    logging.getLogger(\"pykwalify\").setLevel(logging.CRITICAL)\n    del args.verbose\n\n    # Load the project metadata\n    metadata_file = os.path.join(repo.working_dir, \"Metadata.yaml\")\n    args.metadata = yaml.safe_load(open(metadata_file, \"r\"))\n    if args.subprogram == prepare:\n        args.metadata_file = metadata_file\n\n    # Make passing a version name optional in check-only mode\n    if args.subprogram == prepare:\n        assert args.check_only or args.version_name, (\n            \"The '--version-name' argument is required unless you run in \"\n            \"'--check-only' mode.\"\n        )\n        if args.check_only and not args.version_name:\n            args.version_name = args.metadata[\"Version\"]\n\n    # Locate project files\n    if args.subprogram == prepare:\n        args.readme_file = os.path.join(repo.working_dir, \"README.md\")\n        args.citation_file = os.path.join(repo.working_dir, \"CITATION.cff\")\n        args.bib_file = os.path.join(repo.working_dir, \"citation.bib\")\n        args.references_file = os.path.join(\n            repo.working_dir, args.metadata[\"References\"][\"BibliographyFile\"]\n        )\n\n    # Configure the Zenodo API client\n    args.zenodo = Zenodo(\n        base_url=(\n            \"https://sandbox.zenodo.org/api/\"\n            if args.zenodo_sandbox\n            else \"https://zenodo.org/api/\"\n        ),\n        auth=uplink.auth.BearerToken(args.zenodo_token),\n    )\n    del args.zenodo_sandbox\n    del args.zenodo_token\n\n    # Configure the GitHub API client\n    args.github = Github(\n        base_url=\"https://api.github.com/\",\n        auth=(\n            uplink.auth.BearerToken(args.github_token)\n            if args.github_token\n            else None\n        ),\n    )\n    del args.github_token\n\n    # Dispatch to the selected subprogram\n    subprogram = args.subprogram\n    del args.subprogram\n    subprogram(**vars(args))\n"
  },
  {
    "path": ".github/scripts/requirements-release.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Required Python packages for the release workflow\n\nGitPython~=3.1.11\npybtex~=0.24.0\nPyGithub~=1.53\nPyYAML~=5.3.1\ntqdm~=4.55.1\nuplink==0.9.7\ncffconvert==2.0.0\n"
  },
  {
    "path": ".github/workflows/BuildDockerContainer.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This workflow can be run manually on any branch to build and push our Docker\n# containers.\n#\n# We build containers that run both an AMD64/x86-64 and on ARM64. We used to use\n# a cross-compilation approach with Docker's buildx support, but this can be\n# quite fragile since cross-compilation is notoriously difficult. While buildx\n# used an emulator, QEMU, this is still found to be quite fragile with difficult\n# to diagnose segmentation faults and other errors. To get around this we use\n# the GitHub Actions ARM64 runners to build the ARM image and the x86-64 runners\n# to build that image. Then, once both images are pushed to Dockerhub, we create\n# what Docker calls a \"manifest\", which is essentially a file that tells Docker\n# which image to download depending on what it wants. This means for ARM64 it\n# will automatically download the ARM64 container and similar for\n# x86-64. The net result is extremely similar to the cross-compilation approach\n# but since the builds are always native, it is simpler.\nname: Build Docker container\n\non:\n  workflow_dispatch:\n    inputs:\n      image_name:\n        description: >\n          Image name to push to DockerHub\n        required: true\n        default: 'sxscollaboration/spectre'\n      build_arm:\n        type: boolean\n        description: >\n          Build ARM container in addition to x86_64\n        default: true\n\njobs:\n  build:\n    name: Build (${{ matrix.arch }})\n    runs-on: ${{ matrix.runner }}\n    # This environment can be protected to require manual approval before\n    # deployment. On forks where you set your own secrets for authentication\n    # with DockerHub you can choose not to protect this environment.\n    environment: deploy-containers\n    strategy:\n      fail-fast: true\n      # Dynamically set the matrix based on the build_arm input. GitHub Actions\n      # expressions lack a ternary operator, so we use short-circuit evaluation:\n      #   condition && value_if_true || value_if_false\n      # fromJSON() then parses the resulting JSON string into an array for\n      # `include`. The multi-line ${{ }} works because YAML folds unquoted\n      # scalars into a single line before the expression is evaluated.\n      # Docs:\n      #   https://docs.github.com/en/actions/reference/workflows-and-actions/expressions\n      #   https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs#expanding-or-adding-matrix-configurations\n      matrix:\n        include: ${{ fromJSON(inputs.build_arm\n          && '[{\"arch\":\"amd64\",\"runner\":\"ubuntu-latest\"},\n               {\"arch\":\"arm64\",\"runner\":\"ubuntu-24.04-arm\"}]'\n          || '[{\"arch\":\"amd64\",\"runner\":\"ubuntu-latest\"}]') }}\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v4\n      - name: Login to DockerHub\n        uses: docker/login-action@v4\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n      # We could also push containers to GitHub instead of DockerHub\n      # - name: Login to GitHub container registry\n      #   uses: docker/login-action@v3\n      #   with:\n      #     registry: ghcr.io\n      #     username: ${{ github.actor }}\n      #     password: ${{ secrets.GITHUB_TOKEN }}\n      - name: Build dev container\n        uses: docker/build-push-action@v7\n        with:\n          push: true\n          context: .\n          file: \"./containers/Dockerfile.buildenv\"\n          target: dev\n          tags: ${{ inputs.image_name }}:dev-${{ matrix.arch }}\n          platforms: linux/${{ matrix.arch }}\n          build-args: |\n            PARALLEL_MAKE_ARG=-j4\n            UBUNTU_VERSION=22.04\n      # The 18.04 container only supports amd64\n      - name: Build dev1804 container\n        if: ${{ matrix.arch == 'amd64' }}\n        uses: docker/build-push-action@v7\n        with:\n          push: true\n          context: .\n          file: \"./containers/Dockerfile.buildenv\"\n          target: dev\n          tags: ${{ inputs.image_name }}:dev1804\n          platforms: linux/amd64\n          build-args: |\n            PARALLEL_MAKE_ARG=-j4\n            UBUNTU_VERSION=18.04\n\n  create_manifest:\n    name: Create multi-arch manifest\n    needs: build\n    runs-on: ubuntu-latest\n    environment: deploy-containers\n    steps:\n      - name: Login to DockerHub\n        uses: docker/login-action@v4\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v4\n      - name: Create dev manifest\n        run: |\n          SOURCES=\"${{ inputs.image_name }}:dev-amd64\"\n          if [ \"${{ inputs.build_arm }}\" = \"true\" ]; then\n            SOURCES=\"$SOURCES ${{ inputs.image_name }}:dev-arm64\"\n          fi\n          docker buildx imagetools create \\\n            --tag ${{ inputs.image_name }}:dev $SOURCES\n      - name: Inspect manifest\n        run: >\n          docker buildx imagetools inspect ${{ inputs.image_name }}:dev\n\n  run_tests:\n    name: Test\n    uses: ./.github/workflows/Tests.yaml\n    needs: create_manifest\n    secrets: inherit\n    with:\n      container: ${{ inputs.image_name }}:dev\n"
  },
  {
    "path": ".github/workflows/ClobberCache.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Dump a large file into the cache to force GitHub to clobber it.\nname: Clobber Cache\n\non:\n  workflow_dispatch:\n\njobs:\n  clobber_cache:\n    name: Clobber Cache\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n    steps:\n      # Configure the clobber cache\n      - name: Restore cache\n        uses: actions/cache@v4\n        with:\n          path:  cache\n          key: \"clobber-cache\"\n          restore-keys: |\n            clobber-cache\n      # Generate the random file to clobber the cache.\n      # Note that the file size must be updated as GitHub expands the cache\n      # size limits.\n      - name: Clobber cache\n        run: >\n          mkdir -p cache\n\n          cd ./cache\n\n          rm -rf ./*\n\n          head -c 9900MB /dev/urandom > clobber.txt\n"
  },
  {
    "path": ".github/workflows/DemoContainer.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nname: Deploy containers\n\non:\n  workflow_dispatch:\n    inputs:\n      image_name:\n        description: Image name to push to DockerHub\n        default: 'sxscollaboration/spectre'\n      build_arm:\n        type: boolean\n        description: Build ARM container in addition to x86_64\n        default: true\n\n  workflow_call:\n    inputs:\n      image_name:\n        type: string\n        description: Image name to push to DockerHub\n        default: 'sxscollaboration/spectre'\n      build_arm:\n        type: boolean\n        description: Build ARM container in addition to x86_64\n        default: true\n    secrets:\n      DOCKERHUB_USERNAME:\n        required: true\n      DOCKERHUB_TOKEN:\n        required: true\n\njobs:\n  build:\n    name: Build (${{ matrix.arch }})\n    runs-on: ${{ matrix.runner }}\n    environment: deploy-containers\n    strategy:\n      fail-fast: true\n      matrix:\n        include: ${{ fromJSON(inputs.build_arm\n          && '[{\"arch\":\"amd64\",\"runner\":\"ubuntu-latest\"},\n               {\"arch\":\"arm64\",\"runner\":\"ubuntu-24.04-arm\"}]'\n          || '[{\"arch\":\"amd64\",\"runner\":\"ubuntu-latest\"}]') }}\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v4\n      - name: Login to DockerHub\n        uses: docker/login-action@v4\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n      - name: Build and push deploy container\n        uses: docker/build-push-action@v7\n        with:\n          push: true\n          context: .\n          file: \"./containers/Dockerfile.buildenv\"\n          target: deploy\n          tags: ${{ inputs.image_name }}:deploy-${{ matrix.arch }}\n          platforms: linux/${{ matrix.arch }}\n          build-args: PARALLEL_MAKE_ARG=-j4\n      - name: Build and push demo container\n        uses: docker/build-push-action@v5\n        with:\n          push: true\n          context: .\n          file: \"./containers/Dockerfile.buildenv\"\n          target: demo\n          tags: ${{ inputs.image_name }}:demo-${{ matrix.arch }}\n          platforms: linux/${{ matrix.arch }}\n          build-args: PARALLEL_MAKE_ARG=-j4\n\n  create_manifests:\n    name: Create multi-arch manifests\n    needs: build\n    runs-on: ubuntu-latest\n    environment: deploy-containers\n    steps:\n      - name: Login to DockerHub\n        uses: docker/login-action@v4\n        with:\n          username: ${{ secrets.DOCKERHUB_USERNAME }}\n          password: ${{ secrets.DOCKERHUB_TOKEN }}\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v4\n      - name: Create deploy manifest\n        run: |\n          SOURCES=\"${{ inputs.image_name }}:deploy-amd64\"\n          if [ \"${{ inputs.build_arm }}\" = \"true\" ]; then\n            SOURCES=\"$SOURCES ${{ inputs.image_name }}:deploy-arm64\"\n          fi\n          docker buildx imagetools create \\\n            --tag ${{ inputs.image_name }}:deploy \\\n            --tag ${{ inputs.image_name }}:latest \\\n            $SOURCES\n      - name: Create demo manifest\n        run: |\n          SOURCES=\"${{ inputs.image_name }}:demo-amd64\"\n          if [ \"${{ inputs.build_arm }}\" = \"true\" ]; then\n            SOURCES=\"$SOURCES ${{ inputs.image_name }}:demo-arm64\"\n          fi\n          docker buildx imagetools create \\\n            --tag ${{ inputs.image_name }}:demo $SOURCES\n      - name: Inspect manifests\n        run: |\n          docker buildx imagetools inspect ${{ inputs.image_name }}:deploy\n          docker buildx imagetools inspect ${{ inputs.image_name }}:demo\n"
  },
  {
    "path": ".github/workflows/DeployStaticExecutables.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nname: Deploy static executables\n\non:\n  release:\n    types: [published]\n  workflow_dispatch:\n    inputs:\n      tag_name:\n        type: string\n        required: true\n        description: The name of the release tag\n\njobs:\n  deploy_static_execs:\n    name: Deploy static executables and libraries\n    runs-on: ubuntu-latest\n    env:\n      RELEASE_TAG_NAME: ${{ inputs.tag_name || github.event.release.tag_name }}\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n      # Build static executables in a Docker container with an older version\n      # of Ubuntu so that the glibc is compatible with most Linux distros.\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v4\n      - name: Build static executables\n        uses: docker/build-push-action@v7\n        with:\n          push: false\n          context: .\n          file: \"./containers/Dockerfile.buildenv\"\n          target: deploy_static_execs_and_libs\n          tags: sxs-collaboration/spectre:deploy_static_execs_and_libs\n          platforms: linux/amd64\n          load: true\n          build-args: |\n            UBUNTU_VERSION=18.04\n            PARALLEL_MAKE_ARG=-j4\n      - name: Run Docker container\n        run: >\n          docker run --name static-execs\n          sxs-collaboration/spectre:deploy_static_execs_and_libs\n      - name: Install python dependencies\n        run: >\n          pip3 install numpy h5py gdown\n      - name: Download extra data to include for CCE\n        run: |\n          gdown \\\n          \"https://drive.google.com/uc?id=1CmgLLfuLod8stc13EtmjHUvBoIgSVD-Y\"\n          gdown \\\n          \"https://drive.google.com/uc?id=1mN1oFQ7UcB1wsiXw9dphwAprQqufLia0\"\n          gdown \\\n          \"https://drive.google.com/uc?id=1yYMM4PVUec9pIjKTxI4aCpo0Umqconr8\"\n      - name: Copy CCE executables from the container\n        run: >\n          mkdir CceExecutables;\n          mkdir ./CceExecutables/PreprocessCceWorldtube;\n          mkdir ./CceExecutables/Tests;\n\n          cp ./tests/InputFiles/Cce/CharacteristicExtract.yaml\n          ./CceExecutables/CharacteristicExtract.yaml\n\n          cp\n          ./tests/InputFiles/PreprocessCceWorldtube/PreprocessCceWorldtube.yaml\n          ./CceExecutables/PreprocessCceWorldtube/PreprocessCceWorldtube.yaml\n\n          cp\n          ./tests/InputFiles/PreprocessCceWorldtube/AdmFirstOrderDriverPreprocessCceWorldtube.yaml\n          ./CceExecutables/PreprocessCceWorldtube/AdmFirstOrderDriverPreprocessCceWorldtube.yaml\n\n          cp\n          ./tests/InputFiles/PreprocessCceWorldtube/AdmSecondOrderDriverPreprocessCceWorldtube.yaml\n          ./CceExecutables/PreprocessCceWorldtube/AdmSecondOrderDriverPreprocessCceWorldtube.yaml\n\n          docker cp\n          static-execs:/work/spectre/build/bin/CharacteristicExtract\n          ./CceExecutables/\n\n          docker cp static-execs:/work/spectre/build/bin/PreprocessCceWorldtube\n          ./CceExecutables/PreprocessCceWorldtube/\n\n          docker cp\n          static-execs:/work/spectre/build/bin/WriteCceWorldtubeCoordsToFile\n          ./CceExecutables/PreprocessCceWorldtube/\n      - name: Test CCE executable outside of container\n        run: |\n          mv BondiSachsCceR0200.h5 ./CceExecutables/Tests/\n          mv CheckCceOutput.py ./CceExecutables/Tests/\n          mv CharacteristicExtractReduction_Expected.h5 \\\n          ./CceExecutables/Tests/\n\n          sed -i 's/CceR0257/Tests\\/BondiSachsCceR0200/g' \\\n          ./CceExecutables/CharacteristicExtract.yaml\n\n          cd ./CceExecutables/\n\n          ./CharacteristicExtract \\\n          --input-file ./CharacteristicExtract.yaml\n          python ./Tests/CheckCceOutput.py\n          rm CharacteristicExtractReduction.h5\n\n          cd ../\n      - name: Create CCE executables release asset\n        # Note: We use xz compression since it's much better than gzip, even\n        # though it's a decent bit slower. Specifically, xz is two thirds the\n        # size of gzip.\n        run:  |\n          tar cJf CceExecutables.tar.xz CceExecutables\n      - name: Upload to release\n        uses: softprops/action-gh-release@v3\n        with:\n          tag_name: ${{ env.RELEASE_TAG_NAME }}\n          fail_on_unmatched_files: true\n          files: |\n            CceExecutables.tar.xz\n"
  },
  {
    "path": ".github/workflows/NewContributors.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Add new contributors when their first PR was merged\nname: New contributors\n\non:\n  pull_request_target:\n    branches: [develop]\n    types: [closed]\n\njobs:\n  new_contributors:\n    if: github.event.pull_request.merged == true\n    name: Notify\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v5\n      - name: Check PR author is new\n        id: check_pr_author\n        shell: python\n        run: |\n          import yaml\n\n          def all_authors():\n            authors = yaml.safe_load(open('Metadata.yaml', 'r'))['Authors']\n            for tier in ['Core', 'Developers', 'Contributors']:\n              for author in authors[tier]['List']:\n                yield author['GitHub']\n\n          pr_author = '${{ github.event.pull_request.user.login }}'\n          if pr_author not in all_authors():\n            print('::set-output name=is_new::true')\n      - name: Comment on PR\n        if: steps.check_pr_author.outputs.is_new\n        uses: peter-evans/create-or-update-comment@v5\n        with:\n          issue-number: ${{ github.event.pull_request.number }}\n          body: >  # Newlines are line breaks, body renders as GitHub markdown\n            @${{ github.event.pull_request.user.login }} looks like this is your\n            first contribution to SpECTRE. Welcome! 🎉 Your contribution\n            is much appreciated, and we invite you to add your name to the\n            author list:\n\n\n            1. Edit the [`Metadata.yaml`](https://github.com/${{ github.event.pull_request.user.login }}/spectre/blob/develop/Metadata.yaml)\n            file in the repository.\n\n            2. Add an entry to the list in `Authors.Contributors` with your\n            name, affiliation, etc. Note that the list is ordered\n            alphabetically by last name.\n\n            3. Commit the change on a new branch and open a pull request with\n            the change.\n\n\n            Once the pull request is merged, your name will appear on the\n            [SpECTRE DOI on Zenodo](https://doi.org/10.5281/zenodo.4290404)\n            with the next public release.\n"
  },
  {
    "path": ".github/workflows/PostRelease.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Automations that run on published releases\nname: Post release\n\non:\n  release:\n    types: [published]\n  workflow_dispatch:\n    inputs:\n      tag_name:\n        description: 'Tag name of the release'\n        required: true\n\njobs:\n  add_spack_version:\n    name: Add version to Spack\n    # Checksum the new release and send a PR to Spack that adds the new version.\n    # The 'sxs-bot' GitHub account is the author of the commit and the PR.\n    # - This job requires a repository secret named `GH_TOKEN_SXSBOT_SPACK` that\n    #   has write access to the `sxs-bot/spack` repository.\n    runs-on: ubuntu-latest\n    env:\n      RELEASE_TAG_NAME: ${{ inputs.tag_name || github.event.release.tag_name }}\n    steps:\n      - name: Download Spack\n        uses: actions/checkout@v5\n        with:\n          repository: spack/spack\n          fetch-depth: 0\n          token: ${{ secrets.GH_TOKEN_SXSBOT_SPACK }}\n      - name: Add spack-python to path\n        run: |\n          echo \"$GITHUB_WORKSPACE/bin\" >> $GITHUB_PATH\n      - name: Checksum new version\n        # Download and checksum the release tarball, and add a line to Spack's\n        # `spectre/package.py`` file with the new version.\n        #\n        # The package file defines versions in lines like this:\n        #\n        #    version('develop', branch='develop')\n        #    version('2022.01.03', sha256='872a0d1...')\n        #    ...\n        #\n        # The `spack.util.format.get_version_lines` function returns a string\n        # with lines like this for the new version(s), including the correct\n        # indentation. Therefore, we insert the new lines into the file right\n        # below the 'develop' version.\n        shell: spack-python {0}\n        run: |\n          import spack.spec\n          import spack.stage\n          import spack.repo\n          import spack.util.format\n          import os\n\n          # The tag name includes the 'v' prefix\n          release_name = '${{ env.RELEASE_TAG_NAME }}'[1:]\n\n          # Get the URL for the release tarball. The\n          # 'github.event.release.tarball_url' points to a slightly different\n          # 'https://api.github.com/...' URL, which has a different checksum\n          # than the tarball listed on the GitHub release page and used by\n          # Spack.\n          pkg_cls = spack.repo.PATH.get_pkg_class('spectre')\n          pkg = pkg_cls(spack.spec.Spec('spectre'))\n          release_url = pkg.url_for_version(release_name)\n\n          url_dict = {\n              release_name: release_url,\n          }\n          version_hashes = spack.stage.get_checksums_for_versions(\n              url_dict,\n              pkg.name,\n              fetch_options=pkg.fetch_options,\n          )\n          version_lines = spack.util.format.get_version_lines(\n            version_hashes, url_dict\n          )\n\n          package_file = os.path.join(pkg.package_dir, 'package.py')\n          with open(package_file, 'r') as open_package_file:\n            package_file_lines = open_package_file.readlines()\n          with open(package_file, 'w') as open_package_file:\n            for line in package_file_lines:\n              open_package_file.write(line)\n              if line.strip().startswith('version(\"develop\"'):\n                open_package_file.write(version_lines + \"\\n\")\n      - name: Fix code formatting\n        run: |\n          spack style --fix `git diff --name-only`\n        # Formatting can also be fixed later by Spack maintainers in the PR, so\n        # it's fine if it fails here for some reason\n        continue-on-error: true\n      - name: Print diff\n        run: |\n          git diff\n      - name: Send pull request to Spack\n        uses: peter-evans/create-pull-request@v8\n        with:\n          token: ${{ secrets.GH_TOKEN_SXSBOT_SPACK }}\n          commit-message: \"spectre: add ${{ env.RELEASE_TAG_NAME }}\"\n          committer: \"sxs-bot <sxs-bot@black-holes.org>\"\n          branch: \"spectre-${{ env.RELEASE_TAG_NAME }}\"\n          delete-branch: true\n          push-to-fork: sxs-bot/spack\n          title: \"spectre: add ${{ env.RELEASE_TAG_NAME }}\"\n          body: \"\"\n  # Build and push the `demo` container to DockerHub on releases. This is\n  # because the `demo` container has pre-built executables that we want to keep\n  # up to date.\n  docker:\n    uses: ./.github/workflows/DemoContainer.yaml\n    secrets: inherit\n"
  },
  {
    "path": ".github/workflows/Spack.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Installation test with the Spack package manager\nname: Spack\n\non:\n  # Could run this on a schedule once Spack is stable enough to not break every\n  # week.\n  # schedule:\n  #   - cron: '0 0 * * 1' # every Monday morning\n  workflow_dispatch:\n\nconcurrency:\n  group: spack\n  cancel-in-progress: true\n\njobs:\n  spack_install:\n    name: Install\n    strategy:\n      matrix:\n        host: [ubuntu-latest, macos-latest]\n        compiler: [gcc, llvm, apple-clang]\n        exclude:\n          - host: ubuntu-latest\n            compiler: apple-clang\n        version:\n          # A non-exhaustive set of versions to test, e.g., versions listed in\n          # published papers.\n          - 'develop'\n          - '2021.12.15'\n      fail-fast: false\n    runs-on: ${{ matrix.host }}\n    env:\n      # This is the configuration (\"spec\") that we'll install with Spack\n      # - Build a subset of executables, and Python bindings\n      # - Disable debug symbols to fit in the memory of the GitHub Actions VM\n      # - Select the 'multicore' backend for Charm++, since we're running on a\n      #   single node.\n      # - Select HDF5 without MPI to avoid compiling MPI.\n      SPECTRE_SPEC: >-  # Line breaks are spaces, no trailing newline\n        spectre@${{ matrix.version }}\n          executables=SolvePoisson1D\n          +python\n          ~debug_symbols\n          ^${{ matrix.compiler }}\n          ^charmpp backend=multicore\n          ^hdf5~mpi\n      SPACK_COLOR: always\n    steps:\n      - name: Install compiler\n        if: matrix.host == 'macos-latest' && matrix.compiler == 'llvm'\n        run: |\n          brew install llvm\n          echo \"$(brew --prefix llvm)/bin\" >> $GITHUB_PATH\n      - name: Install Spack\n        run: |\n          git clone -c feature.manyFiles=true --depth=1 \\\n            https://github.com/spack/spack.git\n          echo $PWD/spack/bin >> $GITHUB_PATH\n      - name: Configure Spack\n        run: |\n          spack debug report\n          spack compiler find\n          spack compiler info ${{ matrix.compiler }}\n          spack external find\n      - name: Print packages to install\n        run: |\n          spack spec -I ${SPECTRE_SPEC}\n      - name: Install SpECTRE\n        run: |\n          spack install --show-log-on-error ${SPECTRE_SPEC}\n"
  },
  {
    "path": ".github/workflows/Tests.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Continuous integration tests that pull requests are required to pass. This\n# workflow can also be dispatched manually to tag and release versions.\nname: Tests\n\n# Set any defaults for the runs below.\n# - use bash as the default shell since this is almost certainly what\n#   is always expected. We use regular expressions in a few places\n#   that rely on bash.\ndefaults:\n  run:\n    shell: bash\n\n# Note that by default the jobs only run on the base repository, testing pull\n# requests and merge commits. Enable GitHub Actions in your fork's repository\n# settings to also run the tests on every push to one of your branches.\non:\n  # We run all jobs when pull requests are opened, commits are pushed, or pull\n  # requests are re-opened after being closed.\n  # The jobs triggered by this event run on the base repository of the pull\n  # request, so they have access to its caches.\n  pull_request:\n  # We run those jobs that require no information about a pull request (e.g.\n  # unit tests) also on `push` events. This setup tests merge commits into\n  # `develop` and also builds up caches on `develop` that can be re-used by PRs.\n  # It also runs the jobs on forks if they have GitHub Actions enabled.\n  push:\n    branches-ignore:\n      - gh-pages\n  # Allow running the workflow manually to run tests and optionally release a\n  # version on success (see the dev guide on \"Automatic versioning\")\n  workflow_dispatch:\n    inputs:\n      release_version:\n        description: >\n          Enter a version name YYYY.MM.DD[.TWEAK] to create a release on success\n        required: false\n        default: ''\n      timezone:\n        description: >\n          Timezone used for validating the version name. The release must be\n          approved by the end of the day in the specified timezone. See\n          /usr/share/zoneinfo for a list of possible values.\n        required: false\n        default: 'America/Los_Angeles'\n      clear_ccache:\n        description: >\n          Enter 'yes' without quotes to clear ccache before running\n        required: false\n        default: ''\n      container:\n        description: >\n          Container to use for builds\n        required: false\n        default: 'sxscollaboration/spectre:dev'\n  # Allow running this workflow as a job in another workflow. This is used to\n  # test a new Docker container before publishing it.\n  workflow_call:\n    inputs:\n      container:\n        description: >\n          Container to use for builds\n        required: false\n        type: string\n        default: 'sxscollaboration/spectre:dev'\n\n# Cancel all other queued or in-progress runs of this workflow when it is\n# scheduled, so repeated pushes to a branch or a PR don't block CI. Repeated\n# pushes to 'develop' and 'release' are not canceled, so every merge commit is\n# tested.\nconcurrency:\n  group: ${{ github.ref }}\n  cancel-in-progress: ${{ github.ref != 'refs/heads/develop'\n    && github.ref != 'refs/heads/release' }}\n\njobs:\n  # Make sure no commits are prefixed with `fixup` or similar keywords. See\n  # `tools/CheckCommits.sh` for details.\n  check_commits:\n    name: Commits\n    # Only run on pull requests since we don't check _all_ commits, but only\n    # those that came after the PR's base ref.\n    if: github.event_name == 'pull_request'\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n      - name: Check commits\n        # `CheckCommits.sh` tests against the local `develop` branch, so that's\n        # where we fetch the pull-request's base-branch to. Typically, it is\n        # the upstream `sxs-collaboration/spectre/develop` branch.\n        run: >\n          cd $GITHUB_WORKSPACE\n\n          git remote add upstream\n          https://github.com/${{ github.repository }}.git\n\n          git remote -v\n\n          git fetch upstream ${{ github.base_ref }}:develop\n\n          ./tools/CheckCommits.sh\n\n  # - Run simple textual checks over files in the repository, e.g. checking for\n  #   a license, line length limits etc. See `tools/CheckFiles.sh` for details.\n  # - Run format checker for python to make sure the code is formatted correctly\n  # - Check the metadata are consistent\n  check_files_and_formatting:\n    name: Files and formatting\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ inputs.container || 'sxscollaboration/spectre:dev' }}\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n      # Work around https://github.com/actions/checkout/issues/760\n      - name: Trust checkout\n        run: |\n          git config --global --add safe.directory $GITHUB_WORKSPACE\n      # The action above checks out the `github.ref` by default, which points to\n      # the merge commit with the target branch for pull-request events. For\n      # this job we check out the pull-request HEAD instead. It makes\n      # git-related issues easier to debug because the state matches the local\n      # repository. It also prevents releases that happened since the\n      # pull-request branch was last rebased from disrupting tests that involve\n      # the latest release tag.\n      - name: Checkout pull-request HEAD\n        if: github.event_name == 'pull_request'\n        run: |\n          git checkout ${{ github.event.pull_request.head.sha }}\n      # Some tests involve release tags, which may not have been pushed to\n      # forks. Fetching them here.\n      - name: Fetch upstream tags on forks\n        if: github.repository != 'sxs-collaboration/spectre'\n        run: |\n          git fetch --tags https://github.com/sxs-collaboration/spectre\n      - name: Install Python dependencies\n        run: |\n          pip3 install -r .github/scripts/requirements-release.txt\n          pip3 install -r support/Python/dev_requirements.txt\n      - name: Test tools\n        run: |\n          python3 -m unittest discover -p 'Test_CompileReleaseNotes.py' \\\n            tests.tools -v\n      - name: Check Python formatting\n        run: |\n          cd $GITHUB_WORKSPACE\n          echo \"Using 'black' to check Python formatting...\"\n          black --check --extend-exclude '/external/' .\n          echo \"Using 'isort' to check Python formatting...\"\n          isort --check-only --extend-skip external .\n      - name: Test script\n        run: |\n          cd $GITHUB_WORKSPACE\n          ./tools/CheckFiles.sh --test\n      - name: Check files\n        run: |\n          cd $GITHUB_WORKSPACE\n          ./tools/CheckFiles.sh\n      - name: Check metadata\n        run: |\n          python3 tools/CheckMetadata.py\n      - name: Check the metadata is consistent with the releases\n        # No need to check this on forks. They would need to set a Zenodo token\n        # for this test. Also disable on PRs because they don't have access to\n        # the repo's secrets.\n        if: >\n          github.repository == 'sxs-collaboration/spectre'\n            && github.event_name != 'pull_request'\n        run: |\n          python3 .github/scripts/Release.py prepare -vv --check-only \\\n            --zenodo-token ${{ secrets.ZENODO_READONLY_TOKEN }} \\\n            --github-token ${{ secrets.GITHUB_TOKEN }}\n          python3 .github/scripts/Release.py publish -vv --check-only \\\n            --zenodo-token ${{ secrets.ZENODO_READONLY_TOKEN }} \\\n            --github-token ${{ secrets.GITHUB_TOKEN }} \\\n            --auto-publish\n      - name: Check release notes\n        run: |\n          python3 tools/CompileReleaseNotes.py -vv -o release_notes.md \\\n            --github-token ${{ secrets.GITHUB_TOKEN }}\n      - name: Upload release notes\n        uses: actions/upload-artifact@v7\n        with:\n          name: release-notes\n          path: release_notes.md\n      # GitHub doesn't display artifacts until the workflow has completed, so we\n      # print the release notes here to be able to review them before approving\n      # a release\n      - name: Print release notes\n        run: |\n          cat release_notes.md\n\n  # Lint with clang-tidy. We check only code that changed relative to the\n  # nearest common ancestor commit with `sxs-collaboration/spectre/develop`.\n  clang_tidy:\n    name: Clang-tidy\n    if: >\n      (github.event_name == 'pull_request'\n       && github.repository == 'sxs-collaboration/spectre'\n       && github.base_ref == 'develop')\n      || github.ref != 'refs/heads/develop'\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ inputs.container || 'sxscollaboration/spectre:dev' }}\n    strategy:\n      matrix:\n        build_type: [Debug, Release]\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n      # Work around https://github.com/actions/checkout/issues/760\n      - name: Trust checkout\n        run: |\n          git config --global --add safe.directory $GITHUB_WORKSPACE\n      # Some cpp20 features aren't supported until clang-tidy-16. See:\n      # https://stackoverflow.com/questions/46114214/lambda-implicit-capture-fails-with-variable-declared-from-structured-binding#comment135007519_46115028\n      # The clang-tidy-diff tool doesn't report exit codes until version\n      # 18.1.0, so we grab the version that does:\n      # https://github.com/llvm/llvm-project/commit/4294bca5e4f6e6e8cfdbd9fbe8751c5e5415fd47\n      - name: Install clang-tidy-16\n        run: |\n          apt-get update -y\n          apt-get remove -y clang-tidy clang-tidy-14\n          apt-get install -y clang-tidy-16\n          ln -s /usr/bin/clang-tidy-16 /usr/bin/clang-tidy\n          wget https://raw.githubusercontent.com/llvm/llvm-project/4294bca5e4f6e6e8cfdbd9fbe8751c5e5415fd47/clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py\n          chmod +x clang-tidy-diff.py\n          mv clang-tidy-diff.py /usr/bin/clang-tidy-diff.py\n          ln -s /usr/bin/clang-tidy-diff.py /usr/bin/clang-tidy-diff\n      - name: Configure annotations\n        # Has to be accessible outside the container, see issue:\n        # https://github.com/actions/toolkit/issues/305\n        run: |\n          cp .github/problem_matchers/ClangTidy.json \"$HOME/\"\n          echo \"::add-matcher::$HOME/ClangTidy.json\"\n      - name: Fetch sxs-collaboration/spectre/develop\n        run: >\n          cd $GITHUB_WORKSPACE\n\n          git remote add upstream\n          https://github.com/sxs-collaboration/spectre.git\n\n          git remote -v\n\n          git fetch upstream develop\n      - name: Configure with cmake\n        run: >\n          mkdir build && cd build\n\n          cmake\n          -D CMAKE_C_COMPILER=clang-14\n          -D CMAKE_CXX_COMPILER=clang++-14\n          -D CMAKE_Fortran_COMPILER=gfortran\n          -D CHARM_ROOT=${CHARM_ROOT}\n          -D CMAKE_BUILD_TYPE=${{ matrix.build_type }}\n          -D OVERRIDE_ARCH=x86-64\n          -D USE_CCACHE=OFF\n          -D USE_PCH=OFF\n          -D DEBUG_SYMBOLS=OFF\n          -D BUILD_PYTHON_BINDINGS=ON\n          -D SPECTRE_AUTODIFF=ON\n          -D SPECTRE_FETCH_MISSING_DEPS=ON\n          $GITHUB_WORKSPACE\n\n          make -j4 module_All Libsharp-external\n      - name: Check clang-tidy\n        run: >\n          UPSTREAM_HASH=$(git merge-base HEAD upstream/develop)\n\n          echo \"Running clang-tidy relative to: $UPSTREAM_HASH\\n\"\n\n          git diff -U0 $UPSTREAM_HASH |\n            clang-tidy-diff -path build -p1 -use-color -j4 \\\n            -extra-arg=-I/usr/include/hdf5/serial \\\n            -extra-arg=-I$GITHUB_WORKSPACE/build/_deps/autodiff-src\n\n\n  # Build the documentation and check for problems, then upload as a workflow\n  # artifact and deploy to gh-pages.\n  doc_check:\n    name: Documentation\n    needs: check_files_and_formatting\n    runs-on: ubuntu-latest\n    container:\n      image: ${{ inputs.container || 'sxscollaboration/spectre:dev' }}\n      env:\n        CCACHE_DIR: /work/ccache\n        CCACHE_READONLY: 1\n        CCACHE_COMPILERCHECK: content\n        CCACHE_BASEDIR: $GITHUB_WORKSPACE\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n      # Work around https://github.com/actions/checkout/issues/760\n      - name: Trust checkout\n        run: |\n          git config --global --add safe.directory $GITHUB_WORKSPACE\n      # These can be installed directly in the Docker container instead\n      - name: Install Python dependencies\n        run: |\n          python3 -m pip install -r support/Python/dev_requirements.txt\n      - name: Download release notes\n        uses: actions/download-artifact@v8\n        id: release-notes\n        with:\n          name: release-notes\n          path: /work\n      - name: Append release notes to changelog\n        # The sed command escapes @ symbols at beginning of words (GitHub\n        # usernames) so they aren't interpreted as Doxygen commands\n        run: |\n          echo \"\" >> docs/Changelog.md\n          cat /work/release_notes.md | sed 's/\\(\\B\\@\\)/\\\\\\1/g' \\\n            >> docs/Changelog.md\n      - name: Restore ccache\n        uses: actions/cache/restore@v5\n        id: restore-ccache\n        env:\n          CACHE_KEY_PREFIX: ccache-gcc-11-Debug-pch-ON\n        with:\n          path: /work/ccache\n          key: \"${{ env.CACHE_KEY_PREFIX }}-${{ github.run_id }}\"\n          restore-keys: |\n            ${{ env.CACHE_KEY_PREFIX }}-\n      # - Make sure to use the same build configuration as the unit tests from\n      #   which we restore the ccache.\n      # - Set `BUILD_TESTING=OFF` to test a CMake configuration with tests\n      #   turned off.\n      - name: Configure with cmake\n        run: >\n          mkdir build && cd build\n\n          cmake\n          -D CMAKE_C_COMPILER=gcc-11\n          -D CMAKE_CXX_COMPILER=g++-11\n          -D CMAKE_Fortran_COMPILER=gfortran-11\n          -D CMAKE_CXX_FLAGS=\"-Werror\"\n          -D OVERRIDE_ARCH=x86-64\n          -D CHARM_ROOT=${CHARM_ROOT}\n          -D CMAKE_BUILD_TYPE=Debug\n          -D SPECTRE_DEBUG_Og=ON\n          -D DEBUG_SYMBOLS=OFF\n          -D USE_PCH=ON\n          -D USE_XSIMD=ON\n          -D USE_CCACHE=ON\n          -D ENABLE_OPENMP=ON\n          -D BUILD_PYTHON_BINDINGS=ON\n          -D BUILD_SHARED_LIBS=ON\n          -D MEMORY_ALLOCATOR=SYSTEM\n          -D BUILD_DOCS=ON\n          -D BUILD_TESTING=OFF\n          -D SPECTRE_AUTODIFF=ON\n          -D SPECTRE_FETCH_MISSING_DEPS=ON\n          $GITHUB_WORKSPACE\n      - name: Check documentation\n        working-directory: build\n        run: |\n          make doc-check\n      # Re-build with coverage information on pushes to develop for deployment\n      # to gh-pages.\n      - name: Build documentation with coverage\n        if: github.event_name == 'push' && github.ref == 'refs/heads/develop'\n        working-directory: build\n        run: |\n          make doc-coverage\n      - name: Build Python docs\n        working-directory: build\n        run: |\n          make py-docs -j4\n      # Upload as an artifact to make available to deployment and to PRs\n      - name: Prepare for upload\n        working-directory: build\n        run: |\n          tar -cf docs-html.tar --directory docs/html .\n      - name: Upload documentation\n        uses: actions/upload-artifact@v7\n        with:\n          name: docs-html\n          path: build/docs-html.tar\n\n  # Deploy to gh-pages on pushes to develop\n  # See docs: https://github.com/actions/deploy-pages\n  docs-deploy:\n    name: Deploy documentation\n    if: github.event_name == 'push' && github.ref == 'refs/heads/develop'\n    needs: doc_check\n    runs-on: ubuntu-latest\n    permissions:\n      pages: write\n      id-token: write\n    environment:\n      name: github-pages\n      url: ${{ steps.deploy.outputs.page_url }}\n    steps:\n      - uses: actions/deploy-pages@v5\n        id: deploy\n        with:\n          artifact_name: docs-html\n\n  # Build all test executables and run unit tests on a variety of compiler\n  # configurations.\n  unit_tests:\n    name: Unit tests\n    runs-on: ubuntu-latest\n    timeout-minutes: 345\n    strategy:\n      fail-fast: false\n      matrix:\n        # We have a sparse clang test configuration to reduce the amount of\n        # GitHub cache space we use. GCC being the production compiler on\n        # supercomputers means we need to thoroughly test it.\n        compiler:\n          - gcc-10\n          - gcc-11\n          - gcc-13\n        build_type: [Debug, Release]\n        include:\n          # Generate code coverage report for a single build\n          # Note: currently disabled because it exceeds the available disk space\n          # - compiler: gcc-9\n          #   build_type: Debug\n          #   COVERAGE: ON\n          #   TEST_TIMEOUT_FACTOR: 3\n          # This configuration seems to run consistently slower than newer gcc\n          # or clang builds, so we increase the test timeout a bit\n          - compiler: gcc-10\n            build_type: Debug\n            TEST_TIMEOUT_FACTOR: 2\n            # Test 3D rendering with ParaView\n            # Note: currently disabled because of some unknown upstream issue\n            # test_3d_rendering: ON\n            # Need Python version consistent with ParaView\n            PYTHON_VERSION: \"3.9\"\n          # Test with Kokkos enabled but without CUDA\n          - compiler: gcc-10\n            build_type: Release\n            KOKKOS: ON\n            # Disable FMA optimizations to avoid this issue:\n            # https://github.com/sxs-collaboration/spectre/issues/5145\n            EXTRA_CXX_FLAGS: \"-mno-fma\"\n          # Don't modify the gcc-11 Debug build so its cache can be reused for\n          # the documentation build\n          # - compiler: gcc-11\n          #   build_type: Debug\n          # Test with Python 3.8 so that we retain backwards compatibility. We\n          # keep track of Python versions on supercomputers in this issue:\n          # https://github.com/sxs-collaboration/spectre/issues/442\n          - compiler: gcc-11\n            build_type: Release\n            PYTHON_VERSION: \"3.8\"\n            # Disable building executable for this build for now because it\n            # exceeds the available memory. See issue:\n            # https://github.com/sxs-collaboration/spectre/issues/5472\n            test_executables: OFF\n          # Test building with static libraries. Do so with clang in release\n          # mode because these builds use up little disk space compared to GCC\n          # builds or clang Debug builds\n          - compiler: clang-13\n            build_type: Release\n            BUILD_SHARED_LIBS: OFF\n            use_xsimd: ON\n            MEMORY_ALLOCATOR: JEMALLOC\n          # Add a test without PCH to the build matrix, which only builds core\n          # libraries. Building all the tests without the PCH takes very long\n          # and the most we would catch is a missing include of something that's\n          # in the PCH.\n          # Use this test also to build and test all input files with \"normal\"\n          # or higher priority. The other configurations only test input files\n          # with \"high\" priority to reduce the total build time.\n          - compiler: clang-13\n            build_type: Debug\n            use_pch: OFF\n            unit_tests: OFF\n            input_file_tests_min_priority: \"normal\"\n          # Test with ASAN\n          - compiler: clang-14\n            build_type: Debug\n            # When building with ASAN we also need python bindings to be\n            # disabled because otherwise we get link errors. See issue:\n            # https://github.com/sxs-collaboration/spectre/issues/1890\n            # So we are also using this build to test building without Python\n            # bindings enabled.\n            ASAN: ON\n            BUILD_PYTHON_BINDINGS: OFF\n            MEMORY_ALLOCATOR: JEMALLOC\n            TEST_TIMEOUT_FACTOR: 2\n          - compiler: clang-14\n            build_type: Release\n            # Test compatibility with oldest supported CMake version\n            CMAKE_VERSION: \"3.18.2\"\n            # Use an MPI version of Charm\n            CHARM_ROOT: /work/charm_7_0_0/mpi-linux-x86_64-smp-clang\n            # MPI running tests is a bit slower than multicore\n            TEST_TIMEOUT_FACTOR: 3\n            # If MPI should be tested\n            USE_MPI: ON\n            # Test `install` target with clang in Release mode because it uses\n            # little disk space\n            install: ON\n          # Test compiling with nvcc and CUDA\n          - compiler: nvcc-12-6\n            SPECTRE_AUTODIFF: OFF\n            CUDA: ON\n            KOKKOS: ON\n            # Compile for NVIDIA Ampere architecture. Can't run this on\n            # GitHub-hosted runners because they don't have the GPU hardware,\n            # but we can test the compilation.\n            KOKKOS_ARCH: \"AMPERE80\"\n            # Compile in Release mode to speed up the build\n            build_type: Release\n            # Build with static libs because Kokkos CUDA doesn't support shared\n            # libs\n            BUILD_SHARED_LIBS: OFF\n            # Build only a subset of the code because compiling the full code is\n            # too slow and exceeds the available memory. We should extend this\n            # to the full code once we have a better solution for the memory\n            # issue. See 'Test nvcc support' step below.\n            unit_tests: OFF\n            test_executables: OFF\n            # Enable PlaneWave3D.yaml input file test\n            input_file_tests_min_priority: \"normal\"\n            # Disable pybindings until they support nvcc\n            BUILD_PYTHON_BINDINGS: OFF\n            # Disable warnings as nvcc emits a lot of them from system headers\n            ENABLE_WARNINGS: OFF\n\n    container:\n      image: ${{ inputs.container || 'sxscollaboration/spectre:dev' }}\n      env:\n        # We make sure to use a fixed absolute path for the ccache directory\n        CCACHE_DIR: /work/ccache\n        # Use a separate temp directory to conserve cache space on GitHub\n        CCACHE_TEMPDIR: /work/ccache-tmp\n        # Control the max cache size. We evict unused entries in a step below to\n        # make sure that each build only uses what it need of this max size.\n        CCACHE_MAXSIZE: \"2G\"\n        # Control the compression level. The ccache docs recommend at most level\n        # 5 to avoid slowing down compilation.\n        CCACHE_COMPRESS: 1\n        CCACHE_COMPRESSLEVEL: 5\n        # We hash the content of the compiler rather than the location and mtime\n        # to make sure the cache works across the different machines\n        CCACHE_COMPILERCHECK: content\n        # Rewrite absolute paths starting with this base dir to relative paths\n        # before hashing. This is needed to reuse the cache for the formaline\n        # test below, which builds in a different directory.\n        CCACHE_BASEDIR: $GITHUB_WORKSPACE\n        # These vars are to allow running charm with MPI as root inside the\n        # container which arises from using the \"--privileged\" flag just below.\n        OMPI_ALLOW_RUN_AS_ROOT: 1\n        OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1\n        # The number of cores to run on. This is given at:\n        # https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories\n        NUMBER_OF_CORES: 4\n      # See https://lists.cs.illinois.edu/lists/arc/charm/2018-10/msg00011.html\n      # for why we need this\n      options: --privileged\n    steps:\n      - name: Record start time\n        id: start\n        run: |\n          echo \"time=$(date +%s)\" >> \"$GITHUB_OUTPUT\"\n      - name: Checkout repository\n        uses: actions/checkout@v5\n      # Work around https://github.com/actions/checkout/issues/760\n      - name: Trust checkout\n        run: |\n          git config --global --add safe.directory $GITHUB_WORKSPACE\n      - uses: ./.github/actions/parse-compiler\n        with:\n          compiler: ${{ matrix.compiler }}\n      # Install the selected compiler. We don't bundle all of them in the\n      # container because they take up a lot of space.\n      - name: Install compiler\n        run: |\n          add-apt-repository ppa:ubuntu-toolchain-r/test\n          apt-get update -y\n          if [[ $COMPILER_ID = gcc ]]; then\n            apt-get install -y $CC $CXX $FC\n          elif [[ $COMPILER_ID = clang ]]; then\n            apt-get install -y $CC $FC\n          elif [[ $COMPILER_ID = nvcc ]]; then\n            # Install CUDA\n            wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb\n            dpkg -i cuda-keyring_1.1-1_all.deb\n            apt-get update -y\n            apt-get install -y cuda-nvcc-${COMPILER_VERSION} \\\n              build-essential libc6-dev pkg-config\n            echo \"/usr/local/cuda-${COMPILER_VERSION/-/.}/bin\" >> $GITHUB_PATH\n          fi\n      - name: Install Kokkos\n        if: matrix.KOKKOS == 'ON'\n        working-directory: /work\n        run: |\n          KOKKOS_VERSION=4.4.00\n          wget -O kokkos.tar.gz \"https://github.com/kokkos/kokkos/releases/download/${KOKKOS_VERSION}/kokkos-${KOKKOS_VERSION}.tar.gz\"\n          tar -xzf kokkos.tar.gz && mv kokkos-* kokkos && cd kokkos\n          mkdir build && cd build\n          cmake .. \\\n            -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} \\\n            -D CMAKE_INSTALL_PREFIX=/usr/local \\\n            -D Kokkos_ENABLE_SERIAL=ON \\\n            -D Kokkos_ENABLE_OPENMP=ON \\\n            -D Kokkos_ENABLE_CUDA=${{ matrix.CUDA || 'OFF' }} \\\n            -D Kokkos_ARCH_${{ matrix.KOKKOS_ARCH || 'X86_64' }}=ON \\\n            -D Kokkos_ENABLE_CUDA_CONSTEXPR=ON \\\n            -D Kokkos_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE=ON \\\n            -D CMAKE_POSITION_INDEPENDENT_CODE=ON\n          make -j ${NUMBER_OF_CORES} install\n          cd ../../ && rm -rf kokkos.tar.gz kokkos\n          # Use Kokkos compiler wrapper for nvcc\n          if [[ $COMPILER_ID = nvcc ]]; then\n            echo \"CXX=/usr/local/bin/nvcc_wrapper\" >> $GITHUB_ENV\n          fi\n      # Install specific CMake version if requested\n      - name: Install CMake version\n        if: matrix.CMAKE_VERSION\n        working-directory: /work\n        run: |\n          CMAKE_VERSION=${{ matrix.CMAKE_VERSION }}\n          wget -O cmake-install.sh \"https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh\"\n          sh cmake-install.sh --prefix=/usr --skip-license\n          rm cmake-install.sh\n      - name: Install Python version\n        if: matrix.PYTHON_VERSION\n        run: |\n          add-apt-repository ppa:deadsnakes/ppa\n          apt install -y python${{ matrix.PYTHON_VERSION }}-dev \\\n            python${{ matrix.PYTHON_VERSION }}-venv \\\n            python${{ matrix.PYTHON_VERSION }}-distutils\n          update-alternatives --install /usr/bin/python3 python3 \\\n            /usr/bin/python${{ matrix.PYTHON_VERSION }} 1\n      - name: Install Python dependencies\n        if: matrix.PYTHON_VERSION\n        run: |\n          python${{ matrix.PYTHON_VERSION }} -m pip install \\\n            -r support/Python/requirements.txt \\\n            -r support/Python/dev_requirements.txt\n          python${{ matrix.PYTHON_VERSION }} -m pip list -v\n      - name: Install ParaView\n        if: matrix.test_3d_rendering == 'ON'\n        working-directory: /work\n        # Can't just `apt-get install python3-paraview` because we need the\n        # headless build of ParaView. So we download a binary from paraview.org\n        # (which is built for a specific Python version unfortunately).\n        run: |\n          apt-get install -y libglvnd-dev  # Needed to find 'libglapi.so'\n          wget -O paraview.tar.gz --no-check-certificate \"https://www.paraview.org/paraview-downloads/download.php?submit=Download&version=v5.10&type=binary&os=Linux&downloadFile=ParaView-5.10.1-osmesa-MPI-Linux-Python3.9-x86_64.tar.gz\"\n          tar -xzf paraview.tar.gz\n          rm paraview.tar.gz\n          mv ParaView-* /opt/paraview\n          echo \"/opt/paraview/bin\" >> $GITHUB_PATH\n          # Make 'paraview' Python package available\n          PYTHONPATH=/opt/paraview/lib/python3.9/site-packages:$PYTHONPATH\n          # Give system-installed Python packages priority over ParaView's\n          PYTHONPATH=$pythonLocation/lib/python3.9/site-packages:$PYTHONPATH\n          echo \"PYTHONPATH=$PYTHONPATH\" >> $GITHUB_ENV\n      - name: Install MPI and Charm++\n        if: matrix.USE_MPI == 'ON'\n        working-directory: /work\n        run: |\n          apt-get install -y libopenmpi-dev\n          cd /work/charm_7_0_0 && ./build charm++ mpi-linux-x86_64-smp clang \\\n          -j ${NUMBER_OF_CORES} -g0 -O1 --build-shared --with-production\n\n      # Assign a unique cache key for every run.\n      # - We will save the cache using this unique key, but only on the develop\n      #   branch. This way we regularly update the cache without filling up the\n      #   storage space with caches from other branches.\n      # - To restore the most recent cache we provide a partially-matched\n      #   \"restore key\".\n      - name: Restore ccache\n        uses: actions/cache/restore@v5\n        id: restore-ccache\n        env:\n          CACHE_KEY_PREFIX: \"ccache-${{ matrix.compiler }}-\\\n${{ matrix.build_type }}-pch-${{ matrix.use_pch || 'ON' }}\"\n        with:\n          path: /work/ccache\n          key: \"${{ env.CACHE_KEY_PREFIX }}-${{ github.run_id }}\"\n          restore-keys: |\n            ${{ env.CACHE_KEY_PREFIX }}-\n      - name: Configure ccache\n        # Print the ccache configuration and reset statistics\n        run: |\n          ccache -pz\n      - name: Clear ccache\n        # Clear ccache if requested\n        if: >\n          github.event_name == 'workflow_dispatch'\n            && github.event.inputs.clear_ccache == 'yes'\n        run: |\n          ccache -C\n      - name: Configure build with cmake\n        # Notes on the build configuration:\n        # - We don't need debug symbols during CI, so we turn them off to reduce\n        #   memory usage.\n        # - We run unit tests with the following compiler flags:\n        #   -Werror: Treat warnings as error.\n        #   -march=x86-64: Make sure we are building on a consistent\n        #   architecture so caching works. This is necessary because GitHub may\n        #   run the job on different hardware.\n        run: >\n          mkdir build && cd build\n\n          ENABLE_WARNINGS=${{ matrix.ENABLE_WARNINGS }}\n          WERROR=\"${{ matrix.WERROR != 'OFF' && '-Werror' || '' }}\"\n          CXX_FLAGS=\"${WERROR} ${{ matrix.EXTRA_CXX_FLAGS }}\"\n          PYTHON_VERSION=${{ matrix.PYTHON_VERSION }}\n          BUILD_PYTHON_BINDINGS=${{ matrix.BUILD_PYTHON_BINDINGS }}\n          BUILD_SHARED_LIBS=${{ matrix.BUILD_SHARED_LIBS }}\n          KOKKOS=${{ matrix.KOKKOS }}\n          MATRIX_CHARM_ROOT=${{ matrix.CHARM_ROOT }}\n          ASAN=${{ matrix.ASAN }}\n          MEMORY_ALLOCATOR=${{ matrix.MEMORY_ALLOCATOR }}\n          UBSAN_UNDEFINED=${{ matrix.UBSAN_UNDEFINED }}\n          UBSAN_INTEGER=${{ matrix.UBSAN_INTEGER }}\n          USE_PCH=${{ matrix.use_pch }}\n          USE_XSIMD=${{ matrix.use_xsimd }}\n          COVERAGE=${{ matrix.COVERAGE }}\n          TEST_TIMEOUT_FACTOR=${{ matrix.TEST_TIMEOUT_FACTOR }}\n          INPUT_FILE_MIN_PRIO=${{ matrix.input_file_tests_min_priority }}\n          SPECTRE_AUTODIFF=${{ matrix.SPECTRE_AUTODIFF }}\n\n          cmake --version\n\n          cmake\n          -D CMAKE_C_COMPILER=${CC}\n          -D CMAKE_CXX_COMPILER=${CXX}\n          -D CMAKE_Fortran_COMPILER=${FC}\n          -D Python_EXECUTABLE=/usr/bin/python${PYTHON_VERSION:-'3'}\n          -D CMAKE_CXX_FLAGS=\"${CXX_FLAGS}\"\n          -D OVERRIDE_ARCH=x86-64\n          -D ENABLE_WARNINGS=${ENABLE_WARNINGS:-'ON'}\n          -D CHARM_ROOT=${MATRIX_CHARM_ROOT:-$CHARM_ROOT}\n          -D CMAKE_BUILD_TYPE=${{ matrix.build_type }}\n          -D SPECTRE_DEBUG_Og=ON\n          -D DEBUG_SYMBOLS=OFF\n          -D BACKTRACE_LIB=/usr/local/lib/libbacktrace.a\n          -D BACKTRACE_HEADER_DIR=/usr/local/include\n          -D UNIT_TESTS_IN_TEST_EXECUTABLES=OFF\n          -D SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY=${INPUT_FILE_MIN_PRIO:-'high'}\n          -D STRIP_SYMBOLS=ON\n          -D STUB_EXECUTABLE_OBJECT_FILES=ON\n          -D STUB_LIBRARY_OBJECT_FILES=ON\n          -D USE_PCH=${USE_PCH:-'ON'}\n          -D USE_XSIMD=${USE_XSIMD:-'ON'}\n          -D USE_CCACHE=ON\n          -D ENABLE_OPENMP=ON\n          -D SPECTRE_KOKKOS=${KOKKOS:-'OFF'}\n          -D COVERAGE=${COVERAGE:-'OFF'}\n          -D BUILD_PYTHON_BINDINGS=${BUILD_PYTHON_BINDINGS:-'ON'}\n          -D BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS:-'ON'}\n          -D ASAN=${ASAN:-'OFF'}\n          -D UBSAN_UNDEFINED=${UBSAN_UNDEFINED:-'OFF'}\n          -D UBSAN_INTEGER=${UBSAN_INTEGER:-'OFF'}\n          -D MEMORY_ALLOCATOR=${MEMORY_ALLOCATOR:-'SYSTEM'}\n          -D SPECTRE_UNIT_TEST_TIMEOUT_FACTOR=${TEST_TIMEOUT_FACTOR:-'1'}\n          -D SPECTRE_INPUT_FILE_TEST_TIMEOUT_FACTOR=${TEST_TIMEOUT_FACTOR:-'1'}\n          -D SPECTRE_PYTHON_TEST_TIMEOUT_FACTOR=${TEST_TIMEOUT_FACTOR:-'1'}\n          -D CMAKE_INSTALL_PREFIX=/work/spectre_install\n          -D BUILD_DOCS=OFF\n          -D SPECTRE_AUTODIFF=${SPECTRE_AUTODIFF:-'ON'}\n          -D SPECTRE_FETCH_MISSING_DEPS=ON\n          --warn-uninitialized\n          $GITHUB_WORKSPACE 2>&1 | tee CMakeOutput.txt 2>&1\n      - name: Check for CMake warnings\n        working-directory: build\n        run: |\n          ! grep -A 6 \"CMake Warning\" ./CMakeOutput.txt\n      - name: Build unit tests\n        if: matrix.unit_tests != 'OFF'\n        working-directory: build\n        run: |\n          make -j${NUMBER_OF_CORES} unit-tests\n      - name: Run unit tests\n        if: matrix.unit_tests != 'OFF' && matrix.COVERAGE != 'ON'\n        working-directory: build\n        run: |\n          # We get occasional random timeouts, repeat tests to see if\n          # it is a random timeout or systematic.\n          #\n          # We run ctest -L unit before build test-executables to make\n          # sure that all the unit tests are actually built by the\n          # unit-tests target.\n          ctest -j${NUMBER_OF_CORES} -L unit \\\n              --output-on-failure --repeat after-timeout:3\n      - name: Run unit tests with coverage reporting\n        if: matrix.COVERAGE == 'ON'\n        working-directory: build\n        run: |\n          make unit-test-coverage\n          rm -r docs/html/unit-test-coverage\n      - name: Upload coverage report to codecov.io\n        if: matrix.COVERAGE == 'ON'\n        uses: codecov/codecov-action@v4\n        with:\n          files: build/tmp/coverage.info\n          token: ${{ secrets.CODECOV_TOKEN }}\n          # Display the job as failed if upload fails (defaults to false for\n          # some reason)\n          fail_ci_if_error: true\n        # We currently don't require codecov in our guidelines, so don't fail\n        # the CI build if codecov fails to upload\n        continue-on-error: true\n      # Test only a few unit tests with nvcc for now because compiling the full\n      # suite is too slow and exceed the available memory. We should extend this\n      # to the full suite once we have a better solution for the memory issue.\n      - name: Test nvcc support\n        if: matrix.CUDA == 'ON'\n        working-directory: build\n        run: |\n          make -j${NUMBER_OF_CORES} \\\n            Test_DataStructures \\\n            Test_Tensor \\\n            Test_Kokkos \\\n            Test_Spectral \\\n            Test_Utilities \\\n            EvolveScalarWave3D\n          # Can't run these tests on GitHub-hosted runners because they don't\n          # have GPU hardware.\n          # ./bin/Test_DataStructures\n          # ./bin/Test_Tensor\n          # ./bin/Test_Kokkos\n          # ./bin/Test_Spectral\n          # ./bin/Test_Utilities\n          # ctest -R PlaneWave3D.yaml --output-on-failure\n      # Avoid running out of disk space by cleaning up the build directory\n      - name: Clean up unit tests\n        working-directory: build\n        run: |\n          pwd\n          ls | xargs du -sh\n          du -sh .\n          rm -f bin/Test_*\n      # Build the executables in a single thread to reduce memory usage\n      # sufficiently so they compile on the GitHub-hosted runners\n      - name: Build executables\n        if: matrix.COVERAGE != 'ON' && matrix.test_executables != 'OFF'\n        working-directory: build\n        run: |\n          make test-executables\n      - name: Build Benchmark executable\n        if: matrix.build_type == 'Release'\n        working-directory: build\n        run: |\n          make -j${NUMBER_OF_CORES} Benchmark\n      # Delete unused cache entries before uploading the cache\n      - name: Clean up ccache\n        if: github.ref == 'refs/heads/develop'\n        run: |\n          now=$(date +%s)\n          job_duration=$((now - ${{ steps.start.outputs.time }}))\n          ccache --evict-older-than \"${job_duration}s\"\n      # Save the cache after everything has been built. Also save on failure or\n      # on cancellation (`always()`) because a partial cache is better than no\n      # cache.\n      - name: Save ccache\n        if: always() && github.ref == 'refs/heads/develop'\n        uses: actions/cache/save@v5\n        with:\n          path: /work/ccache\n          key: ${{ steps.restore-ccache.outputs.cache-primary-key }}\n      - name: Print size of build directory\n        working-directory: build\n        run: |\n          pwd\n          ls | xargs du -sh\n          du -sh .\n      - name: Diagnose ccache\n        run: |\n          ccache -s\n      - name: Run non-unit tests\n        if: matrix.COVERAGE != 'ON' && matrix.test_executables != 'OFF'\n        working-directory: build\n        run: |\n          # We get occasional random timeouts, repeat tests to see if\n          # it is a random timeout or systematic\n          #\n          # Only use 2 cores because these tests run more slowly.\n          ctest -j2 -LE unit --output-on-failure \\\n                --repeat after-timeout:3\n      - name: Install\n        if: matrix.install == 'ON'\n        working-directory: build\n        # Make sure the `install` target runs without error. We could add some\n        # basic smoke tests here to make sure the installation worked.\n        run: |\n          make install\n      - name: Print size of install directory\n        if: matrix.install == 'ON'\n        working-directory: /work/spectre_install\n        # Remove files post-install to reduce disk space for later on.\n        run: |\n          pwd\n          ls | xargs du -sh\n          du -sh .\n          rm -r ./*\n      - name: Test formaline tar can be built\n        # - We only run the formaline tests in debug mode to reduce total build\n        #   time in CI. We don't run them with ASAN because then we run out of\n        #   disk space.\n        # - We do run for all compilers, though, because formaline injects data\n        #   at the linking stage, which means we are somewhat tied to the\n        #   compiler version.\n        # - We make sure to use the same compiler flags as the full build above\n        #   so ccache is able to speed up the build.\n        if: >\n          matrix.build_type == 'Debug'\n            && matrix.ASAN != 'ON'\n            && matrix.test_executables != 'OFF'\n        working-directory: build\n        run: >\n          make EvolveBurgers -j${NUMBER_OF_CORES}\n\n          if [ ! -f ./bin/EvolveBurgers ]; then\n            echo \"Could not find the executable EvolveBurgers\";\n            echo \"which we use for testing formaline\";\n            exit 1\n          fi\n\n          # We disable ASAN's leak sanitizer because Charm++ has false\n          # positives that would cause the build to fail. We disable\n          # leak sanitizer for the ctest runs inside CMake anyway.\n\n          ASAN_OPTIONS=detect_leaks=0 ./bin/EvolveBurgers\n          --dump-source-tree-as spectre_src --dump-only\n\n          mkdir spectre_src;\n          mv spectre_src.tar.gz spectre_src;\n          cd spectre_src;\n          tar xf spectre_src.tar.gz;\n          mkdir build-formaline;\n          cd build-formaline\n\n          ENABLE_WARNINGS=${{ matrix.ENABLE_WARNINGS }}\n          WERROR=\"${{ matrix.WERROR != 'OFF' && '-Werror' || '' }}\"\n          CXX_FLAGS=\"${WERROR} ${{ matrix.EXTRA_CXX_FLAGS }}\"\n          PYTHON_VERSION=${{ matrix.PYTHON_VERSION }}\n          BUILD_PYTHON_BINDINGS=${{ matrix.BUILD_PYTHON_BINDINGS }}\n          MATRIX_CHARM_ROOT=${{ matrix.CHARM_ROOT }}\n          MEMORY_ALLOCATOR=${{ matrix.MEMORY_ALLOCATOR }};\n          USE_PCH=${{ matrix.use_pch }};\n          USE_XSIMD=${{ matrix.use_xsimd }}\n\n          cmake\n          -D CMAKE_C_COMPILER=${CC}\n          -D CMAKE_CXX_COMPILER=${CXX}\n          -D CMAKE_Fortran_COMPILER=${FC}\n          -D Python_EXECUTABLE=/usr/bin/python${PYTHON_VERSION:-'3'}\n          -D CMAKE_CXX_FLAGS=\"${CXX_FLAGS}\"\n          -D OVERRIDE_ARCH=x86-64\n          -D ENABLE_WARNINGS=${ENABLE_WARNINGS:-'ON'}\n          -D BUILD_SHARED_LIBS=ON\n          -D CHARM_ROOT=${MATRIX_CHARM_ROOT:-$CHARM_ROOT}\n          -D CMAKE_BUILD_TYPE=${{ matrix.build_type }}\n          -D SPECTRE_DEBUG_Og=ON\n          -D DEBUG_SYMBOLS=OFF\n          -D UNIT_TESTS_IN_TEST_EXECUTABLES=OFF\n          -D STRIP_SYMBOLS=ON\n          -D STUB_EXECUTABLE_OBJECT_FILES=ON\n          -D STUB_LIBRARY_OBJECT_FILES=ON\n          -D USE_PCH=${USE_PCH:-'ON'}\n          -D USE_XSIMD=${USE_XSIMD:-'ON'}\n          -D USE_CCACHE=ON\n          -D BUILD_PYTHON_BINDINGS=${BUILD_PYTHON_BINDINGS:-'ON'}\n          -D MEMORY_ALLOCATOR=${MEMORY_ALLOCATOR:-'SYSTEM'}\n          -D BUILD_DOCS=OFF\n          -D SPECTRE_AUTODIFF=ON\n          -D SPECTRE_FETCH_MISSING_DEPS=ON\n          ..\n\n          make EvolveBurgers -j${NUMBER_OF_CORES}\n\n          # Run on multiple cores to run both the \"parse\" and \"execute\" tests\n          # simultaneously.\n\n          ctest -j${NUMBER_OF_CORES} -R InputFiles.Burgers.Step.yaml\n          --output-on-failure\n\n          cd .. && rm -r build-formaline\n      - name: Test bundled exporter\n        if: matrix.ASAN != 'ON' && matrix.CUDA != 'ON'\n        working-directory: build\n        run: |\n          make -j${NUMBER_OF_CORES} BundledExporter\n          mkdir build-test-exporter && cd build-test-exporter\n          cmake -D SPECTRE_ROOT=$GITHUB_WORKSPACE/build \\\n            $GITHUB_WORKSPACE/tests/Unit/IO/Exporter/BundledExporter\n          make -j${NUMBER_OF_CORES}\n          ./TestSpectreExporter \\\n            $GITHUB_WORKSPACE/tests/Unit/Visualization/Python/VolTestData0.h5 \\\n            element_data 0 Psi 0 0 0 -0.07059806932542323\n          cd .. && rm -r build-test-exporter\n      - name: Diagnose ccache\n        run: |\n          ccache -s\n\n  # Build all test executables and run unit tests on macOS\n  unit_tests_macos:\n    name: Unit tests on macOS\n    timeout-minutes: 345\n    strategy:\n      fail-fast: false\n      matrix:\n        os:\n          - macos-15-intel\n          - macos-15\n    runs-on: ${{ matrix.os }}\n    env:\n      # We install some low-level dependencies with Homebrew. They get picked up\n      # by `spack external find`.\n      SPECTRE_BREW_DEPS: >-  # Line breaks are spaces, no trailing newline\n        autoconf automake boost catch2 ccache cmake gsl hdf5 openblas yaml-cpp xsimd\n      # We install these packages with Spack and cache them. The full specs are\n      # listed below. This list is only needed to create the cache.\n      SPECTRE_SPACK_DEPS: blaze charmpp libxsmm\n      CCACHE_DIR: $HOME/ccache\n      CCACHE_TEMPDIR: $HOME/ccache-tmp\n      CCACHE_MAXSIZE: \"2G\"\n      CCACHE_COMPRESS: 1\n      CCACHE_COMPRESSLEVEL: 5\n      CCACHE_COMPILERCHECK: content\n      SPACK_SKIP_MODULES: true\n      SPACK_COLOR: always\n      NUM_CORES: ${{ matrix.NUM_CORES }}\n    steps:\n      - name: Record start time\n        id: start\n        run: |\n          echo \"time=$(date +%s)\" >> \"$GITHUB_OUTPUT\"\n      - name: Get CPU cores\n        id: get_cores\n        run: |\n          CORES=$(sysctl -n hw.ncpu)\n          echo \"NUM_CORES=$CORES\" >> $GITHUB_ENV\n          echo \"Detect $CORES cores\"\n      - name: Checkout repository\n        uses: actions/checkout@v5\n      - uses: actions/setup-python@v6\n        with:\n          python-version: '3.12'\n      - name: Install Homebrew dependencies\n        # uninstall cmake to avoid conflict between brew taps\n        run: |\n          brew uninstall cmake\n          brew install $SPECTRE_BREW_DEPS\n      # We install the remaining dependencies with Spack and cache them.\n      # See the `unit_tests` job above for details on the cache configuration.\n      - name: Restore dependency cache\n        uses: actions/cache/restore@v5\n        id: restore-dependencies\n        env:\n          CACHE_KEY_PREFIX: \"dependencies-${{ matrix.os }}\"\n        with:\n          path: ~/dependencies\n          key: \"${{ env.CACHE_KEY_PREFIX }}-${{ github.run_id }}\"\n          restore-keys: |\n            ${{ env.CACHE_KEY_PREFIX }}-\n      - name: Install Spack\n        # Pin a specific version of Spack to avoid breaking CI builds when\n        # Spack changes.\n        run: |\n          cd $HOME\n          git clone -c feature.manyFiles=true \\\n            https://github.com/spack/spack.git\n          cd spack\n          git checkout 7d1de58378fa210b7db887964fcc17187a504ad8\n      - name: Configure Spack\n        # - To avoid re-building packages that are already installed by Homebrew\n        #   we let Spack find them.\n        # - Add the dependency cache as binary mirror.\n        run: |\n          source $HOME/spack/share/spack/setup-env.sh\n          spack debug report\n          spack compiler find && spack compiler list\n          spack external find\n          spack config get packages\n          spack mirror add dependencies file://$HOME/dependencies/spack\n      # Install the remaining dependencies from source with Spack. We install\n      # them in an environment that we can activate later. After building the\n      # dependencies from source we cache them as compressed tarballs.\n      - name: Install Spack dependencies\n        run: |\n          source $HOME/spack/share/spack/setup-env.sh\n          spack env create spectre\n          spack env activate spectre\n          spack add blaze@3.8.2 ~blas ~lapack smp=none\n          # Support installing charm 7.0.0 with CMake 4.0\n          export CMAKE_POLICY_VERSION_MINIMUM=3.5\n          spack add charmpp@7.0.0 +shared backend=multicore build-target=charm++\n          # Use main branch until spack has 2.0 release\n          spack add libxsmm@main\n          spack concretize --reuse\n          spack install --no-check-signature\n          spack find -v\n      - name: Update dependency cache\n        if: github.ref == 'refs/heads/develop'\n        run: |\n          source $HOME/spack/share/spack/setup-env.sh\n          # Clear existing buildcache so we don't accumulate old versions of\n          # packages in the cache\n          rm -rf $HOME/dependencies\n          spack buildcache create -uf dependencies $SPECTRE_SPACK_DEPS\n        # Allow the buildcache creation to fail without failing the job, since\n        # it sometimes runs out of memory\n        continue-on-error: true\n      - name: Save dependency cache\n        if: github.ref == 'refs/heads/develop'\n        uses: actions/cache/save@v5\n        with:\n          path: ~/dependencies\n          key: ${{ steps.restore-dependencies.outputs.cache-primary-key }}\n      # Install remaining pure Python dependencies with pip because the Spack\n      # package index can be incomplete (it can't mirror all of pip)\n      - name: Install Python dependencies\n        run: |\n          source $HOME/spack/share/spack/setup-env.sh\n          spack env activate spectre\n          pip install -r support/Python/requirements.txt\n      # Replace the ccache directory that building the dependencies may have\n      # generated with the cached ccache directory.\n      - name: Clear ccache from dependencies\n        run: |\n          ccache --clear\n          rm -rf $CCACHE_DIR\n          mkdir -p $CCACHE_DIR\n      - name: Restore ccache\n        uses: actions/cache/restore@v5\n        id: restore-ccache\n        env:\n          CACHE_KEY_PREFIX: \"ccache-${{ matrix.os }}\"\n        with:\n          path: ~/ccache\n          key: \"${{ env.CACHE_KEY_PREFIX }}-${{ github.run_id }}\"\n          restore-keys: |\n            ${{ env.CACHE_KEY_PREFIX }}-\n      - name: Configure ccache\n        run: |\n          ccache -pz\n      - name: Clear ccache\n        # Clear ccache if requested\n        if: >\n          github.event_name == 'workflow_dispatch'\n            && github.event.inputs.clear_ccache == 'yes'\n        run: |\n          ccache -C\n      # Configure, build and run tests. See the `unit_tests` job above for\n      # details.\n      # - We increase the timeout for tests because the GitHub-hosted macOS VMs\n      #   appear to be quite slow.\n      - name: Configure build with cmake\n        run: |\n          source $HOME/spack/share/spack/setup-env.sh\n          spack env activate spectre\n          mkdir build && cd build\n          cmake \\\n            -D CMAKE_C_COMPILER=clang \\\n            -D CMAKE_CXX_COMPILER=clang++ \\\n            -D CMAKE_Fortran_COMPILER=gfortran-14 \\\n            -D CMAKE_CXX_FLAGS=\"-Werror\" \\\n            -D BUILD_SHARED_LIBS=ON \\\n            -D BUILD_PYTHON_BINDINGS=ON \\\n            -D MEMORY_ALLOCATOR=SYSTEM \\\n            -D CHARM_ROOT=$(spack location --install-dir charmpp) \\\n            -D BLAS_ROOT=$(brew --prefix openblas) \\\n            -D LAPACK_ROOT=$(brew --prefix openblas) \\\n            -D CMAKE_BUILD_TYPE=Debug \\\n            -D SPECTRE_DEBUG_Og=ON \\\n            -D DEBUG_SYMBOLS=OFF \\\n            -D UNIT_TESTS_IN_TEST_EXECUTABLES=OFF \\\n            -D STUB_EXECUTABLE_OBJECT_FILES=ON \\\n            -D STUB_LIBRARY_OBJECT_FILES=ON \\\n            -D USE_PCH=ON \\\n            -D USE_CCACHE=ON \\\n            -D SPECTRE_TEST_TIMEOUT_FACTOR=10 \\\n            -D CMAKE_INSTALL_PREFIX=../install \\\n            -D BUILD_DOCS=OFF \\\n            -D CMAKE_OSX_SYSROOT=macosx \\\n            $GITHUB_WORKSPACE\n      - name: Build unit tests\n        working-directory: build\n        run: |\n          make -j${NUM_CORES} unit-tests\n      - name: Build executables\n        working-directory: build\n        run: |\n          make test-executables\n      - name: Clean up ccache\n        if: github.ref == 'refs/heads/develop'\n        run: |\n          now=$(date +%s)\n          job_duration=$((now - ${{ steps.start.outputs.time }}))\n          ccache --evict-older-than \"${job_duration}s\"\n      - name: Save ccache\n        if: always() && github.ref == 'refs/heads/develop'\n        uses: actions/cache/save@v5\n        with:\n          path: ~/ccache\n          key: ${{ steps.restore-ccache.outputs.cache-primary-key }}\n      - name: Print size of build directory\n        working-directory: build\n        run: |\n          ls | xargs du -sh\n          du -sh .\n      - name: Diagnose ccache\n        run: |\n          ccache -s\n      - name: Run unit tests\n        working-directory: build\n        run: |\n          ctest -E TestingFramework.Abort -j${NUM_CORES} \\\n            --repeat after-timeout:3 --output-on-failure\n      - name: Install\n        working-directory: build\n        run: |\n          make install\n      - name: Print size of install directory\n        working-directory: install\n        run: |\n          ls | xargs du -sh\n          du -sh .\n          rm -r ./*\n\n  # Release a new version on manual events when requested and the tests pass.\n  # Only enable this on the `sxs-collaboration/spectre` repository (not on\n  # forks).\n  release_version:\n    name: Release version\n    # Running in a protected environment that provides the necessary secrets\n    environment: release\n    runs-on: ubuntu-latest\n    if: >\n      github.repository == 'sxs-collaboration/spectre'\n        && github.ref == 'refs/heads/develop'\n        && github.event_name == 'workflow_dispatch'\n        && github.event.inputs.release_version != ''\n    needs:\n      - check_files_and_formatting\n      - doc_check\n      - unit_tests\n    env:\n      TZ: ${{ github.event.inputs.timezone }}\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n          # Using a personal access token with admin privileges here so this\n          # action can push to protected branches. Note that this also means\n          # that the pushes can trigger additional workflows (GitHub only\n          # prevents pushes with the default GITHUB_TOKEN from triggering\n          # additional workflows).\n          #\n          # Note: sxs-bot has the personal access token configured.\n          #\n          # GitHub has 2 forms of personal access tokens.\n          # For Fine-Grained Access:\n          #  \"Only selected repositories\"\n          #   - Contents: Access: Read and write\n          #   - Deployments: Access: Read and write\n          #\n          # For Tokens (classic):\n          #   repo: (off)\n          #      public_repo: on\n          #  Everything else is off.\n          token: ${{ secrets.GH_TOKEN_RELEASE }}\n      - uses: actions/setup-python@v6\n        with:\n          python-version: '3.8'\n      - name: Install Python dependencies\n        run: |\n          pip3 install -r .github/scripts/requirements-release.txt\n      # We use the current date as tag name, unless a tag name was specified\n      # as input to the `workflow_dispatch` event\n      - name: Determine release version\n        id: get_version\n        run: |\n          INPUT_RELEASE_VERSION=${{ github.event.inputs.release_version }}\n          RELEASE_VERSION=${INPUT_RELEASE_VERSION:-$(date +'%Y.%m.%d')}\n          echo \"Release version is: ${RELEASE_VERSION}\"\n          echo \"RELEASE_VERSION=$RELEASE_VERSION\" >> $GITHUB_ENV\n      - name: Validate release version\n        run: |\n          VERSION_PATTERN=\"^([0-9]{4})\\.([0-9]{2})\\.([0-9]{2})(\\.[0-9]+)?$\"\n          if [[ $RELEASE_VERSION =~ $VERSION_PATTERN ]]; then\n            if [ $(date +'%Y') != ${BASH_REMATCH[1]} ] ||\n            [ $(date +'%m') != ${BASH_REMATCH[2]} ] ||\n            [ $(date +'%d') != ${BASH_REMATCH[3]} ]; then\n              TODAY=$(date +'%Y.%m.%d')\n              echo \"'$RELEASE_VERSION' doesn't match current date '$TODAY'\"\n              exit 1\n            fi\n          else\n            echo \"'$RELEASE_VERSION' doesn't match '$VERSION_PATTERN'\"\n            exit 1\n          fi\n          if [ $(git tag -l \"v$RELEASE_VERSION\") ]; then\n            echo \"Tag 'v$RELEASE_VERSION' already exists\"\n            exit 1\n          fi\n          if [ $(git rev-parse HEAD) == $(git rev-parse origin/release) ]; then\n            echo \"Nothing changed since last release $(git describe release).\"\n            exit 1\n          fi\n      - name: Reserve Zenodo DOI and prepare repository\n        run: |\n          python3 .github/scripts/Release.py prepare -vv \\\n            --version $RELEASE_VERSION \\\n            --zenodo-token ${{ secrets.ZENODO_READWRITE_TOKEN }} \\\n            --github-token ${{ secrets.GITHUB_TOKEN }}\n          git diff\n      - name: Download release notes\n        uses: actions/download-artifact@v8\n        id: release-notes\n        with:\n          name: release-notes\n          path: ~/release-notes\n      # Push a commit tagged with the new version to `develop` and `release`.\n      # The push will trigger the workflow again because we're using a personal\n      # access token. The triggered workflow will build and deploy the\n      # documentation so we don't need to do that here.\n      - name: Commit and push\n        run: |\n          git config user.name sxs-bot\n          git config user.email sxs-bot@black-holes.org\n          git commit -a -m \"Prepare release $RELEASE_VERSION\"\n          git show HEAD\n          git status\n          git tag -a v$RELEASE_VERSION -m \"Release $RELEASE_VERSION\" HEAD\n          git push origin HEAD:develop\n          git push origin HEAD:release\n          git push origin v$RELEASE_VERSION\n      - name: Create release on GitHub\n        uses: softprops/action-gh-release@v2\n        with:\n          token: ${{ secrets.GH_TOKEN_RELEASE }}\n          tag_name: v${{ env.RELEASE_VERSION }}\n          name: Release ${{ env.RELEASE_VERSION }}\n          body_path: >-\n            ${{ steps.release-notes.outputs.download-path }}/release_notes.md\n      # Publish the Zenodo record. Once published, the record can't be deleted\n      # anymore and editing is limited.\n      - name: Publish to Zenodo\n        run: |\n          python3 .github/scripts/Release.py publish -vv \\\n            --zenodo-token ${{ secrets.ZENODO_PUBLISH_TOKEN }} \\\n            --github-token ${{ secrets.GITHUB_TOKEN }} \\\n            --auto-publish\n\n  arch_datastructures_tests:\n    name: Archs\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        compiler:\n          - gcc-10\n          - gcc-11\n          - clang-13\n          - clang-14\n        include:\n          - sde_arch: (\"-nhm;nehalem\" \"-snb;sandybridge\" \"-hsw;haswell\"\n              \"-skl;skylake\" \"-icx;icelake-server\")\n            compiler: gcc-10\n          - sde_arch: (\"-nhm;nehalem\" \"-snb;sandybridge\" \"-hsw;haswell\"\n              \"-skl;skylake\" \"-icx;icelake-server\")\n            compiler: gcc-11\n          - sde_arch: (\"-nhm;nehalem\" \"-snb;sandybridge\" \"-hsw;haswell\"\n              \"-skl;skylake\" \"-icx;icelake-server\")\n            compiler: clang-13\n          - sde_arch: (\"-nhm;nehalem\" \"-snb;sandybridge\" \"-hsw;haswell\"\n              \"-skl;skylake\" \"-icx;icelake-server\" \"-tgl;tigerlake\")\n            compiler: clang-14\n    container:\n      image: ${{ inputs.container || 'sxscollaboration/spectre:dev' }}\n      env:\n        # See the unit test job for the reasons for these configuration choices\n        CXXFLAGS: \"-Werror\"\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v5\n      # Work around https://github.com/actions/checkout/issues/760\n      - name: Trust checkout\n        run: |\n          git config --global --add safe.directory $GITHUB_WORKSPACE\n      - uses: ./.github/actions/parse-compiler\n        with:\n          compiler: ${{ matrix.compiler }}\n      - name: Install compiler\n        run: |\n          apt-get update -y\n          apt-get install --reinstall ca-certificates\n          if [[ $COMPILER_ID = gcc ]]; then\n            apt-get install -y $CC $CXX $FC\n          else\n            apt-get install -y $CC $FC\n          fi\n      - name: Install Intel SDE\n        working-directory: /work\n        run: |\n          wget -O sde-external.tar.xz https://downloadmirror.intel.com/913594/sde-external-10.7.0-2026-02-18-lin.tar.xz\n          tar -xJf sde-external.tar.xz\n          mv sde-external-* sde\n          rm sde-*\n      - name: Configure, build, and run tests\n        working-directory: /work\n        # Notes on the build configuration:\n        # - We don't need debug symbols during CI, so we turn them off to reduce\n        #   memory usage.\n        run: >\n          ARCH_PARAM_LIST=${{ matrix.sde_arch }}\n\n          for ARCH_PARAM in ${ARCH_PARAM_LIST[@]}; do\n            OVERRIDE_ARCH=`echo ${ARCH_PARAM} | cut -d\";\" -f2`\n            SDE_FLAG=`echo ${ARCH_PARAM} | cut -d\";\" -f1`\n            echo \"CMake arch flag: $OVERRIDE_ARCH\"\n            echo \"Intel SDE arch flag: $SDE_FLAG\"\n            cd /work\n            BUILD_DIR=build$OVERRIDE_ARCH\n            mkdir $BUILD_DIR && cd $BUILD_DIR\n\n            cmake\\\n            -D CMAKE_C_COMPILER=${CC}\\\n            -D CMAKE_CXX_COMPILER=${CXX}\\\n            -D CMAKE_Fortran_COMPILER=${FC}\\\n            -D CMAKE_CXX_FLAGS=\"${CXXFLAGS}\"\\\n            -D OVERRIDE_ARCH=${OVERRIDE_ARCH}\\\n            -D CHARM_ROOT=${CHARM_ROOT}\\\n            -D CMAKE_BUILD_TYPE=Debug\\\n            -D SPECTRE_DEBUG_Og=ON\\\n            -D DEBUG_SYMBOLS=OFF\\\n            -D STRIP_SYMBOLS=ON\\\n            -D STUB_EXECUTABLE_OBJECT_FILES=ON\\\n            -D STUB_LIBRARY_OBJECT_FILES=ON\\\n            -D USE_PCH=ON\\\n            -D USE_CCACHE=ON\\\n            -D BUILD_DOCS=OFF\\\n            -D SPECTRE_AUTODIFF=ON\\\n            -D SPECTRE_FETCH_MISSING_DEPS=ON\\\n            $GITHUB_WORKSPACE\n\n            make -j4 TestArchitectureVectorization\n\n            /work/sde/sde ${SDE_FLAG}\\\n            -- ./bin/TestArchitectureVectorization [Unit]\n          done\n\n        shell: bash\n"
  },
  {
    "path": ".gitignore",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Build directories\n# Ignore build dirs named similarly to the example in the installation notes;\n# in general arbitrary names are possible and cannot all be ignored.\n/build*\n\n# Editor configurations\n.vscode\ncompile_commands.json\n.idea\nCMakeUserPresets.json\n\n# Spack\n/support/spack/\n.spack-env\n/support/DevEnvironments/spack.lock\n\n# Python environments\n# These are conventional locations where people may install Python packages\n# related to the project. They are mentioned in the Python docs:\n# https://docs.python.org/3/library/venv.html\n/.venv\n/env\n\n# Temporary files\n*~\n*.swp\n\\#*\\#\n.\\#*\n.DS_Store\n.dir-locals.el\n.cquery_cached_index\n.ycm_extra_conf.py\n.clangd\n.cache\n.projectile-cache.eld\n\n# TeX products\n*.aux\n*.log\n*.toc\n*.bbl\n*.bbg\n*.blg\n*.out\n*.bak\ndocs/auto\n\n# Python related files\n*.pyc\n.ipynb_checkpoints\n\n# charm++ runtime files\ncharmrun.*\n\n# Observer output\n*.xmf\n*.h5\n\n# Ignore deploy SSH keys and other TravisCI related files\n.travis/deploy_key\ncoverage.info\n\n# ClangFormat git hook file\n.clang_format_diff.patch\n\n# CTags, ETags and GNU Global Tags files\nGPATH\nGRTAGS\nGTAGS\nTAGS\ntags\n# Keep Tags/ directories on case-insensitive file systems\n!Tags/\n\n# AI/LLM Agent/CLI tools\nAGENTS.md\n\n.aider*\n\nCLAUDE.md\n.claude/settings.local.json\nCLAUDE.local.md\n"
  },
  {
    "path": ".skills/scripts/FetchCiLog.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\"\"\"Fetch a failed GitHub Actions job log and extract failure context.\n\nRetrieves the job summary and full log via the gh CLI, locates failure\nlines, and prints a context window around each failure for the LLM.\n\"\"\"\n\nimport argparse\nimport re\nimport subprocess\nimport sys\n\nFAILURE_PATTERNS = [\n    re.compile(r\"FAILED:\"),\n    re.compile(r\"\\berror:\"),\n    re.compile(r\"The following tests FAILED\"),\n    re.compile(r\"##\\[error\\]\"),\n    re.compile(r\"Process completed with exit code\"),\n    re.compile(r\"\\*\\*\\*Failed\"),\n]\n\n# Patterns where only context *after* the failure line is useful\n# (e.g. CTest's \"***Failed\" line is followed by the test output)\nAFTER_ONLY_PATTERNS = {r\"\\*\\*\\*Failed\"}\n\n# Timestamp prefix on every GH Actions log line\nTIMESTAMP_RE = re.compile(r\"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d+Z\\s?\")\n\n\ndef fetch_job_summary(job_id, owner, repo):\n    \"\"\"Fetch the job summary via 'gh run view --job'.\"\"\"\n    cmd = [\n        \"gh\",\n        \"run\",\n        \"view\",\n        \"--job\",\n        str(job_id),\n        \"-R\",\n        f\"{owner}/{repo}\",\n    ]\n    result = subprocess.run(cmd, capture_output=True, text=True)\n    if result.returncode != 0:\n        print(\n            f\"Warning: could not fetch job summary: {result.stderr.strip()}\",\n            file=sys.stderr,\n        )\n        return None\n    return result.stdout\n\n\ndef fetch_job_log(job_id, owner, repo):\n    \"\"\"Fetch the full job log via the GitHub API.\"\"\"\n    cmd = [\n        \"gh\",\n        \"api\",\n        f\"repos/{owner}/{repo}/actions/jobs/{job_id}/logs\",\n    ]\n    result = subprocess.run(cmd, capture_output=True, text=True)\n    if result.returncode != 0:\n        print(f\"Error fetching log: {result.stderr.strip()}\", file=sys.stderr)\n        sys.exit(1)\n    return result.stdout\n\n\ndef strip_timestamps(lines):\n    \"\"\"Remove GH Actions timestamp prefixes from each line.\"\"\"\n    return [TIMESTAMP_RE.sub(\"\", line) for line in lines]\n\n\ndef find_failure_lines(lines):\n    \"\"\"Return sorted list of (line_index, after_only) tuples.\n\n    ``after_only`` is True when the matched pattern only needs context\n    after the failure line (e.g. CTest's ``***Failed`` output).\n    \"\"\"\n    hits = {}\n    for i, line in enumerate(lines):\n        for pattern in FAILURE_PATTERNS:\n            if pattern.search(line):\n                after_only = pattern.pattern in AFTER_ONLY_PATTERNS\n                hits[i] = after_only\n                break\n    return hits\n\n\ndef merge_windows(hits, context, total_lines):\n    \"\"\"Merge overlapping [start, end] windows around each hit.\n\n    ``hits`` is a dict {line_index: after_only}.  For after-only hits\n    the window starts at the hit line itself (no before-context).\n    \"\"\"\n    if not hits:\n        return []\n    windows = []\n    for hit in sorted(hits):\n        after_only = hits[hit]\n        start = hit if after_only else max(0, hit - context)\n        end = min(total_lines - 1, hit + context)\n        if windows and start <= windows[-1][1] + 1:\n            windows[-1] = (windows[-1][0], end, windows[-1][2] | {hit})\n        else:\n            windows.append((start, end, {hit}))\n    return windows\n\n\ndef print_window(lines, start, end, failure_indices):\n    \"\"\"Print a window of lines with line numbers, marking failures.\"\"\"\n    for i in range(start, end + 1):\n        marker = \">>>\" if i in failure_indices else \"   \"\n        print(f\"{marker} {i + 1:>6d} | {lines[i]}\")\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=(\n            \"Fetch a failed GitHub Actions job log and extract failure context.\"\n        )\n    )\n    parser.add_argument(\"job_id\", help=\"GitHub Actions job ID\")\n    parser.add_argument(\n        \"--owner\",\n        default=\"sxs-collaboration\",\n        help=\"Repository owner (default: sxs-collaboration)\",\n    )\n    parser.add_argument(\n        \"--repo\",\n        default=\"spectre\",\n        help=\"Repository name (default: spectre)\",\n    )\n    parser.add_argument(\n        \"--context\",\n        type=int,\n        default=50,\n        help=\"Number of lines before and after each failure (default: 50)\",\n    )\n    args = parser.parse_args()\n\n    # -- Job summary --\n    summary = fetch_job_summary(args.job_id, args.owner, args.repo)\n    if summary:\n        print(\"=\" * 60)\n        print(\"JOB SUMMARY\")\n        print(\"=\" * 60)\n        print(summary.rstrip())\n        print()\n\n    # -- Fetch and process log --\n    raw_log = fetch_job_log(args.job_id, args.owner, args.repo)\n    raw_lines = raw_log.splitlines()\n    lines = strip_timestamps(raw_lines)\n\n    hits = find_failure_lines(lines)\n    if not hits:\n        print(\"No failure patterns found in the log.\")\n        print(f\"Total log lines: {len(lines)}\")\n        sys.exit(1)\n\n    windows = merge_windows(hits, args.context, len(lines))\n    num_hits = len(hits)\n\n    print(\"=\" * 60)\n    print(f\"FAILURE LOG  (job {args.job_id}, {args.owner}/{args.repo})\")\n    print(\n        f\"  {num_hits} failure line(s) found, {len(windows)} context window(s)\"\n    )\n    print(f\"  Total log lines: {len(lines)}\")\n    print(\"=\" * 60)\n\n    for idx, (start, end, failure_indices) in enumerate(windows):\n        print()\n        print(\n            f\"--- Window {idx + 1}/{len(windows)} \"\n            f\"(lines {start + 1}-{end + 1}) ---\"\n        )\n        print_window(lines, start, end, failure_indices)\n\n    print()\n    print(\"=\" * 60)\n    print(\"END OF FAILURE CONTEXT\")\n    print(\"=\" * 60)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": ".skills/scripts/FetchIssue.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\"\"\"Fetch a GitHub issue and print it in a structured format for Claude.\n\nIf the issue is a random test failure (title contains 'random failure in'\nand the body/comments contain MAKE_GENERATOR seed lines), a compact\n[RANDOM_FAILURE] format is emitted instead of the full issue text.\n\"\"\"\n\nimport argparse\nimport json\nimport re\nimport subprocess\nimport sys\n\n\ndef is_random_failure(title, all_text):\n    \"\"\"Return True if this looks like a MAKE_GENERATOR random failure issue.\"\"\"\n    if \"random failure in\" not in title.lower():\n        return False\n    return bool(re.search(r\"Seed is:\\s*\\d+\\s+from\", all_text))\n\n\ndef extract_make_generator_seeds(all_text):\n    \"\"\"Parse 'Seed is: N from path:line' entries, returning {path:line: [seeds]}.\n\n    CI logs often wrap long paths at '/' boundaries, so we strip\n    timestamps and collapse continuation lines before matching.\n    \"\"\"\n    # Strip CI log timestamps (e.g. \"2023-09-11T19:44:29.1292895Z   \")\n    text = re.sub(r\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d+Z\\s*\", \"\", all_text)\n    # Collapse lines where a path wraps after a '/'\n    text = re.sub(r\"/\\s*\\n\\s*\", \"/\", text)\n    pattern = r\"Seed is:\\s*(\\d+)\\s+from\\s+(.*?\\.[hct]pp:\\d+)\"\n    matches = re.findall(pattern, text)\n\n    grouped = {}\n    for seed, raw_path in matches:\n        # Canonicalize: keep only the part after tests/Unit/\n        m = re.search(r\"tests/Unit/(.*)\", raw_path)\n        canonical = m.group(1) if m else raw_path\n        grouped.setdefault(canonical, [])\n        if seed not in grouped[canonical]:\n            grouped[canonical].append(seed)\n    return grouped\n\n\ndef extract_test_name(title):\n    \"\"\"Extract the test name from a 'Random failure in TEST' title.\"\"\"\n    m = re.search(r\"[Rr]andom\\s+[Ff]ailure\\s+in\\s+(\\S+)\", title)\n    return m.group(1) if m else title\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=\"Fetch a GitHub issue via gh CLI\"\n    )\n    parser.add_argument(\"issue_number\", help=\"Issue number (e.g. 1727)\")\n    parser.add_argument(\n        \"--repo\",\n        default=None,\n        help=\"Repository in owner/repo form (default: current repo)\",\n    )\n    parser.add_argument(\n        \"--full\",\n        action=\"store_true\",\n        help=\"Always print full issue text, bypassing random-failure detection\",\n    )\n    args = parser.parse_args()\n\n    cmd = [\n        \"gh\",\n        \"issue\",\n        \"view\",\n        str(args.issue_number),\n        \"--json\",\n        (\n            \"number,title,state,author,assignees,\"\n            \"createdAt,labels,url,body,comments\"\n        ),\n    ]\n    if args.repo:\n        cmd.extend([\"--repo\", args.repo])\n\n    result = subprocess.run(cmd, capture_output=True, text=True)\n    if result.returncode != 0:\n        print(f\"Error: {result.stderr.strip()}\", file=sys.stderr)\n        sys.exit(1)\n\n    data = json.loads(result.stdout)\n\n    title = data.get(\"title\", \"\")\n    body = data.get(\"body\", \"\") or \"\"\n    comments = data.get(\"comments\", [])\n    all_text = body + \"\\n\" + \"\\n\".join((c.get(\"body\") or \"\") for c in comments)\n\n    # --- Random failure detection ---\n    if not args.full and is_random_failure(title, all_text):\n        seeds = extract_make_generator_seeds(all_text)\n        test_name = extract_test_name(title)\n        print(\"[RANDOM_FAILURE]\")\n        print(f\"Issue #{data['number']}: {title}\")\n        print(f\"URL: {data.get('url', '')}\")\n        print(f\"Test: {test_name}\")\n        print()\n        print(\"Seeds by location (paths relative to tests/Unit/):\")\n        for path_line, seed_list in seeds.items():\n            print(f\"  {path_line} -> {', '.join(seed_list)}\")\n        sys.exit(0)\n\n    # --- Normal issue output ---\n    author = (data.get(\"author\") or {}).get(\"login\", \"ghost\")\n    labels = \", \".join(lbl[\"name\"] for lbl in data.get(\"labels\", []))\n    assignees = \", \".join(a[\"login\"] for a in data.get(\"assignees\", []))\n\n    print(f\"Issue #{data['number']}: {data['title']}\")\n    print(f\"URL:     {data.get('url', '')}\")\n    print(f\"State:   {data['state']}\")\n    print(f\"Author:  {author}\")\n    print(f\"Created: {data['createdAt']}\")\n    if assignees:\n        print(f\"Assigned: {assignees}\")\n    if labels:\n        print(f\"Labels:  {labels}\")\n    print()\n    print((data.get(\"body\") or \"\").strip())\n\n    for comment in comments:\n        comment_author = (comment.get(\"author\") or {}).get(\"login\", \"ghost\")\n        print()\n        print(\"---\")\n        print(f\"Comment by: {comment_author} at {comment['createdAt']}\")\n        print((comment.get(\"body\") or \"\").strip())\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": ".skills/scripts/FetchPrComments.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\"\"\"Fetch PR review data and print it in a structured format for Claude.\"\"\"\n\nimport argparse\nimport json\nimport subprocess\nimport sys\n\n\ndef run_gh_command(cmd):\n    \"\"\"Run a gh CLI command and return parsed JSON, or exit on error.\"\"\"\n    result = subprocess.run(cmd, capture_output=True, text=True)\n    if result.returncode != 0:\n        print(f\"Error: {result.stderr.strip()}\", file=sys.stderr)\n        sys.exit(1)\n    return json.loads(result.stdout)\n\n\ndef fetch_pr_metadata(pr_number, repo=None):\n    \"\"\"Fetch PR metadata, comments, reviews, and files via gh pr view.\"\"\"\n    cmd = [\n        \"gh\",\n        \"pr\",\n        \"view\",\n        str(pr_number),\n        \"--json\",\n        (\n            \"number,title,state,author,baseRefName,headRefName,\"\n            \"body,comments,reviews,files,url,createdAt,reviewDecision\"\n        ),\n    ]\n    if repo:\n        cmd.extend([\"--repo\", repo])\n    return run_gh_command(cmd)\n\n\ndef fetch_review_threads(pr_number, repo=None):\n    \"\"\"Fetch review threads with resolution status via GraphQL API.\"\"\"\n    if repo:\n        owner, name = repo.split(\"/\", 1)\n    else:\n        detect_cmd = [\"gh\", \"repo\", \"view\", \"--json\", \"owner,name\"]\n        repo_data = run_gh_command(detect_cmd)\n        owner = repo_data[\"owner\"][\"login\"]\n        name = repo_data[\"name\"]\n\n    query = \"\"\"query {\n      repository(owner:\"%s\", name:\"%s\") {\n        pullRequest(number:%d) {\n          reviewThreads(first:100) {\n            totalCount\n            pageInfo { hasNextPage }\n            nodes {\n              isResolved\n              isOutdated\n              path\n              line\n              originalLine\n              startLine\n              originalStartLine\n              subjectType\n              comments(first:50) {\n                nodes {\n                  body\n                  author { login }\n                  path\n                  diffHunk\n                  line\n                  originalLine\n                  startLine\n                  originalStartLine\n                  createdAt\n                }\n              }\n            }\n          }\n        }\n      }\n    }\"\"\" % (owner, name, int(pr_number))\n\n    cmd = [\"gh\", \"api\", \"graphql\", \"-f\", f\"query={query}\"]\n    data = run_gh_command(cmd)\n\n    threads_data = (\n        data.get(\"data\", {})\n        .get(\"repository\", {})\n        .get(\"pullRequest\", {})\n        .get(\"reviewThreads\", {})\n    )\n\n    if threads_data.get(\"pageInfo\", {}).get(\"hasNextPage\"):\n        print(\n            (\n                \"WARNING: PR has more than 100 review threads. \"\n                \"Only the first 100 are shown.\"\n            ),\n            file=sys.stderr,\n        )\n\n    return threads_data.get(\"nodes\", [])\n\n\ndef print_pr_header(data):\n    \"\"\"Print PR metadata header.\"\"\"\n    author = (data.get(\"author\") or {}).get(\"login\", \"ghost\")\n    print(f\"PR #{data['number']}: {data['title']}\")\n    print(f\"URL:             {data.get('url', '')}\")\n    print(f\"State:           {data['state']}\")\n    print(f\"Author:          {author}\")\n    print(f\"Created:         {data['createdAt']}\")\n    print(f\"Base <- Head:    {data['baseRefName']} <- {data['headRefName']}\")\n    print(f\"Review decision: {data.get('reviewDecision', 'NONE')}\")\n\n\ndef print_changed_files(files):\n    \"\"\"Print changed files list with addition/deletion counts.\"\"\"\n    print()\n    print(\"=\" * 60)\n    print(\"CHANGED FILES\")\n    print(\"=\" * 60)\n    for f in files:\n        print(f\"  +{f['additions']:-4d} -{f['deletions']:-4d}  {f['path']}\")\n    total_add = sum(f[\"additions\"] for f in files)\n    total_del = sum(f[\"deletions\"] for f in files)\n    print(f\"  {'':>10}  ({len(files)} files, +{total_add} -{total_del} total)\")\n\n\ndef print_pr_body(body):\n    \"\"\"Print PR description.\"\"\"\n    body = (body or \"\").strip()\n    if body:\n        print()\n        print(\"=\" * 60)\n        print(\"PR DESCRIPTION\")\n        print(\"=\" * 60)\n        print(body)\n\n\ndef print_top_level_comments(comments):\n    \"\"\"Print top-level PR conversation comments.\"\"\"\n    if not comments:\n        return\n    print()\n    print(\"=\" * 60)\n    print(f\"TOP-LEVEL COMMENTS ({len(comments)})\")\n    print(\"=\" * 60)\n    for c in comments:\n        author = (c.get(\"author\") or {}).get(\"login\", \"ghost\")\n        print()\n        print(f\"--- Comment by {author} at {c['createdAt']} ---\")\n        print((c.get(\"body\") or \"\").strip())\n\n\ndef print_review_summaries(reviews):\n    \"\"\"Print review summaries (APPROVED, CHANGES_REQUESTED, etc.).\"\"\"\n    summaries = [r for r in reviews if (r.get(\"body\") or \"\").strip()]\n    if not summaries:\n        return\n    print()\n    print(\"=\" * 60)\n    print(f\"REVIEW SUMMARIES ({len(summaries)})\")\n    print(\"=\" * 60)\n    for r in summaries:\n        author = (r.get(\"author\") or {}).get(\"login\", \"ghost\")\n        state = r.get(\"state\", \"COMMENTED\")\n        print()\n        print(f\"--- [{state}] Review by {author} at {r['submittedAt']} ---\")\n        print((r.get(\"body\") or \"\").strip())\n\n\ndef print_review_threads(threads):\n    \"\"\"Print inline review threads grouped by resolution status.\"\"\"\n    if not threads:\n        print()\n        print(\"=\" * 60)\n        print(\"INLINE REVIEW THREADS\")\n        print(\"=\" * 60)\n        print(\"  (none)\")\n        return\n\n    unresolved = [t for t in threads if not t[\"isResolved\"]]\n    resolved = [t for t in threads if t[\"isResolved\"]]\n\n    print()\n    print(\"=\" * 60)\n    print(\n        \"INLINE REVIEW THREADS \"\n        f\"({len(unresolved)} unresolved, {len(resolved)} resolved)\"\n    )\n    print(\"=\" * 60)\n\n    for label, group in [(\"UNRESOLVED\", unresolved), (\"RESOLVED\", resolved)]:\n        if not group:\n            continue\n        print()\n        print(f\"--- {label} THREADS ---\")\n        for i, thread in enumerate(group, 1):\n            status_tags = []\n            if thread[\"isResolved\"]:\n                status_tags.append(\"[RESOLVED]\")\n            else:\n                status_tags.append(\"[UNRESOLVED]\")\n            if thread[\"isOutdated\"]:\n                status_tags.append(\"[OUTDATED]\")\n\n            path = thread.get(\"path\", \"unknown\")\n            line = thread.get(\"line\") or thread.get(\"originalLine\")\n            line_info = f\":{line}\" if line else \"\"\n\n            print()\n            print(f\"  Thread {i}: {' '.join(status_tags)} {path}{line_info}\")\n\n            comments = thread.get(\"comments\", {}).get(\"nodes\", [])\n            for j, c in enumerate(comments):\n                author = (c.get(\"author\") or {}).get(\"login\", \"ghost\")\n                print(f\"    [{author} at {c['createdAt']}]\")\n\n                diff_hunk = c.get(\"diffHunk\", \"\")\n                if diff_hunk and j == 0:\n                    for hunk_line in diff_hunk.split(\"\\n\"):\n                        print(f\"      {hunk_line}\")\n\n                body = (c.get(\"body\") or \"\").strip()\n                if body:\n                    for body_line in body.split(\"\\n\"):\n                        print(f\"    {body_line}\")\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description=\"Fetch PR review data via gh CLI and GraphQL\"\n    )\n    parser.add_argument(\"pr_number\", help=\"Pull request number (e.g. 1234)\")\n    parser.add_argument(\n        \"--repo\",\n        default=None,\n        help=\"Repository in owner/repo form (default: current repo)\",\n    )\n    args = parser.parse_args()\n\n    pr_data = fetch_pr_metadata(args.pr_number, args.repo)\n    threads = fetch_review_threads(args.pr_number, args.repo)\n\n    print_pr_header(pr_data)\n    print_changed_files(pr_data.get(\"files\", []))\n    print_pr_body(pr_data.get(\"body\"))\n    print_top_level_comments(pr_data.get(\"comments\", []))\n    print_review_summaries(pr_data.get(\"reviews\", []))\n    print_review_threads(threads)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": ".skills/spectre-fix-ci/SKILL.md",
    "content": "Run the fetch script to retrieve and analyze the CI job log:\n\n```\npython3 \\\n  \"$(git rev-parse --show-toplevel)/.skills/scripts/FetchCiLog.py\" \\\n  <JOB_ID> [--owner OWNER] [--context LINES]\n```\n\n- Extract the job ID from the user's message (required).\n- Pass `--owner OWNER` if the user specifies an owner (default is\n  `sxs-collaboration`).\n- `--context` defaults to 50 lines before and after each failure.\n\nAfter the script returns:\n\n1. **Summarize the failure**: which step failed, which test(s) failed,\n   and what the error was.\n2. **Identify source locations**: find file paths and line numbers in the\n   error output that point to the failing code.\n3. **Suggest next steps**: propose a fix or further investigation.\n\nIf the context window seems too narrow to understand the failure, re-run\nwith `--context 150` for more surrounding lines.\n"
  },
  {
    "path": ".skills/spectre-fix-issue/RANDOM_FAILURE.md",
    "content": "# Fixing a Random Test Failure\n\nYou have a random test failure issue. The script output above contains\nthe test name and a list of `file:line` locations with their failing seeds.\n\nRandom failures are almost always caused by a **numerical bug in the source\ncode**, not by overly tight test tolerances. Your job is to find and fix\nthe source-level bug. Increasing a test tolerance is a last resort, gated\nbehind mandatory investigation.\n\n## 1. Background\n\n`MAKE_GENERATOR(gen)` (defined in `tests/Unit/Framework/TestHelpers.hpp`)\ncreates a `std::mt19937` with a random seed. `MAKE_GENERATOR(gen, SEED)`\nuses the provided constant instead, making the test deterministic. On\nfailure, prints `Seed is: N from file:line` so the failure can be replayed.\n\n### Seeding a test file\n\nWhen the `file:line` points to a test file (not `CheckWithRandomValues.hpp`):\nopen the file at the indicated line (line numbers may have shifted -- search\nnearby for `MAKE_GENERATOR`) and change `MAKE_GENERATOR(gen);` to\n`MAKE_GENERATOR(gen, SEED);` using the first seed listed for that location.\n\n### Seeding CheckWithRandomValues\n\nWhen the `file:line` points to `Framework/CheckWithRandomValues.hpp`:\n`check_with_random_values()` accepts `epsilon` and `seed` parameters. Find\nthe calling test's `check_with_random_values` invocation and pass the failing\nseed as the last argument. For example, if the original call is:\n```cpp\npypp::check_with_random_values<1>(\n    &Foo::bar<DataType>, klass, \"Module\", \"function\",\n    {{{-10.0, 10.0}}}, member_vars, used_for_size);\n```\nadd `epsilon` (default is `1.0e-12`) and the seed:\n```cpp\npypp::check_with_random_values<1>(\n    &Foo::bar<DataType>, klass, \"Module\", \"function\",\n    {{{-10.0, 10.0}}}, member_vars, used_for_size,\n    1.0e-12, FAILING_SEED);\n```\n\n## 2. Reproduce the Failure\n\nFor each `file:line` + seed entry from the script output:\n\n1. **Set the seed** per Section 1 above.\n2. **Find the build target**:\n   ```\n   cd build && ctest --show-only=json-v1 -R \"TEST_NAME\"\n   ```\n   Parse the JSON output for the binary path (first element of `\"command\"`).\n3. **Build**: `ninja -C build <binary_name>`\n4. **Run**: `build/bin/<binary_name> \"TEST_NAME\"`\n5. **If the test does NOT fail**: STOP and report to the user that the failure\n   cannot be reproduced (code may have changed since the issue was filed).\n\n## 3. Investigate the Root Cause (REQUIRED)\n\nThis is the most important section. You MUST complete this investigation\nbefore considering any tolerance change.\n\n### 3a. Trace the call chain\n\n1. Read the error output to identify the **exact failing comparison** -- which\n   values were compared, what the expected vs. actual values were, and what the\n   discrepancy is.\n2. Identify which **function under test** produces the incorrect value.\n3. **Read the source code** of that function. Trace through its arithmetic step\n   by step. Identify the specific operation (e.g., \"the subtraction on line 438\n   of Wedge.cpp\") that amplifies floating-point error.\n4. If the function calls other functions, trace into those as well. Follow the\n   data flow until you reach the arithmetic that is numerically unstable.\n\nYou MUST identify a specific line and operation in the source code. \"The\ncomputation accumulates floating-point error\" is not sufficient -- you need to\nsay *where* and *why*.\n\n### 3b. Numerical patterns checklist\n\nSearch the source code in the call chain for these common floating-point\npitfalls. For each pattern found, attempt the listed fix:\n\n- **Catastrophic cancellation**: subtraction of nearly-equal values\n  (`a - b` where `a ~ b`). Fix: rearrange the algebra to avoid the subtraction,\n  use compensated forms, or factor out common terms.\n\n- **Division by near-zero**: a denominator that approaches zero for certain\n  inputs (e.g., near coordinate boundaries, poles, or special points). Fix:\n  reformulate to multiply instead of divide, combine fractions to cancel the\n  small denominator, or restructure the expression analytically.\n\n- **`atan` instead of `atan2`**: `atan(y/x)` loses quadrant information and\n  divides by near-zero when `x ~ 0`. Fix: replace with `atan2(y, x)`.\n\n- **Missing precision-preserving library functions**: `log(1+x)` loses\n  precision when `x ~ 0`; `exp(x)-1` loses precision when `x ~ 0`;\n  `sqrt(x*x + y*y)` can overflow for large values. Fix: use `log1p(x)`,\n  `expm1(x)`, and `hypot(x, y)`.\n\n- **Naive summation of many terms**: summing a long list of floating-point\n  numbers accumulates round-off. Fix: use Kahan (compensated) summation, or\n  sort terms by magnitude before summing.\n\n- **Unstable polynomial evaluation**: monomial form\n  (`a*x^3 + b*x^2 + c*x + d`) accumulates error for large `x`. Fix: use\n  Horner's method (`((a*x + b)*x + c)*x + d`).\n\n- **Numerically unstable quadratic formula**: the standard formula\n  `(-b +/- sqrt(b^2 - 4ac)) / 2a` loses precision when `b^2 >> 4ac` due to\n  catastrophic cancellation in the numerator. Fix: use the numerically stable\n  form (compute one root via the standard formula, the other via\n  `c / (a * first_root)`).\n\n- **Accumulation of terms with widely varying magnitudes**: adding a tiny\n  correction to a large value drops the correction entirely. Fix: reorder\n  operations so values of similar magnitude are combined first, or use\n  compensated summation.\n\n### 3c. Attempt a source-level fix\n\nBased on the pattern(s) you identified in 3a and 3b:\n\n1. **Implement a fix in the source code** (not the test). For example:\n   rearrange an expression to avoid cancellation, replace a division with a\n   multiplication, switch `atan` to `atan2`, etc.\n2. **Build and run with the failing seed** to verify the fix resolves the\n   failure.\n3. If the fix works, skip Section 4 entirely and go to Section 5 (Verify).\n4. If the fix does not fully resolve the issue, document what you tried and\n   why it was insufficient, then continue investigating. Only proceed to\n   Section 4 after exhausting source-level options.\n\n## 4. LAST RESORT -- Tolerance Adjustment\n\n**MANDATORY GATE -- you may NOT adjust any tolerance until ALL of the\nfollowing are true:**\n\n1. You have identified the full call chain from the test to the failing\n   arithmetic operation.\n2. You have named the specific source-code operation (file, line, expression)\n   that produces the numerical error.\n3. You have attempted at least one source-level fix and can explain why it\n   did not work.\n4. You have confirmed the error is inherent to the algorithm in double\n   precision and cannot be eliminated by rearranging the arithmetic.\n\n**If you cannot satisfy all four conditions, go back to Section 3.**\n\n### Adjusting tolerance in a test file\n\n- `Approx custom_approx = Approx::custom().epsilon(TOL).scale(1.0);`\n- For scalars: `CHECK(expected == custom_approx(computed));`\n- For iterables:\n  `CHECK_ITERABLE_CUSTOM_APPROX(expected, computed, custom_approx);`\n- Start with `1.0e-14` and multiply by 10 until the test passes. Keep the\n  tolerance as small as possible.\n\n### Adjusting tolerance in CheckWithRandomValues\n\nAdjust the `epsilon` parameter in the `check_with_random_values` call.\nStart at `1.0e-12` and multiply by 10. Keep as small as possible.\n\n### Required documentation\n\nAdd a C++ comment next to the tolerance change explaining:\n- What source-level operation causes the error\n- Why a source-level fix is not feasible\n- What magnitude of error is expected\n\n## 5. Verify\n\n1. **Remove the explicit seed** so the generator returns to\n   `MAKE_GENERATOR(gen);` (or remove the seed argument from\n   `check_with_random_values`).\n2. **Run**: `ctest --repeat-until-fail 1000 -R \"TEST_NAME\"` in the build\n   directory.\n3. If any iterations fail, return to Section 3 and investigate the new failure.\n   Repeat until all 1000 iterations pass.\n\n## 6. Report Summary\n\nAfter all `file:line` entries are resolved, report:\n- Every file and line changed\n- For **source-level fixes**: what the numerical issue was and how it was fixed\n- For **tolerance adjustments**: the root cause, why a source fix wasn't\n  feasible, and the new tolerance value\n\n## Important Notes\n\n- The build directory is `build/` under the repository root.\n- Do NOT modify global `approx` in `tests/Unit/Framework/TestingFramework.hpp`.\n- Always remove explicit seeds before committing -- the seed is only for\n  reproducing the failure, not for the final code.\n- If multiple distinct test files have seeds, investigate each independently.\n"
  },
  {
    "path": ".skills/spectre-fix-issue/SKILL.md",
    "content": "Run the fetch script to retrieve the issue title, body, and comments:\n\n```\npython3 \\\n  \"$(git rev-parse --show-toplevel)/.skills/scripts/FetchIssue.py\" \\\n  <number> [--repo owner/repo] [--full]\n```\n\n- Extract the issue number from the user's message.\n- Pass `--repo owner/repo` if the user specifies a repo or says \"upstream\"\n  (for SpECTRE, upstream is `sxs-collaboration/spectre`).\n- Pass `--full` if the user explicitly asks for the full/unshortened issue\n  text (e.g. \"show me the full issue\", \"don't shorten it\", \"skip random\n  failure handling\"). Do NOT pass `--full` by default.\n\n**If the output starts with `[RANDOM_FAILURE]`**: Read and follow the\ninstructions in\n`$(git rev-parse --show-toplevel)/.skills/spectre-fix-issue/RANDOM_FAILURE.md`\nusing the seed data from the script output. Mention to the user that they can\nask for the full issue text if they need it.\n\n**Otherwise**: Summarize the issue, then proceed with the user's request.\n"
  },
  {
    "path": ".skills/spectre-fix-pr/SKILL.md",
    "content": "Run the fetch script to retrieve PR metadata, review comments, and changed\nfiles:\n\n```\npython3 \\\n  \"$(git rev-parse --show-toplevel)/.skills/scripts/FetchPrComments.py\" \\\n  <number> [--repo owner/repo]\n```\n\n- Extract the PR number from the user's message.\n- Pass `--repo owner/repo` if the user specifies a repo or says \"upstream\"\n  (for SpECTRE, upstream is `sxs-collaboration/spectre`).\n\nAfter fetching the PR data:\n\n1. **Summarize the review** before starting work: list the total number of\n   threads, how many are unresolved, and the files affected.\n2. **Prioritize UNRESOLVED threads** -- these are the comments that still need\n   to be addressed. Start with unresolved, non-outdated threads first.\n3. **Group by file** -- work through comments file-by-file rather than jumping\n   around, since multiple threads often apply to the same file.\n4. **Use the diff hunk context** -- each inline comment includes the surrounding\n   diff hunk so you can understand exactly what code the reviewer is referring\n   to.\n5. **Check OUTDATED threads** -- these refer to code that has since changed.\n   Verify whether the concern still applies before making changes.\n6. **Skip resolved threads** unless the user specifically asks about them.\n"
  },
  {
    "path": ".skills/spectre-review/SKILL.md",
    "content": "# SpECTRE Code Review\n\n**Arguments**: $ARGUMENTS\n**Current branch**: !`git branch --show-current`\n\nPerform a thorough code review of SpECTRE changes. Follow every step below\nprecisely. The full SpECTRE code rules reference (provide to review agents):\n!`tail -n +5 .claude/rules/Cxx.md`\n\n## Step 1: Parse Arguments & Acquire Diff\n\nParse `$ARGUMENTS`:\n- **Number present** (e.g. `1234`): PR mode. Fetch with:\n  `gh pr diff <N> --repo sxs-collaboration/spectre` and\n  `gh pr view <N> --repo sxs-collaboration/spectre --json\n  title,body,headRefName,baseRefName`\n- **No number**: Local mode. Use `git diff develop...HEAD` for committed\n  changes, plus `git diff` and `git diff --cached` for uncommitted changes. If\n  no diff, fall back to `git diff HEAD~1`.\n- **\"clang-tidy\" present**: Enable the clang-tidy step.\n- **\"coverage\" present**: Enable the targeted code coverage step.\n\nSave the diff. Extract the list of changed files categorized by type (C++\n`.cpp/.hpp/.tpp`, Python `.py`, CMake `CMakeLists.txt`, other).\n\nCreate a task list tracking all review steps.\n\n## Step 2: Formatting Checks (run in parallel)\n\n### C++ (clang-format)\nFor each changed C++ file:\n1. Parse diff hunk headers (`@@ +START,COUNT @@`) to get changed line ranges\n2. Expand each range by +/-4 lines (clamped to file bounds)\n3. Run: `clang-format -style=file --lines=START:END [--lines=...] FILEPATH`\n4. Diff against original. Collect any formatting differences.\n\n### Python (black + isort)\nFor each changed `.py` file (excluding `external/`):\n```\nblack --check --diff FILEPATH\nisort --check-only --diff FILEPATH\n```\n\n## Step 3: CI Pre-Checks on Changed Files (run in parallel)\n\nCheck each changed file for issues that SpECTRE CI (`tools/FileTestDefs.sh`,\n`tools/CheckFiles.sh`) will flag. Only check lines/patterns introduced in the\ndiff, not pre-existing issues.\n\n**All files**: lines >80 chars (excluding URLs, NOLINT, #include, \\snippet,\n\\image, \\link, import); missing MIT license header; no final newline; tabs;\ntrailing whitespace; carriage returns\n\n**C++ files (.cpp/.hpp/.tpp)**:\n- Missing `#pragma once` in headers\n- `#include <iostream>` -> use `Parallel/Printf/Printf.hpp`\n- `#include <lrtslock.h>` -> use `<converse.h>`\n- `std::enable_if` -> use `requires`\n- `namespace _details` -> use `_detail`\n- `struct TD;` / `class TD;` (debug artifacts)\n- `.ckLocal()` -> `Parallel::local()`\n- `.ckLocalBranch()` -> `Parallel::local_branch()`\n- `return Py_None;` -> `Py_RETURN_NONE`\n- Text after `/*!` on same line\n- `Ls` abbreviation -> use `List`\n- Doxygen (`///` or `/*!`) in `.cpp` files (only use `//` comments in cpp)\n- Top-level `const` on value parameters in function declarations (`.hpp`) ->\n  remove `const` from the declaration (keep in definition)\n- TODO/FIXME comments (not allowed)\n\n**Test files**:\n- `TEST_CASE` instead of `SPECTRE_TEST_CASE`\n- `Approx(` instead of `approx`\n\n**CMake**: New C++ files in a directory must be listed in that directory's\nCMakeLists.txt; removed files must be removed; entries should be alphabetical.\n\n**LLM Comments**: Identify comments that seem like notes from a coding agent\nduring its thinking process.\n\n### Include Order (C++ files in diff)\nVerify:\n1. (Tests) `\"Framework/TestingFramework.hpp\"` first, then blank line\n2. (`.cpp` with `.hpp`) Corresponding `.hpp`, then blank line\n3. STL/external `<headers>` alphabetical\n4. Blank line\n5. SpECTRE `\"headers\"` alphabetical\n\n### Commit Messages (local mode only)\nCheck no commit starts with (case-insensitive): fixup, wip, fixme, deleteme,\nrebaseme, testing, rebase.\n\n## Step 4: Code Review (2 Parallel Agents)\n\nLaunch 2 parallel agents. Provide each with the full diff and the\nSpECTRE code rules reference shown above.\n\n### Agent A: Style, Patterns & Idioms\nInstructions for the agent:\n- Check the diff against every rule in **Banned Patterns** and **Style Rules**\n- Check for **Prefer-Library Patterns** (manual tensor loops that should use\n  EagerMath)\n- When you spot a suspicious pattern NOT in the checklist (e.g., a manual matrix\n  operation, a loop that looks like it reimplements an existing utility), use\n  `grep -r` in `src/DataStructures/Tensor/EagerMath/`, `src/DataStructures/`,\n  `src/NumericalAlgorithms/`, or `src/Utilities/` to find an existing utility\n- Only flag issues in lines that the diff introduces (not pre-existing code)\n- For each finding: `file:line`, severity (`critical`/`important`/`suggestion`),\n  explanation\n\n### Agent B: Bugs, Logic, Tests & Documentation\nInstructions for the agent:\n- Read each changed file in full (not just the diff) to understand surrounding\n  context\n- Look for: logic errors, off-by-one, uninitialized variables, NaN handling,\n  race conditions, incorrect template instantiations, virtual inheritance issues\n  (most-derived must init virtual bases)\n- Check that new/changed public API in `.hpp` files has Doxygen documentation\n- Check that new source files have corresponding tests (`src/Foo/Bar.hpp` ->\n  `tests/Unit/Foo/Test_Bar.cpp`)\n- Check that new `.cpp`/`.hpp` files are listed in their `CMakeLists.txt`\n- Only flag issues introduced by the diff\n- For each finding: `file:line`, severity, explanation\n- Check for potentially problematic floating point math like:\n  - Catastrophic cancellation: subtraction of nearly-equal values\n    (`a - b` where `a ~ b`).\n  - Division by near-zero: a denominator that approaches zero for certain\n    inputs (e.g., near coordinate boundaries, poles, or special points).\n  - Naive summation of many terms: summing a long list of floating-point\n    numbers accumulates round-off.\n  - Unstable polynomial evaluation: monomial form\n    (`a*x^3 + b*x^2 + c*x + d`), instead use Horner's method,\n    `evaluate_polynomial()` from `src/Utilities/Math.hpp`\n  - Numerically unstable quadratic formula: the standard formula\n    `(-b +/- sqrt(b^2 - 4ac)) / 2a` loses precision.\n  - Accumulation of terms with widely varying magnitudes: adding a tiny\n    correction to a large value drops the correction entirely.\n  - Make sure the code always uses `atan2` instead of `atan`, `log1p(x)` instead\n    of `log(1+x)`, `expm1(x)` instead of `exp(x)-1`, `hypot(x,y)` instead of\n    `sqrt(x*x+y*y)`.\n\n## Step 5: clang-tidy (if requested, run in parallel)\n\nIf \"clang-tidy\" was in arguments:\n1. Check for `build/compile_commands.json`. If missing, report that clang-tidy\n   requires a configured build directory and skip.\n2. For each changed `.cpp` file: `clang-tidy -p build/ FILEPATH 2>&1`\n3. Filter output to only warnings on lines in the diff.\n\n## Step 6: Code Coverage (if requested, run in parallel)\n\nIf \"coverage\" was in arguments:\nRead `references/coverage-steps.md` and follow those instructions exactly.\n\n## Step 7: Self-Review Prune\n\nCombine all findings from steps 2-6. Review each finding and REMOVE only clear\nnon-issues:\n- False positives (pattern match that isn't the actual flagged issue)\n- Pre-existing issues not introduced by this diff\n- Issues suppressed by `// NOLINT(...)` comments\n- Exact duplicates between agents or between agents and formatting/CI checks\n\nAssign each remaining finding a confidence score (0-100). Remove findings\nbelow 50. Keep all findings scoring 50 or above -- err on the side of including\nborderline issues rather than missing real ones.\n\n## Step 8: Lightweight Model Critique\n\nSpawn an agent using the cheapest available model (Claude Code: `haiku`;\nCodex: `gpt-5.4-mini`). Provide it with:\n- The SpECTRE code rules reference (from Step 1)\n- The list of pruned findings (with scores)\n- A summary of what the diff does\n\nAsk the critique agent to:\n1. Score each finding 0-100 for \"is this a real, actionable issue?\"\n2. Flag any remaining false positives with reasoning\n3. Note if important issues seem to be missing\n4. Return scores and feedback\n\nAfter receiving the critique agent's feedback:\n- Remove findings scored < 40\n- Downgrade severity (e.g., important -> suggestion) for findings scored\n  40-60\n- Consider adding issues the critique agent suggested (verify them first)\n\n## Step 9: Final Report\n\nRead `references/report-template.md` and present the report in that format.\n"
  },
  {
    "path": ".skills/spectre-review/references/coverage-steps.md",
    "content": "# Code Coverage Steps\n\nThese instructions are followed by the orchestrator when \"coverage\" was in\nthe review arguments. Execute all sub-steps below exactly as written.\n\n### 6a. Check out PR branch (PR mode only)\n\nCoverage must be measured against the actual PR code, not the current working\ntree. Before building or running any tests:\n\n1. Record the current branch and stash any uncommitted changes so they can be\n   restored afterwards:\n   ```bash\n   ORIGINAL_BRANCH=$(git branch --show-current)\n   STASH_RESULT=$(git stash push --include-untracked -m \"coverage-stash\" 2>&1)\n   STASHED=$([[ \"$STASH_RESULT\" == *\"Saved\"* ]] && echo yes || echo no)\n   ```\n2. Attempt to check out the PR branch using the GitHub CLI:\n   ```bash\n   gh pr checkout <N> --repo sxs-collaboration/spectre 2>&1\n   ```\n   If that fails (e.g. SSH key / remote mismatch), fall back to:\n   ```bash\n   git fetch upstream pull/<N>/head:pr-<N> 2>&1\n   git checkout pr-<N> 2>&1\n   ```\n3. **Handle checkout conflicts explicitly.** If checkout fails with any error\n   (e.g. \"Your local changes to the following files would be overwritten\",\n   \"untracked working tree files would be overwritten by checkout\", or \"Please\n   commit your changes or stash them\"), do NOT attempt to force-checkout.\n   Instead, abort coverage and report clearly:\n   > \"Coverage analysis skipped: checking out PR branch '<headRefName>' failed\n   > because the following local files conflict with the PR:\n   >   <list conflicting files from the git error message>\n   > Please resolve the conflict manually (commit, stash, or delete those files)\n   > and re-run the review with 'coverage'.\"\n   Then restore the original branch and pop the stash (step 5) and skip all\n   remaining coverage steps.\n4. Verify the checkout succeeded by comparing HEAD to the expected PR commit:\n   ```bash\n   git log --oneline -1\n   gh pr view <N> --repo sxs-collaboration/spectre \\\n       --json headRefOid -q .headRefOid\n   ```\n   The two commit hashes should match (or the local log should show the PR\n   branch name). If they differ, report: \"Coverage skipped: checkout succeeded\n   but HEAD does not match PR head — local branch may be stale.\"\n5. **Restore step (must run after all coverage steps, or on any failure after\n   this point):** at the end of Step 6f, restore the original working state:\n   ```bash\n   git checkout \"$ORIGINAL_BRANCH\"\n   [[ \"$STASHED\" == \"yes\" ]] && git stash pop\n   ```\n\n### 6b. Prerequisites & Build Verification\n\n1. Check `build/compile_commands.json` exists. If missing, skip with: \"Coverage\n   requires a configured build directory.\"\n2. Check `build/CMakeCache.txt` for `COVERAGE:BOOL=ON`. If not found, skip with:\n   \"Build not compiled with -DCOVERAGE=ON. Rebuild with\n   `cmake -DCOVERAGE=ON ..` to enable coverage.\"\n3. Check `lcov` is available.\n4. **Verify the gcov wrapper is functional.** For Clang builds,\n   `build/llvm-gcov.sh` may contain `LLVM_COV_BIN-NOTFOUND` if `llvm-cov` was\n   not found at CMake configure time. Check:\n   ```bash\n   grep \"NOTFOUND\" build/llvm-gcov.sh\n   ```\n   If found, locate the correct binary and fix the wrapper in-place:\n   ```bash\n   LLVM_VER=$(clang++ --version | grep -oP '\\d+' | head -1)\n   LLVM_COV=$(which llvm-cov-${LLVM_VER} 2>/dev/null || which llvm-cov)\n   printf '#!/bin/bash\\nexec %s gcov \"$@\"\\n' \"$LLVM_COV\" > build/llvm-gcov.sh\n   chmod +x build/llvm-gcov.sh\n   ```\n5. Determine the gcov tool and **always use its absolute path** to avoid lcov\n   failing with \"No such file or directory\" when it resolves relative paths\n   from a different working directory:\n   - Clang: `GCOV=$(realpath build/llvm-gcov.sh)`\n   - GNU: `GCOV=$(which gcov)`\n6. **Verify objects are instrumented.** Pick one `.o` file for a changed source\n   and check it has coverage symbols:\n   ```bash\n   nm <path/to/the_file.cpp.o> 2>/dev/null | grep -c \"__llvm_gcov_ctr\\|__gcov_\"\n   ```\n   If the count is 0, objects were compiled before coverage was enabled —\n   rebuild the relevant test targets:\n   ```bash\n   cmake --build build --target <TestTarget> -- -j$(nproc)\n   ```\n   Re-check instrumentation after the rebuild. If still 0, skip coverage with:\n   \"Objects lack coverage instrumentation even after rebuild. Check that the\n   build was configured with -DCOVERAGE=ON before compiling.\"\n\n### 6c. Map changed files to test targets\nFor each changed C++ file under `src/`:\n1. Find the corresponding test directory: `src/A/B/C.hpp` -> `tests/Unit/A/B/`\n2. Walk up directories reading `CMakeLists.txt` files for `set(LIBRARY\n   \"Test_...\")` to find the test binary name. Test naming is NOT a simple\n   transform (e.g., `Test_DomainCreators`, `Test_EllipticDG`, `Test_DgSubcell`),\n   so CMakeLists.txt parsing is required.\n3. Verify each binary exists in `build/bin/`. If not, build it: `cmake --build\n   build --target <Target> -- -j$(nproc)`\n4. If >5 unique test targets, ask the user before proceeding.\n\n### 6d. Run tests and capture coverage\nFor each test target (using `$GCOV` absolute path from Step 6b):\n```bash\nlcov --gcov-tool $GCOV --directory build/ --zerocounters\nbuild/bin/<TestTarget>\nlcov --gcov-tool $GCOV --capture --rc lcov_branch_coverage=0 \\\n  --directory build/ --output-file /tmp/coverage_<TestTarget>.info\n```\nAfter the lcov capture, verify `.gcda` files were actually produced:\n```bash\nfind build/ -name \"*.gcda\" | grep -q .\n```\nIf no `.gcda` files are found, report:\n> \"No coverage data files (.gcda) generated. Possible causes: (1) objects were\n> not compiled with --coverage (rebuild required after confirming\n> -DCOVERAGE=ON), (2) test binary exited before writing data, (3) build\n> directory path mismatch between compile and run.\"\nThen skip remaining coverage steps (but still run Step 6f to restore the\nbranch).\n\n### 6e. Filter to changed lines only\n1. Merge .info files: `lcov --add-tracefile ... --output-file\n   /tmp/coverage_merged.info`\n2. Extract only changed source files: `lcov --extract /tmp/coverage_merged.info\n   '<abs-path-to-file>' ... -o /tmp/coverage_filtered.info`\n3. Parse `DA:<line>,<count>` entries from the filtered .info file\n4. Cross-reference with diff hunk headers: only report lines that are both NEW\n   in the diff AND have execution_count == 0. **Use new-file line numbers from\n   the diff** — after checking out the PR branch these match the files on disk\n   exactly. Do not use base-branch line numbers, which are offset from the PR.\n5. Group uncovered lines into contiguous ranges\n\n### 6f. Cleanup\nRemove temporary .info files to avoid polluting subsequent builds:\n```bash\nrm -f /tmp/coverage_*.info /tmp/coverage_merged.info /tmp/coverage_filtered.info\n```\nThen restore the original branch and unstash local changes (PR mode only):\n```bash\ngit checkout \"$ORIGINAL_BRANCH\"\n[[ \"$STASHED\" == \"yes\" ]] && git stash pop\n```\n"
  },
  {
    "path": ".skills/spectre-review/references/report-template.md",
    "content": "# Final Report Template\n\nPresent a clean report in the format below. Be brief and specific. No emojis.\nLink to files and lines.\n\n```\n### SpECTRE Code Review\n\n**Reviewing**: [PR #N: title | local changes on branch `name`]\n**Files changed**: N (X C++, Y Python, Z CMake)\n\n#### Formatting\n[clang-format / black / isort changes, or \"No formatting issues.\"]\n\n#### Critical Issues\nN. **Description** `file:line`\n   Explanation and suggested fix.\n\n#### Important Issues\nN. **Description** `file:line`\n   Explanation.\n\n#### Suggestions\nN. **Description** `file:line`\n   Explanation.\n\n#### CI Pre-Check Warnings\n[Issues that CI will flag, or \"None.\"]\n\n[If clang-tidy ran:]\n#### clang-tidy\n[Findings or \"No issues.\"]\n\n[If coverage ran:]\n#### Code Coverage (changed lines)\n[Uncovered changed lines per file, or \"All changed lines are covered by tests.\"]\n[Files with no test target: \"Coverage not checked: file1.cpp, file2.hpp (no test\ntarget found)\"]\n```\n\nIf no issues at all: \"No issues found. Checked style, patterns, bugs,\nformatting, and CI compliance.\"\n"
  },
  {
    "path": "CITATION.cff",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This file is automatically generated. It will be overwritten at every\n# release. See .github/scripts/Release.py for details.\n\nauthors:\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Deppe\n  given-names: Nils\n  orcid: https://orcid.org/0000-0003-4557-4115\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Throwe\n  given-names: William\n  orcid: https://orcid.org/0000-0001-5059-4378\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Kidder\n  given-names: Lawrence E.\n  orcid: https://orcid.org/0000-0001-5392-7342\n- affiliation: Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  family-names: Vu\n  given-names: Nils L.\n  orcid: https://orcid.org/0000-0002-5767-3949\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Nelli\n  given-names: Kyle C.\n  orcid: https://orcid.org/0000-0003-2426-8768\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Armaza\n  given-names: Cristóbal\n  orcid: https://orcid.org/0000-0002-1791-0743\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Bonilla\n  given-names: Marceline S.\n  orcid: https://orcid.org/0000-0003-4502-528X\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Hébert\n  given-names: François\n  orcid: https://orcid.org/0000-0001-9009-6955\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Kim\n  given-names: Yoonsoo\n  orcid: https://orcid.org/0000-0002-4305-6026\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Kumar\n  given-names: Prayush\n  orcid: https://orcid.org/0000-0001-5523-4603\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Lovelace\n  given-names: Geoffrey\n  orcid: https://orcid.org/0000-0002-7084-1070\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Macedo\n  given-names: Alexandra\n  orcid: https://orcid.org/0009-0001-7671-6377\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Moxon\n  given-names: Jordan\n  orcid: https://orcid.org/0000-0001-9891-8677\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: O'Shea\n  given-names: Eamonn\n- affiliation: Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  family-names: Pfeiffer\n  given-names: Harald P.\n  orcid: https://orcid.org/0000-0001-9288-519X\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Scheel\n  given-names: Mark A.\n  orcid: https://orcid.org/0000-0001-6656-9134\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA and Cornell Center\n    for Astrophysics and Planetary Science, Cornell University, Ithaca, New York 14853,\n    USA\n  family-names: Teukolsky\n  given-names: Saul A.\n  orcid: https://orcid.org/0000-0001-9765-4526\n- affiliation: Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  family-names: Wittek\n  given-names: Nikolas A.\n  orcid: https://orcid.org/0000-0001-8575-5450\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Anantpurkar\n  given-names: Isha\n  orcid: https://orcid.org/0000-0002-5814-4109\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Anderson\n  given-names: Carter\n  orcid: https://orcid.org/0009-0009-0359-6763\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Boyle\n  given-names: Michael\n  orcid: https://orcid.org/0000-0002-5075-5116\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Carpenter\n  given-names: Alexander\n  orcid: https://orcid.org/0000-0002-9183-8006\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Ceja\n  given-names: Andrea\n  orcid: https://orcid.org/0000-0002-1681-7299\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Chaudhary\n  given-names: Himanshu\n  orcid: https://orcid.org/0000-0002-4101-0534\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Corso\n  given-names: Nicholas\n  orcid: https://orcid.org/0000-0002-0088-2563\n- affiliation: Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  family-names: Dittmer\n  given-names: Clemens\n  orcid: https://orcid.org/0009-0007-3833-9252\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Fayyazuddin Ljungberg\n  given-names: Nora\n  orcid: https://orcid.org/0000-0002-7074-4545\n- affiliation: Department of Physics & Astronomy, University of New Hampshire, 9 Library\n    Way, Durham NH 03824, USA\n  family-names: Foucart\n  given-names: Francois\n  orcid: https://orcid.org/0000-0003-4617-4738\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Ghadiri\n  given-names: Noora\n  orcid: https://orcid.org/0000-0001-9162-4449\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Giesler\n  given-names: Matthew\n  orcid: https://orcid.org/0000-0003-2300-893X\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Guo\n  given-names: Jason S.\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Habib\n  given-names: Sarah\n  orcid: https://orcid.org/0000-0002-4725-4978\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Huang\n  given-names: Chenhang\n  orcid: https://orcid.org/0000-0003-2617-1011\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Iozzo\n  given-names: Dante A. B.\n  orcid: https://orcid.org/0000-0002-7244-1900\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Jones\n  given-names: Ken Z.\n  orcid: https://orcid.org/0009-0003-1034-0498\n- affiliation: Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  family-names: Lara\n  given-names: Guillermo\n  orcid: https://orcid.org/0000-0001-9461-6292\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Legred\n  given-names: Isaac\n  orcid: https://orcid.org/0000-0002-9523-9617\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Li\n  given-names: Dongjun\n  orcid: https://orcid.org/0000-0002-1962-680X\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Ma\n  given-names: Sizheng\n  orcid: https://orcid.org/0000-0002-4645-453X\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Melchor\n  given-names: Denyz\n  orcid: https://orcid.org/0000-0002-7854-1953\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA and Department of\n    Physics and Astronomy, Oberlin College, Oberlin, Ohio 44074, USA\n  family-names: Mendes\n  given-names: Iago\n  orcid: https://orcid.org/0009-0007-9845-8448\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Morales\n  given-names: Marlo\n  orcid: https://orcid.org/0000-0002-0593-4318\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Most\n  given-names: Elias R.\n  orcid: https://orcid.org/0000-0002-0491-1210\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Murphy\n  given-names: Michael\n  orcid: https://orcid.org/0000-0003-3865-3034\n- affiliation: Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  family-names: Nee\n  given-names: Peter James\n  orcid: https://orcid.org/0000-0002-2362-5420\n- affiliation: Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  family-names: Nishimura\n  given-names: Nami\n  orcid: https://orcid.org/0009-0002-4398-9130\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Osorio\n  given-names: Alejandro\n  orcid: https://orcid.org/0009-0000-5024-0086\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Pajkos\n  given-names: Michael A.\n  orcid: https://orcid.org/0000-0002-4983-4589\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Pannone\n  given-names: Kyle\n  orcid: https://orcid.org/0009-0005-8607-2113\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Pineda\n  given-names: Jose Maria\n  orcid: https://orcid.org/0009-0007-6694-8434\n- affiliation: International Centre for Theoretical Sciences - Tata Institute of Fundamental\n    Research (ICTS-TIFR), Survey No. 151, Shivakote, Hesaraghatta Hobli, Bengaluru\n    - 560 089, Karnataka, India.\n  family-names: Prasad\n  given-names: Vaishak\n  orcid: https://orcid.org/0000-0001-6712-2457\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Ramirez\n  given-names: Teresita\n  orcid: https://orcid.org/0000-0003-0994-115X\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Ring\n  given-names: Noah\n  orcid: https://orcid.org/0000-0001-6510-4300\n- affiliation: CFisUC, Department of Physics, University of Coimbra, 3004-516 Coimbra,\n    Portugal\n  family-names: Rüter\n  given-names: Hannes R.\n  orcid: https://orcid.org/0000-0002-3442-5360\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Sanchez\n  given-names: Jennifer\n  orcid: https://orcid.org/0000-0002-5335-4924\n- affiliation: Department of Physics and Astronomy, University of Mississippi, University,\n    Mississippi 38677, USA\n  family-names: Stein\n  given-names: Leo C.\n  orcid: https://orcid.org/0000-0001-7559-9597\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Tellez\n  given-names: Daniel\n  orcid: https://orcid.org/0009-0008-7784-2528\n- affiliation: Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California 92831,\n    USA\n  family-names: Thomas\n  given-names: Sierra\n  orcid: https://orcid.org/0000-0003-3574-2090\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Tommasini\n  given-names: Vittoria\n  orcid: https://orcid.org/0009-0001-4440-9751\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA and I. Physikalisches Institut, Universität zu Köln,\n    Zülpicher Straße 77, 50937, Köln, Germany\n  family-names: Vieira\n  given-names: Daniel\n  orcid: https://orcid.org/0000-0001-8019-0390\n- affiliation: Theoretical Astrophysics, Walter Burke Institute for Theoretical Physics,\n    California Institute of Technology, Pasadena, CA 91125, USA\n  family-names: Wang\n  given-names: Xiyue\n  orcid: https://orcid.org/0009-0006-3331-0832\n- affiliation: Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  family-names: Wlodarczyk\n  given-names: Tom\n  orcid: https://orcid.org/0000-0003-0005-348X\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Wu\n  given-names: David\n  orcid: https://orcid.org/0000-0002-5287-4389\n- affiliation: Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  family-names: Yoo\n  given-names: Jooheon\n  orcid: https://orcid.org/0000-0002-3251-0924\ncff-version: 1.2.0\ndate-released: 2026-04-01\ndoi: 10.5281/zenodo.19373346\nkeywords:\n- Software\n- Astrophysics\n- General Relativity\n- Numerical Relativity\n- Multiphysics\n- Gravitational Waves\n- Discontinuous Galerkin\n- Finite Difference\n- Finite Volume\nlicense: MIT\nmessage: Please cite SpECTRE in any publications that make use of its code or data.\n  Cite the latest version that you use in your publication. The citation for this\n  version is listed below.\nreferences:\n- authors:\n  - family-names: Iglberger\n    given-names: Klaus\n  - family-names: Hager\n    given-names: Georg\n  - family-names: Treibig\n    given-names: Jan\n  - family-names: Rüde\n    given-names: Ulrich\n  collection-title: 2012 International Conference on High Performance Computing \\&\n    Simulation (HPCS)\n  doi: 10.1109/HPCSim.2012.6266939\n  pages: 367-373\n  title: High performance smart expression template math libraries\n  type: article\n  year: '2012'\n- authors:\n  - family-names: Iglberger\n    given-names: Klaus\n  - family-names: Hager\n    given-names: Georg\n  - family-names: Treibig\n    given-names: Jan\n  - family-names: R\\\"{u}de\n    given-names: Ulrich\n  doi: 10.1137/110830125\n  number: '2'\n  pages: C42-C69\n  title: 'Expression Templates Revisited: A Performance Analysis of Current Methodologies'\n  type: article\n  url: https://doi.org/10.1137/110830125\n  volume: '34'\n  year: '2012'\n- authors:\n  - family-names: Kale\n    given-names: Laxmikant\n  - family-names: Acun\n    given-names: Bilge\n  - family-names: Bak\n    given-names: Seonmyeong\n  - family-names: Becker\n    given-names: Aaron\n  - family-names: Bhandarkar\n    given-names: Milind\n  - family-names: Bhat\n    given-names: Nitin\n  - family-names: Bhatele\n    given-names: Abhinav\n  - family-names: Bohm\n    given-names: Eric\n  - family-names: Bordage\n    given-names: Cyril\n  - family-names: Brunner\n    given-names: Robert\n  - family-names: Buch\n    given-names: Ronak\n  - family-names: Chakravorty\n    given-names: Sayantan\n  - family-names: Chandrasekar\n    given-names: Kavitha\n  - family-names: Choi\n    given-names: Jaemin\n  - family-names: Denardo\n    given-names: Michael\n  - family-names: DeSouza\n    given-names: Jayant\n  - family-names: Diener\n    given-names: Matthias\n  - family-names: Dokania\n    given-names: Harshit\n  - family-names: Dooley\n    given-names: Isaac\n  - family-names: Fenton\n    given-names: Wayne\n  - family-names: Galvez\n    given-names: Juan\n  - family-names: Gioachin\n    given-names: Fillipo\n  - family-names: Gupta\n    given-names: Abhishek\n  - family-names: Gupta\n    given-names: Gagan\n  - family-names: Gupta\n    given-names: Manish\n  - family-names: Gursoy\n    given-names: Attila\n  - family-names: Harsh\n    given-names: Vipul\n  - family-names: Hu\n    given-names: Fang\n  - family-names: Huang\n    given-names: Chao\n  - family-names: Jagathesan\n    given-names: Narain\n  - family-names: Jain\n    given-names: Nikhil\n  - family-names: Jetley\n    given-names: Pritish\n  - family-names: Jindal\n    given-names: Prateek\n  - family-names: Kanakagiri\n    given-names: Raghavendra\n  - family-names: Koenig\n    given-names: Greg\n  - family-names: Krishnan\n    given-names: Sanjeev\n  - family-names: Kumar\n    given-names: Sameer\n  - family-names: Kunzman\n    given-names: David\n  - family-names: Lang\n    given-names: Michael\n  - family-names: Langer\n    given-names: Akhil\n  - family-names: Lawlor\n    given-names: Orion\n  - family-names: Wai Lee\n    given-names: Chee\n  - family-names: Lifflander\n    given-names: Jonathan\n  - family-names: Mahesh\n    given-names: Karthik\n  - family-names: Mendes\n    given-names: Celso\n  - family-names: Menon\n    given-names: Harshitha\n  - family-names: Mei\n    given-names: Chao\n  - family-names: Meneses\n    given-names: Esteban\n  - family-names: Mikida\n    given-names: Eric\n  - family-names: Miller\n    given-names: Phil\n  - family-names: Mokos\n    given-names: Ryan\n  - family-names: Narayanan\n    given-names: Venkatasubrahmanian\n  - family-names: Ni\n    given-names: Xiang\n  - family-names: Nomura\n    given-names: Kevin\n  - family-names: Paranjpye\n    given-names: Sameer\n  - family-names: Ramachandran\n    given-names: Parthasarathy\n  - family-names: Ramkumar\n    given-names: Balkrishna\n  - family-names: Ramos\n    given-names: Evan\n  - family-names: Robson\n    given-names: Michael\n  - family-names: Saboo\n    given-names: Neelam\n  - family-names: Saletore\n    given-names: Vikram\n  - family-names: Sarood\n    given-names: Osman\n  - family-names: Senthil\n    given-names: Karthik\n  - family-names: Shah\n    given-names: Nimish\n  - family-names: Shu\n    given-names: Wennie\n  - family-names: B. Sinha\n    given-names: Amitabh\n  - family-names: Sun\n    given-names: Yanhua\n  - family-names: Sura\n    given-names: Zehra\n  - family-names: Totoni\n    given-names: Ehsan\n  - family-names: Varadarajan\n    given-names: Krishnan\n  - family-names: Venkataraman\n    given-names: Ramprasad\n  - family-names: Wang\n    given-names: Jackie\n  - family-names: Wesolowski\n    given-names: Lukasz\n  - family-names: White\n    given-names: Sam\n  - family-names: Wilmarth\n    given-names: Terry\n  - family-names: Wright\n    given-names: Jeff\n  - family-names: Yelon\n    given-names: Joshua\n  - family-names: Zheng\n    given-names: Gengbin\n  doi: 10.5281/zenodo.3370873\n  month: 8\n  title: '{The Charm++ Parallel Programming System}'\n  type: software\n  url: https://charm.cs.illinois.edu\n  year: '2019'\n- authors:\n  - family-names: Kale\n    given-names: Laxmikant\n  - family-names: Acun\n    given-names: Bilge\n  - family-names: Bak\n    given-names: Seonmyeong\n  - family-names: Becker\n    given-names: Aaron\n  - family-names: Bhandarkar\n    given-names: Milind\n  - family-names: Bhat\n    given-names: Nitin\n  - family-names: Bhatele\n    given-names: Abhinav\n  - family-names: Bohm\n    given-names: Eric\n  - family-names: Bordage\n    given-names: Cyril\n  - family-names: Brunner\n    given-names: Robert\n  - family-names: Buch\n    given-names: Ronak\n  - family-names: Chakravorty\n    given-names: Sayantan\n  - family-names: Chandrasekar\n    given-names: Kavitha\n  - family-names: Choi\n    given-names: Jaemin\n  - family-names: Denardo\n    given-names: Michael\n  - family-names: DeSouza\n    given-names: Jayant\n  - family-names: Diener\n    given-names: Matthias\n  - family-names: Dokania\n    given-names: Harshit\n  - family-names: Dooley\n    given-names: Isaac\n  - family-names: Fenton\n    given-names: Wayne\n  - family-names: Fink\n    given-names: Zane\n  - family-names: Galvez\n    given-names: Juan\n  - family-names: Ghosh\n    given-names: Pathikrit\n  - family-names: Gioachin\n    given-names: Fillipo\n  - family-names: Gupta\n    given-names: Abhishek\n  - family-names: Gupta\n    given-names: Gagan\n  - family-names: Gupta\n    given-names: Manish\n  - family-names: Gursoy\n    given-names: Attila\n  - family-names: Harsh\n    given-names: Vipul\n  - family-names: Hu\n    given-names: Fang\n  - family-names: Huang\n    given-names: Chao\n  - family-names: Jagathesan\n    given-names: Narain\n  - family-names: Jain\n    given-names: Nikhil\n  - family-names: Jetley\n    given-names: Pritish\n  - family-names: Jindal\n    given-names: Prateek\n  - family-names: Kanakagiri\n    given-names: Raghavendra\n  - family-names: Koenig\n    given-names: Greg\n  - family-names: Krishnan\n    given-names: Sanjeev\n  - family-names: Kumar\n    given-names: Sameer\n  - family-names: Kunzman\n    given-names: David\n  - family-names: Lang\n    given-names: Michael\n  - family-names: Langer\n    given-names: Akhil\n  - family-names: Lawlor\n    given-names: Orion\n  - family-names: Lee\n    given-names: Chee Wai\n  - family-names: Lifflander\n    given-names: Jonathan\n  - family-names: Mahesh\n    given-names: Karthik\n  - family-names: Mendes\n    given-names: Celso\n  - family-names: Menon\n    given-names: Harshitha\n  - family-names: Mei\n    given-names: Chao\n  - family-names: Meneses\n    given-names: Esteban\n  - family-names: Mikida\n    given-names: Eric\n  - family-names: Miller\n    given-names: Phil\n  - family-names: Mokos\n    given-names: Ryan\n  - family-names: Narayanan\n    given-names: Venkatasubrahmanian\n  - family-names: Ni\n    given-names: Xiang\n  - family-names: Nomura\n    given-names: Kevin\n  - family-names: Paranjpye\n    given-names: Sameer\n  - family-names: Ramachandran\n    given-names: Parthasarathy\n  - family-names: Ramkumar\n    given-names: Balkrishna\n  - family-names: Ramos\n    given-names: Evan\n  - family-names: Robson\n    given-names: Michael\n  - family-names: Saboo\n    given-names: Neelam\n  - family-names: Saletore\n    given-names: Vikram\n  - family-names: Sarood\n    given-names: Osman\n  - family-names: Senthil\n    given-names: Karthik\n  - family-names: Shah\n    given-names: Nimish\n  - family-names: Shu\n    given-names: Wennie\n  - family-names: Sinha\n    given-names: Amitabh B.\n  - family-names: Sun\n    given-names: Yanhua\n  - family-names: Sura\n    given-names: Zehra\n  - family-names: Szaday\n    given-names: Justin\n  - family-names: Totoni\n    given-names: Ehsan\n  - family-names: Varadarajan\n    given-names: Krishnan\n  - family-names: Venkataraman\n    given-names: Ramprasad\n  - family-names: Wang\n    given-names: Jackie\n  - family-names: Wesolowski\n    given-names: Lukasz\n  - family-names: White\n    given-names: Sam\n  - family-names: Wilmarth\n    given-names: Terry\n  - family-names: Wright\n    given-names: Jeff\n  - family-names: Yelon\n    given-names: Joshua\n  - family-names: Zheng\n    given-names: Gengbin\n  doi: 10.5281/zenodo.5597907\n  month: 10\n  publisher:\n    name: Zenodo\n  title: '{UIUC-PPL}/charm: {Charm++} version 7.0.0'\n  type: software\n  url: https://doi.org/10.5281/zenodo.5597907\n  version: v7.0.0\n  year: '2021'\n- authors:\n  - family-names: Kale\n    given-names: Laxmikant V\n  - family-names: Krishnan\n    given-names: Sanjeev\n  collection-title: Parallel programming using C++\n  pages: 175--213\n  publisher:\n    name: The MIT Press\n  title: 'Charm++: Parallel programming with message-driven objects'\n  type: article\n  year: '1996'\n- authors:\n  - family-names: Galassi\n    given-names: M.\n  - name: others\n  edition: '3'\n  isbn: '9780954612078'\n  publisher:\n    name: Network Theory Ltd.\n  title: '{GNU} Scientific Library Reference Manual'\n  type: book\n  year: '2009'\n- authors:\n  - name: '{The HDF Group}'\n  title: '{Hierarchical Data Format, version 5}'\n  type: software\n  year: 1997-2023\n- authors:\n  - family-names: '{Reinecke}'\n    given-names: M.\n  - family-names: '{Seljebotn}'\n    given-names: D. S.\n  doi: 10.1051/0004-6361/201321494\n  month: 6\n  pages: A112\n  title: '{Libsharp - spherical harmonic transforms revisited}'\n  type: article\n  volume: '554'\n  year: '2013'\n- authors:\n  - family-names: Heinecke\n    given-names: Alexander\n  - family-names: Henry\n    given-names: Greg\n  - family-names: Hutchinson\n    given-names: Maxwell\n  - family-names: Pabst\n    given-names: Hans\n  collection-title: Proceedings of the International Conference for High Performance\n    Computing, Networking, Storage and Analysis\n  isbn: '9781467388153'\n  pages: 1-11\n  publisher:\n    name: IEEE Press\n  title: '{LIBXSMM}: {Accelerating} Small Matrix Multiplications by Runtime Code Generation'\n  type: article\n  year: '2016'\n- authors:\n  - family-names: Hunter\n    given-names: J. D.\n  doi: 10.1109/MCSE.2007.55\n  number: '3'\n  pages: 90--95\n  publisher:\n    name: IEEE COMPUTER SOC\n  title: 'Matplotlib: A 2D graphics environment'\n  type: article\n  volume: '9'\n  year: '2007'\n- authors:\n  - family-names: Caswell\n    given-names: Thomas A\n  - family-names: Droettboom\n    given-names: Michael\n  - family-names: Lee\n    given-names: Antony\n  - family-names: Hunter\n    given-names: John\n  - family-names: Firing\n    given-names: Eric\n  - family-names: Andrade\n    given-names: Elliott Sales\n    name-particle: de\n  - family-names: Hoffmann\n    given-names: Tim\n  - family-names: Stansby\n    given-names: David\n  - family-names: Klymak\n    given-names: Jody\n  - family-names: Varoquaux\n    given-names: Nelle\n  - family-names: Nielsen\n    given-names: Jens Hedegaard\n  - family-names: Root\n    given-names: Benjamin\n  - family-names: May\n    given-names: Ryan\n  - family-names: Elson\n    given-names: Phil\n  - family-names: Dale\n    given-names: Darren\n  - family-names: Lee\n    given-names: Jae-Joon\n  - family-names: Seppänen\n    given-names: Jouni K.\n  - family-names: McDougall\n    given-names: Damon\n  - family-names: Straw\n    given-names: Andrew\n  - family-names: Hobson\n    given-names: Paul\n  - family-names: Gohlke\n    given-names: Christoph\n  - family-names: Yu\n    given-names: Tony S\n  - family-names: Ma\n    given-names: Eric\n  - family-names: Vincent\n    given-names: Adrien F.\n  - family-names: Silvester\n    given-names: Steven\n  - family-names: Moad\n    given-names: Charlie\n  - family-names: Kniazev\n    given-names: Nikita\n  - name: hannah\n  - family-names: Ernest\n    given-names: Elan\n  - family-names: Ivanov\n    given-names: Paul\n  doi: 10.5281/zenodo.3948793\n  month: 7\n  publisher:\n    name: Zenodo\n  title: 'matplotlib/matplotlib: {REL:} v3.3.0'\n  type: software\n  url: https://doi.org/10.5281/zenodo.3948793\n  version: v3.3.0\n  year: '2020'\n- authors:\n  - family-names: Harris\n    given-names: Charles R.\n  - family-names: Millman\n    given-names: K. Jarrod\n  - family-names: Walt\n    given-names: St{\\'{e}}fan J.\n    name-particle: van der\n  - family-names: Gommers\n    given-names: Ralf\n  - family-names: Virtanen\n    given-names: Pauli\n  - family-names: Cournapeau\n    given-names: David\n  - family-names: Wieser\n    given-names: Eric\n  - family-names: Taylor\n    given-names: Julian\n  - family-names: Berg\n    given-names: Sebastian\n  - family-names: Smith\n    given-names: Nathaniel J.\n  - family-names: Kern\n    given-names: Robert\n  - family-names: Picus\n    given-names: Matti\n  - family-names: Hoyer\n    given-names: Stephan\n  - family-names: Kerkwijk\n    given-names: Marten H.\n    name-particle: van\n  - family-names: Brett\n    given-names: Matthew\n  - family-names: Haldane\n    given-names: Allan\n  - family-names: R{\\'{i}}o\n    given-names: Jaime Fern{\\'{a}}ndez\n    name-particle: del\n  - family-names: Wiebe\n    given-names: Mark\n  - family-names: Peterson\n    given-names: Pearu\n  - family-names: G{\\'{e}}rard-Marchant\n    given-names: Pierre\n  - family-names: Sheppard\n    given-names: Kevin\n  - family-names: Reddy\n    given-names: Tyler\n  - family-names: Weckesser\n    given-names: Warren\n  - family-names: Abbasi\n    given-names: Hameer\n  - family-names: Gohlke\n    given-names: Christoph\n  - family-names: Oliphant\n    given-names: Travis E.\n  doi: 10.1038/s41586-020-2649-2\n  month: 9\n  number: '7825'\n  pages: 357--362\n  publisher:\n    name: Springer Science and Business Media {LLC}\n  title: Array programming with {NumPy}\n  type: article\n  url: https://doi.org/10.1038/s41586-020-2649-2\n  volume: '585'\n  year: '2020'\n- authors:\n  - family-names: Ayachit\n    given-names: Utkarsh\n  isbn: '1930934300'\n  publisher:\n    name: Kitware, Inc.\n  title: 'The ParaView Guide: A Parallel Visualization Application'\n  type: book\n  year: '2015'\n- authors:\n  - family-names: Ahrens\n    given-names: J.\n  - family-names: Geveci\n    given-names: Berk\n  - family-names: Law\n    given-names: C.\n  isbn: '9780123875822'\n  publisher:\n    name: Elsevier\n  title: '{ParaView}: {An} end-user tool for large-data visualization'\n  type: book\n  year: '2005'\n- authors:\n  - family-names: Jakob\n    given-names: Wenzel\n  - family-names: Rhinelander\n    given-names: Jason\n  - family-names: Moldovan\n    given-names: Dean\n  title: pybind11 -- Seamless operability between C++11 and Python\n  type: software\n  year: '2017'\n- authors:\n  - family-names: Beder\n    given-names: Jesse\n  - family-names: Woehlke\n    given-names: Matthew\n  - family-names: Breitbart\n    given-names: Jens\n  - family-names: Wolchok\n    given-names: Scott\n  - family-names: Hackimov\n    given-names: Azamat H.\n  - family-names: Snape\n    given-names: Jamie\n  - family-names: Hamlet\n    given-names: Oliver\n  - family-names: Novotny\n    given-names: Paul\n  - family-names: Tambre\n    given-names: Raul\n  - family-names: Reinhold\n    given-names: Stefan\n  - family-names: Vaucher\n    given-names: Alain\n  - family-names: Zaitsev\n    given-names: Alexander\n  - family-names: Anokhin\n    given-names: Alexander\n  - family-names: Karatarakis\n    given-names: Alexander\n  - family-names: Maloney\n    given-names: Andy\n  - family-names: Polukhin\n    given-names: Antony\n  - family-names: Brandenburg\n    given-names: Craig M.\n  - family-names: Ibanez\n    given-names: Dan\n  - family-names: Gladkikh\n    given-names: Denis\n  - family-names: Eich\n    given-names: Florian\n  - family-names: Dumont\n    given-names: Guillaume\n  - family-names: Trigg\n    given-names: Haydn\n  - family-names: King\n    given-names: Jim\n  - family-names: Frederico\n    given-names: Joel\n  - family-names: Hamilton\n    given-names: Jonathan\n  - family-names: Langley\n    given-names: Joseph\n  - family-names: Hämäläinen\n    given-names: Lassi\n  - family-names: Blair\n    given-names: Matt\n  - family-names: Duggan\n    given-names: Michael Welsh\n  - family-names: Wang\n    given-names: Olli\n  - family-names: Stotko\n    given-names: Patrick\n  - family-names: Levine\n    given-names: Peter\n  - family-names: Bena\n    given-names: Petr\n  - family-names: Cordoba\n    given-names: Rodrigo Hernandez\n  - family-names: Schmidt\n    given-names: Ryan\n  - family-names: Gottlieb\n    given-names: Simon Gene\n  - family-names: Prilmeier\n    given-names: Franz\n  - family-names: Zhang\n    given-names: Tanki\n  - family-names: Ishi\n    given-names: Tatsuyuki\n  - family-names: Lyngmo\n    given-names: Ted\n  - family-names: Mataré\n    given-names: Victor\n  - family-names: Konečný\n    given-names: Michael\n  - name: USDOE\n  doi: 10.11578/dc.20220817.13\n  month: 9\n  title: yaml-cpp\n  type: software\n  url: https://www.osti.gov/biblio/1881863\n  year: '2009'\nrepository-code: https://github.com/sxs-collaboration/spectre\ntitle: SpECTRE\nurl: https://spectre-code.org\nversion: 2026.04.01\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\ncmake_minimum_required(VERSION 3.18.0)\n\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_SOURCE_DIR}/cmake\")\n\n# Create a new (or overwrite existing) info file at start of configuration\nfile(WRITE \"${CMAKE_BINARY_DIR}/BuildInfo.txt\" \"\")\n\n# Determine the project version and other metadata\ninclude(SpectreLoadMetadata)\n\n# Set up the project. Notes:\n# - Fortran is needed for SPHEREPACK\nproject(${SPECTRE_NAME} VERSION ${SPECTRE_VERSION} LANGUAGES CXX C Fortran)\n\n# Unset the CMake-defined version variable because it strips zeros from the\n# version components, e.g. 2020.12.07 becomes 2020.12.7\nunset(${SPECTRE_NAME}_VERSION)\n# Also unset the version-component variables because they have no meaning in\n# our versioning scheme\nunset(${SPECTRE_NAME}_VERSION_MAJOR)\nunset(${SPECTRE_NAME}_VERSION_MINOR)\nunset(${SPECTRE_NAME}_VERSION_PATCH)\n\n# Policies\n# The `cmake_minimum_required` above sets policies to `NEW` that are compatible\n# with the given minimum cmake version. Here we overwrite policies that we\n# have back-ported in our cmake code.\n# - We use test names with '.' characters in them\nif (CMAKE_VERSION VERSION_GREATER_EQUAL 3.19)\n  cmake_policy(SET CMP0110 NEW)\nendif ()\n# - We allow both uppercase and case-preserved hints in `find_package` calls, so\n#   `PACKAGENAME_ROOT` and `PackageName_ROOT` are both searched in\n#   `FindPackageName.cmake` (NEW behavior). The OLD behavior was to search only\n#   `PackageName_ROOT` (before CMake v3.27). We have backported the NEW behavior\n#   by adding the uppercase name explicitly to the list of hints.\nif (CMAKE_VERSION VERSION_GREATER_EQUAL 3.27)\n  cmake_policy(SET CMP0144 NEW)\nendif()\n# - The `NEW` behavior of `DOWNLOAD_EXTRACT_TIMESTAMP` is recommended.\nif (CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)\n  cmake_policy(SET CMP0135 NEW)\nendif()\n# - We allow CMake 3.30 and above to load Boost's CMake config file that is\n#   installed alongside Boost. Before, CMake provided its own FindBoost module.\nif (CMAKE_VERSION VERSION_GREATER_EQUAL 3.30)\n  cmake_policy(SET CMP0167 NEW)\nendif ()\n\n# FetchContent can be used to download and build some dependencies. We can\n# consider turning this on by default later.\noption(SPECTRE_FETCH_MISSING_DEPS \"Download missing dependencies\" OFF)\n# - Mark FetchContent as 'SYSTEM' to suppress warnings.\n#   Supported since CMake version 3.25.\nif (CMAKE_VERSION VERSION_GREATER_EQUAL 3.25)\n  set(SPECTRE_FETCHCONTENT_BASE_ARGS SYSTEM)\nelse()\n  set(SPECTRE_FETCHCONTENT_BASE_ARGS \"\")\nendif()\n\n# Define standard installation directories\ninclude(GNUInstallDirs)\n# Disable `make install` depending on `make all` since we want to control what\n# we install more closely. With this setting, and targets marked as `OPTIONAL`,\n# only targets that were built will be installed.\nset(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY ON)\n\nset(CMAKE_VERBOSE_MAKEFILE OFF)\n\ninclude(SpectreGetGitHash)\ninclude(SpectreSetSiteName)\n\n# We need Python in the build system and throughout the code. Setting it up\n# early so we can rely on a consistent Python version in the build system.\noption(ENABLE_PYTHON \"Enable Python\" ON)\nif (ENABLE_PYTHON)\n  find_package(Python 3.8 REQUIRED)\n  # Define the location of Python code in the build directory. Unit tests etc. can\n  # add this location to their `PYTHONPATH`.\n  set(SPECTRE_PYTHON_PREFIX_PARENT \"${CMAKE_BINARY_DIR}/bin/python\")\n  get_filename_component(\n    SPECTRE_PYTHON_PREFIX_PARENT \"${SPECTRE_PYTHON_PREFIX_PARENT}\" ABSOLUTE)\n  set(SPECTRE_PYTHON_SITE_PACKAGES \"${CMAKE_BINARY_DIR}/lib/\\\npython${Python_VERSION_MAJOR}.${Python_VERSION_MINOR}/site-packages\")\n  set(PYTHONPATH \"${SPECTRE_PYTHON_PREFIX_PARENT}:\\\n${SPECTRE_PYTHON_SITE_PACKAGES}\")\n  if(DEFINED ENV{PYTHONPATH})\n    set(PYTHONPATH \"${PYTHONPATH}:$ENV{PYTHONPATH}\")\n  endif()\n  message(STATUS \"PYTHONPATH: ${PYTHONPATH}\")\n  include(BootstrapPyDeps)\nelse()\n  # We still need minimal Python for linking purpose.\n  find_package(Python REQUIRED)\nendif()\n\noption(BUILD_DOCS \"Enable building documentation\" ON)\noption(\n  DOCS_ONLY\n  \"Skip all initialization not required for rendering documentation\"\n  OFF\n)\nif (BUILD_DOCS AND DOCS_ONLY)\n  include(SetupDoxygen)\n  return()\nendif()\n\noption(USE_PCH \"Use precompiled headers\" ON)\n\ninclude(SpectreInitializeVariables)\n\ninclude(CheckCompilerVersion)\ninclude(ProhibitInSourceBuild)\ninclude(SpectreSetupFlagsTarget)\ninclude(SetupFortran)\ninclude(SetupNinjaColors)\ninclude(SetOutputDirectory)\ninclude(SpectreAddInterfaceLibraryHeaders)\ninclude(SpectreTargetHeaders)\ninclude(SpectreTargetSources)\ninclude(SetupFormaline)\ninclude(SetupGitHooks)\ninclude(SetupLicenseInfo)\ninclude(SetBuildType)\ninclude(SetupPic)\ninclude(SetupLinkTimeOptimization)\ninclude(SetCxxStandard)\ninclude(StripSymbols)\n# We need Boost for InfoAtLink\ninclude(SetupBoost)\ninclude(SetupInformer)\ninclude(SetupCCache)\ninclude(SetupCharm)\ninclude(EnableWarnings)\ninclude(SetupGoldOrLldLinker)\n\n# In order to use certain code analysis tools like clang-tidy and cppcheck the\n# compile commands need to be accessible. CMake can write these to a\n# \"compile_commands.json\" file.\nset(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n\ninclude(SetupLIBCXX)\ninclude(SetupSpectreInlining)\ninclude(SetupCxxFlags)\ninclude(SetupProfiling)\ninclude(SetupSanitizers)\ninclude(SetupListTargets)\ninclude(AddSpectreExecutable)\ninclude(CheckBrokenArray0)\n\n# Blaze depends on both Blas and Lapack, since LIBXSMM is a Blas\n# alternative we set it up early too. Finally, Blaze uses the GSL\n# blas headers, so we need to set that up before Blaze as well.\ninclude(SetupBlas)\ninclude(SetupLapack)\ninclude(SetupLIBXSMM)\ninclude(SetupGsl)\n\ninclude(SetupAutodiff)\ninclude(SetupBlaze)\ninclude(SetupFuka)\ninclude(SetupGoogleBenchmark)\ninclude(SetupHdf5)\ninclude(SetupAllocator)\ninclude(SetupPapi)\ninclude(SetupPybind11)\ninclude(SetupSpec)\ninclude(SetupStl)\ninclude(SetupXsimd)\ninclude(SetupYamlCpp)\ninclude(SetupOpenMP)\ninclude(SetupParaView)\n\nadd_subdirectory(external)\n\ninclude(SetupKokkos)\n\ninclude(SetupLIBCXXCharm)\n# The precompiled header must be setup after all libraries have been found\nif (USE_PCH)\n  include(SetupPch)\nendif()\n\n# All special targets and configs that need to be applied to *all*\n# executables must be added at once in the 'UpdateAddExecutables' file.\n# This is because of what is likely a bug in CMake where if a function is\n# overridden multiple times (using the _function_name(...) method) then some\n# versions of CMake (at least 3.13.2) segfault.\ninclude(UpdateAddExecutables)\n\n# The ClangFormat, clang-tidy, Doxygen, and CodeCov are intentionally\n# after the PCH setup because that way they are able to change their\n# dependencies on the PCH if necessary.\ninclude(SetupClangFormat)\ninclude(SetupClangTidy)\nif(BUILD_DOCS)\n  include(SetupDoxygen)\n  include(SetupSphinx)\nendif()\ninclude(CodeCoverageDetection)\ninclude(SpectreAddLibraries)\n\ninclude(SpectreSetupTesting)\nif(BUILD_TESTING)\n  include(SetupCatch)\n  include(SetupPypp)\n  include(SpectreAddTestLibs)\n  include(SpectreAddCatchTests)\n  include(AddInputFileTests)\n  include(AddStandaloneTests)\nendif()\n\ninclude(SpectreSetupPythonPackage)\n\n# Set global include directory for all targets\ninclude_directories(${CMAKE_SOURCE_DIR}/src)\n# Charm++ generated headers are created in the build directory\n# (by `add_charm_module`)\ninclude_directories(SYSTEM ${CMAKE_BINARY_DIR}/src)\n\nadd_subdirectory(src)\nadd_subdirectory(support)\nif(BUILD_TESTING)\n  add_subdirectory(tests)\nendif()\nadd_subdirectory(tools)\n\ninclude(PrintUsefulCMakeInfo)\n\ninclude(SpectreCheckDependencies)\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "==============================================================================\nSpECTRE Release License\n==============================================================================\n\nCopyright 2017 - 2026 Simulating eXtreme Spacetimes Collaboration\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "Metadata.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nName: SpECTRE\n\nLicense: MIT\n\nHomepage: https://spectre-code.org\n\nGitHub: sxs-collaboration/spectre\n\nVersion: 2026.04.01\n\nPublicationDate: 2026-04-01\n\nDoi: 10.5281/zenodo.19373346\n\nZenodoId: 19373346\n\nDescription: |\n  SpECTRE is an open-source code for multi-scale, multi-physics problems\n  in astrophysics and gravitational physics. In the future, we hope that\n  it can be applied to problems across discipline boundaries in fluid\n  dynamics, geoscience, plasma physics, nuclear physics, and\n  engineering. It runs at petascale and is designed for future exascale\n  computers.\n\n  SpECTRE is being developed in support of our collaborative Simulating\n  eXtreme Spacetimes (SXS) research program into the multi-messenger\n  astrophysics of neutron star mergers, core-collapse supernovae, and\n  gamma-ray bursts.\n\nKeywords:\n  - Software\n  - Astrophysics\n  - General Relativity\n  - Numerical Relativity\n  - Multiphysics\n  - Gravitational Waves\n  - Discontinuous Galerkin\n  - Finite Difference\n  - Finite Volume\n\nAffiliations:\n  - &aei\n    Max Planck Institute for Gravitational Physics (Albert Einstein Institute),\n    Am Mühlenberg 1, Potsdam 14476, Germany\n  - &caltech\n    Theoretical Astrophysics, Walter Burke Institute for Theoretical\n    Physics, California Institute of Technology, Pasadena, CA 91125, USA\n  - &cornell\n    Cornell Center for Astrophysics and Planetary Science, Cornell University,\n    Ithaca, New York 14853, USA\n  - &coimbra\n    CFisUC, Department of Physics, University of Coimbra,\n    3004-516 Coimbra, Portugal\n  - &fullerton\n    Nicholas and Lee Begovich Center for Gravitational-Wave Physics and\n    Astronomy, California State University, Fullerton, Fullerton, California\n    92831, USA\n  - &icts\n    International Centre for Theoretical Sciences - Tata Institute of\n    Fundamental Research (ICTS-TIFR), Survey No. 151, Shivakote,\n    Hesaraghatta Hobli, Bengaluru - 560 089, Karnataka, India.\n  - &new_hampshire\n    Department of Physics & Astronomy, University of New Hampshire, 9 Library\n    Way, Durham NH 03824, USA\n  - &koln\n    I. Physikalisches Institut, Universität zu Köln, Zülpicher Straße 77,\n    50937, Köln, Germany\n  - &umiss\n    Department of Physics and Astronomy, University of Mississippi,\n    University, Mississippi 38677, USA\n  - &oberlin\n    Department of Physics and Astronomy, Oberlin College, Oberlin, Ohio 44074,\n    USA\n\nAuthors:\n  Description: |\n    We maintain three tiers of authors for the code as a whole: Core, Developers\n    and Contributors. The full author list for the SpECTRE DOI is built by\n    concatenating all authors in the order they are listed here.\n\n    The primary goal of this author list is to recognize infrastructure work\n    that does not or cannot have any science papers and results associated with\n    it. Through authorship on the SpECTRE DOI such work gains recognition that\n    holds value in academia. Authorship on the SpECTRE DOI does not directly\n    entitle a person for authorship rights on other publications. Authorship\n    rights on publications related to SpECTRE are governed by our publication\n    policy (see `docs/PublicationPolicy.md`).\n\n    Note that the placement of authors in these tiers is inherently a subjective\n    topic since we are forced to compare different projects people work on.\n    Nevertheless, having guidelines and some way of promoting healthy and\n    constructive discussions is vital for ensuring the collaborative aspect of\n    the code is healthy.\n\n  Core:\n    Description: |\n      For the time a Developer serves as Core developer they take on some\n      project-management responsibilities, such as initiating and signing off on\n      code reviews, maintaining compatibility with third-party libraries,\n      maintaining the CI/CD infrastructure and overseeing the code as a whole.\n\n      Guidelines on becoming a Core developer:\n\n      - Have at least 4-5 major infrastructure contributions, each taking\n        roughly 4-5 weeks of full-time work for an existing Core developer.\n      - Seek out PRs to review and volunteer when calls for a reviewer are\n        made.\n      - Reach out to any of the existing Core developers and express your\n        interest in taking on the responsibilities that come with this role. The\n        Core developers will discuss the request and either appoint you to Core\n        developer or provide you with feedback on what they think remains to be\n        done.\n\n      Note that most people will not end up in the Core developer list since a\n      lot of work will lead to science results and authorship on science papers,\n      which pay dividends for the work. Supervising students and postdocs does\n      not count towards Core developer status.\n\n      Guidelines on stepping down as Core developer:\n\n      - After a Core developer leaves the project they will remain in this tier\n        for at least two years. At that point the Core developers will discuss\n        if the person should be moved to the Developer tier.\n      - A Core developer can step down from the Core developer role at any time.\n\n      The author list order is decided by the Core developers.\n    List:\n      - Name: Deppe, Nils\n        Orcid: 0000-0003-4557-4115\n        Affiliations: [*caltech]\n        GitHub: nilsdeppe\n      - Name: Throwe, William\n        Orcid: 0000-0001-5059-4378\n        Affiliations: [*cornell]\n        GitHub: wthrowe\n      - Name: Kidder, Lawrence E.\n        Orcid: 0000-0001-5392-7342\n        Affiliations: [*cornell]\n        GitHub: kidder\n      - Name: Vu, Nils L.\n        Orcid: 0000-0002-5767-3949\n        Affiliations: [*aei]\n        GitHub: nilsvu\n      - Name: Nelli, Kyle C.\n        Orcid: 0000-0003-2426-8768\n        Affiliations: [*caltech]\n        GitHub: knelli2\n\n  Developers:\n    Description: |\n      Developers have contributed major features to SpECTRE and remain in this\n      list indefinitely unless they wish to be removed.\n\n      Guidelines on becoming a Developer:\n\n      - Do some code review, does not need to be self-initiated.\n      - Somewhere around 40 commits and 10k-15k lines of code should trigger a\n        discussion among the Core developers to move someone from Contributors\n        to Developers. These are guidelines and being above or below these\n        arbitrary cutoffs does not automatically include or exclude you. E.g.\n        someone doing complicated low-level code will likely become a Developer\n        with far fewer lines of code contributed.\n      - Anyone supervising a Core developer or two Developers may be considered\n        for Developer status. Whether to be placed as Developer or Contributor\n        will depend on how active the supervision is. If most of the supervision\n        is coming from the Core developers then Contributor status is\n        appropriate. This will be discussed with the person supervising the Core\n        developer and/or Developers.\n\n      This author list is ordered by last name.\n    List:\n      - Name: Armaza, Cristóbal\n        Orcid: 0000-0002-1791-0743\n        Affiliations: [*cornell]\n        GitHub: carmaza\n      - Name: Bonilla, Marceline S.\n        Orcid: 0000-0003-4502-528X\n        Affiliations: [*fullerton]\n        GitHub: mar-celine\n      - Name: Hébert, François\n        Orcid: 0000-0001-9009-6955\n        Affiliations: [*caltech]\n        GitHub: fmahebert\n      - Name: Kim, Yoonsoo\n        Orcid: 0000-0002-4305-6026\n        Affiliations: [*caltech]\n        GitHub: yoonso0-0\n      - Name: Kumar, Prayush\n        Orcid: 0000-0001-5523-4603\n        Affiliations: [*cornell]\n        GitHub: prayush\n      - Name: Lovelace, Geoffrey\n        Orcid: 0000-0002-7084-1070\n        Affiliations: [*fullerton]\n        GitHub: geoffrey4444\n      - Name: Macedo, Alexandra\n        Orcid: 0009-0001-7671-6377\n        Affiliations: [*fullerton]\n        GitHub: macedo22\n      - Name: Moxon, Jordan\n        Orcid: 0000-0001-9891-8677\n        Affiliations: [*caltech]\n        GitHub: moxcodes\n      - Name: O'Shea, Eamonn\n        Affiliations: [*cornell]\n        GitHub: osheamonn\n      - Name: Pfeiffer, Harald P.\n        Orcid: 0000-0001-9288-519X\n        Affiliations: [*aei]\n        GitHub: haraldp271\n      - Name: Scheel, Mark A.\n        Orcid: 0000-0001-6656-9134\n        Affiliations: [*caltech]\n        GitHub: markscheel\n      - Name: Teukolsky, Saul A.\n        Orcid: 0000-0001-9765-4526\n        Affiliations: [*caltech, *cornell]\n        GitHub: teukolsky\n      - Name: Wittek, Nikolas A.\n        Orcid: 0000-0001-8575-5450\n        Affiliations: [*aei]\n        GitHub: nikwit\n\n  Contributors:\n    Description: |\n      Anyone who has contributed to SpECTRE will be added to this list and\n      remain a Contributor indefinitely unless they wish to be removed.\n\n      Guidelines on becoming a Contributor:\n\n      - See our guidelines in `docs/CONTRIBUTING.md` for information on how to\n        get involved with SpECTRE development.\n      - Having any PR merged qualifies for this list. If multiple people worked\n        on the PR together, e.g. several undergraduate students contributing to\n        a single PR, all persons involved will become a Contributor.\n      - Supervising a project that results in a merged PR qualifies for this\n        list. The supervision must be active, i.e. not primarily done by the\n        Core developers.\n      - Having an idea for a contribution does not count towards any contributor\n        status. There must be active supervision of the person(s) implementing\n        the idea, such as advice on debugging, how to best implement the idea,\n        etc. The supervision is what will be counted for contributor status.\n        Generally, a paper is written where the person who came up with the idea\n        is a co-author, which is how credit is given for the idea. An example is\n        suggesting \"We should try reconstruction scheme X\" without continued\n        guidance throughout the implementation process would not be counted for\n        contributor status.\n\n      This author list is ordered by last name.\n    List:\n      - Name: Anantpurkar, Isha\n        Orcid: 0000-0002-5814-4109\n        Affiliations: [*cornell]\n        GitHub: isha1810\n      - Name: Anderson, Carter\n        Orcid: 0009-0009-0359-6763\n        Affiliations: [*caltech]\n        GitHub: ctrandrsn\n      - Name: Boyle, Michael\n        Orcid: 0000-0002-5075-5116\n        Affiliations: [*cornell]\n        GitHub: moble\n      - Name: Carpenter, Alexander\n        Orcid: 0000-0002-9183-8006\n        Affiliations: [*fullerton]\n        GitHub: AlexCarpenter46\n      - Name: Ceja, Andrea\n        Orcid: 0000-0002-1681-7299\n        Affiliations: [*fullerton]\n        GitHub: acifajkya\n      - Name: Chaudhary, Himanshu\n        Orcid: 0000-0002-4101-0534\n        Affiliations: [*caltech]\n        GitHub: GitHimanshuc\n      - Name: Corso, Nicholas\n        Orcid: 0000-0002-0088-2563\n        Affiliations: [*cornell]\n        GitHub: ncorsobh\n      - Name: Dittmer, Clemens\n        Orcid: 0009-0007-3833-9252\n        Affiliations: [*aei]\n        GitHub: clemensdittmer\n      - Name: Fayyazuddin Ljungberg, Nora\n        Orcid: 0000-0002-7074-4545\n        Affiliations: [*cornell]\n        GitHub: norafl\n      - Name: Foucart, Francois\n        Orcid: 0000-0003-4617-4738\n        Affiliations: [*new_hampshire]\n        GitHub: ffoucart\n      - Name: Ghadiri, Noora\n        Orcid: 0000-0001-9162-4449\n        Affiliations: [*fullerton]\n        GitHub: noora-gn\n      - Name: Giesler, Matthew\n        Orcid: 0000-0003-2300-893X\n        Affiliations: [*cornell]\n        GitHub: mgiesler\n      - Name: Guo, Jason S.\n        Affiliations: [*cornell]\n        GitHub: erfz\n      - Name: Habib, Sarah\n        Orcid: 0000-0002-4725-4978\n        Affiliations: [*caltech]\n        GitHub: shabibti\n      - Name: Huang, Chenhang\n        Orcid: 0000-0003-2617-1011\n        Affiliations: [*cornell]\n        GitHub: hen-w\n      - Name: Iozzo, Dante A. B.\n        Orcid: 0000-0002-7244-1900\n        Affiliations: [*cornell]\n        GitHub: \"10220\"\n      - Name: Jones, Ken Z.\n        Orcid: 0009-0003-1034-0498\n        Affiliations: [*fullerton]\n        GitHub: \"kenzenjones\"\n      - Name: Lara, Guillermo\n        Orcid: 0000-0001-9461-6292\n        Affiliations: [*aei]\n        GitHub: guilara\n      - Name: Legred, Isaac\n        Orcid: 0000-0002-9523-9617\n        Affiliations: [*caltech]\n        GitHub: isaaclegred\n      - Name: Li, Dongjun\n        Orcid: 0000-0002-1962-680X\n        Affiliations: [*caltech]\n        GitHub: dongjun826\n      - Name: Ma, Sizheng\n        Orcid: 0000-0002-4645-453X\n        Affiliations: [*caltech]\n        GitHub: Sizheng-Ma\n      - Name: Melchor, Denyz\n        Orcid: 0000-0002-7854-1953\n        Affiliations: [*fullerton]\n        GitHub: denyzamelchor\n      - Name: Mendes, Iago\n        Orcid: 0009-0007-9845-8448\n        Affiliations: [*caltech, *oberlin]\n        GitHub: iago-mendes\n      - Name: Morales, Marlo\n        Orcid: 0000-0002-0593-4318\n        Affiliations: [*fullerton]\n        GitHub: MarloMo\n      - Name: Most, Elias R.\n        Orcid: 0000-0002-0491-1210\n        Affiliations: [*caltech]\n        GitHub: ermost\n      - Name: Murphy, Michael\n        Orcid: 0000-0003-3865-3034\n        Affiliations: [*cornell]\n        GitHub: michaeldmurphy1\n      - Name: Nee, Peter James\n        Orcid: 0000-0002-2362-5420\n        Affiliations: [*aei]\n        GitHub: PunJustice\n      - Name: Nishimura, Nami\n        Orcid: 0009-0002-4398-9130\n        Affiliations: [*aei]\n        GitHub: NamiNishimura73\n      - Name: Osorio, Alejandro\n        Orcid: 0009-0000-5024-0086\n        Affiliations: [*fullerton]\n        GitHub: ajosorio3\n      - Name: Pajkos, Michael A.\n        Orcid: 0000-0002-4983-4589\n        Affiliations: [*caltech]\n        GitHub: pajkosmi\n      - Name: Pannone, Kyle\n        Orcid: 0009-0005-8607-2113\n        Affiliations: [*fullerton]\n        GitHub: Bronoulli\n      - Name: Pineda, Jose Maria\n        Orcid: 0009-0007-6694-8434\n        Affiliations: [*fullerton]\n        GitHub: JosePineda684\n      - Name: Prasad, Vaishak\n        Orcid: 0000-0001-6712-2457\n        Affiliations: [*icts]\n        GitHub: vaishakp\n      - Name: Ramirez, Teresita\n        Orcid: 0000-0003-0994-115X\n        Affiliations: [*fullerton]\n        GitHub: trami18\n      - Name: Ring, Noah\n        Orcid: 0000-0001-6510-4300\n        Affiliations: [*cornell]\n        GitHub: nring21\n      - Name: Rüter, Hannes R.\n        Orcid: 0000-0002-3442-5360\n        Affiliations: [*coimbra]\n        GitHub: hrueter\n      - Name: Sanchez, Jennifer\n        Orcid: 0000-0002-5335-4924\n        Affiliations: [*fullerton]\n        GitHub: jennifersanchez\n      - Name: Stein, Leo C.\n        Orcid: 0000-0001-7559-9597\n        Affiliations: [*umiss]\n        GitHub: duetosymmetry\n      - Name: Tellez, Daniel\n        Orcid: 0009-0008-7784-2528\n        Affiliations: [*fullerton]\n        GitHub: daniel20tellez\n      - Name: Thomas, Sierra\n        Orcid: 0000-0003-3574-2090\n        Affiliations: [*fullerton]\n        GitHub: sierracthomas\n      - Name: Tommasini, Vittoria\n        Orcid: 0009-0001-4440-9751\n        Affiliations: [*caltech]\n        GitHub: vtommasini\n      - Name: Vieira, Daniel\n        Orcid: 0000-0001-8019-0390\n        Affiliations: [*cornell, *koln]\n        GitHub: dkvieira\n      - Name: Wang, Xiyue\n        Orcid: 0009-0006-3331-0832\n        Affiliations: [*caltech]\n        GitHub: EmilyWang0130\n      - Name: Wlodarczyk, Tom\n        Orcid: 0000-0003-0005-348X\n        Affiliations: [*aei]\n        GitHub: tomwlodarczyk\n      - Name: Wu, David\n        Orcid: 0000-0002-5287-4389\n        Affiliations: [*cornell]\n        GitHub: DavidWu421\n      - Name: Yoo, Jooheon\n        Orcid: 0000-0002-3251-0924\n        Affiliations: [*cornell]\n        GitHub: jyoo1042\n\nReferences:\n  Description: |\n    SpECTRE depends on these references. The keys refer to entries in the\n    'BibliographyFile'. We use BibTeX for our references so we can also cite\n    them in publications.\n  BibliographyFile: docs/Dependencies.bib\n  List:\n    - Blaze1\n    - Blaze2\n    - Charmpp1\n    - Charmpp2\n    - Charmpp3\n    - Gsl\n    - Hdf5\n    - Libsharp\n    - Libxsmm\n    - MatplotLib1\n    - MatplotLib2\n    - NumPy\n    - Paraview1\n    - Paraview2\n    - Pybind11\n    - Yamlcpp\n"
  },
  {
    "path": "README.md",
    "content": "[//]: # (Distributed under the MIT License.)\n[//]: # (See LICENSE.txt for details.)\n\n[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/sxs-collaboration/spectre/blob/develop/LICENSE.txt)\n[![Standard](https://img.shields.io/badge/c%2B%2B-20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization)\n[![Build Status](https://github.com/sxs-collaboration/spectre/workflows/Tests/badge.svg?branch=develop)](https://github.com/sxs-collaboration/spectre/actions)\n[![codecov](https://codecov.io/gh/sxs-collaboration/spectre/graph/badge.svg?token=yyJ3uBPUE2)](https://codecov.io/gh/sxs-collaboration/spectre)\n[![release](https://img.shields.io/badge/release-v2026.04.01-informational)](https://github.com/sxs-collaboration/spectre/releases/tag/v2026.04.01)\n[![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.19373346.svg)](https://doi.org/10.5281/zenodo.19373346)\n\n![banner](docs/Images/banner.png)\n\n## What is SpECTRE?\n\nSpECTRE is an open-source code for multi-scale, multi-physics problems in\nastrophysics and gravitational physics. It is based on high-order spectral\nfinite element methods and massive parallelism. In the future, we hope that\nit can be applied to problems across discipline boundaries in fluid\ndynamics, geoscience, plasma physics, nuclear physics, and\nengineering. It runs at petascale and is designed for future exascale\ncomputers.\n\nSpECTRE is being developed in support of our collaborative Simulating\neXtreme Spacetimes (SXS) research program into the multi-messenger\nastrophysics of black hole and neutron star mergers, core-collapse supernovae,\nand gamma-ray bursts.\n\n## Gallery\n\nFor an overview of some of SpECTRE's features and some simulations performed\nwith SpECTRE, visit the gallery:\n\n- Gallery: https://spectre-code.org/gallery\n\n## Documentation\n\nYou find instructions on installing and running the code, as well as many guides\nand tutorials, in the documentation:\n\n- Documentation: https://spectre-code.org/\n\n## Citing SpECTRE\n\nPlease cite SpECTRE in any publications that make use of its code or data. Cite\nthe latest version that you use in your publication. The DOI for this version\nis:\n\n- DOI: [10.5281/zenodo.19373346](https://doi.org/10.5281/zenodo.19373346)\n\nYou can cite this BibTeX entry in your publication:\n\n<!-- The BibTeX entry below is updated automatically at releases -->\n<!-- BIBTEX ENTRY -->\n```bib\n@software{spectrecode,\n    author = \"Deppe, Nils and Throwe, William and Kidder, Lawrence E. and Vu,\nNils L. and Nelli, Kyle C. and Armaza, Crist\\'obal and Bonilla, Marceline S. and\nH\\'ebert, Fran\\c{c}ois and Kim, Yoonsoo and Kumar, Prayush and Lovelace,\nGeoffrey and Macedo, Alexandra and Moxon, Jordan and O'Shea, Eamonn and\nPfeiffer, Harald P. and Scheel, Mark A. and Teukolsky, Saul A. and Wittek,\nNikolas A. and others\",\n    title = \"\\texttt{SpECTRE v2026.04.01}\",\n    version = \"2026.04.01\",\n    publisher = \"Zenodo\",\n    doi = \"10.5281/zenodo.19373346\",\n    url = \"https://spectre-code.org\",\n    howpublished =\n\"\\href{https://doi.org/10.5281/zenodo.19373346}{10.5281/zenodo.19373346}\",\n    license = \"MIT\",\n    year = \"2026\",\n    month = \"4\"\n}\n```\n<!-- BIBTEX ENTRY -->\n\nTo aid reproducibility of your scientific results with SpECTRE, we recommend you\nkeep track of the version(s) you used and report this information in your\npublication. We also recommend you supply the YAML input files and, if\nappropriate, any additional C++ code you wrote to compile SpECTRE executables as\nsupplemental material to the publication.\n\nSee our [publication policy](https://spectre-code.org/publication_policies.html)\nfor more information.\n\n## Logos and visuals\n\nYou can find logos and other visuals (e.g. to put on slides) in various formats,\ncolors, and sizes here:\n\n- [Logos and visuals](https://drive.google.com/drive/folders/1GEvVrXNqlGojr3NBf7RMFo6BzFmf9Ww7?usp=share_link)\n"
  },
  {
    "path": "citation.bib",
    "content": "@software{spectrecode,\n    author = \"Deppe, Nils and Throwe, William and Kidder, Lawrence E. and Vu,\nNils L. and Nelli, Kyle C. and Armaza, Crist\\'obal and Bonilla, Marceline S. and\nH\\'ebert, Fran\\c{c}ois and Kim, Yoonsoo and Kumar, Prayush and Lovelace,\nGeoffrey and Macedo, Alexandra and Moxon, Jordan and O'Shea, Eamonn and\nPfeiffer, Harald P. and Scheel, Mark A. and Teukolsky, Saul A. and Wittek,\nNikolas A. and others\",\n    title = \"\\texttt{SpECTRE v2026.04.01}\",\n    version = \"2026.04.01\",\n    publisher = \"Zenodo\",\n    doi = \"10.5281/zenodo.19373346\",\n    url = \"https://spectre-code.org\",\n    howpublished =\n\"\\href{https://doi.org/10.5281/zenodo.19373346}{10.5281/zenodo.19373346}\",\n    license = \"MIT\",\n    year = \"2026\",\n    month = \"4\"\n}\n"
  },
  {
    "path": "cmake/AddCxxFlag.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Have to compile a file (can be empty) to check for flags.\n# Kokkos' nvcc_wrapper doesn't support taking the file as stdin, so we\n# have to write it to a file.\nset(_CHECK_CXX_FLAGS_SOURCE \"${CMAKE_BINARY_DIR}/CMakeFiles/CheckCxxFlags.cpp\")\nwrite_file(${_CHECK_CXX_FLAGS_SOURCE} \"\")\n\n# Checks if a flag is supported by the compiler and creates the target\n# TARGET_NAME whose INTERFACE_COMPILE_OPTIONS are set to the FLAG_TO_CHECK\n# - LANGUAGE: language to check, setting the compiler and generated property\n# - XTYPE: language as passed to the -x compiler flag\n# - FLAG_TO_CHECK: the CXX flag to add if the compiler supports it\n# - TARGET_NAME: the name of the target whose INTERFACE_COMPILE_OPTIONS are\n#                set\nfunction(create_compile_flag_target LANGUAGE XTYPE FLAG_TO_CHECK TARGET_NAME)\n  # In order to check for a -Wno-* flag in gcc, you have to check the\n  # -W* version instead.  See http://gcc.gnu.org/wiki/FAQ#wnowarning\n  string(REGEX REPLACE ^-Wno- -W POSITIVE_FLAG_TO_CHECK ${FLAG_TO_CHECK})\n  # Escape quotes for compiler command\n  string(REPLACE \"\\\"\" \"\\\\\\\"\" POSITIVE_FLAG_TO_CHECK ${POSITIVE_FLAG_TO_CHECK})\n  execute_process(\n    COMMAND\n    bash -c\n    \"LC_ALL=POSIX ${CMAKE_${LANGUAGE}_COMPILER} -Werror \\\n${POSITIVE_FLAG_TO_CHECK} -x ${XTYPE} \\\n-c ${_CHECK_CXX_FLAGS_SOURCE} -o /dev/null\"\n    RESULT_VARIABLE RESULT\n    ERROR_VARIABLE ERROR_FROM_COMPILATION\n    OUTPUT_QUIET)\n  if(NOT TARGET ${TARGET_NAME})\n    add_library(${TARGET_NAME} INTERFACE)\n  endif(NOT TARGET ${TARGET_NAME})\n  if(${RESULT} EQUAL 0)\n    string(REPLACE \" \" \";\" FLAG_TO_CHECK ${FLAG_TO_CHECK})\n    set_property(TARGET ${TARGET_NAME}\n      APPEND PROPERTY\n      INTERFACE_COMPILE_OPTIONS\n      $<$<COMPILE_LANGUAGE:${LANGUAGE}>:${FLAG_TO_CHECK}>)\n  endif(${RESULT} EQUAL 0)\nendfunction()\n\n# Checks if a CXX flag is supported by the compiler and creates the target\n# TARGET_NAME whose INTERFACE_COMPILE_OPTIONS are set to the FLAG_TO_CHECK\n# - FLAG_TO_CHECK: the CXX flag to add if the compiler supports it\n# - TARGET_NAME: the name of the target whose INTERFACE_COMPILE_OPTIONS are\n#                set\nfunction(create_cxx_flag_target FLAG_TO_CHECK TARGET_NAME)\n  create_compile_flag_target(CXX c++ \"${FLAG_TO_CHECK}\" \"${TARGET_NAME}\")\nendfunction()\n\n# Same, but for C.\nfunction(create_c_flag_target FLAG_TO_CHECK TARGET_NAME)\n  create_compile_flag_target(C c \"${FLAG_TO_CHECK}\" \"${TARGET_NAME}\")\nendfunction()\n\n# Checks which of the CXX FLAGS_TO_CHECK are supported by the compiler\n# and creates the target TARGET_NAME whose INTERFACE_COMPILE_OPTIONS\n# are set to the FLAGS_TO_CHECK that are supported. If adding many flags,\n# this will be much faster than calling create_cxx_flags_target multiple times.\n# - LANGUAGE: language to check, setting the compiler and generated property\n# - XTYPE: language as passed to the -x compiler flag\n# - FLAGS_TO_CHECK: a semicolon separated string of CXX flags to try to add\n#                   for compilation.\n# - TARGET_NAME: the name of the target whose INTERFACE_COMPILE_OPTIONS are\n#                set\nfunction(create_compile_flags_target LANGUAGE XTYPE FLAGS_TO_CHECK TARGET_NAME)\n  # In order to check for a -Wno-* flag in gcc, you have to check the\n  # -W* version instead.  See http://gcc.gnu.org/wiki/FAQ#wnowarning\n  set(POSITIVE_FLAGS_TO_CHECK)\n  foreach(FLAG_TO_CHECK ${FLAGS_TO_CHECK})\n    string(REGEX REPLACE ^-Wno- -W POSITIVE_FLAG_TO_CHECK ${FLAG_TO_CHECK})\n    list(APPEND POSITIVE_FLAGS_TO_CHECK ${POSITIVE_FLAG_TO_CHECK})\n  endforeach()\n  string(REPLACE \";\" \" \"\n    POSITIVE_FLAGS_WITH_SPACES \"${POSITIVE_FLAGS_TO_CHECK}\")\n  execute_process(\n    COMMAND\n    bash -c\n    \"LC_ALL=POSIX ${CMAKE_${LANGUAGE}_COMPILER} -Werror \\\n${POSITIVE_FLAGS_WITH_SPACES} -x ${XTYPE} \\\n-c ${_CHECK_CXX_FLAGS_SOURCE} -o /dev/null\"\n    RESULT_VARIABLE RESULT\n    ERROR_VARIABLE ERROR_FROM_COMPILATION\n    OUTPUT_QUIET)\n\n  if(NOT TARGET ${TARGET_NAME})\n    add_library(${TARGET_NAME} INTERFACE)\n  endif(NOT TARGET ${TARGET_NAME})\n  if(${RESULT} EQUAL 0)\n    set_property(TARGET ${TARGET_NAME}\n      APPEND PROPERTY\n      INTERFACE_COMPILE_OPTIONS\n      $<$<COMPILE_LANGUAGE:${LANGUAGE}>:${FLAGS_TO_CHECK}>)\n  else(${RESULT} EQUAL 0)\n    # Check each flag to see if it was marked as \"invalid\" in the output\n    unset(FLAGS_TO_ADD)\n    foreach(FLAG ${POSITIVE_FLAGS_TO_CHECK})\n      string(FIND \"${ERROR_FROM_COMPILATION}\" \"'${FLAG}'\" FOUND_POS)\n      if(${FOUND_POS} EQUAL -1)\n        # For some reason:\n        # list(FIND ${POSITIVE_FLAGS_TO_CHECK} ${FLAG} INDEX_OF_FLAG)\n        # doesn't work with some compilers. This makes no sense but such is\n        # life. As a work around we basically implement a find manually.\n\n        # Find the index of the current flag in the POSITIVE_FLAGS_TO_CHECK\n        # list. This is the index we use to get the original flag in the\n        # FLAGS_TO_CHECK list.\n        set(INDEX 0)\n        foreach(POS_FLAG ${POSITIVE_FLAGS_TO_CHECK})\n          if(\"${POS_FLAG}\" STREQUAL \"${FLAG}\")\n            break()\n          endif()\n          MATH(EXPR INDEX \"${INDEX}+1\")\n        endforeach()\n        set(TARGET_INDEX ${INDEX})\n        set(INDEX 0)\n        # Get original flag\n        set(NEW_FLAG \"\")\n        foreach(ORIGINAL_FLAG ${FLAGS_TO_CHECK})\n          if(${INDEX} EQUAL ${TARGET_INDEX})\n            set(NEW_FLAG ${ORIGINAL_FLAG})\n            break()\n          endif()\n          MATH(EXPR INDEX \"${INDEX}+1\")\n        endforeach()\n        # Add the flag to the list of flags to add.\n        set(FLAGS_TO_ADD \"${FLAGS_TO_ADD};${NEW_FLAG}\")\n      endif(${FOUND_POS} EQUAL -1)\n    endforeach(FLAG ${FLAGS_TO_CHECK})\n    set_property(TARGET ${TARGET_NAME}\n      APPEND PROPERTY\n      INTERFACE_COMPILE_OPTIONS\n      $<$<COMPILE_LANGUAGE:${LANGUAGE}>:${FLAGS_TO_ADD}>)\n\n  endif(${RESULT} EQUAL 0)\nendfunction()\n\n# Checks which of the CXX FLAGS_TO_CHECK are supported by the compiler\n# and creates the target TARGET_NAME whose INTERFACE_COMPILE_OPTIONS\n# are set to the FLAGS_TO_CHECK that are supported. If adding many flags,\n# this will be much faster than calling create_cxx_flags_target multiple times.\n# - FLAGS_TO_CHECK: a semicolon separated string of CXX flags to try to add\n#                   for compilation.\n# - TARGET_NAME: the name of the target whose INTERFACE_COMPILE_OPTIONS are\n#                set\nfunction(create_cxx_flags_target FLAGS_TO_CHECK TARGET_NAME)\n  create_compile_flags_target(CXX c++ \"${FLAGS_TO_CHECK}\" \"${TARGET_NAME}\")\nendfunction()\n\n# Same, but for C.\nfunction(create_c_flags_target FLAGS_TO_CHECK TARGET_NAME)\n  create_compile_flags_target(C c \"${FLAGS_TO_CHECK}\" \"${TARGET_NAME}\")\nendfunction()\n\nset(CMAKE_SUPPORTS_LINK_OPTIONS OFF)\nif(CMAKE_VERSION VERSION_EQUAL 3.13 OR CMAKE_VERSION VERSION_GREATER 3.13)\n  set(CMAKE_SUPPORTS_LINK_OPTIONS ON)\nendif(CMAKE_VERSION VERSION_EQUAL 3.13 OR CMAKE_VERSION VERSION_GREATER 3.13)\n\nif(CMAKE_SUPPORTS_LINK_OPTIONS)\n  # Creates a target named TARGET_NAME that, if the linker flag FLAG_TO_CHECK\n  # is supported, defines ${FLAG_TO_CHECK} as an INTERFACE_LINK_OPTION\n  function(create_cxx_link_flag_target FLAG_TO_CHECK TARGET_NAME)\n    include(CheckLinkerFlag)\n    unset(CXX_LINKER_FLAG_WORKS CACHE)\n    set(CMAKE_REQUIRED_QUIET 1)\n    check_linker_flag(CXX ${FLAG_TO_CHECK} CXX_LINKER_FLAG_WORKS)\n    unset(CMAKE_REQUIRED_QUIET)\n\n    add_library(${TARGET_NAME} INTERFACE)\n    if(CXX_LINKER_FLAG_WORKS)\n      set_property(TARGET ${TARGET_NAME}\n        APPEND PROPERTY\n        INTERFACE_LINK_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:${FLAG_TO_CHECK}>)\n    endif()\n  endfunction()\nendif(CMAKE_SUPPORTS_LINK_OPTIONS)\n\n# Checks if a flag is supported by the linker and adds it if it is\nfunction(check_and_add_cxx_link_flag FLAG_TO_CHECK)\n  include(CheckLinkerFlag)\n  unset(CXX_LINKER_FLAG_WORKS CACHE)\n  set(CMAKE_REQUIRED_QUIET 1)\n  check_linker_flag(CXX ${FLAG_TO_CHECK} CXX_LINKER_FLAG_WORKS)\n  unset(CMAKE_REQUIRED_QUIET)\n  if(CXX_LINKER_FLAG_WORKS)\n    set(CMAKE_CXX_LINK_FLAGS\n      \"${CMAKE_CXX_LINK_FLAGS} ${FLAG_TO_CHECK}\" PARENT_SCOPE)\n  endif()\nendfunction()\n"
  },
  {
    "path": "cmake/AddInputFileTests.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_define_test_timeout_factor_option(INPUT_FILE \"input file\")\n\noption(SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY \"Minimum priority of input file \\\ntests to run. Possible values are: low (not usually run on CI), normal \\\n(run at least once on CI), high (run always on CI).\" \"normal\")\n\n# Convert priority string to a number.\nif (NOT SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY)\n  set(SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY \"normal\")\nendif()\nstring(TOLOWER \"${SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY}\"\n  SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY)\nif (${SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY} STREQUAL \"high\")\n  set(SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY 1)\nelseif (${SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY} STREQUAL \"normal\")\n  set(SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY 0)\nelseif (${SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY} STREQUAL \"low\")\n  set(SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY -1)\nelse()\n  message(FATAL_ERROR \"Unknown priority in option \"\n    \"SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY: \"\n    \"${SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY}. \"\n    \"Possible values are: high, normal, low\")\nendif()\n\n# Environment variables for test\nset(_INPUT_FILE_TEST_ENV_VARS \"\")\n# - Disable ASAN's leak sanitizer because Charm++ has false positives\nlist(APPEND _INPUT_FILE_TEST_ENV_VARS \"ASAN_OPTIONS=detect_leaks=0\")\n# - Set PYTHONPATH to find Python modules\nlist(APPEND _INPUT_FILE_TEST_ENV_VARS \"PYTHONPATH=${PYTHONPATH}\")\n\nfunction(add_single_input_file_test)\n  cmake_parse_arguments(\n    ARG \"\"\n    \"INPUT_FILE;EXECUTABLE;CHECK_TYPE;TIMEOUT\"\n    \"COMMAND_LINE_ARGS;EXPECTED_EXIT_CODE;COPY_FILES\"\n    ${ARGN})\n  # Extract just the name of the input file\n  get_filename_component(INPUT_FILE_NAME \"${INPUT_FILE}\" NAME)\n  get_filename_component(INPUT_FILE_DIR \"${INPUT_FILE}\" DIRECTORY)\n\n  # Extract the main subdirectory name\n  string(FIND \"${ARG_INPUT_FILE}\" \"tests/InputFiles/\"\n    POSITION_OF_INPUT_FILE_DIR)\n  math(EXPR\n    POSITION_OF_INPUT_FILE_DIR\n    ${POSITION_OF_INPUT_FILE_DIR}+17\n    # 17 is the length of \"tests/InputFiles/\"\n    )\n  string(SUBSTRING \"${ARG_INPUT_FILE}\" ${POSITION_OF_INPUT_FILE_DIR}\n    -1 TEMP)\n  string(FIND \"${TEMP}\" \"/\" POSITION_OF_SLASH)\n  string(SUBSTRING \"${TEMP}\" 0 ${POSITION_OF_SLASH}\n      EXECUTABLE_DIR_NAME)\n\n  # Set tags for the test\n  set(TAGS \"InputFiles;${EXECUTABLE_DIR_NAME};${ARG_CHECK_TYPE}\")\n  string(TOLOWER \"${TAGS}\" TAGS)\n  # Add the executable name as label, without converting to lower case. This\n  # allows running all input file tests for a particular executable.\n  list(APPEND TAGS ${ARG_EXECUTABLE})\n\n  set(\n    CTEST_NAME\n    \"InputFiles.${EXECUTABLE_DIR_NAME}.${INPUT_FILE_NAME}.${ARG_CHECK_TYPE}\"\n    )\n  set(\n    RUN_DIRECTORY\n    \"${EXECUTABLE_DIR_NAME}.${INPUT_FILE_NAME}.${ARG_CHECK_TYPE}\"\n    )\n  if(\"${ARG_CHECK_TYPE}\" STREQUAL \"execute_check_output\")\n    set(_CHECK_OUTPUT_FILES \"true\")\n  else()\n    set(_CHECK_OUTPUT_FILES \"false\")\n  endif()\n\n  if (\"${ARG_CHECK_TYPE}\" STREQUAL \"parse\")\n    add_test(\n      NAME ${CTEST_NAME}\n      # This script is written below, and only once\n      COMMAND sh\n      ${PROJECT_BINARY_DIR}/tmp/RunInputFileTest.sh ${ARG_EXECUTABLE}\n      ${ARG_INPUT_FILE} ${RUN_DIRECTORY} 0 false false\n      \"${ARG_COMMAND_LINE_ARGS} --check-options\"\n      \"${ARG_COPY_FILES}\"\n      )\n  elseif(\"${ARG_CHECK_TYPE}\" STREQUAL \"execute\" OR\n         \"${ARG_CHECK_TYPE}\" STREQUAL \"execute_check_output\")\n    add_test(\n      NAME ${CTEST_NAME}\n      # This script is written below, and only once\n      COMMAND sh ${PROJECT_BINARY_DIR}/tmp/RunInputFileTest.sh\n      ${ARG_EXECUTABLE} ${ARG_INPUT_FILE} ${RUN_DIRECTORY}\n      ${ARG_EXPECTED_EXIT_CODE} ${_CHECK_OUTPUT_FILES} true\n      \"${ARG_COMMAND_LINE_ARGS}\"\n      \"${ARG_COPY_FILES}\"\n      )\n  else()\n    message(FATAL_ERROR \"Unknown check for input file: ${ARG_CHECK_TYPE}.\"\n      \"Known checks are: execute\")\n  endif()\n\n  # Increase timeout if address sanitizer is enabled.\n  if (ASAN)\n    math(EXPR TIMEOUT \"4 * ${ARG_TIMEOUT}\")\n  else()\n    set(TIMEOUT ${ARG_TIMEOUT})\n  endif()\n\n  spectre_test_timeout(TIMEOUT ARG_INPUT_FILE ${TIMEOUT})\n\n  set_tests_properties(\n    ${CTEST_NAME}\n    PROPERTIES\n    TIMEOUT ${TIMEOUT}\n    LABELS \"${TAGS}\"\n    ENVIRONMENT \"${_INPUT_FILE_TEST_ENV_VARS}\")\nendfunction()\n\n# Searches the directory INPUT_FILE_DIR for .yaml files and adds a test for each\n# one. See `WritingTests.md` for details on controlling input file tests. Add\n# input files to the whitelist at the bottom of this file to ignore those tests\nfunction(add_input_file_tests INPUT_FILE_DIR INPUT_FILE_WHITELIST)\n  set(INPUT_FILE_LIST \"\")\n  file(GLOB_RECURSE INPUT_FILE_LIST ${INPUT_FILE_DIR} \"${INPUT_FILE_DIR}*.yaml\")\n  set(TIMEOUT 2)\n  list(TRANSFORM INPUT_FILE_WHITELIST PREPEND ${INPUT_FILE_DIR})\n\n  foreach(INPUT_FILE ${INPUT_FILE_LIST})\n    # Only parse the input file if we are allowed to\n    if (${INPUT_FILE} IN_LIST INPUT_FILE_WHITELIST)\n      continue()\n    endif()\n    if (INPUT_FILE MATCHES \"\\\\.overlay_[0-9]*\\\\.yaml$\")\n      continue()\n    endif()\n\n    file(READ ${INPUT_FILE} INPUT_FILE_CONTENTS)\n\n    # Read the priority of the test specified in input file, empty is accepted.\n    string(REGEX MATCH \"Priority:[^\\n]+\"\n      INPUT_FILE_PRIORITY \"${INPUT_FILE_CONTENTS}\")\n    if(\"${INPUT_FILE_PRIORITY}\" STREQUAL \"\")\n      set(INPUT_FILE_PRIORITY \"normal\")\n    else()\n      string(REGEX REPLACE \"Priority:[ ]*\" \"\"\n        INPUT_FILE_PRIORITY \"${INPUT_FILE_PRIORITY}\")\n      string(STRIP \"${INPUT_FILE_PRIORITY}\" INPUT_FILE_PRIORITY)\n    endif()\n    # Translate strings to numbers\n    string(TOLOWER \"${INPUT_FILE_PRIORITY}\" INPUT_FILE_PRIORITY)\n    if (${INPUT_FILE_PRIORITY} STREQUAL \"high\")\n      set(INPUT_FILE_PRIORITY 1)\n    elseif (${INPUT_FILE_PRIORITY} STREQUAL \"normal\")\n      set(INPUT_FILE_PRIORITY 0)\n    elseif (${INPUT_FILE_PRIORITY} STREQUAL \"low\")\n      set(INPUT_FILE_PRIORITY -1)\n    else()\n      message(FATAL_ERROR \"Unknown priority in input file ${INPUT_FILE}: \"\n        \"${INPUT_FILE_PRIORITY}. Possible values are: high, normal, low\")\n    endif()\n\n    # Only add tests with at least the priority specified in\n    # `SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY`\n    if (${INPUT_FILE_PRIORITY} LESS\n        ${SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY})\n      continue()\n    endif()\n\n    # Check if the executable name is present\n    string(REGEX MATCH \"Executable:[^\\n]+\"\n      INPUT_FILE_EXECUTABLE \"${INPUT_FILE_CONTENTS}\")\n    if(\"${INPUT_FILE_EXECUTABLE}\" STREQUAL \"\")\n      message(FATAL_ERROR \"Could not find the executable in input \"\n        \"file ${INPUT_FILE}. You must supply a line of the form:\"\n        \"'Executable: EXECUTABLE_NAME'\")\n    endif()\n    # Extract executable name, and remove trailing white space\n    string(REGEX REPLACE \"Executable:[ ]*\" \"\"\n      INPUT_FILE_EXECUTABLE \"${INPUT_FILE_EXECUTABLE}\")\n    string(STRIP \"${INPUT_FILE_EXECUTABLE}\" INPUT_FILE_EXECUTABLE)\n\n    string(REGEX MATCH \"CommandLineArgs:[^\\n]+\"\n      COMMAND_LINE_ARGS \"${INPUT_FILE_CONTENTS}\")\n    string(REGEX REPLACE \"CommandLineArgs:[ ]*\" \"\"\n      COMMAND_LINE_ARGS \"${COMMAND_LINE_ARGS}\")\n    string(STRIP \"${COMMAND_LINE_ARGS}\" COMMAND_LINE_ARGS)\n\n    string(REGEX MATCH \"ExpectedExitCode:[^\\n]+\"\n      EXPECTED_EXIT_CODE \"${INPUT_FILE_CONTENTS}\")\n    string(REGEX REPLACE \"ExpectedExitCode:[ ]*\" \"\"\n      EXPECTED_EXIT_CODE \"${EXPECTED_EXIT_CODE}\")\n    string(STRIP \"${EXPECTED_EXIT_CODE}\" EXPECTED_EXIT_CODE)\n    if(\"${EXPECTED_EXIT_CODE}\" STREQUAL \"\")\n      set(EXPECTED_EXIT_CODE \"0\")\n    endif()\n\n    # Read what tests to do. Currently \"execute\" and \"parse\" are available.\n    string(REGEX MATCH \"Check:[^\\n]+\"\n      INPUT_FILE_CHECKS \"${INPUT_FILE_CONTENTS}\")\n    # Extract list of checks to perform\n    string(REGEX REPLACE \"Check:[ ]*\" \"\"\n      INPUT_FILE_CHECKS \"${INPUT_FILE_CHECKS}\")\n    string(STRIP \"${INPUT_FILE_CHECKS}\" INPUT_FILE_CHECKS)\n    set(INPUT_FILE_CHECKS \"${INPUT_FILE_CHECKS}\")\n    list(REMOVE_DUPLICATES \"INPUT_FILE_CHECKS\")\n    # Convert all the checks to lower case to make life easier.\n    string(TOLOWER \"${INPUT_FILE_CHECKS}\" INPUT_FILE_CHECKS)\n\n    # Make sure that the 'parse' check is listed. If not, print an\n    # error message that explains that it's needed and why.\n    list(FIND \"INPUT_FILE_CHECKS\" \"parse\" FOUND_PARSE)\n    if (${FOUND_PARSE} EQUAL -1)\n      message(FATAL_ERROR\n        \"The input file: \"\n        \"'${INPUT_FILE}' \"\n        \"does not specify the 'parse' check. All input file tests must\"\n        \" specify the 'parse' check which runs the executable passing\"\n        \" the '--check-options' flag. With this flag the executable\"\n        \" should check that the input file parses correctly and that\"\n        \" the values specified in the input file do not violate any\"\n        \" bounds or sanity checks.\")\n    endif (${FOUND_PARSE} EQUAL -1)\n\n    # Read the timeout duration specified in input file, empty is accepted.\n    # The default duration is 2 seconds.\n    string(REGEX MATCH \"Timeout:[^\\n]+\"\n      INPUT_FILE_TIMEOUT \"${INPUT_FILE_CONTENTS}\")\n    if(\"${INPUT_FILE_TIMEOUT}\" STREQUAL \"\")\n      set(INPUT_FILE_TIMEOUT \"${TIMEOUT}\")\n    else()\n      string(REGEX REPLACE \"Timeout:[ ]*\" \"\"\n        INPUT_FILE_TIMEOUT \"${INPUT_FILE_TIMEOUT}\")\n      string(STRIP \"${INPUT_FILE_TIMEOUT}\" INPUT_FILE_TIMEOUT)\n    endif()\n\n    # Read out the semicolon list of files to copy to the run directory.\n    # The files are currently restricted to being in the same location\n    # as the input file. This can be relaxed if necessary.\n    set(COPY_FILES \"\")\n    string(REGEX MATCH \"CopyFiles:[^\\n]+\"\n      COPY_FILES \"${INPUT_FILE_CONTENTS}\")\n    if(NOT \"${COPY_FILES}\" STREQUAL \"\")\n      string(REPLACE \"CopyFiles: \" \"\" COPY_FILES ${COPY_FILES})\n      string(REPLACE \"CopyFiles:\" \"\" COPY_FILES ${COPY_FILES})\n    endif()\n\n    foreach(CHECK_TYPE ${INPUT_FILE_CHECKS})\n      add_single_input_file_test(\n        INPUT_FILE ${INPUT_FILE}\n        EXECUTABLE ${INPUT_FILE_EXECUTABLE}\n        COMMAND_LINE_ARGS \"${COMMAND_LINE_ARGS}\"\n        CHECK_TYPE ${CHECK_TYPE}\n        TIMEOUT ${INPUT_FILE_TIMEOUT}\n        EXPECTED_EXIT_CODE ${EXPECTED_EXIT_CODE}\n        COPY_FILES \"${COPY_FILES}\"\n        )\n    endforeach()\n    add_dependencies(test-executables ${INPUT_FILE_EXECUTABLE})\n  endforeach()\nendfunction()\n\n# Dependencies will be added as the tests are processed.\nadd_custom_target(test-executables)\n\n# Write command to execute an input file and clean its output into a shell\n# script, which makes it easier to chain multiple commands\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/cmake/RunInputFileTest.sh\n  ${PROJECT_BINARY_DIR}/tmp/RunInputFileTest.sh\n  @ONLY)\n\n# These paths should be relative to the input file directory passed to\n# `add_input_file_tests`\nset(INPUT_FILE_WHITELIST\n    \"PreprocessCceWorldtube/PreprocessCceWorldtube.yaml\"\n    \"PreprocessCceWorldtube/AdmFirstOrderDriverPreprocessCceWorldtube.yaml\"\n    \"PreprocessCceWorldtube/AdmSecondOrderDriverPreprocessCceWorldtube.yaml\")\n\nadd_input_file_tests(\"${CMAKE_SOURCE_DIR}/tests/InputFiles/\" \"${INPUT_FILE_WHITELIST}\")\n"
  },
  {
    "path": "cmake/AddSpectreExecutable.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Adds an executable by wrapping CMake's 'add_executable' but allows\n# us to inject dependencies, flags, etc. into the targets.\nfunction(add_spectre_executable TARGET_NAME)\n  add_executable(${TARGET_NAME} ${ARGN})\n  # We need to link custom allocators before we link anything else so that\n  # any third-party libraries, which generally should all be built as shared\n  # libraries, use the allocator that we use. Unfortunately, how exactly\n  # CMake decides on the linking order is not clear when using\n  # INTERFACE_LINK_LIBRARIES and targets. To this end, we set a global\n  # property SPECTRE_ALLOCATOR_LIBRARY that contains the link flag to link\n  # to the memory allocator. By linking to the allocator library first\n  # explicitly in target_link_libraries CMake correctly places the allocator\n  # library as the first entry in the link libraries. We also link to the\n  # SpectreAllocator target to pull in any additional allocator-related\n  # flags, such as include directories.\n  get_property(\n    SPECTRE_ALLOCATOR_LIBRARY\n    GLOBAL\n    PROPERTY SPECTRE_ALLOCATOR_LIBRARY\n    )\n  target_link_libraries(\n    ${TARGET_NAME}\n    PUBLIC\n    ${SPECTRE_ALLOCATOR_LIBRARY}\n    SpectreAllocator\n  )\n\n  set(SPECTRE_KOKKOS_LAUNCHER \"\")\n  if(SPECTRE_KOKKOS)\n    # We need to make sure we don't drop the Kokkos link wrapper\n    get_target_property(\n      _RULE_LAUNCH_LINK\n      ${TARGET_NAME}\n      RULE_LAUNCH_LINK)\n    if (_RULE_LAUNCH_LINK)\n      set(SPECTRE_KOKKOS_LAUNCHER ${_RULE_LAUNCH_LINK})\n    endif()\n  endif()\n  set_target_properties(\n    ${TARGET_NAME}\n    PROPERTIES\n    RULE_LAUNCH_LINK\n    \"${CMAKE_BINARY_DIR}/tmp/WrapExecutableLinker.sh ${SPECTRE_KOKKOS_LAUNCHER}\"\n    LINK_DEPENDS \"${CMAKE_BINARY_DIR}/tmp/WrapExecutableLinker.sh\"\n    # Expose readable symbol names in backtrace (adds flags like -rdynamic)\n    ENABLE_EXPORTS ON\n    )\n  target_link_options(${TARGET_NAME} PRIVATE \"-DEXECUTABLE_NAME=${TARGET_NAME}\")\n  # The `WrapExecutableLinker.sh` script needs the `InfoAtLink_flags.txt` file\n  # generated by the `InfoAtLink` target\n  add_dependencies(\n    ${TARGET_NAME}\n    InfoAtLink\n    )\n  target_link_libraries(\n    ${TARGET_NAME}\n    PRIVATE\n    CharmModuleInit\n    SpectreFlags\n    )\n  install(TARGETS ${TARGET_NAME} OPTIONAL\n    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})\nendfunction()\n"
  },
  {
    "path": "cmake/AddStandaloneTests.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_define_test_timeout_factor_option(STANDALONE \"standalone\")\n\n# Helper function to set up a CMake target for a test executable.  It\n# can safely be called multiple times for the same executable.\nfunction(add_standalone_test_executable EXECUTABLE_NAME)\n  add_dependencies(test-executables ${EXECUTABLE_NAME})\n\n  if (TARGET ${EXECUTABLE_NAME})\n    return()\n  endif()\n\n  add_spectre_executable(\n    ${EXECUTABLE_NAME}\n    ${EXECUTABLE_NAME}.cpp\n    )\n\n  target_link_libraries(\n    ${EXECUTABLE_NAME}\n    PRIVATE\n    Catch2::Catch2\n    )\n\n  add_dependencies(\n    ${EXECUTABLE_NAME}\n    module_GlobalCache\n    module_Main\n    )\nendfunction()\n\n# Helper function to set standard test properties for standalone tests.\nfunction(set_standalone_test_properties TEST_NAME)\n  spectre_test_timeout(TIMEOUT STANDALONE 10)\n\n  set_tests_properties(\n    \"${TEST_NAME}\"\n    PROPERTIES\n    TIMEOUT \"${TIMEOUT}\"\n    LABELS \"standalone\"\n    ENVIRONMENT \"ASAN_OPTIONS=detect_leaks=0\")\nendfunction()\n\n# For tests that result in a failure it is necessary to redirect\n# output from stderr to stdout. However, it was necessary at least on\n# some systems to do this redirect inside a shell command.\nfind_program(SHELL_EXECUTABLE \"sh\")\nif (NOT SHELL_EXECUTABLE)\n  message(FATAL_ERROR\n    \"Could not find 'sh' shell to execute standalone failure tests\")\nendif()\n\n# Add a standalone test named TEST_NAME that runs an executable with\n# no arguments.  A test named Foo.Bar.Baz will run the executable\n# Test_Baz by default.\n#\n# A REGEX_TO_MATCH named argument may be passed, in which case the\n# test will pass if the output matches it, otherwise the test will\n# pass if the executable succeeds without any \"ERROR\" output.\n#\n# An EXECUTABLE named argument can be passed to override the\n# executable name.\n#\n# An INPUT_FILE named argument can be passed to pass an input file to\n# the executable.\nfunction(add_standalone_test TEST_NAME)\n  cmake_parse_arguments(\n    ARG\n    \"\"\n    \"REGEX_TO_MATCH;EXECUTABLE;INPUT_FILE\"\n    \"\"\n    ${ARGN})\n\n  if(DEFINED ARG_EXECUTABLE)\n    set(EXECUTABLE_NAME \"${ARG_EXECUTABLE}\")\n  else()\n    # Extract last component of test name as executable name\n    string(REGEX MATCH \"[^.]*$\" EXECUTABLE_NAME \"${TEST_NAME}\")\n    set(EXECUTABLE_NAME \"Test_${EXECUTABLE_NAME}\")\n  endif()\n\n  add_standalone_test_executable(\"${EXECUTABLE_NAME}\")\n  if(DEFINED ARG_INPUT_FILE)\n    set(INPUT_FILE_ARGS\n      \"--input-file ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_INPUT_FILE}\")\n  else()\n    set(INPUT_FILE_ARGS \"\")\n  endif()\n  add_test(\n    NAME \"${TEST_NAME}\"\n    COMMAND\n    ${SHELL_EXECUTABLE}\n    -c\n    \"${SPECTRE_TEST_RUNNER} ${CMAKE_BINARY_DIR}/bin/${EXECUTABLE_NAME} ${INPUT_FILE_ARGS} 2>&1\"\n    )\n\n  set_standalone_test_properties(\"${TEST_NAME}\")\n  if(NOT DEFINED ARG_REGEX_TO_MATCH)\n    set_tests_properties(\n      \"${TEST_NAME}\"\n      PROPERTIES\n      FAIL_REGULAR_EXPRESSION \"ERROR\")\n  else()\n    set_tests_properties(\n      \"${TEST_NAME}\"\n      PROPERTIES\n      PASS_REGULAR_EXPRESSION\n      \"${ARG_REGEX_TO_MATCH}\")\n  endif()\nendfunction()\n"
  },
  {
    "path": "cmake/BootstrapPyDeps.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(BOOTSTRAP_PY_DEPS\n  \"Install missing Python dependencies in the build directory\"\n  ${SPECTRE_FETCH_MISSING_DEPS})\noption(BOOTSTRAP_PY_DEV_DEPS\n  \"Install missing Python dev dependencies in the build directory\"\n  ${SPECTRE_FETCH_MISSING_DEPS})\n\nif (NOT (BOOTSTRAP_PY_DEPS OR BOOTSTRAP_PY_DEV_DEPS))\n  return()\nendif()\n\nmessage(STATUS \"Bootstrapping missing Python dependencies to: \\\n${SPECTRE_PYTHON_SITE_PACKAGES}\")\n\n# Install the packages with pip\nset(_BOOTSTRAP_PY_DEPS_FLAG \"\")\nif (BOOTSTRAP_PY_DEPS)\n  set(_BOOTSTRAP_PY_DEPS_FLAG\n    -r ${CMAKE_SOURCE_DIR}/support/Python/requirements.txt)\nendif()\nset(_BOOTSTRAP_PY_DEV_DEPS_FLAG \"\")\nif (BOOTSTRAP_PY_DEV_DEPS)\n  set(_BOOTSTRAP_PY_DEV_DEPS_FLAG\n    -r ${CMAKE_SOURCE_DIR}/support/Python/dev_requirements.txt)\nendif()\nset(_BOOTSTRAP_PY_DEPS_LOG_FILE \"${CMAKE_BINARY_DIR}/BootstrapPyDeps.log\")\nexecute_process(COMMAND\n  ${CMAKE_COMMAND} -E env\n  PYTHONPATH=${PYTHONPATH}\n  ${Python_EXECUTABLE} -m pip install --disable-pip-version-check\n  --prefix ${CMAKE_BINARY_DIR} --no-warn-script-location\n  ${_BOOTSTRAP_PY_DEPS_FLAG} ${_BOOTSTRAP_PY_DEV_DEPS_FLAG}\n  RESULT_VARIABLE _BOOTSTRAP_PY_DEPS_RESULT\n  OUTPUT_FILE ${_BOOTSTRAP_PY_DEPS_LOG_FILE}\n  ERROR_FILE ${_BOOTSTRAP_PY_DEPS_LOG_FILE}\n  )\nif (NOT _BOOTSTRAP_PY_DEPS_RESULT EQUAL 0)\n  message(WARNING \"Bootstrapping of Python dependencies failed.\"\n    \"See log file: ${_BOOTSTRAP_PY_DEPS_LOG_FILE}\")\nendif()\n"
  },
  {
    "path": "cmake/CheckBrokenArray0.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Check whether the standard library is affected by\n# https://bugs.llvm.org/show_bug.cgi?id=35491\n# This LLVM bug is fixed by commit 59cdf90ac8bea16abbb9d637c5124e69d2c75c09,\n# which is included in the LLVM 7 release.\n\nmessage(STATUS \"Checking for broken std::array<..., 0>\")\ntry_compile(\n  ARRAY0_WORKS\n  ${CMAKE_BINARY_DIR}\n  ${CMAKE_SOURCE_DIR}/cmake/CheckBrokenArray0.cpp\n  CMAKE_FLAGS\n  -DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}\n  -DCMAKE_CXX_STANDARD_REQUIRED=${CMAKE_CXX_STANDARD_REQUIRED}\n  -DCMAKE_CXX_EXTENSIONS=${CMAKE_CXX_EXTENSIONS}\n  OUTPUT_VARIABLE TRY_COMPILE_OUTPUT\n  )\nif (ARRAY0_WORKS)\n  message(STATUS \"Checking for broken std::array<..., 0> -- works\")\nelse (ARRAY0_WORKS)\n  message(STATUS \"Checking for broken std::array<..., 0> -- broken\")\n  add_definitions(-DHAVE_BROKEN_ARRAY0)\n  message(STATUS \"Output when trying to compile broken array test:\\n${TRY_COMPILE_OUTPUT}\")\nendif (ARRAY0_WORKS)\n"
  },
  {
    "path": "cmake/CheckBrokenArray0.cpp",
    "content": "// Distributed under the MIT License.\n// Copyright (c) 2009-2017 by the contributors to libc++, listed at\n// https://llvm.org/svn/llvm-project/libcxx/trunk/CREDITS.TXT\n\n// This is a portion of\n// https://llvm.org/svn/llvm-project/libcxx/trunk/test/std/containers/sequences/array/begin.pass.cpp\n\n#include <array>\n#include <cassert>\n\nint main() {\n  struct NoDefault {\n    NoDefault(int) {}\n  };\n  typedef NoDefault T;\n  typedef std::array<T, 0> C;\n  C c = {};\n  assert(c.begin() == c.end());\n}\n"
  },
  {
    "path": "cmake/CheckCompilerVersion.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n  if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1)\n    message(FATAL_ERROR \"GCC version must be at least 9.1\")\n  endif ()\nelseif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n  if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0)\n    message(FATAL_ERROR \"Clang version must be at least 13.0\")\n  endif ()\nelseif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Intel\")\n  message(FATAL_ERROR \"Intel compiler is not supported.\")\nelseif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"AppleClang\")\n  if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.0.0)\n    message(FATAL_ERROR \"AppleClang version must be at least 13.0.0\")\n  endif ()\nelse ()\n  message(\n      WARNING \"The compiler ${CMAKE_CXX_COMPILER_ID} is unsupported! \"\n      \"Compilation has only been tested with Clang and GCC.\"\n  )\nendif ()\n"
  },
  {
    "path": "cmake/CodeCoverage.cmake",
    "content": "################################################################################\n#\n# \\file      cmake/CodeCoverage.cmake\n# \\author    J. Bakosi\n# \\copyright 2012-2015, Jozsef Bakosi, 2016, Los Alamos National Security, LLC.\n# \\brief     Setup target for code coverage analysis\n# \\date      Wed 22 Feb 2017 07:39:09 AM MST\n#\n# Modifications:\n# 1) Change \"Quinoa\" to \"SpECTRE\" and \"quinoa\" to \"spectre\"\n# 2) Split lines to make commands more legible\n# 3) Silence CMake warnings on uninitialized variables in\n#    `cmake_parse_arguments`\n################################################################################\n\n# ##############################################################################\n# Function to add code coverage target\n#\n# setup_target_for_coverage( <TEST_SUITE> <OUTPUT_PATH> <TARGET_NAME> <TEST_RUNNER>\n#                            [TESTRUNNER_ARGS ...]\n#                            [DEPENDS dep1 dep2 ... ] )\n#\n# Mandatory arguments:\n# --------------------\n#\n# TEST_SUITE - Test TEST_SUITE name to be displayed in HTML report title.\n#\n# OUTPUT_PATH - Path to prepend to where the report is generated:\n# <OUTPUT_PATH>${TARGET_NAME}/index.html.\n#\n# TARGET_NAME - The name of the code coverage target. The HTML report on code\n# coverage is generated at the OUTPUT_PATH <OUTPUT_PATH>/${TARGET_NAME}/index.html.\n#\n# TEST_RUNNER - Command line of the test runner.\n#\n# Optional arguments:\n# -------------------\n#\n# TESTRUNNER_ARGS arg1 arg2 ... - Optional arguments to test runner. Pass them\n# in list form, e.g.: \"-v;-g;group\" for passing '-v -g group'. Default: \"\".\n#\n# DEPENDS dep1 dep2 ... - Optional dependencies added to test coverage target.\n# Default: \"\". Here all dependencies should be given that should be covered by\n# the test suite the coverage is being setup for, as well as those that are\n# required for successfully building the tests and the test runner.\n#\n# Author: J. Bakosi\n#\n# ##############################################################################\nfunction(SETUP_TARGET_FOR_COVERAGE\n    TEST_SUITE OUTPUT_PATH TARGET_NAME TEST_RUNNER)\n  if (NOT IS_ABSOLUTE ${OUTPUT_PATH})\n    set(OUTPUT_PATH ${CMAKE_BINARY_DIR}/${OUTPUT_PATH})\n  endif()\n  if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git)\n    message(FATAL_ERROR, \"Not running in a git repo so we can't do \"\n      \"code coverage\")\n  endif()\n\n  set(multiValueArgs TESTRUNNER_ARGS DEPENDS IGNORE_COV)\n  cmake_parse_arguments(\n      ARG \"\" \"\" \"${multiValueArgs}\"\n      ${ARGN})\n\n  if(NOT LCOV)\n    MESSAGE(FATAL_ERROR \"lcov not found! Aborting...\")\n  endif()\n\n  if(NOT GENHTML)\n    MESSAGE(FATAL_ERROR \"genhtml not found! Aborting...\")\n  endif()\n\n  if(NOT SED)\n    MESSAGE(FATAL_ERROR \"sed not found! Aborting...\")\n  endif()\n\n  if(NOT GIT_HASH)\n    message(FATAL_ERROR \"Git hash not found! Aborting...\")\n  endif()\n\n  # Set shortcut for output: OUTPUT_PATH/target\n  set(OUTPUT ${OUTPUT_PATH}/${TARGET_NAME})\n  file(MAKE_DIRECTORY ${OUTPUT_PATH})\n\n  # Setup code coverage target\n  add_custom_target(\n      ${TARGET_NAME}\n      # Cleanup any old intermediate data\n      COMMAND ${CMAKE_COMMAND} -E remove ${OUTPUT}.base.info\n      ${OUTPUT}.test.info ${OUTPUT}.total.info\n      ${OUTPUT}.filtered.info\n      # Cleanup lcov\n      COMMAND ${LCOV} --gcov-tool ${GCOV} --directory . --zerocounters\n      # Capture initial state yielding zero coverage baseline\n      COMMAND ${LCOV} --gcov-tool ${GCOV} --capture --initial\n      --directory . --output-file ${OUTPUT}.base.info\n      # Run test TEST_SUITE\n      COMMAND ${TEST_RUNNER} ${ARG_TESTRUNNER_ARGS}\n      # Capture lcov counters\n      COMMAND ${LCOV} --gcov-tool ${GCOV} --capture\n      --rc lcov_branch_coverage=0 --directory .\n      --output-file ${OUTPUT}.test.info\n      # Combine trace files\n      COMMAND ${LCOV} --gcov-tool ${GCOV} --rc lcov_branch_coverage=0\n      --add-tracefile ${OUTPUT}.base.info\n      --add-tracefile ${OUTPUT}.test.info\n      --output-file ${OUTPUT}.total.info\n      # Filter out unwanted files\n      COMMAND ${LCOV} --gcov-tool ${GCOV} --rc lcov_branch_coverage=0\n      --remove ${OUTPUT}.total.info '*/c++/*' '*/include/*'\n      '*/boost/*' '*/charm/*' '*.decl.h' '*.def.h'\n      '*/STDIN' '*/tut/*' '*/moduleinit*' '*InfoFromBuild.cpp'\n      '${CMAKE_SOURCE_DIR}/src/Executables/*'\n      ${ARG_IGNORE_COV}\n      --output-file ${OUTPUT}.filtered.info\n      # Generate HTML report\n      COMMAND ${GENHTML} --legend --demangle-cpp\n      --title ${GIT_HASH}\n      -o ${OUTPUT} ${OUTPUT}.filtered.info\n      # Customize page headers in generated html to own\n      COMMAND find ${OUTPUT} -type f -print | xargs file | grep text |\n      cut -f1 -d: | xargs ${SED} -i'.bak' 's/LCOV - code coverage\n      report/SpECTRE ${TEST_SUITE} Test Code Coverage Report/g'\n      COMMAND find ${OUTPUT} -type f -name \\\"*.bak\\\" -print | xargs file |\n      grep text | cut -f1 -d: | xargs rm\n      COMMAND find ${OUTPUT} -type f -print | xargs file | grep text |\n      cut -f1 -d: | xargs ${SED} -i'.bak'\n      's^<td class=\"headerItem\">Test:</td>^<td class=\"headerItem\">Commit:</td>^g'\n      COMMAND find ${OUTPUT} -type f -name \\\"*.bak\\\" -print | xargs file |\n      grep text | cut -f1 -d: | xargs rm\n      COMMAND find ${OUTPUT} -type f -print | xargs file | grep text |\n      cut -f1 -d:\n      | xargs ${SED} -i'.bak' 's^<td class=\"headerValue\">\\\\\\([a-z0-9]\\\\{40\\\\}\\\\\\)^\n      <td class=\"headerValue\"><a target=\"_blank\"\n      href=\"https://github.com/sxs-collaboration/spectre/commit/\\\\1\">\\\\1</a>^g'\n      # Delete backup files created by sed\n      COMMAND find ${OUTPUT} -type f -name \\\"*.bak\\\" -print | xargs file |\n      grep text | cut -f1 -d: | xargs rm\n      # Cleanup any intermediate data\n      COMMAND ${CMAKE_COMMAND} -E remove ${OUTPUT}.base.info\n      ${OUTPUT}.test.info ${OUTPUT}.total.info\n      # Copy output into coverage.info to be used by codecov\n      COMMAND mv ${OUTPUT}.filtered.info ${CMAKE_BINARY_DIR}/tmp/coverage.info\n      # Set work directory for target\n      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n      # Echo what is being done\n      COMMENT \"SpECTRE ${TEST_SUITE} Test Code Coverage Report\"\n  )\n\n  # Make test coverage target dependent on optional dependencies passed in using\n  # keyword DEPENDS\n  add_dependencies(${TARGET_NAME} ${ARG_DEPENDS})\n\n  # Output code coverage target enabled\n  string(REPLACE \";\" \" \" ARGUMENTS \"${ARG_TESTRUNNER_ARGS}\")\n  message(\n      STATUS\n      \"Enabling code coverage target '${TARGET_NAME}' tested by \"\n      \"'${TEST_RUNNER} ${ARGUMENTS}', dependencies {${ARG_DEPENDS}}, \"\n      \"report at ${OUTPUT}/index.html\"\n  )\n\nendfunction()\n"
  },
  {
    "path": "cmake/CodeCoverageDetection.cmake",
    "content": "################################################################################\n#\n# \\file      cmake/CodeCoverageDetection.cmake\n# \\author    J. Bakosi\n# \\copyright 2012-2015, Jozsef Bakosi, 2016, Los Alamos National Security, LLC.\n# \\brief     Detect prerequesites for code coverage analysis\n# \\date      Fri 03 Mar 2017 11:50:24 AM MST\n#\n# Modifications for SpECTRE:\n# 1) Auto find either llvm-cov or llvm-cov-${LLVM_VERSION} instead of using\n#    a shell script that hard codes the LLVM version\n# 2) Formatting changes\n################################################################################\n\n# Attempt to find tools required for code coverage analysis\n\nif(NOT LLVM_COV_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(LLVM_COV_ROOT \"\")\n  set(LLVM_COV_ROOT $ENV{LLVM_COV_ROOT})\nendif()\n\noption(COVERAGE \"Enable code coverage analysis.\" OFF)\n\n# Code coverage analysis only supported if all prerequisites found and the user\n# has requested it via the cmake variable COVERAGE=on..\nif(COVERAGE)\n  if(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" )\n    string(\n      REGEX MATCH \"^[0-9]+.[0-9]+\" LLVM_VERSION\n      \"${CMAKE_CXX_COMPILER_VERSION}\"\n      )\n    find_program(\n      LLVM_COV_BIN\n      NAMES \"llvm-cov-${LLVM_VERSION}\" \"llvm-cov\"\n      HINTS ${LLVM_COV_ROOT}\n      )\n    configure_file(\n      \"${CMAKE_SOURCE_DIR}/tools/llvm-gcov.sh\"\n      \"${CMAKE_BINARY_DIR}/llvm-gcov.sh\"\n      )\n    set(GCOV \"${CMAKE_BINARY_DIR}/llvm-gcov.sh\")\n  elseif( CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\" )\n    find_program(GCOV gcov REQUIRED)\n  endif()\n\n  find_program(LCOV lcov REQUIRED)\n  find_program(GENHTML genhtml REQUIRED)\n  find_program(SED sed REQUIRED)\n\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} --coverage\")\n  set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} --coverage\")\n\n  # Enable code coverage analysis.\n  SET(CODE_COVERAGE ON)\n\n  # Make flag enabling code coverage analysis available in parent cmake scope\n  mark_as_advanced(CODE_COVERAGE)\n\n  # Only include code coverage cmake functions if all prerequisites are met\n  include(CodeCoverage)\nelseif(COVERAGE)\n  message(FATAL_ERROR \"Failed to enable code coverage analysis. Not all \"\n    \"prerequisites found: gcov:${GCOV}, lcov:${LCOV}, genhtml:${GENHTML},\"\n    \" sed:${SED}\")\nendif()\n"
  },
  {
    "path": "cmake/EnableWarnings.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\ninclude(AddCxxFlag)\n\n# On systems where we can't use -isystem (Cray), we don't want\n# all the warnings enabled because we get flooded with system warnings.\noption(ENABLE_WARNINGS \"Enable the default warning level\" ON)\nif(${ENABLE_WARNINGS})\n  create_cxx_flags_target(\n    \"-W;\\\n-Wall;\\\n-Wcast-align;\\\n-Wcast-qual;\\\n-Wdisabled-optimization;\\\n-Wdocumentation;\\\n-Wextra;\\\n-Wformat-nonliteral;\\\n-Wformat-security;\\\n-Wformat-y2k;\\\n-Wformat=2;\\\n-Winvalid-pch;\\\n-Wmissing-declarations;\\\n-Wmissing-field-initializers;\\\n-Wmissing-format-attribute;\\\n-Wmissing-include-dirs;\\\n-Wmissing-noreturn;\\\n-Wnewline-eof;\\\n-Wnon-virtual-dtor;\\\n-Wold-style-cast;\\\n-Woverloaded-virtual;\\\n-Wpacked;\\\n-Wpedantic;\\\n-Wpointer-arith;\\\n-Wredundant-decls;\\\n-Wshadow;\\\n-Wsign-conversion;\\\n-Wstack-protector;\\\n-Wswitch-default;\\\n-Wunreachable-code;\\\n-Wwrite-strings\" SpectreWarnings)\nelse()\n  add_library(SpectreWarnings INTERFACE)\nendif()\n\n# Disable some warnings\ncreate_cxx_flags_target(\n    \"-Wno-dangling-reference;\\\n-Wno-documentation-unknown-command;\\\n-Wno-mismatched-tags;\\\n-Wno-interference-size;\\\n-Wno-non-template-friend;\\\n-Wno-type-limits;\\\n-Wno-undefined-var-template;\\\n-Wno-gnu-zero-variadic-macro-arguments;\\\n-Wno-noexcept-type\"\n  SpectreDisableSomeWarnings)\ntarget_link_libraries(\n  SpectreWarnings\n  INTERFACE\n  SpectreDisableSomeWarnings\n  )\n\n# - GCC versions below 13 don't respect 'GCC diagnostic' pragmas to disable\n#   warnings by the preprocessor:\n#   https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431\n#   So we disable the warning about unknown pragmas because we can't silence it.\n# - GCC has many false positives for `stringop-overflow`, `array-bounds`, and\n#   `restrict`, specifically in libstdc++ <string> with C++20, leading to\n#   warnings from `__builtin_memcpy`.\n# - GCC has false-positive `use-after-free` warnings from Blaze's DynamicMatrix\n#   constructor that gets inlined in many places. Rather than silencing the\n#   warning at every call site or forcing no-inline, we silence it here.\nif (CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n  create_cxx_flags_target(\n    \"-Wno-unknown-pragmas;\\\n-Wno-stringop-overflow;\\\n-Wno-stringop-overread;\\\n-Wno-maybe-uninitialized;\\\n-Wno-array-bounds;\\\n-Wno-restrict;\\\n-Wno-use-after-free\"\n    SpectreDisableGccWarnings)\n  target_link_libraries(\n    SpectreWarnings\n    INTERFACE\n    SpectreDisableGccWarnings\n    )\nendif()\n\n# Suppress CUDA warnings that we don't want\ncreate_cxx_flag_target(\n  \"-Xcudafe \\\"--diag_suppress=177,186,191,554,1301,1305,2189,3060,20012\\\"\"\n  SpectreCudaWarnings)\ntarget_link_libraries(\n  SpectreWarnings\n  INTERFACE\n  SpectreCudaWarnings\n  )\n\ntarget_link_libraries(\n  SpectreFlags\n  INTERFACE\n  SpectreWarnings\n  )\n"
  },
  {
    "path": "cmake/FindBlaze.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif(NOT BLAZE_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(BLAZE_ROOT \"\")\n  set(BLAZE_ROOT $ENV{BLAZE_ROOT})\nendif()\n\nfind_path(\n    BLAZE_INCLUDE_DIR\n    PATH_SUFFIXES include\n    NAMES blaze/Blaze.h\n    HINTS ${BLAZE_ROOT}\n    DOC \"Blaze include directory. Used BLAZE_ROOT to set a search dir.\"\n)\n\nset(BLAZE_INCLUDE_DIRS ${BLAZE_INCLUDE_DIR})\n\nset(BLAZE_VERSION \"\")\n\nif(EXISTS \"${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h\")\n  # Extract version info from header\n  file(READ\n    \"${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h\"\n    BLAZE_FIND_HEADER_CONTENTS)\n\n  string(REGEX MATCH \"#define BLAZE_MAJOR_VERSION [0-9]+\"\n    BLAZE_MAJOR_VERSION \"${BLAZE_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"#define BLAZE_MAJOR_VERSION \" \"\"\n    BLAZE_MAJOR_VERSION\n    ${BLAZE_MAJOR_VERSION})\n\n  string(REGEX MATCH \"#define BLAZE_MINOR_VERSION [0-9]+\"\n    BLAZE_MINOR_VERSION \"${BLAZE_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"#define BLAZE_MINOR_VERSION \" \"\"\n    BLAZE_MINOR_VERSION\n    ${BLAZE_MINOR_VERSION})\n\n  set(BLAZE_VERSION\n    \"${BLAZE_MAJOR_VERSION}.${BLAZE_MINOR_VERSION}\"\n    )\nelseif(BLAZE_INCLUDE_DIRS)\n  message(WARNING \"Failed to find file \"\n    \"'${BLAZE_INCLUDE_DIRS}/blaze/system/Version.h' \"\n    \"while detecting the Blaze version.\")\nendif()\n\nset(Blaze_VERSION ${BLAZE_VERSION})\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  Blaze\n  FOUND_VAR BLAZE_FOUND\n  REQUIRED_VARS BLAZE_INCLUDE_DIR BLAZE_INCLUDE_DIRS\n  VERSION_VAR BLAZE_VERSION\n  )\nmark_as_advanced(BLAZE_INCLUDE_DIR BLAZE_INCLUDE_DIRS\n  BLAZE_VERSION BLAZE_MAJOR_VERSION BLAZE_MINOR_VERSION\n  Blaze_VERSION\n  )\n"
  },
  {
    "path": "cmake/FindBreathe.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif(NOT BREATHE_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(BREATHE_ROOT \"\")\n  set(BREATHE_ROOT $ENV{BREATHE_ROOT})\nendif()\n\n# Look for an executable called breathe-apidoc\nfind_program(\n  BREATHE_APIDOC_EXECUTABLE\n  NAMES breathe-apidoc\n  PATHS ${BREATHE_ROOT}\n  DOC \"Path to breathe-apidoc executable\")\n\nexecute_process(COMMAND \"${BREATHE_APIDOC_EXECUTABLE}\" \"--version\"\n  RESULT_VARIABLE VERSION_RESULT\n  OUTPUT_VARIABLE VERSION_OUTPUT\n  OUTPUT_STRIP_TRAILING_WHITESPACE)\n\ninclude(FindPackageHandleStandardArgs)\n\nif(VERSION_RESULT MATCHES 0)\n  string(REGEX MATCH \"[0-9]+\\.[0-9]+[\\.]?[0-9]*\"\n    BREATHE_APIDOC_VERSION ${VERSION_OUTPUT})\n  set(BREATHE_VERSION ${BREATHE_APIDOC_VERSION})\nendif(VERSION_RESULT MATCHES 0)\n\n# Handle standard arguments to find_package like REQUIRED and QUIET\nfind_package_handle_standard_args(Breathe\n  REQUIRED_VARS BREATHE_APIDOC_EXECUTABLE BREATHE_VERSION\n  BREATHE_APIDOC_VERSION\n  VERSION_VAR BREATHE_VERSION)\n"
  },
  {
    "path": "cmake/FindCharm.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# FindCharm.cmake\n#\n# Finds a suitable Charm++ installation and exposes it via imported targets.\n#\n# This module requires you have set the `CHARM_ROOT` variable to a Charm++\n# installation directory, e.g.:\n#\n#   cmake -D CHARM_ROOT=/path/to/charm++/build-dir\n#\n# This is to ensure that SpECTRE builds with the Charm++ installation that you\n# intended to use. The `CHARM_ROOT/bin` directory should contain the Charm++\n# compiler `charmc`. All information on include directories, library paths,\n# compiler flags etc. will be retrieved by invoking `charmc` with the\n# `-print-building-blocks` flag. Note that we do _not_ use `charmc` to wrap the\n# CMake compiler configuration. Instead, the information retrieved from `charmc`\n# is made available to CMake via imported targets.\n#\n# This module supports CMake \"components\" to load additional Charm++ modules.\n# You can load additional Charm++ modules like this:\n#\n#   find_package(Charm 7.0.0 COMPONENTS EveryLB)\n#\n# This module exposes the following targets:\n#\n# - `Charmxx::charmxx`: All Charm++ libraries.\n# - `Charmxx::pup`: Only the Charm++ PUP serialization library.\n# - `Charmxx::main`: Defines the Charm++ main function. Link to compile\n#   executables.\n# - `CharmModuleInit`: Provides the generated definitions of Charm++'s\n#   module-init functions.\n\noption(CHARM_SHARED_LIBS \"Link Charm++ shared libraries. Defaults to the \\\nvalue of 'BUILD_SHARED_LIBS' but can be overriden to enforce shared or static \\\nlibraries for Charm++.\" ${BUILD_SHARED_LIBS})\n\noption(CHARM_USE_MPI \"Find and link MPI with Charm++. An attempt is made to \\\ndetermine automatically if MPI is needed, but this flag can be turned on to \\\nenable linking MPI if the automatic check fails.\" OFF)\n\noption(CHARM_TRACE_PROJECTIONS \"Charm++ was built with projections support on. \\\nEnables '-tracemode projections'\" OFF)\n\noption(CHARM_TRACE_SUMMARY \"Charm++ was built with projections support on. \\\nEnables '-tracemode summary'\" OFF)\n\nif (DEFINED ENV{CHARM_ROOT} AND \"${CHARM_ROOT}\" STREQUAL \"\")\n  set(CHARM_ROOT \"$ENV{CHARM_ROOT}\")\nendif()\n\nif (NOT EXISTS \"${CHARM_ROOT}\")\n  if (\"${CHARM_ROOT}\" STREQUAL \"\")\n    message(\n        FATAL_ERROR \"CHARM_ROOT was not set. Pass it as a command-line arg: \"\n        \"cmake -D CHARM_ROOT=/path/to/charm++/build-dir\")\n  endif()\n  message(\n      FATAL_ERROR \"CHARM_ROOT=${CHARM_ROOT} does not exist. \"\n      \"Please pass it as a command-line definition to cmake, i.e. \"\n      \"cmake -D CHARM_ROOT=/path/to/charm++/build-dir\"\n  )\nendif ()\n\n# Find the Charm compiler `charmc` first so we can retrieve build info from it\nfind_program(CHARM_COMPILER\n  NAMES charmc\n  PATH_SUFFIXES bin\n  HINTS ${CHARM_ROOT} ENV CHARM_ROOT\n  NO_DEFAULT_PATH\n  DOC \"The full path to the charm++ compiler 'charmc'\"\n  )\nif(CHARM_COMPILER STREQUAL \"CHARM_COMPILER-NOTFOUND\")\n  message(FATAL_ERROR \"The Charm++ compiler 'charmc' could not be found. Make \"\n    \"sure you have set 'CHARM_ROOT' to the path that contains 'bin/charmc'.\")\nendif()\n\n# Assemble options for invoking `charmc`:\n# - Use static or shared libs\nif(CHARM_SHARED_LIBS)\n  list(APPEND CHARMC_OPTIONS \"-charm-shared\")\nendif()\n# - Not linking the main function by default. We expose an extra target that\n#   links it because we sometimes want to link Charm++ without a main function.\nlist(APPEND CHARMC_OPTIONS \"-nomain\")\nlist(APPEND CHARMC_OPTIONS \"-nomain-module\")\nif(CHARM_TRACE_PROJECTIONS)\n  list(APPEND CHARMC_OPTIONS \"-tracemode projections\")\nendif()\nif(CHARM_TRACE_SUMMARY)\n  list(APPEND CHARMC_OPTIONS \"-tracemode summary\")\nendif()\n# - Request optional Charm++ modules. They can be specified as `COMPONENTS` when\n#   calling CMake's `find_package`.\nif(Charm_FIND_COMPONENTS)\n  list(JOIN Charm_FIND_COMPONENTS \",\" CHARM_COMPONENTS_JOINED)\n  list(APPEND CHARMC_OPTIONS \"-modules ${CHARM_COMPONENTS_JOINED}\")\nendif()\n\n# Invoke 'charmc' so that it outputs build info\nlist(JOIN CHARMC_OPTIONS \" \" CHARMC_OPTIONS_JOINED)\nset(CHARM_BUILDING_BLOCKS_CMD\n  \"${CHARM_COMPILER} -print-building-blocks ${CHARMC_OPTIONS_JOINED}\")\nmessage(STATUS \"Charm++ 'charmc' is invoked as: ${CHARM_BUILDING_BLOCKS_CMD}\")\nexecute_process(\n  COMMAND bash -c \"${CHARM_BUILDING_BLOCKS_CMD}\"\n  OUTPUT_VARIABLE CHARM_BUILDING_BLOCKS\n  ERROR_VARIABLE CHARM_BUILDING_BLOCKS_ERROR\n  OUTPUT_STRIP_TRAILING_WHITESPACE\n  )\nif(NOT CHARM_BUILDING_BLOCKS)\n  if(CHARM_BUILDING_BLOCKS_ERROR MATCHES \"lib_so directory not found\"\n      AND CHARM_SHARED_LIBS)\n    message(STATUS \"Charm++ is not built with shared-library support so we \"\n      \"fall back to static libraries. Build Charm++ with the '--build-shared' \"\n      \"option to enable shared libraries.\")\n    set(CHARM_SHARED_LIBS OFF)\n    list(REMOVE_ITEM CHARMC_OPTIONS \"-charm-shared\")\n    list(JOIN CHARMC_OPTIONS \" \" CHARMC_OPTIONS_JOINED)\n    set(CHARM_BUILDING_BLOCKS_CMD\n      \"${CHARM_COMPILER} -print-building-blocks ${CHARMC_OPTIONS_JOINED}\")\n    execute_process(\n      COMMAND bash -c \"${CHARM_BUILDING_BLOCKS_CMD}\"\n      OUTPUT_VARIABLE CHARM_BUILDING_BLOCKS\n      ERROR_VARIABLE CHARM_BUILDING_BLOCKS_ERROR\n      OUTPUT_STRIP_TRAILING_WHITESPACE\n      )\n  else()\n    message(FATAL_ERROR \"Failed retrieving Charm++ building blocks with \"\n      \"command:\\n  ${CHARM_BUILDING_BLOCKS_CMD}\\nPlease make sure the Charm++ \"\n      \"version is at least 6.9.0. Error message:\\n\"\n      \"${CHARM_BUILDING_BLOCKS_ERROR}\")\n  endif()\nendif()\nstring(REGEX REPLACE \";\" \"\\\\\\\\;\"\n  CHARM_BUILDING_BLOCKS_LIST \"${CHARM_BUILDING_BLOCKS}\")\nstring(REGEX REPLACE \"\\n\" \";\" CHARM_BUILDING_BLOCKS_LIST\n  \"${CHARM_BUILDING_BLOCKS_LIST}\")\n\n# Load the charm building blocks into CMake variables\nforeach(CHARM_BUILDING_BLOCK IN LISTS CHARM_BUILDING_BLOCKS_LIST)\n  if(CHARM_BUILDING_BLOCK MATCHES \"^([^=]+)='(.*)'$\")\n    set(${CMAKE_MATCH_1} \"${CMAKE_MATCH_2}\")\n    string(STRIP \"${${CMAKE_MATCH_1}}\" ${CMAKE_MATCH_1})\n  else()\n    message(SEND_ERROR \"Unexpected output from charmc: ${CHARM_BUILDING_BLOCK}\")\n  endif()\nendforeach()\n# Validate the charm building blocks\nlist(APPEND CHARM_REQUIRED_BUILDING_BLOCKS\n  CHARM_CXX\n  CHARM_CXX_FLAGS\n  CHARM_LDXX_FLAGS\n  CHARMINC\n  # Not needed but available, could be added if needed:\n  # CHARM_CC\n  # CHARM_LD\n  # CHARM_LDXX\n  # CHARM_CC_FLAGS\n  # CHARM_LD_FLAGS\n  # CHARMBIN\n  )\nif(CHARM_SHARED_LIBS)\n  list(APPEND CHARM_REQUIRED_BUILDING_BLOCKS CHARMLIBSO)\nelse()\n  list(APPEND CHARM_REQUIRED_BUILDING_BLOCKS CHARMLIB)\nendif()\nforeach(CHARM_REQUIRED_BUILDING_BLOCK IN LISTS CHARM_REQUIRED_BUILDING_BLOCKS)\n  if(NOT ${CHARM_REQUIRED_BUILDING_BLOCK})\n    message(FATAL_ERROR \"Could not find ${CHARM_REQUIRED_BUILDING_BLOCK} \"\n      \"variable in output of command: ${CHARM_BUILDING_BLOCKS_CMD}\")\n  endif()\nendforeach()\n\n# Split flags into lists\nseparate_arguments(CHARM_CXX_FLAGS)\nseparate_arguments(CHARM_LDXX_FLAGS)\n# Define variables with standard names for compatibility, though these should\n# not be used outside this script.\nget_filename_component(CHARM_INCLUDE_DIR ${CHARMINC} ABSOLUTE)\nset(CHARM_INCLUDE_DIRS ${CHARM_INCLUDE_DIR})\nif(CHARM_SHARED_LIBS)\n  set(CHARM_LIBRARIES ${CHARMLIBSO})\nelse()\n  set(CHARM_LIBRARIES ${CHARMLIB})\nendif()\n\n# Find version file\nif(EXISTS \"${CHARM_INCLUDE_DIR}/charm-version.h\")\n  set(CHARM_VERSION_FILE_VERSION \"6_11\")\n  set(CHARM_VERSION_FILE_LOCATION \"${CHARM_INCLUDE_DIR}/charm-version.h\")\nelseif(EXISTS \"${CHARM_INCLUDE_DIR}/VERSION\")\n  set(CHARM_VERSION_FILE_VERSION \"pre_6_11\")\n  set(CHARM_VERSION_FILE_LOCATION \"${CHARM_INCLUDE_DIR}/VERSION\")\nelseif(EXISTS \"${CHARM_ROOT}/VERSION\")\n  set(CHARM_VERSION_FILE_VERSION \"pre_6_11\")\n  set(CHARM_VERSION_FILE_LOCATION \"${CHARM_ROOT}/VERSION\")\nelse()\n  message(FATAL_ERROR \"Failed to find Charm++ version file\")\nendif()\n\n# Parse version from file\nfile(READ \"${CHARM_VERSION_FILE_LOCATION}\" CHARM_VERSION_FILE)\nif(CHARM_VERSION_FILE_VERSION STREQUAL \"6_11\")\n  # Since version 6.11 the file is C++-compatible\n  if(CHARM_VERSION_FILE MATCHES \"#define CHARM_VERSION_MAJOR ([0-9]+)\")\n    set(CHARM_VERSION_MAJOR ${CMAKE_MATCH_1})\n  else()\n    message(FATAL_ERROR \"Could not parse CHARM_VERSION_MAJOR from file: \"\n      \"${CHARM_VERSION_FILE_LOCATION}\")\n  endif()\n  if(CHARM_VERSION_FILE MATCHES \"#define CHARM_VERSION_MINOR ([0-9]+)\")\n    set(CHARM_VERSION_MINOR ${CMAKE_MATCH_1})\n  else()\n    message(FATAL_ERROR \"Could not parse CHARM_VERSION_MINOR from file: \"\n      \"${CHARM_VERSION_FILE_LOCATION}\")\n  endif()\n  if(CHARM_VERSION_FILE MATCHES \"#define CHARM_VERSION_PATCH ([0-9]+)\")\n    set(CHARM_VERSION_PATCH ${CMAKE_MATCH_1})\n  else()\n    message(FATAL_ERROR \"Could not parse CHARM_VERSION_PATCH from file: \"\n      \"${CHARM_VERSION_FILE_LOCATION}\")\n  endif()\nelseif(CHARM_VERSION_FILE_VERSION STREQUAL \"pre_6_11\")\n  # Before version 6.11 the file contains only a string\n  string(REGEX REPLACE \"\\n\" \"\" CHARM_VERSION_FILE \"${CHARM_VERSION_FILE}\")\n  string(\n    REGEX REPLACE\n    \"([0-9])1([0-9])0([0-9])\"\n    \"\\\\1;1\\\\2;\\\\3\"\n    CHARM_VERSIONS_PARSED\n    ${CHARM_VERSION_FILE}\n    )\n  list(GET CHARM_VERSIONS_PARSED 0 CHARM_VERSION_MAJOR)\n  list(GET CHARM_VERSIONS_PARSED 1 CHARM_VERSION_MINOR)\n  list(GET CHARM_VERSIONS_PARSED 2 CHARM_VERSION_PATCH)\nendif()\nset(CHARM_VERSION\n  \"${CHARM_VERSION_MAJOR}.${CHARM_VERSION_MINOR}.${CHARM_VERSION_PATCH}\")\n\n# Filter the compiler and linker flags:\n# - Remove the macOS deployment target so the CMake setting is used.\nlist(FILTER CHARM_CXX_FLAGS EXCLUDE REGEX \"^-mmacosx-version-min=\")\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"^-mmacosx-version-min=\")\n# - Remove the C++ standard flag so the CMake setting is used.\nlist(FILTER CHARM_CXX_FLAGS EXCLUDE REGEX \"^-std=\")\n# - Remove the standard library flag so the CMake setting is used.\nlist(FILTER CHARM_CXX_FLAGS EXCLUDE REGEX \"^-stdlib=\")\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"^-stdlib=\")\n# - Remove the -fPIC and -fPIE flags so the CMake setting is used.\nlist(FILTER CHARM_CXX_FLAGS EXCLUDE REGEX \"-fPI[CE]\")\nlist(FILTER CHARM_CXX_FLAGS EXCLUDE REGEX \"-fpi[ce]\")\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"-fPI[CE]\")\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"-fpi[ce]\")\n# - Remove the -rdynamic flag so the CMake setting is used. This flag is needed\n#   for `backtrace` functions. We handle the flag in our own build system\n#   (instead of taking it over from Charm++).\nlist(FILTER CHARM_CXX_FLAGS EXCLUDE REGEX \"-dynamic\")\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"-rdynamic\")\n# - Extract the include directories so we can set them properly with CMake.\n#   That's better because they are declared as \"SYSTEM\", which silences warnings\n#   from those headers.\nlist(FILTER CHARM_CXX_FLAGS EXCLUDE REGEX \"^-I${CHARMINC}$\")\nset(CHARM_INCLUDE_EXTRA ${CHARM_CXX_FLAGS})\nlist(FILTER CHARM_INCLUDE_EXTRA INCLUDE REGEX \"^-I.+\")\nlist(FILTER CHARM_CXX_FLAGS EXCLUDE REGEX \"^-I.+\")\nlist(TRANSFORM CHARM_INCLUDE_EXTRA REPLACE \"^-I\" \"\")\n# Filter out relative paths to `./proc_management` that are added for some\n# backends (ucx and ofi), since those paths don't seem to exist\nlist(FILTER CHARM_INCLUDE_EXTRA EXCLUDE REGEX \"^[.]/proc_management\")\nlist(APPEND CHARM_INCLUDE_DIRS ${CHARM_INCLUDE_EXTRA})\n# - Remove the rpath linker argument, since CMake adds it automatically.\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"^-Wl,-rpath,${CHARM_LIBRARIES}/?$\")\n# - Extract lib directories\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"^-L${CHARM_LIBRARIES}$\")\nset(CHARM_LIBRARIES_EXTRA ${CHARM_LDXX_FLAGS})\nlist(FILTER CHARM_LIBRARIES_EXTRA INCLUDE REGEX \"^-L.+\")\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"^-L.+\")\nlist(TRANSFORM CHARM_LIBRARIES_EXTRA REPLACE \"^-L\" \"\")\nlist(APPEND CHARM_LIBRARIES ${CHARM_LIBRARIES_EXTRA})\n# - Remove the link directory and find the libraries on the system so we can\n#   configure them properly with CMake. That's more robust when switching\n#   between static and shared libs builds. Instead of parsing the list of libs\n#   below we could just hard-code lib names, but that may break with future\n#   Charm versions and could be annoying to debug. However, it would give us\n#   more control over which libs to link. Ideally, Charm++ would provide\n#   exported targets with these libs.\nset(CHARM_LIB_NAMES ${CHARM_LDXX_FLAGS})\nlist(FILTER CHARM_LIB_NAMES INCLUDE REGEX \"^-l.+\")\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"^-l.+\")\nlist(TRANSFORM CHARM_LIB_NAMES REPLACE \"^-l\" \"\")\nlist(JOIN CHARM_LIB_NAMES \", \" CHARM_LIB_NAMES_OUTPUT)\nmessage(STATUS \"Charm++ lib names: ${CHARM_LIB_NAMES_OUTPUT}\")\nset(CHARM_LIBS_TO_FIND ${CHARM_LIB_NAMES})\n# Also find the 'ckmain' library, which isn't included in the list of lib names\n# because we invoked charmc with '-nomain'\nlist(APPEND CHARM_LIBS_TO_FIND ckmain)\nforeach(CHARM_LIB_NAME IN LISTS CHARM_LIBS_TO_FIND)\n  # First, find the lib _only_ in CHARM_LIBRARIES. This ensures we prefer the\n  # shared libs in the separate CHARM_ROOT/lib_so directory if requested.\n  find_library(CHARM_LIB_${CHARM_LIB_NAME}\n    NAMES ${CHARM_LIB_NAME}\n    HINTS ${CHARM_LIBRARIES}\n    NO_DEFAULT_PATH\n    )\n  # Second, fall back to a wider search on the system\n  find_library(CHARM_LIB_${CHARM_LIB_NAME}\n    NAMES ${CHARM_LIB_NAME}\n    HINTS ENV LIBRARY_PATH\n    )\n  if (\"${CHARM_LIB_${CHARM_LIB_NAME}}\"\n        STREQUAL \"CHARM_LIB_${CHARM_LIB_NAME}-NOTFOUND\")\n    message(SEND_ERROR \"Could not find Charm++ library '${CHARM_LIB_NAME}'. \"\n      \"Make sure you have built Charm++ with support for this library, and you \\\nhave loaded the appropriate modules.\")\n  endif()\n  list(APPEND CHARM_LIBS ${CHARM_LIB_${CHARM_LIB_NAME}})\n  # Collect libs for the PUP serialization library so we can link it separately\n  # if we want. The PUP functionality is in `conv-util` in Charm++ 6.10.2 and\n  # in the combined `converse` lib in later versions.\n  if(CHARM_LIB_NAME STREQUAL conv-util OR CHARM_LIB_NAME STREQUAL converse)\n    list(APPEND PUP_LIBS ${CHARM_LIB_${CHARM_LIB_NAME}})\n  endif()\nendforeach()\n# - Extract libraries that are linked as object files (`conv-static` in Charm++\n#   6.10.2).\nset(CHARM_LIB_conv-static ${CHARM_LDXX_FLAGS})\nlist(FILTER CHARM_LIB_conv-static INCLUDE REGEX \"conv-static.o$\")\nlist(FILTER CHARM_LDXX_FLAGS EXCLUDE REGEX \"conv-static.o$\")\n\n# Report remaining flags that will be ignored. They have been used to compile\n# Charm++ but may not be compatible with the compiler that the SpECTRE build is\n# configured with. They may also include flags that have been chosen differently\n# for the SpECTRE build, e.g. optimization settings. The ignored flags are\n# printed out to catch cases where adding one of the flags could be useful.\nlist(JOIN CHARM_CXX_FLAGS \" \" CHARM_CXX_FLAGS_OUTPUT)\nmessage(STATUS \"These Charm++ compiler flags will be ignored: \"\n  \"${CHARM_CXX_FLAGS_OUTPUT}\")\nlist(JOIN CHARM_LDXX_FLAGS \" \" CHARM_LDXX_FLAGS_OUTPUT)\nmessage(STATUS \"These Charm++ linker flags will be ignored: \"\n  \"${CHARM_LDXX_FLAGS_OUTPUT}\")\n\n# Invoke `charmc` to generate definitions for the module-init functions.\n#\n# `charmc` writes a tiny temporary `moduleinit$$.C` file with a few functions\n# that often do nothing, compiles it and links it into every Charm module. We\n# have `charmc` generate this file and compile it into an object library that\n# can be linked into executables. The interface for this could be improved on\n# the Charm++ side. See this upstream issue:\n# https://github.com/UIUC-PPL/charm/issues/3210\nset(CHARM_MODULEINIT_CMD\n  \"${CHARM_COMPILER} ${CHARMC_OPTIONS_JOINED} -o unused -save\")\nexecute_process(\n  COMMAND\n  bash -c \"(${CHARM_MODULEINIT_CMD} || mv moduleinit*.C CharmModuleInit.C) \\\n    && rm moduleinit*\"\n  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tmp/\n  ERROR_VARIABLE CHARM_MODULEINIT_ERROR\n  )\nconfigure_file(\n  ${CMAKE_BINARY_DIR}/tmp/CharmModuleInit.C\n  ${CMAKE_BINARY_DIR}\n  )\nadd_library(CharmModuleInit OBJECT ${CMAKE_BINARY_DIR}/CharmModuleInit.C)\n# -w -- suppress all warnings because this is charm-generated source\nset_property(TARGET CharmModuleInit\n  APPEND PROPERTY COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:-fPIC -w>)\n\n# Use the charm building blocks to construct imported targets\n# - Imported target with all Charm libs\nadd_library(Charmxx::charmxx INTERFACE IMPORTED)\ntarget_include_directories(Charmxx::charmxx INTERFACE ${CHARM_INCLUDE_DIRS})\ntarget_link_libraries(Charmxx::charmxx INTERFACE ${CHARM_LIBS})\n# - Target just for the PUP serialization library.\nadd_library(Charmxx::pup INTERFACE IMPORTED)\ntarget_include_directories(Charmxx::pup INTERFACE ${CHARM_INCLUDE_DIRS})\ntarget_link_libraries(Charmxx::pup INTERFACE ${PUP_LIBS})\n# - Target that defines the Charm++ main function.\nadd_library(Charmxx::main INTERFACE IMPORTED)\ntarget_link_libraries(\n  Charmxx::main\n  INTERFACE\n  Charmxx::charmxx\n  ${CHARM_LIB_ckmain}\n  ${CHARM_LIB_conv-static}\n  )\n# Add a preprocessing definition to indicate that a Charm++ main module is\n# available. This is used to conditionally compile Charm++-specific code\n# that assumes it is running in a Charm++ environment, such as the\n# `Parallel/CharmRegistration.hpp` code.\ntarget_compile_definitions(\n  Charmxx::main\n  INTERFACE\n  SPECTRE_CHARM_HAS_MAIN\n  )\n\n# Find and link MPI if necessary\n#\n# We currently check if `CHARM_CXX` obviously calls into an MPI compiler wrapper\n# to turn MPI linking on automatically. In the future it may be necessary to\n# further extend support for Charm++ wrapping other compiler wrappers and turn\n# them into CMake targets, as is done with MPI here.\nif (CHARM_CXX MATCHES \".*mpicxx.*\")\n  set(CHARM_USE_MPI ON)\nendif()\nif (CHARM_USE_MPI)\n  find_package(MPI REQUIRED)\n  target_link_libraries(Charmxx::charmxx INTERFACE MPI::MPI_CXX)\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  Charm\n  # Only these paths are input variables, i.e. they are searched on the system\n  # and can be specified on the command line to select the Charm installation.\n  REQUIRED_VARS CHARM_COMPILER\n  VERSION_VAR CHARM_VERSION\n  )\n\nmark_as_advanced(\n  CHARM_COMPILER\n  CHARM_INCLUDE_DIR\n  CHARM_USE_MPI\n  CHARM_VERSION_MAJOR\n  CHARM_VERSION_MINOR\n  CHARM_VERSION_PATCH\n  CHARM_VERSION\n  )\n"
  },
  {
    "path": "cmake/FindClangFormat.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif(NOT CLANG_FORMAT_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(CLANG_FORMAT_ROOT \"\")\n  set(CLANG_FORMAT_ROOT $ENV{CLANG_FORMAT_ROOT})\nendif()\n\nif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  string(\n      REGEX MATCH \"^[0-9]+.[0-9]+\" LLVM_VERSION\n      \"${CMAKE_CXX_COMPILER_VERSION}\"\n  )\n  find_program(\n      CLANG_FORMAT_BIN\n      NAMES \"clang-format-${LLVM_VERSION}\" \"clang-format\"\n      HINTS ${CLANG_FORMAT_ROOT}\n  )\nelse()\n  find_program(\n      CLANG_FORMAT_BIN\n      NAMES\n      \"clang-format-4.0\"\n      \"clang-format-3.9\"\n      \"clang-format-3.8\"\n      \"clang-format\"\n  )\nendif()\n\nif (CLANG_FORMAT_BIN)\n  execute_process(COMMAND ${CLANG_FORMAT_BIN} --version\n    RESULT_VARIABLE CLANG_FORMAT_VERSION_RESULT\n    OUTPUT_VARIABLE CLANG_FORMAT_VERSION\n    OUTPUT_STRIP_TRAILING_WHITESPACE)\n\n  if(CLANG_FORMAT_VERSION_RESULT MATCHES 0)\n    string(REGEX REPLACE \"clang-format version \" \"\"\n      CLANG_FORMAT_VERSION ${CLANG_FORMAT_VERSION})\n  endif()\n\n  # Look for a git-clang-format program in the same place as\n  # clang-format.  Versions of git-clang-format are not always\n  # compatible with other clang-format executables.\n  get_filename_component(CLANG_FORMAT_DIR ${CLANG_FORMAT_BIN} DIRECTORY)\n  get_filename_component(CLANG_FORMAT_NAME ${CLANG_FORMAT_BIN} NAME)\n  find_program(\n    GIT_CLANG_FORMAT_BIN\n    NAMES git-${CLANG_FORMAT_NAME}\n    HINTS ${CLANG_FORMAT_DIR}\n    NO_DEFAULT_PATH\n  )\nendif()\n\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  ClangFormat REQUIRED_VARS CLANG_FORMAT_BIN VERSION_VAR CLANG_FORMAT_VERSION\n  )\n\nmark_as_advanced(CLANG_FORMAT_VERSION)\n"
  },
  {
    "path": "cmake/FindFFTW.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find FFTW3 (The Fastest Fourier Transform in the West)\n# http://www.fftw.org/\n#\n# Defines the `FFTW::FFTW` target to link against.\n\nif(NOT FFTW_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(FFTW_ROOT \"\")\n  set(FFTW_ROOT $ENV{FFTW_ROOT})\nendif()\n\nfind_path(FFTW_INCLUDE_DIR\n  NAMES fftw3.h\n  PATH_SUFFIXES include\n  HINTS ${FFTW_ROOT}\n  )\n\nfind_library(FFTW_LIB\n  NAMES fftw3\n  PATH_SUFFIXES lib64 lib\n  HINTS ${FFTW_ROOT}\n  )\n\nif (FFTW_INCLUDE_DIR AND FFTW_LIB)\n  add_library(FFTW::FFTW INTERFACE IMPORTED)\n  target_include_directories(FFTW::FFTW INTERFACE ${FFTW_INCLUDE_DIR})\n  target_link_libraries(FFTW::FFTW INTERFACE ${FFTW_LIB})\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(FFTW\n  REQUIRED_VARS FFTW_INCLUDE_DIR FFTW_LIB)\nmark_as_advanced(FFTW_INCLUDE_DIR FFTW_LIB)\n"
  },
  {
    "path": "cmake/FindFUKA.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find the FUKA initial data code (https://bitbucket.org/fukaws/fuka/src/fuka/)\n#\n# Pass `FUKA_ROOT` to the CMake build configuration to set up the following\n# targets:\n#\n# - FUKA::Exporter: Functionality to load FUKA volume data and interpolate to\n#   arbitrary points.\n\nif(NOT FUKA_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(FUKA_ROOT \"\")\n  set(FUKA_ROOT $ENV{FUKA_ROOT})\nendif()\n\nfind_library(\n  FUKA_LIB\n  NAMES libkadath.a\n  PATHS ${FUKA_ROOT}\n  NO_DEFAULT_PATHS\n  )\n\n# Link MPI (should be the same MPI that FUKA was built with)\nfind_package(MPI COMPONENTS C)\n\nif (FUKA_LIB AND MPI_C_FOUND)\n  find_package(FFTW)\n  if (FFTW_FOUND)\n    add_library(FUKA::Exporter INTERFACE IMPORTED)\n    target_link_libraries(\n      FUKA::Exporter\n      INTERFACE\n      MPI::MPI_C\n      FFTW::FFTW\n      ${FUKA_LIB}\n    )\n  endif()\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  FUKA\n  REQUIRED_VARS\n  FUKA_LIB\n  )\n"
  },
  {
    "path": "cmake/FindGoogleBenchmark.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find GOOGLE_BENCHMARK: https://github.com/google/benchmark\n# If not in one of the default paths specify\n# -DGOOGLE_BENCHMARK_ROOT=/path/to/GOOGLE_BENCHMARK to search there as well.\n\nif(NOT GOOGLE_BENCHMARK_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(GOOGLE_BENCHMARK_ROOT \"\")\n  set(GOOGLE_BENCHMARK_ROOT $ENV{GOOGLE_BENCHMARK_ROOT})\nendif()\n\nif(NOT GoogleBenchmark_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(GoogleBenchmark_ROOT \"\")\n  set(GoogleBenchmark_ROOT $ENV{GoogleBenchmark_ROOT})\nendif()\n\nfind_path(GoogleBenchmark_INCLUDE_DIRS benchmark.h\n    PATH_SUFFIXES include/benchmark\n    HINTS ${GOOGLE_BENCHMARK_ROOT})\n\nfind_library(GoogleBenchmark_LIBRARIES\n    NAMES benchmark\n    PATH_SUFFIXES lib64 lib\n    HINTS ${GOOGLE_BENCHMARK_ROOT} ${GoogleBenchmark_ROOT})\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(GoogleBenchmark\n  FOUND_VAR GoogleBenchmark_FOUND\n  REQUIRED_VARS GoogleBenchmark_INCLUDE_DIRS GoogleBenchmark_LIBRARIES)\nmark_as_advanced(GoogleBenchmark_INCLUDE_DIRS GoogleBenchmark_LIBRARIES)\n"
  },
  {
    "path": "cmake/FindJEMALLOC.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find jemalloc: https://github.com/jemalloc/jemalloc\n# If not in one of the default paths specify -D JEMALLOC_ROOT=/path/to/jemalloc\n# to search there as well.\n\nif(NOT JEMALLOC_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(JEMALLOC_ROOT \"\")\n  set(JEMALLOC_ROOT $ENV{JEMALLOC_ROOT})\nendif()\n\n# find the jemalloc include directory\nfind_path(JEMALLOC_INCLUDE_DIRS jemalloc/jemalloc.h\n  PATH_SUFFIXES include\n  HINTS ${JEMALLOC_ROOT})\n\nfind_library(JEMALLOC_LIBRARIES\n  NAMES jemalloc\n  PATH_SUFFIXES lib64 lib\n  HINTS ${JEMALLOC_ROOT})\n\nset(JEMALLOC_VERSION \"\")\n\nif(EXISTS \"${JEMALLOC_INCLUDE_DIRS}/jemalloc/jemalloc.h\")\n  # Extract version info from header\n  file(READ\n    \"${JEMALLOC_INCLUDE_DIRS}/jemalloc/jemalloc.h\"\n    JEMALLOC_FIND_HEADER_CONTENTS)\n\n  string(REGEX MATCH \"#define JEMALLOC_VERSION_MAJOR [0-9]+\"\n    JEMALLOC_MAJOR_VERSION \"${JEMALLOC_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"#define JEMALLOC_VERSION_MAJOR \" \"\"\n    JEMALLOC_MAJOR_VERSION\n    \"${JEMALLOC_MAJOR_VERSION}\")\n\n  string(REGEX MATCH \"#define JEMALLOC_VERSION_MINOR [0-9]+\"\n    JEMALLOC_MINOR_VERSION \"${JEMALLOC_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"#define JEMALLOC_VERSION_MINOR \" \"\"\n    JEMALLOC_MINOR_VERSION\n    \"${JEMALLOC_MINOR_VERSION}\")\n\n  string(REGEX MATCH \"#define JEMALLOC_VERSION_BUGFIX [0-9]+\"\n    JEMALLOC_SUBMINOR_VERSION \"${JEMALLOC_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"#define JEMALLOC_VERSION_BUGFIX \" \"\"\n    JEMALLOC_SUBMINOR_VERSION\n    \"${JEMALLOC_SUBMINOR_VERSION}\")\n\n  set(JEMALLOC_VERSION\n    \"${JEMALLOC_MAJOR_VERSION}.${JEMALLOC_MINOR_VERSION}\\\n.${JEMALLOC_SUBMINOR_VERSION}\"\n    )\nelse()\n  message(WARNING \"Failed to find file \"\n    \"'${JEMALLOC_INCLUDE_DIRS}/jemalloc/jemalloc.h' \"\n    \"while detecting the JEMALLOC version.\")\nendif(EXISTS \"${JEMALLOC_INCLUDE_DIRS}/jemalloc/jemalloc.h\")\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  JEMALLOC\n  FOUND_VAR JEMALLOC_FOUND\n  REQUIRED_VARS JEMALLOC_INCLUDE_DIRS JEMALLOC_LIBRARIES\n  VERSION_VAR JEMALLOC_VERSION\n  )\nmark_as_advanced(JEMALLOC_INCLUDE_DIRS JEMALLOC_LIBRARIES\n  JEMALLOC_MAJOR_VERSION JEMALLOC_MINOR_VERSION JEMALLOC_SUBMINOR_VERSION\n  JEMALLOC_VERSION)\n"
  },
  {
    "path": "cmake/FindLIBCXX.cmake",
    "content": "################################################################################\n#\n# \\file      cmake/FindLIBCXX.cmake\n# \\author    J. Bakosi\n# \\copyright 2012-2015, Jozsef Bakosi, 2016, Los Alamos National Security, LLC.\n# \\brief     Find libc++\n# \\date      Fri 20 Jan 2017 12:42:21 PM MST\n#\n################################################################################\n\n# From Quinoa: https://github.com/quinoacomputing/quinoa\n#\n# SpECTRE modifications:\n# - allow finding shared objects of libc++ and libc++abi\n\n# Find libc++.\n# See libc++: http://libcxx.llvm.org, libc++abi: http://libcxxabi.llvm.org.\n#\n#  LIBCXX_FOUND - System has libc++\n#  LIBCXX_INCLUDE_DIRS - The libc++ include directory\n#  LIBCXX_LIBRARIES - The libraries needed to use libc++\n#  LIBCXXABI_LIBRARIES - The libraries needed to use libc++abi\n#\n#  Set the LIBCXX_ROOT cmake variable or shell environment variable before\n#  calling find_package to a path to add an additional search path, e.g.,\n#\n#  Usage:\n#\n#  set(LIBCXX_ROOT \"/path/to/custom/libc++\") # prefer over system\n#  find_package(LibCXX)\n#  if(LIBCXX_FOUND)\n#    target_link_libraries (TARGET ${LIBCXX_LIBRARIES} ${LIBCXXABI_LIBRARIES})\n#  endif()\n\nif(NOT LIBCXX_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(LIBCXX_ROOT \"\")\n  set(LIBCXX_ROOT $ENV{LIBCXX_ROOT})\nendif()\n\n# Extract a reasonable place to look for the libraries from the compiler path\nstring(REGEX REPLACE \"/bin/clang\\\\+\\\\+\" \"\"\n    LIBCXX_PATH_FROM_COMPILER ${CMAKE_CXX_COMPILER})\n\n# If already in cache, be silent\nif(LIBCXX_INCLUDE_DIRS AND LIBCXX_LIBRARIES AND LIBCXXABI_LIBRARIES)\n  set (LIBCXX_FIND_QUIETLY TRUE)\nendif()\n\nfind_path(LIBCXX_INCLUDE_DIR NAMES cmath\n    HINTS ${LIBCXX_ROOT}/include/c++/v1\n    ${LIBCXX_PATH_FROM_COMPILER}/include/c++/v1\n    /usr/include/c++/v1)\n\nif(BUILD_SHARED_LIBS)\n  find_library(LIBCXX_LIBRARIES NAMES c++\n      HINTS ${LIBCXX_ROOT}/lib\n      ${LIBCXX_PATH_FROM_COMPILER}/lib)\n  find_library(LIBCXXABI_LIBRARIES NAMES c++abi\n      HINTS ${LIBCXX_ROOT}/lib\n      ${LIBCXX_PATH_FROM_COMPILER}/lib)\nelse()\n  find_library(LIBCXX_LIBRARIES NAMES libc++.a libc++.so\n      HINTS ${LIBCXX_ROOT}/lib\n      ${LIBCXX_PATH_FROM_COMPILER}/lib)\n  if(ARCH MATCHES \"ppc64\")\n    set(LIBCXXABI_LIBRARIES \" \")\n  else()\n    find_library(LIBCXXABI_LIBRARIES NAMES libc++abi.a libc++abi.so\n        HINTS ${LIBCXX_ROOT}/lib\n        ${LIBCXX_PATH_FROM_COMPILER}/lib)\n  endif()\nendif()\n\nset(LIBCXX_INCLUDE_DIRS ${LIBCXX_INCLUDE_DIR})\n\n# Handle the QUIETLY and REQUIRED arguments and set LIBCXX_FOUND to TRUE if\n# all listed variables are TRUE.\nINCLUDE(FindPackageHandleStandardArgs)\nFIND_PACKAGE_HANDLE_STANDARD_ARGS(\n    LIBCXX\n    DEFAULT_MSG\n    LIBCXX_LIBRARIES\n    LIBCXXABI_LIBRARIES\n    LIBCXX_INCLUDE_DIRS\n)\n\nMARK_AS_ADVANCED(LIBCXX_INCLUDE_DIRS LIBCXX_LIBRARIES LIBCXXABI_LIBRARIES)\n"
  },
  {
    "path": "cmake/FindLIBXSMM.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find LIBXSMM: https://github.com/libxsmm/libxsmm\n# If not in one of the default paths specify -D LIBXSMM_ROOT=/path/to/LIBXSMM\n# to search there as well.\n\nif(NOT LIBXSMM_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(LIBXSMM_ROOT \"\")\n  set(LIBXSMM_ROOT $ENV{LIBXSMM_ROOT})\nendif()\n\n# find the LIBXSMM include directory\nfind_path(LIBXSMM_INCLUDE_DIRS libxsmm.h\n  PATH_SUFFIXES include\n  HINTS ${LIBXSMM_ROOT})\n\nfind_library(LIBXSMM_LIBRARIES\n  NAMES xsmm\n  PATH_SUFFIXES lib64 lib\n  HINTS ${LIBXSMM_ROOT})\n\nset(LIBXSMM_VERSION \"\")\n\nfunction(get_libxsmm_version FILE MAJOR_PREFIX MINOR_PREFIX PATCH_PREFIX)\n  if(NOT EXISTS ${FILE})\n    return()\n  endif()\n  # Extract version info from header\n  file(READ ${FILE} LIBXSMM_FIND_HEADER_CONTENTS)\n\n  string(REGEX MATCH \"${MAJOR_PREFIX}[0-9]+\"\n    LIBXSMM_MAJOR_VERSION \"${LIBXSMM_FIND_HEADER_CONTENTS}\")\n  if(\"${LIBXSMM_MAJOR_VERSION}\" STREQUAL \"\")\n    return()\n  endif()\n\n  string(REPLACE \"${MAJOR_PREFIX}\" \"\" LIBXSMM_MAJOR_VERSION\n    \"${LIBXSMM_MAJOR_VERSION}\")\n\n  string(REGEX MATCH \"${MINOR_PREFIX}[0-9]+\"\n    LIBXSMM_MINOR_VERSION \"${LIBXSMM_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"${MINOR_PREFIX}\" \"\" LIBXSMM_MINOR_VERSION\n    \"${LIBXSMM_MINOR_VERSION}\")\n\n  string(REGEX MATCH \"${PATCH_PREFIX}[0-9]+\"\n    LIBXSMM_SUBMINOR_VERSION \"${LIBXSMM_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"${PATCH_PREFIX}\" \"\" LIBXSMM_SUBMINOR_VERSION\n    \"${LIBXSMM_SUBMINOR_VERSION}\")\n\n  set(LIBXSMM_VERSION\n    \"${LIBXSMM_MAJOR_VERSION}.${LIBXSMM_MINOR_VERSION}.${LIBXSMM_SUBMINOR_VERSION}\"\n    )\n  set(LIBXSMM_VERSION ${LIBXSMM_VERSION} PARENT_SCOPE)\nendfunction(get_libxsmm_version FILE MAJOR_REGEX MINOR_REGEX PATCH_REGEX)\n\nif (LIBXSMM_INCLUDE_DIRS)\n  get_libxsmm_version(\n    ${LIBXSMM_INCLUDE_DIRS}/libxsmm.h\n    \"#define LIBXSMM_VERSION_MAJOR \"\n    \"#define LIBXSMM_VERSION_MINOR \"\n    \"#define LIBXSMM_VERSION_UPDATE \"\n    )\n\n  get_libxsmm_version(\n    ${LIBXSMM_INCLUDE_DIRS}/libxsmm_config.h\n    \"#define LIBXSMM_CONFIG_VERSION_MAJOR \"\n    \"#define LIBXSMM_CONFIG_VERSION_MINOR \"\n    \"#define LIBXSMM_CONFIG_VERSION_UPDATE \"\n    )\n\n  get_libxsmm_version(\n    ${LIBXSMM_INCLUDE_DIRS}/libxsmm_version.h\n    \"#define LIBXSMM_CONFIG_VERSION_MAJOR \"\n    \"#define LIBXSMM_CONFIG_VERSION_MINOR \"\n    \"#define LIBXSMM_CONFIG_VERSION_UPDATE \"\n    )\n\n  if(\"${LIBXSMM_VERSION}\" STREQUAL \"\")\n    message(WARNING \"Failed to detect LIBXSMM version.\")\n  endif()\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  LIBXSMM\n  FOUND_VAR LIBXSMM_FOUND\n  REQUIRED_VARS LIBXSMM_INCLUDE_DIRS LIBXSMM_LIBRARIES\n  VERSION_VAR LIBXSMM_VERSION)\nmark_as_advanced(LIBXSMM_INCLUDE_DIRS LIBXSMM_LIBRARIES\n  LIBXSMM_VERSION)\n\nif (NOT LIBXSMM_FOUND)\n  return()\nendif()\n\n# Define imported target\nadd_library(Libxsmm INTERFACE IMPORTED)\nset_property(TARGET Libxsmm\n  APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LIBXSMM_INCLUDE_DIRS})\nset_property(TARGET Libxsmm\n  APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${LIBXSMM_LIBRARIES})\n# LIBXSMM falls back to BLAS\nfind_package(BLAS REQUIRED)\ntarget_link_libraries(Libxsmm INTERFACE BLAS::BLAS)\n\nadd_interface_lib_headers(\n  TARGET Libxsmm\n  HEADERS\n  libxsmm.h\n  )\n"
  },
  {
    "path": "cmake/FindNumPy.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nexecute_process(COMMAND ${CMAKE_COMMAND} -E env\n  PYTHONPATH=${PYTHONPATH} ${Python_EXECUTABLE} \"-c\"\n  \"import numpy as n; print(n.__version__); print(n.get_include());\"\n  RESULT_VARIABLE RESULT\n  OUTPUT_VARIABLE OUTPUT\n  OUTPUT_STRIP_TRAILING_WHITESPACE)\n\nif(RESULT MATCHES 0)\n  string(REGEX REPLACE \";\" \"\\\\\\\\;\" VALUES ${OUTPUT})\n  string(REGEX REPLACE \"\\r?\\n\" \";\" VALUES ${VALUES})\n  list(GET VALUES 0 NUMPY_VERSION)\n  list(GET VALUES 1 NUMPY_INCLUDE_DIRS)\n\n  string(REGEX MATCH \"^([0-9])+\\\\.([0-9])+\\\\.([0-9])+\" __ver_check \"${NUMPY_VERSION}\")\n  if(\"${__ver_check}\" STREQUAL \"\")\n   unset(NUMPY_VERSION)\n   unset(NUMPY_INCLUDE_DIRS)\n   message(STATUS \"Failed to retrieve NumPy version and include path, but got instead:\\n${OUTPUT}\\n\")\n  endif()\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(NumPy REQUIRED_VARS NUMPY_INCLUDE_DIRS NUMPY_VERSION\n                                        VERSION_VAR   NUMPY_VERSION)\n\nif(NUMPY_FOUND)\n  message(STATUS \"NumPy ver. ${NUMPY_VERSION} found (include: ${NUMPY_INCLUDE_DIRS})\")\nelse()\n  message(STATUS \"NumPy not found!\")\nendif()\n"
  },
  {
    "path": "cmake/FindPAPI.cmake",
    "content": "# Obtained from: https://github.com/LLNL/perf-dump\n\n# Try to find PAPI headers and libraries.\n#\n# Usage of this module as follows:\n#\n#     find_package(PAPI)\n#\n# Variables used by this module, they can change the default behaviour and need\n# to be set before calling find_package:\n#\n#  PAPI_PREFIX         Set this variable to the root installation of\n#                      libpapi if the module has problems finding the\n#                      proper installation path.\n#\n# Variables defined by this module:\n#\n#  PAPI_FOUND              System has PAPI libraries and headers\n#  PAPI_LIBRARIES          The PAPI library\n#  PAPI_INCLUDE_DIRS       The location of PAPI headers\n\nif(NOT PAPI_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(PAPI_ROOT \"\")\n  set(PAPI_ROOT $ENV{PAPI_ROOT})\nendif()\n\nfind_path(\n    PAPI_PREFIX\n    NAMES include/papi.h\n    HINTS ${PAPI_ROOT}\n)\n\nfind_library(\n    PAPI_LIBRARIES\n    # Pick the static library first for easier run-time linking.\n    NAMES libpapi.a papi\n    HINTS ${PAPI_PREFIX}/lib ${HILTIDEPS}/lib\n)\n\nfind_path(\n    PAPI_INCLUDE_DIRS\n    NAMES papi.h\n    HINTS ${PAPI_PREFIX}/include ${HILTIDEPS}/include\n)\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package_handle_standard_args(\n    PAPI\n    DEFAULT_MSG\n    PAPI_LIBRARIES\n    PAPI_INCLUDE_DIRS\n)\n\nmark_as_advanced(\n    PAPI_PREFIX_DIRS\n    PAPI_LIBRARIES\n    PAPI_INCLUDE_DIRS\n)\n"
  },
  {
    "path": "cmake/FindParaView.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_program(PVPYTHON_EXEC pvpython)\n\n# Get version and runtime environment variables\nif (PVPYTHON_EXEC)\n  execute_process(\n    COMMAND ${PVPYTHON_EXEC} --print --version\n    OUTPUT_VARIABLE _OUTPUT\n    ERROR_VARIABLE _OUTPUT\n    OUTPUT_STRIP_TRAILING_WHITESPACE\n    ERROR_STRIP_TRAILING_WHITESPACE)\n  string(REPLACE \"\\n\" \";\" _OUTPUT \"${_OUTPUT}\")\n  foreach(_ENTRY ${_OUTPUT})\n    # Extract the ParaView version from the output\n    string(FIND \"${_ENTRY}\" \"paraview version \" _FOUND)\n    if(NOT ${_FOUND} EQUAL -1)\n      set(PARAVIEW_VERSION ${_ENTRY})\n    endif()\n\n    # On some machines ParaView needs specific environment variables set, e.g.\n    # on CaltechHPC we need to set LD_LIBRARY_PATH. If other env variables\n    # need to be set, then we need to possibly update this.\n    string(FIND \"${_ENTRY}\" \"LD_LIBRARY_PATH=\" _FOUND)\n    if(NOT ${_FOUND} EQUAL -1)\n      set(PARAVIEW_PYTHON_ENV_VARS ${_ENTRY})\n    endif()\n  endforeach()\n  string(REPLACE \"paraview version \" \"\" PARAVIEW_VERSION \"${PARAVIEW_VERSION}\")\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(ParaView\n  REQUIRED_VARS PVPYTHON_EXEC\n  VERSION_VAR PARAVIEW_VERSION)\n\n# Get Python environment variables\nexecute_process(\n  COMMAND ${PVPYTHON_EXEC} -m paraview.inspect\n  OUTPUT_VARIABLE _OUTPUT\n  OUTPUT_STRIP_TRAILING_WHITESPACE\n  RESULT_VARIABLE _RESULT\n  ERROR_QUIET)\nif (_RESULT EQUAL 0)\n  string(REPLACE \"\\n\" \";\" _OUTPUT \"${_OUTPUT}\")\n  list(GET _OUTPUT 0 PARAVIEW_PYTHON_VERSION)\n  list(GET _OUTPUT 1 PARAVIEW_PYTHONPATH)\n  string(REPLACE \"version: \" \"\"\n    PARAVIEW_PYTHON_VERSION \"${PARAVIEW_PYTHON_VERSION}\")\n  string(REPLACE \"pythonpath entry: \" \"\"\n    PARAVIEW_PYTHONPATH \"${PARAVIEW_PYTHONPATH}\")\nelse()\n  # If for whatever reason pvpython call didn't work, try another search\n  get_filename_component(PVPYTHON_BINDIR ${PVPYTHON_EXEC} DIRECTORY)\n  get_filename_component(PVPYTHON_BASEDIR ${PVPYTHON_BINDIR} DIRECTORY)\n  file(GLOB_RECURSE\n    PARAVIEW_PYTHONPATH\n    ${PVPYTHON_BASEDIR}\n    \"${PVPYTHON_BASEDIR}/lib*/python*/site-packages/paraview/simple.py\"\n  )\n  find_file(PARAVIEW_PYTHONPATH\n    NAMES \"simple.py\"\n    PATHS \"${PVPYTHON_BASEDIR}\"\n    PATH_SUFFIXES \"/.*\"\n    REQUIRED)\n  # go back up to the site-packages\n  get_filename_component(PARAVIEW_PYTHONPATH ${PARAVIEW_PYTHONPATH} DIRECTORY)\n  get_filename_component(PARAVIEW_PYTHONPATH ${PARAVIEW_PYTHONPATH} DIRECTORY)\nendif()\n"
  },
  {
    "path": "cmake/FindPythonModule.cmake",
    "content": "# From: https://github.com/ivansafrin/Polycode/\n\n# Find if a Python module is installed\n# Found at http://www.cmake.org/pipermail/cmake/2011-January/041666.html\n# To use do: find_python_module(PyQt4 REQUIRED) # if required\n#        or: find_python_module(PyQt4) # if optional, check PY_PyQt4_FOUND\n#            if(PY_PyQt4_FOUND)\n#              # do stuff...\n#            endif()\nfunction(find_python_module module)\n  # Terminate early if the package has already been found\n  if(PY_${module}_FOUND)\n    return()\n  endif()\n  cmake_parse_arguments(ARG \"REQUIRED\" \"\" \"\" ${ARGN})\n  # Try to import the module and get its location, if it's not already cached\n  if(NOT PY_${module}_LOCATION)\n    # A module's location is usually a directory, but for binary modules\n    # it's a .so file.\n    execute_process(COMMAND ${CMAKE_COMMAND} -E env\n        PYTHONPATH=${PYTHONPATH} ${Python_EXECUTABLE} \"-c\"\n        \"import re, ${module}; print(re.compile('/__init__.py.*').sub('',${module}.__file__))\"\n        RESULT_VARIABLE _${module}_status\n        OUTPUT_VARIABLE _${module}_location\n        ERROR_QUIET\n        OUTPUT_STRIP_TRAILING_WHITESPACE)\n    if(_${module}_status EQUAL 0)\n      set(PY_${module}_LOCATION ${_${module}_location} CACHE STRING\n          \"Location of Python module ${module}\")\n    endif()\n  endif()\n  # Make `find_package_handle_standard_args` error if the package is not found\n  if(ARG_REQUIRED)\n    set(PY_${module}_FIND_REQUIRED TRUE)\n  endif()\n  find_package_handle_standard_args(PY_${module}\n    REQUIRED_VARS PY_${module}_LOCATION)\n  set(PY_${module}_FOUND ${PY_${module}_FOUND} PARENT_SCOPE)\nendfunction(find_python_module)\n"
  },
  {
    "path": "cmake/FindScotch.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find Scotch: https://www.labri.fr/perso/pelegrin/scotch/\n# If not in one of the default paths specify -D SCOTCH_ROOT=/path/to/Scotch\n# to search there as well.\n\nif(NOT SCOTCH_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(SCOTCH_ROOT \"\")\n  set(SCOTCH_ROOT $ENV{SCOTCH_ROOT})\nendif()\n\n# Find the Scotch include directory\nfind_path(SCOTCH_INCLUDE_DIR scotch.h\n  PATH_SUFFIXES include\n  HINTS ${SCOTCH_ROOT})\n\nfind_library(SCOTCH_LIB\n  NAMES scotch\n  PATH_SUFFIXES lib64 lib\n  HINTS ${SCOTCH_ROOT})\n\nfind_library(SCOTCH_ERR_LIB\n  NAMES scotcherr\n  PATH_SUFFIXES lib64 lib\n  HINTS ${SCOTCH_ROOT})\n\nset(SCOTCH_LIBRARIES \"${SCOTCH_LIB};${SCOTCH_ERR_LIB}\")\n\nset(SCOTCH_VERSION \"\")\n\nfile(READ \"${SCOTCH_INCLUDE_DIR}/scotch.h\" SCOTCH_FIND_HEADER_CONTENTS)\n\nset(SCOTCH_MAJOR_PREFIX \"#define SCOTCH_VERSION \")\nset(SCOTCH_MINOR_PREFIX \"#define SCOTCH_RELEASE \")\nset(SCOTCH_PATCH_PREFIX \"#define SCOTCH_PATCHLEVEL \")\n\nstring(REGEX MATCH \"${SCOTCH_MAJOR_PREFIX}[0-9]+\"\n  SCOTCH_MAJOR_VERSION \"${SCOTCH_FIND_HEADER_CONTENTS}\")\nstring(REPLACE \"${SCOTCH_MAJOR_PREFIX}\" \"\" SCOTCH_MAJOR_VERSION\n  \"${SCOTCH_MAJOR_VERSION}\")\n\nstring(REGEX MATCH \"${SCOTCH_MINOR_PREFIX}[0-9]+\"\n  SCOTCH_MINOR_VERSION \"${SCOTCH_FIND_HEADER_CONTENTS}\")\nstring(REPLACE \"${SCOTCH_MINOR_PREFIX}\" \"\" SCOTCH_MINOR_VERSION\n  \"${SCOTCH_MINOR_VERSION}\")\n\nstring(REGEX MATCH \"${SCOTCH_PATCH_PREFIX}[0-9]+\"\n  SCOTCH_SUBMINOR_VERSION \"${SCOTCH_FIND_HEADER_CONTENTS}\")\nstring(REPLACE \"${SCOTCH_PATCH_PREFIX}\" \"\" SCOTCH_SUBMINOR_VERSION\n  \"${SCOTCH_SUBMINOR_VERSION}\")\n\nset(SCOTCH_VERSION\n  \"${SCOTCH_MAJOR_VERSION}.${SCOTCH_MINOR_VERSION}.${SCOTCH_SUBMINOR_VERSION}\"\n  )\n\nset(Scotch_VERSION ${SCOTCH_VERSION})\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  Scotch\n  FOUND_VAR SCOTCH_FOUND\n  REQUIRED_VARS SCOTCH_INCLUDE_DIR SCOTCH_LIBRARIES\n  VERSION_VAR SCOTCH_VERSION)\nmark_as_advanced(SCOTCH_INCLUDE_DIR SCOTCH_LIBRARIES\n  SCOTCH_MAJOR_VERSION SCOTCH_MINOR_VERSION SCOTCH_PATCH_VERSION\n  SCOTCH_VERSION Scotch_VERSION)\n"
  },
  {
    "path": "cmake/FindSleef.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find Sleef: https://github.com/shibatch/sleef\n# If not in one of the default paths specify -D SLEEF_ROOT=/path/to/Sleef\n# to search there as well.\n\nif(NOT SLEEF_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(SLEEF_ROOT \"\")\n  set(SLEEF_ROOT $ENV{SLEEF_ROOT})\nendif()\n\n# find the SLEEF include directory\nfind_path(SLEEF_INCLUDE_DIR sleef.h\n  PATH_SUFFIXES include\n  HINTS ${SLEEF_ROOT})\n\nfind_library(SLEEF_LIBRARIES\n  NAMES sleef\n  PATH_SUFFIXES lib64 lib\n  HINTS ${SLEEF_ROOT})\n\nset(SLEEF_VERSION \"\")\n\nif (EXISTS \"${SLEEF_INCLUDE_DIR}/sleef.h\")\n\n  file(READ \"${SLEEF_INCLUDE_DIR}/sleef.h\" SLEEF_FIND_HEADER_CONTENTS)\n\n  set(SLEEF_MAJOR_PREFIX \"#define SLEEF_VERSION_MAJOR \")\n  set(SLEEF_MINOR_PREFIX \"#define SLEEF_VERSION_MINOR \")\n  set(SLEEF_PATCH_PREFIX \"#define SLEEF_VERSION_PATCHLEVEL \")\n\n  string(REGEX MATCH \"${SLEEF_MAJOR_PREFIX}[0-9]+\"\n    SLEEF_MAJOR_VERSION \"${SLEEF_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"${SLEEF_MAJOR_PREFIX}\" \"\" SLEEF_MAJOR_VERSION\n    \"${SLEEF_MAJOR_VERSION}\")\n\n  string(REGEX MATCH \"${SLEEF_MINOR_PREFIX}[0-9]+\"\n    SLEEF_MINOR_VERSION \"${SLEEF_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"${SLEEF_MINOR_PREFIX}\" \"\" SLEEF_MINOR_VERSION\n    \"${SLEEF_MINOR_VERSION}\")\n\n  string(REGEX MATCH \"${SLEEF_PATCH_PREFIX}[0-9]+\"\n    SLEEF_SUBMINOR_VERSION \"${SLEEF_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"${SLEEF_PATCH_PREFIX}\" \"\" SLEEF_SUBMINOR_VERSION\n    \"${SLEEF_SUBMINOR_VERSION}\")\n\n  set(SLEEF_VERSION\n    \"${SLEEF_MAJOR_VERSION}.${SLEEF_MINOR_VERSION}.${SLEEF_SUBMINOR_VERSION}\"\n    )\nendif()\n\nset(Sleef_VERSION ${SLEEF_VERSION})\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  Sleef\n  FOUND_VAR SLEEF_FOUND\n  REQUIRED_VARS SLEEF_INCLUDE_DIR SLEEF_LIBRARIES\n  VERSION_VAR SLEEF_VERSION)\nmark_as_advanced(SLEEF_INCLUDE_DIR SLEEF_LIBRARIES\n  SLEEF_MAJOR_VERSION SLEEF_MINOR_VERSION SLEEF_PATCH_VERSION\n  SLEEF_VERSION Sleef_VERSION)\n\nadd_library(Sleef INTERFACE IMPORTED)\nset_property(TARGET Sleef PROPERTY\n  INTERFACE_INCLUDE_DIRECTORIES ${SLEEF_INCLUDE_DIR})\nset_property(TARGET Sleef\n  APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${SLEEF_LIBRARIES})\n\nadd_interface_lib_headers(\n  TARGET Sleef\n  HEADERS\n  sleef.h\n  )\n"
  },
  {
    "path": "cmake/FindSpEC.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Optionally link SpEC libraries. Pass `SPEC_ROOT` to the CMake build\n# configuration to set up the following targets:\n#\n# - SpEC::Exporter: Functionality to load SpEC volume data and interpolate to\n#   arbitrary points.\n\nif(NOT SPEC_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(SPEC_ROOT \"\")\n  set(SPEC_ROOT $ENV{SPEC_ROOT})\nendif()\n\nif (SPEC_ROOT)\n  set(SPEC_EXPORTER_ROOT ${SPEC_ROOT}/Support/ApplyObservers/Exporter)\nelse()\n  set(SPEC_EXPORTER_ROOT \"\")\nendif()\n\nfind_library(\n  SPEC_PACKAGED_EXPORTER_LIB\n  NAMES libPackagedExporter.a\n  PATHS ${SPEC_EXPORTER_ROOT}\n  NO_DEFAULT_PATHS\n  )\nfind_file(\n  SPEC_EXPORTER_FACTORY_OBJECTS\n  NAMES ExporterFactoryObjects.o\n  PATHS ${SPEC_EXPORTER_ROOT}\n  NO_DEFAULT_PATHS\n  )\nfind_path(\n  SPEC_EXPORTER_INCLUDE_DIR\n  NAMES Exporter.hpp\n  PATHS ${SPEC_EXPORTER_ROOT}\n  NO_DEFAULT_PATHS\n  )\n\n# SpEC needs MPI.\n# NOTE: You should use the same MPI as SpEC. At least the same distribution. So\n# mixing OpenMPI and MPICH would be bad.\nfind_package(MPI COMPONENTS C)\n\nif (SPEC_PACKAGED_EXPORTER_LIB AND SPEC_EXPORTER_FACTORY_OBJECTS AND\n    SPEC_EXPORTER_INCLUDE_DIR AND MPI_C_FOUND)\n  add_library(SpEC::Exporter INTERFACE IMPORTED)\n  target_include_directories(\n    SpEC::Exporter INTERFACE ${SPEC_EXPORTER_INCLUDE_DIR})\n  add_interface_lib_headers(\n    TARGET SpEC::Exporter\n    HEADERS\n    Exporter.hpp\n  )\n  target_link_libraries(\n    SpEC::Exporter\n    INTERFACE\n    MPI::MPI_C\n    # The order of these next two lines is important\n    ${SPEC_EXPORTER_FACTORY_OBJECTS}\n    ${SPEC_PACKAGED_EXPORTER_LIB}\n  )\n\n  # Deal with FFTW3\n  #\n  # If it was dynamically linked into SpEC then we need to dynamically link it\n  # into SpECTRE.\n  set(_MACHINE_DEF_FILE\n    ${SPEC_ROOT}/MakefileRules/this_machine.def)\n  if (EXISTS ${_MACHINE_DEF_FILE})\n    file(STRINGS ${_MACHINE_DEF_FILE} _MACHINE_DEF)\n    # Filter starting comments\n    list(FILTER _MACHINE_DEF EXCLUDE REGEX \"^[ ]*#\")\n    # Remove inline comments\n    list(TRANSFORM _MACHINE_DEF REPLACE \"[ ]*#.*\" \"\")\n    string(REGEX MATCH \"FFTW_LIB[^;]+\"\n      _FFTW3_DEF \"${_MACHINE_DEF}\")\n    string(FIND ${_FFTW3_DEF} \"-lfftw3\" _FOUND_FFTW3_SHARED)\n    # If we found a shared FFTW lib in SpEC, find and link FFTW here\n    if(NOT ${_FOUND_FFTW3_SHARED} STREQUAL \"-1\")\n      find_package(FFTW REQUIRED)\n      target_link_libraries(SpEC::Exporter INTERFACE FFTW::FFTW)\n    endif()\n  endif()\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  SpEC\n  REQUIRED_VARS\n  SPEC_PACKAGED_EXPORTER_LIB\n  SPEC_EXPORTER_FACTORY_OBJECTS\n  SPEC_EXPORTER_INCLUDE_DIR\n  )\n"
  },
  {
    "path": "cmake/FindTCMALLOC.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find tcmalloc: https://github.com/gperftools/gperftools\n# If not in one of the default paths specify -D TCMALLOC_ROOT=/path/to/tcmalloc\n# to search there as well.\n\nif(NOT TCMALLOC_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(TCMALLOC_ROOT \"\")\n  set(TCMALLOC_ROOT $ENV{TCMALLOC_ROOT})\nendif()\n\n# find the tcmalloc include directory\nfind_path(TCMALLOC_INCLUDE_DIRS gperftools/tcmalloc.h\n  PATH_SUFFIXES include\n  HINTS ${TCMALLOC_ROOT})\n\nfind_library(TCMALLOC_LIBRARIES\n  NAMES tcmalloc\n  PATH_SUFFIXES lib64 lib\n  HINTS ${TCMALLOC_ROOT})\n\nset(TCMALLOC_VERSION \"\")\n\nif(EXISTS \"${TCMALLOC_INCLUDE_DIRS}/gperftools/tcmalloc.h\")\n  # Extract version info from header\n  file(READ\n    \"${TCMALLOC_INCLUDE_DIRS}/gperftools/tcmalloc.h\"\n    TCMALLOC_FIND_HEADER_CONTENTS)\n\n  string(REGEX MATCH \"#define TC_VERSION_MAJOR [0-9]+\"\n    TCMALLOC_MAJOR_VERSION \"${TCMALLOC_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"#define TC_VERSION_MAJOR \" \"\"\n    TCMALLOC_MAJOR_VERSION\n    \"${TCMALLOC_MAJOR_VERSION}\")\n\n  string(REGEX MATCH \"#define TC_VERSION_MINOR [0-9]+\"\n    TCMALLOC_MINOR_VERSION \"${TCMALLOC_FIND_HEADER_CONTENTS}\")\n  string(REPLACE \"#define TC_VERSION_MINOR \" \"\"\n    TCMALLOC_MINOR_VERSION\n    \"${TCMALLOC_MINOR_VERSION}\")\n\n\n  set(TCMALLOC_VERSION\n    \"${TCMALLOC_MAJOR_VERSION}.${TCMALLOC_MINOR_VERSION}\"\n    )\nelse()\n  message(WARNING \"Failed to find file \"\n    \"'${TCMALLOC_INCLUDE_DIRS}/gperftools/tcmalloc.h' \"\n    \"while detecting the TCMALLOC version.\")\nendif(EXISTS \"${TCMALLOC_INCLUDE_DIRS}/gperftools/tcmalloc.h\")\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  TCMALLOC\n  FOUND_VAR TCMALLOC_FOUND\n  REQUIRED_VARS TCMALLOC_INCLUDE_DIRS TCMALLOC_LIBRARIES\n  VERSION_VAR TCMALLOC_VERSION\n  )\nmark_as_advanced(TCMALLOC_INCLUDE_DIRS TCMALLOC_LIBRARIES\n  TCMALLOC_MAJOR_VERSION TCMALLOC_MINOR_VERSION\n  TCMALLOC_VERSION)\n"
  },
  {
    "path": "cmake/Findccache.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_program(CCACHE_EXEC ccache)\n\nif (CCACHE_EXEC)\n  # Get version\n  execute_process(COMMAND ${CCACHE_EXEC} --version\n    OUTPUT_VARIABLE CCACHE_VERSION\n    OUTPUT_STRIP_TRAILING_WHITESPACE\n  )\n  # Keep only first line of output\n  string(REGEX REPLACE \"\\n.*\" \"\" CCACHE_VERSION ${CCACHE_VERSION})\n  # Remove \"ccache version \" prefix\n  string(REGEX REPLACE \"ccache version \" \"\" CCACHE_VERSION ${CCACHE_VERSION})\nendif()\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  ccache REQUIRED_VARS CCACHE_EXEC VERSION_VAR CCACHE_VERSION\n  )\nmark_as_advanced(CCACHE_VERSION)\n"
  },
  {
    "path": "cmake/PrintUsefulCMakeInfo.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# First append useful info to BuildInfo.txt\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"CMake version: ${CMAKE_VERSION}\\n\"\n  \"CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}\\n\"\n  \"CMAKE_CXX_LINK_FLAGS: ${CMAKE_CXX_LINK_FLAGS}\\n\"\n  \"CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}\\n\"\n  \"CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}\\n\"\n  \"CMAKE_C_FLAGS: ${CMAKE_C_FLAGS}\\n\"\n  \"CMAKE_C_FLAGS_DEBUG: ${CMAKE_C_FLAGS_DEBUG}\\n\"\n  \"CMAKE_C_FLAGS_RELEASE: ${CMAKE_C_FLAGS_RELEASE}\\n\"\n  \"CMAKE_Fortran_FLAGS: ${CMAKE_Fortran_FLAGS}\\n\"\n  \"CMAKE_Fortran_FLAGS_DEBUG: ${CMAKE_Fortran_FLAGS_DEBUG}\\n\"\n  \"CMAKE_Fortran_FLAGS_RELEASE: ${CMAKE_Fortran_FLAGS_RELEASE}\\n\"\n  \"CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}\\n\"\n  \"CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\\n\"\n  \"CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}\\n\"\n  \"CMAKE_CXX_COMPILER_VERSION: ${CMAKE_CXX_COMPILER_VERSION}\\n\"\n  \"CMAKE_C_COMPILER: ${CMAKE_C_COMPILER}\\n\"\n  \"CMAKE_C_COMPILER_VERSION: ${CMAKE_C_COMPILER_VERSION}\\n\"\n  \"CMAKE_Fortran_COMPILER: ${CMAKE_Fortran_COMPILER}\\n\"\n  \"CMAKE_Fortran_COMPILER_VERSION: ${CMAKE_Fortran_COMPILER_VERSION}\\n\"\n  \"Python version: ${Python_VERSION}\\n\"\n  )\n\n# Then write (slightly expanded) useful info to command line\nmessage(STATUS \"\\nUseful Information:\")\nmessage(STATUS \"Git description: ${GIT_DESCRIPTION}\")\nmessage(STATUS \"Git branch: ${GIT_BRANCH}\")\nmessage(STATUS \"Git hash: ${GIT_HASH}\")\nmessage(STATUS \"Build directory: ${CMAKE_BINARY_DIR}\")\nmessage(STATUS \"Source directory: ${CMAKE_SOURCE_DIR}\")\nmessage(STATUS \"Bin directory: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}\")\nmessage(STATUS \"CMake modules path: ${CMAKE_MODULE_PATH}\")\nmessage(STATUS \"CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}\")\nmessage(STATUS \"CMAKE_CXX_LINK_FLAGS: ${CMAKE_CXX_LINK_FLAGS}\")\nmessage(STATUS \"CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}\")\nmessage(STATUS \"CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}\")\nmessage(STATUS \"CMAKE_EXE_LINKER_FLAGS: ${CMAKE_EXE_LINKER_FLAGS}\")\nmessage(STATUS \"CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\")\nmessage(STATUS \"CMAKE_CXX_COMPILER: ${CMAKE_CXX_COMPILER}\")\nmessage(STATUS \"BUILD_SHARED_LIBS: ${BUILD_SHARED_LIBS}\")\nmessage(STATUS \"USE_PCH: ${USE_PCH}\")\nmessage(STATUS \"ASAN: ${ASAN}\")\nmessage(STATUS \"UBSAN_UNDEFINED: ${UBSAN_UNDEFINED}\")\nmessage(STATUS \"UBSAN_INTEGER: ${UBSAN_INTEGER}\")\n\nif (Python_FOUND)\n  message(STATUS \"Python: ${Python_EXECUTABLE}\")\n  message(STATUS \"Python version: ${Python_VERSION}\")\nelse()\n  message(STATUS \"Python: Not found\")\nendif()\nmessage(STATUS \"BUILD_PYTHON_BINDINGS: ${BUILD_PYTHON_BINDINGS}\")\n\nif(CLANG_TIDY_BIN)\n  message(STATUS \"Found clang-tidy: ${CLANG_TIDY_BIN}\")\nelseif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  message(\n      STATUS\n      \"Could not find clang-tidy even though LLVM clang is installed\"\n  )\nendif()\n\nif (CODE_COVERAGE)\n  message(STATUS \"Code coverage enabled. All prerequisites found:\")\n  message(STATUS \"  gcov: ${GCOV}\")\n  message(STATUS \"  lcov: ${LCOV}\")\n  message(STATUS \"  genhtml: ${GENHTML}\")\n  message(STATUS \"  sed: ${SED}\")\nendif()\n\nif(DOXYGEN_FOUND)\n  message(STATUS \"Doxygen: ${DOXYGEN_EXECUTABLE}\")\nelse()\n  message(STATUS \"Doxygen: Not found, documentation cannot be built.\")\nendif()\n"
  },
  {
    "path": "cmake/ProhibitInSourceBuild.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nstring(\n    REGEX REPLACE\n    \"^${CMAKE_SOURCE_DIR}/([^/]*)/?.*$\" \"\\\\1\"\n    SUBDIR_OF_SOURCE_DIR ${CMAKE_BINARY_DIR}\n)\nset(PROHIBITED_SUBDIRS \"cmake\" \"containers\" \"docs\" \"external\" \"src\"\n    \"support\" \"tests\" \"tools\")\n\nif (\"${CMAKE_BINARY_DIR}\" STREQUAL \"${CMAKE_SOURCE_DIR}\" OR\n    ${SUBDIR_OF_SOURCE_DIR} IN_LIST PROHIBITED_SUBDIRS)\n  message(FATAL_ERROR \"\\n\"\n      \"You attempted to build ${PROJECT_NAME} in the directory:\\n\"\n      \"  ${CMAKE_BINARY_DIR}\\n\"\n      \"In-source builds, however, are not allowed. \"\n      \"Please create a directory and run cmake from there, passing the path \"\n      \"to the source directory as the last argument; for example:\\n\"\n      \"  cd ${CMAKE_SOURCE_DIR}\\n\"\n      \"  mkdir build\\n\"\n      \"  cd build\\n\"\n      \"  cmake [OPTIONS] ${CMAKE_SOURCE_DIR}\\n\"\n      \"You also need to remove the CMakeCache.txt file and the \"\n      \"CMakeFiles directory in the source directory, or you will trigger \"\n      \"this error again, even when doing an out-of-source build. Run:\\n\"\n      \"  rm -r ${CMAKE_BINARY_DIR}/CMakeCache.txt \"\n      \"${CMAKE_BINARY_DIR}/CMakeFiles\\n\"\n  )\nendif()\n"
  },
  {
    "path": "cmake/RunInputFileTest.sh",
    "content": "#!/bin/sh -e\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Positional arguments to this script:\n# - $1: executable name\n# - $2: path to input file\n# - $3: directory name\n# - $4: space-separated list of expected exit codes\n# - $5: \"true\" to check output files or \"false\" to skip the check\n# - $6: \"true\" to check output files are present or \"false\" to skip\n# - $7: additional command-line arguments forwarded to the executable\n# - $8: extra files to copy\n\n# Set up test directory\ntest_dir=@CMAKE_BINARY_DIR@/tests/InputFiles/$3\nrm -rf $test_dir\nmkdir -p $test_dir\ncd $test_dir\n\ninput_file=$2\ncheck_output_values=$5\ncheck_output_present=$6\n\ninput_dir=`dirname $input_file`\nfor file in $8;\ndo\n    cp $input_dir/$file $test_dir\ndone\n\n# Run the executable\nrestart=\nfor expected_code in $4 ; do\n    if [ -z \"$restart\" ] ; then\n        @SPECTRE_TEST_RUNNER@ @CMAKE_BINARY_DIR@/bin/$1 --input-file \\\n                            $input_file $7\n        exit_code=$?\n        restart=0\n    else\n        if [ $exit_code -ne 2 ] ; then\n            echo \"Must restart after exit code 2\" >&2\n            exit 1\n        fi\n        @SPECTRE_TEST_RUNNER@ @CMAKE_BINARY_DIR@/bin/$1 \\\n            +restart Checkpoints/Checkpoint_$(printf %04d $restart)\n        exit_code=$?\n        restart=$(expr $restart + 1)\n    fi\n    if [ $exit_code -ne $expected_code ]; then\n        echo \"ERROR: Exited with ${exit_code} instead of ${expected_code}\" >&2\n        exit 1\n    fi\ndone\n\n# Check output and clean up\nif [ \"$check_output_values\" = \"true\" ]; then\n    @Python_EXECUTABLE@ @CMAKE_SOURCE_DIR@/tools/CheckOutputFiles.py \\\n        --input-file $input_file --run-directory $test_dir \\\n        --cmake-source-directory @CMAKE_SOURCE_DIR@ \\\n        --cmake-bin-directory @CMAKE_BINARY_DIR@ \\\n        || exit 1\nfi\nif [ \"$check_output_present\" = \"true\" ]; then\n    @Python_EXECUTABLE@ -m spectre.tools.CleanOutput \\\n                      --output-dir $test_dir $input_file \\\n        || exit 1\nfi\nrm -rf $test_dir\n"
  },
  {
    "path": "cmake/SetBuildType.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# CMake lets the user define CMAKE_BUILD_TYPE on the command line and recognizes\n# the values \"Debug\", \"Release\", \"RelWithDebInfo\", and \"MinSizeRel\", which add\n# specific compiler or linker flags.  CMake's default behavior is to add no\n# additional compiler or linker flags if the user does not define\n# CMAKE_BUILD_TYPE on the command line, or passes an unrecognized value.\n# We add a sanity check that checks if CMAKE_BUILD_TYPE is one of the recognized\n# values, and we also set the CMAKE_BUILD_TYPE to \"Debug\" if the user does not\n# specify it on the command line.  In addition, we add \"None\" as a valid value\n# for CMAKE_BUILD_TYPE whose behavior is to add no additional compiler or linker\n# flags. This is done by defining the following flags analagous to those used by\n# the other build types. Additional build types can be defined in a similar\n# manner by defining the appropriate flags, and adding the name of the build\n# type to CMAKE_BUILD_TYPES below.\nset(CMAKE_CXX_FLAGS_NONE \"\" CACHE STRING\n  \"Additional flags used by the compiler for Build type None.\"\n  FORCE)\n\nset(CMAKE_C_FLAGS_NONE \"\" CACHE STRING\n  \"Additional flags used by the compiler for Build type None.\"\n  FORCE)\n\nset(CMAKE_EXE_LINKER_FLAGS_NONE \"\" CACHE STRING\n  \"Additional flags used by the linker for Build type None.\"\n  FORCE)\n\nset(CMAKE_Fortran_FLAGS_NONE \"\" CACHE STRING\n  \"Additional flags used by the compiler for Build type None.\"\n  FORCE)\n\nset(CMAKE_MODULE_LINKER_FLAGS_NONE \"\" CACHE STRING\n  \"Additional flags used by the linker for Build type None.\"\n  FORCE)\n\nset(CMAKE_SHARED_LINKER_FLAGS_NONE \"\" CACHE STRING\n  \"Additional flags used by the linker for Build type None.\"\n  FORCE)\n\nset(CMAKE_STATIC_LINKER_FLAGS_NONE \"\" CACHE STRING\n  \"Additional flags used by the linker for Build type None.\"\n  FORCE)\n\nmark_as_advanced(\n  CMAKE_CXX_FLAGS_NONE\n  CMAKE_C_FLAGS_NONE\n  CMAKE_EXE_LINKER_FLAGS_NONE\n  CMAKE_Fortran_FLAGS_NONE\n  CMAKE_MODULE_LINKER_FLAGS_NONE\n  CMAKE_SHARED_LINKER_FLAGS_NONE\n  CMAKE_STATIC_LINKER_FLAGS_NONE\n  )\n\nset(CMAKE_BUILD_TYPES \"Debug\" \"Release\" \"None\" \"RelWithDebInfo\" \"MinSizeRel\")\n\nif (NOT CMAKE_BUILD_TYPE)\n  message(STATUS \"CMAKE_BUILD_TYPE not specified, setting to 'Debug'\")\n  set(\n      CMAKE_BUILD_TYPE Debug\n      CACHE STRING \"Choose the type of build: ${CMAKE_BUILD_TYPES}\"\n      FORCE\n  )\nelse()\n  if(NOT ${CMAKE_BUILD_TYPE} IN_LIST CMAKE_BUILD_TYPES)\n    message(\n        FATAL_ERROR \"\\n\"\n        \"Invalid value for CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\\n\"\n        \"Valid values: ${CMAKE_BUILD_TYPES}\\n\"\n    )\n  endif()\n  message(STATUS \"CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}\")\nendif()\n"
  },
  {
    "path": "cmake/SetCxxStandard.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\nset(CMAKE_CXX_EXTENSIONS OFF)\n"
  },
  {
    "path": "cmake/SetOutputDirectory.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Set the directory where the libraries and executables are placed\n# The default can be overridden by specifying\n# `-D CMAKE_RUNTIME_OUTPUT_DIRECTORY=/path/`\nif (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)\n  set(\n      CMAKE_RUNTIME_OUTPUT_DIRECTORY\n      \"${CMAKE_BINARY_DIR}/bin/\"\n      CACHE STRING \"Choose the directory where executables are placed\" FORCE\n  )\nendif (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)\n\nif (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)\n  set(\n    CMAKE_LIBRARY_OUTPUT_DIRECTORY\n    \"${CMAKE_BINARY_DIR}/lib/\"\n    CACHE STRING \"Choose the directory where shared libraries are placed\" FORCE\n    )\nendif (NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)\n\nif (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)\n  set(\n    CMAKE_ARCHIVE_OUTPUT_DIRECTORY\n    \"${CMAKE_BINARY_DIR}/lib/\"\n    CACHE STRING \"Choose the directory where static libraries are placed\" FORCE\n    )\nendif (NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)\n"
  },
  {
    "path": "cmake/SetupAllocator.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Always create the target and global property so that we don't need to\n# special case elsewhere in the code.\nset_property(GLOBAL\n  PROPERTY\n  SPECTRE_ALLOCATOR_LIBRARY\n  \"\")\n\nadd_library(SpectreAllocator INTERFACE)\n\n# At some point we had issues with jemalloc/tcmalloc when using the sanitizers.\n# If this becomes an issue again then we can re-add the if(NOT ASAN) block\n# around the allocator setup.\n\noption(MEMORY_ALLOCATOR\n  \"Which allocator to use: SYSTEM, TCMALLOC, JEMALLOC (default)\"\n  OFF)\n\nset(JEMALLOC_LIB_TYPE \"\")\n\n# We need to link custom allocators before we link anything else so that\n# any third-party libraries, which generally should all be built as shared\n# libraries, use the allocator that we use. Unfortunately, how exactly\n# CMake decides on the linking order is not clear when using\n# INTERFACE_LINK_LIBRARIES and targets. To this end, we set a global\n# property SPECTRE_ALLOCATOR_LIBRARY that contains the link flag to link\n# to the memory allocator. By linking to the allocator library first\n# explicitly in target_link_libraries CMake correctly places the allocator\n# library as the first entry in the link libraries. We also link to the\n# SpectreAllocator target to pull in any additional allocator-related\n# flags, such as include directories.\n#\n# Targets can grab the link flags using:\n#\n#   get_property(\n#     SPECTRE_ALLOCATOR_LIBRARY\n#     GLOBAL\n#     PROPERTY SPECTRE_ALLOCATOR_LIBRARY\n#     )\n#   target_link_libraries(${TARGET_NAME}\n#     PUBLIC\n#     ${SPECTRE_ALLOCATOR_LIBRARY}\n#     SpectreAllocator\n#     )\n#\n# These need to be the first call to target_link_libraries of the target.\nif(\"${MEMORY_ALLOCATOR}\" STREQUAL \"JEMALLOC\"\n    OR \"${MEMORY_ALLOCATOR}\" STREQUAL \"OFF\")\n  include(SetupJemalloc)\n  target_link_libraries(\n    SpectreAllocator\n    INTERFACE\n    Jemalloc\n    )\n  get_property(\n    SPECTRE_ALLOCATOR_LIBRARY\n    TARGET Jemalloc\n    PROPERTY INTERFACE_LINK_LIBRARIES\n    )\n  set_property(GLOBAL\n    PROPERTY\n    SPECTRE_ALLOCATOR_LIBRARY\n    ${SPECTRE_ALLOCATOR_LIBRARY})\nelseif(\"${MEMORY_ALLOCATOR}\" STREQUAL \"TCMALLOC\")\n  include(SetupTcmalloc)\n  target_link_libraries(\n    SpectreAllocator\n    INTERFACE\n    Tcmalloc\n    )\n  get_property(\n    SPECTRE_ALLOCATOR_LIBRARY\n    TARGET Tcmalloc\n    PROPERTY INTERFACE_LINK_LIBRARIES\n    )\n  set_property(GLOBAL\n    PROPERTY\n    SPECTRE_ALLOCATOR_LIBRARY\n    ${SPECTRE_ALLOCATOR_LIBRARY})\nelseif(NOT \"${MEMORY_ALLOCATOR}\" STREQUAL \"SYSTEM\")\n  message(FATAL_ERROR\n    \"Unknown memory allocator specified '${MEMORY_ALLOCATOR}'. \"\n    \"Known options are:\\n\"\n    \"  SYSTEM, TCMALLOC, JEMALLOC (default)\")\nelse()\n  message(STATUS \"Using system default memory allocator.\")\nendif()\n"
  },
  {
    "path": "cmake/SetupAutodiff.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Find autodiff (https://github.com/autodiff/autodiff)\n#\n# Note that Boost also has an autodiff module, but it uses templates to\n# distinguish multiple variables. This means a std::array or Tensor can't hold\n# the Boost autodiff variables. For example, to evaluate a 2D function f(x, y),\n# the type for x would be fvar<double> and the type for y would be the nested\n# fvar<fvar<double>>. On the other hand, the autodiff library works with a\n# simpler autodiff::dual or autodiff::var type, which can be stored in a\n# std::array or Tensor.\n\noption(SPECTRE_AUTODIFF \"Enable automatic differentiation\" OFF)\n\nif (NOT SPECTRE_AUTODIFF)\n  return()\nendif()\n\n# we assume the found autodiff is newer than the GIT_TAG below,\n# which has not been in autodiff's official release.\nfind_package(autodiff QUIET)\n\nif (NOT autodiff_FOUND)\n  if (NOT SPECTRE_FETCH_MISSING_DEPS)\n    message(FATAL_ERROR \"Could not find autodiff. If you want to fetch \"\n      \"missing dependencies automatically, set SPECTRE_FETCH_MISSING_DEPS=ON.\")\n  endif()\n\n  message(STATUS \"Fetching autodiff\")\n  include(FetchContent)\n  FetchContent_Declare(autodiff\n      GIT_REPOSITORY https://github.com/autodiff/autodiff\n      # Choose an unreleased version on top of v1.1.2 that makes dependence on\n      # Eigen optional.\n      GIT_TAG cc2aa5726fdbb258d097f87b97da3d1022f8394e\n      ${SPECTRE_FETCHCONTENT_BASE_ARGS}\n  )\n  set(AUTODIFF_BUILD_TESTS OFF CACHE BOOL \"Build autodiff tests\")\n  set(AUTODIFF_BUILD_EXAMPLES OFF CACHE BOOL \"Build autodiff examples\")\n  set(AUTODIFF_BUILD_PYTHON OFF CACHE BOOL \"Build autodiff Python bindings\")\n  set(AUTODIFF_BUILD_DOCS OFF CACHE BOOL \"Build autodiff documentation\")\n  FetchContent_MakeAvailable(autodiff)\n\n  if (CMAKE_VERSION VERSION_LESS 3.25)\n    get_target_property(AUTODIFF_IID autodiff INTERFACE_INCLUDE_DIRECTORIES)\n    set_target_properties(autodiff PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES \"${AUTODIFF_IID}\")\n  endif()\nendif()\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  autodiff::autodiff\n  )\n"
  },
  {
    "path": "cmake/SetupBlas.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(BLAS REQUIRED)\nmessage(STATUS \"BLAS libs: \" ${BLAS_LIBRARIES})\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"BLAS_LIBRARIES: ${BLAS_LIBRARIES}\\n\"\n  )\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  BLAS::BLAS\n  )\n\n# Check if we have found OpenBLAS and can disable its multithreading, since it\n# conflicts with Charm++ parallelism. Details:\n# https://github.com/xianyi/OpenBLAS/wiki/Faq#multi-threaded\n# We use `execute_process` instead of `try_compile` to avoid potentially slow\n# disk IO.\nset(\n  CHECK_DISABLE_OPENBLAS_MULTITHREADING_SOURCE\n  \"extern \\\"C\\\" { void openblas_set_num_threads(int); }\\n\\\nint main() { openblas_set_num_threads(1); }\"\n  )\nstring(REPLACE \";\" \" \" BLAS_LIBRARIES_JOINED_WITH_SPACES \"${BLAS_LIBRARIES}\")\nexecute_process(\n  COMMAND\n  bash -c\n  \"${CMAKE_CXX_COMPILER} ${BLAS_LIBRARIES_JOINED_WITH_SPACES} -x c++ - <<< $'\\\n${CHECK_DISABLE_OPENBLAS_MULTITHREADING_SOURCE}' -o /dev/null\"\n  RESULT_VARIABLE CHECK_DISABLE_OPENBLAS_MULTITHREADING_RESULT\n  ERROR_VARIABLE CHECK_DISABLE_OPENBLAS_MULTITHREADING_ERROR\n  OUTPUT_QUIET\n  )\nif(${CHECK_DISABLE_OPENBLAS_MULTITHREADING_RESULT} EQUAL 0)\n  set(DISABLE_OPENBLAS_MULTITHREADING ON)\n  add_definitions(-DDISABLE_OPENBLAS_MULTITHREADING)\n  message(STATUS \"Disabled OpenBLAS multithreading\")\nelse()\n  message(STATUS \"BLAS vendor is probably not OpenBLAS. Make sure it doesn't \"\n    \"try to do multithreading that might conflict with Charm++ parallelism.\")\nendif()\n"
  },
  {
    "path": "cmake/SetupBlaze.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(USE_SLEEF \"Use Sleef to add more vectorized instructions.\" OFF)\n\nif(USE_SLEEF)\n  # Try to find Sleef to increase vectorization\n  find_package(Sleef)\nendif()\n\nif(SLEEF_FOUND)\n  message(STATUS \"Sleef libs: ${SLEEF_LIBRARIES}\")\n  message(STATUS \"Sleef incl: ${SLEEF_INCLUDE_DIR}\")\n  message(STATUS \"Sleef vers: ${SLEEF_VERSION}\")\n\n  file(APPEND\n    \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n    \"Sleef version: ${SLEEF_VERSION}\\n\"\n  )\nendif()\n\n# Every time we've upgraded blaze compatibility in the past, we've had to change\n# vector code, so we should expect to need changes again on each subsequent\n# release, so we should specify an exact version requirement. However, Blaze\n# hasn't been consistent in naming releases (version 3.8.2 has 3.9.0 written\n# in Version.h).\nfind_package(Blaze 3.8)\n\nif (NOT Blaze_FOUND)\n  if (NOT SPECTRE_FETCH_MISSING_DEPS)\n    message(FATAL_ERROR \"Could not find Blaze. If you want to fetch \"\n      \"missing dependencies automatically, set SPECTRE_FETCH_MISSING_DEPS=ON.\")\n  endif()\n  message(STATUS \"Fetching Blaze\")\n  include(FetchContent)\n  FetchContent_Declare(Blaze\n    URL https://bitbucket.org/blaze-lib/blaze/downloads/blaze-3.8.2.tar.gz\n    ${SPECTRE_FETCHCONTENT_BASE_ARGS}\n  )\n  # Configure Blaze CMake variables. Most configuration is done below.\n  set(BLAZE_SHARED_MEMORY_PARALLELIZATION 0 CACHE INTERNAL \"Blaze SMP mode\")\n  FetchContent_MakeAvailable(Blaze)\n  set(BLAZE_INCLUDE_DIR ${blaze_SOURCE_DIR})\n  set(BLAZE_VERSION \"3.8.2\")\nendif()\n\nmessage(STATUS \"Blaze incl: ${BLAZE_INCLUDE_DIR}\")\nmessage(STATUS \"Blaze vers: ${BLAZE_VERSION}\")\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"Blaze version: ${BLAZE_VERSION}\\n\"\n  )\n\nfind_package(BLAS REQUIRED)\nfind_package(GSL REQUIRED)\nfind_package(LAPACK REQUIRED)\n\nadd_library(Blaze INTERFACE IMPORTED)\nset_property(TARGET Blaze PROPERTY\n  INTERFACE_INCLUDE_DIRECTORIES ${BLAZE_INCLUDE_DIR})\ntarget_link_libraries(\n  Blaze\n  INTERFACE\n  BLAS::BLAS\n  GSL::gsl # for BLAS header\n  LAPACK::LAPACK\n  )\nset(_BLAZE_USE_SLEEF 0)\n\nif(SLEEF_FOUND)\n  target_link_libraries(\n    Blaze\n    INTERFACE\n    Sleef\n    )\n  set_property(\n    GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n    Sleef\n    )\n  set(_BLAZE_USE_SLEEF 1)\nendif()\n\n# If BLAZE_USE_STRONG_INLINE=ON, Blaze will use this keyword to increase the\n# likelihood of inlining. If BLAZE_USE_STRONG_INLINE=OFF, uses inline keyword\n# as a fallback.\noption(BLAZE_USE_STRONG_INLINE \"Increase likelihood of Blaze inlining.\" ON)\n\nset(_BLAZE_USE_STRONG_INLINE 0)\n\nif(BLAZE_USE_STRONG_INLINE)\n  set(_BLAZE_USE_STRONG_INLINE 1)\nendif()\n\n# If BLAZE_USE_ALWAYS_INLINE=ON, Blaze will use this keyword to force inlining.\n# If BLAZE_USE_ALWAYS_INLINE=OFF or if the platform being used cannot 100%\n# guarantee inlining, uses BLAZE_STRONG_INLINE as a fallback.\noption(BLAZE_USE_ALWAYS_INLINE \"Force Blaze inlining.\" ON)\n\nset(_BLAZE_USE_ALWAYS_INLINE 0)\n\nif(BLAZE_USE_ALWAYS_INLINE)\n  set(_BLAZE_USE_ALWAYS_INLINE 1)\nendif()\n\n# Configure Blaze. Some of the Blaze configuration options could be optimized\n# for the machine we are running on. See documentation:\n# https://bitbucket.org/blaze-lib/blaze/wiki/Configuration%20and%20Installation#!step-2-configuration\ntarget_compile_definitions(Blaze\n  INTERFACE\n  # - Enable external BLAS kernels\n  BLAZE_BLAS_MODE=1\n  # - Use BLAS header from GSL. We could also find and include a <cblas.h> (or\n  #   similarly named) header that may be distributed with the BLAS\n  #   implementation, but it's not guaranteed to be available and may conflict\n  #   with the GSL header. Since we use GSL anyway, it's easier to use their\n  #   BLAS header.\n  BLAZE_BLAS_INCLUDE_FILE=<gsl/gsl_cblas.h>\n  # - Set default matrix storage order to column-major, since many of our\n  #   functions are implemented for column-major layout. This default reduces\n  #   conversions.\n  BLAZE_DEFAULT_STORAGE_ORDER=blaze::columnMajor\n  # - Disable SMP parallelization. This disables SMP parallelization for all\n  #   possible backends (OpenMP, C++11 threads, Boost, HPX):\n  #   https://bitbucket.org/blaze-lib/blaze/wiki/Serial%20Execution#!option-3-deactivation-of-parallel-execution\n  BLAZE_USE_SHARED_MEMORY_PARALLELIZATION=0\n  # - Disable MPI parallelization\n  BLAZE_MPI_PARALLEL_MODE=0\n  # - Using the default cache size, which may have been configured automatically\n  #   by the Blaze CMake configuration for the machine we are running on. We\n  #   could override it here explicitly to tune performance.\n  # BLAZE_CACHE_SIZE\n  # - Disable padding for dynamic matrices.\n  #   Blaze warns that this may decrease performance:\n  #   https://bitbucket.org/blaze-lib/blaze/src/c4d9e85414370e880e5e79c86e3c8d4d38dcde7a/blaze/config/Optimizations.h#lines-52\n  #   We haven't tested this much, so we may want to try enabling padding again.\n  #   To support padding, explicit calls to LAPACK functions need to pass\n  #   `.spacing()` instead of `.rows()/.columns()` to the `LDA`, `LDB`, etc.\n  #   parameters (see `[matrix_spacing]` in `Test_Spectral.cpp`).\n  BLAZE_USE_PADDING=0\n  # - Always enable non-temporal stores for cache optimization of large data\n  #   structures: https://bitbucket.org/blaze-lib/blaze/wiki/Configuration%20Files#!streaming-non-temporal-stores\n  BLAZE_USE_STREAMING=1\n  # - Skip initializing default-constructed structures for fundamental types\n  BLAZE_USE_DEFAULT_INITIALIZATON=0\n  # Use Sleef for vectorization of more math functions\n  BLAZE_USE_SLEEF=${_BLAZE_USE_SLEEF}\n  # Set inlining settings\n  BLAZE_USE_STRONG_INLINE=${_BLAZE_USE_STRONG_INLINE}\n  BLAZE_USE_ALWAYS_INLINE=${_BLAZE_USE_ALWAYS_INLINE}\n  )\n\n# We need to make sure `BlazeExceptions.hpp` is included. It is included in the\n# PCH (see tools/SpectrePch.hpp). If there's no PCH, we need to include it here.\nif (NOT USE_PCH)\n  target_compile_options(Blaze\n    INTERFACE\n    \"$<$<COMPILE_LANGUAGE:CXX>:SHELL:-include Utilities/BlazeExceptions.hpp>\")\nendif()\n\nadd_interface_lib_headers(\n  TARGET Blaze\n  HEADERS\n  blaze/math/CustomVector.h\n  blaze/math/DynamicMatrix.h\n  blaze/math/DynamicVector.h\n  blaze/system/Optimizations.h\n  blaze/system/Version.h\n  blaze/util/typetraits/RemoveConst.h\n  )\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  Blaze\n  )\n"
  },
  {
    "path": "cmake/SetupBoost.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(Boost 1.60.0 REQUIRED COMPONENTS program_options)\n\n# CMake versions don't set this consistently\nset(Boost_VERSION \"${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}\")\n\nmessage(STATUS \"Boost libraries: ${Boost_LIBRARIES}\")\nmessage(STATUS \"Boost include: ${Boost_INCLUDE_DIRS}\")\nmessage(STATUS \"Boost version: ${Boost_VERSION}\")\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"Boost version: ${Boost_VERSION}\\n\"\n  )\n\n# Boost organizes targets as:\n# - Boost::boost is the header-only parts of Boost\n# - Boost::COMPONENT are the components that need linking, e.g. program_options\n\nadd_interface_lib_headers(\n  TARGET Boost::boost\n  HEADERS\n  boost/algorithm/string.hpp\n  boost/config.hpp\n  boost/core/demangle.hpp\n  boost/functional/hash.hpp\n  boost/integer/common_factor_rt.hpp\n  boost/iterator/transform_iterator.hpp\n  boost/iterator/zip_iterator.hpp\n  boost/make_shared.hpp\n  boost/math/interpolators/barycentric_rational.hpp\n  boost/math/special_functions/binomial.hpp\n  boost/math/tools/roots.hpp\n  boost/multi_array.hpp\n  boost/multi_array/base.hpp\n  boost/multi_array/extent_gen.hpp\n  boost/numeric/odeint.hpp\n  boost/numeric/odeint/integrate/integrate_adaptive.hpp>\n  boost/numeric/odeint/stepper/controlled_runge_kutta.hpp\n  boost/numeric/odeint/stepper/dense_output_runge_kutta.hpp\n  boost/numeric/odeint/stepper/generation/make_dense_output.hpp\n  boost/numeric/odeint/stepper/runge_kutta_dopri5.hpp\n  boost/parameter/name.hpp\n  boost/preprocessor.hpp\n  boost/preprocessor/arithmetic/dec.hpp\n  boost/preprocessor/arithmetic/inc.hpp\n  boost/preprocessor/arithmetic/sub.hpp\n  boost/preprocessor/control/expr_iif.hpp\n  boost/preprocessor/control/iif.hpp\n  boost/preprocessor/control/while.hpp\n  boost/preprocessor/list/adt.hpp\n  boost/preprocessor/list/fold_left.hpp\n  boost/preprocessor/list/fold_right.hpp\n  boost/preprocessor/list/for_each.hpp\n  boost/preprocessor/list/for_each_product.hpp\n  boost/preprocessor/list/size.hpp\n  boost/preprocessor/list/to_tuple.hpp\n  boost/preprocessor/list/transform.hpp\n  boost/preprocessor/logical/bitand.hpp\n  boost/preprocessor/logical/bool.hpp\n  boost/preprocessor/logical/compl.hpp\n  boost/preprocessor/punctuation/comma_if.hpp\n  boost/preprocessor/repetition/for.hpp\n  boost/preprocessor/repetition/repeat.hpp\n  boost/preprocessor/tuple/elem.hpp\n  boost/preprocessor/tuple/enum.hpp\n  boost/preprocessor/tuple/reverse.hpp\n  boost/preprocessor/tuple/size.hpp\n  boost/preprocessor/tuple/to_list.hpp\n  boost/preprocessor/variadic/elem.hpp\n  boost/preprocessor/variadic/to_list.hpp\n  boost/range/combine.hpp\n  boost/shared_ptr.hpp\n  boost/tuple/tuple.hpp\n  boost/tuple/tuple_comparison.hpp\n  boost/variant.hpp\n  boost/variant/get.hpp\n  boost/variant/variant.hpp\n  )\n\nset_property(\n  TARGET Boost::program_options\n  APPEND PROPERTY PUBLIC_HEADER\n  boost/program_options.hpp\n  )\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  Boost::boost Boost::program_options\n  )\n\n# We disable thread safety of Boost::shared_ptr since it makes them faster\n# to use and we do not share them between threads. If a thread-safe\n# shared_ptr is desired it must be implemented to work with Charm++'s threads\n# anyway.\nset_property(TARGET Boost::boost\n  APPEND PROPERTY\n  INTERFACE_COMPILE_DEFINITIONS\n  $<$<COMPILE_LANGUAGE:CXX>:BOOST_SP_DISABLE_THREADS>\n)\n\n# Some older versions of boost don't play nice with new versions of Clang and\n# GCC-12. We can just blanket enable the BOOST_NO_CXX98_FUNCTION_BASE macro,\n# which is what boost does anyway with newer versions.\nset_property(TARGET Boost::boost\n  APPEND PROPERTY\n  INTERFACE_COMPILE_DEFINITIONS\n  $<$<COMPILE_LANGUAGE:CXX>:BOOST_NO_CXX98_FUNCTION_BASE>\n)\n\n# With newer versions of boost, sometimes there are internal boost warnings\n# about deprecated headers. This disables those as we have no control over what\n# boost does internally.\nset_property(TARGET Boost::boost\n  APPEND PROPERTY\n  INTERFACE_COMPILE_DEFINITIONS\n  $<$<COMPILE_LANGUAGE:CXX>:BOOST_ALLOW_DEPRECATED_HEADERS>)\n\n# Old versions of boost only enabled variadic macros for known compilers.\n# This changed in boost 1.75.0, where variadic macros are always enabled.\n# We enable this here manually for older versions of boost so the code compiles\n# with nvcc.\nif(Boost_VERSION VERSION_LESS 1.75.0)\n  set_property(TARGET Boost::boost\n    APPEND PROPERTY\n    INTERFACE_COMPILE_DEFINITIONS\n    $<$<COMPILE_LANGUAGE:CXX>:BOOST_PP_VARIADICS=1>)\nendif()\n\n# Override the boost index type to match the STL for Boost.MultiArray\n# (std::ptrdiff_t to std::size_t)\n# Note: This header guard changed in Boost 1.73.0\nif(Boost_VERSION VERSION_GREATER_EQUAL 1.73.0)\n  set(BOOST_MULTI_ARRAY_TYPES_HEADER_GUARD BOOST_MULTI_ARRAY_TYPES_HPP)\nelse()\n  set(BOOST_MULTI_ARRAY_TYPES_HEADER_GUARD BOOST_MULTI_ARRAY_TYPES_RG071801_HPP)\nendif()\nset_property(TARGET Boost::boost\n  APPEND PROPERTY\n  INTERFACE_COMPILE_DEFINITIONS\n  $<$<COMPILE_LANGUAGE:CXX>:${BOOST_MULTI_ARRAY_TYPES_HEADER_GUARD}>)\n\n# Work around boost not building with clang 15 (fixed in boost 1.83.0)\n# (https://github.com/boostorg/functional/pull/21)\nif (CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" AND BOOST_VERSION VERSION_LESS 1.83.0)\n  if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER \"15.0.0\" OR\n      CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL \"15.0.0\")\n        target_compile_definitions(Boost::boost INTERFACE _HAS_AUTO_PTR_ETC=0)\n  endif()\nendif()\n\n"
  },
  {
    "path": "cmake/SetupCCache.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Use CCache if available to speed up builds\n#\n# CCache is particularly useful for speeding up builds on CI, where we can share\n# the cache between runs. It is also very useful for general development.\n#\n# To support precompiled headers (PCH) we follow the recommendations in the\n# ccache docs:\n# https://ccache.dev/manual/latest.html#_precompiled_headers\n# Our requirements for CCache with PCH are:\n# - Works with GCC and Clang.\n# - Deleting and recreating the build directory retains ~100% cache hit rate.\n# - `make clean` retains ~100% cache hit rate.\n# - Changing the PCH file in tools/SpectrePch.hpp invalidates the cache.\n# - Sharing caches between runs on CI works.\n\noption(USE_CCACHE \"Use CCache if available to speed up builds\" ON)\n\nset(CCACHE_LAUNCHER_EXTRA_ENV_VARS \"\" CACHE STRING \"Env vars for ccache\")\n\nif(NOT USE_CCACHE)\n  return()\nendif()\n\nfind_package(ccache)\n\nif (NOT CCACHE_EXEC)\n  return()\nendif()\n\nexecute_process(COMMAND realpath ${CCACHE_EXEC}\n                OUTPUT_STRIP_TRAILING_WHITESPACE\n                OUTPUT_VARIABLE REAL_CCACHE_EXEC)\n\n# Configure ccache with environment variables\nset(_CCACHE_LAUNCHER_ENV_VARS\n  ${CCACHE_LAUNCHER_EXTRA_ENV_VARS}\n  \"CCACHE_SLOPPINESS=pch_defines,time_macros,include_file_mtime,\\\ninclude_file_ctime\")\n# The `locale` sloppiness is only available in ccache 3.6 and later\nif (CCACHE_VERSION VERSION_GREATER_EQUAL 3.6)\n  string(APPEND _CCACHE_LAUNCHER_ENV_VARS \",locale\")\nendif()\n\n# Invoke compiler through ccache\nset(CMAKE_CXX_COMPILER_LAUNCHER\n  ${_CCACHE_LAUNCHER_ENV_VARS} ${REAL_CCACHE_EXEC})\nset(CMAKE_C_COMPILER_LAUNCHER ${CMAKE_CXX_COMPILER_LAUNCHER})\nmessage(STATUS \"Using ccache for compilation. It is invoked as: \"\n  \"${CMAKE_CXX_COMPILER_LAUNCHER}\")\n\n# Add `-fno-pch-timestamp` flag to Clang to support precompiled headers\n# (see https://ccache.dev/manual/4.8.2.html#_precompiled_headers)\n# Note that `CMAKE_CXX_COMPILE_OPTIONS_CREATE_PCH` isn't part of the public API\n# so this may break in the future. For a discussion see:\n# https://discourse.cmake.org/t/ccache-clang-and-fno-pch-timestamp/7253/6\nif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  list(APPEND CMAKE_CXX_COMPILE_OPTIONS_CREATE_PCH -Xclang -fno-pch-timestamp)\nendif()\n"
  },
  {
    "path": "cmake/SetupCatch.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(Catch2 3.4.0)\n\nif (NOT Catch2_FOUND)\n  if (NOT SPECTRE_FETCH_MISSING_DEPS)\n    message(FATAL_ERROR \"Could not find Catch2. If you want to fetch \"\n      \"missing dependencies automatically, set SPECTRE_FETCH_MISSING_DEPS=ON.\")\n  endif()\n  message(STATUS \"Fetching Catch2\")\n  include(FetchContent)\n  FetchContent_Declare(Catch2\n    GIT_REPOSITORY https://github.com/catchorg/Catch2.git\n    GIT_TAG v3.4.0\n    GIT_SHALLOW TRUE\n    ${SPECTRE_FETCHCONTENT_BASE_ARGS}\n  )\n  FetchContent_MakeAvailable(Catch2)\nendif()\n"
  },
  {
    "path": "cmake/SetupCharm.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(SPECTRE_REQUIRED_CHARM_VERSION 7.0.0)\n\noption(USE_SCOTCH_LB \"Use the charm++ ScotchLB module\" OFF)\n\nset(SCOTCHLB_COMPONENT \"\")\nif (USE_SCOTCH_LB)\n  find_package(Scotch REQUIRED)\n\n  message(STATUS \"Scotch libs: \" ${SCOTCH_LIBRARIES})\n  message(STATUS \"Scotch incl: \" ${SCOTCH_INCLUDE_DIR})\n  message(STATUS \"Scotch vers: \" ${SCOTCH_VERSION})\n\n  file(APPEND\n    \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n    \"Scotch version: ${SCOTCH_VERSION}\\n\"\n    )\n\n  add_library(Scotch INTERFACE IMPORTED)\n  set_property(TARGET Scotch\n    APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SCOTCH_INCLUDE_DIR})\n  set_property(TARGET Scotch\n    APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${SCOTCH_LIBRARIES})\n\n  add_interface_lib_headers(\n    TARGET Scotch\n    HEADERS\n    scotch.h\n    )\n\n  set(SCOTCHLB_COMPONENT ScotchLB)\nendif()\n\nfind_package(Charm ${SPECTRE_REQUIRED_CHARM_VERSION} REQUIRED\n  COMPONENTS\n  EveryLB\n  ${SCOTCHLB_COMPONENT}\n  )\nif(CHARM_VERSION VERSION_LESS 8.0.0)\n  message(NOTICE \"Charm++ versions less than 8.0.0 have known bugs with \\\nelement creation.  Dynamic h-refinement is disabled.\")\nendif()\n\nif (USE_SCOTCH_LB)\n  target_link_libraries(Charmxx::charmxx INTERFACE Scotch)\nendif()\n\nif(CHARM_TRACE_PROJECTIONS OR CHARM_TRACE_PROJECTIONS)\n  set_property(TARGET SpectreFlags\n    APPEND PROPERTY\n    INTERFACE_COMPILE_OPTIONS\n    -DSPECTRE_CHARM_PROJECTIONS\n    -DSPECTRE_CHARM_NON_ACTION_WALLTIME_EVENT_ID=1000\n    )\nendif()\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"Charm++ version: ${CHARM_VERSION}\\n\"\n  \"CHARM_COMPILER: ${CHARM_COMPILER}\\n\"\n  \"CHARM_SHARED_LIBS: ${CHARM_SHARED_LIBS}\\n\"\n  \"CHARM_BUILDING_BLOCKS:\\n${CHARM_BUILDING_BLOCKS}\\n\"\n  )\n\nadd_interface_lib_headers(\n  TARGET\n  Charmxx::charmxx\n  HEADERS\n  charm++.h\n  )\nadd_interface_lib_headers(\n  TARGET\n  Charmxx::pup\n  HEADERS\n  pup.h\n  pup_stl.h\n  )\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  Charmxx::charmxx Charmxx::pup\n  )\n\nget_filename_component(CHARM_BINDIR ${CHARM_COMPILER} DIRECTORY)\n# In order to avoid problems when compiling in parallel we manually copy the\n# charmrun script over, rather than having charmc do it for us.\nconfigure_file(\n    \"${CHARM_BINDIR}/charmrun\"\n    \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/charmrun\" COPYONLY\n)\n\ninclude(SetupCharmModuleFunctions)\n\n# Make sure Charm++ was patched. If not you can get thread local storage errors\n# when loading the python bindings.\nif(NOT APPLE AND CHARM_VERSION VERSION_EQUAL 7.0.0 AND BUILD_PYTHON_BINDINGS)\n  # Check that the patch was applied:\n  set(CHARM_CHECK_FILE \"${CHARM_INCLUDE_DIR}/conv-mach-opt.sh\")\n  if (EXISTS ${CHARM_CHECK_FILE})\n    set(_CHARM_CMAKE_FILE_TO_CHECK ${CHARM_CHECK_FILE})\n    file(READ ${_CHARM_CMAKE_FILE_TO_CHECK} FILE_CONTENTS)\n    set(_EXPECTED_TLS_STRING \"-ftls-model=initial-exec\")\n    string(FIND \"${FILE_CONTENTS}\"\n      ${_EXPECTED_TLS_STRING} _LOCATION_OF_TLS)\n    if (NOT ${_LOCATION_OF_TLS} EQUAL -1)\n      message(FATAL_ERROR \"Found -ftls-model=initial-exec flag. \"\n        \"It looks like you forgot to apply the Charm++ patch. \"\n        \"This is necessary when using the python bindings.\")\n    endif()\n  else()\n    message(STATUS \"Unable to check if Charm++ was patched. \"\n      \"Missing file ${CHARM_CHECK_FILE}\")\n  endif()\nendif()\n"
  },
  {
    "path": "cmake/SetupCharmModuleFunctions.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Add a function to generate the charm interface files for the module.\nfunction(add_charm_module MODULE)\n  # Arguments:\n  #   MODULE: Name of the Charm++ module.\n\n  if (NOT TARGET module_All)\n    # Target that will have all the Charm modules as dependencies\n    add_custom_target(module_All)\n  endif()\n\n  set(_VERSION_SUFFIX \"_v${CHARM_VERSION}\")\n\n  add_custom_command(\n    OUTPUT ${MODULE}.decl.h ${MODULE}.def.h\n    DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE}.ci\n    COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE}.ci ${MODULE}.ci\n    COMMAND ${CHARM_COMPILER} -no-charmrun ${MODULE}.ci\n    COMMAND ${CMAKE_SOURCE_DIR}/tools/patch_charm_modules.sh ${MODULE} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR} `pwd` ${_VERSION_SUFFIX}\n    )\n  add_custom_target(\n    module_${MODULE}\n    DEPENDS ${MODULE}.decl.h ${MODULE}.def.h\n    )\n  add_dependencies(\n    module_All\n    module_${MODULE}\n    )\nendfunction()\n"
  },
  {
    "path": "cmake/SetupClangFormat.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(ClangFormat)\n\nif(CLANG_FORMAT_BIN AND EXISTS ${CMAKE_SOURCE_DIR}/.git AND Git_FOUND)\n  get_filename_component(CLANG_FORMAT_NAME ${CLANG_FORMAT_BIN} NAME)\n  add_custom_target(\n      git-clang-format\n      COMMAND\n      export PATH=${CMAKE_SOURCE_DIR}/tools:$$PATH\n      && cd ${CMAKE_SOURCE_DIR}\n      && ${GIT_EXECUTABLE} ${CLANG_FORMAT_NAME} -f\n  )\nendif()\n"
  },
  {
    "path": "cmake/SetupClangTidy.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif(NOT CLANG_TIDY_ROOT)\n  # Need to set to empty to avoid warnings with --warn-uninitialized\n  set(CLANG_TIDY_ROOT \"\")\n  set(CLANG_TIDY_ROOT $ENV{CLANG_TIDY_ROOT})\nendif()\n\nif(NOT CMAKE_CXX_CLANG_TIDY AND CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  string(\n      REGEX MATCH \"^[0-9]+.[0-9]+\" LLVM_VERSION\n      \"${CMAKE_CXX_COMPILER_VERSION}\"\n  )\n  find_program(\n      CLANG_TIDY_BIN\n      NAMES \"clang-tidy-${LLVM_VERSION}\" \"clang-tidy\"\n      HINTS ${CLANG_TIDY_ROOT}\n      )\nelseif(CMAKE_CXX_CLANG_TIDY)\n  set(CLANG_TIDY_BIN \"${CMAKE_CXX_CLANG_TIDY}\")\nendif()\n\nif (CLANG_TIDY_BIN)\n  message(STATUS \"clang-tidy: ${CLANG_TIDY_BIN}\")\n  set(MODULES_TO_DEPEND_ON\n    module_All\n    Libsharp\n    )\n  if (TARGET SpectrePch)\n    list(APPEND MODULES_TO_DEPEND_ON SpectrePch)\n  endif()\n  configure_file(\n    ${CMAKE_SOURCE_DIR}/tools/ClangTidyAll.sh\n    ${CMAKE_BINARY_DIR}/ClangTidyAll.sh\n    @ONLY IMMEDIATE\n    )\n  add_custom_target(\n      clang-tidy\n      COMMAND\n      ${CLANG_TIDY_BIN}\n      --quiet\n      -p ${CMAKE_BINARY_DIR}\n      \\${FILE}\n  )\n  add_dependencies(\n    clang-tidy\n    ${MODULES_TO_DEPEND_ON}\n    )\n  add_custom_target(\n      clang-tidy-all\n      COMMAND ${CMAKE_BINARY_DIR}/ClangTidyAll.sh\n      \\${NUM_THREADS}\n  )\n  add_dependencies(\n    clang-tidy-all\n    ${MODULES_TO_DEPEND_ON}\n  )\n  add_custom_target(\n      clang-tidy-hash\n      COMMAND ${CMAKE_SOURCE_DIR}/tools/ClangTidyHash.sh\n      ${CMAKE_BINARY_DIR}\n      ${CMAKE_SOURCE_DIR}\n      \\${HASH}\n      \\${NUM_THREADS}\n  )\n  add_dependencies(\n    clang-tidy-hash\n    ${MODULES_TO_DEPEND_ON}\n  )\nelse()\n  message(STATUS \"clang-tidy: Not using clang or couldn't find clang-tidy.\")\nendif()\n"
  },
  {
    "path": "cmake/SetupCxxFlags.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(DEBUG_SYMBOLS \"Add -g to CMAKE_CXX_FLAGS if ON, -g0 if OFF.\" ON)\n\noption(OVERRIDE_ARCH \"The architecture to use. Default is native.\" OFF)\n\noption(SPECTRE_DEBUG \"Enable ASSERTs and other SPECTRE_DEBUG options\"\n  OFF)\n\n# Because of a bug in macOS on Apple Silicon, executables larger than 2GB in\n# size cannot run. This option minimizes executable size to avoid this bug. It\n# is enabled by default on Apple Silicon.\nset(_SPECTRE_OPTIMIZE_SIZE_DEFAULT OFF)\nif(APPLE AND \"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL \"arm64\")\n  set(_SPECTRE_OPTIMIZE_SIZE_DEFAULT ON)\nendif()\noption(SPECTRE_OPTIMIZE_SIZE \"Optimize for executable size instead of speed\"\n  ${_SPECTRE_OPTIMIZE_SIZE_DEFAULT})\n\noption(SPECTRE_DEBUG_Og \"Compile Debug builds with -Og instead of -O0\" OFF)\n\nif (CMAKE_BUILD_TYPE STREQUAL \"Debug\")\n  set(SPECTRE_DEBUG ON)\nendif()\n\nif(${SPECTRE_DEBUG})\n  set_property(TARGET SpectreFlags\n    APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS SPECTRE_DEBUG)\nendif()\n\noption(SPECTRE_NAN_INIT \"Initialize memory to NaN is various places\"\n  ${SPECTRE_DEBUG})\n\nif(${SPECTRE_NAN_INIT})\n  set_property(TARGET SpectreFlags\n    APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS SPECTRE_NAN_INIT)\nendif()\n\nset(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} -DSPECTRE_DEBUG\")\n\nif(${SPECTRE_OPTIMIZE_SIZE})\n  set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} -Oz\")\nelseif(${SPECTRE_DEBUG_Og})\n  set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} -Og\")\n  set(CMAKE_C_FLAGS_DEBUG \"${CMAKE_C_FLAGS_DEBUG} -Og\")\n  set(CMAKE_Fortran_FLAGS_DEBUG \"${CMAKE_Fortran_FLAGS_DEBUG} -Og\")\nendif()\n\nif(NOT ${DEBUG_SYMBOLS})\n  string(REPLACE \"-g \" \"-g0 \" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})\nendif()\n\n# Always build with -g so we can view backtraces, etc. when production code\n# fails. This can be overridden by passing `-D DEBUG_SYMBOLS=OFF` to CMake\nif(${DEBUG_SYMBOLS})\n  set_property(TARGET SpectreFlags\n    APPEND PROPERTY INTERFACE_COMPILE_OPTIONS -g)\nendif(${DEBUG_SYMBOLS})\n\n# Always compile only for the current architecture. This can be overridden\n# by passing `-D OVERRIDE_ARCH=THE_ARCHITECTURE` to CMake\nif(NOT \"${OVERRIDE_ARCH}\" STREQUAL \"OFF\")\n  set_property(TARGET SpectreFlags\n      APPEND PROPERTY\n      INTERFACE_COMPILE_OPTIONS\n      # The -mno-avx512f flag is necessary to avoid a Blaze 3.8 bug. The flag\n      # should be re-enabled when we can insist on Blaze 3.9 which will include\n      # a fix that allows this vectorization flag again.\n      $<$<COMPILE_LANGUAGE:C>:-march=${OVERRIDE_ARCH} -mno-avx512f>\n      $<$<COMPILE_LANGUAGE:CXX>:-march=${OVERRIDE_ARCH} -mno-avx512f>\n      $<$<COMPILE_LANGUAGE:Fortran>:-march=${OVERRIDE_ARCH} -mno-avx512f>)\nelse()\n  # Apple Silicon Macs do not support the -march flag or the -mno-avx512f flag\n  if((NOT APPLE OR NOT \"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL \"arm64\")\n    # sometimes ARM architectures use the name \"aarch64\"\n    AND NOT \"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL \"aarch64\"\n    )\n    set_property(TARGET SpectreFlags\n        APPEND PROPERTY\n        INTERFACE_COMPILE_OPTIONS\n        $<$<COMPILE_LANGUAGE:C>:-march=native -mno-avx512f>\n        $<$<COMPILE_LANGUAGE:CXX>:-march=native -mno-avx512f>\n        $<$<COMPILE_LANGUAGE:Fortran>:-march=native -mno-avx512f>)\n  endif()\nendif()\n\n# We are getting multiple types of linker warnings on macOS:\n# - \"ranlib: archive library: tmp/libSpectrePchLib.a the table of contents is\n#   empty (no object file members in the library define global symbols)\":\n#   Yes, some of our libs have no symbols. Doesn't seem like a problem.\n# - \"-undefined dynamic_lookup may not work with chained fixups\":\n#   This warning appears when compiling Python bindings. Chained fixups were\n#   introduced in AppleClang 13 and enabled by default in macOS 12. See these\n#   upstream issues:\n#   - CPython: https://github.com/python/cpython/issues/97524\n#   - Pybind11: https://github.com/pybind/pybind11/pull/4301\n#   - CMake: https://gitlab.kitware.com/cmake/cmake/-/issues/24044\n#   Disabling chained fixups with `-Wl-no_fixup_chains` leads to linker warnings\n#   about inconsistent visibility settings in different translation units. We\n#   probably have to wait for an upstream solution to this issue.\n# - \"could not create compact unwind for SYMBOL: registers X and\n#   Y not saved contiguously in frame\":\n#   We have seen these warnings on Apple Silicon chips.\n#   Disabling compact unwind with the flags\n#     -Wl,-keep_dwarf_unwind\n#     -Wl,-no_compact_unwind\n#   seems to work on some machines, but leads to segfaults on others. We haven't\n#   investigated this in any more detail.\n# For now we just suppress these linker warnings altogether, since we haven't\n# encountered any problems with them and some are upstream issues.\nif(APPLE)\n  target_link_options(\n    SpectreFlags\n    INTERFACE\n    -Wl,-w\n    )\nendif()\n\n# We always want a detailed backtrace of template errors to make debugging them\n# easier\nset_property(TARGET SpectreFlags\n  APPEND PROPERTY\n  INTERFACE_COMPILE_OPTIONS\n  $<$<COMPILE_LANGUAGE:CXX>:-ftemplate-backtrace-limit=0>)\n\n# Increase bracket depth for fold expressions\n# (see also https://github.com/llvm/llvm-project/issues/48973)\nif (CMAKE_CXX_COMPILER_ID MATCHES \"Clang\"\n    AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0)\n  set_property(TARGET SpectreFlags\n    APPEND PROPERTY\n    INTERFACE_COMPILE_OPTIONS\n    $<$<COMPILE_LANGUAGE:CXX>:-fbracket-depth=1024>)\nendif()\n\n# Disable cmath setting the error flag. This allows the compiler to more\n# aggressively vectorize code since it doesn't need to respect some global\n# state.\nset_property(TARGET SpectreFlags\n  APPEND PROPERTY\n  INTERFACE_COMPILE_OPTIONS\n  $<$<COMPILE_LANGUAGE:C>:-fno-math-errno>\n  $<$<COMPILE_LANGUAGE:CXX>:-fno-math-errno>\n  $<$<COMPILE_LANGUAGE:Fortran>:-fno-math-errno>)\n\n# Allow the compiler to transform divisions into multiplication by the\n# reciprocal.\nset_property(TARGET SpectreFlags\n  APPEND PROPERTY\n  INTERFACE_COMPILE_OPTIONS\n  $<$<COMPILE_LANGUAGE:C>:-freciprocal-math>\n  $<$<COMPILE_LANGUAGE:CXX>:-freciprocal-math>\n  $<$<COMPILE_LANGUAGE:Fortran>:-freciprocal-math>)\n\n# -ffp-exception-behavior=maytrap - By default, the LLVM optimizer assumes\n#     floating point exceptions are ignored.\n# -fnon-call-exceptions - By default, GCC does not allow signal handlers to\n#     throw exceptions.\n# Note: -ffp-exception-behavior is not supported on ARM64 architectures\nif((NOT APPLE OR NOT \"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL \"arm64\")\n  AND NOT \"${CMAKE_HOST_SYSTEM_PROCESSOR}\" STREQUAL \"aarch64\"\n  )\n  create_c_flags_target(\n    \"-ffp-exception-behavior=maytrap;-fnon-call-exceptions\"\n    SpectreFpExceptions\n    )\n  create_cxx_flags_target(\n    \"-ffp-exception-behavior=maytrap;-fnon-call-exceptions\"\n    SpectreFpExceptions\n    )\n  target_link_libraries(\n    SpectreFlags\n    INTERFACE\n    SpectreFpExceptions\n    )\nelse()\n  # On ARM64, we can't trap FP exceptions, so just enable non-call exceptions\n  create_c_flags_target(\n    \"-fnon-call-exceptions\"\n    SpectreFpExceptions\n    )\n  create_cxx_flags_target(\n    \"-fnon-call-exceptions\"\n    SpectreFpExceptions\n    )\n  target_link_libraries(\n    SpectreFlags\n    INTERFACE\n    SpectreFpExceptions\n    )\nendif()\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"SPECTRE_DEBUG: ${SPECTRE_DEBUG}\\n\"\n  \"SPECTRE_NAN_INIT: ${SPECTRE_NAN_INIT}\\n\"\n  )\n"
  },
  {
    "path": "cmake/SetupDoxygen.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Targets to preprocess the documentation\n# - Convert all `.ipynb` files in `docs/` to markdown, so they get picked up by\n#   Doxygen.\nget_filename_component(_PY_BIN_DIR ${Python_EXECUTABLE} DIRECTORY)\nfind_program(NBCONVERT jupyter-nbconvert HINTS ${_PY_BIN_DIR})\nif (NBCONVERT STREQUAL \"NBCONVERT-NOTFOUND\")\n  message(STATUS \"jupyter-nbconvert not found. Preprocessing .ipynb files for \\\ndocumentation disabled.\")\nelse()\n  message(STATUS \"Found jupyter-nbconvert: ${NBCONVERT}\")\n  add_custom_target(\n    doc-notebooks-to-markdown\n    COMMAND find ${CMAKE_SOURCE_DIR}/docs -name \"*.ipynb\"\n      | xargs ${NBCONVERT} --to markdown --log-level=WARN\n        --output-dir ${CMAKE_BINARY_DIR}/docs/tmp/notebooks_md\n    )\nendif()\n\nfind_package(Doxygen)\nif (DOXYGEN_FOUND)\n  set(SPECTRE_DOXYGEN_GROUPS \"${CMAKE_BINARY_DIR}/docs/tmp/GroupDefs.hpp\")\n  # The layout file is doxygen version dependent.  There was a significant\n  # change in version 1.9.8 to support C++ modules.  Doxygen modules were\n  # renamed topics.  Minor changes in doxygen version may cause warnings\n  # about the layout file, but can appear to cause no problems\n  if(DOXYGEN_VERSION VERSION_LESS 1.9.8)\n    set(DOXYGEN_LAYOUT_FILE\n      \"${PROJECT_SOURCE_DIR}/docs/config/DoxygenLayout_1_8_10.xml\")\n  else()\n    set(DOXYGEN_LAYOUT_FILE\n      \"${PROJECT_SOURCE_DIR}/docs/config/DoxygenLayout_1_9_8.xml\"\n    )\n  endif()\n\n  # For INPUT_FILTER in Doxyfile. Using Python instead of Perl here increases\n  # docs generation time by ~25%. Runtimes are 102s (no filter), 108s (Perl) and\n  # 135s (Python) at the time of writing (Mar 2022).\n  find_package(Perl)\n\n  set(SPECTRE_DOX_GENERATE_HTML \"YES\")\n  set(SPECTRE_DOX_GENERATE_XML \"NO\")\n  configure_file(\n    docs/Doxyfile.in\n    ${PROJECT_BINARY_DIR}/docs/DoxyfileHtml @ONLY IMMEDIATE\n    )\n  # Configure file that contains doxygen groups\n  configure_file(docs/GroupDefs.hpp ${SPECTRE_DOXYGEN_GROUPS})\n\n  # Construct the command that calls Doxygen\n  set(\n    GENERATE_DOCS_COMMAND\n    \"${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/docs/DoxyfileHtml\"\n    )\n\n  # Make sure the Doxygen version is compatible with the CSS, or print a\n  # warning. Notes:\n  # - We use https://github.com/jothepro/doxygen-awesome-css release v2.2.0,\n  #   which ensures compatibility with Doxygen v1.9.1 - v1.9.4 and v1.9.6.\n  #   When upgrading Doxygen, it's probably also a good idea to upgrade the CSS\n  #   files in `docs/config/`.\n  # - The Doxygen release v1.9.1 has a bug so namespaces don't show up in\n  #   groups. It is fixed in v1.9.2.\n  # - The Doxygen release v1.9.2 breaks the ordering of pages in the tree view\n  #   (sidebar). It is fixed in v1.9.3.\n  if(DOXYGEN_VERSION VERSION_LESS 1.9.3)\n    set(_DOX_WARNING \"Your Doxygen version ${DOXYGEN_VERSION} may not be \\\ncompatible with the stylesheet, so the documentation may look odd or not \\\nfunction correctly. Use Doxygen version 1.9.3 or higher.\")\n    message(STATUS ${_DOX_WARNING})\n    # The 'warning' in this message will fail the `doc-check` target (see below)\n    set(\n      GENERATE_DOCS_COMMAND\n      \"${GENERATE_DOCS_COMMAND} && echo 'WARNING: ${_DOX_WARNING}'\"\n      )\n  endif()\n\n  # Construct the command that calls doxygen, but only outputs warnings with a\n  # few lines of context to stderr. Fails with exit code 1 if warnings are\n  # found. Remains silent and succeeds with exit code 0 if everything is fine.\n  # We write this into a shell script to make it easier to append the\n  # postprocessing command.\n  set(\n    GENERATE_AND_CHECK_DOCS_SCRIPT \"\\\n#!/bin/sh\\n\\\n! (${GENERATE_DOCS_COMMAND}) 2>&1 | grep -A 6 -i 'warning' >&2\\n\"\n    )\n\n  include(FindPythonModule)\n  find_python_module(bs4)\n  find_python_module(pybtex)\n  if (PY_bs4_FOUND AND PY_pybtex_FOUND)\n    # Construct the command that runs the postprocessing over the Doxygen HTML\n    # output\n    set(\n      DOCS_POST_PROCESS_COMMAND\n      \"${Python_EXECUTABLE} \\\n${CMAKE_SOURCE_DIR}/docs/config/postprocess_docs.py \\\n--html-dir ${PROJECT_BINARY_DIR}/docs/html \\\n--references-files ${CMAKE_SOURCE_DIR}/docs/References.bib \\\n${CMAKE_SOURCE_DIR}/docs/Dependencies.bib\"\n      )\n    # Append postprocessing to doxygen commands\n    # The commands are supposed to run the postprocessing even if the doc\n    # generation failed with warnings, so that we output useful documentation\n    # in any case. The commands exit successfully only if both generation and\n    # postprocessing succeeded.\n    set(\n      GENERATE_DOCS_COMMAND\n      \"${GENERATE_DOCS_COMMAND} && ${DOCS_POST_PROCESS_COMMAND} -v\"\n      )\n    set(\n      GENERATE_AND_CHECK_DOCS_SCRIPT \"${GENERATE_AND_CHECK_DOCS_SCRIPT}\\\ngenerate_docs_exit=$?\\n\\\n${DOCS_POST_PROCESS_COMMAND} && exit \\${generate_docs_exit}\\n\"\n      )\n  else()\n    message(WARNING \"Doxygen documentation postprocessing is disabled because\"\n    \" Python dependencies were not found:\")\n    if (NOT PY_bs4_FOUND)\n      message(WARNING \"BeautifulSoup4 missing. \"\n        \"Install with: pip install beautifulsoup4\")\n    endif()\n    if (NOT PY_pybtex_FOUND)\n      message(WARNING \"Pybtex missing. Install with: pip install pybtex\")\n    endif()\n  endif()\n\n  # Parse the command into a CMake list for the `add_custom_target`\n  separate_arguments(GENERATE_DOCS_COMMAND)\n\n  add_custom_target(\n    doc\n    COMMAND ${GENERATE_DOCS_COMMAND}\n    DEPENDS\n    ${PROJECT_BINARY_DIR}/docs/DoxyfileHtml\n    ${SPECTRE_DOXYGEN_GROUPS}\n    )\n\n  # Write the shell script to a file to call it in the `add_custom_target`\n  file(\n    WRITE\n    ${PROJECT_BINARY_DIR}/docs/tmp/GenerateAndCheckDocs.sh\n    ${GENERATE_AND_CHECK_DOCS_SCRIPT}\n    )\n\n  add_custom_target(\n    doc-check\n    COMMAND sh ${PROJECT_BINARY_DIR}/docs/tmp/GenerateAndCheckDocs.sh\n    DEPENDS\n    ${PROJECT_BINARY_DIR}/docs/DoxyfileHtml\n    ${SPECTRE_DOXYGEN_GROUPS}\n    )\n\n  set(SPECTRE_DOX_GENERATE_HTML \"NO\")\n  set(SPECTRE_DOX_GENERATE_XML \"YES\")\n  configure_file(\n    docs/Doxyfile.in\n    ${PROJECT_BINARY_DIR}/docs/DoxyfileXml @ONLY IMMEDIATE\n    )\n\n  add_custom_target(\n    doc-xml\n    COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/docs/DoxyfileXml\n    DEPENDS\n    ${PROJECT_BINARY_DIR}/docs/DoxyfileXml\n    ${SPECTRE_DOXYGEN_GROUPS}\n    )\n\n  if (TARGET doc-notebooks-to-markdown)\n    add_dependencies(doc doc-notebooks-to-markdown)\n    add_dependencies(doc-check doc-notebooks-to-markdown)\n    add_dependencies(doc-xml doc-notebooks-to-markdown)\n  endif()\n\n  find_program(LCOV lcov)\n  find_program(GENHTML genhtml)\n  find_program(SED sed)\n\n  # Use [coverxygen](https://github.com/psycofdj/coverxygen) to check the level\n  # of documentation coverage.\n  find_python_module(coverxygen)\n  if (LCOV AND GENHTML AND SED AND PY_coverxygen_FOUND\n      AND EXISTS ${CMAKE_SOURCE_DIR}/.git AND Git_FOUND)\n    set(DOX_COVERAGE_OUTPUT \"${CMAKE_BINARY_DIR}/docs/html/doc_coverage/\")\n    add_custom_target(\n      doc-coverage\n\n      COMMAND ${Python_EXECUTABLE}\n      -m coverxygen\n      --xml-dir ${CMAKE_BINARY_DIR}/docs/xml\n      --src-dir ${CMAKE_SOURCE_DIR}\n      --output ${CMAKE_BINARY_DIR}/docs/tmp/doc_coverage.info\n\n      COMMAND ${LCOV}\n      --remove ${CMAKE_BINARY_DIR}/docs/tmp/doc_coverage.info\n      '${CMAKE_SOURCE_DIR}/src/Executables/*'\n      '${CMAKE_SOURCE_DIR}/tests/*'\n      '${CMAKE_SOURCE_DIR}/citelist'\n      '${CMAKE_SOURCE_DIR}/[generated]'\n      --output ${CMAKE_BINARY_DIR}/docs/tmp/doc_coverage.info\n\n      COMMAND\n      ${LCOV} --summary ${CMAKE_BINARY_DIR}/docs/tmp/doc_coverage.info\n\n      COMMAND\n      ${GENHTML} --legend\n      --no-function-coverage\n      --no-branch-coverage\n      --title ${GIT_HASH}\n      ${CMAKE_BINARY_DIR}/docs/tmp/doc_coverage.info\n      -o ${DOX_COVERAGE_OUTPUT}\n\n      COMMAND\n      find ${DOX_COVERAGE_OUTPUT} -type f -print\n      | xargs file\n      | grep text\n      | cut -f1 -d:\n      | xargs ${SED} -i'.bak' 's/LCOV - code coverage report/\n      SpECTRE Documentation Coverage Report/g'\n\n      COMMAND find ${DOX_COVERAGE_OUTPUT} -type f -print | xargs file\n      | grep text\n      | cut -f1 -d:\n      | xargs ${SED} -i'.bak' 's^<td class=\"headerItem\">Test:</td>^\n      <td class=\"headerItem\">Commit:</td>^g'\n\n      COMMAND find ${DOX_COVERAGE_OUTPUT} -type f -print | xargs file\n      | grep text\n      | cut -f1 -d:\n      | xargs ${SED} -i'.bak' 's^<td class=\"headerValue\">\\\\\\([a-z0-9]\\\\{40\\\\}\\\\\\)^\n      <td class=\"headerValue\"><a target=\"_blank\"\n      href=\"https://github.com/sxs-collaboration/spectre/commit/\\\\1\">\\\\1</a>^g'\n\n      # Delete backup files created by sed\n      COMMAND find ${DOX_COVERAGE_OUTPUT} -type f -name \\\"*.bak\\\" -print\n      | xargs file | grep text | cut -f1 -d: | xargs rm\n\n      DEPENDS\n      ${PROJECT_BINARY_DIR}/docs/DoxyfileXml\n      ${SPECTRE_DOXYGEN_GROUPS}\n      doc-xml\n\n      # Set work directory for target\n      WORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n\n      COMMENT \"SpECTRE Documentation Coverage\"\n      )\n  endif()\nelse(DOXYGEN_FOUND)\n  message(WARNING \"Doxygen is needed to build the documentation.\")\nendif (DOXYGEN_FOUND)\n"
  },
  {
    "path": "cmake/SetupFormaline.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# When running formaline when SpECTRE was not built from a Git repository\n# we need a list of all the files and directories in the root of the source\n# directory that are tracked by Git. I.e.\n#   git ls-tree --full-tree --name-only HEAD\nset(SPECTRE_FORMALINE_LOCATIONS\n  .claude\n  .clang-format\n  .clang-tidy\n  .claude\n  .codecov.yaml\n  .codex\n  .devcontainer\n  .dockerignore\n  .github\n  .gitignore\n  citation.bib\n  CITATION.cff\n  cmake\n  CMakeLists.txt\n  containers\n  docs\n  external\n  LICENSE.txt\n  Metadata.yaml\n  pyproject.toml\n  README.md\n  .skills\n  setup.cfg\n  src\n  support\n  tests\n  tools\n  )\n\nfind_package(Git)\n\nif(EXISTS ${CMAKE_SOURCE_DIR}/.git AND Git_FOUND)\n  execute_process(\n    COMMAND ${GIT_EXECUTABLE} ls-tree --full-tree --name-only HEAD\n    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n    OUTPUT_VARIABLE FORMALINE_GIT_FILES_TRACKED\n    OUTPUT_STRIP_TRAILING_WHITESPACE\n    )\n  string(REPLACE \"\\n\" \";\" FORMALINE_GIT_FILES_TRACKED\n    \"${FORMALINE_GIT_FILES_TRACKED}\")\n\n  # Check all elements in SPECTRE_FORMALINE_LOCATIONS are in\n  # FORMALINE_GIT_FILES_TRACKED. We don't just check that the sorted lists\n  # are equal because we want to be able to print out exactly which file\n  # or directory is missing.\n  foreach(FILE ${SPECTRE_FORMALINE_LOCATIONS})\n    list(FIND FORMALINE_GIT_FILES_TRACKED ${FILE} FOUND_FILE)\n    if(${FOUND_FILE} EQUAL -1)\n      message(FATAL_ERROR\n        \"Couldn't find the file or directory \\\"${FILE}\\\" in the \"\n        \"source directory. You need to remove the entry \\\"${FILE}\\\" \"\n        \"from the CMake variable SPECTRE_FORMALINE_LOCATIONS in \"\n        \"cmake/SetupFormaline.cmake\")\n    endif()\n  endforeach()\n\n  # Check all elements in FORMALINE_GIT_FILES_TRACKED are in\n  # SPECTRE_FORMALINE_LOCATIONS. We don't just check that the sorted lists\n  # are equal because we want to be able to print out exactly which file\n  # or directory is missing.\n  foreach(FILE ${FORMALINE_GIT_FILES_TRACKED})\n    list(FIND SPECTRE_FORMALINE_LOCATIONS ${FILE} FOUND_FILE)\n    if(${FOUND_FILE} EQUAL -1)\n      message(FATAL_ERROR\n        \"Couldn't find the file or directory \\\"${FILE}\\\" in the \"\n        \"CMake variable SPECTRE_FORMALINE_LOCATIONS in \"\n        \"cmake/SetupFormaline.cmake. You need to add \\\"${FILE}\\\" \"\n        \"to the CMake variable SPECTRE_FORMALINE_LOCATIONS in \"\n        \"cmake/SetupFormaline.cmake\")\n    endif()\n  endforeach()\nendif()\n\noption(USE_FORMALINE\n  \"Use Formaline to encode the source tree into executables and output files.\"\n  ON)\n\n# APPLE instead of ${APPLE} is intentional\nif (APPLE)\n  set(USE_FORMALINE OFF)\nendif (APPLE)\n\nif (USE_FORMALINE)\n  # Create a variable that is space-delimited to insert into the Formaline\n  # shell script\n  string(REPLACE \";\" \" \" SPECTRE_FORMALINE_LOCATIONS_SHELL\n    \"${SPECTRE_FORMALINE_LOCATIONS}\")\n\n  configure_file(\n    ${CMAKE_SOURCE_DIR}/tools/Formaline.sh\n    ${CMAKE_BINARY_DIR}/tmp/Formaline.sh\n    @ONLY\n    )\nelse()\n  file(REMOVE ${CMAKE_BINARY_DIR}/tmp/Formaline.sh)\nendif()\n"
  },
  {
    "path": "cmake/SetupFortran.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(SPECTRE_Fortran_STATIC_LIBS\n  \"Link static versions of libgfortran and libquadmath\" OFF)\n\nif(SPECTRE_Fortran_STATIC_LIBS)\n  unset(CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES)\n  find_library(gfortran NAMES libgfortran.a)\n  find_library(quadmath NAMES libquadmath.a)\nendif()\n"
  },
  {
    "path": "cmake/SetupFuka.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(FUKA)\n\nif (FUKA_FOUND)\n  file(APPEND\n    \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n    \"FUKA: ${FUKA_ROOT}\\n\"\n    )\nendif()\n"
  },
  {
    "path": "cmake/SetupGitHooks.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Allow disabling the Git hooks because if you are running the code in a\n# container the host may not have all the right things installed.\noption(\n  USE_GIT_HOOKS\n  \"Set up the git hooks for sanity checks.\"\n  ON)\n\nif(USE_GIT_HOOKS)\n  # Check that the source dir is writable. If it is we set up git hooks, if not\n  # then there probably won't be any commits anyway...\n  EXECUTE_PROCESS(COMMAND test -w ${CMAKE_SOURCE_DIR}\n    RESULT_VARIABLE CHECK_SOURCE_DIR_WRITABLE_RESULT)\n\n  # The logic is inverted because shell\n  if(NOT CHECK_SOURCE_DIR_WRITABLE_RESULT AND EXISTS ${CMAKE_SOURCE_DIR}/.git)\n    find_package(ClangFormat)\n    find_package(Git)\n\n    # We use several client-side git hooks to ensure commits are correct as\n    # early as possible.\n    configure_file(\n      ${CMAKE_SOURCE_DIR}/tools/Hooks/pre-commit.sh\n      ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit\n      @ONLY\n      )\n\n    configure_file(\n      ${CMAKE_SOURCE_DIR}/tools/Hooks/CheckFileSize.py\n      ${CMAKE_SOURCE_DIR}/.git/hooks/CheckFileSize.py\n      @ONLY\n      )\n  endif()\nendif()\n"
  },
  {
    "path": "cmake/SetupGoldOrLldLinker.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(USE_LD\n  \"Override the default linker. Options are: ld, gold, lld\"\n  OFF)\n\nif (USE_LD)\n  if(\"${USE_LD}\" STREQUAL \"gold\")\n    find_program(GNU_GOLD_LINKER \"ld.gold\")\n    if (NOT GNU_GOLD_LINKER)\n      message(FATAL_ERROR\n        \"ld.gold requested but could not find executable\")\n    endif()\n    check_and_add_cxx_link_flag(\"-fuse-ld=gold\")\n  elseif(\"${USE_LD}\" STREQUAL \"lld\")\n    if (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n      message(FATAL_ERROR \"GCC does not support linking with LLD. \"\n        \"If this has changed please remove this error message \"\n        \"and submit a pull request.\")\n    endif()\n    find_program(LLD_LINKER \"ld.lld\")\n    if (NOT LLD_LINKER)\n      message(FATAL_ERROR\n        \"ld.lld requested but could not find executable\")\n    endif()\n    check_and_add_cxx_link_flag(\"-fuse-ld=lld\")\n  elseif(NOT \"${USE_LD}\" STREQUAL \"ld\")\n    message(FATAL_ERROR\n      \"USE_LD must be one of 'ld', 'gold' or 'lld' but got '${USE_LD}'\")\n  endif()\nelse()\n  if (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n    # GCC currently only supports linking with ld.gold, not ld.lld\n    find_program(GNU_GOLD_LINKER \"ld.gold\")\n    if (GNU_GOLD_LINKER)\n      check_and_add_cxx_link_flag(\"-fuse-ld=gold\")\n    endif()\n  else()\n    find_program(LLD_LINKER \"ld.lld\")\n    if (LLD_LINKER)\n      check_and_add_cxx_link_flag(\"-fuse-ld=lld\")\n    else()\n      find_program(GNU_GOLD_LINKER \"ld.gold\")\n      if (GNU_GOLD_LINKER)\n        check_and_add_cxx_link_flag(\"-fuse-ld=gold\")\n      endif()\n    endif()\n  endif()\nendif()\n"
  },
  {
    "path": "cmake/SetupGoogleBenchmark.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(GoogleBenchmark QUIET)\n\n\nif (${GoogleBenchmark_FOUND})\n  message(STATUS \"Google Benchmark libs: \" ${GoogleBenchmark_LIBRARIES})\n  message(STATUS \"Google Benchmark incl: \" ${GoogleBenchmark_INCLUDE_DIRS})\n\n  file(APPEND\n    \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n    \"Google Benchmark found\\n\"\n    )\n\n  add_library(GoogleBenchmark INTERFACE IMPORTED)\n  set_property(TARGET GoogleBenchmark PROPERTY\n    INTERFACE_INCLUDE_DIRECTORIES ${GoogleBenchmark_INCLUDE_DIRS})\n  set_property(TARGET GoogleBenchmark PROPERTY\n    INTERFACE_LINK_LIBRARIES ${GoogleBenchmark_LIBRARIES})\n  set_property(\n    GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n    GoogleBenchmark\n    )\nendif()\n"
  },
  {
    "path": "cmake/SetupGsl.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(GSL_STATIC\n  \"Link static versions of GNU Scientific Library\" OFF)\n\nif(${GSL_STATIC})\n  set(_BACKUP_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})\n  set(CMAKE_FIND_LIBRARY_SUFFIXES \".a\")\nendif(${GSL_STATIC})\nfind_package(GSL REQUIRED)\nif(${GSL_STATIC})\n  set(CMAKE_FIND_LIBRARY_SUFFIXES ${_BACKUP_CMAKE_FIND_LIBRARY_SUFFIXES})\nendif(${GSL_STATIC})\n\nmessage(STATUS \"GSL libs: ${GSL_LIBRARIES}\")\nmessage(STATUS \"GSL incl: ${GSL_INCLUDE_DIR}\")\nmessage(STATUS \"GSL vers: ${GSL_VERSION}\")\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"GSL version: ${GSL_VERSION}\\n\"\n  )\n\n# Link external BLAS library. We don't need the GSL::gslcblas target.\nfind_package(BLAS REQUIRED)\ntarget_link_libraries(\n  GSL::gsl\n  INTERFACE\n  BLAS::BLAS\n  )\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  GSL::gsl GSL::gslcblas\n  )\n"
  },
  {
    "path": "cmake/SetupHdf5.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(HDF5 REQUIRED COMPONENTS C)\n\nmessage(STATUS \"HDF5 libs: \" ${HDF5_C_LIBRARIES})\nmessage(STATUS \"HDF5 incl: \" ${HDF5_C_INCLUDE_DIRS})\nmessage(STATUS \"HDF5 vers: \" ${HDF5_VERSION})\n\nif(NOT TARGET hdf5::hdf5)\n  add_library(hdf5::hdf5 INTERFACE IMPORTED)\n  set_target_properties(\n    hdf5::hdf5\n    PROPERTIES\n    INTERFACE_INCLUDE_DIRECTORIES \"${HDF5_C_INCLUDE_DIRS}\"\n    INTERFACE_LINK_LIBRARIES \"${HDF5_C_LIBRARIES}\"\n    )\n  if(DEFINED HDF5_C_DEFINITIONS)\n    set_target_properties(\n      hdf5::hdf5\n      PROPERTIES\n      INTERFACE_COMPILE_FLAGS \"${HDF5_C_DEFINITIONS}\"\n      )\n  endif()\nendif()\n\nif(NOT TARGET HDF5::HDF5)\n  add_library(HDF5::HDF5 INTERFACE IMPORTED)\n  set_target_properties(\n    HDF5::HDF5\n    PROPERTIES\n    INTERFACE_INCLUDE_DIRECTORIES \"${HDF5_C_INCLUDE_DIRS}\"\n    INTERFACE_LINK_LIBRARIES \"${HDF5_C_LIBRARIES}\"\n    )\n  if(DEFINED HDF5_C_DEFINITIONS)\n    set_target_properties(\n      HDF5::HDF5\n      PROPERTIES\n      INTERFACE_COMPILE_FLAGS \"${HDF5_C_DEFINITIONS}\"\n      )\n  endif()\nendif()\n\nif(HDF5_IS_PARALLEL)\n  find_package(MPI COMPONENTS C)\n  if(MPI_FOUND)\n    target_link_libraries(hdf5::hdf5 INTERFACE MPI::MPI_C)\n    target_link_libraries(HDF5::HDF5 INTERFACE MPI::MPI_C)\n  else()\n    message(WARNING \"HDF5 is built with MPI support, but MPI was not found. \"\n      \"You may encounter build issues with HDF5, such as missing headers.\")\n  endif()\nendif()\n\nif(HDF5_USE_STATIC_LIBRARIES)\n  # If we are using static libraries for HDF5, try to use static libraries\n  # for the libsz and transitive libaec dependency. These aren't very\n  # common, while other dependencies like libz are, so we leave those\n  # as shared library dependencies.\n  get_target_property(\n    _HDF5_INTERFACE_LINK_LIBS HDF5::HDF5 INTERFACE_LINK_LIBRARIES)\n  string(FIND\n    \"${_HDF5_INTERFACE_LINK_LIBS}\" \"libsz.so\" _LOCATION_OF_LIBSZ)\n  if (NOT ${_LOCATION_OF_LIBSZ} EQUAL -1)\n    find_library(_libsz NAMES libsz.a)\n    if(_libsz)\n      string(REPLACE\n        \"libsz.so\" \"libsz.a\" _HDF5_INTERFACE_LINK_LIBS\n        \"${_HDF5_INTERFACE_LINK_LIBS}\")\n      find_library(_libaec NAMES libaec.a)\n      if(_libaec)\n        list(APPEND _HDF5_INTERFACE_LINK_LIBS ${_libaec})\n      endif()\n    endif()\n  endif()\n  string(FIND\n    \"${_HDF5_INTERFACE_LINK_LIBS}\" \"libcrypto.so\" _LOCATION_OF_LIBCRYPTO)\n  if (NOT ${_LOCATION_OF_LIBCRYPTO} EQUAL -1)\n    find_library(_libcrypto NAMES libcrypto.a)\n    if(_libcrypto)\n      string(REPLACE\n        \"libcrypto.so\" \"libcrypto.a\" _HDF5_INTERFACE_LINK_LIBS\n        \"${_HDF5_INTERFACE_LINK_LIBS}\")\n    endif()\n  endif()\n  string(FIND\n    \"${_HDF5_INTERFACE_LINK_LIBS}\" \"libz.so\" _LOCATION_OF_LIBZ)\n  if (NOT ${_LOCATION_OF_LIBZ} EQUAL -1)\n    find_library(_libz NAMES libz.a)\n    if(_libz)\n      string(REPLACE\n        \"libz.so\" \"libz.a\" _HDF5_INTERFACE_LINK_LIBS\n        \"${_HDF5_INTERFACE_LINK_LIBS}\")\n    endif()\n  endif()\n  set_target_properties(\n    HDF5::HDF5\n    PROPERTIES\n    INTERFACE_LINK_LIBRARIES \"${_HDF5_INTERFACE_LINK_LIBS}\"\n  )\nendif()\n\n# Check if file locking API is available. The versions supporting this feature\n# are listed here:\n# https://github.com/HDFGroup/hdf5/blob/develop/doc/file-locking.md\ninclude(CheckCXXSourceCompiles)\nset(CMAKE_REQUIRED_LIBRARIES HDF5::HDF5)\ncheck_cxx_source_compiles(\n  \"#include <hdf5.h>\\n\\\nint main() {\\n\\\n  const hid_t fapl_id = H5Pcopy(H5P_DEFAULT);\\n\\\n  H5Pset_file_locking(fapl_id, false, true);\\n\\\n}\"\n  HDF5_SUPPORTS_SET_FILE_LOCKING)\nif(${HDF5_SUPPORTS_SET_FILE_LOCKING})\n  set_property(\n    TARGET hdf5::hdf5\n    APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS\n    HDF5_SUPPORTS_SET_FILE_LOCKING)\nelse()\n  message(WARNING \"The HDF5 library does not support 'H5Pset_file_locking'. \"\n    \"This means that simulations may crash when you read H5 files while \"\n    \"the simulation is trying to access them. To avoid this, set the \"\n    \"environment variable\\n\"\n    \"  HDF5_USE_FILE_LOCKING=FALSE\\n\"\n    \"when running simulations, or load an HDF5 module that supports \"\n    \"'H5Pset_file_locking'. Supporting versions are listed here:\\n\"\n    \"https://github.com/HDFGroup/hdf5/blob/develop/doc/file-locking.md\")\nendif()\n\ninclude(CheckCXXSourceRuns)\nset(CMAKE_REQUIRED_LIBRARIES HDF5::HDF5)\n# Logic from src/Utilities/ErrorHandling/FloatingPointExceptions.cpp\ncheck_cxx_source_runs(\n  [=[\n#include <hdf5.h>\n\n#ifdef __APPLE__\n#ifndef __arm64__\n#define SPECTRE_FPE_CSR 1\n#include <xmmintrin.h>\n#endif\n#else\n#define SPECTRE_FPE_FENV 1\n#include <cfenv>\n#endif\n\nint main(int /*argc*/, char** /*argv*/) {\n#if SPECTRE_FPE_CSR\n  _mm_setcsr(_MM_MASK_MASK &\n             ~(_MM_MASK_OVERFLOW | _MM_MASK_INVALID | _MM_MASK_DIV_ZERO));\n#elif SPECTRE_FPE_FENV\n  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);\n#endif\n  H5Tcopy(H5T_NATIVE_DOUBLE);\n  return 0;\n}\n]=]\n  HDF5_INIT_WITHOUT_FPES\n  )\nif(NOT HDF5_INIT_WITHOUT_FPES)\n  message(FATAL_ERROR\n    \"The HDF5 library triggers FPEs during initialization.  See upstream bug \"\n    \"https://github.com/HDFGroup/hdf5/issues/3831.  Either switch to an \"\n    \"unaffected version or apply the patch referenced from that issue.\")\nendif()\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  HDF5::HDF5\n  )\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"HDF5 version: ${HDF5_VERSION}\\n\"\n  )\n\n# Find HDF5 tools\nget_filename_component(HDF5_TOOLS_DIR HDF5_DIFF_EXECUTABLE DIRECTORY)\nfind_program(HDF5_REPACK_EXECUTABLE h5repack HINTS ${HDF5_TOOLS_DIR})\n"
  },
  {
    "path": "cmake/SetupInformer.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Configure info from build to give access to unit test path,\n# SpECTRE version, etc. (things known at CMake time)\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/src/Informer/InfoAtCompile.cpp\n  ${CMAKE_BINARY_DIR}/Informer/InfoAtCompile.cpp\n  )\n\noption(\n  STUB_EXECUTABLE_OBJECT_FILES\n  \"Replace executable object files with stubs to reduce disk usage.\"\n  OFF\n  )\n\nset(WRAP_EXECUTABLE_LINKER_USE_STUB_OBJECT_FILES \"false\")\nif (STUB_EXECUTABLE_OBJECT_FILES)\n  set(WRAP_EXECUTABLE_LINKER_USE_STUB_OBJECT_FILES \"true\")\nendif (STUB_EXECUTABLE_OBJECT_FILES)\n\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/tools/WrapExecutableLinker.sh\n  ${CMAKE_BINARY_DIR}/tmp/WrapExecutableLinker.sh\n  @ONLY\n  )\n\noption(\n  STUB_LIBRARY_OBJECT_FILES\n  \"Replace library object files with stubs to reduce disk usage.\"\n  OFF\n  )\n\nset(WRAP_LIBRARY_LINKER_USE_STUB_OBJECT_FILES \"false\")\nif (STUB_LIBRARY_OBJECT_FILES)\n  set(WRAP_LIBRARY_LINKER_USE_STUB_OBJECT_FILES \"true\")\nendif (STUB_LIBRARY_OBJECT_FILES)\n\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/tools/WrapLibraryLinker.sh\n  ${CMAKE_BINARY_DIR}/tmp/WrapLibraryLinker.sh\n  @ONLY\n  )\n"
  },
  {
    "path": "cmake/SetupJemalloc.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(JEMALLOC REQUIRED)\n\nmessage(STATUS \"jemalloc libs: \" ${JEMALLOC_LIBRARIES})\nmessage(STATUS \"jemalloc incl: \" ${JEMALLOC_INCLUDE_DIRS})\nmessage(STATUS \"jemalloc vers: \" ${JEMALLOC_VERSION})\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"jemalloc version: ${JEMALLOC_VERSION}\\n\"\n  )\n\nadd_library(Jemalloc INTERFACE IMPORTED)\nset_property(TARGET Jemalloc\n  APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${JEMALLOC_LIBRARIES})\nset_property(TARGET Jemalloc PROPERTY\n  INTERFACE_INCLUDE_DIRECTORIES ${JEMALLOC_INCLUDE_DIRS})\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  Jemalloc\n  )\n\n# Determine whether we are using JEMALLOC as a shared or static library.\n# Since libraries can be named, e.g. libjemalloc.so.2 we just search for\n# the extension after stripping the directories. It's unlikely jemalloc\n# will rename themselves to \"alloc.so\" and then we'd have libs like\n# \"alloc.so.a\"\nget_filename_component(\n  JEMALLOC_LIB_NAME\n  ${JEMALLOC_LIBRARIES}\n  NAME\n  )\nif(APPLE)\n  string(FIND \"${JEMALLOC_LIB_NAME}\" \".dylib\" FOUND_SHARED)\nelse()\n  string(FIND \"${JEMALLOC_LIB_NAME}\" \".so\" FOUND_SHARED)\nendif()\nif(${FOUND_SHARED} STREQUAL -1)\n  string(FIND \"${JEMALLOC_LIB_NAME}\" \".a\" FOUND_STATIC)\n  if(${FOUND_STATIC} EQUAL -1)\n    message(FATAL_ERROR \"Failed to determine whether JEMALLOC is a shared or \"\n      \"static library. Found: ${JEMALLOC_LIBRARIES}\")\n  endif()\n  set(JEMALLOC_LIB_TYPE STATIC)\nelse()\n  set(JEMALLOC_LIB_TYPE SHARED)\nendif()\n\nmark_as_advanced(JEMALLOC_LIB_TYPE)\n\nif(\"${JEMALLOC_LIB_TYPE}\" STREQUAL STATIC)\n  message(WARNING \"It's probably a better idea to use jemalloc as a shared \"\n    \"library. Statically linking jemalloc might work but has not been tested.\")\nendif()\n"
  },
  {
    "path": "cmake/SetupKokkos.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(SPECTRE_KOKKOS \"Use Kokkos\" OFF)\n\nif(SPECTRE_KOKKOS)\n  find_package(Kokkos)\n\n  if (Kokkos_FOUND)\n    # Found external Kokkos installation, so just check that a few important\n    # features are enabled\n    if (Kokkos_ENABLE_CUDA)\n      # Allow constexpr functions to be called from device code\n      # without the need for a device annotation.\n      # See https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#constexpr-functions-and-function-templates\n      if (NOT Kokkos_ENABLE_CUDA_CONSTEXPR)\n        message(FATAL_ERROR \"Kokkos_ENABLE_CUDA_CONSTEXPR must be ON. \"\n          \"Please recompile Kokkos with this option enabled.\")\n      endif()\n      # Allow CUDA code in static libs, see\n      # https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#using-separate-compilation-in-cuda\n      # We may have to look into LTO if this has a performance penalty.\n      if (NOT Kokkos_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE)\n        message(FATAL_ERROR \"Kokkos_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE \"\n          \"must be ON. Please recompile Kokkos with this option enabled.\")\n      endif()\n      if (BUILD_SHARED_LIBS)\n        message(FATAL_ERROR \"Building with shared libs is not supported when \"\n          \"building with CUDA support. Set BUILD_SHARED_LIBS=OFF.\")\n      endif()\n    endif()  # Kokkos_ENABLE_CUDA\n  else()  # Kokkos_FOUND\n    # Kokkos not found, so we will try to fetch it.\n    if (NOT SPECTRE_FETCH_MISSING_DEPS)\n      message(FATAL_ERROR \"Could not find Kokkos. If you want to fetch \"\n        \"missing dependencies automatically, set SPECTRE_FETCH_MISSING_DEPS=ON.\")\n    endif()\n\n    # Configure the Kokkos build\n    set(Kokkos_ENABLE_AGGRESSIVE_VECTORIZATION ON CACHE BOOL\n      \"Kokkos aggressive vectorization\")\n\n    if (CMAKE_BUILD_TYPE STREQUAL \"Debug\" OR SPECTRE_DEBUG)\n      message(STATUS \"Enabling Kokkos debug mode\")\n      set(Kokkos_ENABLE_DEBUG ON CACHE BOOL \"Most general debug settings\")\n      set(Kokkos_ENABLE_DEBUG_BOUNDS_CHECK ON CACHE BOOL\n        \"Bounds checking on Kokkos views\")\n      set(Kokkos_ENABLE_DEBUG_DUALVIEW_MODIFY_CHECK ON CACHE BOOL\n        \"Sanity checks on Kokkos DualView\")\n    endif()\n\n    if(Kokkos_ENABLE_CUDA)\n      set(CMAKE_CUDA_STANDARD 20)\n      set(CMAKE_CUDA_STANDARD_REQUIRED ON)\n      enable_language(CUDA)\n      find_package(CUDAToolkit REQUIRED)\n      set(Kokkos_ENABLE_CUDA_CONSTEXPR ON CACHE BOOL\n        \"Enable constexpr in CUDA\")\n      set(Kokkos_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE ON CACHE BOOL\n        \"Enable relocatable device code in CUDA\")\n      if (BUILD_SHARED_LIBS)\n        message(FATAL_ERROR \"Building with shared libs is not supported when \"\n          \"building with CUDA support. Set BUILD_SHARED_LIBS=OFF.\")\n      endif()\n    endif()\n\n    message(STATUS \"Fetching Kokkos\")\n    include(FetchContent)\n    FetchContent_Declare(Kokkos\n      GIT_REPOSITORY https://github.com/kokkos/kokkos.git\n      GIT_TAG 4.4.00\n      GIT_SHALLOW TRUE\n      ${SPECTRE_FETCHCONTENT_BASE_ARGS}\n    )\n    FetchContent_MakeAvailable(Kokkos)\n  endif()\nendif()\n\n# Determine if the compiler is NVIDIA's nvcc (if not already determined by\n# Kokkos)\nif (NOT DEFINED KOKKOS_CXX_COMPILER_ID)\n  execute_process(\n    COMMAND ${CMAKE_CXX_COMPILER} --version\n    OUTPUT_VARIABLE _COMPILER_VERSION\n    OUTPUT_STRIP_TRAILING_WHITESPACE)\n  string(REPLACE \"\\n\" \" \" _COMPILER_VERSION ${_COMPILER_VERSION})\n  string(FIND ${_COMPILER_VERSION} \"nvcc\" _COMPILER_IS_NVCC)\n  if(${_COMPILER_IS_NVCC} GREATER -1)\n    set(KOKKOS_CXX_COMPILER_ID \"NVIDIA\")\n  else()\n    set(KOKKOS_CXX_COMPILER_ID ${CMAKE_CXX_COMPILER_ID})\n  endif()\nendif()\n\n# Include CUDA as system headers to suppress warnings. NVCC seems to include\n# CUDA headers as `-I` instead of `-isystem` for some reason (as of\n# version 12.6).\nif (CUDAToolkit_INCLUDE_DIRS)\n  create_cxx_flag_target(\n    \"-Xcompiler \\\"-isystem${CUDAToolkit_INCLUDE_DIRS}\\\"\"\n    SpectreCudaSystemInclude)\n  target_link_libraries(\n    SpectreWarnings\n    INTERFACE\n    SpectreCudaSystemInclude\n    )\nendif()\n"
  },
  {
    "path": "cmake/SetupLIBCXX.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\")\n  if (NOT USE_LIBCXX)\n    option(USE_LIBCXX \"Use libc++ as standard library\" OFF)\n  endif()\n  if (USE_LIBCXX)\n    include(CheckCXXCompilerFlag)\n    unset(CXX_FLAG_WORKS CACHE)\n    set(CMAKE_REQUIRED_QUIET 1)\n    check_cxx_compiler_flag(\"-stdlib=libc++\" CXX_FLAG_WORKS)\n    unset(CMAKE_REQUIRED_QUIET)\n    # If we cannot use the -stdlib=libc++ flag directly we must find libc++\n    # explicity by searching on the file system\n    if (CXX_FLAG_WORKS AND NOT LIBCXX_ROOT)\n      message(STATUS \"libc++: Compiler supports -stdlib=libc++, no need to find \"\n        \"libc++ explicity. Specify `LIBCXX_ROOT` to use a specified version of \"\n        \"libc++. If the specified version is not found we fall back to the \"\n        \"system libc++, if available.\")\n    else()\n      find_package(LIBCXX REQUIRED)\n      set(\n        CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS}\\\n -L${LIBCXXABI_LIBRARIES} -L${LIBCXX_LIBRARIES}\"\n        )\n      include_directories(${LIBCXX_INCLUDE_DIRS})\n      message(STATUS \"libc++ include: ${LIBCXX_INCLUDE_DIRS}\")\n      message(STATUS \"libc++ libraries: ${LIBCXX_LIBRARIES}\")\n    endif()\n\n    set(CMAKE_CXX_FLAGS \"-stdlib=libc++ ${CMAKE_CXX_FLAGS}\")\n\n    option(\n      LIBCXX_DEBUG\n      \"Enable debug mode for libc++ in Debug builds\"\n      OFF\n      )\n    if(LIBCXX_DEBUG)\n      set(CMAKE_CXX_FLAGS_DEBUG  \"${CMAKE_CXX_FLAGS_DEBUG} -D_LIBCPP_DEBUG=0\")\n    endif()\n  endif()\nendif()\n"
  },
  {
    "path": "cmake/SetupLIBCXXCharm.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# We need to add the link flag after finding packages because adding a\n# compilation flag to the linker flags causes packages/libraries to not be\n# always be found correctly.\nif (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\" AND USE_LIBCXX)\n  set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -stdlib=libc++\")\nendif()\n"
  },
  {
    "path": "cmake/SetupLIBXSMM.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(LIBXSMM 1.16.1)\n\nif (NOT LIBXSMM_FOUND)\n  if (NOT SPECTRE_FETCH_MISSING_DEPS)\n    message(FATAL_ERROR \"Could not find LIBXSMM. If you want to fetch \"\n      \"missing dependencies automatically, set SPECTRE_FETCH_MISSING_DEPS=ON.\")\n  endif()\n  message(STATUS \"Fetching LIBXSMM\")\n\n  # This FetchContent code is adapted from the libxsmm docs:\n  # https://libxsmm.readthedocs.io/en/latest/#rules-for-building-libxsmm\n  include(FetchContent)\n\n  FetchContent_GetProperties(xsmm)\n  if(NOT xsmm_POPULATED)\n    # Need an unreleased version to be compatible with newer glibc versions\n    FetchContent_Populate(xsmm\n      GIT_REPOSITORY https://github.com/libxsmm/libxsmm.git\n      GIT_TAG 10b7dc82b3c46157e76eb40e4e959555f895b24d\n      SUBBUILD_DIR ${CMAKE_BINARY_DIR}/_deps/xsmm-subbuild\n      SOURCE_DIR ${CMAKE_BINARY_DIR}/_deps/xsmm-src\n      BINARY_DIR ${CMAKE_BINARY_DIR}/_deps/xsmm-build\n    )\n  endif()\n\n  set(LIBXSMMROOT ${xsmm_SOURCE_DIR})\n  file(GLOB _GLOB_XSMM_SRCS LIST_DIRECTORIES false CONFIGURE_DEPENDS ${LIBXSMMROOT}/src/*.c)\n  list(REMOVE_ITEM _GLOB_XSMM_SRCS ${LIBXSMMROOT}/src/libxsmm_generator_gemm_driver.c)\n  list(REMOVE_ITEM _GLOB_XSMM_SRCS ${LIBXSMMROOT}/src/libxsmm_binaryexport_generator.c)\n  set(XSMM_INCLUDE_DIRS ${LIBXSMMROOT}/include)\n\n  add_library(xsmm STATIC ${_GLOB_XSMM_SRCS})\n  target_include_directories(xsmm SYSTEM PUBLIC ${XSMM_INCLUDE_DIRS})\n  target_compile_definitions(xsmm PUBLIC LIBXSMM_DEFAULT_CONFIG)\n\n  # Link BLAS\n  find_package(BLAS REQUIRED)\n  target_link_libraries(xsmm PUBLIC BLAS::BLAS)\n\n  # Provide `Libxsmm` target\n  add_library(Libxsmm ALIAS xsmm)\nendif()\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  Libxsmm\n  )\n"
  },
  {
    "path": "cmake/SetupLapack.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(LAPACK REQUIRED)\nmessage(STATUS \"LAPACK libs: \" ${LAPACK_LIBRARIES})\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"LAPACK_LIBRARIES: ${LAPACK_LIBRARIES}\\n\"\n  )\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  LAPACK::LAPACK\n  )\n"
  },
  {
    "path": "cmake/SetupLicenseInfo.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(_LIST_OF_3PLS\n  Blaze\n  Boost\n  Brigand\n  Catch2\n  Charm\n  Gsl\n  Hdf5\n  Jemalloc\n  Libbacktrace\n  Libsharp\n  Libxsmm\n  OpenBlas\n  Pybind11\n  Xsimd\n  YamlCpp\n)\n\nfile(READ \"${CMAKE_SOURCE_DIR}/LICENSE.txt\" SPECTRE_MIT_LICENSE)\nfile(READ \"${CMAKE_SOURCE_DIR}/external/Licenses/SpectreGpl3License.txt\"\n      SPECTRE_GPL3_LICENSE)\nset(SPECTRE_LICENSE_CXX_STRING \"\nSpECTRE source code is distributed under the MIT License, while SpECTRE\nexecutables and libraries, generally machine code, is distributed under the GNU\nPublic License v3, or GPL3. The SpECTRE source code is freely available at\nhttps://github.com/sxs-collaboration/spectre/\n\nSpECTRE copyright for source, executables, libraries, and machine code is:\nCopyright 2017 - 2026 Simulating eXtreme Spacetimes Collaboration\n\nSpECTRE source code MIT license:\n${SPECTRE_MIT_LICENSE}\n\nSpECTRE executable, library, and machine code license:\n${SPECTRE_GPL3_LICENSE}\n\n\n\n\nThird Party Libraries:\n\")\n\n# Note: C++ compilers only need to support 65,000 characters in raw string\n# literals, so we split this over 3 strings. 1 for the SpECTRE licenses,\n# and two of the 3PLs.\nset(SPECTRE_3PL_CXX_STRING0 \"\")\nset(SPECTRE_3PL_CXX_STRING1 \"\")\n\nset(SPECTRE_3PL_DOX_STRING \"${SPECTRE_LICENSE_CXX_STRING}\")\n\nforeach(_3PL ${_LIST_OF_3PLS})\n  if(EXISTS \"${CMAKE_SOURCE_DIR}/external/Licenses/${_3PL}Copyright.txt\")\n    file(READ \"${CMAKE_SOURCE_DIR}/external/Licenses/${_3PL}Copyright.txt\"\n      _3PL_COPYRIGHT)\n  else()\n    set(_3PL_COPYRIGHT \"\")\n  endif()\n  file(READ \"${CMAKE_SOURCE_DIR}/external/Licenses/${_3PL}License.txt\"\n    _3PL_LICENSE)\n  string(COMPARE LESS ${_3PL} \"Libbacktrace\" _STRING_0_or_1)\n  if(_STRING_0_or_1)\n    set(SPECTRE_3PL_CXX_STRING0\n      \"${SPECTRE_3PL_CXX_STRING0}${_3PL}\\n${_3PL_COPYRIGHT}\\nDistributed under \\\nthe license\\n${_3PL_LICENSE}\\n\\n\\n\")\n  else()\n    set(SPECTRE_3PL_CXX_STRING1\n      \"${SPECTRE_3PL_CXX_STRING1}${_3PL}\\n${_3PL_COPYRIGHT}\\nDistributed under \\\nthe license\\n${_3PL_LICENSE}\\n\\n\\n\")\n  endif()\n  set(SPECTRE_3PL_DOX_STRING\n    \"${SPECTRE_3PL_DOX_STRING}###${_3PL}\\n${_3PL_COPYRIGHT}\\nDistributed under \\\nthe license\\n```\\n${_3PL_LICENSE}\\n```\\n\\n\\n\")\nendforeach()\n"
  },
  {
    "path": "cmake/SetupLinkTimeOptimization.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(SPECTRE_LTO \"Use link-time optimization if available\" OFF)\n\ninclude(CheckIPOSupported)\ncheck_ipo_supported(RESULT _RESULT OUTPUT _OUTPUT)\nif(_RESULT AND SPECTRE_LTO)\n  option(SPECTRE_LTO_CORES \"Number of cores to use for LTO\" OFF)\n  if (NOT SPECTRE_LTO_CORES)\n    set(SPECTRE_LTO_CORES \"auto\")\n  endif()\n  target_link_options(SpectreFlags\n    INTERFACE\n    -flto=${SPECTRE_LTO_CORES})\n  message(STATUS \"Link-time optimizations enabled with ${SPECTRE_LTO_CORES}\")\nelse()\n  target_link_options(SpectreFlags\n    INTERFACE\n    -fno-lto)\n  message(STATUS \"Link-time optimizations disabled (no LTO)\")\nendif()\n"
  },
  {
    "path": "cmake/SetupListTargets.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_custom_target(\n    list-targets\n    COMMAND\n    ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target help\n    | grep -v \" depend$\"\n    | grep -v \" edit_cache$\"\n    | grep -v \" module_\"\n    VERBATIM\n)\n\nadd_custom_target(targets COMMAND DEPENDS list-targets)\n\nadd_custom_target(list COMMAND DEPENDS list-targets)\n"
  },
  {
    "path": "cmake/SetupNinjaColors.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (\"${CMAKE_GENERATOR}\" STREQUAL \"Ninja\")\n  if (\"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"GNU\")\n    set_property(TARGET SpectreFlags\n      APPEND PROPERTY\n      INTERFACE_COMPILE_OPTIONS\n      $<$<OR:$<COMPILE_LANGUAGE:CXX>,$<COMPILE_LANGUAGE:C>>:\n      -fdiagnostics-color=always>)\n  elseif (\n      \"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"Clang\" OR\n      \"${CMAKE_CXX_COMPILER_ID}\" STREQUAL \"AppleClang\")\n    set_property(TARGET SpectreFlags\n      APPEND PROPERTY\n      INTERFACE_COMPILE_OPTIONS\n      $<$<OR:$<COMPILE_LANGUAGE:CXX>,$<COMPILE_LANGUAGE:C>>:\n      -fcolor-diagnostics>)\n  else ()\n    message(\n      WARNING \"Not sure how to get color output with\"\n      \" Ninja and compiler id ${CMAKE_CXX_COMPILER_ID}\")\n  endif ()\nendif ()\n"
  },
  {
    "path": "cmake/SetupOpenMP.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(ENABLE_OPENMP \"Enable OpenMP in some parts of the code\" OFF)\n\nif (ENABLE_OPENMP)\n  find_package(OpenMP COMPONENTS CXX)\nendif()\n"
  },
  {
    "path": "cmake/SetupPapi.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif(NOT USE_PAPI)\n  option(USE_PAPI \"Search for and include PAPI\" OFF)\nendif()\n\n# Do not set up PAPI again if it was already done once\nif(USE_PAPI AND NOT TARGET Papi)\n  find_package(PAPI REQUIRED)\n  add_library(Papi INTERFACE IMPORTED)\n  set_property(TARGET Papi\n    APPEND PROPERTY\n    INTERFACE_INCLUDE_DIRECTORIES ${PAPI_INCLUDE_DIRS})\n  set_property(TARGET Papi\n    APPEND PROPERTY\n    INTERFACE_COMPILE_DEFINITIONS \"SPECTRE_USE_PAPI\")\n  set_property(TARGET Papi\n    APPEND PROPERTY\n    INTERFACE_LINK_LIBRARIES ${PAPI_LIBRARIES})\n\n  set_property(\n    GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n    Papi\n    )\nendif(USE_PAPI AND NOT TARGET Papi)\n"
  },
  {
    "path": "cmake/SetupParaView.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(ENABLE_PARAVIEW \"Try to find ParaView to enable 3D rendering tools\" OFF)\n\nif (NOT ENABLE_PARAVIEW)\n  return()\nendif()\n\nfind_package(ParaView REQUIRED)\n\n# Help `find_python_module` find ParaView\nif (PARAVIEW_PYTHONPATH)\n  set(PY_paraview_LOCATION ${PARAVIEW_PYTHONPATH}/paraview)\nendif()\n"
  },
  {
    "path": "cmake/SetupPch.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Support for precompiled headers (PCH)\n# =====================================\n#\n# Precompiled headers are a way to speed up compilation by precompiling\n# frequently used headers.\n#\n# We have the following requirements on the PCH:\n# - it must be rebuilt whenever anything it includes is changed.\n# - it must be removed when running `make clean`\n# - it must be generated before any object files are built that\n#   use it\n# - it must be able to use flags from other targets (since in modern CMake\n#   flags, etc. are propagated via targets)\n# - the correct flags to include the PCH must be propagated to any targets\n#   that use the PCH.\n#\n# We use CMake's PCH support to accomplish this. It is documented here:\n# https://cmake.org/cmake/help/latest/command/target_precompile_headers.html.\n#\n# The PCH is generated by compiling a source file that includes all the headers\n# we want to precompile. These precompiled headers are listed in\n# `tools/SpectrePch.hpp`.\n#\n# Instructions for using the PCH:\n# -------------------------------\n#\n# Targets can use the PCH generated for the `SpectrePch` library. They must also\n# link the `SpectrePchFlags` library so they compile with the same flags as the\n# PCH. Make sure to check the PCH exist before using them:\n#\n#     if(TARGET SpectrePch)\n#       target_precompile_headers(${TARGET_NAME} REUSE_FROM SpectrePch)\n#       target_link_libraries(${TARGET_NAME} PRIVATE SpectrePchFlags)\n#     endif()\n\n# This library tracks all compiler flags to build the PCH. Targets that use\n# the PCH must link this library so they compile with the same flags as the\n# PCH.\nadd_library(SpectrePchFlags INTERFACE)\ntarget_link_libraries(\n  SpectrePchFlags\n  INTERFACE\n  Blaze\n  Brigand\n  Charmxx::charmxx\n  Charmxx::pup\n  HDF5::HDF5\n  SpectreFlags\n  SpectreKokkos\n  )\n\n# Targets can reuse the PCH generated for this library. They must also link\n# the `SpectrePchFlags` library. Notes:\n# - We store the PCH header in ${CMAKE_SOURCE_DIR}/tools so that it is not\n#   accidentally included anywhere.\n# - We also store a source file that just includes the PCH header so the\n#   library has something to compile.\n# - This is an object library because we don't need to actually link it, we\n#   only need to build it so it generates the PCH.\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/tools/SpectrePch.hpp\n  ${CMAKE_BINARY_DIR}\n  )\nset(SPECTRE_PCH_HEADER ${CMAKE_BINARY_DIR}/SpectrePch.hpp)\nfile(GENERATE\n  OUTPUT ${CMAKE_BINARY_DIR}/SpectrePch.cpp\n  CONTENT \"#include \\\"${SPECTRE_PCH_HEADER}\\\"\\n\")\nadd_library(\n  SpectrePch\n  OBJECT\n  ${CMAKE_BINARY_DIR}/SpectrePch.cpp)\ntarget_precompile_headers(\n  SpectrePch\n  PRIVATE\n  ${SPECTRE_PCH_HEADER}\n  )\ntarget_link_libraries(\n  SpectrePch\n  PRIVATE\n  SpectrePchFlags\n  )\n"
  },
  {
    "path": "cmake/SetupPic.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Set up position independent code by default since this is required\n# for our python libraries.\n#\n# We cannot use CMake's set(CMAKE_POSITION_INDEPENDENT_CODE ON)\n# because that enables -fPIC in shared libraries and -fPIE on executables.\n# While what CMake does is technically the optimal thing to do, it means\n# we would need to generate two PCHs, one built with -fPIC and one with\n# -fPIE. More specifically, we'd need to generate one using a library target\n# and one using an executable target, which would double the amount of code\n# in SetupPch.cmake for a currently unknown benefit.\nset_property(TARGET SpectreFlags\n  APPEND PROPERTY\n  INTERFACE_COMPILE_OPTIONS\n  $<$<COMPILE_LANGUAGE:C>:-fPIC>\n  $<$<COMPILE_LANGUAGE:CXX>:-fPIC>\n  $<$<COMPILE_LANGUAGE:Fortran>:-fPIC>)\n"
  },
  {
    "path": "cmake/SetupProfiling.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(ENABLE_PROFILING \"Enables various options to make profiling easier\" OFF)\n\noption(KEEP_FRAME_POINTER \"Add keep frame pointer for profiling\" OFF)\n\nadd_library(Profiling::KeepFramePointer IMPORTED INTERFACE)\nadd_library(Profiling::EnableProfiling IMPORTED INTERFACE)\n\nif (KEEP_FRAME_POINTER OR ENABLE_PROFILING)\n  set_property(\n    TARGET Profiling::KeepFramePointer\n    APPEND PROPERTY\n    INTERFACE_COMPILE_OPTIONS\n    $<$<COMPILE_LANGUAGE:CXX>:-fno-omit-frame-pointer>\n    $<$<COMPILE_LANGUAGE:CXX>:-mno-omit-leaf-frame-pointer>\n    )\nendif()\n\nif (ENABLE_PROFILING)\n  set_property(\n    TARGET Profiling::EnableProfiling\n    APPEND PROPERTY\n    INTERFACE_COMPILE_DEFINITIONS\n    $<$<COMPILE_LANGUAGE:CXX>:SPECTRE_PROFILING>\n    )\nendif()\n\ntarget_link_libraries(\n  SpectreFlags\n  INTERFACE\n  Profiling::EnableProfiling\n  Profiling::KeepFramePointer\n  )\n"
  },
  {
    "path": "cmake/SetupPybind11.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(BUILD_PYTHON_BINDINGS \"Build the python bindings for SpECTRE\" ON)\n\nif(BUILD_PYTHON_BINDINGS)\n  # Make sure to find Python first so it's consistent with pybind11\n  find_package(Python REQUIRED COMPONENTS Interpreter Development)\n\n  # Try to find the pybind11-config tool to find pybind11's CMake config files\n  find_program(PYBIND11_CONFIG_TOOL pybind11-config)\n  set(PYBIND11_CMAKEDIR \"\")\n  if(PYBIND11_CONFIG_TOOL)\n    execute_process(\n      COMMAND \"${PYBIND11_CONFIG_TOOL}\" \"--cmakedir\"\n      OUTPUT_VARIABLE PYBIND11_CMAKEDIR\n      OUTPUT_STRIP_TRAILING_WHITESPACE)\n    message(STATUS \"Found pybind11-config tool (${PYBIND11_CONFIG_TOOL}) and \"\n      \"determined CMake dir: ${PYBIND11_CMAKEDIR}\")\n  endif()\n\n  find_package(pybind11 2.7.0 REQUIRED\n    HINTS\n    ${PYBIND11_CMAKEDIR}\n    ${SPECTRE_PYTHON_SITE_PACKAGES}\n    ${Python_SITEARCH}\n    ${Python_SITELIB}\n    ${Python_STDARCH}\n    ${Python_STDLIB}\n    )\n\n  set_property(\n    GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n    pybind11::headers\n    )\n\n  message(STATUS \"Pybind11 include: ${pybind11_INCLUDE_DIR}\")\nendif()\n"
  },
  {
    "path": "cmake/SetupPypp.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# To run Python tests we need the development component (libs and include dirs).\n# It provides the `Python::Python` imported target. We find the interpreter\n# component as well to make sure the find is consistent with earlier finds that\n# only looked for the interpreter, possibly guided by the Python_EXECUTABLE\n# variable set by the user.\nfind_package(Python REQUIRED COMPONENTS Interpreter Development)\n\n# CMake's FindPython has trouble finding NumPy when it isn't installed in the\n# Python executable's directory, and can't be given hints either (as of CMake\n# version 3.25). So we find NumPy ourselves (which isn't much different to\n# what FindPython does internally anyway).\nfind_package(NumPy 1.10 REQUIRED)\n\nmessage(STATUS \"NumPy incl: \" ${NUMPY_INCLUDE_DIRS})\nmessage(STATUS \"NumPy vers: \" ${NUMPY_VERSION})\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"NumPy version: ${NUMPY_VERSION}\\n\"\n  )\n\n# Also check that SciPy is installed\ninclude(FindPythonModule)\nfind_python_module(scipy REQUIRED)\n\nadd_library(Python::NumPy INTERFACE IMPORTED)\nset_property(TARGET Python::NumPy PROPERTY\n  INTERFACE_INCLUDE_DIRECTORIES ${NUMPY_INCLUDE_DIRS})\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  Python::NumPy Python::Python\n  )\n"
  },
  {
    "path": "cmake/SetupSanitizers.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(ASAN \"Add AddressSanitizer compile flags\" OFF)\n\n# We handle the sanitizers using targets for the compile options, but modify\n# CMAKE_EXE_LINKER_FLAGS for the linker flags because the sanitizers should\n# only be linked into the final executable and CMake doesn't support linker\n# interface flags before CMake 3.13.\nadd_library(Sanitizers IMPORTED INTERFACE)\nadd_library(Sanitizers::Address IMPORTED INTERFACE)\nadd_library(Sanitizers::UbInteger IMPORTED INTERFACE)\nadd_library(Sanitizers::UbUndefined IMPORTED INTERFACE)\n\nif (ASAN)\n  set_property(\n    TARGET Sanitizers::Address\n    APPEND PROPERTY\n    INTERFACE_COMPILE_OPTIONS\n    $<$<COMPILE_LANGUAGE:CXX>:-fno-omit-frame-pointer -fsanitize=address>\n    )\n  set(\n    CMAKE_EXE_LINKER_FLAGS\n    \"${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address\"\n    )\nendif ()\n\noption(UBSAN_UNDEFINED \"Add UBSan undefined behavior compile flags\" OFF)\nif (UBSAN_UNDEFINED)\n  set_property(\n    TARGET Sanitizers::UbUndefined\n    APPEND PROPERTY\n    INTERFACE_COMPILE_OPTIONS\n    $<$<COMPILE_LANGUAGE:CXX>:-fno-omit-frame-pointer -fsanitize=undefined>\n    )\n  set(\n    CMAKE_EXE_LINKER_FLAGS\n    \"${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined\"\n    )\nendif()\n\noption(UBSAN_INTEGER \"Add UBSan unsigned integer overflow compile flags\" OFF)\nif (UBSAN_INTEGER)\n  set_property(\n    TARGET Sanitizers::UbInteger\n    APPEND PROPERTY\n    INTERFACE_COMPILE_OPTIONS\n    $<$<COMPILE_LANGUAGE:CXX>:-fno-omit-frame-pointer -fsanitize=integer>\n    )\n  set(\n    CMAKE_CXX_FLAGS\n    \"${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer  -fsanitize=integer\"\n    )\n  set(\n    CMAKE_EXE_LINKER_FLAGS\n    \"${CMAKE_EXE_LINKER_FLAGS} -fsanitize=integer\"\n    )\nendif()\n\ntarget_link_libraries(\n  SpectreFlags\n  INTERFACE\n  Sanitizers::Address\n  Sanitizers::UbInteger\n  Sanitizers::UbUndefined\n  )\n"
  },
  {
    "path": "cmake/SetupSpec.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(SpEC)\n\n# Make SpEC scripts available in Python. These can be used until we have ported\n# them to SpECTRE.\nif (SPEC_ROOT)\n  set(PYTHONPATH \"${SPEC_ROOT}/Support/Python:\\\n${SPEC_ROOT}/Support/DatDataManip:${PYTHONPATH}\")\nendif()\n\nif (NOT SpEC_FOUND)\n  return()\nendif()\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"SpEC exporter: ${SPEC_EXPORTER_ROOT}\\n\"\n  )\n"
  },
  {
    "path": "cmake/SetupSpectreInlining.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(SPECTRE_USE_ALWAYS_INLINE \"Force SpECTRE inlining.\" ON)\n\nset(_SPECTRE_USE_ALWAYS_INLINE 0)\n\nif(SPECTRE_USE_ALWAYS_INLINE)\n  set(_SPECTRE_USE_ALWAYS_INLINE 1)\nendif()\n\nset_property(\n  TARGET SpectreFlags\n  APPEND PROPERTY\n  INTERFACE_COMPILE_DEFINITIONS\n  $<$<COMPILE_LANGUAGE:CXX>:SPECTRE_USE_ALWAYS_INLINE=${_SPECTRE_USE_ALWAYS_INLINE}>\n)\n"
  },
  {
    "path": "cmake/SetupSphinx.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Setup Sphinx to generate documentation for our Python modules. The HTML output\n# is placed in docs/html/py/ so we can link to it from Doxygen.\n\n# We have also experimented with generating our C++ documentation with Sphinx.\n# See issue: https://github.com/sxs-collaboration/spectre/issues/2138\n\nset(SPHINX_SOURCE ${CMAKE_BINARY_DIR}/docs/sphinx)\nset(SPHINX_BUILD ${CMAKE_BINARY_DIR}/docs/html/py)\n\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/docs/conf.py\n  ${SPHINX_SOURCE}/conf.py\n  @ONLY\n  )\n\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/docs/index.rst\n  ${SPHINX_SOURCE}/index.rst\n  )\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/docs/cli.rst\n  ${SPHINX_SOURCE}/cli.rst\n  )\n\nfile(\n  COPY ${CMAKE_SOURCE_DIR}/docs/_templates\n  DESTINATION ${SPHINX_SOURCE}\n)\n\nadd_custom_target(py-docs\n  COMMAND\n  ${CMAKE_COMMAND} -E env\n  ${CMAKE_BINARY_DIR}/bin/python-spectre -m sphinx -b html\n  ${SPHINX_SOURCE} ${SPHINX_BUILD}\n  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n  COMMENT \"Generating Python documentation\"\n  )\nadd_dependencies(py-docs all-pybindings)\n"
  },
  {
    "path": "cmake/SetupStl.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Create an C++ standard library target for tracking dependencies\n# through includes throughout SpECTRE.\nif(NOT TARGET Stl)\n  add_library(Stl INTERFACE IMPORTED)\n\n  add_interface_lib_headers(\n    TARGET Stl\n    HEADERS\n    algorithm\n    any\n    array\n    atomic\n    barrier\n    bit\n    bitset\n    cassert\n    cctype\n    cerrno\n    cfenv\n    cfloat\n    charconv\n    chrono\n    cinttypes\n    climits\n    clocale\n    cmath\n    codecvt\n    compare\n    complex\n    concepts\n    condition_variable\n    coroutine\n    csetjmp\n    csignal\n    cstdarg\n    cstddef\n    cstdint\n    cstdio\n    cstdlib\n    cstring\n    ctime\n    cuchar\n    cwchar\n    cwctype\n    deque\n    exception\n    execution\n    format\n    forward_list\n    fstream\n    functional\n    future\n    initializer_list\n    iomanip\n    ios\n    iosfwd\n    iostream\n    istream\n    iterator\n    latch\n    limits\n    list\n    locale\n    map\n    memory\n    memory_resource\n    mutex\n    new\n    numbers\n    numeric\n    optional\n    ostream\n    queue\n    random\n    ranges\n    ratio\n    regex\n    scoped_allocator\n    semaphore\n    set\n    shared_mutex\n    source_location\n    span\n    sstream\n    stack\n    stdexcept\n    stop_token\n    streambuf\n    string\n    string_view\n    strstream\n    syncstream\n    system_error\n    thread\n    tuple\n    type_traits\n    typeindex\n    typeinfo\n    unordered_map\n    unordered_set\n    utility\n    valarray\n    variant\n    vector\n    version\n\n    # UNIX/Linux specific headers\n    dirent.h\n    libgen.h\n    sys/stat.h\n    sys/types.h\n    unistd.h\n    xmmintrin.h\n    )\n\n  set_property(\n    GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n    Stl\n    )\nendif(NOT TARGET Stl)\n"
  },
  {
    "path": "cmake/SetupTcmalloc.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(TCMALLOC REQUIRED)\n\nmessage(STATUS \"tcmalloc libs: \" ${TCMALLOC_LIBRARIES})\nmessage(STATUS \"tcmalloc incl: \" ${TCMALLOC_INCLUDE_DIRS})\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"tcmalloc version: ${TCMALLOC_VERSION}\\n\"\n  )\n\nadd_library(Tcmalloc INTERFACE IMPORTED)\nset_property(TARGET Tcmalloc\n  APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${TCMALLOC_LIBRARIES})\nset_property(TARGET Tcmalloc PROPERTY\n  INTERFACE_INCLUDE_DIRECTORIES ${TCMALLOC_INCLUDE_DIRS})\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  Tcmalloc\n  )\n"
  },
  {
    "path": "cmake/SetupXsimd.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# To turn this off, one should provide an alternative\n# SIMD backend that supports elementary functions\n# or the auto-differentiation in SpECTRE ceases to\n# work\noption(USE_XSIMD \"Use xsimd if it is available\" ON)\n\nif(USE_XSIMD)\n  find_package(xsimd QUIET)\n\n  if (NOT xsimd_FOUND)\n    if (NOT SPECTRE_FETCH_MISSING_DEPS)\n      message(FATAL_ERROR \"Could not find xsimd. If you want to fetch \"\n        \"missing dependencies automatically, set SPECTRE_FETCH_MISSING_DEPS=ON.\")\n    endif()\n\n    message(STATUS \"Fetching xsimd\")\n    include(FetchContent)\n    FetchContent_Declare(xsimd\n        GIT_REPOSITORY https://github.com/xtensor-stack/xsimd\n        GIT_TAG 13.2.0\n        ${SPECTRE_FETCHCONTENT_BASE_ARGS}\n    )\n    FetchContent_MakeAvailable(xsimd)\n    set(xsimd_INCLUDE_DIRS ${xsimd_SOURCE_DIR}/include)\n    set(xsimd_VERSION \"13.2.0\")\n    if (NOT CMAKE_VERSION VERSION_GREATER_EQUAL 3.25)\n      get_target_property(XSIMD_IID xsimd INTERFACE_INCLUDE_DIRECTORIES)\n      set_target_properties(xsimd PROPERTIES INTERFACE_SYSTEM_INCLUDE_DIRECTORIES \"${XSIMD_IID}\")\n    endif()\n  endif()\n\n  if(xsimd_VERSION VERSION_LESS 11.0.1)\n    message(FATAL_ERROR \"xsimd must be at least 11.0.1, got ${xsimd_VERSION}\")\n  endif()\n\n  message(STATUS \"xsimd incld: ${xsimd_INCLUDE_DIRS}\")\n  message(STATUS \"xsimd vers: ${xsimd_VERSION}\")\n\n  file(APPEND\n    \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n    \"xsimd version: ${xsimd_VERSION}\\n\"\n    )\n\n  add_interface_lib_headers(\n    TARGET xsimd\n    HEADERS\n    xsimd/xsimd.hpp\n    )\n\n  # As long as we want xsimd support to be optional we need to be\n  # able to figure out if we have it available.\n  #\n  # To enable use with Blaze, add:\n  #   -DBLAZE_USE_XSIMD=1\n  target_compile_definitions(\n    xsimd\n    INTERFACE\n    $<$<COMPILE_LANGUAGE:CXX>:SPECTRE_USE_XSIMD>\n    )\n\n  set_property(\n    GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n    xsimd\n    )\n\n  # Currently we still have some compatibility bugs to sort out between Blaze\n  # and xsimd. Once that's done we will enable combining the two.\n  #\n  # target_link_libraries(\n  #   Blaze\n  #   INTERFACE\n  #   xsimd\n  #   )\nendif()\n"
  },
  {
    "path": "cmake/SetupYamlCpp.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_package(YAML_CPP NAMES yaml-cpp)\n\nif (NOT YAML_CPP_FOUND)\n  if (NOT SPECTRE_FETCH_MISSING_DEPS)\n    message(FATAL_ERROR \"Could not find yaml-cpp. If you want to fetch \"\n      \"missing dependencies automatically, set SPECTRE_FETCH_MISSING_DEPS=ON.\")\n  endif()\n  message(STATUS \"Fetching yaml-cpp\")\n  include(FetchContent)\n  FetchContent_Declare(yaml-cpp\n    GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git\n    GIT_TAG 0.8.0\n    GIT_SHALLOW TRUE\n    ${SPECTRE_FETCHCONTENT_BASE_ARGS}\n  )\n  FetchContent_MakeAvailable(yaml-cpp)\nendif()\n\n# New versions of yaml-cpp define the target `yaml-cpp::yaml-cpp`. Old versions\n# define the deprecated target `yaml-cpp`.\nif (NOT TARGET yaml-cpp::yaml-cpp)\n  add_library(yaml-cpp::yaml-cpp INTERFACE IMPORTED)\n  target_link_libraries(yaml-cpp::yaml-cpp INTERFACE yaml-cpp)\nendif()\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  yaml-cpp::yaml-cpp\n  )\n"
  },
  {
    "path": "cmake/SpectreAddCatchTests.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n#\n# This file contains functions used by CMake to add Catch tests to CTest.\n#\n# Catch is a testing framework available on Github:\n# https://github.com/philsquared/Catch\n# It supports a variety of different styles of tests including BDD and fixture\n# tests.\n#\n# Usage\n# =====\n# To run the tests, type 'ctest' in the build directory. You can specify\n# a regex to match the test name using 'ctest -R Unit.Blah', or run all\n# tests with a certain tag using 'ctest -L tag'\n#\n# Attributes\n# ==========\n# Attributes allow you to modify properties of the test. Attributes are\n# specified as follows:\n# // [[TimeOut, 10]]\n# // [[OutputRegex, A regular expression that is expected to match the output\n# //   from the test]]\n# SPECTRE_TEST_CASE(\"Unit.Blah\", \"[Unit]\") {\n#\n# Note the space after the comma!\n#\n# Available attributes are:\n# TimeOut - override the default timeout and set the timeout to N seconds. This\n#           should be set very sparingly since unit tests are designed to be\n#           short. If your test is too long you should consider testing smaller\n#           portions of the code if possible, or writing an integration test\n#           instead.\n# OutputRegex - When testing failure modes the exact error message must be\n#               tested, not just that the test failed. Since the string passed\n#               is a regular expression you must escape any regex tokens. For\n#               example, to match \"some (word) and\" you must specify the\n#               string \"some \\(word\\) and\".\n\nadd_custom_target(unit-tests)\n\nspectre_define_test_timeout_factor_option(UNIT \"unit\")\n\n# Environment variables for test\nset(_CATCH_TEST_ENV_VARS \"\")\n# - Disable ASAN's leak sanitizer because Charm++ has false positives\nlist(APPEND _CATCH_TEST_ENV_VARS \"ASAN_OPTIONS=detect_leaks=0\")\n# - Set PYTHONPATH to find Python modules\nlist(APPEND _CATCH_TEST_ENV_VARS \"PYTHONPATH=${PYTHONPATH}\")\n\n# Main function - the only one designed to be called from outside this module.\nfunction(spectre_add_catch_tests TEST_TARGET)\n  get_target_property(SOURCE_FILES ${TEST_TARGET} SOURCES)\n  list(FILTER SOURCE_FILES EXCLUDE REGEX \"TestMain.*$\")\n  # For each of the source files, we use spectre_parse_file to find all the\n  # Catch tests inside the source file and add them to CTest.\n  # We ignore the Charm++ generated header files.\n\n  file(MAKE_DIRECTORY \"${CMAKE_BINARY_DIR}/tmp\")\n\n  unset(ABSOLUTE_SOURCE_FILES)\n  foreach (SOURCE_FILE ${SOURCE_FILES})\n    string(REGEX MATCH \".*(decl.h|def.h)\" CHARM_INTERFACE_FILE\n      \"${SOURCE_FILE}\")\n    if (NOT CHARM_INTERFACE_FILE)\n      if (NOT IS_ABSOLUTE ${SOURCE_FILE})\n        set(SOURCE_FILE ${CMAKE_CURRENT_LIST_DIR}/${SOURCE_FILE})\n      endif()\n      set(ABSOLUTE_SOURCE_FILES \"${ABSOLUTE_SOURCE_FILES};${SOURCE_FILE}\")\n    endif()\n  endforeach()\n\n  execute_process(\n    COMMAND\n    ${Python_EXECUTABLE}\n    ${CMAKE_SOURCE_DIR}/cmake/SpectreParseTests.py\n    ${ABSOLUTE_SOURCE_FILES}\n    WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/tmp\n    RESULT_VARIABLE PARSED_TESTS_SUCCESSFULLY)\n  if (${PARSED_TESTS_SUCCESSFULLY} GREATER 0)\n    message(FATAL_ERROR \"Failed to parse test files\")\n  endif()\n\n  set_property(GLOBAL PROPERTY SPECTRE_FAILURE_TESTS_PROPERTY \"\")\n\n  foreach (SOURCE_FILE ${ABSOLUTE_SOURCE_FILES})\n    spectre_parse_file(${SOURCE_FILE} ${TEST_TARGET})\n  endforeach ()\n\n  get_property(\n    SPECTRE_FAILURE_TESTS\n    GLOBAL\n    PROPERTY\n    SPECTRE_FAILURE_TESTS_PROPERTY)\n  set_property(GLOBAL PROPERTY SPECTRE_FAILURE_TESTS_PROPERTY \"\")\n\n  # Generate shell script that runs all non-failure tests manually\n  string(REPLACE \";\" \" \" SPECTRE_FAILURE_TESTS \"${SPECTRE_FAILURE_TESTS}\")\n  file(WRITE\n    \"${CMAKE_BINARY_DIR}/tmp/NonFailureTests${TEST_TARGET}.sh\"\n    \"#!/bin/bash -e\\n\"\n    \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_TARGET} ${SPECTRE_FAILURE_TESTS}\")\n  file(COPY\n    \"${CMAKE_BINARY_DIR}/tmp/NonFailureTests${TEST_TARGET}.sh\"\n    DESTINATION \"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}\"\n    FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE\n    )\n  file(REMOVE \"${CMAKE_BINARY_DIR}/tmp/NonFailureTests${TEST_TARGET}.sh\")\nendfunction()\n\n# Parses the cpp file and extracts the tests specified in it. Each test is then\n# added to CTest, and the run command is adjusted according to timeout,\n# willfail, and serialization needs.\nfunction(spectre_parse_file SOURCE_FILE TEST_TARGET)\n  if (NOT EXISTS ${SOURCE_FILE})\n    message(WARNING \"Could not find source file:\\n\\\"${SOURCE_FILE}\\\"\\nfor \\\n        tests\\n.\")\n    return()\n  endif ()\n\n  file(STRINGS ${SOURCE_FILE} CONTENTS NEWLINE_CONSUME)\n\n  # Remove commented out tests so they are not added to ctest\n  string(REGEX REPLACE \"\\n[ \\t]*//+[^\\n]+\" \"\\n\" CONTENTS \"${CONTENTS}\")\n\n  # The regex searches for SPECTRE_TEST_CASE_METHOD, SCENARIO and SPECTRE_TEST_CASE\n  # to find tests that need to be added.  TESTS will be a list of all tests\n  # found in the SOURCE_FILE.\n  string(REGEX MATCHALL\n    \"(CATCH_)?(SPECTRE_TEST_CASE_METHOD|SCENARIO|SPECTRE_TEST_CASE)[ \\t]*\\\\([^\\)]+\\\\)[ \\t]*{\"\n    TESTS\n    \"${CONTENTS}\")\n\n  set(FAILURE_TESTS \"\")\n  foreach (TEST_NAME ${TESTS})\n    # Get test type and fixture if applicable\n    string(REGEX MATCH \"(CATCH_)?(SPECTRE_TEST_CASE_METHOD|SCENARIO|SPECTRE_TEST_CASE)\"\n      TEST_TYPE \"${TEST_NAME}\")\n\n    # Get string parts of test definition\n    string(REGEX MATCHALL \"\\\"[^\\\"]+\\\"\" TEST_STRINGS \"${TEST_NAME}\")\n\n    # Strip wrapping quotation marks of each element of the list\n    # TEST_STRINGS\n    string(REGEX REPLACE \"^\\\"(.*)\\\"$\" \"\\\\1\" TEST_STRINGS \"${TEST_STRINGS}\")\n    # If a test name went on multiple lines, there will either be a sequence\n    # .\";\" or \";\". from the line break (after replacing all the whitespace with\n    # ;) Remove these from the test string\n    string(REPLACE \".\\\";\\\"\" \".\" TEST_STRINGS \"${TEST_STRINGS}\")\n    string(REPLACE \"\\\";\\\".\" \".\" TEST_STRINGS \"${TEST_STRINGS}\")\n    # Remove the last set of quotation marks\n    string(REPLACE \"\\\";\\\"\" \";\" TEST_STRINGS \"${TEST_STRINGS}\")\n\n    # Validate that a test name and tags have been provided\n    list(LENGTH TEST_STRINGS TEST_STRINGS_LENGTH)\n    if (NOT TEST_STRINGS_LENGTH EQUAL 2)\n      message(FATAL_ERROR\n        \"You must provide a valid test name and tags \"\n        \"for all tests in ${SOURCE_FILE}. Cannot use the test:\\n\"\n        \"\\\"${TEST_STRINGS}\\\"\\n\")\n    endif ()\n\n    # Assign name and tags\n    list(GET TEST_STRINGS 0 NAME)\n    if (\"${TEST_TYPE}\" STREQUAL \"SCENARIO\")\n      set(NAME \"Scenario: ${NAME}\")\n    endif ()\n    set(CTEST_NAME \"${NAME}\")\n\n    # Gets the TAGS of the test which is element 1 of TEST_STRINGS,\n    # strips the enclosing brackets, and makes a list\n    list(GET TEST_STRINGS 1 TAGS)\n    string(TOLOWER \"${TAGS}\" TAGS)\n    string(REPLACE \"]\" \";\" TAGS \"${TAGS}\")\n    string(REPLACE \"[\" \"\" TAGS \"${TAGS}\")\n\n    # These files are generated by the SpectreParseTests.py\n    if(NOT EXISTS \"${CMAKE_BINARY_DIR}/tmp/${CTEST_NAME}.output_regex\")\n      message(FATAL_ERROR\n        \"Failed to parse out the test ${TEST_NAME} in the file ${SOURCE_FILE}.\"\n        \" This means the python script cmake/SpectreParseTests.py failed to \"\n        \"correctly identify the tests.\")\n    endif()\n    file(READ \"${CMAKE_BINARY_DIR}/tmp/${CTEST_NAME}.output_regex\" OUTPUT_REGEX)\n    file(REMOVE \"${CMAKE_BINARY_DIR}/tmp/${CTEST_NAME}.output_regex\")\n    file(READ \"${CMAKE_BINARY_DIR}/tmp/${CTEST_NAME}.timeout\" TIMEOUT)\n    file(REMOVE \"${CMAKE_BINARY_DIR}/tmp/${CTEST_NAME}.timeout\")\n\n    # The default TIMEOUT is set to -1, but overwritten by each type\n    # of test or a specified TIMEOUT attribute for a given test.\n    # Thus we can use it to check if each test has been tagged by an\n    # appropriate type.\n    if (TIMEOUT EQUAL -1)\n      message(FATAL_ERROR\n        \"You must set at least one tag of value [unit] \"\n        \"for \\\"${NAME}\\\" in ${SOURCE_FILE}\\n\")\n    endif ()\n\n    # Triple timeout if address sanitizer is enabled.\n    if (ASAN)\n      math(EXPR TIMEOUT \"3 * ${TIMEOUT}\")\n    endif()\n\n    spectre_test_timeout(TIMEOUT UNIT ${TIMEOUT})\n\n    # Add the test and set its properties\n    # - Skip benchmarks during unit testing\n    add_test(NAME ${CTEST_NAME}\n      COMMAND ${SPECTRE_TEST_RUNNER} $<TARGET_FILE:${TEST_TARGET}>\n      \\\"${NAME}\\\" --durations yes\n      --warn NoAssertions\n      --skip-benchmarks\n      --name \"\\\"$<CONFIGURATION>.${CTEST_NAME}\\\"\")\n\n    # Check if the test is supposed to fail. If so then let ctest know\n    # that a failed test is actually a pass.\n    if (NOT \"${OUTPUT_REGEX}\" STREQUAL \"\")\n      if (NOT \"${CMAKE_BUILD_TYPE}\" STREQUAL \"Debug\")\n        set(OUTPUT_REGEX\n          \"${OUTPUT_REGEX}|### No ASSERT tests in release mode ###\")\n      endif()\n      set_tests_properties(\n        ${CTEST_NAME} PROPERTIES\n        FAIL_REGULAR_EXPRESSION \"No tests ran\"\n        TIMEOUT ${TIMEOUT}\n        PASS_REGULAR_EXPRESSION \"${OUTPUT_REGEX}\"\n        LABELS \"${TAGS}\"\n        ENVIRONMENT \"${_CATCH_TEST_ENV_VARS}\")\n      set(FAILURE_TESTS \"\\\"~${CTEST_NAME}\\\";${FAILURE_TESTS}\")\n    else ()\n      set_tests_properties(\n        ${CTEST_NAME} PROPERTIES\n        FAIL_REGULAR_EXPRESSION \"No tests ran\"\n        TIMEOUT ${TIMEOUT}\n        LABELS \"${TAGS}\"\n        ENVIRONMENT \"${_CATCH_TEST_ENV_VARS}\")\n    endif ()\n  endforeach ()\n  set_property(GLOBAL PROPERTY SPECTRE_FAILURE_TESTS ${FAILURE_TESTS})\n  get_property(\n    SPECTRE_FAILURE_TESTS\n    GLOBAL\n    PROPERTY\n    SPECTRE_FAILURE_TESTS_PROPERTY)\n  set(SPECTRE_FAILURE_TESTS\n    \"${SPECTRE_FAILURE_TESTS};${FAILURE_TESTS}\")\n  set_property(GLOBAL\n    PROPERTY\n    SPECTRE_FAILURE_TESTS_PROPERTY\n    ${SPECTRE_FAILURE_TESTS})\nendfunction()\n"
  },
  {
    "path": "cmake/SpectreAddInterfaceLibraryHeaders.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Adds the header files to the target.\n#\n# Usage:\n#\n#   add_interface_lib_headers(\n#     TARGET TARGET_NAME\n#     HEADERS\n#     A.hpp\n#     B.hpp\n#     C.hpp\n#     )\n#\n# This function is intended to be used with libraries added using add_library\n# or added by CMake's provided find_package (e.g. Boost). The\n# add_spectre_library handles adding header files for targets correctly and\n# so this function does not need to be used for libraries added with\n# add_spectre_library.\nfunction(add_interface_lib_headers)\n  cmake_parse_arguments(\n    ARG \"\" \"TARGET\" \"HEADERS\"\n    ${ARGN})\n\n  if(NOT TARGET ${ARG_TARGET})\n    message(FATAL_ERROR\n      \"Unknown target '${ARG_TARGET}'\"\n      )\n  endif(NOT TARGET ${ARG_TARGET})\n\n  get_target_property(\n    TARGET_TYPE\n    ${ARG_TARGET}\n    TYPE\n    )\n  if(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    message(FATAL_ERROR\n      \"The target '${ARG_TARGET}' is not an INTERFACE library and so \"\n      \"add_interface_lib_headers should not be used to add header files \"\n      \"to it.\"\n      )\n  endif(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n\n  get_property(\n    SPECTRE_INTERFACE_LIBRARY_HEADERS\n    GLOBAL\n    PROPERTY SPECTRE_INTERFACE_LIBRARY_HEADERS\n    )\n\n  # Switch to delimiting the headers by ':' because CMake uses ';' to delimit\n  # elements of a list.\n  string(REPLACE \";\" \":\" TARGET_HEADERS\n    \"${ARG_TARGET}=${ARG_HEADERS}\")\n\n  # BEGIN ADD EXISTING HEADERS\n  # This block checks if the current target already has a list of header files\n  # associated with it. If so, then we parse the current headers from the\n  # string SPECTRE_INTERFACE_LIBRARY_HEADERS. Once we've parsed out the\n  # _PREVIOUS_HEADERS we append them to the list of current headers,\n  # (TARGET_HEADERS). Finally, we remove the current target from the interface\n  # libaries string (SPECTRE_INTERFACE_LIBRARY_HEADERS) because we add it with\n  # the new headers later.\n  set(_PREVIOUS_HEADERS \"\")\n  string(FIND \"${SPECTRE_INTERFACE_LIBRARY_HEADERS}\" \"${ARG_TARGET}=\"\n    _POSITION_OF_TARGET)\n  if (NOT ${_POSITION_OF_TARGET} EQUAL -1)\n    string(SUBSTRING \"${SPECTRE_INTERFACE_LIBRARY_HEADERS}\"\n      ${_POSITION_OF_TARGET} -1 _PREVIOUS_HEADERS)\n    string(FIND \"${_PREVIOUS_HEADERS}\" \"=\"\n      POSITION_OF_THIS_EQUALS)\n    math(EXPR POSITION_OF_THIS_EQUALS_PLUS_ONE\n      \"${POSITION_OF_THIS_EQUALS} + 1\")\n    string(SUBSTRING \"${_PREVIOUS_HEADERS}\" ${POSITION_OF_THIS_EQUALS_PLUS_ONE}\n      -1 _PREVIOUS_HEADERS)\n    string(FIND \"${_PREVIOUS_HEADERS}\" \"=\"\n      POSITION_OF_NEXT_EQUALS)\n    string(SUBSTRING \"${_PREVIOUS_HEADERS}\" 0 ${POSITION_OF_NEXT_EQUALS}\n      _PREVIOUS_HEADERS)\n    string(FIND \"${_PREVIOUS_HEADERS}\" \";\" POSITION_OF_LAST_SEMI REVERSE)\n    string(SUBSTRING \"${_PREVIOUS_HEADERS}\" 0 ${POSITION_OF_LAST_SEMI}\n      _PREVIOUS_HEADERS)\n    set(TARGET_HEADERS \"${TARGET_HEADERS}:${_PREVIOUS_HEADERS}\")\n    string(REPLACE \"${ARG_TARGET}=${_PREVIOUS_HEADERS}\" \"\"\n      SPECTRE_INTERFACE_LIBRARY_HEADERS \"${SPECTRE_INTERFACE_LIBRARY_HEADERS}\")\n  endif()\n\n  string(REPLACE \";;\" \";\"\n      SPECTRE_INTERFACE_LIBRARY_HEADERS \"${SPECTRE_INTERFACE_LIBRARY_HEADERS}\")\n  list(APPEND SPECTRE_INTERFACE_LIBRARY_HEADERS ${TARGET_HEADERS})\n  # END ADD EXISTING HEADERS\n\n  set_property(\n    GLOBAL PROPERTY SPECTRE_INTERFACE_LIBRARY_HEADERS\n    ${SPECTRE_INTERFACE_LIBRARY_HEADERS}\n    )\nendfunction(add_interface_lib_headers)\n\n# Returns a list of all the header files for the target `TARGET`\n# by setting the variable with the name `${RESULT_NAME}`\n#\n# Usage:\n#   get_target_headers(MyTarget MY_TARGET_HEADERS)\nfunction(get_target_headers TARGET RESULT_NAME)\n  get_target_property(\n    TARGET_TYPE\n    ${TARGET}\n    TYPE\n    )\n  if(${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    get_property(\n      _SPECTRE_INTERFACE_LIBRARY_HEADERS\n      GLOBAL\n      PROPERTY SPECTRE_INTERFACE_LIBRARY_HEADERS\n      )\n    foreach(_LIB ${_SPECTRE_INTERFACE_LIBRARY_HEADERS})\n      string(REPLACE \"${TARGET}=\" \"\" _LIB_HEADERS ${_LIB})\n      if(NOT ${_LIB} STREQUAL ${_LIB_HEADERS})\n        string(REPLACE \":\" \";\" _LIB_HEADERS ${_LIB_HEADERS})\n        set(${RESULT_NAME} ${_LIB_HEADERS} PARENT_SCOPE)\n        break()\n      endif(NOT ${_LIB} STREQUAL ${_LIB_HEADERS})\n    endforeach(_LIB ${_SPECTRE_INTERFACE_LIBRARY_HEADERS})\n\n  else(${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    get_property(\n      _HEADER_FILES\n      TARGET ${TARGET}\n      PROPERTY PUBLIC_HEADER\n      )\n    set(${RESULT_NAME} ${_HEADER_FILES} PARENT_SCOPE)\n  endif(${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\nendfunction(get_target_headers TARGET)\n"
  },
  {
    "path": "cmake/SpectreAddLibraries.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_custom_target(libs)\n\nfunction(ADD_SPECTRE_LIBRARY LIBRARY_NAME)\n  add_library(${LIBRARY_NAME} ${ARGN})\n  add_dependencies(libs ${LIBRARY_NAME})\n\n  get_target_property(\n    LIBRARY_IS_IMPORTED\n    ${LIBRARY_NAME}\n    IMPORTED\n    )\n  get_target_property(\n    LIBRARY_TYPE\n    ${LIBRARY_NAME}\n    TYPE\n    )\n\n  # We do _not_ link a custom allocator here, such as jemalloc or tcmalloc, but\n  # only dynamically link it into executables. This allows to use the libraries\n  # in Python bindings, where the custom allocator would cause problems.\n  if (NOT ${LIBRARY_TYPE} STREQUAL INTERFACE_LIBRARY)\n    set(SPECTRE_KOKKOS_LAUNCHER \"\")\n    if(SPECTRE_KOKKOS)\n      # We need to make sure we don't drop the Kokkos link wrapper\n      get_target_property(\n        _RULE_LAUNCH_LINK\n        ${LIBRARY_NAME}\n        RULE_LAUNCH_LINK)\n      if (_RULE_LAUNCH_LINK)\n        set(SPECTRE_KOKKOS_LAUNCHER ${_RULE_LAUNCH_LINK})\n      endif()\n    endif()\n    set_target_properties(\n      ${LIBRARY_NAME}\n      PROPERTIES\n      RULE_LAUNCH_LINK\n      \"${CMAKE_BINARY_DIR}/tmp/WrapLibraryLinker.sh ${SPECTRE_KOKKOS_LAUNCHER}\"\n      LINK_DEPENDS \"${CMAKE_BINARY_DIR}/tmp/WrapLibraryLinker.sh\"\n    )\n  endif (NOT ${LIBRARY_TYPE} STREQUAL INTERFACE_LIBRARY)\n  if (NOT \"${LIBRARY_NAME}\" MATCHES \"^SpectrePch\"\n      AND NOT ${LIBRARY_IS_IMPORTED}\n      AND NOT ${LIBRARY_TYPE} STREQUAL INTERFACE_LIBRARY\n      AND TARGET SpectrePch)\n      target_precompile_headers(${LIBRARY_NAME} REUSE_FROM SpectrePch)\n      target_link_libraries(${LIBRARY_NAME} PRIVATE SpectrePchFlags)\n  endif()\n  if (${LIBRARY_TYPE} STREQUAL INTERFACE_LIBRARY)\n    target_link_libraries(\n      ${LIBRARY_NAME}\n      INTERFACE\n      SpectreFlags\n      )\n  else()\n    target_link_libraries(\n      ${LIBRARY_NAME}\n      PUBLIC\n      SpectreFlags\n      )\n    set_property(\n      TARGET ${LIBRARY_NAME}\n      PROPERTY FOLDER ${CMAKE_CURRENT_SOURCE_DIR}\n      )\n  endif()\n  if (BUILD_SHARED_LIBS\n      AND NOT ${LIBRARY_TYPE} STREQUAL INTERFACE_LIBRARY)\n    install(TARGETS ${LIBRARY_NAME} OPTIONAL\n      LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})\n  endif()\nendfunction()\n"
  },
  {
    "path": "cmake/SpectreAddTestLibs.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Add an executable with Catch2 unit tests and register them with CTest.\n#\n# This function is named `add_test_library` although it adds an executable for\n# historical reasons. We can refactor this in the future. We can also switch to\n# `catch_discover_tests` instead of our own test parsing code in the future.\n# At that point we can remove the `SOURCE_FILES` argument of this function and\n# instead use the standard `target_sources` to add the source files (currently\n# the `SOURCE_FILES` are needed to parse the tests).\n#\n# Parameters:\n# - TEST_EXEC_NAME The name of the test executable to add\n# - SOURCE_FILES The source files to compile into the executable\nfunction(add_test_library TEST_EXEC_NAME SOURCE_FILES)\n  cmake_parse_arguments(ARG WITH_CHARM \"\" \"\" ${ARGN})\n\n  add_spectre_executable(\n    ${TEST_EXEC_NAME}\n    ${SOURCE_FILES}\n    )\n  add_dependencies(unit-tests ${TEST_EXEC_NAME})\n  target_link_libraries(\n    ${TEST_EXEC_NAME}\n    PRIVATE\n    Catch2::Catch2\n    Framework\n    )\n\n  # Use either the Charm++ or the non-Charm++ TestMain\n  if (ARG_WITH_CHARM)\n    target_sources(\n      ${TEST_EXEC_NAME}\n      PRIVATE\n      ${CMAKE_SOURCE_DIR}/tests/Unit/TestMainCharm.cpp\n    )\n    add_dependencies(\n      ${TEST_EXEC_NAME}\n      module_TestMainCharm\n      )\n  else()\n    target_sources(\n      ${TEST_EXEC_NAME}\n      PRIVATE\n      ${CMAKE_SOURCE_DIR}/tests/Unit/TestMain.cpp\n    )\n  endif()\n\n  # Register the tests with CTest.\n  # We may want to switch to `catch_discover_tests` and remove our own test\n  # parsing code in the future. Before we do, we should check if it supports all\n  # the features we use.\n  # catch_discover_tests(${TEST_EXEC_NAME})\n  spectre_add_catch_tests(${TEST_EXEC_NAME})\nendfunction()\n\n# Shared test libs are currently unsupported on macOS because Catch2 static\n# variables are not properly initialized. This is only needed for test helper\n# libraries with Catch2 assertions in them.\nset(SPECTRE_TEST_LIBS_TYPE \"\")\nif(APPLE)\n  set(SPECTRE_TEST_LIBS_TYPE STATIC)\nendif()\n"
  },
  {
    "path": "cmake/SpectreCheckDependencies.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\ninclude(SpectreCheckTargetDependencies)\n\nfunction(check_spectre_libs_dependencies)\n  get_property(\n    SPECTRE_TPLS GLOBAL PROPERTY SPECTRE_THIRD_PARTY_LIBS\n    )\n\n  get_property(\n    SPECTRE_LIBS\n    TARGET libs\n    PROPERTY MANUALLY_ADDED_DEPENDENCIES\n    )\n\n  foreach(TARGET_TO_CHECK ${ARGN})\n    check_target_dependencies(\n      TARGET ${TARGET_TO_CHECK}\n      ALL_TARGETS\n      ${SPECTRE_TPLS}\n      ${SPECTRE_LIBS}\n      ALLOWED_EXTRA_TARGETS\n      SpectreAllocator\n      SpectreFlags\n      SpectrePchFlags\n      ERROR_ON_FAILURE\n      )\n  endforeach(TARGET_TO_CHECK ${ARGN})\nendfunction(check_spectre_libs_dependencies)\n\noption(CHECK_LIBRARY_DEPENDENCIES\n  \"Check link dependencies of SpECTRE libraries\" OFF)\n\noption(CHECK_ALL_DEPENDENCIES\n  \"Check link dependencies of all SpECTRE targets\" OFF)\n\nif(CHECK_LIBRARY_DEPENDENCIES OR CHECK_ALL_DEPENDENCIES)\n  get_property(\n    SPECTRE_LIBS\n    TARGET libs\n    PROPERTY MANUALLY_ADDED_DEPENDENCIES\n    )\n  check_spectre_libs_dependencies(\n    ${SPECTRE_LIBS}\n    )\nendif(CHECK_LIBRARY_DEPENDENCIES OR CHECK_ALL_DEPENDENCIES)\n"
  },
  {
    "path": "cmake/SpectreCheckTargetDependencies.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\ninclude(SpectreAddInterfaceLibraryHeaders)\n\n# Get the list of absolute paths of header files for target `TARGET_NAME`.\n#\n# Sets the variable `HEADER_FILES` in parent scope to return the result\nfunction(_absolute_header_paths TARGET_NAME)\n  get_target_headers(${TARGET_NAME} _HEADER_FILES)\n  get_property(\n    _INCLUDE_DIR\n    TARGET ${TARGET_NAME}\n    PROPERTY INTERFACE_INCLUDE_DIRECTORIES\n    )\n  list(LENGTH _INCLUDE_DIR _NUMBER_OF_INCLUDE_DIRS)\n  if(${_NUMBER_OF_INCLUDE_DIRS} GREATER 1)\n    message(FATAL_ERROR\n      \"Currently _absolute_header_paths only supports a single \"\n      \"INTERFACE_INCLUDE_DIRECTORIES for finding header files. Support for \"\n      \"multiple INTERFACE_INCLUDE_DIRECTORIES can be added if needed.\")\n  endif(${_NUMBER_OF_INCLUDE_DIRS} GREATER 1)\n\n  unset(_ABS_HEADER_FILES)\n  foreach(HEADER ${_HEADER_FILES})\n    if(IS_ABSOLUTE ${HEADER})\n      list(APPEND _ABS_HEADER_FILES ${HEADER})\n    else()\n      list(APPEND _ABS_HEADER_FILES \"${_INCLUDE_DIR}/${HEADER}\")\n    endif()\n  endforeach(HEADER ${_HEADER_FILES})\n  # \"Return\" HEADER_FILES by setting it in PARENT_SCOPE\n  set(HEADER_FILES ${_ABS_HEADER_FILES} PARENT_SCOPE)\nendfunction(_absolute_header_paths TARGET_NAME)\n\n# Get the list of absolute paths of source files for target `TARGET_NAME`.\n#\n# Sets the variable `SOURCE_FILES` in parent scope to return the result\nfunction(_absolute_source_paths TARGET_NAME)\n  get_property(\n    _SOURCE_FILES\n    TARGET ${TARGET_NAME}\n    PROPERTY SOURCES\n    )\n  get_property(\n    _LOCATION\n    TARGET ${TARGET_NAME}\n    PROPERTY FOLDER\n    )\n\n  unset(_ABS_SOURCE_FILES)\n  foreach(SOURCE ${_SOURCE_FILES})\n    if(IS_ABSOLUTE ${SOURCE})\n      list(APPEND _ABS_SOURCE_FILES ${SOURCE})\n    else()\n      list(APPEND _ABS_SOURCE_FILES \"${_LOCATION}/${SOURCE}\")\n    endif()\n  endforeach(SOURCE ${_SOURCE_FILES})\n  # \"Return\" SOURCE_FILES by setting it in PARENT_SCOPE\n  set(SOURCE_FILES ${_ABS_SOURCE_FILES} PARENT_SCOPE)\nendfunction(_absolute_source_paths TARGET_NAME)\n\n# Get the list of SpECTRE includes (i.e. not for 3rd party libraries)\n# for the target `TARGET_NAME`.\n#\n# Sets the variable `SPECTRE_INCLUDES` in parent scope to return the result\nfunction(_extract_spectre_includes FILE_NAME)\n  file(READ ${FILE_NAME} FILE_CONTENTS)\n  string(REGEX MATCHALL \"\\n#include \\\"[^\\n]+\\\"\"\n    _RAW_SPECTRE_INCLUDES ${FILE_CONTENTS})\n  unset(SPECTRE_INCLUDES)\n  foreach(INCLUDE ${_RAW_SPECTRE_INCLUDES})\n    string(REPLACE \"\\n#include \\\"\" \"\" INCLUDE \"${INCLUDE}\")\n    string(REPLACE \"\\\"\" \"\" INCLUDE \"${INCLUDE}\")\n    list(APPEND SPECTRE_INCLUDES ${INCLUDE})\n  endforeach(INCLUDE ${_RAW_SPECTRE_INCLUDES})\n  # \"Return\" SPECTRE_INCLUDES by setting it in PARENT_SCOPE\n  set(SPECTRE_INCLUDES ${SPECTRE_INCLUDES} PARENT_SCOPE)\nendfunction(_extract_spectre_includes FILE_NAME)\n\n# Get the list of 3rd party library includes for the target `TARGET_NAME`.\n#\n# Sets the variable `TPL_INCLUDES` in parent scope to return the result\nfunction(_extract_tpl_includes FILE_NAME)\n  file(READ ${FILE_NAME} FILE_CONTENTS)\n  string(REGEX MATCHALL \"\\n#include <[^\\n]+>\"\n    _RAW_TPL_INCLUDES ${FILE_CONTENTS})\n  unset(TPL_INCLUDES)\n  foreach(INCLUDE ${_RAW_TPL_INCLUDES})\n    string(REPLACE \"\\n#include <\" \"\" INCLUDE \"${INCLUDE}\")\n    string(REPLACE \">\" \"\" INCLUDE \"${INCLUDE}\")\n    list(APPEND TPL_INCLUDES ${INCLUDE})\n  endforeach(INCLUDE ${_RAW_TPL_INCLUDES})\n  # \"Return\" TPL_INCLUDES by setting it in PARENT_SCOPE\n  set(TPL_INCLUDES ${TPL_INCLUDES} PARENT_SCOPE)\nendfunction(_extract_tpl_includes FILE_NAME)\n\n# Make the path of `HEADER_FILE` which belongs to the target `TARGET_NAME`\n# an absolute path.\n#\n# Sets the variable `ABS_HEADER_FILE` in parent scope to return the result\nfunction(_make_header_path_absolute TARGET_NAME HEADER_FILE)\n  get_property(\n    _INCLUDE_DIR\n    TARGET ${TARGET_NAME}\n    PROPERTY INTERFACE_INCLUDE_DIRECTORIES\n    )\n  if(IS_ABSOLUTE ${HEADER_FILE})\n    set(_ABS_HEADER_FILE ${HEADER_FILE})\n  else()\n    set(_ABS_HEADER_FILE \"${_INCLUDE_DIR}/${HEADER_FILE}\")\n  endif()\n  # \"Return\" ABS_HEADER_FILE by setting it in PARENT_SCOPE\n  set(ABS_HEADER_FILE ${_ABS_HEADER_FILE} PARENT_SCOPE)\nendfunction(_make_header_path_absolute TARGET_NAME HEADER_FILE)\n\n# Gets the dependencies for a list of includes of one of the source or\n# header files of the target `TARGET_NAME`\n#\n# The `_get_deps_for_includes` function builds a list of all dependencies\n# by finding the target that provides each header file in `INCLUDES_OF_FILE`.\n# `INCLUDES_OF_FILE` must be a list of includes from a file (either header\n# or source file) of the target `TARGET_NAME`. If `RECURSE_THROUGH_INCLUDES`\n# is `TRUE` then for each header file in `INCLUDES_OF_FILE` that is also an\n# include of the target `TARGET_NAME` then the header file will also have its\n# dependencies searched and added to the list of dependencies.\n#\n# Example usage:\n#\n#   _get_deps_for_includes(\n#     TARGET_C\n#     \"TARGET_A;TARGET_B;TARGET_C;TARGET_D\"\n#     \"cmath;utility;vector\"\n#     FALSE\n#     )\n#\n# Arguments:\n#\n# TARGET_NAME: the target for which to check the link libraries/dependencies\n# LIST_OF_ALL_TARGETS: a list of all the targets that might be dependencies\n#                      of the target ${TARGET_NAME}\n# INCLUDES_OF_FILE:\n#     a list of header files for which to get the dependencies.\n# RECURSE_THROUGH_INCLUDES:\n#     if TRUE then any include in any of ${INCLUDES_OF_FILE}\n#     that is also an include of the target ${TARGET_NAME} has its\n#     dependencies added.\n#\n# Since the recursion will cause an infinite loop if there are cyclic includes\n# we limit the recursion depth to 20, at which point an error message is\n# displayed that there may be cyclic dependencies\n#\n# Returns by setting `TARGET_DEPS` in parent scope\nfunction(_get_deps_for_includes TARGET_NAME LIST_OF_ALL_TARGETS\n    INCLUDES_OF_FILE RECURSE_THROUGH_INCLUDES)\n  # Implement guard against infinite recursion when there are cyclic\n  # includes in a library.\n  get_property(\n    GET_DEPS_FOR_SPECTRE_INCLUDES_PATH\n    GLOBAL PROPERTY GET_DEPS_FOR_SPECTRE_INCLUDES_PATH\n    )\n  list(LENGTH GET_DEPS_FOR_SPECTRE_INCLUDES_PATH RECURSE_DEPTH)\n  if(${RECURSE_DEPTH} GREATER 20)\n    message(FATAL_ERROR\n      \" Found what appears to be a cyclic loop in the header files.\"\n      \" The header path is: ${GET_DEPS_FOR_SPECTRE_INCLUDES_PATH}\"\n      )\n  endif(${RECURSE_DEPTH} GREATER 20)\n\n  get_target_headers(${TARGET_NAME} TARGET_HEADER_FILES)\n\n  unset(_TARGET_DEPS)\n  # Now loop over all spectre targets to see if any of them\n  # expose these includes\n  foreach(CURRENT_INCLUDE ${INCLUDES_OF_FILE})\n    # If we are checking header files and the header file is part of\n    # this target, possibly recurse the header files, and skipping search\n    # through targets for which one provides the header file (we know it's us).\n    if(${CURRENT_INCLUDE} IN_LIST TARGET_HEADER_FILES)\n      if(RECURSE_THROUGH_INCLUDES)\n        # Sets ABS_HEADER_FILE\n        _make_header_path_absolute(${TARGET_NAME} ${CURRENT_INCLUDE})\n        # Sets SPECTRE_INCLUDES\n        _extract_spectre_includes(${ABS_HEADER_FILE})\n        set_property(\n          GLOBAL APPEND PROPERTY GET_DEPS_FOR_CURRENT_INCLUDES_PATH\n          ${CURRENT_INCLUDE}\n          )\n        # Sets TARGET_DEPS\n        _get_deps_for_includes(${TARGET_NAME} \"${LIST_OF_ALL_TARGETS}\"\n          \"${CURRENT_INCLUDES}\" ${RECURSE_THROUGH_INCLUDES})\n        # \"Pop\" last element by setting the property to what it was at the\n        # start of the function call.\n        set_property(\n          GLOBAL PROPERTY GET_DEPS_FOR_CURRENT_INCLUDES_PATH\n          ${GET_DEPS_FOR_CURRENT_INCLUDES_PATH}\n          )\n        # Get the dependencies of the header file, since these are all\n        # also PRIVATE dependencies of the target.\n        foreach(_TARGET ${TARGET_DEPS})\n          if(NOT ${_TARGET} IN_LIST _TARGET_DEPS)\n            list(APPEND _TARGET_DEPS ${_TARGET})\n          endif(NOT ${_TARGET} IN_LIST _TARGET_DEPS)\n        endforeach(_TARGET ${TARGET_DEPS})\n        continue()\n      else(RECURSE_THROUGH_INCLUDES)\n        continue()\n      endif(RECURSE_THROUGH_INCLUDES)\n    endif(${CURRENT_INCLUDE} IN_LIST TARGET_HEADER_FILES)\n\n    unset(_TARGET_WITH_INCLUDE)\n    foreach(DEP_TARGET ${LIST_OF_ALL_TARGETS})\n      if(${DEP_TARGET} STREQUAL ${TARGET_NAME})\n        continue()\n      endif(${DEP_TARGET} STREQUAL ${TARGET_NAME})\n\n      # Get the header files of DEP_TARGET and see if any of the\n      # includes are part of DEP_TARGET.\n      get_target_headers(${DEP_TARGET} _DEP_TARGET_HEADERS)\n      if(${CURRENT_INCLUDE} IN_LIST _DEP_TARGET_HEADERS)\n        set(_TARGET_WITH_INCLUDE ${DEP_TARGET})\n        break()\n      endif(${CURRENT_INCLUDE} IN_LIST _DEP_TARGET_HEADERS)\n    endforeach(DEP_TARGET ${LIST_OF_ALL_TARGETS})\n    if(NOT _TARGET_WITH_INCLUDE)\n      message(FATAL_ERROR\n        \"No known targets supply the file ${CURRENT_INCLUDE} included in \"\n        \"file ${HEADER_OR_SOURCE} of target ${TARGET_NAME}.\"\n        )\n    endif(NOT _TARGET_WITH_INCLUDE)\n\n    if(NOT ${_TARGET_WITH_INCLUDE} IN_LIST _TARGET_DEPS)\n      list(APPEND _TARGET_DEPS ${_TARGET_WITH_INCLUDE})\n    endif(NOT ${_TARGET_WITH_INCLUDE} IN_LIST _TARGET_DEPS)\n  endforeach(CURRENT_INCLUDE ${INCLUDES_OF_FILE})\n\n  # \"Return\" TARGET_DEPS by setting it in PARENT_SCOPE\n  set(TARGET_DEPS ${_TARGET_DEPS} PARENT_SCOPE)\nendfunction(_get_deps_for_includes TARGET_NAME LIST_OF_ALL_TARGETS\n  INCLUDES_OF_FILE RECURSE_THROUGH_INCLUDES)\n\n# Returns the dependencies for the target\n#\n# Arguments\n#\n# TARGET_NAME:\n#     the target for which to check the link libraries/dependencies\n# LIST_OF_ALL_TARGETS:\n#     a list of all the targets that might be dependencies\n#     of the target ${TARGET_NAME}\n# LIST_OF_FILES:\n#     a list of the files for which to get the dependencies. Can be\n#     header/tpp or source files.\n# RECURSE_INCLUDES:\n#     if TRUE then the dependencies of the target's included header files\n#     will also be added. This is used when source files are passed because\n#     the dependencies of the header files must be marked as PUBLIC\n#\n# Returns by setting `TARGET_DEPS` in parent scope\nfunction(_get_deps_for_target TARGET_NAME LIST_OF_ALL_TARGETS\n    LIST_OF_FILES RECURSE_INCLUDES)\n  unset(_TARGET_DEPS)\n  foreach(HEADER_OR_SOURCE ${LIST_OF_FILES})\n    if(IS_ABSOLUTE \"${HEADER_OR_SOURCE}\")\n      # Sets SPECTRE_INCLUDES\n      _extract_spectre_includes(\"${HEADER_OR_SOURCE}\")\n    else()\n      message(FATAL_ERROR\n        \"All paths of files passed to _get_deps_for_target must be absolute \"\n        \"but received ${HEADER_OR_SOURCE}.\")\n    endif()\n\n    # Sets TARGET_DEPS\n    _get_deps_for_includes(${TARGET_NAME} \"${LIST_OF_ALL_TARGETS}\"\n      \"${SPECTRE_INCLUDES}\" ${RECURSE_INCLUDES})\n    foreach(_TARGET ${TARGET_DEPS})\n      if(NOT ${_TARGET} IN_LIST _TARGET_DEPS)\n        list(APPEND _TARGET_DEPS ${_TARGET})\n      endif(NOT ${_TARGET} IN_LIST _TARGET_DEPS)\n    endforeach(_TARGET ${TARGET_DEPS})\n\n    # Third party library includes\n\n    # Sets TPL_INCLUDES\n    _extract_tpl_includes(\"${HEADER_OR_SOURCE}\")\n    # Sets TARGET_DEPS\n    _get_deps_for_includes(${TARGET_NAME} \"${LIST_OF_ALL_TARGETS}\"\n      \"${TPL_INCLUDES}\" FALSE)\n    foreach(_TARGET ${TARGET_DEPS})\n      if(NOT ${_TARGET} IN_LIST _TARGET_DEPS)\n        list(APPEND _TARGET_DEPS ${_TARGET})\n      endif(NOT ${_TARGET} IN_LIST _TARGET_DEPS)\n    endforeach(_TARGET ${TARGET_DEPS})\n\n  endforeach(HEADER_OR_SOURCE ${LIST_OF_FILES})\n  # Alphabetize the list of dependencies\n  list(SORT _TARGET_DEPS)\n  # \"Return\" the target dependencies\n  set(TARGET_DEPS ${_TARGET_DEPS} PARENT_SCOPE)\nendfunction(_get_deps_for_target TARGET_NAME LIST_OF_FILES LIST_OF_ALL_TARGETS)\n\n# Add the libraries for the `target_link_libraries` command\n# for a target.\n#\n# Arguments\n#\n# TARGET_DEPS:\n#     dependencies of the target\n# TARGET_DEPS_TYPE:\n#     the type of dependencies, INTERFACE/PRIVATE/PUBLIC\n# TARGET_LINK_LIBRARIES_COMMAND:\n#     the target_link_libraries command so far\n#\n# Returns by setting `TARGET_LINK_LIBS_COMMAND` in parent scope\nfunction(_add_targets_to_link_libs TARGET_DEPS TARGET_DEPS_TYPE\n    TARGET_LINK_LIBS_COMMAND)\n  list(LENGTH TARGET_DEPS _NUM_TARGET_DEPS)\n  set(_TARGET_LINK_LIBS_COMMAND\n    \"${TARGET_LINK_LIBS_COMMAND}\"\n    )\n  if(${_NUM_TARGET_DEPS} GREATER 0)\n    set(\n      _TARGET_LINK_LIBS_COMMAND\n      \"${_TARGET_LINK_LIBS_COMMAND}\\n\"\n      \"   ${TARGET_DEPS_TYPE}\"\n      )\n    foreach(_DEP ${TARGET_DEPS})\n      set(\n        _TARGET_LINK_LIBS_COMMAND\n        \"${_TARGET_LINK_LIBS_COMMAND}\\n\"\n        \"   ${_DEP}\"\n        )\n    endforeach(_DEP ${_TARGET_DEPS})\n    set(_TARGET_LINK_LIBS_COMMAND\n      \"${_TARGET_LINK_LIBS_COMMAND}\\n \"\n      )\n    string(REPLACE \";\" \"\" _TARGET_LINK_LIBS_COMMAND\n      ${_TARGET_LINK_LIBS_COMMAND})\n  endif(${_NUM_TARGET_DEPS} GREATER 0)\n  # \"Return\" the target dependencies\n  set(TARGET_LINK_LIBS_COMMAND ${_TARGET_LINK_LIBS_COMMAND} PARENT_SCOPE)\nendfunction(_add_targets_to_link_libs TARGET_DEPS TARGET_DEPS_TYPE\n  TARGET_LINK_LIBS_COMMAND)\n\n# Queries the INTERFACE_LINK_LIBRARIES of TARGET. This is done recursively.\nfunction(_get_interface_link_libraries TARGET)\n  get_target_property(_INTERFACE_LIBS ${TARGET} INTERFACE_LINK_LIBRARIES)\n  set(LINK_LIBS_FOR_TARGET \"\")\n  foreach(_INTERFACE_LIB ${_INTERFACE_LIBS})\n    if (_INTERFACE_LIB)\n      list(APPEND LINK_LIBS_FOR_TARGET ${_INTERFACE_LIB})\n      if (TARGET ${_INTERFACE_LIB})\n        set(OUTPUT_LIST \"\")\n        _get_interface_link_libraries(${_INTERFACE_LIB})\n        list(APPEND LINK_LIBS_FOR_TARGET ${OUTPUT_LIST})\n      endif()\n    endif()\n  endforeach()\n  # \"Return\" dependencies to calling function\n  set(OUTPUT_LIST ${LINK_LIBS_FOR_TARGET} PARENT_SCOPE)\nendfunction(_get_interface_link_libraries OUTPUT_LIST TARGET)\n\n# Checks the dependencies for the target ${TARGET_NAME} and if they are\n# incorrect produces an error message with the correct dependencies.\n#\n# TARGET_NAME:\n#     the target for which to check the link libraries/dependencies\n# TARGET_LINK_LIBS_COMMAND:\n#     the target_link_libraries CMake command so far\n# TARGET_INTERFACE_DEPS:\n#     the INTERFACE link libraries for the target\n# TARGET_PRIVATE_DEPS:\n#     the PRIVATE link libraries for the target\n# TARGET_PUBLIC_DEPS:\n#     the PUBLIC link libraries for the target\n# LIST_OF_ALLOWED_EXTRA_TARGETS:\n#     a list of allowed extra dependencies. An example of an extra dependency\n#     is the SpectreFlags target, which supplies no header files and there is\n#     not strictly a dependency of ${TARGET_NAME} but can be specified as a\n#     dependency in order to add compiler flags or definitions to the target.\n# ERROR_ON_FAILURE:\n#     if specified, then CMake will produce an error if the dependencies are\n#     incorrect. Otherwise the variable TARGET_DEPENDENCIES_ERROR_MESSAGE\n#     is set in the parent scope\nfunction(_check_and_print_dependencies\n    TARGET_NAME TARGET_LINK_LIBS_COMMAND\n    TARGET_INTERFACE_DEPS TARGET_PRIVATE_DEPS TARGET_PUBLIC_DEPS\n    LIST_OF_ALLOWED_EXTRA_TARGETS)\n  cmake_parse_arguments(\n    ARG \"ERROR_ON_FAILURE\" \"\" \"\" ${ARGN})\n\n  # We add the dependencies of allowed targets as additional allowed targets.\n  # This avoids the situation where target A is allowed, but A depends on B,\n  # and therefore every target has a dependency on B. The helper function\n  # ensures the dependencies are added recursively.\n  set(WORKING_LIST ${LIST_OF_ALLOWED_EXTRA_TARGETS})\n  foreach(_ALLOWED_EXTRA_TARGET ${LIST_OF_ALLOWED_EXTRA_TARGETS})\n    set(OUTPUT_LIST \"\")\n    _get_interface_link_libraries(${_ALLOWED_EXTRA_TARGET})\n    list(APPEND WORKING_LIST ${OUTPUT_LIST})\n  endforeach()\n  list(REMOVE_DUPLICATES WORKING_LIST)\n  set(LIST_OF_ALLOWED_EXTRA_TARGETS ${WORKING_LIST})\n\n  get_property(\n    _INTERFACE_LIBS\n    TARGET ${TARGET_NAME}\n    PROPERTY INTERFACE_LINK_LIBRARIES\n    )\n  # The property INTERFACE_LINK_LIBRARIES contains generator expressions for\n  # private link-time dependencies. We will be handling these private\n  # dependencies explicitly below, so here we want to remove them from the list\n  # of interface dependencies. The generators have format \"$<LINK_ONLY:dep>\"\n  list(FILTER _INTERFACE_LIBS EXCLUDE REGEX \"\\\\$<LINK_ONLY:.+>\")\n\n  unset(_MISSING_INTERFACE_LIBS)\n  foreach(_INTERFACE_DEP ${TARGET_INTERFACE_DEPS})\n    if(NOT ${_INTERFACE_DEP} IN_LIST _INTERFACE_LIBS)\n      list(APPEND _MISSING_INTERFACE_LIBS ${_INTERFACE_DEP})\n    endif(NOT ${_INTERFACE_DEP} IN_LIST _INTERFACE_LIBS)\n  endforeach(_INTERFACE_DEP ${TARGET_INTERFACE_DEPS})\n\n  get_target_property(\n    TARGET_TYPE\n    ${TARGET_NAME}\n    TYPE\n    )\n  set(_NUM_MISSING_PRIVATE_LIBS 0)\n  set(_NUM_MISSING_PUBLIC_LIBS 0)\n  unset(_EXTRA_INTERFACE_LIBS)\n  unset(_EXTRA_PRIVATE_LIBS)\n  unset(_EXTRA_PUBLIC_LIBS)\n  if(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    get_property(\n      _PRIVATE_LIBS\n      TARGET ${TARGET_NAME}\n      PROPERTY LINK_LIBRARIES\n      )\n\n    # Check to see if any interface-only dependencies are also\n    # PRIVATE/PUBLIC\n    foreach(_INTERFACE_DEP ${TARGET_INTERFACE_DEPS})\n      if(${_INTERFACE_DEP} IN_LIST _PRIVATE_LIBS)\n        list(APPEND _MISSING_INTERFACE_LIBS ${_INTERFACE_DEP})\n      endif(${_INTERFACE_DEP} IN_LIST _PRIVATE_LIBS)\n    endforeach(_INTERFACE_DEP ${TARGET_INTERFACE_DEPS})\n\n    unset(_MISSING_PRIVATE_LIBS)\n    foreach(_PRIVATE_DEP ${TARGET_PRIVATE_DEPS})\n      if(NOT ${_PRIVATE_DEP} IN_LIST _PRIVATE_LIBS)\n        list(APPEND _MISSING_PRIVATE_LIBS ${_PRIVATE_DEP})\n      endif(NOT ${_PRIVATE_DEP} IN_LIST _PRIVATE_LIBS)\n    endforeach(_PRIVATE_DEP ${TARGET_PRIVATE_DEPS})\n    list(LENGTH _MISSING_PRIVATE_LIBS _NUM_MISSING_PRIVATE_LIBS)\n\n    unset(_MISSING_PUBLIC_LIBS)\n    foreach(_PUBLIC_DEP ${TARGET_PUBLIC_DEPS})\n      if(NOT ${_PUBLIC_DEP} IN_LIST _PRIVATE_LIBS\n          OR NOT ${_PUBLIC_DEP} IN_LIST _INTERFACE_LIBS)\n        list(APPEND _MISSING_PUBLIC_LIBS ${_PUBLIC_DEP})\n      endif(NOT ${_PUBLIC_DEP} IN_LIST _PRIVATE_LIBS\n        OR NOT ${_PUBLIC_DEP} IN_LIST _INTERFACE_LIBS)\n    endforeach(_PUBLIC_DEP ${TARGET_PUBLIC_DEPS})\n    list(LENGTH _MISSING_PUBLIC_LIBS _NUM_MISSING_PUBLIC_LIBS)\n\n    # Now check for extra target link libraries that aren't needed.\n    unset(_PUBLIC_LIBS)\n    foreach(_INTERFACE_LIB ${_INTERFACE_LIBS})\n      if(${_INTERFACE_LIB} IN_LIST _PRIVATE_LIBS)\n        list(APPEND _PUBLIC_LIBS ${_INTERFACE_LIB})\n      endif(${_INTERFACE_LIB} IN_LIST _PRIVATE_LIBS)\n    endforeach(_INTERFACE_LIB ${_INTERFACE_LIBS})\n\n    foreach(_PUBLIC_LIB ${_PUBLIC_LIBS})\n      list(REMOVE_ITEM _INTERFACE_LIBS ${_PUBLIC_LIB})\n      list(REMOVE_ITEM _PRIVATE_LIBS ${_PUBLIC_LIB})\n      if(NOT ${_PUBLIC_LIB} IN_LIST TARGET_PUBLIC_DEPS AND\n          NOT ${_PUBLIC_LIB} IN_LIST LIST_OF_ALLOWED_EXTRA_TARGETS)\n        list(APPEND _EXTRA_PUBLIC_LIBS ${_PUBLIC_LIB})\n      endif(NOT ${_PUBLIC_LIB} IN_LIST TARGET_PUBLIC_DEPS AND\n          NOT ${_PUBLIC_LIB} IN_LIST LIST_OF_ALLOWED_EXTRA_TARGETS)\n    endforeach(_PUBLIC_LIB ${_PUBLIC_LIBS})\n\n    foreach(_PRIVATE_LIB ${_PRIVATE_LIBS})\n      if(NOT ${_PRIVATE_LIB} IN_LIST TARGET_PRIVATE_DEPS AND\n          NOT ${_PRIVATE_LIB} IN_LIST LIST_OF_ALLOWED_EXTRA_TARGETS)\n        list(APPEND _EXTRA_PRIVATE_LIBS ${_PRIVATE_LIB})\n      endif(NOT ${_PRIVATE_LIB} IN_LIST TARGET_PRIVATE_DEPS AND\n          NOT ${_PRIVATE_LIB} IN_LIST LIST_OF_ALLOWED_EXTRA_TARGETS)\n    endforeach(_PRIVATE_LIB ${_PRIVATE_LIBS})\n\n    foreach(_INTERFACE_LIB ${_INTERFACE_LIBS})\n      if(NOT ${_INTERFACE_LIB} IN_LIST TARGET_INTERFACE_DEPS AND\n          NOT ${_INTERFACE_LIB} IN_LIST LIST_OF_ALLOWED_EXTRA_TARGETS)\n        list(APPEND _EXTRA_INTERFACE_LIBS ${_INTERFACE_LIB})\n      endif(NOT ${_INTERFACE_LIB} IN_LIST TARGET_INTERFACE_DEPS AND\n          NOT ${_INTERFACE_LIB} IN_LIST LIST_OF_ALLOWED_EXTRA_TARGETS)\n    endforeach(_INTERFACE_LIB ${_INTERFACE_LIBS})\n  else(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    foreach(_INTERFACE_DEP ${_INTERFACE_LIBS})\n      if(NOT ${_INTERFACE_DEP} IN_LIST TARGET_INTERFACE_DEPS AND\n          NOT ${_INTERFACE_DEP} IN_LIST LIST_OF_ALLOWED_EXTRA_TARGETS)\n        list(APPEND _EXTRA_INTERFACE_LIBS ${_INTERFACE_DEP})\n      endif()\n    endforeach(_INTERFACE_DEP ${_INTERFACE_LIBS})\n  endif(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n\n  list(LENGTH _MISSING_INTERFACE_LIBS _NUM_MISSING_INTERFACE_LIBS)\n\n  list(LENGTH _EXTRA_INTERFACE_LIBS _NUM_EXTRA_INTERFACE_LIBS)\n  list(LENGTH _EXTRA_PRIVATE_LIBS _NUM_EXTRA_PRIVATE_LIBS)\n  list(LENGTH _EXTRA_PUBLIC_LIBS _NUM_EXTRA_PUBLIC_LIBS)\n\n  set(_MISSING_LIBS FALSE)\n  set(_ERROR_MSG \" Missing dependencies for target ${TARGET_NAME}\")\n  if(${_NUM_MISSING_INTERFACE_LIBS} GREATER 0)\n    set(_ERROR_MSG\n      \"${_ERROR_MSG}\\n Missing interface libs:\\n   ${_MISSING_INTERFACE_LIBS}\")\n    set(_MISSING_LIBS TRUE)\n  endif(${_NUM_MISSING_INTERFACE_LIBS} GREATER 0)\n  if(${_NUM_MISSING_PRIVATE_LIBS} GREATER 0)\n    set(_ERROR_MSG\n      \"${_ERROR_MSG}\\n Missing private libs:\\n   ${_MISSING_PRIVATE_LIBS}\")\n    set(_MISSING_LIBS TRUE)\n  endif(${_NUM_MISSING_PRIVATE_LIBS} GREATER 0)\n  if(${_NUM_MISSING_PUBLIC_LIBS} GREATER 0)\n    set(_ERROR_MSG\n      \"${_ERROR_MSG}\\n Missing public libs:\\n   ${_MISSING_PUBLIC_LIBS}\")\n    set(_MISSING_LIBS TRUE)\n  endif(${_NUM_MISSING_PUBLIC_LIBS} GREATER 0)\n\n  if(${_NUM_EXTRA_INTERFACE_LIBS} GREATER 0 OR\n      ${_NUM_EXTRA_PRIVATE_LIBS} GREATER 0 OR\n      ${_NUM_EXTRA_PUBLIC_LIBS} GREATER 0)\n    set(_ERROR_MSG \" Extra dependencies for target ${TARGET_NAME}\")\n  endif()\n  if(${_NUM_EXTRA_INTERFACE_LIBS} GREATER 0)\n    set(_ERROR_MSG\n      \"${_ERROR_MSG}\\n Extra interface libs:\\n   ${_EXTRA_INTERFACE_LIBS}\")\n    set(_MISSING_LIBS TRUE)\n  endif(${_NUM_EXTRA_INTERFACE_LIBS} GREATER 0)\n  if(${_NUM_EXTRA_PRIVATE_LIBS} GREATER 0)\n    set(_ERROR_MSG\n      \"${_ERROR_MSG}\\n Extra private libs:\\n   ${_EXTRA_PRIVATE_LIBS}\")\n    set(_MISSING_LIBS TRUE)\n  endif(${_NUM_EXTRA_PRIVATE_LIBS} GREATER 0)\n  if(${_NUM_EXTRA_PUBLIC_LIBS} GREATER 0)\n    set(_ERROR_MSG\n      \"${_ERROR_MSG}\\n Extra public libs:\\n   ${_EXTRA_PUBLIC_LIBS}\")\n    set(_MISSING_LIBS TRUE)\n  endif(${_NUM_EXTRA_PUBLIC_LIBS} GREATER 0)\n\n  if(NOT ARG_ERROR_ON_FAILURE)\n    set(\n      TARGET_DEPS_ERROR_MESSAGE\n      \"\"\n      PARENT_SCOPE\n      )\n  endif(NOT ARG_ERROR_ON_FAILURE)\n\n  if(_MISSING_LIBS)\n    set(TARGET_LINK_LIBS_COMMAND\n      \" target_link_libraries(\\n\"\n      \"   ${TARGET_NAME}\"\n      )\n    _add_targets_to_link_libs(\n      \"${TARGET_INTERFACE_DEPS}\" \"INTERFACE\"\n      \"${TARGET_LINK_LIBS_COMMAND}\")\n    _add_targets_to_link_libs(\n      \"${TARGET_PRIVATE_DEPS}\" \"PRIVATE\"\n      \"${TARGET_LINK_LIBS_COMMAND}\")\n    _add_targets_to_link_libs(\n      \"${TARGET_PUBLIC_DEPS}\" \"PUBLIC\"\n      \"${TARGET_LINK_LIBS_COMMAND}\")\n    set(TARGET_LINK_LIBS_COMMAND\n      \"${TARGET_LINK_LIBS_COMMAND}  )\"\n      )\n\n    if(${ARG_ERROR_ON_FAILURE})\n      message(FATAL_ERROR\n        \"${_ERROR_MSG}\\n \\n\"\n        \" Correct target_link_libraries command is:\\n\"\n        \"${TARGET_LINK_LIBS_COMMAND}\"\n        )\n    else(${ARG_ERROR_ON_FAILURE})\n      set(\n        TARGET_DEPS_ERROR_MESSAGE\n        \"${_ERROR_MSG}\\n \\n\\\n Correct target_link_libraries command is:\\n\\\n${TARGET_LINK_LIBS_COMMAND}\"\n        PARENT_SCOPE\n        )\n    endif(${ARG_ERROR_ON_FAILURE})\n  endif(_MISSING_LIBS)\nendfunction(_check_and_print_dependencies\n  TARGET_NAME TARGET_LINK_LIBS_COMMAND\n  TARGET_INTERFACE_DEPS TARGET_PRIVATE_DEPS TARGET_PUBLIC_DEPS\n  LIST_OF_ALLOWED_EXTRA_TARGETS)\n\n# Check that the link libraries of the target are correct and if not,\n# provide an error with the correct target link libraries.\n#\n# Example usage:\n#\n#    check_target_dependencies(\n#      TARGET ${TARGET_TO_CHECK}\n#      ALL_TARGETS\n#      ${SPECTRE_TPLS}\n#      ${SPECTRE_LIBS}\n#      ALLOWED_EXTRA_TARGETS\n#      SpectreFlags\n#      ERROR_ON_FAILURE\n#      )\n#\n# Arguments:\n#\n# TARGET:\n#     the target for which to check the link libraries/dependencies\n# ALL_TARGETS:\n#     a list of all the targets that might be dependencies of the target\n#     ${TARGET}\n# ALLOWED_EXTRA_TARGETS:\n#     a list of allowed extra dependencies. An example of an extra dependency\n#     is the SpectreFlags target, which supplies no header files and therefore\n#     is not strictly a dependency of ${TARGET} but can be specified as\n#     dependency in order to add compiler flags or definitions to the target.\n# ERROR_ON_FAILURE:\n#     if specified, then CMake will produce an error if the dependencies are\n#     incorrect. Otherwise the variable TARGET_DEPENDENCIES_ERROR_MESSAGE\n#     is set in the parent scope\nfunction(check_target_dependencies)\n  cmake_parse_arguments(\n    ARG\n    \"ERROR_ON_FAILURE\"\n    \"TARGET\"\n    \"ALL_TARGETS;ALLOWED_EXTRA_TARGETS\" ${ARGN})\n\n  # Sets HEADER_FILES\n  _absolute_header_paths(${ARG_TARGET})\n\n  # Sets TARGET_DEPS\n  _get_deps_for_target(${ARG_TARGET}\n    \"${ARG_ALL_TARGETS}\" \"${HEADER_FILES}\" FALSE)\n  set(_TARGET_INTERFACE_DEPS ${TARGET_DEPS})\n  list(REMOVE_ITEM _TARGET_INTERFACE_DEPS Stl)\n\n  get_target_property(\n    TARGET_TYPE\n    ${ARG_TARGET}\n    TYPE\n    )\n\n  unset(_TARGET_PRIVATE_DEPS)\n  unset(_TARGET_PUBLIC_DEPS)\n  if(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    # Sets SOURCE_FILES\n    _absolute_source_paths(${ARG_TARGET})\n\n    # Sets TARGET_DEPS\n    _get_deps_for_target(${ARG_TARGET}\n      \"${ARG_ALL_TARGETS}\" \"${SOURCE_FILES}\" TRUE)\n    set(_TARGET_PRIVATE_DEPS ${TARGET_DEPS})\n    # We only use the Stl target to be able to keep track of all header files\n    # and to be able to claim with certainly that an extra header file is\n    # present. Since there are no explicit includes or link dependencies that\n    # need to be added, we don't want the extra noise of explicitly depending\n    # on Stl everywhere in the code and so we remove it from the list.\n    list(REMOVE_ITEM _TARGET_PRIVATE_DEPS Stl)\n\n    # Now need to figure out what the PUBLIC libraries are\n    unset(_TARGET_PUBLIC_DEPS)\n    foreach(_PRIVATE_DEP ${_TARGET_PRIVATE_DEPS})\n      if(${_PRIVATE_DEP} IN_LIST _TARGET_INTERFACE_DEPS)\n        list(APPEND _TARGET_PUBLIC_DEPS ${_PRIVATE_DEP})\n      endif(${_PRIVATE_DEP} IN_LIST _TARGET_INTERFACE_DEPS)\n    endforeach(_PRIVATE_DEP ${_TARGET_PRIVATE_DEPS})\n    # Remove PRIVATE deps from INTERFACE and PUBLIC lists\n    foreach(_PUBLIC_DEP ${_TARGET_PUBLIC_DEPS})\n      list(REMOVE_ITEM _TARGET_PRIVATE_DEPS ${_PUBLIC_DEP})\n      list(REMOVE_ITEM _TARGET_INTERFACE_DEPS ${_PUBLIC_DEP})\n    endforeach(_PUBLIC_DEP ${_TARGET_PUBLIC_DEPS})\n  endif(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n\n  set(ERROR_ON_FAILURE \"\")\n  if(ARG_ERROR_ON_FAILURE)\n    set(ERROR_ON_FAILURE ERROR_ON_FAILURE)\n  endif(ARG_ERROR_ON_FAILURE)\n  _check_and_print_dependencies(\n    ${ARG_TARGET}\n    \"${TARGET_LINK_LIBS_COMMAND}\"\n    \"${_TARGET_INTERFACE_DEPS}\"\n    \"${_TARGET_PRIVATE_DEPS}\"\n    \"${_TARGET_PUBLIC_DEPS}\"\n    \"${ARG_ALLOWED_EXTRA_TARGETS}\"\n    ${ERROR_ON_FAILURE}\n    )\n  if(NOT ARG_ERROR_ON_FAILURE)\n    set(\n      TARGET_DEPS_ERROR_MESSAGE\n      \"${TARGET_DEPS_ERROR_MESSAGE}\"\n      PARENT_SCOPE\n      )\n  endif(NOT ARG_ERROR_ON_FAILURE)\nendfunction(check_target_dependencies)\n"
  },
  {
    "path": "cmake/SpectreGetGitHash.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(GIT_HASH \"\")\nset(GIT_BRANCH_COMMAND \"\")\nset(GIT_BRANCH \"\")\nset(GIT_DESCRIPTION_COMMAND \"\")\nset(GIT_DESCRIPTION \"\")\n\nif(EXISTS ${CMAKE_SOURCE_DIR}/.git)\n  find_package(Git)\n\n  if(Git_FOUND)\n    execute_process(\n      COMMAND ${GIT_EXECUTABLE} rev-parse HEAD\n      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n      OUTPUT_VARIABLE GIT_HASH\n      OUTPUT_STRIP_TRAILING_WHITESPACE\n      )\n    set(GIT_BRANCH_COMMAND \"${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD\")\n    execute_process(\n      COMMAND bash -c \"${GIT_BRANCH_COMMAND}\"\n      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n      OUTPUT_VARIABLE GIT_BRANCH\n      OUTPUT_STRIP_TRAILING_WHITESPACE\n      )\n    set(GIT_DESCRIPTION_COMMAND \"${GIT_EXECUTABLE} describe \\\n--always --first-parent --match 'v[0-9]*' HEAD\")\n    execute_process(\n      COMMAND bash -c \"${GIT_DESCRIPTION_COMMAND}\"\n      WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}\n      OUTPUT_VARIABLE GIT_DESCRIPTION\n      OUTPUT_STRIP_TRAILING_WHITESPACE\n      ERROR_VARIABLE GIT_DESCRIPTION_ERROR\n      ERROR_STRIP_TRAILING_WHITESPACE\n      )\n    if(GIT_DESCRIPTION)\n      message(STATUS \"Git description: ${GIT_DESCRIPTION}\")\n    else()\n      message(STATUS \"Could not determine git description (\"\n        \"${GIT_DESCRIPTION_ERROR}). Using commit hash instead.\")\n      set(GIT_DESCRIPTION ${GIT_HASH})\n    endif()\n  endif()\nelse()\n  message(STATUS \"Not running in a git repository. Some features are disabled.\")\nendif()\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"Git description: ${GIT_DESCRIPTION}\\n\"\n  \"Git branch: ${GIT_BRANCH}\\n\"\n  \"Git hash: ${GIT_HASH}\\n\"\n  )\n"
  },
  {
    "path": "cmake/SpectreInitializeVariables.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif(NOT CMAKE_CXX_LINK_FLAGS)\n  set(CMAKE_CXX_LINK_FLAGS \"\")\nendif()\n"
  },
  {
    "path": "cmake/SpectreLoadMetadata.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Read the following information from the `Metadata.yaml` file:\n# - SPECTRE_NAME: Name\n# - SPECTRE_HOMEPAGE: Homepage\n# - SPECTRE_VERSION: Version\n# - SPECTRE_DOI: Doi\n# - SPECTRE_ZENODO_ID: ZenodoId\n\nfile(READ \"${CMAKE_SOURCE_DIR}/Metadata.yaml\" SPECTRE_METADATA_FILE)\n# Split lines into cmake list\nstring(REGEX REPLACE \";\" \"\\\\\\\\;\" SPECTRE_METADATA_FILE \"${SPECTRE_METADATA_FILE}\")\nstring(REGEX REPLACE \"\\n\" \";\" SPECTRE_METADATA_FILE \"${SPECTRE_METADATA_FILE}\")\n\n# Read data from the file\nset(SPECTRE_NAME \"\")\nset(SPECTRE_HOMEPAGE \"\")\nset(SPECTRE_VERSION \"\")\nset(SPECTRE_DOI \"\")\nset(SPECTRE_ZENODO_ID \"\")\nforeach(LINE ${SPECTRE_METADATA_FILE})\n  if(\"${LINE}\" MATCHES \"^Name: (.*)$\")\n    set(SPECTRE_NAME ${CMAKE_MATCH_1})\n  endif()\n  if(\"${LINE}\" MATCHES \"^Homepage: (.*)$\")\n    set(SPECTRE_HOMEPAGE ${CMAKE_MATCH_1})\n  endif()\n  if(\"${LINE}\" MATCHES \"^Version: (.*)$\")\n    set(SPECTRE_VERSION ${CMAKE_MATCH_1})\n  endif()\n  if(\"${LINE}\" MATCHES \"^Doi: (.*)$\")\n    set(SPECTRE_DOI ${CMAKE_MATCH_1})\n  endif()\n  if(\"${LINE}\" MATCHES \"^ZenodoId: (.*)$\")\n    set(SPECTRE_ZENODO_ID ${CMAKE_MATCH_1})\n  endif()\nendforeach()\n\n# Check we have read all required keys\nif(NOT SPECTRE_NAME)\n  message(\n    FATAL_ERROR \"Could not determine project name. Please check the \"\n    \"Metadata.yaml file exists and has a line that matches: ^Name: (.*)$\")\nendif()\nif(NOT SPECTRE_HOMEPAGE)\n  message(\n    WARNING \"Could not determine project homepage. Please check the \"\n    \"Metadata.yaml file exists and has a line that matches: ^Homepage: (.*)$\")\nendif()\nif(NOT SPECTRE_VERSION)\n  message(\n    FATAL_ERROR \"Could not determine release version. Please check the \"\n    \"Metadata.yaml file exists and has a line that matches: ^Version: (.*)$\")\nendif()\nif(NOT SPECTRE_DOI)\n  message(\n    WARNING \"Could not determine DOI. Please check the \"\n    \"Metadata.yaml file exists and has a line that matches: ^Doi: (.*)$\")\nendif()\nif(NOT SPECTRE_ZENODO_ID)\n  message(\n    WARNING \"Could not determine Zenodo ID. Please check the \"\n    \"Metadata.yaml file exists and has a line that matches: ^ZenodoId: (.*)$\")\nendif()\n\nmessage(STATUS \"${SPECTRE_NAME} release version: ${SPECTRE_VERSION}\")\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"SpECTRE Version: ${SPECTRE_VERSION}\\n\"\n  )\n"
  },
  {
    "path": "cmake/SpectreParseTests.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport re\n\n# A list of all the allowed ctest labels/Catch tags for tests\nallowed_tags = [\n    \"Actions\",\n    \"Ader\",\n    \"ApparentHorizonFinder\",\n    \"Burgers\",\n    \"Cce\",\n    \"CompilationTest\",\n    \"ControlSystem\",\n    \"DataStructures\",\n    \"Domain\",\n    \"Elasticity\",\n    \"Elliptic\",\n    \"EquationsOfState\",\n    \"ErrorHandling\",\n    \"Evolution\",\n    \"Fluxes\",\n    \"ForceFree\",\n    \"GeneralizedHarmonic\",\n    \"GrMhd\",\n    \"H5\",\n    \"Hydro\",\n    \"IO\",\n    \"Informer\",\n    \"Limiters\",\n    \"LinearAlgebra\",\n    \"LinearOperators\",\n    \"LinearSolver\",\n    \"M1Grey\",\n    \"NumericalAlgorithms\",\n    \"Observers\",\n    \"Options\",\n    \"Parallel\",\n    \"ParallelAlgorithms\",\n    \"PointwiseFunctions\",\n    \"Pypp\",\n    \"Python\",\n    \"RelativisticEuler\",\n    \"RootFinding\",\n    \"ScalarAdvection\",\n    \"Serialization\",\n    \"Spectral\",\n    \"Time\",\n    \"Unit\",\n    \"Utilities\",\n    \"VariableFixing\",\n]\n\n# Words disallowed in tests\ndisallowed_test_name_portions = [\"Functors\"]\n\n# Allowed test attributes\nallowed_test_attributes = [\"TimeOut\", \"OutputRegex\"]\n\n# All the timeout times for the different types of tests. The order here\n# matters. Whichever time is specified last is what will be used for the\n# test if it is of multiple types.\ndefault_tag_timeouts = [(\"unit\", 5)]\n\nallowed_tags = [x.lower() for x in allowed_tags]\nallowed_test_attributes = [x.lower() for x in allowed_test_attributes]\n\n\ndef parse_source_file(file_name):\n    # Read the file and remove include directives to make life easier.\n    file_string = str(\n        re.compile(\"#include.*\\n\").sub(\"\", str(open(file_name, \"r\").read()))\n    )\n    # The (.*?); part of the regex is to capture the first line of the test\n    # body. However, if the test case does not (yet) contain any semicolons then\n    # we accidentally fail to find a test.\n    test_regex = re.compile(\n        r\"(\\/\\/ \\[\\[.*?)?SPECTRE_TEST_CASE\\((.*?)\\) {(.*?);\", re.DOTALL\n    )\n    test_cases_found = re.findall(test_regex, file_string)\n\n    if not test_cases_found and not \"static_assert\" in file_string:\n        print(\n            \"\\n\\nERROR!!!\\nFailed to find any test cases in the file \",\n            file_name,\n        )\n        print(\n            \"This occurs when neither a static_assert nor any \"\n            \"SPECTRE_TEST_CASE are found. You may incorrectly hit this error\"\n            \" message if your test case does not yet contain any code. \"\n            \"Specifically, if your test case does not contain a semicolon.\"\n            \"\\n\\n\\n\\n\"\n        )\n        sys.exit(1)\n\n    for attributes, test_name, test_body_first_line in test_cases_found:\n        # Capture the name of the test into the first group and the tags into\n        # the second. For example,\n        # \"Unit.My.Test\", \"[Unit][My]\"\n        # test_name == Unit.My.Test\n        # group(2) == [Unit][My]\n        #\n        # Also handle cases where the name is very long and goes onto multiple\n        # lines,\n        # \"Unit.My.Test.Name.\"\n        #     \"Is.Very.Long\",\n        # \"[Unit][My]\"\n        # test_name == Unit.My.Test.Name.Is.Very.Long\n        # group(2) == [Unit][My]\n        parsed_name = re.search(r'\"(.*)\",[\\s]*\"(.*)\"', test_name, re.S)\n        test_name = \"\".join(parsed_name.group(1).replace('\"', \"\").split())\n        for disallowed_name in disallowed_test_name_portions:\n            if test_name.lower().find(disallowed_name.lower()) != -1:\n                print(\n                    \"\\nERROR: Found disallowed portion of a test name '%s' \"\n                    \"the test named '%s' in the file %s.\"\n                    % (disallowed_name, test_name, file_name)\n                )\n                exit(1)\n        test_tags = parsed_name.group(2).lower().replace(\"[\", \"\")[:-1]\n        test_tags = test_tags.split(\"]\")\n        for test_tag in test_tags:\n            if not test_tag in allowed_tags:\n                print(\n                    \"\\nERROR: The tag '%s' is not allowed but was found in \"\n                    \"the test '%s' in the file '%s'. To allow it add it to \"\n                    \"the 'allowed_tags' list in \"\n                    \"$SPECTRE_ROOT/cmake/SpectreParseTests.py. The currently \"\n                    \"allowed tags are:\\n%s\\n\\n\"\n                    % (test_tag, test_name, file_name, allowed_tags)\n                )\n                exit(1)\n        test_timeout = -1\n        for tag, timeout in default_tag_timeouts:\n            if tag in test_tags:\n                test_timeout = timeout\n\n        # Parse the test attributes\n        should_have_output_regex = \"OUTPUT_TEST()\" in test_body_first_line\n        output_regex = \"\"\n\n        all_attributes_by_name = re.findall(\n            r\"\\[\\[([^,]+), (.*?)\\]\\]\", attributes, re.DOTALL\n        )\n        for attribute in all_attributes_by_name:\n            if not attribute[0].lower() in allowed_test_attributes:\n                print(\n                    \"\\nERROR: Found unknown test attribute '%s' applied \"\n                    \"to test '%s' in file %s.\"\n                    % (attribute[0], test_name, file_name)\n                )\n                exit(1)\n\n            if attribute[0].lower() == \"timeout\":\n                test_timeout = attribute[1]\n\n            if attribute[0].lower() == \"outputregex\":\n                if not should_have_output_regex:\n                    print(\n                        \"\\nERROR: The test '%s' in the file '%s' has the \"\n                        \"attribute OutputRegEx, but does not contain the \"\n                        \"macro OUTPUT_TEST() as its first line.\\n\"\n                        % (test_name, file_name)\n                    )\n                    exit(1)\n                output_regex = attribute[1].replace(\"\\n//\", \"\")\n\n        if should_have_output_regex and output_regex == \"\":\n            print(\n                \"\\nERROR: The test '%s' in the file '%s' was marked as an \"\n                \"OUTPUT_TEST(), but \"\n                \"failed to produce a parsable OutputRegex attribute! \"\n                \"The syntax is // [[OutputRegex, <regular_expression>]] as \"\n                \"a comment before the SPECTRE_TEST_CASE.\\n\"\n                % (test_name, file_name)\n            )\n            exit(1)\n        open(\"%s.timeout\" % test_name, \"w\").write(\"%s\" % test_timeout)\n        open(\"%s.output_regex\" % test_name, \"w\").write(\"%s\" % output_regex)\n\n\nif __name__ == \"__main__\":\n    import sys\n\n    source_files = sys.argv[1:]\n    for filename in source_files:\n        parse_source_file(filename)\n"
  },
  {
    "path": "cmake/SpectrePythonExecutable.sh",
    "content": "#!/bin/bash -e\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nPYTHONPATH=\"@PYTHONPATH@\" @PYTHON_EXEC_ENV_VARS@ @Python_EXECUTABLE@ \\\n  @PYTHON_EXE_COMMAND@ \"$@\"\n"
  },
  {
    "path": "cmake/SpectreSetSiteName.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nsite_name(HOSTNAME)\n\nfile(APPEND\n  \"${CMAKE_BINARY_DIR}/BuildInfo.txt\"\n  \"Hostname: ${HOSTNAME}\\n\"\n  \"Host system: ${CMAKE_HOST_SYSTEM}\\n\"\n  \"Host system version: ${CMAKE_HOST_SYSTEM_VERSION}\\n\"\n  \"Host system processor: ${CMAKE_HOST_SYSTEM_PROCESSOR}\\n\"\n  )\n"
  },
  {
    "path": "cmake/SpectreSetupFlagsTarget.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# The library that contains all the flags for building SpECTRE targets.\n# Generator expressions should be used to limit flag scopes to specific\n# languages and compilers.\nadd_library(SpectreFlags INTERFACE)\n"
  },
  {
    "path": "cmake/SpectreSetupPythonPackage.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (ENABLE_PYTHON)\n  spectre_define_test_timeout_factor_option(PYTHON \"Python\")\n\n  option(PY_DEV_MODE \"Enable development mode for the Python package, meaning \\\n  that Python files are symlinked rather than copied to the build directory\" OFF)\n  function(configure_or_symlink_py_file SOURCE_FILE TARGET_FILE)\n    if(PY_DEV_MODE)\n      get_filename_component(_TARGET_FILE_DIR ${TARGET_FILE} DIRECTORY)\n      execute_process(COMMAND\n        ${CMAKE_COMMAND} -E make_directory ${_TARGET_FILE_DIR})\n      execute_process(COMMAND\n        ${CMAKE_COMMAND} -E create_symlink ${SOURCE_FILE} ${TARGET_FILE})\n    else()\n      configure_file(${SOURCE_FILE} ${TARGET_FILE} @ONLY)\n    endif()\n  endfunction()\n\n  set(SPECTRE_PYTHON_PREFIX \"${SPECTRE_PYTHON_PREFIX_PARENT}/spectre\")\n\n  # Create the root __init__.py file\n  configure_or_symlink_py_file(\n    \"${CMAKE_SOURCE_DIR}/support/Python/__init__.py\"\n    \"${SPECTRE_PYTHON_PREFIX}/__init__.py\"\n  )\n\n  # Create the root __main__.py entry point\n  configure_or_symlink_py_file(\n    \"${CMAKE_SOURCE_DIR}/support/Python/__main__.py\"\n    \"${SPECTRE_PYTHON_PREFIX}/__main__.py\"\n  )\n  # Also link the main entry point to bin/\n  set(PYTHON_EXE_COMMAND \"-m spectre\")\n  set(PYTHON_EXEC_ENV_VARS \"\")\n  # At some point we needed to preload jemalloc for the Python bindings to work,\n  # otherwise we got \"cannot allocate memory in static TLS block\" errors when\n  # using jemalloc. This is fixed by avoiding to link jemalloc into libraries in\n  # the first place (we only dynamically link it into executables). If we want to\n  # link jemalloc into libraries again (e.g. to use jemalloc features explicitly\n  # in the code), the best way to do this would be to build jemalloc with a prefix\n  # and use it as a custom allocator where needed, instead of generically\n  # replacing the system allocator.\n  #\n  # ParaView needs specific environment variables set, e.g. 'LD_LIBRARY_PATH', but\n  # they can interfere with simulations, e.g. when they point to ParaView's\n  # bundled MPI which may be different to the MPI we built with. Therefore we\n  # disable this for now. This means we can't use the pre-built ParaView binaries\n  # that bundle their own MPI and Python, unless we find a way to set the needed\n  # environment variables only when running ParaView. Everything should work\n  # when using a ParaView built with the same MPI and Python as the rest of\n  # SpECTRE.\n  # if(PARAVIEW_PYTHON_ENV_VARS)\n  #   string(APPEND PYTHON_EXEC_ENV_VARS \" ${PARAVIEW_PYTHON_ENV_VARS}\")\n  # endif()\n  #\n  # HDF5 file locking can be a problem when a simulation is writing data and we\n  # try to read it at the same time. We disable file locking by default when\n  # writing H5 files in simulations (see H5File.hpp), but readers may still have\n  # file locking enabled (e.g. h5py does this by default) and this creates file\n  # locks at the system level that prevent the simulation from writing to the\n  # file. Therefore we disable file locking here to avoid crashing simulations.\n  # Better solutions are described in H5File.hpp.\n  string(APPEND PYTHON_EXEC_ENV_VARS \" HDF5_USE_FILE_LOCKING=FALSE\")\n  configure_file(\n    \"${CMAKE_SOURCE_DIR}/cmake/SpectrePythonExecutable.sh\"\n    \"${CMAKE_BINARY_DIR}/tmp/spectre\")\n  file(COPY \"${CMAKE_BINARY_DIR}/tmp/spectre\"\n    DESTINATION \"${CMAKE_BINARY_DIR}/bin\"\n    FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ\n      GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)\n\n  configure_file(\n    \"${CMAKE_SOURCE_DIR}/cmake/SpectrePythonExecutable.sh\"\n    \"${CMAKE_BINARY_DIR}/tmp/spectre\")\n  file(COPY \"${CMAKE_BINARY_DIR}/tmp/spectre\"\n    DESTINATION \"${CMAKE_BINARY_DIR}/bin\"\n    FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ\n      GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)\n\n  # Also link the Python interpreter to bin/python-spectre as an easy way to jump\n  # into a Python shell or run a script that uses pybindings\n  set(PYTHON_EXE_COMMAND \"\")\n  configure_file(\n    \"${CMAKE_SOURCE_DIR}/cmake/SpectrePythonExecutable.sh\"\n    \"${CMAKE_BINARY_DIR}/tmp/python-spectre\")\n  file(COPY \"${CMAKE_BINARY_DIR}/tmp/python-spectre\"\n    DESTINATION \"${CMAKE_BINARY_DIR}/bin\"\n    FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ\n      GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)\n\n  # Write configuration files for installing the Python modules\n  file(STRINGS\n    ${CMAKE_SOURCE_DIR}/support/Python/requirements.txt\n    SPECTRE_PY_DEPS)\n  file(STRINGS\n    ${CMAKE_SOURCE_DIR}/support/Python/dev_requirements.txt\n    SPECTRE_PY_DEV_DEPS)\n  list(FILTER SPECTRE_PY_DEPS EXCLUDE REGEX \"^#\")\n  list(FILTER SPECTRE_PY_DEV_DEPS EXCLUDE REGEX \"^#\")\n  list(REMOVE_ITEM SPECTRE_PY_DEPS \"\")\n  list(REMOVE_ITEM SPECTRE_PY_DEV_DEPS \"\")\n  list(JOIN SPECTRE_PY_DEPS \"\\n    \" SPECTRE_PY_DEPS_OUTPUT)\n  list(JOIN SPECTRE_PY_DEV_DEPS \"\\n    \" SPECTRE_PY_DEV_DEPS_OUTPUT)\n  configure_or_symlink_py_file(\n    \"${CMAKE_SOURCE_DIR}/pyproject.toml\"\n    \"${SPECTRE_PYTHON_PREFIX_PARENT}/pyproject.toml\")\n  configure_or_symlink_py_file(\n    \"${CMAKE_SOURCE_DIR}/setup.cfg\"\n    \"${SPECTRE_PYTHON_PREFIX_PARENT}/setup.cfg\")\n\n  # Write a file to be able to set up the new python path.\n  file(WRITE\n    \"${CMAKE_BINARY_DIR}/tmp/LoadPython.sh\"\n    \"#!/bin/sh\\n\"\n    \"export PYTHONPATH=${PYTHONPATH}\\n\"\n    )\n  configure_file(\n    \"${CMAKE_BINARY_DIR}/tmp/LoadPython.sh\"\n    \"${CMAKE_BINARY_DIR}/bin/LoadPython.sh\")\n\n  # Install the SpECTRE Python package to the CMAKE_INSTALL_PREFIX, using pip.\n  # This will install the package into the expected subdirectory, typically\n  # `lib/pythonX.Y/site-packages/`. It also creates symlinks to entry points\n  # specified in `setup.py`.\n  install(\n    CODE \"execute_process(\\\n      COMMAND ${Python_EXECUTABLE} -m pip install \\\n        --no-deps --no-input --no-cache-dir --no-index --ignore-installed \\\n        --disable-pip-version-check --no-build-isolation \\\n        --prefix ${CMAKE_INSTALL_PREFIX} ${SPECTRE_PYTHON_PREFIX_PARENT} \\\n      )\"\n    )\nendif()\nadd_custom_target(all-pybindings)\n\n# Add a python module, either with or without python bindings and with\n# or without additional python files. If bindings are being provided then\n# the library will be named Py${MODULE_NAME}, e.g. if MODULE_NAME is\n# DataStructures then the library name is PyDataStructures.\n#\n# - MODULE_NAME   The name of the module, e.g. DataStructures.\n#\n# - MODULE_PATH   Path inside the module, e.g. submodule0/submodule1 would\n#                 result in loading spectre.submodule0.submodule1\n#\n# - SOURCES       The C++ source files for bindings. Omit if no bindings\n#                 are being generated.\n#\n# - LIBRARY_NAME  The name of the C++ libray, e.g. PyDataStructures.\n#                 Required if SOURCES are specified. Must begin with \"Py\".\n#\n# - PYTHON_FILES  List of the python files (relative to\n#                 ${CMAKE_SOURCE_DIR}/src) to add to the module. Omit if\n#                 no python files are to be provided.\nfunction(SPECTRE_PYTHON_ADD_MODULE MODULE_NAME)\n  if (NOT ENABLE_PYTHON)\n    return()\n  endif()\n  set(SINGLE_VALUE_ARGS MODULE_PATH LIBRARY_NAME)\n  set(MULTI_VALUE_ARGS SOURCES PYTHON_FILES)\n  cmake_parse_arguments(\n    ARG \"\"\n    \"${SINGLE_VALUE_ARGS}\"\n    \"${MULTI_VALUE_ARGS}\"\n    ${ARGN})\n\n  set(MODULE_LOCATION\n    \"${SPECTRE_PYTHON_PREFIX}/${ARG_MODULE_PATH}/${MODULE_NAME}\")\n  get_filename_component(MODULE_LOCATION ${MODULE_LOCATION} ABSOLUTE)\n\n  # Add our python library, if it has sources\n  if(BUILD_PYTHON_BINDINGS AND NOT \"${ARG_SOURCES}\" STREQUAL \"\")\n    if(\"${ARG_LIBRARY_NAME}\" STREQUAL \"\")\n      message(FATAL_ERROR \"Set a LIBRARY_NAME for Python module \"\n          \"'${MODULE_NAME}' that has sources.\")\n    endif()\n    if(NOT \"${ARG_LIBRARY_NAME}\" MATCHES \"^Py\")\n      message(FATAL_ERROR \"The LIBRARY_NAME for Python module \"\n          \"'${MODULE_NAME}' must begin with 'Py' but is '${ARG_LIBRARY_NAME}'.\")\n    endif()\n\n    Python_add_library(${ARG_LIBRARY_NAME} MODULE ${ARG_SOURCES})\n    set_target_properties(\n      ${ARG_LIBRARY_NAME}\n      PROPERTIES\n      # These can be turned on once we support them\n      INTERPROCEDURAL_OPTIMIZATION OFF\n      CXX__VISIBILITY_PRESET OFF\n      VISIBLITY_INLINES_HIDDEN OFF\n      )\n\n    if (TARGET SpectrePch)\n      target_precompile_headers(${ARG_LIBRARY_NAME} REUSE_FROM SpectrePch)\n      target_link_libraries(${ARG_LIBRARY_NAME} PRIVATE SpectrePchFlags)\n    endif()\n\n    # In order to avoid runtime errors about missing compatibility functions\n    # defined in the PythonBindings library, we need to link in the whole\n    # archive. This is not needed on macOS.\n    if (APPLE)\n      target_link_libraries(\n        ${ARG_LIBRARY_NAME}\n        PUBLIC PythonBindings\n        )\n    else()\n      target_link_libraries(\n        ${ARG_LIBRARY_NAME}\n        PUBLIC\n        -Wl,--whole-archive\n        PythonBindings\n        -Wl,--no-whole-archive\n        )\n    endif()\n    target_link_libraries(\n      ${ARG_LIBRARY_NAME}\n      PRIVATE\n      CharmModuleInit\n      SpectreFlags\n      )\n    # We don't want the 'lib' prefix for python modules, so we set the output\n    # name\n    set_target_properties(\n      ${ARG_LIBRARY_NAME}\n      PROPERTIES\n      PREFIX \"\"\n      LIBRARY_OUTPUT_NAME \"_Pybindings\"\n      LIBRARY_OUTPUT_DIRECTORY ${MODULE_LOCATION}\n      )\n    # We need --no-as-needed since each python module needs to depend on all the\n    # shared libraries in order to run successfully.\n    set(PY_LIB_LINK_FLAGS \"${CMAKE_CXX_LINK_FLAGS}\")\n    if(NOT APPLE)\n      set(PY_LIB_LINK_FLAGS\n        \"${CMAKE_CXX_LINK_FLAGS} -Wl,--no-as-needed\")\n    endif()\n    set_target_properties(\n      ${ARG_LIBRARY_NAME}\n      PROPERTIES\n      LINK_FLAGS \"${PY_LIB_LINK_FLAGS}\"\n      )\n    if(BUILD_TESTING)\n      add_dependencies(test-executables ${ARG_LIBRARY_NAME})\n    endif()\n    add_dependencies(all-pybindings ${ARG_LIBRARY_NAME})\n  endif(BUILD_PYTHON_BINDINGS AND NOT \"${ARG_SOURCES}\" STREQUAL \"\")\n\n  # configure the Python source files into the build directory\n  foreach(PYTHON_FILE ${ARG_PYTHON_FILES})\n    # Configure file\n    get_filename_component(PYTHON_FILE_JUST_NAME\n      \"${CMAKE_CURRENT_SOURCE_DIR}/${PYTHON_FILE}\" NAME)\n    configure_or_symlink_py_file(\n      \"${CMAKE_CURRENT_SOURCE_DIR}/${PYTHON_FILE}\"\n      \"${MODULE_LOCATION}/${PYTHON_FILE_JUST_NAME}\"\n      )\n  endforeach(PYTHON_FILE ${ARG_PYTHON_FILES})\n\n  # Create empty __init__.py files if none exist\n  # We walk up the tree until we get to ${SPECTRE_PYTHON_PREFIX}\n  set(CURRENT_MODULE ${MODULE_LOCATION})\n  while(NOT ${CURRENT_MODULE} STREQUAL ${SPECTRE_PYTHON_PREFIX})\n    set(INIT_FILE_LOCATION \"${CURRENT_MODULE}/__init__.py\")\n    if(NOT EXISTS ${INIT_FILE_LOCATION})\n      file(WRITE ${INIT_FILE_LOCATION} \"\")\n    endif()\n    get_filename_component(CURRENT_MODULE \"${CURRENT_MODULE}/..\" ABSOLUTE)\n  endwhile()\nendfunction()\n\n# Add headers if Python bindings are being built\nfunction (spectre_python_headers LIBRARY_NAME)\n  if(NOT BUILD_PYTHON_BINDINGS)\n    return()\n  endif()\nspectre_target_headers(\n    ${LIBRARY_NAME}\n    # Forward all remaining arguments\n    ${ARGN}\n    )\nendfunction()\n\n# Link with the LIBRARIES if Python bindings are being built\nfunction (spectre_python_link_libraries LIBRARY_NAME)\n  if(NOT BUILD_PYTHON_BINDINGS)\n    return()\n  endif()\n  target_link_libraries(\n    ${LIBRARY_NAME}\n    # Forward all remaining arguments\n    ${ARGN}\n    )\nendfunction()\n\n# Add the DEPENDENCIES if Python bindings are being built\nfunction (spectre_python_add_dependencies LIBRARY_NAME)\n  if(NOT BUILD_PYTHON_BINDINGS)\n    return()\n  endif()\n  add_dependencies(\n    ${LIBRARY_NAME}\n    # Forward all remaining arguments\n    ${ARGN}\n    )\nendfunction()\n\n# Register a python test file with ctest.\n# - TEST_NAME    The name of the test,\n#                e.g. \"Unit.DataStructures.Python.DataVector\"\n#\n# - FILE         The file to add, e.g. Test_DataVector.py\n#\n# - TAGS         A semicolon separated list of labels for the test,\n#                e.g. \"Unit;DataStructures;Python\"\n# - PY_MODULE_DEPENDENCY\n#                The python module that this test depends on\n#                Set to None if there is no python module dependency\n#\n# Optionally:\n# - TIMEOUT secs Set the test timeout (default 2)\nfunction(SPECTRE_ADD_PYTHON_TEST TEST_NAME FILE TAGS\n    PY_MODULE_DEPENDENCY)\n  cmake_parse_arguments(ARG \"\" TIMEOUT \"\" ${ARGN})\n  if(DEFINED ARG_TIMEOUT)\n    set(TIMEOUT ${ARG_TIMEOUT})\n  else()\n    set(TIMEOUT 2)\n  endif()\n  get_filename_component(FILE \"${FILE}\" ABSOLUTE)\n  string(TOLOWER \"${TAGS}\" TAGS)\n\n  add_test(\n    NAME \"${TEST_NAME}\"\n    COMMAND\n    ${Python_EXECUTABLE}\n    ${FILE}\n    )\n\n  spectre_test_timeout(TIMEOUT PYTHON ${TIMEOUT})\n\n  set(_PY_TEST_ENV_VARS \"PYTHONPATH=${PYTHONPATH}\")\n\n  # The fail regular expression is what Python.unittest returns when no\n  # tests are found to be run. We treat this as a test failure.\n  set_tests_properties(\n    \"${TEST_NAME}\"\n    PROPERTIES\n    FAIL_REGULAR_EXPRESSION \"Ran 0 test\"\n    TIMEOUT ${TIMEOUT}\n    LABELS \"${TAGS};Python\"\n    ENVIRONMENT \"${_PY_TEST_ENV_VARS}\"\n    )\n  # check if this is a unit test, and if so add it to the dependencies\n  foreach(LABEL ${TAGS})\n    string(TOLOWER \"${LABEL}\" LOWER_LABEL)\n    string(TOLOWER \"${PY_MODULE_DEPENDENCY}\" LOWER_DEP)\n    if(\"${LOWER_LABEL}\" STREQUAL \"unit\"\n        AND NOT \"${LOWER_DEP}\" STREQUAL \"none\")\n      add_dependencies(unit-tests ${PY_MODULE_DEPENDENCY})\n    endif()\n  endforeach()\nendfunction()\n\n# Register a python test file that uses bindings with ctest.\n# - TEST_NAME    The name of the test,\n#                e.g. \"Unit.DataStructures.Python.DataVector\"\n#\n# - FILE         The file to add, e.g. Test_DataVector.py\n#\n# - TAGS         A semicolon separated list of labels for the test,\n#                e.g. \"Unit;DataStructures;Python\"\n# - PY_MODULE_DEPENDENCY\n#                The python module that this test depends on\n#                Set to None if there is no python module dependency\n#\n# Optionally:\n# - TIMEOUT secs Set the test timeout (default 2)\nfunction(SPECTRE_ADD_PYTHON_BINDINGS_TEST TEST_NAME FILE TAGS\n    PY_MODULE_DEPENDENCY)\n  if(NOT BUILD_PYTHON_BINDINGS)\n    return()\n  endif()\n  spectre_add_python_test(\n    ${TEST_NAME} ${FILE} \"${TAGS}\" ${PY_MODULE_DEPENDENCY} ${ARGN})\nendfunction()\n\n# Add a convenient target name for the pybindings.\nadd_custom_target(cli)\nadd_dependencies(cli all-pybindings)\n"
  },
  {
    "path": "cmake/SpectreSetupTesting.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\ninclude(CTest)\n\nset(SPECTRE_TEST_RUNNER \"\" CACHE STRING\n  \"Run test executables through the given wrapper.\")\n\noption(SPECTRE_TEST_TIMEOUT_FACTOR\n  \"Multiply timeout for tests by this factor\")\n\nfunction(spectre_define_test_timeout_factor_option TEST_TYPE HELP_NAME)\n  option(SPECTRE_${TEST_TYPE}_TEST_TIMEOUT_FACTOR\n    \"Multiply timeout for ${HELP_NAME} tests by this factor\")\nendfunction ()\n\n# Multiply BASE_TIMEOUT by the user specified option for TEST_TYPE\nfunction(spectre_test_timeout RETURN_VARIABLE TEST_TYPE BASE_TIMEOUT)\n  if (SPECTRE_${TEST_TYPE}_TEST_TIMEOUT_FACTOR)\n    set(FACTOR ${SPECTRE_${TEST_TYPE}_TEST_TIMEOUT_FACTOR})\n  elseif (SPECTRE_TEST_TIMEOUT_FACTOR)\n    set(FACTOR ${SPECTRE_TEST_TIMEOUT_FACTOR})\n  else ()\n    set(FACTOR 1)\n  endif ()\n\n  # Note: \"1\" is parsed as \"ON\" by cmake\n  if (NOT \"${FACTOR}\" STREQUAL ON)\n    math(EXPR RESULT \"${FACTOR} * ${BASE_TIMEOUT}\")\n  else ()\n    set(RESULT ${BASE_TIMEOUT})\n  endif ()\n\n  set(${RETURN_VARIABLE} ${RESULT} PARENT_SCOPE)\nendfunction ()\n"
  },
  {
    "path": "cmake/SpectreTargetHeaders.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Add header files for CMake to track for a given library\n# or executable.\n#\n# The header and source files together are used to track dependencies between\n# targets automatically in SpECTRE. Specifically, CI checks that the\n# dependencies of a target supply all the header files used in a library.\n# While this checking still requires includes to be correct (be they checked\n# manually or automatically), it significantly lowers the burden of correct\n# dependency management on developers.\n#\n# Additionally, some IDEs perform better if the header files for libraries\n# are known.\n#\n# Usage:\n#  spectre_target_headers(\n#    TARGET_NAME\n#    INCLUDE_DIRECTORY /path/to/include/relative/to\n#    HEADERS\n#    A.hpp\n#    B.hpp\n#    B.tpp\n#    C.hpp)\n#\n# Arguments:\n# - TARGET_NAME: the name of the library or executable\n# - INCLUDE_DIRECTORY: the directory relative to which the header\n#                      files must be included in C++ files. E.g.\n#                      `${CMAKE_SOURCE_DIR}/src` if the include paths are\n#                      relative to `${CMAKE_SOURCE_DIR}/src`.\n# - HEADERS: the HEADERS argument is a list of all the headers files\n#            to append, including tpp files, relative to the current\n#            source directory.\nfunction(spectre_target_headers TARGET_NAME)\n  cmake_parse_arguments(\n    ARG \"\" \"INCLUDE_DIRECTORY\" \"HEADERS\"\n    ${ARGN})\n\n  get_target_property(\n    TARGET_TYPE\n    ${TARGET_NAME}\n    TYPE\n    )\n\n  if(NOT ARG_INCLUDE_DIRECTORY)\n    message(FATAL_ERROR\n      \"Must specify the include directory relative to which the \"\n      \"header files for a library will be included when calling \"\n      \"spectre_target_headers. The named argument is INCLUDE_DIRECTORY. \"\n      \"Call was made for target ${TARGET_NAME}.\")\n  endif(NOT ARG_INCLUDE_DIRECTORY)\n  # Note: Search the list because an empty HEADERS is okay.\n  list(FIND ARGN \"HEADERS\" _FOUND_HEADERS)\n  if(${_FOUND_HEADERS} EQUAL -1)\n    message(FATAL_ERROR\n      \"No HEADERS section specified in call to spectre_target_headers \"\n      \"for target ${TARGET_NAME}. You must specificy HEADERS before \"\n      \"listing the header files.\")\n  endif()\n\n  if(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    get_property(\n      INCLUDE_DIRS\n      TARGET ${TARGET_NAME}\n      PROPERTY INCLUDE_DIRECTORIES\n      )\n    if(NOT ${ARG_INCLUDE_DIRECTORY} IN_LIST INCLUDE_DIRS)\n      set_property(\n        TARGET ${TARGET_NAME}\n        APPEND\n        PROPERTY INCLUDE_DIRECTORIES ${ARG_INCLUDE_DIRECTORY}\n        )\n    endif(NOT ${ARG_INCLUDE_DIRECTORY} IN_LIST INCLUDE_DIRS)\n  endif(NOT ${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n\n  get_property(\n    INTERFACE_INCLUDE_DIRS\n    TARGET ${TARGET_NAME}\n    PROPERTY INTERFACE_INCLUDE_DIRECTORIES\n    )\n  if(NOT ${ARG_INCLUDE_DIRECTORY} IN_LIST INTERFACE_INCLUDE_DIRS)\n    set_property(\n      TARGET ${TARGET_NAME}\n      APPEND\n      PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${ARG_INCLUDE_DIRECTORY}\n      )\n  endif(NOT ${ARG_INCLUDE_DIRECTORY} IN_LIST INTERFACE_INCLUDE_DIRS)\n\n  unset(_HEADER_FILES)\n  foreach(HEADER ${ARG_HEADERS})\n    if(NOT HEADER STREQUAL \"PRIVATE\" AND\n        NOT HEADER STREQUAL \"PUBLIC\" AND\n        NOT HEADER STREQUAL \"INTERFACE\" AND\n        NOT IS_ABSOLUTE \"${HEADER}\")\n      set(_ABSOLUTE_PATH \"${CMAKE_CURRENT_LIST_DIR}/${HEADER}\")\n      file(RELATIVE_PATH HEADER ${ARG_INCLUDE_DIRECTORY} ${_ABSOLUTE_PATH})\n    endif()\n    list(APPEND _HEADER_FILES ${HEADER})\n  endforeach()\n\n  if(${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    add_interface_lib_headers(\n      TARGET ${TARGET_NAME}\n      HEADERS\n      ${_HEADER_FILES}\n      )\n  else(${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\n    set_property(\n      TARGET ${TARGET_NAME}\n      APPEND\n      PROPERTY\n      PUBLIC_HEADER\n      ${_HEADER_FILES}\n      )\n  endif(${TARGET_TYPE} STREQUAL INTERFACE_LIBRARY)\nendfunction(spectre_target_headers TARGET_NAME)\n"
  },
  {
    "path": "cmake/SpectreTargetSources.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Alias for target_sources.\nfunction(spectre_target_sources TARGET)\n  target_sources(${TARGET} ${ARGN})\nendfunction()\n"
  },
  {
    "path": "cmake/StripSymbols.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\noption(STRIP_SYMBOLS \"Strip symbols from executables\" OFF)\n\nif (STRIP_SYMBOLS)\n  if (APPLE)\n    message(FATAL_ERROR \"Stripping all symbols is currently not supported on \"\n    \"macOS. Please disable the `STRIP_SYMBOLS` flag.\")\n  else (APPLE)\n    set(CMAKE_EXE_LINKER_FLAGS \"${CMAKE_EXE_LINKER_FLAGS} -Wl,--strip-all\")\n  endif (APPLE)\nendif(STRIP_SYMBOLS)\n"
  },
  {
    "path": "cmake/UpdateAddExecutables.cmake",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfunction(add_spectre_executable TARGET_NAME)\n  _add_spectre_executable(${TARGET_NAME} ${ARGN})\n\n  if (USE_PCH)\n    get_target_property(\n      TARGET_IS_IMPORTED\n      ${TARGET_NAME}\n      IMPORTED\n      )\n    if(NOT ${TARGET_IS_IMPORTED} AND TARGET SpectrePch)\n      target_precompile_headers(${TARGET_NAME} REUSE_FROM SpectrePch)\n      target_link_libraries(${TARGET_NAME} PRIVATE SpectrePchFlags)\n    endif()\n  endif (USE_PCH)\nendfunction()\n"
  },
  {
    "path": "containers/Dockerfile.buildenv",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# If you change this file please push the new images to DockerHub. If you do not\n# have permission to push to DockerHub please coordinate with someone who does.\n# Since changes to this image affect our testing infrastructure it is important\n# all changes be carefully reviewed before they are pushed.\n#\n# GitHub Actions (recommended method)\n#\n# This is how you use GitHub Actions to build and push the new images:\n#\n# 1. Push your changes to the `develop` branch on your fork of the spectre\n#    repository on GitHub (https://github.com/YOURNAME/spectre).\n# 2. Go to https://github.com/YOURNAME/spectre/actions/workflows/BuildDockerContainer.yaml.\n# 3. Select \"Run workflow\". Here you can select the location on DockerHub where\n#    you want the new images pushed. Select `sxscollaboration/spectre` (which is\n#    used by CI) or a location on your own DockerHub account. Either way, you\n#    need to set the `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN` secrets in the\n#    repository to credentials that have write access.\n# 4. The images will be built and pushed to DockerHub and CI will run over them.\n#    See `.github/workflows/BuildDockerContainer.yaml` for details.\n#\n# Local build & push (can be tedious and time consuming)\n#\n# What you need to push to Dockerhub is:\n#\n#   Arch: linux/amd64,linux/arm64\n#         # Note that cross compiling can be slow, so for testing just do amd64\n#         # or arm64 depending on your CPU. Docker builds the image in a way\n#         # that when someone clones it, depending on their CPU architecture,\n#         # they will get either the amd64 or arm64 version.\n#   Ubuntu version: 22.04 (default)\n#   --platform linux/amd64,linux/arm64\n#   --target dev\n#   -t sxscollaboration/spectre:dev\n#\n#   Arch: linux/amd64\n#   Ubuntu version: 18.04\n#   --platform linux/amd64\n#   --target dev\n#   -t sxscollaboration/spectre:dev1804\n#   --build-arg UBUNTU_VERSION=18.04\n#\n# Docker must be run as root on your machine. There are 4 different images in\n# this Dockerfile, but you can ignore the 'deploy_static_execs_and_libs' since\n# it's only used in CI. Here is how to build them for x86_64 (for Apple Silicon,\n# replace linux/amd64 with linux/arm64):\n#\n# 1. `dev`\n#\n#   cd $SPECTRE_HOME\n#   docker build --target dev -t sxscollaboration/spectre:dev \\\n#                 --platform linux/amd64 \\\n#                 -f ./containers/Dockerfile.buildenv .\n#\n#   # Cross compiling for AMD64 and ARM64:\n#   #\n#   # To check if you can cross compile, first run\n#   #   docker buildx ls\n#   # This will look something like:\n#   #  NAME/NODE        DRIVER/ENDPOINT                   STATUS    BUILDKIT   \\\n#   #    PLATFORMS\n#   # default*          docker\n#   #  \\_ default       \\_ default                       running   v0.13.2    \\\n#   #     linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386\n#   #\n#   # Note that the DRIVER/ENDPOINT is 'docker'. We want to instead use\n#   # 'docker-container'. To do this, run\n#   #    docker buildx create --name mybuilder --bootstrap --use\n#   # When you run 'docker buildx ls' now you should see\n#   #  mybuilder*       docker-container\n#   # The asterisk means docker will use this builder. You are now ready to\n#   # cross-compile code.\n#   #\n#   # This is documented at:\n#   #   https://docs.docker.com/build/building/multi-platform/#getting-started\n#\n#   docker buildx build --target dev -t sxscollaboration/spectre:dev \\\n#                 --platform linux/amd64,linux/arm64 \\\n#                 -f ./containers/Dockerfile.buildenv --push .\n#\n# You DO NOT need to push the below. CI does that automatically.\n#\n# 3. `demo` To build this, you MUST first push the `dev` images to\n#           DockerHub as the `demo` image uses the remote `dev` image.\n#\n#   docker push sxscollaboration/spectre:dev\n#\n#    To build `demo`, you must be in $SPECTRE_ROOT and there cannot be a\n#    directory named `build` in $SPECTRE_ROOT because the image will create\n#    this directory (in the container).\n#\n#   cd $SPECTRE_HOME\n#   rm -rf build/\n#   docker build --target demo -t sxscollaboration/spectre:demo \\\n#                 --platform linux/amd64 \\\n#                 -f ./containers/Dockerfile.buildenv .\n#\n#    and then to push the `demo` image to DockerHub:\n#\n#   docker push sxscollaboration/spectre:demo\n#\n# 4. `deploy_static_execs_and_libs` This is used for building various\n#    executables and libraries with an old enough version of glibc so\n#    that the resulting executables are quite portable. This must be built\n#    as, but should generally only be done in CI unless you are debugging\n#    the container build.\n#\n#  docker build --target deploy_static_execs_and_libs --platform linux/amd64 \\\n#    --build-arg UBUNTU_VERSION=18.04 -f ./containers/Dockerfile.buildenv .\n#\n#    That is, it uses Ubuntu 18.04 and AMD64. There is no support for ARM and\n#    newer versions of Ubuntu will limit compatibility.\nARG UBUNTU_VERSION=22.04\nFROM ubuntu:${UBUNTU_VERSION} AS base\nARG UBUNTU_VERSION=22.04\n\n# See\n# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope\n# for how TARGETARCH is defined.\nARG TARGETARCH\n\n# Install add-apt-repository and basic tools\nRUN if [ ${UBUNTU_VERSION} = 18.04 ] && [ \"$TARGETARCH\" = \"arm64\" ]; then \\\n    echo \"Cannot use Ubuntu 18.04 with ARM\" && exit 1; fi && apt-get update -y \\\n    && apt-get install -y software-properties-common wget git file \\\n    && add-apt-repository ppa:ubuntu-toolchain-r/test\n\n# Add LLVM apt repository for newer versions of clang\nRUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | \\\n    tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc \\\n    && if [ ${UBUNTU_VERSION} = 18.04 ]; then \\\n    add-apt-repository -y \\\n    'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-17 main'; \\\n    elif [ ${UBUNTU_VERSION} = 22.04 ]; then \\\n    add-apt-repository -y \\\n    'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-16 main'; \\\n    else \\\n    exit 1; \\\n    fi\n\n# Install compilers and build tools\nRUN apt-get update -y \\\n    && if [ ${UBUNTU_VERSION} = 18.04 ]; then \\\n    apt-get install -y gcc-10 g++-10 gfortran-10 clang-17; \\\n    else \\\n    apt-get install -y gcc g++ gfortran clang clang-format clang-tidy cmake; \\\n    fi \\\n    && apt-get install -y lld bison flex libncurses-dev gdb autoconf automake \\\n    ninja-build lcov \\\n    && if [ ${UBUNTU_VERSION} = 18.04 ]; then \\\n    wget -O cmake-install.sh \\\n    \"https://github.com/Kitware/CMake/releases/download/v3.28.1/cmake-3.28.1-linux-x86_64.sh\" \\\n    && chmod +x ./cmake-install.sh && ./cmake-install.sh --skip-license \\\n    --prefix=/usr/local && rm cmake-install.sh; \\\n    fi\n\n# Install SpECTRE dependencies that are available through apt\n#\n# We intentionally don't install libboost-all-dev because that installs\n# Boost.MPI, which installs OpenMPI into the container. When MPI is\n# installed inside the container it makes it very difficult to use\n# Singularity on HPC systems to interface with the system MPI library.\n# The system MPI libraries are usually configured to take advantage of\n# InfiniBand or various other networking layers.\nRUN apt-get update -y \\\n    && apt-get install -y \\\n        libopenblas-dev liblapack-dev \\\n        libgsl-dev \\\n        libboost-dev libboost-program-options-dev \\\n        libboost-thread-dev libboost-tools-dev libssl-dev \\\n        libhdf5-dev hdf5-tools \\\n        libarpack2-dev \\\n        libbenchmark-dev \\\n    && if [ ${UBUNTU_VERSION} = 18.04 ]; then \\\n    wget https://github.com/jbeder/yaml-cpp/archive/refs/tags/0.8.0.tar.gz \\\n    && tar xf 0.8.0.tar.gz && mkdir ./yaml-cpp-0.8.0/build \\\n    && cd ./yaml-cpp-0.8.0/build \\\n    && cmake \\\n    -D CMAKE_C_COMPILER=gcc-10 \\\n    -D CMAKE_CXX_COMPILER=g++-10 \\\n    -D CMAKE_BUILD_TYPE=Release \\\n    -D YAML_BUILD_SHARED_LIBS=OFF \\\n    -D BUILD_SHARED_LIBS=OFF \\\n    -D YAML_ENABLE_PIC=ON \\\n    -D POSITION_INDEPENDENT_CODE=ON \\\n    -D YAML_CPP_BUILD_TESTS=OFF \\\n    -D YAML_CPP_BUILD_CONTRIB=OFF \\\n    -D CMAKE_INSTALL_PREFIX=/usr/local \\\n    .. \\\n    && make ${PARALLEL_MAKE_ARG} && make install \\\n    && cd ../.. && rm -rf ./0.8.0.tar.gz ./yaml-cpp-0.8.0; \\\n    else \\\n    apt-get install -y libjemalloc2 libjemalloc-dev libyaml-cpp-dev; \\\n    fi\n\n# Install Python packages\n# We only install packages that are needed by the build system (e.g. to compile\n# Python bindings or build documentation) or used by Python code that is\n# unit-tested. Any other packages can be installed on-demand.\n# - We use python-is-python3 because on Ubuntu 20.04 /usr/bin/python was removed\n#   to aid in tracking down anything that depends on python 2. However, many\n#   scripts use `/usr/bin/env python` to find python so restore it.\n# - We install h5py explicitly from binary so that cross compilation is quicker.\nCOPY support/Python/requirements.txt requirements.txt\nCOPY support/Python/dev_requirements.txt dev_requirements.txt\nENV DEBIAN_FRONTEND noninteractive\nRUN apt-get update -y \\\n    && if [ ${UBUNTU_VERSION} = 18.04 ]; then \\\n    apt-get install -y zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev \\\n    libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev \\\n    make \\\n    && wget https://www.python.org/ftp/python/3.10.1/Python-3.10.1.tgz \\\n    && tar -xf Python-3.10.1.tgz && cd ./Python-3.10.1 \\\n    && ./configure --enable-optimizations && make ${PARALLEL_MAKE_ARG} \\\n    && make altinstall && cd ../ \\\n    && rm -rf ./Python-3.10.1.tgz ./Python-3.10.1 \\\n    && python3.10 -m pip install --upgrade pip \\\n    && pip3.10 --no-cache-dir install --only-binary=h5py -r requirements.txt \\\n    -r dev_requirements.txt; \\\n    else \\\n    apt-get install -y python3-pip python-is-python3 pkg-config \\\n    && pip3 --no-cache-dir install --only-binary=h5py -r requirements.txt \\\n    -r dev_requirements.txt; \\\n    fi \\\n    && rm requirements.txt dev_requirements.txt\n\n# Enable bash-completion by installing it and then adding it to the .bashrc file\nRUN apt-get update -y \\\n    && apt-get install -y bash-completion \\\n    && printf \"if [ -f /etc/bash_completion ] && ! shopt -oq posix; then\\n\\\n    . /etc/bash_completion\\nfi\\n\\n\" >> /root/.bashrc\n\n# WARNING: We switch to an empty container briefly!!!\n#\n# Cross-compile dependencies on the host system that take a long time to build\n# when emulating the target architecture. The `xbuild` stage is an image with\n# the same architecture as the host. It compiles the packages for the target\n# architecture. Then we can copy the compiled packages into the `base` image.\n#\n# Note: because we only cross-compile the latest Ubuntu version we support,\n# we just delete the executables on Ubuntu 18.04 and disable doxygen and ccache.\nFROM --platform=$BUILDPLATFORM tonistiigi/xx AS xx\nFROM --platform=$BUILDPLATFORM ubuntu:22.04 AS xbuild\nWORKDIR /root\nCOPY --from=xx / /\nARG TARGETPLATFORM\nARG UBUNTU_VERSION=22.04\nARG PARALLEL_MAKE_ARG=-j4\n\n# Install build dependencies for the host system\nRUN apt-get update -y \\\n    && apt-get install -y cmake clang lld flex bison wget git\n\n# Install build dependencies for the target architecture\n#\n# We need libstd++ to work on the target arch that we are cross-compiling for\nRUN xx-apt-get update -y \\\n    && xx-apt-get install -y --no-install-recommends \\\n    libc6-dev libstdc++-10-dev\n\n# - Ccache\nRUN wget https://github.com/ccache/ccache/releases/download/v4.8.2/ccache-4.8.2.tar.gz -O ccache.tar.gz \\\n    && tar -xzf ccache.tar.gz \\\n    && mv ccache-* ccache \\\n    && cd ccache \\\n    && mkdir build && cd build \\\n    && cmake $(xx-clang --print-cmake-defines) \\\n        -D HAVE_ASM_SSE2=OFF -D HAVE_ASM_SSE41=OFF \\\n        -D HAVE_ASM_AVX2=OFF -D HAVE_ASM_AVX512=OFF \\\n        -D CMAKE_BUILD_TYPE=Release .. \\\n    && make $PARALLEL_MAKE_ARG\n# - Doxygen\nRUN wget https://github.com/doxygen/doxygen/archive/Release_1_14_0.tar.gz -O doxygen.tar.gz \\\n    && tar -xzf doxygen.tar.gz \\\n    && mv doxygen-* doxygen \\\n    && cd doxygen \\\n    && mkdir build && cd build \\\n    && cmake $(xx-clang --print-cmake-defines) \\\n        -D CMAKE_BUILD_TYPE=Release .. \\\n    && make $PARALLEL_MAKE_ARG\n\n# WARNING: We now copy the things from the \"xbuild\" container/image into the\n#          xbuild-test container to make sure the copying works. We do the\n#          actual copy into the spectre container below.\n#\nFROM base AS xbuild-test\nARG UBUNTU_VERSION=22.04\nCOPY --from=xbuild /root/ccache/build/ccache /usr/local/bin\nRUN ccache --version\nCOPY --from=xbuild /root/doxygen/build/bin/doxygen /usr/local/bin\nRUN doxygen --version\nCOPY --from=xbuild /usr/local/bin/cmake /work/\nRUN /work/cmake --version \\\n    && if [ ${UBUNTU_VERSION} = 18.04 ]; then \\\n    rm /usr/local/bin/ccache /usr/local/bin/doxygen; \\\n    else \\\n    mv /work/cmake /usr/local/bin; \\\n    fi\n\n\n# WARNING: We are now back in the spectre container. We create two base\n#          images for the 2 possible target architectures and then select\n#          the one we need as the `dev` container below. This is essentially an\n#          `if` in the Dockerfile.\n#\n# Install software that we can't install through apt. We have to distinguish\n# between different architectures for many of those.\nFROM base AS base-amd64\nENV CHARM_ARCH=x86_64\nENV TEX_ARCH=x86_64\n\nFROM base AS base-arm64\nENV CHARM_ARCH=arm8\nENV TEX_ARCH=aarch64\n\nFROM base-${TARGETARCH} AS dev\nARG TARGETARCH\nARG UBUNTU_VERSION=22.04\n\nARG PARALLEL_MAKE_ARG=-j4\nARG DEBIAN_FRONTEND=noninteractive\n\n# We install dependencies not available through apt manually rather than using\n# Spack since Spack ends up building a lot of dependencies from scratch\n# that we don't need. Thus, not building the deps with Spack reduces total\n# build time of the Docker image.\n# - Blaze\nRUN git clone --depth 1 --branch v3.8.1 https://bitbucket.org/blaze-lib/blaze.git blaze \\\n    && mv blaze/blaze /usr/local/include \\\n    && rm -rf blaze*\n# - Catch2\nRUN wget https://github.com/catchorg/Catch2/archive/refs/tags/v3.14.0.tar.gz -O catch.tar.gz \\\n    && tar -xzf catch.tar.gz && rm catch.tar.gz \\\n    && mv Catch2-* Catch2 \\\n    && cd Catch2 \\\n    && cmake -B build -D BUILD_TESTING=OFF \\\n        -D CMAKE_POSITION_INDEPENDENT_CODE=ON \\\n    && cd build \\\n    && make $PARALLEL_MAKE_ARG install \\\n    && cd ../.. && rm -rf Catch2\n# - Ccache\nCOPY --from=xbuild /root/ccache/build/ccache /usr/local/bin\n# - Doxygen\nCOPY --from=xbuild /root/doxygen/build/bin/doxygen /usr/local/bin\n# - Libbacktrace\nRUN git clone https://github.com/ianlancetaylor/libbacktrace \\\n    && cd libbacktrace \\\n    && ./configure --prefix=/usr/local \\\n    && make $PARALLEL_MAKE_ARG install \\\n    && cd .. && rm -rf libbacktrace\n# - LibXSMM\nRUN if [ \"$TARGETARCH\" = \"arm64\" ] ; then \\\n        git clone --single-branch --branch main --depth 1 \\\n            https://github.com/libxsmm/libxsmm.git libxsmm \\\n        && cd libxsmm \\\n        && make $PARALLEL_MAKE_ARG PREFIX=/usr/local/ PLATFORM=1 install \\\n        && cd .. \\\n        && rm -rf libxsmm; \\\n    else \\\n        wget https://github.com/libxsmm/libxsmm/archive/1.16.1.tar.gz \\\n            -O libxsmm.tar.gz \\\n        && tar -xzf libxsmm.tar.gz \\\n        && mv libxsmm-* libxsmm \\\n        && cd libxsmm \\\n        && make $PARALLEL_MAKE_ARG PREFIX=/usr/local/ install \\\n        && cd .. \\\n        && rm -rf libxsmm libxsmm.tar.gz; \\\n    fi\n# - xsimd https://github.com/xtensor-stack/xsimd\nRUN wget http://github.com/xtensor-stack/xsimd/archive/refs/tags/11.1.0.tar.gz -O xsimd.tar.gz \\\n    && tar -xzf xsimd.tar.gz && rm xsimd.tar.gz \\\n    && cd ./xsimd-*  \\\n    && mkdir build \\\n    && cd ./build \\\n    && cmake -D CMAKE_BUILD_TYPE=Release \\\n        -D CMAKE_INSTALL_PREFIX=/usr/local ../ \\\n    && make install \\\n    && cd ../.. && rm -rf xsimd-*\n\n# Update ld cache to find shared libs in /usr/local/lib/\nRUN ldconfig\n\nWORKDIR /work\n# Download and build the Charm++ version used by SpECTRE\n# We check out only a specific branch in order to reduce the repo size.\n#\n# We remove the `doc` and `example` directories since these aren't useful to us\n# in the container and we want to reduce the size of the container. We do NOT\n# remove the `tmp` directories inside the Charm++ build directories because\n# Charm++ stores non-temporary files (such as headers) that are needed when\n# building with Charm++ in the `tmp` directories.\n#\n# We build  with debug symbols to make debugging Charm++-interoperability\n# easier for people, and build with O2 to reduce build size.\nRUN wget https://raw.githubusercontent.com/sxs-collaboration/spectre/develop/support/Charm/v7.0.0.patch\n\nRUN git clone --single-branch --branch v7.0.1 --depth 1 \\\n        https://github.com/UIUC-PPL/charm charm_7_0_0 \\\n    && cd /work/charm_7_0_0 \\\n    && git checkout v7.0.1 \\\n    && git apply /work/v7.0.0.patch \\\n    && ./build charm++ multicore-linux-${CHARM_ARCH} gcc \\\n        ${PARALLEL_MAKE_ARG} -g -O2 --build-shared --with-production \\\n    && rm -r /work/charm_7_0_0/doc /work/charm_7_0_0/examples\nENV CHARM_ROOT=\"/work/charm_7_0_0/multicore-linux-${CHARM_ARCH}-gcc\"\n\n# Set the environment variable SPECTRE_CONTAINER so we can check if we are\n# inside a container (0 is true in bash)\nENV SPECTRE_CONTAINER 0\n\n# The singularity containers work better if the locale is set properly\nRUN apt-get update -y \\\n    && apt-get install -y locales language-pack-fi language-pack-en \\\n    && export LANGUAGE=en_US.UTF-8 \\\n    && export LANG=en_US.UTF-8 \\\n    && export LC_ALL=en_US.UTF-8 \\\n    && locale-gen en_US.UTF-8 \\\n    && dpkg-reconfigure locales\n\n# Install bibtex for Doxygen bibliography management\n# We first install the TeXLive infrastructure according to the configuration in\n# support/TeXLive/texlive.profile and then use it to install the bibtex package.\n#\n# WARNING: if you are rebuilding the container in late March/early April you\n# might have version mismatches between the TeXLive installer respositories like\n# CTAN. You can then use the previous year's TeXLive until CTAN is updated. E.g.\n#    wget \\\n#  ftp://tug.org/historic/systems/texlive/2025/tlnet-final/install-tl-unx.tar.gz\n# and\n# install-tl-*/install-tl -profile=texlive.profile \\\n#        -repository ftp://tug.org/historic/systems/texlive/2025/tlnet-final\nRUN if [ ${UBUNTU_VERSION} = 22.04 ]; then \\\n    mkdir /work/texlive && cd /work/texlive \\\n    && wget http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz \\\n    && tar -xzf install-tl-unx.tar.gz \\\n    && rm install-tl-unx.tar.gz \\\n    && wget https://raw.githubusercontent.com/sxs-collaboration/spectre/develop/support/TeXLive/texlive.profile \\\n    && install-tl-*/install-tl -profile=texlive.profile \\\n    && rm -r install-tl-* texlive.profile install-tl.log \\\n    && /work/texlive/bin/${TEX_ARCH}-linux/tlmgr install bibtex \\\n    ; fi\nENV PATH=\"${PATH}:/work/texlive/bin/${TEX_ARCH}-linux\"\n\n# Remove the apt-get cache in order to reduce image size\nRUN apt-get -y clean\n\nWORKDIR /work\n\n\n# Deploy compiled executables to an image that can be run on HPC systems.\n# - We could also compile in the dev container and then copy the executables to\n#   a minimal deploy container to reduce its size. That requires keeping track\n#   of the shared libraries that are needed to run the executables, so to make\n#   things easier we just base the deploy container on the dev container.\n# - We inherit from the remote image rather than the local dev because it is\n#   faster on release CI to just pull the remote dev image rather than having to\n#   build the dev container again. We could pre-fetch the dev image and then\n#   build from the local cache, but that didn't work right away.\nFROM sxscollaboration/spectre:dev AS deploy\nARG BUILDARCH\nARG TARGETARCH\nARG UBUNTU_VERSION=22.04\nARG PARALLEL_MAKE_ARG=-j2\n\nCOPY . spectre/\n\nRUN mkdir spectre/build && cd spectre/build \\\n    && cmake \\\n    -D CMAKE_C_COMPILER=clang \\\n    -D CMAKE_CXX_COMPILER=clang++ \\\n    -D CMAKE_Fortran_COMPILER=gfortran \\\n    -D CMAKE_BUILD_TYPE=Release \\\n    -D DEBUG_SYMBOLS=OFF \\\n    -D BUILD_PYTHON_BINDINGS=ON \\\n    -D MEMORY_ALLOCATOR=SYSTEM \\\n    ..\n\n# Skip compiling the executables if we are emulating the target architecture\n# because that takes a long time. They can be compiled on-demand.\nRUN if [ \"$TARGETARCH\" = \"$BUILDARCH\" ] ; then \\\n        cd spectre/build \\\n        && make ${PARALLEL_MAKE_ARG} cli \\\n        && make ${PARALLEL_MAKE_ARG} CharacteristicExtract \\\n        && make ${PARALLEL_MAKE_ARG} PreprocessCceWorldtube \\\n        && make ${PARALLEL_MAKE_ARG} WriteCceWorldtubeCoordsToFile \\\n        && make ${PARALLEL_MAKE_ARG} SolveXcts \\\n    ; fi\n\nENV SPECTRE_HOME /work/spectre\nENV PATH $SPECTRE_HOME/build/bin:$PATH\nENV PYTHONPATH $SPECTRE_HOME/build/bin/python:$PYTHONPATH\n\n# Set the CLI as entrypoint\nENTRYPOINT [\"spectre\"]\nCMD [\"--help\"]\n\n# Build a demo image with extra software used in the tutorials.\nFROM deploy AS demo\nARG BUILDARCH\nARG TARGETARCH\nARG UBUNTU_VERSION=22.04\nARG PARALLEL_MAKE_ARG=-j4\n\n# vim and emacs for editing files\n# Also ffmpeg for making movies with paraview output pngs\n# paraview needs curl\nRUN apt-get update -y \\\n    && apt-get install -y vim emacs-nox ffmpeg curl\n\n# Install headless paraview so we can run pvserver in the container\n# Note: there is no arm64 linux binary of paraview available, so don't\n# install paraview when building for Apple Silicon. Apple Silicon users\n# should install a binary of ParaView for Mac and move data to be\n# visualized outside of the container.\nRUN if [ \"$TARGETARCH\" != \"arm64\" ] ; then \\\n        wget -O paraview.tar.gz --no-check-certificate \"https://www.paraview.org/paraview-downloads/download.php?submit=Download&version=v5.10&type=binary&os=Linux&downloadFile=ParaView-5.10.1-osmesa-MPI-Linux-Python3.9-x86_64.tar.gz\" \\\n        && tar -xzf paraview.tar.gz \\\n        && rm paraview.tar.gz \\\n        && mv ParaView-* /opt/paraview; \\\n    fi\n\nENV PATH \"/opt/paraview/bin:$PATH\"\n\n# Build the executables used in the tutorial\nRUN if [ \"$TARGETARCH\" = \"$BUILDARCH\" ] ; then \\\n        cd spectre/build \\\n        && make ${PARALLEL_MAKE_ARG} ExportCoordinates3D \\\n        && make ${PARALLEL_MAKE_ARG} EvolveScalarAdvection2D \\\n    ; fi\n\nRUN pip3 --no-cache-dir install jupyterlab\n\n# Used for compiling the static executables and libraries during CI so that we\n# can release statically linked versions of our executables and libraries\n# for other groups to use.\nFROM sxscollaboration/spectre:dev1804 AS deploy_static_execs_and_libs\nARG BUILDARCH\nARG TARGETARCH\nARG UBUNTU_VERSION=18.04\nARG PARALLEL_MAKE_ARG=-j4\n\nWORKDIR /work\n\nCOPY . spectre/\n\n# Build SpECTRE with as many static libraries as possible. Because we cannot\n# statically link to glibc (it is unsupported and causes a lot of undefined\n# behavior) we build using Ubuntu 18.04 to support a fairly old version of\n# glibc.\nRUN if [ ${UBUNTU_VERSION} != 18.04 ] && [ \"$TARGETARCH\" != \"amd64\" ]; then \\\n    echo \"Must use Ubuntu 18.04 with AMD64 for deploying static execs\" \\\n    && exit 1; fi \\\n    && mkdir spectre/build && cd spectre/build \\\n    && cmake \\\n    -D CMAKE_C_COMPILER=gcc-10 \\\n    -D CMAKE_CXX_COMPILER=g++-10 \\\n    -D CMAKE_Fortran_COMPILER=gfortran-10 \\\n    -D CMAKE_BUILD_TYPE=Release \\\n    -D DEBUG_SYMBOLS=OFF \\\n    -D BUILD_PYTHON_BINDINGS=OFF \\\n    -D MEMORY_ALLOCATOR=SYSTEM \\\n    -D BUILD_SHARED_LIBS=OFF \\\n    -D ENABLE_PARAVIEW=OFF \\\n    -D Boost_USE_STATIC_LIBS=ON \\\n    -D BLA_STATIC=ON \\\n    -D GSL_STATIC=ON \\\n    -D HDF5_USE_STATIC_LIBRARIES=ON \\\n    -D SPECTRE_Fortran_STATIC_LIBS=ON \\\n    -D YAMLCPP_STATIC_LIBRARY=ON \\\n    -D USE_GIT_HOOKS=OFF \\\n    -D OVERRIDE_ARCH=haswell \\\n    -D USE_LD=gold \\\n    -D gfortran=/usr/lib/gcc/x86_64-linux-gnu/9/libgfortran.a \\\n    -D quadmath=/usr/lib/gcc/x86_64-linux-gnu/9/libquadmath.a \\\n    -D BUILD_DOCS=OFF \\\n    -D USE_CCACHE=OFF \\\n    .. \\\n    && make ${PARALLEL_MAKE_ARG} CharacteristicExtract PreprocessCceWorldtube \\\n    WriteCceWorldtubeCoordsToFile \\\n    && ctest -LE unit -R CharacteristicExtract -E KleinGordon\n"
  },
  {
    "path": "docs/.nojekyll",
    "content": ""
  },
  {
    "path": "docs/CODE_OF_CONDUCT.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# SpECTRE Code of Conduct {#code_of_conduct}\n\nWith the goal of supporting our community in doing the best science we\ncan, we expect that all contributors and maintainers of SpECTRE:\n\n- Behave professionally in a way that is welcoming and respectful to\nall participants.\n\n- Behave in a way that is free from any form of discrimination,\nharassment, or retaliation.\n\n- Treat each other with collegiality and respect and help to create a\nsupportive working environment.\n\nThis code of conduct is not an exhaustive list of things that you\nshould or should not do. Rather, take it in the spirit in which it’s\nintended — a guide to make it easier to communicate and participate in\nthe community.\n\nThis code of conduct applies to all spaces managed by the SpECTRE\nproject. This includes Slack channels, wikis, mailing lists, issue\ntrackers, pull request comments, SpECTRE project events, and any other\nforums created by the project for communication. It applies to all of\nyour communication and conduct in these spaces, including emails,\nchats, things you say, slides, videos, posters, signs, or even\nt-shirts you display in these spaces. In addition, violations of this\ncode outside these spaces may, in rare cases, affect a person’s\nability to participate within them, when the conduct amounts to an\negregious violation of this code.\n\nIf you believe someone is violating the code of conduct, we ask that\nyou report it by emailing conduct@spectre-code.org. For more details\nplease see our \\ref reporting_guide \"Reporting Guide\".  If you would\nrather not formally report your concern, you should feel free to\ndiscuss it informally and confidentially with one of the project\nleaders.\n\nMore specifically, we expect members of the SpECTRE community to:\n\n- Be friendly and patient. During teleconferences and meetings,\nparticipants who wish to speak should feel free to type “hand up” or\nsimilar in the comment box, as needed, to get the chairs’\nattention. Meeting/teleconference chairs are encouraged to make space\nfor those unfamiliar with the topic of discussion to ask questions and\nengage.\n\n- Be welcoming. We strive to be a community that welcomes and supports\npeople of all backgrounds and identities. This includes, but is not\nlimited to members of any race, ethnicity, culture, national origin,\ncolour, immigration status, social and economic class, educational\nlevel, sex, sexual orientation, gender identity and expression, age,\nsize, family status, political belief, religion or lack thereof, and\nmental and physical ability.\n\n- Be considerate. Your work will be used by other people, and you in\nturn will depend on the work of others. Any decision you take will\naffect users and colleagues, and you should take those consequences\ninto account. Remember that we are a world-wide community, so you\nmight not be communicating in someone else’s primary language.\n\n- Be respectful. Not all of us will agree all the time, but\ndisagreement is no excuse for poor behavior and poor manners. We might\nall experience some frustration now and then, but we cannot allow that\nfrustration to turn into a personal attack. It is important to\nremember that a community where people feel uncomfortable or\nthreatened is not a productive one. Members of the SpECTRE community\nshould be respectful when dealing with other members as well as with\npeople outside the community.\n\n- Be careful in the words that you choose and be kind to others. Do\nnot insult or put down other participants. Harassment and other\nexclusionary behavior aren’t acceptable. This includes, but is not\nlimited to:\n  - Violent threats or language directed against another person.\n  - Discriminatory jokes and language.\n  - Posting sexually explicit or violent material.\n  - Posting (or threatening to post) other people’s personally\n  identifying information (“doxing”).\n  - Personal insults, especially those using racist or sexist terms.\n  - Unwelcome sexual attention.\n  - Advocating for, or encouraging, any of the above behavior.  In\ngeneral, if someone asks you to stop, then stop. Persisting in such\nbehavior after being asked to stop is considered harassment.\n\n- When we disagree, try to understand why. Disagreements, both social\nand technical, happen all the time, and SpECTRE is no exception. It is\nimportant that we resolve disagreements and differing views\nconstructively. Remember that we are different. The strength of\ncommunities comes from having a varied community, people from a wide\nrange of backgrounds. Different people have different perspectives on\nissues. Being unable to understand why someone holds a viewpoint does\nnot mean that they are wrong. Do not forget that it is human to err\nand blaming each other does not get us anywhere. Instead, focus on\nhelping to resolve issues and learning from mistakes.\n\n### Attribution\n\nThis Code of Conduct is adapted from the [SXS Collaboration Code of\nConduct](https://www.black-holes.org/sxs_code_of_conduct.pdf), which\nis based on a draft of the [LLVM Code of\nConduct](https://llvm.org/docs/CodeOfConduct.html) which is adapted\nfrom [Django Project Code of\nConduct](https://www.djangoproject.com/conduct/) which used text from\nthe [Speak Up! Code of\nConduct](http://web.archive.org/web/20141109123859/http://speakup.io/coc.html).\n"
  },
  {
    "path": "docs/CONTRIBUTING.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n\n# Contributing to SpECTRE {#contributing_to_spectre}\n\n\\tableofcontents\n\n# Contributing to SpECTRE {#contributing_to_spectre_2}\n\nThe following is a set of guidelines for contributing to SpECTRE,\nwhich is hosted in the [Simulating eXtreme Spacetimes\nOrganization](https://github.com/sxs-collaboration) on GitHub.\n\n## Code of Conduct\n\nThis project and everyone participating in it is governed by the \\ref\ncode_of_conduct \"SpECTRE Code of Conduct\". By participating, you are\nexpected to uphold this code. Please report possible violations of the\ncode of conduct to conduct@spectre-code.org.\n\n## What should I know before I get started? {#getting-started}\n\nSpECTRE is being developed in support of our collaborative Simulating\neXtreme Spacetimes (SXS) research program into the multi-messenger\nastrophysics of neutron star mergers, core-collapse supernovae, and\ngamma-ray bursts.  As such, almost all of the current contributors to\nSpECTRE are members of SXS institutions, and a large amount of\ndiscussion about SpECTRE is done in internal SXS meetings.  If you are\na member of SXS and wish to get involved, please contact one of the\nproject leaders.\n\nIn the future, we hope that SpECTRE can be applied to problems across\ndiscipline boundaries, and that it can be a true community code.  At\nthe present time, however, SpECTRE cannot yet solve realistic\nproblems, and broad overview documentation is in an early, incomplete\nstage.  Therefore, if you are not a member of SXS, but are interested\nin contributing to SpECTRE, we strongly encourage you to contact us at\nquestions@spectre-code.org to discuss possible contributions.\n\n## How Can I Contribute? {#how-can-i-contribute}\n\n### Reporting Bugs {#reporting-bugs}\n\nThis section guides you through submitting a bug report for\nSpECTRE. Following these guidelines helps maintainers and the\ncommunity understand your report, reproduce the behavior, and find\nrelated reports.\n\nBefore creating bug reports, please **perform a\n[search](https://github.com/sxs-collaboration/spectre/issues)** to see\nif the problem has already been reported. If it has **and the issue is\nstill open**, please add a comment to the existing issue instead of\nopening a new one.\n\n> **Note:** If you find a **Closed** issue that seems like it is the\n> same thing that you're experiencing, please open a new issue and\n> include a link to the original issue in the body of your new one.\n\n#### How Do I Submit A (Good) Bug Report? {#submit-bug-report}\n\nBugs are tracked as [GitHub\nissues](https://guides.github.com/features/issues/). When you are\ncreating a bug report, please **include as many details as\npossible**. Please fill out the template completely.  The provided\ninformation helps us resolve issues faster.\n\nExplain the problem and include additional details to help maintainers\nreproduce the problem:\n\n* **Use a clear and descriptive title** for the issue to identify the\n  problem.\n* **Describe the exact steps which reproduce the problem** in as much\n  detail as possible. For example, start by explaining how you started\n  SpECTRE, e.g. the exact command you used in the terminal, or the\n  contents of the batch job script you used.\n* **Describe the behavior you observed after following the steps** and\n  point out what exactly is the problem with that behavior.\n* **Explain which behavior you expected to see instead and why.**\n\nProvide more context by answering these questions:\n\n* **Can you reproduce the problem in both debug and release mode?**\n(this is controlled by the CMake flag `CMAKE_BUILD_TYPE`)\n* **Did the problem start happening recently** (e.g. after updating to\n  a new version/commit of SpECTRE) or was this always a problem?\n* If the problem started happening recently, **can you reproduce the\n  problem in an older version/commit?** What's the most recent\n  version/commit in which the problem doesn't happen?\n* **Can you reliably reproduce the issue?** If not, provide details\n  about how often the problem happens and under which conditions it\n  normally happens.\n* **Can you reproduce the problem on another machine?**\n* **Can you reproduce the problem in the docker container?** (see the\n  \\ref installation \"Installation notes\")\n\nInclude details about your configuration and environment:\n\n* **Add as an attachment** (or add the contents of) the following:\n  - The text output by SpECTRE (including any stack trace)\n  - The input file(s)\n  - $SPECTRE_BUILD_DIR/BuildInfo.txt\n* **What is the name and version of the OS you're using**?\n* If possible (for SXS computers or HPC systems), a **path to a run\n  directory** that is accessible by SpECTRE core developers.\n\n### Suggesting Enhancements {#suggesting-enhancements}\n\nThis section guides you through submitting an enhancement suggestion\nfor SpECTRE, including completely new features and minor improvements\nto existing functionality. Following these guidelines helps\nmaintainers and the community understand your suggestion and find\nrelated suggestions.\n\nBefore creating enhancement suggestions, please **perform a\n[search](https://github.com/sxs-collaboration/spectre/issues)** to see\nif the enhancement has already been suggested. If it has, add a\ncomment to the existing issue instead of opening a new one.\n\n#### How Do I Submit A (Good) Enhancement Suggestion? {#submit-enhancement}\n\nEnhancement suggestions are tracked as [GitHub\nissues](https://guides.github.com/features/issues/).  When you are\ncreating an enhancement suggestion, please **include as many details\nas possible** as you fill in the template.\n\n* **Use a clear and descriptive title** for the issue to identify the\n  suggestion.\n* **Provide a step-by-step description of the suggested enhancement**\n  in as many details as possible.\n* **Explain why this enhancement would be useful** to most SpECTRE users.\n\n### Your First Code Contribution {#first-code-contribution}\n\nUnsure where to begin contributing to SpECTRE? You can start by\nlooking through these `good first issue` and `help wanted` issues:\n\n* [Good first issues][good-first-issue] - issues which should only\n  require a few lines of code, and a test or two.\n* [Help wanted issues][help-wanted] - issues which should be a bit\n  more involved than `good first issue`s.\n\n#### Local development {#local-development}\n\nSpECTRE can be developed locally. For instructions on how to do this,\nsee the following sections in the [SpECTRE\ndocumentation](https://spectre-code.org/):\n\n* [Installing SpECTRE](installation.html)\n* [Running Status Checks\n  Locally](github_actions_guide.html#perform-checks-locally)\n\n## Pull Requests {#pull-requests}\n\nCode contributions to SpECTRE follow a [pull request\nmodel](https://help.github.com/articles/about-pull-requests/)\n\nThe process described here has several goals:\n\n- Maintain SpECTRE's code and documentation quality\n- Reach science goals in a timely manner\n- Fix problems that are important to users\n- Engage the community in working toward the best possible code\n- Enable a sustainable system for SpECTRE's maintainers to review\n  contributions\n\nPlease follow these steps to have your contribution considered by the\nmaintainers:\n\n1. Follow the \\ref code_review_guide \"code review guidelines\", the\n  \\ref writing_unit_tests \"guide to writing unit tests\", and the \\ref\n  writing_good_dox \"guide to writing documentation\"\n2. Follow all instructions in the pull request template. Reference\n   related issues and pull requests.\n3. After you submit your pull request, verify that all [status\n   checks](https://help.github.com/articles/about-status-checks/) are\n   passing\n\n   > If a status check is failing, and you believe that the failure is\n   > unrelated to your change, please leave a comment on the pull\n   > request explaining why you believe the failure is unrelated. A\n   > maintainer will re-run the status check for you. If we conclude\n   > that the failure was a false positive, then we will open an issue\n   > to track that problem with our status check suite.\n\n   Only those status check failures that occur in the containerized build\n   environment are your responsibility to fix. If you encounter an issue with a\n   status check that runs in an environment that you do not have access to, e.g.\n   on macOS or on a supercomputer, please notify\n   `@sxs-collaboration/spectre-core-devs`. They will refer the issue to a person\n   who has access to that environment. Unless requested by the reviewers, the PR\n   will not be held up until the issue is resolved.\n\nWhile the prerequisites above must be satisfied prior to having your\npull request reviewed, the reviewers may ask you to complete\nadditional design work, tests, or other changes before your pull\nrequest can be ultimately accepted.\n\n## How SpECTRE pull request reviews are conducted {#pull-request-reviews}\n\n> Note that these are guidelines and not rigid rules.\n\nPlease make your pull requests as small as reasonably possible, as\nsmaller pull requests are easier to review. In general, longer pull\nrequests take longer to review, with the time scaling exponentially\nwith the number of lines changed.  Therefore if your pull request is\ntoo large, we may ask you to break it up into several smaller pull\nrequests.\n\nIf you would like feedback on a pull request prior to it being ready\nfor formal review, please open it\n[in draft mode](https://github.blog/2019-02-14-introducing-draft-pull-requests/)\nand [request reviews][request-reviews] from whomever you wish to get feedback\nfrom. As long as the PR is in draft mode it will not be reviewed, aside from the\nfeedback requested.\n\n> Below, days mean business days, so if the time period includes the\n> weekend, add two days, and for major holidays add a day.\n> Furthermore, most of us are academics, and we occasionally go to\n> conferences which may lead to delays in the review process.  Also do\n> not expect much to happen between December 20th and January 3rd.\n\nMost pull requests submitted to SpECTRE will be reviewed in the\nfollowing manner:\n\n- Within two days, one of the [SpECTRE core developers](#core-developers) will\n  either review the PR or assign reviewers. If this has not happened after two\n  days, please [request a review][request-reviews]\n  and select the `@sxs-collaboration/spectre-core-devs` team. Also feel free to\n  [ping the core developers][github-ping] (e.g\n  `@sxs-collaboration/spectre-core-devs please assign reviewers`) if they fail\n  to respond in a timely manner.\n- Assigned reviewers should either confirm that they are able to review (by\n  reviewing or providing a reasonable timeframe for their review) or decline\n  within two days so another reviewer can be assigned.\n- Anyone is welcome to self-assign themselves as a reviewer.\n- Assigned reviewers should submit their review in as timely a manner\n  as possible.\n- Anyone can request changes within either the first two days of the\n  pull request, or within a day after the initial reviews of the\n  assigned reviewers.  After this period, only the assigned reviewers\n  can request changes, unless someone believes the code is wrong.\n  Non-reviewers are allowed to make comments, which the pull request\n  author is encouraged to address.  Alternatively the author can\n  create an issue with the suggested changes, assigned to themselves,\n  which would then be addressed in a subsequent pull request by the\n  author.\n- If any requested change is unclear to the author, they should ping\n  the reviewer and ask for clarification.  Authors and reviewers are\n  encouraged to talk to one another (in person, via Google hangout, or\n  some other verbal method if possible) to resolve any issues.\n- Reviewers are encouraged to [ping others \\@GITHUB_USERNAME][github-ping] to\n  get opinions on code they are unsure about.\n- It is permissible to have a group code review led by one of the\n  reviewers. The reviewer should comment on who was present at the\n  group review.\n- If necessary, pull requests can also be discussed at one of the\n  weekly SpECTRE meetings.\n- If changes are requested, the author should fix all of them in one\n  or more fixup commits (where the first line of the commit message\n  should begin with `fixup`) and push them to the PR branch.\n  Fixup commits make reviews significantly faster because the reviewers don't\n  have to review the full PR again, but only the parts that changed.\n- After pushing fixup commits, the author should\n  [re-request reviews from the reviewers][request-reviews].\n  They can also ping the reviewers that the pull request is updated.\n- Once a pull request is updated, the reviewers should either request further\n  changes or approve the PR.\n- Once all reviewers have approved the PR or given the okay to squash, the\n  author should [rebase on develop and then squash their\n  commits](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History)\n  into one or more self-contained commits (such that the code will\n  compile and pass all tests after each commit).  The squashed commits\n  will need to be force pushed.\n- Once all reviewers have approved the pull request, someone should\n  ping the `@sxs-collaboration/spectre-core-devs`.  If one of the\n  original reviewers is a core developer, this is not necessary and\n  the core developer can merge the pull request.\n- One of the core developers will perform a final cursory review,\n  requesting changes only for major problems, and commenting on other\n  possible changes.\n- The pull request author should address all final requested changes\n  and may either also fix final suggested changes, or create an issue\n  with the suggested changes, which will be addressed in a subsequent\n  pull request by the author.\n- The SpECTRE core developer will merge the pull request once all comments have\n  been addressed, all reviewers have approved the PR, the code passes CI and all\n  pull requests the pull request depends on have been merged. When approvals are\n  dismissed by minor changes, such as rebasing, squashing fixup commits or\n  adding a missing include, the core developer may merge the PR without waiting\n  for all reviewers to re-approve the PR.\n\nIn addition to the guidelines above, we apply the following exceptions based on\nthe type of change:\n\n- Critical bug fixes (i.e. the code is broken) can be merged after two expedited\n  reviews by SpECTRE core developers. If necessary, an issue can be created if\n  further changes are desired.\n- PRs that add documentation don't need to be perfect, since having some\n  docs is better than having none. Reviewers should approve after one or at most\n  two rounds of review and allow further minor changes to be done in follow-ups.\n- \"Small\" or \"trivial\" PRs can be merged immediately by core developers.\n  Examples for such PRs are:\n\n  - Fixing typos in documentation or adding (small amounts of) documentation\n  - Fixing missing includes or missing linked libraries\n  - Adding Python bindings\n  - Formatting code\n  - Refactoring, renaming or moving files with no change in functionality\n    (unless potentially controversial)\n\n  It is the reviewing core developer's responsibility to decide if the PR is\n  \"small\" or \"trivial\" enough to merge immediately. If they are unsure, they\n  should fall back to the usual procedure of giving people two days to assign\n  themselves as reviewers and/or reach out to whoever they think might want to\n  review the PR.\n- Pull requests that are designated `new design` are expected to have a longer\n  review period, including discussions during weekly SpECTRE meetings. SpECTRE\n  core developers will provide reasonable review deadlines once the new design\n  is finalized.\n\n### Git Commit Message Guidelines {#git-commit-messages}\n\n* Use the present tense (\"Add feature\" not \"Added feature\")\n* Use the imperative mood (\"Move cursor to...\" not \"Moves cursor to...\")\n* Limit the first line to 72 characters or less\n* If needed, a blank second line followed by a more complete description\n\n### SpECTRE core developers {#core-developers}\n\nSpECTRE core developers are people who are very familiar with the\nentire code, comfortable with modern C++, and willing to take the\nresponsibility of overseeing the code as a whole.\n[Current SpECTRE core\ndevelopers](https://github.com/orgs/sxs-collaboration/teams/spectre-core-devs)\ncan be pinged on GitHub at `@sxs-collaboration/spectre-core-devs`.  It\nis expected that as more contributors become familiar with SpECTRE,\nadditional people will be added to the list of core developers.\n\n\n[good-first-issue]:https://github.com/sxs-collaboration/spectre/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+sort%3Acomments-desc\n[help-wanted]:https://github.com/sxs-collaboration/spectre/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+sort%3Acomments-desc\n[github-ping]:https://github.blog/2011-03-23-mention-somebody-they-re-notified/\n[request-reviews]:https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review\n"
  },
  {
    "path": "docs/Changelog.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Changelog {#changelog}\n\nThe complete changelog for released versions of the code is available on GitHub:\n\n- [Changelog for releases on GitHub](https://github.com/sxs-collaboration/spectre/releases)\n\nChanges since the last release are printed below.\n"
  },
  {
    "path": "docs/Contributing/Contributing.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Contributing to SpECTRE {#contributing_index}\n\n- \\subpage contributing_to_spectre\n- \\subpage code_of_conduct\n- \\subpage reporting_guide\n"
  },
  {
    "path": "docs/Contributing/ReportingGuide.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# SpECTRE Code of Conduct - Reporting Guide {#reporting_guide}\n\n\\tableofcontents\n\nIf you believe someone is violating the code of conduct, you can\nalways file a report by emailing conduct@spectre-code.org, or discuss\nit informally and confidentially with one of the project leaders.\n**All reports will be kept confidential.**\n\nIf you believe anyone is in physical danger, please notify appropriate\nlaw enforcement first. If you are unsure what law enforcement agency\nis appropriate, please include this in your report and we will attempt\nto notify them.\n\nReports of violations of the code of conduct can be as formal or\ninformal as needed for the situation at hand. If possible, please\ninclude as much information as you can. If you feel comfortable,\nplease consider including:\n- Your contact info (so we can get in touch with you if we need to\n  follow up).\n- Names (real, nicknames, or pseudonyms) of any individuals\ninvolved. If there were other witnesses besides you, please try to\ninclude them as well.\n- When and where the incident occurred. Please be as specific as\n  possible.\n- Your account of what occurred. If there is a publicly available\nrecord (e.g. a mailing list archive or Slack logs) please include a\nlink.\n- Any extra context you believe existed for the incident.\n- If you believe this incident is ongoing.\n- Any other information you believe we should have.\n\n### What happens after you file a report?\n\nYou will receive an email from the Code of Conduct Committee or\nproject leader(s) you contacted, acknowledging receipt within 24 hours\n(and we will aim to respond much quicker than that). We will review\nthe incident and try to determine:\n- What happened and who was involved.\n- Whether this event constitutes a code of conduct violation.\n- Whether this is an ongoing situation, or if there is a threat to\n  anyone’s physical safety.\n\nOnce the Code of Conduct Committee or contacted project leader(s) have\na complete account of the events we will make a decision on how to\nrespond, possibly in consultation with the full Executive Committee\n(excluding anyone involved in the incident). Responses may include:\n- Nothing, if we determine no violation occurred or it has already\n  been appropriately resolved.\n- Providing either moderation or mediation to ongoing interactions\n  (where appropriate, safe, and desired by both parties).\n- A private reprimand from the working group to the individuals\n  involved.\n- An imposed vacation (i.e. asking someone to “take a week off” from a\n  mailing list or Slack).\n- Escalation to the appropriate institutions.\n- Involvement of relevant law enforcement if appropriate.\n\nIf the situation is not resolved within one week, we’ll respond within\none week to the original reporter with an update and explanation.\nOnce we’ve determined our response, we will separately contact the\noriginal reporter and other individuals to let them know what actions\n(if any) we’ll be taking. We will take into account feedback from the\nindividuals involved on the appropriateness of our response, but we\ndon’t guarantee we’ll act on it.\n\n### Code of Conduct Committee\n\nThe current members of the Code of Conduct Committee (and the only\npeople that see emails sent to conduct@spectre-code.org) are:\n- Saul Teukolsky\n- Harald Pfeiffer\n\nSpECTRE is being developed in support of our collaborative Simulating\neXtreme Spacetimes (SXS) research program into the multi-messenger\nastrophysics of neutron star mergers, core-collapse supernovae, and\ngamma-ray bursts.  As such, it falls under the managerial oversight of\nthe SXS Executive Committee whose current members are:\n- Matthew Duez\n- Francois Foucart\n- Lawrence Kidder\n- Geoffrey Lovelace\n- Harald Pfeiffer\n- Mark Scheel\n- Leo Stein\n- Saul Teukolsky\n- Aaron Zimmerman\n\n"
  },
  {
    "path": "docs/Dependencies.bib",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# When editing this file, please follow the guidelines in\n# https://spectre-code.org/writing_good_dox.html#writing_dox_citations.\n\n@INPROCEEDINGS{Blaze1,\n  author =       {Iglberger, Klaus and Hager, Georg and Treibig, Jan and Rüde,\n                  Ulrich},\n  booktitle =    {2012 International Conference on High Performance Computing \\&\n                  Simulation (HPCS)},\n  title =        {High performance smart expression template math libraries},\n  year =         {2012},\n  pages =        {367-373},\n  doi =          {10.1109/HPCSim.2012.6266939}\n}\n\n@article{Blaze2,\n  author =       {Iglberger, Klaus and Hager, Georg and Treibig, Jan and\n                  R\\\"{u}de, Ulrich},\n  title =        {Expression Templates Revisited: A Performance Analysis of\n                  Current Methodologies},\n  journal =      {SIAM Journal on Scientific Computing},\n  volume =       {34},\n  number =       {2},\n  pages =        {C42-C69},\n  year =         {2012},\n  doi =          {10.1137/110830125},\n  URL =          {https://doi.org/10.1137/110830125}\n}\n\n@software{Charmpp1,\n  author = {Kale, Laxmikant and Acun, Bilge and Bak, Seonmyeong and Becker,\n            Aaron and Bhandarkar, Milind and Bhat, Nitin and Bhatele, Abhinav\n            and Bohm, Eric and Bordage, Cyril and Brunner, Robert and Buch,\n            Ronak and Chakravorty, Sayantan and Chandrasekar, Kavitha and Choi,\n            Jaemin and Denardo, Michael and DeSouza, Jayant and Diener,\n            Matthias and Dokania, Harshit and Dooley, Isaac and Fenton, Wayne\n            and Galvez, Juan and Gioachin, Fillipo and Gupta, Abhishek and\n            Gupta, Gagan and Gupta, Manish and Gursoy, Attila and Harsh, Vipul\n            and Hu, Fang and Huang, Chao and Jagathesan, Narain and Jain,\n            Nikhil and Jetley, Pritish and Jindal, Prateek and Kanakagiri,\n            Raghavendra and Koenig, Greg and Krishnan, Sanjeev and Kumar,\n            Sameer and Kunzman, David and Lang, Michael and Langer, Akhil and\n            Lawlor, Orion and Wai Lee, Chee and Lifflander, Jonathan and Mahesh,\n            Karthik and Mendes, Celso and Menon, Harshitha and Mei, Chao and\n            Meneses, Esteban and Mikida, Eric and Miller, Phil and Mokos, Ryan\n            and Narayanan, Venkatasubrahmanian and Ni, Xiang and Nomura, Kevin\n            and Paranjpye, Sameer and Ramachandran, Parthasarathy and Ramkumar,\n            Balkrishna and Ramos, Evan and Robson, Michael and Saboo, Neelam and\n            Saletore, Vikram and Sarood, Osman and Senthil, Karthik and Shah,\n            Nimish and Shu, Wennie and B. Sinha, Amitabh and Sun, Yanhua and\n            Sura, Zehra and Totoni, Ehsan and Varadarajan, Krishnan and\n            Venkataraman, Ramprasad and Wang, Jackie and Wesolowski, Lukasz and\n            White, Sam and Wilmarth, Terry and Wright, Jeff and Yelon, Joshua\n            and Zheng, Gengbin},\n  doi    = {10.5281/zenodo.3370873},\n  month  = {Aug},\n  title  = {{The Charm++ Parallel Programming System}},\n  url    = {https://charm.cs.illinois.edu},\n  year   = {2019}\n}\n\n@software{Charmpp2,\n  author =       {Laxmikant Kale and Bilge Acun and Seonmyeong Bak and Aaron\n                  Becker and Milind Bhandarkar and Nitin Bhat and Abhinav\n                  Bhatele and Eric Bohm and Cyril Bordage and Robert Brunner and\n                  Ronak Buch and Sayantan Chakravorty and Kavitha Chandrasekar\n                  and Jaemin Choi and Michael Denardo and Jayant DeSouza and\n                  Matthias Diener and Harshit Dokania and Isaac Dooley and Wayne\n                  Fenton and Zane Fink and Juan Galvez and Pathikrit Ghosh and\n                  Fillipo Gioachin and Abhishek Gupta and Gagan Gupta and Manish\n                  Gupta and Attila Gursoy and Vipul Harsh and Fang Hu and Chao\n                  Huang and Narain Jagathesan and Nikhil Jain and Pritish Jetley\n                  and Prateek Jindal and Raghavendra Kanakagiri and Greg Koenig\n                  and Sanjeev Krishnan and Sameer Kumar and David Kunzman and\n                  Michael Lang and Akhil Langer and Orion Lawlor and Chee Wai\n                  Lee and Jonathan Lifflander and Karthik Mahesh and Celso\n                  Mendes and Harshitha Menon and Chao Mei and Esteban Meneses\n                  and Eric Mikida and Phil Miller and Ryan Mokos and\n                  Venkatasubrahmanian Narayanan and Xiang Ni and Kevin Nomura\n                  and Sameer Paranjpye and Parthasarathy Ramachandran and\n                  Balkrishna Ramkumar and Evan Ramos and Michael Robson and\n                  Neelam Saboo and Vikram Saletore and Osman Sarood and Karthik\n                  Senthil and Nimish Shah and Wennie Shu and Amitabh B. Sinha\n                  and Yanhua Sun and Zehra Sura and Justin Szaday and Ehsan\n                  Totoni and Krishnan Varadarajan and Ramprasad Venkataraman and\n                  Jackie Wang and Lukasz Wesolowski and Sam White and Terry\n                  Wilmarth and Jeff Wright and Joshua Yelon and Gengbin Zheng},\n  title =        {{UIUC-PPL}/charm: {Charm++} version 7.0.0},\n  month =        oct,\n  year =         2021,\n  publisher =    {Zenodo},\n  version =      {v7.0.0},\n  doi =          {10.5281/zenodo.5597907},\n  url =          {https://doi.org/10.5281/zenodo.5597907}\n}\n\n@incollection{Charmpp3,\n  title =        {Charm++: Parallel programming with message-driven objects},\n  author =       {Kale, Laxmikant V and Krishnan, Sanjeev},\n  booktitle =    {Parallel programming using C++},\n  editor =       {Gregory V. Wilson and Paul Lu},\n  publisher =    {The MIT Press},\n  pages =        {175--213},\n  year =         1996,\n}\n\n@book{Gsl,\n  title =        {{GNU} Scientific Library Reference Manual},\n  author =       {Galassi, M. and others},\n  publisher =    {Network Theory Ltd.},\n  isbn =         {9780954612078},\n  year =         {2009},\n  edition =      {3}\n}\n\n@software{Hdf5,\n  author =       {{The HDF Group}},\n  title =        \"{Hierarchical Data Format, version 5}\",\n  year =         {1997-2023},\n  note =         {https://www.hdfgroup.org/HDF5/}\n}\n\n@ARTICLE{Libsharp,\n  author =       {{Reinecke}, M. and {Seljebotn}, D.~S.},\n  title =        \"{Libsharp - spherical harmonic transforms revisited}\",\n  journal =      {Astronomy and Astrophysics},\n  keywords =     {methods: numerical, cosmic background radiation, large-scale\n                  structure of Universe, Physics - Computational Physics},\n  year =         2013,\n  month =        jun,\n  volume =       {554},\n  eid =          {A112},\n  pages =        {A112},\n  doi =          {10.1051/0004-6361/201321494},\n  archivePrefix ={arXiv},\n  eprint =       {1303.4945},\n  primaryClass = {physics.comp-ph},\n  adsurl =       {https://ui.adsabs.harvard.edu/abs/2013A&A...554A.112R},\n  adsnote =      {Provided by the SAO/NASA Astrophysics Data System}\n}\n\n@inproceedings{Libxsmm,\n  author =       {Heinecke, Alexander and Henry, Greg and Hutchinson, Maxwell\n                  and Pabst, Hans},\n  title =        {{LIBXSMM}: {Accelerating} Small Matrix Multiplications by\n                  Runtime Code Generation},\n  year =         {2016},\n  isbn =         {9781467388153},\n  publisher =    {IEEE Press},\n  booktitle =    {Proceedings of the International Conference for High\n                  Performance Computing, Networking, Storage and Analysis},\n  articleno =    {84},\n  numpages =     {11},\n  pages =        {1-11},\n  location =     {Salt Lake City, Utah},\n  series =       {SC '16}\n}\n\n@Article{MatplotLib1,\n  Author =       {Hunter, J. D.},\n  Title =        {Matplotlib: A 2D graphics environment},\n  Journal =      {Computing in Science \\& Engineering},\n  Volume =       {9},\n  Number =       {3},\n  Pages =        {90--95},\n  abstract =     {Matplotlib is a 2D graphics package used for Python for\n                  application development, interactive scripting, and\n                  publication-quality image generation across user interfaces\n                  and operating systems.},\n  publisher =    {IEEE COMPUTER SOC},\n  doi =          {10.1109/MCSE.2007.55},\n  year =         2007\n}\n\n@software{MatplotLib2,\n  author =       {Thomas A Caswell and Michael Droettboom and Antony Lee and\n                  John Hunter and Eric Firing and Elliott Sales de Andrade and\n                  Tim Hoffmann and David Stansby and Jody Klymak and Nelle\n                  Varoquaux and Jens Hedegaard Nielsen and Benjamin Root and\n                  Ryan May and Phil Elson and Darren Dale and Jae-Joon Lee and\n                  Jouni K. Seppänen and Damon McDougall and Andrew Straw and\n                  Paul Hobson and Christoph Gohlke and Tony S Yu and Eric Ma and\n                  Adrien F. Vincent and Steven Silvester and Charlie Moad and\n                  Nikita Kniazev and hannah and Elan Ernest and Paul Ivanov},\n  title =        {matplotlib/matplotlib: {REL:} v3.3.0},\n  month =        jul,\n  year =         2020,\n  publisher =    {Zenodo},\n  version =      {v3.3.0},\n  doi =          {10.5281/zenodo.3948793},\n  url =          {https://doi.org/10.5281/zenodo.3948793}\n}\n\n@Article{NumPy,\n  title =        {Array programming with {NumPy}},\n  author =       {Charles R. Harris and K. Jarrod Millman and St{\\'{e}}fan J.\n                  van der Walt and Ralf Gommers and Pauli Virtanen and David\n                  Cournapeau and Eric Wieser and Julian Taylor and Sebastian\n                  Berg and Nathaniel J. Smith and Robert Kern and Matti Picus\n                  and Stephan Hoyer and Marten H. van Kerkwijk and Matthew Brett\n                  and Allan Haldane and Jaime Fern{\\'{a}}ndez del R{\\'{i}}o and\n                  Mark Wiebe and Pearu Peterson and Pierre G{\\'{e}}rard-Marchant\n                  and Kevin Sheppard and Tyler Reddy and Warren Weckesser and\n                  Hameer Abbasi and Christoph Gohlke and Travis E. Oliphant},\n  year =         {2020},\n  month =        sep,\n  journal =      {Nature},\n  volume =       {585},\n  number =       {7825},\n  pages =        {357--362},\n  doi =          {10.1038/s41586-020-2649-2},\n  publisher =    {Springer Science and Business Media {LLC}},\n  url =          {https://doi.org/10.1038/s41586-020-2649-2}\n}\n\n@book{Paraview1,\n  author =       {Ayachit, Utkarsh},\n  title =        {The ParaView Guide: A Parallel Visualization Application},\n  year =         {2015},\n  isbn =         {1930934300},\n  publisher =    {Kitware, Inc.},\n  address =      {Clifton Park, NY, USA},\n}\n\n@book{Paraview2,\n  title =        {{ParaView}: {An} end-user tool for large-data visualization},\n  author =       {J. Ahrens and Berk Geveci and C. Law},\n  publisher =    {Elsevier},\n  isbn =         {9780123875822},\n  year =         {2005}\n}\n\n@software{Pybind11,\n  author =       {Wenzel Jakob and Jason Rhinelander and Dean Moldovan},\n  year =         {2017},\n  note =         {https://github.com/pybind/pybind11},\n  title =        {pybind11 -- Seamless operability between C++11 and Python}\n}\n\n@software{Yamlcpp,\n  title =        {yaml-cpp},\n  author =       {Beder, Jesse and Woehlke, Matthew and Breitbart, Jens and\n                  Wolchok, Scott and Hackimov, Azamat H. and Snape, Jamie and\n                  Hamlet, Oliver and Novotny, Paul and Tambre, Raul and\n                  Reinhold, Stefan and Vaucher, Alain and Zaitsev, Alexander and\n                  Anokhin, Alexander and Karatarakis, Alexander and Maloney,\n                  Andy and Polukhin, Antony and Brandenburg, Craig M. and\n                  Ibanez, Dan and Gladkikh, Denis and Eich, Florian and Dumont,\n                  Guillaume and Trigg, Haydn and King, Jim and Frederico, Joel\n                  and Hamilton, Jonathan and Langley, Joseph and Hämäläinen,\n                  Lassi and Blair, Matt and Duggan, Michael Welsh and Wang, Olli\n                  and Stotko, Patrick and Levine, Peter and Bena, Petr and\n                  Cordoba, Rodrigo Hernandez and Schmidt, Ryan and Gottlieb,\n                  Simon Gene and Prilmeier, Franz and Zhang, Tanki and Ishi,\n                  Tatsuyuki and Lyngmo, Ted and Mataré, Victor and Konečný,\n                  Michael and USDOE},\n  abstractNote = {A {YAML} parser and emitter in {C++}.},\n  doi =          {10.11578/dc.20220817.13},\n  url =          {https://www.osti.gov/biblio/1881863},\n  year =         {2009},\n  month =        {9}\n}\n"
  },
  {
    "path": "docs/DevGuide/Amr.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Adaptive mesh refinement (AMR) {#dev_guide_amr}\n\n\\tableofcontents\n\n### Introduction\n\nSpECTRE implements a block-based anisotropic h-p adaptive mesh\nrefinement (AMR) algorithm.  Currently AMR can only occur at global\nsynchronization points, but it is planned to remove this restriction\nin the future.\n\n\\warning AMR is still under active development. The elliptic\nexecutables fully support AMR.  The evolution executables currently\nonly support p-adaptivity, and only when using a global time stepper,\nand only when using a DG method.  The executable will abort with an\nERROR if local time stepping or h-refinement is done.\n\nHere is an overview of what is described in detail in the sections below:\n\n- \\ref dev_guide_amr_algorithm \"AMR Algorithm\": An overview of the\n  parallel phases, actions, and components that are used to do\n  adaptive mesh refinement.\n- \\ref dev_guide_amr_criteria \"AMR Criteria\": Describes the\n  expectations and restrictions on concrete classes implementing\n  adaptive mesh refinement criteria.\n- \\ref dev_guide_amr_policies \"AMR Policies\": Describes restrictions\n  and assumptions of the current algorithm, and what would be\n  required to relax them.\n- \\ref dev_guide_amr_projectors \"AMR Projectors\": Describes the\n  requirements on concrete classes which project data to the new or\n  modified elements after mesh refinement.\n- \\ref dev_guide_amr_metavariables \"AMR Metavariables\": Describes the\n  code that needs to be added to the metavariables of an executable in\n  order to enable adaptive mesh refinement.\n- \\ref dev_guide_amr_input_options \"Controlling AMR\": Describes the\n  options in the input file that control the behavior of adaptive mesh\n  refinement.\n\n### AMR Algorithm {#dev_guide_amr_algorithm}\n\nThe AMR algorithm requires communication via simple actions between\nthe elements of the array component that are responsible for the\nElement%s of the Domain and a singleton amr::Component.  See \\ref\ndev_guide_parallelization_foundations\n\"Parallelization and Core Concepts\" for an overview of phases,\nactions, metavariables, and other SpECTRE parallelization concepts.\n\nCurrently the AMR algorithm is run over several phases when a phase\nchange is triggered by an executable.  See \\ref\ndev_guide_amr_input_options \"Controlling AMR\" for how to control when\nthe AMR phases are triggered.\n\n#### Evaluating refinement criteria\n\nWhen AMR is triggered, the executable should enter\nParallel::Phase::EvaluateAmrCriteria.  In this phase, the\namr::Component triggers the simple action\namr::Actions::EvaluateRefinementCriteria on each element of the array\ncomponent (specified by `metavariables::amr::element_array`).  Each\nelement evaluates (in order) the user-specified refinement criteria,\neach of which returns a refinement decision in each logical dimension\nof the element represented as an amr::Flag.  The overall refinement\ndecision in each logical dimension is obtained by taking the highest\npriority decision in the dimension over all criteria, where\namr::Flag::Split has the highest priority and amr::Flag::Join has the\nlowest.  See \\ref dev_guide_amr_criteria \"AMR Criteria\" for more\ndetails on implementing criteria.  Once the overall refinement\ndecision based on the refinement criteria is made, the decision may be\nmodified to satisfy the \\ref dev_guide_amr_policies \"AMR Policies\".\nFinally, the element sends its decision (and other information in\namr::Info) to its neighbors by calling the simple action\namr::Actions::UpdateAmrDecision.\n\nIn amr::Actions::UpdateAmrDecision, an element decides whether or not it\nneeds to update its own refinement decision (or other information in\namr::Info) based on decisions (or other information in amr::Info) sent\nby its neighbor.  Possible reasons to update the refinement decision\nare, e.g., if a sibling neighbor does not want to join an element that\nhad initially decided to join, or if any of the amr::Policies would\nbe violated.  If the refinement decision (or other information in\namr::Info) of the element changes, the element will send its\nupdated amr::Info to its neighbors.\n\nNote that simple actions are executed asynchronusly, so both\namr::Actions::EvaluateRefinementCriteria and\namr::Actions::UpdateAmrDecision have to handle the possibility that\nthey are executed out of order.  For example,\namr::Actions::UpdateAmrDecision could be executed before\namr::Actions::EvaluateRefinementCriteria.  Eventually all elements\nwill reach a state where they do not need to send new information to\ntheir neighbors, thus reaching a state of quiescence.  When this\nhappens Parallel::Phase::EvaluateAmrCriteria ends. At this point all\nelements have reached a consensus on how the domain should be adapted,\nbut no changes have been made to the domain.  In order to adjust the\ndomain, the executable should next start\nParallel::Phase::AdjustDomain.\n\n#### Adjusting the domain\n\nAt the beginning of Parallel::Phase::AdjustDomain, the amr::Component\ncalls amr::Actions::AdjustDomain on each element of the array\ncomponent (specified by `metavariables::amr::element_array`).  If the\nelement wants to split in any dimension, it will call the simple\naction amr::Actions::CreateChild on the amr::Component in order to\ncreate new refined elements that will replace the existing element.\nIf the element wants to join in any dimension, it will either call\namr::Actions::CreateParent on the amr::Component (if it is the joining\nsibling with the lowest SegmentId in each dimension being joined) or\ndo nothing in order to create a single new coarsened element that will\nreplace multiple existing elements.  If the element neither wants to\nsplit nor join in any dimension, then the element mutates items in its\ndb::DataBox.  First the Element, Mesh and neighbor Mesh are updated.\nThen the element calls the\n\\ref dev_guide_amr_projectors \"AMR Projectors\" that will update all\nthe mutable items in the db::DataBox.\nFinally the amr::Info of the element and its neighbors are reset and\ncleared so they are ready for a future invocation of AMR.\n\nWhen amr::Actions::CreateChild is executed by the amr::Component, it\ncreates a new element in the `metavariables::amr::element_array`\npassing a Parallel::SimpleActionCallback that will be executed after\nthe new element is created.  The callback will be either to call\namr::Actions::CreateChild again for the next child to be created, or\namr::Actions::SendDataToChildren which will be invoked on the element\nthat is being split.  amr::Actions::SendDataToChildren will then\ninvoke amr::Actions::InitializeChild on each new child element,\npassing along all of the mutable items in its\ndb::DataBox. amr::Actions::SendDataToChildren will then deregister and\ndelete the element being split. amr::Actions::InitializeChild will\ncreate its Element, Mesh, and neighbor Mesh, and then call the \\ref\ndev_guide_amr_projectors \"AMR Projectors\" that will update all the\nmutable items in its db::DataBox.\n\nWhen amr::Actions::CreateParent is executed by the amr::Component, it\ncreates a new element in the `metavariables::amr::element_array`\npassing a Parallel::SimpleActionCallback that will be executed after\nthe new element is created.  The callback will be\namr::Actions::CollectDataFromChildren which will be invoked on one\nof the elements that are joining.\namr::Actions::CollectDataFromChildren will then invoke either itself\non another element that is joining or amr::Actions::InitializeParent\non the new parent element, collecting all of the mutable items in each\njoining element's db::DataBox. amr::Actions::CollectDataFromChildren\nwill then deregister and delete the element that is\njoining. amr::Actions::InitializeParent will create its Element, Mesh,\nand neighbor Mesh, and then call the \\ref dev_guide_amr_projectors\n\"AMR Projectors\" that will update all the mutable items in its\ndb::DataBox.\n\nWhen all of the actions finish, a state of quiescence is reached.\nWhen this happens Parallel::Phase::AdjustDomain ends.  The executable\nshould now either return to the phase which triggered AMR, or enter\nParallel::Phase::CheckDomain.\n\n### AMR Criteria {#dev_guide_amr_criteria}\n\nWhen amr::Actions::EvaluateRefinementCriteria is executed, for each\nelement, it loops over a list of criteria that is specified in the\ninput file options\n(See \\ref dev_guide_amr_input_options \"Controling AMR\").\nEach criterion must be an option-creatable class derived from\namr::Criterion (see its documentation for a list of requirements for\nthe derived classes).  In particular, each criterion should compute an\narray of amr::Flag%s that represent the recommended refinemenent\nchoice in each logical dimension.  These choices can be computed from\nany items in the db::DataBox of the element or additional compute\nitems specified by the criterion.  It is expected that a criterion\nwill compute something and then either choose to recommend refinement,\nno change, or coarsening the element either in size (h-refinement) or\npolynomial order (p-refinement).  A criterion does not need to handle\nanything that is enforced by the amr::Policies such as worry about\nbounds on the refinement level or number of grid points.\n\n### AMR Policies {#dev_guide_amr_policies}\n\nThe current AMR algorithm has a set of rules that are enforced after\nthe overall refinement choice is determined from the AMR criteria.\n\nThe following rules simplify the code:\n\n- An Element cannot join if it is splitting in any dimension.  Instead\n  any amr::Flag::Join is changed to amr::Flag::DoNothing. Relaxing\n  this restriction would be a non-trivial change that would require\n  more complicated logic in determining if neighboring elements can\n  join, as well as an additional code path to handle the creation and\n  initialization of the new elements. This should not be a significant\n  restriction as in most cases when this would occur it is unlikely\n  that the siblings of the Element would agree to a valid join.\n\n- In two or three spatial dimensions, there must be a 2:1 balance\n  (i.e. within one refinement level) between neighbors in the\n  dimension(s) parallel to the face of the element.  Relaxing this\n  restriction would be a significant change to the code as the\n  code for communicating boundary corrections assumes 2:1 balance.  In\n  order to maintain 2:1 balance with neighboring elements, a choice of\n  amr::Flag::Join may be changed to amr::Flag::DoNothing, and\n  amr::Flag::DoNothing may be changed to amr::Flag::Split.\n\nThe following policies are among those controlled by input file\noptions (see \\ref dev_guide_amr_input_options \"Controlling AMR\" and\nthe documentation for amr::Policies):\n\n- Whether to do isotropic or anisotropic refinement.  If isotropic\n  refinement is done, the AMR decision in each dimension are changed\n  to the decision of the dimension with the highest priority amr::Flag\n  (i.e. amr::Flag::Split has the highest priority).\n\n- Whether or not to enforce 2:1 balance for the refinement level in\n  the direction perpendicular to the face.\n\n- The minimum and maximum of the refinement level and the number of\n  grid points.  The actual bounds on these will be the stricter of\n  those specified by the input file and those set by the code.  For\n  the number of grid points, the code bounds are set by\n  Spectral::minimum_number_of_points and\n  Spectral::maximum_number_of_points for the Spectral::Basis and\n  Spectral::Quadrature of the Mesh.  For the refinement level, the\n  minimum is zero, and the maximum is set by\n  `ElementId::max_refinement_level`.\n\n### AMR Projectors {#dev_guide_amr_projectors}\n\nAfter the grid adapts, the mutable items in the DataBox of new or\nexisting elements must be updated.  The Element, Mesh, neighbor Mesh,\namr::Info, and neighbor amr::Info along with the items in\n`Parallel::Tags::distributed_object_tags` are automatically updated by\nthe AMR actions, but all other items must be explicitly updated via\nprojectors that conform to amr::protocols::Projector.  If any mutable\nitem has not been explicitly handled by any of the projectors, a\n`static_assert` will be triggered listing the tags for the items that\nhave not been projected.\n\nWhen a new element is created, the items in its DataBox are default\nconstructed.  Their Element, Mesh, and neighbor Mesh are mutated to\ntheir desired state.  Then the AMR projectors are called, passing\nalong the items in the DataBox(es) from their splitting parent element\nor joining children elements.  Existing elements first mutate their\nElement, Mesh, and neighbor Mesh, and then call the AMR projectors,\npassing along the old Element and Mesh.\n\nThe `return_tags` of each projector lists the tags for the items that\nthe projector mutates.  From the pre-AMR state, the projector must\ncompute the post-AMR state of these items.  Typically during\nParallel::Phase::Initialization, a group of items will be initialized\nby a specific action or mutator in the action\n`Initialization::Actions::InitializeItems`.  Therefore it makes sense\nto create a projector that will handle the same group of items.\nHowever some items are initialized from input file options, while\nothers are left default initialized and not mutated by any\ninitialization action.  These items will still need to be handled by\nsome projector.  Two convenience projectors are provided:\namr::projectors::DefaultInitialize which value initializes the items\ncorresponding to the listed tags; and\namr::projectors::CopyFromCreatorOrLeaveAsIs that will either copy the\nitem from the parent (or children, asserting that all children agree)\nof a newly created element, or leave the item unmodified for an\nexisting element for the items corresponding to the listed tags.\n\nThe list of projectors is specified in\n`metavariables::amr::projectors` in the executable, and the projectors\nare evaluated in the order in which they are specified.\n\n### Enabling AMR for an executable {#dev_guide_amr_metavariables}\n\nIn order to enable AMR for an executable, the following changes need\nto be made in the metavariables:\n\n- The addition of a struct named `amr` such as the following example:\n```\nstruct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = tmpl::list<::amr::projectors::DefaultInitialize<\n        domain::Tags::InitialExtents<Dim>,\n        domain::Tags::InitialRefinementLevels<Dim>,\n        evolution::dg::Tags::Quadrature>>;\n    static constexpr bool keep_coarse_grids = false;\n  };\n```\n\n  where `element_array` specifies the array component on which AMR\n  will operate and `projectors` is a type list of amr::projectors\n  that govern how the items in the DataBox for the `element_array` are\n  updated after refinement.\n\n- The addition of amr::Component to the `component_list` of the metavariables\n\n- The addition (or modification) of the following `tmpl::pair`s in\n  `factory_creation::factory_classes`:\n```\n        tmpl::pair<amr::Criterion,\n                   tmpl::list<LIST_OF_CRITERIA>,\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<\n                PhaseControl::VisitAndReturn<\n                    Parallel::Phase::EvaluateAmrCriteria>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::AdjustDomain>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::CheckDomain>>,\n```\nwhere `LIST_OF_CRITERIA` should be a list of amr::Criteria.\n\n- The addition of the following item in the list of PhaseActions for\n  the component specified in `amr::element_array`:\n\n```\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n```\n\n- The possible addition of `PhaseControl::Actions::ExecutePhaseChange`\n  to the action list for the appropriate Phase in the PhaseAction of\n  the `amr::element_array`\n\n- The addition of the mutator `amr::Initialization::Initialize` to the\n  list of mutators in `Initialization::Actions::InitializeItems` in\n  the Parallel::PhaseActions list for Parallel::Phase::Initialization.\n\n- The appropriate includes for all of the above.\n\n### Controlling AMR {#dev_guide_amr_input_options}\n\nThere are two places in the input file that control how and when AMR happens.\n\nThe \"how\" is controlled by the option group `Amr`.  Here you list the\namr::Criteria being used, the available options for the amr::Policies, and\nthe `Verbosity` of diagnostic messages that are printed.  Here is an\nexample:\n```\nAmr:\n  Criteria:\n    - TruncationError:\n        VariablesToMonitor: [Psi]\n        AbsoluteTarget: 1.e-6\n        RelativeTarget: 1.0\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n```\n\nNote that the values `Auto` for the Limits options choose the default\nlimits set by the code. Also note that if a criterion tries to refine beyond the\nlimits, whether or not the code should error is controlled by\n`ErrorBeyondLimits`.\n\n\"When\" AMR happens is controlled by specifying a Trigger and a list of\n`PhaseChanges` in the top-level option `PhaseChangesAndTriggers`.  For\nexample:\n```\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(CheckDomain)\n```\n\nBoth `EvaluateAmrCriteria` and `AdjustDomain` are required in order\nfor AMR to work.  `VisitAndReturn(CheckDomain)` performs diagnostics\nand can be omitted.  To turn off AMR, omit the three phase changes above.\n"
  },
  {
    "path": "docs/DevGuide/AutomaticVersioning.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Automatic versioning {#dev_guide_automatic_versioning}\n\n\\tableofcontents\n\nOur automated tests can tag and publish a release automatically when requested.\nThe automation is implemented as a [GitHub\nActions](https://docs.github.com/actions) workflow in the file\n`.github/workflows/Tests.yaml`.\n\n## Creating releases\n\nThe GitHub workflow responsible for automated testing also allows creating a\nrelease when dispatched manually. To create a release (roughly on the first\nMonday of each month and/or when needed for publications), follow these\ninstructions:\n\n1. **Check for bugs on `develop`.** If you are aware of any major bugs in the\n   code on `develop`, check with the (other) core developers if it would be\n   better to wait with the release until those bugs are fixed.\n2. **Check the release notes.** Find the latest run of the [`Tests` workflow\n   on the `develop` branch](https://github.com/sxs-collaboration/spectre/actions/workflows/Tests.yaml?query=branch%3Adevelop). Select the \"Files and formatting\" job and the\n   \"Print release notes\" step within. Go through the release notes, checking\n   the following in particular:\n   - Label bugfix PRs with the `bugfix` label so they appear in the \"Bugfixes\"\n     section and don't clutter the list.\n   - Label PRs that you want to highlight with the `new feature` label, so they\n     appear at the top. The selection is up to you.\n   - Add upgrade instructions to PRs where they are missing, to help other\n     developers with rebasing their code.\n3. **Dispatch the release.** Go to the GitHub Actions page for the [`Tests`\n   workflow](https://github.com/sxs-collaboration/spectre/actions/workflows/Tests.yaml?query=branch%3Adevelop).\n   [Select \"Run workflow\"](https://docs.github.com/actions/managing-workflow-runs/manually-running-a-workflow)\n   and the `develop` branch. Type in a valid release version name, such as\n   \"2021.05.04\", and hit \"Run workflow\". The release will only be created if\n   if the version name matches the format defined in\n   \\ref versioning_and_releases _and_ if it matches the current date. Therefore,\n   if you wait too long with the next step, the release will fail and you have\n   to dispatch it again.\n\n   It is probably best to hold off merging PRs while the release workflow runs,\n   to avoid possible conflicts. Therefore, warn the (other) core devs.\n4. **Ask for approval.** Once the unit tests are complete, the release workflow\n   will wait for approval before publishing the release on GitHub and Zenodo.\n   Notify the (other) core devs so one of them can review and approve the\n   release (don't approve the release yourself). You can point them to these\n   guidelines:\n\n   Guidelines for reviewing a release:\n   - Check for bugs on `develop` (see point 1. above).\n   - Check the release notes (see point 2. above).\n\n## Release notes\n\nThe release notes are compiled automatically based on the activity in the\nrepository since the last release. They will contain a list of merged\npull-requests. Pull-requests labeled \"new feature\" or \"bugfix\" on GitHub\nwill be classified as such in the release notes.\n\nThe script `tools/CompileReleaseNotes.py` generates the release notes and can\nalso be invoked manually with a Python 3 interpreter to retrieve an overview of\nwhat happened in the repository recently. The script requires you install\n`GitPython`, `PyGithub` and `tqdm` in your Python environment.\n"
  },
  {
    "path": "docs/DevGuide/Brigand.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Metaprogramming with Brigand {#brigand}\n\n\\tableofcontents\n\n\\tableofcontents{HTML:2}\n\n[comment]: # (The \\pars improve the spacing in the generated document when)\n[comment]: # (many \\snippets are involved.)\n\n\\par\nIn SpECTRE, most complex TMP is done using the [Brigand metaprogramming\nlibrary](https://github.com/edouarda/brigand).  Brigand is a collection of\ntemplated classes, type aliases, and functions, primarily intended to help with\nthe manipulation and use of lists of types.  This document is organized to\nroughly parallel the structure of the C++ standard, rather than following\nBrigand's classifications.\n\n\\par\nBrigand provides all of its functionality in the `brigand` namespace, but in\nSpECTRE we have aliased this namespace to `tmpl`, and the latter should be\npreferred.\n\n\\par\nAll functionality described here is provided by SpECTRE's Brigand wrapper\nheader:\n\\snippet Test_TMPLDocumentation.cpp include\n\n\\par\nExamples in this document use the following declarations and definitions:\n\\snippet Test_TMPLDocumentation.cpp example_declarations\n\n\n\\section Metafunctions\n\n\\par\nA metafunction is an analog of a familiar C++ function that is coded in the C++\ntype system.  It turns out that, using metafunction programming, it is possible\nto perform arbitrary computations at compile time.\n\n\\par\nThere are multiple ways to encode a calculation in the type system.  When using\nBrigand, the relevant ones are eager and lazy metafunctions.\n\n\n\\subsection lazy Eager and lazy metafunctions\n\n\\par\nMetafunctions commonly appear in two forms: eager and lazy.  Lazy metafunctions\nare templated structs (or templated aliases to structs) with a `type` member\nalias that indicates the result:\n\\snippet Test_TMPLDocumentation.cpp metafunctions:lazy\nThe type traits in the standard library, such as std::is_same, are lazy\nmetafunctions.\n\n\\par\nEager metafunctions are aliases to their result types.  As a trivial case,\nstruct templates can be viewed as eager metafunctions returning themselves.  An\neager version of the previous example could be implemented as:\n\\snippet Test_TMPLDocumentation.cpp metafunctions:eager\nThe standard library provides eager versions of some of its metafunctions\n(generally those that modify a type, rather than predicates) using an `_t`\nsuffix.  When both versions are provided, it is often convenient (and less\nerror prone!) to define the eager version in terms of the lazy version:\n\\snippet Test_TMPLDocumentation.cpp metafunctions:eager_from_lazy\nAnd the two definitions agree:\n\\snippet Test_TMPLDocumentation.cpp metafunctions:agreement\n\n\\note\nThe standard library also provides versions of many of its type traits with an\n`_v` suffix.  These evaluate to compile-time *values*, rather than types.  They\ncan be useful for metaprogramming, but are not the types of metafunctions being\ndiscussed here.\n\n\\par\nEager metafunctions are usually more convenient to use, so what is the point of\nadditionally creating lazy ones?  The answer is that lazy metafunctions can be\nused as compile-time functors.  As a simple example, we can write an (eager)\nmetafunction that calls an arbitrary lazy metafunction twice\n\\snippet Test_TMPLDocumentation.cpp metafunctions:call_lazy_metafunction\nand get the expected output:\n\\snippet Test_TMPLDocumentation.cpp metafunctions:call_lazy_metafunction_assert\nBut it fails if you try to call an arbitrary *eager* metafunction\ntwice in the same way, because the function is evaluated too early,\nresulting in the `List1` metafunction being acted upon instead of\n`eager_add_list`:\n\\snippet Test_TMPLDocumentation.cpp metafunctions:call_eager_metafunction\n\\snippet Test_TMPLDocumentation.cpp metafunctions:call_eager_metafunction_assert\n(In this simple case we could have used a template template parameter to pass\nthe eager metafunction in a form more similar to a runtime lambda, but the\npossibilities for generic manipulation of parameter lists containing template\ntemplate parameters are limited, so their use must be minimized in complex\nmetaprogramming.)\n\n\\note\nIn practice, lazy metafunctions are often implemented as empty structs\ninheriting from other lazy metafunctions.  The entire inheritance chain then\ninherits a `type` alias from the ultimate base class.\n\n\\par\nMost of the standard Brigand functions are eager, but many have lazy versions\nin the nested `tmpl::lazy` namespace.  These are indicated by calls to the\n`HAS_LAZY_VERSION` macro in the examples below.\n\n\n\\subsection metalambdas Brigand metalambdas\n\n\\par\nThis use of lazy metafunctions is too limited for general use, however, because\nit requires the definition of a new templated struct for every new function.\nBrigand uses a more general notation, known as metalambdas.  A metalambda is a\n(possibly nested set of) lazy metafunctions with some template arguments\nreplaced by the placeholders `tmpl::_1`, `tmpl::_2`, etc.  These are the first,\nsecond, etc., arguments of the metalambda, and will be replaced by the actual\narguments when the lambda is used.  The lazy nature of the metafunctions\nprevents them from prematurely evaluating to results based on the literal\nplaceholder types.  The \\ref apply \"tmpl::apply\" function can be used to\nevaluate a metalambda with specified arguments, and many other Brigand\nfunctions take metalambdas that are evaluated internally.\n\n\n\\subsection metalambda_structure Evaluation of metalambdas\n\n\\note\nNone of the terminology introduced in this section is standard.\n\n\\par\nWhen evaluating a metalambda, the values of any \\ref args \"arguments\"\nencountered are taken from the evaluation's argument stack.  The argument stack\nis a stack (in the CS sense) of collections of zero or more arguments.  The\nvalues of any \\ref args \"arguments\" that are evaluated are taken from the\ncollection at the head of the stack.  The remaining collections are arguments\ncaptures in closures, and will not be present in code not using \\ref defer\n\"tmpl::defer\".\n\n\\par\nThe \\ref apply \"tmpl::apply\" metafunction is the Brigand metafunction for\nevaluating metalambdas.  It can be called explicitly, and is called internally\nby many other functions.  It takes a metalambda and arguments to be passed to\nthat metalambda.  The argument stack for the evaluation has one entry: the\npassed arguments.\n\n\\par\nThe argument stack can gain additional entries through the creation of\nmetaclosures using \\ref defer \"tmpl::defer\".  When \\ref defer \"tmpl::defer\" is\nevaluated, it produces a \\ref metalambda_metaclosure \"metaclosure\" containing a\ncopy of the current argument stack, acting as the lambda captures.  When that\nmetaclosure is evaluated, the previously active stack is replaced by the stored\nstack with the head of the old stack pushed onto it.\n\n\\par\nThis makes \\ref args \"arguments\" in a \\ref metalambda_metaclosure \"metaclosure\"\nrefer to the arguments \"passed\" to it.  (No explicit call syntax is used, but\nthe arguments are inherited from the calling context.)  The captured arguments\nare accessible using \\ref parent \"tmpl::parent\", which pops off the last entry\nin the argument stack.\n\n\\par\nThere are eight forms that a metalambda can take: an argument, a lazy\nexpression, a bind expression, a pin expression, a defer expression, a parent\nexpression, a constant, or a metaclosure.\n\n\\subsubsection args Argument\n\n\\par\nAn argument is one of the structs `tmpl::_1`, `tmpl::_2`, or `tmpl::args<n>`\nfor `unsigned int` n.  The additional aliases `tmpl::_3`, `tmpl::_4`, ...,\n`tmpl::_9` are provided to `tmpl::args<2>`, `tmpl::args<3>`, ...,\n`tmpl::args<8>`.  (The first two arguments have dedicated types in addition to\nthe general `tmpl::args<0>` and `tmpl::args<1>`.  My best guess is for\nperformance reasons.)\n\\snippet Test_TMPLDocumentation.cpp tmpl::args\nWhen evaluated, they give the first (`tmpl::_1`), second (`tmpl::_2`), or\nzero-indexed Nth (`tmpl::args<N>`) entry from the collection of arguments at\nthe top of the argument stack.\n\\snippet Test_TMPLDocumentation.cpp tmpl::args:eval\nAdditionally, `tmpl::_state` and `tmpl::_element` are aliased to `tmpl::_1` and\n`tmpl::_2`, primarily for use with \\ref fold \"tmpl::fold\".\n\n\\par\nWhen evaluating a metalambdas, the metalambda must be passed enough arguments\nto define all argument placeholders in its body.  When evaluating using \\ref\napply \"tmpl::apply\", arguments are passed as template parameters after the\nmetalambda.  Other Brigand functions that evaluate metalambdas pass them a\nspecified number of arguments (usually 1 or 2).  Failure to pass enough\narguments may error or produce unintuitive results.\n\n\\subsubsection metalambda_lazy Lazy expression\n\n\\par\nA lazy expression is a fully-specialized struct template with only type\ntemplate parameters that is not a specialization of \\ref pin \"tmpl::pin\", \\ref\ndefer \"tmpl::defer\", or \\ref parent \"tmpl::parent\" and is not a \\ref\nmetalambda_metaclosure \"metaclosure\".  When evaluated, each of its template\nparameters is evaluated as a metalambda and replaced by the result, and then\nthe struct's `type` type alias is the result of the full lazy-expression.\n\\snippet Test_TMPLDocumentation.cpp metalambda_lazy\n\n\\subsubsection bind Bind expression\n\n\\par\nA bind expression is a specialization of `tmpl::bind`.  It wraps an eager\nmetafunction and its arguments.  When evaluated, the arguments are each\nevaluated as metalambdas, and then the results are passed to the eager\nmetafunction.\n\\snippet Test_TMPLDocumentation.cpp tmpl::bind\n\n\\note\nThe `tmpl::bind` metafunction does not convert an eager metafunction to a lazy\none.  It is handled specially in the evaluation code.\n\n\\subsubsection pin Pin expression\n\n\\par\nA pin expression is a specialization of `tmpl::pin`.  Evaluating a pin\nexpression gives the (unevaluated) argument to `tmpl::pin`.  This can be used\nto force a type to be treated as a \\ref metalambda_constant \"constant\", even if\nit would normally be treated as a different type of metalambda (usually a \\ref\nmetalambda_lazy \"lazy expression\").\n\\snippet Test_TMPLDocumentation.cpp tmpl::pin\n\n\\par\nPin expressions are often used to protect template arguments to eager\nmetafunctions:\n\\snippet Test_TMPLDocumentation.cpp tmpl::pin:protect_eager\n\n\\subsubsection defer Defer expression\n\n\\par\nA defer expression is a specialization of `tmpl::defer`.  It does not evaluate\nits argument, but results in a \\ref metalambda_metaclosure \"metaclosure\"\ncontaining the passed metalambda and the current argument stack.\n\n\\par\nExample:\n\\snippet Test_TMPLDocumentation.cpp tmpl::defer\nThe evaluation here proceeds as follows:\n\n1. The innermost eager metafunction is the second \\ref apply \"tmpl::apply\".  It\n   creates an argument stack with one collection containing the single argument\n   `Type1` and proceeds to evaluate `tmpl::defer<tmpl::_1>`.\n\n2. Evaluating the defer expression creates a \\ref metalambda_metaclosure\n   \"metaclosure\" with the contents `tmpl::_1` and the argument stack from the\n   first apply.  This is the result of the inner \\ref apply \"tmpl::apply\".\n\n3. Next the outer \\ref apply \"tmpl::apply\" is evaluated.  It creates an\n   argument stack with one collection containing the single argument`Type2`,\n   and proceeds to evaluate the \\ref metalambda_metaclosure \"metaclosure\"\n   created above.\n\n[comment]: # (Keep the numbering here in sync with the `parent` example.)\n\n4. Evaluating the \\ref metalambda_metaclosure \"metaclosure\" (see that section\n   below) evaluates the contained `tmpl::_1` with a two-element argument stack:\n   head to tail [(`Type2`), (`Type1`)].\n\n5. The first (and only) argument (`tmpl::_1`) in the head of the argument stack\n   is `Type2`, which is the result of the \\ref metalambda_metaclosure\n   \"metaclosure\" and the entire expression.\n\n\n\\par\nThe primary purposes for `tmpl::defer` are constructing metalambdas to pass to\nother metafunctions and preventing \"speculative\" evaluation of a portion of a\nmetalambda that is not valid for some arguments.  See the examples below, in\nparticular \\ref make_subtracter, \\ref multiplication_table, \\ref maybe_first,\nand \\ref column_with_zeros.\n\n\\subsubsection parent Parent expression\n\n\\par\nA parent expression is a specialization of `tmpl::parent`.  It evaluates its\nargument (treated as a metalambda) after popping the top entry off the argument\nstack.  This provides access to the captured arguments in a \\ref\nmetalambda_metaclosure \"metaclosure\".\n\n\\par\nExample:\n\\snippet Test_TMPLDocumentation.cpp tmpl::parent\nThe creation of the \\ref metalambda_metaclosure \"metaclosure\" here is similar\nto the example for \\ref defer \"tmpl::defer\", except that the contained\nmetalambda is `tmpl::parent<tmpl::_1>` instead of a plain `tmpl::_1`.  The\nevaluation proceeds as:\n\n1. As in \\ref defer \"tmpl::defer\" example.\n\n2. As in \\ref defer \"tmpl::defer\" example.\n\n3. As in \\ref defer \"tmpl::defer\" example.\n\n4. The \\ref metalambda_metaclosure \"metaclosure\" evaluates the contained\n   `tmpl::parent<tmpl::_1>` with the argument stack [(`Type2`), (`Type1`)].\n\n5. Evaluating the `tmpl::parent` pops the stack, and so evaluates `tmpl::_1`\n   with the stack [(`Type1`)].\n\n6. The first (and only) argument (`tmpl::_1`) in the stack's head is now\n   `Type1`, which is the result of the \\ref metalambda_metaclosure\n   \"metaclosure\" and the entire expression.\n\n\\warning\nDo not call `tmpl::parent` when the argument stack is empty, i.e., do not\nattempt to access more sets of captured arguments than have been captured.  If\nyou want to prevent evaluation of an expression, use \\ref pin \"tmpl::pin\".\n\n\\subsubsection metalambda_constant Constant\n\n\\par\nA constant metalambda is any type that is not a struct template with only type\ntemplate parameters, a specialization of \\ref bind \"tmpl::bind\", or a\nmetaclosure.  A constant metalambda evaluates to itself.\n\\snippet Test_TMPLDocumentation.cpp metalambda_constant\n\n\\subsubsection metalambda_metaclosure Metaclosure\n\n\\par\nA metaclosure is an opaque type produced by \\ref defer \"tmpl::defer\",\ncontaining a metalambda and an argument stack.  When a metaclosure is\nevaluated, it evaluates the packaged metalambda with an argument stack\nconstructed by pushing the head of the current argument stack onto the argument\nstack stored in the metaclosure.  See \\ref defer and \\ref parent for examples.\n\n\n\\subsection Examples\n\n\n\\subsubsection evens\n\n\\par\nFinds all numbers in a list that are even.\n\\snippet Test_TMPLDocumentation.cpp metafunctions:evens\n\\snippet Test_TMPLDocumentation.cpp metafunctions:evens:asserts\n\n\\par\nThe \\ref filter \"tmpl::filter\" metafunction takes a metalambda as its second\nargument.  The \\ref integral_constant \"tmpl::integral_constant\"s have non-type\ntemplate parameters, so they are treated as constant expressions.  The \\ref\nmath_comparison \"tmpl::equal_to\" and \\ref math_arithmetic \"tmpl::modulo\"\nmetafunctions are lazy, despite not being in the `tmpl::lazy` namespace.\n\n\n\\subsubsection maybe_first\n\n\\par\nReturns the first element of a list, or \\ref no_such_type_ \"tmpl::no_such_type_\"\nif the list is empty.\n\\snippet Test_TMPLDocumentation.cpp metafunctions:maybe_first\n\\snippet Test_TMPLDocumentation.cpp metafunctions:maybe_first:asserts\n\n\\par\nIn this example, the inner \\ref apply \"tmpl::apply\" call evaluates the \\ref if_\n\"tmpl::if_\" statement, returning either a \\ref metalambda_metaclosure\n\"metaclosure\" or \\ref no_such_type_ \"tmpl::no_such_type_\".  The outer \\ref\napply \"tmpl::apply\" either evaluates the metaclosure, calling \\ref front\n\"tmpl::front\", or evaluates \\ref no_such_type_ \"tmpl::no_such_type_\", which is\na constant and gives itself.\n\n\\par\nThe reason for creating a metaclosure is that all the arguments to \\ref if_\n\"tmpl::if_\" are always evaluated (it is an ordinary metafunction with no\nspecial treatment during evaluation).  This is a problem, because, in a naive\nattempt at this metafunction, if `L` is an empty list\n`tmpl::front<tmpl::list<>>` would be evaluated for the first branch.  To avoid\nthis we use \\ref defer \"tmpl::defer\" to wrap the call to \\ref front\n\"tmpl::front\" in a metaclosure, which we evaluate only if necessary.\n\n\\par\nNote that this metaclosure does not capture anything.  `L` is substituted\naccording to normal C++ rules before any Brigand evaluation.  This means that\nthe contents of the `tmpl::defer` are, for the first call above,\n`tmpl::bind<tmpl::front, tmpl::pin<tmpl::list<Type1>>>`.  Without the \\ref pin\n\"tmpl::pin\", the list would be interpreted as a lazy metafunction, resulting in\nan error because it does not have a `type` type alias.\n\n\\par\nThe `L` in `tmpl::size<L>` does not need to be protected by a \\ref pin\n\"tmpl::pin\" because \\ref size \"tmpl::size\" is an eager metafunction, so that\nexpression has been converted to a \\ref integral_constant\n\"tmpl::integral_constant\" before the metalambda evaluation starts.\n\n\n\\subsubsection factorial\n\n\\par\nCalculates the factorial using a simple metalambda passed to a \\ref fold\n\"tmpl::fold\".\n\\snippet Test_TMPLDocumentation.cpp metafunctions:factorial\n\\snippet Test_TMPLDocumentation.cpp metafunctions:factorial:asserts\n\n\\par\nA nearly literal rewrite of this into runtime C++ is\n\\snippet Test_TMPLDocumentation.cpp metafunctions:factorial_cpp\n\n\\par\nThe equivalent of a range-based for loop is easier to express in functional\nprogramming (as a fold) than a standard counting for loop.\n\n\n\\subsubsection make_subtracter\n\n\\par\nDemonstrates the use of captures in metalambdas.\n\\snippet Test_TMPLDocumentation.cpp metafunctions:make_subtracter\n\\snippet Test_TMPLDocumentation.cpp metafunctions:make_subtracter:asserts\n\n\\par\nThis metafunction returns a \\ref metalambda_metaclosure \"metaclosure\" that\nsubtracts a given number from it's argument.  That metaclosure uses both the\nargument passed to it (`tmpl::_1`, which is 5 in the example) and the value\ncaptured at it's creation (`tmpl::parent<tmpl::_1>`, which is 3 in the\nexample).\n\n\\par\n(This `make_subtracter` could be implemented more simply as\n\\snippet Test_TMPLDocumentation.cpp metafunctions:make_subtracter_simple\nbut that doesn't demonstrate metaclosures.)\n\n\n\\subsubsection multiplication_table\n\n\\par\nConstructs a multiplication table.\n\\snippet Test_TMPLDocumentation.cpp metafunctions:multiplication_table\n\\snippet Test_TMPLDocumentation.cpp metafunctions:multiplication_table:asserts\n\n\\par\nThis demonstrates the use of \\ref defer \"tmpl::defer\" to pass a closure as an\nargument to a metafunction (\\ref transform \"tmpl::lazy::transform\"), while\ncapturing an argument from the outer context (the metalambda evaluated for the\nouter \\ref transform \"tmpl::transform\").  This is the use most similar to\ncommon uses of the C++ lambda.\n\n\\par\nThe outer (eager) \\ref transform \"tmpl::transform\" evaluates its second\nargument as a metalambda.  This first evaluates the arguments to the inner \\ref\ntransform \"tmpl::lazy::transform\".  The first argument is a \\ref list\n\"tmpl::list\" of \\ref integral_constant \"tmpl::integral_constant\"s (because the\n\\ref range \"tmpl::range\" is eager and has already been evaluated).  This must\nbe protected by a \\ref pin \"tmpl::pin\" because it looks like a lazy\nmetafunction.  The second argument gives a metaclosure, capturing the value\nfrom the outer \\ref transform \"tmpl::transform\" (available as\n`tmpl::parent<tmpl::_1>`).  The \\ref list \"tmpl::list\" (without the \\ref pin\n\"tmpl::pin\", which has already been evaluated) and metaclosure are then passed\nto the inner \\ref transform \"tmpl::lazy::transform\".\n\n\n\\subsubsection column_with_zeros\n\n\\par\nExtracts a column from a row-major matrix, extending any short rows with zeros.\n\\snippet Test_TMPLDocumentation.cpp metafunctions:column_with_zeros\n\\snippet Test_TMPLDocumentation.cpp metafunctions:column_with_zeros:asserts\n\n\\par\nThis example shows another use of \\ref defer \"tmpl::defer\" to avoid evaluating\nan invalid expression, similar to \\ref maybe_first.  The use of an \\ref args\n\"argument\" in the deferred branch makes this case more complicated: a \\ref\nparent \"tmpl::parent\" expression is used to access arguments from where the\n\\ref defer \"tmpl::defer\" occurs to avoid having to pass the argument explicitly\nusing the \\ref apply \"tmpl::apply\" call.\n\n\\par\nThis is the \"apply-defer-parent\" pattern for lazy evaluation.  A \\ref parent\n\"tmpl::parent\" is placed immediately inside a \\ref defer \"tmpl::defer\" with a\n(not immediately) surrounding \\ref apply \"tmpl::apply\".  The \\ref apply\n\"tmpl::apply\" and \\ref defer \"tmpl::defer\" collectively add an (empty) element\nto the head of the argument stack, which is then popped off to restore the\noriginal value.  This causes the interior metalambda to have the same result it\nwould have had without the \\ref defer \"tmpl::defer\".\n\n\n\\subsubsection factorial_recursion\n\n\\par\nAgain calculates the factorial, but using a recursive algorithm.\n\\snippet Test_TMPLDocumentation.cpp metafunctions:factorial_recursion\n\\snippet Test_TMPLDocumentation.cpp metafunctions:factorial_recursion:asserts\n\n\\par\nThis is a direct translation of the common definition \\f$f(N) = N f(N-1)\\f$ for\nnonzero \\f$N\\f$, and \\f$f(0) = 1\\f$.  The metalambda is passed a copy of itself\nas the first argument and the value to take the factorial of as the second.\n\n\\par\nThis again uses the \"apply-defer-parent\" pattern to prevent \"speculative\"\nevaluation of conditional branches.  In this example, speculative evaluation of\nthe branch is invalid because it would recurse infinitely.\n\n\n\\subsubsection primes\n\n\\par\nGenerates a list of prime numbers less than `N` using the sieve of\nEratosthenes.  This example defines three helper metafunctions.  Two, `zero`\nand `replace_at`, are defined only for clarity's sake and could be inlined.\nThe third, `range_from_types`, is not easily inlinable, and works around\nBrigand's lack of sequence generating functions without non-type template\nparameters.\n\\snippet Test_TMPLDocumentation.cpp metafunctions:primes\n\\snippet Test_TMPLDocumentation.cpp metafunctions:primes:asserts\n\n\\par\nThis is roughly equivalent to the following C++ code with loops converted to\nfold expressions over ranges.\n\\snippet Test_TMPLDocumentation.cpp metafunctions:primes_cpp\n\n\n\\section metafunction_guidelines Guidelines for writing metafunctions\n\n\\par\nThis section covers a few general guidelines for writing metafunctions in\nSpECTRE.  Some of the Brigand functions mentioned below have specific advice as\nwell.\n\n1. For general metafunctions, write both lazy and eager versions.  Follow the\n   STL convention of `foo` being lazy, `foo_t` being eager, and `foo_v` being a\n   constexpr value (if applicable).  This does not apply to internal-use or\n   special-purpose metafunctions.\n   \\snippet Test_TMPLDocumentation.cpp guidelines:lazy_eager\n\n2. Don't perform unnecessary return-type conversions.  We often recommend using\n   STL types over equivalent Brigand types, but if the implementation naturally\n   produces a Brigand type do not do extra work to convert it.\n\n\n\\section function_docs Brigand types and functions\n\n\\par\nIn this section, CamelCase identifiers indicate [type template\nparameters](https://en.cppreference.com/w/cpp/language/template_parameters#Type_template_parameter)\nor, occasionally, [template template\nparameters](https://en.cppreference.com/w/cpp/language/template_parameters#Template_template_parameter).\nIdentifiers in all lowercase indicate [non-type template\nparameters](https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter).\nIdentifiers in [brackets] are optional, and the default value will be\nidentified in the prose description.  Identifiers with ellipses... represent\nzero or more parameters.\n\n\\par\nThe *head* of a fully specialized class template is the class template itself\n(e.g., the head of `tmpl::list<T, U, V>` is `tmpl::list`).  When taken as a\nmetafunction parameter, these are template template parameters and are usually\ncalled `Head` below.\n\n\\par\nAn identifier called `Sequence` must be a full specialization of a class\ntemplate with no non-type template parameters.  Many functions do not make\nsense on sequences with a fixed length, and these require the head of their\nsequence arguments to take a variable number of template arguments.  In\npractical applications, sequence arguments will usually be specializations of\n\\ref list \"tmpl::list\".\n\n\\par\nParameters called `Predicate` must be unary metalambdas returning \\ref\nintegral_constant \"tmpl::integral_constant\"s of `bool` or compatible classes.\n\n\\par\nParameters called `Comparator` must be binary metalambdas returning \\ref\nintegral_constant \"tmpl::integral_constant\"s of `bool` or compatible classes.\nThey must establish a [strict weak\nordering](https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings) on\nthe types they will be applied to in the same manner as runtime comparators\nfrom the STL.\n\n\\par\nMetafunctions documented here are eager unless otherwise noted.  In many cases,\nBrigand provides lazy versions of its metafunctions under the same name in the\n`tmpl::lazy` namespace.  These cases are indicated by the presence of the\n`HAS_LAZY_VERSION` macro in the usage example.\n\n\n\\subsection Containers\n\n\\par\nBrigand provides container classes with the sole purpose of wrapping other\nthings.\n\n\n\\subsubsection integral_constant integral_constant<T, value>\n\n\\par\nA compile-time value `value` of type `T`.  Very similar to\nstd::integral_constant, except that the `constexpr` specifiers on the member\nfunctions have been omitted.\n\\snippet Test_TMPLDocumentation.cpp tmpl::integral_constant\n\n\\par\nBrigand supplies type aliases for constants of some specific types:\n\\snippet Test_TMPLDocumentation.cpp tmpl::integral_constant::abbreviations\n\n\\par\nMost metafunctions that accept integral_constants will accept any type with a\n`value` static member variable.\n\n\\par\nBecause of the `type` type alias, integral_constants behave like lazy\nmetafunctions returning themselves.  Most lazy metafunctions producing an\nintegral_constant will actually inherit from their result, so `value` will be\ndirectly available without needing to go through the `type` alias.\n\n\\remark\nPrefer std::integral_constant, except for the convenience wrapper\n`tmpl::size_t` or when necessary for type equality comparison.\n\n\n\\subsubsection list list<T...>\n\n\\par\nAn empty struct templated on a parameter pack, with no additional\nfunctionality.\n\\snippet Test_TMPLDocumentation.cpp tmpl::list\n\n\\par\nMost metafunctions that operate on lists will work on any struct template.\n\n\n\\subsubsection map map<Pair...>\n\n\\par\nA collection of key-value \\ref pair \"tmpl::pair\"s with unique keys.  See the\nsection on \\ref map_operations \"operations on maps\" for details.\n\\snippet Test_TMPLDocumentation.cpp tmpl::map\n\n\\par\nThe actual type of a map is unspecified, but it has the same template\nparameters as a call to `map` that would produce it.\n\n\\warning\nEquivalent maps may have different types, depending on the order their keys are\nstored in internally.\n\n\n\\subsubsection pair pair<T1, T2>\n\n\\par\nA pair of types, with easy access to each type in the pair.\n\\snippet Test_TMPLDocumentation.cpp tmpl::pair\n\n\n\\subsubsection set set<T...>\n\n\\par\nAn unordered collection of distinct types.  Trying to create a `set` with\nduplicate entries is an error (but \\ref set_insert \"tmpl::insert\" ignores\nduplicate entries).  See the section on \\ref set_operations\n\"operations on sets\" for details.\n\\snippet Test_TMPLDocumentation.cpp tmpl::set\n\n\\par\nThe actual type of a set is unspecified, but it has the same template\nparameters as a call to `set` that would produce it.\n\n\\warning\nEquivalent sets may have different types, depending on the order their elements\nare stored in internally.\n\n\n\\subsubsection type_ type_<T>\n\n\\par\nA struct containing a `type` alias to `T`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::type_\n\n\\par\nWhen extracting the type, programmers are encouraged to use \\ref type_from\n\"tmpl::type_from\" to make it clear that the \\c \\::type that would otherwise\nappear is not an evaluation of a lazy metafunction.  See \\ref always\n\"tmpl::always\" or \\ref identity \"tmpl::identity\" for similar functionality that\nis intended for use as a metafunction.\n\n\n\\subsection Constants\n\n\\par\nBrigand defines a few concrete types and type aliases.\n\n\n\\subsubsection empty_base\n\n\\par\nAn empty struct used by \\ref inherit \"tmpl::inherit\" and \\ref inherit_linearly\n\"tmpl::inherit_linearly\".  Primarily for internal use.\n\\snippet Test_TMPLDocumentation.cpp tmpl::empty_base\n\n\n\\subsubsection empty_sequence\n\n\\par\nAn empty \\ref list \"tmpl::list\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::empty_sequence\n\n\\remark\nPrefer just writing `tmpl::list<>`.\n\n\n\\subsubsection false_type\n\n\\par\nA \\ref integral_constant \"tmpl::integral_constant\" representing `false`.\nSimilar to std::false_type.\n\\snippet Test_TMPLDocumentation.cpp tmpl::false_type\n\n\\remark\nPrefer std::false_type.\n\n\n\\subsubsection no_such_type_\n\n\\par\nAn empty struct returned as the failure case for various searching operations.\n\\snippet Test_TMPLDocumentation.cpp tmpl::no_such_type_\n\n\n\\subsubsection true_type\n\n\\par\nA \\ref integral_constant \"tmpl::integral_constant\" representing `true`.\nSimilar to std::true_type.\n\\snippet Test_TMPLDocumentation.cpp tmpl::true_type\n\n\\remark\nPrefer std::true_type.\n\n\n\\subsection list_constructor Constructor-like functions for lists\n\n\\par\nThese functions produce \\ref list \"tmpl::list\"s from non-list values.  They are\noften similar to constructors in the STL.\n\n\n\\subsubsection filled_list filled_list<Entry, n, [Head]>\n\n\\par\nCreates a list containing `n` (passed as an `unsigned int`) of `Entry`.  The\nhead of the list defaults to \\ref list \"tmpl::list\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::filled_list\n\n\n\\subsubsection integral_list integral_list<T, n...>\n\n\\par\nShorthand for a \\ref list \"tmpl::list\" of \\ref integral_constant\n\"tmpl::integral_constant\"s of the type `T` with values `n...`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::integral_list\n\n\\remark\nPrefer std::integer_sequence when used for pack expansion.  Prefer\n`tmpl::integral_list` when the contents need to be manipulated for more\ncomplicated metaprogramming.\n\n\n\\subsubsection make_sequence make_sequence<Start, n, [Next], [Head]>\n\n\\par\nProduces a list with first element `Start` and length `n` (provided as an\n`unsigned int`).  The remaining elements are obtained by repeated applications\nof the \\ref metalambdas \"metalambda\" `Next`, defaulting to \\ref next\n\"tmpl::next\".  The head of the sequence can be specified, and defaults to \\ref\nlist \"tmpl::list\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::make_sequence\n\n\\see \\ref range \"tmpl::range\", \\ref repeat \"tmpl::repeat\"\n\n\n\\subsubsection range range<T, start, stop>\n\n\\par\nProduces a \\ref list \"tmpl::list\" of \\ref integral_constant\n\"tmpl::integral_constant\"s of type `T` representing adjacent ascending integers\nfrom `start` to `stop`, including the starting value and excluding the ending\nvalue.\n\\snippet Test_TMPLDocumentation.cpp tmpl::range\n\n\\see \\ref reverse_range \"tmpl::reverse_range\"\n\n\n\\subsubsection reverse_range reverse_range<T, start, stop>\n\n\\par\nProduces a \\ref list \"tmpl::list\" of \\ref integral_constant\n\"tmpl::integral_constant\"s of type `T` representing adjacent descending\nintegers from `start` to `stop`, including the starting value and excluding the\nending value.\n\\snippet Test_TMPLDocumentation.cpp tmpl::reverse_range\n\n\\see \\ref range \"tmpl::range\"\n\n\n\\subsection list_query Functions for querying lists\n\n\\par\nThese tend to be similar to const member functions in the STL and the\nnon-modifying sequence operations in `<algorithm>`.  They are most frequently\nused with \\ref list \"tmpl::list\", but similar classes will also work.\n\n\n\\subsubsection all all<Sequence, [Predicate]>\n\n\\par\nChecks if `Predicate` is true for all elements of `Sequence`.  The default\npredicate checks that the element's `value` is not equal to zero.\n\\snippet Test_TMPLDocumentation.cpp tmpl::all\n\n\\note\nThe predicate must return the same true value for each element for `all` to\nreturn true. This behaviour may differ by compiler, so it can't be relied upon.\n\\snippet Test_TMPLDocumentation.cpp tmpl::all:inhomogeneous\n\n\\see \\ref any \"tmpl::any\", \\ref none \"tmpl::none\"\n\n\n\\subsubsection any any<Sequence, [Predicate]>\n\n\\par\nChecks if `Predicate` is true for at least one element of `Sequence`.  The\ndefault predicate checks that the element's `value` is not equal to zero.\n\\snippet Test_TMPLDocumentation.cpp tmpl::any\n\n\\remark\nThe \\ref any \"tmpl::any\" and \\ref none \"tmpl::none\" metafunctions perform the\nsame tasks as \\ref found \"tmpl::found\" and \\ref not_found \"tmpl::not_found\",\nbut use different algorithms.  In general, \\ref any \"tmpl::any\" and \\ref none\n\"tmpl::none\" are much faster, but \\ref found \"tmpl::found\" and \\ref not_found\n\"tmpl::not_found\" short-circuit, so they may be preferable with short lists and\nexpensive predicates.\n\n\\note\nThe predicate must return the same false value for each element for `any` to\nreturn false. This behaviour may differ by compiler, so it can't be relied upon.\n\\snippet Test_TMPLDocumentation.cpp tmpl::any:inhomogeneous\n\n\\see \\ref all \"tmpl::all\", \\ref found \"tmpl::found\", \\ref none \"tmpl::none\"\n\n\n\\subsubsection at at<Sequence, Index>\n\n\\par\nRetrieves a given element of `Sequence`, similar to `operator[]` of the STL\ncontainers.  The `Index` is supplied as a \\ref integral_constant\n\"tmpl::integral_constant\" or similar type.\n\\snippet Test_TMPLDocumentation.cpp tmpl::at\n\n\\par\nThis operator is \\ref map_at \"overloaded for maps\".\n\n\\see \\ref at_c \"tmpl::at_c\"\n\n\n\\subsubsection at_c at_c<Sequence, n>\n\n\\par\nRetrieves a given element of `Sequence`, similar to `operator[]` of the STL\ncontainers.  The index `n` is supplied as an `unsigned int`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::at_c\n\n\\see \\ref at \"tmpl::at\"\n\n\n\\subsubsection back back<Sequence>\n\n\\par\nRetrieves the last element of `Sequence`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::back\n\n\n\\subsubsection count_if count_if<Sequence, Predicate>\n\n\\par\nReturns the number of elements of `Sequence` satisfying `Predicate`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::count_if\n\n\n\\subsubsection fold fold<Sequence, State, Functor>\n\n\\par\nPerforms a [left\nfold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)), i.e., given\na list `Sequence`, initial state `State`, and \\ref metalambdas \"metalambda\"\n`Functor`, updates the state by calling `Functor` on the state and the first\nelement of `Sequence`, repeats with the second, and so on, returning the final\nstate.\n\\snippet Test_TMPLDocumentation.cpp tmpl::fold\n\n\\par\nBrigand provides `tmpl::_state` and `tmpl::_element` aliases to the appropriate\n\\ref args \"arguments\" for use in folds.\n\n\\see \\ref reverse_fold \"tmpl::reverse_fold\"\n\n\n\\subsubsection found found<Sequence, [Predicate]>\n\n\\par\nReturns, as a \\ref integral_constant \"tmpl::integral_constant\" of `bool`,\nwhether `Predicate` matches any element of `Sequence`.  The default predicate\nchecks that the element's `value` is not equal to zero.\n\\snippet Test_TMPLDocumentation.cpp tmpl::found\n\n\\remark\nThis function performs the same operation as \\ref any \"tmpl::any\".  See \\ref\nany \"tmpl::any\" for discussion.\n\n\\see \\ref any \"tmpl::any\", \\ref find \"tmpl::find\", \\ref not_found\n\"tmpl::not_found\"\n\n\n\\subsubsection front front<Sequence>\n\n\\par\nRetrieves the first element of `Sequence`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::front\n\n\n\\subsubsection index_if<Sequence, Predicate, [NotFound]>\n\n\\par\nFinds the index as a `size_t` \\ref integral_constant \"tmpl::integral_constant\"\nof the first type in `Sequence` satisfying `Predicate`.  Returns `NotFound`,\ndefaulting to \\ref no_such_type_ \"tmpl::no_such_type_\" if no elements match.\n\\snippet Test_TMPLDocumentation.cpp tmpl::index_if\n\n\n\\subsubsection index_of index_of<Sequence, T>\n\n\\par\nFinds the index as a `size_t` \\ref integral_constant \"tmpl::integral_constant\"\nof the first occurrence of `T` in `Sequence`.  Returns \\ref no_such_type_\n\"tmpl::no_such_type_\" if the type is not found.\n\\snippet Test_TMPLDocumentation.cpp tmpl::index_of\n\n\n\\subsubsection list_contains list_contains<Sequence, T>\n\n\\par\nChecks whether `T` is contained in `Sequence`, returning a \\ref\nintegral_constant \"tmpl::integral_constant\" of `bool`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::list_contains\n\n\\note\nThis is not a Brigand metafunction.  It is implemented in SpECTRE.\n\n\n\\subsubsection none none<Sequence, [Predicate]>\n\n\\par\nChecks if `Predicate` is false for all elements of `Sequence`.  The default\npredicate checks that the element's `value` is not equal to zero.\n\\snippet Test_TMPLDocumentation.cpp tmpl::none\n\n\\remark\nThis function performs the same operation as \\ref not_found \"tmpl::not_found\".\nSee \\ref any \"tmpl::any\" for discussion.\n\n\\note\nThe predicate must return the same false value for each element for `none` to\nreturn true. This behaviour may differ by compiler, so it can't be relied upon.\n\\snippet Test_TMPLDocumentation.cpp tmpl::none:inhomogeneous\n\n\\see \\ref all \"tmpl::all\", \\ref any \"tmpl::any\", \\ref not_found\n\"tmpl::not_found\"\n\n\n\\subsubsection not_found not_found<Sequence, [Predicate]>\n\n\\par\nReturns, as a \\ref integral_constant \"tmpl::integral_constant\" of `bool`,\nwhether `Predicate` matches no elements of `Sequence`.  The default predicate\nchecks that the element's `value` is not equal to zero.\n\\snippet Test_TMPLDocumentation.cpp tmpl::not_found\n\n\\remark\nThis function performs the same operation as \\ref none \"tmpl::none\".  See \\ref\nany \"tmpl::any\" for discussion.\n\n\\see \\ref find \"tmpl::find\", \\ref found \"tmpl::found\", \\ref none \"tmpl::none\"\n\n\n\\subsubsection size size<Sequence>\n\n\\par\nReturns the number of elements in `Sequence` as a \\ref integral_constant\n\"tmpl::integral_constant\" of type `unsigned int`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::size\n\n\\see \\ref count \"tmpl::count\"\n\n\n\\subsection list_to_list Functions producing lists from other lists\n\n\\par\nThese tend to be similar to non-const member functions in the STL and the\nmutating sequence operations in `<algorithm>`, but due to the nature of\nmetaprogramming all return a new list rather than modifying an argument.  They\nare most frequently used with \\ref list \"tmpl::list\", but similar classes will\nalso work.\n\n\n\\subsubsection append append<Sequence...>\n\n\\par\nConcatenates all of its arguments, keeping the head of the first (or \\ref list\n\"tmpl::list\" if passed no arguments).\n\\snippet Test_TMPLDocumentation.cpp tmpl::append\n\n\\see \\ref join \"tmpl::join\"\n\n\n\\subsubsection clear clear<Sequence>\n\n\\par\nProduces a list with the same head as `Sequence` but no elements.\n\\snippet Test_TMPLDocumentation.cpp tmpl::clear\n\n\\remark\nIf the head is known, prefer writing it explicitly.  If the head is irrelevant,\nwrite an empty \\ref list \"tmpl::list\".\n\n\n\\subsubsection erase erase<Sequence, Index>\n\n\\par\nProduces a copy of `Sequence` with the element at index `Index` (passed as a\n\\ref integral_constant \"tmpl::integral_constant\" or similar type) removed.\n\\snippet Test_TMPLDocumentation.cpp tmpl::erase\n\n\\par\nThis operator is overloaded \\ref map_erase \"for maps\" and \\ref set_erase\n\"for sets\".\n\n\\see \\ref erase_c\n\n\n\\subsubsection erase_c erase_c<Sequence, n>\n\n\\par\nProduces a copy of `Sequence` with the element at index `n` (passed as an\n`unsigned int`) removed.\n\\snippet Test_TMPLDocumentation.cpp tmpl::erase_c\n\n\n\\subsubsection filter filter<Sequence, Predicate>\n\n\\par\nRemoves all types not matching `Predicate` from `Sequence`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::filter\n\n\\see \\ref remove_if \"tmpl::remove_if\"\n\n\n\\subsubsection find find<Sequence, [Predicate]>\n\n\\par\nReturns a list containing the first element of `Sequence` for which `Predicate`\nreturns true and all subsequent elements.  The default predicate checks that\nthe element's `value` is not equal to zero.  Returns an empty list if the\npredicate returns false for all elements.\n\\snippet Test_TMPLDocumentation.cpp tmpl::find\n\n\\see \\ref found \"tmpl::found\", \\ref not_found \"tmpl::not_found\", \\ref\nreverse_find \"tmpl::reverse_find\"\n\n\n\\subsubsection flatten flatten<Sequence>\n\n\\par\nRecursively inlines the contents of elements of `Sequence` that are sequences\nwith the same head.\n\\snippet Test_TMPLDocumentation.cpp tmpl::flatten\n\n\\see \\ref join \"tmpl::join\"\n\n\n\\subsubsection join join<Sequence>\n\n\\par\nCombines lists in the same manner as \\ref append \"tmpl::append\", but takes a\nlist of lists instead of multiple arguments.\n\\snippet Test_TMPLDocumentation.cpp tmpl::join\n\n\n\\subsubsection list_difference list_difference<Sequence1, Sequence2>\n\n\\par\nRemove all elements that occur in `Sequence2` from `Sequence1`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::list_difference\n\n\\note\nThis is not a Brigand metafunction.  It is implemented in SpECTRE.\n\n\n\\subsubsection merge merge<Sequence1, Sequence2, [Comparator]>\n\n\\par\nGiven two sorted lists, returns a sorted list containing the elements of both.\nA comparator metalambda can be provided, defaulting to \\ref math_comparison\n\"tmpl::less\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::merge\n\n\\note\nIf there are equivalent elements, those from the second list are placed\nearlier.\n\\snippet Test_TMPLDocumentation.cpp tmpl::merge:equiv\n\n\\see std::merge\n\n\n\\subsubsection partition partition<Sequence, Predicate>\n\n\\par\nReturns a \\ref pair \"tmpl::pair\" containing a list of the elements of\n`Sequence` for which the `Predicate` returns true and a list of the elements of\n`Sequence` for which the `Predicate` returns false.\n\\snippet Test_TMPLDocumentation.cpp tmpl::partition\n\n\\see \\ref filter \"tmpl::filter\", \\ref remove_if \"tmpl::remove_if\"\n\n\n\\subsubsection pop_back pop_back<Sequence, [Count]>\n\n\\par\nRemove `Count` elements from the end of `Sequence`.  The number of elements to\nremove is supplied as a \\ref integral_constant \"tmpl::integral_constant\" and\ndefaults to 1.\n\\snippet Test_TMPLDocumentation.cpp tmpl::pop_back\n\n\n\\subsubsection pop_front pop_front<Sequence, [Count]>\n\n\\par\nRemove `Count` elements from the beginning of `Sequence`.  The number of\nelements to remove is supplied as a \\ref integral_constant\n\"tmpl::integral_constant\" and defaults to 1.\n\\snippet Test_TMPLDocumentation.cpp tmpl::pop_front\n\n\n\\subsubsection push_back push_back<Sequence, T...>\n\n\\par\nAppends types `T...` to `Sequence`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::push_back\n\n\n\\subsubsection push_front push_front<Sequence, T...>\n\n\\par\nPrepends types `T...` to `Sequence`.  The order of the prepended items is\nretained: they are pushed as a unit, not one-by-one.\n\\snippet Test_TMPLDocumentation.cpp tmpl::push_front\n\n\n\\subsubsection remove remove<Sequence, T>\n\n\\par\nRemoves all occurrences of `T` from `Sequence`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::remove\n\n\n\\subsubsection remove_duplicates remove_duplicates<Sequence>\n\n\\par\nRemove duplicates from `Sequence`.  The first occurrence of each type is kept.\n\\snippet Test_TMPLDocumentation.cpp tmpl::remove_duplicates\n\n\\note\nThis is not a Brigand metafunction.  It is implemented in SpECTRE.\n\n\n\\subsubsection remove_if remove_if<Sequence, Predicate>\n\n\\par\nRemoves all types matching `Predicate` from `Sequence`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::remove_if\n\n\\see \\ref filter \"tmpl::filter\"\n\n\n\\subsubsection replace replace<Sequence, Old, New>\n\n\\par\nReplaces all occurrences of `Old` in `Sequence` with `New`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::replace\n\n\n\\subsubsection replace_if replace_if<Sequence, Predicate, T>\n\n\\par\nReplaces all types in `Sequence` matching `Predicate` with `T`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::replace_if\n\n\n\\subsubsection reverse reverse<Sequence>\n\n\\par\nReverses the order of types in `Sequence`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::reverse\n\n\n\\subsubsection reverse_find reverse_find<Sequence, [Predicate]>\n\n\\par\nReturns a list containing the last element of `Sequence` for which `Predicate`\nreturns true and all preceding elements.  The default predicate checks that the\nelement's `value` is not equal to zero.  Returns an empty list if the predicate\nreturns false for all elements.\n\\snippet Test_TMPLDocumentation.cpp tmpl::reverse_find\n\n\\see \\ref find \"tmpl::find\"\n\n\n\\subsubsection reverse_fold reverse_fold<Sequence, State, Functor>\n\n\\par\nPerforms a [right\nfold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)), i.e., given\na list `Sequence`, initial state `State`, and \\ref metalambdas \"metalambda\"\n`Functor`, updates the state by calling `Functor` on the state and the last\nelement of `Sequence`, repeats with the second to last, and so on, returning\nthe final state.\n\\snippet Test_TMPLDocumentation.cpp tmpl::reverse_fold\n\n\\par\nBrigand provides `tmpl::_state` and `tmpl::_element` aliases to the appropriate\n\\ref args \"arguments\" for use in folds.\n\n\\see \\ref fold \"tmpl::fold\"\n\n\n\\subsubsection sort sort<Sequence, [Comparator]>\n\n\\par\nSorts `Sequence` according to `Comparator`, which defaults to \\ref\nmath_comparison \"tmpl::less\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::sort\n\n\\note\nThe sort is not stable.\n\\snippet Test_TMPLDocumentation.cpp tmpl::sort:equiv\n\n\n\\subsubsection split split<Sequence, Delimiter>\n\n\\par\nSplits `Sequence` into parts separated by `Delimiter`, discarding empty parts.\n\\snippet Test_TMPLDocumentation.cpp tmpl::split\n\n\n\\subsubsection split_at split_at<Sequence, Index>\n\n\\par\nReturns a list of two of lists, the first containing the first `Index`\n(supplied as a \\ref integral_constant \"tmpl::integral_constant\") elements or\n`Sequence`, and the second containing the remaining elements.\n\\snippet Test_TMPLDocumentation.cpp tmpl::split_at\n\n\n\\subsubsection transform transform<Sequence, Sequences..., Functor>\n\n\\par\nCalls a `Functor` on each element of `Sequence`, collecting the results in a\nnew list.  If additional `Sequences...` are supplied, elements from those lists\nare passed as additional arguments to `Functor`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::transform\n\n\n\\subsection map_operations Operations on maps\n\n\\par\nBrigand's \\ref map \"tmpl::map\" type can be manipulated by several\nmetafunctions.\n\n\\par\nExamples in this section use this map as an example:\n\\snippet Test_TMPLDocumentation.cpp example_map\n\n\n\\subsubsection map_at at<Map, Key>\n\n\\par\nReturns the value associated with `Key` in `Map`.  Returns \\ref\nno_such_type_ \"tmpl::no_such_type_\" if `Key` is not in the map.\n\\snippet Test_TMPLDocumentation.cpp tmpl::at:map\n\n\\par\nThis operator is \\ref at \"overloaded for lists\".  When called on a \\ref map\n\"tmpl::map\", this is the same as \\ref lookup \"tmpl::lookup\".\n\n\n\\subsubsection map_erase erase<Map, Key>\n\n\\par\nProduces a copy of `Map` with the element with the key `Key` removed.  If `Key`\nis not in the map, returns `Map` unchanged.\n\\snippet Test_TMPLDocumentation.cpp tmpl::erase:map\n\n\\par\nThis operator is overloaded \\ref erase \"for lists\" and \\ref set_erase\n\"for sets\".\n\n\n\\subsubsection map_has_key has_key<Map, Key>\n\n\\par\nReturns a \\ref integral_constant \"tmpl::integral_constant\" of `bool`\nindicating whether `Map` contains the key `Key`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::has_key:map\n\n\\par\nThis operator is \\ref set_has_key \"overloaded for sets\".\n\n\n\\subsubsection map_insert insert<Map, Pair>\n\n\\par\nReturns `Map` with `Pair` added.  If the key of `Pair` is already in the map,\nthe map is returned unchanged.\n\\snippet Test_TMPLDocumentation.cpp tmpl::insert:map\n\n\\par\nThis operator is \\ref set_insert \"overloaded for sets\".\n\n\n\\subsubsection keys_as_sequence keys_as_sequence<Map, [Head]>\n\n\\par\nReturns the keys from `Map` as a sequence with head `Head`, defaulting to \\ref\nset \"tmpl::set\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::keys_as_sequence\n\n\\par\nIf the key-value pairs are required, they can be extracted directly from the\ntemplate arguments of the \\ref map \"tmpl::map\".\n\n\\see \\ref values_as_sequence \"tmpl::values_as_sequence\"\n\n\n\\subsubsection lookup lookup<Map, Key>\n\n\\par\nReturns the value associated with `Key` in `Map`.  Returns \\ref no_such_type_\n\"tmpl::no_such_type_\" if `Key` is not in the map.\n\\snippet Test_TMPLDocumentation.cpp tmpl::lookup\n\n\\see \\ref map_at \"tmpl::at\"\n\n\n\\subsubsection lookup_at lookup_at<Map, Key>\n\n\\par\nReturns the value associated with `Key` in `Map`, wrapped in a \\ref type_\n\"tmpl::type_\".  Returns `type_<no_such_type_>` if `Key` is not in the map.\nThis function has no eager version, but is still in the `tmpl::lazy` namespace.\n\\snippet Test_TMPLDocumentation.cpp tmpl::lookup_at\n\n\\see \\ref lookup \"tmpl::lookup\"\n\n\n\\subsubsection values_as_sequence values_as_sequence<Map, [Head]>\n\n\\par\nReturns the values from `Map` as a sequence with head `Head`, defaulting to\n\\ref list \"tmpl::list\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::values_as_sequence\n\n\\par\nIf the key-value pairs are required, they can be extracted directly from the\ntemplate arguments of the \\ref map \"tmpl::map\".\n\n\\see \\ref keys_as_sequence \"tmpl::keys_as_sequence\"\n\n\n\\subsection set_operations Operations on sets\n\n\\par\nBrigand's \\ref set \"tmpl::set\" type can be manipulated by several\nmetafunctions.\n\n\n\\subsubsection contains contains<Set, T>\n\n\\par\nReturns a \\ref integral_constant \"tmpl::integral_constant\" of `bool`\nindicating whether `Set` contains `T`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::contains\n\n\n\\subsubsection set_erase erase<Set, T>\n\n\\par\nProduces a copy of `Set` with the element `T` removed.  If the element is not\nin the set, returns the set unchanged.\n\\snippet Test_TMPLDocumentation.cpp tmpl::erase:set\n\n\\par\nThis operator is overloaded \\ref erase \"for lists\" and \\ref map_erase\n\"for maps\".\n\n\n\\subsubsection set_has_key has_key<Set, T>\n\n\\par\nReturns a \\ref integral_constant \"tmpl::integral_constant\" of `bool`\nindicating whether `Set` contains `T`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::has_key:set\n\n\\par\nThis operator is \\ref map_has_key \"overloaded for maps\".\n\n\n\\subsubsection set_insert insert<Set, T>\n\n\\par\nReturns a copy of `Set` containing an additional element `T`.  If `T` is\nalready in the set, the set is returned unchanged.\n\\snippet Test_TMPLDocumentation.cpp tmpl::insert:set\n\n\\par\nThis operator is \\ref map_insert \"overloaded for maps\".\n\n\n\\subsection math Mathematical functions\n\n\\par\nThese perform the same operations at their language counterparts, but on \\ref\nintegral_constant \"tmpl::integral_constant\"s (or anything else with a `value`\nstatic member type of type `value_type`).  The results inherit from \\ref\nintegral_constant \"tmpl::integral_constant\"s of types noted below.\n\n\\par\nThese are all lazy metafunctions.\n\n\n\\subsubsection math_arithmetic Arithmetic operators\n\n\\par\nThese operations return classes inheriting from \\ref integral_constant\n\"tmpl::integral_constant\"s of the same type as the result of the language\noperator on their arguments.  The integral promotion and conversion rules are\napplied.  (Contrast the \\ref math_bitwise \"bitwise operators\".)\n\\snippet Test_TMPLDocumentation.cpp math_arithmetic\n\n\\par\nThe standard library runtime functors have the same names for std::plus,\nstd::minus, std::divides, and std::negate, but the other two are\nstd::multiplies and std::modulus.\n\n\n\\subsubsection math_bitwise Bitwise operators\n\n\\par\nThese operations return classes inheriting from \\ref integral_constant\n\"tmpl::integral_constant\"s of the same type as their first argument's `value`.\nThis is *not* generally the same type as the language operator, even when the\ntypes of the values of both arguments are the same.  (The integer promotion and\nconversion rules are not applied.)\n\\snippet Test_TMPLDocumentation.cpp math_bitwise\n\n\\par\nThe standard library runtime functors are called std::bit_not, std::bit_and,\nstd::bit_or, and std::bit_xor.\n\n\n\\subsubsection math_comparison Comparison operators\n\n\\par\nThese operations return classes inheriting from \\ref integral_constant\n\"tmpl::integral_constant\"s of `bool`.\n\\snippet Test_TMPLDocumentation.cpp math_comparison\n\n\\par\nThe standard library runtime functors have the same names, such as\nstd::equal_to.\n\n\n\\subsubsection math_logical Logical operators\n\n\\par\nThese operations return classes inheriting from \\ref integral_constant\n\"tmpl::integral_constant\"s of `bool`.  They should only be used on types\nwrapping `bool`s.  The `and_` and `or_` structs can take any number of\narguments.\n\\snippet Test_TMPLDocumentation.cpp math_logical\n\n\\par\nThe standard library runtime functors are called std::logical_and,\nstd::logical_or, and std::logical_not.  The xor operation is equivalent to \\ref\nmath_comparison \"tmpl::not_equal_to\".\n\n\n\\subsubsection identity identity<T>\n\n\\par\nThe identity function.  Unlike most math functions, this returns the same type\nas its argument, even if that is not a \\ref integral_constant\n\"tmpl::integral_constant\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::identity\n\n\\see \\ref always \"tmpl::always\"\n\n\n\\subsubsection max max<T1, T2>\n\n\\par\nComputes the larger of `T1` and `T2`, returning a \\ref integral_constant\n\"tmpl::integral_constant\" of the common type of its arguments.\n\\snippet Test_TMPLDocumentation.cpp tmpl::max\n\n\n\\subsubsection min min<T1, T2>\n\n\\par\nComputes the smaller of `T1` and `T2`, returning a \\ref integral_constant\n\"tmpl::integral_constant\" of the common type of its arguments.\n\\snippet Test_TMPLDocumentation.cpp tmpl::min\n\n\n\\subsubsection next next<T>\n\n\\par\nComputes `T` plus one, returning a \\ref integral_constant\n\"tmpl::integral_constant\" of the same type as its argument.\n\\snippet Test_TMPLDocumentation.cpp tmpl::next\n\n\n\\subsubsection prev prev<T>\n\n\\par\nComputes `T` minus one, returning a \\ref integral_constant\n\"tmpl::integral_constant\" of the same type as its argument.\n\\snippet Test_TMPLDocumentation.cpp tmpl::prev\n\n\n\\subsection misc Miscellaneous functions\n\n\\par\nFunctions that don't fit into any of the other sections.\n\n\n\\subsubsection always always<T>\n\n\\par\nA lazy identity function.\n\\snippet Test_TMPLDocumentation.cpp tmpl::always\n\n\\see \\ref identity \"tmpl::identity\"\n\n\n\\subsubsection apply apply<Lambda, [Arguments...]>\n\n\\par\nCalls a \\ref metalambdas \"metalambda\" `Lambda` with arguments `Arguments...`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::apply\n\n\n\\subsubsection count count<T...>\n\n\\par\nReturns the number of template parameters provided as a \\ref integral_constant\n\"tmpl::integral_constant\" of `unsigned int`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::count\n\n\n\\subsubsection conditional_t conditional_t<b, TrueResult, FalseResult>\n\n\\par\nReturns `TrueResult` if the `bool` `b` is true, otherwise `FalseResult`.  An\noptimized version of std::conditional_t.\n\\snippet Test_TMPLDocumentation.cpp tmpl::conditional_t\n\n\\note\nThis is not a Brigand metafunction.  It is implemented in SpECTRE.\n\n\n\\subsubsection eval_if eval_if<Condition, TrueFunction, FalseFunction>\n\n\\par\nA lazy metafunction that, if `Condition` has a true `value`, evaluates and\nreturns the result of the lazy metafunction (*not* metalambda) `TrueFunction`,\notherwise, evaluates and returns the result of the lazy metafunction\n`FalseFunction`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::eval_if\n\n\\par\nThis performs lazy evaluation of conditional branches outside of a metalambda.\n\n\n\\subsubsection eval_if_c eval_if_c<b, TrueFunction, FalseFunction>\n\n\\par\nThe same as \\ref eval_if \"tmpl::eval_if\", but takes its first argument as a\n`bool` instead of a type.\n\\snippet Test_TMPLDocumentation.cpp tmpl::eval_if_c\n\n\n\\subsubsection has_type has_type<Ignored, [T]>\n\n\\par\nA lazy metafunction that returns `T` (defaulting to `void`), ignoring its first\nargument.\n\\snippet Test_TMPLDocumentation.cpp tmpl::has_type\n\n\\par\nThis can be used to expand a parameter pack to repetitions of the same type.\n\\snippet Test_TMPLDocumentation.cpp tmpl::has_type:pack_expansion\n\\snippet Test_TMPLDocumentation.cpp tmpl::has_type:pack_expansion:asserts\n\n\n\\subsubsection if_ if_<Condition, TrueResult, FalseResult>\n\n\\par\nA lazy metafunction that returns `TrueResult` if the `value` static member\nvalue of `Condition` is true, and otherwise `FalseResult`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::if_\n\n\\warning\nThe second and third arguments are both evaluated, independent of which is\nreturned.  Use \\ref defer \"tmpl::defer\" or \\ref eval_if \"tmpl::eval_if\" if this\nis undesirable.\n\n\n\\subsubsection if_c if_c<Condition, TrueResult, FalseResult>\n\n\\par\nThe same as std::conditional.\n\\snippet Test_TMPLDocumentation.cpp tmpl::if_c\n\n\n\\subsubsection inherit inherit<T...>\n\n\\par\nA lazy metafunction that produces a type with all of its template arguments as\nbase classes.  All the arguments must be unique.\n\\snippet Test_TMPLDocumentation.cpp tmpl::inherit\n\n\\remark\nThis task can be performed more simply than the algorithm used by Brigand by\ndirectly using pack expansions:\n\\snippet Test_TMPLDocumentation.cpp tmpl::inherit:pack:definitions\n\\snippet Test_TMPLDocumentation.cpp tmpl::inherit:pack:asserts\n\n\\note\nThe \\ref empty_base \"tmpl::empty_base\" type is used internally as a sentinel.\nThe result may or may not inherit from \\ref empty_base \"tmpl::empty_base\",\nindependently of whether it is supplied as an argument.\n\n\n\\subsubsection inherit_linearly inherit_linearly<Sequence, NodePattern, [Root]>\n\n\\par\nTransforms `Sequence` into a linked list.  The `NodePattern` must be a class\ntemplate (*not* a lazy metafunction) instantiated with metalambdas.  The\nfunction performs a [left\nfold](https://en.wikipedia.org/wiki/Fold_(higher-order_function)), with the\n`Root` (defaulting to \\ref empty_base \"tmpl::empty_base\") as the initial state\nand the transform function evaluating the arguments to the node pattern.\n\\snippet Test_TMPLDocumentation.cpp tmpl::inherit_linearly\n\n\\remark\nThis function handles its function-like argument differently from any other\nfunction in Brigand.  Prefer \\ref fold \"tmpl::fold\", which can perform the same\ntask and has a more standard interface.\n\n\n\\subsubsection is_set is_set<T...>\n\n\\par\nTests if all of its arguments are distinct, producing a \\ref integral_constant\n\"tmpl::integral_constant\" of `bool`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::is_set\n\n\\note\nThis is unrelated to the Brigand \\ref set \"tmpl::set\" type.\n\n\n\\subsubsection real_ real_<RealType, IntType, value>\n\n\\par\nRepresents a floating point number of type `RealType` at compile time via its\ninternal memory representation.  The value is stored as a \\ref\nintegral_constant \"tmpl::integral_constant\" of type `IntType` with value\n`value` (which must be the same size as `RealType`) and can be extracted at\nruntime using the conversion operator.  Brigand provides the aliases\n`single_<value>` and `double_<value>` with `RealType` and `IntType` set to\nappropriate values.\n\\snippet Test_TMPLDocumentation.cpp tmpl::real_\n\n\\par\nThere are no compile-time mathematical functions provided for floating point\ntypes.  They are opaque (or sometimes treated as integers) until runtime.\n\n\\remark\nConsider whether you really need to represent floating point values at compile\ntime.\n\n\\see std::ratio\n\n\n\\subsubsection repeat repeat<Function, Count, Initial>\n\n\\par\nCalls a unary eager metafunction `Function` on `Initial`, then on the result of\nthat, then on the result of that, and so on, up to `Count` calls.\n\\snippet Test_TMPLDocumentation.cpp tmpl::repeat\n\n\\note\nThis function has a lazy version, but it cannot be used in a metalambda because\nthe template template parameter prevents manipulation of the parameter list.\n\\snippet Test_TMPLDocumentation.cpp tmpl::repeat:lazy\n\n\\see \\ref make_sequence \"tmpl::make_sequence\"\n\n\n\\subsubsection sizeof_ sizeof_<T>\n\n\\par\nA lazy metafunction that computes `sizeof` its argument as an `unsigned int`\n\\ref integral_constant \"tmpl::integral_constant\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::sizeof_\n\n\n\\subsubsection substitute substitute<Pattern, ArgumentList>\n\n\\par\nSubstitutes values from `ArgumentList` for appearances of \\ref args\n\"tmpl::args\" (but *not* `tmpl::_1` or `tmpl::_2`) appearing in `Pattern`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::substitute\n\n\n\\subsubsection type_from type_from<T>\n\n\\par\nExtracts the `type` from `T`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::type_from\n\n\\remark\nThis function will work on any class with a `type` type alias, but, when used\noutside of a metafunction, it should only be used with \\ref type_ \"tmpl::type_\"\nfor clarity.\n\n\n\\subsubsection wrap wrap<Sequence, Head>\n\n\\par\nReplaces the head of `Sequence` with `Head`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::wrap\n\n\\note\nThis function has a lazy version, but it cannot be used in a metalambda because\nthe template template parameter prevents manipulation of the parameter list.\n\\snippet Test_TMPLDocumentation.cpp tmpl::wrap:lazy\n\n\n\\subsection runtime Runtime functionality\n\n\\par\nBrigand provides a few C++ functions that execute at runtime.\n\n\\par\nThe examples in this section use the following definition:\n\\snippet Test_TMPLDocumentation.cpp runtime_declarations\n\n\n\\subsubsection for_each_args for_each_args(functor, arguments...)\n\n\\par\nCalls `functor` on each of `arguments...`, in order.  Returns `functor`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::for_each_args:defs\n\\snippet Test_TMPLDocumentation.cpp tmpl::for_each_args\n\n\n\\subsubsection for_each for_each<Sequence>(functor)\n\n\\par\nCalls `functor` on \\ref type_ \"tmpl::type_\" objects wrapping each type in\n`Sequence`, in order.  Returns `functor`.\n\\snippet Test_TMPLDocumentation.cpp tmpl::for_each:defs\n\\snippet Test_TMPLDocumentation.cpp tmpl::for_each\n\n\\see \\ref type_from \"tmpl::type_from\"\n\n\n\\subsubsection select select<Condition>(true_result, false_result)\n\n\\par\nReturns `true_result` if `Condition`'s `value` member is true,\nand `false_result` if it is false.\n\\snippet Test_TMPLDocumentation.cpp tmpl::select\n\n\n\\subsection external External integration\n\n\\par\nBrigand provides metafunctions for interfacing with some types from the\nstandard library and Boost.  They usually come in pairs, with `as_X` taking a\nlist and `X_wrapper` taking a parameter pack.  This makes `X_wrapper`\nequivalent to the wrapped class.\n\n\\remark\nAvoid the `*_wrapper` functions in favor of using the class directly.\n\n\n\\subsubsection boost_integration Boost\n\n\\par\nBrigand provides functions to produce the `boost::fusion` types `deque`,\n`list`, `set`, and `vector`, as well as `boost::variant`. Because we use brigand\ninstead of boost::fusion and because `std::variant` replaces `boost::variant`,\nthese are not available in SpECTRE.\n\n\\subsubsection stl_integration STL\n\n\\par\nBrigand provides functions to produce the STL types std::pair and std::tuple.\nIn addition to the usual functions, Brigand provides `pair_wrapper_`, which is\na lazy form of `pair_wrapper`.  The pair functions all assert that they have\nreceived two types.\n\\snippet Test_TMPLDocumentation.cpp stl_integration\n\n\n\\subsubsection make_integral integral_constant\n\n\\par\nBrigand provides two functions for converting from std::integral_constant (or a\nsimilar class with a `value_type` and a `value`) to \\ref integral_constant\n\"tmpl::integral_constant\".  The lazy metafunction `make_integral` performs this\nconversion.  The `as_integral_list` eager metafunction performs this operation\non all elements of a list.\n\\snippet Test_TMPLDocumentation.cpp tmpl::make_integral\n\n\\warning\nThe standard library std::integer_sequence is not a list of types, and so\ncannot be used as input to `as_integral_list`.\n\n\n\\subsubsection as_list list\n\n\\par\nBrigand provides two metafunctions for converting types to Brigand sequences.\nThe more general function, `as_sequence`, is equivalent to \\ref wrap\n\"tmpl::wrap\".  The specialized version, `tmpl::as_list`, produces a \\ref list\n\"tmpl::list\".\n\\snippet Test_TMPLDocumentation.cpp tmpl::as_list\n\n\\remark\nUsing `as_list` is often not necessary because most metafunctions operate on\narbitrary template classes.\n\n\n\\subsubsection as_set set\n\n\\par\nBrigand provides the standard two metafunctions for converting types to Brigand\n\\ref set \"tmpl::set\"s.\n\\snippet Test_TMPLDocumentation.cpp tmpl::as_set\n\n\n\\section oddities Bugs/Oddities\n\n* \\ref push_front and \\ref pop_front have lazy versions, but \\ref push_back,\n  and \\ref pop_back do not.\n\n* \\ref reverse_range validates its arguments, but \\ref range does not.\n  (Probably because the former is called incorrectly more often.)\n\n* Brigand inconsistently uses `unsigned int` and `size_t` for size-related\n  things.  (Most blatantly, the result of \\ref sizeof_ is represented as an\n  `unsigned int`.)\n\n* Brigand has a file containing operator overloads for \\ref integral_constant\n  \"\"s, but it is not included by the main brigand header.  They work poorly,\n  mostly because it inexplicably puts them all in namespace std where the\n  compiler can't find them.\n"
  },
  {
    "path": "docs/DevGuide/BuildOptimization.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Build Profiling and Optimization {#build_profiling_and_optimization}\n\n\\tableofcontents\n\n# Why is our build so expensive?\n\nSpECTRE makes heavy use of compile-time logic, which is responsible for a lot of\nthe nice type-checking, performance, and flexibility the code has to offer.\nFor instance, our central data structure, the \\ref DataBoxGroup \"DataBox\", uses\na type list of several \"tags\" to determine its contents, as well as to\nautomatically propagate dependencies in compute items.\n\nThis use of compile-time logic, however, has the trade-off of making our builds\ntake longer and use more memory than a similar implementation in runtime logic\nwould.\nThere is good reason to believe that some of these costs are payed back at\nruntime, because many of our compile-time switches permit the final runtime code\nto be more efficient or avoid unnecessary computation.\nThere is certainly room for optimization, though, either in finding better\ncompile-time implementations of the algorithms, eliminating expensive template\ninstantiations, or moving inefficient parts to runtime.\nThis guide gives a quick outline of some of the methods that can be used to\nprofile the build and possible pitfalls\n\n# Understanding template expenses\n\nThe cost of compile-time template logic is a bit non-intuitive if you are used\nto thinking only of runtime performance.\nThe main reason is that the typical unit of 'cost' in a compile-time operation\nis the number of instantiated types and functions.\nRe-using a type that has been instantiated elsewhere (in the same translation\nunit) typically has a very low compile-time cost, where instantiating a type\nwith new template parameters will incur its own cost, plus any new types that it\nrequires in e.g. type aliases or member functions.\n\nConsider a Fibonacci calculation at compile-time:\n```\ntemplate <size_t N>\nstruct Fibonacci {\n  static constexpr size_t value =\n      Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;\n};\n\ntemplate <>\nstruct Fibonacci<1> {\n  static constexpr size_t value = 1;\n};\n\ntemplate <>\nstruct Fibonacci<0> {\n  static constexpr size_t value = 1;\n};\n```\nIf we were to write the same logic at runtime, the algorithm would be hopelessly\ninefficient; the recursive calls would cause each call to Fibonacci to make two\ncalls to the same function, resulting in an exponential time algorithm!\nHowever, the C++ language will only instantiate unique types, so only N types\nwill be created, giving a linear in compile-time operation.\n\n\nCompile-time lists and list operations frequently appear in SpECTRE, and should\nbe thought of differently from runtime list operations.\n\nIn compile-time lists, we have no access to true constant-time lookup, speedy\nalgorithms that rely on sorted structures, or more sophisticated data structures\n(balanced trees, hash tables, etc.).\nThe limitations of compile-time list techniques can cause list operations to be\nmore costly than we could achieve with runtime data structures.\n\nConsider a basic version of the compile-time list accessor:\n```\ntemplate <typename List, size_t Index>\nstruct list_at;\n\ntemplate <typename ListItem0, typename... ListItems>\nstruct list_at<tmpl::list<ListItem0, ListItems...>, 0> {\n  using type = ListItem0;\n};\n\ntemplate <typename ListItem0, typename... ListItems, size_t Index>\nstruct list_at<tmpl::list<ListItem0, ListItems...>, Index> {\n  using type = typename list_at<tmpl::list<ListItems...>, Index - 1>::type;\n};\n```\nNow, to access the Nth item in the list, we need to instantiate \\f$O(N)\\f$\ntypes.\nThe above implementation is significantly more costly than we would find in\npractice in template metaprogramming libraries -- in particular, our chosen\nTMPL backend, [Brigand](https://github.com/edouarda/brigand), manages the task\nin \\f$O(\\log(N))\\f$ (at least in type instantiation count).\n\nMost of the list operations in SpECTRE cannot take advantage of any particular\nordering or hashing of the list, so must resort to naive list operations --\nso, searching a list (`tmpl::list_contains` or `tmpl::index_of`) is\n\\f$O(N)\\f$ cost, `tmpl::remove_duplicates` is \\f$O(N^2)\\f$, and\n`tmpl::list_difference` is similarly \\f$O(N^2)\\f$.\nSo, complicated type logic scales pretty badly with long lists, and improvements\ncan sometimes be made by reducing a list's size or avoiding the more costly list\noperations when a list is known to be long.\n\n# Profiling the build\n\nIn the current version of SpECTRE, the most expensive builds are the final\ntranslation units associated with the executables (particularly the most\ncomplicated executables, like Generalized Harmonic and Valencia), which should\nbe unsurprising from the above discussion: it is in these compilation steps that\nwe instantiate the Parallel components, and in turn, all of the \\ref\nDataBoxGroup \"DataBox\" types that will be used during the evolution.\n\nSimilarly, a number of tests have now shown that in the current version of\nSpECTRE (as of early 2021), by far the most expensive part of the build is\n\\ref DataBoxGroup \"DataBox\" operations and instantiations, and the best build\nperformance gains are available by either reducing the number of\n\\ref DataBoxGroup \"DataBox\"es that are instantiated, reducing the number of tags\n(particularly compute tags) stored in the \\ref DataBoxGroup \"DataBox\", or\noptimizing the implementations of the \\ref DataBoxGroup \"DataBox\" and its\nutilities.\nSo, generally speaking, profiling should start by focusing on the\n\\ref DataBoxGroup \"DataBox\", and move to other utilities if it becomes clear\nthat there are other parts of the code that are contributing significantly to\nthe compilation time or memory usage.\n\n\n## Specialized tests and feature exploration\n\nThis is simultaneously the most reliable and most labor-intensive strategy for\nunderstanding build costs.\nThe procedure is to identify a feature you'd like to profile and create a\nspecialized test for that feature.\nThen, you can easily include or exclude uses of functions or classes that you\nwant to profile, and compare the relative total cost of building the test\nexecutable. You may want to temporarily remove all other source files from\nthe test executable.\n\nThere are a number of tools for profiling the cost of an individual process,\nbut for compilation, the detailed tools like `perf` or hpctoolkit are unlikely\nto give useful information about what parts of our code are slow to build.\nInstead, it's best to just carefully measure the full build of the target\nin question, and rapidly iterate to include or exclude potentially expensive\nparts to understand the build costs.\n\nThere are a lot of tools that can give you the global resource usage\ninformation, including the `/proc/$PID/status` file from kernel information,\n`top`, or tools from the `sysstat` package.\nThe `time` utility is particularly user-friendly, available on ubuntu (and\ntherefore in the SpECTRE build container) via `apt-get install time`.\nThen, it can be invoked by `/usr/bin/time -v your_command` (note that simply\n`time` will route to a different alias in the default environment on ubuntu in\nthe container, so the full path `/usr/bin/time` is required).\nAfter completion, it will print a readable report of the time and memory usage.\n\nOne important feature to be aware of in profiling the build by this method is\nthe implicit memoization of many compile-time features.\nFor instance, if feature `A` and `B` both instantiate a class `C` that is\nexpensive to build, you'll see a difference in the build cost when _either_ `A`\nor `B` are included, but the cost won't be additive - the second feature will\njust 'reuse' the instantiation from the first.\nTo optimize this type of situation, either `C` must be improved to be less\ncostly to instantiate, or its use must be eliminated from _both_ `A` and `B` --\nremoving `C` from only one of the classes that use it won't help the build much\nat all.\n\n## Templight++\n\nDetailed profiling of a C++ build is a surprisingly hard task, and there are few\nuseful tools for getting a good idea for what parts of a compilation are\nexpensive. One tool that can perform some profiling of the build is\n[templight++](https://github.com/mikael-s-persson/templight).\n_It is important to note that this tool often produces build profiles that are\nmisleading or incomplete!_ It is included in this guide under the philosophy\nthat a flawed tool can be better than no tool at all in some circumstances,\nbut the templight++ profiles should be taken primarily as a loose guide for\nfeatures to investigate with more careful follow-up investigations like the\nabove suggestion of specialized tests and feature exploration.\n\nThe `templight++` build and usage instructions work nicely with the current\nSpECTRE build system, and the cmake trick suggested by the `templight++`\ndocumentation\n```bash\nexport CC=\"/path/to/llvm/build/bin/templight -Xtemplight -profiler\\\n -Xtemplight -memory\"\nexport CXX=\"/path/to/llvm/build/bin/templight++ -Xtemplight -profiler\\\n -Xtemplight -memory\"\n```\nworks well in SpECTRE. Build profiling with `templight++` are incredibly slow,\nand seem to produce increasingly misleading data for larger builds, so it is\nrecommended to avoid using the tool for our most expensive evolution\nexecutables. Experience indicates that you will likely wait for hours and be\ndisappointed by deeply flawed results.\n\n\nAfter building a target, you will find along side each `.o` file in the\n`build/src` tree an additional file that ends with `.trace.pbf`.\nThese are the `templight++` trace files, and (like many performance tool\noutputs), require post-processing to recover human-readable data.\nThe companion package\n[templight-tools](https://github.com/mikael-s-persson/templight-tools)\ncan be built to obtain the `templight-convert` utility that converts\nthe templight traces to more managable formats.\nIt is recommended to install and use\n[KCacheGrind](https://kcachegrind.github.io/html/Home.html)\n(which does, unfortunately require some KDE libraries, but doesn't require you\nto use the full KDE window system) to visualize the output -- the larger graphs\nproduced by templight are inefficient to render in the graphviz format.\n\n## Clang profiling\n\nWith `clang >= 9`, you can generate a flame graph of what the compiler is\nspending its time on, including specific function instantiations, class\ninstantiations, source file parsing, debug info generation, and function\noptimization. To do so, add `-ftime-trace` to the `cmake` option\n`-D CMAKE_CXX_FLAGS` in your `cmake` command for your build. Then, you can\nsimply `make TargetName`. This will produce a `.json` for each `.cpp.o` that\nwas compiled in the directory that the object file is stored. Note that these\nobject and `.json` files are deep within the build directory. You can then load\nyour `.json` of interest into `chrome://tracing`. Because our code base utilizes\nmany templates and some compilation targets instantiate many types, classes, and\nfunctions, note that the flame graph for a particular compilation target may be\na lot to visually navigate, click through, and analyze at a high level. Because\nof this, the tool described below can be very useful and more manageable for\nsome analyses.\n\nThe companion tool to the flame graph generation is the\n[ClangBuildAnalyzer](https://github.com/aras-p/ClangBuildAnalyzer). This is an\nopen source tool that you can clone and run to give you profiling output\nsimilar to when you profile runtime code in that it identifies compilation hot\nspots for you. It will report things like \"Here are the templates/functions\nthat took the longest to instantiate/compile.\" Note that because many of our\ntypes can have very long names due to our templating, some class and function\nsignatures will not fit in the default character limit of what is printed to the\nterminal. You can adjust the maximum character length printed by creating a\n`ClangBuildAnalyzer.ini` file in your working directory as described in the\n[ClangBuildAnalyzer](https://github.com/aras-p/ClangBuildAnalyzer) `readme.md`.\n\n## Clang AST syntax generation\n\nThere is a nice and poorly documented feature of the `clang++` compiler that it\ncan produce a rough approximation of the collection of C++ template\ninstantiations produced by a particular executable.\nAdding `-Xclang -ast-print -fsyntax-only` to the `CXX_FLAGS` will cause this\ninformation to be printed to stdout, which should probably be redirected to file\nbecause it will be an enormous output.\nImportantly, to the best of our knowledge, this tool has not yet been used to\nsuccessfully profile any SpECTRE build, but with sufficient post-processing the\nC++-syntax version of the AST might be useful to determine the number and nature\nof instantiations produced by a particular piece of code, and might offer some\nproxy for build performance.\n\nFor instance, if we put the above `Fibonacci` struct in a source file with:\n```cpp\nint main(int argc, char** argv) {\n  std::cout << Fibonacci<6>::value << \"\\n\";\n}\n```\nand we compile it with\n```\nclang++-10 -Xclang -ast-print -fsyntax-only -o fib ./fib.cpp >> fib_out\n```\nwe obtain in `fib_out` (after thousands of lines of STL-generated code):\n```cpp\ntemplate <size_t N> struct Fibonacci {\n  static constexpr size_t value =\n      Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;\n};\ntemplate <> struct Fibonacci<6> {\n  static constexpr size_t value =\n      Fibonacci<6UL - 1>::value + Fibonacci<6UL - 2>::value;\n};\ntemplate <> struct Fibonacci<5> {\n  static constexpr size_t value =\n      Fibonacci<5UL - 1>::value + Fibonacci<5UL - 2>::value;\n};\ntemplate <> struct Fibonacci<4> {\n  static constexpr size_t value =\n      Fibonacci<4UL - 1>::value + Fibonacci<4UL - 2>::value;\n};\ntemplate <> struct Fibonacci<3> {\n  static constexpr size_t value =\n      Fibonacci<3UL - 1>::value + Fibonacci<3UL - 2>::value;\n};\ntemplate <> struct Fibonacci<2> {\n  static constexpr size_t value =\n      Fibonacci<2UL - 1>::value + Fibonacci<2UL - 2>::value;\n};\ntemplate <> struct Fibonacci<1> { static constexpr size_t value = 1; };\ntemplate <> struct Fibonacci<0> { static constexpr size_t value = 1; };\nint main(int argc, char **argv) { std::cout << Fibonacci<6>::value << \"\\n\"; }\n```\nWhich is actually pretty illuminating about what the compiler decided to do in\nthis simple case.\nUnfortunately, the AST produced by `clang++` in more complicated cases produces\nextremely large outputs, so realistic cases are likely too large to be usefully\nhuman-readable.\nIt may be possible, though, to script post-processing tools to sift through the\ncollections of template instantiations for particular classes to understand\nspecific cases of template logic.\n\n"
  },
  {
    "path": "docs/DevGuide/CodeReviewGuide.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Code Review Guide {#code_review_guide}\n\n\\tableofcontents\n\nCode must follow the\n<a href=\"https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md\">C++ Core Guidelines</a>\nand the [Google style guide](https://google.github.io/styleguide/cppguide.html).\nIf the Google style guide disagrees with the Core Guidelines, follow the Core\nGuidelines. Python code must also follow the\n[Google style guide](https://google.github.io/styleguide/pyguide.html).\n\nHere we summarize what we view as the more important portions of the guides.\n\nStylistic Items:\n\n* Adhere to [Google style](https://google.github.io/styleguide/cppguide.html).\n  [Can use `clang-format -style=google`][clang_format].\n* CamelCase: class names template parameters, file names, and directory names.\n* snake_case: function, variable, metafunction and metavariable names.\n* SCREAMING_SNAKE_CASE: macros.\n* Functions or classes only used internally in a library should be in a\n  namespace named `LibraryOrFile_detail`. For example, `databox_detail`,\n  `Tensor_detail` or `ConstantExpressions_detail`.\n* Name unused function parameters `/*parameter_name*/` or `/*meta*/` for TMP\n  cases\n* Type aliases that wrap type traits have a trailing `_t` following the STL\n* Private member variables have a [trailing underscore][variable_names].\n* Do not use\n  [Hungarian notation](https://en.wikipedia.org/wiki/Hungarian_notation),\n  e.g. `double* pd_blah` is bad\n* Header order:\n  1. (If a test:) `Framework/TestingFramework.hpp`, followed by one blank line\n  2. (If a cpp file with a corresponding hpp file:) hpp corresponding to cpp,\n     followed by one blank line\n  3. STL and externals (in alphabetical order)\n  4. Blank line\n  5. SpECTRE includes (in alphabetical order)\n* Template definitions in header files are separated from the declaration of\n  the class by the following line, which contains exactly 64 equal signs\n  ``` cpp\n  // ================================================================\n  ```\n\n* File lists in CMake are alphabetical.\n* No blank lines surrounding Doxygen group comments\n  (<code>// \\@{</code> and <code>// \\@}</code>).\n* Use the [alternative tokens][alternative_tokens] `or`, `and`, and `not`\n  instead of `||`, `&&`, and `!`.\n* Use C-style Doxygen comments (`/*! ... */`) when using multi-line math,\n  otherwise C-style and C++ style comments are accepted.\n* Use the `align` environment instead of `eqnarray`. See the\n  [texfaq](https://texfaq.org/FAQ-eqnarray) for an explanation as to why.\n* Multi-line equations must have a blank Doxygen line before and after the\n  equation.\n* When addressing requests on a PR, the commit message must start with\n  `fixup` followed by a descriptive message.\n* All python code must comply with the [black](https://github.com/psf/black)\n  formatting specified by the `pyproject.toml` file.\n* Imports in Python code must be sorted to comply to the\n  [isort](https://github.com/PyCQA/isort) formatting specified in the\n  `pyproject.toml` file.\n\nCode Quality Items:\n\n* All code passes Clang and CppCheck static analyzers. For help\n  with these tools see \\ref static_analysis_tools \"here\".\n* Almost always `auto`, except with expression templates, i.e. `DataVector`\n* All loops and if statements use braces.\n* Order of declaration in classes is `public` before `private` and member\n  functions before data. See\n  [Google](https://google.github.io/styleguide/cppguide.html#Declaration_Order).\n* Prefer return by value over pass-by-mutable-reference except when mutable\n  reference provides superior performance (in practice if you need a mutable\n  reference use `const gsl::not_null<Type*>` instead of `Type&`). The mutable\n  references must be the first arguments passed to the function.\n* All commits for performance changes provide quantitative evidence and the\n  tests used to obtain said evidence.\n* Never include `<iostream>`, use `Parallel::printf` inside\n  `Parallel/Printf/Printf.hpp` instead, which is safe to use in parallel.\n* When using charm++ nodelocks include `<converse.h>` instead of `<lrtslock.h>`.\n* Do not add anything to [the `std` namespace][extending_std].\n* Virtual functions are explicitly overridden using the `override` keyword.\n* `#%pragma once` is to be used for header guards\n* Prefer range-based for loops\n* Use `size_t` for positive integers rather than `int`, specifically when\n  looping over containers. This is in compliance with what the STL uses.\n* Error messages should be helpful. An example of a bad error message is \"Size\n  mismatch\". Instead this message could read \"The number of grid points in the\n  matrix 'F' is not the same as the number of grid points in the determinant.\",\n  along with the runtime values of the mentioned quantities if applicable.\n* Mark immutable objects as `const`\n* Make classes serializable by writing a `pup` function\n* If a class stores an object passed into a constructor the object should\n  be taken by-value and `std::move`d into the member variable.\n* Definitions of function and class templates should be in `.cpp` files with\n  explicit instantiations whenever possible. The macro\n  `GENERATE_EXPLICIT_INSTANTIATIONS` is useful for generating many\n  explicit instantiations.\n* Functions should not be marked `noexcept` unless it is part of the\n  signature of an overriden function (e.g. the what() function of an exception\n  derived from std::exception).\n* Variable names in macros must avoid name collisions, e.g. inside the\n  `PARSE_ERROR` macro you would write\n  `double variable_name_avoid_name_collisions_PARSE_ERROR = 0.0;`\n* Avoid macros if possible. Prefer `constexpr` functions, constexpr\n  variables, and/or template metaprogramming\n* Explicitly specify `double`s, e.g. `sqrt(2.)` or `sqrt(2.0)` instead of\n  `sqrt(2)`.\n* When the index of a `Tensor` is known at compile time, use\n  `get<a, b>(tensor)` instead of the member `get` function.\n* All necessary header files must be included. In header files, prefer\n  forward declarations if class definitions aren't necessary.\n* Explicitly make numbers floating point, e.g. `2.` or `2.0` over `2`.\n* Avoid `mutable` member variables. If you think you really need these, discuss\n  with at least 2 core developers. In a parallel environment `mutable` variables\n  lead to race conditions since multiple threads can mutate a single object by\n  calling a `const` member function.\n\n[alternative_tokens]:\n  http://en.cppreference.com/w/cpp/language/operator_alternative\n[clang_format]: http://clang.llvm.org/docs/ClangFormat.html\n[extending_std]: http://en.cppreference.com/w/cpp/language/extending_std\n[variable_names]:\n  https://google.github.io/styleguide/cppguide.html#Variable_Names\n"
  },
  {
    "path": "docs/DevGuide/CompilerLinkerErrors.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Understanding Compiler and Linker Errors {#compiler_and_linker_errors}\n\n\\tableofcontents\n\n# Linker Errors {#understanding_linker_errors}\n\nThere are a few common mistakes that can lead to linker problems, specifically\nproblems where there is an `undefined reference`. These include:\n- forgetting to add a `.cpp` file to the list of sources of a library in a\n  `CMakeLists.txt` file\n- missing an explicit instantiation of a function or class template in a `cpp`\n  file\n- not including a `tpp` file inside a `cpp` file\n- the template specialization or function overload has been explicitly disabled\n  via SFINAE (usually through the use of a `requires` clause)\n- not linking a library (explained below)\n\nPossibly the most difficult part of fixing linking errors is understanding what\nthey are trying to tell you. Let's take the following example\n```\nerror: undefined reference to 'Tensor<DataVector,\nbrigand::list<brigand::integral_constant<int, 1> >,\nbrigand::list<Tensor_detail::TensorIndexType<3ul, (UpLo)0, Frame::Inertial,\n(IndexType)0> > >\nrandom_unit_normal<DataVector>(gsl::not_null<std::mersenne_twister_engine<\nunsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul,\n2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul>*>, Tensor<DataVector,\nbrigand::list<brigand::integral_constant<int, 1>,\nbrigand::integral_constant<int, 1> >,\nbrigand::list<Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,\n(IndexType)0>, Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,\n(IndexType)0> > > const&)' in\nlib/libTest_GeneralizedHarmonic.a(Test_UpwindFlux.cpp.o):\nTest_UpwindFlux.cpp:function (anonymous namespace)::test_upwind_flux_random()\n```\nWe can start by splitting out information about different parts of the\nerror. First,\n```\nerror: undefined reference to\n```\ntells us that we forgot to add a link dependency for a library or\nexecutable. The next relevant part of information is which library (executable)\nand file the missing function/class was in. This is at the end of the error\nmessage (unfortunately, where in the error message can depend on your linker,\nthese examples used `ld.lld` v9):\n```\nlib/libTest_GeneralizedHarmonic.a(Test_UpwindFlux.cpp.o):\nTest_UpwindFlux.cpp:function (anonymous namespace)::test_upwind_flux_random()\n```\nWhat this means is that the missing link dependency is used in the library\n`Test_GeneralizedHarmonic`, the file `Test_UpwindFlux.cpp`, and the function\n`test_upwind_flux_random()`.\n\nWe have now determined what the linker error is (a missing link dependency), and\nin which library, file, and function the missing link dependency is used. We now\nneed to understand what the missing link dependency is. Since SpECTRE uses a lot\nof templates, the missing reference (link dependency) can be quite\nlong. In this case it is:\n```\nTensor<DataVector,\nbrigand::list<brigand::integral_constant<int, 1> >,\nbrigand::list<Tensor_detail::TensorIndexType<3ul, (UpLo)0, Frame::Inertial,\n(IndexType)0> > >\nrandom_unit_normal<DataVector>(gsl::not_null<std::mersenne_twister_engine<\nunsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul, 4294967295ul, 7ul,\n2636928640ul, 15ul, 4022730752ul, 18ul, 1812433253ul>*>, Tensor<DataVector,\nbrigand::list<brigand::integral_constant<int, 1>,\nbrigand::integral_constant<int, 1> >,\nbrigand::list<Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,\n(IndexType)0>, Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,\n(IndexType)0> > > const&)\n```\nIn order to make this easier to read, it is recommended to run the code through\nClangFormat. This can be done by copying the linker output into an empty `cpp`\nfile and running `clang-format -i EMPTY_FILE_WITH_LINKER_OUTPUT`. Keep in mind\nthat ClangFormat will only work if you have only copied the part of the linker\noutput that resembles valid C++. Doing so in this case gives:\n\\code{.cpp}\nTensor<DataVector, brigand::list<brigand::integral_constant<int, 1> >,\n       brigand::list<Tensor_detail::TensorIndexType<\n           3ul, (UpLo)0, Frame::Inertial, (IndexType)0> > >\nrandom_unit_normal<DataVector>(\n    gsl::not_null<std::mersenne_twister_engine<\n        unsigned long, 32ul, 624ul, 397ul, 31ul, 2567483615ul, 11ul,\n        4294967295ul, 7ul, 2636928640ul, 15ul, 4022730752ul, 18ul,\n        1812433253ul>*>,\n    Tensor<DataVector,\n           brigand::list<brigand::integral_constant<int, 1>,\n                         brigand::integral_constant<int, 1> >,\n           brigand::list<\n               Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,\n                                              (IndexType)0>,\n               Tensor_detail::TensorIndexType<3ul, (UpLo)1, Frame::Inertial,\n                                              (IndexType)0> > > const&);\n\\endcode\nWe see that it is the function `random_unit_normal` that isn't found. Now we can\nsearch in the code base where than function is defined. Doing a\n`git grep \"random_unit_normal\"` points to\n`tests/Unit/Helpers/DataStructures/RandomUnitNormal.?pp`. Looking\nat `tests/Unit/Helpers/DataStructures/RandomUnitNormal.hpp` we see that the\nfunction `random_unit_normal` is declared there, and looking in the\ncorresponding `cpp` we see `random_unit_normal` is instantiated in the source\nfile. Opening up `tests/Unit/Helpers/DataStructures/CMakeLists.txt` we see that\nthe library name is `DataStructuresHelpers`, and `RandomUnitNormal.cpp` is in\nthe list of sources for the library. Thus, linking\n`Test_GeneralizedHarmonic` against `DataStructuresHelpers` will resolve our\nerror. To link against a library, you must add it to the\n`target_link_libraries`.\n\nIf `random_unit_normal` had been defined in the header file, then the error\nwould've indicated that we did not include the header (or `tpp`) file into\n`Test_UpwindFlux.cpp`, and so the compiler could not generate an\ninstantiation.\n\nIn summary:\n- Identify target with undefined reference, i.e. the file included in a library\n  or executable.\n- Identify missing source definition (usually a function or static variable).\n- Find source declaration and definition in repository.\n- If source definition is in a `cpp` file, make sure it is in the list of\n  sources in the `CMakeLists.txt` in the same directory and that the\n  corresponding library is linked against by the target.\n\n  If the undefined reference is a template, make sure the required instantiation\n  exists.\n- If the undefined reference's definition is in a `tpp` file, make sure the\n  `tpp` file is included in the target file.\n- If the undefined reference's source definition is in an `hpp` file, make sure\n  the specific instantiation is possible (e.g. not forbidden by a `requires`\n  clause)\n"
  },
  {
    "path": "docs/DevGuide/Connectivity.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Visualisation Connectivity {#visualisation_connectivity}\n\n\\tableofcontents\n\nThis guide is split into two parts. The first part details what is meant by\n'connectivity' and how it is used to visualise SpECTRE simulations in standard\nvisualisation software such as Paraview. The second part details the\nmethodology associated with ExtendConnectivity - a post-processing executable\nthat removes gaps in the default visualisations produced by SpECTRE.\n\n# What is Connectivity?\n\nSpECTRE simulations are built on a computational grid composed of individual\ngridpoints. The 'connectivity' is a list added in the output simulation files\nthat instructs visualisation software, namely Paraview, on how to connect these\ngridpoints to create lines/surfaces/volumes (depending on the dimension of the\nsimulation) as needed. Given these instructions, Paraview interpolates the data\nover the regions between gridpoints to create line/surface/volume meshes.\n\nIn general, the connectivity list contains gridpoints in a particular sequence\nthat corresponds to a line/surface/volume mesh in Paraview. These gridpoints are\nlabelled with integers that are based on the order in which they appear in the\nh5 files output by the simulation. As one might expect, this list is structured\ndifferently depending on the dimension of the simulation. The key underlying\ndifference is the size of the smallest connected region. In 1D, our domain is\ncomposed of lines so the smallest connected region is a line made up by two\npoints. In 2D, the smallest connected region used by SpECTRE is typically a\nquadrilateral made up by four points. In 3D, the smallest connected region used\nby SpECTRE is typically a hexahedron made up by eight points. A visual\nrepresentation is illusutrated below. (Note: The numbering of points has been\nchosen arbitrarily. In reality, this numbering is determined based on the order\nof gridpoints in the h5 file(s)).\n\n\\image html ConnectivityStructure.png \"Basic connectivity in 1D, 2D, and 3D\"\n\nThe figure above shows how connectivity is defined for a region in each\ndimension. Fig (a) shows the gridpoint structure for these regions and Fig (c)\nshows how the resulting mesh appears. Fig (b) shows how a connectivity list\nshould be sequenced to create the desired mesh. Then, in 1D, our connectivity\nlist to create the line in (c) would be [1,2]. Similarly, in 2D, our\nconnectivity list would be [1,2,4,3] and in 3D, our list would be\n[1,2,4,3,5,6,8,7]. (Note that this sequencing is dictated by Paraview's\nconventions, not SpECTRE's.)\n\nThe above example illustrates how to connect the smallest connected region in\neach dimension. The complete connectivity list, detailing how the entire domain\nmesh is to be created, is made by concatenating individual connectivity lists.\nThus, in 1D, Paraview interprets the list [1,2,2,3] in blocks of 2, with the\nfirst block [1,2] representing a line between gridpoints 1 and 2, and the second\nblock [2,3] representing a line between gridpoints 2 and 3. This works similarly\nin the other dimensions.\n\n\n# How does ExtendConnectivity work?\n\nExtendConnectivity is a post-processing algorithm that looks to add more\nconnectivity entries to fill up any gaps in visualisation. These gaps have\nmultiple sources, the largest of which is the fact that SpECTRE does not add\nconnectivity between elements (as defined in\n\\ref domain_concepts \"Domain Concepts\") by default. This is important in certain\nsimulations where the endpoints of the elements do not overlap (e.g simulations\nusing Gauss quadrature), resulting in gaps between elements in visualisations.\nExtendConnectivity looks to fix this problem specifically. **Note:** The\nremainder of this guide assumes we are working with simulations whose elements\ndon't overlap.\n\n## Neighbours\n\nThe key idea in understanding how ExtendConnectivity works is understanding\nhow neighbouring elements are categorised and dealt with. The next sections\nexplain the categories of 'face neighbour', 'edge neighbour', and 'corner\nneighbour' in detail for 1D, 2D, and 3D.\n\n### 3D\n\nWe shall start in 3D because the categories are most intuitively defined in\n3D. The image below shows a typical example of SpECTRE connectivity for a 3D\nvolume element (with h-refinement 1 and p-refinement 2 in each direction),\ncoloured by their neighbour categorisation.\n\n\\image html DefaultConnectivity3D.png \"3D domain with default connectivity\"\n\nIn the above image, the grey cube is our reference element that neighbour\nelements are defined around. Then, we define any element that, if elements\noverlapped, would share a face with our reference element to be a 'face\nneighbour'. By this definition, we can see that our blue cubes in the above\nimage are our face neighbours. The definitions of the other categories are\nanalogous. We define any element that, if elements overlapped, would\nshare an edge to be an 'edge neighbour' and any element that would share a\ncorner to be a 'corner neighbour'. Hence, our red cubes are our edge neighbours\nand our green cube is our corner neighbour. In general, for a 3D simulation,\nface neighbours share a 2D region, edge neighbours share a 1D region, and corner\nneighbours share a 0D region.\n\nThese categories apply to the element as a whole. Then, after the element is\ncategorised, the gridpoints must be filtered to the particular face, edge, or\ncorner that will be connected with our reference element. Note that edge and\ncorner neighbours require gridpoints from multiple neighbours (e.g. edge\nneighbours involve connecting four edges - one from the reference element, two\nfrom face neighbours, and one from the edge neighbour). Once the correct\ngridpoints are identified, they are connected as described in the previous\nsection. The neighbour direction in particular determines the ordering of the\ngridpoints in this sequence. The image below shows the same domain after the\nmissing connectivity has been added, coloured by the type of connectivity added.\n\n\\image html ExtendedConnectivity3D.png \"3D domain with extended connectivity\"\n\nIn this image, the brown cubes are the face neighbour connections, the orange\ncubes are the edge neighbour connections, and the yellow cube is the corner\nneighbour connection. The entire process is then repeated for the next element.\n\nThis example required only one connectivity entry to be added per neighbour\nsince each element was made up of just one cube. At larger p-refinements, an\nelement consists of more cubes and consequently, more connectivity entries are\nrequired. For example, if the domain above had a p-refinement of 3 in each\ndimension instead, face connections would require adding four cubes\nand edge connections would require two cubes.\n\n### 2D\n\nWe can now generalise these definitions to other dimensions. The image below\nshows a typical example of SpECTRE connectivity for a 2D volume element (with\nh-refinement 1 and p-refinement 2 in each direction), coloured by their\nneighbour categorisation in the same colours as above.\n\n\\image html ExtendedConnectivity2D.png \"2D domain with extended connectivity\"\n\nOnce again, the grey square is our reference element. We now define our\nneighbour types to be analogous to the types in 3D but reduced by one dimension.\nThen, in a 2D simulation, face neighbours share a 1D regions, edge neighbours\nshare a 0D region, and corner neighbours do not exist. In the image above, our\nface neighbours are once again blue and our edge neighbours are once again red.\n\nOnce the elements are categorised, the relevant gridpoints are once again\nfiltered and connected according to the 2D structure explained above. In the\nimage, the face connections are coloured brown and the edge connections are\ncoloured orange again. The next element is then made the reference element and\nthe process is repeated to build up the connectivity for the entire domain.\n\n### 1D\n\nWe display the concepts in 1D for completeness. The image below shows an example\nof SpECTRE connectivity for a 1D volume element (with h-refinement 1 and\np-refinement 2), coloured by their neighbour categorisation in the same colours\nas above.\n\n\\image html ExtendedConnectivity1D.png \"1D domain with extended connectivity\"\n\nIn this image, the grey line is our reference element. In a 1D simulation, we\ncorrespondingly define face neighbours to share a 0D region and both edge and\ncorner neighbours do not exist. Then, in the image above, our face neighbour\nis once again the blue element and the face connection is coloured brown again.\n\n### Summary\n\n* Face Neighbour:<br>\n  A neighbouring element that shares an overlap region one dimension lower than\n  the dimension of the simulation.\n\n* Edge Neighbour:<br>\n  A neighbouring element that shares an overlap region two dimensions lower than\n  the dimension of the simulation.\n\n* Corner Neighbour:<br>\n  A neighbouring element that shares an overlap region three dimensions lower\n  than the dimension of the simulation.\n\n\n## Connecting elements with different p-refinements\n\nTo be added.\n"
  },
  {
    "path": "docs/DevGuide/CreatingExecutables.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Creating Executables {#dev_guide_creating_executables}\n\n\\tableofcontents\n\nThere are several different types of executables that can be built:\n- An executable that uses Charm++ to run in parallel,\n  e.g. `Executables/ParallelInfo`\n- An executable that does not use Charm++, does not run in parallel, and\n  supplies its own `main`, e.g. `Executables/Examples/HelloWorldNoCharm`\n- An executable that uses Charm++ to run in parallel but supplies its own `main`\n- An executable that uses custom compilation or linking flags,\n  e.g. `DebugPreprocessor`\n- Executables used for evolutions or elliptic solves\n\n### Executable Using Charm++ for Parallelization\n\n\\ref tutorial_minimal_parallel_executable tutorial describes how to\nadd a new parallel executable.\n\nAnother simple example of an executable using Charm++ for\nparallelization is in `src/Executables/Examples/HelloWorld`.  In this\nexample, the only additional phase (besides `Initialization` and\n`Exit`) is `Execute`, and the phases are executed in order.\n`SingletonHelloWorld` defines a single component `HelloWorld`\n\n\\snippet SingletonHelloWorld.cpp executable_example_singleton\n\nwhich specifies via the `chare_type` type alias that it is a singleton\nparallel component which means that only one such object will exist\nacross all processors used by the executable.  Each component must\ndefine the static function `execute_next_phase` which is executed\nduring the phases (other than `Initialization` and `Exit`) defined in\nthe metavariables struct.  In `SingletonHelloWorld`, the\n`PrintMessage` action is called during the `Execute` phase.\n\n\\snippet  SingletonHelloWorld.cpp executable_example_action\n\nThe `PrintMessage` action is executed on whatever process the singleton\ncomponent is created upon, and prints a message.\n\nExecutables can read in an input file (specified by the `--input-file`\nargument) that will be parsed when the executable begins.  %Options\nspecified in the input file can be used to either place items in the\nParallel::GlobalCache (by specifying tags in the\n`const_global_cache_tags` type alias of the metavariables, component\nand action structs), to construct items in the db::DataBox of\ncomponents during initialization (by specifying tags in the\n`simple_tags_from_options` type alias of action struct), or be passed to\nthe `allocate_array` function of an array component (by specifying\ntags in the `array_allocation_tags` type alias of the component).\n`SingletonHelloWorld` specifies a single option\n\n\\snippet SingletonHelloWorld.cpp executable_example_options\n\nwhich a string specifying a name that will be placed into the constant global\ncache.  The string is fetched when performing the `PrintMessage` action. Items\nin the constant global cache are stored once per node that the executable runs\non. An example input file for `SingletonHelloWorld` can be found in\n`tests/InputFiles/ExampleExecutables/SingletonHelloWorld.yaml` and shows how to\nspecify the options (lines beginning with a `#` are comments and can be\nignored).\n\nFurthermore among the included header files\n\n\\snippet  SingletonHelloWorld.cpp executable_example_includes\n\nmust be the appropriate header for each parallel component type, which in the\n`SingletonHelloWorld` example is `AlgorithmSingleton.hpp`.  Note that\nthese headers are not in the source tree, but are generated automatically when\nthe code is compiled.\n\nSee [the Parallelization documentation](group__ParallelGroup.html#details)\nfor more details.\n\n### Executable Using Charm++ with Custom main()\n\nWhile this is technically possible, it has not been tested. We recommend using\nthe Charm++ supplied main chare mechanism for the time being.\n\n### Executable Not Using Charm++\n\nAn example of an executable that does not use Charm++ for parallelization but\nstill can use all other infrastructure in SpECTRE is in\n`src/Executables/Examples/HelloWorldNoCharm`. Adding a non-Charm++ executable to\nSpECTRE mostly follows the standard way of adding an executable using CMake. The\nonly deviation is that the `CMakeLists.txt` file must tell Charm++ not to add a\n`main()` by passing the link flags `-nomain-module -nomain`. This is done using\nCMake's `set_target_properties`:\n\n```\nset_target_properties(\n  ${EXECUTABLE}\n  PROPERTIES LINK_FLAGS \"-nomain-module -nomain\"\n  )\n```\n\nTo add the executable as a target you must use the `add_spectre_executable`\nfunction, which is a light weight wrapper around CMake's `add_executable`.\nFor example,\n\n```\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL # Exclude from calls to `make` without a specified target\n  HelloWorld.cpp\n  )\n```\n\nYou can link in any of the SpECTRE libraries by adding them to the\n`target_link_libraries`, for example:\n\n```\ntarget_link_libraries(\n  ${EXECUTABLE}\n  DataStructures\n  )\n```\n\nWe recommend that you add a test that the executable properly runs by adding an\ninput file to `tests/InputFiles` in an appropriate subdirectory. See\n[`tests/InputFiles/ExampleExecutables/HelloWorldNoCharm.yaml`]\n(https://github.com/sxs-collaboration/spectre/tree/develop/tests/InputFiles/\nExampleExecutables/HelloWorldNoCharm.yaml)\nfor an example.\nThe input file is passed to the executable using `--input-file\npath/to/Input.yaml`. In the case of the executable not taking any input file\nthis is just used to generate a test that runs the executable.\n\nFor these types of executables `main` can take the usual `(int argc, char\n*argv[])` and parse command line options. Executables not using Charm++ are just\nstandard executables that can link in any of the libraries in SpECTRE.\n\n\\warning\nCurrently calling `Parallel::abort` results in a segfault deep inside Charm++\ncode. However, the error messages from `ASSERT` and `ERROR` are still printed.\n\n### Executable With Custom Compilation or Linking Flags\n\nUse the CMake function `set_target_properties` to add flags to an executable. To\ncall a completely custom compiler invocation you should use the\n`add_custom_target` CMake function. The need for the `custom_target` level of\ncontrol is rare and should generally be avoided since it adds quite a bit of\ntechnical debt to the code base. Thus, it is not explained here. If you are\ncertain you need it you can see the `DebugPreprocessor` executable's\n`CMakeLists.txt` file for an example.\n\n### Executable Used for Evolution or Elliptic Solve\n\nOnce they are written, see the tutorials specific to evolution and elliptic\nsolves.\n"
  },
  {
    "path": "docs/DevGuide/Databox.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Motivation for SpECTRE's DataBox {#databox_foundations}\n\n\\tableofcontents\n\n# Introduction {#databox_introduction}\nThis page walks the user through the iterative process that led to SpECTRE's\nDataBox. At each stage, it discusses the advances and challenges that result\nfrom each improvement.\n\n# Towards SpECTRE's DataBox {#databox_towards_spectres_databox}\n\n## Working without DataBoxes {#databox_working_without_databoxes}\nIn a small C++ program, it is common to use the built-in fundamental types\n(bool, int, double, etc.) in computations, and to give variable names to\nobjects of these types. For example, a section of a small program may look like\nthis:\n\n\\snippet Test_DataBoxDocumentation.cpp working_without_databoxes_small_program_1\n\nWhat changes as our program's size increases in scale? In SpECTRE, one of our\ndriving design goals is modularity. In other words, functionality should be\neasy to swap in and out as desired. These smaller modules are easier to test,\nand keep the code flexible. We could wrap our calculation in such a module,\nwhich would then allow us to decouple the initial setup of the variables from\nwhere they are used in calculations:\n\n\\snippet Test_DataBoxDocumentation.cpp working_without_databoxes_mass_compute\n\n\\snippet Test_DataBoxDocumentation.cpp working_without_databoxes_accel_compute\n\nOur small program can now be written as:\n\n\\snippet Test_DataBoxDocumentation.cpp working_without_databoxes_small_program_2\n\nOne advantage is immediate: we are free to add other computation modules that\nare independently testable and reusable. As the number of routines grows, we\ncan even begin to write routines that work on top of existing ones.\n\n\\snippet Test_DataBoxDocumentation.cpp working_without_databoxes_force_compute\n\nWhile we have made progress, two problems arise. Our first problem is that as\nthe number of quantities grows, it becomes more unwieldy to have to specify\neach function argument in our routine. The second problem is worse: the\narguments passed to functions can be transposed and the program will still\ncompile and run, but produce incorrect output. For example, the following two\nlines are equally well-formed from the point of view of the program:\n\n\\snippet Test_DataBoxDocumentation.cpp working_without_databoxes_failed_accel\n\nEvery time we call `acceleration_compute` we need to make sure we pass in the\narguments in the correct order. In large programs where `acceleration_compute`\nis called many times, it becomes inevitable that the arguments will be\naccidentally transposed. We can address the two problems described above with a\n`std::map`, the first container we'll consider in this series.\n\n##A std::map DataBox {#databox_a_std_map_databox}\nWe can encapsulate the variables we use in a `std::map`, and the first half of\nour small program example now looks like this:\n\n\\snippet Test_DataBoxDocumentation.cpp std_map_databox_small_program_1\n\nWe have not yet taken full advantage of the encapsulation that `std::map`\nprovides. We do so by rewriting our other routines to take only a single\nargument, i.e. the `std::map` itself:\n\n\\snippet Test_DataBoxDocumentation.cpp std_map_databox_mass_compute\n\n\\snippet Test_DataBoxDocumentation.cpp std_map_databox_accel_compute\n\nNotice that this solves the problem of having to provide the arguments\nin the correct order to every function call of `acceleration_compute`:\n\n\\snippet Test_DataBoxDocumentation.cpp std_map_databox_force_compute\n\n\nOur small program now looks like:\n\n\\snippet Test_DataBoxDocumentation.cpp std_map_databox_small_program_2\n\nWithin each function, we no longer need to worry about passing in the arguments\nin the correct order. This is a great improvement, but our reliance on proper\nnames does leave us open to the following mistake:\n\n~~~{.c}\n// returns 0 without emitting an error!\nreturn naive_databox[\"MisspelledKey\"];\n~~~\n\nIn the above example, the map is asked to return a value given a key\nthat does not exist! As written, however, the program is well-formed and no\nerror is emitted. (In the case of std::map, [a value is created.]\n(https://en.cppreference.com/w/cpp/container/map/operator_at)) Because the keys\nare indistinguishable from their type alone, the mistake cannot be caught at\ncompile time. In our example, the mistake won't even be caught at run time. The\nrun time portion of a SpECTRE calculation will typically be much longer (up to\nthousands of times longer!) than the compile time portion, so it is critical to\ncatch costly mistakes like this as early into the calculation as possible.\nAlthough names encoded as `std::string` cannot be distinguished by the\ncompiler, names encoded as types *can* be. This is possible with C++'s static\ntyping, and to take advantage of this we need a container that is\n*heterogeneous*, that is, capable of holding objects of different types.\n\n## A std::tuple DataBox {#databox_a_std_tuple_databox}\n\nA well-documented example of a fixed-size heterogeneous container of types is\n[std::tuple](https://en.cppreference.com/w/cpp/utility/tuple):\n\n\\snippet Test_DataBoxDocumentation.cpp std_tuple_databox_1\n\nThe contents of the `std_tuple` are obtained using\n[std::get](https://en.cppreference.com/w/cpp/utility/tuple/get):\n\n\\snippet Test_DataBoxDocumentation.cpp std_tuple_databox_2\n\nIn the above, we can see that we have promoted our keys from different values\nall of type `std::string` to different types entirely. We are not limited to\nfundamental types, we are free to make our own structs that serve as keys.\nThese user-created types are called *tags*.\n\nAs the sole purpose of the tag is to provide the compiler with a type\ndistinguishable from other types, they can be as simple as the following:\n\n\\snippet Test_DataBoxDocumentation.cpp std_tuple_tags\n\nNote that we have now promoted `Velocity`, `Radius`, etc. from being *values*\nassociated with `std::string`s at run time, to *types* distinguishable from\nother types at compile time. A large portion of SpECTRE is designed with the\nphilosophy of enlisting the help of the compiler in assuring the correctness\nof our programs. An example of a `std::tuple` making use of these tags might\nlook like:\n\n~~~{.c}\n// Note: This won't work!\nstd::tuple<Velocity, Radius, Density, Volume> sophomore_databox =\n  std::make_tuple(4.0, 2.0, 0.5, 10.0);\n~~~\n\nUnfortunately, this will not work. The types passed as template parameters to\n`std::tuple` must also be the types of the arguments passed to\n`std::make_tuple`. Using a `std::pair`, we could write the above as:\n\n\\snippet Test_DataBoxDocumentation.cpp std_tuple_small_program_1\n\nWhat remains is to rewrite our functions to use `std::tuple` instead of\n`std::map`. Note that since we are now using a heterogeneous container of\npotentially unknown type, our functions must be templated on the\npairs used to create the `sophomore_databox`. Our functions then look like:\n\n\\snippet Test_DataBoxDocumentation.cpp std_tuple_mass_compute\n\n\\snippet Test_DataBoxDocumentation.cpp std_tuple_acceleration_compute\n\n\\snippet Test_DataBoxDocumentation.cpp std_tuple_force_compute\n\nUsing all these `std::pair`s to get our `std::tuple` to work is a bit\ncumbersome. There is another way to package together the tagging ability\nof the struct names with the type information of the values we wish to store.\nTo do this we need to make modifications to both our tags as well as our\n%Databox implementation. This is what is done in SpECTRE's\n`tuples::TaggedTuple`, which is an improved implementation of `std::tuple` in\nterms of both performance and interface.\n\n##A TaggedTuple DataBox {#databox_a_taggedtuple_databox}\n\nTaggedTuple is an implementation of a compile time container where the keys\nare tags.\n\nTags that are compatible with SpECTRE's `tuples::TaggedTuple` must have the\ntype alias `type` in their structs. This type alias carries the type\ninformation of the data we wish to store in the databox. `tuples::TaggedTuple`\nis able to make use of this type information so we won't need auxiliary\nconstructs such as `std::pair` to package this information together anymore.\nOur new tags now look like:\n\n\\snippet Test_DataBoxDocumentation.cpp tagged_tuple_tags\n\nWe are now able to create the `junior_databox` below in the same way we\ninitially wished to create the `sophomore_databox` above:\n\n\\snippet Test_DataBoxDocumentation.cpp tagged_tuple_databox_1\n\nOur functions similarly simplify:\n\n\\snippet Test_DataBoxDocumentation.cpp tagged_tuple_mass_compute\n\n\\snippet Test_DataBoxDocumentation.cpp tagged_tuple_acceleration_compute\n\n\\snippet Test_DataBoxDocumentation.cpp tagged_tuple_force_compute\n\nIn each of these iterations of the Databox, we started with initial quantities\nand computed subsequent quantities. Let us consider again `force_compute`, in\nwhich `mass` and `acceleration` are recomputed for every call to\n`force_compute`. If `Mass` and `Acceleration` were tags somewhow, that is, if we\ncould compute them once, place them in the databox, and get them back out\nthrough the use of tags, we could get around this problem. We are now ready to\nconsider SpECTRE's DataBox, which provides the solution to this problem in the\nform of `ComputeTags`.\n\n# SpECTRE's DataBox {#databox_a_proper_databox}\n\nA brief description of SpECTRE's DataBox: a TaggedTuple with compute-on-demand.\nFor a detailed description of SpECTRE's DataBox, see the\n\\ref DataBoxGroup \"DataBox documentation\".\n\n## SimpleTags {#databox_documentation_for_simple_tags}\nJust as we needed to modify our tags to make them compatible with\n`TaggedTuple`, we need to again modify them for use with DataBox. Our ordinary\ntags become SpECTRE's SimpleTags:\n\n\\snippet Test_DataBoxDocumentation.cpp proper_databox_tags\n\nAs seen above, SimpleTags have a `type` and a `name` in their struct.\nWhen creating tags for use with a DataBox, we must make sure to the tag\ninherits from one of the existing DataBox tag types such as `db::SimpleTag`.\nWe now create our first DataBox using these `SimpleTags`:\n\n\\snippet Test_DataBoxDocumentation.cpp refined_databox\n\nWe can get our quantities out of the DataBox by using `db::get`:\n\n\\snippet Test_DataBoxDocumentation.cpp refined_databox_get\n\nSo far, the usage of DataBox has been similar to the usage of TaggedTuple. To\naddress the desire to combine the functionality of tags with the modularity of\nfunctions, DataBox provides ComputeTags.\n\n## ComputeTags {#databox_documentation_for_compute_tags}\nComputeTags are used to tag functions that are used in conjunction with a\nDataBox to produce a new quantity. ComputeTags look like:\n\n\\snippet Test_DataBoxDocumentation.cpp compute_tags\n\nComputeTags inherit from `db::ComputeTag`, and it is convenient to have them\nadditionally inherit from an existing SimpleTag (in this case `Mass`) so that\nthe quantity `MassCompute` can be obtained through the SimpleTag `Mass`.\nWe use the naming convention `TagNameCompute` so that `TagNameCompute` and\n`TagName` appear next to each other in documentation that lists tags in\nalphabetical order.\n\nWe have also added the type alias `argument_tags`, which is necessary in order\nto refer to the correct tagged quantities in the DataBox.\n\n\\note\nThe `tmpl::list` used in the type alias is a contiguous container only\nholding types. That is, there is no variable runtime data associated with it\nlike there is for `std::tuple`, which is a container associating types with\nvalues. `tmpl::list`s are useful in situations when one is working with\nmultiple tags at once.\n\nUsing nested type aliases to pass around information at compile time is a\ncommon pattern in SpECTRE. Let us see how we can compute our beloved quantity\nof mass times acceleration:\n\n\\snippet Test_DataBoxDocumentation.cpp compute_tags_force_compute\n\nAnd that's it! `db::get` utilizes the `argument_tags` specified in\n`ForceCompute` to determine which items to get out of the `refined_databox`.\nWith the corresponding quantities in hand, `db::get` passes them as arguments\nto the `function` specified in `ForceCompute`. This is why every `ComputeTag`\nmust have an `argument_tags` as well as a `function` specified; this is the\ncontract with DataBox they must satisfy in order to enjoy the full benefits of\nDataBox's generality.\n\n## Mutating DataBox items {#databox_documentation_for_mutate_tags}\nIt is reasonable to expect that in a complicated calculation, we will encounter\ntime-dependent or iteration-dependent variables. As a result, in addition to\nadding and retrieving items from our DataBox, we also need a way to *mutate*\nquantities already present in the DataBox. This can be done via\n`db::mutate_apply` and can look like the following:\n\n\\note\nThere is an alternative to `db::mutate_apply`, `db::mutate`. See the\n\\ref DataBoxGroup \"DataBox documentation\" for more details.\n\n\\snippet Test_DataBoxDocumentation.cpp mutate_tags\n\\snippet Test_DataBoxDocumentation.cpp time_dep_databox\n\n\\note\nThe `not_null`s here are used to give us the assurance that the pointers\n`time` and `falling_speed` are not null pointers. Using raw pointers alone, we\nrisk running into segmentation faults if we dereference null pointers. With\n`not_null`s we instead run into a run time error that tells us what went wrong.\nFor information on the usage of `not_null`, see the documentation for Gsl.hpp.\n\nIn the above `db::mutate_apply` example, we are changing two values in the\nDataBox using four values from the DataBox. The mutated quantities must be\npassed in as `gsl::not_null`s to the lambda. The non-mutated quantities are\npassed in as const references to the lambda.\n\n\\note\nIt is critical to guarantee that there is a strict demarcation between\npre-`db::mutate` and post-`db::mutate` quantities. `db::mutate` provides this\nguarantee via a locking mechanism; within one mutate call, all initial\npre-mutated quantities are obtained from the DataBox before performing a single\nmutation.\n\n\\note\nThe mutate functions described above are the only accepted ways to edit data\nin the Databox. It is technically possible to use pointers or references to\nedit data stored in the Databox, but this bypasses the compute tags\narchitecture. All changes to the Databox must be made by the Databox itself\nvia mutate functions.\n\nFrom the above, we can see that the different kinds of tags are provided in two\ndifferent `tmpl::list`s. The `MutateTags`, also called `ReturnTags`, refer to\nthe quantities in the DataBox we wish to mutate, and the `ArgumentTags` refer to\nadditional quantities we need from the DataBox to complete our computation. We\nnow return to the recurring question of how to make this construction more\nmodular.\n\nWe have now worked our way up to SpECTRE's DataBox, but as we can see in the\nabove `db::mutate_apply` example, the lambda used to perform the mutation ends\nup being independent of any tags or template parameters! This means we can\nfactor it out and place it in its own module, where it can be tested\nindependently of the DataBox.\n\n# Toward SpECTRE's Actions {#databox_towards_actions}\n\n## Mutators {#databox_documentation_for_mutators}\nThese constructs that exist independently of the DataBox are the precursors to\nSpECTRE's *Actions*. As they are designed to be used with `db::mutate` and\n`db::mutate_apply`, we give them the name *Mutators*. Here is the above lambda\nwritten as a Mutator-prototype, a struct-with-void-apply:\n\n\\snippet Test_DataBoxDocumentation.cpp intended_mutation\n\nThe call to `db::mutate_apply` has now been made much simpler:\n\n\\snippet Test_DataBoxDocumentation.cpp time_dep_databox2\n\nThere is a key step that we take here after this point, to make our\nstruct-with-void-apply into a proper Mutator. As we will see, this addition\nwill allow us to entirely divorce the internal details of `IntendedMutation`\nfrom the mechanism through which we update the DataBox. The key step is to\nadd type aliases to `IntendedMutation`:\n\n\\snippet Test_DataBoxDocumentation.cpp intended_mutation2\n\nWe are now able to write our call to `db::mutate_apply` in the following way:\n\n\\snippet Test_DataBoxDocumentation.cpp time_dep_databox3\n\nAs we saw earlier with `QuantityCompute`, we found that we were able to imbue\nstructs with the ability to \"read in\" types specified in other structs, through\nthe use of templates and member type aliases. This liberated us from having to\nhard-code in specific types. We notice immediately that `IntendedMutation` is a\nhard-coded type that we can factor out in favor of a template parameter:\n\n\\snippet Test_DataBoxDocumentation.cpp my_first_action\n\nNote how the `return_tags` and `argument_tags` are used as metavariables and\nare resolved by the compiler. Our call to `db::mutate_apply` has been fully\nwrapped and now takes the form:\n\n\\snippet Test_DataBoxDocumentation.cpp time_dep_databox4\n\nThe details of applying Mutators to the DataBox are entirely handled by\n`MyFirstAction`, with the details of the specific Mutator itself entirely\nencapsulated in `IntendedMutation`.\n\nSpECTRE algorithms are decomposed into Actions which can depend on more\nthings than we have considered here. Feel free to look at the\nexisting \\ref ActionsGroup \"actions that have been written.\" The intricacies of\nActions at the level that SpECTRE uses them is the subject of a future addition\nto the \\ref dev_guide \"Developer's Guide.\"\n"
  },
  {
    "path": "docs/DevGuide/DebuggingTips.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Tips for debugging an executable {#runtime_errors}\n\nLearn how to use a debugger such as gdb.\n\nYou can debug MPI executables using the `sys::attach_debugger()` function. See\nthe documentation of that function for details.\n\n# Useful gdb commands\n\n- To break when an exception is thrown `catch throw`\n\n- To break on a specific exception type `catch throw std::out_of_range`\n  (This may not work on all compilers or older versions of gdb.  In this\n  case you also try setting a breakpoint on the constructor of the exception\n  type, `break std::out_of_range::out_of_range`)\n\n- SpECTRE has pretty printing facilities for various custom types. In order to\n  enable these you must add\n  `add-auto-load-safe-path /path/to/spectre/` to your `~/.gdbinit` file.\n"
  },
  {
    "path": "docs/DevGuide/DevGuide.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Developer Guides {#dev_guide}\n\n### Adaptive Mesh Refinement\n- \\subpage dev_guide_amr \"Adaptive Mesh Refinement\"\n\n### Charm++ Interface\n- \\subpage load_balancing_notes \"Load Balancing Notes and Recommendations\"\n\n### Continuous Integration\nExplanations on our automated tests and deployments can be found here.\n- \\subpage dev_guide_automatic_versioning\n\n### CoordinateMap Guide\nMethods for creating custom coordinate maps are discussed here.\n- \\subpage redistributing_gridpoints \"Methods for redistributing gridpoints\"\n\n### Developing and Improving Executables\n- \\subpage dev_guide_creating_executables \"Executables and how to add them\"\n- \\subpage tutorials_parallel - A series of tutorials demonstrating how to write\n  a parallel executable, and explaining some of the metaprogramming that turns\n  user-provided code into a SpECTRE parallel executable\n- \\subpage dev_guide_option_parsing \"Option parsing\" to get options from input\n  files\n- \\subpage dev_guide_importing \"Importing\" data from files\n- \\subpage profiling \"Profiling\" SpECTRE with a variety of different tools\n- \\subpage spectre_writing_python_bindings \"Writing Python Bindings\" to use\n  SpECTRE C++ classes and functions from within python.\n- \\subpage implementing_vectors \"Implementing SpECTRE vectors\" a quick how-to\n  for making new generalizations of DataVectors\n- \\subpage compiler_and_linker_errors \"How to parse linker and compiler errors\"\n- \\subpage static_analysis_tools \"Static analysis tools\"\n- \\subpage build_profiling_and_optimization - Getting started with improving\n  compilation time and memory use\n- \\subpage runtime_errors \"Tips for debugging an executable\"\n\n### Foundational Concepts in SpECTRE\nDesigned to give the reader an introduction to SpECTRE's most recurring concepts\nand patterns.\n- \\subpage databox_foundations \"Towards SpECTRE's DataBox\"\n- \\subpage protocols \"Protocols: metaprogramming interfaces\"\n- \\subpage variables_foundations \"Using the Variables class\" to improve\n  efficiency\n\n### GPU Support\nInformation on how to use and develop GPU support in SpECTRE.\n- \\subpage gpu_support \"GPU support\"\n\n### General SpECTRE Terminology\nTerms with SpECTRE-specific meanings are defined here.\n- \\subpage domain_concepts \"Domain Concepts\" used throughout the code are\n  defined here for reference.\n- \\subpage visualisation_connectivity \"Visualisation Connectivity\" is defined\n  and explained here for reference.\n\n### Having your Contributions Merged into SpECTRE\n- \\subpage writing_good_dox \"Writing good documentation\" is key for long term\n  maintainability of the project.\n- \\subpage writing_unit_tests \"Writing Unit Tests\" to catch bugs and to make\n  sure future changes don't cause your code to regress.\n- \\subpage github_actions_guide \"GitHub Actions\" is used to test every pull\n  request.\n- \\subpage code_review_guide \"Code review guidelines.\" All code merged into\n  develop must follow these requirements.\n\n### Performance and Optimization\n- \\subpage general_perf_guide \"General performance guidelines\"\n\n### Technical Documentation for Fluent Developers\nAssumes a thorough familiarity and fluency in SpECTRE's usage of TMP.\n- \\subpage DataBoxGroup \"DataBox\"\n- \\subpage observers_infrastructure_dev_guide \"Observers infrastructure\"\n- \\subpage dev_guide_parallelization_foundations - Parallelization\n  infrastructure components and usage\n\n### Template Metaprogramming (TMP)\nExplanations for TMP concepts and patterns known to the greater C++ community\ncan be found here.\n- \\subpage sfinae \"SFINAE\"\n- \\subpage brigand \"The Brigand TMP library\"\n\n### Writing Tensor Equations\n- \\subpage writing_tensorexpressions\n  \"Writing Tensor Equations with TensorExpressions\"\n"
  },
  {
    "path": "docs/DevGuide/DomainConcepts.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Domain Concepts {#domain_concepts}\n\n\\tableofcontents\n\n* Computational Domain:<br>\n  The region of spacetime on which a numerical simulation is performed.\n\n* Inertial Coordinate Frame:<br>\n  A global coordinate frame covering the computational domain that is also the\n  coordinate frame in which the initial (boundary) value problem that is being\n  solved is specified.  Denoted by Frame::Inertial.\n\n* Logical Coordinate Frame:<br> The coordinate frame of a reference\n  cell.  In the cell the logical coordinates cover the interval\n  \\f$[-1, 1]\\f$ in each dimension.  Currently the logical coordinates\n  are Cartesian.\n\n* CoordinateMap \"Coordinate Map\":<br>\n  A mapping between two coordinate frames.  Coordinate maps are allowed to be\n  time-dependent, as long as the time coordinate itself is unchanged.\n\n* Direction:<br>\n  A logical coordinate axis and a label \"Upper\" or \"Lower\" depending on whether\n  the Direction is aligned with or anti-aligned with the logical coordinate\n  axis, respectively.\n\n* Block:<br>\n  The computational domain is partitioned into a set of non-overlapping,\n  distorted reference cells called Blocks. Each Block must have at most one\n  neighboring Block in each Direction.  The reference cell is embedded into a\n  subset of the computational domain using a Coordinate Map from the\n  logical frame of the Block to the global inertial frame.  Blocks are\n  identified by unique integral values.\n\n* Block Logical Coordinate Frame:<br>\n  The logical coordinate frame of a Block, denoted by Frame::BlockLogical.\n  The only requirement upon the logical coordinate frames of neighboring Blocks\n  is that they have the same Coordinate Map from their logical coordinate frame\n  to the global inertial frame on their shared boundary up to a mapping that\n  swaps or negates the logical coordinate axes.  In other words, at each point\n  on their shared boundary, the logical coordinates of the two blocks are\n  equal after possibly applying a permutation and sign flips.\n\n* Grid Coordinate Frame:<br>\n  For time-dependent coordinate maps, it is useful (e.g. for computational\n  efficiency) to split the Coordinate Map from Frame::BlockLogical to\n  Frame::Inertial into a composition of two maps.  This is done by introducing\n  an intermediate coordinate frame (denoted by Frame::Grid) such that the\n  mapping from Frame::BlockLogical to Frame::Grid is time-independent, and\n  the mapping from Frame::Grid to Frame::Inertial is time-dependent.\n\n* Orientation:<br>\n  The information of how the Block Logical Coordinates of neighboring Blocks are\n  related.\n\n* \\ref BlockNeighbors \"Block Neighbors\":<br>\n  The identity and Orientation of neighboring Blocks of a given Block.\n\n* Element:<br> A reference cell that is a refined subregion of a Block\n  defined by its Segments in each dimension. The properties of the\n  Element (e.g coordinate map) are inherited from its Block, i.e. it\n  is self-similar to the Block.\n\n* Refinement Level:<br>\n  The number of times a Block is split in half in a given dimension.\n\n* Segment:<br> In each dimension, the specific subset of the block\n  logical coordinate interval \\f$[-1, 1]\\f$ defined by the Refinement\n  Level and an integer label such that the Segment's interval is\n  \\f$[-1 + 2 \\frac{i}{N}, -1 + 2 \\frac{i+1}{N}]\\f$ where \\f$i\\f$ is\n  the integer label and \\f$N = 2^{(\\textrm{Refinement Level})}\\f$ is\n  the number of segments on the Refinement Level.\n\n* Element Logical Coordinate Frame:<br>\n  The logical coordinate frame of an Element.  In each dimension, the Element\n  Logical Coordinates are related to the Block Logical Coordinates by an affine\n  mapping of the interval \\f$[-1, 1]\\f$ to the interval covered by the Segment\n  in that dimension.\n\n* \\ref SegmentId \"Segment Identifier\":<br>\n  The Refinement Level and an integer labeling a Segment.\n\n* \\ref ElementId \"Element Identifier\":<br>\n  The Block Identifier containing the Element and a Segment Identifier\n  in each dimension.\n\n* External Boundary:<br>\n  A face of a Block or Element that has no neighbor.\n\n* Internal Boundary:<br>\n  A boundary that is not an External Boundary.\n\n* Neighbors:<br>\n  The identities and Orientation of the neighboring Blocks or Elements of a\n  given Block or Element in a particular Direction .\n\n* External Boundary Condition:<br>\n  A prescription for updating the solution on an External Boundary. Each\n  External Boundary of a Block has exactly one External Boundary Condition\n  associated with it.\n"
  },
  {
    "path": "docs/DevGuide/GitHubActions.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# GitHub Actions Continuous Integration {#github_actions_guide}\n\n\\tableofcontents\n\n# Testing SpECTRE with GitHub Actions CI {#github_actions_ci}\n\nSpECTRE uses\n[GitHub Actions](https://github.com/features/actions) for\ntesting the code.  Multiple build jobs (described below) are launched\neach time a pull request is submitted or updated.  GitHub Actions will also\nlaunch these build jobs each time you push to a branch on your fork of SpECTRE\nif you enable it. GitHub Actions is also used to deploy releases of the code.\n\nFor pull requests, you can view the GitHub Actions CI build by clicking on the\n`Checks` tab. Near the bottom of the `Conversation` tab a summary of the CI\nresults are presented. You can view all of the GitHub Actions runs by clicking\non the `Actions` section.\n\n## What is tested {#what-is-tested}\n\nThe GitHub Actions report lists the build jobs which will each have either a\ngreen check mark if it passes, a red `X` if it has failed, or a yellow\ndot with a circle if the build is in progress.  Clicking on a build job will\ndisplay the log of the build.\n\nThe following build jobs are launched:\n* CHECK_COMMITS runs the script `tools/CheckCommits.sh` and fails the build if\n  any casing of the words in the list below is the first word of the commit\n  message.  This allows developers to flag their commits with these keywords to\n  indicate that a pull request should not be merged in its current state.\n  - fixup\n  - wip (for work in progress)\n  - fixme\n  - deleteme\n  - rebaseme\n  - testing\n  - rebase\n* CHECK_FILES runs the script `tools/CheckFiles.sh` (which also runs the script\n  `tools/FileTestDefs.sh`). The checks fail if any of the following are true:\n  - Any file,\n    * contains a line over 80 characters (We allow exceptions for certain file\n      types and inherently long strings like URLs and include lines.\n      See `tools/FileTestDefs.sh` for the full list of exceptions.)\n    * is missing the license line\n    * does not end with a newline\n    * contains a tab character\n    * contains white space at the end of a line\n    * contains a carriage return character\n  - A `c++` header file (i.e., `*.hpp` or `*.tpp`) is missing `#%pragma once`\n  - A `c++` file (i.e., `*.hpp`, `*.tpp`, or `*.cpp`) file,\n    * includes `<iostream>` (useless when running in parallel)\n    * includes `<lrtslock.h>` (use `<converse.h>` instead)\n    * includes `\"Utilities/TmplDebugging.hpp\"` (used only for debugging)\n    * includes any non-header `*.cpp` file\n    * contains a `namespace` ending in `_details` (use `_detail`)\n    * contains a `struct TD` or `class TD` (used only for debugging)\n    * contains `std::enable_if` (use `requires` instead)\n    * contains `Ls` (use `List` instead)\n    * contains additional text after `/*!` (does not render correctly in\n      Doxygen)\n    * contains the string `return Py_None;` (bug prone, use `Py_RETURN_NONE`\n      instead)\n    * contains `.ckLocal()` or `.ckLocalBranch()` (use `Parallel::local` or\n      `Parallel::local_branch` instead)\n  - A `c++` test,\n    * uses `TEST_CASE` (use `SPECTRE_TEST_CASE` instead)\n    * uses `Approx` (use `approx` instead)\n  - A `CMakeLists.txt` file in `src`, but not in an Executables or\n    Python-binding directory,\n    * does not list a `C++` file that is present in the directory\n    * lists a `C++` file that is not present in the directory\n  - A `c++` or `python` file contains a `TODO` (case-insensitive) comment\n  In addition, the CHECK_FILES job tests Python formatting, the release\n  workflow, and other tools in `tools/`.\n* \"Check Python formatting\" runs the `black` and `isort` formatters over the\n  source code.\n* RUN_CLANG_TIDY runs clang-tidy on the source code. This is done for both\n  `Release` and `Debug` builds.\n* TEST_CHECK_FILES runs `tools/CheckFiles.sh --test` which tests the checks\n  performed in the CHECK_FILES build.\n* The other builds compile the code and run the tests for both\n  `Release` and `Debug` builds, for the `gcc` and `clang` compilers\n  using a Linux OS, and the `AppleClang` compiler for `OS X`.\n* Verify the documentation builds successfully. Builds of `develop` deploy the\n  documentation to GitHub pages.\n\n## How to perform the checks locally {#perform-checks-locally}\n\nBefore pushing to GitHub and waiting for GitHub Actions to perform the checks it\nis useful to perform at least the following tests locally:\n- **Unit tests:** Perform a `make unit-tests` and then execute `ctest -L Unit`\n  to run all unit tests. As for `make` you can append a `-jN` flag to `ctest` to\n  run in parallel on `N` cores. To run only a subset of the tests you can use\n  one of the other keywords that the tests are labeled with, such as `ctest -L\n  datastructures`. To run only particular tests you can also execute `ctest -R\n  TEST_NAME` instead, where `TEST_NAME` is a regular expression matching the\n  test identifiers such as `Unit.DataStructures.Mesh`. Pass the flag\n  `--output-on-failure` to get output from failed tests. Consult `ctest -h` for\n  further options.\n\n  To run the input file tests you must build the executables using\n  `make test-executables`. You can then run `ctest -LE unit` to run everything\n  except for the unit tests, or `ctest` to run all tests.\n- **clang-tidy:** In a clang build directory, run `make clang-tidy\n  FILE=SOURCE_FILE` where `SOURCE_FILE` is a relative or absolute path to a\n  `.cpp` file. To perform this check for all source files that changed in your\n  pull request, `make clang-tidy-hash HASH=UPSTREAM_HEAD` where `UPSTREAM_HEAD`\n  is the hash of the commit that your pull request is based on, usually the\n  `HEAD` of the `upstream/develop` branch.\n- **Python formatting:** Run `black --check .` and `isort --check-only .` over\n  the repository. You can install these tools with `pip3 install -r\n  support/Python/dev_requirements.txt`\n- **Documentation:** To render the documentation for the current state\n  of the source tree the command `make doc` (or `make doc-check` to\n  highlight warnings) can be used, placing its result in the `docs`\n  directory in the build tree.  Once code has been made into a pull\n  request to GitHub, the documentation can be rendered locally using\n  the `tools/pr-docs` script.  To view the documentation, simply open the\n  `index.html` file in the `html` subdirectory in a browser. Some functionality\n  requires a web server (e.g. citation popovers), so just run a\n  `python3 -m http.server` in the `html` directory to enable this.\n- The `gcc Debug` build runs code coverage for each GitHub Actions build.\n\n## Troubleshooting {#github-actions-troubleshooting}\n\n* Occasionally, a build job will fail because of a problem with GitHub Actions\n  (e.g. it times out).  On the `Checks` tab you can restart all or only the\n  failed jobs. In the top right corner there's a `Re-run jobs` menu, which also\n  has `Re-run failed jobs`. This button is `Cancel workflow` during the build\n  process. Note that these buttons are only available if you have write access\n  to the repository (core developer status).\n* GitHub Actions caches some things between builds.  Occasionally this may\n  cause a problem leading to strange build failures.  For example, inexplicable\n  segfaults on seemingly random tests or `Illegal instruction` failures. We have\n  to be fairly lax with our caching policies, and so the cache can become stale\n  and outdated when a new container is pushed, among other difficult to\n  understand situations. You can rebuild the ccache by going to `Actions`, then\n  select the `Tests` workflow on the left, click the `Run workflow` drop-down\n  menu, and enter `yes` in the input field below the ccache discussion.\n\n  If clearing the ccache doesn't help, it could be that a Docker image layer is\n  not being updated. GitHub doesn't (yet) have a way to clear the cache, so\n  instead we clobber it to force GitHub to eject all old caches, both ccache and\n  Docker images, along with anything else. To do this go to `Actions`, select\n  the `Clobber Cache` workflow, then run it on develop. This will dump 9.9GB of\n  random data into the cache. The amount is specified in the `ClobberCache.yaml`\n  workflow file and needs to be updated if GitHub increases their cache\n  size. The current cache size limit is 10GB per repository.\n\n  Note that starting these workflows is only possible if you have write access\n  to the repository (core developer status).\n\n## Precompiled Headers and ccache {#precompiled-headers-ccache}\n\nGetting ccache to work with precompiled headers on GitHub Actions is a little\nchallenging. The header to be precompiled is\n`${SPECTRE_SOURCE_DIR}/tools/SpectrePch.hpp` and is symbolically linked to\n`${SPECTRE_BUILD_DIR}/SpectrePch.hpp`. The configuration that seems to work is\nspecifying the environment variables:\n\n\\code{.sh}\nCCACHE_COMPILERCHECK=content\nCCACHE_EXTRAFILES=\"${SPECTRE_SOURCE_DIR}/tools/SpectrePch.hpp\"\nCCACHE_IGNOREHEADERS=\\\n  \"${SPECTRE_BUILD_DIR}/SpectrePch.hpp:${SPECTRE_BUILD_DIR}/SpectrePch.hpp.gch\"\n\\endcode\n\n## Caching Dependencies on macOS Builds {#caching-mac-os}\n\nOn macOS builds we cache all of our dependencies, like LIBXSMM and\nCharm++. These are cached in `$HOME/mac_cache`. Ultimately this saves about\n10-12 minutes even when compared to using ccache to cache the object files from\nbuilding the dependencies. We also cache `$HOME/Library/Caches/Homebrew`, which\nis where Homebrew keeps the downloaded formulas. By caching the Homebrew bottles\nwe are able to avoid brew formulas building from source because a tarball of the\npackage was not available at the time.\n"
  },
  {
    "path": "docs/DevGuide/GpuSupport.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# GPU support {#gpu_support}\n\n\\tableofcontents\n\n# Overview\n\n\\warning GPU support is experimental and this page will be updated as the\nimplementation matures.\n\nSpECTRE supports GPU acceleration through the [Kokkos](https://github.com/kokkos/kokkos)\nlibrary.\n\n## Build configuration\n\nTo enable GPU support, set the CMake option `-D SPECTRE_KOKKOS=ON` when\nconfiguring the build. Either point CMake to a Kokkos installation with\n`-D Kokkos_ROOT=path/to/kokkos` or set `-D SPECTRE_FETCH_MISSING_DEPS=ON` to\nfetch Kokkos automatically and build it as part of SpECTRE. You also have to\nselect a parallelization backend for Kokkos and possibly more configuration\noptions like the GPU architecture to build for. Read the\n[Kokkos documentation](https://kokkos.org/kokkos-core-wiki/get-started/configuration-guide.html)\nfor details on how to configure Kokkos. Here's an example for fetching Kokkos\nautomatically and building it as part of SpECTRE with the CUDA backend:\n\n```sh\ncmake -D SPECTRE_KOKKOS=ON \\\n      -D SPECTRE_FETCH_MISSING_DEPS=ON \\\n      -D Kokkos_ENABLE_CUDA=ON \\\n      ...\n```\n\nHere's an example for using an existing Kokkos installation:\n\n```sh\ncmake -D SPECTRE_KOKKOS=ON \\\n      -D Kokkos_ROOT=path/to/kokkos/build \\\n      -D CMAKE_CXX_COMPILER=path/to/kokkos/bin/nvcc_wrapper \\\n      ...\n```\n\nWhen building Kokkos separately with the CUDA backend, you have to set the\nfollowing configuration options:\n\n- `Kokkos_ENABLE_CUDA_CONSTEXPR=ON`\n- `Kokkos_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE`\n"
  },
  {
    "path": "docs/DevGuide/ImplementingVectors.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Implementing SpECTRE vectors {#implementing_vectors}\n\n\\tableofcontents\n\n# Overview of SpECTRE Vectors {#general_structure}\n\nIn SpECTRE, sets of contiguous or related data are stored in specializations of\nvector data types. The canonical implementation of this is the `DataVector`,\nwhich is used for storage of a contiguous sequence of doubles which support a\nwide variety of mathematical operations and represent data on a grid used during\nan evolution or elliptic solve. However, we support the ability to easily\ngenerate similar vector types which can hold data of a different type\n(e.g. `std::complex<double>`), or support a different set of mathematical\noperations. SpECTRE vector classes are derived from the class template\n`VectorImpl`. The remainder of this brief guide gives a description of the tools\nfor defining additional vector types.\n\nFor reference, all functions described here can also be found in brief in the\nDoxygen documentation for VectorImpl.hpp, and a simple reference implementation\ncan be found in DataVector.hpp and DataVector.cpp.\n\n# The class definition {#class_definition}\n\nSpECTRE vector types inherit from vector types implemented in the\nhigh-performance arithmetic library\n[Blaze](https://bitbucket.org/blaze-lib/blaze). Using inheritance, SpECTRE\nvectors gracefully make use of the math functions defined for the Blaze types,\nbut can be customized for the specific needs in SpECTRE computations.\n\nThe trio of template parameters for `VectorImpl` are the type of the stored data\n(e.g. `double` for `DataVector`), the result type for mathematical operations,\nand the static size. The result type is used by Blaze to ensure that only\ncompatible vector types are used together in mathematical expressions. For\nexample, a vector representing `double` data on a grid (`DataVector`) cannot be\nadded to a vector representing spectral coefficients (`ModalVector`). This\navoids subtle bugs that arise when vector types are unintentionally mixed.\nIn nearly all cases the result type will be the vector type that is being\ndefined, so, for instance, `DataVector` is a derived class of\n`VectorImpl<double, DataVector, 5>`. This template pattern is known as the\n[\"Curiously Recurring Template Pattern\"](https://en.wikipedia.org/wiki/\nCuriously_recurring_template_pattern) (CRTP).\nThe static size is used as an optimization for small vector sizes. If your\nvector is small, rather than doing heap allocations, it will use stack\nallocations in order to be efficient. The default static size is set by a global\nconstexpr bool `default_vector_impl_static_size`.\n\nFor the Blaze system to use the CRTP inheritance appropriately, it requires the\nspecification of separate type traits in the `blaze` namespace.\nThese traits can usually be declared in a standard form, so are abstracted in a\nmacro. For any new vector `MyNewVector`, the pattern that must appear at the\nbeginning of the file (i.e. before the class definition) is:\n```\n/// \\cond\nclass MyNewVector;\n/// \\endcond\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(MyNewVector);\n}\n```\nThe class template `VectorImpl` defines various constructors, assignment\noperators, and iterator generation members. Most of these are inherited from\nBlaze types, but in addition, the methods `set_data_ref`, and `pup` are defined\nfor use in SpECTRE. All except for the assignment operators and constructors\nwill be implicitly inherited from `VectorImpl`. The assignment and constructors\nmay be inherited calling the following alias code in the vector class\ndefinition:\n```\nusing VectorImpl<T,VectorType,StaticSize>::operator=;\nusing VectorImpl<T,VectorType,StaticSize>::VectorImpl;\n```\n\nOnly the mathematical operations supported on the base Blaze types are supported\nby default. Those operations are determined by the storage type `T` and by the\nBlaze library. See [blaze-wiki/Vector_Operations](https://bitbucket.org/\nblaze-lib/blaze/wiki/Vector%20Operations).\n\n# Allowed operator specification {#blaze_definitions}\n\nBlaze keeps track of the return type of unary and binary operations using \"type\ntrait\" structs. These specializations for vector types should be placed in the\nheader file associated with the `VectorImpl` specialization. For `DataVector`,\nthe specializations are defined in `DataStructures/DataVector.hpp`. The presence\nor absence of template specializations of these structs also determines the set\nof allowed operations between the vector type and other types. For example, if\nadding a `double` to a `DataVector` should be allowed and the result should be\ntreated as a `DataVector` for subsequent operations, then the struct\n`blaze::AddTrait<DataVector, double>` needs to be defined as follows:\n\n```\nnamespace blaze {\n// the `template <>` head tells the compiler that\n// `AddTrait<DataVector, double>` is a class template specialization\ntemplate <>\nstruct AddTrait<DataVector, double> {\n    // the `Type` alias tells blaze that the result should be treated like a\n    // `DataVector` for any further operations\n    using Type = DataVector;\n};\n}  // namespace blaze\n```\n\nNote that this only adds support for `DataVector + double`, not `double +\nDataVector`. To get the latter the following AddTrait specialization must be\ndefined\n\n```\nnamespace blaze {\n// the `template <>` head tells the compiler that\n// `AddTrait<double, DataVector>` is a class template specialization\ntemplate <>\nstruct AddTrait<double, DataVector> {\n    // the `Type` alias tells blaze that the result should be treated like a\n    // `DataVector` for any further operations\n    using Type = DataVector;\n};\n}  // namespace blaze\n```\n\nFour helper macros are defined to assist with generating the many\nspecializations that binary operations may require. Both of these macros must be\nput inside the blaze namespace for them to work correctly.\n\nThe first of these helper macros is\n`BLAZE_TRAIT_SPECIALIZE_BINARY_TRAIT(VECTOR_TYPE, BLAZE_MATH_TRAIT)`, which will\ndefine all of the pairwise operations (`BLAZE_MATH_TRAIT`) for the vector type\n(`VECTOR_TYPE`) with itself and for the vector type with its `value_type`. This\nreduces the three specializations similar to the above code blocks to a single\nline call,\n\n```\nnamespace blaze {\nBLAZE_TRAIT_SPECIALIZE_BINARY_TRAIT(DataVector, AddTrait)\n}  // namespace blaze\n```\n\nThe second helper macro is provided to easily define all of the arithmetic\noperations that will typically be supported for a vector type with its value\ntype. The macro is\n`VECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(VECTOR_TYPE)`, and defines all\nof:\n- `IsVector<VECTOR_TYPE>` to `std::true_type`\n- `TransposeFlag<VECTOR_TYPE>`, which informs Blaze of the interpretation of the\n  data as a \"column\" or \"row\" vector\n- `AddTrait` for the `VECTOR_TYPE` and its value type (3 Blaze struct\n  specializations)\n- `SubTrait` for the `VECTOR_TYPE` and its value type (3 Blaze struct\n  specializations)\n- `MultTrait` for the `VECTOR_TYPE` and its value type (3 Blaze struct\n  specializations)\n- `DivTrait` for the `VECTOR_TYPE` and its value type (3 Blaze struct\n  specializations)\n\nThis macro is similarly intended to be used in the `blaze` namespace and can\nsubstantially simplify these specializations for new vector types. For instance,\nthe call for `DataVector` is:\n\n```\nnamespace blaze {\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(DataVector)\n}  // namespace blaze\n```\n\nThe third helper macro is provided to define a combination of Blaze traits for\nsymmetric operations of a vector type with a second type (which may or may not\nbe a vector type). The macro is\n`BLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(VECTOR, COMPATIBLE, TRAIT)`, and\ndefines the appropriate trait for the two combinations `<VECTOR, COMPATIBLE>`\nand `<COMPATIBLE, VECTOR>`, and defines the result type to be `VECTOR`. For\ninstance, to support the multiplication of a `ComplexDataVector` with a\n`DataVector` and have the result be a `ComplexDataVector`, the following macro\ncall should be included in the `blaze` namespace:\n\n```\nnamespace blaze {\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, DataVector,\n                                               MultTrait);\n}  // namespace blaze\n```\n\nFinally, the fourth helper macro is provided to define all of the blaze traits\nwhich are considered either unary or binary maps. This comprises most named\nunary functions (like `sin()` or `sqrt()`) and named binary functions (like\n`hypot()` and `atan2()`). The macro\n`VECTOR_BLAZE_TRAIT_SPECIALIZE_ALL_MAP_TRAITS(VECTOR_TYPE)` broadly specializes\nall blaze-defined maps in which the given `VECTOR_TYPE` as the sole argument\n(for unary maps) or both arguments (for binary maps). This macro is also\nintended to be used in the blaze namespace. The call for `DataVector` is:\n\n```\nnamespace blaze {\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ALL_MAP_TRAITS(DataVector)\n}  // namespace blaze\n```\n\n# Supporting operations for `std::array`s of vectors {#array_vector_definitions}\n\nIn addition to operations between SpECTRE vectors, it is useful to gracefully\nhandle operations between `std::arrays` of vectors element-wise. There are\ngeneral macros defined for handling operations between array specializations:\n`DEFINE_STD_ARRAY_BINOP` and `DEFINE_STD_ARRAY_INPLACE_BINOP` from\n`Utilities/StdArrayHelpers.hpp`.\n\nIn addition, there is a macro for rapidly generating addition and subtraction\nbetween arrays of vectors and arrays of their data types. The macro\n`MAKE_STD_ARRAY_VECTOR_BINOPS(VECTOR_TYPE)` will define:\n- the element-wise `+` and `-` with `std::array<VECTOR_TYPE, N>` and\n  `std::array<VECTOR_TYPE, N>`\n- the element-wise `+` and `-` of either ordering of\n  `std::array<VECTOR_TYPE, N>` with `std::array<VECTOR_TYPE::value_type, N>`\n- the `+=` and `-=` of `std::array<VECTOR_TYPE, N>` with a\n  `std::array<VECTOR_TYPE, N>`\n- the `+=` and `-=` of `std::array<VECTOR_TYPE, N>` with a\n  `std::array<VECTOR_TYPE::value_type, N>`.\n\n# Equivalence operators {#Vector_type_equivalence}\n\nEquivalence operators are supported by the Blaze type inheritance. The\nequivalence operator `==` evaluates to true on a pair of vectors if they are the\nsame size and contain the same values, regardless of ownership.\n\n# MakeWithValueImpl {#Vector_MakeWithValueImpl}\n\nSpECTRE offers the convenience function `make_with_value` for various types. The\ntypical behavior for a SpECTRE vector type is to create a new vector type of the\nsame type and length initialized with the value provided as the second argument\nin all entries. This behavior may be created by placing the macro\n`MAKE_WITH_VALUE_IMPL_DEFINITION_FOR(VECTOR_TYPE)` in the .hpp file. Any other\nspecializations of `MakeWithValueImpl` will need to be written manually.\n\n# Interoperability with other data types {#Vector_tensor_and_variables}\n\nWhen additional vector types are added, small changes are necessary if they are\nto be used as the base container type either for `Tensor`s or for `Variables` (a\n`Variables` contains `Tensor`s), which contain some vector type.\n\nIn `Tensor.hpp`, there is a `static_assert` which white-lists the possible types\nthat can be used as the storage type in `Tensor`s. Any new vectors must be added\nto that white-list if they are to be used within `Tensor`s.\n\n`Variables` is templated on the storage type of the stored `Tensor`s. However,\nany new data type should be appropriately tested. New vector types should be\ntested by invoking new versions of existing testing functions templated on the\nnew vector type, rather than `DataVector`.\n\n# Writing tests {#Vector_tests}\n\nIn addition to the utilities for generating new vector types, there are a number\nof convenience functions and utilities for easily generating the tests necessary\nto verify that the vectors function appropriately. These utilities are in\n`VectorImplTestHelper.hpp`, and documented individually in the\nTestingFrameworkGroup. Presented here are the salient details for rapidly\nassembling basic tests for vectors.\n\n## Utility check functions\nEach of these functions is intended to encapsulate a single frequently used unit\ntest and is templated (in order) on the vector type and the value type to be\ngenerated. The default behavior is to uniformly sample values between -100 and\n100, but alternative bounds may be passed in via the function arguments.\n\n### `TestHelpers::VectorImpl::vector_test_construct_and_assign()`\n This function tests a battery of construction and assignment operators for the\n vector type.\n\n### `TestHelpers::VectorImpl::vector_test_serialize()`\nThis function tests that vector types can be serialized and deserialized,\nretaining their data.\n\n### `TestHelpers::VectorImpl::vector_test_ref()`\nThis function tests the `set_data_ref` method of sharing data between vectors,\nand that the appropriate owning flags and move operations are handled correctly.\n\n### `TestHelpers::VectorImpl::vector_test_math_after_move()`\nTests several combinations of math operations and ownership before and after use\nof `std::move`.\n\n### `TestHelpers::VectorImpl::vector_ref_test_size_error()`\nThis function intentionally generates an error when assigning values from one\nvector to a differently sized, non-owning vector (made non-owning by use of\n`set_data_ref`). The assertion test which calls this function should search for\nthe string \"Must copy/move/assign into same size\". Three forms of the test are\nprovided, which are switched between using a value from the enum\n`RefSizeErrorTestKind` in the first function argument:\n- `RefSizeErrorTestKind::Copy`: tests that the size error is appropriately\n  generated when copying to a non-owning vector of the wrong size. This has\n  \"copy\" in the message.\n- `RefSizeErrorTestKind::ExpressionAssign`: tests that the size error is\n  appropriately generated when assigning the result of a mathematical expression\n  to a non-owning vector of the wrong size. This has \"assign\" in the message.\n- `RefSizeErrorTestKind::Move`: tests that the size error is appropriately\n  generated when a vector is `std::move`d into a non-owning vector of the wrong\n  size. This has \"move\" in the message.\n\n## `TestHelpers::VectorImpl::test_functions_with_vector_arguments()`\n\nThis is a general function for testing the mathematical operation of vector\ntypes with other vector types and/or their base types, with or without various\nreference wrappers. This may be used to efficiently test the full set of\npermitted math operations on a vector. See the documentation of\n`test_functions_with_vector_arguments()` for full usage details.\n\nAn example simple use case for the math test utility:\n\\snippet Test_DataVector.cpp test_functions_with_vector_arguments_example\n\nMore use cases of this functionality can be found in `Test_DataVector.cpp`.\n\n# Vector storage nuts and bolts {#Vector_storage}\n\nInternally, all vector classes inherit from the templated `VectorImpl`, which\ninherits from a `blaze::CustomVector`. Most of the mathematical operations are\nsupported through the Blaze inheritance, which ensures that the math operations\nexecute the optimized forms in Blaze.\n\nBlaze also offers the possibility of restricting operations via `groups` in the\n`blaze::CustomVector` template arguments.\nCurrently, we do not use the `blaze::GroupTag` functionality to determine\navailable operations for vectors, but in principle this feature could allow us\nto further simplify our operator choice logic in the SpECTRE vector code.\n\nSpECTRE vectors can be either \"owning\" or \"non-owning\". If a vector is owning,\nit allocates and controls the data it has access to, and is responsible for\neventually freeing that data when the vector goes out of scope. If the vector is\nnon-owning, it acts as a (possibly complete) \"view\" of otherwise allocated\nmemory. Non-owning vectors do not manage memory, nor can they change size. The\ntwo cases of data ownership cause the underlying data to be handled fairly\ndifferently, so we will discuss each in turn.\n\nWhen a SpECTRE vector is constructed as owning, or becomes owning, its memory\nis allocated in one of two ways.\n\n1. The size of the vector is larger than the `StaticSize` template parameter to\n   `VectorImpl`. In that case, it allocates its own block of memory of\n   appropriate size, and stores a pointer to that memory in a `std::unique_ptr`\n   named `owned_data_`. The `std::unique_ptr` ensures that the SpECTRE vector\n   needs to perform no further direct memory management, and that the memory\n   will be appropriately managed whenever the `std::unique_ptr owned_data_`\n   member is deleted or moved.\n2. The size of the vector is less than or equal to the `StaticSize` template. In\n   this case, the data is stored on the stack in a `std::array<T, StaticSize>`\n   member variable called `static_owned_data_`. Since it is on the stack, this\n   doesn't require any memory management by the user.\n\nIn either case, the base `blaze::CustomVector` must also be told about the\npointer, which is always accomplished by calling the protected function\n`VectorImpl.reset_pointer_vector(const size_t set_size)`, which sets the\n`blaze::CustomVector` internal pointer to either the pointer obtained by\n`std::unique_pointer.get()` or the pointer obtained by `std::array.data()`\ndepending on the size of the vector.\n\nWhen a SpECTRE vector is constructed as non-owning by the `VectorImpl(ValueType*\nstart, size_t set_size)` constructor, or becomes non-owning by the\n`set_data_ref` function, neither the internal `std::unique_ptr` named\n`owned_data_` nor the internal `std::array` named `static_owned_data_`\npoints to the data represented by the vector and both can be thought of as\n\"inactive\" for the purposes of computation and memory management. This behavior\nis desirable, because otherwise the `std::unique_ptr` would attempt to free\nmemory that is presumed to be also used elsewhere, causing difficult to diagnose\nmemory errors. And we needn't worry about the `std::array` because it's\nallocated on the stack. The non-owning SpECTRE vector updates the base\n`blaze::CustomVector` pointer directly by calling `blaze::CustomVector.reset`\nfrom the derived class (on itself).\n"
  },
  {
    "path": "docs/DevGuide/ImportingData.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Importing data {#dev_guide_importing}\n\n\\tableofcontents\n\nThe `importers` namespace holds functionality for importing data into SpECTRE.\nWe currently support loading volume data files in the same format that is\nwritten by the `observers`.\n\n## Importing volume data\n\nThe `importers::ElementDataReader` parallel component is responsible for loading\nvolume data, interpolating it, and distributing it to elements of one or\nmultiple array parallel components. As a first step, make sure you have added\nthe `importers::ElementDataReader` to your `Metavariables::component_list`. Also\nmake sure you have a `Parallel::Phase` in which you will perform\nregistration with the importer, and another in which you want to load the data.\nHere's an example for such `Metavariables`:\n\n\\snippet Test_VolumeDataReaderAlgorithm.hpp metavars\n\nTo load volume data from a file, write an action in which you invoke\n`importers::Actions::ReadAllVolumeDataAndDistribute` on the\n`importers::ElementDataReader`. For simple use cases we provide\n`importers::Actions::ReadVolumeData`, which can be added to the\n`phase_dependent_action_list` of your element array and which will generate\ninput-file options for you. Here's an example that will be explained in more\ndetail below:\n\n\\snippet Test_VolumeDataReaderAlgorithm.hpp import_actions\n\n- The `importers::Actions::ReadVolumeData` action specifies input-file options\n  and dispatches to `importers::Actions::ReadAllVolumeDataAndDistribute` on the\n  `importers::ElementDataReader` nodegroup component. It loads the volume data\n  file once per node on its first invocation. Subsequent invocations of these\n  actions, e.g. from all other elements on the node, will do nothing. The data\n  is distributed into the inboxes of all elements on the node under the\n  `importers::Tags::VolumeData` tag using `Parallel::receive_data`.\n- The `importers::Actions::ReceiveVolumeData` action waits for the volume data\n  to be available and directly moves it into the DataBox. If you wish to verify\n  or post-process the data before populating the DataBox, use your own\n  specialized action in place of `importers::Actions::ReceiveVolumeData`.\n- You need to register the elements of your array parallel component for\n  receiving volume data. To do so, invoke the\n  `importers::Actions::RegisterWithElementDataReader` action in an earlier\n  phase, as shown in the example above.\n\nThe parameters passed to `importers::Actions::ReadAllVolumeDataAndDistribute`\nspecify the volume data to load. See the documentation of\n`importers::Actions::ReadAllVolumeDataAndDistribute` for details. In the example\nabove, we use `importers::Actions::ReadVolumeData` to generate the input-file\noptions for us and place them in an option group:\n\n\\snippet Test_VolumeDataReaderAlgorithm.hpp option_group\n\nThis results in a section in the input file that may look like this:\n\n\\snippet Test_VolumeDataReaderAlgorithm2D.yaml importer_options\n"
  },
  {
    "path": "docs/DevGuide/LoadBalancingNotes.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Notes on SpECTRE load-balancing using Charm++'s built-in load balancers {#load_balancing_notes}\n\n\\tableofcontents\n\nThe goal of load-balancing (LB) is to ensure that HPC resources are well-used\nwhile performing large inhomogeneous simulations. In 2020-2021, Jordan Moxon\nand Francois Hebert performed a number of tests using Charm++'s built-in load\nbalancers with SpECTRE.\n\nThese notes highlight the key points and give (at the bottom) some general\nrecommendations.\n\n### Overview of how LBs work with SpECTRE\n\nIn late 2020 FH tested Charm's LBs on simple, homogeneous, SpECTRE test cases.\nThese tests reveal the following broad behavior patterns:\n- Without using any LBs (no LB command-line args, or `+balancer NullLB`),\n  SpECTRE's performance depends sensitively on how the DG elements are\n  distributed over the HPC system. This indicates that communication costs are\n  very important in SpECTRE runs. This statement remains true for more expensive\n  evolution systems like Generalized Harmonic.\n- Charm's LBs that are not communications aware (e.g., `GreedyLB`, `RefineLB`,\n  ...) all result in low parallel efficiency (20-30%). This is consistent with\n  the understanding that communication costs are large. A good initial\n  distribution of elements over processors will be degraded by these LBs,\n  leading to more complicated communication graph and loss of performance.\n- Some of Charm's communication-aware LBs perform well: they approach the\n  efficiency of a \"manually tuned\" initial distribution of elements onto\n  processors. This suggests these LBs do a good job of partitioning the\n  communications graph. In FH's simple tests, the best results were from\n  `RecBipartLB`, which came within 10-20% of a manual initial distribution.\n  However, it is a slow algorithm and is best used infrequently or only a few\n  times near the start of the simulation.\n\nNote that at the 2020 Charm++ Workshop, the Charm team recommended that we use\n`MetisLB`, or that we combined `MetisLB` with `RefineLB` (syntax:\n`+balancer MetisLB +balancer RefineLB`, which applies the first balancer on the\nfirst invocation and the second balancer on all subsequent invocations, to\n'polish' the results of the first LB). In practice, this failed for two reasons:\n- `MetisLB` tends to error with FPEs\n- When falling back to the pairing of `RecBipartLB` followed by `RefineLB`, the\n  run starts with good performance. However, within a few applications of\n  `RefineLB`, the performance is heavily degraded (down to 20-30% efficiency).\n  It appears that we should stick to the comm-aware LB strategies.\n\n### Contaminated LB measurements on first invocation\n\nThere is reason to suspect that the Charm load balancing may incorrectly balance\nthe load when applied near the start of some simulations. This is because the\n'one-time' setup of the system may involve nontrivial computation, and (e.g.\nfor numeric initial data) communication patterns between components that differ\nsignificantly from the patterns during evolution. Then, the first balancer\ninvocation, based partially on the measurements taken during the\nnon-generalizable initialization phase, can give rise to a poorly-chosen balance\nand injure performance.\nThis is the suspected cause of poor performance that has been noticed\nin cases of homogeneous load and numeric initial data in Generalized Harmonic\ntests performed by Geoffrey Lovelace. JM confirmed the problem.\nIt appears that the balance is not similarly degraded when using cases that do\nnot involve numeric initial data -- some basic 2-node re-tests with Generalized\nHarmonic by JM seem to produce useful balance (improved performance) when not\nusing numeric initial data.\n\nThis issue has not been investigated to a completely satisfactory conclusion,\nthough the above explanation seems most plausible.\n\nIn cases for which it appears that the LB data is problematically impacted by\nthe set up of the evolution system, we can try two main strategies to mitigate\nthe problem:\n- Apply the load balancer at least two times near the start of the simulation,\n  with sufficient gaps to collect useful balancing information. The LB database\n  in Charm is cleared every time a balance is applied, so the later balances\n  during the evolution should be uncontaminated. This strategy has not yet\n  been carefully tested. To do this, use an input file similar to\n```\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [5, 10, 15]\n    PhaseChanges:\n      - VisitAndReturn(LoadBalancing)\n```\n- Use `LBTurnInstrumentOff` and `LBTurnInstrumentOn` to specifically exclude\n  setup procedures from the LB instrumentation. First attempts indicate that\n  this process might be challenging to accomplish correctly, and may require\n  correspondence with the Charm developers to clarify at what points in the\n  code execution those commands may be used, and precisely how they affect\n  the load-balancing database. A first attempt by JM was to turn instrumentation\n  off during array element construction, then turn instrumentation on for each\n  element during the start of the `Evolve` phase, but that attempt led to a\n  hang of the system, so the utility must have more constraints than were\n  initially apparent.\n\n### Scotch load balancer\n\nJM tested `ScotchLB`, and found better performance than with `RecBipartLB`. The\nmargin varied a great deal among the number of nodes used, but at multiple\npoints tested, the runtime was less than 65% of the `RecBipartLB` runtime.\nThe tests were performed with homogeneous load, but starting from the\nround-robin element distribution. The indication is therefore that `ScotchLB`\nis very effective at minimizing communication costs.\n\nHowever, in practical applications, JM found that the `ScotchLB` often generates\nFPEs during the graph partition step and causes the simulation to crash.\nThe issue [charm++ issue #3401](https://github.com/UIUC-PPL/charm/issues/3401)\ntracks the progress to determine the cause of the problem and fix it in Charm.\nThe source of the problem has largely been identified, but the fix is still\npending.\n\n`ScotchLB` will likely replace `RecBipartLB` as the most-frequently recommended\ncentralized communication based balancer for SpECTRE once the FPE bugs have\nbeen fixed.\n\n### General recommendations\n\n#### Homogeneous loads\n\nFor homogeneous loads, it is likely best to omit load-balancing and just use\nthe z-curve distribution (default) to give a good initial distribution and use\nthat for the entire evolution. This means calling the SpECTRE executable with\nno LB-related command-line args, or with `+balancer NullLB`.\n\nYou may find modest gains from using a communication-based load balancer, but\nlikely only from the 'extra' parallel components of the system that cause the\nload to be not completely homogeneous (e.g., components like the interpolator\nor horizon finder).\nIf you need a very long evolution or intend to submit a large number of\nevolutions, it may be worth experimenting to see whether 1-3 applications of\n`RecBipartLB` (or `ScotchLB` once its bugs are fixed, see above) improve\nperformance for the system, for instance by using the input file:\n```\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [5, 10, 15]\n    PhaseChanges:\n      - VisitAndReturn(LoadBalancing)\n```\nand command-line args `+balancer RecBipartLB` (or `ScotchLB` when its\nbugs are fixed). This may be particularly relevant for cases with numeric\ninitial data or other complicated set-up procedures.\n\n#### Inhomogeneous loads\n\nBased on our experiments, we anticipate that using a load-balancer may\nsignificantly improve runtimes with inhomogeneous loads. Our testing on this\ncase is far more sparse, but for SpECTRE executables, it is probably remains\ntrue that managing communication costs will be an important goal for the\nbalancer. It is likely worth attempting the evolution with a\nperiodically-applied centralized communication-aware balancer, e.g.:\n```\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1000\n          Offset: 5\n    PhaseChanges:\n      - VisitAndReturn(LoadBalancing)\n```\npaired with command-line args `+balancer RecBipartLB` (or `ScotchLB` when its\nbugs are fixed).\n\nImportant considerations when choosing the interval with which to balance are:\n- you will want to ensure that the balancer is applied frequently enough to\n  prioritize expensive parts of the simulation before any relevent features\n  'move' to other elements. For example, if a shock is moving across the\n  simulation domain causing certain elements to be more expensive to compute,\n  you want to balance often enough that the LB 'keeps up' with the movement of\n  the shock.\n- you will want to avoid balancing so frequently that the synchronization\n  and balancer calculation itself becomes a significant portion of runtime.\n\nWe have not yet taken much detailed data on using the load-balancers for\ninhomogeneous loads, so more detailed tests determining their efficacy would be\nvaluable.\n"
  },
  {
    "path": "docs/DevGuide/Observers.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Observers Infrastructure {#observers_infrastructure_dev_guide}\n\n\\tableofcontents\n\nThe observers infrastructure works with two parallel components: a group and a\nnodegroup. We have two types of observations: `Reduction` and `Volume` (see the\nenum `observers::TypeOfObservation`). `Reduction` data is anything that is\nwritten once per time/integral identifier per simulation. Some examples of\nreduction data are integrals or L2 norms over the entire domain, integrals or L2\nnorms over part of the domain, and integrals over lower-dimensional surfaces\nsuch as apparent horizons or slices through the domain. Volume data is anything\nthat has physical extent, such as any of the evolved variables (or derived\nquantities thereof) across all or part of the domain, or quantities on\nlower-dimensional surfaces in the domain (e.g. the rest mass density in the\nxy-plane). Reduction and volume data both use the group and nodegroup for\nactually getting the data to disk, but do so in a slightly different manner.\n\n### Reduction Data\n\nReduction data requires combining information from many or all cores of a\nsupercomputer to get a single value. Reductions are tagged by some temporal\nvalue, which for hyperbolic systems is the time and for elliptic systems some\ncombination of linear and non-linear iteration count. The reduction data is\nstored in an object of type `Parallel::ReductionData`, which takes as template\nparameters a series of `Parallel::ReductionDatum`. A `Parallel::ReductionDatum`\ntakes as template parameters the type of the data and operators that define how\ndata from the different cores are to be combined to a single value. See the\nparagraphs below for more detail, and the documentation of\n`Parallel::ReductionDatum` for examples.\n\nAt the start of a simulation, every component and event that wants to perform a\nreduction for observation, or will be part of a reduction observation, must\nregister with the `observers::Observer` component. The `observers::Observer` is\na group, which means there is one per core. The registration is used so that the\n`Observer` knows once all data for a specific reduction (both in time and by\nname/ID) has been contributed. Reduction data is combined on each core as it is\ncontributed by using the binary operator from `Parallel::ReductionDatum`'s\nsecond template parameter. Once all the data is collected on the core, it is\ncopied to the local `observers::ObserverWriter` nodegroup, which keeps track of\nhow many of the cores on the node will be contributing to a specific\nobservation, and again combines all the data as it is being contributed. Once\nall the node's data is collected to the nodegroup, the data is sent to node `0`\nwhich combines the reduction data as it arrives using the binary operator from\n`Parallel::ReductionDatum`'s second template parameter. Using node `0` for\ncollecting the final reduction data is an arbitrary choice, but we are always\nguaranteed to have a node `0`.\n\nOnce all the reductions are received on node `0`, the `ObserverWriter` invokes\nthe `InvokeFinal` (third) template parameter on each `Parallel::ReductionDatum`\n(this is the n-ary) in order to finalize the data before writing. This is used,\nfor example, for dividing by the total number of grid points in an L1 or L2\nnorm. The reduction data is then written to an HDF5 file whose name is set in\nthe input file using the option\n`observers::Tags::ReductionFileName`. Specifically, the data is written into an\n`h5::Dat` subfile since, along with the data, the subfile name must be passed\nthrough the reductions.\n\nThe actions used for registering reductions are\n`observers::Actions::RegisterEventsWithObservers` and\n`observers::Actions::RegisterWithObservers`. There is a separate `Registration`\nphase at the beginning of all simulations where everything must register with\nthe observers. The action `observers::Actions::ContributeReductionData` is used\nto send data to the `observers::Observer` component in the case where there is a\nreduction done across an array or subset of an array. If a singleton parallel\ncomponent or a specific chare needs to write data directly to disk it should use\nthe `observers::ThreadedActions::WriteReductionDataRow` action called on the\nzeroth element of the `observers::ObserverWriter` component.\n\n### Volume Data\n\nVolume data is vaguely defined as anything that has some extent. For example, in\na 3d simulation, data on 2d surfaces is still considered volume data for the\npurposes of observing data. The spectral coefficients can also be written as\nvolume data, though some care must be taken in that case to correctly identify\nwhich mode is associated with which terms in the basis function\nexpansion. Whatever component will contribute volume data to be written must\nregister with the `observers::Observer` component (there currently isn't tested\nsupport for directly registering with the `observers::ObserverWriter`). This\nregistration is the same as in the reduction data case.\n\nOnce the observers are registered, data is contributed to the\n`observers::Observer` component using the\n`observers::Actions::ContributeVolumeData` action. The data is packed into an\n`ElementVolumeData` object that carries `TensorComponent`s on a grid.\nInformation on the grid, such as its extents, basis and quadrature, are stored\nalongside the `TensorComponent`s. Once all the elements on a single core have\ncontributed their volume data to the `observers::Observer` group, the\n`observers::Observer` group moves its data to the `observers::ObserverWriter`\ncomponent to be written. We write one file per node, appending the node ID to\nthe HDF5 file name to distinguish between files written by different nodes. The\nHDF5 file name is specified in the input file using the\n`observers::Tags::VolumeFileName` option. The data is written into a subfile of\nthe HDF5 file using the `h5::VolumeFile` class.\n\nIf a singleton parallel component or a specific chare needs to write volume data\ndirectly to disk, such as surface data from an apparent horizon, it should use\nthe `observers::ThreadedActions::WriteVolumeData` action called on the zeroth\nelement of the `observers::ObserverWriter` component. For surface data (such as\noutput from horizon finds), this data should be written to a file specified by\nthe `observers::Tags::SurfaceFileName` option.\n\n### Threading and NodeLocks\n\nSince the `observers::ObserverWriter` class is a nodegroup, its entry methods\ncan be invoked simultaneously on different cores of the node. However, this can\nlead to race conditions if care isn't taken. The biggest caution is that the\n`DataBox` cannot be mutated on one core and simultaneously accessed on\nanother. This is because in order to guarantee a reasonable state for data in\nthe `DataBox`, it must be impossible to perform a `db::get` on a `DataBox` from\ninside or while a `db::mutate` is being done. What this means in practice is\nthat all entry methods on a nodegroup must put their `DataBox` accesses inside\nof a `node_lock.lock()` and `node_lock.unlock()` block. To achieve better\nparallel performance and threading, the amount of work done while the entire\nnode is locked should be minimized. To this end, we have additional locks. One\nfor the HDF5 files because we do not require a threadsafe HDF5\n(`observers::Tags::H5FileLock`). We also have locks for the objects mutated when\ncontributing reduction data (`observers::Tags::ReductionDataLock`).\n\n### Future changes\n- It would be preferable to make the `Observer` and `ObserverWriter` parallel\n  components more general and have them act as the core (node)group. Since any\n  simple actions can be run on them, it should be possible to use them for most,\n  if not all cases where we need a (node)group.\n"
  },
  {
    "path": "docs/DevGuide/OptionParsing.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Option Parsing {#dev_guide_option_parsing}\n\n\\tableofcontents\n\nSpECTRE can read YAML configuration files at runtime to set parameters\nand choose between classes implementing an interface.  %Options are\nparsed during code initialization and can be used to construct objects\nplaced in the Parallel::GlobalCache and options passed to the\nparallel components.  The types necessary to mark objects for parsing\nare declared in `Options/Options.hpp`.\n\n## Metadata and options\n\nYAML input files begin with a metadata section, terminated by `---`, and\nfollowed by the executable options:\n\n```yaml\n# Metadata here\nDescription: |\n  Briefly describe the configuration and link to papers for details.\n---\n# Options start here\n```\n\nThe metadata section may also be empty:\n\n```yaml\n---\n---\n# Options start here\n```\n\nYou only need the leading `---` marker if the metadata section is empty. This is\nYAML's \"document start marker\" (see the [YAML spec](https://yaml.org/spec/1.2)).\nAny metadata fields at the beginning of the file also imply the start of a\ndocument, so you don't need the first `---` marker.\n\nMetadata provide information for tools, whereas options provide information to\nthe executable. See tools like `CheckOutputFiles` for details on the metadata\nfields that they use. Metadata can also provide information on how to run the\ninput file, such as the name and version of the executable, and a description\nthat may refer to published papers for details on the configuration.\nOptions are defined by the executable and detailed below.\n\n## General option format\n\nAn option is defined by an \"option tag\", represented by a `struct`.  At minimum,\nthe struct must declare the type of the object to be parsed and provide a brief\ndescription of the meaning.  The name of the option in the input file\ndefaults to the name of the struct (excluding any template parameters\nand scope information), but can be overridden by providing a static\n`name()` function.  Several other pieces of information, such as\nsuggestions, limits and grouping, may be provided if desired.  This\ninformation is all included in the generated help output.\n\nIf an option has a suggested value, the value is specified in the\ninput file as usual, but a warning will be issued if the specified\nvalue does not match the suggestion.\n\nExamples:\n\\snippet Options/Test_Options.cpp options_example_scalar_struct\n\\snippet Options/Test_Options.cpp options_example_vector_struct\n\nThe option type can be any type understood natively by yaml-cpp\n(fundamentals, `std::string`, and `std::map`, `std::vector`,\n`std::list`, `std::array`, and `std::pair` of parsable types) and\ntypes SpECTRE adds support for.  SpECTRE adds `std::unordered_map`\n(but only with ordered keys), `std::variant` (with alternatives tested\nin order), and various classes marked as constructible in their\ndeclarations.\n\nAn option tag can be placed in a group by adding a `group` type alias to the\nstruct. The alias should refer to a type that, like option tags, defines a help\nstring and may override a static `name()` function.\n\nExample:\n\\snippet Options/Test_Options.cpp options_example_group\n\n## Constructible classes\n\nA class that defines `static constexpr Options::String help` and a\ntypelist of option structs `options` can be created by the option\nparser.  When the class is requested, the option parser will parse\neach of the options in the `options` list, and then supply them to the\nconstructor of the class.  A class can use Options::Alternatives to\nsupport more than one possible set of options for its creation.  (See\n[Custom parsing](#custom-parsing) below for more general creation\nmechanisms.)\n\nUnlike option descriptions, which should be brief, the class help\nstring has no length limits and should give a description of the class\nand any necessary discussion of its options beyond what can be\ndescribed in their individual help strings.\n\nCreatable classes must be default constructible and move assignable.\n\nThe `Options::Context` is an optional argument to the constructor that should be\nused when the constructor checks for validity of the input. If the input is\ninvalid, `PARSE_ERROR` is used to propagate the error message back through the\noptions ensuring that the error message will have a full backtrace so it is easy\nfor the user to diagnose.\n\nExample:\n\\snippet Options/Test_CustomTypeConstruction.cpp class_creation_example\n\nClasses may use the Metavariables struct, which is effectively the compile time\ninput file, in their parsing by templating the `options` type alias or by taking\nthe Metavariables as a final argument to the constructor (after the\n`Options::Context`).\n\nExample:\n\\snippet Options/Test_CustomTypeConstruction.cpp class_creation_example_with_metavariables\n\n## Factory\n\nThe factory interface creates an object of type\n`std::unique_ptr<Base>` containing a pointer to some class derived\nfrom `Base`.  The list of creatable derived classes is specified in\nthe `factory_creation` struct in the metavariables, which must contain\na `factory_classes` type alias that is a `tmpl::map` from base classes\nto lists of derived classes:\n\\snippet Options/Test_Factory.cpp factory_creation\n\nWhen a `std::unique_ptr<Base>` is requested, the factory will expect a\nsingle YAML argument specifying the name of the class (as given by a\nstatic `name()` function or, lacking that, the actual class name).  If\nthe derived class takes no arguments, the name can be given as a YAML\nstring, otherwise it must be given as a single key-value pair, with\nthe key the name of the class.  The value portion of this pair is then\nused to create the requested derived class in the same way as an\nexplicitly constructible class.  Examples:\n\\snippet Options/Test_Factory.cpp factory_without_arguments\n\\snippet Options/Test_Factory.cpp factory_with_arguments\n\n\\anchor custom-parsing\n## Custom parsing\n\nOccasionally, the requirements imposed by the default creation\nmechanism are too stringent.  In these cases, the construction\nalgorithm can be overridden by providing a specialization of the\nstruct\n\\code{cpp}\ntemplate <typename T>\nstruct Options::create_from_yaml {\n  template <typename Metavariables>\n  static T create(const Options::Option& options);\n};\n\\endcode\nThe create function can perform any operations required to construct\nthe object.\n\nExample of using a specialization to parse an enum:\n\\snippet Options/Test_CustomTypeConstruction.cpp enum_creation_example\n\nNote that in the case where the `create` function does *not* need to use the\n`Metavariables` it is recommended that a general implementation forward to an\nexplicit instantiation with `void` as the `Metavariables` type. The reason for\nusing `void` specialization is to reduce compile time. Since we only need one\nfull implementation of the function independent of what type `Metavariables` is,\nwe should only parse and compile it once. By having a specialization on `void`\n(or some other non-metavariables type like `NoSuchType`) we can handle the\nmetavariables-independent case efficiently. As a concrete example, the general\ndefinition and forward declaration of the `void` specialization in the header\nfile would be:\n\n\\snippet Options/Test_CustomTypeConstruction.cpp enum_void_creation_header_example\n\nwhile in the `cpp` file the definition of the `void` specialization is:\n\n\\snippet Options/Test_CustomTypeConstruction.cpp enum_void_creation_cpp_example\n"
  },
  {
    "path": "docs/DevGuide/ParallelExecutable/Concepts.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Parallelization in SpECTRE {#tutorial_parallel_concepts}\n\n\\tableofcontents\n\nThis overview describes the concepts and terminology that SpECTRE uses\nto enable parallelism.  This overview is a general discussion with no\ncode examples.  Subsequent tutorials will provide a more in-depth\nexploration of the parallelization infrastructure, including code\nexamples.\n\nUnlike many parallel scientific codes which use data-based\nparallelism, SpECTRE uses task-based parallelism.  The classical\nstrategy for parallelism (data-based parallelism) is to assign a\nportion of the data to processes (or threads) that synchronously\nexecute compute kernels.  This is implemented in many codes but it is\ndifficult to design codes with this strategy that will efficiently\nscale for complex multi-scale, multi-physics workloads. Task-based\nparallelism provides a solution: Instead of dividing work between\nparallel processes based on data ownership, there is a set of tasks\nand their inter-dependencies. Tasks are scheduled and assigned to\nprocesses dynamically, providing opportunities for load balancing and\nminimization of idle threads.  By dividing the program into small\nenough tasks such that you have several tasks per thread,\ncommunication time is hidden by interleaving tasks that are ready to\nbe executed with tasks that are waiting for data.\n\nIn order to implement task-based parallelism, SpECTRE is built on top\nof the parallel programming framework of the Charm++ library, which is\ndeveloped by the [Parallel Programming\nLaboratory](http://charm.cs.illinois.edu/) at the University of\nIllinois.  Charm++ is a mature parallel programming framework that\nprovides intra-node threading and can use a variety of communication\ninterfaces (including MPI) to communicate between nodes.  Charm++ has\na large user base, which includes users of the cosmological\n\\f$N\\f$-body code\n[ChaNGa](https://github.com/N-BodyShop/changa/wiki/ChaNGa) and of the\nmolecular dynamics code\n[NAMD](https://www.ks.uiuc.edu/Research/namd/).\n\n## Charm++ basic concepts\n\nIn order to understand how parallelization works in SpECTRE, it is\nuseful to understand the basic concepts in the design of Charm++.\nMuch of the following is quoted verbatim from the [Charm++\ndocumentation](https://charm.readthedocs.io), interspersed with\ncomments on how SpECTRE interacts with Charm++.\n\n> Charm++ is a C++-based parallel programming system, founded on the\n> migratable-objects programming model, and supported by a novel and\n> powerful adaptive runtime system. It supports both irregular as well\n> as regular applications, and can be used to specify task-parallelism\n> as well as data parallelism in a single application. It automates\n> dynamic load balancing for task-parallel as well as data-parallel\n> applications, via separate suites of load-balancing strategies. Via\n> its message-driven execution model, it supports automatic latency\n> tolerance, modularity and parallel composition. Charm++ also supports\n> automatic checkpoint/restart, as well as fault tolerance based on\n> distributed checkpoints.\n\nSpECTRE currently wraps only some of the features of Charm++,\nprimarily the ones that support task-parallelism.  We are just\nbeginning our exploration of dynamic load balancing.  Coming soon we\nwill utilize automatic checkpoint/restart.  At present we do not use\nCharm++ support for fault tolerance.\n\n> The key feature of the migratable-objects programming model is\n> over-decomposition: The programmer decomposes the program into a\n> large number of work units and data units, and specifies the\n> computation in terms of creation of and interactions between these\n> units, without any direct reference to the processor on which any\n> unit resides. This empowers the runtime system to assign units to\n> processors, and to change the assignment at runtime as necessary.\n\nSpECTRE's parallelization module is designed to make it easy for users\nto exploit the migratable-object model by providing a framework to\ndefine the units into which a program can be decomposed.\n\n> A basic unit of parallel computation in Charm++ programs is a\n> chare.  At its most basic level, it is just a C++ object. A Charm++\n> computation consists of a large number of chares distributed on\n> available processors of the system, and interacting with each other\n> via asynchronous method invocations. Asynchronously invoking a\n> method on a remote object can also be thought of as sending a\n> “message” to it. So, these method invocations are sometimes referred\n> to as messages. (besides, in the implementation, the method\n> invocations are packaged as messages anyway). Chares can be created\n> dynamically.\n\nIn SpECTRE, we wrap Charm++ chares in struct templates that we call\n__parallel components__ that represent a collection of distributed\nobjects.  We wrap the asynchronous method invocations between the\nelements of parallel components in struct templates that we call\n__actions__.  Thus, each element of a parallel component can be\nthought of as a C++ object that exists on one core on the\nsupercomputer, and an action as calling a member function of that\nobject, even if the caller is on another core.\n\n> Conceptually, the system maintains a “work-pool” consisting of seeds\n> for new chares, and messages for existing chares. The Charm++\n> runtime system (Charm RTS) may pick multiple items,\n> non-deterministically, from this pool and execute them, with the\n> proviso that two different methods cannot be simultaneously\n> executing on the same chare object (say, on different\n> processors). Although one can define a reasonable theoretical\n> operational semantics of Charm++ in this fashion, a more practical\n> description of execution is useful to understand Charm++. A Charm++\n> application’s execution is distributed among Processing Elements\n> (PEs), which are OS threads or processes depending on the selected\n> Charm++ build options. On each PE, there is a scheduler operating\n> with its own private pool of messages. Each instantiated chare has\n> one PE which is where it currently resides. The pool on each PE\n> includes messages meant for chares residing on that PE, and seeds\n> for new chares that are tentatively meant to be instantiated on that\n> PE. The scheduler picks a message, creates a new chare if the\n> message is a seed (i.e. a constructor invocation) for a new chare,\n> and invokes the method specified by the message. When the method\n> returns control back to the scheduler, it repeats the cycle\n> (i.e. there is no pre-emptive scheduling of other invocations).\n\nIt is very important to keep in mind that the actions that are\nexecuted on elements of parallel components are done so\nnon-deterministically by the run-time system.  Therefore it is the\nresponsibility of the programmer to ensure that actions are not called\nout of order.  This means that if action B must be executed after\naction A on a given element of a parallel component, the programmer\nmust ensure that either action B is called after the completion of\naction A (i.e. it is not sufficient that action B is invoked after\naction A is invoked), or that action B checks that action A has\ncompleted before running.\n\n> When a chare method executes, it may create method invocations for\n> other chares. The Charm Runtime System (RTS) locates the PE where\n> the targeted chare resides, and delivers the invocation to the\n> scheduler on that PE.\n\nIn SpECTRE, this is done by one element of a parallel component\ncalling an action on an element of a another parallel component.\n\n> Methods of a chare that can be remotely invoked are called entry\n> methods. Entry methods may take serializable parameters, or a\n> pointer to a message object. Since chares can be created on remote\n> processors, obviously some constructor of a chare needs to be an\n> entry method. Ordinary entry methods are completely non-preemptive-\n> Charm++ will not interrupt an executing method to start any other\n> work, and all calls made are asynchronous.\n\nIn SpECTRE, the struct template that defines a parallel component has\ntaken care of creating the entry methods for the underlying chare,\nwhich are then called by invoking actions.\n\n> Charm++ provides dynamic seed-based load balancing. Thus location\n> (processor number) need not be specified while creating a remote\n> chare. The Charm RTS will then place the remote chare on a suitable\n> processor. Thus one can imagine chare creation as generating only a\n> seed for the new chare, which may take root on some specific\n> processor at a later time.\n\nWe are just in the process of beginning to explore the load-balancing\nfeatures of Charm++, but plan to have new elements of parallel\ncomponents (the wrapped chares) be creatable using actions, without\nspecifying the location on which the new element is created.\n\n> Chares can be grouped into collections. The types of collections of\n> chares supported in Charm++ are: chare-arrays, chare-groups, and\n> chare-nodegroups, referred to as arrays, groups, and nodegroups\n> throughout this manual for brevity. A Chare-array is a collection of\n> an arbitrary number of migratable chares, indexed by some index\n> type, and mapped to processors according to a user-defined map\n> group. A group (nodegroup) is a collection of chares, with exactly\n> one member element on each PE (“node”).\n\nEach of SpECTRE's parallel components has a type alias `chare_type`\ncorresponding to whether it is a chare-array, chare-group, or\nchare-nodegroup.  In addition we support a singleton which is\nessentially a one-element array.\n\n> Charm++ does not allow global variables, except readonly\n> variables. A chare can normally only access its own data\n> directly. However, each chare is accessible by a globally valid\n> name. So, one can think of Charm++ as supporting a global object\n> space.\n\nSpECTRE does not use the readonly global variables provided by\nCharm++.  Instead SpECTRE provides a nodegroup called the\n`GlobalCache` which provides global access to both read-only\nand writable objects, as well as a way to access every parallel component.\n\n> Every Charm++ program must have at least one mainchare. Each\n> mainchare is created by the system on processor 0 when the Charm++\n> program starts up. Execution of a Charm++ program begins with the\n> Charm RTS constructing all the designated mainchares. For a\n> mainchare named X, execution starts at constructor X() or X(CkArgMsg\n> *) which are equivalent. Typically, the mainchare constructor starts\n> the computation by creating arrays, other chares, and groups. It can\n> also be used to initialize shared readonly objects.\n\nSpECTRE provides a pre-defined mainchare called `Main` that is run\nwhen a SpECTRE executable is started.  `Main` will create the other\nparallel components, and initialize items in the `GlobalCache`\nwhose items can be used by any parallel component.\n\n> Charm++ program execution is terminated by the CkExit call. Like the\n> exit system call, CkExit never returns, and it optionally accepts an\n> integer value to specify the exit code that is returned to the\n> calling shell. If no exit code is specified, a value of zero\n> (indicating successful execution) is returned. The Charm RTS ensures\n> that no more messages are processed and no entry methods are called\n> after a CkExit. CkExit need not be called on all processors; it is\n> enough to call it from just one processor at the end of the\n> computation.\n\nSpECTRE wraps `CkExit` with the function `sys::exit`.  As no more\nmessages are processed by Charm++ after this call, SpECTRE also\ndefines a special `Exit` phase that is guaranteed to be executed after\nall messages and entry methods have been processed.\n\n> As described so far, the execution of individual Chares is\n> “reactive”: When method A is invoked the chare executes this code,\n> and so on. But very often, chares have specific life-cycles, and the\n> sequence of entry methods they execute can be specified in a\n> structured manner, while allowing for some localized non-determinism\n> (e.g. a pair of methods may execute in any order, but when they both\n> finish, the execution continues in a pre-determined manner, say\n> executing a 3rd entry method).\n\nCharm++ provides a special notation to simplify expression of such\ncontrol structures, but this requires writing specialized interface\nfiles that are parsed by Charm++.  SpECTRE does not support this;\nrather we split the executable into a set of phases. In\neach phase, each parallel component will execute a user-defined list\nof actions.\n\n> The normal entry methods, being asynchronous, are not allowed to\n> return any value, and are declared with a void return type.\n\nSpECTRE's actions do not return any value to the calling component.\nInstead when the action is finished it can call another action to send\ndata to an element of any parallel component.\n\n> To support asynchronous method invocation and global object space,\n> the RTS needs to be able to serialize (“marshall”) the parameters,\n> and be able to generate global “names” for chares. For this purpose,\n> programmers have to declare the chare classes and the signature of\n> their entry methods in a special “.ci” file, called an interface\n> file. Other than the interface file, the rest of a Charm++ program\n> consists of just normal C++ code. The system generates several\n> classes based on the declarations in the interface file, including\n> “Proxy” classes for each chare class. Those familiar with various\n> component models (such as CORBA) in the distributed computing world\n> will recognize “proxy” to be a dummy, standin entity that refers to\n> an actual entity. For each chare type, a “proxy” class exists. The\n> methods of this “proxy” class correspond to the remote methods of\n> the actual class, and act as “forwarders”. That is, when one invokes\n> a method on a proxy to a remote object, the proxy marshalls the\n> parameters into a message, puts adequate information about the\n> target chare on the envelope of the message, and forwards it to the\n> remote object. Individual chares, chare array, groups, node-groups,\n> as well as the individual elements of these collections have a such\n> a proxy. Multiple methods for obtaining such proxies are described\n> in the manual. Proxies for each type of entity in Charm++ have some\n> differences among the features they support, but the basic syntax\n> and semantics remain the same - that of invoking methods on the\n> remote object by invoking methods on proxies.\n\nSpECTRE has wrapped all of this functionality in order to make it\neasier to use.  SpECTRE automatically creates the interface files for\neach parallel component using template metaprogramming.  SpECTRE\nprovides proxies for each parallel component that are all held in the\n`GlobalCache` which is available to every parallel component.  In\norder for actions to be called as entry methods on remote parallel\ncomponents, the arguments to the function call must be serializable.\nCharm++ provides the `PUP` framework to serialize objects where `PUP`\nstands for pack-unpack.  Since the PUP framework is used by Charm++\nfor checkpointing, load-balancing, and passing arguments when calling\nactions, all user-defined classes with member data must define a `pup`\nfunction.\n\n> In terms of physical resources, we assume the parallel machine\n> consists of one or more nodes, where a node is a largest unit over\n> which cache coherent shared memory is feasible (and therefore, the\n> maximal set of cores per which a single process can run. Each node\n> may include one or more processor chips, with shared or private\n> caches between them. Each chip may contain multiple cores, and each\n> core may support multiple hardware threads (SMT for example).\n> Charm++ recognizes two logical entities: a PE (processing element)\n> and a logical node, or simply “node”. In a Charm++ program, a PE is\n> a unit of mapping and scheduling: each PE has a scheduler with an\n> associated pool of messages. Each chare is assumed to reside on one\n> PE at a time. A logical node is implemented as an OS process. In\n> non-SMP mode there is no distinction between a PE and a logical\n> node. Otherwise, a PE takes the form of an OS thread, and a logical\n> node may contain one or more PEs. Physical nodes may be partitioned\n> into one or more logical nodes. Since PEs within a logical node\n> share the same memory address space, the Charm++ runtime system\n> optimizes communication between them by using shared\n> memory. Depending on the runtime command-line parameters, a PE may\n> optionally be associated with a subset of cores or hardware threads.\n\nIn other words, how Charm++ defines a node and a PE depends upon how\nCharm++ was installed on a system.  The executable\n`Executables/ParallelInfo` can be used to determine how many nodes and\nPEs exist for a given Charm++ build and the runtime command-line\nparameters passed when calling the executable.\n\nFor more details see the [Charm++\ndocumentation](https://charm.readthedocs.io) and \\ref\ndev_guide_parallelization_foundations.\n"
  },
  {
    "path": "docs/DevGuide/ParallelExecutable/MinimalExecutable.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# The minimal SpECTRE executable {#tutorial_minimal_parallel_executable}\n\n\\tableofcontents\n\nThis tutorial will illustrate what is needed to compile the minimal\nSpECTRE executable that will simply print some useful information\nabout the executable and then exit.\n\nSpecifically, this tutorial will introduce:\n- A user-provided `Metavariables`, a C++ struct that is used to\n  specify the metaprogram that is converted into a C++ executable.\n- how to build a SpECTRE executable\n- how to run a SpECTRE executable\n- useful information that can be extracted from all SpECTRE executables\n- `Main`, the main parallel component that acts as the main function\n  of a C++ executable.\n\nIn this tutorial, `SPECTRE_ROOT` refers to the directory in which\nSpECTRE was cloned, and `SPECTRE_BUILD_DIR` refers to the directory in\nwhich you built SpECTRE.\n\n### Creating a build target for a parallel executable\n\nThe first step is to select (or create) a directory (within\n`SPECTRE_ROOT/src`) that will hold the files from which the executable\nwill be created.  For this tutorial we created the directory\n`ParallelTutorial` in `src/Executables/Examples`.  If you create a new\ndirectory for the executable, you will need to edit the\n`CMakeLists.txt` file in its parent directory and add (replacing\n`ParallelTutorial` with the appropriate directory name)\n\n\\snippet Examples/CMakeLists.txt add_subdirectory\n\nThe second step is to edit (or create) the `CMakeLists.txt` file in\nthe executable directory and add a call to `add_spectre_executable` such as:\n\n\\snippet ParallelTutorial/CMakeLists.txt add_spectre_executable\n\nThe `add_spectre_executable` just forwards its arguments to CMake's\n`add_executable`. It works just like `add_executable` in that you pass it a\nsource file that defines your executable.\n\nYour source file needs a `CkRegisterMainModule()` function. This is the \"main\"\nfunction of the Charm++ program:\n\n\\snippet ParallelTutorial/MinimalExecutable.cpp main_function\n\nIt just calls `Parallel::charmxx::register_main_module<Metavariables>()`. This\nis where SpECTRE code takes over. SpECTRE programs are defined by the\n`Metavariables` class that is passed to this function.\n\n### Writing the metavariables files\n\nThe metavariables can be thought of as a compile-time input file that defines\nwhat the executable will do:\n\n\\snippet MinimalExecutable.cpp metavariables_definition\n\nThe metavariables struct must define a `static constexpr\nstd::array<Parallel::Phase, N> default_phase_order` that is used to\ndefine the default order in which phases are executed.  In this\nexample the executable will execute the `Initialization` phase\nfollowed by the `Exit` phase.\n\nThe metavariables struct must define a type alias `component_list`\nthat is a `tmpl::list` (a typelist defined in `Utilities/TMPL.hpp`) of\nthe parallel components used by the executable.  In this example no\nparallel components are used.\n\nThe metavariables struct must define `help`, a `static constexpr\nOptions::String` that will be printed as part of the help message of the\nexecutable. (`Options::String` is defined in `Options/Options.hpp`.)\n\n### Building a SpECTRE executable\n\nLet `$EXECUTABLE` be the name of the executable (the first argument\npassed to the `add_spectre_executable` `CMake` function, so in this\nexample `MinimalExample`).  Then the executable can be built by\nrunning the following command in `$SPECTRE_BUILD_DIR`:\n\n```\nmake $EXECUTABLE\n```\n\nwhich will produce the executable of the same name in\n`$SPECTRE_BUILD_DIR/bin`.\n\n### Running a SpECTRE executable\n\nTo run a SpECTRE executable, run the command:\n\n```\n./$SPECTRE_BUILD_DIR/bin/$EXECUTABLE  <options>\n```\n\nwhere `<options>` must include any required command-line options.  In\nthe simple example for this tutorial, no command-line options are\nrequired.\n\nOn a laptop, we get the following output:\n\n```\nCharm++: standalone mode (not using charmrun)\nCharm++> Running in Multicore mode:  1 threads\nConverse/Charm++ Commit ID: v6.8.0-0-ga36028edb\nCharmLB> Load balancer assumes all CPUs are same.\nCharm++> Running on 1 unique compute nodes (4-way SMP).\nCharm++> cpu topology info is gathered in 0.000 seconds.\n\nExecuting 'some_path/MinimalExample' using 1 processors.\nDate and time at startup: Fri Oct 25 15:03:05 2019\n\nSpECTRE Build Information:\nVersion:                      0.0.0\nCompiled on host:             kosh-3.local\nCompiled in directory:        some_path/build\nSource directory is:          some_path/parallel_tutorial\nCompiled on git branch:       feature/parallel_tutorial\nCompiled with git hash:       1f4ab20cbda9ae8072a17df69de9731c43687468\nLinked on:                    Fri Oct 25 13:26:06 2019\n\n\nDone!\nWall time in seconds: 0.004185\nDate and time at completion: Fri Oct 25 15:03:05 2019\n\n[Partition 0][Node 0] End of program\n```\n\nwhich includes information that will be printed by every SpECTRE\nexecutable on startup and exit.  First, there is information provided\nby `Charm++` that will depend upon how `Charm++` was built.  Next you\nwill see information provided by SpECTRE which includes:\n\n- the name of the executable\n- how many processes the executable was run on\n- the date and time at startup\n- the version of SpECTRE that was used to compile the executable\n- where the executable was compiled\n- which git branch and hash was used to compile the executable\n- when the executable was linked\n\nOn exit, the executable will print that it is `Done!`, followed by how\nthe long the executable took to run as timed by the `Charm++`\nwall-clock timer, and the date and time at completion, followed by any\ninformation the `Charm++` provides upon exiting the program.\n\n### Extracting useful information from a SpECTRE executable\n\nEvery SpECTRE executable comes with a set of command-line options that\ncan be used to obtain useful information about the executable (and for\nexecutables expecting an input file, whether or not the input file can\nbe parsed successfully).\n\n#### Getting a list of available options\n\nTo get a list of available options for a SpECTRE executable, run\neither of the following commands:\n\n```\n./$SPECTRE_BUILD_DIR/bin/$EXECUTABLE  --help\n./$SPECTRE_BUILD_DIR/bin/$EXECUTABLE  -h\n```\n\nIn the middle of the `Charm++` startup information will now appear a\nlong list of `Charm++` related command-line options which we will\nignore for this tutorial.  After the SpECTRE startup information,\nthere is now a list of available command-line options:\n\n```\n  -h [ --help ]             Describe program options\n  --check-options           Check input file options\n  --dump-source-tree-as arg If specified, then a gzip archive of the source\n                            tree is dumped with the specified name. The archive\n                            can be expanded using 'tar -xzf ARCHIVE.tar.gz'\n  --dump-paths              Dump the PATH, CPATH, LD_LIBRARY_PATH,\n                            LIBRARY_PATH, and CMAKE_PREFIX_PATH at compile\n                            time.\n  --dump-environment        Dump the result of printenv at compile time.\n  --dump-build-info         Dump the contents of SpECTRE's BuildInfo.txt\n  --dump-only               Exit after dumping requested information.\n```\n\nwhich we will describe in detail below. This is followed by a\ndescription of the expected input-file options, which in this example\nwill simply print the `help` string from the metavariables struct, and\nthe statement that there are no options expected:\n\n```\n==== Description of expected options:\nA minimal executable\n\n<No options>\n```\n\nWe will cover input file options in a future tutorial.\n\n#### Checking options\n\nAs the minimal executable in this tutorial expects no input-file\noptions, running the command:\n\n```\n./$SPECTRE_BUILD_DIR/bin/$EXECUTABLE  --check-options\n```\n\nwill print out `No options to check!` and exit the program.  See a\nfuture tutorial for an example of checking input-file options.\n\n#### Dumping the source tree\n\nIn order to aid in reproducibility, all SpECTRE executables contain a\ncopy of `$SPECTRE_ROOT`.  To obtain the source tree as an archive\nfile, run the command:\n\n```\n./$SPECTRE_BUILD_DIR/bin/$EXECUTABLE  --dump-source-tree-as SpECTRE\n```\n\nwhich will produce the archive file `SpECTRE.tar.gz` which can be\nexpanded with the command:\n\n```\ntar -xzf SpECTRE.tar.gz\n```\n\n#### Dumping other information about a SpECTRE executable\n\nIn addition to dumping the entire source tree there are the following options:\n\n- `--dump-paths` will print out various paths from when the\n  executable was compiled\n- `--dump-environment` will print the results of `printenv`\n  (i.e. all of the environment variables) from when the executable was\n  compiled\n- `--dump-build-info` will print the contents of SpECTRE's\n  `BuildInfo.txt` which will contain the versions of libraries\n  that SpECTRE linked against, as well as various `CMake` variables\n\nAlso note that the option `--dump-only` can be used to have the\nSpECTRE executable terminate immediately after dumping the\ninformation.\n\n### Behind the scenes:  the main parallel component\n\nEvery Charm++ executable needs a mainchare.  All SpECTRE executables\nuse `Main` (found in `src/Parallel/Main.hpp`) as the mainchare.  When\na SpECTRE executable is run, the constructor of `Main` that is executed\ntakes the command line options as an argument (as type\n`CkArgMsg*`).  This constructor performs the following operations:\n\n- Prints useful startup information\n- Parses the command line options, performing any requested operations\n- Parses the input file options, populating a tagged tuple with their\n  values.  (This is discussed in more detail in a future tutorial on\n  input file options.)\n- Creates the `GlobalCache` (a nodegroup chare) that holds\n  objects created from input file options that are stored\n  once per Charm++ node, as well as proxies to all other parallel\n  components.\n- Creates user-requested non-array parallel components, passing them\n  the proxy to the `GlobalCache` as well as any items they\n  request that can be created from input file options.\n- Creates empty array user-requested parallel components\n- Sends the complete list of parallel components to the\n  `GlobalCache`.\n\nOnce the list of parallel components is sent to the `GlobalCache`\non each node, the `Main` member function\n`allocate_array_components_and_execute_initial_phase` will be\nexecuted.  This will allocate the elements of the array parallel\ncomponents by calling the `allocate_array` member function of each\ncomponent.  Then the `start_phase` member function is called on each\ncomponent, which will execute the phase action list for the\n`Initialization` phase for each component.  Charm++ will execute each\nphase until it detects that nothing is happening (quiescence\ndetection).  As this represents a global synchronization point, the\nnumber of phases should be minimized in order to exploit the power of\nSpECTRE.  After each phase, the `execute_next_phase` member function\nof `Main` will be called.  This member function first determines what\nthe next phase is.  If the next phase is `Exit`, then some useful\ninformation is printed and the program exits gracefully.  Otherwise the\n`execute_next_phase` member function of each parallel component is\ncalled.\n\n"
  },
  {
    "path": "docs/DevGuide/Parallelization.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Parallelization, Charm++, and Core Concepts {#dev_guide_parallelization_foundations}\n\n\\tableofcontents\n\n# Introduction {#dev_guide_parallelization_introduction}\n\nSpECTRE builds a layer on top of Charm++ that performs various safety checks and\ninitialization for the user that can otherwise lead to difficult-to-debug\nundefined behavior. The central concept is what is called a %Parallel\nComponent. A %Parallel Component is a struct with several type aliases that\nis used by SpECTRE to set up the Charm++ chares and allowed communication\npatterns. %Parallel Components are input arguments to the compiler, which then\nwrites the parallelization infrastructure that you requested for the executable.\nThere is no restriction on the number of %Parallel Components, though\npractically it is best to have around 10 at most.\n\nHere is an overview of what is described in detail in the sections below:\n\n- \\ref dev_guide_parallelization_metavariables_class \"Metavariables\": Provides\n  high-level configuration to the compiler, e.g. the physical system to be\n  simulated.\n- \\ref dev_guide_parallelization_phases_of_execution \"Phases\": Defines distinct\n  simulation phases separated by a global synchronization point,\n  e.g. `Initialization`, `Evolve` and `Exit`.\n- \\ref dev_guide_parallelization_core_algorithm \"Algorithm\": In each phase,\n  repeatedly iterates over a list of actions until the current phase ends.\n- \\ref dev_guide_parallelization_parallel_components \"Parallel component\":\n  Maintains and executes its algorithm.\n- \\ref dev_guide_parallelization_actions \"Action\": Performs a computational\n  task, e.g. evaluating the right hand side of the time evolution equations. May\n  require data to be received from another action potentially being executed on\n  a different core or node.\n\n# The Metavariables Class {#dev_guide_parallelization_metavariables_class}\n\nSpECTRE takes a different approach to input options passed to an executable than\nis common. SpECTRE not only reads an input file at runtime but also has many\nchoices made at compile time. The compile time options are specified by what is\nreferred to as the metavariables. What exactly the metavariables struct\nspecifies depends on the executable, but all metavariables structs must\nspecify the following:\n\n- `help`: a `static constexpr Options::String` that will be printed as part of\n  the help message. It should describe the executable and basic usage of it, as\n  well as any non-standard options that must be specified in the metavariables\n  and their current values. An example of a help string for one of the testing\n  executables is:\n  \\snippet Test_AlgorithmCore.cpp help_string_example\n- `component_list`: a `tmpl::list` of the parallel components (described below)\n  that are to be created. Most evolution executables will have the\n  `DgElementArray` parallel component listed. An example of a `component_list`\n  for one of the test executables is:\n  \\snippet Test_AlgorithmCore.cpp component_list_example\n- `using const_global_cache_tags`: a `tmpl::list` of tags that are\n  used to place const items in the GlobalCache.  The alias may be\n  omitted if the list is empty.\n- `using mutable_global_cache_tags`: a `tmpl::list` of tags that are\n  used to place mutable items in the GlobalCache.  The alias may be\n  omitted if the list is empty.\n- `default_phase_order`: an array of Parallel::Phase that must contain at least\n  `Initialization` as the first element and `Exit`as the last element. Phases\n  are described in the next section.\n\nThere are also several optional members:\n\n- `input_file`: a `static constexpr Options::String` that is the default name of\n  the input file that is to be read. This can be overridden at runtime by\n  passing the `--input-file` argument to the executable.\n- `ignore_unrecognized_command_line_options`: a `static constexpr bool` that\n  defaults to `false`. If set to `true` then unrecognized command line options\n  are ignored. Ignoring unrecognized options is generally only necessary for\n  tests where arguments for the testing framework,\n  [Catch](https://github.com/catchorg/Catch2/), are passed to the executable.\n\n# Phases of an Execution {#dev_guide_parallelization_phases_of_execution}\n\nGlobal synchronization points, where all cores wait for each other, are\nundesirable for scalability reasons. However, they are sometimes inevitable for\nalgorithmic reasons. That is, in order to actually get a correct solution you\nneed to have a global synchronization. SpECTRE executables can have multiple\nphases, where after each phase a global synchronization occurs. By global\nsynchronization we mean that no parallel components are executing or have more\ntasks to execute: everything is waiting to be told what tasks to perform next.\n\nEvery executable must have at least two phases, `Initialization` and\n`Exit`. The next phase is decided by the member function\nParallel::Main<Metavariables>::execute_next_phase.\nUsually the next phase is determined\nfrom the `default_phase_order` provided by the metavariables.  If more\ncomplex decision making is desired, various components can send data\nto Parallel::Main via the PhaseControl infrastructure.  This allows the next\nPhase to be determined dynamically. Here is an example of a\n`default_phase_order` member variable:\n\\snippet LinearSolverAlgorithmTestHelpers.hpp default_phase_order_array\n\nIn contrast, an evolution executable might have phases\n`Initialization`, `SetInitialData`, `Evolve`, and `Exit`.\nThe first phase that is entered is always\n`Initialization`. During the `Initialization` phase the\n`Parallel::GlobalCache` is created, all (node)group components are created,\nand empty array and singleton components are created.  Next, the function\n`allocate_remaining_components_and_execute_initialization_phase` is called\nwhich allocates singleton components and the elements of each array component,\nand then starts the `Initialization` phase on all parallel components. Once all\nparallel components' `Initialization` phase is complete, the next\nphase is determined and the `execute_next_phase` function is called on\nall the parallel components.\n\nAt the end of an execution the `Exit` phase has the executable wait to make sure\nno \\ref dev_guide_parallelization_parallel_components \"parallel components\" are\nperforming or need to perform any more tasks, and then exits. An example where\nthis approach is important is if we are done evolving a system but still need to\nwrite data to disk. We do not want to exit the simulation until all data has\nbeen written to disk, even though we've reached the final time of the evolution.\n\nIf we reach the `Exit` phase, but some \\ref\ndev_guide_parallelization_parallel_components \"parallel components\" have not\nterminated properly, this means a deadlock has occurred. A deadlock usually\nimplies some error in the order messages have been sent/received. For example,\nif core 0 was paused and waiting to receive a message from core 1, but core 1\nwas also paused and waiting to receive a message from core 0, this would be\nconsidered a deadlock. We detect deadlocks during the `Exit` phase. All\nexecutables have the option to specify a function with the following signature\n\n\\snippet Test_DetectHangArray.cpp deadlock_analysis_function\n\nIf this function is specified in the metavariables and a deadlock occurs, this\nfunction and all the simple actions in it will run. The information printed\nduring this function call is executable dependent, but it should print enough\ninformation for you to determine why the deadlock occurred. If this function\nisn't specified and a deadlock occurs, a message about how to add this function\nto your metavariables is printed, but nothing else. After this, the executable\naborts.\n\n# The Algorithm {#dev_guide_parallelization_core_algorithm}\n\nSince most numerical algorithms repeat steps until some criterion such as the\nfinal time or convergence is met, SpECTRE's parallel components are designed to\ndo such iterations for the user. An Algorithm executes an ordered list of\nactions until one of the actions cannot be evaluated, typically because it is\nwaiting on data from elsewhere. When an algorithm can no longer evaluate actions\nit passively waits by handing control back to Charm++. Once an algorithm\nreceives data, typically done by having another parallel component call the\n`receive_data` function, the algorithm will try again to execute the next\naction. If the algorithm is still waiting on more data then the algorithm will\nagain return control to Charm++ and passively wait for more data. This is\nrepeated until all required data is available. The actions that are iterated\nover by the algorithm are called iterable actions and are described below.\nSince the action list is phase dependent we refer to them generally as\nphase-dependent action lists (PDALs, pronounced \"pedals\").\n\n# Parallel Components {#dev_guide_parallelization_parallel_components}\n\nBuilding off the introduction, a %Parallel Component is essentially a wrapper\naround Charm++ chares that makes it easy for a user to add parallel objects into\ntheir program. Charm++ chares can be confusing to work with which is why we wrap\nthem. Each parallel component runs its own \\ref\ndev_guide_parallelization_core_algorithm \"Algorithm\". Data can be sent from one\nparallel component to another and the receiving parallel components' Algorithm\nwill be able to take that data and continue the program.\n\n## 1. Types of Parallel Components {#dev_guide_parallelization_component_types}\n\nThere are four types of %Parallel Components in SpECTRE:\n\n1. `Parallel::Algorithms::Singleton`s have one object in the entire execution of\n   the program. They are implemented as single element Charm++ chare arrays.\n   Charm++ does offer a distributed object called a singleton, however, we\n   explicitly don't use this for various reasons (see\n   Parallel::Algorithms::Singleton). Henceforth and throughout SpECTRE, a\n   `singleton` will refer to Parallel::Algorithms::Singleton and *not* a Charm++\n   singleton.\n2. `Parallel::Algorithms::Array`s hold zero or more elements, each of which is\n   an object distributed to some core. An array can grow and shrink in size\n   dynamically if need be and can also be bound to another array. A bound array\n   has the same number of elements as the array it is bound to, and elements\n   with the same ID are on the same core. See Charm++'s chare arrays for\n   details.\n3. `Parallel::Algorithms::Group`s are arrays with one element per core which are\n   not able to be moved around between cores. These are typically useful for\n   gathering data from elements of a Parallel::Algorithms::Array on their core,\n   and then processing or reducing the data further. See\n   [Charm++'s](http://charm.cs.illinois.edu/help) group chares for details.\n4. `Parallel::Algorithms::Nodegroup`s are similar to groups except that there is\n   one element per node. See [parallel component\n   placement](#dev_guide_parallelization_component_placement) for the definition\n   of a cores and nodes. We ensure that all entry method calls done through the\n   Algorithm's `simple_action` and `receive_data` functions are threadsafe.\n   User-controlled threading is possible by calling the entry method member\n   function `threaded_action`, which is like `simple_action` except it passes a\n   node lock to the `Action`'s apply function. Note that unlike\n   `simple_action`s, multiple `threaded_action`s can be executing simultaneously\n   on the same chare, but on different cores of the node.\n\n\\note Technically there is a fifth type of parallel component called a MainChare\nwhich is placed and executed on the global zeroth core. However, there can only\nbe one of these in a executable, it is entirely different from a Singleton, and\nit is not specifiable by the user so we omit it from the count here.\n\n## 2. Requirements {#dev_guide_parallelization_component_requirements}\n\nEach %Parallel Component struct must have the following type aliases:\n1. `using chare_type` is set to one of the four \\ref\n   dev_guide_parallelization_component_types \"types of Parallel Components\".\n2. `using metavariables` is set to the Metavariables struct that stores the\n   global metavariables. It is often easiest to have the %Parallel\n   Component struct have a template parameter `Metavariables` that is the\n   global metavariables struct. Examples of this technique are given below.\n3. `using phase_dependent_action_list` is set to a `tmpl::list` of\n   `Parallel::PhaseActions<Phase, tmpl::list<Actions...>>`\n   where each `PhaseAction` represents a PDAL that will be executed on\n   the parallel component during the specified phase. The %Actions are\n   executed in the order that they are given in the `tmpl::list`s of\n   the PDALs, but the phases need not be run in linear order. However,\n   `db::DataBox` types are constructed assuming the phases are\n   performed from first in the `phase_dependent_action_list` to the\n   last. Simple actions (described below) can be executed in any\n   phase. If there are no iterable actions in a phase then a\n   `PhaseAction` need not be specified for that phase. However, at\n   least one `PhaseAction`, even if it is empty, must be specified.\n4. `using simple_tags_from_options` which is a `tmpl::list` of all the tags\n   that will be inserted into the initial `db::DataBox` of each component.\n   These tags are db::SimpleTag%s that have have a `using option_tags`\n   type alias and a static function `create_from_options` (see the\n   example below).  This list can usually be constructed from the\n   initialization actions of the component (i.e. the list of actions\n   in the `PhaseAction` list for the `Initialization` phase) using the\n   helper function `Parallel::get_simple_tags_from_options` (see the\n   examples of components below).  Each initialization action may\n   specify a type alias `using simple_tags_from_options` which are a\n   `tmpl::list` of tags that will be fetched from the db::DataBox by the\n   action.\n5. `using const_global_cache_tags` is set to a `tmpl::list` of tags\n   that are required by the `allocate_array` function of an array\n   component, or simple actions called on the parallel component.\n   These tags correspond to const items that are stored in the\n   Parallel::GlobalCache (of which there is one copy per Charm++\n   node).  The alias can be omitted if the list is empty.\n6. `using mutable_global_cache_tags` is set to a `tmpl::list` of tags\n   that correspond to mutable items that are stored in the\n   Parallel::GlobalCache (of which there is one copy per Charm++\n   core).  The alias can be omitted if the list is empty.\n7. `array_allocation_tags` is set to a `tmpl::list` of tags that will be\n   constructed from options and will only be used in the `allocate_array`\n   function of an array component. This type alias is only required for array\n   components.\n\nAdditionally, each %Parallel Component struct must define a `static constexpr\nbool checkpoint_data`, indicating whether the DataBox and inbox contents should\nbe preserved in a checkpoint.  Setting this to false is intended for\norganizational components that only store registration data that can be\nregenerated during the Restart phase.\n\n\\parblock\n\\note Array parallel components must also specify the type alias `using\narray_index`, which is set to the type that indexes the %Parallel Component\nArray. Charm++ allows arrays to be 1 through 6 dimensional or be indexed by a\ncustom type. The Charm++ provided indexes are wrapped as\n`Parallel::ArrayIndex1D` through `Parallel::ArrayIndex6D`. When writing custom\narray indices, the [Charm++ manual](http://charm.cs.illinois.edu/help) tells you\nto write your own `CkArrayIndex`, but we have written a general implementation\nthat provides this functionality (see `Parallel::ArrayIndex`); all that you need\nto provide is a plain-old-data\n([POD](http://en.cppreference.com/w/cpp/concept/PODType)) struct of the size of\nat most 3 integers.\n\\endparblock\n\n\\parblock\n\\note Singletons use an `array_index` of type `int`, but users need not specify\nthis. It is already specified in the implementation of a singleton.\n\\endparblock\n\n%Parallel array components have a static `allocate_array` function\nthat is used to construct the elements of the array. The\nsignature of the `allocate_array` functions must be:\n\\code\nstatic void allocate_array(\n    Parallel::CProxy_GlobalCache<metavariables>& global_cache,\n    const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n    initialization_items,\n    const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n    array_allocation_items,\n    const std::unordered_set<size_t>& procs_to_ignore);\n\\endcode\nThe `allocate_array` function is called by the Main parallel component\nwhen the execution starts and will typically insert elements into\narray parallel components. If the `allocate_array` function depends\nupon input options that are not in the GlobalCache, those tags should be\nadded to the `array_allocation_tags` type alias. A TaggedTuple is constructed\nfrom this type alias and its input options and is only available in the\n`allocate_array` function. All other tags that will be constructed from options\nand used during the %Initialization phase should be placed in the\n`simple_tags_from_options` type alias.\nThis type alias is a `tmpl::list` of tags which\nare db::SimpleTag%s that have have a `using option_tags` type alias\nand a static function `create_from_options`. They only need to be explicitly\nadded to the list if no initialization action has added them to its\n`simple_tags_from_options` type alias.  If you want to ignore specific\nprocessors when placing array elements, you can pass in a\n`std::unordered_set<size_t>` to `allocate_array` that contains all the\nprocessors that shouldn't have array elements on them.\n\nThe `allocate_array` functions of different\narray components are called in random order and so it is not safe to\nhave them depend on each other.\n\nEach parallel component must also decide what to do in the different phases of\nthe execution. This is controlled by an `execute_next_phase` function with\nsignature:\n\\code\nstatic void execute_next_phase(\n    const Parallel::Phase next_phase,\n    const Parallel::CProxy_GlobalCache<metavariables>& global_cache);\n\\endcode\nParallel::Main<Metavariables>::execute_next_phase`\ndetermines the next phase, after\nwhich the `execute_next_phase` function of each component gets called. The\n`execute_next_phase` function determines what the parallel component should do\nduring the next phase. Typically the `execute_next_phase` function should just\ncall `start_phase(phase)` on the parallel component.\n\n## 3. Examples {#dev_guide_parallelization_component_examples}\n\nAn example of a singleton parallel component is:\n\\snippet Test_AlgorithmParallel.cpp singleton_parallel_component\n\nAn example of an array parallel component is:\n\\snippet Test_AlgorithmParallel.cpp array_parallel_component\n\nThere are some parallel components that are common to many executables.\n\n- Parallel::GlobalCache (a Parallel::Algorithms::Nodegroup)\n- DgElementArray (a Parallel::Algorithms::Array)\n- observers::Observer (a Parallel::Algorithms::Group)\n- observers::ObserverWriter (a Parallel::Algorithms::Nodegroup)\n\nThe MutableGlobalCache deserves special mention, which is why is has its own\nsection with instructions on how to use it. See [Mutable items in the\nGlobalCache](#dev_guide_parallelization_mutable_global_cache).\n\n## 4. Placement {#dev_guide_parallelization_component_placement}\n\nThe user has some control over where parallel components get placed on the\nresources it is running on. Here is a figure that illustrates how one may place\nparallel components.\n\n\\image html charm_node_structure.png \"Parallel component placement.\"\n\nIn this example we are running on three (3) nodes that have four (4) cores each.\nFor all our executables, we reserve one core of each node purely for\ncommunication purposes. Nothing else is run on this core. Because of this, what\nCharm++ calls a node, doesn't correspond to a full node on a supercomputer. A\ncharm-node simply corresponds to a collection of cores on a physical node. In\nour case, a charm-node is represented by the remaining cores on a node not used\nfor communication (i.e. the first charm-node corresponds to cores 1-3 on the\nfirst physical node). Also the definition of a charm-core doesn't necessarily\nhave to correspond to an actual core (it could correspond to a hyperthreaded\nvirtual core), however, for our purposes, it does.\n\nSpECTRE offers wrappers around Charm++ functions that will tell you the total\nnumber of charm-nodes/cores in an executable and what charm-node/core a parallel\ncomponent is on. (In the following examples, the type `T` is an `int` or a\n`size_t`)\n\n- `Parallel::my_node<T>()` returns the charm-node that the parallel component is\n  on. In the figure, `Sing. 4` would return `2`.\n- `Parallel::my_proc<T>()` returns the charm-core that the parallel component is\n  on. In the figure, `Sing. 4` would return `6` (*not* `9`).\n- `Parallel::number_of_nodes<T>()` returns the total number of charm-nodes in an\n  executable. The above figure would have `3` charm-nodes.\n- `Parallel::number_of_procs<T>()` returns the total number of charm-cores in an\n  executable. The above figure would have `9` charm-cores (*not* `12`).\n\n\\note For Charm++ SMP (shared memory parallelism) builds, a node corresponds to\na collection of cores on a physical node, and a core corresponds to a processor\non that physical node. However, for non-SMP builds, nodes and cores are\nequivalent. All of our builds are done with Charm++ SMP so nodes and cores have\ntheir usual definitions.\n\nThe placement of Groups and Nodegroups are determined by Charm++. This is\nbecause a Group is on every charm-core and a Nodegroup is on every charm-node.\nEven though Nodegroups are one per charm-node, the user can't choose which core\nis used on the charm-node. They run on the next available charm-core on the\ncharm-node.\n\nThe Elements of an Array, however, can be placed on specific charm-cores. They\nare inserted into the Array by using the Charm++ `insert` member function of the\nCProxy for the Array. The `insert` function is documented in the Charm++ manual.\nIn the Array example in the\n[Examples](#dev_guide_parallelization_component_examples) section, `array_proxy`\nis a `CProxy` and so all the documentation for Charm++ array proxies applies.\nSpECTRE always creates empty arrays with the constructor and requires users to\ninsert however many elements they want and on which charm-cores they want them\nto be placed. Note that load balancing calls may result in array elements being\nmoved.\n\nIn a similar fashion, Singletons can also be placed on specific charm-cores.\nThis can be specified in the input file.\n\nFrom an input file, there are two ways to specify where Array/Singleton parallel\ncomponents can be placed.\n\n```yaml\nResourceInfo:\n  AvoidGlobalProc0: true\n  Singletons:\n    AhA:\n      Proc: 12\n      Exclusive: true\n    AhB:\n      Proc: Auto\n      Exclusive: false\n```\n\nFirst is the `AvoidGlobalProc0` option. This option will tell the program to not\nput *any* Array Elements or Singletons on the global zeroth charm-core. This\ncore is sometimes used to write data to disk which is typically much slower\nthan the program execution. The second is the `Singletons:` option. You can\nset the value to `Auto`, and then each singleton will have their proc be chosen\nautomatically and they won't be exclusive.  Otherwise, you must specify options\nfor each singleton as in the example above.  `AhA` is the `pretty_type::name()`\nof a Singleton in the program and the user has a choice of which proc to place\nthe Singleton on (`Auto` will let the program decide) and whether to exclude\nArray Elements or other Singletons from being put on this core. This is useful\nin case the Singleton does some expensive computation that shouldn't be slowed\ndown by having lots of Array Elements on the same core. In the figure above,\n`AvoidGlobalProc0` is true, and `Sing. 2` requested to be exclusively on core\n`2`.\n\n# Actions {#dev_guide_parallelization_actions}\n\n%Actions are structs with a static `apply` method and come in five\nvariants: simple actions, iterable actions, reduction actions,\nthreaded actions, and local synchronous actions.\n\nThe signature of `apply` methods differs for the different types of\nactions, but all types have the same general form.  Actions receive a\n`db::DataBox`, the Parallel::GlobalCache, and their element's index\nand parallel component, as well as arguments specific to the action\ntype.\n\nThe `db::DataBox` should be thought of as the member data of the parallel\ncomponent while the actions are the member functions. The combination of a\n`db::DataBox` and actions allows building up classes with arbitrary member data\nand methods using template parameters and invocation of actions. This approach\nallows us to eliminate the need for users to work with Charm++'s interface\nfiles, which can be error prone and difficult to use.\n\nThe Parallel::GlobalCache is passed to each action so that the\naction has access to global data and is able to invoke actions on\nother parallel components. The `ParallelComponent` template parameter\nis the tag of the parallel component that invoked the action. A proxy\nto the calling parallel component can then be retrieved from the\nParallel::GlobalCache. The remote entry method invocations are\nslightly different for different types of actions, so they will be\ndiscussed below. However, one thing that is disallowed for all actions\nis calling an action locally from within an action on the same\nparallel component.  Specifically,\n\n\\snippet Test_AlgorithmNestedApply1.cpp bad_recursive_call\n\nHere `Parallel::local()` is a wrapper around `ckLocal()` which is a Charm++\nprovided method that returns a pointer to the local (currently executing)\nparallel component. See the\n[Charm++ manual](http://charm.cs.illinois.edu/help) for more\ninformation.  However, you are able to queue a new action to be\nexecuted later on the same parallel component by getting your own\nparallel component from the Parallel::GlobalCache\n(`Parallel::get_parallel_component<ParallelComponent>(cache)`).  The\ndifference between the two calls is that by calling an action through\nthe parallel component you will first finish the series of actions you\nare in, then when they are complete Charm++ will call the next queued\naction.\n\nArray, group, and nodegroup parallel components can have actions invoked in two\nways. First is a broadcast where the action is called on all elements of the\narray:\n\n\\snippet Test_AlgorithmParallel.cpp broadcast_to_group\n\nThe second case is invoking an action on a specific array element by using the\narray element's index. The below example shows how a broadcast would be done\nmanually by looping over all elements in the array:\n\n\\snippet Test_AlgorithmParallel.cpp call_on_indexed_array\n\nNote that in general you will not know what all the elements in the array are\nand so a broadcast is the correct method of sending data to or invoking an\naction on all elements of an array parallel component.\n\nThe `array_index` argument passed to all `apply` methods is the index into the\nparallel component array. If the parallel component is not an array the value\nand type of `array_index` is implementation defined and cannot be relied on.\n\n## 1. Simple Actions {#dev_guide_parallelization_simple_actions}\n\nSimple actions can be thought of as member functions of remote objects\n(chares/parallel components).  They are the direct analog of entry\nmethods in Charm++ except that the member data is stored in the\n`db::DataBox` that is passed in as the first argument. A simple action\nmust return void but can use `db::mutate` to change values of items in\nthe `db::DataBox` if the `db::DataBox` is taken as a non-const\nreference. In some cases you will need specific items to be in the\n`db::DataBox` otherwise the action won't compile. To restrict which\n`db::DataBox`es can be passed you should use `requires` in the\naction's `apply` function template parameter list. For example,\n\\snippet Test_AlgorithmCore.cpp requires_action\nchecks that `CountActionsCalled` is available in the box.\n\nSimple actions can be called using a `CProxy` (see the [Charm++\nmanual](http://charm.cs.illinois.edu/help)), which is retrieved from\nthe Parallel::GlobalCache using the parallel component struct and the\n`Parallel::get_parallel_component()` function.  For example, the\naction above could be called as\n\\snippet Test_AlgorithmCore.cpp simple_action_call\nAny arguments after the proxy are passed as additional arguments to\nthe action's `apply` function.\n\n## 2. Iterable Actions {#dev_guide_parallelization_iterable_actions}\n\nIterable actions make up the algorithms described by the PDALs.  These\nactions are executed one after the other until one of them cannot be\nevaluated.  Their `apply` methods signature is\n\\snippet Test_AlgorithmCore.cpp apply_iterative\nThe `ActionList` type is the `tmpl::list` of iterable actions in the\ncurrent phase.  That is, it is equal to the `action_list` type alias\nin the current PDAL.  The `inboxes` is a collection of the tags\nspecified as `tmpl::list`s in the iterable actions' member type\naliases `inbox_tags`.  This collection represents data received from\nother chares using the `receive_data` function.\n\nIterable actions can request that the algorithm be paused or halted\nfor the current phase, and control which action in the current PDAL\nwill be executed next. This is all done via the return value from the\n`apply` function.  The `apply` function for iterable actions must\nreturn a Parallel::iterable_action_return_t which is a\n`std::tuple<Parallel::AlgorithmExecution, std::optional<size_t>>`. The\nfirst element of the tuple controls how algorithm execution continues.\nSee the documentation of `Parallel::AlgorithmExecution` for the\nmeanings of different values of that enum.  The second element of the\ntuple is usually set to `std::nullopt` in order to continue iterating\nthrough the algorithm, but can be used to jump to a different action\nin the current PDAL. Most iterable actions will simply return\n\n\\snippet Test_AlgorithmParallel.cpp iterable_action_return_continue_next_action\n\nAn action that pauses the algorithm will return\n\n\\snippet Test_AlgorithmParallel.cpp return_with_termination\n\nAfter an algorithm has been paused it can be restarted by passing\n`false` to the `set_terminate` method or by calling `receive_data(...,\ntrue)`. Since the order in which messages are received is undefined in\nmost cases the `receive_data(..., true)` call should be used to\nrestart the algorithm.\n\nThe return value `Parallel::AlgorithmExecution::Retry` deserves\nspecial mention.  It is intended for use by actions that use data\nsupplied by other parallel objects to indicate that they have not\nreceived all of the data they require.  The algorithm will stop until\nan operation that could supply data has occurred and then the action\nwill be retried.  An example of waiting because of missing data is\n\n\\snippet Test_AlgorithmCore.cpp retry_example\n\nIn order to jump to a specific action, the metafunction\n`tmpl::index_of<list, element>` can be used to get an\n`tmpl::integral_constant` with the value of the index of the element\n`element` in the typelist `list`. For example,\n\n\\snippet Test_AlgorithmCore.cpp out_of_order_action\n\nThe metafunction call `tmpl::index_of<ActionList,\niterate_increment_int0>::%value` returns a `size_t` whose value is\nthat of the action `iterate_increment_int0` in the PDAL.  The indexing\nof actions in the PDAL starts at `0`.\n\nIterable actions are invoked as part of the algorithm and so the only way\nto request they be invoked is by having the algorithm run on the parallel\ncomponent. The algorithm can be explicitly evaluated in a new phase by calling\n`start_phase(Phase::TheCurrentPhase)`:\n\n\\snippet Test_AlgorithmCore.cpp start_phase\n\nAlternatively, to evaluate the algorithm without changing phases the\n`perform_algorithm()` method can be used.\n\nBy passing `true` to `perform_algorithm` the algorithm will be restarted if it\nwas paused.\n\nThe algorithm is also evaluated by calling the `receive_data` function, either\non an entire array or singleton (this does a broadcast), or an on individual\nelement of the array. Here is an example of a broadcast call:\n\n\\snippet Test_AlgorithmParallel.cpp broadcast_to_group\n\nand of calling individual elements:\n\n\\snippet Test_AlgorithmParallel.cpp call_on_indexed_array\n\nThe `receive_data` function always takes a `ReceiveTag`, which is set\nin the actions' `inbox_tags` type aliases.  The `inbox_tags` must have\ntwo member type aliases, a `temporal_id` which is used to identify\nwhen the data was sent, and a `type` which is the type of the data to\nbe stored in the `inboxes`.  The types are typically a\n`std::unordered_map<temporal_id, DATA>`.  In the discussed scenario of\nwaiting for neighboring elements to send their data the `DATA` type\nwould be a `std::unordered_map<TheElementId, DataSent>`.  Inbox tags\nmust also specify a `static bool insert_into_inbox()` function.  For\nexample,\n\n\\snippet Test_AlgorithmParallel.cpp int_receive_tag\n\nThe return value indicates whether `perform_algorithm()` should be called after\ninsertion.  This exists primarily to allow the communication logic used with\n`DgElementArray` to match that used with `DgElementCollection` more closely.\nIt is safe to always return `true`.\n\nFor common types of `DATA`, such as a `map`, a data structure with an `insert`\nfunction, a data structure with a `push_back` function, or copy/move assignment\nthat is used to insert the received data, inserters are available in\n`Parallel::InboxInserters`. For example, there is\n`Parallel::InboxInserters::Map` for `map` data structures. The inbox tag can\ninherit publicly off the inserters to gain the required insertion capabilities:\n\n\\snippet Test_AlgorithmCore.cpp int receive tag insert\n\nAny inbox tag that uses Charm++ messages must also specify a `message_type` type\nalias which is the object that will be sent. An example is:\n\n\\snippet Test_AlgorithmMessages.cpp charm message inbox tag\n\nThe `inbox_tags` type alias for the action is:\n\n\\snippet Test_AlgorithmParallel.cpp int_receive_tag_list\n\nAn inbox tag can also optionally specify a static function called `output_inbox`\nthat returns a `std::string`. This function can be used for printing the\ncontents of the inbox in a nice way as the types can sometimes get complicated.\nYou can also use the `Parallel::output_inbox` function to output a specific\ninbox from all the inboxes. See an above example for the signature of the\n`output_inbox` function.\n\n\\warning\nIt is the responsibility of the iterable action to remove data from the inboxes\nthat will no longer be needed.\n\nNormally when remote functions are invoked they go through the Charm++ runtime\nsystem, which adds some overhead. The `receive_data` function tries to elide\nthe call to the Charm++ RTS for calls into array components. Charm++ refers to\nthese types of remote calls as \"inline entry methods\". With the Charm++ method\nof eliding the RTS, the code becomes susceptible to stack overflows because\nof infinite recursion. The `receive_data` function is limited to at most 64 RTS\nelided calls, though in practice reaching this limit is rare. When the limit is\nreached the remote method invocation is done through the RTS instead of being\nelided.\n\n## 3. Reduction Actions {#dev_guide_parallelization_reduction_actions}\n\nReduction actions are the targets of reducing data over an array. For\nexample, you may want to know the sum of a `int` from every element in\nthe array. You can do this as follows:\n\n\\snippet Test_AlgorithmReduction.cpp contribute_to_reduction_example\n\nThis reduces over the parallel component\n`ArrayParallelComponent<Metavariables>`, reduces to the parallel component\n`SingletonParallelComponent<Metavariables>`, and calls the action\n`ProcessReducedSumOfInts` after the reduction has been performed. The reduction\naction is:\n\n\\snippet Test_AlgorithmReduction.cpp reduce_sum_int_action\n\nAs you can see, the last argument to the `apply` function is of type `int`, and\nis the reduced value.\n\nYou can also broadcast the result back to an array, even yourself. For example,\n\n\\snippet Test_AlgorithmReduction.cpp contribute_to_broadcast_reduction\n\nIt is often necessary to reduce custom data types, such as `std::vector` or\n`std::unordered_map`. Charm++ supports such custom reductions, and so does our\nlayer on top of Charm++.\nCustom reductions require one additional step to calling\n`contribute_to_reduction`, which is writing a reduction function to reduce the\ncustom data. We provide a generic type that can be used in custom reductions,\n`Parallel::ReductionData`, which takes a series of `Parallel::ReductionDatum` as\ntemplate parameters and `ReductionDatum::value_type`s as the arguments to the\nconstructor. Each `ReductionDatum` takes up to four template parameters (two\nare required). The first is the type of data to reduce, and the second is a\nbinary invokable that is called at each step of the reduction to combine two\nmessages. The last two template parameters are used after the reduction has\ncompleted. The third parameter is an n-ary invokable that is called once the\nreduction is complete, whose first argument is the result of the reduction. The\nadditional arguments can be any `ReductionDatum::value_type` in the\n`ReductionData` that are before the current one. The fourth template parameter\nof `ReductionDatum` is used to specify which data should be passed. It is a\n`std::index_sequence` indexing into the `ReductionData`.\n\n\\warning\nAll elements of the array must call the same reductions in the same order. It is\n*defined* behavior to do multiple reductions at once as long as all contribute\ncalls on all array elements occurred in the same order. It is **undefined**\nbehavior if the contribute calls are made in different orders on different array\nelements.\n\n## 4. Threaded Actions {#dev_guide_parallelization_threaded_actions}\n\nThreaded actions are similar to simple actions, with the difference\nbeing that multiple threaded actions may be running on the same chare\nat the same time (potentially in parallel with one simple or reduction\naction).  The `apply` function for a threaded actions has the same\nsignature as that for a simple action, except that it also receives a\n`NodeLock` intended to control access to the chare's `db::DataBox`.\nAll access to the `db::DataBox`, including read-only access, must\noccur while the action owns this lock.  (Simple and reduction actions\nimplicitly hold the lock for their entire execution.)\n\n\\snippet Test_AlgorithmNodelock.cpp threaded_action_example\n\nThreaded actions can only be run on nodegroup chares.\n\n## 5. Local Synchronous Actions {#dev_guide_parallelization_local_synchronous_actions}\n\nThere is limited ability to retrieve data held by another parallel component via\na direct synchronous call. Unlike the above actions, the invocation of a\nsynchronous action is precisely a call to a member function of another parallel\ncomponent; therefore, these invocations will run to completion, and return their\nresult before the calling code proceeds in execution.\n\nAside from being synchronous and being able to return data, local\nsynchronous actions behave the same as threaded actions, except that\nthey will only run on the chare of a nodegroup that is on the local\nnode.\n\nLocal synchronous actions' `apply` functions follow a signature motivated by\nthreaded actions, but take fewer arguments.  This may be a bug.\n\nLocal synchronous actions must specify their return type in a\n`return_type` type alias. This is to help simplify the logic with the\nvariant `db::DataBox` held by the parallel component.\n\nAn example of a definition of a local synchronous action:\n\n\\snippet Test_AlgorithmLocalSyncAction.cpp synchronous_action_example\n\nAnd the corresponding invocation:\n\n\\snippet Test_AlgorithmLocalSyncAction.cpp synchronous_action_invocation_example\n\n\\warning Say an action is being run on a component on a node where a mutable\nitem in the GlobalCache is up-to-date. This action then calls another action on\na different component on a different node. It is **NOT** guaranteed that this\nmutable item in the GlobalCache is up-to-date on this new node. It is up to the\nuser to ensure the mutable item is up-to-date on whatever node they run an\naction on, even if it was up-to-date on the node that sent the message. The\n\\ref dev_guide_parallelization_mutable_global_cache\n\"Mutable items in the GlobalCache\" section gives details about mutable\nGlobalCache items.\n\n# Mutable items in the GlobalCache {#dev_guide_parallelization_mutable_global_cache}\n\nMost items in the GlobalCache are constant, and are specified\nby type aliases called `const_global_cache_tags` as\ndescribed above. However, the GlobalCache can also store mutable\nitems. Because of asynchronous execution, **EXTREME** care must be taken when\nmutating items in the GlobalCache, as described below.\n\nA mutable item can be of any type, as long as that type is something\nthat can be checked for whether it is \"up-to-date\".  Here \"up-to-date\"\nmeans that the item can be safely used (even read-only) without\nneeding to be mutated first. For example, a mutable item might be a\nfunction of time that knows the range of times for which it is valid;\nthe mutable item is then deemed up-to-date if it will be called for a\ntime within its range of validity, and it is deemed not up-to-date if\nit will be called for a time outside its range of validity.  Thus the\nup-to-date status of a mutable item is determined by both the state of\nthe item itself and by the code that wishes to use that item.\n\n## 1. Specification of mutable GlobalCache items\n\nMutable GlobalCache items are specified by a\ntype alias `mutable_global_cache_tags`, which is treated the same way\nas `const_global_cache_tags` for const items.\n\n## 2. Use of mutable GlobalCache items\n\n### 1. Checking if the item is up-to-date\n\nBecause execution is asynchronous, any code that uses a mutable item\nin the GlobalCache must first check whether that item is up-to-date.\nThe information about whether an item is up-to-date is assumed to be\nstored in the item itself.  For example, a mutable object stored in\nthe GlobalCache might have type `std::map<temporal_id,T>` (for some\ntype `T`), and then any code that uses the stored object can check\nwhether an entry exists for a particular `temporal_id`.  To avoid\nrace conditions, it is\nimportant that up-to-date checks are based on something that is\nindependent of the order of mutation (like a `temporal_id`, and not\nlike checking the size of a vector).\n\nTo check an item, use the function\n`Parallel::mutable_cache_item_is_ready`, which returns a bool\nindicating whether the item is up-to-date.  If the item is up-to-date,\nthen it can be used.  `Parallel::mutable_cache_item_is_ready` takes a\nlambda as an argument.  This lambda is passed a single argument: a\nconst reference to the item being retrieved.  The lambda should\ndetermine whether the item is up-to-date. If so, it should return a\ndefault_constructed `std::unique_ptr<Parallel::Callback>`; if not, it should\nreturn a `std::unique_ptr<Parallel::Callback>` to a callback function that will\nbe called on the next `Parallel::mutate` of that item. The callback\nwill typically check again if the item is up-to-date and if so will\nexecute some code that gets the item via `Parallel::get`.\n\nFor the case of iterable actions,\n`Parallel::mutable_cache_item_is_ready` typically uses the callback\n`Parallel::PerformAlgorithmCallback`.  In the example below, the\nvector is considered up-to-date if it is non-empty. If the vector is\nnot up-to-date, then when it becomes up-to-date the callback function\nwill be invoked; in this case the callback function re-runs\n`perform_algorithm()`, which will call the same action again.\n\n\\snippet Test_AlgorithmGlobalCache.cpp check_mutable_cache_item_is_ready\n\nNote that `Parallel::mutable_cache_item_is_ready` is called on the local\nnode and does no parallel communication.\n\n### 2. Retrieving the item\n\nThe item is retrieved using `Parallel::get` just like for constant items.\nFor example, to retrieve the item `Tags::VectorOfDoubles`:\n\\snippet Test_AlgorithmGlobalCache.cpp retrieve_mutable_cache_item\n\nNote that `Parallel::get` is called on the local node and does no\nparallel communication.\n\nWhereas we support getting *non-mutable* items in the GlobalCache from\na DataBox via `db::get`, we intentionally do not support\n`db::get` of *mutable* items in the GlobalCache from a DataBox.\nThe reason is that mutable\nitems should be retrieved only after a `Parallel::mutable_cache_item_is_ready`\ncheck, and being able to retrieve a mutable item from a DataBox makes it\ndifficult to enforce that check, especially when automatically-executing\ncompute items are considered.\n\n## 3. Modifying a mutable GlobalCache item\n\nTo modify a mutable item, pass `Parallel::mutate` two template\nparameters: the tag to mutate, and a struct with an `apply` function\nthat does the mutating. `Parallel::mutate` takes two arguments:\na reference to the local GlobalCache, and a tuple that is passed into the\nmutator function.  For the following example,\n\n\\snippet Test_AlgorithmGlobalCache.cpp mutate_global_cache_item\n\nthe mutator function is defined as below:\n\\snippet Test_AlgorithmGlobalCache.cpp mutate_global_cache_item_mutator\n\n`Parallel::mutate` broadcasts to every node, where it calls the\nmutator function and then calls all the callbacks that have been set\non that node by `Parallel::mutable_cache_item_is_ready`.  The\n`Parallel::mutate` operation is guaranteed to be thread-safe without\nany further action by the developer so long as the item being mutated can be\nmutated in a threadsafe way. See the `Parallel::GlobalCache` docs for more\ndetails.\n\n# Charm++ Node and Processor Level Initialization Functions {#dev_guide_parallelization_charm_node_processor_level_initialization}\n\nCharm++ allows running functions once per core and once per node before the\nconstruction of any parallel components. This is commonly used for setting up\nerror handling and enabling floating point exceptions. Other functions could\nalso be run. Which functions are run on each node and core is set by calling\n`Parallel::charmxx::register_init_node_and_proc` in `CkRegisterMainModule()`\nwith function pointers to the functions to be called. For example:\n\n\\snippet Test_AlgorithmPhaseControlNodegroup.cpp charm_init_funcs_example\n"
  },
  {
    "path": "docs/DevGuide/PerformanceGuidelines.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# General Performance Guidelines {#general_perf_guide}\n\n\\tableofcontents\n\nBelow are some general guidelines for achieving decent performance.\n\n- One good measurement is worth more than a million expert opinions.\n  Our testing framework [Catch2](https://github.com/catchorg/Catch2) supports\n  benchmarks, so we encourage you to add benchmarks to your tests. See the\n  [Catch2 benchmarks documentation](https://github.com/catchorg/Catch2/blob/devel/docs/benchmarks.md)\n  for instructions. Essentially, add a `BENCHMARK` to your test case and run\n  the test executable (such as `./bin/Test_LinearOperators`).\n  Note that we skip benchmarks during automated unit testing with `ctest`\n  because benchmarks are only meaningful in a controlled environment (such as a\n  specific machine or architecture). You can keep track of the benchmark results\n  you ran on specific machines in a comment in the test case (until we have a\n  better way of keeping track of benchmark results).\n\n  Catch2's benchmarking is not as feature-rich as Google Benchmark. We have a\n  `Benchmark` executable that uses Google Benchmark so one can compare\n  different implementations and see how they perform. This executable is only\n  available in release builds.\n- Reduce memory allocations. On all modern hardware (many core CPUs, GPUs, and\n  FPGAs), memory is almost always the bottleneck. Memory allocations are\n  especially expensive since this is a quasi-serial process: the OS has to\n  manage memory allocations for _all_ running threads and processes. SpECTRE has\n  various classes to optimize this. For example, there are `Variables`,\n  `TempBuffer`, and `DynamicBuffer` that allow making large contiguous memory\n  allocations that are then used for individual tensor components.\n"
  },
  {
    "path": "docs/DevGuide/Profiling.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Profiling {#profiling}\n\n\\tableofcontents\n\nThere are a number of tools available for profiling, each with their own\nstrengths and weaknesses. This makes it difficult to recommend one \"right\" way\nof analyzing performance using profilers. Instead, one should use a combination\nof the tools to discover and eliminate performance bottle necks. Common\nprofilers are Charm++ Projections (tracing-based), HPCToolkit (sampling-based,\nvery versatile), Linux perf (sampling-based, command line only), Intel VTune\n(sampling-based, works well on Intel hardware), AMD uProf (similar to Intel\nVTune), and Score-P (sampling and tracing, with MPI and pthread interfacing).\n\n## Profiling with HPCToolkit {#profiling_with_hpctoolkit}\n\nFollow the HPCToolkit installation instructions at\n[hpctoolkit.org](http://hpctoolkit.org). The Spack\ninstallation seems to work well. Once installed, compile your executable in\nRelease mode with `-D ENABLE_PROFILING=ON -D DEBUG_SYMBOLS=ON` since otherwise\nyou won't be able to get call stacks and source analysis. Using `-D\nBUILD_SHARED_LIBS=ON` is recommended since it makes HPCToolkit a lot easier to\nuse. You must also use the system allocator, `-D MEMORY_ALLOCATOR=SYSTEM`. We\nwill work from the build directory and perform all runs and performance analysis\nthere.\n\nFirst run HPCToolkit as:\n```\nhpcrun -t --event CYCLES@f200 ./bin/EXEC --input-file ./Input.yaml +p1\n```\nWe will profile on one core, but you can profile on multiple cores as well as\nmultiple nodes if using MPI as the Charm++ backend. This will generate a\n`hpctoolkit-EXEC-measurements` directory. Run\n```\nhpcstruct -jN ./hpctoolkit-EXEC-measurements\n```\nwhere `N` is the number of cores to run on. This will generate a mapping to line\nnumbers, etc. in the measurements directory.\n\n\\warning Skipping the `hpcstruct` step will make `hprprof` below run extremely\nslowly.\n\nOnce the run is complete, run\n```\nhpcprof hpctoolkit-EXEC-measurements\n```\nor if HPCToolkit version < 2024, run\n```\nhpcprof -I /path/to/spectre/src/+ hpctoolkit-EXEC-measurements\n```\nNote that the `+` is a literal `+` symbol.\n\nThis will create the directory\n```\nhpctoolkit-EXEC-database\n```\nwhich you can view using\n```\nhpcviewer ./hpctoolkit-EXEC-database\n```\n\nHPCViewer will generally start you in the `Top-down view` (callgraph of\ncallers). You can select  `Bottom-up view` (callgraph of callees) to get a\ndifferent perspective. Whether you want to look at the callgraph of callers or\ncallees depends a bit on the executable, what you're looking to measure, and how\nyou like to think about things. The callees graph can give you a nice overview\nof what the low-level things taking up a lot of time are, but certainly makes\nthe call stack not look like you would expect. On the right of the callgraphs\nyou will see `CYCLES:Sum (I)` and `CYCLES:Sum (E)`. `I` means time spent\n_including_ callees, while `E` means time spent in the function itself\n(exclusive time). Sorting by exclusive gives a good idea of what the hot\nfunctions are. Here is a screenshot from HPCViewer:\n\n\\image html HpcViewerCallees.png \"HPCViewer callgraph of callees\"\n\nYou can see that 49.1% of inclusive time is spent in primitive recovery, and the\nline after the 49.1% function is a function inside the Kastaun\nrecovery scheme. The `__nss_database_lookup` is some system call,\ne.g. `__memcpy_avx_unaligned_erms` or `__memset_avx2_unaligned_erms`. Looking at\nthe calling code, e.g. `prepare_neighbor_data` gives a good hint as to what's\ngoing on. In most cases these are memory copies or memory sets (`std::vector`\ndefault initializes its memory, which is bad for performance). The way to fix\nthese bottlenecks is to avoid memory copies and `std::vector<double>` as\nbuffers.\n\nHPCToolkit allows you to sample on a variety of different event counters instead\nof just cycles. Please see the HPCToolkit manual for details.\n\n## Profiling with AMD uProf {#profiling_with_amd_uprof}\n\n[AMD uProf](https://developer.amd.com/amd-uprof/) is AMD's sampling-based\nprofiler that makes it relatively easy to do\nquite a bit of detailed performance analysis. The uProf manual is quite good and\nextensive, so for the most part the reader is referred to that. However, we will\ngo over some basics for profiling executables and understanding the\nresults. Make sure to compile your executable in Release mode with\n`-DENABLE_PROFILING=ON -D DEBUG_SYMBOLS=ON` since otherwise you won't be able to\nget call stacks and source analysis.\n\nWhen you open uProf you may be asked to change the kernel event paranoid\nlevel. Once you have uProf open, select `PROFILE` at the top. Specify the\napplication path, options, etc. We will again run on a single core to analyze\nperformance. It's recommended that you set the Core Affinity in AMD uProf so\nthat your application isn't migrated between cores during a profiling run. Then\nchoose `Next` in the lower right corner. Make sure the `CPU Profile Type` is set\nto `CPU Profile` at the top. We will first do a `Time-based Sampling` run (on\nthe left). This means uProf will interrupt the application every `N`\nmilliseconds and see where the application is. You typically want a few thousand\ntotal samples to get something that's reasonably representative of your\napplication. Under the `Advanced %Options` make sure `Enable CSS` (on the right)\nis enabled (green) and that `Enable FPO` is also enabled. Now click `Start\nProfile` in the bottom right. Once the profile is complete you will be presented\nwith a summary outlining where your code is spending most of its time. Click\n`ANALYZE` at the top to get a more detailed view. On the left you can select\nbetween a callgraph of callees (Function HotSpots), a callgraph of callers (Call\nGraph), and a few other views. Below is an example of a result from the same run\nwe used with HPCToolkit above.\n\n\\image html AmdUprofCallgraph.png \"AMD uProf callgraph of callees\"\n\nAgain we see that most of our time is spent in primitive recovery but also that\na lot of time is spent copying memory. This was grouped into\n`__nss_database_lookup` in HPCToolkit. Unfortunately, getting a call stack out\nof the `memcpy` doesn't always work and so while you know you're spending a lot\nof time copying memory, it's not so obvious where those copies are occurring.\n\n## Profiling With Charm++ Projections {#profiling_with_projections}\n\nTo view trace data after a profiling run you must download Charm++'s\nProjections software from their [website](http://charm.cs.illinois.edu/).\nIf you encounter issues it may\nbe necessary to clone the git repository and build the correct version\nfrom scratch. Note that the version of Charm++ used to compile SpECTRE\nshould match the version of Projections used to analyze the trace data.\nYou can collect the trace data on a different machine than the one you\nwill be analyzing the data on. For example, you can collect the data on\na supercomputer and analyze it on your desktop or laptop.\n\nFor profiling you will want to use a production build of Charm++, which\nmeans compiling Charm++ with the `--with-production` flag. To enable trace\ncollecting you must build with the `--enable-tracing` flag as well. For\nexample, on a multicore 64-bit Linux machine the build command would be\n``` shell\n./build LIBS multicore-linux-x86_64 gcc -j8 --with-production --enable-tracing\n```\nYou must build your executable in Release mode as well, specifying\n`-DCMAKE_BUILD_TYPE=Release` to CMake, as well as\n```\n-DCHARM_TRACE_PROJECTIONS=ON -DCHARM_TRACE_SUMMARY=ON -DENABLE_PROFILING=ON\n```\nto enable SpECTRE to use Charm++'s tracing features.\n\n### Running SpECTRE With Trace Output\n\nWhen running SpECTRE you must specify a directory to output trace data into.\nThis is done by adding the command line argument `+traceroot DIR` where `DIR` is\nthe directory to dump the trace data into. Note that `DIR` must already exist,\nthe application will not create it.\nFor example,\n\n```shell\n./bin/EXEC --input-file ./Input.yaml +p4 +traceroot ./ExecTraces\n```\nYou might get a warning that Charm++ had to flush the log some number of times\nduring the run. Flushing the log adds overhead to the execution and so affects\ntiming measurements. While Charm++ has the ability to manually flush the log\nperiodically (and therefore exclude the time it takes to flush the log from the\ntrace), we have not yet implemented support for this. For short executable runs\nyou can increase the log size by specifying `+logsize M` when running the\nexecutable. The default log size is 1,000,000 (1000000). Note that if you\nincrease the log size too much you will run out of memory/RAM.\n\nFor more information on runtime options to\ncontrol trace data see the\n[Charm++ Projections manual](http://charm.cs.illinois.edu/manuals/html/projections/1.html).\n\n### Visualizing Trace %Data In Projections\n\nBy default Charm++ records entry method names by using the `PRETTY_FUNCTION`\nmacro. This means entry method names include all class (parallel component) and\naction template parameter names, including any template parameters of the\ntemplate parameters. This very quickly leads to incomprehensibly long names that\nare very difficult to read in the Projections interface. We include a basic\nPython executable to handle the majority of renames, but the executable supports\nadditional basic (textual find-replace) and regular expression\nreplacements via a JSON file. These additional replacements are useful for\nmaking executable-specific renames. The Python executable is\n`tools/CharmSimplifyTraces.py` and an example replacements file is\n`tools/CharmTraceReplacements.json`.\n\nSee the [Charm++ Projections manual](http://charm.cs.illinois.edu/manuals/html/projections/2.html)\nfor details.\n\n## Profiling with Score-P {#profiling_with_scorep}\n\nSee the\n[Score-P](https://www.vi-hps.org/projects/score-p/overview/overview.html)\nwebsite for installation instructions specific to Score-P.\n\nTo compile SpECTRE with Score-P you must invoke CMake with the environment\nvariable `SCOREP_WRAPPER=off` (do NOT export this variable!) and the `scorep-`\ncompiler wrappers. For example,\n```sh\nSCOREP_WRAPPER=off cmake -DCMAKE_C_COMPILER=scorep-gcc \\\n    -DCMAKE_CXX_COMPILER=scorep-g++ -DCMAKE_Fortran_COMPILER=scorep-gfortran \\\n    -D CMAKE_BUILD_TYPE=Release -D ENABLE_PROFILING=ON \\\n    -D BUILD_DOCS=OFF -D BUILD_PYTHON_BINDINGS=OFF $SPECTRE_HOME\n```\nwhere `$SPECTRE_HOME` points to your spectre source tree. You can then compile\nthe executable you want to profile. When running the executable you will need to\nmake sure Score-P has enough memory to be able to function properly. For the\nEvolveGhSingleBlackHole, Score-P needs 1.5GB of memory. To enable profiling,\ndisable tracing, and use 1.5GB of memory for Score-P, set the environment\nvariables\n```sh\nSCOREP_ENABLE_PROFILING=1 \\\n    SCOREP_ENABLE_TRACING=0 \\\n    SCOREP_TOTAL_MEMORY=1536000000\n```\nWe also need to use a filter file to keep the data at all manageable and to\navoid some currently not understood interaction between Score-P and our option\nparser. The profile is in `support/Profiling/ScoreP` and named `spectre.flt`.\nThe final command should look something like:\n```sh\nSCOREP_ENABLE_PROFILING=1 SCOREP_ENABLE_TRACING=0 \\\n    SCOREP_TOTAL_MEMORY=1536000000 \\\n    SCOREP_FILTERING_FILE=$SPECTRE_HOME/support/Profiling/ScoreP/spectre.flt \\\n    mpirun -np 2 EvolveGhSingleBlackHole --input-file ./KerrSchild.yaml +ppn 91\n```\nDepending on your profile, you might also need to increase the callpath depth by\nsetting\n```sh\nSCOREP_PROFILING_MAX_CALLPATH_DEPTH=150\n```\nOnce you've successfully run the executable you should have a directory like\n`scorep-20251006_1732_65976792745157592`. Please see the Score-P documentation\nfor how to analyze the output.\n"
  },
  {
    "path": "docs/DevGuide/Protocols.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Protocols {#protocols}\n\n\\tableofcontents\n\n# Overview of protocols {#protocols_overview}\n\nProtocols are a concept we use in SpECTRE to define metaprogramming interfaces.\nA variation of this concept is built into many languages, so this is a quote\nfrom the [Swift documentation](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html):\n\n> A protocol defines a blueprint of methods, properties, and other requirements\n> that suit a particular task or piece of functionality. The protocol can then\n> be adopted by a class, structure, or enumeration to provide an actual\n> implementation of those requirements. Any type that satisfies the requirements\n> of a protocol is said to conform to that protocol.\n\nYou should define a protocol when you need a template parameter to conform to an\ninterface. Here is an example of a protocol that is adapted from the [Swift\ndocumentation](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html):\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp named_protocol\n\nThe protocol defines an interface that any type that adopts it must implement.\nFor example, the following class conforms to the protocol we just defined:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp named_conformance\n\nThe class indicates it conforms to the protocol by (publicly) inheriting from\n`tt::ConformsTo<TheProtocol>`.\n\nOnce you have defined a protocol, you can check if a class conforms to it using\nthe `tt::assert_conforms_to` or `tt::conforms_to` metafunctions:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp conforms_to\n\nNote that checking for protocol conformance is cheap, so you may freely use\nprotocol conformance checks in your code.\n\nThis is how you can write code that relies on the interface defined by the\nprotocol:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp using_named_protocol\n\nChecking for protocol conformance here makes it clear that we are expecting\na template parameter that exposes the particular interface we have defined in\nthe protocol. Therefore, the author of the protocol and of the code that uses it\nhas explicitly defined (and documented!) the interface they expect. And the\ndeveloper who consumes the protocol by writing classes that conform to it knows\nexactly what needs to be implemented.\n\nNote that the `tt::conforms_to` metafunction is SFINAE-friendly, so you can use\nit like this:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp protocol_sfinae\n\nThe `tt::conforms_to` metafunction only checks if the class _indicates_ it\nconforms to the protocol. Where SFINAE-friendliness is not necessary prefer the\n`tt::assert_conforms_to` metafunction that triggers static asserts with\ndiagnostic messages to understand why the class does not conform to the\nprotocol.\n\nWe typically define protocols in a file named `Protocols.hpp` and within a\n`protocols` namespace, similar to how we write \\ref DataBoxTagsGroup \"tags\" in a\n`Tags.hpp` file and within a `Tags` namespace. The file should be placed in the\ndirectory associated with the code that depends on classes conforming to the\nprotocols. For example, the protocol `Named` in the example above would be\nplaced in directory that also has the `greet` function.\n\n# Protocol users: Conforming to a protocol {#protocols_conforming}\n\nTo indicate a class conforms to a protocol it (publicly) inherits from\n`tt::ConformsTo<TheProtocol>`. The class must fulfill all requirements defined\nby the protocol. The requirements are listed in the protocol's documentation.\n\nAny class that indicates it conforms to a protocol must have a unit test to\ncheck that it actually does. You can use the `tt::assert_conforms_to`\nmetafunction for the test:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp test_protocol_conformance\n\n# Protocol authors: Writing a protocol {#protocols_author}\n\nTo author a new protocol you implement a class that provides a `test`\nmetafunction and detailed documentation. The `test` metafunction takes a single\ntemplate parameter (typically named `ConformingType`) and checks that it\nconforms to the requirements laid out in the protocol's documentation. Its\npurpose is to provide diagnostic messages as compiler errors to understand why a\ntype fails to conform to the protocol. You can use `static_assert`s or trigger\nstandard compiler errors where appropriate. See the protocols defined above for\nexamples.\n\nOccasionally, you might be tempted to add template parameters to a protocol. In\nthose situations, add requirements to the protocol instead and retrieve the\nparameters from the conforming class. The reason for this guideline is that\nconforming classes will always inherit from `tt::ConformsTo<Protocol>`.\nTherefore, any template parameters of the protocol must also be template\nparameters of their conforming classes, which means the protocol can just\nrequire and retrieve them. For example, we could be tempted to follow this\nantipattern:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp named_antipattern\n\nHowever, instead of adding template parameters to the protocol we should add a\nrequirement to it:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp named_with_type\n\nClasses would need to specify the template parameters for any\nprotocols they conform to anyway, if the protocols had any. So they might as\nwell expose them:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp person_with_name_type\n\nThis pattern also allows us to check for protocol conformance first and then add\nfurther checks about the types if we wanted to:\n\n\\snippet Utilities/Test_ProtocolHelpers.cpp example_check_name_type\n\n# Protocol authors: Testing a protocol {#protocols_testing}\n\nProtocol authors should provide a unit test for their protocol that includes an\nexample implementation of a class that conforms to it. The protocol author\nshould add this example to the documentation of the protocol through a Doxygen\nsnippet. This gives users a convenient way to see how the author intends their\ninterface to be implemented.\n\n# Protocols and C++20 \"Constraints and concepts\" {#protocols_and_constraints}\n\nA feature related to protocols is in C++20 and goes under the name of\n[constraints and concepts](https://en.cppreference.com/w/cpp/language/constraints).\nEvery protocol defines a _concept_, but it defers checking its requirements to\nthe unit tests to save compile time. In other words, protocols provide a way to\n_indicate_ that a class fulfills a set of requirements, whereas C++20\nconstraints provide a way to _check_ that a class fulfills a set of\nrequirements. Therefore, the two features complement each other. Once C++20\nbecomes available in SpECTRE we can either gradually convert our protocols to\nconcepts and use them as constraints directly if we find the impact on compile\ntime negligible, or we can add a concept that checks protocol conformance the\nsame way that `tt::conforms_to_v` currently does (i.e. by checking inheritance).\n"
  },
  {
    "path": "docs/DevGuide/PythonBindings.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Writing Python Bindings {#spectre_writing_python_bindings}\n\n\\tableofcontents\n\n## CMake and Directory Layout\n\nTo allow users to analyze output from simulations and take advantage of\nSpECTRE's data structures and functions in python, bindings must sometimes be\nwritten. SpECTRE uses [pybind11](https://pybind11.readthedocs.io/)\nto aid with generating the bindings. The C++ code for the bindings should\ngenerally go in a `Python` subdirectory. For example, the bindings for the\nDataStructures library would go in `src/DataStructures/Python/`. SpECTRE\nprovides the `spectre_python_add_module` CMake function to make adding a new\npython module, be it with or without bindings, easy.  The python bindings are\nbuilt only if `-D BUILD_PYTHON_BINDINGS=ON` is passed when invoking cmake\n(enabled by default).\nYou can specify the Python version, interpreter and libraries used for compiling\nand testing the bindings by setting the `-D Python_EXECUTABLE` to an absolute\npath such as `/usr/bin/python3`.\n\nThe function `spectre_python_add_module` takes as its first argument the module,\nin our case `DataStructures`. Optionally, a list of `SOURCES` can be passed to\nthe CMake function. If you specify `SOURCES`, you must also specify a\n`LIBRARY_NAME`. A good `LIBRARY_NAME` is the name of the C++ library for which\nbindings are being built prefixed with `Py`, e.g. `PyDataStructures`. If the\nPython module will only consist of Python files, then the `SOURCES` option\nshould not be specified. Python files that should be part of the module can be\npassed with the keyword `PYTHON_FILES`. Finally, the `MODULE_PATH`\nnamed argument can be passed with a string that is the path to where the module\nshould be. For example, `MODULE_PATH \"submodule0/submodule1/\"` would mean the\nmodule is accessed from python using\n`import spectre.submodule0.submodule1.MODULE_NAME`.\n\nHere is a complete example of how to call the `spectre_python_add_module`\nfunction:\n\n\\code\nspectre_python_add_module(\n  Extra\n  LIBRARY_NAME \"PyExtraDataStructures\"\n  MODULE_PATH \"DataStructures/\"\n  SOURCES Bindings.cpp MyCoolDataStructure.cpp\n  PYTHON_FILES CoolPythonDataStructure.py\n  )\n\\endcode\n\nThe library that is added has the name `PyExtraDataStructures`. Make sure to\ncall `spectre_python_link_libraries` for every Python module that compiles\n`SOURCES`. For example,\n\n\\code\nspectre_python_link_libraries(\n  PyExtraDataStructures\n  PRIVATE\n  ExtraDataStructures\n  pybind11::module\n  )\n\\endcode\n\nYou may also call `spectre_python_add_dependencies` for Python modules that\nhave `SOURCES`, e.g.\n\n\\code\nspectre_python_add_dependencies(\n  PyExtraDataStructures\n  PyDataStructures\n  )\n\\endcode\n\nNote that these functions will skip adding or configure any C++ libraries if\nthe `BUILD_PYTHON_BINDINGS` flag is `OFF`.\n\n## Writing Bindings\n\nOnce a python module has been added you can write the actual bindings. You\nshould structure your bindings directory to reflect the structure of the library\nyou're writing bindings for. For example, say we want bindings for `DataVector`\nand `Matrix` then we should have one source file for each class's bindings\ninside `src/DataStructures/Python`. The functions that generate the bindings\nshould be in the `py_bindings` namespace and have a reasonable name such as\n`bind_datavector`. There should be a file named `Bindings.cpp` which calls all\nthe `bind_*` functions. The `Bindings.cpp` file is quite simple and should\n`include <pybind11/pybind11.h>`, forward declare the `bind_*` functions, and\nthen have a `PYBIND11_MODULE` function. For example,\n\n\\code{.cpp}\n#include <pybind11/pybind11.h>\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_datavector(py::module& m);\n}  // namespace py_bindings\n\nPYBIND11_MODULE(_Pybindings, m) {\n  py_bindings::bind_datavector(m);\n}\n\\endcode\n\nNote that the library name is passed to `PYBIND11_MODULE` and is prefixed\nwith an underscore. The underscore is important and the library name must be the\nsame that is passed as `LIBRARY_NAME` to `spectre_python_add_module` (see\nabove).\n\nThe `DataVector` bindings serve as an example with code comments on how to write\nbindings for a class. There is also extensive documentation available directly\nfrom [pybind11](https://pybind11.readthedocs.io/).\n\nIf you are binding a library full of similarly structured free functions, such\nas libraries in `src/PointwiseFunctions/`, you can bind all functions directly\nin the `Bindings.cpp` file to avoid unnecessary boilerplate code. See\n`src/PointwiseFunctions/GeneralRelativity/Python/Bindings.cpp` for an example.\n\n\\note Exceptions should be allowed to propagate through the bindings so that\nerror handling via exceptions is possible from python rather than having the\npython interpreter being killed with a call to `abort`.\n\n## Testing Python Bindings and Code\n\nAll the python bindings must be tested. SpECTRE uses the\n[unittest](https://docs.python.org/3/library/unittest.html) framework\nprovided as part of python. To register a test file with CMake use the\nSpECTRE-provided function `spectre_add_python_test` passing as the first\nargument the test name (e.g. `\"Unit.DataStructures.Python.DataVector\"`), the\nfile as the second argument (e.g. `Test_DataVector.py`), and a semicolon\nseparated list of labels as the last (e.g. `\"unit;datastructures;python\"`).\nAll the test cases should be in a single class so that the python unit testing\nframework will run all test functions on a single invocation to avoid startup\ncost.\n\nBelow is an example of registering a python test file for bindings:\n\n\\snippet tests/Unit/DataStructures/CMakeLists.txt example_add_pybindings_test\n\nPython code that does not use bindings must also be tested. You can register the\ntest file using the `spectre_add_python_test` CMake function with the same\nsignature as shown above.\n\n## Using The Bindings\n\nSee \\ref spectre_using_python \"Using SpECTRE's Python\"\n\n## Notes:\n\n- All python libraries are dynamic/shared libraries.\n- Exceptions should be allowed to propagate through the bindings so that\n  error handling via exceptions is possible from python rather than having the\n  python interpreter being killed with a call to `abort`.\n- All function arguments in Python bindings should be named using `py::arg`.\n  See the Python bindings in `IO/H5/` for examples. Using the named arguments in\n  Python code is optional, but preferred when it makes code more readable.\n  In particular, use the argument names in the tests for the Python bindings so\n  they are being tested as well.\n\n## Guidelines for writing command-line interfaces (CLIs)\n\n- List all CLI endpoints in `support/Python/__main__.py`.\n- Follow the recommendations in the\n  [click](https://click.palletsprojects.com/en/8.1.x/) documentation.\n- Split your code into free functions that know nothing about the CLI and can\n  just as well be called independently from Python, and the CLI commands that\n  call the functions. Test both.\n- Take only input files that the script operates on as positional arguments\n  (like H5 data files or YAML input files) and everything else as options.\n- Choose option names and shorthands consistent with other CLI endpoints in the\n  repository. For example, H5 subfile names are specified with '--subfile-name'\n  / '-d' and output files are specified with '--output' / '-o'. Look at other\n  CLI endpoints before making choices for option names.\n- Never read or write files to or from \"default\" locations. Instead, take all\n  input files as arguments and write all output files to locations specified\n  explicitly by the user. This is important so users are not afraid of moving\n  and renaming files, and are not left wondering where the script wrote its\n  output. Examples:\n  - Don't try to read a file like \"spectre.out\" from the current directory just\n    because it might be there by convention. Instead, add an argument or option\n    like `--out-filename` so the user can specify it.\n  - Don't write a file like \"plot.pdf\" to the current directory without telling\n    the user. Instead, add an option like `--output` / `-o` for the user to\n    specify explicitly so they know exactly where output is written to.\n- Operate on files instead of directories when possible. For example, prefer\n  taking many H5 volume data files as arguments instead of the directory that\n  contains them. This helps with operating on H5 files in segments or other\n  subdirectory structures. Passing many files to a script is easy for the user\n  by using a glob (note: don't take the glob as a string argument, take the\n  expanded list of files directly using `click.argument(..., nargs=-1,\n  type=click.Path(...), required=True)`).\n  Note that Click recommends to avoid `required=True` here but to [gracefully\n  degrade to a noop](https://click.palletsprojects.com/en/8.1.x/arguments/#variadic-arguments)\n  instead, but that's confusing for the user.\n- Never overwrite or delete files without prompting the user or asking them to\n  run with `--force`.\n- When the user did not specify an option, print possible values for it and\n  return instead of raising an exception. For example, print the subfile names\n  in an H5 file if no subfile name was specified. This allows the user to make\n  selections incrementally.\n- When the user did not specify an output file, write the output to `sys.stdout`\n  if possible instead of raising an exception. This allows the user to use pipes\n  and chain commands if they want, or add a quick `-o` option to write to a\n  file.\n- Always use Python's `logging` module over plain `print` statements. This\n  allows the user to control the verbosity.\n"
  },
  {
    "path": "docs/DevGuide/QuickStartDockerVSCode.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Code development quick-start with Docker and Visual Studio Code {#dev_guide_quick_start_docker_vscode}\n\n\\tableofcontents\n\nThis page describes how to get started developing SpECTRE on Mac, Linux, or\nWindows using [Docker](https://docker.com) and the [Visual Studio\nCode](https://code.visualstudio.com) editor. This is a particularly quick way to\nget up and running with SpECTRE code development, though of course not the only\nway. If you prefer setting up your development environment differently, we\nsuggest you read the \\ref installation \"Installation\" page. If you would like to\njump right into a working development environment, read on!\n\n## Fork the SpECTRE repository on GitHub\n\nThe SpECTRE code lives on GitHub in the\n[sxs-collaboration/spectre](https://github.com/sxs-collaboration/spectre)\nrepository. Developers work on their own copy (or\n[\"fork\"](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-forks))\nof the repository and contribute back to\n[sxs-collaboration/spectre](https://github.com/sxs-collaboration/spectre)\nthrough [pull requests](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests).\nFork the repository to your own account:\n\n- [Fork sxs-collaboration/spectre on GitHub](https://github.com/sxs-collaboration/spectre/fork)\n\n## Clone the SpECTRE repository to your computer\n\nTo work on SpECTRE code you will need a local copy (or \"clone\") of the\nrepository on your computer. We use SSH to communicate with GitHub, so you need\nto set up your SSH keys first. Follow GitHub's instructions to generate an SSH\nkey and add it to your GitHub account:\n\n- [Generating an SSH key and adding to to GitHub](https://docs.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)\n\nNow you can download the repository via SSH. Navigate to **your fork** of the\nSpECTRE repository (i.e. the repository at the URL\nhttps://github.com/YOURNAME/spectre). Follow GitHub's instructions to clone the\nrepository to your computer, selecting the SSH option:\n\n- [Cloning a repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository)\n\n\\note If you clone the SpECTRE repository using HTTPS instead of SSH, you won't\nbe able to push any changes you make to your fork. You'll still be able to make\nlocal changes on your computer, but you won't be able to share your changes with\nanybody.\n\n## Install Docker\n\nOn your computer you will need Docker to run the containerized development\nenvironment. Install and start Docker:\n\n- [Get Docker](https://docs.docker.com/get-docker/)\n\nIf you're new to Docker, you can read through Docker's [Getting\nstarted](https://docs.docker.com/get-started/) documentation to learn about\ntheir basic concepts. We will use Docker to download and jump into a prebuilt\ndevelopment environment that has everything installed to compile and run\nSpECTRE.\n\n## Install Visual Studio Code\n\nWe will use the Visual Studio Code editor to jump into the containerized\ndevelopment environment, edit code, compile executables and run them. Download\nand install Visual Studio Code:\n\n- [Get Visual Studio Code](https://code.visualstudio.com)\n\nMicrosoft maintains [extensive\ndocumentation](https://code.visualstudio.com/docs) for Visual Studio Code that\nyou can read to get started using the editor. We recommend you take a look\nthrough the [Tips and\nTricks](https://code.visualstudio.com/docs/getstarted/tips-and-tricks) to get an\nidea of the editor's features.\n\n## Install the \"Remote - Containers\" extension\n\nVisual Studio Code's \"Remote - Containers\" extension lets you run the editor in\na Docker container. Install the extension:\n\n- [Install the \"Remote - Containers\"\n  extension](vscode:extension/ms-vscode-remote.remote-containers)\n\n## Open the SpECTRE repository in Visual Studio Code\n\nNow open the SpECTRE repository that you have cloned to your computer in Visual\nStudio Code. Depending on your operating system you can select `File > Open`,\ntype `Cmd+O` (macOS) or `Ctrl+O` (Linux or Windows), drag the repository folder\nonto the Visual Studio Code icon or any other means to open the folder in Visual\nStudio Code.\n\nNow is also time to learn how to use the single most important tool in Visual\nStudio Code, the command palette. Try it now: Hit `Cmd+P` (macOS) or `Ctrl+P`\n(Linux or Windows) and start typing the name of any file, for example\n`QuickStartDockerVSCode.md`. Hit `Enter` to open the file. This is how you can\nquickly open any file in the repository. Note that the search is fuzzy, so you\ncan type any few letters in the path to the file. In addition to opening files,\nyou can hit `Cmd+Shift+P` (macOS) or `Ctrl+Shift+P` (Linux or Windows) and type\nthe name of any command that Visual Studio Code supports (or parts of it), for\nexample `Preferences: Open User Settings`.\n\n## Reopen the SpECTRE repository in the development container\n\nOpen the command palette by hitting `Cmd+Shift+P` (macOS) or `Ctrl+Shift+P`\n(Linux or Windows) and run the command `Remote-Containers: Reopen in container`\nby starting to type a few letters of this command and then hitting `Enter`.\n\nVisual Studio Code will download and run the container and drop you into a fully\nconfigured environment where you can proceed to compile and run SpECTRE. This\nmay take a few minutes so just let VSCode run and do it's thing. There may be a\n`show logs` button somewhere on your screen if you're interested in the details\nof the download.\n\nIf you are interested to learn more about how this feature works you can\nread through the [VS Code documentation on remote containers](https://code.visualstudio.com/docs/remote/containers)\nand inspect the `.devcontainer/devcontainer.json` file that's included in the\nSpECTRE repository.\n\n\\note If you look into the `.devcontainer/devcontainer.json` file, you'll see we\nare using the `sxscollaboration/spectre:dev` container. We also offer other\ncontainers. The `sxscollaboration/spectre:ci` container has more compilers and\nthe `sxscollaboration/spectre:demo` container has a couple pre-built\nexecutables. If you'd like to build in a different container, just switch the\nimage name in the json file and reopen in the new container.\n\n## Configure, compile and run SpECTRE\n\nWith Visual Studio Code running in the development container you can now\nconfigure, compile and run SpECTRE with no additional setup. Hit `Cmd+Shift+P`\n(macOS) or `Ctrl+Shift+P` (Linux or Windows) to open the command palette and run\nthe command `CMake: Select a Kit`. You should see a `Default` kit available.\nThen, open the command palette again, run `CMake: Select Variant`, and select\neither `Release` or `Debug`. For this tutorial we recommend using `Release`. If\nyou want to actually develop SpECTRE, the `Debug` option would be better. This\nspecific kit uses `clang` to build SpECTRE along with the Python bindings.\nFinally, open the command palette once more and run `CMake: Configure` which\nwill now actually run CMake.\n\nIt will set up a build directory. You can open\nVisual Studio Code's [integrated\nterminal](https://code.visualstudio.com/docs/editor/integrated-terminal) with\nthe keyboard shortcut ``Ctrl+` `` and navigate to the newly created build\ndirectory to inspect it:\n\n```\ncd build-Default-Debug\n```\n\nFor compiling and running SpECTRE you can either use Visual Studio Code's CMake\nintegration by looking up further commands in the command palette, or use the\nterminal. We will be using the terminal from now on. Try compiling an\nexecutable, for example:\n\n```\nmake -j4 ExportCoordinates3D\n```\n\nOnce the executable has compiled successfully you can try running it:\n\n```\n./bin/ExportCoordinates3D \\\n  --input-file $SPECTRE_HOME/tests/InputFiles/ExportCoordinates/Input3D.yaml\n```\n\nThis executable produced a volume data file in the `build-Default-Debug`\ndirectory that we can visualize in ParaView. Generate an XMF file from the\nvolume data file that ParaView understands:\n\n```\n./bin/spectre generate-xdmf \\\n  --file-prefix ExportCoordinates3DVolume --subfile-name element_data \\\n  --output ExportCoordinates3DVolume\n```\n\nSince the build directory is shared with the host file system you can now open\nParaView on your computer and load the generated XMF file as described in the\n\\ref tutorial_visualization \"visualization tutorial\".\n\n\\note If you wanted to run this executable again, you'd have to first remove the\nexisting `.h5` files, otherwise an error will occur.\n\n## Edit and contribute code to SpECTRE with VS Code\n\nYou are now ready to code! The other \\ref dev_guide \"dev guides\" will teach you\nhow to write SpECTRE code. Return to the [Visual Studio Code\ndocumentation](https://code.visualstudio.com/docs) to learn more about editing\ncode with Visual Studio Code.\n\nIn particular, the Visual Studio Code documentation can teach you how to use Git\nto commit your code changes to your repository:\n\n- [Git support in Visual Studio Code](https://code.visualstudio.com/docs/editor/versioncontrol#_git-support)\n\nSpECTRE code development follows a pull-request model. You can learn more about\nthis process in our contributing guide:\n\n- [Contributing to SpECTRE through pull requests](https://spectre-code.org/contributing_to_spectre.html#pull-requests)\n\nVisual Studio Code can also help you create and review pull requests:\n\n- [Working with pull requests in VS Code](https://code.visualstudio.com/docs/editor/github#_pull-requests)\n\n## Interactively debug a SpECTRE test with VS Code\n\nTo track down an issue with your code it can be very useful to interactively\ndebug it. First, make sure you have written a test case where the issue occurs.\nIf you have not written a test yet, this is a great time to do it. Refer to the\n\\ref writing_unit_tests \"Writing Unit Tests\" dev guide to learn how to write\ntests.\n\nTo launch the interactive debugger, hit `Cmd+Shift+P` (macOS) or `Ctrl+Shift+P`\n(Linux or Windows) to open the command palette, run the command `CMake: Set\nDebug Target` and select your test executable (e.g. `Test_LinearOperators`)\nThen run the command `CMake: Debug`.\nThe test executable will compile, run and stop on any breakpoints. Follow the\nVisual Studio Code documentation to learn how to set breakpoints in your code\nand inspect the state of your variables:\n\n- [Debug actions in VS Code](https://code.visualstudio.com/docs/editor/debugging#_debug-actions)\n\n## Real-time collaboration with Live Share\n\nYou can use the [Live\nShare](https://docs.microsoft.com/en-us/visualstudio/liveshare/) extension to\nwork together with others and edit code simultaneously. Live Share can be very\nuseful to debug code together. Follow these instructions to share a link to your\ncurrent Visual Studio Code workspace:\n\n- [Live Share Quickstart: Share your first project](https://docs.microsoft.com/en-us/visualstudio/liveshare/quickstart/share)\n\n\n## Tips and tricks\n\n- The [**GitLens extension**](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens)\n  is very useful to browse your repository. Select the \"Source Control\" icon and\n  explore the various panels.\n\n- When you build the **documentation** (e.g. with `make doc`), you can open it\n  in a web server within VS Code:\n\n  ```\n  python3 -m http.server -d docs/html\n  ```\n\n  The web server launches on port 8000 by default, which is being forwarded\n  outside the container, so you can just open http://127.0.0.1:8000 in your\n  browser to view the documentation.\n\n- Instead of working in the Docker container, you can use the [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh)\n  extension to **work on a remote machine** such as your local supercomputing\n  cluster. Ask the cluster administrators or users for suggestions concerning\n  installing and running SpECTRE on the particular supercomputer.\n\n- You can work with **Jupyter notebooks in Visual Studio Code**. First, install\n  Jupyter and all other Python packages you want to work with in the container:\n\n  ```\n  pip3 install jupyter matplotlib\n  ```\n\n  Any `.ipynb` notebook files you create or open will be displayed in VS Code's\n  notebook editor. This is very useful for quickly plotting data from SpECTRE\n  runs or using SpECTRE's Python bindings. Refer to the [VS Code documentation\n  on Jupyter support](https://code.visualstudio.com/docs/python/jupyter-support)\n  for more information.\n\n- Docker can quickly use up a lot of disk space. From time to time you\n  can \"prune\" unneeded images and containers to reclaim disk space:\n\n  - [Prune unused Docker objects](https://docs.docker.com/config/pruning/)\n\n  When pruning containers, make sure no data is deleted that you care about!\n"
  },
  {
    "path": "docs/DevGuide/RedistributingGridpoints.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Redistributing Gridpoints {#redistributing_gridpoints}\n\n\\tableofcontents\n\n## Introduction\nThe simplest way to construct a volume map from two parameterized surfaces is\nby linearly interpolating between them:\n\n\\f[\\vec{x}(\\xi,\\eta,\\zeta) =\n\\frac{1-\\zeta}{2}\\vec{\\sigma}_-(\\xi,\\eta)+\n\\frac{1+\\zeta}{2}\\vec{\\sigma}_+(\\xi,\\eta)\\f]\n\nIn the above example, each surface \\f$\\vec{\\sigma}_+\\f$ and\n\\f$\\vec{\\sigma}_-\\f$ is parameterized using the logical coordinates \\f$\\xi\\f$\nand \\f$\\eta\\f$, and a third coordinate \\f$\\zeta\\in[-1,1]\\f$ is used to\ninterpolate between them.\n\nWe then distribute gridpoints on this volume by specifying values of the\ncoordinates \\f$\\xi,\\eta,\\f$ and \\f$\\zeta\\f$ at which the gridpoints are located.\nIn SpECTRE these values are the locations of the quadrature nodes. The\ndistribution of the gridpoints throughout the volume depends on the\nparameterization used, and the simplest choice of parameterization does not\nnecessarily lead to the best gridpoint distribution. In this section we discuss\nsituations in which there exist better parameterizations than those obtained by\nlinear interpolation.\n\n## Generalized Logical Coordinates\n\nIn each of the following examples, we will obtain functions \\f$\\Xi(\\xi),\n\\mathrm{H}(\\eta),\\f$ and \\f$\\mathrm{Z}(\\zeta)\\f$ that give better gridpoint\ndistributions than using the logical coordinates alone. Where possible, we will\nwrite the reparameterized map such that the functional form of the map is\nunchanged when replacing \\f$\\Xi\\f$ with \\f$\\xi\\f$, etc. We therefore refer to\n\\f$\\Xi, \\mathrm{H},\\f$ and \\f$\\mathrm{Z}\\f$ as the\n*generalized logical coordinates*, as they can also refer to the logical\ncoordinates \\f$\\xi, \\eta,\\f$ and \\f$\\zeta\\f$ themselves, when the transformation\nis the identity.\n\n## Equiangular Maps\n\nThe mapping for a cubed sphere surface can be easily obtained by taking points\nthat lie on each face of a cube and normalizing them such that they lie on the\nsphere:\n\n\\f[\\vec{\\sigma}_{+z}(\\xi,\\eta) =\n\\frac{1}{\\sqrt{1 + \\xi^2  + \\eta^2}}\n\\begin{bmatrix}\n\\xi\\\\\n\\eta\\\\\n1\\\\\n\\end{bmatrix}\\f]\n\nIn the above example the parameterization used for the upper \\f$+z\\f$ surface\nof the cube is linear in \\f$\\xi\\f$ and \\f$\\eta\\f$. However, distances measured\non the surface of the sphere are not linear in \\f$\\xi\\f$ and \\f$\\eta\\f$. To see\nthis, one may compute \\f$g_{\\xi\\xi} = |\\frac{\\partial\\vec{x}}{\\partial\\xi}|^2\\f$\nto see how distances are measured in terms of \\f$\\xi\\f$:\n\n\\f[g_{\\xi,\\xi}|_{\\eta=0} = \\frac{1}{(1+\\xi^2)^2}\\f]\n\nThis metric term demonstrates that a gridpoint distribution uniform in\n\\f$\\xi\\f$ will end up being compressed near \\f$\\xi=\\pm1\\f$. Suppose we\nreparameterized the surface using the generalized logical coordinate\n\\f$\\Xi\\in[-1,1]\\f$. We would find:\n\n\\f[g_{\\xi,\\xi}|_{\\eta=0} = \\frac{\\Xi'^2}{(1+\\Xi^2)^2}\\f]\n\nIdeally, we would like distances measured along a curvilinear surface to be\nlinear in the logical coordinates. We solve the differential equation and\nobtain:\n\n\\f[\\Xi = \\tan(\\xi\\pi/4)\\f]\n\nThese two parameterizations of the cubed sphere are known as the *equidistant*\nand *equiangular* central projections of the cube onto the sphere. We now\nsummarize their usage in SpECTRE CoordinateMaps that have\n`with_equiangular_map` as a specifiable parameter:\n\nIn the case where `with_equiangular_map` is `true`, we have the\nequiangular coordinates\n\n\\f[\\textrm{equiangular xi} : \\Xi(\\xi) = \\textrm{tan}(\\xi\\pi/4)\\f]\n\n\\f[\\textrm{equiangular eta}  : \\mathrm{H}(\\eta) = \\textrm{tan}(\\eta\\pi/4)\\f]\n\nwith derivatives\n\n\\f[\\Xi'(\\xi) = \\frac{\\pi}{4}(1+\\Xi^2)\\f],\n\n\\f[\\mathrm{H}'(\\eta) = \\frac{\\pi}{4}(1+\\mathrm{H}^2)\\f]\n\nIn the case where `with_equiangular_map` is `false`, we have the equidistant\ncoordinates\n\n\\f[ \\textrm{equidistant xi}  : \\Xi = \\xi\\f]\n\n\\f[ \\textrm{equidistant eta}  : \\mathrm{H} = \\eta\\f]\n\nwith derivatives:\n\n\\f[\\Xi'(\\xi) = 1\\f] \\f[\\mathrm{H}'(\\eta) = 1\\f]\n\n##  Projective Maps\n\nThe mapping for any convex quadrilateral can be obtained by bilinearly\ninterpolating between each vertex \\f$\\vec{x}_1, \\vec{x}_2, \\vec{x}_3\\f$\nand \\f$\\vec{x}_4\\f$:\n\n\\f[\\vec{x}(\\xi,\\eta) =\n\\frac{(1-\\xi)(1-\\eta)}{4}\\vec{x}_1+\n\\frac{(1+\\xi)(1-\\eta)}{4}\\vec{x}_2+\n\\frac{(1-\\xi)(1+\\eta)}{4}\\vec{x}_3+\n\\frac{(1+\\xi)(1+\\eta)}{4}\\vec{x}_4\n\\f]\n\nIn the case of a trapezoid where two of the sides are parallel, it is\nappropriate to linearly interpolate along the parallel sides. However,\nlinearly interpolating between the two bases results in a less than\nideal gridpoint distribution. This happens in the case of SpECTRE's Frustum,\nwhere the logical coordinate \\f$\\zeta\\f$ interpolates between the bases.\n\n\\image html BilinearVProjective.png \"Comparison of mappings. (Noah Veltman)\"\n\nAs seen in Veltman's [Warp-Off]\n(https://bl.ocks.org/veltman/8f5a157276b1dc18ce2fba1bc06dfb48), linear\ninterpolation between the two bases results in a uniformly spaced grid\nbetween the bases of the frustum. This causes elements near the smaller base\nto be longer in the direction normal to the base, and elements near the larger\nbase to be shorter in the direction normal to the base. We desire elements that\nhave roughly equal sizes along each of their dimensions.\n\nWe can redistribute the gridpoints in the \\f$\\zeta\\f$ direction using a\nprojective map, moving more gridpoints toward the smaller base. We can also see\nin the figure above that a projective map can be applied incorrectly, leaving\nelements distorted at the opposite end. From this we can see that it is\nimportant to control the degree of projection.\n\nWe adapt a technique from projective geometry to obtain the desired grid\nspacing. The heart of the method lies in the fact that objects arranged in a\nline at equal distances from one another will appear to converge as they\napproach the horizon.\n\n\\image html ProjectionOntoPlane.png \"Controlling the degree of projection.\"\n\nThe above diagram demonstrates how to obtain a nonlinearly parameterized\nobject (seen in red) from a linearly parameterized one (seen in purple).\nThis is done by lifting the linearly parameterized object into a higher\nspatial dimension \\f$w\\f$, such that its projection onto the plane remains\nunchanged. As seen above, \\f$w_{\\delta}\\f$ controls the degree of projection\nof one end of the object (purple) into a higher spatial dimension \\f$w\\f$.\nIn projective geometry, these points that exist in the higher dimension are\nlabeled with *homogeneous coordinates* \\f$\\tilde{x}, \\tilde{y}, \\tilde{z}, w\\f$,\nto distinguish them from the Cartesian coordinates that label points that exist\non the \\f$w=1\\f$ hyperplane, \\f$x,y,z\\f$. The resulting grid (seen in red) is\nobtained by projecting back into the \\f$w=1\\f$ hyperplane. The Cartesian\ncoordinates are obtained by dividing each homogeneous coordinate of the\nlinearly parameterized object by its respective \\f$w\\f$ coordinate value.\n\nThe parametric equation for the purple object seen above in homogeneous\ncoordinates is:\n\\f[\\begin{bmatrix}\\tilde{x}\\\\\\tilde{y}\\\\\\tilde{z}\\\\w\\\\\\end{bmatrix}=\n\\frac{1-\\zeta}{2}\\begin{bmatrix}x_1\\\\y_1\\\\z_1\\\\1\\\\\\end{bmatrix}+\n\\frac{1+\\zeta}{2}\\begin{bmatrix}x_2w_{\\delta}\\\\y_2w_{\\delta}\\\\\nz_2w_{\\delta}\\\\w_{\\delta}\\\\\\end{bmatrix}\\f]\n\nThe equation for the projected red object in Cartesian coordinates is\nobtained by dividing by w:\n\\f[\\vec{x}(\\zeta) = \\frac{1}{w(\\zeta)}\n\\begin{bmatrix}\n\\tilde{x}(\\zeta)\\\\\n\\tilde{y}(\\zeta)\\\\\n\\tilde{z}(\\zeta)\\\\\n\\end{bmatrix}\\f]\n\nWe wish to cast our parametric equation for the surface into the form:\n\\f[\\vec{x}(\\zeta) =\n\\frac{1-\\mathrm{Z}}{2}\\vec{x}_1 + \\frac{1+\\mathrm{Z}}{2}\\vec{x}_2\\f]\nfor some appropriate choice of auxiliary variable `projective_zeta`\n\\f$ = \\mathrm{Z}(\\zeta)\\f$. We would also like for \\f$\\mathrm{Z}\\f$ to reduce to\n\\f$\\zeta\\f$ when \\f$w_{\\delta}\\ = 1\\f$.\n\nDefining the auxiliary variables \\f$w_{\\pm} := w_{\\delta}\\pm 1\\f$, the desired\n\\f$\\mathrm{Z}(\\zeta)\\f$ is given by:\n\\f[\\mathrm{Z} = \\frac{w_- + \\zeta w_+}\n{w_+ + \\zeta w_-}\\f]\n\nwith derivative:\n\\f[\\mathrm{Z}' = \\frac{\\partial\\mathrm{Z}}{\\partial \\zeta} =\n\\frac{w_+^2 - w_-^2}{(w_+ + \\zeta w_-)^2}\\f]\n\nFor SpECTRE CoordinateMaps that have `projective_scale_factor` as a specifiable\nparameter, the value \\f$w_{\\delta} = 1\\f$ should be supplied in case the user\ndoes not want to use projective scaling. If `auto_projective_scale_factor` is\nset to `true`, the map will compute a value of \\f$w_{\\delta}\\f$ that is\nappropriate.\n"
  },
  {
    "path": "docs/DevGuide/StaticAnalysis.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Static Analysis Tools {#static_analysis_tools}\n\n\\tableofcontents\n\nSpECTRE code is frequently run through the\n[Clang Tidy](http://clang.llvm.org/extra/clang-tidy/) static analyzer.\nSince analyzing a single source file can take over half a minute it\nis generally not advisable to run clang-tidy over the entire code base.\nIf CMake isn't finding clang-tidy, make sure clang-tidy is installed and that\nyou have chosen clang as your compiler. To analyze a single source file\nrun, for example\n`make clang-tidy FILE=/path/to/source/src/DataStructures/DataVector.cpp`.\nTo analyze the entire code base run `make clang-tidy-all`. To analyze all\nchanged C++ source files in the commits from `FIRST_HASH` to `HEAD`, run `make\nclang-tidy-hash HASH=FIRST_HASH`.\n"
  },
  {
    "path": "docs/DevGuide/TensorExpressions.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Writing Tensor Equations with TensorExpressions {#writing_tensorexpressions}\n\n\\tableofcontents\n\nSpECTRE's `TensorExpression`s interface allows you to write tensor equations in\nSpECTRE in C++ with syntax that resembles tensor index notation. To use it,\nsimply add this include to the top of your file:\n```\n#include \"DataStructures/Tensor/Tensor.hpp\"\n```\nThe following guide assumes a basic understanding of the `Tensor` class and\n\\ref tnsr \"tnsr\" type aliases. **RHS** refers to the right hand side expression\nthat we wish to compute and **LHS** refers to the resulting left hand side\ntensor that stores the result of computing the RHS expression.\n\n# Syntax {#te_syntax}\n`TensorExpression`s are arithmetic expressions of `Tensor`s that can be\nevaluated using `tenex::evaluate`. Terms used in the expression may be `Tensor`s\nor numbers (see [supported types](#te_data_type_support)).\n\nAs a simple example of how `TensorExpression`s are used, if you would like to\nraise the index of some `Tensor` `R` with some inverse spacetime metric `Tensor`\n`g`, i.e. \\f$R^c{}_b = R_{ab} g^{ac}\\f$, you can compute this with\n`TensorExpression`s by doing:\n\n\\snippet Expressions/Test_Examples.cpp te_example_evaluate_lhs_return\n\nwhere `R_up`, `R`, and `g` are rank 2 spacetime `Tensor`s and the `ti::*`\nvariables are `TensorIndex`s representing generic tensor indices. Here is a\nbreakdown of the different parts of this line:\n- the RHS expression to compute is the argument to\n\\ref tenex::evaluate \"evaluate\": `R(ti::a, ti::b) * g(ti::A, ti::C)`\n- the result LHS `Tensor` is `R_up`\n- the LHS `Tensor`'s indices are the template arguments to\n\\ref tenex::evaluate \"evaluate\": `ti::C, ti::b`\n\nThe LHS \\ref ::Symmetry \"Symmetry\" will be deduced from the RHS tensors'\nsymmetries and order of operations.\n\nAlternatively, if you already have a LHS `Tensor` variable, you can pass it into\nthe following \\ref tenex::evaluate \"evaluate\" overload, where the LHS `Tensor`\nprovided will be assigned to the result of the RHS expression:\n\n\\snippet Expressions/Test_Examples.cpp te_example_evaluate_lhs_arg\n\nNote that to use this \\ref tenex::evaluate \"evaluate\" overload, the LHS `Tensor`\ndoes not need to be previously sized unless the data type is a Blaze vector type\n(e.g. `DataVector`) *and* the RHS expression contains no `Tensor` terms\n(see [example](#te_assigning_to_a_number) where sizing is necessary). This\noverload is useful in a couple cases:\n- [Specifying the LHS symmetry](#te_specify_lhs_symmetry): One advantage of this\noverload is that it uses the \\ref ::Symmetry \"Symmetry\" of the provided LHS\ntensor instead of deducing it from the RHS expression. This enables you to\nspecify the LHS symmetry in cases where the previous\n\\ref tenex::evaluate \"evaluate\" overload does not deduce the one you want. While\nthe LHS index order in this example (`ti::C, ti::b`) could theoretically be\ndeduced from the index types of `R_up`, we still require specifying them because\nthis isn't the case for all equations and we would like to have a unified\ninterface. See [this example](#te_specify_lhs_symmetry), which demonstrates a\ncase where you might want to specify the LHS symmetry and where the LHS index\norder would not be deducible.\n- **[Using spatial and time indices on LHS spacetime indices](#te_spatial_time_index_lhs)**\n\n## Tensor indices {#te_tensor_indices}\n\n`TensorIndex`s represent generic tensor indices and are supplied as\ncomma-separated lists in two places:\n- in parentheses for each tensor in the RHS expression\n- in the template parameters of \\ref tenex::evaluate \"evaluate\" to specify the\norder of the LHS result tensor's indices.\n\nEach `TensorIndex` takes the form `ti::*` where `*` is a letter that encodes\nindex properties:\n- Uppercase letters denote upper indices and lowercase letters denote lower\nindices\n- Letters `A/a - H/h` indicate spacetime indices, `I/i - N/n` indicate spatial\nindices, and `T/t` indicates a concrete time index. This is what is currently\ndefined, but more spatial and spacetime indices (letters) can easily be added if\nneeded. Note that there is no precedence or difference between the indices of\nsome type, e.g. `ti::a`, `ti::b`, ... `ti::h` are equivalent\n\nThe properties of each `TensorIndex` and the `Tensor`'s indices (typelist of\n\\ref SpacetimeIndex \"TensorIndexType\"s) must be compatible:\n- valences (being upper or lower indices) must match\n- if a `Tensor`'s index is spacetime, you can use a spacetime `TensorIndex`,\nspatial `TensorIndex`, or concrete time `TensorIndex`\n- if a `Tensor`'s index is spatial, you must use a spatial `TensorIndex`\n\nTo demonstrate correct and incorrect usage, let's say we have tensors\n\\f$R_{ab}\\f$ (two spacetime indices, e.g. type \\ref tnsr \"tnsr::ab\") and\n\\f$S_{ij}\\f$ (two spatial indices, e.g. type \\ref tnsr \"tnsr::ij\"):\n\n```\nR(ti::c, ti::d) // OK\nR(ti::c, ti::k) // OK, can use spatial TensorIndex on spacetime index\nR(ti::c, ti::t) // OK, can use time TensorIndex on spacetime index\nR(ti::c, ti::D) // ERROR, ti::D is upper but the 2nd index is lower\nS(ti::j, ti::k) // OK\nS(ti::a, ti::k) // ERROR, can't use spacetime TensorIndex on a spatial index\nS(ti::j, ti::t) // ERROR, can't use time TensorIndex on a spatial index\n```\n\n# Examples {#te_examples}\n## Basic operations {#te_basic_operations}\n\nIn the following examples:\n- `R` is type \\ref tnsr \"tnsr::ab<DataVector, 3>\"\n- `S` is type \\ref tnsr \"tnsr::ab<DataVector, 3>\"\n- `T` is type \\ref Scalar \"Scalar<DataVector>\"\n- `U` is type \\ref tnsr \"tnsr::Ab<DataVector, 3>\"\n- `V` is type \\ref tnsr \"tnsr::aBC<DataVector, 3>\"\n- `G` is type \\ref tnsr \"tnsr::a<DataVector, 3>\"\n- `H` is type \\ref tnsr \"tnsr::A<DataVector, 3>\"\n\n### Addition and subtraction {#te_addition_and_subtraction}\n\\f$L_{ab} = R_{ab} + S_{ba}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_addition\n\n\\f$L = 1 - T\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_subtraction\n\n### Contraction of a single tensor {#te_contraction}\n\\f$L = U^{a}{}_{a}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_contraction_to_scalar\n\n\\f$L^b = V_{a}{}^{ba}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_contraction_to_tensor\n\n### Inner and outer products {#te_products}\n\\f$L = G_a H^{a}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_inner_product\n\n\\f$L_{cb} = T G_a G_c U^{a}{}_{b}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_inner_and_outer_product\n\n### Division {#te_division}\n\\f$L_a = \\frac{G_a}{2}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_division_by_number\n\n\\f$L_{ba} = \\frac{R_{ab}}{T}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_division_by_tensor\n\n\\f$L = \\frac{5}{U^{a}{}_{a} + 1}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_division_by_tensor_expression\n\n### Square root {#te_square_root}\n\\f$L = \\sqrt{T}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_square_root_tensor\n\n\\f$L = \\sqrt{G_a H^a}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_square_root_inner_product\n\n## More features {#te_more_features}\n\n### Specifying the LHS symmetry {#te_specify_lhs_symmetry}\nWhen using the \\ref tenex::evaluate \"evaluate\" overload that returns the LHS\n`Tensor`, the \\ref ::Symmetry \"Symmetry\" of the LHS `Tensor` will be deduced\nfrom the RHS expression. However, in some cases the deduced LHS symmetry may\nnot be what you want. To specify it yourself, you can pass your LHS `Tensor`\n(that has the desired \\ref ::Symmetry \"Symmetry\") to the\n\\ref tenex::evaluate \"evaluate\" overload that takes the LHS `Tensor` as the\nfirst argument.\n\nFor example, if we have \\f$L_{ab} = R_a R_b\\f$, the indices of \\f$L\\f$ are\nsymmetric. However, when we do:\n\n\\snippet Expressions/Test_Examples.cpp te_example_deduced_lhs_symmetry_fail\n\nthe type of `L` will be \\ref tnsr \"tnsr::ab\" because it is not known at\ncompile time that the two vectors in the product are the same. To override the\ndeduced symmetry and make it a symmetric result, we can create a\n\\ref tnsr \"tnsr::aa\" and pass it into the other overload:\n\n\\snippet Expressions/Test_Examples.cpp te_example_deduced_lhs_symmetry_force\n\n### Assigning to a number {#te_assigning_to_a_number}\nYou can assign a number (e.g. `double`) to a `Tensor` of any rank to fill all\ncomponents with that value, e.g. \\f$L_{ab} = -1\\f$. How you do that is slightly\ndifferent depending on the underlying data type of your `Tensor`:\n\n- When your `Tensor`'s data type is a number type (e.g. `double`,\n`std::complex<double>`):\n\n\\snippet Expressions/Test_Examples.cpp te_example_assign_number_to_tensor_of_numbers\n\n- When your `Tensor`'s data type is a Blaze vector type (e.g. `DataVector`,\n`ComplexDataVector`), the `Tensor` must first be sized before calling\n\\ref tenex::evaluate \"evaluate\" because there is no sizing information (from a\n`Tensor` component) in the RHS expression:\n\n\\snippet Expressions/Test_Examples.cpp te_example_assign_number_to_tensor_of_vectors\n\nSee [supported number types](#te_data_type_support) for the data types that the\nRHS number can be.\n\n### Using spatial and time indices on RHS spacetime indices {#te_spatial_time_index_rhs}\nIf a `Tensor` has spacetime indices, you can use generic spatial indices and\nconcrete time indices to refer to a subset of the components, as we see in\nliterature.\n\nLapse \\f$\\alpha\\f$ computed from the spacetime metric \\f$g_{ab}\\f$ and shift\n\\f$\\beta^i\\f$:\n\n\\f$\\alpha = \\sqrt{\\beta^i g_{it} - g_{tt}}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_rhs_spatial_and_time_indices\n\n### Using spatial and time indices on LHS spacetime indices {#te_spatial_time_index_lhs}\nRelated to the previous example, you can also use generic spatial indices and\nconcrete time indices for the spacetime indices of the LHS `Tensor` to assign\nsubsets of the LHS `Tensor`'s components.\n\nSpacetime metric \\f$g_{ab}\\f$ computed from the lapse \\f$\\alpha\\f$, shift\n\\f$\\beta^i\\f$, and spatial metric \\f$\\gamma_{ij}\\f$:\n\n\\f$g_{tt} = -\\alpha^2 + \\beta^m \\beta^n \\gamma_{mn}\\f$\n\n\\f$g_{ti} = \\gamma_{mi} \\beta^m\\f$\n\n\\f$g_{ij} = \\gamma_{ij}\\f$\n\n\\snippet Expressions/Test_Examples.cpp te_example_lhs_spatial_and_time_indices\n\n\\parblock\n\\note The above example is for demonstration purposes only. In practice, you\nwould want to avoid repeating computing the reused quantity\n\\f$\\beta^i \\gamma_{ij}\\f$ by e.g. storing the result of the reused quantity in\nanother variable.\n\\endparblock\n\n### Using the LHS Tensor in the RHS expression {#te_using_lhs_tensor_in_rhs}\nYou can use the LHS `Tensor` in the RHS expression to emulate operations like\n`+=`, `*=`, etc. For example, say you would like to emulate the following:\n```\n// pseudocode\nL_ab = R_ab\nL_ab += 2.0 * S_ba\n```\nYou can emulate the `+=` operation by calling \\ref tenex::update \"update\"\ninstead of \\ref tenex::evaluate \"evaluate\":\n```\nauto L = tenex::evaluate<ti::a, ti::b>(R(ti::a, ti::b));\n// use the LHS tensor in the RHS\ntenex::update<ti::a, ti::b>(\n    make_not_null(&L), L(ti::a, ti::b) + 2.0 * S(ti::b, ti::a));\n```\nOne limitation is that when using the LHS tensor in the RHS expression, the\nLHS tensor's index order must be the same on the LHS and RHS. This means that\nthe order of the `TensorIndex` template parameters of\n\\ref tenex::update \"update\" must match the order of the `TensorIndex` arguments\nin the parentheses that come after the LHS `Tensor` in the RHS expression. For\nexample, the following is not allowed and will yield a runtime error:\n```\n// ERROR: index order for L on LHS and RHS is not the same\ntenex::update<ti::a, ti::b>(\n    make_not_null(&L), L(ti::b, ti::a) + 2.0 * S(ti::b, ti::a));\n```\n\n\\parblock\n\\note It is not advised to use very large RHS expressions with\n\\ref tenex::update \"update\" because runtime performance does not scale well as\nthe number of operations gets very large. This is because\n\\ref tenex::evaluate \"evaluate\" breaks up large expressions into smaller ones,\nbut \\ref tenex::update \"update\" cannot. One way around this is to break up the\nexpression and use more than one call to \\ref tenex::update \"update\".\n\\endparblock\n\n# Compile time math checks {#te_compile_time_math_checks}\nFor all operations, mathematical legality is checked at compile time. The\ncompiler will catch what is not sound to write on paper, which includes things\nlike no repeated indices, can't divide by a tensor with rank > 0, and that\nspatial dimensions, frames, valences, index types (spatial or spacetime), and\nranks of tensors match where they should.\n\nHere are some examples of illegal math that the compiler will catch:\n\n```\ntnsr::ab<double, 3, Frame::Inertial> R{};\ntnsr::ab<double, 3, Frame::Inertial> S{};\ntnsr::ab<double, 3, Frame::Grid> T{};\ntnsr::AB<double, 2, Frame::Inertial> G{};\n// ERROR: LHS and RHS indices don't match\nauto result1 = tenex::evaluate<ti::a, ti::c>(R(ti::a, ti::b) + S(ti::a, ti::b));\n// ERROR: Can't add Tensors with different indices\nauto result2 = tenex::evaluate<ti::a, ti::b>(R(ti::a, ti::b) + S(ti::a, ti:c));\n// ERROR: Repeated index in the RHS\nauto result3 =\n    tenex::evaluate<ti::a, ti::b, ti::c>(R(ti::a, ti::b) * S(ti::a, ti::c));\n// ERROR: Can't add Tensors with different Frame types\nauto result4 = tenex::evaluate<ti::a, ti::b>(R(ti::a, ti::b) + T(ti::a, ti::b));\n// ERROR: Can't contract indices with different number of spatial dimensions\nauto result5 = tenex::evaluate(R(ti::a, ti::b) * G(ti::A, ti::B));\n// ERROR: Can't divide by a rank > 0 Tensor\nauto result6 = tenex::evaluate<ti::a, ti::b>(R(ti::a, ti::b) / S(ti::a, ti::b));\n```\n\n# Support for data types and operations {#te_data_type_and_op_support}\n\n## Data types {#te_data_type_support}\nThe RHS expression may contain a mixture of number terms and `Tensor` terms,\ne.g. `0.5 * T(ti::a)`.\n\nCurrently supported data types for number terms:\n- `double`\n- `std::complex<double>`\n\nCurrently supported underlying data types for `Tensor` terms:\n- `double`\n- `std::complex<double>`\n- `DataVector`\n- `ComplexDataVector`\n\nSupport for more types can be added.\n\n## Operations {#te_operation_support}\nIt's possible for terms in the RHS expression to have different data types. The\nmixture of `double`s and `Tensor<double>`s is shown in the\n[subtraction example](#te_addition_and_subtraction) and the\n[division examples](#te_division).\n\nIt's also possible for terms to be a mixture of both real-valued and\ncomplex-valued numbers or `Tensor`s. For example, we can compute\n\\f$z^i = x^i + i y*i\\f$, where \\f$x\\f$ and \\f$y\\f$ are real-valued `Tensor`s,\n`i` is a `std::complex<double>`, and \\f$z\\f$ is a complex-valued `Tensor`.\n\n\\snippet Expressions/Test_Examples.cpp te_example_complex_vector\n\nIn the above example, a `std::complex<double>` is multiplied by a\n`Tensor<DataVector>`, which can be thought to have an \"intermediate\" type\n`Tensor<ComplexDataVector>`, and then a `Tensor<DataVector>` is added to that\nintermediate `Tensor<ComplexDataVector>` to yield the resulting type,\n`Tensor<ComplexDataVector>`.\n\nThe following table shows the data type that results from performing a binary\noperation (`+`, `-`, `*`, `/`) between two terms of given data types:\n\n<table>\n  <caption id=\"multi_row\">\n      Data types resulting from binary operations between supported\n      TensorExpression operand types\n  </caption>\n\n  <tr>\n    <th></th>\n    <th><code>double</code></th>\n    <th><code>std::complex&lt;double&gt;</code></th>\n    <th><code>Tensor&lt;double&gt;</code></th>\n    <th><code>Tensor&lt;std::complex&lt;double&gt;&gt;</code></th>\n    <th><code>Tensor&lt;DataVector&gt;</code></th>\n    <th><code>Tensor&lt;ComplexDataVector&gt;</code></th>\n  </tr>\n\n  <tr>\n    <th><code>double</code></th>\n    <td><code>double</code></td>\n    <td>-</td>\n    <td>-</td>\n    <td>-</td>\n    <td>-</td>\n    <td>-</td>\n  </tr>\n\n  <tr>\n    <th><code>std::complex&lt;double&gt;</code></th>\n    <td><code>std::complex&lt;double&gt;</code></td>\n    <td><code>std::complex&lt;double&gt;</code></td>\n    <td>-</td>\n    <td>-</td>\n    <td>-</td>\n    <td>-</td>\n  </tr>\n\n  <tr>\n    <th><code>Tensor&lt;double&gt;</code></th>\n    <td><code>Tensor&lt;double&gt;</code></td>\n    <td><code>Tensor&lt;std::complex&lt;double&gt;&gt;</code></td>\n    <td><code>Tensor&lt;double&gt;</code></td>\n    <td>-</td>\n    <td>-</td>\n    <td>-</td>\n  </tr>\n\n  <tr>\n    <th><code>Tensor&lt;std::complex&lt;double&gt;&gt;</code></th>\n    <td><code>Tensor&lt;std::complex&lt;double&gt;&gt;</code></td>\n    <td><code>Tensor&lt;std::complex&lt;double&gt;&gt;</code></td>\n    <td><code>Tensor&lt;std::complex&lt;double&gt;&gt;</code></td>\n    <td><code>Tensor&lt;std::complex&lt;double&gt;&gt;</code></td>\n    <td>-</td>\n    <td>-</td>\n  </tr>\n\n  <tr>\n    <th><code>Tensor&lt;DataVector&gt;</code></th>\n    <td><code>Tensor&lt;DataVector&gt;</code></td>\n    <td><code>Tensor&lt;ComplexDataVector&gt;</code><strong>*</strong></td>\n    <td>Not supported</td>\n    <td>Not supported</td>\n    <td><code>Tensor&lt;DataVector&gt;</code></td>\n    <td>-</td>\n  </tr>\n\n  <tr>\n    <th><code>Tensor&lt;ComplexDataVector&gt;</code></th>\n    <td><code>Tensor&lt;ComplexDataVector&gt;</code></td>\n    <td><code>Tensor&lt;ComplexDataVector&gt;</code></td>\n    <td>Not supported</td>\n    <td>Not supported</td>\n    <td><code>Tensor&lt;ComplexDataVector&gt;</code></td>\n    <td><code>Tensor&lt;ComplexDataVector&gt;</code></td>\n  </tr>\n</table>\n\n\\parblock\n\\note The only binary operation that is supported between `std::complex<double>`\nand `Tensor<DataVector>` is multiplication. This is because Blaze does not\nsupport addition, subtraction, nor division between `std::complex<double>` and\n`DataVector`.\n\\endparblock"
  },
  {
    "path": "docs/DevGuide/Tmp.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# SFINAE {#sfinae}\n\n\\tableofcontents\n\nSFINAE, Substitution Failure Is Not An Error, means that if a deduced template\nsubstitution fails, compilation must continue. This can be exploited to make\ndecisions at compile time. See [here](http://nilsdeppe.com/posts/tmpl-part1)\nfor a discussion using `std::enable_if` to remove certain functions from\noverload resolution or certain template specializations from name lookup.\nAnother method of controlling name lookup resolution is using\n`std::void_t`. `void_t` is a metafunction from types to `void`, that is\n\n```cpp\ntemplate <typename... Args>\nusing void_t = void;\n```\n\n`void_t` is useful when used in combination with `decltype` and `std::declval`\nto probe if a type has certain members. For example, we can implement a type\ntrait to check if a type `T` is iterable by first have the general definition\ninherit from `std::false_type` as follows,\n\n```cpp\ntemplate <typename T, typename = void>\nstruct is_iterable : std::false_type {};\n```\n\nNext we will have specialization that uses `void_t` to check if the type `T`\nhas a `begin()` and `end()` function.\n\n```cpp\ntemplate <typename T>\nstruct is_iterable<T, std::void_t<decltype(std::declval<T>().begin(),\n                                           std::declval<T>().end())>>\n    : std::true_type {};\n```\n\nWhat is happening here? Well, we use `std::declval` to convert the type `T`\nto a reference type, which allows us to call member functions inside `decltype`\nexpressions without having to construct an object. First we try to call the\nmember function `begin()` on `std::declval<T>()`, and if that succeeds we\nthrow away the result using the comma operator. Next we try to call `end()`\non `std::declval<T>()`, which, if it succeeds we get the return type of\nusing `decltype`. Note that `decltype` is important because we can only call\nmember functions on reference types inside of `decltype`, not just anywhere.\nFinally, if all this succeeded use `void_t` to metareturn `void`, otherwise\nthe template parameters of `void_t` fail to evaluate and the specialization\ncannot be resolved during name lookup. We could just as well use\n\n```cpp\ntemplate <typename T>\nstruct is_iterable<T, std::void_t<decltype(std::declval<T>().begin()),\n                                  decltype(std::declval<T>().end())>>\n    : std::true_type {};\n```\n\nWhich of the two implementations of the `is_iterable` is preferred is simply\na matter of taste, both behave correctly.\n\nIf you're reading closely you might wonder why the `void_t` is necessary at\nall, why not just `decltype(...)`? Well the reason is that since the default\ntemplate parameter metavalue is `void`, the specialization cannot be resolved\nduring name lookup unless the second template parameter in the specialization\nis either `void` as well or is explicitly specified when the class template\nis being invoked. Thus, the clearest implementation probably is\n\n```cpp\ntemplate <typename T, typename = std::void_t<>>\nstruct is_iterable : std::false_type {};\n\ntemplate <typename T>\nstruct is_iterable<T, std::void_t<decltype(std::declval<T>().begin(),\n                                           std::declval<T>().end())>>\n    : std::true_type {};\n```\n\nYou could now also define a helper type alias and constexpr boolean\n\n```cpp\ntemplate <typename T>\nusing is_iterable_t = typename is_iterable<T>::type;\n\ntemplate <typename T>\nconstexpr bool is_iterable_v = is_iterable<T>::value;\n```\n"
  },
  {
    "path": "docs/DevGuide/Variables.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Using Variables in SpECTRE {#variables_foundations}\n\n# What is a Variables and Why Use Them?\nVariables are a data structure that hold a contiguous memory block with Tensors\npointing to it. Variables temporaries allow you to declare temporary tensors and\nscalars so that you can do all allocations needed for a computation at one time.\nSince physical memory is shared between CPU cores, processes can't allocate in\nparallel since they might try to allocate to the same chunk of memory. As more\nCPU cores are used, this becomes a bottleneck, slowing down or stopping other\nprocesses while memory is being allocated. Using a Variables to allocate all\nmemory needed at once can improve efficiency allowing the computation to\noperate smoothly and uninterrupted.\n\n# Defining a Variables of Temporary Tags\nTo define a Variables, you'll need the TempTensor and Variables headers\n```cpp\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n```\nthis will give you access to temporary Scalars and Tensors we'll need to\nallocate. You can define a Variables that allocates one Scalar with something\nlike this:\n```cpp\nVariables<tmpl::list<::Tags::TempScalar<0>>>\ntemp_buffer{get<0,0>(spatial_metric).size()};\n```\nHere, the Variables we've defined `temp_buffer` provides a tmpl::list with a\nTempScalar inside as the template argument, this will allocate a single\ntemporary scalar. The size and DataType of the TempScalar is deduced by what's\ninside the {}, you can provide any tensor or std::array with the correct size\nneeded. Now, to use the allocation you've made, you can do:\n```cpp\nauto& useful_scalar = get<::Tags::TempScalar<0>>(temp_buffer);\n```\n\n# Real Use Example\nNow that we've got the basics, using them to allocate multiple Scalars and\nTensors is quite easy. For instance, let's say I need to allocate 2 scalars,\na spatial vector and 2 lower rank 2 tensors for my function.\n```cpp\nVariables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n::Tags::TempI<0, 3, Frame::Inertial>, ::Tags::Tempij<0, 3, Frame::Inertial>,\n::Tags::Tempij<1, 3, Frame::Inertial>>>\ntemp_buffer{get<0,0>(spatial_metric).size()};\n```\nHere, when we allocate for the same type of scalar or tensor (rank 2 lower) the\nway we distinguish multiple allocations is through the number within the <>.\nNow, to use each individual allocation, we can do something like:\n```cpp\nauto& cool_scalar1 = get<::Tags::TempScalar<0>>(temp_buffer);\nauto& cool_scalar2 = get<::Tags::TempScalar<1>>(temp_buffer);\nauto& cool_tensor1 = get<::Tags::Tempij<0, 3, Frame::Inertial>>(temp_buffer);\nauto& cool_tensor2 = get<::Tags::Tempij<1, 3, Frame::Inertial>>(temp_buffer);\n```\n\n# Tips\nIn the interest of reducing memory allocations, there a certain scenarios where\nyou can resuse old allocations that are no longer useful to your computation.\n\nTo see this, let's say that you're trying to make two unit vectors. You might\nstart by saying you'll need two different vectors (rank 1 upper tensors) and 2\ndifferent scalars as the magnitude of each vector.\nThe way we'd allocate for this is by doing:\n```cpp\nVariables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n::Tags::TempI<0, 3, Frame::Inertial>, ::Tags::TempI<1, 3, Frame::Inertial>>>\ntemp_buffer{get<0,0>(spatial_metric).size()};\n```\nHowever, doing this allocates more memory than we actually need. Once we finish\ncalculating the first unit vector, the memory we've allocated for the scalar\nmagnitude of the first vector will just sit there unused. We can reuse the\nallocation for the TempScalar<0> and use it when calculating the second unit\nvector without having to allocate another TempScalar. Now, allocating an extra\nscalar is not very expensive, but when using tensors, the memory required really\nadds up, so this is just another way to help make SpECTRE a bit more efficient.\n"
  },
  {
    "path": "docs/DevGuide/WritingDox.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Writing Good Documentation {#writing_good_dox}\n\n\\tableofcontents\n\n# Tutorials, Instructions, and Dev Guide {#writing_dox_writing_help}\n\nAll non-code documentation such as tutorials, installation instructions, and\nthe developer guide is written inside Markdown files such as this one. These\nfiles must be placed inside `docs/Tutorials`, `docs/MainSite`, or\n`docs/DevGuide` according to what they document. Each Markdown file must start\nwith the following line\n\n``` markdown\n# The title {#the_tag}\n```\n\nwhere `The title` is replaced with your desired title, and `the_tag` with the\ntag you want to use to reference the Markdown file and documentation. Each\nmain heading of the file starts with a single octothorpe\nand can have a tag. For example,\n\n``` markdown\n# My Section {#file_name_my_section}\n```\n\nWhile the `file_name` portion is not necessary, it is useful for reducing the\nlikelihood of reference collisions. You can add a table of contents using the\nDoxygen command <code>\\\\tableofcontents</code>. All sections, subsections,\nsubsubsections, etc. are shown in the table of contents if they have a tag,\nif not they do not appear.\n\n# C++ Documentation {#writing_dox_cpp_dox_help}\n\nWe require you to add Doxygen documentation for any of the following\nyou may have added to the public interface in any hpp file:\n\n+ classes\n+ structs\n+ enums\n+ functions\n+ namespaces\n+ macros\n+ type aliases\n\nDocumentation begins on the line immediately above the declaration with\neither a triple slash `///` or a `/*!`.\n\nExamples:\n\\verbatim\n/// A brief description of the object to be documented\n///\n/// Doxygen comments can be made\n/// using triple slashes...\nclass ExampleClass{\n  ... rest of code\n}\n\\endverbatim\n\n\\verbatim\n/*!\n * \\brief A brief description of the object to be documented\n *\n * Doxygen comments can also be made\n * using the \"slash-star, star-slash\" pattern, in this way.\n */\nclass ExampleClass{\n  ... rest of code\n}\n\\endverbatim\n\nBuild your documentation by running `make doc` in your build directory.\nYou can then view it by opening `BUILD_DIR/docs/html/index.html` in a\nbrowser and using the file navigator in Doxygen to locate your file.\n\nIn addition to providing a file directory of all the source files in SpECTRE,\nDoxygen also conveniently provides two additional organizations of the files,\nModules and Namespaces. To ensure that your documentation is easily found from\nwithin Doxygen, we recommend that you add any new objects to Modules and any\nnew namespaces to Namespaces.\n\n\\note The `///` Doxygen syntax does not require a `\\brief`, while the C-style\n`/*!` does.\n\n## Add your object to an existing Module:\n\nWithin a Doxygen comment, use the Doxygen keyword\n\\verbatim\n\\ingroup\n\\endverbatim\n\nfollowed by the name of the Module (you can find the list of existing Modules in\n`docs/GroupDefs.hpp`).\n\n## Add a new Module:\n\nWithin `docs/GroupDefs.hpp`, add the name of your Module (which follows the\nnaming convention \"YourNameForModule\" followed by \"Group\") among the rest,\ntaking care to keep the list of Modules in alphabetical order.\n\n## Add a new namespace:\n\nIn the hpp file in which the namespace is declared for the first time,\nadd a Doxygen comment to the line directly above the namespace. Subsequent\nfiles which use this namespace will not need a Doxygen comment.\n\nWe also strongly encourage you to:\n\n## Put any mathematical expressions in your documentation into LaTeX form:\n\nWithin a Doxygen comment, begin and conclude your expression with\n\n\\verbatim\n\\f$\n\\endverbatim\n\nExample:\n\\verbatim\n\\\\\\ We define \\f$ \\xi : = \\eta^2 \\f$\n\\endverbatim\n\nNote that if this expression spans more than one line,\nyour Doxygen comment must of the \"slash-star, star-slash\" form shown in the\nprevious section. One can also use (within a Doxygen comment) the form\n\n\\verbatim\n\\f[ expression \\f]\n\\endverbatim\n\nto put the expression on its own line. We also encourage you to use the latex\nenv `align` for formatting these multiple-line equations. Please prefer the\n`align` environment over the `eqnarray` environment. See the\n[texfaq](https://texfaq.org/FAQ-eqnarray) for an explanation as to why. When\nusing out-of-line equations it is important to have a blank Doxygen line above\nand below the equation so that ClangFormat does not merge the equation with\nother lines. For example,\n\n\\verbatim\n * word word word\n *\n * \\f{align}{\n *   a &= b \\\\\n *   c &= d\n * \\f}\n *\n * word word word\n\\endverbatim\n\nprevents ClangFormat from changing the code to\n\n\\verbatim\nword word word \\f{align}{ a &= b \\\\ c &= d \\f}\n\\endverbatim\n\nwhich may not render properly and makes the source code harder to read.\n\n## Cite publications in your documentation {#writing_dox_citations}\n\nWhen you refer to publications or books in the documentation, add a\ncorresponding entry to `docs/References.bib`. Follow these guidelines when\nediting `docs/References.bib`:\n\n- Ideally, find the publication on [INSPIRE HEP](https://inspirehep.net) and\ncopy its BibTeX entry.\n- For publications that are not listed on [INSPIRE HEP](https://inspirehep.net),\nmake sure to format the new entry's key in the same style, i.e.\n`(<Author>[a-zA-Z]+):(<Year>[0-9]{4})(<ID>[a-z]*)`. Good keys are, for\ninstance, `Einstein:1915` or `LVC:2016a`. For books you may omit the year.\n- Sort the list of BibTeX entries in the file alphabetically by their keys.\n- Provide open access or preprint information whenever possible. For\npublications available on [arXiv](https://arxiv.org), for instance, add the\nfollowing fields (filling in the correct values):\n```\narchivePrefix = {arXiv},\neprint = {1609.00098},\nprimaryClass = {astro-ph.HE},\n```\n- Provide the `url` field whenever possible. If a DOI has been issued by the\npublisher, use `https://doi.org/<doi>` and also fill the `doi` field of the\nentry. Else, use the URL provided by the publisher.\n- Make sure to wrap strings in the BibTeX entries in `{}` when capitalization is\nimportant, or when BibTeX keywords should be ignored (e.g. `and` in author\nlists).\n- Spell out journal names directly instead of using LaTeX macros such as\n  `\\apj` or `\\mnras`, since Doxygen does not expand those macros in the\n  generated bibliography.\n\nTo cite an entry from the `docs/References.bib` file in the documentation, use\nthe Doxygen keyword\n\\verbatim\n\\cite\n\\endverbatim\nfollowed by the BibTeX key at the place in the documentation where you want the\ncitation to appear. It will render as a numbered link to the bibliography\npage. It will also show a popover when hovering over the link, which displays\nthe bibliographic information and provides quick access to the publication.\n\n## Include any pictures that aid in the understanding of your documentation:\n\nFirst, please compress your image to be under 130kB. As most images will be\ndiagrammatical in nature this is certainly achievable!\n\nSecond, add the directory that will contain your image to the `docs/Images/`\ndirectory. Create the appropriate directories such that the directory structure\nin `docs/Images` matches that of `src` and `tests/Unit`.\n\nFinally, include the image by using the Doxygen keyword\n\n\\verbatim\n\\image html NameOfImage.png\n\\endverbatim\n\nin your hpp file.\n\n# Python Documentation {#writing_dox_python_dox_help}\n\nDocStrings...\n"
  },
  {
    "path": "docs/DevGuide/WritingTests.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Writing Unit Tests {#writing_unit_tests}\n\n\\tableofcontents\n\nUnit tests are placed in the appropriate subdirectory of `tests/Unit`, which\nmirrors the directory hierarchy of `src`. Typically there should be one test\nexecutable for each production code library. For example,\nwe have a `DataStructures` library and a `Test_DataStructures` executable. When\nadding a new test there are several scenarios that can occur, which are outlined\nbelow.\n\n- You are adding a new source file to an existing test library:<br>\n  If you are adding a new source file in a directory that already has a\n  `CMakeLists.txt` simply create the source file, which should be named\n  `Test_ProductionCodeFileBeingTest.cpp` and add that to the `LIBRARY_SOURCES`\n  in the `CMakeLists.txt` file in the same directory you are adding the `cpp`\n  file.<br>\n  If you are adding a new source file to a library but want to place it in a\n  subdirectory you must first create the subdirectory. To provide a concrete\n  example, say you are adding the directory `TensorEagerMath` to\n  `tests/Unit/DataStructures`. After creating the directory you must add a call\n  to `add_subdirectory(TensorEagerMath)` to\n  `tests/Unit/DataStructures/CMakeLists.txt` *before* the call to\n  `add_test_library` and *after* the `LIBRARY_SOURCES` are set. Next add the\n  file `tests/Unit/DataStructures/TensorEagerMath/CMakeLists.txt`, which should\n  add the new source files by calling `set`, e.g.\n  ```\n  set(LIBRARY_SOURCES\n      ${LIBRARY_SOURCES}\n      Test_ProductionCodeFileBeingTest.cpp\n      PARENT_SCOPE)\n  ```\n  The `PARENT_SCOPE` flag tells CMake to make the changes visible in the\n  CMakeLists.txt file that called `add_subdirectory`. You can now add the\n  `Test_ProductionCodeFileBeingTested.cpp` source file.\n- You are adding a new directory:<br>\n  If the directory is a new lowest level directory you must add a\n  `add_subdirectory` call to `tests/Unit/CMakeLists.txt`. If it is a new\n  subdirectory you must add a `add_subdirectory` call to the\n  `CMakeLists.txt` file in the directory where you are adding the\n  subdirectory. Next you should read the part on adding a new test library.\n- You are adding a new test library:<br>\n  After creating the subdirectory for the new test library you must add a\n  `CMakeLists.txt` file. See `tests/Unit/DataStructures/CMakeLists.txt` for\n  an example of one. The `LIBRARY` and `LIBRARY_SOURCES` variables set the name\n  of the test library and the source files to be compiled into it. The library\n  name should be of the format `Test_ProductionLibraryName`, for example\n  `Test_DataStructures`. The library sources should be only the source files in\n  the current directory. The `add_subdirectory` command can be used to add\n  source files in subdirectories to the same library as is done in\n  `tests/Unit/CMakeLists.txt`. The `CMakeLists.txt` in\n  `tests/Unit/DataStructures/TensorEagerMath` is an example of how to add source\n  files to a library from a subdirectory of the library. Note that the setting\n  of `LIBRARY_SOURCES` here first includes the current `LIBRARY_SOURCES` and at\n  the end specifies `PARENT_SCOPE`. The `PARENT_SCOPE` flag tells CMake to\n  modify the variable in a scope that is visible to the parent directory,\n  i.e. the `CMakeLists.txt` that called `add_subdirectory`.<br>\n  Finally, in the `CMakeLists.txt` of your new library you must call\n  `add_test_library`. Again, see `tests/Unit/DataStructures/CMakeLists.txt` for\n  an example. The `add_test_library` function adds a test executable with the\n  name of the first argument and the source files of the third argument.\n  Remember to use `target_link_libraries` to link any libraries your test\n  executable uses (see \\ref spectre_build_system).\n\nAll tests must start with\n```cpp\n// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n```\nThe file `tests/Unit/Framework/TestingFramework.hpp` must always be the first\ninclude in the test file and must be separated from the STL includes by a blank\nline. All classes and free functions should be in an anonymous/unnamed\nnamespace, e.g.\n```cpp\nnamespace {\nclass MyFreeClass {\n  /* ... */\n};\n\nvoid my_free_function() {\n  /* ... */\n}\n}  // namespace\n```\nThis is necessary to avoid symbol redefinition errors during linking.\n\nTest cases are added by using the `SPECTRE_TEST_CASE` macro. The first argument\nto the macro is the test name, e.g. `\"Unit.DataStructures.Tensor\"`, and the\nsecond argument is a list of tags. The tags list is a string where each element\nis in square brackets. For example, `\"[Unit][DataStructures]\"`. The tags should\nonly be the type of test, in this case `Unit`, and the library being tested, in\nthis case `DataStructures`. The `SPECTRE_TEST_CASE` macro should be treated as a\nfunction, which means that it should be followed by `{ /* test code */ }`. For\nexample,\n\\snippet Test_Tensor.cpp example_spectre_test_case\nFrom within a `SPECTRE_TEST_CASE` you are able to do all the things you would\nnormally do in a C++ function, including calling other functions, setting\nvariables, using lambdas, etc.\n\nThe `CHECK` macro in the above example is provided by\n[Catch2](https://github.com/catchorg/Catch2) and is used to check conditions. We\nalso provide the `CHECK_ITERABLE_APPROX` macro which checks if two `double`s or\ntwo iterable containers of `double`s are approximately\nequal. `CHECK_ITERABLE_APPROX` is especially useful for comparing `Tensor`s,\n`DataVector`s, and `Tensor<DataVector>`s since it will iterate over nested\ncontainers as well.\n\n\\warning Catch's `CHECK` statement only prints numbers out to approximately 10\ndigits at most, so you should generally prefer `CHECK_ITERABLE_APPROX` for\nchecking double precision numbers, unless you want to check that two numbers are\nbitwise identical.\n\nAll unit tests must finish within a few seconds, the hard limit is 5, but having\nunit tests that long is strongly discouraged. They should typically complete in\nless than half a second. Tests that are longer are often no longer testing a\nsmall enough unit of code and should either be split into several unit tests or\nmoved to an integration test.\n\n#### Discovering New and Renamed Tests\n\nWhen you add a new test to a source file or rename an existing test the change\nneeds to be discovered by the testing infrastructure. This is done by building\nthe target `rebuild_cache`, e.g. by running `make rebuild_cache`.\n\n#### Testing Pointwise Functions\n\nPointwise functions should generally be tested in two different ways. The first\nis by taking input from an analytic solution and checking that the computed\nresult is correct. The second is to use the random number generation comparison\nwith Python infrastructure. In this approach the C++ function being tested is\nre-implemented in Python and the results are compared. Please follow these\nguidelines:\n\n- The Python implementation should be in a file with the same name as the source\n  file that is being re-implemented and placed in the same directory as its\n  corresponding `Test_*.cpp` source file.\n- The functions should have the same names as the C++ functions they\n  re-implement.\n- If a function does sums over tensor indices then\n  [`numpy.einsum`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.einsum.html)\n  should be used in Python to provide an alternative implementation of the loop\n  structure.\n- You can import Python functions from other re-implementations in the\n  `tests/Unit/` directory to reduce code duplication. Note that the path you\n  pass to `pypp::SetupLocalPythonEnvironment` determines the directory from\n  which you can import Python modules. Either import modules directly from the\n  `tests/Unit/` directory (e.g. `import\n  PointwiseFunction.GeneralRelativity.Christoffel as christoffel`) or use\n  relative imports like `from . import Christoffel as christoffel`. Don't assume\n  the Python environment is set up in a subdirectory of `tests/Unit/`.\n\nIt is possible to test C++ functions that return by value and ones that return\nby `gsl::not_null`. In the latter case, since it is possible to return multiple\nvalues, one Python function taking all non-`gsl::not_null` arguments must be\nsupplied for each `gsl::not_null` argument to the C++. To perform the test the\n`pypp::check_with_random_values()` function must be called. For example, the\nfollowing checks various C++ functions by calling into `pypp`:\n\n\\snippet Test_PyppRandomValues.cpp cxx_two_not_null\n\nThe corresponding Python functions are:\n\n\\snippet PyppPyTests.py python_two_not_null\n\n#### Writing and Fixing Random-Value Based Tests\n\nMany tests in SpECTRE make use of randomly generated numbers in order to\nincrease the parameter space covered by the tests. The random number generator\nis set up using:\n```cpp\nMAKE_GENERATOR(gen);\n```\nThe generator `gen` can then be passed to distribution classes such as\n`std::uniform_real_distribution` or `UniformCustomDistribution`.\n\nEach time the test is run, a different random seed will be used.  When writing a\ntest that uses random values, it is good practice to run the test at least\n\\f$10^4\\f$ times in order to set any tolerances on checks used in the test.\nThis can be done by using the following command in the build directory\n(SPECTRE_BUILD_DIR):\n```\nctest --repeat-until-fail 10000 -R TEST_NAME\n```\nwhere `TEST_NAME` is the test name passed to `SPECTRE_TEST_CASE`\n(e.g. `Unit.Evolution.Systems.CurvedScalarWave.Characteristics`).\n\nIf a test case fails when using a random number generated by `MAKE_GENERATOR`,\nas part of the output from the failed test will be the text\n```\nSeed is:  SEED from FILE_NAME:LINE_NUMBER\n```\nNote that the output of tests can be found in\n`SPECTRE_BUILD_DIR/Testing/Temporary/LastTest.log`\n\nThe failing test case can then be reproduced by changing `MAKE_GENERATOR` call\nat the provided line in the given file to\n```cpp\nMAKE_GENERATOR(gen, SEED);\n```\nIf the `MAKE_GENERATOR` is within `CheckWithRandomValues.hpp`, the failing test\ncase most likely has occurred within a call to\n`pypp::check_with_random_values()`.  In such a case, additional information\nshould have been printed to help you determine which call to\n`pypp::check_with_random_values()` has failed.  The critical information is\nthe line\n```\nfunction:  FUNCTION_NAME\n```\nwhere `FUNCTION_NAME` should correspond to the third argument of a call to\n`pypp::check_with_random_values()`.  The seed that caused the test to fail can\nthen be passed as an additional argument to `pypp::check_with_random_values()`,\nwhere you may also need to pass in the default value of the comparison\ntolerance.\n\nTypically, you will need to adjust a tolerance used in a `CHECK` somewhere in\nthe test in order to get the test to succeed reliably.  The function\n`pypp::check_with_random_values()` takes an argument that specifies the lower\nand upper bounds of random quantities.  Typically these should be chosen to be\nof order unity in order to decrease the chance of occasionally generating large\nnumbers through multiplications which can cause an error above a reasonable\ntolerance.\n\n#### Testing Failure Cases {#testing_failure_cases}\n\n`ASSERT`s and `ERROR`s can be tested with the `CHECK_THROWS_WITH`\nmacro.  This macro takes two arguments: the first is either an\nexpression or a lambda that is expected to trigger an exception (which\nnow are thrown by `ASSERT` and `ERROR` (Note: You may need to add `()`\nwrapping the lambda in order for it to compile.); the second is a\nCatch Matcher (see [Catch2](https://github.com/catchorg/Catch2) for\ncomplete documentation), usually a\n`Catch::Matchers::ContainsSubstring()` macro that matches a substring\nof the error message of the thrown exception.\n\nWhen testing `ASSERT`s the `CHECK_THROWS_WITH`\nshould be enclosed between `#%ifdef SPECTRE_DEBUG` and an `#%endif`\nIf the `#%ifdef SPECTRE_DEBUG` block is omitted then compilers will\ncorrectly flag the code as being unreachable which results in\nwarnings.\n\nAdding the \"attribute\" `// [[OutputRegex, Regular expression to match]]`\nbefore the `SPECTRE_TEST_CASE` macro will force ctest to only pass the\nparticular test if the regular expression is found in the output of the\ntest.  In this case, the first line of the test should call the macro\n`OUTPUT_TEST();`.\n\n### Testing Actions\n\nThe action testing framework is documented as part of the `ActionTesting`\nnamespace.\n\n## Input file tests\n\nWe have a suite of input file tests in addition to unit tests. Every input file\nin the `tests/InputFiles/` directory is added to the test suite automatically.\nIf you don't want your input file tested at all, add the relative input file\npath to the whitelist in `cmake/AddInputFileTests.cmake`. If the input file is\nbeing tested, it must specify the `Executable` it should run with in the input\nfile metadata (above the `---` marker in the input file). Properties of the test\nare controlled by the `Testing` section in the input file metadata. The\nfollowing properties are available:\n\n- `Check`: Semicolon-separated list of checks, e.g. `parse;execute`. The\n  following checks are available:\n    - `parse`: Just check that the input file passes option parsing.\n    - `execute`: Run the executable. If the input file metadata has an\n      `ExpectedOutput` field, check that these files have been written. See\n      `spectre.tools.CleanOutput` for details.\n    - `execute_check_output`: In additional to `execute`, check the contents of\n      some output files. The checks are defined by the `OutputFileChecks` in the\n      input file metadata. See `spectre.tools.CheckOutputFiles` for details.\n- `CommandLineArgs` (optional): Additional command-line arguments passed to the\n  executable.\n- `ExpectedExitCode` (optional): The expected exit code of the executable.\n  Default: `0`. See `Parallel::ExitCode` for possible exit codes.\n- `Timeout` (optional): Timeout for the test. Default: 2 seconds.\n- `Priority` (optional): Priority of running this test on CI. Possible values\n  are: `low` (not usually run on CI), `normal` (run at least once on CI), `high`\n  (run always on CI). Default: `normal`.\n"
  },
  {
    "path": "docs/Doxyfile.in",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Last updated for Doxygen 1.9.1\n# See https://www.doxygen.nl/manual/config.html\n# for documentation on the commands in this file.\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\nPROJECT_NAME           = SpECTRE\n\nPROJECT_NUMBER         = @SPECTRE_VERSION@\n\nOUTPUT_DIRECTORY       = @PROJECT_BINARY_DIR@/docs\n\nSTRIP_FROM_PATH        = @PROJECT_SOURCE_DIR@\n\nJAVADOC_AUTOBRIEF      = YES\n\nMULTILINE_CPP_IS_BRIEF = YES\n\nTAB_SIZE               = 2\n\nALIASES                = \"spectreversion=@SPECTRE_VERSION@\" \\\n                         \"spectredoi=@SPECTRE_DOI@\" \\\n                         \"spectrezenodoid=@SPECTRE_ZENODO_ID@\" \\\n                         \"semantics=<p></p><i>Semantics:</i>\" \\\n                         \"metareturns=<p></p><i>Returns:</i> \" \\\n                         \"return=<p></p><i>Returns:</i> \" \\\n                         \"requires=<p></p><i>Requires:</i> \" \\\n                         \"effects=<p></p><i>Effects:</i> \" \\\n                         \"synchronization=<p></p><i>Synchronization:</i> \" \\\n                         \"postconditions=<p></p><i>Postconditions:</i> \" \\\n                         \"returns=<p></p><i>Returns:</i> \" \\\n                         \"throws=<p></p><i>Throws:</i> \" \\\n                         \"complexity=<p></p><i>Complexity:</i> \" \\\n                         \"remarks=<p></p><i>Remarks:</i> \" \\\n                         \"errorconditions=<p></p><i>Error Conditions:</i> \" \\\n                         \"notes=<p></p><i>Notes:</i> \" \\\n                         \"details=<p></p><h3>Details</h3>\" \\\n                         \"usage=<p></p><h3>Usage</h3>\" \\\n                         \"example=<p></p><h3>Example</h3>\" \\\n                         \"derivedrequires=<p></p><i>Derived Class Requires:</i>\"\n\nDISTRIBUTE_GROUP_DOC   = YES\n\nSUBGROUPING            = YES\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\nEXTRACT_ALL            = NO\n\nEXTRACT_PRIVATE        = NO\n\nEXTRACT_STATIC         = NO\n\nEXTRACT_LOCAL_CLASSES  = NO\n\n# We want to be able to have undocumented members, such as size() that show\n# up but what they do is rather obvious.\nWARN_IF_UNDOCUMENTED   = NO\n\nCASE_SENSE_NAMES       = YES\n\nGENERATE_TODOLIST      = NO\n\nGENERATE_TESTLIST      = NO\n\nGENERATE_BUGLIST       = NO\n\nSTRIP_CODE_COMMENTS    = NO\n\nSHOW_FILES             = NO\n\nSHOW_USED_FILES        = YES\n\nVERBATIM_HEADERS       = NO\n\nCITE_BIB_FILES         = @PROJECT_SOURCE_DIR@/docs/References.bib \\\n                         @PROJECT_SOURCE_DIR@/docs/Dependencies.bib\n\n#---------------------------------------------------------------------------\n# configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# Add the documentation pages in the order they should appear in the tree view.\n# Use `\\subpage` within the pages to establish a hierarchy.\n# - We also pick up notebooks converted to markdown by the\n#   `doc-notebooks-to-markdown` target (see `cmake/SetupDoxygen.cmake` for\n#   details).\n# - The changelog is generated by CI (see `.github/workflows/Tests.yaml`)\nINPUT = @PROJECT_SOURCE_DIR@/docs/Installation \\\n        @PROJECT_SOURCE_DIR@/docs/Tutorials \\\n        @PROJECT_SOURCE_DIR@/docs/Examples \\\n        @PROJECT_SOURCE_DIR@/docs/Contributing \\\n        @PROJECT_SOURCE_DIR@/docs/DevGuide \\\n        @PROJECT_SOURCE_DIR@/docs/Gallery.md \\\n        @PROJECT_SOURCE_DIR@/docs/PublicationPolicy.md \\\n        @PROJECT_SOURCE_DIR@/docs/Changelog.md \\\n        @PROJECT_SOURCE_DIR@/docs \\\n        @CMAKE_BINARY_DIR@/docs/tmp/notebooks_md \\\n        @SPECTRE_DOXYGEN_GROUPS@ \\\n        @PROJECT_SOURCE_DIR@/src \\\n        @PROJECT_SOURCE_DIR@/tests/Unit/Framework \\\n        @PROJECT_SOURCE_DIR@/tests/Unit/Helpers\n\nUSE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/docs/Main.md\n\n# We don't parse cpp files because Doxygen has trouble with some template\n# expressions and we place documentation in header files anyway.\nFILE_PATTERNS          = *.hpp \\\n                         *.md\n\nRECURSIVE              = YES\n\nEXCLUDE                =\n\nEXCLUDE_SYMLINKS       = NO\n\nEXCLUDE_PATTERNS       =\n\nEXCLUDE_SYMBOLS        = charm_init_*                                 \\\n                         *detail*                                     \\\n                         PUP*                                         \\\n                         SPECTRE_TEST_CASE*                           \\\n                         std*                                         \\\n                         YAML*\n\nEXAMPLE_PATH           = @PROJECT_SOURCE_DIR@/tests/ \\\n                         @PROJECT_SOURCE_DIR@/src/Executables/Examples/ \\\n                         @PROJECT_SOURCE_DIR@/support/DevEnvironments/spack.yaml \\\n                         @PROJECT_SOURCE_DIR@/support/Python/dev_requirements.txt \\\n                         @PROJECT_SOURCE_DIR@/support/Python/requirements.txt \\\n                         @PROJECT_SOURCE_DIR@/citation.bib\n\nEXAMPLE_PATTERNS       =\n\nEXAMPLE_RECURSIVE      = NO\n\n# Input filters run over content before it is parsed by Doxygen. They are\n# invoked with a file name as additional argument. We run the following filters:\n# - Format markdown math for Doxygen. See the documentation in\n#   `markdown-math-filter.pl` for details.\nINPUT_FILTER           = \"@PERL_EXECUTABLE@ -0777 -p @PROJECT_SOURCE_DIR@/docs/config/markdown-math-filter.pl\"\n\n#---------------------------------------------------------------------------\n# configuration options related to the output\n#---------------------------------------------------------------------------\n\nGENERATE_HTML          = @SPECTRE_DOX_GENERATE_HTML@\n\nHTML_HEADER            = @PROJECT_SOURCE_DIR@/docs/config/header.html\n\nHTML_FOOTER            = @PROJECT_SOURCE_DIR@/docs/config/footer.html\n\nIMAGE_PATH             = @PROJECT_SOURCE_DIR@/docs/ \\\n                         @CMAKE_BINARY_DIR@/docs/tmp/notebooks_md\n\nGENERATE_LATEX         = NO\n\nGENERATE_RTF           = NO\n\nGENERATE_XML           = @SPECTRE_DOX_GENERATE_XML@\n\nXML_OUTPUT             = xml\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\nENABLE_PREPROCESSING   = YES\n\nMACRO_EXPANSION        = YES\n\n# Macros we actually want to expand during preprocessing\nEXPAND_AS_DEFINED      = MAKE_BINARY_FUNCTIONAL \\\n                         MAKE_BINARY_INPLACE_OPERATOR \\\n                         MAKE_BINARY_OPERATOR \\\n                         MAKE_UNARY_FUNCTIONAL\n\nEXPAND_ONLY_PREDEF     = YES\n\nSEARCH_INCLUDES        = YES\n\nPREDEFINED             = SPECTRE_ALWAYS_INLINE= \\\n                         OVERLOADER_CONSTEXPR= \\\n                         SPECTRE_DOXYGEN_INVOKED \\\n                         SPECTRE_AUTODIFF\n\n#---------------------------------------------------------------------------\n# Configuration::additions related to external references\n#---------------------------------------------------------------------------\n\n# The tag file for cppref can be updated as new features are added to the\n# standard library and to C++ by cloning\n# https://github.com/p12tic/cppreference-doc (if that is no longer\n# available there is a fork available at\n# https://github.com/nilsdeppe/cppreference-doc), entering the directory\n# and running `make doc_doxygen`. The up-to-date tag file is place in the\n# `output` subdirectory and can then be copied into the SpECTRE repo\nTAGFILES += \"@PROJECT_SOURCE_DIR@/docs/config/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/\"\n\nALLEXTERNALS           = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\nHAVE_DOT               = NO\n\nCLASS_GRAPH            = NO\n\nCOLLABORATION_GRAPH    = NO\n\nGROUP_GRAPHS           = NO\n\nUML_LOOK               = NO\n\nTEMPLATE_RELATIONS     = NO\n\nINCLUDE_GRAPH          = NO\n\nINCLUDED_BY_GRAPH      = NO\n\nCALL_GRAPH             = NO\n\nCALLER_GRAPH           = NO\n\nGRAPHICAL_HIERARCHY    = NO\n\nDIRECTORY_GRAPH        = NO\n\nDOT_IMAGE_FORMAT       = svg\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\nGENERATE_LEGEND        = NO\n\n#---------------------------------------------------------------------------\n# HTML layout file\n#---------------------------------------------------------------------------\n\nLAYOUT_FILE = @DOXYGEN_LAYOUT_FILE@\n\nHTML_EXTRA_FILES       = @PROJECT_SOURCE_DIR@/docs/config/icons/octicons.eot \\\n                         @PROJECT_SOURCE_DIR@/docs/config/icons/octicons.svg \\\n                         @PROJECT_SOURCE_DIR@/docs/config/icons/octicons.ttf \\\n                         @PROJECT_SOURCE_DIR@/docs/config/icons/octicons.woff \\\n                         @PROJECT_SOURCE_DIR@/docs/config/octicons.css \\\n                         @PROJECT_SOURCE_DIR@/docs/config/doxygen-awesome-fragment-copy-button.js \\\n                         @PROJECT_SOURCE_DIR@/docs/config/doxygen-awesome-interactive-toc.js \\\n                         @PROJECT_SOURCE_DIR@/docs/config/doxygen-awesome-paragraph-link.js \\\n                         @PROJECT_SOURCE_DIR@/docs/config/js/spectre.js \\\n                         @PROJECT_SOURCE_DIR@/docs/config/apple-touch-icon.png \\\n                         @PROJECT_SOURCE_DIR@/docs/config/favicon-16x16.png \\\n                         @PROJECT_SOURCE_DIR@/docs/config/favicon-32x32.png \\\n                         @PROJECT_SOURCE_DIR@/docs/config/favicon.ico \\\n                         @PROJECT_SOURCE_DIR@/docs/config/safari-pinned-tab.svg \\\n                         @PROJECT_SOURCE_DIR@/docs/.nojekyll \\\n                         @PROJECT_SOURCE_DIR@/docs/config/MathJax.js \\\n                         @PROJECT_SOURCE_DIR@/docs/Images/banner.png \\\n                         @PROJECT_SOURCE_DIR@/LICENSE.txt\n\nGENERATE_TREEVIEW      = YES\n\n# This width must correspond to `side-nav-fixed-width` in `custom.css`\nTREEVIEW_WIDTH         = 280\n\nHTML_EXTRA_STYLESHEET  = @PROJECT_SOURCE_DIR@/docs/config/doxygen-awesome.css \\\n                         @PROJECT_SOURCE_DIR@/docs/config/doxygen-awesome-sidebar-only.css \\\n                         @PROJECT_SOURCE_DIR@/docs/config/custom.css\n\nHTML_COLORSTYLE_HUE    = 209\nHTML_COLORSTYLE_SAT    = 255\nHTML_COLORSTYLE_GAMMA  = 113\n\nUSE_MATHJAX = YES\n\n# MATHJAX_RELPATH ultimately links to docs/config/MathJax.js, which is\n# copied to docs/html/MathJax.js with the HTML_EXTRA_FILES\nMATHJAX_RELPATH = ./\n\n# Since Doxygen doesn't support MathJax 3 yet, we configure all the\n# extensions/packages through the docs/config/MathJax.js file.\nMATHJAX_EXTENSIONS =\n\nMATHJAX_CODEFILE =\n\n#---------------------------------------------------------------------------\n# Configuration::additions related to the search engine\n#---------------------------------------------------------------------------\n\nSEARCHENGINE           = YES\n"
  },
  {
    "path": "docs/Examples/BbhInitialData/.gitignore",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n*.yaml\n*.h5\n"
  },
  {
    "path": "docs/Examples/BbhInitialData/BbhInitialData.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"# Binary black hole initial data {#example_bbh_id}\\n\",\n    \"\\n\",\n    \"In this example we run the elliptic solver to compute initial data for binary\\n\",\n    \"black holes.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"# Distributed under the MIT License.\\n\",\n    \"# See LICENSE.txt for details.\\n\",\n    \"\\n\",\n    \"# Dependencies:\\n\",\n    \"%pip install numpy matplotlib pandas 'h5py>=3.0.0' ruamel.yaml\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 2,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"import h5py\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"import multiprocessing\\n\",\n    \"import numpy as np\\n\",\n    \"import os\\n\",\n    \"import pandas as pd\\n\",\n    \"from ruamel.yaml import YAML\\n\",\n    \"\\n\",\n    \"plt.style.use(\\\"../plots.mplstyle\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 3,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"%%bash\\n\",\n    \"# Clean up output files from previous runs\\n\",\n    \"rm -f Bbh*.h5\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"First, make sure you have compiled the `SolveXcts` executable. Put in the path\\n\",\n    \"to your build directory below. Make sure you have compiled in `Release` mode.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"SPECTRE_BUILD_DIR = \\\"/Users/nlf/Work/spectre/build-Default-Release\\\"\\n\",\n    \"SPECTRE_HOME = \\\"/Users/nlf/Projects/spectre/develop\\\"\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"\\n\",\n    \"## Setup input file\\n\",\n    \"\\n\",\n    \"We set up an input file based on the\\n\",\n    \"`tests/InputFiles/Xcts/BinaryBlackHole.yaml` example:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 5,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"# Load example input file\\n\",\n    \"load_input_file_path = os.path.join(\\n\",\n    \"    SPECTRE_HOME, \\\"tests/InputFiles/Xcts/BinaryBlackHole.yaml\\\"\\n\",\n    \")\\n\",\n    \"yaml = YAML()\\n\",\n    \"with open(load_input_file_path, \\\"r\\\") as open_input_file:\\n\",\n    \"    input_file = yaml.load(open_input_file)\\n\",\n    \"\\n\",\n    \"# Modify example input file\\n\",\n    \"# - Set output file names\\n\",\n    \"input_file[\\\"Observers\\\"][\\\"VolumeFileName\\\"] = \\\"BbhVolume\\\"\\n\",\n    \"input_file[\\\"Observers\\\"][\\\"ReductionFileName\\\"] = \\\"BbhReductions\\\"\\n\",\n    \"\\n\",\n    \"# Write modified input file\\n\",\n    \"with open(\\\"Bbh.yaml\\\", \\\"w\\\") as open_input_file:\\n\",\n    \"    yaml.dump(input_file, open_input_file)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Run executable\\n\",\n    \"\\n\",\n    \"We pass the input file to the `SolveXcts` executable to solve the elliptic\\n\",\n    \"problem. It will take a few minutes to complete on ~10 cores. Adapt the command\\n\",\n    \"below to your system, or run the `SolveXcts` executable with the `Bbh.yaml`\\n\",\n    \"input file manually.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 6,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"Charm++: standalone mode (not using charmrun)\\n\",\n      \"Charm++> Running in Multicore mode: 12 threads (PEs)\\n\",\n      \"Converse/Charm++ Commit ID: v6.10.2-0-g7bf00fa\\n\",\n      \"Warning> Randomization of virtual memory (ASLR) is turned on in the kernel, thread migration may not work! Run 'echo 0 > /proc/sys/kernel/randomize_va_space' as root to disable it, or try running with '+isomalloc_sync'.\\n\",\n      \"CharmLB> Load balancer assumes all CPUs are same.\\n\",\n      \"Charm++> Running on 1 hosts (12 sockets x 1 cores x 1 PUs = 12-way SMP)\\n\",\n      \"Charm++> cpu topology info is gathered in 0.002 seconds.\\n\",\n      \"\\n\",\n      \"Executing '/Users/nlf/Work/spectre/build-Default-Release/bin/SolveXcts' using 12 processors.\\n\",\n      \"Charm++ startup time in seconds: 3.621687\\n\",\n      \"Date and time at startup: Tue Feb 22 15:08:18 2022\\n\",\n      \"\\n\",\n      \"SpECTRE Build Information:\\n\",\n      \"Version:                      2022.02.17\\n\",\n      \"Compiled on host:             98c21919c2ac\\n\",\n      \"Compiled in directory:        /Users/nlf/Work/spectre/build-Default-Release\\n\",\n      \"Source directory is:          /Users/nlf/Projects/spectre/develop\\n\",\n      \"Compiled on git branch:       xcts_input_files\\n\",\n      \"Compiled on git revision:     db7100f700b\\n\",\n      \"Linked on:                    Thu Jan 27 17:45:20 2022\\n\",\n      \"\\n\",\n      \"The following options differ from their suggested values:\\n\",\n      \"\\n\",\n      \"Option parsing completed.\\n\",\n      \"Multigrid level 0 has 232 elements in 54 blocks distributed on 12 procs.\\n\",\n      \"Multigrid level 1 has 54 elements in 54 blocks distributed on 12 procs.\\n\",\n      \"NewtonRaphson initialized with residual: 1.058476e+01\\n\",\n      \"Gmres initialized with residual: 1.058476e+01\\n\",\n      \"Gmres(1) iteration complete. Remaining residual: 2.003738e-01\\n\",\n      \"Gmres(2) iteration complete. Remaining residual: 1.206809e-01\\n\",\n      \"Gmres(3) iteration complete. Remaining residual: 8.694367e-02\\n\",\n      \"Gmres(4) iteration complete. Remaining residual: 7.275577e-02\\n\",\n      \"Gmres(5) iteration complete. Remaining residual: 4.520779e-02\\n\",\n      \"Gmres(6) iteration complete. Remaining residual: 1.464362e-02\\n\",\n      \"Gmres(7) iteration complete. Remaining residual: 6.754772e-03\\n\",\n      \"Gmres has converged in 7 iterations: RelativeResidual - The residual magnitude has decreased to a fraction of 0.001 of its initial value or below (0.00063816).\\n\",\n      \"NewtonRaphson(1) iteration complete (0 globalization steps, step length 1). Remaining residual: 2.865222e+00\\n\",\n      \"Gmres initialized with residual: 2.865222e+00\\n\",\n      \"Gmres(1) iteration complete. Remaining residual: 4.062729e-02\\n\",\n      \"Gmres(2) iteration complete. Remaining residual: 2.455538e-02\\n\",\n      \"Gmres(3) iteration complete. Remaining residual: 1.602018e-02\\n\",\n      \"Gmres(4) iteration complete. Remaining residual: 1.181106e-02\\n\",\n      \"Gmres(5) iteration complete. Remaining residual: 5.591212e-03\\n\",\n      \"Gmres(6) iteration complete. Remaining residual: 2.012421e-03\\n\",\n      \"Gmres has converged in 6 iterations: RelativeResidual - The residual magnitude has decreased to a fraction of 0.001 of its initial value or below (0.000702361).\\n\",\n      \"NewtonRaphson(2) iteration complete (0 globalization steps, step length 1). Remaining residual: 8.164886e-03\\n\",\n      \"Gmres initialized with residual: 8.164886e-03\\n\",\n      \"Gmres(1) iteration complete. Remaining residual: 1.279278e-03\\n\",\n      \"Gmres(2) iteration complete. Remaining residual: 7.680224e-04\\n\",\n      \"Gmres(3) iteration complete. Remaining residual: 6.026001e-04\\n\",\n      \"Gmres(4) iteration complete. Remaining residual: 5.116235e-04\\n\",\n      \"Gmres(5) iteration complete. Remaining residual: 3.771227e-04\\n\",\n      \"Gmres(6) iteration complete. Remaining residual: 1.521095e-04\\n\",\n      \"Gmres(7) iteration complete. Remaining residual: 4.029530e-05\\n\",\n      \"Gmres(8) iteration complete. Remaining residual: 1.632393e-05\\n\",\n      \"Gmres(9) iteration complete. Remaining residual: 5.028700e-06\\n\",\n      \"Gmres has converged in 9 iterations: RelativeResidual - The residual magnitude has decreased to a fraction of 0.001 of its initial value or below (0.000615893).\\n\",\n      \"NewtonRaphson(3) iteration complete (0 globalization steps, step length 1). Remaining residual: 1.690461e-03\\n\",\n      \"Gmres initialized with residual: 1.690461e-03\\n\",\n      \"Gmres(1) iteration complete. Remaining residual: 6.584416e-06\\n\",\n      \"Gmres(2) iteration complete. Remaining residual: 5.651935e-06\\n\",\n      \"Gmres(3) iteration complete. Remaining residual: 5.288583e-06\\n\",\n      \"Gmres(4) iteration complete. Remaining residual: 4.922907e-06\\n\",\n      \"Gmres(5) iteration complete. Remaining residual: 4.250901e-06\\n\",\n      \"Gmres(6) iteration complete. Remaining residual: 1.596286e-06\\n\",\n      \"Gmres has converged in 6 iterations: RelativeResidual - The residual magnitude has decreased to a fraction of 0.001 of its initial value or below (0.00094429).\\n\",\n      \"NewtonRaphson(4) iteration complete (0 globalization steps, step length 1). Remaining residual: 1.606242e-06\\n\",\n      \"Gmres initialized with residual: 1.606242e-06\\n\",\n      \"Gmres(1) iteration complete. Remaining residual: 7.008839e-07\\n\",\n      \"Gmres(2) iteration complete. Remaining residual: 5.285042e-07\\n\",\n      \"Gmres(3) iteration complete. Remaining residual: 4.919028e-07\\n\",\n      \"Gmres(4) iteration complete. Remaining residual: 3.871318e-07\\n\",\n      \"Gmres(5) iteration complete. Remaining residual: 1.603757e-07\\n\",\n      \"Gmres(6) iteration complete. Remaining residual: 2.922369e-08\\n\",\n      \"Gmres(7) iteration complete. Remaining residual: 1.438336e-08\\n\",\n      \"Gmres(8) iteration complete. Remaining residual: 5.271254e-09\\n\",\n      \"Gmres(9) iteration complete. Remaining residual: 2.125873e-09\\n\",\n      \"Gmres(10) iteration complete. Remaining residual: 1.044162e-09\\n\",\n      \"Gmres has converged in 10 iterations: RelativeResidual - The residual magnitude has decreased to a fraction of 0.001 of its initial value or below (0.000650065).\\n\",\n      \"NewtonRaphson(5) iteration complete (0 globalization steps, step length 1). Remaining residual: 1.066508e-09\\n\",\n      \"Gmres initialized with residual: 1.066508e-09\\n\",\n      \"Gmres(1) iteration complete. Remaining residual: 8.000055e-10\\n\",\n      \"Gmres(2) iteration complete. Remaining residual: 5.991477e-10\\n\",\n      \"Gmres(3) iteration complete. Remaining residual: 4.073811e-10\\n\",\n      \"Gmres(4) iteration complete. Remaining residual: 2.559880e-10\\n\",\n      \"Gmres(5) iteration complete. Remaining residual: 1.242268e-10\\n\",\n      \"Gmres(6) iteration complete. Remaining residual: 3.521815e-11\\n\",\n      \"Gmres has converged in 6 iterations: AbsoluteResidual - The residual magnitude has decreased to 1e-10 or below (3.52181e-11).\\n\",\n      \"NewtonRaphson(6) iteration complete (0 globalization steps, step length 1). Remaining residual: 3.589802e-11\\n\",\n      \"NewtonRaphson has converged in 6 iterations: AbsoluteResidual - The residual magnitude has decreased to 1e-10 or below (3.5898e-11).\\n\",\n      \"\\n\",\n      \"Done!\\n\",\n      \"Wall time in seconds: 769.172052\\n\",\n      \"Date and time at completion: Tue Feb 22 15:21:03 2022\\n\",\n      \"\\n\",\n      \"[Partition 0][Node 0] End of program\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"NUM_CORES = multiprocessing.cpu_count()\\n\",\n    \"SOLVE_XCTS = os.path.join(SPECTRE_BUILD_DIR, \\\"bin/SolveXcts\\\")\\n\",\n    \"!{SOLVE_XCTS} --input-file Bbh.yaml +p {NUM_CORES}\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Load initial data into evolutions\\n\",\n    \"\\n\",\n    \"The executable has created H5 output files with volume data and diagnostics:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 7,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"BbhReductions.h5  BbhVolume0.h5\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"!ls *.h5\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"The volume data in the `BbhVolume*.h5` files (one per node) can be imported into\\n\",\n    \"evolution executables. It contains the following data:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 8,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stdout\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"/                        Group\\n\",\n      \"/VolumeData.vol          Group\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626 Group\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ConformalFactor Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ExtrinsicCurvature_xx Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ExtrinsicCurvature_yx Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ExtrinsicCurvature_yy Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ExtrinsicCurvature_zx Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ExtrinsicCurvature_zy Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ExtrinsicCurvature_zz Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/HamiltonianConstraint Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/InertialCoordinates_x Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/InertialCoordinates_y Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/InertialCoordinates_z Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/Lapse Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/MomentumConstraint_x Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/MomentumConstraint_y Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/MomentumConstraint_z Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ShiftExcess_x Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ShiftExcess_y Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/ShiftExcess_z Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/Shift_x Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/Shift_y Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/Shift_z Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/SpatialMetric_xx Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/SpatialMetric_yx Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/SpatialMetric_yy Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/SpatialMetric_zx Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/SpatialMetric_zy Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/SpatialMetric_zz Dataset {61344}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/bases Dataset {696}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/connectivity Dataset {294400}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/grid_names Dataset {5272}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/quadratures Dataset {696}\\n\",\n      \"/VolumeData.vol/ObservationId1423324405707320626/total_extents Dataset {696}\\n\",\n      \"/src.tar.gz              Dataset {5307433}\\n\"\n     ]\n    }\n   ],\n   \"source\": [\n    \"!h5ls -r BbhVolume*.h5\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"- `Lapse` ($\\\\alpha$), `Shift` ($\\\\beta^i$), `SpatialMetric` ($\\\\gamma_{ij}$) and\\n\",\n    \"  `ExtrinsicCurvature` ($K_{ij}$): These quantities solve the Einstein\\n\",\n    \"  constraint equations in \\\"corotating\\\" coordinates, such that, e.g.,\\n\",\n    \"  $\\\\partial_t K = 0$. The black holes remain approximately at the same\\n\",\n    \"  coordinate positions when starting to evolve the initial data in these\\n\",\n    \"  coordinates. Note that $\\\\beta^i \\\\propto r$ in these coordinates, which can be\\n\",\n    \"  large and hence numerically undesireable.\\n\",\n    \"- `ShiftExcess` ($\\\\beta_\\\\text{excess}^i$): This shift vector excludes the\\n\",\n    \"  rotational part, so it is asymptotically small. Import `ShiftExcess` instead\\n\",\n    \"  of `Shift` in an evolution to obtain coordinates in which the black holes are\\n\",\n    \"  orbiting. Note that only\\n\",\n    \"  \\\\begin{equation}\\n\",\n    \"    \\\\beta^i = \\\\beta_\\\\text{background}^i + \\\\beta_\\\\text{excess}^i\\n\",\n    \"  \\\\end{equation}\\n\",\n    \"  fulfills all coordinate conditions imposed on the initial data, such as\\n\",\n    \"  $\\\\partial_t K = 0$. The background shift is typically\\n\",\n    \"  \\\\begin{equation}\\n\",\n    \"    \\\\beta_\\\\text{background}^i = \\\\epsilon^{ijk} \\\\Omega^{j} x^k + \\\\dot{a}_0 x^i\\n\",\n    \"    \\\\text{,}\\n\",\n    \"  \\\\end{equation}\\n\",\n    \"  where $\\\\Omega$ is the orbital angular velocity and $\\\\dot{a}_0$ is the\\n\",\n    \"  expansion parameter.\\n\",\n    \"  See the [Xcts::AnalyticData::Binary](https://spectre-code.org/classXcts_1_1AnalyticData_1_1Binary.html)\\n\",\n    \"  class for details.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Plot diagnostics\\n\",\n    \"\\n\",\n    \"We plot the diagnostics in `BbhReductions.h5` to see what happened during the\\n\",\n    \"elliptic solve:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 9,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"# These routines read the data and process them a bit. You can skip to the plot\\n\",\n    \"# below to see the results.\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"def split_iteration_sequence(data):\\n\",\n    \"    left_bounds = np.where(data.index == 0)[0]\\n\",\n    \"    right_bounds = list(left_bounds[1:]) + [-1]\\n\",\n    \"    return [data.iloc[i:j] for i, j in zip(left_bounds, right_bounds)]\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"def load_dataset(subfile):\\n\",\n    \"    legend = subfile.attrs[\\\"Legend\\\"]\\n\",\n    \"    return pd.DataFrame(data=subfile, columns=legend).set_index(legend[0])\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"with h5py.File(\\\"BbhReductions.h5\\\", \\\"r\\\") as h5_file:\\n\",\n    \"    nonlinear_residuals = load_dataset(h5_file[\\\"NewtonRaphsonResiduals.dat\\\"])\\n\",\n    \"    all_linear_residuals = split_iteration_sequence(\\n\",\n    \"        load_dataset(h5_file[\\\"GmresResiduals.dat\\\"])\\n\",\n    \"    )\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 4,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAAA04AAAKHCAYAAAC2MBhJAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAABcSAAAXEgFnn9JSAADDlklEQVR4nOzdd1gUV9sG8HvpVbCLohIRAQsoioK9xq7YYlfU2GuCRmOMmqhRo2ISu0SssaLYe1cQDTasiAVsQbDQ+7LfH37My7qLUnZ3Frh/18X1cs7Mzjyrb3BvzplzJDKZTAYiIiIiIiLKkY7YBRAREREREWk7BiciIiIiIqIvYHAiIiIiIiL6AgYnIiIiIiKiL2BwIiIiIiIi+gIGJyIiIiIioi9gcCIiIiIiIvoCBiciIiIiIqIvYHAiIiIiIiL6AgYnIiIiIiKiL2BwIiIiIiIi+gIGJyIiIiIioi9gcCIiIiIiIvoCPbELKMoqVKiAxMREVKlSRexSiIiIiIiKvefPn8PU1BSRkZF5fi1HnNQoMTER6enpYpdBREREREQA0tPTkZiYmK/XcsRJjbJGmu7duydyJUREREREVKtWrXy/liNOREREREREX8DgRERERERE9AUMTkRERERERF/A4ERERERERPQFDE5ERERERERfwOBERERERET0BVyOnIiIiAodmUwGmUwmdhlEJBKJRAKJRKLRezI4ERERkdaTyWSIj49HXFwckpKSIJVKxS6JiERmYGAAc3NzlC5dGrq6umq/H4MTERERabXMzExERkYiNjZW7FKISIukpaXh3bt3SExMRJUqVdQenhiciIiISKvFxsYKoalUqVIwNzeHoaGhxqfpEJH2yMzMRGJiIt68eYOUlBS8e/cO5cqVU+s9GZyIiIhIq3348AEAUK5cOZQuXVrkaohIG+jo6MDCwgIA8Pr1a8THx6s9OHFVPSIiItJaMpkMqampAIASJUqIXA0RaRtTU1MAH6ftqXvBGAYnIiIi0lrZPwhp4uFvIipcdHT+F2cYnIiIiIiIiETG4ERERERERPQFDE5ERERERERfwOBERERERET0BQxORERERIWQRCLJ9V5WmzZtgkQiwdy5c9VbFAEAPD09IZFIcP78+Vy/5vz585BIJPD09FRbXZ+Tn5qLGwYnIiIiIiKiL+AGuEVcWFgYNmzYgPDwcNjY2GDEiBGws7MTuywiIiLSoB49esDNzQ1lypQRu5RiYeHChZgxYwaqVKkidimkQgxORdjGjRsxcuRISKVSoW/p0qXw8fHBsGHDRKyMiIiINMnCwgIWFhZil1FsWFlZwcrKSuwySMU4Va+ICgsLUwhNACCVSjFy5EiEhYWJVBkRERFpWk7POGV/ruXixYto3bo1zM3NUaJECXTu3Bn379/P8ZrHjx9H586dUbZsWRgaGqJatWr4/vvv8e7dO4Vz//vvP/z+++9o0aIFKlWqBAMDA1SoUAE9e/bEv//+q/T6NjY2kEgkkMlkWLFiBZydnWFiYoK6det+8f1mf18nTpxAq1atYGlpCYlEgpiYmHy9h7S0NKxevRqurq4oXbo0TExMYGNjgy5dumDnzp053v9T9+7dg4eHB0qWLAlzc3M0a9YMx48fz/G9tGzZEhKJBOHh4QrHwsPDIZFI0LJlS7n+mJgYrFixAu3bt0fVqlVhaGiI0qVLo0OHDjh16tRn/+woZwxORdSGDRsUQlMWqVQKX19fDVdERERE2urQoUNo3bo1kpKS0KlTJ1hZWeHo0aNo3rw5IiMjFc6fMWMGOnbsiNOnT8Pe3h7dunWDnp4eli9fjkaNGuHNmzdy5x84cADTp0/Hmzdv4OTkhB49eqBixYrw9/dHkyZNcPLkyRxrGzNmDLy8vFCuXDl069YN1apVy/X72r59Ozp27IjExER07NgRrq6uwoIaeX0PAwcOxPjx4xEaGgo3Nzd0794dVapUweXLl7F27dpc1RMcHAw3NzccOHAA1tbW6NKlC5KTk9GpUyfs2bMn1+/rS4KCgjBp0iQ8evQI9vb26NGjB+zt7XHy5Em0b9+enwPziVP1iihlv5XI6fj79+8xZMgQNG7cGI0bN0bz5s2ho8NMTUREhYNMJkNsbKxKr5mQkIBZs2bh33//haurK+bPnw8zMzOVXd/CwiLXK+Jpwh9//IG9e/fCw8MDwMdfsvbt2xd79+7F6tWr8euvvwrn7tmzB4sXL0bt2rXh7++P6tWrA/j49zB37lz8+uuvmDx5stwoTJMmTXD37l3UqlVL7r4nTpxAt27dMG7cOISFhSn9M9m3bx9u3ryp8Nrc8PHxwc6dO9G3b1+5/ry+h2fPnsHPzw9Vq1bF9evXUbp0aeFaKSkpuHnz5hdrkclkGDp0KBISEjB79mz88ssvwrHVq1dj/PjxeX5/ObG3t8eVK1fg5uYm13/z5k20bt0a3333Hb755huV/n+6OGBwKqJsbGxyfTwoKAhHjhzBkSNHULZsWYXfsBAREWmz2NhYlCxZUm3Xv3//PjZv3qzSa3748AGWlpYqvWZB9O/fXwhNAKCrq4sff/wRe/fuxcWLF+XOXbBgAQBgx44dQuAAIEwFPHjwIPz8/PD27VthMYo6deoovW/79u3Rp08f/PPPP7h7967S86ZPn56v0AQAnTt3VghN+XkP0dHRAIB69erJhSYAMDIygru7+xdrOX/+PO7fv49q1aph9uzZcsfGjRuHLVu24OrVq3l+j8p89dVX+OqrrxT669Wrh/Hjx2PBggU4d+4cunbtqpL7FRcMTkXUiBEjsHTpUqXT9XR1dTF8+HChHRgYKHzfuHFjhd/2JCUloVatWnB1dUXjxo0xevRoGBsbq694IiIi0qivv/5aoa9GjRoAPj6flCUqKgq3b9+GnZ0dateurfAaiUSCJk2a4NatW7h+/Trat28vHEtNTcXx48dx7do1REdHIy0tDQBw584dAB+fz1YWnLp165bv96Xstfl5Dw4ODjA1NcWRI0ewZMkSDBw4EBUrVsxTLZcuXQIA9O7dG7q6ugrH+/fvr7LgBHwcNTxz5gwCAwPx33//ITU1FQCE59z5vHveMTgVUXZ2dvDx8cG3336LzMxMoV9XVxd///233JLk3bp1g66uLgIDA9G2bVuFa127dg3h4eEIDw/H8ePHMXHiRIVz4uLiUKJECfW8GSIiIlIra2trhT5zc3MAED5wA/+b6p/TtLrs3r59K3x/584ddOvW7bOPEsTHxyvtL8iS3spem5/3UKJECfj4+GDUqFH44Ycf8MMPP6BGjRpo1aoVBg8ejCZNmnyxltevXwMAqlatqvT4l2YL5cXLly/RpUsX3L59O8dzcvrzppwxOBVhw4YNg7OzM+rXry/0HTt2DO3atZM7r2HDhmjYsGGO18k+IuXm5qbwW5K0tDRUqFAB1tbWaNy4MZYsWYKyZcuq6F0QERF9noWFBT58+KDSa2riGSdtkttnm7N+GVuhQgW50SRlsgKCTCbDN998g/DwcIwZMwZjxoxBtWrVYGZmBolEgpkzZ2LhwoWQyWRKr2NkZJSHd/Ll1+bnPQAfR4Tatm2LAwcO4OTJk7hw4QLWrVuHdevW4fvvv8eyZcvyXWd+Zf/leHbffvstbt++jV69euGHH36Avb09zM3NoaOjg/Xr12P06NE5/nlTzhicijgXFxfY29sjNDQUAIQ5unkxcuRIODo6IjAwEDVr1lQ4fuvWLSQnJyMsLAzPnz/HunXrFM65ceMGqlevzlEpIiJSOYlEovLnhSwtLbFp0yaVXrMoyBqZKlOmTK7/fB4+fIiHDx+iQYMGWLNmjcLxp0+fqrLEL8rPe8hStmxZfPvtt/j2228hk8lw4sQJ9O3bF97e3hg+fPhnn8XK2tcpIiJC6fGc+g0MDAB8DPOfevHihUJfYmIiTp06hfLly2PXrl0Kv/DW9J93UcKl04oBV1dX4fuc9kr4nLJly6JHjx5YsmSJ0o1zr1+/LnzfoEEDGBoayh3PzMxEq1atULJkSdStWxePHj3Kcw1EREQkPmtrazg4OOD+/fu5/vc8azRQ2XTADx8+aHxfofy8B2UkEgk6dOiAzp07A/i4P9PnNGvWDACwd+9epSNFn+4FlSUrcCmrVdmfXWxsLDIzM2FlZaUQmtLT0+Hv7//ZOilnDE7FQPZpePkJTl8yZswYPHv2DP/88w+8vLwUjt+7dw9xcXHIzMzEnTt3lD5MuX//fgQGBiIlJUXl9REREZHq/Pzzz8jMzESvXr1w69YthePv3r2Dj4+P0K5evTp0dHRw9uxZuQUJUlJSMGbMGLx//14TZcvJ63u4efMm9u3bJyxokeX9+/fCgg6VK1f+7D1btmwJBwcHPHnyBPPnz5c7tm7dOly5ckXp61q0aAEAWLZsGZKSkoT+s2fP4o8//lA4v1y5crCwsMDdu3cREBAg9EulUkyfPp2/wC4ATtUrBrKPON24cQMZGRnQ01PdX71EIoGNjU2ODzU+f/4clpaWiImJgbOzs8IccZlMhtGjRyMqKgoGBgY4ffq08FsZIiIi+rxP9+rJLmtamSoNGDAA9+7dw2+//Yb69eujbt26sLW1hUwmw5MnTxASEgIzMzOMHDkSwMcP8iNGjICPjw+cnZ3RunVrGBsb49KlS5BKpfD09NT4tMi8voeIiAj06tULFhYWaNCgASpUqICYmBhcvHgR8fHx6Nq16xeXJNfR0cGmTZvQpk0bzJkzB35+fqhduzYeP36M4OBgjBs3DqtXr1Z4Xf/+/fH7778jMDAQjo6OcHV1xcuXL/Hvv//i+++/x9KlS+XO19PTww8//ICffvoJLVq0QOvWrVGqVClcvXoVb968wfjx47Fq1SrV/WEWIxxxKgbq1q0rBKXk5OQvDiWrWufOnfHu3Tvcu3cPK1euVDj+9OlTREVFAfi40ISjo6PCOatXr8bq1atx69YtZGRkqL1mIiKiwuLq1as5fr18+VIt91ywYAEuXLiAXr16ITIyEvv378e5c+cglUoxduxYHDx4UO78NWvWYNmyZfjqq69w5swZXLp0CW3btkVwcHCOq8ypW17eg5ubG+bPn4/69esjNDQUe/bsQXBwMJycnODr64u9e/fm6p6NGjXClStX0LVrVzx//hwHDx6Enp4eDh06hD59+ih9jbGxMc6cOYP+/fsjPj4eR48ehVQqxa5du3LcNHfmzJnYvHkznJycEBAQgNOnT8PZ2RlBQUFo0KBB3v+wCAAgkXFJDbXJekBQ00FFGRcXF2FX66xlyrXFrVu3MGXKFFy7dg1VqlTBw4cPFc756quvhOVDd+/eneMPFyIiKloyMzOFBY7s7e1zvfobERUPef0ZUZDP5/zpU0wUdIEIdapbty7Onz+P2NhYnDhxQuH469ev5fZ9UDYU/uuvv2LkyJHYuHEjXr16pc5yiYiIiKgYYnAqJtS9QIQq6OvrKx2uz8zMxOTJk+Hq6opq1aopXZVn165d+PvvvzF8+HCcPn1a4TgHVomIiIioILg4RDGRfcTpzp07SE5OhrGxsYgV5Z61tbWwaoyy55s+fPiA+/fvC+3GjRsrnDN16lRcvHgRjRs3Rv/+/T/7IC0RERER0acYnIqJmjVrwtjYGMnJycjIyMCtW7e+uPqLNlK2GqC+vj42bdqEwMBAPHjwANWrV1c458KFC7h+/TqCg4Ph4OCgEJzi4uJgbGwMfX19tdVORERERIUXp+oVE3p6enBxcRHa2jpdLz/MzMwwdOhQrFu3DhcvXoREIpE7npiYKLdHg7IRqd9++w0WFhZo0aIF/vnnH3WXTERERESFDINTMaLNC0Sok5GREYKDg7F69WoMGzYMtWvXVjgnMDAQycnJuHjxIiIjIxWOv3z5Evfu3VO60zcRERERFX0MTsVIYVggQh10dXVRt25djB07Fr6+vtDV1ZU7LpVKERISIrSVjUj5+vqidu3aKFWqFGbPnq32momIiIhIu/AZp2Ik+4hTaGgoYmNjYWFhIWJF2kFXVxdv3rzBjRs3EBAQIDelMUtgYCAAIDY2FgYGBgrHw8LCcPXqVTRp0gQ2NjYK0wWJiIiIqHDjiFMxYmtri5IlSwrt4OBgEavRLoaGhnB3d8fUqVNhaGiocDwlJUXYUE3ZiNSBAwcwePBgVKtWDT179lR7vURERESkWQxOxYhEIkGDBg2EdnGarldQ58+fR0xMDE6fPo1GjRopHM8akQIAR0dHheOhoaGYNm0a9u3bp/QZKiIiIiLSbpyqV8y4urri1KlTABic8src3Bxt2rRReqxatWqoUaMGHj16pHRE6vTp01i6dCmAj38H165d++y9wsLCsGHDBoSHh8PGxgYjRoyAnZ1dwd8EEREREeULg1MxU1xX1lO3pUuXYunSpYiOjoaZmZnC8ewjUk2aNFE4/vjxY4wZMwaNGzdGYmIi/vzzT0ilUrnr+/j4YNiwYep5A0RERET0WQxOxUz2lfVevHiByMhIVKhQQcSKipayZcsq7W/fvj0yMjIQEBCgdETq0qVLOHPmDM6cOaP09VKpFCNHjkTTpk058kREREQkAj7jVMxUrFgRFStWFNocddKMIUOGYNeuXXj58iV69eqlcDz7iFROpFIpfH191VEeEREVQhKJBBKJBJaWloiJiVF6zqJFiyCRSDB37lyN1pbF09MTEokE58+fl+tv2bIlJBIJwsPDRamruJFIJLCxscnTa3L6u9OU/NSsbgxOxRCn64kra3W+7EaNGoXffvtNLtQqw39giIjoU7GxsfD29ha7DKIij8GpGGJw0j6urq748ccfMXjw4M+ep22/eSEiInFJJBIYGRnhzz//xIcPH8QuJ9e2bNmCBw8eoFKlSmKXUiw8ePAgx8cBKPcYnIqhT4OTTCYTsRrKbsSIEdDV1c3xeKdOnTRYDRERaTsdHR2MGjUKcXFxwuqthUGVKlXg4OAAfX19sUspFhwcHGBrayt2GYUeg1MxlH0vp3fv3nH6lxaxs7ODj49PjuFp48aNGq6IiIi03YwZM2BsbIwVK1bg3bt3uX5dUlIS5s2bh9q1a8PY2BgWFhZo3rw5du7cqfR8GxsbSCQSAMDff/8NJycnGBsbo0KFChg9enSOz1kpk9MzTlnPtUilUixevBg1atSAoaEhKleujOnTpyM1NTXH97Jw4ULUq1cPZmZmMDMzg5ubGzZv3qz0/EuXLmHChAlwcnJCyZIlYWxsDAcHB8yYMUPp+zh//jwkEgk8PT0RGRmJb7/9FtbW1tDT08Mff/zxxfeb9b7S0tLw66+/wsHBAYaGhvDw8Mj3e4iIiMDYsWNRo0YNmJiYoFSpUqhVqxZGjx6N0NBQpfdXxtfXF3Xr1hX+LrPeozLh4eGQSCRo2bKl0uNz586FRCLBpk2b5Ppv3bqFH374AfXr10fZsmVhaGiIatWqYdy4cXj9+rXSa2kjBqdiqFSpUqhevbrQ/tKeQqRZw4YNw+3btxWeherZsydWrlwpUlVERMVLWFgYZsyYgX79+mHGjBkICwsTu6QcWVlZYcyYMYiPj8eSJUty9Zr4+Hg0b94cs2fPRlRUFLp06YImTZrg2rVr6N+/PyZPnpzja3/44QeMHz8eVlZW6NixI2QyGdavX49u3bqpbBbLgAEDMH/+fNjb2+Prr79GfHw8fv/9d4wYMULh3KioKLi7u2PmzJmIjIxEixYt0Lx5czx8+BCenp6YOHGiwmumTZuGDRs2wNjYGG3atEGbNm0QFxeHxYsXo2nTpkhISFBaV3R0NFxdXXHkyBG4u7ujY8eOMDExydV7yszMhIeHB37//XfY2tqie/fusLKyytd7ePHiBVxcXLB27VoAH2ektGjRAoaGhvDx8cGVK1dyVdOMGTMwYsQI3L9/H82bN0fz5s1x7NgxNGrUCO/fv8/VNXJj0aJFWL58OQCgadOm6NSpE2QyGdasWYMGDRoUnvAkI7WpWbOmrGbNmmKXoVT//v1lAGQAZF5eXmKXQ0q4uLgIf0eTJk2SSaVSsUsiItI4qVQqu3//vuz+/fsa+zno6+sr09XVFX4GA5Dp6urKfH19NXL/3MqqSyaTySIjI2UmJiYyU1NTWVRUlHDOwoULZQBkc+bMkXvthAkTZABkrVq1ksXFxQn9Dx48kJUrV04GQHbo0CG511StWlUGQFahQgXZw4cPhf7o6GhZ9erVZQBkZ86ckXvN0KFDZQBk586dk+tv0aKFDIDs2bNnCu8JgMzR0VH233//Cf1Pnz6VWVpaygDIHj9+LPeaTp06yQDIJk+eLEtJSRH6IyMjZQ0aNJABkB07dkzuNUePHpXFxMTI9aWkpMhGjRolAyD75Zdf5I6dO3dOqK1Hjx6y5ORkWV5kvbZ69eqyly9fKhzP63uYPXu2DIBswoQJCteKiIhQ+DMCIKtatapc35UrV2QSiURmYWEhu3HjhtAfHx8va926tVBz9r+7Z8+eyQDIWrRoofR9zpkzRwZAtnHjRrn+s2fPyiIjI+X6pFKp7JdffpEBkA0bNkzhWspqViavPyMK8vmcI07FFBeI0H4uLi7C9xkZGUpX4yMiov9JSUlBTEwMYmJikJSUpHA8IyNDOB4bG6v0Gjdu3MDIkSPlNiEH/ref3u3bt4VrpKSkKLw+LS1NOJ7TqIU6lC9fHmPHjkViYiIWL1782XMTExOxYcMG6OjoYPXq1TA3NxeOOTg4YNasWQCAP//8U+nr582bB3t7e6FdpkwZjBkzBgBw8eLFgr4VAMBff/0lt8/kV199hUGDBgH4OM0uy61bt3D06FG4urrC29sbhoaGwrHy5ctj/fr1AIA1a9bIXb9jx46wsLCQ6zM0NMQff/wBPT09HDhwQGldhoaGWLFiBYyMjPL1vhYuXKiwIEZ+3kN0dDQAoG3btgr3qFKlSq6eZ1qzZg1kMhkmT56MevXqCf1mZmZYsWKFMC1TFVq1aoXy5cvL9eno6GD27NmoVKkSDh48qLJ7qRM/iRVT2YPT9evXFf6BIPFl/yF28+ZNhePR0dEMvURE2SxatAglS5ZEyZIlMW7cOIXjwcHBwvHsH/yza9y4cY7/JkqlUvTv31+4hrJnW7Zv3y4c79KlS4HeT15Nnz4dpqamWLNmDd68eZPjedevX0dycjJcXFzg4OCgcDxrhdeAgABkZmYqHP/6668V+mrUqAEA+O+///JbvkBfXx+tWrXK1T1OnjwJAPDw8FD6C8as54WUPZbw6tUrrF27FlOmTMHw4cPh6emJsWPHwsDAIMepmS4uLvleCVAikaBr164K/fl5D/Xr1wcAzJw5E4cPH1Ya4r8kK4D269dP4VjNmjXh7Oyc52t+zrt377Bx40Z4eXlhxIgR8PT0hKenJ9LT0/Hu3TuVTg1UFz2xCyBx1KtXD7q6upBKpUhMTMTDhw9Rq1YtscuibLKPON2+fRtSqVRYNOLevXvo0qUL4uLicPXqVbln1oiIKP+UBYXsEhMTNVRJ3pUtWxbjx4/H77//LvdMyaeynifJabEAS0tLWFhYIDY2Fh8+fEDp0qXljltbWyu8JmvUKqfFG/KiQoUKShdJUnaPrMUlfvrpJ/z00085XvPTYOHt7Y0ZM2YgPT09T7VVqVIlT+dnV65cObnRpCz5eQ+enp44efIkdu/eja5du8LIyAiurq7o0KEDhg8fLjdal5Os/x9UrVpV6XEbGxvcunXri9fJjR07dmDUqFGfHYWNj49HqVKlVHI/dWFwwsffQP31118IDAzEkydP8NNPP2H+/Plil6VWpqamqFWrFkJCQgB8nK7H4KRdnJycoKOjg8zMTCQlJeHRo0dwdHREUlISWrdujaioKABAly5dEBwcDDMzM5ErJiIq/L40LdrU1FRDleTPtGnTsHr1aqxduxY//PBDvq/zuWla6p46npfrZwXdpk2b5nq57aCgIHh5ecHCwgJ//vknWrZsiQoVKgihpmLFijmOnOV3it7nXpuf96Crq4tdu3ZhxowZOHDgAM6ePYurV6/i0qVLWLRoEY4fP47GjRvnu9b8UvaLh4iICHh6egIA/vjjD3Tu3BmVKlWCsbExgI+jvFeuXCkU2+MwOOHjUHRQUBCaNm2Kt2/fil2Oxri6ugrB6dq1a8L/qUk7mJiYwMHBAffv3wfwcd69o6MjTExM8Oeff6J///6QSCQYOXKk1v9DTkSkCTNmzMCUKVMAAAYGBgrHGzRoIGwSm1MwCAwMRMOGDZVO19PV1cWOHTuE39Ar+yA8YMAAYYlpPT3Nf8wqU6YMJk6ciIULF2LhwoWoWLGiwjlZfREREUqvERsbi5iYGBgbG6NkyZJqrbegska/PDw84OXllavX+Pv7AwAWLFiAoUOHyh1LTk7OcSludcnPe8hSr1491KtXD3PnzkVcXBzmzp2L5cuXY8qUKV9cNdnKygrh4eGIiIiAo6OjwnFl///I+u8qp5GjFy9eKPQdPXoUaWlpmDp1qtLVGp8+ffrZOrUJn3ECMHHiRDx69AibNm2CpaWl2OVoDBeI0H7Zp+tlf86pX79+WLRoEfz9/eHl5aXSBziJiAorIyMjWFpawtLSUukS0Xp6esLxTxcGyOLi4qJ0Pz1dXV38/fffcHZ2Fq6hLDgZGBgIx8WaCeDl5QVzc3OsX78er169Ujhev359GBsb4/r160qf5dm2bRsAoEmTJlq/MFG7du0A/C8M5UZWeFY25XDPnj0aH/nIz3tQpkSJEli4cCEkEgnu3r37xfObNWsGANi9e7fCsYcPHyqdplemTBno6enh2bNnyMjIkDuWnp6OCxcuKLzmc3/eFy9e/OzzeNpGu/9r0BBt/6GgLtmD0+3bt1UyL5lUK/sCETdu3JA7Nn36dHTv3l3TJRERFXnDhg3DgwcP5PZxevDgQaGZmVG6dGlMmjQJqamp2LBhg8JxU1NTDB8+HJmZmRg/frzcc1uPHj0SHleYNGmSxmrOr0aNGqFdu3YICAjA+PHjERcXp3DO7du3cfz4caGdtcjEhg0b5J5xun//PqZPn67+oj+Rn/ewdetWpeHo2LFjkMlkqFy58hfvm7US4h9//IHbt28L/YmJiZg4caLSAGlgYAB3d3e8f/8eq1atEvozMjLg5eWFZ8+eKbwm689727Ztcv9fe/XqlVBDYaHRxHD9+nUsWrQIPXv2hLW1NSQSSa5+U56cnIzZs2ejRo0aMDIyQsWKFTF8+HClv0Wh3KtTp44wnzc9PV2Ytkfa49MRp9z8FkybH1wmIios7OzssHDhQuzYsQMLFy6EnZ2d2CXliZeXF0qUKIHk5GSlxxcuXIj69evj1KlTqFatGr755ht07twZzs7OiIyMxKRJk5SuAKeNtm3bhnr16mH16tWoWrUqWrVqhYEDB6JLly6oUqUK6tatKxc6hg0bhgoVKuDQoUOwt7dH37590a5dO9StWxfNmjXLcbEEbXoPe/fuRZ06dVC9enX06NEDAwYMgLu7O3r27AkdHZ1cPavfuHFjTJ06FTExMcLCEn379oWtrS0ePXqU49//7NmzoaOjgylTpqBx48bo2bMnbG1tsWPHDoWpjwDQrVs31KpVC8HBwahevTp69+6NLl26oEaNGihZsqQoz2Lll0aD07x58/Djjz/C398/16EnJSUFrVu3xrx585CQkIDu3bujcuXK2LhxI+rVq1eo5kVqG319fbkRDU7X0z5169YVvo+JiRFW3snJrl278NVXXzEEExEVcyVLlhSe91LG3NwcFy5cwC+//IIyZcrg4MGDuHTpEho0aIDt27fnuIeTNipXrhwCAwPx119/oWbNmrh58yb8/PwQEhKCatWqYcmSJZg6dapwfunSpfHvv/9iwIABSEtLw8GDB/Hq1SvMmzcPO3bsKBTv4fvvv8f48eNhbm6OS5cuwd/fH1FRUejbty+uXr2KPn365Oq+S5YsgY+PDxwdHXH+/HmcP38e7dq1w5UrV3Jc4a5t27Y4ePAgXF1dcePGDVy4cAFubm74999/la7UaGBggEuXLmHs2LEwMjLC4cOH8eDBA0ycOBGnTp2Cvr5+vv7MxCCRaXAi5+LFi5GYmAhXV1e4urrCxsYGqampn/0t+qxZs7BgwQK4u7vj5MmTwnxhb29veHl5oUWLFjh//rxwfkxMzBcf6jMxMclxOUkbGxsMGjRIJavqZa1Sd+/evQJfS10mTZqEFStWAACGDh2KTZs2iVsQKbC1tRV+QeDn54devXopPW/JkiXCCkpVqlTBtWvXFDabIyIqbDIzMxEaGgoAsLe3L7bT64lIubz+jCjI53ONLveS13mjaWlpWLlyJQBg1apVcg9Zfv/999i8eTMuXLiA69evCxuB7dy5E2PHjv3sdT8NW8UZF4jQftlHVm/evJljcKpZs6awfHlCQgLCw8MZnIiIiIhURKt/bRMQEIDY2FjY2trKTSnL0rt3bwDAoUOHhL4xY8ZAJpN99ouh6X+yB6cHDx4gPj5exGpImezPOX26QER2nTt3hre3N2rUqIGgoCA0atRIE+URERERFQtavY9T1gof2T84ZpfVL/bzHDltHPvkyZNcb2Qmlho1aqBEiRKIi4uDTCbDjRs30KJFC7HLomxyWpJcmUmTJuHbb7/lvk5EREREKqbVI07Pnz8HoHzd9+z9OW3gllvR0dHw8/ODn58fkpKS8PDhQ/j5+eHYsWMFum5hoKOjgwYNGghtTtfTPtlHWyMjI3PczRz4uKEjQxMRERGR6mn1iFPWrsTKNrEDIHxALOj0snv37smtPrJ3717s3bsXVatW/eIqZlmvVyankSht4+rqirNnzwLAF3eZJs0rX748KlasiNevXwP4OF2vc+fOuX69VCrF9OnT0atXL7i7u6urTCIiIqIiTatHnDSlZcuWSp+Fyk1oKgq4QIT2yz7q9KXpetnFxcWhW7duWLZsGTw8PIrN/6eJiIiIVE2rg1PWKnpJSUlKj2dt9Glubq6xmoqi7MEpPDwc0dHRIlZDyuR2gYhP3b9/H6dOnQIAREVFYdasWSqvjYiIiKg40OrglLXX0suXL5Uez+oXY4fnoqRy5cooV66c0A4ODhaxGlImLwtEZOfm5ob169cDAHr06IF169apvDYiInWSSCTC91KpVMRKiEgbZWZmCt9n/3mhDlodnJydnQHk/Bv2rH4nJyeN1VQUSSQSNGzYUGhzup72yT5VLzw8HO/fv8/1az09PXHs2DH4+flx4QgiKnQkEgkMDQ0BfJx+TESUXdYMNAMDA7UHJ61eHKJJkyawsLDAkydPcOvWLdStW1fuuJ+fHwCga9euIlRXtLi6uuLw4cMAuECENqpSpQpKlSolBKZbt26hdevWuX59hw4d1FUaEZHalSxZEpGRkYiKikJGRgbMzc1haGio9g9JRKS9MjMzkZiYiDdv3gDQzKM7Wh2cDAwMMGHCBCxYsADjx4/HyZMnhd+Ye3t7IyQkBC1atED9+vVFrrTw+3SBCJlMxn+QtIhEIkG9evVw5swZAB9HW/MSnJR5+/YtwsLCuNIeEWk9CwsLpKSkICYmBu/fv8/TqDsRFX1GRkYoXbq02u+j0eB05MgRzJs3T2inpaUB+PgcRpaff/5ZbqnlWbNm4fTp0wgMDISdnR2aNWuGiIgIXL16FWXLloWvr6/m3kARlj04RUVF4cWLF8IzZqQdXFxc5IJTQTx48ABdunTBu3fvcOXKFTg6OqqiRCIitdDR0UGFChVgamqK+Ph4JCYm8nknIoKBgQHMzc1RunRp6Orqqv1+Gg1O0dHRuHr1qkJ/9r5PV3QzMjLCuXPnsHDhQmzfvh379+9HqVKl4OnpiXnz5uW4OS7lTZkyZWBjYyMsV/3vv/8yOGmZ/C5J/qmUlBS0bdtW2BeqS5cuuHnzJkqUKFHgGomI1EUikaBEiRLCz6qsrUOIqHiSSCQanx0lkfGnjtpkbYCb0wa52qZv377YvXs3AGD69OlYtGiRyBVRdqGhoXBwcADw8YdFXFycsGR/Xvn7+6Nnz56QSCRYvHgxpk6dyqmZREREVOQV5PO5Vq+qR5rFjXC1m52dnRCUZDIZQkJC8n2tHj16wNvbG/v27cO0adMYmoiIiIi+gMGJBNmDU3BwsNy6+CQ+HR0dYYl+oODPOX333Xfw8PAoYFVERERExQODEwlcXFyEkYe4uDg8evRI5IroU9k3wi1ocMpJfHy8Wq5LREREVJgxOJHA3NxcbnU1TtfTPqpaICInu3fvxldffYXr16+r/NpEREREhRmDE8nhc07aLfuI0927d5Gamqqya3t7e6Nv37549+4dunXrhlevXqns2kRERESFHYMTyWnYsKHwPYOT9qlZsyYMDAwAABkZGSpdsdHZ2VnYAyE5ORnPnz9X2bWJiIiICjsGJ5KTfcTp5s2bwibFpB309fVRp04doa3K55zatGmD1atXw87ODlevXoW7u7vKrk1ERERU2DE4kRwnJyfo6+sDAFJTU3H37l2RK6JPZZ+up+rnnEaNGoVbt27Bzs5OpdclIiIiKuwYnEiOoaGh3JLXnK6nfbIvEKGOlfVMTExUfk0iIiKiwo7BiRRwgQjtln3E6fbt25BKpWq9n1QqxbRp03DhwgW13oeIiIhImzE4kQIuEKHd6tSpAx2dj//pJicnIzQ0VG33io+PR/fu3bF06VL07NkTjx8/Vtu9iIiIiLQZgxMpyD7idPfuXSQmJopYDX3KxMREbr8tdW2ECwCPHj3C6dOnAQDv37/HrFmz1HYvIiIiIm3G4EQKHBwcYGpqCgDIzMxUy0arVDDqXCAiu/r162Pz5s0AAA8PD2zYsEFt9yIiIiLSZgxOpEBXVxf169cX2pyup33UvUBEdn379sXp06exd+9eIVATERERFTcMTqQUF4jQbp+OOMlkMrXer02bNsJzVURERETFET8JkVJcIEK71a1bV/g+NjYWz54903gNb9++xcWLFzV+XyIiIiIxMDiRUtlHnB4/foz379+LWA19ysLCAra2tkJb3dP1PvXgwQM0atQInTt3RkhIiEbvTURERCQGBidSysbGBqVLlxbawcHBIlZDymR/zkmTC3ikpqaiffv2ePr0KRISEtC1a1fExMRo7P5EREREYmBwIqUkEgmfc9Jy2Z9z0uSIk6GhIdauXQsdHR1IJBKMHz8eFhYWGrs/ERERkRgYnChHzs7OwvcrV65EVFSUiNXQpz4NTupeICK7Tp06YcWKFdi7dy9++OEHSCQSjd2biIiISAwMTpSjW7duCd9HRkbC1tYWR44cEa8gkpN9ql5UVBT+++8/jd5/3Lhx6NGjh0bvSURERCQWBifK0atXr+TaCQkJ6NKlC3r16oWXL1+KVBVlKVeuHCpVqiS0tWWj4ri4OLFLICIiIlI5BifKUePGjZX279u3D46OjvD29kZGRoaGq6LsNLkRbm74+fnBxsYGgYGBYpdCREREpFIMTpSjZcuWYdSoUahTpw569+4Nd3d34VhCQgK8vLzQoEEDXLlyRcQqizexFohQ5o8//kCfPn3w4cMHeHh4IDw8XNR6iIiIiFSJwYlyZGZmhnXr1iEkJAR79uzB5cuX4evrK7dM+e3bt9G4cWOMHj2aez2JIHtwEnuqXv369aGvrw8ASE9P53ROIiIiKlIYnCjXdHR0MGzYMISGhmLEiBFyx9avXw8HBwds2bJFo6u7FXfZp+pFRETg3bt3otXSrFkz+Pj4wM7ODkFBQWjatKlotRARERGpGoMT5Vnp0qXx999/4/Lly6hdu7bQHx0djaFDh6JVq1Z48OCBiBUWH5UrV5YbAcy+EqIYhg4dipCQENjb24taBxEREZGqMThRvjVp0gQ3btzA77//DhMTE6H/woULcHZ2xsyZM5GUlCRihUWfRCLRugUijIyMxC6BiIiISOUYnKhA9PX1MW3aNDx48ADdu3cX+tPT07Fw4UKUL18eNjY2GDVqFBISEkSstOjSpgUilJFKpZg2bRpOnjwpdilERERE+cbgRCpRpUoV7N+/HwcOHECVKlWE/oSEBERERMDHxwft2rUT9Rmcoir7iJPYC0R8KiEhAT169MDSpUvRp08f3L9/X+ySiIiIiPKFwYlUqlu3brh//z6mT5+ucCwoKAgVK1bEwIEDceHCBS4ioSLZR5wePXqkVSN7T58+xZkzZwB83Bh31qxZIldERERElD8MTqRypqamWLRoEXr37q1wLC0tDdu3b0fLli3h4OCApUuXIjo6WoQqi47q1avDzMwMACCTyXD79m2RK/ofJycn/PPPP5BIJOjWrRu2bNkidklERERE+cLgRGqzceNGjBo1Cvb29qhbty7KlSsnd/zRo0eYNm0aKlWqhH79+uHs2bPIzMwUqdrCS0dHB3Xr1hXa2vack4eHB86ePYt9+/YJAY+IiIiosGFwIrXJ2kD34cOHuHnzJl6+fIl9+/ahQ4cOkEgkwnnp6enYtWsX2rRpA3t7eyxevBhv3rwRsfLCR5s2wlWmZcuW0NXVFbsMIiIionxjcCKN0dfXR48ePXDs2DE8ffoUs2bNQsWKFeXOefz4MWbMmAFra2v06dMHp06d4ihULmjbkuS58fbtW5w9e1bsMoiIiIhyRSLjE/pqU6tWLQDAvXv3RK5Ee2VkZODo0aNYv349jh49qnTBCF1dXVSsWBGDBw9GixYt0LBhQ1haWmq+WC0WEhICZ2dnAICenh4SEhJgaGgoclU5e/jwIbp06YLXr1/j4sWLaNCggdglERERUTFQkM/nDE5qxOCUN8+fP4evry82bNiAly9ffvZce3t7uLm5oVGjRnBzc0OdOnWgp6enoUq1T3p6OszNzZGamgoACA4ORv369UWuSrn09HTY29vj2bNnAAArKyuEhISgTJkyIldGRERERV1BPp9zqh5pjSpVqmDu3Ll49uwZDh06BHNz8xzPDQ0NxebNmzFu3Di4uLigRIkSaNasGaZOnQo/Pz+8ePGiWC13rq+vjzp16ghtbZ6up6+vDx8fHyHoTpo0CaVLlxa5KiIiIqLPK76/oietpaenhy5duqB///5Yv3690F+5cmUkJibi/fv3Cq9JTk7G5cuXcfnyZaGvfPnyMDY2hkwmw9dffw1vb+8ivaqbi4sLgoODAWjnAhHZtWnTBuvWrYOlpSV69uwpdjlEREREX8TgRFpr2bJlAIArV67A3d0dy5Ytg6mpKZ48eYKgoCBcvXoVV69exa1bt5Cenq7w+uwr8/n4+ODDhw/Ys2ePxurXtMK2QMTw4cPFLoGIiIgo1/iMkxrxGSfNSElJwc2bN3H16lUhUIWHhys9d/z48fjll1+K5NSwa9euoVGjRgAAY2NjxMXFFcrnvmJiYrj4BxEREakFn3GiYs3IyAju7u6YMmUKdu7ciWfPniEyMhLt27dXOHfVqlWws7PDihUrlI5SFWZ16tQR9kpKTk5GaGioyBXlnZ+fH2xsbHD+/HmxSyEiIiKSw+BERVL58uXh5+eHkSNHomrVqihRooRw7MOHD5g0aRLq1q2LU6dOiVilahkbG8PR0VFoF4bpetmtXLkSffr0QWxsLHr16oXHjx+LXRIRERGRgMGJiiwzMzOsX78e4eHhiIqKwpIlS+RW6rt//z6+/vprdO/evch8SHdxcRG+1/YFIj7l6uoq7D2VmZmJ169fi1wRERER0f8wOFGxYGhoiKlTpyIsLAwjRoyARCIRjh08eBA1a9bEDz/8gLi4OBGrLLjCtkBEdo0aNcLmzZtRvXp1XLlyBc2bNxe7JCIiIiIBgxMVK+XLl8fff/+N4OBgNG3aVOhPT0/HkiVLYGdnB19fX2RmZopYZf59OuJU2N5H3759cffuXTg4OIhdChEREZEcBicqllxcXHDx4kXs3LkTlStXFvqjoqIwYsQIuLq6yu0JVVjUrVtX+D4uLg7Pnj0Tr5h8ypquR0RERKRNGJyo2JJIJOjbty8ePnyIuXPnwtjYWDh248YNNGvWDJaWlhg6dCgSEhJErDT3SpQogerVqwvtwvackzJSqRTTpk3DwYMHxS6FiIiIijEGJyr2TExMMGfOHISGhqJ///5yx2JjY7FlyxZ89913IlWXd4X5OadPJSQkoEePHli6dCkGDBiA27dvi10SERERFVMMTkT/r3Llyti+fTsuXbokN/oEAAcOHBCpqrzL2tgNALZu3VpoRsuUefHihbCnU2JiImbNmiVuQURERFRsMTgRfaJp06YYNGiQXF90dDROnjwpUkV5c+3aNeH7ly9folmzZkhKShKxovxzdHTEzp07oaOjg65du2L79u1il0RERETFFIMTkRLe3t4YPny43EIFQ4YMwZs3b0SsKnfCw8Pl2rdu3ULNmjUL7TNCnTp1wvnz5+Hv7y+3DxcRERGRJjE4ESlhZmaGDRs24N9//xXC05s3bzBkyBCtX+I7+zLrWSIiItC9e3d0795dIVgVBs2aNYOurq7YZRAREVExxuBE9Bl16tSBt7e30D558iSWLVsmYkVftmzZMowaNQq1atVC3bp15QJH1ma/ixYtQlpamohVFty7d+8KzfRJIiIiKvwkMplMJnYRRVXWQ/r37t0TuRIqCJlMhl69esHf3x8AoKenh4CAADRs2FDkynLn/v37GDduHC5cuCDX7+DggNWrV6NVq1YiVZZ/oaGh6NKlC54/f45z586hcePGYpdEREREhUBBPp9zxInoCyQSCf7++29ho9yMjAz069cPsbGxIleWOzVr1sS5c+ewZcsWlCtXTuh/+PAhWrdujcGDBxeKZ7eyZGRkoGvXrnj8+DHS0tLg4eFRqOonIiKiwonBiSgXSpUqhe3bt0NH5+N/Ms+ePcOYMWNQWAZsJRIJBg8ejIcPH2LcuHGQSCTCsW3btsHe3h6rV6+GVCoVscrc0dPTw4YNG6Cvrw8A+O677+QCIREREZE6MDgR5VLTpk0xd+5cob1z505s3LhRvILyoWTJkli1ahWuXr2K+vXrC/2xsbEYP3483NzcEBwcLGKFudOsWTP4+vpiz549+PHHH+WCIBEREZE68BknNeIzTkWPVCpF27ZthU1ZTUxMEBwcDEdHR3ELywepVIq1a9fip59+kpt2KJFIMHbsWCxYsACWlpbiFUhERESkYnzGiUhDdHV1sW3bNpQuXRoAkJSUhH79+iElJUXkyvJOV1cX48ePx8OHD+U2/JXJZFi9ejXKlCmDpk2b4v379yJWmXcfPnwQuwQiIiIqghiciPKoUqVKclP0QkJCMHXqVBErKpgKFSpg69atOHv2LBwcHIR+qVSKgIAAVK1aFRs3bkRGRoaIVebOvn37YGNjgxMnTohdChERERUxDE5E+dC1a1dMmjRJaK9atQr79+8XryAVaNWqFW7fvo0KFSrI9SckJGD48OGoXbs2du3apbUbAK9Zswa9evVCXFwcvvnmG9y/f1/skoiIiKgIYXAiyqfff/8d9erVE9rDhw/HixcvRKyo4AwMDNCtWzelx0JDQ9GvXz/Uq1cPBw8e1LoVBd3c3GBiYgLg43NaUVFRIldERERERQmDE1E+GRoaYufOnTA1NQXw8dmagQMHFoopbZ+zbNkyjBo1CnXq1EH//v0xYMAAYRl24OPUxO7du8PNzQ2nTp3SmgBVr149/PPPP7Czs0NQUBBatmwpdklERERUhHBVPTXiqnrFw+bNm+Hp6Sm058yZI7dseVHw4MEDzJ07F7t371Y41rx5cyxYsABNmzYVoTJFaWlpMDAwELsMIiIi0kJcVY9IREOGDMHAgQOF9rx583DhwgURK1I9R0dH7Nq1Czdv3kSXLl3kjl28eBHNmjVDx44dtWIPKIYmIiIiUgcGJ3wcMWjQoAEsLS1hamoKFxcX7Ny5U+yyqJCQSCRYs2YNbG1tAQCZmZkYOHAg3r17J3Jlqle3bl0cOnQIV65cQZs2beSOHT9+HK6urujZsyfu3r0rUoWKpFIpfvjhB/j5+YldChERERViDE74+GyKh4cHtm3bhgMHDqBx48bo379/oV8ljTTH3NwcO3fuhL6+PgDg1atXGDZsmNY8/6Nqbm5uOH36NM6ePQt3d3e5Y/7+/qhTpw4sLS3RuXNnuc11NS0hIQG9evXCkiVLMGTIEK0YESMiIqLCic845aBp06awsrLCnj178n0NPuNU/CxbtkxuT6e//voLEydOFLEi9ZPJZDh27BhmzZqFmzdvKhw3MTHBxIkTMXToUDg6Omq0trCwMDRs2BAxMTEAgA4dOuDYsWMarYGIiIi0B59xUoPSpUsjPT1d7DKokPnuu+/QoUMHoT158mR0794dCQkJIlalXhKJBJ06dUJwcDD8/PxgaGgodzwpKQmLFy9GzZo14erqipUrV2psGqOdnR38/Pygp6eHLl26KF3cgoiIiCg3NBqcrl+/jkWLFqFnz56wtraGRCKBRCL54uuSk5Mxe/Zs1KhRA0ZGRqhYsSKGDx+OV69eqbS+jIwMxMXFYdeuXTh16hRGjx6t0utT0aejo4PNmzfD2NgYwMfRmIMHD6Jv374iV6Z+Ojo66NWrF4YMGZLjOcHBwZg4cSKsrKzQo0cP7N+/H2lpaWqtq02bNrh48SL2798Pc3Nztd6LiIiIii6NTtXz8PDAgQMHFPo/V0JKSgpatWqFoKAgWFlZoVmzZggPD8e1a9dQtmxZBAUFoVq1agWuLTIyElZWVgAAXV1drF69GqNGjSrQNTlVr/iytbXF06dP5fr8/PzQq1cvkSrSnISEBHh5eeHKlSuoW7cuateujZ07dyqdxgd8HN3t378/hg4divr16+fqlylERERE+VGQz+caDU6LFy9GYmIiXF1d4erqChsbG6Smpn42OM2aNQsLFiyAu7s7Tp48CTMzMwCAt7c3vLy80KJFC5w/f144PyYmBpGRkZ+tw8TEBFWqVJHry8jIwK1btxAfH4/jx49j+fLl2LFjR4E+6DI4FV+jR4/G+vXr5fp0dHSwYcMGuT2fipM7d+5gy5Yt2LZtW47/jdasWRNDhw7FwIEDUalSJbXW8+7dOwQFBaFz585qvQ8RERFpj0ITnD5lZGT02eCUlpaGcuXKITY2Fjdu3EC9evXkjjs7OyMkJATBwcGoX78+AGDt2rUYO3bsZ+/7adhSZuTIkbhw4QIePXqU+zf0CQan4itr1OXMmTN48eKF3HS0P/74A5MnTxaxOnFlZGTg9OnT2Lx5M/bv34+UlBSl55UsWRI9e/bEH3/8IfzCRFVCQ0PRpUsXhIeH49SpU2jZsqVKr09ERETaqcguDhEQEIDY2FjY2toqhCYA6N27NwDg0KFDQt+YMWMgk8k++/Wl0AR83K/m06lWRLllZmaGdevW4fHjx7h+/ToqVKggHJsyZQp+/fXXIrtU+Zfo6emhQ4cO2LFjByIjI+Hj44OmTZsqnPfhwwds2LABEyZMUOn9MzMz0aNHDzx+/BgZGRno1asXXr9+rdJ7EBERUdGj1cHp9u3bAAAXFxelx7P6Q0JCVH7vwMBA2NjYqPy6VPzUrl0bly5dQtWqVYW+OXPmwMvLq9iGpywWFhb49ttvcenSJTx+/Bhz5swR9sLKsmvXLvz3338qu6eOjg42btwIIyMjAMD3338vPN9IRERElBM9sQv4nOfPnwMArK2tlR7P6o+IiCjQfVq1aoVevXrBwcEBKSkpOHDgALZv367wjEpOsob8PvXkyRPY2toWqDYqGqpXr47Lly+jXbt2ePjwIQBg+fLliI2Nxfr166GrqytyheKztbXF3Llz8erVK/z9999Cf0pKCho3boyTJ0/Czs5OJfdq1KgRtmzZAgDo06ePSq5JRERERZtWB6esvW9MTEyUHjc1NQUAxMfHF+g+zs7OWLFiBV68eAFTU1PUrFkThw4dQpcuXQp0XaLsrK2tcfHiRXTo0AE3btwAAPj6+iI+Ph7btm2DgYGByBVqh+XLl0MikWD//v2Ijo4GAISHh6NJkyY4evQoGjRooJL7MDARERFRXmj1VD1N+eOPPxAaGoqkpCRER0fjwoULeQpN9+7dU/rF0Sb6VNmyZXH27Fk0a9ZM6NuzZw+6d++OpKQkESvTHmZmZli/fj2ioqLg7e0t9EdHR6NVq1Y4deqU2u4dFhaGyZMno1+/fpgxYwbCwsLUdi8iIiIqXLQ6OGWtpJXTB8rExEQA4KaWVKhYWFjg+PHj6Nixo9B3/PhxtG/fHrGxsSJWpn2+++47bNu2DXp6HwfHExIS0LlzZ+zcuVPl99q4cSMcHBzw119/YdeuXVi8eDEcHR2xceNGld+LiIiICh+tDk5Zey29fPlS6fGs/uwP3RMVBiYmJti/f7/cdLHLly+jVatWwvQ0+mjgwIE4fPiwMDU3PT0d/fv3x19//aWye4SFheHbb79FZmamXL9UKsXIkSM58kRERETaHZycnZ0BQHge5FNZ/U5OThqriUhVDAwMsGPHDowYMULou3nzJpo3b57jLwuKq/bt2+Ps2bMoU6aM0Dd58mT89NNPKlmZcMOGDQqhKYtUKoWvry9SUlIwadIkbNu2DY8ePSr2KyISEREVN1odnJo0aQILCws8efIEt27dUjju5+cHAOjatauGKyNSDV1dXfj4+OD7778X+h4+fIimTZtylOMTDRs2xOXLl+VGmH/77TeMHDkSGRkZBbp2eHj4F4/fvn0bK1aswODBg+Hs7FzgexIREVHhotXBycDAQNj8cvz48cIzTQDg7e2NkJAQtGjRAvXr1xerRKICk0gkWLp0KX799VehLyIiAs7OzqhRowZGjx4trDBZ3Nnb2yMwMBB16tQR+jZs2IBevXohOTk539f90p5tNjY2uHbtmtB2cXFR2G9KKpWiVq1a+Oabb7Bs2TLExcXlux4iIiLSPhKZBuebHDlyBPPmzRPa165dg0wmQ6NGjYS+n3/+GZ07dxbaKSkpaNmyJa5evQorKys0a9YMERERuHr1KsqWLYugoCBUq1ZNU28hT7L2d7p3757IlVBh8ddff2Hy5MkK/aNGjcK6detEqEg7xcTEoFu3brh06ZLQ17RpUxw8eBAlS5bM8/XCwsLg6OgIqVSqcExXVxcPHjxAREQEtm3bhmvXrqFDhw5yK/4BwN27d4VAp6uri7i4OIWtFCIiImBtbc19u4iIiERSkM/nGh1xio6OxtWrV4WvrMyWve/TB+ONjIxw7tw5/Pzzz8ID9REREfD09MSNGze0NjQR5cekSZOwadMmhf4rV65ovhgtZmlpiRMnTqB79+5C3+XLl9G8eXO8evUqz9ezs7ODj4+P0kDTrVs32NnZoW3btti0aRPu37+PZcuWKZx39epV4fs6deoohCaZTIYGDRrAwsICLVu25FRMIiKiQkajI07FDUecKL++/vpruf2KOOKkXEZGBsaOHYu///5b6KtSpQpOnDgBBweHPF8vLCwMvr6+OHz4MO7evQsAKF++PCIiImBoaPjZ10ZHR+PixYu4du0aKlSogO+++07u+LNnz+R+0fPu3TuUKlVK7px9+/bBwsJCCFhERESkWgX5fM7gpEYMTpRfCQkJGDRoEJ48eYLGjRtj2bJlwr5mJE8mk2H27NmYP3++0Kerq4tu3bphy5Yt+fpzi46ORuXKlZGamgrg4x5Pnp6eBarz2LFj6Nq1K6RSKezs7PDo0SOFc6pWrYrnz58D+N/eXkRERKQ6DE5aisGJSHNWrFiBSZMmyfX1798f27dvz9f1Ro0aBR8fHwAfp97dvn0bEomkQDUmJSXh5s2biIuLk9sAGQAiIyNhZWUltF++fIlKlSrJnbNgwQJERUWhYcOGaNOmDSpUqFCgeoiIiIobBictxeBEpFnZR2yAj89Ivnv3TuF5o9y4f/++8N8wAJw+fRpt2rRRSZ3KhIaGYvLkybh27RqMjY2VPqvl6OiIhw8fAgC2bt2KQYMGyR3PyMiAnp6e2mokIiIq7ArN4hBEROrUoUMHuXZKSgq+/fbbfG1WW7NmTblRoU9X0VM1e3t7HD9+HO/evcP169cVjsfGxgqhCfi4r9WnJk2ahGrVqqFfv344efKkWuslIiIqbhiciKjIWLZsGUaNGoXSpUsLfTt27FC6Cl5uZN+Y+OjRo3jw4EGBa/wSiUSidAqeRCLBypUrMWTIEDRs2BB2dnYK51y7dg3Pnj3Drl278OzZM4Xj4eHhuHfvntJl14mIiOjzGJyIqMgwMzPDunXrEBkZidatWwv906dPx4kTJ/J8vTZt2shttvvHH3+oosx8KVGiBMaPH4/Nmzfj6tWrCs9bpaSk4Pbt20I7+/54WVatWoXatWujZMmScnvqERER0ZcxOBFRkaOnp4ddu3bBxsYGAJCZmYl+/frh8ePHebqORCKRW1Z8y5YtCnvNaQtDQ0M8efIEe/bswQ8//IDatWsrnHPt2jUAQHx8vNLVBq9fv45Fixbh7NmziIuLU3vNREREhQmDExEVSWXKlMH+/fthbGwMAIiJiYGHhwfi4+PzdJ0BAwagfPnyAD6O6qxdu1bltaqCRCJBlSpV0Lt3byxevFhhkQiZTAapVAodnY8/9pWNSB08eBA//vgj2rRpo7DwBBERUXHH4ERERZazszM2btwotO/du4ehQ4ciMzMz19cwNDTE+PHjhfaqVauE/Z0KE4lEgsuXLyMuLg4XL16Ei4uLwjlZI1KA8mB1+fJlNG7cGFOmTIG/v79a6yUiItI2DE5EVKT17dsXM2bMENr+/v5YsGBBnq4xZswYGBkZAQDevHmDHTt2qLRGTTI1NUWzZs2E95Nd69at0a5dO1hYWChdtS8wMBBXrlzBn3/+iVWrVikcz8jIwNu3b9VSNxERkdgYnIioyJs/f77c0uKzZ8/GwYMHc/36smXLYsiQIULb29s7X0uca7tp06bh5MmTeP/+vdziGlmyj0gpC1Y3b95E2bJlYWtri+HDh6u1ViIiIk1jcCKiIk9XVxfbt2+XW8J70KBBeVpefMqUKcL3d+7cwZkzZ1RZolbR0dGBrq6uQv+sWbPw119/YdCgQUo3A84KVk+fPlX6ZyuVSrF582Y8ePAgT9MliYiItAGDExEVC5aWlti/f7+wmlx8fDy6d++OmJiYXL3e0dFRoxviaqO6deti4sSJ2Lp1q9LglH0XdmUjUqGhofD09ETNmjVRpkwZpKWlqbVeIiIiVWJwIqJio2bNmti2bZvQDgsLw8CBA3O9IWz2DXGPHTumkQ1xC5NVq1YhPDwcu3fvlpvamOXq1avC95UrV4aBgYHccZlMBk9PTyxevBjnzp1jsCIiIq3C4ERExUr37t0xd+5coX306FHMnj07V6/Vpg1xtZFEIkHVqlXRp08f1K9fX+G4vr4+6tSpAx0dHaUjUs+fP8fmzZsxY8YMtG7dWunS8UXx2TIiIiocGJyIqNj5+eef4eHhIbR/++037Nmz54uvk0gkcqNO2rwhrjYaNGgQQkJCEBsbi/nz5ysczz4iZWtri9KlSyucU7t2bTRt2hTff/89Xrx4odZ6iYiIsmNwIqJiR0dHB1u2bEHNmjWFPk9PT4SEhHzxtf379y8UG+JqMzMzM+HPMDtHR0fMnDkTbdq0Qdu2bRWOR0VF4f79+wgICMDy5cuVXvvevXt4//69ymsmIiJicCKiYsnc3BwHDhyApaUlACApKQkeHh549+7dZ19naGiICRMmCO2VK1ciJSVFnaUWG3Xq1MGCBQtw+vRppYE0+3LoVlZWsLa2Vjjnm2++QenSpVGjRg1cunRJrfUSEVHxwuBERMVW9erVsWPHDujofPxR+OzZM/Tt2xcZGRmffV32DXGjoqIK9Ya4hUnbtm2FDXinTp0KiUQidzwuLk5YsCMsLAxly5ZVuMbu3buxZcsWPHz4kEuiExFRnjA4EVGx1qFDByxcuFBonzlzBlZWVhg9ejQSEhKUvqZMmTJyq8YtX76cixZogJGREdzc3DBp0iS5Z82yhIeHC89FWVhYoEaNGgrnLF26FEOHDoWjoyN8fHzUXjMRERUdDE5EVOxNmzYN/fr1E9pv377F+vXr4eXlleNritOGuIWFk5MToqKi8PTpU/j7+wsjiVlSU1Nx69Ytoe3q6qpwjSVLlqB37974/fff5falIiIiYnAiomJPIpFgw4YNwvS7LJcvX87xNY6OjujUqZPQLo4b4mojiUSCr776Cq1atVI4Fh8fj8GDB6N27dowNTWVW1o+y9GjR7F3715Mnz4dJ06cUDiekJCgMJUzLCwMM2bMQL9+/TBjxgyEhYWp7g0REZHWYHAiIgJgYmKCHj16yPU1bdr0s6/5dEPc+/fvq6U2Uo0yZcpgw4YNuHPnDqKjo6Gvry93XCqVIjg4WGgr22vK29sbJUqUQLNmzeDr64uNGzfC0dERixcvxq5du7B48WI4Ojpi48aNan8/RESkWQxORET/b/369ejevTvs7e0xatQoLFu27LPnt27dGk5OTkKbG+IWHsbGxgp9mZmZ2Lp1K2bMmIE2bdqgXr16CudcvXoVycnJuHz5MkJCQjBy5EhIpVK5c6RSKUaOHMmRJyKiIkYi4xPNalOrVi0A4Dx5oiJs8+bN8PT0BPBxqfIXL14oXc2NCj+ZTIZKlSrhv//+A/Bx6fPdu3fneP6MGTPkFh4hIiLxFeTzOUeciIgKoF+/fqhQoQKAj4sPrFmzRuSKSF0kEgmePHmCwMBALF++HOnp6Z89Pzw8XDOFERGRRjA4EREVgKGhIcaPHy+0V61axQ1xizBjY2O4u7tjypQpSpc7z87GxkYzRRERkUYwOBERFRA3xC2eRowYAV1dXaXHJBIJhg8fruGKiIhInRiciIgKqEyZMhg6dKjQHjt2LEaNGpXjBrpUNNjZ2cHHx0dhvyjg40a7dnZ2IlRFRETqwuBERKQC2TfETU1NhY+PD0aMGCFeQaQRw4YNw9atW4W2np4eHjx4ILdUPRERFQ0MTkREKuDg4IASJUrI9e3evRs///wzkpOTRaqKNKFNmzbC9xkZGVxVkYioiGJwIiJSkU830AWA+fPno06dOjh58qQIFZEmlCtXTi40P3r0SMRqiIhIXRiciIhUZOXKlRg1ahSqVq0KU1NTof/Jkydo3749+vXrJ+wBREWHRCKBvb290M4enJ4/f44uXbrgyZMnYpRGREQqxOBERKQiZmZmWLduHcLDwxEZGYmpU6fKrbq2a9cuODg4YPXq1ZBKpSJWSqqWfWnyrOC0b98+ODk54ciRIxg4cOAX930iIiLtxuBERKQGZmZmWLJkCW7cuAE3NzehPy4uDuPHj4e7uztu3rwpYoWkSsqCk1QqRWxsLADg6tWr8Pf3F6U2IiJSDQYnIiI1cnJyQkBAANauXQtLS0uh/99//0WDBg3w3XffIT4+XrwCSSWUBac+ffrA09MT5ubm2Lp1K/r06SNWeUREpAIMTkREaqajo4PRo0fj4cOHGDhwoNCfmZmJP/74A46Ojti3bx9kMpmIVVJBZA9OYWFhyMzMBAD89ddfuH37NgYNGgSJRCJWeUREpAIMTkREGlK+fHls27YNp0+fltsc9dWrV+jVqxe6deuG8PBw8QqkfMv+95mcnIxXr14BAMzNzfHVV1+JVRYREakQgxMRkYa1adMGISEhmDt3LgwMDIT+w4cPo3r16ihTpgw6deqEt2/filgl5YW5uTmsrKyE9peWJE9MTFR3SUREpGIMTkREIjAyMsKcOXNw584duQ1UpVIp3r17h2PHjsHKygoeHh7w8fERRjBIeyl7zulTmZmZ8Pb2xldffcXRRSKiQobBiYhIRDVq1MCpU6ewbds2uaXLASAjIwMHDhzAqFGjYG1tDRcXF/z8888ICgricuZaKDfB6ZtvvoGXlxeio6MxZMgQ/j0SERUiDE5ERCKTSCQYOHAgBg8e/Nnzbt68ifnz58Pd3R0VKlTA0KFDsXv3bsTExGimUPqs3ASnHj16CN8/efKEo05ERIWIntgFEBHRRytWrICBgQGuXLmChg0bokuXLjh79iwOHz6MZ8+eyZ379u1bbNmyBVu2bIGenh7c3NyQnp6ODx8+oEWLFvD29oaZmZlI76R4yk1wGjhwII4cOYKUlBT4+PigdOnSmiqPiIgKSCLj+rdqU6tWLQDAvXv3RK6EiAozmUyGhw8f4siRIzhy5AguXbr0xSleVlZW6N+/P+rXr4/69evDzs4OOjqcZKBODx8+hKOjIwBAV1cXSUlJcot/ZElNTYWBgQGXJyciEkFBPp8zOKkRgxMRqUNMTAxOnjyJw4cP49ixY7lafc/MzAz16tUTglT9+vVRo0YNheeqKP/S0tJgbGws7OH08OFD2Nvbi1wVERFlx+CkpRiciEjdpFIp/v33X4wbNw43b97M02tNTU1Rt25duTDl4ODAMFUA1atXx5MnTwAABw8eRNeuXXP1utTUVDx48AB169ZVY3VERFSQz+d8xomIqBDT1dWFm5sbLl68CC8vLwQEBKBatWpo0qQJ7t69i+vXr+Phw4dQ9juyxMREBAQEICAgQO569vb2WLVqFVq0aMHpZHlUo0YNITh9aS+nLPfu3cOAAQPw4sULhISEwNraWp0lEhFRPjE4EREVAWZmZli3bp3SYwkJCbh16xauX78ufD18+FCYUpadVCrF/fv30apVKzg4OGDIkCEYPHgwP8znUo0aNXDs2DEAuQtOSUlJaNmypTDd0tPTEydPnuTzaEREWog/mYmIijgzMzM0bdoUkydPxpYtW3Dv3j3ExcUhICAAf/31F4YOHQpDQ0OF1z18+BAzZ85ElSpV0K5dO2zduhWJiYkivIPCIzcr62VnYmKCxYsXC21HR0dkZGSopTYiIioYPuOkRnzGiYgKi9GjR2P9+vVfPM/MzAy9e/fG0KFD0bx5c46MfOL06dNo164dAKBixYp49erVF18jk8kwbtw4dO3aFZ06dVJ3iURExRoXh9BSDE5EVFgkJCTAy8sLV65cgbu7OyZOnIh9+/Zhy5YtwjM7n6patSoGDx6MIUOGwM7OTsMVa6fnz5+jatWqQjs+Pp77aRERaREGJy3F4EREhZ1MJkNgYCA2b96MXbt2IS4uTul5DRs2hKGhId6/f48mTZpg2bJlxTIwZGZmwtTUFCkpKQCAGzduoF69eiJXRUREWQry+ZxzLIiIKEcSiQRNmjTB+vXrERkZiR07dqBjx44KU/SuXbuGS5cu4d69e1i/fj28vLxEqlhcOjo6cqNvoaGh+b7WhQsXcOLECVWURUREKsDgREREuWJsbIx+/frh6NGjePnyJZYsWYLatWsrPffSpUsark575HWBiE+lpaVh5syZaNWqFQYNGoTIyEhVlkdERPnE4ERERHlmZWWFqVOnIiQkBDdu3BCmPmR58+YNYmJixClOZAUNThEREfjjjz8gk8nw9u1bjBo1SpXlERFRPjE4ERFRvkkkEtSrVw9BQUFwd3cX+t+/f4/u3bsLz/oUJwUNTnZ2dli2bJnw/ezZs1VWGxER5R+DExERFZiZmRkCAwMxffp0oe/ixYsYMGAApFKpiJVp3qfBKT9rMI0ZMwYrV67EzZs30aBBA1WWR0RE+cTgREREKrNw4UIMGzZMaPv7+2PcuHH5Cg+FVfbgFBsbi+jo6DxfQyKRYPz48TA1NVVlaUREVAAMTkREpDISiQTr169Hly5dhL7169dj7ty54hWlYaVLl0bJkiWFdn6m6xERkfZhcCIiIpXS09PDrl270LhxY6Hv119/xerVq0WsSnMkEgns7e2FtqqCU1xcHDw9PblEORGRSBiciIhI5UxMTHDo0CHUrFlT6JswYQL27NkjYlWaU9AFIj4VHByMunXrYvPmzfD09MzX9D8iIioYBiciIlKLUqVK4cSJE6hcuTIAQCaTYdCgQTh79qzIlamfqoOToaEhXr9+DQCIjIyEt7d3ga9JRER5w+BERERqY21tjRMnTqBUqVIAPm7u6uHhgZs3b4pcmXqpOjjVqVMHixcvho6ODubOnYt58+YV+JpERJQ3DE5ERKRWjo6OOHLkCIyNjQEA8fHx6NChA548eSJyZeqTPTg9fvxYJUuyT5w4Ebdu3cKcOXOgp6dX4OsREVHeMDgREZHaubm5wc/PD7q6ugCAqKgofP3114iMjBS5MvWoXr268H1qaipevHhR4Gvq6OigTp06Bb4OERHlD4MTERFpRKdOneDr6yu0nz59io4dOyIuLk7EqtTD1NQU1tbWQludS5InJiaq7dpERPQ/DE5ERKQxQ4YMwZIlS4T2rVu34OHhgdTUVBGrUg9VP+ekzP79+/HVV1/h3Llzark+ERH9D4MTgE2bNkEikSh8nT9/XuzSiIiKnKlTp8LLy0tonzt3DoMGDVLJc0DaRN3Baf78+ejRoweio6MxePBgvH//XuX3ICKi/2Fwyuby5cu4cuWK8OXi4iJ2SURERdLvv/+OQYMGCW0/Pz9MmjQJMplMxKpUS93BqVOnTtDX1wcAfPjwATdu3FD5PYiI6H+4LE82jRo14kpFREQaoKOjA19fX7x79w7Hjh0DAKxevRrXrl3DuXPnYGZmJnKFBafu4OTi4oIFCxZg165d2L59u9z9iIhI9TjiREREotDX18eePXtQtmxZoS84OBhDhw4VsSrVyR5kwsPD1fIcl5eXFwIDAxmaiIg0QKPB6fr161i0aBF69uwJa2tr4VmiL0lOTsbs2bNRo0YNGBkZoWLFihg+fDhevXql0voqVaoEPT09ODk5wc/PT6XXJiIiRaampnLBCQCOHTtWJJ53srGxEWYxyGQytexbpaOjAwMDA5Vfl4iIFGk0OM2bNw8//vgj/P39cx16UlJS0Lp1a8ybNw8JCQno3r07KleujI0bN6JevXp4+vRpgeuysrLCggULsH37duzfvx+2trbo06cPDhw4UOBrExHR5zVt2lSunZycjDVr1ohUjero6+ujWrVqQludS5JnJ5PJcO3aNY3ci4ioONHoAz3u7u5wcnKCq6srXF1dYWNj88WpC/Pnz0dQUBDc3d1x8uRJYd67t7c3vLy8MHz4cLnV72JiYr64oaKJiQmqVKkitNu3b4/27dsL7S5duqBZs2b47bff0L1793y8UyIiyq1ly5YBAHbv3o2YmBgAwMyZM9GzZ09UrFhRxMoKrkaNGkJg0kRwevPmDUaMGIFjx47hwoULCqGUiIjyTyITcQkjIyMjpKam5riKUlpaGsqVK4fY2FjcuHED9erVkzvu7OyMkJAQBAcHo379+gCAtWvXYuzYsZ+9b4sWLb641PjSpUvx008/FWhOeq1atQAA9+7dy/c1iIiKizdv3sDBwUEIT9988w127dolblEF5OXlBW9vbwDAiBEj8Pfff6v1fk2bNkVAQAAAoGrVqrh9+zYsLCzUek8iosKkIJ/PtXpxiICAAMTGxsLW1lYhNAFA7969AQCHDh0S+saMGQOZTPbZL+7PRESkfcqXL49FixYJ7d27d+P48eMiVlRwmtgENztvb2/o6uoCAGrWrIm0tDS135OIqLjQ6uB0+/ZtAMhxP6Ws/pCQEJXeVyaTwd/fX2lYIyIi9Rk5ciTc3d2F9rhx45CUlCRiRQWj6eDUsGFDLFq0CCtWrMCRI0cUFt4gIqL80+pNi54/fw4AsLa2Vno8qz8iIqJA9+nduzcaNmwIJycnpKam4u+//8aVK1dw8ODBXL0+a8jvU0+ePIGtrW2BaiMiKk50dHSwdu1auLi4QCqV4tmzZ5g/fz5+++03sUvLl+zB6c2bN4iNjVX71LmpU6eq9fpERMWVVo84JSQkAPi4mIMypqamAID4+PgC3adGjRr4+++/0bNnT/Tv3x/v37/H4cOH0aVLlwJdl4iI8s7JyQnff/+90F6yZEmhfVa0YsWKcv+GhYWFiVgNEREVhFYHJ0357bff8OjRIyQlJSEpKQkBAQHo1KlTrl9/7949pV8cbSIiyp85c+agatWqAICMjAyMGTMGmZmZIleVdxKJROPT9ZQJCwvD5s2bRbk3EVFRodXBKWvp8ZzmtycmJgIAzM3NNVYTERGpn6mpKVauXCm0L1++jE2bNolXUAGIGZxkMhn+/vtv1K1bFyNGjOD+TkREBaDVwSlrr6WXL18qPZ7Vn/VbSSIiKjq6dOmCnj17Cu1p06YhOjpaxIryR8zglJiYiPnz5yMpKQlSqRQDBgwo0DYbRETFmVYHJ2dnZwDAjRs3lB7P6ndyctJYTUREpDl//vmnMPvg/fv3mDZtmsgV5Z2YwcnMzAzbtm2Djo4OSpUqhSVLlsDQ0FCjNRARFRVaHZyaNGkCCwsLPHnyBLdu3VI47ufnBwDo2rWrhisjIiJNsLa2xrx584T25s2bC91efJ8GJ03vO9+0aVNs3rwZd+7cQY8ePTR6byKiokSrg5OBgQEmTJgAABg/frzwTBPwcZO/kJAQtGjRAvXr1xerRCIiUrMJEybI7as3ZsyYQjXdzM7OTvg+Pj4eb9680XgNgwYNQsWKFTV+XyKiokSjwenIkSNwc3MTvrJ2NM/ed+TIEbnXzJo1C40aNUJgYCDs7OzQt29fuLm5wcvLC2XLloWvr68m3wIREWmYnp4e1q1bB4lEAgAIDQ3F77//LnJVuVeqVCmUKVNGaIu1sh4RERWMRoNTdHQ0rl69KnxlTVfI3vfpg79GRkY4d+4cfv75Z5iYmGD//v2IiIiAp6cnbty4gWrVqmnyLRARkQhcXV0xfvx4ob1gwQI8fvxYxIryxt7eXvheG4JTRkYGfv31V3h7e4tdChFRoSGRaXqydTFSq1YtACi0GzcSEWmT2NhYODo64r///gMAtGvXDidOnBBGorTZ8OHDsXHjRgAfVwcUc8Ts5cuX6Nu3LwIDA6Gvr4+goCC4uLiIVg8RkSYV5PO5Vj/jRERElMXCwgJ//vmn0D516hR27twpYkW5pw2b4GYxMTHB8+fPAQDp6en46aefRK2HiKiwYHAiIqJCo3fv3ujYsaPQ/u677xATEyNeQbmUPTiFhoaKWMnHZ662bNkCiUSCIUOGYNeuXaLWQ0RUWDA4ERFRoSGRSLBq1SoYGRkBAN68eYMff/xR5Kq+LHtwevLkCTIyMkSsBmjVqhVu3bqFzZs3o0SJEqLWQkRUWDA4ERFRofLVV19h9uzZQnvdunUICgoSsaIvs7W1FZ7FSk9PR0REhMgVcfN4IqK8YnAiIqJCx8vLCzVr1gQAyGQyjB49WvRRnM8xNjZGlSpVhLbYzznlJCkpCZmZmWKXQUSklRiciIio0DEwMMC6deuEdkhIiNzCEdpImxaIUOb69euoV68eVq5cKXYpRERaicGJiIgKpaZNm2LEiBFCe9q0aRg4cCASEhJErCpn2hyc/P394ebmhkePHuGHH37AnTt3xC6JiEjrMDgREVGhtXjxYmGhCJlMhu3bt2PSpEkiV6WcNgenpk2bokyZMgCAtLQ0XLhwQeSKiIi0D4MTEREVWqVLlxY+8Gfx8/NDenq6SBXlTJuDU9myZbFp0yZUqVIFZ8+exYQJE8QuiYhI6zA4ERFRoZZ9XycAiI+Px/Dhw7VukYPswen58+dITk4WsRpF7du3x6NHj9CyZUuxSyEi0koMTkREVKh5e3tj5MiRcvsRbdu2DdOmTYNMJhOxMnlVq1aFvr6+0H78+LGI1ShnaGgodglERFqLwYmIiAo1MzMzrF+/Hm/evEGrVq2Efm9vbyxZskTEyuTp6uqievXqQlvbpuvl5Nq1a1oVQImIxMLgRERERYKRkRH279+PevXqCX3Tp0/Hxo0bRaxKnjY/5/Sp5ORkTJo0CY0aNZJb+p2IqLhicCIioiKjRIkSOHbsGGxtbYW+kSNH4uDBgyJW9T+FKThNmzYNK1asAAB8//33ePDggcgVERGJi8GJiIiKlPLly+PkyZOoUKECAEAqlaJv3764dOmSyJUVruA0a9YsYcVCOzs7kashIhIfgxMRERU51apVw/Hjx4UFI1JSUtC1a1eEhISIWldhCk4VKlSAr68vpkyZgqtXr8LR0VHskoiIRCWR8YlPtalVqxYA4N69eyJXQkRUPF28eBFff/01UlNTAQBWVlYICAjAV199JUo9kZGRsLKyEtrv3r1DqVKlRKmFiKg4Ksjnc444ERFRkdW8eXPs3LkTOjof/7n777//8PXXXyMqKkqUesqXLw9zc3OhHRYWJkodRESUdwxORERUpHl4eMitCvf48WN07NgRcXFxGq9FIpEUqul6yrx79w7Lly/nEuVEVOwwOBERUZH37bff4rfffhPaN27cQI8ePYQpfJpUmIPT2bNn4ezsjO+//16rlnknItIEBiciIioWZsyYgcmTJwvts2fPYtCgQZBKpRqto7AGJ5lMhkWLFuHVq1cAgEmTJiEiIkLkqoiINIfBiYiIigWJRAJvb28MGDBA6PPz88PEiRM1Ou2ssAYniUSCjRs3omTJktDX18cvv/yCypUri10WEZHG6IldABERkabo6Ohg48aNePfuHU6cOAEAWLNmDcqXL485c+ZopIZPg5NMJoNEItHIvQuqUqVK2LFjB8qXL4+6deuKXQ4RkUZxxImIiIoVAwMD7N27F40aNRL65s6di0qVKmH06NFISEhQ6/2zbyablJSE169fq/V+qta+fXuGJiIqlhiciIio2DE1NcWRI0fkNnV9/fo11q9fDy8vL7Xe28LCAuXLlxfahWm6HhFRccbgRERExVLp0qVx4sQJ6Ovry/VfvnxZ7fe2t7cXvi8Kwemff/7BmDFjxC6DiEitGJyIiKjYqly5Mrp16ybXl5SUpPbFIgrrAhGfSkxMxMCBAzFo0CCsW7cO//zzj9glERGpDYMTEREVa5s2bYKbm5vQDg8Px+bNm9V6z6ISnIyMjPDixQuhPXPmTKSlpYlYERGR+jA4ERFRsWZmZoaAgAC0adNG6Js0aRKePXumtnsWleCkq6uLrVu3okSJEmjWrBkuXrwIAwMDscsiIlILBiciIir2dHR0sGnTJlhaWgIA4uPjMXjwYLVtjps9OD19+hTp6elquY8mVK1aFQEBATh37hyqVq0qdjlERGrD4ERERATA2toaa9euFdoBAQFYvHixWu5VrVo16Oh8/Cc4IyMD4eHharmPptSuXRu6urpil0FEpFYMTkRERP+vb9++GDhwoNCeM2cOrl+/rvL7GBoawsbGRmgX5ul6OZFKpUhOTha7DCIilWFwIiIiymblypWoXLkygI+jQYMGDUJSUpLK71NUnnNS5uXLl2jbti3GjRsndilERCrD4ERERJSNpaUltmzZAolEAgB4+PAhpk+frvL7FNXgdPfuXTg5OeH8+fPYtGkTdu/eLXZJREQqweBERET0iZYtW8LLy0tor1y5EsePH1fpPYpqcHJwcJDb4PfMmTMiVkNEpDoMTkRERErMnz8fTk5OQnvYsGF4+/atyq5fVIOTnp4etm3bBisrK2zatEluwQ0iosKMwYmIiEgJQ0ND/PPPPzA0NAQAREZGYvTo0ZDJZCq5fvbg9PLlSyQmJqrkutrA1tYWT58+xdChQ4Upj0REhR2DExERUQ5q166NhQsXCu19+/Zh8+bNKrl25cqVhVAGAI8fP1bJdbWFkZGR2CUQEakUgxMREdFnTJ48GW3atBHakyZNwrNnzwp8XR0dHdjZ2QntojRdLyd3794tUiNrRFS8MDgRERF9ho6ODjZt2gRLS0sAQHx8PAYPHgypVFrga2efrhcaGlrg62mrzMxM/Pnnn2jQoIHcohtERIUJgxMREdEXWFtbyy1yEBAQgMWLFxf4ukV1gYhPbdy4EVOmTEFqairWrVuHAwcOiF0SEVGeMTgRERHlQt++fTFw4EChPWfOHFy/fr1A16xSpYrw/e7duzFs2DCsXr0aAQEBiI+PL9C1tcngwYPh4uICAKhQoQLMzMxEroiIKO8kMlUtD0QKatWqBQC4d++eyJUQEZEqxMTEwMnJCS9evADwcc+i69evw8TEJF/X8/Dw+Ozoi62tLZydnVG3bl3hfytXrlwoV6oLDQ3FnDlzsHLlSpQpU0bscoiomCrI53MGJzVicCIiKnrOnz+P1q1bC8uST5gwAStWrMjXtZycnHDnzp08vaZkyZJwdnaWC1SOjo5cxY6IKBcYnLQUgxMRUdH0ww8/YMmSJUL72LFj6NChQ56vM3r0aKxfv15o16xZEyVLlkRISEiep+rp6+ujUqVKKFeuHMqWLYsyZcqgbNmyOX5fokSJQjlyRURUEAxOWorBiYioaEpNTUWjRo1w+/ZtAB+f27lz506ep6AlJCTAy8sLV65cgbu7O5YtWwYzMzNkZmbi2bNnuH37Nm7duiX87/Pnz1X2HvT19VGqVCmkp6cjLS0NFSpUwNdffw1ra2uUL18e5cqVE77Kly8PY2Njld07S2pqKv766y9MmDBBLdcnIvoUg5OWYnAiIiq67t69iwYNGiA1NRUAUKJECfTt2xfe3t5qW/zgw4cPuH37tvC1Y8cOpKSkqOVenzIzM1MIU5aWlggICMDr16/RtGlTrF69Gubm5rm63sOHDzFgwADcvHkTEydOxF9//aXmd0BExOCktRiciIiKtuXLl+P777+X6xs1ahTWrVunkft/OtWvU6dOGDhwIN6+fYvo6GjhK3v73bt3UNc//YaGhmjcuDHq1KkDJycn1KlTB7Vq1YKpqanCuZ9Odzx9+rTcRsNEROrA4KSlGJyIiIq2zMxMWFhYICEhQeirU6cOQkJCNHL/nKb6fY5UKsWHDx/w9u1b/PDDDzh06JBwrGbNmqhVqxaioqLw5s0bREVF4f379wWqUSKRoHr16nJhysnJCRUrVoS7uztCQkIwduxYLF26NN+rExIR5VZBPp/rqboYIiKi4kJHRwfdu3fHP//8I/S5u7tr7P5mZmZ5Ht3S1dVFmTJlUKZMGWzfvv2LwSs9PR1v374VglTW15s3b7B///4vbtwrk8kQFhaGsLAw7Nu3T+g3NjaGkZERzM3NER8fj8zMzDy9DyIiTeOIkxpxxImIqOjLz6hPUZH9vdetWxd9+/bF48ePERISgjt37uDu3btITk7O1bUGDx6MLVu2qLliIiruOFVPSzE4ERFRcSaVSvHkyRPcuXNHCFMhISF48uSJwrkmJiZ4//49DA0NRaiUiIqLgnw+11F1MURERETAx2mBNWrUQK9evfDLL79g3759ePz4MeLj4+Hh4SF3blJSEkaOHImLFy/Cw8NDWK2QiEhbMDgRERGRRpmZmWHr1q0YOXIkSpYsKfRv3boVLVu2xIEDBzBr1iwRKyQiUsTgRERERBpnZmaG9evXIzIyEi1atBD6s54g+PPPP1W64S8RUUExOBEREZFoDAwMsHfvXlSvXl3ok0gk8PHxQZUqVUSsjIhIHoMTERERiap06dI4dOgQLCwsAHwcdZo5cyZevXolcmVERP/D4ERERESic3BwgJ+fH3R1dQEAr1+/Rrdu3ZCYmChyZUREHzE4ERERkVZo27YtVq5cKbRv3LiBIUOGIDMzE0lJSSJWRkTE4ERERERaZMyYMZg8ebLQ3rdvH+rVq4eWLVsiPT1dxMqIqLhjcCIiIiKtsmzZMnTs2FFoh4SE4N9//8Uvv/wiYlVEVNwxOBEREZFW0dXVxc6dO1GrVi25/gMHDnDUiYhEw+BEREREWqdEiRI4fPgwypQpI/T9999/ePHihYhVEVFxxuBEREREWsnGxgb79++Hvr4+AODdu3fo0qULYmNjRa6MiIojBiciIiLSWk2aNIGvr6/QfvDgAfr27YuMjAwRqyKi4ojBiYiIiLTaoEGD8NNPPwntEydOYPTo0Xj+/LmIVRFRccPgRERERFrv119/Re/evYW2r68vnJyc0LdvX8yYMQNhYWEiVkdExQGDE4CWLVtCIpEo/frvv//ELo+IiKjY09HRwebNm+Ho6Cj0xcbGYvfu3Vi8eDEcHR2xceNGESskoqJOIpPJZGIXIbb79+8jLi5Orm/ChAlIT0/H7du3833drGVU7927V6D6iIiI6KPLly+jWbNmSo/p6uriwYMHsLOz03BVRFRYFOTzuZ6qiymMatasKdf+8OEDQkJCMHfuXHEKIiIiIqUOHz6c4zGpVApfX18sXLhQgxURUXHBqXpK+Pv7Iz09HX379hW7FCIiIsomPDy8QMeJiPJLo8Hp+vXrWLRoEXr27Alra2vhOaIvSU5OxuzZs1GjRg0YGRmhYsWKGD58OF69eqWWOnft2oX69evD1tZWLdcnIiKi/LGxsfns8WfPnkEqlWqmGCIqVjT6jJOHhwcOHDig0P+5ElJSUtCqVSsEBQXBysoKzZo1Q3h4OK5du4ayZcsiKCgI1apVU1mNb9++hZWVFX777TdMmzatQNfiM05ERESqFRYWBkdHx8+Go2bNmuHEiRMwNjbWYGVEVBgUmmec3N3d4eTkBFdXV7i6usLGxgapqamffc38+fMRFBQEd3d3nDx5EmZmZgAAb29veHl5Yfjw4Th//rxwfkxMDCIjIz97TRMTE1SpUkXpsb179yIjIwPffPNN3t4cERERqZ2dnR18fHwwcuTIHMPTpUuXMGrUKGzdulXD1RFRUSbqqnpGRkZITU3NccQpLS0N5cqVQ2xsLG7cuIF69erJHXd2dkZISAiCg4NRv359AMDatWsxduzYz963RYsWcmEruzZt2iApKQlXrlzJ+xv6BEeciIiI1CMsLAy+vr4IDw+HjY0NqlSpggkTJiAzMxMAYGZmhpMnT8Ld3V3kSolImxSaEae8CggIQGxsLGxtbRVCEwD07t0bISEhOHTokBCcxowZgzFjxuTrfm/evMGFCxewdOnSAtVNRERE6mVnZ6ewel6VKlXQvXt3SKVSJCQkoH379jh58iTc3NxEqpKIihKtDk5Zeyi5uLgoPZ7VHxISopL7+fn5ITMzE3369MnT67KS66eePHnCBSaIiIg0pHPnzjh06BA8PDyQlpaG+Ph4ITzVq1cPBgYGYpdIRIWYVi9H/vz5cwCAtbW10uNZ/RERESq5365du9C0aVNUqlRJJdcjIiIizerYsSP8/f2FkBQXF4e2bdvC3t4emzZtErc4IirUtHrEKSEhAcDHxRyUMTU1BQDEx8cX+F6vX7/G5cuXsWLFijy/Nqc5kjmNRBEREZH6dOrUCXv37kXPnj2Rnp6OhIQEJCQkYNiwYXj58iVmzZoldolEVAhpdXDSpIoVKwoPlBIREVHh1qVLFyE8ZWRkCP1v3rwRsSoiKsy0eqpe1tLjSUlJSo8nJiYCAMzNzTVWExERERUOXbt2xd69e6Gn97/fE2/duhXXr18XsSoiKqy0Ojhl7bX08uVLpcez+qtWraqxmoiIiKjw6NatG/z8/KCrqwsAiI2NRbt27XDjxg2RKyOiwkarg5OzszMA5PjDLavfyclJYzURERFR4dK9e3fs2bNHGHn68OED2rZti5s3b+LYsWN48uSJyBUSUWGg1cGpSZMmsLCwwJMnT3Dr1i2F435+fgA+DsUTERER5aRHjx7YtWuXXHhq1KgROnfujDp16uDChQsiV0hE2k6rg5OBgQEmTJgAABg/frzwTBMAeHt7IyQkBC1atBA2vyUiIiLKSc+ePbFz505h2l56ejpkMhmSk5PRvn17REdHi1whEWkziUwmk2nqZkeOHMG8efOE9rVr1yCTydCoUSOh7+eff0bnzp2FdkpKClq2bImrV6/CysoKzZo1Q0REBK5evYqyZcsiKCgI1apV09RbyJOs5chzWq6ciIiING/Pnj345ptvFPoNDQ3RqVMn9OvXD507dxa2PSGioqMgn881OuIUHR2Nq1evCl9ZmS1736e/7TEyMsK5c+fw888/w8TEBPv370dERAQ8PT1x48YNrQ1NREREpJ369OmDr7/+WqE/NTUV/v7+6Nu3L8qVK4f+/fvjwIEDSE1NFaFKItI2Gh1xKm444kRERKSdEhIS8P333+P06dMwNDTEmzdv8OHDB6XnlihRAj169EC/fv3Qpk0b6Ovra7haIlKVgnw+Z3BSIwYnIiKiwiE9PR2nT5/Gzp074e/vj/j4eKXnlS5dGr169UK/fv3QvHlz4XkpIiocGJy0FIMTERFR4bNo0SL8+OOPAAAdHR1kZmYqPU9PTw9ly5aFm5sbKlWqhHLlyqFcuXIoX7683PdmZmaQSCSafAtElIOCfD7X+/IpRERERMWDTCZDcHCw0HZ2dsbkyZNx4MABHD16VO55p4yMDPz333/w9/f/7DWNjIzkAlXJkiVx9+5dREVFoVmzZvDx8YG5ubna3hMRqQZHnNSII05ERESFj1QqxeTJk3H06FFcuXIF5cuXBwDExsbiwIEDmDBhQo5T+fKjRIkSGDp0KDp37owWLVrAyMhIZdcmInmcqqelGJyIiIgKJ5lMhvfv36N06dIKx0aPHo3169cLbVdXVzRu3BhRUVF48+YNoqKiEBUVhbdv3+Y4zS8nJiYmaN26NTp37oxOnTqhSpUqBX4vRPQ/nKpHREREpEISiURpaHr79i3c3NwAAFeuXIG7uzuWLVsGMzMzhXOlUinevXsnBKmsULVp0ybcunVL6X2TkpJw+PBhHD58GABQu3ZtdOrUCZ06dULjxo25oh+RiDjipEYccSIiIio6kpKS0KZNGwQFBWH27NmYO3duvhZ9SEhIgJeXF65cuQInJyc0atQIZ86cwenTp5GYmJjj6ywsLPD111+jc+fO6NChgzCFkIhyj1P1tBSDExERUdExatQo+Pj4CO2DBw+ia9euKrt+amoqLl68iKNHj+LIkSMICwv77PlmZmbo3r071q5dq3TEi4gUMThpKQYnIiKiouP58+fo0KEDHjx4gNGjR2PNmjVqXWY8LCwMx44dw5EjR3D+/HmkpaUpPW/IkCHYvHmz2uogKkoYnLQUgxMREVHR8uHDByxfvhyzZ8+Gnp7mHhVPTEzEmTNncPToUfj6+iI9PV04ZmxsjMjISJQoUUJj9RAVVgxOWorBiYiIqPhIS0uDgYGB2u/z6ZRBAGjZsiWOHj0KY2Njtd+fqDAryOdzHVUXQ0RERFTcBAUFoXr16ggKClL7vby9vTFy5Ei5Vf/Onz+Pvn37yo1EEZFqMTgRERERFcCjR4/QpUsXvHjxAq1bt8ahQ4fUej8zMzOsX78eUVFRGDJkiNB/6NAheHp65nnvKCLKHQYnIiIiogK4f/8+4uLiAHzcu0lTK9zp6Ohgw4YN8PDwEPq2b9+OiRMngk9iEKkegxMRERFRAXh4eOD48eOwtLTE5s2b0apVK43dW09PDzt27EDr1q2FvtWrV+Pnn3/WWA1ExQWDExEREVEBtW7dGk+ePEG/fv00fm8jIyPs378fjRo1EvoWLFiApUuXarwWoqKMwYmIiIhIBUqVKqW039fXF/Hx8Wq9t7m5OY4ePYratWsLfdOmTVNYfY+I8o/BiYiIiEhN/vzzT4wYMQItW7ZEZGSkWu9VqlQpnDx5EtWqVRP6Ro8ejd27d6v1vkTFBYMTERERkRoEBgbiu+++AwDcuHEDY8aMUfs9rayscPr0aVSsWBEAIJPJMGjQIBw/flzt9yYq6hiciIiIiNTAzc0NU6ZMAQBUqVIFq1ev1sh9v/rqK5w8eVKYOpieno6ePXvi8uXLGrk/UVHF4ERERESkBjo6OvD29saKFStw/PhxYRRIE2rVqoXjx48LS6MnJyejc+fOuHnzpsZqICpqGJyIiIiI1GjChAlwdHRU6E9NTVXrfV1dXXHo0CEYGhoCAOLi4tC+fXuEhoaq9b5ERRWDExEREZGGJScno02bNvjxxx/Vullty5YtsWfPHujq6gIAoqOj0a5dOzx//lxt9yQqqhiciIiIiDRIKpVi0KBBCAgIwKJFizBkyBBkZGSo7X5du3bF5s2bIZFIAAAvXryAk5MTatasidGjRyMhIUFt9yYqSvTELoCIiIioOImJiUFYWJjQNjIyEkaE1GXgwIGIjY3F+PHjAQCxsbGIjY3FgwcPAADr1q1T6/2JigIGJyIiIiINKl26NC5dugQPDw+YmJhgzZo1wmiQOo0bNw4xMTH46aef5PqvXLmi9nsTFQWcqkdERESkYRYWFjh+/Dh2794NPT3N/R77xx9/RI0aNeT6GjVqpLH7ExVmDE5EREREIjA0NISpqalC/7///ouAgAC13FMikWDv3r1yI1yurq5quRdRUcPgRERERKQlHj9+jM6dO6Nt27bw9/dXyz1q166NIUOGCG1vb29IpVK13IuoKGFwIiIiItICUqkUPXr0QHR0NFJSUtC/f3+8fv1aLff68ccfhVGn0NBQ7N27Vy33ISpKGJyIiIiItICuri5WrVoFS0tLAMDff/+NihUrquVe9vb2+Oabb4T2/PnzkZmZqZZ7ERUVDE5EREREWqJ58+a4fPkyVq9ejUGDBqn1XjNnzhS+v3PnDg4fPqzW+xEVdgxORERERFqkVq1aGDt2rNJjqampKruPk5MTunfvLrTnz58PmUymsusTFTUMTkRERESFwKpVq9CwYUO8evVKZdfMvqfTv//+i1OnTqns2kRFDYMTERERkZbz9/fHxIkTERISAnd3dzx48EAl13V1dUX79u2F9oIFC1RyXaKiiMGJiIiISMvdvHlTmEYnk8lgbm6usmvPmjVL+P7ixYu4ePGiyq5NVJQwOBERERFpuV9//RUrVqxAqVKlcPz4cVhbW6vs2k2bNkWLFi2ENkediJRjcCIiIiIqBCZMmIDHjx+jVq1aKr929lGnkydP4tq1ayq/B1Fhx+BEREREVEiULFlSoS8lJQVr1qwp0D5Mbdq0QaNGjYQ2R52IFDE4ERERERVSmZmZGDp0KMaNG4eBAwfme7lyiUQiN+p08OBB3L59W1VlEhUJDE5EREREhdTy5cuxe/duAMDOnTuxYsWKfF+rc+fOcHZ2Ftq//fZbgesjKkoYnIiIiIgKqZEjR6Jt27YAgPbt22Py5Mn5vtano0579uzBw4cPC1wjUVHB4ERERERUSJUoUQJHjhzB3LlzsWfPHujr6xfoej179oSjoyOAj8ueL1y4UBVlEhUJDE5EREREhZiBgQHmzJmjdG+nvD7zpKOjg5kzZwrtf/75B0+fPi1wjURFAYMTERERURH07Nkz1KhRA7t27crT6/r164dq1aoBAKRSKRYvXqyO8ogKHQYnIiIioiLm7du36NChA54/f45+/frhjz/+yPVr9fT08OOPPwrtjRs34uXLl2qokqhwYXAiIiIiKmKeP3+Ot2/fCm1LS8s8vX7IkCGwtrYGAKSnp2Pp0qWqLI+oUGJwIiIiIipiXFxcEBgYCBsbG8ybNw+enp55er2BgQGmT58utNevX483b96ouEqiwoXBiYiIiKgIsre3x82bN/HTTz/l6/UjRoxA+fLlAQDJyclYvny5KssjKnQYnIiIiIiKKEtLS0gkEoX+3bt348WLF599rbGxMaZOnSq0V61ahffv36u8RqLCgsGJiIiIqBg5dOgQ+vfvD3d3d9y5c+ez544ZMwalSpUCACQkJOCvv/7SRIlEWonBiYiIiKiYePXqFfr164fMzEzhe6lUmuP5ZmZmmDJlitD+888/ERcXp4FKibQPgxMRERFRMVGpUiV4e3tDR0cHJUqUwI4dO6Crq/vZ10ycOBElSpQAAMTExGD16tWaKJVI6zA4ERERERUjo0ePhr+/P/z9/eHk5PTF8y0tLTFhwgShvWzZMiQmJqqzRCKtxOBEREREVMx069YNrVu3VuhPS0tTOnVvypQpMDExAfBxc10fHx+110ikbRiciIiIiAiZmZnw9PREv379kJKSInesbNmyGDNmjNBesmSJwjlERR2DExERERFhxowZ2LFjB/z8/PD1118jNjZW7riXlxcMDAwAAK9fv0a7du2QkJAgRqlEomBwIiIiIirm0tPTcffuXaFtaGgIY2NjuXMqVqyI6tWrC+3Lly/jm2++0ViNRGJjcCIiIiIq5vT19XHgwAEMGzYMzs7O2Lt3rzC6lF1mZqZc+9ixY1i3bp2myiQSFYMTEREREUFfXx8bNmzAhQsXhOXHP9W8eXOFvjFjxmDatGkKoYqoqNETuwAiIiIi0g4SiQQWFhYK/REREQgKCsKyZcsAAGfPnsXr16+RlJQEAFi6dCmePn2KrVu3CqvvERU1HHEiIiIiohy9f/8eHTt2RL9+/bBq1SqsXbsWYWFhCA0NhbOzs3Devn370KpVK7x580bEaonUh8GJiIiIiHL07bff4sGDBwA+rrwXGBgIALC2tsalS5fQqVMn4dxr166hUaNGuH//vii1EqkTgxMRERER5Wjx4sWwtbUFAMyePRtNmjQRjpmbm+PAgQMYN26c0BcREYHGjRvjzJkzGq+VSJ0YnIiIiIgoR3Z2dggMDMTChQsxd+5cheN6enpYuXIlli9fDolEAgCIjY1Fhw4d4Ovrq+FqidSHwYmIiIiIPqtcuXKYMWOGEIyyS0lJgUQiwZQpU7Bv3z5h/6eMjAyMGDECM2fO5Ip7VCQwOBERERFRvhw9ehQ1atTArVu3AAAeHh64ePEiKlSoIJyzcOFCDBgwACkpKSJVSaQaDE7/b/Xq1bC1tYWRkRGcnZ1x+PBhsUsiIiIi0lrBwcHo06cPXrx4gWbNmgnPNDVo0ABBQUGoXbu2cO6uXbvQpk0bREdHi1UuUYExOAHYtm0bJk6ciIEDB+LgwYNwcXFBjx49EBQUJHZpRERERFopNDQUqampQrt06dLC91WrVsXly5fRrl07oS8wMBBubm4IDQ3VaJ1EqiKRyWQysYsQW40aNdCiRQv4+PgIfQ0bNkSZMmVw9OjRfF+3Vq1aAIB79+4VuEYiIiIibXPs2DEMGDAAu3fvlgtJWdLT0zF+/Hi5z1glS5aEv78/WrRooclSiQAU7PN5sR9xSkpKwuPHjxX+Y2/Tpg3OnDkj95sUIiIiIvqfjh07Ijw8XGloAgB9fX2sW7cOv//+u9D34cMHtGzZEh06dEBCQoKmSiUqMI0Gp+vXr2PRokXo2bMnrK2tIZFIlK7O8qnk5GTMnj0bNWrUgJGRESpWrIjhw4fj1atXBa4pJSUFMpkMBgYGcv2GhoZIS0vDs2fPCnwPIiIioqLKwsJCoU8mk2HFihVISkqCRCLBtGnTsGfPHujq6grnnDhxAp6enhqslKhgNBqc5s2bhx9//BH+/v65Dj0pKSlo3bo15s2bh4SEBHTv3h2VK1fGxo0bUa9ePTx9+rRANZUqVQolS5bEv//+K9ef1X7//n2Brk9ERERU3Pz000+YNGkS2rRpg7dv3wIAevfuDRsbG7nzDh48yAUjqNDQ0+TN3N3d4eTkBFdXV7i6usLGxuaLU+Hmz5+PoKAguLu74+TJkzAzMwMAeHt7w8vLC8OHD8f58+eF82NiYhAZGfnZa5qYmKBKlSpCe/To0Vi5ciXc3d3RpEkT7Ny5EydPngQA6OgU+9mMRERERLl2+PBhLFy4EAAQFBSE6dOnY8OGDQA+Pgrx5MkT4dz09HT07t0bp06dUpj9Q6RtRF0cwsjICKmpqciphLS0NJQrVw6xsbG4ceMG6tWrJ3fc2dkZISEhCA4ORv369QEAa9euxdixYz973xYtWsiFrcTERAwcOBAHDhwAAFSqVAnffvstfvnlFzx79kzhtyO5xcUhiIiIqLjJyMjA+PHjsX79etSuXRuXLl2CpaUlACAhIQFeXl44dOgQ/vvvP+E1o0ePxpo1a3L1CAdRQRTZxSECAgIQGxsLW1tbhdAEfBzyBYBDhw4JfWPGjIFMJvvsV/bQBACmpqbYv38/Xr9+jbt37+LZs2cwNzdHuXLl8h2aiIiIiIojPT09rF27FsuXL8exY8eE0AQAZmZmWLduHV69eoUhQ4YI/evWrcOaNWtEqJYo9zQ6VS+vbt++DQBwcXFRejyrPyQkRCX3s7KygpWVFVJTU7Fx48ZcP7CYlVw/9eTJE9ja2qqkNiIiIqLCQiKRYMqUKUqPpaSkwMjICOvWrUNoaCiuXr0KAJg0aRIcHBzQunVrDVZKlHtaPeL0/PlzAIC1tbXS41n9ERERBbrPwYMHsXbtWpw7dw7//PMPmjZtioyMDMycObNA1yUiIiKi/4mJiUHDhg2xYMECGBoawt/fHxUrVgQASKVS9OnTp8ALfxGpi1aPOGWt7W9iYqL0uKmpKQAgPj6+QPfR1dXFypUr8eTJE5iZmaFr165YtGiR0uU1lclpjmROI1FERERExU1qaio8PDxw584d3LlzBy9evMCaNWtw4MABNGvWDCkpKXj//j26deuGwMBAlChRQuySieRo9YiTpnTu3Bl3795FcnIyoqOj4evri3LlyoldFhEREVGR8e7dO7kFIUqXLg2JRIIGDRrA19dX6L937x4GDRqEzMxMMcokypFWB6espceTkpKUHk9MTAQAmJuba6wmIiIiIsq7ihUrIiAgAO7u7hgyZAjmz58vHOvfvz9+/PFHoX3o0CH8/PPPYpRJlCOtnqqXtdfSy5cvlR7P6q9atarGaiIiIvq/9u47LIpr/x/4e+kgCCqIigpKMRcUBexgsPeKFTSKoEajxlhi9MYSQ270auCamMQEjCVRMLFrrPGqqCAWvJYYFWygWEBsVGnn94e/na+bXVjqLuX9eh6fxz3nzJzPzM7s8tmZOYeISsfS0hJHjx6Fnp6e0tDjX3zxBf78809ptOQvv/wSLVu2hK+vrzZCJVJSqa84tW7dGgBw8eJFlfXycldXV43FRERERESlZ2JionKy22PHjmHOnDlwdnaWygICAhAbG6vJ8IgKVakTJ09PT5ibm+P27du4dOmSUv327dsBAIMGDdJwZERERERUXi5evIhhw4Zh4MCBmDt3LurWrQvgzdDlQ4YMUXg2ikhbKnXiZGBggBkzZgAApk+fLj3TBAAhISG4cuUKvL294eHhoa0QiYiIiKgM5KPtpaenIyMjA3PmzMGGDRugq6sLAEhKSsKwYcOQnZ2t5UipptNo4rR//3507NhR+peTkwMACmX79+9XWGbRokXo0KEDoqOj4ejoiNGjR6Njx46YO3curKysFEZhISIiIqKqxdDQEOvWrYOpqSl0dXURERGBwYMH4+uvv5banD17FlOnToUQQouRUk2n0cEhUlJSpNmh3/Z2WUpKikKdkZERjh8/juXLlyM8PBy7d+9G3bp14e/vj6CgoEInxyUiIiKiqqF37944efIkrl+/jn79+gEAPvjgA1y5cgWhoaEAgE2bNsHV1RVz5szRZqhUg8kEU/cKI58At7AJcomIiIiocDk5OejRowdOnz4NANDR0cHvv/8uJVdEJVWWv88r9TNORERERFRzGRgYoEOHDtIofAUFBRgzZgxu3Lih5cioJmLiRERERESVUmhoKIKDg5GTkyPN+/Tq1Su0bt0a/v7+SE9P13KEVJMwcSIiIiKiSumvv/6S/m9jYyP9PycnB5s2bcKsWbO0ERbVUEyciIiIiKhS+s9//oMVK1bAxsYGUVFRsLa2Vqjftm0bcnNztRQd1TRMnIiIiIioUpLJZPjkk0/w119/oWnTphg8eLBCfVpaGqZMmcJhykkjmDgRERERUaVWu3ZtAEBISAgmT54svQaAjRs34p///Ke2QqMahIkTEREREVUJpqamCA0Nxf3792FhYSGVr1ixAqtXr9ZaXFQzMHEiIiIioiplxYoVePHihULZ7NmzsWXLFu0ERDUCEyciIiIiqlLmzZsHLy8vAECtWrWkcn9/fxw5ckRbYVE1x8SJiIiIiKqUunXr4o8//kBQUBBiYmJgaWkJAMjLy4OPjw/OnTun5QipOmLiRERERERVjpGRERYtWoSWLVviwIED0pWnjIwM9O/fHzdv3tRyhFTdMHEiIiIioiqtXbt22LVrF/T19QEAqamp6NKlCx4+fKjlyKg6YeJERERERFVer169EBwcLL1OSUlB+/btlQaRICotJk5EREREVC24ubnByMhIep2UlITBgwcjKytLi1FRdcHEiYiIiIiqBS8vL5w9exampqZS2alTp+Dn54e8vDwtRkbVARMnIiIiIqo2XF1d8eDBAwQEBEhlu3fvxrRp0yCE0GJkVNUxcSIiIiKiasXc3Bw//vgjBg0aJJWtW7cOgwYNwuPHj7UYGVVlTJyIiIiIqNrR09PD1q1b4enpKZXt378fLi4uHKqcSoWJExERERFVSyYmJti3bx8cHByksmfPnsHV1RVTpkxBenq6FqOjqoaJExERERFVW3Xq1MGJEydgYWEhleXk5CAsLAxz587VXmBU5TBxIiIiIqJqzcbGBmfOnIFMJlMo37lzJwoKCrQUFVU1TJyIiIiIqNp755134OPjo1D29OlTjBw5EqmpqRxxj9Ri4kRERERENcLGjRsxduxYhUlyd+7cCXt7e4wePRo5OTlajI4qOyZORERERFQjmJqaYvPmzUhOTkb//v2l8pcvX2Lbtm3o1q0bMjMztRghVWZMnIiIiIioRjEzM8OePXswadIkhfLz588jOjpaS1FRZcfEiYiIiIhqHD09PYSGhuLf//63VJabm4t+/fph06ZNWoyMKismTkRERERUI8lkMsyfPx/btm2TnnvKy8uDv78/lixZwgEjSAETJyIiIiKq0UaMGIHIyEjUr19fKgsKCsLAgQOxbds2LUZGlQkTJyIiIiKq8dq3b4+YmBj84x//kMoOHDiAUaNGKdzORzWXnrYDICIiIiKqDJo1a4aoqCj4+PjgxIkTUvmCBQtQr1493Lp1C/fu3YOdnR0CAwPh6OiovWBJ42SCN29WGBcXFwDAtWvXtBwJERERERVXTk4ORo0ahT179hTaRldXF2FhYZg4caIGI6OyKsvf57xVj4iIiIjoLQYGBti1axc+/vjjQtvk5+dj8uTJiI+P12BkpE1MnIiIiIiI/kYmk2HlypUYNGhQoW3y8/Px/fffazAq0iYmTkREREREhTAxMSmy/ptvvsGwYcPw22+/ITMzU0NRkTYwcSIiIiIiKoSdnV2R9QUFBdi9ezdGjx4Na2trvPfeezhw4AByc3M1EyBpDBMnIiIiIqJCBAYGQldXt1ht09PTsXnzZgwYMAANGzbE1KlTcfLkSRQUFFRwlKQJTJyIiIiIiArh6OiIsLAwlcnTwoULcejQIYwfPx5mZmYKdampqfjxxx/h7e0NW1tbzJs3DxcvXgQHtK66OBx5BeJw5ERERETVQ3x8PNavXy/N4/Tee+/B2dlZqs/KysL+/fsRHh6OAwcO4PXr1yrXY2BggFatWiEsLAxubm6aCp/+v7L8fc7EqQIxcSIiIiKqOXJycjBs2DCMHTsWubm5CA8Px9GjRwu9Vc/DwwN+fn4YPXo0bGxsNBxtzcR5nIiIiIiItEgIgRkzZuDAgQMYN24ckpKScOjQITx8+BBr1qxROTpfbGws5s6diyZNmqB79+5Yt24dnj9/roXoqTiYOBERERERlVFiYiK2bt0K4E0SFRcXBwCwtrbGjBkzMG7cuEKXFULg+PHjmDx5MqytrTF06FD8+uuvHN68kuGtehWIt+oRERER1Rx//fUXBg8eDCsrKxw/fhxGRkZSXXp6OubOnYszZ86gU6dOCAwMxJ49exAeHo579+6pXJ+pqSmGDh0KPz8/9OzZE/r6+hrakuqLzzhVUkyciIiIiGqW1NRU5OXlwdrauljthRA4e/YswsPD8euvvyI5OVllu3r16sHa2ho5OTno3r07goODYWpqWp6h1whMnCopJk5EREREBADnz5/HjRs38N577xXaJi8vD8eOHUN4eDh27tyJtLS0Qtv269cP+/fvh0wmq4hwqy0ODkFEREREVEk9evQIQ4cOxfjx4zF//nzk5+erbKenp4fevXtj48aNePLkCbZv3w4fHx8YGhoqtT148CDc3NywdetW5OXlVfQmEJg4ERERERFVqClTpuDhw4cAgJCQEFy+fFntMsbGxhg+fDh27NiBJ0+ewNvbW6nN5cuX4evrixYtWuCHH35AVlZWucdO/4eJExERERFRBVqzZg1atWoFAPjmm2/g7u5eouXNzc3x+++/Y8qUKWjevDmaNm2qUH/nzh1MmzYNdnZ2WL58OV68eFFeodNb+IxTBeIzTkREREQEAGlpadi6dSsmT55cLuu7evUqVq5ciYiICKVb/8zMzDB16lR89NFHaNSoUbn0V13wGSciIiIiokrMzMys0KTp1KlTKOm1jFatWuGXX37BrVu3MGPGDIWhz9PS0rBq1So0a9YMU6ZMQXx8fJlipzeYOBERERERacn69evx7rvv4v3330dOTk6Jl7ezs8OaNWuQkJCARYsWwcLCQqrLyclBWFgYWrRogVGjRiE2NrYcI695mDgREREREWlBdHQ0pk6dCgAICwvDrFmzSr2u+vXrIygoCImJiQgODla4RU8IgW3btqFt27awtbXFhQsXyhx7TcTEiYiIiIhIC5o3bw4PDw8AbxKfhQsXlnmdZmZmmDNnDu7cuYOffvoJLVq0UKhPTExE+/btMXPmTDx9+rTM/dUkTJyIiIiIiLSgQYMGOH78OCZNmoRdu3YpjZZXFoaGhggICMC1a9eU1iuEwLfffgsHBwcEBwfj9evX5dZvdcbEiYiIiIhIS4yMjBAWFobOnTsr1aWnp5d40Ii/09XVRd++fVXWvXz5EvPmzYOLiwt27txZ5r6qOyZORERERESVTE5ODgYMGIAxY8YgMzOzTOsKDg7GlClT0KpVKwQGBuKLL75A7dq1pfrbt29j+PDh6Nq1KweQKALncapAnMeJiIiIiEpKCIFp06bhxx9/BAC4u7vj2LFjMDc3L7c+UlJS8Nlnn+HHH39Umgdq/Pjx+Ne//oXGjRuXW3+VBedxIiIiIiKqJnJycnD79m3ptYuLi8IVovJgZWWF7777DleuXEH//v0V6n7++Wc4OTlh6dKlyMjIKNd+qzImTkRERERElYihoSEOHjyIWbNmoWPHjggNDYVMJquQvpydnbF//34cPnwYLVu2lMqzsrLw+eefw9HRERs3bkRBQUGF9F+V8Fa9CsRb9YiIiIioLLKysmBsbKyRvvLy8rB+/XosXrwYycnJCnX16tWDubk5evbsieDgYJiammokpvLGW/WIiIiIiKohVUlTbGwshg8fjpcvX5ZrX3p6epgyZQri4+OxcOFCGBoaSnWpqam4c+cOQkNDMWfOnHLtt6pg4kREREREVEU8evQIQ4YMwc6dO9GhQwfExcWVex+1a9fGl19+iRs3bmDMmDFK9b/++iuysrLKvd/KjokTEREREVEVsXbtWiQlJQEAbt26hfv371dYX3Z2doiIiMCQIUMUyl+9eoXu3bsjJSWlwvqujJg4ERERERFVEZ999hmWLFkCAFi9ejV69OhR4X1u3rwZEydOhJmZmVQWExODzp0749atWxXef2XBwSEqEAeHICIiIqKKcOHCBXh4eFTYaHuq5OXlYcaMGdL8UgBgaWmJffv2oWPHjhqLoyw4OAQRERERUQ3Stm1blUnTDz/8oDQiXnnR09PD2rVrsXz5cqns6dOn6NatG3bt2lUhfVYmTJyIiIiIiKqBjRs3Ytq0aWjXrh0uX75cIX3IZDIsWLAAmzdvhr6+PgAgOzsbw4cPx5o1ayqkz8qCiRMRERERURUXHx+P999/HwCQmJiI6dOnoyKfyBk7diwOHz4Mc3NzAIAQAh9++CHmzp1bbSfLrfaJ04ULFzB+/Hg4ODhAJpNh0aJFpWpDRERERFRZOTg4YMWKFdDR0YGVlRW2bNlS4c8/devWDVFRUWjSpIlUFhISgtGjRyM7O7tC+9aGap84RUVFISYmBl5eXlJGXJo2RERERESVlUwmw+zZs3HgwAHs3LkTtra2GunXxcUFMTExaNOmjVS2fft29OzZE6mpqRqJQVOqfeI0c+ZMxMXFYePGjbCwsCh1GyIiIiKiyq5Pnz7w8vJSKk9NTUViYmKF9NmoUSOcPHkSffr0kcqioqLg6emJO3fuVEif2lDtEycdHfWbWJw2RERERERVUW5uLkaMGIF27dohKiqqQvowMzPDvn37EBAQIJXdvHkTnTp1wvnz5yukT00rt4whNjYWK1asgI+PDxo3bgyZTFas+yqzsrKwZMkSODk5wcjICI0aNUJAQIA0IzIREREREZXerFmzcOLECSQnJ6Nbt264cuVKhfSjr6+PdevW4fPPP5fKkpOT0bVrV+zbt69C+tSkckucgoKCsHDhQuzatavYSU92dja6d++OoKAgpKenY8iQIWjSpAk2bNgANze3anVpj4iIiIhIG9q1aycNHT569Gi0atWqwvqSyWRYvHgxNm7cCD09PQBAZmYmBg8eDBsbG7z//vtIT0+vsP4rkl55rahTp05wdXVFu3bt0K5dO9jZ2eH169dFLvPFF18gJiYGnTp1wpEjR2BqagrgzWgcc+fORUBAAE6cOCG1f/HiBR4/flzkOk1MTNC0adMybw8RERERUXUwceJEODk54d///jfCwsIqfLQ9AJgwYQJsbGzg4+ODtLQ0AMDDhw8RGhoKIQRCQ0MrPIbyVm6J0yeffFKi9jk5Ofj2228BAN99952UNAHAnDlzsGnTJkRGRiI2NhYeHh4AgK1bt2LatGlFrtfb21sh2SIiIiIiquk8PT2xd+9elXXZ2dkwMjIq9z579uyJ06dPw8PDA3l5eVL5mTNnyr0vTdDaqAhRUVF4+fIl7O3t4ebmplQ/YsQIAFC4H3Lq1KkQQhT5j0kTEREREVHxXLx4Ec2bN8fhw4crZP2urq4YPXq0Qlnnzp0rpK+KVm5XnErq8uXLAAB3d3eV9fLyinp4rTy5uLioLL99+zbs7e01HA0RERERkXqPHz/GkCFD8OjRI/Tv3x/ffvut2ru7SuOHH36Avr4+YmJi8O677yI4OLjc+9AErSVO8nHkGzdurLJeXp6QkFCmflJSUhAZGQngzYNpN27cwPbt21GrVi3069ev2G2IiIiIiKqTa9eu4dmzZwDeDOrg6OhYIf2Ymppiw4YNFbJuTdJa4iQfTcPExERlfa1atQBAepistK5du4aRI0dKr3fs2IEdO3bA1tYW9+7dK3YbdX2oUtiVKCIiIiIibevRoweioqIwZMgQzJs3Dz179tR2SJWa1hInTenatSuEEGVuQ0RERERU3bRp0wZXr16FmZmZtkOp9LQ2OIR8FL3MzEyV9RkZGQDAN5GIiIiIqALVrl1b5RDlS5YswbZt27QQUeWktcRJPtfSgwcPVNbLy21tbTUWExERERERAT///DOCgoIwatQoLF26FAUFBdoOSeu0lji1bt0awJshEFWRl7u6umosJiIiIiKimi4zM1Nhjtbdu3cjOztbixFVDlpLnDw9PWFubo7bt2/j0qVLSvXbt28HAAwaNEjDkRERERER1VwmJiaIjIxEixYtYGlpiT179hQ6oFtNorXEycDAADNmzAAATJ8+XXqmCQBCQkJw5coVeHt7w8PDQ1shEhERERHVSE5OTjh79iyOHDkCOzs7bYdTKZTbqHr79+9HUFCQ9DonJwcA0LFjR6ls8eLFGDBggPR60aJFOHr0KKKjo+Ho6IguXbogISEBZ8+ehZWVFdavX19e4RERERERUQmYm5vDzc1NqTw1NRWHDh3C2LFjtRCV9pRb4pSSkoKzZ88qlb9dlpKSolBnZGSE48ePY/ny5QgPD8fu3btRt25d+Pv7IygoqNDJcYmIiIiISPNyc3MxatQoHDt2DOfOnUNwcDD09Kr9DEcAAJngBEYVRj4BbmET5BIRERERVSX//Oc/sXz5cun1li1b4Ofnp8WISqYsf59r7RknIiIiIiKqWqZNmwZ3d3cAgJ+fH3x9fbUckeYwcSIiIiIiomJp0qQJTp06hcWLF2PdunUqJ86trpg4ERERERFRsZmYmODzzz+HsbGxUl1cXByq65NATJyIiIiIiKjMLl26BDc3N/j7+1fLCXOZOBERERERUZk8efIEgwcPRmZmJn7++Wf4+PhoO6Ryx8SJiIiIiIjKREdHB82aNZP+/9FHH2k3oApQMwZdJyIiIiKiCmNlZYU//vgDM2fOxD/+8Q/07t1bqouPj8dPP/2Ee/fuwc7ODoGBgXB0dNRitKXDeZwqEOdxIiIiIqKaRJ5ayEfb27BhAyZPnoz8/Hypja6uLsLCwjBx4kSNx8d5nIiIiIiISOtkMpmUNMXHxyslTQCQn5+PyZMnIz4+XhshlhoTJyIiIiIiKnc//fSTUtIkl5+fj/Xr12s4orJh4kREREREROXu3r17ZaqvbJg4ERERERFRubOzsytTfWXDxImIiIiIiMpdYGAgdHV1Vdbp6uoiICBAwxGVDRMnIiIiIiIqd46OjggLC1NKnnR1dbFu3boqNyQ553EiIiIiIqIKMXHiRHh5eWH9+vXSPE4BAQFVLmkCmDgREREREVEFcnR0xPLly7UdRpnxVj0iIiIiIiI1mDgRERERERGpwcSJiIiIiIhIDSZOREREREREajBxIiIiIiIiUoOJExERERERkRpMnIiIiIiIiNRg4kRERERERKQGEyciIiIiIiI1mDgRERERERGpwcSJiIiIiIhIDSZOREREREREajBxIiIiIiIiUoOJExERERERkRpMnIiIiIiIiNRg4kRERERERKQGEyciIiIiIiI1mDgRERERERGpIRNCCG0HUV2ZmZkhNzcX9vb22g6FiIiIiKjGu337NvT19ZGWllbiZXnFqQLVqlUL+vr62g4Dt2/fxu3bt7UdBpUQ3zft4H6n0uBxox3c76RpPOZKr7LsO319fdSqVatUy/KKUw3g4uICALh27ZqWI6GS4PumHdzvVBo8brSD+500jcdc6VWHfccrTkRERERERGowcSIiIiIiIlKDiRMREREREZEaTJyIiIiIiIjUYOJERERERESkBkfVIyIiIiIiUoNXnIiIiIiIiNRg4kRERERERKQGEyciIiIiIiI1mDgRERERERGpwcSJiIiIiIhIDSZOREREREREajBxIiIiIiIiUoOJUzWVlZWFJUuWwMnJCUZGRmjUqBECAgKQlJSk7dCoECdOnIBMJlP77/PPP9d2qFVSbGwsVqxYAR8fHzRu3Fjan4XZu3cvJkyYgFatWsHS0hL6+vqoX78++vfvj99//12DkZO2ZGZmYvfu3QgMDESLFi1gZGSEWrVqoXXr1vj888+Rnp6u0L6goACnTp3C/Pnz4eHhATMzMxgaGsLe3h5Tp07F3bt3tbQlVUtJ97vcw4cPMWPGDDg4OMDQ0BAmJiZwdXXF0qVLkZaWpuGtoKooJCQEPj4+cHR0hLm5OQwNDWFra4vx48fj6tWrKpfhcacsNTUV9evXh0wmg4ODg8o2VfU7lhPgVkPZ2dno1q0bYmJi0LBhQ3Tp0gX37t3DuXPnYGVlhZiYGDRv3lzbYdLf3LhxAytWrFBZl5+fj82bNwMAjh07hm7dumkytGph6NCh2LNnj1J5YR+BI0aMwM6dO+Hi4oKmTZvCzMwM9+7dw9mzZwEACxcuxJdfflmhMZN2rVu3DpMnTwYA/OMf/0DLli3x6tUrREdHIy0tDe+88w4iIyNRv359AMCtW7fg6OgIAGjQoAHat28PXV1dnDt3DklJSTAzM8OBAwfg5eWltW2qCkq63wEgPj4enp6eSElJgZ2dHdzd3ZGdnY3o6Gi8ePECzs7OiI6Ohrm5ubY2i6oAS0tLZGRkwNXVFTY2NgCAa9euIS4uDvr6+ti5cycGDhwotedxp5q/vz9+/vlnCCFgb2+PW7duKbWpst+xgqqdTz/9VAAQnTp1EmlpaVJ5cHCwACC8vb21FxyVyoEDBwQA0aRJE1FQUKDtcKqkFStWiMWLF4u9e/eKR48eCUNDQ1HUR+DFixfF06dPlcpjYmKEqampkMlk4sqVKxUZMmnZxo0bxZQpU8Rff/2lUP7w4UPh5uYmAAhfX1+p/NatW6JXr17iv//9r8J5mp2dLfz9/QUA0bRpU5GTk6OxbaiKSrrfhRBi2LBhAoD44IMPRF5enlT+4sUL0bFjRwFALFmyRCPxU9V1+vRpkZWVpVT+3XffCQDC2tpa5ObmSuU87pQdPXpUABBTpkwRAIS9vb3KdlX1O5aJUzXz+vVrYW5uLgCIixcvKtW7uroKAOLChQtaiI5Ky8/PTwAQCxYs0HYo1Ya6xKkogYGBAoD4+uuvyzkqqiqio6MFAGFoaChev36ttn1mZqb02XzixAkNRFg9Fbbf69WrJwCIR48eKS2zc+dOAUD069dPk6FSNWNvby8AiMuXL0tlPO4UZWZmCnt7e+Hs7Czi4uKKTJyKUpm/Y/mMUzUTFRWFly9fwt7eHm5ubkr1I0aMAADs27dP06FRKWVkZEi3mL333ntajoYAQF9fHwBgYGCg5UhIW1q3bg0AeP36NVJTU9W2NzY2hpOTE4A3z0RQ6RS23w0NDdUuW69evQqLi6o/VZ/7PO4ULVu2DHfu3MEPP/wg7a/SqMzfsUycqpnLly8DANzd3VXWy8uvXLmisZiobHbu3ImMjAy4ubnB2dlZ2+HUeFevXsWvv/4KfX199OrVS9vhkJbcuXMHwJsv+Lp166ptX1BQgISEBABvnn+i0ilsv/fu3RsAEBQUhPz8fKn85cuXWLlyJQAgICBAg5FSdfLLL7/g5s2bcHR0lJ5jBHjcve3KlSsIDg7GxIkT0aVLl1Kvp7J/x+ppOwAqX4mJiQCAxo0bq6yXl8u/wKnykw8KwatN2rFv3z7s2LEDubm5SExMRHR0NPT19REWFgZ7e3tth0da8vXXXwMA+vbtW6xfnSMiIpCcnAwrKyt07ty5osOrtgrb78uXL0dsbCy+//57HDhwAB4eHsjOzkZUVBSMjIywefNmDqpDxbZq1Spcu3YNGRkZuH79Oq5du4ZGjRohIiICurq6Ujsed28UFBRg0qRJsLCwkBLG4qpy37HavleQytfkyZMFAPHpp5+qrI+PjxcAhKOjo4Yjo9J4+PCh0NXVFbq6uirvoabSK+4zTkFBQQKA9M/Y2FiEhoaK/Px8DURJldH+/fuFTCYT+vr64tKlS2rbJyYmCktLSwFArF27VgMRVk/q9ntqaqro3bu3wvkKQPj4+Ijr169rIWKqqnr06KFwDNna2orIyEiVbXncCbF69WoBQGzYsEEqu3v3brGecapq37G8VY+oEouIiEB+fj569erF23u0ZNGiRRBCICsrC1evXsXEiRMxZcoUDBkyBDk5OdoOjzTsxo0bGDduHIQQWLVqlfTMTWEyMjLg4+ODp0+fYujQoZg6daqGIq1e1O33K1euoHXr1rh58yb27NmD58+f48GDB1i9ejUOHjwIT09P3Lx5U0vRU1Vz9OhRCCHw/PlznDx5Eo6OjvD29sa//vUvhXY87t7c6bRo0SJ4e3vD39+/xMtXue9YraZtVO5mz54tAIjZs2errL906ZIAINzd3TUcGZWGfOjdLVu2aDuUaqcso+rNnDlTABBfffVVOUdFldmDBw+Era2tACDmzJmjtn1OTo7o16+fACC8vLxEZmamBqKsftTt95ycHOHo6Ch0dHREbGysUr18Ko5Ro0ZpIlyqhnJycoSHh4eQyWTi3LlzUhmPOyEGDhwoDAwMlK6uFfeKkyqV+TuWV5yqmaZNmwIAHjx4oLJeXm5ra6uxmKh0rl+/jv/9738wNTXF0KFDtR0OvUX+vJmqCXWpenr27Bl69+6NhIQETJw4EV999VWR7QsKCjBhwgQcPHgQbdq0wb59+2BsbKyhaKuP4uz3mJgYxMfHo1mzZioHRho5ciQA4OTJkxUeL1VP+vr6GD16NIQQ0qjEPO7e+P3332FiYoKpU6eia9eu0r8xY8YAAJKSkqSyx48fF2udlfk7loNDVDPy2xcuXryosl5e7urqqrGYqHR++eUXAICPjw9MTEy0HA29zdLSEgCQkpKi5UhIE9LT09GvXz/89ddf8PHxQVhYGGQyWZHLzJw5ExEREXBycsLhw4dhYWGhmWCrkeLud/kPgubm5irXIy9//vx5xQVL1d7fP/d53P2fFy9eIDIyUmVddna2VJednV2s9VXm71hecapmPD09YW5ujtu3b+PSpUtK9du3bwcADBo0SMORUUkIIRAeHg6Ao+lVRvIvgUo54g+Vq9evX2PIkCE4d+4c+vTpozSqliqLFi3C999/j6ZNm+KPP/5A/fr1NRRt9VGS/S5//vPmzZtIS0tTqj9//jwAwM7OrsLiperv75/7PO7eEEKo/Hf37l0Ab/aXvKy4+6JSf8dq6x5BqjiffvqpACA6d+4s0tPTpXL5/bbe3t7aC46KJTIyUgAQNjY2lXZkmaquqGeckpOTRWhoqMjIyFCqO3LkiLCyshIAxPbt2ys6TNKivLw8MWzYMAFAdOnSReXx8HchISECgGjQoIGIi4vTQJTVT0n3e1ZWlqhfv74AIMaPHy+ys7OluqSkJNGqVasiR5slEkKI06dPi4MHDyp95+bk5IhvvvlG6OjoCGNjY5GYmCiE4HGnTlHPOFXl71iZEEJoI2GjipOdnY2uXbvi7NmzaNiwIbp06YKEhAScPXsWVlZWiImJQfPmzbUdJhVhypQpCAsLw8cff1ziORFItf379yMoKEh6fe7cOQgh0KFDB6ls8eLFGDBgAO7du4dmzZrBxMQEHh4eaNy4MTIyMhAXF4cbN24AAGbPno2QkBCNbwdpztdff42PPvoIADBs2DDUrl1bZbuvvvoKlpaWuHTpEtzd3SGEQKdOneDk5KSy/aRJk+Dl5VVRYVd5Jd3vALB7926MHDkSeXl5sLGxQdu2bZGVlYUzZ84gLS0N7u7uiIyMhKmpqaY2g6qYjRs3YuLEibC0tISHhwfq1auHp0+f4urVq3j06BGMjIywadMmjBo1SlqGx13h5N+j9vb2uHXrlsq6Kvkdq928jSpKZmamWLx4sbC3txcGBgaiQYMGwt/fX9y/f1/boZEa2dnZok6dOgKAuHz5srbDqTY2bNigNM/G3//J56DIyMgQK1euFP379xe2trbC2NhYGBoaCjs7OzFmzBhx/PhxrW4LacbSpUvVHjMAxN27d4UQQhw/frxY7d+e64SUlXS/y128eFH4+fmJxo0bC319fVGrVi3Rpk0b8eWXX3JEQ1Lrzp074p///Kfw9PQUDRs2lI4hFxcXMXPmTBEfH69yOR53qhV1xakqf8fyihMREREREZEaHByCiIiIiIhIDSZOREREREREajBxIiIiIiIiUoOJExERERERkRpMnIiIiIiIiNRg4kRERERERKQGEyciIiIiIiI1mDgRERERERGpwcSJiIiIiIhIDSZOREREREREajBxIiIiIiIiUoOJExFpVUZGBkJCQtCtWzdYW1vDwMAAderUQadOnbBkyRIkJiZqO8RyI5PJYGdnV+H9nDhxAjKZDP7+/hXeV0l89tlnkMlk2Lhxo0K5v78/ZDIZTpw4oZW4qpN79+5BJpOha9eu2g6lxLp27QqZTIZ79+5pO5QKsXHjRshkMnz22WfaDoWISomJExFpTXR0NBwcHDB37lycO3cOLVu2xIgRI9C5c2fcvn0bQUFBcHJywtGjR7UdaqVSWAJCVB1p6geHsuIPAETVn562AyCimunSpUvo0aMHsrOz8cknn2Dx4sWoVauWVF9QUIDdu3dj/vz5ePDggRYjrXrat2+P69evw9zcXNuhFMvy5cuxYMECNG3aVNuhkBb9/PPPyMzMhI2NjbZDqRDDhg1Dx44dYWlpqe1QiKiUmDgRkcYJIfDee+8hOzsbn332GZYuXarURkdHBz4+PujRowfu37+vhSirLhMTE7zzzjvaDqPYGjZsiIYNG2o7DNKy6p44m5ubV5kfM4hINd6qR0Qad+jQIfz5559o3LgxPv300yLbmpubo2XLltLrop6DKOz5jrdvbYuNjUW/fv1gYWGBunXrYtSoUdIVrYyMDMyfPx92dnYwMjJCy5YtsX37dqV+1D2rUJJnNYQQiIiIwJgxY+Dk5IRatWrBzMwM7du3x/fff4+CggKF9nZ2dli2bBkAYOLEiZDJZNI/+S1Cqp5x+vDDDyGTybB27dpCY/Hw8IBMJsOVK1cUyu/fv48ZM2bA3t4eRkZGqFu3LgYOHIjo6Gi121cchd3iZGdnB5lMBgBYt24dXF1dYWxsjAYNGuD999/HixcvVK4vLy8Pa9euRadOnVC7dm0YGxujTZs2WL16NfLy8pTaX7p0CfPnz4eHhwesrKxgaGiI5s2b44MPPsDDhw+V2r99nL169Qpz5sxBs2bNoK+vj48++kjt9qakpGDBggVwdnaGqakpzM3N4eTkhPHjx+PcuXNK7f/66y+MHTsWDRs2hIGBAWxsbDB+/HjcvHlTbV8AEBISAplMhk8++aTQNsOHD4dMJsPevXsVyp89e4aFCxfC2dkZxsbGMDc3R/fu3fH7778rraOs++Xv5438PAOAhIQEhWP97+d4ZmYmli9fDjc3N5iamsLU1BQdO3bEpk2bVPYlv/0vJycHn3/+Od555x0YGhpi6NChAIDs7Gz89NNPGDJkCJo3bw5jY2NYWFjg3XffxdatW1WuT95Xt27dFGL9+/ao+tzIzMxEUFAQWrZsKe3nwvoCSn9uEFHZ8IoTEWnc/v37AQAjR46Enp7mPobOnj2LqVOnomXLlujTpw8uXryIbdu24fLlyzh37hx69eqFhIQEvPvuu3j69CkiIyMxatQoHDx4EH369KmQmF6/fg0/Pz/Uq1cPzs7OcHd3R2pqKqKjozF9+nScO3dO4VmmESNG4OjRo7h8+TI8PT3h4OAg1TVo0KDQfsaOHYs1a9YgPDwc06ZNU6q/ceMGLl68iFatWsHV1VUqP3PmDAYMGIDnz5+jRYsWGDBgAFJSUnD48GEcOnQIW7ZswejRo8tnZxRi/vz5+Prrr9G1a1c4ODggKioKoaGhuH79OiIjI6U/IAEgKysLAwYMwPHjx1G3bl107NgRRkZGOHv2LGbPno3jx49j165d0NH5v98NV6xYgR07dsDV1RVeXl4A3iRTa9euxe7du3HhwgU0atRIKa6srCx4e3sjISEB3t7ecHd3R506dYrclrS0NHTo0AF3795FkyZN0KtXL+jp6SExMRFbt25F8+bN0b59e6n9f//7XwwaNAhZWVlwc3ND165dcePGDfzyyy/YtWsXDhw4gC5duhTZ55gxY/Dxxx9j69atWLFihcL+AoCXL19i//79qFevHvr16yeVx8XFoWfPnrh//z7s7OzQp08fpKWlISYmBoMGDcKqVaswb968ctkvqjg4OGDChAnYtGkTatWqhREjRkh1b19RTU5ORq9evXDlyhU0aNAA3t7eEEIgOjoa/v7+uHDhAtasWaO0/oKCAgwdOhQnT56Et7c3XF1dUa9ePQBvksBJkyahUaNGaNGiBdq3b4/Hjx8jOjoap06dwo0bNxQSoAkTJuD06dO4ffs2+vTpo3AumpqaFrmdaWlp6NatG2JjY2FlZYWBAwciIyMDx44dw6lTp3DmzBl8/fXXKpctyblBROVAEBFpmKenpwAgfvnllxIv6+3tLQCIu3fvKtXdvXtXABDe3t4K5UuXLhUABACxdu1aqTwnJ0f07NlTABDOzs6ie/fuIj09Xapft26dACDeffddhfVt2LBBABBLly4tUYwAhK2trUJZbm6u2LVrl8jJyVEoT05OFm3bthUARGRkpMrt2bBhg8r+jx8/LgCICRMmKJQ7ODgImUwmEhISlJZZtGiRACBWrFghlb18+VI0bNhQ6Orqis2bNyu0P3/+vKhTp44wNTUVycnJKuP4u8LinjBhggAgjh8/rlBua2srAIgGDRqIGzduSOUpKSnCwcFBABD//e9/FZb54IMPBAAxevRo8eLFC6n81atXon///krHgBBCHDt2TDx+/FihLD8/XyxbtkwAEBMnTlSokx9nAESnTp3E8+fPi7X9Qgixfv16AUAMHjxY5OfnK9QlJyeLq1evSq/T09OFtbW1ACC+/fZbhbYhISECgGjcuLHIyspSiu3v50CPHj0EAHHy5EmlmOTH+dSpU6WyvLw80apVKwFArFy5UiHW+Ph40axZM6Grq6sQb1n2ixAlO2/eJn9fZ82aJbKzs6Xyx48fS+fQwYMHldYJQDg4OIgHDx4orfPp06fijz/+EAUFBQrld+7cEXZ2dkJHR0cpzsKOY7nCPjdmzJghAIhu3bqJV69eSeXXr18X9evXFwDEvn37FJYpzblBRGXHW/WISONSU1MBAFZWVhrt18vLC1OnTpVe6+vrY+bMmQDeXHFZu3atwgAV/v7+sLS0xJkzZ5Cbm1shMenp6WHo0KHQ19dXKLeyssLy5csBAHv27CmXvsaOHQshBMLDw5XqwsPDIZPJ4OfnJ5WtX78ejx49wkcffYSxY8cqtG/bti0WL16M9PR0bN68uVziK0xQUBBatGghvba0tJTex5MnT0rlycnJCAsLQ5MmTbBhwwaF50nMzMzw008/wcDAQOl2RflQ+G/T0dHBkiVLYGNjo3T72tu++eYbWFhYFHtbUlJSAADdu3dXuOoFvHnP374t9bfffsOTJ0/QqVMnTJ8+XaHt7Nmz4eHhgQcPHmDHjh1q+x03bhwAYMuWLUp18rK33+N9+/bh6tWrGD58OD7++GOFWB0cHBAcHIz8/HyEhYWp7K+k+6W0Ll26hAMHDqBdu3YICQmBoaGhVGdtbY3Q0FAAKPQW1eXLl6scjKJevXro2bOn0hWbZs2a4dNPP0VBQQH27dtX5vgzMjLw008/QUdHB99//z3MzMykunfeeQeLFi0CgEKvOBX33CCi8sFb9Yioxujdu7dSWfPmzQG8eWbAyclJoU5XVxe2traIjY3F06dPK3QAg0uXLuHIkSNISEhAZmYmhBBIS0sDAMTHx5dLH2PHjsWyZcsQHh6OBQsWSOVnzpzBnTt34O3tjSZNmkjlR44cAQD4+PioXJ/8FjFVz+WUJ1Xvm/y9evTokVR24sQJ5Obmom/fvjA2NlZapkGDBnB0dMTVq1eRlZWl0CY1NRV79+7Fn3/+iRcvXiA/Px8AkJubi9TUVDx79gx169ZVWF/Dhg3Rtm3bEm2Lh4cHAGDVqlWwtrbGgAEDFP5YftupU6cAQClplRs3bhxiY2Nx6tSpQtvI+fj4YNq0adi+fTvWrFkjJepJSUmIjIyEnZ0dPD09pfZlee9Ls19KSx7n0KFDlRJRANIzT6rilMlkGDRoUJHrP336NE6cOIGkpCRkZ2dDCCEdc+VxXsbGxiIrKwtt27ZVOaDLe++9hw8//BBRUVEoKChQ2sbinhtEVD6YOBGRxsmfI5D/+q4pqn5Zlj9/UNgQyPL6169fV0hMOTk58Pf3R0RERKFt5AlUWTk6OqJdu3Y4f/48rl69ilatWgFQfcUBgPRQ+9t/UKvy9OnTcomvMI0bN1Yqkycbb78v8njDwsIKvRIi9+zZM+k9j4iIwJQpU5Cenl5o+7S0NKXEqTSjwPXo0QOzZ8/G6tWr4evrCz09Pbi7u6NXr14ICAiQEnkA0sAUhc1hJC9PSkpS22/t2rUxaNAgbNu2DYcOHZIShoiICBQUFMDPz0/h6op8X44dO7bIpEzVe6/J0fHkcX766adFDjSTnZ2tVFa/fn2FK1Rve/nyJXx8fHDs2LFC11ke56W699jCwgLm5uZ4+fIlnj9/Ln12yhX33CCi8sHEiYg0rk2bNoiKisLFixelW4jKw99HoPs7Vb9IF6euvON4W0hICCIiItCqVSusXLlSepBeX18fcXFxaNGiBYQQ5RbbuHHjcP78eYSHh2P58uXIy8vDb7/9BkNDQ4WH79/ejhEjRijcwvh3FT30eXHfG3m8bdq0QevWrYtsK/+DOSEhQRp9cPXq1RgwYABsbGykq1GdO3fGmTNnVL4HRkZGxd0EBSEhIXj//fexZ88eHD16FFFRUTh37hxWrlyJiIgIDB8+vFjrKemD/+PGjcO2bdsQHh4uJU6FJc3yfdm3b1+l2xjfpmpOotLul9KQx+nl5QV7e/sSLVtUnJ988gmOHTsGb29vLFu2DC1btoSFhQV0dXVx5MgR9OnTp1zPy6IU9T6X5+cWEanHxImING7AgAH47rvvsG3bNqxcubJEI+sZGBgAgMqrA5qa76moGEoax65duwC8+eXfxcVFoe7OnTuljLBwo0ePxpw5cxAREYEvv/wSR44cQUpKCoYNG6Y08lnjxo1x8+ZNLFiwQLrFrDKT//ru5eWlchQ1VQ4cOICcnBzMmzcPs2bNUqqviPcAAFq0aIH58+dj/vz5yM7OxrfffouPP/4Y06ZNkxIn+Uh+CQkJKtchv9pS3Alj+/Xrh7p162Lv3r1IT09HYmIiLl26BDc3Nzg7Oyu0le/LSZMmFTuR0wZ5nEOHDsXcuXPLbb27du2Crq4u9u7di9q1ayvUlecxoe49fvnyJV68eAFjY+NSjUxIROWLP1UQkcb17dsXLi4uePDgAf71r38V2fbVq1e4du2a9Fr+nFFcXJxS2z/++KN8Ay1EUTHExcUhMTGx2Ot6/vw5ANW33Pz2228ql5EnbqrmJFLH2toaPXv2REJCAqKiogq94gAAvXr1AvB/yV1l161bN+jq6uL3338v9mAeRe3/kydP4smTJ+UaoypGRkaYN28eGjZsiJSUFCQnJwP4v+eICruNUz4oh7rhyOX09fUxcuRIZGZmYvfu3VXmvdfX1y/0WK+oOJ8/f47atWsrJU1A+Z6XHh4eMDY2RmxsrMpnpuTvsaenJ68uEVUCPAuJSONkMhk2b94MIyMjfPbZZ1i4cCEyMjIU2gghsHfvXrRt2xbnz5+Xyr29vQEAwcHByMzMlMqPHTuG1atXayT+du3awcTEBAcPHkRsbKxU/vTpU0yaNKlEt+rJH+T+4YcfFMq3b9+On3/+WeUy8l+pizsB6t/Jb48MDQ3Fnj17YG5ujoEDByq1e//991G/fn2sXLkSoaGhStuVl5eHw4cP488//yxVHOXNxsYGAQEBuHfvHnx9fVUmPbdu3VIYhU6+/zdv3qxwDCYlJSmMwFhedu/ejZiYGKXy2NhYPHnyBKamptJodKNGjYK1tTVOnz4tjQ4n98033+DChQuwsbEp0RWht0fXi4iIgI6ODnx9fZXaDR8+HM7OztiyZQuCgoKUnpcRQiAqKgpRUVHF7ru0GjVqhCdPnqic1LVDhw7o1asXoqKiMH36dLx69UqpzeXLl3Ho0KES9enk5ITnz5/j119/VSj/z3/+g+PHjxcaJ1Cy87JWrVoICAhAQUEBpk+frnAMxsXF4YsvvgDwZgJrIqoEtDcSOhHVdKdPn5bmqTExMRE9evQQfn5+YsCAAVK5kZGROHr0qLRMZmamaNGihQAgmjZtKoYPHy46dOggdHR0xLx584qcx0nVvEeFzXsjV9jcMkuWLJHi69Onj+jbt6+oU6eO6Ny5s+jUqVOx56OJjIwUurq6AoDw8PAQvr6+0twzhW1PUlKSMDIyErq6uqJv374iICBABAYGSvO5FDaPk1xaWpowMTGR5rIJDAxU2U4IIc6cOSMsLS0FANGkSRPRr18/4efnJ7p37y4sLCwEALFr165Cl39baedxUqWwbczMzBS9evUSAEStWrWEp6en8PX1FYMHD5bmtxkyZIjU/vXr18LFxUWaE2f48OFiwIABwsTERHTu3Fl07txZ6b1Ud8wUZdasWQKAsLGxEQMHDhR+fn6ia9eu0jEQHBys0P7o0aPC2NhY4fhwc3MTAISpqanSvEzqYisoKJD2KwDRo0ePQmONi4sTzZo1EwBE/fr1Rc+ePYWfn5/o3bu3NL/Qf/7zn3LZL0IUfq7NnDlTABDNmjUTY8eOFYGBgWLlypVS/ZMnT6R9YmFhIbp27Sp9jjRp0kSa4+ltqs7Ft23evFnaR126dBG+vr7C2dlZ6OjoiNmzZ6s89i5cuCBkMpkwMjISQ4YMEYGBgSIwMFA8ffpUCFH4PE6vXr0SHh4e0n4eOXKk6N+/vzAyMhIAxIcffqgUX2nODSIqOyZORKRVaWlp4quvvhLe3t7CyspK6OnpCQsLC9GhQwexdOlScf/+faVlHjx4IHx9fUWdOnWEsbGxaNu2rdi2bZvaCXDLM3EqKCgQq1atEg4ODkJfX180btxYzJ07V2RkZJR4Is8zZ86I7t27izp16ggzMzPRuXNnsWPHjiJjO3z4sPD09BSmpqbSH3jyxKM4fzj5+vpKyx07dqzQdkII8ejRIzF//nzh4uIiTExMhImJibC3txdDhgwRGzduFGlpaUUuL6eJxEmIN5O3btq0SXTv3l3UrVtX6Ovri0aNGolOnTqJZcuWiZs3byq0f/bsmZg2bZqws7MThoaGonnz5uKTTz4p9L0sS4Lwv//9T8ydO1e0a9dO1K9fXxgaGgpbW1sxaNAghR8I3vbnn38KX19fYW1tLfT19UXDhg3FuHHjFCY+LUlsCxculN779evXFxnvixcvxBdffCHc3d2FqampMDIyEnZ2dqJPnz7iu+++EykpKSXquyiFnTfp6elixowZokmTJkJPT09lH1lZWeKbb74RnTt3Fubm5sLAwEA0adJEeHt7i1WrVil9jqhLnIQQYv/+/aJjx47CzMxMWFhYiJ49e4oTJ04Ueext2bJFuLu7S8nu29tT1MTZ6enpYtmyZcLZ2VkYGhoKMzMz4eXlJcLDw1XGxsSJSDtkQmhoWBgiIiIiIqIqis84ERERERERqcHEiYiIiIiISA0mTkRERERERGowcSIiIiIiIlKDiRMREREREZEaTJyIiIiIiIjUYOJERERERESkBhMnIiIiIiIiNZg4ERERERERqcHEiYiIiIiISA0mTkRERERERGowcSIiIiIiIlKDiRMREREREZEaTJyIiIiIiIjUYOJERERERESkBhMnIiIiIiIiNZg4ERERERERqcHEiYiIiIiISI3/B2y1UM+tdTzGAAAAAElFTkSuQmCC\",\n      \"text/plain\": [\n       \"<Figure size 960x720 with 1 Axes>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"# Plot linear residuals\\n\",\n    \"n = 0\\n\",\n    \"lin_label = \\\"Linear residual\\\"\\n\",\n    \"for linear_residuals in all_linear_residuals:\\n\",\n    \"    plt.semilogy(\\n\",\n    \"        linear_residuals.index + n,\\n\",\n    \"        linear_residuals[\\\"Residual\\\"]\\n\",\n    \"        / all_linear_residuals[0][\\\"Residual\\\"].iloc[0],\\n\",\n    \"        color=\\\"black\\\",\\n\",\n    \"        label=lin_label,\\n\",\n    \"        marker=\\\".\\\",\\n\",\n    \"        markersize=2.5,\\n\",\n    \"        zorder=30,\\n\",\n    \"    )\\n\",\n    \"    n += len(linear_residuals) - 1\\n\",\n    \"    lin_label = None\\n\",\n    \"\\n\",\n    \"# Plot nonlinear residuals\\n\",\n    \"nonlin_xticks = [0] + list(\\n\",\n    \"    np.cumsum([len(l) - 1 for l in all_linear_residuals])\\n\",\n    \")\\n\",\n    \"plt.semilogy(\\n\",\n    \"    nonlin_xticks,\\n\",\n    \"    nonlinear_residuals[\\\"Residual\\\"] / nonlinear_residuals[\\\"Residual\\\"].iloc[0],\\n\",\n    \"    color=\\\"black\\\",\\n\",\n    \"    ls=\\\"dotted\\\",\\n\",\n    \"    marker=\\\".\\\",\\n\",\n    \"    label=\\\"Nonlinear residual\\\",\\n\",\n    \"    zorder=40,\\n\",\n    \")\\n\",\n    \"plt.xticks(nonlin_xticks)\\n\",\n    \"xticks = (\\n\",\n    \"    range(len(all_linear_residuals))\\n\",\n    \"    if nonlinear_residuals is None\\n\",\n    \"    else nonlin_xticks\\n\",\n    \")\\n\",\n    \"\\n\",\n    \"plt.xlabel(\\\"Cumulative linear solver iteration\\\")\\n\",\n    \"plt.legend();\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"The plot shows the convergence of the nonlinear solver (dotted line), along with\\n\",\n    \"the convergence of the linear solver that runs in each nonlinear iteration\\n\",\n    \"(solid line). A few things to note:\\n\",\n    \"\\n\",\n    \"- The residual converges down to almost machine precision, but that doesn't\\n\",\n    \"  reflect the discretization error of the solution. It only shows that we have\\n\",\n    \"  solved the discretized problem very accurately. To get an idea of the\\n\",\n    \"  discretization error we would have to look at quantities such as constraint\\n\",\n    \"  norms.\\n\",\n    \"- The Newton-Raphson nonlinear solver converges slowly at first, and then begins\\n\",\n    \"  to converge quadratically once we are closer to the solution and hence the\\n\",\n    \"  linearization is more accurate.\\n\",\n    \"- The linear solver needs only a few steps to converge, which is a feature of\\n\",\n    \"  our multigrid-Schwarz preconditioner.\\n\",\n    \"\\n\",\n    \"You can read more about the elliptic solver in this paper:\\n\",\n    \"\\n\",\n    \"- N. L. Vu _et al_., A scalable elliptic solver with task-based parallelism for\\n\",\n    \"  the SpECTRE numerical relativity code (2022),\\n\",\n    \"  [arXiv:2111.06767](https://arxiv.org/abs/2111.06767)\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": []\n  }\n ],\n \"metadata\": {\n  \"interpreter\": {\n   \"hash\": \"5c837ee96a6a314af6ca82ef816f2b1d9c1a2b96687b8bdeaa8036bfb18cb0dc\"\n  },\n  \"kernelspec\": {\n   \"display_name\": \"Python 3.9.10 ('spectre')\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.9.10\"\n  },\n  \"orig_nbformat\": 4\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "docs/Examples/Examples.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Examples {#examples}\n\n- \\subpage example_bbh_id\n"
  },
  {
    "path": "docs/Examples/plots.mplstyle",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Matplotlib style suitable for notebooks that get rendered in our documentation\n#\n# These settings configure matplotlib plots so they look good in our online\n# documentation, and have a consistent style. In a notebook, or when plotting\n# in a script, apply the style like this:\n#\n#   import matplotlib.pyplot as plt\n#   plt.style.use('path/to/plots.mplstyle')\n\nfigure.dpi: 150\nfigure.facecolor: white\n"
  },
  {
    "path": "docs/Gallery.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n\\cond NEVER\nInstructions for adding images or videos:\n\n1. Send your media files to a SpECTRE core dev (see CONTRIBUTING.md for details\n   and communication channels). They will place the files in this public Google\n   Drive folder:\n   https://drive.google.com/drive/folders/1xvLplF_pPlkADGfgFNaW_ByVATFv61m-\n2. Open the media files in Google Drive and get their file IDs. To do this,\n   select [...] > Open in new window > Copy the <FILE_ID> in the URL of the form\n   https://drive.google.com/file/d/<FILE_ID>/view.\n3. Add a section to this page.\n   - Embed images like this:\n     <img src=\"https://drive.google.com/thumbnail?id=<FILE_ID>&sz=w<WIDTH>\"/>\n     Replace <WIDTH> with the width you want your image to have on the page.\n     Google Drive will automatically provide an image of this size.\n   - Embed videos like this:\n     \\htmlonly\n     <iframe src=\"https://drive.google.com/file/d/18KIEoK8oH6WDifx0cyFkl2ncKijNtaxs/preview\" width=\"640\" height=\"480\" allow=\"autoplay\" allowfullscreen></iframe>\n     \\endhtmlonly\n4. Open a pull request to contribute your changes (see CONTRIBUTING.md for\n   details).\n\\endcond\n\n# Gallery {#gallery}\n\nThis page highlights some visualizations of SpECTRE simulations.\n\n\\note We're always happy to feature images and videos created by the SpECTRE\ncommunity on this page. See [this page on\nGitHub](https://github.com/sxs-collaboration/spectre/blob/develop/docs/Gallery.md)\nfor instructions.\n\n## Binary black hole mergers {#gallery_bbh}\n\nSpECTRE can simulate black holes that orbit each other and eventually merge,\nemitting gravitational waves.\nSpECTRE uses the Generalized Harmonic formulation of\nEinstein's equations of general relativity to solve this problem. Since we\nexpect the solution of Einstein's equations to be smooth for the BBH problem,\nwe represent our solution using the Discontinuous Galerkin (DG) method because\nof its ability to represent smooth functions to high accuracy. Also, DG allows\nSpECTRE to parallelize the BBH problem.\n\nPublications:\n\n- Lovelace, Nelli, et al. (2024). Simulating binary black hole mergers using\n  discontinuous Galerkin methods. \\cite Lovelace2024wra\n\n\\htmlonly\n<figure>\n<img src=\"https://drive.google.com/thumbnail?id=1Ccz_ZDPTpIsLXh_lGotGBdSLEnc7a__f&sz=w1200\"/>\n<figcaption>\nApparent horizons of an equal mass non-spinning BBH. The\ncolorful surface represents the lapse value in the equatorial plane and the\narrows depict the shift vector. Image credit: Alex Carpenter, CSUF\n</figcaption>\n</figure>\n\\endhtmlonly\n\n\\htmlonly\n<figure>\n<img src=\"https://drive.google.com/thumbnail?id=1Hf1lkmm9oqZ27Qhbi-McUMbSuP4yR7FW&sz=w800\"/>\n<figcaption>\nOverview of a binary black hole simulation in SpECTRE. Upper left panel:\ncomputational grid and shape of black hole horizons during merger. A common\nhorizon has formed (blue) that envelops the two original horizons (black). Upper\nright panel: trajectories of the black holes until merger.\nThis inspiral is 18 orbits long and approximately circular.\nBottom panel: gravitational waveform extracted with CCE (see below).\nImage credit: Geoffrey Lovelace, CSUF\n</figcaption>\n</figure>\n\\endhtmlonly\n\n## Binary black hole initial data\n\nSpECTRE can generate initial data to start simulations of merging black holes.\nThis problem involves solving the elliptic constraint sector of the Einstein\nequations for a slice of spacetime that contains two black holes with the\nrequested parameters. SpECTRE uses the \\ref ::Xcts \"XCTS\" formulation with a\nnon-conformally-flat background defined by the\n\\ref ::Xcts::AnalyticData::Binary \"superposed\"\n\\ref gr::Solutions::KerrSchild \"Kerr-Schild\" formalism\nto reach high spins. Black holes are represented by excisions and\n\\ref ::Xcts::BoundaryConditions::ApparentHorizon \"boundary conditions\".\n\nPublications:\n\n- Vu et al. (2022). A scalable elliptic solver with task-based parallelism for\n  the SpECTRE numerical relativity code. \\cite Vu2021coj\n- Vu (2024). A discontinuous Galerkin scheme for elliptic equations on\n  extremely stretched grids. \\cite Vu2024cgf\n\n\\htmlonly\n<figure>\n<img src=\"https://drive.google.com/thumbnail?id=1lruXf3G4KCZ57CSjkuTE6B-bGPQY1ZyQ&sz=w800\"/>\n<figcaption>\nAn initial slice of spacetime containing two black holes in orbit around each\nother. Shown is the lapse variable. The two black holes are represented as\nboundary conditions on excised regions of the computational domain.\nImage credit: Nils Vu, Caltech\n</figcaption>\n</figure>\n\\endhtmlonly\n\n## Cauchy-characteristic evolution (CCE) {#gallery_cce}\n\nSpECTRE implements a novel Cauchy-characteristic evolution (CCE) system for\nextracting gravitational waveforms from our simulations. It evolves the\nEinstein equations on null slices to infinity, which is more accurate than\nextrapolation and allows us to extract the gravitational memory effect.\nThe CCE waveform extraction is publicly available as a standalone module.\n\nTutorial:\n\n- \\ref tutorial_cce\n\nPublications:\n\n- Moxon et al. (2023). SpECTRE Cauchy-characteristic evolution system for rapid,\n  precise waveform extraction. \\cite Moxon2021gbv\n- Moxon et al. (2020). Improved Cauchy-characteristic evolution system for\n  high-precision numerical relativity waveforms. \\cite Moxon2020gha\n\n## Binary neutron star mergers\n\nSpECTRE can simulate merging neutron stars and other general-relativistic\nmagneto-hydrodynamic (GRMHD) problems with dynamic gravity. Our DG-FD hybrid\nscheme accelerates smooth regions of the grid with high-order spectral methods\n(see \\ref gallery_dgfd).\n\nPublications:\n\n- Deppe et al. (2024). Binary neutron star mergers using a discontinuous\n  Galerkin-finite difference hybrid method. \\cite Deppe2024ckt\n\n\\htmlonly\n<figure>\n<iframe src=\"https://drive.google.com/file/d/1kLx00pjDBmD49bpUcfHRcZNXR9jE8Sxb/preview\" width=\"640\" height=\"480\" allow=\"autoplay\" allowfullscreen></iframe>\n<figcaption>\nSimulation of two merging neutron stars. The colors show density contours.\nVideo credit: Nils Vu, Caltech\n</figcaption>\n</figure>\n\\endhtmlonly\n\n## Curved and moving meshes with control systems\n\nOur computational domains in SpECTRE are designed to adapt to the geometry of\nthe problems we want to solve. They can be curved, e.g. to wrap around excision\nregions in binary black hole problems (see \\ref gallery_bbh) or to resolve the\nwavezone in binary neutron star merger. They can also rotate and deform in time\nusing control systems, which reactively adjust coordinate maps to track the\nposition and shape of the black hole excisions or neutron stars.\n\n## Adaptive mesh refinement\n\nOur discontinuous Galerkin methods allow two types of mesh refinement: splitting\nelements in half along any dimension (h-refinement) or increasing their\npolynomial expansion order (p-refinement). The former allows us to distribute\ncomputational cost to supercomputers, while the latter allows us to use these\nresources efficiently by decreasing the numerical error exponentially with the\nnumber of grid points where the solution is smooth. Our adaptive mesh refinement\ntechnology decides which type of refinement to apply in each region of the\ndomain.\n\n## DG-FD hybrid method {#gallery_dgfd}\n\nOur hydrodynamical simulations use a discontinuous Galerkin-finite difference\n(DG-FD) hybrid method: smooth regions of the simulation are evolved with an\nefficient DG scheme and non-smooth regions fall back to a robust FD method.\nShocks and discontinuities on the grid are tracked with a troubled-cell\nindicator (TCI) to switch between DG and FD. This approach accelerates our\nsimulations by reducing the computational resources spent on smooth regions of\nthe grid, e.g. when evolving inspiral binary neutron stars and their\ngravitational radiation.\n\nPublications:\n\n- Deppe et al. (2022). A high-order shock capturing discontinuous\n  Galerkin-finite difference hybrid method for GRMHD. \\cite Deppe2021ada\n- Deppe et al. (2022). Simulating magnetized neutron stars with discontinuous\n  Galerkin methods. \\cite Deppe2021bhi\n\n\\htmlonly\n<figure>\n<iframe src=\"https://drive.google.com/file/d/1gSRfo2-V6HLK1XwpZ-twrp4f-4G8n08V/preview\" width=\"640\" height=\"480\" allow=\"autoplay\" allowfullscreen></iframe>\n<figcaption>\nSimulation of the Kelvin-Helmholtz instability (KHI). Squares indicate cells\nthat have switched to a finite-difference method. They track shocks and\ndiscontinuities in the solution. The rest of the domain uses an efficient\nDG method.\nVideo credit: Nils Deppe, Cornell University\n</figcaption>\n</figure>\n\\endhtmlonly\n\n## Binary black holes in scalar Gauss-Bonnet gravity\n\nSpECTRE can generate initial data for binary black holes in scalar Gauss-Bonnet\ngravity, evolve the modified Einstein equations, and extract the gravitational\nand scalar radiation.\n\nPublications:\n\n- Nee et al. (2024). Quasistationary hair for binary black hole initial data in\n  scalar Gauss-Bonnet gravity \\cite Nee2024bur\n- Lara et al. (2024). Scalarization of isolated black holes in scalar\n  Gauss-Bonnet theory in the fixing-the-equations approach. \\cite Lara2024rwa\n\n\\htmlonly\n<figure>\n<img src=\"https://drive.google.com/thumbnail?id=1uhmrprFOE9xOWaUQntgE_4J9DgniFBLe&sz=w800\"/>\n<figcaption>\nBinary black hole initial data in scalar Gauss-Bonnet gravity, in a\nconfiguration where the two black holes have opposite charge.\nThe scalar field is solved such that it is in equilibrium with the gravity\nbackground, minimizing initial transients in the evolution and giving control\nover the simulation parameters.\nImage credit: Peter James Nee, MPI for Gravitational Physics Potsdam, Germany\n</figcaption>\n</figure>\n\\endhtmlonly\n\n## Thermal noise in gravitational wave detectors\n\nWe have applied the SpECTRE technology to an interdisciplinary problem,\nsimulating the Brownian thermal noise in the mirrors of interferometric\ngravitational-wave detectors at unprecedented accuracy. It uses the SpECTRE\nelliptic solver \\cite Vu2021coj to solve an elasticity problem, which connects\nto the thermal noise problem through the fluctuation dissipation theorem.\n\nPublications:\n\n- Vu et al. (2024). High-accuracy numerical models of Brownian thermal noise in\n  thin mirror coatings. \\cite Vu2023thn\n"
  },
  {
    "path": "docs/GroupDefs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines all group definitions\n\n#pragma once\n\n/*!\n * \\defgroup ActionsGroup Actions\n * \\brief A collection of steps used in algorithms.\n */\n\n/*!\n * \\defgroup AderGroup ADER\n * \\brief Functions and classes needed for ADER (Arbitrary high-order using\n * DERivatives) time integration\n */\n\n/*!\n * \\defgroup AmrGroup Adaptive Mesh Refinement\n * \\brief Functions and classes specific to hp-adaptive mesh refinement.\n */\n\n/*!\n * \\defgroup AnalyticDataGroup Analytic Data\n * \\brief Analytic data used to specify (for example) initial data to the\n * equations implemented in \\ref EvolutionSystemsGroup.\n */\n\n/*!\n * \\defgroup AnalyticSolutionsGroup Analytic Solutions\n * \\brief Analytic solutions to the equations implemented in \\ref\n * EvolutionSystemsGroup and \\ref EllipticSystemsGroup.\n */\n\n/*!\n * \\defgroup BoundaryConditionsGroup Boundary Conditions\n * A collection of boundary conditions used for evolutions.\n */\n\n/*!\n * \\defgroup CharmExtensionsGroup Charm++ Extensions\n * \\brief Classes and functions used to make Charm++ easier and safer to use.\n */\n\n/*!\n * \\defgroup ComputationalDomainGroup  Computational Domain\n * \\brief The building blocks used to describe the computational domain.\n *\n * ### Description\n * The VolumeDim-dimensional computational Domain is constructed from a set of\n * non-overlapping Block%s.  Each Block is a distorted VolumeDim-dimensional\n * hypercube.  Each codimension-1 boundary of a Block is either part of the\n * external boundary of the computational domain, or is identical to a boundary\n * of one other Block.  Each Block is subdivided into one or more Element%s\n * that may be changed dynamically if AMR is enabled.\n */\n\n/*!\n * \\defgroup ConservativeGroup Conservative System Evolution\n * \\brief Contains generic functions used for evolving conservative\n * systems.\n */\n\n/*!\n * \\defgroup ConstantExpressionsGroup Constant Expressions\n * \\brief Contains an assortment of constexpr functions\n *\n * ### Description\n * Contains an assortment of constexpr functions that are useful for\n * metaprogramming, or efficient mathematical computations, such as\n * exponentiating to an integer power, where the power is known at compile\n * time.\n */\n\n/*!\n * \\defgroup ControlSystemGroup Control System\n * \\brief Contains control system elements\n *\n * The control system manages the time-dependent mapping between frames, such as\n * the fixed computational frame (grid frame) and the inertial frame. The\n * time-dependent parameters of the mapping are adjusted by a feedback control\n * system in order to follow the dynamical evolution of objects such as horizons\n * of black holes or surfaces of neutron stars. For example, in binary black\n * hole simulations the map is typically a composition of maps that include\n * translation, rotation, scaling, shape, etc.\n * Each map under the governance of the control system has an associated\n * time-dependent map parameter \\f$\\lambda(t)\\f$ that is a piecewise Nth order\n * polynomial. At discrete times (called reset times), the control system resets\n * the Nth time derivative of \\f$\\lambda(t)\\f$ to a new constant value, in order\n * to minimize an error function \\f$Q(t)\\f$ that is specific to each map. At\n * each reset time, the Nth derivative of \\f$\\lambda(t)\\f$ is set to a function\n * \\f$U(t)\\f$, called the control signal, that is determined by \\f$Q(t)\\f$ and\n * its time derivatives and time integral. Note that \\f$\\lambda(t)\\f$,\n * \\f$U(t)\\f$, and \\f$Q(t)\\f$ can be vectors.\n *\n * The key components of the control system are:\n * - FunctionsOfTime: each map has an associated FunctionOfTime that represents\n *   the map parameter \\f$\\lambda(t)\\f$ and relevant time derivatives.\n * - ControlError: each map has an associated ControlError that computes\n *   the error, \\f$Q(t)\\f$. Note that for each map, \\f$Q(t)\\f$ is defined to\n *   follow the convention that \\f$dQ = -d \\lambda\\f$ as \\f$Q \\rightarrow 0\\f$.\n * - Averager: an averager can be used to average out the noise in the 'raw'\n *   \\f$Q(t)\\f$ returned by the ControlError.\n * - Controller: the map controller computes the control signal \\f$U(t)\\f$ from\n *   \\f$Q(t)\\f$ and its time integral and time derivatives.\n *   The control is accomplished by setting the Nth derivative of\n *   \\f$\\lambda(t)\\f$ to \\f$U(t)\\f$. Two common controllers are PID\n *   (proportional/integral/derivative)\n *   \\f[U(t) = a_{0}\\int_{t_{0}}^{t} Q(t') dt'+a_{1}Q(t)+a_{2}\\frac{dQ}{dt}\\f]\n *   or\n *   PND (proportional/N derivatives)\n *   \\f[ U(t) = \\sum_{k=0}^{N} a_{k} \\frac{d^kQ}{dt^k} \\f]\n *   The coefficients \\f$ a_{k} \\f$ in the computation of \\f$U(t)\\f$ are chosen\n *   at each time such that the error \\f$Q(t)\\f$ will be critically damped\n *   on a timescale of \\f$\\tau\\f$ (the damping time),\n *   i.e. \\f$Q(t) \\propto e^{-t/\\tau}\\f$.\n * - TimescaleTuner: each map has a TimescaleTuner that dynamically adjusts\n *   the damping timescale \\f$\\tau\\f$ appropriately to keep the error \\f$Q(t)\\f$\n *   within some specified error bounds. Note that the reset time interval,\n *   \\f$\\Delta t\\f$, is a constant fraction of this damping timescale,\n *   i.e. \\f$\\Delta t = \\alpha \\tau\\f$ (empirically, we have found\n *   \\f$\\alpha=0.3\\f$ to be a good choice).\n *\n *\n * For additional details describing our control system approach, see\n * \\cite Hemberger2012jz.\n */\n\n/*!\n * \\defgroup CoordinateMapsGroup  Coordinate Maps\n * \\brief Functions for mapping coordinates between different frames\n *\n * Coordinate maps provide the maps themselves, the inverse maps, along\n * with the Jacobian and inverse Jacobian of the maps.\n */\n\n/*!\n * \\defgroup CoordMapsTimeDependentGroup  Coordinate Maps, Time-dependent\n * \\brief Functions for mapping time-dependent coordinates between different\n * frames\n *\n * Coordinate maps provide the maps themselves, the inverse maps, the Jacobian\n * and inverse Jacobian of the maps, and the frame velocity (time derivative of\n * the map)\n */\n\n/*!\n * \\defgroup DataBoxGroup DataBox\n * \\brief Documentation, functions, metafunctions, and classes necessary for\n * using DataBox\n *\n * DataBox is a heterogeneous compile-time associative container with lazy\n * evaluation of functions. DataBox can not only store data, but can also store\n * functions that depend on other data inside the DataBox. The functions will be\n * evaluated when the data they return is requested. The result is cached, and\n * if a dependency of the function is modified the cache is invalidated.\n *\n * #### Simple and Compute Tags and Their Items\n *\n * The compile-time keys are `struct`s called tags, while the values are called\n * items. Tags are quite minimal, containing only the information necessary to\n * store the data and evaluate functions. There are two different types of tags\n * that a DataBox can hold: simple tags and compute tags. Simple tags are for\n * data that is inserted into the DataBox at the time of creation, while compute\n * tags are for data that will be computed from a function when the compute item\n * is retrieved. If a compute item is never retrieved from the DataBox then it\n * is never evaluated.\n *\n * Simple tags must have a member type alias `type` that is the type of the data\n * to be stored and a `static std::string name()` method that returns the name\n * of the tag. Simple tags must inherit from `db::SimpleTag`.\n *\n * Compute tags must also have a `static std::string name()` method that returns\n * the name of the tag, but they cannot have a `type` type alias. Instead,\n * compute tags must have a static member function or static member function\n * pointer named `function`. `function` can be a function template if necessary.\n * The `function` must take all its arguments by `const` reference. The\n * arguments to the function are retrieved using tags from the DataBox that the\n * compute tag is in. The tags for the arguments are set in the member type\n * alias `argument_tags`, which must be a `tmpl::list` of the tags corresponding\n * to each argument. Note that the order of the tags in the `argument_list` is\n * the order that they will be passed to the function. Compute tags must inherit\n * from `db::ComputeTag`.\n *\n * Here is an example of a simple tag:\n *\n * \\snippet Test_DataBox.cpp databox_tag_example\n *\n * and an example of a compute tag with a function pointer:\n *\n * \\snippet Test_DataBox.cpp databox_mutating_compute_item_tag\n *\n * If the compute item's tag is inline then the compute item is of the form:\n *\n * \\snippet Test_DataBox.cpp compute_item_tag_function\n *\n * Compute tags can also have their functions be overloaded on the type of its\n * arguments:\n *\n * \\snippet Test_DataBox.cpp overload_compute_tag_type\n *\n * or be overloaded on the number of arguments:\n *\n * \\snippet Test_DataBox.cpp overload_compute_tag_number_of_args\n *\n * Compute tag function templates are implemented as follows:\n *\n * \\snippet Test_DataBox.cpp overload_compute_tag_template\n *\n * Finally, overloading, function templates, and variadic functions can be\n * combined to produce extremely generic compute tags. The below compute tag\n * takes as template parameters a parameter pack of integers, which is used to\n * specify several of the arguments. The function is overloaded for the single\n * argument case, and a variadic function template is provided for the multiple\n * arguments case. Note that in practice few compute tags will be this complex.\n *\n * #### Subitems and Prefix Tags\n *\n * A simple or compute tag might also hold a collection of data, such as a\n * container of `Tensor`s. In many cases you will want to be able to retrieve\n * individual elements of the collection from the DataBox without having to\n * first retrieve the collection. The infrastructure that allows for this is\n * called *Subitems*. The subitems of the parent tag must refer to a subset of\n * the data inside the parent tag, e.g. one `Tensor` in the collection. If the\n * parent tag is `Parent` and the subitems tags are `Sub<0>, Sub<1>`, then when\n * `Parent` is added to the DataBox, so are `Sub<0>` and `Sub<1>`. This means\n * the retrieval mechanisms described below will work on `Parent`, `Sub<0>`, and\n * `Sub<1>`.\n *\n * Subitems specify requirements on the tags they act on. For example, there\n * could be a requirement that all tags with a certain type are to be treated as\n * a Subitems. Let's say that the `Parent` tag holds a `Variables`, and\n * `Variables` can be used with the Subitems infrastructure to add the nested\n * `Tensor`s. Then all tags that hold a `Variables` will have their subitems\n * added into the DataBox. To add a new type as a subitem the `db::Subitems`\n * struct must be specialized. See the documentation of `db::Subitems` for more\n * details.\n *\n * The DataBox also supports *prefix tags*, which are commonly used for items\n * that are related to a different item by some operation. Specifically, say\n * you have a tag `MyTensor` and you want to also have the time derivative of\n * `MyTensor`, then you can use the prefix tag `dt` to get `dt<MyTensor>`. The\n * benefit of a prefix tag over, say, a separate tag `dtMyTensor` is that prefix\n * tags can be added and removed by the compute tags acting on the original tag.\n * Prefix tags can also be composed, so a second time derivative would be\n * `dt<dt<MyTensor>>`. The net result of the prefix tags infrastructure is that\n * the compute tag that returns `dt<MyTensor>` only needs to know its input\n * tags, it knows how to name its output based off that. In addition to the\n * normal things a simple or a compute tag must hold, prefix tags must have a\n * nested type alias `tag`, which is the tag being prefixed. Prefix tags must\n * also inherit from `db::PrefixTag` in addition to inheriting from\n * `db::SimpleTag` or `db::ComputeTag`.\n *\n * #### Creating a DataBox\n *\n * You should never call the constructor of a DataBox directly. DataBox\n * construction is quite complicated and the helper function `db::create`\n * should be used instead. `db::create` is used to construct a\n * new DataBox. It takes two typelists as explicit template parameters, the\n * first being a list of the simple tags to add and the second being a list of\n * compute tags to add. If no compute tags are being added then only the simple\n * tags list must be specified. The tags lists should be passed as\n * `db::create<db::AddSimpleTags<simple_tags...>,\n * db::AddComputeTags<compute_tags...>>`. The arguments to `db::create` are the\n * initial values of the simple tags and must be passed in the same order as the\n * tags in the `db::AddSimpleTags` list. If the type of an argument passed to\n * `db::create` does not match the type of the corresponding simple tag a static\n * assertion will trigger. Here is an example of how to use `db::create`:\n *\n * \\snippet Test_DataBox.cpp create_databox\n *\n * #### Accessing and Mutating Items\n *\n * To retrieve an item from a DataBox use the `db::get` function. `db::get`\n * will always return a `const` reference to the object stored in the DataBox\n * and will also have full type information available. This means you are able\n * to use `const auto&` when retrieving tags from the DataBox. For example,\n * \\snippet Test_DataBox.cpp using_db_get\n *\n * If you want to mutate the value of a simple item in the DataBox use\n * `db::mutate`. Any compute item that depends on the mutated item will have its\n * cached value invalidated and be recomputed the next time it is retrieved from\n * the DataBox. `db::mutate` takes a parameter pack of tags to mutate as\n * explicit template parameters, a `gsl::not_null` of the DataBox whose items\n * will be mutated, an invokable, and extra arguments to forward to the\n * invokable. The invokable takes the arguments passed from the DataBox by\n * `const gsl::not_null` while the extra arguments are forwarded to the\n * invokable. The invokable is not allowed to retrieve anything from the\n * DataBox, so any items must be passed as extra arguments using `db::get` to\n * retrieve them. For example,\n *\n * \\snippet Test_DataBox.cpp databox_mutate_example\n *\n * In addition to retrieving items using `db::get` and mutating them using\n * `db::mutate`, there is a facility to invoke an invokable with tags from the\n * DataBox. `db::apply` takes a `tmpl::list` of tags as an explicit template\n * parameter, will retrieve all the tags from the DataBox passed in and then\n * invoke the  invokable with the items in the tag list. Similarly,\n * `db::mutate_apply` invokes the invokable but allows for mutating some of\n * the tags. See the documentation of `db::apply` and `db::mutate_apply` for\n * examples of how to use them.\n *\n */\n\n/*!\n * \\defgroup DataBoxTagsGroup DataBox Tags\n * \\brief Structures and metafunctions for labeling the contents of DataBoxes\n */\n\n/*!\n * \\defgroup DataStructuresGroup Data Structures\n * \\brief Various useful data structures used in SpECTRE\n */\n\n/*!\n * \\defgroup DgSubcellGroup DG-Subcell\n * \\brief Functions and classes specific to the discontinuous Galerkin method\n * supplemented with a finite volume or finite difference subcell limiter. Can\n * also be thought of as a DG-FD hybrid method.\n */\n\n/*!\n * \\defgroup DiscontinuousGalerkinGroup Discontinuous Galerkin\n * \\brief Functions and classes specific to the Discontinuous Galerkin\n * algorithm.\n */\n\n/*!\n * \\defgroup EllipticSystemsGroup Elliptic Systems\n * \\brief All available elliptic systems\n */\n\n/*!\n * \\defgroup EquationsOfStateGroup Equations of State\n * \\brief The various available equations of state\n */\n\n/*!\n * \\defgroup ErrorHandlingGroup Error Handling\n * Macros and functions used for handling errors\n */\n\n/*!\n * \\defgroup EventsAndTriggersGroup Events and Triggers\n * \\brief Classes and functions related to events and triggers\n */\n\n/*!\n * \\defgroup EvolutionSystemsGroup Evolution Systems\n * \\brief All available evolution systems and information on how to implement\n * evolution systems\n *\n * \\details Actions and parallel components may require an evolution system to\n * expose the following types:\n *\n * - `volume_dim`: The number of spatial dimensions\n * - `variables_tag`: The evolved variables to compute DG volume contributions\n * and fluxes for.\n * - `compute_time_derivative`: A struct that computes the bulk contribution to\n * the DG discretization of the time derivative. Must expose a `tmpl::list` of\n * `argument_tags` and a static `apply` function that takes the following\n * arguments in this order:\n *   - First, the types of the tensors in\n * `db::add_tag_prefix<Metavariables::temporal_id::step_prefix, variables_tag>`\n * (which represent the time derivatives of the variables) as not-null pointers.\n *   - The types of the `argument_tags` as constant references.\n *\n * Actions and parallel components may also require the Metavariables to expose\n * the following types:\n *\n * - `system`: See above.\n * - `temporal_id`: A DataBox tag that identifies steps in the algorithm.\n * Generally use `Tags::TimeStepId`.\n */\n\n/*!\n * \\defgroup ExecutablesGroup Executables\n * \\brief A list of executables and how to use them\n *\n * <table class=\"doxtable\">\n * <tr>\n * <th>Executable Name </th><th>Description </th>\n * </tr>\n * <tr>\n * <td> \\ref ParallelInfoExecutablePage \"ParallelInfo\" </td>\n * <td> Executable for checking number of nodes, cores, etc.</td>\n * </tr>\n * <tr>\n * <td> ExportEquationOfStateForRotNS </td>\n * <td> Exports a 1d equation of state in a table format that the RotNS fortran\n * code can read in.</td>\n * </tr>\n * </table>\n */\n\n/*!\n * \\defgroup FileSystemGroup File System\n * \\brief A light-weight file system library.\n */\n\n/*!\n * \\defgroup FiniteDifferenceGroup Finite Difference\n * \\brief Functions needed for (conservative) finite difference methods.\n */\n\n/*!\n * \\defgroup GeneralRelativityGroup General Relativity\n * \\brief Contains functions used in General Relativistic simulations\n */\n\n/*!\n * \\defgroup HDF5Group HDF5\n * \\brief Functions and classes for manipulating HDF5 files\n */\n\n/*!\n * \\defgroup InitializationGroup Initialization\n * \\brief Actions and metafunctions used for initialization of parallel\n * components.\n */\n\n/*!\n * \\defgroup LimitersGroup Limiters\n * \\brief Limiters to control shocks and surfaces in the solution.\n */\n\n/*!\n * \\defgroup LinearSolverGroup  Linear Solver\n * \\brief Algorithms to solve linear systems of equations\n *\n * \\details In a way, the linear solver is for elliptic systems what time\n * stepping is for the evolution code. This is because the DG scheme for an\n * elliptic system reduces to a linear system of equations of the type\n * \\f$Ax=b\\f$, where \\f$A\\f$ is a global matrix representing the DG\n * discretization of the problem. Since this is one equation for each node in\n * the computational domain it becomes unfeasible to numerically invert the\n * global matrix \\f$A\\f$. Instead, we solve the problem iteratively so that we\n * never need to construct \\f$A\\f$ globally but only need \\f$Ax\\f$ that can be\n * evaluated locally by virtue of the DG formulation. This action of the\n * operator is what we have to supply in each step of the iterative algorithms\n * implemented here. It is where most of the computational cost goes and usually\n * involves computing a volume contribution for each element and communicating\n * fluxes with neighboring elements. Since the iterative algorithms typically\n * scale badly with increasing grid size, a preconditioner \\f$P\\f$ is needed\n * in order to make \\f$P^{-1}A\\f$ easier to invert.\n *\n * \\note The smallest possible residual magnitude the linear solver can reach is\n * the product between the machine epsilon and the condition number of the\n * linear operator that is being inverted. Smaller residuals are numerical\n * artifacts. Requiring an absolute or relative residual below this limit will\n * likely make the linear solver run until it reaches its maximum number of\n * iterations.\n *\n * \\note Remember that when the linear operator \\f$A\\f$ corresponds to a PDE\n * discretization, decreasing the linear solver residual below the\n * discretization error will not improve the numerical solution any further.\n * I.e. the error \\f$e_k=x_k-x_\\mathrm{analytic}\\f$ to an analytic solution\n * will be dominated by the linear solver residual at first, but even if the\n * discretization \\f$Ax_k=b\\f$ was exactly solved after some iteration \\f$k\\f$,\n * the discretization residual\n * \\f$Ae_k=b-Ax_\\mathrm{analytic}=r_\\mathrm{discretization}\\f$ would still\n * remain. Therefore, ideally choose the absolute or relative residual criteria\n * based on an estimate of the discretization residual.\n *\n * In the iterative algorithms we usually don't work with the physical field\n * \\f$x\\f$ directly. Instead we need to apply the operator to an internal\n * variable defined by the respective algorithm. This variable is exposed as the\n * `LinearSolver::Tags::Operand` prefix, and the algorithm expects that the\n * computed operator action is written into\n * `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo,\n * LinearSolver::Tags::Operand<...>>` in each step.\n */\n\n/// \\defgroup LoggingGroup Logging\n/// \\brief Functions for logging progress of running code\n\n/// \\defgroup MathFunctionsGroup Math Functions\n/// \\brief Useful analytic functions\n\n/*!\n * \\defgroup NumericalAlgorithmsGroup Numerical Algorithms\n * \\brief Generic numerical algorithms\n */\n\n/*!\n * \\defgroup NumericalFluxesGroup Numerical Fluxes\n * \\brief The set of available numerical fluxes\n */\n\n/*!\n * \\defgroup ObserversGroup Observers\n * \\brief Observing/writing data to disk.\n */\n\n/*!\n * \\defgroup OptionGroupsGroup Option Groups\n * \\brief Tags used for grouping input file options.\n *\n * An \\ref OptionTagsGroup \"option tag\" can be placed in a group with other\n * option tags to give the input file more structure. To assign a group to an\n * option tag, set its `group` type alias to a struct that provides a help\n * string and may override a static `name()` function:\n *\n * \\snippet Test_Options.cpp options_example_group\n *\n * A number of commonly used groups are listed here.\n *\n * See also the \\ref dev_guide_option_parsing \"option parsing guide\".\n */\n\n/*!\n * \\defgroup OptionParsingGroup Option Parsing\n * Things related to parsing YAML input files.\n */\n\n/*!\n * \\defgroup OptionTagsGroup Option Tags\n * \\brief Tags used for options parsed from the input file.\n *\n * These can be stored in the GlobalCache or passed to the `initialize`\n * function of a parallel component.\n */\n\n/*!\n * \\defgroup ParallelGroup Parallelization\n * \\brief Functions, classes and documentation related to parallelization and\n * Charm++\n *\n * See\n * \\ref dev_guide_parallelization_foundations \"Parallelization infrastructure\"\n * for details.\n */\n\n/*!\n * \\defgroup PeoGroup Performance, Efficiency, and Optimizations\n * \\brief Classes and functions useful for performance optimizations.\n */\n\n/*!\n * \\defgroup PrettyTypeGroup Pretty Type\n * \\brief Pretty printing of types\n */\n\n/*!\n * \\defgroup ProtocolsGroup Protocols\n * \\brief Classes that define metaprogramming interfaces\n *\n * See the \\ref protocols section of the dev guide for details.\n */\n\n/*!\n * \\defgroup PythonBindingsGroup Python Bindings\n * \\brief Classes and functions useful when writing python bindings.\n *\n * See the \\ref spectre_writing_python_bindings \"Writing Python Bindings\"\n * section of the dev guide for details on how to write python bindings.\n */\n\n/*!\n * \\defgroup SpecialRelativityGroup Special Relativity\n * \\brief Contains functions used in special relativity calculations\n */\n\n/*!\n * \\defgroup SpectralGroup Spectral\n * Things related to spectral transformations.\n */\n\n// Note: this group is ordered by how it appears in the rendered Doxygen pages\n// (i.e., \"Spin-weighted...\"), rather than the group's name (i.e., \"Swsh...\").\n/*!\n * \\defgroup SwshGroup Spin-weighted spherical harmonics\n * Utilities, tags, and metafunctions for using and manipulating spin-weighted\n * spherical harmonics\n */\n\n/*!\n * \\defgroup SurfacesGroup Surfaces\n * Things related to surfaces.\n */\n\n/*!\n * \\defgroup TensorGroup Tensor\n * Tensor use documentation.\n */\n\n/*!\n * \\defgroup TensorExpressionsGroup Tensor Expressions\n * Tensor Expressions allow writing expressions of\n * tensors in a way similar to what is used with pen and paper.\n *\n * Tensor expressions are implemented using (smart) expression templates. This\n * allows a domain specific language making expressions such as\n * \\code\n * auto T = evaluate<Indices::_a_t, Indices::_b_t>(F(Indices::_b,\n * Indices::_a));\n * \\endcode\n * possible.\n */\n\n/*!\n * \\defgroup TestingFrameworkGroup Testing Framework\n * \\brief Classes, functions, macros, and instructions for developing tests\n *\n * \\details\n *\n * SpECTRE uses the testing framework\n * [Catch](https://github.com/philsquared/Catch). Catch supports a variety of\n * different styles of tests including BDD and fixture tests. The file\n * `cmake/SpectreAddCatchTests.cmake` parses the source files and adds the found\n * tests to ctest with the correct properties specified by tags and attributes.\n *\n * ### Usage\n *\n * To run the tests, type `ctest` in the build directory. You can specify\n * a regex to match the test name using `ctest -R Unit.Blah`, or run all\n * tests with a certain tag using `ctest -L tag`.\n *\n * ### Comparing double-precision results\n *\n * To compare two floating-point numbers that may differ by round-off, use the\n * helper object `approx`. This is an instance of Catch's comparison class\n * `Approx` in which the relative tolerance for comparisons is set to roughly\n * \\f$10^{-14}\\f$ (i.e. `std::numeric_limits<double>::%epsilon()*100`).\n * When possible, we recommend using `approx` for fuzzy comparisons as follows:\n * \\example\n * \\snippet Test_TestingFramework.cpp approx_default\n *\n * For checks that need more control over the precision (e.g. an algorithm in\n * which round-off errors accumulate to a higher level), we recommend using\n * the `approx` helper with a one-time tolerance adjustment. A comment\n * should explain the reason for the adjustment:\n * \\example\n * \\snippet Test_TestingFramework.cpp approx_single_custom\n *\n * For tests in which the same precision adjustment is re-used many times, a new\n * helper object can be created from Catch's `Approx` with a custom precision:\n * \\example\n * \\snippet Test_TestingFramework.cpp approx_new_custom\n *\n * Note: We provide the `approx` object because Catch's `Approx` defaults to a\n * very loose tolerance (`std::numeric_limits<float>::%epsilon()*100`, or\n * roughly \\f$10^{-5}\\f$ relative error), and so is poorly-suited to checking\n * many numerical algorithms that rely on double-precision accuracy. By\n * providing a tighter tolerance with `approx`, we avoid having to redefine the\n * tolerance in every test.\n *\n * ### Attributes\n *\n * Attributes allow you to modify properties of the test. Attributes are\n * specified as follows:\n * \\code\n * // [[TimeOut, 10]]\n * // [[OutputRegex, The error message expected from the test]]\n * SPECTRE_TEST_CASE(\"Unit.Blah\", \"[Unit]\") {\n * \\endcode\n *\n * Available attributes are:\n *\n * <table class=\"doxtable\">\n * <tr>\n * <th>Attribute </th><th>Description  </th>\n * </tr>\n * <tr>\n * <td>TimeOut </td>\n * <td>override the default timeout and set the timeout to N seconds. This\n * should be set very sparingly since unit tests are designed to be\n * short. If your test is too long you should consider testing smaller\n * portions of the code if possible, or writing an integration test instead.\n * </td>\n * </tr>\n * <tr>\n * <td>OutputRegex </td>\n * <td>\n * When testing failure modes the exact error message must be tested, not\n * just that the test failed. Since the string passed is a regular\n * expression you must escape any regex tokens. For example, to match\n * `some (word) and` you must specify the string `some \\(word\\) and`.\n * If your error message contains a newline, you can match it using the\n * dot operator `.`, which matches any character.\n * </td>\n * </tr>\n * </table>\n *\n * ### Debugging Tests in GDB or LLDB\n *\n * Several tests fail intentionally at the executable level to test error\n * handling like ASSERT statements in the code. CTest is aware of which\n * should fail and passes them. If you want to debug an individual test\n * in a debugger you must specify the name of the test as the first argument to\n * the test executable. For example, if you want to debug the \"Unit.Gradient\"\n * test: Launch the debugger, for example if you're using LLDB then run `lldb\n * ./bin/Test_LinearOperators`. Then run the test with `run Unit.Gradient`\n * inside the debugger.\n */\n\n/*!\n * \\defgroup TimeGroup Time\n * \\brief Code related to the representation of time during simulations.\n *\n * The time covered by a simulation is divided up into a sequence of\n * adjacent, non-overlapping (except at endpoints) intervals referred\n * to as \"slabs\".  The boundaries between slabs can be placed at\n * arbitrary times.  Slabs, as represented in the code as the Slab\n * class, provide comparison operators comparing slabs agreeing with\n * the definition as a sequence of intervals.  Slabs that do not\n * jointly belong to any such sequence should not be compared.\n *\n * The specific time is represented by the Time class, which encodes\n * the slab containing the time and the fraction of the slab that has\n * elapsed as an exact rational.  Times are comparable according to\n * their natural time ordering, except for times belonging to\n * incomparable slabs.\n *\n * Differences in time within a slab are represented as exact\n * fractions of that slab by the TimeDelta class.  TimeDeltas are only\n * meaningful within a single slab, with the exception that the ratio\n * of objects with different slabs may be taken, resulting in an\n * inexact floating-point result.  Longer intervals of time are\n * represented using floating-point values.\n */\n\n/*!\n * \\defgroup TimeSteppersGroup Time Steppers\n * A collection of ODE integrators primarily used for time stepping.\n */\n\n/*!\n * \\defgroup TypeTraitsGroup Type Traits\n * A collection of useful type traits, including C++14 and C++17 additions to\n * the standard library.\n */\n\n/*!\n * \\defgroup UtilitiesGroup Utilities\n * \\brief A collection of useful classes, functions and metafunctions.\n */\n\n/*!\n * \\defgroup VariableFixingGroup Variable Fixing\n * \\brief A collection of different variable fixers ranging in sophistication.\n *\n * Build-up of numerical error can cause physical quantities to evolve\n * toward non-physical values. For example, pressure and density may become\n * negative. This will subsequently lead to failures in numerical inversion\n * schemes to recover the corresponding convervative values. A rough fix that\n * enforces physical quantities stay physical is to simply change them by hand\n * when needed. This can be done at various degrees of sophistication, but in\n * general the fixed quantities make up a negligible amount of the physics of\n * the simulation; a rough fix is vastly preferred to a simulation that fails\n * to complete due to nonphysical quantities.\n */\n"
  },
  {
    "path": "docs/Images/Tutorials/make_multicube_figures.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom mpl_toolkits.mplot3d import Axes3D\nfrom mpl_toolkits.mplot3d.art3d import Poly3DCollection\n\nplt.rcParams[\"figure.figsize\"] = (4, 4)\nplt.rcParams[\"font.size\"] = 10\n# higher-quality fonts, but much slower to render:\n# plt.rcParams['font.serif'] = 'Computer Modern'\n# plt.rcParams['text.usetex'] = True\n\n\ndef draw_basis_vectors(\n    ax,\n    x_origin,\n    label_permutation=[0, 1, 2],\n    flip_vector=[False, False, False],\n    use_greek_labels=True,\n):\n    from matplotlib.patches import FancyArrowPatch\n    from mpl_toolkits.mplot3d import proj3d\n\n    class Arrow3D(FancyArrowPatch):\n        def __init__(self, xs, ys, zs, *args, **kwargs):\n            FancyArrowPatch.__init__(self, (0, 0), (0, 0), *args, **kwargs)\n            self._verts3d = xs, ys, zs\n\n        def draw(self, renderer):\n            xs3d, ys3d, zs3d = self._verts3d\n            xs, ys, zs = proj3d.proj_transform(xs3d, ys3d, zs3d, renderer.M)\n            self.set_positions((xs[0], ys[0]), (xs[1], ys[1]))\n            FancyArrowPatch.draw(self, renderer)\n\n    length = 0.3\n    labels = (\n        [r\"$\\xi$\", r\"$\\eta$\", r\"$\\zeta$\"]\n        if use_greek_labels\n        else [\"$x$\", \"$y$\", \"$z$\"]\n    )\n    arrow_props = dict(\n        mutation_scale=10, arrowstyle=\"-|>\", color=\"k\", shrinkA=0, shrinkB=0\n    )\n    x0, y0, z0 = [\n        x_origin[i] + (length if flip_vector[i] else 0.0) for i in range(3)\n    ]\n    dx, dy, dz = [length * (-1.0 if flip_vector[i] else 1.0) for i in range(3)]\n    ax.add_artist(Arrow3D([x0, x0 + dx], [y0, y0], [z0, z0], **arrow_props))\n    ax.add_artist(Arrow3D([x0, x0], [y0, y0 + dy], [z0, z0], **arrow_props))\n    ax.add_artist(Arrow3D([x0, x0], [y0, y0], [z0, z0 + dz], **arrow_props))\n    dxt, dyt, dzt = [\n        -length - 0.15 if flip_vector[i] else length + 0.05 for i in range(3)\n    ]\n    dyt = dyt - (0.15 if flip_vector[1] else 0.0)\n    ax.text(x0 + dxt, y0, z0, labels[label_permutation[0]])\n    ax.text(x0, y0 + dyt, z0, labels[label_permutation[1]])\n    ax.text(x0, y0, z0 + dzt, labels[label_permutation[2]])\n\n\ndef draw_polygon_collection(ax, polygons, face_colors, edge_colors):\n    collection = Poly3DCollection(polygons)\n    collection.set_color(face_colors)\n    collection.set_edgecolor(edge_colors)\n    collection.set_linewidth(0.6)\n    ax.add_collection3d(collection)\n\n\ndef quad(p, i, j, k, l):\n    return [p[i], p[j], p[k], p[l]]\n\n\ndef cube(p, index_start, strides):\n    sx, sy, sz = strides\n\n    def quad_x_normal(i):\n        return quad(p, i, i + sy, i + sy + sz, i + sz)\n\n    def quad_y_normal(i):\n        return quad(p, i, i + sx, i + sx + sz, i + sz)\n\n    def quad_z_normal(i):\n        return quad(p, i, i + sx, i + sx + sy, i + sy)\n\n    quad_lower_x = quad_x_normal(index_start)\n    quad_upper_x = quad_x_normal(index_start + sx)\n    quad_lower_y = quad_y_normal(index_start)\n    quad_upper_y = quad_y_normal(index_start + sy)\n    quad_lower_z = quad_z_normal(index_start)\n    quad_upper_z = quad_z_normal(index_start + sz)\n\n    return [\n        quad_lower_x,\n        quad_upper_x,\n        quad_lower_y,\n        quad_upper_y,\n        quad_lower_z,\n        quad_upper_z,\n    ]\n\n\ndef black_color(mean_xyz):\n    return \"k\"\n\n\nclass CubeManager:\n    def __init__(self, point_list, strides, edge_color_function=black_color):\n        self._points = point_list\n        self._strides = strides\n        self._ecf = edge_color_function\n        self.quads = []\n        self.face_colors = []\n        self.edge_colors = []\n\n    def add_cube(self, start_index, these_faces_transparent=[]):\n        c = cube(self._points, start_index, self._strides)\n        self.quads.extend(c)\n        clear = (1, 1, 1, 0)\n        white_semi_transparent = (1, 1, 1, 0.75)\n        self.face_colors.extend(\n            [\n                (\n                    clear\n                    if face in these_faces_transparent\n                    else white_semi_transparent\n                )\n                for face in range(6)\n            ]\n        )\n        self.edge_colors.extend(\n            [self._ecf(np.average(face, axis=0)) for face in c]\n        )\n\n\ndef one_cube_figure():\n    p = np.array([[x, y, z] for z in [0, 1] for y in [0, 1] for x in [0, 1]])\n    cubes = CubeManager(p, strides=[1, 2, 4])\n    cubes.add_cube(0)\n\n    fig = plt.figure()\n    ax = Axes3D(fig)\n    ax.set_aspect(\"equal\")\n    ax.set_axis_off()\n    ax.set_xlim(-0.5, 1.5)\n    ax.set_ylim(-0.5, 1.5)\n    ax.set_zlim(-0.5, 1.5)\n\n    draw_polygon_collection(\n        ax, cubes.quads, cubes.face_colors, cubes.edge_colors\n    )\n\n    draw_basis_vectors(ax, [0.1, 0.1, 0.1])\n    for i in range(p.shape[0]):\n        # shift labels of upper-y face to avoid overlaps\n        shift_y = -0.25 if p[i, 1] < 0.5 else 0.1\n        ax.text(p[i, 0], p[i, 1] + shift_y, p[i, 2], \"$\" + str(i) + \"$\")\n\n    fig.savefig(\"onecube_numbered.png\")\n\n\ndef two_cube_figures():\n    p = np.array([[x, y, z] for z in [0, 1] for y in [0, 1] for x in [0, 1, 2]])\n    cubes = CubeManager(p, strides=[1, 3, 6])\n    cubes.add_cube(0, [1])\n    cubes.add_cube(1, [0])\n\n    fig = plt.figure()\n    ax = Axes3D(fig)\n    ax.set_aspect(\"equal\")\n    ax.set_axis_off()\n    ax.set_xlim(0.0, 2.0)\n    ax.set_ylim(-0.5, 1.5)\n    ax.set_zlim(-0.5, 1.5)\n\n    draw_polygon_collection(\n        ax, cubes.quads, cubes.face_colors, cubes.edge_colors\n    )\n\n    draw_basis_vectors(ax, [0.1, 0.1, 0.1])\n    draw_basis_vectors(ax, [1.1, 0.1, 0.1], label_permutation=[2, 0, 1])\n\n    fig.savefig(\"twocubes.png\")\n\n    for i in range(p.shape[0]):\n        # shift labels of upper-y face to avoid overlaps\n        shift_y = -0.25 if p[i, 1] < 0.5 else 0.1\n        ax.text(p[i, 0], p[i, 1] + shift_y, p[i, 2], \"$\" + str(i) + \"$\")\n\n    fig.savefig(\"twocubes_numbered.png\")\n\n\ndef eight_cube_figures():\n    p = np.array(\n        [[x, y, z] for z in [0, 1, 2] for y in [0, 1, 2] for x in [0, 1, 2]]\n    )\n\n    def red_black_blue(mean_xyz):\n        r = max(1.0 - mean_xyz[1], 0.0)\n        b = max(mean_xyz[1] - 1.0, 0.0)\n        return [r, 0.0, b]\n\n    cubes = CubeManager(\n        p, strides=[1, 3, 9], edge_color_function=red_black_blue\n    )\n    cubes.add_cube(0, [1, 3, 5])\n    cubes.add_cube(1, [0, 3, 5])\n    cubes.add_cube(3, [1, 2, 5])\n    cubes.add_cube(4, [0, 2, 5])\n    cubes.add_cube(9, [1, 3, 4])\n    cubes.add_cube(10, [0, 3, 4])\n    cubes.add_cube(12, [1, 2, 4])\n    cubes.add_cube(13, [0, 2, 4])\n\n    fig = plt.figure()\n    ax = Axes3D(fig)\n    ax.set_aspect(\"equal\")\n    ax.set_axis_off()\n    ax.set_xlim(0.0, 2.0)\n    ax.set_ylim(0.0, 2.0)\n    ax.set_zlim(0.0, 2.0)\n\n    draw_polygon_collection(\n        ax, cubes.quads, cubes.face_colors, cubes.edge_colors\n    )\n\n    fig.savefig(\"eightcubes.png\")\n\n    draw_basis_vectors(ax, [0.55, -0.75, 0.0], use_greek_labels=False)\n    for i in range(p.shape[0]):\n        # shift labels on each y=constant surface, to reduce overlaps\n        shift = [0.0, 0.0, 0.0]\n        if p[i, 1] == 0.0:\n            shift[0] = -0.05\n            shift[1] = -0.3\n        elif p[i, 1] == 1.0:\n            shift[0] = 0.02\n            shift[2] = -0.2\n        else:\n            shift[1] = 0.1\n        # this label needs additional manual tweaking\n        if i == 15:\n            shift[0] = -0.2\n            shift[1] = -0.1\n        # reduce R,B components so colors aren't too bright\n        textcolor = red_black_blue(p[i])\n        textcolor[0] /= 1.5\n        textcolor[2] /= 1.5\n        ax.text(\n            p[i, 0] + shift[0],\n            p[i, 1] + shift[1],\n            p[i, 2] + shift[2],\n            \"$\" + str(i) + \"$\",\n            color=textcolor,\n        )\n\n    fig.savefig(\"eightcubes_numbered.png\")\n\n\ndef eight_cube_rotated_exploded_figure():\n    p = np.array(\n        [\n            [x, y, z]\n            for z in [0, 1, 2, 3]\n            for y in [0, 1, 2, 3]\n            for x in [0, 1, 2, 3]\n        ]\n    )\n\n    def red_black_blue(mean_xyz):\n        y_normalization = 1.5\n        r = max(1.0 - mean_xyz[1] / y_normalization, 0.0)\n        b = max(mean_xyz[1] / y_normalization - 1.0, 0.0)\n        return [r, 0.0, b]\n\n    cubes = CubeManager(\n        p, strides=[1, 4, 16], edge_color_function=red_black_blue\n    )\n    cubes.add_cube(0)\n    cubes.add_cube(2)\n    cubes.add_cube(8)\n    cubes.add_cube(10)\n    cubes.add_cube(32)\n    cubes.add_cube(34)\n    cubes.add_cube(40)\n    cubes.add_cube(42)\n\n    fig = plt.figure()\n    ax = Axes3D(fig)\n    ax.set_aspect(\"equal\")\n    ax.set_axis_off()\n    ax.set_xlim(0.0, 3.0)\n    ax.set_ylim(0.0, 3.0)\n    ax.set_zlim(0.0, 3.0)\n\n    draw_polygon_collection(\n        ax, cubes.quads, cubes.face_colors, cubes.edge_colors\n    )\n\n    draw_basis_vectors(ax, [1.0, -0.8, 0.0], use_greek_labels=False)\n    draw_basis_vectors(ax, [0.17, 0.17, 0.25])\n    draw_basis_vectors(\n        ax,\n        [2.17, 0.17, 0.25],\n        label_permutation=[2, 1, 0],\n        flip_vector=[False, False, True],\n    )\n    draw_basis_vectors(\n        ax,\n        [0.17, 2.17, 0.45],\n        label_permutation=[0, 2, 1],\n        flip_vector=[False, False, True],\n    )\n    draw_basis_vectors(\n        ax,\n        [2.17, 2.17, 0.3],\n        label_permutation=[2, 0, 1],\n        flip_vector=[False, True, True],\n    )\n    draw_basis_vectors(\n        ax,\n        [0.17, 0.17, 2.25],\n        label_permutation=[1, 0, 2],\n        flip_vector=[False, True, False],\n    )\n    draw_basis_vectors(\n        ax,\n        [2.17, 0.17, 2.25],\n        label_permutation=[1, 2, 0],\n        flip_vector=[False, True, True],\n    )\n    draw_basis_vectors(\n        ax,\n        [0.17, 2.17, 2.3],\n        label_permutation=[2, 0, 1],\n        flip_vector=[False, True, True],\n    )\n    draw_basis_vectors(ax, [2.17, 2.17, 2.45])\n    fig.savefig(\"eightcubes_rotated_exploded.png\")\n\n\ndef tesseract_figure():\n    p = np.array(\n        [\n            [x, y, z]\n            for z in [0, 1, 2, 3, 4]\n            for y in [0, 1, 2, 3]\n            for x in [0, 1, 2, 3]\n        ]\n    )\n\n    def red_black_blue(mean_xyz):\n        # note: the tesseract is a bigger domain, so mean_xyz values can be\n        # larger. the factor of 1.5 normalizes R,B values back into [0,1].\n        y_normalization = 1.5\n        r = max(1.0 - mean_xyz[1] / y_normalization, 0.0)\n        b = max(mean_xyz[1] / y_normalization - 1.0, 0.0)\n        return [r, 0.0, b]\n\n    cubes = CubeManager(\n        p, strides=[1, 4, 16], edge_color_function=red_black_blue\n    )\n    cubes.add_cube(5, [5])\n    cubes.add_cube(21, [4, 5])\n    cubes.add_cube(33, [3])\n    cubes.add_cube(36, [1])\n    cubes.add_cube(38, [0])\n    cubes.add_cube(41, [2])\n    cubes.add_cube(53, [4])\n\n    # also draw the back 3 sides of the bounding box\n    box_quads = np.array(\n        [quad(p, 0, 12, 76, 64), quad(p, 12, 15, 79, 76), quad(p, 0, 3, 15, 12)]\n    )\n\n    fig = plt.figure(figsize=(6, 6))\n    ax = Axes3D(fig)\n    ax.set_aspect(\"equal\")\n    ax.set_axis_off()\n    ax.set_xlim(-0.5, 3.5)\n    ax.set_ylim(-0.5, 3.5)\n    ax.set_zlim(0.0, 4.0)\n\n    # draw bounding box first, else matplotlib gets the layer ordering wrong\n    draw_polygon_collection(ax, box_quads, \"white\", \"lightgrey\")\n    draw_polygon_collection(\n        ax, cubes.quads, cubes.face_colors, cubes.edge_colors\n    )\n\n    # these are the visible points on the tesseract\n    points_to_label = [\n        5,\n        6,\n        10,\n        21,\n        22,\n        26,\n        36,\n        33,\n        34,\n        38,\n        39,\n        43,\n        52,\n        49,\n        50,\n        53,\n        54,\n        55,\n        56,\n        58,\n        59,\n        62,\n        69,\n        70,\n        73,\n        74,\n    ]\n    # these are the points we want to show from the bounding box\n    points_to_label.extend([0, 3, 15, 64, 76, 79])\n    for i in points_to_label:\n        # shift labels on each y=constant surface, to reduce overlaps\n        shift = [0.0, 0.0, 0.0]\n        if p[i, 1] == 0.0 or p[i, 1] == 1.0:\n            shift[0] = -0.07\n            shift[1] = -0.37\n        else:\n            shift[1] = 0.1\n        # specific individual corrections at complex tesseract corners\n        if i == 38:\n            shift[2] = 0.27\n        elif i == 39:\n            shift[0] = 0.15\n            shift[1] = -0.2\n        elif i == 53:\n            shift[0] = 0.24\n        elif i == 54:\n            shift[0] = 0.24\n        elif i == 58:\n            shift[2] = -0.3\n        elif i == 69:\n            shift[2] = 0.15\n        # reduce R,B components so colors aren't too bright\n        textcolor = red_black_blue(p[i])\n        textcolor[0] /= 1.5\n        textcolor[2] /= 1.5\n        ax.text(\n            p[i, 0] + shift[0],\n            p[i, 1] + shift[1],\n            p[i, 2] + shift[2],\n            \"$\" + str(i) + \"$\",\n            color=textcolor,\n        )\n\n    fig.savefig(\"tesseract_numbered.png\")\n\n\none_cube_figure()\ntwo_cube_figures()\neight_cube_figures()\neight_cube_rotated_exploded_figure()\ntesseract_figure()\n"
  },
  {
    "path": "docs/Installation/BuildSystem.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Build System - CMake {#spectre_build_system}\n\n\\tableofcontents\n\nSpECTRE uses [CMake](https://cmake.org/) for the build system. In this\nguide we'll outline how to configure SpECTRE and\ndescribe some [commonly used CMake flags](#common_cmake_flags).\nWe'll also go over how to\n[add new source files](#adding_source_files),\n[libraries](#adding_libraries),\n[unit tests](#adding_unit_tests), [executables](#adding_executables), and\n[external dependencies](#adding_external_dependencies).\n\n# Configuring\n\nThe command to configure SpECTRE will look like this\n\n```\ncmake -D FLAG1=OPT1 ... -D FLAGN=OPTN <SPECTRE_ROOT>\n```\n\nwhere `FLAG{1..N}` are CMake flags that are detailed in the section on\n[commonly used CMake flags](#common_cmake_flags).\n\nCMake will look in your `$PATH` environment variable for all of the\n[build dependencies](installation.html#build_dependencies) and will use the\nfirst one it finds that satisfies the requirements. However, if a dependency is\nnot in your `$PATH`, there are multiple versions of a dependency, or you just\nwant to customize your configuration, you'll have to tell CMake which dependency\nyou want to use and where it is using CMake flags. To that end, even though the\n[commonly used CMake flags](#common_cmake_flags) section has a more detailed\nlist of the flags available, the following list has the *most common* CMake\nflags you'll need to customize your configuration (e.g. compilers, charm, build\ntype, and library types):\n\n- CMAKE_C_COMPILER\n- CMAKE_CXX_COMPILER\n- CMAKE_Fortran_COMPILER\n- CHARM_ROOT\n- CMAKE_BUILD_TYPE\n- BUILD_SHARED_LIBS\n\n\\note If you are on a cluster, take a look at the\n[installation on clusters](installation_on_clusters.html)\ninstructions. If you are on a cluster we support, we will already have an\nenvironment setup and an easy way for you to configure the build without having\nto specify flags yourself.\n\n## Commonly Used CMake flags {#common_cmake_flags}\nThe following are common flags used to control building SpECTRE with CMake (in\nalphabetical order):\n- ASAN\n  - Whether or not to turn on the address sanitizer compile flags\n    (`-fsanitize=address`) (default is `OFF`)\n- BLAZE_USE_ALWAYS_INLINE\n  - Force Blaze inlining (default is `ON`)\n  - If disabled or if the platform is unable to 100% guarantee inlining,\n    falls back to `BLAZE_USE_STRONG_INLINE` (see below)\n  - Forced inlining reduces function call overhead, and so generally reduces\n    runtime. However, it does increase compile time and compile memory usage. It\n    is also easier to use a debugger when forced inlining is disabled. If you\n    are encountering debugger messages like `function inlined`, then forced\n    inlining should be disabled.\n- BLAZE_USE_STRONG_INLINE\n  - Increase the likelihood of Blaze inlining (default is `ON`)\n  - Strong inlining reduces function call overhead, and so generally reduces\n    runtime. However, it does increase compile time and compile memory usage. It\n    is also easier to use a debugger when strong inlining is disabled. If you\n    are encountering debugger messages like `function inlined`, then strong\n    inlining should be disabled.\n- BOOTSTRAP_PY_DEPS and BOOTSTRAP_PY_DEV_DEPS\n  - Install missing Python dependencies into the build directory, as listed in\n    `support/Python/requirements.txt` and `support/Python/dev_requirements.txt`,\n    respectively. This is an alternative to creating a Python environment and\n    installing the packages yourself. If you run into problems with packages\n    like h5py, numpy or scipy you can/should still install them yourself to make\n    sure they use the correct HDF5, BLAS, etc.\n    (default is `OFF`)\n- BUILD_DOCS\n  - Enable building documentation. (default is `ON`)\n- BUILD_PYTHON_BINDINGS\n  - Build python libraries to call SpECTRE C++ code from python\n    (default is `ON`)\n- BUILD_SHARED_LIBS\n  - Whether shared libraries are built instead of static libraries\n    (default is `OFF`)\n- BUILD_TESTING\n  - Enable building tests. (default is `ON`)\n- CHARM_ROOT\n  - The path to the build directory of `Charm++`\n- CHARM_TRACE_PROJECTIONS\n  - Enables tracing with Charm++ projections. Specifically, enables the link\n    flag `-tracemode projections`. (default is `OFF`)\n- CHARM_TRACE_SUMMARY\n  - Enables trace summaries with Charm++ projections. Specifically, enables\n    the link flag `-tracemode summary`. (default is `OFF`)\n- CMAKE_BUILD_TYPE\n  - Sets the build type.  Common options:\n    - `Debug` (the default if the flag is not specified): sets flags\n      that trigger additional error checking\n    - `Release`\n- CMAKE_C_COMPILER\n  - The `C` compiler used (defaults to whatever is determined by\n    `CMake/Modules/CMakeDetermineCCompiler.cmake`, usually `cc`)\n- CMAKE_C_FLAGS\n  - Additional flags passed to the `C` compiler.\n- CMAKE_CXX_COMPILER\n  - The `C++` compiler used (defaults to whatever is determined by\n    `CMake/Modules/CMakeDetermineCXXCompiler.cmake`, usually `c++`)\n- CMAKE_CXX_FLAGS\n  - Additional flags passed to the `C++` compiler.\n- CMAKE_Fortran_COMPILER\n  - The `Fortran` compiler used (defaults to whatever is determined by\n    `CMake/Modules/CMakeDetermineFortranCompiler.cmake`)\n- CMAKE_Fortran_FLAGS\n  - Additional flags passed to the `Fortran` compiler.\n- CMAKE_INSTALL_PREFIX\n  - Location where the `install` target copies executables, libraries, etc. Make\n    sure to set this variable before you `install`, or a default location such\n    as `/usr/local` is used.\n- CMAKE_RUNTIME_OUTPUT_DIRECTORY\n  - Sets the directory where the library and executables are placed.\n    By default libraries end up in `<BUILD_DIR>/lib` and executables\n    in `<BUILD_DIR>/bin`.\n- COVERAGE\n  - Enable code coverage with GCOV and LCOV (default `OFF`)\n- DEBUG_SYMBOLS\n  - Whether or not to use debug symbols (default is `ON`)\n  - Disabling debug symbols will reduce compile time and total size of the build\n    directory.\n- DOCS_ONLY\n  - Build _only_ documentation (default is `OFF`). Requires `BUILD_DOCS=ON`.\n- ENABLE_OPENMP\n  - Enable OpenMP parallelization in some parts of the code, such as Python\n    bindings and interpolating volume data files. Note that simulations do not\n    typically use OpenMP parallelization, so this flag only applies to tools.\n- ENABLE_PARAVIEW\n  - Try to find ParaView to enable 3D rendering tools (default is `OFF`)\n- ENABLE_PROFILING\n  - Enables various options to make profiling SpECTRE easier\n    (default is `OFF`)\n- ENABLE_PYTHON\n  - Enables Python. (default is `ON`)\n  - Set to `OFF` for a minimal build on systems without a recent Python\n    installation. Warning: Many things will not work!!\n  - Note: Even when disabled, a minimal Python installation\n    (Python ≥ 2.7) is still required.\n  - BUILD_PYTHON_BINDINGS, BUILD_TESTING, BUILD_DOCS should be `OFF`.\n- ENABLE_WARNINGS\n  - Whether or not warning flags are enabled (default is `ON`)\n- FUKA_ROOT\n  - Set to a path to a [FUKA](https://bitbucket.org/fukaws/fuka) installation to\n    enable loading FUKA initial data into SpECTRE. Can be the FUKA repository\n    root or the directory where `libkadath.a` was installed. Also requires FFTW\n    to be installed (see FUKA docs on dependencies).\n- GSL_STATIC\n  - If set, then the GNU Scientific Library is statically linked.\n- KEEP_FRAME_POINTER\n  - Whether to keep the frame pointer. Needed for profiling or other cases\n    where you need to be able to figure out what the call stack is.\n    (default is `OFF`)\n- MACHINE\n  - Select a machine that we know how to run on, such as a particular\n    supercomputer. A file named MACHINE.yaml must exist in support/Machines and\n    a submit script template named MACHINE.sh must exist in\n    support/SubmitScripts.\n- MEMORY_ALLOCATOR\n  - Set which memory allocator to use. If there are unexplained segfaults or\n    other memory issues, it would be worth setting `MEMORY_ALLOCATOR=SYSTEM` to\n    see if that resolves the issue. It could be the case that different\n    third-party libraries accidentally end up using different allocators, which\n    is undefined behavior and will result in complete chaos.\n    (default is `JEMALLOC`)\n- PY_DEV_MODE\n  - Enable development mode for the Python package, meaning that Python files\n    are symlinked rather than copied to the build directory. Allows to edit and\n    test Python code much easier, in particular when it uses compiled Python\n    bindings, but doesn't replace CMake placeholders in the Python code such as\n    the project version. (default is `OFF`)\n- SPEC_ROOT\n  - Set to a path to a SpEC installation (the SpEC repository root) to link in\n    SpEC libraries. In particular, the SpEC::Exporter library is linked in and\n    enables loading SpEC data into SpECTRE. See \\ref installation for details.\n- SPECTRE_AUTODIFF\n  - Enable automatic differentation (default is `OFF`). This is required for\n    computing Hessians.\n- SPECTRE_DEBUG\n  - Defines `SPECTRE_DEBUG` macro to enable `ASSERT`s and other debug\n    checks so they can be used in Release builds. That is, you get sanity checks\n    and compiler optimizations. You cannot disable the checks in Debug builds,\n    so this option has no effect in Debug builds.\n    (default is `OFF` in release)\n- SPECTRE_DEBUG_Og\n  - Compile Debug builds with `-Og` instead of the default `-O0`.\n    `-Og` is specifically designed to preserve debuggability while enabling\n    optimizations that do not interfere with source-level debugging.\n    Has no effect when `SPECTRE_OPTIMIZE_SIZE=ON` (which uses `-Oz`).\n    (default is `ON`)\n- SPECTRE_NAN_INIT\n  - Defines `SPECTRE_NAN_INIT` macro to initialize memory to NaN in various\n    places to catch use of uninitialized values.\n    (default is the value of `SPECTRE_DEBUG`, or `ON` in Debug builds)\n- SPECTRE_OPTIMIZE_SIZE\n  - Optimize for executable size instead of speed (adds the `-Oz` compiler\n    flag). The default is `OFF`, unless on Apple Silicon machines, where the\n    default is `ON`. This is because of a bug in macOS on Apple Silicon, which\n    means executables larger than 2GB in size cannot run. Disabling this option\n    on Apple Silicon can improve debugging tests (which are small enough so the\n    bug does not affect them).\n- SPECTRE_FETCH_MISSING_DEPS\n  - Download missing dependencies and build them alongside the SpECTRE source.\n    (default is `OFF`)\n- SPECTRE_Fortran_STATIC_LIBS (default: `OFF`)\n  - Use static version of `libgfortran` and `libquadmath`.\n- SPECTRE_INPUT_FILE_TEST_MIN_PRIORITY\n  - Minimum priority of input file tests to run. Possible values are: `low` (not\n    usually run on CI), `normal` (run at least once on CI), `high` (run always\n    on CI). (default is `normal`)\n- SPECTRE_KOKKOS (default: `OFF`)\n  - Enable Kokkos support. You must have Kokkos installed and detectable.\n- SPECTRE_LTO\n  - Enable link-time optimization if the compiler supports it.\n- SPECTRE_LTO_CORES\n  - Specifies the number of cores to use for parallelizing LTO. Must be a\n    positive integer or \"auto\". This is only available when `SPECTRE_LTO=ON` and\n    the compiler supports LTO.\n- SPECTRE_TEST_RUNNER\n  - Run test executables through a wrapper.  This might be `charmrun`, for\n    example.  (default is to not use one)\n- SPECTRE_TEST_TIMEOUT_FACTOR (and specific overrides\n  SPECTRE_X_TEST_TIMEOUT_FACTOR for X one of UNIT, STANDALONE, INPUT_FILE, or\n  PYTHON)\n  - Multiply the timeout for the respective set of tests by this factor (default\n    is `1`).\n  - This is useful to run tests on slower machines.\n- SPECTRE_USE_ALWAYS_INLINE\n  - Force SpECTRE inlining (default is `ON`)\n  - Forced inlining reduces function call overhead, and so generally reduces\n    runtime. However, it does increase compile time and compile memory usage. It\n    is also easier to use a debugger when forced inlining is disabled. If you\n    are encountering debugger messages like `function inlined`, then forced\n    inlining should be disabled.\n- STRIP_SYMBOLS\n  - Whether or not to strip all symbols (default is `OFF`)\n  - If enabled strips all extraneous symbols from libraries and executables,\n    further reducing the size of them.\n- STUB_EXECUTABLE_OBJECT_FILES\n  - Replace object files from executables after linking with empty stubs\n    (default is `OFF`)\n  - This is useful for drastically reducing the build size in CI, but since the\n    object files are replaced with empty stubs will generally cause linking\n    problems if used during development.\n- STUB_LIBRARY_OBJECT_FILES\n  - Replace object files from libraries after linking with empty stubs\n    (default is `OFF`)\n  - This is useful for drastically reducing the build size in CI, but since the\n    object files are replaced with empty stubs will generally cause linking\n    problems if used during development.\n- UBSAN_INTEGER\n  - Whether or not to turn on the undefined behavior sanitizer\n    [unsigned integer\n    overflow](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html) flag\n    (`-fsanitize=integer`) (default is `OFF`)\n- UBSAN_UNDEFINED\n  - Whether or not to turn on the undefined behavior sanitizer\n    [undefined\n    behavior](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)\n    compile flags (`-fsanitize=undefined`) (default is `OFF`)\n- UNIT_TESTS_IN_TEST_EXECUTABLES\n  - Whether to build the `unit-tests` target as part of the `test-executables`\n    target. This is used to build only the non-unit tests in the CI build\n    that doesn't use the PCH. (default is `ON`)\n- USE_CCACHE\n  - Use ccache to cache build output so that rebuilding parts of the source tree\n    is faster. The cache will use up space on disk, with the default being\n    around 2-5GB. If you are performing a one time build to test something\n    specific you should consider disabling ccache in order to avoid removing\n    cached files that may be useful in other builds.\n    (default is `ON`)\n- USE_FORMALINE\n  - Write the source tree into HDF5 files written to disk in order to increase\n    reproducibility of results.\n    (default is `ON`)\n- USE_GIT_HOOKS\n  - Use git hooks to perform some sanity checks so that small goofs are caught\n    before they are committed. These checks are particularly useful because they\n    also run automatically on \\ref github_actions_guide \"CI\" and must pass\n    before pull requests are merged.\n    (default is `ON`)\n- USE_LD\n  - Override the automatically chosen linker. The options are `ld`, `gold`, and\n    `lld`.\n    (default is `OFF`)\n- USE_PCH\n  - Whether or not to use pre-compiled headers (default is `ON`)\n- USE_SLEEF\n  - Whether to use [Sleef](https://github.com/shibatch/sleef) with Blaze to\n    vectorize addition math functions like `sin`, `cos`, and `exp`.\n    (default is `OFF`)\n  - \\note Blaze isn't tested super thoroughly across different architectures so\n    there's unfortunately no guarantee that Blaze+Sleef will work everywhere.\n- USE_XSIMD\n  - Whether to use [xsimd](https://github.com/xtensor-stack/xsimd) with Blaze to\n    vectorize addition math functions like `sin`, `cos`, and `exp`.\n    Defines the macro `SPECTRE_USE_XSIMD`, which can be check to enable manual\n    vectorization where necessary.\n    (default is `ON`)\n\n## CMake targets\n\nTo see all possible build targets, once you configure SpECTRE run\n\n```\nmake list\n```\n\nThis will be a long list of all libraries, test executables, simulation\nexecutables, and custom build targets. The custom targets that are\navailable to build with `make` or `ninja` are:\n\n- unit-tests\n  - Build unit tests, which you can run with `ctest -L unit`. Available if\n    `BUILD_TESTING` is `ON` (the default).\n- test-executables\n  - Build all tests, including executables, so you can run all tests with\n    `ctest`. Available if `BUILD_TESTING` is `ON` (the default). To compile\n    `test-executables` you may have to reduce the number of cores you build on\n    in parallel to avoid running out of memory.\n- all-pybindings\n  - Build Python bindings. See \\ref spectre_using_python for details.\n- cli\n  - Same as all-pybindings\n- install\n  - Install targets that have been built to the `CMAKE_INSTALL_PREFIX`. Doesn't\n    try to build anything else.\n\n# Editing the build system\n\n\\note When editing `CMakeLists.txt` files, it is conventional to\nindent multiline commands by two spaces (except for the first line),\nand to separate most commands by blank lines.\n\n## Adding Source Files {#adding_source_files}\n\nSpECTRE organizes source files into subdirectories of `src` that are\ncompiled into libraries.  To add a new source file `FILE.cpp` to an\nexisting library in `src/PATH/DIR`, just edit\n`src/PATH/DIR/CMakeLists.txt` and add `FILE.cpp` to the list of files\nin\n```\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  <list_of_files>\n  )\n```\nsuch that the resulting `<list_of_files>` is in alphabetical order.\n\n## Adding Header Files {#adding_header_files}\n\nSimilarly to [adding new source files](#adding_source_files), you can add a new\nheader file `FILE.hpp` to a library by editing the `CMakeLists.txt` in that\ndirectory and adding `FILE.hpp` to the list of files in\n```\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  <list_of_files>\n)\n```\n\n\\note Any `.tpp` files also go in this list of header files.\n\n## Adding Libraries {#adding_libraries}\n\nTo add a source file `FILE.cpp` that is compiled into a new library `LIB` in a\ndirectory `src/PATH/DIR` (either in a new directory, or in an existing\ndirectory that either does not have a `CMakeLists.txt` file, or does\nnot create a library in the existing `CMakeLists.txt`):\n- Create (if necessary) a `CMakeLists.txt` file in `DIR`, with the following\ntwo lines at the top:\n```\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n```\n- In the parent directory (i.e. `src/PATH`), (if necessary) add the\nfollowing line to its `CMakeLists.txt` file (if necessary, recursively\ndo the previous step and this one until you reach a `CMakeLists.txt` that\nadds the appropriate subdirectory):\n```\nadd_subdirectory(DIR)\n```\nIf there are already other `add_subdirectory()` lines in the file, place\nthe new one so that the subdirectories are in alphabetical order.\n- Add the line:\n```\nset(LIBRARY LIB)\n```\nwhere convention is that `LIB` = `DIR`.  As library names must be\nunique, this is not always possible, in which case the convention is to\nprepend the parent directory to `DIR`.\n- Add the lines\n```\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FILE.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FILE.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  <list_of_public_libraries>\n  PRIVATE\n  <list_of_private_libraries>\n  INTERFACE\n  <list_of_interface_libraries>\n  )\n```\nwhere each `<list_of_X_libraries>` is an alphabetized list\nof libraries of the form\n```\n  SomeLibrary\n  SomeOtherLibrary\n  YetAnotherLibrary\n```\nThe libraries listed under `INTERFACE` are those included in at\nleast one `.hpp` file in `LIB` but never used in any `.cpp` files\nin `LIB`.  The libraries listed under `PRIVATE` are used in\nat least one `.cpp` file in `LIB` but not in any `.hpp` file\nin `LIB`.  The libraries listed under `PUBLIC` are used in at\nleast one `.hpp` file and at least one `.cpp` file in `LIB`.\nNote that a library counts as being used in a `.cpp` file if the\ncorresponding `.hpp` file includes it. In other words, list a dependency\nas `PRIVATE` if it is needed only to compile the library, but not for\nincluding headers. List a dependency as `INTERFACE` if it is not needed\nto compile the library, but is needed for including headers. List a\ndependency as `PUBLIC` if it is needed for both.\n\n\\note If your library only contains header files, you must specify `INTERFACE`\nlike so in the `add_spectre_library(${LIBRARY} INTERFACE)` function. You should\nalso omit the `spectre_target_sources` lines as well.\n\n## Adding Unit Tests {#adding_unit_tests}\n\nWe use the [Catch](https://github.com/philsquared/Catch) testing\nframework for unit tests. All unit tests are housed in `tests/Unit`\nwith subdirectories for each subdirectory of `src`. Add the `cpp` file\nto the appropriate subdirectory and also to the `CMakeLists.txt` in\nthat subdirectory. Inside the source file you can create a new test by\nadding a `SPECTRE_TEST_CASE(\"Unit.Dir.Component\",\n\"[Unit][Dir][Tag]\")`. The `[Tag]` is optional and you can have more\nthan one, but the tags should be used quite sparingly.  The purpose of\nthe tags is to be able to run all unit tests or all tests of a\nparticular set of components, e.g. `ctest -L Data` to run all tests\ninside the `Data` directory. Please see \\ref writing_unit_tests\n\"writing unit tests\", other unit tests and the [Catch\ndocumentation](https://github.com/philsquared/Catch) for more help on\nwriting tests. Unit tests should take as short a time as possible,\nwith a goal of less than two seconds.  Please also limit the number of\ndistinct cases (by using `SECTION`s).\n\nYou can check the unit test coverage of your code by installing all the optional\ncomponents and then running `make unit-test-coverage` (after re-running CMake).\nThis will create the\ndirectory `BUILD_DIR/docs/html/unit-test-coverage/` which is where the coverage\ninformation is located. Open the `index.html` file in your browser and make\nsure that your tests are indeed checking all lines of your code. Your pull\nrequests might not be merged until your line coverage is over 90% (we are aiming\nfor 100% line coverage wherever possible). Unreachable lines of code can be\nexcluded from coverage analysis by adding the inline comment `LCOV_EXCL_LINE`\nor a block can be excluded using `LCOV_EXCL_START` and `LCOV_EXCL_STOP`.\nHowever, this should be used extremely sparingly since unreachable code paths\nshould be removed from the code base altogether.\n\n## Adding Executables {#adding_executables}\n\nAll general executables are found in `src/Executables`, while those\nfor specific evolution (elliptic) systems are found in\n`src/Evolution/Executables` (`src/Elliptic/Executables`).  See \\ref\ndev_guide_creating_executables \"how to create executables\".\n\n## Adding External Dependencies {#adding_external_dependencies}\n\nTo add an external dependency, first add a `SetupDEPENDENCY.cmake`\nfile to the `cmake` directory. If CMake\ndoes not already support `find_package` for the library you're adding\nyou can write your own `FindDEPENDENCY.cmake` file.\nThe `SetupDEPENDENCY.cmake` file must then be included in\nthe root `spectre/CMakeLists.txt`. Be sure to test both that setting\n`LIBRARY_ROOT` works correctly for your library, and also that if the\nlibrary is required that CMake fails gracefully if the library is not\nfound.\n\n# Dependencies\n\n## Checking Dependencies\n\nGetting dependencies of libraries correct is quite difficult. SpECTRE offers the\nCMake function `check_spectre_libs_dependencies`, defined in\n`cmake/SpectreCheckDependencies.cmake`, to check the dependencies for all\nlibraries in the `libs` target. Individual target dependencies can be checked\nusing the `check_target_dependencies` CMake function defined in\n`cmake/SpectreCheckTargetDependencies.cmake`. Please see those functions in the\nsource tree for more details on how to use them.\n\n## Formaline\n\nSpECTRE's implementation of Formaline is based on, but distinct in\nimplementation from, the\n[original design](https://github.com/hypercott/formaline)\nwhich embeds an archive of the source tree into the executable. The original\ndesign creates a C/C++ file with a function that returns an array/vector of\n`char`s (a byte stream). However, this results in a very large source file (50MB\nor more), which is very slow to compile and ends up more than doubling the link\ntime. Instead, SpECTRE's Formaline implementation uses the linker `ld` to\nencode a file into an object, which means\nrather than creating a large source file, we can directly encode the source tree\narchive into the binary at the linking stage.\n\nMost of SpECTRE's Formaline is implemented\ninside the `tools/WrapExecutableLinker.sh` script. Function declarations are\nprovided in `Utilities/Formaline.hpp` and a small function that writes the\nsource file to disk is defined in `Utilities/Formaline.cpp`. The first\nFormaline-related thing done in `WrapExecutableLinker.sh` is to archive\neverything in the source directory tracked by git. Once the archive is created\nwe run `ld -r -b binary -o object.o src.tar.gz` (with unique names for\n`object.o` and `src.tar.gz` for each executable that is built to avoid name\ncollisions) to generate an object file with the source file encoded from\n`_binary_src_tar_gz_start` to `_binary_src_tar_gz_end`. Next we write a C++\nsource file that defines a function `get_archive` to convert the byte stream\ninto a `std::vector<char>`. We also encode the output of `printenv`, the various\n`PATH` environment variables, and the CMake generated `BuildInfo.txt` file\ninto the source file. Finally, the generated source file is built during the\nlinking phase and the object file containing the source archive is linked into\nthe executable.\n\nTo further aid in reproducibility, the `printenv` output and\n`BuildInfo.txt` contents are written to HDF5 files as part of the\n`h5::Header` object. The archive of the source tree is written using the\n`h5::SourceArchive` object and can be extracted by running\n```\nh5dump -d /src.tar.gz -b LE -o src.tar.gz /path/to/hdf5/file.h5\n```\n\n# Using Kokkos\n\nKokkos support is still extremely experimental, but can be enabled by passing\n`-D SPECTRE_KOKKOS=ON` to CMake. You must pass additional CMake flags that\nKokkos will use to configure itself. For example, to enable CUDA support you\nmust pass `-D Kokkos_ENABLE_CUDA=ON`. See the\n[Kokkos documentation](https://kokkos.org/kokkos-core-wiki/keywords.html) for\ndetails.\n\n## Nvidia Compiler\n\nIf you are using CUDA to compile for Nvidia GPUs but do not have the target GPU\non the system you are compiling on then you must also tell CMake what CUDA\narchitecture to use. You can do this by passing `-D CMAKE_CUDA_ARCHITECTURES=80`\nto CMake. You must choose the architecture that will be compatible with the GPU\nyou plan to use. Note that this is actually called the compute capability\nversion by Nvidia and can be viewed\n[here](https://developer.nvidia.com/cuda-gpus).\n"
  },
  {
    "path": "docs/Installation/Installation.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Installation {#installation}\n\n\\tableofcontents\n\nThis page details how to install SpECTRE on personal machines and on clusters\nthat have no official support (yet).\n\n- For details on installing SpECTRE on a number of clusters that we support\n  please refer to:\n  \\subpage installation_on_clusters\n- For configuring SpECTRE please refer to:\n  \\subpage spectre_build_system\n- For instructions on installing SpECTRE on Apple Silicon Macs please refer to:\n  \\subpage installation_on_apple_silicon\n- For information on our versioning scheme and public releases please refer to:\n  \\subpage versioning_and_releases\n\n### Running containerized releases\n\n#### CLI Entrypoint\n\nA quick way to run the code without installing anything at all is with our\ncontainerized releases:\n\n```\ndocker run sxscollaboration/spectre --help\n```\n\nYou can also use [Apptainer/Singularity](https://apptainer.org) instead of\nDocker, which works better on computing clusters and is more convenient because\nit shares the host's file system:\n\n```\napptainer run docker://sxscollaboration/spectre --help\n```\n\nThe entrypoint to this container is the SpECTRE\n\\ref tutorial_cli \"command-line interface (CLI)\".\nFor example, you can generate initial data for a simulation of merging black\nholes and plot the result like this:\n\n```\napptainer run docker://sxscollaboration/spectre bbh generate-id \\\n  -q 1 --chi-A 0 0 0 --chi-B 0 0 0 -D 16 -w 0.015 -a 0 -o ./bbh_id\napptainer run docker://sxscollaboration/spectre plot slice \\\n  bbh_id/BbhVolume*.h5 -C 0,0,0 -n 0,0,1 -u 0,1,0 -X 24 24 \\\n  -y ConformalFactor -o plot.pdf\n```\n\nThe containers currently have precompiled code only for Linux x86_64 platforms,\nand only have a limited set of executables precompiled. The supported features\navailable in the precompiled containers are:\n\n- Generating initial data\n- Running CCE (see \\ref tutorial_cce)\n- Running Python support code with the SpECTRE CLI (see \\ref tutorial_cli)\n\n#### Starting a container {#start_deploy_container}\n\nIf you'd rather use an image to start a container, you can run\n\n```\ndocker run --name spectre -i --entrypoint /bin/bash\n  -t sxscollaboration/spectre:deploy\n```\n\n\\note The `--entrypoint /bin/bash` is important so you don't run the CLI.\n\n### Running static binaries\n\nAnother way of running the code without installing anything is with our\nprecompiled static binaries, which are published on GitHub:\n\n- Releases with precompiled executables:\n  https://github.com/sxs-collaboration/spectre/releases\n\nThese are currently compiled only for Linux x86_64 platforms and for Intel\nHaswell architecture, so they should be compatible with machines newer than mid\n2013.\n\nWe only publish a limited set of precompiled static binaries that are useful as\nstand-alone tools, such as the CCE executables (see \\ref tutorial_cce).\n\n### Quick-start guide for code development with Docker and Visual Studio Code\n\nIf you're new to writing code for SpECTRE and would like to jump right into a\nworking development environment, a good place to start is our\n\\subpage dev_guide_quick_start_docker_vscode.\n\n### Quick-start installation {#quick_start_install}\n\nThe easiest way of installing SpECTRE natively on a new machine is this:\n\n1. Collect dependencies. You need a C++ compiler (GCC or Clang), CMake,\n   BLAS/LAPACK, Boost, GSL, HDF5, and Python installed. For details on these\n   required dependencies see \\ref build_dependencies. On many computing clusters\n   they are available as modules. On personal machines you can install them with\n   a package manager.\n\n2. Clone the SpECTRE repository:\n\n  ```sh\n  git clone git@github.com:sxs-collaboration/spectre.git\n  export SPECTRE_HOME=$PWD/spectre\n  ```\n\n3. Install Charm++:\n\n  ```sh\n  git clone https://github.com/UIUC-PPL/charm\n  cd charm\n  git checkout v8.0.0\n  ./build charm++ <version> --with-production --build-shared --disable-tls\n  export CHARM_ROOT=$PWD/<version>\n  ```\n\n  Choose the `<version>` from [this list in the Charm++\n  documentation](https://github.com/charmplusplus/charm?tab=readme-ov-file#how-to-choose-a-version).\n  For example, choose `multicore-linux-x86_64` on a Linux laptop,\n  `multicore-darwin-arm8` on an Apple Silicon laptop, and `mpi-linux-x86_64` on\n  a standard computing cluster (you will also need MPI for this). See\n  \\ref building-charm for details.\n\n4. Configure and build SpECTRE:\n\n  ```sh\n  cd $SPECTRE_HOME\n  mkdir build\n  cd build\n  cmake \\\n    -D CMAKE_C_COMPILER=<clang or gcc> \\\n    -D CMAKE_CXX_COMPILER=<clang++ or g++> \\\n    -D CMAKE_Fortran_COMPILER=gfortran \\\n    -D CMAKE_BUILD_TYPE=<Debug or Release> \\\n    -D CHARM_ROOT=$CHARM_ROOT \\\n    -D SPECTRE_FETCH_MISSING_DEPS=ON \\\n    -D MEMORY_ALLOCATOR=SYSTEM \\\n    $SPECTRE_HOME\n  ```\n\n  See \\ref building-spectre for details and \\ref common_cmake_flags for a list\n  of possible configuration options. For example, set `-D ENABLE_OPENMP=ON`\n  to enable OpenMP-parallelization for the exporter library.\n\n  Now you can compile executables (again, see \\ref building-spectre for\n  details). For example:\n\n  ```sh\n  make -j12 cli\n  make -j12 BundledExporter\n  ```\n\n### Installation with Spack\n\nYou can also install SpECTRE with the [Spack](https://github.com/spack/spack)\npackage manager:\n\n```sh\ngit clone https://github.com/spack/spack\nsource ./spack/share/spack/setup-env.sh\nspack compiler find\nspack external find\nspack install spectre executables=ExportCoordinates3D \\\n  ^charmpp backend=multicore\n```\n\nYou probably want to customize your installation, e.g., to select a particular\nversion of SpECTRE, the executables you want to install, additional options such\nas Python bindings, or the Charm++ backend. You can display all possible options\nwith:\n\n```sh\nspack info spectre  # or charmpp, etc.\n```\n\nRefer to the [Spack documentation](https://spack.readthedocs.io/en/latest/) for\nmore information.\n\n\\warning We have not found the Spack installation particularly stable since the\nSpack package manager is still in development.\n\n## Detailed installation instructions\n\nThis remainder of this page details the installation procedure for SpECTRE.\n\n### Dependencies {#build_dependencies}\n\n\\note You don't need to install any of these dependencies by hand if you\nuse a container or follow the \\ref quick_start_install.\n\n#### Required:\n\n* [GCC](https://gcc.gnu.org/) 10.0 or later,\n[Clang](https://clang.llvm.org/) 13.0 or later (see\n[here](https://apt.llvm.org/) for how to get newer versions of clang through\napt), or AppleClang 13.0.0 or later\n* [CMake](https://cmake.org/) 3.18.0 or later\n* [Git](https://git-scm.com/)\n* BLAS & LAPACK (e.g. [OpenBLAS](http://www.openblas.net))\n* [Boost](http://www.boost.org/) 1.60.0 or later\n* [GSL](https://www.gnu.org/software/gsl/) \\cite Gsl\n* [GNU make](https://www.gnu.org/software/make/)\n* [HDF5](https://support.hdfgroup.org/HDF5/) (non-mpi version on macOS)\n  \\cite Hdf5\n* [Python](https://www.python.org/) 3.8 or later.\n* [Charm++](http://charm.cs.illinois.edu/) 7.0.0, or later (8 preferred).\n  See also \\ref building-charm. \\cite Charmpp1 \\cite Charmpp2 \\cite Charmpp3\n\nThe following dependencies will be fetched automatically if you set\n`SPECTRE_FETCH_MISSING_DEPS=ON`:\n\n* [Blaze](https://bitbucket.org/blaze-lib/blaze/overview) v3.8.\n  When installing manually, it can be beneficial to install Blaze with CMake so\n  some configuration options are determined automatically, such as cache sizes.\n  \\cite Blaze1 \\cite Blaze2\n* [Catch2](https://github.com/catchorg/Catch2) 3.4.0 or later.\n  You can also install Catch2 from your package manager or do a standard CMake\n  build and installation (as detailed in the [Catch2\n  docs](https://github.com/catchorg/Catch2/blob/devel/docs/cmake-integration.md#installing-catch2-from-git-repository)).\n  Compile with `CMAKE_POSITION_INDEPENDENT_CODE=ON`.\n* [LIBXSMM](https://github.com/libxsmm/libxsmm) version 1.16.1 or later.  Some\n  configurations of LIBXSMM build incorrectly against pthreads, causing\n  ``undefined reference to 'pthread_yield'`` when building SpECTRE.  It is\n  unclear what conditions trigger the problem, but it is fixed in versions\n  newer than 1.17.  (As of this writing, there are no newer releases and\n  affected systems must build from git.) \\cite Libxsmm\n* [yaml-cpp](https://github.com/jbeder/yaml-cpp) version 0.7.0 or later.\n  Building with shared library support is recommended when installing from\n  source. \\cite Yamlcpp\n* Python dependencies listed in `support/Python/requirements.txt`.\n  Install with `pip3 install -r support/Python/requirements.txt`.\n  Make sure you are working in a [Python venv](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment)\n  before installing packages.\n  Alternatively, you can set `BOOTSTRAP_PY_DEPS=ON` when configuring a build\n  with CMake to install missing Python packages into the build directory\n  automatically.\n  <details>\n  \\include support/Python/requirements.txt\n  </details>\n\n#### Optional:\n\n* [Pybind11](https://pybind11.readthedocs.io) 2.7.0 or later for SpECTRE Python\n  bindings. Included in `support/Python/requirements.txt`.  \\cite Pybind11\n* [jemalloc](https://github.com/jemalloc/jemalloc)\n* [Doxygen](https://www.doxygen.nl/index.html) 1.9.1 to 1.9.6 — to\n  generate documentation\n* Python dev dependencies listed in `support/Python/dev_requirements.txt`\n  — for documentation pre- and post-processing, formatting code, etc.\n  Install with `pip3 install -r support/Python/dev_requirements.txt`.\n  Make sure you are working in a [Python venv](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment)\n  before installing packages.\n  <details>\n  \\include support/Python/dev_requirements.txt\n  </details>\n* [Kokkos](https://github.com/kokkos/kokkos) (experimental) - for GPU support.\n  See \\ref gpu_support for details.\n* [Google Benchmark](https://github.com/google/benchmark) - to do\n  microbenchmarking inside the SpECTRE framework. v1.2 or newer is required\n* [LCOV](http://ltp.sourceforge.net/coverage/lcov.php) and\n  [gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html) — to check code test\n  coverage\n* [PAPI](http://icl.utk.edu/papi/) — to access hardware performance counters\n* [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) — to format C++\n  code in a clear and consistent fashion\n* [Clang-Tidy](http://clang.llvm.org/extra/clang-tidy/) — to \"lint\" C++ code\n* [Scotch](https://gitlab.inria.fr/scotch/scotch) - to build the `ScotchLB`\n  graph partition based load balancer in charm++.\n* [ffmpeg](https://www.ffmpeg.org/) - for animating 1d simulations with\n  matplotlib\n* [xsimd](https://github.com/xtensor-stack/xsimd) 11.0.1 or newer - for manual\n  vectorization\n* [autodiff](https://github.com/autodiff/autodiff/) commit cc2aa5726fdbb258d097f87b97da3d1022f8394e\n  or newer - for automatic differentiation\n* [libbacktrace](https://github.com/ianlancetaylor/libbacktrace) - to show\n  source files and line numbers in backtraces of errors and asserts. Available\n  by default on many systems, so you may not have to install it at all. The\n  CMake configuration will tell you if you have libbacktrace installed.\n* [ParaView](https://www.paraview.org/) - for visualization \\cite Paraview1\n  \\cite Paraview2 . Make sure your ParaView installation uses the same (major\n  and minor) version of Python as the rest of the build.\n* [SpEC](https://www.black-holes.org/code/SpEC.html) - to load SpEC data.\n  Compile the exporter in SpEC's `Support/ApplyObservers/Exporter/` directory\n  (see the `Makefile` in that directory). Also make sure to compile SpEC with\n  the same compiler and MPI as SpECTRE to avoid compatibility issues.\n\n#### Bundled:\n\n* [Brigand](https://github.com/edouarda/brigand)\n* [libsharp](https://github.com/Libsharp/libsharp) \\cite Libsharp\n\n## Clone the SpECTRE repository\n\nFirst, clone the [SpECTRE repository](https://github.com/sxs-collaboration/spectre)\nto a directory of your choice. In the following we will refer to it as\nSPECTRE_ROOT. You may `git clone` from GitHub, in which case SPECTRE_ROOT will\nbe `<your_current_directory>/spectre`. That is, inside SPECTRE_ROOT are `docs`,\n`src`, `support`, `tests` etc. You can also download the source and extract them\nto your desired working directory, making sure not to leave out hidden files\nwhen you `cp` or `mv` the source files.\n\n## Using Docker to obtain a SpECTRE environment {#docker_install}\n\nA [Docker](https://www.docker.com/) image is available from\n[DockerHub](https://hub.docker.com/r/sxscollaboration/spectre/) and can\nbe used to build SpECTRE on a personal machine.\n\n**Note**: If you have SELinux active\non your system you must figure out how to enable sharing files with the host\nOS. If you receive errors that you do not have permission to access a shared\ndirectory it is likely that your system has SELinux enabled. One option is to\ndisable SELinux at the expense of reducing the security of your system.\n\nTo build with the Docker image:\n\n1. Install [Docker-Desktop](https://docs.docker.com/get-docker/). For Linux, if\n   you want to be able to run the following steps without `sudo`, follow the\n   [post-installation-guide](https://docs.docker.com/engine/install/linux-postinstall/)\n   to add a non-root user.\n\n2. Retrieve the Docker image (you may need `sudo` in front of this command)\n   ```\n   docker pull sxscollaboration/spectre:dev\n   ```\n3. Start the Docker container (you may need `sudo`)\n   ```\n   docker run -v $SPECTRE_ROOT/:$SPECTRE_ROOT/ --name spectre_dev \\\n              -i -t sxscollaboration/spectre:dev /bin/bash\n   ```\n   - `-v $SPECTRE_ROOT/:$SPECTRE_ROOT/` binds the directory `$SPECTRE_ROOT`\n   (which is an environment variable you must set up or just use the actual\n   path) outside the container to `$SPECTRE_ROOT` inside the container. In this\n   way, files in the `$SPECTRE_ROOT` on your host system (outside the container)\n   become accessible within the container through the directory SPECTRE_ROOT\n   inside the container. If you wonder why the same SPECTRE_ROOT needs to be\n   used for both inside and outside the container, which is why `$SPECTRE_ROOT`\n   is repeated in the command above with separated by a colon, please see one of\n   the notes below regarding `-v` flag.\n   - The `--name spectre_dev` is optional. If you don't name your container,\n   docker will generate an arbitrary name.\n   - On macOS you can significantly increase the performance of file system\n   operations by appending the flag `:delegated` to `-v`, e.g.\n   `-v $SPECTRE_ROOT/:$SPECTRE_ROOT/:delegated` (see\n   https://docs.docker.com/docker-for-mac/osxfs-caching/).\n   - The `-i` flag is for interactive mode, which will drop you into the\n   container.\n   - It can be useful to expose a port to the host so you can run servers such\n   as [Jupyter](https://jupyter.org/index.html) for accessing the Python\n   bindings (see \\ref spectre_using_python) or a Python web server to view the\n   documentation. To do so, append the `-p` option, e.g. `-p 8000:8000`.\n\n   You will end up in a bash shell in the docker container,\n   as root (you need to be root).\n   Within the container, the files in `$SPECTRE_ROOT` are available and Charm++\n   is installed in `/work/charm_7_0_0`. For the following steps, stay inside the\n   docker container as root.\n4. Proceed with [building SpECTRE](#building-spectre).\n\n**Notes:**\n  * Everything in your build directory is owned by root, and is\n    accessible only within the container.\n  * You should edit source files in SPECTRE_ROOT in a separate terminal\n    outside the container, and use the container only for compiling and\n    running the code.\n  * If you exit the container (e.g. ctrl-d),\n    your compilation directories are still saved, as are any other changes to\n    the container that you have made.\n    To restart the container, try the following commands\n    (you may need `sudo`):\n    1. `docker ps -a`,\n      to list all containers with their CONTAINER_IDs and CONTAINER_NAMEs,\n    2. `docker start -i CONTAINER_NAME` or `docker start -i CONTAINER_ID`,\n      to restart your container (above, the CONTAINER_NAME was spectre_dev).\n  * When the Docker container gets updated, you can stop it with\n    `docker stop CONTAINER_NAME`, remove it with `docker rm CONTAINER_NAME`\n    and then start at step 2 above to run it again.\n  * You can run more than one shell in the same container, for instance\n    one shell for compiling with gcc and another for compiling\n    with clang.\n    To add a new shell, run `docker exec -it CONTAINER_NAME /bin/bash`\n    (or `docker exec -it CONTAINER_ID /bin/bash`) from\n    a terminal outside the container.\n  * In step 4 above, technically docker allows you to say\n    `-v $SPECTRE_ROOT/:/my/new/path` to map `$SPECTRE_ROOT` outside the\n    container to any path you want inside the container, but **do not do this**.\n    Compiling inside the container sets up git hooks in SPECTRE_ROOT that\n    contain hardcoded pathnames to SPECTRE_ROOT *as seen from inside the\n    container*. So if your source paths inside and outside the container are\n    different, commands like `git commit` run *from outside the container* will\n    die with `No such file or directory`.\n  * If you want to use Docker within VSCode, take a look at our\n    [quick start guide](../DevGuide/QuickStartDockerVSCode.md) for using Docker\n    with VSCode.\n\n## Using Singularity to obtain a SpECTRE environment\n\n[Singularity](https://sylabs.io) is a container alternative\nto Docker with better security and nicer integration.\n\nTo build SpECTRE with Singularity you must:\n\n1. Build [Singularity](https://sylabs.io) and add it to your\n   `$PATH`\n2. `cd` to the directory where you want to store the SpECTRE Singularity image,\n   source, and build directories, let's call it WORKDIR. The WORKDIR must be\n   somewhere in your home directory. If this does not work for you, follow the\n   Singularity instructions on setting up additional [bind\n   points](https://sylabs.io/guides/3.7/user-guide/bind_paths_and_mounts.html)\n   (version 3.7. For other versions, see the [docs](https://sylabs.io/docs/)).\n   Once inside the WORKDIR, clone SpECTRE into `WORKDIR/SPECTRE_ROOT`.\n3. Run `sudo singularity build spectre.img\n   docker://sxscollaboration/spectre:dev`.\n   You can also use spectre:ci instead of spectre:dev if you want more\n   compilers installed.\n\n   If you get the error message that `makesquashfs` did not have enough space to\n   create the image you need to set a different `SINGULARITY_TMPDIR`. This can\n   be done by running: `sudo SINGULARITY_TMPDIR=/path/to/new/tmp singularity\n   build spectre.img docker://sxscollaboration/spectre:dev`. Normally\n   `SINGULARITY_TMPDIR` is `/tmp`, but building the image will temporarily need\n   almost 8GB of space.\n\n   You can control where Singularity stores the downloaded image files from\n   DockerHub by specifying the `SINGULARITY_CACHEDIR` environment variable. The\n   default is `$HOME/.singularity/`. Note that `$HOME` is `/root` when running\n   using `sudo`.\n4. To start the container run `singularity shell spectre.img` and you\n   will be dropped into a bash shell.\n5. Proceed with [building SpECTRE](#building-spectre).\n\n**Notes:**\n- You should edit source files in SPECTRE_ROOT in a separate terminal\n  outside the container, and use the container only for compiling and running\n  the code.\n- If you don't have the same Python version in your environment outside the\n  container as the version inside the container, this will create problems\n  with git hooks. The Singularity container uses python3.8 by default. Thus, it\n  is up to the user to ensure that they are using the same Python version inside\n  and outside the container. To use a different Python version in the container\n  add `-D Python_EXECUTABLE=/path/to/python` to the cmake command where\n  `/path/to/python` is usually `/usr/bin/pythonX` and `X` is the version you\n  want.\n- Unlike Docker, Singularity does not keep the state between runs. However, it\n  shares the home directory with the host OS so you should do all your work\n  somewhere in your home directory.\n- To run more than one container just do `singularity shell spectre.img` in\n  another terminal.\n- Since the data you modify lives on the host OS there is no need to worry about\n  losing any data, needing to clean up old containers, or sharing data between\n  containers and the host.\n\n## Using Spack to set up a SpECTRE environment\n\nSpECTRE's dependencies can be installed with\n[Spack](https://github.com/spack/spack), a package manager tailored for HPC use.\n[Install Spack](https://spack.readthedocs.io/en/latest/getting_started.html) by\ncloning it into `SPACK_DIR` (a directory of your choice). Then, enable Spack's\nshell support with `source SPACK_DIR/share/spack/setup-env.sh`. Consider adding\nthis line to your `.bash_profile`, `.bashrc`, or similar. Refer to [Spack's\ngetting started guide](https://spack.readthedocs.io/en/latest/getting_started.html)\nfor more information.\n\nOnce you have Spack installed, one way to install the SpECTRE dependencies is\nwith a [Spack environment](https://spack.readthedocs.io/en/latest/environments.html):\n\n\\include support/DevEnvironments/spack.yaml\n\nYou can also install the Spack packages listed in the environment file above\nwith a plain `spack install` if you prefer.\n\n**Notes:**\n- Spack allows very flexible configurations and we recommended you read the\n  [documentation](https://spack.readthedocs.io) if you require features such as\n  packages installed with different compilers.\n- For security, it is good practice to make Spack [use the system's\n  OpenSSL](https://spack.readthedocs.io/en/latest/getting_started.html#openssl)\n  rather than allow it to install a new copy.\n- To avoid reinstalling lots of system-provided packages with Spack, use the\n  `spack external find` feature and the `--reuse` flag to `spack concretize` (or\n  `spack install`). You can also install some of the dependencies with your\n  system's package manager in advance, e.g., with `apt` or `brew`. If they are\n  not picked up by `spack external find` automatically, register them with Spack\n  manually. See the [Spack documentation on external\n  packages](https://spack.readthedocs.io/en/latest/build_settings.html#external-packages)\n  for details.\n- Spack works well with a module environment, such as\n  [LMod](https://github.com/TACC/Lmod). See the [Spack documentation on\n  modules](https://spack.readthedocs.io/en/latest/module_file_support.html) for\n  details.\n\n## Building Charm++ {#building-charm}\n\nIf you are not using a container, haven't installed Charm++ with Spack, or want\nto install Charm++ manually for other reasons, follow the installation\ninstructions in the [Charm++ repository](https://github.com/UIUC-PPL/charm)\nand in their [documentation](https://charm.readthedocs.io/en/latest/quickstart.html#installing-charm).\nHere are a few notes:\n\n- Once you cloned the [Charm++ repository](https://github.com/UIUC-PPL/charm),\n  run `git checkout v8.0.0` to switch to a supported, stable release of\n  Charm++.\n- Apply the appropriate patch (if there is one) for the version from\n  `${SPECTRE_ROOT}/support/Charm`. For example, if you have Charm++ v7.0.0\n  then the patch will be `v7.0.0.patch`.\n- Choose the `LIBS` target to compile. This is needed so that we can support the\n  more sophisticated load balancers in SpECTRE executables.\n- On a personal machine the correct target architecture is likely\n  `multicore-linux-x86_64`, or `multicore-darwin-x86_64` on macOS. On an HPC\n  system the correct Charm++ target architecture depends on the machine's\n  inter-node communication architecture. It might take some experimenting to\n  figure out which Charm++ configuration provides the best performance.\n- Compile Charm++ with support for shared libraries by appending the option\n  `--build-shared` to the `./build` command or pass `BUILD_SHARED=ON` to the\n  CMake configuration (see the [Charm++ installation\n  instructions](https://github.com/UIUC-PPL/charm#building-dynamic-libraries)).\n- Passing the `--disable-tls` option to `build` or `-D DISABLE_TLS=ON` to\n  cmake is required for SpECTRE's Python bindings to work.\n- When compiling Charm++ you can specify the compiler using, for example,\n  ```\n  ./build LIBS ARCH clang\n  ```\n\n## Building SpECTRE {#building-spectre}\n\nOnce you have set up your development environment you can compile SpECTRE.\nFollow these steps:\n\n1. Create a build directory where you would like to compile SpECTRE. In the\n   Docker container you could create, e.g., `/work/spectre-build`. It can be\n   useful to add a descriptive label to the name of the build directory since\n   you may create more later, e.g., `build-clang-Debug`. Then, `cd` into the\n   build directory.\n2. Determine the location of your Charm++ installation. In the Docker container\n   it is `/work/charm_7_0_0/multicore-linux-x86_64-gcc` for GCC builds and\n   `/work/charm_7_0_0/mpi-linux-x86_64-smp-clang` for clang builds. For Spack\n   installations you can determine it with\n   `spack location --install-dir charmpp`. We refer to the install directory as\n   `CHARM_ROOT` below.\n3. In your new SpECTRE build directory, configure the build with CMake:\n   ```\n   cmake -D CHARM_ROOT=$CHARM_ROOT SPECTRE_ROOT\n   ```\n   Add options to the `cmake` command to configure the build, select\n   compilers, etc. For instance, to build with clang you may run:\n   ```\n   cmake -D CMAKE_CXX_COMPILER=clang++ \\\n         -D CMAKE_C_COMPILER=clang \\\n         -D CMAKE_Fortran_COMPILER=gfortran \\\n         -D CHARM_ROOT=$CHARM_ROOT \\\n         SPECTRE_ROOT\n   ```\n   See \\ref common_cmake_flags for documentation on possible configuration\n   options.\n4. When cmake configuration is done, you are ready to build target executables.\n   - You can see the list of available targets by running `make list` (or `ninja\n     list` if you are using the Ninja generator) or by using tab completion.\n     Compile targets with `make -jN TARGET` (or `ninja -jN TARGET`), where `N`\n     is the number of cores to build on in parallel (e.g. `-j4`). Note that the\n     Ninja generator allows you to compile individual source files too.\n   - Compile the `unit-tests` target and run `ctest -L unit` to run unit tests.\n     Compile `test-executables` and run `ctest` to run all tests, including\n     executables. To compile `test-executables` you may have to reduce the\n     number of cores you build on in parallel to avoid running out of memory.\n   - To use the command-line interface (CLI), compile the `cli` target (see\n     \\ref tutorial_cli).\n   - To use the Python bindings, compile the `all-pybindings` target (see\n     \\ref spectre_using_python).\n\n## Code Coverage Analysis\n\nFor any coverage analysis you will need to have LCOV installed on the system.\nFor documentation coverage analysis you will also need to install\n[coverxygen](https://github.com/psycofdj/coverxygen) and for test coverage\nanalysis [gcov](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html).\n\nIf you have these installed (which is already done if\nyou are using the docker container), you can look at code coverage as follows:\n\n1. On a gcc build, pass `-D COVERAGE=ON` to `cmake`\n2. `make unit-test-coverage`\n3. The output is in `docs/html/unit-test-coverage`.\n"
  },
  {
    "path": "docs/Installation/InstallationOnAppleSilicon.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n## Installing SpECTRE on Apple Silicon {#installation_on_apple_silicon}\n\n\\tableofcontents\n\nThe following instructions show how to install SpECTRE and its dependencies\non Apple Silicon Macs. Apple Silicon is an arm64 architecture, in contrast\nto the x86-64 architecture that SpECTRE usually targets. These instructions\nwill result in an Apple Silicon native build of SpECTRE.\n\n\\note Floating-point exception trapping is not currently\nsupported on Apple Silicon.\n\n### 0. Install the xcode command-line tools.\n\nInstall the xcode command-line tools, which include the clang compiler, etc.\nRun the following command in the terminal:\n\n```\nxcode-select --install\n```\n\n### 1. Clone spectre\n\nClone the SpECTRE repository in a directory of your choice:\n\n```sh\ngit clone git@github.com:sxs-collaboration/spectre.git\nexport SPECTRE_HOME=$PWD/spectre\n```\n\n### 2. Install python dependencies\n\nSpectre depends on python and some python packages. There are different ways to\ninstall an arm64-native python stack. The following instructions show how\nto do this using the Python 3 interpreter bundled with macOS.\n\n```sh\ncd $SPECTRE_HOME\n\n# Create a Python environment\npython3 -m venv ./env --upgrade-deps\n\n# Activate the Python environment\n. ./env/bin/activate\n\n# Install Python packages\npip install -r support/Python/requirements.txt \\\n  -r support/Python/dev_requirements.txt\n\n# Optionally install additional packages you might want, like Jupyter\npip install jupyterlab\n```\n\n### 3. Install dependencies with Homebrew\n\nMost of spectre's dependencies can be installed using the\n[Homebrew](https://brew.sh) package manager. First, if you haven't already,\ninstall Homebrew by following the instructions on the\n[Homebrew](https://brew.sh) homepage. Then, run the following to install a\nFortran compiler and other dependencies:\n\n```\nbrew install gcc autoconf automake ccache cmake\nbrew install boost catch2 doxygen gsl hdf5 openblas yaml-cpp xsimd\n```\n\n\\note We use OpenBLAS instead of Apple's Accelerate framework here because\nAccelerate fails to reach the same floating point accuracy as OpenBLAS in some\nof our tests (specifically partial derivatives).\n\n### 4. Install remaining dependencies\n\nHere, install the remaining dependencies that cannot be installed with Homebrew.\nYou can install them from source manually, or use the\n[Spack](https://github.com/spack/spack) package manager (see below).\n\n#### Install manually\n\n```sh\nexport SPECTRE_DEPS_ROOT=$HOME/apps\nmkdir -p $SPECTRE_DEPS_ROOT\ncd $SPECTRE_DEPS_ROOT\n\n# Install Charm++\ngit clone https://github.com/UIUC-PPL/charm\npushd charm\ngit checkout v8.0.0\n./build charm++ multicore-darwin-arm8 --with-production -g3 -j --build-shared \\\n  --disable-tls\npopd\n\n# The following dependencies are optional! They will be installed in the build\n# directory automatically if needed in the next step. You can install them\n# manually like this if you want control over where or how they are installed.\n# If you don't care, you can skip ahead.\n\n# Install Blaze\nmkdir blaze\npushd blaze\ncurl -L https://bitbucket.org/blaze-lib/blaze/downloads/blaze-3.8.tar.gz \\\n> blaze-3.8.tar.gz\ntar -xf blaze-3.8.tar.gz\nmv blaze-3.8 include\npopd\n\n# Install libxsmm\n# Need master branch of libxsmm to support Apple Silicon\ngit clone https://github.com/libxsmm/libxsmm.git\npushd libxsmm\nmake\npopd\n```\n\n#### Install with Spack\n\n```sh\n# Download spack\ncd ~\ngit clone -c feature.manyFiles=true https://github.com/spack/spack.git\ncd spack\n# Switch to latest release\ngit checkout releases/latest\n# Load shell support\n. ./share/spack/setup-env.sh\n# Find some system packages so we don't have to install them all from source\nspack external find\n# Install dependencies\nspack install \\\n  charmpp@7.0.0: +shared backend=multicore build-target=charm++ \\\n  blaze@3.8.2 ~blas ~lapack smp=none \\\n  libxsmm@1.16.1: \\\n```\n\n### 5. Configure and build SpECTRE\n\nCreate a build directory in a location of your choice, e.g.\n\n```sh\ncd $SPECTRE_HOME\nmkdir build\ncd build\n```\n\nNext, configure SpECTRE using the following CMake command. If you installed\ndependencies with Spack, you can use `spack find -p` to retrieve the root\ndirectories of the packages and replace them in the command below.\nYou only need to specify `LIBXSMM_ROOT` and `BLAZE_ROOT` if you installed\nthose packages yourself above. The option `SPECTRE_FETCH_MISSING_DEPS` will\ntake care of downloading these if you haven't installed them above.\n\n```sh\ncmake \\\n-D CMAKE_C_COMPILER=clang \\\n-D CMAKE_CXX_COMPILER=clang++ \\\n-D CMAKE_Fortran_COMPILER=gfortran \\\n-D CMAKE_BUILD_TYPE=Debug \\\n-D BUILD_SHARED_LIBS=ON \\\n-D MEMORY_ALLOCATOR=SYSTEM \\\n-D CHARM_ROOT=${SPECTRE_DEPS_ROOT}/charm/multicore-darwin-arm8 \\\n-D SPECTRE_FETCH_MISSING_DEPS=ON \\\n-D SPECTRE_TEST_TIMEOUT_FACTOR=5 \\\n-D BLAS_ROOT=$(brew --prefix openblas) \\\n-D LAPACK_ROOT=$(brew --prefix openblas) \\\n-D LIBXSMM_ROOT=${SPECTRE_DEPS_ROOT}/libxsmm/ \\\n-D BLAZE_ROOT=${SPECTRE_DEPS_ROOT}/blaze/ \\\n..\n```\n\nFinally, build and test SpECTRE. E.g., on a Mac with 10 cores,\n\n```sh\nmake -j10 unit-tests\nmake -j10 test-executables\nctest --output-on-failure -j10\n```\n\nOptionally, to install the python bindings in your python environment,\n\n```sh\nmake -j10 all-pybindings\npip install -e ${SPECTRE_HOME}/build/bin/python\n```\n"
  },
  {
    "path": "docs/Installation/InstallationOnClusters.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Installation on Clusters {#installation_on_clusters}\n\n\\tableofcontents\n\nThe installation instructions are the same for most systems because we use shell\nscripts to set up the environment for each supercomputer. We describe the\ngeneric installation instructions once, and only note special instructions if\nnecessary for the particular system. If you have already built SpECTRE and just\nwant to load the modules, source the shell file for your system and run\n`spectre_load_modules`.\n\n\\note Sample submit scripts for some systems are available in\n`support/SubmitScripts`.\n\n## General Instructions\n\n1. Run `export SPECTRE_HOME=/path/to/where/you/want/to/clone`\n2. Clone SpECTRE using `git clone SPECTRE_URL $SPECTRE_HOME` where the\n   `SPECTRE_URL` is the GitHub url you want to use to clone the repo.\n3. Run `cd $SPECTRE_HOME && mkdir build && cd build`\n4. Run `. $SPECTRE_HOME/support/Environments/SYSTEM_TO_RUN_ON_gcc.sh`, where\n   `SYSTEM_TO_RUN_ON` is replaced by the name of the system as described in the\n   relevant section below.\n5. If you haven't already installed the dependencies, run\n   `export SPECTRE_DEPS=/path/to/where/you/want/the/deps`\n   Then run `spectre_setup_modules $SPECTRE_DEPS`. This\n   will take a while to finish. Near the end the command will tell you how to\n   make the modules available by providing a `module use` command. Make\n   sure you are providing an absolute path to `spectre_setup_modules`.\n6. Run `module use $SPECTRE_DEPS/modules`\n7. Run `spectre_run_cmake`, if you get module loading errors run\n   `spectre_unload_modules` and try running `spectre_run_cmake` again. CMake\n   should set up successfully.\n8. Build the targets you are interested in by running, e.g.\n   `make -j4 test-executables`\n\n## Anvil at Purdue University\n\nYou should build and run tests on a compute node. You can get a compute node by\nrunning\n```\nsinteractive -N1 -n 20 -p debug -t 60:00\n```\nAvoid running `module purge` because this also removes various default modules\nthat are necessary for proper operation. Instead, use `module\nrestore`. Currently the tests can only be run in serial, e.g. `ctest -j1`\nbecause all the MPI jobs end up being launched on the same core.\n\n## Frontera at TACC\n\nFollow the general instructions, using `frontera` for `SYSTEM_TO_RUN_ON`.\n\nProcesses running on the head nodes have restrictions on memory use\nthat will prevent linking the main executables.  It is better to\ncompile on an interactive node.  Interactive nodes can be requested\nwith the `idev` command.\n\nFor unknown reasons, incremental builds work poorly on frontera.\nRunning `make` will often unnecessarily recompile SpECTRE libraries.\n\n## CaltechHPC at Caltech\n\nCluster documentation: https://www.hpc.caltech.edu\n\nFollow the general instructions, using `caltech_hpc` for `SYSTEM_TO_RUN_ON`,\nexcept you don't need to install any dependencies, so you can skip step 5 and\nstep 6.\n\nThere are multiple types of compute nodes on CaltechHPC, which are listed on\nhttps://www.hpc.caltech.edu/resources. We compile SpECTRE to be compatible with\nall of them.\n\nWhen you go to build, you will need to get an interactive node (login nodes\nlimit the amount of memory accessible to individual users, to below the amount\nnecessary to build SpECTRE). Slurm commands are listed on\nhttps://www.hpc.caltech.edu/documentation/slurm-commands. For example to ensure\nyou get an entire node to build on, use the following command:\n\n```\nsrun [--reservation=sxs_standing] [--constraint=skylake/cascadelake/icelake] \\\n  -t 02:00:00 -N 1 --exclusive -D . --pty /bin/bash\n```\n\nIf you are part of the SXS collaboration, you can use our reserved nodes by\nspecifying `--reservation=sxs_standing`. Our reserved nodes are currently\ncascadelake nodes with 56 cores each. If you want to use another type of node\nyou can specify a `--constraint`. However, note that the reservation flag won't\nwork for nodes other than cascadelake.\n\nBe sure to re-source the correct environment files once you get the interactive\nnode shell.\n\n## Ocean at Fullerton\n\nFollow the general instructions, using `ocean` for `SYSTEM_TO_RUN_ON`,\nyou do not need to install any dependencies, so you can skip steps 5 and 6.\n\n## Mbot at Cornell\n\nFollow steps 1-3 of the general instructions.\n\nThe only modules you need to load on `mbot` are `gcc/11.4.0 spectre-deps`.\nThis  will load everything you need, including LLVM/Clang. You\ncan source the environment file by running\n`. $SPECTRE_HOME/support/Environments/mbot.sh` and load the modules using\n`spectre_load_modules`. This offers two functions, `spectre_run_cmake_gcc`\nand `spectre_run_cmake_clang` for the different compilers.\nThese both default to `Release` mode. If you are developing code, please use\neither `spectre_run_cmake_clang -D CMAKE_BUILD_TYPE=Debug` or\n`spectre_run_cmake_clang -D SPECTRE_DEBUG=ON` (you can also use gcc).\nThe second command with `SPECTRE_DEBUG=ON` enables sanity checks and\noptimizations. This means it can be used in production-level runs to ensure\nthere aren't any subtle bugs that might only arise after a decently\nlong simulation.\n\n## Sonic at ICTS-TIFR\n\nCluster documentation: https://it.icts.res.in/docs/sonic-cluster\n\n1. Clone the spectre source code and set `SPECTRE_HOME` to point it.\n2. Create a directory for building SpECTRE (`SPECTRE_BUILD`).\n3. Setup the software environment\n    ```\n    . /mnt/pfs/vaishak.p/soft/modules-5.2.0/init/bash\n    ```\n    Add these lines to your `.bashrc` and source it\n    if you would like to make it persistent.\n4. Source the `$SPECTRE_HOME/support/Environments/Sonic.sh` script.\n5. Build SpECTRE by changing to `$SPECTRE_BUILD` and using\n   one of the variants of the `spectre_run_cmake` commands.\n   This by default compiles SpECTRE in Release mode,\n   without debug symbols and LTO, and with system malloc.\n   If you wish to compile in debug mode, with LTO/JEMALLOC, or other options,\n   simply suffix the additional cmake flags to the command.\n6. Use `make` to build your targets.\n"
  },
  {
    "path": "docs/Installation/VersioningAndReleases.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Versioning and releases {#versioning_and_releases}\n\n\\tableofcontents\n\n# Version format\n\nWe employ a date-based versioning scheme with the format `YYYY.0M.0D[.TWEAK]`,\nwhere `YYYY` is the year, `0M` the zero-padded month and `0D` the zero-padded\nday of the month when the version is released. If additional versions are\nreleased on a single day, the `TWEAK` component enumerates them. For example,\nthe first version released on May 4th, 2021 carries the name `2021.05.04`.\nAdditional versions released on May 4th, 2021 would carry the names\n`2021.05.04.1`, `2021.05.04.2`, etc.\n\nThe reason we use a date-based versioning scheme over, for example, semantic\nversioning is to avoid implying a notion of compatibility between releases (see\nsection below).\n\n# Guarantees and non-guarantees of releases\n\nEach release is **guaranteed** to pass all automated tests laid out in the\n\\ref github_actions_guide \"guide on automated tests\".\n\nWe provide **no guarantee** that releases satisfy any notion of \"compatibility\"\nwith each other. In particular, the interface of any C++ library, Python module\nor executable may change between releases. When writing code for SpECTRE we\nrecommend regularly rebasing on the latest `develop` branch of the\n[`sxs-collaboration/spectre`](https://github.com/sxs-collaboration/spectre)\nrepository and aiming to contribute your code upstream by following our \\ref\ncontributing_to_spectre \"contributing guide\" in a timely manner. That said, we\nmake an effort to retain compatibility between releases for the following\naspects of the code:\n\n- Simulation data: We try to ensure that data produced by a release remains\n  compatible with future releases, in the sense that the data can be read,\n  processed or converted to newer formats.\n- Python bindings: We try to offer deprecation warnings when changing the Python\n  interfaces so developers of external packages that use the Python bindings\n  have time to update their packages.\n\nWe may establish guarantees for specific interfaces related to these aspects.\nThese guarantees will be defined in the documentation of the respective\ninterface.\n\nWe explicitly provide **no guarantee** that input files remain compatible with\nexecutables built on different releases. Instead, we try to make input files as\nexplicit as possible by not supporting default values, so simulations don't\ncontinue with subtly changed parameters part way through. We try to reflect\nchanges that would affect running simulations in the input files, so they can\n(and often must) be reviewed before the simulation continues. Please note that\nlow-level changes or bugs may always alter results, so be advised to stick with\na particular compiled executable if a high level of reproducibility is crucial\nfor your project.\n\n# Schedule\n\nWe typically publish a release at the beginning of each month, and may publish\nadditional releases irregularly. If a major bug is discovered and fixed we will\nlikely create an unscheduled release.\n\n# How to find releases\n\nYou can find all releases on GitHub:\n\n- [sxs-collaboration/spectre releases on GitHub](https://github.com/sxs-collaboration/spectre/releases)\n\nEach release is tagged with the version name and prefixed `v` in the repository.\nFor example, this is how you can obtain the source code for the `2020.12.07`\nrelease:\n\n```sh\ngit clone -b v2020.12.07 https://github.com/sxs-collaboration/spectre\n```\n\nThe latest release is always available on the `release` branch. This is how you\ncan obtain the source code for the latest release:\n\n```sh\ngit clone -b release https://github.com/sxs-collaboration/spectre\n```\n"
  },
  {
    "path": "docs/Main.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\\mainpage Introduction\n\n\\tableofcontents\n\n\\htmlonly\n<p>\n<a\nhref=\"https://github.com/sxs-collaboration/spectre/blob/develop/LICENSE.txt\"><img\nsrc=\"https://img.shields.io/badge/license-MIT-blue.svg\"\nalt=\"license\"\ndata-canonical-src=\"https://img.shields.io/badge/license-MIT-blue.svg\"\nstyle=\"max-width:100%;\"></a>\n\n<a href=\"https://en.wikipedia.org/wiki/C%2B%2B#Standardization\"\nrel=\"nofollow\"><img\nsrc=\"https://img.shields.io/badge/c%2B%2B-17-blue.svg\"\nalt=\"Standard\"\ndata-canonical-src=\"https://img.shields.io/badge/c%2B%2B-17-blue.svg\"\nstyle=\"max-width:100%;\"></a>\n\n<a href=\"https://github.com/sxs-collaboration/spectre/actions\" rel=\"nofollow\"><img\nsrc=\"https://github.com/sxs-collaboration/spectre/workflows/Tests/badge.svg?branch=develop\"\nalt=\"Build Status\"\ndata-canonical-src=\"https://github.com/sxs-collaboration/spectre/workflows/Tests/badge.svg?branch=develop\"\nstyle=\"max-width:100%;\"></a>\n\n<a href=\"https://codecov.io/gh/sxs-collaboration/spectre\" rel=\"nofollow\"><img\nsrc=\"https://codecov.io/gh/sxs-collaboration/spectre/graph/badge.svg?token=yyJ3uBPUE2\"\nalt=\"codecov\"\ndata-canonical-src=\"https://codecov.io/gh/sxs-collaboration/spectre/graph/badge.svg?token=yyJ3uBPUE2\"\nstyle=\"max-width:100%;\"></a>\n\n</p>\n\n<img src=\"banner.png\"/>\n\\endhtmlonly\n\n# What is SpECTRE? {#intro_sec}\n\nSpECTRE is an open-source code for multi-scale, multi-physics problems in\nastrophysics and gravitational physics. It is based on high-order spectral\nfinite element methods and massive parallelism. In the future, we hope that\nit can be applied to problems across discipline boundaries in fluid\ndynamics, geoscience, plasma physics, nuclear physics, and\nengineering. It runs at petascale and is designed for future exascale\ncomputers.\n\nSpECTRE is being developed in support of our collaborative Simulating\neXtreme Spacetimes (SXS) research program into the multi-messenger\nastrophysics of black hole neutron star mergers, core-collapse supernovae, and\ngamma-ray bursts.\n\n## Gallery\n\nFor an overview of some of SpECTRE's features and some simulations performed\nwith SpECTRE, visit the gallery:\n\n- Gallery: https://spectre-code.org/gallery\n\n## Citing SpECTRE\n\nPlease cite SpECTRE in any publications that make use of its code or data. Cite\nthe latest version that you use in your publication. The DOI for this version\nis:\n\n- DOI: [\\spectredoi](https://doi.org/\\spectredoi)\n\nYou can cite this BibTeX entry in your publication:\n\n\\include citation.bib\n\nTo aid reproducibility of your scientific results with SpECTRE, we recommend you\nkeep track of the version(s) you used and report this information in your\npublication. We also recommend you supply the YAML input files and, if\nappropriate, any additional C++ code you wrote to compile SpECTRE executables as\nsupplemental material to the publication.\n\nSee our [publication policy](https://spectre-code.org/publication_policies.html)\nfor more information.\n\n## Navigating the Documentation {#navigate_documentation_sec}\n\nThe SpECTRE documentation is organized into tutorials, developer guides, groups\nof related code, namespaces, and files for easier navigation. These can all be\naccessed by links in the menu bar at the top.\n\n- For instructions on **installing SpECTRE** on personal computers and clusters\n  consult the \\ref installation \"Installation\" and \\ref installation_on_clusters\n  \"Installation on clusters\" pages, respectively. See the\n  \\ref spectre_build_system \"build system\" page for how to configure SpECTRE.\n- If you are looking to **run simulations with SpECTRE** we recommend starting\n  with the \\ref tutorials \"User Tutorials\". The tutorials are designed to get\n  users up and running with a simulation, as well as analyzing and visualizing\n  the output.\n- For people looking to **contribute to SpECTRE** there are tutorials on the\n  \\ref dev_guide \"Dev Guide\" page. For instance, the dev guide details the \\ref\n  code_review_guide \"code review guidelines\", how to \\ref writing_unit_tests\n  \"write unit tests\", how to \\ref writing_good_dox \"write documentation\", and\n  also provides information about C++ and the philosophy behind SpECTRE\n  development.\n- The [Code modules](modules.html) section contains groups of related code\n  (managed through doxygen groups). For example, there is a group for all the\n  data structures we use, a group for utility functions and classes, as well as\n  groups for coordinate maps, domain creation, and many others. The\n  [Code modules](modules.html) are designed to help developers discover existing\n  functionality so that things are not re-implemented several times.\n- You can also get an overview of the code base by namespace by visiting the\n  [Namespaces](namespaces.html) section.\n- To browse all files in the repository you can visit the\n  [GitHub repository](https://github.com/sxs-collaboration/spectre) directly.\n\n## Logos and visuals {#logos_and_visuals}\n\nYou can find logos and other visuals (e.g. to put on slides) in various formats,\ncolors, and sizes here:\n\n- [Logos and visuals](https://drive.google.com/drive/folders/1GEvVrXNqlGojr3NBf7RMFo6BzFmf9Ww7?usp=share_link)\n"
  },
  {
    "path": "docs/PublicationPolicy.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Publication policies {#publication_policies}\n\nSpECTRE is an open-source code that is developed and maintained by the\n[Simulating eXtreme Spacetimes (SXS) collaboration](https://black-holes.org). We\nask all authors of scientific publications that make use of SpECTRE's code or\ndata to follow the citation guidelines laid out in the section \\ref citing. An\nadditional set of guidelines, laid out in the section \\ref\nsxs_publication_policies applies to publications by members of the SXS\ncollaboration.\n\n## Citing SpECTRE {#citing}\n\nPlease cite SpECTRE in any publications that make use of its code or data. Cite\nthe latest version that you use in your publication. The DOI for this version\nis:\n\n- DOI: [\\spectredoi](https://doi.org/\\spectredoi)\n\nYou can cite this BibTeX entry in your publication:\n\n\\include citation.bib\n\nTo aid reproducibility of your scientific results with SpECTRE, we recommend you\nkeep track of the version(s) you used and report this information in your\npublication. We also recommend you supply the YAML input files and, if\nappropriate, any additional C++ code you wrote to compile SpECTRE executables as\nsupplemental material to the publication.\n\n## Policies that apply to SXS publications {#sxs_publication_policies}\n\nThese policies apply to all \"science papers\" by members of the SXS collaboration\nthat make use of SpECTRE’s code or data, as defined by the\n[SXS policies](https://github.com/sxs-collaboration/WelcomeToSXS/blob/master/SxsPolicies.md).\n\nOur publication policies have the goal that contributions to SpECTRE gain value\nin academia. For SpECTRE to be successful, it must be worth a junior\nresearcher's time to contribute to the code. Contributions are already partially\nacknowledged through authorship on the DOI, as laid out in the `Metadata.yaml`\nfile. However, authorship on the DOI alone doesn’t hold enough value in academia\nto make code contributions worthwhile that have no immediate science paper\nassociated with them, but that enable science papers down the line. Therefore,\nour policies have the purpose to grant authorship rights on science papers to\nthe developers of code that enabled them.\n\n### Guidelines for SXS paper authors\n\n- If you're a member of the SXS collaboration and use SpECTRE for your paper,\n  you should probably include a list of SpECTRE developers as co-authors per the\n  [SXS policies](https://github.com/sxs-collaboration/WelcomeToSXS/blob/master/SxsPolicies.md).\n- To obtain the list of co-authors, reach out to any of the people listed in the\n  [SpECTRE core developers](https://github.com/orgs/sxs-collaboration/teams/spectre-core-devs/members)\n  team on GitHub. Do this as early as possible, e.g., when you are setting up a\n  paper repository to share early drafts and results with other co-authors.\n  To contact the SpECTRE core developers, use a communication channel of your\n  choice. Here are a few possibilities:\n\n  - Send a message on Slack.\n  - Knock on their office door.\n  - Send an email to one of the core developers, or to\n    [core-devs@spectre-code.org](mailto:core-devs@spectre-code.org).\n    Possible wording of the email:\n\n    > Dear SpECTRE core devs,\n    >\n    > I am / we are preparing a paper on [title or topic]. I am / we are using\n    > SpECTRE in this way:\n    >\n    > - [Describe your use of SpECTRE briefly, e.g. as a bulleted list. Mention\n    >   which parts of SpECTRE you are using in particular, e.g. the initial\n    >   data solver, the evolution scheme, the wave extraction, etc.]\n    >\n    > Claims of the paper:\n    >\n    > - [Summarize the main claims you make in the paper. In particular, does\n    >   the paper make any claims about physics?]\n    >\n    > Please respond with the list of co-authors to include in the paper.\n    >\n    > Best regards,\n    > [you]\n\n  If you already have a draft or outline of your paper available, please feel\n  free to attach it, to give the core developers a better idea of the ways you\n  are using SpECTRE.\n\n  The core developers will discuss among themselves, following the guidelines\n  listed below, and will get back to you with a list of co-authors and their\n  contributions. In the spirit of transparency within the collaboration the core\n  developers will also share your description of the paper and the suggested\n  list of co-authors with the\n  [spectre-devel@black-holes.org](mailto:spectre-devel@black-holes.org) mailing\n  list.\n- Please make the paper draft available to all co-authors as early as possible,\n  e.g, by sharing access to a Git repository. This allows the co-authors to\n  contribute their technical expertise to the SpECTRE-related part of the paper,\n  and it also makes them aware of the ongoing work, in the spirit of\n  transparency and collaboration within SXS.\n\n## Guidelines to decide who has authorship rights on SXS science papers that use SpECTRE\n\n- A contribution to SpECTRE on the order of ~500 lines of code or more should\n  earn you authorship rights on the first science paper that uses the feature.\n  Major contributions, such as writing a full evolution system, earns you\n  authorship rights on the first three science papers that use the feature.\n  \"Using the feature\" means the code contributes to the result of the paper,\n  either obviously like a new coordinate map in the domain, or in a more subtle\n  way like an optimization that made the simulation run faster.\n- Core developers earn authorship rights to all science papers that use SpECTRE\n  for their infrastructure contributions.\n- In case of controversy, contact any person who you feel comfortable raising\n  your concern with, such as one of the core developers, a member of the\n  [executive committee](https://github.com/sxs-collaboration/WelcomeToSXS/blob/master/SxsPolicies.md#executive-committee),\n  or the [ombudsperson](https://github.com/sxs-collaboration/WelcomeToSXS/blob/master/SxsPolicies.md#ombudsperson).\n"
  },
  {
    "path": "docs/References.bib",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# When editing this file, please follow the guidelines in\n# https://spectre-code.org/writing_good_dox.html#writing_dox_citations.\n\n@article{Alcubierre2003pc,\n  author         = \"Alcubierre, Miguel and others\",\n  title          = \"{Toward standard testbeds for numerical relativity}\",\n  journal        = \"Class. Quant. Grav.\",\n  volume         = \"21\",\n  year           = \"2004\",\n  number         = \"2\",\n  pages          = \"589-613\",\n  url            = \"https://doi.org/10.1088/0264-9381/21/2/019\",\n  doi            = \"10.1088/0264-9381/21/2/019\",\n  eprint         = \"gr-qc/0305023\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  reportNumber   = \"AEI-2003-041\",\n  SLACcitation   = \"%%CITATION = GR-QC/0305023;%%\"\n}\n\n@article{Alic2012,\n  author    = {Daniela Alic and Philipp Moesta and Luciano Rezzolla\n               and Olindo Zanotti and Jos{\\'{e}} Luis Jaramillo},\n  doi       = {10.1088/0004-637x/754/1/36},\n  journal   = {The Astrophysical Journal},\n  number    = {1},\n  pages     = {36},\n  publisher = {American Astronomical Society},\n  title     = {Accurate Simulations of Binary Black Hole Mergers\n               in Force-free Electrodynamics},\n  url       = {https://doi.org/10.1088%2F0004-637x%2F754%2F1%2F36},\n  volume    = {754},\n  year      = 2012\n}\n\n@article{Anton2006,\n  author    = {Luis Antón and Olindo Zanotti and Juan A. Miralles\n               and José M. Martí and José M. Ibáñez and José A. Font\n               and José A. Pons},\n  doi       = {10.1086/498238},\n  journal   = {The Astrophysical Journal},\n  month     = {jan},\n  pages     = {296},\n  title     = {Numerical 3+1 General Relativistic Magnetohydrodynamics:\n               A Local Characteristic Approach},\n  url       = {https://dx.doi.org/10.1086/498238},\n  volume    = {637},\n  year      = {2006}\n}\n\n@article{Arnold2002,\n  author   = \"Arnold, Douglas and Brezzi, Franco and Cockburn, Bernardo and\n              L. Marini, Donatella\",\n  title    = \"Unified Analysis of Discontinuous {Galerkin} Methods for Elliptic\n              Problems\",\n  year     = \"2002\",\n  url      = \"https://doi.org/10.1137/S0036142901384162\",\n  doi      = \"10.1137/S0036142901384162\",\n  journal  = \"SIAM Journal on Numerical Analysis\",\n  number   = \"5\",\n  pages    = \"1749--1779\",\n  volume   = \"39\",\n}\n\n@article{Ayachour2003,\n  author    = \"Ayachour, E.H.\",\n  title     = \"A fast implementation for {GMRES} method\",\n  journal   = \"Journal of Computational and Applied Mathematics\",\n  volume    = \"159\",\n  number    = \"2\",\n  pages     = \"269 - 283\",\n  year      = \"2003\",\n  issn      = \"0377-0427\",\n  doi       = \"10.1016/S0377-0427(03)00534-X\",\n  url       = \"http://www.sciencedirect.com/science/article/pii/S037704270300534X\",\n}\n\n@article{Balsara1999,\n  author =       {{Balsara}, D.~S. and {Spicer}, D.~S.},\n  title =        \"{A Staggered Mesh Algorithm Using High Order Godunov\n                  Fluxes to Ensure Solenoidal Magnetic Fields in\n                  Magnetohydrodynamic Simulations}\",\n  journal =      {Journal of Computational Physics},\n  year =         1999,\n  month =        mar,\n  volume =       149,\n  pages =        {270-292},\n  doi =          {10.1006/jcph.1998.6153},\n  url =          {https://doi.org/10.1006/jcph.1998.6153},\n  adsurl =       {http://adsabs.harvard.edu/abs/1999JCoPh.149..270B},\n  adsnote =      {Provided by the SAO/NASA Astrophysics Data System}\n}\n\n@article{Balsara2001,\n  doi =          {10.1086/318941},\n  url =          {https://doi.org/10.1086/318941},\n  year =         2001,\n  month =        jan,\n  publisher =    {American Astronomical Society},\n  volume =       132,\n  number =       1,\n  pages =        {83--101},\n  author =       {Dinshaw Balsara},\n  title =        {Total Variation Diminishing Scheme for Relativistic\n                  Magnetohydrodynamics},\n  journal =      {Astrophysical Journal, Supplement},\n}\n\n@article{Balsara2016780,\n  title =   {An efficient class of WENO schemes with adaptive order},\n  journal = {Journal of Computational Physics},\n  volume =  326,\n  pages =   {780-804},\n  year =    2016,\n  issn =    {0021-9991},\n  doi =     {10.1016/j.jcp.2016.09.009},\n  url =     {https://www.sciencedirect.com/science/article/pii/S0021999116304211},\n  author =  {Dinshaw S. Balsara and Sudip Garain and Chi-Wang Shu}\n}\n\n@article{Barkett2019uae,\n      author         = \"Barkett, Kevin and Moxon, Jordan and Scheel, Mark A. and\n                        Szilágyi, Béla\",\n      title          = \"{Spectral Cauchy-characteristic extraction of the\n                        gravitational wave news function}\",\n      year           = \"2019\",\n      eprint         = \"1910.09677\",\n      archivePrefix  = \"arXiv\",\n      primaryClass   = \"gr-qc\",\n      SLACcitation   = \"%%CITATION = ARXIV:1910.09677;%%\"\n}\n\n@article{Barreto2004fn,\n  author        = {Barreto, W. and Da Silva, A. and Gomez, R. and Lehner, L.\n                   and Rosales, L. and Winicour, J.},\n  title         = {{The 3-dimensional Einstein-Klein-Gordon system in\n                    characteristic numerical relativity}},\n  eprint        = {gr-qc/0412066},\n  archiveprefix = {arXiv},\n  doi           = {10.1103/PhysRevD.71.064028},\n  journal       = {Phys. Rev. D},\n  volume        = {71},\n  pages         = {064028},\n  year          = {2005},\n  url           = {https://doi.org/10.1103/PhysRevD.71.064028}\n}\n\n@article{Baumgarte1996hh,\n  author         = \"Baumgarte, Thomas W. and Cook, Gregory B. and Scheel,\n                    Mark A. and Shapiro, Stuart L. and Teukolsky, Saul A.\",\n  title          = \"Implementing an apparent horizon finder in\n                    three-dimensions\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D54\",\n  year           = \"1996\",\n  pages          = \"4849-4857\",\n  doi            = \"10.1103/PhysRevD.54.4849\",\n  url            = \"https://doi.org/10.1103/PhysRevD.54.4849\",\n  eprint         = \"gr-qc/9606010\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  reportNumber   = \"CRSR-1115\",\n}\n\n@article{Baumgarte2006ug,\n  author         = \"Baumgarte, Thomas W. and Ó Murchadha, Niall and Pfeiffer,\n                    Harald P.\",\n  title          = \"The {Einstein} constraints: Uniqueness and non-uniqueness\n                    in the conformal thin sandwich approach\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D75\",\n  year           = \"2007\",\n  pages          = \"044009\",\n  doi            = \"10.1103/PhysRevD.75.044009\",\n  url            = \"https://doi.org/10.1103/PhysRevD.75.044009\",\n  eprint         = \"gr-qc/0610120\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = GR-QC/0610120;%%\"\n}\n\n@article{Baumgarte2007ht,\n    author = \"Baumgarte, Thomas W. and Naculich, Stephen G.\",\n    title = \"{Analytical representation of a black hole puncture solution}\",\n    eprint = \"gr-qc/0701037\",\n    archivePrefix = \"arXiv\",\n    reportNumber = \"BOW-PH-138\",\n    doi = \"10.1103/PhysRevD.75.067502\",\n    url = {https://doi.org/10.1103/PhysRevD.75.067502},\n    journal = \"Phys. Rev. D\",\n    volume = \"75\",\n    pages = \"067502\",\n    year = \"2007\"\n}\n\n@book{BaumgarteShapiro,\n  author    = {Baumgarte, Thomas W. and Shapiro, Stuart L.},\n  title     = {Numerical Relativity: Solving {Einstein's} Equations on the\n               Computer},\n  year      = {2010},\n  url       = {https://doi.org/10.1017/CBO9781139193344},\n  doi       = {10.1017/CBO9781139193344},\n  publisher = {Cambridge University Press}\n}\n\n@article{BaylissTurkel,\n  author =  {Bayliss, Alvin and Turkel, Eli},\n  title =   {Radiation boundary conditions for wave-like equations},\n  journal = {Communications on Pure and Applied Mathematics},\n  volume =  33,\n  number =  6,\n  pages =   {707-725},\n  doi =     {10.1002/cpa.3160330603},\n  url =     {https://onlinelibrary.wiley.com/doi/abs/10.1002/cpa.3160330603},\n  eprint =  {https://onlinelibrary.wiley.com/doi/pdf/10.1002/cpa.3160330603},\n  year =    1980\n}\n\n@article{Beckwith2011iy,\n  author         = \"Beckwith, Kris and Stone, James M.\",\n  title          = \"A Second-order {Godunov} Method for Multi-dimensional\n                    Relativistic Magnetohydrodynamics\",\n  year           = \"2011\",\n  url            = \"https://doi.org/10.1088/0067-0049/193/1/6\",\n  doi            = \"10.1088/0067-0049/193/1/6\",\n  eprint         = \"1101.3573\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"astro-ph.HE\",\n  journal        = \"Astrophysical Journal, Supplement\",\n  number         = \"1\",\n  pages          = \"6\",\n  volume         = \"193\",\n}\n\n@article{Bhagwat2017tkm,\n author          = \"Bhagwat, Swetha and Okounkova, Maria and Ballmer,\n                    Stefan W. and Brown, Duncan A. and Giesler,\n                    Matthew and Scheel, Mark A. and Teukolsky, Saul A.\",\n title           = \"{On choosing the start time of binary black hole\n                    ringdowns}\",\n eprint          = \"1711.00926\",\n archivePrefix   = \"arXiv\",\n primaryClass    = \"gr-qc\",\n journal         = \"Phys. Rev. D\",\n volume          = \"97\",\n number          = \"10\",\n pages           = \"104065\",\n year            = \"2018\",\n doi             = \"10.1103/PhysRevD.97.104065\",\n url             = \"https://journals.aps.org/prd/abstract/10.1103/\n                    PhysRevD.97.104065\",\n}\n\n@article{Bishop1997ik,\n  author         = \"Bishop, Nigel T. and Gomez, Roberto and Lehner, Luis and\n                    Maharaj, Manoj and Winicour, Jeffrey\",\n  title          = \"{High powered gravitational news}\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D56\",\n  year           = \"1997\",\n  pages          = \"6298-6309\",\n  doi            = \"10.1103/PhysRevD.56.6298\",\n  eprint         = \"gr-qc/9708065\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  url            = \"https://doi.org/10.1103/PhysRevD.56.6298\",\n}\n\n@incollection{Bishop1998uk,\n      author         = \"Bishop, Nigel T. and Gomez, Roberto and Lehner, Luis and\n                        Szilagyi, Bela and Winicour, Jeffrey and Isaacson,\n                        Richard A.\",\n      title          = \"{Cauchy characteristic matching}\",\n      editor         = \"Iyer, Bala R. and Bhawal, Biblap\",\n      booktitle      = \"Black Holes, Gravitational Radiation and the Universe:\n                        Essays in Honor of C.V. Vishveshwara\",\n      journal        = \"#DONE:In *Iyer, B.R. (ed.) et al.: Black holes,\n                        gravitational radiation and the universe* 383-408\",\n      pages          = \"383-408\",\n      doi            = \"10.1007/978-94-017-0934-7_24\",\n      year           = \"1998\",\n      eprint         = \"gr-qc/9801070\",\n      archivePrefix  = \"arXiv\",\n      primaryClass   = \"gr-qc\",\n      SLACcitation   = \"%%CITATION = GR-QC/9801070;%%\"\n}\n\n@article{Bjorhus1995,\nauthor = {Bjørhus, Morten},\ntitle = {The ODE Formulation of Hyperbolic PDEs Discretized by the Spectral\n         Collocation Method},\njournal = {SIAM Journal on Scientific Computing},\nvolume = {16},\nnumber = {3},\npages = {542-557},\nyear = {1995},\ndoi = {10.1137/0916035},\nURL = {https://doi.org/10.1137/0916035},\neprint = {https://doi.org/10.1137/0916035}\n}\n\n@article{Blanchet:2013haa,\n  archiveprefix = {arXiv},\n  author        = {Blanchet, Luc},\n  doi           = {10.12942/lrr-2014-2},\n  eprint        = {1310.1528},\n  journal       = {Living Rev. Rel.},\n  pages         = {2},\n  primaryclass  = {gr-qc},\n  title         = {Gravitational Radiation from Post-Newtonian Sources and\n                   Inspiralling Compact Binaries},\n  volume        = {17},\n  year          = {2014}\n}\n\n@article{Bohn:2014xxa,\n  author        = {Bohn, Andy and Throwe, William and H\\'ebert, Fran\\c{c}ois and\n                   Henriksson, Katherine and Bunandar, Darius and Scheel,\n                   Mark A. and Taylor, Nicholas W.},\n  title         = {What does a binary black hole merger look like?},\n  eprint        = {1410.7775},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1088/0264-9381/32/6/065002},\n  journal       = {Class. Quant. Grav.},\n  volume        = {32},\n  number        = {6},\n  pages         = {065002},\n  year          = {2015}\n}\n\n@article{Bohn:2016afc,\n  author        = {Bohn, Andy and Kidder, Lawrence E. and Teukolsky, Saul A.},\n  title         = {Parallel adaptive event horizon finder for numerical\n                   relativity},\n  eprint        = {1606.00437},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.94.064008},\n  journal       = {Phys. Rev. D},\n  volume        = {94},\n  number        = {6},\n  pages         = {064008},\n  year          = {2016}\n}\n\n@article{Borges20083191,\n  title =   {An improved weighted essentially non-oscillatory scheme for\n             hyperbolic conservation laws},\n  journal = {Journal of Computational Physics},\n  volume =  227,\n  number =  6,\n  pages =   {3191-3211},\n  year =    2008,\n  issn =    {0021-9991},\n  doi =     {10.1016/j.jcp.2007.11.038},\n  url =   {https://www.sciencedirect.com/science/article/pii/S0021999107005232},\n  author = {Rafael Borges and Monique Carmona and Bruno Costa and Wai Sun\n                  Don},\n}\n\n@article{Borrell2018,\n  title =        {Parallel mesh partitioning based on space filling curves},\n  journal =      {Computers & Fluids},\n  volume =       173,\n  pages =        {264-272},\n  year =         2018,\n  issn =         {0045-7930},\n  doi =          {10.1016/j.compfluid.2018.01.040},\n  url =          {https://www.sciencedirect.com/science/article/pii/S0045793018300446},\n  author =       {R.Borrell and J.C.Cajas and D.Mira and A.Taha and S.Koric and\n                  M.Vázquez and G.Houzeaux}\n}\n\n@book{Boyd2001,\n  author =       {Boyd, John P.},\n  title =        {Chebyshev and Fourier Spectral Methods},\n  year =         2001,\n  isbn =         {0-486-41183-4},\n  publisher =    {Dover Publications}\n}\n\n@article{Boyle:2015nqa,\n  author =       \"Boyle, Michael\",\n  title =        \"{Transformations of asymptotic gravitational-wave data}\",\n  eprint =       \"1509.00862\",\n  archivePrefix =\"arXiv\",\n  primaryClass = \"gr-qc\",\n  doi =          \"10.1103/PhysRevD.93.084031\",\n  journal =      \"Phys.Rev.D\",\n  volume =       \"93\",\n  number =       \"8\",\n  pages =        \"084031\",\n  year =         \"2016\"\n}\n\n@article{Boyle2019kee,\n      author         = \"Boyle, Michael and others\",\n      title          = \"{The SXS Collaboration catalog of binary black hole\n                        simulations}\",\n      year           = \"2019\",\n      eprint         = \"1904.04831\",\n      archivePrefix  = \"arXiv\",\n      primaryClass   = \"gr-qc\",\n      SLACcitation   = \"%%CITATION = ARXIV:1904.04831;%%\"\n}\n\n@article{BrandtBruegmann1997,\n  title          = {A Simple Construction of Initial Data for Multiple Black\n                    Holes},\n  author         = {Brandt, Steven and Br\\\"ugmann, Bernd},\n  journal        = {Phys. Rev. Lett.},\n  volume         = {78},\n  issue          = {19},\n  pages          = {3606--3609},\n  numpages       = {0},\n  year           = {1997},\n  month          = {May},\n  publisher      = {American Physical Society},\n  doi            = {10.1103/PhysRevLett.78.3606},\n  url            = {https://link.aps.org/doi/10.1103/PhysRevLett.78.3606},\n  eprint         = {gr-qc/0404056},\n  archivePrefix  = {arXiv},\n  primaryClass   = {gr-qc}\n}\n\n@article{Brehm2015,\n  author   = {Christoph Brehm and Michael F. Barad and Jeffrey A. Housman and\n              Cetin C. Kiris},\n  doi      = {https://doi.org/10.1016/j.compfluid.2015.08.023},\n  issn     = {0045-7930},\n  journal  = {Computers & Fluids},\n  keywords = {Higher order, Shock capturing, Finite difference, WENO, Localized\n              artificial diffusivity, Artificial dissipation},\n  pages    = {184-208},\n  title    = {A comparison of higher-order finite-difference shock capturing\n              schemes},\n  url      = {https://www.sciencedirect.com/science/article/pii/S0045793015002996},\n  volume   = {122},\n  year     = {2015}\n}\n\n@book{Briggs2000jp,\n  title  = \"A Multigrid Tutorial, Second Edition\",\n  author = \"Briggs, William L and Henson, Van Emden and McCormick, Steve F\",\n  year   =  2000,\n  url    = \"http://dx.doi.org/10.1137/1.9780898719505\",\n  doi    = \"10.1137/1.9780898719505\",\n  publisher = \"Society for Industrial and Applied Mathematics\"\n}\n\n@article{Brill1963yv,\n  author =       \"Brill, Dieter R. and Lindquist, Richard W.\",\n  title =        \"{Interaction energy in geometrostatics}\",\n  url =          \"https://dx.doi.org/10.1103/PhysRev.131.471\",\n  doi =          \"10.1103/PhysRev.131.471\",\n  journal =      \"Phys. Rev.\",\n  volume =       131,\n  pages =        \"471--476\",\n  year =         1963\n}\n\n@article{Brune2015,\n  title          = \"Composing Scalable Nonlinear Algebraic Solvers\",\n  volume         = \"57\",\n  issn           = \"1095-7200\",\n  url            = \"https://doi.org/10.1137/130936725\",\n  doi            = \"10.1137/130936725\",\n  number         = \"4\",\n  journal        = \"SIAM Review\",\n  publisher      = \"Society for Industrial & Applied Mathematics (SIAM)\",\n  author         = \"Brune, Peter R. and Knepley, Matthew G. and Smith, Barry F.\n                    and Tu, Xuemin\",\n  year           = \"2015\",\n  month          = \"Jan\",\n  pages          = \"535–565\",\n  eprint         = \"1607.04254\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"math\",\n}\n\n@article{Buchman:2012dw,\n    author = \"Buchman, Luisa T. and Pfeiffer, Harald P. and Scheel, Mark A.\n             and Szilagyi, Bela\",\n    title = \"{Simulations of non-equal mass black hole binaries with\n              spectral methods}\",\n    eprint = \"1206.3015\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1103/PhysRevD.86.084033\",\n    journal = \"Phys. Rev. D\",\n    volume = \"86\",\n    pages = \"084033\",\n    year = \"2012\"\n}\n\n@article{Casoni2012,\n  author  = {Casoni, E. and Peraire, J. and Huerta, A.},\n  title   = {One-dimensional shock-capturing for high-order discontinuous\n             Galerkin methods},\n  journal = {International Journal for Numerical Methods in Fluids},\n  volume  = 71,\n  number  = 6,\n  pages   = {737-755},\n  doi     = {10.1002/fld.3682},\n  url     = {https://onlinelibrary.wiley.com/doi/abs/10.1002/fld.3682},\n  eprint  = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/fld.3682},\n  year    = 2013\n}\n\n@article{CerdaDuran2007,\n  author =       {{Cerd{\\'a}-Dur{\\'a}n}, P. and {Font}, J.~A. and {Dimmelmeier},\n                  H.},\n  title =        \"General relativistic simulations of\n                  passive-magneto-rotational core collapse with microphysics\",\n  journal =      {Astronomy and Astrophysics},\n  year =         2007,\n  month =        oct,\n  volume =       474,\n  number =       1,\n  pages =        {169-191},\n  doi =          {10.1051/0004-6361:20077432},\n  url =          {https://doi.org/10.1051/0004-6361:20077432},\n  archivePrefix ={arXiv},\n  eprint =       {astro-ph/0703597}\n}\n\n@article{CerdaDuran2008,\n  author =       {{Cerd{\\'a}-Dur{\\'a}n}, P. and {Font}, J.~A. and {Ant{\\'o}n},\n                  L. and {M{\\\"u}ller}, E.},\n  title =        \"A new general relativistic magnetohydrodynamics code for\n                  dynamical spacetimes\",\n  journal =      {Astronomy and Astrophysics},\n  year =         2008,\n  month =        dec,\n  volume =       492,\n  number =       3,\n  pages =        {937-953},\n  doi =          {10.1051/0004-6361:200810086},\n  url =          {https://doi.org/10.1051/0004-6361:200810086},\n  archivePrefix ={arXiv},\n  eprint =       {0804.4572}\n}\n\n@book{Chandrasekhar1939,\n  author = \"Chandrasekhar, Subrahmanyan\",\n  title  = \"An introduction to the study of stellar structure\",\n  year   = \"1939\"\n}\n\n@article{CHEN2016604,\n  title =   {A fifth-order finite difference scheme for hyperbolic\n             equations on block-adaptive curvilinear grids},\n  journal = {Journal of Computational Physics},\n  volume =  305,\n  pages =   {604-621},\n  year =    2016,\n  issn =    {0021-9991},\n  doi =     {10.1016/j.jcp.2015.11.003},\n  url =   {https://www.sciencedirect.com/science/article/pii/S0021999115007378},\n  author = {Yuxi Chen and Gábor Tóth and Tamas I. Gombosi},\n}\n\n@article{Cipolletta2019,\n  author =       {{Cipolletta}, F. and {Kalinani}, J.~V. and {Giacomazzo},\n                  B. and {Ciolfi}, R.},\n  title =        \"{Spritz: a new fully general-relativistic magnetohydrodynamic\n                  code}\",\n  journal =      {Classical and Quantum Gravity},\n  year =         2020,\n  month =        jul,\n  volume =       37,\n  number =       13,\n  eid =          135010,\n  pages =        135010,\n  doi =          {10.1088/1361-6382/ab8be8},\n  url =          {https://doi.org/10.1088/1361-6382/ab8be8},\n  archivePrefix ={arXiv},\n  eprint =       {1912.04794}\n}\n\n@article{CLAIN20114028,\n  title   = {A high-order finite volume method for systems of conservation\n             laws—Multi-dimensional Optimal Order Detection (MOOD)},\n  author  = \"S. Clain and S. Diot and R. Loubère\",\n  journal = \"Journal of Computational Physics\",\n  volume  = 230,\n  number  = 10,\n  pages   = \"4028 - 4050\",\n  year    = 2011,\n  issn    = \"0021-9991\",\n  doi     = \"10.1016/j.jcp.2011.02.026\",\n  url     = \"http://www.sciencedirect.com/science/article/pii/S002199911100115X\"\n}\n\n@book{Cockburn1999,\n  author     = \"Cockburn, Bernardo\",\n  editor     = \"Barth, Timothy J. and Deconinck, Herman\",\n  title      = \"Discontinuous Galerkin Methods for Convection-Dominated\n                Problems\",\n  bookTitle  = \"High-Order Methods for Computational Physics\",\n  year       = \"1999\",\n  publisher  = \"Springer Berlin Heidelberg\",\n  address    = \"Berlin, Heidelberg\",\n  pages      = \"69-224\",\n  isbn       = \"978-3-662-03882-6\",\n  doi        = \"10.1007/978-3-662-03882-6_2\",\n  url        = \"https://doi.org/10.1007/978-3-662-03882-6_2\"\n}\n\n@ARTICLE{Cook1992,\n  author =       {{Cook}, Gregory B. and {Shapiro}, Stuart L. and {Teukolsky},\n                  Saul A.},\n  title =        \"{Spin-up of a Rapidly Rotating Star by Angular Momentum Loss:\n                  Effects of General Relativity}\",\n  journal =      {The Astrophysical Journal},\n  year =         1992,\n  month =        oct,\n  volume =       {398},\n  pages =        {203},\n  doi =          {10.1086/171849},\n  url =          {https://doi.org/10.1086/171849}\n}\n\n@ARTICLE{Cook1994,\n  author =       {{Cook}, Gregory B. and {Shapiro}, Stuart L. and {Teukolsky},\n                  Saul A.},\n  title =        \"{Rapidly Rotating Neutron Stars in General Relativity:\n                  Realistic Equations of State}\",\n  journal =      {The Astrophysical Journal},\n  year =         1994,\n  month =        apr,\n  volume =       {424},\n  pages =        {823},\n  doi =          {10.1086/173934},\n  url =          {https://doi.org/10.1086/173934}\n}\n\n@article{Cook1997qc,\n    author = \"Cook, Gregory B. and Scheel, Mark A.\",\n    title = \"{Well behaved harmonic time slices of a charged, rotating, boosted\n             black hole}\",\n    reportNumber = \"CRSR-1121\",\n    doi = \"10.1103/PhysRevD.56.4775\",\n    journal = \"Phys. Rev. D\",\n    volume = \"56\",\n    pages = \"4775--4781\",\n    year = \"1997\",\n    url = \"https://journals.aps.org/prd/abstract/10.1103/PhysRevD.56.4775\"\n}\n\n@article{Davis1988,\n  author   = \"Davis, S. F.\",\n  title    = \"Simplified Second-Order Godunov-Type Methods\",\n  journal  = \"SIAM J. Sci. Stat. Comput.\",\n  volume   = \"9\",\n  year     = \"1988\",\n  number   = \"3\",\n  pages    = \"445-473\",\n  doi      = \"10.1137/0909030\",\n  url      = \"https://doi.org/10.1137/0909030\"\n}\n\n@article{Deaton2013,\n  author    = {M. Brett Deaton and Matthew D. Duez and Francois Foucart\n               and Evan O'Connor and Christian D. Ott and Lawrence E. Kidder\n               and Curran D. Muhlberger and Mark A. Scheel and Bela Szilagyi},\n  doi       = {10.1088/0004-637X/776/1/47},\n  journal   = {The Astrophysical Journal},\n  month     = {sep},\n  pages     = {47},\n  publisher = {The American Astronomical Society},\n  title     = {BLACK HOLE–NEUTRON STAR MERGERS WITH A HOT NUCLEAR EQUATION OF\n               STATE: OUTFLOW AND NEUTRINO-COOLED DISK FOR A LOW-MASS,\n               HIGH-SPIN CASE},\n  url       = {https://dx.doi.org/10.1088/0004-637X/776/1/47},\n  volume    = {776},\n  year      = {2013}\n}\n\n@article{Dedner2002,\n  author   = \"Dedner, A. and Kemm, F. and Kröner, D. and Munz, C.-D. and\n              Schnitzer, T. and Wesenberg, M.\",\n  title    = \"Hyperbolic Divergence Cleaning for the {MHD} Equations\",\n  year     = \"2002\",\n  url      = \"https://doi.org/10.1006/jcph.2001.6961\",\n  doi      = \"10.1006/jcph.2001.6961\",\n  journal  = \"Journal of Computational Physics\",\n  volume   = \"175\",\n  number   = \"2\",\n  pages    = \"645-673\",\n}\n\n@article{DelZanna2002rv,\n      author         = \"Del Zanna, L. and Bucciantini, N. and Londrillo, P.\",\n      title          = \"{An efficient shock-capturing central-type scheme for\n                        multidimensional relativistic flows. II.\n                        Magnetohydrodynamics}\",\n      journal        = \"Astron. Astrophys.\",\n      volume         = \"400\",\n      year           = \"2003\",\n      pages          = \"397-414\",\n      doi            = \"10.1051/0004-6361:20021641\",\n      url            = \"https://doi.org/10.1051/0004-6361:20021641\",\n      eprint         = \"astro-ph/0210618\",\n      archivePrefix  = \"arXiv\",\n      primaryClass   = \"astro-ph\",\n      SLACcitation   = \"%%CITATION = ASTRO-PH/0210618;%%\"\n}\n\n@article{DelZanna2007pk,\n  author         = \"Del Zanna, L. and Zanotti, O. and Bucciantini, N. and\n                    Londrillo, P.\",\n  title          = \"{ECHO}: An {Eulerian Conservative High Order} scheme for\n                    general relativistic magnetohydrodynamics and\n                    magnetodynamics\",\n  journal        = \"Astron. Astrophys.\",\n  volume         = \"473\",\n  year           = \"2007\",\n  pages          = \"11-30\",\n  doi            = \"10.1051/0004-6361:20077093\",\n  url            = \"https://doi.org/10.1051/0004-6361:20077093\",\n  eprint         = \"0704.3206\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"astro-ph\",\n  SLACcitation   = \"%%CITATION = ARXIV:0704.3206;%%\"\n}\n\n@article{Dennison:2012vf,\n    author = \"Dennison, Kenneth A. and Baumgarte, Thomas W.\",\n    title = \"{Invariants for Tendex and Vortex Fields}\",\n    eprint = \"1208.1218\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1103/PhysRevD.86.107503\",\n    journal = \"Phys. Rev. D\",\n    volume = \"86\",\n    pages = \"107503\",\n    year = \"2012\"\n}\n\n@book{DennisSchnabel,\n  author  = \"Dennis, Jr., J. E. and Schnabel, Robert B.\",\n  title   = \"Numerical Methods for Unconstrained Optimization and Nonlinear\n             Equations\",\n  doi     = \"10.1137/1.9781611971200\",\n  url     = \"https://doi.org/10.1137/1.9781611971200\",\n  year    = \"1996\",\n  publisher = \"Society for Industrial and Applied Mathematics\"\n}\n\n@article{Deppe2018uye,\n  author         = \"Deppe, Nils and Kidder, Lawrence E. and Scheel, Mark A.\n                    and Teukolsky, Saul A.\",\n  title          = \"{Critical behavior in 3D gravitational collapse of\n                    massless scalar fields}\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D99\",\n  year           = \"2019\",\n  number         = \"2\",\n  pages          = \"024018\",\n  doi            = \"10.1103/PhysRevD.99.024018\",\n  url            = \"https://doi.org/10.1103/PhysRevD.99.024018\",\n  eprint         = \"1802.08682\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = ARXIV:1802.08682;%%\"\n}\n\n@article{Deppe2021ada,\n  author        = {Deppe, Nils and H\\'ebert, Fran\\c{c}ois and Kidder, Lawrence\n                   E. and Teukolsky, Saul A.},\n  title         = {{A high-order shock capturing discontinuous\n                   Galerkin\\textendash{}finite difference hybrid method for\n                   GRMHD}},\n  eprint        = {2109.11645},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1088/1361-6382/ac8864},\n  journal       = {Class. Quant. Grav.},\n  volume        = {39},\n  number        = {19},\n  pages         = {195001},\n  year          = {2022}\n}\n\n@article{Deppe2021bhi,\n  author        = {Deppe, Nils and others},\n  title         = {{Simulating magnetized neutron stars with discontinuous\n                   Galerkin methods}},\n  eprint        = {2109.12033},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.105.123031},\n  journal       = {Phys. Rev. D},\n  volume        = {105},\n  number        = {12},\n  pages         = {123031},\n  year          = {2022}\n}\n\n@article{Deppe2024ckt,\n  author        = {Deppe, Nils and others},\n  title         = {{Binary neutron star mergers using a discontinuous\n                   Galerkin-finite difference hybrid method}},\n  eprint        = {2406.19038},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  month         = {6},\n  year          = {2024}\n}\n\n@article{Derry1970fk,\n  author =       \"Derry, L. and Isaacson, R. and Winicour, J.\",\n  title =        \"{Shear-free gravitational radiation}\",\n  doi =          \"10.1103/PhysRev.185.1647\",\n  journal =      \"Phys.Rev.\",\n  volume =       \"185\",\n  pages =        \"1647--1655\",\n  year =         \"1969\"\n}\n\n@article{Detweiler2003,\n  title = {Self-force via a Green's function decomposition},\n  author = {Detweiler, Steven and Whiting, Bernard F.},\n  journal = {Phys. Rev. D},\n  volume = {67},\n  issue = {2},\n  pages = {024025},\n  numpages = {5},\n  year = {2003},\n  month = {Jan},\n  publisher = {American Physical Society},\n  doi = {10.1103/PhysRevD.67.024025},\n  url = {https://link.aps.org/doi/10.1103/PhysRevD.67.024025}\n}\n\n@article{Diener2005tn,\n  author =       \"Diener, Peter and Dorband, Ernst Nils and Schnetter, Erik and\n                  Tiglio, Manuel\",\n  title =        \"{New, efficient, and accurate high order derivative and\n                  dissipation operators satisfying summation by parts, and\n                  applications in three-dimensional multi-block evolutions}\",\n  eprint =       \"gr-qc/0512001\",\n  archivePrefix =\"arXiv\",\n  doi =          \"10.1007/s10915-006-9123-7\",\n  journal =      \"J. Sci. Comput.\",\n  volume =       \"32\",\n  pages =        \"109--145\",\n  year =         \"2007\"\n}\n\n@article{Dimmelmeier2001,\n  doi = {10.1086/324406},\n  url = {https://doi.org/10.1086%2F324406},\n  year = 2001,\n  month = {oct},\n  publisher = {American Astronomical Society},\n  volume = {560},\n  number = {2},\n  pages = {L163--L166},\n  author = {Harald Dimmelmeier and Jose A. Font and Ewald Muller},\n  title = {Gravitational Waves from Relativistic Rotational Core Collapse},\n  journal = {The Astrophysical Journal}\n}\n\n@article{DIOT201243,\n  title   = {Improved detection criteria for the Multi-dimensional Optimal\n             Order Detection (MOOD) on unstructured meshes with very\n             high-order polynomials},\n  author  = \"S. Diot and S. Clain and R. Loubère\",\n  journal = \"Computers & Fluids\",\n  volume  = 64,\n  pages   = \"43 - 63\",\n  year    = 2012,\n  issn    = \"0045-7930\",\n  doi     = \"10.1016/j.compfluid.2012.05.004\",\n  url     = {http://www.sciencedirect.com/science/article/pii/S0045793012001909},\n}\n\n@article{Diot2013,\n  author  = {Diot, S. and Loubère, R. and Clain, S.},\n  title   = {The Multidimensional Optimal Order Detection method in the\n             three-dimensional case: very high-order finite volume method\n             for hyperbolic systems},\n  journal = {International Journal for Numerical Methods in Fluids},\n  volume  = 73,\n  number  = 4,\n  pages   = {362-392},\n  doi     = {10.1002/fld.3804},\n  url     = {https://onlinelibrary.wiley.com/doi/abs/10.1002/fld.3804},\n  eprint  = {https://onlinelibrary.wiley.com/doi/pdf/10.1002/fld.3804},\n  year    = 2013\n}\n\n@article{Dumbser2007,\n  author   = \"Dumbser, M. and K{\\\"a}ser, M.\",\n  title    = \"Arbitrary high order non-oscillatory finite volume schemes on\n              unstructured meshes for linear hyperbolic systems\",\n  journal  = \"Journal of Computational Physics\",\n  volume   = \"221\",\n  year     = \"2007\",\n  pages    = \"693-723\",\n  doi      = \"10.1016/j.jcp.2006.06.043\",\n  url      = \"https://doi.org/10.1016/j.jcp.2006.06.043\",\n}\n\n@article{Dumbser2013,\n  author   = {Michael Dumbser and Arturo Hidalgo and Olindo Zanotti},\n  title    = {High order space–time adaptive {ADER-WENO} finite volume schemes\n              for non-conservative hyperbolic systems},\n  journal  = {Computer Methods in Applied Mechanics and Engineering},\n  volume   = {268},\n  pages    = {359-387},\n  year     = {2014},\n  issn     = {0045-7825},\n  doi      = {https://doi.org/10.1016/j.cma.2013.09.022},\n  url      = {https://www.sciencedirect.com/science/article/pii/S0045782513002491}\n}\n\n@article{Dumbser2014a,\n  title =        \"A posteriori subcell limiting of the discontinuous Galerkin\n                  finite element method for hyperbolic conservation laws\",\n  journal =      \"Journal of Computational Physics\",\n  volume =       \"278\",\n  pages =        \"47 - 75\",\n  year =         2014,\n  issn =         \"0021-9991\",\n  doi =          \"10.1016/j.jcp.2014.08.009\",\n  url =          \"https://doi.org/10.1016/j.jcp.2014.08.009\",\n  author =       \"Michael Dumbser and Olindo Zanotti and Raphaël Loubère and\n                  Steven Diot\",\n}\n\n@article{Dumbser2017okk,\n    author = \"{Dumbser, Michael and Guercilena, Federico and K\\\"oppel, Sven and\n               Rezzolla, Luciano and Zanotti, Olindo}\",\n    title = \"{Conformal and covariant Z4 formulation of the Einstein equations:\n              strongly hyperbolic first-order reduction and solution with\n              discontinuous Galerkin schemes}\",\n    eprint = \"1707.09910\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1103/PhysRevD.97.084053\",\n    url = \"https://doi.org/10.1103/PhysRevD.97.084053\",\n    journal = \"Phys. Rev. D\",\n    volume = \"97\",\n    number = \"8\",\n    pages = \"084053\",\n    year = \"2018\"\n}\n\n@article{Estabrook1973ue,\n    author = \"{Estabrook, Frank and Wahlquist, Hugo and Christensen, Steven and\n               DeWitt, Bryce and Smarr, Larry and Tsiang, Elaine}\",\n    title = \"{Maximally slicing a black hole}\",\n    doi = \"10.1103/PhysRevD.7.2814\",\n    url = \"https://doi.org/10.1103/PhysRevD.7.2814\",\n    journal = \"Phys. Rev. D\",\n    volume = \"7\",\n    pages = \"2814--2817\",\n    year = \"1973\"\n}\n\n\n@article{Etienne2008re,\n  author        = {Etienne, Zachariah B. and Liu, Yuk Tung and Shapiro, Stuart\n                   L. and Baumgarte, Thomas W.},\n  title         = {General relativistic simulations of black-hole-neutron-star\n                   mergers: Effects of black-hole spin},\n  eprint        = {0812.2245},\n  archiveprefix = {arXiv},\n  primaryclass  = {astro-ph},\n  doi           = {10.1103/PhysRevD.79.044024},\n  journal       = {Phys. Rev. D},\n  volume        = {79},\n  pages         = {044024},\n  year          = {2009}\n}\n\n@article{Etienne2010ui,\n  author        = \"Etienne, Zachariah B. and Liu, Yuk Tung and Shapiro, Stuart\n                  L.\",\n  title         = \"{Relativistic magnetohydrodynamics in dynamical spacetimes: A\n                  new AMR implementation}\",\n  journal       = \"Phys. Rev.\",\n  volume        = \"D82\",\n  year          = \"2010\",\n  pages         = \"084031\",\n  doi           = \"10.1103/PhysRevD.82.084031\",\n  eprint        = \"1007.2848\",\n  archivePrefix = \"arXiv\",\n  primaryClass  = \"astro-ph.HE\",\n  SLACcitation  = \"%%CITATION = ARXIV:1007.2848;%%\"\n}\n\n@article{Etienne2017,\n  author    = {Zachariah B Etienne and Mew-Bing Wan and Maria C Babiuc\n               and Sean T McWilliams and Ashok Choudhary},\n  doi       = {10.1088/1361-6382/aa8ab3},\n  journal   = {Classical and Quantum Gravity},\n  month     = {sep},\n  number    = {21},\n  pages     = {215001},\n  publisher = {{IOP} Publishing},\n  title     = {{GiRaFFE}: an open-source general relativistic force-free\n               electrodynamics code},\n  url       = {https://doi.org/10.1088%2F1361-6382%2Faa8ab3},\n  volume    = {34},\n  year      = 2017\n}\n\n@article{Fischer2021voj,\n  author        = {Fischer, Nils L. and Pfeiffer, Harald P.},\n  title         = {Unified discontinuous {Galerkin} scheme for a large class of\n                   elliptic equations},\n  year          = {2022},\n  month         = {Jan},\n  eprint        = {2108.05826},\n  archiveprefix = {arXiv},\n  primaryclass  = {math.NA},\n  journal       = {Phys. Rev. D},\n  volume        = {105},\n  issue         = {2},\n  pages         = {024034},\n  numpages      = {22},\n  doi           = {10.1103/PhysRevD.105.024034}\n}\n\n@article{Fishbone1976apj,\n  author   = \"Fishbone, L. G. and Moncrief, V.\",\n  title    = \"Relativistic Fluid Disks in Orbit Around {Kerr} Black Holes\",\n  year     = \"1976\",\n  url      = \"https://doi.org/10.1086/154565\",\n  doi      = \"10.1086/154565\",\n  journal  = \"The Astrophysical Journal\",\n  number   = \"1\",\n  pages    = \"962--976\",\n  volume   = \"207\",\n}\n\n@ARTICLE{Font1998,\n  author =       {{Font}, Jos{\\'e} A. and {Ib{\\'a}{\\~n}ez}, J.~M.},\n  title =        \"{A Numerical Study of Relativistic Bondi-Hoyle Accretion onto\n                  a Moving Black Hole: Axisymmetric Computations in a\n                  Schwarzschild Background}\",\n  journal =      {The Astrophysical Journal},\n  year =         1998,\n  month =        \"Feb\",\n  volume =       494,\n  number =       1,\n  pages =        {297-316},\n  doi =          {10.1086/305205},\n  adsurl =       {https://ui.adsabs.harvard.edu/abs/1998ApJ...494..297F},\n}\n\n@book{Fornberg1996,\n  author =       {Fornberg, Bengt},\n  title =        {A Practical Guide to Pseudospectral Methods},\n  year =         1996,\n  isbn =         {0-521-49582-2},\n  url =          {https://doi.org/10.1017/CBO9780511626357},\n  doi =          {10.1017/CBO9780511626357},\n  publisher =    {Cambridge University Press}\n}\n\n@article{Fornberg1998,\n  author =       {Fornberg, Bengt},\n  title =        {Classroom Note: Calculation of Weights in Finite\n                  Difference Formulas},\n  journal =      {SIAM Review},\n  volume =       40,\n  number =       3,\n  pages =        {685-691},\n  year =         1998,\n  doi =          {10.1137/S0036144596322507},\n  url =          \"https://doi.org/10.1137/S0036144596322507\"\n}\n\n@article{Fortunato2019jl,\n  title     = \"Efficient Operator-Coarsening Multigrid Schemes for Local\n               Discontinuous {Galerkin} Methods\",\n  author    = \"Fortunato, Daniel and Rycroft, Chris H and Saye, Robert\",\n  journal   = \"SIAM J. Sci. Comput.\",\n  publisher = \"Society for Industrial and Applied Mathematics\",\n  volume    =  41,\n  number    =  6,\n  pages     = \"A3913--A3937\",\n  month     =  jan,\n  year      =  2019,\n  url       = \"https://doi.org/10.1137/18M1206357\",\n  issn      = \"1064-8275\",\n  doi       = \"10.1137/18M1206357\"\n}\n\n@article{Foucart2008qt,\n  author        = {Foucart, Francois and Kidder, Lawrence E. and Pfeiffer,\n                   Harald P. and Teukolsky, Saul A.},\n  title         = {Initial data for black hole-neutron star binaries: A\n                   Flexible, high-accuracy spectral method},\n  eprint        = {0804.3787},\n  archivePrefix = {arXiv},\n  primaryClass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.77.124051},\n  journal       = {Phys. Rev. D},\n  volume        = {77},\n  pages         = {124051},\n  year          = {2008}\n}\n\n@article{Foucart2015vpa,\n  author         = \"Foucart, Francois and O'Connor, Evan and Roberts, Luke\n                    and Duez, Matthew D. and Haas, Roland and Kidder,\n                    Lawrence E. and Ott, Christian D. and Pfeiffer,\n                    Harald P. and Scheel, Mark A. and Szilagyi, Bela\",\n  title          = \"{Post-merger evolution of a neutron star-black hole\n                    binary with neutrino transport}\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D91\",\n  year           = \"2015\",\n  number         = \"12\",\n  pages          = \"124021\",\n  doi            = \"10.1103/PhysRevD.91.124021\",\n  eprint         = \"1502.04146\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"astro-ph.HE\",\n  SLACcitation   = \"%%CITATION = ARXIV:1502.04146;%%\"\n}\n\n@article{Foucart2017mbt,\n    author = \"Foucart, Francois\",\n    title = \"{Monte Carlo closure for moment-based transport schemes in\n              general relativistic radiation hydrodynamic simulations}\",\n    eprint = \"1708.08452\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"astro-ph.HE\",\n    doi = \"10.1093/mnras/sty108\",\n    journal = \"Mon. Not. Roy. Astron. Soc.\",\n    volume = \"475\",\n    number = \"3\",\n    pages = \"4186--4207\",\n    year = \"2018\"\n}\n\n@article{Foucart2021mcb,\n    author = \"Foucart, Francois and Duez, Matthew D. and Hebert, Francois and\n              Kidder, Lawrence E. and Kovarik, Phillip and Pfeiffer, Harald P.\n              and Scheel, Mark A.\",\n    title = \"{Implementation of Monte Carlo Transport in the General\n              Relativistic SpEC Code}\",\n    eprint = \"2103.16588\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"astro-ph.HE\",\n    doi = \"10.3847/1538-4357/ac1737\",\n    journal = \"Astrophys. J.\",\n    volume = \"920\",\n    number = \"2\",\n    pages = \"82\",\n    year = \"2021\"\n}\n\n@article{Galeazzi2013mia,\n    author = \"Galeazzi, Filippo and Kastaun, Wolfgang and Rezzolla, Luciano and\n              Font, Jos\\'e A.\",\n    title = \"{Implementation of a simplified approach to radiative transfer\n              in general relativity}\",\n    eprint = \"1306.4953\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1103/PhysRevD.88.064009\",\n    journal = \"Phys. Rev. D\",\n    volume = \"88\",\n    pages = \"064009\",\n    year = \"2013\"\n}\n\n@article{Gammie2003,\n  author   = \"Gammie, Charles F. and McKinney, Jonathan C. and Tóth, Gábor\",\n  title    = \"{HARM}: A Numerical Scheme for General Relativistic\n              Magnetohydrodynamics\",\n  journal  = \"The Astrophysical Journal\",\n  volume   = \"589\",\n  number   = \"1\",\n  pages    = \"444\",\n  year     = \"2003\",\n  doi      = \"10.1086/374594\",\n  url      = \"https://doi.org/10.1086/374594\",\n}\n\n@article{Gardiner2005hy,\n      author         = \"Gardiner, Thomas A. and Stone, James M.\",\n      title          = \"{An Unsplit Godunov method for ideal MHD via constrained\n                        transport}\",\n      journal        = \"J. Comput. Phys.\",\n      volume         = \"205\",\n      year           = \"2005\",\n      pages          = \"509-539\",\n      doi            = \"10.1016/j.jcp.2004.11.016\",\n      url            = \"https://10.1016/j.jcp.2004.11.016\",\n      eprint         = \"astro-ph/0501557\",\n      archivePrefix  = \"arXiv\",\n      primaryClass   = \"astro-ph\",\n      SLACcitation   = \"%%CITATION = ASTRO-PH/0501557;%%\"\n}\n\n@article{Gassner20114232,\n  title =        {Explicit one-step time discretizations for discontinuous\n                  Galerkin and finite volume schemes based on local predictors},\n  journal =      {Journal of Computational Physics},\n  volume =       {230},\n  number =       {11},\n  pages =        {4232-4247},\n  year =         {2011},\n  note =         {Special issue High Order Methods for CFD Problems},\n  issn =         {0021-9991},\n  doi =          {https://doi.org/10.1016/j.jcp.2010.10.024},\n  url =  {https://www.sciencedirect.com/science/article/pii/S0021999110005802},\n  author =       {Gregor Gassner and Michael Dumbser and Florian Hindenlang and\n                  Claus-Dieter Munz}\n}\n\n@article{Giacomazzo2006,\n  author =       {{Giacomazzo}, Bruno and {Rezzolla}, Luciano},\n  title =        \"{The exact solution of the Riemann problem in relativistic\n                  magnetohydrodynamics}\",\n  journal =      {Journal of Fluid Mechanics},\n  keywords =     {General Relativity and Quantum Cosmology, Astrophysics},\n  year =         2006,\n  month =        sep,\n  volume =       562,\n  pages =        {223-259},\n  doi =          {10.1017/S0022112006001145},\n  url =          {https://doi.org/10.1017/S0022112006001145},\n  archivePrefix ={arXiv},\n  eprint =       {gr-qc/0507102},\n  primaryClass = {gr-qc}\n}\n\n@article{Goldberg1966uu,\n  author   = \"Goldberg, J. N. and MacFarlane, A. J. and Newman, E. T.\n              and Rohrlich, F. and Sudarshan, E. C. G.\",\n  title    = \"{Spin s spherical harmonics and edth}\",\n  journal  = \"J. Math. Phys.\",\n  volume   = \"8\",\n  year     = \"1967\",\n  pages    = \"2155\",\n  doi      = \"10.1063/1.1705135\",\n  url      = \"https://doi.org/10.1063/1.1705135\"\n}\n\n@article{Gundlach1997us,\n  author         = \"Gundlach, Carsten\",\n  title          = \"Pseudospectral apparent horizon finders: An Efficient\n                    new algorithm\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D57\",\n  year           = \"1998\",\n  pages          = \"863-875\",\n  doi            = \"10.1103/PhysRevD.57.863\",\n  url            = \"https://doi.org/10.1103/PhysRevD.57.863\",\n  eprint         = \"gr-qc/9707050\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n}\n\n@book{Hairer1993,\n  address = {Berlin},\n  author = {Hairer, E. and N{\\o}rsett, S.P. and Wanner, G.},\n  keywords = {imported},\n  publisher = {Springer},\n  title = {Solving Ordinary Differential Equations {I} Nonstiff problems},\n  year = 1993\n}\n\n@Inbook{Hairer1996,\n  author=\"Hairer, Ernst and Wanner, Gerhard\",\n  title=\"Solving Ordinary Differential Equations II: Stiff and\n         Differential-Algebraic Problems\",\n  year=\"1996\",\n  publisher=\"Springer Berlin Heidelberg\",\n  address=\"Berlin, Heidelberg\",\n  isbn=\"978-3-642-05221-7\",\n  doi=\"10.1007/978-3-642-05221-7_1\",\n  url=\"https://doi.org/10.1007/978-3-642-05221-7_1\"\n}\n\n@article{Handmer2014qha,\n  author         = \"Handmer, Casey J. and Szilagyi, B.\",\n  title          = \"{Spectral Characteristic Evolution: A New Algorithm for\n                    Gravitational Wave Propagation}\",\n  journal        = \"Class. Quant. Grav.\",\n  volume         = \"32\",\n  year           = \"2015\",\n  number         = \"2\",\n  pages          = \"025008\",\n  doi            = \"10.1088/0264-9381/32/2/025008\",\n  eprint         = \"1406.7029\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  url            = \"https://doi.org/10.1088/0264-9381/32/2/025008\"\n}\n\n@article{Hannam2008,\n  title =        {Wormholes and trumpets: Schwarzschild spacetime for\n                  the moving-puncture generation},\n  author =       {Hannam, Mark and Husa, Sascha and Ohme, Frank and\n                  Br\\\"ugmann, Bernd and \\'O Murchadha, Niall},\n  journal =      {Phys. Rev. D},\n  volume =       78,\n  issue =        6,\n  pages =        064020,\n  numpages =     19,\n  year =         2008,\n  month =        {Sep},\n  publisher =    {American Physical Society},\n  doi =          {10.1103/PhysRevD.78.064020},\n  url =          {https://link.aps.org/doi/10.1103/PhysRevD.78.064020}\n}\n\n@article{Harten1983,\n  author         = \"Harten, A., and Lax, P. D., and B. van Leer\",\n  title          = \"{On Upstream Differencing and Godunov-Type Schemes\n                     for Hyperbolic Conservation Laws}\",\n  journal        = \"SIAM Review\",\n  volume         = \"25\",\n  year           = \"1983\",\n  number         = \"1\",\n  pages          = \"35-61\",\n  doi            = \"10.1137/1025002\",\n  url            = \"https://doi.org/10.1137/1025002\"\n}\n\n@article{Harten19973,\n  title =   {Uniformly High Order Accurate Essentially Non-oscillatory\n            Schemes, III},\n  author =  \"Ami Harten and Bjorn Engquist and Stanley Osher and Sukumar\n            R. Chakravarthy\",\n  journal = \"Journal of Computational Physics\",\n  volume =  131,\n  number =  1,\n  pages =   \"3 - 47\",\n  year =    1997,\n  issn =    \"0021-9991\",\n  doi =     \"10.1006/jcph.1996.5632\",\n  url =     {http://www.sciencedirect.com/science/article/pii/S0021999196956326}\n}\n\n@book{Hartle2003gravity,\n  author     = \"Hartle, J.B.\",\n  title      = \"Gravity: An Introduction to {Einstein's} General Relativity\",\n  isbn       = \"9780805386622\",\n  lccn       = \"2002151118\",\n  year       = \"2003\",\n  publisher  = \"Addison-Wesley\",\n  url        = \"https://www.pearson.com/us/higher-education/program/Hartle-Gravity-An-Introduction-to-Einstein-s-General-Relativity/PGM298455.html\",\n}\n\n@ARTICLE{Heger2005,\n       author = {{Heger}, A. and {Woosley}, S.~E. and {Spruit}, H.~C.},\n        title = \"{Presupernova Evolution of Differentially Rotating Massive\n         Stars Including Magnetic Fields}\",\n      journal = {The Astrophysical Journal},\n     keywords = {Stars: Pulsars: General, Stars: Evolution, Stars: Magnetic\n      Fields, Stars: Rotation, Astrophysics},\n         year = 2005,\n        month = jun,\n       volume = {626},\n       number = {1},\n        pages = {350-363},\n          doi = {10.1086/429868},\narchivePrefix = {arXiv},\n       eprint = {astro-ph/0409422},\n primaryClass = {astro-ph},\n       adsurl = {https://ui.adsabs.harvard.edu/abs/2005ApJ...626..350H},\n      adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n}\n\n@article{Hemberger2012jz,\n  author         = \"Hemberger, Daniel A. and Scheel, Mark A. and Kidder,\n                    Lawrence E. and Szilágyi, Béla and Lovelace, Geoffrey\n                    and Taylor, Nicholas W. and Teukolsky, Saul A.\",\n  title          = \"Dynamical Excision Boundaries in Spectral Evolutions of\n                    Binary Black Hole Spacetimes\",\n  journal        = \"Class. Quant. Grav.\",\n  volume         = \"30\",\n  year           = \"2013\",\n  pages          = \"115001\",\n  doi            = \"10.1088/0264-9381/30/11/115001\",\n  url            = \"https://doi.org/10.1088/0264-9381/30/11/115001\",\n  eprint         = \"1211.6079\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n}\n\n@book{HesthavenWarburton,\n  author     = \"Hesthaven, Jan S. and Warburton, Tim\",\n  title      = \"Nodal Discontinuous {Galerkin} Methods\",\n  year       = \"2008\",\n  url        = \"https://doi.org/10.1007/978-0-387-72067-8\",\n  doi        = \"10.1007/978-0-387-72067-8\",\n  publisher  = \"Springer\",\n}\n\n@article{Hilditch:2012fp,\n    author = \"Hilditch, David and Bernuzzi, Sebastiano and Thierfelder, Marcus\n              and Cao, Zhoujian and Tichy, Wolfgang and Bruegmann, Bernd\",\n    title = \"{Compact binary evolutions with the Z4c formulation}\",\n    eprint = \"1212.2901\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1103/PhysRevD.88.084057\",\n    url = \"https://doi.org/10.1103/PhysRevD.88.084057\",\n    journal = \"Phys. Rev. D\",\n    volume = \"88\",\n    pages = \"084057\",\n    year = \"2013\"\n}\n\n@article{Holst2004wt,\n  author         = \"Holst, Michael and Lindblom, Lee and Owen, Robert and\n                    Pfeiffer, Harald P. and Scheel, Mark A. and Kidder,\n                    Lawrence E.\",\n  title          = \"Optimal constraint projection for hyperbolic evolution\n                    systems\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D70\",\n  year           = \"2004\",\n  pages          = \"084017\",\n  doi            = \"10.1103/PhysRevD.70.084017\",\n  url            = \"https://doi.org/10.1103/PhysRevD.70.084017\",\n  eprint         = \"gr-qc/0407011\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = GR-QC/0407011;%%\"\n}\n\n@Article{Hou2007,\n  author  = \"Hou, Songming and Liu, Xu-Dong\",\n  title   = \"Solutions of Multi-dimensional Hyperbolic Systems of\n             Conservation Laws by Square Entropy Condition Satisfying\n             Discontinuous Galerkin Method\",\n  journal = \"Journal of Scientific Computing\",\n  year    = 2007,\n  month   = \"May\",\n  day     = 01,\n  volume  = 31,\n  number  = 1,\n  pages   = \"127--151\",\n  issn    = \"1573-7691\",\n  doi     = \"10.1007/s10915-006-9105-9\",\n  url     = \"https://doi.org/10.1007/s10915-006-9105-9\"\n}\n\n@article{Jiang1996,\n  author  = {Guang-Shan Jiang and Chi-Wang Shu},\n  doi     = {https://doi.org/10.1006/jcph.1996.0130},\n  issn    = {0021-9991},\n  journal = {Journal of Computational Physics},\n  number  = {1},\n  pages   = {202-228},\n  title   = {Efficient Implementation of Weighted ENO Schemes},\n  url     = {https://www.sciencedirect.com/science/article/pii/S0021999196901308},\n  volume  = {126},\n  year    = {1996}\n}\n\n@article{Kastaun2020uxr,\n  author =       \"Kastaun, Wolfgang and Kalinani, Jay Vijay and Ciolfi,\n                  Riccardo\",\n  title =        \"{Robust Recovery of Primitive Variables in Relativistic Ideal\n                  Magnetohydrodynamics}\",\n  eprint =       \"2005.01821\",\n  archivePrefix =\"arXiv\",\n  primaryClass = \"gr-qc\",\n  doi =          \"10.1103/PhysRevD.103.023018\",\n  url =          \"https://doi.org/10.1103/PhysRevD.103.023018\",\n  journal =      \"Phys. Rev. D\",\n  volume =       103,\n  number =       2,\n  pages =        023018,\n  year =         2021\n}\n\n@article{Kennedy2003,\n  author = \"Kennedy, Christopher A. and Carpenter, Mark H.\",\n  title = \"Additive {Runge-Kutta} schemes for convection-diffusion-reaction\n           equations\",\n  journal = \"Applied Numerical Mathematics\",\n  volume = \"44\",\n  number = \"1\",\n  pages = \"139--181\",\n  year = \"2003\",\n  doi = \"10.1016/S0168-9274(02)00138-1\",\n  issn = \"0168-9274\",\n  url = \"https://www.sciencedirect.com/science/article/pii/S0168927402001381\",\n}\n\n@techreport{Kennedy2016,\n  author = \"Kennedy, Christopher A. and Carpenter, Mark H.\",\n  title = \"Diagonally Implicit {Runge-Kutta} Methods for Ordinary Differential\n           Equations.  {A} Review\",\n  number = \"20160005923\",\n  institution = \"NASA STI Repository\",\n  year = \"2016\",\n  month = \"March\",\n  url = \"https://ntrs.nasa.gov/citations/20160005923\"\n}\n\n@article{Kidder2001tz,\n  author        = \"Kidder, Lawrence E. and Scheel, Mark A. and\n                   Teukolsky, Saul A.\",\n  title         = \"Extending the lifetime of 3-D black hole computations with a\n                   new hyperbolic system of evolution equations\",\n  eprint        = {gr-qc/0105031},\n  archiveprefix = {arXiv},\n  doi           = {10.1103/PhysRevD.64.064017},\n  journal       = {Phys. Rev. D},\n  volume        = {64},\n  pages         = {064017},\n  year          = {2001}\n}\n\n@article{Kidder2004rw,\n    author        = \"Kidder, Lawrence E. and Lindblom, Lee and\n                     Scheel, Mark A. and Buchman, Luisa T. and\n                     Pfeiffer, Harald P.\",\n    title         = \"{Boundary conditions for the Einstein evolution system}\",\n    eprint        = \"gr-qc/0412116\",\n    archivePrefix = \"arXiv\",\n    doi           = \"10.1103/PhysRevD.71.064020\",\n    journal       = \"Phys. Rev. D\",\n    volume        = \"71\",\n    pages         = \"064020\",\n    year          = \"2005\"\n}\n\n@article{Kidder2016hev,\n  author         = \"Kidder, Lawrence E. and Field, Scott E. and Foucart,\n                    Francois and Schnetter, Erik and Teukolsky, Saul A. and\n                    Bohn, Andy and Deppe, Nils and Diener, Peter and Hébert,\n                    François and Lippuner, Jonas and Miller, Jonah and Ott,\n                    Christian D. and Scheel, Mark A. and Vincent, Trevor\",\n  title          = \"{SpECTRE}: A task-based discontinuous {Galerkin} code for\n                    relativistic astrophysics\",\n  year           = \"2017\",\n  url            = \"https://doi.org/10.1016/j.jcp.2016.12.059\",\n  doi            = \"10.1016/j.jcp.2016.12.059\",\n  eprint         = \"1609.00098\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"astro-ph.HE\",\n  issn           = \"0021-9991\",\n  journal        = \"Journal of Computational Physics\",\n  pages          = \"84--114\",\n  volume         = \"335\",\n}\n\n@article{Komissarov1999,\n  author   = \"Komissarov, S. S.\",\n  title    = \"A {Godunov}-type scheme for relativistic magnetohydrodynamics\",\n  year     = \"1999\",\n  url      = \"https://doi.org/10.1046/j.1365-8711.1999.02244.x\",\n  journal  = \"Monthly Notices of the Royal Astronomical Society\",\n  volume   = \"303\",\n  number   = \"2\",\n  pages    = \"343--366\",\n}\n\n@article{Komissarov2002,\n  author  = {Komissarov, S. S.},\n  doi     = {10.1046/j.1365-8711.2002.05313.x},\n  issn    = {0035-8711},\n  journal = {Monthly Notices of the Royal Astronomical Society},\n  number  = {3},\n  pages   = {759-766},\n  title   = {{Time-dependent, force-free, degenerate electrodynamics}},\n  url     = {https://doi.org/10.1046/j.1365-8711.2002.05313.x},\n  volume  = {336},\n  year    = {2002}\n}\n\n@article{Komissarov2004,\n  author  = {Komissarov, S. S.},\n  doi     = {10.1111/j.1365-2966.2004.07598.x},\n  issn    = {0035-8711},\n  journal = {Monthly Notices of the Royal Astronomical Society},\n  month   = may,\n  number  = {2},\n  pages   = {427--448},\n  title   = {Electrodynamics of black hole magnetospheres},\n  url     = {https://doi.org/10.1111/j.1365-2966.2004.07598.x},\n  urldate = {2022-09-28},\n  volume  = {350},\n  year    = {2004}\n}\n\n@article{Komissarov2006,\n  author   = {Komissarov, S. S.},\n  doi      = {https://doi.org/10.1111/j.1365-2966.2005.09932.x},\n  eprint   = {https://onlinelibrary.wiley.com/doi/pdf/10.1111/j.1365-2966.2005.09932.x},\n  journal  = {Monthly Notices of the Royal Astronomical Society},\n  keywords = {MHD, relativity, pulsars: general, stars: winds and outflows},\n  number   = {1},\n  pages    = {19-31},\n  title    = {Simulations of the axisymmetric magnetospheres of neutron stars},\n  url      = {https://onlinelibrary.wiley.com/doi/abs/10.1111/j.1365-2966.2005.09932.x},\n  volume   = {367},\n  year     = {2006}\n}\n\n@article{Komissarov2011,\n  author   = {Komissarov, S. S.},\n  doi      = {https://doi.org/10.1111/j.1745-3933.2011.01150.x},\n  eprint   = {https://onlinelibrary.wiley.com/doi/pdf/10.1111/j.1745-3933.2011.01150.x},\n  journal  = {Monthly Notices of the Royal Astronomical Society: Letters},\n  keywords = {black hole physics, magnetic fields, relativistic processes},\n  number   = {1},\n  pages    = {L94-L98},\n  title    = {3+1 magnetodynamics},\n  url      = {https://onlinelibrary.wiley.com/doi/abs/10.1111/j.1745-3933.2011.01150.x},\n  volume   = {418},\n  year     = {2011}\n}\n\n@book{Kopriva,\n  author     = \"Kopriva, David A.\",\n  title      = \"Implementing Spectral Methods for Partial Differential\n                Equations\",\n  year       = \"2009\",\n  url        = \"https://doi.org/10.1007/978-90-481-2261-5\",\n  doi        = \"10.1007/978-90-481-2261-5\",\n  publisher  = \"Springer\",\n}\n\n@article{Kozlowski1978aa,\n  author   = \"Kozlowski, M. and Jaroszynski, M. and Abramowicz, M. A.\",\n  title    = \"The analytic theory of fluid disks orbiting the {Kerr}\n              black hole\",\n  year     = \"1978\",\n  url      = \"http://cdsads.u-strasbg.fr/abs/1978A%26A....63..209K\",\n  journal  = \"Astronomy and Astrophysics\",\n  volume   = \"63\",\n  number   = \"1-2\",\n  pages    = \"209--220\",\n}\n\n@inproceedings{Kreiss1973,\n  title =        {Methods for the approximate solution of time dependent\n                  problems},\n  author =       {Heinz-Otto Kreiss},\n  year =         {1973},\n  volume =       {10},\n  series =       {Global Atmospheric Research Programme},\n  publisher =    {International Council of Scientific Unions, World\n                  Meteorological Organization}\n}\n\n@article{Krivodonova2004,\n  author   = \"L. Krivodonova and J. Xin and J.-F. Remacle and N. Chevaugeon\n              and J.E. Flaherty\",\n  title    = \"Shock detection and limiting with discontinuous {Galerkin} methods\n              for hyperbolic conservation laws\",\n  journal  = \"Applied Numerical Mathematics\",\n  volume   = \"48\",\n  number   = \"3\",\n  pages    = \"323 - 338\",\n  year     = \"2004\",\n  note     = \"Workshop on Innovative Time Integrators for PDEs\",\n  issn     = \"0168-9274\",\n  doi      = \"https://doi.org/10.1016/j.apnum.2003.11.002\",\n  url      = \"http://www.sciencedirect.com/science/article/pii/S0168927403001831\",\n}\n\n@article{Krivodonova2007,\n  title    = \"Limiters for high-order discontinuous {Galerkin} methods\",\n  journal  = \"Journal of Computational Physics\",\n  volume   = \"226\",\n  number   = \"1\",\n  pages    = \"879 - 896\",\n  year     = \"2007\",\n  issn     = \"0021-9991\",\n  url      = \"https://doi.org/10.1016/j.jcp.2007.05.011\",\n  doi      = \"10.1016/j.jcp.2007.05.011\",\n  author   = \"Krivodonova, Lilia\"\n}\n\n@book{Kulikovskii2000,\n  title      = \"Mathematical Aspects of Numerical Solution of Hyperbolic\n                Systems\",\n  author     = \"Kulikovskii, A. G. and Pogorelov, N. V. and Semenov, A. Y.\",\n  isbn       = \"9780367397739\",\n  series     = \"Monographs and Surveys in Pure and Applied Mathematics\",\n  url        = \"https://www.crcpress.com/Mathematical-Aspects-of-Numerical-Solution-of-Hyperbolic-Systems/Kulikovskii-Pogorelov-Semenov/p/book/9780367397739\",\n  year       = \"2000\",\n  publisher  = \"CRC Press\"\n}\n\n@article{Kuzmin2014,\n  title    = \"Hierarchical slope limiting in explicit and implicit discontinuous\n              Galerkin methods\",\n  author   = \"Kuzmin, Dmitri\",\n  journal  = \"Journal of Computational Physics\",\n  volume   = \"257\",\n  pages    = \"1140-1162\",\n  year     = \"2014\",\n  issn     = \"0021-9991\",\n  doi      = \"https://doi.org/10.1016/j.jcp.2013.04.032\",\n  url      = \"https://www.sciencedirect.com/science/article/pii/S0021999113003161\",\n}\n\n@article{Lara2024rwa,\n  author        = {Lara, Guillermo and Pfeiffer, Harald P. and Wittek, Nikolas\n                   A. and Vu, Nils L. and Nelli, Kyle C. and Carpenter,\n                   Alexander and Lovelace, Geoffrey and Scheel, Mark A. and\n                   Throwe, William},\n  title         = {{Scalarization of isolated black holes in scalar Gauss-Bonnet\n                   theory in the fixing-the-equations approach}},\n  eprint        = {2403.08705},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.110.024033},\n  journal       = {Phys. Rev. D},\n  volume        = {110},\n  number        = {2},\n  pages         = {024033},\n  year          = {2024}\n}\n\n@article{Lehner2010pn,\n  author =       \"Lehner, Luis and Pretorius, Frans\",\n  title =        \"{Black Strings, Low Viscosity Fluids, and Violation of Cosmic\n                  Censorship}\",\n  eprint =       \"1006.5960\",\n  archivePrefix =\"arXiv\",\n  primaryClass = \"hep-th\",\n  doi =          \"10.1103/PhysRevLett.105.101102\",\n  url =          \"https://doi.org/10.1103/PhysRevLett.105.101102\",\n  journal =      \"Phys. Rev. Lett.\",\n  volume =       \"105\",\n  pages =        \"101102\",\n  year =         \"2010\"\n}\n\n@article{Liebendorfer2005,\ndoi = {10.1086/466517},\nurl = {https://dx.doi.org/10.1086/466517},\nyear = {2005},\nmonth = {nov},\npublisher = {},\nvolume = {633},\nnumber = {2},\npages = {1042},\nauthor = {Matthias Liebendörfer},\ntitle = {A Simple Parameterization of the Consequences of Deleptonization\n for Simulations of Stellar Core Collapse},\njournal = {The Astrophysical Journal}\n}\n\n@article{Lindblom1998dp,\n  author         = \"Lindblom, Lee\",\n  title          = \"Phase transitions and the mass radius curves of\n                    relativistic stars\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D58\",\n  year           = \"1998\",\n  pages          = \"024008\",\n  doi            = \"10.1103/PhysRevD.58.024008\",\n  url            = \"https://doi.org/10.1103/PhysRevD.58.024008\",\n  eprint         = \"gr-qc/9802072\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  reportNumber   = \"GRP-494\",\n  SLACcitation   = \"%%CITATION = GR-QC/9802072;%%\"\n}\n\n@article{Lindblom2005qh,\n  author         = \"Lindblom, Lee and Scheel, Mark A. and Kidder,\n                    Lawrence E. and Owen, Robert and Rinne, Oliver\",\n  title          = \"A New generalized harmonic evolution system\",\n  year           = \"2006\",\n  url            = \"https://doi.org/10.1088/0264-9381/23/16/S09\",\n  doi            = \"10.1088/0264-9381/23/16/S09\",\n  journal        = \"Class. Quant. Grav.\",\n  volume         = \"23\",\n  pages          = \"S447-S462\",\n  eprint         = \"gr-qc/0512093\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = GR-QC/0512093;%%\"\n}\n\n@article{Loehner1987,\n  title    = {An adaptive finite element scheme for transient problems in CFD},\n  journal  = {Computer Methods in Applied Mechanics and Engineering},\n  volume   = {61},\n  number   = {3},\n  pages    = {323-338},\n  year     = {1987},\n  issn     = {0045-7825},\n  doi      = {https://doi.org/10.1016/0045-7825(87)90098-3},\n  url      = {https://www.sciencedirect.com/science/article/pii/0045782587900983},\n  author   = {Rainald Löhner}\n}\n\n@article{Loubere2014,\n  title     = {A New Family of High Order Unstructured MOOD and ADER Finite\n               Volume Schemes for Multidimensional Systems of Hyperbolic\n               Conservation Laws},\n  volume    = 16,\n  DOI       = {10.4208/cicp.181113.140314a},\n  number    = 3,\n  journal   = {Communications in Computational Physics},\n  publisher = {Cambridge University Press},\n  author    = {Loubère, Raphaël and Dumbser, Michael and Diot, Steven},\n  year      = 2014,\n  pages     = {718–763}\n}\n\n@article{Lovelace2007tn,\n  author         = \"Lovelace, Geoffrey\",\n  title          = \"The dependence of test-mass thermal noises on beam shape\n                    in gravitational-wave interferometers\",\n  journal        = \"Class. Quant. Grav.\",\n  volume         = \"24\",\n  year           = \"2007\",\n  number         = \"17\",\n  pages          = \"4491-4512\",\n  doi            = \"10.1088/0264-9381/24/17/014\",\n  url            = \"https://doi.org/10.1088/0264-9381/24/17/014\",\n  eprint         = \"gr-qc/0610041\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = GR-QC/0610041;%%\"\n}\n\n@article{Lovelace2008tw,\n  author         = \"Lovelace, Geoffrey and Owen, Robert and Pfeiffer, Harald\n                    P. and Chu, Tony\",\n  title          = \"Binary-black-hole initial data with nearly-extremal spins\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D78\",\n  year           = \"2008\",\n  pages          = \"084017\",\n  doi            = \"10.1103/PhysRevD.78.084017\",\n  url            = \"https://doi.org/10.1103/PhysRevD.78.084017\",\n  eprint         = \"0805.4192\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = ARXIV:0805.4192;%%\"\n}\n\n@article{Lovelace2016uwp,\n      author         = \"Lovelace, Geoffrey and  Lousto, Carlos O. and\n                        Healy, James  and  Scheel, Mark A. and\n                        Garcia, Alyssa and O'Shaughnessy, Richard and\n                        Boyle, Michael  and Campanelli, Manuela and\n                        Hemberger, Daniel A. and Kidder, Lawrence E.\n                        and Pfeiffer, Harald P. and Szil\\'{a}gyi, B\\'{e}la\n                        and Teukolsky, Saul A. and Zlochower, Yosef\",\n      title          = \"{Modeling the source of GW150914 with targeted\n                        numerical-relativity simulations}\",\n      journal        = \"Class. Quant. Grav.\",\n      volume         = \"33\",\n      year           = \"2016\",\n      number         = \"24\",\n      pages          = \"244002\",\n      doi            = \"10.1088/0264-9381/33/24/244002\",\n      eprint         = \"1607.05377\",\n      archivePrefix  = \"arXiv\",\n      primaryClass   = \"gr-qc\",\n      SLACcitation   = \"%%CITATION = ARXIV:1607.05377;%%\",\n      url            = \"https://doi.org/10.1088/0264-9381/33/24/244002\"\n}\n\n@article{Lovelace2017xyf,\n  author        = \"Lovelace, Geoffrey and Demos, Nicholas and Khan, Haroon\",\n  title         = \"Numerically modeling {Brownian} thermal noise in amorphous\n                   and crystalline thin coatings\",\n  eprint        = \"1707.07774\",\n  archivePrefix = \"arXiv\",\n  primaryClass  = \"gr-qc\",\n  reportNumber  = \"LIGO-P1700183\",\n  doi           = \"10.1088/1361-6382/aa9ccc\",\n  journal       = \"Class. Quant. Grav.\",\n  volume        = \"35\",\n  number        = \"2\",\n  pages         = \"025017\",\n  year          = \"2018\",\n  url           = \"https://doi.org/10.1088/1361-6382/aa9ccc\"\n}\n\n@article{Lovelace2024wra,\n  author        = {Geoffrey Lovelace and Kyle C. Nelli and Nils Deppe and Nils\n                   L. Vu and William Throwe and Marceline S. Bonilla and\n                   Alexander Carpenter and Lawrence E. Kidder and Alexandra\n                   Macedo and Mark A. Scheel and Azer Afram and Michael Boyle\n                   and Andrea Ceja and Matthew Giesler and Sarah Habib and Ken\n                   Z. Jones and Prayush Kumar and Guillermo Lara and Denyz\n                   Melchor and Iago B. Mendes and Keefe Mitman and Marlo Morales\n                   and Jordan Moxon and Eamonn O'Shea and Kyle Pannone and\n                   Harald P. Pfeiffer and Teresita Ramirez-Aguilar and Jennifer\n                   Sanchez and Daniel Tellez and Saul A. Teukolsky and Nikolas\n                   A. Wittek},\n  title         = {Simulating binary black hole mergers using discontinuous\n                   {Galerkin} methods},\n  eprint        = {2410.00265},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  month         = {9},\n  year          = {2024}\n}\n\n@article{Ma2023sok,\n  author        = {Ma, Sizheng and Varma, Vijay and Stein, Leo C.\n                   and Foucart, Francois and Duez, Matthew D. and\n                   Kidder, Lawrence E. and Pfeiffer, Harald P. and\n                   Scheel, Mark A.},\n  title         = {{Numerical simulations of black hole-neutron star mergers\n                    in scalar-tensor gravity}},\n  eprint        = {2304.11836},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.107.124051},\n  journal       = {Phys. Rev. D},\n  volume        = {107},\n  number        = {12},\n  pages         = {124051},\n  year          = {2023},\n  url           = {https://doi.org/10.1103/PhysRevD.107.124051}\n}\n\n@article{Mathar2009,\n  author =       {Mathar, Richard J.},\n  title =        {Zernike basis to cartesian transformations},\n  journal =      {Serbian Astronomical Journal},\n  year =         2009,\n  url =          {http://dx.doi.org/10.2298/SAJ0979107M},\n  doi =          {10.2298/saj0979107m},\n  number =       179,\n  pages =        {107–120}\n}\n\n@article{Matsushima1995,\n  author = {T. Matsushima and P.S. Marcus},\n  title = {A Spectral Method for Polar Coordinates},\n  journal = {Journal of Computational Physics},\n  volume = 120,\n  number = 2,\n  pages = {365-374},\n  year = 1995,\n  issn = {0021-9991},\n  doi = {10.1006/jcph.1995.1171},\n  url = {https://doi.org/10.1006/jcph.1995.1171}\n}\n\n@Article{Michel1972,\n  author  = \"Michel, F. Curtis\",\n  title   = \"Accretion of matter by condensed objects\",\n  journal = \"Astrophysics and Space Science\",\n  year    = \"1972\",\n  month   = \"Jan\",\n  day     = \"01\",\n  volume  = \"15\",\n  number  = \"1\",\n  pages   = \"153--160\",\n  doi     = \"10.1007/BF00649949\",\n  url     = \"https://doi.org/10.1007/BF00649949\"\n}\n\n@article{Mignone2010br,\n      author         = \"Mignone, A. and Tzeferacos, P. and Bodo, G.\",\n      title          = \"{High-order conservative finite difference GLM-MHD\n                        schemes for cell-centered MHD}\",\n      journal        = \"J. Comput. Phys.\",\n      volume         = \"229\",\n      year           = \"2010\",\n      pages          = \"5896-5920\",\n      doi            = \"10.1016/j.jcp.2010.04.013\",\n      url            = \"https://doi.org/10.1016/j.jcp.2010.04.013\",\n      eprint         = \"1001.2832\",\n      archivePrefix  = \"arXiv\",\n      primaryClass   = \"astro-ph.HE\",\n      SLACcitation   = \"%%CITATION = ARXIV:1001.2832;%%\"\n}\n\n@article{Minerbo1978,\n  author   = \"Minerbo, Gerald N.\",\n  title    = \"Maximum entropy {Eddington} factors\",\n  journal  = \"Journal of Quantitative Spectroscopy and Radiative Transfer\",\n  volume   = \"20\",\n  number   = \"6\",\n  pages    = \"541 - 545\",\n  year     = \"1978\",\n  issn     = \"0022-4073\",\n  doi      = \"10.1016/0022-4073(78)90024-9\",\n  url      = \"https://doi.org/10.1016/0022-4073(78)90024-9\"\n}\n\n@book{Misner1973,\n  author    = {Misner, Charles W. and Thorne, Kip S.\n               and Wheeler, John Archibald},\n  publisher = {W. H. Freeman},\n  title     = {Gravitation},\n  year      = {1973}\n}\n\n@article{Mitman2024review,\n  author        = {Keefe Mitman and Michael Boyle and Leo C. Stein and\n                   Nils Deppe and Lawrence E. Kidder and Jordan Moxon and\n                   Harald P. Pfeiffer and Mark A. Scheel and Saul A. Teukolsky\n                   and William Throwe and Nils L. Vu},\n  title         = {A Review of Gravitational Memory and BMS Frame Fixing in\n                   Numerical Relativity},\n  eprint        = {2405.08868},\n  archivePrefix = {arXiv},\n  primaryClass  = {gr-qc},\n  url           = {https://doi.org/10.48550/arXiv.2405.08868},\n  year          = {2024}\n}\n\n@article{Moesta2013dna,\n  author =       {M\\\"osta, Philipp and Mundim, Bruno C. and Faber, Joshua A. and\n                  Haas, Roland and Noble, Scott C. and Bode, Tanja and\n                  L\\\"offler, Frank and Ott, Christian D. and Reisswig, Christian\n                  and Schnetter, Erik},\n  title =        \"{GRHydro: A new open source general-relativistic\n                  magnetohydrodynamics code for the Einstein Toolkit}\",\n  eprint =       \"1304.5544\",\n  archivePrefix =\"arXiv\",\n  primaryClass = \"gr-qc\",\n  doi =          \"10.1088/0264-9381/31/1/015005\",\n  journal =      \"Class.Quant.Grav.\",\n  volume =       \"31\",\n  pages =        \"015005\",\n  year =         \"2014\"\n}\n\n@article{Moesta2014,\n  author    = {Philipp Mösta and Bruno C Mundim and Joshua A Faber\n               and Roland Haas and Scott C Noble and Tanja Bode\n               and Frank Löffler and Christian D Ott and Christian Reisswig\n               and Erik Schnetter},\n  doi       = {10.1088/0264-9381/31/1/015005},\n  journal   = {Classical and Quantum Gravity},\n  month     = {nov},\n  pages     = {015005},\n  publisher = {IOP Publishing},\n  title     = {GRHydro: a new open-source general-relativistic\n               magnetohydrodynamics code for the Einstein toolkit},\n  url       = {https://dx.doi.org/10.1088/0264-9381/31/1/015005},\n  volume    = {31},\n  year      = {2013}\n}\n\n@article{Moldenhauer2014yaa,\n  author        = {Moldenhauer, Niclas and Markakis, Charalampos M. and\n                   Johnson-McDaniel, Nathan K. and Tichy, Wolfgang and\n                   Br\\\"ugmann, Bernd},\n  title         = {Initial data for binary neutron stars with adjustable\n                   eccentricity},\n  eprint        = {1408.4136},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.90.084043},\n  journal       = {Phys. Rev. D},\n  volume        = {90},\n  number        = {8},\n  pages         = {084043},\n  year          = {2014}\n}\n\n@article{Most2022,\n  author  = {Most, Elias R and Philippov, Alexander A},\n  doi     = {10.1093/mnras/stac1909},\n  issn    = {0035-8711},\n  journal = {Monthly Notices of the Royal Astronomical Society},\n  month   = {07},\n  number  = {2},\n  pages   = {2710-2724},\n  title   = {{Electromagnetic precursor flares from the late inspiral of\n             neutron star binaries}},\n  url     = {https://doi.org/10.1093/mnras/stac1909},\n  volume  = {515},\n  year    = {2022}\n}\n\n@article{Moxon2020gha,\n    author           = \"Moxon, Jordan and Scheel, Mark A. and\n                        Teukolsky, Saul A.\",\n    title            = \"{Improved Cauchy-characteristic evolution system for\n                        high-precision numerical relativity waveforms}\",\n    journal          = \"Phys. Rev. D\",\n    volume           = \"102\",\n    number           = \"4\",\n    pages            = \"044052\",\n    year             = \"2020\",\n    doi              = \"10.1103/PhysRevD.102.044052\",\n    url              =\"http://dx.doi.org/10.1103/PhysRevD.102.044052\",\n    eprint           = \"2007.01339\",\n    archivePrefix    = \"arXiv\",\n    primaryClass     = \"gr-qc\"\n}\n\n@article{Moxon2021gbv,\n  author        = {Moxon, Jordan and Scheel, Mark A. and Teukolsky, Saul A. and\n                   Deppe, Nils and Fischer, Nils and H\\'ebert, Francois and\n                   Kidder, Lawrence E. and Throwe, William},\n  title         = {{SpECTRE Cauchy-characteristic evolution system for rapid,\n                   precise waveform extraction}},\n  eprint        = {2110.08635},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.107.064013},\n  journal       = {Phys. Rev. D},\n  volume        = {107},\n  number        = {6},\n  pages         = {064013},\n  year          = {2023}\n}\n\n@article{Muhlberger2014pja,\n  author =       \"Muhlberger, Curran D. and Nouri, Fatemeh Hossein and Duez,\n                  Matthew D. and Foucart, Francois and Kidder, Lawrence E. and\n                  Ott, Christian D. and Scheel, Mark A. and Szil\\'agyi, B\\'ela\n                  and Teukolsky, Saul A.\",\n  title =        \"{Magnetic effects on the low-T/|W| instability in\n                  differentially rotating neutron stars}\",\n  eprint =       \"1405.2144\",\n  archivePrefix =\"arXiv\",\n  primaryClass = \"astro-ph.HE\",\n  doi =          \"10.1103/PhysRevD.90.104014\",\n  url = {https://journals.aps.org/prd/abstract/10.1103/PhysRevD.90.104014},\n  journal =      \"Phys. Rev. D\",\n  volume =       90,\n  number =       10,\n  pages =        104014,\n  year =         2014\n}\n\n@article{Nee2024bur,\n  author        = {Nee, Peter James and Lara, Guillermo and Pfeiffer, Harald\n                    P. and Vu, Nils L.},\n  title         = {{Quasistationary hair for binary black hole initial data\n                    in scalar Gauss-Bonnet gravity}},\n  eprint        = {2406.08410},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  month         = {6},\n  year          = {2024}\n}\n\n@article{Nonomura2013,\n  author   = {Taku Nonomura and Kozo Fujii},\n  doi      = {https://doi.org/10.1016/j.compfluid.2012.09.001},\n  issn     = {0045-7930},\n  journal  = {Computers & Fluids},\n  keywords = {Weighted compact nonlinear scheme, Robustness},\n  note     = {International Workshop on Future of CFD and Aerospace Sciences},\n  pages    = {8-18},\n  title    = {Robust explicit formulation of weighted compact nonlinear scheme},\n  url      = {https://www.sciencedirect.com/science/article/pii/S0045793012003441},\n  volume   = {85},\n  year     = {2013}\n}\n\n@book{NumericalRecipes,\n  author    = \"Press, William~H. and Teukolsky, Saul~A. and\n               Vetterling, William~T. and Flannery, Brian~P.\",\n  title     = \"Numerical Recipes 3rd Edition: The Art of Scientific Computing\",\n  publisher = \"Cambridge University Press\",\n  year      = \"2007\",\n  month     = \"sep\",\n  url       = \"{http://numerical.recipes}\",\n}\n\n@article{OBoyle2020,\n  doi = {10.1103/physrevd.102.083027},\n  url = {https://doi.org/10.1103%2Fphysrevd.102.083027},\n  year = 2020,\n  month = {oct},\n  publisher = {American Physical Society ({APS})},\n  volume = {102},\n  number = {8},\n  author = {Michael F. O'Boyle and Charalampos Markakis and Nikolaos\n    Stergioulas and Jocelyn S. Read},\n  title = {Parametrized equation of state for neutron star matter with\n    continuous sound speed},\n  journal = {Physical Review D}\n}\n\n@ARTICLE{Oconnor2015,\n       author = {{O'Connor}, Evan},\n        title = \"{An Open-source Neutrino Radiation Hydrodynamics Code for\n         Core-collapse Supernovae}\",\n      journal = {Astrophysical Journal, Supplement},\n     keywords = {black hole physics, hydrodynamics, neutrinos, radiative\n      transfer, stars: neutron, supernovae: general, Astrophysics - High\n      Energy Astrophysical Phenomena, Nuclear Theory},\n         year = 2015,\n        month = aug,\n       volume = {219},\n       number = {2},\n          eid = {24},\n        pages = {24},\n          doi = {10.1088/0067-0049/219/2/24},\narchivePrefix = {arXiv},\n       eprint = {1411.7058},\n primaryClass = {astro-ph.HE},\n       adsurl = {https://ui.adsabs.harvard.edu/abs/2015ApJS..219...24O}\n}\n\n@article{Okounkova:2019zjf,\n    author = \"Okounkova, Maria and Stein, Leo C. and Moxon, Jordan and Scheel,\n     Mark A. and Teukolsky, Saul A.\",\n    title = \"{Numerical relativity simulation of GW150914 beyond general\n     relativity}\",\n    eprint = \"1911.02588\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1103/PhysRevD.101.104016\",\n    journal = \"Phys. Rev. D\",\n    volume = \"101\",\n    number = \"10\",\n    pages = \"104016\",\n    year = \"2020\"\n}\n\n@article{Osburn:2022bby,\n  author        = {Osburn, Thomas and Nishimura, Nami},\n  title         = {New self-force method via elliptic partial differential\n                   equations for {Kerr} inspiral models},\n  eprint        = {2206.07031},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.106.044056},\n  journal       = {Phys. Rev. D},\n  volume        = {106},\n  number        = {4},\n  pages         = {044056},\n  year          = {2022}\n}\n\n@article{Ossokine2013zga,\n  author        = {Ossokine, Serguei and Kidder, Lawrence E. and Pfeiffer,\n                   Harald P.},\n  title         = {Precession-tracking coordinates for simulations of compact-\n                   object-binaries},\n  eprint        = {1304.3067},\n  archivePrefix = {arXiv},\n  primaryClass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.88.084031},\n  journal       = {Phys. Rev. D},\n  volume        = {88},\n  pages         = {084031},\n  year          = {2013}\n}\n\n@article{Ossokine2015yla,\n  author        = {Ossokine, Serguei and Foucart, Francois and Pfeiffer, Harald\n                   P. and Boyle, Michael and Szil\\'agyi, B\\'ela},\n  title         = {Improvements to the construction of binary black hole initial\n                   data},\n  eprint        = {1506.01689},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1088/0264-9381/32/24/245010},\n  journal       = {Class. Quant. Grav.},\n  volume        = {32},\n  pages         = {245010},\n  year          = {2015}\n}\n\n@article{Owen2009sb,\n  author         = \"Owen, Robert\",\n  title          = \"The Final Remnant of Binary Black Hole Mergers:\n                    Multipolar Analysis\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D80\",\n  year           = \"2009\",\n  pages          = \"084012\",\n  doi            = \"10.1103/PhysRevD.80.084012\",\n  url            = \"https://doi.org/10.1103/PhysRevD.80.084012\",\n  eprint         = \"0907.0280\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = ARXIV:0907.0280;%%\"\n}\n\n@article{Owen2017yaj,\n  author         = \"Owen, Robert and Fox, Alex S. and Freiberg, John A. and\n                    Jacques, Terrence Pierre\",\n  title          = \"Black Hole Spin Axis in Numerical Relativity\",\n  year           = \"2017\",\n  eprint         = \"1708.07325\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = ARXIV:1708.07325;%%\"\n}\n\n@article{Owren1992,\n  author =       {Owren, Brynjulf and Zennaro, Marino},\n  title =        {Derivation of Efficient, Continuous, Explicit Runge–Kutta\n                  Methods},\n  journal =      {SIAM Journal on Scientific and Statistical Computing},\n  volume =       {13},\n  number =       {6},\n  pages =        {1488-1501},\n  year =         {1992},\n  doi =          {10.1137/0913084},\n  URL =          {https://epubs.siam.org/doi/10.1137/0913084}\n}\n\n@article{Pajkos2019,\n  doi = {10.3847/1538-4357/ab1de2},\n  url = {https://doi.org/10.3847%2F1538-4357%2Fab1de2},\n  year = 2019,\n  month = {jun},\n  publisher = {American Astronomical Society},\n  volume = {878},\n  number = {1},\n  pages = {13},\n  author = {Michael A. Pajkos and Sean M. Couch and Kuo-Chuan Pan\n   and Evan P. O'Connor},\n  title = {Features of Accretion-phase Gravitational-wave Emission\n   from Two-dimensional Rotating Core-collapse Supernovae},\n  journal = {The Astrophysical Journal}\n}\n\n@article{Palenzuela2010,\n  author    = {Palenzuela, Carlos and Garrett, Travis and Lehner,\n               Luis and Liebling, Steven L.},\n  doi       = {10.1103/PhysRevD.82.044045},\n  issue     = {4},\n  journal   = {Phys. Rev. D},\n  month     = {Aug},\n  numpages  = {14},\n  pages     = {044045},\n  publisher = {American Physical Society},\n  title     = {Magnetospheres of black hole systems in force-free plasma},\n  url       = {https://link.aps.org/doi/10.1103/PhysRevD.82.044045},\n  volume    = {82},\n  year      = {2010}\n}\n\n@article{Palenzuela2018,\n   title = \"A Simflowny-based finite-difference code for high-performance\n           computing in numerical relativity\",\n   volume = \"35\",\n   ISSN = \"1361-6382\",\n   url = \"http://dx.doi.org/10.1088/1361-6382/aad7f6\",\n   DOI = \"10.1088/1361-6382/aad7f6\",\n   number = \"18\",\n   journal = \"Classical and Quantum Gravity\",\n   publisher = \"IOP Publishing\",\n   author = \"Palenzuela, Carlos and Miñano, Borja and Viganò, Daniele and\n            Arbona, Antoni and Bona-Casas, Carles and Rigo, Andreu and\n            Bezares, Miguel and Bona, Carles and Massó, Joan\",\n   year = \"2018\",\n   month = \"aug\",\n   pages=\"185007\"\n}\n\n@article{Pareschi2005,\n  author        = {Pareschi, Lorenzo and Russo, Giovanni},\n  title         = {Implicit-Explicit {Runge-Kutta} Schemes and Applications to\n                   Hyperbolic Systems with Relaxation},\n  journal       = {J Sci Comput},\n  volume        = {25},\n  year          = {2005},\n  pages         = {129--155},\n  doi           = {10.1007/s10915-004-4636-4},\n  url           = {https://doi.org/10.1007/s10915-004-4636-4},\n  archivePrefix = {arXiv},\n  eprint        = {1009.2757},\n  primaryClass  = {math.NA},\n}\n\n@article{Paschalidis2013,\n  author  = {Paschalidis, Vasileios and Shapiro, Stuart L.},\n  doi     = {10.1103/PhysRevD.88.104031},\n  journal = {Physical Review D},\n  month   = nov,\n  note    = {Publisher: American Physical Society},\n  number  = {10},\n  pages   = {104031},\n  title   = {A new scheme for matching general relativistic ideal\n             magnetohydrodynamics to its force-free limit},\n  url     = {https://link.aps.org/doi/10.1103/PhysRevD.88.104031},\n  urldate = {2022-09-11},\n  volume  = {88},\n  year    = {2013}\n}\n\n@article{Penner2011,\n  author =       {{Penner}, A.~J.},\n  title =        \"{General relativistic magnetohydrodynamic Bondi-Hoyle\n                  accretion}\",\n  journal =      {Monthly Notices of the Royal Astronomical Society},\n  year =         2011,\n  month =        \"Jun\",\n  volume =       414,\n  number =       2,\n  pages =        {1467-1482},\n  doi =          {10.1111/j.1365-2966.2011.18480.x},\n  archivePrefix ={arXiv},\n  eprint =       {1011.2976},\n  primaryClass = {astro-ph.HE},\n  adsurl =       {https://ui.adsabs.harvard.edu/abs/2011MNRAS.414.1467P},\n}\n\n@inproceedings{Persson2006sub,\n  title     = {Sub-cell shock capturing for discontinuous Galerkin methods},\n  author    = {Persson, Per-Olof and Peraire, Jaime},\n  booktitle = {44th AIAA Aerospace Sciences Meeting and Exhibit},\n  pages     = 112,\n  year      = 2006,\n  doi       = {10.2514/6.2006-112}\n}\n\n@phdthesis{Pfeiffer2005zm,\n  author        = \"Pfeiffer, Harald Paul\",\n  title         = \"Initial data for black hole evolutions\",\n  eprint        = \"gr-qc/0510016\",\n  archivePrefix = \"arXiv\",\n  reportnumber  = \"UMI-31-04429\",\n  school        = \"Cornell U.\",\n  year          = \"2005\"\n}\n\n@article{Porth2016rfi,\n  author         = \"Porth, Oliver and Olivares, Hector and Mizuno, Yosuke and\n                    Younsi, Ziri and Rezzolla, Luciano and Moscibrodzka,\n                    Monika and Falcke, Heino and Kramer, Michael\",\n  title          = \"The Black Hole Accretion Code\",\n  url            = \"https://doi.org/10.1186/s40668-017-0020-2\",\n  year           = \"2016\",\n  eprint         = \"1611.09720\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = ARXIV:1611.09720;%%\"\n}\n\n@ARTICLE{radice2022,\n       author = {{Radice}, David and {Bernuzzi}, Sebastiano and {Perego},\n       Albino and {Haas}, Roland},\n        title = \"{A new moment-based general-relativistic neutrino-radiation\n        transport code: Methods and first applications to neutron star\n        mergers}\",\n      journal = {Monthly Notices of the Royal Astronomical Society},\n     keywords = {neutrinos, methods: numerical, neutron star mergers,\n     Astrophysics - High Energy Astrophysical Phenomena, General Relativity\n     and Quantum Cosmology},\n         year = 2022,\n        month = may,\n       volume = {512},\n       number = {1},\n        pages = {1499-1521},\n          doi = {10.1093/mnras/stac589},\narchivePrefix = {arXiv},\n       eprint = {2111.14858},\n primaryClass = {astro-ph.HE},\n       adsurl = {https://ui.adsabs.harvard.edu/abs/2022MNRAS.512.1499R},\n      adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n}\n\n@article{Renkhoff2023,\n  author        = {Renkhoff, Sarah and Cors, Daniela and Hilditch, David and\n                   Br\\\"ugmann, Bernd},\n  title         = {Adaptive {hp} refinement for spectral elements in numerical\n                   relativity},\n  eprint        = {2302.00575},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.107.104043},\n  journal       = {Phys. Rev. D},\n  volume        = {107},\n  number        = {10},\n  pages         = {104043},\n  year          = {2023}\n}\n\n@book{RezzollaBook,\n  author    = \"{{Rezzolla}, L. and {Zanotti}, O.}\",\n  title     = \"{Relativistic Hydrodynamics}\",\n  publisher = \"Oxford University Press\",\n  year      = \"2013\",\n  month     = \"sep\",\n  url       = \"{http://adsabs.harvard.edu/abs/2013rehy.book.....R}\",\n}\n\n@ARTICLE{Richers2017,\n       author = {{Richers}, Sherwood and {Ott}, Christian D. and\n        {Abdikamalov}, Ernazar and {O'Connor}, Evan and {Sullivan}, Chris},\n        title = \"{Equation of state effects on gravitational waves from\n         rotating core collapse}\",\n      journal = {Phys. Rev. D},\n     keywords = {Astrophysics - High Energy Astrophysical Phenomena},\n         year = 2017,\n        month = mar,\n       volume = {95},\n       number = {6},\n          eid = {063019},\n        pages = {063019},\n          doi = {10.1103/PhysRevD.95.063019},\narchivePrefix = {arXiv},\n       eprint = {1701.02752},\n primaryClass = {astro-ph.HE},\n       adsurl = {https://ui.adsabs.harvard.edu/abs/2017PhRvD..95f3019R},\n      adsnote = {Provided by the SAO/NASA Astrophysics Data System}\n}\n\n@article{Rinne2007ui,\n    author = \"Rinne, Oliver and Lindblom, Lee and Scheel, Mark A.\",\n    title = \"{Testing outer boundary treatments for the Einstein equations}\",\n    eprint = \"0704.0782\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1088/0264-9381/24/16/006\",\n    journal = \"Class. Quant. Grav.\",\n    volume = \"24\",\n    pages = \"4053--4078\",\n    year = \"2007\"\n}\n\n@book{Saad2003,\n  title     = \"Iterative Methods for Sparse Linear Systems: Second Edition\",\n  author    = \"Saad, Yousef\",\n  isbn      = \"978-0898715347\",\n  publisher = \"Society for Industrial and Applied Mathematics\",\n  year      = \"2003\",\n}\n\n@article{Schaal2015,\n  author   = \"Schaal, K. and Bauer, A. and Chandrashekar, P. and Pakmor, R. and\n              Klingenberg, C. and Springel, V.\",\n  title    = \"Astrophysical hydrodynamics with a high-order discontinuous\n              Galerkin scheme and adaptive mesh refinement\",\n  year     = \"2015\",\n  url      = \"https://academic.oup.com/mnras/article/453/4/4278/2593686\",\n  doi      = \"10.1093/mnras/stv1859\",\n  journal  = \"MNRAS\",\n  pages    = \"4278--4300\",\n  volume   = \"453\",\n}\n\n@article{Scheel2003vs,\n    author = \"Scheel, Mark A. and Erickcek, Adrienne L. and Burko, Lior M. and\n              Kidder, Lawrence E. and Pfeiffer, Harald P.\n              and Teukolsky, Saul A.\",\n    title = \"{3-D simulations of linearized scalar fields in Kerr space-time}\",\n    eprint = \"gr-qc/0305027\",\n    archivePrefix = \"arXiv\",\n    doi = \"10.1103/PhysRevD.69.104006\",\n    journal = \"Phys. Rev. D\",\n    volume = \"69\",\n    pages = \"104006\",\n    year = \"2004\",\n    url = {https://journals.aps.org/prd/abstract/10.1103/PhysRevD.69.104006}\n}\n\n@book{Shapiro1983,\n  author = \"Shapiro, Stuart L. and Teukolsky, Saul A.\",\n  title  = \"Black holes, white dwarfs, and neutron stars:\n            the physics of compact objects\",\n  year   = \"1983\",\n  url     = {https://ui.adsabs.harvard.edu/abs/1983bhwd.book.....S},\n}\n\n@article{Shibata2003,\n  title     = \"Gravitational waves from axisymmetrically oscillating neutron\n               stars in general relativistic simulations\",\n  author    = \"Shibata, Masaru and Sekiguchi, Yu-ichirou\",\n  journal   = \"Phys. Rev. D\",\n  volume    = \"68\",\n  issue     = \"10\",\n  pages     = \"104020\",\n  numpages  = \"14\",\n  year      = \"2003\",\n  month     = \"Nov\",\n  publisher = \"American Physical Society\",\n  doi       = \"10.1103/PhysRevD.68.104020\",\n  url       = \"https://link.aps.org/doi/10.1103/PhysRevD.68.104020\"\n}\n\n@article{Shiokawa2011ih,\n  author = \"Shiokawa, Hotaka and Dolence, Joshua C. and Gammie, Charles F.\n            and Noble, Scott C.\",\n  title = \"{Global GRMHD Simulations of Black Hole Accretion Flows:\n            a Convergence Study}\",\n  eprint = \"1111.0396\",\n  archivePrefix = \"arXiv\",\n  primaryClass = \"astro-ph.HE\",\n  doi = \"10.1088/0004-637X/744/2/187\",\n  journal = \"Astrophys. J.\",\n  volume = \"744\",\n  pages = \"187\",\n  year = \"2012\"\n}\n\n@article{Shu1988439,\n  title =        \"Efficient implementation of essentially non-oscillatory\n                  shock-capturing schemes\",\n  journal =      \"Journal of Computational Physics\",\n  volume =       77,\n  number =       2,\n  pages =        \"439 - 471\",\n  year =         1988,\n  issn =         \"0021-9991\",\n  doi =          \"10.1016/0021-9991(88)90177-5\",\n  author =       \"Chi-Wang Shu and Stanley Osher\",\n}\n\n@article{Sod19781,\n  title =   {A survey of several finite difference methods for systems of\n             nonlinear hyperbolic conservation laws},\n  journal = {Journal of Computational Physics},\n  volume =  27,\n  number =  1,\n  pages =   {1-31},\n  year =    1978,\n  issn =    {0021-9991},\n  doi =     {https://doi.org/10.1016/0021-9991(78)90023-2},\n  url =     {https://www.sciencedirect.com/science/article/pii/0021999178900232},\n  author =  {Gary A Sod},\n}\n\n@book{Sommerfeld1949,\n  author =    {Sommerfeld, Arnold},\n  title =     {Partial Differential Equations in Physics},\n  publisher = {Academic Press},\n  year =      1949,\n  isbn =      {978-0-12-654658-3},\n  doi =       {10.1016/B978-0-12-654658-3.50001-X},\n  url = {https://www.sciencedirect.com/science/article/pii/B978012654658350001X}\n}\n\n@InProceedings{Sonntag2014,\n  author    = \"Sonntag, Matthias and Munz, Claus-Dieter\",\n  editor    = \"Fuhrmann, J{\\\"u}rgen and Ohlberger, Mario and Rohde,\n               Christian\",\n  title     = \"Shock Capturing for Discontinuous Galerkin Methods using\n               Finite Volume Subcells\",\n  booktitle = \"Finite Volumes for Complex Applications VII-Elliptic,\n               Parabolic and Hyperbolic Problems\",\n  year      = 2014,\n  publisher = \"Springer International Publishing\",\n  pages     = \"945--953\",\n  isbn      = \"978-3-319-05591-6\",\n  doi       = {10.1007/978-3-319-05591-6_96},\n  url       = {https://link.springer.com/chapter/10.1007%2F978-3-319-05591-6_96}\n}\n\n@article{Spitkovsky2006,\n  author     = {Anatoly Spitkovsky},\n  bdsk-url-1 = {https://doi.org/10.1086/507518},\n  doi        = {10.1086/507518},\n  journal    = {The Astrophysical Journal},\n  month      = {aug},\n  number     = {1},\n  pages      = {L51--L54},\n  publisher  = {American Astronomical Society},\n  title      = {Time-dependent Force-free Pulsar Magnetospheres:\n                Axisymmetric and Oblique Rotators},\n  url        = {https://doi.org/10.1086/507518},\n  volume     = {648},\n  year       = 2006\n}\n\n@article{Stamm2010,\n  author   = \"Stamm, Benjamin and Wihler, Thomas P.\",\n  title    = \"{hp-Optimal} discontinuous {Galerkin} methods for linear elliptic\n              problems\",\n  year     = \"2010\",\n  url      = \"https://doi.org/10.1090/S0025-5718-10-02335-5\",\n  doi      = \"10.1090/S0025-5718-10-02335-5\",\n  journal  = \"Mathematics of Computation\",\n  pages    = \"2117--2133\",\n  volume   = \"79\",\n}\n\n@article{Stiller2016a,\n  author        = \"Stiller, Jörg\",\n  title         = \"Robust multigrid for high-order discontinuous {Galerkin}\n                   methods: A fast {Poisson} solver suitable for high-aspect\n                   ratio {Cartesian} grids\",\n  volume        = \"327\",\n  issn          = \"0021-9991\",\n  url           = \"https://doi.org/10.1016/j.jcp.2016.09.041\",\n  doi           = \"10.1016/j.jcp.2016.09.041\",\n  journal       = \"Journal of Computational Physics\",\n  publisher     = \"Elsevier BV\",\n  year          = \"2016\",\n  eprint        = \"1603.02524\",\n  archivePrefix = \"arXiv\",\n  primaryClass  = \"cs.CE\"\n}\n\n@article{Stiller2016b,\n  author        = \"Stiller, Jörg\",\n  title         = \"Robust Multigrid for {Cartesian} Interior Penalty {DG}\n                   Formulations of the {Poisson} Equation in 3D\",\n  year          = \"2016\",\n  eprint        = \"1612.04796\",\n  archivePrefix = \"arXiv\",\n  primaryClass  = \"cs.NA\"\n}\n\n@article{Suresh1997,\n  author  = {A. Suresh and H.T. Huynh},\n  doi     = {https://doi.org/10.1006/jcph.1997.5745},\n  issn    = {0021-9991},\n  journal = {Journal of Computational Physics},\n  number  = {1},\n  pages   = {83-99},\n  title   = {Accurate Monotonicity-Preserving Schemes with Runge–Kutta Time\n  Stepping},\n  volume  = {136},\n  year    = {1997}\n}\n\n@article{Szilagyi2009qz,\n  author         = \"Szilagyi, Bela and Lindblom, Lee and Scheel, Mark A.\",\n  title          = \"{Simulations of Binary Black Hole Mergers Using Spectral\n                    Methods}\",\n  journal        = \"Phys. Rev.\",\n  volume         = \"D80\",\n  year           = \"2009\",\n  pages          = \"124010\",\n  doi            = \"10.1103/PhysRevD.80.124010\",\n  url            = \"https://doi.org/10.1103/PhysRevD.80.124010\",\n  eprint         = \"0909.3557\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = ARXIV:0909.3557;%%\"\n}\n\n@article{Szilagyi2014fna,\n    author = \"Szil\\'agyi, B\\'ela\",\n    title = \"{Key Elements of Robustness in Binary Black Hole Evolutions\n              using Spectral Methods}\",\n    eprint = \"1405.3693\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1142/S0218271814300146\",\n    journal = \"Int. J. Mod. Phys. D\",\n    volume = \"23\",\n    number = \"7\",\n    pages = \"1430014\",\n    year = \"2014\"\n}\n\n@article{Tacik2016zal,\n  author        = {Tacik, Nick and Foucart, Francois and Pfeiffer, Harald P.\n                   and Muhlberger, Curran and Kidder, Lawrence E. and Scheel,\n                   Mark A. and Szil\\'agyi, B.},\n  title         = {Initial data for black hole-neutron star binaries, with\n                   rotating stars},\n  eprint        = {1607.07962},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1088/0264-9381/33/22/225012},\n  journal       = {Class. Quant. Grav.},\n  volume        = {33},\n  number        = {22},\n  pages         = {225012},\n  year          = {2016}\n}\n\n@article{Teukolsky1982nz,\n    author = \"Teukolsky, S. A.\",\n    title = \"{Linearized quadrupole waves in general relativity and the motion\n              of test particles}\",\n    doi = \"10.1103/PhysRevD.26.745\",\n    url = \"https://doi.org/10.1103/PhysRevD.26.745\",\n    journal = \"Phys.~Rev.~D\",\n    volume = \"26\",\n    pages = \"745--750\",\n    year = \"1982\"\n}\n\n@article{Teukolsky2015ega,\n  author         = \"Teukolsky, Saul A.\",\n  title          = \"Formulation of discontinuous {Galerkin} methods for\n                    relativistic astrophysics\",\n  journal        = \"J. Comput. Phys.\",\n  volume         = \"312\",\n  year           = \"2016\",\n  pages          = \"333-356\",\n  doi            = \"10.1016/j.jcp.2016.02.031\",\n  url            = \"https://doi.org/10.1016/j.jcp.2016.02.031\",\n  eprint         = \"1510.01190\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"gr-qc\",\n  SLACcitation   = \"%%CITATION = ARXIV:1510.01190;%%\"\n}\n\n@article{Thorne1980,\n  title = {Multipole expansions of gravitational radiation},\n  author = {Thorne, Kip S.},\n  journal = {Rev. Mod. Phys.},\n  volume = {52},\n  issue = {2},\n  pages = {299--339},\n  numpages = {0},\n  year = {1980},\n  month = {Apr},\n  publisher = {American Physical Society},\n  doi = {10.1103/RevModPhys.52.299},\n  url = {https://link.aps.org/doi/10.1103/RevModPhys.52.299}\n}\n\n@book{ThorneBlandford2017,\n  author     = \"Thorne, Kip S. and Blandford, Roger D.\",\n  title      = \"Modern Classical Physics\",\n  year       = \"2017\",\n  url        = \"https://press.princeton.edu/titles/10157.html\",\n  publisher  = \"Princeton University Press\",\n}\n\n@article{Toro1994,\n  author         = \"Toro, E. F. and Spruce, M. and Speares, W.\",\n  title          = \"{Restoration of the Contact Surface in the HLL–Riemann\n                     Solver}\",\n  journal        = \"Shock Waves\",\n  volume         = \"4\",\n  year           = \"1994\",\n  number         = \"1\",\n  pages          = \"25--34\",\n  doi            = \"10.1007/BF01414629\",\n  url            = \"https://doi.org/10.1007/BF01414629\"\n}\n\n@book{Toro2009,\n  author     = \"Toro, E. F.\",\n  title      = \"Riemann Solvers and Numerical Methods for Fluid Dynamics\",\n  year       = \"2009\",\n  url        = \"https://www.springer.com/us/book/9783540252023\",\n  publisher  = \"Springer-Verlag Berlin Heidelberg\",\n}\n\n@article{Tsitouras2011,\n  author  = \"Tsitouras, Ch.\",\n  title   = \"Runge-Kutta pairs of order 5(4) satisfying only the first column\n             simplifying assumption\",\n  doi     = \"10.1016/j.camwa.2011.06.002\",\n  journal = \"Computers & Mathematics with Applications\",\n  volume  = \"62\",\n  number  = \"2\",\n  pages   = \"770--775\",\n  year    = \"2011\",\n}\n\n@article{Varma2018sqd,\n  author        = \"Varma, Vijay and Scheel, Mark A. and Pfeiffer, Harald P.\",\n  title         = \"Comparison of binary black hole initial data sets\",\n  eprint        = \"1808.08228\",\n  archivePrefix = \"arXiv\",\n  primaryClass  = \"gr-qc\",\n  doi           = \"10.1103/PhysRevD.98.104011\",\n  journal       = \"Phys. Rev. D\",\n  volume        = \"98\",\n  number        = \"10\",\n  pages         = \"104011\",\n  year          = \"2019\"\n}\n\n@article{Vincent2019qpd,\n  author        = \"Vincent, Trevor and Pfeiffer, Harald P. and Fischer, Nils\n                   L.\",\n  title         = \"{hp-adaptive} discontinuous {Galerkin} solver for elliptic\n                   equations in numerical relativity\",\n  eprint        = \"1907.01572\",\n  archivePrefix = \"arXiv\",\n  primaryClass  = \"physics.comp-ph\",\n  doi           = \"10.1103/PhysRevD.100.084052\",\n  url           = \"https://journals.aps.org/prd/abstract/10.1103/PhysRevD.100.084052\",\n  journal       = \"Phys. Rev. D\",\n  volume        = \"100\",\n  number        = \"8\",\n  pages         = \"084052\",\n  year          = \"2019\"\n}\n\n@article{Vu2021coj,\n  author        = {Vu, Nils L. and others},\n  title         = {A scalable elliptic solver with task-based parallelism for\n                   the {SpECTRE} numerical relativity code},\n  eprint        = {2111.06767},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.105.084027},\n  journal       = {Phys. Rev. D},\n  volume        = {105},\n  number        = {8},\n  pages         = {084027},\n  year          = {2022},\n  month         = {apr}\n}\n\n@article{Vu2023thn,\n  author        = {Nils L Vu and Samuel Rodriguez and Tom Włodarczyk and\n                   Geoffrey Lovelace and Harald P Pfeiffer and Gabriel S Bonilla\n                   and Nils Deppe and François Hébert and Lawrence E Kidder and\n                   Jordan Moxon and William Throwe},\n  title         = {High-accuracy numerical models of Brownian thermal noise in\n                   thin mirror coatings},\n  eprint        = {2111.06893},\n  archiveprefix = {arXiv},\n  primaryclass  = {astro-ph.IM},\n  journal       = {Classical and Quantum Gravity},\n  doi           = {10.1088/1361-6382/acad62},\n  year          = {2023},\n  month         = {jan},\n  volume        = {40},\n  number        = {2},\n  pages         = {025015}\n}\n\n@article{Vu2024cgf,\n  author        = {Vu, Nils L.},\n  title         = {A discontinuous {Galerkin} scheme for elliptic equations on\n                   extremely stretched grids},\n  eprint        = {2405.06120},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  month         = {5},\n  year          = {2024}\n}\n\n@article{Wald1974,\n  author    = {Wald, Robert M.},\n  doi       = {10.1103/PhysRevD.10.1680},\n  issue     = {6},\n  journal   = {Phys. Rev. D},\n  month     = {Sep},\n  numpages  = {0},\n  pages     = {1680--1685},\n  publisher = {American Physical Society},\n  title     = {Black hole in a uniform magnetic field},\n  url       = {https://link.aps.org/doi/10.1103/PhysRevD.10.1680},\n  volume    = {10},\n  year      = {1974}\n}\n\n@article{Wardell:2011gb,\n  author        = {Wardell, Barry and Vega, Ian and Thornburg, Jonathan and\n                   Diener, Peter},\n  title         = {A Generic effective source for scalar self-force\n                   calculations},\n  eprint        = {1112.6355},\n  archiveprefix = {arXiv},\n  primaryclass  = {gr-qc},\n  doi           = {10.1103/PhysRevD.85.104044},\n  journal       = {Phys. Rev. D},\n  volume        = {85},\n  pages         = {104044},\n  year          = {2012}\n}\n\n@article{White2015omx,\n  author         = \"White, Christopher J. and Stone, James M. and Gammie,\n                    Charles F.\",\n  title          = \"{An Extension of the Athena++ Code Framework for GRMHD\n                    Based on Advanced Riemann Solvers and Staggered-Mesh\n                    Constrained Transport}\",\n  journal        = \"Astrophys. J. Suppl.\",\n  volume         = \"225\",\n  year           = \"2016\",\n  number         = \"2\",\n  pages          = \"22\",\n  doi            = \"10.3847/0067-0049/225/2/22\",\n  url            = \"https://doi.org/10.3847/0067-0049/225/2/22\",\n  eprint         = \"1511.00943\",\n  archivePrefix  = \"arXiv\",\n  primaryClass   = \"astro-ph.HE\",\n  SLACcitation   = \"%%CITATION = ARXIV:1511.00943;%%\"\n}\n\n@article{Wittek:2023nyi,\n    author = \"Wittek, Nikolas A. and others\",\n    title = \"{Worldtube excision method for intermediate-mass-ratio\n              inspirals: Scalar-field model in 3+1 dimensions}\",\n    eprint = \"2304.05329\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    doi = \"10.1103/PhysRevD.108.024041\",\n    journal = \"Phys. Rev. D\",\n    volume = \"108\",\n    number = \"2\",\n    pages = \"024041\",\n    year = \"2023\"\n}\n\n@article{Wittek:2024gxn,\n    author = \"Wittek, Nikolas A. and Pound, Adam and Pfeiffer, Harald P.\n              and Barack, Leor\",\n    title = \"{Worldtube excision method for intermediate-mass-ratio\n              inspirals: self-consistent evolution in a scalar-charge\n              model}\",\n    eprint = \"2403.08864\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    month = \"3\",\n    year = \"2024\"\n}\n\n@article{Wittek:2024pis,\n    author = \"Wittek, Nikolas A. and Barack, Leor and Pfeiffer, Harald P.\n              and Pound, Adam and Deppe, Nils and Kidder, Lawrence E. and\n              Macedo, Alexandra and Nelli, Kyle C. and Throwe, William\n              and Vu, Nils L.\",\n    title = \"{Relieving scale disparity in binary black hole simulations}\",\n    eprint = \"2410.22290\",\n    archivePrefix = \"arXiv\",\n    primaryClass = \"gr-qc\",\n    month = \"10\",\n    year = \"2024\"\n}\n\n@article{Yee1999,\n  author   = \"Yee, H. C. and Sandham, N. D. and Djomehri, M. J.\",\n  title    = \"Low-Dissipative High-Order Shock-Capturing Methods Using\n              Characteristic-Based Filters\",\n  journal  = \"J. Comput. Phys.\",\n  volume   = \"150\",\n  year     = \"1999\",\n  pages    = \"199--238\",\n  doi      = \"10.1006/jcph.1998.6177\",\n  url      = \"http://cdsads.u-strasbg.fr/abs/1999JCoPh.150..199Y\",\n}\n\n@article{Zanotti2016efficient,\n  title =        {Efficient conservative ADER schemes based on WENO\n                  reconstruction and space-time predictor in primitive\n                  variables},\n  author =       {Zanotti, Olindo and Dumbser, Michael},\n  journal =      {Computational astrophysics and cosmology},\n  volume =       3,\n  number =       1,\n  pages =        {1--32},\n  year =         2016,\n  publisher =    {SpringerOpen},\n  doi = \"10.1186/s40668-015-0014-x\",\n  eprint         = \"1511.04728\",\n  archivePrefix  = \"arXiv\",\n  url = \"https://arxiv.org/abs/1511.04728\"\n}\n\n@article{Zhong2013,\n  author   = \"Zhong, Xinghui and Shu, Chi-Wang\",\n  title    = \"A simple weighted essentially nonoscillatory limiter for\n              {Runge-Kutta} discontinuous {Galerkin} methods\",\n  journal  = \"Journal of Computational Physics\",\n  volume   = \"232\",\n  year     = \"2013\",\n  number   = \"1\",\n  pages    = \"397-415\",\n  doi      = \"10.1016/j.jcp.2012.08.028\",\n  url      = \"https://doi.org/10.1016/j.jcp.2012.08.028\",\n}\n\n@article{Zhu2016,\n  author   = \"Zhu, Jun and Zhong, Xinghui and Shu, Chi-Wang and Qiu, Jianxian\",\n  title    = \"{Runge-Kutta} Discontinuous {Galerkin} Method with a Simple and\n              Compact {Hermite} {WENO} Limiter\",\n  journal  = \"Communications in Computational Physics\",\n  volume   = \"19\",\n  year     = \"2016\",\n  number   = \"4\",\n  pages    = \"944–969\",\n  doi      = \"10.4208/cicp.070215.200715a\",\n  url      = \"https://doi.org/10.4208/cicp.070215.200715a\",\n}\n\n\n# When editing this file, please follow the guidelines in\n# https://spectre-code.org/writing_good_dox.html#writing_dox_citations.\n"
  },
  {
    "path": "docs/Tutorials/BeginnersTutorial.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# A Hitchhiker's Guide to Running SpECTRE {#beginners_guide}\n\n\\tableofcontents\n\nSpECTRE can be a bit complicated to get started with, especially if you aren't\nfamiliar with our core concepts of task-based parallelism and Template\nMeta-Programming (TMP). However, <a\nhref=\"https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#Don't_Panic\">\nDon't Panic</a>. This guide aims to get you introduced to running,\nvisualizing, editing, and then rebuilding SpECTRE to give you a feel for what\nSpECTRE is all about, all on your own laptop! Hopefully by the end of this guide\nyou'll feel comfortable enough to look at other executables and maybe even\nventure into the code itself!\n\n## Prerequisites\n\nTo start off, you'll need to obtain an environment to build and run SpECTRE in.\nYou could try and install all the dependencies yourself, but that is very\ntedious and very error prone. Instead, we provide a\n[Docker](https://docs.docker.com/get-docker/) container with all the\ndependencies pre-installed for you to use. The container also has the SpECTRE\nrepository cloned in it already so you don't have to worry about getting it\nyourself. To obtain the docker image, run\n\n```\ndocker pull sxscollaboration/spectre:demo\n```\n\nAnother program you will need for this tutorial is\n[Paraview](https://www.paraview.org/download/) for visualizing the output. You\nspecifically will need version 5.10.1 for this tutorial.\n\nIf you'd like to use VSCode, the tutorial also has instructions for how to start\nin VSCode as well.\n\n## Into the Container\n\nFor both a terminal and VSCode, create the container in a terminal and start it.\n\n```\ndocker create --entrypoint \"/bin/bash\" --rm --name spectre_demo -p 11111:11111 \\\n    -i -t sxscollaboration/spectre:demo\n```\n```\ndocker start spectre_demo\n```\n\nWe connect port `11111` on your local machine to port `11111` of the container\nso we can use Paraview. The `--rm` will delete the container when you stop it.\nThis won't put you into the container, only start it in the background.\n\n\\note The `--entrypoint \"/bin/bash\"` is important because the default entrypoint\nof the container is the SpECTRE CLI. You can try out the default entrypoint by\nrunning `docker run sxscollaboration/spectre:demo -h` and it'll print the help\nstring for the CLI. See the [Python documentation](py/cli.html) for more.\n\nYou can also run a [Jupyter](https://jupyter.org/index.html) server for\naccessing the Python bindings (see \\ref spectre_using_python) or running Jupyter\nnotebooks. To do so, append another `-p` option with your specified port, e.g.\n`-p 8000:8000`. You can chain as many `-p` options as you want to expose more\nports.\n\nThe SpECTRE repository is located at `/work/spectre` inside the container.\n\n\n### With a Terminal {#with_terminal}\n\nTo hop in the container from a terminal, simply type\n\n```\ndocker attach spectre_demo\n```\n\nand now you're in the container!\n\n### With VSCode\n\nIf you're using VSCode, you'll need the `Remote-Containers` extension to be\nable to access the container. Once you have it, open the\n[command palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette)\nand run the following commands.\n\n1. `Remote-Containers: Attach to Running Container` - you should see the\n   container `spectre_demo` that's currently running. Select that.\n2. `File: Open Folder` - select `/work/spectre` which is where the repo is.\n\nNow you're in the container within VSCode! The terminal in VSCode will look\nidentical to the one if you hadn't used VSCode.\n\n\\note Any changes you make inside `/work/spectre` will be lost once you stop the\ncontainer. If you'd like your changes to persist, get rid of the `--rm` flag in\nthe `docker create` command.\n\n## Compiling the code\n\n\\note From here on out, all paths are assumed to be inside the container unless\nspecified otherwise.\n\nThe container already has a SpECTRE build pre-configured. Go to the build\ndirectory and compile the executables that we will use in this tutorial:\n\n```sh\ncd /work/spectre/build\nmake -j2 ExportCoordinates3D EvolveScalarAdvection2D all-pybindings\n```\n\nThis will compile the code on two cores. If you'd like to use more cores, use\nthe `-j N` option where `N` is the number of cores.\n\nOnce the executables are compiled they will be available in the\n`/work/spectre/build/bin` directory. The container already has this directory\nadded to the `PATH` environment variable, so you can run executables from the\ncommand line right away:\n\n```sh\nspectre --help\n```\n\nIf you are not in the container but instead on a desktop or cluster you can add\nthe bin directory to your path by `cd`ing to the build directory and running\n```\nexport PATH=$PATH:`pwd`/bin\n```\nIf you log out and log back in you will need to set this again, unless you add\nit to your shell's init file.\n\n## Running ExportCoordinates3D\n\nFirst we will run the `ExportCoordinates3D` executable to visualize\nthe coordinates of a binary black hole domain.\nMake a directory where you will run everything:\n\n```\nmkdir /work/runs\ncd /work/runs\n```\n\nCopy over the input file\n`/work/spectre/tests/InputFiles/ExportCoordinates/InputTimeDependent3D.yaml`\ninto your `/work/runs` directory. To run the executable, do\n\n```\nspectre run --no-schedule -j 4 InputTimeDependent3D.yaml\n```\n\nThis will run it on 4 cores (the `--no-schedule` means it will run on the\nlogin/head node if you are using an HPC system). After this finishes you should\nsee two `H5` files in your run directory:\n\n1. ExportCoordinates3DVolume0\n2. ExportCoordinates3DReductions\n\nThe `Volume` file is where we store data from every element in our domain, like\nthe coordinates or the metric. The `Reductions` file is for more global\nquantities like the minimum grid spacing over all the elements in our domain.\n\n\\note Next time you run the executable, you will have to either move or delete\nthe existing `H5` files as SpECTRE will error if it detects that an output file\nalready exists. This is to prevent you from accidentally overwriting data.\nYou can also use the `--force / -f` and `--clean-output / -C` flags to have\n`spectre run` delete the existing files before running the executable.\n\n## Visualizing our BBH Coordinates\n\nNow it's time to use Paraview to visualize the coordinates we use for our BBH\nevolutions! SpECTRE will actually export the physical frame coordinates for\nevery executable we have because they are a really useful diagnostic to have. We\nare just using the ExportCoordinates executable here so that you don't have to\nrun a BBH evolution on your laptop which probably wouldn't work because of\nmemory requirements.\n\nBefore we get to Paraview, we have to tell paraview how to actually use the\ncoordinates in the `Volume` `H5` file. To do this we have a tool called\n`generate-xdmf` in our Python command-line interface. Inside the `runs`\ndirectory where you have the `H5` files, run\n\n```\nspectre generate-xdmf \\\n  --subfile-name element_data --output BBH_Coords \\\n  ExportCoordinates3DVolume*h5\n```\n\nWe output volume data per node so we append the\nnode number to each volume file we have. Since you're most likely running on a\nlaptop, you'll only be running on one node so you should only get one output\nfile for the volume. The `--subfile-name` argument is the group name inside the\n`H5` file where the data is stored (groups can be checked by\n`h5ls -r FILE_NAME`). `generate-xdmf` will generate a file called\n`BBH_Coords.xmf`. Make sure to keep this `.xmf` file next to the volume file it\nwas generated from. It uses relative paths to find the volume file which means\nif you move it, you won't be able to visualize anything.\n\n### Attaching Paraview\n\nThis is where we actually need Paraview. We have a headless (no GUI) vesion of\nparaview inside the container which we will refer to as the \"server\". To start\nthe Paraview server, run\n\n```\npvserver &\n```\n\n\\note For cluster users, check cluster-specific instructions for ParaView.\nYou may require MPI and specify which port to use.\nYou can run `mpirun -np 1 pvserver -p PARAVIEW_REMOTE_PORT` on the remote\ncluster. Here, `PARAVIEW_REMOTE_PORT` is the port that the ParaView server\nwill use on the cluster. You may choose it to be, for example, 11112.\nWith 'pvserver' running on the remote machine, open a new local terminal.\nLocally, run\n`ssh -L11111:YOUR_CLUSTER:PARAVIEW_REMOTE_PORT USERNAME@YOUR_CLUSTER`.\n\nThe `&` is so that the server runs in the background. If you hit `Enter`\na couple times you'll get back to being able to type commands. You should see\nsome output similar to\n\n```\nWaiting for client...\nConnection URL: cs://92bbb69f2af2:11111\nAccepting connection(s): 92bbb69f2af2:11111\n```\n\nThis means it's waiting for you to connect some external Paraview session (the\n\"client\") to the server. Now, ***outside*** the container, start a session of\nParaview 5.10.1. (Again, you must use this version otherwise it won't work\nproperly.)\n\n\\note For cluster users, run `mpirun -n 1 pvserver --version` to check ParaView\nversion. On your computer, download ***exactly the same*** version of Paraview.\n\nGo to `File > Connect`. Click `Add Server`. Name it whatever you\nwant, but keep the Host as `localhost`, the Server Type as `Client/Server`, the\nPort as `11111` (remember the `-p 11111:11111` flag from the docker command?).\nHere's a snapshot of what it should look like before you configure.\n\n\\image html paraview_server.png \"Paraview server settings\"\n\nHit `Configure`, then hit `Save` (we don't care about the launch configuration).\nNow you should see a list of your configured servers. Select the one you just\ncreated and hit `Connect`. It may take a minute or two to connect to the server,\nbut once you do on the left you'll see something like\n\n\\image html paraview_connect.png \"Successfully connected Paraview to a server\"\n\n\\note If you close your client, the server will stop and you won't be able to\nreconnect. You'll have to restart the server in the container.\n\n### Open the XMF File in Paraview Client {#open_xmf}\n\nNow that you have Paraview connected to the container, open the `BBH_Coords.xmf`\nfile you just generated inside Paraview (the paths you'll see are the ones in\nthe container, not your filesystem). You may be prompted to choose which XDMF\nreader to use. Choose the `XDMF Reader` option. The `Xdmf3` options won't work.\nOnce you choose a reader, on the left, you'll see\n\n\\image html beginners_paraview_left.png \"Paraview side-bar\"\n\nYou can uncheck all the boxes in the `Point Arrays` section as they aren't\nnecessary for visualizing the coordinates. Then hit `Apply`. Now you should see\na solid sphere. This isn't super helpful. In the top bar you should see a\ndropdown to change the style that the points are plotted in. Select `Surface\nWith Edges` like so. (Note: Your top bar may look slightly different from this\ndepending on what version of `Paraview` you have.)\n\n\\image html beginners_paraview_top.png \"Paraview top-bar\"\n\nNow you'll have a solid sphere with highlighted lines. To view the interior of\nthe domain, you'll need to add a filter. To access the filters, navigate to\n`Filters` on the top menu bar, hover over `Alphabetical`, and search for your\nfilter of choice.  Probably the two most helpful filters\nfor viewing the domain are the `Slice` and `Clip` filters. (Note that you'll\nhave to choose the `Surface With Edges` option for each filter separately.)\n\n`Slice` is fairly self explanatory in that it will show you a single plane\nthrough the domain. Experiment with different planes to see our whole domain\nstructure!\n\nThe `Clip` filter will remove all points \"above\" a certain plane, where \"above\"\nis in the direction of the normal of that plane. If you combine two orthogonal\n`Clip`s, you can actually view a 3D wedge of our domain. Try moving the centers\nof the planes to view the domain around our excision surfaces! They have a lot\nof cool structure.\n\nIf you'd like to read more about our BBH domain, you can look at the\ndocumentation for `domain::creators::BinaryCompactObject`.\n\n## Evolution of BBH Coordinates\n\nNow that you are able to export and visualize our BBH domain coordinates at a\nsingle time, let's make a small movie of the coordinates as they evolve! To do\nthis, we'll need to edit the input file `InputTimeDependent3D.yaml`. If you\naren't familiar with YAML, it's a file type that uses key-value pairs to create\nactual objects in our C++ code. Feel free to experiment with keys and values in\nour input files. If you're unsure about what a key or value should be, we offer\nan easy way to check the options in the input file without running a whole\nsimulation. In your `/work/runs` directory, if you run\n\n```\nspectre validate InputTimeDependent3D.yaml\n```\n\nthe executable will parse and check the input file. If you made a typo, or added\nan incorrect key/value, a list of the available keys and their associated values\nwill be printed.\n\nTo change the number of times we output the coordinates, we'll need to go to the\n`%EventsAndTriggers:` block of the input file. This block is mainly where we\nspecify which quantities we want to observe in a simulation or where we\n\"Trigger\" a specific \"Event\" to happen. (For more info on `%EventsAndTriggers`,\nsee the \\ref tutorial_events_and_triggers tutorial.) Currently in this input\nfile we only have one Trigger/Event pair. The %Trigger is `TimeCompares:` and\nthe %Event is `Completion`. To have the simulation run longer, change the\n`Value:` under `TimeCompares:` to something larger. If you look at the\n`Evolution:` block above the `%EventsAndTriggers:` block, you'll see that the\ninitial time step is `0.5`. The way this executable is set up, the coordinates\nwill be exported every time step. So set the final time `Value:` under\n`TimeCompares:` to some larger multiple of `0.5` so that you'll have the\ncoordinates at a bunch of different times (a final time of `20` is reasonable.\nDepending on how many cores you run on this should take a couple minutes).\n\nThen, run the executable just like you did above (remember to move or delete the\nexisting `H5` files), run `generate-xdmf`, and open it in Paraview and apply\nsome filters of your choice. We recommend using a `Slice` filter with the normal\npointing in the `-z` direction. This is because our BBH domain rotates about the\n`z` axis. Now, in the top bar of Paraview, you should see a \"Play\" button that\nlooks like a sideways triangle (see the second image in the \\ref open_xmf\nsection). If you click this, Paraview will step through all the timesteps in the\noutput files and you'll be able to see the domain rotate a bit!\n\nNext, we encourage you to play with the other inputs that control how the domain\nevolves over time. These options are housed in the\n\n```yaml\nDomainCreator:\n  BinaryCompactObject:\n    ...\n    TimeDependentMaps:\n      ExpansionMap:\n        ...\n      RotationMap:\n        ...\n      SizeMapA:\n        ...\n      SizeMapB:\n        ...\n```\n\nblock of the input file. Since this tutorial is more about running the code, we\nwon't go into too much detail about each option. However, in general:\n\n1. `ExpansionMap` is a global map (all parts of the domain) that controls the\n   separation between the excision surfaces\n2. `RotationMap` is a global map that controls how the excision spheres rotate\n   about each other\n3. `SizeMap` is a local map only around the excision spheres (not in the wave\n   zone) that control the compression of grid points.\n\nPlay around with these values! You may get an error if you put something that's\ntoo unphysical, but this is a fairly consequence-free playground for you to\nexplore so just try a different value.\n\nNow you have a movie of how BBH coordinates evolve in a SpECTRE simulation!\n\n## Exploring DG+FD\n\nNow that you are able to run, and visualize SpECTRE, let's explore a feature\nthat is fairly unique to SpECTRE and is really powerful for handling\ndiscontinuities and shocks in our simulations. We call this feature `DG+FD`\n(it's also sometimes referred to as just `subcell`).\n\n### Description of DG+FD\n\n`FD` is the usual finite difference you are used to. All of the BSSN codes use\nfinite difference for solving Einstein's equations. FD is very good at capturing\nshocks and discontinuities and is a very robust method, making it well suited\nfor hydro problems and other systems that have shocks and discontinuities.\n\n`DG` stands for Discontinuous Galerkin. DG is a spectral method for representing\na solution on a grid, meaning that instead of taking the difference between the\nfunction value at two points to get the derivative, it uses known basis\nfunctions to represent the solution. Then the derivative can be known\nanalytically and you only need to supply the coefficients for the basis. DG\nworks best for representing smooth solutions; ones with very few shocks and\ndiscontinuities (like GR in vacuum). This makes DG much faster than FD for\nsmooth solutions.\n\nIn SpECTRE, we combine these two different methods into one system to take\nadvantage of the strengths of each. When we have a solution that is smooth in\nsome parts of the domain, but has shocks in other parts, using only one of these\nmethods has disadvantages. If we only used DG, we wouldn't be able to resolve\nthe shocks very well driving the errors up a lot. If we only used FD, we'd be\nable to represent the solution well, but it would be computationally\ninefficient. So we combine DG+FD so that we only do DG in the parts of the\ndomain where the solution is smooth, and switch to FD in parts where there may\nbe a shock or discontinuity. The algorithm for switching between DG and FD is\nexplained in this image.\n\n\\image html dg_fd_schematic.png \"Scheme for switching between DG and FD (credit: Nils Deppe)\"\n\nIf you'd like to learn more about how SpECTRE implements its DG+FD scheme, you\ncan read [the paper](https://arxiv.org/abs/2109.11645) on the ArXiv.\n\n### Running the Kuzmin Problem\n\nTo demonstrate DG+FD, we will be evolving the \\link\nScalarAdvection::Solutions::Kuzmin Kuzmin \\endlink problem using the\n`EvolveScalarAdvection2D` executable. This is a simple test problem that\nrotates a set of geometric shapes with uniform angular velocity, which can be\nused to evaluate how well a numerical code can handle discontinuities stably\nover time. Inside the container make a new directory `/work/runs2` where you\nwill run it. Also copy the default input file in\n`/work/spectre/tests/InputFiles/ScalarAdvection/Kuzmin2D.yaml` to this new\n`/work/runs2` directory.\n\n### Changing the Default Input File\n\nThe default input file has very low resolution so we'll need to crank that up a\nbit. The way to do this is to change the initial refinement levels and initial\nnumber of grid points which are located in\n\n```yaml\nDomainCreator:\n  Rectangle:\n    ...\n    InitialRefinement: [x, y]\n    InitialGridPoints: [x, y]\n```\n\n`InitialRefinement:` represents how many times we split a `Block` in half in\norder to create `Element`s, which are the fundamental units of our domain. So an\ninitial refinement of `[1, 1]` means we split a single Block into 4 elements\n(split in half once in each direction). For an initial refinement of `[2, 2]` we\nfirst do 1 refinement like before, and then split each of the resulting 4\nelements in half again in each direction, resulting in 16 total Elements. To\ndetermine the total number of Elements for a given refinement (same in all\ndirections), just do $2^{\\mathrm{Dim * Refinement}}$. If you're confused by\nthe terminology we use to describe the domain, we have a \\ref domain_concepts\nguide that explains all terms related to our domain.\n\n`InitialGridPoints` represents the number of grid points per dimension in each\nElement after the final refinement has been applied. So if we had an initial\nrefinement of `[2, 2]` like above and then initial grid points `[3, 3]` in each\nElement, we'd have a total of 9x16=144 grid points.\n\nAs for actual numbers to use, you can experiment to see what gives good,\nwell-resolved results. You'll definitely need more refinement than the default\ninput file, but since refinement scales exponentially, this can become very\nexpensive very quickly. On a laptop, you probably shouldn't go higher than\nrefinement `[6, 6]`. As for grid points, this will depend on how much refinement\nyou have. If you have a ton of small elements, you won't need too many grid\npoints to resolve the solution; something like `[4, 4]` would work. If you don't\nhave a lot of refinement, you may want more grid points if you still want to\nresolve your solution. For a DG scheme, increasing the number of grid points (p\nrefinement) reduces the numerical error exponentially where the solution is\nsmooth, so computational resources are used more effectively. However, to\nresolve shocks and discontinuities we have to refine the domain into more and\nsmaller elements instead (h refinement). Striking the most effective balance\nbetween h and p refinement in different parts of the domain is the job of an\nadaptive mesh refinement (AMR) algorithm.\n\nThe default input file only runs for a few time steps so we'll want to make this\nrun longer so we can actually see some evolution. From the documentation of the\n\\link ScalarAdvection::Solutions::Kuzmin Kuzmin \\endlink system, the solution\nwill rotate with an angular velocity of `1.0` (in code units). Thus, to do a\nfull orbit, it will take `6.28` code units of time. In the `%EventsAndTriggers:`\nblock of the input file, we see that the `Completion` event is triggered by the\n`Slabs` trigger. We could, in theory, calculate out how many slabs `6.28` code\nunits is using the time step, but that's super tedious. Instead let's trigger\ncompletion using the `TimeCompares` trigger instead. We used this before when\nexporting the BBH coordinates, so just copy over the yaml block and change the\n`Value:`.\n\nYour final `%EventsAndTriggers:` block should look something like this:\n\n```yaml\nEventsAndTriggers:\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: 6.28\n    Events:\n      - Completion\n  ...\n```\n\nNow you should be ready to run the executable and get some output. Here, you\nwill almost definitely benefit by running this on many cores by adding the\n`-j N` flag to the command you use to run the executable. Since we use lots\nof smaller elements, we distribute these over the available resources via a\n\\link domain::BlockZCurveProcDistribution space filling curve \\endlink to speed\nthings up.\n\n```\nspectre run --no-schedule -j 4 Kuzmin2D.yaml\n```\n\n### Visualizing the Kuzmin Problem\n\nOnce your run finishes, extract the volume data with `generate-xdmf` using\n\n```\nspectre generate-xdmf \\\n  --subfile-name VolumeData --output kuzmin_problem \\\n  ScalarAdvectionKuzmin2DVolume*h5\n```\n\n(Note that the `subfile-name` is different than before because it was different\nin the input file) and load it into Paraview once again. We are only interested\nin the quantity `U` which is the scalar field we were evolving. You can uncheck\nany other boxes. So now, instead of coordinates on your screen, you should see a\nlarge square colored by the solution profile described in the \\link\nScalarAdvection::Solutions::Kuzmin Kuzmin \\endlink system. You should also\nnotice that there are smaller squares that don't touch each other in the middle\nof the domain and on the edges there are large sections that are continuous.\nThese are the FD and DG grids, respectively. If you go to the top bar in\nParaview and change how you view the grid to `Surface With Edges`, this will\nbecome even more apparent.\n\nYou will notice that the FD grid is mostly around where the interesting features\nare in the solution profile; the cylinder with a wedge cut out, the cone, and\nthe hump. And then the DG grid is mostly where the solution should be zero\ntowards the boundary of the domain (i.e. the very smooth part). So right from\nthe start, you can see that we are saving computational effort by only doing the\nexpensive, yet robust, method (FD) where it is necessary and the efficient\nmethod (DG) everywhere else where the solution is smooth.\n\nNow hit the \"Play\" button in the top bar of Paraview and watch the solution\nevolve. You'll notice that the elements in the domain switch back and forth\nbetween FD and DG. They do so in such a way that the elements will switch to FD\nwhen an interesting feature enters the element and then switch back to DG once\nthe feature leaves. In this way, we are able to actually track shocks and\ndiscontinuities in real time in our solution by where the code switches to using\nFD instead of DG. This is extremely useful for expensive GRMHD simulations where\nwe only want to do FD at a shock boundary, yet that shock boundary is moving\nthrough the domain. We are able to dynamically track this shock and resolve it\nwell with FD, then switch back to DG after the shock passes through and the\nsolution has settled down again.\n\nA pretty cool filter you can add is `Warp By Scalar`. In the left panel, choose\nthe solution variable `U` as the scalar to use and hit `Apply`. In the viewing\npanel there should be a `2D` or `3D` button that you can toggle to make the view\n3D. Once you do that you should be able to see that the height of the feature is\nyour solution `U`. If you change the grid to `Surface With Edges` you can see\nthe FD or DG grids warp with the solution. And if you hit \"Play\" it'll rotate\naround and you'll see the features moving in 3D! (Don't worry if you can't find\nthis filter. Not all versions of Paraview may have it.)\n\nWe encourage you to play around with the refinement and grid points before the\nnext section to get a feel for how each changes the runtime and accuracy of\nsolution.\n\n## Editing the Kuzmin System\n\nHopefully now you feel comfortable enough running SpECTRE that you\ncan get the default input file for the pre-built executables, edit it, and run\nit. Now we are going to try our hand at actually editing some code in SpECTRE\nand then building SpECTRE. We're going to stick with the \\link\nScalarAdvection::Solutions::Kuzmin Kuzmin \\endlink system and add a new feature\nto the solution profile!\n\nYou can find the files for the Kuzmin system at\n`/work/spectre/src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/\nKuzmin.?pp`.\nIn the `hpp` file, you'll see a lot of Doxygen documentation and then the actual\nKuzmin class. The only function that you will need to care about is\n\n```cpp\ntemplate <typename DataType>\ntuples::TaggedTuple<ScalarAdvection::Tags::U> variables(\n    const tnsr::I<DataType, 2>& x, double t,\n    tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const;\n```\n\nAll of our analytic solutions have a function similar to this that will set the\nvalue corresponding to the tag in the return type. If you're unfamiliar with\ntags in SpECTRE, you can look at these sections for an explanation, \\ref\ndatabox_a_taggedtuple_databox and \\ref databox_a_proper_databox. However, it's\nbasically just a fancy way of doing a compile-time key/value pair. The tag is\nthe key, and the value is whatever you want it to be. In our case, the value is\na tensor, representing the solution.\n\nThe definition of this function in the `cpp` file is where you will be editing\nthe actual Kuzmin solution. Towards the bottom of this function, there is a\n`for` loop that sets the solution at every grid point. This is where you will\nadd in a new feature to the solution.\n\nYou can pick any feature you want to add, so long as it's inside the domain\nbounds of `[0,1]x[0,1]` and centered around `(0.75, 0.5)`. This is because of\nhow the kuzmin solution is set up with existing features at `(0.25, 0.5); (0.5,\n0.25); (0.5, 0.75)`. If you're having trouble thinking of a feature to add try\none of the following features:\n\n- Square centered at `(0.75, 0.5)` with solution value `1.0`\n  - Side length `0.1` (any larger and it might interfere with the other\n    features)\n  - Circle of radius `0.045` centered on the square with value `0.0`\n- Triangle centered at `(0.75, 0.5)` with one corner facing in the `+x`\n  direction with solution value `1.0`\n- Square centered at `(0.75, 0.5)`\n  - Side length `0.1` (any larger and it might interfere with the other\n    features)\n  - Left half of the square has value `1.0` and right half of the square has\n    value `0.5`\n\n\\note The more detailed you make your feature, the more resolution you will need\nto resolve it.\n\n### Re-building SpECTRE\n\nOnce you have your feature coded up, go ahead and save your changes. Now we will\nbuild SpECTRE! Go to the `/work/spectre/build` directory. This is where you have\nto be in order to build SpECTRE. We use [CMake](https://cmake.org/) to configure\nour build directory. However, since the executables are already pre-built, this\nmeans the build directory is already configured! So you don't have to worry\nabout `CMake` for now. If you wanted to reconfigure, for example using a\ndifferent compiler, then you'd have to run `CMake`. If you want to learn more\nabout how we use `CMake`, take a look at the \\ref common_cmake_flags developers\nguide.\n\nTo build the Kuzmin executable, run\n\n```\nmake EvolveScalarAdvection2D\n```\n\nThis should be very fast because you only edited a `cpp` file. Congrats! You've\njust built SpECTRE!\n\nNow re-run the executable in your `/work/runs2` directory. Hopefully everything\nworks and you get some output. When you plot it in Paraview, it should look\nalmost the same as before except your feature will be there too rotating with\nthe others! How cool! You can also see if your feature needs FD or DG more by\nhow much it switches back and forth.\n\nExperiment some more with either different features or different resolution!\n\n## Conclusions\n\nCongrats! You've made it through the tutorial! If you only want to run our\npre-built executables, you have all the tools necessary to run, visualize, and\nre-build them. If you want a full list of our executables, do\n`make list` in the build directory. This will also include our `Test_`\nexecutables which you can just ignore.\n\nIn an already configured build directory, all you have to do to build a new\nexecutable is\n\n```\nmake ExecutableName\n```\n\nand then you can copy the default input file from\n`/work/spectre/tests/InputFiles` and run it. Running an executable with the\n`--help` flag will give a description of what system is being evolved and the\ninput options necessary.\n"
  },
  {
    "path": "docs/Tutorials/CCE.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# %Running CCE {#tutorial_cce}\n\n\\tableofcontents\n\n## Acquiring the CCE module {#acquiring_the_cce_module}\n\nThere are a couple different ways to acquire the CCE module/executable.\n\n### From a release {#cce_from_release}\n\nStarting from late May 2024, in every\n[Release of SpECTRE](https://github.com/sxs-collaboration/spectre/releases) we\noffer a tarball that contains everything needed to run CCE on a large number of\ndifferent systems. This will be under the `Assets` section towards the bottom\nof the release (there may be a lot of text detailing what's been updated in this\nrelease). Inside this tarball is\n\n- the CCE executable `CharacteristicExtract`\n- an example YAML input file\n- an example set of Bondi-Sachs worldtube data in the `Tests/` directory (see\n   [Input worldtube data formats](#input_worldtube_data_format) section)\n- example output from CCE in the `Tests/` directory\n- a `PreprocessCceWorldtube` executable and YAML files for converting between\n   [worldtube data formats](#input_worldtube_data_format) in the\n   `PreprocessCceWorldtube/` directory\n- a `WriteCceWorldtubeCoordsToFile` executable that writes\n   [grid points on a sphere](#spherical_nodes) to a text file in the\n   `PreprocessCceWorldtube/` directory\n- a python script `CheckCceOutput.py` (meant to be run from the root of the\n  tarball and after you run the example YAML input file also in the root of the\n  tarball) that will check if the example output is correct\n\n\\note The tarball is `.xz` so use `tar -xf TarName.tar.xz` to extract. The `-z`\nflag to use gzip will cause an error.\n\nSee [Running the CCE executable](#running_the_cce_executable) for how to run\nCCE.\n\nWe have tested that this executable works natively on the following machines:\n\n- Expanse\n- Anvil\n- Stampede3\n- Delta (if you add\n  `LD_LIBRARY_PATH=/sw/spack/deltas11-2023-03/apps/linux-rhel8-x86_64/gcc-8.5.0/gcc-11.4.0-yycklku/lib64/:$LD_LIBRARY_PATH`\n  before running CCE)\n- Perlmutter\n- Ubuntu 18.04 LTS or later (LTS version only)\n\nWe have also tested that this executable works inside our\n[`dev` Docker container](https://hub.docker.com/r/sxscollaboration/spectre/tags)\non the following machines (in addition to the ones above):\n\n- Frontera\n- Delta\n\n### From Docker\n\nYou can download a docker image `sxscollaboration/spectre:deploy` which has a\nfew pre-built executables within, including the ones listed above in the\n[release](#cce_from_release) section. See the containerized releases section of\nour \\ref installation instructions for how start the container.\n\nThe input files can be found within the container at\n`/work/spectre/tests/InputFiles/`.\n\n### From source\n\nYou can clone the [spectre repo](https://github.com/sxs-collaboration/spectre)\nand follow the instructions on the \\ref installation page to obtain an\nenvironment to configure and build SpECTRE. Once you have a configured `build`\ndirectory, build the CCE executable with\n\n```\nmake CharacteristicExtract\n```\n\n\\note You may want to add the `-j4` flag to speed up compilation. However, be\nwarned that this executable will need several GB of memory to build.\n\n## Input worldtube data format {#input_worldtube_data_format}\n\nIn order to run the CCE executable, the worldtube data must be represented as\nBondi-Sachs variables decomposed as a subset of spin-weighted spherical harmonic\nmodes on a sphere of constant coordinate radius. We have chosen this format\nbecause it is far more space-efficient to store on disk than other formats. This\nsection will detail the\n[required data format](#required_h5_worldtube_data_format), provide options for\n[converting worldtube data](#converting_worldtube_data) from other NR codes into\nour format, and give insights into\n[what the worldtube data should look like](#worldtube_data_looks).\n\n### Required H5 worldtube data format {#required_h5_worldtube_data_format}\n\nWithin the H5 file that holds the worldtube data, there must be the following\ndatasets with these exact names (including the `.dat` suffix):\n\n- `Beta.dat`\n- `DrJ.dat`\n- `DuR.dat`\n- `H.dat`\n- `J.dat`\n- `Q.dat`\n- `R.dat`\n- `U.dat`\n- `W.dat`\n\nEach dataset in the file must also have an attribute named `Legend` which\nis an ASCII-encoded null-terminated variable-length string. That is, the HDF5\ntype is:\n\n```\nDATATYPE  H5T_STRING {\n  STRSIZE H5T_VARIABLE;\n  STRPAD H5T_STR_NULLTERM;\n  CSET H5T_CSET_ASCII;\n  CTYPE H5T_C_S1;\n}\n```\n\nThis can be checked for a dataset by running\n\n```\nh5dump -a Beta.dat/Legend WorldtubeFile.h5\n```\n\nFor the ordering of the data, we use spherical harmonic conventions documented\nby the ylm::Spherepack class. Each row must start with the time stamp, and the\nremaining values are the complex modes in m-varies-fastest format. For\nspin-weight zero Bondi variables (`Beta`, `R`, `DuR`, `W`), we omit the\nredundant negative-m modes and imaginary parts of the m=0 modes to save space on\ndisk. Here is an example of a legend for the spin-weight zero variables:\n\n```\n\"time\", \"Re(0,0)\", \"Re(1,0)\", \"Re(1,1)\", \"Im(1,1)\", \"Re(2,0)\",\n\"Re(2,1)\", \"Im(2,1)\", \"Re(2,2)\", \"Im(2,2)\", \"Re(3,0)\", \"Re(3,1)\",\n\"Im(3,1)\", \"Re(3,2)\", \"Im(3,2)\", \"Re(3,3)\", \"Im(3,3)\", ...\n```\n\nFor non-zero spin-weight Bondi variables (`J`, `DrJ`, `H`, `Q`, `U`) we must\nstore all complex m-modes. Here is an example of a legend for variables where\nall complex m-modes must be specified:\n\n```\n\"time\", \"Re(0,0)\", \"Im(0,0)\", \"Re(1,-1)\", \"Im(1,-1)\", \"Re(1,0)\", \"Im(1,0)\",\n\"Re(1,1)\", \"Im(1,1)\", \"Re(2,-2)\", \"Im(2,-2)\", \"Re(2,-1)\", \"Im(2,-1)\", \"Re(2,0)\",\n\"Im(2,0)\", \"Re(2,1)\", \"Im(2,1)\", \"Re(2,2)\", \"Im(2,2)\", ...\n```\n\nWe don't have strict requirement on the name of the H5 file that holds the\nworldtube data. However, it is recommended to name the H5 file `...CceRXXXX.h5`,\nwhere the `XXXX` is to be replaced by the zero-padded integer for which the\nextraction radius is equal to `XXXX`M. For instance, a 100M extraction should\nhave filename `...CceR0100.h5`. If you do not adhere to this naming convention,\nyou will need to specify the extraction radius in your YAML input file.\n\n\\note This scheme of labeling files with the extraction radius is constructed\nfor compatibility with worldtube data from the SXS Collaboration's SpEC code.\n\n### Converting to the required H5 format {#converting_worldtube_data}\n\nUnless you are using worldtube data that was generated from SpECTRE (or SpEC),\nit's possible that your worldtube data is not in the correct format. We allow\nconversion into our data format from a few other data formats using the\n[`PreprocessCceWorldtube` executable provided](#acquiring_the_cce_module). These are\n\n- Nodal cartesian metric data (which we refer to as \"metric nodal\")\n- Modal cartesian metric data (which we refer to as \"metric modal\")\n- Nodal Bondi-Sachs data (which we refer to as \"bondi nodal\")\n\nRequirements for these data formats are listed below.\n\n#### Spherical harmonic modes {#spherical_modes}\n\nWhen we refer to a \"modal\" data format, we mean that the worldtube data are\nstored as spherical harmonic coefficients (a.k.a. modes). We use spherical\nharmonic conventions documented by the ylm::Spherepack class. For each dataset,\neach row must start with the time stamp, and the remaining values are the\ncomplex modes in m-varies-fastest format. That is,\n\n```\n\"time\", \"Re(0,0)\", \"Im(0,0)\", \"Re(1,-1)\", \"Im(1,-1)\", \"Re(1,0)\", \"Im(1,0)\",\n\"Re(1,1)\", \"Im(1,1)\", \"Re(2,-2)\", \"Im(2,-2)\", \"Re(2,-1)\", \"Im(2,-1)\", \"Re(2,0)\",\n\"Im(2,0)\", \"Re(2,1)\", \"Im(2,1)\", \"Re(2,2)\", \"Im(2,2)\", ...\n```\n\nEach dataset in the H5 file must also have an attribute\nnamed `Legend` which is an ASCII-encoded null-terminated variable-length string.\n\n#### Spherical harmonic nodes {#spherical_nodes}\n\nWhen we refer to a \"nodal\" data format, we mean that the worldtube data are\nstored as values at specially chosen collocation points (a.k.a. grid points or\nnodes). This allows SpECTRE to perform integrals, derivatives, and interpolation\nexactly on the input data. These grid points are Gauss-Legendre in $cos(\\theta)$\nand equally spaced in $\\phi$.\n\nBelow is a routine for computing the spherical\nharmonic $\\theta$ and $\\phi$ values. These can be used to compute the Cartesian\nlocations for a given radius using the standard transformation. The routine\nsupports \\f$\\ell\\in[4, 32]\\f$.\n\n<details id=\"details\">\n<summary> C Code for computing SpECTRE CCE gridpoint locations </summary>\n\\snippet Test_Spherepack.cpp spectre_cce_grid_point_locations\n</details>\n\nAlternatively, if your code can read in grid points from a text file, you can\nrun the `WriteCceWorldtubeCoordsToFile` executable like so to get a text file\nwith three columns for the x,y,z coordinates of each point.\n\n```\n./WriteCceWorldtubeCoordsToFile -r 200 -L 16 -o GridPointsR200.txt\n```\n\nEach dataset holds `1 + (l_max + 1) * (2 * l_max + 1)` columns, with the\nfirst one being the `time`. The columns must be in \\f$\\theta\\f$-varies-fastest\nordering. That is,\n\n```\n\"time\",\n\"Phi_0_Theta_0\", \"Phi_0_Theta_1\", \"Phi_0_Theta_2\", \"Phi_0_Theta_3\", \"Phi_0_Theta_4\",\n\"Phi_1_Theta_0\", \"Phi_1_Theta_1\", \"Phi_1_Theta_2\", \"Phi_1_Theta_3\", \"Phi_1_Theta_4\",\n```\n\nEach dataset in the H5 file must also have an attribute\nnamed `Legend` which is an ASCII-encoded null-terminated variable-length string.\n\n\\note Nodal data is likely the easiest to write out since no conversion to\nspherical harmonic coefficients is necessary.\n\n#### ADM Cartesian metric and derivatives {#adm_cartesian_metric_and_derivatives}\n\nFor worldtube data stored in an H5 file in the \"ADM metric nodal\" format, there\nmust be the following datasets with these exact names (including the `.dat`\nsuffix):\n\n- `gxx.dat`, `gxy.dat`, `gxz.dat`, `gyy.dat`, `gyz.dat`, `gzz.dat`\n- `Dxgxx.dat`, `Dxgxy.dat`, `Dxgxz.dat`, `Dxgyy.dat`, `Dxgyz.dat`, `Dxgzz.dat`\n- `Dygxx.dat`, `Dygxy.dat`, `Dygxz.dat`, `Dygyy.dat`, `Dygyz.dat`, `Dygzz.dat`\n- `Dzgxx.dat`, `Dzgxy.dat`, `Dzgxz.dat`, `Dzgyy.dat`, `Dzgyz.dat`, `Dzgzz.dat`\n- `Shiftx.dat`, `Shifty.dat`, `Shiftz.dat`\n- `DxShiftx.dat`, `DxShifty.dat`, `DxShiftz.dat`\n- `DyShiftx.dat`, `DyShifty.dat`, `DyShiftz.dat`\n- `DzShiftx.dat`, `DzShifty.dat`, `DzShiftz.dat`\n- `Lapse.dat`, `DxLapse.dat`, `DyLapse.dat`, `DzLapse.dat`\n- `Kxx.dat`, `Kxy.dat`, `Kxz.dat`, `Kyy.dat`, `Kyz.dat`, `Kzz.dat`\n- Either: `AuxiliaryShiftx.dat`, `AuxiliaryShifty.dat`, `AuxiliaryShiftz.dat`\n- Or: `ConformalChristoffelx.dat`, `ConformalChristoffely.dat`,\n  `ConformalChristoffelz.dat`\n\nHere `g` represents the spacetime metric, but we only require the spatial\ncomponents (e.g. `gxx.dat`, `gxy.dat`, etc...) so in practice, those are the\ntensor components of the spatial metric. The temporal components of the\nspacetime metric are stored separately in the lapse and shift. Each of the\nspatial metric, lapse, and shift must also have their cartesian derivatives. `K`\nis the extrinsic curvature, `AuxiliaryShift` is the auxiliary shift vector used\nin the first-order form of the Gamma-driver condition, and\n`ConformalChristoffel` is the trace of the conformal second_order symbols.\n\nWe will compute the time derivative of the spatial metric using Eq. (2.134) of\n\\cite BaumgarteShapiro,\n\n\\begin{equation}\n\\partial_t \\gamma_{ij} = -2\\alpha K_{ij} + D_i\\beta_j + D_j\\beta_i.\n\\end{equation}\n\nThe time derivative of the lapse is computed using the `1+log` slicing condition\nfrom Eq. (4.87) of \\cite BaumgarteShapiro\n\n\\begin{equation}\n\\partial_t \\alpha = -2\\alpha K + \\beta^j\\partial_j\\alpha,\n\\end{equation}\n\nand the time derivative of the shift is computed using either the first order\nreduction form of the Gamma-driver condition from Eq. (4.89) of\n\\cite BaumgarteShapiro\n\n\\begin{equation}\n\\partial_t \\beta^i = \\eta B^i + \\beta^j\\partial_j\\beta^i,\n\\end{equation}\n\nwhere you can choose $\\eta$ (typically $\\eta=0.75$) or using the integrated\nGamma-driver condition from Eq. (12) of \\cite Hilditch:2012fp\n\n\\begin{equation}\n\\partial_t \\beta^i = \\tilde{\\Gamma}^i - \\eta\\beta^i + \\beta^j\\partial_j\\beta^i,\n\\end{equation}\n\nagain, where you can choose $\\eta$ (typically $\\eta=2/M_{\\textrm{ADM}}$).\n\n\\warning If your worldtube data is in the ADM metric nodal format but you have\nnot used `1+log` slicing and the Gamma-driver conditions specified above, your\ntime derivatives will be **wrong**. If you'd like us to support other commonly\nused gauge conditions (or variants of `1+log` or Gamma-driver), please open an\nissue on our\n[GitHub](https://github.com/sxs-collaboration/spectre).\n\nThe layout of each of these datasets must be\n[spherical harmonic nodes](#spherical_nodes).\n\n#### Cartesian metric and derivatives {#cartesian_metric_and_derivatives}\n\nFor worldtube data stored in an H5 file in either the \"metric nodal\" or \"metric\nmodal\" formats, there must be the following datasets with these exact names\n(including the `.dat` suffix):\n\n- `gxx.dat`, `gxy.dat`, `gxz.dat`, `gyy.dat`, `gyz.dat`, `gzz.dat`\n- `Drgxx.dat`, `Drgxy.dat`, `Drgxz.dat`, `Drgyy.dat`, `Drgyz.dat`, `Drgzz.dat`\n- `Dtgxx.dat`, `Dtgxy.dat`, `Dtgxz.dat`, `Dtgyy.dat`, `Dtgyz.dat`, `Dtgzz.dat`\n- `Shiftx.dat`, `Shifty.dat`, `Shiftz.dat`\n- `DrShiftx.dat`, `DrShifty.dat`, `DrShiftz.dat`\n- `DtShiftx.dat`, `DtShifty.dat`, `DtShiftz.dat`\n- `Lapse.dat`\n- `DrLapse.dat`\n- `DtLapse.dat`\n\nHere `g` represents the spacetime metric, but we only require the spatial\ncomponents (e.g. `gxx.dat`, `gxy.dat`, etc...) so in practice, those are the\ntensor components of the spatial metric. The temporal components of the\nspacetime metric are stored separately in the lapse and shift. The layout of\neach of these datasets must be in either\n[spherical harmonic modes](#spherical_modes) or\n[spherical harmonic nodes](#spherical_nodes).\n\n#### Bondi-Sachs {#bondi_sachs}\n\nIn the \"bondi nodal\" format, you must have the same Bondi variables as the\n[required format](#required_h5_worldtube_data_format), but each variable layout\nmust be the [spherical harmonic nodal layout](#spherical_nodes) with complex\nvalues interleaved as `Re`, `Im`, `Re`, `Im`, ...\n\nIf you already have data in the\n[required \"bondi modal\" format](#required_h5_worldtube_data_format), then\nnothing needs to be done.\n\n#### Running the PreprocessCceWorldtube executable\n\nThe `PreprocessCceWorldtube` executable should be run on any of the\n[allowed input formats](#converting_worldtube_data), and will produce a\ncorresponding Bondi-Sachs worldtube file that can be read in by CCE. This\nexecutable works similarly to our other executables by accepting a YAML input\nfile:\n\n```\nPreprocessCceWorldtube --input-file PreprocessCceWorldtube.yaml\n```\n\nwith a YAML file\n\n\\snippet PreprocessCceWorldtube.yaml preprocess_cce_worldtube_yaml_doxygen_example\n\nIn addition to converting worldtube data formats, `PreprocessCceWorldtube` also\naccepts multiple input worldtube H5 files that have sequential times (e.g. from\ndifferent checkpoints) and will combine the times from all H5 files alongside\nconverting the worldtube data format. If there are duplicate or overlapping\ntimes, the last/latest of the times are chosen. If you pass multiple input\nworldtube H5 files, it is assumed that they are ordered increasing in time.\n\nHere are some notes about the different options in the YAML input file:\n\n- If the extraction radius is in the `InputH5File` names, then the\n  `ExtractionRadius` option can be `Auto`. Otherwise, it must be specified.\n- The option `LMaxFactor` determines the factor by which the resolution of the\n  boundary computation that is run will exceed the resolution of the input and\n  output files. Empirically, we have found that `LMaxFactor` of 3 is sufficient\n  to achieve roundoff precision in all boundary data we have attempted, and an\n  `LMaxFactor` of 2 is usually sufficient to vastly exceed the precision of the\n  simulation that provided the boundary dataset.\n- `FixSpecNormalization` should always be `False` unless you are using a\n  particualy old version of SpEC\n- `DescendingM` should always be `False`. This option is to support metric modal\n  data from SpEC. Our [data format](#input_worldtube_data_format) is always in\n  *ascending* m.\n- `BufferDepth` is an advanced option that lets you load more data into RAM at\n  once so there are fewer filesystem accesses.\n- If using the ADM metric nodal input data format with a first-order form gamma\n  driver, specify the `InputDataFormat:` like so:\n\\snippet AdmFirstOrderDriverPreprocessCceWorldtube.yaml first_order_input_data_format_example\n- If using the ADM metric nodal input data format with an integrated gamma\n  driver, specify the `InputDataFormat:` like so:\n\\snippet AdmSecondOrderDriverPreprocessCceWorldtube.yaml second_order_input_data_format_example\n\n### What Worldtube data \"should\" look like {#worldtube_data_looks}\n\nWhile no two simulations will look exactly the same, there are some general\ntrends in the worldtube data to look for. Here is a plot of some modes of the\nBondi variable `J` from the [Bondi-Sachs](#bondi_sachs) worldtube format.\n\n\\image html worldtube_J.png \"Bondi variable J on the Worldtube\"\n\nThe 2,2 modes are oscillatory and capture the orbits of the two objects. The\nreal part of the 2,0 mode contains the gravitational memory of the system. Then\nfor this system, all the other modes are subdominant.\n\nIf you are using the [cartesian metric](#cartesian_metric_and_derivatives) or\n[adm cartesian metric](#adm_cartesian_metric_and_derivatives)\nworldtube format, here is a plot of the imaginary part of the 2,2 mode of the\nlapse and its radial and time derivative during inspiral.\n\n\\image html lapse.png \"2,2 component of lapse and its radial and time derivative\"\n\nOne thing to keep in mind with this plot is that it was produced using the\nGeneralized Harmonic formulation of Einstein's equations using the damped\nharmonic gauge. Therefore, if you are using a different formulation and gauge\n(like BSSN + moving punctures), the lapse may look different than this. One way\nto sanity check your data (regardless of what type it is or where you got it\nfrom) is to look and how the mode amplitude for a given quantity decays as you\nincrease (l,m). Here is a plot of the amplitude of the modes for the lapse in\nthe above plot.\n\n\\image html amp_lapse.png \"Amplitude of modes of Lapse\"\n\nYou'll notice that most modes are around machine precision and only the first\nfew have any real impact. This is expected.\n\n## Input file for CCE\n\nInput files for CCE are commonly named `CharacteristicExtract.yaml`. An example\ninput file with comments explaining some of the options can be found in\n`$SPECTRE_HOME/tests/InputFiles/Cce/CharacteristicExtract.yaml`. Here we expand\na bit on why we chose some of those parameters.\n\n### General options\n\n- For resolution, the example input file has lmax (`Cce.LMax`) of 20, and\n  filter lmax (`Filtering.FilterLMax`) of 18; that may run a bit slow for\n  basic tests, but this value yields the best precision-to-run-time ratio\n  for a typical BBH system. Note that precision doesn't improve above lmax 24,\n  filter 22 (be sure to update the filter as you update lmax -- the filter\n  should generally be around 2 lower than the maximum l to reduce possible\n  aliasing).\n- If you want to just run over all times in the worldtube H5 file, you can\n  set both the `StartTime` and `EndTime` to `Auto` and it will automatically\n  figure it out based on the data in the worldtube file.\n- The `ScriOutputDensity` adds extra interpolation points to the output,\n  which is useful for finite-difference derivatives on the output data, but\n  otherwise it'll just unnecessarily inflate the output files, so if you\n  don't need the extra points, best just set it to 1.\n- For production level runs, it's recommended to have the\n  `Cce.Evolution.StepChoosers.Constant` option set to 0.1 for an accurate time\n  evolution. However, if you're just testing, this can be increased to 0.5 to\n  speed things up.\n- We generally do not recommend extracting at less than 100M due to the CCE junk\n  radiation being much worse at these smaller worldtube radii. That being said,\n  we also recommend running CCE over several worldtube radii and checking which\n  is the best based on the Bianchi identity violations. There isn't necessarily\n  a \"best radius\" to extract waveforms at.\n- Most users will not need it, but if you want to dump data from the volume\n  (instead of only on future null infinity), this is possible with the\n  `ObserveFields` option to `Events` within `EventsAndTriggersAtSlabs` in the\n  input file. See the input file referenced above for a (commented-out) example,\n  and the documentation of the class `Cce::Events::ObserveFields` for details.\n\n### Initial data on the null hypersurface\n\nChoosing initial data on the initial null hypersurface is a non-trivial task and\nis an active area of research. We want initial data that will reduce the amount\nof CCE junk radiation as much as possible, while also having the initial data\nwork for as many cases as possible.\n\nSpECTRE currently has four different methods to choose the initial data on\nthe null hypersurface. In order from most recommended to least recommended,\nthese are:\n\n- `ConformalFactor`: Try to make initial time coordinate as inertial as\n  possible at \\f$\\mathscr{I}^+\\f$ with a smart choice of the conformal factor.\n  This will work for many cases, but not all. But will produce the best initial\n  data when it does work.\n- `InverseCubic`: Ansatz where \\f$J = A/r + B/r^3\\f$. This is very robust and\n  almost never fails, but contains a lot of CCE junk radiation compared to\n  `ConformalFactor`.\n- `ZeroNonSmooth`: Make `J` vanish. Like the name says, it's not smooth.\n- `NoIncomingRadiation`: Make \\f$\\Psi_0 = 0\\f$; this does not actually lead\n  to no incoming radiation, since \\f$\\Psi_0\\f$ and \\f$\\Psi_4\\f$ both include\n  incoming and outgoing radiation.\n\n### Rechunking worldtube data\n\n\\note This section is less important than the others and really only matters if\nyou will be doing a large number of CCE runs like for a catalog. For only a\ncouple runs or just for testing, this part is unnecessary and can be skipped.\n\nCCE will run faster if the input worldtube hdf5 file is chunked in small numbers\nof complete rows. This is relevant because by default, SpEC and SpECTRE write\ntheir worldtube  files chunked along full time-series columns, which is\nefficient for writing and compression, but not for reading in to CCE. In that\ncase, you can rechunk the input file before running CCE for maximum performance.\nThis can be done, for instance, using h5py (you will need to fill in filenames\nappropriate to your case in place of \"BondiCceR0050.h5\" and\n\"RechunkBondiCceR0050.h5\"):\n\n```py\nimport h5py\ninput_file = \"BondiCceR0050.h5\"\noutput_file = \"RechunkBondiCceR0050.h5\"\nwith h5py.File(input_file,'r') as input_h5,\\\n  h5py.File(output_file, 'w') as output_h5:\n  for dset in input_h5:\n      if(\"Version\" in dset):\n          output_h5[dset] = input_h5[dset][()]\n          continue\n      number_of_columns = input_h5[dset][()].shape[1]\n      output_h5.create_dataset(dset, data=input_h5[dset],\n                               maxshape=(None, number_of_columns),\n                               chunks=(4, number_of_columns), dtype='d')\n      for attribute in input_h5[dset].attrs.keys():\n          output_h5[dset].attrs[attribute] = input_h5[dset].attrs[attribute]\n```\n\nThe rechunked data will still be in the same\n[format](#input_worldtube_data_format) as before, but will just have a\ndifferent underlying structure in the H5 file that makes it faster to read in.\n\n## Running the CCE executable {#running_the_cce_executable}\n\nOnce you have [acquired an executable](#acquiring_the_cce_module), running CCE\nin a supported environment is a simple command:\n\n```\n./CharacteristicExtract --input-file CharacteristicExtract.yaml\n```\n\nYou may notice at the beginning you get some warnings that look like\n\n```\nWarning: iterative angular solve did not reach target tolerance 1.000000e-13.\nExited after 300 iterations, achieving final maximum over collocation points\n for deviation from target of 2.073455e-08\nProceeding with evolution using the partial result from partial angular solve.\n```\n\nThis is normal and expected. All it means is that initially an angular solve\ndidn't hit a tolerance. We've found that it never really reaches the tolerance\nof 1e-13, but we still keep this tolerance so it gets as low as possible.\n\nAfter this, you'll likely see some output like\n\n```\nSimulation time: 10.000000\n  Wall time: 00:00:44\nSimulation time: 20.000000\n  Wall time: 00:01:14\n```\n\nwhich tells you that the simulation is proceeding as expected. When the run\nfinished, you'll see something like\n\n```\nDone!\nWall time: 06:01:41\nDate and time at completion: Thu May 23 22:31:27 2024\n\n[Partition 0][Node 0] End of program\n```\n\nIn terms of runtime, we've found that for a ~5000M long cauchy simulation, CCE\ntakes about 6 hours to run. This will vary based on a number of factors like how\nlong the cauchy evolution actually is, the desired error tolerance of your\ncharacteristic timestepper, and also how much data you output at future null\ninfinity. Therefore, take these numbers with a grain of salt and only use them\nas a rough estimate for how long a job will take.\n\n\\note CCE can technically run on two (2) cores by adding the option `++ppn 2` to\nthe above command, however, we have found in practice that this makes\nlittle-to-no difference in the runtime of the executable.\n\n## Output from CCE\n\nOnce you have the reduction data output file from a successful CCE run, you can\nconfirm the integrity of the h5 file and its contents by running\n\n```\nh5ls -r CharacteristicExtractReduction.h5\n```\n\nFor the reduction file produced by a successful run, the output of the `h5ls`\nshould resemble\n\n```\n/SpectreR0100.cce                 Group\n/SpectreR0100.cce/EthInertialRetardedTime Dataset {26451/Inf, 163}\n/SpectreR0100.cce/News            Dataset {26451/Inf, 163}\n/SpectreR0100.cce/Psi0            Dataset {26451/Inf, 163}\n/SpectreR0100.cce/Psi1            Dataset {26451/Inf, 163}\n/SpectreR0100.cce/Psi2            Dataset {26451/Inf, 163}\n/SpectreR0100.cce/Psi3            Dataset {26451/Inf, 163}\n/SpectreR0100.cce/Psi4            Dataset {26451/Inf, 163}\n/SpectreR0100.cce/Strain          Dataset {26451/Inf, 163}\n/src.tar.gz              Dataset {7757329}\n```\n\nNotice that the worldtube radius will be encoded into the subfile name.\n\n\\note Prior to\n[this Pull Request](https://github.com/sxs-collaboration/spectre/pull/5985),\nmerged May 15, 2024, the output of `h5ls` looked like this\n```\n/                        Group\n/Cce                     Group\n/Cce/EthInertialRetardedTime.dat Dataset {3995/Inf, 163}\n/Cce/News.dat                 Dataset {3995/Inf, 163}\n/Cce/Psi0.dat                 Dataset {3995/Inf, 163}\n/Cce/Psi1.dat                 Dataset {3995/Inf, 163}\n/Cce/Psi2.dat                 Dataset {3995/Inf, 163}\n/Cce/Psi3.dat                 Dataset {3995/Inf, 163}\n/Cce/Psi4.dat                 Dataset {3995/Inf, 163}\n/Cce/Strain.dat               Dataset {3995/Inf, 163}\n/src.tar.gz               Dataset {3750199}\n```\n\nNo matter the spin weight of the quantity, the $\\ell = 0,1$ modes will always be\noutput by CCE. Also, the `/SpectreR0100.cce` group will have a legend attribute\nthat specifies the columns.\n\n### Raw CCE output\n\nThe `Strain` represents the asymptotic transverse-traceless contribution\nto the metric scaled by the Bondi radius (to give the asymptotically leading\npart), the `News` represents the first time derivative of the strain, and each\nof the `Psi...` datasets represent the Weyl scalars, each scaled by the\nappropriate factor of the Bondi-Sachs radius to retrieve the asymptotically\nleading contribution.\n\nThe `EthInertialRetardedTime` is a diagnostic dataset that represents the\nangular derivative of the inertial retarded time, which determines the\ncoordinate transformation that is performed at future null infinity.\n\nIf you'd like to visualize the output of a CCE run, we offer a\n[CLI](py/cli.html) that will produce a plot of all of quantities except\n`EthInertialRetardedTime`. To see how to use this CLI, run\n\n```\nspectre plot cce -h\n```\n\nIf you'd like to do something more complicated than just make a quick plot,\nyou'll have to load in the output data yourself using `h5py` or our\n`spectre.IO.H5` bindings.\n\n\\note The CLI can also plot the \"old\" version of CCE output, described above.\nPass `--cce-group %Cce` to the CLI. This option is only for backwards\ncompatibility with the old CCE output and is not supported for the current\nversion of output. This options is deprecated and will be removed in the future.\n\n### Frame fixing\n\nYou may notice some odd features in some of the output quantities if you try and\nplot them. This is not a bug in CCE (not that we know of at least). This occurs\nbecause the data at future null infinity is in the wrong Bondi-Metzner-Sachs\n(BMS) frame. In order to put this data into the correct BMS frame, the\n[SXS Collaboration](https://github.com/sxs-collaboration) offers a python/numba\ncode called [scri](https://github.com/moble/scri) to do these transformations.\nSee their documentation for how to install/run/plot a waveform at future null\ninfinity in the correct BMS frame.\n\nBelow is a plot of the imaginary part of the 2,2 component for the strain when\nplotted using the raw output from SpECTRE CCE and BMS frame-fixing with scri.\nYou'll notice that there is a non-zero offset, and this component of the strain\ndoesn't decay back down to zero during the ringdown. This is because of the\nimproper BMS frame that the SpECTRE CCE waveform is in. A supertranslation must\nbe applied to transform the waveform into the correct BMS frame. See\n\\cite Mitman2024review for a review of BMS transformations and gravitational\nmemory. To perform the frame fixing, see\n[this tutorial](https://scri.readthedocs.io/en/latest/tutorial_abd.html#loading-cce-data-and-adjusting-the-bms-frame)\nin scri.\n\n\\image html im_h22.png \"Imaginary part of 2,2 component of the strain\"\n\nThe discrepancy is even more apparent if you plot the amplitude of the two\nwaveforms. It's pretty clear which waveform is the more \"physical\" one.\n\n\\image html amp_im_h22.png \"Amplitude of imaginary part of 2,2 component of the strain\"\n\nNotice that there are still some oscillations in the strain output by scri\ntowards the beginning of the waveform (up to ~1500M). This is caused by CCE junk\nradiation from imperfect initial data on the null hypersurface. In order to use\nthis waveform in analysis, the CCE junk must be cut off from the beginning.\n\nYou are also able to see gravitational memory effects with SpECTRE CCE! This\nshows up in the real part of the 2,0 mode of the strain. Though you can see the\nmemory effects in the SpECTRE CCE waveform, in order to do any analysis, you\nmust also transform the waveform to a more physically motivated BMS frame.\n\n\\image html re_h20.png \"Real part of 2,0 component of the strain\n\n## Citing CCE\n\nIf you use the SpECTRE CCE module to extract your waveforms at future null\ninfinity, please cite the following:\n\n- [SpECTRE DOI](https://zenodo.org/doi/10.5281/zenodo.4290404) (This link\n  defaults to the latest release of SpECTRE. From there, you can find links to\n  past releases of SpECTRE as well)\n- [SpECTRE CCE paper](https://doi.org/10.1103/PhysRevD.107.064013)\n- [CCE paper](https://doi.org/10.1103/PhysRevD.102.044052)\n\nIf you used scri to perform any frame-fixing, please also consult the\n[scri GitHub](https://github.com/moble/scri) for how you should cite it.\n\nYou can also consult our \\ref publication_policies page for further questions or\ncontact [spectre-devel@black-holes.org](mailto:spectre-devel@black-holes.org).\n"
  },
  {
    "path": "docs/Tutorials/CheckpointRestart.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# %Setting up checkpoints and restarts {#tutorial_checkpoint_restart}\n\n\\tableofcontents\n\nSpECTRE executables can write checkpoints that save their instantaneous state to\ndisc; the execution can be restarted later from a saved checkpoint. This feature\nis useful for expensive simulations that would run longer than the wallclock\nlimits on a supercomputer system.\n\nExecutables can checkpoint when:\n1. The `default_phase_order` member variable in the `Metavariables` includes a\n   `WriteCheckpoint` phase.\n2. The `WriteCheckpoint` phase is run by a `PhaseControl` specified in the\n   `Metavariables` and the input file. The two supported ways of running the\n   checkpoint phase are:\n   - with `CheckpointAndExitAfterWallclock`. This is the recommended phase\n     control for checkpointing, because it writes only one checkpoint before\n     cleanly terminating the code.\n     This reduces the disc space taken up by checkpoint files and stops using\n     up the allocation's CPU-hours on work that would be redone anyway after the\n     run is restarted.\n     The executable will return exit code 2 when it terminates from\n     `CheckpointAndExitAfterWallclock`, meaning it is incomplete and should\n     continue from the checkpoint. See `Parallel::ExitCode` for a definition of\n     all exit code.\n   - using `VisitAndReturn(WriteCheckpoint)`. This is useful for writing more\n     frequent checkpoint files, which could help when debugging a run by\n     restarting it from just before the failure.\n\nTo restart an executable from a checkpoint file, run a command like this:\n```\n./MySpectreExecutable +restart Checkpoints/Checkpoint_0123\n```\nwhere the `0123` should be the number of the checkpoint to restart from. You can\nalso use the \\ref tutorial_cli \"command-line interface (CLI)\" for restarting:\n```\n./spectre run INPUT_FILE --from-last-checkpoint Checkpoints/\n```\n\nThere are a number of caveats in the current implementation of checkpointing\nand restarting:\n\n1. The same binary must be used when writing the checkpoint and when restarting\n   from the checkpoint. If a different binary is used to restart the code,\n   there are no guarantees that the code will restart or that the continued\n   execution will be correct.\n2. The code must be restarted on the same hardware configuration used when\n   writing the checkpoint --- this means the same number of nodes with the same\n   number of processors per node.\n3. When using `CheckpointAndExitAfterWallclock` to trigger checkpoints, note\n   that the elapsed wallclock time is checked only when the `PhaseControl` is\n   run, i.e., at global synchronization points defined in the input file.\n   This means that to write a checkpoint in the 30 minutes before the end of a\n   job's queue time, the triggers in the input file must trigger global\n   synchronizations at least once every 30 minutes (and probably 2-3 times so\n   there is a margin for the time to write files to disc, etc). It is currently\n   up to the user to find the balance between too-frequent synchronizations\n   (that slow the code) and too-infrequent synchronizations (that won't allow\n   checkpoints to be written).\n\nCertain simulation parameters can be modified when restarting from a checkpoint\nfile. This is done by parsing a new input file containing just those options to\nmodify; all other options will preserve their value from the original run.\n\nNote, however, that not all tags are permitted to be modified: in the current\nimplementation, only tags from the `const_global_cache_tags` that also have a\nmember variable `static constexpr bool is_overlayable = true;` can be modified.\nThe reason for this \"opt-in\" design is that in general, most tags interact with\npast or current simulation data in a way that would invalidate the simulation\nstate if the tag were modified on restart (example: changing the domain\ninvalidates all spatial data, changing the timestepper invalidates the history).\nOnly tags that do not interact with the state should be permitted to be updated.\nFor example: activation thresholds on various algorithms, or frequency of data\nobservation, are safe parameters to modify.\n\nThe executable will update the global cache with new input file values during\nthe phase `UpdateOptionsAtRestartFromCheckpoint`. The\n`CheckpointAndExitAfterWallclock` phase control automatically directs code flow\nto this phase after a restart.\n\nIn this option-updating phase, the code tries to read an \"overlay\" input file\nwhose name is computed from the original input file and the number of the\ncheckpoint used to restart. Say the original input file is `path/to/Input.yaml`\nand the code is restarted using a checkpoint\n`+restart Checkpoints/Checkpoint_0123`, then the overlay input file to read\nhas name `path/to/Input.overlay_0123.yaml`. If this file does not exist, the\nexecutable continues with previous parameter values.\n"
  },
  {
    "path": "docs/Tutorials/Cli.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Command-line interface (CLI) {#tutorial_cli}\n\n\\tableofcontents\n\nSpECTRE has a command-line interface (CLI) that allows you to run executables,\nwork with output data, and generate plots and visualizations. To get started,\ncompile the `cli` target in your build directory. Then run:\n\n```sh\n./bin/spectre --help\n```\n\nAll available commands are listed in the [Python documentation](py/cli.html).\n\n## Autocompletion\n\nThe CLI supports autocompletion for Bash, Zsh, and Fish. Depending on your\nshell, source the corresponding file:\n\n```sh\n# In the build directory:\n# - Bash:\n. ./bin/python/shell-completion.bash\n# - Fish:\ncp ./bin/python/shell-completion.fish ~/.config/fish/completions/spectre.fish\n# - Zsh:\n. ./bin/python/shell-completion.zsh\n```\n\nYou may want to source the shell completion script on startup, e.g., in your\n`.bashrc` or `.zshrc` file.\n"
  },
  {
    "path": "docs/Tutorials/DomainCreation.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# %Domain Creation {#tutorial_domain_creation}\n\n\\tableofcontents\n\n### Domain Decomposition\nThe spatial domain on which the solution is computed is divided into\nnon-overlapping cells. For 2D and 3D domains, we use curvilinear\nquadrilateral and curvilinear hexahedral elements, respectively. The\nspecification of a particular spatial domain is given by the DomainCreator.\nDomainCreators specify the properties of the initial elements (before AMR takes\nplace), which are then used by Domain to create Blocks. Each Block\nholds a CoordinateMap (described below) as well as information about its\nneighbors. For geometrical domains, (e.g. rectilinear, spherical) there are a\nfew shortcuts that can be used such that a user does not need to specify by\nhand the map and neighbor information for each %Block; these methods are\nexplained below.\n\n## CoordinateMaps\nEach Block in the Domain must hold a CoordinateMap which describes how to\nmap the logical cube (a reference cell that covers the interval \\f$[-1, 1]\\f$\nin each dimension) to the curvilinear hexahedral element the %Block describes.\nThe %CoordinateMap also provides the jacobian of the mapping.\n\n### Shortcuts for CoordinateMaps\nFor spherical domains, `Wedge<3>` implements the cubed-sphere map, and there\nexists the method `sph_wedge_coordinate_maps` in Domain/DomainHelpers.hpp to\nquickly construct six of these maps at once. For rectilinear multicube domains,\nAffine and Equiangular map the logical cubes to cartesian cubes. The method\n`maps_for_rectilinear_domains` in DomainHelpers allows the user to obtain\nthe CoordinateMaps for all of the Blocks in such a domain at once. These\ncan both be used to provide the map arguments to the Domain constructor.\n\n## Boundary Information\nEach Block must know which of the other Blocks in the Domain are its\nneighbors and which of its logical directions points to an external boundary.\n\n### Shortcuts for Boundary Information\nA quick way to encode the neighbor and boundary information for the blocks\nin a domain is through our convention of numbering and ordering the corners\nof the blocks in a well-defined way. The corner numbering scheme is described\nin the \\ref tutorial_orientations tutorial. For spherical domains,\nDomainHelpers has the methods `corners_for_radially_layered_domains` and\n`corners_for_biradially_layered_domains`, which provides the proper corner\nnumberings for the maps obtained from the method `sph_wedge_coordinate_maps`.\nThese methods are used in the\n\\ref domain::creators::Sphere \"Sphere\" DomainCreator.\nFor rectilinear multicube domains, DomainHelpers has the methods\n`corners_for_rectilinear_domains`, which provides the proper corner\nnumberings for the maps obtained from the method\n`maps_for_rectilinear_domains`.\n\n## Creating Rectilinear Domains with Shortcuts:\nThe construction of a rectilinear domain begins with the specification of\nthe total extents of the domain in each cartesian direction, in terms of\nthe number of blocks. These extents are held in an Index object.\nFor an illustrative example, we will explain how to construct a cubical\ndomain which has an extent of two blocks in each dimension:\n\n\\image html eightcubes.png \"A 2x2x2 domain.\"\n\nThe first step is to generate the corner numbering for this Domain. For\nthe purposes of this example, we will construct all blocks with their\nlogical directions aligned with one another. As each block must also have\nan associated block id, we must be aware of the order in which the corners\nfor each block are constructed.\n\nThe algorithm `corners_for_rectilinear_domains` always begins with the block\nlocated in the lowest cartesian corner of the domain. The second block is\nits immediate neighbor in the \\f$+x\\f$ direction, and so on until the block\nin this row with the highest \\f$x\\f$ coordinate is reached. The next block is\nthe the immediate neighbor of the lowest corner block in the \\f$+y\\f$\ndirection, and then continues through the neighboring blocks in the \\f$+x\\f$ as\nbefore. This is the same order in which the global corners numbers are assigned\nto the vertices of the blocks.\n\n\\image html eightcubes_numbered.png \"With global corners.\"\n\nThe block corners generated by `corners_for_rectilinear_domains` are then:\n\n```\n{0,1,3,4,9,10,12,13},\n{1,2,4,5,10,11,13,14},\n{3,4,6,7,12,13,15,16},\n{4,5,7,8,13,14,16,17},\n{9,10,12,13,18,19,21,22},\n{10,11,13,14,19,20,22,23},\n{12,13,15,16,21,22,24,25},\n{13,14,16,17,22,23,25,26}\n\n```\n\nWhat remains is to specify the CoordinateMaps that each Block will hold.\nThis is handled by `maps_for_rectilinear_domains` and currently supports\nboth Affine and Equiangular mappings. The coordinate extents of each map\nis set by the argument to `block_demarcations`. For a 2x2x1 domain, the\ncall to `maps_for_rectilinear_domains` could be:\n\n\\snippet Test_DomainHelpers.cpp show_maps_for_rectilinear_domains\n\nFor this choice of arguments we obtain the maps for a domain that extends\nfrom 0.0 to 2.0 in the \\f$x\\f$-direction, from 0.0 to 2.0 in the \\f$y\\f$\n-direction, and from -0.4 to 0.3 in the \\f$z\\f$ direction.\n\nWith the corners and maps in hand, we can pass these as arguments to the\nDomain constructor.\n\n### Non-trivial Domain Example\nThe aforementioned functions can also take an additional argument to exclude\nblocks from a domain. For this one needs to know the Index<Dim> for each block\nthey wish to exclude from the domain. For the 2x2x2 domain example, the block\nindices are:\n\n```\n{0,0,0},\n{1,0,0},\n{0,1,0},\n{1,1,0},\n{0,0,1},\n{1,0,1},\n{0,1,1},\n{1,1,1}\n\n```\n\nIn this example, we construct a domain with topology \\f$S^3\\f$, which begins\nwith the net for a tesseract. We begin by specifying that the domain extents\nare three blocks along the \\f$x\\f$ and \\f$y\\f$ directions, and four blocks\nalong the \\f$z\\f$ direction. In addition, we exclude the following block\nindices:\n\n```\n{0,0,0}, //first block\n{1,0,0}, //second block\n{2,0,0}, //third block\n{0,1,0}, //fourth block\n{2,1,0}, //sixth block\n{0,2,0}, //seventh block\n{1,2,0}, //eight block\n{2,2,0}, //ninth block\n\n{0,0,1}, //10th block\n{1,0,1}, //11th block\n{2,0,1}, //12th block\n{0,1,1}, //13th block\n{2,1,1}, //15th block\n{0,2,1}, //16th block\n{1,2,1}, //17th block\n{2,2,1}, //18th block\n\n{0,0,2}, //19th block\n{2,0,2}, //21st block\n{0,2,2}, //25th block\n{2,2,2}, //27th block\n\n{0,0,2}, //28rd block\n{1,0,2}, //29th block\n{2,0,2}, //30th block\n{0,1,2}, //31th block\n{2,1,2}, //33th block\n{0,2,2}, //34th block\n{1,2,2}, //35th block\n{2,2,2}, //36th block\n\n```\nAlternatively, we can exclude none of the blocks in this way and instead\nselectively copy the block corners from the returned vector into a new vector\nthat only contains the corners for the desired blocks, if one prefers to work\nwith single-number array indices as opposed to tuples.\n\nEither way, we end up with a vector of block corners corresponding to the\nfollowing diagram:\n\n\\image html tesseract_numbered.png \"A numbered tesseract net.\"\n\nThe maps are obtained similarly. In this case we suggest using the Equiangular\nmaps for this Domain since they adapt the logical coordinates to the angular\ncoordinates on a sphere. We also need to identify corresponding faces with each\nother to obtain a domain of topology \\f$S^3\\f$, so we also need to supply the\nconstructor of Domain with PairsOfFaces. For the \\f$S^3\\f$ domain, these faces\nare:\n\n```\n\n//Folding highest cube downward:\n{{53,54,69,70},{53,54,49,50}},\n{{53,57,69,73},{53,57,52,56}},\n{{57,58,61,62},{57,58,73,74}},\n{{54,58,70,74},{54,58,55,59}},\n\n//Folding cross cubes together:\n{{54,55,38,39},{54,50,38,34}},\n{{58,59,42,43},{58,62,42,46}},\n{{53,52,37,36},{53,49,37,33}},\n{{57,56,41,40},{57,61,41,45}},\n\n//Folding second lowest cube upward:\n{{38,42,39,43},{38,42,22,26}},\n{{33,34,37,38},{21,22,37,38}},\n{{36,37,40,41},{21,37,25,41}},\n{{41,42,45,46},{41,42,25,26}},\n\n//Folding bottom cube around domain:\n{{33,34,49,50},{21,22,5,6}},\n{{39,43,55,59},{22,26,6,10}},\n{{45,46,61,62},{25,26,9,10}},\n{{36,40,52,56},{21,25,5,9}},\n{{5,6,9,10},{69,70,73,74}}\n\n```\n"
  },
  {
    "path": "docs/Tutorials/EventsAndTriggers.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n\n# Events and triggers {#tutorial_events_and_triggers}\n\n\\tableofcontents\n\nThe \"events and triggers\" infrastructure (and the related \"events and\ndense triggers\") provide some control of SpECTRE executable execution\nfrom the input file contents by running user-specified code on the\nelements.  It does not generally have major effects on the flow of the\n\\ref dev_guide_parallelization_core_algorithm \"algorithm\", but is\nprimarily used for observations, such as writing volume data to H5\nfiles, and minor simulation adjustments.\n\n### Events\n\nAn \\ref Event \"event\" is an input-file-creatable object that performs\nsome task that (with the exception of the \\ref Events::Completion\n\"Completion\" event) does not directly affect the execution of the\nalgorithm on the element.  The effects of events are limited to\nsending messages.  The most commonly used events (such as the \\ref\n::Events::ObserveNorms \"ObserveNorms\" event) send data to\nbe written to disk, but have no long-term effects on the simulation\nstate.  Others (such as \\ref Events::ChangeSlabSize \"ChangeSlabSize\")\ncan have indirect effects when \\ref dev_guide_parallelization_actions\n\"actions\" react to the messages they send.\n\n### Triggers\n\nA \\ref Trigger \"trigger\" is an input-file-creatable object that\ncontrols when events are run.  They are checked periodically, once per\nSlab in an evolution executable, and once per iteration in an elliptic\nsolve.  At each check a trigger may fire, causing its associated\nevents to run or not.  Triggers must give a consistent result over\nthe entire domain at each check, so their result must always be\nindependent of element-specific state, such as the local values of the\nsystem variables.\n\n### Dense triggers\n\nA \\ref DenseTrigger \"dense trigger\" is similar to a trigger, but uses\ntime-stepper dense output (i.e., interpolation or extrapolation) to\nprovide more precise control of the time that it fires.  As a\ntime-related feature, dense triggers are only applicable to evolution\nexecutables.  Dense triggers fire at a precise set of times,\nindependent of the time-step or slab state, by interpolating the\nevolved variables to the requested time before running the associated\nevents.  As with normal triggers, dense triggers are required to fire\nconsistently over the entire domain.\n\n### Input file syntax\n\nThe `%EventsAndTriggers` (and `%EventsAndDenseTriggers`) sections of\nthe \\ref dev_guide_option_parsing \"input file\" are parsed as a list of\nTrigger/Events pairs:\n\n\\snippet PlaneWave1DEventsAndTriggersExample.yaml multiple_events\n\nIn this example, we are using the \\ref Triggers::Slabs \"Slabs\" trigger\nto run two events every 10 slabs: ObserveFields and\n\\ref ::Events::ObserveNorms \"ObserveNorms\". We also run the\n\\ref ::Events::Completion \"Completion\" event at the\n\\ref ::Triggers::Always \"Always\" trigger, so the run will terminate immediately.\n"
  },
  {
    "path": "docs/Tutorials/Imex.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# IMEX {#tutorial_imex}\n\n\\tableofcontents\n\n### Introduction to IMEX\n\nIMEX (implicit-explicit) integration is a method for stabilizing the numerical\nevolution of stiff differential equations.  While \"stiff\" is not a precisely\ndefined term, it generally refers to equations where the time step required to\nachieve numerical stability is much shorter than the timescale of the evolution\nof the solution, often by orders of magnitude.  An example of a stiff equation\nis\n\n\\f{equation}\n  \\label{eq:example-equation}\n  \\frac{dy}{dt} = -k (y - f(t))\n\\f}\n\nwith $k$ very large.  After a brief initial transient, the solution is very\nwell-approximated as $y = f(t) + O(k^{-1})$, but will be unstable if evolved\nusing an explicit time stepper with a step size larger than approximately\n$k^{-1}$, even if $f$ is slowly varying.\n\nTo see the source of the stability problem, consider evolving\n$\\eqref{eq:example-equation}$ using Euler's method.  For a step from $t_0$ to\n$t_1 = t_0 + \\Delta t$, we find\n\n\\f{equation}\n  \\label{eq:example-Euler}\n  y_1 = y_0 + \\Delta t \\frac{dy}{dt}(y_0, t_0)\n  = y_0 - k \\Delta t (y_0 - f(t_0)),\n\\f}\n\ngiving the divergence from the known infinite-$k$ solution of\n\n\\f{equation}\n  y_1 - f(t_1) = y_0 - f(t_1) - k \\Delta t (y_0 - f(t_0)).\n\\f}\n\nIf $k \\Delta t$, is large, the last term will dominate and the deviation from\nthe approximate solution will increase by roughly that factor every step.\n\nTo improve this, we can evolve using an implicit time stepper.  Taking the same\nstep as above with the backwards Euler method gives\n\n\\f{equation}\n  \\label{eq:example-backwards-Euler}\n  y_1 = y_0 + \\Delta t \\frac{dy}{dt}(y_1, t_1)\n  = y_0 - k \\Delta t (y_1 - f(t_1)),\n\\f}\n\nfor a deviation of\n\n\\f{equation}\n  y_1 - f(t_1) = \\frac{y_0 - f(t_1)}{1 + k \\Delta t}.\n\\f}\n\nThis goes to zero as $k \\Delta t$ becomes large, showing convergence to the\ninfinite-$k$ solution.\n\nThe unconditional (linear) stability achievable by implicit integrators allows\nthem to solve problems not feasible with explicit methods, but it comes at a\nsignificant price.  While the explicit update $\\eqref{eq:example-Euler}$\ndirectly gave $y_1$, the implicit update $\\eqref{eq:example-backwards-Euler}$\nrequired the equation to be solved for the updated quantity.  In this example\nthat was fairly easy, but, in a real application, finding an analytic solution\nis likely to be difficult, if not impossible.  Implicit integrators are\ntherefore usually used with a numerical solve of the update equation.  Even for\nsimple systems, this root-finding procedure adds significantly to the\ncomputational cost of the method, and for a system like a large PDE evolution\nit is intractable.\n\nTo mitigate this problem, we turn to the hybrid IMEX methods.  The idea behind\nIMEX is to split the derivative into two terms, one without stability problems\nthat will be treated explicitly, and another that will be treated implicitly.\nNotationally, we will write\n\n\\f{equation}\n  \\frac{dy}{dt}(y, t) = E(y, t) + I(y, t).\n\\f}\n\nThe simplest IMEX integrator combines the two forms of Euler's method from\nabove:\n\n\\f{equation}\n  y_1 = y_0 + \\Delta t (E(y_0, t_0) + I(y_1, t_1)).\n\\f}\n\n(This integrator is not implemented in SpECTRE.)  In our example above, we can,\nas an example, split $\\eqref{eq:example-equation}$ as\n\n\\f{align}\n  E(y, t) &= k f(t) &\n  I(y, t) &= -k y,\n\\f}\n\nwhich gives\n\n\\f{equation}\n  y_1 = y_0 + k \\Delta t (f(t_0) - y_1)\n  = \\frac{y_0 + k \\Delta t\\, f(t_0)}{1 + k \\Delta t}.\n\\f}\n\nIn the limit where $k \\Delta t$ goes to infinity, this gives $y_1 = f(t_0)$,\nwhich is as small a deviation from the infinite-$k$ solution as possible when\n$f$ is treated explicitly.\n\nAgain, in this simple case there is no downside to using an IMEX integrator\nover an explicit one, or an implicit method over IMEX.  The gain appears in\nreal cases where the implicit-step equation cannot be solved exactly.\n\n#### Properties of IMEX time steppers\n\nAny IMEX time stepper can be used as either an implicit or an explicit time\nstepper by choosing $E$ or $I$ to be zero.  As such, many significant\nproperties of an IMEX time stepper are actually properties of one or the other\npart.  We won't discuss the standard properties of explicit time steppers here,\nbut most of them, such as error estimation, are used in SpECTRE ignoring the\nimplicit part.\n\nAs the entire point of using implicit methods is to increase stability, the\nmost important properties of the implicit part of a time stepper are stability\nproperties.  There are many stability classifications, but we will only discuss\nthe most important here, and those only qualitatively.  More information can be\nfound in \\cite Hairer1996 and \\cite Kennedy2016.\n\nThe basic desire for an implicit method is that as the equation being evolved\nbecomes increasingly stiff the evolution remains stable.  (Linear) stability\nfor an infinitely large step size (or, equivalently, an infinitely stiff\nequation) is known as *A-stability*.  Adams methods above second order cannot\nbe A-stable, so IMEX work generally focuses on Runge-Kutta schemes. All IMEX\ntime steppers implemented in SpECTRE are A-stable.\n\nA stronger stability condition is known as *L-stability*.  L-stability is\nsimilar to A-stability, but instead of merely requiring that an analytically\ndecaying equation does not numerically diverge in the large-step-size limit, we\nrequire that the solution reaches zero in a single step.  Since IMEX\napplications can take steps orders of magnitude larger than the decay timescale\nof stiff terms, this property is often desirable.\n\nIn addition to the stability properties, there is one interesting property of\nthe IMEX stepper as a whole.  A wide class of time steppers, including all\nexplicit (global time-stepping) methods implementable in the current SpECTRE\ninterface, preserve (linear) conserved quantities.  A linear conserved quantity\nis a linear combination of the evolved variables that is analytically constant\nunder evolution, independent of the initial conditions.  In physics, a common\nexample is the integral of the density, i.e., the total mass.  Such quantities\nwill be numerically preserved during evolution to the level of roundoff error,\neven if that is much smaller than the truncation error in the solution.\n\nBoth the explicit and implicit portions of all IMEX time steppers that we will\nconsider are conservative in this manner.  However, the combination of the two\nparts into an IMEX scheme does not necessarily preserve this property.  In\ngeneral, both the explicit and implicit parts of the split derivative must\nindividually conserve something for the full IMEX method to conserve it as\nwell.  Some IMEX schemes, however, do preserve conserved quantities,\nindependent of how the derivative is split between the explicit and implicit\nparts.  Such methods are termed *stiffly accurate*.  (This property is also\nuseful in deriving and analyzing implicit schemes, but that is not of great\nimportance to users of the methods.)\n\n### IMEX in SpECTRE\n\nSpECTRE supports IMEX integration, with restrictions to reduce the cost of the\nsolve of the implicit equation as much as possible.  Additionally, not all\nintegration schemes extend to IMEX integration, so the list of available time\nsteppers is limited for IMEX evolutions.\n\n#### Mathematical restrictions\n\nFor problems of interest to us, the evolved variables consist of several fields\ncoupled by the system derivative.  The implicit solve is more expensive when\nperformed on more variables, so there is a method to restrict the solve to a\nsubset of the system if only some variables are affected by stiff terms.  The\nsystem can define one or more *implicit sectors*, which are subsets of the\nevolved variables with implicit terms that can be solved independently from one\nanother.  (If sectors depend on variables from other sectors, the evolution\nsystem defines the order in which the implicit updates are applied.)\n\nAs an example, for a fluid coupled to neutrinos, the neutrinos obey stiff\nequations under some conditions.  If we consider neutrino flavors to be\nnon-interacting, they can each be placed in their own implicit sector and\nsolved independently.  The fluid equations will usually be non-stiff, so the\nfluid variables will not be in any sector, even though they will still be used\nas arguments for computing the stiff sources.\n\nWithin each sector, restrictions are placed on the form of the implicit\nderivative (the $I$ above).  When evolving a hyperbolic PDE, in general, the\nequation for an implicit step becomes an elliptic PDE.  This is far too complex\nand expensive to solve for every integration substep.  We therefore restrict\nthe implicit derivative to only contain source terms, i.e., terms that depend\non the sector variables only in a pointwise manner, rather than through\nderivatives or similar.  (The terms may still depend on the derivatives of\nnon-sector variables.)  This results in each point in the domain having an\nindependent set of algebraic equations to solve.\n\nThere is a small complexity in handling non-autonomous (time-dependent) systems\nof equations, such as those for evolutions with control systems.  Some methods\nare defined in a way such that the values of time for the explicit and implicit\nparts of a substep appear to be inconsistent.  SpECTRE always uses the time\nderived from the explicit portion of the method, which is equivalent to\ntreating time as an additional evolved variable with a constant explicit\nderivative of 1 and implicit derivative of 0.\n\nSpECTRE supports using an analytic solution of the implicit-step equation as\nwell as two modes for numerical root-finding: implicit and semi-implicit.  The\nanalytic mode can be chosen in the definition of the implicit sector if the\nform of the implicit source allows an analytic solution.  The numerical methods\ncan be toggled at runtime.  The fully implicit method does a numerical\nroot-find, while the semi-implicit one linearizes the equation and solves that.\nSemi-implicit solves are faster, and, experimentally, they still stabilize the\nevolution fairly well.  Using a semi-implicit solve instead of a fully implicit\none does not affect conservation properties of the method.  Both numerical\nsolvers require the jacobian of the implicit source to be coded, but an\nanalytic solution does not.\n\n#### Creation of SpECTRE IMEX executables\n\nThe SpECTRE IMEX interface is defined by two protocols: \\link\nimex::protocols::ImexSystem \\endlink and \\link imex::protocols::ImplicitSector\n\\endlink.  The normal evolution-system interface now only contains the explicit\nportion of the derivative, and the implicit portion is defined by the contents\nof the `implicit_sectors` typelist.  See those protocols for details.\n\nIn order to use an IMEX system, various classes must be explicitly\ninstantiated.  This is done in a separate source file in the system directory.\nAs an example, the instantiation file for the sectors in one of the tests is\n\n\\include tests/Unit/Helpers/Evolution/Imex/DoImplicitStepInstantiate.cpp\n\nIn the executable itself, four sets of changes are necessary.  First, the\ntime-stepper type, usually defined as a type alias near the start of the\nmetavariables, must be changed from `TimeStepper` to `ImexTimeStepper`\n(IMEX-LTS is not currently supported), and an entry must be added to the\n`factory_creation` list:\n\n```\ntmpl::pair<ImexTimeStepper, TimeSteppers::imex_time_steppers>\n```\n\n(The `TimeStepper` line can be removed, although doing so is not necessary.)\n\nSecond, the IMEX actions must be added after the corresponding explicit\nactions:\n\n* In the initialization phase, the `imex::Initialize<system>` mutator must be\n  applied by adding it to the arguments of\n  `Initialization::Actions::InitializeItems`.  It uses objects initialized by\n  `Initialization::TimeStepperHistory`, so must appear after that.\n\n* In the step actions,\n```\nimex::Actions::RecordTimeStepperData<system>\n```\n  must be added after each occurrence of `RecordTimeStepperData<system>`,\n```\nimex::Actions::DoImplicitStep<system>\n```\n  must be added after each occurrence of `UpdateU<system>`, and\n```\nActions::MutateApply<imex::CleanHistory<system>>\n```\n  must be added after each occurrence of `CleanHistory<system>`.\n\nThird, the `imex::ImplicitDenseOutput<system>` dense output postprocessor must\nbe added to the argument of the `evolution::Actions::RunEventsAndDenseTriggers`\naction.  It must appear before any other preprocessors that use the evolved\nvariables.\n\nFinally, header includes must be added for all these things.  The required\nheaders are\n```\n#include \"Evolution/Imex/Actions/DoImplicitStep.hpp\"\n#include \"Evolution/Imex/Actions/RecordTimeStepperData.hpp\"\n#include \"Evolution/Imex/CleanHistory.hpp\"\n#include \"Evolution/Imex/ImplicitDenseOutput.hpp\"\n#include \"Evolution/Imex/Initialize.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Time/TimeSteppers/ImexTimeStepper.hpp\"\n```\n"
  },
  {
    "path": "docs/Tutorials/Orientation.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# OrientationMap {#tutorial_orientations}\n\n\\tableofcontents\n\n### Introduction\n\nThis tutorial only applies to a Domain (or a region of the Domain) that is\nconstructed from Blocks that are logical hypercubes where each Block has at\nmost a single neighboring Block in each direction.\n\nEach element in a domain has a set of internal directions which it uses\nfor computations in its own local coordinate system. These are referred to\nas the logical directions \\f$\\xi\\f$, \\f$\\eta\\f$, and \\f$\\zeta\\f$, where\n\\f$\\xi\\f$ is the first dimension, \\f$\\eta\\f$ is the second dimension, and\n\\f$\\zeta\\f$ is the third dimension. In a\ndomain with multiple Blocks, the logical directions are not necessarily\naligned on the interfaces between two Blocks, as shown in the figure below.\nAs certain operations (e.g. fluxes, limiting) communicate information across\nthe boundaries of adjacent elements, there needs to be a class that takes\ninto account the relative orientations of elements which neighbor each other.\nThis class is OrientationMap.\n\n### %OrientationMaps between %Blocks\nEach Block in a Domain has a set of BlockNeighbors, which each hold an\nOrientationMap. In this scenario, the Block is referred to as the host, and\nthe OrientationMap held by each BlockNeighbors is referred to as \"the\norientation the BlockNeighbors has with respect to the host Block.\" This is\na convention, so we give an example of constructing and assigning the correct\nOrientationMaps:\n\n\\image html twocubes.png \"Two neighboring blocks.\"\n\nIn the image above, we see a domain decomposition into two Blocks, which have\ntheir logical axes rotated relative to one another. With the left block as\nthe host Block, we see that it has a neighbor in the \\f$+\\xi\\f$ direction.\nThe host Block holds a `std::unordered_map` from Directions to BlockNeighbors;\nthe BlockNeighbors itself holds an OrientationMap that determines the mapping\nfrom each logical direction in the host Block to that in the neighboring Block.\nThat is, the OrientationMap takes as input local information (i.e. logical\ndirections in the host's coordinate system) and returns neighbor information\n(i.e. logical directions in the neighbor's coordinate system). An\nOrientationMap is constructed by passing in the block neighbor directions that\ncorrespond to the \\f$+\\xi\\f$, \\f$+\\eta\\f$, \\f$+\\zeta\\f$ directions in the host.\nIn this case, these directions in the host map to the \\f$+\\zeta\\f$,\n\\f$+\\xi\\f$, \\f$+\\eta\\f$ directions in the neighbor, respectively.\nThis BlockNeighbors thus holds the OrientationMap constructed with the list\n(\\f$+\\zeta\\f$, \\f$+\\xi\\f$, \\f$+\\eta\\f$). With the right block as the host\nblock, we see that it has a BlockNeighbors in the \\f$-\\zeta\\f$ direction, and\nthe OrientationMap held by this BlockNeighbors is the one constructed with the\narray (\\f$+\\eta\\f$, \\f$+\\zeta\\f$, \\f$+\\xi\\f$). For convenience, OrientationMap\nhas a method `inverse_map` which returns the OrientationMap that takes as input\nneighbor information and returns local information.\n\nOrientationMaps need to be provided for each BlockNeighbors in each direction\nfor each Block. This quickly becomes too large of a number to determine by\nhand as the number of Blocks and the number of dimensions increases. A remedy\nto this problem is the corner numbering scheme.\n\n### Encoding BlockNeighbors information using Corner Orderings and Numberings\nThe orientation of the \\f${dim}\\f$ logical directions within each element\ndetermines an ordering of the \\f$2^{dim}\\f$ vertices of that element. This is\ncalled the local corner numbering scheme (Local CNS) with respect to that\nelement. We give the ordering of the local corners below for the case of a\nthree-dimensional element:\n\n\\image html onecube_numbered.png \"The local corner numbering.\"\n\n```\nCorner 0 is the location of the lower xi, lower eta, lower zeta corner.\nCorner 1 is the location of the upper xi, lower eta, lower zeta corner.\nCorner 2 is the location of the lower xi, upper eta, lower zeta corner.\nCorner 3 is the location of the upper xi, upper eta, lower zeta corner.\nCorner 4 is the location of the lower xi, lower eta, upper zeta corner.\nCorner 5 is the location of the upper xi, lower eta, upper zeta corner.\nCorner 6 is the location of the lower xi, upper eta, upper zeta corner.\nCorner 7 is the location of the upper xi, upper eta, upper zeta corner.\n```\n\nWhat remains is to endow the domain decomposition with a global corner\nnumbering (Global CNS). We give an example below:\n\n\\image html twocubes_numbered.png \"A global corner numbering.\"\n\nIn the image above, we see that each vertex of the two-block domain has\nbeen assigned a number. Although each block has eight corners, four are\nshared among them, so there are only twelve unique corners in this domain.\nAny numbering may be used in the global corner numbering, so long as the\neach distinct corner is given a single distinct corner number.\n\n\\note This Global CNS assumes that there is no additional identifying of faces\nwith one another for periodic boundary conditions. That is, each element must\nhave \\f$2^{dim}\\f$ distinct corner numbers. If you wish to additionally\nidentify faces of the same block with each other, that must be done in an\nadditional step. This step is explained in the \"Setting Periodic Boundary\nConditions\" section.\n\n### The Ordered Subset of the Global CNS (Subset CNS):\nWith the Global CNS in hand, each Block inherits an ordered subset of Global\nCNS. The ordering in this set is determined by the ordering of the Local CNS,\nand the elements of the set determined by how one assigned the Global CNS to\nthe Domain. For the image above, the Subset CNS corresponding to the left block\nis {0, 1, 3, 4, 6, 7, 9, 10}, while the Subset CNS corresponding to the right\nblock is {1, 4, 7, 10, 2, 5, 8, 11}. This ordering of the Subset CNS encodes\nthe relative orientations between each Block. Subset CNSs need to be provided\nfor each Block in a Domain. It turns out that for very regular domains,\n(i.e. spherical or rectilinear) we can generate the appropriate Subset CNSs.\nAs this is a conceptual tutorial, how to construct these domains in SpECTRE\nis described in the \\ref tutorial_domain_creation tutorial.\n\n### Explanation of the Algorithms in DomainHelpers:\n\nFor illustrative purposes, we will use the following Domain composed of two\nBlocks as described above as an example.\nBecause there are 12 corners in this Domain, we will arbitrarily assign a\nunique id to each corner.\nKnowing the orientation of the logical axes within a block, we construct a\nSubset CNS for each Block.<br>\nHere is one possible result, given some relative orientation between the\nblocks:\n\n```\nBlock1: {0, 1, 3, 4, 6, 7, 9, 10}\nBlock2: {1, 4, 7, 10, 2, 5, 8, 11}\n```\n\nThe values of the ids only serve to identify which corners are unique and which\nare shared. This is determined by the Global CNS. The order of the ids in the\nlist is determined by the Local CNS. We take advantage of the fact that the\narray index of the global corner id is the number of the corner in the local\nCNS.\n\nThe algorithm begins by determining the shared corners between the\nBlocks:\n\n```\nresult: {1, 4, 7, 10}\n```\n\nThe next step is to determine the local ids of these shared global ids:\n\n```\nBlock1 result: {1,3,5,7}\nBlock2 result: {0,1,2,3}\n```\n\nThe next step is to convert these ids into their binary representation.\nFor reference, we give the binary representation for each number 0-7:\n\n```\nCorner 0: 000\nCorner 1: 001\nCorner 2: 010\nCorner 3: 011\nCorner 4: 100\nCorner 5: 101\nCorner 6: 110\nCorner 7: 111\n```\n\nHere 0 and 1 indicate lower and upper in the corresponding axis (zeta,\neta, xi), respectively, and the ordering has been reversed so that the\nrightmost column corresponds to the xi position and the leftmost column\nto the zeta position. Returning to the example at hand, we have:\n\n\n```\nBlock1 result: {001,011,101,111}\nBlock2 result: {000,001,010,011}\n```\n\n Note that we can now read off the shared face relative to each Block\n easily:\n\n```\nBlock1 result: Upper xi (All binary Block1 ids have a 1 in the third position)\nBlock2 result: Lower zeta (All binary Block2 ids have a 0 in the first position)\n```\n\nNow we know that `Direction<3>::%upper_xi()`\nin Block1 corresponds to `Direction<3>::%lower_zeta().%opposite()` in Block2.\n\nThe use of `.%opposite()` is a result of the Directions to a Block face being\nanti-parallel because each Block lies on the opposite side of the shared face.\n<br>\n\nThe remaining two correspondences are given by the alignment of the shared\nface. It is useful to know the following information:<br>\nIn the Local CNS, if an edge lies along the xi direction, if one takes the\ntwo corners making up that edge and takes the difference of their ids, one\n always gets the result \\f$ \\pm 1\\f$. Similarly, if the edge lies in the eta\n direction, the result will be \\f$ \\pm 2\\f$. Finally, if the edge lies in the\n zeta direction, the result will be \\f$ \\pm 4\\f$. We use this information to\n determine the alignment of the shared face:\n\n```\nBlock1: 3-1=2  => This edge is in the +eta direction.\nBlock2: 1-0=1 => This edge is in the +xi direction.\nThen, +eta in Block1 corresponds to +xi in Block2.\n\nBlock1: 5-1=4 => This edge is in the +zeta direction.\nBlock2: 2-0=2 => This edge is in the +eta direction.\nThen, +zeta in Block1 corresponds to +eta in Block2.\n```\n\nThe corresponding directions in each Block have now been deduced.\n\nTo confirm, we can use the other ids as well and arrive at the same result:<br>\n\n```\nBlock1: 7-5=2  => +eta\nBlock2: 3-2=1 => +xi\n\nBlock1: 7-3=4  => +zeta\nBlock2: 3-1=2  => +eta\n```\n\n### Setting Periodic Boundary Conditions\nIt is also possible to identify faces of a Block using the subset CNS. For\nexample, to identify the lower zeta face with the upper zeta face of a Block\nwhere the corners are labeled `{3,0,4,1,9,6,10,7}`, one may supply the lists\n`{3,0,4,1}` and `{9,6,10,7}` to the `set_identified_boundaries` function.\n\\note The `set_identified_boundaries` function is sensitive to the order of the\ncorners in the lists supplied as arguments. This is because the function\nidentifies corners and edges with each other as opposed to simply faces. This\nallows the user to specify more peculiar boundary conditions. For example,\nusing `{3,0,4,1}` and `{6,7,9,10}` to set the periodic boundaries will identify\nthe lower zeta face with the upper zeta face, but after a rotation of a\nquarter-turn.\n\nFor reference, here are the corners to use for each face for a Block with\ncorners labelled as `{0,1,2,3,4,5,6,7}` to set up periodic boundary conditions\nin each dimension, i.e. a \\f$\\mathrm{T}^3\\f$ topology:\n\nFace | Corners\n------|--------\nupper xi| `{1,3,5,7}`\nlower xi| `{0,2,4,6}`\nupper eta| `{2,3,6,7}`\nlower eta| `{0,1,4,5}`\nupper zeta| `{4,5,6,7}`\nlower zeta| `{0,1,2,3}`\n"
  },
  {
    "path": "docs/Tutorials/ParallelExecutable/Tutorials.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Writing SpECTRE executables {#tutorials_parallel}\n\n\\tableofcontents\n\nThis set of tutorials will develop a parallel executable in stages,\nbeginning with the simplest possible executable, and then introducing\nSpECTRE's parallel features one step at a time.  At each stage, the\ntutorials will illustrate what code a user needs to add in order to\nuse a particular feature, and then, for those interested, an\nexplanation of the metaprogramming that turns the user-provided code\ninto an actual SpECTRE parallel executable.\n\nPrior to going through these tutorials, you should have \\ref\ninstallation \"installed SpECTRE\" and built it successfully.\n\n| Tutorial name | Concepts introduced |\n|---------------|---------------------|\n| \\subpage tutorial_parallel_concepts \"Parallelism in SpECTRE \" | SpECTRE and Charm++ |\n| \\subpage tutorial_minimal_parallel_executable \"Minimal executable \" | Metavariables, Main component |\n\nSee also: \\subpage ParallelInfoExecutablePage\nSee also: \\subpage RandomAmrExecutablePage\n"
  },
  {
    "path": "docs/Tutorials/Python.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Using SpECTRE's Python modules {#spectre_using_python}\n\n\\tableofcontents\n\nSome classes and functions from SpECTRE have Python bindings to make it easier\nto visualize data, write test code, and provide an introduction to numerical\nrelativity without needing to delve into C++.\n\n## Building the SpECTRE Python modules\n\n> tl;dr: Compile the `all-pybindings` or `cli` target.\n\nFirst, build SpECTRE with Python bindings enabled by appending the\n`-D BUILD_PYTHON_BINDINGS=ON` flag to the `cmake` command (enabled by default).\nYou can specify the\nPython version, interpreter and libraries used for compiling and testing the\nbindings by setting the `-D Python_EXECUTABLE` to an absolute path such as\n`/usr/bin/python3`. Compile the `all-pybindings` (or `cli`) target. You will\nfind that a `BUILD_DIR/bin/python` directory is created that contains the\nPython modules.\n\n## Importing the SpECTRE Python modules\n\n> tl;dr: Spin up a Jupyter notebook with `./bin/python-spectre -m jupyterlab`,\n> then `import spectre`.\n\nYou have many options for making the Python modules accessible to import in your\nscripts:\n\n- Use the `BUILD_DIR/bin/python-spectre` shortcut to run your scripts. It just\n  points the `PYTHONPATH` to the modules in `BUILD_DIR/bin/python` and invokes\n  the Python interpreter that you configured your build with. You can also use\n  this shortcut to spin up a Jupyter server:\n\n  ```sh\n  BUILD_DIR/bin/python-spectre -m jupyterlab\n  ```\n\n  If Jupyter is not yet installed in your Python environment, you can install it\n  like this:\n\n  ```sh\n  BUILD_DIR/bin/python-spectre -m pip install jupyterlab\n  ```\n\n  > Note for VSCode users: You can also select this Jupyter server as kernel for\n  > notebooks running in VSCode (see [docs](https://code.visualstudio.com/docs/datascience/jupyter-notebooks#_connect-to-a-remote-jupyter-server)).\n\n- Install the modules into your Python environment:\n\n  ```sh\n  pip install [-e] BUILD_DIR/bin/python\n  ```\n\n  The optional `-e` flag installs the modules in _development mode_, which means\n  they are symlinked so that changes to the modules will be reflected in your\n  Python environment.\n\n  You can install the Python modules like this in any Python environment that\n  supports `pip`, for instance in a\n  [`virtualenv`/`venv`](https://docs.python.org/3/tutorial/venv.html) or in an\n  [Anaconda](https://www.anaconda.com/distribution/) environment.\n\n- You can also get access to the SpECTRE Python modules by manually adding\n  `BUILD_DIR/bin/python` to your `PYTHONPATH`. This is done automatically by\n  the `LoadPython.sh` script:\n\n  ```sh\n  . BUILD_DIR/bin/LoadPython.sh\n  ```\n\nUsing any of the above options you should be able to import the `spectre` Python\nmodules in your scripts or notebooks. You can try it like this:\n\n```py\nimport spectre\n```\n\nFor an overview of all available Python modules see the\n[Python modules documentation](py/_autosummary/spectre.html). For an example\nwhat you can do with them see the tutorial on \\ref tutorial_vis_python.\n\n## Running Jupyter within the Docker container\n\nYou can run [Jupyter lab](https://jupyterlab.readthedocs.io/) in the Docker\ncontainer and access it through a browser on\nthe host for a convenient way to work with the SpECTRE Python bindings. To do\nso, make sure you have exposed a port when running the Docker container, e.g.\nby appending the option `-p 8000:8000` to the `docker run` command (see\n\\ref installation). Inside the docker container, it can be convenient to\nuse `disown` or to `apt-get install screen` and use `screen` to obtain a shell\nthat runs the Jupyter server permanently in the background. Within the shell you\nwant to run your Jupyter server, navigate to a directory that will serve as the\nroot for the file system that Jupyter has access to. Make sure it is shared with\nthe host (e.g. `SPECTRE_HOME`) so your Jupyter notebooks are not lost when the\ncontainer is deleted. Then, run the following command:\n\n```sh\nBUILD_DIR/bin/python-spectre -m jupyterlab --ip 0.0.0.0 --port 8000 --allow-root\n```\n\nCopy the token that is being displayed. Now you can open a browser on the host\nmachine, point it to `http://localhost:8000` and paste in the token. You will\nhave access to the Python environment within the Docker container.\n\n## Developing Python code\n\n> tl;dr: Set `PY_DEV_MODE=ON` in your CMake configuration so Python files are\n> symlinked to the build directory.\n\nWhen you edit any of the Python files in the repository, the changes are not\nimmediately reflected in the Python modules that you import from the build\ndirectory. You have to run `cmake .` in the build directory to update the Python\nfiles. This can be avoided by setting `cmake -D PY_DEV_MODE=ON .` in the build\ndirectory, which configures CMake to symlink the Python files rather than copy\nthem. Some features that rely on CMake variables break with this mode, e.g. the\nSpECTRE version number is not inserted into the Python modules so\n`spectre.__version__` will return nonsense. Nonetheless, enabling this mode\navoids a lot of the hassle that comes from re-configuring CMake every time you\nchange a file.\n\nFurthermore, enabling `autoreload` in Jupyter notebooks can be very helpful when\nyou are editing Python code. Add the following code before any import statements\nin your notebook:\n\n```\n%load_ext autoreload\n%autoreload 2\n```\n\nFor details on writing Python bindings for our C++ functions, rather than\nwriting Python code, see the developer guide on\n\\ref spectre_writing_python_bindings.\n"
  },
  {
    "path": "docs/Tutorials/Tutorials.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# User Tutorials {#tutorials}\n\n- \\subpage beginners_guide - A brief intro to getting started with SpECTRE,\n  building and rebuilding an executable with new features, and visualizing the\n  output\n- \\subpage tutorial_visualization - How to run an existing executable and\n  visualize its output\n- \\subpage tutorial_orientations - How to determine orientations\n- \\subpage tutorial_domain_creation - How to set up the initial domain\n- \\subpage spectre_using_python - How to install and work with the Python\n  bindings and other Python modules provided by SpECTRE.\n- \\subpage tutorial_vis_python - How to visualize volume data with Python\n- \\subpage tutorial_cli - How to use the command-line interface\n- \\subpage tutorial_cce - How to get started running stand-alone CCE with\n  externally produced data\n- \\subpage tutorial_checkpoint_restart - How to set up a SpECTRE executable for\n  checkpointing and restarting\n- \\subpage tutorial_events_and_triggers - How to specify code to run in an input\n  file\n- \\subpage tutorial_imex - How to add IMEX support to an evolution system\n"
  },
  {
    "path": "docs/Tutorials/Visualization.md",
    "content": "\\cond NEVER\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n\\endcond\n# Running and Visualizing {#tutorial_visualization}\n\n\\tableofcontents\n\n### Building an Executable from an Existing Source File\n\nSpECTRE source files for evolution executables are located in\n`src/Evolution/Executables`. Executables can be compiled by running the command\n`make EXECUTABLE_NAME` where `EXECUTABLE_NAME` is the name of the executable\nas defined in the `CMakeLists.txt` file located in the same directory as the\nsource file. For example, to compile the executable that evolves a scalar wave\nusing a three-dimensional domain, navigate to `$SPECTRE_HOME/build/`, then\none runs the command: `make EvolveScalarWave3D`, which then results\nin an executable of the same name in the `bin` directory of the user's\nbuild directory.\n\n### Running an Evolution Executable\n\nEach SpECTRE executable reads a user-provided YAML file that specifies the\nruntime settings of the evolution. This is where options such as the Domain,\nAnalyticSolution, TimeStepper, etc are supplied. Example input files are kept\nin `tests/InputFiles`, and are written to provide a functional base input file\nwhich the user can then modify as desired. Copy the executable and YAML file\nto a directory of your choice. The YAML file is then passed as an argument to\nthe executable using the flag `--input-file`. For example, for a scalar wave\nevolution, run the command:\n\n```\n./EvolveScalarWave3D --input-file PlaneWave3D.yaml\n```\n\nYou can also use the \\ref tutorial_cli \"command-line interface (CLI)\" to run\nexecutables:\n\n```\n./spectre run PlaneWave3D.yaml\n```\n\nBy default, the example input files do not produce any output. This can be\nchanged by modifying the options passed to `EventsAndTriggers` or\n`EventsAndDenseTriggers`:\n\n\\snippet PlaneWave1DObserveExample.yaml observe_event_trigger\n\nThis will observe the norms of the errors in the system at times\n\\f$0.0\\f$ and \\f$1.0\\f$ and the field data at the start of every 50th\nslab.  A successful observation will result in the creation of H5\nfiles whose names can be specified in the YAML file under the options\n`VolumeFileName` and `ReductionFileName`. One volume data file will be\nproduced from each Charm++ node that is used to run the\nexecutable. Each volume data file will have its corresponding node\nnumber appended to its file name.  Visualization of the volume data\nwill be described in the next sections.\n\n### Plotting data with Python\n\nSee the tutorial on \\ref tutorial_vis_python to learn how to load and visualize\nvolume data in Python.\n\n### 3D Volume Data In ParaView\n\nA SpECTRE executable with observers produces volume and/or reduced data h5\nfiles. An XDMF file must be created from the volume data in order to do\nvisualization using ParaView. To this end we provide the tool\n`generate-xdmf` in the Python command-line interface. Run it in your build\ndirectory as `spectre generate-xdmf`. It\ntakes two required arguments which are passed to `--subfile-name`, and\n`--output`. The\nargument passed to `--subfile-name` is the name of the subfile inside the H5\nvolume data file where data is stored. In the above example, `--subfile-name`\nwould be `VolumePsiPiPhiEvery50Slabs`. The argument passed to `--output` is the\ndesired .xmf file name, also without filename extension. Use `--help` to see a\nfurther description of possible arguments.\n\nOpen the .xmf file in ParaView and select the `Xdmf Reader`, *not* the version 3\nreaders. On the left hand side of the main ParaView window is a section named\n`Properties`, here you must click the highlighted `Apply` button. ParaView will\nnow render your volume data. If you only wish to visualize a few datasets out of\na large set, we recommended unchecking the boxes for the datasets you wish to\nignore under `Point Arrays` before clicking `Apply`. Also, don't forget to check\nwhich dataset is being used for the color under `Coloring`. The default won't\nnecessarily be the dataset you want.\n\n\\note ParaView cannot understand `.xmf` files when subfile names have colons in\nthem. Please avoid subfile names like `My::Subfile:Name`.\n\n### Helpful ParaView Filters\n\nHere we describe the usage of filters we've found to better visualize our data.\nFeel free to contribute to this section!\n\n#### Removing Mesh Imprinting\nYou may notice what appears to be mesh imprinting on the data. The imprinting\neffect can be removed by applying the `Tetrahedralize` filter. To apply the\nfilter select the `Filters` menu item, then `Alphabetical` and finally\n`Tetrahedralize`.\n\n#### Creating Derived Volume Data\nNew volume data can be created from the existing volume data using the\n`Calculator` filter. In the `Calculator`'s text box, input a numerical\nexpression in terms of existing datasets evaluating to the desired\nquantity. For example, a vector-valued velocity dataset can be created\nfrom three scalar velocity component datasets using the expression\n`velocity_x * iHat + velocity_y * jHat + velocity_z * kHat` and hitting\nthe `Apply` button. By default, this will create a new dataset `Result`.\nThe name of the new dataset can be changed by changing the name provided\nto `Result Array Name` above the `Calculator` text box.\n\n#### Visualizing Vector Fields Derived From Scalar Fields\nUse the `Calculator` filter described above in \"Creating Derived Volume Data\"\nto create a new vector-valued dataset. Once this is created, use the `Glyph`\nfilter and set the `Active Attributes` to the vector you wish to visualize.\nMake sure that `Scale Mode` is set to `vector`.\n\n### Visualizing and Extracting Dat Files\n\nGlobal quantities such as error norms are stored in `h5::Dat` subfiles in the\nreduction/global HDF5 file. The command-line endpoint `spectre plot dat`\ncan be used to plot quantities, and `spectre extract-dat` can be used\nto extract them into space-separated text files. These text\nfiles can then be read with other plotting programs or viewed in an editor.\n\n### Reproducibility of Results\n\nBeing able to reproduce the results of simulations is important for scientific\nintegrity and accuracy. To this end, SpECTRE encodes the source tree,\nenvironment variables, and a record of build information (e.g., the third-party\nlibrary versions) into the executables. From most executables these can be\nretrieved using the flags `--dump-source-as filename_without_extension`,\n`--dump-paths`, `--dump-build-info`, and `--dump-environment`. The flag\n`--dump-only` will cause the program to exit after the information has been\ndumped. You can use the `--help` flag to get more details on what flags are\navailable. The flag `--dump-paths` will print to screen the environment\nvariables `PATH`, `CPATH`, `LD_LIBRARY_PATH`, `LIBRARY_PATH`, and\n`CMAKE_PREFIX_PATH` at link time. The flag `--dump-build-info` will print to\nscreen the contents of the `BuildInfo.txt` file generated by CMake. The flag\n`--dump-environment` will print to screen the output of `printenv` at link\ntime. Finally, the flag `--dump-source-as filename_without_extension` will dump\nthe archive `filename_without_extension.tar.gz`, which contains the entire\nsource tree at link time. The combined above information should be enough to\nreproduce simulations results to roundoff error as long as the third party\nlibraries used to build the executable have not changed on the system the\nsimulation was run on.\n\nOften only data from the simulation is retained, not the entire executable. In\norder to make simulations reproducible from only the HDF5 output, any HDF5 files\nwritten with SpECTRE's HDF5 wrappers contain the info about the run, output of\n`printenv` at link time, the contents of `BuildInfo.txt`, and an archive\nof the source tree. These are inside the `header.hdr` group with names\n`SimulationInfo`, `EnvironmentVariablesAtCompilation`, `BuildInfo`, and\n`src.tar.gz`. The source archive can be extracted by running the command\n```\nh5dump -d /src.tar.gz -b LE -o src.tar.gz /path/to/hdf5/file.h5\n```\nYou will need to make sure the argument to `-b` is the same endianness as your\ncomputer. In the above example little endian is used.\n"
  },
  {
    "path": "docs/Tutorials/VisualizationWithPython.ipynb",
    "content": "{\n \"cells\": [\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Visualization with Python {#tutorial_vis_python}\\n\",\n    \"\\n\",\n    \"This tutorial shows you how to load 3D volume data in Python and visualize it\\n\",\n    \"using `matplotlib`.\\n\",\n    \"\\n\",\n    \"First, you need access to the `spectre` Python bindings. A simple way to do this\\n\",\n    \"is to run a Jupyter server from the build directory:\\n\",\n    \"\\n\",\n    \"```sh\\n\",\n    \"./bin/python-spectre -m jupyterlab\\n\",\n    \"```\\n\",\n    \"\\n\",\n    \"You can find detailed instruction in the tutorial on \\\\ref spectre_using_python.\\n\",\n    \"\\n\",\n    \"> Note for VSCode users: You can also select this Jupyter server as kernel for\\n\",\n    \"> notebooks running in VSCode (see [docs](https://code.visualstudio.com/docs/datascience/jupyter-notebooks#_connect-to-a-remote-jupyter-server)).\\n\",\n    \"\\n\",\n    \"Now we can import modules from the `spectre` Python package.\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"# Distributed under the MIT License.\\n\",\n    \"# See LICENSE.txt for details.\\n\",\n    \"\\n\",\n    \"import matplotlib.pyplot as plt\\n\",\n    \"import numpy as np\\n\",\n    \"import os\\n\",\n    \"\\n\",\n    \"plt.style.use(\\\"../Examples/plots.mplstyle\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Select an observation\\n\",\n    \"\\n\",\n    \"First, we open the H5 volume data files from a simulation and get a list of all\\n\",\n    \"available observations and their times:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from spectre.IO.H5 import list_observations, open_volfiles\\n\",\n    \"import glob\\n\",\n    \"\\n\",\n    \"h5files = glob.glob(\\n\",\n    \"    # Point this to your volume data files\\n\",\n    \"    \\\"BbhVolume*.h5\\\"\\n\",\n    \")\\n\",\n    \"subfile_name = \\\"/VolumeData\\\"\\n\",\n    \"obs_ids, obs_times = list_observations(open_volfiles(h5files, subfile_name))\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"We can also list all available variables in the volume data files:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 21,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"text/html\": [\n       \"<pre style=\\\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\\\">ConformalFactor                 ExtrinsicCurvature_xx           ExtrinsicCurvature_yx          \\n\",\n       \"ExtrinsicCurvature_yy           ExtrinsicCurvature_zx           ExtrinsicCurvature_zy          \\n\",\n       \"ExtrinsicCurvature_zz           InertialCoordinates_x           InertialCoordinates_y          \\n\",\n       \"InertialCoordinates_z           Lapse                           RadiallyCompressedCoordinates_x\\n\",\n       \"RadiallyCompressedCoordinates_y RadiallyCompressedCoordinates_z ShiftExcess_x                  \\n\",\n       \"ShiftExcess_y                   ShiftExcess_z                   Shift_x                        \\n\",\n       \"Shift_y                         Shift_z                         SpatialMetric_xx               \\n\",\n       \"SpatialMetric_yx                SpatialMetric_yy                SpatialMetric_zx               \\n\",\n       \"SpatialMetric_zy                SpatialMetric_zz                                               \\n\",\n       \"</pre>\\n\"\n      ],\n      \"text/plain\": [\n       \"ConformalFactor                 ExtrinsicCurvature_xx           ExtrinsicCurvature_yx          \\n\",\n       \"ExtrinsicCurvature_yy           ExtrinsicCurvature_zx           ExtrinsicCurvature_zy          \\n\",\n       \"ExtrinsicCurvature_zz           InertialCoordinates_x           InertialCoordinates_y          \\n\",\n       \"InertialCoordinates_z           Lapse                           RadiallyCompressedCoordinates_x\\n\",\n       \"RadiallyCompressedCoordinates_y RadiallyCompressedCoordinates_z ShiftExcess_x                  \\n\",\n       \"ShiftExcess_y                   ShiftExcess_z                   Shift_x                        \\n\",\n       \"Shift_y                         Shift_z                         SpatialMetric_xx               \\n\",\n       \"SpatialMetric_yx                SpatialMetric_yy                SpatialMetric_zx               \\n\",\n       \"SpatialMetric_zy                SpatialMetric_zz                                               \\n\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"import rich.columns\\n\",\n    \"\\n\",\n    \"for volfile in open_volfiles(h5files, subfile_name, obs_ids[-1]):\\n\",\n    \"    all_vars = volfile.list_tensor_components(obs_ids[-1])\\n\",\n    \"    # Only look into the first file. The other should have the same variables.\\n\",\n    \"    break\\n\",\n    \"\\n\",\n    \"rich.print(rich.columns.Columns(all_vars))\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Interpolate to a slice\\n\",\n    \"\\n\",\n    \"One way of visualizing your 3D volume data in a 2D plot is with a slice. Use\\n\",\n    \"`interpolate_to_points` to interpolate your volume data to any set of\\n\",\n    \"coordinates:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 30,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"from spectre.IO.Exporter import interpolate_to_points\\n\",\n    \"\\n\",\n    \"# Coordinates of a regular grid in the xy plane\\n\",\n    \"x, y = np.meshgrid(np.linspace(-15, 15, 300), np.linspace(-15, 15, 300))\\n\",\n    \"z = np.zeros(x.shape)\\n\",\n    \"\\n\",\n    \"# Interpolation can be slow, so it's useful to cache the result. Remember to\\n\",\n    \"# delete the cache file if you change anything.\\n\",\n    \"cache_file = \\\"interpolated_data.npy\\\"\\n\",\n    \"if os.path.isfile(cache_file):\\n\",\n    \"    lapse = np.load(cache_file)\\n\",\n    \"else:\\n\",\n    \"    lapse = np.array(\\n\",\n    \"        interpolate_to_points(\\n\",\n    \"            h5files,\\n\",\n    \"            subfile_name=subfile_name,\\n\",\n    \"            observation_id=obs_ids[-1],\\n\",\n    \"            tensor_components=[\\\"Lapse\\\"],\\n\",\n    \"            target_points=[x.flatten(), y.flatten(), z.flatten()],\\n\",\n    \"        )\\n\",\n    \"    ).reshape(x.shape)\\n\",\n    \"    np.save(cache_file, lapse)\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Now we can plot the 2D slice with `matplotlib`:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 37,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"name\": \"stderr\",\n     \"output_type\": \"stream\",\n     \"text\": [\n      \"/var/folders/xp/5t2ny359187c4ljckf8sz3m80000gn/T/ipykernel_19858/3375439396.py:2: RuntimeWarning: invalid value encountered in subtract\\n\",\n      \"  plt.contourf(x, y, np.log(1 - lapse))\\n\"\n     ]\n    },\n    {\n     \"data\": {\n      \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAAApAAAAJyCAYAAABzOfDZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAABcSAAAXEgFnn9JSAABJ2ElEQVR4nO3df3RU9Z3/8VdCCJAEiQoIJPJDBKsRlGAQBCu6rdVaW0G726+1FrTdXWplu1pL7THoEbW2a6142u763bqg1P22K3zRr2WLWqkuyg8FRCxFwIj8EjVkEUhCEn7M9w92MD9mkrkz98fn87nPxzmeIxPIfDK5ufeZ970zk5dIJBICAAAAMpQf9QIAAABgFwISAAAAnhCQAAAA8ISABAAAgCcEJAAAADwhIAEAAOAJAQkAAABPCEgAAAB4QkACAADAEwISAAAAnhCQAAAA8ISABAAAgCcEJAAAADwhIAEAAOCJUQG5du1aPfjgg5o6darKy8uVl5envLy8tH//nnvuOfF3Uv33wx/+MMTVAwAAxENB1Atobc6cOXr22Wc9/7uJEyfqzDPP7HD72LFj/VgWAAAAWjEqICdMmKDRo0erqqpKVVVVGjp0qJqbm7v8d9/61rc0bdq04BcIAAAAswJy1qxZUS8BAAAAXTDqGkgAAACYz6gJZLaWLVum9evXq6mpSeXl5bryyiu5/hEAACAgTgTkggUL2vy5urpa1157rebPn6+SkpKMP09FRUXK2zdv3qxevXpp8ODBOa0TAADAbzt27FBxcbE+/PDD0O7T6oA888wz9dBDD+nKK6/UkCFDtG/fPv3Xf/2XfvCDH2jRokU6evSoFi9enPP9JBIJNdY3asfmPT6s2myJQqs3CQBwRl7LkaiXAEs0Hm0M/T7zEolEIvR7zVDPnj3V3Nwsr0vcs2ePRo0apbq6Oq1cuVLjx4/PaR0VFRXasXmPJp36Nzl9HpMdGV4W9RIAAGkU1OyOegkw2Kt1v9PgswZq48aNod2nk0+iGThwoKZPny5JWrp0acSrMduR4WXEIwAYLrmvZn8NUzh7vnLEiBGSjk8j0RE7IQCwU+v9N5NJRMXZgNy3b58kqbi4OOKVmIVwBAB3EJOIipMBmUgkTjx5prKyMuLVmIFwBAC3EZMIk7XXQNbW1uqXv/ylDh482Ob2+vp6zZgxQ6tXr9aAAQM0derUiFZoBq6ZAYD4Yd+PoBk1gVyyZInmzJlz4s8tLS2S1OZZ1NXV1brqqqvU0NCg7373u/rhD3+oqqoqDRw4ULW1tVq3bp3q6upUWlqqhQsXqqioKPSvwwTsOAAATCURFKMCsra2VqtXr+5we+vbamtrJUmnnnqqZs2apVWrVmnLli1asWKFunXrpmHDhmnatGn6x3/8R5WVxS+iCEcAQCrEJPxk9OtAmsKG14EkHAEA2SAm7RfF60AaNYGEd4QjACAXyeMIIQkvCEhLEY4AAD9xihteEJCWIRwBAEFjKomuEJCWIBwBAGFjKol0CEjDEY4AABMwlURrBKShCEcAgImYSkKy+J1oXEY8AgBswDvexBcTSIPwQwgAsBGnt+OHgDQA4QgAcAGnt+ODU9gRIx4BAC7i9LbbmEBGhB8qAEAccHrbTQRkyAhHAEAccXrbLZzCDhHxCAAAp7ddwAQyBPyQAADQEae37cUEMmDEIwAAnWMiaR8mkAHhBwEAAG+YSNqDCWQAiEcAALLHRNJ8TCB9xMYOAIB/mEiaiwmkT4hHAACCwUTSPEwgc8QGDQBAOJhImoMJZA6IRwAAwsdEMnpMILPARgsAQPSYSEaHCaRHxCMAAGZhIhk+AjJDicICNk4AAAxGSIaHgAQAAE4hJINHQAIAACcRksEhIAEAgNMISf8RkAAAIBYISf8QkAAAIFYIydwRkAAAIJaIyOwRkAAAILaYRmaHgAQAALFHSHpDQAIAAPwPQjIzBCQAAEA7RGTnCEgAAIAUmEamR0ACAAB0gpDsiIAEAADIABH5KQISAAAgQ0wjjyMgAQAAPIp7SBKQAAAAWYprRBKQAAAAOYjjNJKABAAA8EGcIpKABAAA8ElcppEEJAAAgM9cD8mCqBcAwJuDw3pFvQRPem87FPUSACAyR4aXqaBmd9TL8B0BCUTMtiD0Kpuvj+gE4JLkJNKlkCQggQC5HodB8fK4EZsAbOHSNJKABHxAKEanq8eewARgElemkQQk4AGhaB8CE4CJbJ9GEpBACoRifHT2vSYuAQTJ5mkkAQmIYERqxCWAMNg4jSQgETvEIvxAXALwk20RSUDCeQQjwpZumyMsAXTGplPaBCScRDTCRIQlgEzYMI0kIOEEghE2IywBtGf6NJKAhLWIRrgu1TZOVALxYuo0koCEVYhGxB3TSiB+TIxIAhJGIxiBzDCtBNxm2iltAhJGIhyB3DGtBNxjyjSSgIQxiEYgHEwrAbuZEJEEJCJFNAJmICoBu0R9SpuAROiIRsAORCVgviPDy6S68O+XgERoCEfAfkQlYJ5EYfg5R0AicIQj4DaiEogfAhKBIBqBeCMqAbcRkPAV4QggHaIScAcBCV8QjgCyQVQCdiIgkRPCEYDf2u9XCErAPAQkPCMaAYSJKSVgHgISGSMcAZiCKSUQLQISXSIcAZiOKSUQLgISaRGOAGzGlBIIDgGJlIhHAK5hSgn4h4BEG4QjgDhhSglkh4CEJMIRACSCEshUftQLaG3t2rV68MEHNXXqVJWXlysvL095eXld/rv58+dr3LhxKikp0SmnnKIvfvGLWrFiRQgrtt/BYb2IRwBII7mPZF8JtGXUBHLOnDl69tlnPf2b733ve5o7d6569eqlyy+/XE1NTXrxxRf1wgsvaOHChbrmmmuCWazl2BECgHdMKIHjjArICRMmaPTo0aqqqlJVVZWGDh2q5ubmtH//j3/8o+bOnatTTz1VK1eu1IgRIyRJK1eu1OTJkzV9+nRNnjxZpaWlIX0FdiAeAcAfBCXiyqiAnDVrlqe///DDD0uS7rrrrhPxKB0P0b//+7/Xo48+qscff1y33367r+u0FeEIAMFqvZ8lJuEyo66B9OLQoUNatmyZJOm6667r8PHkbc8991yo6zIR1+4AQPi4fhIuM2oC6cXmzZvV3Nysfv36qby8vMPHKysrJUkbNmwIe2lGYacFAGbgdDdcYm1A7tixQ5JSxqMkFRcXq7S0VPv27dPBgwfVu3fvLj9nRUVFyttrampUkH9S9ouNAOEIAGbjdDdsZm1A1tfXS5KKiorS/p3i4mJ98sknGQekK4hHALAL00nYxtqADMLGjRtT3l5RUaHt22pDXo13hCMAuIHpJExnbUCWlJRIkhobG9P+nYaGBkmKxfSReAQANxGTMJG1ATl48GBJ0q5du1J+vKGhQZ988olOPvlkpwOScASA+CAmYQprX8bnrLPOUo8ePVRbW6vdu3d3+Pi6deskSaNHjw57aaEhHgEgvniJIETJ2oDs1auXLrvsMknS008/3eHjCxculCRdffXVoa4rDOwwAACtEZMIm7UBKUm33XabJOm+++7T1q1bT9y+cuVKPfbYYyotLdXNN98c1fICwc4BANAZYhJhMOoayCVLlmjOnDkn/tzS0iJJGj9+/InbqqurddVVV0mSPve5z+kf/uEfNHfuXJ1//vn6/Oc/r5aWFr344otKJBKaN2+eM++DzY4AAOAV10wiKEYFZG1trVavXt3h9ta31da2fTmdRx55ROeff75+8Ytf6MUXX1RhYaE+97nPqbq6WhdddFHgaw4D8QgAyBUxCT/lJRKJRNSLMF3ydSAnnD8z9PsmHgEAQSIm7bdy/aMaMqxf2tezDoLV10C6jngEAASN6yWRDaNOYeM4fpABAGHjFDe8YAJpGOIRABA1ppLoChNIg/DDCgAwCVNJpMME0hDEIwDAZEwl0RoTyIjxwwgAsAlTSUhMICNFPAIAbMZUMr4IyIjwAwcAcAUhGT+cwo4AP2QAABdxejs+mECGjHgEAMQBU0m3EZAh4gcJABA3hKSbOIUdAn5wAABxlzwWcmrbDUwgA0Y8AgDwKSaSbiAgA8QPCAAAqRGSdiMgA8IPBQAAXSMk7URABoAfBAAAvCEk7cKTaHzGxo/6MnN+LyvZfSzqJQCAJzzZxg4EpI+IR/eZFIeZyGS9RCYAExGSZiMgfUI8usW2UMxFV18rgQkgSoSkmQhIHxCPdotTLGYj1eNDVAIIGyFpFgIyR8SjXYhFf6R7HAlLAEEjJM1AQOaAeDQfwRguppUAwkJIRouAzBLxaC6i0Sztvx8EJQA/EZLRICCzQDyah2i0B0EJIAgHh/UiIkNEQMJKBKM7CEoAfmEaGR4C0iOmj9EiHN1HUALIFSEZPALSA+IxGkRjvBGUALLFae3gEJAZOlZIxISJaEQ6rbcNYhJAV5hGBoOAhFEIR3jBdBJApghJfxGQMALhCD8wnQTQFU5r+4OARGSIRgSJ6SSAdJhG5o6AROgIR0SB6SSA9phGZo+ARGgIR5iCmASQxDQyOwQkAkc4wmTEJACJkPSKgERgCEfYhusmAXBaOzMc4REI4hEuqC/LP/EfgPjgjUO6xgQSvuJAC1dxqhuIF05pd46AhC8IR8QJMQnEB6e0UyMgkRPCEXHHdZOA+5hGdsTRH1kjHoGOuG4ScBfXRn6KCSQ848AIZIbpJOAeppHHUQLwhHgEssd0EnBH3KeRTCCREQ54gL+YTgL2i/MTbAhIdIl4BIJHUAJ2iuspbQISaRGOQHQISsAucZtGEpBIiXgEzJLqZ5KoBMwSp4ikEtAB8QjYgSflAOaJy5NrmEDiBA5CgL3S/fwypQTCF4frIglISCIeAVdx6huIjsuntAlIEI9AzHT2M09cAv5yNSIJyBgjHAG0x6lwwH8uRiQBGVPEIwAvmFoCuXEtIgnIGCIeAfipq30KgQkc51JEEpAxQzwCCBuBCXzKlWdoE5AxQjwCMBGBiTiyfRpJQMYE8QjAVpnsv4hM2MjmiCQgY4B4BOA6ppiwla0RSUA6jngEAJ5FDrPZGJEEpMOIRwDoGnEJE9gWkQSko4hHAMgdcYkw2RSRBKSDiEcACB7vM44g2BKRBKRjiEcAiA5vBQk/2BCRBKRDiEcAMBPTSnhlekQSkI4gHqPRWJ77AaBoF987II7a77cJSrRnckQSkA4gHoPhRxz6dT9EJuA+ppRIxdSIJCAtRzz6I6xYzFa69RGWgNuYUkIyMyIJSIsRj9kxPRa9aP+1EJSA2wjK+DItIglISxGP3rgUjZ0hKIF4aX0sICbdZ1JEEpAWIh4zE5do7AxBCcQH08l4MCUiCUjLEI+dIxo71/rxISYBtzGddJcJEUlAwnpEY3aISSA+iEn3RB2RBKRFmD62RTj6J/lYEpKA+zjV7Y4oI5KAtATxeBzRGCymkkD8MJ2028FhvaT14d8vAWkB4pFwjAJTSSB+iElkioCE0QjH6DGVBOIpGZOEpPmOFYa/byYgDRfX6SPhaCamkkD8MJVEKtYfBSZPnqy8vLy0/y1dujTqJWaNeISpGsuP8X0CYqi+LP/Ef4g3ZyaQ1157rUpKSjrcXlZWFsFqchfHH06CxD5MJIH44hR3vDkTkA899JCGDh0a9TKQBcLRfoQkEF+c4o4n9vYGitP0kXh0C6e2gXjj9HZ8ODOBdEVcfvCIDLcxkQTijdPb7nMmIB9//HHV1dUpPz9fI0eO1DXXXKPBgwdHvSykQDzGR2P5MSISiDFOb7vLmYC877772vz5+9//vqqrq1VdXZ3x56ioqEh5e01NjfJ7luayvIy4Pn0kHOOJaSQAiamka6zfo3/2s5/VggULVFNTo8bGRm3evFn333+/CgoKNHv2bM2dOzfqJWaEeITruD4SgMR1kq7ISyQSiagXEYQXXnhBX/jCF1RaWqoPPvhAvXr1yvpzVVRUaNvuOo2+6g4fV9iWyz9MRANSYSIJQGIi6YcNS/5Jw8pO1caNG0O7T2f34JdffrkuuOACffLJJ1q9enXUy+mUq/HIxAmdYdsAIDGRtJXT37ERI0ZIkvbs2RPxSuKHOEAm+CUDQBIhaRenv1P79u2TJBUXF0e8kvRc/GEhCOAV2wyAJELSDs5+h2pra7V8+XJJUmVlZcSrSc3FHxBCANliGgmgNULSbFZ/Z1asWKFnnnlGR48ebXP7+++/rylTpqihoUFf/vKXVV5eHtEK44WDP/zAdgSgNSLSTFa/DuSWLVs0ffp0DRgwQJWVlSotLdX27du1du1aNTU1qaKiQv/6r/8a9TJTcu0HgoM+/MRrRwJojdeQNI/VAXnhhRdqxowZWr16td544w3t27dPxcXFOv/88/XVr35VM2bMyOnle5AZ4hFB4Z1sALRGSJrD6oA8++yz9atf/SrqZXjm0vSReETQiEgA7RGS0WOvjKwRjwgLT7ABkIpLAxnb8MiHzJWNnYM5osB2B6A9nq0dDR5xeMZBHFFiGgkgFSIyXDzaIXJh4+bADVOwLQJoj2lkeHiUkTEO2DAN2ySAVIjI4PEIh4SNGQgGp7QBpMI0Mlg8ssgIB2iYjm0UQCpEZDCsfh1IW9i+8cb1wFwwqDGQz3vkg6JAPi94zUgAqdWX5fOakT4jINGpuMRjULGY6X0Rlf4hIgGkwouP+4uARFoux2OYwZiJ9ushKHPDe2kDSIdppD8IyIDZfvraJaZFY2dar5WYzB7TSACpEJG5Y8+KlFyaPhYMarQqHttLrt/mryFKLm3LAPzDgCc3PHoBsnXjdOWA62J0ufg1hcGVbRqAv3ipn+zxqME5cYisOHyNfuP1IgGkQ0R6xyOGNmw+wMYxquL4NefK5m0cQHCISG94tAJi44Zo64GViOIx8MrWbR1AsGw8dkeFRwrWIpo64jHJHBEJIBUiMjM8SpBk38GUSOocj09muC4SQCpEZNd4hALAhhcs4igzTCMzR0QCaI9jeed4dGDNwZMgyg6PWWZs+TkAEB4iMj3eiQZWIIJyUzCokXe0yQDvXIMgfpFgm7Ib71qTGgHpM9t+W7Fh6kI8+iP5OBKSneN9tN0U5b7Oy32z3ZmJiOyIgITRiEf/MY3MDNNIO9nwS3FnOls/22O0iMi2CMgYM31HSzwGh4jMDBFpLtP3X0FI9zWzjYaHiPwUAekj205fm4x4DB4RmRki0gxxDMZMpXps2GaDQ0QeR0DGlMk7Y+IxPERkZrguMlwm759sQVQGi4gkIGEY4jF8RGTmmEYGh2gMXvvHmG05N3GPSAIyhkzdUROP0SEiM0dE+sPU/VCctP4esE1nJ84RSUD6hOsfc0M8Ro+IzByntL0jGM3GdDJ7cY1IAhKRIx7NQUR6wzSyc0SjvZhOoisEZMyYtkMnHs1DRHrDNLIt0/YxyB0x2bU4TiHZEgB0QNh7F+dwaiw/duI/uI3vdXpxu5SNCaQPbNloTPuBJ1LMxiTSu7hMI03blyAacdnevYjTJJKARCSIR7jMxWsjiUakwynutuISkQQkgLSYQmbPhekM0QivXNjukRkCEqGzYfp49oCPcv4cmz48zYeVRI+IzI1tB1SiEX6wbbv3WxymkARkTJhyUDA5Hv2Ixs4+nytBieyYekA1Zd8AN5m63YfB9YgkIBFrfkdjpvdlW0wyhfSPCdeLEY0IW5xD0lUEZI5seQa2CUyaPoYZjp3dv00hSUT6L4yYJBZhkriFpMtTSAIyBjiAfCrqcGzPxpBEMFL9nHo5yPJzDpu4+EoF6bgakQQkQhH19NG0cGzv7AEfWRGRTCHDRRTCZXGbRrqG7xqcZ3o8Jp094CNr1goAfonDO9u4eLmbe18R0IqNQWb6mqOeJgNwExFpF7e+GhgpiuCwfZpn89oBIFtxmEa6goB0XBx/EF2JL5O/DqaQAILk6rHLpSmkO19JBFzaEIISdmiYHF3ZcO3rAYBMuRqRrqCA4AxXY8vUr4spJICguRiRrgyfeBkfwAK2vMwPAG+C+kXMpZfbcvHlflx4bUgCEk4wdUrnJyISsFuYU/tU92V7VMbpxcdtwHcCgQlrZxmHeEwy7WvlNDaQWsGgxg7/Rc2ktWTLpVPatp/KZgIJq5kWVADiy6Ywa71W2yaTTCLNwHfAYS79poZPEc2AOVyY6tn4NbhyfLN5CmnvyhF7cQ4pk752mw46gB9sDK5M2fR1uRKRtiIgEQhbdkAAkCmb4ipXtnytLkSkrVNIroGElaKYwF3W950T/79s72dCv//2eFY2EDwbIipIya/f5OskuSYyGgQkkELrWPTycRPCEkDu4h6O7ZkekrZHpI2vC0lAwjpBTR+7ikYvnyOskDRlClkwqNHYAwvgFfGYnsk/67ZHpG0ISMSeH+GY7nMykQTsYWI45voLcxC/YJo+jbSVbVNIAhJW8XP6GEQ4prsPQhIwl2nh6Od+rv3n8jMoTZxGMoUMD48yYimMeAzr/kx6SR/ANibF49kDPgr859nv+zDp8Uty4ZnZNmACidgJOx5b3y+TSMAMpoRPVL8AJu/Xj4mkiae0bZ1E2nQam4BErEQVj63vn4gEouVKPKban3ndv/j5RDzTTmnbGpG2ICBhjSB2tlEgIoHomBCP2ezLMt1/tf97mexr/J5GmhSRNrJlCklAIhZMicegmPJyPoCpXA/HTP59VzHp137EpIhkChkcAhLOy2UHXP/fLardfkiHm45KeVJhr27qP6xIRSd1z3lNTCGBcNgYj13tt44dS6hu5yEdqG3WkZaEuhXkqahPgfoPK1ZBYepgyuRVIYhIZIqABFppajiilhff1PJXmrXmrTzt29OU8u8NHz5cJ408qOuvaFbe5Mq0O+zOEJFA8GyLx87C8dy6DVr0u0Pa9PYFWrdunQ4cONDh7xQWFmrkZxIafV53fXlqL+056zzl5eV1uI+uIlLK/ZS2SRFpGxtOYxOQDivalR/7lzPIdPp47t4Nmv94gxYvPKSG+kSXf7+mpkaqkd78g3TyKa+o8trT9dkbTlef/j1yXTIAn7gSj5terdM7/2eT/vFPzUokJOnltJ+jpaVFf94g/XnDYf37gkadXfFfuv7GYhVdWalu3T/9RTesaaQpEckU0n88moi1Y0cT+vDfX9dVn6vVb+Y3ZhSP7e3772N66V+362dXv6bmpWuUSGT+OVy/NhOIigvxOL75bT1/x2t67Nvr9cqyZDx6s2njEVXP2q/Hr1+uM3a+ldH9+s2E7wX8R0DCWV3tGOt2H9Jvpi3Xg3MOqrk59/s7cCChWf+4X8/e+prGt/w5908IICtRB4vXF+tOta/quXKdvvhXe/WH36e+jMarv/z5iKZetVcv/Mu2Dr/kdrav9Ot1KqP+nkj2vcB4fZnZiWb26oD/4feL7X5Y06BHv75W69Yc9vXzStLLLzXrhq/WaWz92xn9faaQQHyl+vlv/P0afedb+/Tfdf4Gz5HD0n/OfU+/q35Hx47GMyLhHwISTupsZ7h3R6N+NW2d9n/kw9gxjXe3HNE3/1edxjdnFpEA/BF1pOQ6eWx5fo1+dMd+HQtwWLZq0Qd6+t53PF1u48pbpto2hTQZAZkD058hhY4ONx/V/57xlg7sbQn8vt5796hm/v0nOnYsiwuXAHhmezyWv7ted96+388lpbXyPz7Qfy3Y2eWaWvMjIqP+Hkl2RaTJp7HNXRmsZsKz7lLZ8tgb+vi98HZgr69q0Sf/d01o9wfEVdRhkmtcHWk5ph/evl9Hj/q0oAwsfWSXKmo3tLmNJ9UgUwSk41x52QIvLyWRbgdYtnW9Hn+swa8lZeyfHjiovTsPdfp3bL8O0tRfGBAPUQeJHy8SvvV/v653txzxa0kZOXTokO78/n5PZ0m4HhJJbtQFkIGHf3ow0OuK0jl0KKFNv34z/DsGEDg/gupgXYv+7V/D/+VWkta83qLi19vun2z/hTYTnMbOnZmr8ujQoUOaPXu2Ro4cqZ49e2rQoEG66aabtHv37qiXBkN89F6DVr4a/HWP6fz+2UO66HBwL+3D+2AjrmybZKWKs09+v16Ho9s96aknvT2GTCEhORCQTU1NuuyyyzRnzhzV19frK1/5ik4//XTNmzdPY8aM0XvvvRf1EmGA3Ys2dP2XAtTcLC36D3aWgEv8CKljRxP67W+i3Tcsf7lZe3fEb/9k0xTSRNYH5H333adVq1ZpwoQJ2rJli373u99p9erV+tnPfqba2lrddNNNUS8xtky6Lm6pTy/Gm4vf/r+8rv8SgIxFOcHKJh5TTR93vH1Au3eF+MyZFBIJqfmVtmdIwnhGtsQU0mZWB2RLS4t+8YtfSJJ++ctfqqSk5MTHbrvtNo0ePVqvvPKK1q5dG9gabHgpH1eeSJOt/R836+OPo/8+fbSlRUcPR78Ov5n0iwLiw5Xw2LnxQNRLkHT8vbPjyJYppInXQZq3Ig9ee+017d+/X8OHD9eYMWM6fPy6666TJD333HNhLw0GOa1mY9RLkHT8cosPa/y/UJ7rH4Fw+fmi2js3HvTtc+Uim4BkChlvVgfkW28df2P4ysrKlB9P3r5hQ7TXv8WZn9OpbENpy+ZwXxqjM/13bI56CYD1bAyOdKeEG2v2hryS1HbuOKrmxmhPpcMuBVEvIBc7duyQJJWXl6f8ePL27du3Z/T5KioqUt5eU1Oj/J6l3hdokKJd+daM6v321n+fIsmM3/Ib6hMqjXoRPuL0NeLG77f0a6g3Z7/c0nhEPYq6nfjzZX3f0bK9nwnlvgsGNUa2P2ksPxb7S72yYfUjVl9fL0kqKkq90RUXF0uSDh40Ix7iKurIqCz960jvv7UjaX7Bz3YnzelrwG5hvvNMV44e9f62q668Rza8s3oC6beNG1NfK1dRUaFtu+tCXg388lb901Ev4YSePd15JnbUvxggnmw8fd2ZHj3M2Sd079Gt678UoCinkDaoL8s36om7Vk8gk8+6bmxMvUNpaDj+hIXevXsHuw6DvqGdcWFEn83E7ZIz9gWwkuyUlfm3g2b6CIQriGnboPJooy2puCRPvU5ipoTMWV0UgwcPliTt2rUr5ceTtw8ZMiS0NSG1KH+rrBjVPbL7bu+DYedGvQRfMCVAFFybPkpSj7MGRr0ESVLFud2Vn2/ONDRscX2OQC6sDsjzzjtPkrRu3bqUH0/ePnr06NDWhOB1NXlrfz1hzaBRKuwR5IoyM2zYMBWf3DFms7n+kekj4IbB554U9RIkSeeONuMXbRd/SXCV1QE5ceJE9enTRzU1NVq/fn2Hjy9cuFCSdPXVV4e8MnNFeRo7qqlVt+75GltVGMl9t1ZZ9WHUS/AF00fEUVBPFhk6po8KDGi3Cydkv4/kiTTxZHVAFhYW6rvf/a4k6ZZbbjlxzaMkPfzww9qwYYMuueQSjR07NvC12HIdZNT8ig+vE7izrhvpy/3m4ms3RPO1AzBXycmFuuKLPSNdw8kDe6qhquObcQCdsTogJemuu+7ShRdeqBUrVmjEiBH6m7/5G40fP1633367+vXrp3/7t3+LeomIQPvTwqMu66f+/aPb3M8d1V27zjyvw+1eT19HHY9MHxEVl09tXn9jcaT3P+0b3ZXfreP1j2G9BqQpbLgO0qS3NDRnJVnq2bOn/vSnP6m6ulpFRUV65plntH37dk2bNk3r1q3TGWecEfUSjRP1s7GjmEJ2656vG2+Obid9098WKy/P7gvUiUcgGB+dfZ7OGxPNeeyS3nk65cvnR3Lf6bj8y4JLrA9ISerVq5fuvfdevfvuu2pubtaePXs0b968tO9Qg3ho/9vzwP91gc6uCP9lKi65rIfy/qrj223aNn0EEIy8vDzd/9NoroW8c/ZJ6n1q9NeIwz5OBKQpbLoOMq5TyC/dV6n8gvAmgb1752nOg31ynj5GHY9MH4FgvVt+nm79Xkmo9/mZSaeo6KrgnyMANxGQiEwYUdJ+ylf2md66ZtaIwO9XkvLzpQce6qN1vUd1ua7OEI+A/TL5mS+/sUoXXRzONLB0QA99bc7ZaX+5jdv1j/COgIyxqKeQkj9x4vV1IT97w+m64rvDcr7fzuTlSff9pI+Ofrbjb/fEI4BUuhXk6ys/n6DKqmDPZffuW6gZj49R6YBon/1tIhueSGOK6AvCMTadxnaJ19C64pYz9JUfnKkgntfSvbv00NxSFX3pgg4fsykeAVPE6UkVPYoL9G+/OUUXTw7m3Q9OPb2XZv5mrE47I9pnfnclTt9zWxGQMefKFFLqPLhShdul04fot4tP1bAz/Hsv2lHnddezS/uq2+dzmzyagOkj4K9M9wEvHztXU345UXfde5J69fLvt9zrv1GkOxaPU78hnf9s27avQjSirwdELs4Ruees8zVj0cX69neKVVSU/Y66qE+BvnTbcE37zSRtGZTb6z1u+vC0yKePxCMQrfz8PPX9apWee6GvzvnsqTl9rpFnFeiJ356icT+aoJ7F4b8SBdzElhSAkt3HjHqxT1sc+aDIl9MWmz48Le1bayVD7rK+75y4rbBnN1XcOl6zpx9R49I39e8LGvXuliMZ3deo87rr698sUv6lY1TYM/Uk07bT1sQjEJxlez/TZv/TlT/3Ha2/fUw6p3aD/s+CRi36j0Yd2J/o8t8VFEiXX9lTX/9msT4+5zzty/B6HaaPyBQBCUnHp5AmXDwcRkRKqXfiPUsK1PO6Kt1ybUL7P2rWzo0HtfPPB1S7vVGHm49Jecdj87QzinV6RW+dXtFbvfumv07Jxtd5JB4BM/2l32idd5t07sxj+ui9Ru3ceEC7Nh7Ugb0tOtJ8TN2656lX7wKVn9Nb5eecpLLPlKiwVzfVSsr03ArxaIf6snwjnm9BQMI4YUakpA4hmZeXp9IBPVU6oKdG/VW/rO6beASQjtcpZGvdCvI1aGSJBo0s0YVTfF4Y4AEBGRAbT2ObMoWUwotIKX1IZoNwBGCjXKaPJuzHED4CEm3ENSKltjvQTGPS9p0u8QhEI5cppN84dY1sEJABsnEKaRo/I1JSRiEpBbtDNSEcJeIRiJoJEUk8IlvUDTow4WV9WjvyQZGvL/MTVcCZ8PI8kr+PJxAGE7bXoH52oww44hG5MKsUYAzTIlLy9yASZsyZEo6SGQdiAG1FEXJ+3acp+zaEj1PYAbP5NLZJ10Mm+XVKO6n1zi/T09teP68JCEfAbH4+mS+T+wFyRUCiU6ZGpOT/e6W2j75Mg9K0WGyPeAT8kekT8nIRVEgSjvAbARkCm6eQJvN7Gtme6WHYFcIRsJdfIRmHJwQiGgQkumTiFDIpqGmkzQhHIDhhTCFbC/vlxUzBfsx8BGRIbJ9CmhyREiEpscOF24I+42ADk8KQ6SPsLRqEzsRnZrcXx5eoiePXDESJeAIIyFCZ8ObnubIhIiX3oyr59bn8NQIwEwENiYBEFmyJSMmtkCQaAXPENaLi+nWjI3tKwBEuTCEluyJSsje+bF03EATTfg6IqWCY9n02jSkdwZNokDXTn1iTTuudk2kX5bPjBGAqghmtEZARsP0Z2a3ZGpFJ7YMtzKAkFgH7hf2yPlEhHtEeAYmcJU9n2xySSV1FnZfAJBAB/5n4cj6uRyTxiFQIyIi4NIVMsn0amQmiEEAqrkdkWKLex9p2fX+UeKTgK374AMSVi5M6F78m+IOjfYRMeSaV34hIAEGKekrVGZeCy6WvBf7jSB8xlyOSkAQQR7aH16YPT4vkazD5FwN0xBEegSIiAQTB9NiwNSJtXTfCx9HdAK5OIZOYRgKIo6gmedmKcq0m/ELAccobHi1DuB6REj+cAPxlQnRkwvSItC1048ykVuBlfBAql14zEu7y85cdtvVgmfi6kKkkA82kl/oxJRpt+UUAbRGQBnHxtSHTISRhkiCn460/N9s7TAhJU8IRdiMgDROniJQISUQjqsspiMlg2DKFbC2KkDQxHE2ZPnKJlXcEJIxASCJIJh4ciElIHaPO76A0MRrhBgLSQHGbQrZGSMIvJkZjOmz3ubNxCplKquDLNCpti0VTpo/IDgFpqDhHpMQBFdmxKRpTicP7yQfJlYhsz7YwRDBMega2REDCcIQkumJ7NLbHNp8bVyPSNSZNH13bh4SFgDRY3KeQrXG9GJLisrMnJOEqk+IR2YvHnthipo2sTcA728RP8nsex+97HL/mXBEoQPCYQFqASWRqTCXdRjh9immkd5zKNpNpcc9+Jns8cpZgEtm5OE+oXML3sXM8Lt6YFitxx/cjeyY2ABNIOKf9QZapjbkIIu+YRnrDJNIMJsYj+5/c8OhZxMTfQGzAVMssfD/8weOXORPjJU54/N3EBNIyXA+ZG6aT4SN0gsPrRmaOSWQ0iEd3EZAWIiL9Q1D6j2AMF6e0M0dEhsvkeLRpP2Xq2UcC0lJEZDBS7VQ4MKdn007YdUwjM0NEhsPkeIQ/CEiLEZHhSBdJcTtYE4vmIyIzQ0QGy/R4ZF/mDwLSckRkdFwNS3audiMiM0NEBsP0eLSNqaevJQLSCUSkWTIJsKgP8ESi27guMjNEpL9siEf2ff4hIB1BRNqFnRjCwDSya8noISRzY0M8wl8cxRxi8qgbQDT4ZSUzBFD2bHnsbPtZMP2YbtejiS6ZvsEBCJ9tB86oHPmgyJoYMgGPV7yxV3EQEQmgPd79J3NEUddse4zY9v3HI+ooIhJAKhxIM8N0LTUbHxcbt3kbjuH2ParImA0bIIDw2XhAjYqNwRQUHge0xl7EcUQkgFSISG/iHJI2f+1s58HhZXxiIBmRvMwPgNZ4vUjv4vSyP7ZGY5Kt8WjL4MfORxdZsWWjBBAuWw+0UbJ5KtcVl782+Ie9RswQkQBSISKz40psJb8OF74Wie05DJzCjiHetQZAKrxzTfZah5ctp7ddicX2bI5Hm4Y8BGRMEZEAUuG6yNy1DzOTgtLVaET4CMgY48k1ANJhGumfKIMybsHI9DE8BCSYRgJIiYgMRrqoyyUs4xaKqdgcjzYiICGJiASQGhEZHiIwe7bHo23TR4lnYaOVkt3HrNyIAQSL99EG0B57BHRARAJIhYiEidguo8GjjpSYRgJIhYM1TOLC9mjrsdb+Rx6BsnXDBhAcTmnDBGyD0eLRR5eYRgJIhQM4ouLKtmfzsdWN7wBCYfOGDiAYTCMRNrY3M/BdgCdMIwGkwkEdYXBpO7P9WOrOdwKhIiQBtOfSwR3mcWn7cuH46c53A5Fw4YcAgH84pY0gsE2Zx9rvyMsvv6y8vLy0/40fPz7qJcYG00gA7XHAh19c25ZcOV5a/1aGw4cP16RJk1LejnAlfyh4S0QA0qcHft4KEdlyLR5dYn1ATpo0SfPnz496GWiFkATQGu+njWy4GI+uTB8li09hw3yc2gaQxLWR8IJtxXx8hxA4QhJAEmGAzrj8i4Zrx0HrT2Fv3bpVd955p+rq6tS3b19NmjRJV1xxhfLz3dwAbcapbQAS10YiNVfDUXIvHiUHAnLFihVasWJFm9tGjRqlRYsWacSIEZ4+V0VFRcrba2pqlN+zNNsloh1CEoBESOJTLsejq6z9jvXp00d33HGHVq1apbq6OtXV1emll17S+PHj9fbbb+vyyy/X/v37o14mOsGpbQAS8RBnLp+yTnL1OJeXSCQSUdzxlClTtGnTJk//5sknn9S4ceM6/TtHjx7VpZdequXLl+uBBx7QnXfemcsyJR2fTG7bXafRV92R8+dCekwkATCNjA/Xw1EKLx43LPknDSs7VRs3bgzl/qQIT2Fv27ZNmzdv9vRvGhsbu/w73bp106xZs7R8+XI9//zzvgQkwsGpbQCc1o6HOMSj6yILyPXr1wf2uZPXPu7Zsyew+0BwWv/GRkwC8URIuilO4ejqqesk659Ek8q+ffskScXFxRGvBLliKgnEGyHphjiFo+R+PEqOBuSiRYskSZWVlRGvBH5hKgnEGyFpr7jFY1xY+1195JFHtHPnzja3JRIJPfbYY/r5z3+uvLw8zZgxI6LVIUg8exuIrzg8a9cVcf1exeX4ZO0E8pFHHtH3v/99VVZWatiwYWpqatLbb7+tbdu2KT8/X48++qjGjh0b9TIRIKaSQHwxkTRXHKMxKS7xKFkckLfffrteeOEFbdy4UX/5y190+PBhDRw4UDfccINmzpypqqqqqJeIEBGTQDwRkuaIczjGkbUBeeutt+rWW28N7f7yW9g52YKYBOKndbwQk+EiHI+L0/RRsjgggUwQk0D8MJUMB+H4qbjFo0RAetJ72yEdHNYr6mUgS8QkEC9MJYNBOLYVx3iUCEjPiEg3EJNAvDCVzA3RmFpc41EiILNCRLqFmATio30IEZTpEY3oDAGZJSLSTe1/myQoAbdxmrstojFzcZ4+SgRkTohI9zGdBOIjrtNJotG7uMejREDmjIiMD6aTQLy4GpQEY26Ix+MISB8QkfFEUALxkiq8TI9KYtFfxOOnCEifEJEgKIH46SzQwopLIjEcxGNbBKSPiEi0lmpnQ1QC8UHYuYN47IiA9BkRic4QlQBgF+IxNQIyAEQkvCAqAcBMxGN6BGRAiEjkgqgEgGgRj50jIANERMJPne3MiEsA8A/x2DUCMmBEJMKQbmdHWAKAN8RjZgjIEBCRiEpXO0ICEwA+RTxmjoAMCREJE3FaHACOIx69ISBDRETCJpnsTIlMAC4gHr0jIENGRMIlnCIHYDviMTsEZAR6bzskSYQknEdgAjAZ8Zg9AjJCTCMRdwQmgKgQj7khICNGRALp8SQfAEEgHnNHQBqAiAS8Iy4BeEU4+oeANAQRCfiHF1YH0B7x6C8C0iBEJBAswhKIJ+LRfwSkYXiGNhA+whJwF/EYDALSUEwjgei1P/AQlIA9CMdgEZAGIyIBsxCUgB2Ix+ARkIYjIgFzpTpIEZVAtIjHcBCQFuC6SMAeTCmBaBCO4SIgLcI0ErAPQQkEj3gMHwFpGSISsFvrAx0xCeSGcIwOAWkhTmkDbmA6CWSPeIwWAWkxppGAW5hOAl0jHM1AQFqOaSTgJqaTQFuEo1nYIzkiGZIA3FSy+9iJ/4C4Ybs3DxNIh3BKG4gHTnUjLghHcxGQjuGUNhAvnOqGiwhH8xGQjmIaCcQT00nYjHC0BwHpMKaRQLwRk7AF4WgfAjIGmEYCICZhIsLRXgRkTDCNBJDEdZOIGuFoPwIyZghJAO0xnURYCEd3EJAxxWltAKkQkwgC4egeAjLGmEYC6AynupELotFtBCSYRgLICNNJZIJwDF9+S/iPOQEJSUwjAXjDdBKtEY3RieqtjAlItEFIAsgGQRk/RGP0oopHiYBEGpzWBpALgtJNRCOSCEikxTQSgF8ISnsRjWaKcvooEZDIACEJwG8EpdmIRrNFHY8SAQkPCEkAQUkVLERleAhGe5gQjxIBiSwQkgDCQFQGh2C0kynxKBGQyAFPtAEQNqLSO2LRDSbFo0RAIkdMIwFErbNAilNcEoruMi0eJQISPiEkAZioq6iyLTCJxPgxMR4lAhI+IyQB2CSbIPMzOglCdMbUeJQISASEkATgKqIPYTA5HiXJrtk9rNN72yHjfwgAADCJDcdNAhKhICQBAOiaLcdKAhKhIiQBAEjNpuMj10AiElwjCQDAcTaFYxIBiUi1/qEhJgEAcWNjPEqcwoZBOL0NAIgTm495BCSMQ0gCAFxn+3GOgISxCEkAgItcOLZxDSSMx3WSAABXuBCPEhNIWIapJADAVi4dv5hAwkpMJQEAtnApHJOYQMJ6TCUBAKZy9fjEBBLOYCoJADCJq/EoMYGEo5hKAgCi5PoxiAkknMZUEgAQJtfDMYkJJGKDqSQAIEhxOsYwgUTsMJUEAPgtTvEoGTKBbGho0IIFC3TrrbfqwgsvVI8ePZSXl6d77rmny3+7a9cuTZ8+XYMGDVLPnj01cuRI3X333Wpqagp+4bBecioZtx98AIA/4noMMWICuXXrVt14442e/927776rCRMmaO/evTr33HN18cUXa82aNbr33nv10ksv6aWXXlKPHj0CWDFcxGQSAOBFHMMxyYgJZO/evXXzzTfrX/7lX7R27Vrde++9Gf27adOmae/evZo5c6befvtt/e53v9PmzZs1ZcoUvfbaa/rxj38c8MrhKiaTAIDOxP34YERADh8+XL/+9a/1d3/3d6qsrFT37t27/Devv/66XnvtNfXv318//elPT9xeUFCgf/7nf1b37t316KOP6siRI0EuHTFATAIAkjgeHGdEQGZjyZIlkqSrr766w2nq0047TRdffLH27dunV199NYrlwVHEJADEF/v+T1kbkG+99ZYkqbKyMuXHk7dv2LAhtDUhXohJAIgH9vUdGfEkmmzs2LFDklReXp7y48nbt2/fnvHnrKioSHl7TU2NCvJP8rhCxEn7HQtPwgEANxCOqVkbkPX19ZKkoqKilB8vLi6WJB08eDC0NQFJPKMbAOxGOHbOl4CcMmWKNm3a5OnfPPnkkxo3bpwfd++bjRs3pry9oqJC27fVhrwauIKYBAC7EI9d8yUgt23bps2bN3v6N42NjTndZ0lJSaefp6GhQdLxlwgCTMGpbgAwF+GYOV8Ccv369X58Gk8GDx6sN998U7t27Ur58eTtQ4YMCXNZgCcEJQBEj3D0ztpnYZ933nmSpHXr1qX8ePL20aNHh7YmIFc8sxsAwsX+NjvWBuRVV10lSXruuefU3Nzc5mMfffSRli9frpNPPlkTJ06MYnlAzlrHJDs4APAX+9bcWBuQ48aN08SJE/Xxxx9r1qxZJ24/cuSIvvOd7+jw4cOaOXNmRu9qA9iAoASA3LEP9UdeIpFIRL0I6fgzuffs2SNJ+uCDD7Rz506VlZWdeD3HgQMHavHixW3+zdatWzVhwgTV1dVp1KhROuecc/TGG2/ovffe00UXXaRly5Z1eJeabCSfhT3h/Jk5fy4gKFw/CQCdczUcV65/VEOG9Uv7ajJBMOZ1IN98880OL/q9e/du7d69W1LqJ8OMGDFCb775pmbPnq2lS5dq8eLFGjx4sKqrq/WjH/3Il3gEbMETcgAgNVfDMUrGBOT777+f1b87/fTTNW/ePH8XAziAoAQQd4RjcIwJSADBIigBxAXhGDwCEoipVDtYohKAzQjH8BCQAE5gSgnARoRj+AhIAGkxpQRgMsIxOgQkAE+YUgKIGuEYPQISQE6YUgIIC+FoDgISgO+ISgB+IhzNQ0ACCAWnvgF4QTSajYAEEAmmlABSIRztQEACMAZRCcQX4WgXAhKA0YhKwF1Eo70IyAzltRyJegkA/gdRCdiNcLQfAelBQc1uSdKR4WURrwRAe0QlYDai0S0EZBYKanYTkYAF0h2wCEsgHESjuwjILBGRgL2YVgLBIRrjgYDMAae0AXcQlUD2iMb4ISB9wDQScBOnwIH0iMZ4IyB9QkQC8cG0EnFFNCKJgPQRp7SB+GJaCVcRjUiFgAwA00gASYQlbEMwIhMEZECYRgLoDGEJUxCMyAYBGTCmkQC86OxgTlzCDwQj/EBAhoCIBOAHppbwilhEUAjIkHBKG0BQmFpCIhYRLgIyZEwjAYSpq6ggMO1ELCJqBGQEmEYCMAWBaS4iESYjICPENBKA6TKJGCIze0QibEVARoxpJADbeY2gOAQnYQjXEZCGYBoJIC6CiKtco5TgA7whIA3CNBIAskMAAuHKj3oB6CgZkgAAACYiIA1VULObkAQAAEYiIA1HSAIAANMQkJYgIgEAgCkISIswjQQAACYgIC1ERAIAgCgRkJZiGgkAAKJCQFqOkAQAAGEjIB1BRAIAgLAQkA5hGgkAAMJAQDqIkAQAAEEiIB1GSAIAgCAQkDFARAIAAD8RkDHBNBIAAPiFgIwZQhIAAOSKgIwpQhIAAGSLgIw5QhIAAHhFQEISIQkAADJHQKINIhIAAHSFgEQHTCMBALBDQc1u5bUcCf1+CUikRUgCAGCuKI/RBCS6REgCAGCWqI/LBZHeO6yS3FiPDC+LeCUAAMRT1OGYxAQSnjGRBAAgfCYde5lAImtMJAEACJ5J4ZjEBBI5YyIJAEAwTD2+MoGEb5hIAgDgD1PDMYmAhO9ab/TEJAAA3pgejxIBiYAxlQQAIDM2hGMS10AiFFwnCQBAerYdI5lAIlRMJAEA+JRt4ZhEQCIShCQAIM5sDcckTmEjUpzaBgDEjQvHPSaQMALP3AYAuM6FcEwiIGEcTm8DAFziUjgmcQobxuL0NgDAdq4ex5hAwnic3gYA2MbVcEwiIGEVTm8DAEzmejgmEZCwElNJAIBJ4hKOSQQkrMdUEgAQlbiFYxIBCWcwlQQAhCmu8SgRkHAUU0kAQFDiHI5JBCScxlQSAOAXwvFTBCRig5gEAGSDcOyIgEQscYobANAVwjE9I96JpqGhQQsWLNCtt96qCy+8UD169FBeXp7uueeeTv9dXl5ep/81NTWF8wXAWsl3u2EnAQBI4rjQNSMmkFu3btWNN96Y1b8tLi7Wddddl/Jj3bp1y2VZiBlOcQNAvBGNmTMiIHv37q2bb75ZVVVVqqqq0pIlSzR79uyM/m3fvn01f/78YBeI2CEmASA+CEfvjAjI4cOH69e//vWJP7/wwgsRrgZoi+slAcBNhGP2jAhIwAZMJQHAfkSjP6wPyIaGBt1///3asWOHioqKNGbMGE2dOlUlJSVRLw0OIyYBwC6Eo7+sD8i9e/fqrrvuanPbbbfdpieeeEJXXXWVp89VUVGR8vZ33nlHOia9Wve7rNcJh9V9+r+JQut/pADAKXktR6JeQuAaj+7Xjh2HQ71Pq492N954o66//nqNGjVKffr00datW/Xwww9rwYIFmjp1ql599VVVVVXlfD/Hjh1Tfn6+Bp810IdVoys1NTWSjl8bi+DxeIePxzxcPN7h4zEP1zvv7NehQ4dCvU9fAnLKlCnatGmTp3/z5JNPaty4cTnd7xNPPNHmz+eff76efPJJnX766XrggQd011136fnnn8/4823cuDHl7cnJZLqPw1883uHi8Q4fj3m4eLzDx2MernRnUIPkS0Bu27ZNmzdv9vRvGhsb/bjrlH7wgx/oJz/5iV5++WW1tLSosLAwsPsCAACIG18Ccv369X58Gt/06dNH/fv31549e1RXV6eBAzn1DAAA4Bcj3srQb8eOHdOBAwckHX+nGgAAAPjHyYBcunSpGhoaNHz4cJ100klRLwcAAMAp1gbkb3/7W73xxhsdbn/llVf07W9/W5J0yy23hL0sAAAA5+UlEolE1IuQjj+Te8+ePZKkDz74QDt37lRZWZnKy8slSQMHDtTixYtP/P1p06bpiSee0MiRI1VRUaHu3btry5YtJ67H/NrXvqannnpK+fnWNjIAAICRjAnIoUOHavv27Wk/PmTIEL3//vsn/vyHP/xBTz31lNauXasPP/xQ9fX1OuWUUzR27FjddNNNuu6660JYNQAAQPwYE5AAAACwA+d3AQAA4AkBCQAAAE8ISAAAAHhCQAIAAMATAhIAAACeEJAAAADwhIBMo6GhQQsWLNCtt96qCy+8UD169FBeXp7uueeeTv9dXl5ep/81NTWF8wVYJtvHW5J27dql6dOna9CgQerZs6dGjhypu+++m8c6Sy+//HKn2/D48eOjXqK1Dh06pNmzZ2vkyJHq2bOnBg0apJtuukm7d++OemnOmTx5cqfb8dKlS6NeopXWrl2rBx98UFOnTlV5efmJx7Mr8+fP17hx41RSUqJTTjlFX/ziF7VixYoQVmw3r4/3Pffc0+l2/8Mf/tC3tRX49pkcs3XrVt14441Z/dvi4uK0L2TerVu3XJblrGwf73fffVcTJkzQ3r17de655+riiy/WmjVrdO+99+qll17SSy+9pB49egSwYvcNHz5ckyZNSnk7vGtqatJll12mVatWaeDAgfrKV76i999/X/PmzdPvf/97rVq1SmeccUbUy3TOtddeq5KSkg63l5WVRbAa+82ZM0fPPvusp3/zve99T3PnzlWvXr10+eWXq6mpSS+++KJeeOEFLVy4UNdcc00wi3VANo+3JE2cOFFnnnlmh9vHjh3rx7IkEZBp9e7dWzfffLOqqqpUVVWlJUuWaPbs2Rn92759+2r+/PnBLtAx2T7e06ZN0969ezVz5kzNnTtXknTkyBH99V//tRYvXqwf//jHGU0x0dGkSZPYjn103333adWqVZowYYJeeOGFE1Hz8MMP6/bbb9dNN92kl19+OdpFOuihhx7S0KFDo16GMyZMmKDRo0ef2FcPHTpUzc3Naf/+H//4R82dO1ennnqqVq5cqREjRkiSVq5cqcmTJ2v69OmaPHmySktLQ/oK7OL18U761re+pWnTpgW7uAQy8uMf/zghKXH33Xd3+vckJYYMGRLKmlyWyeO9evXqhKRE//79E01NTW0+9uGHHya6d++eOPnkkxOHDx8OeLVu+dOf/pSQlPjmN78Z9VKc0dzcnOjTp09CUmLdunUdPj569OiEpMSaNWsiWJ2bLrnkkoSkxLZt26JeitN69OiR6CwlrrzyyoSkxM9//vMOH5s5c2ZCUuKhhx4KcIVu6erxvvvuuxOSEvPmzQt8LVwDCWstWbJEknT11Vd3OE192mmn6eKLL9a+ffv06quvRrE84ITXXntN+/fv1/DhwzVmzJgOH09e8vLcc8+FvTQgMIcOHdKyZcskKeVlXWz3duMUdgAaGhp0//33a8eOHSoqKtKYMWM0derUlNfhIHtvvfWWJKmysjLlxysrK7Vs2TJt2LBBkydPDnFlbti6davuvPNO1dXVqW/fvpo0aZKuuOIK5efze6dXmWyrkrRhw4bQ1hQXjz/+uOrq6pSfn6+RI0fqmmuu0eDBg6NeVixs3rxZzc3N6tevn8rLyzt8nO0+OMuWLdP69evV1NSk8vJyXXnllb5e/ygRkIHYu3ev7rrrrja33XbbbXriiSd01VVXRbQq9+zYsUOSUu6YWt++ffv20NbkkhUrVnR4luSoUaO0aNGiE9cxITNsq9G577772vz5+9//vqqrq1VdXR3RiuKjq+2+uLhYpaWl2rdvnw4ePKjevXuHuTynLViwoM2fq6urde2112r+/Pm+DbMYJfjsxhtv1NKlS7V7927V19frzTff1De+8Q3V1dVp6tSpeuONN6JeojPq6+slSUVFRSk/XlxcLEk6ePBgaGtyQZ8+fXTHHXdo1apVqqurU11dnV566SWNHz9eb7/9ti6//HLt378/6mVahW01fJ/97Ge1YMEC1dTUqLGxUZs3b9b999+vgoICzZ49+8ST7hCcrrZ7iW3fb2eeeaYeeughbdy4UfX19dq5c6eeeuoplZWVadGiRfrGN77h2305O4GcMmWKNm3a5OnfPPnkkxo3blxO9/vEE0+0+fP555+vJ598UqeffroeeOAB3XXXXXr++edzug8TRfV4o6NcvxdjxozpcJ3eZZddpldffVWXXnqpli9frl/96le68847fVsz4Ld77723zZ9HjhypH/3oR7rgggv0hS98Qffcc4/+9m//Vr169YpohYD/brjhhjZ/Li4u1vXXX69LL71Uo0aN0jPPPKNVq1b58nq+zgbktm3btHnzZk//prGxMaDVSD/4wQ/0k5/8RC+//LJaWlpUWFgY2H1FIYrHOzmGT/d5GhoaJCl2p0WC+l5069ZNs2bN0vLly/X8888TkB6wrZrj8ssv1wUXXKA1a9Zo9erVXB8doK62e4ltPywDBw7U9OnT9dBDD2np0qUEZGfWr18f9RLa6NOnj/r37689e/aorq5OAwcOjHpJvori8R48eLDefPNN7dq1K+XHk7cPGTIkzGVFLsjvRfLaxz179gR2Hy5KPmmDbdUMI0aM0Jo1a9iOA9bVdt/Q0KBPPvlEJ598MgEZAr/331wDGZJjx47pwIEDkj695gO5Oe+88yRJ69atS/nx5O2jR48ObU2u27dvnyS2Ya/YVs3CdhyOs846Sz169FBtbW3Kt+tkuw+X39s9ARmSpUuXqqGhQcOHD9dJJ50U9XKckHxG+3PPPdfhlfk/+ugjLV++XCeffLImTpwYxfKctGjRIknpX44GqU2cOFF9+vRRTU1NygnxwoULJR1/TVMEq7a2VsuXL5fEdhy0Xr166bLLLpMkPf300x0+znYfnkQiocWLF0vyb7snIH3029/+NuWzrF955RV9+9vfliTdcsstYS/LWePGjdPEiRP18ccfa9asWSduP3LkiL7zne/o8OHDmjlzprp37x7hKu3zyCOPaOfOnW1uSyQSeuyxx/Tzn/9ceXl5mjFjRkSrs1NhYaG++93vSjq+D0he9yUdfyvDDRs26JJLLvH9ddriasWKFXrmmWd09OjRNre///77mjJlihoaGvTlL3857cvLwD+33XabpOMvp7R169YTt69cuVKPPfaYSktLdfPNN0e1PKfU1tbql7/8ZYdntNfX12vGjBlavXq1BgwYoKlTp/pyf3mJRCLhy2dy0JQpU05cK/DBBx9o586dKisrO7HTGThw4Imil46/L/MTTzyhkSNHqqKiQt27d9eWLVtOTBy+9rWv6amnnuKFmNPw+nhLx1/sesKECaqrq9OoUaN0zjnn6I033tB7772niy66SMuWLevwLjXo3NChQ7Vr1y5VVlZq2LBhampq0ttvv61t27YpPz9fc+fOPRFDyFxTU5MmT56s1atXa+DAgbr44ou1fft2rV69Wv369dOqVat0xhlnRL1MJ8yfP1/Tp0/XgAEDVFlZqdLSUm3fvl1r165VU1OTKioqtGzZMvXv3z/qpVpnyZIlmjNnzok/v/7660okErrwwgtP3FZdXd3mNY+/973vae7cuSoqKtLnP/95tbS06MUXX1QikdDChQt1zTXXhPklWMXL4/3+++9r2LBhKikpUVVVlQYOHKja2lqtW7dOdXV1Ki0t1e9//3v/zsoF/maJFhsyZEhCUtr/2r/n9X/+538mvv71ryc+85nPJEpLSxMFBQWJ/v37J6688srE008/Hc0XYRGvj3fSjh07EtOmTUsMGDAgUVhYmDjzzDMT1dXViUOHDoX7BTji0UcfTXzpS19KDBs2LFFcXJwoLCxMDBkyJHHDDTckXn/99aiXZ7XGxsZEdXV1Yvjw4YnCwsLEgAEDEtOmTUvs3Lkz6qU55S9/+UtixowZicrKykS/fv0SBQUFiT59+iTGjx+f+NnPfpZobGyMeonWmjdvXqf7aaV5H+Z58+Ylxo4dmygqKkqUlpYmrrjiisRrr70W/hdgGS+P94EDBxKzZs1KXHLJJYmysrJEjx49EkVFRYmKiorE7bffnti1a5eva2MCCQAAAE84lwoAAABPCEgAAAB4QkACAADAEwISAAAAnhCQAAAA8ISABAAAgCcEJAAAADwhIAEAAOAJAQkAAABPCEgAAAB4QkACAADAEwISAAAAnhCQAAAA8ISABAAAgCcEJAAAADwhIAEAAOAJAQkAAABP/j+/sr3Dwge5LAAAAABJRU5ErkJggg==\",\n      \"text/plain\": [\n       \"<Figure size 960x720 with 1 Axes>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"# Plot lapse\\n\",\n    \"plt.contourf(x, y, np.log(1 - lapse))\\n\",\n    \"\\n\",\n    \"# Plot circles for black holes\\n\",\n    \"ax = plt.gca()\\n\",\n    \"for bh_pos in [-8, 8]:\\n\",\n    \"    ax.add_patch(\\n\",\n    \"        plt.Circle(xy=(bh_pos, 0), radius=0.89, color=\\\"black\\\", fill=True)\\n\",\n    \"    )\\n\",\n    \"\\n\",\n    \"# Make plot square\\n\",\n    \"ax.set_aspect(\\\"equal\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Plot individual elements\\n\",\n    \"\\n\",\n    \"You can also iterate over elements in your volume data to plot things like\\n\",\n    \"element boundaries, collocation points, etc. Use `iter_elements`:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 27,\n   \"metadata\": {},\n   \"outputs\": [\n    {\n     \"data\": {\n      \"image/png\": \"iVBORw0KGgoAAAANSUhEUgAAAkgAAAJICAYAAACaHhuvAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAABcSAAAXEgFnn9JSAABqgElEQVR4nO3deXhM1/8H8PdkXyQRiS22ECQiliwiiT32rZZaQu1atKXVlha1JlpVVFstpahd7bXvWyIJIgmCSEiEIEGCIPsyvz/y6/3O3KDCTO4s79fzeJ5z7r2Z+Wglec89554jk8vlchARERGRwEDqAoiIiIg0DQMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxIRERGRCAMSERERkQgDEhEREZEIAxKpVVhYGAoLC6Uug4iIqFSMpC6AdNd3332H6dOnw8jICI6OjrC2thbCkkwmg6GhIYyNjWFiYgJTU1OYm5vDwsIC5cqVg5WVFWxsbGBjYwNbW1vY2trCzs4O9vb2sLe3h62tLQwMmO+JiEg9ZHK5XC51EaSb7O3tkZ6erpbXNjIyQsWKFVG1alVUrVoVDg4OqFatGqpXr44aNWqgRo0aqFmzJiwtLdXy/kREpNt4B4nUYvfu3WoLRwBQUFCAlJQUpKSkvPY6Ozs7ODo6onbt2qhduzbq1KkDJycn1K1bFzVr1oShoaHaaiQiIu3FO0ikcunp6XBzc0NqaioAwMTEBHl5eUrXGBgYoGXLlujTpw9sbW2Rk5ODrKwsZGVl4cWLF3jx4gUyMjLw9OlTPHnyBI8fP0Z6ejrS0tJUNqfJ2NgYderUQf369eHs7AxnZ2e4uLjAxcUF9vb2KnkPIiLSTgxIpHJDhw7Fhg0bAACmpqaIiopCXFwcZsyYgatXrypda2RkhFGjRmH69OmoUaPGf752UVERnj59iocPH+LBgwfCXaT79+/j3r17uHv3LpKTk3H37l0UFBS89d/B3t4eDRo0gKurKxo2bAg3Nze4ubmhYsWKb/2aRESkPRiQSKX279+PHj16CP0FCxZg0qRJAIDCwkJs3boVs2fPRnx8vNLXmZiYYOzYsZg6dSqqVq36znUUFhYiJSUFd+7cQVJSEhITE3Hr1i3cvHkTCQkJuHfv3lu9bqVKldC4cWOlP66urjA1NX3nmomISHMwIJHKZGRkoGHDhkL4aN68OUJDQ0vM8ykoKMCGDRsQGBiIW7duKZ0zNzfHp59+iq+//lqtd2uys7ORkJCAGzduID4+HnFxcYiLi0NsbCyePHlSqtcyMjKCq6srmjZtCnd3d3h4eKBp06awtrZWU/VERKRuDEikMh9//DH++OMPAMV3hKKiotCwYcNXXp+Xl4e//voLc+fOxd27d5XOlStXDp9//jm++uor2NraqrVuRXK5HA8fPkRsbCyuXbuGq1ev4urVq7hy5UqpJ53XrVsXnp6e8PLygpeXFzw8PBiaiIi0BAMSqcSZM2fQqlUroR8YGIgZM2a80dfm5ORgxYoV+P777/HgwQOlczY2Nvjqq6/w+eefSxou5HI5Hjx4gJiYGMTExODy5cu4dOkSrl27VmIC+qvIZDLUr18fzZo1g7e3N5o3b44mTZpweI6ISAMxINE7y8vLQ9OmTREbGwsAcHNzQ2RkJExMTEr1OpmZmVi6dCnmz59f4m6NnZ0dvvnmG3z66aewsLBQWe3vKj8/H9evX8fFixcRHR0t/MnIyHijrzcxMYG7uzuaN28OHx8f+Pj4wNHRETKZTM2VExHR6zAg0Tv7d8VsoPguSWhoKHx9fd/69Z4/f46ff/4ZixYtKhE0KleujKlTp2Ls2LEwMzN7p7rVpaioCImJiYiKisKFCxcQGRmJyMjINw5NlSpVgp+fH3x9feHn5wcvLy+N/bsSEekqBiR6J4mJiWjYsCFycnIAAJ988gl+//13lbz2kydP8NNPP+Hnn3/GixcvlM5Vq1YN06dPx6hRo0p9p0oKRUVFSEhIQEREBM6fP4+IiAhERUUJ/91ex8TEBJ6enmjRogVatmyJFi1acJ0mIiI1Y0CityaXy9G9e3ccPHgQQPHdnevXr6N8+fIqfZ+0tDTMnz8fv//+O7Kzs5XOOTo6YtasWRgyZAiMjLRrYfj8/HxcvnwZ586dw9mzZ3H27FncuHHjjb7WxcUFLVu2RKtWrdC6dWs4Ojqqt1giIj3DgERv7Z9//kGfPn2E/qZNmzBo0CC1vV9qairmzZuHP/74o8TE6Pr162P27NkYOHCgVm9im5aWhrNnzyIsLAxhYWE4f/58iVD4MjVr1kTr1q3RunVrtGnTBvXq1eM8JiKid8CARG8lKysLrq6uuH37NgCgffv2OHr0aJn8Uk5OTsZ3332HVatWlVgt283NDXPmzEGfPn10IiDk5+fj4sWLCA0NRWhoKM6cOSNs4fI6VapUQdu2bdG2bVu0a9eOgYmIqJQYkOitzJw5E0FBQQCKF0q8fPkyGjRoUKY1JCYmIjAwEOvXr0dRUZHSOQ8PDwQGBqJbt246FQzkcjkSEhJw5swZhISEIDg4GDdv3vzPr3NwcEDbtm3h7+8Pf39/1K5duwyqJSLSXgxIVGpJSUlwcXFBbm4uAGDy5Mn48ccfJasnLi4Os2fPxpYtWyD+5+zj44OgoCC0b99ep4KSopSUFAQHByM4OBinT58usd/dyzg6OsLf3x/t27eHv78/qlSpUgaVEhFpDwYkKrX+/ftj+/btAICqVasiLi4OVlZWElcFxMTEYNasWdi1a1eJc23atEFQUJDSYpa6Ki0tDcHBwTh58iROnTqFK1eu/OfXuLm5oUOHDujQoQNat26tEf8/iYikxIBEpXLq1Cm0a9dO6K9btw5Dhw6VsKKSIiMjMXPmTBw4cKDEuU6dOiEoKAje3t4SVCaNhw8f4vTp0zh58iROnDiBuLi4115vZGQEX19fdOzYEZ06dYKXl1eJ/fSIiHQdAxK9scLCQnh5eeHixYsAAG9vb4SHh2vsU2NhYWGYMWMGTpw4UeJcz549ERgYiKZNm5Z9YRK7f/8+Tp48iWPHjuH48eNITk5+7fW2trZo3749OnXqhE6dOqFWrVplVCkRkXQYkOiNrVmzBiNHjhT64eHh8PHxkbCiN3Py5ElMnz4dYWFhJc71798fs2fPhqurqwSVSU8ul+PmzZs4fvw4jh49ihMnTuDp06ev/RoXFxd07twZXbp0QZs2bWBubl42xRIRlSEGJHojWVlZqFevHu7fvw8ACAgIwObNmyWu6s3J5XIcPnwYM2bMwIULF5TOyWQyDB48GLNmzUK9evUkqlAzFBYWIjIyEkeOHMHRo0cRFhZWYikFRWZmZmjTpg26dOmCrl27on79+jo7GZ6I9AsDEr0Rxf3WTE1Ncf36da1cvVkul2PPnj2YOXMmLl++rHTO0NAQw4cPx8yZMzmM9P+eP3+OU6dO4ejRozh8+DDi4+Nfe33t2rXRrVs3dOvWDW3bttWojYWJiEqDAYn+06NHj+Dk5ITnz58DAL7++mvMnz9f4qreTVFREbZv345Zs2bh+vXrSueMjY3x0Ucf4dtvv4WDg4NEFWqmW7du4fDhwzh48CBOnDhRYo88RWZmZmjXrh26d++Obt26ce0lItIqDEj0nyZOnIhffvkFAFChQgUkJCSofL81qRQWFmLjxo2YM2cOEhMTlc6ZmZnh448/xpQpU1CpUiWJKtRceXl5CAsLw6FDh3Dw4MESd+TEXF1d0aNHD/To0QO+vr5at3ceEekXBiR6rdu3b6NevXrIz88HACxatAhffvmlxFWpXn5+PtauXYvAwMAST3VZWlpiwoQJmDx5MipUqCBRhZrv3r17OHToEA4cOICjR48KdxxfxtbWFl27dkXPnj3RpUsXnQncRKQ7GJDotUaOHIk1a9YAAGrUqIH4+HiYmZlJW5Qa5ebm4s8//8R3331XYs8za2trfPHFF/jiiy9gY2MjUYXaIS8vD2fOnMH+/ftx4MCBEsOYioyMjNCqVSu89957eO+991CnTp0yrJSI6OUYkOiVYmNj4ebmJuxztmrVKowaNUriqspGVlYWli1bhh9++AFpaWlK52xtbfH1119jwoQJsLS0lKhC7ZKQkID9+/dj3759OHXqlHBH8mUaNmwohCVvb2+NXWeLiHQbAxK9UkBAALZs2QIAqF+/Pq5evap380aeP3+OJUuWYMGCBSXWB6pYsSKmTp2KcePGcS2gUnj+/DmOHDmCffv2Yf/+/Xj06NErr61SpQp69uyJ3r17w9/fX6fvXhKRZmFAopeKiYlBkyZNhM1fN2/ejICAAImrks7Tp0+xePFiLF68uMTcGgcHB3z77bcYPXo0TE1NJapQOxUWFuL8+fPYs2cP9uzZg2vXrr3y2nLlyqFr167o3bs3unfvzmFOIlIrBiR6qX79+mHHjh0Aioc8Ll++zKEOAOnp6ViwYAGWLFmCrKwspXO1atXCjBkzMGzYMBgbG0tUoXZLSEjAnj17sHv3bpw5cwaFhYUvvc7Y2Bj+/v7o06cPevXqhSpVqpRxpUSk6xiQqISYmBg0btxY6G/btg39+vWTsCLN8+DBA/zwww9YtmwZcnNzlc7VrVsXs2fPRkBAADd5fQfp6enYv38/du/ejUOHDpUIpP+SyWTw8/ND37590adPH663REQqwYBEJQwcOBBbt24FADRq1AgXL17k3aNXuHfvHr777jusXLmyxMRjV1dXzJ49G++//z7/+72j7OxsHDt2DP/88w/27NlTYuK8Ind3d7z//vt4//334eLiUoZVEpEuYUAiJbGxsWjYsKEw92jLli0YMGCAxFVpvqSkJAQFBWHt2rUlhoWaNGmCwMBA9OzZk/uUqUBhYSFCQ0Oxa9cu7Nq1C7dv337ltQ0bNsT777+Pfv36wc3Njf/9ieiNMSCRkuHDh2PdunUAgAYNGiAmJobDRKVw48YNzJkzB5s2bYL4W6tZs2aYO3cuOnbsyF/UKiKXyxEVFYVdu3Zh586diI2NfeW19evXR79+/dCvXz80bdqU/w+I6LUYkEhw+/Zt1K1bV9i9ff369RgyZIjEVWmnq1evYvbs2di+fXuJcy1btsTcuXPRpk0bCSrTbbGxsdixYwd27NiBixcvvvK6unXrol+/fujfvz/c3d0ZloioBAYkEnz22WdYsmQJAMDR0RE3btzQu3WPVC06OhozZ87Evn37Spxr3749goKC4OvrK0Flui8hIQE7d+7E9u3bcf78+Vde5+TkhAEDBmDAgAFo0qQJwxIRAWBAov+XlpaGmjVrIjs7GwCwdOlSfPzxxxJXpTvOnTuHGTNm4OjRoyXOde/eHUFBQXB3d5egMv1w+/Zt7Ny5E9u2bUN4ePgrr6tXrx4GDhyIgQMHws3NrQwrJCJNw4BEAIDAwEDMmjULQPEK0bdv3+bq0GoQHByM6dOnIyQkpMS5vn37Ys6cOfzFrGbJycnYsWMHtm3bhrCwsFde5+rqioEDByIgIAD169cvwwqJSBMwIBGys7NRq1YtYcuHwMBAzJgxQ+KqdJdcLsexY8cwY8YMnDt3TumcTCZDQEAAZs+ezV/KZeDu3bvYvn07tm7d+to7S+7u7ggICMDAgQNRq1atMqyQiKTCgERYuXIlPvroIwCAubk5kpOTYWdnJ3FVuk8ul2P//v2YMWNGiQnFBgYGGDZsGGbOnMmFD8vInTt3sHXrVmzZsgUXLlx45XV+fn4YNGgQ+vfvj8qVK5dhhURUlhiQ9JxcLoebm5uwB9bHH3+MpUuXSlyVfikqKsKuXbswc+bMEnuRGRkZYfTo0Zg+fTqqV68uUYX6JyEhAVu3bsXff/+Ny5cvv/QaAwMDdOjQAYMGDUKfPn24NxyRjmFA0nNHjhxB586dhf7169fh7OwsYUX6q7CwEFu2bMGsWbNw8+ZNpXOmpqYYN24cpkyZwn3HylhsbCy2bNmCzZs3Iz4+/qXXmJqaokePHhg8eDC6desGMzOzMq6SiFSNAUnP9ezZU3gEvVu3bti/f7/EFVFBQQHWrVuHwMDAEqtEm5ubY8KECZg8eTLs7e0lqlA/yeVyREdH4++//8bff/+N5OTkl15nY2ODfv364YMPPkCbNm24zQyRlmJA0mMJCQmoV6+esOLz4cOH0alTJ4mron/l5eVh9erVCAoKwv3795XOlStXDl988QW+/PJLlC9fXpoC9VhRURFCQ0OxefNmbN26Fenp6S+9rlq1ahg8eDCGDh2KRo0alXGVRPQuGJD02KRJk7Bo0SIAgIuLC65du8ZF8jRQdnY2li9fjnnz5uHhw4dK58qXL49Jkybhs88+g5WVlUQV6rf8/HwcPXoUGzduxD///IOsrKyXXte4cWMMGTIEgwcPRrVq1cq4SiIqLQYkPZWdnY1q1arhyZMnAIAlS5Zg/PjxEldFr5OZmYnffvsNP/74Ix4/fqx0zt7eHt988w0++eQTWFhYSFQhZWZmYvfu3diwYQOOHDlSYuNioHgph/bt22Po0KHo27cvypUrJ0GlRPRfGJD01Jo1azBy5EgAgKWlJe7du8encLTEs2fP8PPPP2PRokV49uyZ0rkqVapg2rRpGDNmDExNTSWqkADg4cOH2Lp1KzZs2FBivat/WVhYoG/fvhg2bBj8/f25MTSRBmFA0lN+fn7CwnhjxozB8uXLJa6ISuvx48dYuHAhfv31V2RmZiqdq1GjBmbMmIERI0bA2NhYogrpX/Hx8diwYQM2bNiAW7duvfQaBwcHDBkyBMOHD4erq2sZV0hEYgxIeigmJgaNGzcW+pGRkfDw8JCwInoXDx8+xPz587F06VLk5OQonatTpw5mzpyJDz74gBsPawC5XI7Q0FCsX78eW7duxdOnT196nZeXF4YPH45BgwZx0VYiiTAg6aHPPvsMS5YsAQB4enq+dtVg0h7379/HvHnzsHz5cuTn5yudc3Z2xpw5c9C/f38+dq4hcnJysG/fPqxbtw4HDx5EQUFBiWuMjY3x3nvvYcSIEejSpQtDLlEZYkDSMzk5OXBwcBAmZ//xxx8YO3asxFWRKt2+fRtz587FX3/9VWKScKNGjTBnzhz07t2bTyxqkIcPH2Lz5s1Yt24doqKiXnpN5cqVMWTIEIwcORINGzYs4wqJ9A8Dkp7ZunUrBg4cCKB40cGUlBROztZRN2/eRGBgIDZs2ADxt7mnpyeCgoLQpUsXBiUNExMTg7Vr12LDhg148ODBS6/x8vLCqFGjMGjQIK6DRaQmDEh6pmvXrjh06BAAYNiwYVi7dq3EFZG6xcbGYtasWdi2bVuJc76+vpg7dy78/f0lqIxep6CgAEeOHMGaNWuwe/du5OXllbjG1NQUffv2xahRo+Dv78/hUyIVYkDSIykpKahevTqKiooAACdPnkTbtm2lLYrKzKVLlzBr1izs3r27xLl27dohKCgILVq0kKAy+i+PHz/G5s2b8ddffyEyMvKl19SqVQsjR47EiBEjUKtWrTKukEj3MCDpkYULF2Ly5MkAin+YJiYm8hOnHoqIiMCMGTNw+PDhEue6dOmCoKAgeHl5SVAZvYmYmBj89ddf2LBhAx49elTivEwmQ4cOHTB69Gj07t2b62ERvSUGJD3SpEkTXL58GQAwffp0BAUFSVwRSenMmTOYMWMGTp06VeJcr169EBgYqLQcBGmWvLw87N+/H6tXr8aBAweEO8OKKlSogGHDhmH06NFwc3OToEoi7cWApCeuXLmitFlmXFwc6tevL2FFpAnkcjlOnDiBGTNmCAuHKhowYABmz56NBg0aSFAdvan79+9j/fr1WLVqFW7cuPHSa3x8fPDhhx9i4MCB3N6E6A0wIOmJadOmYd68eQCKn4CJiIiQuCLSJHK5HAcPHsSMGTNKPGZuYGCADz74ALNmzYKTk5NEFdKbkMvlCAkJwapVq7Bt2zZkZ2eXuMbKygqDBg3CRx99xKFUotdgQNIDcrkcTk5OwhYHP/30E7744guJqyJNJJfLsXv3bsyYMQNXrlxROmdkZISRI0di+vTpqFmzpkQV0pvKyMjA5s2bsXLlyldO7HZ3d8eYMWMwePBgWFtbl3GFRJqNAUkPREREwNvbG0DxBM7k5GRUq1ZN4qpIkxUVFWHr1q2YPXs24uLilM6ZmJjgo48+wrRp0+Dg4CBRhVQa0dHRWLlyJTZs2FBig2OgeNPcQYMGYcyYMWjWrBnXxiICA5JemDx5MhYuXAgAaNWqFYKDgyWuiLRFQUEBNm7ciDlz5pTYZNXMzAyffvopvvnmG1SsWFGiCqk0srKysG3bNqxYsQJhYWEvvaZp06YYO3YsPvjgA1hZWZVxhUSagwFJx8nlctSpUwdJSUkAgN9++w2ffvqptEWR1snPz8dff/2FoKAg3L17V+mcpaUlPv/8c0yaNAm2trYSVUildfXqVaxcuRJr164Vth5SVK5cOQwePBjjxo2Du7u7BBUSSYsBScdFRkYKEzFlMhnu3buHqlWrSlwVaaucnBysWLEC33//fYltMGxsbPDVV1/h888/53wWLZKTk4Pt27djxYoVCAkJeek1zZs3x7hx4zBw4ECYm5uXcYVE0mBA0nHffvstvv/+ewBAixYtcObMGYkrIl2QlZWFpUuX4ocffkB6errSuQoVKuCbb77Bp59+CktLS4kqpLdx7do1rFixAmvXrsXTp09LnLe1tcWIESMwbtw4LhNCOo8BScc1bNgQ165dAwAsWrQIX375pcQVkS55/vw5fvnlFyxcuBAZGRlK5ypVqoRp06Zh7NixMDMzk6hCehv/zlVatmwZzp0799JrOnTogI8//hjvvfcejIyMyrhCIvVjQNJhN27cUPqUl5CQgDp16khYEemqJ0+e4KeffsLPP/+MFy9eKJ2rVq0apk+fjlGjRsHExESiCultRUdHY/ny5diwYQMyMzNLnK9WrRrGjh2LDz/8kMP3pFMYkHTYokWLMGnSJACAm5sbYmJiJK6IdF1aWhrmz5+P33//vcQihY6Ojpg5cyaGDh3KOw5a6NmzZ9iwYQOWLl2Kq1evljhvZGSE999/H59++ilatmzJpQJI63GnUh22b98+od2rVy8JKyF9YW9vjwULFiAxMRGfffaZ0h2jpKQkjBo1Cq6urti0aRMKCwslrJRKy9raGp988gliYmIQHByMgIAAGBsbC+cLCgqwZcsWtG7dGk2bNsWKFSteeseJSFvwDpKOevr0Kezt7YVfQuHh4fDx8ZG4KtI3ycnJmDt3LlavXo2CggKlcw0bNsScOXPQp08fGBjws5o2Sk1NxapVq/DHH3+UWP4BKH6ycdSoURgwYADy8vLQunVrCaokejsMSDpq27ZtGDBgAIDiT/WpqakwNDSUuCrSV4mJiQgMDMT69etL7Drv7u6OwMBAdO/encMyWqqgoAD79u3Db7/9huPHj7/yur1796JHjx5lWBnR2+PHNh118OBBod2lSxeGI5JUnTp1sGbNGly7dg0BAQFKQSg6Oho9e/aEr68vjh49Cn5m0z5GRkbo3bs3jh07hmvXrmHs2LEvnZD/xx9/SFAd0dthQNJBcrkchw4dEvpdu3aVsBqi/3F2dsbmzZtx6dIl9OnTR+ncuXPn0KlTJ7Rt2/aVCxaSZsvLy8OJEyewe/du5OXllThfuXJlCaoiejsMSDro6tWrSElJAVC8enbHjh0lrohIWaNGjbBz505cuHAB3bp1UzoXHByM1q1bo1OnTq9cg4c0S2FhIdatWwdnZ2eMHz8eqampwjnFidwMSKRNGJB00NGjR4W2u7s7NxIljeXp6Yn9+/cjLCwM/v7+SueOHj0KHx8fvPfee7h48aI0BdJryeVy7Nq1C40bN8bw4cOFPR+B4s2Mv/76a3z11VfCsbi4OAmqJHo7DEg6SHGSJO8ekTbw9fXF8ePHcfLkSbRo0ULp3N69e+Hu7o7+/fsLq8KT9E6cOAFfX1/07dtX6f+LkZERxo0bh4SEBMyfP19po1sGJNImDEg6Jj8/H6dPnxb6HTp0kLAaotL5d/7RoUOH0KxZM6Vz27dvh5ubG4YMGYIbN25IVCGdP38eHTp0QPv27ZWGQGUyGQYPHozY2FgsW7YMDg4OAKC0mv/NmzdLPMVIpKkYkHRMZGSksNWDiYkJ/Pz8JK6IqHRkMhk6d+6Mc+fOYffu3WjcuLFwTi6XY+PGjWjQoAFGjx6tNKRD6nX16lX06dMHzZs3L/Eof8+ePXHp0iVs3LgRdevWVTpXr149oZ2bm4vk5OQyqZfoXTEg6RjFu0c+Pj6wsLCQsBqityeTyfDee+8hOjoaW7ZsgYuLi3CusLAQq1evRv369fHJJ5/g3r17Elaq25KSkjB8+HA0atQI//zzj9K5Nm3aICwsDHv27EGjRo1e+vWWlpaoXr260I+Pj1dnuUQqw4CkYxQDUps2bSSshEg1DAwMMGDAAFy5cgXr1q2Dk5OTcC4/Px/Lli2Dk5MTGjdujO3bt+PevXvIysqSsGLd8ODBA0yYMAH169fHunXrlNan8vDwwOHDh3Hy5En4+vr+52sp3kViQCJtwR0jdUhhYSFCQ0OFPpf1J11iaGiIoUOHIiAgAGvXrkVQUBDu3LkDoHjoJiYmBv379xeut7CwgIODA2rUqIE6deqgbt26cHFxgaurK5ycnLh46is8ffoUCxYswM8//1wiaDo7O2Pu3Ll4//33S7Xqef369XHy5EkA4Pwx0hrcakSHXLp0CU2bNgVQ/Mvk6dOnKFeunLRFEalJbm4uVq5cicDAQDx8+LBUX2tubg43Nzd4eHjAy8sL3t7eaNiwoV6HpszMTCxZsgTz58/H06dPlc7VqFEDs2fPxrBhw2BkVPrP1YsWLcKkSZMAFC9ce+DAAVWUTKRWvIOkQ8LCwoR206ZNGY5Ip5mamuKTTz7B1q1bSx2QsrOzERERgYiICCxfvhwAUK5cOXh7e6NFixZo3bo1fH19YWlpqY7SNUpeXh7+/PNPzJ07V2mBRwCoWLEipk2bhnHjxsHMzOyt30NxiI13kEhbMCDpkPDwcKEtXkuGSBetWbMGwcHBQn/q1KmYOXMmnjx5ggcPHuDevXu4ffs2EhIScOPGDVy7dg2JiYkv3e/txYsXOHHiBE6cOAGgeD0fb29v+Pv7o0OHDvD19X3p/mLaqrCwEJs2bcKsWbNw69YtpXPW1taYPHkyPv/8c1hZWb3zeykGpKSkJOTn5yutsE2kiTjEpkPq168vfDrbtGkTBg0aJHFFROrz+PFj1K9fH+np6QCAXr16YdeuXf85NyYrKwtXr15FdHQ0IiMjERERgcuXL6OwsPC1X2dpaYl27dqhS5cu6Nq1K+rUqaOyv0tZksvl2LNnD6ZPn44rV64onTMzM8P48eMxZcoU2NnZqew9c3JyYGFhIQTTmzdvKk22J9JEDEg6Ij09Hfb29kI/MTERtWvXlrAiIvX6/PPP8euvvwIoDi+xsbGoUaPGW71WVlYWLly4gNDQUISEhCA0NBTPnj177dc4OzujR48e6NmzJ1q0aPFWc3PK2okTJzBt2rQSe9wZGRlh9OjRmDlzprDAo6rVqlVLmFR/6NAhdO7cWS3vQ6QqDEg64vDhw+jSpQsAwN7eHg8fPizVUyZE2uTmzZto0KABCgoKAADz5s3DlClTVPb6hYWFiI6OxvHjx3H8+HGEhIQgJyfnlddXqFABPXr0QJ8+fdC5c2eYm5urrBZViIiIwLRp03Ds2DGl4zKZDAEBAQgMDCyxwKOqtW/fXhi+XLJkCcaPH6/W9yN6V1wHSUdcuHBBaDdr1ozhiHTarFmzhHDk6OiIiRMnqvT1DQ0N4eXlhW+++QZHjhzBkydPcOTIEXz11Vdo0KBBiesfP36MdevWoU+fPrC3t8eAAQOwdetWZGZmqrSu0rp27Rr69u0Lb2/vEuGoR48euHjxIjZt2qT2cARAaUgtISFB7e9H9K4YkHSEYkDy8vKSsBIi9bp+/To2b94s9IOCgt7pCas3YWZmho4dO2LhwoW4du0abt26hSVLlqBTp04lJhtnZWVh27ZtGDhwICpWrIgBAwZg586dr70DpWpJSUkYMWIEGjVqhF27dimda9OmDUJDQ7F3716lbVzUjQGJtA0Dko6Ijo4W2h4eHhJWQqRe8+fPFyb7uri4SPIwgqOjI8aPH4/Dhw8jLS0NW7ZsQUBAQIknvrKzs7Ft2za8//77qFy5MkaOHIljx47954Twt/XgwQN89tlnqF+/PtauXau0MayHhwcOHTqEkydPSrJHIwMSaRvOQdIBT548QYUKFYR+UlISatWqJWFFROqRkpKCWrVqIT8/HwCwbt06DB06VOKq/ic3NxdHjx7F9u3b8c8//yAjI+Ol1zk4OGDw4MEYPnw43Nzc3vl91bH6tapFR0cLH97MzMyQlZXFqQCk0RiQdMDp06fRtm1bAICtrS3S09P5g4d00uzZszFnzhwAQPXq1ZGYmKix6+nk5ubiyJEj+Pvvv7F79+5Xzkfy9PTEyJEjMXjwYNja2pbqPdS5+rWqZWRkoHz58kL//v37qFq1qnQFEf0HDrHpgMuXLwvtxo0bMxyRTiooKMDKlSuF/qeffqqx4QgoXum7Z8+e2LhxIx4+fIjNmzejR48eJcJKZGQkxo8fj6pVq2Lw4ME4ceKE0tDYy+Tl5WHZsmWoW7cupk6dqhSO7O3tsXjxYsTHx2PUqFEaEY4AwMbGRmltpcTERAmrIfpvDEg6ICYmRmg3atRIwkqI1Ofo0aO4d+8eAMDY2BijRo2SuKI3Z2FhgYCAAOzduxf379/Hr7/+Ck9PT6VrcnNzsXnzZrRv3x7Ozs5YsGABHj16pHRNYWEhNmzYgAYNGuCTTz5R2hrE2toac+bMQWJiIiZOnKj2ietvQ3FxTQYk0nQMSDrg6tWrQpsBiXTVhg0bhHaPHj1QqVIlCat5exUrVsSECRNw4cIFxMTE4KuvvkLFihWVrrl58ya+/vprVKtWDR988AHOnDmDf/75B02aNMHQoUOVwoWZmRkmTZqEhIQEzJw5UyVbg6iLYkDiRG3SdAxIWk4ul+PatWtC39XVVcJqiNQjJycHe/bsEfpDhgyRsBrVcXNzw8KFC3H37l3s2LEDXbt2VRoiz8/Px6ZNm9CqVSv06dNH6cOQoaEhxowZg5s3b2LBggVKK+lrKsXV/cX7vxFpGgYkLffgwQOl+QcvW8SOSNudOHECL168AFC8rUjXrl0lrki1TExM0LdvXxw4cABJSUmYPn36aycwBwQEIDY2FsuXL0e1atXKsNJ3w4BE2oQBSctdv35daNvb26t0g0kiTbF//36hrYlbeahSzZo1ERQUhNu3b2Pbtm1Ke6NZWFjg4sWL2Lx5M+rVqydhlW+HAYm0iWY83kBvLS4uTmg7OztLWAmR+ihuk6Frd49exdjYGP369UP58uXRsWNHAICVlRWaNGkicWVvTzEg3bt3D3l5eTAxMZGwIqJX4x0kLXfz5k2hXb9+fQkrIVKP1NRUxMfHC/327dtLWE3ZU/y+fvDggTDUqI1q1qwpzLGSy+VITk6WuCKiV2NA0nKKT4KUxYaTRGUtPDxcaFerVg2Ojo7SFSOB6tWrK91l0ebH401MTJTmTCUlJUlXDNF/YEDScooBSXGvIyJdERkZKbR9fHz0biFUAwMDpaEpbQ5IAJQCLuchkSZjQNJicrlc6QeM4g9RIl2huFK8u7u7hJVIR5cmNyvuE3n79m0JKyF6PQYkLfbkyRM8f/5c6DMgkS6KjY0V2g0bNpSwEuko3nXR9lDBgETaggFJi925c0dom5uba8VCcUSlUVhYqDRPRRsfbVcFxVCh+H2vjXQp7JFuY0DSYoo/KBWfDiHSFampqSgoKBD6+jZB+181a9YU2toekHgHibQFA5IWu3v3rtCuXr26hJUQqceDBw+EtrW1NSwtLSWsRjqK39+K3/faSDHs3b17F4WFhRJWQ/RqDEha7P79+0Jbm7YbIHpTjx8/FtoVKlSQsBJpKX5/P3z4EPn5+RJW824UA1JhYaHSzzEiTcKApMUYkEjXKS6KaG1tLWEl0lLcbkQulyvdWdM2FhYWSvMlOcxGmooBSYulpqYK7SpVqkhYCZF65ObmCm1TU1MJK5GWubk5bGxshH5KSoqE1by7GjVqCG2upk2aigFJiyl+iqxcubKElRCph1wuF9r6/hCC4ocgxQ9H2khxmI0BiTQVA5IWe/jwodCuVKmShJUQqYexsbHQ1uZ5N6qg+CHo0aNHElby7ngHibQBA5IWS0tLE9oVK1aUsBIi9VB8ak2bN2lVBcXvcQYkIvVjQNJSWVlZyMnJEfp2dnYSVkOkHra2tkJb8Yk2faT4Pa744UgbKQYkbV+2gHQXA5KWevLkiVJfnx+BJt2lOKyUnp6uNGlb3ygGJG0Pi4rrOvEOEmkqBiQtpRiQzM3N9foJH9JdDg4OSpOz9fmXqeKHIG0PSIp3kB4+fKjXwZc0FwOSlsrIyBDa5cuXl64QIjUyMTFR+mUaHx8vYTXSUvw+f/r0qWR1qILiuk4AuFgkaSQGJC317Nkzoa3PC+iR7nN1dRXaMTExElYiLcV1kBQ/IGkjExMTpeHTe/fuSVgN0csxIGkpxYBkZWUlYSVE6tWkSROhHRkZKWEl0lL8IPT8+XMJK1ENxdX/OVGbNBEDkpbKysoS2vq6gSfpB29vb6EdGhqqtHikPilXrpzQ1oUlDxQDEu8gkSZiQNJSigHJwsJCwkqI1Ktly5ZC+/79+3o7D0nxg1BmZqaElagGAxJpOgYkLZWdnS20zc3NJayESL0qVaqExo0bC/0DBw5IWI10FL/PFb//tRUDEmk6BiQtxU08SZ90795daO/cuVPCSqRjZmYmtAsKClBYWChhNe9O8Uk2BiTSRAxIWooBifRJ3759hfaZM2dw584dCauRhvj7PC8vT6JKVEMxIKWkpEhYCdHLMSBpqYKCAqFtZGQkYSVE6ufp6Yl69eoJ/TVr1khXjEQUN+4FtH/zXnFA0tfJ96S5GJC0FAMS6ROZTIaRI0cK/eXLl2t9QCgt8fe54s8AbaQYkLKzs7V+8UvSPQxIWqqoqEhoGxjwfyPpvlGjRgl3Ue7fv4+///5b4orKlvj7XPFngDays7NTuivGYTbSNPzNqgMU96oi0lWVK1fGBx98IPS///57rZ+orM9kMhmqVKki9FNTUyWshqgkBiQdwLF70hfffPON8IHg+vXrWLduncQVlR3x97kufDCqWrWq0OYdJNI0DEhaSvF2OwMS6QsXFxelu0jffvutTmy78SbEd8sMDQ0lqkR1GJBIkzEgaSnFgMRhBtInc+fOFR55T0lJwcyZMyWuqGyIJ6WLn2rTRhxiI03GgKSlFH84avvTLESlUatWLUyZMkXo//LLLwgLC5OworIhXvfIxMREokpUhwGJNBkDkpZS/OGouGgkkT6YMmUKnJ2dARQPMQ8dOhTPnj2TuCr1UtxexMDAgHeQiNSMAUlLMSCRPjMzM8OaNWuEoebExESMGDFCp9dGUtygWnHjWm1WuXJlof3gwQMJKyEqiQFJS+naxpVEpeXj44NZs2YJ/V27dqFmzZoSVqReipPRy5UrJ2ElqsOARJqMAUlLKQYkxU+WRPrk22+/haenp9BPTU1Fnz59JKxIfTIyMoS2tbW1hJWojmJASktL43xK0igMSFpK8RMkAxLpq507d+L69etKx3bv3q2Tu8M/efJEaNva2kpYieooBiS5XI60tDQJqyFSxoCkpaysrIS2vqwDQ/SvvLw8TJw4EQMGDEBmZqbSOblcjoCAAK3f7V5MMTzY2dlJWInqWFpaKt0Nf/jwoYTVECljQNJSigFJ15/eIVKUnJyMNm3a4JdffhGO2djYYNiwYUL/zJkzGD9+vE4toqoYHipVqiRhJaojk8mU/i4MSKRJGJC0VPny5YW24twEIl129OhReHh44OzZs8KxJk2a4MKFC1izZg1GjBghHP/zzz/x/fffS1Clety/f19oKz4er+0YkEhTMSBpKcWA9Pz5c05uJJ1WVFSEoKAgdO7cWWmoadSoUQgPD0fdunUhk8mwbNky+Pr6CuenT5+O33//XYqSVe7u3btCu3r16hJWoloVK1YU2o8ePZKwEiJlDEhaqkKFCkr9x48fS1QJkXqlpaWhW7dumDlzpjBkZmZmhlWrVmHVqlVKc1jMzMywe/du1KtXTzg2fvx4/Pbbb2Vet6rdvn1baOvScgaKd5AYkEiTMCBpqXLlyimtpJueni5hNUTqcf78eXh4eODw4cPCMScnJ4SHh2PUqFEv/ZqKFSvi6NGjqFGjhnBswoQJCAwM1No5SQUFBUoBqXbt2hJWo1q8g0SaigFJS8lkMv5gIZ0ll8vx+++/o2XLlkhOThaO9+7dGxcuXEDTpk1f+/W1atXCqVOnlO60zJo1C6NGjdLKledv3bolrBIuk8ng5OQkcUWqw59jpKkYkLQYJzeSLnrx4gU++OADjB8/XggFhoaGWLBgAXbu3Kk0/+516tSpg5CQEGHPNgBYs2YNWrdurXQ3RhtcvXpVaNeuXRtmZmYSVqNa9vb2QpvrIJEmYUDSYoqLrHGjR9IFsbGx8Pb2xubNm4VjVatWxcmTJzFp0iTIZLJSvV7NmjURGhqKVq1aCcfOnz+Ppk2bYtu2bSqrW90uXboktBs1aiRhJaqnGJB4B4k0CQOSFqtatarQTklJkbASonf3999/o1mzZoiNjRWOtW3bFlFRUUoBp7Ts7Oxw7NgxjBkzRjj29OlTDBgwAAMHDtSKDxfnz58X2h4eHhJWonq8g0SaigFJizk4OAhtXdxagfRDbm4uJkyYgEGDBimtij1lyhQcPXpUJWv+mJiYYPny5Vi3bp3SNj1bt26Fs7Mzfv75Z41debuoqAjh4eFCv3nz5hJWo3qKAenx48coKiqSsBqi/2FA0mLVqlUT2oprpBBpizt37qB169ZKj+GXL18ee/bswbx582BkZKTS9xs6dCguXryIli1bCseePXuGL774Aq6urli/fr3GrSkWHR0t7MNmaGiotM6TLlDcNqWoqAhPnz6VrhgiBQxIWkzxMeY7d+5IWAlR6R0+fBju7u5Kw0fu7u6IiopCz5491fa+Tk5OOH36NJYuXQobGxvheEJCAoYNGwZnZ2f89ttvGrPH4YEDB4S2t7c3rK2tJaxG9cqXLw8Dg//9KuKSJaQpGJC0mOIjzMnJyVq7xgvpl8LCQsyaNQtdu3ZVWuD0ww8/RFhYWJms8WNgYICPP/4YN27cwLhx42BoaCicS0xMxIQJE+Dg4IAxY8YgJCREkmGf/Px8/PzzzwgKChKO9ejRo8zrUDcDAwPY2toKfS56S5pCJudvVa2VkZGh9MhzSkqKTu3RRLrn0aNH+OCDD3D06FHhmLm5OZYtW4bhw4dLVteNGzcQFBSETZs2obCwsMR5BwcH9OrVC127dkWbNm3UchdHLpcjMTERJ0+exJEjR3DkyJES+yxevnxZ555iAwBnZ2fEx8cDAPbv349u3bpJXBERA5LWq1ChgjA/ITQ0FH5+fhJXRPRy4eHhGDBggNJ8uXr16mH79u1o3LixhJX9T1JSEn799VesXr36lZtAGxgYoEmTJvD29kbTpk3h6uqKevXqoXLlykpDRa/z7Nkz3Lp1C/Hx8bhy5Qqio6Nx/vx5PHjw4LVft3HjRgwePLjUfy9N5+fnJ0xEX7duHYYOHSpxRUSAamdAUplzcnLChQsXABTPoWBAIk0jl8vx66+/YtKkSUoToPv164dVq1Zp1JwaR0dH/PTTT5g7dy527NiBdevW4cSJE0pDbEVFRYiOjkZ0dLTS15qYmKBKlSqws7ODtbU1zM3NYWRkhKKiIuTk5ODFixd4/PgxHjx48Mbzm8zMzGBiYoJnz54B0N35OYp7S/77gY9IagxIWq5u3bpCQLpx44bE1RApe/78OUaPHq20KKORkRF+/PFHTJw4sdQLP5YVCwsLDB06FEOHDsWjR4+wb98+HDhwAMePH3/lL/C8vDzcuXPnnR+YcHFxgb+/Pzp37oz27dtj5syZ+OmnnwAAp0+fxoQJE97p9TWR4hwkBiTSFAxIWk5x1/J/x/CJNMGVK1fQr18/xMXFCcccHBywdetWtGjRQsLKSqdixYoYOXIkRo4ciaKiIly9ehVnz55FZGQkYmJiEBcX91Z3dkxNTeHk5IQGDRqgcePG8PT0hLe3t9LeZEDxYpn/BqTg4GDI5XKNDZZvS/EOEidpk6ZgQNJyLi4uQvv69esSVkL0Pxs2bMDYsWORlZUlHGvfvj02bdqktIegtjEwMECjRo3QqFEjfPTRR8LxjIwM3L17Fw8ePEB6ejqeP3+OnJwcFBQUwMDAAKampihXrhxsbW1RsWJFODg4vPGcpZYtW0Imk0Eul+PRo0eIjY2Fq6urOv+aZU7xYRPeQSJNwYCk5RQDUlxcHAoLC5UeWSYqS7m5uZg4cSL++OMPpePffvst5syZo7P/Nm1sbGBjY4OGDRuq/LVtbW3RuHFjYT+206dP61xA4hAbaSKug6TlXFxchNvtOTk5SEpKkrYg0ltJSUlo0aKFUjiytbXF/v37MXfuXJ0NR2WhTZs2Qjs4OFjCStSDAYk0EQOSlrOwsICjo6PQv3LlinTFkN46cOAAPDw8EBkZKRxr1qwZoqKiuKaNCigGpNOnT+vcorCKAelVyysQlTUGJB2guIZMTEyMhJWQviksLMT06dPRvXt3pU/+H3/8MUJCQpTCO729Vq1aCe2UlBTcvHlTwmpUj3OQSBMxIOkAxYD07zwFInV7+PAhOnXqhO+++044ZmFhgY0bN2Lp0qUwNTWVsDrdUrFiRaX5TadPn5awGtVT3BOPm9WSpmBA0gFNmjQR2uLF64jU4cyZM3B3d8eJEyeEYy4uLoiIiNDJlZ41QevWrYW2rgUkxTtImZmZSguKEkmFAUkHuLu7C+2EhASO4ZPayOVy/PTTT2jbti3u378vHB84cCDOnz+vc09XaRLFgKRrE7UV7yABEFYOJ5ISA5IOqF27ttInMMWJskSqkpGRgX79+uGrr74SNnQ1NjbGr7/+is2bN8PKykriCnWb4kTtO3fu6NQTq+J/OxxmI03AgKQDZDIZPD09hX5ERISE1ZAuunz5Mry8vLBz507hWI0aNRAcHIwJEybo3MrOmqhq1apKK+fr0jCboaGhUkh6073qiNSJAUlHeHt7C+3z589LWAnpmjVr1qB58+ZKT0516tQJUVFR8PHxkbAy/SN+3F+XKG5azCE20gQMSDqiefPmQjs8PFzn1kmhspeTk4OPPvoII0eORE5ODoDiu5WzZs3CgQMHYG9vL3GF+kdfAhLnUZIm4FYjOkLxk3xKSgqSk5NRs2ZNCSsibZaYmIh+/fopPRVpZ2eHjRs3onPnzhJWpt8UA1JiYiLu3r2L6tWrS1iR6nCIjTQN7yDpiMqVK6NOnTpC/8yZMxJWQ9psz5498PDwUApHzZs3R1RUFMORxGrUqIHatWsLfV26i8SARJqGAUmHKK62y4BEpVVQUIApU6agV69eSkMc48ePR3BwMO9IaghdfdxfMSBxDhJpAgYkHdKyZUuhrUufLEn9UlNT0aFDB8yfP184Zmlpic2bN2PJkiUwMTGRsDpS1LZtW6GtS9/nigHpxYsXElZCVIwBSYco/uC8du0aHj58KF0xpDWCg4Ph7u6u9MvW1dUVERERCAgIkLAyehnFO0hxcXF48OCBhNWoDgMSaRoGJB3i5OSEatWqCf2TJ09KWA1pOrlcjgULFsDf3x+pqanC8cGDB+PcuXNo0KCBhNXRq9SuXVtpYrau3EUqV66c0OYcJNIEDEg6RCaTwd/fX+gfP35cwmpIkz19+hR9+vTB119/rbQq9u+//44NGzYo/bIizSKTyZSeZtOVeUiK/+YyMzMlrISoGAOSjunQoYPQPnbsGNdDohIuXrwILy8v7N69WzhWs2ZNhIaG4pNPPuGq2FpAF9dDUgxIHGIjTcCApGMUA9KtW7eUVj8mWr16NXx9fZGQkCAc69q1K6KiotCsWTMJK6PSUAxIV65cQVpamoTVqIalpaXQ5hAbaQIGJB3j4OAANzc3oX/o0CEJqyFNkZ2djVGjRmH06NFKq2IHBQVh3759sLOzk7hCKo169eqhSpUqQj8kJETCalRDMSBlZWVJWAlRMQYkHdSlSxehfeDAAQkrIU1w48YN+Pj44K+//hKO2dvb48iRI5g+fToMDPhjQNvIZDKlp9l0YZhNMSBxDhJpAv5k1EHdunUT2idPnuQPGz22a9cueHl54fLly8IxPz8/REdHKw3HkvbRtXlIFhYWQps/s0gTMCDpoJYtWwobP+bm5uLYsWMSV0RlLT8/H5MmTULfvn2VViX+4osvcOrUKZ3Zv0ufKQakS5cu4enTp9IVowKKd5Cys7MlrISoGAOSDjI2NlYaZlN8Wol03/379+Hv749FixYJx6ysrLBt2zb89NNPMDY2lrA6UhVXV1fY29sDKF7TStu3F1K8g8SARJqAAUlH9e7dW2jv3btXWOuGdNvJkyfh7u6u9MvSzc0NFy5cQL9+/SSsjFRNJpMp7b+o7cNs5ubmQpuTtEkTMCDpqG7dugl3CtLS0nTiKRd6taKiInz//ffo0KGD0hYzw4YNw7lz51C/fn0JqyN10aV5SIoBKS8vjx/qSHIMSDrKxsYG7du3F/rbt2+XsBpSpydPnqBXr1749ttvUVRUBAAwMTHB8uXLsWbNGqWhC9ItigEpKipKq9cPUgxIAITlKIikwoCkwxSHVHbs2MFPZDooMjISHh4e2Ldvn3DM0dERYWFhGDNmDFfF1nGNGjVC+fLlAQCFhYUICwuTtqB3IA5InIdEUmNA0mG9e/eGkZERACA1NZXDbDpELpdjxYoV8PPzQ1JSknC8e/fuiIyMhKenp3TFUZkxNDTUmXlIZmZmSn3eQSKpMSDpMDs7O3Ts2FHob968WcJqSFUyMzMxfPhwjB07Fnl5eQAAAwMDfP/999izZw8qVKggcYVUlnRlHpKRkZHSoqW8g0RSY0DScQEBAUJ727ZtyM3NlbAaeldxcXHw8fHB+vXrhWOVKlXCsWPHMHXqVK6KrYcUV9SOiIjQ2ifAZDIZTE1NhT5/VpHU+NNUx/Xp00cY23/y5Am3HtFi27dvR7NmzXDlyhXhWKtWrRAdHY127dpJWBlJyd3dHVZWVgCKFwg9e/asxBW9PQYk0iQMSDrOysoKvXr1Evrr1q2TsBp6G3l5eZg4cSL69++v9JTS5MmTcfz4cTg4OEhYHUnNyMgILVq0EPraPMzGgESahAFJDwwfPlxo79u3D48ePZKwGiqNu3fvom3btvjll1+EY9bW1ti5cyd+/PFHropNAHRnHpJiQPp3fh2RVBiQ9EDHjh2FuwwFBQXYsGGDxBXRmzh27Bjc3d0RHh4uHGvSpAkiIyPRp08fCSsjTaM4D+ns2bNa+wQY7yCRJmFA0gOGhoZKd5FWrlwJuVwuYUX0OkVFRQgKCkKnTp2QlpYmHB85ciTCw8NRt25dCasjTeTl5SXMNczNzcX58+clrujtmJiYCG3eQSKpMSDpiVGjRgnta9euafWCcrosPT0dPXr0wMyZM4UQa2ZmhlWrVmH16tUlFtMjAoqDhZ+fn9DX1mE2xSHj/Px8CSshYkDSG3Xr1lV60mn58uUSVkMvc/78eXh4eODgwYPCsTp16iAsLEwp4BK9jC7MQ1K8g8SARFJjQNIj48aNE9pbt25VGr4h6cjlcixduhQtW7bEnTt3hOO9e/dGZGQk3N3dJayOtIViQAoPD9fKISreQSJNwoCkR3r37o3KlSsDKJ6nsGrVKokrohcvXmDIkCH49NNPhV8IhoaG+PHHH7Fz505hny2i/+Lt7S1Mcs7KykJkZKTEFZXev1sjAQxIJD0GJD1iYmKCMWPGCP2lS5eioKBAwor0W2xsLJo3b45NmzYJx6pUqYITJ05g8uTJ3GiWSsXMzAw+Pj5CXxuH2XgHiTQJA5KeGTt2rPAp7c6dO9i9e7fEFemnLVu2oFmzZrh27ZpwrG3btoiOjlZ6ZJuoNBT/7WhjQDI0NBTahYWFElZCxICkd6pVq4Z+/foJ/cWLF0tYjf7Jy8vDZ599hoCAAGRmZgrHv/nmGxw9ehRVqlSRsDrSdorzkM6cOaN1d4gVh9i0rXbSPQxIeuiLL74Q2qGhoTh37pyE1eiPO3fuoHXr1liyZIlwrHz58tizZw9++OEHpV8ORG/D19dX+Hf04sULREdHS1xR6fAOEmkSBiQ95O3trbR304IFCySsRj8cPnwYHh4eSmHUw8MDkZGR6Nmzp4SVkS6xsLBAs2bNhH5wcLCE1ZSeYkAqKiqSsBIiBiS9NXnyZKG9c+dOxMfHS1iN7iosLMSsWbPQtWtXpKenC8c/+ugjhIaGok6dOhJWR7pIm9dD4h0k0iQMSHqqZ8+ecHZ2BlC8Ds/8+fMlrkj3pKWloVu3bggMDBRWxTY3N8eaNWuwYsUKmJmZSVwh6SLFgBQSEqJVd2IYkEiTMCDpKQMDA0yZMkXor1u3TmmRQno3Z8+ehbu7O44cOSIcq1u3Ls6ePau0Lx6RqrVo0UIIGk+fPkVMTIzEFb05xaUtuF8kSY0BSY998MEHqFmzJoDiJ0Z4F+ndyeVy/Prrr2jVqhXu3r0rHO/bty8uXLiAxo0bS1gd6QMrKyt4eHgI/VOnTklXTCkZGPzvVxLvIJHUGJD0mLGxMb755huhv3LlSqVf6lQ6z58/R0BAAD7//HPhEWUjIyP89NNP2L59O2xsbCSukPSF4jCbNk3U5uKopEkYkPTcqFGj4ODgAKB4jZ558+ZJXJF2unr1Kpo1a4atW7cKxxwcHHDy5El88cUX/MFPZUockLRxuEobaybdwoCk58zMzDB16lSh/+eff+L27dsSVqR9Nm7cCG9vb8TFxQnH/P39ER0djZYtW0pYGemrFi1aCKE8LS1NacV2TcY5SKRJGJAIH330EWrUqAGgeP+jOXPmSFyRdsjNzcUnn3yCIUOGICsrSzj+7bff4siRI6hUqZKE1ZE+s7W1RZMmTYS+tjzuzzutpEkYkAimpqaYOXOm0F+7dq3WfOKUSlJSElq2bIlly5YJx2xtbbFv3z7MnTtX6XFlIilo6zwkIk3BgEQAgBEjRqB+/foAilewnTZtmsQVaa79+/fDw8MDFy5cEI55eXkhKioK3bt3l7Ayov8RLxipDUNW2lAj6Q8GJAJQ/LTV3Llzhf7u3bsREhIiYUWap7CwENOnT0ePHj3w5MkT4fi4ceNw5swZODo6SlcckUirVq2EdmpqKm7cuCFhNW9GcVFLxUf+iaTAf4Ek6NevH7y9vYX+pEmTtGoVXnV6+PAhOnfujO+++044ZmFhgfXr12PZsmUwNTWVsDqikuzt7dGwYUOhrw3zkBTvIHE+EkmNAYkEMpkMCxcuFPrnz5/H5s2bJaxIM4SFhcHDwwPHjx8XjtWvXx/nzp3DkCFDJKyM6PW0bV823kEiTcJ/gaSkVatW6Nu3r9D/5ptvkJmZKWFF0pHL5Vi8eDHatGmDe/fuCcf79++PCxcuwM3NTcLqiP6btq2HxIBEmoT/AqmEBQsWwMTEBABw7949/PDDDxJXVPaePXuG/v3748svv1RaFfuXX37Bli1bYGVlJXGFRP+tdevWQjs5ORlJSUnSFfMGFLcX4ZOgJDUGJCqhTp06+PLLL4X+ggULkJCQIGFFZSsmJgZeXl7YsWOHcKx69eoIDg7GZ599xrkRpDWqVKkCZ2dnoa/pw2wMSKRJGJDopb799lthC5Lc3Fx8/vnnGn97XhXWrl2L5s2bKz3x07FjR0RFRcHX11fCyojejuJdJAYkojfHgEQvVa5cOSxatEjo79+/H//88490BalZTk4OxowZgxEjRiA7OxtA8aT1WbNm4eDBg6hYsaLEFRK9HW2aqK0YkIyMjCSshIgBiV5j4MCB8Pf3F/qfffYZnj9/LmFF6pGYmAg/Pz/8+eefwjE7OzscPHgQs2fP5idZ0mqKAenWrVtITk6WsJrX+3e+H8CARNJjQKJXkslkWLp0qTBh++7du5g+fbrEVanW3r174enpiejoaOGYt7c3oqKi0LlzZwkrI1KN6tWro3bt2kJfk7cdyc/PF9oMSCQ1BiR6LWdnZ0ydOlXoL1myBGfPnpWwItUoKCjA1KlT8d577+Hp06fC8fHjxyMkJAQ1a9aUrjgiFdOWYTbFgGRsbCxhJUQMSPQGpk6dChcXFwDFawONHj0aubm5Elf19lJTU9GxY0el5QssLS2xadMmLFmyRLhjRqQrGJCISo8Bif6TqakpVq5cKTzefu3aNQQGBkpc1dsJDg6Gu7s7Tp06JRxr0KABIiIiMGjQIOkKI1IjxYAUHx+P1NRUCat5NcWAxA8qJDUGJHojLVq0wPjx44X+/PnzERERIWFFpSOXy7FgwQL4+/sr/XIYNGgQzp8/jwYNGkhYHZF6OTo6okaNGkJfU+ch5eXlCW0GJJIaAxK9sXnz5gmTPQsLCzFs2DDhkXhN9vTpU/Tt2xdff/218BixsbExfvvtN2zcuBHlypWTuEIi9ZLJZFoxzKY4dM+ARFJjQKI3Zmlpib/++ksYart+/Tq+/fZbiat6vYsXL8LLy0tpDaeaNWvizJkz+PTTT7kqNukNbVgwUjEgmZqaSlgJEQMSlVKbNm0wceJEob948WIcO3ZMuoJeY9WqVfDx8VHaJqVLly6IioqCt7e3hJURlT3FO0hXr15FWlqahNW8XE5OjtBmQCKpMSBRqX333XdwdXUV+sOGDUN6erqEFSnLzs7G6NGj8eGHHwqfSGUyGQIDA7F//37Y2dlJXCFR2atXrx6qVKki9DVxHpLiHSQzMzMJKyFiQKK3YG5ujk2bNglzBFJSUjBq1CiN2KstISEBvr6+WL16tXDM3t4eR44cwYwZM2BgwH/ypJ+0YR6S4h0kBiSSGn9b0Ftp0qQJ5s2bJ/T37NmD3377TcKKgH/++Qeenp64dOmScMzX1xfR0dHo0KGDhJURaQbFgKRpd5CKioqUnmJjQCKpMSDRW5s4cSK6dOki9CdNmoTIyMgyr6OgoACTJ09Gnz59kJGRoVTf6dOnUb169TKviUgTKQakS5cu4cmTJxJWo0z8RKyFhYVElRAVY0Cit2ZgYIC1a9cK8xry8vLQv3//Mv2hm5KSAn9/fyxcuFA4Vq5cOWzduhWLFy/marxECho0aAB7e3sAxWuDnTlzRuKK/kcckMzNzSWqhKgYAxK9k0qVKmHz5s3C3J5bt25h+PDhKCoqUvt7nzp1Cu7u7ggJCRGOubm54cKFC+jfv7/a359I28hkMo193D8rK0upzztIJDUGJHpnbdu2RVBQkNDfu3ev0vwkVSsqKsIPP/yA9u3b48GDB8LxoUOH4uzZs3B2dlbbexNpO02dh6QYkExNTWFoaChhNUQMSKQiU6ZMQffu3YX+jBkzcOjQIZW/z5MnT9CrVy9MnTpVuEtlYmKC5cuXY+3atbC0tFT5exLpEsWAFBUVhefPn0tYzf9kZmYKbd49Ik3AgEQqYWBggPXr16NOnToAiuc3BAQE4MaNGyp7j6ioKHh6emLfvn3CMUdHR4SGhmLMmDFcFZvoDTRq1Ajly5cHULxlUGhoqLQF/b8XL14IbW7/Q5qAAYlUxtbWFrt27RI+/WVkZKBXr1549uzZO72uXC7HihUr4Ofnh1u3bgnHu3fvjsjISHh5eb3T6xPpEwMDA7Rq1Uroa8o8JMWAxDvBpAkYkEilGjdujDVr1gj92NhYBAQEoKCg4K1eLysrCyNGjMDYsWOFVXYNDAzw3XffYc+ePahQoYIqyibSK5q4YKRiQLKyspKwEqJiDEikcv3798f06dOF/sGDBzFp0qRSv058fDx8fHywbt064VilSpVw9OhRTJs2jatiE70lxYAUERGhNP9HKopzoTjERpqAv2FILebMmYO+ffsK/V9++QW///77G3/9jh074OXlhZiYGOFYy5YtER0dDX9/f5XWSqRvmjZtKtylKSgoQHh4uMQVKQck3kEiTcCARGphYGCAdevWwcPDQzj22WefYe/eva/9uvz8fHz55Zfo16+f0g/Mr776CidOnICDg4PaaibSF0ZGRmjRooXQ14TH/RW/362trSWshKgYAxKpjaWlJfbu3Sts9VFUVISAgACcP3/+pdffu3cP7dq1w+LFi4Vj1tbW2LFjBxYuXMhVsYlUSNPmISluE8Q7SKQJGJBIrRwcHHDgwAHhE2FWVha6d++O+Ph4peuOHz8Od3d3pUeOGzdujAsXLigN1RGRaigGpHPnziEnJ0fCapQDko2NjYSVEBVjQCK1a9SoEXbs2CHcAUpLS0Pnzp1x//59FBUVYe7cuejYsSMePXokfM2IESMQHh6OevXqSVU2kU7z8vISluTIzc3FuXPnJK1HcTkQBiTSBAxIVCY6dOig9DRaUlIS2rZti5YtW2LGjBmQy+UAircY+PPPP/HXX39xNV0iNTI2Noafn5/Ql3qY7enTp0L734UsiaTEgERlJiAgAD///LPQv3HjhtLTM3Xq1EF4eDg+/PBDCaoj0j+atC8bAxJpGgYkKlOff/45Ro8eXeJ4z549ERkZCXd3dwmqItJPigEpLCwMeXl5ktXy5MkToc2ARJqAAYnK3J9//okqVaooHfv666/5Q5GojHl7e8PU1BQAkJ2djQsXLkhWi2JA4gr5pAkYkKjMyWQyxMfHKz22P3r0aGRnZ0tYFZH+MTU1hY+Pj9CXah6SXC5XGmKztbWVpA4iRQxIJAkrKyucPn0aMpkMQPG2IorbkxBR2dCE9ZAyMjJQWFgo9O3s7CSpg0gRAxJJxtfXF1988YXQX7x4MUJCQiSsiEj/KAak0NDQt95Y+l08fvxYaMtkMj7mTxqBAYkkNXfuXLi4uAAovs0+fPhwpS0HiEi9fHx8hOHuFy9eICoqqsxrSEtLE9oVKlSAoaFhmddAJMaARJIyNzfH2rVrhR+It27dwsSJE6UtikiPWFhYwNvbW+hL8bi/YkDi8BppCgYkkpy3tze+/fZbob969Wrs2rVLwoqI9Evr1q2FthTzkBRX0be3ty/z9yd6GQYk0gjTp09X+hQ7evRoJCcnS1gRkf5QnIcUEhKiNGG6LCgGpIoVK5bpexO9CgMSaQRjY2Ns2LABlpaWAIrXRBkyZEiZ/6Am0kd+fn7CMHdGRgYuX75cpu/PgESaiAGJNEa9evXw22+/Cf3g4GAEBQVJWBGRfrCysoKnp6fQL+t5SA8ePBDalStXLtP3JnoVBiTSKMOHD8egQYOEfmBgIE6cOCFhRUT6Qcp5SA8fPhTalSpVKtP3JnoVBiTSKDKZDH/88QecnJwAFD/6P3jwYKSkpEhcGZFuE29cW1RUVGbvnZqaKrTF2xARSYUBiTSOtbU1tm3bBhMTEwDFt98HDhyI/Px8iSsj0l0tW7YUVrZPT09HbGxsmb23YkDiEBtpCgYk0kju7u74+eefhX5ISAimTp0qXUFEOq58+fJo2rSp0C+rYbbCwkKlOUhVq1Ytk/cl+i8MSKSxxo0bhyFDhgj9RYsWYcuWLRJWRKTbpNiX7dGjR0rDeQxIpCkYkEhjyWQyLF++HI0aNRKOjRo1CpcuXZKwKiLdJQ5Icrlc7e95//59oW1paQkrKyu1vyfRm2BAIo1mYWGBXbt2oXz58gCArKws9O7dW2lrAiJSjZYtWwrtBw8eID4+Xu3vqRiQqlWrpvb3I3pTDEik8ZycnLB582ZhAmlSUhL69euHvLw8iSsj0i329vZwc3MT+mWxHtK9e/eEtoODg9rfj+hNMSCRVujSpQt++OEHoX/69GmMHz++TIYAiPRJWc9DUtxSqEaNGmp/P6I3xYBEWmPy5MkYOnSo0P/zzz/x008/SVgRke4p63lId+/eFdrVq1dX63sRlQYDEmkNmUyGFStWwMfHRzg2efJk/PPPP9IVRaRjFFfUvnv3Lm7duqXW97tz547Qrlmzplrfi6g0GJBIq5iZmWH37t1wdHQE8L+Vts+dOydtYUQ6onLlynB2dhb66h5m4xAbaSoGJNI6lSpVwv79+2FjYwMAyM7ORs+ePZGQkCBxZUS6oazmIRUVFSndQapVq5ba3ouotBiQSCu5urpi586dMDY2BlC82Fznzp2VVuQlordTVgHpwYMHSk+jcoiNNAkDEmktf39/rF69WugnJCSgW7duePbsmYRVEWk/xYCUlJSkdJdHlZKSkoR2hQoVYG1trZb3IXobDEik1YYMGYL58+cL/aioKPTu3Rs5OTkSVkWk3apVqwYnJyehr667SIoTwP+dV0ikKRiQSOtNnjwZX3zxhdA/efIkBg4ciPz8fAmrItJuZTHMlpiYKLTr1KmjlvcgelsMSKT1ZDIZFi5cqLRG0p49ezBixAgUFhZKWBmR9lIMSOpaUVvxwQoGJNI0DEikEwwMDLB69Wr06tVLOLZp0yaMHTtWaadwInoziush3bhxAykpKSp/DwYk0mQMSKQzjIyM8Pfff6N9+/bCsVWrVmHChAnckoSolBwdHZWeKlPHMNvNmzeFdr169VT++kTvggGJdMq/C0m2atVKOLZ06VKGJKK3oM55SC9evFC6K1W3bl2Vvj7Ru2JAIp1jaWmJffv2KW1J8vvvv2PChAkcbiMqBXUGpBs3bghtMzMz7sNGGocBiXSStbU1Dh06BG9vb+HY77//jo8//pghiegNKQak2NhYPHz4UGWvHR8fL7Tr1q0LAwP+OiLNwn+RpLNsbGxw+PBhpZC0YsUKjBw5EgUFBRJWRqQdnJyc4ODgIPRDQkJU9trXr18X2i4uLip7XSJVYUAinVa+fHkcOXIEfn5+wrF169Zh0KBBSlscEFFJMplM6Wk2VQ6zxcbGCm0GJNJEDEik82xsbHDo0CGl4YLt27ejV69eyMzMlLAyIs2nrnlIigGpQYMGKntdIlVhQCK9YGVlhQMHDqBLly7CsUOHDqFjx454/PixhJURaTbFO0gxMTEq+X4pKChQGmJjQCJNxIBEesPCwgK7d+9Gv379hGPh4eFo1aoVkpOTJayMSHM1aNAAFStWBADI5XKcOXPmnV8zMTFRGOI2MDDgEBtpJAYk0ismJib4+++/MXbsWOHYtWvX4OvriytXrkhYGZFmUsc8pJiYGKHt5OQEc3Pzd35NIlVjQCK9Y2hoiGXLlmHGjBnCsXv37qFFixY4fvy4hJURaSZVz0NSDEiNGjV659cjUgcGJNJLMpkMgYGBWLZsmbD+yrNnz9ClSxesWbNG2uKINIxiQIqOjkZGRsY7vd7ly5eFNgMSaSoGJNJr48aNw65du4Rb/AUFBRg5ciSmTp3KBSWJ/p+bmxsqVKgAACgqKkJYWNg7vd7FixeFdtOmTd/ptYjUhQGJ9N57772H06dPo1KlSsKxH374Af369cOLFy8krIxIMxgYGCjtb/guw2wZGRm4deuW0G/SpMk71UakLgxIRACaNWuGc+fOwc3NTTi2a9cutGjRArdv35awMiLNoKp5SIp3j8qXLw9HR8d3qIpIfRiQiP6fo6MjQkND0bVrV+HY5cuX4eXlhZMnT0pYGZH0FJ9ku3DhwlsvshoZGSm03d3dIZPJ3rk2InVgQCJSYG1tjb1792LSpEnCsbS0NHTs2BGLFy+GXC6XsDoi6TRt2hTW1tYAiufqhYeHv9XrXLhwQWh7enqqpDYidWBAIhIxNDTEggULsG7dOpiamgIACgsL8eWXXyIgIADPnz+XuEKismdoaIiWLVsK/bcdZjt//rzQ9vLyeue6iNSFAYnoFYYOHYrQ0FDUqFFDOLZ161Z4e3tzUUnSS++6YGR6ejoSEhKEfvPmzVVSF5E6MCARvYanpyciIyPRvn174dj169fh7e3N9ZJI7yhO1D537hyys7NL9fXnzp0T2hUrVkStWrVUVhuRqjEgEf2HihUr4vDhw5g2bZpwLDs7GyNHjsTQoUM55EZ6w9PTE5aWlgCAvLw8pcDzJs6ePSu0fXx8OEGbNBoDEtEbMDQ0xHfffYcDBw7Azs5OOL5hwwZ4eHggIiJCwuqIyoaxsTH8/PyEfmmH2UJDQ4W2r6+vyuoiUgcGJKJS6Nq1K6Kjo5Umq968eRN+fn6YN28eCgsLJayOSP0U5yEFBwe/8dfl5+cr3XFq0aKFSusiUjUGJKJSqlGjBk6ePImZM2cKQwQFBQWYNm0a2rZti8TERIkrJFIfxXlI4eHhyMvLe6Ovi46OFtZOMjExQbNmzdRSH5GqMCARvQUjIyPMmTMHp06dUnrK7cyZM2jSpAlWrFjBNZNIJ3l7e8PMzAxA8Vy8Nx1eVrzb1KxZM2H/QyJNxYBE9A5at26Ny5cvY9CgQcKxFy9eYOzYsejSpQvu3LkjYXVEqmdqagofHx+h/6bzkE6dOiW0Fe9CEWkqBiSid1S+fHls2rQJf//9N2xtbYXjR44cgZubG/744w8UFRVJWCGRapV2X7b8/HylO0gMSKQNGJCIVGTgwIG4cuUKevToIRx7/vw5Pv74Y7Rr1w5xcXESVkekOooBJywsDAUFBa+9PiIiQlgOw8TEROkhByJNxYBEpEIODg7Ys2cP1q5dq3Q3KTg4GI0bN0ZgYCByc3MlrJDo3fn4+MDY2BhA8ZByVFTUa68/evSo0Pbz84OFhYVa6yNSBQYkIhWTyWQYNmwYrl27hr59+wrH8/LyMGvWLDRu3BjHjx+XsEKid2Nubg5vb2+h/1/DbIcPHxbanTp1UltdRKrEgESkJlWqVMGOHTuwa9cuVKtWTTgeHx+PDh06ICAgAHfv3pWwQqK396bzkB4/fqy0/hEDEmkLBiQiNevduzeuXbuGzz77DAYG//uW27JlC1xcXDBv3jzk5ORIWCFR6SkGpJCQkFcuknr48GHhIYUqVarA3d29TOojelcMSERlwNraGr/88gsiIiKUhiYyMzMxbdo0NGzYEDt37uTaSaQ1/Pz8YGhoCAB49uwZLl269NLr9u7dK7S7du2q9CGBSJPxXypRGfLw8EB4eDj+/PNPpT3dEhMT8f7776NNmzbc1420Qrly5eDl5SX0XzbMlpeXhwMHDgj9nj17lkltRKrAgERUxgwMDPDhhx/ixo0bmDhxIoyMjIRzISEh8Pb2RkBAABISEiSskui//dc8pFOnTiEjIwMAYGZmxvlHpFUYkIgkYmtri8WLFyMmJkZp7STgf/OTPvnkE6SkpEhUIdHriechiRdE3bFjh9Du1KkTLC0ty6w2onfFgEQkMRcXF+zduxfHjh1TmsBaUFCAZcuWwcnJCZMnT8ajR48krJKopBYtWghzih4/foyrV68K5/Lz87Fz506h369fvzKvj+hdMCARaYj27dvjwoULWL9+PRwdHYXj2dnZWLhwIWrXro0pU6YwKJHGsLGxQdOmTYW+4jDb8ePHkZaWBqB49WzOPyJtw4BEpEEMDAwwZMgQXL9+HUuWLEHlypWFc5mZmZg/fz5q166NyZMnIzU1VcJKiYq9ah7Sxo0bhXa3bt1Qvnz5siyL6J0xIBFpIFNTU4wfPx4JCQn48ccflZ54y8zMxMKFC+Ho6IhPP/0UiYmJElZK+k4xIAUHB0Mul+PFixfYtWuXcPyDDz6QojSidyKTc+EVIo33/Plz/P7771i4cCHS09OVzhkYGKB///6YNGmS0mPXRGUhPT0d9vb2Qj82Nhbh4eEYNWoUgOJhuNTUVJiZmUlVItFb4R0kIi1gZWWFKVOmICkpCYsWLUKVKlWEc0VFRdiyZQuaNWuGNm3a4J9//nnlqsZEqmZnZ4dGjRoJ/dOnT2PlypVCf/DgwQxHpJUYkIi0SLly5fDll1/i1q1b+OOPP+Dk5KR0Pjg4GH369EHdunWxcOFCPH78WKJKSZ8oDrPt2bMHYWFhQn/06NFSlET0zjjERqTFCgsLsWvXLixatAhnz54tcd7MzAyDBw/Gxx9/zOE3Upvt27ejf//+AABLS0tkZmYCKF45PjIyUsrSiN4aAxKRjggLC8Mvv/yCHTt2vHSIzcPDAx999BEGDRoEGxsbCSokXfXw4UOlJy7/tWrVKmEuEpG2YUAi0jHJycn4448/8Oeff750zSRzc3O8//77GDlyJNq2bcvNQ0klXF1dERsbK/Tt7Oxw584dWFhYSFgV0dvjT0YiHVOjRg189913SE5OxoYNG9CyZUul89nZ2diwYQPat2+P2rVrY9q0abh27ZpE1ZIuuHHjRoltRMaNG8dwRFqNd5CI9MDVq1excuVKrFu37pUTt5s0aYJBgwZh4MCBSit5E4nJ5XJcvnwZu3btwq5du3D58uUS19y/fx9Vq1aVoDoi1WBAItIjubm52Lt3L/766y8cOnSoxOai/2rWrBn69++Pvn37lnhSjvRTXl4egoODsW/fPuzZswe3bt165bUymQwxMTFo2LBhGVZIpFoMSER6KjU1FZs3b8b69esRHR39yuuaNGmCXr164b333oOHhwdkMlkZVklSunfvHg4dOoSDBw/iyJEjeP78+Suvbdy4MerWrQsHBwd8+eWXqF27dhlWSqR6DEhEhLi4OGzevBl///034uLiXnmdg4MDunfvju7du8Pf3x9WVlZlWCWpW1ZWFoKDg3Hs2DEcOXIEMTExr73ex8cHvXv3Rp8+fVC/fv0yqpKobDAgEZFALpfjypUr2Lp1K3bt2oWrV6++8lojIyO0bNkSHTt2RIcOHeDp6QlDQ8MyrJbeVW5uLs6ePYtTp07h5MmTCA8PR15e3iuvNzMzQ4cOHdCjRw/07NkTDg4OZVgtUdliQCKiV4qLi8OePXuwe/duhIWF4XU/LsqXL4/WrVujXbt2aNOmDRo3bszApGEyMjJw9uxZhISEICQkBOfOnUNubu5rv6Z27dro2rUrunbtCn9/fz6ZRnqDAYmI3sijR49w6NAh7N+/H0eOHMGTJ09ee721tTX8/Pzg5+eHFi1aoFmzZhySK0MFBQW4du0aIiIicO7cOYSHh+Pq1auvDblA8b5/7dq1Q8eOHdGpUyfUq1eP885ILzEgEVGpFRYWIiIiAkeOHMHRo0dx9uxZFBQUvPZrZDIZXF1d0bx5c3h5ecHLywtubm4wNzcvo6p1V15eHmJjYxEdHY2oqChERUUhOjoaWVlZ//m15ubmaNGiBdq1a4d27dqhWbNmMDIyKoOqiTQbAxIRvbPnz5/jzJkzOHnyJE6fPo3IyMiXbnciZmhoCBcXFzRp0gSNGzdGo0aN0LBhQ9SoUYMrfL9EYWEhkpKScPXqVeHP5cuXERsb+58B9V8VKlSAr68vWrdujVatWsHT0xMmJiZqrpxI+zAgEZHKvXjxAmfPnkVYWBhCQ0Nx7tw5ZGRkvPHXW1pawsXFBc7OznB2dka9evVQt25dODk5wdbWVqeHfAoKCnD37l3cunULN2/eREJCAm7cuIG4uDjcuHHjtZOoxQwNDdGoUSM0a9YMvr6+8PPzQ/369XX6vx+RqjAgEZHaFRUVIT4+HmfPnkVkZCQiIiJw6dIl5OTklPq1rKysULt2bdSsWRM1a9ZEjRo14ODggGrVqqFKlSqoXLkyKlSooJF3oDIzM/Hw4UOkpqYiJSUF9+/fx71795CcnIzk5GTcuXMHycnJb3T3TczY2Biurq7w9PSEh4cH3N3d0bRpU06qJnpLDEhEJImCggLExcXh4sWLuHz5MmJiYhATE4O7d+++82sbGRnBzs4O9vb2sLOzg62tLWxtbVG+fHlYWVnB2toa5cqVg4WFBSwsLGBqair8MTIygpGREQwMDCCTySCTySCXy1FYWIjCwkLk5+cjLy8PeXl5yM7OFv68ePECL168wPPnz/H06VNkZGTg6dOnSE9PR3p6OtLS0pCZmamC/3LFT5Y1bNgQDRs2hJubG5o0aQJnZ2cOlRGpEAMSEWmUZ8+eITY2FtevX0d8fDzi4uKQkJCAmzdv4sWLF1KXV2aqVKkCJycnYWhRcbiRd4WI1I8BiYi0glwuR1paGm7duoVbt24Jw1LJyclISUnBvXv3kJqaivz8fKlL/U92dnaoWrUqHBwcULVqVdSoUUP4U7t2bdSqVYtP9xFJjAGJiHSGXC7H06dP8eDBA6SlpQnDW0+ePMGTJ0+QkZGB58+f49mzZ8jKykJWVhYyMzORm5sr/CksLERBQQEKCwshl8shl8thYGAAAwMDGBoawtjYGCYmJjA2Noa5ubnwp1y5crCyskK5cuVQvnx52NjYwMbGRmmor1KlSrC3t4exsbHU/6mI6D8wIBERERGJaN5jHkREREQSY0AiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhJhQCIiIiISYUAiIiIiEmFAIiIiIhL5PxxpDhR+pBK3AAAAAElFTkSuQmCC\",\n      \"text/plain\": [\n       \"<Figure size 960x720 with 1 Axes>\"\n      ]\n     },\n     \"metadata\": {},\n     \"output_type\": \"display_data\"\n    }\n   ],\n   \"source\": [\n    \"from spectre.IO.H5.IterElements import iter_elements\\n\",\n    \"\\n\",\n    \"# Create a 3D plot\\n\",\n    \"ax = plt.gcf().add_subplot(111, projection=\\\"3d\\\")\\n\",\n    \"ax.axis(\\\"off\\\")\\n\",\n    \"\\n\",\n    \"# Iterate over elements\\n\",\n    \"for element in iter_elements(\\n\",\n    \"    open_volfiles(h5files, subfile_name),\\n\",\n    \"    obs_ids=obs_ids[0],\\n\",\n    \"    element_patterns=[\\\"B3,*\\\"],  # Only plot elements in block 3\\n\",\n    \"):\\n\",\n    \"    # Draw outline of the element by mapping the edges of the logical cube to\\n\",\n    \"    # inertial coordinates using the element map\\n\",\n    \"    for d in range(3):\\n\",\n    \"        for edge in range(4):\\n\",\n    \"            line = np.zeros((3, 100))\\n\",\n    \"            line[d, :] = np.linspace(-1, 1, 100)\\n\",\n    \"            line[(d + 1) % 3, :] = 2 * (edge % 2) - 1\\n\",\n    \"            line[(d + 2) % 3, :] = 2 * (edge // 2) - 1\\n\",\n    \"            x, y, z = element.map(line)\\n\",\n    \"            ax.plot(x, y, z, color=\\\"black\\\")\\n\",\n    \"\\n\",\n    \"# Make plot square\\n\",\n    \"ax.set_aspect(\\\"equal\\\")\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"### Transforming volume data\\n\",\n    \"\\n\",\n    \"Often you want to post-process volume data to compute derived quantities from\\n\",\n    \"the ones the simulation has written out. You can sometimes do this within tools\\n\",\n    \"like ParaView (e.g. using ParaView's \\\"Calculator\\\" filter) if the computation is\\n\",\n    \"simple enough and pointwise (i.e., needs no derivatives or other mesh\\n\",\n    \"information). If you can't get what you need in ParaView, you can use `spectre\\n\",\n    \"transform-vol`. It takes any Python function (a \\\"kernel\\\"), runs it over your\\n\",\n    \"volume data, and writes the result back into the files.\\n\",\n    \"\\n\",\n    \"For example, let's add the number of grid points in each element as a field\\n\",\n    \"that we can visualize:\\n\",\n    \"\\n\",\n    \"```sh\\n\",\n    \"spectre transform-vol BbhVolume*.h5 -d VolumeData \\\\\\n\",\n    \"    -k spectre.Spectral.Mesh3D:extents\\n\",\n    \"```\\n\",\n    \"\\n\",\n    \"You will be prompted to select an output dataset name (hit enter to select the\\n\",\n    \"default \\\"Extents\\\"). The result will be written back into the H5 files. If you\\n\",\n    \"now regenerate an XDMF file for the volume data you will be able to see the\\n\",\n    \"output in ParaView.\\n\",\n    \"\\n\",\n    \"The `transform-vol` tool supports arbitrary Python functions as kernels. Many\\n\",\n    \"Python functions from the `spectre` module are useful kernels. For example,\\n\",\n    \"here are some useful kernels:\\n\",\n    \"\\n\",\n    \"- p-refinement (number of grid points per dimension):\\n\",\n    \"  `spectre.Spectral.Mesh3D:extents`\\n\",\n    \"- Relative/absolute truncation error (from power monitors):\\n\",\n    \"  `spectre.NumericalAlgorithms.LinearOperators:relative_truncation_error`\\n\",\n    \"  or `absolute_truncation_error`\\n\",\n    \"- Coordinates in different frames: `Element:grid_coordinates` or\\n\",\n    \"  `Element:distorted_coordinates`\\n\",\n    \"\\n\",\n    \"You can find more useful kernels by browsing the `spectre.PointwiseFunctions`\\n\",\n    \"module: https://spectre-code.org/py/_autosummary/spectre.PointwiseFunctions.html\\n\",\n    \"\\n\",\n    \"You can also write your own kernels. Create a Python file, e.g. `kernel.py`, and\\n\",\n    \"write a function like this:\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"%%file kernel.py\\n\",\n    \"from spectre.DataStructures.Tensor import Scalar, DataVector\\n\",\n    \"\\n\",\n    \"def lapse_squared(lapse: Scalar[DataVector]) -> Scalar[DataVector]:\\n\",\n    \"    return np.array(lapse)**2\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"Now you can run this kernel over your volume data like this:\\n\",\n    \"\\n\",\n    \"```sh\\n\",\n    \"spectre transform-vol BbhVolume*.h5 -d VolumeData \\\\\\n\",\n    \"    -e kernel.py -k lapse_squared\\n\",\n    \"```\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"You can find more details by running `spectre transform-vol --help`.\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": []\n  }\n ],\n \"metadata\": {\n  \"kernelspec\": {\n   \"display_name\": \"Python 3 (ipykernel)\",\n   \"language\": \"python\",\n   \"name\": \"python3\"\n  },\n  \"language_info\": {\n   \"codemirror_mode\": {\n    \"name\": \"ipython\",\n    \"version\": 3\n   },\n   \"file_extension\": \".py\",\n   \"mimetype\": \"text/x-python\",\n   \"name\": \"python\",\n   \"nbconvert_exporter\": \"python\",\n   \"pygments_lexer\": \"ipython3\",\n   \"version\": \"3.10.13\"\n  },\n  \"orig_nbformat\": 4\n },\n \"nbformat\": 4,\n \"nbformat_minor\": 2\n}\n"
  },
  {
    "path": "docs/_templates/autosummary/module.rst",
    "content": "{% extends \"!autosummary/module.rst\" %}\n\n.. Distributed under the MIT License.\n   See LICENSE.txt for details.\n\n.. Include pybindings documentation. This won't do anything for modules that\n   have no pybindings but print a warning.\n{% block modules %}\n.. automodule:: {{ fullname }}._Pybindings\n\n.. currentmodule:: {{ fullname }}\n\n{{ super() }}\n{% endblock %}\n"
  },
  {
    "path": "docs/cli.rst",
    "content": ".. Distributed under the MIT License.\n   See LICENSE.txt for details.\n\n.. click:: spectre.__main__:cli\n   :prog: spectre\n   :nested: full\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Configuration file for the Sphinx documentation builder.\n#\n# This file only contains a selection of the most common options. For a full\n# list see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\n# -- Path setup --------------------------------------------------------------\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport sys\n\n# -- Project information -----------------------------------------------------\n\nproject = \"SpECTRE Python interface\"\ncopyright = \"2017-2026, SXS Collaboration\"\nauthor = \"SXS Collaboration\"\n\n# -- General configuration ---------------------------------------------------\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.autosummary\",\n    \"sphinx.ext.mathjax\",\n    # Parse Numpy-style docstrings\n    \"sphinx.ext.napoleon\",\n    # Support markdown files\n    \"myst_parser\",\n    # \"breathe\",\n    \"sphinx_click\",\n]\n\n# Autodoc configuration\nautodoc_default_options = {\n    \"members\": True,\n    \"undoc-members\": True,\n    \"inherited-members\": True,\n}\nautodoc_preserve_defaults = True\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = [\"_templates\"]\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This pattern also affects html_static_path and html_extra_path.\nexclude_patterns = [\"_build\", \"Thumbs.db\", \".DS_Store\"]\n\n# -- Options for HTML output -------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = \"furo\"\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = [\"_static\"]\n\n# -- C++ documentation with Breathe + Exhale ---------------------------------\n\n# We have also experimented with generating our C++ documentation with Sphinx.\n# See issue: https://github.com/sxs-collaboration/spectre/issues/2138\n\n# Tell sphinx what the primary language being documented is.\n# primary_domain = \"cpp\"\n\n# Tell sphinx what the pygments highlight language should be.\n# highlight_language = \"cpp\"\n\n# Configure Breathe for parsing and including C++ documentation.\n# breathe_projects = {\"SpECTRE\": \"@CMAKE_BINARY_DIR@/docs/xml\"}\n# breathe_default_project = \"SpECTRE\"\n\n# Setup the Exhale extension for automatically building namespace,\n# file, and group documentation. We have had issues with Exhale\n# failing when dealing with function overloads. To enable you must\n# add `'exhale'` to the extensions.\n#\n# exhale_args = {\n#     # These arguments are required\n#     \"containmentFolder\": \"./api\",\n#     \"rootFileName\": \"library_root.rst\",\n#     \"rootFileTitle\": \"Library API\",\n#     \"doxygenStripFromPath\": \"..\",\n#     # Suggested optional arguments\n#     \"createTreeView\": True,\n#     # TIP: if using the sphinx-bootstrap-theme, you need\n#     # \"treeViewIsBootstrap\": True,\n#     \"exhaleExecutesDoxygen\": False\n#     # \"exhaleDoxygenStdin\": \"INPUT = ../include\"\n# }\n\n# # a simple label style which uses the bibtex keys for labels\n# class NumberedLabelStyle(BaseLabelStyle):\n#     def format_labels(self, sorted_entries):\n#         for entry in sorted_entries:\n#             # Add one since refs usually start at 1 not 0\n#             yield str(sorted_entries.index(entry) + 1)\n\n# class SpectreBibStyle(UnsrtStyle):\n#     default_label_style = NumberedLabelStyle\n\n# register_plugin('pybtex.style.formatting', 'SpectreStyle', SpectreBibStyle)\n"
  },
  {
    "path": "docs/config/DoxygenLayout_1_8_10.xml",
    "content": "<doxygenlayout version=\"1.0\">\n  <!-- Generated by doxygen 1.8.10 -->\n  <!-- Navigation index tabs for HTML output -->\n  <navindex>\n    <tab type=\"mainpage\" visible=\"yes\" title=\"Main\"/>\n    <tab type=\"pages\" visible=\"yes\" title=\"Documentation\" intro=\"\"/>\n    <tab type=\"user\" visible=\"yes\" title=\"GitHub repository\" url=\"https://github.com/sxs-collaboration/spectre\"/>\n    <tab type=\"usergroup\" visible=\"yes\" title=\"Code reference\">\n      <tab type=\"modules\" visible=\"yes\" title=\"Topics\" intro=\"Here is a list of all topics with brief descriptions:\"/>\n      <tab type=\"namespaces\" visible=\"yes\" title=\"\">\n        <tab type=\"namespacelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"namespacemembers\" visible=\"no\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"classes\" visible=\"yes\" title=\"\">\n        <tab type=\"classlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"classindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n        <tab type=\"hierarchy\" visible=\"no\" title=\"\" intro=\"\"/>\n        <tab type=\"classmembers\" visible=\"no\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"files\" visible=\"no\" title=\"\">\n        <tab type=\"filelist\" visible=\"no\" title=\"\" intro=\"\"/>\n        <tab type=\"globals\" visible=\"no\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"examples\" visible=\"no\" title=\"\" intro=\"\"/>\n    </tab>\n    <tab type=\"user\" visible=\"yes\" title=\"Python interface\" url=\"py/index.html\"/>\n  </navindex>\n\n  <!-- Layout definition for a class page -->\n  <class>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_INCLUDE_FILES\"/>\n    <inheritancegraph visible=\"$CLASS_GRAPH\"/>\n    <collaborationgraph visible=\"$COLLABORATION_GRAPH\"/>\n    <memberdecl>\n      <nestedclasses visible=\"yes\" title=\"\"/>\n      <publictypes title=\"\"/>\n      <services title=\"\"/>\n      <interfaces title=\"\"/>\n      <publicslots title=\"\"/>\n      <signals title=\"\"/>\n      <publicmethods title=\"\"/>\n      <publicstaticmethods title=\"\"/>\n      <publicattributes title=\"\"/>\n      <publicstaticattributes title=\"\"/>\n      <protectedtypes title=\"\"/>\n      <protectedslots title=\"\"/>\n      <protectedmethods title=\"\"/>\n      <protectedstaticmethods title=\"\"/>\n      <protectedattributes title=\"\"/>\n      <protectedstaticattributes title=\"\"/>\n      <packagetypes title=\"\"/>\n      <packagemethods title=\"\"/>\n      <packagestaticmethods title=\"\"/>\n      <packageattributes title=\"\"/>\n      <packagestaticattributes title=\"\"/>\n      <properties title=\"\"/>\n      <events title=\"\"/>\n      <privatetypes title=\"\"/>\n      <privateslots title=\"\"/>\n      <privatemethods title=\"\"/>\n      <privatestaticmethods title=\"\"/>\n      <privateattributes title=\"\"/>\n      <privatestaticattributes title=\"\"/>\n      <friends title=\"\"/>\n      <related title=\"\" subtitle=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <services title=\"\"/>\n      <interfaces title=\"\"/>\n      <constructors title=\"\"/>\n      <functions title=\"\"/>\n      <related title=\"\"/>\n      <variables title=\"\"/>\n      <properties title=\"\"/>\n      <events title=\"\"/>\n    </memberdef>\n    <allmemberslink visible=\"yes\"/>\n    <usedfiles visible=\"$SHOW_USED_FILES\"/>\n    <authorsection visible=\"yes\"/>\n  </class>\n\n  <!-- Layout definition for a namespace page -->\n  <namespace>\n    <briefdescription visible=\"yes\"/>\n    <memberdecl>\n      <nestednamespaces visible=\"yes\" title=\"\"/>\n      <constantgroups visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n    </memberdef>\n    <authorsection visible=\"yes\"/>\n  </namespace>\n\n  <!-- Layout definition for a file page -->\n  <file>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_INCLUDE_FILES\"/>\n    <includegraph visible=\"$INCLUDE_GRAPH\"/>\n    <includedbygraph visible=\"$INCLUDED_BY_GRAPH\"/>\n    <sourcelink visible=\"yes\"/>\n    <memberdecl>\n      <classes visible=\"yes\" title=\"\"/>\n      <namespaces visible=\"yes\" title=\"\"/>\n      <constantgroups visible=\"yes\" title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n    </memberdef>\n    <authorsection/>\n  </file>\n\n  <!-- Layout definition for a group page -->\n  <group>\n    <briefdescription visible=\"yes\"/>\n    <groupgraph visible=\"$GROUP_GRAPHS\"/>\n    <memberdecl>\n      <nestedgroups visible=\"yes\" title=\"\"/>\n      <dirs visible=\"yes\" title=\"\"/>\n      <files visible=\"yes\" title=\"\"/>\n      <namespaces visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <enumvalues title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <signals title=\"\"/>\n      <publicslots title=\"\"/>\n      <protectedslots title=\"\"/>\n      <privateslots title=\"\"/>\n      <events title=\"\"/>\n      <properties title=\"\"/>\n      <friends title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <pagedocs/>\n      <inlineclasses title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <enumvalues title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <signals title=\"\"/>\n      <publicslots title=\"\"/>\n      <protectedslots title=\"\"/>\n      <privateslots title=\"\"/>\n      <events title=\"\"/>\n      <properties title=\"\"/>\n      <friends title=\"\"/>\n    </memberdef>\n    <authorsection visible=\"yes\"/>\n  </group>\n\n  <!-- Layout definition for a directory page -->\n  <directory>\n    <briefdescription visible=\"yes\"/>\n    <directorygraph visible=\"yes\"/>\n    <memberdecl>\n      <dirs visible=\"yes\"/>\n      <files visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n  </directory>\n</doxygenlayout>\n"
  },
  {
    "path": "docs/config/DoxygenLayout_1_9_8.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doxygenlayout version=\"1.0\">\n  <!-- Generated by doxygen 1.9.8 -->\n  <!-- Navigation index tabs for HTML output -->\n  <navindex>\n    <tab type=\"mainpage\" visible=\"yes\" title=\"\"/>\n    <tab type=\"pages\" visible=\"yes\" title=\"\" intro=\"\"/>\n    <!-- SpECTRE change: add link to GitHub repo -->\n    <tab type=\"user\" visible=\"yes\" title=\"GitHub repository\" url=\"https://github.com/sxs-collaboration/spectre\"/>\n    <!-- SpECTRE change: add link to Python documentation -->\n    <tab type=\"user\" visible=\"yes\" title=\"Python interface\" url=\"py/index.html\"/>\n    <!-- SpECTRE change: add user group for doxygen documentation -->\n    <!-- moving all remaining tabs subgroups of this group -->\n    <!-- change visibility to no for  subgroups that we do not document -->\n    <tab type=\"usergroup\" visible=\"yes\" title=\"Code reference\">\n      <tab type=\"topics\" visible=\"yes\" title=\"\" intro=\"\"/>\n      <tab type=\"modules\" visible=\"no\" title=\"\" intro=\"\">\n        <tab type=\"modulelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"modulemembers\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"namespaces\" visible=\"yes\" title=\"\">\n        <tab type=\"namespacelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"namespacemembers\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"concepts\" visible=\"no\" title=\"\">\n      </tab>\n      <tab type=\"interfaces\" visible=\"no\" title=\"\">\n        <tab type=\"interfacelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"interfaceindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n        <tab type=\"interfacehierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"classes\" visible=\"yes\" title=\"\">\n        <tab type=\"classlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"classindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n        <tab type=\"hierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"classmembers\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"structs\" visible=\"no\" title=\"\">\n        <tab type=\"structlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"structindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n      </tab>\n      <tab type=\"exceptions\" visible=\"no\" title=\"\">\n        <tab type=\"exceptionlist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"exceptionindex\" visible=\"$ALPHABETICAL_INDEX\" title=\"\"/>\n        <tab type=\"exceptionhierarchy\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"files\" visible=\"no\" title=\"\">\n        <tab type=\"filelist\" visible=\"yes\" title=\"\" intro=\"\"/>\n        <tab type=\"globals\" visible=\"yes\" title=\"\" intro=\"\"/>\n      </tab>\n      <tab type=\"examples\" visible=\"yes\" title=\"\" intro=\"\"/>\n    </tab>\n  </navindex>\n\n  <!-- Layout definition for a class page -->\n  <class>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_HEADERFILE\"/>\n    <inheritancegraph visible=\"$CLASS_GRAPH\"/>\n    <collaborationgraph visible=\"yes\"/>\n    <memberdecl>\n      <nestedclasses visible=\"yes\" title=\"\"/>\n      <publictypes title=\"\"/>\n      <services title=\"\"/>\n      <interfaces title=\"\"/>\n      <publicslots title=\"\"/>\n      <signals title=\"\"/>\n      <publicmethods title=\"\"/>\n      <publicstaticmethods title=\"\"/>\n      <publicattributes title=\"\"/>\n      <publicstaticattributes title=\"\"/>\n      <protectedtypes title=\"\"/>\n      <protectedslots title=\"\"/>\n      <protectedmethods title=\"\"/>\n      <protectedstaticmethods title=\"\"/>\n      <protectedattributes title=\"\"/>\n      <protectedstaticattributes title=\"\"/>\n      <packagetypes title=\"\"/>\n      <packagemethods title=\"\"/>\n      <packagestaticmethods title=\"\"/>\n      <packageattributes title=\"\"/>\n      <packagestaticattributes title=\"\"/>\n      <properties title=\"\"/>\n      <events title=\"\"/>\n      <privatetypes title=\"\"/>\n      <privateslots title=\"\"/>\n      <privatemethods title=\"\"/>\n      <privatestaticmethods title=\"\"/>\n      <privateattributes title=\"\"/>\n      <privatestaticattributes title=\"\"/>\n      <friends title=\"\"/>\n      <related title=\"\" subtitle=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <typedefs title=\"\"/>\n      <enums title=\"\"/>\n      <services title=\"\"/>\n      <interfaces title=\"\"/>\n      <constructors title=\"\"/>\n      <functions title=\"\"/>\n      <related title=\"\"/>\n      <variables title=\"\"/>\n      <properties title=\"\"/>\n      <events title=\"\"/>\n    </memberdef>\n    <allmemberslink visible=\"yes\"/>\n    <usedfiles visible=\"$SHOW_USED_FILES\"/>\n    <authorsection visible=\"yes\"/>\n  </class>\n\n  <!-- Layout definition for a namespace page -->\n  <namespace>\n    <briefdescription visible=\"yes\"/>\n    <memberdecl>\n      <nestednamespaces visible=\"yes\" title=\"\"/>\n      <constantgroups visible=\"yes\" title=\"\"/>\n      <interfaces visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <concepts visible=\"yes\" title=\"\"/>\n      <structs visible=\"yes\" title=\"\"/>\n      <exceptions visible=\"yes\" title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n    </memberdef>\n    <authorsection visible=\"yes\"/>\n  </namespace>\n\n  <!-- Layout definition for a concept page -->\n  <concept>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_HEADERFILE\"/>\n    <definition visible=\"yes\" title=\"\"/>\n    <detaileddescription title=\"\"/>\n    <authorsection visible=\"yes\"/>\n  </concept>\n\n  <!-- Layout definition for a file page -->\n  <file>\n    <briefdescription visible=\"yes\"/>\n    <includes visible=\"$SHOW_INCLUDE_FILES\"/>\n    <includegraph visible=\"yes\"/>\n    <includedbygraph visible=\"yes\"/>\n    <sourcelink visible=\"yes\"/>\n    <memberdecl>\n      <interfaces visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <structs visible=\"yes\" title=\"\"/>\n      <exceptions visible=\"yes\" title=\"\"/>\n      <namespaces visible=\"yes\" title=\"\"/>\n      <concepts visible=\"yes\" title=\"\"/>\n      <constantgroups visible=\"yes\" title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <inlineclasses title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n    </memberdef>\n    <authorsection/>\n  </file>\n\n  <!-- Layout definition for a group page -->\n  <group>\n    <briefdescription visible=\"yes\"/>\n    <groupgraph visible=\"yes\"/>\n    <memberdecl>\n      <nestedgroups visible=\"yes\" title=\"\"/>\n      <modules visible=\"yes\" title=\"\"/>\n      <dirs visible=\"yes\" title=\"\"/>\n      <files visible=\"yes\" title=\"\"/>\n      <namespaces visible=\"yes\" title=\"\"/>\n      <concepts visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <enumvalues title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <signals title=\"\"/>\n      <publicslots title=\"\"/>\n      <protectedslots title=\"\"/>\n      <privateslots title=\"\"/>\n      <events title=\"\"/>\n      <properties title=\"\"/>\n      <friends title=\"\"/>\n      <membergroups visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdef>\n      <pagedocs/>\n      <inlineclasses title=\"\"/>\n      <defines title=\"\"/>\n      <typedefs title=\"\"/>\n      <sequences title=\"\"/>\n      <dictionaries title=\"\"/>\n      <enums title=\"\"/>\n      <enumvalues title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <signals title=\"\"/>\n      <publicslots title=\"\"/>\n      <protectedslots title=\"\"/>\n      <privateslots title=\"\"/>\n      <events title=\"\"/>\n      <properties title=\"\"/>\n      <friends title=\"\"/>\n    </memberdef>\n    <authorsection visible=\"yes\"/>\n  </group>\n\n  <!-- Layout definition for a C++20 module page -->\n  <module>\n    <briefdescription visible=\"yes\"/>\n    <exportedmodules visible=\"yes\"/>\n    <memberdecl>\n      <concepts visible=\"yes\" title=\"\"/>\n      <classes visible=\"yes\" title=\"\"/>\n      <enums title=\"\"/>\n      <typedefs title=\"\"/>\n      <functions title=\"\"/>\n      <variables title=\"\"/>\n      <membergroups title=\"\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n    <memberdecl>\n      <files visible=\"yes\"/>\n    </memberdecl>\n  </module>\n\n  <!-- Layout definition for a directory page -->\n  <directory>\n    <briefdescription visible=\"yes\"/>\n    <directorygraph visible=\"yes\"/>\n    <memberdecl>\n      <dirs visible=\"yes\"/>\n      <files visible=\"yes\"/>\n    </memberdecl>\n    <detaileddescription title=\"\"/>\n  </directory>\n</doxygenlayout>\n"
  },
  {
    "path": "docs/config/MathJax.js",
    "content": "window.MathJax = {\n    tex: {\n        tags: \"ams\",\n        packages: ['base', 'ams', 'bbox', 'color', 'physics', 'newcommand',\n            'boldsymbol']\n    },\n    options: {\n        ignoreHtmlClass: 'tex2jax_ignore',\n        processHtmlClass: 'tex2jax_process'\n    },\n    loader: {\n        load: ['[tex]/bbox', '[tex]/color', '[tex]/physics', '[tex]/newcommand',\n            '[tex]/boldsymbol']\n    }\n};\n\n(function () {\n  var script = document.createElement('script');\n  script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3.1.0/es5/tex-chtml-full.js';\n  // cryptographic hashes can be found here:\n  // https://www.jsdelivr.com/package/npm/mathjax?path=es5\n  script.integrity = 'sha256-HJUiQvFxmEVWZ3D0qyz7Bg0JyJ2bkriI/WHQYo1ch5Y=';\n  script.crossOrigin = 'anonymous';\n  script.async = true;\n  document.head.appendChild(script);\n})();\n"
  },
  {
    "path": "docs/config/cppreference-doxygen-web.tag.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n<tagfile>\n  <compound kind=\"file\">\n    <name>algorithm</name>\n    <filename>cpp/header/algorithm</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>any</name>\n    <filename>cpp/header/any</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>array</name>\n    <filename>cpp/header/array</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>atomic</name>\n    <filename>cpp/header/atomic</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>bitset</name>\n    <filename>cpp/header/bitset</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cassert</name>\n    <filename>cpp/header/cassert</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>ccomplex</name>\n    <filename>cpp/header/ccomplex</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cctype</name>\n    <filename>cpp/header/cctype</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cerrno</name>\n    <filename>cpp/header/cerrno</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cfenv</name>\n    <filename>cpp/header/cfenv</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cfloat</name>\n    <filename>cpp/header/cfloat</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>chrono</name>\n    <filename>cpp/header/chrono</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cinttypes</name>\n    <filename>cpp/header/cinttypes</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>ciso646</name>\n    <filename>cpp/header/ciso646</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>climits</name>\n    <filename>cpp/header/climits</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>clocale</name>\n    <filename>cpp/header/clocale</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cmath</name>\n    <filename>cpp/header/cmath</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>codecvt</name>\n    <filename>cpp/header/codecvt</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>complex</name>\n    <filename>cpp/header/complex</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>condition_variable</name>\n    <filename>cpp/header/condition_variable</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>csetjmp</name>\n    <filename>cpp/header/csetjmp</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>csignal</name>\n    <filename>cpp/header/csignal</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cstdalign</name>\n    <filename>cpp/header/cstdalign</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cstdarg</name>\n    <filename>cpp/header/cstdarg</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cstdbool</name>\n    <filename>cpp/header/cstdbool</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cstddef</name>\n    <filename>cpp/header/cstddef</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cstdint</name>\n    <filename>cpp/header/cstdint</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cstdio</name>\n    <filename>cpp/header/cstdio</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cstdlib</name>\n    <filename>cpp/header/cstdlib</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cstring</name>\n    <filename>cpp/header/cstring</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>ctgmath</name>\n    <filename>cpp/header/ctgmath</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>ctime</name>\n    <filename>cpp/header/ctime</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cuchar</name>\n    <filename>cpp/header/cuchar</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cwchar</name>\n    <filename>cpp/header/cwchar</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>cwctype</name>\n    <filename>cpp/header/cwctype</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>deque</name>\n    <filename>cpp/header/deque</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>exception</name>\n    <filename>cpp/header/exception</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>execution</name>\n    <filename>cpp/header/execution</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>filesystem</name>\n    <filename>cpp/header/filesystem</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>forward_list</name>\n    <filename>cpp/header/forward_list</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>fstream</name>\n    <filename>cpp/header/fstream</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>functional</name>\n    <filename>cpp/header/functional</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>future</name>\n    <filename>cpp/header/future</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>initializer_list</name>\n    <filename>cpp/header/initializer_list</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>iomanip</name>\n    <filename>cpp/header/iomanip</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>ios</name>\n    <filename>cpp/header/ios</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>iosfwd</name>\n    <filename>cpp/header/iosfwd</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>iostream</name>\n    <filename>cpp/header/iostream</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>istream</name>\n    <filename>cpp/header/istream</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>iterator</name>\n    <filename>cpp/header/iterator</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>limits</name>\n    <filename>cpp/header/limits</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>list</name>\n    <filename>cpp/header/list</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>locale</name>\n    <filename>cpp/header/locale</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>map</name>\n    <filename>cpp/header/map</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>memory</name>\n    <filename>cpp/header/memory</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>memory_resource</name>\n    <filename>cpp/header/memory_resource</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>mutex</name>\n    <filename>cpp/header/mutex</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>new</name>\n    <filename>cpp/header/new</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>numeric</name>\n    <filename>cpp/header/numeric</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>optional</name>\n    <filename>cpp/header/optional</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>ostream</name>\n    <filename>cpp/header/ostream</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>queue</name>\n    <filename>cpp/header/queue</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>random</name>\n    <filename>cpp/header/random</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>ratio</name>\n    <filename>cpp/header/ratio</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>regex</name>\n    <filename>cpp/header/regex</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>scoped_allocator</name>\n    <filename>cpp/header/scoped_allocator</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>set</name>\n    <filename>cpp/header/set</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>shared_mutex</name>\n    <filename>cpp/header/shared_mutex</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>sstream</name>\n    <filename>cpp/header/sstream</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>stack</name>\n    <filename>cpp/header/stack</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>stdexcept</name>\n    <filename>cpp/header/stdexcept</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>streambuf</name>\n    <filename>cpp/header/streambuf</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>string</name>\n    <filename>cpp/header/string</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>string_view</name>\n    <filename>cpp/header/string_view</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>strstream</name>\n    <filename>cpp/header/strstream</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>system_error</name>\n    <filename>cpp/header/system_error</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>thread</name>\n    <filename>cpp/header/thread</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>tuple</name>\n    <filename>cpp/header/tuple</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>type_traits</name>\n    <filename>cpp/header/type_traits</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>typeindex</name>\n    <filename>cpp/header/typeindex</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>typeinfo</name>\n    <filename>cpp/header/typeinfo</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>unordered_map</name>\n    <filename>cpp/header/unordered_map</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>unordered_set</name>\n    <filename>cpp/header/unordered_set</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>utility</name>\n    <filename>cpp/header/utility</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>valarray</name>\n    <filename>cpp/header/valarray</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>variant</name>\n    <filename>cpp/header/variant</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"file\">\n    <name>vector</name>\n    <filename>cpp/header/vector</filename>\n    <namespace>std</namespace>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std</name>\n    <filename></filename>\n    <class kind=\"class\">std::FILE</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>_Exit</name>\n      <anchorfile>cpp/utility/program/_Exit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>abort</name>\n      <anchorfile>cpp/utility/program/abort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>abs(float)</name>\n      <anchorfile>cpp/numeric/math/fabs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>abs(int)</name>\n      <anchorfile>cpp/numeric/math/abs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>accumulate</name>\n      <anchorfile>cpp/algorithm/accumulate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>acos</name>\n      <anchorfile>cpp/numeric/math/acos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>acosh</name>\n      <anchorfile>cpp/numeric/math/acosh</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::add_const</class>\n    <class kind=\"class\">std::add_const_t</class>\n    <class kind=\"class\">std::add_cv</class>\n    <class kind=\"class\">std::add_cv_t</class>\n    <class kind=\"class\">std::add_lvalue_reference</class>\n    <class kind=\"class\">std::add_lvalue_reference_t</class>\n    <class kind=\"class\">std::add_pointer</class>\n    <class kind=\"class\">std::add_pointer_t</class>\n    <class kind=\"class\">std::add_rvalue_reference</class>\n    <class kind=\"class\">std::add_rvalue_reference_t</class>\n    <class kind=\"class\">std::add_volatile</class>\n    <class kind=\"class\">std::add_volatile_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>addressof</name>\n      <anchorfile>cpp/memory/addressof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>adjacent_difference</name>\n      <anchorfile>cpp/algorithm/adjacent_difference</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>adjacent_find</name>\n      <anchorfile>cpp/algorithm/adjacent_find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::adopt_lock_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>advance</name>\n      <anchorfile>cpp/iterator/advance</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>align</name>\n      <anchorfile>cpp/memory/align</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::align_val_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>aligned_alloc</name>\n      <anchorfile>cpp/memory/c/aligned_alloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::aligned_storage</class>\n    <class kind=\"class\">std::aligned_storage_t</class>\n    <class kind=\"class\">std::aligned_union</class>\n    <class kind=\"class\">std::aligned_union_t</class>\n    <class kind=\"class\">std::alignment_of</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>alignment_of_v</name>\n      <anchorfile>cpp/types/alignment_of</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>all_of</name>\n      <anchorfile>cpp/algorithm/all_any_none_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate_shared</name>\n      <anchorfile>cpp/memory/shared_ptr/allocate_shared</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::allocator</class>\n    <class kind=\"class\">std::allocator_arg_t</class>\n    <class kind=\"class\">std::allocator_traits</class>\n    <class kind=\"class\">std::any</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>any_cast</name>\n      <anchorfile>cpp/utility/any/any_cast</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>any_of</name>\n      <anchorfile>cpp/algorithm/all_any_none_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>apply</name>\n      <anchorfile>cpp/utility/apply</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::array</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>as_const</name>\n      <anchorfile>cpp/utility/as_const</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>asctime</name>\n      <anchorfile>cpp/chrono/c/asctime</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>asin</name>\n      <anchorfile>cpp/numeric/math/asin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>asinh</name>\n      <anchorfile>cpp/numeric/math/asinh</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assoc_laguerre</name>\n      <anchorfile>cpp/numeric/special_math/assoc_laguerre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assoc_laguerref</name>\n      <anchorfile>cpp/numeric/special_math/assoc_laguerre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assoc_laguerrel</name>\n      <anchorfile>cpp/numeric/special_math/assoc_laguerre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assoc_legendre</name>\n      <anchorfile>cpp/numeric/special_math/assoc_legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assoc_legendref</name>\n      <anchorfile>cpp/numeric/special_math/assoc_legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assoc_legendrel</name>\n      <anchorfile>cpp/numeric/special_math/assoc_legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>async</name>\n      <anchorfile>cpp/thread/async</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at_quick_exit</name>\n      <anchorfile>cpp/utility/program/at_quick_exit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atan</name>\n      <anchorfile>cpp/numeric/math/atan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atan2</name>\n      <anchorfile>cpp/numeric/math/atan2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atanh</name>\n      <anchorfile>cpp/numeric/math/atanh</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atexit</name>\n      <anchorfile>cpp/utility/program/atexit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atof</name>\n      <anchorfile>cpp/string/byte/atof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atoi</name>\n      <anchorfile>cpp/string/byte/atoi</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atol</name>\n      <anchorfile>cpp/string/byte/atoi</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atoll</name>\n      <anchorfile>cpp/string/byte/atoi</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::atomic</class>\n    <class kind=\"class\">std::atomic_bool</class>\n    <class kind=\"class\">std::atomic_char16_t</class>\n    <class kind=\"class\">std::atomic_char32_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic_compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_compare_exchange_strong_explicit</name>\n      <anchorfile>cpp/atomic/atomic_compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic_compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_compare_exchange_weak_explicit</name>\n      <anchorfile>cpp/atomic/atomic_compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_exchange</name>\n      <anchorfile>cpp/atomic/atomic_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_exchange_explicit</name>\n      <anchorfile>cpp/atomic/atomic_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_add</name>\n      <anchorfile>cpp/atomic/atomic_fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_add_explicit</name>\n      <anchorfile>cpp/atomic/atomic_fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_and</name>\n      <anchorfile>cpp/atomic/atomic_fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_and_explicit</name>\n      <anchorfile>cpp/atomic/atomic_fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_or</name>\n      <anchorfile>cpp/atomic/atomic_fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_or_explicit</name>\n      <anchorfile>cpp/atomic/atomic_fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic_fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_sub_explicit</name>\n      <anchorfile>cpp/atomic/atomic_fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic_fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_fetch_xor_explicit</name>\n      <anchorfile>cpp/atomic/atomic_fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::atomic_flag</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_init</name>\n      <anchorfile>cpp/atomic/atomic_init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::atomic_int</class>\n    <class kind=\"class\">std::atomic_int16_t</class>\n    <class kind=\"class\">std::atomic_int32_t</class>\n    <class kind=\"class\">std::atomic_int64_t</class>\n    <class kind=\"class\">std::atomic_int8_t</class>\n    <class kind=\"class\">std::atomic_int_fast16_t</class>\n    <class kind=\"class\">std::atomic_int_fast32_t</class>\n    <class kind=\"class\">std::atomic_int_fast64_t</class>\n    <class kind=\"class\">std::atomic_int_fast8_t</class>\n    <class kind=\"class\">std::atomic_int_least16_t</class>\n    <class kind=\"class\">std::atomic_int_least32_t</class>\n    <class kind=\"class\">std::atomic_int_least64_t</class>\n    <class kind=\"class\">std::atomic_int_least8_t</class>\n    <class kind=\"class\">std::atomic_intmax_t</class>\n    <class kind=\"class\">std::atomic_intptr_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic_is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::atomic_llong</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_load</name>\n      <anchorfile>cpp/atomic/atomic_load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_load_explicit</name>\n      <anchorfile>cpp/atomic/atomic_load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::atomic_long</class>\n    <class kind=\"class\">std::atomic_ptrdiff_t</class>\n    <class kind=\"class\">std::atomic_schar</class>\n    <class kind=\"class\">std::atomic_short</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_signal_fence</name>\n      <anchorfile>cpp/atomic/atomic_signal_fence</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::atomic_size_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_store</name>\n      <anchorfile>cpp/atomic/atomic_store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_store_explicit</name>\n      <anchorfile>cpp/atomic/atomic_store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_thread_fence</name>\n      <anchorfile>cpp/atomic/atomic_thread_fence</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::atomic_uchar</class>\n    <class kind=\"class\">std::atomic_uint</class>\n    <class kind=\"class\">std::atomic_uint16_t</class>\n    <class kind=\"class\">std::atomic_uint32_t</class>\n    <class kind=\"class\">std::atomic_uint64_t</class>\n    <class kind=\"class\">std::atomic_uint8_t</class>\n    <class kind=\"class\">std::atomic_uint_fast16_t</class>\n    <class kind=\"class\">std::atomic_uint_fast32_t</class>\n    <class kind=\"class\">std::atomic_uint_fast64_t</class>\n    <class kind=\"class\">std::atomic_uint_fast8_t</class>\n    <class kind=\"class\">std::atomic_uint_least16_t</class>\n    <class kind=\"class\">std::atomic_uint_least32_t</class>\n    <class kind=\"class\">std::atomic_uint_least64_t</class>\n    <class kind=\"class\">std::atomic_uint_least8_t</class>\n    <class kind=\"class\">std::atomic_uintmax_t</class>\n    <class kind=\"class\">std::atomic_uintptr_t</class>\n    <class kind=\"class\">std::atomic_ullong</class>\n    <class kind=\"class\">std::atomic_ulong</class>\n    <class kind=\"class\">std::atomic_ushort</class>\n    <class kind=\"class\">std::atomic_wchar_t</class>\n    <class kind=\"class\">std::auto_ptr</class>\n    <class kind=\"class\">std::back_insert_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back_inserter</name>\n      <anchorfile>cpp/iterator/back_inserter</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::bad_alloc</class>\n    <class kind=\"class\">std::bad_any_cast</class>\n    <class kind=\"class\">std::bad_array_length</class>\n    <class kind=\"class\">std::bad_array_new_length</class>\n    <class kind=\"class\">std::bad_cast</class>\n    <class kind=\"class\">std::bad_exception</class>\n    <class kind=\"class\">std::bad_function_call</class>\n    <class kind=\"class\">std::bad_optional_access</class>\n    <class kind=\"class\">std::bad_typeid</class>\n    <class kind=\"class\">std::bad_variant_access</class>\n    <class kind=\"class\">std::bad_weak_ptr</class>\n    <class kind=\"class\">std::basic_filebuf</class>\n    <class kind=\"class\">std::basic_fstream</class>\n    <class kind=\"class\">std::basic_ifstream</class>\n    <class kind=\"class\">std::basic_ios</class>\n    <class kind=\"class\">std::basic_iostream</class>\n    <class kind=\"class\">std::basic_istream</class>\n    <class kind=\"class\">std::basic_istringstream</class>\n    <class kind=\"class\">std::basic_ofstream</class>\n    <class kind=\"class\">std::basic_ostream</class>\n    <class kind=\"class\">std::basic_ostringstream</class>\n    <class kind=\"class\">std::basic_regex</class>\n    <class kind=\"class\">std::basic_streambuf</class>\n    <class kind=\"class\">std::basic_string</class>\n    <class kind=\"class\">std::basic_string_view</class>\n    <class kind=\"class\">std::basic_stringbuf</class>\n    <class kind=\"class\">std::basic_stringstream</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/iterator/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::bernoulli_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>beta</name>\n      <anchorfile>cpp/numeric/special_math/beta</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>betaf</name>\n      <anchorfile>cpp/numeric/special_math/beta</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>betal</name>\n      <anchorfile>cpp/numeric/special_math/beta</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::bidirectional_iterator_tag</class>\n    <class kind=\"class\">std::binary_function</class>\n    <class kind=\"class\">std::binary_negate</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>binary_search</name>\n      <anchorfile>cpp/algorithm/binary_search</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bind</name>\n      <anchorfile>cpp/utility/functional/bind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::binomial_distribution</class>\n    <class kind=\"class\">std::bit_and</class>\n    <class kind=\"class\">std::bit_not</class>\n    <class kind=\"class\">std::bit_or</class>\n    <class kind=\"class\">std::bitset</class>\n    <class kind=\"class\">std::bool_constant</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>boolalpha</name>\n      <anchorfile>cpp/io/manip/boolalpha</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::boyer_moore_horspool_searcher</class>\n    <class kind=\"class\">std::boyer_moore_searcher</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bsearch</name>\n      <anchorfile>cpp/algorithm/bsearch</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>btowc</name>\n      <anchorfile>cpp/string/multibyte/btowc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c16rtomb</name>\n      <anchorfile>cpp/string/multibyte/c16rtomb</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c32rtomb</name>\n      <anchorfile>cpp/string/multibyte/c32rtomb</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>call_once</name>\n      <anchorfile>cpp/thread/call_once</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>calloc</name>\n      <anchorfile>cpp/memory/c/calloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::cauchy_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/iterator/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbrt</name>\n      <anchorfile>cpp/numeric/math/cbrt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ceil</name>\n      <anchorfile>cpp/numeric/math/ceil</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/iterator/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::centi</class>\n    <class kind=\"class\">std::cerr</class>\n    <class kind=\"class\">std::char_traits</class>\n    <class kind=\"class\">std::chi_squared_distribution</class>\n    <namespace>std::chrono</namespace>\n    <class kind=\"class\">std::cin</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clamp</name>\n      <anchorfile>cpp/algorithm/clamp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clearerr</name>\n      <anchorfile>cpp/io/c/clearerr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clock</name>\n      <anchorfile>cpp/chrono/c/clock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::clock_t</class>\n    <class kind=\"class\">std::clog</class>\n    <class kind=\"class\">std::cmatch</class>\n    <class kind=\"class\">std::codecvt</class>\n    <class kind=\"class\">std::codecvt_base</class>\n    <class kind=\"class\">std::codecvt_byname</class>\n    <class kind=\"class\">std::codecvt_utf16</class>\n    <class kind=\"class\">std::codecvt_utf8</class>\n    <class kind=\"class\">std::codecvt_utf8_utf16</class>\n    <class kind=\"class\">std::collate</class>\n    <class kind=\"class\">std::collate_byname</class>\n    <class kind=\"class\">std::common_type</class>\n    <class kind=\"class\">std::common_type_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_1</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_1</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_1f</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_1</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_1l</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_1</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_2</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_2f</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_2l</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_3</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_3f</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>comp_ellint_3l</name>\n      <anchorfile>cpp/numeric/special_math/comp_ellint_3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::complex</class>\n    <class kind=\"class\">std::condition_variable</class>\n    <class kind=\"class\">std::condition_variable_any</class>\n    <class kind=\"class\">std::conditional</class>\n    <class kind=\"class\">std::conditional_t</class>\n    <class kind=\"class\">std::conjunction</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>conjunction_v</name>\n      <anchorfile>cpp/types/conjunction</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>const_pointer_cast</name>\n      <anchorfile>cpp/memory/shared_ptr/pointer_cast</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/algorithm/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy_backward</name>\n      <anchorfile>cpp/algorithm/copy_backward</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy_if</name>\n      <anchorfile>cpp/algorithm/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy_n</name>\n      <anchorfile>cpp/algorithm/copy_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copysign</name>\n      <anchorfile>cpp/numeric/math/copysign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cos</name>\n      <anchorfile>cpp/numeric/math/cos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cosh</name>\n      <anchorfile>cpp/numeric/math/cosh</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/algorithm/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count_if</name>\n      <anchorfile>cpp/algorithm/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::cout</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/iterator/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cref</name>\n      <anchorfile>cpp/utility/functional/ref</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::cregex_iterator</class>\n    <class kind=\"class\">std::cregex_token_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/iterator/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::csub_match</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ctime</name>\n      <anchorfile>cpp/chrono/c/ctime</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ctype</class>\n    <class kind=\"class\">std::ctype_base</class>\n    <class kind=\"class\">std::ctype_byname</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>current_exception</name>\n      <anchorfile>cpp/error/current_exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_i</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_i</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_if</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_i</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_il</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_i</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_j</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_j</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_jf</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_j</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_jl</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_j</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_k</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_k</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_kf</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_k</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_bessel_kl</name>\n      <anchorfile>cpp/numeric/special_math/cyl_bessel_k</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_neumann</name>\n      <anchorfile>cpp/numeric/special_math/cyl_neumann</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_neumannf</name>\n      <anchorfile>cpp/numeric/special_math/cyl_neumann</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cyl_neumannl</name>\n      <anchorfile>cpp/numeric/special_math/cyl_neumann</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/iterator/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>dec</name>\n      <anchorfile>cpp/io/manip/hex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::deca</class>\n    <class kind=\"class\">std::decay</class>\n    <class kind=\"class\">std::decay_t</class>\n    <class kind=\"class\">std::deci</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>declare_no_pointers</name>\n      <anchorfile>cpp/memory/gc/declare_no_pointers</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>declare_reachable</name>\n      <anchorfile>cpp/memory/gc/declare_reachable</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>declval</name>\n      <anchorfile>cpp/utility/declval</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::default_delete</class>\n    <class kind=\"class\">std::default_random_engine</class>\n    <class kind=\"class\">std::default_searcher</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>defaultfloat</name>\n      <anchorfile>cpp/io/manip/fixed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::defer_lock_t</class>\n    <class kind=\"class\">std::deque</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>destroy</name>\n      <anchorfile>cpp/memory/destroy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>destroy_at</name>\n      <anchorfile>cpp/memory/destroy_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>destroy_n</name>\n      <anchorfile>cpp/memory/destroy_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>difftime</name>\n      <anchorfile>cpp/chrono/c/difftime</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::discard_block_engine</class>\n    <class kind=\"class\">std::discrete_distribution</class>\n    <class kind=\"class\">std::disjunction</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>disjunction_v</name>\n      <anchorfile>cpp/types/disjunction</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>distance</name>\n      <anchorfile>cpp/iterator/distance</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>div</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::div_t</class>\n    <class kind=\"class\">std::divides</class>\n    <class kind=\"class\">std::domain_error</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>dynamic_pointer_cast</name>\n      <anchorfile>cpp/memory/shared_ptr/pointer_cast</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_1</name>\n      <anchorfile>cpp/numeric/special_math/ellint_1</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_1f</name>\n      <anchorfile>cpp/numeric/special_math/ellint_1</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_1l</name>\n      <anchorfile>cpp/numeric/special_math/ellint_1</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_2</name>\n      <anchorfile>cpp/numeric/special_math/ellint_2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_2f</name>\n      <anchorfile>cpp/numeric/special_math/ellint_2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_2l</name>\n      <anchorfile>cpp/numeric/special_math/ellint_2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_3</name>\n      <anchorfile>cpp/numeric/special_math/ellint_3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_3f</name>\n      <anchorfile>cpp/numeric/special_math/ellint_3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ellint_3l</name>\n      <anchorfile>cpp/numeric/special_math/ellint_3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/iterator/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::enable_if</class>\n    <class kind=\"class\">std::enable_if_t</class>\n    <class kind=\"class\">std::enable_shared_from_this</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/iterator/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>endl</name>\n      <anchorfile>cpp/io/manip/endl</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ends</name>\n      <anchorfile>cpp/io/manip/ends</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal</name>\n      <anchorfile>cpp/algorithm/equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/algorithm/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::equal_to</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erf</name>\n      <anchorfile>cpp/numeric/math/erf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erfc</name>\n      <anchorfile>cpp/numeric/math/erfc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::errc</class>\n    <class kind=\"class\">std::error_category</class>\n    <class kind=\"class\">std::error_code</class>\n    <class kind=\"class\">std::error_condition</class>\n    <class kind=\"class\">std::exa</class>\n    <class kind=\"class\">std::exception</class>\n    <class kind=\"class\">std::exception_ptr</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/utility/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exclusive_scan</name>\n      <anchorfile>cpp/algorithm/exclusive_scan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <namespace>std::execution</namespace>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exit</name>\n      <anchorfile>cpp/utility/program/exit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exp</name>\n      <anchorfile>cpp/numeric/math/exp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exp2</name>\n      <anchorfile>cpp/numeric/math/exp2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <namespace>std::experimental</namespace>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>expint</name>\n      <anchorfile>cpp/numeric/special_math/expint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>expintf</name>\n      <anchorfile>cpp/numeric/special_math/expint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>expintl</name>\n      <anchorfile>cpp/numeric/special_math/expint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>expm1</name>\n      <anchorfile>cpp/numeric/math/expm1</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::exponential_distribution</class>\n    <class kind=\"class\">std::extent</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>extent_v</name>\n      <anchorfile>cpp/types/extent</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::extreme_value_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fabs</name>\n      <anchorfile>cpp/numeric/math/fabs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::false_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fclose</name>\n      <anchorfile>cpp/io/c/fclose</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fdim</name>\n      <anchorfile>cpp/numeric/math/fdim</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>feclearexcept</name>\n      <anchorfile>cpp/numeric/fenv/feclearexcept</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fegetenv</name>\n      <anchorfile>cpp/numeric/fenv/feenv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fegetexceptflag</name>\n      <anchorfile>cpp/numeric/fenv/feexceptflag</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fegetround</name>\n      <anchorfile>cpp/numeric/fenv/feround</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>feholdexcept</name>\n      <anchorfile>cpp/numeric/fenv/feholdexcept</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::femto</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>feof</name>\n      <anchorfile>cpp/io/c/feof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>feraiseexcept</name>\n      <anchorfile>cpp/numeric/fenv/feraiseexcept</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ferror</name>\n      <anchorfile>cpp/io/c/ferror</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fesetenv</name>\n      <anchorfile>cpp/numeric/fenv/feenv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fesetexceptflag</name>\n      <anchorfile>cpp/numeric/fenv/feexceptflag</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fesetround</name>\n      <anchorfile>cpp/numeric/fenv/feround</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetestexcept</name>\n      <anchorfile>cpp/numeric/fenv/fetestexcept</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>feupdateenv</name>\n      <anchorfile>cpp/numeric/fenv/feupdateenv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fflush</name>\n      <anchorfile>cpp/io/c/fflush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fgetc</name>\n      <anchorfile>cpp/io/c/fgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fgetpos</name>\n      <anchorfile>cpp/io/c/fgetpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fgets</name>\n      <anchorfile>cpp/io/c/fgets</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fgetwc</name>\n      <anchorfile>cpp/io/c/fgetwc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fgetws</name>\n      <anchorfile>cpp/io/c/fgetws</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::filebuf</class>\n    <namespace>std::filesystem</namespace>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/algorithm/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill_n</name>\n      <anchorfile>cpp/algorithm/fill_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/algorithm/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_end</name>\n      <anchorfile>cpp/algorithm/find_end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/algorithm/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_if</name>\n      <anchorfile>cpp/algorithm/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_if_not</name>\n      <anchorfile>cpp/algorithm/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::fisher_f_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fixed</name>\n      <anchorfile>cpp/io/manip/fixed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>floor</name>\n      <anchorfile>cpp/numeric/math/floor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/manip/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fma</name>\n      <anchorfile>cpp/numeric/math/fma</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fmax</name>\n      <anchorfile>cpp/numeric/math/fmax</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fmin</name>\n      <anchorfile>cpp/numeric/math/fmin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fmod</name>\n      <anchorfile>cpp/numeric/math/fmod</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fopen</name>\n      <anchorfile>cpp/io/c/fopen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>for_each</name>\n      <anchorfile>cpp/algorithm/for_each</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>for_each_n</name>\n      <anchorfile>cpp/algorithm/for_each_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>forward</name>\n      <anchorfile>cpp/utility/forward</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>forward_as_tuple</name>\n      <anchorfile>cpp/utility/tuple/forward_as_tuple</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::forward_iterator_tag</class>\n    <class kind=\"class\">std::forward_list</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fpclassify</name>\n      <anchorfile>cpp/numeric/math/fpclassify</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::fpos</class>\n    <class kind=\"class\">std::fpos_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fprintf</name>\n      <anchorfile>cpp/io/c/fprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fputc</name>\n      <anchorfile>cpp/io/c/fputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fputs</name>\n      <anchorfile>cpp/io/c/fputs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fputwc</name>\n      <anchorfile>cpp/io/c/fputwc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fputws</name>\n      <anchorfile>cpp/io/c/fputws</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fread</name>\n      <anchorfile>cpp/io/c/fread</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>free</name>\n      <anchorfile>cpp/memory/c/free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>freopen</name>\n      <anchorfile>cpp/io/c/freopen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>frexp</name>\n      <anchorfile>cpp/numeric/math/frexp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::front_insert_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front_inserter</name>\n      <anchorfile>cpp/iterator/front_inserter</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fscanf</name>\n      <anchorfile>cpp/io/c/fscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fseek</name>\n      <anchorfile>cpp/io/c/fseek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fsetpos</name>\n      <anchorfile>cpp/io/c/fsetpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::fstream</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ftell</name>\n      <anchorfile>cpp/io/c/ftell</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::function</class>\n    <class kind=\"class\">std::future</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>future_category</name>\n      <anchorfile>cpp/thread/future/future_category</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::future_error</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fwprintf</name>\n      <anchorfile>cpp/io/c/fwprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fwrite</name>\n      <anchorfile>cpp/io/c/fwrite</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fwscanf</name>\n      <anchorfile>cpp/io/c/fwscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::gamma_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcd</name>\n      <anchorfile>cpp/numeric/gcd</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generate</name>\n      <anchorfile>cpp/algorithm/generate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generate_canonical</name>\n      <anchorfile>cpp/numeric/random/generate_canonical</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generate_n</name>\n      <anchorfile>cpp/algorithm/generate_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_category</name>\n      <anchorfile>cpp/error/generic_category</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::geometric_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_if</name>\n      <anchorfile>cpp/utility/variant/get_if</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_money</name>\n      <anchorfile>cpp/io/manip/get_money</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_new_handler</name>\n      <anchorfile>cpp/memory/new/get_new_handler</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_pointer_safety</name>\n      <anchorfile>cpp/memory/gc/get_pointer_safety</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_temporary_buffer</name>\n      <anchorfile>cpp/memory/get_temporary_buffer</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_terminate</name>\n      <anchorfile>cpp/error/get_terminate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_time</name>\n      <anchorfile>cpp/io/manip/get_time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_unexpected</name>\n      <anchorfile>cpp/error/get_unexpected</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getc</name>\n      <anchorfile>cpp/io/c/fgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getchar</name>\n      <anchorfile>cpp/io/c/getchar</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getenv</name>\n      <anchorfile>cpp/utility/program/getenv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/string/basic_string/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gets</name>\n      <anchorfile>cpp/io/c/gets</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getwchar</name>\n      <anchorfile>cpp/io/c/getwchar</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::giga</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gmtime</name>\n      <anchorfile>cpp/chrono/c/gmtime</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::greater</class>\n    <class kind=\"class\">std::greater_equal</class>\n    <class kind=\"class\">std::gslice</class>\n    <class kind=\"class\">std::gslice_array</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_facet</name>\n      <anchorfile>cpp/locale/has_facet</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::has_unique_object_representations</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>has_unique_object_representations_v</name>\n      <anchorfile>cpp/types/has_unique_object_representations</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::has_virtual_destructor</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>has_virtual_destructor_v</name>\n      <anchorfile>cpp/types/has_virtual_destructor</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::hash</class>\n    <class kind=\"class\">std::hecto</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hermite</name>\n      <anchorfile>cpp/numeric/special_math/hermite</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hermitef</name>\n      <anchorfile>cpp/numeric/special_math/hermite</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hermitel</name>\n      <anchorfile>cpp/numeric/special_math/hermite</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hex</name>\n      <anchorfile>cpp/io/manip/hex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hexfloat</name>\n      <anchorfile>cpp/io/manip/fixed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>holds_alternative</name>\n      <anchorfile>cpp/utility/variant/holds_alternative</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hypot</name>\n      <anchorfile>cpp/numeric/math/hypot</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ifstream</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ilogb</name>\n      <anchorfile>cpp/numeric/math/ilogb</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imaxdiv</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::imaxdiv_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_place</name>\n      <anchorfile>cpp/utility/in_place</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::in_place_index_t</class>\n    <class kind=\"class\">std::in_place_t</class>\n    <class kind=\"class\">std::in_place_tag</class>\n    <class kind=\"class\">std::in_place_type_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>includes</name>\n      <anchorfile>cpp/algorithm/includes</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>inclusive_scan</name>\n      <anchorfile>cpp/algorithm/inclusive_scan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::independent_bits_engine</class>\n    <class kind=\"class\">std::index_sequence</class>\n    <class kind=\"class\">std::index_sequence_for</class>\n    <class kind=\"class\">std::indirect_array</class>\n    <class kind=\"class\">std::initializer_list</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>inner_product</name>\n      <anchorfile>cpp/algorithm/inner_product</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>inplace_merge</name>\n      <anchorfile>cpp/algorithm/inplace_merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::input_iterator_tag</class>\n    <class kind=\"class\">std::insert_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>inserter</name>\n      <anchorfile>cpp/iterator/inserter</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::int16_t</class>\n    <class kind=\"class\">std::int32_t</class>\n    <class kind=\"class\">std::int64_t</class>\n    <class kind=\"class\">std::int8_t</class>\n    <class kind=\"class\">std::int_fast16_t</class>\n    <class kind=\"class\">std::int_fast32_t</class>\n    <class kind=\"class\">std::int_fast64_t</class>\n    <class kind=\"class\">std::int_fast8_t</class>\n    <class kind=\"class\">std::int_least16_t</class>\n    <class kind=\"class\">std::int_least32_t</class>\n    <class kind=\"class\">std::int_least64_t</class>\n    <class kind=\"class\">std::int_least8_t</class>\n    <class kind=\"class\">std::integer_sequence</class>\n    <class kind=\"class\">std::integral_constant</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>internal</name>\n      <anchorfile>cpp/io/manip/left</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::intmax_t</class>\n    <class kind=\"class\">std::intptr_t</class>\n    <class kind=\"class\">std::invalid_argument</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>invoke</name>\n      <anchorfile>cpp/utility/functional/invoke</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ios_base</class>\n    <class kind=\"class\">std::iostream</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iostream_category</name>\n      <anchorfile>cpp/io/iostream_category</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iota</name>\n      <anchorfile>cpp/algorithm/iota</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::is_abstract</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_abstract_v</name>\n      <anchorfile>cpp/types/is_abstract</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_arithmetic</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_arithmetic_v</name>\n      <anchorfile>cpp/types/is_arithmetic</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_array</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_array_v</name>\n      <anchorfile>cpp/types/is_array</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_assignable_v</name>\n      <anchorfile>cpp/types/is_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_base_of</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_base_of_v</name>\n      <anchorfile>cpp/types/is_base_of</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_bind_expression</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_bind_expression_v</name>\n      <anchorfile>cpp/utility/functional/is_bind_expression</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_callable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_callable_v</name>\n      <anchorfile>cpp/types/is_callable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_class</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_class_v</name>\n      <anchorfile>cpp/types/is_class</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_compound</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_compound_v</name>\n      <anchorfile>cpp/types/is_compound</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_const</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_const_v</name>\n      <anchorfile>cpp/types/is_const</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_constructible_v</name>\n      <anchorfile>cpp/types/is_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_convertible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_convertible_v</name>\n      <anchorfile>cpp/types/is_convertible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_copy_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_copy_assignable_v</name>\n      <anchorfile>cpp/types/is_copy_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_copy_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_copy_constructible_v</name>\n      <anchorfile>cpp/types/is_copy_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_default_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_default_constructible_v</name>\n      <anchorfile>cpp/types/is_default_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_destructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_destructible_v</name>\n      <anchorfile>cpp/types/is_destructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_empty</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_empty_v</name>\n      <anchorfile>cpp/types/is_empty</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_enum</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_enum_v</name>\n      <anchorfile>cpp/types/is_enum</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_error_code_enum</class>\n    <class kind=\"class\">std::is_error_code_enum_v</class>\n    <class kind=\"class\">std::is_error_condition_enum</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_error_condition_enum_v</name>\n      <anchorfile>cpp/error/error_condition/is_error_condition_enum</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_execution_policy</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_execution_policy_v</name>\n      <anchorfile>cpp/algorithm/is_execution_policy</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_final</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_final_v</name>\n      <anchorfile>cpp/types/is_final</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_floating_point</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_floating_point_v</name>\n      <anchorfile>cpp/types/is_floating_point</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_function</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_function_v</name>\n      <anchorfile>cpp/types/is_function</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_fundamental</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_fundamental_v</name>\n      <anchorfile>cpp/types/is_fundamental</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_heap</name>\n      <anchorfile>cpp/algorithm/is_heap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_heap_until</name>\n      <anchorfile>cpp/algorithm/is_heap_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::is_integral</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_integral_v</name>\n      <anchorfile>cpp/types/is_integral</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_literal_type</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_literal_type_v</name>\n      <anchorfile>cpp/types/is_literal_type</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_lvalue_reference</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_lvalue_reference_v</name>\n      <anchorfile>cpp/types/is_lvalue_reference</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_member_function_pointer</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_member_function_pointer_v</name>\n      <anchorfile>cpp/types/is_member_function_pointer</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_member_object_pointer</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_member_object_pointer_v</name>\n      <anchorfile>cpp/types/is_member_object_pointer</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_member_pointer</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_member_pointer_v</name>\n      <anchorfile>cpp/types/is_member_pointer</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_move_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_move_assignable_v</name>\n      <anchorfile>cpp/types/is_move_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_move_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_move_constructible_v</name>\n      <anchorfile>cpp/types/is_move_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_assignable_v</name>\n      <anchorfile>cpp/types/is_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_callable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_callable_v</name>\n      <anchorfile>cpp/types/is_callable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_constructible_v</name>\n      <anchorfile>cpp/types/is_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_copy_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_copy_assignable_v</name>\n      <anchorfile>cpp/types/is_copy_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_copy_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_copy_constructible_v</name>\n      <anchorfile>cpp/types/is_copy_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_default_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_default_constructible_v</name>\n      <anchorfile>cpp/types/is_default_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_destructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_destructible_v</name>\n      <anchorfile>cpp/types/is_destructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_move_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_move_assignable_v</name>\n      <anchorfile>cpp/types/is_move_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_move_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_move_constructible_v</name>\n      <anchorfile>cpp/types/is_move_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_swappable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_swappable_v</name>\n      <anchorfile>cpp/types/is_swappable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_nothrow_swappable_with</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_swappable_with_v</name>\n      <anchorfile>cpp/types/is_swappable_with</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_null_pointer</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_null_pointer_v</name>\n      <anchorfile>cpp/types/is_null_pointer</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_object</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_object_v</name>\n      <anchorfile>cpp/types/is_object</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_partitioned</name>\n      <anchorfile>cpp/algorithm/is_partitioned</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_permutation</name>\n      <anchorfile>cpp/algorithm/is_permutation</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::is_placeholder</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_placeholder_v</name>\n      <anchorfile>cpp/utility/functional/is_placeholder</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_pod</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_pod_v</name>\n      <anchorfile>cpp/types/is_pod</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_pointer</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_pointer_v</name>\n      <anchorfile>cpp/types/is_pointer</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_polymorphic</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_polymorphic_v</name>\n      <anchorfile>cpp/types/is_polymorphic</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_reference</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_reference_v</name>\n      <anchorfile>cpp/types/is_reference</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_rvalue_reference</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_rvalue_reference_v</name>\n      <anchorfile>cpp/types/is_rvalue_reference</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_same</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_same_v</name>\n      <anchorfile>cpp/types/is_same</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_scalar</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_scalar_v</name>\n      <anchorfile>cpp/types/is_scalar</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_signed</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_signed_v</name>\n      <anchorfile>cpp/types/is_signed</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_sorted</name>\n      <anchorfile>cpp/algorithm/is_sorted</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_sorted_until</name>\n      <anchorfile>cpp/algorithm/is_sorted_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::is_standard_layout</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_standard_layout_v</name>\n      <anchorfile>cpp/types/is_standard_layout</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_swappable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_swappable_v</name>\n      <anchorfile>cpp/types/is_swappable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_swappable_with</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_swappable_with_v</name>\n      <anchorfile>cpp/types/is_swappable_with</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivial</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivial_v</name>\n      <anchorfile>cpp/types/is_trivial</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_assignable_v</name>\n      <anchorfile>cpp/types/is_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_constructible_v</name>\n      <anchorfile>cpp/types/is_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_copy_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_copy_assignable_v</name>\n      <anchorfile>cpp/types/is_copy_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_copy_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_copy_constructible_v</name>\n      <anchorfile>cpp/types/is_copy_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_copyable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_copyable_v</name>\n      <anchorfile>cpp/types/is_trivially_copyable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_default_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_default_constructible_v</name>\n      <anchorfile>cpp/types/is_default_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_destructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_destructible_v</name>\n      <anchorfile>cpp/types/is_destructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_move_assignable</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_move_assignable_v</name>\n      <anchorfile>cpp/types/is_move_assignable</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_trivially_move_constructible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_move_constructible_v</name>\n      <anchorfile>cpp/types/is_move_constructible</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_union</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_union_v</name>\n      <anchorfile>cpp/types/is_union</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_unsigned</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_unsigned_v</name>\n      <anchorfile>cpp/types/is_unsigned</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_void</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_void_v</name>\n      <anchorfile>cpp/types/is_void</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::is_volatile</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_volatile_v</name>\n      <anchorfile>cpp/types/is_volatile</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isalnum (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isalnum</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isalnum (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isalnum</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isalpha (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isalpha</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isalpha (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isalpha</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isblank (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isblank</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isblank (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isblank</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iscntrl (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/iscntrl</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iscntrl (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/iscntrl</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isdigit (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isdigit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isdigit (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isdigit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isfinite</name>\n      <anchorfile>cpp/numeric/math/isfinite</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isgraph (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isgraph</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isgraph (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isgraph</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isinf</name>\n      <anchorfile>cpp/numeric/math/isinf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>islower (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/islower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>islower (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/islower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isnan</name>\n      <anchorfile>cpp/numeric/math/isnan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isnormal</name>\n      <anchorfile>cpp/numeric/math/isnormal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isprint (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isprint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isprint (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isprint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ispunct (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/ispunct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ispunct (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/ispunct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isspace (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isspace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isspace (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isspace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istream</class>\n    <class kind=\"class\">std::istream_iterator</class>\n    <class kind=\"class\">std::istreambuf_iterator</class>\n    <class kind=\"class\">std::istringstream</class>\n    <class kind=\"class\">std::istrstream</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isupper (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isupper (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswalnum</name>\n      <anchorfile>cpp/string/wide/iswalnum</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswalpha</name>\n      <anchorfile>cpp/string/wide/iswalpha</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswblank</name>\n      <anchorfile>cpp/string/wide/iswblank</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswcntrl</name>\n      <anchorfile>cpp/string/wide/iswcntrl</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswctype</name>\n      <anchorfile>cpp/string/wide/iswctype</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswdigit</name>\n      <anchorfile>cpp/string/wide/iswdigit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswgraph</name>\n      <anchorfile>cpp/string/wide/iswgraph</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswlower</name>\n      <anchorfile>cpp/string/wide/iswlower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswprint</name>\n      <anchorfile>cpp/string/wide/iswprint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswpunct</name>\n      <anchorfile>cpp/string/wide/iswpunct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswspace</name>\n      <anchorfile>cpp/string/wide/iswspace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswupper</name>\n      <anchorfile>cpp/string/wide/iswupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iswxdigit</name>\n      <anchorfile>cpp/string/wide/iswxdigit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isxdigit (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/isxdigit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isxdigit (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/isxdigit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iter_swap</name>\n      <anchorfile>cpp/algorithm/iter_swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::iterator</class>\n    <class kind=\"class\">std::iterator_traits</class>\n    <class kind=\"class\">std::jmp_buf</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>kill_dependency</name>\n      <anchorfile>cpp/atomic/kill_dependency</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::kilo</class>\n    <class kind=\"class\">std::knuth_b</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>labs</name>\n      <anchorfile>cpp/numeric/math/abs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>laguerre</name>\n      <anchorfile>cpp/numeric/special_math/laguerre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>laguerref</name>\n      <anchorfile>cpp/numeric/special_math/laguerre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>laguerrel</name>\n      <anchorfile>cpp/numeric/special_math/laguerre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>launder</name>\n      <anchorfile>cpp/utility/launder</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lcm</name>\n      <anchorfile>cpp/numeric/lcm</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::lconv</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ldexp</name>\n      <anchorfile>cpp/numeric/math/ldexp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ldiv</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ldiv_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>left</name>\n      <anchorfile>cpp/io/manip/left</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>legendre</name>\n      <anchorfile>cpp/numeric/special_math/legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>legendref</name>\n      <anchorfile>cpp/numeric/special_math/legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>legendrel</name>\n      <anchorfile>cpp/numeric/special_math/legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::length_error</class>\n    <class kind=\"class\">std::less</class>\n    <class kind=\"class\">std::less_equal</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lexicographical_compare</name>\n      <anchorfile>cpp/algorithm/lexicographical_compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lgamma</name>\n      <anchorfile>cpp/numeric/math/lgamma</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::linear_congruential_engine</class>\n    <class kind=\"class\">std::list</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>llabs</name>\n      <anchorfile>cpp/numeric/math/abs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lldiv</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::lldiv_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>llrint</name>\n      <anchorfile>cpp/numeric/math/rint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>llround</name>\n      <anchorfile>cpp/numeric/math/round</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::locale</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>localeconv</name>\n      <anchorfile>cpp/locale/localeconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>localtime</name>\n      <anchorfile>cpp/chrono/c/localtime</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::lock_guard</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>log</name>\n      <anchorfile>cpp/numeric/math/log</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>log10</name>\n      <anchorfile>cpp/numeric/math/log10</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>log1p</name>\n      <anchorfile>cpp/numeric/math/log1p</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>logb</name>\n      <anchorfile>cpp/numeric/math/logb</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::logic_error</class>\n    <class kind=\"class\">std::logical_and</class>\n    <class kind=\"class\">std::logical_not</class>\n    <class kind=\"class\">std::logical_or</class>\n    <class kind=\"class\">std::lognormal_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>longjmp</name>\n      <anchorfile>cpp/utility/program/longjmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/algorithm/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lrint</name>\n      <anchorfile>cpp/numeric/math/rint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lround</name>\n      <anchorfile>cpp/numeric/math/round</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_any</name>\n      <anchorfile>cpp/utility/any/make_any</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::make_boyer_moore_horspool_searcher</class>\n    <class kind=\"class\">std::make_boyer_moore_searcher</class>\n    <class kind=\"class\">std::make_default_searcher</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_exception_ptr</name>\n      <anchorfile>cpp/error/make_exception_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_from_tuple</name>\n      <anchorfile>cpp/utility/make_from_tuple</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_heap</name>\n      <anchorfile>cpp/algorithm/make_heap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::make_index_sequence</class>\n    <class kind=\"class\">std::make_integer_sequence</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_move_iterator</name>\n      <anchorfile>cpp/iterator/make_move_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_optional</name>\n      <anchorfile>cpp/utility/optional/make_optional</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_pair</name>\n      <anchorfile>cpp/utility/pair/make_pair</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_reverse_iterator</name>\n      <anchorfile>cpp/iterator/make_reverse_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_shared</name>\n      <anchorfile>cpp/memory/shared_ptr/make_shared</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::make_signed</class>\n    <class kind=\"class\">std::make_signed_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_tuple</name>\n      <anchorfile>cpp/utility/tuple/make_tuple</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_unique</name>\n      <anchorfile>cpp/memory/unique_ptr/make_unique</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::make_unsigned</class>\n    <class kind=\"class\">std::make_unsigned_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>malloc</name>\n      <anchorfile>cpp/memory/c/malloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::map</class>\n    <class kind=\"class\">std::mask_array</class>\n    <class kind=\"class\">std::match_results</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/algorithm/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::max_align_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_element</name>\n      <anchorfile>cpp/algorithm/max_element</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mblen</name>\n      <anchorfile>cpp/string/multibyte/mblen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mbrlen</name>\n      <anchorfile>cpp/string/multibyte/mbrlen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mbrtoc16</name>\n      <anchorfile>cpp/string/multibyte/mbrtoc16</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mbrtoc32</name>\n      <anchorfile>cpp/string/multibyte/mbrtoc32</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mbrtowc</name>\n      <anchorfile>cpp/string/multibyte/mbrtowc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mbsinit</name>\n      <anchorfile>cpp/string/multibyte/mbsinit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mbsrtowcs</name>\n      <anchorfile>cpp/string/multibyte/mbsrtowcs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::mbstate_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mbstowcs</name>\n      <anchorfile>cpp/string/multibyte/mbstowcs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mbtowc</name>\n      <anchorfile>cpp/string/multibyte/mbtowc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::mega</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mem_fn</name>\n      <anchorfile>cpp/utility/functional/mem_fn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>memchr</name>\n      <anchorfile>cpp/string/byte/memchr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>memcmp</name>\n      <anchorfile>cpp/string/byte/memcmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>memcpy</name>\n      <anchorfile>cpp/string/byte/memcpy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>memmove</name>\n      <anchorfile>cpp/string/byte/memmove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>memset</name>\n      <anchorfile>cpp/string/byte/memset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/algorithm/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::mersenne_twister_engine</class>\n    <class kind=\"class\">std::messages</class>\n    <class kind=\"class\">std::messages_base</class>\n    <class kind=\"class\">std::messages_byname</class>\n    <class kind=\"class\">std::micro</class>\n    <class kind=\"class\">std::milli</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/algorithm/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min_element</name>\n      <anchorfile>cpp/algorithm/min_element</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>minmax</name>\n      <anchorfile>cpp/algorithm/minmax</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>minmax_element</name>\n      <anchorfile>cpp/algorithm/minmax_element</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::minstd_rand</class>\n    <class kind=\"class\">std::minstd_rand0</class>\n    <class kind=\"class\">std::minus</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mismatch</name>\n      <anchorfile>cpp/algorithm/mismatch</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mktime</name>\n      <anchorfile>cpp/chrono/c/mktime</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>modf</name>\n      <anchorfile>cpp/numeric/math/modf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::modulus</class>\n    <class kind=\"class\">std::money_base</class>\n    <class kind=\"class\">std::money_get</class>\n    <class kind=\"class\">std::money_put</class>\n    <class kind=\"class\">std::moneypunct</class>\n    <class kind=\"class\">std::moneypunct_byname</class>\n    <class kind=\"class\">std::monostate</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move (algorithm)</name>\n      <anchorfile>cpp/algorithm/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move (utility)</name>\n      <anchorfile>cpp/utility/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move_backward</name>\n      <anchorfile>cpp/algorithm/move_backward</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move_if_noexcept</name>\n      <anchorfile>cpp/utility/move_if_noexcept</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::move_iterator</class>\n    <class kind=\"class\">std::mt19937</class>\n    <class kind=\"class\">std::mt19937_64</class>\n    <class kind=\"class\">std::multimap</class>\n    <class kind=\"class\">std::multiplies</class>\n    <class kind=\"class\">std::multiset</class>\n    <class kind=\"class\">std::mutex</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nan</name>\n      <anchorfile>cpp/numeric/math/nan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nanf</name>\n      <anchorfile>cpp/numeric/math/nan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nanl</name>\n      <anchorfile>cpp/numeric/math/nan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::nano</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nearbyint</name>\n      <anchorfile>cpp/numeric/math/nearbyint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::negate</class>\n    <class kind=\"class\">std::negation</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>negation_v</name>\n      <anchorfile>cpp/types/negation</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::negative_binomial_distribution</class>\n    <class kind=\"class\">std::nested_exception</class>\n    <class kind=\"class\">std::new_handler</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>next</name>\n      <anchorfile>cpp/iterator/next</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>next_permutation</name>\n      <anchorfile>cpp/algorithm/next_permutation</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nextafter</name>\n      <anchorfile>cpp/numeric/math/nextafter</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nexttoward</name>\n      <anchorfile>cpp/numeric/math/nextafter</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>noboolalpha</name>\n      <anchorfile>cpp/io/manip/boolalpha</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>none_of</name>\n      <anchorfile>cpp/algorithm/all_any_none_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::normal_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>noshowbase</name>\n      <anchorfile>cpp/io/manip/showbase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>noshowpoint</name>\n      <anchorfile>cpp/io/manip/showpoint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>noshowpos</name>\n      <anchorfile>cpp/io/manip/showpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>noskipws</name>\n      <anchorfile>cpp/io/manip/skipws</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>not1</name>\n      <anchorfile>cpp/utility/functional/not1</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>not2</name>\n      <anchorfile>cpp/utility/functional/not2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::not_equal_to</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>not_fn</name>\n      <anchorfile>cpp/utility/functional/not_fn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::nothrow_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>notify_all_at_thread_exit</name>\n      <anchorfile>cpp/thread/notify_all_at_thread_exit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nounitbuf</name>\n      <anchorfile>cpp/io/manip/unitbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nouppercase</name>\n      <anchorfile>cpp/io/manip/uppercase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nth_element</name>\n      <anchorfile>cpp/algorithm/nth_element</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::nullopt_t</class>\n    <class kind=\"class\">std::nullptr_t</class>\n    <class kind=\"class\">std::num_get</class>\n    <class kind=\"class\">std::num_put</class>\n    <class kind=\"class\">std::numeric_limits</class>\n    <class kind=\"class\">std::numpunct</class>\n    <class kind=\"class\">std::numpunct_byname</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>oct</name>\n      <anchorfile>cpp/io/manip/hex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ofstream</class>\n    <class kind=\"class\">std::once_flag</class>\n    <class kind=\"class\">std::optional</class>\n    <class kind=\"class\">std::ostream</class>\n    <class kind=\"class\">std::ostream_iterator</class>\n    <class kind=\"class\">std::ostreambuf_iterator</class>\n    <class kind=\"class\">std::ostringstream</class>\n    <class kind=\"class\">std::ostrstream</class>\n    <class kind=\"class\">std::out_of_range</class>\n    <class kind=\"class\">std::output_iterator_tag</class>\n    <class kind=\"class\">std::overflow_error</class>\n    <class kind=\"class\">std::owner_less</class>\n    <class kind=\"class\">std::packaged_task</class>\n    <class kind=\"class\">std::pair</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>partial_sort</name>\n      <anchorfile>cpp/algorithm/partial_sort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>partial_sort_copy</name>\n      <anchorfile>cpp/algorithm/partial_sort_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>partial_sum</name>\n      <anchorfile>cpp/algorithm/partial_sum</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>partition</name>\n      <anchorfile>cpp/algorithm/partition</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>partition_copy</name>\n      <anchorfile>cpp/algorithm/partition_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>partition_point</name>\n      <anchorfile>cpp/algorithm/partition_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>perror</name>\n      <anchorfile>cpp/io/c/perror</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::peta</class>\n    <class kind=\"class\">std::pico</class>\n    <class kind=\"class\">std::piecewise_constant_distribution</class>\n    <class kind=\"class\">std::piecewise_construct_t</class>\n    <class kind=\"class\">std::piecewise_linear_distribution</class>\n    <class kind=\"class\">std::placeholders</class>\n    <class kind=\"class\">std::plus</class>\n    <namespace>std::pmr</namespace>\n    <class kind=\"class\">std::pointer_safety</class>\n    <class kind=\"class\">std::pointer_traits</class>\n    <class kind=\"class\">std::poisson_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_heap</name>\n      <anchorfile>cpp/algorithm/pop_heap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pow</name>\n      <anchorfile>cpp/numeric/math/pow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>prev</name>\n      <anchorfile>cpp/iterator/prev</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>prev_permutation</name>\n      <anchorfile>cpp/algorithm/prev_permutation</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>printf</name>\n      <anchorfile>cpp/io/c/fprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::priority_queue</class>\n    <class kind=\"class\">std::promise</class>\n    <class kind=\"class\">std::ptrdiff_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_heap</name>\n      <anchorfile>cpp/algorithm/push_heap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put_money</name>\n      <anchorfile>cpp/io/manip/put_money</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put_time</name>\n      <anchorfile>cpp/io/manip/put_time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putc</name>\n      <anchorfile>cpp/io/c/fputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putchar</name>\n      <anchorfile>cpp/io/c/putchar</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>puts</name>\n      <anchorfile>cpp/io/c/puts</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putwchar</name>\n      <anchorfile>cpp/io/c/putwchar</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>qsort</name>\n      <anchorfile>cpp/algorithm/qsort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::queue</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>quick_exit</name>\n      <anchorfile>cpp/utility/program/quick_exit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>quoted</name>\n      <anchorfile>cpp/io/manip/quoted</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>raise</name>\n      <anchorfile>cpp/utility/program/raise</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rand</name>\n      <anchorfile>cpp/numeric/random/rand</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::random_access_iterator_tag</class>\n    <class kind=\"class\">std::random_device</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>random_shuffle</name>\n      <anchorfile>cpp/algorithm/random_shuffle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::range_error</class>\n    <class kind=\"class\">std::rank</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>rank_v</name>\n      <anchorfile>cpp/types/rank</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::ranlux24</class>\n    <class kind=\"class\">std::ranlux24_base</class>\n    <class kind=\"class\">std::ranlux48</class>\n    <class kind=\"class\">std::ranlux48_base</class>\n    <class kind=\"class\">std::ratio</class>\n    <class kind=\"class\">std::ratio_add</class>\n    <class kind=\"class\">std::ratio_divide</class>\n    <class kind=\"class\">std::ratio_equal</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_equal_v</name>\n      <anchorfile>cpp/numeric/ratio/ratio_equal</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::ratio_greater</class>\n    <class kind=\"class\">std::ratio_greater_equal</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_greater_equal_v</name>\n      <anchorfile>cpp/numeric/ratio/ratio_greater_equal</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_greater_v</name>\n      <anchorfile>cpp/numeric/ratio/ratio_greater</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::ratio_less</class>\n    <class kind=\"class\">std::ratio_less_equal</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_less_equal_v</name>\n      <anchorfile>cpp/numeric/ratio/ratio_less_equal</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_less_v</name>\n      <anchorfile>cpp/numeric/ratio/ratio_less</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::ratio_multiply</class>\n    <class kind=\"class\">std::ratio_not_equal</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_not_equal_v</name>\n      <anchorfile>cpp/numeric/ratio/ratio_not_equal</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::ratio_subtract</class>\n    <class kind=\"class\">std::raw_storage_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/iterator/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>realloc</name>\n      <anchorfile>cpp/memory/c/realloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::recursive_mutex</class>\n    <class kind=\"class\">std::recursive_timed_mutex</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reduce</name>\n      <anchorfile>cpp/algorithm/reduce</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ref</name>\n      <anchorfile>cpp/utility/functional/ref</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::reference_wrapper</class>\n    <class kind=\"class\">std::regex</class>\n    <namespace>std::regex_constants</namespace>\n    <class kind=\"class\">std::regex_error</class>\n    <class kind=\"class\">std::regex_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>regex_match</name>\n      <anchorfile>cpp/regex/regex_match</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>regex_replace</name>\n      <anchorfile>cpp/regex/regex_replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>regex_search</name>\n      <anchorfile>cpp/regex/regex_search</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::regex_token_iterator</class>\n    <class kind=\"class\">std::regex_traits</class>\n    <namespace>std::rel_ops</namespace>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remainder</name>\n      <anchorfile>cpp/numeric/math/remainder</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove (&lt;algorithm&gt;)</name>\n      <anchorfile>cpp/algorithm/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove (&lt;cstdio&gt;)</name>\n      <anchorfile>cpp/io/c/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::remove_all_extents</class>\n    <class kind=\"class\">std::remove_all_extents_t</class>\n    <class kind=\"class\">std::remove_const</class>\n    <class kind=\"class\">std::remove_const_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_copy</name>\n      <anchorfile>cpp/algorithm/remove_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_copy_if</name>\n      <anchorfile>cpp/algorithm/remove_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::remove_cv</class>\n    <class kind=\"class\">std::remove_cv_t</class>\n    <class kind=\"class\">std::remove_extent</class>\n    <class kind=\"class\">std::remove_extent_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_if</name>\n      <anchorfile>cpp/algorithm/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::remove_pointer</class>\n    <class kind=\"class\">std::remove_pointer_t</class>\n    <class kind=\"class\">std::remove_reference</class>\n    <class kind=\"class\">std::remove_reference_t</class>\n    <class kind=\"class\">std::remove_volatile</class>\n    <class kind=\"class\">std::remove_volatile_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remquo</name>\n      <anchorfile>cpp/numeric/math/remquo</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rename</name>\n      <anchorfile>cpp/io/c/rename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/iterator/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/algorithm/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_copy</name>\n      <anchorfile>cpp/algorithm/replace_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_copy_if</name>\n      <anchorfile>cpp/algorithm/replace_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_if</name>\n      <anchorfile>cpp/algorithm/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resetiosflags</name>\n      <anchorfile>cpp/io/manip/resetiosflags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::result_of</class>\n    <class kind=\"class\">std::result_of_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rethrow_exception</name>\n      <anchorfile>cpp/error/rethrow_exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rethrow_if_nested</name>\n      <anchorfile>cpp/error/rethrow_if_nested</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>return_temporary_buffer</name>\n      <anchorfile>cpp/memory/return_temporary_buffer</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reverse</name>\n      <anchorfile>cpp/algorithm/reverse</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reverse_copy</name>\n      <anchorfile>cpp/algorithm/reverse_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::reverse_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rewind</name>\n      <anchorfile>cpp/io/c/rewind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>riemann_zeta</name>\n      <anchorfile>cpp/numeric/special_math/riemann_zeta</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>riemann_zetaf</name>\n      <anchorfile>cpp/numeric/special_math/riemann_zeta</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>riemann_zetal</name>\n      <anchorfile>cpp/numeric/special_math/riemann_zeta</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>right</name>\n      <anchorfile>cpp/io/manip/left</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rint</name>\n      <anchorfile>cpp/numeric/math/rint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rotate</name>\n      <anchorfile>cpp/algorithm/rotate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rotate_copy</name>\n      <anchorfile>cpp/algorithm/rotate_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>round</name>\n      <anchorfile>cpp/numeric/math/round</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::runtime_error</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sample</name>\n      <anchorfile>cpp/algorithm/sample</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>scalbln</name>\n      <anchorfile>cpp/numeric/math/scalbn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>scalbn</name>\n      <anchorfile>cpp/numeric/math/scalbn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>scanf</name>\n      <anchorfile>cpp/io/c/fscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>scientific</name>\n      <anchorfile>cpp/io/manip/fixed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::scoped_allocator_adaptor</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>search</name>\n      <anchorfile>cpp/algorithm/search</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>search_n</name>\n      <anchorfile>cpp/algorithm/search_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::seed_seq</class>\n    <class kind=\"class\">std::set</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_difference</name>\n      <anchorfile>cpp/algorithm/set_difference</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_intersection</name>\n      <anchorfile>cpp/algorithm/set_intersection</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_new_handler</name>\n      <anchorfile>cpp/memory/new/set_new_handler</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_symmetric_difference</name>\n      <anchorfile>cpp/algorithm/set_symmetric_difference</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_terminate</name>\n      <anchorfile>cpp/error/set_terminate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_unexpected</name>\n      <anchorfile>cpp/error/set_unexpected</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_union</name>\n      <anchorfile>cpp/algorithm/set_union</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbase</name>\n      <anchorfile>cpp/io/manip/setbase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/c/setbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setfill</name>\n      <anchorfile>cpp/io/manip/setfill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setiosflags</name>\n      <anchorfile>cpp/io/manip/setiosflags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setlocale</name>\n      <anchorfile>cpp/locale/setlocale</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setprecision</name>\n      <anchorfile>cpp/io/manip/setprecision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setvbuf</name>\n      <anchorfile>cpp/io/c/setvbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setw</name>\n      <anchorfile>cpp/io/manip/setw</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::shared_future</class>\n    <class kind=\"class\">std::shared_lock</class>\n    <class kind=\"class\">std::shared_mutex</class>\n    <class kind=\"class\">std::shared_ptr</class>\n    <class kind=\"class\">std::shared_timed_mutex</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showbase</name>\n      <anchorfile>cpp/io/manip/showbase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showpoint</name>\n      <anchorfile>cpp/io/manip/showpoint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showpos</name>\n      <anchorfile>cpp/io/manip/showpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shuffle</name>\n      <anchorfile>cpp/algorithm/random_shuffle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::shuffle_order_engine</class>\n    <class kind=\"class\">std::sig_atomic_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>signal</name>\n      <anchorfile>cpp/utility/program/signal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>signbit</name>\n      <anchorfile>cpp/numeric/math/signbit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sin</name>\n      <anchorfile>cpp/numeric/math/sin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sinh</name>\n      <anchorfile>cpp/numeric/math/sinh</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/iterator/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::size_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>skipws</name>\n      <anchorfile>cpp/io/manip/skipws</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::slice</class>\n    <class kind=\"class\">std::slice_array</class>\n    <class kind=\"class\">std::smatch</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snprintf</name>\n      <anchorfile>cpp/io/c/fprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sort</name>\n      <anchorfile>cpp/algorithm/sort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sort_heap</name>\n      <anchorfile>cpp/algorithm/sort_heap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_bessel</name>\n      <anchorfile>cpp/numeric/special_math/sph_bessel</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_besself</name>\n      <anchorfile>cpp/numeric/special_math/sph_bessel</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_bessell</name>\n      <anchorfile>cpp/numeric/special_math/sph_bessel</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_legendre</name>\n      <anchorfile>cpp/numeric/special_math/sph_legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_legendref</name>\n      <anchorfile>cpp/numeric/special_math/sph_legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_legendrel</name>\n      <anchorfile>cpp/numeric/special_math/sph_legendre</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_neumann</name>\n      <anchorfile>cpp/numeric/special_math/sph_neumann</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_neumannf</name>\n      <anchorfile>cpp/numeric/special_math/sph_neumann</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sph_neumannl</name>\n      <anchorfile>cpp/numeric/special_math/sph_neumann</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sprintf</name>\n      <anchorfile>cpp/io/c/fprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sqrt</name>\n      <anchorfile>cpp/numeric/math/sqrt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>srand</name>\n      <anchorfile>cpp/numeric/random/srand</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::sregex_iterator</class>\n    <class kind=\"class\">std::sregex_token_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sscanf</name>\n      <anchorfile>cpp/io/c/fscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ssub_match</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stable_partition</name>\n      <anchorfile>cpp/algorithm/stable_partition</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stable_sort</name>\n      <anchorfile>cpp/algorithm/stable_sort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::stack</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>static_pointer_cast</name>\n      <anchorfile>cpp/memory/shared_ptr/pointer_cast</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stod</name>\n      <anchorfile>cpp/string/basic_string/stof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stof</name>\n      <anchorfile>cpp/string/basic_string/stof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stoi</name>\n      <anchorfile>cpp/string/basic_string/stol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stol</name>\n      <anchorfile>cpp/string/basic_string/stol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stold</name>\n      <anchorfile>cpp/string/basic_string/stof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stoll</name>\n      <anchorfile>cpp/string/basic_string/stol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stoul</name>\n      <anchorfile>cpp/string/basic_string/stoul</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stoull</name>\n      <anchorfile>cpp/string/basic_string/stoul</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strcat</name>\n      <anchorfile>cpp/string/byte/strcat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strchr</name>\n      <anchorfile>cpp/string/byte/strchr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strcmp</name>\n      <anchorfile>cpp/string/byte/strcmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strcoll</name>\n      <anchorfile>cpp/string/byte/strcoll</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strcpy</name>\n      <anchorfile>cpp/string/byte/strcpy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strcspn</name>\n      <anchorfile>cpp/string/byte/strcspn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::streambuf</class>\n    <class kind=\"class\">std::streamoff</class>\n    <class kind=\"class\">std::streampos</class>\n    <class kind=\"class\">std::streamsize</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strerror</name>\n      <anchorfile>cpp/string/byte/strerror</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strftime</name>\n      <anchorfile>cpp/chrono/c/strftime</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::string</class>\n    <class kind=\"class\">std::string_view</class>\n    <class kind=\"class\">std::stringbuf</class>\n    <class kind=\"class\">std::stringstream</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strlen</name>\n      <anchorfile>cpp/string/byte/strlen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strncat</name>\n      <anchorfile>cpp/string/byte/strncat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strncmp</name>\n      <anchorfile>cpp/string/byte/strncmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strncpy</name>\n      <anchorfile>cpp/string/byte/strncpy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strpbrk</name>\n      <anchorfile>cpp/string/byte/strpbrk</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strrchr</name>\n      <anchorfile>cpp/string/byte/strrchr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strspn</name>\n      <anchorfile>cpp/string/byte/strspn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strstr</name>\n      <anchorfile>cpp/string/byte/strstr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::strstream</class>\n    <class kind=\"class\">std::strstreambuf</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtod</name>\n      <anchorfile>cpp/string/byte/strtof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtof</name>\n      <anchorfile>cpp/string/byte/strtof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtoimax</name>\n      <anchorfile>cpp/string/byte/strtoimax</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtok</name>\n      <anchorfile>cpp/string/byte/strtok</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtol</name>\n      <anchorfile>cpp/string/byte/strtol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtold</name>\n      <anchorfile>cpp/string/byte/strtof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtoll</name>\n      <anchorfile>cpp/string/byte/strtol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtoul</name>\n      <anchorfile>cpp/string/byte/strtoul</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtoull</name>\n      <anchorfile>cpp/string/byte/strtoul</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strtoumax</name>\n      <anchorfile>cpp/string/byte/strtoimax</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strxfrm</name>\n      <anchorfile>cpp/string/byte/strxfrm</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::student_t_distribution</class>\n    <class kind=\"class\">std::sub_match</class>\n    <class kind=\"class\">std::subtract_with_carry_engine</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/algorithm/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap_ranges</name>\n      <anchorfile>cpp/algorithm/swap_ranges</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swprintf</name>\n      <anchorfile>cpp/io/c/fwprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swscanf</name>\n      <anchorfile>cpp/io/c/fwscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>system</name>\n      <anchorfile>cpp/utility/program/system</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>system_category</name>\n      <anchorfile>cpp/error/system_category</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::system_error</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tan</name>\n      <anchorfile>cpp/numeric/math/tan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tanh</name>\n      <anchorfile>cpp/numeric/math/tanh</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::tera</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>terminate</name>\n      <anchorfile>cpp/error/terminate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::terminate_handler</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tgamma</name>\n      <anchorfile>cpp/numeric/math/tgamma</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <namespace>std::this_thread</namespace>\n    <class kind=\"class\">std::thread</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>throw_with_nested</name>\n      <anchorfile>cpp/error/throw_with_nested</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/utility/tuple/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>time</name>\n      <anchorfile>cpp/chrono/c/time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::time_base</class>\n    <class kind=\"class\">std::time_get</class>\n    <class kind=\"class\">std::time_get_byname</class>\n    <class kind=\"class\">std::time_put</class>\n    <class kind=\"class\">std::time_put_byname</class>\n    <class kind=\"class\">std::time_t</class>\n    <class kind=\"class\">std::timed_mutex</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>timespec</name>\n      <anchorfile>cpp/chrono/c/timespec</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>timespec_get</name>\n      <anchorfile>cpp/chrono/c/timespec_get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::tm</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tmpfile</name>\n      <anchorfile>cpp/io/c/tmpfile</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tmpnam</name>\n      <anchorfile>cpp/io/c/tmpnam</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_string</name>\n      <anchorfile>cpp/string/basic_string/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_wstring</name>\n      <anchorfile>cpp/string/basic_string/to_wstring</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tolower (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/tolower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tolower (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/tolower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>toupper (&lt;cctype&gt;)</name>\n      <anchorfile>cpp/string/byte/toupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>toupper (&lt;clocale&gt;)</name>\n      <anchorfile>cpp/locale/toupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>towctrans</name>\n      <anchorfile>cpp/string/wide/towctrans</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>towlower</name>\n      <anchorfile>cpp/string/wide/towlower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>towupper</name>\n      <anchorfile>cpp/string/wide/towupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>transform</name>\n      <anchorfile>cpp/algorithm/transform</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>transform_exclusive_scan</name>\n      <anchorfile>cpp/algorithm/transform_exclusive_scan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>transform_inclusive_scan</name>\n      <anchorfile>cpp/algorithm/transform_inclusive_scan</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>transform_reduce</name>\n      <anchorfile>cpp/algorithm/transform_reduce</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::true_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>trunc</name>\n      <anchorfile>cpp/numeric/math/trunc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::try_to_lock_t</class>\n    <class kind=\"class\">std::tuple</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tuple_cat</name>\n      <anchorfile>cpp/utility/tuple/tuple_cat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>tuple_size_v</name>\n      <anchorfile>cpp/utility/tuple/tuple_size</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::type_index</class>\n    <class kind=\"class\">std::type_info</class>\n    <class kind=\"class\">std::u16streampos</class>\n    <class kind=\"class\">std::u16string</class>\n    <class kind=\"class\">std::u16string_view</class>\n    <class kind=\"class\">std::u32streampos</class>\n    <class kind=\"class\">std::u32string</class>\n    <class kind=\"class\">std::u32string_view</class>\n    <class kind=\"class\">std::uint16_t</class>\n    <class kind=\"class\">std::uint32_t</class>\n    <class kind=\"class\">std::uint64_t</class>\n    <class kind=\"class\">std::uint8_t</class>\n    <class kind=\"class\">std::uint_fast16_t</class>\n    <class kind=\"class\">std::uint_fast32_t</class>\n    <class kind=\"class\">std::uint_fast64_t</class>\n    <class kind=\"class\">std::uint_fast8_t</class>\n    <class kind=\"class\">std::uint_least16_t</class>\n    <class kind=\"class\">std::uint_least32_t</class>\n    <class kind=\"class\">std::uint_least64_t</class>\n    <class kind=\"class\">std::uint_least8_t</class>\n    <class kind=\"class\">std::uintmax_t</class>\n    <class kind=\"class\">std::uintptr_t</class>\n    <class kind=\"class\">std::unary_function</class>\n    <class kind=\"class\">std::unary_negate</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uncaught_exception</name>\n      <anchorfile>cpp/error/uncaught_exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>undeclare_no_pointers</name>\n      <anchorfile>cpp/memory/gc/undeclare_no_pointers</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>undeclare_reachable</name>\n      <anchorfile>cpp/memory/gc/undeclare_reachable</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::underflow_error</class>\n    <class kind=\"class\">std::underlying_type</class>\n    <class kind=\"class\">std::underlying_type_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unexpected</name>\n      <anchorfile>cpp/error/unexpected</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::unexpected_handler</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ungetc</name>\n      <anchorfile>cpp/io/c/ungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ungetwc</name>\n      <anchorfile>cpp/io/c/ungetwc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::uniform_int_distribution</class>\n    <class kind=\"class\">std::uniform_real_distribution</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_copy</name>\n      <anchorfile>cpp/memory/uninitialized_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_copy_n</name>\n      <anchorfile>cpp/memory/uninitialized_copy_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_default_construct</name>\n      <anchorfile>cpp/memory/uninitialized_default_construct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_default_construct_n</name>\n      <anchorfile>cpp/memory/uninitialized_default_construct_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_fill</name>\n      <anchorfile>cpp/memory/uninitialized_fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_fill_n</name>\n      <anchorfile>cpp/memory/uninitialized_fill_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_move</name>\n      <anchorfile>cpp/memory/uninitialized_move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_move_n</name>\n      <anchorfile>cpp/memory/uninitialized_move_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_value_construct</name>\n      <anchorfile>cpp/memory/uninitialized_value_construct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uninitialized_value_construct_n</name>\n      <anchorfile>cpp/memory/uninitialized_value_construct_n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique</name>\n      <anchorfile>cpp/algorithm/unique</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique_copy</name>\n      <anchorfile>cpp/algorithm/unique_copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::unique_lock</class>\n    <class kind=\"class\">std::unique_ptr</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unitbuf</name>\n      <anchorfile>cpp/io/manip/unitbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::unordered_map</class>\n    <class kind=\"class\">std::unordered_multimap</class>\n    <class kind=\"class\">std::unordered_multiset</class>\n    <class kind=\"class\">std::unordered_set</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/algorithm/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uppercase</name>\n      <anchorfile>cpp/io/manip/uppercase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>use_facet</name>\n      <anchorfile>cpp/locale/use_facet</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::uses_allocator</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>uses_allocator_v</name>\n      <anchorfile>cpp/memory/uses_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::valarray</class>\n    <class kind=\"class\">std::variant</class>\n    <class kind=\"class\">std::variant_alternative</class>\n    <class kind=\"class\">std::variant_alternative_t</class>\n    <class kind=\"class\">std::variant_size</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>variant_size_v</name>\n      <anchorfile>cpp/utility/variant/variant_size</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::vector</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vfprintf</name>\n      <anchorfile>cpp/io/c/vfprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vfscanf</name>\n      <anchorfile>cpp/io/c/vfscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vfwprintf</name>\n      <anchorfile>cpp/io/c/vfwprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vfwscanf</name>\n      <anchorfile>cpp/io/c/vfwscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>visit</name>\n      <anchorfile>cpp/utility/variant/visit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::void_t</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vprintf</name>\n      <anchorfile>cpp/io/c/vfprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vscanf</name>\n      <anchorfile>cpp/io/c/vfscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vsnprintf</name>\n      <anchorfile>cpp/io/c/vfprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vsprintf</name>\n      <anchorfile>cpp/io/c/vfprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vsscanf</name>\n      <anchorfile>cpp/io/c/vfscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vswprintf</name>\n      <anchorfile>cpp/io/c/vfwprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vswscanf</name>\n      <anchorfile>cpp/io/c/vfwscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vwprintf</name>\n      <anchorfile>cpp/io/c/vfwprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vwscanf</name>\n      <anchorfile>cpp/io/c/vfwscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wbuffer_convert</class>\n    <class kind=\"class\">std::wcerr</class>\n    <class kind=\"class\">std::wcin</class>\n    <class kind=\"class\">std::wclog</class>\n    <class kind=\"class\">std::wcmatch</class>\n    <class kind=\"class\">std::wcout</class>\n    <class kind=\"class\">std::wcregex_iterator</class>\n    <class kind=\"class\">std::wcregex_token_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcrtomb</name>\n      <anchorfile>cpp/string/multibyte/wcrtomb</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcscat</name>\n      <anchorfile>cpp/string/wide/wcscat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcschr</name>\n      <anchorfile>cpp/string/wide/wcschr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcscmp</name>\n      <anchorfile>cpp/string/wide/wcscmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcscoll</name>\n      <anchorfile>cpp/string/wide/wcscoll</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcscpy</name>\n      <anchorfile>cpp/string/wide/wcscpy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcscspn</name>\n      <anchorfile>cpp/string/wide/wcscspn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsftime</name>\n      <anchorfile>cpp/chrono/c/wcsftime</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcslen</name>\n      <anchorfile>cpp/string/wide/wcslen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsncat</name>\n      <anchorfile>cpp/string/wide/wcsncat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsncmp</name>\n      <anchorfile>cpp/string/wide/wcsncmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsncpy</name>\n      <anchorfile>cpp/string/wide/wcsncpy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcspbrk</name>\n      <anchorfile>cpp/string/wide/wcspbrk</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsrchr</name>\n      <anchorfile>cpp/string/wide/wcsrchr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsrtombs</name>\n      <anchorfile>cpp/string/multibyte/wcsrtombs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsspn</name>\n      <anchorfile>cpp/string/wide/wcsspn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsstr</name>\n      <anchorfile>cpp/string/wide/wcsstr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstod</name>\n      <anchorfile>cpp/string/wide/wcstof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstof</name>\n      <anchorfile>cpp/string/wide/wcstof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstoimax</name>\n      <anchorfile>cpp/string/wide/wcstoimax</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstok</name>\n      <anchorfile>cpp/string/wide/wcstok</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstol</name>\n      <anchorfile>cpp/string/wide/wcstol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstold</name>\n      <anchorfile>cpp/string/wide/wcstof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstoll</name>\n      <anchorfile>cpp/string/wide/wcstol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstombs</name>\n      <anchorfile>cpp/string/multibyte/wcstombs</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstoul</name>\n      <anchorfile>cpp/string/wide/wcstoul</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstoull</name>\n      <anchorfile>cpp/string/wide/wcstoul</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcstoumax</name>\n      <anchorfile>cpp/string/wide/wcstoimax</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wcsub_match</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsxfrm</name>\n      <anchorfile>cpp/string/wide/wcsxfrm</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wctob</name>\n      <anchorfile>cpp/string/multibyte/wctob</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wctomb</name>\n      <anchorfile>cpp/string/multibyte/wctomb</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wctrans</name>\n      <anchorfile>cpp/string/wide/wctrans</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wctype</name>\n      <anchorfile>cpp/string/wide/wctype</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::weak_ptr</class>\n    <class kind=\"class\">std::weibull_distribution</class>\n    <class kind=\"class\">std::wfilebuf</class>\n    <class kind=\"class\">std::wfstream</class>\n    <class kind=\"class\">std::wifstream</class>\n    <class kind=\"class\">std::wiostream</class>\n    <class kind=\"class\">std::wistream</class>\n    <class kind=\"class\">std::wistringstream</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wmemchr</name>\n      <anchorfile>cpp/string/wide/wmemchr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wmemcmp</name>\n      <anchorfile>cpp/string/wide/wmemcmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wmemcpy</name>\n      <anchorfile>cpp/string/wide/wmemcpy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wmemmove</name>\n      <anchorfile>cpp/string/wide/wmemmove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wmemset</name>\n      <anchorfile>cpp/string/wide/wmemset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wofstream</class>\n    <class kind=\"class\">std::wostream</class>\n    <class kind=\"class\">std::wostringstream</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wprintf</name>\n      <anchorfile>cpp/io/c/fwprintf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wregex</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ws</name>\n      <anchorfile>cpp/io/manip/ws</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wscanf</name>\n      <anchorfile>cpp/io/c/fwscanf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wsmatch</class>\n    <class kind=\"class\">std::wsregex_iterator</class>\n    <class kind=\"class\">std::wsregex_token_iterator</class>\n    <class kind=\"class\">std::wssub_match</class>\n    <class kind=\"class\">std::wstreambuf</class>\n    <class kind=\"class\">std::wstreampos</class>\n    <class kind=\"class\">std::wstring</class>\n    <class kind=\"class\">std::wstring_convert</class>\n    <class kind=\"class\">std::wstring_view</class>\n    <class kind=\"class\">std::wstringbuf</class>\n    <class kind=\"class\">std::wstringstream</class>\n    <class kind=\"class\">std::yocto</class>\n    <class kind=\"class\">std::yotta</class>\n    <class kind=\"class\">std::zetta</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::FILE</name>\n    <filename>cpp/io/c</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_const</name>\n    <filename>cpp/types/add_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_const_t</name>\n    <filename>cpp/types/add_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_cv</name>\n    <filename>cpp/types/add_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_cv_t</name>\n    <filename>cpp/types/add_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_lvalue_reference</name>\n    <filename>cpp/types/add_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_lvalue_reference_t</name>\n    <filename>cpp/types/add_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_pointer</name>\n    <filename>cpp/types/add_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_pointer_t</name>\n    <filename>cpp/types/add_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_rvalue_reference</name>\n    <filename>cpp/types/add_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_rvalue_reference_t</name>\n    <filename>cpp/types/add_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_volatile</name>\n    <filename>cpp/types/add_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::add_volatile_t</name>\n    <filename>cpp/types/add_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::adopt_lock_t</name>\n    <filename>cpp/thread/lock_tag_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::align_val_t</name>\n    <filename>cpp/memory/new/align_val_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::aligned_storage</name>\n    <filename>cpp/types/aligned_storage</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::aligned_storage_t</name>\n    <filename>cpp/types/aligned_storage</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::aligned_union</name>\n    <filename>cpp/types/aligned_union</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::aligned_union_t</name>\n    <filename>cpp/types/aligned_union</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::alignment_of</name>\n    <filename>cpp/types/alignment_of</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::allocator</name>\n    <filename>cpp/memory/allocator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>address</name>\n      <anchorfile>cpp/memory/allocator/address</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/memory/allocator/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocator</name>\n      <anchorfile>cpp/memory/allocator/allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>construct</name>\n      <anchorfile>cpp/memory/allocator/construct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/memory/allocator/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>destroy</name>\n      <anchorfile>cpp/memory/allocator/destroy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/memory/allocator/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~allocator</name>\n      <anchorfile>cpp/memory/allocator/~allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::allocator_arg_t</name>\n    <filename>cpp/memory/allocator_arg_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::allocator_traits</name>\n    <filename>cpp/memory/allocator_traits</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/memory/allocator_traits/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>construct</name>\n      <anchorfile>cpp/memory/allocator_traits/construct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/memory/allocator_traits/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>destroy</name>\n      <anchorfile>cpp/memory/allocator_traits/destroy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/memory/allocator_traits/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>select_on_container_copy_construction</name>\n      <anchorfile>cpp/memory/allocator_traits/select_on_container_copy_construction</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::any</name>\n    <filename>cpp/utility/any</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>any</name>\n      <anchorfile>cpp/utility/any/any</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/utility/any/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_value</name>\n      <anchorfile>cpp/utility/any/has_value</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/utility/any/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/utility/any/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/utility/any/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>type</name>\n      <anchorfile>cpp/utility/any/type</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~any</name>\n      <anchorfile>cpp/utility/any/~any</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::array</name>\n    <filename>cpp/container/array</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/array/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/container/array/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/array/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/array/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/array/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/array/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/array/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/container/array/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/array/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/array/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/container/array/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/array/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/array/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/array/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/array/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/array/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/array/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/array/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_bool</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_bool</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_char16_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_char16_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_char32_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_char32_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_flag</name>\n    <filename>cpp/atomic/atomic_flag</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_flag</name>\n      <anchorfile>cpp/atomic/atomic_flag/atomic_flag</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/atomic/atomic_flag/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic_flag/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>test_and_set</name>\n      <anchorfile>cpp/atomic/atomic_flag/test_and_set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int16_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int16_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int32_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int32_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int64_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int64_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int8_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int8_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int_fast16_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int_fast16_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int_fast32_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int_fast32_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int_fast64_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int_fast64_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int_fast8_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int_fast8_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int_least16_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int_least16_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int_least32_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int_least32_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int_least64_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int_least64_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_int_least8_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_int_least8_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_intmax_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_intmax_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_intptr_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_intptr_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_llong</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_llong</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_long</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_long</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_ptrdiff_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_ptrdiff_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_schar</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_schar</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_short</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_short</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_size_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_size_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uchar</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uchar</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint16_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint16_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint32_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint32_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint64_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint64_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint8_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint8_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint_fast16_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint_fast16_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint_fast32_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint_fast32_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint_fast64_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint_fast64_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint_fast8_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint_fast8_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint_least16_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint_least16_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint_least32_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint_least32_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint_least64_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint_least64_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uint_least8_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uint_least8_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uintmax_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uintmax_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_uintptr_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_uintptr_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_ullong</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_ullong</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_ulong</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_ulong</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_ushort</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_ushort</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::atomic_wchar_t</name>\n    <filename>cpp/atomic/atomic</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>atomic_wchar_t</name>\n      <anchorfile>cpp/atomic/atomic/atomic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_strong</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare_exchange_weak</name>\n      <anchorfile>cpp/atomic/atomic/compare_exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exchange</name>\n      <anchorfile>cpp/atomic/atomic/exchange</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_add</name>\n      <anchorfile>cpp/atomic/atomic/fetch_add</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_and</name>\n      <anchorfile>cpp/atomic/atomic/fetch_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_or</name>\n      <anchorfile>cpp/atomic/atomic/fetch_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_sub</name>\n      <anchorfile>cpp/atomic/atomic/fetch_sub</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fetch_xor</name>\n      <anchorfile>cpp/atomic/atomic/fetch_xor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_lock_free</name>\n      <anchorfile>cpp/atomic/atomic/is_lock_free</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load</name>\n      <anchorfile>cpp/atomic/atomic/load</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T</name>\n      <anchorfile>cpp/atomic/atomic/operator_T</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/atomic/atomic/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/atomic/atomic/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>store</name>\n      <anchorfile>cpp/atomic/atomic/store</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::auto_ptr</name>\n    <filename>cpp/memory/auto_ptr</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>auto_ptr</name>\n      <anchorfile>cpp/memory/auto_ptr/auto_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/memory/auto_ptr/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator auto_ptr&lt;Y&gt;</name>\n      <anchorfile>cpp/memory/auto_ptr/operator_auto_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/memory/auto_ptr/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/memory/auto_ptr/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/memory/auto_ptr/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>release</name>\n      <anchorfile>cpp/memory/auto_ptr/release</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/memory/auto_ptr/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~auto_ptr</name>\n      <anchorfile>cpp/memory/auto_ptr/~auto_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::back_insert_iterator</name>\n    <filename>cpp/iterator/back_insert_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_alloc</name>\n    <filename>cpp/memory/new/bad_alloc</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad_alloc</name>\n      <anchorfile>cpp/memory/new/bad_alloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/memory/new/bad_alloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_any_cast</name>\n    <filename>cpp/utility/any/bad_any_cast</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_array_length</name>\n    <filename>cpp/memory/new/bad_array_length</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad_array_length</name>\n      <anchorfile>cpp/memory/new/bad_array_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/memory/new/bad_alloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_array_new_length</name>\n    <filename>cpp/memory/new/bad_array_new_length</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad_array_new_length</name>\n      <anchorfile>cpp/memory/new/bad_array_new_length/bad_array_new_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/memory/new/bad_alloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_cast</name>\n    <filename>cpp/types/bad_cast</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad_cast</name>\n      <anchorfile>cpp/types/bad_cast/bad_cast</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_exception</name>\n    <filename>cpp/error/bad_exception</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_function_call</name>\n    <filename>cpp/utility/functional/bad_function_call</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad_function_call</name>\n      <anchorfile>cpp/utility/functional/bad_function_call</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_optional_access</name>\n    <filename>cpp/utility/optional/bad_optional_access</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_typeid</name>\n    <filename>cpp/types/bad_typeid</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad_typeid</name>\n      <anchorfile>cpp/types/bad_typeid/bad_typeid</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_variant_access</name>\n    <filename>cpp/utility/variant/bad_variant_access</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bad_weak_ptr</name>\n    <filename>cpp/memory/bad_weak_ptr</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad_weak_ptr</name>\n      <anchorfile>cpp/memory/bad_weak_ptr/bad_weak_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_filebuf</name>\n    <filename>cpp/io/basic_filebuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_filebuf</name>\n      <anchorfile>cpp/io/basic_filebuf/basic_filebuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_filebuf/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_filebuf/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_filebuf/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_filebuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~basic_filebuf</name>\n      <anchorfile>cpp/io/basic_filebuf/~basic_filebuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_fstream</name>\n    <filename>cpp/io/basic_fstream</filename>\n    <class kind=\"class\">std::basic_fstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_fstream</name>\n      <anchorfile>cpp/io/basic_fstream/basic_fstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_fstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_fstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_fstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_fstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_fstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_fstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_fstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_fstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_fstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_fstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_fstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ifstream</name>\n    <filename>cpp/io/basic_ifstream</filename>\n    <class kind=\"class\">std::basic_ifstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_ifstream</name>\n      <anchorfile>cpp/io/basic_ifstream/basic_ifstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_ifstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ifstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ifstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_ifstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_ifstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ifstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ifstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ifstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ifstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ifstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ifstream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ios</name>\n    <filename>cpp/io/basic_ios</filename>\n    <class kind=\"class\">std::basic_ios::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_ios</name>\n      <anchorfile>cpp/io/basic_ios/basic_ios</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ios::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ios::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/ios_base/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~basic_ios</name>\n      <anchorfile>cpp/io/basic_ios/~basic_ios</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ios::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ios::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ios::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_iostream</name>\n    <filename>cpp/io/basic_iostream</filename>\n    <class kind=\"class\">std::basic_iostream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_iostream</name>\n      <anchorfile>cpp/io/basic_iostream/basic_iostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_iostream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_iostream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_iostream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~basic_iostream</name>\n      <anchorfile>cpp/io/basic_iostream/~basic_iostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_iostream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_iostream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_iostream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_iostream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istream</name>\n    <filename>cpp/io/basic_istream</filename>\n    <class kind=\"class\">std::basic_istream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_istream</name>\n      <anchorfile>cpp/io/basic_istream/basic_istream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_istream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_istream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_istream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~basic_istream</name>\n      <anchorfile>cpp/io/basic_istream/~basic_istream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istringstream</name>\n    <filename>cpp/io/basic_istringstream</filename>\n    <class kind=\"class\">std::basic_istringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_istringstream</name>\n      <anchorfile>cpp/io/basic_istringstream/basic_istringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_istringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_istringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_istringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_istringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_istringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_istringstream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ofstream</name>\n    <filename>cpp/io/basic_ofstream</filename>\n    <class kind=\"class\">std::basic_ofstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_ofstream</name>\n      <anchorfile>cpp/io/basic_ofstream/basic_ofstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_ofstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ofstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ofstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_ofstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_ofstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ofstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ofstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ofstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ofstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ofstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ofstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostream</name>\n    <filename>cpp/io/basic_ostream</filename>\n    <class kind=\"class\">std::basic_ostream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_ostream</name>\n      <anchorfile>cpp/io/basic_ostream/basic_ostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ostream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ostream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ostream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~basic_ostream</name>\n      <anchorfile>cpp/io/basic_ostream/~basic_ostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostringstream</name>\n    <filename>cpp/io/basic_ostringstream</filename>\n    <class kind=\"class\">std::basic_ostringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_ostringstream</name>\n      <anchorfile>cpp/io/basic_ostringstream/basic_ostringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ostringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ostringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ostringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_ostringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_ostringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_ostringstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_regex</name>\n    <filename>cpp/regex/basic_regex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/regex/basic_regex/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_regex</name>\n      <anchorfile>cpp/regex/basic_regex/basic_regex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/regex/basic_regex/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/regex/basic_regex/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/regex/basic_regex/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mark_count</name>\n      <anchorfile>cpp/regex/basic_regex/mark_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/basic_regex/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/regex/basic_regex/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~basic_regex</name>\n      <anchorfile>cpp/regex/basic_regex/~basic_regex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_streambuf</name>\n    <filename>cpp/io/basic_streambuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_streambuf</name>\n      <anchorfile>cpp/io/basic_streambuf/basic_streambuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_streambuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~basic_streambuf</name>\n      <anchorfile>cpp/io/basic_streambuf/~basic_streambuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_string</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_string</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_string_view</name>\n    <filename>cpp/string/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_string_view</name>\n      <anchorfile>cpp/string/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_stringbuf</name>\n    <filename>cpp/io/basic_stringbuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_stringbuf</name>\n      <anchorfile>cpp/io/basic_stringbuf/basic_stringbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_stringbuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_stringbuf/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_stringstream</name>\n    <filename>cpp/io/basic_stringstream</filename>\n    <class kind=\"class\">std::basic_stringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_stringstream</name>\n      <anchorfile>cpp/io/basic_stringstream/basic_stringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_stringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_stringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_stringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::basic_stringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_stringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_stringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_stringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_stringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::basic_stringstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bernoulli_distribution</name>\n    <filename>cpp/numeric/random/bernoulli_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bernoulli_distribution</name>\n      <anchorfile>cpp/numeric/random/bernoulli_distribution/bernoulli_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/bernoulli_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/bernoulli_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>p</name>\n      <anchorfile>cpp/numeric/random/bernoulli_distribution/p</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/bernoulli_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/bernoulli_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bidirectional_iterator_tag</name>\n    <filename>cpp/iterator/iterator_tags</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::binary_function</name>\n    <filename>cpp/utility/functional/binary_function</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::binary_negate</name>\n    <filename>cpp/utility/functional/binary_negate</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>binary_negate</name>\n      <anchorfile>cpp/utility/functional/binary_negate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/binary_negate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::binomial_distribution</name>\n    <filename>cpp/numeric/random/binomial_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>binomial_distribution</name>\n      <anchorfile>cpp/numeric/random/binomial_distribution/binomial_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/binomial_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/binomial_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>p</name>\n      <anchorfile>cpp/numeric/random/binomial_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/binomial_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/binomial_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>t</name>\n      <anchorfile>cpp/numeric/random/binomial_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bit_and</name>\n    <filename>cpp/utility/functional/bit_and</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/bit_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bit_not</name>\n    <filename>cpp/utility/functional/bit_not</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/bit_not</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bit_or</name>\n    <filename>cpp/utility/functional/bit_or</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/bit_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bitset</name>\n    <filename>cpp/utility/bitset</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>all</name>\n      <anchorfile>cpp/utility/bitset/all_any_none</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>any</name>\n      <anchorfile>cpp/utility/bitset/all_any_none</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bitset</name>\n      <anchorfile>cpp/utility/bitset/bitset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/utility/bitset/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flip</name>\n      <anchorfile>cpp/utility/bitset/flip</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>none</name>\n      <anchorfile>cpp/utility/bitset/all_any_none</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/utility/bitset/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&amp;=</name>\n      <anchorfile>cpp/utility/bitset/operator_logic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/utility/bitset/operator_ltltgtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;=</name>\n      <anchorfile>cpp/utility/bitset/operator_ltltgtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/utility/bitset/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/utility/bitset/operator_ltltgtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;=</name>\n      <anchorfile>cpp/utility/bitset/operator_ltltgtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/utility/bitset/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator^=</name>\n      <anchorfile>cpp/utility/bitset/operator_logic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator|=</name>\n      <anchorfile>cpp/utility/bitset/operator_logic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator~</name>\n      <anchorfile>cpp/utility/bitset/operator_logic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::bitset::reference</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/utility/bitset/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set</name>\n      <anchorfile>cpp/utility/bitset/set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/utility/bitset/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>test</name>\n      <anchorfile>cpp/utility/bitset/test</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_string</name>\n      <anchorfile>cpp/utility/bitset/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_ullong</name>\n      <anchorfile>cpp/utility/bitset/to_ullong</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_ulong</name>\n      <anchorfile>cpp/utility/bitset/to_ulong</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bitset::reference</name>\n    <filename>cpp/utility/bitset/reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::bool_constant</name>\n    <filename>cpp/types/integral_constant</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::boyer_moore_horspool_searcher</name>\n    <filename>cpp/utility/functional/boyer_moore_horspool_searcher</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>boyer_moore_horspool_searcher</name>\n      <anchorfile>cpp/utility/functional/boyer_moore_horspool_searcher</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::boyer_moore_searcher</name>\n    <filename>cpp/utility/functional/boyer_moore_searcher</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>boyer_moore_searcher</name>\n      <anchorfile>cpp/utility/functional/boyer_moore_searcher</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::cauchy_distribution</name>\n    <filename>cpp/numeric/random/cauchy_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>a</name>\n      <anchorfile>cpp/numeric/random/cauchy_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>b</name>\n      <anchorfile>cpp/numeric/random/cauchy_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cauchy_distribution</name>\n      <anchorfile>cpp/numeric/random/cauchy_distribution/cauchy_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/cauchy_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/cauchy_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/cauchy_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/cauchy_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/cauchy_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::centi</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::cerr</name>\n    <filename>cpp/io/cerr</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::char_traits</name>\n    <filename>cpp/string/char_traits</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/char_traits/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/char_traits/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/char_traits/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/string/char_traits/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eq</name>\n      <anchorfile>cpp/string/char_traits/cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eq_int_type</name>\n      <anchorfile>cpp/string/char_traits/eq_int_type</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/char_traits/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/char_traits/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lt</name>\n      <anchorfile>cpp/string/char_traits/cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/string/char_traits/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>not_eof</name>\n      <anchorfile>cpp/string/char_traits/not_eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_char_type</name>\n      <anchorfile>cpp/string/char_traits/to_char_type</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_int_type</name>\n      <anchorfile>cpp/string/char_traits/to_int_type</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chi_squared_distribution</name>\n    <filename>cpp/numeric/random/chi_squared_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>chi_squared_distribution</name>\n      <anchorfile>cpp/numeric/random/chi_squared_distribution/chi_squared_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/chi_squared_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/chi_squared_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>n</name>\n      <anchorfile>cpp/numeric/random/chi_squared_distribution/n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/chi_squared_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/chi_squared_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/chi_squared_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::chrono</name>\n    <filename></filename>\n    <class kind=\"class\">std::chrono::duration</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>duration_cast</name>\n      <anchorfile>cpp/chrono/duration/duration_cast</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::chrono::duration_values</class>\n    <class kind=\"class\">std::chrono::high_resolution_clock</class>\n    <class kind=\"class\">std::chrono::hours</class>\n    <class kind=\"class\">std::chrono::microseconds</class>\n    <class kind=\"class\">std::chrono::milliseconds</class>\n    <class kind=\"class\">std::chrono::minutes</class>\n    <class kind=\"class\">std::chrono::nanoseconds</class>\n    <class kind=\"class\">std::chrono::seconds</class>\n    <class kind=\"class\">std::chrono::steady_clock</class>\n    <class kind=\"class\">std::chrono::system_clock</class>\n    <class kind=\"class\">std::chrono::time_point</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>time_point_cast</name>\n      <anchorfile>cpp/chrono/time_point/time_point_cast</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::chrono::treat_as_floating_point</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::duration</name>\n    <filename>cpp/chrono/duration</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/chrono/duration/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>duration</name>\n      <anchorfile>cpp/chrono/duration/duration</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/duration/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/duration/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator%=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/chrono/duration/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>zero</name>\n      <anchorfile>cpp/chrono/duration/zero</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::duration_values</name>\n    <filename>cpp/chrono/duration_values</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/duration_values/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/duration_values/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>zero</name>\n      <anchorfile>cpp/chrono/duration_values/zero</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::high_resolution_clock</name>\n    <filename>cpp/chrono/high_resolution_clock</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>now</name>\n      <anchorfile>cpp/chrono/high_resolution_clock/now</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::hours</name>\n    <filename>cpp/chrono/duration</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/chrono/duration/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hours</name>\n      <anchorfile>cpp/chrono/duration/duration</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/duration/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/duration/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator%=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/chrono/duration/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>zero</name>\n      <anchorfile>cpp/chrono/duration/zero</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::microseconds</name>\n    <filename>cpp/chrono/duration</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/chrono/duration/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/duration/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>microseconds</name>\n      <anchorfile>cpp/chrono/duration/duration</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/duration/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator%=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/chrono/duration/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>zero</name>\n      <anchorfile>cpp/chrono/duration/zero</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::milliseconds</name>\n    <filename>cpp/chrono/duration</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/chrono/duration/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/duration/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>milliseconds</name>\n      <anchorfile>cpp/chrono/duration/duration</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/duration/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator%=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/chrono/duration/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>zero</name>\n      <anchorfile>cpp/chrono/duration/zero</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::minutes</name>\n    <filename>cpp/chrono/duration</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/chrono/duration/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/duration/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/duration/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>minutes</name>\n      <anchorfile>cpp/chrono/duration/duration</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator%=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/chrono/duration/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>zero</name>\n      <anchorfile>cpp/chrono/duration/zero</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::nanoseconds</name>\n    <filename>cpp/chrono/duration</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/chrono/duration/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/duration/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/duration/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nanoseconds</name>\n      <anchorfile>cpp/chrono/duration/duration</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator%=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/chrono/duration/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>zero</name>\n      <anchorfile>cpp/chrono/duration/zero</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::seconds</name>\n    <filename>cpp/chrono/duration</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/chrono/duration/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/duration/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/duration/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator%=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-</name>\n      <anchorfile>cpp/chrono/duration/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator--(int)</name>\n      <anchorfile>cpp/chrono/duration/operator_arith2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/chrono/duration/operator_arith3</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/chrono/duration/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seconds</name>\n      <anchorfile>cpp/chrono/duration/duration</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>zero</name>\n      <anchorfile>cpp/chrono/duration/zero</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::steady_clock</name>\n    <filename>cpp/chrono/steady_clock</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>now</name>\n      <anchorfile>cpp/chrono/steady_clock/now</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::system_clock</name>\n    <filename>cpp/chrono/system_clock</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>from_time_t</name>\n      <anchorfile>cpp/chrono/system_clock/from_time_t</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>now</name>\n      <anchorfile>cpp/chrono/system_clock/now</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_time_t</name>\n      <anchorfile>cpp/chrono/system_clock/to_time_t</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::time_point</name>\n    <filename>cpp/chrono/time_point</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/chrono/time_point/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/chrono/time_point/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+</name>\n      <anchorfile>cpp/chrono/time_point/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-</name>\n      <anchorfile>cpp/chrono/time_point/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>time_point</name>\n      <anchorfile>cpp/chrono/time_point/time_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>time_since_epoch</name>\n      <anchorfile>cpp/chrono/time_point/time_since_epoch</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::chrono::treat_as_floating_point</name>\n    <filename>cpp/chrono/treat_as_floating_point</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::cin</name>\n    <filename>cpp/io/cin</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::clock_t</name>\n    <filename>cpp/chrono/c/clock_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::clog</name>\n    <filename>cpp/io/clog</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::cmatch</name>\n    <filename>cpp/regex/match_results</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cmatch</name>\n      <anchorfile>cpp/regex/match_results/match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/regex/match_results/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>format</name>\n      <anchorfile>cpp/regex/match_results/format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/regex/match_results/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/match_results/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/regex/match_results/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/regex/match_results/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>position</name>\n      <anchorfile>cpp/regex/match_results/position</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>prefix</name>\n      <anchorfile>cpp/regex/match_results/prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ready</name>\n      <anchorfile>cpp/regex/match_results/ready</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/regex/match_results/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/match_results/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>suffix</name>\n      <anchorfile>cpp/regex/match_results/suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/regex/match_results/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~cmatch</name>\n      <anchorfile>cpp/regex/match_results/~match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt</name>\n    <filename>cpp/locale/codecvt</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>codecvt</name>\n      <anchorfile>cpp/locale/codecvt/codecvt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt::extern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt::intern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt::state_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~codecvt</name>\n      <anchorfile>cpp/locale/codecvt/~codecvt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt::extern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt::intern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt::state_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_base</name>\n    <filename>cpp/locale/codecvt_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_byname</name>\n    <filename>cpp/locale/codecvt_byname</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>codecvt_byname</name>\n      <anchorfile>cpp/locale/codecvt_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_byname::extern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_byname::intern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_byname::state_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~codecvt_byname</name>\n      <anchorfile>cpp/locale/codecvt_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_byname::extern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_byname::intern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_byname::state_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf16</name>\n    <filename>cpp/locale/codecvt_utf16</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf16::extern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf16::intern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf16::state_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf16::extern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf16::intern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf16::state_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf8</name>\n    <filename>cpp/locale/codecvt_utf8</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf8::extern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf8::intern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf8::state_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf8::extern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf8::intern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf8::state_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf8_utf16</name>\n    <filename>cpp/locale/codecvt_utf8_utf16</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_always_noconv</name>\n      <anchorfile>cpp/locale/codecvt/always_noconv</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>encoding</name>\n      <anchorfile>cpp/locale/codecvt/encoding</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf8_utf16::extern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in</name>\n      <anchorfile>cpp/locale/codecvt/in</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf8_utf16::intern_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/locale/codecvt/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_length</name>\n      <anchorfile>cpp/locale/codecvt/max_length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>out</name>\n      <anchorfile>cpp/locale/codecvt/out</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::codecvt_utf8_utf16::state_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unshift</name>\n      <anchorfile>cpp/locale/codecvt/unshift</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf8_utf16::extern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf8_utf16::intern_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::codecvt_utf8_utf16::state_type</name>\n    <filename>cpp/locale/codecvt</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::collate</name>\n    <filename>cpp/locale/collate</filename>\n    <class kind=\"class\">std::collate::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>collate</name>\n      <anchorfile>cpp/locale/collate/collate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/locale/collate/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_compare</name>\n      <anchorfile>cpp/locale/collate/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_hash</name>\n      <anchorfile>cpp/locale/collate/hash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_transform</name>\n      <anchorfile>cpp/locale/collate/transform</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash</name>\n      <anchorfile>cpp/locale/collate/hash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::collate::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>transform</name>\n      <anchorfile>cpp/locale/collate/transform</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~collate</name>\n      <anchorfile>cpp/locale/collate/~collate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::collate::char_type</name>\n    <filename>cpp/locale/collate</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::collate::string_type</name>\n    <filename>cpp/locale/collate</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::collate_byname</name>\n    <filename>cpp/locale/collate_byname</filename>\n    <class kind=\"class\">std::collate_byname::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>collate_byname</name>\n      <anchorfile>cpp/locale/collate_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/locale/collate/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_compare</name>\n      <anchorfile>cpp/locale/collate/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_hash</name>\n      <anchorfile>cpp/locale/collate/hash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_transform</name>\n      <anchorfile>cpp/locale/collate/transform</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash</name>\n      <anchorfile>cpp/locale/collate/hash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::collate_byname::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>transform</name>\n      <anchorfile>cpp/locale/collate/transform</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~collate_byname</name>\n      <anchorfile>cpp/locale/collate_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::collate_byname::char_type</name>\n    <filename>cpp/locale/collate</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::collate_byname::string_type</name>\n    <filename>cpp/locale/collate</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::common_type</name>\n    <filename>cpp/types/common_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::common_type_t</name>\n    <filename>cpp/types/common_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::complex</name>\n    <filename>cpp/numeric/complex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>complex</name>\n      <anchorfile>cpp/numeric/complex/complex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imag</name>\n      <anchorfile>cpp/numeric/complex/imag</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*=</name>\n      <anchorfile>cpp/numeric/complex/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/numeric/complex/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-=</name>\n      <anchorfile>cpp/numeric/complex/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/numeric/complex/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/numeric/complex/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>real</name>\n      <anchorfile>cpp/numeric/complex/real</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::condition_variable</name>\n    <filename>cpp/thread/condition_variable</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>condition_variable</name>\n      <anchorfile>cpp/thread/condition_variable/condition_variable</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native_handle</name>\n      <anchorfile>cpp/thread/condition_variable/native_handle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>notify_all</name>\n      <anchorfile>cpp/thread/condition_variable/notify_all</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>notify_one</name>\n      <anchorfile>cpp/thread/condition_variable/notify_one</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait</name>\n      <anchorfile>cpp/thread/condition_variable/wait</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait_for</name>\n      <anchorfile>cpp/thread/condition_variable/wait_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait_until</name>\n      <anchorfile>cpp/thread/condition_variable/wait_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~condition_variable</name>\n      <anchorfile>cpp/thread/condition_variable/~condition_variable</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::condition_variable_any</name>\n    <filename>cpp/thread/condition_variable_any</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>condition_variable_any</name>\n      <anchorfile>cpp/thread/condition_variable_any/condition_variable_any</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native_handle</name>\n      <anchorfile>cpp/thread/condition_variable_any/native_handle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>notify_all</name>\n      <anchorfile>cpp/thread/condition_variable_any/notify_all</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>notify_one</name>\n      <anchorfile>cpp/thread/condition_variable_any/notify_one</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait</name>\n      <anchorfile>cpp/thread/condition_variable_any/wait</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait_for</name>\n      <anchorfile>cpp/thread/condition_variable_any/wait_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait_until</name>\n      <anchorfile>cpp/thread/condition_variable_any/wait_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~condition_variable_any</name>\n      <anchorfile>cpp/thread/condition_variable_any/~condition_variable_any</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::conditional</name>\n    <filename>cpp/types/conditional</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::conditional_t</name>\n    <filename>cpp/types/conditional</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::conjunction</name>\n    <filename>cpp/types/conjunction</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::cout</name>\n    <filename>cpp/io/cout</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::cregex_iterator</name>\n    <filename>cpp/regex/regex_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cregex_iterator</name>\n      <anchorfile>cpp/regex/regex_iterator/regex_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::cregex_token_iterator</name>\n    <filename>cpp/regex/regex_token_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cregex_token_iterator</name>\n      <anchorfile>cpp/regex/regex_token_iterator/regex_token_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::csub_match</name>\n    <filename>cpp/regex/sub_match</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/regex/sub_match/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>csub_match</name>\n      <anchorfile>cpp/regex/sub_match/sub_match</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/sub_match/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator string_type</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ctype</name>\n    <filename>cpp/locale/ctype</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ctype</name>\n      <anchorfile>cpp/locale/ctype/ctype</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_is</name>\n      <anchorfile>cpp/locale/ctype/is</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_narrow</name>\n      <anchorfile>cpp/locale/ctype/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_scan_is</name>\n      <anchorfile>cpp/locale/ctype/scan_is</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_tolower</name>\n      <anchorfile>cpp/locale/ctype/tolower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_toupper</name>\n      <anchorfile>cpp/locale/ctype/toupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_widen</name>\n      <anchorfile>cpp/locale/ctype/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is</name>\n      <anchorfile>cpp/locale/ctype/is</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ctype::mask</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/locale/ctype/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>scan_is</name>\n      <anchorfile>cpp/locale/ctype/scan_is</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tolower</name>\n      <anchorfile>cpp/locale/ctype/tolower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>toupper</name>\n      <anchorfile>cpp/locale/ctype/toupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/locale/ctype/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~ctype</name>\n      <anchorfile>cpp/locale/ctype/~ctype</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ctype::mask</name>\n    <filename>cpp/locale/ctype_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ctype_base</name>\n    <filename>cpp/locale/ctype_base</filename>\n    <class kind=\"class\">std::ctype_base::mask</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ctype_base::mask</name>\n    <filename>cpp/locale/ctype_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ctype_byname</name>\n    <filename>cpp/locale/ctype_byname</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ctype_byname</name>\n      <anchorfile>cpp/locale/ctype_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_is</name>\n      <anchorfile>cpp/locale/ctype/is</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_narrow</name>\n      <anchorfile>cpp/locale/ctype/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_scan_is</name>\n      <anchorfile>cpp/locale/ctype/scan_is</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_tolower</name>\n      <anchorfile>cpp/locale/ctype/tolower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_toupper</name>\n      <anchorfile>cpp/locale/ctype/toupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_widen</name>\n      <anchorfile>cpp/locale/ctype/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is</name>\n      <anchorfile>cpp/locale/ctype/is</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ctype_byname::mask</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/locale/ctype/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>scan_is</name>\n      <anchorfile>cpp/locale/ctype/scan_is</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tolower</name>\n      <anchorfile>cpp/locale/ctype/tolower</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>toupper</name>\n      <anchorfile>cpp/locale/ctype/toupper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/locale/ctype/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~ctype_byname</name>\n      <anchorfile>cpp/locale/ctype_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ctype_byname::mask</name>\n    <filename>cpp/locale/ctype_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::deca</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::decay</name>\n    <filename>cpp/types/decay</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::decay_t</name>\n    <filename>cpp/types/decay</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::deci</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::default_delete</name>\n    <filename>cpp/memory/default_delete</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>default_delete</name>\n      <anchorfile>cpp/memory/default_delete</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/memory/default_delete</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::default_random_engine</name>\n    <filename>cpp/numeric/random</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::default_searcher</name>\n    <filename>cpp/utility/functional/default_searcher</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>default_searcher</name>\n      <anchorfile>cpp/utility/functional/default_searcher</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::defer_lock_t</name>\n    <filename>cpp/thread/lock_tag_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::deque</name>\n    <filename>cpp/container/deque</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/container/deque/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/deque/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/container/deque/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/deque/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/deque/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/deque/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/deque/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/deque/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/deque/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deque</name>\n      <anchorfile>cpp/container/deque/deque</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/deque/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_back</name>\n      <anchorfile>cpp/container/deque/emplace_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_front</name>\n      <anchorfile>cpp/container/deque/emplace_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/deque/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/deque/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/deque/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/deque/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/deque/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/deque/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/deque/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/deque/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/deque/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/container/deque/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_front</name>\n      <anchorfile>cpp/container/deque/pop_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/container/deque/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_front</name>\n      <anchorfile>cpp/container/deque/push_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/deque/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/deque/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/container/deque/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/container/deque/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/deque/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/deque/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~deque</name>\n      <anchorfile>cpp/container/deque/~deque</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::discard_block_engine</name>\n    <filename>cpp/numeric/random/discard_block_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>base</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/base</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard_block_engine</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/discard_block_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::discrete_distribution</name>\n    <filename>cpp/numeric/random/discrete_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discrete_distribution</name>\n      <anchorfile>cpp/numeric/random/discrete_distribution/discrete_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/discrete_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/discrete_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/discrete_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/discrete_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>probabilities</name>\n      <anchorfile>cpp/numeric/random/discrete_distribution/probabilities</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/discrete_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::disjunction</name>\n    <filename>cpp/types/disjunction</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::div_t</name>\n    <filename>cpp/numeric/math/div</filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>quot</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>rem</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::divides</name>\n    <filename>cpp/utility/functional/divides</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/divides</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::domain_error</name>\n    <filename>cpp/error/domain_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>domain_error</name>\n      <anchorfile>cpp/error/domain_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::enable_if</name>\n    <filename>cpp/types/enable_if</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::enable_if_t</name>\n    <filename>cpp/types/enable_if</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::enable_shared_from_this</name>\n    <filename>cpp/memory/enable_shared_from_this</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>enable_shared_from_this</name>\n      <anchorfile>cpp/memory/enable_shared_from_this/enable_shared_from_this</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/memory/enable_shared_from_this/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shared_from_this</name>\n      <anchorfile>cpp/memory/enable_shared_from_this/shared_from_this</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>weak_from_this</name>\n      <anchorfile>cpp/memory/enable_shared_from_this/weak_from_this</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~enable_shared_from_this</name>\n      <anchorfile>cpp/memory/enable_shared_from_this/~enable_shared_from_this</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::equal_to</name>\n    <filename>cpp/utility/functional/equal_to</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/equal_to</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::errc</name>\n    <filename>cpp/error/errc</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::error_category</name>\n    <filename>cpp/error/error_category</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>default_error_condition</name>\n      <anchorfile>cpp/error/error_category/default_error_condition</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equivalent</name>\n      <anchorfile>cpp/error/error_category/equivalent</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>error_category</name>\n      <anchorfile>cpp/error/error_category/error_category</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>message</name>\n      <anchorfile>cpp/error/error_category/message</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>name</name>\n      <anchorfile>cpp/error/error_category/name</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/error/error_category/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;</name>\n      <anchorfile>cpp/error/error_category/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/error/error_category/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~error_category</name>\n      <anchorfile>cpp/error/error_category/~error_category</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::error_code</name>\n    <filename>cpp/error/error_code</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/error/error_code/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>category</name>\n      <anchorfile>cpp/error/error_code/category</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/error/error_code/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>default_error_condition</name>\n      <anchorfile>cpp/error/error_code/default_error_condition</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>error_code</name>\n      <anchorfile>cpp/error/error_code/error_code</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>message</name>\n      <anchorfile>cpp/error/error_code/message</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/error/error_code/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/error/error_code/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value</name>\n      <anchorfile>cpp/error/error_code/value</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::error_condition</name>\n    <filename>cpp/error/error_condition</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/error/error_condition/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>category</name>\n      <anchorfile>cpp/error/error_condition/category</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/error/error_condition/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>error_condition</name>\n      <anchorfile>cpp/error/error_condition/error_condition</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>message</name>\n      <anchorfile>cpp/error/error_condition/message</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/error/error_condition/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/error/error_condition/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value</name>\n      <anchorfile>cpp/error/error_condition/value</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::exa</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::exception</name>\n    <filename>cpp/error/exception</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exception</name>\n      <anchorfile>cpp/error/exception/exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/error/exception/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~exception</name>\n      <anchorfile>cpp/error/exception/~exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::exception_ptr</name>\n    <filename>cpp/error/exception_ptr</filename>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::execution</name>\n    <filename></filename>\n    <class kind=\"class\">std::execution::parallel_policy</class>\n    <class kind=\"class\">std::execution::parallel_unsequenced_policy</class>\n    <class kind=\"class\">std::execution::sequenced_policy</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::execution::parallel_policy</name>\n    <filename>cpp/algorithm/execution_policy_tag_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::execution::parallel_unsequenced_policy</name>\n    <filename>cpp/algorithm/execution_policy_tag_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::execution::sequenced_policy</name>\n    <filename>cpp/algorithm/execution_policy_tag_t</filename>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::experimental</name>\n    <filename></filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>alignment_of_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::experimental::any</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>any_cast</name>\n      <anchorfile>cpp/experimental/any/any_cast</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>apply</name>\n      <anchorfile>cpp/experimental/apply</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::bad_optional_access</class>\n    <class kind=\"class\">std::experimental::basic_string_view</class>\n    <class kind=\"class\">std::experimental::boyer_moore_horspool_searcher</class>\n    <class kind=\"class\">std::experimental::boyer_moore_searcher</class>\n    <class kind=\"class\">std::experimental::default_searcher</class>\n    <class kind=\"class\">std::experimental::detected_or</class>\n    <class kind=\"class\">std::experimental::detected_or_t</class>\n    <class kind=\"class\">std::experimental::erased_type</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>extent_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <namespace>std::experimental::filesystem</namespace>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcd</name>\n      <anchorfile>cpp/experimental/gcd</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>has_virtual_destructor_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::experimental::invocation_type</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_abstract_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_arithmetic_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_array_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_base_of_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_bind_expression_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_class_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_compound_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_const_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_convertible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_copy_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_copy_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_default_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_destructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::experimental::is_detected</class>\n    <class kind=\"class\">std::experimental::is_detected_convertible</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_detected_convertible_v</name>\n      <anchorfile>cpp/experimental/is_detected</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::experimental::is_detected_exact</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_detected_exact_v</name>\n      <anchorfile>cpp/experimental/is_detected</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_detected_v</name>\n      <anchorfile>cpp/experimental/is_detected</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_empty_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_enum_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_error_code_enum_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_error_condition_enum_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_final_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_floating_point_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_function_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_fundamental_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_integral_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_literal_type_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_lvalue_reference_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_member_function_pointer_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_member_object_pointer_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_member_pointer_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_move_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_move_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_copy_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_copy_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_default_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_destructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_move_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_nothrow_move_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_null_pointer_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_object_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_placeholder_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_pod_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_pointer_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_polymorphic_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_reference_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_rvalue_reference_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_same_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_scalar_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_signed_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_standard_layout_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivial_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_copy_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_copy_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_copyable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_default_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_destructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_move_assignable_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_trivially_move_constructible_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_union_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_unsigned_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_void_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>is_volatile_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lcm</name>\n      <anchorfile>cpp/experimental/lcm</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_array</name>\n      <anchorfile>cpp/experimental/make_array</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_boyer_moore_horspool_searcher</name>\n      <anchorfile>cpp/experimental/make_boyer_moore_horspool_searcher</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_boyer_moore_searcher</name>\n      <anchorfile>cpp/experimental/make_boyer_moore_searcher</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_default_searcher</name>\n      <anchorfile>cpp/experimental/make_default_searcher</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_optional</name>\n      <anchorfile>cpp/experimental/optional/make_optional</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::nonesuch</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>not_fn</name>\n      <anchorfile>cpp/experimental/not_fn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::observer_ptr</class>\n    <class kind=\"class\">std::experimental::optional</class>\n    <class kind=\"class\">std::experimental::ostream_joiner</class>\n    <namespace>std::experimental::pmr</namespace>\n    <class kind=\"class\">std::experimental::propagate_const</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>randint</name>\n      <anchorfile>cpp/experimental/randint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>rank_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_equal_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_greater_equal_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_greater_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_less_equal_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_less_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>ratio_not_equal_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::experimental::raw_invocation_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reseed</name>\n      <anchorfile>cpp/experimental/reseed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sample</name>\n      <anchorfile>cpp/experimental/sample</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>search</name>\n      <anchorfile>cpp/experimental/search</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::source_location</class>\n    <class kind=\"class\">std::experimental::string_view</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_array</name>\n      <anchorfile>cpp/experimental/to_array</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>treat_as_floating_point_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>tuple_size_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::experimental::u16string_view</class>\n    <class kind=\"class\">std::experimental::u32string_view</class>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>uses_allocator_v</name>\n      <anchorfile>cpp/experimental/type_trait_variable_templates</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <class kind=\"class\">std::experimental::wstring_view</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::any</name>\n    <filename>cpp/experimental/any</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>any</name>\n      <anchorfile>cpp/experimental/any/any</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/experimental/any/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/experimental/any/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/any/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/experimental/any/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>type</name>\n      <anchorfile>cpp/experimental/any/type</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::bad_optional_access</name>\n    <filename>cpp/utility/optional/bad_optional_access</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad_optional_access</name>\n      <anchorfile>cpp/utility/optional/bad_optional_access</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::basic_string_view</name>\n    <filename>cpp/experimental/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/experimental/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/experimental/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>basic_string_view</name>\n      <anchorfile>cpp/experimental/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/experimental/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/experimental/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/experimental/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/experimental/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/experimental/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/experimental/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/experimental/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator basic_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/experimental/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/experimental/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/experimental/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::boyer_moore_horspool_searcher</name>\n    <filename>cpp/experimental/boyer_moore_horspool_searcher</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::boyer_moore_searcher</name>\n    <filename>cpp/experimental/boyer_moore_searcher</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::default_searcher</name>\n    <filename>cpp/experimental/default_searcher</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::detected_or</name>\n    <filename>cpp/experimental/is_detected</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::detected_or_t</name>\n    <filename>cpp/experimental/is_detected</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::erased_type</name>\n    <filename>cpp/experimental/erased_type</filename>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::experimental::filesystem</name>\n    <filename></filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>absolute</name>\n      <anchorfile>cpp/experimental/fs/absolute</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>canonical</name>\n      <anchorfile>cpp/experimental/fs/canonical</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/experimental/fs/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy_file</name>\n      <anchorfile>cpp/experimental/fs/copy_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::filesystem::copy_options</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy_symlink</name>\n      <anchorfile>cpp/experimental/fs/copy_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_directories</name>\n      <anchorfile>cpp/experimental/fs/create_directory</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_directory</name>\n      <anchorfile>cpp/experimental/fs/create_directory</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_directory_symlink</name>\n      <anchorfile>cpp/experimental/fs/create_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_hard_link</name>\n      <anchorfile>cpp/experimental/fs/create_hard_link</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_symlink</name>\n      <anchorfile>cpp/experimental/fs/create_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>current_path</name>\n      <anchorfile>cpp/experimental/fs/current_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::filesystem::directory_entry</class>\n    <class kind=\"class\">std::experimental::filesystem::directory_iterator</class>\n    <class kind=\"class\">std::experimental::filesystem::directory_options</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equivalent</name>\n      <anchorfile>cpp/experimental/fs/equivalent</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exists</name>\n      <anchorfile>cpp/experimental/fs/exists</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>file_size</name>\n      <anchorfile>cpp/experimental/fs/file_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::filesystem::file_status</class>\n    <class kind=\"class\">std::experimental::filesystem::file_time_type</class>\n    <class kind=\"class\">std::experimental::filesystem::file_type</class>\n    <class kind=\"class\">std::experimental::filesystem::filesystem_error</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hard_link_count</name>\n      <anchorfile>cpp/experimental/fs/hard_link_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_block_file</name>\n      <anchorfile>cpp/experimental/fs/is_block_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_character_file</name>\n      <anchorfile>cpp/experimental/fs/is_character_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_directory</name>\n      <anchorfile>cpp/experimental/fs/is_directory</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_empty</name>\n      <anchorfile>cpp/experimental/fs/is_empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_fifo</name>\n      <anchorfile>cpp/experimental/fs/is_fifo</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_other</name>\n      <anchorfile>cpp/experimental/fs/is_other</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_regular_file</name>\n      <anchorfile>cpp/experimental/fs/is_regular_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_socket</name>\n      <anchorfile>cpp/experimental/fs/is_socket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_symlink</name>\n      <anchorfile>cpp/experimental/fs/is_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>last_write_time</name>\n      <anchorfile>cpp/experimental/fs/last_write_time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::filesystem::path</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>permissions</name>\n      <anchorfile>cpp/experimental/fs/permissions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::filesystem::perms</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read_symlink</name>\n      <anchorfile>cpp/experimental/fs/read_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::filesystem::recursive_directory_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove</name>\n      <anchorfile>cpp/experimental/fs/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_all</name>\n      <anchorfile>cpp/experimental/fs/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rename</name>\n      <anchorfile>cpp/experimental/fs/rename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize_file</name>\n      <anchorfile>cpp/experimental/fs/resize_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>space</name>\n      <anchorfile>cpp/experimental/fs/space</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::filesystem::space_info</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>status</name>\n      <anchorfile>cpp/experimental/fs/status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>status_known</name>\n      <anchorfile>cpp/experimental/fs/status_known</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>symlink_status</name>\n      <anchorfile>cpp/experimental/fs/status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>system_complete</name>\n      <anchorfile>cpp/experimental/fs/absolute</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>temp_directory_path</name>\n      <anchorfile>cpp/experimental/fs/temp_directory_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::copy_options</name>\n    <filename>cpp/experimental/fs/copy_options</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::directory_entry</name>\n    <filename>cpp/experimental/fs/directory_entry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/experimental/fs/directory_entry/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>directory_entry</name>\n      <anchorfile>cpp/experimental/fs/directory_entry/directory_entry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/fs/directory_entry/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>path</name>\n      <anchorfile>cpp/experimental/fs/directory_entry/path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_filename</name>\n      <anchorfile>cpp/experimental/fs/directory_entry/replace_filename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>status</name>\n      <anchorfile>cpp/experimental/fs/directory_entry/status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>symlink_status</name>\n      <anchorfile>cpp/experimental/fs/directory_entry/status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::directory_iterator</name>\n    <filename>cpp/experimental/fs/directory_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>directory_iterator</name>\n      <anchorfile>cpp/experimental/fs/directory_iterator/directory_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>increment</name>\n      <anchorfile>cpp/experimental/fs/directory_iterator/increment</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/experimental/fs/directory_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/experimental/fs/directory_iterator/increment</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/experimental/fs/directory_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/fs/directory_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::directory_options</name>\n    <filename>cpp/experimental/fs/directory_options</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::file_status</name>\n    <filename>cpp/experimental/fs/file_status</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>file_status</name>\n      <anchorfile>cpp/experimental/fs/file_status/file_status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/fs/file_status/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>permissions</name>\n      <anchorfile>cpp/experimental/fs/file_status/permissions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>type</name>\n      <anchorfile>cpp/experimental/fs/file_status/type</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::file_time_type</name>\n    <filename>cpp/experimental/fs/file_time_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::file_type</name>\n    <filename>cpp/experimental/fs/file_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::filesystem_error</name>\n    <filename>cpp/experimental/fs/filesystem_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>filesystem_error</name>\n      <anchorfile>cpp/experimental/fs/filesystem_error/filesystem_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>path1</name>\n      <anchorfile>cpp/experimental/fs/filesystem_error/path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>path2</name>\n      <anchorfile>cpp/experimental/fs/filesystem_error/path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/experimental/fs/filesystem_error/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::path</name>\n    <filename>cpp/experimental/fs/path</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/experimental/fs/path/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/experimental/fs/path/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/experimental/fs/path/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/experimental/fs/path/native</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/experimental/fs/path/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/experimental/fs/path/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>concat</name>\n      <anchorfile>cpp/experimental/fs/path/concat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/experimental/fs/path/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/experimental/fs/path/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extension</name>\n      <anchorfile>cpp/experimental/fs/path/extension</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>filename</name>\n      <anchorfile>cpp/experimental/fs/path/filename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_string</name>\n      <anchorfile>cpp/experimental/fs/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_u16string</name>\n      <anchorfile>cpp/experimental/fs/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_u32string</name>\n      <anchorfile>cpp/experimental/fs/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_u8string</name>\n      <anchorfile>cpp/experimental/fs/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_wstring</name>\n      <anchorfile>cpp/experimental/fs/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_extension</name>\n      <anchorfile>cpp/experimental/fs/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_filename</name>\n      <anchorfile>cpp/experimental/fs/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_parent_path</name>\n      <anchorfile>cpp/experimental/fs/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_relative_path</name>\n      <anchorfile>cpp/experimental/fs/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_root_directory</name>\n      <anchorfile>cpp/experimental/fs/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_root_name</name>\n      <anchorfile>cpp/experimental/fs/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_root_path</name>\n      <anchorfile>cpp/experimental/fs/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_stem</name>\n      <anchorfile>cpp/experimental/fs/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_preferred</name>\n      <anchorfile>cpp/experimental/fs/path/make_preferred</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native</name>\n      <anchorfile>cpp/experimental/fs/path/native</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator string_type</name>\n      <anchorfile>cpp/experimental/fs/path/native</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/experimental/fs/path/concat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/experimental/fs/path/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/fs/path/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>parent_path</name>\n      <anchorfile>cpp/experimental/fs/path/parent_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>path</name>\n      <anchorfile>cpp/experimental/fs/path/path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>relative_path</name>\n      <anchorfile>cpp/experimental/fs/path/relative_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_filename</name>\n      <anchorfile>cpp/experimental/fs/path/remove_filename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_extension</name>\n      <anchorfile>cpp/experimental/fs/path/replace_extension</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_filename</name>\n      <anchorfile>cpp/experimental/fs/path/replace_filename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>root_directory</name>\n      <anchorfile>cpp/experimental/fs/path/root_directory</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>root_name</name>\n      <anchorfile>cpp/experimental/fs/path/root_name</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>root_path</name>\n      <anchorfile>cpp/experimental/fs/path/root_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stem</name>\n      <anchorfile>cpp/experimental/fs/path/stem</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>string</name>\n      <anchorfile>cpp/experimental/fs/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/experimental/fs/path/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u16string</name>\n      <anchorfile>cpp/experimental/fs/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u32string</name>\n      <anchorfile>cpp/experimental/fs/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u8string</name>\n      <anchorfile>cpp/experimental/fs/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstring</name>\n      <anchorfile>cpp/experimental/fs/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~path</name>\n      <anchorfile>cpp/experimental/fs/path/~path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::perms</name>\n    <filename>cpp/experimental/fs/perms</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::recursive_directory_iterator</name>\n    <filename>cpp/experimental/fs/recursive_directory_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>depth</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/depth</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>disable_recursion_pending</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/disable_recursion_pending</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>increment</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/increment</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/increment</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>options</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/options</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/pop</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>recursion_pending</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/recursion_pending</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>recursive_directory_iterator</name>\n      <anchorfile>cpp/experimental/fs/recursive_directory_iterator/recursive_directory_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::filesystem::space_info</name>\n    <filename>cpp/experimental/fs/space_info</filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>available</name>\n      <anchorfile>cpp/experimental/fs/space_info</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/experimental/fs/space_info</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>free</name>\n      <anchorfile>cpp/experimental/fs/space_info</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::invocation_type</name>\n    <filename>cpp/experimental/invocation_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::is_detected</name>\n    <filename>cpp/experimental/is_detected</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::is_detected_convertible</name>\n    <filename>cpp/experimental/is_detected</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::is_detected_exact</name>\n    <filename>cpp/experimental/is_detected</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::nonesuch</name>\n    <filename>cpp/experimental/nonesuch</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::observer_ptr</name>\n    <filename>cpp/experimental/observer_ptr</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::optional</name>\n    <filename>cpp/experimental/optional</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/experimental/optional/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/experimental/optional/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/experimental/optional/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/experimental/optional/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/optional/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>optional</name>\n      <anchorfile>cpp/experimental/optional/optional</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/experimental/optional/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value</name>\n      <anchorfile>cpp/experimental/optional/value</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_or</name>\n      <anchorfile>cpp/experimental/optional/value_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~optional</name>\n      <anchorfile>cpp/experimental/optional/~optional</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::ostream_joiner</name>\n    <filename>cpp/experimental/ostream_joiner</filename>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::experimental::pmr</name>\n    <filename></filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_default_resource</name>\n      <anchorfile>cpp/experimental/get_default_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::pmr::memory_resource</class>\n    <class kind=\"class\">std::experimental::pmr::monotonic_buffer_resource</class>\n    <class kind=\"class\">std::experimental::pmr::new_delete_resource</class>\n    <class kind=\"class\">std::experimental::pmr::null_memory_resource</class>\n    <class kind=\"class\">std::experimental::pmr::polymorphic_allocator</class>\n    <class kind=\"class\">std::experimental::pmr::resource_adaptor</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_default_resource</name>\n      <anchorfile>cpp/experimental/set_default_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::experimental::pmr::synchronized_pool_resource</class>\n    <class kind=\"class\">std::experimental::pmr::unsynchronized_pool_resource</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::pmr::memory_resource</name>\n    <filename>cpp/experimental/memory_resource</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/experimental/memory_resource/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/experimental/memory_resource/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_allocate</name>\n      <anchorfile>cpp/experimental/memory_resource/do_allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_deallocate</name>\n      <anchorfile>cpp/experimental/memory_resource/do_deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_is_equal</name>\n      <anchorfile>cpp/experimental/memory_resource/do_is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_equal</name>\n      <anchorfile>cpp/experimental/memory_resource/is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>memory_resource</name>\n      <anchorfile>cpp/experimental/memory_resource/memory_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::pmr::monotonic_buffer_resource</name>\n    <filename>cpp/experimental/monotonic_buffer_resource</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::pmr::new_delete_resource</name>\n    <filename>cpp/experimental/new_delete_resource</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::pmr::null_memory_resource</name>\n    <filename>cpp/experimental/null_Memory_resource</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::pmr::polymorphic_allocator</name>\n    <filename>cpp/experimental/polymorphic_allocator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::pmr::resource_adaptor</name>\n    <filename>cpp/experimental/resource_adaptor</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::pmr::synchronized_pool_resource</name>\n    <filename>cpp/experimental/synchronized_pool_resource</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::pmr::unsynchronized_pool_resource</name>\n    <filename>cpp/experimental/unsynchronized_pool_resource</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::propagate_const</name>\n    <filename>cpp/experimental/propagate_const</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::raw_invocation_type</name>\n    <filename>cpp/experimental/raw_invocation_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::source_location</name>\n    <filename>cpp/experimental/source_location</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::string_view</name>\n    <filename>cpp/experimental/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/experimental/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/experimental/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/experimental/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/experimental/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/experimental/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/experimental/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/experimental/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/experimental/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/experimental/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator basic_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/experimental/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>string_view</name>\n      <anchorfile>cpp/experimental/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/experimental/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/experimental/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::u16string_view</name>\n    <filename>cpp/experimental/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/experimental/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/experimental/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/experimental/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/experimental/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/experimental/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/experimental/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/experimental/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/experimental/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/experimental/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator basic_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/experimental/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/experimental/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/experimental/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u16string_view</name>\n      <anchorfile>cpp/experimental/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::u32string_view</name>\n    <filename>cpp/experimental/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/experimental/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/experimental/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/experimental/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/experimental/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/experimental/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/experimental/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/experimental/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/experimental/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/experimental/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator basic_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/experimental/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/experimental/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/experimental/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u32string_view</name>\n      <anchorfile>cpp/experimental/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::experimental::wstring_view</name>\n    <filename>cpp/experimental/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/experimental/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/experimental/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/experimental/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/experimental/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/experimental/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/experimental/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/experimental/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/experimental/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/experimental/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/experimental/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/experimental/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator basic_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/experimental/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/experimental/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/experimental/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/experimental/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/experimental/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/experimental/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/experimental/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/experimental/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_string</name>\n      <anchorfile>cpp/experimental/basic_string_view/to_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstring_view</name>\n      <anchorfile>cpp/experimental/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::exponential_distribution</name>\n    <filename>cpp/numeric/random/exponential_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exponential_distribution</name>\n      <anchorfile>cpp/numeric/random/exponential_distribution/exponential_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lambda</name>\n      <anchorfile>cpp/numeric/random/exponential_distribution/lambda</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/exponential_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/exponential_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/exponential_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/exponential_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/exponential_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::extent</name>\n    <filename>cpp/types/extent</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::extreme_value_distribution</name>\n    <filename>cpp/numeric/random/extreme_value_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>a</name>\n      <anchorfile>cpp/numeric/random/extreme_value_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>b</name>\n      <anchorfile>cpp/numeric/random/extreme_value_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extreme_value_distribution</name>\n      <anchorfile>cpp/numeric/random/extreme_value_distribution/extreme_value_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/extreme_value_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/extreme_value_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/extreme_value_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/extreme_value_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/extreme_value_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::false_type</name>\n    <filename>cpp/types/integral_constant</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::femto</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filebuf</name>\n    <filename>cpp/io/basic_filebuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_filebuf/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>filebuf</name>\n      <anchorfile>cpp/io/basic_filebuf/basic_filebuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_filebuf/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_filebuf/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_filebuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~filebuf</name>\n      <anchorfile>cpp/io/basic_filebuf/~basic_filebuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::filesystem</name>\n    <filename></filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>absolute</name>\n      <anchorfile>cpp/filesystem/absolute</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>canonical</name>\n      <anchorfile>cpp/filesystem/canonical</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/filesystem/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy_file</name>\n      <anchorfile>cpp/filesystem/copy_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::filesystem::copy_options</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy_symlink</name>\n      <anchorfile>cpp/filesystem/copy_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_directories</name>\n      <anchorfile>cpp/filesystem/create_directory</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_directory</name>\n      <anchorfile>cpp/filesystem/create_directory</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_directory_symlink</name>\n      <anchorfile>cpp/filesystem/create_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_hard_link</name>\n      <anchorfile>cpp/filesystem/create_hard_link</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>create_symlink</name>\n      <anchorfile>cpp/filesystem/create_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>current_path</name>\n      <anchorfile>cpp/filesystem/current_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::filesystem::directory_entry</class>\n    <class kind=\"class\">std::filesystem::directory_iterator</class>\n    <class kind=\"class\">std::filesystem::directory_options</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equivalent</name>\n      <anchorfile>cpp/filesystem/equivalent</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exists</name>\n      <anchorfile>cpp/filesystem/exists</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>file_size</name>\n      <anchorfile>cpp/filesystem/file_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::filesystem::file_status</class>\n    <class kind=\"class\">std::filesystem::file_time_type</class>\n    <class kind=\"class\">std::filesystem::file_type</class>\n    <class kind=\"class\">std::filesystem::filesystem_error</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hard_link_count</name>\n      <anchorfile>cpp/filesystem/hard_link_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_block_file</name>\n      <anchorfile>cpp/filesystem/is_block_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_character_file</name>\n      <anchorfile>cpp/filesystem/is_character_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_directory</name>\n      <anchorfile>cpp/filesystem/is_directory</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_empty</name>\n      <anchorfile>cpp/filesystem/is_empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_fifo</name>\n      <anchorfile>cpp/filesystem/is_fifo</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_other</name>\n      <anchorfile>cpp/filesystem/is_other</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_regular_file</name>\n      <anchorfile>cpp/filesystem/is_regular_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_socket</name>\n      <anchorfile>cpp/filesystem/is_socket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_symlink</name>\n      <anchorfile>cpp/filesystem/is_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>last_write_time</name>\n      <anchorfile>cpp/filesystem/last_write_time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::filesystem::path</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>permissions</name>\n      <anchorfile>cpp/filesystem/permissions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::filesystem::perms</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>proximate</name>\n      <anchorfile>cpp/filesystem/relative</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read_symlink</name>\n      <anchorfile>cpp/filesystem/read_symlink</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::filesystem::recursive_directory_iterator</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>relative</name>\n      <anchorfile>cpp/filesystem/relative</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove</name>\n      <anchorfile>cpp/filesystem/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_all</name>\n      <anchorfile>cpp/filesystem/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rename</name>\n      <anchorfile>cpp/filesystem/rename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize_file</name>\n      <anchorfile>cpp/filesystem/resize_file</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>space</name>\n      <anchorfile>cpp/filesystem/space</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::filesystem::space_info</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>status</name>\n      <anchorfile>cpp/filesystem/status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>status_known</name>\n      <anchorfile>cpp/filesystem/status_known</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>symlink_status</name>\n      <anchorfile>cpp/filesystem/status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>system_complete</name>\n      <anchorfile>cpp/filesystem/absolute</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>temp_directory_path</name>\n      <anchorfile>cpp/filesystem/temp_directory_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u8path</name>\n      <anchorfile>cpp/filesystem/path/u8path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>weakly_canonical</name>\n      <anchorfile>cpp/filesystem/canonical</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::copy_options</name>\n    <filename>cpp/filesystem/copy_options</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::directory_entry</name>\n    <filename>cpp/filesystem/directory_entry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/filesystem/directory_entry/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>directory_entry</name>\n      <anchorfile>cpp/filesystem/directory_entry/directory_entry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/filesystem/directory_entry/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>path</name>\n      <anchorfile>cpp/filesystem/directory_entry/path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_filename</name>\n      <anchorfile>cpp/filesystem/directory_entry/replace_filename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>status</name>\n      <anchorfile>cpp/filesystem/directory_entry/status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>symlink_status</name>\n      <anchorfile>cpp/filesystem/directory_entry/status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::directory_iterator</name>\n    <filename>cpp/filesystem/directory_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>directory_iterator</name>\n      <anchorfile>cpp/filesystem/directory_iterator/directory_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>increment</name>\n      <anchorfile>cpp/filesystem/directory_iterator/increment</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/filesystem/directory_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/filesystem/directory_iterator/increment</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/filesystem/directory_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/filesystem/directory_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::directory_options</name>\n    <filename>cpp/filesystem/directory_options</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::file_status</name>\n    <filename>cpp/filesystem/file_status</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>file_status</name>\n      <anchorfile>cpp/filesystem/file_status/file_status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/filesystem/file_status/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>permissions</name>\n      <anchorfile>cpp/filesystem/file_status/permissions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>type</name>\n      <anchorfile>cpp/filesystem/file_status/type</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~file_status</name>\n      <anchorfile>cpp/filesystem/file_status/~file_status</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::file_time_type</name>\n    <filename>cpp/filesystem/file_time_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::file_type</name>\n    <filename>cpp/filesystem/file_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::filesystem_error</name>\n    <filename>cpp/filesystem/filesystem_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>filesystem_error</name>\n      <anchorfile>cpp/filesystem/filesystem_error/filesystem_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>path1</name>\n      <anchorfile>cpp/filesystem/filesystem_error/path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>path2</name>\n      <anchorfile>cpp/filesystem/filesystem_error/path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/filesystem/filesystem_error/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::path</name>\n    <filename>cpp/filesystem/path</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/filesystem/path/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/filesystem/path/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/filesystem/path/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/filesystem/path/native</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/filesystem/path/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/filesystem/path/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>concat</name>\n      <anchorfile>cpp/filesystem/path/concat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/filesystem/path/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/filesystem/path/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extension</name>\n      <anchorfile>cpp/filesystem/path/extension</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>filename</name>\n      <anchorfile>cpp/filesystem/path/filename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_string</name>\n      <anchorfile>cpp/filesystem/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_u16string</name>\n      <anchorfile>cpp/filesystem/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_u32string</name>\n      <anchorfile>cpp/filesystem/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_u8string</name>\n      <anchorfile>cpp/filesystem/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generic_wstring</name>\n      <anchorfile>cpp/filesystem/path/generic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_extension</name>\n      <anchorfile>cpp/filesystem/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_filename</name>\n      <anchorfile>cpp/filesystem/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_parent_path</name>\n      <anchorfile>cpp/filesystem/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_relative_path</name>\n      <anchorfile>cpp/filesystem/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_root_directory</name>\n      <anchorfile>cpp/filesystem/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_root_name</name>\n      <anchorfile>cpp/filesystem/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_root_path</name>\n      <anchorfile>cpp/filesystem/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_stem</name>\n      <anchorfile>cpp/filesystem/path/has_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_absolute</name>\n      <anchorfile>cpp/filesystem/path/is_absrel</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_relative</name>\n      <anchorfile>cpp/filesystem/path/is_absrel</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lexically_normal</name>\n      <anchorfile>cpp/filesystem/path/lexically_normal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lexically_proximate</name>\n      <anchorfile>cpp/filesystem/path/lexically_normal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lexically_relative</name>\n      <anchorfile>cpp/filesystem/path/lexically_normal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_preferred</name>\n      <anchorfile>cpp/filesystem/path/make_preferred</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native</name>\n      <anchorfile>cpp/filesystem/path/native</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator string_type</name>\n      <anchorfile>cpp/filesystem/path/native</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator+=</name>\n      <anchorfile>cpp/filesystem/path/concat</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator/=</name>\n      <anchorfile>cpp/filesystem/path/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/filesystem/path/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>parent_path</name>\n      <anchorfile>cpp/filesystem/path/parent_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>path</name>\n      <anchorfile>cpp/filesystem/path/path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>relative_path</name>\n      <anchorfile>cpp/filesystem/path/relative_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_filename</name>\n      <anchorfile>cpp/filesystem/path/remove_filename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_extension</name>\n      <anchorfile>cpp/filesystem/path/replace_extension</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace_filename</name>\n      <anchorfile>cpp/filesystem/path/replace_filename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>root_directory</name>\n      <anchorfile>cpp/filesystem/path/root_directory</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>root_name</name>\n      <anchorfile>cpp/filesystem/path/root_name</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>root_path</name>\n      <anchorfile>cpp/filesystem/path/root_path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stem</name>\n      <anchorfile>cpp/filesystem/path/stem</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>string</name>\n      <anchorfile>cpp/filesystem/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/filesystem/path/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u16string</name>\n      <anchorfile>cpp/filesystem/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u32string</name>\n      <anchorfile>cpp/filesystem/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u8string</name>\n      <anchorfile>cpp/filesystem/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstring</name>\n      <anchorfile>cpp/filesystem/path/string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~path</name>\n      <anchorfile>cpp/filesystem/path/~path</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::perms</name>\n    <filename>cpp/filesystem/perms</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::recursive_directory_iterator</name>\n    <filename>cpp/filesystem/recursive_directory_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>depth</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/depth</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>disable_recursion_pending</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/disable_recursion_pending</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>increment</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/increment</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/increment</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>options</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/options</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/pop</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>recursion_pending</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/recursion_pending</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>recursive_directory_iterator</name>\n      <anchorfile>cpp/filesystem/recursive_directory_iterator/recursive_directory_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::filesystem::space_info</name>\n    <filename>cpp/filesystem/space_info</filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>available</name>\n      <anchorfile>cpp/filesystem/space_info</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/filesystem/space_info</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>free</name>\n      <anchorfile>cpp/filesystem/space_info</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::fisher_f_distribution</name>\n    <filename>cpp/numeric/random/fisher_f_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fisher_f_distribution</name>\n      <anchorfile>cpp/numeric/random/fisher_f_distribution/fisher_f_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>m</name>\n      <anchorfile>cpp/numeric/random/fisher_f_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/fisher_f_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/fisher_f_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>n</name>\n      <anchorfile>cpp/numeric/random/fisher_f_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/fisher_f_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/fisher_f_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/fisher_f_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::forward_iterator_tag</name>\n    <filename>cpp/iterator/iterator_tags</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::forward_list</name>\n    <filename>cpp/container/forward_list</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/container/forward_list/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>before_begin</name>\n      <anchorfile>cpp/container/forward_list/before_begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/forward_list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbefore_begin</name>\n      <anchorfile>cpp/container/forward_list/before_begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/forward_list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/forward_list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/forward_list/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_after</name>\n      <anchorfile>cpp/container/forward_list/emplace_after</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_front</name>\n      <anchorfile>cpp/container/forward_list/emplace_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/forward_list/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/forward_list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase_after</name>\n      <anchorfile>cpp/container/forward_list/erase_after</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>forward_list</name>\n      <anchorfile>cpp/container/forward_list/forward_list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/forward_list/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/forward_list/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert_after</name>\n      <anchorfile>cpp/container/forward_list/insert_after</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/forward_list/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/forward_list/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/forward_list/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_front</name>\n      <anchorfile>cpp/container/forward_list/pop_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_front</name>\n      <anchorfile>cpp/container/forward_list/push_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove</name>\n      <anchorfile>cpp/container/forward_list/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_if</name>\n      <anchorfile>cpp/container/forward_list/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/container/forward_list/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reverse</name>\n      <anchorfile>cpp/container/forward_list/reverse</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sort</name>\n      <anchorfile>cpp/container/forward_list/sort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>splice_after</name>\n      <anchorfile>cpp/container/forward_list/splice_after</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/forward_list/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique</name>\n      <anchorfile>cpp/container/forward_list/unique</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~forward_list</name>\n      <anchorfile>cpp/container/forward_list/~forward_list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::fpos</name>\n    <filename>cpp/io/fpos</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>state</name>\n      <anchorfile>cpp/io/fpos/state</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::fpos_t</name>\n    <filename>cpp/io/c</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::front_insert_iterator</name>\n    <filename>cpp/iterator/front_insert_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::fstream</name>\n    <filename>cpp/io/basic_fstream</filename>\n    <class kind=\"class\">std::fstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_fstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::fstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::fstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fstream</name>\n      <anchorfile>cpp/io/basic_fstream/basic_fstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_fstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_fstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_fstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::fstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::fstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::fstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::fstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::fstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::function</name>\n    <filename>cpp/utility/functional/function</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/utility/functional/function/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>function</name>\n      <anchorfile>cpp/utility/functional/function/function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/utility/functional/function/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/function/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/utility/functional/function/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/utility/functional/function/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>target</name>\n      <anchorfile>cpp/utility/functional/function/target</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>target_type</name>\n      <anchorfile>cpp/utility/functional/function/target_type</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~function</name>\n      <anchorfile>cpp/utility/functional/function/~function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::future</name>\n    <filename>cpp/thread/future</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>future</name>\n      <anchorfile>cpp/thread/future/future</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/thread/future/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/thread/future/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>share</name>\n      <anchorfile>cpp/thread/future/share</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>valid</name>\n      <anchorfile>cpp/thread/future/valid</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait</name>\n      <anchorfile>cpp/thread/future/wait</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait_for</name>\n      <anchorfile>cpp/thread/future/wait_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait_until</name>\n      <anchorfile>cpp/thread/future/wait_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~future</name>\n      <anchorfile>cpp/thread/future/~future</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::future_error</name>\n    <filename>cpp/thread/future_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>code</name>\n      <anchorfile>cpp/thread/future_error/code</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>future_error</name>\n      <anchorfile>cpp/thread/future_error/future_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::gamma_distribution</name>\n    <filename>cpp/numeric/random/gamma_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>alpha</name>\n      <anchorfile>cpp/numeric/random/gamma_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>beta</name>\n      <anchorfile>cpp/numeric/random/gamma_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gamma_distribution</name>\n      <anchorfile>cpp/numeric/random/gamma_distribution/gamma_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/gamma_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/gamma_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/gamma_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/gamma_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/gamma_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::geometric_distribution</name>\n    <filename>cpp/numeric/random/geometric_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>geometric_distribution</name>\n      <anchorfile>cpp/numeric/random/geometric_distribution/geometric_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/geometric_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/geometric_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>p</name>\n      <anchorfile>cpp/numeric/random/geometric_distribution/p</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/geometric_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/geometric_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::giga</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::greater</name>\n    <filename>cpp/utility/functional/greater</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/greater</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::greater_equal</name>\n    <filename>cpp/utility/functional/greater_equal</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/greater_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::gslice</name>\n    <filename>cpp/numeric/valarray/gslice</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::gslice_array</name>\n    <filename>cpp/numeric/valarray/gslice_array</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::has_unique_object_representations</name>\n    <filename>cpp/types/has_unique_object_representations</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::has_virtual_destructor</name>\n    <filename>cpp/types/has_virtual_destructor</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::hash</name>\n    <filename>cpp/utility/hash</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash</name>\n      <anchorfile>cpp/utility/hash/hash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/hash/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::hecto</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ifstream</name>\n    <filename>cpp/io/basic_ifstream</filename>\n    <class kind=\"class\">std::ifstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_ifstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ifstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ifstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ifstream</name>\n      <anchorfile>cpp/io/basic_ifstream/basic_ifstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_ifstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_ifstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ifstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ifstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ifstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ifstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ifstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ifstream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::imaxdiv_t</name>\n    <filename>cpp/numeric/math/div</filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>quot</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>rem</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::in_place_index_t</name>\n    <filename>cpp/utility/in_place</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::in_place_t</name>\n    <filename>cpp/utility/in_place</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::in_place_tag</name>\n    <filename>cpp/utility/in_place_tag</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::in_place_type_t</name>\n    <filename>cpp/utility/in_place</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::independent_bits_engine</name>\n    <filename>cpp/numeric/random/independent_bits_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>base</name>\n      <anchorfile>cpp/numeric/random/independent_bits_engine/base</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/independent_bits_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>independent_bits_engine</name>\n      <anchorfile>cpp/numeric/random/independent_bits_engine/independent_bits_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/independent_bits_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/independent_bits_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/independent_bits_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/independent_bits_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::index_sequence</name>\n    <filename>cpp/utility/integer_sequence</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::index_sequence_for</name>\n    <filename>cpp/utility/integer_sequence</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::indirect_array</name>\n    <filename>cpp/numeric/valarray/indirect_array</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::initializer_list</name>\n    <filename>cpp/utility/initializer_list</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/utility/initializer_list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/utility/initializer_list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>initializer_list</name>\n      <anchorfile>cpp/utility/initializer_list/initializer_list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/utility/initializer_list/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::input_iterator_tag</name>\n    <filename>cpp/iterator/iterator_tags</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::insert_iterator</name>\n    <filename>cpp/iterator/insert_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int16_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int32_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int64_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int8_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int_fast16_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int_fast32_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int_fast64_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int_fast8_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int_least16_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int_least32_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int_least64_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::int_least8_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::integer_sequence</name>\n    <filename>cpp/utility/integer_sequence</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::integral_constant</name>\n    <filename>cpp/types/integral_constant</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::intmax_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::intptr_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::invalid_argument</name>\n    <filename>cpp/error/invalid_argument</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>invalid_argument</name>\n      <anchorfile>cpp/error/invalid_argument</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ios_base</name>\n    <filename>cpp/io/ios_base</filename>\n    <class kind=\"class\">std::ios_base::Init</class>\n    <class kind=\"class\">std::ios_base::event_callback</class>\n    <class kind=\"class\">std::ios_base::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/ios_base/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ios_base</name>\n      <anchorfile>cpp/io/ios_base/ios_base</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~ios_base</name>\n      <anchorfile>cpp/io/ios_base/~ios_base</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ios_base::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ios_base::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ios_base::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::iostream</name>\n    <filename>cpp/io/basic_iostream</filename>\n    <class kind=\"class\">std::iostream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::iostream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::iostream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iostream</name>\n      <anchorfile>cpp/io/basic_iostream/basic_iostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::iostream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~iostream</name>\n      <anchorfile>cpp/io/basic_iostream/~basic_iostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::iostream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::iostream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::iostream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::iostream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_abstract</name>\n    <filename>cpp/types/is_abstract</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_arithmetic</name>\n    <filename>cpp/types/is_arithmetic</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_array</name>\n    <filename>cpp/types/is_array</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_assignable</name>\n    <filename>cpp/types/is_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_base_of</name>\n    <filename>cpp/types/is_base_of</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_bind_expression</name>\n    <filename>cpp/utility/functional/is_bind_expression</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_callable</name>\n    <filename>cpp/types/is_callable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_class</name>\n    <filename>cpp/types/is_class</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_compound</name>\n    <filename>cpp/types/is_compound</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_const</name>\n    <filename>cpp/types/is_const</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_constructible</name>\n    <filename>cpp/types/is_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_convertible</name>\n    <filename>cpp/types/is_convertible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_copy_assignable</name>\n    <filename>cpp/types/is_copy_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_copy_constructible</name>\n    <filename>cpp/types/is_copy_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_default_constructible</name>\n    <filename>cpp/types/is_default_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_destructible</name>\n    <filename>cpp/types/is_destructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_empty</name>\n    <filename>cpp/types/is_empty</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_enum</name>\n    <filename>cpp/types/is_enum</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_error_code_enum</name>\n    <filename>cpp/error/error_code/is_error_code_enum</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_error_code_enum_v</name>\n    <filename>cpp/error/error_code/is_error_code_enum</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_error_condition_enum</name>\n    <filename>cpp/error/error_condition/is_error_condition_enum</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_execution_policy</name>\n    <filename>cpp/algorithm/is_execution_policy</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_final</name>\n    <filename>cpp/types/is_final</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_floating_point</name>\n    <filename>cpp/types/is_floating_point</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_function</name>\n    <filename>cpp/types/is_function</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_fundamental</name>\n    <filename>cpp/types/is_fundamental</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_integral</name>\n    <filename>cpp/types/is_integral</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_literal_type</name>\n    <filename>cpp/types/is_literal_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_lvalue_reference</name>\n    <filename>cpp/types/is_lvalue_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_member_function_pointer</name>\n    <filename>cpp/types/is_member_function_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_member_object_pointer</name>\n    <filename>cpp/types/is_member_object_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_member_pointer</name>\n    <filename>cpp/types/is_member_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_move_assignable</name>\n    <filename>cpp/types/is_move_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_move_constructible</name>\n    <filename>cpp/types/is_move_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_assignable</name>\n    <filename>cpp/types/is_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_callable</name>\n    <filename>cpp/types/is_callable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_constructible</name>\n    <filename>cpp/types/is_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_copy_assignable</name>\n    <filename>cpp/types/is_copy_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_copy_constructible</name>\n    <filename>cpp/types/is_copy_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_default_constructible</name>\n    <filename>cpp/types/is_default_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_destructible</name>\n    <filename>cpp/types/is_destructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_move_assignable</name>\n    <filename>cpp/types/is_move_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_move_constructible</name>\n    <filename>cpp/types/is_move_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_swappable</name>\n    <filename>cpp/types/is_swappable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_nothrow_swappable_with</name>\n    <filename>cpp/types/is_swappable_with</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_null_pointer</name>\n    <filename>cpp/types/is_null_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_object</name>\n    <filename>cpp/types/is_object</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_placeholder</name>\n    <filename>cpp/utility/functional/is_placeholder</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_pod</name>\n    <filename>cpp/types/is_pod</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_pointer</name>\n    <filename>cpp/types/is_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_polymorphic</name>\n    <filename>cpp/types/is_polymorphic</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_reference</name>\n    <filename>cpp/types/is_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_rvalue_reference</name>\n    <filename>cpp/types/is_rvalue_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_same</name>\n    <filename>cpp/types/is_same</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_scalar</name>\n    <filename>cpp/types/is_scalar</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_signed</name>\n    <filename>cpp/types/is_signed</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_standard_layout</name>\n    <filename>cpp/types/is_standard_layout</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_swappable</name>\n    <filename>cpp/types/is_swappable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_swappable_with</name>\n    <filename>cpp/types/is_swappable_with</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivial</name>\n    <filename>cpp/types/is_trivial</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_assignable</name>\n    <filename>cpp/types/is_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_constructible</name>\n    <filename>cpp/types/is_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_copy_assignable</name>\n    <filename>cpp/types/is_copy_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_copy_constructible</name>\n    <filename>cpp/types/is_copy_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_copyable</name>\n    <filename>cpp/types/is_trivially_copyable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_default_constructible</name>\n    <filename>cpp/types/is_default_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_destructible</name>\n    <filename>cpp/types/is_destructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_move_assignable</name>\n    <filename>cpp/types/is_move_assignable</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_trivially_move_constructible</name>\n    <filename>cpp/types/is_move_constructible</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_union</name>\n    <filename>cpp/types/is_union</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_unsigned</name>\n    <filename>cpp/types/is_unsigned</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_void</name>\n    <filename>cpp/types/is_void</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::is_volatile</name>\n    <filename>cpp/types/is_volatile</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istream</name>\n    <filename>cpp/io/basic_istream</filename>\n    <class kind=\"class\">std::istream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>istream</name>\n      <anchorfile>cpp/io/basic_istream/basic_istream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~istream</name>\n      <anchorfile>cpp/io/basic_istream/~basic_istream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istream_iterator</name>\n    <filename>cpp/iterator/istream_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istreambuf_iterator</name>\n    <filename>cpp/iterator/istreambuf_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istringstream</name>\n    <filename>cpp/io/basic_istringstream</filename>\n    <class kind=\"class\">std::istringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>istringstream</name>\n      <anchorfile>cpp/io/basic_istringstream/basic_istringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_istringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_istringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istringstream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istrstream</name>\n    <filename>cpp/io/istrstream</filename>\n    <class kind=\"class\">std::istrstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istrstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istrstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>istrstream</name>\n      <anchorfile>cpp/io/istrstream/istrstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::istrstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/istrstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~istrstream</name>\n      <anchorfile>cpp/io/istrstream/~istrstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istrstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istrstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istrstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::istrstream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::iterator</name>\n    <filename>cpp/iterator/iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::iterator_traits</name>\n    <filename>cpp/iterator/iterator_traits</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::jmp_buf</name>\n    <filename>cpp/utility/program/jmp_buf</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::kilo</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::knuth_b</name>\n    <filename>cpp/numeric/random/shuffle_order_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>base</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/base</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>knuth_b</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/shuffle_order_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::lconv</name>\n    <filename>cpp/locale/lconv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ldiv_t</name>\n    <filename>cpp/numeric/math/div</filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>quot</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>rem</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::length_error</name>\n    <filename>cpp/error/length_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length_error</name>\n      <anchorfile>cpp/error/length_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::less</name>\n    <filename>cpp/utility/functional/less</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/less</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::less_equal</name>\n    <filename>cpp/utility/functional/less_equal</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/less_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::linear_congruential_engine</name>\n    <filename>cpp/numeric/random/linear_congruential_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>linear_congruential_engine</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/linear_congruential_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::list</name>\n    <filename>cpp/container/list</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/container/list/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/container/list/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/list/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/list/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/list/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/list/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_back</name>\n      <anchorfile>cpp/container/list/emplace_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_front</name>\n      <anchorfile>cpp/container/list/emplace_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/list/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/list/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/list/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/list/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/list/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>list</name>\n      <anchorfile>cpp/container/list/list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/list/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/list/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/list/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/container/list/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_front</name>\n      <anchorfile>cpp/container/list/pop_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/container/list/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_front</name>\n      <anchorfile>cpp/container/list/push_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/list/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove</name>\n      <anchorfile>cpp/container/list/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_if</name>\n      <anchorfile>cpp/container/list/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/list/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/container/list/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reverse</name>\n      <anchorfile>cpp/container/list/reverse</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/list/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sort</name>\n      <anchorfile>cpp/container/list/sort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>splice</name>\n      <anchorfile>cpp/container/list/splice</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/list/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique</name>\n      <anchorfile>cpp/container/list/unique</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~list</name>\n      <anchorfile>cpp/container/list/~list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::lldiv_t</name>\n    <filename>cpp/numeric/math/div</filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>quot</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>rem</name>\n      <anchorfile>cpp/numeric/math/div</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::locale</name>\n    <filename>cpp/locale/locale</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>classic</name>\n      <anchorfile>cpp/locale/locale/classic</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>combine</name>\n      <anchorfile>cpp/locale/locale/combine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::locale::facet</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>global</name>\n      <anchorfile>cpp/locale/locale/global</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::locale::id</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>locale</name>\n      <anchorfile>cpp/locale/locale/locale</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>name</name>\n      <anchorfile>cpp/locale/locale/name</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/locale/locale/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/locale/locale/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/locale/locale/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/locale/locale/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~locale</name>\n      <anchorfile>cpp/locale/locale/~locale</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::locale::facet</name>\n    <filename>cpp/locale/locale/facet</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>facet</name>\n      <anchorfile>cpp/locale/locale/facet/facet</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::locale::id</name>\n    <filename>cpp/locale/locale/id</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>id</name>\n      <anchorfile>cpp/locale/locale/id/id</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::lock_guard</name>\n    <filename>cpp/thread/lock_guard</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock_guard</name>\n      <anchorfile>cpp/thread/lock_guard/lock_guard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~lock_guard</name>\n      <anchorfile>cpp/thread/lock_guard/~lock_guard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::logic_error</name>\n    <filename>cpp/error/logic_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>logic_error</name>\n      <anchorfile>cpp/error/logic_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::logical_and</name>\n    <filename>cpp/utility/functional/logical_and</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/logical_and</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::logical_not</name>\n    <filename>cpp/utility/functional/logical_not</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/logical_not</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::logical_or</name>\n    <filename>cpp/utility/functional/logical_or</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/logical_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::lognormal_distribution</name>\n    <filename>cpp/numeric/random/lognormal_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lognormal_distribution</name>\n      <anchorfile>cpp/numeric/random/lognormal_distribution/lognormal_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>m</name>\n      <anchorfile>cpp/numeric/random/lognormal_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/lognormal_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/lognormal_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/lognormal_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/lognormal_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/lognormal_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>s</name>\n      <anchorfile>cpp/numeric/random/lognormal_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_boyer_moore_horspool_searcher</name>\n    <filename>cpp/utility/functional/boyer_moore_horspool_searcher</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_boyer_moore_searcher</name>\n    <filename>cpp/utility/functional/boyer_moore_searcher</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_default_searcher</name>\n    <filename>cpp/utility/functional/default_searcher</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_index_sequence</name>\n    <filename>cpp/utility/integer_sequence</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_integer_sequence</name>\n    <filename>cpp/utility/integer_sequence</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_signed</name>\n    <filename>cpp/types/make_signed</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_signed_t</name>\n    <filename>cpp/types/make_signed</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_unsigned</name>\n    <filename>cpp/types/make_unsigned</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::make_unsigned_t</name>\n    <filename>cpp/types/make_unsigned</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::map</name>\n    <filename>cpp/container/map</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/map/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/map/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/map/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/map/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/map/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/map/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/map/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/map/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/map/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/map/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/map/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/map/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/map/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/map/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/map/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/map/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/map/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/map/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert_or_assign</name>\n      <anchorfile>cpp/container/map/insert_or_assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_comp</name>\n      <anchorfile>cpp/container/map/key_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/container/map/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>map</name>\n      <anchorfile>cpp/container/map/map</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/map/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/map/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/map/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/map/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/map/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/map/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/map/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/map/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_emplace</name>\n      <anchorfile>cpp/container/map/try_emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/container/map/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_comp</name>\n      <anchorfile>cpp/container/map/value_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::map::value_compare</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~map</name>\n      <anchorfile>cpp/container/map/~map</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::map::value_compare</name>\n    <filename>cpp/container/map/value_compare</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::mask_array</name>\n    <filename>cpp/numeric/valarray/mask_array</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::match_results</name>\n    <filename>cpp/regex/match_results</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/regex/match_results/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>format</name>\n      <anchorfile>cpp/regex/match_results/format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/regex/match_results/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/match_results/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>match_results</name>\n      <anchorfile>cpp/regex/match_results/match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/regex/match_results/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/regex/match_results/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>position</name>\n      <anchorfile>cpp/regex/match_results/position</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>prefix</name>\n      <anchorfile>cpp/regex/match_results/prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ready</name>\n      <anchorfile>cpp/regex/match_results/ready</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/regex/match_results/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/match_results/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>suffix</name>\n      <anchorfile>cpp/regex/match_results/suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/regex/match_results/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~match_results</name>\n      <anchorfile>cpp/regex/match_results/~match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::max_align_t</name>\n    <filename>cpp/types/max_align_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::mbstate_t</name>\n    <filename>cpp/string/multibyte/mbstate_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::mega</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::mersenne_twister_engine</name>\n    <filename>cpp/numeric/random/mersenne_twister_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mersenne_twister_engine</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/mersenne_twister_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages</name>\n    <filename>cpp/locale/messages</filename>\n    <class kind=\"class\">std::messages::catalog</class>\n    <class kind=\"class\">std::messages::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/locale/messages/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_close</name>\n      <anchorfile>cpp/locale/messages/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get</name>\n      <anchorfile>cpp/locale/messages/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_open</name>\n      <anchorfile>cpp/locale/messages/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/locale/messages/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>messages</name>\n      <anchorfile>cpp/locale/messages/messages</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/locale/messages/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::messages::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~messages</name>\n      <anchorfile>cpp/locale/messages/~messages</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages::catalog</name>\n    <filename>cpp/locale/messages_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages::char_type</name>\n    <filename>cpp/locale/messages</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages::string_type</name>\n    <filename>cpp/locale/messages</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages_base</name>\n    <filename>cpp/locale/messages_base</filename>\n    <class kind=\"class\">std::messages_base::catalog</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages_base::catalog</name>\n    <filename>cpp/locale/messages_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages_byname</name>\n    <filename>cpp/locale/messages_byname</filename>\n    <class kind=\"class\">std::messages_byname::catalog</class>\n    <class kind=\"class\">std::messages_byname::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/locale/messages/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_close</name>\n      <anchorfile>cpp/locale/messages/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get</name>\n      <anchorfile>cpp/locale/messages/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_open</name>\n      <anchorfile>cpp/locale/messages/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/locale/messages/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>messages_byname</name>\n      <anchorfile>cpp/locale/messages_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/locale/messages/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::messages_byname::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~messages_byname</name>\n      <anchorfile>cpp/locale/messages_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages_byname::catalog</name>\n    <filename>cpp/locale/messages_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages_byname::char_type</name>\n    <filename>cpp/locale/messages</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::messages_byname::string_type</name>\n    <filename>cpp/locale/messages</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::micro</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::milli</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::minstd_rand</name>\n    <filename>cpp/numeric/random/linear_congruential_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>minstd_rand</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/linear_congruential_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::minstd_rand0</name>\n    <filename>cpp/numeric/random/linear_congruential_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>minstd_rand0</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/linear_congruential_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/linear_congruential_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::minus</name>\n    <filename>cpp/utility/functional/minus</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/minus</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::modulus</name>\n    <filename>cpp/utility/functional/modulus</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/modulus</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_base</name>\n    <filename>cpp/locale/money_base</filename>\n    <class kind=\"class\">std::money_base::pattern</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_base::pattern</name>\n    <filename>cpp/locale/money_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_get</name>\n    <filename>cpp/locale/money_get</filename>\n    <class kind=\"class\">std::money_get::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get</name>\n      <anchorfile>cpp/locale/money_get/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/locale/money_get/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::money_get::iter_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>money_get</name>\n      <anchorfile>cpp/locale/money_get/money_get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::money_get::pattern</class>\n    <class kind=\"class\">std::money_get::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~money_get</name>\n      <anchorfile>cpp/locale/money_get/~money_get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_get::char_type</name>\n    <filename>cpp/locale/money_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_get::iter_type</name>\n    <filename>cpp/locale/money_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_get::pattern</name>\n    <filename>cpp/locale/money_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_get::string_type</name>\n    <filename>cpp/locale/money_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_put</name>\n    <filename>cpp/locale/money_put</filename>\n    <class kind=\"class\">std::money_put::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_put</name>\n      <anchorfile>cpp/locale/money_put/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::money_put::iter_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>money_put</name>\n      <anchorfile>cpp/locale/money_put/money_put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::money_put::pattern</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/locale/money_put/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::money_put::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~money_put</name>\n      <anchorfile>cpp/locale/money_put/~money_put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_put::char_type</name>\n    <filename>cpp/locale/money_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_put::iter_type</name>\n    <filename>cpp/locale/money_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_put::pattern</name>\n    <filename>cpp/locale/money_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::money_put::string_type</name>\n    <filename>cpp/locale/money_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::moneypunct</name>\n    <filename>cpp/locale/moneypunct</filename>\n    <class kind=\"class\">std::moneypunct::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>curr_symbol</name>\n      <anchorfile>cpp/locale/moneypunct/curr_symbol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>decimal_point</name>\n      <anchorfile>cpp/locale/moneypunct/decimal_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_curr_symbol</name>\n      <anchorfile>cpp/locale/moneypunct/curr_symbol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_decimal_point</name>\n      <anchorfile>cpp/locale/moneypunct/decimal_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_frac_digits</name>\n      <anchorfile>cpp/locale/moneypunct/frac_digits</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_grouping</name>\n      <anchorfile>cpp/locale/moneypunct/grouping</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_neg_format</name>\n      <anchorfile>cpp/locale/moneypunct/pos_format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_negative_sign</name>\n      <anchorfile>cpp/locale/moneypunct/positive_sign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_pos_format</name>\n      <anchorfile>cpp/locale/moneypunct/pos_format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_positive_sign</name>\n      <anchorfile>cpp/locale/moneypunct/positive_sign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_thousands_sep</name>\n      <anchorfile>cpp/locale/moneypunct/thousands_sep</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>frac_digits</name>\n      <anchorfile>cpp/locale/moneypunct/frac_digits</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>grouping</name>\n      <anchorfile>cpp/locale/moneypunct/grouping</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>moneypunct</name>\n      <anchorfile>cpp/locale/moneypunct/moneypunct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>neg_format</name>\n      <anchorfile>cpp/locale/moneypunct/pos_format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>negative_sign</name>\n      <anchorfile>cpp/locale/moneypunct/positive_sign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::moneypunct::pattern</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pos_format</name>\n      <anchorfile>cpp/locale/moneypunct/pos_format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>positive_sign</name>\n      <anchorfile>cpp/locale/moneypunct/positive_sign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::moneypunct::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>thousands_sep</name>\n      <anchorfile>cpp/locale/moneypunct/thousands_sep</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~moneypunct</name>\n      <anchorfile>cpp/locale/moneypunct/~moneypunct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::moneypunct::char_type</name>\n    <filename>cpp/locale/moneypunct</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::moneypunct::pattern</name>\n    <filename>cpp/locale/money_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::moneypunct::string_type</name>\n    <filename>cpp/locale/moneypunct</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::moneypunct_byname</name>\n    <filename>cpp/locale/moneypunct_byname</filename>\n    <class kind=\"class\">std::moneypunct_byname::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>curr_symbol</name>\n      <anchorfile>cpp/locale/moneypunct/curr_symbol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>decimal_point</name>\n      <anchorfile>cpp/locale/moneypunct/decimal_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_curr_symbol</name>\n      <anchorfile>cpp/locale/moneypunct/curr_symbol</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_decimal_point</name>\n      <anchorfile>cpp/locale/moneypunct/decimal_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_frac_digits</name>\n      <anchorfile>cpp/locale/moneypunct/frac_digits</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_grouping</name>\n      <anchorfile>cpp/locale/moneypunct/grouping</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_neg_format</name>\n      <anchorfile>cpp/locale/moneypunct/pos_format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_negative_sign</name>\n      <anchorfile>cpp/locale/moneypunct/positive_sign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_pos_format</name>\n      <anchorfile>cpp/locale/moneypunct/pos_format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_positive_sign</name>\n      <anchorfile>cpp/locale/moneypunct/positive_sign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_thousands_sep</name>\n      <anchorfile>cpp/locale/moneypunct/thousands_sep</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>frac_digits</name>\n      <anchorfile>cpp/locale/moneypunct/frac_digits</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>grouping</name>\n      <anchorfile>cpp/locale/moneypunct/grouping</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>moneypunct_byname</name>\n      <anchorfile>cpp/locale/moneypunct_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>neg_format</name>\n      <anchorfile>cpp/locale/moneypunct/pos_format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>negative_sign</name>\n      <anchorfile>cpp/locale/moneypunct/positive_sign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::moneypunct_byname::pattern</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pos_format</name>\n      <anchorfile>cpp/locale/moneypunct/pos_format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>positive_sign</name>\n      <anchorfile>cpp/locale/moneypunct/positive_sign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::moneypunct_byname::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>thousands_sep</name>\n      <anchorfile>cpp/locale/moneypunct/thousands_sep</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~moneypunct_byname</name>\n      <anchorfile>cpp/locale/moneypunct_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::moneypunct_byname::char_type</name>\n    <filename>cpp/locale/moneypunct</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::moneypunct_byname::pattern</name>\n    <filename>cpp/locale/money_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::moneypunct_byname::string_type</name>\n    <filename>cpp/locale/moneypunct</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::monostate</name>\n    <filename>cpp/utility/variant/monostate</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>monostate</name>\n      <anchorfile>cpp/utility/variant/monostate/monostate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/utility/variant/monostate/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~monostate</name>\n      <anchorfile>cpp/utility/variant/monostate/~monostate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::move_iterator</name>\n    <filename>cpp/iterator/move_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::mt19937</name>\n    <filename>cpp/numeric/random/mersenne_twister_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mt19937</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/mersenne_twister_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::mt19937_64</name>\n    <filename>cpp/numeric/random/mersenne_twister_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mt19937_64</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/mersenne_twister_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/mersenne_twister_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::multimap</name>\n    <filename>cpp/container/multimap</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/multimap/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/multimap/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/multimap/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/multimap/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/multimap/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/multimap/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/multimap/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/multimap/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/multimap/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/multimap/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/multimap/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/multimap/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/multimap/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/multimap/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/multimap/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/multimap/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/multimap/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_comp</name>\n      <anchorfile>cpp/container/multimap/key_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/container/multimap/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/multimap/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/multimap/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>multimap</name>\n      <anchorfile>cpp/container/multimap/multimap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/multimap/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/multimap/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/multimap/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/multimap/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/multimap/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/container/multimap/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_comp</name>\n      <anchorfile>cpp/container/multimap/value_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::multimap::value_compare</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~multimap</name>\n      <anchorfile>cpp/container/multimap/~multimap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::multimap::value_compare</name>\n    <filename>cpp/container/multimap/value_compare</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::multiplies</name>\n    <filename>cpp/utility/functional/multiplies</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/multiplies</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::multiset</name>\n    <filename>cpp/container/multiset</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/multiset/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/multiset/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/multiset/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/multiset/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/multiset/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/multiset/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/multiset/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/multiset/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/multiset/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/multiset/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/multiset/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/multiset/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/multiset/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/multiset/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/multiset/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/multiset/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/multiset/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_comp</name>\n      <anchorfile>cpp/container/multiset/key_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/container/multiset/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/multiset/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/multiset/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>multiset</name>\n      <anchorfile>cpp/container/multiset/multiset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/multiset/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/multiset/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/multiset/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/multiset/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/multiset/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/container/multiset/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_comp</name>\n      <anchorfile>cpp/container/multiset/value_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~multiset</name>\n      <anchorfile>cpp/container/multiset/~multiset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::mutex</name>\n    <filename>cpp/thread/mutex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/mutex/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mutex</name>\n      <anchorfile>cpp/thread/mutex/mutex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native_handle</name>\n      <anchorfile>cpp/thread/mutex/native_handle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/mutex/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock</name>\n      <anchorfile>cpp/thread/mutex/unlock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::nano</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::negate</name>\n    <filename>cpp/utility/functional/negate</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/negate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::negation</name>\n    <filename>cpp/types/negation</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::negative_binomial_distribution</name>\n    <filename>cpp/numeric/random/negative_binomial_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>k</name>\n      <anchorfile>cpp/numeric/random/negative_binomial_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/negative_binomial_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/negative_binomial_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>negative_binomial_distribution</name>\n      <anchorfile>cpp/numeric/random/negative_binomial_distribution/negative_binomial_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>p</name>\n      <anchorfile>cpp/numeric/random/negative_binomial_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/negative_binomial_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/negative_binomial_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::nested_exception</name>\n    <filename>cpp/error/nested_exception</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nested_exception</name>\n      <anchorfile>cpp/error/nested_exception/nested_exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>nested_ptr</name>\n      <anchorfile>cpp/error/nested_exception/nested_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/error/nested_exception/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rethrow_nested</name>\n      <anchorfile>cpp/error/nested_exception/rethrow_nested</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~nested_exception</name>\n      <anchorfile>cpp/error/nested_exception/~nested_exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::new_handler</name>\n    <filename>cpp/memory/new/new_handler</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::normal_distribution</name>\n    <filename>cpp/numeric/random/normal_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/normal_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mean</name>\n      <anchorfile>cpp/numeric/random/normal_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/normal_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>normal_distribution</name>\n      <anchorfile>cpp/numeric/random/normal_distribution/normal_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/normal_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/normal_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/normal_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stddev</name>\n      <anchorfile>cpp/numeric/random/normal_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::not_equal_to</name>\n    <filename>cpp/utility/functional/not_equal_to</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/not_equal_to</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::nothrow_t</name>\n    <filename>cpp/memory/new/nothrow_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::nullopt_t</name>\n    <filename>cpp/utility/optional/nullopt_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::nullptr_t</name>\n    <filename>cpp/types/nullptr_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::num_get</name>\n    <filename>cpp/locale/num_get</filename>\n    <class kind=\"class\">std::num_get::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get</name>\n      <anchorfile>cpp/locale/num_get/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/locale/num_get/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::num_get::iter_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>num_get</name>\n      <anchorfile>cpp/locale/num_get/num_get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~num_get</name>\n      <anchorfile>cpp/locale/num_get/~num_get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::num_get::char_type</name>\n    <filename>cpp/locale/num_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::num_get::iter_type</name>\n    <filename>cpp/locale/num_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::num_put</name>\n    <filename>cpp/locale/num_put</filename>\n    <class kind=\"class\">std::num_put::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_put</name>\n      <anchorfile>cpp/locale/num_put/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::num_put::iter_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>num_put</name>\n      <anchorfile>cpp/locale/num_put/num_put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/locale/num_put/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~num_put</name>\n      <anchorfile>cpp/locale/num_put/~num_put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::num_put::char_type</name>\n    <filename>cpp/locale/num_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::num_put::iter_type</name>\n    <filename>cpp/locale/num_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::numeric_limits</name>\n    <filename>cpp/types/numeric_limits</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>denorm_min</name>\n      <anchorfile>cpp/types/numeric_limits/denorm_min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epsilon</name>\n      <anchorfile>cpp/types/numeric_limits/epsilon</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>infinity</name>\n      <anchorfile>cpp/types/numeric_limits/infinity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lowest</name>\n      <anchorfile>cpp/types/numeric_limits/lowest</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/types/numeric_limits/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/types/numeric_limits/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>quiet_NaN</name>\n      <anchorfile>cpp/types/numeric_limits/quiet_NaN</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>round_error</name>\n      <anchorfile>cpp/types/numeric_limits/round_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>signaling_NaN</name>\n      <anchorfile>cpp/types/numeric_limits/signaling_NaN</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::numpunct</name>\n    <filename>cpp/locale/numpunct</filename>\n    <class kind=\"class\">std::numpunct::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>decimal_point</name>\n      <anchorfile>cpp/locale/numpunct/decimal_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_decimal_point</name>\n      <anchorfile>cpp/locale/numpunct/decimal_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_falsename</name>\n      <anchorfile>cpp/locale/numpunct/truefalsename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_grouping</name>\n      <anchorfile>cpp/locale/numpunct/grouping</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_thousands_sep</name>\n      <anchorfile>cpp/locale/numpunct/thousands_sep</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_truename</name>\n      <anchorfile>cpp/locale/numpunct/truefalsename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>falsename</name>\n      <anchorfile>cpp/locale/numpunct/truefalsename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>grouping</name>\n      <anchorfile>cpp/locale/numpunct/grouping</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>numpunct</name>\n      <anchorfile>cpp/locale/numpunct/numpunct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::numpunct::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>thousands_sep</name>\n      <anchorfile>cpp/locale/numpunct/thousands_sep</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>truename</name>\n      <anchorfile>cpp/locale/numpunct/truefalsename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~numpunct</name>\n      <anchorfile>cpp/locale/numpunct/~numpunct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::numpunct::char_type</name>\n    <filename>cpp/locale/numpunct</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::numpunct::string_type</name>\n    <filename>cpp/locale/numpunct</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::numpunct_byname</name>\n    <filename>cpp/locale/numpunct_byname</filename>\n    <class kind=\"class\">std::numpunct_byname::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>decimal_point</name>\n      <anchorfile>cpp/locale/numpunct/decimal_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_decimal_point</name>\n      <anchorfile>cpp/locale/numpunct/decimal_point</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_falsename</name>\n      <anchorfile>cpp/locale/numpunct/truefalsename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_grouping</name>\n      <anchorfile>cpp/locale/numpunct/grouping</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_thousands_sep</name>\n      <anchorfile>cpp/locale/numpunct/thousands_sep</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_truename</name>\n      <anchorfile>cpp/locale/numpunct/truefalsename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>falsename</name>\n      <anchorfile>cpp/locale/numpunct/truefalsename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>grouping</name>\n      <anchorfile>cpp/locale/numpunct/grouping</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>numpunct_byname</name>\n      <anchorfile>cpp/locale/numpunct_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::numpunct_byname::string_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>thousands_sep</name>\n      <anchorfile>cpp/locale/numpunct/thousands_sep</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>truename</name>\n      <anchorfile>cpp/locale/numpunct/truefalsename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~numpunct_byname</name>\n      <anchorfile>cpp/locale/numpunct_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::numpunct_byname::char_type</name>\n    <filename>cpp/locale/numpunct</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::numpunct_byname::string_type</name>\n    <filename>cpp/locale/numpunct</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ofstream</name>\n    <filename>cpp/io/basic_ofstream</filename>\n    <class kind=\"class\">std::ofstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_ofstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ofstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ofstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_ofstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ofstream</name>\n      <anchorfile>cpp/io/basic_ofstream/basic_ofstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_ofstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ofstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ofstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ofstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ofstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ofstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ofstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::once_flag</name>\n    <filename>cpp/thread/once_flag</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>once_flag</name>\n      <anchorfile>cpp/thread/once_flag</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::optional</name>\n    <filename>cpp/utility/optional</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/utility/optional/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>has_value</name>\n      <anchorfile>cpp/utility/optional/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/utility/optional/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/utility/optional/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/utility/optional/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/utility/optional/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>optional</name>\n      <anchorfile>cpp/utility/optional/optional</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/utility/optional/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/utility/optional/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value</name>\n      <anchorfile>cpp/utility/optional/value</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_or</name>\n      <anchorfile>cpp/utility/optional/value_or</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~optional</name>\n      <anchorfile>cpp/utility/optional/~optional</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostream</name>\n    <filename>cpp/io/basic_ostream</filename>\n    <class kind=\"class\">std::ostream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ostream</name>\n      <anchorfile>cpp/io/basic_ostream/basic_ostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~ostream</name>\n      <anchorfile>cpp/io/basic_ostream/~basic_ostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostream_iterator</name>\n    <filename>cpp/iterator/ostream_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostreambuf_iterator</name>\n    <filename>cpp/iterator/ostreambuf_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostringstream</name>\n    <filename>cpp/io/basic_ostringstream</filename>\n    <class kind=\"class\">std::ostringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ostringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ostringstream</name>\n      <anchorfile>cpp/io/basic_ostringstream/basic_ostringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_ostringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostringstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostrstream</name>\n    <filename>cpp/io/ostrstream</filename>\n    <class kind=\"class\">std::ostrstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostrstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostrstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>freeze</name>\n      <anchorfile>cpp/io/ostrstream/freeze</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ostrstream</name>\n      <anchorfile>cpp/io/ostrstream/ostrstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pcount</name>\n      <anchorfile>cpp/io/ostrstream/pcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::ostrstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/ostrstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~ostrstream</name>\n      <anchorfile>cpp/io/ostrstream/~ostrstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostrstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostrstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostrstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ostrstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::out_of_range</name>\n    <filename>cpp/error/out_of_range</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>out_of_range</name>\n      <anchorfile>cpp/error/out_of_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::output_iterator_tag</name>\n    <filename>cpp/iterator/iterator_tags</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::overflow_error</name>\n    <filename>cpp/error/overflow_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow_error</name>\n      <anchorfile>cpp/error/overflow_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::owner_less</name>\n    <filename>cpp/memory/owner_less</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/memory/owner_less</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::packaged_task</name>\n    <filename>cpp/thread/packaged_task</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_future</name>\n      <anchorfile>cpp/thread/packaged_task/get_future</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>make_ready_at_thread_exit</name>\n      <anchorfile>cpp/thread/packaged_task/make_ready_at_thread_exit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/thread/packaged_task/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/thread/packaged_task/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>packaged_task</name>\n      <anchorfile>cpp/thread/packaged_task/packaged_task</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/thread/packaged_task/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/thread/packaged_task/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>valid</name>\n      <anchorfile>cpp/thread/packaged_task/valid</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~packaged_task</name>\n      <anchorfile>cpp/thread/packaged_task/~packaged_task</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pair</name>\n    <filename>cpp/utility/pair</filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>first</name>\n      <anchorfile>cpp/utility/pair</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/utility/pair/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pair</name>\n      <anchorfile>cpp/utility/pair/pair</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>second</name>\n      <anchorfile>cpp/utility/pair</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/utility/pair/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::peta</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pico</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::piecewise_constant_distribution</name>\n    <filename>cpp/numeric/random/piecewise_constant_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>densities</name>\n      <anchorfile>cpp/numeric/random/piecewise_constant_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>intervals</name>\n      <anchorfile>cpp/numeric/random/piecewise_constant_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/piecewise_constant_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/piecewise_constant_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/piecewise_constant_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/piecewise_constant_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>piecewise_constant_distribution</name>\n      <anchorfile>cpp/numeric/random/piecewise_constant_distribution/piecewise_constant_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/piecewise_constant_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::piecewise_construct_t</name>\n    <filename>cpp/utility/piecewise_construct_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::piecewise_linear_distribution</name>\n    <filename>cpp/numeric/random/piecewise_linear_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>densities</name>\n      <anchorfile>cpp/numeric/random/piecewise_linear_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>intervals</name>\n      <anchorfile>cpp/numeric/random/piecewise_linear_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/piecewise_linear_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/piecewise_linear_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/piecewise_linear_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/piecewise_linear_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>piecewise_linear_distribution</name>\n      <anchorfile>cpp/numeric/random/piecewise_linear_distribution/piecewise_linear_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/piecewise_linear_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::placeholders</name>\n    <filename>cpp/utility/functional/placeholders</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::plus</name>\n    <filename>cpp/utility/functional/plus</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/plus</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::pmr</name>\n    <filename></filename>\n    <class kind=\"class\">std::pmr::deque</class>\n    <class kind=\"class\">std::pmr::forward_list</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_default_resource</name>\n      <anchorfile>cpp/memory/get_default_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::pmr::list</class>\n    <class kind=\"class\">std::pmr::map</class>\n    <class kind=\"class\">std::pmr::memory_resource</class>\n    <class kind=\"class\">std::pmr::monotonic_buffer_resource</class>\n    <class kind=\"class\">std::pmr::multimap</class>\n    <class kind=\"class\">std::pmr::multiset</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>new_delete_resource</name>\n      <anchorfile>cpp/memory/new_delete_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>null_memory_resource</name>\n      <anchorfile>cpp/memory/null_memory_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::pmr::polymorphic_allocator</class>\n    <class kind=\"class\">std::pmr::pool_options</class>\n    <class kind=\"class\">std::pmr::set</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_default_resource</name>\n      <anchorfile>cpp/memory/set_default_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::pmr::string</class>\n    <class kind=\"class\">std::pmr::synchronized_pool_resource</class>\n    <class kind=\"class\">std::pmr::u16string</class>\n    <class kind=\"class\">std::pmr::u32string</class>\n    <class kind=\"class\">std::pmr::unordered_map</class>\n    <class kind=\"class\">std::pmr::unordered_multimap</class>\n    <class kind=\"class\">std::pmr::unordered_multiset</class>\n    <class kind=\"class\">std::pmr::unordered_set</class>\n    <class kind=\"class\">std::pmr::unsynchronized_pool_resource</class>\n    <class kind=\"class\">std::pmr::vector</class>\n    <class kind=\"class\">std::pmr::wstring</class>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::deque</name>\n    <filename>cpp/container/deque</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/container/deque/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/deque/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/container/deque/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/deque/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/deque/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/deque/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/deque/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/deque/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/deque/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deque</name>\n      <anchorfile>cpp/container/deque/deque</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/deque/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_back</name>\n      <anchorfile>cpp/container/deque/emplace_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_front</name>\n      <anchorfile>cpp/container/deque/emplace_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/deque/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/deque/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/deque/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/deque/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/deque/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/deque/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/deque/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/deque/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/deque/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/container/deque/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_front</name>\n      <anchorfile>cpp/container/deque/pop_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/container/deque/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_front</name>\n      <anchorfile>cpp/container/deque/push_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/deque/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/deque/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/container/deque/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/container/deque/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/deque/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/deque/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~deque</name>\n      <anchorfile>cpp/container/deque/~deque</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::forward_list</name>\n    <filename>cpp/container/forward_list</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/container/forward_list/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>before_begin</name>\n      <anchorfile>cpp/container/forward_list/before_begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/forward_list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbefore_begin</name>\n      <anchorfile>cpp/container/forward_list/before_begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/forward_list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/forward_list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/forward_list/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_after</name>\n      <anchorfile>cpp/container/forward_list/emplace_after</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_front</name>\n      <anchorfile>cpp/container/forward_list/emplace_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/forward_list/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/forward_list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase_after</name>\n      <anchorfile>cpp/container/forward_list/erase_after</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>forward_list</name>\n      <anchorfile>cpp/container/forward_list/forward_list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/forward_list/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/forward_list/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert_after</name>\n      <anchorfile>cpp/container/forward_list/insert_after</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/forward_list/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/forward_list/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/forward_list/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_front</name>\n      <anchorfile>cpp/container/forward_list/pop_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_front</name>\n      <anchorfile>cpp/container/forward_list/push_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove</name>\n      <anchorfile>cpp/container/forward_list/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_if</name>\n      <anchorfile>cpp/container/forward_list/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/container/forward_list/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reverse</name>\n      <anchorfile>cpp/container/forward_list/reverse</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sort</name>\n      <anchorfile>cpp/container/forward_list/sort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>splice_after</name>\n      <anchorfile>cpp/container/forward_list/splice_after</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/forward_list/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique</name>\n      <anchorfile>cpp/container/forward_list/unique</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~forward_list</name>\n      <anchorfile>cpp/container/forward_list/~forward_list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::list</name>\n    <filename>cpp/container/list</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/container/list/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/container/list/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/list/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/list/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/list/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/list/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/list/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_back</name>\n      <anchorfile>cpp/container/list/emplace_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_front</name>\n      <anchorfile>cpp/container/list/emplace_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/list/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/list/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/list/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/list/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/list/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/list/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>list</name>\n      <anchorfile>cpp/container/list/list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/list/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/list/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/list/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/container/list/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_front</name>\n      <anchorfile>cpp/container/list/pop_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/container/list/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_front</name>\n      <anchorfile>cpp/container/list/push_front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/list/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove</name>\n      <anchorfile>cpp/container/list/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_if</name>\n      <anchorfile>cpp/container/list/remove</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/list/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/container/list/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reverse</name>\n      <anchorfile>cpp/container/list/reverse</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/list/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sort</name>\n      <anchorfile>cpp/container/list/sort</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>splice</name>\n      <anchorfile>cpp/container/list/splice</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/list/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique</name>\n      <anchorfile>cpp/container/list/unique</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~list</name>\n      <anchorfile>cpp/container/list/~list</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::map</name>\n    <filename>cpp/container/map</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/map/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/map/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/map/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/map/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/map/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/map/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/map/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/map/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/map/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/map/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/map/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/map/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/map/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/map/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/map/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/map/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/map/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/map/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert_or_assign</name>\n      <anchorfile>cpp/container/map/insert_or_assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_comp</name>\n      <anchorfile>cpp/container/map/key_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/container/map/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>map</name>\n      <anchorfile>cpp/container/map/map</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/map/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/map/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/map/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/map/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/map/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/map/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/map/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/map/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_emplace</name>\n      <anchorfile>cpp/container/map/try_emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/container/map/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_comp</name>\n      <anchorfile>cpp/container/map/value_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::pmr::map::value_compare</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~map</name>\n      <anchorfile>cpp/container/map/~map</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::map::value_compare</name>\n    <filename>cpp/container/map/value_compare</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::memory_resource</name>\n    <filename>cpp/memory/memory_resource</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/memory/memory_resource/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/memory/memory_resource/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_allocate</name>\n      <anchorfile>cpp/memory/memory_resource/do_allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_deallocate</name>\n      <anchorfile>cpp/memory/memory_resource/do_deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_is_equal</name>\n      <anchorfile>cpp/memory/memory_resource/do_is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_equal</name>\n      <anchorfile>cpp/memory/memory_resource/is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>memory_resource</name>\n      <anchorfile>cpp/memory/memory_resource/memory_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::monotonic_buffer_resource</name>\n    <filename>cpp/memory/monotonic_buffer_resource</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/memory/memory_resource/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/memory/memory_resource/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_allocate</name>\n      <anchorfile>cpp/memory/memory_resource/do_allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_deallocate</name>\n      <anchorfile>cpp/memory/memory_resource/do_deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_is_equal</name>\n      <anchorfile>cpp/memory/memory_resource/do_is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_equal</name>\n      <anchorfile>cpp/memory/memory_resource/is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>monotonic_buffer_resource</name>\n      <anchorfile>cpp/memory/monotonic_buffer_resource/monotonic_buffer_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>release</name>\n      <anchorfile>cpp/memory/monotonic_buffer_resource/release</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upstream_resource</name>\n      <anchorfile>cpp/memory/monotonic_buffer_resource/upstream_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~monotonic_buffer_resource</name>\n      <anchorfile>cpp/memory/monotonic_buffer_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::multimap</name>\n    <filename>cpp/container/multimap</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/multimap/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/multimap/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/multimap/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/multimap/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/multimap/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/multimap/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/multimap/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/multimap/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/multimap/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/multimap/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/multimap/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/multimap/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/multimap/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/multimap/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/multimap/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/multimap/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/multimap/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_comp</name>\n      <anchorfile>cpp/container/multimap/key_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/container/multimap/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/multimap/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/multimap/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>multimap</name>\n      <anchorfile>cpp/container/multimap/multimap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/multimap/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/multimap/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/multimap/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/multimap/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/multimap/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/container/multimap/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_comp</name>\n      <anchorfile>cpp/container/multimap/value_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::pmr::multimap::value_compare</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~multimap</name>\n      <anchorfile>cpp/container/multimap/~multimap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::multimap::value_compare</name>\n    <filename>cpp/container/multimap/value_compare</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::multiset</name>\n    <filename>cpp/container/multiset</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/multiset/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/multiset/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/multiset/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/multiset/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/multiset/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/multiset/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/multiset/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/multiset/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/multiset/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/multiset/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/multiset/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/multiset/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/multiset/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/multiset/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/multiset/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/multiset/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/multiset/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_comp</name>\n      <anchorfile>cpp/container/multiset/key_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/container/multiset/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/multiset/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/multiset/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>multiset</name>\n      <anchorfile>cpp/container/multiset/multiset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/multiset/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/multiset/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/multiset/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/multiset/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/multiset/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/container/multiset/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_comp</name>\n      <anchorfile>cpp/container/multiset/value_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~multiset</name>\n      <anchorfile>cpp/container/multiset/~multiset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::polymorphic_allocator</name>\n    <filename>cpp/memory/polymorphic_allocator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/memory/polymorphic_allocator/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>construct</name>\n      <anchorfile>cpp/memory/polymorphic_allocator/construct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/memory/polymorphic_allocator/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>destroy</name>\n      <anchorfile>cpp/memory/polymorphic_allocator/destroy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>polymorphic_allocator</name>\n      <anchorfile>cpp/memory/polymorphic_allocator/polymorphic_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resource</name>\n      <anchorfile>cpp/memory/polymorphic_allocator/resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>select_on_container_copy_contruction</name>\n      <anchorfile>cpp/memory/polymorphic_allocator/select_on_container_copy_contruction</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~polymorphic_allocator</name>\n      <anchorfile>cpp/memory/polymorphic_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::pool_options</name>\n    <filename>cpp/memory/pool_options</filename>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>largest_required_pool_block</name>\n      <anchorfile>cpp/memory/pool_options</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n    <member kind=\"variable\">\n      <type>T</type>\n      <name>max_blocks_per_chunk</name>\n      <anchorfile>cpp/memory/pool_options</anchorfile>\n      <anchor></anchor>\n      <arglist></arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::set</name>\n    <filename>cpp/container/set</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/set/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/set/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/set/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/set/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/set/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/set/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/set/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/set/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/set/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/set/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/set/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/set/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/set/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/set/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/set/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/set/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/set/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_comp</name>\n      <anchorfile>cpp/container/set/key_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/container/set/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/set/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/set/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/set/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/set/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/set/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set</name>\n      <anchorfile>cpp/container/set/set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/set/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/set/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/container/set/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_comp</name>\n      <anchorfile>cpp/container/set/value_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~set</name>\n      <anchorfile>cpp/container/set/~set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::string</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>string</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::synchronized_pool_resource</name>\n    <filename>cpp/memory/synchronized_pool_resource</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/memory/memory_resource/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/memory/memory_resource/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_allocate</name>\n      <anchorfile>cpp/memory/memory_resource/do_allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_deallocate</name>\n      <anchorfile>cpp/memory/memory_resource/do_deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_is_equal</name>\n      <anchorfile>cpp/memory/memory_resource/do_is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_equal</name>\n      <anchorfile>cpp/memory/memory_resource/is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>options</name>\n      <anchorfile>cpp/memory/synchronized_pool_resource/options</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>release</name>\n      <anchorfile>cpp/memory/synchronized_pool_resource/release</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>synchronized_pool_resource</name>\n      <anchorfile>cpp/memory/synchronized_pool_resource/synchronized_pool_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upstream_resource</name>\n      <anchorfile>cpp/memory/synchronized_pool_resource/upstream_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~synchronized_pool_resource</name>\n      <anchorfile>cpp/memory/synchronized_pool_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::u16string</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u16string</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::u32string</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u32string</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::unordered_map</name>\n    <filename>cpp/container/unordered_map</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/unordered_map/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/unordered_map/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin(int)</name>\n      <anchorfile>cpp/container/unordered_map/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket</name>\n      <anchorfile>cpp/container/unordered_map/bucket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_count</name>\n      <anchorfile>cpp/container/unordered_map/bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_size</name>\n      <anchorfile>cpp/container/unordered_map/bucket_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/unordered_map/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin(int)</name>\n      <anchorfile>cpp/container/unordered_map/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/unordered_map/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend(int)</name>\n      <anchorfile>cpp/container/unordered_map/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/unordered_map/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/unordered_map/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/unordered_map/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/unordered_map/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/unordered_map/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/unordered_map/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end(int)</name>\n      <anchorfile>cpp/container/unordered_map/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/unordered_map/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/unordered_map/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/unordered_map/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/unordered_map/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/unordered_map/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_function</name>\n      <anchorfile>cpp/container/unordered_map/hash_function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/unordered_map/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert_or_assign</name>\n      <anchorfile>cpp/container/unordered_map/insert_or_assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_eq</name>\n      <anchorfile>cpp/container/unordered_map/key_eq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load_factor</name>\n      <anchorfile>cpp/container/unordered_map/load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_bucket_count</name>\n      <anchorfile>cpp/container/unordered_map/max_bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_load_factor</name>\n      <anchorfile>cpp/container/unordered_map/max_load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/unordered_map/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/unordered_map/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/unordered_map/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/unordered_map/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rehash</name>\n      <anchorfile>cpp/container/unordered_map/rehash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/unordered_map/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/unordered_map/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/unordered_map/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_emplace</name>\n      <anchorfile>cpp/container/unordered_map/try_emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unordered_map</name>\n      <anchorfile>cpp/container/unordered_map/unordered_map</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unordered_map</name>\n      <anchorfile>cpp/container/unordered_map/~unordered_map</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::unordered_multimap</name>\n    <filename>cpp/container/unordered_multimap</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/unordered_multimap/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin(int)</name>\n      <anchorfile>cpp/container/unordered_multimap/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket</name>\n      <anchorfile>cpp/container/unordered_multimap/bucket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_count</name>\n      <anchorfile>cpp/container/unordered_multimap/bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_size</name>\n      <anchorfile>cpp/container/unordered_multimap/bucket_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/unordered_multimap/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin(int)</name>\n      <anchorfile>cpp/container/unordered_multimap/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/unordered_multimap/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend(int)</name>\n      <anchorfile>cpp/container/unordered_multimap/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/unordered_multimap/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/unordered_multimap/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/unordered_multimap/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/unordered_multimap/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/unordered_multimap/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/unordered_multimap/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end(int)</name>\n      <anchorfile>cpp/container/unordered_multimap/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/unordered_multimap/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/unordered_multimap/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/unordered_multimap/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/unordered_multimap/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/unordered_multimap/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_function</name>\n      <anchorfile>cpp/container/unordered_multimap/hash_function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/unordered_multimap/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_eq</name>\n      <anchorfile>cpp/container/unordered_multimap/key_eq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load_factor</name>\n      <anchorfile>cpp/container/unordered_multimap/load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_bucket_count</name>\n      <anchorfile>cpp/container/unordered_multimap/max_bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_load_factor</name>\n      <anchorfile>cpp/container/unordered_multimap/max_load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/unordered_multimap/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/unordered_multimap/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/unordered_multimap/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rehash</name>\n      <anchorfile>cpp/container/unordered_multimap/rehash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/unordered_multimap/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/unordered_multimap/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/unordered_multimap/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unordered_multimap</name>\n      <anchorfile>cpp/container/unordered_multimap/unordered_multimap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unordered_multimap</name>\n      <anchorfile>cpp/container/unordered_multimap/~unordered_multimap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::unordered_multiset</name>\n    <filename>cpp/container/unordered_multiset</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/unordered_multiset/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin(int)</name>\n      <anchorfile>cpp/container/unordered_multiset/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket</name>\n      <anchorfile>cpp/container/unordered_multiset/bucket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_count</name>\n      <anchorfile>cpp/container/unordered_multiset/bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_size</name>\n      <anchorfile>cpp/container/unordered_multiset/bucket_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/unordered_multiset/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin(int)</name>\n      <anchorfile>cpp/container/unordered_multiset/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/unordered_multiset/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend(int)</name>\n      <anchorfile>cpp/container/unordered_multiset/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/unordered_multiset/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/unordered_multiset/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/unordered_multiset/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/unordered_multiset/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/unordered_multiset/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/unordered_multiset/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end(int)</name>\n      <anchorfile>cpp/container/unordered_multiset/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/unordered_multiset/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/unordered_multiset/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/unordered_multiset/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/unordered_multiset/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/unordered_multiset/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_function</name>\n      <anchorfile>cpp/container/unordered_multiset/hash_function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/unordered_multiset/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_eq</name>\n      <anchorfile>cpp/container/unordered_multiset/key_eq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load_factor</name>\n      <anchorfile>cpp/container/unordered_multiset/load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_bucket_count</name>\n      <anchorfile>cpp/container/unordered_multiset/max_bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_load_factor</name>\n      <anchorfile>cpp/container/unordered_multiset/max_load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/unordered_multiset/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/unordered_multiset/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/unordered_multiset/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rehash</name>\n      <anchorfile>cpp/container/unordered_multiset/rehash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/unordered_multiset/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/unordered_multiset/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/unordered_multiset/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unordered_multiset</name>\n      <anchorfile>cpp/container/unordered_multiset/unordered_multiset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unordered_multiset</name>\n      <anchorfile>cpp/container/unordered_multiset/~unordered_multiset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::unordered_set</name>\n    <filename>cpp/container/unordered_set</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/unordered_set/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin(int)</name>\n      <anchorfile>cpp/container/unordered_set/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket</name>\n      <anchorfile>cpp/container/unordered_set/bucket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_count</name>\n      <anchorfile>cpp/container/unordered_set/bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_size</name>\n      <anchorfile>cpp/container/unordered_set/bucket_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/unordered_set/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin(int)</name>\n      <anchorfile>cpp/container/unordered_set/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/unordered_set/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend(int)</name>\n      <anchorfile>cpp/container/unordered_set/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/unordered_set/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/unordered_set/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/unordered_set/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/unordered_set/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/unordered_set/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/unordered_set/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end(int)</name>\n      <anchorfile>cpp/container/unordered_set/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/unordered_set/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/unordered_set/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/unordered_set/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/unordered_set/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/unordered_set/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_function</name>\n      <anchorfile>cpp/container/unordered_set/hash_function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/unordered_set/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_eq</name>\n      <anchorfile>cpp/container/unordered_set/key_eq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load_factor</name>\n      <anchorfile>cpp/container/unordered_set/load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_bucket_count</name>\n      <anchorfile>cpp/container/unordered_set/max_bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_load_factor</name>\n      <anchorfile>cpp/container/unordered_set/max_load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/unordered_set/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/unordered_set/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/unordered_set/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rehash</name>\n      <anchorfile>cpp/container/unordered_set/rehash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/unordered_set/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/unordered_set/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/unordered_set/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unordered_set</name>\n      <anchorfile>cpp/container/unordered_set/unordered_set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unordered_set</name>\n      <anchorfile>cpp/container/unordered_set/~unordered_set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::unsynchronized_pool_resource</name>\n    <filename>cpp/memory/unsynchronized_pool_resource</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/memory/memory_resource/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/memory/memory_resource/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_allocate</name>\n      <anchorfile>cpp/memory/memory_resource/do_allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_deallocate</name>\n      <anchorfile>cpp/memory/memory_resource/do_deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_is_equal</name>\n      <anchorfile>cpp/memory/memory_resource/do_is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_equal</name>\n      <anchorfile>cpp/memory/memory_resource/is_equal</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>options</name>\n      <anchorfile>cpp/memory/unsynchronized_pool_resource/options</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>release</name>\n      <anchorfile>cpp/memory/unsynchronized_pool_resource/release</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsynchronized_pool_resource</name>\n      <anchorfile>cpp/memory/unsynchronized_pool_resource/unsynchronized_pool_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upstream_resource</name>\n      <anchorfile>cpp/memory/unsynchronized_pool_resource/upstream_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unsynchronized_pool_resource</name>\n      <anchorfile>cpp/memory/unsynchronized_pool_resource</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::vector</name>\n    <filename>cpp/container/vector</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/container/vector/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/vector/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/container/vector/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/vector/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/container/vector/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/vector/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/vector/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/vector/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/vector/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/vector/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/container/vector/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/vector/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_back</name>\n      <anchorfile>cpp/container/vector/emplace_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/vector/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/vector/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/vector/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/vector/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/vector/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/vector/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/vector/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/vector/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/vector/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/container/vector/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/container/vector/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/vector/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/vector/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/vector/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/container/vector/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/container/vector/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/vector/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/vector/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vector</name>\n      <anchorfile>cpp/container/vector/vector</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~vector</name>\n      <anchorfile>cpp/container/vector/~vector</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pmr::wstring</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstring</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pointer_safety</name>\n    <filename>cpp/memory/gc/pointer_safety</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::pointer_traits</name>\n    <filename>cpp/memory/pointer_traits</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pointer_to</name>\n      <anchorfile>cpp/memory/pointer_traits/pointer_to</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::poisson_distribution</name>\n    <filename>cpp/numeric/random/poisson_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/poisson_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mean</name>\n      <anchorfile>cpp/numeric/random/poisson_distribution/mean</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/poisson_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/poisson_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>poisson_distribution</name>\n      <anchorfile>cpp/numeric/random/poisson_distribution/poisson_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/poisson_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::priority_queue</name>\n    <filename>cpp/container/priority_queue</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/priority_queue/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/priority_queue/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/priority_queue/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop</name>\n      <anchorfile>cpp/container/priority_queue/pop</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>priority_queue</name>\n      <anchorfile>cpp/container/priority_queue/priority_queue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push</name>\n      <anchorfile>cpp/container/priority_queue/push</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/priority_queue/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/priority_queue/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>top</name>\n      <anchorfile>cpp/container/priority_queue/top</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~priority_queue</name>\n      <anchorfile>cpp/container/priority_queue/~priority_queue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::promise</name>\n    <filename>cpp/thread/promise</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_future</name>\n      <anchorfile>cpp/thread/promise/get_future</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/thread/promise/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>promise</name>\n      <anchorfile>cpp/thread/promise/promise</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_exception</name>\n      <anchorfile>cpp/thread/promise/set_exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_exception_at_thread_exit</name>\n      <anchorfile>cpp/thread/promise/set_exception_at_thread_exit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_value</name>\n      <anchorfile>cpp/thread/promise/set_value</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_value_at_thread_exit</name>\n      <anchorfile>cpp/thread/promise/set_value_at_thread_exit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/thread/promise/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~promise</name>\n      <anchorfile>cpp/thread/promise/~promise</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ptrdiff_t</name>\n    <filename>cpp/types/ptrdiff_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::queue</name>\n    <filename>cpp/container/queue</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/container/queue/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/queue/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/queue/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/queue/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/queue/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop</name>\n      <anchorfile>cpp/container/queue/pop</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push</name>\n      <anchorfile>cpp/container/queue/push</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>queue</name>\n      <anchorfile>cpp/container/queue/queue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/queue/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/queue/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~queue</name>\n      <anchorfile>cpp/container/queue/~queue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::random_access_iterator_tag</name>\n    <filename>cpp/iterator/iterator_tags</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::random_device</name>\n    <filename>cpp/numeric/random/random_device</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>entropy</name>\n      <anchorfile>cpp/numeric/random/random_device/entropy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/random_device/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/random_device/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/random_device/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>random_device</name>\n      <anchorfile>cpp/numeric/random/random_device/random_device</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::range_error</name>\n    <filename>cpp/error/range_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>range_error</name>\n      <anchorfile>cpp/error/range_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::rank</name>\n    <filename>cpp/types/rank</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ranlux24</name>\n    <filename>cpp/numeric/random/discard_block_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>base</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/base</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ranlux24</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/discard_block_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ranlux24_base</name>\n    <filename>cpp/numeric/random/subtract_with_carry_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ranlux24_base</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/subtract_with_carry_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ranlux48</name>\n    <filename>cpp/numeric/random/discard_block_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>base</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/base</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ranlux48</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/discard_block_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/discard_block_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ranlux48_base</name>\n    <filename>cpp/numeric/random/subtract_with_carry_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ranlux48_base</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/subtract_with_carry_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_add</name>\n    <filename>cpp/numeric/ratio/ratio_add</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_divide</name>\n    <filename>cpp/numeric/ratio/ratio_divide</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_equal</name>\n    <filename>cpp/numeric/ratio/ratio_equal</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_greater</name>\n    <filename>cpp/numeric/ratio/ratio_greater</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_greater_equal</name>\n    <filename>cpp/numeric/ratio/ratio_greater_equal</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_less</name>\n    <filename>cpp/numeric/ratio/ratio_less</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_less_equal</name>\n    <filename>cpp/numeric/ratio/ratio_less_equal</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_multiply</name>\n    <filename>cpp/numeric/ratio/ratio_multiply</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_not_equal</name>\n    <filename>cpp/numeric/ratio/ratio_not_equal</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ratio_subtract</name>\n    <filename>cpp/numeric/ratio/ratio_subtract</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::raw_storage_iterator</name>\n    <filename>cpp/memory/raw_storage_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/memory/raw_storage_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/memory/raw_storage_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/memory/raw_storage_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>raw_storage_iterator</name>\n      <anchorfile>cpp/memory/raw_storage_iterator/raw_storage_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::recursive_mutex</name>\n    <filename>cpp/thread/recursive_mutex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/recursive_mutex/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native_handle</name>\n      <anchorfile>cpp/thread/recursive_mutex/native_handle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>recursive_mutex</name>\n      <anchorfile>cpp/thread/recursive_mutex/recursive_mutex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/recursive_mutex/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock</name>\n      <anchorfile>cpp/thread/recursive_mutex/unlock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::recursive_timed_mutex</name>\n    <filename>cpp/thread/recursive_timed_mutex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/recursive_timed_mutex/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native_handle</name>\n      <anchorfile>cpp/thread/recursive_timed_mutex/native_handle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>recursive_timed_mutex</name>\n      <anchorfile>cpp/thread/recursive_timed_mutex/recursive_timed_mutex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/recursive_timed_mutex/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_for</name>\n      <anchorfile>cpp/thread/recursive_timed_mutex/try_lock_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_until</name>\n      <anchorfile>cpp/thread/recursive_timed_mutex/try_lock_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock</name>\n      <anchorfile>cpp/thread/recursive_timed_mutex/unlock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::reference_wrapper</name>\n    <filename>cpp/utility/functional/reference_wrapper</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/utility/functional/reference_wrapper/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator T&amp;</name>\n      <anchorfile>cpp/utility/functional/reference_wrapper/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/reference_wrapper/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/utility/functional/reference_wrapper/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reference_wrapper</name>\n      <anchorfile>cpp/utility/functional/reference_wrapper/reference_wrapper</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::regex</name>\n    <filename>cpp/regex/basic_regex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/regex/basic_regex/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/regex/basic_regex/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/regex/basic_regex/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/regex/basic_regex/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mark_count</name>\n      <anchorfile>cpp/regex/basic_regex/mark_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/basic_regex/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>regex</name>\n      <anchorfile>cpp/regex/basic_regex/basic_regex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/regex/basic_regex/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~regex</name>\n      <anchorfile>cpp/regex/basic_regex/~basic_regex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::regex_constants</name>\n    <filename></filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::regex_error</name>\n    <filename>cpp/regex/regex_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>code</name>\n      <anchorfile>cpp/regex/regex_error/code</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>regex_error</name>\n      <anchorfile>cpp/regex/regex_error/regex_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::regex_iterator</name>\n    <filename>cpp/regex/regex_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>regex_iterator</name>\n      <anchorfile>cpp/regex/regex_iterator/regex_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::regex_token_iterator</name>\n    <filename>cpp/regex/regex_token_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>regex_token_iterator</name>\n      <anchorfile>cpp/regex/regex_token_iterator/regex_token_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::regex_traits</name>\n    <filename>cpp/regex/regex_traits</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/regex/regex_traits/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/regex/regex_traits/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>isctype</name>\n      <anchorfile>cpp/regex/regex_traits/isctype</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/regex_traits/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lookup_classname</name>\n      <anchorfile>cpp/regex/regex_traits/lookup_classname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lookup_collatename</name>\n      <anchorfile>cpp/regex/regex_traits/lookup_collatename</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>regex_traits</name>\n      <anchorfile>cpp/regex/regex_traits/regex_traits</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>transform</name>\n      <anchorfile>cpp/regex/regex_traits/transform</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>transform_primary</name>\n      <anchorfile>cpp/regex/regex_traits/transform_primary</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>translate</name>\n      <anchorfile>cpp/regex/regex_traits/translate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>translate_nocase</name>\n      <anchorfile>cpp/regex/regex_traits/translate_nocase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value</name>\n      <anchorfile>cpp/regex/regex_traits/value</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::rel_ops</name>\n    <filename></filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/utility/rel_ops/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;=</name>\n      <anchorfile>cpp/utility/rel_ops/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;</name>\n      <anchorfile>cpp/utility/rel_ops/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;=</name>\n      <anchorfile>cpp/utility/rel_ops/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_all_extents</name>\n    <filename>cpp/types/remove_all_extents</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_all_extents_t</name>\n    <filename>cpp/types/remove_all_extents</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_const</name>\n    <filename>cpp/types/remove_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_const_t</name>\n    <filename>cpp/types/remove_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_cv</name>\n    <filename>cpp/types/remove_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_cv_t</name>\n    <filename>cpp/types/remove_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_extent</name>\n    <filename>cpp/types/remove_extent</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_extent_t</name>\n    <filename>cpp/types/remove_extent</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_pointer</name>\n    <filename>cpp/types/remove_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_pointer_t</name>\n    <filename>cpp/types/remove_pointer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_reference</name>\n    <filename>cpp/types/remove_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_reference_t</name>\n    <filename>cpp/types/remove_reference</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_volatile</name>\n    <filename>cpp/types/remove_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::remove_volatile_t</name>\n    <filename>cpp/types/remove_cv</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::result_of</name>\n    <filename>cpp/types/result_of</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::result_of_t</name>\n    <filename>cpp/types/result_of</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::reverse_iterator</name>\n    <filename>cpp/iterator/reverse_iterator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::runtime_error</name>\n    <filename>cpp/error/runtime_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>runtime_error</name>\n      <anchorfile>cpp/error/runtime_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::scoped_allocator_adaptor</name>\n    <filename>cpp/memory/scoped_allocator_adaptor</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>allocate</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/allocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>construct</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/construct</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>deallocate</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/deallocate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>destroy</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/destroy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>inner_allocator</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/inner_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>outer_allocator</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/outer_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>scoped_allocator_adaptor</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/scoped_allocator_adaptor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>select_on_container_copy_construction</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/select_on_container_copy_construction</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~scoped_allocator_adaptor</name>\n      <anchorfile>cpp/memory/scoped_allocator_adaptor/~scoped_allocator_adaptor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::seed_seq</name>\n    <filename>cpp/numeric/random/seed_seq</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>generate</name>\n      <anchorfile>cpp/numeric/random/seed_seq/generate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/seed_seq/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed_seq</name>\n      <anchorfile>cpp/numeric/random/seed_seq/seed_seq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/numeric/random/seed_seq/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::set</name>\n    <filename>cpp/container/set</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/set/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/set/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/set/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/set/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/set/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/set/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/set/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/set/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/set/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/set/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/set/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/set/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/set/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/set/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/set/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/set/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/set/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_comp</name>\n      <anchorfile>cpp/container/set/key_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lower_bound</name>\n      <anchorfile>cpp/container/set/lower_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/set/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/set/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/set/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/set/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/set/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set</name>\n      <anchorfile>cpp/container/set/set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/set/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/set/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>upper_bound</name>\n      <anchorfile>cpp/container/set/upper_bound</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>value_comp</name>\n      <anchorfile>cpp/container/set/value_comp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~set</name>\n      <anchorfile>cpp/container/set/~set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::shared_future</name>\n    <filename>cpp/thread/shared_future</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/thread/shared_future/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/thread/shared_future/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shared_future</name>\n      <anchorfile>cpp/thread/shared_future/shared_future</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>valid</name>\n      <anchorfile>cpp/thread/shared_future/valid</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait</name>\n      <anchorfile>cpp/thread/shared_future/wait</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait_for</name>\n      <anchorfile>cpp/thread/shared_future/wait_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wait_until</name>\n      <anchorfile>cpp/thread/shared_future/wait_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~shared_future</name>\n      <anchorfile>cpp/thread/shared_future/~shared_future</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::shared_lock</name>\n    <filename>cpp/thread/shared_lock</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/shared_lock/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mutex</name>\n      <anchorfile>cpp/thread/shared_lock/mutex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/thread/shared_lock/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/thread/shared_lock/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>owns_lock</name>\n      <anchorfile>cpp/thread/shared_lock/owns_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>release</name>\n      <anchorfile>cpp/thread/shared_lock/release</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shared_lock</name>\n      <anchorfile>cpp/thread/shared_lock/shared_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/thread/shared_lock/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/shared_lock/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_for</name>\n      <anchorfile>cpp/thread/shared_lock/try_lock_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_until</name>\n      <anchorfile>cpp/thread/shared_lock/try_lock_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock</name>\n      <anchorfile>cpp/thread/shared_lock/unlock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~shared_lock</name>\n      <anchorfile>cpp/thread/shared_lock/~shared_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::shared_mutex</name>\n    <filename>cpp/thread/shared_mutex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/shared_mutex/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock_shared</name>\n      <anchorfile>cpp/thread/shared_mutex/lock_shared</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native_handle</name>\n      <anchorfile>cpp/thread/shared_mutex/native_handle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shared_mutex</name>\n      <anchorfile>cpp/thread/shared_mutex/shared_mutex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/shared_mutex/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_shared</name>\n      <anchorfile>cpp/thread/shared_mutex/try_lock_shared</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock</name>\n      <anchorfile>cpp/thread/shared_mutex/unlock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock_shared</name>\n      <anchorfile>cpp/thread/shared_mutex/unlock_shared</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::shared_ptr</name>\n    <filename>cpp/memory/shared_ptr</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/memory/shared_ptr/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/memory/shared_ptr/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/memory/shared_ptr/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/memory/shared_ptr/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/memory/shared_ptr/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/memory/shared_ptr/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>owner_before</name>\n      <anchorfile>cpp/memory/shared_ptr/owner_before</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/memory/shared_ptr/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shared_ptr</name>\n      <anchorfile>cpp/memory/shared_ptr/shared_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/memory/shared_ptr/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique</name>\n      <anchorfile>cpp/memory/shared_ptr/unique</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>use_count</name>\n      <anchorfile>cpp/memory/shared_ptr/use_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~shared_ptr</name>\n      <anchorfile>cpp/memory/shared_ptr/~shared_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::shared_timed_mutex</name>\n    <filename>cpp/thread/shared_timed_mutex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock_shared</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/lock_shared</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shared_timed_mutex</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/shared_timed_mutex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_for</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/try_lock_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_shared</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/try_lock_shared</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_shared_for</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/try_lock_shared_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_shared_until</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/try_lock_shared_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_until</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/try_lock_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/unlock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock_shared</name>\n      <anchorfile>cpp/thread/shared_timed_mutex/unlock_shared</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::shuffle_order_engine</name>\n    <filename>cpp/numeric/random/shuffle_order_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>base</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/base</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shuffle_order_engine</name>\n      <anchorfile>cpp/numeric/random/shuffle_order_engine/shuffle_order_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::sig_atomic_t</name>\n    <filename>cpp/utility/program/sig_atomic_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::size_t</name>\n    <filename>cpp/types/size_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::slice</name>\n    <filename>cpp/numeric/valarray/slice</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::slice_array</name>\n    <filename>cpp/numeric/valarray/slice_array</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::smatch</name>\n    <filename>cpp/regex/match_results</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/regex/match_results/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>format</name>\n      <anchorfile>cpp/regex/match_results/format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/regex/match_results/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/match_results/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/regex/match_results/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/regex/match_results/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>position</name>\n      <anchorfile>cpp/regex/match_results/position</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>prefix</name>\n      <anchorfile>cpp/regex/match_results/prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ready</name>\n      <anchorfile>cpp/regex/match_results/ready</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/regex/match_results/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>smatch</name>\n      <anchorfile>cpp/regex/match_results/match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/match_results/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>suffix</name>\n      <anchorfile>cpp/regex/match_results/suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/regex/match_results/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~smatch</name>\n      <anchorfile>cpp/regex/match_results/~match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::sregex_iterator</name>\n    <filename>cpp/regex/regex_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sregex_iterator</name>\n      <anchorfile>cpp/regex/regex_iterator/regex_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::sregex_token_iterator</name>\n    <filename>cpp/regex/regex_token_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sregex_token_iterator</name>\n      <anchorfile>cpp/regex/regex_token_iterator/regex_token_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::ssub_match</name>\n    <filename>cpp/regex/sub_match</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/regex/sub_match/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/sub_match/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator string_type</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ssub_match</name>\n      <anchorfile>cpp/regex/sub_match/sub_match</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::stack</name>\n    <filename>cpp/container/stack</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/stack/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/stack/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/stack/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop</name>\n      <anchorfile>cpp/container/stack/pop</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push</name>\n      <anchorfile>cpp/container/stack/push</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/stack/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stack</name>\n      <anchorfile>cpp/container/stack/stack</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/stack/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>top</name>\n      <anchorfile>cpp/container/stack/top</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~stack</name>\n      <anchorfile>cpp/container/stack/~stack</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::streambuf</name>\n    <filename>cpp/io/basic_streambuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_streambuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>streambuf</name>\n      <anchorfile>cpp/io/basic_streambuf/basic_streambuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~streambuf</name>\n      <anchorfile>cpp/io/basic_streambuf/~basic_streambuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::streamoff</name>\n    <filename>cpp/io/streamoff</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::streampos</name>\n    <filename>cpp/io/fpos</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>state</name>\n      <anchorfile>cpp/io/fpos/state</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::streamsize</name>\n    <filename>cpp/io/streamsize</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::string</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>string</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::string_view</name>\n    <filename>cpp/string/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>string_view</name>\n      <anchorfile>cpp/string/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::stringbuf</name>\n    <filename>cpp/io/basic_stringbuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_stringbuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_stringbuf/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stringbuf</name>\n      <anchorfile>cpp/io/basic_stringbuf/basic_stringbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::stringstream</name>\n    <filename>cpp/io/basic_stringstream</filename>\n    <class kind=\"class\">std::stringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::stringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::stringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_stringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::stringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_stringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>stringstream</name>\n      <anchorfile>cpp/io/basic_stringstream/basic_stringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::stringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::stringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::stringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::stringstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::strstream</name>\n    <filename>cpp/io/strstream</filename>\n    <class kind=\"class\">std::strstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::strstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::strstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>freeze</name>\n      <anchorfile>cpp/io/strstream/freeze</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pcount</name>\n      <anchorfile>cpp/io/strstream/pcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::strstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/strstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strstream</name>\n      <anchorfile>cpp/io/strstream/strstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~strstream</name>\n      <anchorfile>cpp/io/strstream/~strstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::strstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::strstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::strstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::strstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::strstreambuf</name>\n    <filename>cpp/io/strstreambuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>freeze</name>\n      <anchorfile>cpp/io/strstreambuf/freeze</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pcount</name>\n      <anchorfile>cpp/io/strstreambuf/pcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/strstreambuf/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>strstreambuf</name>\n      <anchorfile>cpp/io/strstreambuf/strstreambuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~strstreambuf</name>\n      <anchorfile>cpp/io/strstreambuf/~strstreambuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::student_t_distribution</name>\n    <filename>cpp/numeric/random/student_t_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/student_t_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/student_t_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>n</name>\n      <anchorfile>cpp/numeric/random/student_t_distribution/n</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/student_t_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/student_t_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/student_t_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>student_t_distribution</name>\n      <anchorfile>cpp/numeric/random/student_t_distribution/student_t_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::sub_match</name>\n    <filename>cpp/regex/sub_match</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/regex/sub_match/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/sub_match/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator string_type</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sub_match</name>\n      <anchorfile>cpp/regex/sub_match/sub_match</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::subtract_with_carry_engine</name>\n    <filename>cpp/numeric/random/subtract_with_carry_engine</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>discard</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/discard</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seed</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/seed</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>subtract_with_carry_engine</name>\n      <anchorfile>cpp/numeric/random/subtract_with_carry_engine/subtract_with_carry_engine</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::system_error</name>\n    <filename>cpp/error/system_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>code</name>\n      <anchorfile>cpp/error/system_error/code</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>system_error</name>\n      <anchorfile>cpp/error/system_error/system_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::tera</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::terminate_handler</name>\n    <filename>cpp/error/terminate_handler</filename>\n  </compound>\n  <compound kind=\"namespace\">\n    <name>std::this_thread</name>\n    <filename></filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_id</name>\n      <anchorfile>cpp/thread/get_id</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sleep_for</name>\n      <anchorfile>cpp/thread/sleep_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sleep_until</name>\n      <anchorfile>cpp/thread/sleep_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>yield</name>\n      <anchorfile>cpp/thread/yield</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::thread</name>\n    <filename>cpp/thread/thread</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>detach</name>\n      <anchorfile>cpp/thread/thread/detach</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_id</name>\n      <anchorfile>cpp/thread/thread/get_id</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hardware_concurrency</name>\n      <anchorfile>cpp/thread/thread/hardware_concurrency</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::thread::id</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>join</name>\n      <anchorfile>cpp/thread/thread/join</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>joinable</name>\n      <anchorfile>cpp/thread/thread/joinable</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native_handle</name>\n      <anchorfile>cpp/thread/thread/native_handle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/thread/thread/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/thread/thread/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>thread</name>\n      <anchorfile>cpp/thread/thread/thread</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~thread</name>\n      <anchorfile>cpp/thread/thread/~thread</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::thread::id</name>\n    <filename>cpp/thread/thread/id</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>id</name>\n      <anchorfile>cpp/thread/thread/id/id</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/thread/thread/id/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;</name>\n      <anchorfile>cpp/thread/thread/id/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/thread/thread/id/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;=</name>\n      <anchorfile>cpp/thread/thread/id/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/thread/thread/id/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;</name>\n      <anchorfile>cpp/thread/thread/id/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;=</name>\n      <anchorfile>cpp/thread/thread/id/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_base</name>\n    <filename>cpp/locale/time_base</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_get</name>\n    <filename>cpp/locale/time_get</filename>\n    <class kind=\"class\">std::time_get::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>date_order</name>\n      <anchorfile>cpp/locale/time_get/date_order</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_date_order</name>\n      <anchorfile>cpp/locale/time_get/date_order</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get</name>\n      <anchorfile>cpp/locale/time_get/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_date</name>\n      <anchorfile>cpp/locale/time_get/get_date</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_monthname</name>\n      <anchorfile>cpp/locale/time_get/get_monthname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_time</name>\n      <anchorfile>cpp/locale/time_get/get_time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_weekday</name>\n      <anchorfile>cpp/locale/time_get/get_weekday</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_year</name>\n      <anchorfile>cpp/locale/time_get/get_year</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/locale/time_get/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_date</name>\n      <anchorfile>cpp/locale/time_get/get_date</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_monthname</name>\n      <anchorfile>cpp/locale/time_get/get_monthname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_time</name>\n      <anchorfile>cpp/locale/time_get/get_time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_weekday</name>\n      <anchorfile>cpp/locale/time_get/get_weekday</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_year</name>\n      <anchorfile>cpp/locale/time_get/get_year</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::time_get::iter_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>time_get</name>\n      <anchorfile>cpp/locale/time_get/time_get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~time_get</name>\n      <anchorfile>cpp/locale/time_get/~time_get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_get::char_type</name>\n    <filename>cpp/locale/time_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_get::iter_type</name>\n    <filename>cpp/locale/time_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_get_byname</name>\n    <filename>cpp/locale/time_get_byname</filename>\n    <class kind=\"class\">std::time_get_byname::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>date_order</name>\n      <anchorfile>cpp/locale/time_get/date_order</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_date_order</name>\n      <anchorfile>cpp/locale/time_get/date_order</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get</name>\n      <anchorfile>cpp/locale/time_get/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_date</name>\n      <anchorfile>cpp/locale/time_get/get_date</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_monthname</name>\n      <anchorfile>cpp/locale/time_get/get_monthname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_time</name>\n      <anchorfile>cpp/locale/time_get/get_time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_weekday</name>\n      <anchorfile>cpp/locale/time_get/get_weekday</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_get_year</name>\n      <anchorfile>cpp/locale/time_get/get_year</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/locale/time_get/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_date</name>\n      <anchorfile>cpp/locale/time_get/get_date</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_monthname</name>\n      <anchorfile>cpp/locale/time_get/get_monthname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_time</name>\n      <anchorfile>cpp/locale/time_get/get_time</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_weekday</name>\n      <anchorfile>cpp/locale/time_get/get_weekday</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_year</name>\n      <anchorfile>cpp/locale/time_get/get_year</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::time_get_byname::iter_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>time_get_byname</name>\n      <anchorfile>cpp/locale/time_get_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~time_get_byname</name>\n      <anchorfile>cpp/locale/time_get_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_get_byname::char_type</name>\n    <filename>cpp/locale/time_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_get_byname::iter_type</name>\n    <filename>cpp/locale/time_get</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_put</name>\n    <filename>cpp/locale/time_put</filename>\n    <class kind=\"class\">std::time_put::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_put</name>\n      <anchorfile>cpp/locale/time_put/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::time_put::iter_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/locale/time_put/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>time_put</name>\n      <anchorfile>cpp/locale/time_put/time_put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~time_put</name>\n      <anchorfile>cpp/locale/time_put/~time_put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_put::char_type</name>\n    <filename>cpp/locale/time_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_put::iter_type</name>\n    <filename>cpp/locale/time_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_put_byname</name>\n    <filename>cpp/locale/time_put_byname</filename>\n    <class kind=\"class\">std::time_put_byname::char_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>do_put</name>\n      <anchorfile>cpp/locale/time_put/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::time_put_byname::iter_type</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/locale/time_put/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>time_put_byname</name>\n      <anchorfile>cpp/locale/time_put_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~time_put_byname</name>\n      <anchorfile>cpp/locale/time_put_byname</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_put_byname::char_type</name>\n    <filename>cpp/locale/time_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_put_byname::iter_type</name>\n    <filename>cpp/locale/time_put</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::time_t</name>\n    <filename>cpp/chrono/c/time_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::timed_mutex</name>\n    <filename>cpp/thread/timed_mutex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/timed_mutex/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>native_handle</name>\n      <anchorfile>cpp/thread/timed_mutex/native_handle</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>timed_mutex</name>\n      <anchorfile>cpp/thread/timed_mutex/timed_mutex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/timed_mutex/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_for</name>\n      <anchorfile>cpp/thread/timed_mutex/try_lock_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_until</name>\n      <anchorfile>cpp/thread/timed_mutex/try_lock_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock</name>\n      <anchorfile>cpp/thread/timed_mutex/unlock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::tm</name>\n    <filename>cpp/chrono/c/tm</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::true_type</name>\n    <filename>cpp/types/integral_constant</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::try_to_lock_t</name>\n    <filename>cpp/thread/lock_tag_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::tuple</name>\n    <filename>cpp/utility/tuple</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/utility/tuple/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/utility/tuple/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tuple</name>\n      <anchorfile>cpp/utility/tuple/tuple</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::type_index</name>\n    <filename>cpp/types/type_index</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_code</name>\n      <anchorfile>cpp/types/type_index/hash_code</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>name</name>\n      <anchorfile>cpp/types/type_index/name</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/types/type_index/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;</name>\n      <anchorfile>cpp/types/type_index/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;=</name>\n      <anchorfile>cpp/types/type_index/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/types/type_index/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;</name>\n      <anchorfile>cpp/types/type_index/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;=</name>\n      <anchorfile>cpp/types/type_index/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>type_index</name>\n      <anchorfile>cpp/types/type_index/type_index</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::type_info</name>\n    <filename>cpp/types/type_info</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>before</name>\n      <anchorfile>cpp/types/type_info/before</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_code</name>\n      <anchorfile>cpp/types/type_info/hash_code</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>name</name>\n      <anchorfile>cpp/types/type_info/name</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/types/type_info/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/types/type_info/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::u16streampos</name>\n    <filename>cpp/io/fpos</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>state</name>\n      <anchorfile>cpp/io/fpos/state</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::u16string</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u16string</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::u16string_view</name>\n    <filename>cpp/string/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u16string_view</name>\n      <anchorfile>cpp/string/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::u32streampos</name>\n    <filename>cpp/io/fpos</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>state</name>\n      <anchorfile>cpp/io/fpos/state</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::u32string</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u32string</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::u32string_view</name>\n    <filename>cpp/string/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>u32string_view</name>\n      <anchorfile>cpp/string/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint16_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint32_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint64_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint8_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint_fast16_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint_fast32_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint_fast64_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint_fast8_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint_least16_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint_least32_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint_least64_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uint_least8_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uintmax_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uintptr_t</name>\n    <filename>cpp/types/integer</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unary_function</name>\n    <filename>cpp/utility/functional/unary_function</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unary_negate</name>\n    <filename>cpp/utility/functional/unary_negate</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/utility/functional/unary_negate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unary_negate</name>\n      <anchorfile>cpp/utility/functional/unary_negate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::underflow_error</name>\n    <filename>cpp/error/underflow_error</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow_error</name>\n      <anchorfile>cpp/error/underflow_error</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::underlying_type</name>\n    <filename>cpp/types/underlying_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::underlying_type_t</name>\n    <filename>cpp/types/underlying_type</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unexpected_handler</name>\n    <filename>cpp/error/unexpected_handler</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uniform_int_distribution</name>\n    <filename>cpp/numeric/random/uniform_int_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>a</name>\n      <anchorfile>cpp/numeric/random/uniform_int_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>b</name>\n      <anchorfile>cpp/numeric/random/uniform_int_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/uniform_int_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/uniform_int_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/uniform_int_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/uniform_int_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/uniform_int_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uniform_int_distribution</name>\n      <anchorfile>cpp/numeric/random/uniform_int_distribution/uniform_int_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uniform_real_distribution</name>\n    <filename>cpp/numeric/random/uniform_real_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>a</name>\n      <anchorfile>cpp/numeric/random/uniform_real_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>b</name>\n      <anchorfile>cpp/numeric/random/uniform_real_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/uniform_real_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/uniform_real_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/uniform_real_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/uniform_real_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uniform_real_distribution</name>\n      <anchorfile>cpp/numeric/random/uniform_real_distribution/uniform_real_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unique_lock</name>\n    <filename>cpp/thread/unique_lock</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/thread/unique_lock/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mutex</name>\n      <anchorfile>cpp/thread/unique_lock/mutex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/thread/unique_lock/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/thread/unique_lock/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>owns_lock</name>\n      <anchorfile>cpp/thread/unique_lock/owns_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>release</name>\n      <anchorfile>cpp/thread/unique_lock/release</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/thread/unique_lock/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock</name>\n      <anchorfile>cpp/thread/unique_lock/try_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_for</name>\n      <anchorfile>cpp/thread/unique_lock/try_lock_for</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_lock_until</name>\n      <anchorfile>cpp/thread/unique_lock/try_lock_until</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique_lock</name>\n      <anchorfile>cpp/thread/unique_lock/unique_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unlock</name>\n      <anchorfile>cpp/thread/unique_lock/unlock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unique_lock</name>\n      <anchorfile>cpp/thread/unique_lock/~unique_lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unique_ptr</name>\n    <filename>cpp/memory/unique_ptr</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/memory/unique_ptr/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_deleter</name>\n      <anchorfile>cpp/memory/unique_ptr/get_deleter</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/memory/unique_ptr/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/memory/unique_ptr/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/memory/unique_ptr/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/memory/unique_ptr/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>release</name>\n      <anchorfile>cpp/memory/unique_ptr/release</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/memory/unique_ptr/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/memory/unique_ptr/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unique_ptr</name>\n      <anchorfile>cpp/memory/unique_ptr/unique_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unique_ptr</name>\n      <anchorfile>cpp/memory/unique_ptr/~unique_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unordered_map</name>\n    <filename>cpp/container/unordered_map</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/unordered_map/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/unordered_map/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin(int)</name>\n      <anchorfile>cpp/container/unordered_map/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket</name>\n      <anchorfile>cpp/container/unordered_map/bucket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_count</name>\n      <anchorfile>cpp/container/unordered_map/bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_size</name>\n      <anchorfile>cpp/container/unordered_map/bucket_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/unordered_map/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin(int)</name>\n      <anchorfile>cpp/container/unordered_map/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/unordered_map/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend(int)</name>\n      <anchorfile>cpp/container/unordered_map/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/unordered_map/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/unordered_map/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/unordered_map/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/unordered_map/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/unordered_map/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/unordered_map/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end(int)</name>\n      <anchorfile>cpp/container/unordered_map/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/unordered_map/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/unordered_map/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/unordered_map/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/unordered_map/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/unordered_map/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_function</name>\n      <anchorfile>cpp/container/unordered_map/hash_function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/unordered_map/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert_or_assign</name>\n      <anchorfile>cpp/container/unordered_map/insert_or_assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_eq</name>\n      <anchorfile>cpp/container/unordered_map/key_eq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load_factor</name>\n      <anchorfile>cpp/container/unordered_map/load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_bucket_count</name>\n      <anchorfile>cpp/container/unordered_map/max_bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_load_factor</name>\n      <anchorfile>cpp/container/unordered_map/max_load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/unordered_map/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/unordered_map/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/unordered_map/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/unordered_map/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rehash</name>\n      <anchorfile>cpp/container/unordered_map/rehash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/unordered_map/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/unordered_map/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/unordered_map/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>try_emplace</name>\n      <anchorfile>cpp/container/unordered_map/try_emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unordered_map</name>\n      <anchorfile>cpp/container/unordered_map/unordered_map</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unordered_map</name>\n      <anchorfile>cpp/container/unordered_map/~unordered_map</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unordered_multimap</name>\n    <filename>cpp/container/unordered_multimap</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/unordered_multimap/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin(int)</name>\n      <anchorfile>cpp/container/unordered_multimap/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket</name>\n      <anchorfile>cpp/container/unordered_multimap/bucket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_count</name>\n      <anchorfile>cpp/container/unordered_multimap/bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_size</name>\n      <anchorfile>cpp/container/unordered_multimap/bucket_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/unordered_multimap/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin(int)</name>\n      <anchorfile>cpp/container/unordered_multimap/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/unordered_multimap/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend(int)</name>\n      <anchorfile>cpp/container/unordered_multimap/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/unordered_multimap/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/unordered_multimap/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/unordered_multimap/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/unordered_multimap/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/unordered_multimap/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/unordered_multimap/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end(int)</name>\n      <anchorfile>cpp/container/unordered_multimap/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/unordered_multimap/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/unordered_multimap/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/unordered_multimap/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/unordered_multimap/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/unordered_multimap/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_function</name>\n      <anchorfile>cpp/container/unordered_multimap/hash_function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/unordered_multimap/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_eq</name>\n      <anchorfile>cpp/container/unordered_multimap/key_eq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load_factor</name>\n      <anchorfile>cpp/container/unordered_multimap/load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_bucket_count</name>\n      <anchorfile>cpp/container/unordered_multimap/max_bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_load_factor</name>\n      <anchorfile>cpp/container/unordered_multimap/max_load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/unordered_multimap/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/unordered_multimap/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/unordered_multimap/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rehash</name>\n      <anchorfile>cpp/container/unordered_multimap/rehash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/unordered_multimap/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/unordered_multimap/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/unordered_multimap/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unordered_multimap</name>\n      <anchorfile>cpp/container/unordered_multimap/unordered_multimap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unordered_multimap</name>\n      <anchorfile>cpp/container/unordered_multimap/~unordered_multimap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unordered_multiset</name>\n    <filename>cpp/container/unordered_multiset</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/unordered_multiset/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin(int)</name>\n      <anchorfile>cpp/container/unordered_multiset/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket</name>\n      <anchorfile>cpp/container/unordered_multiset/bucket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_count</name>\n      <anchorfile>cpp/container/unordered_multiset/bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_size</name>\n      <anchorfile>cpp/container/unordered_multiset/bucket_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/unordered_multiset/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin(int)</name>\n      <anchorfile>cpp/container/unordered_multiset/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/unordered_multiset/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend(int)</name>\n      <anchorfile>cpp/container/unordered_multiset/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/unordered_multiset/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/unordered_multiset/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/unordered_multiset/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/unordered_multiset/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/unordered_multiset/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/unordered_multiset/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end(int)</name>\n      <anchorfile>cpp/container/unordered_multiset/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/unordered_multiset/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/unordered_multiset/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/unordered_multiset/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/unordered_multiset/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/unordered_multiset/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_function</name>\n      <anchorfile>cpp/container/unordered_multiset/hash_function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/unordered_multiset/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_eq</name>\n      <anchorfile>cpp/container/unordered_multiset/key_eq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load_factor</name>\n      <anchorfile>cpp/container/unordered_multiset/load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_bucket_count</name>\n      <anchorfile>cpp/container/unordered_multiset/max_bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_load_factor</name>\n      <anchorfile>cpp/container/unordered_multiset/max_load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/unordered_multiset/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/unordered_multiset/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/unordered_multiset/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rehash</name>\n      <anchorfile>cpp/container/unordered_multiset/rehash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/unordered_multiset/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/unordered_multiset/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/unordered_multiset/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unordered_multiset</name>\n      <anchorfile>cpp/container/unordered_multiset/unordered_multiset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unordered_multiset</name>\n      <anchorfile>cpp/container/unordered_multiset/~unordered_multiset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::unordered_set</name>\n    <filename>cpp/container/unordered_set</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/unordered_set/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin(int)</name>\n      <anchorfile>cpp/container/unordered_set/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket</name>\n      <anchorfile>cpp/container/unordered_set/bucket</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_count</name>\n      <anchorfile>cpp/container/unordered_set/bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bucket_size</name>\n      <anchorfile>cpp/container/unordered_set/bucket_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/unordered_set/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin(int)</name>\n      <anchorfile>cpp/container/unordered_set/begin2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/unordered_set/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend(int)</name>\n      <anchorfile>cpp/container/unordered_set/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/unordered_set/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>count</name>\n      <anchorfile>cpp/container/unordered_set/count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/unordered_set/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_hint</name>\n      <anchorfile>cpp/container/unordered_set/emplace_hint</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/unordered_set/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/unordered_set/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end(int)</name>\n      <anchorfile>cpp/container/unordered_set/end2</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>equal_range</name>\n      <anchorfile>cpp/container/unordered_set/equal_range</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/unordered_set/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>extract</name>\n      <anchorfile>cpp/container/unordered_set/extract</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/container/unordered_set/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/unordered_set/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>hash_function</name>\n      <anchorfile>cpp/container/unordered_set/hash_function</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/unordered_set/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>key_eq</name>\n      <anchorfile>cpp/container/unordered_set/key_eq</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>load_factor</name>\n      <anchorfile>cpp/container/unordered_set/load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_bucket_count</name>\n      <anchorfile>cpp/container/unordered_set/max_bucket_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_load_factor</name>\n      <anchorfile>cpp/container/unordered_set/max_load_factor</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/unordered_set/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>merge</name>\n      <anchorfile>cpp/container/unordered_set/merge</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/unordered_set/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rehash</name>\n      <anchorfile>cpp/container/unordered_set/rehash</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/unordered_set/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/unordered_set/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/unordered_set/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unordered_set</name>\n      <anchorfile>cpp/container/unordered_set/unordered_set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~unordered_set</name>\n      <anchorfile>cpp/container/unordered_set/~unordered_set</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::uses_allocator</name>\n    <filename>cpp/memory/uses_allocator</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::valarray</name>\n    <filename>cpp/numeric/valarray</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::variant</name>\n    <filename>cpp/utility/variant</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/utility/variant/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>index</name>\n      <anchorfile>cpp/utility/variant/index</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/utility/variant/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/utility/variant/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>valueless_by_exception</name>\n      <anchorfile>cpp/utility/variant/valueless_by_exception</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>variant</name>\n      <anchorfile>cpp/utility/variant/variant</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~variant</name>\n      <anchorfile>cpp/utility/variant/~variant</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::variant_alternative</name>\n    <filename>cpp/utility/variant/variant_alternative</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::variant_alternative_t</name>\n    <filename>cpp/utility/variant/variant_alternative</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::variant_size</name>\n    <filename>cpp/utility/variant/variant_size</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::vector</name>\n    <filename>cpp/container/vector</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/container/vector/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/container/vector/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/container/vector/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/container/vector/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/container/vector/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/container/vector/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/container/vector/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/container/vector/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/container/vector/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/container/vector/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/container/vector/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace</name>\n      <anchorfile>cpp/container/vector/emplace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>emplace_back</name>\n      <anchorfile>cpp/container/vector/emplace_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/container/vector/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/container/vector/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/container/vector/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/container/vector/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/container/vector/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/container/vector/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/container/vector/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/container/vector/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/container/vector/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/container/vector/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/container/vector/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/container/vector/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/container/vector/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/container/vector/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/container/vector/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/container/vector/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/container/vector/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/container/vector/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>vector</name>\n      <anchorfile>cpp/container/vector/vector</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~vector</name>\n      <anchorfile>cpp/container/vector/~vector</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::void_t</name>\n    <filename>cpp/types/void_t</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wbuffer_convert</name>\n    <filename>cpp/locale/wbuffer_convert</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/locale/wbuffer_convert/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>state</name>\n      <anchorfile>cpp/locale/wbuffer_convert/state</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wbuffer_convert</name>\n      <anchorfile>cpp/locale/wbuffer_convert/wbuffer_convert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wbuffer_convert</name>\n      <anchorfile>cpp/locale/wbuffer_convert/~wbuffer_convert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wcerr</name>\n    <filename>cpp/io/cerr</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wcin</name>\n    <filename>cpp/io/cin</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wclog</name>\n    <filename>cpp/io/clog</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wcmatch</name>\n    <filename>cpp/regex/match_results</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/regex/match_results/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>format</name>\n      <anchorfile>cpp/regex/match_results/format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/regex/match_results/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/match_results/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/regex/match_results/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/regex/match_results/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>position</name>\n      <anchorfile>cpp/regex/match_results/position</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>prefix</name>\n      <anchorfile>cpp/regex/match_results/prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ready</name>\n      <anchorfile>cpp/regex/match_results/ready</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/regex/match_results/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/match_results/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>suffix</name>\n      <anchorfile>cpp/regex/match_results/suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/regex/match_results/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcmatch</name>\n      <anchorfile>cpp/regex/match_results/match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wcmatch</name>\n      <anchorfile>cpp/regex/match_results/~match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wcout</name>\n    <filename>cpp/io/cout</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wcregex_iterator</name>\n    <filename>cpp/regex/regex_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcregex_iterator</name>\n      <anchorfile>cpp/regex/regex_iterator/regex_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wcregex_token_iterator</name>\n    <filename>cpp/regex/regex_token_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcregex_token_iterator</name>\n      <anchorfile>cpp/regex/regex_token_iterator/regex_token_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wcsub_match</name>\n    <filename>cpp/regex/sub_match</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/regex/sub_match/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/sub_match/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator string_type</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wcsub_match</name>\n      <anchorfile>cpp/regex/sub_match/sub_match</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::weak_ptr</name>\n    <filename>cpp/memory/weak_ptr</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>expired</name>\n      <anchorfile>cpp/memory/weak_ptr/expired</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>lock</name>\n      <anchorfile>cpp/memory/weak_ptr/lock</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/memory/weak_ptr/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>owner_before</name>\n      <anchorfile>cpp/memory/weak_ptr/owner_before</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/memory/weak_ptr/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/memory/weak_ptr/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>use_count</name>\n      <anchorfile>cpp/memory/weak_ptr/use_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>weak_ptr</name>\n      <anchorfile>cpp/memory/weak_ptr/weak_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~weak_ptr</name>\n      <anchorfile>cpp/memory/weak_ptr/~weak_ptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::weibull_distribution</name>\n    <filename>cpp/numeric/random/weibull_distribution</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>a</name>\n      <anchorfile>cpp/numeric/random/weibull_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>b</name>\n      <anchorfile>cpp/numeric/random/weibull_distribution/params</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max</name>\n      <anchorfile>cpp/numeric/random/weibull_distribution/max</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>min</name>\n      <anchorfile>cpp/numeric/random/weibull_distribution/min</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator()</name>\n      <anchorfile>cpp/numeric/random/weibull_distribution/operator()</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>param</name>\n      <anchorfile>cpp/numeric/random/weibull_distribution/param</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reset</name>\n      <anchorfile>cpp/numeric/random/weibull_distribution/reset</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>weibull_distribution</name>\n      <anchorfile>cpp/numeric/random/weibull_distribution/weibull_distribution</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wfilebuf</name>\n    <filename>cpp/io/basic_filebuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_filebuf/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_filebuf/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_filebuf/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_filebuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wfilebuf</name>\n      <anchorfile>cpp/io/basic_filebuf/basic_filebuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wfilebuf</name>\n      <anchorfile>cpp/io/basic_filebuf/~basic_filebuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wfstream</name>\n    <filename>cpp/io/basic_fstream</filename>\n    <class kind=\"class\">std::wfstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_fstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wfstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wfstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_fstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_fstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_fstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wfstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wfstream</name>\n      <anchorfile>cpp/io/basic_fstream/basic_fstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wfstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wfstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wfstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wfstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wifstream</name>\n    <filename>cpp/io/basic_ifstream</filename>\n    <class kind=\"class\">std::wifstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_ifstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wifstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wifstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_ifstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_ifstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ifstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wifstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wifstream</name>\n      <anchorfile>cpp/io/basic_ifstream/basic_ifstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wifstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wifstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wifstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wifstream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wiostream</name>\n    <filename>cpp/io/basic_iostream</filename>\n    <class kind=\"class\">std::wiostream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wiostream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wiostream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wiostream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wiostream</name>\n      <anchorfile>cpp/io/basic_iostream/basic_iostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wiostream</name>\n      <anchorfile>cpp/io/basic_iostream/~basic_iostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wiostream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wiostream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wiostream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wiostream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistream</name>\n    <filename>cpp/io/basic_istream</filename>\n    <class kind=\"class\">std::wistream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wistream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wistream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wistream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wistream</name>\n      <anchorfile>cpp/io/basic_istream/basic_istream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wistream</name>\n      <anchorfile>cpp/io/basic_istream/~basic_istream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistringstream</name>\n    <filename>cpp/io/basic_istringstream</filename>\n    <class kind=\"class\">std::wistringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wistringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wistringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_istringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wistringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_istringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wistringstream</name>\n      <anchorfile>cpp/io/basic_istringstream/basic_istringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wistringstream::sentry</name>\n    <filename>cpp/io/basic_istream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wofstream</name>\n    <filename>cpp/io/basic_ofstream</filename>\n    <class kind=\"class\">std::wofstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>close</name>\n      <anchorfile>cpp/io/basic_ofstream/close</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wofstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wofstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>is_open</name>\n      <anchorfile>cpp/io/basic_ofstream/is_open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>open</name>\n      <anchorfile>cpp/io/basic_ofstream/open</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ofstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wofstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wofstream</name>\n      <anchorfile>cpp/io/basic_ofstream/basic_ofstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wofstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wofstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wofstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wofstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostream</name>\n    <filename>cpp/io/basic_ostream</filename>\n    <class kind=\"class\">std::wostream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wostream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wostream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wostream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wostream</name>\n      <anchorfile>cpp/io/basic_ostream/basic_ostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wostream</name>\n      <anchorfile>cpp/io/basic_ostream/~basic_ostream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostringstream</name>\n    <filename>cpp/io/basic_ostringstream</filename>\n    <class kind=\"class\">std::wostringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wostringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wostringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_ostringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wostringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_ostringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wostringstream</name>\n      <anchorfile>cpp/io/basic_ostringstream/basic_ostringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wostringstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_ostream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wregex</name>\n    <filename>cpp/regex/basic_regex</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/regex/basic_regex/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/regex/basic_regex/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/regex/basic_regex/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/regex/basic_regex/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>mark_count</name>\n      <anchorfile>cpp/regex/basic_regex/mark_count</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/basic_regex/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/regex/basic_regex/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wregex</name>\n      <anchorfile>cpp/regex/basic_regex/basic_regex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wregex</name>\n      <anchorfile>cpp/regex/basic_regex/~basic_regex</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wsmatch</name>\n    <filename>cpp/regex/match_results</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/regex/match_results/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/regex/match_results/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/regex/match_results/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>format</name>\n      <anchorfile>cpp/regex/match_results/format</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/regex/match_results/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/match_results/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/regex/match_results/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/regex/match_results/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>position</name>\n      <anchorfile>cpp/regex/match_results/position</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>prefix</name>\n      <anchorfile>cpp/regex/match_results/prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ready</name>\n      <anchorfile>cpp/regex/match_results/ready</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/regex/match_results/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/match_results/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>suffix</name>\n      <anchorfile>cpp/regex/match_results/suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/regex/match_results/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wsmatch</name>\n      <anchorfile>cpp/regex/match_results/match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wsmatch</name>\n      <anchorfile>cpp/regex/match_results/~match_results</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wsregex_iterator</name>\n    <filename>cpp/regex/regex_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wsregex_iterator</name>\n      <anchorfile>cpp/regex/regex_iterator/regex_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wsregex_token_iterator</name>\n    <filename>cpp/regex/regex_token_iterator</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator*</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator++(int)</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_arith</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator-&gt;</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator*</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator==</name>\n      <anchorfile>cpp/regex/regex_token_iterator/operator_cmp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wsregex_token_iterator</name>\n      <anchorfile>cpp/regex/regex_token_iterator/regex_token_iterator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wssub_match</name>\n    <filename>cpp/regex/sub_match</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/regex/sub_match/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/regex/sub_match/length</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator string_type</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/regex/sub_match/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wssub_match</name>\n      <anchorfile>cpp/regex/sub_match/sub_match</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstreambuf</name>\n    <filename>cpp/io/basic_streambuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_streambuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstreambuf</name>\n      <anchorfile>cpp/io/basic_streambuf/basic_streambuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wstreambuf</name>\n      <anchorfile>cpp/io/basic_streambuf/~basic_streambuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstreampos</name>\n    <filename>cpp/io/fpos</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>state</name>\n      <anchorfile>cpp/io/fpos/state</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstring</name>\n    <filename>cpp/string/basic_string</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>append</name>\n      <anchorfile>cpp/string/basic_string/append</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>assign</name>\n      <anchorfile>cpp/string/basic_string/assign</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>c_str</name>\n      <anchorfile>cpp/string/basic_string/c_str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>capacity</name>\n      <anchorfile>cpp/string/basic_string/capacity</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/string/basic_string/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>erase</name>\n      <anchorfile>cpp/string/basic_string/erase</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get_allocator</name>\n      <anchorfile>cpp/string/basic_string/get_allocator</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>insert</name>\n      <anchorfile>cpp/string/basic_string/insert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pop_back</name>\n      <anchorfile>cpp/string/basic_string/pop_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>push_back</name>\n      <anchorfile>cpp/string/basic_string/push_back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>replace</name>\n      <anchorfile>cpp/string/basic_string/replace</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>reserve</name>\n      <anchorfile>cpp/string/basic_string/reserve</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>resize</name>\n      <anchorfile>cpp/string/basic_string/resize</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>shrink_to_fit</name>\n      <anchorfile>cpp/string/basic_string/shrink_to_fit</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstring</name>\n      <anchorfile>cpp/string/basic_string/basic_string</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstring_convert</name>\n    <filename>cpp/locale/wstring_convert</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>converted</name>\n      <anchorfile>cpp/locale/wstring_convert/converted</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>from_bytes</name>\n      <anchorfile>cpp/locale/wstring_convert/from_bytes</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>state</name>\n      <anchorfile>cpp/locale/wstring_convert/state</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>to_bytes</name>\n      <anchorfile>cpp/locale/wstring_convert/to_bytes</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstring_convert</name>\n      <anchorfile>cpp/locale/wstring_convert/wstring_convert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~wstring_convert</name>\n      <anchorfile>cpp/locale/wstring_convert/~wstring_convert</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstring_view</name>\n    <filename>cpp/string/basic_string_view</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>at</name>\n      <anchorfile>cpp/string/basic_string_view/at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>back</name>\n      <anchorfile>cpp/string/basic_string_view/back</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>begin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cbegin</name>\n      <anchorfile>cpp/string/basic_string_view/begin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>cend</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>compare</name>\n      <anchorfile>cpp/string/basic_string_view/compare</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copy</name>\n      <anchorfile>cpp/string/basic_string_view/copy</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>crend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>data</name>\n      <anchorfile>cpp/string/basic_string_view/data</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>empty</name>\n      <anchorfile>cpp/string/basic_string_view/empty</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>end</name>\n      <anchorfile>cpp/string/basic_string_view/end</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find</name>\n      <anchorfile>cpp/string/basic_string_view/find</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_first_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_first_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_not_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_not_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>find_last_of</name>\n      <anchorfile>cpp/string/basic_string_view/find_last_of</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>front</name>\n      <anchorfile>cpp/string/basic_string_view/front</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>length</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>max_size</name>\n      <anchorfile>cpp/string/basic_string_view/max_size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/string/basic_string_view/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator[]</name>\n      <anchorfile>cpp/string/basic_string_view/operator_at</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rbegin</name>\n      <anchorfile>cpp/string/basic_string_view/rbegin</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_prefix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_prefix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>remove_suffix</name>\n      <anchorfile>cpp/string/basic_string_view/remove_suffix</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rend</name>\n      <anchorfile>cpp/string/basic_string_view/rend</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rfind</name>\n      <anchorfile>cpp/string/basic_string_view/rfind</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>size</name>\n      <anchorfile>cpp/string/basic_string_view/size</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>substr</name>\n      <anchorfile>cpp/string/basic_string_view/substr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/string/basic_string_view/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstring_view</name>\n      <anchorfile>cpp/string/basic_string_view/basic_string_view</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstringbuf</name>\n    <filename>cpp/io/basic_stringbuf</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eback</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>egptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>epptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gbump</name>\n      <anchorfile>cpp/io/basic_streambuf/gbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/basic_streambuf/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gptr</name>\n      <anchorfile>cpp/io/basic_streambuf/gptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>in_avail</name>\n      <anchorfile>cpp/io/basic_streambuf/in_avail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_stringbuf/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>overflow</name>\n      <anchorfile>cpp/io/basic_streambuf/overflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbackfail</name>\n      <anchorfile>cpp/io/basic_streambuf/pbackfail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbase</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pbump</name>\n      <anchorfile>cpp/io/basic_streambuf/pbump</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pptr</name>\n      <anchorfile>cpp/io/basic_streambuf/pptr</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubimbue</name>\n      <anchorfile>cpp/io/basic_streambuf/pubimbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubseekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsetbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pubsync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sbumpc</name>\n      <anchorfile>cpp/io/basic_streambuf/sbumpc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekoff</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekoff</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekpos</name>\n      <anchorfile>cpp/io/basic_streambuf/pubseekpos</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setbuf</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsetbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setg</name>\n      <anchorfile>cpp/io/basic_streambuf/setg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setp</name>\n      <anchorfile>cpp/io/basic_streambuf/setp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>showmanyc</name>\n      <anchorfile>cpp/io/basic_streambuf/showmanyc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>snextc</name>\n      <anchorfile>cpp/io/basic_streambuf/snextc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputbackc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputbackc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputc</name>\n      <anchorfile>cpp/io/basic_streambuf/sputc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_stringbuf/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sungetc</name>\n      <anchorfile>cpp/io/basic_streambuf/sungetc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_streambuf/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_streambuf/pubsync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>uflow</name>\n      <anchorfile>cpp/io/basic_streambuf/uflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>underflow</name>\n      <anchorfile>cpp/io/basic_streambuf/underflow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstringbuf</name>\n      <anchorfile>cpp/io/basic_stringbuf/basic_stringbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsgetn</name>\n      <anchorfile>cpp/io/basic_streambuf/sgetn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xsputn</name>\n      <anchorfile>cpp/io/basic_streambuf/sputn</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstringstream</name>\n    <filename>cpp/io/basic_stringstream</filename>\n    <class kind=\"class\">std::wstringstream::Init</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>bad</name>\n      <anchorfile>cpp/io/basic_ios/bad</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>clear</name>\n      <anchorfile>cpp/io/basic_ios/clear</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>copyfmt</name>\n      <anchorfile>cpp/io/basic_ios/copyfmt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>eof</name>\n      <anchorfile>cpp/io/basic_ios/eof</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wstringstream::event_callback</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>exceptions</name>\n      <anchorfile>cpp/io/basic_ios/exceptions</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fail</name>\n      <anchorfile>cpp/io/basic_ios/fail</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wstringstream::failure</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>fill</name>\n      <anchorfile>cpp/io/basic_ios/fill</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flags</name>\n      <anchorfile>cpp/io/ios_base/flags</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>flush</name>\n      <anchorfile>cpp/io/basic_ostream/flush</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>gcount</name>\n      <anchorfile>cpp/io/basic_istream/gcount</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>get</name>\n      <anchorfile>cpp/io/basic_istream/get</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getline</name>\n      <anchorfile>cpp/io/basic_istream/getline</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>getloc</name>\n      <anchorfile>cpp/io/ios_base/getloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>good</name>\n      <anchorfile>cpp/io/basic_ios/good</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>ignore</name>\n      <anchorfile>cpp/io/basic_istream/ignore</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>imbue</name>\n      <anchorfile>cpp/io/basic_ios/imbue</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>init</name>\n      <anchorfile>cpp/io/basic_ios/init</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>iword</name>\n      <anchorfile>cpp/io/ios_base/iword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>move</name>\n      <anchorfile>cpp/io/basic_ios/move</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>narrow</name>\n      <anchorfile>cpp/io/basic_ios/narrow</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_ios/operator_bool</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator!</name>\n      <anchorfile>cpp/io/basic_ios/operator!</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&lt;&lt;</name>\n      <anchorfile>cpp/io/basic_ostream/operator_ltlt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator=</name>\n      <anchorfile>cpp/io/basic_stringstream/operator=</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator&gt;&gt;</name>\n      <anchorfile>cpp/io/basic_istream/operator_gtgt</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>peek</name>\n      <anchorfile>cpp/io/basic_istream/peek</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>precision</name>\n      <anchorfile>cpp/io/ios_base/precision</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>put</name>\n      <anchorfile>cpp/io/basic_ostream/put</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>putback</name>\n      <anchorfile>cpp/io/basic_istream/putback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>pword</name>\n      <anchorfile>cpp/io/ios_base/pword</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>rdstate</name>\n      <anchorfile>cpp/io/basic_ios/rdstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>read</name>\n      <anchorfile>cpp/io/basic_istream/read</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>readsome</name>\n      <anchorfile>cpp/io/basic_istream/readsome</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>register_callback</name>\n      <anchorfile>cpp/io/ios_base/register_callback</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekg</name>\n      <anchorfile>cpp/io/basic_istream/seekg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>seekp</name>\n      <anchorfile>cpp/io/basic_ostream/seekp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <class kind=\"class\">std::wstringstream::sentry</class>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>set_rdbuf</name>\n      <anchorfile>cpp/io/basic_ios/set_rdbuf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setf</name>\n      <anchorfile>cpp/io/ios_base/setf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>setstate</name>\n      <anchorfile>cpp/io/basic_ios/setstate</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>str</name>\n      <anchorfile>cpp/io/basic_stringstream/str</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>swap</name>\n      <anchorfile>cpp/io/basic_ios/swap</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync</name>\n      <anchorfile>cpp/io/basic_istream/sync</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sync_with_stdio</name>\n      <anchorfile>cpp/io/ios_base/sync_with_stdio</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellg</name>\n      <anchorfile>cpp/io/basic_istream/tellg</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tellp</name>\n      <anchorfile>cpp/io/basic_ostream/tellp</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>tie</name>\n      <anchorfile>cpp/io/basic_ios/tie</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unget</name>\n      <anchorfile>cpp/io/basic_istream/unget</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>unsetf</name>\n      <anchorfile>cpp/io/ios_base/unsetf</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>widen</name>\n      <anchorfile>cpp/io/basic_ios/widen</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>width</name>\n      <anchorfile>cpp/io/ios_base/width</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>write</name>\n      <anchorfile>cpp/io/basic_ostream/write</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>wstringstream</name>\n      <anchorfile>cpp/io/basic_stringstream/basic_stringstream</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>xalloc</name>\n      <anchorfile>cpp/io/ios_base/xalloc</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstringstream::Init</name>\n    <filename>cpp/io/ios_base/Init</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstringstream::event_callback</name>\n    <filename>cpp/io/ios_base/event_callback</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstringstream::failure</name>\n    <filename>cpp/io/ios_base/failure</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>failure</name>\n      <anchorfile>cpp/io/ios_base/failure</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>what</name>\n      <anchorfile>cpp/error/exception/what</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::wstringstream::sentry</name>\n    <filename>cpp/io/basic_ostream/sentry</filename>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>operator bool</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n    <member kind=\"function\">\n      <type>T</type>\n      <name>~sentry</name>\n      <anchorfile>cpp/io/basic_istream/sentry</anchorfile>\n      <anchor></anchor>\n      <arglist>(T... args)</arglist>\n    </member>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::yocto</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::yotta</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>std::zetta</name>\n    <filename>cpp/numeric/ratio/ratio</filename>\n  </compound>\n  <compound kind=\"class\">\n    <name>va_list</name>\n    <filename>cpp/utility/variadic/va_list</filename>\n  </compound>\n</tagfile>\n"
  },
  {
    "path": "docs/config/custom.css",
    "content": "/**\nDistributed under the MIT License.\nSee LICENSE.txt for details.\n*/\n\nhtml {\n    /* This width must correspond to Doxygen's `TREEVIEW_WIDTH` */\n    --side-nav-fixed-width: 280px;\n}\n\n#nav-sync {\n    display: none;\n}\n"
  },
  {
    "path": "docs/config/doxygen-awesome-fragment-copy-button.js",
    "content": "/**\n\nDoxygen Awesome\nhttps://github.com/jothepro/doxygen-awesome-css\n\nMIT License\n\nCopyright (c) 2022 - 2023 jothepro\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n*/\n\nclass DoxygenAwesomeFragmentCopyButton extends HTMLElement {\n    constructor() {\n        super();\n        this.onclick=this.copyContent\n    }\n    static title = \"Copy to clipboard\"\n    static copyIcon = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\"><path d=\"M0 0h24v24H0V0z\" fill=\"none\"/><path d=\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z\"/></svg>`\n    static successIcon = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\"><path d=\"M0 0h24v24H0V0z\" fill=\"none\"/><path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z\"/></svg>`\n    static successDuration = 980\n    static init() {\n        $(function() {\n            $(document).ready(function() {\n                if(navigator.clipboard) {\n                    const fragments = document.getElementsByClassName(\"fragment\")\n                    for(const fragment of fragments) {\n                        const fragmentWrapper = document.createElement(\"div\")\n                        fragmentWrapper.className = \"doxygen-awesome-fragment-wrapper\"\n                        const fragmentCopyButton = document.createElement(\"doxygen-awesome-fragment-copy-button\")\n                        fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon\n                        fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title\n                        fragment.parentNode.replaceChild(fragmentWrapper, fragment)\n                        fragmentWrapper.appendChild(fragment)\n                        fragmentWrapper.appendChild(fragmentCopyButton)\n                    }\n                }\n            })\n        })\n    }\n\n\n    copyContent() {\n        const content = this.previousSibling.cloneNode(true)\n        // filter out line number from file listings\n        content.querySelectorAll(\".lineno, .ttc\").forEach((node) => {\n            node.remove()\n        })\n        let textContent = content.textContent\n        // remove trailing newlines that appear in file listings\n        let numberOfTrailingNewlines = 0\n        while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\\n') {\n            numberOfTrailingNewlines++;\n        }\n        textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines)\n        navigator.clipboard.writeText(textContent);\n        this.classList.add(\"success\")\n        this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon\n        window.setTimeout(() => {\n            this.classList.remove(\"success\")\n            this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon\n        }, DoxygenAwesomeFragmentCopyButton.successDuration);\n    }\n}\n\ncustomElements.define(\"doxygen-awesome-fragment-copy-button\", DoxygenAwesomeFragmentCopyButton)\n"
  },
  {
    "path": "docs/config/doxygen-awesome-interactive-toc.js",
    "content": "/**\n\nDoxygen Awesome\nhttps://github.com/jothepro/doxygen-awesome-css\n\nMIT License\n\nCopyright (c) 2022 - 2023 jothepro\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n*/\n\nclass DoxygenAwesomeInteractiveToc {\n    static topOffset = 38\n    static hideMobileMenu = true\n    static headers = []\n\n    static init() {\n        window.addEventListener(\"load\", () => {\n            let toc = document.querySelector(\".contents > .toc\")\n            if(toc) {\n                toc.classList.add(\"interactive\")\n                if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) {\n                    toc.classList.add(\"open\")\n                }\n                document.querySelector(\".contents > .toc > h3\")?.addEventListener(\"click\", () => {\n                    if(toc.classList.contains(\"open\")) {\n                        toc.classList.remove(\"open\")\n                    } else {\n                        toc.classList.add(\"open\")\n                    }\n                })\n\n                document.querySelectorAll(\".contents > .toc > ul a\").forEach((node) => {\n                    let id = node.getAttribute(\"href\").substring(1)\n                    DoxygenAwesomeInteractiveToc.headers.push({\n                        node: node,\n                        headerNode: document.getElementById(id)\n                    })\n\n                    document.getElementById(\"doc-content\")?.addEventListener(\"scroll\",this.throttle(DoxygenAwesomeInteractiveToc.update, 100))\n                })\n                DoxygenAwesomeInteractiveToc.update()\n            }\n        })\n    }\n\n    static update() {\n        let active = DoxygenAwesomeInteractiveToc.headers[0]?.node\n        DoxygenAwesomeInteractiveToc.headers.forEach((header) => {\n            let position = header.headerNode.getBoundingClientRect().top\n            header.node.classList.remove(\"active\")\n            header.node.classList.remove(\"aboveActive\")\n            if(position < DoxygenAwesomeInteractiveToc.topOffset) {\n                active = header.node\n                active?.classList.add(\"aboveActive\")\n            }\n        })\n        active?.classList.add(\"active\")\n        active?.classList.remove(\"aboveActive\")\n    }\n\n    static throttle(func, delay) {\n        let lastCall = 0;\n        return function (...args) {\n            const now = new Date().getTime();\n            if (now - lastCall < delay) {\n                return;\n            }\n            lastCall = now;\n            return setTimeout(() => {func(...args)}, delay);\n        };\n    }\n}\n"
  },
  {
    "path": "docs/config/doxygen-awesome-paragraph-link.js",
    "content": "/**\n\nDoxygen Awesome\nhttps://github.com/jothepro/doxygen-awesome-css\n\nMIT License\n\nCopyright (c) 2022 - 2023 jothepro\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n*/\n\nclass DoxygenAwesomeParagraphLink {\n    // Icon from https://fonts.google.com/icons\n    // Licensed under the Apache 2.0 license:\n    // https://www.apache.org/licenses/LICENSE-2.0.html\n    static icon = `<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"20px\" viewBox=\"0 0 24 24\" width=\"20px\"><path d=\"M0 0h24v24H0V0z\" fill=\"none\"/><path d=\"M17 7h-4v2h4c1.65 0 3 1.35 3 3s-1.35 3-3 3h-4v2h4c2.76 0 5-2.24 5-5s-2.24-5-5-5zm-6 8H7c-1.65 0-3-1.35-3-3s1.35-3 3-3h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-2zm-3-4h8v2H8z\"/></svg>`\n    static title = \"Permanent Link\"\n    static init() {\n        $(function() {\n            $(document).ready(function() {\n                document.querySelectorAll(\".contents a.anchor[id], .contents .groupheader > a[id]\").forEach((node) => {\n                    let anchorlink = document.createElement(\"a\")\n                    anchorlink.setAttribute(\"href\", `#${node.getAttribute(\"id\")}`)\n                    anchorlink.setAttribute(\"title\", DoxygenAwesomeParagraphLink.title)\n                    anchorlink.classList.add(\"anchorlink\")\n                    node.classList.add(\"anchor\")\n                    anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon\n                    node.parentElement.appendChild(anchorlink)\n                })\n            })\n        })\n    }\n}\n"
  },
  {
    "path": "docs/config/doxygen-awesome-sidebar-only.css",
    "content": "/**\n\nDoxygen Awesome\nhttps://github.com/jothepro/doxygen-awesome-css\n\nMIT License\n\nCopyright (c) 2021 - 2023 jothepro\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n */\n\nhtml {\n    /* side nav width. MUST be = `TREEVIEW_WIDTH`.\n     * Make sure it is wide enough to contain the page title (logo + title + version)\n     */\n    --side-nav-fixed-width: 335px;\n    --menu-display: none;\n\n    --top-height: 120px;\n    --toc-sticky-top: -25px;\n    --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px);\n}\n\n#projectname {\n    white-space: nowrap;\n}\n\n\n@media screen and (min-width: 768px) {\n    html {\n        --searchbar-background: var(--page-background-color);\n    }\n\n    #side-nav {\n        min-width: var(--side-nav-fixed-width);\n        max-width: var(--side-nav-fixed-width);\n        top: var(--top-height);\n        overflow: visible;\n    }\n\n    #nav-tree, #side-nav {\n        height: calc(100vh - var(--top-height)) !important;\n    }\n\n    #nav-tree {\n        padding: 0;\n    }\n\n    #top {\n        display: block;\n        border-bottom: none;\n        height: var(--top-height);\n        margin-bottom: calc(0px - var(--top-height));\n        max-width: var(--side-nav-fixed-width);\n        overflow: hidden;\n        background: var(--side-nav-background);\n    }\n    #main-nav {\n        float: left;\n        padding-right: 0;\n    }\n\n    .ui-resizable-handle {\n        cursor: default;\n        width: 1px !important;\n        background: var(--separator-color);\n        box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color);\n    }\n\n    #nav-path {\n        position: fixed;\n        right: 0;\n        left: var(--side-nav-fixed-width);\n        bottom: 0;\n        width: auto;\n    }\n\n    #doc-content {\n        height: calc(100vh - 31px) !important;\n        padding-bottom: calc(3 * var(--spacing-large));\n        padding-top: calc(var(--top-height) - 80px);\n        box-sizing: border-box;\n        margin-left: var(--side-nav-fixed-width) !important;\n    }\n\n    #MSearchBox {\n        width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));\n    }\n\n    #MSearchField {\n        width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px);\n    }\n\n    #MSearchResultsWindow {\n        left: var(--spacing-medium) !important;\n        right: auto;\n    }\n}\n"
  },
  {
    "path": "docs/config/doxygen-awesome.css",
    "content": "/**\n\nDoxygen Awesome\nhttps://github.com/jothepro/doxygen-awesome-css\n\nMIT License\n\nCopyright (c) 2021 - 2023 jothepro\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n*/\n\nhtml {\n    /* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */\n    --primary-color: #1779c4;\n    --primary-dark-color: #335c80;\n    --primary-light-color: #70b1e9;\n\n    /* page base colors */\n    --page-background-color: #ffffff;\n    --page-foreground-color: #2f4153;\n    --page-secondary-foreground-color: #6f7e8e;\n\n    /* color for all separators on the website: hr, borders, ... */\n    --separator-color: #dedede;\n\n    /* border radius for all rounded components. Will affect many components, like dropdowns, memitems, codeblocks, ... */\n    --border-radius-large: 8px;\n    --border-radius-small: 4px;\n    --border-radius-medium: 6px;\n\n    /* default spacings. Most components reference these values for spacing, to provide uniform spacing on the page. */\n    --spacing-small: 5px;\n    --spacing-medium: 10px;\n    --spacing-large: 16px;\n\n    /* default box shadow used for raising an element above the normal content. Used in dropdowns, search result, ... */\n    --box-shadow: 0 2px 8px 0 rgba(0,0,0,.075);\n\n    --odd-color: rgba(0,0,0,.028);\n\n    /* font-families. will affect all text on the website\n     * font-family: the normal font for text, headlines, menus\n     * font-family-monospace: used for preformatted text in memtitle, code, fragments\n     */\n    --font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;\n    --font-family-monospace: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;\n\n    /* font sizes */\n    --page-font-size: 15.6px;\n    --navigation-font-size: 14.4px;\n    --toc-font-size: 13.4px;\n    --code-font-size: 14px; /* affects code, fragment */\n    --title-font-size: 22px;\n\n    /* content text properties. These only affect the page content, not the navigation or any other ui elements */\n    --content-line-height: 27px;\n    /* The content is centered and constraint in it's width. To make the content fill the whole page, set the variable to auto.*/\n    --content-maxwidth: 1050px;\n    --table-line-height: 24px;\n    --toc-sticky-top: var(--spacing-medium);\n    --toc-width: 200px;\n    --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 85px);\n\n    /* colors for various content boxes: @warning, @note, @deprecated @bug */\n    --warning-color: #faf3d8;\n    --warning-color-dark: #f3a600;\n    --warning-color-darker: #5f4204;\n    --note-color: #e4f3ff;\n    --note-color-dark: #1879C4;\n    --note-color-darker: #274a5c;\n    --todo-color: #e4dafd;\n    --todo-color-dark: #5b2bdd;\n    --todo-color-darker: #2a0d72;\n    --deprecated-color: #ecf0f3;\n    --deprecated-color-dark: #5b6269;\n    --deprecated-color-darker: #43454a;\n    --bug-color: #f8d1cc;\n    --bug-color-dark: #b61825;\n    --bug-color-darker: #75070f;\n    --invariant-color: #d8f1e3;\n    --invariant-color-dark: #44b86f;\n    --invariant-color-darker: #265532;\n\n    /* blockquote colors */\n    --blockquote-background: #f8f9fa;\n    --blockquote-foreground: #636568;\n\n    /* table colors */\n    --tablehead-background: #f1f1f1;\n    --tablehead-foreground: var(--page-foreground-color);\n\n    /* menu-display: block | none\n     * Visibility of the top navigation on screens >= 768px. On smaller screen the menu is always visible.\n     * `GENERATE_TREEVIEW` MUST be enabled!\n     */\n    --menu-display: block;\n\n    --menu-focus-foreground: var(--page-background-color);\n    --menu-focus-background: var(--primary-color);\n    --menu-selected-background: rgba(0,0,0,.05);\n\n\n    --header-background: var(--page-background-color);\n    --header-foreground: var(--page-foreground-color);\n\n    /* searchbar colors */\n    --searchbar-background: var(--side-nav-background);\n    --searchbar-foreground: var(--page-foreground-color);\n\n    /* searchbar size\n     * (`searchbar-width` is only applied on screens >= 768px.\n     * on smaller screens the searchbar will always fill the entire screen width) */\n    --searchbar-height: 33px;\n    --searchbar-width: 210px;\n    --searchbar-border-radius: var(--searchbar-height);\n\n    /* code block colors */\n    --code-background: #f5f5f5;\n    --code-foreground: var(--page-foreground-color);\n\n    /* fragment colors */\n    --fragment-background: #F8F9FA;\n    --fragment-foreground: #37474F;\n    --fragment-keyword: #bb6bb2;\n    --fragment-keywordtype: #8258b3;\n    --fragment-keywordflow: #d67c3b;\n    --fragment-token: #438a59;\n    --fragment-comment: #969696;\n    --fragment-link: #5383d6;\n    --fragment-preprocessor: #46aaa5;\n    --fragment-linenumber-color: #797979;\n    --fragment-linenumber-background: #f4f4f5;\n    --fragment-linenumber-border: #e3e5e7;\n    --fragment-lineheight: 20px;\n\n    /* sidebar navigation (treeview) colors */\n    --side-nav-background: #fbfbfb;\n    --side-nav-foreground: var(--page-foreground-color);\n    --side-nav-arrow-opacity: 0;\n    --side-nav-arrow-hover-opacity: 0.9;\n\n    --toc-background: var(--side-nav-background);\n    --toc-foreground: var(--side-nav-foreground);\n\n    /* height of an item in any tree / collapsible table */\n    --tree-item-height: 30px;\n\n    --memname-font-size: var(--code-font-size);\n    --memtitle-font-size: 18px;\n\n    --webkit-scrollbar-size: 7px;\n    --webkit-scrollbar-padding: 4px;\n    --webkit-scrollbar-color: var(--separator-color);\n\n    --animation-duration: .12s\n}\n\n@media screen and (max-width: 767px) {\n    html {\n        --page-font-size: 16px;\n        --navigation-font-size: 16px;\n        --toc-font-size: 15px;\n        --code-font-size: 15px; /* affects code, fragment */\n        --title-font-size: 22px;\n    }\n}\n\n@media (prefers-color-scheme: dark) {\n    html:not(.light-mode) {\n        color-scheme: dark;\n\n        --primary-color: #1982d2;\n        --primary-dark-color: #86a9c4;\n        --primary-light-color: #4779ac;\n\n        --box-shadow: 0 2px 8px 0 rgba(0,0,0,.35);\n\n        --odd-color: rgba(100,100,100,.06);\n\n        --menu-selected-background: rgba(0,0,0,.4);\n\n        --page-background-color: #1C1D1F;\n        --page-foreground-color: #d2dbde;\n        --page-secondary-foreground-color: #859399;\n        --separator-color: #38393b;\n        --side-nav-background: #252628;\n\n        --code-background: #2a2c2f;\n\n        --tablehead-background: #2a2c2f;\n\n        --blockquote-background: #222325;\n        --blockquote-foreground: #7e8c92;\n\n        --warning-color: #3b2e04;\n        --warning-color-dark: #f1b602;\n        --warning-color-darker: #ceb670;\n        --note-color: #163750;\n        --note-color-dark: #1982D2;\n        --note-color-darker: #dcf0fa;\n        --todo-color: #2a2536;\n        --todo-color-dark: #7661b3;\n        --todo-color-darker: #ae9ed6;\n        --deprecated-color: #2e323b;\n        --deprecated-color-dark: #738396;\n        --deprecated-color-darker: #abb0bd;\n        --bug-color: #2e1917;\n        --bug-color-dark: #ad2617;\n        --bug-color-darker: #f5b1aa;\n        --invariant-color: #303a35;\n        --invariant-color-dark: #76ce96;\n        --invariant-color-darker: #cceed5;\n\n        --fragment-background: #282c34;\n        --fragment-foreground: #dbe4eb;\n        --fragment-keyword: #cc99cd;\n        --fragment-keywordtype: #ab99cd;\n        --fragment-keywordflow: #e08000;\n        --fragment-token: #7ec699;\n        --fragment-comment: #999999;\n        --fragment-link: #98c0e3;\n        --fragment-preprocessor: #65cabe;\n        --fragment-linenumber-color: #cccccc;\n        --fragment-linenumber-background: #35393c;\n        --fragment-linenumber-border: #1f1f1f;\n    }\n}\n\n/* dark mode variables are defined twice, to support both the dark-mode without and with doxygen-awesome-darkmode-toggle.js */\nhtml.dark-mode {\n    color-scheme: dark;\n\n    --primary-color: #1982d2;\n    --primary-dark-color: #86a9c4;\n    --primary-light-color: #4779ac;\n\n    --box-shadow: 0 2px 8px 0 rgba(0,0,0,.30);\n\n    --odd-color: rgba(100,100,100,.06);\n\n    --menu-selected-background: rgba(0,0,0,.4);\n\n    --page-background-color: #1C1D1F;\n    --page-foreground-color: #d2dbde;\n    --page-secondary-foreground-color: #859399;\n    --separator-color: #38393b;\n    --side-nav-background: #252628;\n\n    --code-background: #2a2c2f;\n\n    --tablehead-background: #2a2c2f;\n\n    --blockquote-background: #222325;\n    --blockquote-foreground: #7e8c92;\n\n    --warning-color: #3b2e04;\n    --warning-color-dark: #f1b602;\n    --warning-color-darker: #ceb670;\n    --note-color: #163750;\n    --note-color-dark: #1982D2;\n    --note-color-darker: #dcf0fa;\n    --todo-color: #2a2536;\n    --todo-color-dark: #7661b3;\n    --todo-color-darker: #ae9ed6;\n    --deprecated-color: #2e323b;\n    --deprecated-color-dark: #738396;\n    --deprecated-color-darker: #abb0bd;\n    --bug-color: #2e1917;\n    --bug-color-dark: #ad2617;\n    --bug-color-darker: #f5b1aa;\n    --invariant-color: #303a35;\n    --invariant-color-dark: #76ce96;\n    --invariant-color-darker: #cceed5;\n\n    --fragment-background: #282c34;\n    --fragment-foreground: #dbe4eb;\n    --fragment-keyword: #cc99cd;\n    --fragment-keywordtype: #ab99cd;\n    --fragment-keywordflow: #e08000;\n    --fragment-token: #7ec699;\n    --fragment-comment: #999999;\n    --fragment-link: #98c0e3;\n    --fragment-preprocessor: #65cabe;\n    --fragment-linenumber-color: #cccccc;\n    --fragment-linenumber-background: #35393c;\n    --fragment-linenumber-border: #1f1f1f;\n}\n\nbody {\n    color: var(--page-foreground-color);\n    background-color: var(--page-background-color);\n    font-size: var(--page-font-size);\n}\n\nbody, table, div, p, dl, #nav-tree .label, .title,\n.sm-dox a, .sm-dox a:hover, .sm-dox a:focus, #projectname,\n.SelectItem, #MSearchField, .navpath li.navelem a,\n.navpath li.navelem a:hover, p.reference, p.definition, div.toc li, div.toc h3 {\n    font-family: var(--font-family);\n}\n\nh1, h2, h3, h4, h5 {\n    margin-top: 1em;\n    font-weight: 600;\n    line-height: initial;\n}\n\np, div, table, dl, p.reference, p.definition {\n    font-size: var(--page-font-size);\n}\n\np.reference, p.definition {\n    color: var(--page-secondary-foreground-color);\n}\n\na:link, a:visited, a:hover, a:focus, a:active {\n    color: var(--primary-color) !important;\n    font-weight: 500;\n    background: none;\n}\n\na.anchor {\n    scroll-margin-top: var(--spacing-large);\n    display: block;\n}\n\n/*\n Title and top navigation\n */\n\n#top {\n    background: var(--header-background);\n    border-bottom: 1px solid var(--separator-color);\n}\n\n@media screen and (min-width: 768px) {\n    #top {\n        display: flex;\n        flex-wrap: wrap;\n        justify-content: space-between;\n        align-items: center;\n    }\n}\n\n#main-nav {\n    flex-grow: 5;\n    padding: var(--spacing-small) var(--spacing-medium);\n}\n\n#titlearea {\n    width: auto;\n    padding: var(--spacing-medium) var(--spacing-large);\n    background: none;\n    color: var(--header-foreground);\n    border-bottom: none;\n}\n\n@media screen and (max-width: 767px) {\n    #titlearea {\n        padding-bottom: var(--spacing-small);\n    }\n}\n\n#titlearea table tbody tr {\n    height: auto !important;\n}\n\n#projectname {\n    font-size: var(--title-font-size);\n    font-weight: 600;\n}\n\n#projectnumber {\n    font-family: inherit;\n    font-size: 60%;\n}\n\n#projectbrief {\n    font-family: inherit;\n    font-size: 80%;\n}\n\n#projectlogo {\n    vertical-align: middle;\n}\n\n#projectlogo img {\n    max-height: calc(var(--title-font-size) * 2);\n    margin-right: var(--spacing-small);\n}\n\n.sm-dox, .tabs, .tabs2, .tabs3 {\n    background: none;\n    padding: 0;\n}\n\n.tabs, .tabs2, .tabs3 {\n    border-bottom: 1px solid var(--separator-color);\n    margin-bottom: -1px;\n}\n\n.main-menu-btn-icon, .main-menu-btn-icon:before, .main-menu-btn-icon:after {\n    background: var(--page-secondary-foreground-color);\n}\n\n@media screen and (max-width: 767px) {\n    .sm-dox a span.sub-arrow {\n        background: var(--code-background);\n    }\n\n    #main-menu a.has-submenu span.sub-arrow {\n        color: var(--page-secondary-foreground-color);\n        border-radius: var(--border-radius-medium);\n    }\n\n    #main-menu a.has-submenu:hover span.sub-arrow {\n        color: var(--page-foreground-color);\n    }\n}\n\n@media screen and (min-width: 768px) {\n    .sm-dox li, .tablist li {\n        display: var(--menu-display);\n    }\n\n    .sm-dox a span.sub-arrow {\n        border-color: var(--header-foreground) transparent transparent transparent;\n    }\n\n    .sm-dox a:hover span.sub-arrow {\n        border-color: var(--menu-focus-foreground) transparent transparent transparent;\n    }\n\n    .sm-dox ul a span.sub-arrow {\n        border-color: transparent transparent transparent var(--page-foreground-color);\n    }\n\n    .sm-dox ul a:hover span.sub-arrow {\n        border-color: transparent transparent transparent var(--menu-focus-foreground);\n    }\n}\n\n.sm-dox ul {\n    background: var(--page-background-color);\n    box-shadow: var(--box-shadow);\n    border: 1px solid var(--separator-color);\n    border-radius: var(--border-radius-medium) !important;\n    padding: var(--spacing-small);\n    animation: ease-out 150ms slideInMenu;\n}\n\n@keyframes slideInMenu {\n    from {\n        opacity: 0;\n        transform: translate(0px, -2px);\n    }\n\n    to {\n        opacity: 1;\n        transform: translate(0px, 0px);\n    }\n}\n\n.sm-dox ul a {\n    color: var(--page-foreground-color) !important;\n    background: var(--page-background-color);\n    font-size: var(--navigation-font-size);\n}\n\n.sm-dox>li>ul:after {\n    border-bottom-color: var(--page-background-color) !important;\n}\n\n.sm-dox>li>ul:before {\n    border-bottom-color: var(--separator-color) !important;\n}\n\n.sm-dox ul a:hover, .sm-dox ul a:active, .sm-dox ul a:focus {\n    font-size: var(--navigation-font-size) !important;\n    color: var(--menu-focus-foreground) !important;\n    text-shadow: none;\n    background-color: var(--menu-focus-background);\n    border-radius: var(--border-radius-small) !important;\n}\n\n.sm-dox a, .sm-dox a:focus, .tablist li, .tablist li a, .tablist li.current a {\n    text-shadow: none;\n    background: transparent;\n    background-image: none !important;\n    color: var(--header-foreground) !important;\n    font-weight: normal;\n    font-size: var(--navigation-font-size);\n    border-radius: var(--border-radius-small) !important;\n}\n\n.sm-dox a:focus {\n    outline: auto;\n}\n\n.sm-dox a:hover, .sm-dox a:active, .tablist li a:hover {\n    text-shadow: none;\n    font-weight: normal;\n    background: var(--menu-focus-background);\n    color: var(--menu-focus-foreground) !important;\n    border-radius: var(--border-radius-small) !important;\n    font-size: var(--navigation-font-size);\n}\n\n.tablist li.current {\n    border-radius: var(--border-radius-small);\n    background: var(--menu-selected-background);\n}\n\n.tablist li {\n    margin: var(--spacing-small) 0 var(--spacing-small) var(--spacing-small);\n}\n\n.tablist a {\n    padding: 0 var(--spacing-large);\n}\n\n\n/*\n Search box\n */\n\n#MSearchBox {\n    height: var(--searchbar-height);\n    background: var(--searchbar-background);\n    border-radius: var(--searchbar-border-radius);\n    border: 1px solid var(--separator-color);\n    overflow: hidden;\n    width: var(--searchbar-width);\n    position: relative;\n    box-shadow: none;\n    display: block;\n    margin-top: 0;\n}\n\n/* until Doxygen 1.9.4 */\n.left img#MSearchSelect {\n    left: 0;\n    user-select: none;\n    padding-left: 8px;\n}\n\n/* Doxygen 1.9.5 */\n.left span#MSearchSelect {\n    left: 0;\n    user-select: none;\n    margin-left: 8px;\n    padding: 0;\n}\n\n.left #MSearchSelect[src$=\".png\"] {\n    padding-left: 0\n}\n\n.SelectionMark {\n    user-select: none;\n}\n\n.tabs .left #MSearchSelect {\n    padding-left: 0;\n}\n\n.tabs #MSearchBox {\n    position: absolute;\n    right: var(--spacing-medium);\n}\n\n@media screen and (max-width: 767px) {\n    .tabs #MSearchBox {\n        position: relative;\n        right: 0;\n        margin-left: var(--spacing-medium);\n        margin-top: 0;\n    }\n}\n\n#MSearchSelectWindow, #MSearchResultsWindow {\n    z-index: 9999;\n}\n\n#MSearchBox.MSearchBoxActive {\n    border-color: var(--primary-color);\n    box-shadow: inset 0 0 0 1px var(--primary-color);\n}\n\n#main-menu > li:last-child {\n    margin-right: 0;\n}\n\n@media screen and (max-width: 767px) {\n    #main-menu > li:last-child {\n        height: 50px;\n    }\n}\n\n#MSearchField {\n    font-size: var(--navigation-font-size);\n    height: calc(var(--searchbar-height) - 2px);\n    background: transparent;\n    width: calc(var(--searchbar-width) - 64px);\n}\n\n.MSearchBoxActive #MSearchField {\n    color: var(--searchbar-foreground);\n}\n\n#MSearchSelect {\n    top: calc(calc(var(--searchbar-height) / 2) - 11px);\n}\n\n#MSearchBox span.left, #MSearchBox span.right {\n    background: none;\n    background-image: none;\n}\n\n#MSearchBox span.right {\n    padding-top: calc(calc(var(--searchbar-height) / 2) - 12px);\n    position: absolute;\n    right: var(--spacing-small);\n}\n\n.tabs #MSearchBox span.right {\n    top: calc(calc(var(--searchbar-height) / 2) - 12px);\n}\n\n@keyframes slideInSearchResults {\n    from {\n        opacity: 0;\n        transform: translate(0, 15px);\n    }\n\n    to {\n        opacity: 1;\n        transform: translate(0, 20px);\n    }\n}\n\n#MSearchResultsWindow {\n    left: auto !important;\n    right: var(--spacing-medium);\n    border-radius: var(--border-radius-large);\n    border: 1px solid var(--separator-color);\n    transform: translate(0, 20px);\n    box-shadow: var(--box-shadow);\n    animation: ease-out 280ms slideInSearchResults;\n    background: var(--page-background-color);\n}\n\niframe#MSearchResults {\n    margin: 4px;\n}\n\niframe {\n    color-scheme: normal;\n}\n\n@media (prefers-color-scheme: dark) {\n    html:not(.light-mode) iframe#MSearchResults {\n        filter: invert() hue-rotate(180deg);\n    }\n}\n\nhtml.dark-mode iframe#MSearchResults {\n    filter: invert() hue-rotate(180deg);\n}\n\n#MSearchResults .SRPage {\n    background-color: transparent;\n}\n\n#MSearchResults .SRPage .SREntry {\n    font-size: 10pt;\n    padding: var(--spacing-small) var(--spacing-medium);\n}\n\n#MSearchSelectWindow {\n    border: 1px solid var(--separator-color);\n    border-radius: var(--border-radius-medium);\n    box-shadow: var(--box-shadow);\n    background: var(--page-background-color);\n    padding-top: var(--spacing-small);\n    padding-bottom: var(--spacing-small);\n}\n\n#MSearchSelectWindow a.SelectItem {\n    font-size: var(--navigation-font-size);\n    line-height: var(--content-line-height);\n    margin: 0 var(--spacing-small);\n    border-radius: var(--border-radius-small);\n    color: var(--page-foreground-color) !important;\n    font-weight: normal;\n}\n\n#MSearchSelectWindow a.SelectItem:hover {\n    background: var(--menu-focus-background);\n    color: var(--menu-focus-foreground) !important;\n}\n\n@media screen and (max-width: 767px) {\n    #MSearchBox {\n        margin-top: var(--spacing-medium);\n        margin-bottom: var(--spacing-medium);\n        width: calc(100vw - 30px);\n    }\n\n    #main-menu > li:last-child {\n        float: none !important;\n    }\n\n    #MSearchField {\n        width: calc(100vw - 110px);\n    }\n\n    @keyframes slideInSearchResultsMobile {\n        from {\n            opacity: 0;\n            transform: translate(0, 15px);\n        }\n\n        to {\n            opacity: 1;\n            transform: translate(0, 20px);\n        }\n    }\n\n    #MSearchResultsWindow {\n        left: var(--spacing-medium) !important;\n        right: var(--spacing-medium);\n        overflow: auto;\n        transform: translate(0, 20px);\n        animation: ease-out 280ms slideInSearchResultsMobile;\n        width: auto !important;\n    }\n\n    /*\n     * Overwrites for fixing the searchbox on mobile in doxygen 1.9.2\n     */\n    label.main-menu-btn ~ #searchBoxPos1 {\n        top: 3px !important;\n        right: 6px !important;\n        left: 45px;\n        display: flex;\n    }\n\n    label.main-menu-btn ~ #searchBoxPos1 > #MSearchBox {\n        margin-top: 0;\n        margin-bottom: 0;\n        flex-grow: 2;\n        float: left;\n    }\n}\n\n/*\n Tree view\n */\n\n#side-nav {\n    padding: 0 !important;\n    background: var(--side-nav-background);\n    min-width: 8px;\n    max-width: 50vw;\n}\n\n@media screen and (max-width: 767px) {\n    #side-nav {\n        display: none;\n    }\n\n    #doc-content {\n        margin-left: 0 !important;\n    }\n}\n\n#nav-tree {\n    background: transparent;\n    margin-right: 1px;\n}\n\n#nav-tree .label {\n    font-size: var(--navigation-font-size);\n}\n\n#nav-tree .item {\n    height: var(--tree-item-height);\n    line-height: var(--tree-item-height);\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n\n#nav-tree .item > a:focus {\n    outline: none;\n}\n\n#nav-sync {\n    bottom: 12px;\n    right: 12px;\n    top: auto !important;\n    user-select: none;\n}\n\n#nav-tree .selected {\n    text-shadow: none;\n    background-image: none;\n    background-color: transparent;\n    position: relative;\n    color: var(--primary-color) !important;\n    font-weight: 500;\n}\n\n#nav-tree .selected::after {\n    content: \"\";\n    position: absolute;\n    top: 1px;\n    bottom: 1px;\n    left: 0;\n    width: 4px;\n    border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;\n    background: var(--primary-color);\n}\n\n\n#nav-tree a {\n    color: var(--side-nav-foreground) !important;\n    font-weight: normal;\n}\n\n#nav-tree a:focus {\n    outline-style: auto;\n}\n\n#nav-tree .arrow {\n    opacity: var(--side-nav-arrow-opacity);\n    background: none;\n}\n\n.arrow {\n    color: inherit;\n    cursor: pointer;\n    font-size: 45%;\n    vertical-align: middle;\n    margin-right: 2px;\n    font-family: serif;\n    height: auto;\n    text-align: right;\n}\n\n#nav-tree div.item:hover .arrow, #nav-tree a:focus .arrow {\n    opacity: var(--side-nav-arrow-hover-opacity);\n}\n\n#nav-tree .selected a {\n    color: var(--primary-color) !important;\n    font-weight: bolder;\n    font-weight: 600;\n}\n\n.ui-resizable-e {\n    width: 4px;\n    background: transparent;\n    box-shadow: inset -1px 0 0 0 var(--separator-color);\n}\n\n/*\n Contents\n */\n\ndiv.header {\n    border-bottom: 1px solid var(--separator-color);\n    background-color: var(--page-background-color);\n    background-image: none;\n}\n\n@media screen and (min-width: 1000px) {\n    #doc-content > div > div.contents,\n    .PageDoc > div.contents {\n        display: flex;\n        flex-direction: row-reverse;\n        flex-wrap: nowrap;\n        align-items: flex-start;\n    }\n\n    div.contents .textblock {\n        min-width: 200px;\n        flex-grow: 1;\n    }\n}\n\ndiv.contents, div.header .title, div.header .summary {\n    max-width: var(--content-maxwidth);\n}\n\ndiv.contents, div.header .title  {\n    line-height: initial;\n    margin: calc(var(--spacing-medium) + .2em) auto var(--spacing-medium) auto;\n}\n\ndiv.header .summary {\n    margin: var(--spacing-medium) auto 0 auto;\n}\n\ndiv.headertitle {\n    padding: 0;\n}\n\ndiv.header .title {\n    font-weight: 600;\n    font-size: 225%;\n    padding: var(--spacing-medium) var(--spacing-large);\n    word-break: break-word;\n}\n\ndiv.header .summary {\n    width: auto;\n    display: block;\n    float: none;\n    padding: 0 var(--spacing-large);\n}\n\ntd.memSeparator {\n    border-color: var(--separator-color);\n}\n\nspan.mlabel {\n    background: var(--primary-color);\n    border: none;\n    padding: 4px 9px;\n    border-radius: 12px;\n    margin-right: var(--spacing-medium);\n}\n\nspan.mlabel:last-of-type {\n    margin-right: 2px;\n}\n\ndiv.contents {\n    padding: 0 var(--spacing-large);\n}\n\ndiv.contents p, div.contents li {\n    line-height: var(--content-line-height);\n}\n\ndiv.contents div.dyncontent {\n    margin: var(--spacing-medium) 0;\n}\n\n@media (prefers-color-scheme: dark) {\n    html:not(.light-mode) div.contents div.dyncontent img,\n    html:not(.light-mode) div.contents center img,\n    html:not(.light-mode) div.contents > table img,\n    html:not(.light-mode) div.contents div.dyncontent iframe,\n    html:not(.light-mode) div.contents center iframe,\n    html:not(.light-mode) div.contents table iframe,\n    html:not(.light-mode) div.contents .dotgraph iframe {\n        filter: brightness(89%) hue-rotate(180deg) invert();\n    }\n}\n\nhtml.dark-mode div.contents div.dyncontent img,\nhtml.dark-mode div.contents center img,\nhtml.dark-mode div.contents > table img,\nhtml.dark-mode div.contents div.dyncontent iframe,\nhtml.dark-mode div.contents center iframe,\nhtml.dark-mode div.contents table iframe,\nhtml.dark-mode div.contents .dotgraph iframe\n {\n    filter: brightness(89%) hue-rotate(180deg) invert();\n}\n\nh2.groupheader {\n    border-bottom: 0px;\n    color: var(--page-foreground-color);\n    box-shadow:\n        100px 0 var(--page-background-color),\n        -100px 0 var(--page-background-color),\n        100px 0.75px var(--separator-color),\n        -100px 0.75px var(--separator-color),\n        500px 0 var(--page-background-color),\n        -500px 0 var(--page-background-color),\n        500px 0.75px var(--separator-color),\n        -500px 0.75px var(--separator-color),\n        900px 0 var(--page-background-color),\n        -900px 0 var(--page-background-color),\n        900px 0.75px var(--separator-color),\n        -900px 0.75px var(--separator-color),\n        1400px 0 var(--page-background-color),\n        -1400px 0 var(--page-background-color),\n        1400px 0.75px var(--separator-color),\n        -1400px 0.75px var(--separator-color),\n        1900px 0 var(--page-background-color),\n        -1900px 0 var(--page-background-color),\n        1900px 0.75px var(--separator-color),\n        -1900px 0.75px var(--separator-color);\n}\n\nblockquote {\n    margin: 0 var(--spacing-medium) 0 var(--spacing-medium);\n    padding: var(--spacing-small) var(--spacing-large);\n    background: var(--blockquote-background);\n    color: var(--blockquote-foreground);\n    border-left: 0;\n    overflow: visible;\n    border-radius: var(--border-radius-medium);\n    overflow: visible;\n    position: relative;\n}\n\nblockquote::before, blockquote::after {\n    font-weight: bold;\n    font-family: serif;\n    font-size: 360%;\n    opacity: .15;\n    position: absolute;\n}\n\nblockquote::before {\n    content: \"“\";\n    left: -10px;\n    top: 4px;\n}\n\nblockquote::after {\n    content: \"”\";\n    right: -8px;\n    bottom: -25px;\n}\n\nblockquote p {\n    margin: var(--spacing-small) 0 var(--spacing-medium) 0;\n}\n.paramname, .paramname em {\n    font-weight: 600;\n    color: var(--primary-dark-color);\n}\n\n.paramname > code {\n    border: 0;\n}\n\ntable.params .paramname {\n    font-weight: 600;\n    font-family: var(--font-family-monospace);\n    font-size: var(--code-font-size);\n    padding-right: var(--spacing-small);\n    line-height: var(--table-line-height);\n}\n\nh1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {\n    text-shadow: 0 0 15px var(--primary-light-color);\n}\n\n.alphachar a {\n    color: var(--page-foreground-color);\n}\n\n.dotgraph {\n    max-width: 100%;\n    overflow-x: scroll;\n}\n\n.dotgraph .caption {\n    position: sticky;\n    left: 0;\n}\n\n/* Wrap Graphviz graphs with the `interactive_dotgraph` class if `INTERACTIVE_SVG = YES` */\n.interactive_dotgraph .dotgraph iframe {\n    max-width: 100%;\n}\n\n/*\n Table of Contents\n */\n\ndiv.contents .toc {\n    max-height: var(--toc-max-height);\n    min-width: var(--toc-width);\n    border: 0;\n    border-left: 1px solid var(--separator-color);\n    border-radius: 0;\n    background-color: var(--page-background-color);\n    box-shadow: none;\n    position: sticky;\n    top: var(--toc-sticky-top);\n    padding: 0 var(--spacing-large);\n    margin: var(--spacing-small) 0 var(--spacing-large) var(--spacing-large);\n}\n\ndiv.toc h3 {\n    color: var(--toc-foreground);\n    font-size: var(--navigation-font-size);\n    margin: var(--spacing-large) 0 var(--spacing-medium) 0;\n}\n\ndiv.toc li {\n    padding: 0;\n    background: none;\n    line-height: var(--toc-font-size);\n    margin: var(--toc-font-size) 0 0 0;\n}\n\ndiv.toc li::before {\n    display: none;\n}\n\ndiv.toc ul {\n    margin-top: 0\n}\n\ndiv.toc li a {\n    font-size: var(--toc-font-size);\n    color: var(--page-foreground-color) !important;\n    text-decoration: none;\n}\n\ndiv.toc li a:hover, div.toc li a.active {\n    color: var(--primary-color) !important;\n}\n\ndiv.toc li a.aboveActive {\n    color: var(--page-secondary-foreground-color) !important;\n}\n\n\n@media screen and (max-width: 999px) {\n    div.contents .toc {\n        max-height: 45vh;\n        float: none;\n        width: auto;\n        margin: 0 0 var(--spacing-medium) 0;\n        position: relative;\n        top: 0;\n        position: relative;\n        border: 1px solid var(--separator-color);\n        border-radius: var(--border-radius-medium);\n        background-color: var(--toc-background);\n        box-shadow: var(--box-shadow);\n    }\n\n    div.contents .toc.interactive {\n        max-height: calc(var(--navigation-font-size) + 2 * var(--spacing-large));\n        overflow: hidden;\n    }\n\n    div.contents .toc > h3 {\n        -webkit-tap-highlight-color: transparent;\n        cursor: pointer;\n        position: sticky;\n        top: 0;\n        background-color: var(--toc-background);\n        margin: 0;\n        padding: var(--spacing-large) 0;\n        display: block;\n    }\n\n    div.contents .toc.interactive > h3::before {\n        content: \"\";\n        width: 0;\n        height: 0;\n        border-left: 4px solid transparent;\n        border-right: 4px solid transparent;\n        border-top: 5px solid var(--primary-color);\n        display: inline-block;\n        margin-right: var(--spacing-small);\n        margin-bottom: calc(var(--navigation-font-size) / 4);\n        transform: rotate(-90deg);\n        transition: transform var(--animation-duration) ease-out;\n    }\n\n    div.contents .toc.interactive.open > h3::before {\n        transform: rotate(0deg);\n    }\n\n    div.contents .toc.interactive.open {\n        max-height: 45vh;\n        overflow: auto;\n        transition: max-height 0.2s ease-in-out;\n    }\n\n    div.contents .toc a, div.contents .toc a.active {\n        color: var(--primary-color) !important;\n    }\n\n    div.contents .toc a:hover {\n        text-decoration: underline;\n    }\n}\n\n/*\n Code & Fragments\n */\n\ncode, div.fragment, pre.fragment {\n    border-radius: var(--border-radius-small);\n    border: 1px solid var(--separator-color);\n    overflow: hidden;\n}\n\ncode {\n    display: inline;\n    background: var(--code-background);\n    color: var(--code-foreground);\n    padding: 2px 6px;\n}\n\ndiv.fragment, pre.fragment {\n    margin: var(--spacing-medium) 0;\n    padding: calc(var(--spacing-large) - (var(--spacing-large) / 6)) var(--spacing-large);\n    background: var(--fragment-background);\n    color: var(--fragment-foreground);\n    overflow-x: auto;\n}\n\n@media screen and (max-width: 767px) {\n    div.fragment, pre.fragment {\n        border-top-right-radius: 0;\n        border-bottom-right-radius: 0;\n        border-right: 0;\n    }\n\n    .contents > div.fragment,\n    .textblock > div.fragment,\n    .textblock > pre.fragment,\n    .textblock > .tabbed > ul > li > div.fragment,\n    .textblock > .tabbed > ul > li > pre.fragment,\n    .contents > .doxygen-awesome-fragment-wrapper > div.fragment,\n    .textblock > .doxygen-awesome-fragment-wrapper > div.fragment,\n    .textblock > .doxygen-awesome-fragment-wrapper > pre.fragment,\n    .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > div.fragment,\n    .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > pre.fragment {\n        margin: var(--spacing-medium) calc(0px - var(--spacing-large));\n        border-radius: 0;\n        border-left: 0;\n    }\n\n    .textblock li > .fragment,\n    .textblock li > .doxygen-awesome-fragment-wrapper > .fragment {\n        margin: var(--spacing-medium) calc(0px - var(--spacing-large));\n    }\n\n    .memdoc li > .fragment,\n    .memdoc li > .doxygen-awesome-fragment-wrapper > .fragment {\n        margin: var(--spacing-medium) calc(0px - var(--spacing-medium));\n    }\n\n    .textblock ul, .memdoc ul {\n        overflow: initial;\n    }\n\n    .memdoc > div.fragment,\n    .memdoc > pre.fragment,\n    dl dd > div.fragment,\n    dl dd pre.fragment,\n    .memdoc > .doxygen-awesome-fragment-wrapper > div.fragment,\n    .memdoc > .doxygen-awesome-fragment-wrapper > pre.fragment,\n    dl dd > .doxygen-awesome-fragment-wrapper > div.fragment,\n    dl dd .doxygen-awesome-fragment-wrapper > pre.fragment {\n        margin: var(--spacing-medium) calc(0px - var(--spacing-medium));\n        border-radius: 0;\n        border-left: 0;\n    }\n}\n\ncode, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span {\n    font-family: var(--font-family-monospace);\n    font-size: var(--code-font-size) !important;\n}\n\ndiv.line:after {\n    margin-right: var(--spacing-medium);\n}\n\ndiv.fragment .line, pre.fragment {\n    white-space: pre;\n    word-wrap: initial;\n    line-height: var(--fragment-lineheight);\n}\n\ndiv.fragment span.keyword {\n    color: var(--fragment-keyword);\n}\n\ndiv.fragment span.keywordtype {\n    color: var(--fragment-keywordtype);\n}\n\ndiv.fragment span.keywordflow {\n    color: var(--fragment-keywordflow);\n}\n\ndiv.fragment span.stringliteral {\n    color: var(--fragment-token)\n}\n\ndiv.fragment span.comment {\n    color: var(--fragment-comment);\n}\n\ndiv.fragment a.code {\n    color: var(--fragment-link) !important;\n}\n\ndiv.fragment span.preprocessor {\n    color: var(--fragment-preprocessor);\n}\n\ndiv.fragment span.lineno {\n    display: inline-block;\n    width: 27px;\n    border-right: none;\n    background: var(--fragment-linenumber-background);\n    color: var(--fragment-linenumber-color);\n}\n\ndiv.fragment span.lineno a {\n    background: none;\n    color: var(--fragment-link) !important;\n}\n\ndiv.fragment > .line:first-child .lineno {\n    box-shadow: -999999px 0px 0 999999px var(--fragment-linenumber-background), -999998px 0px 0 999999px var(--fragment-linenumber-border);\n    background-color: var(--fragment-linenumber-background) !important;\n}\n\ndiv.line {\n    border-radius: var(--border-radius-small);\n}\n\ndiv.line.glow {\n    background-color: var(--primary-light-color);\n    box-shadow: none;\n}\n\n/*\n dl warning, attention, note, deprecated, bug, ...\n */\n\ndl.bug dt a, dl.deprecated dt a, dl.todo dt a {\n    font-weight: bold !important;\n}\n\ndl.warning, dl.attention, dl.note, dl.deprecated, dl.bug, dl.invariant, dl.pre, dl.post, dl.todo, dl.remark {\n    padding: var(--spacing-medium);\n    margin: var(--spacing-medium) 0;\n    color: var(--page-background-color);\n    overflow: hidden;\n    margin-left: 0;\n    border-radius: var(--border-radius-small);\n}\n\ndl.section dd {\n    margin-bottom: 2px;\n}\n\ndl.warning, dl.attention {\n    background: var(--warning-color);\n    border-left: 8px solid var(--warning-color-dark);\n    color: var(--warning-color-darker);\n}\n\ndl.warning dt, dl.attention dt {\n    color: var(--warning-color-dark);\n}\n\ndl.note, dl.remark {\n    background: var(--note-color);\n    border-left: 8px solid var(--note-color-dark);\n    color: var(--note-color-darker);\n}\n\ndl.note dt, dl.remark dt {\n    color: var(--note-color-dark);\n}\n\ndl.todo {\n    background: var(--todo-color);\n    border-left: 8px solid var(--todo-color-dark);\n    color: var(--todo-color-darker);\n}\n\ndl.todo dt a {\n    color: var(--todo-color-dark) !important;\n}\n\ndl.bug dt a {\n    color: var(--todo-color-dark) !important;\n}\n\ndl.bug {\n    background: var(--bug-color);\n    border-left: 8px solid var(--bug-color-dark);\n    color: var(--bug-color-darker);\n}\n\ndl.bug dt a {\n    color: var(--bug-color-dark) !important;\n}\n\ndl.deprecated {\n    background: var(--deprecated-color);\n    border-left: 8px solid var(--deprecated-color-dark);\n    color: var(--deprecated-color-darker);\n}\n\ndl.deprecated dt a {\n    color: var(--deprecated-color-dark) !important;\n}\n\ndl.section dd, dl.bug dd, dl.deprecated dd, dl.todo dd {\n    margin-inline-start: 0px;\n}\n\ndl.invariant, dl.pre, dl.post {\n    background: var(--invariant-color);\n    border-left: 8px solid var(--invariant-color-dark);\n    color: var(--invariant-color-darker);\n}\n\ndl.invariant dt, dl.pre dt, dl.post dt {\n    color: var(--invariant-color-dark);\n}\n\n/*\n memitem\n */\n\ndiv.memdoc, div.memproto, h2.memtitle {\n    box-shadow: none;\n    background-image: none;\n    border: none;\n}\n\ndiv.memdoc {\n    padding: 0 var(--spacing-medium);\n    background: var(--page-background-color);\n}\n\nh2.memtitle, div.memitem {\n    border: 1px solid var(--separator-color);\n    box-shadow: var(--box-shadow);\n}\n\nh2.memtitle {\n    box-shadow: 0px var(--spacing-medium) 0 -1px var(--fragment-background), var(--box-shadow);\n}\n\ndiv.memitem {\n    transition: none;\n}\n\ndiv.memproto, h2.memtitle {\n    background: var(--fragment-background);\n}\n\nh2.memtitle {\n    font-weight: 500;\n    font-size: var(--memtitle-font-size);\n    font-family: var(--font-family-monospace);\n    border-bottom: none;\n    border-top-left-radius: var(--border-radius-medium);\n    border-top-right-radius: var(--border-radius-medium);\n    word-break: break-all;\n    position: relative;\n}\n\nh2.memtitle:after {\n    content: \"\";\n    display: block;\n    background: var(--fragment-background);\n    height: var(--spacing-medium);\n    bottom: calc(0px - var(--spacing-medium));\n    left: 0;\n    right: -14px;\n    position: absolute;\n    border-top-right-radius: var(--border-radius-medium);\n}\n\nh2.memtitle > span.permalink {\n    font-size: inherit;\n}\n\nh2.memtitle > span.permalink > a {\n    text-decoration: none;\n    padding-left: 3px;\n    margin-right: -4px;\n    user-select: none;\n    display: inline-block;\n    margin-top: -6px;\n}\n\nh2.memtitle > span.permalink > a:hover {\n    color: var(--primary-dark-color) !important;\n}\n\na:target + h2.memtitle, a:target + h2.memtitle + div.memitem {\n    border-color: var(--primary-light-color);\n}\n\ndiv.memitem {\n    border-top-right-radius: var(--border-radius-medium);\n    border-bottom-right-radius: var(--border-radius-medium);\n    border-bottom-left-radius: var(--border-radius-medium);\n    overflow: hidden;\n    display: block !important;\n}\n\ndiv.memdoc {\n    border-radius: 0;\n}\n\ndiv.memproto {\n    border-radius: 0 var(--border-radius-small) 0 0;\n    overflow: auto;\n    border-bottom: 1px solid var(--separator-color);\n    padding: var(--spacing-medium);\n    margin-bottom: -1px;\n}\n\ndiv.memtitle {\n    border-top-right-radius: var(--border-radius-medium);\n    border-top-left-radius: var(--border-radius-medium);\n}\n\ndiv.memproto table.memname {\n    font-family: var(--font-family-monospace);\n    color: var(--page-foreground-color);\n    font-size: var(--memname-font-size);\n    text-shadow: none;\n}\n\ndiv.memproto div.memtemplate {\n    font-family: var(--font-family-monospace);\n    color: var(--primary-dark-color);\n    font-size: var(--memname-font-size);\n    margin-left: 2px;\n    text-shadow: none;\n}\n\ntable.mlabels, table.mlabels > tbody {\n    display: block;\n}\n\ntd.mlabels-left {\n    width: auto;\n}\n\ntd.mlabels-right {\n    margin-top: 3px;\n    position: sticky;\n    left: 0;\n}\n\ntable.mlabels > tbody > tr:first-child {\n    display: flex;\n    justify-content: space-between;\n    flex-wrap: wrap;\n}\n\n.memname, .memitem span.mlabels {\n    margin: 0\n}\n\n/*\n reflist\n */\n\ndl.reflist {\n    box-shadow: var(--box-shadow);\n    border-radius: var(--border-radius-medium);\n    border: 1px solid var(--separator-color);\n    overflow: hidden;\n    padding: 0;\n}\n\n\ndl.reflist dt, dl.reflist dd {\n    box-shadow: none;\n    text-shadow: none;\n    background-image: none;\n    border: none;\n    padding: 12px;\n}\n\n\ndl.reflist dt {\n    font-weight: 500;\n    border-radius: 0;\n    background: var(--code-background);\n    border-bottom: 1px solid var(--separator-color);\n    color: var(--page-foreground-color)\n}\n\n\ndl.reflist dd {\n    background: none;\n}\n\n/*\n Table\n */\n\n.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname),\n.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody {\n    display: inline-block;\n    max-width: 100%;\n}\n\n.contents > table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname):not(.classindex) {\n    margin-left: calc(0px - var(--spacing-large));\n    margin-right: calc(0px - var(--spacing-large));\n    max-width: calc(100% + 2 * var(--spacing-large));\n}\n\ntable.fieldtable,\ntable.markdownTable tbody,\ntable.doxtable tbody {\n    border: none;\n    margin: var(--spacing-medium) 0;\n    box-shadow: 0 0 0 1px var(--separator-color);\n    border-radius: var(--border-radius-small);\n}\n\ntable.markdownTable, table.doxtable, table.fieldtable {\n    padding: 1px;\n}\n\ntable.doxtable caption {\n    display: block;\n}\n\ntable.fieldtable {\n    border-collapse: collapse;\n    width: 100%;\n}\n\nth.markdownTableHeadLeft,\nth.markdownTableHeadRight,\nth.markdownTableHeadCenter,\nth.markdownTableHeadNone,\ntable.doxtable th {\n    background: var(--tablehead-background);\n    color: var(--tablehead-foreground);\n    font-weight: 600;\n    font-size: var(--page-font-size);\n}\n\nth.markdownTableHeadLeft:first-child,\nth.markdownTableHeadRight:first-child,\nth.markdownTableHeadCenter:first-child,\nth.markdownTableHeadNone:first-child,\ntable.doxtable tr th:first-child {\n    border-top-left-radius: var(--border-radius-small);\n}\n\nth.markdownTableHeadLeft:last-child,\nth.markdownTableHeadRight:last-child,\nth.markdownTableHeadCenter:last-child,\nth.markdownTableHeadNone:last-child,\ntable.doxtable tr th:last-child {\n    border-top-right-radius: var(--border-radius-small);\n}\n\ntable.markdownTable td,\ntable.markdownTable th,\ntable.fieldtable td,\ntable.fieldtable th,\ntable.doxtable td,\ntable.doxtable th {\n    border: 1px solid var(--separator-color);\n    padding: var(--spacing-small) var(--spacing-medium);\n}\n\ntable.markdownTable td:last-child,\ntable.markdownTable th:last-child,\ntable.fieldtable td:last-child,\ntable.fieldtable th:last-child,\ntable.doxtable td:last-child,\ntable.doxtable th:last-child {\n    border-right: none;\n}\n\ntable.markdownTable td:first-child,\ntable.markdownTable th:first-child,\ntable.fieldtable td:first-child,\ntable.fieldtable th:first-child,\ntable.doxtable td:first-child,\ntable.doxtable th:first-child {\n    border-left: none;\n}\n\ntable.markdownTable tr:first-child td,\ntable.markdownTable tr:first-child th,\ntable.fieldtable tr:first-child td,\ntable.fieldtable tr:first-child th,\ntable.doxtable tr:first-child td,\ntable.doxtable tr:first-child th {\n    border-top: none;\n}\n\ntable.markdownTable tr:last-child td,\ntable.markdownTable tr:last-child th,\ntable.fieldtable tr:last-child td,\ntable.fieldtable tr:last-child th,\ntable.doxtable tr:last-child td,\ntable.doxtable tr:last-child th {\n    border-bottom: none;\n}\n\ntable.markdownTable tr, table.doxtable tr {\n    border-bottom: 1px solid var(--separator-color);\n}\n\ntable.markdownTable tr:last-child, table.doxtable tr:last-child {\n    border-bottom: none;\n}\n\n.full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) {\n    display: block;\n}\n\n.full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody {\n    display: table;\n    width: 100%;\n}\n\ntable.fieldtable th {\n    font-size: var(--page-font-size);\n    font-weight: 600;\n    background-image: none;\n    background-color: var(--tablehead-background);\n    color: var(--tablehead-foreground);\n}\n\ntable.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fieldinit, .fieldtable td.fielddoc, .fieldtable th {\n    border-bottom: 1px solid var(--separator-color);\n    border-right: 1px solid var(--separator-color);\n}\n\ntable.fieldtable tr:last-child td:first-child {\n    border-bottom-left-radius: var(--border-radius-small);\n}\n\ntable.fieldtable tr:last-child td:last-child {\n    border-bottom-right-radius: var(--border-radius-small);\n}\n\n.memberdecls td.glow, .fieldtable tr.glow {\n    background-color: var(--primary-light-color);\n    box-shadow: none;\n}\n\ntable.memberdecls {\n    display: block;\n    -webkit-tap-highlight-color: transparent;\n}\n\ntable.memberdecls tr[class^='memitem'] {\n    font-family: var(--font-family-monospace);\n    font-size: var(--code-font-size);\n}\n\ntable.memberdecls tr[class^='memitem'] .memTemplParams {\n    font-family: var(--font-family-monospace);\n    font-size: var(--code-font-size);\n    color: var(--primary-dark-color);\n    white-space: normal;\n}\n\ntable.memberdecls .memItemLeft,\ntable.memberdecls .memItemRight,\ntable.memberdecls .memTemplItemLeft,\ntable.memberdecls .memTemplItemRight,\ntable.memberdecls .memTemplParams {\n    transition: none;\n    padding-top: var(--spacing-small);\n    padding-bottom: var(--spacing-small);\n    border-top: 1px solid var(--separator-color);\n    border-bottom: 1px solid var(--separator-color);\n    background-color: var(--fragment-background);\n}\n\ntable.memberdecls .memTemplItemLeft,\ntable.memberdecls .memTemplItemRight {\n    padding-top: 2px;\n}\n\ntable.memberdecls .memTemplParams {\n    border-bottom: 0;\n    border-left: 1px solid var(--separator-color);\n    border-right: 1px solid var(--separator-color);\n    border-radius: var(--border-radius-small) var(--border-radius-small) 0 0;\n    padding-bottom: var(--spacing-small);\n}\n\ntable.memberdecls .memTemplItemLeft {\n    border-radius: 0 0 0 var(--border-radius-small);\n    border-left: 1px solid var(--separator-color);\n    border-top: 0;\n}\n\ntable.memberdecls .memTemplItemRight {\n    border-radius: 0 0 var(--border-radius-small) 0;\n    border-right: 1px solid var(--separator-color);\n    padding-left: 0;\n    border-top: 0;\n}\n\ntable.memberdecls .memItemLeft {\n    border-radius: var(--border-radius-small) 0 0 var(--border-radius-small);\n    border-left: 1px solid var(--separator-color);\n    padding-left: var(--spacing-medium);\n    padding-right: 0;\n}\n\ntable.memberdecls .memItemRight  {\n    border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;\n    border-right: 1px solid var(--separator-color);\n    padding-right: var(--spacing-medium);\n    padding-left: 0;\n\n}\n\ntable.memberdecls .mdescLeft, table.memberdecls .mdescRight {\n    background: none;\n    color: var(--page-foreground-color);\n    padding: var(--spacing-small) 0;\n}\n\ntable.memberdecls .memItemLeft,\ntable.memberdecls .memTemplItemLeft {\n    padding-right: var(--spacing-medium);\n}\n\ntable.memberdecls .memSeparator {\n    background: var(--page-background-color);\n    height: var(--spacing-large);\n    border: 0;\n    transition: none;\n}\n\ntable.memberdecls .groupheader {\n    margin-bottom: var(--spacing-large);\n}\n\ntable.memberdecls .inherit_header td {\n    padding: 0 0 var(--spacing-medium) 0;\n    text-indent: -12px;\n    color: var(--page-secondary-foreground-color);\n}\n\ntable.memberdecls img[src=\"closed.png\"],\ntable.memberdecls img[src=\"open.png\"],\ndiv.dynheader img[src=\"open.png\"],\ndiv.dynheader img[src=\"closed.png\"] {\n    width: 0;\n    height: 0;\n    border-left: 4px solid transparent;\n    border-right: 4px solid transparent;\n    border-top: 5px solid var(--primary-color);\n    margin-top: 8px;\n    display: block;\n    float: left;\n    margin-left: -10px;\n    transition: transform var(--animation-duration) ease-out;\n}\n\ntable.memberdecls img {\n    margin-right: 10px;\n}\n\ntable.memberdecls img[src=\"closed.png\"],\ndiv.dynheader img[src=\"closed.png\"] {\n    transform: rotate(-90deg);\n}\n\n.compoundTemplParams {\n    font-family: var(--font-family-monospace);\n    color: var(--primary-dark-color);\n    font-size: var(--code-font-size);\n}\n\n@media screen and (max-width: 767px) {\n\n    table.memberdecls .memItemLeft,\n    table.memberdecls .memItemRight,\n    table.memberdecls .mdescLeft,\n    table.memberdecls .mdescRight,\n    table.memberdecls .memTemplItemLeft,\n    table.memberdecls .memTemplItemRight,\n    table.memberdecls .memTemplParams {\n        display: block;\n        text-align: left;\n        padding-left: var(--spacing-large);\n        margin: 0 calc(0px - var(--spacing-large)) 0 calc(0px - var(--spacing-large));\n        border-right: none;\n        border-left: none;\n        border-radius: 0;\n        white-space: normal;\n    }\n\n    table.memberdecls .memItemLeft,\n    table.memberdecls .mdescLeft,\n    table.memberdecls .memTemplItemLeft {\n        border-bottom: 0;\n        padding-bottom: 0;\n    }\n\n    table.memberdecls .memTemplItemLeft {\n        padding-top: 0;\n    }\n\n    table.memberdecls .mdescLeft {\n        margin-bottom: calc(0px - var(--page-font-size));\n    }\n\n    table.memberdecls .memItemRight,\n    table.memberdecls .mdescRight,\n    table.memberdecls .memTemplItemRight {\n        border-top: 0;\n        padding-top: 0;\n        padding-right: var(--spacing-large);\n        overflow-x: auto;\n    }\n\n    table.memberdecls tr[class^='memitem']:not(.inherit) {\n        display: block;\n        width: calc(100vw - 2 * var(--spacing-large));\n    }\n\n    table.memberdecls .mdescRight {\n        color: var(--page-foreground-color);\n    }\n\n    table.memberdecls tr.inherit {\n        visibility: hidden;\n    }\n\n    table.memberdecls tr[style=\"display: table-row;\"] {\n        display: block !important;\n        visibility: visible;\n        width: calc(100vw - 2 * var(--spacing-large));\n        animation: fade .5s;\n    }\n\n    @keyframes fade {\n        0% {\n            opacity: 0;\n            max-height: 0;\n        }\n\n        100% {\n            opacity: 1;\n            max-height: 200px;\n        }\n    }\n}\n\n\n/*\n Horizontal Rule\n */\n\nhr {\n    margin-top: var(--spacing-large);\n    margin-bottom: var(--spacing-large);\n    height: 1px;\n    background-color: var(--separator-color);\n    border: 0;\n}\n\n.contents hr {\n    box-shadow: 100px 0 var(--separator-color),\n                -100px 0 var(--separator-color),\n                500px 0 var(--separator-color),\n                -500px 0 var(--separator-color),\n                900px 0 var(--separator-color),\n                -900px 0 var(--separator-color),\n                1400px 0 var(--separator-color),\n                -1400px 0 var(--separator-color),\n                1900px 0 var(--separator-color),\n                -1900px 0 var(--separator-color);\n}\n\n.contents img, .contents .center, .contents center, .contents div.image object {\n    max-width: 100%;\n    overflow: auto;\n}\n\n@media screen and (max-width: 767px) {\n    .contents .dyncontent > .center, .contents > center {\n        margin-left: calc(0px - var(--spacing-large));\n        margin-right: calc(0px - var(--spacing-large));\n        max-width: calc(100% + 2 * var(--spacing-large));\n    }\n}\n\n/*\n Directories\n */\ndiv.directory {\n    border-top: 1px solid var(--separator-color);\n    border-bottom: 1px solid var(--separator-color);\n    width: auto;\n}\n\ntable.directory {\n    font-family: var(--font-family);\n    font-size: var(--page-font-size);\n    font-weight: normal;\n    width: 100%;\n}\n\ntable.directory td.entry, table.directory td.desc {\n    padding: calc(var(--spacing-small) / 2) var(--spacing-small);\n    line-height: var(--table-line-height);\n}\n\ntable.directory tr.even td:last-child {\n    border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;\n}\n\ntable.directory tr.even td:first-child {\n    border-radius: var(--border-radius-small) 0 0 var(--border-radius-small);\n}\n\ntable.directory tr.even:last-child td:last-child {\n    border-radius: 0 var(--border-radius-small) 0 0;\n}\n\ntable.directory tr.even:last-child td:first-child {\n    border-radius: var(--border-radius-small) 0 0 0;\n}\n\ntable.directory td.desc {\n    min-width: 250px;\n}\n\ntable.directory tr.even {\n    background-color: var(--odd-color);\n}\n\ntable.directory tr.odd {\n    background-color: transparent;\n}\n\n.icona {\n    width: auto;\n    height: auto;\n    margin: 0 var(--spacing-small);\n}\n\n.icon {\n    background: var(--primary-color);\n    border-radius: var(--border-radius-small);\n    font-size: var(--page-font-size);\n    padding: calc(var(--page-font-size) / 5);\n    line-height: var(--page-font-size);\n    transform: scale(0.8);\n    height: auto;\n    width: var(--page-font-size);\n    user-select: none;\n}\n\n.iconfopen, .icondoc, .iconfclosed {\n    background-position: center;\n    margin-bottom: 0;\n    height: var(--table-line-height);\n}\n\n.icondoc {\n    filter: saturate(0.2);\n}\n\n@media screen and (max-width: 767px) {\n    div.directory {\n        margin-left: calc(0px - var(--spacing-large));\n        margin-right: calc(0px - var(--spacing-large));\n    }\n}\n\n@media (prefers-color-scheme: dark) {\n    html:not(.light-mode) .iconfopen, html:not(.light-mode) .iconfclosed {\n        filter: hue-rotate(180deg) invert();\n    }\n}\n\nhtml.dark-mode .iconfopen, html.dark-mode .iconfclosed {\n    filter: hue-rotate(180deg) invert();\n}\n\n/*\n Class list\n */\n\n.classindex dl.odd {\n    background: var(--odd-color);\n    border-radius: var(--border-radius-small);\n}\n\n.classindex dl.even {\n    background-color: transparent;\n}\n\n/*\n Class Index Doxygen 1.8\n*/\n\ntable.classindex {\n    margin-left: 0;\n    margin-right: 0;\n    width: 100%;\n}\n\ntable.classindex table div.ah {\n    background-image: none;\n    background-color: initial;\n    border-color: var(--separator-color);\n    color: var(--page-foreground-color);\n    box-shadow: var(--box-shadow);\n    border-radius: var(--border-radius-large);\n    padding: var(--spacing-small);\n}\n\ndiv.qindex {\n    background-color: var(--odd-color);\n    border-radius: var(--border-radius-small);\n    border: 1px solid var(--separator-color);\n    padding: var(--spacing-small) 0;\n}\n\n/*\n  Footer and nav-path\n */\n\n#nav-path {\n    width: 100%;\n}\n\n#nav-path ul {\n    background-image: none;\n    background: var(--page-background-color);\n    border: none;\n    border-top: 1px solid var(--separator-color);\n    border-bottom: 1px solid var(--separator-color);\n    border-bottom: 0;\n    box-shadow: 0 0.75px 0 var(--separator-color);\n    font-size: var(--navigation-font-size);\n}\n\nimg.footer {\n    width: 60px;\n}\n\n.navpath li.footer {\n    color: var(--page-secondary-foreground-color);\n}\n\naddress.footer {\n    color: var(--page-secondary-foreground-color);\n    margin-bottom: var(--spacing-large);\n}\n\n#nav-path li.navelem {\n    background-image: none;\n    display: flex;\n    align-items: center;\n}\n\n.navpath li.navelem a {\n    text-shadow: none;\n    display: inline-block;\n    color: var(--primary-color) !important;\n}\n\n.navpath li.navelem b {\n    color: var(--primary-dark-color);\n    font-weight: 500;\n}\n\nli.navelem {\n    padding: 0;\n    margin-left: -8px;\n}\n\nli.navelem:first-child {\n    margin-left: var(--spacing-large);\n}\n\nli.navelem:first-child:before {\n    display: none;\n}\n\n#nav-path li.navelem:after {\n    content: '';\n    border: 5px solid var(--page-background-color);\n    border-bottom-color: transparent;\n    border-right-color: transparent;\n    border-top-color: transparent;\n    transform: translateY(-1px) scaleY(4.2);\n    z-index: 10;\n    margin-left: 6px;\n}\n\n#nav-path li.navelem:before {\n    content: '';\n    border: 5px solid var(--separator-color);\n    border-bottom-color: transparent;\n    border-right-color: transparent;\n    border-top-color: transparent;\n    transform: translateY(-1px) scaleY(3.2);\n    margin-right: var(--spacing-small);\n}\n\n.navpath li.navelem a:hover {\n    color: var(--primary-color);\n}\n\n/*\n Scrollbars for Webkit\n*/\n\n#nav-tree::-webkit-scrollbar,\ndiv.fragment::-webkit-scrollbar,\npre.fragment::-webkit-scrollbar,\ndiv.memproto::-webkit-scrollbar,\n.contents center::-webkit-scrollbar,\n.contents .center::-webkit-scrollbar,\n.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar,\ndiv.contents .toc::-webkit-scrollbar,\n.contents .dotgraph::-webkit-scrollbar,\n.contents .tabs-overview-container::-webkit-scrollbar {\n    background: transparent;\n    width: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding));\n    height: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding));\n}\n\n#nav-tree::-webkit-scrollbar-thumb,\ndiv.fragment::-webkit-scrollbar-thumb,\npre.fragment::-webkit-scrollbar-thumb,\ndiv.memproto::-webkit-scrollbar-thumb,\n.contents center::-webkit-scrollbar-thumb,\n.contents .center::-webkit-scrollbar-thumb,\n.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-thumb,\ndiv.contents .toc::-webkit-scrollbar-thumb,\n.contents .dotgraph::-webkit-scrollbar-thumb,\n.contents .tabs-overview-container::-webkit-scrollbar-thumb {\n    background-color: transparent;\n    border: var(--webkit-scrollbar-padding) solid transparent;\n    border-radius: calc(var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding));\n    background-clip: padding-box;\n}\n\n#nav-tree:hover::-webkit-scrollbar-thumb,\ndiv.fragment:hover::-webkit-scrollbar-thumb,\npre.fragment:hover::-webkit-scrollbar-thumb,\ndiv.memproto:hover::-webkit-scrollbar-thumb,\n.contents center:hover::-webkit-scrollbar-thumb,\n.contents .center:hover::-webkit-scrollbar-thumb,\n.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody:hover::-webkit-scrollbar-thumb,\ndiv.contents .toc:hover::-webkit-scrollbar-thumb,\n.contents .dotgraph:hover::-webkit-scrollbar-thumb,\n.contents .tabs-overview-container:hover::-webkit-scrollbar-thumb {\n    background-color: var(--webkit-scrollbar-color);\n}\n\n#nav-tree::-webkit-scrollbar-track,\ndiv.fragment::-webkit-scrollbar-track,\npre.fragment::-webkit-scrollbar-track,\ndiv.memproto::-webkit-scrollbar-track,\n.contents center::-webkit-scrollbar-track,\n.contents .center::-webkit-scrollbar-track,\n.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-track,\ndiv.contents .toc::-webkit-scrollbar-track,\n.contents .dotgraph::-webkit-scrollbar-track,\n.contents .tabs-overview-container::-webkit-scrollbar-track {\n    background: transparent;\n}\n\n#nav-tree::-webkit-scrollbar-corner {\n    background-color: var(--side-nav-background);\n}\n\n#nav-tree,\ndiv.fragment,\npre.fragment,\ndiv.memproto,\n.contents center,\n.contents .center,\n.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody,\ndiv.contents .toc {\n    overflow-x: auto;\n    overflow-x: overlay;\n}\n\n#nav-tree {\n    overflow-x: auto;\n    overflow-y: auto;\n    overflow-y: overlay;\n}\n\n/*\n Scrollbars for Firefox\n*/\n\n#nav-tree,\ndiv.fragment,\npre.fragment,\ndiv.memproto,\n.contents center,\n.contents .center,\n.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody,\ndiv.contents .toc,\n.contents .dotgraph,\n.contents .tabs-overview-container {\n    scrollbar-width: thin;\n}\n\n/*\n  Optional Dark mode toggle button\n*/\n\ndoxygen-awesome-dark-mode-toggle {\n    display: inline-block;\n    margin: 0 0 0 var(--spacing-small);\n    padding: 0;\n    width: var(--searchbar-height);\n    height: var(--searchbar-height);\n    background: none;\n    border: none;\n    border-radius: var(--searchbar-height);\n    vertical-align: middle;\n    text-align: center;\n    line-height: var(--searchbar-height);\n    font-size: 22px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    user-select: none;\n    cursor: pointer;\n}\n\ndoxygen-awesome-dark-mode-toggle > svg {\n    transition: transform var(--animation-duration) ease-in-out;\n}\n\ndoxygen-awesome-dark-mode-toggle:active > svg {\n    transform: scale(.5);\n}\n\ndoxygen-awesome-dark-mode-toggle:hover {\n    background-color: rgba(0,0,0,.03);\n}\n\nhtml.dark-mode doxygen-awesome-dark-mode-toggle:hover {\n    background-color: rgba(0,0,0,.18);\n}\n\n/*\n Optional fragment copy button\n*/\n.doxygen-awesome-fragment-wrapper {\n    position: relative;\n}\n\ndoxygen-awesome-fragment-copy-button {\n    opacity: 0;\n    background: var(--fragment-background);\n    width: 28px;\n    height: 28px;\n    position: absolute;\n    right: calc(var(--spacing-large) - (var(--spacing-large) / 2.5));\n    top: calc(var(--spacing-large) - (var(--spacing-large) / 2.5));\n    border: 1px solid var(--fragment-foreground);\n    cursor: pointer;\n    border-radius: var(--border-radius-small);\n    display: flex;\n    justify-content: center;\n    align-items: center;\n}\n\n.doxygen-awesome-fragment-wrapper:hover doxygen-awesome-fragment-copy-button, doxygen-awesome-fragment-copy-button.success {\n    opacity: .28;\n}\n\ndoxygen-awesome-fragment-copy-button:hover, doxygen-awesome-fragment-copy-button.success {\n    opacity: 1 !important;\n}\n\ndoxygen-awesome-fragment-copy-button:active:not([class~=success]) svg {\n    transform: scale(.91);\n}\n\ndoxygen-awesome-fragment-copy-button svg {\n    fill: var(--fragment-foreground);\n    width: 18px;\n    height: 18px;\n}\n\ndoxygen-awesome-fragment-copy-button.success svg {\n    fill: rgb(14, 168, 14);\n}\n\ndoxygen-awesome-fragment-copy-button.success {\n    border-color: rgb(14, 168, 14);\n}\n\n@media screen and (max-width: 767px) {\n    .textblock > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button,\n    .textblock li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button,\n    .memdoc li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button,\n    .memdoc > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button,\n    dl dd > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button {\n        right: 0;\n    }\n}\n\n/*\n Optional paragraph link button\n*/\n\na.anchorlink {\n    font-size: 90%;\n    margin-left: var(--spacing-small);\n    color: var(--page-foreground-color) !important;\n    text-decoration: none;\n    opacity: .15;\n    display: none;\n    transition: opacity var(--animation-duration) ease-in-out, color var(--animation-duration) ease-in-out;\n}\n\na.anchorlink svg {\n    fill: var(--page-foreground-color);\n}\n\nh3 a.anchorlink svg, h4 a.anchorlink svg {\n    margin-bottom: -3px;\n    margin-top: -4px;\n}\n\na.anchorlink:hover {\n    opacity: .45;\n}\n\nh2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a.anchorlink  {\n    display: inline-block;\n}\n\n/*\n Optional tab feature\n*/\n\n.tabbed > ul {\n    padding-inline-start: 0px;\n    margin: 0;\n    padding: var(--spacing-small) 0;\n}\n\n.tabbed > ul > li {\n    display: none;\n}\n\n.tabbed > ul > li.selected {\n    display: block;\n}\n\n.tabs-overview-container {\n    overflow-x: auto;\n    display: block;\n    overflow-y: visible;\n}\n\n.tabs-overview {\n    border-bottom: 1px solid var(--separator-color);\n    display: flex;\n    flex-direction: row;\n}\n\n@media screen and (max-width: 767px) {\n    .tabs-overview-container {\n        margin: 0 calc(0px - var(--spacing-large));\n    }\n    .tabs-overview {\n        padding: 0 var(--spacing-large)\n    }\n}\n\n.tabs-overview button.tab-button {\n    color: var(--page-foreground-color);\n    margin: 0;\n    border: none;\n    background: transparent;\n    padding: calc(var(--spacing-large) / 2) 0;\n    display: inline-block;\n    font-size: var(--page-font-size);\n    cursor: pointer;\n    box-shadow: 0 1px 0 0 var(--separator-color);\n    position: relative;\n\n    -webkit-tap-highlight-color: transparent;\n}\n\n.tabs-overview button.tab-button .tab-title::before {\n    display: block;\n    content: attr(title);\n    font-weight: 600;\n    height: 0;\n    overflow: hidden;\n    visibility: hidden;\n}\n\n.tabs-overview button.tab-button .tab-title {\n    float: left;\n    white-space: nowrap;\n    font-weight: normal;\n    padding: calc(var(--spacing-large) / 2) var(--spacing-large);\n    border-radius: var(--border-radius-medium);\n    transition: background-color var(--animation-duration) ease-in-out, font-weight var(--animation-duration) ease-in-out;\n}\n\n.tabs-overview button.tab-button:not(:last-child) .tab-title {\n    box-shadow: 8px 0 0 -7px var(--separator-color);\n}\n\n.tabs-overview button.tab-button:hover .tab-title {\n    background: var(--separator-color);\n    box-shadow: none;\n}\n\n.tabs-overview button.tab-button.active .tab-title {\n    font-weight: 600;\n}\n\n.tabs-overview button.tab-button::after {\n    content: '';\n    display: block;\n    position: absolute;\n    left: 0;\n    bottom: 0;\n    right: 0;\n    height: 0;\n    width: 0%;\n    margin: 0 auto;\n    border-radius: var(--border-radius-small) var(--border-radius-small) 0 0;\n    background-color: var(--primary-color);\n    transition: width var(--animation-duration) ease-in-out, height var(--animation-duration) ease-in-out;\n}\n\n.tabs-overview button.tab-button.active::after {\n    width: 100%;\n    box-sizing: border-box;\n    height: 3px;\n}\n\n\n/*\n Navigation Buttons\n*/\n\n.section_buttons:not(:empty) {\n    margin-top: calc(var(--spacing-large) * 3);\n}\n\n.section_buttons table.markdownTable {\n    display: block;\n    width: 100%;\n}\n\n.section_buttons table.markdownTable tbody {\n    display: table !important;\n    width: 100%;\n    box-shadow: none;\n    border-spacing: 10px;\n}\n\n.section_buttons table.markdownTable td {\n    padding: 0;\n}\n\n.section_buttons table.markdownTable th {\n    display: none;\n}\n\n.section_buttons table.markdownTable tr.markdownTableHead {\n    border: none;\n}\n\n.section_buttons tr th, .section_buttons tr td {\n    background: none;\n    border: none;\n    padding: var(--spacing-large) 0 var(--spacing-small);\n}\n\n.section_buttons a {\n    display: inline-block;\n    border: 1px solid var(--separator-color);\n    border-radius: var(--border-radius-medium);\n    color: var(--page-secondary-foreground-color) !important;\n    text-decoration: none;\n    transition: color var(--animation-duration) ease-in-out, background-color var(--animation-duration) ease-in-out;\n}\n\n.section_buttons a:hover {\n    color: var(--page-foreground-color) !important;\n    background-color: var(--odd-color);\n}\n\n.section_buttons tr td.markdownTableBodyLeft a {\n    padding: var(--spacing-medium) var(--spacing-large) var(--spacing-medium) calc(var(--spacing-large) / 2);\n}\n\n.section_buttons tr td.markdownTableBodyRight a {\n    padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large);\n}\n\n.section_buttons tr td.markdownTableBodyLeft a::before,\n.section_buttons tr td.markdownTableBodyRight a::after {\n    color: var(--page-secondary-foreground-color) !important;\n    display: inline-block;\n    transition: color .08s ease-in-out, transform .09s ease-in-out;\n}\n\n.section_buttons tr td.markdownTableBodyLeft a::before {\n    content: '〈';\n    padding-right: var(--spacing-large);\n}\n\n\n.section_buttons tr td.markdownTableBodyRight a::after {\n    content: '〉';\n    padding-left: var(--spacing-large);\n}\n\n\n.section_buttons tr td.markdownTableBodyLeft a:hover::before {\n    color: var(--page-foreground-color) !important;\n    transform: translateX(-3px);\n}\n\n.section_buttons tr td.markdownTableBodyRight a:hover::after {\n    color: var(--page-foreground-color) !important;\n    transform: translateX(3px);\n}\n\n@media screen and (max-width: 450px) {\n    .section_buttons a {\n        width: 100%;\n        box-sizing: border-box;\n    }\n\n    .section_buttons tr td:nth-of-type(1).markdownTableBodyLeft a {\n        border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium);\n        border-right: none;\n    }\n\n    .section_buttons tr td:nth-of-type(2).markdownTableBodyRight a {\n        border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0;\n    }\n}\n"
  },
  {
    "path": "docs/config/footer.html",
    "content": "<!-- HTML footer for doxygen 1.9.1-->\n<!-- start footer part -->\n<!--BEGIN GENERATE_TREEVIEW-->\n<div id=\"nav-path\" class=\"navpath\"><!-- id is needed for treeview function! -->\n  <ul>\n    $navpath\n    <li class=\"footer\">\n    &copy; Copyright 2017 - 2026\n    <a href=\"https://black-holes.org\">SXS Collaboration</a>,\n    <a href=\"LICENSE.txt\" target=\"_blank\">\n    <span class=\"hidden-xs\">Distributed under the</span>\n    MIT License</a>\n  </ul>\n</div>\n<!--END GENERATE_TREEVIEW-->\n<!--BEGIN !GENERATE_TREEVIEW-->\n<hr class=\"footer\"/><address class=\"footer\"><small>\n&copy; Copyright 2017 - 2026\n<a href=\"https://black-holes.org\">SXS Collaboration</a>,\n<a href=\"LICENSE.txt\" target=\"_blank\">\n<span class=\"hidden-xs\">Distributed under the</span>\nMIT License</a>\n</small></address>\n<!--END !GENERATE_TREEVIEW-->\n</body>\n</html>\n"
  },
  {
    "path": "docs/config/header.html",
    "content": "<!-- HTML header for doxygen 1.9.1-->\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n  <head>\n    <meta http-equiv=\"Content-Type\" content=\"text/xhtml;charset=UTF-8\"/>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=9\"/>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n    <meta name=\"author\" content=\"SXS Collaboration\">\n    <meta name=\"generator\" content=\"Doxygen $doxygenversion\"/>\n  <!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->\n  <!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->\n  <link href=\"$relpath^tabs.css\" rel=\"stylesheet\" type=\"text/css\"/>\n  <script type=\"text/javascript\" src=\"$relpath^jquery.js\"></script>\n  <script type=\"text/javascript\" src=\"$relpath^dynsections.js\"></script>\n  $treeview\n  $search\n  $mathjax\n  <link href=\"$relpath^octicons.css\" rel=\"stylesheet\">\n  <link href=\"$relpath^$stylesheet\" rel=\"stylesheet\" type=\"text/css\" />\n  <link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"$relpath^apple-touch-icon.png\">\n  <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"$relpath^favicon-32x32.png\">\n  <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"$relpath^favicon-16x16.png\">\n  <link rel=\"mask-icon\" href=\"$relpath^safari-pinned-tab.svg\">\n  $extrastylesheet\n  <script src=\"https://unpkg.com/@popperjs/core@2\"></script>\n  <script src=\"https://unpkg.com/tippy.js@6\"></script>\n  <script type=\"text/javascript\" src=\"$relpath^spectre.js\"></script>\n  <script type=\"text/javascript\" src=\"$relpath^doxygen-awesome-fragment-copy-button.js\"></script>\n  <script type=\"text/javascript\" src=\"$relpath^doxygen-awesome-interactive-toc.js\"></script>\n  <script type=\"text/javascript\" src=\"$relpath^doxygen-awesome-paragraph-link.js\"></script>\n  <script type=\"text/javascript\">\n    DoxygenAwesomeFragmentCopyButton.init()\n    DoxygenAwesomeInteractiveToc.init()\n    DoxygenAwesomeParagraphLink.init()\n  </script>\n  </head>\n  <body>\n  <div id=\"top\"><!-- do not remove this div, it is closed by doxygen! -->\n\n  <!--BEGIN TITLEAREA-->\n  <div id=\"titlearea\">\n    <table cellspacing=\"0\" cellpadding=\"0\">\n      <tbody>\n        <tr style=\"height: 56px;\">\n          <!--BEGIN PROJECT_LOGO-->\n          <td id=\"projectlogo\"><img alt=\"Logo\" src=\"$relpath^$projectlogo\" /></td>\n          <!--END PROJECT_LOGO-->\n          <!--BEGIN PROJECT_NAME-->\n          <td id=\"projectalign\" style=\"padding-left: 0.5em;\">\n            <div id=\"projectname\">\n              <a href=\"index.html\">$projectname</a>\n              <!--BEGIN PROJECT_NUMBER-->&#160;<span id=\"projectnumber\">v$projectnumber</span>\n              <!--END PROJECT_NUMBER-->\n            </div>\n            <!--BEGIN PROJECT_BRIEF-->\n            <div id=\"projectbrief\">$projectbrief</div>\n            <!--END PROJECT_BRIEF-->\n          </td>\n          <!--END PROJECT_NAME-->\n          <!--BEGIN !PROJECT_NAME-->\n          <!--BEGIN PROJECT_BRIEF-->\n          <td style=\"padding-left: 0.5em;\">\n            <div id=\"projectbrief\">$projectbrief</div>\n          </td>\n          <!--END PROJECT_BRIEF-->\n          <!--END !PROJECT_NAME-->\n          <!--BEGIN DISABLE_INDEX-->\n          <!--BEGIN SEARCHENGINE-->\n          <td>$searchbox</td>\n          <!--END SEARCHENGINE-->\n          <!--END DISABLE_INDEX-->\n        </tr>\n      </tbody>\n    </table>\n  </div>\n  <!--END TITLEAREA-->\n  <!-- end header part -->\n"
  },
  {
    "path": "docs/config/icons/octicons.css",
    "content": "@font-face {\n  font-family: 'octicons';\n  src: url('octicons.eot?#iefix') format('embedded-opentype'),\n       url('octicons.woff') format('woff'),\n       url('octicons.ttf') format('truetype'),\n       url('octicons.svg#octicons') format('svg');\n  font-weight: normal;\n  font-style: normal;\n}\n\n/*\n\n.octicon is optimized for 16px.\n.mega-octicon is optimized for 32px but can be used larger.\n\n*/\n.octicon, .mega-octicon {\n  font: normal normal normal 16px/1 octicons;\n  display: inline-block;\n  text-decoration: none;\n  text-rendering: auto;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n.mega-octicon { font-size: 32px; }\n\n.octicon-alert:before { content: '\\f02d'} /*  */\n.octicon-arrow-down:before { content: '\\f03f'} /*  */\n.octicon-arrow-left:before { content: '\\f040'} /*  */\n.octicon-arrow-right:before { content: '\\f03e'} /*  */\n.octicon-arrow-small-down:before { content: '\\f0a0'} /*  */\n.octicon-arrow-small-left:before { content: '\\f0a1'} /*  */\n.octicon-arrow-small-right:before { content: '\\f071'} /*  */\n.octicon-arrow-small-up:before { content: '\\f09f'} /*  */\n.octicon-arrow-up:before { content: '\\f03d'} /*  */\n.octicon-microscope:before,\n.octicon-beaker:before { content: '\\f0dd'} /*  */\n.octicon-bell:before { content: '\\f0de'} /*  */\n.octicon-bold:before { content: '\\f0e2'} /*  */\n.octicon-book:before { content: '\\f007'} /*  */\n.octicon-bookmark:before { content: '\\f07b'} /*  */\n.octicon-briefcase:before { content: '\\f0d3'} /*  */\n.octicon-broadcast:before { content: '\\f048'} /*  */\n.octicon-browser:before { content: '\\f0c5'} /*  */\n.octicon-bug:before { content: '\\f091'} /*  */\n.octicon-calendar:before { content: '\\f068'} /*  */\n.octicon-check:before { content: '\\f03a'} /*  */\n.octicon-checklist:before { content: '\\f076'} /*  */\n.octicon-chevron-down:before { content: '\\f0a3'} /*  */\n.octicon-chevron-left:before { content: '\\f0a4'} /*  */\n.octicon-chevron-right:before { content: '\\f078'} /*  */\n.octicon-chevron-up:before { content: '\\f0a2'} /*  */\n.octicon-circle-slash:before { content: '\\f084'} /*  */\n.octicon-circuit-board:before { content: '\\f0d6'} /*  */\n.octicon-clippy:before { content: '\\f035'} /*  */\n.octicon-clock:before { content: '\\f046'} /*  */\n.octicon-cloud-download:before { content: '\\f00b'} /*  */\n.octicon-cloud-upload:before { content: '\\f00c'} /*  */\n.octicon-code:before { content: '\\f05f'} /*  */\n.octicon-color-mode:before { content: '\\f065'} /*  */\n.octicon-comment-add:before,\n.octicon-comment:before { content: '\\f02b'} /*  */\n.octicon-comment-discussion:before { content: '\\f04f'} /*  */\n.octicon-credit-card:before { content: '\\f045'} /*  */\n.octicon-dash:before { content: '\\f0ca'} /*  */\n.octicon-dashboard:before { content: '\\f07d'} /*  */\n.octicon-database:before { content: '\\f096'} /*  */\n.octicon-clone:before,\n.octicon-desktop-download:before { content: '\\f0dc'} /*  */\n.octicon-device-camera:before { content: '\\f056'} /*  */\n.octicon-device-camera-video:before { content: '\\f057'} /*  */\n.octicon-device-desktop:before { content: '\\f27c'} /*  */\n.octicon-device-mobile:before { content: '\\f038'} /*  */\n.octicon-diff:before { content: '\\f04d'} /*  */\n.octicon-diff-added:before { content: '\\f06b'} /*  */\n.octicon-diff-ignored:before { content: '\\f099'} /*  */\n.octicon-diff-modified:before { content: '\\f06d'} /*  */\n.octicon-diff-removed:before { content: '\\f06c'} /*  */\n.octicon-diff-renamed:before { content: '\\f06e'} /*  */\n.octicon-ellipsis:before { content: '\\f09a'} /*  */\n.octicon-eye-unwatch:before,\n.octicon-eye-watch:before,\n.octicon-eye:before { content: '\\f04e'} /*  */\n.octicon-file-binary:before { content: '\\f094'} /*  */\n.octicon-file-code:before { content: '\\f010'} /*  */\n.octicon-file-directory:before { content: '\\f016'} /*  */\n.octicon-file-media:before { content: '\\f012'} /*  */\n.octicon-file-pdf:before { content: '\\f014'} /*  */\n.octicon-file-submodule:before { content: '\\f017'} /*  */\n.octicon-file-symlink-directory:before { content: '\\f0b1'} /*  */\n.octicon-file-symlink-file:before { content: '\\f0b0'} /*  */\n.octicon-file-text:before { content: '\\f011'} /*  */\n.octicon-file-zip:before { content: '\\f013'} /*  */\n.octicon-flame:before { content: '\\f0d2'} /*  */\n.octicon-fold:before { content: '\\f0cc'} /*  */\n.octicon-gear:before { content: '\\f02f'} /*  */\n.octicon-gift:before { content: '\\f042'} /*  */\n.octicon-gist:before { content: '\\f00e'} /*  */\n.octicon-gist-secret:before { content: '\\f08c'} /*  */\n.octicon-git-branch-create:before,\n.octicon-git-branch-delete:before,\n.octicon-git-branch:before { content: '\\f020'} /*  */\n.octicon-git-commit:before { content: '\\f01f'} /*  */\n.octicon-git-compare:before { content: '\\f0ac'} /*  */\n.octicon-git-merge:before { content: '\\f023'} /*  */\n.octicon-git-pull-request-abandoned:before,\n.octicon-git-pull-request:before { content: '\\f009'} /*  */\n.octicon-globe:before { content: '\\f0b6'} /*  */\n.octicon-graph:before { content: '\\f043'} /*  */\n.octicon-heart:before { content: '\\2665'} /* ♥ */\n.octicon-history:before { content: '\\f07e'} /*  */\n.octicon-home:before { content: '\\f08d'} /*  */\n.octicon-horizontal-rule:before { content: '\\f070'} /*  */\n.octicon-hubot:before { content: '\\f09d'} /*  */\n.octicon-inbox:before { content: '\\f0cf'} /*  */\n.octicon-info:before { content: '\\f059'} /*  */\n.octicon-issue-closed:before { content: '\\f028'} /*  */\n.octicon-issue-opened:before { content: '\\f026'} /*  */\n.octicon-issue-reopened:before { content: '\\f027'} /*  */\n.octicon-italic:before { content: '\\f0e4'} /*  */\n.octicon-jersey:before { content: '\\f019'} /*  */\n.octicon-key:before { content: '\\f049'} /*  */\n.octicon-keyboard:before { content: '\\f00d'} /*  */\n.octicon-law:before { content: '\\f0d8'} /*  */\n.octicon-light-bulb:before { content: '\\f000'} /*  */\n.octicon-link:before { content: '\\f05c'} /*  */\n.octicon-link-external:before { content: '\\f07f'} /*  */\n.octicon-list-ordered:before { content: '\\f062'} /*  */\n.octicon-list-unordered:before { content: '\\f061'} /*  */\n.octicon-location:before { content: '\\f060'} /*  */\n.octicon-gist-private:before,\n.octicon-mirror-private:before,\n.octicon-git-fork-private:before,\n.octicon-lock:before { content: '\\f06a'} /*  */\n.octicon-logo-gist:before { content: '\\f0ad'} /*  */\n.octicon-logo-github:before { content: '\\f092'} /*  */\n.octicon-mail:before { content: '\\f03b'} /*  */\n.octicon-mail-read:before { content: '\\f03c'} /*  */\n.octicon-mail-reply:before { content: '\\f051'} /*  */\n.octicon-mark-github:before { content: '\\f00a'} /*  */\n.octicon-markdown:before { content: '\\f0c9'} /*  */\n.octicon-megaphone:before { content: '\\f077'} /*  */\n.octicon-mention:before { content: '\\f0be'} /*  */\n.octicon-milestone:before { content: '\\f075'} /*  */\n.octicon-mirror-public:before,\n.octicon-mirror:before { content: '\\f024'} /*  */\n.octicon-mortar-board:before { content: '\\f0d7'} /*  */\n.octicon-mute:before { content: '\\f080'} /*  */\n.octicon-no-newline:before { content: '\\f09c'} /*  */\n.octicon-octoface:before { content: '\\f008'} /*  */\n.octicon-organization:before { content: '\\f037'} /*  */\n.octicon-package:before { content: '\\f0c4'} /*  */\n.octicon-paintcan:before { content: '\\f0d1'} /*  */\n.octicon-pencil:before { content: '\\f058'} /*  */\n.octicon-person-add:before,\n.octicon-person-follow:before,\n.octicon-person:before { content: '\\f018'} /*  */\n.octicon-pin:before { content: '\\f041'} /*  */\n.octicon-plug:before { content: '\\f0d4'} /*  */\n.octicon-repo-create:before,\n.octicon-gist-new:before,\n.octicon-file-directory-create:before,\n.octicon-file-add:before,\n.octicon-plus:before { content: '\\f05d'} /*  */\n.octicon-primitive-dot:before { content: '\\f052'} /*  */\n.octicon-primitive-square:before { content: '\\f053'} /*  */\n.octicon-pulse:before { content: '\\f085'} /*  */\n.octicon-question:before { content: '\\f02c'} /*  */\n.octicon-quote:before { content: '\\f063'} /*  */\n.octicon-radio-tower:before { content: '\\f030'} /*  */\n.octicon-repo-delete:before,\n.octicon-repo:before { content: '\\f001'} /*  */\n.octicon-repo-clone:before { content: '\\f04c'} /*  */\n.octicon-repo-force-push:before { content: '\\f04a'} /*  */\n.octicon-gist-fork:before,\n.octicon-repo-forked:before { content: '\\f002'} /*  */\n.octicon-repo-pull:before { content: '\\f006'} /*  */\n.octicon-repo-push:before { content: '\\f005'} /*  */\n.octicon-rocket:before { content: '\\f033'} /*  */\n.octicon-rss:before { content: '\\f034'} /*  */\n.octicon-ruby:before { content: '\\f047'} /*  */\n.octicon-search-save:before,\n.octicon-search:before { content: '\\f02e'} /*  */\n.octicon-server:before { content: '\\f097'} /*  */\n.octicon-settings:before { content: '\\f07c'} /*  */\n.octicon-shield:before { content: '\\f0e1'} /*  */\n.octicon-log-in:before,\n.octicon-sign-in:before { content: '\\f036'} /*  */\n.octicon-log-out:before,\n.octicon-sign-out:before { content: '\\f032'} /*  */\n.octicon-squirrel:before { content: '\\f0b2'} /*  */\n.octicon-star-add:before,\n.octicon-star-delete:before,\n.octicon-star:before { content: '\\f02a'} /*  */\n.octicon-stop:before { content: '\\f08f'} /*  */\n.octicon-repo-sync:before,\n.octicon-sync:before { content: '\\f087'} /*  */\n.octicon-tag-remove:before,\n.octicon-tag-add:before,\n.octicon-tag:before { content: '\\f015'} /*  */\n.octicon-tasklist:before { content: '\\f0e5'} /*  */\n.octicon-telescope:before { content: '\\f088'} /*  */\n.octicon-terminal:before { content: '\\f0c8'} /*  */\n.octicon-text-size:before { content: '\\f0e3'} /*  */\n.octicon-three-bars:before { content: '\\f05e'} /*  */\n.octicon-thumbsdown:before { content: '\\f0db'} /*  */\n.octicon-thumbsup:before { content: '\\f0da'} /*  */\n.octicon-tools:before { content: '\\f031'} /*  */\n.octicon-trashcan:before { content: '\\f0d0'} /*  */\n.octicon-triangle-down:before { content: '\\f05b'} /*  */\n.octicon-triangle-left:before { content: '\\f044'} /*  */\n.octicon-triangle-right:before { content: '\\f05a'} /*  */\n.octicon-triangle-up:before { content: '\\f0aa'} /*  */\n.octicon-unfold:before { content: '\\f039'} /*  */\n.octicon-unmute:before { content: '\\f0ba'} /*  */\n.octicon-versions:before { content: '\\f064'} /*  */\n.octicon-watch:before { content: '\\f0e0'} /*  */\n.octicon-remove-close:before,\n.octicon-x:before { content: '\\f081'} /*  */\n.octicon-zap:before { content: '\\26A1'} /* ⚡ */\n"
  },
  {
    "path": "docs/config/js/doxygen-navtree-hacks.js",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is mostly copied from:\n// https://gitlab.com/libeigen/eigen/-/blob/master/doc/eigen_navtree_hacks.js\n\nvar global_navtree_object;\n\n// Overloaded to remove links to sections/subsections\nfunction getNode(o, po) {\n    po.childrenVisited = true;\n    var l = po.childrenData.length - 1;\n    for (var i in po.childrenData) {\n        var nodeData = po.childrenData[i];\n        if ((!nodeData[1]) || (nodeData[1].indexOf('#') == -1)) // <- we added this line\n            po.children[i] = newNode(o, po, nodeData[0], nodeData[1], nodeData[2], i == l);\n    }\n}\n\n// Overloaded to remove the animation of the sidebar\nfunction expandNode(o, node, imm, showRoot) {\n    if (node.childrenData && !node.expanded) {\n        if (typeof (node.childrenData) === 'string') {\n            var varName = node.childrenData;\n            getScript(node.relpath + varName, function () {\n                node.childrenData = getData(varName);\n                expandNode(o, node, imm, showRoot);\n            }, showRoot);\n        } else {\n            if (!node.childrenVisited) {\n                getNode(o, node);\n            }\n            $(node.getChildrenUL()).show(); // <- removed slideDown animation\n            // node.plus_img.innerHTML = arrowDown; // <- removed this\n            node.expanded = true;\n        }\n    }\n}\n\n// Overloaded to save the root node into global_navtree_object\nfunction initNavTree(toroot, relpath) {\n    var o = new Object();\n    global_navtree_object = o; // <- we added this line\n    o.toroot = toroot;\n    o.node = new Object();\n    o.node.li = document.getElementById(\"nav-tree-contents\");\n    o.node.childrenData = NAVTREE;\n    o.node.children = new Array();\n    o.node.childrenUL = document.createElement(\"ul\");\n    o.node.getChildrenUL = function () { return o.node.childrenUL; };\n    o.node.li.appendChild(o.node.childrenUL);\n    o.node.depth = 0;\n    o.node.relpath = relpath;\n    o.node.expanded = false;\n    o.node.isLast = true;\n    o.node.plus_img = document.createElement(\"img\");\n    o.node.plus_img.src = relpath + \"ftv2pnode.png\";\n    o.node.plus_img.width = 16;\n    o.node.plus_img.height = 22;\n\n    if (localStorageSupported()) {\n        var navSync = $('#nav-sync');\n        if (cachedLink()) {\n            showSyncOff(navSync, relpath);\n            navSync.removeClass('sync');\n        } else {\n            showSyncOn(navSync, relpath);\n        }\n        navSync.click(function () { toggleSyncButton(relpath); });\n    }\n\n    navTo(o, toroot, window.location.hash, relpath);\n\n    $(window).bind('hashchange', function () {\n        if (window.location.hash && window.location.hash.length > 1) {\n            var a;\n            if ($(location).attr('hash')) {\n                var clslink = stripPath($(location).attr('pathname')) + ':' +\n                    $(location).attr('hash').substring(1);\n                a = $('.item a[class$=\"' + clslink + '\"]');\n            }\n            if (a == null || !$(a).parent().parent().hasClass('selected')) {\n                $('.item').removeClass('selected');\n                $('.item').removeAttr('id');\n            }\n            var link = stripPath2($(location).attr('pathname'));\n            navTo(o, link, $(location).attr('hash'), relpath);\n        } else if (!animationInProgress) {\n            $('#doc-content').scrollTop(0);\n            $('.item').removeClass('selected');\n            $('.item').removeAttr('id');\n            navTo(o, toroot, window.location.hash, relpath);\n        }\n    })\n\n    $(window).on(\"load\", showRoot);\n}\n\n// return false if the the node has no children at all, or has only section/subsection children\nfunction checkChildrenData(node) {\n    if (!(typeof (node.childrenData) === 'string')) {\n        for (var i in node.childrenData) {\n            var url = node.childrenData[i][1];\n            if (url.indexOf(\"#\") == -1)\n                return true;\n        }\n        return false;\n    }\n    return (node.childrenData);\n}\n\n// Modified to:\n// 1 - remove the root node\n// 2 - remove the section/subsection children\nfunction createIndent(o, domNode, node, level) {\n    var level = -2; // <- we replaced level=-1 by level=-2\n    var n = node;\n    while (n.parentNode) { level++; n = n.parentNode; }\n    if (checkChildrenData(node)) { // <- we modified this line to use checkChildrenData(node) instead of node.childrenData\n        var imgNode = document.createElement(\"span\");\n        imgNode.className = 'arrow';\n        imgNode.style.paddingLeft = (16 * level).toString() + 'px';\n        imgNode.innerHTML = arrowRight;\n        node.plus_img = imgNode;\n        node.expandToggle = document.createElement(\"a\");\n        node.expandToggle.href = \"javascript:void(0)\";\n        node.expandToggle.onclick = function () {\n            if (node.expanded) {\n                $(node.getChildrenUL()).slideUp(\"fast\");\n                node.plus_img.innerHTML = arrowRight;\n                node.expanded = false;\n            } else {\n                expandNode(o, node, false, false);\n            }\n        }\n        node.expandToggle.appendChild(imgNode);\n        domNode.appendChild(node.expandToggle);\n    } else {\n        var span = document.createElement(\"span\");\n        span.className = 'arrow';\n        span.style.width = 16 * (level + 1) + 'px';\n        span.innerHTML = '&#160;';\n        domNode.appendChild(span);\n    }\n}\n\n// Overloaded to automatically expand the selected node\nfunction selectAndHighlight(hash, n) {\n    var a;\n    if (hash) {\n        var link = stripPath($(location).attr('pathname')) + ':' + hash.substring(1);\n        a = $('.item a[class$=\"' + link + '\"]');\n    }\n    if (a && a.length) {\n        a.parent().parent().addClass('selected');\n        a.parent().parent().attr('id', 'selected');\n        highlightAnchor();\n    } else if (n) {\n        $(n.itemDiv).addClass('selected');\n        $(n.itemDiv).attr('id', 'selected');\n    }\n    if ($('#nav-tree-contents .item:first').hasClass('selected')) {\n        $('#nav-sync').css('top', '30px');\n    } else {\n        $('#nav-sync').css('top', '5px');\n    }\n    expandNode(global_navtree_object, n, true, true); // <- we added this line\n    showRoot();\n}\n\n\n$(document).ready(function () {\n    (function () { // wait until the first \"selected\" element has been created\n        try {\n\n            // this line will triger an exception if there is no #selected element, i.e., before the tree structure is complete.\n            document.getElementById(\"selected\").className = \"item selected\";\n\n            // ok, the default tree has been created, we can keep going...\n\n            // expand the \"Code reference\" node\n            var sidebar_root_items = global_navtree_object.node.children[0].children;\n            expandNode(global_navtree_object, sidebar_root_items[sidebar_root_items.length - 1], true, true);\n\n            // Hide the root node\n            $(document.getElementsByClassName('index.html')[0]).parent().parent().css({ display: \"none\" });\n\n        } catch (err) {\n            setTimeout(arguments.callee, 10);\n        }\n    })();\n});\n\n"
  },
  {
    "path": "docs/config/js/spectre.js",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nwindow.onload = function(){\n\n    // Hide type alias RHS that refers to a metafunction result in a (*_)detail namespace\n    $(\"body\").children().\n    find(\"td.memItemRight, td.memTemplItemRight, td.memname\").each(function () {\n        $(this).html( $(this).html().replace(/( =[ ]+)[typedef ]*typename [A-Za-z0-9]*[_]?detail[s]?::[A-Za-z0-9\\-_\\.&;<> /;:\\\"=,#]+::[A-Za-z0-9_]+/g,\n            \"$1implementation defined\") );\n    });\n\n    // Hide type alias RHS that refers to a type in a (*_)detail namespace\n    // Also remove ... = decltype(something(_detail)::)\n    $(\"body\").children().find(\"td.memItemRight, td.memTemplItemRight, td.memname\").each(function () {\n        $(this).html( $(this).html().replace(/( =[ ]+)[typedef ]*[A-Za-z0-9\\(]*[_]?detail[s]?::[A-Za-z0-9\\-_\\.&;<> /;:\\\"=,#\\(\\)]+/g,\n            \"$1implementation defined\") );\n    });\n\n    // Hide unnamed template parameters: , typename = void\n    // Not applied to div.memitem because that causes problems with rendering\n    // MathJAX\n    $(\"body\").children().find(\"td.memItemRight, td.memTemplItemRight\").each(function () {\n        $(this).html( $(this).html().replace(/(template[&lt; ]+.*)(,[ ]*typename[ ]*=[ ]*void[ ]*)+/g,\n            \"$1\") );\n    });\n    $(\"body\").children().find(\"td.memItemRight, td.memTemplItemRight, div.title\").each(function () {\n        $(this).html( $(this).html().replace(/(&lt;[A-Za-z0-9 ,]+.*)(,[ ]*typename[ ]*&gt;)+/g,\n            \"$1&gt;\") );\n    });\n\n    // Hide SFINAE in template parameters\n    $(\"body\").children().find(\"td.memTemplParams, div.memtemplate\").each(function () {\n        $(this).html( $(this).html().replace(/(template[A-Za-z0-9&;,\\.=\\(\\) _]+)(,[ ]+typename[ ]+=[ ]+typename[ ]+std::enable_if.*::type)+/g,\n            \"$1\") );\n\n    });\n\n    $(\"body\").children().find(\"td.memTemplParams, div.memtemplate\").each(function () {\n        $(this).html( $(this).html().replace(/(template[A-Za-z0-9&;,\\.=\\(\\) _]+)(,[ ]+typename[ ]+std::enable_if.*)+&gt;/g,\n            \"$1&gt;\") );\n\n    });\n\n    // Hide enable_if_t for SFINAE\n    $(\"body\").children().find(\"td.memTemplParams, div.memtemplate\").each(function () {\n        $(this).html( $(this).html()\n            .replace(/(template[A-Za-z0-9&;,\\.=\\(\\) _]+)(,[ ]+std::enable_if_t.*)+&gt;/g,\n                \"$1&gt;\") );\n\n    });\n\n    // Hide metafunctions that use only the template metaprogramming libraries\n    $(\"body\").children().find(\"td.memItemRight, td.memTemplItemRight\").each(function () {\n        $(this).html( $(this).html().replace(/( =[ ]+).*tmpl::(?!list\\b).*/g,\n            \"$1implementation defined\") );\n    });\n\n    // Italicize \"implementation defined\"\n    // Not applied to div.memitem because that causes problems with rendering\n    // MathJAX\n    $(\"body\").children().find(\"td.memItemRight, td.memTemplItemRight\").each(function () {\n        $(this).html( $(this).html().replace(/implementation defined/g,\n            \"implementation defined\".italics()) );\n    });\n\n    // Show popovers for references\n    $(\"body\").children().find('a[href*=\"citelist.html#CITEREF_\"]').each(function () {\n        var tooltip_base = this;\n        // Get the reference id, e.g. `CITEREF_Kopriva`. This is the one used as\n        // anchor on the bibliography page.\n        var ref_id = $(this).attr('href').match(/CITEREF_(.*)/)[0];\n        // Backslashify colons, which indicate CSS pseudo-selectors\n        ref_id = ref_id.replaceAll(\":\", \"\\\\:\");\n        // Load the bibliography page to retrieve the reference data as nicely\n        // formatted HTML.\n        // This does not work locally because it is forbidden to access files,\n        // so just spin up a web server in the `html` directory with\n        // `python3 -m http.server` to enable this functionality\n        $.get('citelist.html', function(data) {\n            // Filter the bibliography data for this particular reference\n            var ref_html = $('<citelist>').append($.parseHTML(data))\n                .find('#' + ref_id)\n                .parent()\n                .next()\n                .find('p.startdd')\n                .html();\n            // Show the reference in a tooltip\n            tippy(tooltip_base, {\n                content: ref_html,\n                allowHTML: true,\n                interactive: true,\n            });\n        }, 'html');\n    });\n};\n"
  },
  {
    "path": "docs/config/markdown-math-filter.pl",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Doxygen filter to format markdown math for Doxygen\n#\n# In Doxygen documentation, including markdown files or Jupyter notebooks that\n# get converted to markdown, you can use standard $...$ syntax for inline math,\n# and \"naked LaTeX\" syntax for display-style math:\n#\n# \\begin{equation}\n# ...\n# \\end{equation}\n#\n# You can also replace `equation` with another supported environment, e.g.\n# `align`.\n#\n# This Doxygen filter formats the math for Doxygen, i.e., uses \\f$...\\f$ for\n# inline math and \\f{equation}{ ... \\f} for display-style math. This is needed\n# until Doxygen adds support for markdown-style math, see\n# https://github.com/doxygen/doxygen/issues/8009.\n\n# Wrap inline math in \\f$...\\f$\n# - Match code blocks (wrapped in ``` or \\code...\\endcode) and inline code\n#   (wrapped in ` or ``), and print them directly to the output with no changes.\n# - Match display math blocks (wrapped in \\f[...\\f], \\f{...\\f}, or\n#   \\begin...\\end), and print them directly to the output with no changes.\n# - Replace $...$ by \\f$...\\f$, unless already preceded by '\\f$'.\n# - The '?' makes the pattern match lazily, i.e., match as few characters as\n#   possible.\n# - Modifiers:\n#     s: Allow '.' to match newlines\n#     g: Replace all occurences\n#     e: Evaluate replacement as perl code, so we can switch between\n#        replacements depending on the matched alternative, using the '//'\n#        (definedness) operator.\n#     x: Ignore spaces for better readability\ns{ (```.*?```)\n   | (``.*?``)\n   | (`.*?`)\n   | (\\\\code.*?\\\\endcode)\n   | (\\\\f\\[.*?\\\\f\\])\n   | (\\\\f\\{.*?\\\\f\\})\n   | (\\\\begin\\{(.*?)\\}(.*?)\\\\end\\{\\6\\})\n   | (?<!\\\\f)\\$(.*?)\\$\n}{ $1 // $2 // $3 // $4 // $5 // $6 // $7 // \"\\\\f\\$$10\\\\f\\$\" }sgex;\n\n# Wrap display math in \\f{equation}{ ... \\f}\n# - Match code blocks (wrapped in ``` or \\code...\\endcode) and Doxygen-style\n#   display equations formatted either \\f[...\\f] or \\f{...}{...\\f}, and print\n#   them to the output with no changes.\n# - Replace \\begin{...}...\\end{...} with \\f{...}{...\\f}.\n# - Modifiers: see above\ns{ (```.*?```)\n   | (\\\\code.*?\\\\endcode)\n   | (\\\\f\\[.*?\\\\f\\])\n   | (\\\\f\\{.*?\\\\f\\})\n   | \\\\begin\\{(.*?)\\}(.*?)\\\\end\\{\\5\\}\n}{ $1 // $2 // $3 // $4 // \"\\\\f\\{$5\\}\\{$6\\\\f\\}\" }sgex;\n"
  },
  {
    "path": "docs/config/octicons.css",
    "content": "@font-face {\n  font-family: 'octicons';\n  src: url('octicons.eot?#iefix') format('embedded-opentype'),\n       url('octicons.woff') format('woff'),\n       url('octicons.ttf') format('truetype'),\n       url('octicons.svg#octicons') format('svg');\n  font-weight: normal;\n  font-style: normal;\n}\n\n/*\n\n.octicon is optimized for 16px.\n.mega-octicon is optimized for 32px but can be used larger.\n\n*/\n.octicon, .mega-octicon {\n  font: normal normal normal 16px/1 octicons;\n  display: inline-block;\n  padding-right: 5px;\n  text-decoration: none;\n  text-rendering: auto;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n.mega-octicon { font-size: 32px; }\n\n.octicon-alert:before { content: '\\f02d'} /*  */\n.octicon-arrow-down:before { content: '\\f03f'} /*  */\n.octicon-arrow-left:before { content: '\\f040'} /*  */\n.octicon-arrow-right:before { content: '\\f03e'} /*  */\n.octicon-arrow-small-down:before { content: '\\f0a0'} /*  */\n.octicon-arrow-small-left:before { content: '\\f0a1'} /*  */\n.octicon-arrow-small-right:before { content: '\\f071'} /*  */\n.octicon-arrow-small-up:before { content: '\\f09f'} /*  */\n.octicon-arrow-up:before { content: '\\f03d'} /*  */\n.octicon-microscope:before,\n.octicon-beaker:before { content: '\\f0dd'} /*  */\n.octicon-bell:before { content: '\\f0de'} /*  */\n.octicon-bold:before { content: '\\f0e2'} /*  */\n.octicon-book:before { content: '\\f007'} /*  */\n.octicon-bookmark:before { content: '\\f07b'} /*  */\n.octicon-briefcase:before { content: '\\f0d3'} /*  */\n.octicon-broadcast:before { content: '\\f048'} /*  */\n.octicon-browser:before { content: '\\f0c5'} /*  */\n.octicon-bug:before { content: '\\f091'} /*  */\n.octicon-calendar:before { content: '\\f068'} /*  */\n.octicon-check:before { content: '\\f03a'} /*  */\n.octicon-checklist:before { content: '\\f076'} /*  */\n.octicon-chevron-down:before { content: '\\f0a3'} /*  */\n.octicon-chevron-left:before { content: '\\f0a4'} /*  */\n.octicon-chevron-right:before { content: '\\f078'} /*  */\n.octicon-chevron-up:before { content: '\\f0a2'} /*  */\n.octicon-circle-slash:before { content: '\\f084'} /*  */\n.octicon-circuit-board:before { content: '\\f0d6'} /*  */\n.octicon-clippy:before { content: '\\f035'} /*  */\n.octicon-clock:before { content: '\\f046'} /*  */\n.octicon-cloud-download:before { content: '\\f00b'} /*  */\n.octicon-cloud-upload:before { content: '\\f00c'} /*  */\n.octicon-code:before { content: '\\f05f'} /*  */\n.octicon-color-mode:before { content: '\\f065'} /*  */\n.octicon-comment-add:before,\n.octicon-comment:before { content: '\\f02b'} /*  */\n.octicon-comment-discussion:before { content: '\\f04f'} /*  */\n.octicon-credit-card:before { content: '\\f045'} /*  */\n.octicon-dash:before { content: '\\f0ca'} /*  */\n.octicon-dashboard:before { content: '\\f07d'} /*  */\n.octicon-database:before { content: '\\f096'} /*  */\n.octicon-clone:before,\n.octicon-desktop-download:before { content: '\\f0dc'} /*  */\n.octicon-device-camera:before { content: '\\f056'} /*  */\n.octicon-device-camera-video:before { content: '\\f057'} /*  */\n.octicon-device-desktop:before { content: '\\f27c'} /*  */\n.octicon-device-mobile:before { content: '\\f038'} /*  */\n.octicon-diff:before { content: '\\f04d'} /*  */\n.octicon-diff-added:before { content: '\\f06b'} /*  */\n.octicon-diff-ignored:before { content: '\\f099'} /*  */\n.octicon-diff-modified:before { content: '\\f06d'} /*  */\n.octicon-diff-removed:before { content: '\\f06c'} /*  */\n.octicon-diff-renamed:before { content: '\\f06e'} /*  */\n.octicon-ellipsis:before { content: '\\f09a'} /*  */\n.octicon-eye-unwatch:before,\n.octicon-eye-watch:before,\n.octicon-eye:before { content: '\\f04e'} /*  */\n.octicon-file-binary:before { content: '\\f094'} /*  */\n.octicon-file-code:before { content: '\\f010'} /*  */\n.octicon-file-directory:before { content: '\\f016'} /*  */\n.octicon-file-media:before { content: '\\f012'} /*  */\n.octicon-file-pdf:before { content: '\\f014'} /*  */\n.octicon-file-submodule:before { content: '\\f017'} /*  */\n.octicon-file-symlink-directory:before { content: '\\f0b1'} /*  */\n.octicon-file-symlink-file:before { content: '\\f0b0'} /*  */\n.octicon-file-text:before { content: '\\f011'} /*  */\n.octicon-file-zip:before { content: '\\f013'} /*  */\n.octicon-flame:before { content: '\\f0d2'} /*  */\n.octicon-fold:before { content: '\\f0cc'} /*  */\n.octicon-gear:before { content: '\\f02f'} /*  */\n.octicon-gift:before { content: '\\f042'} /*  */\n.octicon-gist:before { content: '\\f00e'} /*  */\n.octicon-gist-secret:before { content: '\\f08c'} /*  */\n.octicon-git-branch-create:before,\n.octicon-git-branch-delete:before,\n.octicon-git-branch:before { content: '\\f020'} /*  */\n.octicon-git-commit:before { content: '\\f01f'} /*  */\n.octicon-git-compare:before { content: '\\f0ac'} /*  */\n.octicon-git-merge:before { content: '\\f023'} /*  */\n.octicon-git-pull-request-abandoned:before,\n.octicon-git-pull-request:before { content: '\\f009'} /*  */\n.octicon-globe:before { content: '\\f0b6'} /*  */\n.octicon-graph:before { content: '\\f043'} /*  */\n.octicon-heart:before { content: '\\2665'} /* ♥ */\n.octicon-history:before { content: '\\f07e'} /*  */\n.octicon-home:before { content: '\\f08d'} /*  */\n.octicon-horizontal-rule:before { content: '\\f070'} /*  */\n.octicon-hubot:before { content: '\\f09d'} /*  */\n.octicon-inbox:before { content: '\\f0cf'} /*  */\n.octicon-info:before { content: '\\f059'} /*  */\n.octicon-issue-closed:before { content: '\\f028'} /*  */\n.octicon-issue-opened:before { content: '\\f026'} /*  */\n.octicon-issue-reopened:before { content: '\\f027'} /*  */\n.octicon-italic:before { content: '\\f0e4'} /*  */\n.octicon-jersey:before { content: '\\f019'} /*  */\n.octicon-key:before { content: '\\f049'} /*  */\n.octicon-keyboard:before { content: '\\f00d'} /*  */\n.octicon-law:before { content: '\\f0d8'} /*  */\n.octicon-light-bulb:before { content: '\\f000'} /*  */\n.octicon-link:before { content: '\\f05c'} /*  */\n.octicon-link-external:before { content: '\\f07f'} /*  */\n.octicon-list-ordered:before { content: '\\f062'} /*  */\n.octicon-list-unordered:before { content: '\\f061'} /*  */\n.octicon-location:before { content: '\\f060'} /*  */\n.octicon-gist-private:before,\n.octicon-mirror-private:before,\n.octicon-git-fork-private:before,\n.octicon-lock:before { content: '\\f06a'} /*  */\n.octicon-logo-github:before { content: '\\f092'} /*  */\n.octicon-mail:before { content: '\\f03b'} /*  */\n.octicon-mail-read:before { content: '\\f03c'} /*  */\n.octicon-mail-reply:before { content: '\\f051'} /*  */\n.octicon-mark-github:before { content: '\\f00a'} /*  */\n.octicon-markdown:before { content: '\\f0c9'} /*  */\n.octicon-megaphone:before { content: '\\f077'} /*  */\n.octicon-mention:before { content: '\\f0be'} /*  */\n.octicon-milestone:before { content: '\\f075'} /*  */\n.octicon-mirror-public:before,\n.octicon-mirror:before { content: '\\f024'} /*  */\n.octicon-mortar-board:before { content: '\\f0d7'} /*  */\n.octicon-mute:before { content: '\\f080'} /*  */\n.octicon-no-newline:before { content: '\\f09c'} /*  */\n.octicon-octoface:before { content: '\\f008'} /*  */\n.octicon-organization:before { content: '\\f037'} /*  */\n.octicon-package:before { content: '\\f0c4'} /*  */\n.octicon-paintcan:before { content: '\\f0d1'} /*  */\n.octicon-pencil:before { content: '\\f058'} /*  */\n.octicon-person-add:before,\n.octicon-person-follow:before,\n.octicon-person:before { content: '\\f018'} /*  */\n.octicon-pin:before { content: '\\f041'} /*  */\n.octicon-plug:before { content: '\\f0d4'} /*  */\n.octicon-repo-create:before,\n.octicon-gist-new:before,\n.octicon-file-directory-create:before,\n.octicon-file-add:before,\n.octicon-plus:before { content: '\\f05d'} /*  */\n.octicon-primitive-dot:before { content: '\\f052'} /*  */\n.octicon-primitive-square:before { content: '\\f053'} /*  */\n.octicon-pulse:before { content: '\\f085'} /*  */\n.octicon-question:before { content: '\\f02c'} /*  */\n.octicon-quote:before { content: '\\f063'} /*  */\n.octicon-radio-tower:before { content: '\\f030'} /*  */\n.octicon-repo-delete:before,\n.octicon-repo:before { content: '\\f001'} /*  */\n.octicon-repo-clone:before { content: '\\f04c'} /*  */\n.octicon-repo-force-push:before { content: '\\f04a'} /*  */\n.octicon-gist-fork:before,\n.octicon-repo-forked:before { content: '\\f002'} /*  */\n.octicon-repo-pull:before { content: '\\f006'} /*  */\n.octicon-repo-push:before { content: '\\f005'} /*  */\n.octicon-rocket:before { content: '\\f033'} /*  */\n.octicon-rss:before { content: '\\f034'} /*  */\n.octicon-ruby:before { content: '\\f047'} /*  */\n.octicon-search-save:before,\n.octicon-search:before { content: '\\f02e'} /*  */\n.octicon-server:before { content: '\\f097'} /*  */\n.octicon-settings:before { content: '\\f07c'} /*  */\n.octicon-shield:before { content: '\\f0e1'} /*  */\n.octicon-log-in:before,\n.octicon-sign-in:before { content: '\\f036'} /*  */\n.octicon-log-out:before,\n.octicon-sign-out:before { content: '\\f032'} /*  */\n.octicon-squirrel:before { content: '\\f0b2'} /*  */\n.octicon-star-add:before,\n.octicon-star-delete:before,\n.octicon-star:before { content: '\\f02a'} /*  */\n.octicon-stop:before { content: '\\f08f'} /*  */\n.octicon-repo-sync:before,\n.octicon-sync:before { content: '\\f087'} /*  */\n.octicon-tag-remove:before,\n.octicon-tag-add:before,\n.octicon-tag:before { content: '\\f015'} /*  */\n.octicon-tasklist:before { content: '\\f0e5'} /*  */\n.octicon-telescope:before { content: '\\f088'} /*  */\n.octicon-terminal:before { content: '\\f0c8'} /*  */\n.octicon-text-size:before { content: '\\f0e3'} /*  */\n.octicon-three-bars:before { content: '\\f05e'} /*  */\n.octicon-thumbsdown:before { content: '\\f0db'} /*  */\n.octicon-thumbsup:before { content: '\\f0da'} /*  */\n.octicon-tools:before { content: '\\f031'} /*  */\n.octicon-trashcan:before { content: '\\f0d0'} /*  */\n.octicon-triangle-down:before { content: '\\f05b'} /*  */\n.octicon-triangle-left:before { content: '\\f044'} /*  */\n.octicon-triangle-right:before { content: '\\f05a'} /*  */\n.octicon-triangle-up:before { content: '\\f0aa'} /*  */\n.octicon-unfold:before { content: '\\f039'} /*  */\n.octicon-unmute:before { content: '\\f0ba'} /*  */\n.octicon-versions:before { content: '\\f064'} /*  */\n.octicon-watch:before { content: '\\f0e0'} /*  */\n.octicon-remove-close:before,\n.octicon-x:before { content: '\\f081'} /*  */\n.octicon-zap:before { content: '\\26A1'} /* ⚡ */\n"
  },
  {
    "path": "docs/config/postprocess_docs.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport re\n\nimport pybtex.database\nfrom bs4 import BeautifulSoup\n\n\ndef append_eprint_links_to_citelist(html_dir, references_files):\n    logger = logging.getLogger(__name__)\n\n    # Load `citelist.html` file as structured HTML\n    with open(os.path.join(html_dir, \"citelist.html\"), \"r\") as citelist_file:\n        citelist = BeautifulSoup(citelist_file, \"html.parser\")\n    # Load `References.bib` as structured bibliography database\n    references = pybtex.database.parse_string(\n        \"\".join(\n            [\n                pybtex.database.parse_file(references_file).to_string(\"bibtex\")\n                for references_file in references_files\n            ]\n        ),\n        \"bibtex\",\n    )\n\n    for citeref_anchor in citelist.find_all(id=re.compile(\"CITEREF_*\")):\n        key = re.match(\"CITEREF_(.*)\", citeref_anchor.attrs[\"id\"]).groups()[0]\n        entry = references.entries[key]\n        # We support only arXiv eprints for now. More formats may be added here\n        # if necessary.\n        if (\n            \"archivePrefix\" in entry.fields\n            and entry.fields[\"archivePrefix\"] == \"arXiv\"\n            and \"eprint\" in entry.fields\n        ):\n            logger.debug(\"Found arXiv link for {}.\".format(key))\n            arxiv_url = \"https://arxiv.org/abs/{}\".format(\n                entry.fields[\"eprint\"]\n            )\n            eprint_link = citelist.new_tag(\"a\", href=arxiv_url)\n            eprint_link.string = \"arXiv:{}\".format(entry.fields[\"eprint\"])\n        else:\n            logger.debug(\"Found no supported eprint data for {}.\".format(key))\n            continue\n        # Append the eprint link to the citation HTML\n        citeref_item = citeref_anchor.find_next(\"p\")\n        citeref_item.append(eprint_link)\n        citeref_item.append(\".\")\n        logger.info(\"Added eprint link to {}: {}\".format(key, eprint_link))\n\n    # Write the `citelist.html` file back to disk\n    with open(os.path.join(html_dir, \"citelist.html\"), \"w\") as citelist_file:\n        citelist_file.write(str(citelist))\n\n\ndef parse_args():\n    \"\"\"\n    Parse the command line arguments\n    \"\"\"\n    import argparse as ap\n\n    parser = ap.ArgumentParser(description=\"\"\"\n        Postprocess the documentation generated by Doxygen. Currently only adds\n        eprint links to the bibliography.\n        \"\"\")\n    parser.add_argument(\n        \"--html-dir\",\n        required=True,\n        help=\"\"\"\n        Doxygen's HTML output directory. Files in this directory will be\n        modified by the postprocessing.\n        \"\"\",\n    )\n    parser.add_argument(\n        \"--references-files\",\n        required=True,\n        nargs=\"+\",\n        help=\"\"\"\n        One or more .bib file that Doxygen used to generate the bibliography.\n        \"\"\",\n    )\n    parser.add_argument(\n        \"-v\",\n        \"--verbose\",\n        action=\"count\",\n        default=0,\n        help=\"Verbosity (-v, -vv, ...)\",\n    )\n    return parser.parse_args()\n\n\nif __name__ == \"__main__\":\n    args = parse_args()\n\n    # Set the log level\n    logging.basicConfig(level=logging.WARNING - args.verbose * 10)\n\n    logger = logging.getLogger(__name__)\n    logger.info(\"Postprocessing docs HTML directory: {}\".format(args.html_dir))\n\n    logger.info(\"Appending eprint links to citelist...\")\n    append_eprint_links_to_citelist(args.html_dir, args.references_files)\n\n    logger.info(\"Done postprocessing docs.\")\n"
  },
  {
    "path": "docs/index.rst",
    "content": ".. Distributed under the MIT License.\n   See LICENSE.txt for details.\n\nThe SpECTRE Python interface\n============================\n\nThe SpECTRE Python interface exposes some SpECTRE functionality in Python. For\nexample, it helps with visualizing data, scheduling simulations, and working\nwith simulation output. To get started quickly, compile the ``all-pybindings``\n(or ``cli``) target and then try running the command-line interface (CLI):\n\n.. code-block:: sh\n\n   # In the build directory:\n   ./bin/spectre --help\n\nOr jump into a Jupyter notebook where you can import the ``spectre`` modules:\n\n.. code-block:: sh\n\n   # In the build directory:\n   ./bin/python-spectre -m jupyter notebook\n\n\nDocumentation\n-------------\n\n.. toctree::\n   :maxdepth: 2\n\n   Getting started <https://spectre-code.org/spectre_using_python.html>\n   C++ documentation <https://spectre-code.org>\n\n\n.. toctree::\n   :maxdepth: 3\n   :caption: Command-line interface (CLI)\n\n   cli\n\n\nPython modules\n--------------\n\n.. autosummary::\n   :toctree: _autosummary\n   :recursive:\n   :caption: Python modules\n\n   spectre\n"
  },
  {
    "path": "external/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(brigand)\nadd_subdirectory(EffectiveSource)\nadd_subdirectory(GravitationalEffectiveSource)\nadd_subdirectory(SPHEREPACK)\nadd_subdirectory(slatec)\n\nset(LIBRARY Libsharp)\nset(LIBSHARP_LIBDIR ${CMAKE_CURRENT_BINARY_DIR}/libsharp/auto/lib)\nset(LIBSHARP_LIB_libsharp ${LIBSHARP_LIBDIR}/libsharp.a)\nset(LIBSHARP_LIB_libfftpack ${LIBSHARP_LIBDIR}/libfftpack.a)\nset(LIBSHARP_LIB_libc_utils ${LIBSHARP_LIBDIR}/libc_utils.a)\nset(LIBSHARP_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/libsharp/auto/include)\n\nfind_program(MAKE NAMES gmake make REQUIRED)\nset(LIBSHARP_BUILD ${MAKE})\n# When using the Unix Makefile generator, verbosity is inherited from\n# the main build and works acceptably well.  There's no way to\n# dynamically detect the verbosity with ninja, so silence it\n# unconditionally because ninja is quieter than make.\nif (NOT CMAKE_GENERATOR STREQUAL \"Unix Makefiles\")\n  list(APPEND LIBSHARP_BUILD > /dev/null)\nendif()\n\nset(JOB_SERVER \"\")\nif (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)\n  set(JOB_SERVER BUILD_JOB_SERVER_AWARE TRUE)\nendif()\n\ninclude(ExternalProject)\nExternalProject_Add(\n  Libsharp-external\n  PREFIX ${CMAKE_BINARY_DIR}/external/libsharp\n  SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/libsharp\n  DOWNLOAD_COMMAND\n  cp -r ${CMAKE_CURRENT_SOURCE_DIR}/libsharp ${CMAKE_CURRENT_BINARY_DIR}\n  # libsharp has an autoconf build system, but it doesn't do anything\n  # except set CFLAGS, so we can skip it to avoid depending on\n  # autoconf and set the flags below by manually writing config.auto.\n  CONFIGURE_COMMAND \"\"\n  # out-of-tree builds don't work\n  BUILD_IN_SOURCE TRUE\n  BUILD_COMMAND \"${LIBSHARP_BUILD}\"\n  ${JOB_SERVER}\n  BUILD_BYPRODUCTS\n  ${LIBSHARP_LIB_libsharp}\n  ${LIBSHARP_LIB_libfftpack}\n  ${LIBSHARP_LIB_libc_utils}\n  INSTALL_COMMAND \"\"\n)\n\n# Always build libsharp with optimization, since there is a big speed\n# difference and we're not interested in debugging it.\nfile(\n  GENERATE OUTPUT \"libsharp/config/config.auto\"\n  CONTENT\n  \"CC=${CMAKE_C_COMPILER}\nCL=\\$(CC)\nSPECTRE_FLAGS=\\\\\n\\$(subst ;, ,$<TARGET_PROPERTY:SpectreFlags,INTERFACE_COMPILE_OPTIONS>)\nCCFLAGS=\\$(SPECTRE_FLAGS) -fno-openmp -O3 -c\nCLFLAGS=-L. -L\\$(LIBDIR) \\$(SPECTRE_FLAGS) -fno-openmp -O3 -lm\nARCREATE=ar cr\"\n  CONDITION \"$<COMPILE_LANGUAGE:C>\"\n)\n\nadd_library(Libsharp::libsharp STATIC IMPORTED GLOBAL)\nset_target_properties(\n  Libsharp::libsharp\n  PROPERTIES\n  IMPORTED_LOCATION\n  ${LIBSHARP_LIB_libsharp}\n)\nadd_library(Libsharp::libfftpack STATIC IMPORTED GLOBAL)\nset_target_properties(\n  Libsharp::libfftpack\n  PROPERTIES\n  IMPORTED_LOCATION\n  ${LIBSHARP_LIB_libfftpack}\n)\nadd_library(Libsharp::libc_utils STATIC IMPORTED GLOBAL)\nset_target_properties(\n  Libsharp::libc_utils\n  PROPERTIES\n  IMPORTED_LOCATION\n  ${LIBSHARP_LIB_libc_utils}\n)\nadd_library(Libsharp INTERFACE IMPORTED GLOBAL)\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Libsharp::libsharp\n  Libsharp::libfftpack\n  Libsharp::libc_utils\n)\n# cmake issue #15052\nfile(MAKE_DIRECTORY ${LIBSHARP_INCLUDE})\ntarget_include_directories(\n  ${LIBRARY}\n  SYSTEM\n  INTERFACE\n  ${LIBSHARP_INCLUDE}\n)\nadd_dependencies(\n  ${LIBRARY}\n  Libsharp-external\n)\n"
  },
  {
    "path": "external/EffectiveSource/CMakeLists.txt",
    "content": "set(LIBRARY EffectiveSource)\n\nadd_library(${LIBRARY} kerr-circular.c)\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  GSL::gsl\n  )\n\ntarget_include_directories(${LIBRARY} SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "external/EffectiveSource/LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2025 Black Hole Perturbation Toolkit\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "external/EffectiveSource/README.md",
    "content": "This is a modified version of https://github.com/barrywardell/EffectiveSource\n(revision 07a31cedfcc895fbdaab47de88825234a03f7cec).\nThis code is licensed under MIT. The license can be found in LICENSE.md.\n\nSummary of modifications:\n\n- Added CMakeLists.txt.\n- Added C++ compatibility header `effsource.hpp`.\n"
  },
  {
    "path": "external/EffectiveSource/effsource.h",
    "content": "/*******************************************************************************\n * Copyright (C) 2011 Barry Wardell\n ******************************************************************************/\n\nstruct coordinate {\n  double r;\n  double theta;\n  double phi;\n  double t;\n};\n\nvoid effsource_init(double M, double a);\nvoid effsource_set_particle(struct coordinate * x_p, double e, double l, double ur_p);\n\nvoid effsource_PhiS(struct coordinate * x, double * PhiS);\nvoid effsource_calc(struct coordinate * x,\n  double * PhiS, double * dPhiS_dx, double * d2PhiS_dx2, double * src);\n\nvoid effsource_PhiS_m(int m, struct coordinate * x, double * PhiS);\nvoid effsource_calc_m(int m, struct coordinate * x,\n  double * PhiS, double * dPhiS_dx, double * d2PhiS_dx2, double * src);\n"
  },
  {
    "path": "external/EffectiveSource/effsource.hpp",
    "content": "#pragma once\n\nextern \"C\" {\n#include <effsource.h>\n}\n"
  },
  {
    "path": "external/EffectiveSource/kerr-circular.c",
    "content": "/*******************************************************************************\n * Copyright (C) 2012 Barry Wardell\n ******************************************************************************/\n\n#include <math.h>\n#include <assert.h>\n#include \"effsource.h\"\n#include <stdio.h>\n#include <gsl/gsl_sf_ellint.h>\n\n/* The particle's coordinate location and 4-velocity */\nstatic struct coordinate xp;\n\n/* Mass and spin of the Kerr black hole */\nstatic double M, a;\n\n/* Static variables used to store the coefficients of the series expansions */\nstatic double A006, A008, A024, A026, A042, A044, A060, A062, A080, A106, A108, A124, A126, A142, A144, A160, A162, A180, A204, A206, A222, A224, A240, A242, A260, A304, A306, A322, A324, A340, A342, A360, A402, A404, A420, A422, A440, A502, A504, A520, A522, A540, A600, A602, A620, A700, A702, A720, A800, A900;\nstatic double alpha20, alpha02, beta;\n\n/* Numerical coefficients appearing in the elliptic integrals expressions. The\n   indices here correspond to mode m, EllipticK/EllipticE, order in Sin[dphi]/Sin[dphi/2],\n   term in polynomial in alpha/beta. */\nstatic const double ReEI[21][2][5][27] =\n {\n  {{{0,-0.26666666666666666,-0.5333333333333333,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.06666666666666667,0.2,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.4,0.13333333333333333,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.2666666666666666,0.5333333333333333,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,-4,-6.933333333333334,-3.2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,1.5333333333333334,1.5333333333333334,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.13333333333333333,0.4666666666666667,-0.2,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.2,-0.4666666666666667,-0.13333333333333333,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.5333333333333334,-1.5333333333333334,-0.5333333333333333,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,6.866666666666666,8.533333333333333,3.2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,-0.4,-0.4,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.06666666666666667,-0.6,-0.26666666666666666,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.6,-2.4,-1.0666666666666667,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,9.266666666666667,14.4,6.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.3333333333333335,-24.933333333333334,-38.4,-17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,1.2666666666666666,0.6,0.4,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.13333333333333333,0.06666666666666667,0.7333333333333333,0.26666666666666666,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.2,2.6,2.933333333333333,1.0666666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-3.533333333333333,-15.266666666666667,-17.6,-6.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.3333333333333333,10.2,40.93333333333333,46.93333333333333,17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,0,1.6,1.0666666666666667,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.06666666666666667,5,9.066666666666666,4.266666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-7.6,-42,-59.733333333333334,-25.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,30.6,128,166.4,68.26666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.466666666666667,-73.6,-275.2,-341.3333333333333,-136.53333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,0.4666666666666667,-0.6,-2.1333333333333333,-1.0666666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-1.1333333333333333,-8.733333333333333,-11.2,-4.266666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,19.8,67.06666666666666,72.53333333333333,25.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-4.2,-69.8,-198.4,-200.53333333333333,-68.26666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.06666666666666667,13.266666666666667,160,420.26666666666665,409.6,136.53333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,0.6666666666666666,-16.4,-34.13333333333333,-17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,41,186.4,247.46666666666667,102.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-17.6,-204,-630.4,-716.8,-273.06666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,65.8,536,1427.2,1501.8666666666666,546.1333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4857142857142858,-153.0857142857143,-1073.3714285714286,-2640.457142857143,-2652.647619047619,-936.2285714285714,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-0.8666666666666667,2.7333333333333334,30.266666666666666,42.666666666666664,17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-3.1333333333333333,-97.4,-290.93333333333334,-298.6666666666667,-102.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,53.8,410.4,937.6,853.3333333333334,273.06666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-4.6,-177.4,-1019.2,-2075.733333333333,-1774.9333333333334,-546.1333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.02857142857142857,14.771428571428572,392.62857142857143,1984,3791.2380952380954,3120.7619047619046,936.2285714285714,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,1.6,-198.4,-814.9333333333333,-1024,-409.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,147.4,1179.7333333333333,3012.266666666667,3072,1092.2666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-31.6,-629.2,-3345.0666666666666,-7116.8,-6553.6,-2184.5333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,114.94285714285714,1556.3428571428572,6981.4857142857145,13497.295238095237,11702.857142857143,3744.9142857142856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.492063492063492,-263.84761904761905,-3017.295238095238,-12327.009523809524,-22469.485714285714,-18724.571428571428,-5825.422222222222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-2.7333333333333334,18.6,452.26666666666665,1250.1333333333334,1228.8,409.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-5.933333333333334,-401.26666666666665,-2212.266666666667,-4343.466666666666,-3618.133333333333,-1092.2666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,106.2,1463.3333333333333,5879.466666666666,9984,7645.866666666667,2184.5333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-4.885714285714286,-344.0857142857143,-3410.5142857142855,-11886.933333333332,-18646.55238095238,-13575.314285714287,-3744.9142857142856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.015873015873015872,15.806349206349207,753.3714285714286,6407.466666666666,20597.02857142857,30739.504761904762,21637.28253968254,5825.422222222222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,2.8,-970,-6314.666666666667,-14080,-13107.2,-4369.066666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,380.2,4689.866666666667,19157.333333333332,34508.8,28398.933333333334,8738.133333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-49.6,-1514.1714285714286,-12285.104761904762,-41442.74285714285,-66823.31428571428,-51180.49523809524,-14979.657142857142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,178.05396825396826,3632.0761904761903,24604.95238095238,74800.76190476191,112737.52380952382,82388.11428571428,23301.68888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.494949494949495,-405.9919191919192,-6920.145454545454,-42364.121212121216,-120645.81818181818,-173769.69696969696,-122863.45050505051,-33893.36565656566,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-5.133333333333334,58.2,2479.3333333333335,11306.666666666666,19814.4,15291.733333333334,4369.066666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-9.533333333333333,-1129.9333333333334,-9986.933333333332,-31906.133333333335,-47069.86666666667,-32768,-8738.133333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,178.14285714285714,3892.609523809524,24463.390476190478,66662.4,89604.87619047619,58670.32380952381,14979.657142857142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.1079365079365076,-573.9968253968254,-8815.314285714287,-47328,-117906.28571428571,-149562.51428571428,-94038.95873015873,-23301.68888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.010101010101010102,16.6,1251.761616161616,16279.660606060606,79786.66666666667,187671.27272727274,228846.41616161616,139810.13333333333,33893.36565656566,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,4.266666666666667,-3262.4,-30573.866666666665,-103765.33333333333,-163840,-122333.86666666667,-34952.53333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,811.4,14256.685714285713,84442.81904761905,231716.57142857142,322998.85714285716,222198.24761904762,59918.62857142857,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-71.6,-3111.35873015873,-35753.44761904762,-174188.49523809523,-426296.0761904762,-551126.5523809524,-359511.77142857143,-93206.75555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,255.14487734487736,7331.7772005772,69909.00086580086,305560.71341991343,696355.4632034632,856110.1021645021,538057.1797979798,135573.46262626263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4965034965034967,-579.5630147630147,-13824.999533799533,-118555.33053613054,-483592.47365967365,-1.0497981165501166e6,-1.2450923412587412e6,-761297.1362859363,-187717.1020979021,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-8.066666666666666,135.93333333333334,9026.133333333333,61310.933333333334,166024.53333333333,218453.33333333334,139810.13333333333,34952.53333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-13.933333333333334,-2571.1714285714284,-33332.038095238095,-157322.3619047619,-357171.2,-422863.2380952381,-252157.5619047619,-59918.62857142857,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,270.5174603174603,8617.873015873016,78308.57142857143,312652.8,643189.0285714286,713406.1714285715,406115.1492063492,93206.75555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.28975468975469,-870.3079365079365,-19227.160750360752,-147887.70909090908,-536312.6857142857,-1.0362348051948051e6,-1.0997186678210679e6,-605843.911111111,-135573.46262626263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.006993006993006993,17.244910644910647,1895.0315462315461,35180.35244755245,245442.05874125875,836234.2041958042,1.5471995524475526e6,1.5905439527583527e6,855155.6873348873,187717.1020979021,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,6,-8800.4,-111859.80952380953,-531221.9428571429,-1.2414390857142857e6,-1.5322940952380951e6,-958698.0571428571,-239674.51428571428,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,1529,36268.93968253968,292742.09523809527,1.1256783238095238e6,2.3227830857142857e6,2.6401645714285714e6,1.557884342857143e6,372827.02222222224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-97.6,-5729.1255411255415,-88548.89927849927,-586570.2510822511,-2.0080116917748917e6,-3.8364944623376625e6,-4.1284842943722946e6,-2.3386422303030305e6,-542293.8505050505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,346.2218004218004,13349.73026973027,170513.0474858475,1.0105419870795871e6,3.214343297236097e6,5.829024556243756e6,6.033182035742036e6,3.3163354703962705e6,750868.4083916084,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4974358974358974,-784.5839937839938,-25005.26247086247,-286327.7053613054,-1.5797995114219114e6,-4.77710082983683e6,-8.342656417715617e6,-8.387304409013209e6,-4.50521045034965e6,-1.0011578778554779e6,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-11.533333333333333,269.4,25857.619047619046,244643.3523809524,941524.1142857143,1.8502997333333334e6,1.9667041523809524e6,1.0785353142857142e6,239674.51428571428,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-19.133333333333333,-5094.479365079365,-91148.63492063493,-595972.8761904762,-1.9178837333333334e6,-3.3857145904761907e6,-3.3492016761904764e6,-1.744297853968254e6,-372827.02222222224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,384.0510822510823,16824.368253968252,209062.3953823954,1.1501940363636364e6,3.3420239238095237e6,5.513081239826839e6,5.196125312554113e6,2.6097891555555556e6,542293.8505050505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.443600843600843,-1235.5986901986903,-37219.1333999334,-389168.827972028,-1.9364873206793207e6,-5.267843912887113e6,-8.294196586702187e6,-7.550561944366745e6,-3.6917696745920745e6,-750868.4083916084,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.005128205128205128,17.78850038850039,2689.031546231546,67733.57202797203,639727.53006993,2.9806964363636365e6,7.743910966899767e6,1.1785440213830614e7,1.0452192532090131e7,5.005789389277389e6,1.0011578778554779e6,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,8,-20454.4,-338221.5111111111,-2.129646933333333e6,-6.821751466666667e6,-1.2233386666666666e7,-1.2443101866666667e7,-6.7108864e6,-1.491308088888889e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,2637,81197.69581529581,855236.9685425685,4.365921080519481e6,1.2354614081385281e7,2.0404108744588744e7,1.9569787234632034e7,1.0100222965656566e7,2.169175402020202e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-127.6,-9731.779553779554,-194743.272016872,-1.6810041968697968e6,-7.631895255677655e6,-1.9979235561238762e7,-3.121355504229104e7,-2.8716805848018646e7,-1.4349929582595183e7,-3.0034736335664336e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,451.2884670884671,22506.019314019315,371138.143989344,2.8608340821844824e6,1.2051529176956376e7,2.991034232967033e7,4.489893256254856e7,4.0046315114219114e7,1.9522578618181817e7,4.0046315114219114e6,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4980392156862745,-1021.0684583390466,-41964.88205128205,-619011.4680926916,-4.434788679994515e6,-1.7736541547237076e7,-4.234410989606472e7,-6.1679177494547285e7,-5.3709175564952694e7,-2.5676754984999314e7,-5.182464308898944e6,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-15.533333333333333,479.4,63067.02222222222,792570.3111111111,4.1009152e6,1.1153681066666666e7,1.7336456533333335e7,1.55189248e7,7.456540444444444e6,1.491308088888889e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-25.133333333333333,-9153.99163059163,-216262.70476190475,-1.870872972005772e6,-8.079372412121212e6,-1.9716661638095237e7,-2.849857074978355e7,-2.421317832958153e7,-1.1184810666666666e7,-2.169175402020202e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,519.3591075591075,29973.258252858253,488692.7529359529,3.5444105846153847e6,1.3786808101764902e7,3.138413195977356e7,4.3162921822710626e7,3.5328619333022535e7,1.58516663993784e7,3.0034736335664336e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.576934176934177,-1672.0434454434455,-65967.23223443223,-901384.234965035,-5.895572075924076e6,-2.1422238766033966e7,-4.646431837318237e7,-6.163704083294483e7,-4.905673601491842e7,-2.1524894373892773e7,-4.0046315114219114e6,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.00392156862745098,18.25848530554413,3638.6809634809633,119650.73401892226,1.4725623786096256e6,8.997631810448375e6,3.1165726124667488e7,6.523953848801134e7,8.420522974629554e7,6.55758409995338e7,2.8267987139448788e7,5.182464308898944e6,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,10.266666666666667,-42656.4,-889373.995959596,-7.149019797979798e6,-2.9865748169696968e7,-7.235293556363636e7,-1.0554394065454546e8,-9.146124722424242e7,-4.3383508040404044e7,-8.676701608080808e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,4255.4,165053.57016317017,2.19670492991453e6,1.4330180624708625e7,5.29140260997669e7,1.1808499516270396e8,1.624359102955711e8,1.346948423011655e8,6.173806913442113e7,1.2013894534265734e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-161.6,-15539.587434787434,-390961.72201132204,-4.259990718614719e6,-2.4699115148318347e7,-8.432809336796537e7,-1.779399951088911e8,-2.3469069971592852e8,-1.8834282577156177e8,-8.409726173986015e7,-1.6018526045687646e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,570.3472906178788,35746.582191664544,739622.7686979687,7.187172049660143e6,3.862786977665472e7,1.249378262920452e8,2.5314112267361397e8,3.234930047362311e8,2.531309910877828e8,1.1071628296284108e8,2.0729857235595778e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.498452012383901,-1289.0251146857959,-66438.47422257825,-1.227617738434115e6,-1.107397992473244e7,-5.645272137256345e7,-1.7550347663965014e8,-3.4482027794523466e8,-4.29947715579034e8,-3.2974049077368206e8,-1.4183586529618162e8,-2.6185082823910456e7,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-20.066666666666666,789.9333333333333,136892.39191919193,2.2055480888888887e6,1.4743798173737373e7,5.277455204848485e7,1.1157285546666667e8,1.4395359728484848e8,1.115261196929293e8,4.7721858844444446e7,8.676701608080808e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-31.933333333333334,-15290.940326340326,-461269.8076146076,-5.10054407956488e6,-2.841850342937063e7,-9.11948410927739e7,-1.7918446766247088e8,-2.193337460960373e8,-1.6331127164320123e8,-6.7745016401554e7,-1.2013894534265734e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,676.9748695748696,49810.32203352203,1.0321920394272394e6,9.544164072727272e6,4.781388058341658e7,1.4294413774385613e8,2.6698699182559663e8,3.145956128422688e8,2.2738798300792542e8,9.210652476270397e7,1.6018526045687646e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.694581235757706,-2181.5221419103773,-109270.05492285492,-1.892202578033731e6,-1.5744906345607335e7,-7.356716086097433e7,-2.0929734465833253e8,-3.7668167367356825e8,-4.312426213404269e8,-3.046022843375291e8,-1.2108121158063897e8,-2.0729857235595778e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.0030959752321981426,18.672520393263426,4748.226915980476,197766.84242568578,3.0782871159107145e6,2.3888763615371626e7,1.062565981512135e8,2.914255876520172e8,5.098456950542433e8,5.706785877375826e8,3.9574872039228964e8,1.5492840670813686e8,2.6185082823910456e7,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,12.8,-81880,-2.1007407614607615e6,-2.095521238228438e7,-1.1023046737156177e8,-3.441285888596737e8,-6.709847600932401e8,-8.259552492307693e8,-6.240550771965812e8,-2.6430567975384617e8,-4.805557813706294e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,6520.2,311067.7942501943,5.102019779331779e6,4.133440304335664e7,1.922698987114219e8,5.530782550526806e8,1.0170620137920746e9,1.1980522605003884e9,8.746782659530692e8,3.6041683602797204e8,6.407410418275058e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-199.6,-23628.784478266833,-730166.5007724302,-9.797468315151514e6,-7.051502323959962e7,-3.0318843028131086e8,-8.24417740343672e8,-1.4532290842782943e9,-1.6563077409054527e9,-1.1777150141972852e9,-4.749021839427396e8,-8.291942894238311e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,703.3999221968262,54143.24552805296,1.3738612902969064e6,1.6423980970872264e7,1.0950013007528281e8,4.457761957446739e8,1.1633972193432798e9,1.9862208061391819e9,2.20653026587352e9,1.5363898975089884e9,6.088031756559181e8,1.0474033129564182e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.49874686716792,-1588.4598780648316,-100391.40281967774,-2.2721153274630425e6,-2.5192147588914696e7,-1.5919834323671788e8,-6.225945846062396e8,-1.5748847121862333e9,-2.622330288934644e9,-2.854181112514796e9,-1.9537500785578425e9,-7.631081280111047e8,-1.2967850541365178e8,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-25.133333333333333,1228.2,271881.5229215229,5.463605097125097e6,4.5788857465734266e7,2.0806943674778554e8,5.705886187710955e8,9.891261404195805e8,1.0929306833255634e9,7.47197496172805e8,2.883334688223776e8,4.805557813706294e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-39.53333333333333,-24135.788500388502,-905518.1986013986,-1.2458983344832946e7,-8.693429073752914e7,-3.539285428811189e8,-9.016469155505828e8,-1.4824022811648796e9,-1.5738201839888113e9,-1.0428727894327894e9,-3.9245388811934733e8,-6.407410418275058e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,857.3689565336624,78373.47438182733,2.0127715456099457e6,2.3112727872974087e7,1.448205784474976e8,5.48772786971836e8,1.3280004404618127e9,2.1005893010457883e9,2.16389478497818e9,1.3996187132419581e9,5.163618984139312e8,8.291942894238311e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.799844393652443,-2765.6909199503625,-171565.7852751995,-3.67408829080517e6,-3.790738907893345e7,-2.2126713741740116e8,-7.971567635483366e8,-1.8574286571222606e9,-2.852811286882909e9,-2.870394025251496e9,-1.8211526732190146e9,-6.61173341303739e8,-1.0474033129564182e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.002506265664160401,19.04256314720711,6021.407418450143,310071.7180627422,5.959559314048181e6,5.72743914936333e7,3.179286576710175e8,1.1032734622114787e9,2.4971591341437187e9,3.7476056187814e9,3.700130737674165e9,2.310989422798335e9,8.279473807179306e8,1.2967850541365178e8,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,15.6,-147184.4,-4.556378703962704e6,-5.516948408391608e7,-3.559327987468532e8,-1.3849838370013986e9,-3.448738599742657e9,-5.628697306405595e9,-5.996935688354312e9,-4.0186477217118883e9,-1.537778500386014e9,-2.5629641673100233e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,9583.4,551597.1562502857,1.092800063183875e7,1.0752849872261073e8,6.136858642814754e8,2.2008252360679007e9,5.169359078663266e9,8.074326015812789e9,8.312849426393528e9,5.419679834310819e9,2.0277569441364596e9,3.3167771576953244e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-241.6,-34531.581210533535,-1.2859442616457501e6,-2.0830391072875936e7,-1.8202685521436995e8,-9.600492874957876e8,-3.2540002685571485e9,-7.325356346213779e9,-1.1069155880596071e10,-1.1097453830148788e10,-7.079345431875746e9,-2.6010515605084386e9,-4.189613251825673e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,850.4475412444452,78893.74837670069,2.409665423174555e6,3.474925120193656e7,2.8114098072956973e8,1.403407938144174e9,4.564031654437904e9,9.948487456287066e9,1.4649250696397833e10,1.4378787962114319e10,9.011876808307611e9,3.2619131746357026e9,5.187140216546071e8,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4989648033126293,-1919.3769533617697,-146019.8335099459,-3.9742071741968985e6,-5.311713496745179e7,-4.071180069944188e8,-1.951471853701304e9,-6.149049338477993e9,-1.3068470999123806e10,-1.8848566822377876e10,-1.8183385037681484e10,-1.1230798287201794e10,-4.014395471935655e9,-6.314779394056087e8,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-30.733333333333334,1824.6,503413.80792540795,1.23437348997669e7,1.2666985013706294e8,7.111014843972028e8,2.4453185536e9,5.440886345756643e9,8.003944371617716e9,7.74195386945641e9,4.739481393767832e9,1.6659267087515152e9,2.5629641673100233e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-47.93333333333333,-36410.112500571326,-1.6632604871154989e6,-2.7868508792540792e7,-2.3790098953758398e8,-1.1959445584594543e9,-3.819200511326039e9,-8.057472578080973e9,-1.1386686300517574e10,-1.0673579702376745e10,-6.371368734672261e9,-2.193595802021226e9,-3.3167771576953244e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,1060.962421067065,117999.39040993406,3.6796141928934497e6,5.137764483644158e7,3.934568326273124e8,1.8396699858694205e9,5.577640943291375e9,1.131629761773835e10,1.5511646734480068e10,1.4188707002727196e10,8.301315963658234e9,2.8105322230997224e9,4.189613251825673e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.895082488890538,-3426.029950579177,-257947.1453013447,-6.696260875827578e6,-8.390810906845228e7,-5.980646799483613e8,-2.656730813122411e9,-7.751269489416868e9,-1.5263016489000265e10,-2.042660824129106e10,-1.832174708555405e10,-1.0545574516565224e10,-3.521270185463006e9,-5.187140216546071e8,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.002070393374741201,19.377095129336347,7461.561160324547,465737.4659903026,1.0838682409513706e7,1.2638856718220124e8,8.560311134982536e8,3.660454950424734e9,1.0368589915334404e10,1.9940417844891014e10,2.6176608626888607e10,2.310528608711372e10,1.3119593909531069e10,4.330134441638459e9,6.314779394056087e8,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,18.666666666666668,-250822.4,-9.21840527146579e6,-1.3309575831290278e8,-1.0317491250125874e9,-4.880582542269025e9,-1.5029793383663677e10,-3.1108875678233482e10,-4.36671203627632e10,-4.102438746924404e10,-2.470622075987824e10,-8.623620610007843e9,-1.3267108630781298e9,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,13613,930252.8430925112,2.189775853092872e7,2.5704415869336855e8,1.7632092507090087e9,7.688769367066271e9,2.233765721776305e10,4.425202515598031e10,6.002764410921827e10,5.486875815318882e10,3.230882151159315e10,1.1067561673572819e10,1.6758453007302692e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-287.6,-48836.168107778016,-2.157297016025873e6,-4.150197552329858e7,-4.325356919968865e8,-2.7414740676285434e9,-1.129721454934732e10,-3.1453610225900883e10,-6.027595384543495e10,-7.961419543169162e10,-7.119894052826602e10,-4.116669092530494e10,-1.3885575348907944e10,-2.0748560866184285e9,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,1011.4910195053149,111321.75903879551,4.0295488900919873e6,6.897135012978844e7,6.652503712536638e8,3.9895117085213494e9,1.5770553504337744e10,4.2506951460346924e10,7.936585512341737e10,1.0261545331623116e11,9.014849059168524e10,5.1342279166782936e10,1.7095009931051834e10,2.5259117576224346e9,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4991304347826087,-2281.7794378338194,-205750.77203382071,-6.631606823604064e6,-1.0514449275916624e8,-9.603493437127837e8,-5.528469326922443e9,-2.116888539068655e10,-5.561882541965658e10,-1.0169630183320364e11,-1.2921016311537668e11,-1.1184445811395491e11,-6.289520276479862e10,-2.0712476412503963e10,-3.0310941091469216e9,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-36.86666666666667,2612.733333333333,880586.5429315782,2.58769066508707e7,3.190248779785548e8,2.165737490196956e9,9.103826722266722e9,2.516944859639786e10,4.714484837223376e10,6.028345544481031e10,5.188494812822027e10,2.876927277805501e10,9.286976041546907e9,1.3267108630781298e9,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-57.13333333333333,-52928.28618502241,-2.8930003372693355e6,-5.799649101182244e7,-5.943782676834052e8,-3.6117879045470395e9,-1.4095434050579176e10,-3.694310664816257e10,-6.646474288799973e10,-8.235087355324098e10,-6.910511159213396e10,-3.752838135449264e10,-1.1905484323937954e10,-1.6758453007302692e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,1288.1362155560298,171329.4262403575,6.378245421382333e6,1.0642760530321938e8,9.777255230809364e8,5.52282590456045e9,2.045420469861058e10,5.153708389209838e10,8.991146463998302e10,1.0868422825737668e11,8.937325837111873e10,4.772044308351795e10,1.4923003392217157e10,2.0748560866184285e9,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.982039010629669,-4163.8773617253355,-374174.74996878806,-1.1581383324811196e7,-1.7326355869602597e8,-1.4804164912081263e9,-7.940429316689499e9,-2.828693012341041e10,-6.914758135245525e10,-1.1774449383689468e11,-1.395287760054435e11,-1.1285112004028159e11,-5.941617567775465e10,-1.8357965809863052e10,-2.5259117576224346e9,0,0,0,0,0,0,0,0,0},{0,0,0,-0.0017391304347826088,19.68235392850819,9071.70504037235,675142.9984015393,1.87165462532353e7,2.6038147706447074e8,2.1128045296213334e9,1.0902989617985832e10,3.76931192037466e10,8.995871805969456e10,1.5020871457643378e11,1.7511705338495316e11,1.3969263524174225e11,7.268311082558556e10,2.2228023467077423e10,3.0310941091469216e9,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,22,-408912.4,-1.760144321133892e7,-2.9861306666560435e8,-2.735742086051796e9,-1.5429018250372686e10,-5.7365925635849785e10,-1.459396829211136e11,-2.5834540566109332e11,-3.181407036536493e11,-2.6734969563212573e11,-1.462262308496573e11,-4.692366842044753e10,-6.703381202921077e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,18793,1.5042532023658487e6,4.1496704494122185e7,5.725677207573206e8,4.642875405124088e9,2.4142609768578815e10,8.469481355425212e10,2.0624416519975134e11,3.5284625417378284e11,4.227746181988333e11,3.4738564819211395e11,1.8647020933259586e11,5.8893991997092316e10,8.299424346473714e9,0,0,0,0,0,0,0,0,0,0},{0,0,0,-337.6,-67186.71908304376,-3.473936806561178e6,-7.829175553408918e7,-9.589299292985568e8,-7.183900332775953e9,-3.529847566422289e10,-1.1866407812293896e11,-2.795105576516615e11,-4.6559435752690393e11,-5.4578034888958093e11,-4.4032629265684235e11,-2.3272130603976505e11,-7.25297518974442e10,-1.0103647030489738e10,0,0,0,0,0,0,0,0,0},{0,0,0,1,1186.5310195053148,152876.8874522256,6.472435445144703e6,1.2971987635205658e8,1.4699377328144228e9,1.0417012853749659e10,4.909095266192736e10,1.5974095702715295e11,3.6655776122906323e11,5.976373260031626e11,6.881257568365696e11,5.468004776147498e11,2.8525211689964355e11,8.790172916526073e10,1.2124376436587687e10,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4992592592592593,-2675.669679379713,-282242.09231669543,-1.0633815228365488e7,-1.9732870645890525e8,-2.1167235434403558e9,-1.4395851669845222e10,-6.570092170701432e10,-2.08363647527213e11,-4.6815844108415533e11,-7.499685194097299e11,-8.507323675829946e11,-6.674090341577878e11,-3.4432387109323157e11,-1.0507792911709328e11,-1.4369631332252073e10,0,0,0,0,0,0,0}},{{0.5333333333333333,-43.53333333333333,3629.4,1.4694724226778382e6,5.0982079751051836e7,7.433658698673364e8,5.9978865667303295e9,3.02094865091216e10,1.0127974704935579e11,2.3405817896550006e11,3.7868392857216315e11,4.284339090756366e11,3.3229306521589435e11,1.6843118108433334e11,5.027535902190807e10,6.703381202921077e9,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-67.13333333333334,-74599.00473169761,-4.808057892384602e6,-1.1363745491663341e8,-1.3765659670421038e9,-9.93853334295837e9,-4.646297120807411e10,-1.476455595126865e11,-3.276911350438754e11,-5.136740259640373e11,-5.665757674791024e11,-4.3035620039143896e11,-2.143610632661782e11,-6.304370417032917e10,-8.299424346473714e9,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,1539.2381660875133,241314.9715176823,1.0573575649746368e7,2.0780309906924197e8,2.255048448318664e9,1.5127647788083984e10,6.709248572068778e10,2.0490559066010007e11,4.4090160795530695e11,6.741577248596049e11,7.285603048003533e11,5.440348341050625e11,2.6709174817027032e11,7.758157541268906e10,1.0103647030489738e10,0,0,0,0,0,0,0,0,0},{0,0,0,-6.062039010629669,-4980.454188585528,-526689.1747977207,-1.9167077767400973e7,-3.374851933365408e8,-3.4042942107257743e9,-2.167534593874192e10,-9.243454922934729e10,-2.738046230308966e11,-5.748972516712766e11,-8.61570663231068e11,-9.156248791038105e11,-6.740816221370153e11,-3.269296609004137e11,-9.396391738355457e10,-1.2124376436587687e10,0,0,0,0,0,0,0,0},{0,0,0,-0.0014814814814814814,19.963062463129766,10854.591016749811,949896.1873083068,3.0939338982764497e7,5.0627985565643203e8,4.84757118825071e9,2.9683411242703876e10,1.2280305832607028e11,3.5504229585249896e11,7.308189481361008e11,1.0772753882648191e12,1.1289483918947014e12,8.212160109323472e11,3.941685297769809e11,1.1226274478321931e11,1.4369631332252073e10,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,25.6,-642174.4,-3.2001493960675444e7,-6.301171929947947e8,-6.730005930120287e9,-4.45489555789727e10,-1.9627249696235114e11,-5.995497443810022e11,-1.2980003517584287e12,-2.0066420365331858e12,-2.2010916432997065e12,-1.6733714338577625e12,-8.384014633082003e11,-2.4898273039421143e11,-3.3197697385894855e10,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,25323.4,2.3470004379713614e6,7.499139768356036e7,1.2012583049576294e9,1.1358261276733395e10,6.933205150119518e10,2.8824587254763275e11,8.428926177352782e11,1.7637070948449934e12,2.6530579083520938e12,2.8455838704309585e12,2.1231780276189883e12,1.0469973628525146e12,3.067178562827242e11,4.0414588121958954e10,0,0,0,0,0,0,0,0,0},{0,0,0,-391.6,-90283.39413633075,-5.402084146905385e6,-1.409712773254586e8,-2.0042036720084534e9,-1.7506077529181057e10,-1.009677881940971e11,-4.0223835666768646e11,-1.1377089895665e12,-2.317805564538369e12,-3.4109110612635464e12,-3.591988921734658e12,-2.6387589471896216e12,-1.2839931153068442e12,-3.718142107220224e11,-4.8497505746350746e10,0,0,0,0,0,0,0,0},{0,0,0,1,1375.5680565423518,205134.69458317457,1.00442903694642e7,2.3300366348778668e8,3.0639399202108026e9,2.531140817273939e10,1.3999475849889786e11,5.397786494220163e11,1.4872042574435613e12,2.9652929630267153e12,4.285977100720373e12,4.445188146394695e12,3.223045360499386e12,1.5505730309458252e12,4.4456046934154846e11,5.747852532900829e10,0,0,0,0,0,0,0},{0,0,0,0,-3.499361430395913,-3101.049500580224,-378382.55774830375,-1.6479398081656067e7,-3.538256790695258e8,-4.4032169385133705e9,-3.490151422566471e10,-1.869146820137703e11,-7.023015906202007e11,-1.8944051915725413e12,-3.7109112830375283e12,-5.283759154390902e12,-5.409848733761012e12,-3.8788481619419673e12,-1.8478757481648335e12,-5.2523480042024817e11,-6.7388615902975235e10,0,0,0,0,0,0}},{{0.5333333333333333,-50.733333333333334,4914.6,2.3567543213508883e6,9.531948442077139e7,1.6222566814226518e9,1.533629951143212e10,9.106927966780995e10,3.6324193033461017e11,1.0115537476095123e12,2.0078489418750176e12,2.859678973879202e12,2.902119927570143e12,2.0490001876928757e12,9.566682602454508e11,2.6558157908715884e11,3.3197697385894855e10,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-77.93333333333334,-102426.67594272287,-7.688375333570598e6,-2.1157272142042986e8,-2.9899660626777706e9,-2.5285472574644356e10,-1.39341099517905e11,-5.2671795525996155e11,-1.4085398898401514e12,-2.708603610196061e12,-3.76067732280449e12,-3.7374252386297983e12,-2.5929559786286685e12,-1.1927785557210093e12,-3.269251503437037e11,-4.0414588121958954e10,0,0,0,0,0,0,0,0,0},{0,0,0.2,1814.5882726615152,331222.3335493492,1.6875662577301286e7,3.858469039048684e8,4.882312146902555e9,3.834964651451061e10,2.0043215049773267e11,7.280122943454908e11,1.8871102433942173e12,3.5392202281469395e12,4.81403000415386e12,4.70284874154185e12,3.215586981496385e12,1.4608069383404146e12,3.9606296359519775e11,4.8497505746350746e10,0,0,0,0,0,0,0,0},{0,0,0,-6.136113084703743,-5876.883265298239,-722621.9636936528,-3.0552356959223974e7,-6.254641839081137e8,-7.35329135286915e9,-5.480054731379718e10,-2.753158133060997e11,-9.696738980136191e11,-2.4522191929156543e12,-4.506888343001096e12,-6.027500191226616e12,-5.804540636175082e12,-3.920365399720353e12,-1.7620760421174104e12,-4.732997320060526e11,-5.747852532900829e10,0,0,0,0,0,0,0},{0,0,0,-0.001277139208173691,20.222883663640623,12812.749057488787,1.302854067321981e6,4.927317433912787e7,9.370025785364087e8,1.045225177549322e10,7.488955072282285e10,3.6490079744731726e11,1.254099223039917e12,3.108551076865836e12,5.618437072552979e12,7.408490300586078e12,7.048384464155902e12,4.710622193686491e12,2.09785778289315e12,5.589291083717358e11,6.7388615902975235e10,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,29.466666666666665,-976730,-5.578954646189742e7,-1.2615869100582263e9,-1.5529302994019297e10,-1.1910193801450629e11,-6.125072742936332e11,-2.206143296031422e12,-5.709225768796877e12,-1.0749971748792695e13,-1.4747065224375662e13,-1.4582852118589701e13,-1.0122591368671906e13,-4.679251530995561e12,-1.2932668199026865e12,-1.6165835248783582e11,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,33420.2,3.550881250122197e6,1.3009442746062008e8,2.3938339984597726e9,2.6090461836376514e10,1.8454580335053577e11,8.956680843174019e11,3.088494496941551e12,7.725403893782204e12,1.415456345665883e13,1.898742633772399e13,1.842784153284401e13,1.2590136522466424e13,5.741108749446352e12,1.5680860191320073e12,1.9399002298540298e11,0,0,0,0,0,0,0,0},{0,0,0,-449.6,-118882.34141578716,-8.1507702655822905e6,-2.4382796059468478e8,-3.981492068669497e9,-4.008401440642937e10,-2.6788273341492886e11,-1.2457916692080254e12,-4.155022019169404e12,-1.011883380132408e13,-1.8137213625744375e13,-2.3887559953693965e13,-2.2825549173918258e13,-1.538763792071379e13,-6.935816898196855e12,-1.8752368888588955e12,-2.2991410131603317e11,0,0,0,0,0,0,0},{0,0,0,1,1578.6025393009725,269796.6997164444,1.5129675524776911e7,4.02205042950141e8,6.073244823885286e9,5.781867124578261e10,3.705046212385272e11,1.6674750698719607e12,5.417057116138862e12,1.2910569366340434e13,2.272747357675429e13,2.947861243273825e13,2.7800233539143176e13,1.8528636980494258e13,8.268499554905844e12,2.2158962523390093e12,2.6955446361190094e11,0,0,0,0,0,0},{0,0,0,0,-3.49944382647386,-3557.9203447280424,-497291.8376827858,-2.479476383769259e7,-6.098901382048699e8,-8.713451379196568e9,-7.957942158518886e10,-4.937089707091298e11,-2.1650359733823113e12,-6.885306538377396e12,-1.6120601827215521e13,-2.7953517732769695e13,-3.579038142482068e13,-3.3375171285503258e13,-2.2026160316691473e13,-9.744096556853e12,-2.5912009727853706e12,-3.1303099000091724e11,0,0,0,0,0}},{{0.5333333333333333,-58.46666666666667,6511.533333333334,3.65374292379483e6,1.7041039042477334e8,3.3474383786596866e9,3.6634576706844986e10,2.5307096312507574e11,1.1825685116914846e12,3.8959211372933706e12,9.271173514538479e12,1.6125338479467012e13,2.0514656081142355e13,1.8879270148820613e13,1.2234885075983668e13,5.295573999855435e12,1.3740959961466045e12,1.6165835248783582e11,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-89.53333333333333,-137512.7002443942,-1.189358210895399e7,-3.7701063823343146e8,-6.146753773932911e9,-6.016166039394057e10,-3.856178138857783e11,-1.7075189529981672e12,-5.401437822018391e12,-1.2451974168004955e13,-2.1111659790717637e13,-2.6300614293483598e13,-2.3782945860158555e13,-1.5184861333257229e13,-6.488778629702592e12,-1.665081030624709e12,-1.9399002298540298e11,0,0,0,0,0,0,0,0},{0,0,0.2,2114.4828315743325,444637.2286422091,2.6068237359630816e7,6.861001365773908e8,1.00114325811189e10,9.098481065241385e10,5.529740045299663e11,2.3523822512552803e12,7.2120144708876045e12,1.621315950309689e13,2.692705307551175e13,3.29716634138851e13,2.938224475952283e13,1.8525493900149555e13,7.830326448629547e12,1.990193939516912e12,2.2991410131603317e11,0,0,0,0,0,0,0},{0,0,0,-6.2050786019451225,-6854.203876665512,-969805.7488195399,-4.71490524203349e7,-1.110535871836198e9,-1.5050298809158161e10,-1.2973475579082533e11,-7.577552802741434e11,-3.1251463627856104e12,-9.3459380263197e12,-2.058641033023447e13,-3.3613123793561336e13,-4.056896085255746e13,-3.57073389951639e13,-2.2272676941597234e13,-9.325906219148117e12,-2.3506734841449595e12,-2.6955446361190094e11,0,0,0,0,0,0},{0,0,0,-0.0011123470522803114,20.46471615241416,14948.520395986918,1.7481414056108568e6,7.598676584469618e7,1.6618757729228277e9,2.1362875654996433e10,1.7699538829298035e11,1.0024052815552432e12,4.0333265266471006e12,1.182051050899169e13,2.5601839654882164e13,4.120957066054989e13,4.913190571704601e13,4.278719239553509e13,2.6441705068033305e13,1.0981003732620514e13,2.747716467785829e12,3.1303099000091724e11,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,33.6,-1.4449664e6,-9.378112396360818e7,-2.4134343369641385e9,-3.3903936051757397e10,-2.9801676779554596e11,-1.7669252650270583e12,-7.395409184951448e12,-2.247442917360153e13,-5.039491703618102e13,-8.391129263208694e13,-1.034420381542176e14,-9.311814740541167e13,-5.94700664214626e13,-2.5517770940204883e13,-6.595660781503701e12,-7.759600919416119e11,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,43315.4,5.230291427936181e6,2.1780053830000895e8,4.561721534340654e9,5.6748630670697624e10,4.600956128255938e11,2.574621229746886e12,1.0317263052979926e13,3.0307188769397027e13,6.613140421026188e13,1.0767812330799452e14,1.302827448528449e14,1.1543547237775336e14,7.272605132546514e13,3.083902504292948e13,7.888927601406388e12,9.196564052641327e11,0,0,0,0,0,0,0},{0,0,0,-511.6,-153795.69885244747,-1.1978643183124974e7,-4.072033459630186e8,-7.567600637131104e9,-8.695493057921883e10,-6.660723636188911e11,-3.571363466702473e12,-1.384223532826015e13,-3.9588090502665234e13,-8.450579365558694e13,-1.3509302529267044e14,-1.609267891315999e14,-1.4069221688269344e14,-8.761436750764872e13,-3.677597449159481e13,-9.323413211988104e12,-1.0782178544476038e12,0,0,0,0,0,0},{0,0,0,1,1795.6347973654888,348690.3862286264,2.2204228030820765e7,6.705854777632657e8,1.1522110280379248e10,1.2517973983561208e11,9.19332170318912e11,4.770007373158425e12,1.8007053868363406e13,5.039707192316374e13,1.056513773616503e14,1.663261076501606e14,1.955389474170697e14,1.6900738607205572e14,1.0419707990723603e14,4.335070020676101e13,1.0903912818365283e13,1.252123960003669e12,0,0,0,0,0},{0,0,0,0,-3.4995112414467253,-4046.283374356923,-642320.5204937553,-3.6354442541503884e7,-1.0156406403679376e9,-1.6508254693268639e10,-1.7202985544924103e11,-1.2230324761502656e12,-6.182633991853942e12,-2.2846441388588523e13,-6.281007640234248e13,-1.2969530267124028e14,-2.0154019556485406e14,-2.3427740497541834e14,-2.0049551613303962e14,-1.2253405597896867e14,-5.058454321247145e13,-1.2635069050946113e13,-1.4418397115193762e12,0,0,0,0}},{{0.5333333333333333,-66.73333333333333,8466.6,5.500782327216349e6,2.9307562486459124e8,6.580662628830447e9,8.25198689979895e10,6.557192881581038e11,3.544366348591472e12,1.3610364338969898e13,3.81402493752755e13,7.919889465707464e13,1.2257383139758511e14,1.4091199728388275e14,1.1864488533235611e14,7.106501175365262e13,2.8670108813717684e13,6.983640827474507e12,7.759600919416119e11,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-101.93333333333334,-181056.65587236284,-1.787733779890622e7,-6.467215044849563e8,-1.2047942031497614e10,-1.3508357853771237e11,-9.958337708633071e11,-5.100215968856238e12,-1.8803864759217754e13,-5.1043402609364266e13,-1.0331528027101834e14,-1.565720156503844e14,-1.7685958939722884e14,-1.4670592314888475e14,-8.675260770965969e13,-3.461105326764565e13,-8.348755804038454e12,-9.196564052641327e11,0,0,0,0,0,0,0},{0,0,0.2,2439.19770489493,585468.939241731,3.91400351372385e7,1.1749495789312527e9,1.958269650268004e10,2.0382117002694974e11,1.4244548817255012e12,7.007773329261644e12,2.5037504149523434e13,6.627085088332564e13,1.3138571746780752e14,1.956929732604737e14,2.1782474594777912e14,1.7841953684261428e14,1.0435529770005283e14,4.123551524987994e13,9.862522139211904e12,1.0782178544476038e12,0,0,0,0,0,0},{0,0,0,-6.2695947309773805,-7913.383352642443,-1.2767836155293747e6,-7.073831350388925e7,-1.899554623143067e9,-2.939480935713848e10,-2.901204668878392e11,-1.9481763004141086e12,-9.290239953778574e12,-3.237296296631215e13,-8.39479743534346e13,-1.6360610601542472e14,-2.401716637111394e14,-2.6402193545879147e14,-2.1393204742321744e14,-1.2394533297842339e14,-4.856788337344296e13,-1.1529974798367117e13,-1.252123960003669e12,0,0,0,0,0},{0,0,0,-0.0009775171065493646,20.69089338637782,17264.0838134564,2.3011678842776404e6,1.1394226862290737e8,2.840139762731206e9,4.167633267110191e10,3.95266965553167e11,2.5731613895229917e12,1.1969456812409002e13,4.086863287421054e13,1.0419401598474152e14,2.0016347293020044e14,2.902331341320077e14,3.156571512430503e14,2.533843334077847e14,1.4559242461110694e14,5.6631732792034625e13,1.33559889067058e13,1.4418397115193762e12,0,0,0,0}}},\n  {{{0,-0.26666666666666666,38,-2.0864644e6,-1.5269386717706367e8,-4.4364839170700865e9,-7.052545524685811e10,-7.039261202593761e11,-4.761725707062858e12,-2.288315999285902e13,-8.050868479032589e13,-2.1126861740115747e14,-4.1760113688084575e14,-6.230137174218235e14,-6.97244682853531e14,-5.760027708229431e14,-3.406464803623676e14,-1.3638576338223748e14,-3.3107630589508773e13,-3.678625621056531e12,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,55257,7.524884401159955e6,3.534212218718977e8,8.358422454073611e9,1.1767720205354315e11,1.0834651446175271e12,6.917863829651936e12,3.183148547582819e13,1.0825791495805245e14,2.7646060697452588e14,5.3439199490061275e14,7.825097493435275e14,8.619879647594399e14,7.024798614322576e14,4.105674340973219e14,1.6268776314620662e14,3.913296565848068e13,4.312871417790415e12,0,0,0,0,0,0},{0,0,0,-577.6,-195891.5954785697,-1.720127764558216e7,-6.593956434546355e8,-1.3836051116645775e10,-1.7991428787352527e11,-1.5649809531653018e12,-9.574263945472568e12,-4.260947812998459e13,-1.4108556267746003e14,-3.5246266137808444e14,-6.68904000078248e14,-9.643324596899239e14,-1.0481525855129518e15,-8.443224624053235e14,-4.884650317232749e14,-1.9181971314970422e14,-4.577208698235634e13,-5.008495840014676e12,0,0,0,0,0},{0,0,0,1,2026.6651003957916,443769.2062382361,3.1848062618839644e7,1.084383682742406e9,2.1033538298344963e10,2.5857382719851614e11,2.1562791411962607e12,1.2764669581045951e13,5.532758658800373e13,1.7926934003434116e14,4.398155867559162e14,8.21954813552544e14,1.1694384435544125e15,1.2565957550652905e15,1.0021077247917248e15,5.746217280770576e14,2.2387644402300447e14,5.304452412379179e13,5.767358846077505e12,0,0,0,0},{0,0,0,0,-3.4995670995670998,-4566.139539696959,-817050.1240826164,-5.210286556396761e7,-1.6407138496930685e9,-3.010064457529331e10,-3.548915820326058e11,-2.864643885149056e12,-1.6520807008205463e13,-7.0090278456826984e13,-2.2307320285268588e14,-5.3903548690406256e14,-9.943274230202304e14,-1.3987464189591615e15,-1.488142833281783e15,-1.1763925899836822e15,-6.693106216369468e14,-2.589498048063432e14,-6.0969222087105055e13,-6.591267252660006e12,0,0,0}},{{0.5333333333333333,-75.53333333333333,10829.4,8.072048354127319e6,4.87250210500034e8,1.2400772896941032e10,1.7659692941512448e11,1.5987019273419814e12,9.889499268944688e12,4.3725671416071375e13,1.4222478427288353e14,3.464460612721393e14,6.378941277981012e14,8.892327870204825e14,9.324932431776439e14,7.236581421029889e14,4.029765524699275e14,1.5224983637304378e14,3.494694340003704e13,3.678625621056531e12,0,0,0,0,0,0,0},{0,0.13333333333333333,-115.13333333333334,-234357.4023199112,-2.6202969245825373e7,-1.0729925112854352e9,-2.264892860478733e10,-2.8833628269220435e11,-2.421334407948635e12,-1.4190732511616314e13,-6.023738501346369e13,-1.897848476488173e14,-4.506023042846216e14,-8.123862868785152e14,-1.1127081259761939e15,-1.1495259687556288e15,-8.80694584160074e14,-4.849782163048779e14,-1.8144558258461128e14,-4.128940136737588e13,-4.312871417790415e12,0,0,0,0,0,0},{0,0,0.2,2788.9909571394373,757954.1832518226,5.731896737696369e7,1.9467522753006718e9,3.6752178916813446e10,4.342367668300395e11,3.456417626472393e12,1.945586960892444e13,8.002395364327356e13,2.45820394128803e14,5.716362792738416e14,1.0128387071687611e15,1.366953901980593e15,1.394401321620245e15,1.0566084586443459e15,5.762621684739365e14,2.1376666367087962e14,4.827633490236368e13,5.008495840014676e12,0,0,0,0,0},{0,0,0,-6.330200791583441,-9055.326402164885,-1.652817817493035e6,-1.0353224612455417e8,-3.1443424489415655e9,-5.5099878956043625e10,-6.172115086692606e11,-4.719665182766741e12,-2.5747979102863504e13,-1.0327781515594061e14,-3.107840698734769e14,-7.103751521762541e14,-1.240428258290523e15,-1.6532690684504498e15,-1.6682200228770388e15,-1.2521020933688375e15,-6.771547917071688e14,-2.4931732630126084e14,-5.592820354683055e13,-5.767358846077505e12,0,0,0,0},{0,0,0,-0.0008658008658008658,20.90332181816109,19761.47677528686,2.9786440888161287e6,1.6669440060546213e8,4.697988868588864e9,7.804846936458011e10,8.399601182809939e11,6.225772187860445e12,3.3126459614313613e13,1.3018123394897244e14,3.8510657020790756e14,8.676089997567239e14,1.4962826923778152e15,1.9729018315606848e15,1.9720423433634065e15,1.4678534354359658e15,7.87971726203723e14,2.8819855324002194e14,6.4264855713435055e13,6.591267252660006e12,0,0,0}}},\n  {{{0,-0.26666666666666666,42.666666666666664,-2.9489904e6,-2.4170614799515897e8,-7.873218384663625e9,-1.405806642582533e11,-1.580544280937379e12,-1.2090386727568768e13,-6.6042366119374336e13,-2.65868882310363e14,-8.051739775983438e14,-1.857133409027708e15,-3.2801533762918805e15,-4.4314102156757605e15,-4.540487334262183e15,-3.465694635513561e15,-1.9084456023722588e15,-7.161903536719025e14,-1.6388911387603578e14,-1.725148567116166e13,0,0,0,0,0,0},{0,0,-0.06666666666666667,69509,1.060304375629801e7,5.578470160261151e8,1.479270594762511e10,2.3394997790297177e11,2.4264918279035615e12,1.7521030242218191e13,9.164276368056673e13,3.5664655366664625e14,1.05113206044863e15,2.370956512005569e15,4.1103527541472025e15,5.465870295900822e15,5.524844013590857e15,4.167585692773008e15,2.2713467255841065e15,8.446251275105141e14,1.917140907650062e14,2.0033983360058703e13,0,0,0,0,0},{0,0,0,-647.6,-246094.15250627516,-2.4198988932992294e7,-1.038980191421611e9,-2.444203632713007e10,-3.570077983612116e11,-3.498184851505777e12,-2.4202280989027707e13,-1.2243539925589284e14,-4.638933812042697e14,-1.3374968892379465e15,-2.9619773218874905e15,-5.055557384458424e15,-6.63336672836086e15,-6.627413577564161e15,-4.948584988121854e15,-2.672802925395651e15,-9.85971732202283e14,-2.2219508817519653e14,-2.306943538431002e13,0,0,0,0},{0,0,0,1,2271.693671824363,557112.5844066336,4.4760097703188986e7,1.7065933499774168e9,3.710782171508295e10,5.123704928625934e11,4.812792440204033e12,3.2217907329529703e13,1.587313473404659e14,5.885027219728265e14,1.6662726075609255e15,3.6337254670711775e15,6.120647631772677e15,7.939169799895172e15,7.852589702803345e15,5.811463115882084e15,3.1140914617754305e15,1.1406361435129525e15,2.5541160604057522e14,2.6365069010640023e13,0,0,0},{0,0,0,0,-3.4996138996138995,-5117.489627447047,-1.0252931044630904e6,-7.317764631938808e7,-2.5799387735712266e9,-5.305164082877038e10,-7.024571823174719e11,-6.386343641756565e12,-4.1646702455182195e13,-2.0082495548407444e14,-7.313240134063729e14,-2.0393725447628995e15,-4.389582156680698e15,-7.310312322718411e15,-9.388355782488322e15,-9.204613694406318e15,-6.75889981067322e15,-3.596449851906305e15,-1.3090123157014138e15,-2.9144089798248025e14,-2.9927916174240027e13,0,0}},{{0.5333333333333333,-84.86666666666666,13652.733333333334,1.1580743990317933e7,7.862366731187782e8,2.251285321019336e10,3.6126970425140875e11,3.6950296972946763e12,2.591996982109244e13,1.3060390853963811e14,4.872588592243463e14,1.3728326184824732e15,2.955623641337104e15,4.887098724271143e15,6.197174104927707e15,5.97435514747484e15,4.3000613327664335e15,2.2374288971381245e15,7.949002570465775e14,1.725148567116166e14,1.725148567116166e13,0,0,0,0,0,0},{0,0.13333333333333333,-129.13333333333333,-298814.1125960173,-3.756041742575181e7,-1.7285393875937688e9,-4.103692427927973e10,-5.885977025139501e11,-5.58378211235412e12,-3.710691956026596e13,-1.7949438370340738e14,-6.486221072613646e14,-1.7811709048134368e15,-3.7547456752020125e15,-6.099911491230204e15,-7.620161622284745e15,-7.252241847091468e15,-5.161779213919494e15,-2.65959108326093e15,-9.367258010130061e14,-2.0173108244503553e14,-2.0033983360058703e13,0,0,0,0,0},{0,0,0.2,3164.105012550278,966660.7328705739,8.210917088666102e7,3.1326839561683073e9,6.649903112671311e10,8.850518794573038e11,7.957226405896327e12,5.07826266196542e13,2.3800231954847772e14,8.384811979980429e14,2.2550231205737835e15,4.671485049782065e15,7.477796091175499e15,9.223459380199674e15,8.681741711096794e15,6.119697195173343e15,3.1262899720312225e15,1.0927437571553231e15,2.3372980586735153e14,2.306943538431002e13,0,0,0,0},{0,0,0,-6.387343648726298,-10280.88273895997,-2.1078979265578426e6,-1.482407551004836e8,-5.055907877161912e9,-9.959690402930423e10,-1.2564964279090027e12,-1.0850994538243352e13,-6.710880700339022e13,-3.066885058028235e14,-1.0583451011894089e15,-2.7975704791687135e15,-5.711120851896802e15,-9.02764697095337e15,-1.10141153245736e16,-1.0268428227212878e16,-7.177155441784758e15,-3.6389915826190465e15,-1.2633984960937452e15,-2.6859414054589525e14,-2.6365069010640023e13,0,0,0},{0,0,0,-0.0007722007722007722,21.103579218418492,22442.61268540996,3.7985964582853154e6,2.3859794710035256e8,7.549734457672135e9,1.4096953641872162e11,1.7083678819651787e12,1.429842236994991e13,8.623782828471784e13,3.860843010721173e14,1.3096501921718585e15,3.4118527798952095e15,6.87871387556754e15,1.0756091873106256e16,1.2998921089798014e16,1.2017683521567574e16,8.337385441942756e15,4.199116583526632e15,1.4491212804099838e15,3.0640485606960025e14,2.9927916174240027e13,0,0}}},\n  {{{0,-0.26666666666666666,47.6,-4.0895524e6,-3.731306038384463e8,-1.3541340916361624e10,-2.698066970601489e11,-3.3929623532272686e12,-2.912501505968892e13,-1.792856932147624e14,-8.178001345526671e14,-2.825665552462115e15,-7.50119392296909e15,-1.5421119083184214e16,-2.46069867650868e16,-3.036845308130471e16,-2.8690118845028184e16,-2.035084821245431e16,-1.0484659979090722e16,-3.7026001121730715e15,-8.013593344023481e14,-8.013593344023481e13,0,0,0,0,0},{0,0,-0.06666666666666667,86351.4,1.4665579721461162e7,8.590687659123018e8,2.5382910206863052e10,4.4799089906092065e11,5.197549704634442e12,4.2116936060807555e13,2.4826316584141612e14,1.0947767893988114e15,3.681377038739813e15,9.557508586062312e15,1.928610504650084e16,3.02919821648759e16,3.68806750791929e16,3.4434258261705336e16,2.417434564050942e16,1.2341328990945152e16,4.323002238921354e15,9.288483194209035e14,9.227774153724008e13,0,0,0,0},{0,0,0,-721.6,-305383.484221278,-3.3425150558028962e7,-1.5976051302013426e9,-4.187439493752403e10,-6.825325957092296e11,-7.480870721296626e12,-5.808146779339898e13,-3.311329496999199e14,-1.421626589092943e15,-4.676542004989115e15,-1.1920141073940436e16,-2.3681684177511636e16,-3.670117139829947e16,-4.416718389230369e16,-4.0819063755733816e16,-2.8399638601169932e16,-1.4382537224523932e16,-5.001926254562189e15,-1.067785294930921e15,-1.054602760425601e14,0,0,0},{0,0,0,1,2530.72069885139,690925.9210848194,6.1773305205390684e7,2.6215136024899797e9,6.350186022610584e10,9.783737218417522e11,1.0279134468481693e13,7.721651964503972e13,4.287219179136568e14,1.8010359258476095e15,5.818025681991426e15,1.460298629964141e16,2.8630185446713732e16,4.3862993135598696e16,5.225649481076826e16,4.7866759281312376e16,3.3039905722220736e16,1.6614027170527334e16,5.741105116040728e15,1.218493729951201e15,1.1971166469696011e14,0,0},{0,0,0,0,-3.4996534996535,-5700.334296291716,-1.2710928628651593e6,-1.0093436202872059e8,-3.9601706432110143e9,-9.070909301290082e10,-1.3400853937505337e12,-1.3626158227398803e13,-9.970831360069828e13,-5.418127541007572e14,-2.2355599883931558e15,-7.112384380705038e15,-1.7619359196305414e16,-3.415306825634278e16,-5.180484759888663e16,-6.1176142561382664e16,-5.559884636602166e16,-3.810791930105565e16,-1.904137287813753e16,-6.54222329112722e15,-1.3812884388110782e15,-1.350593140170832e14,0}},{{0.5333333333333333,-94.73333333333333,16992.6,1.6284696076892616e7,1.2354648054405298e9,3.953909107382076e10,7.10090176204812e11,8.145748277303172e12,6.428556882265668e13,3.6590982390426875e14,1.5502704689894388e15,4.993707604738265e15,1.2398036774129854e16,2.3903947362329856e16,3.5861500931598584e16,4.170569864198567e16,3.720611350142881e16,2.496972481358118e16,1.2193217903736838e16,4.0882542918542015e15,8.414273011224655e14,8.013593344023481e13,0,0,0,0,0},{0,0.13333333333333333,-143.93333333333334,-375927.24292232457,-5.278450853716055e7,-2.7125229474487467e9,-7.195565577844536e10,-1.1548552468425483e12,-1.2286437334588822e13,-9.185201223250092e13,-5.0188188887196794e14,-2.0594686450678228e15,-6.465664297334423e15,-1.5717197545820644e16,-2.9773055055612476e16,-4.400198164051586e16,-5.051762663082156e16,-4.456554705592129e16,-2.961652536463062e16,-1.4337322088783526e16,-4.770124322093573e15,-9.749871901895235e14,-9.227774153724008e13,0,0,0,0},{0,0,0.2,3564.768442556024,1.2164908134451648e6,1.1533096610258973e8,4.911578657437878e9,1.1646951484526044e11,1.7342546082017852e12,1.7484020896956271e13,1.2551336178335739e14,6.644159134672428e14,2.6578960250785765e15,8.171790366631361e15,1.9520413309070224e16,3.643312168795762e16,5.316310532259258e16,6.036333626184364e16,5.273684513072426e16,3.4747435047530596e16,1.6693177509884468e16,5.516045100269669e15,1.120515432952201e15,1.054602760425601e14,0,0,0},{0,0,0,-6.441397702780352,-11590.853392628731,-2.6527484863642906e6,-2.081436499095543e8,-7.921865093783669e9,-1.742928669332534e11,-2.459659492823631e12,-2.3815795421580676e13,-1.6566332438371656e14,-8.550505095885778e14,-3.35024333679381e15,-1.0123366574848608e16,-2.382922456508392e16,-4.391688037572645e16,-6.338437811869518e16,-7.128043872024897e16,-6.174789017813265e16,-4.037820760670786e16,-1.9267335122747188e16,-6.327906043885648e15,-1.2783495622996812e15,-1.1971166469696011e14,0,0},{0,0,0,-0.000693000693000693,21.292986207825482,25309.29515631317,4.780381324186655e6,3.3492374593636465e8,1.1823731636670485e10,2.4653622561126352e11,3.3416154029679727e12,3.135410116283323e13,2.126729757114602e14,1.0752440457778672e15,4.1409771925451395e15,1.233117595422076e16,2.8664288229529544e16,5.225580879389953e16,7.470388245200797e16,8.330526615716496e16,7.162566653980231e16,4.652411455487196e16,2.2066154752113164e16,7.207543889154556e15,1.4488180958196198e15,1.350593140170832e14,0}}},\n  {{{0,-0.26666666666666666,52.8,-5575520,-5.632173785675783e8,-2.264573851170883e10,-5.005643706894967e11,-6.997344021760841e12,-6.694984221034684e13,-4.609967645533807e14,-2.3627840501606355e15,-9.225264284921132e15,-2.7870762591869724e16,-6.579211913432579e16,-1.219269835005012e17,-1.7736434748785085e17,-2.014372433713144e17,-1.7653028386935773e17,-1.1695051216654723e17,-5.660662707425071e16,-1.887747613881895e16,-3.8756651445640835e15,-3.691109661489603e14,0,0,0,0},{0,0,-0.06666666666666667,106080.2,1.9949649623509426e7,1.2939911180306041e9,4.2363534986906364e10,8.295293999689657e11,1.0698737964202242e13,9.66363247645834e13,6.372094391758614e14,3.1574388239306565e15,1.1998128373402768e16,3.545022868007551e16,8.214242163461622e16,1.498448307438975e17,2.1504119173799283e17,2.413696663998524e17,2.0935359957837e17,1.3743658725905952e17,6.598443928517343e16,2.1845367673730504e16,4.455696662798164e15,4.218411041702404e14,0,0,0},{0,0,0,-799.6,-374795.69873168645,-4.541501586740491e7,-2.4033232898026366e9,-6.979280795787791e10,-1.2620648339616711e12,-1.5377051478454482e13,-1.3307719247169702e14,-8.486965787241058e14,-4.0942474686362435e15,-1.521973082413072e16,-4.415040755395909e16,-1.0071975616913048e17,-1.8128971785509302e17,-2.5715892674943798e17,-2.8571552176641878e17,-2.4559351140219373e17,-1.5993872560572854e17,-7.623776610305851e16,-2.5076921618640444e16,-5.084895471889925e15,-4.7884665878784044e14,0,0},{0,0,0,1,2803.746339877031,847540.594948172,8.387088415905303e7,3.940171303117425e9,1.057365813099728e11,1.8072133228725144e12,2.1105818071761484e13,1.7671976487014494e14,1.0975457428896409e15,5.180820146125421e15,1.8912018709994548e16,5.402178465503179e16,1.2161716004687645e17,2.1639908949667334e17,3.038797903142797e17,3.3462662569227334e17,2.853611054467527e17,1.845190954017643e17,8.739229013859099e16,2.857951555540064e16,5.764577084638233e15,5.402372560683328e14,0},{0,0,0,0,-3.499687304565353,-6314.674103265669,-1.5587237516807944e6,-1.3697283658064875e8,-5.948433133232432e9,-1.5092828425611444e11,-2.4733417883407153e12,-2.7953736163984633e13,-2.2798507703929834e14,-1.3857335293027922e15,-6.424397479638844e15,-2.30959575834661e16,-6.511275177576189e16,-1.4492407203283318e17,-2.5530540726016147e17,-3.553604828117912e17,-3.882498975703751e17,-3.287640702824053e17,-2.1123805647598467e17,-9.947298157155096e16,-3.2360331425024412e16,-6.496023591260685e15,-6.06119848271788e14}},{{0.5333333333333333,-105.13333333333334,20908.2,2.2492357135156598e7,1.8958309719109235e9,6.741542072724315e10,1.3467332712719966e12,1.721588784468186e13,1.517969171625762e14,9.68623788198903e14,4.620809634242403e15,1.6852457171265348e16,4.7703107096260296e16,1.0578958618367114e17,1.8461804798071936e17,2.5344496869855994e17,2.7218267219337744e17,2.2596420895370938e17,1.4205763700894958e17,6.535328208213089e16,2.074610040494806e16,4.0602206276385635e15,3.691109661489603e14,0,0,0,0},{0,0.13333333333333333,-159.53333333333333,-467299.4470188559,-7.287456290234995e7,-4.157830371714083e9,-1.2252070190736252e11,-2.186990850552188e12,-2.5926110989630664e13,-2.1653318849256094e14,-1.326322841173958e15,-6.127978836743251e15,-2.1781741790169716e16,-6.036681088296472e16,-1.3152785185627565e17,-2.2611628841057744e17,-3.06435432704922e17,-3.2542265994529766e17,-2.6752084016889734e17,-1.667276585666764e17,-7.611122760127998e16,-2.3994120798097664e16,-4.666617214883284e15,-4.218411041702404e14,0,0,0},{0,0,0.2,3991.1974633728914,1.5126843075902709e6,1.591637552639756e8,7.523047958345665e9,1.981274589389424e11,3.28062880467928e12,3.684945335931074e13,2.9550727863338325e14,1.753482804920874e15,7.897512890513422e15,2.748953095986418e16,7.486280786775282e16,1.6070534554507542e17,2.7277027276789126e17,3.6558244328737126e17,3.8447574794727104e17,3.133601408355606e17,1.9380644168944502e17,8.786770088566074e16,2.7529585606062684e16,5.324318801283845e15,4.7884665878784044e14,0,0},{0,0,0,-6.4926797540624035,-12985.995991128793,-3.298836226795991e6,-2.871680700153716e8,-1.2127517294150885e10,-2.9628375638388446e11,-4.648967460183313e12,-5.014705791678989e13,-3.896342464153759e14,-2.254104416220173e15,-9.943117755509682e15,-3.401307144070906e16,-9.127203209515408e16,-1.934632666986177e17,-3.247778760884149e17,-4.311079790213169e17,-4.495396895431404e17,-3.636200897900059e17,-2.2336772996888845e17,-1.0065183695567805e17,-3.1360509612206944e16,-6.034695712672399e15,-5.402372560683328e14,0},{0,0,0,-0.0006253908692933083,21.472659314327856,28363.229946619325,5.944698143021086e6,4.619832444694353e8,1.809376056308954e10,4.1886637559539825e11,6.31178461726564e12,6.596977935869714e13,4.997768062476191e14,2.8319808910505985e15,1.2277848441468622e16,4.138797722399096e16,1.0967212084846331e17,2.299374395484744e17,3.823298988047106e17,5.032265327743345e17,5.208059242745585e17,4.1843448110868006e17,2.5548313610900355e17,1.1449196659647726e17,3.5494695749103796e16,6.799083515396579e15,6.06119848271788e14}}}\n};\n\nstatic const double ImEI[21][2][5][27] =\n {\n  {{{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,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,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,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,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,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  {{{0,-0.03333333333333333,-0.06666666666666667,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.3,-0.26666666666666666,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.5,2.1333333333333333,1.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,0,-1.8333333333333333,-6.133333333333334,-4.266666666666667,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.03333333333333333,4.2,12.8,8.533333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.06666666666666667,0.06666666666666667,0.06666666666666667,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,0.43333333333333335,0.26666666666666666,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.2666666666666666,-2.933333333333333,-1.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,0.16666666666666666,4.1,8.266666666666667,4.266666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.06666666666666667,-0.4666666666666667,-9,-17.066666666666666,-8.533333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.06666666666666667,1.0666666666666667,1.0666666666666667,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,-2.6,-9.066666666666666,-6.4,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,11.6,27.733333333333334,17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-3.533333333333333,-29.066666666666666,-59.733333333333334,-34.13333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.009523809523809525,8,56.68571428571428,107.27619047619048,58.51428571428571,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.13333333333333333,-0.26666666666666666,-1.6,-1.0666666666666667,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.2,5.933333333333334,12.266666666666667,6.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-3.2,-22.266666666666666,-36.266666666666666,-17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.06666666666666667,10.066666666666666,52.53333333333333,76.8,34.13333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.01904761904761905,-0.26666666666666666,-21.714285714285715,-99.35238095238095,-136.53333333333333,-58.51428571428571,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.1,12.6,38.4,25.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,-8.9,-64.26666666666667,-123.73333333333333,-68.26666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,1.5,35.2,170.13333333333333,273.06666666666666,136.53333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.271428571428571,-84,-341.9428571428571,-497.37142857142857,-234.05714285714285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0015873015873015873,11.88888888888889,159.4920634920635,589.6126984126984,806.1968253968254,364.0888888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.2,-1.4,-27,-51.2,-25.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.3,24.233333333333334,113.33333333333333,157.86666666666667,68.26666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-5.4,-81.86666666666666,-281.06666666666666,-341.3333333333333,-136.53333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.04285714285714286,17.1,183.65714285714284,546.7428571428571,614.4,234.05714285714285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0031746031746031746,-0.15555555555555556,-36.93015873015873,-337.26984126984127,-924.4444444444445,-988.2412698412699,-364.0888888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.13333333333333333,59.733333333333334,332.8,546.1333333333333,273.06666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-21.2,-260.26666666666665,-921.6,-1228.8,-546.1333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,2,79.88571428571429,648.5333333333333,1896.8380952380953,2262.552380952381,936.2285714285714,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-7.015873015873016,-186.48888888888888,-1262.120634920635,-3319.0603174603175,-3692.9015873015874,-1456.3555555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0005772005772005772,15.806060606060607,349.617316017316,2133.1116883116883,5241.461471861472,5560.630303030303,2118.3353535353535,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.26666666666666666,-3.7333333333333334,-149.33333333333334,-554.6666666666666,-682.6666666666666,-273.06666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.4,64.13333333333334,541.8666666666667,1433.6,1501.8666666666666,546.1333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-7.771428571428571,-209.86666666666667,-1260.4952380952382,-2852.5714285714284,-2730.6666666666665,-936.2285714285714,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.031746031746031744,24.755555555555556,462.5015873015873,2365.765079365079,4892.444444444444,4421.079365079365,1456.3555555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0011544011544011544,-0.11313131313131314,-53.58268398268398,-839.8961038961039,-3909.818181818182,-7624.588744588745,-6619.79797979798,-2118.3353535353535,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.16666666666666666,191.66666666666666,1668.2666666666667,4753.066666666667,5461.333333333333,2184.5333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-41.5,-775.8095238095239,-4313.6,-9976.685714285713,-10142.47619047619,-3744.9142857142856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,2.5,152.63492063492063,1872.888888888889,8569.092063492064,17664.812698412698,16644.06349206349,5825.422222222222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-8.762626262626263,-352.2020202020202,-3580.7676767676767,-14673.19595959596,-28117.59191919192,-25155.23232323232,-8473.341414141414,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0002775002775002775,19.731934731934732,655.7495837495837,5983.52047952048,22836.666267066266,41605.430303030305,35848.75213675214,11732.318881118881,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.3333333333333333,-7.666666666666667,-528.2,-3225.6,-7074.133333333333,-6553.6,-2184.5333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.5,135.11904761904762,1809.8666666666666,7751.314285714286,14345.752380952381,12014.933333333332,3744.9142857142856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-10.26984126984127,-436.8888888888889,-4089.930158730159,-14826.869841269841,-24894.577777777777,-19556.774603174603,-5825.422222222222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.025252525252525252,32.86363636363637,955.6767676767677,7544.371717171717,24809.761616161617,39106.45656565657,29391.90303030303,8473.341414141414,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.000555000555000555,-0.08935508935508936,-71.26440226440226,-1727.040959040959,-12325.773426573427,-38017.645288045285,-57329.99658119658,-41714.91157731158,-11732.318881118881,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.2,489.6,6106.971428571429,26214.4,50556.34285714286,44938.97142857143,14979.657142857142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-71.8,-1901.8920634920635,-15197.155555555555,-53000.93968253968,-90398.06984126984,-74066.08253968254,-23301.68888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,3,260.43463203463205,4506.052525252525,29564.416738816737,91759.85685425685,144822.26608946608,112271.77373737373,33893.36565656566,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-10.51048951048951,-596.8428904428904,-8522.413053613054,-49961.41351981352,-143888.94731934732,-215275.8303030303,-160341.69137529138,-46929.275524475524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0001554001554001554,23.661849261849262,1106.6579642579643,14141.066045066045,77056.03978243978,210652.15602175603,303084.9044289044,219003.28578088578,62572.367365967366,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.4,-13.6,-1447.5428571428572,-13136,-44470.857142857145,-70217.14285714286,-52428.8,-14979.657142857142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.6,247.18412698412698,4825.422222222222,30428.749206349206,86497.11746031746,123062.04444444444,85716.92698412699,23301.68888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-12.86926406926407,-795.3656565656565,-10742.07215007215,-56978.655122655124,-146297.53535353535,-194603.1468975469,-129218.45656565657,-33893.36565656566,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.02097902097902098,41.329137529137526,1733.909557109557,19631.14219114219,94030.1724941725,225862.41491841493,286647.4368298368,183806.32913752913,46929.275524475524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0003108003108003108,-0.07397047397047397,-89.75540015540015,-3126.114063714064,-31870.1898989899,-142693.88282828283,-326997.6515928516,-400854.2284382284,-250289.46946386946,-62572.367365967366,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.23333333333333334,1074.7333333333333,18153.244444444445,108464.35555555555,307655.1111111111,449285.68888888886,326223.64444444445,93206.75555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-114.1,-4069.7131313131313,-44104.92121212121,-214282.8606060606,-537693.0909090909,-723676.3151515152,-495690.4727272727,-135573.46262626263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,3.5,410.27630147630146,9526.587723387724,84649.39362859362,365700.5550893551,848556.257964258,1.0798214464646464e6,709153.4968142968,187717.1020979021,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-12.25897435897436,-936.1311577311577,-17890.690287490288,-141813.89526029525,-567884.4618492619,-1.2480050523698523e6,-1.524549659052059e6,-969871.6941724942,-250289.46946386946,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0000959824489236254,27.593994241053064,1731.1486631016044,29547.359934183463,217402.40296174414,825519.0614561909,1.7431695357191828e6,2.0641979866721514e6,1.280893167256273e6,323904.019306184,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.4666666666666667,-21.933333333333334,-3355.6222222222223,-42348.08888888889,-203582.57777777777,-479869.15555555554,-594921.2444444444,-372827.02222222224,-93206.75555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.7,410.7262626262626,11022.440404040404,96074.47272727273,386563.103030303,819299.296969697,946101.5272727272,563477.2040404041,135573.46262626263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-15.552602952602953,-1319.1589743589743,-24329.85236985237,-177655.00916860916,-643821.182905983,-1.273099178865579e6,-1.3992012382284382e6,-803012.0478632479,-187717.1020979021,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.017948717948717947,50.09052059052059,2871.1639471639473,44222.36891996892,290731.34296814294,983393.9045843045,1.851893577000777e6,1.9625562306138305e6,1.0950164289044288e6,250289.46946386946,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0001919648978472508,-0.06315645139174551,-108.91493212669683,-5170.4839160839165,-71524.53607568901,-438562.81431509665,-1.4125950964486494e6,-2.565467062004662e6,-2.6439125666803783e6,-1.4428451769093652e6,-323904.019306184,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,2116.266666666667,46478.36998556999,367133.99595959595,1.4218853217893217e6,3.00440476998557e6,3.5321728923520925e6,2.169175402020202e6,542293.8505050505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-170.4,-7878.639405039405,-111119.03776223776,-714162.356043956,-2.447790332334332e6,-4.76751157962038e6,-5.287365042424242e6,-3.1077609125097124e6,-750868.4083916084,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,4,609.1538683538683,18291.647552447554,211297.13166833165,1.2068968343656345e6,3.8238280972360973e6,7.039391328671329e6,7.482612264180264e6,4.254920980885781e6,1.0011578778554779e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-14.007843137254902,-1385.7954019836372,-34183.56217377394,-351855.81673751085,-1.8614895882261528e6,-5.582810520261438e6,-9.861435769605558e6,-1.0149606059509119e7,-5.624151607952831e6,-1.295616077224736e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0000635071842502183,31.527506549178376,2558.0482279330577,56272.57883911754,537113.6969927904,2.6926125869276235e6,7.7549386769432835e6,1.3272122169070559e7,1.3318034401472213e7,7.228173904516948e6,1.6365676764944035e6,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-33.06666666666667,-6906.606637806638,-115649.93939393939,-746992.1893217893,-2.4357735803751806e6,-4.414610876767676e6,-4.515080496392496e6,-2.440322327272727e6,-542293.8505050505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.8,636.47881007881,22496.094483294484,259039.60572760573,1.3972491252747253e6,4.090821281118881e6,6.898882843023643e6,6.700457672105672e6,3.483195116705517e6,750868.4083916084,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-18.30773670773671,-2043.3143745143745,-49402.28837828838,-475220.8388944389,-2.3042702023310023e6,-6.28517082917083e6,-1.0076758327894328e7,-9.422355652525252e6,-4.75549991981352e6,-1.0011578778554779e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.01568627450980392,59.10452945747063,4444.078065725124,89495.53131313132,773530.6577448695,3.495176651510581e6,9.067520102966314e6,1.400317438010878e7,1.2718753849005897e7,6.2719596465652e6,1.295616077224736e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0001270143685004366,-0.05512423592918949,-128.64458637337896,-7998.43481348373,-144411.5393015653,-1.162342659386434e6,-4.9947002896331735e6,-1.2482808925253488e7,-1.8729284982381087e7,-1.6625264914387986e7,-8.04645774276415e6,-1.6365676764944035e6,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.3,3839.4,106318.05314685314,1.0702797874125873e6,5.392764956643357e6,1.5350685180419581e7,2.580816845874126e7,2.539460421818182e7,1.3515631351048952e7,3.0034736335664336e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-242.7,-14124.181152181152,-251299.74452214452,-2.0592303781551782e6,-9.185474911222111e6,-2.4106659233033635e7,-3.8236604926806524e7,-3.601039741911422e7,-1.852142074032634e7,-4.0046315114219114e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,4.5,864.0628077804548,32599.88898944193,474679.1184031655,3.455516380591957e6,1.4245058289412549e7,3.533020329192376e7,5.370181410996846e7,4.892054909771013e7,2.449892218752228e7,5.182464308898944e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-15.756965944272446,-1961.5696724328304,-60708.62697178982,-786988.0535711966,-5.303549502574206e6,-2.0688066335863516e7,-4.921807658793219e7,-7.241782910112797e7,-6.427015135233064e7,-3.1503927772517268e7,-6.546270705977614e6,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.00004422821760283061,35.461919504643966,3616.1956285200868,99702.79558212686,1.1976321362253844e6,7.643730469367165e6,2.8621924200018764e7,6.595342171821161e7,9.458539786739412e7,8.219612447821817e7,3.9589351412340805e7,8.104906588353236e6,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.6,-47.4,-13003.506293706294,-278936.1678321678,-2.3265602237762236e6,-9.980674506293707e6,-2.4597585633566435e7,-3.625286534265734e7,-3.158926858741259e7,-1.5017368167832168e7,-3.0034736335664336e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.9,935.4623043623044,42143.00606060606,619613.8922410923,4.308818503096903e6,1.6579424074592074e7,3.799234747545788e7,5.314447145174825e7,4.452023938088578e7,2.0523736496037297e7,4.0046315114219114e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-21.12561556090968,-3003.887892499657,-92251.03664962489,-1.130768793872794e6,-7.0588418527355e6,-2.5277949085518405e7,-5.502439582942548e7,-7.405439677762237e7,-6.019829813355272e7,-2.7090154341971755e7,-5.182464308898944e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.01393188854489164,68.33903526813744,6531.570529779818,166762.43296022862,1.8339761297278572e6,1.0656330035982594e7,3.6261947787464544e7,7.597502823061767e7,9.92596311986317e7,7.879468948121847e7,3.477706312550607e7,6.546270705977614e6,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.00008845643520566122,-0.04891640866873065,-148.871487026905,-11752.440015711842,-268684.86844919785,-2.74861261174986e6,-1.5174070609897017e7,-4.97030496228197e7,-1.0110656940201923e8,-1.2902029170934741e8,-1.0047113019907235e8,-4.364180470651743e7,-8.104906588353236e6,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.3333333333333333,6533.333333333333,222654.46091686093,2.778624556954157e6,1.7600566104118105e7,6.443886523574203e7,1.4451080095726496e8,2.0156645274156955e8,1.7053055852804974e8,8.009263022843823e7,1.6018526045687646e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-333,-23825.97413044472,-521880.007239819,-5.303014873879062e6,-2.974522501247772e7,-1.0042604004409708e8,-2.1250665797888386e8,-2.837202903655926e8,-2.319741694631016e8,-1.0600495177293295e8,-2.0729857235595778e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,5,1181.999610984131,54754.40911616763,980895.6130251585,8.852088141103067e6,4.5880486491885826e7,1.4637328059863028e8,2.967933223074854e8,3.832574936190464e8,3.0507729190651166e8,1.3638063970786697e8,2.6185082823910456e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-17.506265664160402,-2679.1916209005994,-101698.47428388605,-1.6209314727333898e6,-1.3536137961823797e7,-6.6368244028934255e7,-2.0306241420633388e8,-3.984997390155933e8,-5.012577425662849e8,-3.9049673738068247e8,-1.7144994706131846e8,-3.2419626353412945e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.000032049433045529426,39.39695786781532,4934.437997592661,166727.4017375813,2.460969788432378e6,1.945561056293358e7,9.154241300341979e7,2.712219032798776e8,5.1868580194167066e8,6.387408437835916e8,4.888559794904897e8,2.114323457831279e8,3.946737121285054e7,0,0,0,0,0,0,0,0,0,0,0}},{{0.6666666666666666,-65.33333333333333,-22842.255167055166,-610794.4205128205,-6.384583827195027e6,-3.4760849444599845e7,-1.1109825668873349e8,-2.199600400907537e8,-2.733161006545454e8,-2.075734000087024e8,-8.810189325128205e7,-1.6018526045687646e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,1,1318.9482608894373,73805.46920791626,1.3491923253530783e6,1.1743463056410257e7,5.730339427900727e7,1.7019760844996572e8,3.196860685490927e8,3.8177487075555557e8,2.810897971178939e8,1.1636988039073084e8,2.0729857235595778e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-23.999221968262216,-4237.817551557489,-161226.06704008067,-2.4533664806920635e6,-1.9149318022051428e7,-8.689604748579635e7,-2.450198244808148e8,-4.4258655082519025e8,-5.1267962114181876e8,-3.683579087309619e8,-1.4947318111982217e8,-2.6185082823910456e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.012531328320802004,77.76920671347916,9214.54894194213,291039.0946701733,3.9691610755076366e6,2.8811933867454045e7,1.2415265791155085e8,3.367590805724054e8,5.902267861563345e8,6.673985861532614e8,4.701430309700768e8,1.8765976023802492e8,3.2419626353412945e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.00006409886609105885,-0.04397182213846637,-169.53941844936594,-16578.624554339185,-468446.0812958478,-5.9377916416310035e6,-4.0923115567559e7,-1.696412196057358e8,-4.465302941617743e8,-7.640784094103152e8,-8.472253347457047e8,-5.871720202796443e8,-2.3116603138955316e8,-3.946737121285054e7,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.36666666666666664,10559.266666666666,434294.7652086476,6.574489355455003e6,5.10204267471091e7,2.3250794534097537e8,6.64917593632177e8,1.2277649331804926e9,1.4624266471674209e9,1.0850784646757164e9,4.560568591831071e8,8.291942894238311e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-443.3,-38255.76555264481,-1.0114779064221641e6,-1.2470381821703581e7,-8.571332767814559e7,-3.6026736682270384e8,-9.722698334254823e8,-1.7186034725420127e9,-1.9784568926620624e9,-1.4283144396604905e9,-5.869822733026594e8,-1.0474033129564182e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,5.5,1569.961476844449,87625.69572649573,1.8939060047826958e6,2.0732385792547733e7,1.3166046654165429e8,5.228893340210738e8,1.352134487330338e9,2.311593394202756e9,2.5906866318906407e9,1.8295657646295648e9,7.381699538930948e8,1.2967850541365178e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-19.255693581780537,-3554.4014008396293,-162426.07358635293,-3.1217844988459083e6,-3.16126055748029e7,-1.8987086397373933e8,-7.230764038384147e8,-1.809469262049569e9,-3.0129885415469217e9,-3.304452922123214e9,-2.2917775413786182e9,-9.105686358393375e8,-1.5786948485140216e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.00002397297591805601,43.33244851258581,6541.62812737135,265926.5903814425,4.731094982759278e6,4.5340842546994835e7,2.612763288477679e8,9.633487135585018e8,2.348932232111619e9,3.828700312931434e9,4.124825501869743e9,2.8177588722517457e9,1.105086393959815e9,1.894433818216826e8,0,0,0,0,0,0,0,0,0,0}},{{0.7333333333333333,-87.26666666666667,-37956.79708396179,-1.2378745500251383e6,-1.5839376732026143e7,-1.06514876608547e8,-4.268125048599296e8,-1.084457648638713e9,-1.7937872069180493e9,-1.927228914871795e9,-1.297559501340573e9,-4.9751657365429866e8,-8.291942894238311e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,1.1,1798.4311052896192,122419.20256145643,2.723675026625387e6,2.8991573532491863e7,1.7462463657162815e8,6.49987059367659e8,1.5663069635696964e9,2.4893757054309864e9,2.5923743423070254e9,1.7021667641938875e9,6.393524389504802e8,1.0474033129564182e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-26.922953688897962,-5782.824037469239,-267059.29319830495,-4.940120546192026e6,-4.71164384933979e7,-2.6376188135589048e8,-9.316258847784076e8,-2.1581550271623797e9,-3.3260680887754226e9,-3.3792200077329974e9,-2.1743360218110523e9,-8.030092065999206e8,-1.2967850541365178e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.011387163561076604,87.37526544737474,12575.685027901907,481630.25459747476,7.978130346470807e6,7.071870426544951e7,3.7573583030587316e8,1.2761080113371363e9,2.867315117761134e9,4.312316120466565e9,4.2944103377974663e9,2.717461330888649e9,9.895033782650386e8,1.5786948485140216e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.00004794595183611202,-0.039938977879481315,-190.60366239225843,-22626.351358782555,-774685.3323404123,-1.1919507515836926e7,-1.0026023448105781e8,-5.122220252548393e8,-1.6875254790953407e9,-3.7006690484617324e9,-5.456117441460743e9,-5.344261556173933e9,-3.3347814351400876e9,-1.1998080848706565e9,-1.894433818216826e8,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.4,16358.4,798958.1317110135,1.4416265779644504e7,1.3432181467691442e8,7.434303361587911e8,2.626393853597904e9,6.143042747321345e9,9.628736625974842e9,1.0003891869771244e10,6.609352950962489e9,2.5137679510954037e9,4.189613251825673e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-575.6,-58965.4028822227,-1.851578588510492e6,-2.721337278865888e7,-2.246109735140044e8,-1.1467505597679276e9,-3.823590344956376e9,-8.562024604039784e9,-1.2971313192672047e10,-1.3113370455110611e10,-8.471497747887982e9,-3.1621604781636624e9,-5.187140216546071e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,6,2034.9461170318891,134714.58794018233,3.456620690885645e6,4.5099821439376034e7,3.4389086889063436e8,1.6588888433384745e9,5.299781267706849e9,1.1477806342984741e10,1.6928250138062456e10,1.6740562578044119e10,1.0617264990476751e10,3.9016315541846533e9,6.314779394056087e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-21.00521739130435,-4602.940974829003,-249320.1853983052,-5.686343886484196e6,-6.861345311881912e7,-4.947368541065237e8,-2.288195708861567e9,-7.073833639317552e9,-1.4920448268974035e10,-2.1533210358591484e10,-2.091140714317489e10,-1.3059753134332243e10,-4.736084545542065e9,-7.577735272867304e8,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.00001840349666436623,47.2682769726248,8466.622898449223,407758.88048708765,8.605464337728756e6,9.82448816901423e7,6.795288568188525e8,3.042463166214668e9,9.163485366790909e9,1.8918381774570183e10,2.681818912368284e10,2.565065895730421e10,1.5811505960561546e10,5.669268611552575e9,8.981019582657546e8,0,0,0,0,0,0,0,0,0}},{{0.8,-113.6,-60265.06342202689,-2.354631489783282e6,-3.6188869743358806e7,-2.9423519790246165e8,-1.4410252993176384e9,-4.548903494657308e9,-9.579486167460339e9,-1.3604387251458832e10,-1.2876514478657967e10,-7.78768167803846e9,-2.7232486136866875e9,-4.189613251825673e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,1.2,2385.605764445393,194164.23206487356,5.166394900690916e6,6.6000166193341576e7,4.804116328378407e8,2.1848240357089143e9,6.539431397703931e9,1.3229596648755936e10,1.8207931242597847e10,1.6804843678929035e10,9.955319107909575e9,3.4215174889909663e9,5.187140216546071e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-29.892234063778012,-7677.3315851704865,-423195.2594681369,-9.353395630081888e6,-1.069960803506276e8,-7.235025322877827e8,-3.121175266299493e9,-8.978155548848114e9,-1.7608717027453564e10,-2.363934658860129e10,-2.1376840213692146e10,-1.2449678653930527e10,-4.217370523887458e9,-6.314779394056087e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.010434782608695653,97.14108009278826,16699.23576343072,762726.8199599064,1.5085840946795626e7,1.603035047267927e8,1.0283599406244267e9,4.2643450431212244e9,1.1894505365747297e10,2.2759909848626797e10,2.9943147410467827e10,2.6624309126235004e10,1.5285712870737015e10,5.114971309185431e9,7.577735272867304e8,0,0,0,0,0,0,0,0,0,0},{0,0,-0.00003680699332873246,-0.03658615136876006,-212.02777987521023,-30047.89230250005,-1.2262431435256745e6,-2.251696238611375e7,-2.2695429228591478e8,-1.3994804759815316e9,-5.627673799689461e9,-1.5316193575786037e10,-2.8723952643539726e10,-3.7161678742566864e10,-3.257762113150629e10,-1.8477746149163006e10,-6.118319590685452e9,-8.981019582657546e8,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.43333333333333335,24459.933333333334,1.39948310974227e6,2.9664356557875972e7,3.264766937613553e8,2.1527966210326242e9,9.174872352635342e9,2.63526819412877e10,5.20431450209414e10,7.080795804555496e10,6.521613086318431e10,3.8830607464800354e10,1.3486564563019785e10,2.0748560866184285e9,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-731.9,-87814.82467992813,-3.230537622359549e6,-5.578284651915676e7,-5.439079051073674e8,-3.308797228275689e9,-1.3310466229841167e10,-3.660457080636495e10,-6.987539486462099e10,-9.251142702281056e10,-8.331826353464978e10,-4.868864058693869e10,-1.6643954260047829e10,-2.5259117576224346e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,6.5,2583.9516267845465,200215.24405318496,6.016616118581815e6,9.221263263214113e7,8.305750837148908e8,4.77383576908197e9,1.840021088352963e10,4.8939353849036934e10,9.094760672013042e10,1.177844008258246e11,1.0414198859975996e11,5.991259714028463e10,2.0207294060979477e10,3.0310941091469216e9,0,0,0,0,0,0,0,0,0,0},{0,0,0,-22.754814814814814,-5840.553656708876,-370080.7888040544,-9.881938796070835e6,-1.4003615801375034e8,-1.1925823709998772e9,-6.57143044116013e9,-2.4508050681444206e10,-6.348184946356478e10,-1.1543549489334048e11,-1.468037932397025e11,-1.27811573462042e11,-7.256032344847891e10,-2.4192621500783764e10,-3.5924078330630183e9,0,0,0,0,0,0,0,0,0},{0,0,0,0.00001443722583152868,51.20436448442445,10738.282242670764,604748.6713855794,1.493782547301189e7,2.0024139920202476e8,1.6355739069693673e9,8.723538381973366e9,3.1694134443060642e10,8.035017116564796e10,1.435061488294865e11,1.797385196878227e11,1.544460759994406e11,8.668740434615086e10,2.861538653232956e10,4.211788493935952e9,0,0,0,0,0,0,0,0}},{{0.8666666666666667,-144.73333333333332,-92115.75281787357,-4.24804535780092e6,-7.720394235745513e7,-7.459561839697425e8,-4.37688462512671e9,-1.675264548351156e10,-4.352291071881835e10,-7.819993218306961e10,-9.728719442636674e10,-8.229722149813876e10,-4.518485423006929e10,-1.4523992606328999e10,-2.0748560866184285e9,0,0,0,0,0,0,0,0,0,0,0,0},{0,1.3,3092.3493598562786,296618.58790897264,9.301895812628992e6,1.4042234137451884e8,1.2141829505065637e9,6.6136711412013e9,2.3997256897036137e10,5.988243978747334e10,1.0425835625270293e11,1.2646490694806935e11,1.0477864663163727e11,5.65370092624084e10,1.7906910138859047e10,2.5259117576224346e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,-32.90325356909285,-9960.403453242889,-646128.1687916917,-1.6817577993811376e7,-2.272190859950398e8,-1.8244572021268456e9,-9.424164410318285e9,-3.2855536243018745e10,-7.94693874291209e10,-1.3493911256397678e11,-1.6035193404744455e11,-1.3059358460620116e11,-6.944791402530933e10,-2.1722841115552937e10,-3.0310941091469216e9,0,0,0,0,0,0,0,0,0,0},{0,0,0.00962962962962963,107.05323934367682,21670.89761846037,1.1640149545323383e6,2.7098524121223144e7,3.3995615473259085e8,2.5888012200866394e9,1.2850503978831438e10,4.34323908124913e10,1.0247227787993118e11,1.704876950572971e11,1.9917787189184882e11,1.5989240688923416e11,8.398305773017148e10,2.5988825417315273e10,3.5924078330630183e9,0,0,0,0,0,0,0,0,0},{0,0,-0.00002887445166305736,-0.033754233994114057,-233.78149683578326,-38998.16031069183,-1.8707910950575937e6,-4.041802774693853e7,-4.8079742512005436e8,-3.518362643932259e9,-1.6932117246290905e10,-5.582666431497299e10,-1.2906973332811464e11,-2.1113776786988696e11,-2.4316208621413403e11,-1.9281924836901074e11,-1.0020538726970265e11,-3.0721280779297535e10,-4.211788493935952e9,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.4666666666666667,35489.066666666666,2.3512673864932396e6,5.783459782554564e7,7.417629589224231e8,5.737794526335927e9,2.8958539764775536e10,9.980418605461873e10,2.4087583664136777e11,4.1111360367982263e11,4.938686168666484e11,4.0842050111710443e11,2.2130873014861176e11,7.072552921342816e10,1.0103647030489738e10,0,0,0,0,0,0,0,0,0,0,0},{0,0,-914.2,-127000.05298477177,-5.410357632421346e6,-1.084187847239819e8,-1.2320553672292948e9,-8.793144231643013e9,-4.189268519105384e10,-1.3824796054280862e11,-3.2253689193271765e11,-5.3569991047103217e11,-6.2930263262471e11,-5.107830750947536e11,-2.7241958305957956e11,-8.588099975916278e10,-1.2124376436587687e10,0,0,0,0,0,0,0,0,0,0},{0,0,7,3223.9763957964633,289078.1147930231,1.0056934701494202e7,1.7885319900918955e8,1.8774039305941489e9,1.2659112178824976e10,5.778636226399293e10,1.8443310408086594e11,4.188943611564575e11,6.80570218646967e11,7.848818851678533e11,6.271698565745526e11,3.30024403976469e11,1.028326742214289e11,1.4369631332252073e10,0,0,0,0,0,0,0,0,0},{0,0,0,-24.504469987228607,-7282.983796526446,-533794.5211804865,-1.6496528391199792e7,-2.7121016480072916e8,-2.69139348470133e9,-1.7396960801336163e10,-7.683642125146432e10,-2.388197884303981e11,-5.3073846409068134e11,-8.467207746541761e11,-9.615174725410426e11,-7.581688663491782e11,-3.943770868588842e11,-1.2164636179544427e11,-1.6847153975743809e10,0,0,0,0,0,0,0,0},{0,0,0,0.000011535450912536563,55.140655048819674,13385.468430331828,871673.8196526119,2.4913415664470177e7,3.873820740702931e8,3.686591045638684e9,2.3063912016993534e10,9.92281083489501e10,3.018436477318775e11,6.588209340059844e11,1.0351031513181844e12,1.160082623098246e12,9.043453403023804e11,4.6572230892101843e11,1.4238562392402832e11,1.9564436875057327e10,0,0,0,0,0,0,0}},{{0.9333333333333333,-181.06666666666666,-136335.83965314535,-7.327933577454448e6,-1.5542659822206742e8,-1.7593665355350988e9,-1.2169475752962036e10,-5.541107203717101e10,-1.734553628583209e11,-3.823252362645737e11,-5.987312993848872e11,-6.626663891105165e11,-5.067610463730009e11,-2.5477706093710904e11,-7.577735272867303e10,-1.0103647030489738e10,0,0,0,0,0,0,0,0,0,0,0},{0,1.4,3930.705969543536,438914.5575660432,1.6021909399733834e7,2.821148285309416e8,2.8568306121554456e9,1.834034705520107e10,7.915174003764148e10,2.3795742789826575e11,5.0818633097929156e11,7.758814204731168e11,8.410097103451432e11,6.320268394606305e11,3.130867623573008e11,9.194318797745662e10,1.2124376436587687e10,0,0,0,0,0,0,0,0,0,0},{0,0,-35.952791592926204,-12671.688278689997,-955744.668589088,-2.8937820175670214e7,-4.558361881243446e8,-4.2852364908962717e9,-2.6082358434683636e10,-1.0813493285251514e11,-3.150596210807534e11,-6.56127990059077e11,-9.812778081103854e11,-1.045448978125867e12,-7.742480850836678e11,-3.7874643521238617e11,-1.1001748988755493e11,-1.4369631332252073e10,0,0,0,0,0,0,0,0,0},{0,0,0.008939974457215836,117.10041553054182,27577.685460833156,1.7212957303475374e6,4.6594371675881036e7,6.812809157261431e8,6.07243220002907e9,3.551011349027069e10,1.4269874524439893e11,4.054913031711275e11,8.273111760165848e11,1.2162825061370105e12,1.2771474457642676e12,9.341281376272004e11,4.520414263861544e11,1.3006993878331618e11,1.6847153975743809e10,0,0,0,0,0,0,0,0},{0,0,-0.000023070901825073126,-0.031330284678449304,-255.83925591764077,-49634.48688799174,-2.765829691982738e6,-6.945891513716699e7,-9.627476140344408e8,-8.244250951607839e9,-4.673088800021546e10,-1.8316187642444714e11,-5.0994384612902716e11,-1.022841420534557e12,-1.4821974712563923e12,-1.5372206353757112e12,-1.112343356234171e12,-5.3324678896895935e11,-1.5216784236155698e11,-1.9564436875057327e10,0,0,0,0,0,0,0}}},\n  {{{0,-0.5,50175,3.8110518751832955e6,1.076462696229007e8,1.5907243629794188e9,1.4251013900556408e10,8.39147841034878e10,3.408264309321651e11,9.827003704330287e11,2.041566817044961e12,3.065320744754241e12,3.2952209252682485e12,2.4722631961132183e12,1.2290545345785562e12,3.6373129309763055e11,4.8497505746350746e10,0,0,0,0,0,0,0,0,0,0},{0,0,-1124.5,-179081.18687280125,-8.746490101655236e6,-2.0128246173478386e8,-2.6356156253879805e9,-2.1787106957386932e10,-1.2111135286756642e11,-4.7103679933522406e11,-1.312930805324278e12,-2.654465838545084e12,-3.897567876302638e12,-4.1124050502739814e12,-3.036874638157322e12,-1.489333703666579e12,-4.355794497588909e11,-5.747852532900829e10,0,0,0,0,0,0,0,0,0},{0,0,7.5,3962.019044757294,407072.920718967,1.6232431369128535e7,3.3147987895103717e8,4.0090734366763234e9,3.13099625273733e10,1.6676035684980087e11,6.272725393185522e11,1.702120269314377e12,3.366309892830835e12,4.852504880352651e12,5.040497006172991e12,3.6725169792886714e12,1.7801426017692478e12,5.153247098462812e11,6.7388615902975235e10,0,0,0,0,0,0,0,0},{0,0,0,-26.25417130144605,-8945.976556539295,-751050.1272782461,-2.6597813264939196e7,-5.0203783424558866e8,-5.739732894683955e9,-4.2968855344066925e10,-2.214204398894586e11,-8.110701607930319e11,-2.1534160242155322e12,-4.1819214920925874e12,-5.935635976892781e12,-6.084107901642862e12,-4.381942913426506e12,-2.1025895514418992e12,-6.03236803647601e11,-7.825774750022931e10,0,0,0,0,0,0,0},{0,0,0,9.363190675760198e-6,59.07710774785302,16437.045563842872,1.2257532331766458e6,4.013768184797409e7,7.164271778242302e8,7.854094148649289e9,5.6903267984881676e10,2.856175070922272e11,1.0238847552212717e12,2.6698130647352876e12,5.105908405067799e12,7.152238271875518e12,7.247671528966757e12,5.167805995087918e12,2.457745328571359e12,6.99576833714171e11,9.011498196996101e10,0,0,0,0,0,0}},{{1,-223,-196278.75036659127,-1.2163480027372148e7,-2.9773689168561506e8,-3.901236201224221e9,-3.1389389329419144e10,-1.6743088681163474e11,-6.199947751483894e11,-1.6382802519391409e12,-3.132920728260574e12,-4.3465612386153613e12,-4.3323760798965054e12,-3.0231374871104106e12,-1.4018268987999307e12,-3.8798004597080597e11,-4.8497505746350746e10,0,0,0,0,0,0,0,0,0,0},{0,1.5,4912.873745602497,631897.2803378738,2.656490166119503e7,5.395585963900452e8,6.322823026301706e9,4.720796893278593e10,2.3863565691698135e11,8.485721474191187e11,2.172352790028894e12,4.049809894234472e12,5.50235200650019e12,5.38931258216232e12,3.7052589549104136e12,1.6963462050468354e12,4.643187124233951e11,5.747852532900829e10,0,0,0,0,0,0,0,0,0},{0,0,-39.038089514588414,-15851.374766259272,-1.3756719102298168e6,-4.7943245278919205e7,-8.708307769571735e8,-9.47113944257027e9,-6.703028125916526e10,-3.254547834230943e11,-1.1214448357099817e12,-2.7992820802386875e12,-5.111448386239666e12,-6.825434396193574e12,-6.587638039199097e12,-4.472282579818021e12,-2.0251695912105806e12,-5.4901901779776886e11,-6.7388615902975235e10,0,0,0,0,0,0,0,0},{0,0,0.008342602892102336,127.27291285612064,34507.8303268765,2.4771144537147037e6,7.715353799846987e7,1.3004413372683635e9,1.3407053636109262e10,9.11467268081413e10,4.288886516771684e11,1.4411578341601123e12,3.5239100414919814e12,6.3247528225204e12,8.32319236966487e12,7.933155759027074e12,5.327467452291677e12,2.3895346256094067e12,6.423656773977156e11,7.825774750022931e10,0,0,0,0,0,0,0},{0,0,-0.000018726381351520396,-0.029231881289723337,-278.179190366686,-62116.434369362025,-3.979701958366857e6,-1.1496637205380797e8,-1.8365386839217353e9,-1.8187049742974773e10,-1.198292372617549e11,-5.49880612073813e11,-1.810124500260231e12,-4.3508491817515527e12,-7.696337232599344e12,-1.000270962164139e13,-9.431448443943848e12,-6.273956282611874e12,-2.7906371863090767e12,-7.446343246991516e11,-9.011498196996101e10,0,0,0,0,0,0}}},\n  {{{0,-0.5333333333333333,69358.93333333333,5.9871611113645565e6,1.924439018268767e8,3.2448156139755497e9,3.330669577632881e10,2.2601945627174777e11,1.0662940872799772e12,3.608672101565586e12,8.923743251707213e12,1.6254636226822168e13,2.1772292854629945e13,2.117283786317375e13,1.453488209257297e13,6.674693753831088e12,1.8393128105282654e12,2.2991410131603317e11,0,0,0,0,0,0,0,0,0},{0,0,-1364.8,-247010.3969398599,-1.3710914253764993e7,-3.5907586422209996e8,-5.365095399350815e9,-5.081736342983615e10,-3.255698706535722e11,-1.470867065526843e12,-4.812414875990048e12,-1.1581712843876643e13,-2.0631072691303906e13,-2.7124046546863812e13,-2.5963317717947027e13,-1.758285432472868e13,-7.979604930158186e12,-2.1762558900431414e12,-2.6955446361190094e11,0,0,0,0,0,0,0,0},{0,0,8,4805.078378923909,560851.6328710681,2.5412128545875855e7,5.904978119526415e8,8.148894279740852e9,7.291974445465744e10,4.476097728118765e11,1.955793734114073e12,6.229626820644424e12,1.4665713663876832e13,2.56478138439078e13,3.319636806334255e13,3.135144116117232e13,2.098522418302309e13,9.426637994127908e12,2.547724446396354e12,3.1303099000091724e11,0,0,0,0,0,0,0},{0,0,0,-28.003910068426197,-10845.277747543556,-1.0340539156313771e6,-4.160211107138518e7,-8.934162310823445e8,-1.1653781015963501e10,-9.995712648139485e10,-5.936175304669795e11,-2.525790561524574e12,-7.87163260522547e12,-1.819637446847215e13,-3.1333388101515715e13,-4.001900002443862e13,-3.736019485586116e13,-2.4754756721440023e13,-1.1020607767604893e13,-2.954822829857143e12,-3.6045992787984406e11,0,0,0,0,0,0},{0,0,0,7.704568327482677e-6,63.01369198098898,19921.879208008424,1.6868344786841804e6,6.273964748932823e7,1.273959927266718e9,1.593305010069684e10,1.3225038205179854e11,7.649853076246484e11,3.185325437278451e12,9.749212126813445e12,2.219341094639236e13,3.771515415332075e13,4.76205488262829e13,4.401174323403532e13,2.890378760514151e13,1.2766176186368576e13,3.3986221771528154e12,4.1195420329125037e11,0,0,0,0,0}},{{1.0666666666666667,-270.93333333333334,-275873.1560624472,-1.9526615598641343e7,-5.463067917939428e8,-8.201340252473525e9,-7.590059501108307e10,-4.682825773172421e11,-2.0210768151063833e12,-6.288523857290183e12,-1.4356521317251836e13,-2.4229695665058094e13,-3.016739913942077e13,-2.7348748241932996e13,-1.7548912264512844e13,-7.551241265098464e12,-1.9542698611862817e12,-2.2991410131603317e11,0,0,0,0,0,0,0,0,0},{0,1.6,6051.193879719814,888285.5021747415,4.261058156357549e7,9.887753453648256e8,1.327213854827666e10,1.1396014858065186e11,6.662406988329823e11,2.7610060006323486e12,8.322298591918423e12,1.852086668895058e13,3.0609487847613902e13,3.744833590963831e13,3.344798330425991e13,2.11898795413883e13,9.017191413252525e12,2.311033121849092e12,2.6955446361190094e11,0,0,0,0,0,0,0,0},{0,0,-42.156757847819044,-19540.15290525303,-1.9336304731780519e6,-7.685712819702701e7,-1.594439517783882e9,-1.9858807743317722e10,-1.6160641862808392e11,-9.073615412262385e11,-3.643384657047884e12,-1.0707067507058748e13,-2.333725913033687e13,-3.790443110434288e13,-4.569379747197401e13,-4.029844176053195e13,-2.5250191501700312e13,-1.0641806906700912e13,-2.7042399413968125e12,-3.1303099000091724e11,0,0,0,0,0,0,0},{0,0,0.007820136852394917,137.56233770685833,42550.691954386595,3.4813989145593178e6,1.2363151215452614e8,2.3794672470258155e9,2.8087829807876396e10,2.19531539097911e11,1.1943955071569575e12,4.67634681344435e12,1.34610069080083e13,2.883661986591e13,4.615458622396285e13,5.4943021866992516e13,4.792868725397259e13,2.9744824442882992e13,1.2430432946055994e13,3.135052793797065e12,3.6045992787984406e11,0,0,0,0,0,0},{0,0,-0.000015409136654965353,-0.027397444972528398,-300.78237626127407,-76605.63532406824,-5.592621537223804e6,-1.841644469419035e8,-3.3586851599070764e9,-3.807661046434377e10,-2.883838211797314e11,-1.5299369967994255e12,-5.867608218199371e12,-1.6601437441364713e13,-3.5048526877146164e13,-5.5398397289151914e13,-6.523376765866994e13,-5.636627223479088e13,-3.46882547466682e13,-1.4388245861827873e13,-3.604599278798441e12,-4.1195420329125037e11,0,0,0,0,0}}},\n  {{{0,-0.5666666666666667,94002.06666666667,9.151311935305258e6,3.320851910459127e8,6.33521032186008e9,7.382275924950447e10,5.713935821593523e11,3.094041226242078e12,1.2118112094805514e13,3.5055579190329652e13,7.577080308439667e13,1.2277365554920727e14,1.4836959871768953e14,1.316939805223555e14,8.331707351347927e13,3.554854783594292e13,9.164851762804633e12,1.0782178544476038e12,0,0,0,0,0,0,0,0},{0,0,-1637.1,-334159.9205226142,-2.0918744937359024e7,-6.185177808929194e8,-1.0456566421124214e10,-1.1244319702124878e11,-8.217073662582078e11,-4.261136422965968e12,-1.613507388311211e13,-4.54274671317859e13,-9.60274321618125e13,-1.5272694341908484e14,-1.8167523305110694e14,-1.5908213959048006e14,-9.946452714264984e13,-4.200158523126891e13,-1.0730006712809219e13,-1.252123960003669e12,0,0,0,0,0,0,0},{0,0,8.5,5760.15335336423,758011.4560133434,3.872804138514868e7,1.0159241892397866e9,1.586238004713476e10,1.611446504457956e11,1.1282876946995315e12,5.658785050777512e12,2.0860295618072766e13,5.745164785582471e13,1.1922862068135155e14,1.8668579152657497e14,2.1910636544081044e14,1.8963108518772156e14,1.1735697816416962e14,4.9110491347309305e13,1.2445353299430406e13,1.4418397115193762e12,0,0,0,0,0,0},{0,0,0,-29.753679653679654,-12996.633706471774,-1.396745220816542e6,-6.3353746792567894e7,-1.5357532178764133e9,-2.2663668846309917e10,-2.2067759802151013e11,-1.4948161492516946e12,-7.300452192754711e12,-2.633115093534921e13,-7.12076759521055e13,-1.4550478401170806e14,-2.2481450876051734e14,-2.608206421034274e14,-2.2345389682757944e14,-1.3705312915212823e14,-5.689609265768836e13,-1.4315408564370951e13,-1.6478168131650015e12,0,0,0,0,0},{0,0,0,6.416135448393513e-6,66.95038440199731,23868.836113681442,2.2775814000040796e6,9.549105192520149e7,2.1884705815436573e9,3.0963237347720776e10,2.91743710502673e11,1.9247605684553428e12,9.198896562512525e12,3.258322450023937e13,8.677160313048934e13,1.749810194392509e14,2.6727116291968612e14,3.0697010372751906e14,2.6065987367419022e14,1.5860983054210656e14,6.537859338635338e13,1.6344561363015016e13,1.8704947608900017e12,0,0,0,0}},{{1.1333333333333333,-325.26666666666665,-379672.3372771824,-3.044289317860951e7,-9.653190181565726e8,-1.645654568518213e10,-1.7350035488585364e11,-1.224974862145457e12,-6.087121650396307e12,-2.198279146517062e13,-5.886931627391679e13,-1.1819629684730314e14,-1.7844037358940066e14,-2.014677026353088e14,-1.674879308472654e14,-9.947402064977933e13,-3.992880786963631e13,-9.703960690028434e12,-1.0782178544476038e12,0,0,0,0,0,0,0,0},{0,1.7,7358.141045228405,1.2228343356326213e6,6.639078335866816e7,1.7454206071277142e9,2.6599212141387142e10,2.601475592017887e11,1.740271415261319e12,8.302886018750533e12,2.904578302199949e13,7.582019351190167e13,1.490656602100751e14,2.211250836544858e14,2.4596589754150544e14,2.018771799198576e14,1.185708301208829e14,4.713181534517283e13,1.1356068692811053e13,1.252123960003669e12,0,0,0,0,0,0,0},{0,0,-45.30670672845925,-23779.18039507422,-2.661791803943833e6,-1.1969660677337208e8,-2.812574307228695e9,-3.9764345032168434e10,-3.685338752285082e11,-2.367394455421027e12,-1.0942906507013615e13,-3.73202401876562e13,-9.540709750646728e13,-1.843299072761322e14,-2.694183018006003e14,-2.9589668840921594e14,-2.401899624280804e14,-1.3971389256713602e14,-5.5062823051114625e13,-1.3166273155190094e13,-1.4418397115193762e12,0,0,0,0,0,0},{0,0,0.00735930735930736,147.96135233748615,51796.68308694178,4.792105916700205e6,1.9247997245947763e8,4.195150944550882e9,5.6203346619271835e10,5.002227932450558e11,3.113437647355672e12,1.403128377992629e13,4.686853664732211e13,1.1775423667475845e14,2.241800875868455e14,3.235464771315656e14,3.5146497769033325e14,2.825652031993201e14,1.6297151920138706e14,6.37448312874054e13,1.5139316970953451e13,1.6478168131650015e12,0,0,0,0,0},{0,0,-0.000012832270896787026,-0.025780032231645135,-323.6322744648659,-93265.65360101484,-7.69771425123931e6,-2.866519541825688e8,-5.919173280043663e9,-7.614963399570706e10,-6.566791772809753e11,-3.985094039738327e12,-1.7590885222630846e13,-5.775012578906397e13,-1.429804558962491e14,-2.6879959044347166e14,-3.8372757967297875e14,-4.1286846760141056e14,-3.291372259583687e14,-1.8840988086355138e14,-7.320015630019402e13,-1.7279808743460016e13,-1.8704947608900017e12,0,0,0,0}}},\n  {{{0,-0.6,125193.6,1.3652102441336416e7,5.554014600143847e8,1.1899676153316288e10,1.56165088407177e11,1.366540496131952e12,8.40861783023701e12,3.767209777990931e13,1.2572771066140053e14,3.1699683846911475e14,6.078847468910346e14,8.864741332485448e14,9.755540111565658e14,7.962880116350478e14,4.6711589143195694e14,1.8610613034936884e14,4.507646256013208e13,5.008495840014676e12,0,0,0,0,0,0,0},{0,0,-1943.4,-444350.0575188254,-3.1158621445006426e7,-1.0328718631043336e9,-1.9611734199856762e10,-2.3751855080120044e11,-1.9624234636460256e12,-1.1564556559203219e13,-5.009287309334422e13,-1.6271440389198622e14,-4.0123038305561994e14,-7.55244323016712e14,-1.0841303144905668e15,-1.1770068866194732e15,-9.494737901060278e14,-5.512548272291732e14,-2.1763242435124375e14,-5.228566111772896e13,-5.767358846077505e12,0,0,0,0,0,0},{0,0,9,6834.2430464192685,1.0071578139815058e6,5.7630935107687674e7,1.6947655536972034e9,2.971887868603432e10,3.400242318221734e11,2.691676008673227e12,1.5341040964447729e13,6.469277318651995e13,2.0556218578916944e14,4.9764013132721725e14,9.221943510366396e14,1.306119324762518e15,1.4015595146061295e15,1.1191029056445291e15,6.438857748567351e14,2.5216204623961334e14,6.0145313680522555e13,6.591267252660006e12,0,0,0,0,0},{0,0,0,-31.503474903474903,-15415.791203007979,-1.8549118704564322e6,-9.421570811376691e7,-2.5600544123712883e9,-4.242743900283097e10,-4.6525101895250854e11,-3.5629896100471016e12,-1.977415609780622e13,-8.158625551552306e13,-2.545518754366963e14,-6.067625924438578e14,-1.1095340952535998e15,-1.553365741029738e15,-1.650033451843795e15,-1.3057251452334125e15,-7.452766846539458e14,-2.8978473074627556e14,-6.867387907839006e13,-7.481979043560007e12,0,0,0,0},{0,0,0,5.4000054000054e-6,70.88716688716688,28306.784007021753,3.0236617450400395e6,1.4194238784983155e8,3.6460856395006375e9,5.792852759286986e10,6.146661187977393e11,4.584553043233062e12,2.4898101684512574e13,1.008823224117668e14,3.099519834507942e14,7.291125023795298e14,1.3180326124569462e15,1.826761435393357e15,1.9232229378190795e15,1.5098730462049652e15,8.556875473106161e14,3.305876811925353e14,7.788932029962469e13,8.4412071260677e12,0,0,0}},{{1.2,-386.4,-512904.0826728312,-4.6250508101108156e7,-1.6498943779538748e9,-3.1693433546401592e10,-3.774969633261728e11,-3.0222573837808267e12,-1.7114252146731506e13,-7.088860314805319e13,-2.195652433185797e14,-5.154436720771549e14,-9.229759888796809e14,-1.260086991646628e15,-1.3012905395960398e15,-9.989055197138905e14,-5.52186666361618e14,-2.0770526865943212e14,-4.758071048013942e13,-5.008495840014676e12,0,0,0,0,0,0,0},{0,1.8,8846.315037650835,1.652499890670798e6,1.0081815236953616e8,2.980852295984091e9,5.117658296341241e10,5.653923499515992e11,4.2884376326036885e12,2.3314343858552824e13,9.354089348013502e13,2.8240048108547756e14,6.491502225984054e14,1.142121460503862e15,1.5361570189811522e15,1.5661512339351905e15,1.188884318540835e15,6.508081678370406e14,2.4269387512646872e14,5.516934054076771e13,5.767358846077505e12,0,0,0,0,0,0},{0,0,-48.486092838536685,-28610.053294245357,-3.5971398670573817e6,-1.8170450901651797e8,-4.800628986511418e9,-7.645048609922267e10,-8.002747183060198e11,-5.828318542596038e12,-3.0696178946402035e13,-1.2005854798795881e14,-3.549510824718861e14,-8.017723301469554e14,-1.3898631564766085e15,-1.8456808435841622e15,-1.8609896441157935e15,-1.3990467910248515e15,-7.593074829663808e14,-2.8099884047000084e14,-6.3440947306852555e13,-6.591267252660006e12,0,0,0,0,0},{0,0,0.0069498069498069494,158.46348709704054,62337.20331735644,6.475875533245151e6,2.921192897693864e8,7.157442458806223e9,1.0799555655084192e11,1.0855125833750288e12,7.659196496678287e12,3.932659690536154e13,1.5063960727715928e14,4.376723566398628e14,9.741274125347245e14,1.6673482941700252e15,2.189911586818609e15,2.1868594523613995e15,1.630061509290193e15,8.779941332352192e14,3.227187992148031e14,7.241486860017006e13,7.481979043560007e12,0,0,0,0},{0,0,-0.0000108000108000108,-0.024343224343224345,-346.71430558980086,-112261.86290506809,-1.0402072220276337e7,-4.349568507848248e8,-1.009552791463289e10,-1.4625792737830328e11,-1.4242626335574956e12,-9.797394622514166e12,-4.9269031116791766e13,-1.8547288277928522e14,-5.3100005187471706e14,-1.167000294197266e15,-1.9756813909489115e15,-2.5700586158413735e15,-2.544779679266689e15,-1.882576246583223e15,-1.007168503518773e15,-3.6794961500620994e14,-8.210992386265853e13,-8.4412071260677e12,0,0,0}}},\n  {{{0,-0.6333333333333333,164158.73333333334,1.9930293177442875e7,9.033522147628825e8,2.159562943718983e10,3.1695435436427655e11,3.1117616133093535e12,2.1572888288816043e13,1.09484309701826e14,4.1677133450584825e14,1.2090804894217778e15,2.6979173704553415e15,4.645555959846248e15,6.155935299753043e15,6.217286496234509e15,4.696578395452082e15,2.568066717852626e15,9.596044046707916e14,2.191596361509452e14,2.306943538431002e13,0,0,0,0,0,0},{0,0,-2285.7,-581877.1667007137,-4.542712920857985e7,-1.6777496857464986e9,-3.554596419337812e10,-4.8146941932528937e11,-4.463236488322537e12,-2.963468073013338e13,-1.4541483616136725e14,-5.387724333982664e14,-1.528682077106105e15,-3.3483222279232085e15,-5.675362800076585e15,-7.419408932698538e15,-7.405721111493924e15,-5.536919691805194e15,-3.000077941416619e15,-1.1119673832339076e15,-2.5211597241424522e14,-2.6365069010640023e13,0,0,0,0,0},{0,0,9.5,8034.346639088207,1.3179673367617e6,8.39534763120524e7,2.750469750609688e9,5.381559497441904e10,6.886135692098751e11,6.116068398456957e12,3.927525531830635e13,1.87621875146192e14,6.800177457846064e14,1.8942591437198682e15,4.0847532803221355e15,6.831268665098625e15,8.82695090814349e15,8.720982640500069e15,6.461561639299811e15,3.4729854777955795e15,1.277987710634628e15,2.8787805081888025e14,2.9927916174240027e13,0,0,0,0},{0,0,0,-33.253291753291755,-18118.497367092852,-2.4263056561244098e6,-1.371723164151261e8,-4.1521251582654886e9,-7.677555171735455e10,-9.415385502549863e11,-8.089827546033466e12,-5.058607217578998e13,-2.364337668337472e14,-8.414253645683544e14,-2.307828334679184e15,-4.910703691264801e15,-8.118041289783834e15,-1.0383658748433438e16,-1.0167288912591014e16,-7.473149461470064e15,-3.9880042421706445e15,-1.4580506661137562e15,-3.2652123928561875e14,-3.37648285042708e13,0,0,0},{0,0,0,4.587809465858246e-6,74.82402515085442,33264.59142595566,3.9539347999028377e6,2.0657696267892247e8,5.910713251530081e9,1.0476957462492262e11,1.2431924580143315e12,1.0402955953492928e13,6.365382991017692e13,2.9216326172935256e14,1.0238733031895974e15,2.7713247980029775e15,5.829535183879195e15,9.540280533475378e15,1.209446128636118e16,1.1748726073620186e16,8.574250203286003e15,4.5463150567317e15,1.6525312353663298e15,3.6811898393680606e14,3.788249051698675e13,0,0}},{{1.2666666666666666,-454.73333333333335,-681521.0882190833,-6.866812258629e7,-2.7377423472293215e9,-5.885373059155742e10,-7.861999036201415e11,-7.081109305222594e12,-4.529513317348146e13,-2.1305179026514706e14,-7.544247442837826e14,-2.042292971648917e15,-4.264112494563583e15,-6.887192122673396e15,-8.579721224269423e15,-8.162941954518764e15,-5.819981799016939e15,-3.0089392479769985e15,-1.064858703611706e15,-2.3069435384310022e14,-2.306943538431002e13,0,0,0,0,0,0},{0,1.9,10528.43340142741,2.1966056588859563e6,1.496340808486337e8,4.94310202090654e9,9.495646973148334e10,1.1764390850983005e12,1.0037702723170137e13,6.163907586873927e13,2.808207197238787e14,9.692149606108888e14,2.56903911487428e15,5.27020715794106e15,8.385800766327233e15,1.0313148662287174e16,9.703156383423962e15,6.850671684428315e15,3.5112616134256495e15,1.2330819190015352e15,2.6529850691956525e14,2.6365069010640023e13,0,0,0,0,0},{0,0,-51.693278176413344,-34074.78014180465,-4.781836743440356e6,-2.6961591339264446e8,-7.957160824871687e9,-1.417663970015018e11,-1.6639977875868992e12,-1.3631292963271166e13,-8.108623737250361e13,-3.6010324340970756e14,-1.2170534965459422e15,-3.1699041795772295e15,-6.406817661291515e15,-1.006485817543921e16,-1.2241435673077942e16,-1.1405817498928164e16,-7.983798180042419e15,-4.0608079407256885e15,-1.4163152517613982e15,-3.0284200890600025e14,-2.9927916174240027e13,0,0,0,0},{0,0,0.006583506583506584,169.06299475396784,74264.58077013028,8.60869260141037e6,4.3336689596040046e8,1.1859561376200863e10,2.0016987814094357e11,2.255834083018215e12,1.7902005880230887e13,1.0381148852085344e14,4.5148680237416356e14,1.499482007722108e15,3.8480749449814715e15,7.679151287997549e15,1.1931083759742532e16,1.4371388164888972e16,1.3276270216445754e16,9.222548421515924e15,4.658972295533745e15,1.614980380412015e15,3.434036535377542e14,3.37648285042708e13,0,0,0},{0,0,-9.175618931716493e-6,-0.023058330375403547,-370.01552049520996,-133761.33977549043,-1.3827819745792182e7,-6.451738118427956e8,-1.6723378636699493e10,-2.709901673195793e11,-2.9584696444771226e12,-2.288771647820421e13,-1.2998082039545089e14,-5.555315701855969e14,-1.8179762075703875e15,-4.606611728235598e15,-9.092222501474912e15,-1.3990963605707124e16,-1.6709589472590812e16,-1.5319694204447672e16,-1.0570016078374418e16,-5.307109848412682e15,-1.8294877603627978e15,-3.8706022919529944e14,-3.788249051698675e13,0,0}}},\n  {{{0,-0.6666666666666666,212266.66666666666,2.853599258035219e7,1.4330114863456995e9,3.800258146404937e10,6.199274770948887e11,6.782860407787912e12,5.259010470068523e13,2.998352445442762e14,1.289474302995765e15,4.256253873967506e15,1.0902678302444068e16,2.1797926698136784e16,3.403776778865586e16,4.131592071162812e16,3.85489253454479e16,2.709770607257666e16,1.3874444112447926e16,4.881180309397506e15,1.054602760425601e15,1.054602760425601e14,0,0,0,0,0},{0,0,-2666,-751541.6624389548,-6.496750631920008e7,-2.6584414136368732e9,-6.248207318933823e10,-9.406826677470544e11,-9.718503789838549e12,-7.216907849489138e13,-3.978378205026157e14,-1.6653179634893145e15,-5.376204754295016e15,-1.3518436707709178e16,-2.6605608406951144e16,-4.0986885639172104e16,-4.9169772065455784e16,-4.540651511838149e16,-3.162877001568124e16,-1.6063591016532404e16,-5.610415428520925e15,-1.204242341296801e15,-1.1971166469696011e14,0,0,0,0},{0,0,10,9367.463398770313,1.701250849010266e6,1.1998124014091475e8,4.354863213182676e9,9.45208118958657e10,1.344305113466995e12,1.3306614989260633e13,9.556877985629884e13,5.12895265809024e14,2.1002083731150322e15,6.656579614385937e15,1.6478625548324368e16,3.1999232770493936e16,4.8724507460816184e16,5.785749736908936e16,5.294839315410694e16,3.65864067552318e16,1.844784707627131e16,6.401367498840126e15,1.365940789490955e15,1.350593140170832e14,0,0,0},{0,0,0,-35.00312695434646,-21120.499631781146,-3.13075780749621e6,-1.9594466391609663e8,-6.57050375487538e9,-1.3476651743799847e11,-1.8369043800571548e12,-1.7589435314077154e13,-1.2300981206774544e14,-6.45896597792679e14,-2.5969519006174225e15,-8.1043872896729e15,-1.979717247928791e16,-3.800085757405471e16,-5.727840617892459e16,-6.7406697825393336e16,-6.119592653276693e16,-4.198322456673902e16,-2.1032687680075924e16,-7.255685440992997e15,-1.5400055927557658e15,-1.51529962067947e14,0,0},{0,0,0,3.930803703917714e-6,78.76094777966587,38771.1275913068,5.100639029001081e6,2.949841095687273e8,9.349478097378347e9,1.8381938202018082e11,2.42419630975418e12,2.2606778024802125e13,1.547013295439825e14,7.976887641336864e14,3.1582328358075825e15,9.726354425919532e15,2.3487483127377148e16,4.463176708335417e16,6.667543951844797e16,7.784411667165024e16,7.0169853688251656e16,4.78314795584771e16,2.3823442630743476e16,8.174976337050191e15,1.7267367770533495e15,1.6914972509910362e14,0}},{{1.3333333333333333,-530.6666666666666,-892251.827371045,-9.987215970701566e7,-4.424126639899304e9,-1.0578643572033942e11,-1.5747287228400437e12,-1.5845717567057562e13,-1.1362682509291195e14,-6.01771280857198e14,-2.4125671241445005e15,-7.446002113664234e15,-1.7881405459744606e16,-3.35951258854644e16,-4.9401354974251736e16,-5.657957303847233e16,-4.9900402465368104e16,-3.3212859965007824e16,-1.612718315044587e16,-5.388707887852326e15,-1.107332898446881e15,-1.054602760425601e14,0,0,0,0,0},{0,2,12417.32487790964,2.877010547668333e6,2.1757735630149266e8,7.983815399399566e9,1.7056499979904105e11,2.354551408251009e12,2.244295896408418e13,1.5448897973201038e14,7.924454954576368e14,3.09644241377513e15,9.357141993333924e15,2.2077909573687176e16,4.086271515883938e16,5.931957879239599e16,6.7183171140618856e16,5.867380519673225e16,3.8715022071894504e16,1.8654226260365056e16,6.190090662038646e15,1.2640981736452812e15,1.1971166469696011e14,0,0,0,0},{0,0,-54.92679754062404,-40215.75896837982,-6.263591942903851e6,-3.919620881744125e8,-1.2847160247073557e10,-2.5452055340858438e11,-3.3284003694315527e12,-3.045760371601915e13,-2.0308431931952872e14,-1.0153938929148701e15,-3.8851031201166525e15,-1.1535964059895936e16,-2.6816071946032308e16,-4.9000640659284456e16,-7.0346216741010216e16,-7.889814086638738e16,-6.831336974290147e16,-4.473081072704507e16,-2.1405078738350924e16,-7.0590142722074e15,-1.4334704464994968e15,-1.350593140170832e14,0,0,0},{0,0,0.006253908692933083,179.75473573239677,87672.02030488498,1.127655502867755e7,6.299257811345832e8,1.9142375540966686e10,3.592377142778432e11,4.510116251939719e12,3.997869384169198e13,2.598472460491622e14,1.2722572790529712e15,4.783417939546741e15,1.3993927307709768e16,3.2117401194767464e16,5.8041145938566104e16,8.251977130024504e16,9.176083681396456e16,7.884587952758733e16,5.127529010535035e16,2.4385985285874588e16,7.99727636948314e15,1.6157705737897392e15,1.51529962067947e14,0,0},{0,0,-7.861607407835427e-6,-0.021902438238229502,-393.52434085950074,-157932.76854200763,-1.8113190266049244e7,-9.376913624338514e8,-2.698711715217962e10,-4.86188476940676e11,-5.912673590075985e12,-5.109028982891983e13,-3.251901708214681e14,-1.5646005583864498e15,-5.796048104691107e15,-1.6742037879323554e16,-3.800261305735843e16,-6.801536029880263e16,-9.587749223709138e16,-1.0580649570083427e17,-9.029733700258528e16,-5.8363106600548536e16,-2.7603025440299108e16,-9.006629152120784e15,-1.8113116396029015e15,-1.6914972509910362e14,0}}}\n };\n\n/* Compute the singular field at the point x for the particle at xp */\nvoid effsource_PhiS(struct coordinate * x, double * PhiS)\n{\n\n  double A, alpha, rho2;\n\n  double r      = x->r;\n  double theta  = x->theta;\n  double phi    = x->phi;\n  double rp     = xp.r;\n  double thetap = xp.theta;\n  double phip   = xp.phi;\n\n  double dr     = r - rp;\n  double dtheta = theta - thetap;\n  double dphi   = phi - phip;\n\n  double dr2 = dr*dr;\n  double dr3 = dr2*dr;\n  double dr4 = dr2*dr2;\n  double dr6 = dr3*dr3;\n  double dr8 = dr4*dr4;\n\n  double dtheta2  = dtheta*dtheta;\n  double dtheta4  = dtheta2*dtheta2;\n  double dtheta8  = dtheta4*dtheta4;\n  \n  double sindphi  = sin(0.5*dphi);\n  double sindphi2 = sindphi*sindphi;\n  double sindphi4 = sindphi2*sindphi2;\n  double sindphi8 = sindphi4*sindphi4;\n\n  A = dr6*(A600 + A700*dr) + dr8*(A800 + A900*dr) + dr4*(A420 + A520*dr + dr2*(A620 + A720*dr))*dtheta2 + \n   (A080 + A180*dr)*dtheta8 + dtheta4*(dr2*(A240 + A340*dr) + dr4*(A440 + A540*dr) + \n      (A060 + A160*dr + dr2*(A260 + A360*dr))*dtheta2) + \n   (dr4*(A402 + A502*dr + dr2*(A602 + A702*dr)) + (dr2*(A222 + A322*dr) + dr4*(A422 + A522*dr))*dtheta2 + \n      dtheta4*(A042 + A142*dr + dr2*(A242 + A342*dr) + (A062 + A162*dr)*dtheta2))*sindphi2 + \n   (A008 + A108*dr)*sindphi8 + sindphi4*(dr2*(A204 + A304*dr) + dr4*(A404 + A504*dr) + \n      (A024 + A124*dr + dr2*(A224 + A324*dr))*dtheta2 + (A044 + A144*dr)*dtheta4 + \n      (A006 + A106*dr + dr2*(A206 + A306*dr) + (A026 + A126*dr)*dtheta2)*sindphi2);\n\n  alpha = alpha20*dr2 + alpha02*dtheta2;\n  rho2 = alpha + beta*sindphi2;\n\n  *PhiS = A/pow(rho2, 3.5);\n}\n\n/* Compute the singular field at the point x for the particle at xp */\nvoid effsource_PhiS_m(int m, struct coordinate * x, double * PhiS)\n{\n  double A[5], num, alpha, ellE, ellK;\n\n  double r      = x->r;\n  const double theta  = x->theta;\n  const double rp     = xp.r;\n  const double thetap = xp.theta;\n\n  const double dr     = r - rp;\n  const double dtheta = theta - thetap;\n\n  const double dr2 = dr*dr;\n  const double dr3 = dr2*dr;\n  const double dr4 = dr2*dr2;\n  const double dr6 = dr3*dr3;\n  const double dr8 = dr4*dr4;\n\n  const double dtheta2  = dtheta*dtheta;\n  const double dtheta4  = dtheta2*dtheta2;\n  const double dtheta8  = dtheta4*dtheta4;\n\n  A[0] = dr6*(A600 + A700*dr) + dr8*(A800 + A900*dr) + dr4*(A420 + A520*dr + dr2*(A620 + A720*dr))*dtheta2 + (A080 + A180*dr)*dtheta8 + dtheta4*(dr2*(A240 + A340*dr) + dr4*(A440 + A540*dr) + (A060 + A160*dr + dr2*(A260 + A360*dr))*dtheta2);\n  A[1] = dr4*(A402 + A502*dr + dr2*(A602 + A702*dr)) + (dr2*(A222 + A322*dr) + dr4*(A422 + A522*dr))*dtheta2 + dtheta4*(A042 + A142*dr + dr2*(A242 + A342*dr) + (A062 + A162*dr)*dtheta2);\n  A[2] = dr2*(A204 + A304*dr) + dr4*(A404 + A504*dr) + (A024 + A124*dr + dr2*(A224 + A324*dr))*dtheta2 + (A044 + A144*dr)*dtheta4;\n  A[3] = A006 + A106*dr + dr2*(A206 + A306*dr) + (A026 + A126*dr)*dtheta2;\n  A[4] = A008 + A108*dr;\n\n  alpha = alpha20*dr2 + alpha02*dtheta2;\n\n  const double C1 = alpha / beta;\n\n  double C[27];\n  C[0]  = 1;\n  C[1]  = C1;\n  C[2]  = C[1]*C[1];\n  C[3]  = C[2]*C[1];\n  C[4]  = C[2]*C[2];\n  C[5]  = C[3]*C[2];\n  C[6]  = C[3]*C[3];\n  C[7]  = C[4]*C[3];\n  C[8]  = C[4]*C[4];\n  C[9]  = C[5]*C[4];\n  C[10] = C[5]*C[5];\n  C[11] = C[6]*C[5];\n  C[12] = C[6]*C[6];\n  C[13] = C[7]*C[6];\n  C[14] = C[7]*C[7];\n  C[15] = C[8]*C[7];\n  C[16] = C[8]*C[8];\n  C[17] = C[9]*C[8];\n  C[18] = C[9]*C[9];\n  C[19] = C[10]*C[9];\n  C[20] = C[10]*C[10];\n  C[21] = C[11]*C[10];\n  C[22] = C[11]*C[11];\n  C[23] = C[12]*C[11];\n  C[24] = C[12]*C[12];\n  C[25] = C[13]*C[12];\n  C[26] = C[13]*C[13];\n\n  ellE = gsl_sf_ellint_Ecomp(sqrt(1.0/(1.0+C1)), GSL_PREC_DOUBLE);\n  ellK = gsl_sf_ellint_Kcomp(sqrt(1.0/(1.0+C1)), GSL_PREC_DOUBLE);\n  const double ellip[2] = {ellK, ellE};\n\n  if(m>20)\n  {\n    printf(\"Support for computing mode %d has not yet been added.\\n\", m);\n    return;\n  }\n\n  num = 0;\n  for(int i=0; i<2; i++)\n    for(int j=0; j<5; j++)\n      for(int k=0; k<27; k++)\n        num += ReEI[m][i][j][k]*ellip[i]*A[j]*C[k];\n\n  double RePhiS = 4.0*num/(beta*C[3]*pow(alpha+beta, 2.5));\n\n  /* Store calculated quantities into the arrays provided by the caller,\n     including the phase factor exp(-i*m*phi_p) */\n  double cosmph = cos(m*xp.phi);\n  double sinmph = sin(m*xp.phi);\n\n  PhiS[0] = RePhiS*cosmph;\n  PhiS[1] = - RePhiS*sinmph;\n}\n\n/* Compute the singular field, its derivatives and its d'Alembertian */\nvoid effsource_calc(struct coordinate * x,\n  double *PhiS, double *dPhiS_dx, double *d2PhiS_dx2, double *src)\n{\n  double A, dA_dr, d2A_dr2, dA_dth, d2A_dth2, dA_dR, dA_dph,  d2A_dR2,  d2A_dph2, dA_dt, d2A_dt2, d2A_dphdt;\n  double s2, sqrts2, s2_15, s2_25, s2_35, s2_45, s2_55, ds2_dr, d2s2_dr2, ds2_dth, d2s2_dth2, ds2_dR, ds2_dph, d2s2_dR2, d2s2_dph2, ds2_dt, d2s2_dt2, d2s2_dphdt;\n\n  double dPhiS_dt, dPhiS_dr, dPhiS_dth, dPhiS_dph, d2PhiS_dt2, d2PhiS_dtr, d2PhiS_dtth;\n  double d2PhiS_dtph, d2PhiS_dr2, d2PhiS_drth, d2PhiS_drph, d2PhiS_dth2, d2PhiS_dthph, d2PhiS_dph2;\n\n  double r      = x->r;\n  double theta  = x->theta;\n  double phi    = x->phi;\n  double rp     = xp.r;\n  double thetap = xp.theta;\n  double phip   = xp.phi;\n\n  double dr     = r - rp;\n  double dtheta = theta - thetap;\n  double dphi   = phi - phip;\n\n  double dr2      = dr*dr;\n  double dr3      = dr2*dr;\n  double dr4      = dr2*dr2;\n  double dr5      = dr3*dr2;\n  double dr6      = dr3*dr3;\n  double dr7      = dr4*dr3;\n  double dr8      = dr4*dr4;\n\n  double dtheta2  = dtheta*dtheta;\n  double dtheta3  = dtheta2*dtheta;\n  double dtheta4  = dtheta2*dtheta2;\n  double dtheta5  = dtheta3*dtheta2;\n  double dtheta6  = dtheta3*dtheta3;\n  double dtheta7  = dtheta4*dtheta3;\n  double dtheta8  = dtheta4*dtheta4;\n\n  double R        = sin(0.5*dphi);\n  double R2       = R*R;\n  double R3       = R2*R;\n  double R4       = R2*R2;\n  double R5       = R3*R2;\n  double R6       = R3*R3;\n  double R7       = R4*R3;\n  double R8       = R4*R4;\n  double dR       = 0.5*cos(0.5*dphi);\n\n  double om       = M / (a*M + sqrt(M*pow(rp,3)));\n\n  /* A, dA/dx, d^2A/dx^2 */\n  A         = dr6*(A600 + A700*dr) + dr8*(A800 + A900*dr) + dr4*(A420 + A520*dr + dr2*(A620 + A720*dr))*dtheta2 + (A080 + A180*dr)*dtheta8 + dtheta4*(dr2*(A240 + A340*dr) + dr4*(A440 + A540*dr) + (A060 + A160*dr + dr2*(A260 + A360*dr))*dtheta2) + (dr4*(A402 + A502*dr + dr2*(A602 + A702*dr)) + (dr2*(A222 + A322*dr) + dr4*(A422 + A522*dr))*dtheta2 + dtheta4*(A042 + A142*dr + dr2*(A242 + A342*dr) + (A062 + A162*dr)*dtheta2))*R2 + (A008 + A108*dr)*R8 + R4*(dr2*(A204 + A304*dr) + dr4*(A404 + A504*dr) + (A024 + A124*dr + dr2*(A224 + A324*dr))*dtheta2 + (A044 + A144*dr)*dtheta4 + (A006 + A106*dr + dr2*(A206 + A306*dr) + (A026 + A126*dr)*dtheta2)*R2);;\n  dA_dr     = 6*(A600 + A700*dr)*dr5 + A700*dr6 + 8*(A800 + A900*dr)*dr7 + A900*dr8 + 4*(A420 + A520*dr + (A620 + A720*dr)*dr2)*dr3*dtheta2 + (A520 + 2*dr*(A620 + A720*dr) + A720*dr2)*dr4*dtheta2 + (2*dr*(A240 + A340*dr) + A340*dr2 + 4*(A440 + A540*dr)*dr3 + A540*dr4 + (A160 + 2*dr*(A260 + A360*dr) + A360*dr2)*dtheta2)*dtheta4 + A180*dtheta8 + (4*(A402 + A502*dr + (A602 + A702*dr)*dr2)*dr3 + (A502 + 2*dr*(A602 + A702*dr) + A702*dr2)*dr4 + (2*dr*(A222 + A322*dr) + A322*dr2 + 4*(A422 + A522*dr)*dr3 + A522*dr4)* dtheta2 + (A142 + 2*dr*(A242 + A342*dr) + A342*dr2 + A162*dtheta2)* dtheta4)*R2 + (2*dr*(A204 + A304*dr) + A304*dr2 + 4*(A404 + A504*dr)*dr3 + A504*dr4 + (A124 + 2*dr*(A224 + A324*dr) + A324*dr2)*dtheta2 + A144*dtheta4 + (A106 + 2*dr*(A206 + A306*dr) + A306*dr2 + A126*dtheta2)*R2)* R4 + A108*R8;\n  dA_dth    = 2*(A420 + A520*dr + (A620 + A720*dr)*dr2)*dr4*dtheta + 4*((A240 + A340*dr)*dr2 + (A440 + A540*dr)*dr4 + (A060 + A160*dr + (A260 + A360*dr)*dr2)*dtheta2)*dtheta3 + 2*(A060 + A160*dr + (A260 + A360*dr)*dr2)*dtheta5 + 8*(A080 + A180*dr)*dtheta7 + (2*((A222 + A322*dr)*dr2 + (A422 + A522*dr)*dr4)*dtheta + 4*(A042 + A142*dr + (A242 + A342*dr)*dr2 + (A062 + A162*dr)*dtheta2)* dtheta3 + 2*(A062 + A162*dr)*dtheta5)*R2 + (2*(A024 + A124*dr + (A224 + A324*dr)*dr2)*dtheta + 4*(A044 + A144*dr)*dtheta3 + 2*(A026 + A126*dr)*dtheta*R2)* R4;\n  dA_dR     = 2*((A402 + A502*dr + (A602 + A702*dr)*dr2)*dr4 + ((A222 + A322*dr)*dr2 + (A422 + A522*dr)*dr4)*dtheta2 + (A042 + A142*dr + (A242 + A342*dr)*dr2 + (A062 + A162*dr)*dtheta2)* dtheta4)*R + 4*((A204 + A304*dr)*dr2 + (A404 + A504*dr)*dr4 + (A024 + A124*dr + (A224 + A324*dr)*dr2)*dtheta2 + (A044 + A144*dr)*dtheta4 + (A006 + A106*dr + (A206 + A306*dr)*dr2 + (A026 + A126*dr)*dtheta2)* R2)*R3 + 2*(A006 + A106*dr + (A206 + A306*dr)*dr2 + (A026 + A126*dr)*dtheta2)*R5 + 8*(A008 + A108*dr)*R7;\n  dA_dph    = dA_dR*dR;\n  dA_dt     = -om*dA_dph;\n  d2A_dr2   = 30*(A600 + A700*dr)*dr4 + 12*A700*dr5 + 56*(A800 + A900*dr)*dr6 + 16*A900*dr7 + 12*dr2*(A420 + A520*dr + (A620 + A720*dr)*dr2)*dtheta2 + 8*(A520 + 2*dr*(A620 + A720*dr) + A720*dr2)*dr3*dtheta2 + (4*A720*dr + 2*(A620 + A720*dr))*dr4*dtheta2 + (4*A340*dr + 2*(A240 + A340*dr) + 12*(A440 + A540*dr)*dr2 + 8*A540*dr3 + (4*A360*dr + 2*(A260 + A360*dr))*dtheta2)*dtheta4 + (12*dr2*(A402 + A502*dr + (A602 + A702*dr)*dr2) + 8*(A502 + 2*dr*(A602 + A702*dr) + A702*dr2)*dr3 + (4*A702*dr + 2*(A602 + A702*dr))*dr4 + (4*A322*dr + 2*(A222 + A322*dr) + 12*(A422 + A522*dr)*dr2 + 8*A522*dr3)*dtheta2 + (4*A342*dr + 2*(A242 + A342*dr))*dtheta4)* R2 + (4*A304*dr + 2*(A204 + A304*dr) + 12*(A404 + A504*dr)*dr2 + 8*A504*dr3 + (4*A324*dr + 2*(A224 + A324*dr))*dtheta2 + (4*A306*dr + 2*(A206 + A306*dr))*R2)*R4;\n  d2A_dth2  = 2*(A420 + A520*dr + (A620 + A720*dr)*dr2)*dr4 + 12*dtheta2*((A240 + A340*dr)*dr2 + (A440 + A540*dr)*dr4 + (A060 + A160*dr + (A260 + A360*dr)*dr2)*dtheta2) + 18*(A060 + A160*dr + (A260 + A360*dr)*dr2)*dtheta4 + 56*(A080 + A180*dr)*dtheta6 + (2*((A222 + A322*dr)*dr2 + (A422 + A522*dr)*dr4) + 12*dtheta2*(A042 + A142*dr + (A242 + A342*dr)*dr2 +  (A062 + A162*dr)*dtheta2) + 18*(A062 + A162*dr)*dtheta4)*R2 + (2*(A024 + A124*dr + (A224 + A324*dr)*dr2) + 12*(A044 + A144*dr)*dtheta2 + 2*(A026 + A126*dr)*R2)*R4; d2A_dR2 = 2*((A402 + A502*dr + (A602 + A702*dr)*dr2)*dr4 + ((A222 + A322*dr)*dr2 + (A422 + A522*dr)*dr4)*dtheta2 + (A042 + A142*dr + (A242 + A342*dr)*dr2 + (A062 + A162*dr)*dtheta2)* dtheta4) + 12*R2*((A204 + A304*dr)*dr2 + (A404 + A504*dr)*dr4 + (A024 + A124*dr + (A224 + A324*dr)*dr2)*dtheta2 + (A044 + A144*dr)*dtheta4 + (A006 + A106*dr + (A206 + A306*dr)*dr2 + (A026 + A126*dr)*dtheta2)* R2) + 18*(A006 + A106*dr + (A206 + A306*dr)*dr2 + (A026 + A126*dr)*dtheta2)*R4 + 56*(A008 + A108*dr)*R6;\n  d2A_dph2  = - 0.25*R*dA_dR + dR*dR*d2A_dR2;\n  d2A_dt2   = om*om*d2A_dph2;\n  d2A_dphdt = -om*d2A_dph2;\n\n  /* s, ds/dr, d^2s/dr^2 */\n  s2         = alpha20*dr2 + alpha02*dtheta2 + beta*R2;\n  ds2_dr     = 2*alpha20*dr;\n  ds2_dth    = 2*alpha02*dtheta;\n  ds2_dR     = 2*beta*R;\n  ds2_dph    = ds2_dR*dR;\n  ds2_dt     = -om*ds2_dph;\n  d2s2_dr2   = 2*alpha20;\n  d2s2_dth2  = 2*alpha02;\n  d2s2_dR2   = 2*beta;\n  d2s2_dph2  = - 0.25*R*ds2_dR + dR*dR*d2s2_dR2;\n  d2s2_dt2   = om*om*d2s2_dph2;\n  d2s2_dphdt = -om*d2s2_dph2;\n  sqrts2     = sqrt(s2);\n  s2_15      = s2*sqrts2;\n  s2_25      = s2*s2_15;\n  s2_35      = s2*s2_25;\n  s2_45      = s2*s2_35;\n  s2_55      = s2*s2_45;\n\n  /* PhiS */\n  *PhiS = A/s2_35;\n\n  /* First derivatives of PhiS */\n  dPhiS_dt  = (-7*ds2_dt*A + 2*dA_dt*s2)/(2.*s2_45);\n  dPhiS_dr  = (-7*ds2_dr*A + 2*dA_dr*s2) /(2.*s2_45);\n  dPhiS_dth = (-7*ds2_dth*A + 2*dA_dth*s2)/(2.*s2_45);\n  dPhiS_dph = (-7*ds2_dph*A + 2*dA_dph*s2)/(2.*s2_45);\n  \n  /* Second derivatives of PhiS */\n  d2PhiS_dr2 =\n    (63*ds2_dr*ds2_dr*A - 14*s2*(2*dA_dr*ds2_dr + d2s2_dr2*A) + 4*d2A_dr2*s2*s2)/(4.*s2_55);\n  d2PhiS_dth2 =\n    (63*ds2_dth*ds2_dth*A - 14*s2*(2*dA_dth*ds2_dth + d2s2_dth2*A) + 4*d2A_dth2*s2*s2)/(4.*s2_55);\n  d2PhiS_dph2 =\n    (63*ds2_dph*ds2_dph*A - 14*s2*(2*dA_dph*ds2_dph + d2s2_dph2*A) + 4*d2A_dph2*s2*s2)/(4.*s2_55);\n  d2PhiS_dt2 =\n    (63*ds2_dt*ds2_dt*A - 14*s2*(2*dA_dt*ds2_dt + d2s2_dt2*A) + 4*d2A_dt2*s2*s2)/(4.*s2_55);\n  d2PhiS_dtph =\n    (63*ds2_dph*ds2_dt*A - 14*s2*(dA_dt*ds2_dph + dA_dph*ds2_dt + d2s2_dphdt*A) + 4*d2A_dphdt*s2*s2)/(4.*s2_55);\n  d2PhiS_dtr   = NAN;\n  d2PhiS_dtth  = NAN;\n  d2PhiS_drth  = NAN;\n  d2PhiS_drph  = NAN;\n  d2PhiS_dthph = NAN;\n  \n  \n  /* Box[PhiS] */\n  double sinth  = sin(theta);\n  double sinth2 = sinth*sinth;\n  double sin2th = sin(2*theta);\n  double cos2th = cos(2*theta);\n  double r2 = r*r;\n  double r3 = r2*r;\n  double r4 = r2*r2;\n  double a2 = a*a;\n  double a4 = a2*a2;\n\n  *src = -((2*a2*dPhiS_dr - a2*d2PhiS_dph2 - a4*d2PhiS_dr2 - a2*d2PhiS_dth2 - 4*dPhiS_dr*r - 2*a2*dPhiS_dr*r +\n         4*d2PhiS_dph2*r + 2*a*d2PhiS_dtph*r + 4*a2*d2PhiS_dr2*r + 2*d2PhiS_dth2*r + 6*dPhiS_dr*r2 - 2*d2PhiS_dph2*r2 - 4*d2PhiS_dr2*r2 -\n         2*a2*d2PhiS_dr2*r2 - d2PhiS_dth2*r2 - 2*dPhiS_dr*r3 + 4*d2PhiS_dr2*r3 - d2PhiS_dr2*r4 +\n         (a4*d2PhiS_dt2 + 4*a*d2PhiS_dtph*r + 2*d2PhiS_dt2*r4 + a2*d2PhiS_dt2*r*(2 + 3*r))*sinth2 +\n         cos2th*(a4*d2PhiS_dr2 - 2*a*d2PhiS_dtph*r + (-2 + r)*r*(d2PhiS_dth2 + 2*dPhiS_dr*(-1 + r) + d2PhiS_dr2*(-2 + r)*r) +\n            a2*(-d2PhiS_dph2 + d2PhiS_dth2 + 2*dPhiS_dr*(-1 + r) - 4*d2PhiS_dr2*r + 2*d2PhiS_dr2*r2) +\n            a2*d2PhiS_dt2*(a2 + (-2 + r)*r)*sinth2) - a2*dPhiS_dth*sin2th + 2*dPhiS_dth*r*sin2th -\n         dPhiS_dth*r2*sin2th))/((sinth2*(a2 + (-2 + r)*r)*(a2 + 2*r2 + a2*cos2th)));\n\n  dPhiS_dx[0] = dPhiS_dt;\n  dPhiS_dx[1] = dPhiS_dr;\n  dPhiS_dx[2] = dPhiS_dth;\n  dPhiS_dx[3] = dPhiS_dph;\n\n  d2PhiS_dx2[0] = d2PhiS_dt2;\n  d2PhiS_dx2[1] = d2PhiS_dtr;\n  d2PhiS_dx2[2] = d2PhiS_dtth;\n  d2PhiS_dx2[3] = d2PhiS_dtph;\n  d2PhiS_dx2[4] = d2PhiS_dr2;\n  d2PhiS_dx2[5] = d2PhiS_drth;\n  d2PhiS_dx2[6] = d2PhiS_drph;\n  d2PhiS_dx2[7] = d2PhiS_dth2;\n  d2PhiS_dx2[8] = d2PhiS_dthph;\n  d2PhiS_dx2[9] = d2PhiS_dph2;\n}\n\n/* Compute the 2D singular field, its derivatives and its d'Alembertian */\nvoid effsource_calc_m(int m, struct coordinate * x,\n  double *PhiS, double *dPhiS_dx, double *d2PhiS_dx2, double *src)\n{\n  double A[5], alpha, ellE, ellK;\n  double dA_dr[5], dalpha_dr, dC1_dr, dellE_dC, dellK_dC, d2ellE_dC2, d2ellK_dC2, dellE_dr, dellK_dr;\n  double d2A_dr2[5], d2alpha_dr2, d2C1_dr2, d2ellE_dr2, d2ellK_dr2;\n  double dA_dtheta[5], dalpha_dtheta, dC1_dtheta, dellE_dtheta, dellK_dtheta;\n  double d2A_dtheta2[5], d2alpha_dtheta2, d2C1_dtheta2, d2ellE_dtheta2, d2ellK_dtheta2;\n\n  double s, ds_dr, d2s_dr2, ds_dtheta, d2s_dtheta2;\n\n  double dPhiS_dt, dPhiS_dr, dPhiS_dth, dPhiS_dph, d2PhiS_dt2, d2PhiS_dtr, d2PhiS_dtth;\n  double d2PhiS_dtph, d2PhiS_dr2, d2PhiS_drth, d2PhiS_drph, d2PhiS_dth2, d2PhiS_dthph, d2PhiS_dph2;\n\n  const double r      = x->r;\n  const double theta  = x->theta;\n  const double rp     = xp.r;\n  const double thetap = xp.theta;\n  const double om       = M / (a*M + sqrt(M*pow(rp,3)));\n\n  const double dr     = r - rp;\n  const double dtheta = theta - thetap;\n\n  const double dr2 = dr*dr;\n  const double dr3 = dr2*dr;\n  const double dr4 = dr2*dr2;\n  const double dr5 = dr3*dr2;\n  const double dr6 = dr3*dr3;\n  const double dr7 = dr4*dr3;\n  const double dr8 = dr4*dr4;\n\n  const double dtheta2  = dtheta*dtheta;\n  const double dtheta3  = dtheta2*dtheta;\n  const double dtheta4  = dtheta2*dtheta2;\n  const double dtheta5  = dtheta3*dtheta2;\n  const double dtheta6  = dtheta3*dtheta3;\n  const double dtheta7  = dtheta4*dtheta3;\n  const double dtheta8  = dtheta4*dtheta4;\n\n  /* Coefficients of sin(dphi) in the numerator */\n  A[0] = dr6*(A600 + A700*dr) + dr8*(A800 + A900*dr) + dr4*(A420 + A520*dr + dr2*(A620 + A720*dr))*dtheta2 + (A080 + A180*dr)*dtheta8 + dtheta4*(dr2*(A240 + A340*dr) + dr4*(A440 + A540*dr) + (A060 + A160*dr + dr2*(A260 + A360*dr))*dtheta2);\n  A[1] = dr4*(A402 + A502*dr + dr2*(A602 + A702*dr)) + (dr2*(A222 + A322*dr) + dr4*(A422 + A522*dr))*dtheta2 + dtheta4*(A042 + A142*dr + dr2*(A242 + A342*dr) + (A062 + A162*dr)*dtheta2);\n  A[2] = dr2*(A204 + A304*dr) + dr4*(A404 + A504*dr) + (A024 + A124*dr + dr2*(A224 + A324*dr))*dtheta2 + (A044 + A144*dr)*dtheta4;\n  A[3] = A006 + A106*dr + dr2*(A206 + A306*dr) + (A026 + A126*dr)*dtheta2;\n  A[4] = A008 + A108*dr;\n\n  /* r derivatives of coefficients */\n  dA_dr[0] = 6*A600*dr5 + 7*A700*dr6 + 8*A800*dr7 + 9*A900*dr8 + 4*A420*dr3*dtheta2 + 5*A520*dr4*dtheta2 + 6*A620*dr5*dtheta2 + 7*A720*dr6*dtheta2 + 2*A240*dr*dtheta4 + 3*A340*dr2*dtheta4 + 4*A440*dr3*dtheta4 + 5*A540*dr4*dtheta4 + A160*dtheta6 + 2*A260*dr*dtheta6 + 3*A360*dr2*dtheta6 + A180*dtheta8;\n  dA_dr[1] = 4*A402*dr3 + 5*A502*dr4 + 6*A602*dr5 + 7*A702*dr6 + 2*A222*dr*dtheta2 + 3*A322*dr2*dtheta2 + 4*A422*dr3*dtheta2 + 5*A522*dr4*dtheta2 + A142*dtheta4 + 2*A242*dr*dtheta4 + 3*A342*dr2*dtheta4 + A162*dtheta6;\n  dA_dr[2] = 2*A204*dr + 3*A304*dr2 + 4*A404*dr3 + 5*A504*dr4 + A124*dtheta2 + 2*A224*dr*dtheta2 + 3*A324*dr2*dtheta2 + A144*dtheta4;\n  dA_dr[3] = A106 + 2*A206*dr + 3*A306*dr2 + A126*dtheta2;\n  dA_dr[4] = A108;\n\n  /* r,r derivatives of coefficients */\n  d2A_dr2[0] = 2*(15*A600*dr4 + 21*A700*dr5 + 28*A800*dr6 + 36*A900*dr7 + 6*A420*dr2*dtheta2 + 10*A520*dr3*dtheta2 + 15*A620*dr4*dtheta2 + 21*A720*dr5*dtheta2 + A240*dtheta4 + 3*A340*dr*dtheta4 + 6*A440*dr2*dtheta4 + 10*A540*dr3*dtheta4 + A260*dtheta6 + 3*A360*dr*dtheta6);\n  d2A_dr2[1] = 2*(6*A402*dr2 + 10*A502*dr3 + 15*A602*dr4 + 21*A702*dr5 + A222*dtheta2 + 3*A322*dr*dtheta2 + 6*A422*dr2*dtheta2 + 10*A522*dr3*dtheta2 + A242*dtheta4 + 3*A342*dr*dtheta4);\n  d2A_dr2[2] = 2*(A204 + 3*A304*dr + 6*A404*dr2 + 10*A504*dr3 + A224*dtheta2 + 3*A324*dr*dtheta2);\n  d2A_dr2[3] = 2*(A206 + 3*A306*dr);\n  d2A_dr2[4] = 0;\n\n  /* theta derivatives of coefficients */\n  dA_dtheta[0] = 2*(A420 + dr*(A520 + dr*(A620 + A720*dr)))*dr4*dtheta + 4*((A240 + A340*dr)*dr2 + (A440 + A540*dr)*dr4 + (A060 + dr*(A160 + dr*(A260 + A360*dr)))*dtheta2)*dtheta3 + 2*(A060 + dr*(A160 + dr*(A260 + A360*dr)))*dtheta5 + 8*(A080 + A180*dr)*dtheta7;\n  dA_dtheta[1] = 2*dtheta*((A222 + A322*dr)*dr2 + (A422 + A522*dr)*dr4 + 2*dtheta2*(A042 + A142*dr + (A242 + A342*dr)*dr2 + (A062 + A162*dr)*dtheta2) + (A062 + A162*dr)*dtheta4);\n  dA_dtheta[2] = 2*dtheta*(A024 + A124*dr + (A224 + A324*dr)*dr2 + 2*(A044 + A144*dr)*dtheta2);\n  dA_dtheta[3] = 2*(A026 + A126*dr)*dtheta;\n  dA_dtheta[4] = 0;\n\n  /* theta,theta derivatives of coefficients */\n  d2A_dtheta2[0] = 2*((A420 + dr*(A520 + dr*(A620 + A720*dr)))*dr4 + 6*dtheta2*((A240 + A340*dr)*dr2 + (A440 + A540*dr)*dr4 + (A060 + dr*(A160 + dr*(A260 + A360*dr)))*dtheta2) + 9*(A060 + dr*(A160 + dr*(A260 + A360*dr)))*dtheta4 + 28*(A080 + A180*dr)*dtheta6);\n  d2A_dtheta2[1] = 2*((A222 + A322*dr)*dr2 + (A422 + A522*dr)*dr4 + 6*dtheta2*(A042 + A142*dr + (A242 + A342*dr)*dr2 + (A062 + A162*dr)*dtheta2) + 9*(A062 + A162*dr)*dtheta4);\n  d2A_dtheta2[2] = 2*(A024 + A124*dr + (A224 + A324*dr)*dr2 + 6*(A044 + A144*dr)*dtheta2);\n  d2A_dtheta2[3] = 2*(A026 + A126*dr);\n  d2A_dtheta2[4] = 0;\n\n  /* alpha term appearing in the denominator */\n  alpha = alpha20*dr2 + alpha02*dtheta2;\n\n  /* Derivatives of alpha */\n  dalpha_dr       = 2*alpha20*dr;\n  d2alpha_dr2     = 2*alpha20;\n  dalpha_dtheta   = 2*alpha02*dtheta;\n  d2alpha_dtheta2 = 2*alpha02;\n\n  /* C term appearing in Elliptic integrals and related power series in numerator */\n  const double C1 = alpha / beta;\n\n  double C[27];\n  C[0]  = 1;\n  C[1]  = C1;\n  C[2]  = C1*C1;\n  C[3]  = C[2]*C[1];\n  C[4]  = C[2]*C[2];\n  C[5]  = C[3]*C[2];\n  C[6]  = C[3]*C[3];\n  C[7]  = C[4]*C[3];\n  C[8]  = C[4]*C[4];\n  C[9]  = C[5]*C[4];\n  C[10] = C[5]*C[5];\n  C[11] = C[6]*C[5];\n  C[12] = C[6]*C[6];\n  C[13] = C[7]*C[6];\n  C[14] = C[7]*C[7];\n  C[15] = C[8]*C[7];\n  C[16] = C[8]*C[8];\n  C[17] = C[9]*C[8];\n  C[18] = C[9]*C[9];\n  C[19] = C[10]*C[9];\n  C[20] = C[10]*C[10];\n  C[21] = C[11]*C[10];\n  C[22] = C[11]*C[11];\n  C[23] = C[12]*C[11];\n  C[24] = C[12]*C[12];\n  C[25] = C[13]*C[12];\n  C[26] = C[13]*C[13];\n\n  dC1_dr       = dalpha_dr / beta;\n  d2C1_dr2     = d2alpha_dr2 / beta;\n  dC1_dtheta   = dalpha_dtheta / beta;\n  d2C1_dtheta2 = d2alpha_dtheta2 / beta;\n\n  double dC_dr[27];\n  dC_dr[0]  = 0;\n  dC_dr[1]  = 1*C[0]*dC1_dr;\n  dC_dr[2]  = 2*C[1]*dC1_dr;\n  dC_dr[3]  = 3*C[2]*dC1_dr;\n  dC_dr[4]  = 4*C[3]*dC1_dr;\n  dC_dr[5]  = 5*C[4]*dC1_dr;\n  dC_dr[6]  = 6*C[5]*dC1_dr;\n  dC_dr[7]  = 7*C[6]*dC1_dr;\n  dC_dr[8]  = 8*C[7]*dC1_dr;\n  dC_dr[9]  = 9*C[8]*dC1_dr;\n  dC_dr[10] = 10*C[9]*dC1_dr;\n  dC_dr[11] = 11*C[10]*dC1_dr;\n  dC_dr[12] = 12*C[11]*dC1_dr;\n  dC_dr[13] = 13*C[12]*dC1_dr;\n  dC_dr[14] = 14*C[13]*dC1_dr;\n  dC_dr[15] = 15*C[14]*dC1_dr;\n  dC_dr[16] = 16*C[15]*dC1_dr;\n  dC_dr[17] = 17*C[16]*dC1_dr;\n  dC_dr[18] = 18*C[17]*dC1_dr;\n  dC_dr[19] = 19*C[18]*dC1_dr;\n  dC_dr[20] = 20*C[19]*dC1_dr;\n  dC_dr[21] = 21*C[20]*dC1_dr;\n  dC_dr[22] = 22*C[21]*dC1_dr;\n  dC_dr[23] = 23*C[22]*dC1_dr;\n  dC_dr[24] = 24*C[23]*dC1_dr;\n  dC_dr[25] = 25*C[24]*dC1_dr;\n  dC_dr[26] = 26*C[25]*dC1_dr;\n\n  double d2C_dr2[27];\n  d2C_dr2[0]  = 0;\n  d2C_dr2[1]  = d2C1_dr2;\n  d2C_dr2[2]  = 2*1*C[0]*dC1_dr*dC1_dr + 2*C[1]*d2C1_dr2;\n  d2C_dr2[3]  = 3*2*C[1]*dC1_dr*dC1_dr + 3*C[2]*d2C1_dr2;\n  d2C_dr2[4]  = 4*3*C[2]*dC1_dr*dC1_dr + 4*C[3]*d2C1_dr2;\n  d2C_dr2[5]  = 5*4*C[3]*dC1_dr*dC1_dr + 5*C[4]*d2C1_dr2;\n  d2C_dr2[6]  = 6*5*C[4]*dC1_dr*dC1_dr + 6*C[5]*d2C1_dr2;\n  d2C_dr2[7]  = 7*6*C[5]*dC1_dr*dC1_dr + 7*C[6]*d2C1_dr2;\n  d2C_dr2[8]  = 8*7*C[6]*dC1_dr*dC1_dr + 8*C[7]*d2C1_dr2;\n  d2C_dr2[9]  = 9*8*C[7]*dC1_dr*dC1_dr + 9*C[8]*d2C1_dr2;\n  d2C_dr2[10] = 10*9*C[8]*dC1_dr*dC1_dr + 10*C[9]*d2C1_dr2;\n  d2C_dr2[11] = 11*10*C[9]*dC1_dr*dC1_dr + 11*C[10]*d2C1_dr2;\n  d2C_dr2[12] = 12*11*C[10]*dC1_dr*dC1_dr + 12*C[11]*d2C1_dr2;\n  d2C_dr2[13] = 13*12*C[11]*dC1_dr*dC1_dr + 13*C[12]*d2C1_dr2;\n  d2C_dr2[14] = 14*13*C[12]*dC1_dr*dC1_dr + 14*C[13]*d2C1_dr2;\n  d2C_dr2[15] = 15*14*C[13]*dC1_dr*dC1_dr + 15*C[14]*d2C1_dr2;\n  d2C_dr2[16] = 16*15*C[14]*dC1_dr*dC1_dr + 16*C[15]*d2C1_dr2;\n  d2C_dr2[17] = 17*16*C[15]*dC1_dr*dC1_dr + 17*C[16]*d2C1_dr2;\n  d2C_dr2[18] = 18*17*C[16]*dC1_dr*dC1_dr + 18*C[17]*d2C1_dr2;\n  d2C_dr2[19] = 19*18*C[17]*dC1_dr*dC1_dr + 19*C[18]*d2C1_dr2;\n  d2C_dr2[20] = 20*19*C[18]*dC1_dr*dC1_dr + 20*C[19]*d2C1_dr2;\n  d2C_dr2[21] = 21*20*C[19]*dC1_dr*dC1_dr + 21*C[20]*d2C1_dr2;\n  d2C_dr2[22] = 22*21*C[20]*dC1_dr*dC1_dr + 22*C[21]*d2C1_dr2;\n  d2C_dr2[23] = 23*22*C[21]*dC1_dr*dC1_dr + 23*C[22]*d2C1_dr2;\n  d2C_dr2[24] = 24*23*C[22]*dC1_dr*dC1_dr + 24*C[23]*d2C1_dr2;\n  d2C_dr2[25] = 25*24*C[23]*dC1_dr*dC1_dr + 25*C[24]*d2C1_dr2;\n  d2C_dr2[26] = 26*25*C[24]*dC1_dr*dC1_dr + 26*C[25]*d2C1_dr2;\n\n  double dC_dtheta[27];\n  dC_dtheta[0]  = 0;\n  dC_dtheta[1]  = 1*C[0]*dC1_dtheta;\n  dC_dtheta[2]  = 2*C[1]*dC1_dtheta;\n  dC_dtheta[3]  = 3*C[2]*dC1_dtheta;\n  dC_dtheta[4]  = 4*C[3]*dC1_dtheta;\n  dC_dtheta[5]  = 5*C[4]*dC1_dtheta;\n  dC_dtheta[6]  = 6*C[5]*dC1_dtheta;\n  dC_dtheta[7]  = 7*C[6]*dC1_dtheta;\n  dC_dtheta[8]  = 8*C[7]*dC1_dtheta;\n  dC_dtheta[9]  = 9*C[8]*dC1_dtheta;\n  dC_dtheta[10] = 10*C[9]*dC1_dtheta;\n  dC_dtheta[11] = 11*C[10]*dC1_dtheta;\n  dC_dtheta[12] = 12*C[11]*dC1_dtheta;\n  dC_dtheta[13] = 13*C[12]*dC1_dtheta;\n  dC_dtheta[14] = 14*C[13]*dC1_dtheta;\n  dC_dtheta[15] = 15*C[14]*dC1_dtheta;\n  dC_dtheta[16] = 16*C[15]*dC1_dtheta;\n  dC_dtheta[17] = 17*C[16]*dC1_dtheta;\n  dC_dtheta[18] = 18*C[17]*dC1_dtheta;\n  dC_dtheta[19] = 19*C[18]*dC1_dtheta;\n  dC_dtheta[20] = 20*C[19]*dC1_dtheta;\n  dC_dtheta[21] = 21*C[20]*dC1_dtheta;\n  dC_dtheta[22] = 22*C[21]*dC1_dtheta;\n  dC_dtheta[23] = 23*C[22]*dC1_dtheta;\n  dC_dtheta[24] = 24*C[23]*dC1_dtheta;\n  dC_dtheta[25] = 25*C[24]*dC1_dtheta;\n  dC_dtheta[26] = 26*C[25]*dC1_dtheta;\n\n  double d2C_dtheta2[27];\n  d2C_dtheta2[0]  = 0;\n  d2C_dtheta2[1]  = d2C1_dtheta2;\n  d2C_dtheta2[2]  = 2*1*C[0]*dC1_dtheta*dC1_dtheta + 2*C[1]*d2C1_dtheta2;\n  d2C_dtheta2[3]  = 3*2*C[1]*dC1_dtheta*dC1_dtheta + 3*C[2]*d2C1_dtheta2;\n  d2C_dtheta2[4]  = 4*3*C[2]*dC1_dtheta*dC1_dtheta + 4*C[3]*d2C1_dtheta2;\n  d2C_dtheta2[5]  = 5*4*C[3]*dC1_dtheta*dC1_dtheta + 5*C[4]*d2C1_dtheta2;\n  d2C_dtheta2[6]  = 6*5*C[4]*dC1_dtheta*dC1_dtheta + 6*C[5]*d2C1_dtheta2;\n  d2C_dtheta2[7]  = 7*6*C[5]*dC1_dtheta*dC1_dtheta + 7*C[6]*d2C1_dtheta2;\n  d2C_dtheta2[8]  = 8*7*C[6]*dC1_dtheta*dC1_dtheta + 8*C[7]*d2C1_dtheta2;\n  d2C_dtheta2[9]  = 9*8*C[7]*dC1_dtheta*dC1_dtheta + 9*C[8]*d2C1_dtheta2;\n  d2C_dtheta2[10] = 10*9*C[8]*dC1_dtheta*dC1_dtheta + 10*C[9]*d2C1_dtheta2;\n  d2C_dtheta2[11] = 11*10*C[9]*dC1_dtheta*dC1_dtheta + 11*C[10]*d2C1_dtheta2;\n  d2C_dtheta2[12] = 12*11*C[10]*dC1_dtheta*dC1_dtheta + 12*C[11]*d2C1_dtheta2;\n  d2C_dtheta2[13] = 13*12*C[11]*dC1_dtheta*dC1_dtheta + 13*C[12]*d2C1_dtheta2;\n  d2C_dtheta2[14] = 14*13*C[12]*dC1_dtheta*dC1_dtheta + 14*C[13]*d2C1_dtheta2;\n  d2C_dtheta2[15] = 15*14*C[13]*dC1_dtheta*dC1_dtheta + 15*C[14]*d2C1_dtheta2;\n  d2C_dtheta2[16] = 16*15*C[14]*dC1_dtheta*dC1_dtheta + 16*C[15]*d2C1_dtheta2;\n  d2C_dtheta2[17] = 17*16*C[15]*dC1_dtheta*dC1_dtheta + 17*C[16]*d2C1_dtheta2;\n  d2C_dtheta2[18] = 18*17*C[16]*dC1_dtheta*dC1_dtheta + 18*C[17]*d2C1_dtheta2;\n  d2C_dtheta2[19] = 19*18*C[17]*dC1_dtheta*dC1_dtheta + 19*C[18]*d2C1_dtheta2;\n  d2C_dtheta2[20] = 20*19*C[18]*dC1_dtheta*dC1_dtheta + 20*C[19]*d2C1_dtheta2;\n  d2C_dtheta2[21] = 21*20*C[19]*dC1_dtheta*dC1_dtheta + 21*C[20]*d2C1_dtheta2;\n  d2C_dtheta2[22] = 22*21*C[20]*dC1_dtheta*dC1_dtheta + 22*C[21]*d2C1_dtheta2;\n  d2C_dtheta2[23] = 23*22*C[21]*dC1_dtheta*dC1_dtheta + 23*C[22]*d2C1_dtheta2;\n  d2C_dtheta2[24] = 24*23*C[22]*dC1_dtheta*dC1_dtheta + 24*C[23]*d2C1_dtheta2;\n  d2C_dtheta2[25] = 25*24*C[23]*dC1_dtheta*dC1_dtheta + 25*C[24]*d2C1_dtheta2;\n  d2C_dtheta2[26] = 26*25*C[24]*dC1_dtheta*dC1_dtheta + 26*C[25]*d2C1_dtheta2;\n\n  /* Elliptic integrals */\n  ellE = gsl_sf_ellint_Ecomp(sqrt(1.0/(1.0+C1)), GSL_PREC_DOUBLE);\n  ellK = gsl_sf_ellint_Kcomp(sqrt(1.0/(1.0+C1)), GSL_PREC_DOUBLE);\n  const double ellip[2] = {ellK, ellE};\n\n  /* Derivatives of elliptic integrals */\n  dellE_dC   = (ellK - ellE)/(2.*(1+C1));\n  dellK_dC   = (C1*ellK - (1+C1)*ellE)/(2.*C1*(1+C1));\n  d2ellE_dC2 = -(2*C1*ellK - (2*C1-1)*ellE)/(4.*C1*(1+C1)*(1+C1));\n  d2ellK_dC2 = -(C1*(1+2*C1)*ellK - 2*(1+C1)*(1+C1)*ellE)/(4.*C1*C1*(1+C1)*(1+C1));\n\n  dellE_dr       = dellE_dC * dC1_dr;\n  dellE_dtheta   = dellE_dC * dC1_dtheta;\n  d2ellE_dr2     = d2ellE_dC2 * dC1_dr * dC1_dr + dellE_dC * d2C1_dr2;\n  d2ellE_dtheta2 = d2ellE_dC2 * dC1_dtheta * dC1_dtheta + dellE_dC * d2C1_dtheta2;\n\n  dellK_dr       = dellK_dC * dC1_dr;\n  dellK_dtheta   = dellK_dC * dC1_dtheta;\n  d2ellK_dr2     = d2ellK_dC2 * dC1_dr * dC1_dr + dellK_dC * d2C1_dr2;\n  d2ellK_dtheta2 = d2ellK_dC2 * dC1_dtheta * dC1_dtheta + dellK_dC * d2C1_dtheta2;\n\n  const double dellip_dr[2] = {dellK_dr, dellE_dr};\n  const double dellip_dtheta[2] = {dellK_dtheta, dellE_dtheta};\n\n  const double d2ellip_dr2[2] = {d2ellK_dr2, d2ellE_dr2};\n  const double d2ellip_dtheta2[2] = {d2ellK_dtheta2, d2ellE_dtheta2};\n\n  if(m>20)\n  {\n    printf(\"Support for computing mode %d has not yet been added.\\n\", m);\n    return;\n  }\n\n  /* Numerator */\n  double num = 0;\n  for(int i=0; i<2; i++)\n    for(int j=0; j<5; j++)\n      for(int k=0; k<27; k++)\n        num += ReEI[m][i][j][k]*ellip[i]*A[j]*C[k];\n  double dnum_dr = 0;\n  for(int i=0; i<2; i++)\n    for(int j=0; j<5; j++)\n      for(int k=0; k<27; k++)\n        dnum_dr += ReEI[m][i][j][k]*\n            (dellip_dr[i]*A[j]*C[k]\n           + ellip[i]*dA_dr[j]*C[k]\n           + ellip[i]*A[j]*dC_dr[k]);\n  double d2num_dr2 = 0;\n  for(int i=0; i<2; i++)\n    for(int j=0; j<5; j++)\n      for(int k=0; k<27; k++)\n        d2num_dr2 += ReEI[m][i][j][k]*\n            (d2ellip_dr2[i]*A[j]*C[k]\n           + dellip_dr[i]*dA_dr[j]*C[k]\n           + dellip_dr[i]*A[j]*dC_dr[k]\n           + dellip_dr[i]*dA_dr[j]*C[k]\n           + ellip[i]*d2A_dr2[j]*C[k]\n           + ellip[i]*dA_dr[j]*dC_dr[k]\n           + dellip_dr[i]*A[j]*dC_dr[k]\n           + ellip[i]*dA_dr[j]*dC_dr[k]\n           + ellip[i]*A[j]*d2C_dr2[k]);\n  double dnum_dtheta = 0;\n  for(int i=0; i<2; i++)\n    for(int j=0; j<5; j++)\n      for(int k=0; k<27; k++)\n        dnum_dtheta += ReEI[m][i][j][k]*\n            (dellip_dtheta[i]*A[j]*C[k]\n           + ellip[i]*dA_dtheta[j]*C[k]\n           + ellip[i]*A[j]*dC_dtheta[k]);\n  double d2num_dtheta2 = 0;\n  for(int i=0; i<2; i++)\n    for(int j=0; j<5; j++)\n      for(int k=0; k<27; k++)\n        d2num_dtheta2 += ReEI[m][i][j][k]*\n            (d2ellip_dtheta2[i]*A[j]*C[k]\n           + dellip_dtheta[i]*dA_dtheta[j]*C[k]\n           + dellip_dtheta[i]*A[j]*dC_dtheta[k]\n           + dellip_dtheta[i]*dA_dtheta[j]*C[k]\n           + ellip[i]*d2A_dtheta2[j]*C[k]\n           + ellip[i]*dA_dtheta[j]*dC_dtheta[k]\n           + dellip_dtheta[i]*A[j]*dC_dtheta[k]\n           + ellip[i]*dA_dtheta[j]*dC_dtheta[k]\n           + ellip[i]*A[j]*d2C_dtheta2[k]);\n\n  /* Denominator */\n  s        = beta*C[3]*pow(alpha+beta, 2.5);\n  ds_dr    = beta*(3*C[2]*pow(alpha+beta, 2.5)*dC1_dr + C[3]*2.5*pow(alpha+beta, 1.5)*dalpha_dr);\n  ds_dtheta   = beta*(3*C[2]*pow(alpha+beta, 2.5)*dC1_dtheta + C[3]*2.5*pow(alpha+beta, 1.5)*dalpha_dtheta);\n  d2s_dr2  = beta*(6*C1*pow(alpha+beta, 2.5)*dC1_dr*dC1_dr\n               + 3*C[2]*2.5*pow(alpha+beta, 1.5)*dalpha_dr*dC1_dr\n               + 3*C[2]*pow(alpha+beta, 2.5)*d2C1_dr2\n               + 3*C[2]*dC1_dr*2.5*pow(alpha+beta, 1.5)*dalpha_dr\n               + C[3]*2.5*1.5*pow(alpha+beta, 0.5)*dalpha_dr*dalpha_dr\n               + C[3]*2.5*pow(alpha+beta, 1.5)*d2alpha_dr2);\n  d2s_dtheta2 = beta*(6*C1*pow(alpha+beta, 2.5)*dC1_dtheta*dC1_dtheta\n               + 3*C[2]*2.5*pow(alpha+beta, 1.5)*dalpha_dtheta*dC1_dtheta\n               + 3*C[2]*pow(alpha+beta, 2.5)*d2C1_dtheta2\n               + 3*C[2]*dC1_dtheta*2.5*pow(alpha+beta, 1.5)*dalpha_dtheta\n               + C[3]*2.5*1.5*pow(alpha+beta, 0.5)*dalpha_dtheta*dalpha_dtheta\n               + C[3]*2.5*pow(alpha+beta, 1.5)*d2alpha_dtheta2);\n\n  /* Singular field */\n  double RePhiS = 4.0*num/s;\n\n  /* First derivatives of PhiS */\n  dPhiS_dt  = - m * om * (RePhiS); // This should be interpreted as pure-imaginary\n  dPhiS_dr  = 4.0*(-ds_dr*num + dnum_dr*s) /(s*s);\n  dPhiS_dth = 4.0*(-ds_dtheta*num + dnum_dtheta*s)/(s*s);\n  dPhiS_dph = m * (RePhiS); // This should be interpreted as pure-imaginary\n\n  /* Second derivatives of PhiS */\n  d2PhiS_dr2   = 4.0*(2.0*ds_dr*ds_dr*num - s*(2*dnum_dr*ds_dr + d2s_dr2*num) + d2num_dr2*s*s)/(s*s*s);\n  d2PhiS_dth2  = 4.0*(2.0*ds_dtheta*ds_dtheta*num - s*(2*dnum_dtheta*ds_dtheta + d2s_dtheta2*num) + d2num_dtheta2*s*s)/(s*s*s);\n  d2PhiS_dph2  = - m*m*(RePhiS);\n  d2PhiS_dt2   = - m*m*om*om*(RePhiS);\n  d2PhiS_dtph = m*m*om*(RePhiS);\n  d2PhiS_dtr   = NAN;\n  d2PhiS_dtth  = NAN;\n  d2PhiS_drth  = NAN;\n  d2PhiS_drph  = NAN;\n  d2PhiS_dthph = NAN;\n\n  /* Box[PhiS] */\n  double sinth  = sin(theta);\n  double sinth2 = sinth*sinth;\n  double sin2th = sin(2*theta);\n  double cos2th = cos(2*theta);\n  double r2 = r*r;\n  double r3 = r2*r;\n  double r4 = r2*r2;\n  double a2 = a*a;\n  double a4 = a2*a2;\n\n  double effsrc = -((2*a2*dPhiS_dr - a2*d2PhiS_dph2 - a4*d2PhiS_dr2 - a2*d2PhiS_dth2 - 4*dPhiS_dr*r - 2*a2*dPhiS_dr*r +\n         4*d2PhiS_dph2*r + 2*a*d2PhiS_dtph*r + 4*a2*d2PhiS_dr2*r + 2*d2PhiS_dth2*r + 6*dPhiS_dr*r2 - 2*d2PhiS_dph2*r2 - 4*d2PhiS_dr2*r2 -\n         2*a2*d2PhiS_dr2*r2 - d2PhiS_dth2*r2 - 2*dPhiS_dr*r3 + 4*d2PhiS_dr2*r3 - d2PhiS_dr2*r4 +\n         (a4*d2PhiS_dt2 + 4*a*d2PhiS_dtph*r + 2*d2PhiS_dt2*r4 + a2*d2PhiS_dt2*r*(2 + 3*r))*sinth2 +\n         cos2th*(a4*d2PhiS_dr2 - 2*a*d2PhiS_dtph*r + (-2 + r)*r*(d2PhiS_dth2 + 2*dPhiS_dr*(-1 + r) + d2PhiS_dr2*(-2 + r)*r) +\n            a2*(-d2PhiS_dph2 + d2PhiS_dth2 + 2*dPhiS_dr*(-1 + r) - 4*d2PhiS_dr2*r + 2*d2PhiS_dr2*r2) +\n            a2*d2PhiS_dt2*(a2 + (-2 + r)*r)*sinth2) - a2*dPhiS_dth*sin2th + 2*dPhiS_dth*r*sin2th -\n         dPhiS_dth*r2*sin2th))/((sinth2*(a2 + (-2 + r)*r)*(a2 + 2*r2 + a2*cos2th)));\n\n  /* Store calculated quantities into the arrays provided by the caller,\n     including the phase factor exp(-i*m*phi_p) */\n  double cosmph = cos(m*xp.phi);\n  double sinmph = sin(m*xp.phi);\n\n  PhiS[0] = RePhiS*cosmph;\n  PhiS[1] = - RePhiS*sinmph;\n\n  dPhiS_dx[0] = dPhiS_dt*sinmph;\n  dPhiS_dx[1] = dPhiS_dt*cosmph;\n  dPhiS_dx[2] = dPhiS_dr*cosmph;\n  dPhiS_dx[3] = - dPhiS_dr*sinmph;\n  dPhiS_dx[4] = dPhiS_dth*cosmph;\n  dPhiS_dx[5] = - dPhiS_dth*sinmph;\n  dPhiS_dx[6] = dPhiS_dph*sinmph;\n  dPhiS_dx[7] = dPhiS_dph*cosmph;\n\n  d2PhiS_dx2[0]  = d2PhiS_dt2*cosmph;\n  d2PhiS_dx2[1]  = - d2PhiS_dt2*sinmph;\n  d2PhiS_dx2[2]  = d2PhiS_dtr*cosmph;\n  d2PhiS_dx2[3]  = - d2PhiS_dtr*sinmph;\n  d2PhiS_dx2[4]  = d2PhiS_dtth*cosmph;\n  d2PhiS_dx2[5]  = - d2PhiS_dtth*sinmph;\n  d2PhiS_dx2[6]  = d2PhiS_dtph*cosmph;\n  d2PhiS_dx2[7]  = - d2PhiS_dtph*sinmph;\n  d2PhiS_dx2[8]  = d2PhiS_dr2*cosmph;\n  d2PhiS_dx2[9]  = - d2PhiS_dr2*sinmph;\n  d2PhiS_dx2[10] = d2PhiS_drth*cosmph;\n  d2PhiS_dx2[11] = - d2PhiS_drth*sinmph;\n  d2PhiS_dx2[12] = d2PhiS_drph*cosmph;\n  d2PhiS_dx2[13] = - d2PhiS_drph*sinmph;\n  d2PhiS_dx2[14] = d2PhiS_dth2*cosmph;\n  d2PhiS_dx2[15] = - d2PhiS_dth2*sinmph;\n  d2PhiS_dx2[16] = d2PhiS_dthph*cosmph;\n  d2PhiS_dx2[17] = - d2PhiS_dthph*sinmph;\n  d2PhiS_dx2[18] = d2PhiS_dph2*cosmph;\n  d2PhiS_dx2[19] = - d2PhiS_dph2*sinmph;\n\n  src[0] = effsrc*cosmph;\n  src[1] = - effsrc*sinmph;\n}\n\n/* Initialize array of coefficients of pows of dr, dtheta and dphi. */\nvoid effsource_init(double mass, double spin)\n{\n  M = mass;\n  a = spin;\n}\n\n/* Initialize array of coefficients of pows of dr, dtheta and dphi. */\nvoid effsource_set_particle(struct coordinate * x_p, double E, double L, double ur_p)\n{\n  xp = *x_p;\n  double r = xp.r;\n\n  /* Compute A coefficients */\n  {\n\tA006 = 64*pow(pow(L,2) + pow(r,2) + (pow(a,2)*(2*M + r))/r,3);\n\tA008 = (-32*pow(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2)),2)*(-(pow(a,8)*pow(M,2)) + 4*pow(a,7)*E*L*pow(M,2) + 4*a*E*L*M*(2*M - 3*r)*pow(r,6) - 4*pow(a,3)*E*L*M*pow(r,3)*(22*pow(M,2) + 3*M*r + 4*pow(r,2)) - 4*pow(a,5)*E*L*M*(16*pow(M,3) + 18*pow(M,2)*r + pow(r,3)) + pow(r,6)*(2*M*pow(r,2)*(-2*M + r + 2*pow(E,2)*r) + pow(L,2)*(-16*pow(M,2) + 14*M*r - 3*pow(r,2))) + 2*pow(a,6)*M*(-2*pow(L,2)*M + 2*pow(E,2)*pow(2*M + r,3) + r*(2*pow(M,2) - M*r + pow(r,2))) + pow(a,2)*pow(r,3)*(2*M*pow(r,2)*(4*pow(M,2) + 6*(-1 + 2*pow(E,2))*M*r + 3*(1 + 2*pow(E,2))*pow(r,2)) + pow(L,2)*(32*pow(M,3) - 24*pow(M,2)*r + 16*M*pow(r,2) - 3*pow(r,3))) + pow(a,4)*M*(4*pow(L,2)*(8*pow(M,3) + 6*pow(M,2)*r - 3*M*pow(r,2) + 2*pow(r,3)) + pow(r,2)*(-4*pow(M,3) + 3*(-3 + 16*pow(E,2))*M*pow(r,2) + 6*(1 + 2*pow(E,2))*pow(r,3) + 4*pow(M,2)*(r + 12*pow(E,2)*r)))))/(3.*pow(r,8)*(pow(a,2) + r*(-2*M + r)));\n\tA024 = 48*pow(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2)),2);\n\tA026 = (-16*(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2)))*(-2*pow(a,7)*E*L*M*(24*pow(M,2) + 16*M*r + 3*pow(r,2)) + 2*a*E*L*M*pow(r,5)*(pow(L,2)*(10*M - 9*r) + (14*M - 15*r)*pow(r,2)) - 2*pow(a,5)*E*L*M*r*(16*pow(M,3) + 56*pow(M,2)*r + 50*M*pow(r,2) + 17*pow(r,3) + 3*pow(L,2)*(4*M + r)) + pow(a,8)*M*(6*pow(E,2)*pow(2*M + r,2) - r*(7*M + 3*r)) + pow(r,5)*(2*pow(L,2)*pow(r,2)*(-18*pow(M,2) + (21 + 2*pow(E,2))*M*r - 6*pow(r,2)) + pow(r,4)*(-8*pow(M,2) + 2*(5 + 4*pow(E,2))*M*r - 3*pow(r,2)) - 2*pow(L,4)*(8*pow(M,2) - 10*M*r + 3*pow(r,2))) + 2*pow(a,3)*E*L*M*pow(r,2)*(2*pow(L,2)*(4*pow(M,2) - 7*M*r - 6*pow(r,2)) - pow(r,2)*(16*pow(M,2) + 28*M*r + 29*pow(r,2))) + pow(a,4)*r*(12*pow(L,4)*pow(M,2) + pow(L,2)*(16*pow(M,4) - 8*(-5 + pow(E,2))*pow(M,3)*r + 16*(2 + pow(E,2))*pow(M,2)*pow(r,2) + 2*(-3 + 8*pow(E,2))*M*pow(r,3) - 9*pow(r,4)) + pow(r,2)*(4*pow(M,4) + 16*(2 + 3*pow(E,2))*pow(M,3)*r + 3*(3 + 32*pow(E,2))*pow(M,2)*pow(r,2) + 3*(-5 + 14*pow(E,2))*M*pow(r,3) - 9*pow(r,4))) + pow(a,2)*pow(r,2)*(pow(L,2)*pow(r,2)*(-4*pow(M,3) + 4*(6 + pow(E,2))*pow(M,2)*r + (33 + 14*pow(E,2))*M*pow(r,2) - 21*pow(r,3)) + pow(L,4)*(-8*pow(M,3) + 12*pow(M,2)*r + 8*M*pow(r,2) - 6*pow(r,3)) + pow(r,4)*(4*pow(M,3) + 3*(3 + 10*pow(E,2))*M*pow(r,2) - 9*pow(r,3) + 12*pow(M,2)*(r + 3*pow(E,2)*r))) + pow(a,6)*(pow(L,2)*M*(24*pow(M,2) + 4*(2 + 3*pow(E,2))*M*r + 3*(-1 + 2*pow(E,2))*pow(r,2)) + r*(-(r*(-12*pow(M,3) + 18*pow(M,2)*r + 17*M*pow(r,2) + 3*pow(r,3))) + 2*pow(E,2)*M*(8*pow(M,3) + 36*pow(M,2)*r + 42*M*pow(r,2) + 13*pow(r,3))))))/(3.*pow(r,6)*(pow(a,2) + r*(-2*M + r)));\n\tA042 = 12*pow(r,3)*(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2)));\n\tA044 = (-2*(-4*pow(a,7)*E*L*M*(72*pow(M,2) + 59*M*r + 12*pow(r,2)) + 4*a*E*L*M*pow(r,3)*(pow(L,4)*(8*M - 6*r) + 6*pow(L,2)*(6*M - 5*r)*pow(r,2) + 3*(10*M - 9*r)*pow(r,4)) + pow(a,8)*(-13*pow(M,2)*r + 3*pow(r,3) + 36*pow(E,2)*M*pow(2*M + r,2)) - 4*pow(a,5)*E*L*M*r*(-48*pow(M,3) + 50*pow(M,2)*r + 140*M*pow(r,2) + 49*pow(r,3) + 6*pow(L,2)*(8*M + 3*r)) + pow(r,3)*(8*pow(L,6)*M*(-2*M + r) + pow(L,2)*pow(r,4)*(-120*pow(M,2) + 2*(71 + 12*pow(E,2))*M*r - 41*pow(r,2)) + pow(L,4)*pow(r,2)*(-100*pow(M,2) + 4*(25 + pow(E,2))*M*r - 25*pow(r,2)) + pow(r,6)*(-24*pow(M,2) + 2*(19 + 12*pow(E,2))*M*r - 13*pow(r,2))) - 4*pow(a,3)*E*L*M*pow(r,2)*(6*pow(L,4) + pow(L,2)*(-48*pow(M,2) + 36*M*r + 48*pow(r,2)) + pow(r,2)*(-66*pow(M,2) + 55*M*r + 64*pow(r,2))) + pow(a,2)*pow(r,2)*(12*pow(L,6)*M + pow(L,2)*pow(r,2)*(-192*pow(M,3) + 176*pow(M,2)*r + 12*(9 + 8*pow(E,2))*M*pow(r,2) - 73*pow(r,3)) + 2*pow(r,4)*(-12*pow(M,3) + 2*(23 + 18*pow(E,2))*pow(M,2)*r + 9*(1 + 6*pow(E,2))*M*pow(r,2) - 18*pow(r,3)) - 2*pow(L,4)*(48*pow(M,3) + 8*(-3 + pow(E,2))*pow(M,2)*r - 2*(17 + 4*pow(E,2))*M*pow(r,2) + 11*pow(r,3))) + pow(a,4)*r*(3*pow(L,4)*(32*pow(M,2) + 4*(2 + pow(E,2))*M*r + pow(r,2)) + pow(r,2)*(12*pow(M,4) + 116*pow(M,3)*r + (55 + 288*pow(E,2))*pow(M,2)*pow(r,2) + 6*(-13 + 30*pow(E,2))*M*pow(r,3) - 30*pow(r,4)) - 2*pow(L,2)*(48*pow(M,4) + 4*(-7 + 12*pow(E,2))*pow(M,3)*r - 2*(53 + 24*pow(E,2))*pow(M,2)*pow(r,2) + 4*(2 - 15*pow(E,2))*M*pow(r,3) + 13*pow(r,4))) + 2*pow(a,6)*(pow(L,2)*(72*pow(M,3) + 2*(23 + 24*pow(E,2))*pow(M,2)*r + 6*(1 + 4*pow(E,2))*M*pow(r,2) + 3*pow(r,3)) + r*(-(r*(-10*pow(M,3) + 37*pow(M,2)*r + 29*M*pow(r,2) + 2*pow(r,3))) + 6*pow(E,2)*M*(-8*pow(M,3) + 12*pow(M,2)*r + 30*M*pow(r,2) + 11*pow(r,3))))))/(3.*pow(r,3)*(pow(a,2) + r*(-2*M + r)));\n\tA060 = pow(r,6);\n\tA062 = (6*pow(a,5)*E*L*M*(12*M + 5*r) + 4*pow(a,3)*E*L*M*r*(6*pow(L,2) - 20*pow(M,2) + 11*M*r + 18*pow(r,2)) - 3*pow(a,6)*(r*(M + r) + 6*pow(E,2)*M*(2*M + r)) + 2*a*E*L*M*pow(r,2)*(-4*pow(L,2)*(4*M - 3*r) + pow(r,2)*(-26*M + 21*r)) + pow(r,2)*(8*pow(L,4)*M*(2*M - r) + 4*pow(r,4)*(2*pow(M,2) - (3 + 2*pow(E,2))*M*r + pow(r,2)) + pow(L,2)*pow(r,2)*(36*pow(M,2) - 4*(8 + pow(E,2))*M*r + 7*pow(r,2))) + pow(a,2)*r*(-12*pow(L,4)*M + 4*pow(L,2)*(10*pow(M,3) + (-3 + 4*pow(E,2))*pow(M,2)*r - 2*(3 + 2*pow(E,2))*M*pow(r,2) + pow(r,3)) + pow(r,2)*(4*pow(M,3) + 4*(-4 + pow(E,2))*pow(M,2)*r + (1 - 34*pow(E,2))*M*pow(r,2) + 5*pow(r,3))) - pow(a,4)*(3*pow(L,2)*(12*pow(M,2) + 4*(1 + pow(E,2))*M*r + pow(r,2)) + 2*r*(r*(-2*pow(M,2) - 5*M*r + pow(r,2)) + 2*pow(E,2)*M*(-10*pow(M,2) + 8*M*r + 11*pow(r,2)))))/(3.*(pow(a,2) + r*(-2*M + r)));\n\tA080 = (pow(r,3)*(r*(-3*pow(a,2) + r*(-2*M + r)) + (4*M*(-3*pow(a,4)*pow(E,2) + 6*pow(a,3)*E*L + 2*a*E*L*r*(-4*M + 3*r) + pow(a,2)*(-3*pow(L,2) + 4*pow(E,2)*(M - r)*r) - r*(pow(E,2)*pow(r,3) + pow(L,2)*(-4*M + 2*r))))/(pow(a,2) + r*(-2*M + r))))/24.;\n\tA106 = (-16*pow(pow(L,2) + pow(r,2) + (pow(a,2)*(2*M + r))/r,2)*(-2*pow(a,2)*M + 2*pow(r,3) + (4*L*(pow(a,3)*E*M - pow(a,2)*L*M + 3*a*E*M*pow(r,2) + L*pow(r,2)*(-2*M + r)))/(pow(a,2) + r*(-2*M + r))))/pow(r,2);\n\tA108 = (-16*(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2)))*(2*pow(a,11)*E*L*pow(M,2)*(80*pow(M,2) + 73*M*r + 12*pow(r,2)) + 2*pow(a,9)*E*L*M*r*(32*(-1 + 6*pow(E,2))*pow(M,4) + 12*(23 + 24*pow(E,2))*pow(M,3)*r + (353 + 144*pow(E,2))*pow(M,2)*pow(r,2) + 2*(11 + 12*pow(E,2))*M*pow(r,3) - 12*pow(r,4) + 4*pow(L,2)*M*(16*M + 3*r)) + pow(a,12)*pow(M,2)*(3*r*(3*M + 2*r) - 8*pow(E,2)*(10*pow(M,2) + 11*M*r + 3*pow(r,2))) + 2*a*E*L*M*pow(r,5)*(6*pow(L,4)*pow(r,2)*(20*pow(M,2) + 4*pow(E,2)*M*r - 5*pow(r,2)) + 12*pow(L,6)*(4*pow(M,2) - pow(r,2)) + pow(L,2)*pow(r,4)*(136*pow(M,2) + 2*(-37 + 30*pow(E,2))*M*r + 3*pow(r,2)) - 2*pow(r,6)*(34*pow(M,2) - 41*M*r + 12*pow(r,2))) + 4*pow(a,3)*E*L*M*pow(r,3)*(6*pow(L,6)*(12*pow(M,2) - 2*M*r - pow(r,2)) + pow(L,2)*pow(r,3)*(-340*pow(M,3) + 6*(61 + 20*pow(E,2))*pow(M,2)*r + (-83 + 90*pow(E,2))*M*pow(r,2) - 23*pow(r,3)) + 2*pow(r,5)*(-192*pow(M,3) + 81*pow(M,2)*r + 6*(7 + pow(E,2))*M*pow(r,2) - 19*pow(r,3)) + 6*pow(L,4)*r*(20*pow(M,3) + 4*(10 + pow(E,2))*pow(M,2)*r + 2*(-5 + 2*pow(E,2))*M*pow(r,2) - 5*pow(r,3))) + 2*pow(a,5)*E*L*M*pow(r,2)*(6*pow(L,4)*(2*M + r)*(60*pow(M,2) + 2*(-5 + 2*pow(E,2))*M*r - 5*pow(r,2)) + pow(r,3)*(-420*pow(M,4) - 876*pow(M,3)*r + (907 + 144*pow(E,2))*pow(M,2)*pow(r,2) + 24*(5 + 3*pow(E,2))*M*pow(r,3) - 92*pow(r,4)) + pow(L,2)*r*(-608*pow(M,4) + 8*(13 + 30*pow(E,2))*pow(M,3)*r + 48*(17 + 10*pow(E,2))*pow(M,2)*pow(r,2) + 36*(-7 + 5*pow(E,2))*M*pow(r,3) - 79*pow(r,4))) + pow(a,2)*pow(r,3)*(48*pow(L,8)*pow(M,2)*(-2*M + r) - 24*pow(L,6)*M*pow(r,2)*(2*(5 + 4*pow(E,2))*pow(M,2) + (-5 + 4*pow(E,2))*M*r - 2*pow(E,2)*pow(r,2)) + 2*M*pow(r,7)*(60*pow(M,3) + 4*(-22 + 25*pow(E,2))*pow(M,2)*r + (51 - 86*pow(E,2))*M*pow(r,2) + (-11 + 23*pow(E,2))*pow(r,3)) + pow(L,2)*pow(r,5)*(752*pow(M,4) - 4*(197 + 66*pow(E,2))*pow(M,3)*r + 8*(28 - 11*pow(E,2))*pow(M,2)*pow(r,2) + 3*(1 + 48*pow(E,2))*M*pow(r,3) - 6*pow(r,4)) + 2*pow(L,4)*pow(r,3)*(232*pow(M,4) - 36*(9 + 10*pow(E,2))*pow(M,3)*r - 2*(-71 + 60*pow(E,2))*pow(M,2)*pow(r,2) + 5*(-5 + 18*pow(E,2))*M*pow(r,3) + 3*pow(r,4))) + 2*pow(a,7)*E*L*M*r*(2*pow(L,2)*(288*pow(M,4) + 8*(29 + 15*pow(E,2))*pow(M,3)*r + 2*(71 + 60*pow(E,2))*pow(M,2)*pow(r,2) + 2*(-26 + 15*pow(E,2))*M*pow(r,3) - 15*pow(r,4)) + r*(-256*pow(M,5) - 556*pow(M,4)*r + 8*(-7 + 36*pow(E,2))*pow(M,3)*pow(r,2) + (827 + 288*pow(E,2))*pow(M,2)*pow(r,3) + 4*(11 + 18*pow(E,2))*M*pow(r,4) - 52*pow(r,5))) - pow(a,4)*pow(r,2)*(24*pow(L,6)*M*(20*pow(M,3) + 12*pow(E,2)*pow(M,2)*r + (-5 + 2*pow(E,2))*M*pow(r,2) - pow(E,2)*pow(r,3)) + 2*pow(L,4)*M*r*(-304*pow(M,4) + 8*(11 + 60*pow(E,2))*pow(M,3)*r + 60*(5 + 14*pow(E,2))*pow(M,2)*pow(r,2) + 60*(-3 + pow(E,2))*M*pow(r,3) + (11 - 90*pow(E,2))*pow(r,4)) + M*pow(r,5)*(48*pow(M,4) - 4*(31 + 168*pow(E,2))*pow(M,3)*r - 8*(-34 + 21*pow(E,2))*pow(M,2)*pow(r,2) + (-199 + 448*pow(E,2))*M*pow(r,3) + 12*(4 - 7*pow(E,2))*pow(r,4)) + pow(L,2)*pow(r,3)*(-296*pow(M,5) - 16*(50 + 57*pow(E,2))*pow(M,4)*r + 2*(627 + 452*pow(E,2))*pow(M,3)*pow(r,2) + 2*(-215 + 12*pow(E,2))*pow(M,2)*pow(r,3) - 6*(-3 + 38*pow(E,2))*M*pow(r,4) + 3*pow(r,5))) + pow(a,6)*M*r*(-4*pow(L,4)*(96*pow(M,4) + 4*(11 + 90*pow(E,2))*pow(M,3)*r + 16*(2 + 15*pow(E,2))*pow(M,2)*pow(r,2) - 38*M*pow(r,3) - 15*pow(E,2)*pow(r,4)) + pow(r,3)*(-56*pow(M,5) + 4*(9 + 128*pow(E,2))*pow(M,4)*r + 2*(41 + 520*pow(E,2))*pow(M,3)*pow(r,2) - (157 + 464*pow(E,2))*pow(M,2)*pow(r,3) + (163 - 568*pow(E,2))*M*pow(r,4) + 4*(-13 + 19*pow(E,2))*pow(r,5)) + pow(L,2)*r*(256*pow(M,5) + 8*(47 + 76*pow(E,2))*pow(M,4)*r - 8*(-3 + 76*pow(E,2))*pow(M,3)*pow(r,2) - 2*(415 + 716*pow(E,2))*pow(M,2)*pow(r,3) + 2*(143 - 8*pow(E,2))*M*pow(r,4) + (-17 + 160*pow(E,2))*pow(r,5))) - (2*M - r)*pow(r,8)*(2*pow(E,2)*M*(12*pow(L,6) + 30*pow(L,4)*pow(r,2) + 17*pow(L,2)*pow(r,4) + 5*pow(r,6)) + (2*M - r)*r*(2*pow(L,4)*(8*M - 3*r) + 4*M*pow(r,4) + pow(L,2)*pow(r,2)*(2*M + 3*r))) - pow(a,10)*M*(2*pow(L,2)*M*(40*pow(M,2) + (29 + 32*pow(E,2))*M*r + 3*(-1 + 4*pow(E,2))*pow(r,2)) + r*(r*(50*pow(M,3) + 11*pow(M,2)*r - 19*M*pow(r,2) + 6*pow(r,3)) + pow(E,2)*(-32*pow(M,4) + 336*pow(M,3)*r + 504*pow(M,2)*pow(r,2) + 156*M*pow(r,3) - 6*pow(r,4)))) - pow(a,8)*M*r*(64*pow(L,4)*pow(M,2) + 2*pow(L,2)*(16*(-1 + 36*pow(E,2))*pow(M,4) + 4*(27 + 166*pow(E,2))*pow(M,3)*r + (141 + 364*pow(E,2))*pow(M,2)*pow(r,2) + 3*(-13 + 6*pow(E,2))*M*pow(r,3) + 3*(1 - 7*pow(E,2))*pow(r,4)) + r*(r*(-92*pow(M,4) + 20*pow(M,3)*r + 65*pow(M,2)*pow(r,2) - 63*M*pow(r,3) + 28*pow(r,4)) - 2*pow(E,2)*(128*pow(M,5) + 368*pow(M,4)*r + 120*pow(M,3)*pow(r,2) - 424*pow(M,2)*pow(r,3) - 202*M*pow(r,4) + 17*pow(r,5))))))/(3.*pow(r,10)*pow(pow(a,2) + r*(-2*M + r),2));\n\tA124 = 8*(pow(L,2) + pow(r,2) + (pow(a,2)*(2*M + r))/r)*(-(pow(a,2)*r) - pow(L,2)*r - 3*pow(r,3) - (4*L*(pow(a,3)*E*M - pow(a,2)*L*M + 3*a*E*M*pow(r,2) + L*pow(r,2)*(-2*M + r)))/(pow(a,2) + r*(-2*M + r)));\n\tA126 = (-4*(2*pow(a,11)*E*L*pow(M,2)*(-16*(-16 + 9*pow(E,2))*pow(M,2) + (293 - 144*pow(E,2))*M*r + 6*(13 - 6*pow(E,2))*pow(r,2)) + 2*a*E*L*M*pow(r,5)*(2*pow(L,4)*pow(r,2)*(328*pow(M,2) + 2*(-79 + 21*pow(E,2))*M*r - 3*pow(r,2)) + 42*pow(L,6)*(4*pow(M,2) - pow(r,2)) + 3*pow(L,2)*pow(r,4)*(128*pow(M,2) + 2*(-37 + 20*pow(E,2))*M*r + 5*pow(r,2)) - 2*pow(r,6)*(118*pow(M,2) - 125*M*r + 33*pow(r,2))) - (2*M - r)*pow(r,7)*(2*pow(r,6)*(16*pow(M,2) + 2*(-7 + 10*pow(E,2))*M*r + 3*pow(r,2)) + 4*pow(L,6)*(16*pow(M,2) + (-20 + 21*pow(E,2))*M*r + 6*pow(r,2)) + 2*pow(L,2)*pow(r,4)*(10*pow(M,2) + (-17 + 66*pow(E,2))*M*r + 6*pow(r,2)) + pow(L,4)*pow(r,2)*(88*pow(M,2) + 2*(-61 + 94*pow(E,2))*M*r + 39*pow(r,2))) + pow(a,12)*M*(3*r*(21*pow(M,2) + 23*M*r + 6*pow(r,2)) - 2*pow(E,2)*(128*pow(M,3) + 172*pow(M,2)*r + 72*M*pow(r,2) + 9*pow(r,3))) + 4*pow(a,3)*E*L*M*pow(r,3)*(21*pow(L,6)*(12*pow(M,2) - 2*M*r - pow(r,2)) + pow(r,5)*(-1104*pow(M,3) + 4*(82 + 27*pow(E,2))*pow(M,2)*r + (311 - 18*pow(E,2))*M*pow(r,2) - 103*pow(r,3)) + 2*pow(L,4)*r*(256*pow(M,3) + 2*(127 + 21*pow(E,2))*pow(M,2)*r + 6*(-24 + 7*pow(E,2))*M*pow(r,2) - 21*pow(r,3)) - 2*pow(L,2)*pow(r,3)*(102*pow(M,3) - 2*(92 + 87*pow(E,2))*pow(M,2)*r + 5*(17 - 15*pow(E,2))*M*pow(r,2) + 11*pow(r,3))) + 2*pow(a,5)*E*L*M*pow(r,2)*(2*pow(L,4)*(1224*pow(M,3) + 28*(-1 + 3*pow(E,2))*pow(M,2)*r + 2*(-95 + 21*pow(E,2))*M*pow(r,2) - 39*pow(r,3)) + pow(r,3)*(-3300*pow(M,4) + 4*(-517 + 216*pow(E,2))*pow(M,3)*r + (2435 + 504*pow(E,2))*pow(M,2)*pow(r,2) + 12*(59 - 12*pow(E,2))*M*pow(r,3) - 250*pow(r,4)) + pow(L,2)*r*(-192*pow(M,4) + 8*(169 + 114*pow(E,2))*pow(M,3)*r + 72*(4 + 17*pow(E,2))*pow(M,2)*pow(r,2) + 2*(-131 + 102*pow(E,2))*M*pow(r,3) - 131*pow(r,4))) - 2*pow(a,9)*E*L*M*(2*pow(L,2)*M*(216*pow(M,2) + 4*(2 + 9*pow(E,2))*M*r + 3*(-17 + 6*pow(E,2))*pow(r,2)) + r*(-288*(1 + 3*pow(E,2))*pow(M,4) - 4*(275 + 144*pow(E,2))*pow(M,3)*r + 3*(-403 + 72*pow(E,2))*pow(M,2)*pow(r,2) + 6*(-43 + 24*pow(E,2))*M*pow(r,3) + 36*pow(r,4))) + pow(a,2)*pow(r,3)*(-168*pow(L,8)*pow(M,2)*(2*M - r) - 2*pow(L,6)*r*(56*pow(M,4) + 12*(23 + 28*pow(E,2))*pow(M,3)*r + 14*(-13 + 12*pow(E,2))*pow(M,2)*pow(r,2) + 3*(13 - 28*pow(E,2))*M*pow(r,3) - 12*pow(r,4)) + 2*pow(r,7)*(132*pow(M,4) + 4*(-39 + 64*pow(E,2))*pow(M,3)*r + (113 - 246*pow(E,2))*pow(M,2)*pow(r,2) + (-58 + 79*pow(E,2))*M*pow(r,3) + 12*pow(r,4)) + 2*pow(L,4)*pow(r,3)*(592*pow(M,4) - 4*(139 + 329*pow(E,2))*pow(M,3)*r + 2*(91 - 22*pow(E,2))*pow(M,2)*pow(r,2) + 5*(-22 + 53*pow(E,2))*M*pow(r,3) + 42*pow(r,4)) + pow(L,2)*pow(r,5)*(2112*pow(M,4) - 4*(419 + 324*pow(E,2))*pow(M,3)*r - 4*(-73 + 4*pow(E,2))*pow(M,2)*pow(r,2) + (-93 + 464*pow(E,2))*M*pow(r,3) + 51*pow(r,4))) + 2*pow(a,7)*E*L*M*r*(-24*pow(L,4)*M*(9*M - r) + 2*pow(L,2)*(1296*pow(M,4) + 8*(35 + 51*pow(E,2))*pow(M,3)*r + 4*(-52 + 57*pow(E,2))*pow(M,2)*pow(r,2) - (19 + 6*pow(E,2))*M*pow(r,3) - 36*pow(r,4)) + r*(-1600*pow(M,5) + 4*(-515 + 216*pow(E,2))*pow(M,4)*r + 72*(13 + 22*pow(E,2))*pow(M,3)*pow(r,2) + 3*(857 + 72*pow(E,2))*pow(M,2)*pow(r,3) + 12*(43 - 18*pow(E,2))*M*pow(r,4) - 146*pow(r,5))) + pow(a,4)*pow(r,2)*(-4*pow(L,6)*M*(408*pow(M,3) + 4*(-32 + 63*pow(E,2))*pow(M,2)*r + 2*(-26 + 21*pow(E,2))*M*pow(r,2) - (5 + 21*pow(E,2))*pow(r,3)) + 2*pow(r,5)*(192*pow(M,5) + 24*(-1 + 53*pow(E,2))*pow(M,4)*r + 12*(-18 + 11*pow(E,2))*pow(M,3)*pow(r,2) + 4*(27 - 152*pow(E,2))*pow(M,2)*pow(r,3) + (-47 + 107*pow(E,2))*M*pow(r,4) + 18*pow(r,5)) + pow(L,4)*r*(1056*pow(M,5) - 80*(12 + 47*pow(E,2))*pow(M,4)*r - 8*(60 + 491*pow(E,2))*pow(M,3)*pow(r,2) + 12*(12 + 65*pow(E,2))*pow(M,2)*pow(r,3) + 2*(38 + 239*pow(E,2))*M*pow(r,4) + 45*pow(r,5)) + pow(L,2)*pow(r,3)*(3096*pow(M,5) + 12*(55 - 192*pow(E,2))*pow(M,4)*r - 2*(1367 + 788*pow(E,2))*pow(M,3)*pow(r,2) + (275 + 1116*pow(E,2))*pow(M,2)*pow(r,3) + 6*(21 + 94*pow(E,2))*M*pow(r,4) + 66*pow(r,5))) + pow(a,6)*r*(6*pow(L,6)*M*(24*pow(M,2) - 10*M*r + 3*pow(r,2)) - 2*pow(L,4)*M*(864*pow(M,4) + 8*(-1 + 306*pow(E,2))*pow(M,3)*r + 4*(-29 + 164*pow(E,2))*pow(M,2)*pow(r,2) - 32*(-2 + 9*pow(E,2))*M*pow(r,3) - (69 + 59*pow(E,2))*pow(r,4)) + pow(r,3)*(-152*pow(M,6) + 4*(23 + 800*pow(E,2))*pow(M,5)*r + 2*(-31 + 1672*pow(E,2))*pow(M,4)*pow(r,2) - (331 + 1792*pow(E,2))*pow(M,3)*pow(r,3) + (109 - 1704*pow(E,2))*pow(M,2)*pow(r,4) + 4*(8 + 19*pow(E,2))*M*pow(r,5) + 24*pow(r,6)) + pow(L,2)*r*(1600*pow(M,6) - 8*(-167 + 300*pow(E,2))*pow(M,5)*r - 4*(243 + 1228*pow(E,2))*pow(M,4)*pow(r,2) + 6*(-333 + 40*pow(E,2))*pow(M,3)*pow(r,3) + (59 + 1196*pow(E,2))*pow(M,2)*pow(r,4) + (293 + 228*pow(E,2))*M*pow(r,5) + 27*pow(r,6))) - pow(a,10)*M*(pow(L,2)*(-32*(-8 + 27*pow(E,2))*pow(M,3) + (242 - 448*pow(E,2))*pow(M,2)*r + 3*(-19 + 20*pow(E,2))*M*pow(r,2) + 18*(-3 + 2*pow(E,2))*pow(r,3)) + r*(r*(290*pow(M,3) + 165*pow(M,2)*r - 141*M*pow(r,2) - 68*pow(r,3)) + 2*pow(E,2)*(144*pow(M,4) + 680*pow(M,3)*r + 864*pow(M,2)*pow(r,2) + 358*M*pow(r,3) + 37*pow(r,4)))) + pow(a,8)*(2*pow(L,4)*M*(144*pow(M,3) + 8*(-8 + 27*pow(E,2))*pow(M,2)*r + 6*(-6 + 7*pow(E,2))*M*pow(r,2) - 9*(-3 + pow(E,2))*pow(r,3)) - pow(L,2)*M*r*(288*(1 + 18*pow(E,2))*pow(M,4) + 8*(105 + 286*pow(E,2))*pow(M,3)*r - 2*(-589 + 516*pow(E,2))*pow(M,2)*pow(r,2) + (15 - 268*pow(E,2))*M*pow(r,3) + 2*(-93 + 20*pow(E,2))*pow(r,4)) + pow(r,2)*(4*pow(E,2)*M*(400*pow(M,5) + 696*pow(M,4)*r - 12*pow(M,3)*pow(r,2) - 732*pow(M,2)*pow(r,3) - 368*M*pow(r,4) - 19*pow(r,5)) + r*(404*pow(M,5) + 8*pow(M,4)*r - 375*pow(M,3)*pow(r,2) + 103*pow(M,2)*pow(r,3) + 100*M*pow(r,4) + 6*pow(r,5))))))/(3.*pow(r,7)*pow(pow(a,2) + r*(-2*M + r),2));\n\tA142 = pow(r,2)*(2*pow(a,2)*M - 2*pow(r,3) - (4*L*(pow(a,3)*E*M - pow(a,2)*L*M + 3*a*E*M*pow(r,2) + L*pow(r,2)*(-2*M + r)))/(pow(a,2) + r*(-2*M + r)) - 4*(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2))));\n\tA144 = (2*pow(a,9)*E*L*M*(16*(-16 + 9*pow(E,2))*pow(M,2) + 12*(-13 + 6*pow(E,2))*M*r - 3*pow(r,2)) + (2*M - r)*pow(r,4)*(16*pow(L,6)*M*(2*M - r) + 2*pow(L,2)*pow(r,4)*(-6*pow(M,2) + (-5 + 66*pow(E,2))*M*r + 4*pow(r,2)) + 2*pow(L,4)*pow(r,2)*(34*pow(M,2) + (-33 + 47*pow(E,2))*M*r + 8*pow(r,2)) + pow(r,6)*(48*pow(M,2) + 10*(-5 + 6*pow(E,2))*M*r + 13*pow(r,2))) - 2*a*E*L*M*pow(r,4)*(2*pow(L,4)*(200*pow(M,2) - 94*M*r - 3*pow(r,2)) + 3*pow(L,2)*pow(r,2)*(188*pow(M,2) + 4*(-27 + 5*pow(E,2))*M*r + 7*pow(r,2)) - 3*pow(r,4)*(100*pow(M,2) - 116*M*r + 33*pow(r,2))) + pow(a,10)*M*(-9*r*(13*M + 7*r) + pow(E,2)*(256*pow(M,2) + 264*M*r + 66*pow(r,2))) - 2*pow(a,3)*E*L*M*pow(r,2)*(4*pow(L,4)*(180*pow(M,2) - 92*M*r - 3*pow(r,2)) + pow(L,2)*r*(768*pow(M,3) + 4*(67 + 84*pow(E,2))*pow(M,2)*r + 4*(-149 + 9*pow(E,2))*M*pow(r,2) + 15*pow(r,3)) - 4*pow(r,3)*(426*pow(M,3) - (85 + 108*pow(E,2))*pow(M,2)*r + 12*(-13 + 3*pow(E,2))*M*pow(r,2) + 52*pow(r,3))) - 2*pow(a,5)*E*L*M*r*(-6*pow(L,4)*(18*M + r) + pow(L,2)*(1728*pow(M,3) + 24*(-33 + 10*pow(E,2))*pow(M,2)*r - 4*(83 + 15*pow(E,2))*M*pow(r,2) - 9*pow(r,3)) + 4*r*(-336*pow(M,4) + 2*(-107 + 108*pow(E,2))*pow(M,3)*r + 3*(79 + 36*pow(E,2))*pow(M,2)*pow(r,2) + (161 - 90*pow(E,2))*M*pow(r,3) - 34*pow(r,4))) - pow(a,2)*pow(r,2)*(-2*pow(L,6)*M*(240*pow(M,2) - 154*M*r + 17*pow(r,2)) + 2*pow(L,4)*r*(48*pow(M,4) - 4*(35 + 176*pow(E,2))*pow(M,3)*r + 8*(8 + 15*pow(E,2))*pow(M,2)*pow(r,2) + (-35 + 73*pow(E,2))*M*pow(r,3) + 16*pow(r,4)) + pow(r,5)*(264*pow(M,4) + 4*(-11 + 144*pow(E,2))*pow(M,3)*r + 2*(11 - 222*pow(E,2))*pow(M,2)*pow(r,2) + (-125 + 138*pow(E,2))*M*pow(r,3) + 46*pow(r,4)) + pow(L,2)*pow(r,3)*(1656*pow(M,4) - 12*(65 + 166*pow(E,2))*pow(M,3)*r + 2*(-161 + 374*pow(E,2))*pow(M,2)*pow(r,2) + (47 + 256*pow(E,2))*M*pow(r,3) + 51*pow(r,4))) - pow(a,4)*r*(6*pow(L,6)*M*(12*M + r) + 2*pow(L,4)*(-576*pow(M,4) - 36*(-9 + 20*pow(E,2))*pow(M,3)*r + 2*(-19 + 137*pow(E,2))*pow(M,2)*pow(r,2) + (47 + 29*pow(E,2))*M*pow(r,3) + 8*pow(r,4)) + pow(r,3)*(192*pow(M,5) + 4*(85 + 468*pow(E,2))*pow(M,4)*r + 228*(-1 + 2*pow(E,2))*pow(M,3)*pow(r,2) - 3*(149 + 268*pow(E,2))*pow(M,2)*pow(r,3) - 12*(-9 + pow(E,2))*M*pow(r,4) + 60*pow(r,5)) + pow(L,2)*r*(1344*pow(M,5) + 32*(13 - 105*pow(E,2))*pow(M,4)*r - 4*(243 + 280*pow(E,2))*pow(M,3)*pow(r,2) + 28*(-29 + 75*pow(E,2))*pow(M,2)*pow(r,3) + (391 + 56*pow(E,2))*M*pow(r,4) + 66*pow(r,5))) + 2*pow(a,7)*E*L*M*(4*r*(-8*(5 + 18*pow(E,2))*pow(M,3) + 3*(-59 + 12*pow(E,2))*pow(M,2)*r + (-121 + 72*pow(E,2))*M*pow(r,2) + 6*pow(r,3)) + 3*pow(L,2)*(144*pow(M,2) + pow(r,2) + 4*M*(r + 3*pow(E,2)*r))) + pow(a,8)*(pow(L,2)*M*(-32*(-8 + 27*pow(E,2))*pow(M,2) + 12*(4 - 19*pow(E,2))*M*r + 3*(-41 + 20*pow(E,2))*pow(r,2)) + r*(-(r*(-420*pow(M,3) + 115*pow(M,2)*r + 256*M*pow(r,2) + 7*pow(r,3))) + 4*pow(E,2)*M*(40*pow(M,3) + 178*pow(M,2)*r + 209*M*pow(r,2) + 60*pow(r,3)))) - pow(a,6)*(6*pow(L,4)*M*(48*pow(M,2) + 2*(-5 + 18*pow(E,2))*M*r + (11 + pow(E,2))*pow(r,2)) + pow(L,2)*r*(-32*(5 + 108*pow(E,2))*pow(M,4) + 8*(-88 + 153*pow(E,2))*pow(M,3)*r + 2*(-247 + 658*pow(E,2))*pow(M,2)*pow(r,2) + (417 - 128*pow(E,2))*M*pow(r,3) + 23*pow(r,4)) + pow(r,2)*(4*pow(E,2)*M*(336*pow(M,4) + 324*pow(M,3)*r - 128*pow(M,2)*pow(r,2) - 263*M*pow(r,3) - 66*pow(r,4)) + r*(276*pow(M,4) - 652*pow(M,3)*r - 323*pow(M,2)*pow(r,2) + 350*M*pow(r,3) + 34*pow(r,4)))))/(3.*pow(r,4)*pow(pow(a,2) + r*(-2*M + r),2));\n\tA160 = -pow(r,5)/2.;\n\tA162 = (6*pow(a,7)*E*L*M*(12*(-4 + pow(E,2))*M - 11*r) + 3*pow(a,8)*(3*r*(-3*M + r) + 2*pow(E,2)*M*(24*M + 13*r)) + 2*a*E*L*M*(2*M - r)*pow(r,3)*((82*M - 51*r)*pow(r,2) + pow(L,2)*(-124*M + 30*r)) + (2*M - r)*pow(r,3)*(8*pow(L,4)*M*(2*M - r) + pow(L,2)*pow(r,2)*(-36*pow(M,2) + 4*(8 + 11*pow(E,2))*M*r - 7*pow(r,2)) + 8*pow(r,4)*(4*pow(M,2) + (-4 + 5*pow(E,2))*M*r + pow(r,2))) - 2*pow(a,5)*E*L*M*(-12*pow(L,2)*(9*M - 2*r) + r*(8*(-11 + 15*pow(E,2))*pow(M,2) - 24*(-9 + 5*pow(E,2))*M*r + 63*pow(r,2))) - 2*pow(a,3)*E*L*M*r*(pow(L,2)*(360*pow(M,2) - 352*M*r + 54*pow(r,2)) - r*(400*pow(M,3) + 4*(19 - 54*pow(E,2))*pow(M,2)*r + 4*(-40 + 21*pow(E,2))*M*pow(r,2) + 21*pow(r,3))) - pow(a,2)*r*(-8*pow(L,4)*M*(30*pow(M,2) - 29*M*r + 7*pow(r,2)) + pow(r,3)*(88*pow(M,4) + 4*(27 + 64*pow(E,2))*pow(M,3)*r - 2*(57 + 50*pow(E,2))*pow(M,2)*pow(r,2) + (-11 + 26*pow(E,2))*M*pow(r,3) + 15*pow(r,4)) + pow(L,2)*r*(400*pow(M,4) + 8*(-26 + 63*pow(E,2))*pow(M,2)*pow(r,2) + 4*(17 + 4*pow(E,2))*M*pow(r,3) + pow(r,4) - 64*pow(M,3)*(r + 14*pow(E,2)*r))) + pow(a,6)*(-3*pow(L,2)*(24*(-2 + 3*pow(E,2))*pow(M,2) + (4 - 8*pow(E,2))*M*r - 3*pow(r,2)) + r*(r*(86*pow(M,2) - 139*M*r + 19*pow(r,2)) + 2*pow(E,2)*M*(-44*pow(M,2) + 98*M*r + 105*pow(r,2)))) + pow(a,4)*(24*pow(L,4)*M*(-3*M + r) + pow(L,2)*r*(8*(-11 + 90*pow(E,2))*pow(M,3) + 4*(59 - 178*pow(E,2))*pow(M,2)*r + 2*(-41 + 26*pow(E,2))*M*pow(r,2) + pow(r,3)) + pow(r,2)*(r*(-20*pow(M,3) + 296*pow(M,2)*r - 149*M*pow(r,2) + 3*pow(r,3)) + 2*pow(E,2)*M*(-200*pow(M,3) - 108*pow(M,2)*r + 36*M*pow(r,2) + 73*pow(r,3)))))/(12.*r*pow(pow(a,2) + r*(-2*M + r),2));\n\tA180 = -(pow(r,2)*(60*pow(a,3)*E*L*M + 4*a*E*L*M*r*(8*M + 3*r) - 3*pow(a,4)*(10*pow(E,2)*M + 3*r) + r*(8*pow(L,2)*M*(-2*M + r) + pow(r,2)*(8*pow(M,2) + 2*(-3 + 5*pow(E,2))*M*r + pow(r,2))) - 2*pow(a,2)*(15*pow(L,2)*M + r*(r*(-7*M + 4*r) + 2*pow(E,2)*M*(4*M + 5*r)))))/(48.*(pow(a,2) + r*(-2*M + r)));\n\tA204 = (48*pow(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2)),2))/(pow(a,2) + r*(-2*M + r));\n\tA206 = (8*(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2)))*(-4*pow(a,7)*E*L*M*(12*pow(M,2) + 23*M*r + 3*pow(r,2)) - 4*a*E*L*M*pow(r,5)*(pow(L,2)*(67*M - 36*r) + (17*M - 15*r)*pow(r,2)) - 4*pow(a,5)*E*L*M*r*(-64*pow(M,3) - 104*pow(M,2)*r + 55*M*pow(r,2) + 7*pow(r,3) + 3*pow(L,2)*(8*M + r)) + pow(a,8)*M*(-(r*(M + 6*r)) + 12*pow(E,2)*(2*pow(M,2) + 3*M*r + pow(r,2))) + 4*pow(a,3)*E*L*M*pow(r,2)*(pow(L,2)*(16*pow(M,2) - 73*M*r + 3*pow(r,2)) + pow(r,2)*(196*pow(M,2) - 17*M*r + 11*pow(r,2))) + pow(r,5)*(8*pow(L,4)*(10*pow(M,2) - 11*M*r + 3*pow(r,2)) + pow(r,4)*(40*pow(M,2) - 2*(19 + 2*pow(E,2))*M*r + 9*pow(r,2)) + 2*pow(L,2)*pow(r,2)*(54*pow(M,2) + (-57 + 2*pow(E,2))*M*r + 15*pow(r,2))) - 2*pow(a,2)*pow(r,2)*(pow(r,4)*(58*pow(M,3) + 3*(-23 + 26*pow(E,2))*pow(M,2)*r + 33*M*pow(r,2) - 6*pow(r,3)) + 2*pow(L,4)*(8*pow(M,3) - 33*pow(M,2)*r + 10*M*pow(r,2) + 3*pow(r,3)) - pow(L,2)*pow(r,2)*(-194*pow(M,3) + (171 + 104*pow(E,2))*pow(M,2)*r + (-57 + 10*pow(E,2))*M*pow(r,2) + 6*pow(r,3))) + pow(a,4)*r*(48*pow(L,4)*pow(M,2) + pow(r,2)*(4*pow(M,4) + 20*(1 - 18*pow(E,2))*pow(M,3)*r - 3*(-53 + 76*pow(E,2))*pow(M,2)*pow(r,2) + 12*(-7 + 2*pow(E,2))*M*pow(r,3) - 3*pow(r,4)) - 2*pow(L,2)*(64*pow(M,4) + 8*(11 + 2*pow(E,2))*pow(M,3)*r - (109 + 80*pow(E,2))*pow(M,2)*pow(r,2) + (51 - 14*pow(E,2))*M*pow(r,3) + 9*pow(r,4))) + 2*pow(a,6)*(pow(L,2)*M*(12*pow(M,2) + 4*(7 + 6*pow(E,2))*M*r + 3*(-1 + 2*pow(E,2))*pow(r,2)) - r*(pow(r,3)*(31*M + 3*r) + 2*pow(E,2)*M*(32*pow(M,3) + 60*pow(M,2)*r + 9*M*pow(r,2) - 8*pow(r,3))))))/(3.*pow(r,6)*pow(pow(a,2) + r*(-2*M + r),2));\n\tA222 = (24*pow(r,3)*(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2))))/(pow(a,2) + r*(-2*M + r));\n\tA224 = (2*(4*pow(a,7)*E*L*M*(24*pow(M,2) + 19*M*r + 9*pow(r,2)) - 4*a*E*L*M*pow(r,3)*(4*pow(L,4)*M + 9*pow(L,2)*(12*M - 7*r)*pow(r,2) + 6*(9*M - 7*r)*pow(r,4)) + 4*pow(a,5)*E*L*M*r*(48*pow(M,3) + 142*pow(M,2)*r + 76*M*pow(r,2) + 50*pow(r,3) + pow(L,2)*(-6*M + 9*r)) - pow(a,8)*(24*pow(E,2)*pow(M,2)*(2*M + r) + r*(49*pow(M,2) + 42*M*r + 6*pow(r,2))) + pow(r,3)*(4*pow(L,6)*M*(2*M - r) + 12*pow(L,2)*pow(r,4)*(28*pow(M,2) - 32*M*r + 9*pow(r,2)) + 3*pow(r,6)*(40*pow(M,2) - 2*(23 + 2*pow(E,2))*M*r + 13*pow(r,2)) + 2*pow(L,4)*pow(r,2)*(118*pow(M,2) + (-131 + 2*pow(E,2))*M*r + 36*pow(r,2))) + 4*pow(a,3)*E*L*M*pow(r,3)*(-6*pow(L,2)*(9*M - 8*r) + r*(114*pow(M,2) + 23*M*r + 83*pow(r,2))) + 2*pow(a,2)*pow(r,3)*(pow(L,4)*(4*(18 + pow(E,2))*pow(M,2) + (-71 + 2*pow(E,2))*M*r + 15*pow(r,2)) - 3*pow(r,3)*(12*pow(M,3) + 2*(-7 + 24*pow(E,2))*pow(M,2)*r + (31 + 6*pow(E,2))*M*pow(r,2) - 15*pow(r,3)) + 3*pow(L,2)*r*(-32*pow(M,3) + 2*(19 + 9*pow(E,2))*pow(M,2)*r - 61*M*pow(r,2) + 25*pow(r,3))) + pow(a,4)*r*(6*pow(L,4)*(2*pow(M,2) - 6*M*r - pow(r,2)) - 3*pow(r,2)*(76*pow(M,4) + 12*(-1 + 16*pow(E,2))*pow(M,3)*r + 3*(-19 + 56*pow(E,2))*pow(M,2)*pow(r,2) + 4*(7 + 3*pow(E,2))*M*pow(r,3) - 19*pow(r,4)) - 2*pow(L,2)*(48*pow(M,4) + 68*pow(M,3)*r - 4*(13 + 9*pow(E,2))*pow(M,2)*pow(r,2) + 84*M*pow(r,3) - 15*pow(r,4))) - 2*pow(a,6)*(pow(L,2)*(24*pow(M,3) + (26 - 6*pow(E,2))*pow(M,2)*r + 39*M*pow(r,2) + 6*pow(r,3)) + M*r*(r*(-106*pow(M,2) - 37*M*r + 39*pow(r,2)) + 6*pow(E,2)*(8*pow(M,3) + 36*pow(M,2)*r + 20*M*pow(r,2) + pow(r,3))))))/(3.*pow(r,3)*pow(pow(a,2) + r*(-2*M + r),2));\n\tA240 = (3*pow(r,6))/(pow(a,2) + r*(-2*M + r));\n\tA242 = (24*pow(a,5)*E*L*M*(7*M + 4*r) + 12*pow(a,3)*E*L*M*r*(4*pow(L,2) - 16*pow(M,2) + 9*M*r + 23*pow(r,2)) - 12*a*E*L*M*pow(r,2)*(pow(L,2)*(8*M - 4*r) + (19*M - 13*r)*pow(r,2)) - 3*pow(a,6)*(4*pow(E,2)*M*(7*M + 3*r) + r*(10*M + 3*r)) + pow(r,2)*(24*pow(L,4)*M*(2*M - r) + pow(r,4)*(120*pow(M,2) - 2*(71 + 6*pow(E,2))*M*r + 41*pow(r,2)) + pow(L,2)*pow(r,2)*(228*pow(M,2) - 232*M*r + 59*pow(r,2))) + pow(a,2)*r*(-24*pow(L,4)*M + pow(r,2)*(-36*pow(M,3) + (22 - 84*pow(E,2))*pow(M,2)*r - 6*(19 + 10*pow(E,2))*M*pow(r,2) + 67*pow(r,3)) + 2*pow(L,2)*(48*pow(M,3) - 6*(13 + 2*pow(E,2))*M*pow(r,2) + 25*pow(r,3) + 6*pow(M,2)*(r + 4*pow(E,2)*r))) - pow(a,4)*(3*pow(L,2)*(28*pow(M,2) + 4*(5 + 2*pow(E,2))*M*r + 3*pow(r,2)) + r*(r*(-78*pow(M,2) + 14*M*r - 17*pow(r,2)) + 12*pow(E,2)*M*(-8*pow(M,2) + 10*M*r + 7*pow(r,2)))))/(6.*pow(pow(a,2) + r*(-2*M + r),2));\n\tA260 = (pow(r,3)*(48*pow(a,3)*E*L*M - 3*pow(a,4)*(8*pow(E,2)*M + r) + 16*a*E*L*M*r*(-5*M + 3*r) + r*(20*pow(L,2)*M*(2*M - r) + pow(r,2)*(40*pow(M,2) - 2*(21 + 2*pow(E,2))*M*r + 11*pow(r,2))) - 2*pow(a,2)*(12*pow(L,2)*M + r*((7*M - 4*r)*r + 2*pow(E,2)*M*(-10*M + 7*r)))))/(24.*pow(pow(a,2) + r*(-2*M + r),2));\n\tA304 = (-8*(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2)))*(4*pow(a,3)*E*L*M + pow(a,4)*r + 12*a*E*L*M*pow(r,2) + pow(r,2)*(pow(r,2)*(-5*M + 2*r) + pow(L,2)*(-9*M + 4*r)) + pow(a,2)*(pow(L,2)*(-4*M + r) + r*(2*pow(M,2) - 3*M*r + 3*pow(r,2)))))/(r*pow(pow(a,2) + r*(-2*M + r),2));\n\tA306 = (4*(-4*pow(a,11)*E*L*pow(M,2)*(4*(35 + 9*pow(E,2))*pow(M,2) + (127 + 54*pow(E,2))*M*r + 3*(5 + 6*pow(E,2))*pow(r,2)) - 2*a*E*L*M*pow(r,5)*(pow(r,6)*(-184*pow(M,2) + 12*(17 + 9*pow(E,2))*M*r - 57*pow(r,2)) + pow(L,2)*pow(r,4)*(212*pow(M,2) + 4*(-23 + 57*pow(E,2))*M*r - 3*pow(r,2)) + 42*pow(L,6)*(4*pow(M,2) - pow(r,2)) + 2*pow(L,4)*pow(r,2)*(486*pow(M,2) + 2*(-170 + 21*pow(E,2))*M*r + 51*pow(r,2))) - 2*pow(a,3)*E*L*M*pow(r,3)*(4*pow(L,4)*pow(r,2)*((829 + 42*pow(E,2))*pow(M,2) + 2*(-107 + 21*pow(E,2))*M*r - 84*pow(r,2)) + 42*pow(L,6)*(12*pow(M,2) - 2*M*r - pow(r,2)) + pow(r,5)*(-3444*pow(M,3) + 2*(961 - 270*pow(E,2))*pow(M,2)*r + 4*(106 + 135*pow(E,2))*M*pow(r,2) - 257*pow(r,3)) + 2*pow(L,2)*pow(r,3)*(-3230*pow(M,3) + 3*(895 + 224*pow(E,2))*pow(M,2)*r + (-109 + 384*pow(E,2))*M*pow(r,2) - 214*pow(r,3))) + pow(r,7)*(2*pow(L,6)*(2*M - r)*(54*pow(M,2) + (-71 + 42*pow(E,2))*M*r + 24*pow(r,2)) + pow(L,4)*pow(r,2)*(272*pow(M,3) + 2*(-247 + 222*pow(E,2))*pow(M,2)*r + (311 - 226*pow(E,2))*M*pow(r,2) - 66*pow(r,3)) + pow(L,2)*pow(r,4)*(220*pow(M,3) + 340*(-1 + pow(E,2))*pow(M,2)*r + (193 - 184*pow(E,2))*M*pow(r,2) - 39*pow(r,3)) + pow(r,6)*(116*pow(M,3) + 2*(-83 + 44*pow(E,2))*pow(M,2)*r + 6*(14 - 9*pow(E,2))*M*pow(r,2) - 15*pow(r,3))) + pow(a,12)*M*(3*r*(-8*pow(M,2) - 3*M*r + 2*pow(r,2)) + pow(E,2)*(280*pow(M,3) + 308*pow(M,2)*r + 72*M*pow(r,2) - 6*pow(r,3))) - 4*pow(a,9)*E*L*M*(pow(L,2)*M*(108*pow(M,2) + 2*(137 + 24*pow(E,2))*M*r + 3*(13 + 6*pow(E,2))*pow(r,2)) + r*(24*(-1 + 12*pow(E,2))*pow(M,4) + 2*(215 + 234*pow(E,2))*pow(M,3)*r + 12*(49 + 39*pow(E,2))*pow(M,2)*pow(r,2) + 9*(-5 + 18*pow(E,2))*M*pow(r,3) - 30*pow(r,4))) - pow(a,2)*pow(r,3)*(-168*pow(L,8)*pow(M,2)*(2*M - r) + pow(L,4)*pow(r,3)*(4168*pow(M,4) - 4*(1441 + 1086*pow(E,2))*pow(M,3)*r + 4*(583 + 6*pow(E,2))*pow(M,2)*pow(r,2) + (-85 + 732*pow(E,2))*M*pow(r,3) - 81*pow(r,4)) + 2*pow(L,6)*r*(240*pow(M,4) - 4*(233 + 84*pow(E,2))*pow(M,3)*r - 6*(-83 + 28*pow(E,2))*pow(M,2)*pow(r,2) + (23 + 84*pow(E,2))*M*pow(r,3) - 36*pow(r,4)) + pow(L,2)*pow(r,5)*(3640*pow(M,4) + 12*(-413 + 58*pow(E,2))*pow(M,3)*r + 2*(1091 - 1004*pow(E,2))*pow(M,2)*pow(r,2) + (-243 + 800*pow(E,2))*M*pow(r,3) - 27*pow(r,4)) + pow(r,7)*(696*pow(M,4) + 4*(-315 + 274*pow(E,2))*pow(M,3)*r + 4*(204 - 241*pow(E,2))*pow(M,2)*pow(r,2) + (-205 + 268*pow(E,2))*M*pow(r,3) + 15*pow(r,4))) - 2*pow(a,5)*E*L*M*pow(r,2)*(2*pow(L,4)*(1008*pow(M,3) + 4*(257 + 21*pow(E,2))*pow(M,2)*r + 2*(-104 + 21*pow(E,2))*M*pow(r,2) - 87*pow(r,3)) + pow(L,2)*r*(-1888*pow(M,4) + 20*(-161 + 24*pow(E,2))*pow(M,3)*r + 288*(21 + 8*pow(E,2))*pow(M,2)*pow(r,2) + 12*(-89 + 74*pow(E,2))*M*pow(r,3) - 617*pow(r,4)) - pow(r,3)*(768*pow(M,4) + 4*(823 + 378*pow(E,2))*pow(M,3)*r + 4*(-1091 + 18*pow(E,2))*pow(M,2)*pow(r,2) - 18*(-1 + 56*pow(E,2))*M*pow(r,3) + 403*pow(r,4))) + pow(a,4)*pow(r,2)*(2*pow(L,6)*M*(672*pow(M,3) + 4*(101 + 126*pow(E,2))*pow(M,2)*r + 28*(-11 + 3*pow(E,2))*M*pow(r,2) - (31 + 42*pow(E,2))*pow(r,3)) + pow(r,5)*(180*pow(M,5) + 12*(1 - 238*pow(E,2))*pow(M,4)*r - 9*(-161 + 60*pow(E,2))*pow(M,3)*pow(r,2) + 8*(-219 + 287*pow(E,2))*pow(M,2)*pow(r,3) + (421 - 538*pow(E,2))*M*pow(r,4) + 45*pow(r,5)) + pow(L,4)*r*(-1888*pow(M,5) + 8*(-239 + 180*pow(E,2))*pow(M,4)*r + 32*(163 + 293*pow(E,2))*pow(M,3)*pow(r,2) + 12*(-223 + 58*pow(E,2))*pow(M,2)*pow(r,3) + 2*(79 - 396*pow(E,2))*M*pow(r,4) + 147*pow(r,5)) + pow(L,2)*pow(r,3)*(-364*pow(M,5) - 4*(763 + 2976*pow(E,2))*pow(M,4)*r + (6129 + 4072*pow(E,2))*pow(M,3)*pow(r,2) + 4*(-965 + 831*pow(E,2))*pow(M,2)*pow(r,3) + 3*(181 - 436*pow(E,2))*M*pow(r,4) + 171*pow(r,5))) - 2*pow(a,7)*E*L*M*r*(48*pow(L,4)*M*(6*M + r) + 2*pow(L,2)*(864*pow(M,4) + 4*(163 + 84*pow(E,2))*pow(M,3)*r + (1271 + 624*pow(E,2))*pow(M,2)*pow(r,2) + 2*(-101 + 96*pow(E,2))*M*pow(r,3) - 96*pow(r,4)) - r*(896*pow(M,5) + 1840*pow(M,4)*r + 36*(17 + 2*pow(E,2))*pow(M,3)*pow(r,2) - 4*(769 + 324*pow(E,2))*pow(M,2)*pow(r,3) + 2*(179 - 432*pow(E,2))*M*pow(r,4) + 263*pow(r,5))) + pow(a,6)*r*(6*pow(L,6)*M*(32*pow(M,2) + 2*M*r + pow(r,2)) + 4*pow(L,4)*M*(288*pow(M,4) + 4*(23 + 252*pow(E,2))*pow(M,3)*r + (389 + 1450*pow(E,2))*pow(M,2)*pow(r,2) + 2*(-125 + 69*pow(E,2))*M*pow(r,3) - (6 + 73*pow(E,2))*pow(r,4)) + pow(r,3)*(184*pow(M,6) - 4*(11 + 268*pow(E,2))*pow(M,5)*r - 6*(37 + 632*pow(E,2))*pow(M,4)*pow(r,2) + (77 + 2080*pow(E,2))*pow(M,3)*pow(r,3) + (-1231 + 2360*pow(E,2))*pow(M,2)*pow(r,4) + 3*(203 - 184*pow(E,2))*M*pow(r,5) + 75*pow(r,6)) + pow(L,2)*r*(-896*pow(M,6) - 32*(38 + 59*pow(E,2))*pow(M,5)*r - 16*(21 + 292*pow(E,2))*pow(M,4)*pow(r,2) + 2*(1643 + 4488*pow(E,2))*pow(M,3)*pow(r,3) + (-1991 + 2372*pow(E,2))*pow(M,2)*pow(r,4) + (571 - 964*pow(E,2))*M*pow(r,5) + 105*pow(r,6))) + pow(a,10)*M*(pow(L,2)*(8*(35 + 54*pow(E,2))*pow(M,3) + 8*(25 + 109*pow(E,2))*pow(M,2)*r + 3*(-7 + 76*pow(E,2))*M*pow(r,2) + 6*(3 - 2*pow(E,2))*pow(r,3)) + r*(r*(134*pow(M,3) - 21*pow(M,2)*r - 69*M*pow(r,2) + 58*pow(r,3)) + pow(E,2)*(-48*pow(M,4) + 1072*pow(M,3)*r + 1704*pow(M,2)*pow(r,2) + 452*M*pow(r,3) - 76*pow(r,4)))) + pow(a,8)*(2*pow(L,4)*M*(72*pow(M,3) + 4*(55 + 72*pow(E,2))*pow(M,2)*r + 78*pow(E,2)*M*pow(r,2) - 3*(-3 + pow(E,2))*pow(r,3)) + pow(L,2)*M*r*(48*(-1 + 72*pow(E,2))*pow(M,4) + 8*(81 + 514*pow(E,2))*pow(M,3)*r + (869 + 5400*pow(E,2))*pow(M,2)*pow(r,2) + 2*(-285 + 472*pow(E,2))*M*pow(r,3) + 4*(24 - 71*pow(E,2))*pow(r,4)) + pow(r,2)*(r*(-264*pow(M,5) + 92*pow(M,4)*r + 135*pow(M,3)*pow(r,2) - 189*pow(M,2)*pow(r,3) + 361*M*pow(r,4) + 30*pow(r,5)) - 2*pow(E,2)*M*(448*pow(M,5) + 1232*pow(M,4)*r + 680*pow(M,3)*pow(r,2) - 1460*pow(M,2)*pow(r,3) - 660*M*pow(r,4) + 149*pow(r,5))))))/(3.*pow(r,7)*pow(pow(a,2) + r*(-2*M + r),3));\n\tA322 = (-4*pow(r,2)*(2*pow(a,3)*E*L*M + 6*a*E*L*M*pow(r,2) + pow(a,4)*(3*M + 2*r) + pow(r,2)*(pow(r,2)*(-5*M + 2*r) + pow(L,2)*(-7*M + 3*r)) - 2*pow(a,2)*(pow(L,2)*(M - r) + r*(2*pow(M,2) + M*r - 2*pow(r,2)))))/pow(pow(a,2) + r*(-2*M + r),2);\n\tA324 = (4*pow(a,9)*E*L*M*(2*(-113 + 9*pow(E,2))*pow(M,2) - 57*M*r + 3*pow(r,2)) - 4*a*E*L*M*pow(r,4)*(pow(L,4)*(240*pow(M,2) - 22*M*r - 51*pow(r,2)) + pow(L,2)*pow(r,2)*(314*pow(M,2) + (-83 + 114*pow(E,2))*M*r - 42*pow(r,2)) + 3*pow(r,4)*(-84*pow(M,2) + 4*(26 + 9*pow(E,2))*M*r - 33*pow(r,2))) + pow(a,10)*M*(-21*r*(7*M + 2*r) + 4*pow(E,2)*(113*pow(M,2) + 81*M*r + 9*pow(r,2))) - 4*pow(a,7)*E*L*M*(3*pow(L,2)*(-18*pow(M,2) + 8*M*r + pow(r,2)) + r*(4*(31 + 108*pow(E,2))*pow(M,3) + 9*(109 + 30*pow(E,2))*pow(M,2)*r + (37 + 108*pow(E,2))*M*pow(r,2) - 69*pow(r,3))) - 4*pow(a,3)*E*L*M*pow(r,2)*(pow(L,4)*(612*pow(M,2) - 92*M*r - 45*pow(r,2)) + pow(r,3)*(-2250*pow(M,3) + (955 - 54*pow(E,2))*pow(M,2)*r + 9*(49 + 36*pow(E,2))*M*pow(r,2) - 211*pow(r,3)) + pow(L,2)*r*(16*pow(M,3) + 2*(409 + 114*pow(E,2))*pow(M,2)*r + 2*(-185 + 108*pow(E,2))*M*pow(r,2) - 45*pow(r,3))) + pow(r,4)*(2*pow(L,6)*M*(12*pow(M,2) - 16*M*r + 5*pow(r,2)) + pow(L,2)*pow(r,4)*(304*pow(M,3) + 2*(-243 + 340*pow(E,2))*pow(M,2)*r + (257 - 368*pow(E,2))*M*pow(r,2) - 45*pow(r,3)) + 2*pow(L,4)*pow(r,2)*(128*pow(M,3) + 2*(-113 + 111*pow(E,2))*pow(M,2)*r + (129 - 113*pow(E,2))*M*pow(r,2) - 24*pow(r,3)) + 3*pow(r,6)*(116*pow(M,3) + 2*(-85 + 44*pow(E,2))*pow(M,2)*r + 2*(41 - 27*pow(E,2))*M*pow(r,2) - 13*pow(r,3))) - 4*pow(a,5)*E*L*M*r*(6*pow(L,4)*r + pow(L,2)*(1296*pow(M,3) + 6*(47 + 34*pow(E,2))*pow(M,2)*r + (-371 + 102*pow(E,2))*M*pow(r,2) - 60*pow(r,3)) + r*(-1008*pow(M,4) + 2*(-641 + 216*pow(E,2))*pow(M,3)*r + 6*(335 + 63*pow(E,2))*pow(M,2)*pow(r,2) + (209 + 324*pow(E,2))*M*pow(r,3) - 178*pow(r,4))) + pow(a,2)*pow(r,2)*(2*pow(L,6)*M*(408*pow(M,2) - 194*M*r - 5*pow(r,2)) - 8*pow(L,4)*M*r*(104*pow(M,3) - 3*(73 + 77*pow(E,2))*pow(M,2)*r + (115 - 101*pow(E,2))*M*pow(r,2) + 17*(-1 + 3*pow(E,2))*pow(r,3)) - 3*pow(r,5)*(464*pow(M,4) + 4*(-187 + 194*pow(E,2))*pow(M,3)*r - 8*(-66 + 83*pow(E,2))*pow(M,2)*pow(r,2) + 2*(-83 + 98*pow(E,2))*M*pow(r,3) + 15*pow(r,4)) + pow(L,2)*pow(r,3)*(-4696*pow(M,4) + 8*(672 + 127*pow(E,2))*pow(M,3)*r + 6*(-347 + 324*pow(E,2))*pow(M,2)*pow(r,2) - 3*(-87 + 352*pow(E,2))*M*pow(r,3) + 15*pow(r,4))) + pow(a,4)*r*(12*pow(L,6)*M*r + 3*pow(r,3)*(60*pow(M,5) - 4*(23 + 326*pow(E,2))*pow(M,4)*r + (751 - 436*pow(E,2))*pow(M,3)*pow(r,2) + 2*(-387 + 586*pow(E,2))*pow(M,2)*pow(r,3) - 4*(-50 + 63*pow(E,2))*M*pow(r,4) + 19*pow(r,5)) + 2*pow(L,2)*r*(-1008*pow(M,5) + 4*(-227 + 328*pow(E,2))*pow(M,4)*r + 2*(1325 + 758*pow(E,2))*pow(M,3)*pow(r,2) + 14*(-95 + 6*pow(E,2))*pow(M,2)*pow(r,3) + 3*(39 - 160*pow(E,2))*M*pow(r,4) + 69*pow(r,5)) + 2*pow(L,4)*(864*pow(M,4) + 2*(-193 + 107*pow(E,2))*pow(M,2)*pow(r,2) + 17*(1 - 5*pow(E,2))*M*pow(r,3) + 24*pow(r,4) + 12*pow(M,3)*(r + 102*pow(E,2)*r))) + pow(a,8)*(2*pow(L,2)*M*((226 - 108*pow(E,2))*pow(M,2) + 24*(-2 + pow(E,2))*M*r + 3*(-15 + 8*pow(E,2))*pow(r,2)) + r*(2*pow(E,2)*M*(124*pow(M,3) + 982*pow(M,2)*r + 686*M*pow(r,2) + 3*pow(r,3)) + 3*r*(179*pow(M,3) - 70*pow(M,2)*r + 30*M*pow(r,2) + 10*pow(r,3)))) + pow(a,6)*(-12*pow(L,4)*M*(6*pow(M,2) - 4*M*r - (-3 + pow(E,2))*pow(r,2)) + 2*pow(L,2)*r*(4*(31 + 648*pow(E,2))*pow(M,4) + 28*(35 + 39*pow(E,2))*pow(M,3)*r - (361 + 140*pow(E,2))*pow(M,2)*pow(r,2) + (43 - 112*pow(E,2))*M*pow(r,3) + 39*pow(r,4)) + pow(r,2)*(3*r*(-192*pow(M,4) + 210*pow(M,3)*r - 381*pow(M,2)*pow(r,2) + 160*M*pow(r,3) + 31*pow(r,4)) - 4*pow(E,2)*M*(504*pow(M,4) + 828*pow(M,3)*r - 433*pow(M,2)*pow(r,2) - 709*M*pow(r,3) + 90*pow(r,4)))))/(3.*pow(r,4)*pow(pow(a,2) + r*(-2*M + r),3));\n\tA340 = -(pow(r,5)*(3*pow(a,2) + r*(-5*M + 2*r)))/(2.*pow(pow(a,2) + r*(-2*M + r),2));\n\tA342 = (24*pow(a,7)*E*L*M*((-25 + 3*pow(E,2))*M - 7*r) + 3*pow(a,8)*(r*(8*M + 9*r) + 2*pow(E,2)*M*(50*M + 19*r)) - 4*a*E*L*M*pow(r,3)*(3*pow(r,2)*(-76*pow(M,2) + (103 + 18*pow(E,2))*M*r - 36*pow(r,2)) + pow(L,2)*(208*pow(M,2) - 64*M*r - 27*pow(r,2))) - 12*pow(a,5)*E*L*M*(pow(L,2)*(-18*M + 8*r) + r*((4 + 48*pow(E,2))*pow(M,2) + (71 - 6*pow(E,2))*M*r + 20*pow(r,2))) - 12*pow(a,3)*E*L*M*r*(pow(L,2)*(144*pow(M,2) - 64*M*r - pow(r,2)) + 2*r*(-88*pow(M,3) + 2*(-5 + 18*pow(E,2))*pow(M,2)*r + 9*(4 + pow(E,2))*M*pow(r,2) - 13*pow(r,3))) + pow(r,3)*(-8*pow(L,4)*M*(2*pow(M,2) - 3*M*r + pow(r,2)) + pow(r,4)*(348*pow(M,3) + 8*(-64 + 33*pow(E,2))*pow(M,2)*r + (245 - 162*pow(E,2))*M*pow(r,2) - 38*pow(r,3)) + pow(L,2)*pow(r,2)*(-52*pow(M,3) + 4*(19 + 85*pow(E,2))*pow(M,2)*r - (33 + 184*pow(E,2))*M*pow(r,2) + 4*pow(r,3))) - pow(a,2)*r*(-12*pow(L,4)*M*(48*pow(M,2) - 29*M*r + pow(r,2)) + pow(L,2)*r*(1056*pow(M,4) - 16*(33 + 107*pow(E,2))*pow(M,3)*r + 4*(-35 + 16*pow(E,2))*pow(M,2)*pow(r,2) + 4*(9 + 64*pow(E,2))*M*pow(r,3) + 25*pow(r,4)) + pow(r,3)*(696*pow(M,4) + 8*(-103 + 171*pow(E,2))*pow(M,3)*r + 4*(166 - 273*pow(E,2))*pow(M,2)*pow(r,2) + 3*(-111 + 124*pow(E,2))*M*pow(r,3) + 57*pow(r,4))) + pow(a,6)*(pow(L,2)*((300 - 216*pow(E,2))*pow(M,2) + 6*(9 + 8*pow(E,2))*M*r + 27*pow(r,2)) + r*(12*pow(E,2)*M*(2*pow(M,2) + 61*M*r + 15*pow(r,2)) + r*(-312*pow(M,2) - 25*M*r + 61*pow(r,2)))) + pow(a,4)*(24*pow(L,4)*M*(-3*M + 2*r) + pow(L,2)*r*(24*(1 + 72*pow(E,2))*pow(M,3) + 12*(10 - 41*pow(E,2))*pow(M,2)*r - 3*(23 + 8*pow(E,2))*M*pow(r,2) - 2*pow(r,3)) + pow(r,2)*(-12*pow(E,2)*M*(88*pow(M,3) + 64*pow(M,2)*r - 97*M*pow(r,2) + 12*pow(r,3)) + r*(876*pow(M,3) - 512*pow(M,2)*r + 63*M*pow(r,2) + 15*pow(r,3)))))/(12.*r*pow(pow(a,2) + r*(-2*M + r),3));\n\tA360 = (pow(r,2)*(-144*pow(a,5)*E*L*M + 28*pow(a,3)*E*L*M*(4*M - 3*r)*r + 3*pow(a,6)*(24*pow(E,2)*M + 7*r) + 4*a*E*L*M*pow(r,2)*(68*pow(M,2) - 54*M*r + 15*pow(r,2)) + pow(r,2)*(-2*pow(L,2)*M*(68*pow(M,2) - 76*M*r + 21*pow(r,2)) + pow(r,2)*(116*pow(M,3) + 8*(-21 + 11*pow(E,2))*pow(M,2)*r + (83 - 54*pow(E,2))*M*pow(r,2) - 14*pow(r,3))) + pow(a,4)*(72*pow(L,2)*M + r*(r*(-53*M + 28*r) + pow(E,2)*(-56*pow(M,2) + 74*M*r))) - pow(a,2)*r*(2*pow(L,2)*M*(28*M - 5*r) + r*(r*(36*pow(M,2) - 30*M*r + 7*pow(r,2)) + 4*pow(E,2)*M*(34*pow(M,2) - 16*M*r + 13*pow(r,2))))))/(48.*pow(pow(a,2) + r*(-2*M + r),3));\n\tA402 = (12*pow(r,3)*(pow(a,2)*(2*M + r) + r*(pow(L,2) + pow(r,2))))/pow(pow(a,2) + r*(-2*M + r),2);\n\tA404 = (2*(-4*pow(a,7)*E*L*M*(48*pow(M,2) + 40*M*r + 3*pow(r,2)) - 4*pow(a,5)*E*L*M*r*(-96*pow(M,3) - 110*pow(M,2)*r + 73*M*pow(r,2) + 8*pow(r,3) + 9*pow(L,2)*(6*M + r)) + pow(a,8)*(12*pow(E,2)*M*(8*pow(M,2) + 10*M*r + 3*pow(r,2)) - r*(62*pow(M,2) + 42*M*r + 3*pow(r,2))) + pow(r,3)*(4*pow(L,6)*M*(-2*M + r) + 3*pow(r,6)*(23*pow(M,2) + 4*(-5 + pow(E,2))*M*r + 3*pow(r,2)) + 3*pow(L,2)*pow(r,4)*(42*pow(M,2) + 4*(-9 + 2*pow(E,2))*M*r + 5*pow(r,2)) + pow(L,4)*pow(r,2)*(73*pow(M,2) + 4*(-17 + 2*pow(E,2))*M*r + 12*pow(r,2))) - 4*pow(a,3)*E*L*M*pow(r,2)*(6*pow(L,4) + pow(L,2)*(-48*pow(M,2) + 81*M*r + 9*pow(r,2)) + pow(r,2)*(-234*pow(M,2) + 50*M*r + 17*pow(r,2))) + 4*a*E*L*M*pow(r,3)*(pow(L,4)*(4*M - 6*r) + 3*(M - 4*r)*pow(r,4) + pow(L,2)*(-45*M*pow(r,2) + 6*pow(r,3))) + pow(a,2)*pow(r,2)*(12*pow(L,6)*M + 6*pow(r,4)*(-16*pow(M,3) + (26 - 36*pow(E,2))*pow(M,2)*r + (-25 + 12*pow(E,2))*M*pow(r,2) + 6*pow(r,3)) + 3*pow(L,2)*pow(r,2)*(-152*pow(M,3) + 4*(35 + 9*pow(E,2))*pow(M,2)*r + 2*(-33 + 16*pow(E,2))*M*pow(r,2) + 19*pow(r,3)) + pow(L,4)*(-96*pow(M,3) + 4*(39 - 2*pow(E,2))*pow(M,2)*r + 10*(-5 + 2*pow(E,2))*M*pow(r,2) + 24*pow(r,3))) + pow(a,4)*r*(3*pow(L,4)*(36*pow(M,2) + 4*(-1 + pow(E,2))*M*r - pow(r,2)) - 2*pow(L,2)*(96*pow(M,4) + 4*(19 + 12*pow(E,2))*pow(M,3)*r - (143 + 84*pow(E,2))*pow(M,2)*pow(r,2) + 15*(3 - 4*pow(E,2))*M*pow(r,3) - 18*pow(r,4)) - 3*pow(r,2)*(36*pow(M,4) + 4*(-5 + 48*pow(E,2))*pow(M,3)*r + 9*(-5 + 8*pow(E,2))*pow(M,2)*pow(r,2) + 2*(19 - 24*pow(E,2))*M*pow(r,3) - 14*pow(r,4))) + 2*pow(a,6)*(pow(L,2)*(48*pow(M,3) + 2*(10 + 27*pow(E,2))*pow(M,2)*r + 3*(-11 + 8*pow(E,2))*M*pow(r,2) - 3*pow(r,3)) + r*(-12*pow(E,2)*M*(8*pow(M,3) + 12*pow(M,2)*r - 5*M*pow(r,2) - 5*pow(r,3)) + r*(74*pow(M,3) + 17*pow(M,2)*r - 33*M*pow(r,2) + 6*pow(r,3))))))/(3.*pow(r,3)*pow(pow(a,2) + r*(-2*M + r),3));\n\tA420 = (3*pow(r,6))/pow(pow(a,2) + r*(-2*M + r),2);\n\tA422 = (2*pow(a,5)*E*L*M*(-8*M + r) - 2*pow(a,3)*E*L*M*r*(4*pow(L,2) - 8*pow(M,2) + M*r - 7*pow(r,2)) - 2*a*E*L*M*pow(r,3)*(4*pow(L,2) + r*(3*M + 4*r)) + pow(a,6)*M*(-7*r + pow(E,2)*(8*M + 6*r)) + pow(r,4)*(pow(r,2)*(23*pow(M,2) + 2*(-11 + 2*pow(E,2))*M*r + 4*pow(r,2)) + pow(L,2)*(25*pow(M,2) + (-23 + 4*pow(E,2))*M*r + 4*pow(r,2))) + pow(a,2)*r*(4*pow(L,4)*M + pow(r,2)*(-16*pow(M,3) - 4*(-5 + 8*pow(E,2))*pow(M,2)*r + 7*(-5 + 2*pow(E,2))*M*pow(r,2) + 14*pow(r,3)) + pow(L,2)*(-8*pow(M,3) + 10*pow(M,2)*r + (-23 + 8*pow(E,2))*M*pow(r,2) + 15*pow(r,3))) + pow(a,4)*(4*pow(L,2)*M*(2*M + (-2 + pow(E,2))*r) + r*(-8*pow(E,2)*M*(pow(M,2) + M*r - 2*pow(r,2)) + r*(17*pow(M,2) - 8*M*r + 10*pow(r,2)))))/pow(pow(a,2) + r*(-2*M + r),3);\n\tA440 = (pow(r,4)*(9*pow(a,4) - 48*a*E*L*pow(M,2) + 12*pow(L,2)*M*(2*M - r) + pow(r,2)*(69*pow(M,2) + 2*(-31 + 6*pow(E,2))*M*r + 10*pow(r,2)) + 2*pow(a,2)*(6*pow(E,2)*M*(2*M + r) + r*(-30*M + 17*r))))/(24.*pow(pow(a,2) + r*(-2*M + r),3));\n\tA502 = (-2*pow(r,2)*(2*pow(a,3)*E*L*M + 6*a*E*L*M*pow(r,2) + pow(a,4)*(3*M + 2*r) + pow(r,2)*(pow(r,2)*(-4*M + r) + pow(L,2)*(-6*M + 2*r)) + pow(a,2)*(-2*pow(L,2)*(M - r) + r*(-2*pow(M,2) - 3*M*r + 3*pow(r,2)))))/pow(pow(a,2) + r*(-2*M + r),3);\n\tA504 = (-2*pow(a,9)*E*L*M*(4*(49 + 27*pow(E,2))*pow(M,2) + 6*(-7 + 12*pow(E,2))*M*r - 9*pow(r,2)) + pow(a,10)*M*(3*r*(-10*M + 7*r) + 2*pow(E,2)*(98*pow(M,2) + 30*M*r - 15*pow(r,2))) - 2*a*E*L*M*pow(r,4)*(pow(L,2)*pow(r,2)*(-725*pow(M,2) + 8*(172 + 21*pow(E,2))*M*r - 570*pow(r,2)) + 3*pow(r,4)*(-135*pow(M,2) + 4*(49 + 18*pow(E,2))*M*r - 82*pow(r,2)) + 16*pow(L,4)*(2*pow(M,2) + 15*M*r - 9*pow(r,2))) - 2*pow(a,7)*E*L*M*(pow(L,2)*(324*pow(M,2) + 12*(5 + 3*pow(E,2))*M*r + 9*pow(r,2)) + 2*r*(16*(2 + 9*pow(E,2))*pow(M,3) + 6*(94 + 57*pow(E,2))*pow(M,2)*r + 2*(-65 + 126*pow(E,2))*M*pow(r,2) - 39*pow(r,3))) + pow(r,4)*(-2*pow(L,6)*M*(44*pow(M,2) - 52*M*r + 15*pow(r,2)) + 3*M*pow(r,6)*(52*pow(M,2) + (-65 + 44*pow(E,2))*M*r + (23 - 30*pow(E,2))*pow(r,2)) + 2*pow(L,4)*pow(r,2)*(-153*pow(M,3) + (271 + 140*pow(E,2))*pow(M,2)*r - 2*(82 + 39*pow(E,2))*M*pow(r,2) + 36*pow(r,3)) + pow(L,2)*pow(r,4)*(34*pow(M,3) + (51 + 428*pow(E,2))*pow(M,2)*r - (109 + 248*pow(E,2))*M*pow(r,2) + 48*pow(r,3))) - 2*pow(a,3)*E*L*M*pow(r,2)*(2*pow(L,4)*(252*pow(M,2) + 116*M*r - 63*pow(r,2)) + pow(L,2)*r*(-784*pow(M,3) + 3*(219 + 40*pow(E,2))*pow(M,2)*r + 18*(39 + 22*pow(E,2))*M*pow(r,2) - 168*pow(r,3)) - 2*pow(r,3)*(765*pow(M,3) + 2*(8 + 135*pow(E,2))*pow(M,2)*r - 3*(89 + 132*pow(E,2))*M*pow(r,2) + 119*pow(r,3))) - 2*pow(a,5)*E*L*M*r*(18*pow(L,4)*(6*M + r) + pow(L,2)*(864*pow(M,3) + 84*(15 + 2*pow(E,2))*pow(M,2)*r + 2*(-145 + 132*pow(E,2))*M*pow(r,2) - 123*pow(r,3)) + r*(-576*pow(M,4) - 1390*pow(M,3)*r + 3*(667 + 108*pow(E,2))*pow(M,2)*pow(r,2) + 4*(89 + 252*pow(E,2))*M*pow(r,3) - 61*pow(r,4))) - pow(a,2)*pow(r,2)*(4*pow(L,6)*M*(-84*pow(M,2) + 8*M*r + 23*pow(r,2)) + 3*pow(r,5)*(210*pow(M,4) + (-355 + 484*pow(E,2))*pow(M,3)*r + 12*(23 - 33*pow(E,2))*pow(M,2)*pow(r,2) + 2*(-62 + 65*pow(E,2))*M*pow(r,3) + 21*pow(r,4)) + 2*pow(L,4)*r*(392*pow(M,4) - (409 + 196*pow(E,2))*pow(M,3)*r - (39 + 584*pow(E,2))*pow(M,2)*pow(r,2) + (-41 + 167*pow(E,2))*M*pow(r,3) + 84*pow(r,4)) + pow(L,2)*pow(r,3)*(1618*pow(M,4) + (-1559 + 2272*pow(E,2))*pow(M,3)*r + (683 - 4036*pow(E,2))*pow(M,2)*pow(r,2) + (-413 + 848*pow(E,2))*M*pow(r,3) + 165*pow(r,4))) + pow(a,4)*r*(18*pow(L,6)*M*(4*M + r) + 8*pow(L,4)*(72*pow(M,4) + 18*(4 + 7*pow(E,2))*pow(M,3)*r + (-91 + 128*pow(E,2))*pow(M,2)*pow(r,2) - 4*(-4 + 5*pow(E,2))*M*pow(r,3) + 9*pow(r,4)) + 3*pow(r,3)*(44*pow(M,5) - 56*(-1 + 8*pow(E,2))*pow(M,4)*r + 2*(233 - 192*pow(E,2))*pow(M,3)*pow(r,2) + (-499 + 736*pow(E,2))*pow(M,2)*pow(r,3) + (151 - 220*pow(E,2))*M*pow(r,4) - 27*pow(r,5)) - pow(L,2)*r*(576*pow(M,5) + 4*(263 + 196*pow(E,2))*pow(M,4)*r - 13*(215 + 88*pow(E,2))*pow(M,3)*pow(r,2) - 6*(-313 + 524*pow(E,2))*pow(M,2)*pow(r,3) + 4*(-82 + 241*pow(E,2))*M*pow(r,4) + 96*pow(r,5))) + pow(a,8)*(pow(L,2)*M*(4*(49 + 162*pow(E,2))*pow(M,2) + 12*(-12 + 23*pow(E,2))*M*r + 3*(11 - 4*pow(E,2))*pow(r,2)) + r*(2*pow(E,2)*M*(32*pow(M,3) + 608*pow(M,2)*r + 286*M*pow(r,2) - 105*pow(r,3)) + 3*r*(24*pow(M,3) + 2*pow(M,2)*r + 130*M*pow(r,2) + 15*pow(r,3)))) + pow(a,6)*(6*pow(L,4)*M*(36*pow(M,2) + 2*(-1 + 18*pow(E,2))*M*r + (5 + 3*pow(E,2))*pow(r,2)) + pow(L,2)*r*(64*(1 + 27*pow(E,2))*pow(M,4) + 16*(65 + 207*pow(E,2))*pow(M,3)*r + 68*(-14 + 17*pow(E,2))*pow(M,2)*pow(r,2) + (595 - 376*pow(E,2))*M*pow(r,3) + 117*pow(r,4)) + pow(r,2)*(3*r*(-58*pow(M,4) - 45*pow(M,3)*r - 420*pow(M,2)*pow(r,2) + 173*M*pow(r,3) + 9*pow(r,4)) - 4*pow(E,2)*M*(144*pow(M,4) + 432*pow(M,3)*r - 218*pow(M,2)*pow(r,2) - 416*M*pow(r,3) + 135*pow(r,4)))))/(3.*pow(r,4)*pow(pow(a,2) + r*(-2*M + r),4));\n\tA520 = -(pow(r,5)*(3*pow(a,2) + r*(-4*M + r)))/(2.*pow(pow(a,2) + r*(-2*M + r),3));\n\tA522 = (-6*pow(a,7)*E*L*M*(4*(14 + 3*pow(E,2))*M + 23*r) + 3*pow(a,8)*(2*pow(E,2)*M*(28*M - r) + r*(43*M + 9*r)) - 2*a*E*L*M*pow(r,3)*(pow(L,2)*(64*pow(M,2) + 344*M*r - 222*pow(r,2)) + 3*pow(r,2)*(-199*pow(M,2) + 4*(79 + 18*pow(E,2))*M*r - 134*pow(r,2))) - 6*pow(a,5)*E*L*M*(4*pow(L,2)*(9*M + 2*r) + r*(12*(7 + 6*pow(E,2))*pow(M,2) + 6*(17 + 16*pow(E,2))*M*r + 7*pow(r,2))) - 6*pow(a,3)*E*L*M*r*(2*pow(L,2)*(108*pow(M,2) + 52*M*r - 33*pow(r,2)) + r*(-272*pow(M,3) + (-73 + 72*pow(E,2))*pow(M,2)*r + 2*(103 + 78*pow(E,2))*M*pow(r,2) - 116*pow(r,3))) + pow(r,3)*(-4*pow(L,4)*M*(38*pow(M,2) - 45*M*r + 13*pow(r,2)) + 6*pow(r,4)*(52*pow(M,3) + (-63 + 44*pow(E,2))*pow(M,2)*r - 6*(-3 + 5*pow(E,2))*M*pow(r,2) + 2*pow(r,3)) + pow(L,2)*pow(r,2)*(-158*pow(M,3) + (369 + 428*pow(E,2))*pow(M,2)*r - 4*(73 + 62*pow(E,2))*M*pow(r,2) + 84*pow(r,3))) + pow(a,2)*r*(24*pow(L,4)*M*(18*pow(M,2) + M*r - 7*pow(r,2)) + pow(L,2)*r*(-816*pow(M,4) + 2*(213 + 356*pow(E,2))*pow(M,3)*r + (103 + 1444*pow(E,2))*pow(M,2)*pow(r,2) + 4*(32 - 125*pow(E,2))*M*pow(r,3) - 156*pow(r,4)) - 3*pow(r,3)*(210*pow(M,4) + (-295 + 548*pow(E,2))*pow(M,3)*r + (313 - 488*pow(E,2))*pow(M,2)*pow(r,2) + 2*(-97 + 95*pow(E,2))*M*pow(r,3) + 40*pow(r,4))) + 3*pow(a,6)*(pow(L,2)*(8*(7 + 9*pow(E,2))*pow(M,2) + 8*(6 + pow(E,2))*M*r + 9*pow(r,2)) + r*(2*pow(E,2)*M*(42*pow(M,2) + 148*M*r - 37*pow(r,2)) + r*(-240*pow(M,2) + 87*M*r + 11*pow(r,2)))) + 3*pow(a,4)*(8*pow(L,4)*M*(3*M + r) + pow(L,2)*r*(12*(7 + 36*pow(E,2))*pow(M,3) + 4*(-23 + 98*pow(E,2))*pow(M,2)*r + 4*(3 - 19*pow(E,2))*M*pow(r,2) - 13*pow(r,3)) - pow(r,2)*(r*(-385*pow(M,3) + 317*pow(M,2)*r - 120*M*pow(r,2) + 42*pow(r,3)) + 2*pow(E,2)*M*(136*pow(M,3) + 144*pow(M,2)*r - 312*M*pow(r,2) + 101*pow(r,3)))))/(12.*r*pow(pow(a,2) + r*(-2*M + r),4));\n\tA540 = (pow(r,2)*(-72*pow(a,5)*E*L*M + 9*pow(a,6)*(4*pow(E,2)*M + r) + 12*pow(a,3)*E*L*M*r*(-12*M + 11*r) + 12*a*E*L*M*pow(r,2)*(32*pow(M,2) - 42*M*r + 17*pow(r,2)) + pow(a,4)*(36*pow(L,2)*M + r*(18*pow(E,2)*M*(4*M - 3*r) + (24*M - 13*r)*r)) + pow(r,2)*(-6*pow(L,2)*M*(32*pow(M,2) - 38*M*r + 11*pow(r,2)) + pow(r,2)*(156*pow(M,3) + (-193 + 132*pow(E,2))*pow(M,2)*r + 2*(32 - 45*pow(E,2))*M*pow(r,2) + 2*pow(r,3))) - pow(a,2)*r*(pow(L,2)*(-72*pow(M,2) + 78*M*r) + r*(12*pow(E,2)*M*(16*pow(M,2) - 23*M*r + 15*pow(r,2)) + r*(183*pow(M,2) - 196*M*r + 62*pow(r,2))))))/(48.*pow(pow(a,2) + r*(-2*M + r),4));\n\tA600 = pow(r,6)/pow(pow(a,2) + r*(-2*M + r),3);\n\tA602 = (-24*pow(a,5)*E*L*M*(5*M + r) - 16*pow(a,3)*E*L*M*r*(3*pow(L,2) - 8*pow(M,2) + 2*M*r + 3*pow(r,2)) + 8*a*E*L*M*pow(r,2)*(pow(L,2)*(4*M - 6*r) + (11*M - 15*r)*pow(r,2)) + 3*pow(a,6)*(r*(-6*M + r) + 4*pow(E,2)*M*(5*M + 3*r)) + pow(r,2)*(8*pow(L,4)*M*(-2*M + r) + M*pow(r,4)*(43*M + 4*(-8 + 5*pow(E,2))*r) + pow(L,2)*pow(r,2)*(3*pow(M,2) + 4*(3 + 4*pow(E,2))*M*r - 12*pow(r,2))) + pow(a,2)*r*(24*pow(L,4)*M + pow(r,2)*(-34*pow(M,3) + (39 - 100*pow(E,2))*pow(M,2)*r + 2*(-47 + 38*pow(E,2))*M*pow(r,2) + 36*pow(r,3)) + pow(L,2)*(-64*pow(M,3) - 8*(-3 + 2*pow(E,2))*pow(M,2)*r + 10*(-3 + 4*pow(E,2))*M*pow(r,2) + 48*pow(r,3))) + pow(a,4)*(3*pow(L,2)*(20*pow(M,2) + 4*(-1 + 2*pow(E,2))*M*r + pow(r,2)) + r*(r*(32*pow(M,2) - 14*M*r + 39*pow(r,2)) + pow(E,2)*(-64*pow(M,3) + 8*pow(M,2)*r + 92*M*pow(r,2)))))/(6.*pow(pow(a,2) + r*(-2*M + r),4));\n\tA620 = (pow(r,3)*(-48*pow(a,3)*E*L*M + 16*a*E*L*M*(M - 3*r)*r + 3*pow(a,4)*(8*pow(E,2)*M + 5*r) + M*r*(pow(L,2)*(-8*M + 4*r) + pow(r,2)*(43*M + 4*(-8 + 5*pow(E,2))*r)) + pow(a,2)*(24*pow(L,2)*M + 2*r*(r*(-31*M + 18*r) + pow(E,2)*(-4*pow(M,2) + 22*M*r)))))/(24.*pow(pow(a,2) + r*(-2*M + r),4));\n\tA700 = (pow(r,5)*(-pow(a,2) + M*r))/(2.*pow(pow(a,2) + r*(-2*M + r),4));\n\tA702 = (-12*pow(a,7)*E*L*M*((2 + 6*pow(E,2))*M + 3*r) + 3*pow(a,8)*(2*pow(E,2)*M*(2*M - 7*r) + r*(26*M + 3*r)) - 4*a*E*L*M*pow(r,3)*(pow(L,2)*(-52*pow(M,2) + 144*M*r - 69*pow(r,2)) + pow(r,2)*(-85*pow(M,2) + 2*(61 + 27*pow(E,2))*M*r - 51*pow(r,2))) - 8*pow(a,5)*E*L*M*(27*pow(L,2)*M + r*((35 + 12*pow(E,2))*pow(M,2) + 3*(8 + 17*pow(E,2))*M*r - 9*pow(r,2))) + M*pow(r,3)*(-4*pow(L,4)*(26*pow(M,2) - 31*M*r + 9*pow(r,2)) + pow(L,2)*pow(r,2)*(-25*pow(M,2) + 4*(3 + 44*pow(E,2))*M*r + 2*(5 - 54*pow(E,2))*pow(r,2)) + pow(r,4)*(91*pow(M,2) + 2*(-59 + 40*pow(E,2))*M*r + 2*(23 - 29*pow(E,2))*pow(r,2))) - 4*pow(a,3)*E*L*M*r*(pow(L,2)*(72*pow(M,2) + 172*M*r - 69*pow(r,2)) + r*(-80*pow(M,3) - 65*pow(M,2)*r + 2*(64 + 69*pow(E,2))*M*pow(r,2) - 84*pow(r,3))) + pow(a,2)*r*(4*pow(L,4)*M*(24*pow(M,2) + 35*M*r - 31*pow(r,2)) + pow(L,2)*r*(-160*pow(M,4) - 52*(-1 + 2*pow(E,2))*pow(M,3)*r + (27 + 1004*pow(E,2))*pow(M,2)*pow(r,2) + 2*(51 - 130*pow(E,2))*M*pow(r,3) - 84*pow(r,4)) - pow(r,3)*(166*pow(M,4) + (-289 + 532*pow(E,2))*pow(M,3)*r + (371 - 472*pow(E,2))*pow(M,2)*pow(r,2) + 16*(-17 + 14*pow(E,2))*M*pow(r,3) + 72*pow(r,4))) + pow(a,6)*(3*pow(L,2)*((4 + 72*pow(E,2))*pow(M,2) + 26*M*r + 3*pow(r,2)) + r*(4*pow(E,2)*M*(35*pow(M,2) + 88*M*r - 48*pow(r,2)) + r*(-322*pow(M,2) + 147*M*r - 9*pow(r,2)))) + pow(a,4)*(72*pow(L,4)*pow(M,2) + pow(L,2)*r*(4*(35 + 72*pow(E,2))*pow(M,3) + 4*(-40 + 239*pow(E,2))*pow(M,2)*r + (23 - 152*pow(E,2))*M*pow(r,2) - 36*pow(r,3)) - pow(r,2)*(4*pow(E,2)*M*(40*pow(M,3) + 78*pow(M,2)*r - 195*M*pow(r,2) + 79*pow(r,3)) + r*(-376*pow(M,3) + 365*pow(M,2)*r - 205*M*pow(r,2) + 90*pow(r,3)))))/(12.*r*pow(pow(a,2) + r*(-2*M + r),5));\n\tA720 = -(pow(r,2)*(-48*pow(a,5)*E*L*M + 4*pow(a,3)*E*L*M*(68*M - 57*r)*r + 3*pow(a,6)*(8*pow(E,2)*M + 3*r) - 4*a*E*L*M*pow(r,2)*(52*pow(M,2) - 98*M*r + 45*pow(r,2)) + M*pow(r,2)*(2*pow(L,2)*(52*pow(M,2) - 64*M*r + 19*pow(r,2)) + pow(r,2)*(-91*pow(M,2) + 2*(59 - 40*pow(E,2))*M*r + 2*(-23 + 29*pow(E,2))*pow(r,2))) + pow(a,4)*(24*pow(L,2)*M + r*(-2*pow(E,2)*M*(68*M - 65*r) + r*(-79*M + 42*r))) + pow(a,2)*r*(2*pow(L,2)*M*(-68*M + 49*r) + r*(4*pow(E,2)*M*(26*pow(M,2) - 66*M*r + 41*pow(r,2)) + r*(187*pow(M,2) - 212*M*r + 72*pow(r,2))))))/(48.*pow(pow(a,2) + r*(-2*M + r),5));\n\tA800 = (pow(r,3)*(-12*pow(a,3)*E*L*M + 4*a*E*L*M*(2*M - 3*r)*r + 3*pow(a,4)*(2*pow(E,2)*M + r) + M*r*(pow(L,2)*(-4*M + 2*r) + pow(r,2)*(5*M + 4*(-1 + pow(E,2))*r)) + 2*pow(a,2)*(3*pow(L,2)*M + r*(r*(-5*M + 3*r) + pow(E,2)*M*(-2*M + 5*r)))))/(12.*pow(pow(a,2) + r*(-2*M + r),5));\n\tA900 = -(pow(r,2)*(-18*pow(a,5)*E*L*M + 2*pow(a,3)*E*L*M*(26*M - 21*r)*r + 3*pow(a,6)*(3*pow(E,2)*M + r) - 8*a*E*L*M*pow(r,2)*(2*pow(M,2) - 6*M*r + 3*pow(r,2)) + M*pow(r,2)*(pow(L,2)*(8*pow(M,2) - 10*M*r + 3*pow(r,2)) + 2*pow(r,2)*(-5*pow(M,2) + (7 - 4*pow(E,2))*M*r + 3*(-1 + pow(E,2))*pow(r,2))) + pow(a,4)*(9*pow(L,2)*M + r*(-26*pow(E,2)*M*(M - r) + r*(-17*M + 9*r))) + pow(a,2)*r*(2*pow(L,2)*M*(-13*M + 8*r) + r*(r*(30*pow(M,2) - 35*M*r + 12*pow(r,2)) + pow(E,2)*M*(8*pow(M,2) - 38*M*r + 23*pow(r,2))))))/(24.*pow(pow(a,2) + r*(-2*M + r),6));\n  }\n\n  /* Compute aLpha, beta coefficients */\n  alpha20 = r*r/(a*a+r*(r-2*M));\n  alpha02 = r*r;\n  beta = 4.0*(L*L + r*r + a*a*(r+2*M)/r);\n}\n"
  },
  {
    "path": "external/GravitationalEffectiveSource/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GravitationalEffectiveSource)\n\nadd_library(${LIBRARY} kerr-circular.c)\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  GSL::gsl\n  )\n\ntarget_include_directories(${LIBRARY} SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})\n"
  },
  {
    "path": "external/GravitationalEffectiveSource/LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2025 Black Hole Perturbation Toolkit\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "external/GravitationalEffectiveSource/README.md",
    "content": "This is a modified version of https://github.com/barrywardell/GravitationalEffectiveSource\n(revision 4c73ea39b23bdf17bcc17dc28f980dabdd97dfec).\nThis repository is currently private (as of Jun 2025) and can be made available\nupon request by the developers of the Black Hole Perturbation Toolkit\n(https://bhptoolkit.org). A similar public repository but for the scalar (not\ngravitational) effective source with more information is available at:\nhttps://github.com/barrywardell/EffectiveSource.\nThis code is licensed under MIT. The license can be found in LICENSE.md.\n\nSummary of modifications:\n\n- Added CMakeLists.txt.\n- Renamed `effsource.h` to `effsource_gr.h`.\n- Added C++ compatibility header `effsource_gr.hpp`.\n"
  },
  {
    "path": "external/GravitationalEffectiveSource/effsource_gr.h",
    "content": "/*******************************************************************************\n * Copyright (C) 2011 Barry Wardell\n ******************************************************************************/\n\nstruct coordinate {\n  double t;\n  double r;\n  double theta;\n  double phi;\n};\n\nvoid effsource_init(double M, double a);\nvoid effsource_set_particle(struct coordinate * x, double E, double L, double ur);\n\nvoid effsource_hS(struct coordinate * x, double * hS);\nvoid effsource_hS_m(int m, struct coordinate * x, double * hS_re, double * hS_im);\n\nvoid effsource_calc(struct coordinate * x, double *hS, double *dhS_dr, double *dhS_dth,\n                    double *dhS_dph, double *dhS_dt, double *src);\nvoid effsource_calc_m(int m, struct coordinate * x, double *hS_re, double *hS_im,\n   double *dhS_dr_re, double *dhS_dr_im, double *dhS_dth_re, double *dhS_dth_im,\n   double *dhS_dph_re, double *dhS_dph_im, double *dhS_dt_re, double *dhS_dt_im,\n   double *src_re, double *src_im);\n"
  },
  {
    "path": "external/GravitationalEffectiveSource/effsource_gr.hpp",
    "content": "#pragma once\n\nextern \"C\" {\n#include <effsource_gr.h>\n}\n"
  },
  {
    "path": "external/GravitationalEffectiveSource/kerr-circular.c",
    "content": "/*******************************************************************************\n * Copyright (C) 2012-2013 Barry Wardell\n *******************************************************************************/\n\n#include <math.h>\n#include \"effsource_gr.h\"\n#include <gsl/gsl_sf_ellint.h>\n#include <stdio.h>\n\n/* The particle's coordinate location and 4-velocity */\nstatic struct coordinate xp;\n\n/* Mass of the black hole */\nstatic double M, a;\n\n/* Static variables used to store the coefficients of the series expansions */\nstatic double A00060, A00080, A00240, A00260, A00420, A00440, A00600, A00620, A00800, A01060, A01080, A01240, A01260, A01420, A01440, A01600, A01620, A01800, A02040, A02060, A02220, A02240, A02400, A02420, A02600, A03040, A03060, A03220, A03240, A03400, A03420, A03600, A04020, A04040, A04200, A04220, A04400, A05020, A05040, A05200, A05220, A05400, A06000, A06020, A06200, A07000, A07020, A07200, A08000, A09000;\nstatic double A10061, A10081, A10241, A10261, A10421, A10441, A10601, A10621, A10801, A11061, A11241, A11421, A11601, A12041, A12061, A12221, A12241, A12401, A12421, A12601, A13041, A13221, A13401, A14021, A14041, A14201, A14221, A14401, A15021, A15201, A16001, A16021, A16201, A17001, A18001;\nstatic double A20161, A20341, A20521, A20701, A21161, A21341, A21521, A21701, A22141, A22321, A22501, A23141, A23321, A23501, A24121, A24301, A25121, A25301, A26101, A27101;\nstatic double A30060, A30080, A30240, A30260, A30420, A30440, A30600, A30620, A30800, A31060, A31080, A31240, A31260, A31420, A31440, A31600, A31620, A31800, A32040, A32060, A32220, A32240, A32400, A32420, A32600, A33040, A33060, A33220, A33240, A33400, A33420, A33600, A34020, A34040, A34200, A34220, A34400, A35020, A35040, A35200, A35220, A35400, A36000, A36020, A36200, A37000, A37020, A37200, A38000, A39000;\nstatic double A40080, A40260, A40440, A40620, A40800, A41080, A41260, A41440, A41620, A41800, A42060, A42240, A42420, A42600, A43060, A43240, A43420, A43600, A44040, A44220, A44400, A45040, A45220, A45400, A46020, A46200, A47020, A47200, A48000, A49000;\nstatic double A50180, A50360, A50540, A50720, A50900, A52160, A52340, A52520, A52700, A54140, A54320, A54500, A56120, A56300, A58100;\nstatic double A60061, A60081, A60241, A60261, A60421, A60441, A60601, A60621, A60801, A61061, A61241, A61421, A61601, A62041, A62061, A62221, A62241, A62401, A62421, A62601, A63041, A63221, A63401, A64021, A64041, A64201, A64221, A64401, A65021, A65201, A66001, A66021, A66201, A67001, A68001;\nstatic double A70080, A70260, A70440, A70620, A70800, A71080, A71260, A71440, A71620, A71800, A72060, A72240, A72420, A72600, A73060, A73240, A73420, A73600, A74040, A74220, A74400, A75040, A75220, A75400, A76020, A76200, A77020, A77200, A78000, A79000;\nstatic double A80161, A80341, A80521, A80701, A81161, A81341, A81521, A81701, A82141, A82321, A82501, A83141, A83321, A83501, A84121, A84301, A85121, A85301, A86101, A87101;\nstatic double A90060, A90080, A90240, A90260, A90420, A90440, A90600, A90620, A90800, A91060, A91080, A91240, A91260, A91420, A91440, A91600, A91620, A91800, A92040, A92060, A92220, A92240, A92400, A92420, A92600, A93040, A93060, A93220, A93240, A93400, A93420, A93600, A94020, A94040, A94200, A94220, A94400, A95020, A95040, A95200, A95220, A95400, A96000, A96020, A96200, A97000, A97020, A97200, A98000, A99000;\nstatic double alpha20, alpha02, beta;\n\n\n/* Numerical coefficients appearing in the elliptic integrals expressions. The\n   indices here correspond to mode m, EllipticK/EllipticE, order in Sin[dphi],\n   term in polynomial in alpha/beta. */\nstatic const double ReEI[21][2][5][27] =\n {\n  {{{0,-0.26666666666666666,-0.5333333333333333,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.06666666666666667,0.2,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.4,0.13333333333333333,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.2666666666666666,0.5333333333333333,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,-4,-6.933333333333334,-3.2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,1.5333333333333334,1.5333333333333334,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.13333333333333333,0.4666666666666667,-0.2,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.2,-0.4666666666666667,-0.13333333333333333,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.5333333333333334,-1.5333333333333334,-0.5333333333333333,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,6.866666666666666,8.533333333333333,3.2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,-0.4,-0.4,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.06666666666666667,-0.6,-0.26666666666666666,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.6,-2.4,-1.0666666666666667,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,9.266666666666667,14.4,6.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.3333333333333335,-24.933333333333334,-38.4,-17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,1.2666666666666666,0.6,0.4,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.13333333333333333,0.06666666666666667,0.7333333333333333,0.26666666666666666,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.2,2.6,2.933333333333333,1.0666666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-3.533333333333333,-15.266666666666667,-17.6,-6.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.3333333333333333,10.2,40.93333333333333,46.93333333333333,17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,0,1.6,1.0666666666666667,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.06666666666666667,5,9.066666666666666,4.266666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-7.6,-42,-59.733333333333334,-25.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,30.6,128,166.4,68.26666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.466666666666667,-73.6,-275.2,-341.3333333333333,-136.53333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,0.4666666666666667,-0.6,-2.1333333333333333,-1.0666666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-1.1333333333333333,-8.733333333333333,-11.2,-4.266666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,19.8,67.06666666666666,72.53333333333333,25.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-4.2,-69.8,-198.4,-200.53333333333333,-68.26666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.06666666666666667,13.266666666666667,160,420.26666666666665,409.6,136.53333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,0.6666666666666666,-16.4,-34.13333333333333,-17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,41,186.4,247.46666666666667,102.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-17.6,-204,-630.4,-716.8,-273.06666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,65.8,536,1427.2,1501.8666666666666,546.1333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4857142857142858,-153.0857142857143,-1073.3714285714286,-2640.457142857143,-2652.647619047619,-936.2285714285714,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-0.8666666666666667,2.7333333333333334,30.266666666666666,42.666666666666664,17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-3.1333333333333333,-97.4,-290.93333333333334,-298.6666666666667,-102.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,53.8,410.4,937.6,853.3333333333334,273.06666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-4.6,-177.4,-1019.2,-2075.733333333333,-1774.9333333333334,-546.1333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.02857142857142857,14.771428571428572,392.62857142857143,1984,3791.2380952380954,3120.7619047619046,936.2285714285714,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,1.6,-198.4,-814.9333333333333,-1024,-409.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,147.4,1179.7333333333333,3012.266666666667,3072,1092.2666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-31.6,-629.2,-3345.0666666666666,-7116.8,-6553.6,-2184.5333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,114.94285714285714,1556.3428571428572,6981.4857142857145,13497.295238095237,11702.857142857143,3744.9142857142856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.492063492063492,-263.84761904761905,-3017.295238095238,-12327.009523809524,-22469.485714285714,-18724.571428571428,-5825.422222222222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-2.7333333333333334,18.6,452.26666666666665,1250.1333333333334,1228.8,409.6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-5.933333333333334,-401.26666666666665,-2212.266666666667,-4343.466666666666,-3618.133333333333,-1092.2666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,106.2,1463.3333333333333,5879.466666666666,9984,7645.866666666667,2184.5333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-4.885714285714286,-344.0857142857143,-3410.5142857142855,-11886.933333333332,-18646.55238095238,-13575.314285714287,-3744.9142857142856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.015873015873015872,15.806349206349207,753.3714285714286,6407.466666666666,20597.02857142857,30739.504761904762,21637.28253968254,5825.422222222222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,2.8,-970,-6314.666666666667,-14080,-13107.2,-4369.066666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,380.2,4689.866666666667,19157.333333333332,34508.8,28398.933333333334,8738.133333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-49.6,-1514.1714285714286,-12285.104761904762,-41442.74285714285,-66823.31428571428,-51180.49523809524,-14979.657142857142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,178.05396825396826,3632.0761904761903,24604.95238095238,74800.76190476191,112737.52380952382,82388.11428571428,23301.68888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.494949494949495,-405.9919191919192,-6920.145454545454,-42364.121212121216,-120645.81818181818,-173769.69696969696,-122863.45050505051,-33893.36565656566,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-5.133333333333334,58.2,2479.3333333333335,11306.666666666666,19814.4,15291.733333333334,4369.066666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-9.533333333333333,-1129.9333333333334,-9986.933333333332,-31906.133333333335,-47069.86666666667,-32768,-8738.133333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,178.14285714285714,3892.609523809524,24463.390476190478,66662.4,89604.87619047619,58670.32380952381,14979.657142857142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.1079365079365076,-573.9968253968254,-8815.314285714287,-47328,-117906.28571428571,-149562.51428571428,-94038.95873015873,-23301.68888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.010101010101010102,16.6,1251.761616161616,16279.660606060606,79786.66666666667,187671.27272727274,228846.41616161616,139810.13333333333,33893.36565656566,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,4.266666666666667,-3262.4,-30573.866666666665,-103765.33333333333,-163840,-122333.86666666667,-34952.53333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,811.4,14256.685714285713,84442.81904761905,231716.57142857142,322998.85714285716,222198.24761904762,59918.62857142857,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-71.6,-3111.35873015873,-35753.44761904762,-174188.49523809523,-426296.0761904762,-551126.5523809524,-359511.77142857143,-93206.75555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,255.14487734487736,7331.7772005772,69909.00086580086,305560.71341991343,696355.4632034632,856110.1021645021,538057.1797979798,135573.46262626263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4965034965034967,-579.5630147630147,-13824.999533799533,-118555.33053613054,-483592.47365967365,-1.0497981165501166e6,-1.2450923412587412e6,-761297.1362859363,-187717.1020979021,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-8.066666666666666,135.93333333333334,9026.133333333333,61310.933333333334,166024.53333333333,218453.33333333334,139810.13333333333,34952.53333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-13.933333333333334,-2571.1714285714284,-33332.038095238095,-157322.3619047619,-357171.2,-422863.2380952381,-252157.5619047619,-59918.62857142857,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,270.5174603174603,8617.873015873016,78308.57142857143,312652.8,643189.0285714286,713406.1714285715,406115.1492063492,93206.75555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.28975468975469,-870.3079365079365,-19227.160750360752,-147887.70909090908,-536312.6857142857,-1.0362348051948051e6,-1.0997186678210679e6,-605843.911111111,-135573.46262626263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.006993006993006993,17.244910644910647,1895.0315462315461,35180.35244755245,245442.05874125875,836234.2041958042,1.5471995524475526e6,1.5905439527583527e6,855155.6873348873,187717.1020979021,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,6,-8800.4,-111859.80952380953,-531221.9428571429,-1.2414390857142857e6,-1.5322940952380951e6,-958698.0571428571,-239674.51428571428,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,1529,36268.93968253968,292742.09523809527,1.1256783238095238e6,2.3227830857142857e6,2.6401645714285714e6,1.557884342857143e6,372827.02222222224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-97.6,-5729.1255411255415,-88548.89927849927,-586570.2510822511,-2.0080116917748917e6,-3.8364944623376625e6,-4.1284842943722946e6,-2.3386422303030305e6,-542293.8505050505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,346.2218004218004,13349.73026973027,170513.0474858475,1.0105419870795871e6,3.214343297236097e6,5.829024556243756e6,6.033182035742036e6,3.3163354703962705e6,750868.4083916084,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4974358974358974,-784.5839937839938,-25005.26247086247,-286327.7053613054,-1.5797995114219114e6,-4.77710082983683e6,-8.342656417715617e6,-8.387304409013209e6,-4.50521045034965e6,-1.0011578778554779e6,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-11.533333333333333,269.4,25857.619047619046,244643.3523809524,941524.1142857143,1.8502997333333334e6,1.9667041523809524e6,1.0785353142857142e6,239674.51428571428,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-19.133333333333333,-5094.479365079365,-91148.63492063493,-595972.8761904762,-1.9178837333333334e6,-3.3857145904761907e6,-3.3492016761904764e6,-1.744297853968254e6,-372827.02222222224,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,384.0510822510823,16824.368253968252,209062.3953823954,1.1501940363636364e6,3.3420239238095237e6,5.513081239826839e6,5.196125312554113e6,2.6097891555555556e6,542293.8505050505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.443600843600843,-1235.5986901986903,-37219.1333999334,-389168.827972028,-1.9364873206793207e6,-5.267843912887113e6,-8.294196586702187e6,-7.550561944366745e6,-3.6917696745920745e6,-750868.4083916084,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.005128205128205128,17.78850038850039,2689.031546231546,67733.57202797203,639727.53006993,2.9806964363636365e6,7.743910966899767e6,1.1785440213830614e7,1.0452192532090131e7,5.005789389277389e6,1.0011578778554779e6,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,8,-20454.4,-338221.5111111111,-2.129646933333333e6,-6.821751466666667e6,-1.2233386666666666e7,-1.2443101866666667e7,-6.7108864e6,-1.491308088888889e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,2637,81197.69581529581,855236.9685425685,4.365921080519481e6,1.2354614081385281e7,2.0404108744588744e7,1.9569787234632034e7,1.0100222965656566e7,2.169175402020202e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-127.6,-9731.779553779554,-194743.272016872,-1.6810041968697968e6,-7.631895255677655e6,-1.9979235561238762e7,-3.121355504229104e7,-2.8716805848018646e7,-1.4349929582595183e7,-3.0034736335664336e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,451.2884670884671,22506.019314019315,371138.143989344,2.8608340821844824e6,1.2051529176956376e7,2.991034232967033e7,4.489893256254856e7,4.0046315114219114e7,1.9522578618181817e7,4.0046315114219114e6,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4980392156862745,-1021.0684583390466,-41964.88205128205,-619011.4680926916,-4.434788679994515e6,-1.7736541547237076e7,-4.234410989606472e7,-6.1679177494547285e7,-5.3709175564952694e7,-2.5676754984999314e7,-5.182464308898944e6,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-15.533333333333333,479.4,63067.02222222222,792570.3111111111,4.1009152e6,1.1153681066666666e7,1.7336456533333335e7,1.55189248e7,7.456540444444444e6,1.491308088888889e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-25.133333333333333,-9153.99163059163,-216262.70476190475,-1.870872972005772e6,-8.079372412121212e6,-1.9716661638095237e7,-2.849857074978355e7,-2.421317832958153e7,-1.1184810666666666e7,-2.169175402020202e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,519.3591075591075,29973.258252858253,488692.7529359529,3.5444105846153847e6,1.3786808101764902e7,3.138413195977356e7,4.3162921822710626e7,3.5328619333022535e7,1.58516663993784e7,3.0034736335664336e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.576934176934177,-1672.0434454434455,-65967.23223443223,-901384.234965035,-5.895572075924076e6,-2.1422238766033966e7,-4.646431837318237e7,-6.163704083294483e7,-4.905673601491842e7,-2.1524894373892773e7,-4.0046315114219114e6,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.00392156862745098,18.25848530554413,3638.6809634809633,119650.73401892226,1.4725623786096256e6,8.997631810448375e6,3.1165726124667488e7,6.523953848801134e7,8.420522974629554e7,6.55758409995338e7,2.8267987139448788e7,5.182464308898944e6,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,10.266666666666667,-42656.4,-889373.995959596,-7.149019797979798e6,-2.9865748169696968e7,-7.235293556363636e7,-1.0554394065454546e8,-9.146124722424242e7,-4.3383508040404044e7,-8.676701608080808e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,4255.4,165053.57016317017,2.19670492991453e6,1.4330180624708625e7,5.29140260997669e7,1.1808499516270396e8,1.624359102955711e8,1.346948423011655e8,6.173806913442113e7,1.2013894534265734e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-161.6,-15539.587434787434,-390961.72201132204,-4.259990718614719e6,-2.4699115148318347e7,-8.432809336796537e7,-1.779399951088911e8,-2.3469069971592852e8,-1.8834282577156177e8,-8.409726173986015e7,-1.6018526045687646e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,570.3472906178788,35746.582191664544,739622.7686979687,7.187172049660143e6,3.862786977665472e7,1.249378262920452e8,2.5314112267361397e8,3.234930047362311e8,2.531309910877828e8,1.1071628296284108e8,2.0729857235595778e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.498452012383901,-1289.0251146857959,-66438.47422257825,-1.227617738434115e6,-1.107397992473244e7,-5.645272137256345e7,-1.7550347663965014e8,-3.4482027794523466e8,-4.29947715579034e8,-3.2974049077368206e8,-1.4183586529618162e8,-2.6185082823910456e7,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-20.066666666666666,789.9333333333333,136892.39191919193,2.2055480888888887e6,1.4743798173737373e7,5.277455204848485e7,1.1157285546666667e8,1.4395359728484848e8,1.115261196929293e8,4.7721858844444446e7,8.676701608080808e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-31.933333333333334,-15290.940326340326,-461269.8076146076,-5.10054407956488e6,-2.841850342937063e7,-9.11948410927739e7,-1.7918446766247088e8,-2.193337460960373e8,-1.6331127164320123e8,-6.7745016401554e7,-1.2013894534265734e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,676.9748695748696,49810.32203352203,1.0321920394272394e6,9.544164072727272e6,4.781388058341658e7,1.4294413774385613e8,2.6698699182559663e8,3.145956128422688e8,2.2738798300792542e8,9.210652476270397e7,1.6018526045687646e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.694581235757706,-2181.5221419103773,-109270.05492285492,-1.892202578033731e6,-1.5744906345607335e7,-7.356716086097433e7,-2.0929734465833253e8,-3.7668167367356825e8,-4.312426213404269e8,-3.046022843375291e8,-1.2108121158063897e8,-2.0729857235595778e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.0030959752321981426,18.672520393263426,4748.226915980476,197766.84242568578,3.0782871159107145e6,2.3888763615371626e7,1.062565981512135e8,2.914255876520172e8,5.098456950542433e8,5.706785877375826e8,3.9574872039228964e8,1.5492840670813686e8,2.6185082823910456e7,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,12.8,-81880,-2.1007407614607615e6,-2.095521238228438e7,-1.1023046737156177e8,-3.441285888596737e8,-6.709847600932401e8,-8.259552492307693e8,-6.240550771965812e8,-2.6430567975384617e8,-4.805557813706294e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,6520.2,311067.7942501943,5.102019779331779e6,4.133440304335664e7,1.922698987114219e8,5.530782550526806e8,1.0170620137920746e9,1.1980522605003884e9,8.746782659530692e8,3.6041683602797204e8,6.407410418275058e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-199.6,-23628.784478266833,-730166.5007724302,-9.797468315151514e6,-7.051502323959962e7,-3.0318843028131086e8,-8.24417740343672e8,-1.4532290842782943e9,-1.6563077409054527e9,-1.1777150141972852e9,-4.749021839427396e8,-8.291942894238311e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,703.3999221968262,54143.24552805296,1.3738612902969064e6,1.6423980970872264e7,1.0950013007528281e8,4.457761957446739e8,1.1633972193432798e9,1.9862208061391819e9,2.20653026587352e9,1.5363898975089884e9,6.088031756559181e8,1.0474033129564182e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.49874686716792,-1588.4598780648316,-100391.40281967774,-2.2721153274630425e6,-2.5192147588914696e7,-1.5919834323671788e8,-6.225945846062396e8,-1.5748847121862333e9,-2.622330288934644e9,-2.854181112514796e9,-1.9537500785578425e9,-7.631081280111047e8,-1.2967850541365178e8,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-25.133333333333333,1228.2,271881.5229215229,5.463605097125097e6,4.5788857465734266e7,2.0806943674778554e8,5.705886187710955e8,9.891261404195805e8,1.0929306833255634e9,7.47197496172805e8,2.883334688223776e8,4.805557813706294e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-39.53333333333333,-24135.788500388502,-905518.1986013986,-1.2458983344832946e7,-8.693429073752914e7,-3.539285428811189e8,-9.016469155505828e8,-1.4824022811648796e9,-1.5738201839888113e9,-1.0428727894327894e9,-3.9245388811934733e8,-6.407410418275058e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,857.3689565336624,78373.47438182733,2.0127715456099457e6,2.3112727872974087e7,1.448205784474976e8,5.48772786971836e8,1.3280004404618127e9,2.1005893010457883e9,2.16389478497818e9,1.3996187132419581e9,5.163618984139312e8,8.291942894238311e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.799844393652443,-2765.6909199503625,-171565.7852751995,-3.67408829080517e6,-3.790738907893345e7,-2.2126713741740116e8,-7.971567635483366e8,-1.8574286571222606e9,-2.852811286882909e9,-2.870394025251496e9,-1.8211526732190146e9,-6.61173341303739e8,-1.0474033129564182e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.002506265664160401,19.04256314720711,6021.407418450143,310071.7180627422,5.959559314048181e6,5.72743914936333e7,3.179286576710175e8,1.1032734622114787e9,2.4971591341437187e9,3.7476056187814e9,3.700130737674165e9,2.310989422798335e9,8.279473807179306e8,1.2967850541365178e8,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,15.6,-147184.4,-4.556378703962704e6,-5.516948408391608e7,-3.559327987468532e8,-1.3849838370013986e9,-3.448738599742657e9,-5.628697306405595e9,-5.996935688354312e9,-4.0186477217118883e9,-1.537778500386014e9,-2.5629641673100233e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,9583.4,551597.1562502857,1.092800063183875e7,1.0752849872261073e8,6.136858642814754e8,2.2008252360679007e9,5.169359078663266e9,8.074326015812789e9,8.312849426393528e9,5.419679834310819e9,2.0277569441364596e9,3.3167771576953244e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-241.6,-34531.581210533535,-1.2859442616457501e6,-2.0830391072875936e7,-1.8202685521436995e8,-9.600492874957876e8,-3.2540002685571485e9,-7.325356346213779e9,-1.1069155880596071e10,-1.1097453830148788e10,-7.079345431875746e9,-2.6010515605084386e9,-4.189613251825673e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,850.4475412444452,78893.74837670069,2.409665423174555e6,3.474925120193656e7,2.8114098072956973e8,1.403407938144174e9,4.564031654437904e9,9.948487456287066e9,1.4649250696397833e10,1.4378787962114319e10,9.011876808307611e9,3.2619131746357026e9,5.187140216546071e8,0,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4989648033126293,-1919.3769533617697,-146019.8335099459,-3.9742071741968985e6,-5.311713496745179e7,-4.071180069944188e8,-1.951471853701304e9,-6.149049338477993e9,-1.3068470999123806e10,-1.8848566822377876e10,-1.8183385037681484e10,-1.1230798287201794e10,-4.014395471935655e9,-6.314779394056087e8,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-30.733333333333334,1824.6,503413.80792540795,1.23437348997669e7,1.2666985013706294e8,7.111014843972028e8,2.4453185536e9,5.440886345756643e9,8.003944371617716e9,7.74195386945641e9,4.739481393767832e9,1.6659267087515152e9,2.5629641673100233e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-47.93333333333333,-36410.112500571326,-1.6632604871154989e6,-2.7868508792540792e7,-2.3790098953758398e8,-1.1959445584594543e9,-3.819200511326039e9,-8.057472578080973e9,-1.1386686300517574e10,-1.0673579702376745e10,-6.371368734672261e9,-2.193595802021226e9,-3.3167771576953244e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,1060.962421067065,117999.39040993406,3.6796141928934497e6,5.137764483644158e7,3.934568326273124e8,1.8396699858694205e9,5.577640943291375e9,1.131629761773835e10,1.5511646734480068e10,1.4188707002727196e10,8.301315963658234e9,2.8105322230997224e9,4.189613251825673e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.895082488890538,-3426.029950579177,-257947.1453013447,-6.696260875827578e6,-8.390810906845228e7,-5.980646799483613e8,-2.656730813122411e9,-7.751269489416868e9,-1.5263016489000265e10,-2.042660824129106e10,-1.832174708555405e10,-1.0545574516565224e10,-3.521270185463006e9,-5.187140216546071e8,0,0,0,0,0,0,0,0,0,0},{0,0,0,-0.002070393374741201,19.377095129336347,7461.561160324547,465737.4659903026,1.0838682409513706e7,1.2638856718220124e8,8.560311134982536e8,3.660454950424734e9,1.0368589915334404e10,1.9940417844891014e10,2.6176608626888607e10,2.310528608711372e10,1.3119593909531069e10,4.330134441638459e9,6.314779394056087e8,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,18.666666666666668,-250822.4,-9.21840527146579e6,-1.3309575831290278e8,-1.0317491250125874e9,-4.880582542269025e9,-1.5029793383663677e10,-3.1108875678233482e10,-4.36671203627632e10,-4.102438746924404e10,-2.470622075987824e10,-8.623620610007843e9,-1.3267108630781298e9,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,13613,930252.8430925112,2.189775853092872e7,2.5704415869336855e8,1.7632092507090087e9,7.688769367066271e9,2.233765721776305e10,4.425202515598031e10,6.002764410921827e10,5.486875815318882e10,3.230882151159315e10,1.1067561673572819e10,1.6758453007302692e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-287.6,-48836.168107778016,-2.157297016025873e6,-4.150197552329858e7,-4.325356919968865e8,-2.7414740676285434e9,-1.129721454934732e10,-3.1453610225900883e10,-6.027595384543495e10,-7.961419543169162e10,-7.119894052826602e10,-4.116669092530494e10,-1.3885575348907944e10,-2.0748560866184285e9,0,0,0,0,0,0,0,0,0,0},{0,0,0,1,1011.4910195053149,111321.75903879551,4.0295488900919873e6,6.897135012978844e7,6.652503712536638e8,3.9895117085213494e9,1.5770553504337744e10,4.2506951460346924e10,7.936585512341737e10,1.0261545331623116e11,9.014849059168524e10,5.1342279166782936e10,1.7095009931051834e10,2.5259117576224346e9,0,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4991304347826087,-2281.7794378338194,-205750.77203382071,-6.631606823604064e6,-1.0514449275916624e8,-9.603493437127837e8,-5.528469326922443e9,-2.116888539068655e10,-5.561882541965658e10,-1.0169630183320364e11,-1.2921016311537668e11,-1.1184445811395491e11,-6.289520276479862e10,-2.0712476412503963e10,-3.0310941091469216e9,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-36.86666666666667,2612.733333333333,880586.5429315782,2.58769066508707e7,3.190248779785548e8,2.165737490196956e9,9.103826722266722e9,2.516944859639786e10,4.714484837223376e10,6.028345544481031e10,5.188494812822027e10,2.876927277805501e10,9.286976041546907e9,1.3267108630781298e9,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-57.13333333333333,-52928.28618502241,-2.8930003372693355e6,-5.799649101182244e7,-5.943782676834052e8,-3.6117879045470395e9,-1.4095434050579176e10,-3.694310664816257e10,-6.646474288799973e10,-8.235087355324098e10,-6.910511159213396e10,-3.752838135449264e10,-1.1905484323937954e10,-1.6758453007302692e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,1288.1362155560298,171329.4262403575,6.378245421382333e6,1.0642760530321938e8,9.777255230809364e8,5.52282590456045e9,2.045420469861058e10,5.153708389209838e10,8.991146463998302e10,1.0868422825737668e11,8.937325837111873e10,4.772044308351795e10,1.4923003392217157e10,2.0748560866184285e9,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.982039010629669,-4163.8773617253355,-374174.74996878806,-1.1581383324811196e7,-1.7326355869602597e8,-1.4804164912081263e9,-7.940429316689499e9,-2.828693012341041e10,-6.914758135245525e10,-1.1774449383689468e11,-1.395287760054435e11,-1.1285112004028159e11,-5.941617567775465e10,-1.8357965809863052e10,-2.5259117576224346e9,0,0,0,0,0,0,0,0,0},{0,0,0,-0.0017391304347826088,19.68235392850819,9071.70504037235,675142.9984015393,1.87165462532353e7,2.6038147706447074e8,2.1128045296213334e9,1.0902989617985832e10,3.76931192037466e10,8.995871805969456e10,1.5020871457643378e11,1.7511705338495316e11,1.3969263524174225e11,7.268311082558556e10,2.2228023467077423e10,3.0310941091469216e9,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,22,-408912.4,-1.760144321133892e7,-2.9861306666560435e8,-2.735742086051796e9,-1.5429018250372686e10,-5.7365925635849785e10,-1.459396829211136e11,-2.5834540566109332e11,-3.181407036536493e11,-2.6734969563212573e11,-1.462262308496573e11,-4.692366842044753e10,-6.703381202921077e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,18793,1.5042532023658487e6,4.1496704494122185e7,5.725677207573206e8,4.642875405124088e9,2.4142609768578815e10,8.469481355425212e10,2.0624416519975134e11,3.5284625417378284e11,4.227746181988333e11,3.4738564819211395e11,1.8647020933259586e11,5.8893991997092316e10,8.299424346473714e9,0,0,0,0,0,0,0,0,0,0},{0,0,0,-337.6,-67186.71908304376,-3.473936806561178e6,-7.829175553408918e7,-9.589299292985568e8,-7.183900332775953e9,-3.529847566422289e10,-1.1866407812293896e11,-2.795105576516615e11,-4.6559435752690393e11,-5.4578034888958093e11,-4.4032629265684235e11,-2.3272130603976505e11,-7.25297518974442e10,-1.0103647030489738e10,0,0,0,0,0,0,0,0,0},{0,0,0,1,1186.5310195053148,152876.8874522256,6.472435445144703e6,1.2971987635205658e8,1.4699377328144228e9,1.0417012853749659e10,4.909095266192736e10,1.5974095702715295e11,3.6655776122906323e11,5.976373260031626e11,6.881257568365696e11,5.468004776147498e11,2.8525211689964355e11,8.790172916526073e10,1.2124376436587687e10,0,0,0,0,0,0,0,0},{0,0,0,0,-3.4992592592592593,-2675.669679379713,-282242.09231669543,-1.0633815228365488e7,-1.9732870645890525e8,-2.1167235434403558e9,-1.4395851669845222e10,-6.570092170701432e10,-2.08363647527213e11,-4.6815844108415533e11,-7.499685194097299e11,-8.507323675829946e11,-6.674090341577878e11,-3.4432387109323157e11,-1.0507792911709328e11,-1.4369631332252073e10,0,0,0,0,0,0,0}},{{0.5333333333333333,-43.53333333333333,3629.4,1.4694724226778382e6,5.0982079751051836e7,7.433658698673364e8,5.9978865667303295e9,3.02094865091216e10,1.0127974704935579e11,2.3405817896550006e11,3.7868392857216315e11,4.284339090756366e11,3.3229306521589435e11,1.6843118108433334e11,5.027535902190807e10,6.703381202921077e9,0,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-67.13333333333334,-74599.00473169761,-4.808057892384602e6,-1.1363745491663341e8,-1.3765659670421038e9,-9.93853334295837e9,-4.646297120807411e10,-1.476455595126865e11,-3.276911350438754e11,-5.136740259640373e11,-5.665757674791024e11,-4.3035620039143896e11,-2.143610632661782e11,-6.304370417032917e10,-8.299424346473714e9,0,0,0,0,0,0,0,0,0,0},{0,0,0.2,1539.2381660875133,241314.9715176823,1.0573575649746368e7,2.0780309906924197e8,2.255048448318664e9,1.5127647788083984e10,6.709248572068778e10,2.0490559066010007e11,4.4090160795530695e11,6.741577248596049e11,7.285603048003533e11,5.440348341050625e11,2.6709174817027032e11,7.758157541268906e10,1.0103647030489738e10,0,0,0,0,0,0,0,0,0},{0,0,0,-6.062039010629669,-4980.454188585528,-526689.1747977207,-1.9167077767400973e7,-3.374851933365408e8,-3.4042942107257743e9,-2.167534593874192e10,-9.243454922934729e10,-2.738046230308966e11,-5.748972516712766e11,-8.61570663231068e11,-9.156248791038105e11,-6.740816221370153e11,-3.269296609004137e11,-9.396391738355457e10,-1.2124376436587687e10,0,0,0,0,0,0,0,0},{0,0,0,-0.0014814814814814814,19.963062463129766,10854.591016749811,949896.1873083068,3.0939338982764497e7,5.0627985565643203e8,4.84757118825071e9,2.9683411242703876e10,1.2280305832607028e11,3.5504229585249896e11,7.308189481361008e11,1.0772753882648191e12,1.1289483918947014e12,8.212160109323472e11,3.941685297769809e11,1.1226274478321931e11,1.4369631332252073e10,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,25.6,-642174.4,-3.2001493960675444e7,-6.301171929947947e8,-6.730005930120287e9,-4.45489555789727e10,-1.9627249696235114e11,-5.995497443810022e11,-1.2980003517584287e12,-2.0066420365331858e12,-2.2010916432997065e12,-1.6733714338577625e12,-8.384014633082003e11,-2.4898273039421143e11,-3.3197697385894855e10,0,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,25323.4,2.3470004379713614e6,7.499139768356036e7,1.2012583049576294e9,1.1358261276733395e10,6.933205150119518e10,2.8824587254763275e11,8.428926177352782e11,1.7637070948449934e12,2.6530579083520938e12,2.8455838704309585e12,2.1231780276189883e12,1.0469973628525146e12,3.067178562827242e11,4.0414588121958954e10,0,0,0,0,0,0,0,0,0},{0,0,0,-391.6,-90283.39413633075,-5.402084146905385e6,-1.409712773254586e8,-2.0042036720084534e9,-1.7506077529181057e10,-1.009677881940971e11,-4.0223835666768646e11,-1.1377089895665e12,-2.317805564538369e12,-3.4109110612635464e12,-3.591988921734658e12,-2.6387589471896216e12,-1.2839931153068442e12,-3.718142107220224e11,-4.8497505746350746e10,0,0,0,0,0,0,0,0},{0,0,0,1,1375.5680565423518,205134.69458317457,1.00442903694642e7,2.3300366348778668e8,3.0639399202108026e9,2.531140817273939e10,1.3999475849889786e11,5.397786494220163e11,1.4872042574435613e12,2.9652929630267153e12,4.285977100720373e12,4.445188146394695e12,3.223045360499386e12,1.5505730309458252e12,4.4456046934154846e11,5.747852532900829e10,0,0,0,0,0,0,0},{0,0,0,0,-3.499361430395913,-3101.049500580224,-378382.55774830375,-1.6479398081656067e7,-3.538256790695258e8,-4.4032169385133705e9,-3.490151422566471e10,-1.869146820137703e11,-7.023015906202007e11,-1.8944051915725413e12,-3.7109112830375283e12,-5.283759154390902e12,-5.409848733761012e12,-3.8788481619419673e12,-1.8478757481648335e12,-5.2523480042024817e11,-6.7388615902975235e10,0,0,0,0,0,0}},{{0.5333333333333333,-50.733333333333334,4914.6,2.3567543213508883e6,9.531948442077139e7,1.6222566814226518e9,1.533629951143212e10,9.106927966780995e10,3.6324193033461017e11,1.0115537476095123e12,2.0078489418750176e12,2.859678973879202e12,2.902119927570143e12,2.0490001876928757e12,9.566682602454508e11,2.6558157908715884e11,3.3197697385894855e10,0,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-77.93333333333334,-102426.67594272287,-7.688375333570598e6,-2.1157272142042986e8,-2.9899660626777706e9,-2.5285472574644356e10,-1.39341099517905e11,-5.2671795525996155e11,-1.4085398898401514e12,-2.708603610196061e12,-3.76067732280449e12,-3.7374252386297983e12,-2.5929559786286685e12,-1.1927785557210093e12,-3.269251503437037e11,-4.0414588121958954e10,0,0,0,0,0,0,0,0,0},{0,0,0.2,1814.5882726615152,331222.3335493492,1.6875662577301286e7,3.858469039048684e8,4.882312146902555e9,3.834964651451061e10,2.0043215049773267e11,7.280122943454908e11,1.8871102433942173e12,3.5392202281469395e12,4.81403000415386e12,4.70284874154185e12,3.215586981496385e12,1.4608069383404146e12,3.9606296359519775e11,4.8497505746350746e10,0,0,0,0,0,0,0,0},{0,0,0,-6.136113084703743,-5876.883265298239,-722621.9636936528,-3.0552356959223974e7,-6.254641839081137e8,-7.35329135286915e9,-5.480054731379718e10,-2.753158133060997e11,-9.696738980136191e11,-2.4522191929156543e12,-4.506888343001096e12,-6.027500191226616e12,-5.804540636175082e12,-3.920365399720353e12,-1.7620760421174104e12,-4.732997320060526e11,-5.747852532900829e10,0,0,0,0,0,0,0},{0,0,0,-0.001277139208173691,20.222883663640623,12812.749057488787,1.302854067321981e6,4.927317433912787e7,9.370025785364087e8,1.045225177549322e10,7.488955072282285e10,3.6490079744731726e11,1.254099223039917e12,3.108551076865836e12,5.618437072552979e12,7.408490300586078e12,7.048384464155902e12,4.710622193686491e12,2.09785778289315e12,5.589291083717358e11,6.7388615902975235e10,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,29.466666666666665,-976730,-5.578954646189742e7,-1.2615869100582263e9,-1.5529302994019297e10,-1.1910193801450629e11,-6.125072742936332e11,-2.206143296031422e12,-5.709225768796877e12,-1.0749971748792695e13,-1.4747065224375662e13,-1.4582852118589701e13,-1.0122591368671906e13,-4.679251530995561e12,-1.2932668199026865e12,-1.6165835248783582e11,0,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,33420.2,3.550881250122197e6,1.3009442746062008e8,2.3938339984597726e9,2.6090461836376514e10,1.8454580335053577e11,8.956680843174019e11,3.088494496941551e12,7.725403893782204e12,1.415456345665883e13,1.898742633772399e13,1.842784153284401e13,1.2590136522466424e13,5.741108749446352e12,1.5680860191320073e12,1.9399002298540298e11,0,0,0,0,0,0,0,0},{0,0,0,-449.6,-118882.34141578716,-8.1507702655822905e6,-2.4382796059468478e8,-3.981492068669497e9,-4.008401440642937e10,-2.6788273341492886e11,-1.2457916692080254e12,-4.155022019169404e12,-1.011883380132408e13,-1.8137213625744375e13,-2.3887559953693965e13,-2.2825549173918258e13,-1.538763792071379e13,-6.935816898196855e12,-1.8752368888588955e12,-2.2991410131603317e11,0,0,0,0,0,0,0},{0,0,0,1,1578.6025393009725,269796.6997164444,1.5129675524776911e7,4.02205042950141e8,6.073244823885286e9,5.781867124578261e10,3.705046212385272e11,1.6674750698719607e12,5.417057116138862e12,1.2910569366340434e13,2.272747357675429e13,2.947861243273825e13,2.7800233539143176e13,1.8528636980494258e13,8.268499554905844e12,2.2158962523390093e12,2.6955446361190094e11,0,0,0,0,0,0},{0,0,0,0,-3.49944382647386,-3557.9203447280424,-497291.8376827858,-2.479476383769259e7,-6.098901382048699e8,-8.713451379196568e9,-7.957942158518886e10,-4.937089707091298e11,-2.1650359733823113e12,-6.885306538377396e12,-1.6120601827215521e13,-2.7953517732769695e13,-3.579038142482068e13,-3.3375171285503258e13,-2.2026160316691473e13,-9.744096556853e12,-2.5912009727853706e12,-3.1303099000091724e11,0,0,0,0,0}},{{0.5333333333333333,-58.46666666666667,6511.533333333334,3.65374292379483e6,1.7041039042477334e8,3.3474383786596866e9,3.6634576706844986e10,2.5307096312507574e11,1.1825685116914846e12,3.8959211372933706e12,9.271173514538479e12,1.6125338479467012e13,2.0514656081142355e13,1.8879270148820613e13,1.2234885075983668e13,5.295573999855435e12,1.3740959961466045e12,1.6165835248783582e11,0,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-89.53333333333333,-137512.7002443942,-1.189358210895399e7,-3.7701063823343146e8,-6.146753773932911e9,-6.016166039394057e10,-3.856178138857783e11,-1.7075189529981672e12,-5.401437822018391e12,-1.2451974168004955e13,-2.1111659790717637e13,-2.6300614293483598e13,-2.3782945860158555e13,-1.5184861333257229e13,-6.488778629702592e12,-1.665081030624709e12,-1.9399002298540298e11,0,0,0,0,0,0,0,0},{0,0,0.2,2114.4828315743325,444637.2286422091,2.6068237359630816e7,6.861001365773908e8,1.00114325811189e10,9.098481065241385e10,5.529740045299663e11,2.3523822512552803e12,7.2120144708876045e12,1.621315950309689e13,2.692705307551175e13,3.29716634138851e13,2.938224475952283e13,1.8525493900149555e13,7.830326448629547e12,1.990193939516912e12,2.2991410131603317e11,0,0,0,0,0,0,0},{0,0,0,-6.2050786019451225,-6854.203876665512,-969805.7488195399,-4.71490524203349e7,-1.110535871836198e9,-1.5050298809158161e10,-1.2973475579082533e11,-7.577552802741434e11,-3.1251463627856104e12,-9.3459380263197e12,-2.058641033023447e13,-3.3613123793561336e13,-4.056896085255746e13,-3.57073389951639e13,-2.2272676941597234e13,-9.325906219148117e12,-2.3506734841449595e12,-2.6955446361190094e11,0,0,0,0,0,0},{0,0,0,-0.0011123470522803114,20.46471615241416,14948.520395986918,1.7481414056108568e6,7.598676584469618e7,1.6618757729228277e9,2.1362875654996433e10,1.7699538829298035e11,1.0024052815552432e12,4.0333265266471006e12,1.182051050899169e13,2.5601839654882164e13,4.120957066054989e13,4.913190571704601e13,4.278719239553509e13,2.6441705068033305e13,1.0981003732620514e13,2.747716467785829e12,3.1303099000091724e11,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,33.6,-1.4449664e6,-9.378112396360818e7,-2.4134343369641385e9,-3.3903936051757397e10,-2.9801676779554596e11,-1.7669252650270583e12,-7.395409184951448e12,-2.247442917360153e13,-5.039491703618102e13,-8.391129263208694e13,-1.034420381542176e14,-9.311814740541167e13,-5.94700664214626e13,-2.5517770940204883e13,-6.595660781503701e12,-7.759600919416119e11,0,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,43315.4,5.230291427936181e6,2.1780053830000895e8,4.561721534340654e9,5.6748630670697624e10,4.600956128255938e11,2.574621229746886e12,1.0317263052979926e13,3.0307188769397027e13,6.613140421026188e13,1.0767812330799452e14,1.302827448528449e14,1.1543547237775336e14,7.272605132546514e13,3.083902504292948e13,7.888927601406388e12,9.196564052641327e11,0,0,0,0,0,0,0},{0,0,0,-511.6,-153795.69885244747,-1.1978643183124974e7,-4.072033459630186e8,-7.567600637131104e9,-8.695493057921883e10,-6.660723636188911e11,-3.571363466702473e12,-1.384223532826015e13,-3.9588090502665234e13,-8.450579365558694e13,-1.3509302529267044e14,-1.609267891315999e14,-1.4069221688269344e14,-8.761436750764872e13,-3.677597449159481e13,-9.323413211988104e12,-1.0782178544476038e12,0,0,0,0,0,0},{0,0,0,1,1795.6347973654888,348690.3862286264,2.2204228030820765e7,6.705854777632657e8,1.1522110280379248e10,1.2517973983561208e11,9.19332170318912e11,4.770007373158425e12,1.8007053868363406e13,5.039707192316374e13,1.056513773616503e14,1.663261076501606e14,1.955389474170697e14,1.6900738607205572e14,1.0419707990723603e14,4.335070020676101e13,1.0903912818365283e13,1.252123960003669e12,0,0,0,0,0},{0,0,0,0,-3.4995112414467253,-4046.283374356923,-642320.5204937553,-3.6354442541503884e7,-1.0156406403679376e9,-1.6508254693268639e10,-1.7202985544924103e11,-1.2230324761502656e12,-6.182633991853942e12,-2.2846441388588523e13,-6.281007640234248e13,-1.2969530267124028e14,-2.0154019556485406e14,-2.3427740497541834e14,-2.0049551613303962e14,-1.2253405597896867e14,-5.058454321247145e13,-1.2635069050946113e13,-1.4418397115193762e12,0,0,0,0}},{{0.5333333333333333,-66.73333333333333,8466.6,5.500782327216349e6,2.9307562486459124e8,6.580662628830447e9,8.25198689979895e10,6.557192881581038e11,3.544366348591472e12,1.3610364338969898e13,3.81402493752755e13,7.919889465707464e13,1.2257383139758511e14,1.4091199728388275e14,1.1864488533235611e14,7.106501175365262e13,2.8670108813717684e13,6.983640827474507e12,7.759600919416119e11,0,0,0,0,0,0,0,0},{0,0.13333333333333333,-101.93333333333334,-181056.65587236284,-1.787733779890622e7,-6.467215044849563e8,-1.2047942031497614e10,-1.3508357853771237e11,-9.958337708633071e11,-5.100215968856238e12,-1.8803864759217754e13,-5.1043402609364266e13,-1.0331528027101834e14,-1.565720156503844e14,-1.7685958939722884e14,-1.4670592314888475e14,-8.675260770965969e13,-3.461105326764565e13,-8.348755804038454e12,-9.196564052641327e11,0,0,0,0,0,0,0},{0,0,0.2,2439.19770489493,585468.939241731,3.91400351372385e7,1.1749495789312527e9,1.958269650268004e10,2.0382117002694974e11,1.4244548817255012e12,7.007773329261644e12,2.5037504149523434e13,6.627085088332564e13,1.3138571746780752e14,1.956929732604737e14,2.1782474594777912e14,1.7841953684261428e14,1.0435529770005283e14,4.123551524987994e13,9.862522139211904e12,1.0782178544476038e12,0,0,0,0,0,0},{0,0,0,-6.2695947309773805,-7913.383352642443,-1.2767836155293747e6,-7.073831350388925e7,-1.899554623143067e9,-2.939480935713848e10,-2.901204668878392e11,-1.9481763004141086e12,-9.290239953778574e12,-3.237296296631215e13,-8.39479743534346e13,-1.6360610601542472e14,-2.401716637111394e14,-2.6402193545879147e14,-2.1393204742321744e14,-1.2394533297842339e14,-4.856788337344296e13,-1.1529974798367117e13,-1.252123960003669e12,0,0,0,0,0},{0,0,0,-0.0009775171065493646,20.69089338637782,17264.0838134564,2.3011678842776404e6,1.1394226862290737e8,2.840139762731206e9,4.167633267110191e10,3.95266965553167e11,2.5731613895229917e12,1.1969456812409002e13,4.086863287421054e13,1.0419401598474152e14,2.0016347293020044e14,2.902331341320077e14,3.156571512430503e14,2.533843334077847e14,1.4559242461110694e14,5.6631732792034625e13,1.33559889067058e13,1.4418397115193762e12,0,0,0,0}}},\n  {{{0,-0.26666666666666666,38,-2.0864644e6,-1.5269386717706367e8,-4.4364839170700865e9,-7.052545524685811e10,-7.039261202593761e11,-4.761725707062858e12,-2.288315999285902e13,-8.050868479032589e13,-2.1126861740115747e14,-4.1760113688084575e14,-6.230137174218235e14,-6.97244682853531e14,-5.760027708229431e14,-3.406464803623676e14,-1.3638576338223748e14,-3.3107630589508773e13,-3.678625621056531e12,0,0,0,0,0,0,0},{0,0,-0.06666666666666667,55257,7.524884401159955e6,3.534212218718977e8,8.358422454073611e9,1.1767720205354315e11,1.0834651446175271e12,6.917863829651936e12,3.183148547582819e13,1.0825791495805245e14,2.7646060697452588e14,5.3439199490061275e14,7.825097493435275e14,8.619879647594399e14,7.024798614322576e14,4.105674340973219e14,1.6268776314620662e14,3.913296565848068e13,4.312871417790415e12,0,0,0,0,0,0},{0,0,0,-577.6,-195891.5954785697,-1.720127764558216e7,-6.593956434546355e8,-1.3836051116645775e10,-1.7991428787352527e11,-1.5649809531653018e12,-9.574263945472568e12,-4.260947812998459e13,-1.4108556267746003e14,-3.5246266137808444e14,-6.68904000078248e14,-9.643324596899239e14,-1.0481525855129518e15,-8.443224624053235e14,-4.884650317232749e14,-1.9181971314970422e14,-4.577208698235634e13,-5.008495840014676e12,0,0,0,0,0},{0,0,0,1,2026.6651003957916,443769.2062382361,3.1848062618839644e7,1.084383682742406e9,2.1033538298344963e10,2.5857382719851614e11,2.1562791411962607e12,1.2764669581045951e13,5.532758658800373e13,1.7926934003434116e14,4.398155867559162e14,8.21954813552544e14,1.1694384435544125e15,1.2565957550652905e15,1.0021077247917248e15,5.746217280770576e14,2.2387644402300447e14,5.304452412379179e13,5.767358846077505e12,0,0,0,0},{0,0,0,0,-3.4995670995670998,-4566.139539696959,-817050.1240826164,-5.210286556396761e7,-1.6407138496930685e9,-3.010064457529331e10,-3.548915820326058e11,-2.864643885149056e12,-1.6520807008205463e13,-7.0090278456826984e13,-2.2307320285268588e14,-5.3903548690406256e14,-9.943274230202304e14,-1.3987464189591615e15,-1.488142833281783e15,-1.1763925899836822e15,-6.693106216369468e14,-2.589498048063432e14,-6.0969222087105055e13,-6.591267252660006e12,0,0,0}},{{0.5333333333333333,-75.53333333333333,10829.4,8.072048354127319e6,4.87250210500034e8,1.2400772896941032e10,1.7659692941512448e11,1.5987019273419814e12,9.889499268944688e12,4.3725671416071375e13,1.4222478427288353e14,3.464460612721393e14,6.378941277981012e14,8.892327870204825e14,9.324932431776439e14,7.236581421029889e14,4.029765524699275e14,1.5224983637304378e14,3.494694340003704e13,3.678625621056531e12,0,0,0,0,0,0,0},{0,0.13333333333333333,-115.13333333333334,-234357.4023199112,-2.6202969245825373e7,-1.0729925112854352e9,-2.264892860478733e10,-2.8833628269220435e11,-2.421334407948635e12,-1.4190732511616314e13,-6.023738501346369e13,-1.897848476488173e14,-4.506023042846216e14,-8.123862868785152e14,-1.1127081259761939e15,-1.1495259687556288e15,-8.80694584160074e14,-4.849782163048779e14,-1.8144558258461128e14,-4.128940136737588e13,-4.312871417790415e12,0,0,0,0,0,0},{0,0,0.2,2788.9909571394373,757954.1832518226,5.731896737696369e7,1.9467522753006718e9,3.6752178916813446e10,4.342367668300395e11,3.456417626472393e12,1.945586960892444e13,8.002395364327356e13,2.45820394128803e14,5.716362792738416e14,1.0128387071687611e15,1.366953901980593e15,1.394401321620245e15,1.0566084586443459e15,5.762621684739365e14,2.1376666367087962e14,4.827633490236368e13,5.008495840014676e12,0,0,0,0,0},{0,0,0,-6.330200791583441,-9055.326402164885,-1.652817817493035e6,-1.0353224612455417e8,-3.1443424489415655e9,-5.5099878956043625e10,-6.172115086692606e11,-4.719665182766741e12,-2.5747979102863504e13,-1.0327781515594061e14,-3.107840698734769e14,-7.103751521762541e14,-1.240428258290523e15,-1.6532690684504498e15,-1.6682200228770388e15,-1.2521020933688375e15,-6.771547917071688e14,-2.4931732630126084e14,-5.592820354683055e13,-5.767358846077505e12,0,0,0,0},{0,0,0,-0.0008658008658008658,20.90332181816109,19761.47677528686,2.9786440888161287e6,1.6669440060546213e8,4.697988868588864e9,7.804846936458011e10,8.399601182809939e11,6.225772187860445e12,3.3126459614313613e13,1.3018123394897244e14,3.8510657020790756e14,8.676089997567239e14,1.4962826923778152e15,1.9729018315606848e15,1.9720423433634065e15,1.4678534354359658e15,7.87971726203723e14,2.8819855324002194e14,6.4264855713435055e13,6.591267252660006e12,0,0,0}}},\n  {{{0,-0.26666666666666666,42.666666666666664,-2.9489904e6,-2.4170614799515897e8,-7.873218384663625e9,-1.405806642582533e11,-1.580544280937379e12,-1.2090386727568768e13,-6.6042366119374336e13,-2.65868882310363e14,-8.051739775983438e14,-1.857133409027708e15,-3.2801533762918805e15,-4.4314102156757605e15,-4.540487334262183e15,-3.465694635513561e15,-1.9084456023722588e15,-7.161903536719025e14,-1.6388911387603578e14,-1.725148567116166e13,0,0,0,0,0,0},{0,0,-0.06666666666666667,69509,1.060304375629801e7,5.578470160261151e8,1.479270594762511e10,2.3394997790297177e11,2.4264918279035615e12,1.7521030242218191e13,9.164276368056673e13,3.5664655366664625e14,1.05113206044863e15,2.370956512005569e15,4.1103527541472025e15,5.465870295900822e15,5.524844013590857e15,4.167585692773008e15,2.2713467255841065e15,8.446251275105141e14,1.917140907650062e14,2.0033983360058703e13,0,0,0,0,0},{0,0,0,-647.6,-246094.15250627516,-2.4198988932992294e7,-1.038980191421611e9,-2.444203632713007e10,-3.570077983612116e11,-3.498184851505777e12,-2.4202280989027707e13,-1.2243539925589284e14,-4.638933812042697e14,-1.3374968892379465e15,-2.9619773218874905e15,-5.055557384458424e15,-6.63336672836086e15,-6.627413577564161e15,-4.948584988121854e15,-2.672802925395651e15,-9.85971732202283e14,-2.2219508817519653e14,-2.306943538431002e13,0,0,0,0},{0,0,0,1,2271.693671824363,557112.5844066336,4.4760097703188986e7,1.7065933499774168e9,3.710782171508295e10,5.123704928625934e11,4.812792440204033e12,3.2217907329529703e13,1.587313473404659e14,5.885027219728265e14,1.6662726075609255e15,3.6337254670711775e15,6.120647631772677e15,7.939169799895172e15,7.852589702803345e15,5.811463115882084e15,3.1140914617754305e15,1.1406361435129525e15,2.5541160604057522e14,2.6365069010640023e13,0,0,0},{0,0,0,0,-3.4996138996138995,-5117.489627447047,-1.0252931044630904e6,-7.317764631938808e7,-2.5799387735712266e9,-5.305164082877038e10,-7.024571823174719e11,-6.386343641756565e12,-4.1646702455182195e13,-2.0082495548407444e14,-7.313240134063729e14,-2.0393725447628995e15,-4.389582156680698e15,-7.310312322718411e15,-9.388355782488322e15,-9.204613694406318e15,-6.75889981067322e15,-3.596449851906305e15,-1.3090123157014138e15,-2.9144089798248025e14,-2.9927916174240027e13,0,0}},{{0.5333333333333333,-84.86666666666666,13652.733333333334,1.1580743990317933e7,7.862366731187782e8,2.251285321019336e10,3.6126970425140875e11,3.6950296972946763e12,2.591996982109244e13,1.3060390853963811e14,4.872588592243463e14,1.3728326184824732e15,2.955623641337104e15,4.887098724271143e15,6.197174104927707e15,5.97435514747484e15,4.3000613327664335e15,2.2374288971381245e15,7.949002570465775e14,1.725148567116166e14,1.725148567116166e13,0,0,0,0,0,0},{0,0.13333333333333333,-129.13333333333333,-298814.1125960173,-3.756041742575181e7,-1.7285393875937688e9,-4.103692427927973e10,-5.885977025139501e11,-5.58378211235412e12,-3.710691956026596e13,-1.7949438370340738e14,-6.486221072613646e14,-1.7811709048134368e15,-3.7547456752020125e15,-6.099911491230204e15,-7.620161622284745e15,-7.252241847091468e15,-5.161779213919494e15,-2.65959108326093e15,-9.367258010130061e14,-2.0173108244503553e14,-2.0033983360058703e13,0,0,0,0,0},{0,0,0.2,3164.105012550278,966660.7328705739,8.210917088666102e7,3.1326839561683073e9,6.649903112671311e10,8.850518794573038e11,7.957226405896327e12,5.07826266196542e13,2.3800231954847772e14,8.384811979980429e14,2.2550231205737835e15,4.671485049782065e15,7.477796091175499e15,9.223459380199674e15,8.681741711096794e15,6.119697195173343e15,3.1262899720312225e15,1.0927437571553231e15,2.3372980586735153e14,2.306943538431002e13,0,0,0,0},{0,0,0,-6.387343648726298,-10280.88273895997,-2.1078979265578426e6,-1.482407551004836e8,-5.055907877161912e9,-9.959690402930423e10,-1.2564964279090027e12,-1.0850994538243352e13,-6.710880700339022e13,-3.066885058028235e14,-1.0583451011894089e15,-2.7975704791687135e15,-5.711120851896802e15,-9.02764697095337e15,-1.10141153245736e16,-1.0268428227212878e16,-7.177155441784758e15,-3.6389915826190465e15,-1.2633984960937452e15,-2.6859414054589525e14,-2.6365069010640023e13,0,0,0},{0,0,0,-0.0007722007722007722,21.103579218418492,22442.61268540996,3.7985964582853154e6,2.3859794710035256e8,7.549734457672135e9,1.4096953641872162e11,1.7083678819651787e12,1.429842236994991e13,8.623782828471784e13,3.860843010721173e14,1.3096501921718585e15,3.4118527798952095e15,6.87871387556754e15,1.0756091873106256e16,1.2998921089798014e16,1.2017683521567574e16,8.337385441942756e15,4.199116583526632e15,1.4491212804099838e15,3.0640485606960025e14,2.9927916174240027e13,0,0}}},\n  {{{0,-0.26666666666666666,47.6,-4.0895524e6,-3.731306038384463e8,-1.3541340916361624e10,-2.698066970601489e11,-3.3929623532272686e12,-2.912501505968892e13,-1.792856932147624e14,-8.178001345526671e14,-2.825665552462115e15,-7.50119392296909e15,-1.5421119083184214e16,-2.46069867650868e16,-3.036845308130471e16,-2.8690118845028184e16,-2.035084821245431e16,-1.0484659979090722e16,-3.7026001121730715e15,-8.013593344023481e14,-8.013593344023481e13,0,0,0,0,0},{0,0,-0.06666666666666667,86351.4,1.4665579721461162e7,8.590687659123018e8,2.5382910206863052e10,4.4799089906092065e11,5.197549704634442e12,4.2116936060807555e13,2.4826316584141612e14,1.0947767893988114e15,3.681377038739813e15,9.557508586062312e15,1.928610504650084e16,3.02919821648759e16,3.68806750791929e16,3.4434258261705336e16,2.417434564050942e16,1.2341328990945152e16,4.323002238921354e15,9.288483194209035e14,9.227774153724008e13,0,0,0,0},{0,0,0,-721.6,-305383.484221278,-3.3425150558028962e7,-1.5976051302013426e9,-4.187439493752403e10,-6.825325957092296e11,-7.480870721296626e12,-5.808146779339898e13,-3.311329496999199e14,-1.421626589092943e15,-4.676542004989115e15,-1.1920141073940436e16,-2.3681684177511636e16,-3.670117139829947e16,-4.416718389230369e16,-4.0819063755733816e16,-2.8399638601169932e16,-1.4382537224523932e16,-5.001926254562189e15,-1.067785294930921e15,-1.054602760425601e14,0,0,0},{0,0,0,1,2530.72069885139,690925.9210848194,6.1773305205390684e7,2.6215136024899797e9,6.350186022610584e10,9.783737218417522e11,1.0279134468481693e13,7.721651964503972e13,4.287219179136568e14,1.8010359258476095e15,5.818025681991426e15,1.460298629964141e16,2.8630185446713732e16,4.3862993135598696e16,5.225649481076826e16,4.7866759281312376e16,3.3039905722220736e16,1.6614027170527334e16,5.741105116040728e15,1.218493729951201e15,1.1971166469696011e14,0,0},{0,0,0,0,-3.4996534996535,-5700.334296291716,-1.2710928628651593e6,-1.0093436202872059e8,-3.9601706432110143e9,-9.070909301290082e10,-1.3400853937505337e12,-1.3626158227398803e13,-9.970831360069828e13,-5.418127541007572e14,-2.2355599883931558e15,-7.112384380705038e15,-1.7619359196305414e16,-3.415306825634278e16,-5.180484759888663e16,-6.1176142561382664e16,-5.559884636602166e16,-3.810791930105565e16,-1.904137287813753e16,-6.54222329112722e15,-1.3812884388110782e15,-1.350593140170832e14,0}},{{0.5333333333333333,-94.73333333333333,16992.6,1.6284696076892616e7,1.2354648054405298e9,3.953909107382076e10,7.10090176204812e11,8.145748277303172e12,6.428556882265668e13,3.6590982390426875e14,1.5502704689894388e15,4.993707604738265e15,1.2398036774129854e16,2.3903947362329856e16,3.5861500931598584e16,4.170569864198567e16,3.720611350142881e16,2.496972481358118e16,1.2193217903736838e16,4.0882542918542015e15,8.414273011224655e14,8.013593344023481e13,0,0,0,0,0},{0,0.13333333333333333,-143.93333333333334,-375927.24292232457,-5.278450853716055e7,-2.7125229474487467e9,-7.195565577844536e10,-1.1548552468425483e12,-1.2286437334588822e13,-9.185201223250092e13,-5.0188188887196794e14,-2.0594686450678228e15,-6.465664297334423e15,-1.5717197545820644e16,-2.9773055055612476e16,-4.400198164051586e16,-5.051762663082156e16,-4.456554705592129e16,-2.961652536463062e16,-1.4337322088783526e16,-4.770124322093573e15,-9.749871901895235e14,-9.227774153724008e13,0,0,0,0},{0,0,0.2,3564.768442556024,1.2164908134451648e6,1.1533096610258973e8,4.911578657437878e9,1.1646951484526044e11,1.7342546082017852e12,1.7484020896956271e13,1.2551336178335739e14,6.644159134672428e14,2.6578960250785765e15,8.171790366631361e15,1.9520413309070224e16,3.643312168795762e16,5.316310532259258e16,6.036333626184364e16,5.273684513072426e16,3.4747435047530596e16,1.6693177509884468e16,5.516045100269669e15,1.120515432952201e15,1.054602760425601e14,0,0,0},{0,0,0,-6.441397702780352,-11590.853392628731,-2.6527484863642906e6,-2.081436499095543e8,-7.921865093783669e9,-1.742928669332534e11,-2.459659492823631e12,-2.3815795421580676e13,-1.6566332438371656e14,-8.550505095885778e14,-3.35024333679381e15,-1.0123366574848608e16,-2.382922456508392e16,-4.391688037572645e16,-6.338437811869518e16,-7.128043872024897e16,-6.174789017813265e16,-4.037820760670786e16,-1.9267335122747188e16,-6.327906043885648e15,-1.2783495622996812e15,-1.1971166469696011e14,0,0},{0,0,0,-0.000693000693000693,21.292986207825482,25309.29515631317,4.780381324186655e6,3.3492374593636465e8,1.1823731636670485e10,2.4653622561126352e11,3.3416154029679727e12,3.135410116283323e13,2.126729757114602e14,1.0752440457778672e15,4.1409771925451395e15,1.233117595422076e16,2.8664288229529544e16,5.225580879389953e16,7.470388245200797e16,8.330526615716496e16,7.162566653980231e16,4.652411455487196e16,2.2066154752113164e16,7.207543889154556e15,1.4488180958196198e15,1.350593140170832e14,0}}},\n  {{{0,-0.26666666666666666,52.8,-5575520,-5.632173785675783e8,-2.264573851170883e10,-5.005643706894967e11,-6.997344021760841e12,-6.694984221034684e13,-4.609967645533807e14,-2.3627840501606355e15,-9.225264284921132e15,-2.7870762591869724e16,-6.579211913432579e16,-1.219269835005012e17,-1.7736434748785085e17,-2.014372433713144e17,-1.7653028386935773e17,-1.1695051216654723e17,-5.660662707425071e16,-1.887747613881895e16,-3.8756651445640835e15,-3.691109661489603e14,0,0,0,0},{0,0,-0.06666666666666667,106080.2,1.9949649623509426e7,1.2939911180306041e9,4.2363534986906364e10,8.295293999689657e11,1.0698737964202242e13,9.66363247645834e13,6.372094391758614e14,3.1574388239306565e15,1.1998128373402768e16,3.545022868007551e16,8.214242163461622e16,1.498448307438975e17,2.1504119173799283e17,2.413696663998524e17,2.0935359957837e17,1.3743658725905952e17,6.598443928517343e16,2.1845367673730504e16,4.455696662798164e15,4.218411041702404e14,0,0,0},{0,0,0,-799.6,-374795.69873168645,-4.541501586740491e7,-2.4033232898026366e9,-6.979280795787791e10,-1.2620648339616711e12,-1.5377051478454482e13,-1.3307719247169702e14,-8.486965787241058e14,-4.0942474686362435e15,-1.521973082413072e16,-4.415040755395909e16,-1.0071975616913048e17,-1.8128971785509302e17,-2.5715892674943798e17,-2.8571552176641878e17,-2.4559351140219373e17,-1.5993872560572854e17,-7.623776610305851e16,-2.5076921618640444e16,-5.084895471889925e15,-4.7884665878784044e14,0,0},{0,0,0,1,2803.746339877031,847540.594948172,8.387088415905303e7,3.940171303117425e9,1.057365813099728e11,1.8072133228725144e12,2.1105818071761484e13,1.7671976487014494e14,1.0975457428896409e15,5.180820146125421e15,1.8912018709994548e16,5.402178465503179e16,1.2161716004687645e17,2.1639908949667334e17,3.038797903142797e17,3.3462662569227334e17,2.853611054467527e17,1.845190954017643e17,8.739229013859099e16,2.857951555540064e16,5.764577084638233e15,5.402372560683328e14,0},{0,0,0,0,-3.499687304565353,-6314.674103265669,-1.5587237516807944e6,-1.3697283658064875e8,-5.948433133232432e9,-1.5092828425611444e11,-2.4733417883407153e12,-2.7953736163984633e13,-2.2798507703929834e14,-1.3857335293027922e15,-6.424397479638844e15,-2.30959575834661e16,-6.511275177576189e16,-1.4492407203283318e17,-2.5530540726016147e17,-3.553604828117912e17,-3.882498975703751e17,-3.287640702824053e17,-2.1123805647598467e17,-9.947298157155096e16,-3.2360331425024412e16,-6.496023591260685e15,-6.06119848271788e14}},{{0.5333333333333333,-105.13333333333334,20908.2,2.2492357135156598e7,1.8958309719109235e9,6.741542072724315e10,1.3467332712719966e12,1.721588784468186e13,1.517969171625762e14,9.68623788198903e14,4.620809634242403e15,1.6852457171265348e16,4.7703107096260296e16,1.0578958618367114e17,1.8461804798071936e17,2.5344496869855994e17,2.7218267219337744e17,2.2596420895370938e17,1.4205763700894958e17,6.535328208213089e16,2.074610040494806e16,4.0602206276385635e15,3.691109661489603e14,0,0,0,0},{0,0.13333333333333333,-159.53333333333333,-467299.4470188559,-7.287456290234995e7,-4.157830371714083e9,-1.2252070190736252e11,-2.186990850552188e12,-2.5926110989630664e13,-2.1653318849256094e14,-1.326322841173958e15,-6.127978836743251e15,-2.1781741790169716e16,-6.036681088296472e16,-1.3152785185627565e17,-2.2611628841057744e17,-3.06435432704922e17,-3.2542265994529766e17,-2.6752084016889734e17,-1.667276585666764e17,-7.611122760127998e16,-2.3994120798097664e16,-4.666617214883284e15,-4.218411041702404e14,0,0,0},{0,0,0.2,3991.1974633728914,1.5126843075902709e6,1.591637552639756e8,7.523047958345665e9,1.981274589389424e11,3.28062880467928e12,3.684945335931074e13,2.9550727863338325e14,1.753482804920874e15,7.897512890513422e15,2.748953095986418e16,7.486280786775282e16,1.6070534554507542e17,2.7277027276789126e17,3.6558244328737126e17,3.8447574794727104e17,3.133601408355606e17,1.9380644168944502e17,8.786770088566074e16,2.7529585606062684e16,5.324318801283845e15,4.7884665878784044e14,0,0},{0,0,0,-6.4926797540624035,-12985.995991128793,-3.298836226795991e6,-2.871680700153716e8,-1.2127517294150885e10,-2.9628375638388446e11,-4.648967460183313e12,-5.014705791678989e13,-3.896342464153759e14,-2.254104416220173e15,-9.943117755509682e15,-3.401307144070906e16,-9.127203209515408e16,-1.934632666986177e17,-3.247778760884149e17,-4.311079790213169e17,-4.495396895431404e17,-3.636200897900059e17,-2.2336772996888845e17,-1.0065183695567805e17,-3.1360509612206944e16,-6.034695712672399e15,-5.402372560683328e14,0},{0,0,0,-0.0006253908692933083,21.472659314327856,28363.229946619325,5.944698143021086e6,4.619832444694353e8,1.809376056308954e10,4.1886637559539825e11,6.31178461726564e12,6.596977935869714e13,4.997768062476191e14,2.8319808910505985e15,1.2277848441468622e16,4.138797722399096e16,1.0967212084846331e17,2.299374395484744e17,3.823298988047106e17,5.032265327743345e17,5.208059242745585e17,4.1843448110868006e17,2.5548313610900355e17,1.1449196659647726e17,3.5494695749103796e16,6.799083515396579e15,6.06119848271788e14}}}\n};\n\nstatic const double ImEI[21][2][5][27] =\n {\n  {{{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,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,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,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,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,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  {{{0,-0.03333333333333333,-0.06666666666666667,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.3,-0.26666666666666666,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.5,2.1333333333333333,1.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,0,-1.8333333333333333,-6.133333333333334,-4.266666666666667,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.03333333333333333,4.2,12.8,8.533333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.06666666666666667,0.06666666666666667,0.06666666666666667,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,0.43333333333333335,0.26666666666666666,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.2666666666666666,-2.933333333333333,-1.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,0.16666666666666666,4.1,8.266666666666667,4.266666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.06666666666666667,-0.4666666666666667,-9,-17.066666666666666,-8.533333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.06666666666666667,1.0666666666666667,1.0666666666666667,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,-2.6,-9.066666666666666,-6.4,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,11.6,27.733333333333334,17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-3.533333333333333,-29.066666666666666,-59.733333333333334,-34.13333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.009523809523809525,8,56.68571428571428,107.27619047619048,58.51428571428571,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.13333333333333333,-0.26666666666666666,-1.6,-1.0666666666666667,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.2,5.933333333333334,12.266666666666667,6.4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-3.2,-22.266666666666666,-36.266666666666666,-17.066666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.06666666666666667,10.066666666666666,52.53333333333333,76.8,34.13333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.01904761904761905,-0.26666666666666666,-21.714285714285715,-99.35238095238095,-136.53333333333333,-58.51428571428571,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.1,12.6,38.4,25.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,-8.9,-64.26666666666667,-123.73333333333333,-68.26666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,1.5,35.2,170.13333333333333,273.06666666666666,136.53333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-5.271428571428571,-84,-341.9428571428571,-497.37142857142857,-234.05714285714285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0015873015873015873,11.88888888888889,159.4920634920635,589.6126984126984,806.1968253968254,364.0888888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.2,-1.4,-27,-51.2,-25.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.3,24.233333333333334,113.33333333333333,157.86666666666667,68.26666666666667,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-5.4,-81.86666666666666,-281.06666666666666,-341.3333333333333,-136.53333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.04285714285714286,17.1,183.65714285714284,546.7428571428571,614.4,234.05714285714285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0031746031746031746,-0.15555555555555556,-36.93015873015873,-337.26984126984127,-924.4444444444445,-988.2412698412699,-364.0888888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.13333333333333333,59.733333333333334,332.8,546.1333333333333,273.06666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-21.2,-260.26666666666665,-921.6,-1228.8,-546.1333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,2,79.88571428571429,648.5333333333333,1896.8380952380953,2262.552380952381,936.2285714285714,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-7.015873015873016,-186.48888888888888,-1262.120634920635,-3319.0603174603175,-3692.9015873015874,-1456.3555555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0005772005772005772,15.806060606060607,349.617316017316,2133.1116883116883,5241.461471861472,5560.630303030303,2118.3353535353535,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.26666666666666666,-3.7333333333333334,-149.33333333333334,-554.6666666666666,-682.6666666666666,-273.06666666666666,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.4,64.13333333333334,541.8666666666667,1433.6,1501.8666666666666,546.1333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-7.771428571428571,-209.86666666666667,-1260.4952380952382,-2852.5714285714284,-2730.6666666666665,-936.2285714285714,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.031746031746031744,24.755555555555556,462.5015873015873,2365.765079365079,4892.444444444444,4421.079365079365,1456.3555555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0011544011544011544,-0.11313131313131314,-53.58268398268398,-839.8961038961039,-3909.818181818182,-7624.588744588745,-6619.79797979798,-2118.3353535353535,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.16666666666666666,191.66666666666666,1668.2666666666667,4753.066666666667,5461.333333333333,2184.5333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-41.5,-775.8095238095239,-4313.6,-9976.685714285713,-10142.47619047619,-3744.9142857142856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,2.5,152.63492063492063,1872.888888888889,8569.092063492064,17664.812698412698,16644.06349206349,5825.422222222222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-8.762626262626263,-352.2020202020202,-3580.7676767676767,-14673.19595959596,-28117.59191919192,-25155.23232323232,-8473.341414141414,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0002775002775002775,19.731934731934732,655.7495837495837,5983.52047952048,22836.666267066266,41605.430303030305,35848.75213675214,11732.318881118881,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.3333333333333333,-7.666666666666667,-528.2,-3225.6,-7074.133333333333,-6553.6,-2184.5333333333333,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.5,135.11904761904762,1809.8666666666666,7751.314285714286,14345.752380952381,12014.933333333332,3744.9142857142856,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-10.26984126984127,-436.8888888888889,-4089.930158730159,-14826.869841269841,-24894.577777777777,-19556.774603174603,-5825.422222222222,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.025252525252525252,32.86363636363637,955.6767676767677,7544.371717171717,24809.761616161617,39106.45656565657,29391.90303030303,8473.341414141414,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.000555000555000555,-0.08935508935508936,-71.26440226440226,-1727.040959040959,-12325.773426573427,-38017.645288045285,-57329.99658119658,-41714.91157731158,-11732.318881118881,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.2,489.6,6106.971428571429,26214.4,50556.34285714286,44938.97142857143,14979.657142857142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-71.8,-1901.8920634920635,-15197.155555555555,-53000.93968253968,-90398.06984126984,-74066.08253968254,-23301.68888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,3,260.43463203463205,4506.052525252525,29564.416738816737,91759.85685425685,144822.26608946608,112271.77373737373,33893.36565656566,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-10.51048951048951,-596.8428904428904,-8522.413053613054,-49961.41351981352,-143888.94731934732,-215275.8303030303,-160341.69137529138,-46929.275524475524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0001554001554001554,23.661849261849262,1106.6579642579643,14141.066045066045,77056.03978243978,210652.15602175603,303084.9044289044,219003.28578088578,62572.367365967366,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.4,-13.6,-1447.5428571428572,-13136,-44470.857142857145,-70217.14285714286,-52428.8,-14979.657142857142,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.6,247.18412698412698,4825.422222222222,30428.749206349206,86497.11746031746,123062.04444444444,85716.92698412699,23301.68888888889,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-12.86926406926407,-795.3656565656565,-10742.07215007215,-56978.655122655124,-146297.53535353535,-194603.1468975469,-129218.45656565657,-33893.36565656566,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.02097902097902098,41.329137529137526,1733.909557109557,19631.14219114219,94030.1724941725,225862.41491841493,286647.4368298368,183806.32913752913,46929.275524475524,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0003108003108003108,-0.07397047397047397,-89.75540015540015,-3126.114063714064,-31870.1898989899,-142693.88282828283,-326997.6515928516,-400854.2284382284,-250289.46946386946,-62572.367365967366,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.23333333333333334,1074.7333333333333,18153.244444444445,108464.35555555555,307655.1111111111,449285.68888888886,326223.64444444445,93206.75555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-114.1,-4069.7131313131313,-44104.92121212121,-214282.8606060606,-537693.0909090909,-723676.3151515152,-495690.4727272727,-135573.46262626263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,3.5,410.27630147630146,9526.587723387724,84649.39362859362,365700.5550893551,848556.257964258,1.0798214464646464e6,709153.4968142968,187717.1020979021,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-12.25897435897436,-936.1311577311577,-17890.690287490288,-141813.89526029525,-567884.4618492619,-1.2480050523698523e6,-1.524549659052059e6,-969871.6941724942,-250289.46946386946,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0000959824489236254,27.593994241053064,1731.1486631016044,29547.359934183463,217402.40296174414,825519.0614561909,1.7431695357191828e6,2.0641979866721514e6,1.280893167256273e6,323904.019306184,0,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.4666666666666667,-21.933333333333334,-3355.6222222222223,-42348.08888888889,-203582.57777777777,-479869.15555555554,-594921.2444444444,-372827.02222222224,-93206.75555555556,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.7,410.7262626262626,11022.440404040404,96074.47272727273,386563.103030303,819299.296969697,946101.5272727272,563477.2040404041,135573.46262626263,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-15.552602952602953,-1319.1589743589743,-24329.85236985237,-177655.00916860916,-643821.182905983,-1.273099178865579e6,-1.3992012382284382e6,-803012.0478632479,-187717.1020979021,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.017948717948717947,50.09052059052059,2871.1639471639473,44222.36891996892,290731.34296814294,983393.9045843045,1.851893577000777e6,1.9625562306138305e6,1.0950164289044288e6,250289.46946386946,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0001919648978472508,-0.06315645139174551,-108.91493212669683,-5170.4839160839165,-71524.53607568901,-438562.81431509665,-1.4125950964486494e6,-2.565467062004662e6,-2.6439125666803783e6,-1.4428451769093652e6,-323904.019306184,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.26666666666666666,2116.266666666667,46478.36998556999,367133.99595959595,1.4218853217893217e6,3.00440476998557e6,3.5321728923520925e6,2.169175402020202e6,542293.8505050505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-170.4,-7878.639405039405,-111119.03776223776,-714162.356043956,-2.447790332334332e6,-4.76751157962038e6,-5.287365042424242e6,-3.1077609125097124e6,-750868.4083916084,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,4,609.1538683538683,18291.647552447554,211297.13166833165,1.2068968343656345e6,3.8238280972360973e6,7.039391328671329e6,7.482612264180264e6,4.254920980885781e6,1.0011578778554779e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-14.007843137254902,-1385.7954019836372,-34183.56217377394,-351855.81673751085,-1.8614895882261528e6,-5.582810520261438e6,-9.861435769605558e6,-1.0149606059509119e7,-5.624151607952831e6,-1.295616077224736e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.0000635071842502183,31.527506549178376,2558.0482279330577,56272.57883911754,537113.6969927904,2.6926125869276235e6,7.7549386769432835e6,1.3272122169070559e7,1.3318034401472213e7,7.228173904516948e6,1.6365676764944035e6,0,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.5333333333333333,-33.06666666666667,-6906.606637806638,-115649.93939393939,-746992.1893217893,-2.4357735803751806e6,-4.414610876767676e6,-4.515080496392496e6,-2.440322327272727e6,-542293.8505050505,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.8,636.47881007881,22496.094483294484,259039.60572760573,1.3972491252747253e6,4.090821281118881e6,6.898882843023643e6,6.700457672105672e6,3.483195116705517e6,750868.4083916084,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-18.30773670773671,-2043.3143745143745,-49402.28837828838,-475220.8388944389,-2.3042702023310023e6,-6.28517082917083e6,-1.0076758327894328e7,-9.422355652525252e6,-4.75549991981352e6,-1.0011578778554779e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.01568627450980392,59.10452945747063,4444.078065725124,89495.53131313132,773530.6577448695,3.495176651510581e6,9.067520102966314e6,1.400317438010878e7,1.2718753849005897e7,6.2719596465652e6,1.295616077224736e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.0001270143685004366,-0.05512423592918949,-128.64458637337896,-7998.43481348373,-144411.5393015653,-1.162342659386434e6,-4.9947002896331735e6,-1.2482808925253488e7,-1.8729284982381087e7,-1.6625264914387986e7,-8.04645774276415e6,-1.6365676764944035e6,0,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.3,3839.4,106318.05314685314,1.0702797874125873e6,5.392764956643357e6,1.5350685180419581e7,2.580816845874126e7,2.539460421818182e7,1.3515631351048952e7,3.0034736335664336e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-242.7,-14124.181152181152,-251299.74452214452,-2.0592303781551782e6,-9.185474911222111e6,-2.4106659233033635e7,-3.8236604926806524e7,-3.601039741911422e7,-1.852142074032634e7,-4.0046315114219114e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,4.5,864.0628077804548,32599.88898944193,474679.1184031655,3.455516380591957e6,1.4245058289412549e7,3.533020329192376e7,5.370181410996846e7,4.892054909771013e7,2.449892218752228e7,5.182464308898944e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-15.756965944272446,-1961.5696724328304,-60708.62697178982,-786988.0535711966,-5.303549502574206e6,-2.0688066335863516e7,-4.921807658793219e7,-7.241782910112797e7,-6.427015135233064e7,-3.1503927772517268e7,-6.546270705977614e6,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.00004422821760283061,35.461919504643966,3616.1956285200868,99702.79558212686,1.1976321362253844e6,7.643730469367165e6,2.8621924200018764e7,6.595342171821161e7,9.458539786739412e7,8.219612447821817e7,3.9589351412340805e7,8.104906588353236e6,0,0,0,0,0,0,0,0,0,0,0,0}},{{0.6,-47.4,-13003.506293706294,-278936.1678321678,-2.3265602237762236e6,-9.980674506293707e6,-2.4597585633566435e7,-3.625286534265734e7,-3.158926858741259e7,-1.5017368167832168e7,-3.0034736335664336e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0.9,935.4623043623044,42143.00606060606,619613.8922410923,4.308818503096903e6,1.6579424074592074e7,3.799234747545788e7,5.314447145174825e7,4.452023938088578e7,2.0523736496037297e7,4.0046315114219114e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-21.12561556090968,-3003.887892499657,-92251.03664962489,-1.130768793872794e6,-7.0588418527355e6,-2.5277949085518405e7,-5.502439582942548e7,-7.405439677762237e7,-6.019829813355272e7,-2.7090154341971755e7,-5.182464308898944e6,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.01393188854489164,68.33903526813744,6531.570529779818,166762.43296022862,1.8339761297278572e6,1.0656330035982594e7,3.6261947787464544e7,7.597502823061767e7,9.92596311986317e7,7.879468948121847e7,3.477706312550607e7,6.546270705977614e6,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.00008845643520566122,-0.04891640866873065,-148.871487026905,-11752.440015711842,-268684.86844919785,-2.74861261174986e6,-1.5174070609897017e7,-4.97030496228197e7,-1.0110656940201923e8,-1.2902029170934741e8,-1.0047113019907235e8,-4.364180470651743e7,-8.104906588353236e6,0,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.3333333333333333,6533.333333333333,222654.46091686093,2.778624556954157e6,1.7600566104118105e7,6.443886523574203e7,1.4451080095726496e8,2.0156645274156955e8,1.7053055852804974e8,8.009263022843823e7,1.6018526045687646e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-333,-23825.97413044472,-521880.007239819,-5.303014873879062e6,-2.974522501247772e7,-1.0042604004409708e8,-2.1250665797888386e8,-2.837202903655926e8,-2.319741694631016e8,-1.0600495177293295e8,-2.0729857235595778e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,5,1181.999610984131,54754.40911616763,980895.6130251585,8.852088141103067e6,4.5880486491885826e7,1.4637328059863028e8,2.967933223074854e8,3.832574936190464e8,3.0507729190651166e8,1.3638063970786697e8,2.6185082823910456e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-17.506265664160402,-2679.1916209005994,-101698.47428388605,-1.6209314727333898e6,-1.3536137961823797e7,-6.6368244028934255e7,-2.0306241420633388e8,-3.984997390155933e8,-5.012577425662849e8,-3.9049673738068247e8,-1.7144994706131846e8,-3.2419626353412945e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.000032049433045529426,39.39695786781532,4934.437997592661,166727.4017375813,2.460969788432378e6,1.945561056293358e7,9.154241300341979e7,2.712219032798776e8,5.1868580194167066e8,6.387408437835916e8,4.888559794904897e8,2.114323457831279e8,3.946737121285054e7,0,0,0,0,0,0,0,0,0,0,0}},{{0.6666666666666666,-65.33333333333333,-22842.255167055166,-610794.4205128205,-6.384583827195027e6,-3.4760849444599845e7,-1.1109825668873349e8,-2.199600400907537e8,-2.733161006545454e8,-2.075734000087024e8,-8.810189325128205e7,-1.6018526045687646e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,1,1318.9482608894373,73805.46920791626,1.3491923253530783e6,1.1743463056410257e7,5.730339427900727e7,1.7019760844996572e8,3.196860685490927e8,3.8177487075555557e8,2.810897971178939e8,1.1636988039073084e8,2.0729857235595778e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-23.999221968262216,-4237.817551557489,-161226.06704008067,-2.4533664806920635e6,-1.9149318022051428e7,-8.689604748579635e7,-2.450198244808148e8,-4.4258655082519025e8,-5.1267962114181876e8,-3.683579087309619e8,-1.4947318111982217e8,-2.6185082823910456e7,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.012531328320802004,77.76920671347916,9214.54894194213,291039.0946701733,3.9691610755076366e6,2.8811933867454045e7,1.2415265791155085e8,3.367590805724054e8,5.902267861563345e8,6.673985861532614e8,4.701430309700768e8,1.8765976023802492e8,3.2419626353412945e7,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.00006409886609105885,-0.04397182213846637,-169.53941844936594,-16578.624554339185,-468446.0812958478,-5.9377916416310035e6,-4.0923115567559e7,-1.696412196057358e8,-4.465302941617743e8,-7.640784094103152e8,-8.472253347457047e8,-5.871720202796443e8,-2.3116603138955316e8,-3.946737121285054e7,0,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.36666666666666664,10559.266666666666,434294.7652086476,6.574489355455003e6,5.10204267471091e7,2.3250794534097537e8,6.64917593632177e8,1.2277649331804926e9,1.4624266471674209e9,1.0850784646757164e9,4.560568591831071e8,8.291942894238311e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-443.3,-38255.76555264481,-1.0114779064221641e6,-1.2470381821703581e7,-8.571332767814559e7,-3.6026736682270384e8,-9.722698334254823e8,-1.7186034725420127e9,-1.9784568926620624e9,-1.4283144396604905e9,-5.869822733026594e8,-1.0474033129564182e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,5.5,1569.961476844449,87625.69572649573,1.8939060047826958e6,2.0732385792547733e7,1.3166046654165429e8,5.228893340210738e8,1.352134487330338e9,2.311593394202756e9,2.5906866318906407e9,1.8295657646295648e9,7.381699538930948e8,1.2967850541365178e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-19.255693581780537,-3554.4014008396293,-162426.07358635293,-3.1217844988459083e6,-3.16126055748029e7,-1.8987086397373933e8,-7.230764038384147e8,-1.809469262049569e9,-3.0129885415469217e9,-3.304452922123214e9,-2.2917775413786182e9,-9.105686358393375e8,-1.5786948485140216e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.00002397297591805601,43.33244851258581,6541.62812737135,265926.5903814425,4.731094982759278e6,4.5340842546994835e7,2.612763288477679e8,9.633487135585018e8,2.348932232111619e9,3.828700312931434e9,4.124825501869743e9,2.8177588722517457e9,1.105086393959815e9,1.894433818216826e8,0,0,0,0,0,0,0,0,0,0}},{{0.7333333333333333,-87.26666666666667,-37956.79708396179,-1.2378745500251383e6,-1.5839376732026143e7,-1.06514876608547e8,-4.268125048599296e8,-1.084457648638713e9,-1.7937872069180493e9,-1.927228914871795e9,-1.297559501340573e9,-4.9751657365429866e8,-8.291942894238311e7,0,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,1.1,1798.4311052896192,122419.20256145643,2.723675026625387e6,2.8991573532491863e7,1.7462463657162815e8,6.49987059367659e8,1.5663069635696964e9,2.4893757054309864e9,2.5923743423070254e9,1.7021667641938875e9,6.393524389504802e8,1.0474033129564182e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-26.922953688897962,-5782.824037469239,-267059.29319830495,-4.940120546192026e6,-4.71164384933979e7,-2.6376188135589048e8,-9.316258847784076e8,-2.1581550271623797e9,-3.3260680887754226e9,-3.3792200077329974e9,-2.1743360218110523e9,-8.030092065999206e8,-1.2967850541365178e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.011387163561076604,87.37526544737474,12575.685027901907,481630.25459747476,7.978130346470807e6,7.071870426544951e7,3.7573583030587316e8,1.2761080113371363e9,2.867315117761134e9,4.312316120466565e9,4.2944103377974663e9,2.717461330888649e9,9.895033782650386e8,1.5786948485140216e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,-0.00004794595183611202,-0.039938977879481315,-190.60366239225843,-22626.351358782555,-774685.3323404123,-1.1919507515836926e7,-1.0026023448105781e8,-5.122220252548393e8,-1.6875254790953407e9,-3.7006690484617324e9,-5.456117441460743e9,-5.344261556173933e9,-3.3347814351400876e9,-1.1998080848706565e9,-1.894433818216826e8,0,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.4,16358.4,798958.1317110135,1.4416265779644504e7,1.3432181467691442e8,7.434303361587911e8,2.626393853597904e9,6.143042747321345e9,9.628736625974842e9,1.0003891869771244e10,6.609352950962489e9,2.5137679510954037e9,4.189613251825673e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-575.6,-58965.4028822227,-1.851578588510492e6,-2.721337278865888e7,-2.246109735140044e8,-1.1467505597679276e9,-3.823590344956376e9,-8.562024604039784e9,-1.2971313192672047e10,-1.3113370455110611e10,-8.471497747887982e9,-3.1621604781636624e9,-5.187140216546071e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,6,2034.9461170318891,134714.58794018233,3.456620690885645e6,4.5099821439376034e7,3.4389086889063436e8,1.6588888433384745e9,5.299781267706849e9,1.1477806342984741e10,1.6928250138062456e10,1.6740562578044119e10,1.0617264990476751e10,3.9016315541846533e9,6.314779394056087e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0,-21.00521739130435,-4602.940974829003,-249320.1853983052,-5.686343886484196e6,-6.861345311881912e7,-4.947368541065237e8,-2.288195708861567e9,-7.073833639317552e9,-1.4920448268974035e10,-2.1533210358591484e10,-2.091140714317489e10,-1.3059753134332243e10,-4.736084545542065e9,-7.577735272867304e8,0,0,0,0,0,0,0,0,0,0},{0,0,0,0.00001840349666436623,47.2682769726248,8466.622898449223,407758.88048708765,8.605464337728756e6,9.82448816901423e7,6.795288568188525e8,3.042463166214668e9,9.163485366790909e9,1.8918381774570183e10,2.681818912368284e10,2.565065895730421e10,1.5811505960561546e10,5.669268611552575e9,8.981019582657546e8,0,0,0,0,0,0,0,0,0}},{{0.8,-113.6,-60265.06342202689,-2.354631489783282e6,-3.6188869743358806e7,-2.9423519790246165e8,-1.4410252993176384e9,-4.548903494657308e9,-9.579486167460339e9,-1.3604387251458832e10,-1.2876514478657967e10,-7.78768167803846e9,-2.7232486136866875e9,-4.189613251825673e8,0,0,0,0,0,0,0,0,0,0,0,0,0},{0,1.2,2385.605764445393,194164.23206487356,5.166394900690916e6,6.6000166193341576e7,4.804116328378407e8,2.1848240357089143e9,6.539431397703931e9,1.3229596648755936e10,1.8207931242597847e10,1.6804843678929035e10,9.955319107909575e9,3.4215174889909663e9,5.187140216546071e8,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-29.892234063778012,-7677.3315851704865,-423195.2594681369,-9.353395630081888e6,-1.069960803506276e8,-7.235025322877827e8,-3.121175266299493e9,-8.978155548848114e9,-1.7608717027453564e10,-2.363934658860129e10,-2.1376840213692146e10,-1.2449678653930527e10,-4.217370523887458e9,-6.314779394056087e8,0,0,0,0,0,0,0,0,0,0,0},{0,0,0.010434782608695653,97.14108009278826,16699.23576343072,762726.8199599064,1.5085840946795626e7,1.603035047267927e8,1.0283599406244267e9,4.2643450431212244e9,1.1894505365747297e10,2.2759909848626797e10,2.9943147410467827e10,2.6624309126235004e10,1.5285712870737015e10,5.114971309185431e9,7.577735272867304e8,0,0,0,0,0,0,0,0,0,0},{0,0,-0.00003680699332873246,-0.03658615136876006,-212.02777987521023,-30047.89230250005,-1.2262431435256745e6,-2.251696238611375e7,-2.2695429228591478e8,-1.3994804759815316e9,-5.627673799689461e9,-1.5316193575786037e10,-2.8723952643539726e10,-3.7161678742566864e10,-3.257762113150629e10,-1.8477746149163006e10,-6.118319590685452e9,-8.981019582657546e8,0,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.43333333333333335,24459.933333333334,1.39948310974227e6,2.9664356557875972e7,3.264766937613553e8,2.1527966210326242e9,9.174872352635342e9,2.63526819412877e10,5.20431450209414e10,7.080795804555496e10,6.521613086318431e10,3.8830607464800354e10,1.3486564563019785e10,2.0748560866184285e9,0,0,0,0,0,0,0,0,0,0,0,0},{0,0,-731.9,-87814.82467992813,-3.230537622359549e6,-5.578284651915676e7,-5.439079051073674e8,-3.308797228275689e9,-1.3310466229841167e10,-3.660457080636495e10,-6.987539486462099e10,-9.251142702281056e10,-8.331826353464978e10,-4.868864058693869e10,-1.6643954260047829e10,-2.5259117576224346e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,6.5,2583.9516267845465,200215.24405318496,6.016616118581815e6,9.221263263214113e7,8.305750837148908e8,4.77383576908197e9,1.840021088352963e10,4.8939353849036934e10,9.094760672013042e10,1.177844008258246e11,1.0414198859975996e11,5.991259714028463e10,2.0207294060979477e10,3.0310941091469216e9,0,0,0,0,0,0,0,0,0,0},{0,0,0,-22.754814814814814,-5840.553656708876,-370080.7888040544,-9.881938796070835e6,-1.4003615801375034e8,-1.1925823709998772e9,-6.57143044116013e9,-2.4508050681444206e10,-6.348184946356478e10,-1.1543549489334048e11,-1.468037932397025e11,-1.27811573462042e11,-7.256032344847891e10,-2.4192621500783764e10,-3.5924078330630183e9,0,0,0,0,0,0,0,0,0},{0,0,0,0.00001443722583152868,51.20436448442445,10738.282242670764,604748.6713855794,1.493782547301189e7,2.0024139920202476e8,1.6355739069693673e9,8.723538381973366e9,3.1694134443060642e10,8.035017116564796e10,1.435061488294865e11,1.797385196878227e11,1.544460759994406e11,8.668740434615086e10,2.861538653232956e10,4.211788493935952e9,0,0,0,0,0,0,0,0}},{{0.8666666666666667,-144.73333333333332,-92115.75281787357,-4.24804535780092e6,-7.720394235745513e7,-7.459561839697425e8,-4.37688462512671e9,-1.675264548351156e10,-4.352291071881835e10,-7.819993218306961e10,-9.728719442636674e10,-8.229722149813876e10,-4.518485423006929e10,-1.4523992606328999e10,-2.0748560866184285e9,0,0,0,0,0,0,0,0,0,0,0,0},{0,1.3,3092.3493598562786,296618.58790897264,9.301895812628992e6,1.4042234137451884e8,1.2141829505065637e9,6.6136711412013e9,2.3997256897036137e10,5.988243978747334e10,1.0425835625270293e11,1.2646490694806935e11,1.0477864663163727e11,5.65370092624084e10,1.7906910138859047e10,2.5259117576224346e9,0,0,0,0,0,0,0,0,0,0,0},{0,0,-32.90325356909285,-9960.403453242889,-646128.1687916917,-1.6817577993811376e7,-2.272190859950398e8,-1.8244572021268456e9,-9.424164410318285e9,-3.2855536243018745e10,-7.94693874291209e10,-1.3493911256397678e11,-1.6035193404744455e11,-1.3059358460620116e11,-6.944791402530933e10,-2.1722841115552937e10,-3.0310941091469216e9,0,0,0,0,0,0,0,0,0,0},{0,0,0.00962962962962963,107.05323934367682,21670.89761846037,1.1640149545323383e6,2.7098524121223144e7,3.3995615473259085e8,2.5888012200866394e9,1.2850503978831438e10,4.34323908124913e10,1.0247227787993118e11,1.704876950572971e11,1.9917787189184882e11,1.5989240688923416e11,8.398305773017148e10,2.5988825417315273e10,3.5924078330630183e9,0,0,0,0,0,0,0,0,0},{0,0,-0.00002887445166305736,-0.033754233994114057,-233.78149683578326,-38998.16031069183,-1.8707910950575937e6,-4.041802774693853e7,-4.8079742512005436e8,-3.518362643932259e9,-1.6932117246290905e10,-5.582666431497299e10,-1.2906973332811464e11,-2.1113776786988696e11,-2.4316208621413403e11,-1.9281924836901074e11,-1.0020538726970265e11,-3.0721280779297535e10,-4.211788493935952e9,0,0,0,0,0,0,0,0}}},\n  {{{0,-0.4666666666666667,35489.066666666666,2.3512673864932396e6,5.783459782554564e7,7.417629589224231e8,5.737794526335927e9,2.8958539764775536e10,9.980418605461873e10,2.4087583664136777e11,4.1111360367982263e11,4.938686168666484e11,4.0842050111710443e11,2.2130873014861176e11,7.072552921342816e10,1.0103647030489738e10,0,0,0,0,0,0,0,0,0,0,0},{0,0,-914.2,-127000.05298477177,-5.410357632421346e6,-1.084187847239819e8,-1.2320553672292948e9,-8.793144231643013e9,-4.189268519105384e10,-1.3824796054280862e11,-3.2253689193271765e11,-5.3569991047103217e11,-6.2930263262471e11,-5.107830750947536e11,-2.7241958305957956e11,-8.588099975916278e10,-1.2124376436587687e10,0,0,0,0,0,0,0,0,0,0},{0,0,7,3223.9763957964633,289078.1147930231,1.0056934701494202e7,1.7885319900918955e8,1.8774039305941489e9,1.2659112178824976e10,5.778636226399293e10,1.8443310408086594e11,4.188943611564575e11,6.80570218646967e11,7.848818851678533e11,6.271698565745526e11,3.30024403976469e11,1.028326742214289e11,1.4369631332252073e10,0,0,0,0,0,0,0,0,0},{0,0,0,-24.504469987228607,-7282.983796526446,-533794.5211804865,-1.6496528391199792e7,-2.7121016480072916e8,-2.69139348470133e9,-1.7396960801336163e10,-7.683642125146432e10,-2.388197884303981e11,-5.3073846409068134e11,-8.467207746541761e11,-9.615174725410426e11,-7.581688663491782e11,-3.943770868588842e11,-1.2164636179544427e11,-1.6847153975743809e10,0,0,0,0,0,0,0,0},{0,0,0,0.000011535450912536563,55.140655048819674,13385.468430331828,871673.8196526119,2.4913415664470177e7,3.873820740702931e8,3.686591045638684e9,2.3063912016993534e10,9.92281083489501e10,3.018436477318775e11,6.588209340059844e11,1.0351031513181844e12,1.160082623098246e12,9.043453403023804e11,4.6572230892101843e11,1.4238562392402832e11,1.9564436875057327e10,0,0,0,0,0,0,0}},{{0.9333333333333333,-181.06666666666666,-136335.83965314535,-7.327933577454448e6,-1.5542659822206742e8,-1.7593665355350988e9,-1.2169475752962036e10,-5.541107203717101e10,-1.734553628583209e11,-3.823252362645737e11,-5.987312993848872e11,-6.626663891105165e11,-5.067610463730009e11,-2.5477706093710904e11,-7.577735272867303e10,-1.0103647030489738e10,0,0,0,0,0,0,0,0,0,0,0},{0,1.4,3930.705969543536,438914.5575660432,1.6021909399733834e7,2.821148285309416e8,2.8568306121554456e9,1.834034705520107e10,7.915174003764148e10,2.3795742789826575e11,5.0818633097929156e11,7.758814204731168e11,8.410097103451432e11,6.320268394606305e11,3.130867623573008e11,9.194318797745662e10,1.2124376436587687e10,0,0,0,0,0,0,0,0,0,0},{0,0,-35.952791592926204,-12671.688278689997,-955744.668589088,-2.8937820175670214e7,-4.558361881243446e8,-4.2852364908962717e9,-2.6082358434683636e10,-1.0813493285251514e11,-3.150596210807534e11,-6.56127990059077e11,-9.812778081103854e11,-1.045448978125867e12,-7.742480850836678e11,-3.7874643521238617e11,-1.1001748988755493e11,-1.4369631332252073e10,0,0,0,0,0,0,0,0,0},{0,0,0.008939974457215836,117.10041553054182,27577.685460833156,1.7212957303475374e6,4.6594371675881036e7,6.812809157261431e8,6.07243220002907e9,3.551011349027069e10,1.4269874524439893e11,4.054913031711275e11,8.273111760165848e11,1.2162825061370105e12,1.2771474457642676e12,9.341281376272004e11,4.520414263861544e11,1.3006993878331618e11,1.6847153975743809e10,0,0,0,0,0,0,0,0},{0,0,-0.000023070901825073126,-0.031330284678449304,-255.83925591764077,-49634.48688799174,-2.765829691982738e6,-6.945891513716699e7,-9.627476140344408e8,-8.244250951607839e9,-4.673088800021546e10,-1.8316187642444714e11,-5.0994384612902716e11,-1.022841420534557e12,-1.4821974712563923e12,-1.5372206353757112e12,-1.112343356234171e12,-5.3324678896895935e11,-1.5216784236155698e11,-1.9564436875057327e10,0,0,0,0,0,0,0}}},\n  {{{0,-0.5,50175,3.8110518751832955e6,1.076462696229007e8,1.5907243629794188e9,1.4251013900556408e10,8.39147841034878e10,3.408264309321651e11,9.827003704330287e11,2.041566817044961e12,3.065320744754241e12,3.2952209252682485e12,2.4722631961132183e12,1.2290545345785562e12,3.6373129309763055e11,4.8497505746350746e10,0,0,0,0,0,0,0,0,0,0},{0,0,-1124.5,-179081.18687280125,-8.746490101655236e6,-2.0128246173478386e8,-2.6356156253879805e9,-2.1787106957386932e10,-1.2111135286756642e11,-4.7103679933522406e11,-1.312930805324278e12,-2.654465838545084e12,-3.897567876302638e12,-4.1124050502739814e12,-3.036874638157322e12,-1.489333703666579e12,-4.355794497588909e11,-5.747852532900829e10,0,0,0,0,0,0,0,0,0},{0,0,7.5,3962.019044757294,407072.920718967,1.6232431369128535e7,3.3147987895103717e8,4.0090734366763234e9,3.13099625273733e10,1.6676035684980087e11,6.272725393185522e11,1.702120269314377e12,3.366309892830835e12,4.852504880352651e12,5.040497006172991e12,3.6725169792886714e12,1.7801426017692478e12,5.153247098462812e11,6.7388615902975235e10,0,0,0,0,0,0,0,0},{0,0,0,-26.25417130144605,-8945.976556539295,-751050.1272782461,-2.6597813264939196e7,-5.0203783424558866e8,-5.739732894683955e9,-4.2968855344066925e10,-2.214204398894586e11,-8.110701607930319e11,-2.1534160242155322e12,-4.1819214920925874e12,-5.935635976892781e12,-6.084107901642862e12,-4.381942913426506e12,-2.1025895514418992e12,-6.03236803647601e11,-7.825774750022931e10,0,0,0,0,0,0,0},{0,0,0,9.363190675760198e-6,59.07710774785302,16437.045563842872,1.2257532331766458e6,4.013768184797409e7,7.164271778242302e8,7.854094148649289e9,5.6903267984881676e10,2.856175070922272e11,1.0238847552212717e12,2.6698130647352876e12,5.105908405067799e12,7.152238271875518e12,7.247671528966757e12,5.167805995087918e12,2.457745328571359e12,6.99576833714171e11,9.011498196996101e10,0,0,0,0,0,0}},{{1,-223,-196278.75036659127,-1.2163480027372148e7,-2.9773689168561506e8,-3.901236201224221e9,-3.1389389329419144e10,-1.6743088681163474e11,-6.199947751483894e11,-1.6382802519391409e12,-3.132920728260574e12,-4.3465612386153613e12,-4.3323760798965054e12,-3.0231374871104106e12,-1.4018268987999307e12,-3.8798004597080597e11,-4.8497505746350746e10,0,0,0,0,0,0,0,0,0,0},{0,1.5,4912.873745602497,631897.2803378738,2.656490166119503e7,5.395585963900452e8,6.322823026301706e9,4.720796893278593e10,2.3863565691698135e11,8.485721474191187e11,2.172352790028894e12,4.049809894234472e12,5.50235200650019e12,5.38931258216232e12,3.7052589549104136e12,1.6963462050468354e12,4.643187124233951e11,5.747852532900829e10,0,0,0,0,0,0,0,0,0},{0,0,-39.038089514588414,-15851.374766259272,-1.3756719102298168e6,-4.7943245278919205e7,-8.708307769571735e8,-9.47113944257027e9,-6.703028125916526e10,-3.254547834230943e11,-1.1214448357099817e12,-2.7992820802386875e12,-5.111448386239666e12,-6.825434396193574e12,-6.587638039199097e12,-4.472282579818021e12,-2.0251695912105806e12,-5.4901901779776886e11,-6.7388615902975235e10,0,0,0,0,0,0,0,0},{0,0,0.008342602892102336,127.27291285612064,34507.8303268765,2.4771144537147037e6,7.715353799846987e7,1.3004413372683635e9,1.3407053636109262e10,9.11467268081413e10,4.288886516771684e11,1.4411578341601123e12,3.5239100414919814e12,6.3247528225204e12,8.32319236966487e12,7.933155759027074e12,5.327467452291677e12,2.3895346256094067e12,6.423656773977156e11,7.825774750022931e10,0,0,0,0,0,0,0},{0,0,-0.000018726381351520396,-0.029231881289723337,-278.179190366686,-62116.434369362025,-3.979701958366857e6,-1.1496637205380797e8,-1.8365386839217353e9,-1.8187049742974773e10,-1.198292372617549e11,-5.49880612073813e11,-1.810124500260231e12,-4.3508491817515527e12,-7.696337232599344e12,-1.000270962164139e13,-9.431448443943848e12,-6.273956282611874e12,-2.7906371863090767e12,-7.446343246991516e11,-9.011498196996101e10,0,0,0,0,0,0}}},\n  {{{0,-0.5333333333333333,69358.93333333333,5.9871611113645565e6,1.924439018268767e8,3.2448156139755497e9,3.330669577632881e10,2.2601945627174777e11,1.0662940872799772e12,3.608672101565586e12,8.923743251707213e12,1.6254636226822168e13,2.1772292854629945e13,2.117283786317375e13,1.453488209257297e13,6.674693753831088e12,1.8393128105282654e12,2.2991410131603317e11,0,0,0,0,0,0,0,0,0},{0,0,-1364.8,-247010.3969398599,-1.3710914253764993e7,-3.5907586422209996e8,-5.365095399350815e9,-5.081736342983615e10,-3.255698706535722e11,-1.470867065526843e12,-4.812414875990048e12,-1.1581712843876643e13,-2.0631072691303906e13,-2.7124046546863812e13,-2.5963317717947027e13,-1.758285432472868e13,-7.979604930158186e12,-2.1762558900431414e12,-2.6955446361190094e11,0,0,0,0,0,0,0,0},{0,0,8,4805.078378923909,560851.6328710681,2.5412128545875855e7,5.904978119526415e8,8.148894279740852e9,7.291974445465744e10,4.476097728118765e11,1.955793734114073e12,6.229626820644424e12,1.4665713663876832e13,2.56478138439078e13,3.319636806334255e13,3.135144116117232e13,2.098522418302309e13,9.426637994127908e12,2.547724446396354e12,3.1303099000091724e11,0,0,0,0,0,0,0},{0,0,0,-28.003910068426197,-10845.277747543556,-1.0340539156313771e6,-4.160211107138518e7,-8.934162310823445e8,-1.1653781015963501e10,-9.995712648139485e10,-5.936175304669795e11,-2.525790561524574e12,-7.87163260522547e12,-1.819637446847215e13,-3.1333388101515715e13,-4.001900002443862e13,-3.736019485586116e13,-2.4754756721440023e13,-1.1020607767604893e13,-2.954822829857143e12,-3.6045992787984406e11,0,0,0,0,0,0},{0,0,0,7.704568327482677e-6,63.01369198098898,19921.879208008424,1.6868344786841804e6,6.273964748932823e7,1.273959927266718e9,1.593305010069684e10,1.3225038205179854e11,7.649853076246484e11,3.185325437278451e12,9.749212126813445e12,2.219341094639236e13,3.771515415332075e13,4.76205488262829e13,4.401174323403532e13,2.890378760514151e13,1.2766176186368576e13,3.3986221771528154e12,4.1195420329125037e11,0,0,0,0,0}},{{1.0666666666666667,-270.93333333333334,-275873.1560624472,-1.9526615598641343e7,-5.463067917939428e8,-8.201340252473525e9,-7.590059501108307e10,-4.682825773172421e11,-2.0210768151063833e12,-6.288523857290183e12,-1.4356521317251836e13,-2.4229695665058094e13,-3.016739913942077e13,-2.7348748241932996e13,-1.7548912264512844e13,-7.551241265098464e12,-1.9542698611862817e12,-2.2991410131603317e11,0,0,0,0,0,0,0,0,0},{0,1.6,6051.193879719814,888285.5021747415,4.261058156357549e7,9.887753453648256e8,1.327213854827666e10,1.1396014858065186e11,6.662406988329823e11,2.7610060006323486e12,8.322298591918423e12,1.852086668895058e13,3.0609487847613902e13,3.744833590963831e13,3.344798330425991e13,2.11898795413883e13,9.017191413252525e12,2.311033121849092e12,2.6955446361190094e11,0,0,0,0,0,0,0,0},{0,0,-42.156757847819044,-19540.15290525303,-1.9336304731780519e6,-7.685712819702701e7,-1.594439517783882e9,-1.9858807743317722e10,-1.6160641862808392e11,-9.073615412262385e11,-3.643384657047884e12,-1.0707067507058748e13,-2.333725913033687e13,-3.790443110434288e13,-4.569379747197401e13,-4.029844176053195e13,-2.5250191501700312e13,-1.0641806906700912e13,-2.7042399413968125e12,-3.1303099000091724e11,0,0,0,0,0,0,0},{0,0,0.007820136852394917,137.56233770685833,42550.691954386595,3.4813989145593178e6,1.2363151215452614e8,2.3794672470258155e9,2.8087829807876396e10,2.19531539097911e11,1.1943955071569575e12,4.67634681344435e12,1.34610069080083e13,2.883661986591e13,4.615458622396285e13,5.4943021866992516e13,4.792868725397259e13,2.9744824442882992e13,1.2430432946055994e13,3.135052793797065e12,3.6045992787984406e11,0,0,0,0,0,0},{0,0,-0.000015409136654965353,-0.027397444972528398,-300.78237626127407,-76605.63532406824,-5.592621537223804e6,-1.841644469419035e8,-3.3586851599070764e9,-3.807661046434377e10,-2.883838211797314e11,-1.5299369967994255e12,-5.867608218199371e12,-1.6601437441364713e13,-3.5048526877146164e13,-5.5398397289151914e13,-6.523376765866994e13,-5.636627223479088e13,-3.46882547466682e13,-1.4388245861827873e13,-3.604599278798441e12,-4.1195420329125037e11,0,0,0,0,0}}},\n  {{{0,-0.5666666666666667,94002.06666666667,9.151311935305258e6,3.320851910459127e8,6.33521032186008e9,7.382275924950447e10,5.713935821593523e11,3.094041226242078e12,1.2118112094805514e13,3.5055579190329652e13,7.577080308439667e13,1.2277365554920727e14,1.4836959871768953e14,1.316939805223555e14,8.331707351347927e13,3.554854783594292e13,9.164851762804633e12,1.0782178544476038e12,0,0,0,0,0,0,0,0},{0,0,-1637.1,-334159.9205226142,-2.0918744937359024e7,-6.185177808929194e8,-1.0456566421124214e10,-1.1244319702124878e11,-8.217073662582078e11,-4.261136422965968e12,-1.613507388311211e13,-4.54274671317859e13,-9.60274321618125e13,-1.5272694341908484e14,-1.8167523305110694e14,-1.5908213959048006e14,-9.946452714264984e13,-4.200158523126891e13,-1.0730006712809219e13,-1.252123960003669e12,0,0,0,0,0,0,0},{0,0,8.5,5760.15335336423,758011.4560133434,3.872804138514868e7,1.0159241892397866e9,1.586238004713476e10,1.611446504457956e11,1.1282876946995315e12,5.658785050777512e12,2.0860295618072766e13,5.745164785582471e13,1.1922862068135155e14,1.8668579152657497e14,2.1910636544081044e14,1.8963108518772156e14,1.1735697816416962e14,4.9110491347309305e13,1.2445353299430406e13,1.4418397115193762e12,0,0,0,0,0,0},{0,0,0,-29.753679653679654,-12996.633706471774,-1.396745220816542e6,-6.3353746792567894e7,-1.5357532178764133e9,-2.2663668846309917e10,-2.2067759802151013e11,-1.4948161492516946e12,-7.300452192754711e12,-2.633115093534921e13,-7.12076759521055e13,-1.4550478401170806e14,-2.2481450876051734e14,-2.608206421034274e14,-2.2345389682757944e14,-1.3705312915212823e14,-5.689609265768836e13,-1.4315408564370951e13,-1.6478168131650015e12,0,0,0,0,0},{0,0,0,6.416135448393513e-6,66.95038440199731,23868.836113681442,2.2775814000040796e6,9.549105192520149e7,2.1884705815436573e9,3.0963237347720776e10,2.91743710502673e11,1.9247605684553428e12,9.198896562512525e12,3.258322450023937e13,8.677160313048934e13,1.749810194392509e14,2.6727116291968612e14,3.0697010372751906e14,2.6065987367419022e14,1.5860983054210656e14,6.537859338635338e13,1.6344561363015016e13,1.8704947608900017e12,0,0,0,0}},{{1.1333333333333333,-325.26666666666665,-379672.3372771824,-3.044289317860951e7,-9.653190181565726e8,-1.645654568518213e10,-1.7350035488585364e11,-1.224974862145457e12,-6.087121650396307e12,-2.198279146517062e13,-5.886931627391679e13,-1.1819629684730314e14,-1.7844037358940066e14,-2.014677026353088e14,-1.674879308472654e14,-9.947402064977933e13,-3.992880786963631e13,-9.703960690028434e12,-1.0782178544476038e12,0,0,0,0,0,0,0,0},{0,1.7,7358.141045228405,1.2228343356326213e6,6.639078335866816e7,1.7454206071277142e9,2.6599212141387142e10,2.601475592017887e11,1.740271415261319e12,8.302886018750533e12,2.904578302199949e13,7.582019351190167e13,1.490656602100751e14,2.211250836544858e14,2.4596589754150544e14,2.018771799198576e14,1.185708301208829e14,4.713181534517283e13,1.1356068692811053e13,1.252123960003669e12,0,0,0,0,0,0,0},{0,0,-45.30670672845925,-23779.18039507422,-2.661791803943833e6,-1.1969660677337208e8,-2.812574307228695e9,-3.9764345032168434e10,-3.685338752285082e11,-2.367394455421027e12,-1.0942906507013615e13,-3.73202401876562e13,-9.540709750646728e13,-1.843299072761322e14,-2.694183018006003e14,-2.9589668840921594e14,-2.401899624280804e14,-1.3971389256713602e14,-5.5062823051114625e13,-1.3166273155190094e13,-1.4418397115193762e12,0,0,0,0,0,0},{0,0,0.00735930735930736,147.96135233748615,51796.68308694178,4.792105916700205e6,1.9247997245947763e8,4.195150944550882e9,5.6203346619271835e10,5.002227932450558e11,3.113437647355672e12,1.403128377992629e13,4.686853664732211e13,1.1775423667475845e14,2.241800875868455e14,3.235464771315656e14,3.5146497769033325e14,2.825652031993201e14,1.6297151920138706e14,6.37448312874054e13,1.5139316970953451e13,1.6478168131650015e12,0,0,0,0,0},{0,0,-0.000012832270896787026,-0.025780032231645135,-323.6322744648659,-93265.65360101484,-7.69771425123931e6,-2.866519541825688e8,-5.919173280043663e9,-7.614963399570706e10,-6.566791772809753e11,-3.985094039738327e12,-1.7590885222630846e13,-5.775012578906397e13,-1.429804558962491e14,-2.6879959044347166e14,-3.8372757967297875e14,-4.1286846760141056e14,-3.291372259583687e14,-1.8840988086355138e14,-7.320015630019402e13,-1.7279808743460016e13,-1.8704947608900017e12,0,0,0,0}}},\n  {{{0,-0.6,125193.6,1.3652102441336416e7,5.554014600143847e8,1.1899676153316288e10,1.56165088407177e11,1.366540496131952e12,8.40861783023701e12,3.767209777990931e13,1.2572771066140053e14,3.1699683846911475e14,6.078847468910346e14,8.864741332485448e14,9.755540111565658e14,7.962880116350478e14,4.6711589143195694e14,1.8610613034936884e14,4.507646256013208e13,5.008495840014676e12,0,0,0,0,0,0,0},{0,0,-1943.4,-444350.0575188254,-3.1158621445006426e7,-1.0328718631043336e9,-1.9611734199856762e10,-2.3751855080120044e11,-1.9624234636460256e12,-1.1564556559203219e13,-5.009287309334422e13,-1.6271440389198622e14,-4.0123038305561994e14,-7.55244323016712e14,-1.0841303144905668e15,-1.1770068866194732e15,-9.494737901060278e14,-5.512548272291732e14,-2.1763242435124375e14,-5.228566111772896e13,-5.767358846077505e12,0,0,0,0,0,0},{0,0,9,6834.2430464192685,1.0071578139815058e6,5.7630935107687674e7,1.6947655536972034e9,2.971887868603432e10,3.400242318221734e11,2.691676008673227e12,1.5341040964447729e13,6.469277318651995e13,2.0556218578916944e14,4.9764013132721725e14,9.221943510366396e14,1.306119324762518e15,1.4015595146061295e15,1.1191029056445291e15,6.438857748567351e14,2.5216204623961334e14,6.0145313680522555e13,6.591267252660006e12,0,0,0,0,0},{0,0,0,-31.503474903474903,-15415.791203007979,-1.8549118704564322e6,-9.421570811376691e7,-2.5600544123712883e9,-4.242743900283097e10,-4.6525101895250854e11,-3.5629896100471016e12,-1.977415609780622e13,-8.158625551552306e13,-2.545518754366963e14,-6.067625924438578e14,-1.1095340952535998e15,-1.553365741029738e15,-1.650033451843795e15,-1.3057251452334125e15,-7.452766846539458e14,-2.8978473074627556e14,-6.867387907839006e13,-7.481979043560007e12,0,0,0,0},{0,0,0,5.4000054000054e-6,70.88716688716688,28306.784007021753,3.0236617450400395e6,1.4194238784983155e8,3.6460856395006375e9,5.792852759286986e10,6.146661187977393e11,4.584553043233062e12,2.4898101684512574e13,1.008823224117668e14,3.099519834507942e14,7.291125023795298e14,1.3180326124569462e15,1.826761435393357e15,1.9232229378190795e15,1.5098730462049652e15,8.556875473106161e14,3.305876811925353e14,7.788932029962469e13,8.4412071260677e12,0,0,0}},{{1.2,-386.4,-512904.0826728312,-4.6250508101108156e7,-1.6498943779538748e9,-3.1693433546401592e10,-3.774969633261728e11,-3.0222573837808267e12,-1.7114252146731506e13,-7.088860314805319e13,-2.195652433185797e14,-5.154436720771549e14,-9.229759888796809e14,-1.260086991646628e15,-1.3012905395960398e15,-9.989055197138905e14,-5.52186666361618e14,-2.0770526865943212e14,-4.758071048013942e13,-5.008495840014676e12,0,0,0,0,0,0,0},{0,1.8,8846.315037650835,1.652499890670798e6,1.0081815236953616e8,2.980852295984091e9,5.117658296341241e10,5.653923499515992e11,4.2884376326036885e12,2.3314343858552824e13,9.354089348013502e13,2.8240048108547756e14,6.491502225984054e14,1.142121460503862e15,1.5361570189811522e15,1.5661512339351905e15,1.188884318540835e15,6.508081678370406e14,2.4269387512646872e14,5.516934054076771e13,5.767358846077505e12,0,0,0,0,0,0},{0,0,-48.486092838536685,-28610.053294245357,-3.5971398670573817e6,-1.8170450901651797e8,-4.800628986511418e9,-7.645048609922267e10,-8.002747183060198e11,-5.828318542596038e12,-3.0696178946402035e13,-1.2005854798795881e14,-3.549510824718861e14,-8.017723301469554e14,-1.3898631564766085e15,-1.8456808435841622e15,-1.8609896441157935e15,-1.3990467910248515e15,-7.593074829663808e14,-2.8099884047000084e14,-6.3440947306852555e13,-6.591267252660006e12,0,0,0,0,0},{0,0,0.0069498069498069494,158.46348709704054,62337.20331735644,6.475875533245151e6,2.921192897693864e8,7.157442458806223e9,1.0799555655084192e11,1.0855125833750288e12,7.659196496678287e12,3.932659690536154e13,1.5063960727715928e14,4.376723566398628e14,9.741274125347245e14,1.6673482941700252e15,2.189911586818609e15,2.1868594523613995e15,1.630061509290193e15,8.779941332352192e14,3.227187992148031e14,7.241486860017006e13,7.481979043560007e12,0,0,0,0},{0,0,-0.0000108000108000108,-0.024343224343224345,-346.71430558980086,-112261.86290506809,-1.0402072220276337e7,-4.349568507848248e8,-1.009552791463289e10,-1.4625792737830328e11,-1.4242626335574956e12,-9.797394622514166e12,-4.9269031116791766e13,-1.8547288277928522e14,-5.3100005187471706e14,-1.167000294197266e15,-1.9756813909489115e15,-2.5700586158413735e15,-2.544779679266689e15,-1.882576246583223e15,-1.007168503518773e15,-3.6794961500620994e14,-8.210992386265853e13,-8.4412071260677e12,0,0,0}}},\n  {{{0,-0.6333333333333333,164158.73333333334,1.9930293177442875e7,9.033522147628825e8,2.159562943718983e10,3.1695435436427655e11,3.1117616133093535e12,2.1572888288816043e13,1.09484309701826e14,4.1677133450584825e14,1.2090804894217778e15,2.6979173704553415e15,4.645555959846248e15,6.155935299753043e15,6.217286496234509e15,4.696578395452082e15,2.568066717852626e15,9.596044046707916e14,2.191596361509452e14,2.306943538431002e13,0,0,0,0,0,0},{0,0,-2285.7,-581877.1667007137,-4.542712920857985e7,-1.6777496857464986e9,-3.554596419337812e10,-4.8146941932528937e11,-4.463236488322537e12,-2.963468073013338e13,-1.4541483616136725e14,-5.387724333982664e14,-1.528682077106105e15,-3.3483222279232085e15,-5.675362800076585e15,-7.419408932698538e15,-7.405721111493924e15,-5.536919691805194e15,-3.000077941416619e15,-1.1119673832339076e15,-2.5211597241424522e14,-2.6365069010640023e13,0,0,0,0,0},{0,0,9.5,8034.346639088207,1.3179673367617e6,8.39534763120524e7,2.750469750609688e9,5.381559497441904e10,6.886135692098751e11,6.116068398456957e12,3.927525531830635e13,1.87621875146192e14,6.800177457846064e14,1.8942591437198682e15,4.0847532803221355e15,6.831268665098625e15,8.82695090814349e15,8.720982640500069e15,6.461561639299811e15,3.4729854777955795e15,1.277987710634628e15,2.8787805081888025e14,2.9927916174240027e13,0,0,0,0},{0,0,0,-33.253291753291755,-18118.497367092852,-2.4263056561244098e6,-1.371723164151261e8,-4.1521251582654886e9,-7.677555171735455e10,-9.415385502549863e11,-8.089827546033466e12,-5.058607217578998e13,-2.364337668337472e14,-8.414253645683544e14,-2.307828334679184e15,-4.910703691264801e15,-8.118041289783834e15,-1.0383658748433438e16,-1.0167288912591014e16,-7.473149461470064e15,-3.9880042421706445e15,-1.4580506661137562e15,-3.2652123928561875e14,-3.37648285042708e13,0,0,0},{0,0,0,4.587809465858246e-6,74.82402515085442,33264.59142595566,3.9539347999028377e6,2.0657696267892247e8,5.910713251530081e9,1.0476957462492262e11,1.2431924580143315e12,1.0402955953492928e13,6.365382991017692e13,2.9216326172935256e14,1.0238733031895974e15,2.7713247980029775e15,5.829535183879195e15,9.540280533475378e15,1.209446128636118e16,1.1748726073620186e16,8.574250203286003e15,4.5463150567317e15,1.6525312353663298e15,3.6811898393680606e14,3.788249051698675e13,0,0}},{{1.2666666666666666,-454.73333333333335,-681521.0882190833,-6.866812258629e7,-2.7377423472293215e9,-5.885373059155742e10,-7.861999036201415e11,-7.081109305222594e12,-4.529513317348146e13,-2.1305179026514706e14,-7.544247442837826e14,-2.042292971648917e15,-4.264112494563583e15,-6.887192122673396e15,-8.579721224269423e15,-8.162941954518764e15,-5.819981799016939e15,-3.0089392479769985e15,-1.064858703611706e15,-2.3069435384310022e14,-2.306943538431002e13,0,0,0,0,0,0},{0,1.9,10528.43340142741,2.1966056588859563e6,1.496340808486337e8,4.94310202090654e9,9.495646973148334e10,1.1764390850983005e12,1.0037702723170137e13,6.163907586873927e13,2.808207197238787e14,9.692149606108888e14,2.56903911487428e15,5.27020715794106e15,8.385800766327233e15,1.0313148662287174e16,9.703156383423962e15,6.850671684428315e15,3.5112616134256495e15,1.2330819190015352e15,2.6529850691956525e14,2.6365069010640023e13,0,0,0,0,0},{0,0,-51.693278176413344,-34074.78014180465,-4.781836743440356e6,-2.6961591339264446e8,-7.957160824871687e9,-1.417663970015018e11,-1.6639977875868992e12,-1.3631292963271166e13,-8.108623737250361e13,-3.6010324340970756e14,-1.2170534965459422e15,-3.1699041795772295e15,-6.406817661291515e15,-1.006485817543921e16,-1.2241435673077942e16,-1.1405817498928164e16,-7.983798180042419e15,-4.0608079407256885e15,-1.4163152517613982e15,-3.0284200890600025e14,-2.9927916174240027e13,0,0,0,0},{0,0,0.006583506583506584,169.06299475396784,74264.58077013028,8.60869260141037e6,4.3336689596040046e8,1.1859561376200863e10,2.0016987814094357e11,2.255834083018215e12,1.7902005880230887e13,1.0381148852085344e14,4.5148680237416356e14,1.499482007722108e15,3.8480749449814715e15,7.679151287997549e15,1.1931083759742532e16,1.4371388164888972e16,1.3276270216445754e16,9.222548421515924e15,4.658972295533745e15,1.614980380412015e15,3.434036535377542e14,3.37648285042708e13,0,0,0},{0,0,-9.175618931716493e-6,-0.023058330375403547,-370.01552049520996,-133761.33977549043,-1.3827819745792182e7,-6.451738118427956e8,-1.6723378636699493e10,-2.709901673195793e11,-2.9584696444771226e12,-2.288771647820421e13,-1.2998082039545089e14,-5.555315701855969e14,-1.8179762075703875e15,-4.606611728235598e15,-9.092222501474912e15,-1.3990963605707124e16,-1.6709589472590812e16,-1.5319694204447672e16,-1.0570016078374418e16,-5.307109848412682e15,-1.8294877603627978e15,-3.8706022919529944e14,-3.788249051698675e13,0,0}}},\n  {{{0,-0.6666666666666666,212266.66666666666,2.853599258035219e7,1.4330114863456995e9,3.800258146404937e10,6.199274770948887e11,6.782860407787912e12,5.259010470068523e13,2.998352445442762e14,1.289474302995765e15,4.256253873967506e15,1.0902678302444068e16,2.1797926698136784e16,3.403776778865586e16,4.131592071162812e16,3.85489253454479e16,2.709770607257666e16,1.3874444112447926e16,4.881180309397506e15,1.054602760425601e15,1.054602760425601e14,0,0,0,0,0},{0,0,-2666,-751541.6624389548,-6.496750631920008e7,-2.6584414136368732e9,-6.248207318933823e10,-9.406826677470544e11,-9.718503789838549e12,-7.216907849489138e13,-3.978378205026157e14,-1.6653179634893145e15,-5.376204754295016e15,-1.3518436707709178e16,-2.6605608406951144e16,-4.0986885639172104e16,-4.9169772065455784e16,-4.540651511838149e16,-3.162877001568124e16,-1.6063591016532404e16,-5.610415428520925e15,-1.204242341296801e15,-1.1971166469696011e14,0,0,0,0},{0,0,10,9367.463398770313,1.701250849010266e6,1.1998124014091475e8,4.354863213182676e9,9.45208118958657e10,1.344305113466995e12,1.3306614989260633e13,9.556877985629884e13,5.12895265809024e14,2.1002083731150322e15,6.656579614385937e15,1.6478625548324368e16,3.1999232770493936e16,4.8724507460816184e16,5.785749736908936e16,5.294839315410694e16,3.65864067552318e16,1.844784707627131e16,6.401367498840126e15,1.365940789490955e15,1.350593140170832e14,0,0,0},{0,0,0,-35.00312695434646,-21120.499631781146,-3.13075780749621e6,-1.9594466391609663e8,-6.57050375487538e9,-1.3476651743799847e11,-1.8369043800571548e12,-1.7589435314077154e13,-1.2300981206774544e14,-6.45896597792679e14,-2.5969519006174225e15,-8.1043872896729e15,-1.979717247928791e16,-3.800085757405471e16,-5.727840617892459e16,-6.7406697825393336e16,-6.119592653276693e16,-4.198322456673902e16,-2.1032687680075924e16,-7.255685440992997e15,-1.5400055927557658e15,-1.51529962067947e14,0,0},{0,0,0,3.930803703917714e-6,78.76094777966587,38771.1275913068,5.100639029001081e6,2.949841095687273e8,9.349478097378347e9,1.8381938202018082e11,2.42419630975418e12,2.2606778024802125e13,1.547013295439825e14,7.976887641336864e14,3.1582328358075825e15,9.726354425919532e15,2.3487483127377148e16,4.463176708335417e16,6.667543951844797e16,7.784411667165024e16,7.0169853688251656e16,4.78314795584771e16,2.3823442630743476e16,8.174976337050191e15,1.7267367770533495e15,1.6914972509910362e14,0}},{{1.3333333333333333,-530.6666666666666,-892251.827371045,-9.987215970701566e7,-4.424126639899304e9,-1.0578643572033942e11,-1.5747287228400437e12,-1.5845717567057562e13,-1.1362682509291195e14,-6.01771280857198e14,-2.4125671241445005e15,-7.446002113664234e15,-1.7881405459744606e16,-3.35951258854644e16,-4.9401354974251736e16,-5.657957303847233e16,-4.9900402465368104e16,-3.3212859965007824e16,-1.612718315044587e16,-5.388707887852326e15,-1.107332898446881e15,-1.054602760425601e14,0,0,0,0,0},{0,2,12417.32487790964,2.877010547668333e6,2.1757735630149266e8,7.983815399399566e9,1.7056499979904105e11,2.354551408251009e12,2.244295896408418e13,1.5448897973201038e14,7.924454954576368e14,3.09644241377513e15,9.357141993333924e15,2.2077909573687176e16,4.086271515883938e16,5.931957879239599e16,6.7183171140618856e16,5.867380519673225e16,3.8715022071894504e16,1.8654226260365056e16,6.190090662038646e15,1.2640981736452812e15,1.1971166469696011e14,0,0,0,0},{0,0,-54.92679754062404,-40215.75896837982,-6.263591942903851e6,-3.919620881744125e8,-1.2847160247073557e10,-2.5452055340858438e11,-3.3284003694315527e12,-3.045760371601915e13,-2.0308431931952872e14,-1.0153938929148701e15,-3.8851031201166525e15,-1.1535964059895936e16,-2.6816071946032308e16,-4.9000640659284456e16,-7.0346216741010216e16,-7.889814086638738e16,-6.831336974290147e16,-4.473081072704507e16,-2.1405078738350924e16,-7.0590142722074e15,-1.4334704464994968e15,-1.350593140170832e14,0,0,0},{0,0,0.006253908692933083,179.75473573239677,87672.02030488498,1.127655502867755e7,6.299257811345832e8,1.9142375540966686e10,3.592377142778432e11,4.510116251939719e12,3.997869384169198e13,2.598472460491622e14,1.2722572790529712e15,4.783417939546741e15,1.3993927307709768e16,3.2117401194767464e16,5.8041145938566104e16,8.251977130024504e16,9.176083681396456e16,7.884587952758733e16,5.127529010535035e16,2.4385985285874588e16,7.99727636948314e15,1.6157705737897392e15,1.51529962067947e14,0,0},{0,0,-7.861607407835427e-6,-0.021902438238229502,-393.52434085950074,-157932.76854200763,-1.8113190266049244e7,-9.376913624338514e8,-2.698711715217962e10,-4.86188476940676e11,-5.912673590075985e12,-5.109028982891983e13,-3.251901708214681e14,-1.5646005583864498e15,-5.796048104691107e15,-1.6742037879323554e16,-3.800261305735843e16,-6.801536029880263e16,-9.587749223709138e16,-1.0580649570083427e17,-9.029733700258528e16,-5.8363106600548536e16,-2.7603025440299108e16,-9.006629152120784e15,-1.8113116396029015e15,-1.6914972509910362e14,0}}}\n };\n\nstatic inline int max(int a, int b)\n{\n  return a > b ? a : b;\n}\n\n/* Compute the singular field at the point x for the particle at xp */\nvoid effsource_hS(struct coordinate * x, double * hS)\n{\n  double A[10], s2, s2_35;\n\n  double r      = x->r;\n  double theta  = x->theta;\n  double phi    = x->phi;\n  double rp     = xp.r;\n  double thetap = xp.theta;\n  double phip   = xp.phi;\n\n  double dr     = r - rp;\n  double dth = theta - thetap;\n  double dphi   = phi - phip;\n\n  double dr2 = dr*dr;\n\n  double dth2  = dth*dth;\n  \n  double Q       = sin(0.5*dphi);\n  double Q2      = Q*Q;\n  double Q4      = Q2*Q2;\n  double Q6      = Q4*Q2;\n  double Q8      = Q4*Q4;\n\n  double R       = sin(dphi);\n\n  A[0] = dth2*(dth2*(Q2*(A00420 + A00440*Q2) + dth2*(A00600 + A00800*dth2 + A00620*Q2)) + (A00240 + A00260*Q2)*Q4) + (A00060 + A00080*Q2)*Q6 + dr*(dth2*(dth2*(Q2*(A01420 + A01440*Q2) + dth2*(A01600 + A01800*dth2 + A01620*Q2)) + (A01240 + A01260*Q2)*Q4) + dr*(dth2*(Q2*(A02220 + A02240*Q2) + dth2*(A02400 + A02600*dth2 + A02420*Q2)) + (A02040 + A02060*Q2)*Q4 + dr*(dth2*(Q2*(A03220 + A03240*Q2) + dth2*(A03400 + A03600*dth2 + A03420*Q2)) + dr*(Q2*(A04020 + A04040*Q2) + dth2*(A04200 + A04400*dth2 + A04220*Q2) + dr*(Q2*(A05020 + A05040*Q2) + dth2*(A05200 + A05400*dth2 + A05220*Q2) + dr*(A06000 + A06200*dth2 + A06020*Q2 + dr*(A07000 + dr*(A08000 + A09000*dr) + A07200*dth2 + A07020*Q2)))) + (A03040 + A03060*Q2)*Q4)) + (A01060 + A01080*Q2)*Q6);\n  A[1] = Q6*(A10061*R + A10081*Q2*R) + dth2*(Q4*(A10241*R + A10261*Q2*R) + dth2*(Q2*(A10421*R + A10441*Q2*R) + dth2*(A10601*R + A10801*dth2*R + A10621*Q2*R))) + dr*(A11061*Q6*R + dth2*(A11241*Q4*R + dth2*(A11601*dth2*R + A11421*Q2*R)) + dr*(Q4*(A12041*R + A12061*Q2*R) + dth2*(Q2*(A12221*R + A12241*Q2*R) + dth2*(A12401*R + A12601*dth2*R + A12421*Q2*R)) + dr*(A13041*Q4*R + dth2*(A13401*dth2*R + A13221*Q2*R) + dr*(Q2*(A14021*R + A14041*Q2*R) + dth2*(A14201*R + A14401*dth2*R + A14221*Q2*R) + dr*(A15201*dth2*R + A15021*Q2*R + dr*(A16001*R + A16201*dth2*R + A16021*Q2*R + dr*(A17001*R + A18001*dr*R)))))));\n  A[2] = dth*(A20161*Q6*R + dth2*(A20341*Q4*R + dth2*(A20701*dth2*R + A20521*Q2*R))) + dr*(dth*(A21161*Q6*R + dth2*(A21341*Q4*R + dth2*(A21701*dth2*R + A21521*Q2*R))) + dr*(dth*(A22141*Q4*R + dth2*(A22501*dth2*R + A22321*Q2*R)) + dr*(dth*(A23141*Q4*R + dth2*(A23501*dth2*R + A23321*Q2*R)) + dr*(dth*(A24301*dth2*R + A24121*Q2*R) + dr*(dr*(A26101*dth*R + A27101*dr*dth*R) + dth*(A25301*dth2*R + A25121*Q2*R))))));\n  A[3] = dth2*(dth2*(Q2*(A30420 + A30440*Q2) + dth2*(A30600 + A30800*dth2 + A30620*Q2)) + (A30240 + A30260*Q2)*Q4) + (A30060 + A30080*Q2)*Q6 + dr*(dth2*(dth2*(Q2*(A31420 + A31440*Q2) + dth2*(A31600 + A31800*dth2 + A31620*Q2)) + (A31240 + A31260*Q2)*Q4) + dr*(dth2*(Q2*(A32220 + A32240*Q2) + dth2*(A32400 + A32600*dth2 + A32420*Q2)) + (A32040 + A32060*Q2)*Q4 + dr*(dth2*(Q2*(A33220 + A33240*Q2) + dth2*(A33400 + A33600*dth2 + A33420*Q2)) + dr*(Q2*(A34020 + A34040*Q2) + dth2*(A34200 + A34400*dth2 + A34220*Q2) + dr*(Q2*(A35020 + A35040*Q2) + dth2*(A35200 + A35400*dth2 + A35220*Q2) + dr*(A36000 + A36200*dth2 + A36020*Q2 + dr*(A37000 + dr*(A38000 + A39000*dr) + A37200*dth2 + A37020*Q2)))) + (A33040 + A33060*Q2)*Q4)) + (A31060 + A31080*Q2)*Q6);\n  A[4] = dth2*(dth2*(dth2*(A40800*dth2 + A40620*Q2) + A40440*Q4) + A40260*Q6) + A40080*Q8 + dr*(dth2*(dth2*(dth2*(A41800*dth2 + A41620*Q2) + A41440*Q4) + A41260*Q6) + dr*(dth2*(dth2*(A42600*dth2 + A42420*Q2) + A42240*Q4) + A42060*Q6 + dr*(dth2*(dth2*(A43600*dth2 + A43420*Q2) + A43240*Q4) + dr*(dth2*(A44400*dth2 + A44220*Q2) + A44040*Q4 + dr*(dth2*(A45400*dth2 + A45220*Q2) + dr*(A46200*dth2 + A46020*Q2 + dr*(dr*(A48000 + A49000*dr) + A47200*dth2 + A47020*Q2)) + A45040*Q4)) + A43060*Q6)) + A41080*Q8);\n  A[5] = dr2*(dr2*(dr2*(A58100*dr2*dth + dth*(A56300*dth2 + A56120*Q2)) + dth*(dth2*(A54500*dth2 + A54320*Q2) + A54140*Q4)) + dth*(dth2*(dth2*(A52700*dth2 + A52520*Q2) + A52340*Q4) + A52160*Q6)) + dth*(dth2*(dth2*(dth2*(A50900*dth2 + A50720*Q2) + A50540*Q4) + A50360*Q6) + A50180*Q8);\n  A[6] = Q6*(A60061*R + A60081*Q2*R) + dth2*(Q4*(A60241*R + A60261*Q2*R) + dth2*(Q2*(A60421*R + A60441*Q2*R) + dth2*(A60601*R + A60801*dth2*R + A60621*Q2*R))) + dr*(A61061*Q6*R + dth2*(A61241*Q4*R + dth2*(A61601*dth2*R + A61421*Q2*R)) + dr*(Q4*(A62041*R + A62061*Q2*R) + dth2*(Q2*(A62221*R + A62241*Q2*R) + dth2*(A62401*R + A62601*dth2*R + A62421*Q2*R)) + dr*(A63041*Q4*R + dth2*(A63401*dth2*R + A63221*Q2*R) + dr*(Q2*(A64021*R + A64041*Q2*R) + dth2*(A64201*R + A64401*dth2*R + A64221*Q2*R) + dr*(A65201*dth2*R + A65021*Q2*R + dr*(A66001*R + A66201*dth2*R + A66021*Q2*R + dr*(A67001*R + A68001*dr*R)))))));\n  A[7] = dth2*(dth2*(dth2*(A70800*dth2 + A70620*Q2) + A70440*Q4) + A70260*Q6) + A70080*Q8 + dr*(dth2*(dth2*(dth2*(A71800*dth2 + A71620*Q2) + A71440*Q4) + A71260*Q6) + dr*(dth2*(dth2*(A72600*dth2 + A72420*Q2) + A72240*Q4) + A72060*Q6 + dr*(dth2*(dth2*(A73600*dth2 + A73420*Q2) + A73240*Q4) + dr*(dth2*(A74400*dth2 + A74220*Q2) + A74040*Q4 + dr*(dth2*(A75400*dth2 + A75220*Q2) + dr*(A76200*dth2 + A76020*Q2 + dr*(dr*(A78000 + A79000*dr) + A77200*dth2 + A77020*Q2)) + A75040*Q4)) + A73060*Q6)) + A71080*Q8);\n  A[8] = dth*(A80161*Q6*R + dth2*(A80341*Q4*R + dth2*(A80701*dth2*R + A80521*Q2*R))) + dr*(dth*(A81161*Q6*R + dth2*(A81341*Q4*R + dth2*(A81701*dth2*R + A81521*Q2*R))) + dr*(dth*(A82141*Q4*R + dth2*(A82501*dth2*R + A82321*Q2*R)) + dr*(dth*(A83141*Q4*R + dth2*(A83501*dth2*R + A83321*Q2*R)) + dr*(dth*(A84301*dth2*R + A84121*Q2*R) + dr*(dr*(A86101*dth*R + A87101*dr*dth*R) + dth*(A85301*dth2*R + A85121*Q2*R))))));\n  A[9] = dth2*(dth2*(Q2*(A90420 + A90440*Q2) + dth2*(A90600 + A90800*dth2 + A90620*Q2)) + (A90240 + A90260*Q2)*Q4) + (A90060 + A90080*Q2)*Q6 + dr*(dth2*(dth2*(Q2*(A91420 + A91440*Q2) + dth2*(A91600 + A91800*dth2 + A91620*Q2)) + (A91240 + A91260*Q2)*Q4) + dr*(dth2*(Q2*(A92220 + A92240*Q2) + dth2*(A92400 + A92600*dth2 + A92420*Q2)) + (A92040 + A92060*Q2)*Q4 + dr*(dth2*(Q2*(A93220 + A93240*Q2) + dth2*(A93400 + A93600*dth2 + A93420*Q2)) + dr*(Q2*(A94020 + A94040*Q2) + dth2*(A94200 + A94400*dth2 + A94220*Q2) + dr*(Q2*(A95020 + A95040*Q2) + dth2*(A95200 + A95400*dth2 + A95220*Q2) + dr*(A96000 + A96200*dth2 + A96020*Q2 + dr*(A97000 + dr*(A98000 + A99000*dr) + A97200*dth2 + A97020*Q2)))) + (A93040 + A93060*Q2)*Q4)) + (A91060 + A91080*Q2)*Q6);\n\n  s2 = alpha20*dr2 + alpha02*dth2 + beta*Q2;\n  s2_35 = pow(s2, 3.5);\n\n  /* hS */\n  for(int i=0; i<10; i++)\n  {\n    hS[i] = A[i]/s2_35;\n  }\n}\n\n/* Compute the singular field at the point x for the particle at xp */\nvoid effsource_hS_m(int m, struct coordinate * x, double * hS_re, double * hS_im)\n{\n  double A_re[10][5];\n  double A_im[10][5];\n  double alpha, ellE, ellK;\n\n  if(m>20)\n  {\n    printf(\"Support for computing mode %d has not yet been added.\\n\", m);\n    return;\n  }\n\n  const double r      = x->r;\n  const double theta  = x->theta;\n  const double rp     = xp.r;\n  const double thetap = xp.theta;\n\n  const double dr     = r - rp;\n  const double dth = theta - thetap;\n\n  const double dr2 = dr*dr;\n\n  const double dth2  = dth*dth;\n  const double dth3  = dth*dth2;\n  const double dth4  = dth2*dth2;\n  const double dth5  = dth3*dth2;\n  const double dth6  = dth3*dth3;\n  const double dth7  = dth4*dth3;\n  const double dth8  = dth4*dth4;\n  const double dth9  = dth5*dth4;\n\n  A_re[0][0] = (A00600 + A00800*dth2)*dth6 + dr*(dr*((A02400 + A02600*dth2)*dth4 + dr*(dr*(dth2*(A04200 + A04400*dth2) + dr*(dth2*(A05200 + A05400*dth2) + dr*(A06000 + A06200*dth2 + dr*(A07000 + dr*(A08000 + A09000*dr) + A07200*dth2)))) + (A03400 + A03600*dth2)*dth4)) + (A01600 + A01800*dth2)*dth6);\n  A_re[0][1] = (A00420 + A00620*dth2)*dth4 + dr*(dr*(dth2*(A02220 + A02420*dth2) + dr*(dth2*(A03220 + A03420*dth2) + dr*(A04020 + A04220*dth2 + dr*(A05020 + dr*(A06020 + A07020*dr) + A05220*dth2)))) + (A01420 + A01620*dth2)*dth4);\n  A_re[0][2] = dth2*(A00240 + A00440*dth2) + dr*(dth2*(A01240 + A01440*dth2) + dr*(A02040 + A02240*dth2 + dr*(A03040 + dr*(A04040 + A05040*dr) + A03240*dth2)));\n  A_re[0][3] = A00060 + A00260*dth2 + dr*(A01060 + dr*(A02060 + A03060*dr) + A01260*dth2);\n  A_re[0][4] = A00080 + A01080*dr;\n  A_im[0][0] = A_im[0][1] = A_im[0][2] = A_im[0][3] = A_im[0][4] = 0.0;\n\n  A_re[1][0] = A_re[1][1] = A_re[1][2] = A_re[1][3] = A_re[1][4] = 0.0;\n  A_im[1][0] = (A10601 + A10801*dth2)*dth6 + dr*(dr*((A12401 + A12601*dth2)*dth4 + dr*(dr*(dth2*(A14201 + A14401*dth2) + dr*(A15201*dth2 + dr*(A16001 + dr*(A17001 + A18001*dr) + A16201*dth2))) + A13401*dth4)) + A11601*dth6);\n  A_im[1][1] = (A10421 + A10621*dth2)*dth4 + dr*(dr*(dth2*(A12221 + A12421*dth2) + dr*(A13221*dth2 + dr*(A14021 + dr*(A15021 + A16021*dr) + A14221*dth2))) + A11421*dth4);\n  A_im[1][2] = dth2*(A10241 + A10441*dth2) + dr*(A11241*dth2 + dr*(A12041 + dr*(A13041 + A14041*dr) + A12241*dth2));\n  A_im[1][3] = A10061 + dr*(A11061 + A12061*dr) + A10261*dth2;\n  A_im[1][4] = A10081;\n\n  A_re[2][0] = A_re[2][1] = A_re[2][2] = A_re[2][3] = A_re[2][4] = 0.0;\n  A_im[2][0] = A20701*dth7 + dr*(dr*(A22501*dth5 + dr*(dr*(A24301*dth3 + dr*(dr*(A26101*dth + A27101*dr*dth) + A25301*dth3)) + A23501*dth5)) + A21701*dth7);\n  A_im[2][1] = A20521*dth5 + dr*(dr*(A22321*dth3 + dr*(dr*(A24121*dth + A25121*dr*dth) + A23321*dth3)) + A21521*dth5);\n  A_im[2][2] = A20341*dth3 + dr*(dr*(A22141*dth + A23141*dr*dth) + A21341*dth3);\n  A_im[2][3] = A20161*dth + A21161*dr*dth;\n  A_im[2][4] = 0.0;\n\n  A_re[3][0] = (A30600 + A30800*dth2)*dth6 + dr*(dr*((A32400 + A32600*dth2)*dth4 + dr*(dr*(dth2*(A34200 + A34400*dth2) + dr*(dth2*(A35200 + A35400*dth2) + dr*(A36000 + A36200*dth2 + dr*(A37000 + dr*(A38000 + A39000*dr) + A37200*dth2)))) + (A33400 + A33600*dth2)*dth4)) + (A31600 + A31800*dth2)*dth6);\n  A_re[3][1] = (A30420 + A30620*dth2)*dth4 + dr*(dr*(dth2*(A32220 + A32420*dth2) + dr*(dth2*(A33220 + A33420*dth2) + dr*(A34020 + A34220*dth2 + dr*(A35020 + dr*(A36020 + A37020*dr) + A35220*dth2)))) + (A31420 + A31620*dth2)*dth4);\n  A_re[3][2] = dth2*(A30240 + A30440*dth2) + dr*(dth2*(A31240 + A31440*dth2) + dr*(A32040 + A32240*dth2 + dr*(A33040 + dr*(A34040 + A35040*dr) + A33240*dth2)));\n  A_re[3][3] = A30060 + A30260*dth2 + dr*(A31060 + dr*(A32060 + A33060*dr) + A31260*dth2);\n  A_re[3][4] = A30080 + A31080*dr;\n  A_im[3][0] = A_im[3][1] = A_im[3][2] = A_im[3][3] = A_im[3][4] = 0.0;\n\n  A_re[4][0] = A40800*dth8 + dr*(dr*(A42600*dth6 + dr*(dr*(A44400*dth4 + dr*(dr*(A46200*dth2 + dr*(dr*(A48000 + A49000*dr) + A47200*dth2)) + A45400*dth4)) + A43600*dth6)) + A41800*dth8);\n  A_re[4][1] = A40620*dth6 + dr*(dr*(A42420*dth4 + dr*(dr*(A44220*dth2 + dr*(dr*(A46020 + A47020*dr) + A45220*dth2)) + A43420*dth4)) + A41620*dth6);\n  A_re[4][2] = A40440*dth4 + dr*(dr*(A42240*dth2 + dr*(dr*(A44040 + A45040*dr) + A43240*dth2)) + A41440*dth4);\n  A_re[4][3] = A40260*dth2 + dr*(dr*(A42060 + A43060*dr) + A41260*dth2);\n  A_re[4][4] = A40080 + A41080*dr;\n  A_im[4][0] = A_im[4][1] = A_im[4][2] = A_im[4][3] = A_im[4][4] = 0.0;\n\n  A_re[5][0] = dr2*(dr2*(dr2*(A58100*dr2*dth + A56300*dth3) + A54500*dth5) + A52700*dth7) + A50900*dth9;\n  A_re[5][1] = dr2*(dr2*(A56120*dr2*dth + A54320*dth3) + A52520*dth5) + A50720*dth7;\n  A_re[5][2] = dr2*(A54140*dr2*dth + A52340*dth3) + A50540*dth5;\n  A_re[5][3] = A52160*dr2*dth + A50360*dth3;\n  A_re[5][4] = A50180*dth;\n  A_im[5][0] = A_im[5][1] = A_im[5][2] = A_im[5][3] = A_im[5][4] = 0.0;\n\n  A_re[6][0] = A_re[6][1] = A_re[6][2] = A_re[6][3] = A_re[6][4] = 0.0;\n  A_im[6][0] = (A60601 + A60801*dth2)*dth6 + dr*(dr*((A62401 + A62601*dth2)*dth4 + dr*(dr*(dth2*(A64201 + A64401*dth2) + dr*(A65201*dth2 + dr*(A66001 + dr*(A67001 + A68001*dr) + A66201*dth2))) + A63401*dth4)) + A61601*dth6);\n  A_im[6][1] = (A60421 + A60621*dth2)*dth4 + dr*(dr*(dth2*(A62221 + A62421*dth2) + dr*(A63221*dth2 + dr*(A64021 + dr*(A65021 + A66021*dr) + A64221*dth2))) + A61421*dth4);\n  A_im[6][2] = dth2*(A60241 + A60441*dth2) + dr*(A61241*dth2 + dr*(A62041 + dr*(A63041 + A64041*dr) + A62241*dth2));\n  A_im[6][3] = A60061 + dr*(A61061 + A62061*dr) + A60261*dth2;\n  A_im[6][4] = A60081;\n\n  A_re[7][0] = A70800*dth8 + dr*(dr*(A72600*dth6 + dr*(dr*(A74400*dth4 + dr*(dr*(A76200*dth2 + dr*(dr*(A78000 + A79000*dr) + A77200*dth2)) + A75400*dth4)) + A73600*dth6)) + A71800*dth8);\n  A_re[7][1] = A70620*dth6 + dr*(dr*(A72420*dth4 + dr*(dr*(A74220*dth2 + dr*(dr*(A76020 + A77020*dr) + A75220*dth2)) + A73420*dth4)) + A71620*dth6);\n  A_re[7][2] = A70440*dth4 + dr*(dr*(A72240*dth2 + dr*(dr*(A74040 + A75040*dr) + A73240*dth2)) + A71440*dth4);\n  A_re[7][3] = A70260*dth2 + dr*(dr*(A72060 + A73060*dr) + A71260*dth2);\n  A_re[7][4] = A70080 + A71080*dr;\n  A_im[7][0] = A_im[7][1] = A_im[7][2] = A_im[7][3] = A_im[7][4] = 0.0;\n\n  A_re[8][0] = A_re[8][1] = A_re[8][2] = A_re[8][3] = A_re[8][4] = 0.0;\n  A_im[8][0] = A80701*dth7 + dr*(dr*(A82501*dth5 + dr*(dr*(A84301*dth3 + dr*(dr*(A86101*dth + A87101*dr*dth) + A85301*dth3)) + A83501*dth5)) + A81701*dth7);\n  A_im[8][1] = A80521*dth5 + dr*(dr*(A82321*dth3 + dr*(dr*(A84121*dth + A85121*dr*dth) + A83321*dth3)) + A81521*dth5);\n  A_im[8][2] = A80341*dth3 + dr*(dr*(A82141*dth + A83141*dr*dth) + A81341*dth3);\n  A_im[8][3] = A80161*dth + A81161*dr*dth;\n  A_im[8][4] = 0.0;\n\n  A_re[9][0] = (A90600 + A90800*dth2)*dth6 + dr*(dr*((A92400 + A92600*dth2)*dth4 + dr*(dr*(dth2*(A94200 + A94400*dth2) + dr*(dth2*(A95200 + A95400*dth2) + dr*(A96000 + A96200*dth2 + dr*(A97000 + dr*(A98000 + A99000*dr) + A97200*dth2)))) + (A93400 + A93600*dth2)*dth4)) + (A91600 + A91800*dth2)*dth6);\n  A_re[9][1] = (A90420 + A90620*dth2)*dth4 + dr*(dr*(dth2*(A92220 + A92420*dth2) + dr*(dth2*(A93220 + A93420*dth2) + dr*(A94020 + A94220*dth2 + dr*(A95020 + dr*(A96020 + A97020*dr) + A95220*dth2)))) + (A91420 + A91620*dth2)*dth4);\n  A_re[9][2] = dth2*(A90240 + A90440*dth2) + dr*(dth2*(A91240 + A91440*dth2) + dr*(A92040 + A92240*dth2 + dr*(A93040 + dr*(A94040 + A95040*dr) + A93240*dth2)));\n  A_re[9][3] = A90060 + A90260*dth2 + dr*(A91060 + dr*(A92060 + A93060*dr) + A91260*dth2);\n  A_re[9][4] = A90080 + A91080*dr;\n  A_im[9][0] = A_im[9][1] = A_im[9][2] = A_im[9][3] = A_im[9][4] = 0.0;\n\n  alpha = alpha20*dr2 + alpha02*dth2;\n\n  /* Powers of C = alpha / beta */\n  double C[27];\n  C[0]  = 1;\n  C[1]  = alpha / beta;\n  for(int i=2; i<=26; i++)\n    C[i] = C[1]*C[i-1];\n\n  ellE = gsl_sf_ellint_Ecomp(sqrt(1.0/(1.0+C[1])), GSL_PREC_DOUBLE);\n  ellK = gsl_sf_ellint_Kcomp(sqrt(1.0/(1.0+C[1])), GSL_PREC_DOUBLE);\n\n  const double fac_re = 4.0/(beta*C[3]*pow(alpha+beta, 2.5));\n  const double fac_im = -32.0/(beta*beta*C[2]*pow(alpha+beta, 1.5));\n  for(int l=0; l<10; l++)\n  {\n    hS_re[l] = 0.0;\n    hS_im[l] = 0.0;\n    for(int j=0; j<5; j++) {\n      double fac1K = ellK*A_re[l][j];\n      for(int k=max(j,0); k<=m+2+j; k++) {\n        hS_re[l] += fac1K*C[k]*ReEI[m][0][j][k];\n      }\n\n      double fac1E = ellE*A_re[l][j];\n      for(int k=max(j-1,0); k<=m+2+j; k++) {\n        hS_re[l] += fac1E*C[k]*ReEI[m][1][j][k];\n      }\n      \n      double fac2K = ellK*A_im[l][j];\n      for(int k=max(j-1,0); k<=m+1+j; k++) {\n        hS_im[l] += fac2K*C[k]*ImEI[m][0][j][k];\n      }\n      \n      double fac2E = ellE*A_im[l][j];\n      for(int k=max(j-2,0); k<=m+1+j; k++) {\n        hS_im[l] += fac2E*C[k]*ImEI[m][1][j][k];\n      }\n    }\n    hS_re[l] *= fac_re;\n    hS_im[l] *= fac_im;\n  }\n}\n\n\n/* Compute the singular field, its derivatives and its d'Alembertian */\nvoid effsource_calc(struct coordinate * x, double *hS, double *dhS_dr,\n      double *dhS_dth, double *dhS_dph, double *dhS_dt, double *src)\n{\n  double A[10], dA_dr[10], d2A_dr2[10], dA_dth[10], d2A_dth2[10], dA_dQ[10], dA_dR[10], dA_dph[10];\n  double d2A_dQ2[10], d2A_dQR[10], d2A_dph2[10], dA_dt[10], d2A_dt2[10], d2A_dtph[10];\n\n  double s2, sqrts2, s2_15, s2_25, s2_35, s2_45, s2_55, ds2_dr, d2s2_dr2, ds2_dth;\n  double d2s2_dth2, ds2_dQ, ds2_dph, d2s2_dQ2, d2s2_dph2, ds2_dt, d2s2_dt2, d2s2_dtph;\n\n  double d2hS_dt2[10], d2hS_dtr[10], d2hS_dtth[10];\n  double d2hS_dtph[10], d2hS_dr2[10], d2hS_drth[10], d2hS_drph[10], d2hS_dth2[10], d2hS_dthph[10], d2hS_dph2[10];\n\n  double r      = x->r;\n  double theta  = x->theta;\n  double phi    = x->phi;\n  double rp     = xp.r;\n  double thetap = xp.theta;\n  double phip   = xp.phi;\n\n  double Omega  = M / (a*M + sqrt(M*rp*rp*rp));\n\n  double dr     = r - rp;\n  double dth = theta - thetap;\n  double dphi   = phi - phip;\n\n  double dr2 = dr*dr;\n\n  double dth2  = dth*dth;\n\n  double Q  = sin(0.5*dphi);\n  double Q2 = Q*Q;\n  double Q3 = Q2*Q;\n  double Q4 = Q2*Q2;\n  double Q5 = Q3*Q2;\n  double Q6 = Q4*Q2;\n  double Q7 = Q4*Q3;\n  double Q8 = Q4*Q4;\n\n  double R  = sin(dphi);\n\n  double dQ_dph = 0.5*cos(0.5*dphi);\n  double dR_dph = cos(dphi);\n\n  double d2Q_dph2 = -0.25*Q;\n  double d2R_dph2 = -R;\n\n  /* A */\n  A[0] = dth2*(dth2*(Q2*(A00420 + A00440*Q2) + dth2*(A00600 + A00800*dth2 + A00620*Q2)) + (A00240 + A00260*Q2)*Q4) + (A00060 + A00080*Q2)*Q6 + dr*(dth2*(dth2*(Q2*(A01420 + A01440*Q2) + dth2*(A01600 + A01800*dth2 + A01620*Q2)) + (A01240 + A01260*Q2)*Q4) + dr*(dth2*(Q2*(A02220 + A02240*Q2) + dth2*(A02400 + A02600*dth2 + A02420*Q2)) + (A02040 + A02060*Q2)*Q4 + dr*(dth2*(Q2*(A03220 + A03240*Q2) + dth2*(A03400 + A03600*dth2 + A03420*Q2)) + dr*(Q2*(A04020 + A04040*Q2) + dth2*(A04200 + A04400*dth2 + A04220*Q2) + dr*(Q2*(A05020 + A05040*Q2) + dth2*(A05200 + A05400*dth2 + A05220*Q2) + dr*(A06000 + A06200*dth2 + A06020*Q2 + dr*(A07000 + dr*(A08000 + A09000*dr) + A07200*dth2 + A07020*Q2)))) + (A03040 + A03060*Q2)*Q4)) + (A01060 + A01080*Q2)*Q6);\n  A[1] = Q6*(A10061*R + A10081*Q2*R) + dth2*(Q4*(A10241*R + A10261*Q2*R) + dth2*(Q2*(A10421*R + A10441*Q2*R) + dth2*(A10601*R + A10801*dth2*R + A10621*Q2*R))) + dr*(A11061*Q6*R + dth2*(A11241*Q4*R + dth2*(A11601*dth2*R + A11421*Q2*R)) + dr*(Q4*(A12041*R + A12061*Q2*R) + dth2*(Q2*(A12221*R + A12241*Q2*R) + dth2*(A12401*R + A12601*dth2*R + A12421*Q2*R)) + dr*(A13041*Q4*R + dth2*(A13401*dth2*R + A13221*Q2*R) + dr*(Q2*(A14021*R + A14041*Q2*R) + dth2*(A14201*R + A14401*dth2*R + A14221*Q2*R) + dr*(A15201*dth2*R + A15021*Q2*R + dr*(A16001*R + A16201*dth2*R + A16021*Q2*R + dr*(A17001*R + A18001*dr*R)))))));\n  A[2] = dth*(A20161*Q6*R + dth2*(A20341*Q4*R + dth2*(A20701*dth2*R + A20521*Q2*R))) + dr*(dth*(A21161*Q6*R + dth2*(A21341*Q4*R + dth2*(A21701*dth2*R + A21521*Q2*R))) + dr*(dth*(A22141*Q4*R + dth2*(A22501*dth2*R + A22321*Q2*R)) + dr*(dth*(A23141*Q4*R + dth2*(A23501*dth2*R + A23321*Q2*R)) + dr*(dth*(A24301*dth2*R + A24121*Q2*R) + dr*(dr*(A26101*dth*R + A27101*dr*dth*R) + dth*(A25301*dth2*R + A25121*Q2*R))))));\n  A[3] = dth2*(dth2*(Q2*(A30420 + A30440*Q2) + dth2*(A30600 + A30800*dth2 + A30620*Q2)) + (A30240 + A30260*Q2)*Q4) + (A30060 + A30080*Q2)*Q6 + dr*(dth2*(dth2*(Q2*(A31420 + A31440*Q2) + dth2*(A31600 + A31800*dth2 + A31620*Q2)) + (A31240 + A31260*Q2)*Q4) + dr*(dth2*(Q2*(A32220 + A32240*Q2) + dth2*(A32400 + A32600*dth2 + A32420*Q2)) + (A32040 + A32060*Q2)*Q4 + dr*(dth2*(Q2*(A33220 + A33240*Q2) + dth2*(A33400 + A33600*dth2 + A33420*Q2)) + dr*(Q2*(A34020 + A34040*Q2) + dth2*(A34200 + A34400*dth2 + A34220*Q2) + dr*(Q2*(A35020 + A35040*Q2) + dth2*(A35200 + A35400*dth2 + A35220*Q2) + dr*(A36000 + A36200*dth2 + A36020*Q2 + dr*(A37000 + dr*(A38000 + A39000*dr) + A37200*dth2 + A37020*Q2)))) + (A33040 + A33060*Q2)*Q4)) + (A31060 + A31080*Q2)*Q6);\n  A[4] = dth2*(dth2*(dth2*(A40800*dth2 + A40620*Q2) + A40440*Q4) + A40260*Q6) + A40080*Q8 + dr*(dth2*(dth2*(dth2*(A41800*dth2 + A41620*Q2) + A41440*Q4) + A41260*Q6) + dr*(dth2*(dth2*(A42600*dth2 + A42420*Q2) + A42240*Q4) + A42060*Q6 + dr*(dth2*(dth2*(A43600*dth2 + A43420*Q2) + A43240*Q4) + dr*(dth2*(A44400*dth2 + A44220*Q2) + A44040*Q4 + dr*(dth2*(A45400*dth2 + A45220*Q2) + dr*(A46200*dth2 + A46020*Q2 + dr*(dr*(A48000 + A49000*dr) + A47200*dth2 + A47020*Q2)) + A45040*Q4)) + A43060*Q6)) + A41080*Q8);\n  A[5] = dr2*(dr2*(dr2*(A58100*dr2*dth + dth*(A56300*dth2 + A56120*Q2)) + dth*(dth2*(A54500*dth2 + A54320*Q2) + A54140*Q4)) + dth*(dth2*(dth2*(A52700*dth2 + A52520*Q2) + A52340*Q4) + A52160*Q6)) + dth*(dth2*(dth2*(dth2*(A50900*dth2 + A50720*Q2) + A50540*Q4) + A50360*Q6) + A50180*Q8);\n  A[6] = Q6*(A60061*R + A60081*Q2*R) + dth2*(Q4*(A60241*R + A60261*Q2*R) + dth2*(Q2*(A60421*R + A60441*Q2*R) + dth2*(A60601*R + A60801*dth2*R + A60621*Q2*R))) + dr*(A61061*Q6*R + dth2*(A61241*Q4*R + dth2*(A61601*dth2*R + A61421*Q2*R)) + dr*(Q4*(A62041*R + A62061*Q2*R) + dth2*(Q2*(A62221*R + A62241*Q2*R) + dth2*(A62401*R + A62601*dth2*R + A62421*Q2*R)) + dr*(A63041*Q4*R + dth2*(A63401*dth2*R + A63221*Q2*R) + dr*(Q2*(A64021*R + A64041*Q2*R) + dth2*(A64201*R + A64401*dth2*R + A64221*Q2*R) + dr*(A65201*dth2*R + A65021*Q2*R + dr*(A66001*R + A66201*dth2*R + A66021*Q2*R + dr*(A67001*R + A68001*dr*R)))))));\n  A[7] = dth2*(dth2*(dth2*(A70800*dth2 + A70620*Q2) + A70440*Q4) + A70260*Q6) + A70080*Q8 + dr*(dth2*(dth2*(dth2*(A71800*dth2 + A71620*Q2) + A71440*Q4) + A71260*Q6) + dr*(dth2*(dth2*(A72600*dth2 + A72420*Q2) + A72240*Q4) + A72060*Q6 + dr*(dth2*(dth2*(A73600*dth2 + A73420*Q2) + A73240*Q4) + dr*(dth2*(A74400*dth2 + A74220*Q2) + A74040*Q4 + dr*(dth2*(A75400*dth2 + A75220*Q2) + dr*(A76200*dth2 + A76020*Q2 + dr*(dr*(A78000 + A79000*dr) + A77200*dth2 + A77020*Q2)) + A75040*Q4)) + A73060*Q6)) + A71080*Q8);\n  A[8] = dth*(A80161*Q6*R + dth2*(A80341*Q4*R + dth2*(A80701*dth2*R + A80521*Q2*R))) + dr*(dth*(A81161*Q6*R + dth2*(A81341*Q4*R + dth2*(A81701*dth2*R + A81521*Q2*R))) + dr*(dth*(A82141*Q4*R + dth2*(A82501*dth2*R + A82321*Q2*R)) + dr*(dth*(A83141*Q4*R + dth2*(A83501*dth2*R + A83321*Q2*R)) + dr*(dth*(A84301*dth2*R + A84121*Q2*R) + dr*(dr*(A86101*dth*R + A87101*dr*dth*R) + dth*(A85301*dth2*R + A85121*Q2*R))))));\n  A[9] = dth2*(dth2*(Q2*(A90420 + A90440*Q2) + dth2*(A90600 + A90800*dth2 + A90620*Q2)) + (A90240 + A90260*Q2)*Q4) + (A90060 + A90080*Q2)*Q6 + dr*(dth2*(dth2*(Q2*(A91420 + A91440*Q2) + dth2*(A91600 + A91800*dth2 + A91620*Q2)) + (A91240 + A91260*Q2)*Q4) + dr*(dth2*(Q2*(A92220 + A92240*Q2) + dth2*(A92400 + A92600*dth2 + A92420*Q2)) + (A92040 + A92060*Q2)*Q4 + dr*(dth2*(Q2*(A93220 + A93240*Q2) + dth2*(A93400 + A93600*dth2 + A93420*Q2)) + dr*(Q2*(A94020 + A94040*Q2) + dth2*(A94200 + A94400*dth2 + A94220*Q2) + dr*(Q2*(A95020 + A95040*Q2) + dth2*(A95200 + A95400*dth2 + A95220*Q2) + dr*(A96000 + A96200*dth2 + A96020*Q2 + dr*(A97000 + dr*(A98000 + A99000*dr) + A97200*dth2 + A97020*Q2)))) + (A93040 + A93060*Q2)*Q4)) + (A91060 + A91080*Q2)*Q6);\n\n  /* dA/dx */\n  dA_dr[0]  = dth2*(dth2*(Q2*(A01420 + A01440*Q2) + dth2*(A01600 + A01800*dth2 + A01620*Q2)) + (A01240 + A01260*Q2)*Q4) + dr*(dth2*(Q2*(2*A02220 + 2*A02240*Q2) + dth2*(2*A02400 + 2*A02600*dth2 + 2*A02420*Q2)) + (2*A02040 + 2*A02060*Q2)*Q4 + dr*(dth2*(Q2*(3*A03220 + 3*A03240*Q2) + dth2*(3*A03400 + 3*A03600*dth2 + 3*A03420*Q2)) + dr*(Q2*(4*A04020 + 4*A04040*Q2) + dth2*(4*A04200 + 4*A04400*dth2 + 4*A04220*Q2) + dr*(Q2*(5*A05020 + 5*A05040*Q2) + dth2*(5*A05200 + 5*A05400*dth2 + 5*A05220*Q2) + dr*(6*A06000 + 6*A06200*dth2 + 6*A06020*Q2 + dr*(7*A07000 + dr*(8*A08000 + 9*A09000*dr) + 7*A07200*dth2 + 7*A07020*Q2)))) + (3*A03040 + 3*A03060*Q2)*Q4)) + (A01060 + A01080*Q2)*Q6;\n  dA_dr[1]  = A11061*Q6*R + dth2*(A11241*Q4*R + dth2*(A11601*dth2*R + A11421*Q2*R)) + dr*(Q4*(2*A12041*R + 2*A12061*Q2*R) + dth2*(Q2*(2*A12221*R + 2*A12241*Q2*R) + dth2*(2*A12401*R + 2*A12601*dth2*R + 2*A12421*Q2*R)) + dr*(3*A13041*Q4*R + dth2*(3*A13401*dth2*R + 3*A13221*Q2*R) + dr*(Q2*(4*A14021*R + 4*A14041*Q2*R) + dth2*(4*A14201*R + 4*A14401*dth2*R + 4*A14221*Q2*R) + dr*(5*A15201*dth2*R + 5*A15021*Q2*R + dr*(6*A16001*R + 6*A16201*dth2*R + 6*A16021*Q2*R + dr*(7*A17001*R + 8*A18001*dr*R))))));\n  dA_dr[2]  = dth*(A21161*Q6*R + dth2*(A21341*Q4*R + dth2*(A21701*dth2*R + A21521*Q2*R))) + dr*(dth*(2*A22141*Q4*R + dth2*(2*A22501*dth2*R + 2*A22321*Q2*R)) + dr*(dth*(3*A23141*Q4*R + dth2*(3*A23501*dth2*R + 3*A23321*Q2*R)) + dr*(dth*(4*A24301*dth2*R + 4*A24121*Q2*R) + dr*(dr*(6*A26101*dth*R + 7*A27101*dr*dth*R) + dth*(5*A25301*dth2*R + 5*A25121*Q2*R)))));\n  dA_dr[3]  = dth2*(dth2*(Q2*(A31420 + A31440*Q2) + dth2*(A31600 + A31800*dth2 + A31620*Q2)) + (A31240 + A31260*Q2)*Q4) + dr*(dth2*(Q2*(2*A32220 + 2*A32240*Q2) + dth2*(2*A32400 + 2*A32600*dth2 + 2*A32420*Q2)) + (2*A32040 + 2*A32060*Q2)*Q4 + dr*(dth2*(Q2*(3*A33220 + 3*A33240*Q2) + dth2*(3*A33400 + 3*A33600*dth2 + 3*A33420*Q2)) + dr*(Q2*(4*A34020 + 4*A34040*Q2) + dth2*(4*A34200 + 4*A34400*dth2 + 4*A34220*Q2) + dr*(Q2*(5*A35020 + 5*A35040*Q2) + dth2*(5*A35200 + 5*A35400*dth2 + 5*A35220*Q2) + dr*(6*A36000 + 6*A36200*dth2 + 6*A36020*Q2 + dr*(7*A37000 + dr*(8*A38000 + 9*A39000*dr) + 7*A37200*dth2 + 7*A37020*Q2)))) + (3*A33040 + 3*A33060*Q2)*Q4)) + (A31060 + A31080*Q2)*Q6;\n  dA_dr[4]  = dth2*(dth2*(dth2*(A41800*dth2 + A41620*Q2) + A41440*Q4) + A41260*Q6) + dr*(dth2*(dth2*(2*A42600*dth2 + 2*A42420*Q2) + 2*A42240*Q4) + 2*A42060*Q6 + dr*(dth2*(dth2*(3*A43600*dth2 + 3*A43420*Q2) + 3*A43240*Q4) + dr*(dth2*(4*A44400*dth2 + 4*A44220*Q2) + 4*A44040*Q4 + dr*(dth2*(5*A45400*dth2 + 5*A45220*Q2) + dr*(6*A46200*dth2 + 6*A46020*Q2 + dr*(dr*(8*A48000 + 9*A49000*dr) + 7*A47200*dth2 + 7*A47020*Q2)) + 5*A45040*Q4)) + 3*A43060*Q6)) + A41080*Q8;\n  dA_dr[5]  = dr*(dr2*(dr2*(8*A58100*dr2*dth + dth*(6*A56300*dth2 + 6*A56120*Q2)) + dth*(dth2*(4*A54500*dth2 + 4*A54320*Q2) + 4*A54140*Q4)) + dth*(dth2*(dth2*(2*A52700*dth2 + 2*A52520*Q2) + 2*A52340*Q4) + 2*A52160*Q6));\n  dA_dr[6]  = A61061*Q6*R + dth2*(A61241*Q4*R + dth2*(A61601*dth2*R + A61421*Q2*R)) + dr*(Q4*(2*A62041*R + 2*A62061*Q2*R) + dth2*(Q2*(2*A62221*R + 2*A62241*Q2*R) + dth2*(2*A62401*R + 2*A62601*dth2*R + 2*A62421*Q2*R)) + dr*(3*A63041*Q4*R + dth2*(3*A63401*dth2*R + 3*A63221*Q2*R) + dr*(Q2*(4*A64021*R + 4*A64041*Q2*R) + dth2*(4*A64201*R + 4*A64401*dth2*R + 4*A64221*Q2*R) + dr*(5*A65201*dth2*R + 5*A65021*Q2*R + dr*(6*A66001*R + 6*A66201*dth2*R + 6*A66021*Q2*R + dr*(7*A67001*R + 8*A68001*dr*R))))));\n  dA_dr[7]  = dth2*(dth2*(dth2*(A71800*dth2 + A71620*Q2) + A71440*Q4) + A71260*Q6) + dr*(dth2*(dth2*(2*A72600*dth2 + 2*A72420*Q2) + 2*A72240*Q4) + 2*A72060*Q6 + dr*(dth2*(dth2*(3*A73600*dth2 + 3*A73420*Q2) + 3*A73240*Q4) + dr*(dth2*(4*A74400*dth2 + 4*A74220*Q2) + 4*A74040*Q4 + dr*(dth2*(5*A75400*dth2 + 5*A75220*Q2) + dr*(6*A76200*dth2 + 6*A76020*Q2 + dr*(dr*(8*A78000 + 9*A79000*dr) + 7*A77200*dth2 + 7*A77020*Q2)) + 5*A75040*Q4)) + 3*A73060*Q6)) + A71080*Q8;\n  dA_dr[8]  = dth*(A81161*Q6*R + dth2*(A81341*Q4*R + dth2*(A81701*dth2*R + A81521*Q2*R))) + dr*(dth*(2*A82141*Q4*R + dth2*(2*A82501*dth2*R + 2*A82321*Q2*R)) + dr*(dth*(3*A83141*Q4*R + dth2*(3*A83501*dth2*R + 3*A83321*Q2*R)) + dr*(dth*(4*A84301*dth2*R + 4*A84121*Q2*R) + dr*(dr*(6*A86101*dth*R + 7*A87101*dr*dth*R) + dth*(5*A85301*dth2*R + 5*A85121*Q2*R)))));\n  dA_dr[9]  = dth2*(dth2*(Q2*(A91420 + A91440*Q2) + dth2*(A91600 + A91800*dth2 + A91620*Q2)) + (A91240 + A91260*Q2)*Q4) + dr*(dth2*(Q2*(2*A92220 + 2*A92240*Q2) + dth2*(2*A92400 + 2*A92600*dth2 + 2*A92420*Q2)) + (2*A92040 + 2*A92060*Q2)*Q4 + dr*(dth2*(Q2*(3*A93220 + 3*A93240*Q2) + dth2*(3*A93400 + 3*A93600*dth2 + 3*A93420*Q2)) + dr*(Q2*(4*A94020 + 4*A94040*Q2) + dth2*(4*A94200 + 4*A94400*dth2 + 4*A94220*Q2) + dr*(Q2*(5*A95020 + 5*A95040*Q2) + dth2*(5*A95200 + 5*A95400*dth2 + 5*A95220*Q2) + dr*(6*A96000 + 6*A96200*dth2 + 6*A96020*Q2 + dr*(7*A97000 + dr*(8*A98000 + 9*A99000*dr) + 7*A97200*dth2 + 7*A97020*Q2)))) + (3*A93040 + 3*A93060*Q2)*Q4)) + (A91060 + A91080*Q2)*Q6;\n  dA_dth[0] = dth*(dth2*(Q2*(4*A00420 + 4*A00440*Q2) + dth2*(6*A00600 + 8*A00800*dth2 + 6*A00620*Q2)) + (2*A00240 + 2*A00260*Q2)*Q4) + dr*(dr*(dth*(Q2*(2*A02220 + 2*A02240*Q2) + dth2*(4*A02400 + 6*A02600*dth2 + 4*A02420*Q2)) + dr*(dth*(Q2*(2*A03220 + 2*A03240*Q2) + dth2*(4*A03400 + 6*A03600*dth2 + 4*A03420*Q2)) + dr*(dth*(2*A04200 + 4*A04400*dth2 + 2*A04220*Q2) + dr*(dr*(2*A06200*dth + 2*A07200*dr*dth) + dth*(2*A05200 + 4*A05400*dth2 + 2*A05220*Q2))))) + dth*(dth2*(Q2*(4*A01420 + 4*A01440*Q2) + dth2*(6*A01600 + 8*A01800*dth2 + 6*A01620*Q2)) + (2*A01240 + 2*A01260*Q2)*Q4));\n  dA_dth[1] = dth*(Q4*(2*A10241*R + 2*A10261*Q2*R) + dth2*(Q2*(4*A10421*R + 4*A10441*Q2*R) + dth2*(6*A10601*R + 8*A10801*dth2*R + 6*A10621*Q2*R))) + dr*(dth*(2*A11241*Q4*R + dth2*(6*A11601*dth2*R + 4*A11421*Q2*R)) + dr*(dth*(Q2*(2*A12221*R + 2*A12241*Q2*R) + dth2*(4*A12401*R + 6*A12601*dth2*R + 4*A12421*Q2*R)) + dr*(dth*(4*A13401*dth2*R + 2*A13221*Q2*R) + dr*(dr*(2*A15201*dth*R + 2*A16201*dr*dth*R) + dth*(2*A14201*R + 4*A14401*dth2*R + 2*A14221*Q2*R)))));\n  dA_dth[2] = A20161*Q6*R + dth2*(3*A20341*Q4*R + dth2*(7*A20701*dth2*R + 5*A20521*Q2*R)) + dr*(A21161*Q6*R + dth2*(3*A21341*Q4*R + dth2*(7*A21701*dth2*R + 5*A21521*Q2*R)) + dr*(A22141*Q4*R + dth2*(5*A22501*dth2*R + 3*A22321*Q2*R) + dr*(A23141*Q4*R + dth2*(5*A23501*dth2*R + 3*A23321*Q2*R) + dr*(3*A24301*dth2*R + A24121*Q2*R + dr*(3*A25301*dth2*R + A25121*Q2*R + dr*(A26101*R + A27101*dr*R))))));\n  dA_dth[3] = dth*(dth2*(Q2*(4*A30420 + 4*A30440*Q2) + dth2*(6*A30600 + 8*A30800*dth2 + 6*A30620*Q2)) + (2*A30240 + 2*A30260*Q2)*Q4) + dr*(dr*(dth*(Q2*(2*A32220 + 2*A32240*Q2) + dth2*(4*A32400 + 6*A32600*dth2 + 4*A32420*Q2)) + dr*(dth*(Q2*(2*A33220 + 2*A33240*Q2) + dth2*(4*A33400 + 6*A33600*dth2 + 4*A33420*Q2)) + dr*(dth*(2*A34200 + 4*A34400*dth2 + 2*A34220*Q2) + dr*(dr*(2*A36200*dth + 2*A37200*dr*dth) + dth*(2*A35200 + 4*A35400*dth2 + 2*A35220*Q2))))) + dth*(dth2*(Q2*(4*A31420 + 4*A31440*Q2) + dth2*(6*A31600 + 8*A31800*dth2 + 6*A31620*Q2)) + (2*A31240 + 2*A31260*Q2)*Q4));\n  dA_dth[4] = dth*(dth2*(dth2*(8*A40800*dth2 + 6*A40620*Q2) + 4*A40440*Q4) + 2*A40260*Q6) + dr*(dr*(dth*(dth2*(6*A42600*dth2 + 4*A42420*Q2) + 2*A42240*Q4) + dr*(dr*(dth*(4*A44400*dth2 + 2*A44220*Q2) + dr*(dr*(2*A46200*dth + 2*A47200*dr*dth) + dth*(4*A45400*dth2 + 2*A45220*Q2))) + dth*(dth2*(6*A43600*dth2 + 4*A43420*Q2) + 2*A43240*Q4))) + dth*(dth2*(dth2*(8*A41800*dth2 + 6*A41620*Q2) + 4*A41440*Q4) + 2*A41260*Q6));\n  dA_dth[5] = dth2*(dth2*(dth2*(9*A50900*dth2 + 7*A50720*Q2) + 5*A50540*Q4) + 3*A50360*Q6) + dr2*(dth2*(dth2*(7*A52700*dth2 + 5*A52520*Q2) + 3*A52340*Q4) + dr2*(dth2*(5*A54500*dth2 + 3*A54320*Q2) + dr2*(A58100*dr2 + 3*A56300*dth2 + A56120*Q2) + A54140*Q4) + A52160*Q6) + A50180*Q8;\n  dA_dth[6] = dth*(Q4*(2*A60241*R + 2*A60261*Q2*R) + dth2*(Q2*(4*A60421*R + 4*A60441*Q2*R) + dth2*(6*A60601*R + 8*A60801*dth2*R + 6*A60621*Q2*R))) + dr*(dth*(2*A61241*Q4*R + dth2*(6*A61601*dth2*R + 4*A61421*Q2*R)) + dr*(dth*(Q2*(2*A62221*R + 2*A62241*Q2*R) + dth2*(4*A62401*R + 6*A62601*dth2*R + 4*A62421*Q2*R)) + dr*(dth*(4*A63401*dth2*R + 2*A63221*Q2*R) + dr*(dr*(2*A65201*dth*R + 2*A66201*dr*dth*R) + dth*(2*A64201*R + 4*A64401*dth2*R + 2*A64221*Q2*R)))));\n  dA_dth[7] = dth*(dth2*(dth2*(8*A70800*dth2 + 6*A70620*Q2) + 4*A70440*Q4) + 2*A70260*Q6) + dr*(dr*(dth*(dth2*(6*A72600*dth2 + 4*A72420*Q2) + 2*A72240*Q4) + dr*(dr*(dth*(4*A74400*dth2 + 2*A74220*Q2) + dr*(dr*(2*A76200*dth + 2*A77200*dr*dth) + dth*(4*A75400*dth2 + 2*A75220*Q2))) + dth*(dth2*(6*A73600*dth2 + 4*A73420*Q2) + 2*A73240*Q4))) + dth*(dth2*(dth2*(8*A71800*dth2 + 6*A71620*Q2) + 4*A71440*Q4) + 2*A71260*Q6));\n  dA_dth[8] = A80161*Q6*R + dth2*(3*A80341*Q4*R + dth2*(7*A80701*dth2*R + 5*A80521*Q2*R)) + dr*(A81161*Q6*R + dth2*(3*A81341*Q4*R + dth2*(7*A81701*dth2*R + 5*A81521*Q2*R)) + dr*(A82141*Q4*R + dth2*(5*A82501*dth2*R + 3*A82321*Q2*R) + dr*(A83141*Q4*R + dth2*(5*A83501*dth2*R + 3*A83321*Q2*R) + dr*(3*A84301*dth2*R + A84121*Q2*R + dr*(3*A85301*dth2*R + A85121*Q2*R + dr*(A86101*R + A87101*dr*R))))));\n  dA_dth[9] = dth*(dth2*(Q2*(4*A90420 + 4*A90440*Q2) + dth2*(6*A90600 + 8*A90800*dth2 + 6*A90620*Q2)) + (2*A90240 + 2*A90260*Q2)*Q4) + dr*(dr*(dth*(Q2*(2*A92220 + 2*A92240*Q2) + dth2*(4*A92400 + 6*A92600*dth2 + 4*A92420*Q2)) + dr*(dth*(Q2*(2*A93220 + 2*A93240*Q2) + dth2*(4*A93400 + 6*A93600*dth2 + 4*A93420*Q2)) + dr*(dth*(2*A94200 + 4*A94400*dth2 + 2*A94220*Q2) + dr*(dr*(2*A96200*dth + 2*A97200*dr*dth) + dth*(2*A95200 + 4*A95400*dth2 + 2*A95220*Q2))))) + dth*(dth2*(Q2*(4*A91420 + 4*A91440*Q2) + dth2*(6*A91600 + 8*A91800*dth2 + 6*A91620*Q2)) + (2*A91240 + 2*A91260*Q2)*Q4));\n  dA_dQ[0]  = dth2*(dth2*(2*A00620*dth2*Q + Q*(2*A00420 + 4*A00440*Q2)) + (4*A00240 + 6*A00260*Q2)*Q3) + (6*A00060 + 8*A00080*Q2)*Q5 + dr*(dth2*(dth2*(2*A01620*dth2*Q + Q*(2*A01420 + 4*A01440*Q2)) + (4*A01240 + 6*A01260*Q2)*Q3) + dr*(dth2*(2*A02420*dth2*Q + Q*(2*A02220 + 4*A02240*Q2)) + (4*A02040 + 6*A02060*Q2)*Q3 + dr*(dth2*(2*A03420*dth2*Q + Q*(2*A03220 + 4*A03240*Q2)) + dr*(2*A04220*dth2*Q + Q*(2*A04020 + 4*A04040*Q2) + dr*(2*A05220*dth2*Q + dr*(2*A06020*Q + 2*A07020*dr*Q) + Q*(2*A05020 + 4*A05040*Q2))) + (4*A03040 + 6*A03060*Q2)*Q3)) + (6*A01060 + 8*A01080*Q2)*Q5);\n  dA_dQ[1]  = Q5*(6*A10061*R + 8*A10081*Q2*R) + dth2*(Q3*(4*A10241*R + 6*A10261*Q2*R) + dth2*(2*A10621*dth2*Q*R + Q*(2*A10421*R + 4*A10441*Q2*R))) + dr*(6*A11061*Q5*R + dth2*(2*A11421*dth2*Q*R + 4*A11241*Q3*R) + dr*(Q3*(4*A12041*R + 6*A12061*Q2*R) + dth2*(2*A12421*dth2*Q*R + Q*(2*A12221*R + 4*A12241*Q2*R)) + dr*(2*A13221*dth2*Q*R + 4*A13041*Q3*R + dr*(2*A14221*dth2*Q*R + dr*(2*A15021*Q*R + 2*A16021*dr*Q*R) + Q*(2*A14021*R + 4*A14041*Q2*R)))));\n  dA_dQ[2]  = dth*(6*A20161*Q5*R + dth2*(2*A20521*dth2*Q*R + 4*A20341*Q3*R)) + dr*(dth*(6*A21161*Q5*R + dth2*(2*A21521*dth2*Q*R + 4*A21341*Q3*R)) + dr*(dth*(2*A22321*dth2*Q*R + 4*A22141*Q3*R) + dr*(dr*(2*A24121*dth*Q*R + 2*A25121*dr*dth*Q*R) + dth*(2*A23321*dth2*Q*R + 4*A23141*Q3*R))));\n  dA_dQ[3]  = dth2*(dth2*(2*A30620*dth2*Q + Q*(2*A30420 + 4*A30440*Q2)) + (4*A30240 + 6*A30260*Q2)*Q3) + (6*A30060 + 8*A30080*Q2)*Q5 + dr*(dth2*(dth2*(2*A31620*dth2*Q + Q*(2*A31420 + 4*A31440*Q2)) + (4*A31240 + 6*A31260*Q2)*Q3) + dr*(dth2*(2*A32420*dth2*Q + Q*(2*A32220 + 4*A32240*Q2)) + (4*A32040 + 6*A32060*Q2)*Q3 + dr*(dth2*(2*A33420*dth2*Q + Q*(2*A33220 + 4*A33240*Q2)) + dr*(2*A34220*dth2*Q + Q*(2*A34020 + 4*A34040*Q2) + dr*(2*A35220*dth2*Q + dr*(2*A36020*Q + 2*A37020*dr*Q) + Q*(2*A35020 + 4*A35040*Q2))) + (4*A33040 + 6*A33060*Q2)*Q3)) + (6*A31060 + 8*A31080*Q2)*Q5);\n  dA_dQ[4]  = dth2*(dth2*(2*A40620*dth2*Q + 4*A40440*Q3) + 6*A40260*Q5) + 8*A40080*Q7 + dr*(dth2*(dth2*(2*A41620*dth2*Q + 4*A41440*Q3) + 6*A41260*Q5) + dr*(dth2*(2*A42420*dth2*Q + 4*A42240*Q3) + 6*A42060*Q5 + dr*(dth2*(2*A43420*dth2*Q + 4*A43240*Q3) + dr*(2*A44220*dth2*Q + 4*A44040*Q3 + dr*(2*A45220*dth2*Q + dr*(2*A46020*Q + 2*A47020*dr*Q) + 4*A45040*Q3)) + 6*A43060*Q5)) + 8*A41080*Q7);\n  dA_dQ[5]  = dr2*(dr2*(2*A56120*dr2*dth*Q + dth*(2*A54320*dth2*Q + 4*A54140*Q3)) + dth*(dth2*(2*A52520*dth2*Q + 4*A52340*Q3) + 6*A52160*Q5)) + dth*(dth2*(dth2*(2*A50720*dth2*Q + 4*A50540*Q3) + 6*A50360*Q5) + 8*A50180*Q7);\n  dA_dQ[6]  = Q5*(6*A60061*R + 8*A60081*Q2*R) + dth2*(Q3*(4*A60241*R + 6*A60261*Q2*R) + dth2*(2*A60621*dth2*Q*R + Q*(2*A60421*R + 4*A60441*Q2*R))) + dr*(6*A61061*Q5*R + dth2*(2*A61421*dth2*Q*R + 4*A61241*Q3*R) + dr*(Q3*(4*A62041*R + 6*A62061*Q2*R) + dth2*(2*A62421*dth2*Q*R + Q*(2*A62221*R + 4*A62241*Q2*R)) + dr*(2*A63221*dth2*Q*R + 4*A63041*Q3*R + dr*(2*A64221*dth2*Q*R + dr*(2*A65021*Q*R + 2*A66021*dr*Q*R) + Q*(2*A64021*R + 4*A64041*Q2*R)))));\n  dA_dQ[7]  = dth2*(dth2*(2*A70620*dth2*Q + 4*A70440*Q3) + 6*A70260*Q5) + 8*A70080*Q7 + dr*(dth2*(dth2*(2*A71620*dth2*Q + 4*A71440*Q3) + 6*A71260*Q5) + dr*(dth2*(2*A72420*dth2*Q + 4*A72240*Q3) + 6*A72060*Q5 + dr*(dth2*(2*A73420*dth2*Q + 4*A73240*Q3) + dr*(2*A74220*dth2*Q + 4*A74040*Q3 + dr*(2*A75220*dth2*Q + dr*(2*A76020*Q + 2*A77020*dr*Q) + 4*A75040*Q3)) + 6*A73060*Q5)) + 8*A71080*Q7);\n  dA_dQ[8]  = dth*(6*A80161*Q5*R + dth2*(2*A80521*dth2*Q*R + 4*A80341*Q3*R)) + dr*(dth*(6*A81161*Q5*R + dth2*(2*A81521*dth2*Q*R + 4*A81341*Q3*R)) + dr*(dth*(2*A82321*dth2*Q*R + 4*A82141*Q3*R) + dr*(dr*(2*A84121*dth*Q*R + 2*A85121*dr*dth*Q*R) + dth*(2*A83321*dth2*Q*R + 4*A83141*Q3*R))));\n  dA_dQ[9]  = dth2*(dth2*(2*A90620*dth2*Q + Q*(2*A90420 + 4*A90440*Q2)) + (4*A90240 + 6*A90260*Q2)*Q3) + (6*A90060 + 8*A90080*Q2)*Q5 + dr*(dth2*(dth2*(2*A91620*dth2*Q + Q*(2*A91420 + 4*A91440*Q2)) + (4*A91240 + 6*A91260*Q2)*Q3) + dr*(dth2*(2*A92420*dth2*Q + Q*(2*A92220 + 4*A92240*Q2)) + (4*A92040 + 6*A92060*Q2)*Q3 + dr*(dth2*(2*A93420*dth2*Q + Q*(2*A93220 + 4*A93240*Q2)) + dr*(2*A94220*dth2*Q + Q*(2*A94020 + 4*A94040*Q2) + dr*(2*A95220*dth2*Q + dr*(2*A96020*Q + 2*A97020*dr*Q) + Q*(2*A95020 + 4*A95040*Q2))) + (4*A93040 + 6*A93060*Q2)*Q3)) + (6*A91060 + 8*A91080*Q2)*Q5);\n  dA_dR[0]  = 0;\n  dA_dR[1]  = dth2*(dth2*(Q2*(A10421 + A10441*Q2) + dth2*(A10601 + A10801*dth2 + A10621*Q2)) + (A10241 + A10261*Q2)*Q4) + (A10061 + A10081*Q2)*Q6 + dr*(dth2*(dth2*(A11601*dth2 + A11421*Q2) + A11241*Q4) + dr*(dth2*(Q2*(A12221 + A12241*Q2) + dth2*(A12401 + A12601*dth2 + A12421*Q2)) + (A12041 + A12061*Q2)*Q4 + dr*(dth2*(A13401*dth2 + A13221*Q2) + dr*(Q2*(A14021 + A14041*Q2) + dth2*(A14201 + A14401*dth2 + A14221*Q2) + dr*(A15201*dth2 + A15021*Q2 + dr*(A16001 + dr*(A17001 + A18001*dr) + A16201*dth2 + A16021*Q2))) + A13041*Q4)) + A11061*Q6);\n  dA_dR[2]  = dth*(dth2*(dth2*(A20701*dth2 + A20521*Q2) + A20341*Q4) + A20161*Q6) + dr*(dr*(dth*(dth2*(A22501*dth2 + A22321*Q2) + A22141*Q4) + dr*(dr*(dth*(A24301*dth2 + A24121*Q2) + dr*(dr*(A26101*dth + A27101*dr*dth) + dth*(A25301*dth2 + A25121*Q2))) + dth*(dth2*(A23501*dth2 + A23321*Q2) + A23141*Q4))) + dth*(dth2*(dth2*(A21701*dth2 + A21521*Q2) + A21341*Q4) + A21161*Q6));\n  dA_dR[3]  = 0;\n  dA_dR[4]  = 0;\n  dA_dR[5]  = 0;\n  dA_dR[6]  = dth2*(dth2*(Q2*(A60421 + A60441*Q2) + dth2*(A60601 + A60801*dth2 + A60621*Q2)) + (A60241 + A60261*Q2)*Q4) + (A60061 + A60081*Q2)*Q6 + dr*(dth2*(dth2*(A61601*dth2 + A61421*Q2) + A61241*Q4) + dr*(dth2*(Q2*(A62221 + A62241*Q2) + dth2*(A62401 + A62601*dth2 + A62421*Q2)) + (A62041 + A62061*Q2)*Q4 + dr*(dth2*(A63401*dth2 + A63221*Q2) + dr*(Q2*(A64021 + A64041*Q2) + dth2*(A64201 + A64401*dth2 + A64221*Q2) + dr*(A65201*dth2 + A65021*Q2 + dr*(A66001 + dr*(A67001 + A68001*dr) + A66201*dth2 + A66021*Q2))) + A63041*Q4)) + A61061*Q6);\n  dA_dR[7]  = 0;\n  dA_dR[8]  = dth*(dth2*(dth2*(A80701*dth2 + A80521*Q2) + A80341*Q4) + A80161*Q6) + dr*(dr*(dth*(dth2*(A82501*dth2 + A82321*Q2) + A82141*Q4) + dr*(dr*(dth*(A84301*dth2 + A84121*Q2) + dr*(dr*(A86101*dth + A87101*dr*dth) + dth*(A85301*dth2 + A85121*Q2))) + dth*(dth2*(A83501*dth2 + A83321*Q2) + A83141*Q4))) + dth*(dth2*(dth2*(A81701*dth2 + A81521*Q2) + A81341*Q4) + A81161*Q6));\n  dA_dR[9]  = 0;\n  for(int i=0; i<10; i++)\n  {\n    dA_dph[i] = dA_dQ[i]*dQ_dph + dA_dR[i]*dR_dph;\n    dA_dt[i]  = -Omega*dA_dph[i];\n  }\n\n  /* d^2A/dx^2 */\n  d2A_dr2[0]  = dth2*(Q2*(2*A02220 + 2*A02240*Q2) + dth2*(2*A02400 + 2*A02600*dth2 + 2*A02420*Q2)) + (2*A02040 + 2*A02060*Q2)*Q4 + dr*(dth2*(Q2*(6*A03220 + 6*A03240*Q2) + dth2*(6*A03400 + 6*A03600*dth2 + 6*A03420*Q2)) + dr*(Q2*(12*A04020 + 12*A04040*Q2) + dth2*(12*A04200 + 12*A04400*dth2 + 12*A04220*Q2) + dr*(Q2*(20*A05020 + 20*A05040*Q2) + dth2*(20*A05200 + 20*A05400*dth2 + 20*A05220*Q2) + dr*(30*A06000 + 30*A06200*dth2 + 30*A06020*Q2 + dr*(42*A07000 + dr*(56*A08000 + 72*A09000*dr) + 42*A07200*dth2 + 42*A07020*Q2)))) + (6*A03040 + 6*A03060*Q2)*Q4);\n  d2A_dr2[1]  = Q4*(2*A12041*R + 2*A12061*Q2*R) + dth2*(Q2*(2*A12221*R + 2*A12241*Q2*R) + dth2*(2*A12401*R + 2*A12601*dth2*R + 2*A12421*Q2*R)) + dr*(6*A13041*Q4*R + dth2*(6*A13401*dth2*R + 6*A13221*Q2*R) + dr*(Q2*(12*A14021*R + 12*A14041*Q2*R) + dth2*(12*A14201*R + 12*A14401*dth2*R + 12*A14221*Q2*R) + dr*(20*A15201*dth2*R + 20*A15021*Q2*R + dr*(30*A16001*R + 30*A16201*dth2*R + 30*A16021*Q2*R + dr*(42*A17001*R + 56*A18001*dr*R)))));\n  d2A_dr2[2]  = dth*(2*A22141*Q4*R + dth2*(2*A22501*dth2*R + 2*A22321*Q2*R)) + dr*(dth*(6*A23141*Q4*R + dth2*(6*A23501*dth2*R + 6*A23321*Q2*R)) + dr*(dth*(12*A24301*dth2*R + 12*A24121*Q2*R) + dr*(dr*(30*A26101*dth*R + 42*A27101*dr*dth*R) + dth*(20*A25301*dth2*R + 20*A25121*Q2*R))));\n  d2A_dr2[3]  = dth2*(Q2*(2*A32220 + 2*A32240*Q2) + dth2*(2*A32400 + 2*A32600*dth2 + 2*A32420*Q2)) + (2*A32040 + 2*A32060*Q2)*Q4 + dr*(dth2*(Q2*(6*A33220 + 6*A33240*Q2) + dth2*(6*A33400 + 6*A33600*dth2 + 6*A33420*Q2)) + dr*(Q2*(12*A34020 + 12*A34040*Q2) + dth2*(12*A34200 + 12*A34400*dth2 + 12*A34220*Q2) + dr*(Q2*(20*A35020 + 20*A35040*Q2) + dth2*(20*A35200 + 20*A35400*dth2 + 20*A35220*Q2) + dr*(30*A36000 + 30*A36200*dth2 + 30*A36020*Q2 + dr*(42*A37000 + dr*(56*A38000 + 72*A39000*dr) + 42*A37200*dth2 + 42*A37020*Q2)))) + (6*A33040 + 6*A33060*Q2)*Q4);\n  d2A_dr2[4]  = dth2*(dth2*(2*A42600*dth2 + 2*A42420*Q2) + 2*A42240*Q4) + 2*A42060*Q6 + dr*(dth2*(dth2*(6*A43600*dth2 + 6*A43420*Q2) + 6*A43240*Q4) + dr*(dth2*(12*A44400*dth2 + 12*A44220*Q2) + 12*A44040*Q4 + dr*(dth2*(20*A45400*dth2 + 20*A45220*Q2) + dr*(30*A46200*dth2 + 30*A46020*Q2 + dr*(dr*(56*A48000 + 72*A49000*dr) + 42*A47200*dth2 + 42*A47020*Q2)) + 20*A45040*Q4)) + 6*A43060*Q6);\n  d2A_dr2[5]  = dr2*(dr2*(56*A58100*dr2*dth + dth*(30*A56300*dth2 + 30*A56120*Q2)) + dth*(dth2*(12*A54500*dth2 + 12*A54320*Q2) + 12*A54140*Q4)) + dth*(dth2*(dth2*(2*A52700*dth2 + 2*A52520*Q2) + 2*A52340*Q4) + 2*A52160*Q6);\n  d2A_dr2[6]  = Q4*(2*A62041*R + 2*A62061*Q2*R) + dth2*(Q2*(2*A62221*R + 2*A62241*Q2*R) + dth2*(2*A62401*R + 2*A62601*dth2*R + 2*A62421*Q2*R)) + dr*(6*A63041*Q4*R + dth2*(6*A63401*dth2*R + 6*A63221*Q2*R) + dr*(Q2*(12*A64021*R + 12*A64041*Q2*R) + dth2*(12*A64201*R + 12*A64401*dth2*R + 12*A64221*Q2*R) + dr*(20*A65201*dth2*R + 20*A65021*Q2*R + dr*(30*A66001*R + 30*A66201*dth2*R + 30*A66021*Q2*R + dr*(42*A67001*R + 56*A68001*dr*R)))));\n  d2A_dr2[7]  = dth2*(dth2*(2*A72600*dth2 + 2*A72420*Q2) + 2*A72240*Q4) + 2*A72060*Q6 + dr*(dth2*(dth2*(6*A73600*dth2 + 6*A73420*Q2) + 6*A73240*Q4) + dr*(dth2*(12*A74400*dth2 + 12*A74220*Q2) + 12*A74040*Q4 + dr*(dth2*(20*A75400*dth2 + 20*A75220*Q2) + dr*(30*A76200*dth2 + 30*A76020*Q2 + dr*(dr*(56*A78000 + 72*A79000*dr) + 42*A77200*dth2 + 42*A77020*Q2)) + 20*A75040*Q4)) + 6*A73060*Q6);\n  d2A_dr2[8]  = dth*(2*A82141*Q4*R + dth2*(2*A82501*dth2*R + 2*A82321*Q2*R)) + dr*(dth*(6*A83141*Q4*R + dth2*(6*A83501*dth2*R + 6*A83321*Q2*R)) + dr*(dth*(12*A84301*dth2*R + 12*A84121*Q2*R) + dr*(dr*(30*A86101*dth*R + 42*A87101*dr*dth*R) + dth*(20*A85301*dth2*R + 20*A85121*Q2*R))));\n  d2A_dr2[9]  = dth2*(Q2*(2*A92220 + 2*A92240*Q2) + dth2*(2*A92400 + 2*A92600*dth2 + 2*A92420*Q2)) + (2*A92040 + 2*A92060*Q2)*Q4 + dr*(dth2*(Q2*(6*A93220 + 6*A93240*Q2) + dth2*(6*A93400 + 6*A93600*dth2 + 6*A93420*Q2)) + dr*(Q2*(12*A94020 + 12*A94040*Q2) + dth2*(12*A94200 + 12*A94400*dth2 + 12*A94220*Q2) + dr*(Q2*(20*A95020 + 20*A95040*Q2) + dth2*(20*A95200 + 20*A95400*dth2 + 20*A95220*Q2) + dr*(30*A96000 + 30*A96200*dth2 + 30*A96020*Q2 + dr*(42*A97000 + dr*(56*A98000 + 72*A99000*dr) + 42*A97200*dth2 + 42*A97020*Q2)))) + (6*A93040 + 6*A93060*Q2)*Q4);\n  d2A_dth2[0] = dth2*(Q2*(12*A00420 + 12*A00440*Q2) + dth2*(30*A00600 + 56*A00800*dth2 + 30*A00620*Q2)) + (2*A00240 + 2*A00260*Q2)*Q4 + dr*(dth2*(Q2*(12*A01420 + 12*A01440*Q2) + dth2*(30*A01600 + 56*A01800*dth2 + 30*A01620*Q2)) + dr*(Q2*(2*A02220 + 2*A02240*Q2) + dth2*(12*A02400 + 30*A02600*dth2 + 12*A02420*Q2) + dr*(Q2*(2*A03220 + 2*A03240*Q2) + dth2*(12*A03400 + 30*A03600*dth2 + 12*A03420*Q2) + dr*(2*A04200 + 12*A04400*dth2 + 2*A04220*Q2 + dr*(2*A05200 + dr*(2*A06200 + 2*A07200*dr) + 12*A05400*dth2 + 2*A05220*Q2)))) + (2*A01240 + 2*A01260*Q2)*Q4);\n  d2A_dth2[1] = Q4*(2*A10241*R + 2*A10261*Q2*R) + dth2*(Q2*(12*A10421*R + 12*A10441*Q2*R) + dth2*(30*A10601*R + 56*A10801*dth2*R + 30*A10621*Q2*R)) + dr*(2*A11241*Q4*R + dth2*(30*A11601*dth2*R + 12*A11421*Q2*R) + dr*(Q2*(2*A12221*R + 2*A12241*Q2*R) + dth2*(12*A12401*R + 30*A12601*dth2*R + 12*A12421*Q2*R) + dr*(12*A13401*dth2*R + 2*A13221*Q2*R + dr*(2*A14201*R + 12*A14401*dth2*R + 2*A14221*Q2*R + dr*(2*A15201*R + 2*A16201*dr*R)))));\n  d2A_dth2[2] = dth*(6*A20341*Q4*R + dth2*(42*A20701*dth2*R + 20*A20521*Q2*R)) + dr*(dth*(6*A21341*Q4*R + dth2*(42*A21701*dth2*R + 20*A21521*Q2*R)) + dr*(dth*(20*A22501*dth2*R + 6*A22321*Q2*R) + dr*(dr*(6*A24301*dth*R + 6*A25301*dr*dth*R) + dth*(20*A23501*dth2*R + 6*A23321*Q2*R))));\n  d2A_dth2[3] = dth2*(Q2*(12*A30420 + 12*A30440*Q2) + dth2*(30*A30600 + 56*A30800*dth2 + 30*A30620*Q2)) + (2*A30240 + 2*A30260*Q2)*Q4 + dr*(dth2*(Q2*(12*A31420 + 12*A31440*Q2) + dth2*(30*A31600 + 56*A31800*dth2 + 30*A31620*Q2)) + dr*(Q2*(2*A32220 + 2*A32240*Q2) + dth2*(12*A32400 + 30*A32600*dth2 + 12*A32420*Q2) + dr*(Q2*(2*A33220 + 2*A33240*Q2) + dth2*(12*A33400 + 30*A33600*dth2 + 12*A33420*Q2) + dr*(2*A34200 + 12*A34400*dth2 + 2*A34220*Q2 + dr*(2*A35200 + dr*(2*A36200 + 2*A37200*dr) + 12*A35400*dth2 + 2*A35220*Q2)))) + (2*A31240 + 2*A31260*Q2)*Q4);\n  d2A_dth2[4] = dth2*(dth2*(56*A40800*dth2 + 30*A40620*Q2) + 12*A40440*Q4) + 2*A40260*Q6 + dr*(dth2*(dth2*(56*A41800*dth2 + 30*A41620*Q2) + 12*A41440*Q4) + dr*(dth2*(30*A42600*dth2 + 12*A42420*Q2) + 2*A42240*Q4 + dr*(dth2*(30*A43600*dth2 + 12*A43420*Q2) + dr*(12*A44400*dth2 + 2*A44220*Q2 + dr*(dr*(2*A46200 + 2*A47200*dr) + 12*A45400*dth2 + 2*A45220*Q2)) + 2*A43240*Q4)) + 2*A41260*Q6);\n  d2A_dth2[5] = dr2*(dr2*(6*A56300*dr2*dth + dth*(20*A54500*dth2 + 6*A54320*Q2)) + dth*(dth2*(42*A52700*dth2 + 20*A52520*Q2) + 6*A52340*Q4)) + dth*(dth2*(dth2*(72*A50900*dth2 + 42*A50720*Q2) + 20*A50540*Q4) + 6*A50360*Q6);\n  d2A_dth2[6] = Q4*(2*A60241*R + 2*A60261*Q2*R) + dth2*(Q2*(12*A60421*R + 12*A60441*Q2*R) + dth2*(30*A60601*R + 56*A60801*dth2*R + 30*A60621*Q2*R)) + dr*(2*A61241*Q4*R + dth2*(30*A61601*dth2*R + 12*A61421*Q2*R) + dr*(Q2*(2*A62221*R + 2*A62241*Q2*R) + dth2*(12*A62401*R + 30*A62601*dth2*R + 12*A62421*Q2*R) + dr*(12*A63401*dth2*R + 2*A63221*Q2*R + dr*(2*A64201*R + 12*A64401*dth2*R + 2*A64221*Q2*R + dr*(2*A65201*R + 2*A66201*dr*R)))));\n  d2A_dth2[7] = dth2*(dth2*(56*A70800*dth2 + 30*A70620*Q2) + 12*A70440*Q4) + 2*A70260*Q6 + dr*(dth2*(dth2*(56*A71800*dth2 + 30*A71620*Q2) + 12*A71440*Q4) + dr*(dth2*(30*A72600*dth2 + 12*A72420*Q2) + 2*A72240*Q4 + dr*(dth2*(30*A73600*dth2 + 12*A73420*Q2) + dr*(12*A74400*dth2 + 2*A74220*Q2 + dr*(dr*(2*A76200 + 2*A77200*dr) + 12*A75400*dth2 + 2*A75220*Q2)) + 2*A73240*Q4)) + 2*A71260*Q6);\n  d2A_dth2[8] = dth*(6*A80341*Q4*R + dth2*(42*A80701*dth2*R + 20*A80521*Q2*R)) + dr*(dth*(6*A81341*Q4*R + dth2*(42*A81701*dth2*R + 20*A81521*Q2*R)) + dr*(dth*(20*A82501*dth2*R + 6*A82321*Q2*R) + dr*(dr*(6*A84301*dth*R + 6*A85301*dr*dth*R) + dth*(20*A83501*dth2*R + 6*A83321*Q2*R))));\n  d2A_dth2[9] = dth2*(Q2*(12*A90420 + 12*A90440*Q2) + dth2*(30*A90600 + 56*A90800*dth2 + 30*A90620*Q2)) + (2*A90240 + 2*A90260*Q2)*Q4 + dr*(dth2*(Q2*(12*A91420 + 12*A91440*Q2) + dth2*(30*A91600 + 56*A91800*dth2 + 30*A91620*Q2)) + dr*(Q2*(2*A92220 + 2*A92240*Q2) + dth2*(12*A92400 + 30*A92600*dth2 + 12*A92420*Q2) + dr*(Q2*(2*A93220 + 2*A93240*Q2) + dth2*(12*A93400 + 30*A93600*dth2 + 12*A93420*Q2) + dr*(2*A94200 + 12*A94400*dth2 + 2*A94220*Q2 + dr*(2*A95200 + dr*(2*A96200 + 2*A97200*dr) + 12*A95400*dth2 + 2*A95220*Q2)))) + (2*A91240 + 2*A91260*Q2)*Q4);\n  d2A_dQ2[0]  = dth2*(Q2*(12*A00240 + 30*A00260*Q2) + dth2*(2*A00420 + 2*A00620*dth2 + 12*A00440*Q2)) + (30*A00060 + 56*A00080*Q2)*Q4 + dr*(dth2*(Q2*(12*A01240 + 30*A01260*Q2) + dth2*(2*A01420 + 2*A01620*dth2 + 12*A01440*Q2)) + dr*(Q2*(12*A02040 + 30*A02060*Q2) + dth2*(2*A02220 + 2*A02420*dth2 + 12*A02240*Q2) + dr*(Q2*(12*A03040 + 30*A03060*Q2) + dth2*(2*A03220 + 2*A03420*dth2 + 12*A03240*Q2) + dr*(2*A04020 + 2*A04220*dth2 + 12*A04040*Q2 + dr*(2*A05020 + dr*(2*A06020 + 2*A07020*dr) + 2*A05220*dth2 + 12*A05040*Q2)))) + (30*A01060 + 56*A01080*Q2)*Q4);\n  d2A_dQ2[1]  = Q4*(30*A10061*R + 56*A10081*Q2*R) + dth2*(Q2*(12*A10241*R + 30*A10261*Q2*R) + dth2*(2*A10421*R + 2*A10621*dth2*R + 12*A10441*Q2*R)) + dr*(30*A11061*Q4*R + dth2*(2*A11421*dth2*R + 12*A11241*Q2*R) + dr*(Q2*(12*A12041*R + 30*A12061*Q2*R) + dth2*(2*A12221*R + 2*A12421*dth2*R + 12*A12241*Q2*R) + dr*(2*A13221*dth2*R + 12*A13041*Q2*R + dr*(2*A14021*R + 2*A14221*dth2*R + 12*A14041*Q2*R + dr*(2*A15021*R + 2*A16021*dr*R)))));\n  d2A_dQ2[2]  = dth*(30*A20161*Q4*R + dth2*(2*A20521*dth2*R + 12*A20341*Q2*R)) + dr*(dth*(30*A21161*Q4*R + dth2*(2*A21521*dth2*R + 12*A21341*Q2*R)) + dr*(dth*(2*A22321*dth2*R + 12*A22141*Q2*R) + dr*(dr*(2*A24121*dth*R + 2*A25121*dr*dth*R) + dth*(2*A23321*dth2*R + 12*A23141*Q2*R))));\n  d2A_dQ2[3]  = dth2*(Q2*(12*A30240 + 30*A30260*Q2) + dth2*(2*A30420 + 2*A30620*dth2 + 12*A30440*Q2)) + (30*A30060 + 56*A30080*Q2)*Q4 + dr*(dth2*(Q2*(12*A31240 + 30*A31260*Q2) + dth2*(2*A31420 + 2*A31620*dth2 + 12*A31440*Q2)) + dr*(Q2*(12*A32040 + 30*A32060*Q2) + dth2*(2*A32220 + 2*A32420*dth2 + 12*A32240*Q2) + dr*(Q2*(12*A33040 + 30*A33060*Q2) + dth2*(2*A33220 + 2*A33420*dth2 + 12*A33240*Q2) + dr*(2*A34020 + 2*A34220*dth2 + 12*A34040*Q2 + dr*(2*A35020 + dr*(2*A36020 + 2*A37020*dr) + 2*A35220*dth2 + 12*A35040*Q2)))) + (30*A31060 + 56*A31080*Q2)*Q4);\n  d2A_dQ2[4]  = dth2*(dth2*(2*A40620*dth2 + 12*A40440*Q2) + 30*A40260*Q4) + 56*A40080*Q6 + dr*(dth2*(dth2*(2*A41620*dth2 + 12*A41440*Q2) + 30*A41260*Q4) + dr*(dth2*(2*A42420*dth2 + 12*A42240*Q2) + 30*A42060*Q4 + dr*(dth2*(2*A43420*dth2 + 12*A43240*Q2) + dr*(2*A44220*dth2 + 12*A44040*Q2 + dr*(dr*(2*A46020 + 2*A47020*dr) + 2*A45220*dth2 + 12*A45040*Q2)) + 30*A43060*Q4)) + 56*A41080*Q6);\n  d2A_dQ2[5]  = dr2*(dr2*(2*A56120*dr2*dth + dth*(2*A54320*dth2 + 12*A54140*Q2)) + dth*(dth2*(2*A52520*dth2 + 12*A52340*Q2) + 30*A52160*Q4)) + dth*(dth2*(dth2*(2*A50720*dth2 + 12*A50540*Q2) + 30*A50360*Q4) + 56*A50180*Q6);\n  d2A_dQ2[6]  = Q4*(30*A60061*R + 56*A60081*Q2*R) + dth2*(Q2*(12*A60241*R + 30*A60261*Q2*R) + dth2*(2*A60421*R + 2*A60621*dth2*R + 12*A60441*Q2*R)) + dr*(30*A61061*Q4*R + dth2*(2*A61421*dth2*R + 12*A61241*Q2*R) + dr*(Q2*(12*A62041*R + 30*A62061*Q2*R) + dth2*(2*A62221*R + 2*A62421*dth2*R + 12*A62241*Q2*R) + dr*(2*A63221*dth2*R + 12*A63041*Q2*R + dr*(2*A64021*R + 2*A64221*dth2*R + 12*A64041*Q2*R + dr*(2*A65021*R + 2*A66021*dr*R)))));\n  d2A_dQ2[7]  = dth2*(dth2*(2*A70620*dth2 + 12*A70440*Q2) + 30*A70260*Q4) + 56*A70080*Q6 + dr*(dth2*(dth2*(2*A71620*dth2 + 12*A71440*Q2) + 30*A71260*Q4) + dr*(dth2*(2*A72420*dth2 + 12*A72240*Q2) + 30*A72060*Q4 + dr*(dth2*(2*A73420*dth2 + 12*A73240*Q2) + dr*(2*A74220*dth2 + 12*A74040*Q2 + dr*(dr*(2*A76020 + 2*A77020*dr) + 2*A75220*dth2 + 12*A75040*Q2)) + 30*A73060*Q4)) + 56*A71080*Q6);\n  d2A_dQ2[8]  = dth*(30*A80161*Q4*R + dth2*(2*A80521*dth2*R + 12*A80341*Q2*R)) + dr*(dth*(30*A81161*Q4*R + dth2*(2*A81521*dth2*R + 12*A81341*Q2*R)) + dr*(dth*(2*A82321*dth2*R + 12*A82141*Q2*R) + dr*(dr*(2*A84121*dth*R + 2*A85121*dr*dth*R) + dth*(2*A83321*dth2*R + 12*A83141*Q2*R))));\n  d2A_dQ2[9]  = dth2*(Q2*(12*A90240 + 30*A90260*Q2) + dth2*(2*A90420 + 2*A90620*dth2 + 12*A90440*Q2)) + (30*A90060 + 56*A90080*Q2)*Q4 + dr*(dth2*(Q2*(12*A91240 + 30*A91260*Q2) + dth2*(2*A91420 + 2*A91620*dth2 + 12*A91440*Q2)) + dr*(Q2*(12*A92040 + 30*A92060*Q2) + dth2*(2*A92220 + 2*A92420*dth2 + 12*A92240*Q2) + dr*(Q2*(12*A93040 + 30*A93060*Q2) + dth2*(2*A93220 + 2*A93420*dth2 + 12*A93240*Q2) + dr*(2*A94020 + 2*A94220*dth2 + 12*A94040*Q2 + dr*(2*A95020 + dr*(2*A96020 + 2*A97020*dr) + 2*A95220*dth2 + 12*A95040*Q2)))) + (30*A91060 + 56*A91080*Q2)*Q4);\n  d2A_dQR[0]  = 0;\n  d2A_dQR[1]  = dth2*(dth2*(2*A10621*dth2*Q + Q*(2*A10421 + 4*A10441*Q2)) + (4*A10241 + 6*A10261*Q2)*Q3) + (6*A10061 + 8*A10081*Q2)*Q5 + dr*(dth2*(2*A11421*dth2*Q + 4*A11241*Q3) + dr*(dth2*(2*A12421*dth2*Q + Q*(2*A12221 + 4*A12241*Q2)) + (4*A12041 + 6*A12061*Q2)*Q3 + dr*(2*A13221*dth2*Q + dr*(2*A14221*dth2*Q + dr*(2*A15021*Q + 2*A16021*dr*Q) + Q*(2*A14021 + 4*A14041*Q2)) + 4*A13041*Q3)) + 6*A11061*Q5);\n  d2A_dQR[2]  = dth*(dth2*(2*A20521*dth2*Q + 4*A20341*Q3) + 6*A20161*Q5) + dr*(dr*(dth*(2*A22321*dth2*Q + 4*A22141*Q3) + dr*(dr*(2*A24121*dth*Q + 2*A25121*dr*dth*Q) + dth*(2*A23321*dth2*Q + 4*A23141*Q3))) + dth*(dth2*(2*A21521*dth2*Q + 4*A21341*Q3) + 6*A21161*Q5));\n  d2A_dQR[3]  = 0;\n  d2A_dQR[4]  = 0;\n  d2A_dQR[5]  = 0;\n  d2A_dQR[6]  = dth2*(dth2*(2*A60621*dth2*Q + Q*(2*A60421 + 4*A60441*Q2)) + (4*A60241 + 6*A60261*Q2)*Q3) + (6*A60061 + 8*A60081*Q2)*Q5 + dr*(dth2*(2*A61421*dth2*Q + 4*A61241*Q3) + dr*(dth2*(2*A62421*dth2*Q + Q*(2*A62221 + 4*A62241*Q2)) + (4*A62041 + 6*A62061*Q2)*Q3 + dr*(2*A63221*dth2*Q + dr*(2*A64221*dth2*Q + dr*(2*A65021*Q + 2*A66021*dr*Q) + Q*(2*A64021 + 4*A64041*Q2)) + 4*A63041*Q3)) + 6*A61061*Q5);\n  d2A_dQR[7]  = 0;\n  d2A_dQR[8]  = dth*(dth2*(2*A80521*dth2*Q + 4*A80341*Q3) + 6*A80161*Q5) + dr*(dr*(dth*(2*A82321*dth2*Q + 4*A82141*Q3) + dr*(dr*(2*A84121*dth*Q + 2*A85121*dr*dth*Q) + dth*(2*A83321*dth2*Q + 4*A83141*Q3))) + dth*(dth2*(2*A81521*dth2*Q + 4*A81341*Q3) + 6*A81161*Q5));\n  d2A_dQR[9]  = 0;\n  for(int i=0; i<10; i++)\n  {\n    d2A_dph2[i] = dA_dR[i]*d2R_dph2 + dA_dQ[i]*d2Q_dph2 + 2.0*dQ_dph*dR_dph*d2A_dQR[i] + dQ_dph*dQ_dph*d2A_dQ2[i];\n    d2A_dtph[i] = -Omega*d2A_dph2[i];\n    d2A_dt2[i]  = Omega*Omega*d2A_dph2[i];\n  }\n\n  /* s */\n  s2         = alpha20*dr2 + alpha02*dth2 + beta*Q2;\n\n  /* ds/dx */\n  ds2_dr     = 2*alpha20*dr;\n  ds2_dth    = 2*alpha02*dth;\n  ds2_dQ     = 2*beta*Q;\n  ds2_dph    = ds2_dQ*dQ_dph;\n  ds2_dt     = -Omega*ds2_dph;\n\n  /* d^2s/dx^2 */\n  d2s2_dr2   = 2*alpha20;\n  d2s2_dth2  = 2*alpha02;\n  d2s2_dQ2   = 2*beta;\n  d2s2_dph2  = ds2_dQ*d2Q_dph2 + dQ_dph*dQ_dph*d2s2_dQ2;\n  d2s2_dtph  = -Omega*d2s2_dph2;\n  d2s2_dt2   = Omega*Omega*d2s2_dph2;\n\n  sqrts2     = sqrt(s2);\n  s2_15      = s2*sqrts2;\n  s2_25      = s2*s2_15;\n  s2_35      = s2*s2_25;\n  s2_45      = s2*s2_35;\n  s2_55      = s2*s2_45;\n\n  /* hS */\n  for(int i=0; i<10; i++)\n  {\n    hS[i] = A[i]/s2_35;\n  }\n\n  /* First derivatives of PhiS */\n  for(int i=0; i<10; i++)\n  {\n    dhS_dt[i]  = (-7*ds2_dt*A[i] + 2*dA_dt[i]*s2)/(2.*s2_45);\n    dhS_dr[i]  = (-7*ds2_dr*A[i] + 2*dA_dr[i]*s2) /(2.*s2_45);\n    dhS_dth[i] = (-7*ds2_dth*A[i] + 2*dA_dth[i]*s2)/(2.*s2_45);\n    dhS_dph[i] = (-7*ds2_dph*A[i] + 2*dA_dph[i]*s2)/(2.*s2_45);\n  }\n  \n  /* Second derivatives of hS */\n  for(int i=0; i<10; i++)\n  {\n    d2hS_dr2[i] =\n      (63*ds2_dr*ds2_dr*A[i] - 14*s2*(2*dA_dr[i]*ds2_dr + d2s2_dr2*A[i]) + 4*d2A_dr2[i]*s2*s2)/(4.*s2_55);\n    d2hS_dth2[i] =\n      (63*ds2_dth*ds2_dth*A[i] - 14*s2*(2*dA_dth[i]*ds2_dth + d2s2_dth2*A[i]) + 4*d2A_dth2[i]*s2*s2)/(4.*s2_55);\n    d2hS_dph2[i] =\n      (63*ds2_dph*ds2_dph*A[i] - 14*s2*(2*dA_dph[i]*ds2_dph + d2s2_dph2*A[i]) + 4*d2A_dph2[i]*s2*s2)/(4.*s2_55);\n    d2hS_dt2[i] =\n      (63*ds2_dt*ds2_dt*A[i] - 14*s2*(2*dA_dt[i]*ds2_dt + d2s2_dt2*A[i]) + 4*d2A_dt2[i]*s2*s2)/(4.*s2_55);\n    d2hS_dtph[i] =\n      (63*ds2_dph*ds2_dt*A[i] - 14*s2*(dA_dt[i]*ds2_dph + dA_dph[i]*ds2_dt + d2s2_dtph*A[i]) + 4*d2A_dtph[i]*s2*s2)/(4.*s2_55);\n    d2hS_dtr[i]   = NAN;\n    d2hS_dtth[i]  = NAN;\n    d2hS_drth[i]  = NAN;\n    d2hS_drph[i]  = NAN;\n    d2hS_dthph[i] = NAN;\n  }\n\n  double a10, a2, a2p2r2pa2c2th2, a2p2r2pa2c2th3, a2p2r2pa2c2th4, a2p2r2pa2c2th5, a2pr2m2Mr2, a2pr2m2Mr3, a3, a4, a6, a8, costh2, costh4, M2, M3, r10, r11, r2, r2pa2cth22, r2pa2cth23, r3, r4, r5, r6, r7, r8, r9, sinth2, sinth3, sinth4, sin4th, cos2th, cos4th, cos6th, cos8th, cos10th, sin2th, r2pa2cth2, a2p2r2pa2c2th, a2pr2m2Mr, costh, sinth, tanth;\n\n  M2 = M*M;\n  M3 = M2*M;\n\n  a2 = a*a;\n  a3 = a2*a;\n  a4 = a2*a2;\n  a6 = a4*a2;\n  a8 = a4*a4;\n  a10 = a6*a4;\n\n  r2 = r*r;\n  r3 = r2*r;\n  r4 = r2*r2;\n  r5 = r3*r2;\n  r6 = r3*r3;\n  r7 = r4*r3;\n  r8 = r4*r4;\n  r9 = r5*r4;\n  r10 = r5*r5;\n  r11 = r6*r5;\n\n  sinth = sin(theta);\n  costh = cos(theta);\n  tanth = sinth/costh;\n\n  costh2 = costh*costh;\n  costh4 = costh2*costh2;\n  sinth2 = sinth*sinth;\n  sinth3 = sinth2*sinth;\n  sinth4 = sinth2*sinth2;\n\n  cos2th = costh2 - sinth2;\n  sin2th = 2.0*costh*sinth;\n  sin4th = 2.0*cos2th*sin2th;\n  cos4th = cos2th*cos2th - sin2th*sin2th;\n  cos6th = cos2th*cos2th*cos2th - 3.0*cos2th*sin2th*sin2th;\n  cos8th = cos4th*cos4th - sin4th*sin4th;\n  cos10th = cos2th*cos2th*cos2th*cos2th*cos2th - 10.0*cos2th*cos2th*cos2th*sin2th*sin2th + 5.0*cos2th*sin2th*sin2th*sin2th*sin2th;\n\n  r2pa2cth2 = a2*costh2 + r2;\n  r2pa2cth22 = r2pa2cth2*r2pa2cth2;\n  r2pa2cth23 = r2pa2cth22*r2pa2cth2;\n\n  a2p2r2pa2c2th = a2 + a2*cos2th + 2*r2;\n  a2p2r2pa2c2th2 = a2p2r2pa2c2th*a2p2r2pa2c2th;\n  a2p2r2pa2c2th3 = a2p2r2pa2c2th2*a2p2r2pa2c2th;\n  a2p2r2pa2c2th4 = a2p2r2pa2c2th2*a2p2r2pa2c2th2;\n  a2p2r2pa2c2th5 = a2p2r2pa2c2th3*a2p2r2pa2c2th2;\n\n  a2pr2m2Mr = a2 + r*(-2*M + r);\n  a2pr2m2Mr2 = a2pr2m2Mr*a2pr2m2Mr;\n  a2pr2m2Mr3 = a2pr2m2Mr2*a2pr2m2Mr;\n\n  src[0] = (-8*a*d2hS_dtph[0]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (16*dhS_dt[1]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2[0]/r2pa2cth2 + (d2hS_dr2[0]*(a2 - 2*M*r + r2))/r2pa2cth2 + (4*a*dhS_dr[3]*M*(a2*costh2 - r2))/r2pa2cth23 + (4*a*dhS_dph[1]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2[0]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (16*hS[7]*M*r*(-9*a4 + 3*a4*cos4th + 20*a2*M*r - 2*a2*cos2th*(3*a2 + 5*r*(-2*M + r)) + 2*a2*r2 - 8*M*r3 + 4*r4))/a2p2r2pa2c2th5 + (2*dhS_dr[0]*(5*a4*M - a4*cos4th*(M - r) + 3*a4*r - 16*a2*M*r2 + 8*a2*r3 + 4*a2*cos2th*(a2*(M + r) + 2*r3) - 24*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (8*a2pr2m2Mr*hS[4]*M*(3*a4*M - 27*a4*r + a4*cos4th*(M + 3*r) + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 6*r) + (10*M - 7*r)*r2) - 4*a2*r3 - 24*M*r4 + 16*r5))/a2p2r2pa2c2th5 + (16*a*hS[3]*M2*(3*a6 + 29*a4*r2 + a4*cos4th*(a2 + 7*r2) - 80*a2*M*r3 + 32*a2*r4 + 4*a2*cos2th*(a4 + 9*a2*r2 - 20*M*r3 + 6*r4) + 32*M*r5 - 8*r6))/(a2p2r2pa2c2th5*a2pr2m2Mr) + (8*hS[0]*M2*(3*a8 + 2*a6*r2 - 20*a4*M*r3 + a4*cos4th*(a4 - 2*a2*r2 + 20*M*r3 - 3*r4) - a4*r4 + 16*a2*M*r5 + 4*a2*cos2th*(a6 - 3*a2*r4 - 2*(2*M + r)*r5) + 8*a2*r6 + 8*r8))/(a2p2r2pa2c2th5*a2pr2m2Mr) - (32*a2*dhS_dt[2]*M*r*sin2th)/a2p2r2pa2c2th3 - (96*a2*a2pr2m2Mr*hS[5]*M*(a2 + a2*cos2th - 6*r2)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2[0]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (2*hS[9]*M*(-2*a6*M + a6*cos6th*M + 30*a6*r + 3*a6*cos6th*r - 200*a4*M*r2 + 2*a4*cos4th*(a2*(M + 9*r) + 2*(-14*M + 5*r)*r2) + 60*a4*r3 + 320*a2*M2*r3 - a2*cos2th*(a4*(M - 45*r) + 16*a2*(16*M - 5*r)*r2 - 16*(20*M2 - 11*M*r + r2)*r3) - 208*a2*M*r4 + 16*a2*r5 - 128*M2*r5 + 128*M*r6 - 32*r7))/(a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (64*a*dhS_dph[2]*M*r)/(a2p2r2pa2c2th3*tanth) + (64*a*dhS_dth[3]*M*r)/(a2p2r2pa2c2th3*tanth) + (dhS_dth[0]*(3*a4 + a4*cos4th + 32*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-4*M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth);\n  src[1] = (-8*a*d2hS_dtph[1]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt[4]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2[1]/r2pa2cth2 + (d2hS_dr2[1]*(a2 - 2*M*r + r2))/r2pa2cth2 - (2*dhS_dth[2]*r)/r2pa2cth22 + (2*a*dhS_dr[6]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph[4]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (4*a*dhS_dph[0]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2[1]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (4*dhS_dr[1]*(-(a4*M) - a4*cos4th*(M - r) + a4*r - 6*a2*M*r2 - 2*a2*cos2th*(a2*(M - r) + (M - 3*r)*r2) + 2*a2*r3 - 4*M*r4 + 4*r5))/a2p2r2pa2c2th3 - (8*a*hS[6]*M*(3*a4*M + a4*cos4th*(M - r) - 23*a4*r + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 6*r) + 5*(2*M - r)*r2) - 12*a2*r3 - 24*M*r4 + 16*r5))/(a2p2r2pa2c2th4*a2pr2m2Mr) - (4*dhS_dt[0]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) + (hS[1]*(10*a8 + a8*cos6th - 24*a6*M2 + 116*a6*M*r - 2*a6*cos6th*M*r + 22*a6*r2 + a6*cos6th*r2 - 248*a4*M2*r2 + 2*a4*cos4th*(3*a4 + 2*r2*(6*M2 - 4*M*r + r2) + a2*(-4*M2 - 10*M*r + 5*r2)) + 208*a4*M*r3 - 4*a4*r4 - 128*a2*M2*r4 + a2*cos2th*(15*a6 - 32*a2*M*(7*M - 8*r)*r2 + a4*(-32*M2 + 98*M*r + 31*r2) - 16*(20*M2 - 12*M*r + r2)*r4) + 128*a2*M*r5 - 48*a2*r6 - 64*M2*r6 + 64*M*r7 - 32*r8))/(a2p2r2pa2c2th4*a2pr2m2Mr) - (16*a2*dhS_dt[5]*M*r*sin2th)/a2p2r2pa2c2th3 - (32*a3*hS[8]*M*(a2 + a2*cos2th - 4*r2)*sin2th)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (2*a2*costh*dhS_dr[2]*sinth)/r2pa2cth22 + (d2hS_dph2[1]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (dhS_dph[3]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (32*a*dhS_dph[5]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth[6]*M*r)/(a2p2r2pa2c2th3*tanth) + (dhS_dth[1]*(5*a4 - a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 - 4*M*r) + 16*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (4*hS[2]*(4*a6*M + 3*a6*r - 34*a4*M*r2 + a4*cos4th*(a2*(-4*M + r) + (-6*M + r)*r2) + 11*a4*r3 + 4*a2*cos2th*r*(a4 + 3*a2*r*(2*M + r) + 2*(2*M + r)*r3) - 48*a2*M*r4 + 16*a2*r5 - 16*M*r6 + 8*r7))/(a2p2r2pa2c2th4*a2pr2m2Mr*tanth);\n  src[2] = (-8*a*d2hS_dtph[2]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt[5]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2[2]/r2pa2cth2 + (d2hS_dr2[2]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth[1]*r)/r2pa2cth22 + (2*a*dhS_dr[8]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph[5]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2[2]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*a3*dhS_dph[0]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt[3]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (16*a2*dhS_dt[7]*M*r*sin2th)/a2p2r2pa2c2th3 - (8*a2*dhS_dt[0]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr[1]*sin2th)/r2pa2cth22 + (4*a2*hS[1]*(-7*a4*M - a4*cos4th*(M - r) + 3*a4*r + 8*a2*M2*r - 4*a2*cos2th*(a2*(2*M - r) + r*(-2*M2 + 3*M*r - 2*r2)) + 28*a2*M*r2 + 8*a2*r3 - 80*M2*r3 + 32*M*r4 + 8*r5)*sin2th)/a2p2r2pa2c2th4 + (d2hS_dph2[2]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) + (8*a*hS[8]*M*r*(-a2 + 5*a2*cos4th + 4*cos2th*(a2 - r2) + 12*r2))/(a2p2r2pa2c2th4*sinth2) - (hS[2]*(15*a6 + 6*a6*cos6th + a6*cos8th + 96*a4*M*r + 16*a4*cos6th*M*r + 56*a4*r2 + 8*a4*cos6th*r2 - 160*a2*M*r3 + 8*a2*cos4th*(2*a4 + a2*r*(-12*M + 5*r) + 2*(-6*M + r)*r3) + 80*a2*r4 - 128*M*r5 + 2*cos2th*(13*a6 + a4*(-8*M*r + 44*r2) + 16*a2*(8*M + 3*r)*r3 + 64*M*r5) + 64*r6))/(4.*a2p2r2pa2c2th4*sinth2) - (8*a2*dhS_dr[2]*(-(a2*cos2th*(M - r)) + a2*(-M + r) + 2*(M + r)*r2)*sinth2)/a2p2r2pa2c2th3 - (32*a*dhS_dph[7]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth[8]*M*r)/(a2p2r2pa2c2th3*tanth) - (32*a*a2pr2m2Mr*hS[6]*M*(a2 + a2*cos2th - 10*r2))/(a2p2r2pa2c2th4*tanth) + (dhS_dth[2]*(5*a4 - a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 - 4*M*r) + 16*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (dhS_dph[3]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth);\n  src[3] = (-8*a*d2hS_dtph[3]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt[6]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2[3]/r2pa2cth2 + (d2hS_dr2[3]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a*dhS_dr[9]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph[6]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2[3]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*a*hS[7]*M*r*(-3*a4 + 10*a2*M*r + 17*a2*r2 + a2*cos4th*(11*a2 - 10*M*r + 11*r2) - 8*M*r3 + 4*cos2th*(2*a4 + a2*r2 + (2*M - r)*r3) + 20*r4))/a2p2r2pa2c2th5 + (2*dhS_dph[1]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (4*hS[3]*M*(2*a8*M - a8*cos6th*M + 30*a8*r + 3*a8*cos6th*r - 54*a6*M*r2 - 5*a6*cos6th*M*r2 + 90*a6*r3 + 3*a6*cos6th*r3 - 80*a4*M2*r3 - 2*a4*cos4th*(a4*(M - 9*r) + a2*(21*M - 19*r)*r2 - 2*(20*M2 - 12*M*r + 5*r2)*r3) - 96*a4*M*r4 + 76*a4*r5 + 64*a2*M2*r5 + a2*cos2th*(a6*(M + 45*r) + a4*(-91*M + 125*r)*r2 + 16*a2*(-11*M + 6*r)*r4 + 16*(-4*M2 - 5*M*r + r2)*r5) + 16*a2*M*r6 - 16*a2*r7 + 64*M*r8 - 32*r9))/(a2p2r2pa2c2th5*a2pr2m2Mr) - (16*a2*dhS_dt[8]*M*r*sin2th)/a2p2r2pa2c2th3 - (16*a*a2pr2m2Mr*hS[5]*M*(-3*a4 + a4*cos4th + 26*a2*r2 - 2*a2*cos2th*(a2 + 5*r2) + 20*r4)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2[3]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (a*hS[9]*M*(3*a6*M + a6*cos8th*(M - r) + 85*a6*r + 124*a6*cos2th*r + 4*a6*cos6th*r - 80*a4*M*r2 - 216*a4*cos2th*M*r2 - 40*a4*cos6th*M*r2 + 280*a4*r3 + 380*a4*cos2th*r3 + 4*a4*cos6th*r3 - 320*a2*M2*r3 - 4*a2*cos4th*(a4*(M - 11*r) + 2*a2*(22*M - 13*r)*r2 - 20*(4*M2 - 3*M*r + r2)*r3) - 208*a2*M*r4 - 576*a2*cos2th*M*r4 + 304*a2*r5 + 384*a2*cos2th*r5 + 256*M2*r5 - 256*cos2th*M2*r5 - 256*M*r6 - 256*cos2th*M*r6 + 64*r7 + 192*cos2th*r7))/(2.*a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (8*a*dhS_dr[0]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*a2pr2m2Mr*hS[4]*M*(3*a4*M + a4*cos4th*(M - r) - 39*a4*r + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 10*r) + (10*M - 9*r)*r2) - 12*a2*r3 - 24*M*r4 + 32*r5)*sinth2)/a2p2r2pa2c2th5 - (8*a*hS[0]*M2*(3*a8 - 22*a6*r2 - 20*a4*M*r3 + a4*cos4th*(a4 - 10*a2*r2 + 20*M*r3 - 11*r4) - 41*a4*r4 + 16*a2*M*r5 + 4*a2*cos2th*(a6 - 8*a4*r2 - 15*a2*r4 - 2*(2*M + 3*r)*r5) + 24*a2*r6 + 40*r8)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (32*a3*costh*dhS_dth[0]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt[2]*M*r*sinth3)/a2p2r2pa2c2th3 - (2*dhS_dth[3])/(a2p2r2pa2c2th*tanth) - (32*a*dhS_dph[8]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth[9]*M*r)/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph[2]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth);\n  src[4] = (-8*a*d2hS_dtph[4]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dr[4]*(3*a2*cos2th*(M - r) + a2*(3*M + r) - 2*(M + r)*r2))/a2p2r2pa2c2th2 + d2hS_dth2[4]/r2pa2cth2 + (d2hS_dr2[4]*(a2 - 2*M*r + r2))/r2pa2cth2 - (4*dhS_dth[5]*r)/r2pa2cth22 - (8*a*dhS_dph[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (8*a*dhS_dt[6]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2[4]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (2*hS[4]*(7*a6 + 3*a4*M2 + 4*a4*M*r + 10*a4*r2 - 40*a2*M2*r2 + a4*cos4th*(a2 + M2 - 4*M*r + 2*r2) + 4*a2*cos2th*(2*a4 + 2*M*(-5*M + 3*r)*r2 + a2*(M2 + r2)) + 40*a2*M*r3 - 16*a2*r4 - 56*M2*r4 + 64*M*r5 - 16*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr) - (8*dhS_dt[1]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*hS[3]*M*(3*a6*M - 39*a6*r + a4*cos4th*(M - r)*(a2 - r2) + 101*a4*M*r2 - 45*a4*r3 - 48*a2*M2*r3 + 4*a2*cos2th*(a4*(M - 10*r) + a2*(25*M - 17*r)*r2 + (-12*M2 + 18*M*r - 5*r2)*r3) - 16*a2*M*r4 + 36*a2*r5 + 96*M2*r5 - 136*M*r6 + 48*r7))/(a2p2r2pa2c2th3*a2pr2m2Mr3) - (2*hS[0]*M*(3*a8*M - 27*a8*r + 62*a6*M*r2 - 58*a6*r3 - 12*a4*M2*r3 + a4*cos4th*(a4*(M + 3*r) + 2*a2*(-7*M + 3*r)*r2 + (12*M2 - 11*M*r + 3*r2)*r3) + 39*a4*M*r4 - 19*a4*r5 + 48*a2*M2*r5 + 4*a2*cos2th*(a6*(M - 6*r) + a4*(12*M - 19*r)*r2 + a2*(29*M - 20*r)*r4 + (-12*M2 + 22*M*r - 7*r2)*r5) - 56*a2*M*r6 + 28*a2*r7 - 24*M*r8 + 16*r9))/(a2p2r2pa2c2th3*a2pr2m2Mr3) - (4*a2*costh*dhS_dr[5]*sinth)/r2pa2cth22 + (d2hS_dph2[4]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (2*dhS_dph[6]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) + (hS[9]*(-2*a6*M2 + a6*cos6th*M2 + 76*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 248*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 14*M*r + 3*r2) + 2*r2*(14*M2 - 12*M*r + 3*r2)) - 16*a4*M*r3 + 192*a2*M3*r3 - a2*cos2th*(a4*(M2 - 50*M*r - 15*r2) - 16*a2*r2*(-12*M2 + 2*M*r + 3*r2) - 16*r3*(12*M3 - 7*M2*r - 5*M*r2 + 3*r3)) + 36*a4*r4 + 240*a2*M2*r4 - 272*a2*M*r5 - 384*M3*r5 + 48*a2*r6 + 512*M2*r6 - 224*M*r7 + 32*r8))/(2.*a2p2r2pa2c2th3*a2pr2m2Mr3*sinth2) + (hS[7]*(3*a2*cos2th*M*r + r*(3*a2*M + 2*a2*r - 6*M*r2 + 2*r3) + 2*a4*costh2*sinth2))/(a2pr2m2Mr*r2pa2cth23) - (2*dhS_dth[4]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) + (8*hS[5]*(a2*(M - 3*r) - a2*cos2th*(M - r) + 2*(2*M - r)*r2))/(a2p2r2pa2c2th2*a2pr2m2Mr*tanth);\n  src[5] = (-8*a*d2hS_dtph[5]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dr[5]*(a2*cos2th*(M - r) + a2*(M + r) - 2*M*r2))/a2p2r2pa2c2th2 + d2hS_dth2[5]/r2pa2cth2 + (d2hS_dr2[5]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth[4]*r)/r2pa2cth22 - (2*dhS_dth[7]*r)/r2pa2cth22 - (4*a*dhS_dph[2]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt[8]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2[5]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dt[2]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (8*a3*dhS_dph[1]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt[6]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (12*a2*hS[4]*(-M + r)*sin2th)/a2p2r2pa2c2th2 - (8*a2*dhS_dt[1]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr[4]*sin2th)/r2pa2cth22 - (4*a2*hS[0]*M*(3*a6 - 4*a4*r*(M + 3*r) + a2*(32*M - 33*r)*r3 + a2*cos2th*(3*a4 + a2*(-4*M*r + 6*r2) + (-8*M + 3*r)*r3) + 2*(16*M - 9*r)*r5)*sin2th)/(a2p2r2pa2c2th3*a2pr2m2Mr2) - (2*a2*costh*dhS_dr[7]*sinth)/r2pa2cth22 + (d2hS_dph2[5]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS[5]*(a4 + 3*a4*cos4th + a4*cos6th + 8*a2*r2 - 32*M*r3 + cos2th*(3*a4 + 8*a2*r2 + 16*(2*M - r)*r3) + 24*r4))/(a2p2r2pa2c2th3*sinth2) - (dhS_dph[8]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (8*hS[7]*r)/(a2p2r2pa2c2th2*tanth) - (2*dhS_dth[5]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) + (8*a*hS[3]*M*(-3*a6 + 5*a4*M*r + a4*cos4th*(a2 - M*r) + 20*a4*r2 - 48*a2*M*r3 - 2*a2*cos2th*(a4 - 2*a2*(M - 4*r)*r + (-16*M + 9*r)*r3) + 38*a2*r4 - 24*M*r5 + 12*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr2*tanth) - (dhS_dph[6]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth) + (hS[9]*(-10*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 16*a4*M2*r + 92*a4*M*r2 + 36*a4*r3 - 256*a2*M2*r3 + 2*a4*cos4th*(5*a2*M + 3*a2*r - 8*M2*r - 6*M*r2 + 6*r3) + a2*cos2th*(a4*(M + 15*r) + 16*a2*(-13*M + 3*r)*r2 + 48*(8*M2 - 6*M*r + r2)*r3) + 32*a2*M*r4 + 48*a2*r5 + 128*M2*r5 - 128*M*r6 + 32*r7))/(2.*a2p2r2pa2c2th3*a2pr2m2Mr2*sinth2*tanth);\n  src[6] = (-8*a*d2hS_dtph[6]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2[6]/r2pa2cth2 + (d2hS_dr2[6]*(a2 - 2*M*r + r2))/r2pa2cth2 - (2*dhS_dth[8]*r)/r2pa2cth22 - (4*a*dhS_dph[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt[9]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2[6]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (2*dhS_dr[6]*(7*a4*M + a4*cos4th*(M - r) + a4*r - 4*a2*M*r2 + 4*a2*cos2th*(2*a2*M + (M - r)*r2) + 4*a2*r3 - 16*M*r4))/a2p2r2pa2c2th3 + (2*dhS_dph[4]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (4*dhS_dt[3]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (2*hS[6]*(-2*a6*M2 + a6*cos6th*M2 + 24*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 40*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 12*M*r + 3*r2) + 2*r2*(10*M2 - 9*M*r + 3*r2)) - 44*a4*M*r3 + 36*a4*r4 + 112*a2*M2*r4 - a2*cos2th*(a4*(M2 - 2*M*r - 15*r2) + 48*a2*(M - r)*r3 - 16*(M2 - 5*M*r + 3*r2)*r4) - 176*a2*M*r5 + 48*a2*r6 + 128*M2*r6 - 128*M*r7 + 32*r8))/(a2p2r2pa2c2th4*a2pr2m2Mr) + (4*a*hS[2]*M*(5*a6 - 21*a4*r2 + cos4th*(-a6 + a4*r2) - 56*a2*r4 + 4*a2*cos2th*(a4 + 3*a2*r2 + 4*r4) - 24*r6)*sin2th)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (2*a2*costh*dhS_dr[8]*sinth)/r2pa2cth22 + (d2hS_dph2[6]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (dhS_dph[9]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (8*a*dhS_dr[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt[4]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*hS[1]*M*(3*a6*M - 20*a6*r + 31*a4*M*r2 - 26*a4*r3 + a4*cos4th*(a2*M - 3*M*r2 + 2*r3) + 4*a2*cos2th*(a4*(M - 5*r) + a2*(7*M - 8*r)*r2 + (6*M - r)*r4) + 12*a2*r5 - 24*M*r6 + 24*r7)*sinth2)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (32*a3*costh*dhS_dth[1]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt[5]*M*r*sinth3)/a2p2r2pa2c2th3 - (dhS_dth[6]*(a4 + 3*a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 + 4*r*(-M + r)) + 8*r4))/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph[5]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (2*hS[8]*(-6*a6*M - a6*cos6th*M + 4*a6*r + a6*cos6th*r + 52*a4*M*r2 + 2*a4*cos4th*(a2*(3*M + 2*r) + (-2*M + 5*r)*r2) + 14*a4*r3 + 48*a2*M*r4 + a2*cos2th*(a4*(M + 7*r) + 8*a2*(-10*M + 3*r)*r2 + 16*(-7*M + 2*r)*r4) + 16*a2*r5 - 32*M*r6 + 16*r7))/(a2p2r2pa2c2th4*a2pr2m2Mr*tanth);\n  src[7] = (-8*a*d2hS_dtph[7]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dr[7]*(a2*cos2th*(M - r) + a2*(M + 3*r) + 2*(-3*M + r)*r2))/a2p2r2pa2c2th2 + d2hS_dth2[7]/r2pa2cth2 + (d2hS_dr2[7]*(a2 - 2*M*r + r2))/r2pa2cth2 + (4*a2pr2m2Mr*dhS_dth[5]*r)/r2pa2cth22 - (d2hS_dt2[7]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (8*a*hS[3]*M*r*(-15*a4 + a4*cos4th + 16*a2*M*r - 2*a2*cos2th*(7*a2 + r*(-8*M + 7*r)) - 2*a2*r2 - 16*M*r3 + 12*r4))/(a2p2r2pa2c2th3*a2pr2m2Mr) + (4*hS[0]*M*r*(-9*a6 + 4*a4*M*r - 7*a4*r2 + a4*cos4th*(3*a2 - 4*M*r + 3*r2) - 8*a2*M*r3 - 2*a2*cos2th*(3*a4 + 8*a2*r2 + (-4*M + 5*r)*r3) + 6*a2*r4 + 4*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr) - (16*a3*dhS_dph[2]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (16*a3*dhS_dt[8]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (16*a2*hS[5]*(-M + r)*sin2th)/a2p2r2pa2c2th2 - (16*a2*dhS_dt[2]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (2*a2*a2pr2m2Mr*dhS_dr[5]*sin2th)/r2pa2cth22 + (d2hS_dph2[7]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS[7]*(6*a4 + a4*cos6th + 8*a2*r2 + 2*a2*cos4th*(5*a2 + 4*r2) + 32*M*r3 + cos2th*(15*a4 + 48*a2*r2 + 16*(-2*M + r)*r3) + 16*r4))/(2.*a2p2r2pa2c2th3*sinth2) + (a2pr2m2Mr*hS[4]*(3*a2*cos2th*M*r + r*(3*a2*M + 2*a2*r - 6*M*r2 + 2*r3) + 2*a4*costh2*sinth2))/r2pa2cth23 - (2*dhS_dth[7]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) - (2*dhS_dph[8]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth) + (hS[9]*(35*a6 + 8*a6*cos6th + a6*cos8th - 168*a4*M*r - 36*a4*cos6th*M*r + 120*a4*r2 + 12*a4*cos6th*r2 + 128*a2*M2*r2 + 4*a2*cos4th*(7*a4 + 2*a2*r*(5*M + 9*r) - 4*(8*M2 + M*r - 3*r2)*r2) - 48*a2*M*r3 + 144*a2*r4 - 256*M2*r4 + 64*r6 + 4*cos2th*(14*a6 + a4*r*(-23*M + 45*r) - 16*a2*(7*M - 3*r)*r3 + 16*r4*pow(-2*M + r,2))))/(8.*a2p2r2pa2c2th3*a2pr2m2Mr*sinth4);\n  src[8] = (-8*a*d2hS_dtph[8]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2[8]/r2pa2cth2 + (d2hS_dr2[8]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth[6]*r)/r2pa2cth22 - (d2hS_dt2[8]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dr[8]*(a4*(M + r) + 3*a2*(-M + r)*r2 + a2*cos2th*(a2*(M + r) + (-M + r)*r2) + 2*(-3*M + r)*r4))/a2p2r2pa2c2th3 + (2*dhS_dph[5]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (8*a3*dhS_dph[3]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt[9]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a2*dhS_dt[3]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr[6]*sin2th)/r2pa2cth22 + (d2hS_dph2[8]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS[8]*(55*a6 + 10*a6*cos6th + a6*cos8th - 80*a4*M*r - 24*a4*cos6th*M*r + 184*a4*r2 + 16*a4*cos6th*r2 + 224*a2*M*r3 + 8*a2*cos4th*(5*a4 + a2*r*(10*M + 13*r) + 2*(2*M + 5*r)*r3) + 208*a2*r4 + 256*M*r5 + 2*cos2th*(43*a6 + 4*a4*r*(3*M + 34*r) - 16*a2*(8*M - 9*r)*r3 + 64*(-2*M + r)*r5) + 64*r6))/(4.*a2p2r2pa2c2th4*sinth2) - (8*a*dhS_dr[2]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt[5]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (32*a*hS[2]*M*r*(4*a4 - a2*r2 + a2*cos2th*(4*a2 + 3*r2) - 6*r4)*sinth2)/a2p2r2pa2c2th4 - (32*a3*costh*dhS_dth[2]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt[7]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a2pr2m2Mr*a3*costh*hS[1]*M*(a2 + a2*cos2th - 10*r2)*sinth3)/a2p2r2pa2c2th4 - (dhS_dth[8]*(a4 + 3*a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 + 4*r*(-M + r)) + 8*r4))/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph[7]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (2*hS[6]*(-2*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 8*a4*M2*r + 60*a4*M*r2 + 2*a4*cos4th*(a2*(M + 3*r) + 2*r*(-2*M2 - 3*M*r + 3*r2)) + 36*a4*r3 - 160*a2*M2*r3 + a2*cos2th*(a4*(M + 15*r) - 16*a2*(7*M - 3*r)*r2 + 16*(10*M2 - 10*M*r + 3*r2)*r3) + 32*a2*M*r4 + 48*a2*r5 - 64*M*r6 + 32*r7))/(a2p2r2pa2c2th4*tanth) - (dhS_dph[9]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth);\n  src[9] = (-8*a*d2hS_dtph[9]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2[9]/r2pa2cth2 + (d2hS_dr2[9]*(a2 - 2*M*r + r2))/r2pa2cth2 - (d2hS_dt2[9]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (2*dhS_dr[9]*(5*a4*M - a4*cos4th*(M - r) + 3*a4*r - 16*a2*M*r2 + 8*a2*r3 + 4*a2*cos2th*(a2*(M + r) + 2*r3) - 24*M*r4 + 8*r5))/a2p2r2pa2c2th3 + (4*dhS_dph[6]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 + (hS[7]*(35*a8 + 8*a8*cos6th + a8*cos8th - 78*a6*M*r - 36*a6*cos6th*M*r - 2*a6*cos8th*M*r + 155*a6*r2 + 20*a6*cos6th*r2 + a6*cos8th*r2 + 80*a4*M2*r2 + 40*a4*cos6th*M2*r2 + 88*a4*M*r3 - 44*a4*cos6th*M*r3 + 264*a4*r4 + 12*a4*cos6th*r4 - 96*a2*M2*r4 + 4*a2*cos4th*(7*a6 + 5*a4*r*(4*M + 5*r) + 4*(-2*M2 + M*r + 3*r2)*r4 + a2*(-20*M2*r2 + 26*M*r3 + 30*r4)) + 304*a2*M*r5 + 208*a2*r6 + 128*M*r7 + 4*cos2th*(14*a8 + a6*r*(9*M + 59*r) + a4*r2*(-10*M2 - 37*M*r + 93*r2) + 16*a2*(2*M2 - 5*M*r + 4*r2)*r4 + 16*(-2*M + r)*r7) + 64*r8))/(2.*a2p2r2pa2c2th5) + (2*a2pr2m2Mr*hS[5]*(-10*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 140*a4*M*r2 + 2*a4*cos4th*(a2*(5*M + 3*r) + 2*(M + 3*r)*r2) + 36*a4*r3 + 160*a2*M*r4 + a2*cos2th*(a4*(M + 15*r) - 48*a2*(3*M - r)*r2 + 16*(-10*M + 3*r)*r4) + 48*a2*r5 + 32*r7)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2[9]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) + (hS[9]*(126*a10 + a10*cos10th + 45*a10*cos6th + 10*a10*cos8th + 6*a8*M2 - a8*cos10th*M2 + 3*a8*cos6th*M2 + 2*a8*cos8th*M2 - 160*a8*M*r - 160*a8*cos6th*M*r - 32*a8*cos8th*M*r + 512*r10 + 700*a8*r2 + 160*a8*cos6th*r2 + 20*a8*cos8th*r2 - 120*a6*M2*r2 + 160*a6*cos6th*M2*r2 + 24*a6*cos8th*M2*r2 - 840*a6*M*r3 - 384*a6*cos6th*M*r3 - 24*a6*cos8th*M*r3 - 640*a4*M3*r3 - 320*a4*cos6th*M3*r3 + 1600*a6*r4 + 160*a6*cos6th*r4 - 96*a4*M2*r4 + 240*a4*cos6th*M2*r4 - 1856*a4*M*r5 - 160*a4*cos6th*M*r5 + 768*a2*M3*r5 + 8*a2*cos4th*(15*a8 - a6*(M2 + 40*M*r - 70*r2) + 4*a4*r2*(3*M2 - 37*M*r + 30*r2) + 4*a2*r3*(20*M3 + 11*M2*r - 38*M*r2 + 20*r3) + 16*M*(2*M2 + 2*M*r - r2)*r5) + 1920*a4*r6 + 768*a2*M2*r6 - 2432*a2*M*r7 + 1280*a2*r8 + 1024*M2*r8 + 2*cos2th*(105*a10 - a8*(M2 + 176*M*r - 560*r2) + 16*a6*r2*(-5*M2 - 52*M*r + 75*r2) + 8*a4*r3*(20*M3 - 31*M2*r - 182*M*r2 + 160*r3) - 128*a2*(4*M3 + 4*M2*r + 6*M*r2 - 5*r3)*r5 + 256*M*(-2*M + r)*r8) - 1536*M*r9))/(8.*a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (16*a*dhS_dr[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (16*a*dhS_dt[6]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (2*a2pr2m2Mr*hS[4]*(-2*a6*M2 + a6*cos6th*M2 + 76*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 40*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 14*M*r + 3*r2) + 2*r2*(10*M2 - 8*M*r + 3*r2)) + 32*a4*M*r3 + 36*a4*r4 + 48*a2*M2*r4 - a2*cos2th*(a4*(M2 - 50*M*r - 15*r2) - 48*a2*(2*M + r)*r3 - 48*(-M2 + M*r + r2)*r4) - 144*a2*M*r5 + 48*a2*r6 - 96*M*r7 + 32*r8)*sinth2)/a2p2r2pa2c2th5 + (4*a*hS[3]*M*(2*a8*M - a8*cos6th*M + 30*a8*r + 3*a8*cos6th*r - 130*a6*M*r2 - 3*a6*cos6th*M*r2 + 70*a6*r3 + a6*cos6th*r3 - 80*a4*M2*r3 - 2*a4*cos4th*(a4*(M - 9*r) + a2*(31*M - 13*r)*r2 + 2*(-20*M2 + 14*M*r + r2)*r3) - 184*a4*M*r4 + 4*a4*r5 + 64*a2*M2*r5 + a2*cos2th*(a6*(M + 45*r) + a4*(-189*M + 95*r)*r2 - 272*a2*M*r4 - 16*(4*M2 + 3*M*r + 5*r2)*r5) + 176*a2*M*r6 - 112*a2*r7 + 256*M*r8 - 96*r9)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (2*hS[0]*M*(-2*a10*M + a10*cos6th*M + 30*a10*r + 3*a10*cos6th*r - 32*r11 + 28*a8*M*r2 - 18*a8*cos6th*M*r2 + 120*a8*r3 + 6*a8*cos6th*r3 + 40*a6*M2*r3 + 20*a6*cos6th*M2*r3 + 46*a6*M*r4 - 15*a6*cos6th*M*r4 + 166*a6*r5 + 3*a6*cos6th*r5 - 48*a4*M2*r5 + 2*a4*cos4th*(a6*(M + 9*r) - 14*a4*(M - 2*r)*r2 + a2*(-20*M2 - 31*M*r + 29*r2)*r3 + 2*(-4*M2 - 2*M*r + 5*r2)*r5) - 152*a4*M*r6 + 60*a4*r7 - 208*a2*M*r8 + a2*cos2th*(-(a8*(M - 45*r)) + 2*a6*(9*M + 85*r)*r2 + a4*(-20*M2 + 31*M*r + 221*r2)*r3 + 16*a2*(4*M2 + 10*M*r + 7*r2)*r5 + 16*(13*M + r)*r8) - 48*a2*r9)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (64*a3*costh*dhS_dth[3]*M*r*sinth3)/a2p2r2pa2c2th3 + (64*a3*costh*dhS_dt[8]*M*r*sinth3)/a2p2r2pa2c2th3 + (4*dhS_dph[8]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (dhS_dth[9]*(9*a4 + 3*a4*cos4th + 32*a2*M*r + 24*a2*r2 + 4*a2*cos2th*(3*a2 - 8*M*r + 6*r2) + 24*r4))/(a2p2r2pa2c2th3*tanth);\n}\n\n\n/* Compute the 2D singular field, its derivatives and its d'Alembertian */\nvoid effsource_calc_m(int m, struct coordinate * x, double *hS_re, double *hS_im,\n   double *dhS_dr_re, double *dhS_dr_im, double *dhS_dth_re, double *dhS_dth_im,\n   double *dhS_dph_re, double *dhS_dph_im, double *dhS_dt_re, double *dhS_dt_im,\n   double *src_re, double *src_im)\n{\n  double A_re[10][5], A_im[10][5];\n  double dA_dr_re[10][5], dA_dr_im[10][5], dA_dth_re[10][5], dA_dth_im[10][5];\n  double d2A_dr2_re[10][5], d2A_dr2_im[10][5];\n  double d2A_dth2_re[10][5], d2A_dth2_im[10][5];\n\n  double d2hS_dt2_re[10], d2hS_dt2_im[10], d2hS_dtr_re[10], d2hS_dtr_im[10];\n  double d2hS_dtth_re[10], d2hS_dtth_im[10], d2hS_dtph_re[10], d2hS_dtph_im[10];\n  double d2hS_dr2_re[10], d2hS_dr2_im[10], d2hS_drth_re[10], d2hS_drth_im[10];\n  double d2hS_drph_re[10], d2hS_drph_im[10], d2hS_dth2_re[10], d2hS_dth2_im[10];\n  double d2hS_dthph_re[10], d2hS_dthph_im[10], d2hS_dph2_re[10], d2hS_dph2_im[10];\n\n  if(m>20)\n  {\n    printf(\"Support for computing mode %d has not yet been added.\\n\", m);\n    return;\n  }\n\n  const double r      = x->r;\n  const double theta  = x->theta;\n  const double rp     = xp.r;\n  const double thetap = xp.theta;\n\n  double Omega  = M / (a*M + sqrt(M*rp*rp*rp));\n\n  const double dr  = r - rp;\n  const double dth = theta - thetap;\n\n  const double dr2 = dr*dr;\n\n  const double dth2  = dth*dth;\n  const double dth3  = dth*dth2;\n  const double dth4  = dth2*dth2;\n  const double dth5  = dth3*dth2;\n  const double dth6  = dth3*dth3;\n  const double dth7  = dth4*dth3;\n  const double dth8  = dth4*dth4;\n  const double dth9  = dth5*dth4;\n\n  /* A */\n  A_re[0][0] = (A00600 + A00800*dth2)*dth6 + dr*(dr*((A02400 + A02600*dth2)*dth4 + dr*(dr*(dth2*(A04200 + A04400*dth2) + dr*(dth2*(A05200 + A05400*dth2) + dr*(A06000 + A06200*dth2 + dr*(A07000 + dr*(A08000 + A09000*dr) + A07200*dth2)))) + (A03400 + A03600*dth2)*dth4)) + (A01600 + A01800*dth2)*dth6);\n  A_re[0][1] = (A00420 + A00620*dth2)*dth4 + dr*(dr*(dth2*(A02220 + A02420*dth2) + dr*(dth2*(A03220 + A03420*dth2) + dr*(A04020 + A04220*dth2 + dr*(A05020 + dr*(A06020 + A07020*dr) + A05220*dth2)))) + (A01420 + A01620*dth2)*dth4);\n  A_re[0][2] = dth2*(A00240 + A00440*dth2) + dr*(dth2*(A01240 + A01440*dth2) + dr*(A02040 + A02240*dth2 + dr*(A03040 + dr*(A04040 + A05040*dr) + A03240*dth2)));\n  A_re[0][3] = A00060 + A00260*dth2 + dr*(A01060 + dr*(A02060 + A03060*dr) + A01260*dth2);\n  A_re[0][4] = A00080 + A01080*dr;\n  A_im[0][0] = A_im[0][1] = A_im[0][2] = A_im[0][3] = A_im[0][4] = 0.0;\n\n  A_re[1][0] = A_re[1][1] = A_re[1][2] = A_re[1][3] = A_re[1][4] = 0.0;\n  A_im[1][0] = (A10601 + A10801*dth2)*dth6 + dr*(dr*((A12401 + A12601*dth2)*dth4 + dr*(dr*(dth2*(A14201 + A14401*dth2) + dr*(A15201*dth2 + dr*(A16001 + dr*(A17001 + A18001*dr) + A16201*dth2))) + A13401*dth4)) + A11601*dth6);\n  A_im[1][1] = (A10421 + A10621*dth2)*dth4 + dr*(dr*(dth2*(A12221 + A12421*dth2) + dr*(A13221*dth2 + dr*(A14021 + dr*(A15021 + A16021*dr) + A14221*dth2))) + A11421*dth4);\n  A_im[1][2] = dth2*(A10241 + A10441*dth2) + dr*(A11241*dth2 + dr*(A12041 + dr*(A13041 + A14041*dr) + A12241*dth2));\n  A_im[1][3] = A10061 + dr*(A11061 + A12061*dr) + A10261*dth2;\n  A_im[1][4] = A10081;\n\n  A_re[2][0] = A_re[2][1] = A_re[2][2] = A_re[2][3] = A_re[2][4] = 0.0;\n  A_im[2][0] = A20701*dth7 + dr*(dr*(A22501*dth5 + dr*(dr*(A24301*dth3 + dr*(dr*(A26101*dth + A27101*dr*dth) + A25301*dth3)) + A23501*dth5)) + A21701*dth7);\n  A_im[2][1] = A20521*dth5 + dr*(dr*(A22321*dth3 + dr*(dr*(A24121*dth + A25121*dr*dth) + A23321*dth3)) + A21521*dth5);\n  A_im[2][2] = A20341*dth3 + dr*(dr*(A22141*dth + A23141*dr*dth) + A21341*dth3);\n  A_im[2][3] = A20161*dth + A21161*dr*dth;\n  A_im[2][4] = 0.0;\n\n  A_re[3][0] = (A30600 + A30800*dth2)*dth6 + dr*(dr*((A32400 + A32600*dth2)*dth4 + dr*(dr*(dth2*(A34200 + A34400*dth2) + dr*(dth2*(A35200 + A35400*dth2) + dr*(A36000 + A36200*dth2 + dr*(A37000 + dr*(A38000 + A39000*dr) + A37200*dth2)))) + (A33400 + A33600*dth2)*dth4)) + (A31600 + A31800*dth2)*dth6);\n  A_re[3][1] = (A30420 + A30620*dth2)*dth4 + dr*(dr*(dth2*(A32220 + A32420*dth2) + dr*(dth2*(A33220 + A33420*dth2) + dr*(A34020 + A34220*dth2 + dr*(A35020 + dr*(A36020 + A37020*dr) + A35220*dth2)))) + (A31420 + A31620*dth2)*dth4);\n  A_re[3][2] = dth2*(A30240 + A30440*dth2) + dr*(dth2*(A31240 + A31440*dth2) + dr*(A32040 + A32240*dth2 + dr*(A33040 + dr*(A34040 + A35040*dr) + A33240*dth2)));\n  A_re[3][3] = A30060 + A30260*dth2 + dr*(A31060 + dr*(A32060 + A33060*dr) + A31260*dth2);\n  A_re[3][4] = A30080 + A31080*dr;\n  A_im[3][0] = A_im[3][1] = A_im[3][2] = A_im[3][3] = A_im[3][4] = 0.0;\n\n  A_re[4][0] = A40800*dth8 + dr*(dr*(A42600*dth6 + dr*(dr*(A44400*dth4 + dr*(dr*(A46200*dth2 + dr*(dr*(A48000 + A49000*dr) + A47200*dth2)) + A45400*dth4)) + A43600*dth6)) + A41800*dth8);\n  A_re[4][1] = A40620*dth6 + dr*(dr*(A42420*dth4 + dr*(dr*(A44220*dth2 + dr*(dr*(A46020 + A47020*dr) + A45220*dth2)) + A43420*dth4)) + A41620*dth6);\n  A_re[4][2] = A40440*dth4 + dr*(dr*(A42240*dth2 + dr*(dr*(A44040 + A45040*dr) + A43240*dth2)) + A41440*dth4);\n  A_re[4][3] = A40260*dth2 + dr*(dr*(A42060 + A43060*dr) + A41260*dth2);\n  A_re[4][4] = A40080 + A41080*dr;\n  A_im[4][0] = A_im[4][1] = A_im[4][2] = A_im[4][3] = A_im[4][4] = 0.0;\n\n  A_re[5][0] = dr2*(dr2*(dr2*(A58100*dr2*dth + A56300*dth3) + A54500*dth5) + A52700*dth7) + A50900*dth9;\n  A_re[5][1] = dr2*(dr2*(A56120*dr2*dth + A54320*dth3) + A52520*dth5) + A50720*dth7;\n  A_re[5][2] = dr2*(A54140*dr2*dth + A52340*dth3) + A50540*dth5;\n  A_re[5][3] = A52160*dr2*dth + A50360*dth3;\n  A_re[5][4] = A50180*dth;\n  A_im[5][0] = A_im[5][1] = A_im[5][2] = A_im[5][3] = A_im[5][4] = 0.0;\n\n  A_re[6][0] = A_re[6][1] = A_re[6][2] = A_re[6][3] = A_re[6][4] = 0.0;\n  A_im[6][0] = (A60601 + A60801*dth2)*dth6 + dr*(dr*((A62401 + A62601*dth2)*dth4 + dr*(dr*(dth2*(A64201 + A64401*dth2) + dr*(A65201*dth2 + dr*(A66001 + dr*(A67001 + A68001*dr) + A66201*dth2))) + A63401*dth4)) + A61601*dth6);\n  A_im[6][1] = (A60421 + A60621*dth2)*dth4 + dr*(dr*(dth2*(A62221 + A62421*dth2) + dr*(A63221*dth2 + dr*(A64021 + dr*(A65021 + A66021*dr) + A64221*dth2))) + A61421*dth4);\n  A_im[6][2] = dth2*(A60241 + A60441*dth2) + dr*(A61241*dth2 + dr*(A62041 + dr*(A63041 + A64041*dr) + A62241*dth2));\n  A_im[6][3] = A60061 + dr*(A61061 + A62061*dr) + A60261*dth2;\n  A_im[6][4] = A60081;\n\n  A_re[7][0] = A70800*dth8 + dr*(dr*(A72600*dth6 + dr*(dr*(A74400*dth4 + dr*(dr*(A76200*dth2 + dr*(dr*(A78000 + A79000*dr) + A77200*dth2)) + A75400*dth4)) + A73600*dth6)) + A71800*dth8);\n  A_re[7][1] = A70620*dth6 + dr*(dr*(A72420*dth4 + dr*(dr*(A74220*dth2 + dr*(dr*(A76020 + A77020*dr) + A75220*dth2)) + A73420*dth4)) + A71620*dth6);\n  A_re[7][2] = A70440*dth4 + dr*(dr*(A72240*dth2 + dr*(dr*(A74040 + A75040*dr) + A73240*dth2)) + A71440*dth4);\n  A_re[7][3] = A70260*dth2 + dr*(dr*(A72060 + A73060*dr) + A71260*dth2);\n  A_re[7][4] = A70080 + A71080*dr;\n  A_im[7][0] = A_im[7][1] = A_im[7][2] = A_im[7][3] = A_im[7][4] = 0.0;\n\n  A_re[8][0] = A_re[8][1] = A_re[8][2] = A_re[8][3] = A_re[8][4] = 0.0;\n  A_im[8][0] = A80701*dth7 + dr*(dr*(A82501*dth5 + dr*(dr*(A84301*dth3 + dr*(dr*(A86101*dth + A87101*dr*dth) + A85301*dth3)) + A83501*dth5)) + A81701*dth7);\n  A_im[8][1] = A80521*dth5 + dr*(dr*(A82321*dth3 + dr*(dr*(A84121*dth + A85121*dr*dth) + A83321*dth3)) + A81521*dth5);\n  A_im[8][2] = A80341*dth3 + dr*(dr*(A82141*dth + A83141*dr*dth) + A81341*dth3);\n  A_im[8][3] = A80161*dth + A81161*dr*dth;\n  A_im[8][4] = 0.0;\n\n  A_re[9][0] = (A90600 + A90800*dth2)*dth6 + dr*(dr*((A92400 + A92600*dth2)*dth4 + dr*(dr*(dth2*(A94200 + A94400*dth2) + dr*(dth2*(A95200 + A95400*dth2) + dr*(A96000 + A96200*dth2 + dr*(A97000 + dr*(A98000 + A99000*dr) + A97200*dth2)))) + (A93400 + A93600*dth2)*dth4)) + (A91600 + A91800*dth2)*dth6);\n  A_re[9][1] = (A90420 + A90620*dth2)*dth4 + dr*(dr*(dth2*(A92220 + A92420*dth2) + dr*(dth2*(A93220 + A93420*dth2) + dr*(A94020 + A94220*dth2 + dr*(A95020 + dr*(A96020 + A97020*dr) + A95220*dth2)))) + (A91420 + A91620*dth2)*dth4);\n  A_re[9][2] = dth2*(A90240 + A90440*dth2) + dr*(dth2*(A91240 + A91440*dth2) + dr*(A92040 + A92240*dth2 + dr*(A93040 + dr*(A94040 + A95040*dr) + A93240*dth2)));\n  A_re[9][3] = A90060 + A90260*dth2 + dr*(A91060 + dr*(A92060 + A93060*dr) + A91260*dth2);\n  A_re[9][4] = A90080 + A91080*dr;\n  A_im[9][0] = A_im[9][1] = A_im[9][2] = A_im[9][3] = A_im[9][4] = 0.0;\n\n\n  /* dA/dr */\n  dA_dr_re[0][0] = dr*((2*A02400 + 2*A02600*dth2)*dth4 + dr*(dr*(dth2*(4*A04200 + 4*A04400*dth2) + dr*(dth2*(5*A05200 + 5*A05400*dth2) + dr*(6*A06000 + 6*A06200*dth2 + dr*(7*A07000 + dr*(8*A08000 + 9*A09000*dr) + 7*A07200*dth2)))) + (3*A03400 + 3*A03600*dth2)*dth4)) + (A01600 + A01800*dth2)*dth6;\n  dA_dr_re[0][1] = dr*(dth2*(2*A02220 + 2*A02420*dth2) + dr*(dth2*(3*A03220 + 3*A03420*dth2) + dr*(4*A04020 + 4*A04220*dth2 + dr*(5*A05020 + dr*(6*A06020 + 7*A07020*dr) + 5*A05220*dth2)))) + (A01420 + A01620*dth2)*dth4;\n  dA_dr_re[0][2] = dth2*(A01240 + A01440*dth2) + dr*(2*A02040 + 2*A02240*dth2 + dr*(3*A03040 + dr*(4*A04040 + 5*A05040*dr) + 3*A03240*dth2));\n  dA_dr_re[0][3] = A01060 + dr*(2*A02060 + 3*A03060*dr) + A01260*dth2;\n  dA_dr_re[0][4] = A01080;\n  dA_dr_im[0][0] = dA_dr_im[0][1] = dA_dr_im[0][2] = dA_dr_im[0][3] = dA_dr_im[0][4] = 0.0;\n\n  dA_dr_re[1][0] = dA_dr_re[1][1] = dA_dr_re[1][2] = dA_dr_re[1][3] = dA_dr_re[1][4] = 0.0;\n  dA_dr_im[1][0] = dr*((2*A12401 + 2*A12601*dth2)*dth4 + dr*(dr*(dth2*(4*A14201 + 4*A14401*dth2) + dr*(5*A15201*dth2 + dr*(6*A16001 + dr*(7*A17001 + 8*A18001*dr) + 6*A16201*dth2))) + 3*A13401*dth4)) + A11601*dth6;\n  dA_dr_im[1][1] = dr*(dth2*(2*A12221 + 2*A12421*dth2) + dr*(3*A13221*dth2 + dr*(4*A14021 + dr*(5*A15021 + 6*A16021*dr) + 4*A14221*dth2))) + A11421*dth4;\n  dA_dr_im[1][2] = A11241*dth2 + dr*(2*A12041 + dr*(3*A13041 + 4*A14041*dr) + 2*A12241*dth2);\n  dA_dr_im[1][3] = A11061 + 2*A12061*dr;\n  dA_dr_im[1][4] = 0.0;\n\n  dA_dr_re[2][0] = dA_dr_re[2][1] = dA_dr_re[2][2] = dA_dr_re[2][3] = dA_dr_re[2][4] = 0.0;\n  dA_dr_im[2][0] = dr*(2*A22501*dth5 + dr*(dr*(4*A24301*dth3 + dr*(dr*(6*A26101*dth + 7*A27101*dr*dth) + 5*A25301*dth3)) + 3*A23501*dth5)) + A21701*dth7;\n  dA_dr_im[2][1] = dr*(2*A22321*dth3 + dr*(dr*(4*A24121*dth + 5*A25121*dr*dth) + 3*A23321*dth3)) + A21521*dth5;\n  dA_dr_im[2][2] = dr*(2*A22141*dth + 3*A23141*dr*dth) + A21341*dth3;\n  dA_dr_im[2][3] = A21161*dth;\n  dA_dr_im[2][4] = 0.0;\n\n  dA_dr_re[3][0] = dr*((2*A32400 + 2*A32600*dth2)*dth4 + dr*(dr*(dth2*(4*A34200 + 4*A34400*dth2) + dr*(dth2*(5*A35200 + 5*A35400*dth2) + dr*(6*A36000 + 6*A36200*dth2 + dr*(7*A37000 + dr*(8*A38000 + 9*A39000*dr) + 7*A37200*dth2)))) + (3*A33400 + 3*A33600*dth2)*dth4)) + (A31600 + A31800*dth2)*dth6;\n  dA_dr_re[3][1] = dr*(dth2*(2*A32220 + 2*A32420*dth2) + dr*(dth2*(3*A33220 + 3*A33420*dth2) + dr*(4*A34020 + 4*A34220*dth2 + dr*(5*A35020 + dr*(6*A36020 + 7*A37020*dr) + 5*A35220*dth2)))) + (A31420 + A31620*dth2)*dth4;\n  dA_dr_re[3][2] = dth2*(A31240 + A31440*dth2) + dr*(2*A32040 + 2*A32240*dth2 + dr*(3*A33040 + dr*(4*A34040 + 5*A35040*dr) + 3*A33240*dth2));\n  dA_dr_re[3][3] = A31060 + dr*(2*A32060 + 3*A33060*dr) + A31260*dth2;\n  dA_dr_re[3][4] = A31080;\n  dA_dr_im[3][0] = dA_dr_im[3][1] = dA_dr_im[3][2] = dA_dr_im[3][3] = dA_dr_im[3][4] = 0.0;\n\n  dA_dr_re[4][0] = dr*(2*A42600*dth6 + dr*(dr*(4*A44400*dth4 + dr*(dr*(6*A46200*dth2 + dr*(dr*(8*A48000 + 9*A49000*dr) + 7*A47200*dth2)) + 5*A45400*dth4)) + 3*A43600*dth6)) + A41800*dth8;\n  dA_dr_re[4][1] = dr*(2*A42420*dth4 + dr*(dr*(4*A44220*dth2 + dr*(dr*(6*A46020 + 7*A47020*dr) + 5*A45220*dth2)) + 3*A43420*dth4)) + A41620*dth6;\n  dA_dr_re[4][2] = dr*(2*A42240*dth2 + dr*(dr*(4*A44040 + 5*A45040*dr) + 3*A43240*dth2)) + A41440*dth4;\n  dA_dr_re[4][3] = dr*(2*A42060 + 3*A43060*dr) + A41260*dth2;\n  dA_dr_re[4][4] = A41080;\n  dA_dr_im[4][0] = dA_dr_im[4][1] = dA_dr_im[4][2] = dA_dr_im[4][3] = dA_dr_im[4][4] = 0.0;\n\n  dA_dr_re[5][0] = dr*(dr2*(dr2*(8*A58100*dr2*dth + 6*A56300*dth3) + 4*A54500*dth5) + 2*A52700*dth7);\n  dA_dr_re[5][1] = dr*(dr2*(6*A56120*dr2*dth + 4*A54320*dth3) + 2*A52520*dth5);\n  dA_dr_re[5][2] = dr*(4*A54140*dr2*dth + 2*A52340*dth3);\n  dA_dr_re[5][3] = 2*A52160*dr*dth;\n  dA_dr_re[5][4] = 0.0;\n  dA_dr_im[5][0] = dA_dr_im[5][1] = dA_dr_im[5][2] = dA_dr_im[5][3] = dA_dr_im[5][4] = 0.0;\n\n  dA_dr_re[6][0] = dA_dr_re[6][1] = dA_dr_re[6][2] = dA_dr_re[6][3] = dA_dr_re[6][4] = 0.0;\n  dA_dr_im[6][0] = dr*((2*A62401 + 2*A62601*dth2)*dth4 + dr*(dr*(dth2*(4*A64201 + 4*A64401*dth2) + dr*(5*A65201*dth2 + dr*(6*A66001 + dr*(7*A67001 + 8*A68001*dr) + 6*A66201*dth2))) + 3*A63401*dth4)) + A61601*dth6;\n  dA_dr_im[6][1] = dr*(dth2*(2*A62221 + 2*A62421*dth2) + dr*(3*A63221*dth2 + dr*(4*A64021 + dr*(5*A65021 + 6*A66021*dr) + 4*A64221*dth2))) + A61421*dth4;\n  dA_dr_im[6][2] = A61241*dth2 + dr*(2*A62041 + dr*(3*A63041 + 4*A64041*dr) + 2*A62241*dth2);\n  dA_dr_im[6][3] = A61061 + 2*A62061*dr;\n  dA_dr_im[6][4] = 0.0;\n\n  dA_dr_re[7][0] = dr*(2*A72600*dth6 + dr*(dr*(4*A74400*dth4 + dr*(dr*(6*A76200*dth2 + dr*(dr*(8*A78000 + 9*A79000*dr) + 7*A77200*dth2)) + 5*A75400*dth4)) + 3*A73600*dth6)) + A71800*dth8;\n  dA_dr_re[7][1] = dr*(2*A72420*dth4 + dr*(dr*(4*A74220*dth2 + dr*(dr*(6*A76020 + 7*A77020*dr) + 5*A75220*dth2)) + 3*A73420*dth4)) + A71620*dth6;\n  dA_dr_re[7][2] = dr*(2*A72240*dth2 + dr*(dr*(4*A74040 + 5*A75040*dr) + 3*A73240*dth2)) + A71440*dth4;\n  dA_dr_re[7][3] = dr*(2*A72060 + 3*A73060*dr) + A71260*dth2;\n  dA_dr_re[7][4] = A71080;\n  dA_dr_im[7][0] = dA_dr_im[7][1] = dA_dr_im[7][2] = dA_dr_im[7][3] = dA_dr_im[7][4] = 0.0;\n\n  dA_dr_re[8][0] = dA_dr_re[8][1] = dA_dr_re[8][2] = dA_dr_re[8][3] = dA_dr_re[8][4] = 0.0;\n  dA_dr_im[8][0] = dr*(2*A82501*dth5 + dr*(dr*(4*A84301*dth3 + dr*(dr*(6*A86101*dth + 7*A87101*dr*dth) + 5*A85301*dth3)) + 3*A83501*dth5)) + A81701*dth7;\n  dA_dr_im[8][1] = dr*(2*A82321*dth3 + dr*(dr*(4*A84121*dth + 5*A85121*dr*dth) + 3*A83321*dth3)) + A81521*dth5;\n  dA_dr_im[8][2] = dr*(2*A82141*dth + 3*A83141*dr*dth) + A81341*dth3;\n  dA_dr_im[8][3] = A81161*dth;\n  dA_dr_im[8][4] = 0.0;\n\n  dA_dr_re[9][0] = dr*((2*A92400 + 2*A92600*dth2)*dth4 + dr*(dr*(dth2*(4*A94200 + 4*A94400*dth2) + dr*(dth2*(5*A95200 + 5*A95400*dth2) + dr*(6*A96000 + 6*A96200*dth2 + dr*(7*A97000 + dr*(8*A98000 + 9*A99000*dr) + 7*A97200*dth2)))) + (3*A93400 + 3*A93600*dth2)*dth4)) + (A91600 + A91800*dth2)*dth6;\n  dA_dr_re[9][1] = dr*(dth2*(2*A92220 + 2*A92420*dth2) + dr*(dth2*(3*A93220 + 3*A93420*dth2) + dr*(4*A94020 + 4*A94220*dth2 + dr*(5*A95020 + dr*(6*A96020 + 7*A97020*dr) + 5*A95220*dth2)))) + (A91420 + A91620*dth2)*dth4;\n  dA_dr_re[9][2] = dth2*(A91240 + A91440*dth2) + dr*(2*A92040 + 2*A92240*dth2 + dr*(3*A93040 + dr*(4*A94040 + 5*A95040*dr) + 3*A93240*dth2));\n  dA_dr_re[9][3] = A91060 + dr*(2*A92060 + 3*A93060*dr) + A91260*dth2;\n  dA_dr_re[9][4] = A91080;\n  dA_dr_im[9][0] = dA_dr_im[9][1] = dA_dr_im[9][2] = dA_dr_im[9][3] = dA_dr_im[9][4] = 0.0;\n\n\n  /* dA/dth */\n  dA_dth_re[0][0] = (6*A00600 + 8*A00800*dth2)*dth5 + dr*(dr*((4*A02400 + 6*A02600*dth2)*dth3 + dr*(dr*(dth*(2*A04200 + 4*A04400*dth2) + dr*(dr*(2*A06200*dth + 2*A07200*dr*dth) + dth*(2*A05200 + 4*A05400*dth2))) + (4*A03400 + 6*A03600*dth2)*dth3)) + (6*A01600 + 8*A01800*dth2)*dth5);\n  dA_dth_re[0][1] = (4*A00420 + 6*A00620*dth2)*dth3 + dr*(dr*(dth*(2*A02220 + 4*A02420*dth2) + dr*(dr*(2*A04220*dth + 2*A05220*dr*dth) + dth*(2*A03220 + 4*A03420*dth2))) + (4*A01420 + 6*A01620*dth2)*dth3);\n  dA_dth_re[0][2] = dth*(2*A00240 + 4*A00440*dth2) + dr*(dr*(2*A02240*dth + 2*A03240*dr*dth) + dth*(2*A01240 + 4*A01440*dth2));\n  dA_dth_re[0][3] = 2*A00260*dth + 2*A01260*dr*dth;\n  dA_dth_re[0][4] = 0.0;\n  dA_dth_im[0][0] = dA_dth_im[0][1] = dA_dth_im[0][2] = dA_dth_im[0][3] = dA_dth_im[0][4] = 0.0;\n\n  dA_dth_re[1][0] = dA_dth_re[1][1] = dA_dth_re[1][2] = dA_dth_re[1][3] = dA_dth_re[1][4] = 0.0;\n  dA_dth_im[1][0] = (6*A10601 + 8*A10801*dth2)*dth5 + dr*(dr*((4*A12401 + 6*A12601*dth2)*dth3 + dr*(dr*(dr*(2*A15201*dth + 2*A16201*dr*dth) + dth*(2*A14201 + 4*A14401*dth2)) + 4*A13401*dth3)) + 6*A11601*dth5);\n  dA_dth_im[1][1] = (4*A10421 + 6*A10621*dth2)*dth3 + dr*(dr*(dr*(2*A13221*dth + 2*A14221*dr*dth) + dth*(2*A12221 + 4*A12421*dth2)) + 4*A11421*dth3);\n  dA_dth_im[1][2] = dr*(2*A11241*dth + 2*A12241*dr*dth) + dth*(2*A10241 + 4*A10441*dth2);\n  dA_dth_im[1][3] = 2*A10261*dth;\n  dA_dth_im[1][4] = 0.0;\n\n  dA_dth_re[2][0] = dA_dth_re[2][1] = dA_dth_re[2][2] = dA_dth_re[2][3] = dA_dth_re[2][4] = 0.0;\n  dA_dth_im[2][0] = 7*A20701*dth6 + dr*(dr*(5*A22501*dth4 + dr*(dr*(3*A24301*dth2 + dr*(dr*(A26101 + A27101*dr) + 3*A25301*dth2)) + 5*A23501*dth4)) + 7*A21701*dth6);\n  dA_dth_im[2][1] = 5*A20521*dth4 + dr*(dr*(3*A22321*dth2 + dr*(dr*(A24121 + A25121*dr) + 3*A23321*dth2)) + 5*A21521*dth4);\n  dA_dth_im[2][2] = 3*A20341*dth2 + dr*(dr*(A22141 + A23141*dr) + 3*A21341*dth2);\n  dA_dth_im[2][3] = A20161 + A21161*dr;\n  dA_dth_im[2][4] = 0.0;\n\n  dA_dth_re[3][0] = (6*A30600 + 8*A30800*dth2)*dth5 + dr*(dr*((4*A32400 + 6*A32600*dth2)*dth3 + dr*(dr*(dth*(2*A34200 + 4*A34400*dth2) + dr*(dr*(2*A36200*dth + 2*A37200*dr*dth) + dth*(2*A35200 + 4*A35400*dth2))) + (4*A33400 + 6*A33600*dth2)*dth3)) + (6*A31600 + 8*A31800*dth2)*dth5);\n  dA_dth_re[3][1] = (4*A30420 + 6*A30620*dth2)*dth3 + dr*(dr*(dth*(2*A32220 + 4*A32420*dth2) + dr*(dr*(2*A34220*dth + 2*A35220*dr*dth) + dth*(2*A33220 + 4*A33420*dth2))) + (4*A31420 + 6*A31620*dth2)*dth3);\n  dA_dth_re[3][2] = dth*(2*A30240 + 4*A30440*dth2) + dr*(dr*(2*A32240*dth + 2*A33240*dr*dth) + dth*(2*A31240 + 4*A31440*dth2));\n  dA_dth_re[3][3] = 2*A30260*dth + 2*A31260*dr*dth;\n  dA_dth_re[3][4] = 0.0;\n  dA_dth_im[3][0] = dA_dth_im[3][1] = dA_dth_im[3][2] = dA_dth_im[3][3] = dA_dth_im[3][4] = 0.0;\n\n  dA_dth_re[4][0] = 8*A40800*dth7 + dr*(dr*(6*A42600*dth5 + dr*(dr*(4*A44400*dth3 + dr*(dr*(2*A46200*dth + 2*A47200*dr*dth) + 4*A45400*dth3)) + 6*A43600*dth5)) + 8*A41800*dth7);\n  dA_dth_re[4][1] = 6*A40620*dth5 + dr*(dr*(4*A42420*dth3 + dr*(dr*(2*A44220*dth + 2*A45220*dr*dth) + 4*A43420*dth3)) + 6*A41620*dth5);\n  dA_dth_re[4][2] = 4*A40440*dth3 + dr*(dr*(2*A42240*dth + 2*A43240*dr*dth) + 4*A41440*dth3);\n  dA_dth_re[4][3] = 2*A40260*dth + 2*A41260*dr*dth;\n  dA_dth_re[4][4] = 0.0;\n  dA_dth_im[4][0] = dA_dth_im[4][1] = dA_dth_im[4][2] = dA_dth_im[4][3] = dA_dth_im[4][4] = 0.0;\n\n  dA_dth_re[5][0] = dr2*(dr2*(dr2*(A58100*dr2 + 3*A56300*dth2) + 5*A54500*dth4) + 7*A52700*dth6) + 9*A50900*dth8;\n  dA_dth_re[5][1] = dr2*(dr2*(A56120*dr2 + 3*A54320*dth2) + 5*A52520*dth4) + 7*A50720*dth6;\n  dA_dth_re[5][2] = dr2*(A54140*dr2 + 3*A52340*dth2) + 5*A50540*dth4;\n  dA_dth_re[5][3] = A52160*dr2 + 3*A50360*dth2;\n  dA_dth_re[5][4] = A50180;\n  dA_dth_im[5][0] = dA_dth_im[5][1] = dA_dth_im[5][2] = dA_dth_im[5][3] = dA_dth_im[5][4] = 0.0;\n\n  dA_dth_re[6][0] = dA_dth_re[6][1] = dA_dth_re[6][2] = dA_dth_re[6][3] = dA_dth_re[6][4] = 0.0;\n  dA_dth_im[6][0] = (6*A60601 + 8*A60801*dth2)*dth5 + dr*(dr*((4*A62401 + 6*A62601*dth2)*dth3 + dr*(dr*(dr*(2*A65201*dth + 2*A66201*dr*dth) + dth*(2*A64201 + 4*A64401*dth2)) + 4*A63401*dth3)) + 6*A61601*dth5);\n  dA_dth_im[6][1] = (4*A60421 + 6*A60621*dth2)*dth3 + dr*(dr*(dr*(2*A63221*dth + 2*A64221*dr*dth) + dth*(2*A62221 + 4*A62421*dth2)) + 4*A61421*dth3);\n  dA_dth_im[6][2] = dr*(2*A61241*dth + 2*A62241*dr*dth) + dth*(2*A60241 + 4*A60441*dth2);\n  dA_dth_im[6][3] = 2*A60261*dth;\n  dA_dth_im[6][4] = 0.0;\n\n  dA_dth_re[7][0] = 8*A70800*dth7 + dr*(dr*(6*A72600*dth5 + dr*(dr*(4*A74400*dth3 + dr*(dr*(2*A76200*dth + 2*A77200*dr*dth) + 4*A75400*dth3)) + 6*A73600*dth5)) + 8*A71800*dth7);\n  dA_dth_re[7][1] = 6*A70620*dth5 + dr*(dr*(4*A72420*dth3 + dr*(dr*(2*A74220*dth + 2*A75220*dr*dth) + 4*A73420*dth3)) + 6*A71620*dth5);\n  dA_dth_re[7][2] = 4*A70440*dth3 + dr*(dr*(2*A72240*dth + 2*A73240*dr*dth) + 4*A71440*dth3);\n  dA_dth_re[7][3] = 2*A70260*dth + 2*A71260*dr*dth;\n  dA_dth_re[7][4] = 0.0;\n  dA_dth_im[7][0] = dA_dth_im[7][1] = dA_dth_im[7][2] = dA_dth_im[7][3] = dA_dth_im[7][4] = 0.0;\n\n  dA_dth_re[8][0] = dA_dth_re[8][1] = dA_dth_re[8][2] = dA_dth_re[8][3] = dA_dth_re[8][4] = 0.0;\n  dA_dth_im[8][0] = 7*A80701*dth6 + dr*(dr*(5*A82501*dth4 + dr*(dr*(3*A84301*dth2 + dr*(dr*(A86101 + A87101*dr) + 3*A85301*dth2)) + 5*A83501*dth4)) + 7*A81701*dth6);\n  dA_dth_im[8][1] = 5*A80521*dth4 + dr*(dr*(3*A82321*dth2 + dr*(dr*(A84121 + A85121*dr) + 3*A83321*dth2)) + 5*A81521*dth4);\n  dA_dth_im[8][2] = 3*A80341*dth2 + dr*(dr*(A82141 + A83141*dr) + 3*A81341*dth2);\n  dA_dth_im[8][3] = A80161 + A81161*dr;\n  dA_dth_im[8][4] = 0.0;\n\n  dA_dth_re[9][0] = (6*A90600 + 8*A90800*dth2)*dth5 + dr*(dr*((4*A92400 + 6*A92600*dth2)*dth3 + dr*(dr*(dth*(2*A94200 + 4*A94400*dth2) + dr*(dr*(2*A96200*dth + 2*A97200*dr*dth) + dth*(2*A95200 + 4*A95400*dth2))) + (4*A93400 + 6*A93600*dth2)*dth3)) + (6*A91600 + 8*A91800*dth2)*dth5);\n  dA_dth_re[9][1] = (4*A90420 + 6*A90620*dth2)*dth3 + dr*(dr*(dth*(2*A92220 + 4*A92420*dth2) + dr*(dr*(2*A94220*dth + 2*A95220*dr*dth) + dth*(2*A93220 + 4*A93420*dth2))) + (4*A91420 + 6*A91620*dth2)*dth3);\n  dA_dth_re[9][2] = dth*(2*A90240 + 4*A90440*dth2) + dr*(dr*(2*A92240*dth + 2*A93240*dr*dth) + dth*(2*A91240 + 4*A91440*dth2));\n  dA_dth_re[9][3] = 2*A90260*dth + 2*A91260*dr*dth;\n  dA_dth_re[9][4] = 0.0;\n  dA_dth_im[9][0] = dA_dth_im[9][1] = dA_dth_im[9][2] = dA_dth_im[9][3] = dA_dth_im[9][4] = 0.0;\n\n\n  /* d2A/dr2 */\n  d2A_dr2_re[0][0] = (2*A02400 + 2*A02600*dth2)*dth4 + dr*(dr*(dth2*(12*A04200 + 12*A04400*dth2) + dr*(dth2*(20*A05200 + 20*A05400*dth2) + dr*(30*A06000 + 30*A06200*dth2 + dr*(42*A07000 + dr*(56*A08000 + 72*A09000*dr) + 42*A07200*dth2)))) + (6*A03400 + 6*A03600*dth2)*dth4);\n  d2A_dr2_re[0][1] = dth2*(2*A02220 + 2*A02420*dth2) + dr*(dth2*(6*A03220 + 6*A03420*dth2) + dr*(12*A04020 + 12*A04220*dth2 + dr*(20*A05020 + dr*(30*A06020 + 42*A07020*dr) + 20*A05220*dth2)));\n  d2A_dr2_re[0][2] = 2*A02040 + 2*A02240*dth2 + dr*(6*A03040 + dr*(12*A04040 + 20*A05040*dr) + 6*A03240*dth2);\n  d2A_dr2_re[0][3] = 2*A02060 + 6*A03060*dr;\n  d2A_dr2_re[0][4] = 0.0;\n  d2A_dr2_im[0][0] = d2A_dr2_im[0][1] = d2A_dr2_im[0][2] = d2A_dr2_im[0][3] = d2A_dr2_im[0][4] = 0.0;\n\n  d2A_dr2_re[1][0] = d2A_dr2_re[1][1] = d2A_dr2_re[1][2] = d2A_dr2_re[1][3] = d2A_dr2_re[1][4] = 0.0;\n  d2A_dr2_im[1][0] = (2*A12401 + 2*A12601*dth2)*dth4 + dr*(dr*(dth2*(12*A14201 + 12*A14401*dth2) + dr*(20*A15201*dth2 + dr*(30*A16001 + dr*(42*A17001 + 56*A18001*dr) + 30*A16201*dth2))) + 6*A13401*dth4);\n  d2A_dr2_im[1][1] = dth2*(2*A12221 + 2*A12421*dth2) + dr*(6*A13221*dth2 + dr*(12*A14021 + dr*(20*A15021 + 30*A16021*dr) + 12*A14221*dth2));\n  d2A_dr2_im[1][2] = 2*A12041 + dr*(6*A13041 + 12*A14041*dr) + 2*A12241*dth2;\n  d2A_dr2_im[1][3] = 2*A12061;\n  d2A_dr2_im[1][4] = 0.0;\n\n  d2A_dr2_re[2][0] = d2A_dr2_re[2][1] = d2A_dr2_re[2][2] = d2A_dr2_re[2][3] = d2A_dr2_re[2][4] = 0.0;\n  d2A_dr2_im[2][0] = 2*A22501*dth5 + dr*(dr*(12*A24301*dth3 + dr*(dr*(30*A26101*dth + 42*A27101*dr*dth) + 20*A25301*dth3)) + 6*A23501*dth5);\n  d2A_dr2_im[2][1] = 2*A22321*dth3 + dr*(dr*(12*A24121*dth + 20*A25121*dr*dth) + 6*A23321*dth3);\n  d2A_dr2_im[2][2] = 2*A22141*dth + 6*A23141*dr*dth;\n  d2A_dr2_im[2][3] = 0.0;\n  d2A_dr2_im[2][4] = 0.0;\n\n  d2A_dr2_re[3][0] = (2*A32400 + 2*A32600*dth2)*dth4 + dr*(dr*(dth2*(12*A34200 + 12*A34400*dth2) + dr*(dth2*(20*A35200 + 20*A35400*dth2) + dr*(30*A36000 + 30*A36200*dth2 + dr*(42*A37000 + dr*(56*A38000 + 72*A39000*dr) + 42*A37200*dth2)))) + (6*A33400 + 6*A33600*dth2)*dth4);\n  d2A_dr2_re[3][1] = dth2*(2*A32220 + 2*A32420*dth2) + dr*(dth2*(6*A33220 + 6*A33420*dth2) + dr*(12*A34020 + 12*A34220*dth2 + dr*(20*A35020 + dr*(30*A36020 + 42*A37020*dr) + 20*A35220*dth2)));\n  d2A_dr2_re[3][2] = 2*A32040 + 2*A32240*dth2 + dr*(6*A33040 + dr*(12*A34040 + 20*A35040*dr) + 6*A33240*dth2);\n  d2A_dr2_re[3][3] = 2*A32060 + 6*A33060*dr;\n  d2A_dr2_re[3][4] = 0.0;\n  d2A_dr2_im[3][0] = d2A_dr2_im[3][1] = d2A_dr2_im[3][2] = d2A_dr2_im[3][3] = d2A_dr2_im[3][4] = 0.0;\n\n  d2A_dr2_re[4][0] = 2*A42600*dth6 + dr*(dr*(12*A44400*dth4 + dr*(dr*(30*A46200*dth2 + dr*(dr*(56*A48000 + 72*A49000*dr) + 42*A47200*dth2)) + 20*A45400*dth4)) + 6*A43600*dth6);\n  d2A_dr2_re[4][1] = 2*A42420*dth4 + dr*(dr*(12*A44220*dth2 + dr*(dr*(30*A46020 + 42*A47020*dr) + 20*A45220*dth2)) + 6*A43420*dth4);\n  d2A_dr2_re[4][2] = 2*A42240*dth2 + dr*(dr*(12*A44040 + 20*A45040*dr) + 6*A43240*dth2);\n  d2A_dr2_re[4][3] = 2*A42060 + 6*A43060*dr;\n  d2A_dr2_re[4][4] = 0.0;\n  d2A_dr2_im[4][0] = d2A_dr2_im[4][1] = d2A_dr2_im[4][2] = d2A_dr2_im[4][3] = d2A_dr2_im[4][4] = 0.0;\n\n  d2A_dr2_re[5][0] = dr2*(dr2*(56*A58100*dr2*dth + 30*A56300*dth3) + 12*A54500*dth5) + 2*A52700*dth7;\n  d2A_dr2_re[5][1] = dr2*(30*A56120*dr2*dth + 12*A54320*dth3) + 2*A52520*dth5;\n  d2A_dr2_re[5][2] = 12*A54140*dr2*dth + 2*A52340*dth3;\n  d2A_dr2_re[5][3] = 2*A52160*dth;\n  d2A_dr2_re[5][4] = 0.0;\n  d2A_dr2_im[5][0] = d2A_dr2_im[5][1] = d2A_dr2_im[5][2] = d2A_dr2_im[5][3] = d2A_dr2_im[5][4] = 0.0;\n\n  d2A_dr2_re[6][0] = d2A_dr2_re[6][1] = d2A_dr2_re[6][2] = d2A_dr2_re[6][3] = d2A_dr2_re[6][4] = 0.0;\n  d2A_dr2_im[6][0] = (2*A62401 + 2*A62601*dth2)*dth4 + dr*(dr*(dth2*(12*A64201 + 12*A64401*dth2) + dr*(20*A65201*dth2 + dr*(30*A66001 + dr*(42*A67001 + 56*A68001*dr) + 30*A66201*dth2))) + 6*A63401*dth4);\n  d2A_dr2_im[6][1] = dth2*(2*A62221 + 2*A62421*dth2) + dr*(6*A63221*dth2 + dr*(12*A64021 + dr*(20*A65021 + 30*A66021*dr) + 12*A64221*dth2));\n  d2A_dr2_im[6][2] = 2*A62041 + dr*(6*A63041 + 12*A64041*dr) + 2*A62241*dth2;\n  d2A_dr2_im[6][3] = 2*A62061;\n  d2A_dr2_im[6][4] = 0.0;\n\n  d2A_dr2_re[7][0] = 2*A72600*dth6 + dr*(dr*(12*A74400*dth4 + dr*(dr*(30*A76200*dth2 + dr*(dr*(56*A78000 + 72*A79000*dr) + 42*A77200*dth2)) + 20*A75400*dth4)) + 6*A73600*dth6);\n  d2A_dr2_re[7][1] = 2*A72420*dth4 + dr*(dr*(12*A74220*dth2 + dr*(dr*(30*A76020 + 42*A77020*dr) + 20*A75220*dth2)) + 6*A73420*dth4);\n  d2A_dr2_re[7][2] = 2*A72240*dth2 + dr*(dr*(12*A74040 + 20*A75040*dr) + 6*A73240*dth2);\n  d2A_dr2_re[7][3] = 2*A72060 + 6*A73060*dr;\n  d2A_dr2_re[7][4] = 0.0;\n  d2A_dr2_im[7][0] = d2A_dr2_im[7][1] = d2A_dr2_im[7][2] = d2A_dr2_im[7][3] = d2A_dr2_im[7][4] = 0.0;\n\n  d2A_dr2_re[8][0] = d2A_dr2_re[8][1] = d2A_dr2_re[8][2] = d2A_dr2_re[8][3] = d2A_dr2_re[8][4] = 0.0;\n  d2A_dr2_im[8][0] = 2*A82501*dth5 + dr*(dr*(12*A84301*dth3 + dr*(dr*(30*A86101*dth + 42*A87101*dr*dth) + 20*A85301*dth3)) + 6*A83501*dth5);\n  d2A_dr2_im[8][1] = 2*A82321*dth3 + dr*(dr*(12*A84121*dth + 20*A85121*dr*dth) + 6*A83321*dth3);\n  d2A_dr2_im[8][2] = 2*A82141*dth + 6*A83141*dr*dth;\n  d2A_dr2_im[8][3] = 0.0;\n  d2A_dr2_im[8][4] = 0.0;\n\n  d2A_dr2_re[9][0] = (2*A92400 + 2*A92600*dth2)*dth4 + dr*(dr*(dth2*(12*A94200 + 12*A94400*dth2) + dr*(dth2*(20*A95200 + 20*A95400*dth2) + dr*(30*A96000 + 30*A96200*dth2 + dr*(42*A97000 + dr*(56*A98000 + 72*A99000*dr) + 42*A97200*dth2)))) + (6*A93400 + 6*A93600*dth2)*dth4);\n  d2A_dr2_re[9][1] = dth2*(2*A92220 + 2*A92420*dth2) + dr*(dth2*(6*A93220 + 6*A93420*dth2) + dr*(12*A94020 + 12*A94220*dth2 + dr*(20*A95020 + dr*(30*A96020 + 42*A97020*dr) + 20*A95220*dth2)));\n  d2A_dr2_re[9][2] = 2*A92040 + 2*A92240*dth2 + dr*(6*A93040 + dr*(12*A94040 + 20*A95040*dr) + 6*A93240*dth2);\n  d2A_dr2_re[9][3] = 2*A92060 + 6*A93060*dr;\n  d2A_dr2_re[9][4] = 0.0;\n  d2A_dr2_im[9][0] = d2A_dr2_im[9][1] = d2A_dr2_im[9][2] = d2A_dr2_im[9][3] = d2A_dr2_im[9][4] = 0.0;\n\n\n  /* d2A/dth2 */\n  d2A_dth2_re[0][0] = (30*A00600 + 56*A00800*dth2)*dth4 + dr*(dr*(dth2*(12*A02400 + 30*A02600*dth2) + dr*(dth2*(12*A03400 + 30*A03600*dth2) + dr*(2*A04200 + 12*A04400*dth2 + dr*(2*A05200 + dr*(2*A06200 + 2*A07200*dr) + 12*A05400*dth2)))) + (30*A01600 + 56*A01800*dth2)*dth4);\n  d2A_dth2_re[0][1] = dth2*(12*A00420 + 30*A00620*dth2) + dr*(dth2*(12*A01420 + 30*A01620*dth2) + dr*(2*A02220 + 12*A02420*dth2 + dr*(2*A03220 + dr*(2*A04220 + 2*A05220*dr) + 12*A03420*dth2)));\n  d2A_dth2_re[0][2] = 2*A00240 + 12*A00440*dth2 + dr*(2*A01240 + dr*(2*A02240 + 2*A03240*dr) + 12*A01440*dth2);\n  d2A_dth2_re[0][3] = 2*A00260 + 2*A01260*dr;\n  d2A_dth2_re[0][4] = 0.0;\n  d2A_dth2_im[0][0] = d2A_dth2_im[0][1] = d2A_dth2_im[0][2] = d2A_dth2_im[0][3] = d2A_dth2_im[0][4] = 0.0;\n\n  d2A_dth2_re[1][0] = d2A_dth2_re[1][1] = d2A_dth2_re[1][2] = d2A_dth2_re[1][3] = d2A_dth2_re[1][4] = 0.0;\n  d2A_dth2_im[1][0] = (30*A10601 + 56*A10801*dth2)*dth4 + dr*(dr*(dth2*(12*A12401 + 30*A12601*dth2) + dr*(12*A13401*dth2 + dr*(2*A14201 + dr*(2*A15201 + 2*A16201*dr) + 12*A14401*dth2))) + 30*A11601*dth4);\n  d2A_dth2_im[1][1] = dth2*(12*A10421 + 30*A10621*dth2) + dr*(12*A11421*dth2 + dr*(2*A12221 + dr*(2*A13221 + 2*A14221*dr) + 12*A12421*dth2));\n  d2A_dth2_im[1][2] = 2*A10241 + dr*(2*A11241 + 2*A12241*dr) + 12*A10441*dth2;\n  d2A_dth2_im[1][3] = 2*A10261;\n  d2A_dth2_im[1][4] = 0.0;\n\n  d2A_dth2_re[2][0] = d2A_dth2_re[2][1] = d2A_dth2_re[2][2] = d2A_dth2_re[2][3] = d2A_dth2_re[2][4] = 0.0;\n  d2A_dth2_im[2][0] = 42*A20701*dth5 + dr*(dr*(20*A22501*dth3 + dr*(dr*(6*A24301*dth + 6*A25301*dr*dth) + 20*A23501*dth3)) + 42*A21701*dth5);\n  d2A_dth2_im[2][1] = 20*A20521*dth3 + dr*(dr*(6*A22321*dth + 6*A23321*dr*dth) + 20*A21521*dth3);\n  d2A_dth2_im[2][2] = 6*A20341*dth + 6*A21341*dr*dth;\n  d2A_dth2_im[2][3] = 0.0;\n  d2A_dth2_im[2][4] = 0.0;\n\n  d2A_dth2_re[3][0] = (30*A30600 + 56*A30800*dth2)*dth4 + dr*(dr*(dth2*(12*A32400 + 30*A32600*dth2) + dr*(dth2*(12*A33400 + 30*A33600*dth2) + dr*(2*A34200 + 12*A34400*dth2 + dr*(2*A35200 + dr*(2*A36200 + 2*A37200*dr) + 12*A35400*dth2)))) + (30*A31600 + 56*A31800*dth2)*dth4);\n  d2A_dth2_re[3][1] = dth2*(12*A30420 + 30*A30620*dth2) + dr*(dth2*(12*A31420 + 30*A31620*dth2) + dr*(2*A32220 + 12*A32420*dth2 + dr*(2*A33220 + dr*(2*A34220 + 2*A35220*dr) + 12*A33420*dth2)));\n  d2A_dth2_re[3][2] = 2*A30240 + 12*A30440*dth2 + dr*(2*A31240 + dr*(2*A32240 + 2*A33240*dr) + 12*A31440*dth2);\n  d2A_dth2_re[3][3] = 2*A30260 + 2*A31260*dr;\n  d2A_dth2_re[3][4] = 0.0;\n  d2A_dth2_im[3][0] = d2A_dth2_im[3][1] = d2A_dth2_im[3][2] = d2A_dth2_im[3][3] = d2A_dth2_im[3][4] = 0.0;\n\n  d2A_dth2_re[4][0] = 56*A40800*dth6 + dr*(dr*(30*A42600*dth4 + dr*(dr*(12*A44400*dth2 + dr*(dr*(2*A46200 + 2*A47200*dr) + 12*A45400*dth2)) + 30*A43600*dth4)) + 56*A41800*dth6);\n  d2A_dth2_re[4][1] = 30*A40620*dth4 + dr*(dr*(12*A42420*dth2 + dr*(dr*(2*A44220 + 2*A45220*dr) + 12*A43420*dth2)) + 30*A41620*dth4);\n  d2A_dth2_re[4][2] = 12*A40440*dth2 + dr*(dr*(2*A42240 + 2*A43240*dr) + 12*A41440*dth2);\n  d2A_dth2_re[4][3] = 2*A40260 + 2*A41260*dr;\n  d2A_dth2_re[4][4] = 0.0;\n  d2A_dth2_im[4][0] = d2A_dth2_im[4][1] = d2A_dth2_im[4][2] = d2A_dth2_im[4][3] = d2A_dth2_im[4][4] = 0.0;\n\n  d2A_dth2_re[5][0] = dr2*(dr2*(6*A56300*dr2*dth + 20*A54500*dth3) + 42*A52700*dth5) + 72*A50900*dth7;\n  d2A_dth2_re[5][1] = dr2*(6*A54320*dr2*dth + 20*A52520*dth3) + 42*A50720*dth5;\n  d2A_dth2_re[5][2] = 6*A52340*dr2*dth + 20*A50540*dth3;\n  d2A_dth2_re[5][3] = 6*A50360*dth;\n  d2A_dth2_re[5][4] = 0.0;\n  d2A_dth2_im[5][0] = d2A_dth2_im[5][1] = d2A_dth2_im[5][2] = d2A_dth2_im[5][3] = d2A_dth2_im[5][4] = 0.0;\n\n  d2A_dth2_re[6][0] = d2A_dth2_re[6][1] = d2A_dth2_re[6][2] = d2A_dth2_re[6][3] = d2A_dth2_re[6][4] = 0.0;\n  d2A_dth2_im[6][0] = (30*A60601 + 56*A60801*dth2)*dth4 + dr*(dr*(dth2*(12*A62401 + 30*A62601*dth2) + dr*(12*A63401*dth2 + dr*(2*A64201 + dr*(2*A65201 + 2*A66201*dr) + 12*A64401*dth2))) + 30*A61601*dth4);\n  d2A_dth2_im[6][1] = dth2*(12*A60421 + 30*A60621*dth2) + dr*(12*A61421*dth2 + dr*(2*A62221 + dr*(2*A63221 + 2*A64221*dr) + 12*A62421*dth2));\n  d2A_dth2_im[6][2] = 2*A60241 + dr*(2*A61241 + 2*A62241*dr) + 12*A60441*dth2;\n  d2A_dth2_im[6][3] = 2*A60261;\n  d2A_dth2_im[6][4] = 0.0;\n\n  d2A_dth2_re[7][0] = 56*A70800*dth6 + dr*(dr*(30*A72600*dth4 + dr*(dr*(12*A74400*dth2 + dr*(dr*(2*A76200 + 2*A77200*dr) + 12*A75400*dth2)) + 30*A73600*dth4)) + 56*A71800*dth6);\n  d2A_dth2_re[7][1] = 30*A70620*dth4 + dr*(dr*(12*A72420*dth2 + dr*(dr*(2*A74220 + 2*A75220*dr) + 12*A73420*dth2)) + 30*A71620*dth4);\n  d2A_dth2_re[7][2] = 12*A70440*dth2 + dr*(dr*(2*A72240 + 2*A73240*dr) + 12*A71440*dth2);\n  d2A_dth2_re[7][3] = 2*A70260 + 2*A71260*dr;\n  d2A_dth2_re[7][4] = 0.0;\n  d2A_dth2_im[7][0] = d2A_dth2_im[7][1] = d2A_dth2_im[7][2] = d2A_dth2_im[7][3] = d2A_dth2_im[7][4] = 0.0;\n\n  d2A_dth2_re[8][0] = d2A_dth2_re[8][1] = d2A_dth2_re[8][2] = d2A_dth2_re[8][3] = d2A_dth2_re[8][4] = 0.0;\n  d2A_dth2_im[8][0] = 42*A80701*dth5 + dr*(dr*(20*A82501*dth3 + dr*(dr*(6*A84301*dth + 6*A85301*dr*dth) + 20*A83501*dth3)) + 42*A81701*dth5);\n  d2A_dth2_im[8][1] = 20*A80521*dth3 + dr*(dr*(6*A82321*dth + 6*A83321*dr*dth) + 20*A81521*dth3);\n  d2A_dth2_im[8][2] = 6*A80341*dth + 6*A81341*dr*dth;\n  d2A_dth2_im[8][3] = 0.0;\n  d2A_dth2_im[8][4] = 0.0;\n\n  d2A_dth2_re[9][0] = (30*A90600 + 56*A90800*dth2)*dth4 + dr*(dr*(dth2*(12*A92400 + 30*A92600*dth2) + dr*(dth2*(12*A93400 + 30*A93600*dth2) + dr*(2*A94200 + 12*A94400*dth2 + dr*(2*A95200 + dr*(2*A96200 + 2*A97200*dr) + 12*A95400*dth2)))) + (30*A91600 + 56*A91800*dth2)*dth4);\n  d2A_dth2_re[9][1] = dth2*(12*A90420 + 30*A90620*dth2) + dr*(dth2*(12*A91420 + 30*A91620*dth2) + dr*(2*A92220 + 12*A92420*dth2 + dr*(2*A93220 + dr*(2*A94220 + 2*A95220*dr) + 12*A93420*dth2)));\n  d2A_dth2_re[9][2] = 2*A90240 + 12*A90440*dth2 + dr*(2*A91240 + dr*(2*A92240 + 2*A93240*dr) + 12*A91440*dth2);\n  d2A_dth2_re[9][3] = 2*A90260 + 2*A91260*dr;\n  d2A_dth2_re[9][4] = 0.0;\n  d2A_dth2_im[9][0] = d2A_dth2_im[9][1] = d2A_dth2_im[9][2] = d2A_dth2_im[9][3] = d2A_dth2_im[9][4] = 0.0;\n\n  /* alpha term appearing in the denominator */\n  double alpha = alpha20*dr2 + alpha02*dth2;\n\n  /* Derivatives of alpha */\n  double dalpha_dr       = 2*alpha20*dr;\n  double d2alpha_dr2     = 2*alpha20;\n  double dalpha_dth      = 2*alpha02*dth;\n  double d2alpha_dth2    = 2*alpha02;\n\n  /* Powers of C = alpha / beta */\n  const double beta_inv = 1.0 / beta;\n  double C[27];\n  C[0]  = 1;\n  C[1]  = alpha * beta_inv;\n  for(int i=2; i<=26; i++)\n    C[i] = C[1]*C[i-1];\n\n  double dC1_dr       = dalpha_dr * beta_inv;\n  double d2C1_dr2     = d2alpha_dr2 * beta_inv;\n  double dC1_dth      = dalpha_dth * beta_inv;\n  double d2C1_dth2    = d2alpha_dth2 * beta_inv;\n\n  double dC_dr[27];\n  dC_dr[0]  = 0;\n  for(int i=1; i<=26; i++)\n    dC_dr[i] = i*C[i-1]*dC1_dr;\n\n  double d2C_dr2[27];\n  d2C_dr2[0]  = 0;\n  d2C_dr2[1]  = d2C1_dr2;\n  for(int i=2; i<=26; i++)\n    d2C_dr2[i] = i*(i-1)*C[i-2]*dC1_dr*dC1_dr + i*C[i-1]*d2C1_dr2;\n\n  double dC_dth[27];\n  dC_dth[0]  = 0;\n  for(int i=1; i<=26; i++)\n    dC_dth[i] = i*C[i-1]*dC1_dth;\n\n  double d2C_dth2[27];\n  d2C_dth2[0]  = 0;\n  d2C_dth2[1]  = d2C1_dth2;\n  for(int i=2; i<=26; i++)\n    d2C_dth2[i] = i*(i-1)*C[i-2]*dC1_dth*dC1_dth + i*C[i-1]*d2C1_dth2;\n\n  /* Elliptic integrals */\n  double gam = sqrt(1.0/(1.0+C[1]));\n  double ellE = gsl_sf_ellint_Ecomp(gam, GSL_PREC_DOUBLE);\n  double ellK = gsl_sf_ellint_Kcomp(gam, GSL_PREC_DOUBLE);\n\n  /* Derivatives of elliptic integrals */\n  double dellE_dC   = (ellK - ellE)/(2.*(1+C[1]));\n  double dellK_dC   = (C[1]*ellK - (1+C[1])*ellE)/(2.*C[1]*(1+C[1]));\n  double d2ellE_dC2 = -(2*C[1]*ellK - (2*C[1]-1)*ellE)/(4.*C[1]*(1+C[1])*(1+C[1]));\n  double d2ellK_dC2 = -(C[1]*(1+2*C[1])*ellK - 2*(1+C[1])*(1+C[1])*ellE)/(4.*C[1]*C[1]*(1+C[1])*(1+C[1]));\n\n  double dellE_dr       = dellE_dC * dC1_dr;\n  double dellE_dth      = dellE_dC * dC1_dth;\n  double d2ellE_dr2     = d2ellE_dC2 * dC1_dr * dC1_dr + dellE_dC * d2C1_dr2;\n  double d2ellE_dth2    = d2ellE_dC2 * dC1_dth * dC1_dth + dellE_dC * d2C1_dth2;\n\n  double dellK_dr       = dellK_dC * dC1_dr;\n  double dellK_dth      = dellK_dC * dC1_dth;\n  double d2ellK_dr2     = d2ellK_dC2 * dC1_dr * dC1_dr + dellK_dC * d2C1_dr2;\n  double d2ellK_dth2    = d2ellK_dC2 * dC1_dth * dC1_dth + dellK_dC * d2C1_dth2;\n\n  /* Denominator - there is a different denominator for real and imaginary parts */\n  double alpha_plus_beta_10 = alpha+beta;\n  double alpha_plus_beta_05 = sqrt(alpha_plus_beta_10);\n  double alpha_plus_beta_15 = alpha_plus_beta_10*alpha_plus_beta_05;\n  double alpha_plus_beta_20 = alpha_plus_beta_10*alpha_plus_beta_10;\n  double alpha_plus_beta_25 = alpha_plus_beta_10*alpha_plus_beta_15;\n\n  double beta_2 = beta*beta;\n\n  double den_re        = beta*C[3]*alpha_plus_beta_25/4.0;\n  double dden_dr_re    = beta*(6.0*C[2]*alpha_plus_beta_25*dC1_dr + C[3]*5.0*alpha_plus_beta_15*dalpha_dr)/8.0;\n  double dden_dth_re   = beta*(6.0*C[2]*alpha_plus_beta_25*dC1_dth + C[3]*5.0*alpha_plus_beta_15*dalpha_dth)/8.0;\n  double d2den_dr2_re  = beta*C[1]*alpha_plus_beta_05*(24*alpha_plus_beta_20*dC1_dr*dC1_dr + 60.0*C[1]*(alpha+beta)*dalpha_dr*dC1_dr + C[1]*(12.0*alpha_plus_beta_20*d2C1_dr2 + 5.0*C[1]*(3.0*dalpha_dr*dalpha_dr + 2.0*(alpha+beta)*d2alpha_dr2)))/16.0;\n  double d2den_dth2_re = beta*C[1]*alpha_plus_beta_05*(24*alpha_plus_beta_20*dC1_dth*dC1_dth + 60.0*C[1]*(alpha+beta)*dalpha_dth*dC1_dth + C[1]*(12.0*alpha_plus_beta_20*d2C1_dth2 + 5.0*C[1]*(3.0*dalpha_dth*dalpha_dth + 2.0*(alpha+beta)*d2alpha_dth2)))/16.0;\n\n  double den_im        = -(beta_2*C[2]*alpha_plus_beta_15)/32.0;\n  double dden_dr_im    = -(beta_2*C[1]*alpha_plus_beta_05*(4.0*alpha_plus_beta_10*dC1_dr + 3*C[1]*dalpha_dr))/64.;\n  double dden_dth_im   = -(beta_2*C[1]*alpha_plus_beta_05*(4.0*alpha_plus_beta_10*dC1_dth + 3*C[1]*dalpha_dth))/64.;\n  double d2den_dr2_im  = -(beta_2*(C[1]*(8*alpha_plus_beta_20*d2C1_dr2 + 3.0*C[1]*(2.0*alpha_plus_beta_10*d2alpha_dr2 + dalpha_dr*dalpha_dr)) + 24.0*alpha_plus_beta_10*C[1]*dalpha_dr*dC1_dr + 8.0*alpha_plus_beta_20*dC1_dr*dC1_dr))/(128.*alpha_plus_beta_05);\n  double d2den_dth2_im = -(beta_2*(C[1]*(8*alpha_plus_beta_20*d2C1_dth2 + 3.0*C[1]*(2.0*alpha_plus_beta_10*d2alpha_dth2 + dalpha_dth*dalpha_dth)) + 24.0*alpha_plus_beta_10*C[1]*dalpha_dth*dC1_dth + 8.0*alpha_plus_beta_20*dC1_dth*dC1_dth))/(128.*alpha_plus_beta_05);\n\n  for(int l=0; l<10; l++)\n  {\n    /* First, compute derivatives of numerator  - we will add the denominator at the end */\n\n    /* hS */\n    hS_re[l] = 0.0;\n    hS_im[l] = 0.0;\n    for(int j=0; j<5; j++) {\n      double fac1K = ellK*A_re[l][j];\n      for(int k=max(j,0); k<=m+2+j; k++) {\n        hS_re[l] += fac1K*C[k]*ReEI[m][0][j][k];\n      }\n\n      double fac1E = ellE*A_re[l][j];\n      for(int k=max(j-1,0); k<=m+2+j; k++) {\n        hS_re[l] += fac1E*C[k]*ReEI[m][1][j][k];\n      }\n      \n      double fac2K = ellK*A_im[l][j];\n      for(int k=max(j-1,0); k<=m+1+j; k++) {\n        hS_im[l] += fac2K*C[k]*ImEI[m][0][j][k];\n      }\n      \n      double fac2E = ellE*A_im[l][j];\n      for(int k=max(j-2,0); k<=m+1+j; k++) {\n        hS_im[l] += fac2E*C[k]*ImEI[m][1][j][k];\n      }\n    }\n\n    /* dhS_dr */\n    dhS_dr_re[l] = 0.0;\n    dhS_dr_im[l] = 0.0;\n    for(int j=0; j<5; j++) {\n      double fac1K = (dellK_dr*A_re[l][j] + ellK*dA_dr_re[l][j]);\n      double fac2K = ellK*A_re[l][j];\n      for(int k=max(j,0); k<=m+2+j; k++) {\n        dhS_dr_re[l] += ReEI[m][0][j][k]*(C[k]*fac1K + fac2K*dC_dr[k]);\n      }\n\n      double fac1E = (dellE_dr*A_re[l][j] + ellE*dA_dr_re[l][j]);\n      double fac2E = ellE*A_re[l][j];\n      for(int k=max(j-1,0); k<=m+2+j; k++) {\n        dhS_dr_re[l] += ReEI[m][1][j][k]*(C[k]*fac1E + fac2E*dC_dr[k]);\n      }\n\n      double fac3K = (dellK_dr*A_im[l][j] + ellK*dA_dr_im[l][j]);\n      double fac4K = ellK*A_im[l][j];\n      for(int k=max(j-1,0); k<=m+1+j; k++) {\n        dhS_dr_im[l] += ImEI[m][0][j][k]*(C[k]*fac3K + fac4K*dC_dr[k]);\n      }\n\n      double fac3E = (dellE_dr*A_im[l][j] + ellE*dA_dr_im[l][j]);\n      double fac4E = ellE*A_im[l][j];\n      for(int k=max(j-2,0); k<=m+1+j; k++) {\n        dhS_dr_im[l] += ImEI[m][1][j][k]*(C[k]*fac3E + fac4E*dC_dr[k]);\n      }\n    }\n\n    /* dhS_dth */\n    dhS_dth_re[l] = 0.0;\n    dhS_dth_im[l] = 0.0;\n    for(int j=0; j<5; j++) {\n      double fac1K = (dellK_dth*A_re[l][j] + ellK*dA_dth_re[l][j]);\n      double fac2K = ellK*A_re[l][j];\n      for(int k=max(j,0); k<=m+2+j; k++) {\n        dhS_dth_re[l] += ReEI[m][0][j][k]*(C[k]*fac1K + fac2K*dC_dth[k]);\n      }\n\n      double fac1E = (dellE_dth*A_re[l][j] + ellE*dA_dth_re[l][j]);\n      double fac2E = ellE*A_re[l][j];\n      for(int k=max(j-1,0); k<=m+2+j; k++) {\n        dhS_dth_re[l] += ReEI[m][1][j][k]*(C[k]*fac1E + fac2E*dC_dth[k]);\n      }\n\n      double fac3K = (dellK_dth*A_im[l][j] + ellK*dA_dth_im[l][j]);\n      double fac4K = ellK*A_im[l][j];\n      for(int k=max(j-1,0); k<=m+1+j; k++) {\n        dhS_dth_im[l] += ImEI[m][0][j][k]*(C[k]*fac3K + fac4K*dC_dth[k]);\n      }\n\n      double fac3E = (dellE_dth*A_im[l][j] + ellE*dA_dth_im[l][j]);\n      double fac4E = ellE*A_im[l][j];\n      for(int k=max(j-2,0); k<=m+1+j; k++) {\n        dhS_dth_im[l] += ImEI[m][1][j][k]*(C[k]*fac3E + fac4E*dC_dth[k]);\n      }\n    }\n\n    /* d2hS_dr2 */\n    d2hS_dr2_re[l] = 0.0;\n    d2hS_dr2_im[l] = 0.0;\n    for(int j=0; j<5; j++) {\n      double fac1K = (d2ellK_dr2*A_re[l][j] + 2.0*dellK_dr*dA_dr_re[l][j] + ellK*d2A_dr2_re[l][j]);\n      double fac2K = (dellK_dr*A_re[l][j] + ellK*dA_dr_re[l][j]);\n      double fac3K = ellK*A_re[l][j];\n      for(int k=max(j,0); k<=m+2+j; k++) {\n        d2hS_dr2_re[l] += ReEI[m][0][j][k]*(C[k]*fac1K + 2.0*dC_dr[k]*fac2K + fac3K*d2C_dr2[k]);\n      }\n\n      double fac1E = (d2ellE_dr2*A_re[l][j] + 2.0*dellE_dr*dA_dr_re[l][j] + ellE*d2A_dr2_re[l][j]);\n      double fac2E = (dellE_dr*A_re[l][j] + ellE*dA_dr_re[l][j]);\n      double fac3E = ellE*A_re[l][j];\n      for(int k=max(j-1,0); k<=m+2+j; k++) {\n        d2hS_dr2_re[l] += ReEI[m][1][j][k]*(C[k]*fac1E + 2.0*dC_dr[k]*fac2E + fac3E*d2C_dr2[k]);\n      }\n\n      double fac4K = (d2ellK_dr2*A_im[l][j] + 2.0*dellK_dr*dA_dr_im[l][j] + ellK*d2A_dr2_im[l][j]);\n      double fac5K = (dellK_dr*A_im[l][j] + ellK*dA_dr_im[l][j]);\n      double fac6K = ellK*A_im[l][j];\n      for(int k=max(j-1,0); k<=m+1+j; k++) {\n        d2hS_dr2_im[l] += ImEI[m][0][j][k]*(C[k]*fac4K + 2.0*dC_dr[k]*fac5K + fac6K*d2C_dr2[k]);\n      }\n\n      double fac4E = (d2ellE_dr2*A_im[l][j] + 2.0*dellE_dr*dA_dr_im[l][j] + ellE*d2A_dr2_im[l][j]);\n      double fac5E = (dellE_dr*A_im[l][j] + ellE*dA_dr_im[l][j]);\n      double fac6E = ellE*A_im[l][j];\n      for(int k=max(j-2,0); k<=m+1+j; k++) {\n        d2hS_dr2_im[l] += ImEI[m][1][j][k]*(C[k]*fac4E + 2.0*dC_dr[k]*fac5E + fac6E*d2C_dr2[k]);\n      }\n    }\n\n    /* d2hS_dth2 */\n    d2hS_dth2_re[l] = 0.0;\n    d2hS_dth2_im[l] = 0.0;\n    for(int j=0; j<5; j++) {\n      double fac1K = (d2ellK_dth2*A_re[l][j] + 2.0*dellK_dth*dA_dth_re[l][j] + ellK*d2A_dth2_re[l][j]);\n      double fac2K = (dellK_dth*A_re[l][j] + ellK*dA_dth_re[l][j]);\n      double fac3K = ellK*A_re[l][j];\n      for(int k=max(j,0); k<=m+2+j; k++) {\n        d2hS_dth2_re[l] += ReEI[m][0][j][k]*(C[k]*fac1K + 2.0*dC_dth[k]*fac2K + fac3K*d2C_dth2[k]);\n      }\n\n      double fac1E = (d2ellE_dth2*A_re[l][j] + 2.0*dellE_dth*dA_dth_re[l][j] + ellE*d2A_dth2_re[l][j]);\n      double fac2E = (dellE_dth*A_re[l][j] + ellE*dA_dth_re[l][j]);\n      double fac3E = ellE*A_re[l][j];\n      for(int k=max(j-1,0); k<=m+2+j; k++) {\n        d2hS_dth2_re[l] += ReEI[m][1][j][k]*(C[k]*fac1E + 2.0*dC_dth[k]*fac2E + fac3E*d2C_dth2[k]);\n      }\n\n      double fac4K = (d2ellK_dth2*A_im[l][j] + 2.0*dellK_dth*dA_dth_im[l][j] + ellK*d2A_dth2_im[l][j]);\n      double fac5K = (dellK_dth*A_im[l][j] + ellK*dA_dth_im[l][j]);\n      double fac6K = ellK*A_im[l][j];\n      for(int k=max(j-1,0); k<=m+1+j; k++) {\n        d2hS_dth2_im[l] += ImEI[m][0][j][k]*(C[k]*fac4K + 2.0*dC_dth[k]*fac5K + fac6K*d2C_dth2[k]);\n      }\n\n      double fac4E = (d2ellE_dth2*A_im[l][j] + 2.0*dellE_dth*dA_dth_im[l][j] + ellE*d2A_dth2_im[l][j]);\n      double fac5E = (dellE_dth*A_im[l][j] + ellE*dA_dth_im[l][j]);\n      double fac6E = ellE*A_im[l][j];\n      for(int k=max(j-2,0); k<=m+1+j; k++) {\n        d2hS_dth2_im[l] += ImEI[m][1][j][k]*(C[k]*fac4E + 2.0*dC_dth[k]*fac5E + fac6E*d2C_dth2[k]);\n      }\n    }\n\n    /* Quotient rule of differentiation to include the denominator*/\n    d2hS_dr2_re[l] = (d2hS_dr2_re[l]*den_re*den_re - 2.0*dden_dr_re*den_re*dhS_dr_re[l] + 2.0*dden_dr_re*dden_dr_re*hS_re[l] - den_re*d2den_dr2_re*hS_re[l])/(den_re*den_re*den_re);\n    d2hS_dr2_im[l] = (d2hS_dr2_im[l]*den_im*den_im - 2*dden_dr_im*den_im*dhS_dr_im[l] + 2*dden_dr_im*dden_dr_im*hS_im[l] - den_im*d2den_dr2_im*hS_im[l])/(den_im*den_im*den_im);\n    d2hS_dth2_re[l] = (d2hS_dth2_re[l]*den_re*den_re - 2*dden_dth_re*den_re*dhS_dth_re[l] + 2*dden_dth_re*dden_dth_re*hS_re[l] - den_re*d2den_dth2_re*hS_re[l])/(den_re*den_re*den_re);\n    d2hS_dth2_im[l] = (d2hS_dth2_im[l]*den_im*den_im - 2*dden_dth_im*den_im*dhS_dth_im[l] + 2*dden_dth_im*dden_dth_im*hS_im[l] - den_im*d2den_dth2_im*hS_im[l])/(den_im*den_im*den_im);\n    dhS_dr_re[l] = (den_re*dhS_dr_re[l] - dden_dr_re*hS_re[l])/(den_re*den_re);\n    dhS_dr_im[l] = (den_im*dhS_dr_im[l] - dden_dr_im*hS_im[l])/(den_im*den_im);\n    dhS_dth_re[l] = (den_re*dhS_dth_re[l] - dden_dth_re*hS_re[l])/(den_re*den_re);\n    dhS_dth_im[l] = (den_im*dhS_dth_im[l] - dden_dth_im*hS_im[l])/(den_im*den_im);\n\n    hS_re[l] /= den_re;\n    hS_im[l] /= den_im;\n  }\n\n  for(int i=0; i<10; i++)\n  {\n    dhS_dph_re[i] = -m*hS_im[i];\n    dhS_dph_im[i] = m*hS_re[i];\n    dhS_dt_re[i]  = -Omega*dhS_dph_re[i];\n    dhS_dt_im[i]  = -Omega*dhS_dph_im[i];\n\n    d2hS_dph2_re[i] = -m*dhS_dph_im[i];\n    d2hS_dph2_im[i] = m*dhS_dph_re[i];\n    d2hS_dtph_re[i] = -Omega*d2hS_dph2_re[i];\n    d2hS_dtph_im[i] = -Omega*d2hS_dph2_im[i];\n    d2hS_dt2_re[i]  = -Omega*d2hS_dtph_re[i];\n    d2hS_dt2_im[i]  = -Omega*d2hS_dtph_im[i];\n\n    d2hS_dtr_re[i]   = NAN;\n    d2hS_dtr_im[i]   = NAN;\n    d2hS_dtth_re[i]  = NAN;\n    d2hS_dtth_im[i]  = NAN;\n    d2hS_drth_re[i]  = NAN;\n    d2hS_drth_im[i]  = NAN;\n    d2hS_drph_re[i]  = NAN;\n    d2hS_drph_im[i]  = NAN;\n    d2hS_dthph_re[i] = NAN;\n    d2hS_dthph_im[i] = NAN;\n  }\n\n  double a10, a2, a2p2r2pa2c2th2, a2p2r2pa2c2th3, a2p2r2pa2c2th4, a2p2r2pa2c2th5, a2pr2m2Mr2, a2pr2m2Mr3, a3, a4, a6, a8, costh2, costh4, M2, M3, r10, r11, r2, r2pa2cth22, r2pa2cth23, r3, r4, r5, r6, r7, r8, r9, sinth2, sinth3, sinth4, sin4th, cos2th, cos4th, cos6th, cos8th, cos10th, sin2th, r2pa2cth2, a2p2r2pa2c2th, a2pr2m2Mr, costh, sinth, tanth;\n\n  M2 = M*M;\n  M3 = M2*M;\n\n  a2 = a*a;\n  a3 = a2*a;\n  a4 = a2*a2;\n  a6 = a4*a2;\n  a8 = a4*a4;\n  a10 = a6*a4;\n\n  r2 = r*r;\n  r3 = r2*r;\n  r4 = r2*r2;\n  r5 = r3*r2;\n  r6 = r3*r3;\n  r7 = r4*r3;\n  r8 = r4*r4;\n  r9 = r5*r4;\n  r10 = r5*r5;\n  r11 = r6*r5;\n\n  sinth = sin(theta);\n  costh = cos(theta);\n  tanth = sinth/costh;\n\n  costh2 = costh*costh;\n  costh4 = costh2*costh2;\n  sinth2 = sinth*sinth;\n  sinth3 = sinth2*sinth;\n  sinth4 = sinth2*sinth2;\n\n  cos2th = costh2 - sinth2;\n  sin2th = 2.0*costh*sinth;\n  sin4th = 2.0*cos2th*sin2th;\n  cos4th = cos2th*cos2th - sin2th*sin2th;\n  cos6th = cos2th*cos2th*cos2th - 3.0*cos2th*sin2th*sin2th;\n  cos8th = cos4th*cos4th - sin4th*sin4th;\n  cos10th = cos2th*cos2th*cos2th*cos2th*cos2th - 10.0*cos2th*cos2th*cos2th*sin2th*sin2th + 5.0*cos2th*sin2th*sin2th*sin2th*sin2th;\n\n  r2pa2cth2 = a2*costh2 + r2;\n  r2pa2cth22 = r2pa2cth2*r2pa2cth2;\n  r2pa2cth23 = r2pa2cth22*r2pa2cth2;\n\n  a2p2r2pa2c2th = a2 + a2*cos2th + 2*r2;\n  a2p2r2pa2c2th2 = a2p2r2pa2c2th*a2p2r2pa2c2th;\n  a2p2r2pa2c2th3 = a2p2r2pa2c2th2*a2p2r2pa2c2th;\n  a2p2r2pa2c2th4 = a2p2r2pa2c2th2*a2p2r2pa2c2th2;\n  a2p2r2pa2c2th5 = a2p2r2pa2c2th3*a2p2r2pa2c2th2;\n\n  a2pr2m2Mr = a2 + r*(-2*M + r);\n  a2pr2m2Mr2 = a2pr2m2Mr*a2pr2m2Mr;\n  a2pr2m2Mr3 = a2pr2m2Mr2*a2pr2m2Mr;\n\n  src_re[0] = (-8*a*d2hS_dtph_re[0]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (16*dhS_dt_re[1]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2_re[0]/r2pa2cth2 + (d2hS_dr2_re[0]*(a2 - 2*M*r + r2))/r2pa2cth2 + (4*a*dhS_dr_re[3]*M*(a2*costh2 - r2))/r2pa2cth23 + (4*a*dhS_dph_re[1]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2_re[0]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (16*hS_re[7]*M*r*(-9*a4 + 3*a4*cos4th + 20*a2*M*r - 2*a2*cos2th*(3*a2 + 5*r*(-2*M + r)) + 2*a2*r2 - 8*M*r3 + 4*r4))/a2p2r2pa2c2th5 + (2*dhS_dr_re[0]*(5*a4*M - a4*cos4th*(M - r) + 3*a4*r - 16*a2*M*r2 + 8*a2*r3 + 4*a2*cos2th*(a2*(M + r) + 2*r3) - 24*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (8*a2pr2m2Mr*hS_re[4]*M*(3*a4*M - 27*a4*r + a4*cos4th*(M + 3*r) + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 6*r) + (10*M - 7*r)*r2) - 4*a2*r3 - 24*M*r4 + 16*r5))/a2p2r2pa2c2th5 + (16*a*hS_re[3]*M2*(3*a6 + 29*a4*r2 + a4*cos4th*(a2 + 7*r2) - 80*a2*M*r3 + 32*a2*r4 + 4*a2*cos2th*(a4 + 9*a2*r2 - 20*M*r3 + 6*r4) + 32*M*r5 - 8*r6))/(a2p2r2pa2c2th5*a2pr2m2Mr) + (8*hS_re[0]*M2*(3*a8 + 2*a6*r2 - 20*a4*M*r3 + a4*cos4th*(a4 - 2*a2*r2 + 20*M*r3 - 3*r4) - a4*r4 + 16*a2*M*r5 + 4*a2*cos2th*(a6 - 3*a2*r4 - 2*(2*M + r)*r5) + 8*a2*r6 + 8*r8))/(a2p2r2pa2c2th5*a2pr2m2Mr) - (32*a2*dhS_dt_re[2]*M*r*sin2th)/a2p2r2pa2c2th3 - (96*a2*a2pr2m2Mr*hS_re[5]*M*(a2 + a2*cos2th - 6*r2)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2_re[0]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (2*hS_re[9]*M*(-2*a6*M + a6*cos6th*M + 30*a6*r + 3*a6*cos6th*r - 200*a4*M*r2 + 2*a4*cos4th*(a2*(M + 9*r) + 2*(-14*M + 5*r)*r2) + 60*a4*r3 + 320*a2*M2*r3 - a2*cos2th*(a4*(M - 45*r) + 16*a2*(16*M - 5*r)*r2 - 16*(20*M2 - 11*M*r + r2)*r3) - 208*a2*M*r4 + 16*a2*r5 - 128*M2*r5 + 128*M*r6 - 32*r7))/(a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (64*a*dhS_dph_re[2]*M*r)/(a2p2r2pa2c2th3*tanth) + (64*a*dhS_dth_re[3]*M*r)/(a2p2r2pa2c2th3*tanth) + (dhS_dth_re[0]*(3*a4 + a4*cos4th + 32*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-4*M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth);\n  src_re[1] = (-8*a*d2hS_dtph_re[1]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt_re[4]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2_re[1]/r2pa2cth2 + (d2hS_dr2_re[1]*(a2 - 2*M*r + r2))/r2pa2cth2 - (2*dhS_dth_re[2]*r)/r2pa2cth22 + (2*a*dhS_dr_re[6]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph_re[4]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (4*a*dhS_dph_re[0]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt_re[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2_re[1]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (4*dhS_dr_re[1]*(-(a4*M) - a4*cos4th*(M - r) + a4*r - 6*a2*M*r2 - 2*a2*cos2th*(a2*(M - r) + (M - 3*r)*r2) + 2*a2*r3 - 4*M*r4 + 4*r5))/a2p2r2pa2c2th3 - (8*a*hS_re[6]*M*(3*a4*M + a4*cos4th*(M - r) - 23*a4*r + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 6*r) + 5*(2*M - r)*r2) - 12*a2*r3 - 24*M*r4 + 16*r5))/(a2p2r2pa2c2th4*a2pr2m2Mr) - (4*dhS_dt_re[0]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) + (hS_re[1]*(10*a8 + a8*cos6th - 24*a6*M2 + 116*a6*M*r - 2*a6*cos6th*M*r + 22*a6*r2 + a6*cos6th*r2 - 248*a4*M2*r2 + 2*a4*cos4th*(3*a4 + 2*r2*(6*M2 - 4*M*r + r2) + a2*(-4*M2 - 10*M*r + 5*r2)) + 208*a4*M*r3 - 4*a4*r4 - 128*a2*M2*r4 + a2*cos2th*(15*a6 - 32*a2*M*(7*M - 8*r)*r2 + a4*(-32*M2 + 98*M*r + 31*r2) - 16*(20*M2 - 12*M*r + r2)*r4) + 128*a2*M*r5 - 48*a2*r6 - 64*M2*r6 + 64*M*r7 - 32*r8))/(a2p2r2pa2c2th4*a2pr2m2Mr) - (16*a2*dhS_dt_re[5]*M*r*sin2th)/a2p2r2pa2c2th3 - (32*a3*hS_re[8]*M*(a2 + a2*cos2th - 4*r2)*sin2th)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (2*a2*costh*dhS_dr_re[2]*sinth)/r2pa2cth22 + (d2hS_dph2_re[1]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (dhS_dph_re[3]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (32*a*dhS_dph_re[5]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth_re[6]*M*r)/(a2p2r2pa2c2th3*tanth) + (dhS_dth_re[1]*(5*a4 - a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 - 4*M*r) + 16*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (4*hS_re[2]*(4*a6*M + 3*a6*r - 34*a4*M*r2 + a4*cos4th*(a2*(-4*M + r) + (-6*M + r)*r2) + 11*a4*r3 + 4*a2*cos2th*r*(a4 + 3*a2*r*(2*M + r) + 2*(2*M + r)*r3) - 48*a2*M*r4 + 16*a2*r5 - 16*M*r6 + 8*r7))/(a2p2r2pa2c2th4*a2pr2m2Mr*tanth);\n  src_re[2] = (-8*a*d2hS_dtph_re[2]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt_re[5]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2_re[2]/r2pa2cth2 + (d2hS_dr2_re[2]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth_re[1]*r)/r2pa2cth22 + (2*a*dhS_dr_re[8]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph_re[5]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2_re[2]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*a3*dhS_dph_re[0]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt_re[3]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (16*a2*dhS_dt_re[7]*M*r*sin2th)/a2p2r2pa2c2th3 - (8*a2*dhS_dt_re[0]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr_re[1]*sin2th)/r2pa2cth22 + (4*a2*hS_re[1]*(-7*a4*M - a4*cos4th*(M - r) + 3*a4*r + 8*a2*M2*r - 4*a2*cos2th*(a2*(2*M - r) + r*(-2*M2 + 3*M*r - 2*r2)) + 28*a2*M*r2 + 8*a2*r3 - 80*M2*r3 + 32*M*r4 + 8*r5)*sin2th)/a2p2r2pa2c2th4 + (d2hS_dph2_re[2]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) + (8*a*hS_re[8]*M*r*(-a2 + 5*a2*cos4th + 4*cos2th*(a2 - r2) + 12*r2))/(a2p2r2pa2c2th4*sinth2) - (hS_re[2]*(15*a6 + 6*a6*cos6th + a6*cos8th + 96*a4*M*r + 16*a4*cos6th*M*r + 56*a4*r2 + 8*a4*cos6th*r2 - 160*a2*M*r3 + 8*a2*cos4th*(2*a4 + a2*r*(-12*M + 5*r) + 2*(-6*M + r)*r3) + 80*a2*r4 - 128*M*r5 + 2*cos2th*(13*a6 + a4*(-8*M*r + 44*r2) + 16*a2*(8*M + 3*r)*r3 + 64*M*r5) + 64*r6))/(4.*a2p2r2pa2c2th4*sinth2) - (8*a2*dhS_dr_re[2]*(-(a2*cos2th*(M - r)) + a2*(-M + r) + 2*(M + r)*r2)*sinth2)/a2p2r2pa2c2th3 - (32*a*dhS_dph_re[7]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth_re[8]*M*r)/(a2p2r2pa2c2th3*tanth) - (32*a*a2pr2m2Mr*hS_re[6]*M*(a2 + a2*cos2th - 10*r2))/(a2p2r2pa2c2th4*tanth) + (dhS_dth_re[2]*(5*a4 - a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 - 4*M*r) + 16*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (dhS_dph_re[3]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth);\n  src_re[3] = (-8*a*d2hS_dtph_re[3]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt_re[6]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2_re[3]/r2pa2cth2 + (d2hS_dr2_re[3]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a*dhS_dr_re[9]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph_re[6]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2_re[3]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*a*hS_re[7]*M*r*(-3*a4 + 10*a2*M*r + 17*a2*r2 + a2*cos4th*(11*a2 - 10*M*r + 11*r2) - 8*M*r3 + 4*cos2th*(2*a4 + a2*r2 + (2*M - r)*r3) + 20*r4))/a2p2r2pa2c2th5 + (2*dhS_dph_re[1]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (4*hS_re[3]*M*(2*a8*M - a8*cos6th*M + 30*a8*r + 3*a8*cos6th*r - 54*a6*M*r2 - 5*a6*cos6th*M*r2 + 90*a6*r3 + 3*a6*cos6th*r3 - 80*a4*M2*r3 - 2*a4*cos4th*(a4*(M - 9*r) + a2*(21*M - 19*r)*r2 - 2*(20*M2 - 12*M*r + 5*r2)*r3) - 96*a4*M*r4 + 76*a4*r5 + 64*a2*M2*r5 + a2*cos2th*(a6*(M + 45*r) + a4*(-91*M + 125*r)*r2 + 16*a2*(-11*M + 6*r)*r4 + 16*(-4*M2 - 5*M*r + r2)*r5) + 16*a2*M*r6 - 16*a2*r7 + 64*M*r8 - 32*r9))/(a2p2r2pa2c2th5*a2pr2m2Mr) - (16*a2*dhS_dt_re[8]*M*r*sin2th)/a2p2r2pa2c2th3 - (16*a*a2pr2m2Mr*hS_re[5]*M*(-3*a4 + a4*cos4th + 26*a2*r2 - 2*a2*cos2th*(a2 + 5*r2) + 20*r4)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2_re[3]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (a*hS_re[9]*M*(3*a6*M + a6*cos8th*(M - r) + 85*a6*r + 124*a6*cos2th*r + 4*a6*cos6th*r - 80*a4*M*r2 - 216*a4*cos2th*M*r2 - 40*a4*cos6th*M*r2 + 280*a4*r3 + 380*a4*cos2th*r3 + 4*a4*cos6th*r3 - 320*a2*M2*r3 - 4*a2*cos4th*(a4*(M - 11*r) + 2*a2*(22*M - 13*r)*r2 - 20*(4*M2 - 3*M*r + r2)*r3) - 208*a2*M*r4 - 576*a2*cos2th*M*r4 + 304*a2*r5 + 384*a2*cos2th*r5 + 256*M2*r5 - 256*cos2th*M2*r5 - 256*M*r6 - 256*cos2th*M*r6 + 64*r7 + 192*cos2th*r7))/(2.*a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (8*a*dhS_dr_re[0]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt_re[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*a2pr2m2Mr*hS_re[4]*M*(3*a4*M + a4*cos4th*(M - r) - 39*a4*r + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 10*r) + (10*M - 9*r)*r2) - 12*a2*r3 - 24*M*r4 + 32*r5)*sinth2)/a2p2r2pa2c2th5 - (8*a*hS_re[0]*M2*(3*a8 - 22*a6*r2 - 20*a4*M*r3 + a4*cos4th*(a4 - 10*a2*r2 + 20*M*r3 - 11*r4) - 41*a4*r4 + 16*a2*M*r5 + 4*a2*cos2th*(a6 - 8*a4*r2 - 15*a2*r4 - 2*(2*M + 3*r)*r5) + 24*a2*r6 + 40*r8)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (32*a3*costh*dhS_dth_re[0]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt_re[2]*M*r*sinth3)/a2p2r2pa2c2th3 - (2*dhS_dth_re[3])/(a2p2r2pa2c2th*tanth) - (32*a*dhS_dph_re[8]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth_re[9]*M*r)/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph_re[2]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth);\n  src_re[4] = (-8*a*d2hS_dtph_re[4]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dr_re[4]*(3*a2*cos2th*(M - r) + a2*(3*M + r) - 2*(M + r)*r2))/a2p2r2pa2c2th2 + d2hS_dth2_re[4]/r2pa2cth2 + (d2hS_dr2_re[4]*(a2 - 2*M*r + r2))/r2pa2cth2 - (4*dhS_dth_re[5]*r)/r2pa2cth22 - (8*a*dhS_dph_re[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (8*a*dhS_dt_re[6]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2_re[4]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (2*hS_re[4]*(7*a6 + 3*a4*M2 + 4*a4*M*r + 10*a4*r2 - 40*a2*M2*r2 + a4*cos4th*(a2 + M2 - 4*M*r + 2*r2) + 4*a2*cos2th*(2*a4 + 2*M*(-5*M + 3*r)*r2 + a2*(M2 + r2)) + 40*a2*M*r3 - 16*a2*r4 - 56*M2*r4 + 64*M*r5 - 16*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr) - (8*dhS_dt_re[1]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*hS_re[3]*M*(3*a6*M - 39*a6*r + a4*cos4th*(M - r)*(a2 - r2) + 101*a4*M*r2 - 45*a4*r3 - 48*a2*M2*r3 + 4*a2*cos2th*(a4*(M - 10*r) + a2*(25*M - 17*r)*r2 + (-12*M2 + 18*M*r - 5*r2)*r3) - 16*a2*M*r4 + 36*a2*r5 + 96*M2*r5 - 136*M*r6 + 48*r7))/(a2p2r2pa2c2th3*a2pr2m2Mr3) - (2*hS_re[0]*M*(3*a8*M - 27*a8*r + 62*a6*M*r2 - 58*a6*r3 - 12*a4*M2*r3 + a4*cos4th*(a4*(M + 3*r) + 2*a2*(-7*M + 3*r)*r2 + (12*M2 - 11*M*r + 3*r2)*r3) + 39*a4*M*r4 - 19*a4*r5 + 48*a2*M2*r5 + 4*a2*cos2th*(a6*(M - 6*r) + a4*(12*M - 19*r)*r2 + a2*(29*M - 20*r)*r4 + (-12*M2 + 22*M*r - 7*r2)*r5) - 56*a2*M*r6 + 28*a2*r7 - 24*M*r8 + 16*r9))/(a2p2r2pa2c2th3*a2pr2m2Mr3) - (4*a2*costh*dhS_dr_re[5]*sinth)/r2pa2cth22 + (d2hS_dph2_re[4]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (2*dhS_dph_re[6]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) + (hS_re[9]*(-2*a6*M2 + a6*cos6th*M2 + 76*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 248*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 14*M*r + 3*r2) + 2*r2*(14*M2 - 12*M*r + 3*r2)) - 16*a4*M*r3 + 192*a2*M3*r3 - a2*cos2th*(a4*(M2 - 50*M*r - 15*r2) - 16*a2*r2*(-12*M2 + 2*M*r + 3*r2) - 16*r3*(12*M3 - 7*M2*r - 5*M*r2 + 3*r3)) + 36*a4*r4 + 240*a2*M2*r4 - 272*a2*M*r5 - 384*M3*r5 + 48*a2*r6 + 512*M2*r6 - 224*M*r7 + 32*r8))/(2.*a2p2r2pa2c2th3*a2pr2m2Mr3*sinth2) + (hS_re[7]*(3*a2*cos2th*M*r + r*(3*a2*M + 2*a2*r - 6*M*r2 + 2*r3) + 2*a4*costh2*sinth2))/(a2pr2m2Mr*r2pa2cth23) - (2*dhS_dth_re[4]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) + (8*hS_re[5]*(a2*(M - 3*r) - a2*cos2th*(M - r) + 2*(2*M - r)*r2))/(a2p2r2pa2c2th2*a2pr2m2Mr*tanth);\n  src_re[5] = (-8*a*d2hS_dtph_re[5]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dr_re[5]*(a2*cos2th*(M - r) + a2*(M + r) - 2*M*r2))/a2p2r2pa2c2th2 + d2hS_dth2_re[5]/r2pa2cth2 + (d2hS_dr2_re[5]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth_re[4]*r)/r2pa2cth22 - (2*dhS_dth_re[7]*r)/r2pa2cth22 - (4*a*dhS_dph_re[2]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt_re[8]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2_re[5]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dt_re[2]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (8*a3*dhS_dph_re[1]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt_re[6]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (12*a2*hS_re[4]*(-M + r)*sin2th)/a2p2r2pa2c2th2 - (8*a2*dhS_dt_re[1]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr_re[4]*sin2th)/r2pa2cth22 - (4*a2*hS_re[0]*M*(3*a6 - 4*a4*r*(M + 3*r) + a2*(32*M - 33*r)*r3 + a2*cos2th*(3*a4 + a2*(-4*M*r + 6*r2) + (-8*M + 3*r)*r3) + 2*(16*M - 9*r)*r5)*sin2th)/(a2p2r2pa2c2th3*a2pr2m2Mr2) - (2*a2*costh*dhS_dr_re[7]*sinth)/r2pa2cth22 + (d2hS_dph2_re[5]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS_re[5]*(a4 + 3*a4*cos4th + a4*cos6th + 8*a2*r2 - 32*M*r3 + cos2th*(3*a4 + 8*a2*r2 + 16*(2*M - r)*r3) + 24*r4))/(a2p2r2pa2c2th3*sinth2) - (dhS_dph_re[8]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (8*hS_re[7]*r)/(a2p2r2pa2c2th2*tanth) - (2*dhS_dth_re[5]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) + (8*a*hS_re[3]*M*(-3*a6 + 5*a4*M*r + a4*cos4th*(a2 - M*r) + 20*a4*r2 - 48*a2*M*r3 - 2*a2*cos2th*(a4 - 2*a2*(M - 4*r)*r + (-16*M + 9*r)*r3) + 38*a2*r4 - 24*M*r5 + 12*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr2*tanth) - (dhS_dph_re[6]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth) + (hS_re[9]*(-10*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 16*a4*M2*r + 92*a4*M*r2 + 36*a4*r3 - 256*a2*M2*r3 + 2*a4*cos4th*(5*a2*M + 3*a2*r - 8*M2*r - 6*M*r2 + 6*r3) + a2*cos2th*(a4*(M + 15*r) + 16*a2*(-13*M + 3*r)*r2 + 48*(8*M2 - 6*M*r + r2)*r3) + 32*a2*M*r4 + 48*a2*r5 + 128*M2*r5 - 128*M*r6 + 32*r7))/(2.*a2p2r2pa2c2th3*a2pr2m2Mr2*sinth2*tanth);\n  src_re[6] = (-8*a*d2hS_dtph_re[6]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2_re[6]/r2pa2cth2 + (d2hS_dr2_re[6]*(a2 - 2*M*r + r2))/r2pa2cth2 - (2*dhS_dth_re[8]*r)/r2pa2cth22 - (4*a*dhS_dph_re[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt_re[9]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2_re[6]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (2*dhS_dr_re[6]*(7*a4*M + a4*cos4th*(M - r) + a4*r - 4*a2*M*r2 + 4*a2*cos2th*(2*a2*M + (M - r)*r2) + 4*a2*r3 - 16*M*r4))/a2p2r2pa2c2th3 + (2*dhS_dph_re[4]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (4*dhS_dt_re[3]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (2*hS_re[6]*(-2*a6*M2 + a6*cos6th*M2 + 24*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 40*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 12*M*r + 3*r2) + 2*r2*(10*M2 - 9*M*r + 3*r2)) - 44*a4*M*r3 + 36*a4*r4 + 112*a2*M2*r4 - a2*cos2th*(a4*(M2 - 2*M*r - 15*r2) + 48*a2*(M - r)*r3 - 16*(M2 - 5*M*r + 3*r2)*r4) - 176*a2*M*r5 + 48*a2*r6 + 128*M2*r6 - 128*M*r7 + 32*r8))/(a2p2r2pa2c2th4*a2pr2m2Mr) + (4*a*hS_re[2]*M*(5*a6 - 21*a4*r2 + cos4th*(-a6 + a4*r2) - 56*a2*r4 + 4*a2*cos2th*(a4 + 3*a2*r2 + 4*r4) - 24*r6)*sin2th)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (2*a2*costh*dhS_dr_re[8]*sinth)/r2pa2cth22 + (d2hS_dph2_re[6]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (dhS_dph_re[9]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (8*a*dhS_dr_re[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt_re[4]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*hS_re[1]*M*(3*a6*M - 20*a6*r + 31*a4*M*r2 - 26*a4*r3 + a4*cos4th*(a2*M - 3*M*r2 + 2*r3) + 4*a2*cos2th*(a4*(M - 5*r) + a2*(7*M - 8*r)*r2 + (6*M - r)*r4) + 12*a2*r5 - 24*M*r6 + 24*r7)*sinth2)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (32*a3*costh*dhS_dth_re[1]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt_re[5]*M*r*sinth3)/a2p2r2pa2c2th3 - (dhS_dth_re[6]*(a4 + 3*a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 + 4*r*(-M + r)) + 8*r4))/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph_re[5]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (2*hS_re[8]*(-6*a6*M - a6*cos6th*M + 4*a6*r + a6*cos6th*r + 52*a4*M*r2 + 2*a4*cos4th*(a2*(3*M + 2*r) + (-2*M + 5*r)*r2) + 14*a4*r3 + 48*a2*M*r4 + a2*cos2th*(a4*(M + 7*r) + 8*a2*(-10*M + 3*r)*r2 + 16*(-7*M + 2*r)*r4) + 16*a2*r5 - 32*M*r6 + 16*r7))/(a2p2r2pa2c2th4*a2pr2m2Mr*tanth);\n  src_re[7] = (-8*a*d2hS_dtph_re[7]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dr_re[7]*(a2*cos2th*(M - r) + a2*(M + 3*r) + 2*(-3*M + r)*r2))/a2p2r2pa2c2th2 + d2hS_dth2_re[7]/r2pa2cth2 + (d2hS_dr2_re[7]*(a2 - 2*M*r + r2))/r2pa2cth2 + (4*a2pr2m2Mr*dhS_dth_re[5]*r)/r2pa2cth22 - (d2hS_dt2_re[7]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (8*a*hS_re[3]*M*r*(-15*a4 + a4*cos4th + 16*a2*M*r - 2*a2*cos2th*(7*a2 + r*(-8*M + 7*r)) - 2*a2*r2 - 16*M*r3 + 12*r4))/(a2p2r2pa2c2th3*a2pr2m2Mr) + (4*hS_re[0]*M*r*(-9*a6 + 4*a4*M*r - 7*a4*r2 + a4*cos4th*(3*a2 - 4*M*r + 3*r2) - 8*a2*M*r3 - 2*a2*cos2th*(3*a4 + 8*a2*r2 + (-4*M + 5*r)*r3) + 6*a2*r4 + 4*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr) - (16*a3*dhS_dph_re[2]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (16*a3*dhS_dt_re[8]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (16*a2*hS_re[5]*(-M + r)*sin2th)/a2p2r2pa2c2th2 - (16*a2*dhS_dt_re[2]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (2*a2*a2pr2m2Mr*dhS_dr_re[5]*sin2th)/r2pa2cth22 + (d2hS_dph2_re[7]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS_re[7]*(6*a4 + a4*cos6th + 8*a2*r2 + 2*a2*cos4th*(5*a2 + 4*r2) + 32*M*r3 + cos2th*(15*a4 + 48*a2*r2 + 16*(-2*M + r)*r3) + 16*r4))/(2.*a2p2r2pa2c2th3*sinth2) + (a2pr2m2Mr*hS_re[4]*(3*a2*cos2th*M*r + r*(3*a2*M + 2*a2*r - 6*M*r2 + 2*r3) + 2*a4*costh2*sinth2))/r2pa2cth23 - (2*dhS_dth_re[7]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) - (2*dhS_dph_re[8]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth) + (hS_re[9]*(35*a6 + 8*a6*cos6th + a6*cos8th - 168*a4*M*r - 36*a4*cos6th*M*r + 120*a4*r2 + 12*a4*cos6th*r2 + 128*a2*M2*r2 + 4*a2*cos4th*(7*a4 + 2*a2*r*(5*M + 9*r) - 4*(8*M2 + M*r - 3*r2)*r2) - 48*a2*M*r3 + 144*a2*r4 - 256*M2*r4 + 64*r6 + 4*cos2th*(14*a6 + a4*r*(-23*M + 45*r) - 16*a2*(7*M - 3*r)*r3 + 16*r4*pow(-2*M + r,2))))/(8.*a2p2r2pa2c2th3*a2pr2m2Mr*sinth4);\n  src_re[8] = (-8*a*d2hS_dtph_re[8]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2_re[8]/r2pa2cth2 + (d2hS_dr2_re[8]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth_re[6]*r)/r2pa2cth22 - (d2hS_dt2_re[8]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dr_re[8]*(a4*(M + r) + 3*a2*(-M + r)*r2 + a2*cos2th*(a2*(M + r) + (-M + r)*r2) + 2*(-3*M + r)*r4))/a2p2r2pa2c2th3 + (2*dhS_dph_re[5]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (8*a3*dhS_dph_re[3]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt_re[9]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a2*dhS_dt_re[3]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr_re[6]*sin2th)/r2pa2cth22 + (d2hS_dph2_re[8]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS_re[8]*(55*a6 + 10*a6*cos6th + a6*cos8th - 80*a4*M*r - 24*a4*cos6th*M*r + 184*a4*r2 + 16*a4*cos6th*r2 + 224*a2*M*r3 + 8*a2*cos4th*(5*a4 + a2*r*(10*M + 13*r) + 2*(2*M + 5*r)*r3) + 208*a2*r4 + 256*M*r5 + 2*cos2th*(43*a6 + 4*a4*r*(3*M + 34*r) - 16*a2*(8*M - 9*r)*r3 + 64*(-2*M + r)*r5) + 64*r6))/(4.*a2p2r2pa2c2th4*sinth2) - (8*a*dhS_dr_re[2]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt_re[5]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (32*a*hS_re[2]*M*r*(4*a4 - a2*r2 + a2*cos2th*(4*a2 + 3*r2) - 6*r4)*sinth2)/a2p2r2pa2c2th4 - (32*a3*costh*dhS_dth_re[2]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt_re[7]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a2pr2m2Mr*a3*costh*hS_re[1]*M*(a2 + a2*cos2th - 10*r2)*sinth3)/a2p2r2pa2c2th4 - (dhS_dth_re[8]*(a4 + 3*a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 + 4*r*(-M + r)) + 8*r4))/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph_re[7]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (2*hS_re[6]*(-2*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 8*a4*M2*r + 60*a4*M*r2 + 2*a4*cos4th*(a2*(M + 3*r) + 2*r*(-2*M2 - 3*M*r + 3*r2)) + 36*a4*r3 - 160*a2*M2*r3 + a2*cos2th*(a4*(M + 15*r) - 16*a2*(7*M - 3*r)*r2 + 16*(10*M2 - 10*M*r + 3*r2)*r3) + 32*a2*M*r4 + 48*a2*r5 - 64*M*r6 + 32*r7))/(a2p2r2pa2c2th4*tanth) - (dhS_dph_re[9]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth);\n  src_re[9] = (-8*a*d2hS_dtph_re[9]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2_re[9]/r2pa2cth2 + (d2hS_dr2_re[9]*(a2 - 2*M*r + r2))/r2pa2cth2 - (d2hS_dt2_re[9]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (2*dhS_dr_re[9]*(5*a4*M - a4*cos4th*(M - r) + 3*a4*r - 16*a2*M*r2 + 8*a2*r3 + 4*a2*cos2th*(a2*(M + r) + 2*r3) - 24*M*r4 + 8*r5))/a2p2r2pa2c2th3 + (4*dhS_dph_re[6]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 + (hS_re[7]*(35*a8 + 8*a8*cos6th + a8*cos8th - 78*a6*M*r - 36*a6*cos6th*M*r - 2*a6*cos8th*M*r + 155*a6*r2 + 20*a6*cos6th*r2 + a6*cos8th*r2 + 80*a4*M2*r2 + 40*a4*cos6th*M2*r2 + 88*a4*M*r3 - 44*a4*cos6th*M*r3 + 264*a4*r4 + 12*a4*cos6th*r4 - 96*a2*M2*r4 + 4*a2*cos4th*(7*a6 + 5*a4*r*(4*M + 5*r) + 4*(-2*M2 + M*r + 3*r2)*r4 + a2*(-20*M2*r2 + 26*M*r3 + 30*r4)) + 304*a2*M*r5 + 208*a2*r6 + 128*M*r7 + 4*cos2th*(14*a8 + a6*r*(9*M + 59*r) + a4*r2*(-10*M2 - 37*M*r + 93*r2) + 16*a2*(2*M2 - 5*M*r + 4*r2)*r4 + 16*(-2*M + r)*r7) + 64*r8))/(2.*a2p2r2pa2c2th5) + (2*a2pr2m2Mr*hS_re[5]*(-10*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 140*a4*M*r2 + 2*a4*cos4th*(a2*(5*M + 3*r) + 2*(M + 3*r)*r2) + 36*a4*r3 + 160*a2*M*r4 + a2*cos2th*(a4*(M + 15*r) - 48*a2*(3*M - r)*r2 + 16*(-10*M + 3*r)*r4) + 48*a2*r5 + 32*r7)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2_re[9]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) + (hS_re[9]*(126*a10 + a10*cos10th + 45*a10*cos6th + 10*a10*cos8th + 6*a8*M2 - a8*cos10th*M2 + 3*a8*cos6th*M2 + 2*a8*cos8th*M2 - 160*a8*M*r - 160*a8*cos6th*M*r - 32*a8*cos8th*M*r + 512*r10 + 700*a8*r2 + 160*a8*cos6th*r2 + 20*a8*cos8th*r2 - 120*a6*M2*r2 + 160*a6*cos6th*M2*r2 + 24*a6*cos8th*M2*r2 - 840*a6*M*r3 - 384*a6*cos6th*M*r3 - 24*a6*cos8th*M*r3 - 640*a4*M3*r3 - 320*a4*cos6th*M3*r3 + 1600*a6*r4 + 160*a6*cos6th*r4 - 96*a4*M2*r4 + 240*a4*cos6th*M2*r4 - 1856*a4*M*r5 - 160*a4*cos6th*M*r5 + 768*a2*M3*r5 + 8*a2*cos4th*(15*a8 - a6*(M2 + 40*M*r - 70*r2) + 4*a4*r2*(3*M2 - 37*M*r + 30*r2) + 4*a2*r3*(20*M3 + 11*M2*r - 38*M*r2 + 20*r3) + 16*M*(2*M2 + 2*M*r - r2)*r5) + 1920*a4*r6 + 768*a2*M2*r6 - 2432*a2*M*r7 + 1280*a2*r8 + 1024*M2*r8 + 2*cos2th*(105*a10 - a8*(M2 + 176*M*r - 560*r2) + 16*a6*r2*(-5*M2 - 52*M*r + 75*r2) + 8*a4*r3*(20*M3 - 31*M2*r - 182*M*r2 + 160*r3) - 128*a2*(4*M3 + 4*M2*r + 6*M*r2 - 5*r3)*r5 + 256*M*(-2*M + r)*r8) - 1536*M*r9))/(8.*a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (16*a*dhS_dr_re[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (16*a*dhS_dt_re[6]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (2*a2pr2m2Mr*hS_re[4]*(-2*a6*M2 + a6*cos6th*M2 + 76*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 40*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 14*M*r + 3*r2) + 2*r2*(10*M2 - 8*M*r + 3*r2)) + 32*a4*M*r3 + 36*a4*r4 + 48*a2*M2*r4 - a2*cos2th*(a4*(M2 - 50*M*r - 15*r2) - 48*a2*(2*M + r)*r3 - 48*(-M2 + M*r + r2)*r4) - 144*a2*M*r5 + 48*a2*r6 - 96*M*r7 + 32*r8)*sinth2)/a2p2r2pa2c2th5 + (4*a*hS_re[3]*M*(2*a8*M - a8*cos6th*M + 30*a8*r + 3*a8*cos6th*r - 130*a6*M*r2 - 3*a6*cos6th*M*r2 + 70*a6*r3 + a6*cos6th*r3 - 80*a4*M2*r3 - 2*a4*cos4th*(a4*(M - 9*r) + a2*(31*M - 13*r)*r2 + 2*(-20*M2 + 14*M*r + r2)*r3) - 184*a4*M*r4 + 4*a4*r5 + 64*a2*M2*r5 + a2*cos2th*(a6*(M + 45*r) + a4*(-189*M + 95*r)*r2 - 272*a2*M*r4 - 16*(4*M2 + 3*M*r + 5*r2)*r5) + 176*a2*M*r6 - 112*a2*r7 + 256*M*r8 - 96*r9)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (2*hS_re[0]*M*(-2*a10*M + a10*cos6th*M + 30*a10*r + 3*a10*cos6th*r - 32*r11 + 28*a8*M*r2 - 18*a8*cos6th*M*r2 + 120*a8*r3 + 6*a8*cos6th*r3 + 40*a6*M2*r3 + 20*a6*cos6th*M2*r3 + 46*a6*M*r4 - 15*a6*cos6th*M*r4 + 166*a6*r5 + 3*a6*cos6th*r5 - 48*a4*M2*r5 + 2*a4*cos4th*(a6*(M + 9*r) - 14*a4*(M - 2*r)*r2 + a2*(-20*M2 - 31*M*r + 29*r2)*r3 + 2*(-4*M2 - 2*M*r + 5*r2)*r5) - 152*a4*M*r6 + 60*a4*r7 - 208*a2*M*r8 + a2*cos2th*(-(a8*(M - 45*r)) + 2*a6*(9*M + 85*r)*r2 + a4*(-20*M2 + 31*M*r + 221*r2)*r3 + 16*a2*(4*M2 + 10*M*r + 7*r2)*r5 + 16*(13*M + r)*r8) - 48*a2*r9)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (64*a3*costh*dhS_dth_re[3]*M*r*sinth3)/a2p2r2pa2c2th3 + (64*a3*costh*dhS_dt_re[8]*M*r*sinth3)/a2p2r2pa2c2th3 + (4*dhS_dph_re[8]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (dhS_dth_re[9]*(9*a4 + 3*a4*cos4th + 32*a2*M*r + 24*a2*r2 + 4*a2*cos2th*(3*a2 - 8*M*r + 6*r2) + 24*r4))/(a2p2r2pa2c2th3*tanth);\n\n  src_im[0] = (-8*a*d2hS_dtph_im[0]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (16*dhS_dt_im[1]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2_im[0]/r2pa2cth2 + (d2hS_dr2_im[0]*(a2 - 2*M*r + r2))/r2pa2cth2 + (4*a*dhS_dr_im[3]*M*(a2*costh2 - r2))/r2pa2cth23 + (4*a*dhS_dph_im[1]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2_im[0]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (16*hS_im[7]*M*r*(-9*a4 + 3*a4*cos4th + 20*a2*M*r - 2*a2*cos2th*(3*a2 + 5*r*(-2*M + r)) + 2*a2*r2 - 8*M*r3 + 4*r4))/a2p2r2pa2c2th5 + (2*dhS_dr_im[0]*(5*a4*M - a4*cos4th*(M - r) + 3*a4*r - 16*a2*M*r2 + 8*a2*r3 + 4*a2*cos2th*(a2*(M + r) + 2*r3) - 24*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (8*a2pr2m2Mr*hS_im[4]*M*(3*a4*M - 27*a4*r + a4*cos4th*(M + 3*r) + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 6*r) + (10*M - 7*r)*r2) - 4*a2*r3 - 24*M*r4 + 16*r5))/a2p2r2pa2c2th5 + (16*a*hS_im[3]*M2*(3*a6 + 29*a4*r2 + a4*cos4th*(a2 + 7*r2) - 80*a2*M*r3 + 32*a2*r4 + 4*a2*cos2th*(a4 + 9*a2*r2 - 20*M*r3 + 6*r4) + 32*M*r5 - 8*r6))/(a2p2r2pa2c2th5*a2pr2m2Mr) + (8*hS_im[0]*M2*(3*a8 + 2*a6*r2 - 20*a4*M*r3 + a4*cos4th*(a4 - 2*a2*r2 + 20*M*r3 - 3*r4) - a4*r4 + 16*a2*M*r5 + 4*a2*cos2th*(a6 - 3*a2*r4 - 2*(2*M + r)*r5) + 8*a2*r6 + 8*r8))/(a2p2r2pa2c2th5*a2pr2m2Mr) - (32*a2*dhS_dt_im[2]*M*r*sin2th)/a2p2r2pa2c2th3 - (96*a2*a2pr2m2Mr*hS_im[5]*M*(a2 + a2*cos2th - 6*r2)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2_im[0]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (2*hS_im[9]*M*(-2*a6*M + a6*cos6th*M + 30*a6*r + 3*a6*cos6th*r - 200*a4*M*r2 + 2*a4*cos4th*(a2*(M + 9*r) + 2*(-14*M + 5*r)*r2) + 60*a4*r3 + 320*a2*M2*r3 - a2*cos2th*(a4*(M - 45*r) + 16*a2*(16*M - 5*r)*r2 - 16*(20*M2 - 11*M*r + r2)*r3) - 208*a2*M*r4 + 16*a2*r5 - 128*M2*r5 + 128*M*r6 - 32*r7))/(a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (64*a*dhS_dph_im[2]*M*r)/(a2p2r2pa2c2th3*tanth) + (64*a*dhS_dth_im[3]*M*r)/(a2p2r2pa2c2th3*tanth) + (dhS_dth_im[0]*(3*a4 + a4*cos4th + 32*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-4*M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth);\n  src_im[1] = (-8*a*d2hS_dtph_im[1]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt_im[4]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2_im[1]/r2pa2cth2 + (d2hS_dr2_im[1]*(a2 - 2*M*r + r2))/r2pa2cth2 - (2*dhS_dth_im[2]*r)/r2pa2cth22 + (2*a*dhS_dr_im[6]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph_im[4]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (4*a*dhS_dph_im[0]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt_im[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2_im[1]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (4*dhS_dr_im[1]*(-(a4*M) - a4*cos4th*(M - r) + a4*r - 6*a2*M*r2 - 2*a2*cos2th*(a2*(M - r) + (M - 3*r)*r2) + 2*a2*r3 - 4*M*r4 + 4*r5))/a2p2r2pa2c2th3 - (8*a*hS_im[6]*M*(3*a4*M + a4*cos4th*(M - r) - 23*a4*r + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 6*r) + 5*(2*M - r)*r2) - 12*a2*r3 - 24*M*r4 + 16*r5))/(a2p2r2pa2c2th4*a2pr2m2Mr) - (4*dhS_dt_im[0]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) + (hS_im[1]*(10*a8 + a8*cos6th - 24*a6*M2 + 116*a6*M*r - 2*a6*cos6th*M*r + 22*a6*r2 + a6*cos6th*r2 - 248*a4*M2*r2 + 2*a4*cos4th*(3*a4 + 2*r2*(6*M2 - 4*M*r + r2) + a2*(-4*M2 - 10*M*r + 5*r2)) + 208*a4*M*r3 - 4*a4*r4 - 128*a2*M2*r4 + a2*cos2th*(15*a6 - 32*a2*M*(7*M - 8*r)*r2 + a4*(-32*M2 + 98*M*r + 31*r2) - 16*(20*M2 - 12*M*r + r2)*r4) + 128*a2*M*r5 - 48*a2*r6 - 64*M2*r6 + 64*M*r7 - 32*r8))/(a2p2r2pa2c2th4*a2pr2m2Mr) - (16*a2*dhS_dt_im[5]*M*r*sin2th)/a2p2r2pa2c2th3 - (32*a3*hS_im[8]*M*(a2 + a2*cos2th - 4*r2)*sin2th)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (2*a2*costh*dhS_dr_im[2]*sinth)/r2pa2cth22 + (d2hS_dph2_im[1]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (dhS_dph_im[3]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (32*a*dhS_dph_im[5]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth_im[6]*M*r)/(a2p2r2pa2c2th3*tanth) + (dhS_dth_im[1]*(5*a4 - a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 - 4*M*r) + 16*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (4*hS_im[2]*(4*a6*M + 3*a6*r - 34*a4*M*r2 + a4*cos4th*(a2*(-4*M + r) + (-6*M + r)*r2) + 11*a4*r3 + 4*a2*cos2th*r*(a4 + 3*a2*r*(2*M + r) + 2*(2*M + r)*r3) - 48*a2*M*r4 + 16*a2*r5 - 16*M*r6 + 8*r7))/(a2p2r2pa2c2th4*a2pr2m2Mr*tanth);\n  src_im[2] = (-8*a*d2hS_dtph_im[2]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt_im[5]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2_im[2]/r2pa2cth2 + (d2hS_dr2_im[2]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth_im[1]*r)/r2pa2cth22 + (2*a*dhS_dr_im[8]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph_im[5]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2_im[2]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*a3*dhS_dph_im[0]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt_im[3]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (16*a2*dhS_dt_im[7]*M*r*sin2th)/a2p2r2pa2c2th3 - (8*a2*dhS_dt_im[0]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr_im[1]*sin2th)/r2pa2cth22 + (4*a2*hS_im[1]*(-7*a4*M - a4*cos4th*(M - r) + 3*a4*r + 8*a2*M2*r - 4*a2*cos2th*(a2*(2*M - r) + r*(-2*M2 + 3*M*r - 2*r2)) + 28*a2*M*r2 + 8*a2*r3 - 80*M2*r3 + 32*M*r4 + 8*r5)*sin2th)/a2p2r2pa2c2th4 + (d2hS_dph2_im[2]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) + (8*a*hS_im[8]*M*r*(-a2 + 5*a2*cos4th + 4*cos2th*(a2 - r2) + 12*r2))/(a2p2r2pa2c2th4*sinth2) - (hS_im[2]*(15*a6 + 6*a6*cos6th + a6*cos8th + 96*a4*M*r + 16*a4*cos6th*M*r + 56*a4*r2 + 8*a4*cos6th*r2 - 160*a2*M*r3 + 8*a2*cos4th*(2*a4 + a2*r*(-12*M + 5*r) + 2*(-6*M + r)*r3) + 80*a2*r4 - 128*M*r5 + 2*cos2th*(13*a6 + a4*(-8*M*r + 44*r2) + 16*a2*(8*M + 3*r)*r3 + 64*M*r5) + 64*r6))/(4.*a2p2r2pa2c2th4*sinth2) - (8*a2*dhS_dr_im[2]*(-(a2*cos2th*(M - r)) + a2*(-M + r) + 2*(M + r)*r2)*sinth2)/a2p2r2pa2c2th3 - (32*a*dhS_dph_im[7]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth_im[8]*M*r)/(a2p2r2pa2c2th3*tanth) - (32*a*a2pr2m2Mr*hS_im[6]*M*(a2 + a2*cos2th - 10*r2))/(a2p2r2pa2c2th4*tanth) + (dhS_dth_im[2]*(5*a4 - a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 - 4*M*r) + 16*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (dhS_dph_im[3]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth);\n  src_im[3] = (-8*a*d2hS_dtph_im[3]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dt_im[6]*M*(a2 + a2*cos2th - 2*r2)*(a2 + r2))/a2p2r2pa2c2th3 + d2hS_dth2_im[3]/r2pa2cth2 + (d2hS_dr2_im[3]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a*dhS_dr_im[9]*M*(a2*costh2 - r2))/r2pa2cth23 + (2*a*dhS_dph_im[6]*M*(-(a2*costh2) + r2))/r2pa2cth23 - (d2hS_dt2_im[3]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*a*hS_im[7]*M*r*(-3*a4 + 10*a2*M*r + 17*a2*r2 + a2*cos4th*(11*a2 - 10*M*r + 11*r2) - 8*M*r3 + 4*cos2th*(2*a4 + a2*r2 + (2*M - r)*r3) + 20*r4))/a2p2r2pa2c2th5 + (2*dhS_dph_im[1]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (4*hS_im[3]*M*(2*a8*M - a8*cos6th*M + 30*a8*r + 3*a8*cos6th*r - 54*a6*M*r2 - 5*a6*cos6th*M*r2 + 90*a6*r3 + 3*a6*cos6th*r3 - 80*a4*M2*r3 - 2*a4*cos4th*(a4*(M - 9*r) + a2*(21*M - 19*r)*r2 - 2*(20*M2 - 12*M*r + 5*r2)*r3) - 96*a4*M*r4 + 76*a4*r5 + 64*a2*M2*r5 + a2*cos2th*(a6*(M + 45*r) + a4*(-91*M + 125*r)*r2 + 16*a2*(-11*M + 6*r)*r4 + 16*(-4*M2 - 5*M*r + r2)*r5) + 16*a2*M*r6 - 16*a2*r7 + 64*M*r8 - 32*r9))/(a2p2r2pa2c2th5*a2pr2m2Mr) - (16*a2*dhS_dt_im[8]*M*r*sin2th)/a2p2r2pa2c2th3 - (16*a*a2pr2m2Mr*hS_im[5]*M*(-3*a4 + a4*cos4th + 26*a2*r2 - 2*a2*cos2th*(a2 + 5*r2) + 20*r4)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2_im[3]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (a*hS_im[9]*M*(3*a6*M + a6*cos8th*(M - r) + 85*a6*r + 124*a6*cos2th*r + 4*a6*cos6th*r - 80*a4*M*r2 - 216*a4*cos2th*M*r2 - 40*a4*cos6th*M*r2 + 280*a4*r3 + 380*a4*cos2th*r3 + 4*a4*cos6th*r3 - 320*a2*M2*r3 - 4*a2*cos4th*(a4*(M - 11*r) + 2*a2*(22*M - 13*r)*r2 - 20*(4*M2 - 3*M*r + r2)*r3) - 208*a2*M*r4 - 576*a2*cos2th*M*r4 + 304*a2*r5 + 384*a2*cos2th*r5 + 256*M2*r5 - 256*cos2th*M2*r5 - 256*M*r6 - 256*cos2th*M*r6 + 64*r7 + 192*cos2th*r7))/(2.*a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (8*a*dhS_dr_im[0]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt_im[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*a2pr2m2Mr*hS_im[4]*M*(3*a4*M + a4*cos4th*(M - r) - 39*a4*r + 40*a2*M*r2 + 4*a2*cos2th*(a2*(M - 10*r) + (10*M - 9*r)*r2) - 12*a2*r3 - 24*M*r4 + 32*r5)*sinth2)/a2p2r2pa2c2th5 - (8*a*hS_im[0]*M2*(3*a8 - 22*a6*r2 - 20*a4*M*r3 + a4*cos4th*(a4 - 10*a2*r2 + 20*M*r3 - 11*r4) - 41*a4*r4 + 16*a2*M*r5 + 4*a2*cos2th*(a6 - 8*a4*r2 - 15*a2*r4 - 2*(2*M + 3*r)*r5) + 24*a2*r6 + 40*r8)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (32*a3*costh*dhS_dth_im[0]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt_im[2]*M*r*sinth3)/a2p2r2pa2c2th3 - (2*dhS_dth_im[3])/(a2p2r2pa2c2th*tanth) - (32*a*dhS_dph_im[8]*M*r)/(a2p2r2pa2c2th3*tanth) + (32*a*dhS_dth_im[9]*M*r)/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph_im[2]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth);\n  src_im[4] = (-8*a*d2hS_dtph_im[4]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dr_im[4]*(3*a2*cos2th*(M - r) + a2*(3*M + r) - 2*(M + r)*r2))/a2p2r2pa2c2th2 + d2hS_dth2_im[4]/r2pa2cth2 + (d2hS_dr2_im[4]*(a2 - 2*M*r + r2))/r2pa2cth2 - (4*dhS_dth_im[5]*r)/r2pa2cth22 - (8*a*dhS_dph_im[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (8*a*dhS_dt_im[6]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2_im[4]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (2*hS_im[4]*(7*a6 + 3*a4*M2 + 4*a4*M*r + 10*a4*r2 - 40*a2*M2*r2 + a4*cos4th*(a2 + M2 - 4*M*r + 2*r2) + 4*a2*cos2th*(2*a4 + 2*M*(-5*M + 3*r)*r2 + a2*(M2 + r2)) + 40*a2*M*r3 - 16*a2*r4 - 56*M2*r4 + 64*M*r5 - 16*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr) - (8*dhS_dt_im[1]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*hS_im[3]*M*(3*a6*M - 39*a6*r + a4*cos4th*(M - r)*(a2 - r2) + 101*a4*M*r2 - 45*a4*r3 - 48*a2*M2*r3 + 4*a2*cos2th*(a4*(M - 10*r) + a2*(25*M - 17*r)*r2 + (-12*M2 + 18*M*r - 5*r2)*r3) - 16*a2*M*r4 + 36*a2*r5 + 96*M2*r5 - 136*M*r6 + 48*r7))/(a2p2r2pa2c2th3*a2pr2m2Mr3) - (2*hS_im[0]*M*(3*a8*M - 27*a8*r + 62*a6*M*r2 - 58*a6*r3 - 12*a4*M2*r3 + a4*cos4th*(a4*(M + 3*r) + 2*a2*(-7*M + 3*r)*r2 + (12*M2 - 11*M*r + 3*r2)*r3) + 39*a4*M*r4 - 19*a4*r5 + 48*a2*M2*r5 + 4*a2*cos2th*(a6*(M - 6*r) + a4*(12*M - 19*r)*r2 + a2*(29*M - 20*r)*r4 + (-12*M2 + 22*M*r - 7*r2)*r5) - 56*a2*M*r6 + 28*a2*r7 - 24*M*r8 + 16*r9))/(a2p2r2pa2c2th3*a2pr2m2Mr3) - (4*a2*costh*dhS_dr_im[5]*sinth)/r2pa2cth22 + (d2hS_dph2_im[4]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (2*dhS_dph_im[6]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) + (hS_im[9]*(-2*a6*M2 + a6*cos6th*M2 + 76*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 248*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 14*M*r + 3*r2) + 2*r2*(14*M2 - 12*M*r + 3*r2)) - 16*a4*M*r3 + 192*a2*M3*r3 - a2*cos2th*(a4*(M2 - 50*M*r - 15*r2) - 16*a2*r2*(-12*M2 + 2*M*r + 3*r2) - 16*r3*(12*M3 - 7*M2*r - 5*M*r2 + 3*r3)) + 36*a4*r4 + 240*a2*M2*r4 - 272*a2*M*r5 - 384*M3*r5 + 48*a2*r6 + 512*M2*r6 - 224*M*r7 + 32*r8))/(2.*a2p2r2pa2c2th3*a2pr2m2Mr3*sinth2) + (hS_im[7]*(3*a2*cos2th*M*r + r*(3*a2*M + 2*a2*r - 6*M*r2 + 2*r3) + 2*a4*costh2*sinth2))/(a2pr2m2Mr*r2pa2cth23) - (2*dhS_dth_im[4]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) + (8*hS_im[5]*(a2*(M - 3*r) - a2*cos2th*(M - r) + 2*(2*M - r)*r2))/(a2p2r2pa2c2th2*a2pr2m2Mr*tanth);\n  src_im[5] = (-8*a*d2hS_dtph_im[5]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dr_im[5]*(a2*cos2th*(M - r) + a2*(M + r) - 2*M*r2))/a2p2r2pa2c2th2 + d2hS_dth2_im[5]/r2pa2cth2 + (d2hS_dr2_im[5]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth_im[4]*r)/r2pa2cth22 - (2*dhS_dth_im[7]*r)/r2pa2cth22 - (4*a*dhS_dph_im[2]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt_im[8]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2_im[5]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dt_im[2]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (8*a3*dhS_dph_im[1]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt_im[6]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (12*a2*hS_im[4]*(-M + r)*sin2th)/a2p2r2pa2c2th2 - (8*a2*dhS_dt_im[1]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr_im[4]*sin2th)/r2pa2cth22 - (4*a2*hS_im[0]*M*(3*a6 - 4*a4*r*(M + 3*r) + a2*(32*M - 33*r)*r3 + a2*cos2th*(3*a4 + a2*(-4*M*r + 6*r2) + (-8*M + 3*r)*r3) + 2*(16*M - 9*r)*r5)*sin2th)/(a2p2r2pa2c2th3*a2pr2m2Mr2) - (2*a2*costh*dhS_dr_im[7]*sinth)/r2pa2cth22 + (d2hS_dph2_im[5]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS_im[5]*(a4 + 3*a4*cos4th + a4*cos6th + 8*a2*r2 - 32*M*r3 + cos2th*(3*a4 + 8*a2*r2 + 16*(2*M - r)*r3) + 24*r4))/(a2p2r2pa2c2th3*sinth2) - (dhS_dph_im[8]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (8*hS_im[7]*r)/(a2p2r2pa2c2th2*tanth) - (2*dhS_dth_im[5]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) + (8*a*hS_im[3]*M*(-3*a6 + 5*a4*M*r + a4*cos4th*(a2 - M*r) + 20*a4*r2 - 48*a2*M*r3 - 2*a2*cos2th*(a4 - 2*a2*(M - 4*r)*r + (-16*M + 9*r)*r3) + 38*a2*r4 - 24*M*r5 + 12*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr2*tanth) - (dhS_dph_im[6]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth) + (hS_im[9]*(-10*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 16*a4*M2*r + 92*a4*M*r2 + 36*a4*r3 - 256*a2*M2*r3 + 2*a4*cos4th*(5*a2*M + 3*a2*r - 8*M2*r - 6*M*r2 + 6*r3) + a2*cos2th*(a4*(M + 15*r) + 16*a2*(-13*M + 3*r)*r2 + 48*(8*M2 - 6*M*r + r2)*r3) + 32*a2*M*r4 + 48*a2*r5 + 128*M2*r5 - 128*M*r6 + 32*r7))/(2.*a2p2r2pa2c2th3*a2pr2m2Mr2*sinth2*tanth);\n  src_im[6] = (-8*a*d2hS_dtph_im[6]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2_im[6]/r2pa2cth2 + (d2hS_dr2_im[6]*(a2 - 2*M*r + r2))/r2pa2cth2 - (2*dhS_dth_im[8]*r)/r2pa2cth22 - (4*a*dhS_dph_im[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (4*a*dhS_dt_im[9]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 + 8*M*r3 - 6*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (d2hS_dt2_im[6]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (2*dhS_dr_im[6]*(7*a4*M + a4*cos4th*(M - r) + a4*r - 4*a2*M*r2 + 4*a2*cos2th*(2*a2*M + (M - r)*r2) + 4*a2*r3 - 16*M*r4))/a2p2r2pa2c2th3 + (2*dhS_dph_im[4]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (4*dhS_dt_im[3]*M*(a6 + a2*(4*M - 3*r)*r3 + a2*cos2th*(a4 + 2*a2*r2 + (-4*M + r)*r3) - 2*r6))/(a2p2r2pa2c2th2*a2pr2m2Mr2) - (2*hS_im[6]*(-2*a6*M2 + a6*cos6th*M2 + 24*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 40*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 12*M*r + 3*r2) + 2*r2*(10*M2 - 9*M*r + 3*r2)) - 44*a4*M*r3 + 36*a4*r4 + 112*a2*M2*r4 - a2*cos2th*(a4*(M2 - 2*M*r - 15*r2) + 48*a2*(M - r)*r3 - 16*(M2 - 5*M*r + 3*r2)*r4) - 176*a2*M*r5 + 48*a2*r6 + 128*M2*r6 - 128*M*r7 + 32*r8))/(a2p2r2pa2c2th4*a2pr2m2Mr) + (4*a*hS_im[2]*M*(5*a6 - 21*a4*r2 + cos4th*(-a6 + a4*r2) - 56*a2*r4 + 4*a2*cos2th*(a4 + 3*a2*r2 + 4*r4) - 24*r6)*sin2th)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (2*a2*costh*dhS_dr_im[8]*sinth)/r2pa2cth22 + (d2hS_dph2_im[6]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (dhS_dph_im[9]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 20*a2*M*r2 + 4*a2*cos2th*r*(a2 - 3*M*r + 2*r2) + 8*a2*r3 + 32*M2*r3 - 32*M*r4 + 8*r5))/(a2p2r2pa2c2th2*a2pr2m2Mr2*sinth2) - (8*a*dhS_dr_im[1]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt_im[4]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*hS_im[1]*M*(3*a6*M - 20*a6*r + 31*a4*M*r2 - 26*a4*r3 + a4*cos4th*(a2*M - 3*M*r2 + 2*r3) + 4*a2*cos2th*(a4*(M - 5*r) + a2*(7*M - 8*r)*r2 + (6*M - r)*r4) + 12*a2*r5 - 24*M*r6 + 24*r7)*sinth2)/(a2p2r2pa2c2th4*a2pr2m2Mr) - (32*a3*costh*dhS_dth_im[1]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt_im[5]*M*r*sinth3)/a2p2r2pa2c2th3 - (dhS_dth_im[6]*(a4 + 3*a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 + 4*r*(-M + r)) + 8*r4))/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph_im[5]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (2*hS_im[8]*(-6*a6*M - a6*cos6th*M + 4*a6*r + a6*cos6th*r + 52*a4*M*r2 + 2*a4*cos4th*(a2*(3*M + 2*r) + (-2*M + 5*r)*r2) + 14*a4*r3 + 48*a2*M*r4 + a2*cos2th*(a4*(M + 7*r) + 8*a2*(-10*M + 3*r)*r2 + 16*(-7*M + 2*r)*r4) + 16*a2*r5 - 32*M*r6 + 16*r7))/(a2p2r2pa2c2th4*a2pr2m2Mr*tanth);\n  src_im[7] = (-8*a*d2hS_dtph_im[7]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) - (4*dhS_dr_im[7]*(a2*cos2th*(M - r) + a2*(M + 3*r) + 2*(-3*M + r)*r2))/a2p2r2pa2c2th2 + d2hS_dth2_im[7]/r2pa2cth2 + (d2hS_dr2_im[7]*(a2 - 2*M*r + r2))/r2pa2cth2 + (4*a2pr2m2Mr*dhS_dth_im[5]*r)/r2pa2cth22 - (d2hS_dt2_im[7]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) + (8*a*hS_im[3]*M*r*(-15*a4 + a4*cos4th + 16*a2*M*r - 2*a2*cos2th*(7*a2 + r*(-8*M + 7*r)) - 2*a2*r2 - 16*M*r3 + 12*r4))/(a2p2r2pa2c2th3*a2pr2m2Mr) + (4*hS_im[0]*M*r*(-9*a6 + 4*a4*M*r - 7*a4*r2 + a4*cos4th*(3*a2 - 4*M*r + 3*r2) - 8*a2*M*r3 - 2*a2*cos2th*(3*a4 + 8*a2*r2 + (-4*M + 5*r)*r3) + 6*a2*r4 + 4*r6))/(a2p2r2pa2c2th3*a2pr2m2Mr) - (16*a3*dhS_dph_im[2]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (16*a3*dhS_dt_im[8]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (16*a2*hS_im[5]*(-M + r)*sin2th)/a2p2r2pa2c2th2 - (16*a2*dhS_dt_im[2]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (2*a2*a2pr2m2Mr*dhS_dr_im[5]*sin2th)/r2pa2cth22 + (d2hS_dph2_im[7]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS_im[7]*(6*a4 + a4*cos6th + 8*a2*r2 + 2*a2*cos4th*(5*a2 + 4*r2) + 32*M*r3 + cos2th*(15*a4 + 48*a2*r2 + 16*(-2*M + r)*r3) + 16*r4))/(2.*a2p2r2pa2c2th3*sinth2) + (a2pr2m2Mr*hS_im[4]*(3*a2*cos2th*M*r + r*(3*a2*M + 2*a2*r - 6*M*r2 + 2*r3) + 2*a4*costh2*sinth2))/r2pa2cth23 - (2*dhS_dth_im[7]*(-5*a2 + 3*a2*cos2th - 2*r2))/(a2p2r2pa2c2th2*tanth) - (2*dhS_dph_im[8]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth) + (hS_im[9]*(35*a6 + 8*a6*cos6th + a6*cos8th - 168*a4*M*r - 36*a4*cos6th*M*r + 120*a4*r2 + 12*a4*cos6th*r2 + 128*a2*M2*r2 + 4*a2*cos4th*(7*a4 + 2*a2*r*(5*M + 9*r) - 4*(8*M2 + M*r - 3*r2)*r2) - 48*a2*M*r3 + 144*a2*r4 - 256*M2*r4 + 64*r6 + 4*cos2th*(14*a6 + a4*r*(-23*M + 45*r) - 16*a2*(7*M - 3*r)*r3 + 16*r4*pow(-2*M + r,2))))/(8.*a2p2r2pa2c2th3*a2pr2m2Mr*sinth4);\n  src_im[8] = (-8*a*d2hS_dtph_im[8]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2_im[8]/r2pa2cth2 + (d2hS_dr2_im[8]*(a2 - 2*M*r + r2))/r2pa2cth2 + (2*a2pr2m2Mr*dhS_dth_im[6]*r)/r2pa2cth22 - (d2hS_dt2_im[8]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (8*dhS_dr_im[8]*(a4*(M + r) + 3*a2*(-M + r)*r2 + a2*cos2th*(a2*(M + r) + (-M + r)*r2) + 2*(-3*M + r)*r4))/a2p2r2pa2c2th3 + (2*dhS_dph_im[5]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 - (8*a3*dhS_dph_im[3]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a3*dhS_dt_im[9]*M*r*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) - (8*a2*dhS_dt_im[3]*M*r*(a2 + r2)*sin2th)/(a2p2r2pa2c2th2*a2pr2m2Mr) + (a2*a2pr2m2Mr*dhS_dr_im[6]*sin2th)/r2pa2cth22 + (d2hS_dph2_im[8]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) - (hS_im[8]*(55*a6 + 10*a6*cos6th + a6*cos8th - 80*a4*M*r - 24*a4*cos6th*M*r + 184*a4*r2 + 16*a4*cos6th*r2 + 224*a2*M*r3 + 8*a2*cos4th*(5*a4 + a2*r*(10*M + 13*r) + 2*(2*M + 5*r)*r3) + 208*a2*r4 + 256*M*r5 + 2*cos2th*(43*a6 + 4*a4*r*(3*M + 34*r) - 16*a2*(8*M - 9*r)*r3 + 64*(-2*M + r)*r5) + 64*r6))/(4.*a2p2r2pa2c2th4*sinth2) - (8*a*dhS_dr_im[2]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (8*a*dhS_dt_im[5]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (32*a*hS_im[2]*M*r*(4*a4 - a2*r2 + a2*cos2th*(4*a2 + 3*r2) - 6*r4)*sinth2)/a2p2r2pa2c2th4 - (32*a3*costh*dhS_dth_im[2]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a3*costh*dhS_dt_im[7]*M*r*sinth3)/a2p2r2pa2c2th3 + (32*a2pr2m2Mr*a3*costh*hS_im[1]*M*(a2 + a2*cos2th - 10*r2)*sinth3)/a2p2r2pa2c2th4 - (dhS_dth_im[8]*(a4 + 3*a4*cos4th + 16*a2*M*r + 4*a2*cos2th*(a2 + 4*r*(-M + r)) + 8*r4))/(a2p2r2pa2c2th3*tanth) + (2*dhS_dph_im[7]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (2*hS_im[6]*(-2*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 8*a4*M2*r + 60*a4*M*r2 + 2*a4*cos4th*(a2*(M + 3*r) + 2*r*(-2*M2 - 3*M*r + 3*r2)) + 36*a4*r3 - 160*a2*M2*r3 + a2*cos2th*(a4*(M + 15*r) - 16*a2*(7*M - 3*r)*r2 + 16*(10*M2 - 10*M*r + 3*r2)*r3) + 32*a2*M*r4 + 48*a2*r5 - 64*M*r6 + 32*r7))/(a2p2r2pa2c2th4*tanth) - (dhS_dph_im[9]*(3*a4 + a4*cos4th + 4*a2*cos2th*(a2 + 2*r*(-2*M + r)) + 8*a2*r2 - 16*M*r3 + 8*r4))/(a2p2r2pa2c2th2*a2pr2m2Mr*sinth2*tanth);\n  src_im[9] = (-8*a*d2hS_dtph_im[9]*M*r)/(a2p2r2pa2c2th*a2pr2m2Mr) + d2hS_dth2_im[9]/r2pa2cth2 + (d2hS_dr2_im[9]*(a2 - 2*M*r + r2))/r2pa2cth2 - (d2hS_dt2_im[9]*(a4 + a2*a2pr2m2Mr*cos2th + a2*r*(2*M + 3*r) + 2*r4))/(a2p2r2pa2c2th*a2pr2m2Mr) - (2*dhS_dr_im[9]*(5*a4*M - a4*cos4th*(M - r) + 3*a4*r - 16*a2*M*r2 + 8*a2*r3 + 4*a2*cos2th*(a2*(M + r) + 2*r3) - 24*M*r4 + 8*r5))/a2p2r2pa2c2th3 + (4*dhS_dph_im[6]*(a4*M - a4*cos4th*(M - r) + 3*a4*r - 12*a2*M*r2 + 4*a2*cos2th*r*(a2 - M*r + 2*r2) + 8*a2*r3 - 16*M*r4 + 8*r5))/a2p2r2pa2c2th3 + (hS_im[7]*(35*a8 + 8*a8*cos6th + a8*cos8th - 78*a6*M*r - 36*a6*cos6th*M*r - 2*a6*cos8th*M*r + 155*a6*r2 + 20*a6*cos6th*r2 + a6*cos8th*r2 + 80*a4*M2*r2 + 40*a4*cos6th*M2*r2 + 88*a4*M*r3 - 44*a4*cos6th*M*r3 + 264*a4*r4 + 12*a4*cos6th*r4 - 96*a2*M2*r4 + 4*a2*cos4th*(7*a6 + 5*a4*r*(4*M + 5*r) + 4*(-2*M2 + M*r + 3*r2)*r4 + a2*(-20*M2*r2 + 26*M*r3 + 30*r4)) + 304*a2*M*r5 + 208*a2*r6 + 128*M*r7 + 4*cos2th*(14*a8 + a6*r*(9*M + 59*r) + a4*r2*(-10*M2 - 37*M*r + 93*r2) + 16*a2*(2*M2 - 5*M*r + 4*r2)*r4 + 16*(-2*M + r)*r7) + 64*r8))/(2.*a2p2r2pa2c2th5) + (2*a2pr2m2Mr*hS_im[5]*(-10*a6*M - a6*cos6th*M + 10*a6*r + a6*cos6th*r + 140*a4*M*r2 + 2*a4*cos4th*(a2*(5*M + 3*r) + 2*(M + 3*r)*r2) + 36*a4*r3 + 160*a2*M*r4 + a2*cos2th*(a4*(M + 15*r) - 48*a2*(3*M - r)*r2 + 16*(-10*M + 3*r)*r4) + 48*a2*r5 + 32*r7)*sin2th)/a2p2r2pa2c2th5 + (d2hS_dph2_im[9]*(a2 + a2*cos2th + 2*r*(-2*M + r)))/(a2p2r2pa2c2th*a2pr2m2Mr*sinth2) + (hS_im[9]*(126*a10 + a10*cos10th + 45*a10*cos6th + 10*a10*cos8th + 6*a8*M2 - a8*cos10th*M2 + 3*a8*cos6th*M2 + 2*a8*cos8th*M2 - 160*a8*M*r - 160*a8*cos6th*M*r - 32*a8*cos8th*M*r + 512*r10 + 700*a8*r2 + 160*a8*cos6th*r2 + 20*a8*cos8th*r2 - 120*a6*M2*r2 + 160*a6*cos6th*M2*r2 + 24*a6*cos8th*M2*r2 - 840*a6*M*r3 - 384*a6*cos6th*M*r3 - 24*a6*cos8th*M*r3 - 640*a4*M3*r3 - 320*a4*cos6th*M3*r3 + 1600*a6*r4 + 160*a6*cos6th*r4 - 96*a4*M2*r4 + 240*a4*cos6th*M2*r4 - 1856*a4*M*r5 - 160*a4*cos6th*M*r5 + 768*a2*M3*r5 + 8*a2*cos4th*(15*a8 - a6*(M2 + 40*M*r - 70*r2) + 4*a4*r2*(3*M2 - 37*M*r + 30*r2) + 4*a2*r3*(20*M3 + 11*M2*r - 38*M*r2 + 20*r3) + 16*M*(2*M2 + 2*M*r - r2)*r5) + 1920*a4*r6 + 768*a2*M2*r6 - 2432*a2*M*r7 + 1280*a2*r8 + 1024*M2*r8 + 2*cos2th*(105*a10 - a8*(M2 + 176*M*r - 560*r2) + 16*a6*r2*(-5*M2 - 52*M*r + 75*r2) + 8*a4*r3*(20*M3 - 31*M2*r - 182*M*r2 + 160*r3) - 128*a2*(4*M3 + 4*M2*r + 6*M*r2 - 5*r3)*r5 + 256*M*(-2*M + r)*r8) - 1536*M*r9))/(8.*a2p2r2pa2c2th5*a2pr2m2Mr*sinth2) - (16*a*dhS_dr_im[3]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (16*a*dhS_dt_im[6]*M*(a4 + a2*cos2th*(a2 - r2) - 3*a2*r2 - 6*r4)*sinth2)/a2p2r2pa2c2th3 + (2*a2pr2m2Mr*hS_im[4]*(-2*a6*M2 + a6*cos6th*M2 + 76*a6*M*r - 2*a6*cos6th*M*r + 10*a6*r2 + a6*cos6th*r2 - 40*a4*M2*r2 + 2*a4*cos4th*(a2*(M2 - 14*M*r + 3*r2) + 2*r2*(10*M2 - 8*M*r + 3*r2)) + 32*a4*M*r3 + 36*a4*r4 + 48*a2*M2*r4 - a2*cos2th*(a4*(M2 - 50*M*r - 15*r2) - 48*a2*(2*M + r)*r3 - 48*(-M2 + M*r + r2)*r4) - 144*a2*M*r5 + 48*a2*r6 - 96*M*r7 + 32*r8)*sinth2)/a2p2r2pa2c2th5 + (4*a*hS_im[3]*M*(2*a8*M - a8*cos6th*M + 30*a8*r + 3*a8*cos6th*r - 130*a6*M*r2 - 3*a6*cos6th*M*r2 + 70*a6*r3 + a6*cos6th*r3 - 80*a4*M2*r3 - 2*a4*cos4th*(a4*(M - 9*r) + a2*(31*M - 13*r)*r2 + 2*(-20*M2 + 14*M*r + r2)*r3) - 184*a4*M*r4 + 4*a4*r5 + 64*a2*M2*r5 + a2*cos2th*(a6*(M + 45*r) + a4*(-189*M + 95*r)*r2 - 272*a2*M*r4 - 16*(4*M2 + 3*M*r + 5*r2)*r5) + 176*a2*M*r6 - 112*a2*r7 + 256*M*r8 - 96*r9)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (2*hS_im[0]*M*(-2*a10*M + a10*cos6th*M + 30*a10*r + 3*a10*cos6th*r - 32*r11 + 28*a8*M*r2 - 18*a8*cos6th*M*r2 + 120*a8*r3 + 6*a8*cos6th*r3 + 40*a6*M2*r3 + 20*a6*cos6th*M2*r3 + 46*a6*M*r4 - 15*a6*cos6th*M*r4 + 166*a6*r5 + 3*a6*cos6th*r5 - 48*a4*M2*r5 + 2*a4*cos4th*(a6*(M + 9*r) - 14*a4*(M - 2*r)*r2 + a2*(-20*M2 - 31*M*r + 29*r2)*r3 + 2*(-4*M2 - 2*M*r + 5*r2)*r5) - 152*a4*M*r6 + 60*a4*r7 - 208*a2*M*r8 + a2*cos2th*(-(a8*(M - 45*r)) + 2*a6*(9*M + 85*r)*r2 + a4*(-20*M2 + 31*M*r + 221*r2)*r3 + 16*a2*(4*M2 + 10*M*r + 7*r2)*r5 + 16*(13*M + r)*r8) - 48*a2*r9)*sinth2)/(a2p2r2pa2c2th5*a2pr2m2Mr) - (64*a3*costh*dhS_dth_im[3]*M*r*sinth3)/a2p2r2pa2c2th3 + (64*a3*costh*dhS_dt_im[8]*M*r*sinth3)/a2p2r2pa2c2th3 + (4*dhS_dph_im[8]*(3*a4 + a4*cos4th + 8*a2*M*r + 4*a2*cos2th*(a2 + 2*r*(-M + r)) + 8*a2*r2 + 8*r4))/(a2p2r2pa2c2th3*tanth) - (dhS_dth_im[9]*(9*a4 + 3*a4*cos4th + 32*a2*M*r + 24*a2*r2 + 4*a2*cos2th*(3*a2 - 8*M*r + 6*r2) + 24*r4))/(a2p2r2pa2c2th3*tanth);\n}\n\n/* Initialize some constants. */\nvoid effsource_init(double mass, double spin)\n{\n  M = mass;\n  a = spin;\n}\n\n/* Initialize coefficients of pows of dr, dtheta and Q=sin(dphi/2), R=sin(dphi). */\nvoid effsource_set_particle(struct coordinate * x, double E, double L, double ur)\n{\n  xp = *x;\n  double r0 = xp.r;\n\n  double E_2 = E*E;\n  double E_3 = E_2*E;\n  double E_4 = E_2*E_2;\n\n  double L_2 = L*L;\n  double L_3 = L_2*L;\n  double L_4 = L_2*L_2;\n  double L_6 = L_4*L_2;\n  double L_8 = L_4*L_4;\n  double L_10 = L_6*L_4;\n\n  double r0_2 = r0*r0;\n  double r0_3 = r0_2*r0;\n  double r0_4 = r0_2*r0_2;\n  double r0_5 = r0_3*r0_2;\n  double r0_6 = r0_3*r0_3;\n  double r0_7 = r0_4*r0_3;\n  double r0_8 = r0_4*r0_4;\n  double r0_9 = r0_5*r0_4;\n  double r0_10 = r0_5*r0_5;\n  double r0_11 = r0_6*r0_5;\n\n  double M_2 = M*M;\n  double M_3 = M_2*M;\n  double M_4 = M_2*M_2;\n  double M_5 = M_3*M_2;\n  double M_6 = M_3*M_3;\n  double M_7 = M_4*M_3;\n\n  double a_2 = a*a;\n  double a_3 = a_2*a;\n  double a_4 = a_2*a_2;\n  double a_5 = a_3*a_2;\n  double a_6 = a_3*a_3;\n  double a_7 = a_4*a_3;\n  double a_8 = a_4*a_4;\n  double a_9 = a_5*a_4;\n  double a_10 = a_5*a_5;\n  double a_11 = a_6*a_5;\n  double a_12 = a_6*a_6;\n  double a_13 = a_7*a_6;\n  double a_14 = a_7*a_7;\n\n  double r0_2M = r0 - 2*M;\n  double r0_2M_2 = r0_2M*r0_2M;\n\n  double r0_p_2M = 2*M + r0;\n  double r0_p_2M_2 = r0_p_2M*r0_p_2M;\n  double r0_p_2M_3 = r0_p_2M_2*r0_p_2M;\n  double r0_p_2M_4 = r0_p_2M_2*r0_p_2M_2;\n\n  double expr1 = a_2*(2*M + r0) + r0*(L_2 + r0_2);\n  double expr1_2 = expr1*expr1;\n  double expr1_3 = expr1_2*expr1;\n  double expr1_4 = expr1_2*expr1_2;\n  \n  double expr2 = a_2 + r0*(-2*M + r0);\n  double expr2_2 = expr2*expr2;\n  double expr2_3 = expr2_2*expr2;\n  double expr2_4 = expr2_2*expr2_2;\n  double expr2_5 = expr2_3*expr2_2;\n  double expr2_6 = expr2_3*expr2_3;\n  double expr2_7 = expr2_4*expr2_3;\n\n  double expr3 = a_3 + 3*a*r0_2;\n  double expr3_2 = expr3*expr3;\n\n  double expr4 = L_2 + r0_2 + (a_2*(2*M + r0))/r0;\n  double expr4_2 = expr4*expr4;\n  double expr4_3 = expr4_2*expr4;\n\n  double expr5 = a_2*M + (2*M - r0)*r0_2;\n  double expr5_2 = expr5*expr5;\n  \n  double expr6 = r0_3 + a_2*(2*M + r0);\n  double expr6_2 = expr6*expr6;\n\n  double expr7 = 2*a*E*M + L*(-2*M + r0);\n  double expr7_2 = expr7*expr7;\n\n  double expr8 = -2*a*L*M + E*r0_3 + a_2*E*(2*M + r0);\n  double expr8_2 = expr8*expr8;\n\n  /* A_{t,t} */\n  {\n    A00060 = 256*E_2*expr4_3;\n    A00080 = (-128*expr1_2*(-(a_8*E_2*M_2*(48*M + 25*r0)) + 4*a_7*E*L*M_2*(12*M + (6 + E_2)*r0) + r0_2*(24*L_6*M*r0_2M_2 + 2*E_2*M*r0_7*(-2*M + r0 + 2*E_2*r0) + 24*L_4*M*(2*M - r0)*r0_2*(4*M - (2 + E_2)*r0) + L_2*(2*M - r0)*r0_4*(48*M_2 - 8*(3 + 4*E_2)*M*r0 + 3*E_2*r0_2)) + 4*a*E*L*M*r0_2*(12*L_4*M*(-2*M + r0) - 6*L_2*r0_2*(16*M_2 - 2*(5 + E_2)*M*r0 + r0_2) + r0_4*(-72*M_2 + 2*(24 + 7*E_2)*M*r0 - 3*(2 + E_2)*r0_2)) + 4*a_5*E*L*M*(-192*M_4 + 8*(-15 + 4*E_2)*M_3*r0 + 6*(12 + 5*E_2)*M_2*r0_2 - (6 + E_2)*r0_4 + 6*M*r0*(L_2 + 2*(3 + E_2)*r0_2)) + 4*a_3*E*L*M*r0*(-6*L_2*(24*M_3 + (2 - 4*E_2)*M_2*r0 - (9 + 2*E_2)*M*r0_2 + r0_3) + r0_2*(-240*M_3 + 2*(6 + 13*E_2)*M_2*r0 + 3*(26 + 7*E_2)*M*r0_2 - 4*(3 + E_2)*r0_3)) + 2*a_6*E_2*M*(192*M_4 + 16*(15 + E_2)*M_3*r0 + 2*(-11 + 12*E_2)*M_2*r0_2 + (1 + 2*E_2)*r0_4 + M*(-14*L_2*r0 + (-61 + 12*E_2)*r0_3)) + a_2*r0*(24*L_4*M*(4*M - (2 + E_2)*r0)*(4*M_2 - r0_2) + 2*E_2*M*r0_5*(124*M_2 + 6*(-7 + 2*E_2)*M*r0 + 3*(1 + 2*E_2)*r0_2) + L_2*r0_2*(384*M_4 + 16*(-12 + 5*E_2)*M_3*r0 - 96*(1 + E_2)*M_2*r0_2 + 16*(3 + 4*E_2)*M*r0_3 - 3*E_2*r0_4)) + a_4*M*(E_2*r0_3*(668*M_3 + 4*(61 + 12*E_2)*M_2*r0 + 3*(-59 + 16*E_2)*M*r0_2 + 6*(1 + 2*E_2)*r0_3) + 4*L_2*(96*M_4 + 8*E_2*M_3*r0 + 6*(-8 + 3*E_2)*M_2*r0_2 - 15*E_2*M*r0_3 + 2*(3 + 4*E_2)*r0_4))))/(3.*r0_9*(a_2 + r0*(-2*M + r0)));\n    A00240 = 192*E_2*expr1_2;\n    A00260 = (-64*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-2*a_7*E*L*M*(12*(-7 + 2*E_2)*M_2 + 2*(-33 + 8*E_2)*M*r0 + 3*(-4 + E_2)*r0_2) + a_8*E_2*M*(24*(-7 + E_2)*M_2 + (-139 + 24*E_2)*M*r0 + 3*(-9 + 2*E_2)*r0_2) + a_6*E_2*(768*M_5 + 16*(69 + E_2)*M_4*r0 + 12*(7 + 6*E_2)*M_3*r0_2 + 6*(-57 + 14*E_2)*M_2*r0_3 + (-89 + 26*E_2)*M*r0_4 - 3*r0_5 + L_2*M*(24*M_2 + 4*(-31 + 3*E_2)*M*r0 + 3*(-17 + 2*E_2)*r0_2)) + 2*a*E*L*M*r0_2*(12*L_4*(-10*M_2 + 3*M*r0 + r0_2) + L_2*r0_2*(-384*M_2 + 2*(90 + 23*E_2)*M*r0 + 3*(2 - 3*E_2)*r0_2) + r0_4*(-264*M_2 + 2*(72 + 25*E_2)*M*r0 - 3*(2 + 5*E_2)*r0_2)) - 2*a_5*E*L*M*(768*M_4 - 8*(-69 + 16*E_2)*M_3*r0 - 8*(27 + 11*E_2)*M_2*r0_2 + 2*M*r0*((-33 + 6*E_2)*L_2 + (-102 + 7*E_2)*r0_2) + r0_2*(3*(-8 + E_2)*L_2 + (-18 + 17*E_2)*r0_2)) + r0_2*(48*L_6*M*r0_2M_2 + E_2*r0_7*(-8*M_2 + 2*(5 + 4*E_2)*M*r0 - 3*r0_2) + 2*L_4*(2*M - r0)*r0_2*(96*M_2 - 2*(24 + 11*E_2)*M*r0 + 3*E_2*r0_2) + 2*L_2*r0_4*(96*M_3 - 6*(16 + 9*E_2)*M_2*r0 + (24 + 39*E_2 + 2*E_4)*M*r0_2 - 6*E_2*r0_3)) + 2*a_3*E*L*M*r0*(12*L_4*r0 + r0_2*(-912*M_3 + 4*(-15 + 32*E_2)*M_2*r0 + 2*(141 + 22*E_2)*M*r0_2 - 29*E_2*r0_3) + L_2*(-624*M_3 + 4*(-33 + 20*E_2)*M_2*r0 + 2*(105 + 11*E_2)*M*r0_2 + 6*(5 - 2*E_2)*r0_3)) + a_2*r0*(E_2*r0_5*(412*M_3 + 12*(-4 + 3*E_2)*M_2*r0 + 15*(-1 + 2*E_2)*M*r0_2 - 9*r0_3) + L_2*r0_2*(768*M_4 + 4*(-96 + 41*E_2)*M_3*r0 + 4*(-48 + 3*E_2 + E_4)*M_2*r0_2 + (96 + 57*E_2 + 14*E_4)*M*r0_3 - 21*E_2*r0_4) + 2*L_4*(384*M_4 - 4*(48 + 13*E_2)*M_3*r0 + 6*(-16 + 5*E_2)*M_2*r0_2 + 2*(24 + 5*E_2)*M*r0_3 - 3*E_2*r0_4)) + a_4*(12*E_2*L_4*M*(M - 2*r0)*r0 + E_2*r0_3*(1204*M_4 + 8*(85 + 6*E_2)*M_3*r0 + 3*(-81 + 32*E_2)*M_2*r0_2 + 3*(-29 + 14*E_2)*M*r0_3 - 9*r0_4) + L_2*(768*M_5 + 208*E_2*M_4*r0 - 8*(48 - 50*E_2 + E_4)*M_3*r0_2 + 8*E_2*(-5 + 2*E_2)*M_2*r0_3 + 2*(24 - 33*E_2 + 8*E_4)*M*r0_4 - 9*E_2*r0_5))))/(3.*r0_6*(a_2 + r0*(-2*M + r0)));\n    A00420 = 48*E_2*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2));\n    A00440 = (8*((16*a_2*E_2*L_4*M_3)/(r0*(a_2 + r0*(-2*M + r0))) - (16*a*E_3*L_3*M_2*(r0_3 + a_2*(2*M + r0)))/(r0*(a_2 + r0*(-2*M + r0))) + (4*E_4*L_2*M*expr6_2)/(r0*(a_2 + r0*(-2*M + r0))) + E_2*(-3*a_2 + r0*(-2*M + r0))*expr4_2 + (72*E_2*M_2*(a_4 + 3*a_2*r0_2)*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/r0_3 - (72*a*E*L*M*(a_2*M + (2*M - r0)*r0_2)*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/r0_3 - (48*a_2*E_2*L_2*M_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(r0_2*(a_2 + r0*(-2*M + r0))) + (144*a*E*L_2*M_2*(2*a*E*M + L*(-2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(r0_2*(a_2 + r0*(-2*M + r0))) + (48*a*E_3*L*M_2*(r0_3 + a_2*(2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(r0_2*(a_2 + r0*(-2*M + r0))) - (72*E_2*L*M*(2*a*E*M + L*(-2*M + r0))*(r0_3 + a_2*(2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(r0_2*(a_2 + r0*(-2*M + r0))) - (12*E_4*M*expr6_2*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(r0_2*(a_2 + r0*(-2*M + r0))) + (144*a_2*E_2*M*expr1_2)/r0_3 - (144*a*E*L*M*expr1_2)/r0_3 - (144*M*expr7_2*expr1_2)/(r0_3*(a_2 + r0*(-2*M + r0))) - (8*E_2*L_2*M*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(r0_2*(a_2 + r0*(-2*M + r0))) + (12*E_2*M*expr1_2*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(r0_3*(a_2 + r0*(-2*M + r0))) + (E_2*(a_6*M_2 - 4*a_5*E*L*M_2 + 12*a*E*L*M*r0_5 + 4*a_3*E*L*M*r0_2*(-3*M + r0) + 4*a_2*M*(M - r0)*r0_2*(2*L_2 + r0_2) + r0_5*(-2*M*r0_2 + L_2*(-8*M + 3*r0)) + a_4*M*(4*L_2*M + r0*(-2*M_2 + M*r0 - 2*r0_2))))/r0_2 + (4*E_2*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(3*a_4*M - 6*a_3*E*L*M + 6*a*E*L*M*r0_2 - (2*M - 3*r0)*r0_2*(2*L_2 + r0_2) + a_2*(6*L_2*M + r0*(2*M_2 + 7*M*r0 + 3*r0_2))))/r0_2))/3.;\n    A00600 = 4*E_2*r0_6;\n    A00620 = (-4*(-6*a_5*E*L*M*(2*(-13 + 6*E_2)*M + (-12 + 5*E_2)*r0) + 3*a_6*E_2*(4*(-13 + 3*E_2)*M_2 + (-23 + 6*E_2)*M*r0 + r0_2) + 2*a*E*L*M*r0*(r0_2*(-216*M_2 + 2*(24 + 19*E_2)*M*r0 + 3*(10 - 7*E_2)*r0_2) - 4*L_2*(42*M_2 - (3 + 4*E_2)*M*r0 + 3*(-3 + E_2)*r0_2)) - 4*a_3*E*L*M*(192*M_3 + (78 - 32*E_2)*M_2*r0 + (-57 + 5*E_2)*M*r0_2 + 3*r0*(2*(-3 + E_2)*L_2 + (-11 + 6*E_2)*r0_2)) + r0*(8*L_4*M*(2*M - r0)*(12*M - (6 + E_2)*r0) + 4*E_2*r0_5*(-2*M_2 + (3 + 2*E_2)*M*r0 - r0_2) + L_2*r0_2*(192*M_3 - 12*(16 + 5*E_2)*M_2*r0 + 4*(12 + 11*E_2 + E_4)*M*r0_2 - 7*E_2*r0_3)) + a_4*E_2*(3*L_2*(12*M_2 + 4*(-5 + E_2)*M*r0 + r0_2) + 2*(192*M_4 - 4*(-63 + 5*E_2)*M_3*r0 + 2*(-13 + 8*E_2)*M_2*r0_2 + 11*(-7 + 2*E_2)*M*r0_3 + r0_4)) + a_2*(12*E_2*L_4*M*r0 + E_2*r0_3*(260*M_3 - 4*(-31 + E_2)*M_2*r0 + (-73 + 34*E_2)*M*r0_2 - 5*r0_3) + 4*L_2*(96*M_4 + 2*(-24 + 7*E_2)*M_3*r0 + (-24 + 39*E_2 - 4*E_4)*M_2*r0_2 + (12 - 9*E_2 + 4*E_4)*M*r0_3 - E_2*r0_4))))/(3.*(a_2 + r0*(-2*M + r0)));\n    A00800 = (r0_3*(48*a_2*E_2*M - 48*a*E*L*M + E_2*r0*(-3*a_2 + r0*(-2*M + r0)) - (24*M*expr7_2)/(a_2 + r0*(-2*M + r0)) + (4*E_2*M*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(a_2 + r0*(-2*M + r0))))/6.;\n    A01060 = (-128*E*expr1_2*(-(a_4*E*M*(8*M + 5*r0)) + 2*a_3*L*M*(4*M + (2 + E_2)*r0) + 2*a*L*M*r0*(2*L_2 + (2 + 3*E_2)*r0_2) + E*r0_3*(r0_2*(-6*M + r0) + L_2*(-8*M + 2*r0)) + a_2*E*r0*(-6*L_2*M + r0*(-6*M_2 - 9*M*r0 + r0_2))))/(r0_5*(a_2 + r0*(-2*M + r0)));\n    A01080 = (-64*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_12*E_2*M_2*(-192*M_3 + 8*(41 - 10*E_2)*M_2*r0 + (509 - 88*E_2)*M*r0_2 + 6*(25 - 4*E_2)*r0_3) - 2*a_11*E*L*M_2*(-192*M_3 + 4*(17 + 36*E_2)*M_2*r0 + (226 + 183*E_2)*M*r0_2 + 12*(6 + 5*E_2)*r0_3) + 2*a_9*E*L*M*r0*(-768*(-5 + 4*E_2)*M_5 + 16*(359 - 236*E_2 + 12*E_4)*M_4*r0 + 4*(372 - 371*E_2 + 72*E_4)*M_3*r0_2 + (-1214 - 647*E_2 + 144*E_4)*M_2*r0_3 + 2*(-206 - 165*E_2 + 12*E_4)*M*r0_4 - 12*(-4 + E_2)*r0_5 - 2*L_2*M*(16*M_2 + (217 + 96*E_2)*M*r0 + 6*(18 + 11*E_2)*r0_2)) + 2*a*E*L*M*r0_4*(-48*L_8*r0_2M_2 - 2*(2*M - r0)*r0_7*(312*M_2 + (-170 + 37*E_2)*M*r0 + 6*(3 - 2*E_2)*r0_2) + 12*L_6*(2*M - r0)*r0*(4*M_2 - 2*(8 + 7*E_2)*M*r0 + (8 + E_2)*r0_2) + L_2*r0_5*(-2592*M_3 - 8*(-353 + 29*E_2)*M_2*r0 + 2*(-472 + 67*E_2 + 30*E_4)*M*r0_2 + 3*(30 + E_2)*r0_3) - 2*L_4*r0_3*(624*M_3 + 4*(-160 + 69*E_2)*M_2*r0 - 2*(-85 + 90*E_2 + 6*E_4)*M*r0_2 + 3*(-1 + 5*E_2)*r0_3)) + r0_5*(24*L_8*M*r0_2M_2*(6*M + (-1 + 4*E_2)*r0) - 2*E_2*M*r0_9*(16*M_2 + 2*(-6 + E_2)*M*r0 + (2 - 5*E_2)*r0_2) + 24*L_6*M*(2*M - r0)*r0_2*(40*M_2 + 4*(-7 + 3*E_2)*M*r0 - (-4 + 8*E_2 + E_4)*r0_2) + 2*L_4*(2*M - r0)*r0_4*(528*M_3 - 8*(48 + E_2)*M_2*r0 - 10*(-6 + 4*E_2 + 3*E_4)*M*r0_2 - 3*E_2*r0_3) + L_2*r0_6*(768*M_4 - 8*(120 + 23*E_2)*M_3*r0 + 4*(96 + 15*E_2 - 13*E_4)*M_2*r0_2 + 2*(-24 + 11*E_2 + 17*E_4)*M*r0_3 - 3*E_2*r0_4)) + 4*a_3*E*L*M*r0_3*(-6*L_6*(32*M_3 + 12*(-5 + 3*E_2)*M_2*r0 + 2*(1 - 7*E_2)*M*r0_2 + (8 + E_2)*r0_3) + 2*r0_5*(-1704*M_4 + 12*(57 - 47*E_2)*M_3*r0 + (702 + 151*E_2)*M_2*r0_2 + 2*(-188 + 19*E_2 + 3*E_4)*M*r0_3 + (39 - 19*E_2)*r0_4) + L_4*r0*(-1104*M_4 - 8*(-23 + 63*E_2)*M_3*r0 + 24*(62 - 11*E_2 + E_4)*M_2*r0_2 + 8*(-74 + 30*E_2 + 3*E_4)*M*r0_3 + 3*(1 - 10*E_2)*r0_4) + L_2*r0_3*(-4704*M_4 - 4*(-478 + 383*E_2)*M_3*r0 + 2*(1272 + 157*E_2 + 60*E_4)*M_2*r0_2 + 3*(-458 + 27*E_2 + 30*E_4)*M*r0_3 - 23*(-6 + E_2)*r0_4)) + a_2*r0_3*(-24*L_8*M*(2*M - r0)*(8*M_2 + (8 - 22*E_2)*M*r0 + (-3 + 4*E_2)*r0_2) + 2*E_2*M*r0_8*(1260*M_3 + 4*(-246 + 101*E_2)*M_2*r0 + (203 - 46*E_2)*M*r0_2 + (-11 + 23*E_2)*r0_3) + 24*L_6*M*r0*(184*M_4 - 4*(41 + 2*E_2)*M_3*r0 + 2*(-29 + 29*E_2 + 4*E_4)*M_2*r0_2 + (73 - 57*E_2 - 4*E_4)*M*r0_3 + (-13 + 14*E_2 + 2*E_4)*r0_4) + L_2*r0_5*(5184*M_5 + 16*(-246 + 349*E_2)*M_4*r0 + 12*(-84 - 499*E_2 + 74*E_4)*M_3*r0_2 - 24*(-53 - 69*E_2 + E_4)*M_2*r0_3 + 9*(-24 - E_2 + 16*E_4)*M*r0_4 - 6*E_2*r0_5) + 2*L_4*r0_3*(4800*M_5 + 8*(-468 + 163*E_2)*M_4*r0 + 12*(-92 - 147*E_2 + 10*E_4)*M_3*r0_2 + 2*(672 + 179*E_2 - 60*E_4)*M_2*r0_3 + (-228 + 83*E_2 + 90*E_4)*M*r0_4 + 3*E_2*r0_5)) + 2*a_5*E*L*M*r0_2*(-72*L_6*M*r0 + 2*L_4*(256*M_4 - 24*(-63 + 41*E_2)*M_3*r0 + 24*(18 - 4*E_2 + E_4)*M_2*r0_2 + 4*(-134 + 6*E_2 + 3*E_4)*M*r0_3 - 15*E_2*r0_4) + L_2*r0*(-9024*M_5 - 8*(197 + 344*E_2)*M_4*r0 + 24*(491 - 189*E_2 + 10*E_4)*M_3*r0_2 + 2*(679 + 360*E_2 + 240*E_4)*M_2*r0_3 + 4*(-714 - 91*E_2 + 45*E_4)*M*r0_4 + (282 - 79*E_2)*r0_5) + r0_3*(-12864*M_5 - 60*(46 + 71*E_2)*M_4*r0 - 92*(-114 + 37*E_2)*M_3*r0_2 + (798 + 1219*E_2 + 144*E_4)*M_2*r0_3 + 24*(-85 - 10*E_2 + 3*E_4)*M*r0_4 + 4*(63 - 23*E_2)*r0_5)) - a_10*M*(2*L_2*M*(96*M_3 + 24*(4 - 13*E_2)*M_2*r0 + (24 - 709*E_2 + 32*E_4)*M*r0_2 + 3*E_2*(-97 + 4*E_2)*r0_3) + E_2*r0*(-1280*(-3 + 2*E_2)*M_5 + 32*(269 - 125*E_2)*M_4*r0 + 2*(2021 - 888*E_2)*M_3*r0_2 + (-1429 + 88*E_2)*M_2*r0_3 + 5*(-183 + 28*E_2)*M*r0_4 - 6*(-1 + E_2)*r0_5)) + a_4*r0_2*(E_2*M*r0_6*(10848*M_4 + 4*(-149 + 984*E_2)*M_3*r0 + 4*(-1475 + 602*E_2)*M_2*r0_2 - 3*(-509 + 96*E_2)*M*r0_3 + 12*(-4 + 7*E_2)*r0_4) - 24*L_6*M*(64*M_4 - 4*(-26 + 43*E_2)*M_3*r0 - 2*(25 - 28*E_2 + 2*E_4)*M_2*r0_2 + (-27 + 21*E_2 + 2*E_4)*M*r0_3 - (-9 + 6*E_2 + E_4)*r0_4) + 2*L_4*M*r0*(5376*M_5 + 16*(-132 + 17*E_2)*M_4*r0 + 8*(-648 + 265*E_2 + 60*E_4)*M_3*r0_2 + 12*(132 - 267*E_2 + 10*E_4)*M_2*r0_3 + 4*(246 + 257*E_2 - 15*E_4)*M*r0_4 + (-276 + 43*E_2 + 90*E_4)*r0_5) + L_2*r0_3*(11520*M_6 + 8*(-384 + 1703*E_2)*M_5*r0 + 16*(-516 - 89*E_2 + 333*E_4)*M_4*r0_2 + 2*(1080 - 5793*E_2 + 1180*E_4)*M_3*r0_3 + 2*(684 + 2227*E_2 + 36*E_4)*M_2*r0_4 + 6*(-60 - 21*E_2 + 38*E_4)*M*r0_5 - 3*E_2*r0_6)) + 2*a_7*E*L*M*r0*(-8*L_4*M*r0*(26*M + 9*(3 + E_2)*r0) + 2*L_2*(512*M_5 + 16*(199 - 126*E_2)*M_4*r0 + 4*(629 - 332*E_2 + 30*E_4)*M_3*r0_2 + 2*(-329 - 83*E_2 + 60*E_4)*M_2*r0_3 + 2*(-317 - 120*E_2 + 15*E_4)*M*r0_4 + 3*(16 - 5*E_2)*r0_5) - r0*(8448*M_6 + 16*(413 + 164*E_2)*M_5*r0 + 4*(-2410 + 1699*E_2)*M_4*r0_2 - 4*(1991 - 658*E_2 + 72*E_4)*M_3*r0_3 + (1694 - 35*E_2 - 288*E_4)*M_2*r0_4 + 4*(322 + 135*E_2 - 18*E_4)*M*r0_5 + 4*(-45 + 13*E_2)*r0_6)) + a_8*M*r0*(-48*L_4*M*(4*M_2 + (2 - 19*E_2)*M*r0 - 15*E_2*r0_2) + 2*L_2*(384*(-5 + 4*E_2)*M_5 + 16*(-90 - 95*E_2 + 76*E_4)*M_4*r0 + 4*(108 - 1013*E_2 + 290*E_4)*M_3*r0_2 + (456 + 537*E_2 + 116*E_4)*M_2*r0_3 + (-12 + 1291*E_2 - 10*E_4)*M*r0_4 + 3*(-12 - 9*E_2 + 7*E_4)*r0_5) + E_2*r0*(8448*M_6 + 32*(353 + 136*E_2)*M_5*r0 + 4*(-2293 + 2744*E_2)*M_4*r0_2 + 12*(-1257 + 644*E_2)*M_3*r0_3 + 21*(-37 + 48*E_2)*M_2*r0_4 + (2167 - 324*E_2)*M*r0_5 + 2*(-14 + 17*E_2)*r0_6)) + a_6*M*r0*(-48*L_6*M*r0*(M - 6*E_2*r0) + E_2*r0_4*(16232*M_5 + 4*(2617 + 1696*E_2)*M_4*r0 + 2*(-6891 + 4840*E_2)*M_3*r0_2 + (-5661 + 2608*E_2)*M_2*r0_3 + 3*(849 - 136*E_2)*M*r0_4 + 4*(-13 + 19*E_2)*r0_5) - 4*L_4*(384*M_5 - 96*(-16 + 15*E_2)*M_4*r0 - 4*(-48 - 71*E_2 + 30*E_4)*M_3*r0_2 + 24*(-24 + 29*E_2)*M_2*r0_3 - 4*(18 + 119*E_2)*M*r0_4 + 3*(18 - 5*E_4)*r0_5) + L_2*r0*(8448*M_6 + 128*(15 + 64*E_2)*M_5*r0 + 8*(-1344 + 1249*E_2 + 620*E_4)*M_4*r0_2 + 8*(-288 - 1841*E_2 + 932*E_4)*M_3*r0_3 + 2*(1584 - 3129*E_2 + 820*E_4)*M_2*r0_4 + 2*(228 + 2435*E_2 + 24*E_4)*M*r0_5 + (-264 - 149*E_2 + 160*E_4)*r0_6))))/(3.*r0_11*expr2_2);\n    A01240 = (-32*E*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(4*a_3*L*M*(6*M + (3 + E_2)*r0) + a_4*E*(-24*M_2 - 12*M*r0 + r0_2) + 12*a*L*M*r0*(L_2 + (1 + E_2)*r0_2) + E*r0_3*(3*r0_2*(-6*M + r0) + L_2*(-22*M + 5*r0)) + a_2*E*r0*(L_2*(-16*M + r0) + 2*r0*(-12*M_2 - 13*M*r0 + 2*r0_2))))/(r0_2*(a_2 + r0*(-2*M + r0)));\n    A01260 = (-16*(-2*a_11*E*L*M*(192*(-7 + 3*E_2)*M_4 + 8*(17 + 132*E_2 + 18*E_4)*M_3*r0 + (1748 + 651*E_2 + 144*E_4)*M_2*r0_2 + 6*(160 + 23*E_2 + 6*E_4)*M*r0_3 + 144*r0_4) + a_12*E_2*M*(192*(-7 + 2*E_2)*M_4 + 80*(25 + 4*E_2)*M_3*r0 + (4279 - 56*E_2)*M_2*r0_2 + 3*(695 - 32*E_2)*M*r0_3 - 18*(-17 + E_2)*r0_4) + 2*a*E*L*M*r0_4*(-168*L_8*r0_2M_2 - 2*L_6*(2*M - r0)*r0*(144*M_2 + 2*(202 + 147*E_2)*M*r0 - 3*(76 + 7*E_2)*r0_2) + 2*L_4*r0_3*(-3024*M_3 - 16*(-125 + 64*E_2)*M_2*r0 + 2*(52 + 265*E_2 + 21*E_4)*M*r0_2 - 3*(58 + E_2)*r0_3) + L_2*r0_5*(-9984*M_3 - 64*(-137 + 21*E_2)*M_2*r0 + 2*(-908 + 321*E_2 + 60*E_4)*M*r0_2 + 3*(-12 + 5*E_2)*r0_3) - 2*r0_7*(2256*M_3 + 2*(-1028 + 151*E_2)*M_2*r0 + (488 - 205*E_2)*M*r0_2 + 3*(-4 + 11*E_2)*r0_3)) + r0_5*(24*L_8*M*r0_2M_2*(26*M + (-5 + 14*E_2)*r0) + 2*E_2*r0_9*(-64*M_3 + (84 - 8*E_2)*M_2*r0 + 4*(-8 + 5*E_2)*M*r0_2 + 3*r0_3) + 4*L_6*(2*M - r0)*r0_2*(1008*M_3 + 4*(-180 + 67*E_2)*M_2*r0 + (108 - 154*E_2 - 21*E_4)*M*r0_2 - 6*E_2*r0_3) + 2*L_2*r0_6*(1536*M_4 - 4*(480 + 61*E_2)*M_3*r0 + 12*(64 + 13*E_2 - 7*E_4)*M_2*r0_2 + (-96 - 29*E_2 + 66*E_4)*M*r0_3 + 6*E_2*r0_4) + L_4*r0_4*(8640*M_4 + 16*(-666 + 23*E_2)*M_3*r0 - 4*(-1044 + 177*E_2 + 86*E_4)*M_2*r0_2 + 4*(-126 + 46*E_2 + 47*E_4)*M*r0_3 + 39*E_2*r0_4)) + 4*a_3*E*L*M*r0_3*(L_6*(-896*M_3 + 12*(104 - 63*E_2)*M_2*r0 + 2*(160 + 147*E_2)*M*r0_2 - 3*(100 + 7*E_2)*r0_3) + r0_5*(-12672*M_4 - 168*(-23 + 20*E_2)*M_3*r0 + 4*(1311 + 68*E_2 + 27*E_4)*M_2*r0_2 + (-1880 + 395*E_2 - 18*E_4)*M*r0_3 - 103*E_2*r0_4) - 2*L_4*r0*(2784*M_4 + 4*(61 + 280*E_2)*M_3*r0 - 2*(1461 - 413*E_2 + 21*E_4)*M_2*r0_2 - 2*(-158 + 227*E_2 + 21*E_4)*M*r0_3 + 3*(93 + 7*E_2)*r0_4) - 2*L_2*r0_3*(9312*M_4 + 18*(-128 + 135*E_2)*M_3*r0 - 6*(822 - 66*E_2 + 29*E_4)*M_2*r0_2 + (1458 - 355*E_2 - 75*E_4)*M*r0_3 + (120 + 11*E_2)*r0_4)) + a_2*r0_3*(-24*L_8*M*(2*M - r0)*(24*M_2 + 7*(4 - 11*E_2)*M*r0 + (-11 + 14*E_2)*r0_2) + 2*E_2*r0_8*(4068*M_4 + 4*(-599 + 320*E_2)*M_3*r0 + (25 - 62*E_2)*M_2*r0_2 + (50 + 79*E_2)*M*r0_3 + 12*r0_4) + 2*L_6*r0*(8928*M_5 + 8*(-954 + 133*E_2)*M_4*r0 + 12*(-222 + 261*E_2 + 28*E_4)*M_3*r0_2 - 6*(-558 + 571*E_2 + 28*E_4)*M_2*r0_3 + 3*(-204 + 251*E_2 + 28*E_4)*M*r0_4 + 12*E_2*r0_5) + 2*L_4*r0_3*(19008*M_5 + 16*(-918 + 359*E_2)*M_4*r0 + 4*(-1044 - 635*E_2 + 143*E_4)*M_3*r0_2 + 2*(2592 - 1001*E_2 + 14*E_4)*M_2*r0_3 + (-900 + 814*E_2 + 265*E_4)*M*r0_4 + 42*E_2*r0_5) + L_2*r0_5*(20160*M_5 + 96*(-159 + 190*E_2)*M_4*r0 + 4*(-948 - 3251*E_2 + 780*E_4)*M_3*r0_2 + 4*(1218 + 57*E_2 + 116*E_4)*M_2*r0_3 + (-840 + 747*E_2 + 464*E_4)*M*r0_4 + 51*E_2*r0_5)) + a_10*M*(L_2*(192*(-7 + 6*E_2)*M_4 + 96*(-18 + 16*E_2 + 13*E_4)*M_3*r0 + 2*(-360 + 3083*E_2 + 416*E_4)*M_2*r0_2 + 3*(-32 + 1571*E_2 + 12*E_4)*M*r0_3 + 18*E_2*(51 - 2*E_2)*r0_4) + E_2*r0*(4352*(-3 + 2*E_2)*M_5 + 32*(-1134 + 439*E_2)*M_4*r0 + 2*(-11145 + 3832*E_2)*M_3*r0_2 + (6651 + 896*E_2)*M_2*r0_3 + (8285 - 412*E_2)*M*r0_4 + 2*(766 - 37*E_2)*r0_5)) - 2*a_9*E*L*M*(2*L_2*(96*M_4 + 8*(-62 + 63*E_2)*M_3*r0 + 2*(421 + 276*E_2 + 18*E_4)*M_2*r0_2 + 3*(332 + 55*E_2 + 6*E_4)*M*r0_3 + 216*r0_4) + r0*(768*(-17 + 14*E_2)*M_5 - 32*(714 - 445*E_2 + 27*E_4)*M_4*r0 - 4*(2520 - 1757*E_2 + 144*E_4)*M_3*r0_2 + (3844 + 2423*E_2 + 216*E_4)*M_2*r0_3 + 2*(1712 + 351*E_2 + 72*E_4)*M*r0_4 + 36*(14 + E_2)*r0_5)) + 2*a_5*E*L*M*r0_2*(-24*L_6*(2*M_2 + 3*M*r0 + 6*r0_2) + 2*L_4*(192*M_4 - 8*(-598 + 471*E_2)*M_3*r0 + 4*(783 - 263*E_2 + 21*E_4)*M_2*r0_2 + 2*(-638 + 105*E_2 + 21*E_4)*M*r0_3 - 3*(200 + 13*E_2)*r0_4) - r0_3*(48384*M_5 + 12*(1148 + 1219*E_2)*M_4*r0 - 4*(9036 - 3125*E_2 + 216*E_4)*M_3*r0_2 - (7716 + 1571*E_2 + 504*E_4)*M_2*r0_3 + 12*(508 - 3*E_2 + 12*E_4)*M*r0_4 + 2*(144 + 125*E_2)*r0_5) - L_2*r0*(35712*M_5 + 48*(239 + 200*E_2)*M_4*r0 - 8*(5006 - 2319*E_2 + 114*E_4)*M_3*r0_2 - 4*(3319 - 528*E_2 + 306*E_4)*M_2*r0_3 - 6*(-1328 + 39*E_2 + 34*E_4)*M*r0_4 + (1284 + 131*E_2)*r0_5)) + a_4*r0_2*(-96*L_8*M_2*r0 - 4*L_6*M*(1152*M_4 - 24*(-90 + 167*E_2)*M_3*r0 - 4*(261 - 212*E_2 + 21*E_4)*M_2*r0_2 + 2*(-243 + 506*E_2 + 21*E_4)*M*r0_3 + (198 - 299*E_2 - 21*E_4)*r0_4) + 2*E_2*r0_6*(19248*M_5 + 24*(61 + 277*E_2)*M_4*r0 + 4*(-2703 + 1021*E_2)*M_3*r0_2 + 4*(163 - 42*E_2)*M_2*r0_3 + (649 + 107*E_2)*M*r0_4 + 18*r0_5) + L_4*r0*(41472*M_6 + 96*(-156 + 85*E_2)*M_5*r0 + 16*(-2376 + 1608*E_2 + 229*E_4)*M_4*r0_2 + 8*(1476 - 2762*E_2 + 441*E_4)*M_3*r0_3 + 4*(1692 - 772*E_2 + 255*E_4)*M_2*r0_4 + 2*(-1044 + 1742*E_2 + 239*E_4)*M*r0_5 + 45*E_2*r0_6) + L_2*r0_3*(43776*M_6 + 120*(-96 + 425*E_2)*M_5*r0 + 4*(-7632 + 2413*E_2 + 3552*E_4)*M_4*r0_2 + 2*(4104 - 20579*E_2 + 5644*E_4)*M_3*r0_3 + (4872 + 3235*E_2 + 2076*E_4)*M_2*r0_4 + 6*(-228 + 585*E_2 + 94*E_4)*M*r0_5 + 66*E_2*r0_6)) + 2*a_7*E*L*M*r0*(-8*L_4*(24*M_3 + (-2 + 45*E_2)*M_2*r0 + 6*(23 + 4*E_2)*M*r0_2 + 54*r0_3) + 2*L_2*(1280*M_5 - 48*(-214 + 153*E_2)*M_4*r0 + 8*(1387 - 795*E_2 + 51*E_4)*M_3*r0_2 + 4*(109 - 414*E_2 + 57*E_4)*M_2*r0_3 - (2972 + 435*E_2 + 6*E_4)*M*r0_4 - 12*(53 + 3*E_2)*r0_5) - r0*(31488*M_6 + 32*(835 + 286*E_2)*M_5*r0 - 4*(7996 - 6067*E_2 + 216*E_4)*M_4*r0_2 - 8*(4217 - 1475*E_2 + 198*E_4)*M_3*r0_3 + (756 + 1237*E_2 - 216*E_4)*M_2*r0_4 + 4*(1444 + 227*E_2 + 54*E_4)*M*r0_5 + 2*(312 + 73*E_2)*r0_6)) + a_8*r0*(6*L_4*M*(48*(-6 + 5*E_2)*M_3 + 8*(-30 + 49*E_2 + 11*E_4)*M_2*r0 + 2*(-24 + 266*E_2 + 11*E_4)*M*r0_2 - 3*E_2*(-51 + E_2)*r0_3) + L_2*M*(768*(-17 + 16*E_2)*M_5 + 32*(-294 - 185*E_2 + 246*E_4)*M_4*r0 + 8*(276 - 4461*E_2 + 1490*E_4)*M_3*r0_2 + 6*(328 - 971*E_2 + 972*E_4)*M_2*r0_3 + (-456 + 13057*E_2 + 748*E_4)*M*r0_4 - 2*(132 - 1809*E_2 + 20*E_4)*r0_5) + E_2*r0*(31488*M_7 + 64*(721 + 233*E_2)*M_6*r0 + 4*(-7347 + 9304*E_2)*M_5*r0_2 + 24*(-2775 + 1126*E_2)*M_4*r0_3 + (-11815 + 5264*E_2)*M_3*r0_4 + (12023 - 672*E_2)*M_2*r0_5 + 4*(763 - 19*E_2)*M*r0_6 + 6*r0_7)) + a_6*r0*(6*L_6*M*r0*(24*(-5 + 3*E_2)*M_2 + 2*(-24 + 47*E_2)*M*r0 + 51*E_2*r0_2) + 2*L_4*M*(-2304*M_5 + 288*(-36 + 41*E_2)*M_4*r0 + 8*(-144 + 117*E_2 + 134*E_4)*M_3*r0_2 + 4*(864 - 1935*E_2 + 308*E_4)*M_2*r0_3 + 8*(18 + 202*E_2 + 47*E_4)*M*r0_4 + (-396 + 1473*E_2 + 59*E_4)*r0_5) + E_2*r0_4*(59816*M_6 + 20*(2283 + 1184*E_2)*M_5*r0 + 2*(-23783 + 16360*E_2)*M_4*r0_2 + (-31147 + 9920*E_2)*M_3*r0_3 + (7245 - 584*E_2)*M_2*r0_4 + 4*(740 + 19*E_2)*M*r0_5 + 24*r0_6) + L_2*r0*(31488*M_7 + 64*(114 + 521*E_2)*M_6*r0 + 8*(-4800 + 6155*E_2 + 1716*E_4)*M_5*r0_2 + 4*(-1920 - 11123*E_2 + 6356*E_4)*M_4*r0_3 + 2*(5424 - 20299*E_2 + 6552*E_4)*M_3*r0_4 + (1176 + 11723*E_2 + 2156*E_4)*M_2*r0_5 + (-984 + 5405*E_2 + 228*E_4)*M*r0_6 + 27*E_2*r0_7))))/(3.*r0_8*expr2_2);\n    A01420 = (-8*E*r0*(2*a_3*L*M*(12*M + (6 + E_2)*r0) + a_4*E*(-24*M_2 - 9*M*r0 + 2*r0_2) + 6*a*L*M*r0*(2*L_2 + (2 + E_2)*r0_2) + E*r0_3*(3*r0_2*(-6*M + r0) + 4*L_2*(-5*M + r0)) + a_2*E*r0*(2*L_2*(-7*M + r0) + 5*r0*(-6*M_2 - 5*M*r0 + r0_2))))/(a_2 + r0*(-2*M + r0));\n    A01440 = (-4*(a_10*E_2*M*(288*(-5 + 2*E_2)*M_3 + 4*(761 + 80*E_2)*M_2*r0 + 3*(1287 - 40*E_2)*M*r0_2 + 3*(329 - 22*E_2)*r0_3) - 2*a_9*E*L*M*(288*(-5 + 3*E_2)*M_3 + 2*(437 + 360*E_2 + 72*E_4)*M_2*r0 + 12*(144 + 11*E_2 + 6*E_4)*M*r0_2 - 3*(-154 + E_2)*r0_3) + 2*a*E*L*M*r0_3*(-8*L_6*(104*M_2 - 106*M*r0 + 27*r0_2) - 2*L_4*r0*(1104*M_3 + 4*(65 + 158*E_2)*M_2*r0 - 10*(98 + 31*E_2)*M*r0_2 + (287 + 3*E_2)*r0_3) - r0_5*(6048*M_3 + 12*(-376 + 77*E_2)*M_2*r0 + 4*(109 - 147*E_2)*M*r0_2 + 11*(14 + 9*E_2)*r0_3) + L_2*r0_3*(-8448*M_3 + 12*(424 - 129*E_2)*M_2*r0 + 4*(139 + 165*E_2 + 15*E_4)*M*r0_2 + (-494 + 21*E_2)*r0_3)) + r0_4*(8*L_6*M*(2*M - r0)*(252*M_2 + 20*(-9 + 5*E_2)*M*r0 + (27 - 52*E_2)*r0_2) + E_2*r0_7*(-192*M_3 + 12*(25 - 2*E_2)*M_2*r0 + 4*(-32 + 15*E_2)*M*r0_2 + 13*r0_3) + 2*L_2*r0_4*(2304*M_4 - 12*(240 + 7*E_2)*M_3*r0 - 12*(-96 + 7*E_4)*M_2*r0_2 + (-144 + 13*E_2 + 66*E_4)*M*r0_3 + 4*E_2*r0_4) + 2*L_4*r0_2*(4320*M_4 + 4*(-1332 + 173*E_2)*M_3*r0 - 2*(-1044 + 402*E_2 + 43*E_4)*M_2*r0_2 + (-252 + 213*E_2 + 47*E_4)*M*r0_3 + 8*E_2*r0_4)) - 2*a_7*E*L*M*(L_2*(288*M_3 + 112*(-8 + 9*E_2)*M_2*r0 + 12*(132 + 31*E_2 + 3*E_4)*M*r0_2 + 3*(308 + E_2)*r0_3) + 4*r0*(288*(-7 + 6*E_2)*M_4 - 2*(1687 - 822*E_2 + 72*E_4)*M_3*r0 + (-919 + 545*E_2 + 36*E_4)*M_2*r0_2 + (949 + 85*E_2 + 72*E_4)*M*r0_3 + 2*(185 + 3*E_2)*r0_4)) + 2*a_3*E*L*M*r0_2*(-24*L_6*M - 4*L_4*(288*M_3 + 12*(-51 + 37*E_2)*M_2*r0 - 4*(95 + 23*E_2)*M*r0_2 + (259 + 3*E_2)*r0_3) + L_2*r0*(-15840*M_4 - 48*(11 + 82*E_2)*M_3*r0 + 4*(3008 - 881*E_2 + 84*E_4)*M_2*r0_2 + 4*(174 + 217*E_2 + 9*E_4)*M*r0_3 + 5*(-374 + 3*E_2)*r0_4) - 4*r0_3*(5688*M_4 + 6*(-132 + 241*E_2)*M_3*r0 + (-2846 + 229*E_2 - 108*E_4)*M_2*r0_2 + (363 - 214*E_2 + 36*E_4)*M*r0_3 + 4*(54 + 13*E_2)*r0_4)) + a_8*(3*L_2*M*(96*(-5 + 6*E_2)*M_3 + 16*(-27 + 13*E_2 + 26*E_4)*M_2*r0 + 4*(-24 + 344*E_2 + 35*E_4)*M*r0_2 + E_2*(657 - 20*E_2)*r0_3) + E_2*r0*(2688*(-3 + 2*E_2)*M_5 + 16*(-1597 + 422*E_2)*M_4*r0 + 56*(-159 + 49*E_2)*M_3*r0_2 + (9267 - 164*E_2)*M_2*r0_3 + 12*(333 - 20*E_2)*M*r0_4 + 7*r0_5)) + a_2*r0_2*(-2*L_6*M*(576*M_3 - 48*(-12 + 47*E_2)*M_2*r0 + 2*(-396 + 743*E_2)*M*r0_2 + (180 - 199*E_2)*r0_3) + E_2*r0_6*(9672*M_4 + 12*(-269 + 240*E_2)*M_3*r0 + 6*(-403 + 14*E_2)*M_2*r0_2 + 3*(229 + 46*E_2)*M*r0_3 + 46*r0_4) + 2*L_4*r0*(9504*M_5 + 48*(-159 + 35*E_2)*M_4*r0 + 4*(-666 + 791*E_2 + 32*E_4)*M_3*r0_2 + 4*(819 - 962*E_2 + 40*E_4)*M_2*r0_3 + (-612 + 927*E_2 + 73*E_4)*M*r0_4 + 16*E_2*r0_5) + L_2*r0_3*(20160*M_5 + 24*(-636 + 553*E_2)*M_4*r0 + 4*(-1044 - 715*E_2 + 366*E_4)*M_3*r0_2 + 2*(2628 - 3105*E_2 + 614*E_4)*M_2*r0_3 + (-936 + 2087*E_2 + 256*E_4)*M*r0_4 + 51*E_2*r0_5)) + 2*a_5*E*L*M*r0*(-6*L_4*(32*M_2 + 10*(-2 + 3*E_2)*M*r0 + (77 + E_2)*r0_2) + L_2*(768*M_4 + 16*(479 - 396*E_2)*M_3*r0 + 8*(1078 - 417*E_2 + 30*E_4)*M_2*r0_2 - 4*(340 + 53*E_2 + 15*E_4)*M*r0_3 - (2300 + 9*E_2)*r0_4) - 2*r0*(10944*M_5 + 12*(403 + 244*E_2)*M_4*r0 - 4*(2437 - 1219*E_2 + 108*E_4)*M_3*r0_2 + (-5591 + 798*E_2 - 216*E_4)*M_2*r0_3 + 2*(771 - 25*E_2 + 90*E_4)*M*r0_4 + 4*(216 + 17*E_2)*r0_5)) + a_6*r0*(6*L_4*M*(24*(-9 + 10*E_2)*M_2 + 2*(-48 + 43*E_2 + 22*E_4)*M*r0 + E_2*(165 + E_2)*r0_2) + L_2*(1152*(-7 + 8*E_2)*M_5 + 288*(-5 - 5*E_2 + 8*E_4)*M_4*r0 + 8*(270 - 2368*E_2 + 633*E_4)*M_3*r0_2 + 2*(-108 + 1085*E_2 + 994*E_4)*M_2*r0_3 + (-360 + 6049*E_2 - 128*E_4)*M*r0_4 + 23*E_2*r0_5) + E_2*r0*(21888*M_6 + 48*(523 + 188*E_2)*M_5*r0 + 4*(-6059 + 3924*E_2)*M_4*r0_2 + 16*(-1991 + 436*E_2)*M_3*r0_3 + (4005 + 196*E_2)*M_2*r0_4 + 2*(2987 - 132*E_2)*M*r0_5 + 34*r0_6)) + a_4*r0*(6*L_6*M*r0*(12*(-4 + 3*E_2)*M + E_2*r0) + 3*E_2*r0_4*(9776*M_5 + 4*(923 + 828*E_2)*M_4*r0 + 8*(-958 + 307*E_2)*M_3*r0_2 + (-1373 + 116*E_2)*M_2*r0_3 - 4*(-315 + E_2)*M*r0_4 + 20*r0_5) + 2*L_4*(-1152*M_5 + 288*(-13 + 22*E_2)*M_4*r0 + 4*(414 - 231*E_2 + 28*E_4)*M_3*r0_2 + 2*(342 - 1403*E_2 + 165*E_4)*M_2*r0_3 + (-360 + 1209*E_2 + 29*E_4)*M*r0_4 + 8*E_2*r0_5) + L_2*r0*(21888*M_6 + 1920*(-3 + 8*E_2)*M_5*r0 + 32*(-576 + 629*E_2 + 99*E_4)*M_4*r0_2 + 4*(1332 - 5551*E_2 + 1448*E_4)*M_3*r0_3 + 4*(756 - 2051*E_2 + 741*E_4)*M_2*r0_4 + (-1008 + 6163*E_2 + 56*E_4)*M*r0_5 + 66*E_2*r0_6))))/(3.*r0_5*expr2_2);\n    A01600 = (-2*E*r0_4*(4*a*L*M + E*r0_2*(-6*M + r0) + a_2*E*(-4*M + r0)))/(a_2 + r0*(-2*M + r0));\n    A01620 = (6*a_7*E*L*M*(16*(-13 + 9*E_2)*M_2 + 4*(62 + 6*E_2 + 3*E_4)*M*r0 + (164 - 11*E_2)*r0_2) - 3*a_8*E_2*(16*(-13 + 6*E_2)*M_3 + 592*M_2*r0 + (337 - 26*E_2)*M*r0_2 - 3*r0_3) + 2*a_5*E*L*M*(12*L_2*(12*M_2 + 3*(-6 + 7*E_2)*M*r0 + (41 - 2*E_2)*r0_2) + r0*(192*(-11 + 10*E_2)*M_3 - 8*(503 - 151*E_2 + 15*E_4)*M_2*r0 + 8*(13 + 13*E_2 + 15*E_4)*M*r0_2 + (1196 - 63*E_2)*r0_3)) + 2*a*E*L*M*r0_2*(8*L_4*(52*M_2 - 56*M*r0 + 15*r0_2) + 2*L_2*r0*(1152*M_3 + 4*(-48 + 73*E_2)*M_2*r0 - 4*(137 + 23*E_2)*M*r0_2 + (178 - 15*E_2)*r0_3) + r0_3*(3552*M_3 + 4*(-476 + 157*E_2)*M_2*r0 - 8*(51 + 43*E_2)*M*r0_2 + (236 + 51*E_2)*r0_3)) + a_6*(L_2*((624 - 864*E_2)*M_3 + 24*(12 - 13*E_4)*M_2*r0 + 12*E_2*(-83 + 2*E_2)*M*r0_2 + 9*E_2*r0_3) + E_2*r0*(-704*(-3 + 2*E_2)*M_4 + 8*(1108 - 187*E_2)*M_3*r0 - 2*(229 + 150*E_2)*M_2*r0_2 + (-3107 + 210*E_2)*M*r0_3 + 19*r0_4)) + r0_3*(-8*L_4*M*(2*M - r0)*(180*M_2 + 2*(-66 + 25*E_2)*M*r0 + (21 - 29*E_2)*r0_2) - 8*E_2*r0_5*(-16*M_3 - 2*(-12 + E_2)*M_2*r0 + 5*(-2 + E_2)*M*r0_2 + r0_3) + L_2*r0_2*(-3072*M_4 + 24*(160 - 11*E_2)*M_3*r0 + 4*(-384 + 105*E_2 + 14*E_4)*M_2*r0_2 - 2*(-96 + 79*E_2 + 22*E_4)*M*r0_3 + 7*E_2*r0_4)) + 2*a_3*E*L*M*r0*(48*L_4*M + 2*L_2*(64*M_3 + 12*(-44 + 37*E_2)*M_2*r0 + 8*(-93 + 8*E_2)*M*r0_2 + (424 - 27*E_2)*r0_3) + r0*(6720*M_4 + 16*(29 + 101*E_2)*M_3*r0 - 4*(1094 - 283*E_2 + 54*E_4)*M_2*r0_2 + 4*(-250 - 54*E_2 + 21*E_4)*M*r0_3 + (940 + 21*E_2)*r0_4)) + a_4*r0*(24*L_4*M*(-3*(-4 + 5*E_2)*M + E_2*r0) + L_2*(-192*(-11 + 16*E_2)*M_4 - 8*(102 - 53*E_2 + 14*E_4)*M_3*r0 - 4*(66 - 995*E_2 + 234*E_4)*M_2*r0_2 + 2*(108 - 1081*E_2 + 26*E_4)*M*r0_3 + E_2*r0_4) + E_2*r0*(-6720*M_5 - 16*(376 + 137*E_2)*M_4*r0 + 4*(2119 - 590*E_2)*M_3*r0_2 + 24*(197 - 23*E_2)*M_2*r0_3 + (-3101 + 146*E_2)*M*r0_4 + 3*r0_5)) - a_2*r0*(-8*L_4*M*(48*M_3 + 6*(16 - 47*E_2)*M_2*r0 + (-114 + 167*E_2)*M*r0_2 + (27 - 23*E_2)*r0_3) + E_2*r0_4*(4760*M_4 + 4*(43 + 320*E_2)*M_3*r0 + 2*(-1609 + 118*E_2)*M_2*r0_2 + (925 + 26*E_2)*M*r0_3 + 15*r0_4) + L_2*r0*(6720*M_5 + 16*(-318 + 173*E_2)*M_4*r0 - 16*(111 - 168*E_2 + 4*E_4)*M_3*r0_2 + 8*(267 - 552*E_2 + 83*E_4)*M_2*r0_3 + 4*(-102 + 319*E_2 + 4*E_4)*M*r0_4 + E_2*r0_5)))/(3.*r0_2*expr2_2);\n    A01800 = (r0*(12*a_5*E*L*M*(4*(-4 + 3*E_2)*M + (29 - 5*E_2)*r0) + 3*a_6*E_2*(-16*(-2 + E_2)*M_2 + 2*(-58 + 5*E_2)*M*r0 + 3*r0_2) + 8*a_3*E*L*M*(6*L_2*M + r0*(48*(-1 + E_2)*M_2 + (-134 + 19*E_2)*M*r0 + (67 - 9*E_2)*r0_2)) - r0_2*(8*L_2*M*(2*M - r0)*(48*M_2 + 2*(-18 + 5*E_2)*M*r0 + (6 - 7*E_2)*r0_2) + E_2*r0_3*(-32*M_3 - 4*(-9 + E_2)*M_2*r0 + 2*(-6 + 5*E_2)*M*r0_2 + r0_3)) + a_4*(6*L_2*M*(-8*(-2 + 3*E_2)*M + 5*E_2*r0) + E_2*r0*(-64*(-3 + 2*E_2)*M_3 - 4*(-316 + 39*E_2)*M_2*r0 + 2*(-362 + 25*E_2)*M*r0_2 + 17*r0_3)) + 4*a*E*L*M*r0*(4*L_2*(8*M_2 - 10*M*r0 + 3*r0_2) + r0*(384*M_3 + 20*(-5 + 4*E_2)*M_2*r0 - 2*(70 + 11*E_2)*M*r0_2 + (47 - 3*E_2)*r0_3)) - a_2*r0*(2*L_2*M*(96*(-1 + 2*E_2)*M_2 + (96 - 82*E_2)*M*r0 + (-24 + 13*E_2)*r0_2) + E_2*r0*(768*M_4 + 80*(7 + 2*E_2)*M_3*r0 + 4*(-293 + 26*E_2)*M_2*r0_2 + 2*(182 - 5*E_2)*M*r0_3 - 7*r0_4))))/(12.*expr2_2);\n    A02040 = (192*E_2*expr1_2)/(a_2 + r0*(-2*M + r0));\n    A02060 = (32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-4*a_7*E*L*M*(48*M_3 + 12*(4 + 3*E_2)*M_2*r0 + (-12 + 35*E_2)*M*r0_2 + 3*(-4 + E_2)*r0_3) + a_8*E_2*M*(96*M_3 + 24*(4 + E_2)*M_2*r0 + (-73 + 36*E_2)*M*r0_2 + 6*(-9 + 2*E_2)*r0_3) - 4*a*E*L*M*r0_3*(-6*L_4*(20*M_2 - 21*M*r0 + 5*r0_2) + L_2*r0_2*(-384*M_2 + (348 + 139*E_2)*M*r0 - 36*(2 + E_2)*r0_2) + r0_4*(-264*M_2 + (222 + 89*E_2)*M*r0 - 3*(14 + 5*E_2)*r0_2)) + r0_3*(-96*L_6*M*r0_2M_2 + E_2*r0_7*(184*M_2 - 2*(55 + 2*E_2)*M*r0 + 9*r0_2) - 8*L_4*(2*M - r0)*r0_2*(48*M_2 - 2*(12 + 13*E_2)*M*r0 + 3*E_2*r0_2) + 2*L_2*r0_4*(-192*M_3 + 6*(32 + 49*E_2)*M_2*r0 + (-48 - 177*E_2 + 2*E_4)*M*r0_2 + 15*E_2*r0_3)) - 4*a_3*E*L*M*r0_2*(12*L_4*(2*M - r0) + L_2*(-624*M_3 + 56*(3 + E_2)*M_2*r0 + (264 + 157*E_2)*M*r0_2 - 3*(32 + E_2)*r0_3) + r0_2*(-912*M_3 + 4*(54 + 5*E_2)*M_2*r0 + (312 + 173*E_2)*M*r0_2 - (96 + 11*E_2)*r0_3)) + 2*a_2*r0_2*(12*L_6*M_2 + E_2*r0_5*(-250*M_3 + 3*(99 - 26*E_2)*M_2*r0 - 165*M*r0_2 + 6*r0_3) + L_4*(-768*M_4 + 16*(24 + 5*E_2)*M_3*r0 + 6*(36 + 49*E_2)*M_2*r0_2 - 32*(3 + 4*E_2)*M*r0_3 - 6*E_2*r0_4) + L_2*r0_2*(-768*M_4 + (384 - 98*E_2)*M_3*r0 + (204 + 627*E_2 + 104*E_4)*M_2*r0_2 + (-96 - 345*E_2 + 10*E_4)*M*r0_3 + 6*E_2*r0_4)) + 2*a_6*(L_2*M*(48*M_3 + 12*(4 + 13*E_2)*M_2*r0 + 4*(3 + 13*E_2 + 6*E_4)*M*r0_2 + 3*E_2*(-17 + 2*E_2)*r0_3) + E_2*r0*(-768*M_5 - 16*(39 + 4*E_2)*M_4*r0 - 120*(-2 + E_2)*M_3*r0_2 - 6*(-2 + 3*E_2)*M_2*r0_3 + (-139 + 16*E_2)*M*r0_4 - 3*r0_5)) + a_4*r0*(24*L_4*M*(4*M_2 + (2 + 7*E_2)*M*r0 - 2*E_2*r0_2) + E_2*r0_3*(-2108*M_4 + 20*(1 - 18*E_2)*M_3*r0 + 3*(189 - 76*E_2)*M_2*r0_2 + 12*(-37 + 2*E_2)*M*r0_3 - 3*r0_4) - 2*L_2*(768*M_5 + 256*E_2*M_4*r0 + 8*(-54 - 43*E_2 + 2*E_4)*M_3*r0_2 - (24 + 349*E_2 + 80*E_4)*M_2*r0_3 + (48 + 267*E_2 - 14*E_4)*M*r0_4 + 9*E_2*r0_5)) - 4*a_5*E*L*M*r0*(-768*M_4 + 8*(-39 + 10*E_2)*M_3*r0 + 136*(3 + E_2)*M_2*r0_2 + (78 + 151*E_2)*M*r0_3 + (-66 + 7*E_2)*r0_4 + 3*L_2*(24*M_2 + (-8 + E_2)*r0_2 + 4*M*(r0 + 3*E_2*r0)))))/(3.*r0_7*expr2_2);\n    A02220 = (96*E_2*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(a_2 + r0*(-2*M + r0));\n    A02240 = (-8*(4*a_7*E*L*M*(144*M_3 + 24*(9 + E_2)*M_2*r0 + 5*(12 + E_2)*M*r0_2 - 3*(2 + 3*E_2)*r0_3) + a_8*E_2*(-288*M_4 + 48*(-12 + E_2)*M_3*r0 + (-119 + 24*E_2)*M_2*r0_2 + 66*M*r0_3 + 6*r0_4) + 4*a*E*L*M*r0_3*(L_4*(-432*M_2 + 2*(165 + 2*E_2)*M*r0 - 48*r0_2) - 6*r0_4*(120*M_2 - 3*(29 + 11*E_2)*M*r0 + (12 + 7*E_2)*r0_2) - 3*L_2*r0_2*(384*M_2 - 4*(71 + 21*E_2)*M*r0 + (40 + 21*E_2)*r0_2)) + r0_3*(4*L_6*M*(2*M - r0)*(144*M - (72 + E_2)*r0) + 3*E_2*r0_7*(-184*M_2 + 2*(59 + 2*E_2)*M*r0 - 13*r0_2) + 36*L_2*(2*M - r0)*r0_4*(16*M_2 - 2*(4 + 11*E_2)*M*r0 + 3*E_2*r0_2) + 2*L_4*r0_2*(1152*M_3 - 2*(576 + 263*E_2)*M_2*r0 + (288 + 335*E_2 - 2*E_4)*M*r0_2 - 36*E_2*r0_3)) + 4*a_3*E*L*M*r0_2*(L_4*(60*M - 6*r0) - 6*L_2*(336*M_3 - 4*(11 + 6*E_2)*M_2*r0 - (126 + 37*E_2)*M*r0_2 + 2*(11 + 4*E_2)*r0_3) + r0_2*(-2592*M_3 + 6*(60 + 53*E_2)*M_2*r0 + (840 + 289*E_2)*M*r0_2 - (150 + 83*E_2)*r0_3)) - 2*a_2*r0_2*(36*L_6*M_2 - 3*E_2*r0_5*(108*M_3 + 2*(-77 + 24*E_2)*M_2*r0 + (143 + 6*E_2)*M*r0_2 - 15*r0_3) + L_4*(-2304*M_4 + 1152*M_3*r0 + 4*(162 + 123*E_2 + E_4)*M_2*r0_2 + (-288 - 287*E_2 + 2*E_4)*M*r0_3 + 15*E_2*r0_4) + 3*L_2*r0_2*(-768*M_4 + 384*M_3*r0 + 6*(34 + 53*E_2 + 3*E_4)*M_2*r0_2 - (96 + 277*E_2)*M*r0_3 + 25*E_2*r0_4)) - 2*a_6*(L_2*(144*M_4 + 24*(6 + 13*E_2)*M_3*r0 + 2*(18 + 95*E_2 + 3*E_4)*M_2*r0_2 - 63*E_2*M*r0_3 - 6*E_2*r0_4) - E_2*M*r0*(2304*M_4 + 48*(45 + E_2)*M_3*r0 + 2*(-149 + 108*E_2)*M_2*r0_2 + (-193 + 120*E_2)*M*r0_3 + 3*(61 + 2*E_2)*r0_4)) + 4*a_5*E*L*M*r0*(3*L_2*(64*M_2 + 10*(4 + E_2)*M*r0 - (4 + 3*E_2)*r0_2) + 2*(-1152*M_4 + 60*(-9 + 2*E_2)*M_3*r0 + (516 + 169*E_2)*M_2*r0_2 + (189 + 58*E_2)*M*r0_3 - (42 + 25*E_2)*r0_4)) + a_4*r0*(-6*L_4*(48*M_3 + 2*(12 + 23*E_2)*M_2*r0 - 10*E_2*M*r0_2 - E_2*r0_3) + 3*E_2*r0_3*(1804*M_4 + 4*(61 + 48*E_2)*M_3*r0 + (-241 + 168*E_2)*M_2*r0_2 + 4*(67 + 3*E_2)*M*r0_3 - 19*r0_4) + 2*L_2*(2304*M_5 + 1200*E_2*M_4*r0 - 4*(324 + 67*E_2)*M_3*r0_2 - 4*(18 + 121*E_2 + 9*E_4)*M_2*r0_3 + 12*(12 + 37*E_2)*M*r0_4 - 15*E_2*r0_5))))/(3.*r0_4*expr2_2);\n    A02400 = (12*E_2*r0_6)/(a_2 + r0*(-2*M + r0));\n    A02420 = (-2*(-24*a_5*E*L*M*(-12*M_2 + (-18 + 5*E_2)*M*r0 + 4*(-1 + E_2)*r0_2) + 3*a_6*E_2*(-48*M_3 + 4*(-30 + 7*E_2)*M_2*r0 + 2*(-11 + 6*E_2)*M*r0_2 + 3*r0_3) - 12*a_3*E*L*M*r0*(384*M_3 + 4*(3 - 10*E_2)*M_2*r0 - (134 + 19*E_2)*M*r0_2 + (-2 + 23*E_2)*r0_3 - 4*L_2*(4*M - (-2 + E_2)*r0)) + r0_2*(24*L_4*M*(2*M - r0)*(24*M - (12 + E_2)*r0) + E_2*r0_5*(-552*M_2 + 2*(179 + 6*E_2)*M*r0 - 41*r0_2) + L_2*(2*M - r0)*r0_2*(576*M_2 - 6*(48 + 71*E_2)*M*r0 + 59*E_2*r0_2)) + 12*a*E*L*M*r0_2*(-2*L_2*(84*M_2 - (47 + 4*E_2)*M*r0 + (1 + 2*E_2)*r0_2) + r0_2*(-216*M_2 + (126 + 43*E_2)*M*r0 - (6 + 13*E_2)*r0_2)) + a_4*(-3*L_2*(48*M_3 + 4*(6 + 7*E_2)*M_2*r0 + 4*E_2*(3 - 2*E_2)*M*r0_2 - 3*E_2*r0_3) + E_2*r0*(2304*M_4 - 48*(-27 + 2*E_2)*M_3*r0 + 6*(-53 + 20*E_2)*M_2*r0_2 + 2*(19 + 42*E_2)*M*r0_3 - 17*r0_4)) + a_2*r0*(24*L_4*M*(-3*M + E_2*r0) + E_2*r0_3*(900*M_3 + 2*(-167 + 42*E_2)*M_2*r0 + 30*(15 + 2*E_2)*M*r0_2 - 67*r0_3) + 2*L_2*(1152*M_4 + 48*(-12 + 5*E_2)*M_3*r0 - 6*(54 + 27*E_2 + 4*E_4)*M_2*r0_2 + 6*(24 + 31*E_2 + 2*E_4)*M*r0_3 - 25*E_2*r0_4))))/(3.*r0*expr2_2);\n    A02600 = (r0_2*(24*a_3*E*L*M*(-2*M + (-3 + 2*E_2)*r0) - 3*a_4*E_2*(-8*M_2 + 8*(-3 + E_2)*M*r0 + r0_2) - 8*a*E*L*M*r0*(-96*M_2 + (39 + 10*E_2)*M*r0 - 6*(-1 + E_2)*r0_2) + r0*(-4*L_2*M*(2*M - r0)*(48*M - (24 + 5*E_2)*r0) + E_2*r0_3*(184*M_2 - 2*(57 + 2*E_2)*M*r0 + 11*r0_2)) + 2*a_2*(12*L_2*M*(M - E_2*r0) - E_2*r0*(192*M_3 - 4*(-9 + 5*E_2)*M_2*r0 + 7*(1 + 2*E_2)*M*r0_2 - 4*r0_3))))/(6.*expr2_2);\n    A03040 = (-32*E*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(4*a_3*L*M*(6*M + (3 + E_2)*r0) + a_4*E*(-24*M_2 - 12*M*r0 + r0_2) + 12*a*L*M*r0*(L_2 + (1 + E_2)*r0_2) + E*r0_3*(r0_2*(-17*M + 2*r0) + L_2*(-21*M + 4*r0)) + a_2*E*r0*(L_2*(-16*M + r0) + r0*(-22*M_2 - 27*M*r0 + 3*r0_2))))/(r0_2*expr2_2);\n    A03060 = (-16*(4*a_11*E*L*M*(48*(-1 + 4*E_2)*M_4 + 2*(-265 + 18*E_2 + 18*E_4)*M_3*r0 + (-421 - 189*E_2 + 54*E_4)*M_2*r0_2 + 3*(-12 - 31*E_2 + 6*E_4)*M*r0_3 + 24*r0_4) + a_12*E_2*M*(-96*(-1 + 2*E_2)*M_4 - 8*(-205 + 83*E_2)*M_3*r0 + 4*(325 - 137*E_2)*M_2*r0_2 + 3*(19 - 40*E_2)*M*r0_3 + 6*(-17 + E_2)*r0_4) + 2*a*E*L*M*r0_4*(-168*L_8*r0_2M_2 + r0_7*(-4560*M_3 - 8*(-664 + 33*E_2)*M_2*r0 + 4*(-505 + 67*E_2 + 27*E_4)*M*r0_2 + 3*(82 - 19*E_2)*r0_3) + L_2*r0_5*(-10224*M_3 + 4*(3194 - 127*E_2)*M_2*r0 + 4*(-1318 + 91*E_2 + 57*E_4)*M*r0_2 - 3*(-234 + E_2)*r0_3) - 2*L_6*r0*(360*M_3 + 4*(-44 + 147*E_2)*M_2*r0 - 28*(5 + 12*E_2)*M*r0_2 + 3*(24 + 7*E_2)*r0_3) + 2*L_4*r0_3*(-3192*M_3 + (4244 - 506*E_2)*M_2*r0 + 2*(-911 + 96*E_2 + 21*E_4)*M*r0_2 + 3*(80 + 17*E_2)*r0_3)) + r0_5*(24*L_8*M*r0_2M_2*(27*M + 2*(-3 + 7*E_2)*r0) + 3*E_2*r0_9*(-284*M_3 + (298 - 24*E_2)*M_2*r0 + 2*(-44 + 9*E_2)*M*r0_2 + 5*r0_3) + 2*L_6*(2*M - r0)*r0_2*(2088*M_3 + 2*(-774 + 143*E_2)*M_2*r0 + (252 - 169*E_2 - 42*E_4)*M*r0_2 - 24*E_2*r0_3) + L_2*r0_6*(3168*M_4 - 4*(1008 + 695*E_2)*M_3*r0 - 4*(-414 - 651*E_2 + 85*E_4)*M_2*r0_2 + (-216 - 685*E_2 + 184*E_4)*M*r0_3 + 39*E_2*r0_4) + L_4*r0_4*(8928*M_4 - 416*(27 + 5*E_2)*M_3*r0 + 2*(2268 + 867*E_2 - 230*E_4)*M_2*r0_2 + (-576 - 479*E_2 + 226*E_4)*M*r0_3 + 66*E_2*r0_4)) + 2*a_3*E*L*M*r0_3*(-2*L_6*(944*M_3 + 108*(-17 + 7*E_2)*M_2*r0 + 2*(194 - 147*E_2)*M*r0_2 + 3*(36 + 7*E_2)*r0_3) + r0_5*(-25728*M_4 + 12*(1146 - 695*E_2)*M_3*r0 + 2*(4542 + 1529*E_2 - 270*E_4)*M_2*r0_2 + 4*(-1696 + 46*E_2 + 135*E_4)*M*r0_3 + (1026 - 257*E_2)*r0_4) + 2*L_2*r0_3*(-19200*M_4 - 22*(-504 + 353*E_2)*M_3*r0 + (8880 + 3397*E_2 + 672*E_4)*M_2*r0_2 + 3*(-2270 + 69*E_2 + 128*E_4)*M*r0_3 + 2*(525 - 107*E_2)*r0_4) - 4*L_4*r0*(2976*M_4 + 2*(-751 + 718*E_2)*M_3*r0 - (3057 + 373*E_2 + 42*E_4)*M_2*r0_2 - 2*(-935 + 129*E_2 + 21*E_4)*M*r0_3 + 42*(-5 + 2*E_2)*r0_4)) - a_2*r0_3*(24*L_8*M*(48*M_3 + 2*(27 - 77*E_2)*M_2*r0 + (-62 + 105*E_2)*M*r0_2 + (11 - 14*E_2)*r0_3) - E_2*r0_8*(5880*M_4 + 4*(-1795 + 846*E_2)*M_3*r0 + 4*(1102 - 233*E_2)*M_2*r0_2 + (-1141 + 268*E_2)*M*r0_3 + 15*r0_4) + L_2*r0_5*(-20736*M_5 - 8*(-2016 + 1919*E_2)*M_4*r0 + 12*(344 + 2065*E_2 - 374*E_4)*M_3*r0_2 + 2*(-2676 - 6891*E_2 + 1052*E_4)*M_2*r0_3 + (912 + 2511*E_2 - 800*E_4)*M*r0_4 + 27*E_2*r0_5) + 2*L_6*r0*(-9216*M_5 - 64*(-126 + 25*E_2)*M_4*r0 + 4*(840 + 197*E_2 - 84*E_4)*M_3*r0_2 + 2*(-1938 + 385*E_2 + 84*E_4)*M_2*r0_3 + (648 - 419*E_2 - 84*E_4)*M*r0_4 + 36*E_2*r0_5) + L_4*r0_3*(-39168*M_5 - 24*(-1296 + 539*E_2)*M_4*r0 + 4*(2388 + 5793*E_2 + 358*E_4)*M_3*r0_2 + 4*(-2904 - 2653*E_2 + 18*E_4)*M_2*r0_3 + (1944 + 1021*E_2 - 732*E_4)*M*r0_4 + 81*E_2*r0_5)) + 4*a_9*E*L*M*(L_2*(192*M_4 + 4*(-110 + 129*E_2)*M_3*r0 + (-1037 + 114*E_2 + 48*E_4)*M_2*r0_2 + 3*(-84 - 59*E_2 + 6*E_4)*M*r0_3 + 72*r0_4) + r0*(-96*(-85 + 62*E_2)*M_5 + 8*(1302 - 899*E_2 + 36*E_4)*M_4*r0 + 2*(348 - 1145*E_2 + 234*E_4)*M_3*r0_2 + (-3053 - 736*E_2 + 468*E_4)*M_2*r0_3 + (-488 - 585*E_2 + 162*E_4)*M*r0_4 + 6*(36 - 5*E_2)*r0_5)) + a_10*M*(L_2*(-96*(-1 + 12*E_2)*M_4 - 24*(-20 - 83*E_2 + 38*E_4)*M_3*r0 + 4*(102 + 1171*E_2 - 326*E_4)*M_2*r0_2 + 3*(32 + 407*E_2 - 108*E_4)*M*r0_3 + 6*E_2*(-51 + 2*E_2)*r0_4) + E_2*r0*(64*(-255 + 148*E_2)*M_5 + 16*(-1818 + 935*E_2)*M_4*r0 + 2*(-4311 + 3016*E_2)*M_3*r0_2 + 11*(543 - 104*E_2)*M_2*r0_3 + (1397 - 676*E_2)*M*r0_4 + 2*(-329 + 38*E_2)*r0_5)) + a_4*r0_2*(-48*L_8*M_2*(M - 2*r0) - 2*L_6*M*(2304*M_4 + 24*(241 - 352*E_2)*M_3*r0 - 4*(525 - 1111*E_2 + 42*E_4)*M_2*r0_2 + 4*(-420 + 47*E_2 + 21*E_4)*M*r0_3 + (396 - 139*E_2 - 42*E_4)*r0_4) + E_2*r0_6*(34140*M_5 + 12*(-817 + 1278*E_2)*M_4*r0 + (-13965 + 8684*E_2)*M_3*r0_2 + 8*(1168 - 297*E_2)*M_2*r0_3 + (-2461 + 538*E_2)*M*r0_4 - 45*r0_5) + L_2*r0_3*(44928*M_6 + 4*(-3168 + 11983*E_2)*M_5*r0 + 4*(-8460 - 4609*E_2 + 6984*E_4)*M_4*r0_2 + (8904 - 34221*E_2 + 6296*E_4)*M_3*r0_3 + 4*(1482 + 6265*E_2 - 927*E_4)*M_2*r0_4 + 3*(-480 - 1561*E_2 + 436*E_4)*M*r0_5 - 171*E_2*r0_6) + L_4*r0*(42624*M_6 + 32*(-504 + 347*E_2)*M_5*r0 + 8*(-5532 + 1145*E_2 + 772*E_4)*M_4*r0_2 - 24*(-524 + 1207*E_2 + 148*E_4)*M_3*r0_3 + 4*(2262 + 3755*E_2 - 222*E_4)*M_2*r0_4 + 2*(-1080 - 907*E_2 + 396*E_4)*M*r0_5 - 147*E_2*r0_6)) + 2*a_7*E*L*M*r0*(8*L_4*(66*M_3 + (-133 + 75*E_2)*M_2*r0 - 3*(33 + 7*E_2)*M*r0_2 + 18*r0_3) + 2*L_2*(1088*M_5 - 96*(-139 + 87*E_2)*M_4*r0 + 4*(2231 - 1059*E_2 + 84*E_4)*M_3*r0_2 + (-4418 + 1167*E_2 + 624*E_4)*M_2*r0_3 + 2*(-1171 - 387*E_2 + 96*E_4)*M*r0_4 + 12*(41 - 8*E_2)*r0_5) - r0*(32256*M_6 + 16*(1307 + 588*E_2)*M_5*r0 + 8*(-5255 + 3282*E_2)*M_4*r0_2 + 4*(-6409 + 2653*E_2 + 18*E_4)*M_3*r0_3 - 6*(-1859 + 162*E_2 + 216*E_4)*M_2*r0_4 + 2*(2240 + 1119*E_2 - 432*E_4)*M*r0_5 + (-1254 + 263*E_2)*r0_6)) + a_8*(-6*L_4*M*(32*M_4 + 24*(-2 + 15*E_2)*M_3*r0 + 8*(-16 - 51*E_2 + 16*E_4)*M_2*r0_2 + 2*(-24 - 188*E_2 + 17*E_4)*M*r0_3 - E_2*(-51 + E_2)*r0_4) + L_2*M*r0*(192*(-85 + 76*E_2)*M_5 + 16*(-786 - 841*E_2 + 648*E_4)*M_4*r0 + 8*(594 - 3447*E_2 + 1034*E_4)*M_3*r0_2 + 3*(1768 + 2685*E_2 - 1160*E_4)*M_2*r0_3 + 2*(192 + 3881*E_2 - 664*E_4)*M*r0_4 + 4*(-66 - 438*E_2 + 71*E_4)*r0_5) + E_2*r0_2*(32256*M_7 + 32*(1091 + 468*E_2)*M_6*r0 + 8*(-4993 + 5068*E_2)*M_5*r0_2 + 20*(-2189 + 1460*E_2)*M_4*r0_3 + (5057 + 2488*E_2)*M_3*r0_4 + (5293 - 1720*E_2)*M_2*r0_5 + (-1981 + 298*E_2)*M*r0_6 - 30*r0_7)) + a_6*r0*(-6*L_6*M*(32*M_3 + 4*(-13 + 38*E_2)*M_2*r0 - 2*(24 + 91*E_2)*M*r0_2 + 17*E_2*r0_3) - 4*L_4*M*(1152*M_5 - 96*(-68 + 69*E_2)*M_4*r0 - 32*(-39 - 78*E_2 + 25*E_4)*M_3*r0_2 + (-2700 + 2937*E_2 + 770*E_4)*M_2*r0_3 + 2*(-282 - 1070*E_2 + 89*E_4)*M*r0_4 + (198 + 288*E_2 - 73*E_4)*r0_5) + E_2*r0_4*(57960*M_6 + 12*(2009 + 2004*E_2)*M_5*r0 + 14*(-3451 + 2616*E_2)*M_4*r0_2 + (-6893 + 8384*E_2)*M_3*r0_3 + (9783 - 2680*E_2)*M_2*r0_4 + 3*(-1003 + 184*E_2)*M*r0_5 - 75*r0_6) + L_2*r0*(32256*M_7 + 768*(9 + 46*E_2)*M_6*r0 + 16*(-2772 + 1921*E_2 + 1174*E_4)*M_5*r0_2 + 16*(-618 - 3587*E_2 + 2098*E_4)*M_4*r0_3 - 6*(-2316 + 1679*E_2 + 8*E_4)*M_3*r0_4 + (2520 + 19847*E_2 - 2948*E_4)*M_2*r0_5 + (-1008 - 4303*E_2 + 964*E_4)*M*r0_6 - 105*E_2*r0_7)) + 2*a_5*E*L*M*r0_2*(24*L_6*(7*M_2 - 15*M*r0 + 2*r0_2) + 2*L_4*r0*((7088 - 4128*E_2)*M_3 + 2*(561 + 428*E_2 + 42*E_4)*M_2*r0 + 2*(-1289 + 48*E_2 + 21*E_4)*M*r0_2 + 3*(84 - 29*E_2)*r0_3) - L_2*r0*(37056*M_5 + 8*(165 + 1532*E_2)*M_4*r0 - 4*(12514 - 4809*E_2 + 120*E_4)*M_3*r0_2 - 2*(-589 + 4152*E_2 + 1152*E_4)*M_2*r0_3 + 4*(3132 + 401*E_2 - 222*E_4)*M*r0_4 + (-2238 + 617*E_2)*r0_5) - r0_3*(49344*M_5 + 4*(-10362 + 3239*E_2 + 378*E_4)*M_3*r0_2 + 2*(1059 - 2746*E_2 + 36*E_4)*M_2*r0_3 + 6*(1390 + 223*E_2 - 168*E_4)*M*r0_4 + (-1650 + 403*E_2)*r0_5 + 3624*M_4*(r0 + 4*E_2*r0)))))/(3.*r0_8*expr2_3);\n    A03220 = (-16*E*r0*(2*a_3*L*M*(12*M + (6 + E_2)*r0) + a_4*E*(-24*M_2 - 9*M*r0 + 2*r0_2) + 6*a*L*M*r0*(2*L_2 + (2 + E_2)*r0_2) + E*r0_3*(r0_2*(-17*M + 2*r0) + L_2*(-19*M + 3*r0)) + 2*a_2*E*r0*(L_2*(-7*M + r0) + r0*(-14*M_2 - 13*M*r0 + 2*r0_2))))/expr2_2;\n    A03240 = (-4*(a_10*E_2*M*(192*(-3 + E_2)*M_3 + 4*(1033 - 89*E_2)*M_2*r0 + 3*(1201 - 108*E_2)*M*r0_2 + 6*(107 - 6*E_2)*r0_3) - 4*a_9*E*L*M*(96*(-3 + E_2)*M_3 + (973 + 258*E_2 + 18*E_4)*M_2*r0 + 15*(58 + 13*E_2)*M*r0_2 + 3*(50 + E_2)*r0_3) + 4*a*E*L*M*r0_3*(-4*L_6*(214*M_2 - 215*M*r0 + 54*r0_2) + 3*r0_5*(-2048*M_3 - 4*(-532 + 53*E_2)*M_2*r0 + 4*(-173 + 43*E_2 + 9*E_4)*M*r0_2 + (67 - 33*E_2)*r0_3) + L_2*r0_3*(-8736*M_3 + 2*(4608 - 695*E_2)*M_2*r0 + (-3036 + 817*E_2 + 114*E_4)*M*r0_2 + 6*(48 - 7*E_2)*r0_3) - L_4*r0*(2400*M_3 + 92*(-19 + 16*E_2)*M_2*r0 + 2*(8 - 423*E_2)*M*r0_2 + 3*(46 + 17*E_2)*r0_3)) + r0_4*(2*L_6*M*(2*M - r0)*(2088*M_2 + 2*(-774 + 425*E_2)*M*r0 + 7*(36 - 61*E_2)*r0_2) + 3*E_2*r0_7*(-852*M_3 + (930 - 72*E_2)*M_2*r0 + 2*(-139 + 27*E_2)*M*r0_2 + 13*r0_3) + 2*L_4*r0_2*(8928*M_4 + 312*(-36 + E_2)*M_3*r0 - 2*(-2268 + 237*E_2 + 115*E_4)*M_2*r0_2 + (-576 + 111*E_2 + 113*E_4)*M*r0_3 + 24*E_2*r0_4) + L_2*r0_4*(9504*M_4 - 112*(108 + 43*E_2)*M_3*r0 + 2*(2484 + 2283*E_2 - 340*E_4)*M_2*r0_2 + (-648 - 1169*E_2 + 368*E_4)*M*r0_3 + 45*E_2*r0_4)) + 4*a_3*E*L*M*r0_2*(-(L_4*(1344*M_3 + 12*(-316 + 163*E_2)*M_2*r0 - 8*(-62 + 97*E_2)*M*r0_2 + 3*(128 + 15*E_2)*r0_3)) + r0_3*(-23232*M_4 + (9432 - 6834*E_2)*M_3*r0 + (10068 + 815*E_2 - 54*E_4)*M_2*r0_2 + (-5208 + 505*E_2 + 324*E_4)*M*r0_3 + (456 - 211*E_2)*r0_4) + L_2*r0*(-16512*M_4 + 32*(189 - 160*E_2)*M_3*r0 + 2*(5946 - 653*E_2 + 114*E_4)*M_2*r0_2 + 2*(-2631 + 505*E_2 + 108*E_4)*M*r0_3 + 3*(58 - 15*E_2)*r0_4)) + 4*a_7*E*L*M*(L_2*(96*M_3 + 2*(-134 + 3*E_2)*M_2*r0 - 6*(189 + 38*E_2)*M*r0_2 + 3*(-100 + E_2)*r0_3) - r0*(288*(-37 + 27*E_2)*M_4 - 4*(2567 - 1443*E_2 + 108*E_4)*M_3*r0 + (1222 + 719*E_2 - 270*E_4)*M_2*r0_2 + (2784 + 811*E_2 - 108*E_4)*M*r0_3 + 3*(82 + 23*E_2)*r0_4)) + a_8*(-6*L_2*M*(96*M_3 - 2*(-20 + 233*E_2 + 14*E_4)*M_2*r0 + 4*(-1 - 233*E_2 + 2*E_4)*M*r0_2 + E_2*(-215 + 8*E_2)*r0_3) + E_2*r0*(192*(-111 + 62*E_2)*M_5 + 8*(-4474 + 1649*E_2)*M_4*r0 + (-3949 + 1972*E_2)*M_3*r0_2 - 2*(-5145 + 662*E_2)*M_2*r0_3 - 6*(-309 + E_2)*M*r0_4 - 30*r0_5)) + a_2*r0_2*(-2*L_6*M*(1152*M_3 - 24*(-80 + 197*E_2)*M_2*r0 + 2*(-1002 + 1627*E_2)*M*r0_2 + (360 - 437*E_2)*r0_3) + 3*E_2*r0_6*(4976*M_4 + 4*(-1155 + 626*E_2)*M_3*r0 - 40*(-61 + 15*E_2)*M_2*r0_2 + 2*(-353 + 98*E_2)*M*r0_3 + 15*r0_4) + 8*L_4*M*r0*(4896*M_4 + 4*(-1008 + 275*E_2)*M_3*r0 + (-1668 - 277*E_2 + 197*E_4)*M_2*r0_2 + (1896 - 454*E_2 - 105*E_4)*M*r0_3 + (-324 + 148*E_2 + 51*E_4)*r0_4) + L_2*r0_3*(41472*M_5 + 8*(-4032 + 3071*E_2)*M_4*r0 + 8*(-1188 - 3480*E_2 + 683*E_4)*M_3*r0_2 - 6*(-1956 - 1967*E_2 + 324*E_4)*M_2*r0_3 + 3*(-672 - 655*E_2 + 352*E_4)*M*r0_4 - 15*E_2*r0_5)) + 4*a_5*E*L*M*r0*(6*L_4*(14*M_2 - 44*M*r0 + (-25 + E_2)*r0_2) + L_2*(480*M_4 - 8*(-1393 + 918*E_2)*M_3*r0 + 2*(1960 - 375*E_2 + 102*E_4)*M_2*r0_2 + (-3564 - 143*E_2 + 102*E_4)*M*r0_3 - 6*(69 + 10*E_2)*r0_4) + r0*(-22464*M_5 - 12*(367 + 516*E_2)*M_4*r0 + 2*(11742 - 5065*E_2 + 216*E_4)*M_3*r0_2 + 3*(1141 + 346*E_2 + 126*E_4)*M_2*r0_3 + (-5046 - 527*E_2 + 324*E_4)*M*r0_4 + (159 - 178*E_2)*r0_5)) + a_6*(-12*L_4*M*(16*M_3 + 2*(14 + 15*E_2)*M_2*r0 - 2*(2 + 83*E_2)*M*r0_2 + E_2*(-53 + E_2)*r0_3) + 2*L_2*r0*(288*(-37 + 38*E_2)*M_5 + 12*(-220 - 585*E_2 + 296*E_4)*M_4*r0 + 4*(1044 - 2693*E_2 + 453*E_4)*M_3*r0_2 + (780 + 5621*E_2 + 140*E_4)*M_2*r0_3 + (-360 + 1217*E_2 + 112*E_4)*M*r0_4 - 39*E_2*r0_5) + E_2*r0_2*(44928*M_6 + 48*(631 + 386*E_2)*M_5*r0 + 16*(-3290 + 2187*E_2)*M_4*r0_2 + 2*(-14365 + 5518*E_2)*M_3*r0_3 + (12111 - 2644*E_2)*M_2*r0_4 + 12*(97 + 30*E_2)*M*r0_5 - 93*r0_6)) + a_4*r0*(-12*L_6*M*(8*M_2 - 2*M*r0 + E_2*r0_2) + 3*E_2*r0_4*(18292*M_5 + 4*(-253 + 1862*E_2)*M_4*r0 + (-10739 + 5140*E_2)*M_3*r0_2 + 2*(1687 - 538*E_2)*M_2*r0_3 + 12*(-37 + 21*E_2)*M*r0_4 - 19*r0_5) - 2*L_4*(2304*M_5 - 48*(-215 + 294*E_2)*M_4*r0 - 4*(846 - 1629*E_2 + 122*E_4)*M_3*r0_2 + 2*(-1392 + 541*E_2 + 111*E_4)*M_2*r0_3 + (720 - 703*E_2 - 85*E_4)*M*r0_4 + 24*E_2*r0_5) + 2*L_2*r0*(22464*M_6 + 144*(-44 + 115*E_2)*M_5*r0 + 4*(-5364 + 1699*E_2 + 1400*E_4)*M_4*r0_2 + 2*(2868 - 10673*E_2 + 2326*E_4)*M_3*r0_3 + 2*(2076 + 3331*E_2 - 42*E_4)*M_2*r0_4 + 3*(-348 + 49*E_2 + 160*E_4)*M*r0_5 - 69*E_2*r0_6))))/(3.*r0_5*expr2_3);\n    A03400 = (-2*E*r0_4*(12*a*L*M + 3*a_2*E*(-4*M + r0) + E*r0_2*(-17*M + 2*r0)))/expr2_2;\n    A03420 = (12*a_7*E*L*M*(4*(-27 + 20*E_2)*M_2 + 2*(132 + 11*E_2 + 3*E_4)*M*r0 + (131 - 14*E_2)*r0_2) - 3*a_8*E_2*(8*(-27 + 14*E_2)*M_3 + 4*(284 - 13*E_2)*M_2*r0 + 2*(258 - 19*E_2)*M*r0_2 - 9*r0_3) - 4*a*E*L*M*r0_2*(-12*L_4*(56*M_2 - 58*M*r0 + 15*r0_2) + r0_3*(-5448*M_3 + 12*(400 - 73*E_2)*M_2*r0 + (-1102 + 621*E_2 + 54*E_4)*M*r0_2 + (23 - 108*E_2)*r0_3) - L_2*r0*(3624*M_3 + 4*(-669 + 284*E_2)*M_2*r0 + 16*(13 - 38*E_2)*M*r0_2 + (121 + 27*E_2)*r0_3)) - 4*a_5*E*L*M*(-3*L_2*(16*M_2 + 2*(4 + 21*E_2)*M*r0 + (131 - 8*E_2)*r0_2) + r0*(-72*(-63 + 46*E_2)*M_3 + 18*(185 - 56*E_2 + 8*E_4)*M_2*r0 - (1118 + 63*E_2 + 18*E_4)*M*r0_2 + (-769 + 60*E_2)*r0_3)) + a_6*(-3*L_2*(72*(-3 + 4*E_2)*M_3 + 4*(-20 + 63*E_2 + 26*E_4)*M_2*r0 + 2*E_2*(253 - 8*E_2)*M*r0_2 - 9*E_2*r0_3) + E_2*r0*((9072 - 4800*E_2)*M_4 - 24*(-662 + 141*E_2)*M_3*r0 + 4*(-800 + 63*E_2)*M_2*r0_2 + (-4169 + 180*E_2)*M*r0_3 + 61*r0_4)) + r0_3*(-8*L_4*M*(2*M - r0)*(558*M_2 + (-423 + 169*E_2)*M*r0 + (72 - 91*E_2)*r0_2) + E_2*r0_5*(2556*M_3 + 216*(-13 + E_2)*M_2*r0 + (841 - 162*E_2)*M*r0_2 - 38*r0_3) + L_2*r0_2*(-9504*M_4 + 4*(3024 + 323*E_2)*M_3*r0 + 4*(-1242 - 243*E_2 + 85*E_4)*M_2*r0_2 + (648 + 155*E_2 - 184*E_4)*M*r0_3 + 4*E_2*r0_4)) - 4*a_3*E*L*M*r0*(-24*L_4*M + L_2*(-336*M_3 + 12*(215 - 132*E_2)*M_2*r0 + 12*(21 + 34*E_2)*M*r0_2 - (562 + 3*E_2)*r0_3) + r0*(-10368*M_4 - 36*(-71 + 74*E_2)*M_3*r0 + 2*(3095 - 612*E_2 + 108*E_4)*M_2*r0_2 + 6*(-236 + 78*E_2 + 9*E_4)*M*r0_3 - (353 + 78*E_2)*r0_4)) + a_4*(24*L_4*M*(2*M_2 - 5*(-2 + 3*E_2)*M*r0 + 2*E_2*r0_2) - L_2*r0*(144*(-63 + 76*E_2)*M_4 + 24*(107 - 245*E_2 + 40*E_4)*M_3*r0 + 12*(184 - 338*E_2 + 57*E_4)*M_2*r0_2 + (-648 + 2809*E_2 + 24*E_4)*M*r0_3 + 2*E_2*r0_4) - E_2*r0_2*(20736*M_5 + 48*(123 + 146*E_2)*M_4*r0 + 4*(-5005 + 1836*E_2)*M_3*r0_2 - 4*(422 + 147*E_2)*M_2*r0_3 + 3*(971 + 48*E_2)*M*r0_4 - 15*r0_5)) - a_2*r0*(-12*L_4*M*(96*M_3 + 4*(79 - 156*E_2)*M_2*r0 + (-296 + 419*E_2)*M*r0_2 + (54 - 59*E_2)*r0_3) + E_2*r0_4*(12216*M_4 + 24*(-289 + 203*E_2)*M_3*r0 + 4*(320 - 201*E_2)*M_2*r0_2 + 3*(-175 + 124*E_2)*M*r0_3 + 57*r0_4) + L_2*r0*(20736*M_5 + 2304*(-7 + 4*E_2)*M_4*r0 + 16*(-414 - 120*E_2 + 61*E_4)*M_3*r0_2 + 4*(1854 - 817*E_2 + 40*E_4)*M_2*r0_3 + 8*(-162 + 127*E_2 + 32*E_4)*M*r0_4 + 25*E_2*r0_5)))/(3.*r0_2*expr2_3);\n    A03600 = (r0*(12*a_5*E*L*M*(4*(-7 + 6*E_2)*M + (73 - 12*E_2)*r0) + 3*a_6*E_2*(-8*(-7 + 4*E_2)*M_2 + 4*(-73 + 6*E_2)*M*r0 + 7*r0_2) + 4*a_3*E*L*M*(24*L_2*M - r0*((624 - 456*E_2)*M_2 + 40*(10 + E_2)*M*r0 + (-286 + 21*E_2)*r0_2)) + r0_2*(-6*L_2*M*(2*M - r0)*(264*M_2 + 2*(-102 + 31*E_2)*M*r0 + 3*(12 - 13*E_2)*r0_2) + E_2*r0_3*(852*M_3 + 24*(-38 + 3*E_2)*M_2*r0 + (271 - 54*E_2)*M*r0_2 - 14*r0_3)) + a_4*(-24*L_2*M*((-7 + 12*E_2)*M - 3*E_2*r0) + E_2*r0*(-32*(-39 + 19*E_2)*M_3 - 8*(-329 + 33*E_2)*M_2*r0 + (-1569 + 74*E_2)*M*r0_2 + 28*r0_3)) + 4*a*E*L*M*r0*(4*L_2*(38*M_2 - 43*M*r0 + 12*r0_2) + r0*(1584*M_3 + 4*(-268 + 93*E_2)*M_2*r0 + 2*(18 - 97*E_2)*M*r0_2 + 5*(11 + 3*E_2)*r0_3)) - a_2*r0*(2*L_2*M*(48*(-13 + 19*E_2)*M_2 + (516 - 556*E_2)*M*r0 + (-96 + 91*E_2)*r0_2) + E_2*r0*(3168*M_4 + 8*(-32 + 93*E_2)*M_3*r0 + 4*(-373 + 16*E_2)*M_2*r0_2 + 2*(211 + 26*E_2)*M*r0_3 + 7*r0_4))))/(12.*expr2_3);\n    A04020 = (48*E_2*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/expr2_2;\n    A04040 = (8*(-4*a_7*E*L*M*(144*M_3 + 12*(3 + 8*E_2)*M_2*r0 + 2*(-51 + 32*E_2)*M*r0_2 + 3*(-14 + E_2)*r0_3) + a_8*E_2*(288*M_4 + 48*(-3 + 2*E_2)*M_3*r0 + 2*(-271 + 60*E_2)*M_2*r0_2 + 6*(-35 + 6*E_2)*M*r0_3 - 3*r0_4) + 4*a*E*L*M*r0_3*(L_4*(216*M_2 + 4*(-72 + E_2)*M*r0 - 6*(-13 + E_2)*r0_2) - 3*r0_4*(-120*M_2 + (124 + 35*E_2)*M*r0 + 4*(-7 + E_2)*r0_2) + 3*L_2*r0_2*(192*M_2 - (220 + 51*E_2)*M*r0 + 2*(27 + E_2)*r0_2)) - 4*a_5*E*L*M*r0*(-1152*M_4 + 24*(-7 + 2*E_2)*M_3*r0 + 2*(408 + 113*E_2)*M_2*r0_2 + 229*E_2*M*r0_3 + 8*(-21 + E_2)*r0_4 + 3*L_2*(64*M_2 + 2*(-7 + 13*E_2)*M*r0 + (-28 + 3*E_2)*r0_2)) + r0_3*(-4*L_6*M*(2*M - r0)*(72*M + (-36 + E_2)*r0) + 3*E_2*r0_7*(159*M_2 + 4*(-21 + E_2)*M*r0 + 3*r0_2) + 3*L_2*r0_4*(-192*M_3 + 2*(96 + 197*E_2)*M_2*r0 + 4*(-12 - 51*E_2 + 2*E_4)*M*r0_2 + 5*E_2*r0_3) + L_4*r0_2*(-1152*M_3 + (1152 + 721*E_2)*M_2*r0 + 4*(-72 - 95*E_2 + 2*E_4)*M*r0_2 + 12*E_2*r0_3)) - 4*a_3*E*L*M*r0_2*(6*L_4*(10*M + (-7 + E_2)*r0) + 3*L_2*(-336*M_3 + 4*(47 + 2*E_2)*M_2*r0 + (154 + 71*E_2)*M*r0_2 + (-82 + 3*E_2)*r0_3) + r0_2*(-1296*M_3 + 6*(98 + 9*E_2)*M_2*r0 + 2*(237 + 145*E_2)*M*r0_2 + (-210 + 17*E_2)*r0_3)) + a_2*r0_2*(12*L_6*M*(6*M + E_2*r0) + 6*E_2*r0_5*(40*M_3 + 2*(91 - 18*E_2)*M_2*r0 + (-149 + 12*E_2)*M*r0_2 + 6*r0_3) - 2*L_4*(1152*M_4 + 48*(-12 + E_2)*M_3*r0 + 2*(-180 - 315*E_2 + 2*E_4)*M_2*r0_2 + (144 + 265*E_2 - 10*E_4)*M*r0_3 - 12*E_2*r0_4) + 3*L_2*r0_2*(-768*M_4 + 24*(16 + 5*E_2)*M_3*r0 + 4*(54 + 205*E_2 + 9*E_4)*M_2*r0_2 + 2*(-48 - 257*E_2 + 16*E_4)*M*r0_3 + 19*E_2*r0_4)) + 2*a_6*(L_2*(144*M_4 + 48*(3 + 8*E_2)*M_3*r0 + 2*(18 - 44*E_2 + 27*E_4)*M_2*r0_2 + 3*E_2*(-67 + 8*E_2)*M*r0_3 - 3*E_2*r0_4) + E_2*r0*(-1152*M_5 - 48*(7 + 2*E_2)*M_4*r0 + 2*(313 - 72*E_2)*M_3*r0_2 + (-403 + 60*E_2)*M_2*r0_3 + 3*(-127 + 20*E_2)*M*r0_4 + 6*r0_5)) + a_4*r0*(3*L_4*(96*M_3 + 4*(12 + 31*E_2)*M_2*r0 + 4*E_2*(-15 + E_2)*M*r0_2 - E_2*r0_3) + 3*E_2*r0_3*(-740*M_4 + 4*(137 - 48*E_2)*M_3*r0 + (101 - 72*E_2)*M_2*r0_2 + 2*(-199 + 24*E_2)*M*r0_3 + 14*r0_4) - 2*L_2*(1152*M_5 + 672*E_2*M_4*r0 + 4*(-180 - 287*E_2 + 12*E_4)*M_3*r0_2 - (72 + 527*E_2 + 84*E_4)*M_2*r0_3 + 3*(24 + 211*E_2 - 20*E_4)*M*r0_4 - 18*E_2*r0_5))))/(3.*r0_4*expr2_3);\n    A04200 = (12*E_2*r0_6)/expr2_2;\n    A04220 = (4*(2*a_5*E*L*M*(-48*M_2 + 2*(3 - 8*E_2)*M*r0 + (20 + E_2)*r0_2) + a_6*E_2*M*(48*M_2 + 4*(-9 + 2*E_2)*M*r0 + (-47 + 6*E_2)*r0_2) - 2*a_3*E*L*M*r0*(-384*M_3 + 4*(31 + 4*E_2)*M_2*r0 + (158 + 45*E_2)*M*r0_2 - (58 + 7*E_2)*r0_3 + 4*L_2*(8*M + (-5 + E_2)*r0)) - 2*a*E*L*M*r0_2*(-4*L_2*(42*M_2 - 43*M*r0 - (-9 + E_2)*r0_2) + r0_2*(-216*M_2 + (200 + 39*E_2)*M*r0 + 2*(-19 + 2*E_2)*r0_2)) + r0_2*(-48*L_4*M*r0_2M_2 + E_2*r0_5*(159*M_2 + 2*(-43 + 2*E_2)*M*r0 + 4*r0_2) + L_2*r0_2*(-192*M_3 + 3*(64 + 67*E_2)*M_2*r0 + (-48 - 107*E_2 + 4*E_4)*M*r0_2 + 4*E_2*r0_3)) + a_4*(4*L_2*M*(12*M_2 + 2*(3 + 8*E_2)*M*r0 + E_2*(-12 + E_2)*r0_2) + E_2*r0*(-384*M_4 - 8*(-7 + E_2)*M_3*r0 + (57 - 8*E_2)*M_2*r0_2 + 8*(-19 + 2*E_2)*M*r0_3 + 10*r0_4)) + a_2*r0*(4*L_4*M*(6*M + E_2*r0) + E_2*r0_3*(-56*M_3 - 8*(-29 + 4*E_2)*M_2*r0 + 7*(-29 + 2*E_2)*M*r0_2 + 14*r0_3) + L_2*(-384*M_4 - 8*(-24 + 13*E_2)*M_3*r0 + 10*(12 + 25*E_2)*M_2*r0_2 + (-48 - 147*E_2 + 8*E_4)*M*r0_3 + 15*E_2*r0_4))))/(r0*expr2_3);\n    A04400 = (r0_2*(-72*a_3*E*L*M*(2*M - r0) - 24*a*E*L*M*r0*(-48*M_2 + 2*(19 + E_2)*M*r0 - 5*r0_2) + 9*a_4*E_2*(8*M_2 - 8*M*r0 + r0_2) + r0*(-12*L_2*M*(2*M - r0)*(24*M - (12 + E_2)*r0) + E_2*r0_3*(477*M_2 + 2*(-127 + 6*E_2)*M*r0 + 10*r0_2)) + 2*a_2*(36*L_2*M_2 + E_2*r0*(-288*M_3 + 12*(14 + E_2)*M_2*r0 + 6*(-27 + E_2)*M*r0_2 + 17*r0_3))))/(6.*expr2_3);\n    A05020 = (-8*E*r0*(2*a_3*L*M*(12*M + (6 + E_2)*r0) + a_4*E*(-24*M_2 - 9*M*r0 + 2*r0_2) + 6*a*L*M*r0*(2*L_2 + (2 + E_2)*r0_2) + E*r0_3*(r0_2*(-16*M + r0) + 2*L_2*(-9*M + r0)) + a_2*E*r0*(2*L_2*(-7*M + r0) + r0*(-26*M_2 - 27*M*r0 + 3*r0_2))))/expr2_3;\n    A05040 = (-4*(-(a_10*E_2*M*(96*(-9 + 4*E_2)*M_3 + 4*(-272 + 169*E_2)*M_2*r0 + 6*(43 + 34*E_2)*M*r0_2 + 15*(23 - 2*E_2)*r0_3)) + 2*a_9*E*L*M*(96*(-9 + 7*E_2)*M_3 + 4*(-268 + 51*E_2 + 27*E_4)*M_2*r0 + 6*(-2 - 43*E_2 + 12*E_4)*M*r0_2 - 9*(-18 + E_2)*r0_3) + 2*a*E*L*M*r0_3*(-8*L_6*(110*M_2 - 109*M*r0 + 27*r0_2) + L_2*r0_3*(-9024*M_3 + (12900 - 2237*E_2)*M_2*r0 + 8*(-747 + 301*E_2 + 21*E_4)*M*r0_2 + 6*(141 - 95*E_2)*r0_3) + 3*r0_5*(-2080*M_3 - 5*(-538 + 51*E_2)*M_2*r0 + 4*(-287 + 81*E_2 + 18*E_4)*M*r0_2 + 2*(77 - 41*E_2)*r0_3) - 2*L_4*r0*(1296*M_3 + (-1879 + 864*E_2)*M_2*r0 + 2*(403 - 292*E_2)*M*r0_2 + 12*(-7 + 6*E_2)*r0_3)) + r0_4*(6*L_6*M*r0_2M_2*(180*M + (-48 + 77*E_2)*r0) - 3*E_2*M*r0_7*(688*M_2 + (-673 + 60*E_2)*M*r0 + (163 - 30*E_2)*r0_2) + 2*L_4*r0_2*(4608*M_4 + (-5904 + 7*E_2)*M_3*r0 - 3*(-816 + 109*E_2 + 52*E_4)*M_2*r0_2 + 2*(-162 + 118*E_2 + 39*E_4)*M*r0_3 - 36*E_2*r0_4) - L_2*r0_4*(-4896*M_4 + 2*(3168 + 1901*E_2)*M_3*r0 + (-2664 - 3213*E_2 + 524*E_4)*M_2*r0_2 + (360 + 551*E_2 - 248*E_4)*M*r0_3 + 48*E_2*r0_4)) + 2*a_3*E*L*M*r0_2*(24*L_6*M - 2*L_4*(768*M_3 + 12*(-210 + 89*E_2)*M_2*r0 + 4*(299 - 154*E_2)*M*r0_2 + 63*(-2 + E_2)*r0_3) + 2*r0_3*(-11856*M_4 + (7584 - 3525*E_2)*M_3*r0 + 2*(2298 + 218*E_2 - 135*E_4)*M_2*r0_2 + (-4356 + 467*E_2 + 396*E_4)*M*r0_3 + (786 - 119*E_2)*r0_4) + L_2*r0*(-17184*M_4 - 16*(-747 + 397*E_2)*M_3*r0 + 3*(4080 - 29*E_2 + 40*E_4)*M_2*r0_2 + 6*(-1802 + 381*E_2 + 66*E_4)*M*r0_3 + 6*(327 - 28*E_2)*r0_4)) + 2*a_7*E*L*M*(L_2*(480*M_3 + 4*(-358 + 255*E_2)*M_2*r0 + 12*(-57 - 7*E_2 + 3*E_4)*M*r0_2 + 9*(36 + E_2)*r0_3) + 2*r0*(-48*(-137 + 90*E_2)*M_4 + 4*(883 - 642*E_2 + 36*E_4)*M_3*r0 + 2*(-1495 + 172*E_2 + 171*E_4)*M_2*r0_2 + 2*(-438 - 265*E_2 + 126*E_4)*M*r0_3 + 3*(162 - 13*E_2)*r0_4)) + a_8*(-3*L_2*M*(288*(-1 + 2*E_2)*M_3 + 4*(-88 - 181*E_2 + 90*E_4)*M_2*r0 + 4*(-26 - 122*E_2 + 39*E_4)*M*r0_2 + E_2*(227 - 4*E_2)*r0_3) + E_2*r0*(96*(-137 + 68*E_2)*M_5 + 16*(-643 + 404*E_2)*M_4*r0 - 8*(-599 + 92*E_2)*M_3*r0_2 + 2*(453 - 598*E_2)*M_2*r0_3 + 6*(-359 + 35*E_2)*M*r0_4 - 45*r0_5)) + a_2*r0_2*(-4*L_6*M*(288*M_3 + 6*(111 - 206*E_2)*M_2*r0 + 8*(-75 + 112*E_2)*M*r0_2 + (90 - 131*E_2)*r0_3) + 3*E_2*r0_6*(1714*M_4 + (-3143 + 1444*E_2)*M_3*r0 + 4*(689 - 127*E_2)*M_2*r0_2 + 2*(-404 + 65*E_2)*M*r0_3 + 21*r0_4) + 2*L_4*r0*(10080*M_5 + 8*(-1062 + 343*E_2)*M_4*r0 + 3*(-1328 - 1133*E_2 + 228*E_4)*M_3*r0_2 + (4284 + 941*E_2 - 640*E_4)*M_2*r0_3 + (-684 - 233*E_2 + 167*E_4)*M*r0_4 + 84*E_2*r0_5) + L_2*r0_3*(21312*M_5 + 2*(-8496 + 5273*E_2)*M_4*r0 + (-5304 - 21071*E_2 + 5296*E_4)*M_3*r0_2 + (6456 + 14171*E_2 - 4516*E_4)*M_2*r0_3 + (-1080 - 3485*E_2 + 848*E_4)*M*r0_4 + 165*E_2*r0_5)) + 2*a_5*E*L*M*r0*(18*L_4*(20*M_2 + 2*(-18 + 5*E_2)*M*r0 + (9 + E_2)*r0_2) + L_2*(192*M_4 - 464*(-31 + 18*E_2)*M_3*r0 + 4*(-151 + 417*E_2 + 42*E_4)*M_2*r0_2 + 2*(-2814 + 59*E_2 + 132*E_4)*M*r0_3 + 3*(480 - 41*E_2)*r0_4) + r0*(-23040*M_5 + 24*(23 - 268*E_2)*M_4*r0 + 2*(13716 - 5383*E_2)*M_3*r0_2 + 3*(-1294 + 939*E_2 + 108*E_4)*M_2*r0_3 + 4*(-1752 - 53*E_2 + 252*E_4)*M*r0_4 + (1920 - 61*E_2)*r0_5)) + a_6*(-6*L_4*M*(32*M_3 + 20*(-8 + 15*E_2)*M_2*r0 + 2*(-52 - 123*E_2 + 22*E_4)*M*r0_2 + E_2*(59 + 3*E_2)*r0_3) - L_2*r0*(-96*(-137 + 132*E_2)*M_5 - 192*(-20 - 62*E_2 + 25*E_4)*M_4*r0 + 8*(-765 + 355*E_2 + 168*E_4)*M_3*r0_2 + 4*(-438 - 2108*E_2 + 457*E_4)*M_2*r0_3 + (360 + 3643*E_2 - 376*E_4)*M*r0_4 + 117*E_2*r0_5) + E_2*r0_2*(23040*M_6 + 48*(121 + 196*E_2)*M_5*r0 + 2*(-14153 + 9504*E_2)*M_4*r0_2 + (2243 + 4408*E_2)*M_3*r0_3 + 20*(387 - 136*E_2)*M_2*r0_4 + 3*(-1429 + 180*E_2)*M*r0_5 - 27*r0_6)) + a_4*r0*(-6*L_6*M*(16*M_2 + 4*(-13 + 9*E_2)*M*r0 + 3*E_2*r0_2) + 3*E_2*r0_4*(8580*M_5 + 8*(-541 + 488*E_2)*M_4*r0 + 6*(-529 + 464*E_2)*M_3*r0_2 + (4131 - 1024*E_2)*M_2*r0_3 + (-1471 + 220*E_2)*M*r0_4 + 27*r0_5) - 4*L_4*(576*M_5 - 48*(-68 + 81*E_2)*M_4*r0 - 2*(426 - 1347*E_2 + 94*E_4)*M_3*r0_2 + 2*(-519 - 371*E_2 + 144*E_4)*M_2*r0_3 - 5*(-36 - 49*E_2 + 8*E_4)*M*r0_4 + 18*E_2*r0_5) + L_2*r0*(23040*M_6 + 768*(-9 + 23*E_2)*M_5*r0 + 4*(-6096 - 1097*E_2 + 2020*E_4)*M_4*r0_2 + (6096 - 20243*E_2 + 4280*E_4)*M_3*r0_3 + 2*(2616 + 9131*E_2 - 2004*E_4)*M_2*r0_4 + 4*(-270 - 1285*E_2 + 241*E_4)*M*r0_5 + 96*E_2*r0_6))))/(3.*r0_5*expr2_4);\n    A05200 = (-2*E*r0_4*(12*a*L*M + E*r0_2*(-16*M + r0) + 3*a_2*E*(-4*M + r0)))/expr2_3;\n    A05220 = (-6*a_7*E*L*M*(16*(-12 + 7*E_2)*M_2 + 4*(-78 - 4*E_2 + 3*E_4)*M*r0 + (-32 + 23*E_2)*r0_2) + 3*a_8*E_2*(64*(-3 + E_2)*M_3 + 8*(-62 + 13*E_2)*M_2*r0 - (21 + 2*E_2)*M*r0_2 + 9*r0_3) - 6*a_5*E*L*M*(4*L_2*(20*M_2 + (-62 + 21*E_2)*M*r0 + 2*(-4 + E_2)*r0_2) + r0*(-64*(-61 + 39*E_2)*M_3 + 4*(119 - 45*E_2 + 18*E_4)*M_2*r0 + 2*(-688 + 39*E_2 + 48*E_4)*M*r0_2 + (148 + 7*E_2)*r0_3)) - 2*a*E*L*M*r0_2*(-360*L_4*r0_2M_2 - 2*L_2*r0*(3792*M_3 + 22*(-207 + 64*E_2)*M_2*r0 + 2*(867 - 482*E_2)*M*r0_2 + 3*(-60 + 37*E_2)*r0_3) - 3*r0_3*(3712*M_3 + (-4372 + 671*E_2)*M_2*r0 - 4*(-424 + 161*E_2 + 18*E_4)*M*r0_2 + 2*(-102 + 67*E_2)*r0_3)) + 3*a_6*(L_2*(96*(-2 + 3*E_2)*M_3 + 8*(-16 - 63*E_2 + 13*E_4)*M_2*r0 + 8*E_2*(-2 + E_2)*M*r0_2 + 9*E_2*r0_3) + E_2*r0*(-64*(-61 + 28*E_2)*M_4 + 4*(458 - 195*E_2)*M_3*r0 + 8*(-202 + 59*E_2)*M_2*r0_2 + (247 - 74*E_2)*M*r0_3 + 11*r0_4)) + r0_3*(-4*L_4*M*(2*M - r0)*(1152*M_2 + (-900 + 379*E_2)*M*r0 + (162 - 193*E_2)*r0_2) + 6*E_2*r0_5*(688*M_3 + (-679 + 60*E_2)*M_2*r0 - 6*(-27 + 5*E_2)*M*r0_2 + 2*r0_3) + L_2*r0_2*(-9792*M_4 + 2*(6336 + 1277*E_2)*M_3*r0 + (-5328 - 1863*E_2 + 524*E_4)*M_2*r0_2 + 4*(180 + 29*E_2 - 62*E_4)*M*r0_3 + 84*E_2*r0_4)) - 6*a_3*E*L*M*r0*(16*L_4*M - 2*L_2*(160*M_3 + 4*(-293 + 153*E_2)*M_2*r0 + 2*(271 - 170*E_2)*M*r0_2 + (-38 + 33*E_2)*r0_3) + r0*(-7104*M_4 - 112*(-33 + 17*E_2)*M_3*r0 + (4016 - 665*E_2 + 72*E_4)*M_2*r0_2 + 2*(-1388 + 291*E_2 + 78*E_4)*M*r0_3 + 4*(96 - 29*E_2)*r0_4)) + 3*a_4*(8*L_4*M*(4*M_2 + (-16 + 15*E_2)*M*r0 + E_2*r0_2) - L_2*r0*(64*(-61 + 66*E_2)*M_4 + 4*(220 - 841*E_2 + 132*E_4)*M_3*r0 + 4*(298 + 261*E_2 - 122*E_4)*M_2*r0_2 + 4*(-54 - 55*E_2 + 19*E_4)*M*r0_3 + 13*E_2*r0_4) - E_2*r0_2*(7104*M_5 + 144*(-12 + 17*E_2)*M_4*r0 + (-4937 + 2528*E_2)*M_3*r0_2 + (3037 - 864*E_2)*M_2*r0_3 + 2*(-460 + 101*E_2)*M*r0_4 + 42*r0_5)) - a_2*r0*(-24*L_4*M*(48*M_3 + (218 - 342*E_2)*M_2*r0 + (-180 + 253*E_2)*M*r0_2 + (27 - 37*E_2)*r0_3) + 3*E_2*r0_4*(3346*M_4 + 3*(-1429 + 620*E_2)*M_3*r0 + (3385 - 632*E_2)*M_2*r0_2 + 2*(-529 + 95*E_2)*M*r0_3 + 40*r0_4) + L_2*r0*(21312*M_5 + 48*(-354 + 209*E_2)*M_4*r0 + 2*(-3936 - 5001*E_2 + 1084*E_4)*M_3*r0_2 + (8376 + 4361*E_2 - 1732*E_4)*M_2*r0_3 + 4*(-342 - 338*E_2 + 125*E_4)*M*r0_4 + 156*E_2*r0_5)))/(3.*r0_2*expr2_4);\n    A05400 = (r0*(-36*a_5*E*L*M*(-4*M + (-15 + 2*E_2)*r0) + 9*a_6*E_2*(-8*M_2 + 4*(-15 + E_2)*M*r0 + r0_2) + 4*a_3*E*L*M*r0*(12*(-107 + 66*E_2)*M_2 - 6*(-61 + 56*E_2)*M*r0 + (80 + 33*E_2)*r0_2) + a_4*(36*L_2*M*(-2*M + E_2*r0) + E_2*r0*(-24*(-107 + 44*E_2)*M_3 + 24*(19 + 5*E_2)*M_2*r0 - 2*(238 + 27*E_2)*M*r0_2 - 13*r0_3)) + r0_2*(-6*L_2*M*(2*M - r0)*(408*M_2 + 4*(-81 + 26*E_2)*M*r0 + (60 - 59*E_2)*r0_2) + E_2*r0_3*(2064*M_3 + 45*(-45 + 4*E_2)*M_2*r0 + 2*(244 - 45*E_2)*M*r0_2 + 2*r0_3)) + 4*a*E*L*M*r0*(12*L_2*(22*M_2 - 23*M*r0 + 6*r0_2) + r0*(2448*M_3 + 3*(-841 + 208*E_2)*M_2*r0 + 2*(406 - 213*E_2)*M*r0_2 + (-70 + 51*E_2)*r0_3)) - a_2*r0*(6*L_2*M*((-428 + 528*E_2)*M_2 - 4*(-80 + 97*E_2)*M*r0 + (-48 + 61*E_2)*r0_2) + E_2*r0*(4896*M_4 + 12*(-313 + 104*E_2)*M_3*r0 + (1919 - 372*E_2)*M_2*r0_2 + 60*(-11 + 3*E_2)*M*r0_3 + 62*r0_4))))/(12.*expr2_4);\n    A06000 = (4*E_2*r0_6)/expr2_3;\n    A06020 = (2*(-24*a_5*E*L*M*(12*M_2 + (-8 + 7*E_2)*M*r0 + (-8 + E_2)*r0_2) + 3*a_6*E_2*(48*M_3 + 4*(-22 + 5*E_2)*M_2*r0 + 2*(-35 + 6*E_2)*M*r0_2 + r0_3) - 8*a_3*E*L*M*r0*(-192*M_3 + 2*(63 - 2*E_2)*M_2*r0 + (93 + 34*E_2)*M*r0_2 + 3*(-19 + 2*E_2)*r0_3 + 6*L_2*(4*M + (-4 + E_2)*r0)) + 8*a*E*L*M*r0_2*(r0_2*(108*M_2 - (135 + 13*E_2)*M*r0 + 3*(11 - 5*E_2)*r0_2) + L_2*(84*M_2 + (-123 + 4*E_2)*M*r0 + 3*(11 - 2*E_2)*r0_2)) + r0_2*(-8*L_4*M*(2*M - r0)*(24*M + (-12 + E_2)*r0) + E_2*M*r0_5*(427*M + 20*(-10 + E_2)*r0) + L_2*r0_2*(-384*M_3 + 3*(128 + 161*E_2)*M_2*r0 + 4*(-24 - 51*E_2 + 4*E_4)*M*r0_2 - 12*E_2*r0_3)) + a_4*(3*L_2*(48*M_3 + 4*(6 + 19*E_2)*M_2*r0 + 4*E_2*(-17 + 2*E_2)*M*r0_2 + E_2*r0_3) + E_2*r0*(-768*M_4 + 16*(39 - 4*E_2)*M_3*r0 + 8*(16 + E_2)*M_2*r0_2 + 2*(-283 + 46*E_2)*M*r0_3 + 39*r0_4)) + a_2*r0*(24*L_4*M*(3*M + E_2*r0) + E_2*r0_3*(62*M_3 + (783 - 100*E_2)*M_2*r0 + 2*(-311 + 38*E_2)*M*r0_2 + 36*r0_3) + L_2*(-768*M_4 - 128*(-3 + 2*E_2)*M_3*r0 + 8*(33 + 108*E_2 - 2*E_4)*M_2*r0_2 + 2*(-48 - 219*E_2 + 20*E_4)*M*r0_3 + 48*E_2*r0_4))))/(3.*r0*expr2_4);\n    A06200 = (r0_2*(-24*a_3*E*L*M*(6*M + (-7 + 2*E_2)*r0) + 3*a_4*E_2*(24*M_2 + 8*(-7 + E_2)*M*r0 + 5*r0_2) + 8*a*E*L*M*r0*(96*M_2 + (-111 + 2*E_2)*M*r0 - 6*(-4 + E_2)*r0_2) + M*r0*(-4*L_2*(2*M - r0)*(48*M + (-24 + E_2)*r0) + E_2*r0_3*(427*M + 20*(-10 + E_2)*r0)) + a_2*(24*L_2*M*(3*M + E_2*r0) + 2*E_2*r0*(-192*M_3 - 4*(-63 + E_2)*M_2*r0 + (-199 + 22*E_2)*M*r0_2 + 18*r0_3))))/(6.*expr2_4);\n    A07000 = (2*E*r0_4*(-4*a*L*M + a_2*E*(4*M - r0) + 5*E*M*r0_2))/expr2_4;\n    A07020 = (-12*a_7*E*L*M*(4*(-25 + 16*E_2)*M_2 + 2*(-8 + E_2 + 3*E_4)*M*r0 + 3*(11 + E_2)*r0_2) + 3*a_8*E_2*(40*(-5 + 2*E_2)*M_3 + 4*(12 + 13*E_2)*M_2*r0 - 2*(-79 + 7*E_2)*M*r0_2 + 3*r0_3) - 4*a*E*L*M*r0_2*(-4*L_4*(64*M_2 - 62*M*r0 + 15*r0_2) + L_2*r0*(-1320*M_3 + 3*(693 - 188*E_2)*M_2*r0 + 4*(-249 + 112*E_2)*M*r0_2 + 3*(44 - 23*E_2)*r0_3) + r0_3*(-1896*M_3 + (2719 - 377*E_2)*M_2*r0 + 2*(-628 + 191*E_2 + 27*E_4)*M*r0_2 + 3*(58 - 17*E_2)*r0_3)) - 4*a_5*E*L*M*(3*L_2*(32*M_2 + 2*(-40 + 21*E_2)*M*r0 + 33*r0_2) + r0*(-24*(-99 + 58*E_2)*M_3 + 2*(-302 + 67*E_2 + 12*E_4)*M_2*r0 + 2*(-499 + 64*E_2 + 51*E_4)*M*r0_2 + 3*(131 - 6*E_2)*r0_3)) + M*r0_3*(E_2*r0_5*(1799*M_2 + 2*(-843 + 80*E_2)*M*r0 + 2*(191 - 29*E_2)*r0_2) - 12*L_4*(2*M - r0)*(132*M_2 + (-106 + 47*E_2)*M*r0 + (20 - 23*E_2)*r0_2) + L_2*r0_2*(-3360*M_3 + (4416 + 1187*E_2)*M_2*r0 + 12*(-158 - 77*E_2 + 20*E_4)*M*r0_2 + 2*(132 + 77*E_2 - 54*E_4)*r0_3)) + a_6*(L_2*(24*(-25 + 36*E_2)*M_3 + 12*(-28 - 63*E_2 + 26*E_4)*M_2*r0 + 474*E_2*M*r0_2 + 9*E_2*r0_3) - E_2*r0*(16*(-297 + 124*E_2)*M_4 + 4*(382 + 113*E_2)*M_3*r0 - 54*(-39 + 16*E_2)*M_2*r0_2 + 3*(-601 + 64*E_2)*M*r0_3 + 9*r0_4)) - 4*a_3*E*L*M*r0*(24*L_4*M + L_2*(-208*M_3 - 24*(-61 + 29*E_2)*M_2*r0 + 2*(-567 + 274*E_2)*M*r0_2 + 3*(84 - 23*E_2)*r0_3) + r0*(-3648*M_4 + 2*(1387 - 500*E_2)*M_3*r0 + (1995 - 317*E_2)*M_2*r0_2 + 2*(-1124 + 234*E_2 + 69*E_4)*M*r0_3 + 12*(39 - 7*E_2)*r0_4)) + a_4*(24*L_4*M_2*(2*M + (-14 + 15*E_2)*r0) - L_2*r0*(48*(-99 + 100*E_2)*M_4 + 4*(222 - 1159*E_2 + 184*E_4)*M_3*r0 - 4*(-408 - 802*E_2 + 303*E_4)*M_2*r0_2 + (-216 - 1307*E_2 + 152*E_4)*M*r0_3 + 36*E_2*r0_4) - E_2*r0_2*(7296*M_5 + 8*(-643 + 316*E_2)*M_4*r0 + 52*(-63 + 50*E_2)*M_3*r0_2 + (6293 - 1452*E_2)*M_2*r0_3 + (-2665 + 316*E_2)*M*r0_4 + 90*r0_5)) - a_2*r0*(-4*L_4*M*(96*M_3 + 24*(23 - 31*E_2)*M_2*r0 + 35*(-12 + 17*E_2)*M*r0_2 + (54 - 91*E_2)*r0_3) + E_2*r0_4*(2654*M_4 + (-6017 + 1988*E_2)*M_3*r0 + (5939 - 856*E_2)*M_2*r0_2 + 16*(-113 + 14*E_2)*M*r0_3 + 72*r0_4) + L_2*r0*(7296*M_5 + 64*(-93 + 56*E_2)*M_4*r0 + 24*(-126 - 230*E_2 + 47*E_4)*M_3*r0_2 + (3096 + 3429*E_2 - 1228*E_4)*M_2*r0_3 + 2*(-240 - 567*E_2 + 130*E_4)*M*r0_4 + 84*E_2*r0_5)))/(3.*r0_2*expr2_5);\n    A07200 = -(r0*(12*a_5*E*L*M*(4*(-11 + 6*E_2)*M + (13 - 4*E_2)*r0) - 3*a_6*E_2*(8*(-11 + 4*E_2)*M_2 + (52 - 8*E_2)*M*r0 - 3*r0_2) - 4*a_3*E*L*M*(-24*L_2*M + r0*(120*(-9 + 5*E_2)*M_2 + 20*(43 - 20*E_2)*M*r0 + 3*(-54 + 19*E_2)*r0_2)) + M*r0_2*(2*L_2*(2*M - r0)*(840*M_2 + 2*(-342 + 113*E_2)*M*r0 + (132 - 115*E_2)*r0_2) + E_2*r0_3*(-1799*M_2 - 2*(-843 + 80*E_2)*M*r0 + 2*(-191 + 29*E_2)*r0_2)) + a_4*(24*L_2*M*((11 - 12*E_2)*M + E_2*r0) + E_2*r0*(80*(-27 + 10*E_2)*M_3 - 8*(-241 + 51*E_2)*M_2*r0 + (-859 + 130*E_2)*M*r0_2 + 42*r0_3)) - 4*a*E*L*M*r0*(4*L_2*(50*M_2 - 49*M*r0 + 12*r0_2) + r0*(1680*M_3 + (-2239 + 452*E_2)*M_2*r0 + 2*(487 - 179*E_2)*M*r0_2 + 9*(-14 + 5*E_2)*r0_3)) + a_2*r0*(2*L_2*M*(120*(-9 + 10*E_2)*M_2 + (756 - 988*E_2)*M*r0 + (-96 + 145*E_2)*r0_2) + E_2*r0*(3360*M_4 + 4*(-1135 + 226*E_2)*M_3*r0 + (3707 - 520*E_2)*M_2*r0_2 + 4*(-293 + 41*E_2)*M*r0_3 + 72*r0_4))))/(12.*expr2_5);\n    A08000 = (r0_2*(-12*a_3*E*L*M*(2*M + (-3 + E_2)*r0) + 3*a_4*E_2*(4*M_2 + 2*(-6 + E_2)*M*r0 + r0_2) + 4*a*E*L*M*r0*(24*M_2 + 2*(-18 + E_2)*M*r0 - 3*(-3 + E_2)*r0_2) + M*r0*(E_2*r0_3*(65*M + 4*(-7 + E_2)*r0) - 2*L_2*(2*M - r0)*(12*M + (-6 + E_2)*r0)) + 2*a_2*(3*L_2*M*(2*M + E_2*r0) + E_2*r0*(-24*M_3 - 2*(-24 + E_2)*M_2*r0 + 5*(-7 + E_2)*M*r0_2 + 3*r0_3))))/(3.*expr2_5);\n    A09000 = -(r0*(6*a_5*E*L*M*(4*(-5 + 3*E_2)*M + (14 - 3*E_2)*r0) + 3*a_6*E_2*((20 - 8*E_2)*M_2 + (-28 + 3*E_2)*M*r0 + r0_2) + 2*a_3*E*L*M*(12*L_2*M + r0*((324 - 168*E_2)*M_2 + 2*(-181 + 71*E_2)*M*r0 + 3*(30 - 7*E_2)*r0_2)) + 3*M*r0_2*(L_2*(2*M - r0)*(72*M_2 + 20*(-3 + E_2)*M*r0 + 3*(4 - 3*E_2)*r0_2) + 2*E_2*r0_3*(-45*M_2 + (41 - 4*E_2)*M*r0 + (-9 + E_2)*r0_2)) + a_4*(3*L_2*M*(-4*(-5 + 6*E_2)*M + 3*E_2*r0) + E_2*r0*(4*(-81 + 28*E_2)*M_3 + (508 - 90*E_2)*M_2*r0 + (-245 + 26*E_2)*M*r0_2 + 9*r0_3)) - 4*a*E*L*M*r0*(L_2*(28*M_2 - 26*M*r0 + 6*r0_2) + r0*(216*M_3 + 4*(-86 + 15*E_2)*M_2*r0 + 13*(13 - 4*E_2)*M*r0_2 + 6*(-4 + E_2)*r0_3)) + a_2*r0*(2*L_2*M*(6*(-27 + 28*E_2)*M_2 + (108 - 149*E_2)*M*r0 + 4*(-3 + 5*E_2)*r0_2) + E_2*r0*(432*M_4 + 40*(-20 + 3*E_2)*M_3*r0 + 2*(367 - 47*E_2)*M_2*r0_2 + (-227 + 23*E_2)*M*r0_3 + 12*r0_4))))/(6.*expr2_6);\n  }\n\n  /* A_{t,r} */\n  {\n    A10061 = (-256*E*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*expr1_3)/(r0_5*(a_2 + r0*(-2*M + r0)));\n    A10081 = (-128*expr1_2*(a_11*E_2*M_2*(312*M_2 + 289*M*r0 + 66*r0_2) + a_10*E*L*M_2*(-624*M_2 + (-589 + 32*E_2)*M*r0 + 6*(-23 + 3*E_2)*r0_2) + a_8*E*L*M*r0*(1248*M_4 + 4*(-193 + 18*E_2)*M_3*r0 + 4*(-479 + 45*E_2)*M_2*r0_2 + (-525 + 74*E_2)*M*r0_3 + 6*(5 - 3*E_2)*r0_4 - 2*L_2*M*(284*M + 3*(44 - 3*E_2)*r0)) + E*L*(2*M - r0)*r0_5*(36*L_6*M*(-2*M + r0) + 6*L_4*M*r0_2*(-26*M + (13 + 4*E_2)*r0) + 4*M*r0_6*(8*M + (-4 + 7*E_2)*r0) + L_2*r0_4*(-64*M_2 + 2*(19 + 24*E_2)*M*r0 - 3*r0_2)) + a_2*E*L*r0_3*(-12*L_6*M*(28*M_2 - 20*M*r0 + 3*r0_2) + 2*M*r0_5*(284*M_3 - 6*(-7 + 2*E_2)*M_2*r0 + 10*(-17 + 8*E_2)*M*r0_2 + 3*(13 - 17*E_2)*r0_3) - 6*L_4*M*r0*(156*M_3 - 8*(4 + E_2)*M_2*r0 - (67 + 16*E_2)*M*r0_2 + 2*(11 + 4*E_2)*r0_3) + L_2*r0_3*(-512*M_4 + 648*M_3*r0 + 2*(-43 + 108*E_2)*M_2*r0_2 - (61 + 138*E_2)*M*r0_3 + 3*r0_4)) + a_9*M*(2*L_2*M*(156*M_2 + 2*(75 + 59*E_2)*M*r0 + 3*(12 + 17*E_2)*r0_2) + E_2*r0*(-16*(39 + 14*E_2)*M_4 + 4*(169 - 84*E_2)*M_3*r0 - 7*(-187 + 24*E_2)*M_2*r0_2 + 2*(201 - 14*E_2)*M*r0_3 + 6*r0_4)) + a_6*E*L*M*r0*(-126*L_4*M*r0 + r0_2*(-68*(-39 + 4*E_2)*M_4 - 4*(-369 + 58*E_2)*M_3*r0 + (-2101 + 168*E_2)*M_2*r0_2 + 6*(-142 + 21*E_2)*M*r0_3 + 2*(53 - 41*E_2)*r0_4) + 2*L_2*(-896*M_4 + 60*(5 + 2*E_2)*M_3*r0 + 60*(-5 + 2*E_2)*M_2*r0_2 + 42*(-9 + E_2)*M*r0_3 + 3*(2 - 7*E_2)*r0_4)) - 2*a_3*M*r0_2*(E_2*r0_6*(456*M_3 + 12*(11 + 9*E_2)*M_2*r0 + (-201 + 68*E_2)*M*r0_2 - 12*r0_3) - 6*L_6*(52*M_3 + 2*(-19 + 6*E_2)*M_2*r0 - 8*E_2*M*r0_2 + (3 + 2*E_2)*r0_3) + L_2*r0_3*(96*M_4 - 4*(-60 + 83*E_2)*M_3*r0 + 48*(-3 + 2*E_2 + 3*E_4)*M_2*r0_2 + 4*(-21 + 16*E_2 + 27*E_4)*M*r0_3 + 7*(6 - 11*E_2)*r0_4) - 6*L_4*r0*(16*M_4 + 12*(-1 + 9*E_2)*M_3*r0 - 2*(9 - 5*E_2 + 4*E_4)*M_2*r0_2 + (20 - 35*E_2 - 8*E_4)*M*r0_3 + 5*(-1 + 2*E_2)*r0_4)) - a_5*M*r0*(-72*L_6*M*r0 + 3*E_2*r0_4*(364*M_4 + 4*(173 + 36*E_2)*M_3*r0 + (-221 + 200*E_2)*M_2*r0_2 + 4*(-73 + 16*E_2)*M*r0_3 - 12*r0_4) + L_2*r0_2*(-64*(-12 + 25*E_2)*M_4 + 8*(51 - 70*E_2 + 36*E_4)*M_3*r0 + 12*(-53 + 5*E_2 + 48*E_4)*M_2*r0_2 + 6*(-24 - 67*E_2 + 36*E_4)*M*r0_3 + (96 - 133*E_2)*r0_4) - 12*L_4*(56*M_4 + 2*(-25 + 42*E_2)*M_3*r0 + (3 - 6*E_2 - 8*E_4)*M_2*r0_2 - 2*(-11 + E_2 + 2*E_4)*M*r0_3 + (-3 + 5*E_2)*r0_4)) + a_7*M*r0*(12*L_4*M*(25*M + 3*(4 + E_2)*r0) - 3*E_2*r0_2*((628 + 96*E_2)*M_4 + 16*(17 + 16*E_2)*M_3*r0 + (-597 + 184*E_2)*M_2*r0_2 + 4*(-73 + 10*E_2)*M*r0_3 - 8*r0_4) + 2*L_2*(24*(-13 + 28*E_2)*M_4 + 12*(4 + 11*E_2 - 12*E_4)*M_3*r0 + 12*(25 + 23*E_2 - 12*E_4)*M_2*r0_2 + (60 + 221*E_2 - 36*E_4)*M*r0_3 + 3*(-6 + 7*E_2)*r0_4)) + a_4*E*L*M*r0_2*(-6*L_4*(264*M_3 - 24*(4 + E_2)*M_2*r0 - 2*(1 + 4*E_2)*M*r0_2 + (9 + 4*E_2)*r0_3) + r0_3*(808*M_4 - 4*(-535 + 48*E_2)*M_3*r0 + 2*(-373 + 14*E_2)*M_2*r0_2 + 3*(-247 + 58*E_2)*M*r0_3 - 138*(-1 + E_2)*r0_4) + L_2*r0*(-1232*M_4 - 8*(-73 + 21*E_2)*M_3*r0 + 8*(85 + 27*E_2)*M_2*r0_2 + 2*(-356 + 93*E_2)*M*r0_3 - 11*(1 + 12*E_2)*r0_4)) + a*M*r0_3*(36*L_8*r0_2M_2 + 6*E_2*r0_8*(-26*M_2 + (11 - 6*E_2)*M*r0 + r0_2) + 12*L_4*r0_4*(8*(-1 + 5*E_2)*M_2 - 2*(-4 + 15*E_2 + 2*E_4)*M*r0 + (-2 + 5*E_2)*r0_2) + L_2*r0_5*(-96*M_3 + 184*E_2*M_2*r0 - 2*(-36 + 109*E_2 + 36*E_4)*M*r0_2 + 3*(-8 + 21*E_2)*r0_3) + 12*L_6*(2*M - r0)*r0*(4*M_2 - (3 + 2*E_2)*r0_2 + 4*M*(r0 + 2*E_2*r0)))))/(3.*r0_11*expr2_2);\n    A10241 = (-192*E*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*expr1_2)/(r0_2*(a_2 + r0*(-2*M + r0)));\n    A10261 = (64*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_11*E_2*M*(12*(-55 + 2*E_2)*M_3 + (-619 + 24*E_2)*M_2*r0 + 6*(-23 + E_2)*M*r0_2 + 3*r0_3) - a_10*E*L*M*(24*(-55 + 3*E_2)*M_3 + (-1261 + 122*E_2)*M_2*r0 + 3*(-98 + 17*E_2)*M*r0_2 + 3*(1 + E_2)*r0_3) - E*L*(2*M - r0)*r0_5*(-81*L_6*M*(2*M - r0) + 2*L_4*M*r0_2*(-170*M + 85*r0 + 24*E_2*r0) + L_2*M*r0_4*(-126*M + 63*r0 + 100*E_2*r0) + r0_6*(64*M_2 - 38*M*r0 + 56*E_2*M*r0 + 3*r0_2)) + a*M*r0_3*(-72*L_8*r0_2M_2 + 3*E_2*r0_8*(104*M_2 + 2*(-25 + 12*E_2)*M*r0 - r0_2) + L_4*r0_4*(-4*(-78 + 277*E_2)*M_2 + 4*(-78 + 212*E_2 + 21*E_4)*M*r0 + 3*(26 - 49*E_2)*r0_2) - 6*L_6*(2*M - r0)*r0*(12*M_2 + 2*(7 + 20*E_2)*M*r0 - (10 + 9*E_2)*r0_2) + L_2*r0_5*(144*M_3 - 8*(-15 + 46*E_2)*M_2*r0 + 2*(-114 + 209*E_2 + 72*E_4)*M*r0_2 + 3*(22 - 39*E_2)*r0_3)) + a_9*M*(2*L_2*(6*(-55 + 6*E_2)*M_3 + (-321 - 235*E_2 + 6*E_4)*M_2*r0 + 3*(-26 - 35*E_2 + E_4)*M*r0_2 + 6*E_2*r0_3) + E_2*r0*(40*(33 + 10*E_2)*M_4 + 48*(-28 + 15*E_2)*M_3*r0 + 111*(-25 + 4*E_2)*M_2*r0_2 + 4*(-215 + 23*E_2)*M*r0_3 + 3*r0_4)) + a_2*E*L*r0_3*(6*L_6*M*(116*M_2 - 84*M*r0 + 13*r0_2) + L_4*M*r0*(1852*M_3 - 4*(127 + 3*E_2)*M_2*r0 - 3*(253 + 64*E_2)*M*r0_2 + (275 + 93*E_2)*r0_3) + L_2*r0_3*(1160*M_4 + 4*(-433 + 19*E_2)*M_3*r0 + 2*(207 - 251*E_2)*M_2*r0_2 + (75 + 287*E_2)*M*r0_3 + 3*r0_4) + r0_5*(-980*M_4 + 12*(-33 + 4*E_2)*M_3*r0 + 5*(157 - 70*E_2)*M_2*r0_2 + 21*(-9 + 10*E_2)*M*r0_3 + 9*r0_4)) + a_3*M*r0_2*(E_2*r0_6*(1680*M_3 + 44*(14 + 9*E_2)*M_2*r0 + 10*(-86 + 29*E_2)*M*r0_2 - 15*r0_3) - 12*L_6*(102*M_3 + (-77 + 30*E_2)*M_2*r0 + (3 - 19*E_2)*M*r0_2 + (5 + 4*E_2)*r0_3) + 2*L_2*r0_3*(144*M_4 + (600 - 892*E_2)*M_3*r0 + 2*(-144 + 250*E_2 + 135*E_4)*M_2*r0_2 + (-240 + 57*E_2 + 221*E_4)*M*r0_3 + (108 - 149*E_2)*r0_4) + L_4*r0*(-288*M_4 - 8*(-51 + 346*E_2)*M_3*r0 + 84*(5 + 2*E_4)*M_2*r0_2 + 4*(-156 + 223*E_2 + 42*E_4)*M*r0_3 + 3*(58 - 89*E_2)*r0_4)) + a_5*M*r0*(6*L_6*r0*(-26*M + E_2*r0) + E_2*r0_4*(2004*M_4 + 80*(53 + 9*E_2)*M_3*r0 + 27*(-43 + 44*E_2)*M_2*r0_2 + 6*(-309 + 74*E_2)*M*r0_3 - 24*r0_4) + L_4*(-1296*M_4 + 12*(107 - 176*E_2)*M_3*r0 + 2*(-75 + 188*E_2 + 84*E_4)*M_2*r0_2 + 4*(-147 - E_2 + 21*E_4)*M*r0_3 + 3*(32 - 35*E_2)*r0_4) + L_2*r0_2*(-32*(-51 + 106*E_2)*M_4 + 4*(249 - 179*E_2 + 126*E_4)*M_3*r0 + 2*(-717 + 411*E_2 + 554*E_4)*M_2*r0_2 + 2*(-180 - 557*E_2 + 229*E_4)*M*r0_3 + 13*(18 - 19*E_2)*r0_4)) - a_8*E*L*M*(L_2*(24*M_3 + 2*(-605 + 18*E_2)*M_2*r0 + 3*(-189 + 17*E_2)*M*r0_2 + 3*(3 + 2*E_2)*r0_3) + r0*(-48*(-55 + 2*E_2)*M_4 + 16*(-96 + 25*E_2)*M_3*r0 + 4*(-1028 + 157*E_2)*M_2*r0_2 + (-1144 + 177*E_2)*M*r0_3 + 3*(28 - 11*E_2)*r0_4)) + a_7*M*r0*(3*L_4*(2*(-107 + 6*E_2)*M_2 - 8*(13 + 3*E_2)*M*r0 + 5*E_2*r0_2) + E_2*r0_2*(4*(961 + 108*E_2)*M_4 + 4*(485 + 354*E_2)*M_3*r0 + 3*(-1207 + 404*E_2)*M_2*r0_2 + 6*(-311 + 52*E_2)*M*r0_3 - 12*r0_4) - 2*L_2*(12*(-55 + 112*E_2)*M_4 + 2*(48 + 19*E_2 - 130*E_4)*M_3*r0 + 2*(327 + 248*E_2 - 145*E_4)*M_2*r0_2 + (132 + 524*E_2 - 83*E_4)*M*r0_3 + 3*(-14 + 9*E_2)*r0_4)) - a_6*E*L*r0*(3*L_4*M*(4*M_2 - 91*M*r0 + (3 + E_2)*r0_2) + L_2*M*(-3488*M_4 + 8*(191 + 42*E_2)*M_3*r0 + 2*(-663 + 298*E_2)*M_2*r0_2 + 4*(-437 + 52*E_2)*M*r0_3 + 3*(22 - 25*E_2)*r0_4) + r0_2*((5476 - 904*E_2)*M_5 + 4*(863 - 119*E_2)*M_4*r0 + (-4413 + 854*E_2)*M_3*r0_2 + (-1951 + 311*E_2)*M_2*r0_3 + (260 - 173*E_2)*M*r0_4 - 3*r0_5)) + a_4*E*L*r0_2*(-3*L_6*M*r0 + 2*L_4*M*(1576*M_3 - 6*(107 + 18*E_2)*M_2*r0 + (41 - 48*E_2)*M*r0_2 + 3*(16 + 7*E_2)*r0_3) + L_2*r0*(2344*M_5 + 4*(-396 + 139*E_2)*M_4*r0 - 72*(23 + 7*E_2)*M_3*r0_2 + 17*(109 - 27*E_2)*M_2*r0_3 + (-39 + 268*E_2)*M*r0_4 + 3*r0_5) + r0_3*(-1376*M_5 + 4*(-1159 + 195*E_2)*M_4*r0 + 2*(631 - 166*E_2)*M_3*r0_2 - 9*(-194 + 47*E_2)*M_2*r0_3 + 3*(-108 + 97*E_2)*M*r0_4 + 9*r0_5))))/(3.*r0_8*expr2_2);\n    A10421 = (-48*E*r0*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(a_2 + r0*(-2*M + r0));\n    A10441 = (8*(a_11*E_2*M*(72*(-29 + 2*E_2)*M_3 + (-1957 + 144*E_2)*M_2*r0 + 18*(-23 + 2*E_2)*M*r0_2 + 21*r0_3) - a_10*E*L*M*(144*(-29 + 3*E_2)*M_3 + (-4009 + 560*E_2)*M_2*r0 + 6*(-153 + 35*E_2)*M*r0_2 + 3*(7 + 6*E_2)*r0_3) - E*L*(2*M - r0)*r0_5*(-278*L_6*M*(2*M - r0) + L_4*r0_2*(-1108*M_2 + 4*(133 + 37*E_2)*M*r0 + 11*r0_2) + r0_6*(192*M_2 + 2*(-71 + 84*E_2)*M*r0 + 23*r0_2) + L_2*r0_4*(-372*M_2 + 4*(31 + 78*E_2)*M*r0 + 31*r0_2)) + a*M*r0_3*(-216*L_8*r0_2M_2 + 3*E_2*r0_8*(312*M_2 + 2*(-89 + 36*E_2)*M*r0 + 11*r0_2) - 4*L_6*(2*M - r0)*r0*(36*M_2 + 2*(27 + 115*E_2)*M*r0 - 3*(12 + 17*E_2)*r0_2) - 3*L_4*r0_4*(4*(-108 + 319*E_2)*M_2 - 4*(-108 + 243*E_2 + 19*E_4)*M*r0 + (-108 + 167*E_2)*r0_2) + 3*L_2*r0_5*(96*M_3 + 16*(15 - 23*E_2)*M_2*r0 + 2*(-156 + 187*E_2 + 72*E_4)*M*r0_2 + (84 - 95*E_2)*r0_3)) + a_2*E*L*r0_3*(8*L_6*M*(272*M_2 - 202*M*r0 + 33*r0_2) + L_4*r0*(5592*M_4 + 172*(-11 + 2*E_2)*M_3*r0 - 2*(1093 + 350*E_2)*M_2*r0_2 + (839 + 286*E_2)*M*r0_3 + 14*r0_4) + L_2*r0_3*(3840*M_4 + 16*(-391 + 36*E_2)*M_3*r0 + 10*(187 - 180*E_2)*M_2*r0_2 + (7 + 906*E_2)*M*r0_3 + 71*r0_4) + r0_5*(-2472*M_4 + 8*(-221 + 18*E_2)*M_3*r0 - 12*(-219 + 95*E_2)*M_2*r0_2 + (-707 + 648*E_2)*M*r0_3 + 72*r0_4)) + a_9*M*(2*L_2*(36*(-29 + 6*E_2)*M_3 + 2*(-513 - 341*E_2 + 24*E_4)*M_2*r0 + 3*(-84 - 101*E_2 + 8*E_4)*M*r0_2 + 39*E_2*r0_3) + E_2*r0*(48*(87 + 22*E_2)*M_4 + 4*(-1009 + 576*E_2)*M_3*r0 + (-8705 + 1656*E_2)*M_2*r0_2 + 2*(-1319 + 192*E_2)*M*r0_3 + 77*r0_4)) + a_5*M*r0*(36*L_6*r0*((-14 + E_2)*M + E_2*r0) + E_2*r0_4*(5364*M_4 + 12*(1069 + 144*E_2)*M_3*r0 + 7*(-457 + 504*E_2)*M_2*r0_2 + 108*(-53 + 14*E_2)*M*r0_3 + 162*r0_4) + L_2*r0_2*(-192*(-27 + 56*E_2)*M_4 + 8*(441 - 130*E_2 + 144*E_4)*M_3*r0 + 4*(-1197 + 1055*E_2 + 792*E_4)*M_2*r0_2 + 2*(-648 - 1993*E_2 + 768*E_4)*M*r0_3 + (828 - 581*E_2)*r0_4) + L_4*(-3744*M_4 - 72*(-57 + 94*E_2)*M_3*r0 + 4*(-171 + 486*E_2 + 104*E_4)*M_2*r0_2 + 4*(-486 - E_2 + 67*E_4)*M*r0_3 + (360 - 283*E_2)*r0_4)) + a_3*M*r0_2*(E_2*r0_6*(4608*M_3 + 12*(163 + 90*E_2)*M_2*r0 + 14*(-197 + 66*E_2)*M*r0_2 + 113*r0_3) - 4*L_6*(900*M_3 + 6*(-117 + 56*E_2)*M_2*r0 + (54 - 227*E_2)*M*r0_2 + 6*(6 + 7*E_2)*r0_3) + L_4*r0*(-576*M_4 - 48*(-33 + 191*E_2)*M_3*r0 + 4*(306 + 167*E_2 + 96*E_4)*M_2*r0_2 + 4*(-576 + 751*E_2 + 121*E_4)*M*r0_3 + (684 - 877*E_2)*r0_4) + 2*L_2*r0_3*(288*M_4 + 12*(180 - 271*E_2)*M_3*r0 + 16*(-54 + 136*E_2 + 45*E_4)*M_2*r0_2 + 4*(-234 - 5*E_2 + 174*E_4)*M*r0_3 + 3*(132 - 125*E_2)*r0_4)) + a_7*M*r0*(3*L_4*(12*(-57 + 8*E_2)*M_2 + 4*(-84 - 13*E_2 + E_4)*M*r0 + 31*E_2*r0_2) + E_2*r0_2*(12*(977 + 72*E_2)*M_4 + 16*(413 + 243*E_2)*M_3*r0 + 11*(-997 + 360*E_2)*M_2*r0_2 + 36*(-159 + 32*E_2)*M*r0_3 + 138*r0_4) - 2*L_2*(72*(-29 + 56*E_2)*M_4 - 4*(-72 + 131*E_2 + 168*E_4)*M_3*r0 + 4*(531 + 329*E_2 - 228*E_4)*M_2*r0_2 + (432 + 1729*E_2 - 312*E_4)*M*r0_3 + (-144 + 19*E_2)*r0_4)) - a_6*E*L*r0*(3*L_4*M*(32*M_2 + 2*(-143 + 6*E_2)*M*r0 + (19 + 6*E_2)*r0_2) + r0_2*((16908 - 3792*E_2)*M_5 - 12*(-979 + 126*E_2)*M_4*r0 + (-13853 + 3952*E_2)*M_3*r0_2 + 2*(-3228 + 581*E_2)*M_2*r0_3 + 2*(460 - 273*E_2)*M*r0_4 - 32*r0_5) + 2*L_2*(-5088*M_5 + 4*(691 + 54*E_2)*M_4*r0 + 24*(-91 + 48*E_2)*M_3*r0_2 + 6*(-487 + 77*E_2)*M_2*r0_3 + (167 - 105*E_2)*M*r0_4 - 3*r0_5)) - a_8*E*L*(2*L_2*M*(72*M_3 + 4*(-479 + 36*E_2)*M_2*r0 + 3*(-298 + 41*E_2)*M*r0_2 + 6*(5 + 3*E_2)*r0_3) + r0*(-288*(-29 + 2*E_2)*M_5 + 4*(-1153 + 482*E_2)*M_4*r0 + 4*(-3277 + 677*E_2)*M_3*r0_2 + 5*(-729 + 134*E_2)*M_2*r0_3 + 2*(163 - 45*E_2)*M*r0_4 - 3*r0_5)) + a_4*E*L*r0_2*(-6*L_6*M*(2*M + 3*r0) + L_4*(9456*M_4 - 48*(89 + 8*E_2)*M_3*r0 - 8*(-65 + 53*E_2)*M_2*r0_2 + 8*(32 + 15*E_2)*M*r0_3 + 3*r0_4) + L_2*r0*(6672*M_5 + 8*(-743 + 339*E_2)*M_4*r0 - 8*(710 + 207*E_2)*M_3*r0_2 + 2*(3326 - 927*E_2)*M_2*r0_3 + (-323 + 840*E_2)*M*r0_4 + 46*r0_5) + r0_3*(-3336*M_5 + 4*(-3727 + 828*E_2)*M_4*r0 + 2*(1601 - 846*E_2)*M_3*r0_2 + (5881 - 1506*E_2)*M_2*r0_3 + 54*(-21 + 17*E_2)*M*r0_4 + 78*r0_5))))/(3.*r0_5*expr2_2);\n    A10601 = (-4*E*r0_4*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/(a_2 + r0*(-2*M + r0));\n    A10621 = (4*(6*a_9*E_2*M*((-61 + 6*E_2)*M_2 + (-26 + 3*E_2)*M*r0 + 2*r0_2) - 3*a_8*E*L*M*(4*(-61 + 9*E_2)*M_2 + (-112 + 25*E_2)*M*r0 + (4 + 3*E_2)*r0_2) - E*L*(2*M - r0)*r0_4*(-107*L_4*M*(2*M - r0) + 2*r0_4*(32*M_2 + 2*(-15 + 14*E_2)*M*r0 + 7*r0_2) + L_2*r0_2*(-186*M_2 + (71 + 52*E_2)*M*r0 + 11*r0_2)) + 2*a_7*M*(3*L_2*((-61 + 18*E_2)*M_2 + 2*(-15 - 7*E_2 + E_4)*M*r0 + 5*E_2*r0_2) + E_2*r0*((366 + 76*E_2)*M_3 + 2*(-261 + 83*E_2)*M_2*r0 + (-397 + 73*E_2)*M*r0_2 + 22*r0_3)) + 2*a_5*M*r0*(9*L_4*(2*(-5 + E_2)*M + E_2*r0) + E_2*r0_2*(18*(55 + 2*E_2)*M_3 + (-73 + 238*E_2)*M_2*r0 + (-624 + 155*E_2)*M*r0_2 + 41*r0_3) + L_2*((366 - 672*E_2)*M_3 + (-231 + 340*E_2 + 52*E_4)*M_2*r0 + 2*(-87 - 63*E_2 + 28*E_4)*M*r0_2 + (54 - 17*E_2)*r0_3)) + 2*a*M*r0_2*(-36*L_6*r0_2M_2 + 3*E_2*r0_6*(52*M_2 + 12*(-3 + E_2)*M*r0 + 5*r0_2) - L_4*(2*M - r0)*r0*(12*M_2 + 14*(-3 + 14*E_2)*M*r0 + 9*(2 - 5*E_2)*r0_2) + L_2*r0_3*(24*M_3 - 20*(-9 + 17*E_2)*M_2*r0 + 2*(-99 + 130*E_2 + 18*E_4)*M*r0_2 + 3*(17 - 15*E_2)*r0_3)) + 2*a_3*M*r0*(E_2*r0_4*(384*M_3 + 18*(21 + 5*E_2)*M_2*r0 + (-413 + 127*E_2)*M*r0_2 + 40*r0_3) + L_2*r0_2*((456 - 944*E_2)*M_3 + 2*(-36 + 155*E_2 + 18*E_4)*M_2*r0 + 2*(-144 + 90*E_2 + 43*E_4)*M*r0_2 + 7*(15 - 11*E_2)*r0_3) - 2*L_4*(150*M_3 + 3*(-55 + 46*E_2)*M_2*r0 + (63 - 104*E_2)*M*r0_2 + 9*(-1 + 2*E_2)*r0_3)) - a_6*E*L*(3*L_2*M*(12*M_2 + (-107 + 12*E_2)*M*r0 + (7 + 3*E_2)*r0_2) + r0*(-24*(-61 + 6*E_2)*M_4 + 2*(-753 + 254*E_2)*M_3*r0 + (-1183 + 284*E_2)*M_2*r0_2 - 4*(-32 + 9*E_2)*M*r0_3 - 3*r0_4)) + a_4*E*L*r0*(-3*L_4*M*(4*M + 3*r0) + L_2*(1648*M_4 - 12*(97 + 2*E_2)*M_3*r0 + 2*(321 - 116*E_2)*M_2*r0_2 + (-17 + 42*E_2)*M*r0_3 + 3*r0_4) + r0_2*(4*(-723 + 203*E_2)*M_4 + 6*(1 - 34*E_2)*M_3*r0 + (1652 - 507*E_2)*M_2*r0_2 + (-264 + 155*E_2)*M*r0_3 + 20*r0_4)) + a_2*E*L*r0_2*(2*L_4*M*(380*M_2 - 292*M*r0 + 51*r0_2) + L_2*r0*(1052*M_4 + 4*(-271 + 71*E_2)*M_3*r0 + (85 - 316*E_2)*M_2*r0_2 + (69 + 103*E_2)*M*r0_3 + 14*r0_4) + r0_3*(-412*M_4 + 4*(-247 + 68*E_2)*M_3*r0 + 5*(189 - 82*E_2)*M_2*r0_2 + 2*(-118 + 83*E_2)*M*r0_3 + 31*r0_4))))/(3.*r0_2*expr2_2);\n    A10801 = (r0*(3*a_7*E_2*M*(4*(-8 + E_2)*M + 3*r0) - 3*a_6*E*L*M*(4*(-16 + 3*E_2)*M + (3 + 2*E_2)*r0) + a*M*r0*(-36*L_4*r0_2M_2 - 4*L_2*(2*M - r0)*r0_2*((-30 + 62*E_2)*M - 15*(-1 + E_2)*r0) + 3*E_2*r0_4*(52*M_2 + 4*(-11 + 3*E_2)*M*r0 + 9*r0_2)) - E*L*(2*M - r0)*r0_3*(-62*L_2*M*(2*M - r0) + r0_2*(32*M_2 - 38*M*r0 + 28*E_2*M*r0 + 11*r0_2)) + a_5*M*(12*L_2*((-8 + 3*E_2)*M + E_2*r0) + E_2*r0*(32*(6 + E_2)*M_2 + 4*(-91 + 19*E_2)*M*r0 + 29*r0_2)) + a_3*M*r0*(E_2*r0_2*(500*M_2 + 4*(-92 + 25*E_2)*M*r0 + 47*r0_2) - 4*L_2*(12*(-4 + 7*E_2)*M_2 + (54 - 71*E_2)*M*r0 + 3*(-5 + 4*E_2)*r0_2)) + a_4*E*L*(-6*L_2*M*(2*M + r0) + r0*(48*(-8 + E_2)*M_3 + 4*(145 - 46*E_2)*M_2*r0 + 2*(-37 + 12*E_2)*M*r0_2 + 3*r0_3)) + a_2*E*L*r0*(20*L_2*M*(20*M_2 - 16*M*r0 + 3*r0_2) + r0_2*(4*(-185 + 62*E_2)*M_3 + 4*(133 - 55*E_2)*M_2*r0 + (-109 + 58*E_2)*M*r0_2 + 14*r0_3))))/(6.*expr2_2);\n    A11061 = (-128*expr1_2*(a_7*E_2*M*(4*M_2 + M*r0 - r0_2) + a_6*E*L*M*(-8*M_2 + (-3 + 4*E_2)*M*r0 + (1 + 3*E_2)*r0_2) + a_4*E*L*r0_2*((4 + 3*E_2)*L_2*M - 2*(15 + 2*E_2)*M_3 - (19 + 4*E_2)*M_2*r0 + (9 + 8*E_2)*M*r0_2 + 2*r0_3) + E*L*r0_3*(L_4*M*(-2*M + r0) - 2*L_2*r0_2*(6*M_2 - (5 + E_2)*M*r0 + r0_2) - r0_4*(6*M_2 - (5 + 2*E_2)*M*r0 + r0_2)) + a_5*M*(-2*E_2*r0_2*(-11*M_2 - 9*M*r0 + r0_2) + L_2*(4*M_2 + (2 - 6*E_2)*M*r0 - 7*E_2*r0_2)) + a_2*E*L*r0_2*(3*L_4*M + r0_3*(-2*(11 + 8*E_2)*M_2 + 7*(1 + E_2)*M*r0 + r0_2) + L_2*(-4*M_3 - 2*(12 + E_2)*M_2*r0 + 5*(2 + E_2)*M*r0_2 + 2*r0_3)) + a*M*r0_3*(6*E_2*(2*M - r0)*r0_4 + L_2*r0_2*((4 + 34*E_2)*M - (2 + 21*E_2)*r0) + L_4*(4*(1 + E_2)*M - 2*(r0 + 3*E_2*r0))) + a_3*M*r0*(2*L_4*(M - 3*E_2*r0) + E_2*r0_3*(6*M_2 + 17*M*r0 - 7*r0_2) + 2*L_2*r0*(4*(1 + E_2)*M_2 - (1 + 8*E_2)*r0_2 + M*(r0 + 12*E_2*r0)))))/(r0_7*expr2_2);\n    A11241 = (-32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(4*a_7*E_2*M*(3*M_2 - r0_2) + a_6*E*L*M*(-24*M_2 + 2*(-3 + 7*E_2)*M*r0 + (4 + 9*E_2)*r0_2) + 6*a*M*r0_3*(3*E_2*(2*M - r0)*r0_4 - L_2*r0_2*(-2*(1 + 7*E_2)*M + r0 + 9*E_2*r0) + L_4*(2*(1 + E_2)*M - (1 + 3*E_2)*r0)) + E*L*r0_3*(3*L_4*M*(-2*M + r0) + L_2*r0_2*(-32*M_2 + 2*(13 + 3*E_2)*M*r0 - 5*r0_2) - 3*r0_4*(6*M_2 - (5 + 2*E_2)*M*r0 + r0_2)) + 2*a_5*M*(E_2*r0_2*(36*M_2 + 22*M*r0 - 5*r0_2) + L_2*(6*M_2 + (3 - 11*E_2)*M*r0 - 11*E_2*r0_2)) + a_4*E*L*r0*(L_2*M*(2*M + (13 + 9*E_2)*r0) + r0*(-12*(8 + E_2)*M_3 - 50*M_2*r0 + 3*(9 + 8*E_2)*M*r0_2 + 5*r0_3)) + a_2*E*L*r0_2*(9*L_4*M + r0_2*(-12*M_3 - 2*(29 + 15*E_2)*M_2*r0 + (22 + 21*E_2)*M*r0_2 + 2*r0_3) + L_2*(-12*M_3 - 6*(11 + E_2)*M_2*r0 + (29 + 15*E_2)*M*r0_2 + 5*r0_3)) + 6*a_3*M*r0*(L_4*(M - 3*E_2*r0) + 2*E_2*r0_3*(3*M_2 + 4*M*r0 - 2*r0_2) + L_2*r0*(4*(1 + E_2)*M_2 - (1 + 8*E_2)*r0_2 + M*(r0 + 9*E_2*r0)))))/(r0_4*expr2_2);\n    A11421 = (-8*(a_7*E_2*M*(12*M_2 - 3*M*r0 - 5*r0_2) + a_6*E*L*M*(-24*M_2 + (-3 + 16*E_2)*M*r0 + (5 + 9*E_2)*r0_2) + E*L*r0_3*(3*L_4*M*(-2*M + r0) + 2*L_2*r0_2*(-14*M_2 + (11 + 3*E_2)*M*r0 - 2*r0_2) - 3*r0_4*(6*M_2 - (5 + 2*E_2)*M*r0 + r0_2)) + a_5*M*(2*E_2*r0_2*(39*M_2 + 17*M*r0 - 7*r0_2) + L_2*(12*M_2 + (6 - 26*E_2)*M*r0 - 23*E_2*r0_2)) + a_4*E*L*r0*(L_2*M*(4*M + (14 + 9*E_2)*r0) + r0*(-6*(17 + 2*E_2)*M_3 + (-43 + 12*E_2)*M_2*r0 + 3*(9 + 8*E_2)*M*r0_2 + 4*r0_3)) + a_2*E*L*r0_2*(9*L_4*M + r0_2*(-24*M_3 - 2*(25 + 6*E_2)*M_2*r0 + (23 + 21*E_2)*M*r0_2 + r0_3) + L_2*(-12*M_3 - 6*(10 + E_2)*M_2*r0 + (28 + 15*E_2)*M*r0_2 + 4*r0_3)) + 3*a*M*r0_3*(6*E_2*(2*M - r0)*r0_4 + L_2*r0_2*((4 + 22*E_2)*M - (2 + 15*E_2)*r0) + L_4*(4*(1 + E_2)*M - 2*(r0 + 3*E_2*r0))) + 3*a_3*M*r0*(2*L_4*(M - 3*E_2*r0) + 3*E_2*r0_3*(6*M_2 + 5*M*r0 - 3*r0_2) + 2*L_2*r0*(4*(1 + E_2)*M_2 - (1 + 8*E_2)*r0_2 + M*(r0 + 6*E_2*r0)))))/(r0*expr2_2);\n    A11601 = (2*r0_2*(2*a_5*E_2*M*(-M + r0) + a_4*E*L*M*(4*M - (2 + 3*E_2)*r0) - 2*a_3*M*(E_2*(7*M - 2*r0)*r0_2 + L_2*(M - 3*E_2*r0)) + 2*a*M*r0_2*(3*E_2*r0_2*(-2*M + r0) + L_2*(-2*(1 + E_2)*M + r0 + 3*E_2*r0)) - a_2*E*L*r0*(3*L_2*M + r0*(-2*(9 + E_2)*M_2 + 5*(1 + E_2)*M*r0 + r0_2)) + E*L*r0_2*(L_2*M*(2*M - r0) + r0_2*(6*M_2 - (5 + 2*E_2)*M*r0 + r0_2))))/expr2_2;\n    A12041 = (-192*E*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*expr1_2)/(r0_2*expr2_2);\n    A12061 = (-32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_11*E_2*M*(24*(46 + 7*E_2)*M_3 + (1031 + 180*E_2)*M_2*r0 + 6*(41 + 8*E_2)*M*r0_2 + 6*r0_3) - a_10*E*L*M*(24*(92 + 17*E_2)*M_3 + (2087 + 296*E_2)*M_2*r0 + 6*(83 + 6*E_2)*M*r0_2 + 6*(1 + E_2)*r0_3) + E*L*r0_5*(-6*L_6*M*(102*M_2 - 103*M*r0 + 26*r0_2) + r0_6*(124*M_3 + 2*(-41 + 94*E_2)*M_2*r0 - 4*(2 + 25*E_2)*M*r0_2 + 9*r0_3) + 2*L_4*r0_2*(-734*M_3 + (791 + 102*E_2)*M_2*r0 - 2*(118 + 27*E_2)*M*r0_2 + 12*r0_3) + 2*L_2*r0_4*(-354*M_3 + 47*(9 + 4*E_2)*M_2*r0 - (153 + 100*E_2)*M*r0_2 + 15*r0_3)) + a*M*r0_3*(-3*E_2*r0_8*(116*M_2 + 4*(4 + 3*E_2)*M*r0 - 37*r0_2) + 12*L_8*(50*M_2 - 49*M*r0 + 12*r0_2) + 2*L_4*r0_4*(2*(-18 + 725*E_2)*M_2 + (18 - 1385*E_2 - 84*E_4)*M*r0 + 330*E_2*r0_2) + 12*L_6*(2*M - r0)*r0*(12*M_2 + (28 + 37*E_2)*M*r0 - (17 + 15*E_2)*r0_2) + 2*L_2*r0_5*(-144*M_3 + 614*E_2*M_2*r0 + (96 - 805*E_2 - 90*E_4)*M*r0_2 + 3*(-10 + 83*E_2)*r0_3)) - a_2*E*L*r0_3*(6*L_6*M*(244*M_2 - 193*M*r0 + 33*r0_2) + 2*L_4*r0*(1672*M_4 + (416 - 30*E_2)*M_3*r0 - 3*(478 + 87*E_2)*M_2*r0_2 + 5*(61 + 24*E_2)*M*r0_3 + 36*r0_4) + r0_5*(-1912*M_4 + 24*(7 + 4*E_2)*M_3*r0 + 14*(44 - 29*E_2)*M_2*r0_2 + 3*(-69 + 128*E_2)*M*r0_3 + 36*r0_4) + 2*L_2*r0_3*(764*M_4 + 2*(-95 + 329*E_2)*M_3*r0 - (657 + 748*E_2)*M_2*r0_2 + (153 + 308*E_2)*M*r0_3 + 48*r0_4)) + a_9*M*(2*L_2*(12*(46 + 9*E_2)*M_3 + 2*(264 + 247*E_2 + 30*E_4)*M_2*r0 + 3*(42 + 65*E_2 + 8*E_4)*M*r0_2 + 12*E_2*r0_3) + E_2*r0*(-16*(129 + 62*E_2)*M_4 + 24*(103 - 31*E_2)*M_3*r0 + 3*(1531 + 96*E_2)*M_2*r0_2 + 2*(707 + 106*E_2)*M*r0_3 + 54*r0_4)) - a_8*E*L*M*(2*L_2*(-60*M_3 + 8*(113 + 15*E_2)*M_2*r0 + 435*M*r0_2 + 3*(3 + 2*E_2)*r0_3) + r0*(-96*(43 + 3*E_2)*M_4 + 8*(363 + 125*E_2)*M_3*r0 + 2*(3311 + 734*E_2)*M_2*r0_2 + (1733 + 342*E_2)*M*r0_3 + 6*(-16 + 17*E_2)*r0_4)) - a_3*M*r0_2*(36*L_8*M + E_2*r0_6*(3084*M_3 + 2*(373 + 414*E_2)*M_2*r0 + 4*(-223 + 10*E_2)*M*r0_2 - 243*r0_3) - 12*L_6*(212*M_3 + (-133 + 66*E_2)*M_2*r0 - (19 + 60*E_2)*M*r0_2 + 4*(3 + 4*E_2)*r0_3) + 2*L_2*r0_3*(288*M_4 + (792 - 458*E_2)*M_3*r0 + (-426 - 785*E_2 + 180*E_4)*M_2*r0_2 + 2*(-135 + 414*E_2 + 113*E_4)*M*r0_3 + (132 - 391*E_2)*r0_4) - 2*L_4*r0*(288*M_4 + 8*(12 + 283*E_2)*M_3*r0 - 12*(39 - 118*E_2 + 14*E_4)*M_2*r0_2 - 2*(-135 + 773*E_2 + 84*E_4)*M*r0_3 + 9*(-8 + 37*E_2)*r0_4)) + a_7*M*(6*L_4*(-16*M_3 + 156*M_2*r0 + 6*(13 + E_2)*M*r0_2 + 5*E_2*r0_3) + E_2*r0_3*(-20*(307 + 72*E_2)*M_4 - 4*(667 + 738*E_2)*M_3*r0 + 3*(1901 - 232*E_2)*M_2*r0_2 + 78*(33 + 4*E_2)*M*r0_3 + 111*r0_4) + 2*L_2*r0*(24*(-43 + 106*E_2)*M_4 + 4*(54 + 313*E_2 - 142*E_4)*M_3*r0 + (1002 + 1583*E_2 - 220*E_4)*M_2*r0_2 + 2*(81 + 380*E_2 + E_4)*M*r0_3 + 3*(-24 + 43*E_2)*r0_4)) + a_5*M*r0*(-12*L_6*(10*M_2 + 3*(-5 + 3*E_2)*M*r0 - E_2*r0_2) + E_2*r0_4*(-3588*M_4 - 8*(832 + 279*E_2)*M_3*r0 - 3*(-749 + 544*E_2)*M_2*r0_2 + 6*(391 + 24*E_2)*M*r0_3 + 195*r0_4) + 2*L_4*(1344*M_4 + 96*(-9 + 22*E_2)*M_3*r0 - 2*(30 - 181*E_2 + 84*E_4)*M_2*r0_2 + (342 - 287*E_2 - 84*E_4)*M*r0_3 + 18*(-4 + 11*E_2)*r0_4) + 2*L_2*r0_2*(16*(-81 + 158*E_2)*M_4 - 2*(348 - 775*E_2 + 324*E_4)*M_3*r0 + 4*(267 + 261*E_2 - 139*E_4)*M_2*r0_2 + 2*(105 + 59*E_2 - 79*E_4)*M*r0_3 + (-174 + 259*E_2)*r0_4)) - a_6*E*L*r0*(-6*L_4*M*(40*M_2 + 2*(-22 + 3*E_2)*M*r0 - (3 + E_2)*r0_2) + 2*L_2*M*(3536*M_4 - 4*(121 + 108*E_2)*M_3*r0 + (933 + 250*E_2)*M_2*r0_2 + 2*(523 - 2*E_2)*M*r0_3 + 3*(1 + 40*E_2)*r0_4) + r0_2*((-8732 + 80*E_2)*M_5 + 4*(-1267 + 76*E_2)*M_4*r0 + (6519 + 2456*E_2)*M_3*r0_2 + 2*(1234 + 361*E_2)*M_2*r0_3 + (-181 + 370*E_2)*M*r0_4 + 54*r0_5)) - a_4*E*L*r0_2*(6*L_6*M*(-18*M + r0) + 2*L_4*M*(3248*M_3 - 6*(151 + 34*E_2)*M_2*r0 - (395 + 159*E_2)*M*r0_2 + 3*(50 + 23*E_2)*r0_3) + 2*L_2*r0*(2056*M_5 + 2*(9 + 44*E_2)*M_4*r0 + 3*(-359 + 236*E_2)*M_3*r0_2 + (281 - 348*E_2)*M_2*r0_3 + 2*(75 + 161*E_2)*M*r0_4 + 63*r0_5) + r0_3*(-2536*M_5 - 8*(829 + 174*E_2)*M_4*r0 + 4*(574 + 257*E_2)*M_3*r0_2 + 3*(505 + 66*E_2)*M_2*r0_3 + 6*(-49 + 93*E_2)*M*r0_4 + 99*r0_5))))/(3.*r0_8*expr2_3);\n    A12221 = (-96*E*r0*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/expr2_2;\n    A12241 = (-8*(a_11*E_2*M_2*(384*(9 + E_2)*M_2 + (3239 + 408*E_2)*M*r0 + 6*(125 + 18*E_2)*r0_2) - a_10*E*L*M*(48*(144 + 17*E_2)*M_3 + (6599 + 548*E_2)*M_2*r0 + 6*(261 + 14*E_2)*M*r0_2 + 18*E_2*r0_3) + E*L*r0_5*(-2*L_6*M*(1022*M_2 - 1031*M*r0 + 260*r0_2) + 3*r0_6*(124*M_3 + 2*(-57 + 94*E_2)*M_2*r0 - 4*(-6 + 25*E_2)*M*r0_2 + r0_3) + 6*L_2*r0_4*(-342*M_3 + (375 + 188*E_2)*M_2*r0 - 2*(57 + 50*E_2)*M*r0_2 + 6*r0_3) + 2*L_4*r0_2*(-2246*M_3 + 5*(469 + 58*E_2)*M_2*r0 - (647 + 154*E_2)*M*r0_2 + 18*r0_3)) + a*M*r0_3*(-9*E_2*r0_8*(116*M_2 + 12*E_2*M*r0 - 29*r0_2) + 36*L_8*(50*M_2 - 49*M*r0 + 12*r0_2) + 4*L_6*(2*M - r0)*r0*(72*M_2 + (228 + 397*E_2)*M*r0 - 3*(44 + 45*E_2)*r0_2) + 12*L_4*r0_4*((-82 + 670*E_2)*M_2 + (73 - 617*E_2 - 29*E_4)*M*r0 + (-16 + 141*E_2)*r0_2) - 12*L_2*r0_5*(48*M_3 + 6*(10 - 47*E_2)*M_2*r0 + (-90 + 361*E_2 + 36*E_4)*M*r0_2 + 2*(12 - 55*E_2)*r0_3)) + a_9*M*(2*L_2*(72*(24 + E_2)*M_3 + 2*(840 + 712*E_2 + 75*E_4)*M_2*r0 + 3*(136 + 217*E_2 + 24*E_4)*M*r0_2 + 18*E_2*r0_3) + E_2*r0*(-48*(135 + 56*E_2)*M_4 + 4*(1787 - 576*E_2)*M_3*r0 + (14027 + 336*E_2)*M_2*r0_2 + 6*(729 + 70*E_2)*M*r0_3 + 66*r0_4)) - a_2*E*L*r0_3*(2*L_6*M*(2260*M_2 - 1793*M*r0 + 309*r0_2) + 6*L_2*r0_3*(1104*M_4 + 4*(-229 + 64*E_2)*M_3*r0 - 3*(107 + 170*E_2)*M_2*r0_2 + 2*(59 + 156*E_2)*M*r0_3 + 51*r0_4) + 3*r0_5*(-1440*M_4 + 4*(-112 + 3*E_2)*M_3*r0 + 2*(431 - 207*E_2)*M_2*r0_2 + (-271 + 400*E_2)*M*r0_3 + 54*r0_4) + 2*L_4*r0*(5016*M_4 + 22*(19 + 5*E_2)*M_3*r0 - (3873 + 625*E_2)*M_2*r0_2 + 10*(95 + 34*E_2)*M*r0_3 + 87*r0_4)) + a_7*M*(-12*L_4*(28*M_3 + (-248 + 21*E_2)*M_2*r0 - (127 + 28*E_2 + 3*E_4)*M*r0_2 - 6*E_2*r0_3) + 3*E_2*r0_3*(-12*(505 + 96*E_2)*M_4 - 16*(185 + 156*E_2)*M_3*r0 + (5539 - 768*E_2)*M_2*r0_2 + 6*(459 + 28*E_2)*M*r0_3 + 51*r0_4) + 2*L_2*r0*(72*(-45 + 106*E_2)*M_4 - 8*(-75 - 268*E_2 + 192*E_4)*M_3*r0 + 2*(1635 + 1685*E_2 - 381*E_4)*M_2*r0_2 + (564 + 2791*E_2 + 48*E_4)*M*r0_3 + 3*(-82 + 127*E_2)*r0_4)) - a_3*M*r0_2*(108*L_8*M + 3*E_2*r0_6*(2520*M_3 + 4*(271 + 162*E_2)*M_2*r0 + 4*(-249 + 19*E_2)*M*r0_2 - 181*r0_3) - 4*L_6*(1872*M_3 + 3*(-411 + 230*E_2)*M_2*r0 - (114 + 535*E_2)*M*r0_2 + 3*(31 + 48*E_2)*r0_3) + 2*L_2*r0_3*(576*M_4 - 48*(-63 + 97*E_2)*M_3*r0 + 2*(-615 + 631*E_2 + 567*E_4)*M_2*r0_2 + (-1236 + 1921*E_2 + 528*E_4)*M*r0_3 + (534 - 1255*E_2)*r0_4) - 4*L_4*r0*(288*M_4 + 24*(-1 + 155*E_2)*M_3*r0 + (-714 + 965*E_2 - 228*E_4)*M_2*r0_2 + (627 - 2041*E_2 - 161*E_4)*M*r0_3 + 3*(-62 + 193*E_2)*r0_4)) - a_5*M*r0*(12*L_6*(32*M_2 + 2*(-25 + 9*E_2)*M*r0 - 3*E_2*r0_2) + 3*E_2*r0_4*(3252*M_4 + 4*(1631 + 432*E_2)*M_3*r0 + (-1615 + 1392*E_2)*M_2*r0_2 - 2*(1271 + 12*E_2)*M*r0_3 - 123*r0_4) + 4*L_4*(-1944*M_4 - 144*(-10 + 23*E_2)*M_3*r0 + 2*(6 + 87*E_2 + 110*E_4)*M_2*r0_2 + (-609 + 244*E_2 + 65*E_4)*M*r0_3 + 6*(23 - 53*E_2)*r0_4) - 2*L_2*r0_2*(48*(-84 + 173*E_2)*M_4 - 8*(327 - 559*E_2 + 216*E_4)*M_3*r0 - 6*(-598 + 164*E_2 + 349*E_4)*M_2*r0_2 + (852 + 1565*E_2 - 336*E_4)*M*r0_3 + 2*(-318 + 479*E_2)*r0_4)) - a_8*E*L*(2*L_2*M*(-312*M_3 + 2*(1421 + 129*E_2)*M_2*r0 + 3*(475 + 14*E_2)*M*r0_2 + 9*(1 + 2*E_2)*r0_3) + r0*(-288*(45 + E_2)*M_5 + 4*(2087 + 392*E_2)*M_4*r0 + 4*(5202 + 515*E_2)*M_3*r0_2 + (5761 + 898*E_2)*M_2*r0_3 + 6*(-53 + 59*E_2)*M*r0_4 + 18*r0_5)) - a_4*E*L*r0_2*(18*L_6*M*(-16*M + r0) + 2*L_4*(9696*M_4 - 12*(291 + 35*E_2)*M_3*r0 - (830 + 289*E_2)*M_2*r0_2 + 3*(157 + 65*E_2)*M*r0_3 + 9*r0_4) + 3*r0_3*(-2120*M_5 + 4*(-1739 + 102*E_2)*M_4*r0 + 2*(787 + 94*E_2)*M_3*r0_2 + (2045 + 38*E_2)*M_2*r0_3 + 6*(-62 + 101*E_2)*M*r0_4 + 117*r0_5) + 2*L_2*r0*(5880*M_5 + 4*(-163 + 246*E_2)*M_4*r0 + 2*(-2530 + 93*E_2)*M_3*r0_2 + 14*(163 - 45*E_2)*M_2*r0_3 + 6*(58 + 165*E_2)*M*r0_4 + 189*r0_5)) - a_6*E*L*r0*(6*L_4*M*(-142*M_2 + 166*M*r0 + 3*(2 + E_2)*r0_2) + 2*L_2*(10320*M_5 - 16*(167 + 54*E_2)*M_4*r0 + 12*(207 - 7*E_2)*M_3*r0_2 + (3875 + 162*E_2)*M_2*r0_3 + 3*(1 + 124*E_2)*M*r0_4 + 18*r0_5) + r0_2*(12*(-2187 + 212*E_2)*M_5 + 4*(-4325 + 666*E_2)*M_4*r0 + (19571 + 2392*E_2)*M_3*r0_2 + 2*(4349 + 803*E_2)*M_2*r0_3 + 33*(-21 + 38*E_2)*M*r0_4 + 204*r0_5))))/(3.*r0_5*expr2_3);\n    A12401 = (-12*E*r0_4*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/expr2_2;\n    A12421 = (2*(-3*a_9*E_2*M*((600 + 44*E_2)*M_2 + 6*(45 + 4*E_2)*M*r0 - 5*r0_2) + 3*a_8*E*L*M*(4*(300 + 17*E_2)*M_2 + 2*(281 + 6*E_2)*M*r0 + (-5 + 6*E_2)*r0_2) + a_7*M*(3*L_2*(60*(-10 + E_2)*M_2 - 4*(73 + 52*E_2 + 4*E_4)*M*r0 - 7*E_2*r0_2) + 2*E_2*r0*(12*(141 + 50*E_2)*M_3 + 147*(-17 + 2*E_2)*M_2*r0 - (1871 + 66*E_2)*M*r0_2 + 8*r0_3)) + E*L*r0_4*(6*L_4*M*(386*M_2 - 389*M*r0 + 98*r0_2) + L_2*r0_2*(2352*M_3 - 4*(584 + 141*E_2)*M_2*r0 + 2*(277 + 150*E_2)*M*r0_2 + 13*r0_3) + r0_4*(-372*M_3 + (478 - 564*E_2)*M_2*r0 + 4*(-52 + 75*E_2)*M*r0_2 + 31*r0_3)) + a_5*M*(12*L_4*(16*M_2 + (-64 + 15*E_2)*M*r0 - 3*E_2*r0_2) + 2*E_2*r0_3*(6*(731 + 108*E_2)*M_3 + 2*(-199 + 453*E_2)*M_2*r0 + 3*(-837 + 10*E_2)*M*r0_2 + 40*r0_3) + L_2*r0*(-72*(-47 + 106*E_2)*M_3 + 12*(-187 + 144*E_2 + 60*E_4)*M_2*r0 + 6*(-252 - 151*E_2 + 20*E_4)*M*r0_2 + (552 - 695*E_2)*r0_3)) + a_3*M*r0*(108*L_6*M + 2*E_2*r0_4*(1890*M_3 + 3*(553 + 198*E_2)*M_2*r0 + (-1399 + 114*E_2)*M*r0_2 - 40*r0_3) + L_2*r0_2*(-48*(-87 + 188*E_2)*M_3 + 12*(-53 + 43*E_2 + 60*E_4)*M_2*r0 + 4*(-633 + 844*E_2 + 96*E_4)*M*r0_2 + (948 - 1295*E_2)*r0_3) - 12*L_4*(312*M_3 + 9*(-31 + 30*E_2)*M_2*r0 + (65 - 196*E_2)*M*r0_2 + 2*(-5 + 26*E_2)*r0_3)) - 3*a*M*r0_2*(12*L_6*(50*M_2 - 49*M*r0 + 12*r0_2) + E_2*r0_6*(-348*M_2 + 4*(17 - 9*E_2)*M*r0 + 53*r0_2) + 4*L_4*(2*M - r0)*r0*(12*M_2 + (-7 + 163*E_2)*M*r0 - (1 + 49*E_2)*r0_2) + L_2*r0_3*(-96*M_3 + 32*(-15 + 43*E_2)*M_2*r0 - 2*(-264 + 647*E_2 + 36*E_4)*M*r0_2 + 3*(-44 + 101*E_2)*r0_3)) + a_6*E*L*(3*L_2*M*(-148*M_2 + 4*(119 + 3*E_2)*M*r0 + (1 + 6*E_2)*r0_2) + r0*(144*(-47 + E_2)*M_4 + (7242 - 72*E_2)*M_3*r0 + 2*(2720 + 57*E_2)*M_2*r0_2 + (-421 + 384*E_2)*M*r0_3 + 33*r0_4)) + a_2*E*L*r0_2*(18*L_4*M*(260*M_2 - 209*M*r0 + 37*r0_2) + L_2*r0*(5592*M_4 + 12*(-247 + 73*E_2)*M_3*r0 - 2*(1082 + 591*E_2)*M_2*r0_2 + 21*(31 + 32*E_2)*M*r0_3 + 178*r0_4) + r0_3*(-2160*M_4 + 16*(-223 + 57*E_2)*M_3*r0 - 14*(-227 + 93*E_2)*M_2*r0_2 + (-941 + 948*E_2)*M*r0_3 + 221*r0_4)) + a_4*E*L*r0*(-6*L_4*M*(46*M - 3*r0) + L_2*(10032*M_4 - 24*(233 + 15*E_2)*M_3*r0 - 6*(-284 + 79*E_2)*M_2*r0_2 + 2*(136 + 195*E_2)*M*r0_3 + 33*r0_4) + r0_2*(12*(-1079 + 202*E_2)*M_4 + 2*(-55 + 162*E_2)*M_3*r0 + 2*(3133 - 330*E_2)*M_2*r0_2 + (-887 + 1014*E_2)*M*r0_3 + 223*r0_4))))/(3.*r0_2*expr2_3);\n    A12601 = (r0*(-3*a_7*E_2*M*(4*(26 + E_2)*M - 3*r0) + 3*a_6*E*L*M*(208*M + (-3 + 2*E_2)*r0) + a*M*r0*(-4*L_2*(2*M - r0)*r0_2*((-90 + 203*E_2)*M + 42*r0 - 57*E_2*r0) + 3*E_2*r0_4*(116*M_2 + 4*(-13 + 3*E_2)*M*r0 - 3*r0_2) - 12*L_4*(50*M_2 - 49*M*r0 + 12*r0_2)) + a_5*M*(12*L_2*((-26 + 6*E_2)*M - E_2*r0) + E_2*r0*(4*(147 + 44*E_2)*M_2 + 4*(-268 + 7*E_2)*M*r0 + 25*r0_2)) + a_3*M*(36*L_4*M + E_2*r0_3*(16*(86 + 9*E_2)*M_2 + 4*(-219 + 19*E_2)*M*r0 + 79*r0_2) - 4*L_2*r0*(3*(-49 + 106*E_2)*M_2 + (168 - 233*E_2)*M*r0 + 3*(-17 + 20*E_2)*r0_2)) + a_4*E*L*(6*L_2*M*(-16*M + r0) + r0*(24*(-49 + 3*E_2)*M_3 + 2*(872 - 107*E_2)*M_2*r0 + 2*(-95 + 69*E_2)*M*r0_2 + 15*r0_3)) + E*L*r0_3*(2*L_2*M*(442*M_2 - 445*M*r0 + 112*r0_2) + r0_2*(-124*M_3 + 2*(109 - 94*E_2)*M_2*r0 + 4*(-32 + 25*E_2)*M*r0_2 + 25*r0_3)) + a_2*E*L*r0*(2*L_2*M*(812*M_2 - 667*M*r0 + 123*r0_2) + r0_2*(4*(-524 + 149*E_2)*M_3 + 2*(673 - 227*E_2)*M_2*r0 + (-325 + 232*E_2)*M*r0_2 + 76*r0_3))))/(6.*expr2_3);\n    A13041 = (-32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(4*a_7*E_2*M*(3*M_2 - r0_2) + a_6*E*L*M*(-24*M_2 + 2*(-3 + 7*E_2)*M*r0 + (4 + 9*E_2)*r0_2) + E*L*r0_3*(3*L_4*M*(-2*M + r0) + L_2*r0_2*(-30*M_2 + (23 + 6*E_2)*M*r0 - 4*r0_2) - 2*r0_4*(8*M_2 - 3*(2 + E_2)*M*r0 + r0_2)) + a_5*M*(E_2*r0_2*(70*M_2 + 45*M*r0 - 9*r0_2) + 2*L_2*(6*M_2 + (3 - 11*E_2)*M*r0 - 11*E_2*r0_2)) + a_3*M*r0*(6*L_4*(M - 3*E_2*r0) + 10*E_2*r0_3*(3*M_2 + 5*M*r0 - 2*r0_2) + L_2*r0*(24*(1 + E_2)*M_2 + (6 + 53*E_2)*M*r0 - (6 + 47*E_2)*r0_2)) + a_4*E*L*r0*(L_2*M*(2*M + (13 + 9*E_2)*r0) + r0*(-2*(47 + 6*E_2)*M_3 - 51*M_2*r0 + 2*(13 + 12*E_2)*M*r0_2 + 5*r0_3)) + a_2*E*L*r0_2*(9*L_4*M + r0_2*(-8*M_3 - (61 + 30*E_2)*M_2*r0 + (20 + 21*E_2)*M*r0_2 + 3*r0_3) + L_2*(-12*M_3 - (65 + 6*E_2)*M_2*r0 + (28 + 15*E_2)*M*r0_2 + 5*r0_3)) + 3*a*M*r0_3*(E_2*(11*M - 5*r0)*r0_4 + L_2*r0_2*((4 + 27*E_2)*M - (2 + 17*E_2)*r0) + L_4*(4*(1 + E_2)*M - 2*(r0 + 3*E_2*r0)))))/(r0_4*expr2_3);\n    A13221 = (-16*(a_7*E_2*M*(12*M_2 - 3*M*r0 - 5*r0_2) + a_6*E*L*M*(-24*M_2 + (-3 + 16*E_2)*M*r0 + (5 + 9*E_2)*r0_2) + E*L*r0_3*(3*L_4*M*(-2*M + r0) + L_2*r0_2*(-26*M_2 + (19 + 6*E_2)*M*r0 - 3*r0_2) - 2*r0_4*(8*M_2 - 3*(2 + E_2)*M*r0 + r0_2)) + a_5*M*(E_2*r0_2*(76*M_2 + 35*M*r0 - 13*r0_2) + L_2*(12*M_2 + (6 - 26*E_2)*M*r0 - 23*E_2*r0_2)) + a_3*M*r0*(6*L_4*(M - 3*E_2*r0) + E_2*r0_3*(48*M_2 + 47*M*r0 - 23*r0_2) + L_2*r0*(24*(1 + E_2)*M_2 + (6 + 35*E_2)*M*r0 - (6 + 47*E_2)*r0_2)) + a_4*E*L*r0*(L_2*M*(4*M + (14 + 9*E_2)*r0) + 2*r0*(-2*(25 + 3*E_2)*M_3 + 2*(-11 + 3*E_2)*M_2*r0 + (13 + 12*E_2)*M*r0_2 + 2*r0_3)) + a_2*E*L*r0_2*(9*L_4*M + r0_2*(-20*M_3 - (53 + 12*E_2)*M_2*r0 + 21*(1 + E_2)*M*r0_2 + 2*r0_3) + L_2*(-12*M_3 - (59 + 6*E_2)*M_2*r0 + 3*(9 + 5*E_2)*M*r0_2 + 4*r0_3)) + 3*a*M*r0_3*(E_2*(11*M - 5*r0)*r0_4 + L_4*(4*(1 + E_2)*M - 2*(r0 + 3*E_2*r0)) + L_2*r0_2*((4 + 21*E_2)*M - 2*(r0 + 7*E_2*r0)))))/(r0*expr2_3);\n    A13401 = (-2*r0_2*(6*a_5*E_2*M*(M - r0) + 3*a_4*E*L*M*(-4*M + (2 + 3*E_2)*r0) + a_3*M*(E_2*(41*M - 11*r0)*r0_2 + 6*L_2*(M - 3*E_2*r0)) + E*L*r0_2*(3*L_2*M*(-2*M + r0) - 2*r0_2*(8*M_2 - 3*(2 + E_2)*M*r0 + r0_2)) + a_2*E*L*r0*(9*L_2*M + r0*(-((53 + 6*E_2)*M_2) + (14 + 15*E_2)*M*r0 + 3*r0_2)) + 3*a*M*r0_2*(E_2*(11*M - 5*r0)*r0_2 + L_2*(4*(1 + E_2)*M - 2*(r0 + 3*E_2*r0)))))/expr2_3;\n    A14021 = (-48*E*r0*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/expr2_3;\n    A14041 = (-8*(a_11*E_2*M*(24*(57 + 22*E_2)*M_3 + 2*(641 + 276*E_2)*M_2*r0 + 48*(7 + 3*E_2)*M*r0_2 + 21*r0_3) - a_10*E*L*M*(48*(57 + 26*E_2)*M_3 + 2*(1295 + 554*E_2)*M_2*r0 + 6*(108 + 49*E_2)*M*r0_2 + 3*(7 + 12*E_2)*r0_3) + a*M*r0_3*(36*L_8*(26*M_2 - 25*M*r0 + 6*r0_2) + 9*E_2*r0_8*(-25*M_2 + 4*(-10 + 3*E_2)*M*r0 + 25*r0_2) + 3*L_4*r0_4*((88 + 1241*E_2)*M_2 - 4*(29 + 309*E_2 + 10*E_4)*M*r0 + 4*(9 + 74*E_2)*r0_2) - 3*L_2*r0_5*(96*M_3 + (8 - 566*E_2)*M_2*r0 + 4*(-15 + 193*E_2)*M*r0_2 + (16 - 233*E_2)*r0_3) + 4*L_6*r0*(72*M_3 + 2*(153 + 164*E_2)*M_2*r0 - (357 + 320*E_2)*M*r0_2 + 3*(31 + 25*E_2)*r0_3)) - a_2*E*L*r0_3*(8*L_6*M*(293*M_2 - 244*M*r0 + 42*r0_2) + 3*r0_5*(-632*M_4 + (103 - 132*E_2)*M_3*r0 + 2*(29 + 40*E_2)*M_2*r0_2 + (-63 + 166*E_2)*M*r0_3 + 32*r0_4) + 3*L_2*r0_3*(848*M_4 + 2*(113 + 110*E_2)*M_3*r0 - 4*(275 + 73*E_2)*M_2*r0_2 + (169 + 294*E_2)*M*r0_3 + 77*r0_4) + L_4*r0*(4392*M_4 + (2545 - 136*E_2)*M_3*r0 - 2*(2613 + 254*E_2)*M_2*r0_2 + 2*(493 + 182*E_2)*M*r0_3 + 132*r0_4)) + a_9*M*(2*L_2*(36*(19 + 8*E_2)*M_3 + 2*(327 + 371*E_2 + 99*E_4)*M_2*r0 + 12*(13 + 29*E_2 + 8*E_4)*M*r0_2 + 57*E_2*r0_3) + E_2*r0*(-48*(49 + 34*E_2)*M_4 + 3052*M_3*r0 + 8*(674 + 249*E_2)*M_2*r0_2 + 12*(150 + 67*E_2)*M*r0_3 + 153*r0_4)) + a_7*M*(3*L_4*(-112*M_3 + 4*(77 + 3*E_2)*M_2*r0 + 4*(43 + 15*E_2 + 4*E_4)*M*r0_2 + 55*E_2*r0_3) + 3*E_2*r0_3*(-4*(553 + 216*E_2)*M_4 - 24*(37 + 50*E_2)*M_3*r0 + (1975 + 552*E_2)*M_2*r0_2 + 24*(40 + 23*E_2)*M*r0_3 + 114*r0_4) + 2*L_2*r0*(24*(-49 + 150*E_2)*M_4 + 4*(78 + 661*E_2 - 216*E_4)*M_3*r0 + (1164 + 2081*E_2 + 150*E_4)*M_2*r0_2 + (138 + 1079*E_2 + 360*E_4)*M*r0_3 + 6*(-17 + 59*E_2)*r0_4)) + a_3*M*r0_2*(-108*L_8*M + 3*E_2*r0_6*(-1032*M_3 - (437 + 288*E_2)*M_2*r0 + 8*(18 + 29*E_2)*M*r0_2 + 183*r0_3) + 4*L_6*(972*M_3 + 6*(-89 + 59*E_2)*M_2*r0 - (165 + 299*E_2)*M*r0_2 + 3*(19 + 31*E_2)*r0_3) + 2*L_2*r0_3*(-288*M_4 + 48*(-19 + 20*E_2)*M_3*r0 + (408 + 1115*E_2 - 414*E_4)*M_2*r0_2 + 2*(159 - 763*E_2 + 84*E_4)*M*r0_3 + 10*(-15 + 68*E_2)*r0_4) + L_4*r0*(576*M_4 + 48*(29 + 117*E_2)*M_3*r0 + (-1560 + 4297*E_2 - 528*E_4)*M_2*r0_2 - 2*(-126 + 2329*E_2 + 80*E_4)*M*r0_3 + 12*(-7 + 106*E_2)*r0_4)) + a_5*M*r0*(-12*L_6*(32*M_2 + (-8 + 15*E_2)*M*r0 - 6*E_2*r0_2) + 3*E_2*r0_4*(-1404*M_4 - 36*(67 + 32*E_2)*M_3*r0 - 9*(-59 + 24*E_2)*M_2*r0_2 + 16*(46 + 33*E_2)*M*r0_3 + 178*r0_4) + L_4*(4032*M_4 + 24*(-71 + 270*E_2)*M_3*r0 - 4*(177 - 327*E_2 + 116*E_4)*M_2*r0_2 + 2*(258 - 487*E_2 + 4*E_4)*M*r0_3 + 3*(-64 + 309*E_2)*r0_4) + L_2*r0_2*(96*(-31 + 60*E_2)*M_4 - 8*(213 - 889*E_2 + 288*E_4)*M_3*r0 - 6*(-414 - 455*E_2 + 170*E_4)*M_2*r0_2 + 2*(210 - 155*E_2 + 432*E_4)*M*r0_3 + (-456 + 1255*E_2)*r0_4)) - a_8*E*L*(2*L_2*M*(-240*M_3 + 2*(463 + 201*E_2)*M_2*r0 + 3*(177 + 55*E_2)*M*r0_2 + 3*(13 + 12*E_2)*r0_3) + r0*(-96*(49 + 9*E_2)*M_5 + 4*(919 + 874*E_2)*M_4*r0 + 2*(3903 + 2402*E_2)*M_3*r0_2 + 2*(1096 + 775*E_2)*M_2*r0_3 + 6*(3 + 41*E_2)*M*r0_4 + 15*r0_5)) - a_6*E*L*r0*(3*L_4*M*(-252*M_2 + 2*(23 + 6*E_2)*M*r0 + (31 + 12*E_2)*r0_2) + 2*L_2*(5232*M_5 + (44 - 648*E_2)*M_4*r0 + 3*(107 + 362*E_2)*M_3*r0_2 + (991 + 624*E_2)*M_2*r0_3 + 3*(57 + 83*E_2)*M*r0_4 + 15*r0_5) + r0_2*(-108*(89 + 12*E_2)*M_5 + 20*(-293 + 42*E_2)*M_4*r0 + (6187 + 6632*E_2)*M_3*r0_2 + 2*(1246 + 1453*E_2)*M_2*r0_3 + 6*(24 + 107*E_2)*M*r0_4 + 144*r0_5)) - a_4*E*L*r0_2*(12*L_6*M*(-23*M + 3*r0) + L_4*(9936*M_4 - 12*(225 + 38*E_2)*M_3*r0 - 2*(1081 + 68*E_2)*M_2*r0_2 + 12*(55 + 21*E_2)*M*r0_3 + 15*r0_4) + 3*r0_3*(-952*M_5 - 76*(29 + 12*E_2)*M_4*r0 + 2*(277 + 418*E_2)*M_3*r0_2 + (233 + 702*E_2)*M_2*r0_3 + 2*(-13 + 135*E_2)*M*r0_4 + 78*r0_5) + L_2*r0*(5040*M_5 - 8*(-508 + 99*E_2)*M_4*r0 + 4*(-1028 + 489*E_2)*M_3*r0_2 + 2*(-788 + 405*E_2)*M_2*r0_3 + 3*(289 + 348*E_2)*M*r0_4 + 276*r0_5)) + E*L*r0_5*(-4*L_6*M*(230*M_2 - 233*M*r0 + 59*r0_2) + 3*L_2*r0_4*(-364*M_3 + 2*(203 + 80*E_2)*M_2*r0 - 2*(61 + 44*E_2)*M*r0_2 + 5*r0_3) + L_4*r0_2*(-2114*M_3 + (2213 + 272*E_2)*M_2*r0 - 2*(301 + 74*E_2)*M*r0_2 + 12*r0_3) + 3*r0_6*(18*M_3 - 4*(3 + 10*E_2)*M*r0_2 + 3*r0_3 + 3*M_2*(r0 + 24*E_2*r0)))))/(3.*r0_5*expr2_4);\n    A14201 = (-12*E*r0_4*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/expr2_3;\n    A14221 = (-4*(a_9*E_2*M*((234 + 80*E_2)*M_2 + 6*(19 + 7*E_2)*M*r0 + 7*r0_2) - a_8*E*L*M*(4*(117 + 44*E_2)*M_2 + (226 + 87*E_2)*M*r0 + (7 + 15*E_2)*r0_2) - E*L*r0_4*(L_4*M*(340*M_2 - 344*M*r0 + 87*r0_2) + r0_4*(-18*M_3 + (17 - 72*E_2)*M_2*r0 + 8*(-1 + 5*E_2)*M*r0_2 + 2*r0_3) + L_2*r0_2*(374*M_3 - (379 + 80*E_2)*M_2*r0 + 4*(23 + 11*E_2)*M*r0_2 + 2*r0_3)) + a_7*M*(L_2*(6*(39 + 8*E_2)*M_2 + 4*(28 + 31*E_2 + 7*E_4)*M*r0 + 37*E_2*r0_2) + E_2*r0*(-4*(101 + 62*E_2)*M_3 + (625 + 136*E_2)*M_2*r0 + 2*(231 + 95*E_2)*M*r0_2 + 39*r0_3)) + a_5*M*(L_4*(-64*M_2 + 4*(19 - 6*E_2)*M*r0 + 30*E_2*r0_2) + E_2*r0_3*(-8*(124 + 45*E_2)*M_3 + (121 - 128*E_2)*M_2*r0 + 10*(48 + 29*E_2)*M*r0_2 + 57*r0_3) + L_2*r0*(4*(-101 + 300*E_2)*M_3 + 2*(145 + 56*E_2 - 68*E_4)*M_2*r0 + (160 + 55*E_2 + 72*E_4)*M*r0_2 + 2*(-38 + 93*E_2)*r0_3)) + a_3*M*r0*(-36*L_6*M + E_2*r0_4*(-516*M_3 - 9*(41 + 24*E_2)*M_2*r0 + 2*(81 + 89*E_2)*M*r0_2 + 85*r0_3) + 4*L_4*(162*M_3 + (-115 + 132*E_2)*M_2*r0 + (3 - 89*E_2)*M*r0_2 + (-1 + 31*E_2)*r0_3) + L_2*r0_2*(16*(-31 + 69*E_2)*M_3 + (80 + 393*E_2 - 168*E_4)*M_2*r0 + 4*(69 - 165*E_2 + 11*E_4)*M*r0_2 + 2*(-55 + 123*E_2)*r0_3)) + a*M*r0_2*(12*L_6*(26*M_2 - 25*M*r0 + 6*r0_2) + 3*E_2*r0_6*(-25*M_2 + 6*(-5 + 2*E_2)*M*r0 + 20*r0_2) + 2*L_4*r0*(24*M_3 + 8*(5 + 32*E_2)*M_2*r0 - 2*(31 + 113*E_2)*M*r0_2 + (18 + 47*E_2)*r0_3) + L_2*r0_3*(-48*M_3 + (-128 + 595*E_2)*M_2*r0 + 3*(48 - 205*E_2)*M*r0_2 + (-34 + 151*E_2)*r0_3)) - a_6*E*L*(L_2*M*(-112*M_2 + (155 + 48*E_2)*M*r0 + (22 + 15*E_2)*r0_2) + r0*(-8*(101 + 12*E_2)*M_4 + 5*(183 + 98*E_2)*M_3*r0 + (643 + 322*E_2)*M_2*r0_2 + 2*(-6 + 43*E_2)*M*r0_3 + 8*r0_4)) - a_2*E*L*r0_2*(2*L_4*M*(400*M_2 - 332*M*r0 + 57*r0_2) + L_2*r0*(804*M_4 + (45 + 4*E_2)*M_3*r0 - 4*(181 + 16*E_2)*M_2*r0_2 + (131 + 111*E_2)*M*r0_3 + 39*r0_4) + r0_3*(-316*M_4 - (241 + 18*E_2)*M_3*r0 + (177 + 40*E_2)*M_2*r0_2 + 8*(-12 + 17*E_2)*M*r0_3 + 42*r0_4)) - a_4*E*L*r0*(5*L_4*M*(-16*M + 3*r0) + L_2*(1696*M_4 - 6*(117 + 16*E_2)*M_3*r0 + 5*(-13 + 16*E_2)*M_2*r0_2 + 2*(51 + 41*E_2)*M*r0_3 + 8*r0_4) + r0_2*(-12*(124 + E_2)*M_4 + 12*(-2 + 25*E_2)*M_3*r0 + (505 + 323*E_2)*M_2*r0_2 + (-47 + 167*E_2)*M*r0_3 + 48*r0_4))))/(r0_2*expr2_4);\n    A14401 = (r0*(-9*a_7*E_2*M*(4*(10 + 3*E_2)*M + 3*r0) + 9*a_6*E*L*M*(80*M + 24*E_2*M + 3*r0 + 6*E_2*r0) - a_5*M*(36*L_2*(10*M + 3*E_2*r0) + E_2*r0*(-48*(13 + 7*E_2)*M_2 + 12*(88 + 31*E_2)*M*r0 + 91*r0_2)) + a_3*M*(108*L_4*M + E_2*r0_3*(3*(409 + 144*E_2)*M_2 - 2*(275 + 186*E_2)*M*r0 - 22*r0_2) - 12*L_2*r0*(2*(-26 + 75*E_2)*M_2 + (61 - 88*E_2)*M*r0 + 3*(-7 + 11*E_2)*r0_2)) - 3*a*M*r0*(12*L_4*(26*M_2 - 25*M*r0 + 6*r0_2) + E_2*r0_4*(-75*M_2 + 2*(-25 + 18*E_2)*M*r0 + 40*r0_2) + 4*L_2*r0_2*(2*(-31 + 78*E_2)*M_2 + (57 - 128*E_2)*M*r0 + (-13 + 24*E_2)*r0_2)) + E*L*r0_3*(6*L_2*M*(192*M_2 - 194*M*r0 + 49*r0_2) + r0_2*(-54*M_3 + (131 - 216*E_2)*M_2*r0 + 8*(-13 + 15*E_2)*M*r0_2 + 26*r0_3)) + a_4*E*L*(-54*L_2*M*(4*M - r0) + r0*(-24*(52 + 3*E_2)*M_3 + 12*(149 + 40*E_2)*M_2*r0 + 2*(-67 + 126*E_2)*M*r0_2 + 27*r0_3)) + a_2*E*L*r0*(24*L_2*M*(103*M_2 - 86*M*r0 + 15*r0_2) + r0_2*(9*(-219 + 32*E_2)*M_3 + 1006*M_2*r0 + 6*(-58 + 53*E_2)*M*r0_2 + 134*r0_3))))/(6.*expr2_4);\n    A15021 = (-8*(a_7*E_2*M*(12*M_2 - 3*M*r0 - 5*r0_2) + a_6*E*L*M*(-24*M_2 + (-3 + 16*E_2)*M*r0 + (5 + 9*E_2)*r0_2) + E*L*r0_3*(3*L_4*M*(-2*M + r0) - r0_4*(14*M_2 - 3*(3 + 2*E_2)*M*r0 + r0_2) - 2*L_2*r0_2*(12*M_2 - (8 + 3*E_2)*M*r0 + r0_2)) + a_5*M*(2*E_2*r0_2*(37*M_2 + 18*M*r0 - 6*r0_2) + L_2*(12*M_2 + (6 - 26*E_2)*M*r0 - 23*E_2*r0_2)) + a_3*M*r0*(6*L_4*(M - 3*E_2*r0) + E_2*r0_3*(42*M_2 + 49*M*r0 - 19*r0_2) + 2*L_2*r0*(12*(1 + E_2)*M_2 + (3 + 17*E_2)*M*r0 - (3 + 23*E_2)*r0_2)) + a_2*E*L*r0_2*(9*L_4*M + r0_2*(-16*M_3 - 4*(14 + 3*E_2)*M_2*r0 + (19 + 21*E_2)*M*r0_2 + 3*r0_3) + L_2*(-12*M_3 - 2*(29 + 3*E_2)*M_2*r0 + (26 + 15*E_2)*M*r0_2 + 4*r0_3)) + a_4*E*L*r0*(L_2*M*(4*M + (14 + 9*E_2)*r0) + r0*(-2*(49 + 6*E_2)*M_3 + 3*(-15 + 4*E_2)*M_2*r0 + (25 + 24*E_2)*M*r0_2 + 4*r0_3)) + 3*a*M*r0_3*(2*E_2*(5*M - 2*r0)*r0_4 + L_2*r0_2*(4*(1 + 5*E_2)*M - (2 + 13*E_2)*r0) + L_4*(4*(1 + E_2)*M - 2*(r0 + 3*E_2*r0)))))/(r0*expr2_4);\n    A15201 = (-2*r0_2*(6*a_5*E_2*M*(M - r0) + 3*a_4*E*L*M*(-4*M + (2 + 3*E_2)*r0) + 2*a_3*M*(5*E_2*(4*M - r0)*r0_2 + 3*L_2*(M - 3*E_2*r0)) + 6*a*M*r0_2*(E_2*(5*M - 2*r0)*r0_2 + L_2*(2*(1 + E_2)*M - (1 + 3*E_2)*r0)) + E*L*r0_2*(3*L_2*M*(-2*M + r0) - r0_2*(14*M_2 - 3*(3 + 2*E_2)*M*r0 + r0_2)) + a_2*E*L*r0*(9*L_2*M + r0*(-2*(26 + 3*E_2)*M_2 + (13 + 15*E_2)*M*r0 + 3*r0_2))))/expr2_4;\n    A16001 = (-4*E*r0_4*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/expr2_4;\n    A16021 = (-2*(3*a_9*E_2*M*(4*(28 + 23*E_2)*M_2 + 2*(31 + 24*E_2)*M*r0 + 11*r0_2) - 3*a_8*E*L*M*(4*(56 + 53*E_2)*M_2 + 2*(57 + 56*E_2)*M*r0 + (11 + 18*E_2)*r0_2) - E*L*r0_4*(2*L_4*M*(290*M_2 - 293*M*r0 + 74*r0_2) + M*r0_4*(26*M_2 - (53 + 92*E_2)*M*r0 + 4*(5 + 13*E_2)*r0_2) + L_2*r0_2*(654*M_3 - (639 + 124*E_2)*M_2*r0 + 4*(33 + 17*E_2)*M*r0_2 + 12*r0_3)) + a_7*M*(3*L_2*(28*(4 + 3*E_2)*M_2 + 4*(13 + 24*E_2 + 8*E_4)*M*r0 + 47*E_2*r0_2) + 2*E_2*r0*(-4*(63 + 74*E_2)*M_3 + 10*(42 + 37*E_2)*M_2*r0 + (309 + 358*E_2)*M*r0_2 + 81*r0_3)) + a*M*r0_2*(36*L_6*(18*M_2 - 17*M*r0 + 4*r0_2) + 3*E_2*r0_6*(-M_2 + 2*(-61 + 30*E_2)*M*r0 + 56*r0_2) + 4*L_4*r0*(24*M_3 + 2*(51 + 91*E_2)*M_2*r0 - (123 + 181*E_2)*M*r0_2 + 3*(11 + 13*E_2)*r0_3) + L_2*r0_3*(-96*M_3 + (-48 + 829*E_2)*M_2*r0 + 2*(36 - 451*E_2 + 36*E_4)*M*r0_2 + 12*(-1 + 17*E_2)*r0_3)) + a_3*M*r0*(-108*L_6*M + 2*E_2*r0_4*(-399*M_3 - 6*(40 + 39*E_2)*M_2*r0 + 2*(-48 + 197*E_2)*M*r0_2 + 153*r0_3) + 4*L_4*(336*M_3 + 3*(-61 + 86*E_2)*M_2*r0 - (51 + 154*E_2)*M*r0_2 + 6*(1 + 11*E_2)*r0_3) + L_2*r0_2*(16*(-39 + 86*E_2)*M_3 + (132 + 1643*E_2 - 432*E_4)*M_2*r0 + 2*(138 - 661*E_2 + 152*E_4)*M*r0_2 + 6*(-22 + 83*E_2)*r0_3)) + a_5*M*(-12*L_4*(16*M_2 + (-4 + 3*E_2)*M*r0 - 9*E_2*r0_2) + E_2*r0_3*(-6*(187 + 168*E_2)*M_3 + (195 + 92*E_2)*M_2*r0 + 118*(3 + 10*E_2)*M*r0_2 + 267*r0_3) + L_2*r0*(24*(-21 + 94*E_2)*M_3 + 4*(105 + 260*E_2 - 76*E_4)*M_2*r0 + 8*(18 - 9*E_2 + 41*E_4)*M*r0_2 + 3*(-40 + 163*E_2)*r0_3)) - a_6*E*L*(3*L_2*M*(-100*M_2 + 12*(4 + 5*E_2)*M*r0 + (29 + 18*E_2)*r0_2) + r0*(-144*(7 + 3*E_2)*M_4 + 4*(315 + 499*E_2)*M_3*r0 + 2*(392 + 625*E_2)*M_2*r0_2 + 3*(31 + 68*E_2)*M*r0_3 + 21*r0_4)) - a_2*E*L*r0_2*(2*L_4*M*(820*M_2 - 695*M*r0 + 111*r0_2) + r0_3*(-524*M_4 + (83 - 476*E_2)*M_3*r0 + (-217 + 722*E_2)*M_2*r0_2 + 40*(-2 + 5*E_2)*M*r0_3 + 84*r0_4) + L_2*r0*(1336*M_4 + (1075 - 284*E_2)*M_3*r0 + 2*(-1014 + 83*E_2)*M_2*r0_2 + 2*(141 + 100*E_2)*M*r0_3 + 84*r0_4)) - a_4*E*L*r0*(-6*L_4*M*(38*M - 9*r0) + L_2*(3440*M_4 - 12*(79 + 22*E_2)*M_3*r0 + 10*(-81 + 49*E_2)*M_2*r0_2 + 6*(51 + 31*E_2)*M*r0_3 + 21*r0_4) + r0_2*(-2*(873 + 436*E_2)*M_4 + (-49 + 1068*E_2)*M_3*r0 + 4*(17 + 396*E_2)*M_2*r0_2 + 2*(43 + 149*E_2)*M*r0_3 + 105*r0_4))))/(3.*r0_2*expr2_5);\n    A16201 = (r0*(-3*a_7*E_2*M*((56 + 44*E_2)*M + 15*r0) + 3*a_6*E*L*M*(16*(7 + 6*E_2)*M + (15 + 22*E_2)*r0) - a_5*M*(12*L_2*(2*(7 + 3*E_2)*M + 11*E_2*r0) + E_2*r0*(-4*(63 + 68*E_2)*M_2 + 4*(88 + 131*E_2)*M*r0 + 141*r0_2)) + a_3*M*(108*L_4*M + E_2*r0_3*((317 + 432*E_2)*M_2 + (74 - 572*E_2)*M*r0 - 102*r0_2) - 4*L_2*r0*(3*(-21 + 94*E_2)*M_2 + (78 - 113*E_2)*M*r0 + 33*(-1 + 2*E_2)*r0_2)) + a*M*r0*(3*E_2*r0_4*(M_2 + (98 - 60*E_2)*M*r0 - 44*r0_2) - 36*L_4*(18*M_2 - 17*M*r0 + 4*r0_2) - 4*L_2*r0_2*((-72 + 214*E_2)*M_2 + 5*(12 - 37*E_2)*M*r0 + 3*(-4 + 11*E_2)*r0_2)) + E*L*r0_3*(2*L_2*M*(322*M_2 - 325*M*r0 + 82*r0_2) + r0_2*(26*M_3 - (5 + 92*E_2)*M_2*r0 + 4*(-7 + 13*E_2)*M*r0_2 + 12*r0_3)) + a_4*E*L*(6*L_2*M*(-32*M + 11*r0) + r0*(-168*(3 + E_2)*M_3 + 2*(332 + 433*E_2)*M_2*r0 + 6*(1 + 31*E_2)*M*r0_2 + 21*r0_3)) + a_2*E*L*r0*(2*L_2*M*(836*M_2 - 703*M*r0 + 111*r0_2) + r0_2*(-55*(11 + 4*E_2)*M_3 + 2*(42 + 241*E_2)*M_2*r0 + 4*(-37 + 43*E_2)*M*r0_2 + 96*r0_3))))/(6.*expr2_5);\n    A17001 = (-2*r0_2*(2*a_5*E_2*M*(M - r0) + a_4*E*L*M*(-4*M + (2 + 3*E_2)*r0) + a_3*M*(E_2*(13*M - 3*r0)*r0_2 + 2*L_2*(M - 3*E_2*r0)) + E*L*M*r0_2*(L_2*(-2*M + r0) + 2*r0_2*(-2*M + r0 + E_2*r0)) + a_2*E*L*r0*(3*L_2*M + r0*(-((17 + 2*E_2)*M_2) + (4 + 5*E_2)*M*r0 + r0_2)) + a*M*r0_2*(3*E_2*(3*M - r0)*r0_2 + L_2*(4*(1 + E_2)*M - 2*(r0 + 3*E_2*r0)))))/expr2_5;\n    A18001 = (r0*(-3*a_7*E_2*M*((4 + 8*E_2)*M + 3*r0) + 3*a_6*E*L*M*(2*(4 + 9*E_2)*M + (3 + 4*E_2)*r0) + E*L*M*(2*M - r0)*r0_3*(16*L_2*(2*M - r0) + r0_2*(5*M - 2*(2 + E_2)*r0)) - a*M*r0*(6*L_4*(14*M_2 - 13*M*r0 + 3*r0_2) + 3*E_2*r0_4*(5*M_2 + 4*(-5 + 3*E_2)*M*r0 + 8*r0_2) + 2*L_2*r0_2*((-6 + 28*E_2)*M_2 + (3 - 26*E_2)*M*r0 + 3*E_2*r0_2)) + a_3*M*(18*L_4*M + E_2*r0_3*((-17 + 72*E_2)*M_2 + 2*(29 - 56*E_2)*M*r0 - 24*r0_2) - 2*L_2*r0*((-6 + 66*E_2)*M_2 + (9 - 11*E_2)*M*r0 + 3*(-2 + 5*E_2)*r0_2)) + a_4*E*L*(-6*L_2*M*(5*M - 2*r0) + r0*(-12*(2 + 3*E_2)*M_3 + 2*(10 + 89*E_2)*M_2*r0 + 12*(1 + 2*E_2)*M*r0_2 + 3*r0_3)) + a_2*E*L*r0*(2*L_2*M*(106*M_2 - 89*M*r0 + 12*r0_2) + r0_2*((5 - 80*E_2)*M_3 + 2*(-27 + 62*E_2)*M_2*r0 + 2*(-4 + 7*E_2)*M*r0_2 + 12*r0_3)) - a_5*M*(6*L_2*((2 + 3*E_2)*M + 4*E_2*r0) + E_2*r0*(-4*(3 + 10*E_2)*M_2 + 27*r0_2 + 2*M*(r0 + 50*E_2*r0)))))/(3.*expr2_6);\n  }\n\n  /* A_{t,theta} */\n  {\n    A20161 = (128*E*expr1_3*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/(r0_6*(a_2 + r0*(-2*M + r0)));\n    A20341 = (96*E*expr1_2*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/(r0_3*(a_2 + r0*(-2*M + r0)));\n    A20521 = (24*E*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/(a_2 + r0*(-2*M + r0));\n    A20701 = 2*E*r0_3*(-(E*M*(a_3 - 3*a*r0_2)) + L*(a_2*M + 2*r0_2*(-M + r0)) - (L*M*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(a_2 + r0*(-2*M + r0)));\n    A21161 = (64*expr1_2*(a_8*E*L*M*((8 - 24*E_2)*M_2 - 2*(1 + E_2)*r0_2 + M*(r0 - 11*E_2*r0)) + a_9*E_2*M*(4*(-1 + 3*E_2)*M_2 + 2*r0_2 + M*(r0 + 6*E_2*r0)) + a_6*E*L*M*(L_2*(24*M_2 + (17 + 6*E_2)*M*r0 - 2*(2 + E_2)*r0_2) + r0*(16*(-1 + 2*E_2)*M_3 - 2*(4 + 57*E_2)*M_2*r0 + (6 - 49*E_2)*M*r0_2 + (-1 + E_2)*r0_3)) - a_5*M*(2*L_4*(6*M_2 + (4 + 9*E_2)*M*r0 - 2*E_2*r0_2) + E_2*r0_3*(12*(1 + 4*E_2)*M_3 + 4*(1 - 5*E_2)*M_2*r0 - (19 + 52*E_2)*M*r0_2 - 13*r0_3) + 2*L_2*r0*(-4*M_3 - 2*(2 + 25*E_2)*M_2*r0 + (4 + 7*E_2)*M*r0_2 + (2 + E_2)*r0_3)) + a_4*E*L*r0*(2*L_4*M*(9*M - r0) + L_2*M*(-32*M_3 - 2*(17 + 4*E_2)*M_2*r0 + (39 + 10*E_2)*M*r0_2 + 5*r0_3) + r0_2*(4*(7 + 38*E_2)*M_4 - 4*(3 + 13*E_2)*M_3*r0 - (23 + 97*E_2)*M_2*r0_2 + (8 + 13*E_2)*M*r0_3 + 2*r0_4)) + a_2*E*L*r0_2*(2*L_4*M*(-12*M_2 + 9*M*r0 + r0_2) + L_2*M*r0*(56*M_3 + 4*(-11 + 2*E_2)*M_2*r0 - 2*(10 + E_2)*M*r0_2 + 3*(7 + 2*E_2)*r0_3) + r0_3*(-8*M_4 + 6*(8 + 13*E_2)*M_3*r0 - (40 + 67*E_2)*M_2*r0_2 + 3*(1 + 5*E_2)*M*r0_3 + 4*r0_4)) + a_7*M*(E_2*r0*((8 - 16*E_2)*M_3 + 44*E_2*M_2*r0 + (3 + 32*E_2)*M*r0_2 + 7*r0_3) - 2*L_2*(2*M_2 - 3*E_2*r0_2 + M*(r0 + 2*E_2*r0))) + E*L*r0_4*(4*L_4*M*(2*M_2 - 3*M*r0 + r0_2) + 2*L_2*M*r0_2*(16*M_2 - (20 + 3*E_2)*M*r0 + 2*(3 + E_2)*r0_2) + r0_4*(8*M_3 + (-4 + 5*E_2)*M*r0_2 + 2*r0_3 - 4*M_2*(r0 + 2*E_2*r0))) - a_3*M*r0*(6*L_6*M + E_2*r0_4*(4*M_3 + 4*(11 + 3*E_2)*M_2*r0 - (17 + 32*E_2)*M*r0_2 - 13*r0_3) + 2*L_2*r0_2*((8 + 80*E_2)*M_3 - 2*(5 + 19*E_2)*M_2*r0 - (3 + 34*E_2)*M*r0_2 + (4 + 15*E_2)*r0_3) + L_4*(-16*M_3 + 8*(1 + 3*E_2)*M*r0_2 + 2*(2 + E_2)*r0_3 - 4*M_2*(r0 + 6*E_2*r0))) - a*M*r0_2*(4*L_6*M*(-2*M + r0) + E_2*r0_6*(20*M_2 - 6*E_2*M*r0 - 5*r0_2) + 2*L_2*r0_4*((4 + 56*E_2)*M_2 - (6 + 47*E_2)*M*r0 + (2 + 11*E_2)*r0_2) + 2*L_4*r0_2*(2*r0*(-2*M + r0) + E_2*(8*M_2 - 7*M*r0 + 3*r0_2)))))/(r0_8*expr2_2);\n    A21341 = (16*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_8*E*L*M*(24*(-1 + 3*E_2)*M_2 + 44*E_2*M*r0 + (7 + 9*E_2)*r0_2)) + a_9*E_2*M*(12*(-1 + 3*E_2)*M_2 + 7*r0_2 + 6*M*(r0 + 3*E_2*r0)) + a_7*M*(L_2*(-12*M_2 + 2*(-3 + 5*E_2 + 3*E_4)*M*r0 + 25*E_2*r0_2) + 2*E_2*r0*((12 - 24*E_2)*M_3 + 6*(-1 + 11*E_2)*M_2*r0 + (1 + 48*E_2)*M*r0_2 + 10*r0_3)) + a_5*M*(-6*L_4*(6*M_2 + 2*(2 + 3*E_2)*M*r0 - 3*E_2*r0_2) + 2*E_2*r0_3*(-12*(1 + 6*E_2)*M_3 + 2*(4 + 15*E_2)*M_2*r0 + (25 + 78*E_2)*M*r0_2 + 17*r0_3) + L_2*r0*(24*M_3 - 8*(-3 - 29*E_2 + E_4)*M_2*r0 + 2*(-12 - 13*E_2 + 13*E_4)*M*r0_2 + (-12 + 7*E_2)*r0_3)) + a_2*E*L*r0_2*(L_4*M*(-64*M_2 + 48*M*r0 + 7*r0_2) + r0_4*(4*(26 + 51*E_2)*M_3 - 2*(53 + 92*E_2)*M_2*r0 + (13 + 44*E_2)*M*r0_2 + 10*r0_3) + L_2*M*r0*(144*M_3 + 40*(-3 + 2*E_2)*M_2*r0 - 2*(25 + 28*E_2)*M*r0_2 + (58 + 21*E_2)*r0_3)) - a_4*E*L*r0*(L_4*(-48*M_2 + 9*M*r0) + r0_2*(-72*(1 + 6*E_2)*M_4 + 4*(13 + 32*E_2)*M_3*r0 + 4*(10 + 69*E_2)*M_2*r0_2 - (23 + 34*E_2)*M*r0_3 - 2*r0_4) + L_2*(96*M_4 + 68*M_3*r0 + 2*(-57 + 14*E_2)*M_2*r0_2 + (-8 + E_2)*M*r0_3 + 2*r0_4)) - a_6*E*L*(L_2*M*(-72*M_2 - 40*M*r0 + (16 + 9*E_2)*r0_2) + r0*((48 - 96*E_2)*M_4 + 4*(3 + 77*E_2)*M_3*r0 + 2*(-11 + 80*E_2)*M_2*r0_2 + (9 + 4*E_2)*M*r0_3 + 2*r0_4)) + E*L*r0_4*(2*L_4*M*(16*M_2 - 22*M*r0 + 7*r0_2) + L_2*r0_2*(88*M_3 - 4*(26 + 5*E_2)*M_2*r0 + 13*(2 + E_2)*M*r0_2 + 2*r0_3) + 3*r0_4*(8*M_3 + (-4 + 5*E_2)*M*r0_2 + 2*r0_3 - 4*M_2*(r0 + 2*E_2*r0))) - a_3*M*r0*(18*L_6*M + 6*E_2*r0_4*(8*M_3 + 6*(3 + E_2)*M_2*r0 - (9 + 16*E_2)*M*r0_2 - 6*r0_3) + L_2*r0_2*(48*(1 + 9*E_2)*M_3 + 4*(-15 - 47*E_2 + 6*E_4)*M_2*r0 - 2*(9 + 91*E_2 + 13*E_4)*M*r0_2 + 3*(8 + 23*E_2)*r0_3) + L_4*(-48*M_3 + 2*(12 + 17*E_2)*M*r0_2 + 6*(2 + E_2)*r0_3 - 12*M_2*(r0 + 4*E_2*r0))) + a*M*r0_2*(12*L_6*M*(2*M - r0) + 3*E_2*r0_6*(-20*M_2 + 6*E_2*M*r0 + 5*r0_2) - 3*L_2*r0_4*(4*(2 + 25*E_2)*M_2 - 2*(6 + 39*E_2 + E_4)*M*r0 + (4 + 17*E_2)*r0_2) - 2*L_4*r0_2*(6*r0*(-2*M + r0) + E_2*(44*M_2 - 37*M*r0 + 12*r0_2)))))/(r0_5*expr2_2);\n    A21521 = (4*(-(a_8*E*L*M*(24*(-1 + 3*E_2)*M_2 + (3 + 55*E_2)*M*r0 + 4*(2 + 3*E_2)*r0_2)) + a_9*E_2*M*(12*(-1 + 3*E_2)*M_2 + 8*r0_2 + 9*M*(r0 + 2*E_2*r0)) + a_7*M*(L_2*(-12*M_2 + 2*(-3 + 16*E_2 + 6*E_4)*M*r0 + 32*E_2*r0_2) + E_2*r0*((24 - 48*E_2)*M_3 + 12*(-2 + 11*E_2)*M_2*r0 + (-5 + 96*E_2)*M*r0_2 + 19*r0_3)) + a_5*M*(-6*L_4*(6*M_2 + (4 + 3*E_2)*M*r0 - 4*E_2*r0_2) + E_2*r0_3*(-12*(1 + 12*E_2)*M_3 + 4*(11 + 15*E_2)*M_2*r0 + (43 + 156*E_2)*M*r0_2 + 29*r0_3) + 2*L_2*r0*(12*M_3 + 2*(6 + 41*E_2 - 4*E_4)*M_2*r0 + (-12 - 5*E_2 + 26*E_4)*M*r0_2 + 2*(-3 + 5*E_2)*r0_3)) - a_4*E*L*r0*(-6*L_4*M*(7*M - 2*r0) + r0_2*(-12*(5 + 34*E_2)*M_4 + 4*(17 + 25*E_2)*M_3*r0 + (11 + 261*E_2)*M_2*r0_2 - (22 + 29*E_2)*M*r0_3 + 2*r0_4) + L_2*(96*M_4 + (34 - 24*E_2)*M_3*r0 + (-111 + 86*E_2)*M_2*r0_2 + (-1 + 2*E_2)*M*r0_3 + 4*r0_4)) - a_6*E*L*(L_2*M*(-72*M_2 + (-29 + 18*E_2)*M*r0 + 4*(5 + 3*E_2)*r0_2) + r0*((48 - 96*E_2)*M_4 + 274*E_2*M_3*r0 + (-26 + 173*E_2)*M_2*r0_2 + (15 + 11*E_2)*M*r0_3 + 4*r0_4)) + a_2*E*L*r0_2*(L_4*(-56*M_3 + 42*M_2*r0 + 8*M*r0_2) + L_2*M*r0*(120*M_3 + 4*(-27 + 34*E_2)*M_2*r0 - 2*(20 + 53*E_2)*M*r0_2 + (53 + 24*E_2)*r0_3) + r0_3*(24*M_4 + 2*(32 + 87*E_2)*M_3*r0 - (92 + 167*E_2)*M_2*r0_2 + (17 + 43*E_2)*M*r0_3 + 8*r0_4)) + E*L*r0_4*(4*L_4*M*(10*M_2 - 13*M*r0 + 4*r0_2) + 2*L_2*r0_2*(40*M_3 - 11*(4 + E_2)*M_2*r0 + (8 + 7*E_2)*M*r0_2 + 2*r0_3) + 3*r0_4*(8*M_3 + (-4 + 5*E_2)*M*r0_2 + 2*r0_3 - 4*M_2*(r0 + 2*E_2*r0))) + a_3*M*r0*(-18*L_6*M + 3*E_2*r0_4*(-28*M_3 - 4*(7 + 3*E_2)*M_2*r0 + (19 + 32*E_2)*M*r0_2 + 11*r0_3) - 2*L_2*r0_2*(24*(1 + 8*E_2)*M_3 + 2*(-15 - 37*E_2 + 12*E_4)*M_2*r0 - (9 + 80*E_2 + 26*E_4)*M*r0_2 + 12*(1 + 2*E_2)*r0_3) + 2*L_4*(24*M_3 + 2*(-6 + E_2)*M*r0_2 - 3*(2 + E_2)*r0_3 + 6*M_2*(r0 + 2*E_2*r0))) + a*M*r0_2*(12*L_6*M*(2*M - r0) + 3*E_2*r0_6*(-20*M_2 + 6*E_2*M*r0 + 5*r0_2) - 6*L_2*r0_4*((4 + 44*E_2)*M_2 - (6 + 31*E_2 + 2*E_4)*M*r0 + 2*(1 + 3*E_2)*r0_2) - 2*L_4*r0_2*(6*r0*(-2*M + r0) + E_2*(64*M_2 - 53*M*r0 + 15*r0_2)))))/(r0_2*expr2_2);\n    A21701 = (r0*(a_7*E_2*M*((-2 + 6*E_2)*M + 3*r0) - a_6*E*L*M*(4*(-1 + 3*E_2)*M + (3 + 5*E_2)*r0) + a_5*M*(-2*L_2*(M - 5*E_2*r0) + E_2*r0*((4 - 8*E_2)*M_2 + 2*(-4 + 13*E_2)*M*r0 + 3*r0_2)) + a*M*r0*(4*L_4*M*(2*M - r0) + E_2*r0_4*(-20*M_2 + 6*E_2*M*r0 + 5*r0_2) - 2*L_2*r0_2*(4*(1 + 7*E_2)*M_2 - (6 + 23*E_2)*M*r0 + 2*(1 + 3*E_2)*r0_2)) + a_2*E*L*M*r0*(L_2*(-16*M_2 + 12*M*r0 + 3*r0_2) + r0_2*(8*(1 + 8*E_2)*M_2 - 2*(7 + 26*E_2)*M*r0 + (7 + 9*E_2)*r0_2)) + a_4*E*L*(L_2*M*(12*M - 5*r0) - r0*((8 - 16*E_2)*M_3 + 6*(-1 + 8*E_2)*M_2*r0 + E_2*M*r0_2 + 2*r0_3)) + E*L*r0_3*(2*L_2*M*(8*M_2 - 10*M*r0 + 3*r0_2) + r0_2*(8*M_3 + (-4 + 5*E_2)*M*r0_2 + 2*r0_3 - 4*M_2*(r0 + 2*E_2*r0))) + a_3*M*(-6*L_4*M + 2*L_2*r0*(2*M_2 - (2 + E_2)*r0_2 + M*(r0 + 7*E_2*r0)) + E_2*r0_3*(5*r0*(2*M + r0) + E_2*(-24*M_2 + 26*M*r0)))))/expr2_2;\n    A22141 = (96*E*expr1_2*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/(r0_3*expr2_2);\n    A22321 = (48*E*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_2;\n    A22501 = (6*E*r0_3*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_2;\n    A23141 = (16*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_8*E*L*M*(24*(-1 + 3*E_2)*M_2 + 44*E_2*M*r0 + (7 + 9*E_2)*r0_2)) + a_9*E_2*M*(12*(-1 + 3*E_2)*M_2 + 7*r0_2 + 6*M*(r0 + 3*E_2*r0)) + E*L*r0_4*(2*L_4*M*(18*M_2 - 25*M*r0 + 8*r0_2) + L_2*r0_2*(88*M_3 - (100 + 21*E_2)*M_2*r0 + 2*(10 + 7*E_2)*M*r0_2 + 4*r0_3) + r0_4*(20*M_3 - (2 + 25*E_2)*M_2*r0 + 4*(-5 + 4*E_2)*M*r0_2 + 8*r0_3)) + a_7*M*(L_2*(-12*M_2 + 2*(-3 + 5*E_2 + 3*E_4)*M*r0 + 25*E_2*r0_2) + E_2*r0*((24 - 48*E_2)*M_3 + 2*(-5 + 66*E_2)*M_2*r0 + (1 + 96*E_2)*M*r0_2 + 19*r0_3)) + a_5*M*(-6*L_4*(6*M_2 + 2*(2 + 3*E_2)*M*r0 - 3*E_2*r0_2) + L_2*r0*(24*M_3 + 4*(6 + 61*E_2 - 2*E_4)*M_2*r0 + (-24 - 31*E_2 + 26*E_4)*M*r0_2 - 12*r0_3) + E_2*r0_3*(-4*(7 + 36*E_2)*M_3 + 2*(7 + 30*E_2)*M_2*r0 + (55 + 156*E_2)*M*r0_2 + 35*r0_3)) - a_6*E*L*(L_2*M*(-72*M_2 - 40*M*r0 + (16 + 9*E_2)*r0_2) + r0*((48 - 96*E_2)*M_4 + 2*(7 + 157*E_2)*M_3*r0 + (-23 + 157*E_2)*M_2*r0_2 + (8 + E_2)*M*r0_3 + 2*r0_4)) + a_4*E*L*r0*(3*L_4*M*(16*M - 3*r0) + L_2*(-96*M_4 - 74*M_3*r0 + (116 - 31*E_2)*M_2*r0_2 + 2*(6 + E_2)*M*r0_3 - 2*r0_4) + r0_2*((76 + 440*E_2)*M_4 - 4*(13 + 35*E_2)*M_3*r0 - 3*(16 + 93*E_2)*M_2*r0_2 + (25 + 41*E_2)*M*r0_3 + 4*r0_4)) + a_2*E*L*r0_2*(L_4*M*(-64*M_2 + 45*M*r0 + 10*r0_2) + L_2*r0*(152*M_4 + 42*(-3 + 2*E_2)*M_3*r0 - 8*(7 + 8*E_2)*M_2*r0_2 + 5*(12 + 5*E_2)*M*r0_3 + 2*r0_4) + r0_3*(-8*M_4 + 2*(61 + 103*E_2)*M_3*r0 - (113 + 191*E_2)*M_2*r0_2 + (6 + 49*E_2)*M*r0_3 + 14*r0_4)) - a_3*M*r0*(18*L_6*M + E_2*r0_4*(36*M_3 + 2*(61 + 18*E_2)*M_2*r0 - 3*(17 + 32*E_2)*M*r0_2 - 41*r0_3) + L_2*r0_2*(16*(3 + 28*E_2)*M_3 + 2*(-30 - 103*E_2 + 12*E_4)*M_2*r0 - 2*(9 + 95*E_2 + 13*E_4)*M*r0_2 + (24 + 79*E_2)*r0_3) - 4*L_4*(12*M_3 - (6 + 7*E_2)*M*r0_2 - 3*(1 + E_2)*r0_3 + 3*M_2*(r0 + 4*E_2*r0))) - a*M*r0_2*(12*L_6*M*(-2*M + r0) + L_2*r0_4*((24 + 302*E_2)*M_2 - (36 + 239*E_2 + 6*E_4)*M*r0 + 6*(2 + 9*E_2)*r0_2) + 9*E_2*r0_6*(6*M_2 - 2*r0_2 + M*(r0 - 2*E_2*r0)) + 2*L_4*r0_2*(6*r0*(-2*M + r0) + E_2*(48*M_2 - 44*M*r0 + 15*r0_2)))))/(r0_5*expr2_3);\n    A23321 = (8*(-(a_8*E*L*M*(24*(-1 + 3*E_2)*M_2 + (3 + 55*E_2)*M*r0 + 4*(2 + 3*E_2)*r0_2)) + a_9*E_2*M*(12*(-1 + 3*E_2)*M_2 + 8*r0_2 + 9*M*(r0 + 2*E_2*r0)) - 2*a_7*M*(L_2*(6*M_2 + (3 - 16*E_2 - 6*E_4)*M*r0 - 16*E_2*r0_2) + E_2*r0*(12*(-1 + 2*E_2)*M_3 + 11*(1 - 6*E_2)*M_2*r0 + 3*(1 - 16*E_2)*M*r0_2 - 9*r0_3)) + E*L*r0_4*(2*L_4*M*(22*M_2 - 29*M*r0 + 9*r0_2) + L_2*r0_2*(80*M_3 - (84 + 23*E_2)*M_2*r0 + 5*(2 + 3*E_2)*M*r0_2 + 6*r0_3) + r0_4*(20*M_3 - (2 + 25*E_2)*M_2*r0 + 4*(-5 + 4*E_2)*M*r0_2 + 8*r0_3)) + a_5*M*(-6*L_4*(6*M_2 + (4 + 3*E_2)*M*r0 - 4*E_2*r0_2) + 2*E_2*r0_3*(-8*(1 + 9*E_2)*M_3 + 3*(7 + 10*E_2)*M_2*r0 + 6*(4 + 13*E_2)*M*r0_2 + 15*r0_3) + L_2*r0*(24*M_3 - 8*(-3 - 22*E_2 + 2*E_4)*M_2*r0 + (-24 - 15*E_2 + 52*E_4)*M*r0_2 + (-12 + 13*E_2)*r0_3)) + a_4*E*L*r0*(6*L_4*M*(7*M - 2*r0) + M*r0_2*(32*(2 + 13*E_2)*M_3 - 4*(17 + 28*E_2)*M_2*r0 - (19 + 264*E_2)*M*r0_2 + 12*(2 + 3*E_2)*r0_3) + L_2*(-96*M_4 + 8*(-5 + 3*E_2)*M_3*r0 + (113 - 89*E_2)*M_2*r0_2 + (5 + E_2)*M*r0_3 - 4*r0_4)) + a_2*E*L*r0_2*(L_4*M*(-56*M_2 + 39*M*r0 + 11*r0_2) + L_2*r0*(128*M_4 + 2*(-57 + 70*E_2)*M_3*r0 - 2*(23 + 57*E_2)*M_2*r0_2 + (55 + 28*E_2)*M*r0_3 + 2*r0_4) + r0_3*(16*M_4 + 2*(41 + 88*E_2)*M_3*r0 - 3*(33 + 58*E_2)*M_2*r0_2 + 2*(5 + 24*E_2)*M*r0_3 + 12*r0_4)) - 2*a_3*M*r0*(9*L_6*M + E_2*r0_4*(36*M_3 + (49 + 18*E_2)*M_2*r0 - 3*(9 + 16*E_2)*M*r0_2 - 19*r0_3) + L_2*r0_2*(8*(3 + 25*E_2)*M_3 + (-30 - 83*E_2 + 24*E_4)*M_2*r0 - (9 + 84*E_2 + 26*E_4)*M*r0_2 + (12 + 29*E_2)*r0_3) + L_4*(-24*M_3 + (12 - 5*E_2)*M*r0_2 + 6*(1 + E_2)*r0_3 - 6*M_2*(r0 + 2*E_2*r0))) - a_6*E*L*(L_2*M*(-72*M_2 + (-29 + 18*E_2)*M*r0 + 4*(5 + 3*E_2)*r0_2) + r0*((48 - 96*E_2)*M_4 + (-27 + 170*E_2)*M_2*r0_2 + 2*(7 + 4*E_2)*M*r0_3 + 4*r0_4 + 2*M_3*(r0 + 140*E_2*r0))) - a*M*r0_2*(12*L_6*M*(-2*M + r0) + L_2*r0_4*((24 + 266*E_2)*M_2 - (36 + 191*E_2 + 12*E_4)*M*r0 + 3*(4 + 13*E_2)*r0_2) + 9*E_2*r0_6*(6*M_2 - 2*r0_2 + M*(r0 - 2*E_2*r0)) + 4*L_4*r0_2*(3*r0*(-2*M + r0) + E_2*(34*M_2 - 30*M*r0 + 9*r0_2)))))/(r0_2*expr2_3);\n    A23501 = (r0*(3*a_7*E_2*M*((-2 + 6*E_2)*M + 3*r0) - 3*a_6*E*L*M*(4*(-1 + 3*E_2)*M + (3 + 5*E_2)*r0) + a_5*M*(-6*L_2*(M - 5*E_2*r0) + E_2*r0*((12 - 24*E_2)*M_2 + (-23 + 78*E_2)*M*r0 + 8*r0_2)) + a_4*E*L*(3*L_2*M*(12*M - 5*r0) + r0*(24*(-1 + 2*E_2)*M_3 + (17 - 147*E_2)*M_2*r0 + M*r0_2 - 6*r0_3)) + a_2*E*L*r0*(3*L_2*M*(-16*M_2 + 11*M*r0 + 4*r0_2) + r0_2*(2*(13 + 98*E_2)*M_3 - (43 + 164*E_2)*M_2*r0 + (18 + 31*E_2)*M*r0_2 + 2*r0_3)) + E*L*r0_3*(2*L_2*M*(26*M_2 - 33*M*r0 + 10*r0_2) + r0_2*(20*M_3 - (2 + 25*E_2)*M_2*r0 + 4*(-5 + 4*E_2)*M*r0_2 + 8*r0_3)) - a*M*r0*(12*L_4*M*(-2*M + r0) + 2*L_2*r0_2*(4*(3 + 22*E_2)*M_2 - 2*(9 + 38*E_2)*M*r0 + 3*(2 + 7*E_2)*r0_2) + 9*E_2*r0_4*(6*M_2 - 2*r0_2 + M*(r0 - 2*E_2*r0))) + a_3*M*(-18*L_4*M + E_2*r0_3*(-2*(1 + 36*E_2)*M_2 + 6*(5 + 13*E_2)*M*r0 + 17*r0_2) + 6*L_2*r0*(2*M_2 - 2*(1 + E_2)*r0_2 + M*(r0 + 8*E_2*r0)))))/expr2_3;\n    A24121 = (24*E*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_3;\n    A24301 = (6*E*r0_3*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_3;\n    A25121 = (4*(-(a_8*E*L*M*(24*(-1 + 3*E_2)*M_2 + (3 + 55*E_2)*M*r0 + 4*(2 + 3*E_2)*r0_2)) + a_9*E_2*M*(12*(-1 + 3*E_2)*M_2 + 8*r0_2 + 9*M*(r0 + 2*E_2*r0)) + E*L*r0_4*(4*L_4*M*(12*M_2 - 16*M*r0 + 5*r0_2) + 4*L_2*r0_2*(20*M_3 - 2*(10 + 3*E_2)*M_2*r0 + (1 + 4*E_2)*M*r0_2 + 2*r0_3) + r0_4*(16*M_3 + (8 - 26*E_2)*M_2*r0 + (-28 + 17*E_2)*M*r0_2 + 10*r0_3)) + a_7*M*(L_2*(-12*M_2 + 2*(-3 + 16*E_2 + 6*E_4)*M*r0 + 32*E_2*r0_2) + E_2*r0*((24 - 48*E_2)*M_3 + 4*(-5 + 33*E_2)*M_2*r0 + (-7 + 96*E_2)*M*r0_2 + 17*r0_3)) + a_5*M*(-6*L_4*(6*M_2 + (4 + 3*E_2)*M*r0 - 4*E_2*r0_2) + E_2*r0_3*(-4*(5 + 36*E_2)*M_3 + 20*(2 + 3*E_2)*M_2*r0 + (53 + 156*E_2)*M*r0_2 + 31*r0_3) + 2*L_2*r0*(12*M_3 + 2*(6 + 47*E_2 - 4*E_4)*M_2*r0 + 2*(-6 - 5*E_2 + 13*E_4)*M*r0_2 + 3*(-2 + E_2)*r0_3)) + a_4*E*L*r0*(6*L_4*M*(7*M - 2*r0) + L_2*(-96*M_4 + 2*(-23 + 12*E_2)*M_3*r0 - 23*(-5 + 4*E_2)*M_2*r0_2 + (9 + 4*E_2)*M*r0_3 - 4*r0_4) + r0_2*((68 + 424*E_2)*M_4 - 4*(17 + 31*E_2)*M_3*r0 - 3*(9 + 89*E_2)*M_2*r0_2 + (26 + 43*E_2)*M*r0_3 + 2*r0_4)) - a_6*E*L*(L_2*M*(-72*M_2 + (-29 + 18*E_2)*M*r0 + 4*(5 + 3*E_2)*r0_2) + r0*((48 - 96*E_2)*M_4 + 2*(2 + 143*E_2)*M_3*r0 + (-28 + 167*E_2)*M_2*r0_2 + (13 + 5*E_2)*M*r0_3 + 4*r0_4)) + a_2*E*L*r0_2*(2*L_4*M*(-28*M_2 + 18*M*r0 + 7*r0_2) + L_2*r0*(136*M_4 + 24*(-5 + 6*E_2)*M_3*r0 - 2*(26 + 61*E_2)*M_2*r0_2 + (57 + 32*E_2)*M*r0_3 + 4*r0_4) + r0_3*(8*M_4 + 2*(50 + 89*E_2)*M_3*r0 - (106 + 181*E_2)*M_2*r0_2 + (3 + 53*E_2)*M*r0_3 + 16*r0_4)) + a_3*M*r0*(-18*L_6*M + E_2*r0_4*(-60*M_3 - 4*(28 + 9*E_2)*M_2*r0 + 3*(17 + 32*E_2)*M*r0_2 + 43*r0_3) - 2*L_2*r0_2*(8*(3 + 26*E_2)*M_3 + 2*(-15 - 46*E_2 + 12*E_4)*M_2*r0 - (9 + 88*E_2 + 26*E_4)*M*r0_2 + 2*(6 + 17*E_2)*r0_3) + 2*L_4*(24*M_3 + 4*(-3 + 2*E_2)*M*r0_2 - 3*(2 + 3*E_2)*r0_3 + 6*M_2*(r0 + 2*E_2*r0))) - a*M*r0_2*(12*L_6*M*(-2*M + r0) + 3*E_2*r0_6*(16*M_2 - 6*(-1 + E_2)*M*r0 - 7*r0_2) + 2*L_2*r0_4*(2*(6 + 67*E_2)*M_2 - 2*(9 + 49*E_2 + 3*E_4)*M*r0 + 3*(2 + 7*E_2)*r0_2) + 2*L_4*r0_2*(6*r0*(-2*M + r0) + E_2*(72*M_2 - 67*M*r0 + 21*r0_2)))))/(r0_2*expr2_4);\n    A25301 = (r0*(3*a_7*E_2*M*((-2 + 6*E_2)*M + 3*r0) - 3*a_6*E*L*M*(4*(-1 + 3*E_2)*M + (3 + 5*E_2)*r0) + a_5*M*(-6*L_2*(M - 5*E_2*r0) + E_2*r0*((12 - 24*E_2)*M_2 + 2*(-11 + 39*E_2)*M*r0 + 7*r0_2)) - a*M*r0*(12*L_4*M*(-2*M + r0) + 3*E_2*r0_4*(16*M_2 - 6*(-1 + E_2)*M*r0 - 7*r0_2) + 2*L_2*r0_2*(4*(3 + 23*E_2)*M_2 - (18 + 83*E_2)*M*r0 + 6*(1 + 4*E_2)*r0_2)) + a_4*E*L*(3*L_2*M*(12*M - 5*r0) + r0*(24*(-1 + 2*E_2)*M_3 - 2*(-8 + 75*E_2)*M_2*r0 + (2 + 3*E_2)*M*r0_2 - 6*r0_3)) + a_2*E*L*r0*(3*L_2*M*(-16*M_2 + 10*M*r0 + 5*r0_2) + r0_2*(4*(7 + 50*E_2)*M_3 - 4*(11 + 43*E_2)*M_2*r0 + 5*(3 + 7*E_2)*M*r0_2 + 4*r0_3)) + E*L*r0_3*(2*L_2*M*(28*M_2 - 36*M*r0 + 11*r0_2) + r0_2*(16*M_3 + (8 - 26*E_2)*M_2*r0 + (-28 + 17*E_2)*M*r0_2 + 10*r0_3)) + a_3*M*(-18*L_4*M + E_2*r0_3*(-4*(1 + 18*E_2)*M_2 + 6*(5 + 13*E_2)*M*r0 + 19*r0_2) + 6*L_2*r0*(2*M_2 - (2 + 3*E_2)*r0_2 + M*(r0 + 9*E_2*r0)))))/expr2_4;\n    A26101 = (2*E*r0_3*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_4;\n    A27101 = (r0*(a_7*E_2*M*((-2 + 6*E_2)*M + 3*r0) - a_6*E*L*M*(4*(-1 + 3*E_2)*M + (3 + 5*E_2)*r0) + a_5*M*(-2*L_2*(M - 5*E_2*r0) + E_2*r0*((4 - 8*E_2)*M_2 + (-7 + 26*E_2)*M*r0 + 2*r0_2)) + a_4*E*L*(L_2*M*(12*M - 5*r0) + r0*(8*(-1 + 2*E_2)*M_3 + (5 - 51*E_2)*M_2*r0 + (1 + 2*E_2)*M*r0_2 - 2*r0_3)) + E*L*r0_3*(2*L_2*M*(10*M_2 - 13*M*r0 + 4*r0_2) + r0_2*(4*M_3 + (6 - 9*E_2)*M_2*r0 + 6*(-2 + E_2)*M*r0_2 + 4*r0_3)) + a_2*E*L*r0*(L_2*M*(-16*M_2 + 9*M*r0 + 6*r0_2) + r0_2*(2*(5 + 34*E_2)*M_3 + (4 + 13*E_2)*M*r0_2 + 2*r0_3 - 15*M_2*(r0 + 4*E_2*r0))) - a*M*r0*(4*L_4*M*(-2*M + r0) + E_2*r0_4*(14*M_2 + (9 - 6*E_2)*M*r0 - 8*r0_2) + 2*L_2*r0_2*(4*(1 + 8*E_2)*M_2 + (2 + 9*E_2)*r0_2 - 6*M*(r0 + 5*E_2*r0))) + a_3*M*(-6*L_4*M + E_2*r0_3*(-2*(1 + 12*E_2)*M_2 + 2*(5 + 13*E_2)*M*r0 + 7*r0_2) + 2*L_2*r0*(2*M_2 - 2*(1 + 2*E_2)*r0_2 + M*(r0 + 10*E_2*r0)))))/expr2_5;\n  }\n\n  /* A_{t,phi} */\n  {\n    A30060 = -256*E*L*expr4_3;\n    A30080 = (128*expr1_2*(-(a_8*E*L*M_2*r0) - 12*a_9*E_2*M_2*(2*M + r0) + E*L*r0_5*(36*L_4*M*(-2*M + r0) + 2*r0_4*(-2*M_2 + (-11 + 8*E_2)*M*r0 + 6*r0_2) + L_2*r0_2*(-88*M_2 + 2*(13 + 6*E_2)*M*r0 + 9*r0_2)) + 4*a*M*r0_2*(18*L_6*M*(2*M - r0) + 3*E_2*r0_7*(-2*M + 3*r0) - 3*L_4*r0_2*(-16*M_2 - 2*(-3 + E_2)*M*r0 + r0_2) + L_2*r0_4*(12*M_2 + 2*E_2*M*r0 + 3*(-1 + 2*E_2)*r0_2)) + 4*a_3*M*r0*(3*E_2*r0_5*(18*M_2 + 7*M*r0 + 7*r0_2) + 3*L_4*(40*M_3 + 2*(-1 + 2*E_2)*M_2*r0 + (-7 + 2*E_2)*M*r0_2 - r0_3) + L_2*r0_2*(72*M_3 + 2*(3 + 34*E_2)*M_2*r0 + 9*(-1 + 2*E_2)*M*r0_2 + 2*(-3 + 4*E_2)*r0_3)) + 2*a_6*E*L*M*(-384*M_4 + 64*(-6 + E_2)*M_3*r0 + 2*(-29 + 48*E_2)*M_2*r0_2 + (1 + 8*E_2)*r0_4 + M*(-2*L_2*r0 + (17 + 48*E_2)*r0_3)) + 4*a_7*M*(L_2*M*(6*M + 3*r0 - 2*E_2*r0) + 3*E_2*(32*M_4 + 52*M_3*r0 + 18*M_2*r0_2 + M*r0_3 + r0_4)) + a_4*E*L*r0*(4*L_2*M*(-208*M_3 + 6*(-17 + 2*E_2)*M_2*r0 + 12*(2 + E_2)*M*r0_2 + (11 + 3*E_2)*r0_3) + r0_2*(-916*M_4 + 4*(-95 + 48*E_2)*M_3*r0 + 3*(21 + 64*E_2)*M_2*r0_2 + 6*(5 + 8*E_2)*M*r0_3 + 12*r0_4)) + 4*a_5*M*(3*L_4*M*r0 + 3*E_2*r0_3*(60*M_3 + 46*M_2*r0 + 11*M*r0_2 + 5*r0_3) + L_2*(96*M_4 + 4*(9 + 14*E_2)*M_3*r0 + 12*(-1 + 5*E_2)*M_2*r0_2 + 6*(-1 + E_2)*M*r0_3 + (-3 + 2*E_2)*r0_4)) + a_2*E*L*r0_2*(L_4*(-240*M_3 + 36*M*r0_2) + 2*r0_4*(-128*M_3 + 12*(-1 + 4*E_2)*M_2*r0 + 3*(1 + 8*E_2)*M*r0_2 + 12*r0_3) + L_2*r0_2*(-616*M_3 + 8*(11 + 3*E_2)*M*r0_2 + 9*r0_3 + 12*M_2*(r0 + 4*E_2*r0)))))/(3.*r0_9*(a_2 + r0*(-2*M + r0)));\n    A30240 = -192*E*L*expr1_2;\n    A30260 = (64*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-6*a_9*E_2*M*(14*M_2 + 11*M*r0 + 2*r0_2) + a_8*E*L*M*(6*E_2*r0_p_2M_2 - r0*(7*M + 3*r0)) + E*L*r0_5*(-74*L_4*M*(2*M - r0) + 2*L_2*r0_2*(-96*M_2 + (30 + 11*E_2)*M*r0 + 9*r0_2) + r0_4*(-32*M_2 + 26*(-1 + E_2)*M*r0 + 21*r0_2)) + a_6*E*L*(-1536*M_5 + 32*(-48 + 5*E_2)*M_4*r0 + 72*(-3 + 4*E_2)*M_3*r0_2 + 12*(9 + 16*E_2)*M_2*r0_3 + (19 + 44*E_2)*M*r0_4 + 3*r0_5 + L_2*M*(24*M_2 + 4*(2 + 3*E_2)*M*r0 + 3*(-1 + 2*E_2)*r0_2)) + 2*a*M*r0_2*(3*E_2*r0_7*(-2*M + 9*r0) + 6*L_6*(22*M_2 - 13*M*r0 + r0_2) + L_4*r0_2*(192*M_2 + 2*(-51 + 20*E_2)*M*r0 + 3*(1 - 3*E_2)*r0_2) + L_2*r0_4*(60*M_2 + 2*(-12 + 19*E_2)*M*r0 + 3*(-1 + 4*E_2)*r0_2)) + a_2*E*L*r0_2*(2*L_4*M*(-232*M_2 + 6*M*r0 + 37*r0_2) + L_2*r0_2*(-1264*M_3 + 2*(15 + 38*E_2)*M_2*r0 + (189 + 50*E_2)*M*r0_2 + 21*r0_3) + r0_4*(-584*M_3 + 18*(-3 + 8*E_2)*M_2*r0 + 3*(15 + 28*E_2)*M*r0_2 + 45*r0_3)) + 2*a_3*M*r0*(6*L_6*r0 + 3*E_2*r0_5*(78*M_2 + 37*M*r0 + 19*r0_2) + L_2*r0_2*(312*M_3 + 10*(-3 + 35*E_2)*M_2*r0 + (-51 + 107*E_2)*M*r0_2 - 5*E_2*r0_3) + L_4*(456*M_3 + 2*(-33 + 34*E_2)*M_2*r0 + (-87 + 28*E_2)*M*r0_2 + 3*(5 - 6*E_2)*r0_3)) - 2*a_7*M*(L_2*(6*(-7 + 4*E_2)*M_2 + (-33 + 49*E_2)*M*r0 + 3*(-2 + 5*E_2)*r0_2) - 3*E_2*(128*M_4 + 220*M_3*r0 + 86*M_2*r0_2 - 5*M*r0_3 - 3*r0_4)) + a_4*E*L*r0*(12*L_4*M_2 + L_2*(-1664*M_4 + 32*(-25 + 2*E_2)*M_3*r0 + 22*(11 + 4*E_2)*M_2*r0_2 + 2*(48 + 17*E_2)*M*r0_3 + 3*r0_4) + r0_2*(-1844*M_4 + 8*(-104 + 33*E_2)*M_3*r0 + 3*(55 + 104*E_2)*M_2*r0_2 + 3*(31 + 32*E_2)*M*r0_3 + 27*r0_4)) + 2*a_5*M*(-3*L_4*r0*((-11 + 4*E_2)*M + (-4 + 3*E_2)*r0) + 3*E_2*r0_3*(228*M_3 + 202*M_2*r0 + 45*M*r0_2 + 9*r0_3) + L_2*(384*M_4 + 4*(27 + 74*E_2)*M_3*r0 + 14*(-6 + 23*E_2)*M_2*r0_2 + 2*(3 - 4*E_2)*M*r0_3 + (9 - 32*E_2)*r0_4))))/(3.*r0_6*(a_2 + r0*(-2*M + r0)));\n    A30420 = -48*E*L*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2));\n    A30440 = (8*(-36*a_9*E_2*M*(10*M_2 + 9*M*r0 + 2*r0_2) + a_8*E*L*(-13*M_2*r0 + 3*r0_3 + 36*E_2*M*r0_p_2M_2) + E*L*r0_3*(8*L_6*M*(-2*M + r0) + L_4*r0_2*(-460*M_2 + 4*(52 + E_2)*M*r0 + 11*r0_2) + r0_6*(-168*M_2 + 2*(-17 + 30*E_2)*M*r0 + 59*r0_2) + L_2*r0_4*(-624*M_2 + 2*(89 + 30*E_2)*M*r0 + 67*r0_2)) + 4*a*M*r0_2*(9*E_2*r0_7*(2*M + 3*r0) + 3*L_2*r0_4*(36*M_2 + 2*(-12 + 17*E_2)*M*r0 + 3*r0_2) + 3*L_4*r0_2*(96*M_2 + 6*(-11 + 5*E_2)*M*r0 + (9 - 10*E_2)*r0_2) + 2*L_6*(90*M_2 + (-63 + 4*E_2)*M*r0 - 3*(-3 + E_2)*r0_2)) + 2*a_6*E*L*(-2304*M_5 + 96*(-24 + E_2)*M_4*r0 + 2*(-157 + 144*E_2)*M_3*r0_2 + (233 + 288*E_2)*M_2*r0_3 + (79 + 84*E_2)*M*r0_4 + 16*r0_5 + L_2*(72*M_3 + 2*(23 + 24*E_2)*M_2*r0 + 6*(1 + 4*E_2)*M*r0_2 + 3*r0_3)) + a_2*E*L*r0_2*(12*L_6*M - 2*L_4*(696*M_3 + 8*(-3 + E_2)*M_2*r0 - 4*(31 + 2*E_2)*M*r0_2 - 7*r0_3) + 2*r0_4*(-984*M_3 + 4*(-29 + 36*E_2)*M_2*r0 + 9*(13 + 12*E_2)*M*r0_2 + 72*r0_3) + L_2*r0_2*(-3864*M_3 + 4*(-1 + 36*E_2)*M_2*r0 + 12*(51 + 14*E_2)*M*r0_2 + 107*r0_3)) + 4*a_3*M*r0*(-6*(-3 + E_2)*L_6*r0 + 9*E_2*r0_5*(42*M_2 + 23*M*r0 + 5*r0_2) + 3*L_4*(216*M_3 + 2*(-27 + 26*E_2)*M_2*r0 + 9*(-5 + 2*E_2)*M*r0_2 + (21 - 22*E_2)*r0_3) + L_2*r0_2*(504*M_3 + 6*(-21 + 104*E_2)*M_2*r0 + (-99 + 224*E_2)*M*r0_2 + 4*(9 - 16*E_2)*r0_3)) - 4*a_7*M*(L_2*(18*(-5 + 4*E_2)*M_2 + (-81 + 140*E_2)*M*r0 + 6*(-3 + 8*E_2)*r0_2) - 9*E_2*(64*M_4 + 116*M_3*r0 + 50*M_2*r0_2 - 7*M*r0_3 - 5*r0_4)) + a_4*E*L*r0*(3*L_4*(32*M_2 + 4*(2 + E_2)*M*r0 + r0_2) + L_2*(-4992*M_4 + 8*(-299 + 6*E_2)*M_3*r0 + 8*(103 + 30*E_2)*M_2*r0_2 + 4*(95 + 39*E_2)*M*r0_3 + 46*r0_4) + r0_2*(-5604*M_4 + 4*(-691 + 108*E_2)*M_3*r0 + (559 + 720*E_2)*M_2*r0_2 + 6*(71 + 48*E_2)*M*r0_3 + 114*r0_4)) + 4*a_5*M*(3*L_4*r0*((27 - 16*E_2)*M - 12*(-1 + E_2)*r0) + 9*E_2*r0_3*(108*M_3 + 110*M_2*r0 + 23*M*r0_2 - r0_3) + L_2*(576*M_4 + 12*(9 + 46*E_2)*M_3*r0 + 4*(-45 + 154*E_2)*M_2*r0_2 + 2*(27 - 25*E_2)*M*r0_3 + (45 - 112*E_2)*r0_4))))/(3.*r0_3*(a_2 + r0*(-2*M + r0)));\n    A30600 = -4*E*L*r0_6;\n    A30620 = (-4*(6*a_7*E_2*M*(13*M + 6*r0) - 3*a_6*E*L*(r0*(M + r0) + 6*E_2*M*(2*M + r0)) - E*L*r0_2*(8*L_4*M*(-2*M + r0) + 2*r0_4*(-40*M_2 + 7*E_2*M*r0 + 10*r0_2) + L_2*r0_2*(-144*M_2 + 2*(25 + 2*E_2)*M*r0 + 11*r0_2)) - 2*a*M*r0*(3*E_2*r0_5*(10*M + 3*r0) + L_2*r0_2*(84*M_2 + 4*(-18 + 17*E_2)*M*r0 + 3*(5 - 7*E_2)*r0_2) + 2*L_4*(54*M_2 + (-45 + 8*E_2)*M*r0 + 3*(3 - 2*E_2)*r0_2)) - 6*a_5*M*(L_2*((13 - 12*E_2)*M + (6 - 11*E_2)*r0) + E_2*(64*M_3 + 90*M_2*r0 + 12*M*r0_2 - 11*r0_3)) - a_2*E*L*r0*(12*L_4*M - 2*L_2*(224*M_3 + 2*(-3 + 4*E_2)*M_2*r0 - (39 + 8*E_2)*M*r0_2 - 7*r0_3) + r0_2*(-568*M_3 + 2*(-19 + 10*E_2)*M_2*r0 + (71 + 46*E_2)*M*r0_2 + 37*r0_3)) - 2*a_3*M*(-6*(-3 + 2*E_2)*L_4*r0 + 3*E_2*r0_3*(70*M_2 + 39*M*r0 - 2*r0_2) + L_2*(192*M_3 + 2*(-39 + 62*E_2)*M_2*r0 + (-39 + 56*E_2)*M*r0_2 + 3*(11 - 18*E_2)*r0_3)) - a_4*E*L*(3*L_2*(12*M_2 + 4*(1 + E_2)*M*r0 + r0_2) + 2*(-384*M_4 - 8*(24 + E_2)*M_3*r0 + (43 + 28*E_2)*M_2*r0_2 + (31 + 25*E_2)*M*r0_3 + 10*r0_4))))/(3.*(a_2 + r0*(-2*M + r0)));\n    A30800 = (r0_3*(-24*a_5*E_2*M + 3*a_4*E*L*(4*E_2*M + r0) - 2*a_2*E*L*(-6*L_2*M + 96*M_3 + 8*E_2*M_2*r0 - 2*(5 + 4*E_2)*M*r0_2 - 7*r0_3) - 24*a_3*M*((-1 + E_2)*L_2 + E_2*(-4*M_2 - 4*M*r0 + r0_2)) + E*L*r0*(8*L_2*M*(-2*M + r0) + r0_2*(-52*M_2 + 4*(1 + E_2)*M*r0 + 11*r0_2)) + 8*a*M*(6*E_2*M*r0_3 + L_2*(12*M_2 + 4*(-3 + E_2)*M*r0 - 3*(-1 + E_2)*r0_2))))/(6.*(a_2 + r0*(-2*M + r0)));\n    A31060 = (-128*expr1_2*(a_4*E*L*M*r0 + 2*a_5*E_2*M*(2*M + r0) + E*L*r0_3*(2*L_2*M + r0_3) - 2*a*M*r0*(L_4 + L_2*r0_2 - 3*E_2*r0_4) - 2*a_3*M*(L_2*(2*M + r0) - 2*E_2*r0_2*(3*M + 2*r0)) + a_2*E*L*r0*(2*L_2*M + r0*(-6*M_2 + 3*M*r0 + r0_2))))/(r0_5*(a_2 + r0*(-2*M + r0)));\n    A31080 = (64*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(2*a_13*E_2*M_2*(-96*M_3 + 34*M_2*r0 + 113*M*r0_2 + 36*r0_3) + a_12*E*L*M_2*(384*M_3 + 16*(24 - 19*E_2)*M_2*r0 + (105 - 344*E_2)*M*r0_2 + 6*(1 - 16*E_2)*r0_3) + a_10*E*L*M*r0*(-512*(-15 + 2*E_2)*M_5 - 1920*(-7 + E_2)*M_4*r0 + 2*(3565 - 1248*E_2)*M_3*r0_2 + (695 - 2096*E_2)*M_2*r0_3 - (223 + 512*E_2)*M*r0_4 + 6*(-1 + 9*E_2)*r0_5 + 2*L_2*M*(264*M_2 - 5*(-39 + 32*E_2)*M*r0 + 3*(13 - 28*E_2)*r0_2)) + E*L*r0_6*(48*L_8*M*r0_2M_2 + 24*L_6*M*(2*M - r0)*r0_2*(11*M - (7 + 3*E_2)*r0) + 2*L_4*M*r0_4*(296*M_2 - 2*(197 + 108*E_2)*M*r0 + 3*(41 + 38*E_2)*r0_2) + L_2*r0_6*(320*M_3 - 4*(121 + 109*E_2)*M_2*r0 + 6*(26 + 41*E_2)*M*r0_2 + 3*r0_3) + 2*r0_8*(-4*M_3 + (2 - 86*E_2)*M_2*r0 + 3*(-4 + 17*E_2)*M*r0_2 + 6*r0_3)) - 2*a*M*r0_4*(24*L_10*r0_2M_2 - 6*E_2*r0_10*(-14*M_2 + (5 + 12*E_2)*M*r0 + r0_2) - 12*L_8*(2*M - r0)*r0*(14*M_2 - 2*(6 + E_2)*M*r0 + (4 + 3*E_2)*r0_2) + L_6*r0_3*(-864*M_3 + 8*(94 + 63*E_2)*M_2*r0 - 2*(101 + 258*E_2 + 36*E_4)*M*r0_2 + 3*(7 + 50*E_2)*r0_3) + L_2*r0_7*(-288*M_3 + 4*(65 + 244*E_2)*M_2*r0 - 2*(35 + 379*E_2 + 72*E_4)*M*r0_2 + 3*(2 + 53*E_2)*r0_3) + L_4*r0_5*(-816*M_3 + 4*(163 + 304*E_2)*M_2*r0 - 4*(32 + 248*E_2 + 45*E_4)*M*r0_2 + 3*(1 + 78*E_2)*r0_3)) + a_2*E*L*r0_4*(48*L_8*M*(6*M_2 - 5*M*r0 + r0_2) + 48*L_6*M*r0*(18*M_3 + (5 - 8*E_2)*M_2*r0 - 2*(7 + 3*E_2)*M*r0_2 + (5 + 3*E_2)*r0_3) + 4*L_4*M*r0_3*(124*M_3 - 48*(-3 + 4*E_2)*M_2*r0 - 6*(43 + 41*E_2)*M*r0_2 + 3*(41 + 55*E_2)*r0_3) + L_2*r0_5*(-2432*M_4 + 4*(583 + 48*E_2)*M_3*r0 - 1228*(1 + E_2)*M_2*r0_2 + 3*(153 + 308*E_2)*M*r0_3 + 6*r0_4) + 2*r0_7*(-1284*M_4 + 56*(19 + 4*E_2)*M_3*r0 - (263 + 376*E_2)*M_2*r0_2 + 3*(1 + 77*E_2)*M*r0_3 + 18*r0_4)) - 2*a_11*M*(L_2*M*(96*M_3 + (226 - 96*E_2)*M_2*r0 + (161 - 290*E_2)*M*r0_2 + 12*(3 - 10*E_2)*r0_3) - 2*E_2*r0*(320*(-3 + E_2)*M_5 + 4*(-599 + 160*E_2)*M_4*r0 + 3*(-551 + 160*E_2)*M_3*r0_2 + (-229 + 160*E_2)*M_2*r0_3 + (49 + 20*E_2)*M*r0_4 - 6*r0_5)) + a_4*E*L*r0_3*(24*L_6*M*(108*M_3 + 2*(9 - 14*E_2)*M_2*r0 - 2*(4 + 3*E_2)*M*r0_2 + 3*(1 + E_2)*r0_3) - 2*L_4*M*r0*(736*M_4 + 112*(-23 + 3*E_2)*M_3*r0 + 24*(-58 + 59*E_2)*M_2*r0_2 + 28*(7 + 15*E_2)*M*r0_3 - 3*(41 + 106*E_2)*r0_4) + L_2*r0_3*(-14144*M_5 + 48*(108 + 53*E_2)*M_4*r0 - 8*(-818 + 171*E_2)*M_3*r0_2 - 4*(356 + 351*E_2)*M_2*r0_3 + 24*(17 + 54*E_2)*M*r0_4 + 3*r0_5) + r0_5*(-14616*M_5 + 4*(691 + 516*E_2)*M_4*r0 + 2*(2735 - 4*E_2)*M_3*r0_2 - (1487 + 1288*E_2)*M_2*r0_3 + 12*(7 + 69*E_2)*M*r0_4 + 36*r0_5)) + 2*a_9*M*r0*(L_4*M*(-208*M_2 + (-313 + 168*E_2)*M*r0 + 12*(-9 + 10*E_2)*r0_2) + L_2*(-384*(5 + 6*E_2)*M_5 + 8*(-241 - 706*E_2 + 136*E_4)*M_4*r0 + 8*(-45 - 698*E_2 + 204*E_4)*M_3*r0_2 + (65 - 1270*E_2 + 816*E_4)*M_2*r0_3 + 2*(23 + 34*E_2 + 68*E_4)*M*r0_4 + 12*(2 - 9*E_2)*r0_5) + 2*E_2*r0*(2112*M_6 + 4*(893 + 144*E_2)*M_5*r0 + 2*(-695 + 896*E_2)*M_4*r0_2 + (-4079 + 1824*E_2)*M_3*r0_3 + 2*(-617 + 384*E_2)*M_2*r0_4 + (61 + 116*E_2)*M*r0_5 - 27*r0_6)) + a_6*E*L*r0_2*(72*L_6*M_2*r0 + 4*L_4*M*(1952*M_4 - 8*(-272 + 93*E_2)*M_3*r0 - 60*(-10 + 9*E_2)*M_2*r0_2 - (1 + 90*E_2)*M*r0_3 + 51*E_2*r0_4) + L_2*M*r0*(-14272*M_5 + 40*(-13 + 64*E_2)*M_4*r0 - 304*(-70 + 3*E_2)*M_3*r0_2 + 2*(3499 - 1952*E_2)*M_2*r0_3 - 2*(533 + 550*E_2)*M*r0_4 + 3*(25 + 268*E_2)*r0_5) + r0_3*(-26824*M_6 + 4*(-3273 + 424*E_2)*M_5*r0 + 2*(8659 + 1248*E_2)*M_4*r0_2 + (5521 - 2128*E_2)*M_3*r0_3 - (1759 + 1328*E_2)*M_2*r0_4 + 12*(4 + 61*E_2)*M*r0_5 + 12*r0_6)) + 2*a_7*M*r0*(-4*L_6*M*r0*(38*M - 9*(-3 + E_2)*r0) + 6*E_2*r0_4*(1460*M_5 + 12*(107 + 32*E_2)*M_4*r0 + (-893 + 736*E_2)*M_3*r0_2 + (-615 + 448*E_2)*M_2*r0_3 + 8*(5 + 11*E_2)*M*r0_4 - 14*r0_5) - 2*L_4*(704*M_5 + 8*(233 + 108*E_2)*M_4*r0 + 2*(307 + 598*E_2 - 180*E_4)*M_3*r0_2 + (-127 + 502*E_2 - 360*E_4)*M_2*r0_3 + (-31 + 84*E_2 - 90*E_4)*M*r0_4 + 3*(-8 + 35*E_2)*r0_5) + L_2*r0*(4224*M_6 + 8*(307 + 20*E_2)*M_5*r0 + 4*(-1051 - 734*E_2 + 288*E_4)*M_4*r0_2 + 2*(-1129 - 4344*E_2 + 1680*E_4)*M_3*r0_3 + (305 - 3082*E_2 + 2496*E_4)*M_2*r0_4 + 2*(122 + 53*E_2 + 276*E_4)*M*r0_5 + (66 - 449*E_2)*r0_6)) + a_8*E*L*M*r0*(72*L_4*M*r0*(4*M - (-2 + E_2)*r0) + 2*L_2*(3584*M_5 - 32*(-283 + 50*E_2)*M_4*r0 + 4*(1775 - 508*E_2)*M_3*r0_2 - 2*(-713 + 698*E_2)*M_2*r0_3 - 4*(37 + 82*E_2)*M*r0_4 + 3*(-5 + 31*E_2)*r0_5) + r0*(-16896*M_6 + 64*(-300 + 23*E_2)*M_5*r0 + 4*(3331 + 296*E_2)*M_4*r0_2 - 4*(-4751 + 280*E_2)*M_3*r0_3 + (2681 - 3424*E_2)*M_2*r0_4 - (1031 + 1036*E_2)*M*r0_5 + 6*(-2 + 53*E_2)*r0_6)) + 2*a_3*M*r0_3*(2*E_2*M*r0_8*(588*M_2 + 2*(-223 + 144*E_2)*M*r0 + (103 + 164*E_2)*r0_2) + L_2*r0_5*(2352*M_4 - 24*(47 + 68*E_2)*M_3*r0 + 36*(-13 - 39*E_2 + 24*E_4)*M_2*r0_2 + 2*(92 + 855*E_2 + 284*E_4)*M*r0_3 + (6 - 551*E_2)*r0_4) + 2*L_4*r0_3*(2160*M_4 - 4*(265 + 437*E_2)*M_3*r0 + 2*(-264 - 137*E_2 + 180*E_4)*M_2*r0_2 + (165 + 748*E_2 + 270*E_4)*M*r0_3 + (21 - 331*E_2)*r0_4) + L_6*r0*(2160*M_4 - 8*(229 + 144*E_2)*M_3*r0 + 24*(-22 + 13*E_2 + 6*E_4)*M_2*r0_2 + 4*(92 + 99*E_2 + 36*E_4)*M*r0_3 - 3*(7 + 100*E_2)*r0_4) - 12*L_8*(56*M_3 - (15 + 2*E_2)*M*r0_2 + (4 + 3*E_2)*r0_3 - 6*M_2*(r0 + 2*E_2*r0))) + 2*a_5*M*r0_2*(-36*L_8*M*r0 + E_2*r0_6*(6012*M_4 + 108*(5 + 16*E_2)*M_3*r0 + (-2601 + 2048*E_2)*M_2*r0_2 + 4*(89 + 148*E_2)*M*r0_3 - 48*r0_4) - 2*L_6*(832*M_4 + 12*(65 - 6*E_2)*M_3*r0 - 12*(16 + 3*E_2 + 6*E_4)*M_2*r0_2 - 2*(25 - 21*E_2 + 18*E_4)*M*r0_3 + 75*E_2*r0_4) + L_4*r0*(5088*M_5 - 4*(245 + 964*E_2)*M_4*r0 + 12*(-381 - 304*E_2 + 60*E_4)*M_3*r0_2 + (-41 - 504*E_2 + 1440*E_4)*M_2*r0_3 + 4*(93 + 62*E_2 + 135*E_4)*M*r0_4 + (93 - 638*E_2)*r0_5) + L_2*r0_3*(5664*M_5 + 12*(-275 - 384*E_2 + 144*E_4)*M_3*r0_2 + (-129 - 2086*E_2 + 2544*E_4)*M_2*r0_3 + 6*(46 + 185*E_2 + 140*E_4)*M*r0_4 + (54 - 733*E_2)*r0_5 + 156*M_4*(r0 + 6*E_2*r0)))))/(3.*r0_11*expr2_2);\n    A31240 = (-32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_4*E*L*r0_2) + 6*a_5*E_2*M*(2*M + r0) - 6*a*M*r0*(L_4 - (-1 + E_2)*L_2*r0_2 - 3*E_2*r0_4) + E*L*r0_3*(3*r0_3 + L_2*(4*M + r0)) + 2*a_3*M*(6*E_2*r0_2*(3*M + 2*r0) + L_2*(-6*M + (-3 + E_2)*r0)) + a_2*E*L*r0*(L_2*(4*M - r0) + 2*r0*(-6*M_2 + 4*M*r0 + r0_2))))/(r0_2*(a_2 + r0*(-2*M + r0)));\n    A31260 = (16*(4*a_13*E_2*M*(2*M + r0)*(24*(-7 + E_2)*M_3 + (101 + 24*E_2)*M_2*r0 + 6*(28 + E_2)*M*r0_2 + 36*r0_3) + a_12*E*L*M*(-384*(-7 + E_2)*M_4 - 128*(-27 + 10*E_2)*M_3*r0 + (1503 - 1144*E_2)*M_2*r0_2 + 3*(87 - 112*E_2)*M*r0_3 - 18*(-1 + E_2)*r0_4) + E*L*r0_6*(168*L_8*M*r0_2M_2 + 4*L_6*M*(2*M - r0)*r0_2*(220*M - 21*(7 + 3*E_2)*r0) + L_4*r0_4*(1856*M_3 - 4*(679 + 374*E_2)*M_2*r0 + 24*(38 + 33*E_2)*M*r0_2 - 9*r0_3) + 2*L_2*r0_6*(356*M_3 - 4*(172 + 195*E_2)*M_2*r0 + 3*(77 + 146*E_2)*M*r0_2 + 12*r0_3) + 2*r0_8*(-64*M_3 - 8*(-4 + 37*E_2)*M_2*r0 + 6*(-7 + 29*E_2)*M*r0_2 + 21*r0_3)) + a_2*E*L*r0_4*(168*L_8*M*(6*M_2 - 5*M*r0 + r0_2) + 2*L_6*M*r0*(968*M_3 + (676 - 672*E_2)*M_2*r0 - 2*(571 + 252*E_2)*M*r0_2 + (421 + 252*E_2)*r0_3) - 2*L_4*r0_3*(1192*M_4 + 4*(-352 + 421*E_2)*M_3*r0 + 2*(729 + 722*E_2)*M_2*r0_2 - (900 + 1127*E_2)*M*r0_3 + 12*r0_4) + L_2*r0_5*(-12672*M_4 - 12*(-647 + 88*E_2)*M_3*r0 - 4*(671 + 994*E_2)*M_2*r0_2 + (1467 + 3224*E_2)*M*r0_3 + 27*r0_4) + 2*r0_7*(-4788*M_4 + 8*(399 + 43*E_2)*M_3*r0 - (543 + 1154*E_2)*M_2*r0_2 + 5*(2 + 157*E_2)*M*r0_3 + 60*r0_4)) + 4*a_3*M*r0_3*(2*E_2*r0_8*(978*M_3 + (-631 + 486*E_2)*M_2*r0 + 2*(10 + 149*E_2)*M*r0_2 + 51*r0_3) + L_8*(-1168*M_3 + 12*(8 + 21*E_2)*M_2*r0 + 2*(218 + 21*E_2)*M*r0_2 - 9*(16 + 7*E_2)*r0_3) + L_6*r0*(4416*M_4 - 4*(925 + 404*E_2)*M_3*r0 + 2*(-519 + 290*E_2 + 126*E_4)*M_2*r0_2 + 4*(299 + 86*E_2 + 63*E_4)*M*r0_3 - 3*(93 + 142*E_2)*r0_4) + L_2*r0_5*(4608*M_4 - 12*(187 + 166*E_2)*M_3*r0 + 2*(-465 - 722*E_2 + 882*E_4)*M_2*r0_2 + (560 + 1717*E_2 + 1098*E_4)*M*r0_3 - 2*(42 + 313*E_2)*r0_4) - 2*L_4*r0_3*(-4416*M_4 + 24*(96 + 95*E_2)*M_3*r0 + (1026 + 173*E_2 - 648*E_4)*M_2*r0_2 - (639 + 801*E_2 + 443*E_4)*M*r0_3 + (105 + 457*E_2)*r0_4)) + a_4*E*L*r0_3*(4*L_6*M*(2216*M_3 + 12*(43 - 49*E_2)*M_2*r0 - 2*(76 + 63*E_2)*M*r0_2 + (68 + 63*E_2)*r0_3) + L_2*r0_3*(-59256*M_5 + 4*(1885 + 672*E_2)*M_4*r0 + 2*(13743 - 4196*E_2)*M_3*r0_2 - (1537 + 3516*E_2)*M_2*r0_3 + 438*(3 + 10*E_2)*M*r0_4 - 18*r0_5) - L_4*r0*(12576*M_5 + 16*(-886 + 227*E_2)*M_4*r0 + 8*(-1641 + 1225*E_2)*M_3*r0_2 + 4*(58 + 453*E_2)*M_2*r0_3 - 2*(468 + 1057*E_2)*M*r0_4 + 15*r0_5) + 2*r0_5*(-26472*M_5 + 24*(81 + 151*E_2)*M_4*r0 + 2*(4917 - 758*E_2)*M_3*r0_2 - 2*(723 + 904*E_2)*M_2*r0_3 + (133 + 1391*E_2)*M*r0_4 + 54*r0_5)) - 2*a_11*M*(L_2*(672*M_4 + 4*(449 + 96*E_2 + 12*E_4)*M_3*r0 + (1594 - 1063*E_2 + 48*E_4)*M_2*r0_2 + 6*(96 - 177*E_2 + 2*E_4)*M*r0_3 + 72*(1 - 3*E_2)*r0_4) - 4*E_2*r0*(544*(-3 + E_2)*M_5 + 8*(-591 + 151*E_2)*M_4*r0 + 3*(-1239 + 344*E_2)*M_3*r0_2 + 2*(-236 + 199*E_2)*M_2*r0_3 + 2*(190 + 29*E_2)*M*r0_4 + 87*r0_5)) + a_10*E*L*M*(L_2*(384*M_4 + 96*(44 + 5*E_2)*M_3*r0 + 2*(1719 - 224*E_2)*M_2*r0_2 + 3*(275 - 148*E_2)*M*r0_3 + 18*(3 - 2*E_2)*r0_4) + r0*(-512*(-51 + 8*E_2)*M_5 - 32*(-1524 + 329*E_2)*M_4*r0 - 50*(-627 + 248*E_2)*M_3*r0_2 + (7199 - 7568*E_2)*M_2*r0_3 + (329 - 1572*E_2)*M*r0_4 + 2*(22 + 53*E_2)*r0_5)) - 2*a_5*M*r0_2*(12*L_8*(2*M_2 + 11*M*r0 + 6*r0_2) - 2*E_2*r0_6*(10332*M_4 + 24*(85 + 117*E_2)*M_3*r0 + (-4857 + 3604*E_2)*M_2*r0_2 + 10*(-2 + 113*E_2)*M*r0_3 + 228*r0_4) + L_6*(5568*M_4 + 16*(349 - 45*E_2)*M_3*r0 - 4*(567 - 76*E_2 + 126*E_4)*M_2*r0_2 - 4*(86 - 201*E_2 + 63*E_4)*M*r0_3 + 6*(94 + 53*E_2)*r0_4) - L_2*r0_3*(21312*M_5 + 60*(10 + 67*E_2)*M_4*r0 + 4*(-3042 - 1595*E_2 + 1800*E_4)*M_3*r0_2 + (690 - 9047*E_2 + 9960*E_4)*M_2*r0_3 + 24*(43 + 130*E_4)*M*r0_4 - 4*(72 + 281*E_2)*r0_5) + L_4*r0*(-20160*M_5 + 24*(143 + 376*E_2)*M_4*r0 - 8*(-2141 - 830*E_2 + 342*E_4)*M_3*r0_2 - 2*(1087 - 2004*E_2 + 2488*E_4)*M_2*r0_3 - 2*(708 - 779*E_2 + 842*E_4)*M*r0_4 + (678 + 1249*E_2)*r0_5)) + a_6*E*L*r0_2*(6*L_6*M*(40*M_2 + 54*M*r0 + 3*r0_2) + 2*L_4*M*(12576*M_4 - 8*(-1887 + 674*E_2)*M_3*r0 + 4*(1433 - 814*E_2)*M_2*r0_2 + 4*(113 - 66*E_2)*M*r0_3 + (57 + 317*E_2)*r0_4) - L_2*r0*(59968*M_6 - 72*(-217 + 44*E_2)*M_5*r0 + 4*(-17777 + 3124*E_2)*M_4*r0_2 + 2*(-17037 + 7136*E_2)*M_3*r0_3 + (221 + 2012*E_2)*M_2*r0_4 - 3*(103 + 852*E_2)*M*r0_5 + 21*r0_6) + r0_3*(-99448*M_6 + 4*(-13813 + 2608*E_2)*M_5*r0 + 2*(27917 + 1752*E_2)*M_4*r0_2 + (25417 - 12128*E_2)*M_3*r0_3 - 3*(1013 + 1192*E_2)*M_2*r0_4 + 12*(12 + 199*E_2)*M*r0_5 + 24*r0_6)) - 2*a_9*M*(2*L_4*(48*M_4 + 8*(77 + 27*E_2)*M_3*r0 + (1141 + 60*E_2 + 24*E_4)*M_2*r0_2 + 3*(214 - 105*E_2 + 4*E_4)*M*r0_3 - 108*(-1 + E_2)*r0_4) + L_2*r0*(384*(17 + 18*E_2)*M_5 - 32*(-171 - 523*E_2 + 129*E_4)*M_4*r0 + 4*(192 + 5491*E_2 - 1488*E_4)*M_3*r0_2 + (1250 + 8149*E_2 - 2808*E_4)*M_2*r0_3 - 2*(-598 + 627*E_2 + 216*E_4)*M*r0_4 + 12*(20 - 49*E_2)*r0_5) - 4*E_2*r0_2*(3936*M_6 + 4*(1771 + 216*E_2)*M_5*r0 + 2*(-955 + 1492*E_2)*M_4*r0_2 + (-8261 + 3360*E_2)*M_3*r0_3 + 12*(-256 + 131*E_2)*M_2*r0_4 + (359 + 265*E_2)*M*r0_5 + 168*r0_6)) - 2*a_7*M*r0*(8*L_6*(12*M_3 + (89 + 27*E_2)*M_2*r0 + 3*(35 - 2*E_2)*M*r0_2 - 9*(-3 + E_2)*r0_3) - 12*E_2*r0_4*(2652*M_5 + 8*(323 + 75*E_2)*M_4*r0 + (-1463 + 1256*E_2)*M_3*r0_2 + (-1411 + 836*E_2)*M_2*r0_3 + 12*(2 + 15*E_2)*M*r0_4 + 58*r0_5) + 2*L_4*(2240*M_5 + 48*(133 + 45*E_2)*M_4*r0 - 4*(-341 - 936*E_2 + 322*E_4)*M_3*r0_2 - 2*(325 - 1723*E_2 + 584*E_4)*M_2*r0_3 + (694 + 577*E_2 - 250*E_4)*M*r0_4 + 12*(25 - 2*E_2)*r0_5) + L_2*r0*(-15744*M_6 - 16*(557 + 184*E_2)*M_5*r0 + 4*(3682 + 385*E_2 - 1224*E_4)*M_4*r0_2 + 4*(1543 + 6142*E_2 - 3300*E_4)*M_3*r0_3 + (-1686 + 18007*E_2 - 9288*E_4)*M_2*r0_4 - 8*(-40 - 150*E_2 + 243*E_4)*M*r0_5 + 4*(84 - 23*E_2)*r0_6)) + a_8*E*L*r0*(6*L_4*M*(112*M_3 + 8*(46 + 7*E_2)*M_2*r0 - 2*(-74 + 9*E_2)*M*r0_2 - 3*(-3 + E_2)*r0_3) + L_2*M*(22528*M_5 - 32*(-1871 + 426*E_2)*M_4*r0 - 8*(-6691 + 1994*E_2)*M_3*r0_2 - 22*(-771 + 356*E_2)*M_2*r0_3 + (1013 - 1356*E_2)*M*r0_4 + 2*(27 + 244*E_2)*r0_5) + r0*(-62976*M_7 + 64*(-1164 + 107*E_2)*M_6*r0 + 212*(193 + 8*E_2)*M_5*r0_2 - 8*(-8869 + 1698*E_2)*M_4*r0_3 + (17573 - 14832*E_2)*M_3*r0_4 - (1229 + 2912*E_2)*M_2*r0_5 + 8*(1 + 119*E_2)*M*r0_6 - 6*r0_7)) - 2*a*M*r0_4*(84*L_10*r0_2M_2 - 2*L_8*(2*M - r0)*r0*(336*M_2 - 2*(155 + 21*E_2)*M*r0 + 9*(12 + 7*E_2)*r0_2) + 2*L_6*r0_3*(-1872*M_3 + 4*(470 + 187*E_2)*M_2*r0 - 2*(341 + 385*E_2 + 63*E_4)*M*r0_2 + 21*(5 + 11*E_2)*r0_3) + 2*L_2*r0_7*(-624*M_3 + 2*(302 + 691*E_2)*M_2*r0 - (194 + 1021*E_2 + 288*E_4)*M*r0_2 + 12*(2 + 17*E_2)*r0_3) + L_4*r0_5*(-3648*M_3 + 32*(106 + 117*E_2)*M_2*r0 - 2*(518 + 1539*E_2 + 306*E_4)*M*r0_2 + 9*(14 + 83*E_2)*r0_3) - 12*E_2*r0_10*(-18*M_2 + 4*r0_2 + M*(r0 + 21*E_2*r0)))))/(3.*r0_8*expr2_2);\n    A31420 = (-8*r0*(6*a_5*E_2*M*(2*M + r0) - a_4*E*L*r0*(3*M + 2*r0) - 6*a*M*r0*(L_4 + (1 - 2*E_2)*L_2*r0_2 - 3*E_2*r0_4) + E*L*r0_3*(3*r0_3 + 2*L_2*(M + r0)) + 2*a_3*M*(6*E_2*r0_2*(3*M + 2*r0) + L_2*(-6*M - 3*r0 + 2*E_2*r0)) + a_2*E*L*r0*(2*L_2*(M - r0) + r0*(-6*M_2 + 7*M*r0 + r0_2))))/(a_2 + r0*(-2*M + r0));\n    A31440 = (4*(2*a_11*E_2*M*(144*(-5 + E_2)*M_3 + (437 + 144*E_2)*M_2*r0 + 36*(24 + E_2)*M*r0_2 + 231*r0_3) + a_10*E*L*M*(-576*(-5 + E_2)*M_3 + 16*(162 - 59*E_2)*M_2*r0 + 3*(231 - 160*E_2)*M*r0_2 + 3*(21 - 22*E_2)*r0_3) + E*L*r0_5*(8*L_6*M*(104*M_2 - 106*M*r0 + 27*r0_2) + 2*L_4*r0_2*(728*M_3 - 2*(512 + 157*E_2)*M_2*r0 + (352 + 159*E_2)*M*r0_2 - 11*r0_3) - 2*L_2*r0_4*(-324*M_3 + 18*(43 + 36*E_2)*M_2*r0 - 2*(154 + 177*E_2)*M*r0_2 + r0_3) + r0_6*(-336*M_3 - 24*(-7 + 31*E_2)*M_2*r0 + 2*(-59 + 216*E_2)*M*r0_2 + 59*r0_3)) - 2*a*M*r0_3*(3*E_2*r0_8*(48*M_2 + 2*(29 - 54*E_2)*M*r0 - 41*r0_2) + 4*L_8*(104*M_2 - 106*M*r0 + 27*r0_2) + L_6*r0*(-2064*M_3 + 4*(641 + 28*E_2)*M_2*r0 - 4*(317 + 80*E_2)*M*r0_2 + (251 + 138*E_2)*r0_3) + L_2*r0_5*(-2016*M_3 + 12*(172 + 209*E_2)*M_2*r0 - 2*(377 + 939*E_2 + 252*E_4)*M*r0_2 + (113 + 402*E_2)*r0_3) + L_4*r0_3*(-3984*M_3 + 24*(170 + 71*E_2)*M_2*r0 - 2*(769 + 798*E_2 + 144*E_4)*M*r0_2 + (247 + 462*E_2)*r0_3)) - a_2*E*L*r0_3*(-2*L_6*M*(592*M_2 - 482*M*r0 + 103*r0_2) + r0_5*(12936*M_4 - 12*(563 + 102*E_2)*M_3*r0 + 2*(101 + 1368*E_2)*M_2*r0_2 + (5 - 1518*E_2)*M*r0_3 - 118*r0_4) + 2*L_4*r0*(480*M_4 + 4*(-181 + 256*E_2)*M_3*r0 + 16*(37 + 26*E_2)*M_2*r0_2 - (403 + 305*E_2)*M*r0_3 + 6*r0_4) + L_2*r0_3*(11304*M_4 + 4*(-1127 + 438*E_2)*M_3*r0 + 26*(15 + 94*E_2)*M_2*r0_2 - (1153 + 1912*E_2)*M*r0_3 + 23*r0_4)) - 2*a_9*M*(L_2*(720*M_3 + (1733 + 336*E_2 + 48*E_4)*M_2*r0 + 24*(48 - 38*E_2 + E_4)*M*r0_2 + 3*(77 - 155*E_2)*r0_3) - E_2*r0*(1344*(-3 + E_2)*M_4 + 4*(-3055 + 684*E_2)*M_3*r0 + (-6107 + 1872*E_2)*M_2*r0_2 + 2*(793 + 210*E_2)*M*r0_3 + 965*r0_4)) - 2*a_3*M*r0_2*(12*L_8*M - E_2*r0_6*(5184*M_3 + 72*(-29 + 24*E_2)*M_2*r0 + 4*(-274 + 321*E_2)*M*r0_2 + 581*r0_3) + 2*L_6*(1008*M_3 - 36*(-3 + 10*E_2)*M_2*r0 + 2*(-316 + 25*E_2)*M*r0_2 + (241 + 72*E_2)*r0_3) + 2*L_2*r0_3*(-4824*M_4 + 12*(204 + 109*E_2)*M_3*r0 + 2*(503 + 13*E_2 - 540*E_4)*M_2*r0_2 - (879 + 191*E_2 + 708*E_4)*M*r0_3 + 2*(117 + 136*E_2)*r0_4) + L_4*r0*(-9360*M_4 + 24*(251 + 122*E_2)*M_3*r0 - 16*(-155 + 78*E_2 + 45*E_4)*M_2*r0_2 - 4*(699 - 64*E_2 + 130*E_4)*M*r0_3 + (863 + 592*E_2)*r0_4)) + a_8*E*L*(3*L_2*M*(192*M_3 + 16*(63 + 10*E_2)*M_2*r0 + 4*(110 - 3*E_2)*M*r0_2 + (41 - 20*E_2)*r0_3) + r0*(-768*(-21 + 4*E_2)*M_5 + 16*(1548 - 485*E_2)*M_4*r0 + 2*(5797 - 3244*E_2)*M_3*r0_2 + (1553 - 1912*E_2)*M_2*r0_3 + 2*(65 + 12*E_2)*M*r0_4 + r0_5)) + a_4*E*L*r0_2*(6*L_6*M*(20*M + r0) + 2*L_4*(4032*M_4 - 4*(-423 + 388*E_2)*M_3*r0 + 2*(131 - 25*E_2)*M_2*r0_2 + (116 + 149*E_2)*M*r0_3 + 5*r0_4) + L_2*r0*(-25248*M_5 + 16*(61 - 120*E_2)*M_4*r0 + 4*(4271 - 1696*E_2)*M_3*r0_2 + 12*(187 - 113*E_2)*M_2*r0_3 + (543 + 1640*E_2)*M*r0_4 - 10*r0_5) + r0_3*(-46632*M_5 + 12*(-353 + 492*E_2)*M_4*r0 + 2*(9973 - 1764*E_2)*M_3*r0_2 + (11 - 4032*E_2)*M_2*r0_3 + 12*(9 + 158*E_2)*M*r0_4 + 60*r0_5)) - 2*a_7*M*(2*L_4*(72*M_3 + 8*(53 + 27*E_2)*M_2*r0 + 12*(57 + 5*E_2 + E_4)*M*r0_2 + 3*(77 - 38*E_2)*r0_3) + 2*L_2*r0*(288*(7 + 6*E_2)*M_4 + 2*(41 + 1242*E_2 - 504*E_4)*M_3*r0 + (-307 + 4030*E_2 - 888*E_4)*M_2*r0_2 + (823 + 406*E_2 - 180*E_4)*M*r0_3 + 2*(176 - 295*E_2)*r0_4) - E_2*r0_2*(10944*M_5 + 12*(1315 + 144*E_2)*M_4*r0 + 8*(-1103 + 702*E_2)*M_3*r0_2 + 5*(-3001 + 1008*E_2)*M_2*r0_3 + 24*(-17 + 57*E_2)*M*r0_4 + 1572*r0_5)) + 2*a_5*M*r0*(-3*L_6*(32*M_2 + 4*(19 + 9*E_2)*M*r0 + (77 + 2*E_2)*r0_2) + E_2*r0_4*(15660*M_4 + 252*(31 + 12*E_2)*M_3*r0 + 5*(-2033 + 1008*E_2)*M_2*r0_2 + 108*(-19 + 18*E_2)*M*r0_3 + 1296*r0_4) - 2*L_4*(1248*M_4 + 2260*M_3*r0 - 4*(305 - 120*E_2 + 82*E_4)*M_2*r0_2 - 2*(19 - 441*E_2 + 52*E_4)*M*r0_3 - 49*(-11 + E_2)*r0_4) + L_2*r0*(10944*M_5 - 12*(-29 + 92*E_2)*M_4*r0 + 4*(-1955 + 244*E_2 + 576*E_4)*M_3*r0_2 + (1991 - 5956*E_2 + 3984*E_4)*M_2*r0_3 + 2*(255 - 1600*E_2 + 648*E_4)*M*r0_4 + 3*(-276 + 191*E_2)*r0_5)) + a_6*E*L*r0*(6*L_4*M*(112*M_2 + 2*(65 + 14*E_2)*M*r0 + (11 + E_2)*r0_2) + L_2*(12288*M_5 - 48*(-443 + 168*E_2)*M_4*r0 - 8*(-1748 + 573*E_2)*M_3*r0_2 + 2*(1259 - 122*E_2)*M_2*r0_3 + (189 + 376*E_2)*M*r0_4 + 11*r0_5) + r0*(-43776*M_6 + 96*(-336 + 55*E_2)*M_5*r0 - 44*(-673 + 48*E_2)*M_4*r0_2 + 8*(2774 - 1247*E_2)*M_3*r0_3 - 7*(-143 + 496*E_2)*M_2*r0_4 + 2*(31 + 450*E_2)*M*r0_5 + 2*r0_6))))/(3.*r0_5*expr2_2);\n    A31600 = (-2*r0_4*(2*a_3*E_2*M - a_2*E*L*r0 + E*L*r0_3 - 2*a*M*(L_2 - 3*E_2*r0_2)))/(a_2 + r0*(-2*M + r0));\n    A31620 = (12*a_9*E_2*M*(4*(-13 + 3*E_2)*M_2 + 2*(31 + 3*E_2)*M*r0 + 41*r0_2) - 3*a_8*E*L*(32*(-13 + 3*E_2)*M_3 + 96*(-2 + E_2)*M_2*r0 + (-9 + 26*E_2)*M*r0_2 + 3*r0_3) + E*L*r0_4*(16*L_4*M*(32*M_2 - 34*M*r0 + 9*r0_2) + L_2*r0_2*(408*M_3 - 4*(207 + 86*E_2)*M_2*r0 + 2*(167 + 90*E_2)*M*r0_2 - 11*r0_3) + 4*r0_4*(-80*M_3 - 20*(-2 + 5*E_2)*M_2*r0 + (-20 + 57*E_2)*M*r0_2 + 10*r0_3)) - 2*a_7*M*(3*L_2*(104*M_2 + 4*(55 + E_4)*M*r0 + (82 - 93*E_2)*r0_2) - 4*E_2*r0*(88*(-3 + E_2)*M_3 + (-947 + 178*E_2)*M_2*r0 + 4*(-23 + 19*E_2)*M*r0_2 + 202*r0_3)) - 2*a*M*r0_2*(4*L_6*(52*M_2 - 56*M*r0 + 15*r0_2) - 6*E_2*r0_6*(4*M_2 + 2*(-22 + 15*E_2)*M*r0 + 21*r0_2) + 2*L_4*r0*(-720*M_3 + 4*(204 + 19*E_2)*M_2*r0 - 2*(185 + 52*E_2)*M*r0_2 + (71 + 39*E_2)*r0_3) + L_2*r0_3*(-1440*M_3 + 4*(386 + 241*E_2)*M_2*r0 - 16*(39 + 47*E_2 + 9*E_4)*M*r0_2 + (106 + 177*E_2)*r0_3)) + a_6*E*L*(3*L_2*(96*M_3 + 8*(24 + 5*E_2)*M_2*r0 + 4*(1 - 2*E_2)*M*r0_2 - 3*r0_3) - r0*(128*(-33 + 8*E_2)*M_4 + 8*(-684 + 287*E_2)*M_3*r0 + 46*(-31 + 22*E_2)*M_2*r0_2 + (17 + 30*E_2)*M*r0_3 + 31*r0_4)) + a_2*E*L*r0_2*(8*L_4*M*(74*M_2 - 55*M*r0 + 14*r0_2) + L_2*r0*(-2992*M_4 + 16*(39 - 58*E_2)*M_3*r0 - 392*(-1 + E_2)*M_2*r0_2 + 4*(81 + 80*E_2)*M*r0_3 - 15*r0_4) + r0_3*(-7480*M_4 + 4*(673 + 200*E_2)*M_3*r0 + 2*(367 - 714*E_2)*M_2*r0_2 + (-31 + 582*E_2)*M*r0_3 + 43*r0_4)) - 2*a_5*M*(6*L_4*(12*M_2 + 6*(5 + 3*E_2)*M*r0 + (41 - 4*E_2)*r0_2) + L_2*r0*(96*(11 + 6*E_2)*M_3 - 4*(263 - 44*E_2 + 82*E_4)*M_2*r0 + 8*(11 + 155*E_2 - 13*E_4)*M*r0_2 + (562 - 469*E_2)*r0_3) - 4*E_2*r0_2*(840*M_4 + 2*(473 + 36*E_2)*M_3*r0 + 2*(-446 + 143*E_2)*M_2*r0_2 + (-491 + 170*E_2)*M*r0_3 + 245*r0_4)) - 2*a_3*M*r0*(24*L_6*M - 4*E_2*r0_4*(666*M_3 + (-37 + 126*E_2)*M_2*r0 + 4*(-90 + 37*E_2)*M*r0_2 + 136*r0_3) + 2*L_4*(272*M_3 + 12*(14 - 15*E_2)*M_2*r0 + 16*(-27 + 4*E_2)*M*r0_2 + (194 + 27*E_2)*r0_3) + L_2*r0*(-3360*M_4 + 104*(17 + 10*E_2)*M_3*r0 - 4*(-209 + 255*E_2 + 90*E_4)*M_2*r0_2 - 4*(281 - 166*E_2 + 65*E_4)*M*r0_3 + (422 - 13*E_2)*r0_4)) + a_4*E*L*r0*(24*L_4*M*(7*M - r0) + L_2*(2560*M_4 - 8*(-237 + 194*E_2)*M_3*r0 + 4*(289 + 10*E_2)*M_2*r0_2 + 2*(17 + 58*E_2)*M*r0_3 - 13*r0_4) - r0*(13440*M_5 + 16*(252 - 101*E_2)*M_4*r0 + 4*(-1827 + 422*E_2)*M_3*r0_2 + 8*(-194 + 219*E_2)*M_2*r0_3 + (43 - 402*E_2)*M*r0_4 + 19*r0_5)))/(3.*r0_2*expr2_2);\n    A31800 = (r0*(6*a_7*E_2*M*(4*(-4 + E_2)*M + 29*r0) - 3*a_6*E*L*(16*(-4 + E_2)*M_2 + 10*E_2*M*r0 + 3*r0_2) - 2*a_5*M*(L_2*(48*M + 87*r0 - 30*E_2*r0) + E_2*r0*(-32*(-3 + E_2)*M_2 + (484 - 76*E_2)*M*r0 - 203*r0_2)) + E*L*r0_3*(16*L_2*M*(10*M_2 - 11*M*r0 + 3*r0_2) + r0_2*(-104*M_3 + (52 - 76*E_2)*M_2*r0 + 2*(-11 + 21*E_2)*M*r0_2 + 11*r0_3)) - a_4*E*L*(-6*L_2*M*(8*M - 5*r0) + r0*(128*(-3 + E_2)*M_3 + 20*(-24 + 11*E_2)*M_2*r0 + 2*(11 + E_2)*M*r0_2 + 23*r0_3)) + 2*a_3*M*(-12*L_4*M + 2*L_2*r0*(-48*M_2 + 2*(61 + 8*E_2)*M*r0 - 61*r0_2) + E_2*r0_2*(384*M_3 + 332*M_2*r0 + 4*(-128 + 25*E_2)*M*r0_2 + 161*r0_3)) + 2*a*M*r0*(-4*L_4*(8*M_2 - 10*M*r0 + 3*r0_2) + 9*E_2*r0_4*(4*M_2 + 4*(-3 + E_2)*M*r0 + 5*r0_2) + L_2*r0*(384*M_3 - 4*(107 + 40*E_2)*M_2*r0 + 4*(47 + 32*E_2)*M*r0_2 - 5*(7 + 6*E_2)*r0_3)) + a_2*E*L*r0*(2*L_2*M*(64*M_2 - 38*M*r0 + 13*r0_2) + r0*(-1536*M_4 + 32*(6 + 5*E_2)*M_3*r0 - 140*(-3 + 2*E_2)*M_2*r0_2 + 2*(-6 + 35*E_2)*M*r0_3 - 3*r0_4))))/(12.*expr2_2);\n    A32040 = (-192*E*L*expr1_2)/(a_2 + r0*(-2*M + r0));\n    A32060 = (32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-24*a_9*E_2*M*(4*M_3 + 4*M_2*r0 - M*r0_2 - r0_3) + a_8*E*L*M*(192*M_3 + 24*(8 + E_2)*M_2*r0 + (49 - 12*E_2)*M*r0_2 + 6*(1 - 2*E_2)*r0_3) + E*L*r0_6*(-4*L_4*M*(71*M - 37*r0) + 2*L_2*r0_2*(-162*M_2 + (45 + 16*E_2)*M*r0 + 21*r0_2) + r0_4*(-52*M_2 + 2*(-23 + 20*E_2)*M*r0 + 39*r0_2)) + 2*a_6*E*L*r0*(-1536*M_5 + 208*(-6 + E_2)*M_4*r0 + 24*(7 + 20*E_2)*M_3*r0_2 + 6*(32 + 35*E_2)*M_2*r0_3 + (13 + 2*E_2)*M*r0_4 - 3*r0_5 + L_2*M*(60*M_2 + 4*(2 - 3*E_2)*M*r0 + 3*(1 - 2*E_2)*r0_2)) + 4*a*M*r0_3*(3*E_2*r0_7*(-17*M + 15*r0) + 3*L_6*(44*M_2 - 17*M*r0 - 3*r0_2) + L_4*r0_2*(192*M_2 + 2*(-27 + 8*E_2)*M*r0 + 3*(-8 + 3*E_2)*r0_2) + L_2*r0_4*(60*M_2 - (3 + 85*E_2)*M*r0 + 15*(-1 + 5*E_2)*r0_2)) + 2*a_2*E*L*r0_3*(-2*L_4*M*(220*M_2 + 9*M*r0 - 37*r0_2) + L_2*r0_2*(-1006*M_3 + (-15 + 76*E_2)*M_2*r0 + (177 + 26*E_2)*M*r0_2 + 18*r0_3) + r0_4*(-494*M_3 + 3*(-3 + 98*E_2)*M_2*r0 + 3*(17 + 18*E_2)*M*r0_2 + 36*r0_3)) + 4*a_3*M*r0_2*(-6*L_6*r0 + 3*E_2*r0_5*(36*M_2 + 9*M*r0 + 37*r0_2) + 2*L_4*(228*M_3 + 2*(3 + 11*E_2)*M_2*r0 + 2*(-24 + 7*E_2)*M*r0_2 + 9*(-2 + E_2)*r0_3) + L_2*r0_2*(312*M_3 - 4*(-9 + 7*E_2)*M_2*r0 - (60 + E_2)*M*r0_2 + (-36 + 121*E_2)*r0_3)) + 4*a_7*M*(-(L_2*(24*M_3 + 24*(1 + E_2)*M_2*r0 + (18 - 17*E_2)*M*r0_2 + 3*(2 - 5*E_2)*r0_3)) + 3*E_2*r0*(128*M_4 + 156*M_3*r0 + 24*M_2*r0_2 + 7*M*r0_3 + 11*r0_4)) + a_4*E*L*r0_2*(-24*L_4*M_2 - 2*L_2*(1616*M_4 + 8*(64 - 11*E_2)*M_3*r0 - (239 + 64*E_2)*M_2*r0_2 - (87 + 4*E_2)*M*r0_3 + 3*r0_4) + r0_2*(-3700*M_4 + 4*(-245 + 306*E_2)*M_3*r0 + 3*(183 + 340*E_2)*M_2*r0_2 + 84*(2 + E_2)*M*r0_3 + 27*r0_4)) + 4*a_5*M*r0*(-3*L_4*(4*M_2 + (6 - 4*E_2)*M*r0 + (4 - 3*E_2)*r0_2) + 3*E_2*r0_3*(204*M_3 + 108*M_2*r0 + 31*M*r0_2 + 31*r0_3) + L_2*(384*M_4 + 4*(39 + 62*E_2)*M_3*r0 + 4*(-21 + E_2)*M_2*r0_2 + (-75 + 13*E_2)*M*r0_3 + (-27 + 61*E_2)*r0_4))))/(3.*r0_7*expr2_2);\n    A32220 = (-96*E*L*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(a_2 + r0*(-2*M + r0));\n    A32240 = (8*(-12*a_9*E_2*M*(24*M_3 + 36*M_2*r0 + 10*M*r0_2 - r0_3) + a_8*E*L*(576*M_4 + 144*(4 + E_2)*M_3*r0 + (193 + 72*E_2)*M_2*r0_2 + 42*M*r0_3 + 6*r0_4) + E*L*r0_4*(4*L_6*M*(-2*M + r0) + 12*L_2*r0_4*(-90*M_2 + 2*(13 + 3*E_2)*M*r0 + 11*r0_2) + 2*L_4*r0_2*(-400*M_2 + (185 - 2*E_2)*M*r0 + 12*r0_2) + 3*r0_6*(-100*M_2 + 14*(-1 + 2*E_2)*M*r0 + 35*r0_2)) + 4*a*M*r0_3*(9*E_2*r0_7*(-11*M + 12*r0) + L_6*(360*M_2 + (-177 + 4*E_2)*M*r0 - 6*r0_2) + 3*L_4*r0_2*(192*M_2 + (-86 + 3*E_2)*M*r0 + (-8 + 15*E_2)*r0_2) + 3*L_2*r0_4*(72*M_2 - 3*(9 + 16*E_2)*M*r0 + 2*(-3 + 29*E_2)*r0_2)) + 2*a_6*E*L*r0*(-4608*M_5 + 48*(-78 + 7*E_2)*M_4*r0 + 2*(199 + 468*E_2)*M_3*r0_2 + 7*(101 + 72*E_2)*M_2*r0_3 + 3*(39 + 14*E_2)*M*r0_4 + 6*r0_5 + L_2*(264*M_3 + 2*(73 + 9*E_2)*M_2*r0 + 39*M*r0_2 + 6*r0_3)) - 2*a_2*E*L*r0_3*(L_4*(1296*M_3 + 4*(-24 + E_2)*M_2*r0 + (-221 + 2*E_2)*M*r0_2 + 9*r0_3) - 3*L_2*r0_2*(-1128*M_3 + 6*(9 + 17*E_2)*M_2*r0 + (197 + 24*E_2)*M*r0_2 + 19*r0_3) - 3*r0_4*(-588*M_3 + 2*(-5 + 96*E_2)*M_2*r0 + (85 + 42*E_2)*M*r0_2 + 35*r0_3)) - 4*a_3*M*r0_2*(3*L_6*(2*M + r0) - 9*E_2*r0_5*(36*M_2 + 19*M*r0 + 29*r0_2) + L_2*r0_2*(-1008*M_3 - 6*(-6 + 71*E_2)*M_2*r0 + (228 - 97*E_2)*M*r0_2 + (39 - 223*E_2)*r0_3) - 3*L_4*(432*M_3 + 4*(-7 + 18*E_2)*M_2*r0 + (-102 + E_2)*M*r0_2 - (10 + E_2)*r0_3)) - 4*a_7*M*(L_2*(72*M_3 + 12*(3 + 10*E_2)*M_2*r0 + (6 + 79*E_2)*M*r0_2 + 3*(1 + E_2)*r0_3) - 3*E_2*r0*(384*M_4 + 492*M_3*r0 + 128*M_2*r0_2 + 15*M*r0_3 + 17*r0_4)) + a_4*E*L*r0_2*(6*L_4*(14*M_2 + 6*M*r0 + r0_2) + L_2*(-9696*M_4 + 8*(-403 + 36*E_2)*M_3*r0 + 8*(251 + 63*E_2)*M_2*r0_2 + 24*(26 + 3*E_2)*M*r0_3 - 6*r0_4) + 3*r0_2*(-3620*M_4 + 4*(-299 + 192*E_2)*M_3*r0 + (635 + 696*E_2)*M_2*r0_2 + 4*(62 + 21*E_2)*M*r0_3 + 37*r0_4)) - 4*a_5*M*r0*(-3*E_2*r0_3*(540*M_3 + 360*M_2*r0 + 115*M*r0_2 + 67*r0_3) + L_2*(-1152*M_4 - 12*(33 + 80*E_2)*M_3*r0 + 2*(174 - 211*E_2)*M_2*r0_2 + (153 + 82*E_2)*M*r0_3 + 2*(12 - 23*E_2)*r0_4) + 6*L_4*(8*M_2 + (1 + E_2)*r0_2 + 2*M*(r0 + 2*E_2*r0)))))/(3.*r0_4*expr2_2);\n    A32400 = (-12*E*L*r0_6)/(a_2 + r0*(-2*M + r0));\n    A32420 = (2*(-24*a_7*E_2*M*(6*M_2 + 9*M*r0 + 2*r0_2) + 3*a_6*E*L*(96*M_3 + 12*(4 + 3*E_2)*M_2*r0 + 2*(5 + 6*E_2)*M*r0_2 + 3*r0_3) + a_4*E*L*r0*(-4608*M_4 + 48*(-30 + E_2)*M_3*r0 + 6*(167 + 68*E_2)*M_2*r0_2 + 2*(157 + 60*E_2)*M*r0_3 + 43*r0_4 + 3*L_2*(68*M_2 + 4*(5 + 2*E_2)*M*r0 + 3*r0_2)) + E*L*r0_3*(24*L_4*M*(-2*M + r0) + L_2*r0_2*(-744*M_2 + 268*M*r0 + 61*r0_2) + r0_4*(-444*M_2 + 2*(17 + 24*E_2)*M*r0 + 103*r0_2)) + 12*a*M*r0_2*(3*E_2*r0_5*(-5*M + 9*r0) + L_4*(108*M_2 + (-67 + 8*E_2)*M*r0 + (5 - 4*E_2)*r0_2) + L_2*r0_2*(84*M_2 + (-51 + 4*E_2)*M*r0 + (3 + 14*E_2)*r0_2)) + 12*a_5*M*(-2*L_2*(6*M_2 + (-3 + 11*E_2)*M*r0 + 2*(-1 + 3*E_2)*r0_2) + E_2*r0*(192*M_3 + 162*M_2*r0 + 17*M*r0_2 + 5*r0_3)) + a_2*E*L*r0_2*(24*L_4*M + L_2*(-2544*M_3 + 12*(31 - 4*E_2)*M_2*r0 + 24*(18 + E_2)*M*r0_2 + 10*r0_3) + r0_2*(-3204*M_3 + 2*(103 + 222*E_2)*M_2*r0 + 6*(79 + 22*E_2)*M*r0_2 + 137*r0_3)) + 12*a_3*M*r0*(-4*L_4*(M + (-1 + E_2)*r0) + 6*E_2*r0_3*(23*M_2 + 8*M*r0 + 6*r0_2) + L_2*(192*M_3 + 2*(-21 + 50*E_2)*M_2*r0 + (-47 + 2*E_2)*M*r0_2 + 7*(1 - 2*E_2)*r0_3))))/(3.*r0*expr2_2);\n    A32600 = (r0_2*(-12*a_5*E_2*M*(2*M + 3*r0) + 3*a_4*E*L*(16*M_2 + 8*E_2*M*r0 + r0_2) + 2*a_2*E*L*r0*(12*L_2*M - 384*M_3 + 4*(18 - 5*E_2)*M_2*r0 + 7*(7 + 2*E_2)*M*r0_2 + 14*r0_3) + 12*a_3*M*(L_2*(-2*M + (3 - 4*E_2)*r0) + E_2*r0*(32*M_2 + 13*M*r0 + r0_2)) + E*L*r0_2*(20*L_2*M*(-2*M + r0) + r0_2*(-196*M_2 + 2*(15 + 2*E_2)*M*r0 + 37*r0_2)) + 4*a*M*r0*(3*E_2*r0_3*(M + 6*r0) + L_2*(96*M_2 + 5*(-15 + 4*E_2)*M*r0 - 12*(-1 + E_2)*r0_2))))/(6.*expr2_2);\n    A33040 = (-32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_4*E*L*r0_2) + 6*a_5*E_2*M*(2*M + r0) - 6*a*M*r0*(L_4 - (-1 + E_2)*L_2*r0_2 - 3*E_2*r0_4) + E*L*r0_3*(L_2*(3*M + 2*r0) + r0_2*(-M + 4*r0)) + 2*a_3*M*(6*E_2*r0_2*(3*M + 2*r0) + L_2*(-6*M + (-3 + E_2)*r0)) + a_2*E*L*r0*(L_2*(4*M - r0) + r0*(-14*M_2 + 9*M*r0 + 3*r0_2))))/(r0_2*expr2_2);\n    A33060 = (16*(-2*a_13*E_2*M*(2*M + r0)*(24*(-1 + E_2)*M_3 + (-253 + 36*E_2)*M_2*r0 + 12*(-7 + E_2)*M*r0_2 + 24*r0_3) + a_12*E*L*M*(192*(-1 + 2*E_2)*M_4 - 8*(120 + 73*E_2)*M_3*r0 - 4*(198 + 259*E_2)*M_2*r0_2 - 3*(61 + 104*E_2)*M*r0_3 + 6*(-1 + E_2)*r0_4) + E*L*r0_6*(168*L_8*M*r0_2M_2 + 2*L_6*M*r0_2*(908*M_2 - 14*(73 + 18*E_2)*M*r0 + (281 + 126*E_2)*r0_2) + L_4*r0_4*(1976*M_3 - 2*(1337 + 776*E_2)*M_2*r0 + (861 + 818*E_2)*M*r0_2 - 18*r0_3) + L_2*r0_6*(604*M_3 - 52*(20 + 31*E_2)*M_2*r0 + (285 + 904*E_2)*M*r0_2 + 33*r0_3) + r0_8*(-180*M_3 + 2*(95 - 294*E_2)*M_2*r0 + 2*(-73 + 175*E_2)*M*r0_2 + 45*r0_3)) + 2*a*M*r0_4*(-84*L_10*r0_2M_2 + 3*E_2*r0_10*(-180*M_2 + 2*(61 + 36*E_2)*M*r0 - 15*r0_2) + 2*L_6*r0_3*(1932*M_3 - 2*(847 + 493*E_2)*M_2*r0 + (373 + 1014*E_2 + 126*E_4)*M*r0_2 - 291*E_2*r0_3) + L_4*r0_5*(3816*M_3 - 4*(797 + 1375*E_2)*M_2*r0 + 2*(278 + 2417*E_2 + 342*E_4)*M*r0_2 + 3*(17 - 393*E_2)*r0_3) + L_2*r0_7*(1320*M_3 - 4*(296 + 957*E_2)*M_2*r0 + 2*(131 + 1562*E_2 + 306*E_4)*M*r0_2 + 3*(1 - 226*E_2)*r0_3) + 2*L_8*r0*(684*M_3 - 4*(215 + 21*E_2)*M_2*r0 + 2*(197 + 84*E_2)*M*r0_2 - 3*(22 + 21*E_2)*r0_3)) + a_2*E*L*r0_4*(168*L_8*M*(6*M_2 - 5*M*r0 + r0_2) + 2*L_6*M*r0*(1144*M_3 + (908 - 672*E_2)*M_2*r0 - 8*(167 + 63*E_2)*M*r0_2 + 3*(139 + 84*E_2)*r0_3) + L_4*r0_3*(-480*M_4 - 8*(-639 + 335*E_2)*M_3*r0 - 16*(282 + 247*E_2)*M_2*r0_2 + (1539 + 2384*E_2)*M*r0_3 + 15*r0_4) + L_2*r0_5*(-12728*M_4 + 4*(3143 + 960*E_2)*M_3*r0 - 2*(2269 + 3524*E_2)*M_2*r0_2 + (825 + 3392*E_2)*M*r0_3 + 105*r0_4) + r0_7*(-9744*M_4 + 24*(339 + 74*E_2)*M_3*r0 - 4*(415 + 671*E_2)*M_2*r0_2 + 9*(-27 + 176*E_2)*M*r0_3 + 141*r0_4)) + 2*a_3*M*r0_3*(2*E_2*r0_8*(1422*M_3 + (-1493 + 1062*E_2)*M_2*r0 + (793 + 482*E_2)*M*r0_2 - 135*r0_3) - 2*L_8*(1192*M_3 - 36*(2 + 7*E_2)*M_2*r0 - 2*(155 + 21*E_2)*M*r0_2 + 3*(20 + 21*E_2)*r0_3) + L_2*r0_5*(9600*M_4 - 12*(369 + 527*E_2)*M_3*r0 + 2*(-1251 - 3031*E_2 + 1152*E_4)*M_2*r0_2 + 2*(428 + 3823*E_2 + 1194*E_4)*M*r0_3 + (141 - 2290*E_2)*r0_4) + 2*L_4*r0_3*(9120*M_4 - 4*(1116 + 1943*E_2)*M_3*r0 + 18*(-153 - 56*E_2 + 72*E_4)*M_2*r0_2 + (843 + 3982*E_2 + 1042*E_4)*M*r0_3 + 23*(9 - 64*E_2)*r0_4) + 2*L_6*r0*(4512*M_4 - 2*(1787 + 986*E_2)*M_3*r0 + (-1461 + 470*E_2 + 252*E_4)*M_2*r0_2 + 2*(379 + 495*E_2 + 126*E_4)*M*r0_3 + 18*(4 - 33*E_2)*r0_4)) + 2*a_11*M*(L_2*(-48*(-1 + 6*E_2)*M_4 - 2*(25 - 336*E_2 + 24*E_4)*M_3*r0 + (-13 + 1315*E_2)*M_2*r0_2 + 6*(10 + 49*E_2 + 2*E_4)*M*r0_3 + 24*(1 - 3*E_2)*r0_4) + 2*E_2*r0*(16*(-255 + 74*E_2)*M_5 + 8*(-1056 + 293*E_2)*M_4*r0 + 3*(-1571 + 528*E_2)*M_3*r0_2 + 2*(-169 + 203*E_2)*M_2*r0_3 + 2*(11 + 13*E_2)*M*r0_4 - 96*r0_5)) + a_4*E*L*r0_3*(2*L_6*M*(4672*M_3 - 12*(-65 + 98*E_2)*M_2*r0 - 4*(151 + 63*E_2)*M*r0_2 + 7*(19 + 18*E_2)*r0_3) + L_4*r0*(-13088*M_5 - 8*(-3535 + 254*E_2)*M_4*r0 - 4*(-2965 + 2696*E_2)*M_3*r0_2 - 4*(1228 + 987*E_2)*M_2*r0_3 + 2*(351 + 1160*E_2)*M*r0_4 + 33*r0_5) + L_2*r0_3*(-66044*M_5 + 4*(8387 + 4320*E_2)*M_4*r0 + (28813 - 3160*E_2)*M_3*r0_2 - 2*(4883 + 4878*E_2)*M_2*r0_3 + 3*(193 + 1588*E_2)*M*r0_4 + 111*r0_5) + r0_5*(-55644*M_5 + 36*(413 + 134*E_2)*M_4*r0 + (20481 + 1156*E_2)*M_3*r0_2 - 2*(3279 + 2288*E_2)*M_2*r0_3 + (-151 + 2842*E_2)*M*r0_4 + 153*r0_5)) + 2*a_5*M*r0_2*(12*L_8*(2*M_2 - 7*M*r0 + 2*r0_2) + E_2*r0_6*(19404*M_4 + 1152*(-1 + 6*E_2)*M_3*r0 + (-6861 + 7268*E_2)*M_2*r0_2 + 2*(1267 + 842*E_2)*M*r0_3 - 660*r0_4) + 2*L_6*(-2880*M_4 + 8*(-415 + 18*E_2)*M_3*r0 + 7*(69 + 52*E_2 + 36*E_4)*M_2*r0_2 + (403 + 96*E_2 + 126*E_4)*M*r0_3 + 9*(12 - 35*E_2)*r0_4) + L_2*r0_3*(21984*M_5 + 12*(65 + 721*E_2)*M_4*r0 + 4*(-3531 - 5738*E_2 + 1116*E_4)*M_3*r0_2 + (-1143 - 7025*E_2 + 7824*E_4)*M_2*r0_3 + 6*(281 + 1112*E_2 + 584*E_4)*M*r0_4 + (429 - 3134*E_2)*r0_5) + L_4*r0*(20640*M_5 - 4*(885 + 2344*E_2)*M_4*r0 + 4*(-5035 - 5092*E_2 + 612*E_4)*M_3*r0_2 + (-1081 - 624*E_2 + 5264*E_4)*M_2*r0_3 + 2*(1248 + 1821*E_2 + 1070*E_4)*M*r0_4 + (747 - 2737*E_2)*r0_5)) + a_10*E*L*M*(L_2*(384*M_4 + 24*(-7 + 10*E_2)*M_3*r0 - 16*(57 + 85*E_2)*M_2*r0_2 - 3*(105 + 236*E_2)*M*r0_3 + 6*(-3 + 2*E_2)*r0_4) + r0*(-128*(-255 + 38*E_2)*M_5 + 16*(3192 - 445*E_2)*M_4*r0 + 2*(10011 - 3416*E_2)*M_3*r0_2 - (2713 + 6368*E_2)*M_2*r0_3 - (2041 + 1740*E_2)*M*r0_4 + 2*(-41 + 104*E_2)*r0_5)) + 2*a_9*M*(L_4*(-48*M_4 - 8*(25 + 9*E_2)*M_3*r0 + (-281 + 1116*E_2 + 48*E_4)*M_2*r0_2 + 6*(6 + 83*E_2 + 4*E_4)*M*r0_3 - 72*(-1 + E_2)*r0_4) + L_2*r0*(-96*(85 + 72*E_2)*M_5 + 16*(-540 - 1403*E_2 + 252*E_4)*M_4*r0 + 8*(-141 - 2558*E_2 + 678*E_4)*M_3*r0_2 + (1543 - 2299*E_2 + 2832*E_4)*M_2*r0_3 + 4*(220 + 75*E_2 + 147*E_4)*M*r0_4 + 12*(17 - 61*E_2)*r0_5) + E_2*r0_2*(16128*M_6 + 8*(2903 + 504*E_2)*M_5*r0 + 4*(-3431 + 3356*E_2)*M_4*r0_2 + 2*(-12925 + 6576*E_2)*M_3*r0_3 + 12*(-421 + 402*E_2)*M_2*r0_4 + 8*(55 + 68*E_2)*M*r0_5 - 567*r0_6)) - a_6*E*L*r0_2*(6*L_6*M*(40*M_2 - 6*M*r0 + r0_2) - 4*L_4*M*(6624*M_4 + 4*(2283 - 632*E_2)*M_3*r0 + (1591 - 2186*E_2)*M_2*r0_2 - 2*(304 + 243*E_2)*M*r0_3 + 2*(-12 + 95*E_2)*r0_4) + r0_3*(102120*M_6 + 4*(9595 + 132*E_2)*M_5*r0 - 2*(36117 + 3464*E_2)*M_4*r0_2 + (-15573 + 4304*E_2)*M_3*r0_3 + (9519 + 4568*E_2)*M_2*r0_4 + (227 - 2528*E_2)*M*r0_5 - 63*r0_6) - L_2*r0*(-63168*M_6 + 32*(138 + 143*E_2)*M_5*r0 + 8*(12187 + 668*E_2)*M_4*r0_2 - 2*(-9847 + 6776*E_2)*M_3*r0_3 - 5*(2017 + 1380*E_2)*M_2*r0_4 + (-219 + 2980*E_2)*M*r0_5 + 39*r0_6)) + 2*a_7*M*r0*(4*L_6*(6*M_3 + (-61 + 54*E_2)*M_2*r0 + 3*(-9 + 20*E_2)*M*r0_2 - 6*(-3 + E_2)*r0_3) + 6*E_2*r0_4*(5196*M_5 + 8*(452 + 189*E_2)*M_4*r0 + (-3323 + 2800*E_2)*M_3*r0_2 + (-1395 + 1516*E_2)*M_2*r0_3 + (291 + 236*E_2)*M*r0_4 - 139*r0_5) - 2*L_4*(2336*M_5 + 24*(323 + 108*E_2)*M_4*r0 + 2*(1597 + 2796*E_2 - 620*E_4)*M_3*r0_2 + (-857 + 824*E_2 - 1360*E_4)*M_2*r0_3 - (701 + 191*E_2 + 382*E_4)*M*r0_4 + 6*(-38 + 87*E_2)*r0_5) + L_2*r0*(16128*M_6 + 8*(1153 + 660*E_2)*M_5*r0 + 4*(-4405 - 3027*E_2 + 1152*E_4)*M_4*r0_2 + 2*(-5147 - 18986*E_2 + 5352*E_4)*M_3*r0_3 + 3*(577 - 2455*E_2 + 2784*E_4)*M_2*r0_4 + 4*(478 + 539*E_2 + 576*E_4)*M*r0_5 + (471 - 2182*E_2)*r0_6)) - a_8*E*L*r0*(6*L_4*M*(8*M_3 + 8*(8 + 7*E_2)*M_2*r0 + 2*(8 + 33*E_2)*M*r0_2 - (-3 + E_2)*r0_3) + L_2*M*(-23296*M_5 + 16*(-4603 + 792*E_2)*M_4*r0 + 8*(-6775 + 1466*E_2)*M_3*r0_2 + (-3009 + 10664*E_2)*M_2*r0_3 + 4*(1039 + 822*E_2)*M*r0_4 + 4*(69 - 179*E_2)*r0_5) + r0*(64512*M_7 - 192*(-338 + 17*E_2)*M_6*r0 - 8*(7809 + 4*E_2)*M_5*r0_2 + 4*(-17149 + 284*E_2)*M_4*r0_3 + (-1175 + 9016*E_2)*M_3*r0_4 + 3*(2223 + 1172*E_2)*M_2*r0_5 - 3*(-83 + 374*E_2)*M*r0_6 - 6*r0_7))))/(3.*r0_8*expr2_3);\n    A33220 = (-16*r0*(6*a_5*E_2*M*(2*M + r0) - a_4*E*L*r0*(3*M + 2*r0) - 6*a*M*r0*(L_4 + (1 - 2*E_2)*L_2*r0_2 - 3*E_2*r0_4) + E*L*r0_3*(L_2*(M + 3*r0) + r0_2*(-M + 4*r0)) + 2*a_3*M*(6*E_2*r0_2*(3*M + 2*r0) + L_2*(-6*M - 3*r0 + 2*E_2*r0)) + 2*a_2*E*L*r0*(L_2*(M - r0) + r0*(-4*M_2 + 4*M*r0 + r0_2))))/expr2_2;\n    A33240 = (4*(2*a_11*E_2*M*(48*(-6 + E_2)*M_3 + (973 + 24*E_2)*M_2*r0 + 870*M*r0_2 + 150*r0_3) + a_10*E*L*M*(1152*M_3 + 4*(120 - 331*E_2)*M_2*r0 + 9*(11 - 92*E_2)*M*r0_2 + 6*(7 - 6*E_2)*r0_3) + E*L*r0_5*(430*L_6*M*r0_2M_2 + 2*L_4*r0_2*(1548*M_3 - 26*(79 + 25*E_2)*M_2*r0 + (703 + 325*E_2)*M*r0_2 - 36*r0_3) + L_2*r0_4*(1136*M_3 - 2*(1281 + 1336*E_2)*M_2*r0 + (1015 + 1448*E_2)*M*r0_2 - 27*r0_3) + 3*r0_6*(-260*M_3 + (202 - 488*E_2)*M_2*r0 + 2*(-56 + 143*E_2)*M*r0_2 + 35*r0_3)) - 2*a*M*r0_3*(-9*E_2*r0_8*(-144*M_2 + 4*(22 + 15*E_2)*M*r0 - 7*r0_2) + 4*L_8*(214*M_2 - 215*M*r0 + 54*r0_2) + 2*L_6*r0*(-2112*M_3 + 2*(1183 + 106*E_2)*M_2*r0 - 2*(467 + 213*E_2)*M*r0_2 + 3*(45 + 53*E_2)*r0_3) + 2*L_4*r0_3*(-4128*M_3 + 4*(954 + 661*E_2)*M_2*r0 - (990 + 2569*E_2 + 324*E_4)*M*r0_2 + 3*(16 + 233*E_2)*r0_3) + 3*L_2*r0_5*(-1408*M_3 + 16*(83 + 148*E_2)*M_2*r0 - 8*(44 + 249*E_2 + 45*E_4)*M*r0_2 + (17 + 458*E_2)*r0_3)) + a_2*E*L*r0_3*(2*L_6*M*(1304*M_2 - 1102*M*r0 + 221*r0_2) + 4*L_4*r0*(-536*M_4 + 2*(981 - 433*E_2)*M_3*r0 - 6*(205 + 106*E_2)*M_2*r0_2 + (307 + 316*E_2)*M*r0_3 + 21*r0_4) + L_2*r0_3*(-23432*M_4 + 8*(2403 + 218*E_2)*M_3*r0 - 6*(831 + 1436*E_2)*M_2*r0_2 + 3*(433 + 1344*E_2)*M*r0_3 + 69*r0_4) + 3*r0_5*(-8736*M_4 + 4*(1535 + 362*E_2)*M_3*r0 - 8*(93 + 250*E_2)*M_2*r0_2 + 6*(-31 + 170*E_2)*M*r0_3 + 83*r0_4)) - 2*a_9*M*(L_2*(288*(1 + E_2)*M_3 + (1213 - 624*E_2 + 48*E_4)*M_2*r0 + 6*(141 - 208*E_2)*M*r0_2 + 6*(25 - 49*E_2)*r0_3) - E_2*r0*(96*(-111 + 31*E_2)*M_4 + 4*(-4871 + 1224*E_2)*M_3*r0 + (-6155 + 2472*E_2)*M_2*r0_2 + 6*(315 + 62*E_2)*M*r0_3 + 480*r0_4)) - 2*a_3*M*r0_2*(-3*E_2*r0_6*(2712*M_3 + 4*(-493 + 324*E_2)*M_2*r0 + 664*(1 + E_2)*M*r0_2 - 55*r0_3) + 2*L_6*(2112*M_3 + 12*(35 - 51*E_2)*M_2*r0 - 2*(467 + 64*E_2)*M*r0_2 + 9*(22 + 17*E_2)*r0_3) + 2*L_4*r0*(-9600*M_4 + 8*(729 + 388*E_2)*M_3*r0 + 2*(1779 + 631*E_2 - 324*E_4)*M_2*r0_2 - (2001 + 1864*E_2 + 640*E_4)*M*r0_3 + 39*(3 + 22*E_2)*r0_4) + 2*L_2*r0_3*(-9984*M_4 + 6*(786 + 557*E_2)*M_3*r0 + (2850 + 3865*E_2 - 1512*E_4)*M_2*r0_2 - 6*(225 + 691*E_2 + 262*E_4)*M*r0_3 + 2*(15 + 709*E_2)*r0_4)) + a_8*E*L*(6*L_2*M*(128*M_3 + 2*(107 + 30*E_2)*M_2*r0 + 4*(21 - 23*E_2)*M*r0_2 + (15 - 8*E_2)*r0_3) - r0*(384*(-111 + 19*E_2)*M_5 + 8*(-5928 + 1277*E_2)*M_4*r0 + (-8077 + 9892*E_2)*M_3*r0_2 + 4*(676 + 1043*E_2)*M_2*r0_3 - 42*(-5 + 9*E_2)*M*r0_4 + 18*r0_5)) + a_4*E*L*r0_2*(-12*L_6*M*r0 + 2*L_4*(8736*M_4 - 8*(-534 + 367*E_2)*M_3*r0 - 2*(407 + 323*E_2)*M_2*r0_2 + (61 + 301*E_2)*M*r0_3 - 18*r0_4) + 2*L_2*r0*(-26928*M_5 - 4*(-3163 + 316*E_2)*M_4*r0 + 2*(9509 - 1814*E_2)*M_3*r0_2 - 2*(1483 + 1698*E_2)*M_2*r0_3 + 3*(11 + 612*E_2)*M*r0_4 + 21*r0_5) + 3*r0_3*(-32308*M_5 + 4*(999 + 602*E_2)*M_4*r0 + (13939 - 4*E_2)*M_3*r0_2 - 4*(656 + 781*E_2)*M_2*r0_3 + 18*(-11 + 74*E_2)*M*r0_4 + 55*r0_5)) + 2*a_7*M*(-2*L_4*(72*M_3 + 2*(157 + 63*E_2)*M_2*r0 + 3*(181 - 52*E_2)*M*r0_2 + 6*(25 - 13*E_2)*r0_3) + 2*L_2*r0*(-144*(37 + 24*E_2)*M_4 + 2*(-1057 - 4032*E_2 + 984*E_4)*M_3*r0 + (937 - 4811*E_2 + 1752*E_4)*M_2*r0_2 + 2*(-33 + 151*E_2 + 246*E_4)*M*r0_3 + 3*(-43 + 10*E_2)*r0_4) + 3*E_2*r0_2*(7488*M_5 + 4*(1879 + 360*E_2)*M_4*r0 + 16*(-457 + 255*E_2)*M_3*r0_2 + 3*(-2005 + 952*E_2)*M_2*r0_3 + 2*(259 + 276*E_2)*M*r0_4 + 157*r0_5)) + 2*a_5*M*r0*(-6*L_6*(6*M_2 + 40*M*r0 + (25 - 2*E_2)*r0_2) + 3*E_2*r0_4*(10068*M_4 + 12*(145 + 216*E_2)*M_3*r0 + (-5171 + 3336*E_2)*M_2*r0_2 + 2*(289 + 456*E_2)*M*r0_3 + 13*r0_4) - 2*L_4*(2640*M_4 + 4*(1457 + 108*E_2)*M_3*r0 - 4*(247 - 315*E_2 + 158*E_4)*M_2*r0_2 + (-726 + 309*E_2 - 316*E_4)*M*r0_3 + 3*(73 + 103*E_2)*r0_4) + L_2*r0*(22464*M_5 + 12*(89 + 48*E_2)*M_4*r0 + 4*(-4749 - 3241*E_2 + 1080*E_4)*M_3*r0_2 + 3*(133 - 3800*E_2 + 2320*E_4)*M_2*r0_3 + 2*(1179 + 1276*E_2 + 1524*E_4)*M*r0_4 - (117 + 1696*E_2)*r0_5)) - a_6*E*L*r0*(-12*L_4*M*(26*M_2 + 34*M*r0 - (-3 + E_2)*r0_2) + L_2*(-25728*M_5 + 24*(-2333 + 632*E_2)*M_4*r0 + 368*(-55 + 24*E_2)*M_3*r0_2 + 142*(19 + 20*E_2)*M_2*r0_3 + 2*(241 - 520*E_2)*M*r0_4 + 54*r0_5) + r0*(89856*M_6 - 96*(-492 + 59*E_2)*M_5*r0 + 8*(-10031 + 66*E_2)*M_4*r0_2 + 2*(-15339 + 5710*E_2)*M_3*r0_3 + (8029 + 8180*E_2)*M_2*r0_4 - 48*(-13 + 46*E_2)*M*r0_5 - 3*r0_6))))/(3.*r0_5*expr2_3);\n    A33400 = (2*r0_4*(-6*a_3*E_2*M + 3*a_2*E*L*r0 + E*L*(M - 4*r0)*r0_2 + 6*a*M*(L_2 - 3*E_2*r0_2)))/expr2_2;\n    A33420 = (6*a_9*E_2*M*(4*(-27 + 7*E_2)*M_2 + 12*(22 + E_2)*M*r0 + 131*r0_2) - 3*a_8*E*L*(48*(-9 + 2*E_2)*M_3 + 20*(-8 + 11*E_2)*M_2*r0 + 2*(4 + 19*E_2)*M*r0_2 + 9*r0_3) + E*L*r0_4*(8*L_4*M*(194*M_2 - 201*M*r0 + 52*r0_2) + L_2*r0_2*(1132*M_3 - 4*(552 + 265*E_2)*M_2*r0 + (903 + 544*E_2)*M*r0_2 - 50*r0_3) + r0_4*(-1020*M_3 - 12*(-56 + 97*E_2)*M_2*r0 + (-299 + 666*E_2)*M*r0_2 + 100*r0_3)) - 2*a_7*M*(3*L_2*(12*(9 + 2*E_2)*M_2 + 4*(86 - 24*E_2 + E_4)*M*r0 + (131 - 159*E_2)*r0_2) - 2*E_2*r0*(12*(-189 + 50*E_2)*M_3 + 3*(-1281 + 278*E_2)*M_2*r0 + (-17 + 258*E_2)*M*r0_2 + 578*r0_3)) - 2*a*M*r0_2*(-3*E_2*r0_6*(-324*M_2 + 2*(71 + 72*E_2)*M*r0 + 13*r0_2) + 12*L_6*(56*M_2 - 58*M*r0 + 15*r0_2) + L_4*r0*(-4440*M_3 + 4*(1149 + 136*E_2)*M_2*r0 - 808*(2 + E_2)*M*r0_2 + (205 + 282*E_2)*r0_3) + L_2*r0_3*(-4488*M_3 + 12*(368 + 317*E_2)*M_2*r0 - 2*(673 + 1683*E_2 + 234*E_4)*M*r0_2 + (121 + 837*E_2)*r0_3)) + a_6*E*L*(3*L_2*(160*M_3 + 20*(9 + 2*E_2)*M_2*r0 - 2*(9 + 8*E_2)*M*r0_2 - 9*r0_3) - r0*(96*(-189 + 38*E_2)*M_4 + 48*(-256 + 93*E_2)*M_3*r0 + 12*(2 + 241*E_2)*M_2*r0_2 + (329 - 216*E_2)*M*r0_3 + 67*r0_4)) + a_2*E*L*r0_2*(12*L_4*M*(176*M_2 - 147*M*r0 + 33*r0_2) + L_2*r0*(-9936*M_4 + 16*(489 - 155*E_2)*M_3*r0 - 8*(199 + 226*E_2)*M_2*r0_2 + 4*(133 + 256*E_2)*M*r0_3 + 69*r0_4) + r0_3*(-22800*M_4 + 36*(367 + 80*E_2)*M_3*r0 - 4*(13 + 1047*E_2)*M_2*r0_2 + (-535 + 1776*E_2)*M*r0_3 + 187*r0_4)) - 2*a_5*M*(3*L_4*(36*M_2 + 4*(22 + 9*E_2)*M*r0 + (131 - 16*E_2)*r0_2) + L_2*r0*(216*(21 + 8*E_2)*M_3 - 6*(257 - 358*E_2 + 160*E_4)*M_2*r0 - 2*(257 - 567*E_2 + 210*E_4)*M*r0_2 + (757 - 493*E_2)*r0_3) - 2*E_2*r0_2*(5184*M_4 + 18*(167 + 36*E_2)*M_3*r0 + 2*(-2284 + 831*E_2)*M_2*r0_2 + 3*(-289 + 226*E_2)*M*r0_3 + 592*r0_4)) - 2*a_3*M*r0*(24*L_6*M - 2*E_2*r0_4*(3438*M_3 + 3*(-463 + 306*E_2)*M_2*r0 + (-253 + 654*E_2)*M*r0_2 + 230*r0_3) + 2*L_4*(888*M_3 + 12*(67 - 36*E_2)*M_2*r0 - 6*(163 + 8*E_2)*M*r0_2 + (275 + 117*E_2)*r0_3) + L_2*r0*(-10368*M_4 + 36*(139 + 58*E_2)*M_3*r0 + 2*(1891 + 744*E_2 - 504*E_4)*M_2*r0_2 - 4*(648 + 328*E_2 + 225*E_4)*M*r0_3 + (485 + 701*E_2)*r0_4)) - a_4*E*L*r0*(-24*L_4*M*(7*M - 2*r0) + L_2*(-8256*M_4 + 192*(-45 + 23*E_2)*M_3*r0 + 12*(-18 + 55*E_2)*M_2*r0_2 + 3*(43 - 144*E_2)*M*r0_3 + 4*r0_4) + r0*(41472*M_5 - 48*(-42 + 55*E_2)*M_4*r0 + 48*(-499 + 48*E_2)*M_3*r0_2 + 4*(293 + 1314*E_2)*M_2*r0_3 + (541 - 1440*E_2)*M*r0_4 - 47*r0_5)))/(3.*r0_2*expr2_3);\n    A33600 = (r0*(6*a_7*E_2*M*(4*(-7 + 2*E_2)*M + 73*r0) - 3*a_6*E*L*(16*(-7 + 2*E_2)*M_2 + 24*E_2*M*r0 + 7*r0_2) - 2*a_5*M*(3*L_2*(28*M + (73 - 24*E_2)*r0) + E_2*r0*(-8*(-78 + 19*E_2)*M_2 - 4*(-271 + 49*E_2)*M*r0 - 463*r0_2)) + a_4*E*L*(24*L_2*M*(4*M - 3*r0) + r0*(-32*(-78 + 19*E_2)*M_3 - 8*(-84 + 71*E_2)*M_2*r0 + 5*(-29 + 14*E_2)*M*r0_2 - 46*r0_3)) + E*L*r0_3*(2*L_2*M*(300*M_2 - 328*M*r0 + 89*r0_2) + r0_2*(-420*M_3 - 32*(-8 + 9*E_2)*M_2*r0 + (-109 + 158*E_2)*M*r0_2 + 40*r0_3)) + 2*a_3*M*(-24*L_4*M - 2*L_2*r0*(312*M_2 - 2*(187 + 38*E_2)*M*r0 + (137 + 27*E_2)*r0_2) + E_2*r0_2*(1584*M_3 + 8*(34 + 9*E_2)*M_2*r0 + 8*(-114 + 35*E_2)*M*r0_2 + 325*r0_3)) - 2*a*M*r0*(4*L_4*(38*M_2 - 43*M*r0 + 12*r0_2) - 3*E_2*r0_4*(-72*M_2 + 36*E_2*M*r0 + 19*r0_2) + L_2*r0*(-1584*M_3 + 16*(101 + 33*E_2)*M_2*r0 - 4*(138 + 121*E_2)*M*r0_2 + (67 + 126*E_2)*r0_3)) + a_2*E*L*r0*(2*L_2*M*(304*M_2 - 236*M*r0 + 67*r0_2) + r0*(-6336*M_4 + 24*(112 + 13*E_2)*M_3*r0 + 4*(185 - 218*E_2)*M_2*r0_2 + 10*(-19 + 30*E_2)*M*r0_3 + 39*r0_4))))/(12.*expr2_3);\n    A34020 = (-48*E*L*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/expr2_2;\n    A34040 = (8*(-12*a_9*E_2*M*(24*M_3 + 6*M_2*r0 - 17*M*r0_2 - 7*r0_3) + a_8*E*L*(576*M_4 + 576*M_3*r0 + 2*(103 - 36*E_2)*M_2*r0_2 - 6*(-7 + 6*E_2)*M*r0_3 + 3*r0_4) + E*L*r0_4*(4*L_6*M*(2*M - r0) + 3*r0_6*(-39*M_2 + 8*(-1 + E_2)*M*r0 + 17*r0_2) + 3*L_2*r0_4*(-130*M_2 + 4*(4 + E_2)*M*r0 + 31*r0_2) + L_4*r0_2*(-289*M_2 + (92 - 8*E_2)*M*r0 + 36*r0_2)) + 4*a*M*r0_3*(36*E_2*r0_7*(-3*M + 2*r0) + L_6*(180*M_2 - 2*(27 + 2*E_2)*M*r0 + 3*(-7 + 2*E_2)*r0_2) + 3*L_4*r0_2*(96*M_2 - 11*(2 + 3*E_2)*M*r0 + (-15 + 31*E_2)*r0_2) + 3*L_2*r0_4*(36*M_2 - (4 + 85*E_2)*M*r0 + (-8 + 61*E_2)*r0_2)) + 2*a_6*E*L*r0*(-2304*M_5 + 240*(-6 + E_2)*M_4*r0 + 2*(377 + 324*E_2)*M_3*r0_2 + (457 + 216*E_2)*M_2*r0_3 + 3*(1 - 14*E_2)*M*r0_4 - 18*r0_5 + L_2*(192*M_3 + 10*(10 - 3*E_2)*M_2*r0 + 3*(11 - 8*E_2)*M*r0_2 + 3*r0_3)) + a_2*E*L*r0_3*(-12*L_6*M + 3*L_2*r0_2*(-960*M_3 + 4*(32 + 39*E_2)*M_2*r0 + 2*(85 - 4*E_2)*M*r0_2 + r0_3) + 6*r0_4*(-268*M_3 + 4*(11 + 36*E_2)*M_2*r0 + (43 + 6*E_2)*M*r0_2 + 10*r0_3) - 2*L_4*(600*M_3 - 2*(45 + 2*E_2)*M_2*r0 + 5*(-17 + 2*E_2)*M*r0_2 + 24*r0_3)) - 4*a_3*M*r0_2*(3*L_6*(2*M + (7 - 2*E_2)*r0) + 3*E_2*r0_5*(6*M_2 + 17*M*r0 - 65*r0_2) + L_2*r0_2*(-504*M_3 + 6*(-13 + 36*E_2)*M_2*r0 + (123 + 121*E_2)*M*r0_2 + 23*(3 - 13*E_2)*r0_3) - 3*L_4*(216*M_3 + 2*(11 + 10*E_2)*M_2*r0 - (55 + 19*E_2)*M*r0_2 + (-29 + 23*E_2)*r0_3)) - 4*a_7*M*(L_2*(72*M_3 + 6*(21 + 8*E_2)*M_2*r0 + (87 - 61*E_2)*M*r0_2 + 3*(7 - 15*E_2)*r0_3) - 3*E_2*r0*(192*M_4 + 148*M_3*r0 - 22*M_2*r0_2 + 33*M*r0_3 + 31*r0_4)) + a_4*E*L*r0_2*(-3*L_4*(4*M_2 + 4*(-1 + E_2)*M*r0 - r0_2) - 3*r0_2*(1804*M_4 + 12*(19 - 52*E_2)*M_3*r0 - (491 + 456*E_2)*M_2*r0_2 + 2*(-41 + 6*E_2)*M*r0_3 + 10*r0_4) - 2*L_2*(2352*M_4 - 20*(-19 + 6*E_2)*M_3*r0 - (607 + 132*E_2)*M_2*r0_2 + 3*(-25 + 14*E_2)*M*r0_3 + 42*r0_4)) + 4*a_5*M*r0*(-3*L_4*(16*M_2 + (31 - 8*E_2)*M*r0 + 2*(7 - 5*E_2)*r0_2) + 3*E_2*r0_3*(228*M_3 + 34*M_2*r0 + 35*M*r0_2 + 65*r0_3) + L_2*(576*M_4 + 12*(23 + 34*E_2)*M_3*r0 - 8*(21 + 25*E_2)*M_2*r0_2 - (198 + 29*E_2)*M*r0_3 + (-66 + 161*E_2)*r0_4))))/(3.*r0_4*expr2_3);\n    A34200 = (-12*E*L*r0_6)/expr2_2;\n    A34220 = (4*(2*a_7*E_2*M*(-24*M_2 + 3*M*r0 + 10*r0_2) + a_6*E*L*M*(96*M_2 + 48*M*r0 + (7 - 6*E_2)*r0_2) + a_4*E*L*r0*(-768*M_4 + 32*(-3 + E_2)*M_3*r0 + (253 + 80*E_2)*M_2*r0_2 + 2*(18 - 5*E_2)*M*r0_3 - 8*r0_4 + 4*L_2*M*(8*M - (-2 + E_2)*r0)) + E*L*r0_5*(L_2*(-93*M_2 + (25 - 4*E_2)*M*r0 + 14*r0_2) + r0_2*(-63*M_2 + 2*(3 + E_2)*M*r0 + 16*r0_2)) + 2*a*M*r0_2*(3*E_2*r0_5*(-18*M + 13*r0) + 2*L_4*(54*M_2 - 23*M*r0 + (-3 + 2*E_2)*r0_2) + L_2*r0_2*(84*M_2 - (32 + 63*E_2)*M*r0 + (-7 + 52*E_2)*r0_2)) + a_2*E*L*r0_2*(-4*L_4*M + L_2*(-400*M_3 + 118*M_2*r0 + (61 - 8*E_2)*M*r0_2 - 13*r0_3) + r0_2*(-508*M_3 + 2*(61 + 64*E_2)*M_2*r0 + (83 - 2*E_2)*M*r0_2 + 8*r0_3)) - 2*a_5*M*(L_2*(24*M_2 + (27 + 8*E_2)*M*r0 + (10 - 9*E_2)*r0_2) - E_2*r0*(192*M_3 + 58*M_2*r0 - 4*M*r0_2 + 41*r0_3)) - 2*a_3*M*r0*(2*L_4*(4*M + (5 - 2*E_2)*r0) + E_2*r0_3*(-78*M_2 + 25*M*r0 - 70*r0_2) + L_2*(-192*M_3 + 2*(5 - 38*E_2)*M_2*r0 + 53*(1 + E_2)*M*r0_2 + (17 - 27*E_2)*r0_3))))/(r0*expr2_3);\n    A34400 = (r0_2*(36*a_5*E_2*M*(-2*M + r0) + 9*a_4*E*L*(16*M_2 - r0_2) - 2*a_2*E*L*r0*(576*M_3 + 12*(-18 + E_2)*M_2*r0 + 6*(-14 + E_2)*M*r0_2 - r0_3) + 12*a*M*r0*(3*E_2*r0_3*(-6*M + 5*r0) + L_2*(48*M_2 + 4*(-7 + E_2)*M*r0 + r0_2)) - 12*a_3*M*(3*L_2*(2*M + r0) - 2*E_2*r0*(24*M_2 - 4*M*r0 + 7*r0_2)) + E*L*r0_2*(12*L_2*M*(-2*M + r0) + r0_2*(-261*M_2 + 2*(25 - 6*E_2)*M*r0 + 50*r0_2))))/(6.*expr2_3);\n    A35020 = (-8*r0*(6*a_5*E_2*M*(2*M + r0) - a_4*E*L*r0*(3*M + 2*r0) - 6*a*M*r0*(L_4 + (1 - 2*E_2)*L_2*r0_2 - 3*E_2*r0_4) + E*L*r0_4*(4*L_2 + r0*(-2*M + 5*r0)) + 2*a_3*M*(6*E_2*r0_2*(3*M + 2*r0) + L_2*(-6*M - 3*r0 + 2*E_2*r0)) + a_2*E*L*r0*(2*L_2*(M - r0) + r0*(-10*M_2 + 9*M*r0 + 3*r0_2))))/expr2_3;\n    A35040 = (4*(-2*a_11*E_2*M*(48*(-9 + 2*E_2)*M_3 + 8*(-67 + 15*E_2)*M_2*r0 + 6*(-1 + 6*E_2)*M*r0_2 + 81*r0_3) + a_10*E*L*M*(576*(-3 + E_2)*M_3 - 4*(528 + 95*E_2)*M_2*r0 - 6*(99 + 58*E_2)*M*r0_2 + 3*(-7 + 10*E_2)*r0_3) + E*L*r0_5*(2*L_6*M*(468*M_2 - 472*M*r0 + 119*r0_2) + L_2*r0_4*(578*M_3 - (1119 + 1388*E_2)*M_2*r0 + (439 + 752*E_2)*M*r0_2 - 30*r0_3) + 4*L_4*r0_2*(482*M_3 - 58*(11 + 3*E_2)*M_2*r0 + (242 + 89*E_2)*M*r0_2 - 24*r0_3) + 3*r0_6*(-138*M_3 + (131 - 236*E_2)*M_2*r0 + (-65 + 138*E_2)*M*r0_2 + 14*r0_3)) + 2*a*M*r0_3*(9*E_2*r0_8*(-113*M_2 + 2*(43 + 12*E_2)*M*r0 - 13*r0_2) - 4*L_8*(110*M_2 - 109*M*r0 + 27*r0_2) + L_4*r0_3*(4272*M_3 - 2*(1851 + 2011*E_2)*M_2*r0 + 2*(336 + 2095*E_2 + 180*E_4)*M*r0_2 + 9*(7 - 130*E_2)*r0_3) + 3*L_2*r0_5*(736*M_3 - (659 + 1437*E_2)*M_2*r0 + 2*(65 + 608*E_2 + 96*E_4)*M*r0_2 + 3*(3 - 91*E_2)*r0_3) + L_6*r0*(2160*M_3 - (2261 + 360*E_2)*M_2*r0 + 2*(371 + 314*E_2)*M*r0_2 - 12*(6 + 19*E_2)*r0_3)) + 2*a_9*M*(L_2*(-144*(-3 + 2*E_2)*M_3 + 40*(13 + 24*E_2)*M_2*r0 + 6*(51 + 56*E_2 + 4*E_4)*M*r0_2 + 9*(9 - 19*E_2)*r0_3) + E_2*r0*(48*(-137 + 34*E_2)*M_4 + 4*(-1819 + 540*E_2)*M_3*r0 + 2*(-59 + 300*E_2)*M_2*r0_2 - 6*(-49 + 8*E_2)*M*r0_3 - 477*r0_4)) + a_2*E*L*r0_3*(4*L_6*M*(356*M_2 - 322*M*r0 + 71*r0_2) + 3*r0_5*(-4606*M_4 + (4181 + 724*E_2)*M_3*r0 - 8*(101 + 94*E_2)*M_2*r0_2 + 2*(-96 + 247*E_2)*M*r0_3 + 61*r0_4) + 2*L_4*r0*(-568*M_4 + (3527 - 684*E_2)*M_3*r0 - (2279 + 916*E_2)*M_2*r0_2 + 3*(69 + 121*E_2)*M*r0_3 + 120*r0_4) + L_2*r0_3*(-13742*M_4 + (17785 + 4144*E_2)*M_3*r0 - (6037 + 6868*E_2)*M_2*r0_2 + (-47 + 2168*E_2)*M*r0_3 + 291*r0_4)) + 2*a_3*M*r0_2*(12*L_8*M + 3*E_2*r0_6*(1056*M_3 + (-1273 + 720*E_2)*M_2*r0 + 2*(465 + 118*E_2)*M*r0_2 - 203*r0_3) + L_6*(-2208*M_3 + 72*(-9 + 7*E_2)*M_2*r0 + 4*(160 + 101*E_2)*M*r0_2 + 6*(13 - 35*E_2)*r0_3) + 2*L_2*r0_3*(5160*M_4 - 3*(776 + 359*E_2)*M_3*r0 + (-1782 - 4751*E_2 + 432*E_4)*M_2*r0_2 + 6*(83 + 622*E_2 + 144*E_4)*M*r0_3 + (165 - 982*E_2)*r0_4) + L_4*r0*(9840*M_4 - 8*(729 + 416*E_2)*M_3*r0 + 2*(-2232 - 2087*E_2 + 288*E_4)*M_2*r0_2 + 2*(657 + 2230*E_2 + 380*E_4)*M*r0_3 + 21*(25 - 54*E_2)*r0_4)) + 2*a_7*M*r0*(2*L_4*(10*(11 + 9*E_2)*M_2 + 3*(47 + 72*E_2 + 4*E_4)*M*r0 + 9*(9 - 4*E_2)*r0_2) - 2*L_2*(24*(137 + 72*E_2)*M_4 + 2*(1013 + 2766*E_2 - 480*E_4)*M_3*r0 + (-629 + 889*E_2 - 864*E_4)*M_2*r0_2 - 2*(375 + 374*E_2 + 156*E_4)*M*r0_3 + 3*(-73 + 178*E_2)*r0_4) + 3*E_2*r0*(3840*M_5 + 4*(589 + 216*E_2)*M_4*r0 + 8*(-535 + 276*E_2)*M_3*r0_2 + (-1159 + 1176*E_2)*M_2*r0_3 + 4*(143 + 24*E_2)*M*r0_4 - 362*r0_5)) - a_4*E*L*r0_2*(6*L_6*M*(20*M + 3*r0) + L_4*(-9408*M_4 + 16*(-330 + 173*E_2)*M_3*r0 + 8*(287 + 155*E_2)*M_2*r0_2 - 22*(-5 + 16*E_2)*M*r0_3 + 54*r0_4) + L_2*r0*(28704*M_5 + 4*(-5923 + 140*E_2)*M_4*r0 + (-22617 + 136*E_2)*M_3*r0_2 + 30*(311 + 196*E_2)*M_2*r0_3 - 4*(-88 + 523*E_2)*M*r0_4 - 240*r0_5) + 3*r0_3*(16628*M_5 + 8*(-627 + 28*E_2)*M_4*r0 - 24*(317 + 56*E_2)*M_3*r0_2 + (2787 + 1252*E_2)*M_2*r0_3 + (191 - 664*E_2)*M*r0_4 - 71*r0_5)) - a_8*E*L*(3*L_2*M*(-64*M_3 + 20*(29 + 2*E_2)*M_2*r0 + 4*(68 + 43*E_2)*M*r0_2 + (11 - 4*E_2)*r0_3) + r0*(192*(-137 + 22*E_2)*M_5 + 16*(-1416 + 161*E_2)*M_4*r0 + 32*(104 + 103*E_2)*M_3*r0_2 + 2*(2155 + 1112*E_2)*M_2*r0_3 - 6*(-64 + 55*E_2)*M*r0_4 + 27*r0_5)) + 2*a_5*M*r0*(3*L_6*(20*M_2 + 4*(-1 + 9*E_2)*M*r0 + 3*(9 + 2*E_2)*r0_2) + 3*E_2*r0_4*(4812*M_4 + 132*(-5 + 12*E_2)*M_3*r0 + (-1843 + 1656*E_2)*M_2*r0_2 + 4*(287 + 66*E_2)*M*r0_3 - 394*r0_4) - 2*L_4*(1392*M_4 + 4*(895 + 108*E_2)*M_3*r0 + (211 + 804*E_2 - 304*E_4)*M_2*r0_2 - (699 + 603*E_2 + 212*E_4)*M*r0_3 + 24*(-13 + 15*E_2)*r0_4) + L_2*r0*(11520*M_5 + 12*(59 + 148*E_2)*M_4*r0 + 2*(-5574 - 6427*E_2 + 1008*E_4)*M_3*r0_2 + 3*(-507 - 2283*E_2 + 992*E_4)*M_2*r0_3 + 8*(225 + 722*E_2 + 219*E_4)*M*r0_4 + 2*(330 - 1021*E_2)*r0_5)) - a_6*E*L*r0*(6*L_4*M*(60*M_2 + 2*(31 + 14*E_2)*M*r0 + (5 + 3*E_2)*r0_2) + L_2*(-13440*M_5 + 48*(-723 + 148*E_2)*M_4*r0 + 32*(-203 + 132*E_2)*M_3*r0_2 + 4*(1364 + 661*E_2)*M_2*r0_3 + (763 - 688*E_2)*M*r0_4 + 81*r0_5) + r0*(46080*M_6 - 288*(-54 + E_2)*M_5*r0 - 2*(25075 + 216*E_2)*M_4*r0_2 + (-9459 + 736*E_2)*M_3*r0_3 + 4*(2231 + 1021*E_2)*M_2*r0_4 + 3*(185 - 408*E_2)*M*r0_5 - 45*r0_6))))/(3.*r0_5*expr2_4);\n    A35200 = (-2*r0_4*(6*a_3*E_2*M - 3*a_2*E*L*r0 + E*L*r0_2*(-2*M + 5*r0) - 6*a*M*(L_2 - 3*E_2*r0_2)))/expr2_3;\n    A35220 = (-24*a_9*E_2*M*(4*(-6 + E_2)*M_2 + 3*(-13 + E_2)*M*r0 - 4*r0_2) + 3*a_8*E*L*(96*(-4 + E_2)*M_3 - 8*(32 + 19*E_2)*M_2*r0 + (-43 + 2*E_2)*M*r0_2 - 9*r0_3) + 6*a_7*M*(L_2*(-48*(-2 + E_2)*M_2 + 4*(-7 + 48*E_2 + E_4)*M*r0 + (-16 + 39*E_2)*r0_2) + 2*E_2*r0*(16*(-61 + 14*E_2)*M_3 + (-683 + 200*E_2)*M_2*r0 + 10*(17 + 2*E_2)*M*r0_2 - 13*r0_3)) + E*L*r0_4*(4*L_4*M*(398*M_2 - 405*M*r0 + 103*r0_2) + L_2*r0_2*(1106*M_3 - 25*(81 + 44*E_2)*M_2*r0 + 28*(31 + 20*E_2)*M*r0_2 - 84*r0_3) + 6*r0_4*(-170*M_3 + (125 - 184*E_2)*M_2*r0 + 2*(-23 + 52*E_2)*M*r0_2 + 10*r0_3)) - 2*a*M*r0_2*(180*L_6*r0_2M_2 - 18*E_2*r0_6*(-97*M_2 + 2*(35 + 9*E_2)*M*r0 - 9*r0_2) - 3*L_2*r0_3*(1552*M_3 - (1438 + 1469*E_2)*M_2*r0 + 2*(166 + 663*E_2 + 84*E_4)*M*r0_2 + 2*(1 - 161*E_2)*r0_3) + 2*L_4*r0*(-2280*M_3 + (2223 + 328*E_2)*M_2*r0 - (621 + 520*E_2)*M*r0_2 + 3*(12 + 59*E_2)*r0_3)) + 6*a_5*M*r0*(4*L_4*(M + 9*E_2*M + 2*(-2 + E_2)*r0) + L_2*(-32*(61 + 18*E_2)*M_3 + 2*(-5 - 628*E_2 + 156*E_4)*M_2*r0 + 4*(105 + 121*E_2 + 44*E_4)*M*r0_2 + (46 - 139*E_2)*r0_3) + 2*E_2*r0*(1776*M_4 + 36*(5 + 8*E_2)*M_3*r0 + (-1293 + 536*E_2)*M_2*r0_2 + 8*(43 + 14*E_2)*M*r0_3 - 81*r0_4)) - 3*a_6*E*L*(L_2*(-32*M_3 + 8*(27 + 5*E_2)*M_2*r0 + 8*(6 + E_2)*M*r0_2 + 9*r0_3) + r0*(128*(-61 + 11*E_2)*M_4 + 4*(-688 + 171*E_2)*M_3*r0 + 8*(183 + 112*E_2)*M_2*r0_2 + (167 - 158*E_2)*M*r0_3 + 3*r0_4)) + a_2*E*L*r0_2*(24*L_4*M*(102*M_2 - 93*M*r0 + 20*r0_2) + 3*r0_3*(-7902*M_4 + (6345 + 796*E_2)*M_3*r0 - 7*(121 + 144*E_2)*M_2*r0_2 + 2*(-157 + 285*E_2)*M*r0_3 + 88*r0_4) + L_2*r0*(-10992*M_4 + 2*(7035 - 1076*E_2)*M_3*r0 - (4603 + 2500*E_2)*M_2*r0_2 + 4*(28 + 281*E_2)*M*r0_3 + 216*r0_4)) + 6*a_3*M*r0*(8*L_6*M + 2*E_2*r0_4*(996*M_3 + (-829 + 360*E_2)*M_2*r0 + 10*(47 + 14*E_2)*M*r0_2 - 87*r0_3) + L_4*(-640*M_3 + 4*(-185 + 54*E_2)*M_2*r0 + 2*(229 + 100*E_2)*M*r0_2 + 10*(1 - 11*E_2)*r0_3) + L_2*r0*(3552*M_4 - 8*(201 + 40*E_2)*M_3*r0 + (-1640 - 1935*E_2 + 312*E_4)*M_2*r0_2 + 2*(314 + 715*E_2 + 170*E_4)*M*r0_3 + 2*(32 - 239*E_2)*r0_4)) - 3*a_4*E*L*r0*(8*L_4*M*(7*M + r0) + L_2*(-2944*M_4 + 12*(-323 + 116*E_2)*M_3*r0 + 4*(267 + 122*E_2)*M_2*r0_2 + 12*(8 - 15*E_2)*M*r0_3 - 21*r0_4) + r0*(14208*M_5 - 16*(156 + 7*E_2)*M_4*r0 - 8923*M_3*r0_2 + (2447 + 1528*E_2)*M_2*r0_3 + 14*(20 - 37*E_2)*M*r0_4 - 74*r0_5)))/(3.*r0_2*expr2_4);\n    A35400 = (r0*(18*a_7*E_2*M*(4*M + 15*r0) - 9*a_6*E*L*(16*M_2 + 4*E_2*M*r0 + r0_2) - a_4*E*L*r0*(36*L_2*M + 48*(-107 + 22*E_2)*M_3 + 24*(34 + 15*E_2)*M_2*r0 - 6*(-49 + 33*E_2)*M*r0_2 + 5*r0_3) + 2*a_5*M*(9*L_2*(4*M + (-15 + 4*E_2)*r0) + E_2*r0*(12*(-107 + 22*E_2)*M_2 + 6*(-65 + 22*E_2)*M*r0 + 197*r0_2)) + E*L*r0_3*(6*L_2*M*(136*M_2 - 146*M*r0 + 39*r0_2) + r0_2*(-606*M_3 - 9*(-43 + 44*E_2)*M_2*r0 + 2*(-68 + 105*E_2)*M*r0_2 + 38*r0_3)) + 2*a_3*M*r0*(-2*L_2*(642*M_2 - 3*(133 + 40*E_2)*M*r0 + (58 + 69*E_2)*r0_2) + E_2*r0*(2448*M_3 + 3*(-349 + 72*E_2)*M_2*r0 + 2*(73 + 120*E_2)*M*r0_2 + 62*r0_3)) - 2*a*M*r0*(12*L_4*(22*M_2 - 23*M*r0 + 6*r0_2) + 3*E_2*r0_4*(243*M_2 - 4*(38 + 9*E_2)*M*r0 + 10*r0_2) + L_2*r0*(-2448*M_3 + 3*(779 + 200*E_2)*M_2*r0 - 4*(154 + 159*E_2)*M*r0_2 + 2*(13 + 87*E_2)*r0_3)) + a_2*E*L*r0*(6*L_2*M*(176*M_2 - 156*M*r0 + 37*r0_2) + r0*(-9792*M_4 - 48*(-141 + E_2)*M_3*r0 - 3*(63 + 292*E_2)*M_2*r0_2 + 4*(-113 + 111*E_2)*M*r0_3 + 114*r0_4))))/(12.*expr2_4);\n    A36000 = (-4*E*L*r0_6)/expr2_3;\n    A36020 = (-2*(48*a_7*E_2*M*(3*M_2 - 2*M*r0 - 2*r0_2) + 3*a_6*E*L*(-96*M_3 + 12*(-4 + E_2)*M_2*r0 + 6*(-1 + 2*E_2)*M*r0_2 + r0_3) + E*L*r0_3*(8*L_4*M*(-2*M + r0) + L_2*r0_2*(111*M_2 + 16*E_2*M*r0 - 36*r0_2) + r0_4*(103*M_2 + 4*(-5 + 2*E_2)*M*r0 - 24*r0_2)) + a_4*E*L*r0*(1536*M_4 - 16*(6 + 7*E_2)*M_3*r0 - 8*(86 + 23*E_2)*M_2*r0_2 + 2*(-13 + 40*E_2)*M*r0_3 + 51*r0_4 - 3*L_2*(20*M_2 + (4 - 8*E_2)*M*r0 - r0_2)) - 4*a*M*r0_2*(3*E_2*r0_5*(-29*M + 15*r0) + L_4*(108*M_2 - (27 + 8*E_2)*M*r0 + 3*(-5 + 4*E_2)*r0_2) + L_2*r0_2*(84*M_2 - (15 + 133*E_2)*M*r0 + 3*(-5 + 31*E_2)*r0_2)) + 12*a_5*M*(2*L_2*(6*M_2 - (-10 + E_2)*M*r0 + (4 - 5*E_2)*r0_2) + E_2*r0*(-64*M_3 + 14*M_2*r0 + 9*M*r0_2 - 25*r0_3)) + a_2*E*L*r0_2*(24*L_4*M + r0_2*(998*M_3 - (477 + 364*E_2)*M_2*r0 + 2*(-83 + 26*E_2)*M*r0_2 + 24*r0_3) + 2*L_2*(376*M_3 - 4*(45 + 2*E_2)*M_2*r0 + 5*(-9 + 4*E_2)*M*r0_2 + 30*r0_3)) + 4*a_3*M*r0*(12*L_4*(M - (-2 + E_2)*r0) - 6*E_2*r0_3*(5*M_2 - 17*M*r0 + 16*r0_2) + L_2*(-192*M_3 - 2*(9 + 26*E_2)*M_2*r0 + (57 + 109*E_2)*M*r0_2 + 3*(13 - 23*E_2)*r0_3))))/(3.*r0*expr2_4);\n    A36200 = (r0_2*(12*a_5*E_2*M*(-6*M + 7*r0) - 3*a_4*E*L*(-48*M_2 + 8*E_2*M*r0 + 5*r0_2) - 2*a_2*E*L*r0*(12*L_2*M + 384*M_3 - 4*(54 + E_2)*M_2*r0 + (-61 + 22*E_2)*M*r0_2 + 12*r0_3) - 12*a_3*M*(L_2*(6*M + (7 - 4*E_2)*r0) + E_2*r0*(-32*M_2 + 23*M*r0 - 17*r0_2)) + E*L*r0_2*(4*L_2*M*(2*M - r0) + r0_2*(-151*M_2 + 4*(11 - 5*E_2)*M*r0 + 24*r0_2)) + 4*a*M*r0*(3*E_2*r0_3*(-23*M + 12*r0) + L_2*(96*M_2 - (39 + 4*E_2)*M*r0 + 6*(-1 + 2*E_2)*r0_2))))/(6.*expr2_4);\n    A37000 = (2*r0_4*(-2*a_3*E_2*M + a_2*E*L*r0 + E*L*(M - 2*r0)*r0_2 + 2*a*M*(L_2 - 3*E_2*r0_2)))/expr2_4;\n    A37020 = (-6*a_9*E_2*M*(20*(-5 + E_2)*M_2 + 4*(-4 + 3*E_2)*M*r0 + 33*r0_2) + 3*a_8*E*L*(16*(-25 + 6*E_2)*M_3 - 28*(8 + E_2)*M_2*r0 + 2*(-13 + 7*E_2)*M*r0_2 - 3*r0_3) + E*L*r0_4*(4*L_4*M*(138*M_2 - 139*M*r0 + 35*r0_2) + r0_4*(-365*M_3 - 340*(-1 + E_2)*M_2*r0 + 6*(-23 + 31*E_2)*M*r0_2 + 24*r0_3) + L_2*r0_2*(247*M_3 - 6*(53 + 64*E_2)*M_2*r0 + 2*(19 + 98*E_2)*M*r0_2 + 24*r0_3)) + 2*a_7*M*(3*L_2*(-4*(-25 + 6*E_2)*M_2 + 4*(24 + 24*E_2 + E_4)*M*r0 + 3*(11 - 9*E_2)*r0_2) - 2*E_2*r0*(-4*(-297 + 62*E_2)*M_3 - 2*(-50 + 61*E_2)*M_2*r0 + (-343 + 46*E_2)*M*r0_2 + 213*r0_3)) - 2*a*M*r0_2*(4*L_6*(64*M_2 - 62*M*r0 + 15*r0_2) + 3*E_2*r0_6*(259*M_2 - 8*(26 + 3*E_2)*M*r0 + 34*r0_2) + L_4*r0*(-1560*M_3 + 3*(491 + 88*E_2)*M_2*r0 - 4*(87 + 110*E_2)*M*r0_2 + 150*E_2*r0_3) + L_2*r0_3*(-1608*M_3 + (1433 + 1459*E_2)*M_2*r0 - 4*(64 + 287*E_2 + 45*E_4)*M*r0_2 + 6*(-5 + 33*E_2)*r0_3)) + 2*a_5*M*(3*L_4*(12*M_2 + 4*(8 + 9*E_2)*M*r0 + 33*r0_2) + L_2*r0*(-72*(33 + 8*E_2)*M_3 + 4*(-130 - 449*E_2 + 76*E_4)*M_2*r0 + 2*(329 + 673*E_2 + 106*E_4)*M*r0_2 + 9*(37 - 49*E_2)*r0_3) + E_2*r0_2*(3648*M_4 + 2*(-583 + 360*E_2)*M_3*r0 + 7*(-309 + 148*E_2)*M_2*r0_2 + 2*(917 - 2*E_2)*M*r0_3 - 699*r0_4)) - a_6*E*L*(3*L_2*(32*M_3 + 4*(51 + 10*E_2)*M_2*r0 + 26*M*r0_2 + 3*r0_3) + r0*(32*(-297 + 50*E_2)*M_4 - 4*(360 + 29*E_2)*M_3*r0 + 2*(1471 + 404*E_2)*M_2*r0_2 - 3*(-63 + 76*E_2)*M*r0_3 - 27*r0_4)) + a_2*E*L*r0_2*(4*L_4*M*(232*M_2 - 227*M*r0 + 49*r0_2) + L_2*r0*(-4048*M_4 + 60*(113 - 10*E_2)*M_3*r0 - (2475 + 1084*E_2)*M_2*r0_2 + 6*(-17 + 70*E_2)*M*r0_3 + 84*r0_4) + r0_3*(-8206*M_4 + (8089 + 308*E_2)*M_3*r0 - 3*(497 + 88*E_2)*M_2*r0_2 + 12*(-36 + 43*E_2)*M*r0_3 + 96*r0_4)) + 2*a_3*M*r0*(24*L_6*M + 2*E_2*r0_4*(855*M_3 + 2*(-568 + 207*E_2)*M_2*r0 + 2*(476 + 31*E_2)*M*r0_2 - 237*r0_3) - 2*L_4*(344*M_3 + 6*(79 - 12*E_2)*M_2*r0 - 47*(3 + 4*E_2)*M*r0_2 + 3*(-32 + 25*E_2)*r0_3) + L_2*r0*(3648*M_4 + 2*(-785 + 44*E_2)*M_3*r0 + 3*(-667 - 1087*E_2 + 96*E_4)*M_2*r0_2 + 2*(208 + 1121*E_2 + 190*E_4)*M*r0_3 + 12*(22 - 57*E_2)*r0_4)) + a_4*E*L*r0*(-168*L_4*M_2 + L_2*(3136*M_4 + 4*(1221 - 328*E_2)*M_3*r0 - 4*(566 + 191*E_2)*M_2*r0_2 + (-125 + 224*E_2)*M*r0_3 + 54*r0_4) + r0*(-14592*M_5 + 16*(342 - 43*E_2)*M_4*r0 + 8*(1251 + 77*E_2)*M_3*r0_2 - 15*(293 + 72*E_2)*M_2*r0_3 + 3*(-133 + 172*E_2)*M*r0_4 + 108*r0_5)))/(3.*r0_2*expr2_5);\n    A37200 = (r0*(-6*a_7*E_2*M*((-44 + 8*E_2)*M + 13*r0) + 3*a_6*E*L*(16*(-11 + 2*E_2)*M_2 + 8*E_2*M*r0 + 3*r0_2) + 2*a_5*M*(3*L_2*(44*M + (13 - 8*E_2)*r0) + E_2*r0*(40*(-27 + 5*E_2)*M_2 + 4*(134 - 5*E_2)*M*r0 - 183*r0_2)) + 2*a_3*M*(24*L_4*M - 2*L_2*r0*(540*M_2 - 4*(41 + 19*E_2)*M*r0 + 3*(-17 + 19*E_2)*r0_2) + E_2*r0_2*(1680*M_3 + (-1591 + 216*E_2)*M_2*r0 + 2*(499 + 20*E_2)*M*r0_2 - 216*r0_3)) + E*L*r0_3*(2*L_2*M*(236*M_2 - 244*M*r0 + 63*r0_2) + r0_2*(-413*M_3 - 4*(-79 + 58*E_2)*M_2*r0 + 114*(-1 + E_2)*M*r0_2 + 24*r0_3)) + a_4*E*L*(24*L_2*M*(-4*M + r0) + r0*(-160*(-27 + 5*E_2)*M_3 + 8*(-216 + 13*E_2)*M_2*r0 + (-241 + 178*E_2)*M*r0_2 + 36*r0_3)) - 2*a*M*r0*(4*L_4*(50*M_2 - 49*M*r0 + 12*r0_2) + 3*E_2*r0_4*(231*M_2 - 2*(89 + 6*E_2)*M*r0 + 26*r0_2) + L_2*r0*(-1680*M_3 + (1529 + 256*E_2)*M_2*r0 - 2*(155 + 166*E_2)*M*r0_2 + 18*(-1 + 5*E_2)*r0_3)) + a_2*E*L*r0*(2*L_2*M*(400*M_2 - 380*M*r0 + 73*r0_2) + r0*(-6720*M_4 - 8*(-780 + 49*E_2)*M_3*r0 - (909 + 256*E_2)*M_2*r0_2 + 4*(-97 + 67*E_2)*M*r0_3 + 96*r0_4))))/(12.*expr2_5);\n    A38000 = (r0_2*(-6*a_5*E_2*M*(2*M - 3*r0) - 3*a_4*E*L*(-8*M_2 + 2*E_2*M*r0 + r0_2) - 2*a_2*E*L*r0*(3*L_2*M + 48*M_3 - 2*(18 + E_2)*M_2*r0 + (-8 + 5*E_2)*M*r0_2 + 3*r0_3) + E*L*M*r0_2*(L_2*(4*M - 2*r0) + r0_2*(-17*M + 2*(5 - 2*E_2)*r0)) - 6*a_3*M*(L_2*(2*M + (3 - 2*E_2)*r0) - 2*E_2*r0*(4*M_2 - 5*M*r0 + 3*r0_2)) + 2*a*M*r0*(3*E_2*r0_3*(-8*M + 3*r0) + L_2*(24*M_2 - 2*(3 + 2*E_2)*M*r0 + 3*(-1 + 2*E_2)*r0_2))))/(3.*expr2_5);\n    A39000 = (r0*(-6*a_7*E_2*M*(2*(-5 + E_2)*M + 7*r0) + 3*a_6*E*L*(8*(-5 + E_2)*M_2 + 3*E_2*M*r0 + r0_2) + 2*a_5*M*(3*L_2*(10*M + (7 - 3*E_2)*r0) + E_2*r0*(2*(-81 + 14*E_2)*M_2 + (163 - 16*E_2)*M*r0 - 60*r0_2)) + E*L*M*r0_3*(L_2*(48*M_2 - 46*M*r0 + 11*r0_2) + 2*r0_2*(-27*M_2 + 2*(11 - 6*E_2)*M*r0 + 5*(-1 + E_2)*r0_2)) + 2*a_3*M*(6*L_4*M + L_2*r0*(-162*M_2 + (17 + 16*E_2)*M*r0 + 3*(11 - 5*E_2)*r0_2) + E_2*r0_2*(216*M_3 + 2*(-151 + 18*E_2)*M_2*r0 + 2*(113 - 5*E_2)*M*r0_2 - 57*r0_3)) + a_4*E*L*(3*L_2*M*(-8*M + 3*r0) + r0*(-8*(-81 + 14*E_2)*M_3 + 2*(-180 + 29*E_2)*M_2*r0 + (-35 + 26*E_2)*M*r0_2 + 9*r0_3)) - 2*a*M*r0*(3*E_2*r0_4*(36*M_2 - 31*M*r0 + 6*r0_2) + L_4*(28*M_2 - 26*M*r0 + 6*r0_2) + L_2*r0*(-216*M_3 + 2*(95 + 6*E_2)*M_2*r0 - (29 + 26*E_2)*M*r0_2 + 6*(-1 + E_2)*r0_3)) + a_2*E*L*r0*(2*L_2*M*(56*M_2 - 55*M*r0 + 8*r0_2) + r0*(-864*M_4 + 24*(41 - 4*E_2)*M_3*r0 + 2*(-100 + 7*E_2)*M_2*r0_2 + 3*(-19 + 9*E_2)*M*r0_3 + 12*r0_4))))/(6.*expr2_6);\n  }\n\n  /* A_{r,r} */\n  {\n    A40080 = (1024*expr1_3*(a_6*E_2*M*(7*M + 3*r0) - 2*a_5*E*L*M*(7*M + 3*r0) - 2*a_3*E*L*M*r0*(3*L_2 - 4*M_2 + 9*M*r0 + 5*r0_2) + 2*a*E*L*M*r0_2*(L_2*(2*M - 3*r0) - 4*M*r0_2) + r0_2*(2*E_2*M*r0_5 + L_4*M*(-2*M + r0) + L_2*r0_2*(2*M_2 + (-3 + 2*E_2)*M*r0 + r0_2)) + a_4*M*(L_2*(7*M + 3*(1 + E_2)*r0) + 2*E_2*r0*(-2*M_2 + 7*M*r0 + 4*r0_2)) + a_2*M*r0*(3*L_4 + E_2*r0_3*(11*M + 7*r0) + L_2*(-4*M_2 - 2*(-2 + E_2)*M*r0 + (2 + 5*E_2)*r0_2))))/(r0_7*expr2_2);\n    A40260 = (256*expr1_2*(3*a_6*E_2*M*(9*M + 4*r0) - 6*a_5*E*L*M*(9*M + 4*r0) - 2*a_3*E*L*M*r0*(12*L_2 - 16*M_2 + 31*M*r0 + 21*r0_2) + 2*a*E*L*M*r0_2*(4*L_2*(2*M - 3*r0) - r0_2*(10*M + 3*r0)) + r0_2*(8*E_2*M*r0_5 + 4*L_4*M*(-2*M + r0) + L_2*r0_2*(4*M_2 + 8*(-1 + E_2)*M*r0 + 3*r0_2)) + a_4*M*(3*L_2*(9*M + 4*(1 + E_2)*r0) + 2*E_2*r0*(-8*M_2 + 25*M*r0 + 16*r0_2)) + a_2*M*r0*(12*L_4 + 7*E_2*r0_3*(5*M + 4*r0) - 2*L_2*(8*M_2 + 2*(-3 + 2*E_2)*M*r0 - 5*(1 + 2*E_2)*r0_2))))/(r0_4*expr2_2);\n    A40440 = (192*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_6*E_2*M*(13*M + 6*r0) - 2*a_5*E*L*M*(13*M + 6*r0) - 2*a_3*E*L*M*r0*(6*L_2 - 8*M_2 + 13*M*r0 + 11*r0_2) + r0_2*(4*E_2*M*r0_5 + 2*L_4*M*(-2*M + r0) + L_2*r0_3*(-2*M + 4*E_2*M + r0)) + 2*a*E*L*M*r0_2*(L_2*(4*M - 6*r0) - r0_2*(2*M + 3*r0)) + a_4*M*(L_2*(13*M + 6*(1 + E_2)*r0) + 2*E_2*r0*(-4*M_2 + 11*M*r0 + 8*r0_2)) + a_2*M*r0*(6*L_4 + E_2*r0_3*(13*M + 14*r0) + L_2*(-8*M_2 - 4*(-1 + E_2)*M*r0 + 2*(3 + 5*E_2)*r0_2))))/(r0*expr2_2);\n    A40620 = (16*r0_2*(E_2*M_2*expr3_2 + L_2*expr5_2 + 2*E*L*M*(a_3 + 3*a*r0_2)*(-(a_2*M) + r0_2*(-2*M + r0)) - 4*M*r0*(L_2 + r0_2 + (a_2*(2*M + r0))/r0)*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0)))))/expr2_2;\n    A40800 = (-4*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0))))/expr2_2;\n    A41080 = (-512*expr1_2*(a_10*E_2*M*(21*M_2 + 17*M*r0 + 3*r0_2) - 2*a_9*E*L*M*((21 + 11*E_2)*M_2 + (17 + 6*E_2)*M*r0 + 3*r0_2) - 2*a*E*L*M*r0_4*(-4*L_4*M*(6*M - 5*r0) + L_2*r0_2*(-54*M_2 + (49 + 12*E_2)*M*r0 - 3*r0_2) + r0_4*(-6*M_2 + (5 + 12*E_2)*M*r0 + 3*r0_2)) + a_8*M*(L_2*(3*(7 + 22*E_2)*M_2 + (17 + 53*E_2)*M*r0 + 3*(1 + 2*E_2)*r0_2) + E_2*r0*(-94*M_3 + 5*M_2*r0 + 74*M*r0_2 + 19*r0_3)) - 2*a_7*E*L*M*(L_2*(33*M_2 + (35 + 6*E_2)*M*r0 + 6*r0_2) + r0*(-2*(47 + 4*E_2)*M_3 + (-4 + 43*E_2)*M_2*r0 + 2*(35 + 17*E_2)*M*r0_2 + 19*r0_3)) + 2*a_3*E*L*M*r0_2*(L_4*(12*M_2 - 40*M*r0 - 3*r0_2) + L_2*r0*(36*M_3 + 6*(17 + 2*E_2)*M_2*r0 - (97 + 34*E_2)*M*r0_2 - 27*r0_3) - r0_3*(36*M_3 - (88 + 15*E_2)*M_2*r0 + 2*(14 + 23*E_2)*M*r0_2 + 29*r0_3)) + a_2*r0_2*(2*L_6*M*(-4*M_2 + 6*M*r0 + r0_2) + E_2*M*r0_6*(-30*M_2 + 29*M*r0 + 22*r0_2) + L_2*r0_3*(36*M_4 - 4*(16 + 39*E_2)*M_3*r0 + (11 + 174*E_2)*M_2*r0_2 + 3*(6 + 5*E_2)*M*r0_3 - 3*r0_4) - L_4*r0*(12*M_4 + 4*(14 + 15*E_2)*M_3*r0 - (39 + 100*E_2)*M_2*r0_2 + (-16 + E_2)*M*r0_3 + 4*r0_4)) + a_6*M*(L_4*(22*M_2 + (29 + 36*E_2)*M*r0 + 3*(2 + E_2)*r0_2) + L_2*r0*(-2*(47 + 24*E_2)*M_3 + (-13 + 128*E_2)*M_2*r0 + 6*(11 + 34*E_2)*M*r0_2 + (19 + 23*E_2)*r0_3) + E_2*r0_2*(24*M_4 - 152*M_3*r0 - 123*M_2*r0_2 + 106*M*r0_3 + 43*r0_4)) + r0_4*(4*E_2*M*r0_8 - L_6*M*(12*M_2 - 8*M*r0 + r0_2) + L_4*r0_2*(-20*M_3 + 4*(5 + 3*E_2)*M_2*r0 - (9 + 2*E_2)*M*r0_2 + 2*r0_3) + L_2*r0_5*(r0*(-2*M + r0) + 2*E_2*M*(6*M + r0))) + a_4*r0*(3*L_6*M*(4*M + r0) + E_2*M*r0_4*(6*M_3 - 97*M_2*r0 + 78*M*r0_2 + 45*r0_3) + L_4*M*(-16*M_3 - 4*(11 + 6*E_2)*M_2*r0 + 2*(27 + 62*E_2)*M*r0_2 + (21 + 4*E_2)*r0_3) + L_2*r0*(24*M_5 - 2*(48 + 49*E_2)*M_3*r0_2 + 7*(10 + 39*E_2)*M_2*r0_3 + 2*(13 + 15*E_2)*M*r0_4 - 4*r0_5 - 36*M_4*(r0 + 3*E_2*r0))) + 2*a_5*E*L*M*r0*(-3*L_4*(6*M + r0) - r0*(24*M_4 - 2*(47 + 12*E_2)*M_3*r0 + (-107 + 25*E_2)*M_2*r0_2 + 2*(48 + 31*E_2)*M*r0_3 + 42*r0_4) + L_2*(24*M_3 - (95 + 28*E_2)*M*r0_2 - 22*r0_3 + M_2*(r0 + 4*E_2*r0)))))/(r0_8*expr2_3);\n    A41260 = (-128*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_10*E_2*M*(66*M_2 + 52*M*r0 + 9*r0_2) - 2*a_9*E*L*M*((66 + 34*E_2)*M_2 + 2*(26 + 9*E_2)*M*r0 + 9*r0_2) - 2*a*E*L*M*r0_4*(L_4*(-72*M_2 + 50*M*r0 + 9*r0_2) + L_2*r0_2*(-138*M_2 + (103 + 36*E_2)*M*r0 + 15*r0_2) + r0_4*(-18*M_2 + (5 + 36*E_2)*M*r0 + 18*r0_2)) + r0_4*(16*E_2*M*r0_8 - L_6*M*(36*M_2 - 20*M*r0 + r0_2) + L_2*r0_5*(4*(-1 + 9*E_2)*M_2 + 2*(-2 + 7*E_2)*M*r0 + 3*r0_2) + L_4*r0_2*(-52*M_3 + 4*(10 + 9*E_2)*M_2*r0 - (17 + 2*E_2)*M*r0_2 + 5*r0_3)) + a_8*M*(L_2*(6*(11 + 34*E_2)*M_2 + 4*(13 + 40*E_2)*M*r0 + 9*(1 + 2*E_2)*r0_2) + E_2*r0*(-320*M_3 + 34*M_2*r0 + 253*M*r0_2 + 65*r0_3)) - 2*a_7*E*L*M*(2*L_2*(51*M_2 + (53 + 9*E_2)*M*r0 + 9*r0_2) + r0*(-8*(40 + 3*E_2)*M_3 + 2*(4 + 69*E_2)*M_2*r0 + (241 + 102*E_2)*M*r0_2 + 65*r0_3)) + 2*a_3*E*L*M*r0_2*(2*L_4*(18*M_2 - 56*M*r0 - 9*r0_2) + 2*L_2*r0*(54*M_3 + 2*(77 + 9*E_2)*M_2*r0 - (137 + 51*E_2)*M*r0_2 - 58*r0_3) - r0_3*(72*M_3 - 2*(140 + 9*E_2)*M_2*r0 + (95 + 138*E_2)*M*r0_2 + 111*r0_3)) + a_2*r0_2*(-4*L_6*M*(6*M_2 - 7*M*r0 - 4*r0_2) + E_2*M*r0_6*(-90*M_2 + 87*M*r0 + 86*r0_2) + L_4*r0*(-36*M_4 - 12*(14 + 15*E_2)*M_3*r0 + (89 + 284*E_2)*M_2*r0_2 + 9*(8 + E_2)*M*r0_3 - 11*r0_4) + L_2*r0_3*(84*M_4 - 18*(10 + 21*E_2)*M_3*r0 + (19 + 461*E_2)*M_2*r0_2 + (65 + 77*E_2)*M*r0_3 - 8*r0_4)) + 2*a_5*E*L*M*r0*(-9*L_4*(6*M + r0) + L_2*(72*M_3 + 4*(4 + 3*E_2)*M_2*r0 - (305 + 84*E_2)*M*r0_2 - 83*r0_3) - r0*(72*M_4 - 8*(44 + 9*E_2)*M_3*r0 + 2*(-164 + 51*E_2)*M_2*r0_2 + (335 + 186*E_2)*M*r0_3 + 149*r0_4)) + a_6*M*(L_4*(68*M_2 + 4*(22 + 27*E_2)*M*r0 + 9*(2 + E_2)*r0_2) + L_2*r0*(-16*(20 + 9*E_2)*M_3 + 2*(-9 + 199*E_2)*M_2*r0 + (229 + 629*E_2)*M*r0_2 + 5*(13 + 17*E_2)*r0_3) + E_2*r0_2*(72*M_4 - 556*M_3*r0 - 384*M_2*r0_2 + 381*M*r0_3 + 157*r0_4)) + a_4*r0*(9*L_6*M*(4*M + r0) + 3*E_2*M*r0_4*(-12*M_3 - 110*M_2*r0 + 89*M*r0_2 + 57*r0_3) + L_4*M*(-48*M_3 - 2*(77 + 36*E_2)*M_2*r0 + (185 + 364*E_2)*M*r0_2 + (81 + 20*E_2)*r0_3) + L_2*r0*(72*M_5 - 4*(37 + 81*E_2)*M_4*r0 - 4*(75 + 61*E_2)*M_3*r0_2 + 2*(116 + 399*E_2)*M_2*r0_3 + (97 + 130*E_2)*M*r0_4 - 11*r0_5))))/(r0_5*expr2_3);\n    A41440 = (-32*(a_10*E_2*M*(69*M_2 + 53*M*r0 + 9*r0_2) - 2*a_9*E*L*M*((69 + 35*E_2)*M_2 + (53 + 18*E_2)*M*r0 + 9*r0_2) - 6*a*E*L*M*r0_4*(L_4*(-24*M_2 + 10*M*r0 + 9*r0_2) + r0_4*(-6*M_2 + (-5 + 12*E_2)*M*r0 + 12*r0_2) + L_2*r0_2*(-38*M_2 + (13 + 12*E_2)*M*r0 + 19*r0_2)) + 2*a_3*E*L*M*r0_2*(12*L_4*(3*M_2 - 8*M*r0 - 3*r0_2) + L_2*r0*(108*M_3 + 2*(175 + 18*E_2)*M_2*r0 - (257 + 102*E_2)*M*r0_2 - 187*r0_3) - 3*r0_3*(12*M_3 + (-112 + 3*E_2)*M_2*r0 + 2*(19 + 23*E_2)*M*r0_2 + 54*r0_3)) + a_8*M*(L_2*(3*(23 + 70*E_2)*M_2 + (53 + 161*E_2)*M*r0 + 9*(1 + 2*E_2)*r0_2) + E_2*r0*(-390*M_3 + 53*M_2*r0 + 308*M*r0_2 + 81*r0_3)) - 2*a_7*E*L*M*(L_2*(105*M_2 + (107 + 18*E_2)*M*r0 + 18*r0_2) + r0*(-6*(65 + 4*E_2)*M_3 + (32 + 147*E_2)*M_2*r0 + 6*(50 + 17*E_2)*M*r0_2 + 82*r0_3)) + a_2*r0_2*(12*L_6*M*(-2*M_2 + M*r0 + 3*r0_2) + 3*E_2*M*r0_6*(-30*M_2 + 29*M*r0 + 42*r0_2) + L_4*r0*(-36*M_4 - 4*(46 + 45*E_2)*M_3*r0 + 9*(5 + 28*E_2)*M_2*r0_2 + 3*(40 + 11*E_2)*M*r0_3 - 10*r0_4) + L_2*r0_3*(60*M_4 - 8*(23 + 36*E_2)*M_3*r0 + 3*(-1 + 128*E_2)*M_2*r0_2 + 3*(30 + 47*E_2)*M*r0_3 - 7*r0_4)) + a_6*M*(L_4*(70*M_2 + (89 + 108*E_2)*M*r0 + 9*(2 + E_2)*r0_2) + L_2*r0*(-6*(65 + 24*E_2)*M_3 + (11 + 380*E_2)*M_2*r0 + 2*(146 + 331*E_2)*M*r0_2 + (83 + 117*E_2)*r0_3) + 3*E_2*r0_2*(24*M_4 - 240*M_3*r0 - 149*M_2*r0_2 + 164*M*r0_3 + 71*r0_4)) + 2*a_5*E*L*M*r0*(-9*L_4*(6*M + r0) + L_2*(72*M_3 + (61 + 12*E_2)*M_2*r0 - 3*(115 + 28*E_2)*M*r0_2 - 118*r0_3) - r0*(72*M_4 - 6*(77 + 12*E_2)*M_3*r0 + (-371 + 129*E_2)*M_2*r0_2 + 2*(214 + 93*E_2)*M*r0_3 + 199*r0_4)) + a_4*r0*(9*L_6*M*(4*M + r0) + 3*E_2*M*r0_4*(-30*M_3 - 139*M_2*r0 + 108*M*r0_2 + 81*r0_3) + L_4*M*(-48*M_3 - 8*(26 + 9*E_2)*M_2*r0 + 116*(2 + 3*E_2)*M*r0_2 + (119 + 36*E_2)*r0_3) + L_2*r0*(72*M_5 - 12*(17 + 27*E_2)*M_4*r0 - 2*(176 + 129*E_2)*M_3*r0_2 + 3*(94 + 259*E_2)*M_2*r0_3 + 6*(23 + 35*E_2)*M*r0_4 - 10*r0_5)) + r0_4*(24*E_2*M*r0_8 + 3*L_6*M*(-12*M_2 + 4*M*r0 + r0_2) + 3*L_2*r0_5*(4*(-1 + 3*E_2)*M_2 + 10*E_2*M*r0 + r0_2) + L_4*r0_2*(-44*M_3 + 3*(-1 + 2*E_2)*M*r0_2 + 4*r0_3 + 12*M_2*(r0 + 3*E_2*r0)))))/(r0_2*expr2_3);\n    A41620 = (-8*r0*(3*a_8*E_2*M*(4*M + r0) - 6*a_7*E*L*M*(2*(2 + E_2)*M + r0) + 2*a_5*E*L*M*(-3*L_2*(6*M + r0) + r0*((98 + 4*E_2)*M_2 - (61 + 28*E_2)*M*r0 - 42*r0_2)) + 2*a*E*L*M*r0_3*(r0_2*(6*M_2 + (25 - 12*E_2)*M*r0 - 30*r0_2) + L_2*(24*M_2 + 10*M*r0 - 27*r0_2)) + r0_3*(16*E_2*M*r0_6 + L_2*r0_3*(12*(-1 + E_2)*M_2 + 2*(2 + 5*E_2)*M*r0 + r0_2) + L_4*M*(-12*M_2 - 4*M*r0 + 5*r0_2)) + a_6*M*(3*L_2*(4*M + 12*E_2*M + r0 + E_2*r0) + E_2*r0*(-98*M_2 + 61*M*r0 + 40*r0_2)) + 2*a_3*E*L*M*r0*(2*L_2*(6*M_2 - 8*M*r0 - 15*r0_2) - r0*(12*M_3 - 4*(32 + 3*E_2)*M_2*r0 + 2*(10 + 17*E_2)*M*r0_2 + 77*r0_3)) + a_4*M*(3*L_4*(4*M + r0) + L_2*r0*(-2*(49 + 12*E_2)*M_2 + (61 + 100*E_2)*M*r0 + 4*(11 + 7*E_2)*r0_2) + E_2*r0_2*(12*M_3 - 196*M_2*r0 + 30*M*r0_2 + 87*r0_3)) - a_2*r0*(3*E_2*M*r0_4*(10*M_2 + M*r0 - 22*r0_2) + 4*L_4*M*(2*M_2 + 3*M*r0 - 8*r0_2) + L_2*r0*(-12*M_4 + 60*(1 + E_2)*M_3*r0 + (15 - 52*E_2)*M_2*r0_2 - (51 + 35*E_2)*M*r0_3 + 3*r0_4))))/expr2_3;\n    A41800 = (4*M*r0_5*(4*a_4*E_2*(M - r0) + a*E*L*r0_2*(-10*M + 9*r0) + a_3*E*L*(-8*M + 9*r0) + a_2*(L_2*(4*M - 5*r0) + 2*E_2*(4*M - 3*r0)*r0_2) - r0_2*(2*E_2*r0_3 + L_2*(-2*M + r0))))/expr2_3;\n    A42060 = (256*expr1_2*(3*a_6*E_2*M*(9*M + 4*r0) - 6*a_5*E*L*M*(9*M + 4*r0) - 2*a_3*E*L*M*r0*(12*L_2 - 16*M_2 + 31*M*r0 + 21*r0_2) + 2*a*E*L*M*r0_2*(4*L_2*(2*M - 3*r0) - r0_2*(10*M + 3*r0)) + r0_2*(8*E_2*M*r0_5 + 4*L_4*M*(-2*M + r0) + L_2*r0_2*(4*M_2 + 8*(-1 + E_2)*M*r0 + 3*r0_2)) + a_4*M*(3*L_2*(9*M + 4*(1 + E_2)*r0) + 2*E_2*r0*(-8*M_2 + 25*M*r0 + 16*r0_2)) + a_2*M*r0*(12*L_4 + 7*E_2*r0_3*(5*M + 4*r0) - 2*L_2*(8*M_2 + 2*(-3 + 2*E_2)*M*r0 - 5*(1 + 2*E_2)*r0_2))))/(r0_4*expr2_3);\n    A42240 = (384*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_6*E_2*M*(13*M + 6*r0) - 2*a_5*E*L*M*(13*M + 6*r0) - 2*a_3*E*L*M*r0*(6*L_2 - 8*M_2 + 13*M*r0 + 11*r0_2) + r0_2*(4*E_2*M*r0_5 + 2*L_4*M*(-2*M + r0) + L_2*r0_3*(-2*M + 4*E_2*M + r0)) + 2*a*E*L*M*r0_2*(L_2*(4*M - 6*r0) - r0_2*(2*M + 3*r0)) + a_4*M*(L_2*(13*M + 6*(1 + E_2)*r0) + 2*E_2*r0*(-4*M_2 + 11*M*r0 + 8*r0_2)) + a_2*M*r0*(6*L_4 + E_2*r0_3*(13*M + 14*r0) + L_2*(-8*M_2 - 4*(-1 + E_2)*M*r0 + 2*(3 + 5*E_2)*r0_2))))/(r0*expr2_3);\n    A42420 = (48*r0_2*(E_2*M_2*expr3_2 + L_2*expr5_2 + 2*E*L*M*(a_3 + 3*a*r0_2)*(-(a_2*M) + r0_2*(-2*M + r0)) - 4*M*r0*(L_2 + r0_2 + (a_2*(2*M + r0))/r0)*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0)))))/expr2_3;\n    A42600 = (-16*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0))))/expr2_3;\n    A43060 = (-128*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_10*E_2*M*(66*M_2 + 52*M*r0 + 9*r0_2) - 2*a_9*E*L*M*((66 + 34*E_2)*M_2 + 2*(26 + 9*E_2)*M*r0 + 9*r0_2) - 2*a*E*L*M*r0_4*(4*L_2*r0_2*(-32*M_2 + 3*(7 + 3*E_2)*M*r0 + 6*r0_2) + L_4*(-70*M_2 + 45*M*r0 + 12*r0_2) + r0_4*(-10*M_2 + 9*(-1 + 4*E_2)*M*r0 + 24*r0_2)) + r0_4*(-2*E_2*M*(M - 9*r0)*r0_7 + 17*L_6*M_2*(-2*M + r0) + 2*L_4*r0_2*(-22*M_3 + (13 + 17*E_2)*M_2*r0 - 5*M*r0_2 + 2*r0_3) + L_2*r0_4*(6*M_3 + (-15 + 32*E_2)*M_2*r0 + 2*(1 + 9*E_2)*M*r0_2 + 2*r0_3)) + a_8*M*(L_2*(6*(11 + 34*E_2)*M_2 + 4*(13 + 40*E_2)*M*r0 + 9*(1 + 2*E_2)*r0_2) + E_2*r0*(-330*M_3 + 33*M_2*r0 + 261*M*r0_2 + 68*r0_3)) - 2*a_7*E*L*M*(2*L_2*(51*M_2 + (53 + 9*E_2)*M*r0 + 9*r0_2) + r0*(-6*(55 + 4*E_2)*M_3 + (7 + 138*E_2)*M_2*r0 + 3*(83 + 34*E_2)*M*r0_2 + 68*r0_3)) + 2*a_3*E*L*M*r0_2*(L_4*(36*M_2 - 109*M*r0 - 21*r0_2) + L_2*r0*(100*M_3 + (319 + 36*E_2)*M_2*r0 - 6*(44 + 17*E_2)*M*r0_2 - 129*r0_3) - r0_3*(92*M_3 - 3*(101 + 6*E_2)*M_2*r0 + (85 + 138*E_2)*M*r0_2 + 124*r0_3)) + a_2*r0_2*(L_6*M*(-24*M_2 + 25*M*r0 + 19*r0_2) + E_2*M*r0_6*(-87*M_2 + 75*M*r0 + 95*r0_2) + L_4*r0*(-28*M_4 - 2*(86 + 89*E_2)*M_3*r0 + (75 + 277*E_2)*M_2*r0_2 + 2*(41 + 7*E_2)*M*r0_3 - 11*r0_4) + L_2*r0_3*(100*M_4 - (196 + 373*E_2)*M_3*r0 + 2*(5 + 221*E_2)*M_2*r0_2 + (75 + 91*E_2)*M*r0_3 - 9*r0_4)) + 2*a_5*E*L*M*r0*(-9*L_4*(6*M + r0) + L_2*(72*M_3 + 3*(9 + 4*E_2)*M_2*r0 - 2*(155 + 42*E_2)*M*r0_2 - 89*r0_3) - r0*(80*M_4 - 6*(59 + 12*E_2)*M_3*r0 + 2*(-176 + 51*E_2)*M_2*r0_2 + (343 + 186*E_2)*M*r0_3 + 159*r0_4)) + a_6*M*(L_4*(68*M_2 + 4*(22 + 27*E_2)*M*r0 + 9*(2 + E_2)*r0_2) + L_2*r0*(-6*(55 + 24*E_2)*M_3 + (-19 + 387*E_2)*M_2*r0 + (237 + 634*E_2)*M*r0_2 + (68 + 91*E_2)*r0_3) + E_2*r0_2*(80*M_4 - 564*M_3*r0 - 407*M_2*r0_2 + 393*M*r0_3 + 168*r0_4)) + a_4*r0*(9*L_6*M*(4*M + r0) + 3*E_2*M*r0_4*(-6*M_3 - 119*M_2*r0 + 87*M*r0_2 + 62*r0_3) + L_4*M*(-48*M_3 - 3*(55 + 24*E_2)*M_2*r0 + 19*(10 + 19*E_2)*M*r0_2 + (87 + 23*E_2)*r0_3) + L_2*r0*(80*M_5 - 4*(36 + 79*E_2)*M_4*r0 - (325 + 262*E_2)*M_3*r0_2 + 4*(59 + 198*E_2)*M_2*r0_3 + 2*(53 + 73*E_2)*M*r0_4 - 11*r0_5))))/(r0_5*expr2_4);\n    A43240 = (-32*(2*a_10*E_2*M*(69*M_2 + 53*M*r0 + 9*r0_2) - 4*a_9*E*L*M*((69 + 35*E_2)*M_2 + (53 + 18*E_2)*M*r0 + 9*r0_2) - 6*a*E*L*M*r0_4*(L_4*(-46*M_2 + 15*M*r0 + 21*r0_2) + 2*L_2*r0_2*(-34*M_2 + (5 + 12*E_2)*M*r0 + 23*r0_2) + r0_4*(-6*M_2 + 3*(-7 + 8*E_2)*M*r0 + 29*r0_2)) + r0_4*(-6*E_2*M*(M - 9*r0)*r0_7 + 3*L_6*M*(-22*M_2 + 5*M*r0 + 3*r0_2) + 2*L_4*r0_2*(-34*M_3 + (-5 + 33*E_2)*M_2*r0 + (5 + 9*E_2)*M*r0_2 + 3*r0_3) + L_2*r0_4*(14*M_3 + (-49 + 60*E_2)*M_2*r0 + (13 + 72*E_2)*M*r0_2 + 4*r0_3)) + a_8*M*(2*L_2*(3*(23 + 70*E_2)*M_2 + (53 + 161*E_2)*M*r0 + 9*(1 + 2*E_2)*r0_2) + E_2*r0*(-812*M_3 + 104*M_2*r0 + 641*M*r0_2 + 171*r0_3)) - 2*a_7*E*L*M*(L_2*(210*M_2 + 2*(107 + 18*E_2)*M*r0 + 36*r0_2) + r0*(-4*(203 + 12*E_2)*M_3 + 2*(31 + 147*E_2)*M_2*r0 + (625 + 204*E_2)*M*r0_2 + 173*r0_3)) + 2*a_3*E*L*M*r0_2*(3*L_4*(24*M_2 - 61*M*r0 - 27*r0_2) + 2*L_2*r0*(96*M_3 + 9*(41 + 4*E_2)*M_2*r0 - (245 + 102*E_2)*M*r0_2 - 206*r0_3) - r0_3*(120*M_3 + 2*(-367 + 9*E_2)*M_2*r0 + 69*(3 + 4*E_2)*M*r0_2 + 359*r0_3)) + a_2*r0_2*(3*L_6*M*(-16*M_2 + 5*M*r0 + 27*r0_2) + 3*E_2*M*r0_6*(-60*M_2 + 49*M*r0 + 93*r0_2) + L_4*r0*(-48*M_4 - 6*(64 + 59*E_2)*M_3*r0 + 3*(18 + 161*E_2)*M_2*r0_2 + (268 + 81*E_2)*M*r0_3 - 20*r0_4) + L_2*r0_3*(160*M_4 - 6*(68 + 95*E_2)*M_3*r0 + (-29 + 720*E_2)*M_2*r0_2 + (205 + 324*E_2)*M*r0_3 - 16*r0_4)) + 2*a_5*E*L*M*r0*(-18*L_4*(6*M + r0) + 2*L_2*(72*M_3 + 6*(13 + 2*E_2)*M_2*r0 - (353 + 84*E_2)*M*r0_2 - 127*r0_3) - r0*(168*M_4 - 4*(235 + 36*E_2)*M_3*r0 + 2*(-404 + 129*E_2)*M_2*r0_2 + 3*(295 + 124*E_2)*M*r0_3 + 427*r0_4)) + a_6*M*(2*L_4*(70*M_2 + (89 + 108*E_2)*M*r0 + 9*(2 + E_2)*r0_2) + L_2*r0*(-4*(203 + 72*E_2)*M_3 + 2*(10 + 363*E_2)*M_2*r0 + (609 + 1340*E_2)*M*r0_2 + 7*(25 + 36*E_2)*r0_3) + E_2*r0_2*(168*M_4 - 1476*M_3*r0 - 958*M_2*r0_2 + 1027*M*r0_3 + 459*r0_4)) + a_4*r0*(18*L_6*M*(4*M + r0) + 3*E_2*M*r0_4*(-48*M_3 - 304*M_2*r0 + 215*M*r0_2 + 177*r0_3) + L_4*M*(-96*M_3 - 18*(25 + 8*E_2)*M_2*r0 + 3*(160 + 229*E_2)*M*r0_2 + (256 + 81*E_2)*r0_3) + L_2*r0*(168*M_5 - 4*(101 + 156*E_2)*M_4*r0 - 4*(193 + 144*E_2)*M_3*r0_2 + 3*(193 + 514*E_2)*M_2*r0_3 + (301 + 468*E_2)*M*r0_4 - 20*r0_5))))/(r0_2*expr2_4);\n    A43420 = (-8*r0*(9*a_8*E_2*M*(4*M + r0) - 18*a_7*E*L*M*(2*(2 + E_2)*M + r0) + 2*a_5*E*L*M*(-9*L_2*(6*M + r0) + r0*((311 + 12*E_2)*M_2 - (191 + 84*E_2)*M*r0 - 135*r0_2)) - 6*a*E*L*M*r0_3*(L_2*(-22*M_2 - 15*M*r0 + 30*r0_2) + r0_2*(-2*M_2 + 3*(-11 + 4*E_2)*M*r0 + 34*r0_2)) + a_6*M*(9*L_2*(4*M + 12*E_2*M + r0 + E_2*r0) + E_2*r0*(-311*M_2 + 191*M*r0 + 129*r0_2)) + r0_3*(-6*E_2*M*(M - 9*r0)*r0_5 - 3*L_4*M*(10*M_2 + 7*M*r0 - 6*r0_2) + L_2*r0_2*(10*M_3 + (-53 + 30*E_2)*M_2*r0 + 4*(5 + 9*E_2)*M*r0_2 + 2*r0_3)) + a_4*M*(9*L_4*(4*M + r0) + L_2*r0*(-((311 + 72*E_2)*M_2) + (191 + 291*E_2)*M*r0 + 3*(47 + 31*E_2)*r0_2) + 3*E_2*r0_2*(16*M_3 - 206*M_2*r0 + 28*M*r0_2 + 95*r0_3)) + 2*a_3*E*L*M*r0*(L_2*(36*M_2 - 39*M*r0 - 99*r0_2) - r0*(48*M_3 - (403 + 36*E_2)*M_2*r0 + 6*(8 + 17*E_2)*M*r0_2 + 250*r0_3)) - a_2*r0*(3*E_2*M*r0_4*(29*M_2 + 11*M*r0 - 73*r0_2) + 3*L_4*M*(8*M_2 + 15*M*r0 - 35*r0_2) + L_2*r0*(-48*M_4 + 2*(94 + 87*E_2)*M_3*r0 - 9*(-7 + 15*E_2)*M_2*r0_2 - (167 + 120*E_2)*M*r0_3 + 9*r0_4))))/expr2_4;\n    A43600 = (-2*M*r0_5*(2*a_3*E*L*(35*M - 39*r0) - 35*a_4*E_2*(M - r0) - 2*a*E*L*r0*(2*M_2 - 45*M*r0 + 39*r0_2) + (M - 9*r0)*r0*(L_2*(2*M - r0) - 2*E_2*r0_3) + a_2*(L_2*(-35*M + 43*r0) + E_2*r0*(2*M_2 - 71*M*r0 + 53*r0_2))))/expr2_4;\n    A44040 = (192*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_6*E_2*M*(13*M + 6*r0) - 2*a_5*E*L*M*(13*M + 6*r0) - 2*a_3*E*L*M*r0*(6*L_2 - 8*M_2 + 13*M*r0 + 11*r0_2) + r0_2*(4*E_2*M*r0_5 + 2*L_4*M*(-2*M + r0) + L_2*r0_3*(-2*M + 4*E_2*M + r0)) + 2*a*E*L*M*r0_2*(L_2*(4*M - 6*r0) - r0_2*(2*M + 3*r0)) + a_4*M*(L_2*(13*M + 6*(1 + E_2)*r0) + 2*E_2*r0*(-4*M_2 + 11*M*r0 + 8*r0_2)) + a_2*M*r0*(6*L_4 + E_2*r0_3*(13*M + 14*r0) + L_2*(-8*M_2 - 4*(-1 + E_2)*M*r0 + 2*(3 + 5*E_2)*r0_2))))/(r0*expr2_4);\n    A44220 = (48*r0_2*(E_2*M_2*expr3_2 + L_2*expr5_2 + 2*E*L*M*(a_3 + 3*a*r0_2)*(-(a_2*M) + r0_2*(-2*M + r0)) - 4*M*r0*(L_2 + r0_2 + (a_2*(2*M + r0))/r0)*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0)))))/expr2_4;\n    A44400 = (-24*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0))))/expr2_4;\n    A45040 = (-32*(a_10*E_2*M*(69*M_2 + 53*M*r0 + 9*r0_2) - 2*a_9*E*L*M*((69 + 35*E_2)*M_2 + (53 + 18*E_2)*M*r0 + 9*r0_2) - 6*a*E*L*M*r0_4*(r0_5*(4*(-4 + 3*E_2)*M + 17*r0) + 3*L_2*r0_2*(-10*M_2 + (-1 + 4*E_2)*M*r0 + 9*r0_2) + L_4*(-22*M_2 + 5*M*r0 + 12*r0_2)) + r0_4*(-6*E_2*M*(M - 5*r0)*r0_7 + 3*L_6*M*(-10*M_2 + M*r0 + 2*r0_2) + L_2*r0_4*(14*M_3 + (-37 + 24*E_2)*M_2*r0 + (13 + 42*E_2)*M*r0_2 + r0_3) + L_4*r0_2*(-24*M_3 + 2*(-11 + 15*E_2)*M_2*r0 + (13 + 12*E_2)*M*r0_2 + 2*r0_3)) + a_8*M*(L_2*(3*(23 + 70*E_2)*M_2 + (53 + 161*E_2)*M*r0 + 9*(1 + 2*E_2)*r0_2) + E_2*r0*(-422*M_3 + 51*M_2*r0 + 333*M*r0_2 + 90*r0_3)) - 2*a_7*E*L*M*(L_2*(105*M_2 + (107 + 18*E_2)*M*r0 + 18*r0_2) + r0*(-2*(211 + 12*E_2)*M_3 + 3*(10 + 49*E_2)*M_2*r0 + (325 + 102*E_2)*M*r0_2 + 91*r0_3)) + 2*a_3*E*L*M*r0_2*(L_4*(36*M_2 - 87*M*r0 - 45*r0_2) + L_2*r0*(84*M_3 + 4*(97 + 9*E_2)*M_2*r0 - (233 + 102*E_2)*M*r0_2 - 225*r0_3) - r0_3*(84*M_3 + (-398 + 9*E_2)*M_2*r0 + 3*(31 + 46*E_2)*M*r0_2 + 197*r0_3)) + a_2*r0_2*(3*L_6*M*(-8*M_2 + M*r0 + 15*r0_2) + 3*E_2*M*r0_6*(-30*M_2 + 20*M*r0 + 51*r0_2) + L_4*r0*(-12*M_4 - 2*(100 + 87*E_2)*M_3*r0 + 3*(3 + 77*E_2)*M_2*r0_2 + 4*(37 + 12*E_2)*M*r0_3 - 10*r0_4) + L_2*r0_3*(100*M_4 - 2*(112 + 141*E_2)*M_3*r0 + 2*(-13 + 168*E_2)*M_2*r0_2 + (115 + 183*E_2)*M*r0_3 - 9*r0_4)) + 2*a_5*E*L*M*r0*(-9*L_4*(6*M + r0) + L_2*(72*M_3 + (95 + 12*E_2)*M_2*r0 - (361 + 84*E_2)*M*r0_2 - 136*r0_3) - r0*(96*M_4 - 2*(239 + 36*E_2)*M_3*r0 + (-437 + 129*E_2)*M_2*r0_2 + (457 + 186*E_2)*M*r0_3 + 228*r0_4)) + a_6*M*(L_4*(70*M_2 + (89 + 108*E_2)*M*r0 + 9*(2 + E_2)*r0_2) + L_2*r0*(-2*(211 + 72*E_2)*M_3 + (9 + 346*E_2)*M_2*r0 + (317 + 678*E_2)*M*r0_2 + (92 + 135*E_2)*r0_3) + E_2*r0_2*(96*M_4 - 756*M_3*r0 - 511*M_2*r0_2 + 535*M*r0_3 + 246*r0_4)) + a_4*r0*(9*L_6*M*(4*M + r0) + 3*E_2*M*r0_4*(-18*M_3 - 165*M_2*r0 + 107*M*r0_2 + 96*r0_3) + L_4*M*(-48*M_3 - 2*(121 + 36*E_2)*M_2*r0 + (248 + 339*E_2)*M*r0_2 + (137 + 45*E_2)*r0_3) + L_2*r0*(96*M_5 - 100*(2 + 3*E_2)*M_4*r0 - 6*(70 + 53*E_2)*M_3*r0_2 + 9*(33 + 85*E_2)*M_2*r0_3 + (163 + 258*E_2)*M*r0_4 - 10*r0_5))))/(r0_2*expr2_5);\n    A45220 = (-8*r0*(9*a_8*E_2*M*(4*M + r0) - 18*a_7*E*L*M*(2*(2 + E_2)*M + r0) + 2*a_5*E*L*M*(-9*L_2*(6*M + r0) + r0*(4*(82 + 3*E_2)*M_2 - (199 + 84*E_2)*M*r0 - 144*r0_2)) - 6*a*E*L*M*r0_3*(L_2*(-20*M_2 - 20*M*r0 + 33*r0_2) + r0_2*(2*M_2 + (-41 + 12*E_2)*M*r0 + 38*r0_2)) + a_6*M*(9*L_2*(4*M + 12*E_2*M + r0 + E_2*r0) + E_2*r0*(-328*M_2 + 199*M*r0 + 138*r0_2)) + r0_3*(-12*E_2*M*(M - 5*r0)*r0_5 - 3*L_4*M*(8*M_2 + 10*M*r0 - 7*r0_2) + L_2*r0_2*(20*M_3 + 2*(-35 + 12*E_2)*M_2*r0 + 14*(2 + 3*E_2)*M*r0_2 + r0_3)) + a_4*M*(9*L_4*(4*M + r0) + L_2*r0*(-8*(41 + 9*E_2)*M_2 + (199 + 282*E_2)*M*r0 + 6*(25 + 17*E_2)*r0_2) + 3*E_2*r0_2*(20*M_3 - 216*M_2*r0 + 26*M*r0_2 + 103*r0_3)) + 2*a_3*E*L*M*r0*(6*L_2*(6*M_2 - 5*M*r0 - 18*r0_2) - r0*(60*M_3 - 2*(211 + 18*E_2)*M_2*r0 + 6*(6 + 17*E_2)*M*r0_2 + 269*r0_3)) - a_2*r0*(3*E_2*M*r0_4*(28*M_2 + 19*M*r0 - 80*r0_2) + 6*L_4*M*(4*M_2 + 9*M*r0 - 19*r0_2) + L_2*r0*(-60*M_4 + 28*(7 + 6*E_2)*M_3*r0 + 3*(27 - 38*E_2)*M_2*r0_2 - (181 + 135*E_2)*M*r0_3 + 9*r0_4))))/expr2_5;\n    A45400 = (-6*M*r0_5*(2*a_3*E*L*(19*M - 21*r0) - 19*a_4*E_2*(M - r0) - 2*a*E*L*r0*(2*M_2 - 25*M*r0 + 21*r0_2) + (M - 5*r0)*r0*(L_2*(2*M - r0) - 2*E_2*r0_3) + a_2*(L_2*(-19*M + 23*r0) + E_2*r0*(2*M_2 - 39*M*r0 + 29*r0_2))))/expr2_5;\n    A46020 = (16*r0_2*(E_2*M_2*expr3_2 + L_2*expr5_2 + 2*E*L*M*(a_3 + 3*a*r0_2)*(-(a_2*M) + r0_2*(-2*M + r0)) - 4*M*r0*(L_2 + r0_2 + (a_2*(2*M + r0))/r0)*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0)))))/expr2_5;\n    A46200 = (-16*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0))))/expr2_5;\n    A47020 = (-8*r0*(3*a_8*E_2*M*(4*M + r0) - 6*a_7*E*L*M*(2*(2 + E_2)*M + r0) + 2*a_5*E*L*M*(-3*L_2*(6*M + r0) + r0*((115 + 4*E_2)*M_2 - (69 + 28*E_2)*M*r0 - 51*r0_2)) - 2*a*E*L*M*r0_3*(L_2*(-18*M_2 - 25*M*r0 + 36*r0_2) + r0_2*(6*M_2 + (-49 + 12*E_2)*M*r0 + 42*r0_2)) + a_6*M*(3*L_2*(4*M + 12*E_2*M + r0 + E_2*r0) + E_2*r0*(-115*M_2 + 69*M*r0 + 49*r0_2)) + M*r0_3*(2*E_2*r0_5*(-3*M + 11*r0) + L_4*(-6*M_2 - 13*M*r0 + 8*r0_2) + L_2*r0_2*(10*M_2 + (-29 + 6*E_2)*M*r0 + 4*(3 + 4*E_2)*r0_2)) + 2*a_3*E*L*M*r0*(L_2*(12*M_2 - 7*M*r0 - 39*r0_2) - r0*(24*M_3 - 3*(49 + 4*E_2)*M_2*r0 + 2*(4 + 17*E_2)*M*r0_2 + 96*r0_3)) + a_4*M*(3*L_4*(4*M + r0) + L_2*r0*(-((115 + 24*E_2)*M_2) + (69 + 91*E_2)*M*r0 + (53 + 37*E_2)*r0_2) + E_2*r0_2*(24*M_3 - 226*M_2*r0 + 24*M*r0_2 + 111*r0_3)) - a_2*r0*(L_4*M*(8*M_2 + 21*M*r0 - 41*r0_2) + 3*E_2*M*r0_4*(9*M_2 + 9*M*r0 - 29*r0_2) + L_2*r0*(-24*M_4 + 2*(34 + 27*E_2)*M_3*r0 + (33 - 31*E_2)*M_2*r0_2 - 5*(13 + 10*E_2)*M*r0_3 + 3*r0_4))))/expr2_6;\n    A47200 = (-2*M*r0_5*(2*a_3*E*L*(41*M - 45*r0) - 41*a_4*E_2*(M - r0) - 2*a*E*L*r0*(6*M_2 - 55*M*r0 + 45*r0_2) + (3*M - 11*r0)*r0*(L_2*(2*M - r0) - 2*E_2*r0_3) + a_2*(L_2*(-41*M + 49*r0) + E_2*r0*(6*M_2 - 85*M*r0 + 63*r0_2))))/expr2_6;\n    A48000 = (-4*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-2*M + 3*r0) + a_2*(-3*L_2 + E_2*(2*M - 5*r0)*r0) - r0*(2*E_2*r0_3 + L_2*(-2*M + r0))))/expr2_6;\n    A49000 = (-2*M*r0_5*(2*a_3*E*L*(11*M - 12*r0) - 11*a_4*E_2*(M - r0) - 2*a*E*L*r0*(2*M_2 - 15*M*r0 + 12*r0_2) + (M - 3*r0)*r0*(L_2*(2*M - r0) - 2*E_2*r0_3) + a_2*(L_2*(-11*M + 13*r0) + E_2*r0*(2*M_2 - 23*M*r0 + 17*r0_2))))/expr2_7;\n  }\n\n  /* A_{r,theta} */\n  {\n    A50180 = (-512*expr1_3*(-(a_8*E_2*M*(7*M + 3*r0)) + a_7*E*L*M*((14 + 3*E_2)*M + 6*r0) + a*E*L*M*r0_3*(3*r0_3*((-4 + E_2)*M + 2*r0) + L_2*(-52*M_2 + 44*M*r0 - 9*r0_2)) + (2*M - r0)*r0_3*(5*L_4*M*(2*M - r0) - 3*E_2*M*r0_5 + L_2*r0_2*(2*M_2 + (3 - 4*E_2)*M*r0 - 2*r0_2)) - a_6*M*(L_2*((7 + 9*E_2)*M + 3*(1 + E_2)*r0) + E_2*r0*(-2*M_2 + 7*M*r0 + 6*r0_2)) + a_5*E*L*M*(L_2*(9*M + 6*r0) + r0*(-4*(1 + E_2)*M_2 + (8 + 13*E_2)*M*r0 + 10*r0_2)) + a_3*E*L*M*r0*(L_2*(-12*M_2 + 29*M*r0 - 3*r0_2) + r0*(-48*M_3 - 12*(-1 + E_2)*M_2*r0 + (-2 + 13*E_2)*M*r0_2 + 10*r0_3)) + a_2*r0*(3*E_2*M*r0_4*(-6*M_2 + M*r0 + 2*r0_2) + L_4*M*(4*M_2 - 8*M*r0 + 3*r0_2) + L_2*r0*(24*M_4 + 4*(-3 + 11*E_2)*M_3*r0 - 37*E_2*M_2*r0_2 + (-4 + 7*E_2)*M*r0_3 + 2*r0_4)) - a_4*M*(3*L_4*(M + r0) - 3*E_2*M*r0_2*(8*M_2 + 3*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + 4*r0_2 + M*(r0 + 34*E_2*r0)))))/(r0_8*expr2_2);\n    A50360 = (384*expr1_2*(a_8*E_2*M*(9*M + 4*r0) - a_7*E*L*M*(3*(6 + E_2)*M + 8*r0) + a_5*E*L*M*(-(L_2*(9*M + 8*r0)) + r0*(4*(1 + E_2)*M_2 - (10 + 13*E_2)*M*r0 - 13*r0_2)) - (2*M - r0)*r0_3*(6*L_4*M*(2*M - r0) - 4*E_2*M*r0_5 + L_2*r0_2*(4*M_2 + (2 - 5*E_2)*M*r0 - 2*r0_2)) + a*E*L*M*r0_3*(r0_2*(8*M_2 - 3*(-2 + E_2)*M*r0 - 5*r0_2) + 10*L_2*(6*M_2 - 5*M*r0 + r0_2)) + a_6*M*((1 + E_2)*L_2*(9*M + 4*r0) + E_2*r0*(-2*M_2 + 9*M*r0 + 8*r0_2)) + a_3*E*L*M*r0*(L_2*(12*M_2 - 29*M*r0 + 2*r0_2) - r0*(-64*M_3 - 4*(-4 + 3*E_2)*M_2*r0 + (2 + 13*E_2)*M*r0_2 + 10*r0_3)) - a_2*r0*(L_4*M*(4*M_2 - 8*M*r0 + 3*r0_2) + E_2*M*r0_4*(-18*M_2 + M*r0 + 8*r0_2) + L_2*r0*(32*M_4 + 16*(-1 + 3*E_2)*M_3*r0 - (2 + 39*E_2)*M_2*r0_2 + (-3 + 8*E_2)*M*r0_3 + 2*r0_4)) + a_4*M*(L_4*(3*M + 4*r0) - E_2*M*r0_2*(32*M_2 + 9*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (5 + E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/(r0_5*expr2_2);\n    A50540 = (96*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_8*E_2*M*(13*M + 6*r0) - a_7*E*L*M*((26 + 3*E_2)*M + 12*r0) + a_5*E*L*M*(-3*L_2*(3*M + 4*r0) + r0*(4*(1 + E_2)*M_2 - (14 + 13*E_2)*M*r0 - 19*r0_2)) - (2*M - r0)*r0_3*(8*L_4*M*(2*M - r0) - 6*E_2*M*r0_5 + L_2*r0_2*(8*M_2 - 7*E_2*M*r0 - 2*r0_2)) + a*E*L*M*r0_3*(-3*r0_2*(-8*M_2 + (2 + E_2)*M*r0 + r0_2) + 2*L_2*(38*M_2 - 31*M*r0 + 6*r0_2)) + a_6*M*(L_2*((13 + 9*E_2)*M + 6*(1 + E_2)*r0) + E_2*r0*(-2*M_2 + 13*M*r0 + 12*r0_2)) + a_3*E*L*M*r0*(L_2*M*(12*M - 29*r0) - r0*(-96*M_3 - 12*(-2 + E_2)*M_2*r0 + (10 + 13*E_2)*M*r0_2 + 10*r0_3)) + a_2*r0*(3*E_2*M*r0_4*(6*M_2 + M*r0 - 4*r0_2) + L_4*M*(-4*M_2 + 8*M*r0 - 3*r0_2) + L_2*r0*(-48*M_4 - 8*(-3 + 7*E_2)*M_3*r0 + (6 + 43*E_2)*M_2*r0_2 + (1 - 10*E_2)*M*r0_3 - 2*r0_4)) + a_4*M*(3*L_4*(M + 2*r0) - 3*E_2*M*r0_2*(16*M_2 + 3*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (7 + 3*E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/(r0_2*expr2_2);\n    A50720 = (-8*r0*(-(a_8*E_2*M*(25*M + 12*r0)) + a_7*E*L*M*((50 + 3*E_2)*M + 24*r0) + (2*M - r0)*r0_3*(14*L_4*M*(2*M - r0) - 12*E_2*M*r0_5 - L_2*r0_2*(-20*M_2 + (6 + 13*E_2)*M*r0 + 2*r0_2)) + a*E*L*M*r0_3*(-3*r0_2*(24*M_2 - (14 + E_2)*M*r0 + r0_2) - 2*L_2*(62*M_2 - 49*M*r0 + 9*r0_2)) - a_6*M*(L_2*((25 + 9*E_2)*M + 12*(1 + E_2)*r0) + E_2*r0*(-2*M_2 + 25*M*r0 + 24*r0_2)) + a_5*E*L*M*(3*L_2*(3*M + 8*r0) + r0*(-4*(1 + E_2)*M_2 + 13*(2 + E_2)*M*r0 + 37*r0_2)) + a_3*E*L*M*r0*(L_2*(-12*M_2 + 29*M*r0 + 6*r0_2) + r0*(-192*M_3 - 12*(-4 + E_2)*M_2*r0 + (34 + 13*E_2)*M*r0_2 + 10*r0_3)) + a_2*r0*(-3*E_2*M*r0_4*(6*M_2 + 5*M*r0 - 8*r0_2) + L_4*M*(4*M_2 - 8*M*r0 + 3*r0_2) + L_2*r0*(96*M_4 + 16*(-3 + 5*E_2)*M_3*r0 - (18 + 55*E_2)*M_2*r0_2 + (5 + 16*E_2)*M*r0_3 + 2*r0_4)) - a_4*M*(3*L_4*(M + 4*r0) - 3*E_2*M*r0_2*(32*M_2 + 3*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (13 + 9*E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/expr2_2;\n    A50900 = (6*M*r0_4*(a_4*E_2 - 2*a_3*E*L + a*E*L*r0*(-4*M + r0) + a_2*(L_2 + 2*E_2*M*r0) - r0*(E_2*r0_3 + L_2*(-2*M + r0))))/(a_2 + r0*(-2*M + r0));\n    A52160 = (384*expr1_2*(a_8*E_2*M*(9*M + 4*r0) - a_7*E*L*M*(3*(6 + E_2)*M + 8*r0) + a_5*E*L*M*(-(L_2*(9*M + 8*r0)) + r0*(4*(1 + E_2)*M_2 - (10 + 13*E_2)*M*r0 - 13*r0_2)) - (2*M - r0)*r0_3*(6*L_4*M*(2*M - r0) - 4*E_2*M*r0_5 + L_2*r0_2*(4*M_2 + (2 - 5*E_2)*M*r0 - 2*r0_2)) + a*E*L*M*r0_3*(r0_2*(8*M_2 - 3*(-2 + E_2)*M*r0 - 5*r0_2) + 10*L_2*(6*M_2 - 5*M*r0 + r0_2)) + a_6*M*((1 + E_2)*L_2*(9*M + 4*r0) + E_2*r0*(-2*M_2 + 9*M*r0 + 8*r0_2)) + a_3*E*L*M*r0*(L_2*(12*M_2 - 29*M*r0 + 2*r0_2) - r0*(-64*M_3 - 4*(-4 + 3*E_2)*M_2*r0 + (2 + 13*E_2)*M*r0_2 + 10*r0_3)) - a_2*r0*(L_4*M*(4*M_2 - 8*M*r0 + 3*r0_2) + E_2*M*r0_4*(-18*M_2 + M*r0 + 8*r0_2) + L_2*r0*(32*M_4 + 16*(-1 + 3*E_2)*M_3*r0 - (2 + 39*E_2)*M_2*r0_2 + (-3 + 8*E_2)*M*r0_3 + 2*r0_4)) + a_4*M*(L_4*(3*M + 4*r0) - E_2*M*r0_2*(32*M_2 + 9*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (5 + E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/(r0_5*expr2_3);\n    A52340 = (192*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_8*E_2*M*(13*M + 6*r0) - a_7*E*L*M*((26 + 3*E_2)*M + 12*r0) + a_5*E*L*M*(-3*L_2*(3*M + 4*r0) + r0*(4*(1 + E_2)*M_2 - (14 + 13*E_2)*M*r0 - 19*r0_2)) - (2*M - r0)*r0_3*(8*L_4*M*(2*M - r0) - 6*E_2*M*r0_5 + L_2*r0_2*(8*M_2 - 7*E_2*M*r0 - 2*r0_2)) + a*E*L*M*r0_3*(-3*r0_2*(-8*M_2 + (2 + E_2)*M*r0 + r0_2) + 2*L_2*(38*M_2 - 31*M*r0 + 6*r0_2)) + a_6*M*(L_2*((13 + 9*E_2)*M + 6*(1 + E_2)*r0) + E_2*r0*(-2*M_2 + 13*M*r0 + 12*r0_2)) + a_3*E*L*M*r0*(L_2*M*(12*M - 29*r0) - r0*(-96*M_3 - 12*(-2 + E_2)*M_2*r0 + (10 + 13*E_2)*M*r0_2 + 10*r0_3)) + a_2*r0*(3*E_2*M*r0_4*(6*M_2 + M*r0 - 4*r0_2) + L_4*M*(-4*M_2 + 8*M*r0 - 3*r0_2) + L_2*r0*(-48*M_4 - 8*(-3 + 7*E_2)*M_3*r0 + (6 + 43*E_2)*M_2*r0_2 + (1 - 10*E_2)*M*r0_3 - 2*r0_4)) + a_4*M*(3*L_4*(M + 2*r0) - 3*E_2*M*r0_2*(16*M_2 + 3*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (7 + 3*E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/(r0_2*expr2_3);\n    A52520 = (-24*r0*(-(a_8*E_2*M*(25*M + 12*r0)) + a_7*E*L*M*((50 + 3*E_2)*M + 24*r0) + (2*M - r0)*r0_3*(14*L_4*M*(2*M - r0) - 12*E_2*M*r0_5 - L_2*r0_2*(-20*M_2 + (6 + 13*E_2)*M*r0 + 2*r0_2)) + a*E*L*M*r0_3*(-3*r0_2*(24*M_2 - (14 + E_2)*M*r0 + r0_2) - 2*L_2*(62*M_2 - 49*M*r0 + 9*r0_2)) - a_6*M*(L_2*((25 + 9*E_2)*M + 12*(1 + E_2)*r0) + E_2*r0*(-2*M_2 + 25*M*r0 + 24*r0_2)) + a_5*E*L*M*(3*L_2*(3*M + 8*r0) + r0*(-4*(1 + E_2)*M_2 + 13*(2 + E_2)*M*r0 + 37*r0_2)) + a_3*E*L*M*r0*(L_2*(-12*M_2 + 29*M*r0 + 6*r0_2) + r0*(-192*M_3 - 12*(-4 + E_2)*M_2*r0 + (34 + 13*E_2)*M*r0_2 + 10*r0_3)) + a_2*r0*(-3*E_2*M*r0_4*(6*M_2 + 5*M*r0 - 8*r0_2) + L_4*M*(4*M_2 - 8*M*r0 + 3*r0_2) + L_2*r0*(96*M_4 + 16*(-3 + 5*E_2)*M_3*r0 - (18 + 55*E_2)*M_2*r0_2 + (5 + 16*E_2)*M*r0_3 + 2*r0_4)) - a_4*M*(3*L_4*(M + 4*r0) - 3*E_2*M*r0_2*(32*M_2 + 3*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (13 + 9*E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/expr2_3;\n    A52700 = (24*M*r0_4*(a_4*E_2 - 2*a_3*E*L + a*E*L*r0*(-4*M + r0) + a_2*(L_2 + 2*E_2*M*r0) - r0*(E_2*r0_3 + L_2*(-2*M + r0))))/expr2_2;\n    A54140 = (96*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(a_8*E_2*M*(13*M + 6*r0) - a_7*E*L*M*((26 + 3*E_2)*M + 12*r0) + a_5*E*L*M*(-3*L_2*(3*M + 4*r0) + r0*(4*(1 + E_2)*M_2 - (14 + 13*E_2)*M*r0 - 19*r0_2)) - (2*M - r0)*r0_3*(8*L_4*M*(2*M - r0) - 6*E_2*M*r0_5 + L_2*r0_2*(8*M_2 - 7*E_2*M*r0 - 2*r0_2)) + a*E*L*M*r0_3*(-3*r0_2*(-8*M_2 + (2 + E_2)*M*r0 + r0_2) + 2*L_2*(38*M_2 - 31*M*r0 + 6*r0_2)) + a_6*M*(L_2*((13 + 9*E_2)*M + 6*(1 + E_2)*r0) + E_2*r0*(-2*M_2 + 13*M*r0 + 12*r0_2)) + a_3*E*L*M*r0*(L_2*M*(12*M - 29*r0) - r0*(-96*M_3 - 12*(-2 + E_2)*M_2*r0 + (10 + 13*E_2)*M*r0_2 + 10*r0_3)) + a_2*r0*(3*E_2*M*r0_4*(6*M_2 + M*r0 - 4*r0_2) + L_4*M*(-4*M_2 + 8*M*r0 - 3*r0_2) + L_2*r0*(-48*M_4 - 8*(-3 + 7*E_2)*M_3*r0 + (6 + 43*E_2)*M_2*r0_2 + (1 - 10*E_2)*M*r0_3 - 2*r0_4)) + a_4*M*(3*L_4*(M + 2*r0) - 3*E_2*M*r0_2*(16*M_2 + 3*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (7 + 3*E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/(r0_2*expr2_4);\n    A54320 = (-24*r0*(-(a_8*E_2*M*(25*M + 12*r0)) + a_7*E*L*M*((50 + 3*E_2)*M + 24*r0) + (2*M - r0)*r0_3*(14*L_4*M*(2*M - r0) - 12*E_2*M*r0_5 - L_2*r0_2*(-20*M_2 + (6 + 13*E_2)*M*r0 + 2*r0_2)) + a*E*L*M*r0_3*(-3*r0_2*(24*M_2 - (14 + E_2)*M*r0 + r0_2) - 2*L_2*(62*M_2 - 49*M*r0 + 9*r0_2)) - a_6*M*(L_2*((25 + 9*E_2)*M + 12*(1 + E_2)*r0) + E_2*r0*(-2*M_2 + 25*M*r0 + 24*r0_2)) + a_5*E*L*M*(3*L_2*(3*M + 8*r0) + r0*(-4*(1 + E_2)*M_2 + 13*(2 + E_2)*M*r0 + 37*r0_2)) + a_3*E*L*M*r0*(L_2*(-12*M_2 + 29*M*r0 + 6*r0_2) + r0*(-192*M_3 - 12*(-4 + E_2)*M_2*r0 + (34 + 13*E_2)*M*r0_2 + 10*r0_3)) + a_2*r0*(-3*E_2*M*r0_4*(6*M_2 + 5*M*r0 - 8*r0_2) + L_4*M*(4*M_2 - 8*M*r0 + 3*r0_2) + L_2*r0*(96*M_4 + 16*(-3 + 5*E_2)*M_3*r0 - (18 + 55*E_2)*M_2*r0_2 + (5 + 16*E_2)*M*r0_3 + 2*r0_4)) - a_4*M*(3*L_4*(M + 4*r0) - 3*E_2*M*r0_2*(32*M_2 + 3*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (13 + 9*E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/expr2_4;\n    A54500 = (36*M*r0_4*(a_4*E_2 - 2*a_3*E*L + a*E*L*r0*(-4*M + r0) + a_2*(L_2 + 2*E_2*M*r0) - r0*(E_2*r0_3 + L_2*(-2*M + r0))))/expr2_3;\n    A56120 = (-8*r0*(-(a_8*E_2*M*(25*M + 12*r0)) + a_7*E*L*M*((50 + 3*E_2)*M + 24*r0) + (2*M - r0)*r0_3*(14*L_4*M*(2*M - r0) - 12*E_2*M*r0_5 - L_2*r0_2*(-20*M_2 + (6 + 13*E_2)*M*r0 + 2*r0_2)) + a*E*L*M*r0_3*(-3*r0_2*(24*M_2 - (14 + E_2)*M*r0 + r0_2) - 2*L_2*(62*M_2 - 49*M*r0 + 9*r0_2)) - a_6*M*(L_2*((25 + 9*E_2)*M + 12*(1 + E_2)*r0) + E_2*r0*(-2*M_2 + 25*M*r0 + 24*r0_2)) + a_5*E*L*M*(3*L_2*(3*M + 8*r0) + r0*(-4*(1 + E_2)*M_2 + 13*(2 + E_2)*M*r0 + 37*r0_2)) + a_3*E*L*M*r0*(L_2*(-12*M_2 + 29*M*r0 + 6*r0_2) + r0*(-192*M_3 - 12*(-4 + E_2)*M_2*r0 + (34 + 13*E_2)*M*r0_2 + 10*r0_3)) + a_2*r0*(-3*E_2*M*r0_4*(6*M_2 + 5*M*r0 - 8*r0_2) + L_4*M*(4*M_2 - 8*M*r0 + 3*r0_2) + L_2*r0*(96*M_4 + 16*(-3 + 5*E_2)*M_3*r0 - (18 + 55*E_2)*M_2*r0_2 + (5 + 16*E_2)*M*r0_3 + 2*r0_4)) - a_4*M*(3*L_4*(M + 4*r0) - 3*E_2*M*r0_2*(32*M_2 + 3*r0_2) + L_2*r0*(-2*(1 + 6*E_2)*M_2 + (13 + 9*E_2)*r0_2 + M*(r0 + 34*E_2*r0)))))/expr2_5;\n    A56300 = (24*M*r0_4*(a_4*E_2 - 2*a_3*E*L + a*E*L*r0*(-4*M + r0) + a_2*(L_2 + 2*E_2*M*r0) - r0*(E_2*r0_3 + L_2*(-2*M + r0))))/expr2_4;\n    A58100 = (6*M*r0_4*(a_4*E_2 - 2*a_3*E*L + a*E*L*r0*(-4*M + r0) + a_2*(L_2 + 2*E_2*M*r0) - r0*(E_2*r0_3 + L_2*(-2*M + r0))))/expr2_5;\n  }\n\n  /* A_{r,phi} */\n  {\n    A60061 = (256*L*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*expr1_3)/(r0_5*(a_2 + r0*(-2*M + r0)));\n    A60081 = (128*expr1_2*(12*a_12*E_2*M_2*(26*M_2 + 25*M*r0 + 6*r0_2) - a_11*E*L*M_2*(624*M_2 + 611*M*r0 + 150*r0_2) - a_9*E*L*M*r0*(32*(-39 + 28*E_2)*M_4 + 4*(215 + 336*E_2)*M_3*r0 + (1739 + 672*E_2)*M_2*r0_2 + 2*(135 + 56*E_2)*M*r0_3 - 114*r0_4 + 2*L_2*M*(332*M + 165*r0)) - a*E*L*M*r0_5*(L_2*r0_4*(-112*M_2 + 2*(271 + 126*E_2)*M*r0 - 243*r0_2) + 12*L_4*r0_2*(14*M_2 + (33 + 10*E_2)*M*r0 - 20*r0_2) + 6*r0_6*(2*M_2 + (13 + 24*E_2)*M*r0 - 7*r0_2) + 60*L_6*(4*M_2 - r0_2)) - 2*a_3*E*L*M*r0_3*(30*L_6*(12*M_2 - 2*M*r0 - r0_2) + L_2*r0_3*(-8*M_3 + 42*(11 + 12*E_2)*M_2*r0 + 2*(293 + 183*E_2)*M*r0_2 - 407*r0_3) + r0_5*(-372*M_3 + 18*(13 + 24*E_2)*M_2*r0 + (219 + 272*E_2)*M*r0_2 - 144*r0_3) + 6*L_4*r0*(96*M_3 + 2*(41 + 10*E_2)*M_2*r0 + (31 + 20*E_2)*M*r0_2 - 40*r0_3)) - a_5*E*L*M*r0_2*(12*L_4*(228*M_3 + 4*(12 + 5*E_2)*M_2*r0 + (13 + 10*E_2)*M*r0_2 - 20*r0_3) + L_2*r0*(1568*M_4 + 16*(52 + 63*E_2)*M_3*r0 + 48*(41 + 40*E_2)*M_2*r0_2 + 6*(135 + 118*E_2)*M*r0_3 - 865*r0_4) + 3*r0_3*(-548*M_4 + 4*(-131 + 144*E_2)*M_3*r0 + 5*(127 + 160*E_2)*M_2*r0_2 + 4*(35 + 64*E_2)*M*r0_3 - 188*r0_4)) - a_7*E*L*M*r0*(180*L_4*M*r0 + 2*L_2*(1344*M_4 + 24*(20 + 19*E_2)*M_3*r0 + 114*(7 + 4*E_2)*M_2*r0_2 + (289 + 114*E_2)*M*r0_3 - 147*r0_4) + 3*r0_2*(12*(-81 + 32*E_2)*M_4 + 64*(-3 + 16*E_2)*M_3*r0 + (795 + 736*E_2)*M_2*r0_2 + 20*(3 + 8*E_2)*M*r0_3 - 144*r0_4)) + a_10*M*(L_2*M*(312*M_2 + (311 + 332*E_2)*M*r0 + 6*(13 + 27*E_2)*r0_2) + 12*E_2*r0*(-52*M_4 + 60*M_3*r0 + 102*M_2*r0_2 + 23*M*r0_3 - 3*r0_4)) + a_2*r0_3*(120*L_8*M_2*(2*M - r0) + 36*E_2*M*r0_8*(6*M_2 + 5*M*r0 - 5*r0_2) + 6*L_6*M*r0*(12*M_3 + 8*(3 + 10*E_2)*M_2*r0 + 5*(5 + 8*E_2)*M*r0_2 - 20*(1 + E_2)*r0_3) + L_4*r0_3*(-368*M_4 + 24*(3 + 49*E_2)*M_3*r0 + 2*(251 + 402*E_2)*M_2*r0_2 - (205 + 486*E_2)*M*r0_3 - 9*r0_4) - 2*L_2*r0_5*(100*M_4 + 6*(1 - 82*E_2)*M_3*r0 - 2*(41 + 196*E_2)*M_2*r0_2 + 3*(1 + 95*E_2)*M*r0_3 + 12*r0_4)) + a_8*M*r0*(2*L_4*M*(166*M + 84*r0 + 45*E_2*r0) - 12*E_2*r0_2*(168*M_4 + 28*M_3*r0 - 154*M_2*r0_2 - 34*M*r0_3 + 15*r0_4) + L_2*(48*(-13 + 56*E_2)*M_4 + 4*(35 + 624*E_2)*M_3*r0 + 4*(127 + 498*E_2)*M_2*r0_2 + (-9 + 578*E_2)*M*r0_3 - 6*(13 + 21*E_2)*r0_4)) + a_6*M*r0*(90*L_6*M*r0 - 24*E_2*r0_4*(66*M_4 + 66*M_3*r0 - 52*M_2*r0_2 - 13*M*r0_3 + 15*r0_4) + 2*L_4*(448*M_4 + 24*(-4 + 57*E_2)*M_3*r0 + 138*(1 + 6*E_2)*M_2*r0_2 + 6*(9 + 32*E_2)*M*r0_3 - 3*(28 + 25*E_2)*r0_4) + L_2*r0_2*(4*(-225 + 628*E_2)*M_4 + 4*(3 + 998*E_2)*M_3*r0 + (863 + 3612*E_2)*M_2*r0_2 + 6*(-10 + 149*E_2)*M*r0_3 - 2*(109 + 263*E_2)*r0_4)) + a_4*r0_2*(-36*E_2*M*r0_6*(18*M_3 - 15*M_2*r0 - 6*M*r0_2 + 10*r0_3) + 6*L_6*M*(152*M_3 + 4*(-7 + 30*E_2)*M_2*r0 + 20*E_2*M*r0_2 - 5*(3 + 2*E_2)*r0_3) + L_4*M*r0*(208*M_4 + 8*(-11 + 261*E_2)*M_3*r0 + 8*(73 + 345*E_2)*M_2*r0_2 + 2*(166 + 381*E_2)*M*r0_3 - (359 + 468*E_2)*r0_4) + L_2*r0_3*(-536*M_5 + 4*(-101 + 402*E_2)*M_4*r0 + 2*(347 + 1436*E_2)*M_3*r0_2 + 69*(3 + 14*E_2)*M_2*r0_3 - 6*(33 + 137*E_2)*M*r0_4 - 12*r0_5)) + (2*M - r0)*r0_7*(36*E_2*M*r0_7 + 30*L_6*M*(-2*M + r0 + 2*E_2*r0) + 4*L_2*r0_4*(2*M_2 + (-7 + 37*E_2)*M*r0 + 3*r0_2) + L_4*r0_2*(-64*M_2 + 9*r0_2 + 14*M*(r0 + 12*E_2*r0)))))/(3.*r0_11*expr2_2);\n    A60241 = (192*L*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*expr1_2)/(r0_2*(a_2 + r0*(-2*M + r0)));\n    A60261 = (-64*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-6*a_12*E_2*M_2*(110*M_2 + 107*M*r0 + 26*r0_2) + a_11*E*L*M*(24*(55 + E_2)*M_3 + (1307 + 24*E_2)*M_2*r0 + 6*(55 + E_2)*M*r0_2 + 3*r0_3) + a*E*L*M*r0_5*(L_4*r0_2*(80*M_2 + 2*(469 + 114*E_2)*M*r0 - 489*r0_2) + L_2*r0_4*(-476*M_2 + 2*(596 + 243*E_2)*M*r0 - 477*r0_2) + 3*r0_6*(-40*M_2 + 2*(41 + 45*E_2)*M*r0 - 31*r0_2) + 6*L_6*(64*M_2 + 10*M*r0 - 21*r0_2)) + (2*M - r0)*r0_5*(9*L_8*M*(2*M - r0) - 72*E_2*M*r0_9 + L_2*r0_6*(8*M_2 + 10*(5 - 29*E_2)*M*r0 - 27*r0_2) - 2*L_6*r0_2*(-80*M_2 + 2*(17 + 30*E_2)*M*r0 + 3*r0_2) - L_4*r0_4*(-162*M_2 + (21 + 334*E_2)*M*r0 + 30*r0_2)) + a_3*E*L*M*r0_3*(12*L_6*(114*M_2 - 17*M*r0 - 10*r0_2) + L_4*r0*(1768*M_3 + 24*(82 + 19*E_2)*M_2*r0 + 4*(241 + 114*E_2)*M*r0_2 - 975*r0_3) + r0_5*(-1668*M_3 + 2*(257 + 792*E_2)*M_2*r0 + 2*(596 + 523*E_2)*M*r0_2 - 579*r0_3) - 2*L_2*r0_3*(406*M_3 - (785 + 954*E_2)*M_2*r0 - 2*(681 + 358*E_2)*M*r0_2 + 803*r0_3)) + a_5*E*L*M*r0_2*(6*L_6*r0 + L_4*(5232*M_3 + 4*(319 + 114*E_2)*M_2*r0 + 2*(199 + 114*E_2)*M*r0_2 - 471*r0_3) + L_2*r0*(2224*M_4 + 4*(223 + 468*E_2)*M_3*r0 + 4*(1077 + 925*E_2)*M_2*r0_2 + 4*(506 + 353*E_2)*M*r0_3 - 1711*r0_4) + r0_3*(-2964*M_4 + 8*(-520 + 387*E_2)*M_3*r0 + 21*(175 + 216*E_2)*M_2*r0_2 + 6*(231 + 254*E_2)*M*r0_3 - 1104*r0_4)) + a_7*E*L*M*r0*(3*L_4*(12*M_2 + 132*M*r0 + 5*r0_2) + r0_2*(4*(-1535 + 504*E_2)*M_4 + 4*(-445 + 1434*E_2)*M_3*r0 + 3*(1753 + 1448*E_2)*M_2*r0_2 + 6*(133 + 166*E_2)*M*r0_3 - 840*r0_4) + 2*L_2*(2544*M_4 + 8*(119 + 109*E_2)*M_3*r0 + (1775 + 902*E_2)*M_2*r0_2 + (667 + 236*E_2)*M*r0_3 - 285*r0_4)) + a_9*E*L*M*(2*L_2*(36*M_3 + 2*(364 + 3*E_2)*M_2*r0 + 3*(121 + E_2)*M*r0_2 + 6*r0_3) + r0*(16*(-165 + 106*E_2)*M_4 + 888*(2 + 3*E_2)*M_3*r0 + 3*(1271 + 472*E_2)*M_2*r0_2 + 2*(344 + 127*E_2)*M*r0_3 - 219*r0_4)) - a_10*M*(L_2*(12*(55 + 6*E_2)*M_3 + (665 + 764*E_2)*M_2*r0 + 3*(58 + 121*E_2)*M*r0_2 + 3*(1 + E_2)*r0_3) + 6*E_2*r0*(-220*M_4 + 244*M_3*r0 + 438*M_2*r0_2 + 105*M*r0_3 - 12*r0_4)) - a_8*M*(L_4*(24*M_3 + 4*(179 + 9*E_2)*M_2*r0 + 9*(41 + 23*E_2)*M*r0_2 + 3*(3 + 2*E_2)*r0_3) - 12*E_2*r0_3*(348*M_4 + 86*M_3*r0 - 321*M_2*r0_2 - 84*M*r0_3 + 30*r0_4) + L_2*r0*(24*(-55 + 212*E_2)*M_4 + 4*(78 + 1237*E_2)*M_3*r0 + 2*(578 + 2141*E_2)*M_2*r0_2 + (38 + 1305*E_2)*M*r0_3 - 3*(50 + 83*E_2)*r0_4)) - a_4*r0_2*(3*L_8*M*r0 - 18*E_2*M*r0_6*(78*M_3 - 49*M_2*r0 - 34*M*r0_2 + 40*r0_3) + 2*L_6*M*(872*M_3 + 12*(-11 + 57*E_2)*M_2*r0 + 10*(1 + 12*E_2)*M*r0_2 - 3*(31 + 19*E_2)*r0_3) + L_2*r0_3*(-928*M_5 + 4*(-347 + 588*E_2)*M_4*r0 + 22*(67 + 245*E_2)*M_3*r0_2 + 3*(236 + 729*E_2)*M_2*r0_3 - 3*(150 + 547*E_2)*M*r0_4 - 45*r0_5) + L_4*r0*(104*M_5 + 4*(-96 + 923*E_2)*M_4*r0 + 72*(19 + 74*E_2)*M_3*r0_2 + (817 + 1647*E_2)*M_2*r0_3 - (753 + 934*E_2)*M*r0_4 - 15*r0_5)) - a_6*r0*(3*L_6*M*(4*M_2 + 65*M*r0 + (3 + E_2)*r0_2) - 12*E_2*M*r0_4*(246*M_4 + 294*M_3*r0 - 202*M_2*r0_2 - 73*M*r0_3 + 60*r0_4) + L_4*M*(1696*M_4 + 4*(-95 + 1308*E_2)*M_3*r0 + 76*(9 + 44*E_2)*M_2*r0_2 + 2*(128 + 425*E_2)*M*r0_3 - 3*(110 + 97*E_2)*r0_4) + L_2*r0_2*(4*(-491 + 1034*E_2)*M_5 + 4*(-58 + 1753*E_2)*M_4*r0 + 5*(405 + 1468*E_2)*M_3*r0_2 + (17 + 2063*E_2)*M_2*r0_3 - (472 + 1055*E_2)*M*r0_4 - 9*r0_5)) + a_2*r0_3*(-18*E_2*M*r0_8*(18*M_2 + 23*M*r0 - 20*r0_2) + 6*L_8*M*(-76*M_2 + 36*M*r0 + r0_2) + L_6*r0*(52*M_4 - 4*(85 + 219*E_2)*M_3*r0 - 3*(131 + 160*E_2)*M_2*r0_2 + (263 + 237*E_2)*M*r0_3 + 6*r0_4) + L_2*r0_5*(460*M_4 + 12*(19 - 140*E_2)*M_3*r0 - (535 + 1646*E_2)*M_2*r0_2 + 3*(9 + 376*E_2)*M*r0_3 + 63*r0_4) + L_4*r0_3*(944*M_4 - 8*(153 + 209*E_2)*M_2*r0_2 + (405 + 971*E_2)*M*r0_3 + 45*r0_4 - 4*M_3*(r0 + 530*E_2*r0)))))/(3.*r0_8*expr2_2);\n    A60421 = (48*L*r0*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(a_2 + r0*(-2*M + r0));\n    A60441 = (-8*(-36*a_12*E_2*M_2*(58*M_2 + 57*M*r0 + 14*r0_2) + a_11*E*L*M*(144*(29 + E_2)*M_3 + (4199 + 144*E_2)*M_2*r0 + 18*(61 + 2*E_2)*M*r0_2 + 21*r0_3) + a*E*L*M*r0_5*(3*L_4*r0_2*(-196*M_2 + 20*(54 + 11*E_2)*M*r0 - 491*r0_2) + L_6*(752*M_2 + 464*M*r0 - 420*r0_2) + 3*r0_6*(-264*M_2 + 2*(163 + 126*E_2)*M*r0 - 97*r0_2) - 39*L_2*r0_4*(56*M_2 - 2*(49 + 18*E_2)*M*r0 + 35*r0_2)) + (2*M - r0)*r0_5*(62*L_8*M*(2*M - r0) - 216*E_2*M*r0_9 + L_2*r0_6*(96*M_2 - 142*(-1 + 6*E_2)*M*r0 - 95*r0_2) - L_6*r0_2*(-604*M_2 + 52*(4 + 7*E_2)*M*r0 + 47*r0_2) - L_4*r0_4*(-588*M_2 + 4*(4 + 249*E_2)*M*r0 + 139*r0_2)) + a_5*E*L*M*r0_2*(36*L_6*(M + r0) + L_4*(14832*M_3 + 160*(27 + 8*E_2)*M_2*r0 + 4*(404 + 175*E_2)*M*r0_2 - 1399*r0_3) + L_2*r0*(3936*M_4 + 16*(16 + 315*E_2)*M_3*r0 + 8*(1711 + 1332*E_2)*M_2*r0_2 + 2*(3785 + 2154*E_2)*M*r0_3 - 4973*r0_4) + r0_3*(-8028*M_4 + 12*(-1307 + 684*E_2)*M_3*r0 + (10409 + 12816*E_2)*M_2*r0_2 + 108*(55 + 42*E_2)*M*r0_3 - 3150*r0_4)) + a_7*E*L*M*r0*(3*L_4*(96*M_2 + 4*(113 + E_2)*M*r0 + 31*r0_2) + r0_2*(12*(-1615 + 432*E_2)*M_4 + 16*(-460 + 999*E_2)*M_3*r0 + (17257 + 12816*E_2)*M_2*r0_2 + 36*(109 + 86*E_2)*M*r0_3 - 2382*r0_4) + 2*L_2*(7200*M_4 + 8*(349 + 309*E_2)*M_3*r0 + 2*(2933 + 1356*E_2)*M_2*r0_2 + (2357 + 762*E_2)*M*r0_3 - 811*r0_4)) + a_9*E*L*M*(L_2*(432*M_3 + 8*(599 + 12*E_2)*M_2*r0 + 6*(403 + 8*E_2)*M*r0_2 + 78*r0_3) + r0*(96*(-87 + 50*E_2)*M_4 + 4*(1367 + 1980*E_2)*M_3*r0 + (12535 + 4464*E_2)*M_2*r0_2 + 2*(1309 + 426*E_2)*M*r0_3 - 607*r0_4)) - a_10*M*(L_2*(72*(29 + 6*E_2)*M_3 + (2147 + 2612*E_2)*M_2*r0 + 6*(99 + 203*E_2)*M*r0_2 + 3*(7 + 6*E_2)*r0_3) - 36*E_2*r0*(116*M_4 - 124*M_3*r0 - 234*M_2*r0_2 - 59*M*r0_3 + 6*r0_4)) + a_2*r0_3*(-108*E_2*M*r0_8*(6*M_2 + 13*M*r0 - 10*r0_2) - 16*L_8*M*(80*M_2 - 34*M*r0 - 3*r0_2) + L_6*r0*(840*M_4 - 4*(293 + 562*E_2)*M_3*r0 - 2*(751 + 782*E_2)*M_2*r0_2 + (839 + 718*E_2)*M*r0_3 + 50*r0_4) + L_4*r0_3*(3408*M_4 - 8*(-82 + 693*E_2)*M_3*r0 - 2*(2215 + 2646*E_2)*M_2*r0_2 + (1123 + 2922*E_2)*M*r0_3 + 251*r0_4) + L_2*r0_5*(1560*M_4 - 8*(-175 + 522*E_2)*M_3*r0 - 12*(189 + 431*E_2)*M_2*r0_2 + (85 + 3348*E_2)*M*r0_3 + 252*r0_4)) - a_4*r0_2*(6*L_8*M*(2*M + 3*r0) - 108*E_2*M*r0_6*(42*M_3 - 19*M_2*r0 - 22*M*r0_2 + 20*r0_3) + L_6*(4944*M_4 + 24*(-23 + 160*E_2)*M_3*r0 + 4*(41 + 214*E_2)*M_2*r0_2 - 4*(145 + 84*E_2)*M*r0_3 - 3*r0_4) + L_2*r0_3*(-2424*M_5 + 4*(-1493 + 1170*E_2)*M_4*r0 + 2*(2287 + 7488*E_2)*M_3*r0_2 + (3155 + 7338*E_2)*M_2*r0_3 - 18*(83 + 273*E_2)*M*r0_4 - 222*r0_5) + L_4*r0*(-624*M_5 + 8*(-229 + 1155*E_2)*M_4*r0 + 8*(566 + 1917*E_2)*M_3*r0_2 + 2*(1552 + 2763*E_2)*M_2*r0_3 - (2341 + 2820*E_2)*M*r0_4 - 118*r0_5)) - a_6*r0*(3*L_6*M*(32*M_2 + 2*(109 + 6*E_2)*M*r0 + (19 + 6*E_2)*r0_2) - 72*E_2*M*r0_4*(114*M_4 + 162*M_3*r0 - 98*M_2*r0_2 - 47*M*r0_3 + 30*r0_4) + L_2*r0_2*(12*(-535 + 812*E_2)*M_5 + 12*(-131 + 1506*E_2)*M_4*r0 + (6991 + 22204*E_2)*M_3*r0_2 + 2*(300 + 3533*E_2)*M_2*r0_3 - 2*(764 + 1587*E_2)*M*r0_4 - 68*r0_5) + 2*L_4*(2400*M_5 + 8*(-73 + 927*E_2)*M_4*r0 + 6*(203 + 858*E_2)*M_3*r0_2 + 6*(83 + 248*E_2)*M_2*r0_3 - 13*(37 + 33*E_2)*M*r0_4 - 3*r0_5)) - a_8*(2*L_4*M*(72*M_3 + 2*(581 + 72*E_2)*M_2*r0 + 3*(206 + 125*E_2)*M*r0_2 + 6*(5 + 3*E_2)*r0_3) - 72*E_2*M*r0_3*(180*M_4 + 58*M_3*r0 - 167*M_2*r0_2 - 50*M*r0_3 + 15*r0_4) + L_2*r0*(144*(-29 + 100*E_2)*M_5 + 4*(251 + 3668*E_2)*M_4*r0 + 172*(23 + 80*E_2)*M_3*r0_2 + (351 + 4414*E_2)*M_2*r0_3 - 2*(215 + 369*E_2)*M*r0_4 - 3*r0_5)) + a_3*E*L*M*r0_3*(4*L_6*(960*M_2 - 97*M*r0 - 96*r0_2) + L_4*r0*(3360*M_3 + 4*(1463 + 312*E_2)*M_2*r0 + 4*(931 + 337*E_2)*M*r0_2 - 2965*r0_3) + 2*L_2*r0_3*(-2280*M_3 + 2*(845 + 1332*E_2)*M_2*r0 + 2*(2339 + 1059*E_2)*M*r0_2 - 2319*r0_3) + r0_5*(-5544*M_3 + 2*(2257 + 1506*E_2)*M*r0_2 - 1687*r0_3 + 120*M_2*(r0 + 36*E_2*r0)))))/(3.*r0_5*expr2_2);\n    A60601 = (4*L*r0_4*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/(a_2 + r0*(-2*M + r0));\n    A60621 = (-4*(-6*a_10*E_2*M_2*(61*M + 30*r0) + 6*a_9*E*L*M*(2*(61 + 3*E_2)*M_2 + (64 + 3*E_2)*M*r0 + 2*r0_2) + 2*a*E*L*M*r0_4*(L_2*r0_2*(-286*M_2 + (467 + 108*E_2)*M*r0 - 162*r0_2) + L_4*(40*M_2 + 142*M*r0 - 81*r0_2) + 3*r0_4*(-68*M_2 + 3*(22 + 13*E_2)*M*r0 - 16*r0_2)) + (2*M - r0)*r0_4*(35*L_6*M*(2*M - r0) - 72*E_2*M*r0_7 + 2*L_2*r0_4*(28*M_2 + (24 - 103*E_2)*M*r0 - 19*r0_2) - L_4*r0_2*(-174*M_2 + (29 + 124*E_2)*M*r0 + 29*r0_2)) + 2*a_3*E*L*M*r0_2*(L_4*(588*M_2 - 8*M*r0 - 72*r0_2) + L_2*r0*(100*M_3 + 2*(23 + 90*E_2)*M_2*r0 + 2*(408 + 115*E_2)*M*r0_2 - 347*r0_3) + r0_3*(-606*M_3 + 3*(-145 + 138*E_2)*M_2*r0 + 7*(115 + 52*E_2)*M*r0_2 - 215*r0_3)) + 2*a_5*E*L*M*r0*(9*L_4*(2*M + r0) + L_2*(1128*M_3 + 2*(125 + 98*E_2)*M_2*r0 + (501 + 128*E_2)*M*r0_2 - 170*r0_3) + 2*r0_2*(3*(-283 + 60*E_2)*M_3 + (211 + 431*E_2)*M_2*r0 + (468 + 193*E_2)*M*r0_2 - 134*r0_3)) + 2*a_7*E*L*M*(3*L_2*(18*M_2 + 2*(38 + E_2)*M*r0 + 5*r0_2) + r0*(4*(-183 + 94*E_2)*M_3 + (831 + 466*E_2)*M_2*r0 + (545 + 148*E_2)*M*r0_2 - 95*r0_3)) - 3*a_8*M*(L_2*(2*(61 + 18*E_2)*M_2 + 17*(4 + 5*E_2)*M*r0 + (4 + 3*E_2)*r0_2) + 2*E_2*r0*(-122*M_3 + 187*M_2*r0 + 125*M*r0_2 - 12*r0_3)) - a_6*(3*L_4*M*(12*M_2 + (73 + 12*E_2)*M*r0 + (7 + 3*E_2)*r0_2) + 6*E_2*M*r0_3*(-372*M_3 + 101*M_2*r0 + 203*M*r0_2 - 48*r0_3) + L_2*r0*(12*(-61 + 188*E_2)*M_4 + 4*(135 + 412*E_2)*M_3*r0 + (299 + 1010*E_2)*M_2*r0_2 - 2*(71 + 90*E_2)*M*r0_3 - 3*r0_4)) + a_4*r0*(-3*L_6*M*(4*M + 3*r0) + 18*E_2*M*r0_4*(70*M_3 + 21*M_2*r0 - 57*M*r0_2 + 24*r0_3) + L_4*(-752*M_4 - 24*(-9 + 49*E_2)*M_3*r0 - 8*(36 + 47*E_2)*M_2*r0_2 + (181 + 114*E_2)*M*r0_3 + 3*r0_4) + L_2*r0_2*(-4*(-291 + 295*E_2)*M_4 - 18*(29 + 106*E_2)*M_3*r0 - (790 + 1623*E_2)*M_2*r0_2 + (330 + 593*E_2)*M*r0_3 + 38*r0_4)) + a_2*r0_2*(2*L_6*M*(-196*M_2 + 68*M*r0 + 15*r0_2) + 18*E_2*M*r0_6*(10*M_2 - 29*M*r0 + 16*r0_2) + L_4*r0*(260*M_4 + (92 - 580*E_2)*M_3*r0 - (689 + 604*E_2)*M_2*r0_2 + (225 + 247*E_2)*M*r0_3 + 32*r0_4) + L_2*r0_3*(356*M_4 + (548 - 460*E_2)*M_3*r0 - (783 + 1280*E_2)*M_2*r0_2 + 2*(32 + 305*E_2)*M*r0_3 + 73*r0_4))))/(3.*r0_2*expr2_2);\n    A60801 = -(r0*(-96*a_8*E_2*M_2 + 3*a_7*E*L*M*(4*(16 + E_2)*M + 3*r0) + a_5*E*L*M*(12*L_2*(3*M + r0) + r0*(16*(-24 + 11*E_2)*M_2 + 4*(155 + 37*E_2)*M*r0 - 91*r0_2)) + a_3*E*L*M*r0*(r0_2*(4*(-223 + 36*E_2)*M_2 + 4*(196 + 61*E_2)*M*r0 - 145*r0_2) + L_2*(528*M_2 + 68*M*r0 - 84*r0_2)) + (2*M - r0)*r0_3*(26*L_4*M*(2*M - r0) - 36*E_2*M*r0_5 + L_2*r0_2*(40*M_2 + (26 - 64*E_2)*M*r0 - 23*r0_2)) - 3*a_6*M*(L_2*(4*(8 + 3*E_2)*M + (3 + 2*E_2)*r0) - 4*E_2*r0*(16*M_2 - 32*M*r0 + 3*r0_2)) + a*E*L*M*r0_3*(3*r0_2*(-92*M_2 + 4*(19 + 9*E_2)*M*r0 - 15*r0_2) - 32*L_2*(2*M_2 - 7*M*r0 + 3*r0_2)) + a_4*(-6*L_4*M*(2*M + r0) + 36*E_2*M*r0_3*(16*M_2 - 14*M*r0 + 3*r0_2) + L_2*r0*(-48*(-4 + 11*E_2)*M_3 - 4*(59 + 64*E_2)*M_2*r0 + 10*(7 + 6*E_2)*M*r0_2 + 3*r0_3)) + a_2*r0*(-8*L_4*M*(22*M_2 - 5*M*r0 - 3*r0_2) + 36*E_2*M*r0_4*(8*M_2 - 8*M*r0 + 3*r0_2) + L_2*r0_2*(-4*(-79 + 46*E_2)*M_3 - 4*(89 + 91*E_2)*M_2*r0 + (47 + 130*E_2)*M*r0_2 + 26*r0_3))))/(6.*expr2_2);\n    A61061 = (128*expr1_2*(2*a_8*E_2*M_2*(2*M + r0) - a_7*E*L*M*(8*M_2 + 5*M*r0 + r0_2) + a*E*L*M*r0_3*(L_4*(4*M - 6*r0) + L_2*(4*M - 9*r0)*r0_2 + 6*r0_4*(-3*M + r0)) - a_5*E*L*M*r0*(L_2*(12*M + 7*r0) - 2*r0*(-17*M_2 - 4*M*r0 + r0_2)) + L_2*r0_3*(2*E_2*L_2*M*r0_3 + L_4*M*(-2*M + r0) + r0_4*(6*M_2 + (-5 + 2*E_2)*M*r0 + r0_2)) - a_3*E*L*M*r0_2*(6*L_4 + r0_2*(54*M_2 + 17*M*r0 - 9*r0_2) + L_2*(-8*M_2 + 4*M*r0 + 12*r0_2)) + a_6*M*(2*E_2*M*r0_2*(12*M + 7*r0) + L_2*(4*M_2 + 3*M*r0 + 6*E_2*M*r0 + r0_2 + 3*E_2*r0_2)) + a_4*r0*(6*E_2*M_2*r0_3*(6*M + 5*r0) + L_4*M*(6*M + (4 + 3*E_2)*r0) + L_2*r0*((10 - 4*E_2)*M_3 + (-5 + 8*E_2)*M_2*r0 + (3 + 8*E_2)*M*r0_2 + 2*r0_3)) + a_2*r0_2*(3*L_6*M + 18*E_2*M_2*r0_5 + L_4*(-4*M_3 - 2*(2 + E_2)*M_2*r0 + (4 + 5*E_2)*M*r0_2 + 2*r0_3) + L_2*r0_2*(24*M_3 + 2*(-5 + E_2)*M_2*r0 + (-5 + 7*E_2)*M*r0_2 + 3*r0_3))))/(r0_7*expr2_2);\n    A61241 = (32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(6*a_8*E_2*M_2*(2*M + r0) - 2*a_7*E*L*M*(12*M_2 + 9*M*r0 + 2*r0_2) + 6*a*E*L*M*r0_3*(L_4*(2*M - 3*r0) + 3*r0_4*(-3*M + r0) - L_2*r0_2*(M + 3*r0)) - 2*a_5*E*L*M*r0*(L_2*(20*M + 11*r0) + r0*(48*M_2 + 17*M*r0 - r0_2)) + L_2*r0_3*(3*L_4*M*(-2*M + r0) + 3*r0_4*(6*M_2 + (-5 + 2*E_2)*M*r0 + r0_2) + L_2*r0_2*(4*M_2 + 2*(-2 + 3*E_2)*M*r0 + r0_2)) - 6*a_3*E*L*M*r0_2*(3*L_4 + r0_2*(24*M_2 + 9*M*r0 - 4*r0_2) + L_2*(-4*M_2 + 5*M*r0 + 6*r0_2)) + a_6*M*(6*E_2*M*r0_2*(12*M + 7*r0) + L_2*(12*M_2 + 4*(3 + 5*E_2)*M*r0 + (4 + 9*E_2)*r0_2)) + a_4*r0*(18*E_2*M_2*r0_3*(6*M + 5*r0) + L_4*M*(20*M + (13 + 9*E_2)*r0) + L_2*r0*(-12*(-2 + E_2)*M_3 + 4*(-2 + 9*E_2)*M_2*r0 + 3*(3 + 8*E_2)*M*r0_2 + 5*r0_3)) + a_2*r0_2*(9*L_6*M + 54*E_2*M_2*r0_5 + L_4*(-12*M_3 - 6*(1 + E_2)*M_2*r0 + (11 + 15*E_2)*M*r0_2 + 5*r0_3) + L_2*r0_2*(60*M_3 + 2*(-11 + 12*E_2)*M_2*r0 + 7*(-2 + 3*E_2)*M*r0_2 + 8*r0_3))))/(r0_4*expr2_2);\n    A61421 = (8*(6*a_8*E_2*M_2*(2*M + r0) - a_7*E*L*M*(24*M_2 + 21*M*r0 + 5*r0_2) + 3*a*E*L*M*r0_3*(L_4*(4*M - 6*r0) + 6*r0_4*(-3*M + r0) - L_2*r0_2*(8*M + 3*r0)) - a_5*E*L*M*r0*(L_2*(44*M + 23*r0) + 2*r0*(45*M_2 + 22*M*r0 + r0_2)) + L_2*r0_3*(3*L_4*M*(-2*M + r0) + 3*r0_4*(6*M_2 + (-5 + 2*E_2)*M*r0 + r0_2) + 2*L_2*r0_2*(4*M_2 + (-4 + 3*E_2)*M*r0 + r0_2)) - 3*a_3*E*L*M*r0_2*(6*L_4 + r0_2*(42*M_2 + 19*M*r0 - 7*r0_2) + L_2*(-8*M_2 + 16*M*r0 + 12*r0_2)) + a_6*M*(6*E_2*M*r0_2*(12*M + 7*r0) + L_2*(12*M_2 + (15 + 22*E_2)*M*r0 + (5 + 9*E_2)*r0_2)) + a_4*r0*(18*E_2*M_2*r0_3*(6*M + 5*r0) + L_4*M*(22*M + (14 + 9*E_2)*r0) + L_2*r0*(-6*(-3 + 2*E_2)*M_3 + (-1 + 48*E_2)*M_2*r0 + 3*(3 + 8*E_2)*M*r0_2 + 4*r0_3)) + a_2*r0_2*(9*L_6*M + 54*E_2*M_2*r0_5 + L_4*(-12*M_3 - 6*E_2*M_2*r0 + 5*(2 + 3*E_2)*M*r0_2 + 4*r0_3) + L_2*r0_2*(48*M_3 + 14*(-1 + 3*E_2)*M_2*r0 + (-13 + 21*E_2)*M*r0_2 + 7*r0_3))))/(r0*expr2_2);\n    A61601 = (2*r0_2*(2*a_6*E_2*M_2 - 2*a_5*E*L*M*(2*M + r0) - 2*a_3*E*L*M*r0*(3*L_2 + 7*M*r0) + 2*a*E*L*M*r0_2*(L_2*(2*M - 3*r0) + 3*r0_2*(-3*M + r0)) + a_4*M*(12*E_2*M*r0_2 + L_2*(2*M + 2*r0 + 3*E_2*r0)) + L_2*r0_2*(L_2*M*(-2*M + r0) + r0_2*(6*M_2 + (-5 + 2*E_2)*M*r0 + r0_2)) + a_2*r0*(3*L_4*M + 18*E_2*M_2*r0_3 + L_2*r0*(-2*(-1 + E_2)*M_2 + (-1 + 5*E_2)*M*r0 + r0_2))))/expr2_2;\n    A62041 = (192*L*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*expr1_2)/(r0_2*expr2_2);\n    A62061 = (-32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-12*a_12*E_2*M_2*(92*M_2 + 88*M*r0 + 21*r0_2) + a_11*E*L*M*(-24*(-92 + 11*E_2)*M_3 + (2137 - 300*E_2)*M_2*r0 + 6*(85 - 14*E_2)*M*r0_2 - 6*r0_3) + a*E*L*M*r0_5*(2*L_4*r0_2*(584*M_2 + (557 + 192*E_2)*M*r0 - 474*r0_2) + 2*L_2*r0_4*(250*M_2 + (571 + 360*E_2)*M*r0 - 393*r0_2) + 3*r0_6*(-16*M_2 + 8*(14 + 15*E_2)*M*r0 - 61*r0_2) + 12*L_6*(88*M_2 - 8*M*r0 - 21*r0_2)) + a_3*E*L*M*r0_3*(12*L_6*(234*M_2 - 57*M*r0 - 22*r0_2) + 2*L_2*r0_3*(2134*M_3 + (1219 + 1800*E_2)*M_2*r0 + 10*(117 + 94*E_2)*M*r0_2 - 1459*r0_3) + r0_5*(-2820*M_3 + 2*(805 + 1818*E_2)*M_2*r0 + 4*(404 + 301*E_2)*M*r0_2 - 1227*r0_3) + 2*L_4*r0*(2704*M_3 + 12*(151 + 41*E_2)*M_2*r0 + 2*(38 + 177*E_2)*M*r0_2 - 909*r0_3)) + r0_5*(18*L_8*M_2*(-2*M + r0) + 144*E_2*M*r0_9*(-2*M + r0) + 2*L_6*M*r0_2*(158*M_2 - (167 + 210*E_2)*M*r0 + 2*(22 + 57*E_2)*r0_2) + 2*L_4*r0_4*(174*M_3 - (81 + 584*E_2)*M_2*r0 + 5*(-9 + 62*E_2)*M*r0_2 + 21*r0_3) + L_2*r0_6*(20*M_3 - 2*(-77 + 526*E_2)*M_2*r0 + 32*(-5 + 17*E_2)*M*r0_2 + 39*r0_3)) + a_5*E*L*M*r0_2*(-12*L_6*(9*M + r0) + 2*L_4*(5520*M_3 + 4*(76 + 117*E_2)*M_2*r0 + (-337 + 144*E_2)*M*r0_2 - 486*r0_3) + r0_3*(-6636*M_4 + 8*(-626 + 1035*E_2)*M_3*r0 + 3*(2363 + 2896*E_2)*M_2*r0_2 + 6*(173 + 228*E_2)*M*r0_3 - 2379*r0_4) + 2*L_2*r0*(3520*M_4 + 2*(2129 + 1080*E_2)*M_3*r0 + 4*(513 + 790*E_2)*M_2*r0_2 + 4*(77 + 185*E_2)*M*r0_3 - 1651*r0_4)) + a_7*E*L*M*r0*(-6*L_4*(120*M_2 + 6*(-8 + E_2)*M*r0 + 5*r0_2) + r0_2*((-9700 + 4896*E_2)*M_4 + 4*(-125 + 3114*E_2)*M_3*r0 + 3*(2803 + 2152*E_2)*M_2*r0_2 + 6*(-13 + 80*E_2)*M*r0_3 - 1815*r0_4) + 2*L_2*(5520*M_4 + 4*(443 + 460*E_2)*M_3*r0 + (1309 + 1240*E_2)*M_2*r0_2 + 2*(181 + 50*E_2)*M*r0_3 - 597*r0_4)) + a_9*E*L*M*(-2*L_2*(396*M_3 + 10*(-73 + 12*E_2)*M_2*r0 + 3*(-151 + 20*E_2)*M*r0_2 + 12*r0_3) + r0*(32*(-129 + 115*E_2)*M_4 + 264*(13 + 17*E_2)*M_3*r0 + 3*(1985 + 368*E_2)*M_2*r0_2 + 2*(337 - 64*E_2)*M*r0_3 - 486*r0_4)) + a_10*M*(L_2*(24*(-46 + 33*E_2)*M_3 - (1081 + 280*E_2)*M_2*r0 - 6*(43 + 54*E_2)*M*r0_2 + 6*(1 + E_2)*r0_3) + 12*E_2*r0*(172*M_4 - 228*M_3*r0 - 357*M_2*r0_2 - 74*M*r0_3 + 12*r0_4)) + a_2*r0_3*(-36*E_2*M*r0_8*(27*M_2 + 14*M*r0 - 20*r0_2) + 18*L_8*M*(-52*M_2 + 27*M*r0 + r0_2) - 2*L_6*r0*(272*M_4 + 2*(146 + 501*E_2)*M_3*r0 + 3*(64 + 105*E_2)*M_2*r0_2 - (203 + 234*E_2)*M*r0_3 - 18*r0_4) + 2*L_4*r0_3*(188*M_4 - 242*(-1 + 10*E_2)*M_3*r0 - (927 + 994*E_2)*M_2*r0_2 + (315 + 908*E_2)*M*r0_3 + 48*r0_4) + L_2*r0_5*(824*M_4 - 48*(-5 + 109*E_2)*M_3*r0 - 2*(430 + 647*E_2)*M_2*r0_2 + 3*(31 + 704*E_2)*M*r0_3 + 96*r0_4)) + a_8*M*(2*L_4*(132*M_3 + 40*(-11 + 9*E_2)*M_2*r0 + 3*(-83 + 6*E_2)*M*r0_2 + 3*(3 + 2*E_2)*r0_3) + 24*E_2*r0_3*(276*M_4 + 8*M_3*r0 - 278*M_2*r0_2 - 47*M*r0_3 + 30*r0_4) + L_2*r0*(-48*(-43 + 230*E_2)*M_4 - 8*(87 + 1063*E_2)*M_3*r0 - 2*(823 + 1786*E_2)*M_2*r0_2 + 19*(11 - 30*E_2)*M*r0_3 + 6*(56 + 83*E_2)*r0_4)) + a_6*r0*(6*L_6*M*(40*M_2 + 2*(-17 + 9*E_2)*M*r0 + (3 + E_2)*r0_2) + 24*E_2*M*r0_4*(246*M_4 + 222*M_3*r0 - 181*M_2*r0_2 - 22*M*r0_3 + 60*r0_4) - 2*L_4*M*(1840*M_4 + 4*(-59 + 1380*E_2)*M_3*r0 + (75 + 2012*E_2)*M_2*r0_2 + 2*(2 - 49*E_2)*M*r0_3 - 15*(23 + 20*E_2)*r0_4) + L_2*r0_2*(-4*(-769 + 2716*E_2)*M_5 - 4*(175 + 4916*E_2)*M_4*r0 - (3129 + 9496*E_2)*M_3*r0_2 + 2*(226 + 25*E_2)*M_2*r0_3 + (983 + 2002*E_2)*M*r0_4 + 18*r0_5)) + a_4*r0_2*(6*L_8*M*(6*M + r0) + 36*E_2*M*r0_6*(60*M_3 - 52*M_2*r0 - 7*M*r0_2 + 40*r0_3) + 2*L_6*M*(-1840*M_3 - 468*(-1 + 3*E_2)*M_2*r0 + (139 - 45*E_2)*M*r0_2 + 3*(62 + 41*E_2)*r0_3) - 2*L_4*r0*(536*M_5 + 2*(327 + 2296*E_2)*M_4*r0 + 3*(203 + 1560*E_2)*M_3*r0_2 + 5*(101 + 66*E_2)*M_2*r0_3 - 4*(177 + 223*E_2)*M*r0_4 - 27*r0_5) + L_2*r0_3*(2360*M_5 - 8*(-161 + 1434*E_2)*M_4*r0 - 4*(665 + 2563*E_2)*M_3*r0_2 + 27*(-35 + 2*E_2)*M_2*r0_3 + 6*(151 + 513*E_2)*M*r0_4 + 75*r0_5))))/(3.*r0_8*expr2_3);\n    A62221 = (96*L*r0*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/expr2_2;\n    A62241 = (-8*(-48*a_12*E_2*M_2*(72*M_2 + 70*M*r0 + 17*r0_2) + a_11*E*L*M_2*(-144*(-48 + 5*E_2)*M_2 + (6841 - 792*E_2)*M*r0 + 6*(283 - 36*E_2)*r0_2) + a*E*L*M*r0_5*(12*L_4*r0_2*(227*M_2 + (269 + 83*E_2)*M*r0 - 216*r0_2) + 4*L_6*(664*M_2 - 8*M*r0 - 189*r0_2) + 9*r0_6*(-112*M_2 + 12*(14 + 9*E_2)*M*r0 - 65*r0_2) - 24*L_2*r0_4*(21*M_2 - (182 + 81*E_2)*M*r0 + 97*r0_2)) + a_3*E*L*M*r0_3*(8*L_6*(1005*M_2 - 259*M*r0 - 99*r0_2) + 2*L_2*r0_3*(1896*M_3 + 2*(1897 + 2727*E_2)*M_2*r0 + (4915 + 2544*E_2)*M*r0_2 - 4447*r0_3) + 4*L_4*r0*(3192*M_3 + (3127 + 714*E_2)*M_2*r0 + (217 + 440*E_2)*M*r0_2 - 1419*r0_3) + 3*r0_5*(-3096*M_3 + 4*(181 + 756*E_2)*M_2*r0 + 20*(111 + 56*E_2)*M*r0_2 - 1213*r0_3)) + r0_5*(432*E_2*M*r0_9*(-2*M + r0) + 2*L_8*M*(50*M_2 - 77*M*r0 + 26*r0_2) + 2*L_6*r0_2*(662*M_3 - (569 + 614*E_2)*M_2*r0 + (59 + 334*E_2)*M*r0_2 + 30*r0_3) + 6*L_4*r0_4*(258*M_3 - (113 + 572*E_2)*M_2*r0 + 76*(-1 + 4*E_2)*M*r0_2 + 34*r0_3) + 3*L_2*r0_6*(116*M_3 + 2*(45 - 514*E_2)*M_2*r0 + 28*(-6 + 19*E_2)*M*r0_2 + 47*r0_3)) + a_5*E*L*M*r0_2*(-36*L_6*(12*M + r0) + 4*L_4*(7920*M_3 + 5*(159 + 134*E_2)*M_2*r0 + (-488 + 155*E_2)*M*r0_2 - 753*r0_3) + 2*L_2*r0*(7680*M_4 + 16*(523 + 378*E_2)*M_3*r0 + 6*(1360 + 1549*E_2)*M_2*r0_2 + (2077 + 1992*E_2)*M*r0_3 - 5062*r0_4) + 3*r0_3*(-5244*M_4 + 4*(-1765 + 1728*E_2)*M_3*r0 + 3*(2187 + 2504*E_2)*M_2*r0_2 + 14*(151 + 96*E_2)*M*r0_3 - 2299*r0_4)) + a_7*E*L*M*r0*(-12*L_4*(171*M_2 + 4*(-17 + 3*E_2)*M*r0 + 6*r0_2) + 2*L_2*(15696*M_4 + 32*(149 + 165*E_2)*M_3*r0 + 2*(2629 + 1821*E_2)*M_2*r0_2 + 5*(307 + 48*E_2)*M*r0_3 - 1797*r0_4) + 3*r0_2*(12*(-823 + 360*E_2)*M_4 + 16*(-140 + 687*E_2)*M_3*r0 + 21*(433 + 280*E_2)*M_2*r0_2 + 2*(415 + 276*E_2)*M*r0_3 - 1739*r0_4)) + a_9*E*L*M*(-2*L_2*(1080*M_3 + 2*(-1232 + 171*E_2)*M_2*r0 + 3*(-491 + 60*E_2)*M*r0_2 + 18*r0_3) + r0*(96*(-135 + 109*E_2)*M_4 + 44*(227 + 288*E_2)*M_3*r0 + (19657 + 3384*E_2)*M_2*r0_2 - 6*(-499 + 34*E_2)*M*r0_3 - 1374*r0_4)) + a_10*M*(L_2*(432*(-8 + 5*E_2)*M_3 - (3481 + 1276*E_2)*M_2*r0 - 18*(49 + 62*E_2)*M*r0_2 + 18*E_2*r0_3) + 12*E_2*r0*(540*M_4 - 664*M_3*r0 - 1125*M_2*r0_2 - 253*M*r0_3 + 36*r0_4)) + a_2*r0_3*(-108*E_2*M*r0_8*(21*M_2 + 17*M*r0 - 20*r0_2) + 2*L_8*M*(-1340*M_2 + 673*M*r0 + 39*r0_2) - 2*L_6*r0*(168*M_4 + 2*(613 + 1403*E_2)*M_3*r0 + (717 + 787*E_2)*M_2*r0_2 - 2*(340 + 341*E_2)*M*r0_3 - 57*r0_4) + 6*L_4*r0_3*(576*M_4 + (212 - 2450*E_2)*M_3*r0 - 3*(373 + 280*E_2)*M_2*r0_2 + 36*(9 + 25*E_2)*M*r0_3 + 71*r0_4) + 3*L_2*r0_5*(960*M_4 + (632 - 4308*E_2)*M_3*r0 - 2*(629 + 789*E_2)*M_2*r0_2 + (109 + 2092*E_2)*M*r0_3 + 130*r0_4)) + a_8*(2*L_4*M*(360*M_3 + 2*(-715 + 513*E_2)*M_2*r0 + 3*(-269 + 22*E_2)*M*r0_2 + 9*(1 + 2*E_2)*r0_3) + 48*E_2*M*r0_3*(414*M_4 + 63*M_3*r0 - 416*M_2*r0_2 - 89*M*r0_3 + 45*r0_4) + L_2*r0*(-144*(-45 + 218*E_2)*M_5 - 4*(505 + 5944*E_2)*M_4*r0 - 68*(87 + 179*E_2)*M_3*r0_2 - 7*(-43 + 350*E_2)*M_2*r0_3 + 6*(175 + 257*E_2)*M*r0_4 + 18*r0_5)) + a_4*r0_2*(18*L_8*M*(8*M + r0) + 432*E_2*M*r0_6*(15*M_3 - 10*M_2*r0 - 4*M*r0_2 + 10*r0_3) + 2*L_6*(-5280*M_4 - 6*(-171 + 670*E_2)*M_3*r0 + (388 + 53*E_2)*M_2*r0_2 + 3*(205 + 119*E_2)*M*r0_3 + 9*r0_4) - 2*L_4*r0*(600*M_5 + 4*(157 + 3066*E_2)*M_4*r0 + 2*(1462 + 7161*E_2)*M_3*r0_2 + 2*(998 + 405*E_2)*M_2*r0_3 - 6*(379 + 447*E_2)*M*r0_4 - 129*r0_5) + 3*L_2*r0_3*(1720*M_5 + 4*(685 - 2022*E_2)*M_4*r0 - 2*(1403 + 4574*E_2)*M_3*r0_2 - (1591 + 694*E_2)*M_2*r0_3 + 2*(502 + 1545*E_2)*M*r0_4 + 125*r0_5)) + a_6*r0*(6*L_6*M*(114*M_2 + 2*(-49 + 36*E_2)*M*r0 + 3*(2 + E_2)*r0_2) + 216*E_2*M*r0_4*(70*M_4 + 80*M_3*r0 - 55*M_2*r0_2 - 13*M*r0_3 + 20*r0_4) - 2*L_4*(5232*M_5 + 16*(-49 + 990*E_2)*M_4*r0 + 6*(144 + 1043*E_2)*M_3*r0_2 + (103 - 348*E_2)*M_2*r0_3 - 3*(367 + 304*E_2)*M*r0_4 - 18*r0_5) + L_2*r0_2*((9756 - 27120*E_2)*M_5 + (508 - 48456*E_2)*M_4*r0 - (11533 + 28712*E_2)*M_3*r0_2 + 2*(125 - 883*E_2)*M_2*r0_3 + 3*(1077 + 2038*E_2)*M*r0_4 + 144*r0_5))))/(3.*r0_5*expr2_3);\n    A62401 = (12*L*r0_4*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/expr2_2;\n    A62421 = (-2*(-12*a_10*E_2*M_2*(150*M + 73*r0) - 3*a_9*E*L*M*(12*(-100 + 9*E_2)*M_2 + 6*(-101 + 10*E_2)*M*r0 - 5*r0_2) + 3*a*E*L*M*r0_4*(L_2*r0_2*(-212*M_2 + 2*(533 + 144*E_2)*M*r0 - 543*r0_2) + r0_4*(-624*M_2 + 4*(163 + 72*E_2)*M*r0 - 197*r0_2) + 4*L_4*(160*M_2 + 36*M*r0 - 67*r0_2)) + a_5*E*L*M*r0*(-36*L_4*(13*M + r0) + L_2*(14832*M_3 + 36*(9 + 70*E_2)*M_2*r0 + 6*(379 + 80*E_2)*M*r0_2 - 2231*r0_3) + 2*r0_2*(6*(-1273 + 468*E_2)*M_3 + 2*(1364 + 2253*E_2)*M_2*r0 + 3*(1317 + 286*E_2)*M*r0_2 - 1808*r0_3)) + a_3*E*L*M*r0_2*(60*L_4*(126*M_2 - 31*M*r0 - 14*r0_2) + L_2*r0*(4800*M_3 + 12*(221 + 222*E_2)*M_2*r0 + 4*(1237 + 375*E_2)*M*r0_2 - 4127*r0_3) + 2*r0_3*(-3186*M_3 + 3*(-473 + 918*E_2)*M_2*r0 + 11*(349 + 102*E_2)*M*r0_2 - 1444*r0_3)) + a_7*E*L*M*(-3*L_2*(324*M_2 + 4*(-113 + 13*E_2)*M*r0 + 7*r0_2) + 2*r0*(24*(-141 + 103*E_2)*M_3 + 3*(1393 + 578*E_2)*M_2*r0 + (2371 + 78*E_2)*M*r0_2 - 652*r0_3)) + 3*a_8*M*(L_2*(12*(-50 + 27*E_2)*M_2 - 2*(157 + 68*E_2)*M*r0 + (-5 + 6*E_2)*r0_2) + 4*E_2*r0*(282*M_3 - 463*M_2*r0 - 283*M*r0_2 + 36*r0_3)) + r0_4*(432*E_2*M*r0_7*(-2*M + r0) + 6*L_6*M*(62*M_2 - 71*M*r0 + 20*r0_2) + L_4*r0_2*(1416*M_3 - 4*(221 + 303*E_2)*M_2*r0 + 2*(-89 + 330*E_2)*M*r0_2 + 133*r0_3) + L_2*r0_4*(636*M_3 + 2*(59 - 1074*E_2)*M_2*r0 + 8*(-71 + 141*E_2)*M*r0_2 + 175*r0_3)) + a_6*(3*L_4*M*(108*M_2 + 4*(-64 + 39*E_2)*M*r0 + (1 + 6*E_2)*r0_2) + 36*E_2*M*r0_3*(276*M_3 - 109*M_2*r0 - 145*M*r0_2 + 48*r0_3) + L_2*r0*(-72*(-47 + 206*E_2)*M_4 - 6*(467 + 894*E_2)*M_3*r0 - 2*(580 + 759*E_2)*M_2*r0_2 + (1019 + 1140*E_2)*M*r0_3 + 33*r0_4)) + a_4*r0*(6*L_6*M*(26*M + 3*r0) + 108*E_2*M*r0_4*(58*M_3 + 11*M_2*r0 - 37*M*r0_2 + 24*r0_3) + L_4*(-4944*M_4 + 12*(131 - 630*E_2)*M_3*r0 + 6*(-152 + 35*E_2)*M_2*r0_2 + 2*(586 + 357*E_2)*M*r0_3 + 33*r0_4) + L_2*r0_2*(-12*(-445 + 902*E_2)*M_4 - 2*(1537 + 5850*E_2)*M_3*r0 - 2*(1931 + 1608*E_2)*M_2*r0_2 + (2053 + 3354*E_2)*M*r0_3 + 211*r0_4)) + a_2*r0_2*(-18*L_6*M*(140*M_2 - 65*M*r0 - 7*r0_2) + 108*E_2*M*r0_6*(M_2 - 20*M*r0 + 16*r0_2) + L_4*r0*(408*M_4 + 12*(3 - 413*E_2)*M_3*r0 - 2*(1448 + 753*E_2)*M_2*r0_2 + 3*(397 + 452*E_2)*M*r0_3 + 166*r0_4) + L_2*r0_3*(2016*M_4 - 28*(-86 + 213*E_2)*M_3*r0 - 2*(2017 + 1767*E_2)*M_2*r0_2 + 7*(73 + 480*E_2)*M*r0_3 + 353*r0_4))))/(3.*r0_2*expr2_3);\n    A62601 = -(r0*(-312*a_8*E_2*M_2 + 3*a_7*E*L*M*(-16*(-13 + E_2)*M + 3*r0) + a_5*E*L*M*(-12*L_2*(12*M + r0) + r0*(8*(-147 + 97*E_2)*M_2 + 4*(506 + 37*E_2)*M*r0 - 419*r0_2)) + a*E*L*M*r0_3*(4*L_2*(80*M_2 + 92*M*r0 - 75*r0_2) + 3*r0_2*(-304*M_2 + 4*(65 + 21*E_2)*M*r0 - 63*r0_2)) + a_3*E*L*M*r0*(r0_2*(8*(-332 + 99*E_2)*M_2 + 28*(93 + 16*E_2)*M*r0 - 689*r0_2) + 8*L_2*(291*M_2 - 59*M*r0 - 39*r0_2)) + 3*a_6*M*(L_2*(8*(-13 + 6*E_2)*M + (-3 + 2*E_2)*r0) + 4*E_2*r0*(49*M_2 - 101*M*r0 + 12*r0_2)) + a_4*(6*L_4*M*(8*M + r0) + 72*E_2*M*r0_3*(23*M_2 - 22*M*r0 + 6*r0_2) + L_2*r0*((588 - 2328*E_2)*M_3 + 14*(-58 + E_2)*M_2*r0 + 2*(157 + 123*E_2)*M*r0_2 + 15*r0_3)) + r0_3*(144*E_2*M*r0_5*(-2*M + r0) + 2*L_4*M*(118*M_2 - 127*M*r0 + 34*r0_2) + L_2*r0_2*(308*M_3 + (2 - 404*E_2)*M_2*r0 + 4*(-56 + 55*E_2)*M*r0_2 + 73*r0_3)) + a_2*r0*(36*E_2*M*r0_4*(23*M_2 - 23*M*r0 + 12*r0_2) + L_4*(-776*M_3 + 310*M_2*r0 + 66*M*r0_2) + L_2*r0_2*(-4*(-250 + 337*E_2)*M_3 - 2*(623 + 281*E_2)*M_2*r0 + 5*(43 + 92*E_2)*M*r0_2 + 88*r0_3))))/(6.*expr2_3);\n    A63041 = (32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(6*a_8*E_2*M_2*(2*M + r0) - 2*a_7*E*L*M*(12*M_2 + 9*M*r0 + 2*r0_2) + 3*a*E*L*M*r0_3*(L_4*(4*M - 6*r0) - L_2*r0_2*(3*M + 5*r0) + r0_4*(-19*M + 7*r0)) - a_5*E*L*M*r0*(L_2*(40*M + 22*r0) + r0*(98*M_2 + 33*M*r0 - 3*r0_2)) + L_2*r0_3*(3*L_4*M*(-2*M + r0) + 2*r0_4*(10*M_2 + 3*(-3 + E_2)*M*r0 + 2*r0_2) + L_2*r0_2*(6*M_2 + (-7 + 6*E_2)*M*r0 + 2*r0_2)) - a_3*E*L*M*r0_2*(18*L_4 + 2*r0_2*(75*M_2 + 26*M*r0 - 14*r0_2) + L_2*(-24*M_2 + 31*M*r0 + 35*r0_2)) + a_6*M*(6*E_2*M*r0_2*(12*M + 7*r0) + L_2*(12*M_2 + 4*(3 + 5*E_2)*M*r0 + (4 + 9*E_2)*r0_2)) + a_4*r0*(18*E_2*M_2*r0_3*(6*M + 5*r0) + L_4*M*(20*M + (13 + 9*E_2)*r0) + L_2*r0*((26 - 12*E_2)*M_3 + 9*(-1 + 4*E_2)*M_2*r0 + 8*(1 + 3*E_2)*M*r0_2 + 5*r0_3)) + a_2*r0_2*(9*L_6*M + 54*E_2*M_2*r0_5 + L_4*(-12*M_3 - (5 + 6*E_2)*M_2*r0 + 5*(2 + 3*E_2)*M*r0_2 + 5*r0_3) + L_2*r0_2*(64*M_3 + (-25 + 24*E_2)*M_2*r0 + (-16 + 21*E_2)*M*r0_2 + 9*r0_3))))/(r0_4*expr2_3);\n    A63221 = (16*(6*a_8*E_2*M_2*(2*M + r0) - a_7*E*L*M*(24*M_2 + 21*M*r0 + 5*r0_2) + 3*a*E*L*M*r0_3*(L_4*(4*M - 6*r0) - L_2*r0_2*(9*M + 2*r0) + r0_4*(-19*M + 7*r0)) - a_5*E*L*M*r0*(L_2*(44*M + 23*r0) + r0*(92*M_2 + 43*M*r0 + r0_2)) + L_2*r0_3*(3*L_4*M*(-2*M + r0) + 2*r0_4*(10*M_2 + 3*(-3 + E_2)*M*r0 + 2*r0_2) + L_2*r0_2*(10*M_2 + (-11 + 6*E_2)*M*r0 + 3*r0_2)) - a_3*E*L*M*r0_2*(18*L_4 + r0_2*(132*M_2 + 55*M*r0 - 25*r0_2) + L_2*(-24*M_2 + 49*M*r0 + 35*r0_2)) + a_6*M*(6*E_2*M*r0_2*(12*M + 7*r0) + L_2*(12*M_2 + (15 + 22*E_2)*M*r0 + (5 + 9*E_2)*r0_2)) + a_4*r0*(18*E_2*M_2*r0_3*(6*M + 5*r0) + L_4*M*(22*M + (14 + 9*E_2)*r0) + 2*L_2*r0*((10 - 6*E_2)*M_3 + (-1 + 24*E_2)*M_2*r0 + 4*(1 + 3*E_2)*M*r0_2 + 2*r0_3)) + a_2*r0_2*(9*L_6*M + 54*E_2*M_2*r0_5 + L_2*r0_2*(52*M_3 + (-17 + 42*E_2)*M_2*r0 + 3*(-5 + 7*E_2)*M*r0_2 + 8*r0_3) + L_4*(-12*M_3 + 3*(3 + 5*E_2)*M*r0_2 + 4*r0_3 + M_2*(r0 - 6*E_2*r0)))))/(r0*expr2_3);\n    A63401 = (2*r0_2*(6*a_6*E_2*M_2 - 6*a_5*E*L*M*(2*M + r0) + a_3*E*L*M*r0*(-18*L_2 + r0*(-43*M + r0)) + 3*a*E*L*M*r0_2*(L_2*(4*M - 6*r0) + r0_2*(-19*M + 7*r0)) + 3*a_4*M*(12*E_2*M*r0_2 + L_2*(2*M + 2*r0 + 3*E_2*r0)) + L_2*r0_2*(3*L_2*M*(-2*M + r0) + 2*r0_2*(10*M_2 + 3*(-3 + E_2)*M*r0 + 2*r0_2)) + a_2*r0*(9*L_4*M + 54*E_2*M_2*r0_3 + L_2*r0*((7 - 6*E_2)*M_2 + (-4 + 15*E_2)*M*r0 + 3*r0_2))))/expr2_3;\n    A64021 = (48*L*r0*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0))*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/expr2_3;\n    A64041 = (-8*(-12*a_12*E_2*M_2*(114*M_2 + 109*M*r0 + 26*r0_2) + a_11*E*L*M*(-144*(-19 + 6*E_2)*M_3 + (2642 - 936*E_2)*M_2*r0 + 12*(50 - 21*E_2)*M*r0_2 - 21*r0_3) + a*E*L*M*r0_5*(3*L_4*r0_2*(1207*M_2 + 4*(-45 + 28*E_2)*M*r0 - 296*r0_2) + 3*L_2*r0_4*(634*M_2 + 4*(13 + 45*E_2)*M*r0 - 257*r0_2) + 4*L_6*(482*M_2 - 139*M*r0 - 75*r0_2) - 9*r0_6*(31*M_2 - 4*(17 + 6*E_2)*M*r0 + 33*r0_2)) + a_3*E*L*M*r0_3*(4*L_6*(1050*M_2 - 430*M*r0 - 93*r0_2) + L_4*r0*(9504*M_3 + (6719 + 1608*E_2)*M_2*r0 + 2*(-1583 + 206*E_2)*M*r0_2 - 2568*r0_3) + 2*L_2*r0_3*(4260*M_3 + (2059 + 2790*E_2)*M_2*r0 + 2*(61 + 213*E_2)*M*r0_2 - 2024*r0_3) + 3*r0_5*(-1440*M_3 + 3*(283 + 528*E_2)*M_2*r0 + 4*(196 + 29*E_2)*M*r0_2 - 671*r0_3)) + r0_5*(216*E_2*M*r0_9*(-2*M + r0) - 16*L_8*M*(10*M_2 - 7*M*r0 + r0_2) + 3*L_2*r0_6*(54*M_3 + (25 - 456*E_2)*M_2*r0 + 4*(-15 + 61*E_2)*M*r0_2 + 17*r0_3) + 3*L_4*r0_4*(100*M_3 + (30 - 472*E_2)*M_2*r0 + 2*(-51 + 134*E_2)*M*r0_2 + 31*r0_3) + L_6*r0_2*(26*M_3 + (55 - 488*E_2)*M_2*r0 + 2*(-53 + 146*E_2)*M*r0_2 + 36*r0_3)) + a_5*E*L*M*r0_2*(-36*L_6*(13*M + 2*r0) + L_4*(16848*M_3 + 4*(-309 + 350*E_2)*M_2*r0 - 2*(1769 + 40*E_2)*M*r0_2 - 1551*r0_3) + L_2*r0*(11520*M_4 + 8*(2075 + 882*E_2)*M_3*r0 + 6*(419 + 1322*E_2)*M_2*r0_2 - 2*(1801 + 162*E_2)*M*r0_3 - 5119*r0_4) + 3*r0_3*(-2868*M_4 + 4*(-449 + 1044*E_2)*M_3*r0 + (3421 + 3240*E_2)*M_2*r0_2 - 8*(-16 + 21*E_2)*M*r0_3 - 1290*r0_4)) + a_9*E*L*M*(-2*L_2*(1296*M_3 + 2*(-34 + 195*E_2)*M_2*r0 + 12*(-22 + 17*E_2)*M*r0_2 + 57*r0_3) + r0*(96*(-49 + 59*E_2)*M_4 + 4*(1145 + 1188*E_2)*M_3*r0 - 40*(-179 + 27*E_2)*M_2*r0_2 - 12*(-29 + 88*E_2)*M*r0_3 - 777*r0_4)) + a_10*M*(L_2*(72*(-19 + 36*E_2)*M_3 + 2*(-667 + 668*E_2)*M_2*r0 + 6*(-48 + 17*E_2)*M*r0_2 + 3*(7 + 12*E_2)*r0_3) + 12*E_2*r0*(196*M_4 - 292*M_3*r0 - 426*M_2*r0_2 - 77*M*r0_3 + 18*r0_4)) + a_2*r0_3*(-108*E_2*M*r0_8*(14*M_2 + 5*M*r0 - 10*r0_2) + 4*L_8*M*(-350*M_2 + 205*M*r0 + 3*r0_2) + L_6*r0*(-1224*M_4 - (1343 + 3376*E_2)*M_3*r0 + 2*(123 + 16*E_2)*M_2*r0_2 + 2*(241 + 308*E_2)*M*r0_3 + 36*r0_4) + 3*L_2*r0_5*(520*M_4 + 3*(13 - 980*E_2)*M_3*r0 + 2*(-237 + 94*E_2)*M_2*r0_2 + (97 + 958*E_2)*M*r0_3 + 40*r0_4) + 3*L_4*r0_3*(32*M_4 + 2*(65 - 1558*E_2)*M_3*r0 + 4*(-173 + 44*E_2)*M_2*r0_2 + (265 + 798*E_2)*M*r0_3 + 49*r0_4)) - a_7*E*L*M*r0*(3*L_4*(780*M_2 + 4*(45 + 13*E_2)*M*r0 + 55*r0_2) - 2*L_2*(8496*M_4 + 8*(241 + 351*E_2)*M_3*r0 + (-599 + 930*E_2)*M_2*r0_2 - (803 + 522*E_2)*M*r0_3 - 978*r0_4) + 3*r0_2*(-4*(-895 + 648*E_2)*M_4 - 8*(41 + 708*E_2)*M_3*r0 - 3*(1147 + 536*E_2)*M_2*r0_2 + 8*(67 + 60*E_2)*M*r0_3 + 970*r0_4)) + a_8*(2*L_4*M*(432*M_3 + 2*(-134 + 585*E_2)*M_2*r0 + 63*(-3 + 7*E_2)*M*r0_2 + 3*(13 + 12*E_2)*r0_3) + 24*E_2*M*r0_3*(300*M_4 - 46*M_3*r0 - 341*M_2*r0_2 - 32*M*r0_3 + 45*r0_4) + L_2*r0*(-48*(-49 + 354*E_2)*M_5 - 4*(269 + 2264*E_2)*M_4*r0 + 2*(-981 + 800*E_2)*M_3*r0_2 + 2*(346 + 961*E_2)*M_2*r0_3 + 6*(105 + 131*E_2)*M*r0_4 + 15*r0_5)) + a_6*r0*(3*L_6*M*(260*M_2 + 2*(11 + 78*E_2)*M*r0 + (31 + 12*E_2)*r0_2) + 24*E_2*M*r0_4*(306*M_4 + 246*M_3*r0 - 220*M_2*r0_2 + 13*M*r0_3 + 90*r0_4) + L_4*(-5664*M_5 + 16*(28 - 1053*E_2)*M_4*r0 + 6*(113 - 362*E_2)*M_3*r0_2 + 2*(397 + 1830*E_2)*M_2*r0_3 + 6*(207 + 155*E_2)*M*r0_4 + 30*r0_5) + L_2*r0_2*((3540 - 17424*E_2)*M_5 - 28*(49 + 1086*E_2)*M_4*r0 - 5*(901 + 1268*E_2)*M_3*r0_2 + 2*(508 + 2635*E_2)*M_2*r0_3 + 6*(276 + 479*E_2)*M*r0_4 + 48*r0_5)) + a_4*r0_2*(12*L_8*M*(13*M + 3*r0) + 36*E_2*M*r0_6*(66*M_3 - 67*M_2*r0 + 10*M*r0_2 + 60*r0_3) + L_6*(-5616*M_4 + 12*(129 - 350*E_2)*M_3*r0 + 2*(461 + 490*E_2)*M_2*r0_2 + 24*(26 + 15*E_2)*M*r0_3 + 15*r0_4) + 3*L_2*r0_3*(1064*M_5 + 12*(51 - 550*E_2)*M_4*r0 - 2*(703 + 2048*E_2)*M_3*r0_2 + 3*(-141 + 598*E_2)*M_2*r0_3 + 2*(253 + 711*E_2)*M*r0_4 + 34*r0_5) + L_4*r0*(-1872*M_5 - 8*(398 + 1917*E_2)*M_4*r0 - 4*(326 + 3327*E_2)*M_3*r0_2 + 2*(-308 + 2025*E_2)*M_2*r0_3 + 3*(709 + 816*E_2)*M*r0_4 + 84*r0_5))))/(3.*r0_5*expr2_4);\n    A64201 = (12*L*r0_4*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/expr2_3;\n    A64221 = (-4*(-2*a_10*E_2*M_2*(117*M + 56*r0) + a_9*E*L*M*(-36*(-13 + 4*E_2)*M_2 + 6*(37 - 13*E_2)*M*r0 - 7*r0_2) + a*E*L*M*r0_4*(L_2*r0_2*(401*M_2 + 3*(19 + 24*E_2)*M*r0 - 181*r0_2) + 2*L_4*(284*M_2 - 80*M*r0 - 47*r0_2) + 3*r0_4*(-79*M_2 + 2*(47 + 9*E_2)*M*r0 - 34*r0_2)) + a_3*E*L*M*r0_2*(4*L_4*(336*M_2 - 154*M*r0 - 31*r0_2) + L_2*r0*(1416*M_3 + (791 + 528*E_2)*M_2*r0 + 8*(-3 + 5*E_2)*M*r0_2 - 658*r0_3) + r0_3*(-1008*M_3 + (-5 + 1008*E_2)*M_2*r0 + 2*(497 + 10*E_2)*M*r0_2 - 543*r0_3)) + a_5*E*L*M*r0*(-6*L_4*(32*M + 5*r0) + r0_2*(32*(-55 + 36*E_2)*M_3 + (1033 + 1280*E_2)*M_2*r0 - 8*(-96 + 25*E_2)*M*r0_2 - 679*r0_3) + L_2*(2688*M_3 + 4*(-103 + 112*E_2)*M_2*r0 - 3*(79 + 32*E_2)*M*r0_2 - 392*r0_3)) + a_8*M*(L_2*(18*(-13 + 24*E_2)*M_2 + (-110 + 119*E_2)*M*r0 + (7 + 15*E_2)*r0_2) + 2*E_2*r0*(202*M_3 - 367*M_2*r0 - 193*M*r0_2 + 36*r0_3)) + a_6*(L_4*M*(144*M_2 + (-37 + 192*E_2)*M*r0 + (22 + 15*E_2)*r0_2) + 2*E_2*M*r0_3*(564*M_3 - 361*M_2*r0 - 275*M*r0_2 + 144*r0_3) + L_2*r0*((404 - 2688*E_2)*M_4 - (399 + 130*E_2)*M_3*r0 + (-87 + 500*E_2)*M_2*r0_2 + 2*(99 + 97*E_2)*M*r0_3 + 8*r0_4)) + a_4*r0*(L_6*M*(64*M + 15*r0) + 6*E_2*M*r0_4*(150*M_3 + M_2*r0 - 61*M*r0_2 + 72*r0_3) + L_4*(-896*M_4 - 6*(-53 + 224*E_2)*M_3*r0 + (-19 + 452*E_2)*M_2*r0_2 + 2*(102 + 59*E_2)*M*r0_3 + 8*r0_4) + L_2*r0_2*((632 - 2436*E_2)*M_4 - 4*(134 + 495*E_2)*M_3*r0 + (-469 + 563*E_2)*M_2*r0_2 + (351 + 509*E_2)*M*r0_3 + 26*r0_4)) + a_2*r0_2*(-18*E_2*M*r0_6*(6*M_2 + 13*M*r0 - 16*r0_2) + L_6*(-448*M_3 + 260*M_2*r0 + 6*M*r0_2) + L_4*r0*(-132*M_4 - (91 + 1076*E_2)*M_3*r0 + 2*(-123 + 58*E_2)*M_2*r0_2 + (167 + 195*E_2)*M*r0_3 + 17*r0_4) + L_2*r0_3*(356*M_4 + (191 - 1542*E_2)*M_3*r0 + (-547 + 130*E_2)*M_2*r0_2 + 4*(29 + 124*E_2)*M*r0_3 + 40*r0_4)) - a_7*E*L*M*(r0*((808 - 896*E_2)*M_3 - (1133 + 224*E_2)*M_2*r0 + 2*(-247 + 122*E_2)*M*r0_2 + 245*r0_3) + L_2*(432*M_2 + 37*r0_2 + 4*M*(r0 + 16*E_2*r0))) + r0_4*(72*E_2*M*r0_7*(-2*M + r0) + L_6*M*(-20*M_2 + 4*M*r0 + 3*r0_2) + L_4*r0_2*(110*M_3 - (35 + 152*E_2)*M_2*r0 + 2*(-25 + 46*E_2)*M*r0_2 + 20*r0_3) + L_2*r0_4*(102*M_3 + 2*(-34 + 83*E_2)*M*r0_2 + 22*r0_3 - 3*M_2*(r0 + 100*E_2*r0)))))/(r0_2*expr2_4);\n    A64401 = -(r0*(-360*a_8*E_2*M_2 - 9*a_7*E*L*M*(8*(-10 + 3*E_2)*M + 3*r0) + a_5*E*L*M*(-108*L_2*(6*M + r0) + r0*(24*(-52 + 53*E_2)*M_2 + 12*(197 - 37*E_2)*M*r0 - 703*r0_2)) + a_3*E*L*M*r0*(r0_2*(3*(-895 + 504*E_2)*M_2 + 10*(317 - 12*E_2)*M*r0 - 1198*r0_2) + 12*L_2*(318*M_2 - 155*M*r0 - 33*r0_2)) + 3*a*E*L*M*r0_3*(r0_2*(-381*M_2 + 350*M*r0 + 36*E_2*M*r0 - 100*r0_2) + 4*L_2*(114*M_2 - 25*M*r0 - 24*r0_2)) + 3*a_6*M*(3*L_2*(-40*M + 72*E_2*M + 3*r0 + 6*E_2*r0) + 8*E_2*r0*(26*M_2 - 56*M*r0 + 9*r0_2)) + a_4*(54*L_4*M*(4*M + r0) + 72*E_2*M*r0_3*(22*M_2 - 25*M*r0 + 9*r0_2) + L_2*r0*((624 - 3816*E_2)*M_3 + 12*(-85 + 133*E_2)*M_2*r0 + 2*(257 + 180*E_2)*M*r0_2 + 27*r0_3)) + r0_3*(216*E_2*M*r0_5*(-2*M + r0) + 6*L_4*M*(12*M_2 - 20*M*r0 + 7*r0_2) + L_2*r0_2*(450*M_3 - (73 + 432*E_2)*M_2*r0 + 8*(-31 + 33*E_2)*M*r0_2 + 86*r0_3)) + a_2*r0*(216*E_2*M*r0_4*(4*M_2 - 4*M*r0 + 3*r0_2) + 12*L_4*M*(-106*M_2 + 59*M*r0 + 3*r0_2) + L_2*r0_2*(-3*(-367 + 984*E_2)*M_3 + 2*(-799 + 270*E_2)*M_2*r0 + 6*(62 + 95*E_2)*M*r0_2 + 98*r0_3))))/(6.*expr2_4);\n    A65021 = (8*(6*a_8*E_2*M_2*(2*M + r0) - a_7*E*L*M*(24*M_2 + 21*M*r0 + 5*r0_2) + 3*a*E*L*M*r0_3*(L_4*(4*M - 6*r0) - L_2*r0_2*(10*M + r0) + 4*r0_4*(-5*M + 2*r0)) - a_5*E*L*M*r0*(2*M*r0*(47*M + 21*r0) + L_2*(44*M + 23*r0)) + L_2*r0_3*(3*L_4*M*(-2*M + r0) + 2*L_2*r0_2*(6*M_2 + (-7 + 3*E_2)*M*r0 + 2*r0_2) + r0_4*(22*M_2 + 3*(-7 + 2*E_2)*M*r0 + 5*r0_2)) - a_3*E*L*M*r0_2*(18*L_4 + r0_2*(138*M_2 + 53*M*r0 - 29*r0_2) + L_2*(-24*M_2 + 50*M*r0 + 34*r0_2)) + a_6*M*(6*E_2*M*r0_2*(12*M + 7*r0) + L_2*(12*M_2 + (15 + 22*E_2)*M*r0 + (5 + 9*E_2)*r0_2)) + a_4*r0*(18*E_2*M_2*r0_3*(6*M + 5*r0) + L_4*M*(22*M + (14 + 9*E_2)*r0) + L_2*r0*((22 - 12*E_2)*M_3 + 3*(-1 + 16*E_2)*M_2*r0 + (7 + 24*E_2)*M*r0_2 + 4*r0_3)) + a_2*r0_2*(9*L_6*M + 54*E_2*M_2*r0_5 + L_4*(-12*M_3 + (2 - 6*E_2)*M_2*r0 + (8 + 15*E_2)*M*r0_2 + 4*r0_3) + L_2*r0_2*(56*M_3 + 2*(-10 + 21*E_2)*M_2*r0 + (-17 + 21*E_2)*M*r0_2 + 9*r0_3))))/(r0*expr2_4);\n    A65201 = (2*r0_2*(6*a_6*E_2*M_2 - 6*a_5*E*L*M*(2*M + r0) - 2*a_3*E*L*M*r0*(9*L_2 + (22*M - r0)*r0) + 6*a*E*L*M*r0_2*(L_2*(2*M - 3*r0) + 2*r0_2*(-5*M + 2*r0)) + 3*a_4*M*(12*E_2*M*r0_2 + L_2*(2*M + 2*r0 + 3*E_2*r0)) + a_2*r0*(9*L_4*M + 54*E_2*M_2*r0_3 + L_2*r0*((8 - 6*E_2)*M_2 + 5*(-1 + 3*E_2)*M*r0 + 3*r0_2)) + L_2*r0_2*(3*L_2*M*(-2*M + r0) + r0_2*(22*M_2 + 3*(-7 + 2*E_2)*M*r0 + 5*r0_2))))/expr2_4;\n    A66001 = (4*L*r0_4*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/expr2_4;\n    A66021 = (-2*(-12*a_10*E_2*M_2*(28*M + 13*r0) - 3*a_9*E*L*M*(4*(-56 + 39*E_2)*M_2 + 2*(-47 + 42*E_2)*M*r0 + 11*r0_2) - a*E*L*M*r0_4*(3*r0_4*(131*M_2 + 2*(-97 + 12*E_2)*M*r0 + 80*r0_2) + L_4*(-1648*M_2 + 824*M*r0 + 84*r0_2) + L_2*r0_2*(-1871*M_2 + 934*M*r0 + 132*r0_2)) + a_5*E*L*M*r0*(-36*L_4*(17*M + 3*r0) + r0_2*(6*(-349 + 456*E_2)*M_3 + (2457 + 2116*E_2)*M_2*r0 + 2*(225 - 686*E_2)*M*r0_2 - 1539*r0_3) + L_2*(5808*M_3 + 4*(-449 + 238*E_2)*M_2*r0 - 4*(423 + 136*E_2)*M*r0_2 - 801*r0_3)) + a_3*E*L*M*r0_2*(4*L_4*(714*M_2 - 467*M*r0 - 48*r0_2) + L_2*r0*(4096*M_3 + (2269 + 1224*E_2)*M_2*r0 - 10*(181 + 34*E_2)*M*r0_2 - 1218*r0_3) + 2*r0_3*(-1077*M_3 + 6*(95 + 183*E_2)*M_2*r0 + 2*(381 - 167*E_2)*M*r0_2 - 633*r0_3)) + 3*a_8*M*(L_2*(4*(-28 + 117*E_2)*M_2 + 6*(-7 + 34*E_2)*M*r0 + (11 + 18*E_2)*r0_2) + 4*E_2*r0*(42*M_3 - 91*M_2*r0 - 35*M*r0_2 + 12*r0_3)) + r0_4*(144*E_2*M*r0_7*(-2*M + r0) - 2*L_6*M*(106*M_2 - 85*M*r0 + 16*r0_2) + L_2*r0_4*(218*M_3 - (101 + 476*E_2)*M_2*r0 + 4*(-13 + 70*E_2)*M*r0_2 + 24*r0_3) + L_4*r0_2*(-42*M_3 + (165 - 196*E_2)*M_2*r0 + 4*(-36 + 35*E_2)*M*r0_2 + 36*r0_3)) - a_7*E*L*M*(3*L_2*(468*M_2 + 4*(39 + 17*E_2)*M*r0 + 47*r0_2) + 2*r0*((504 - 968*E_2)*M_3 + 2*(-441 + 65*E_2)*M_2*r0 + (-201 + 514*E_2)*M*r0_2 + 273*r0_3)) + a_6*(3*L_4*M*(156*M_2 + 12*(3 + 17*E_2)*M*r0 + (29 + 18*E_2)*r0_2) + 12*E_2*M*r0_3*(108*M_3 - 135*M_2*r0 - 43*M*r0_2 + 48*r0_3) + L_2*r0*((504 - 5808*E_2)*M_4 + 56*(-12 + 23*E_2)*M_3*r0 + 2*(20 + 1249*E_2)*M_2*r0_2 + 3*(151 + 128*E_2)*M*r0_3 + 21*r0_4)) + a_4*r0*(6*L_6*M*(34*M + 9*r0) + 36*E_2*M*r0_4*(46*M_3 - 11*M_2*r0 - 7*M*r0_2 + 24*r0_3) + L_4*(-1936*M_4 + 24*(32 - 119*E_2)*M_3*r0 + 2*(111 + 875*E_2)*M_2*r0_2 + 6*(69 + 37*E_2)*M*r0_3 + 21*r0_4) + L_2*r0_2*((798 - 6152*E_2)*M_4 - (1213 + 3996*E_2)*M_3*r0 + 4*(-133 + 837*E_2)*M_2*r0_2 + 2*(361 + 443*E_2)*M*r0_3 + 21*r0_4)) + a_2*r0_2*(-36*E_2*M*r0_6*(11*M_2 + 8*M*r0 - 16*r0_2) - 2*L_6*M*(476*M_2 - 331*M*r0 + 15*r0_2) + L_4*M*r0*(-680*M_3 - (389 + 2660*E_2)*M_2*r0 + 2*(12 + 497*E_2)*M*r0_2 + 2*(135 + 154*E_2)*r0_3) + L_2*r0_3*(868*M_4 - (229 + 4208*E_2)*M_3*r0 + (-805 + 1754*E_2)*M_2*r0_2 + 4*(85 + 209*E_2)*M*r0_3 + 24*r0_4))))/(3.*r0_2*expr2_5);\n    A66201 = (r0*(168*a_8*E_2*M_2 + 3*a_7*E*L*M*(16*(-7 + 5*E_2)*M + 15*r0) - 3*a_6*M*(L_2*(8*(-7 + 30*E_2)*M + (15 + 22*E_2)*r0) + 4*E_2*r0*(21*M_2 - 49*M*r0 + 12*r0_2)) + a*E*L*M*r0_3*(L_2*(-1520*M_2 + 808*M*r0 + 60*r0_2) + 3*r0_2*(227*M_2 - 242*M*r0 + 36*E_2*M*r0 + 80*r0_2)) + a_5*E*L*M*(12*L_2*(60*M + 11*r0) + r0*((504 - 920*E_2)*M_2 + 4*(-284 + 185*E_2)*M*r0 + 513*r0_2)) + a_3*E*L*M*r0*(-8*L_2*(345*M_2 - 254*M*r0 - 24*r0_2) + r0_2*((979 - 1224*E_2)*M_2 + 2*(-841 + 304*E_2)*M*r0 + 918*r0_2)) + r0_3*(144*E_2*M*(2*M - r0)*r0_5 + 2*L_4*M*(74*M_2 - 53*M*r0 + 8*r0_2) + L_2*r0_2*(-314*M_3 + (149 + 164*E_2)*M_2*r0 + 4*(19 - 31*E_2)*M*r0_2 - 36*r0_3)) - a_4*(6*L_4*M*(40*M + 11*r0) + 72*E_2*M*r0_3*(7*M_2 - 12*M*r0 + 6*r0_2) + L_2*r0*((252 - 2760*E_2)*M_3 + 2*(-274 + 1063*E_2)*M_2*r0 + 6*(61 + 37*E_2)*M*r0_2 + 21*r0_3)) - a_2*r0*(36*E_2*M*r0_4*(11*M_2 - 11*M*r0 + 12*r0_2) - 2*L_4*M*(460*M_2 - 323*M*r0 + 15*r0_2) + L_2*r0_2*((475 - 2596*E_2)*M_3 + 10*(-90 + 131*E_2)*M_2*r0 + 8*(37 + 35*E_2)*M*r0_2 + 36*r0_3))))/(6.*expr2_5);\n    A67001 = (2*r0_2*(2*a_6*E_2*M_2 - 2*a_5*E*L*M*(2*M + r0) + a_3*E*L*M*r0*(-6*L_2 + r0*(-15*M + r0)) + a*E*L*M*r0_2*(L_2*(4*M - 6*r0) + 3*r0_2*(-7*M + 3*r0)) + a_4*M*(12*E_2*M*r0_2 + L_2*(2*M + 2*r0 + 3*E_2*r0)) + L_2*r0_2*(L_2*M*(-2*M + r0) + 2*r0_2*(4*M_2 + (-4 + E_2)*M*r0 + r0_2)) + a_2*r0*(3*L_4*M + 18*E_2*M_2*r0_3 + L_2*r0*((3 - 2*E_2)*M_2 + (-2 + 5*E_2)*M*r0 + r0_2))))/expr2_5;\n    A68001 = -(r0*(-12*a_8*E_2*M_2 - 3*a_7*E*L*M*(2*(-4 + 7*E_2)*M + 3*r0) + a_5*E*L*M*(-6*L_2*(21*M + 4*r0) + r0*(4*(-6 + 31*E_2)*M_2 + 4*(22 - 37*E_2)*M*r0 - 69*r0_2)) + a_3*E*L*M*r0*(r0_2*((-29 + 180*E_2)*M_2 - 2*(-83 + 71*E_2)*M*r0 - 132*r0_2) + 4*L_2*(93*M_2 - 89*M*r0 - 3*r0_2)) + 3*a_6*M*(L_2*(-4*M + 42*E_2*M + 3*r0 + 4*E_2*r0) + 2*E_2*r0*(2*M_2 - 6*M*r0 + 3*r0_2)) + a*E*L*M*r0_3*(2*L_2*(134*M_2 - 91*M*r0 + 6*r0_2) - 3*r0_2*(29*M_2 + 2*(-19 + 6*E_2)*M*r0 + 14*r0_2)) + M*r0_3*(18*E_2*r0_5*(-2*M + r0) + L_4*(-44*M_2 + 38*M*r0 - 8*r0_2) + L_2*r0_2*(46*M_2 - (43 + 4*E_2)*M*r0 + 2*(5 + 4*E_2)*r0_2)) + a_2*M*r0*(-4*L_4*(31*M_2 - 26*M*r0 + 3*r0_2) + 18*E_2*r0_4*(2*M_2 - 2*M*r0 + 3*r0_2) + L_2*r0_2*((29 - 404*E_2)*M_2 + 2*(-48 + 143*E_2)*M*r0 + 2*(23 + 10*E_2)*r0_2)) + a_4*(6*L_4*M*(7*M + 2*r0) + 18*E_2*M*r0_4*(-4*M + 3*r0) + L_2*r0*((12 - 372*E_2)*M_3 + 4*(-13 + 100*E_2)*M_2*r0 + 24*(2 + E_2)*M*r0_2 + 3*r0_3))))/(3.*expr2_6);\n  }\n\n  /* A_{theta,theta} */\n  {\n    A70080 = (1024*M*expr1_4*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(r0_7*(a_2 + r0*(-2*M + r0)));\n    A70260 = (1024*M*expr1_3*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(r0_4*(a_2 + r0*(-2*M + r0)));\n    A70440 = (384*M*r0*expr4_2*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(a_2 + r0*(-2*M + r0));\n    A70620 = (64*M*r0_3*(L_2 + r0_2 + (a_2*(2*M + r0))/r0)*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(a_2 + r0*(-2*M + r0));\n    A70800 = (4*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(a_2 + r0*(-2*M + r0));\n    A71080 = (512*M*expr4_3*((-(a_2*M) + r0_3 + (2*L*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/(a_2 + r0*(-2*M + r0)))*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))) - 4*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(3*a_4*E_2 - 6*a_3*E*L + 2*a*E*L*(4*M - 3*r0)*r0 + a_2*(3*L_2 + 4*E_2*r0*(-M + r0)) + r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))) + 3*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(5*a_4*E_2 - 10*a_3*E*L + 8*a*E*L*(M - r0)*r0 + r0*(E_2*r0_3 + L_2*(-4*M + 2*r0)) + a_2*(5*L_2 + 2*E_2*r0*(-2*M + 3*r0)))))/(r0_5*(a_2 + r0*(-2*M + r0)));\n    A71260 = (128*M*expr4_2*(3*(-(a_2*M) + r0_3 + (2*L*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/(a_2 + r0*(-2*M + r0)))*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))) - 17*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(3*a_4*E_2 - 6*a_3*E*L + 2*a*E*L*(4*M - 3*r0)*r0 + a_2*(3*L_2 + 4*E_2*r0*(-M + r0)) + r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))) + 12*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(5*a_4*E_2 - 10*a_3*E*L + 8*a*E*L*(M - r0)*r0 + r0*(E_2*r0_3 + L_2*(-4*M + 2*r0)) + a_2*(5*L_2 + 2*E_2*r0*(-2*M + 3*r0)))))/(r0_3*(a_2 + r0*(-2*M + r0)));\n    A71440 = (96*M*(L_2 + r0_2 + (a_2*(2*M + r0))/r0)*(9*(L_2 + r0_2 + (a_2*(2*M + r0))/r0)*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))) + ((-(a_2*M) + r0_3 + (2*L*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/(a_2 + r0*(-2*M + r0)))*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/r0 + (6*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(5*a_4*E_2 - 10*a_3*E*L + 8*a*E*L*(M - r0)*r0 + r0*(E_2*r0_3 + L_2*(-4*M + 2*r0)) + a_2*(5*L_2 + 2*E_2*r0*(-2*M + 3*r0))))/r0))/(a_2 + r0*(-2*M + r0));\n    A71620 = (8*M*r0*((-(a_2*M) + r0_3 + (2*L*(a_3*E*M - a_2*L*M + 3*a*E*M*r0_2 + L*r0_2*(-2*M + r0)))/(a_2 + r0*(-2*M + r0)))*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))) - 19*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(3*a_4*E_2 - 6*a_3*E*L + 2*a*E*L*(4*M - 3*r0)*r0 + a_2*(3*L_2 + 4*E_2*r0*(-M + r0)) + r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))) + 12*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(5*a_4*E_2 - 10*a_3*E*L + 8*a*E*L*(M - r0)*r0 + r0*(E_2*r0_3 + L_2*(-4*M + 2*r0)) + a_2*(5*L_2 + 2*E_2*r0*(-2*M + 3*r0)))))/(a_2 + r0*(-2*M + r0));\n    A71800 = (4*M*r0_5*(L_2*(4*M - 2*r0) + a_2*E_2*(4*M - r0) - E_2*r0_3 + a*E*L*(-8*M + 3*r0)))/(a_2 + r0*(-2*M + r0));\n    A72060 = (1024*M*expr1_3*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(r0_4*expr2_2);\n    A72240 = (768*M*r0*expr4_2*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_2;\n    A72420 = (192*M*r0_2*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_2;\n    A72600 = (16*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_2;\n    A73060 = (-128*M*expr1_2*(-9*a_8*E_2*(3*M + r0) + 18*a_7*E*L*((3 + E_2)*M + r0) + r0_3*(E_2*r0_3 + L_2*(-4*M + 2*r0))*(r0_2*(-15*M + 7*r0) + L_2*(-21*M + 10*r0)) + 2*a*E*L*r0_3*(L_2*(-120*M_2 + 97*M*r0 - 18*r0_2) + r0_2*(-60*M_2 + (49 + 9*E_2)*M*r0 - 9*r0_2)) - a_6*(9*L_2*(3*M + 6*E_2*M + r0 + E_2*r0) + 4*E_2*r0*(-8*M_2 + 13*M*r0 + 4*r0_2)) + 2*a_5*E*L*(9*L_2*(3*M + r0) + r0*(-4*(8 + 3*E_2)*M_2 + (35 + 39*E_2)*M*r0 + 9*r0_2)) + a_4*(-9*L_4*(2*M + r0) + L_2*r0*(8*(4 + 9*E_2)*M_2 - (18 + 203*E_2)*M*r0 + 2*(-1 + E_2)*r0_2) + 2*E_2*r0_2*(24*M_3 + 32*M_2*r0 - 27*M*r0_2 + r0_3)) - 2*a_3*E*L*r0*(L_2*(36*M_2 - 86*M*r0 + 9*r0_2) + r0*(48*M_3 + 4*(7 + 9*E_2)*M_2*r0 - 3*(19 + 13*E_2)*M*r0_2 + 9*r0_3)) + a_2*r0*(4*E_2*r0_4*(12*M_2 - 11*M*r0 + 4*r0_2) + L_4*(24*M_2 - 47*M*r0 + 16*r0_2) + L_2*r0*(48*M_3 + 4*(-2 + 57*E_2)*M_2*r0 - (53 + 190*E_2)*M*r0_2 + 21*(1 + E_2)*r0_3))))/(r0_5*expr2_3);\n    A73240 = (-96*M*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-6*a_8*E_2*(3*M + r0) + 12*a_7*E*L*((3 + E_2)*M + r0) + r0_3*(E_2*r0_3 + L_2*(-4*M + 2*r0))*(r0_2*(-15*M + 7*r0) + L_2*(-19*M + 9*r0)) + 2*a*E*L*r0_3*(L_2*(-100*M_2 + 81*M*r0 - 15*r0_2) + r0_2*(-60*M_2 + (49 + 6*E_2)*M*r0 - 9*r0_2)) + 2*a_5*E*L*(6*L_2*(3*M + r0) + r0*(-2*(1 + 4*E_2)*M_2 + (27 + 26*E_2)*M*r0 + 3*r0_2)) - a_6*(6*L_2*(3*M + 6*E_2*M + r0 + E_2*r0) + E_2*r0*(-2*M_2 + 41*M*r0 + 9*r0_2)) + a_4*(-6*L_4*(2*M + r0) + L_2*r0*((2 + 48*E_2)*M_2 - (13 + 145*E_2)*M*r0 + 3*(1 + E_2)*r0_2) + E_2*r0_2*(72*M_3 + 36*M_2*r0 - 51*M*r0_2 + 7*r0_3)) + a_2*r0*(L_4*(16*M_2 - 41*M*r0 + 15*r0_2) + E_2*r0_4*(42*M_2 - 43*M*r0 + 17*r0_2) + L_2*r0*(72*M_3 + 4*(-6 + 43*E_2)*M_2*r0 - 5*(11 + 28*E_2)*M*r0_2 + (23 + 18*E_2)*r0_3)) - 2*a_3*E*L*r0*(L_2*(24*M_2 - 67*M*r0 + 9*r0_2) + 2*r0*(36*M_3 - (29 + 13*E_2)*M*r0_2 + 6*r0_3 + 3*M_2*(r0 + 4*E_2*r0)))))/(r0_2*expr2_3);\n    A73420 = (-24*M*r0*(-3*a_8*E_2*(3*M + r0) + 6*a_7*E*L*((3 + E_2)*M + r0) + r0_3*(E_2*r0_3 + L_2*(-4*M + 2*r0))*(r0_2*(-15*M + 7*r0) + L_2*(-17*M + 8*r0)) + 2*a*E*L*r0_3*(L_2*(-80*M_2 + 65*M*r0 - 12*r0_2) + r0_2*(-60*M_2 + (49 + 3*E_2)*M*r0 - 9*r0_2)) + 2*a_5*E*L*(3*L_2*(3*M + r0) + r0*(-4*(-7 + E_2)*M_2 + (19 + 13*E_2)*M*r0 - 3*r0_2)) - a_6*(3*L_2*(3*M + 6*E_2*M + r0 + E_2*r0) + 2*E_2*r0*(14*M_2 + 15*M*r0 + r0_2)) + a_4*(-3*L_4*(2*M + r0) + L_2*r0*(4*(-7 + 6*E_2)*M_2 - (8 + 87*E_2)*M*r0 + 4*(2 + E_2)*r0_2) + 4*E_2*r0_2*(24*M_3 + 2*M_2*r0 - 12*M*r0_2 + 3*r0_3)) - 2*a_3*E*L*r0*(3*L_2*(4*M_2 - 16*M*r0 + 3*r0_2) + r0*(96*M_3 + 4*(-4 + 3*E_2)*M_2*r0 - (59 + 13*E_2)*M*r0_2 + 15*r0_3)) + a_2*r0*(6*E_2*r0_4*(6*M_2 - 7*M*r0 + 3*r0_2) + L_4*(8*M_2 - 35*M*r0 + 14*r0_2) + L_2*r0*(96*M_3 + 4*(-10 + 29*E_2)*M_2*r0 - 3*(19 + 30*E_2)*M*r0_2 + 5*(5 + 3*E_2)*r0_3))))/expr2_3;\n    A73600 = (-2*M*r0_5*(2*a_3*E*L*(29*M - 9*r0) + a_4*E_2*(-29*M + 5*r0) - 2*a*E*L*r0*(60*M_2 - 49*M*r0 + 9*r0_2) + (15*M - 7*r0)*r0*(L_2*(4*M - 2*r0) - E_2*r0_3) + a_2*(L_2*(-29*M + 13*r0) + 4*E_2*r0*(15*M_2 - 10*M*r0 + 3*r0_2))))/expr2_3;\n    A74040 = (384*M*r0*expr4_2*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_3;\n    A74220 = (192*M*r0_2*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_3;\n    A74400 = (24*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_3;\n    A75040 = (-96*M*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-3*a_8*E_2*(3*M + r0) + 6*a_7*E*L*((3 + E_2)*M + r0) + r0_3*(E_2*r0_3 + L_2*(-4*M + 2*r0))*(r0_2*(-7*M + 3*r0) + L_2*(-9*M + 4*r0)) + a_4*(-3*L_4*(2*M + r0) + L_2*M*r0*(4*M + 24*E_2*M - 8*r0 - 71*E_2*r0) + 8*E_2*M*r0_2*(4*M_2 + 3*M*r0 - 3*r0_2)) + 2*a*E*L*r0_3*(L_2*(-48*M_2 + 37*M*r0 - 6*r0_2) + r0_2*(-28*M_2 + 3*(7 + E_2)*M*r0 - 3*r0_2)) - a_6*(3*L_2*(3*M + 6*E_2*M + r0 + E_2*r0) + 2*E_2*r0*(-2*M_2 + 11*M*r0 + 3*r0_2)) + 2*a_5*E*L*(3*L_2*(3*M + r0) + r0*(-4*(1 + E_2)*M_2 + (15 + 13*E_2)*M*r0 + 3*r0_2)) - 2*a_3*E*L*r0*(L_2*(12*M_2 - 32*M*r0 + 3*r0_2) + r0*(32*M_3 + 4*(2 + 3*E_2)*M_2*r0 - (27 + 13*E_2)*M*r0_2 + 3*r0_3)) + a_2*r0*(2*E_2*r0_4*(10*M_2 - 9*M*r0 + 3*r0_2) + L_4*(8*M_2 - 19*M*r0 + 6*r0_2) + L_2*r0*(32*M_3 + 4*(-2 + 21*E_2)*M_2*r0 - (25 + 66*E_2)*M*r0_2 + (9 + 7*E_2)*r0_3))))/(r0_2*expr2_4);\n    A75220 = (-24*M*r0*(-3*a_8*E_2*(3*M + r0) + 6*a_7*E*L*((3 + E_2)*M + r0) + r0_3*(E_2*r0_3 + L_2*(-4*M + 2*r0))*(2*r0_2*(-7*M + 3*r0) + L_2*(-16*M + 7*r0)) + 2*a_5*E*L*(3*L_2*(3*M + r0) + M*r0*(22*M - 4*E_2*M + 22*r0 + 13*E_2*r0)) + 2*a*E*L*r0_3*(L_2*(-76*M_2 + 58*M*r0 - 9*r0_2) + r0_2*(-56*M_2 + 3*(14 + E_2)*M*r0 - 6*r0_2)) - a_6*(3*L_2*(3*M + 6*E_2*M + r0 + E_2*r0) + E_2*r0*(22*M_2 + 33*M*r0 + 5*r0_2)) + a_4*(-3*L_4*(2*M + r0) + L_2*r0*((-22 + 24*E_2)*M_2 - (11 + 84*E_2)*M*r0 + (5 + E_2)*r0_2) + E_2*r0_2*(88*M_3 + 20*M_2*r0 - 45*M*r0_2 + 5*r0_3)) - 2*a_3*E*L*r0*(3*L_2*(4*M_2 - 15*M*r0 + 2*r0_2) + r0*(88*M_3 + 6*(-1 + 2*E_2)*M_2*r0 - (55 + 13*E_2)*M*r0_2 + 9*r0_3)) + a_2*r0*(L_4*(8*M_2 - 32*M*r0 + 11*r0_2) + E_2*r0_4*(34*M_2 - 35*M*r0 + 13*r0_2) + L_2*r0*(88*M_3 + 16*(-2 + 7*E_2)*M_2*r0 - 2*(26 + 41*E_2)*M*r0_2 + (20 + 11*E_2)*r0_3))))/expr2_4;\n    A75400 = (-6*M*r0_5*(2*a_3*E*L*(13*M - 3*r0) + a_4*E_2*(-13*M + r0) - 2*a*E*L*r0*(28*M_2 - 21*M*r0 + 3*r0_2) + (7*M - 3*r0)*r0*(L_2*(4*M - 2*r0) - E_2*r0_3) + a_2*(L_2*(-13*M + 5*r0) + 4*E_2*r0*(7*M_2 - 4*M*r0 + r0_2))))/expr2_4;\n    A76020 = (64*M*r0_2*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_4;\n    A76200 = (16*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_4;\n    A77020 = (-8*M*r0*(-3*a_8*E_2*(3*M + r0) + 6*a_7*E*L*((3 + E_2)*M + r0) + r0_3*(E_2*r0_3 + L_2*(-4*M + 2*r0))*(r0_2*(-13*M + 5*r0) + L_2*(-15*M + 6*r0)) + 2*a*E*L*r0_3*(L_2*(-72*M_2 + 51*M*r0 - 6*r0_2) + r0_2*(-52*M_2 + (35 + 3*E_2)*M*r0 - 3*r0_2)) - a_6*(3*L_2*(3*M + 6*E_2*M + r0 + E_2*r0) + 4*E_2*r0*(4*M_2 + 9*M*r0 + 2*r0_2)) + 2*a_5*E*L*(3*L_2*(3*M + r0) + r0*(-4*(-4 + E_2)*M_2 + (25 + 13*E_2)*M*r0 + 3*r0_2)) - a_4*(3*L_4*(2*M + r0) + L_2*r0*(-8*(-2 + 3*E_2)*M_2 + (14 + 81*E_2)*M*r0 + 2*(-1 + E_2)*r0_2) + 2*E_2*r0_2*(-40*M_3 - 16*M_2*r0 + 21*M*r0_2 + r0_3)) + a_2*r0*(4*E_2*r0_4*(8*M_2 - 7*M*r0 + 2*r0_2) + L_4*(8*M_2 - 29*M*r0 + 8*r0_2) + L_2*r0*(80*M_3 + 12*(-2 + 9*E_2)*M_2*r0 - (47 + 74*E_2)*M*r0_2 + (15 + 7*E_2)*r0_3)) - 2*a_3*E*L*r0*(3*L_2*(4*M_2 - 14*M*r0 + r0_2) + r0*(80*M_3 - (51 + 13*E_2)*M*r0_2 + 3*r0_3 + 4*M_2*(r0 + 3*E_2*r0)))))/expr2_5;\n    A77200 = (-2*M*r0_5*(2*a_3*E*L*(23*M - 3*r0) - a_4*E_2*(23*M + r0) - 2*a*E*L*r0*(52*M_2 - 35*M*r0 + 3*r0_2) + (13*M - 5*r0)*r0*(L_2*(4*M - 2*r0) - E_2*r0_3) + a_2*(L_2*(-23*M + 7*r0) + 4*E_2*r0*(13*M_2 - 6*M*r0 + r0_2))))/expr2_5;\n    A78000 = (4*M*r0_5*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/expr2_5;\n    A79000 = (-2*M*r0_5*(10*a_3*E*L*M - a_4*E_2*(5*M + r0) + 2*a*E*L*M*r0*(-12*M + 7*r0) + (3*M - r0)*r0*(L_2*(4*M - 2*r0) - E_2*r0_3) + a_2*(4*E_2*M*(3*M - r0)*r0 + L_2*(-5*M + r0))))/expr2_6;\n  }\n\n  /* A_{theta,phi} */\n  {\n    A80161 = (-128*L*expr1_3*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/(r0_6*(a_2 + r0*(-2*M + r0)));\n    A80341 = (-96*L*expr1_2*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/(r0_3*(a_2 + r0*(-2*M + r0)));\n    A80521 = (-24*L*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/(a_2 + r0*(-2*M + r0));\n    A80701 = 2*L*r0_3*(E*M*(a_3 - 3*a*r0_2) - L*(a_2*M + 2*r0_2*(-M + r0)) + (L*M*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(a_2 + r0*(-2*M + r0)));\n    A81161 = (64*expr1_2*(2*a_10*E_2*M_2*(2*M + r0) - a_9*E*L*M*(8*(1 + 3*E_2)*M_2 + (7 + 12*E_2)*M*r0 + 2*r0_2) + L_2*(2*M - r0)*r0_4*(8*L_4*M*(-2*M + r0) + 2*L_2*r0_2*(-8*M_2 + 3*E_2*M*r0 + 2*r0_2) + r0_4*(8*M_2 + (-16 + 7*E_2)*M*r0 + 6*r0_2)) + a*E*L*M*r0_4*(2*L_2*r0_2*(62*M_2 - (47 + 3*E_2)*M*r0 + 8*r0_2) + 2*L_4*(44*M_2 - 40*M*r0 + 9*r0_2) - r0_4*(40*M_2 + 6*(-11 + 2*E_2)*M*r0 + 23*r0_2)) + a_8*M*(4*E_2*M*r0*(-2*M_2 + r0_2) + L_2*((4 + 72*E_2)*M_2 + (5 + 37*E_2)*M*r0 + 2*(1 + E_2)*r0_2)) - a_7*E*L*M*(L_2*(72*M_2 + 2*(19 + 3*E_2)*M*r0 + 6*r0_2) + r0*(-16*(1 + 2*E_2)*M_3 + 8*(-1 + 11*E_2)*M_2*r0 + (15 + 64*E_2)*M*r0_2 + 9*r0_3)) + a_3*E*L*M*r0_2*(2*L_4*(12*M_2 - 33*M*r0 + 7*r0_2) + r0_3*(-116*M_3 + 12*(9 + 2*E_2)*M_2*r0 + (51 - 64*E_2)*M*r0_2 - 51*r0_3) + 2*L_2*r0*(152*M_3 + 12*(-4 + E_2)*M_2*r0 - (81 + 13*E_2)*M*r0_2 + 17*r0_3)) + a_5*E*L*M*r0*(-2*L_4*(9*M + 2*r0) + 2*L_2*(48*M_3 + 2*(-55 + 2*E_2)*M_2*r0 - (45 + 13*E_2)*M*r0_2 + 6*r0_3) - r0_2*((4 - 96*E_2)*M_3 + 4*(-21 + 10*E_2)*M_2*r0 + (23 + 104*E_2)*M*r0_2 + 35*r0_3)) + a_6*M*(-8*E_2*M*r0_4*(5*M + 2*r0) + L_4*(24*M_2 + (13 + 18*E_2)*M*r0 + 2*(2 + E_2)*r0_2) + L_2*r0*(-8*(1 + 12*E_2)*M_3 + 2*(-4 + 121*E_2)*M_2*r0 + 5*(2 + 29*E_2)*M*r0_2 - 7*(-1 + E_2)*r0_3)) + a_4*r0*(2*L_6*M*(3*M + r0) + 36*E_2*M_2*r0_4*(2*M_2 - r0_2) + L_4*M*(-32*M_3 + 6*(11 - 4*E_2)*M_2*r0 + 9*(1 + 8*E_2)*M*r0_2 - (5 + 6*E_2)*r0_3) + L_2*r0_2*((4 - 296*E_2)*M_4 + 4*(-12 + 23*E_2)*M_3*r0 + (33 + 217*E_2)*M_2*r0_2 - 3*(-4 + 9*E_2)*M*r0_3 - 6*r0_4)) - a_2*r0_2*(18*E_2*M_2*r0_6*(-2*M + r0) + 4*L_6*M*(2*M_2 - 5*M*r0 + 2*r0_2) + L_4*r0*(104*M_4 + 4*(-11 + 20*E_2)*M_3*r0 - 2*(19 + 37*E_2)*M_2*r0_2 + (9 + 14*E_2)*M*r0_3 + 4*r0_4) + L_2*r0_3*(-56*M_4 + 6*(16 + 17*E_2)*M_3*r0 - 3*(4 + 41*E_2)*M_2*r0_2 + 5*(-7 + 5*E_2)*M*r0_3 + 12*r0_4))))/(r0_8*expr2_2);\n    A81341 = (16*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(6*a_10*E_2*M_2*(2*M + r0) - a_9*E*L*M*(24*(1 + 3*E_2)*M_2 + 12*(2 + 3*E_2)*M*r0 + 7*r0_2) + L_2*(2*M - r0)*r0_4*(26*L_4*M*(-2*M + r0) + 3*r0_4*(8*M_2 + (-16 + 7*E_2)*M*r0 + 6*r0_2) + L_2*r0_2*(-44*M_2 + (-6 + 19*E_2)*M*r0 + 14*r0_2)) + a*E*L*M*r0_4*(3*L_2*r0_2*(112*M_2 - 2*(39 + 4*E_2)*M*r0 + 11*r0_2) + 4*L_4*(76*M_2 - 68*M*r0 + 15*r0_2) - 3*r0_4*(40*M_2 + 6*(-11 + 2*E_2)*M*r0 + 23*r0_2)) + a_8*M*(12*E_2*M*r0*(-2*M_2 + r0_2) + L_2*(12*(1 + 18*E_2)*M_2 + 2*(9 + 61*E_2)*M*r0 + (7 + 9*E_2)*r0_2)) - a_7*E*L*M*(L_2*(216*M_2 + 8*(17 + 3*E_2)*M*r0 + 25*r0_2) + 2*r0*(-24*(1 + 2*E_2)*M_3 + 6*(-3 + 22*E_2)*M_2*r0 + (19 + 96*E_2)*M*r0_2 + 13*r0_3)) + a_5*E*L*M*r0*(-18*L_4*(4*M + r0) + L_2*(288*M_3 + 16*(-37 + 2*E_2)*M_2*r0 - 26*(11 + 4*E_2)*M*r0_2 + 23*r0_3) - 2*r0_2*((12 - 144*E_2)*M_3 + 4*(-28 + 15*E_2)*M_2*r0 + (31 + 156*E_2)*M*r0_2 + 50*r0_3)) + a_3*E*L*M*r0_2*(L_4*(96*M_2 - 236*M*r0 + 42*r0_2) - 6*r0_3*(52*M_3 - 2*(25 + 6*E_2)*M_2*r0 + (-25 + 32*E_2)*M*r0_2 + 25*r0_3) + L_2*r0*(864*M_3 + 8*(-31 + 12*E_2)*M_2*r0 - 8*(58 + 13*E_2)*M*r0_2 + 81*r0_3)) + a_6*(-24*E_2*M_2*r0_4*(5*M + 2*r0) + L_4*M*(72*M_2 + 2*(25 + 36*E_2)*M*r0 + (16 + 9*E_2)*r0_2) + L_2*r0*(-24*(1 + 12*E_2)*M_4 + 4*(-9 + 173*E_2)*M_3*r0 + 2*(13 + 224*E_2)*M_2*r0_2 + (27 - 14*E_2)*M*r0_3 + 2*r0_4)) + a_4*r0*(3*L_6*M*(8*M + 3*r0) + 108*E_2*M_2*r0_4*(2*M_2 - r0_2) + L_2*r0_2*((24 - 864*E_2)*M_4 + 8*(-16 + 31*E_2)*M_3*r0 + 2*(35 + 318*E_2)*M_2*r0_2 + (37 - 76*E_2)*M*r0_3 - 14*r0_4) + L_4*(-96*M_4 + 4*(41 - 24*E_2)*M_3*r0 + 2*(15 + 137*E_2)*M_2*r0_2 - (8 + 17*E_2)*M*r0_3 + 2*r0_4)) - a_2*r0_2*(54*E_2*M_2*r0_6*(-2*M + r0) + L_6*M*(32*M_2 - 66*M*r0 + 25*r0_2) + L_4*r0*(288*M_4 + 8*(-15 + 37*E_2)*M_3*r0 - 8*(13 + 34*E_2)*M_2*r0_2 + (22 + 45*E_2)*M*r0_3 + 12*r0_4) + L_2*r0_3*(-144*M_4 + 4*(62 + 69*E_2)*M_3*r0 - 22*(1 + 16*E_2)*M_2*r0_2 + (-101 + 74*E_2)*M*r0_3 + 34*r0_4))))/(r0_5*expr2_2);\n    A81521 = (4*(6*a_10*E_2*M_2*(2*M + r0) - a_9*E*L*M*(24*(1 + 3*E_2)*M_2 + 9*(3 + 4*E_2)*M*r0 + 8*r0_2) + L_2*(2*M - r0)*r0_4*(28*L_4*M*(-2*M + r0) + 4*L_2*r0_2*(-10*M_2 + (-3 + 5*E_2)*M*r0 + 4*r0_2) + 3*r0_4*(8*M_2 + (-16 + 7*E_2)*M*r0 + 6*r0_2)) + a*E*L*M*r0_4*(6*L_2*r0_2*(50*M_2 - (31 + 5*E_2)*M*r0 + 3*r0_2) - 3*r0_4*(40*M_2 + 6*(-11 + 2*E_2)*M*r0 + 23*r0_2) + L_4*(344*M_2 - 304*M*r0 + 66*r0_2)) + a_8*M*(12*E_2*M*r0*(-2*M_2 + r0_2) + L_2*(12*(1 + 18*E_2)*M_2 + 7*(3 + 19*E_2)*M*r0 + 4*(2 + 3*E_2)*r0_2)) - a_7*E*L*M*(2*L_2*(108*M_2 + (79 + 15*E_2)*M*r0 + 16*r0_2) + r0*(-48*(1 + 2*E_2)*M_3 + 24*(-2 + 11*E_2)*M_2*r0 + (31 + 192*E_2)*M*r0_2 + 25*r0_3)) + a_3*E*L*M*r0_2*(2*L_4*(60*M_2 - 137*M*r0 + 21*r0_2) + 2*L_2*r0*(408*M_3 + 4*(-26 + 15*E_2)*M_2*r0 - 13*(17 + 5*E_2)*M*r0_2 + 30*r0_3) - 3*r0_3*(92*M_3 - 4*(23 + 6*E_2)*M_2*r0 + (-49 + 64*E_2)*M*r0_2 + 49*r0_3)) + a_5*E*L*M*r0*(-6*L_4*(15*M + 4*r0) + 2*L_2*(144*M_3 + 2*(-131 + 10*E_2)*M_2*r0 - (151 + 65*E_2)*M*r0_2 + 5*r0_3) - r0_2*(-36*(-1 + 8*E_2)*M_3 + 4*(-49 + 30*E_2)*M_2*r0 + (55 + 312*E_2)*M*r0_2 + 95*r0_3)) + a_6*(-24*E_2*M_2*r0_4*(5*M + 2*r0) + L_4*M*(72*M_2 + (61 + 90*E_2)*M*r0 + 4*(5 + 3*E_2)*r0_2) + L_2*r0*(-24*(1 + 12*E_2)*M_4 + 2*(-24 + 329*E_2)*M_3*r0 + (22 + 461*E_2)*M_2*r0_2 + (33 - 7*E_2)*M*r0_3 + 4*r0_4)) + a_4*r0*(6*L_6*M*(5*M + 2*r0) + 108*E_2*M_2*r0_4*(2*M_2 - r0_2) + L_2*r0_2*((36 - 840*E_2)*M_4 + 4*(-28 + 55*E_2)*M_3*r0 + (41 + 621*E_2)*M_2*r0_2 + (38 - 71*E_2)*M*r0_3 - 10*r0_4) + L_4*(-96*M_4 + 10*(13 - 12*E_2)*M_3*r0 + (33 + 332*E_2)*M_2*r0_2 - (1 + 16*E_2)*M*r0_3 + 4*r0_4)) - a_2*r0_2*(54*E_2*M_2*r0_6*(-2*M + r0) + 2*L_6*M*(20*M_2 - 36*M*r0 + 13*r0_2) + L_4*r0*(264*M_4 + 4*(-27 + 88*E_2)*M_3*r0 - 2*(47 + 161*E_2)*M_2*r0_2 + (17 + 48*E_2)*M*r0_3 + 12*r0_4) + L_2*r0_3*(-120*M_4 + 2*(104 + 123*E_2)*M_3*r0 - (8 + 335*E_2)*M_2*r0_2 + (-97 + 73*E_2)*M*r0_3 + 32*r0_4))))/(r0_2*expr2_2);\n    A81701 = -((r0*(-2*a_8*E_2*M_2 + a_7*E*L*M*(4*(1 + 3*E_2)*M + 3*r0) - a_6*M*(2*E_2*M*r0*(-2*M + r0) + L_2*(2*M + 36*E_2*M + 3*r0 + 5*E_2*r0)) + a_5*E*L*M*(2*L_2*(18*M + 5*r0) + r0*(-8*(1 + 2*E_2)*M_2 + 2*(-3 + 26*E_2)*M*r0 + 5*r0_2)) - L_2*(2*M - r0)*r0_3*(10*L_2*M*(-2*M + r0) + r0_2*(8*M_2 + (-16 + 7*E_2)*M*r0 + 6*r0_2)) + a*E*L*M*r0_3*(-8*L_2*(16*M_2 - 14*M*r0 + 3*r0_2) + r0_2*(40*M_2 + 6*(-11 + 2*E_2)*M*r0 + 23*r0_2)) + a_3*E*L*M*r0*(-2*L_2*(24*M_2 - 52*M*r0 + 7*r0_2) + r0_2*((8 - 48*E_2)*M_2 + 4*(-7 + 13*E_2)*M*r0 + 25*r0_2)) + a_4*(18*E_2*M_2*r0_4 - L_4*M*(12*M + 5*r0) + L_2*r0*((4 + 48*E_2)*M_3 + 2*(4 - 65*E_2)*M_2*r0 + (-6 + 5*E_2)*M*r0_2 - 2*r0_3)) + a_2*r0*(18*E_2*M_2*r0_4*(-2*M + r0) + L_4*M*(16*M_2 - 26*M*r0 + 9*r0_2) + L_2*r0_2*(8*(-1 + 17*E_2)*M_3 - 2*(-9 + 62*E_2)*M_2*r0 + (-15 + 17*E_2)*M*r0_2 + 4*r0_3))))/expr2_2);\n    A82141 = (-96*L*expr1_2*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/(r0_3*expr2_2);\n    A82321 = (-48*L*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_2;\n    A82501 = (-6*L*r0_3*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_2;\n    A83141 = (-16*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-6*a_10*E_2*M_2*(2*M + r0) + a_9*E*L*M*(24*(1 + 3*E_2)*M_2 + 12*(2 + 3*E_2)*M*r0 + 7*r0_2) + a*E*L*M*r0_4*(L_4*(-312*M_2 + 286*M*r0 - 66*r0_2) + L_2*r0_2*(-338*M_2 + (239 + 24*E_2)*M*r0 - 36*r0_2) + 9*r0_4*(14*M_2 + (-23 + 4*E_2)*M*r0 + 8*r0_2)) - a_8*M*(12*E_2*M*r0*(-2*M_2 + r0_2) + L_2*(12*(1 + 18*E_2)*M_2 + 2*(9 + 61*E_2)*M*r0 + (7 + 9*E_2)*r0_2)) + L_2*r0_4*(2*L_4*M*(54*M_2 - 55*M*r0 + 14*r0_2) + L_2*r0_2*(88*M_3 - (28 + 39*E_2)*M_2*r0 + 20*(-2 + E_2)*M*r0_2 + 16*r0_3) + r0_4*(-52*M_3 + (130 - 43*E_2)*M_2*r0 + 2*(-46 + 11*E_2)*M*r0_2 + 20*r0_3)) + a_7*E*L*M*(L_2*(216*M_2 + 8*(17 + 3*E_2)*M*r0 + 25*r0_2) + r0*(-48*(1 + 2*E_2)*M_3 + 2*(-17 + 132*E_2)*M_2*r0 + (37 + 192*E_2)*M*r0_2 + 25*r0_3)) + a_5*E*L*M*r0*(18*L_4*(4*M + r0) + L_2*(-288*M_3 + 4*(151 - 8*E_2)*M_2*r0 + (281 + 104*E_2)*M*r0_2 - 30*r0_3) + r0_2*(-4*(-5 + 72*E_2)*M_3 + 2*(-113 + 60*E_2)*M_2*r0 + (67 + 312*E_2)*M*r0_2 + 101*r0_3)) + a_3*E*L*M*r0_2*(L_4*(-96*M_2 + 242*M*r0 - 48*r0_2) + L_2*r0*(-880*M_3 + (266 - 96*E_2)*M_2*r0 + 8*(59 + 13*E_2)*M*r0_2 - 91*r0_3) + r0_3*(324*M_3 - 2*(157 + 36*E_2)*M_2*r0 + 3*(-51 + 64*E_2)*M*r0_2 + 155*r0_3)) - a_6*(-24*E_2*M_2*r0_4*(5*M + 2*r0) + L_4*M*(72*M_2 + 2*(25 + 36*E_2)*M*r0 + (16 + 9*E_2)*r0_2) + L_2*r0*(-24*(1 + 12*E_2)*M_4 + 2*(-17 + 349*E_2)*M_3*r0 + 5*(5 + 89*E_2)*M_2*r0_2 + (26 - 17*E_2)*M*r0_3 + 2*r0_4)) - a_4*r0*(3*L_6*M*(8*M + 3*r0) + 108*E_2*M_2*r0_4*(2*M_2 - r0_2) + L_2*r0_2*((20 - 872*E_2)*M_4 + 4*(-32 + 65*E_2)*M_3*r0 + 3*(26 + 213*E_2)*M_2*r0_2 + (35 - 83*E_2)*M*r0_3 - 16*r0_4) + L_4*(-96*M_4 + 2*(85 - 48*E_2)*M_3*r0 + (28 + 277*E_2)*M_2*r0_2 - 4*(3 + 5*E_2)*M*r0_3 + 2*r0_4)) + a_2*r0_2*(54*E_2*M_2*r0_6*(-2*M + r0) + L_6*M*(32*M_2 - 69*M*r0 + 28*r0_2) + L_4*r0*(296*M_4 + 6*(-21 + 50*E_2)*M_3*r0 - 10*(11 + 28*E_2)*M_2*r0_2 + (24 + 49*E_2)*M*r0_3 + 14*r0_4) + L_2*r0_3*(-152*M_4 + 2*(133 + 139*E_2)*M_3*r0 - (29 + 359*E_2)*M_2*r0_2 + (-108 + 79*E_2)*M*r0_3 + 38*r0_4))))/(r0_5*expr2_3);\n    A83321 = (8*(6*a_10*E_2*M_2*(2*M + r0) - a_9*E*L*M*(24*(1 + 3*E_2)*M_2 + 9*(3 + 4*E_2)*M*r0 + 8*r0_2) + a*E*L*M*r0_4*(-9*r0_4*(14*M_2 + (-23 + 4*E_2)*M*r0 + 8*r0_2) + L_2*r0_2*(302*M_2 - (191 + 30*E_2)*M*r0 + 21*r0_2) + L_4*(352*M_2 - 318*M*r0 + 72*r0_2)) + a_8*M*(12*E_2*M*r0*(-2*M_2 + r0_2) + L_2*(12*(1 + 18*E_2)*M_2 + 7*(3 + 19*E_2)*M*r0 + 4*(2 + 3*E_2)*r0_2)) + L_2*r0_4*(-2*L_4*M*(58*M_2 - 59*M*r0 + 15*r0_2) + r0_4*(52*M_3 + (-130 + 43*E_2)*M_2*r0 + 2*(46 - 11*E_2)*M*r0_2 - 20*r0_3) + L_2*r0_2*(-80*M_3 + (12 + 41*E_2)*M_2*r0 + (50 - 21*E_2)*M*r0_2 - 18*r0_3)) - 2*a_7*E*L*M*(L_2*(108*M_2 + (79 + 15*E_2)*M*r0 + 16*r0_2) + r0*(-24*(1 + 2*E_2)*M_3 + (-23 + 132*E_2)*M_2*r0 + 3*(5 + 32*E_2)*M*r0_2 + 12*r0_3)) + 2*a_3*E*L*M*r0_2*(4*L_4*(15*M_2 - 35*M*r0 + 6*r0_2) + r0_3*(-144*M_3 + (145 + 36*E_2)*M_2*r0 + 3*(25 - 32*E_2)*M*r0_2 - 76*r0_3) + L_2*r0*(416*M_3 + (-113 + 60*E_2)*M_2*r0 - 5*(45 + 13*E_2)*M*r0_2 + 35*r0_3)) + a_5*E*L*M*r0*(-6*L_4*(15*M + 4*r0) + L_2*(288*M_3 + 8*(-67 + 5*E_2)*M_2*r0 - (297 + 130*E_2)*M*r0_2 + 17*r0_3) - 2*r0_2*(-16*(-1 + 9*E_2)*M_3 + 3*(-33 + 20*E_2)*M_2*r0 + 6*(5 + 26*E_2)*M*r0_2 + 48*r0_3)) + a_6*(-24*E_2*M_2*r0_4*(5*M + 2*r0) + L_4*M*(72*M_2 + (61 + 90*E_2)*M*r0 + 4*(5 + 3*E_2)*r0_2) + L_2*r0*(-24*(1 + 12*E_2)*M_4 + 2*(-23 + 332*E_2)*M_3*r0 + (21 + 458*E_2)*M_2*r0_2 + 2*(16 - 5*E_2)*M*r0_3 + 4*r0_4)) + a_4*r0*(6*L_6*M*(5*M + 2*r0) + 108*E_2*M_2*r0_4*(2*M_2 - r0_2) + L_2*r0_2*((32 - 848*E_2)*M_4 + 8*(-14 + 29*E_2)*M_3*r0 + (49 + 624*E_2)*M_2*r0_2 + 6*(6 - 13*E_2)*M*r0_3 - 12*r0_4) + L_4*(-96*M_4 - 8*(-17 + 15*E_2)*M_3*r0 + (31 + 335*E_2)*M_2*r0_2 - (5 + 19*E_2)*M*r0_3 + 4*r0_4)) - a_2*r0_2*(54*E_2*M_2*r0_6*(-2*M + r0) + L_6*M*(40*M_2 - 75*M*r0 + 29*r0_2) + L_4*r0*(272*M_4 + 2*(-57 + 178*E_2)*M_3*r0 - 10*(10 + 33*E_2)*M_2*r0_2 + (19 + 52*E_2)*M*r0_3 + 14*r0_4) + L_2*r0_3*(-128*M_4 + 2*(113 + 124*E_2)*M_3*r0 - 3*(5 + 114*E_2)*M_2*r0_2 + 26*(-4 + 3*E_2)*M*r0_3 + 36*r0_4))))/(r0_2*expr2_3);\n    A83501 = -((r0*(-6*a_8*E_2*M_2 + 3*a_7*E*L*M*(4*(1 + 3*E_2)*M + 3*r0) - 3*a_6*M*(2*E_2*M*r0*(-2*M + r0) + L_2*(2*M + 36*E_2*M + 3*r0 + 5*E_2*r0)) + a*E*L*M*r0_3*(L_2*(-392*M_2 + 350*M*r0 - 78*r0_2) + 9*r0_2*(14*M_2 + (-23 + 4*E_2)*M*r0 + 8*r0_2)) + a_5*E*L*M*(6*L_2*(18*M + 5*r0) + r0*(-24*(1 + 2*E_2)*M_2 + (-17 + 156*E_2)*M*r0 + 14*r0_2)) + a_3*E*L*M*r0*(-6*L_2*(24*M_2 - 53*M*r0 + 8*r0_2) + r0_2*((22 - 144*E_2)*M_2 + 12*(-7 + 13*E_2)*M*r0 + 77*r0_2)) + a_4*(54*E_2*M_2*r0_4 - 3*L_4*M*(12*M + 5*r0) + L_2*r0*(12*(1 + 12*E_2)*M_3 + (23 - 393*E_2)*M_2*r0 + (-17 + 18*E_2)*M*r0_2 - 6*r0_3)) + a_2*r0*(54*E_2*M_2*r0_4*(-2*M + r0) + 3*L_4*M*(16*M_2 - 27*M*r0 + 10*r0_2) + L_2*r0_2*((-22 + 412*E_2)*M_3 + (53 - 380*E_2)*M_2*r0 + (-48 + 55*E_2)*M*r0_2 + 14*r0_3)) + L_2*r0_3*(2*L_2*M*(62*M_2 - 63*M*r0 + 16*r0_2) + r0_2*(-52*M_3 + (130 - 43*E_2)*M_2*r0 + 2*(-46 + 11*E_2)*M*r0_2 + 20*r0_3))))/expr2_3);\n    A84121 = (-24*L*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_3;\n    A84301 = (-6*L*r0_3*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_3;\n    A85121 = (-4*(-6*a_10*E_2*M_2*(2*M + r0) + a_9*E*L*M*(24*(1 + 3*E_2)*M_2 + 9*(3 + 4*E_2)*M*r0 + 8*r0_2) + a*E*L*M*r0_4*(L_4*(-360*M_2 + 332*M*r0 - 78*r0_2) + 2*L_2*r0_2*(-152*M_2 + (98 + 15*E_2)*M*r0 - 12*r0_2) + 3*r0_4*(44*M_2 + 12*(-6 + E_2)*M*r0 + 25*r0_2)) - a_8*M*(12*E_2*M*r0*(-2*M_2 + r0_2) + L_2*(12*(1 + 18*E_2)*M_2 + 7*(3 + 19*E_2)*M*r0 + 4*(2 + 3*E_2)*r0_2)) + L_2*r0_4*(4*L_4*M*(30*M_2 - 31*M*r0 + 8*r0_2) + 2*L_2*r0_2*(40*M_3 - (4 + 21*E_2)*M_2*r0 + (-28 + 11*E_2)*M*r0_2 + 10*r0_3) + r0_4*(-56*M_3 + 4*(35 - 11*E_2)*M_2*r0 + (-100 + 23*E_2)*M*r0_2 + 22*r0_3)) + a_7*E*L*M*(2*L_2*(108*M_2 + (79 + 15*E_2)*M*r0 + 16*r0_2) + r0*(-48*(1 + 2*E_2)*M_3 + 44*(-1 + 6*E_2)*M_2*r0 + (29 + 192*E_2)*M*r0_2 + 23*r0_3)) + a_5*E*L*M*r0*(6*L_4*(15*M + 4*r0) + L_2*(-288*M_3 + (548 - 40*E_2)*M_2*r0 + 2*(146 + 65*E_2)*M*r0_2 - 24*r0_3) + r0_2*(-4*(-7 + 72*E_2)*M_3 + 40*(-5 + 3*E_2)*M_2*r0 + 13*(5 + 24*E_2)*M*r0_2 + 97*r0_3)) + a_3*E*L*M*r0_2*(-2*L_4*(60*M_2 - 143*M*r0 + 27*r0_2) - 2*L_2*r0*(424*M_3 + 2*(-61 + 30*E_2)*M_2*r0 - (229 + 65*E_2)*M*r0_2 + 40*r0_3) + r0_3*(300*M_3 - 8*(38 + 9*E_2)*M_2*r0 + 3*(-51 + 64*E_2)*M*r0_2 + 157*r0_3)) - a_6*(-24*E_2*M_2*r0_4*(5*M + 2*r0) + L_4*M*(72*M_2 + (61 + 90*E_2)*M*r0 + 4*(5 + 3*E_2)*r0_2) + L_2*r0*(-24*(1 + 12*E_2)*M_4 + 2*(-22 + 335*E_2)*M_3*r0 + 5*(4 + 91*E_2)*M_2*r0_2 + (31 - 13*E_2)*M*r0_3 + 4*r0_4)) - a_4*r0*(6*L_6*M*(5*M + 2*r0) + 108*E_2*M_2*r0_4*(2*M_2 - r0_2) + L_2*r0_2*((28 - 856*E_2)*M_4 + 4*(-28 + 61*E_2)*M_3*r0 + 57*(1 + 11*E_2)*M_2*r0_2 + 17*(2 - 5*E_2)*M*r0_3 - 14*r0_4) + L_4*(-96*M_4 - 2*(-71 + 60*E_2)*M_3*r0 + (29 + 338*E_2)*M_2*r0_2 - (9 + 22*E_2)*M*r0_3 + 4*r0_4)) + a_2*r0_2*(54*E_2*M_2*r0_6*(-2*M + r0) + 2*L_6*M*(20*M_2 - 39*M*r0 + 16*r0_2) + L_4*r0*(280*M_4 + 120*(-1 + 3*E_2)*M_3*r0 - 2*(53 + 169*E_2)*M_2*r0_2 + 7*(3 + 8*E_2)*M*r0_3 + 16*r0_4) + L_2*r0_3*(-136*M_4 + 2*(122 + 125*E_2)*M_3*r0 - (22 + 349*E_2)*M_2*r0_2 + (-111 + 83*E_2)*M*r0_3 + 40*r0_4))))/(r0_2*expr2_4);\n    A85301 = -((r0*(-6*a_8*E_2*M_2 + 3*a_7*E*L*M*(4*(1 + 3*E_2)*M + 3*r0) - 3*a_6*M*(2*E_2*M*r0*(-2*M + r0) + L_2*(2*M + 36*E_2*M + 3*r0 + 5*E_2*r0)) + a_5*E*L*M*(6*L_2*(18*M + 5*r0) + r0*(-24*(1 + 2*E_2)*M_2 + 4*(-4 + 39*E_2)*M*r0 + 13*r0_2)) + a*E*L*M*r0_3*(-4*L_2*(100*M_2 - 91*M*r0 + 21*r0_2) + 3*r0_2*(44*M_2 + 12*(-6 + E_2)*M*r0 + 25*r0_2)) + a_3*E*L*M*r0*(-18*L_2*(8*M_2 - 18*M*r0 + 3*r0_2) + r0_2*(-4*(-5 + 36*E_2)*M_2 + 12*(-7 + 13*E_2)*M*r0 + 79*r0_2)) + a_4*(54*E_2*M_2*r0_4 - 3*L_4*M*(12*M + 5*r0) + L_2*r0*(12*(1 + 12*E_2)*M_3 + 22*(1 - 18*E_2)*M_2*r0 + (-16 + 21*E_2)*M*r0_2 - 6*r0_3)) + a_2*r0*(54*E_2*M_2*r0_4*(-2*M + r0) + 3*L_4*M*(16*M_2 - 28*M*r0 + 11*r0_2) + L_2*r0_2*(4*(-5 + 104*E_2)*M_3 + 4*(13 - 97*E_2)*M_2*r0 + (-51 + 59*E_2)*M*r0_2 + 16*r0_3)) + L_2*r0_3*(2*L_2*M*(64*M_2 - 66*M*r0 + 17*r0_2) + r0_2*(-56*M_3 + 4*(35 - 11*E_2)*M_2*r0 + (-100 + 23*E_2)*M*r0_2 + 22*r0_3))))/expr2_4);\n    A86101 = (-2*L*r0_3*(-(a_5*E*M) + a_4*(1 + 3*E_2)*L*M + a*E*M*r0*(L_2*(8*M - 6*r0) + 3*r0_2*(-2*M + r0)) + 2*a_3*E*M*(-3*L_2 + r0*(M + r0)) + L*r0*(2*L_2*M*(-2*M + r0) + r0_2*(4*M_2 + (-6 + E_2)*M*r0 + 2*r0_2)) + a_2*L*(3*L_2*M + r0*(-2*(1 + 2*E_2)*M_2 + (-1 + 4*E_2)*M*r0 + 2*r0_2))))/expr2_4;\n    A87101 = -((r0*(-2*a_8*E_2*M_2 + a_7*E*L*M*(4*(1 + 3*E_2)*M + 3*r0) - a_6*M*(2*E_2*M*r0*(-2*M + r0) + L_2*(2*M + 36*E_2*M + 3*r0 + 5*E_2*r0)) + a_5*E*L*M*(2*L_2*(18*M + 5*r0) + r0*(-8*(1 + 2*E_2)*M_2 + (-5 + 52*E_2)*M*r0 + 4*r0_2)) + a*E*L*M*r0_3*(-2*L_2*(68*M_2 - 63*M*r0 + 15*r0_2) + r0_2*(46*M_2 + 3*(-25 + 4*E_2)*M*r0 + 26*r0_2)) + a_3*E*L*M*r0*(-2*L_2*(24*M_2 - 55*M*r0 + 10*r0_2) + r0_2*((6 - 48*E_2)*M_2 + 4*(-7 + 13*E_2)*M*r0 + 27*r0_2)) + a_4*(18*E_2*M_2*r0_4 - L_4*M*(12*M + 5*r0) + L_2*r0*((4 + 48*E_2)*M_3 + 7*(1 - 19*E_2)*M_2*r0 + (-5 + 8*E_2)*M*r0_2 - 2*r0_3)) + a_2*r0*(18*E_2*M_2*r0_4*(-2*M + r0) + L_4*M*(16*M_2 - 29*M*r0 + 12*r0_2) + L_2*r0_2*(2*(-3 + 70*E_2)*M_3 + (17 - 132*E_2)*M_2*r0 + 3*(-6 + 7*E_2)*M*r0_2 + 6*r0_3)) + L_2*r0_3*(2*L_2*M*(22*M_2 - 23*M*r0 + 6*r0_2) + r0_2*(-20*M_3 + 5*(10 - 3*E_2)*M_2*r0 + 4*(-9 + 2*E_2)*M*r0_2 + 8*r0_3))))/expr2_5);\n  }\n\n  /* A_{phi,phi} */\n  {\n    A90060 = 256*L_2*expr4_3;\n    A90080 = (-128*expr1_2*(-24*a_9*E*L*M_2*(2*M + r0) - 4*a_7*E*L*M*(192*M_4 + 5*L_2*M*r0 + 264*M_3*r0 + 180*M_2*r0_2 + 42*M*r0_3 - 6*r0_4) - 4*a*E*L*M*r0_5*(48*L_4*M + L_2*(106*M - 15*r0)*r0_2 + 6*(10*M - 3*r0)*r0_4) + a_8*M*(24*E_2*r0_p_2M_4 + L_2*M*(48*M + 23*r0)) - 4*a_3*E*L*M*r0_2*(48*L_4*M*(2*M + r0) + L_2*r0_2*(274*M_2 + 177*M*r0 - 20*r0_2) + 6*r0_4*(30*M_2 + 17*M*r0 - 7*r0_2)) + r0_7*(24*E_2*M*r0_5 + 2*L_2*r0_2*(46*M_2 + (-47 + 38*E_2)*M*r0 + 12*r0_2) + L_4*(80*M_2 + 2*(-41 + 24*E_2)*M*r0 + 21*r0_2)) - 4*a_5*E*L*M*r0*(6*r0_2*(36*M_3 + 50*M_2*r0 + 13*M*r0_2 - 5*r0_3) + L_2*(304*M_3 + 294*M_2*r0 + 96*M*r0_2 - 5*r0_3)) + a_2*r0_2*(192*L_6*M_3 + 96*E_2*M*r0_7*(2*M + r0) + L_4*r0_2*(224*M_3 + 24*(5 + 8*E_2)*M_2*r0 + 16*(-5 + 6*E_2)*M*r0_2 + 21*r0_3) + 2*L_2*r0_4*(100*M_3 + 6*(3 + 38*E_2)*M_2*r0 + 3*(-23 + 38*E_2)*M*r0_2 + 24*r0_3)) + a_4*r0*(144*E_2*M*r0_5*r0_p_2M_2 + 4*L_4*M*(152*M_3 + 6*(11 + 8*E_2)*M_2*r0 + 3*(5 + 16*E_2)*M*r0_2 + 2*(-5 + 6*E_2)*r0_3) + L_2*r0_2*(188*M_4 + 4*(85 + 228*E_2)*M_3*r0 + 3*(5 + 304*E_2)*M_2*r0_2 + 6*(-15 + 38*E_2)*M*r0_3 + 24*r0_4)) + 2*a_6*M*(10*L_4*M*r0 + 48*E_2*r0_3*r0_p_2M_3 + L_2*(192*M_4 + 16*(9 + 19*E_2)*M_3*r0 + 2*(49 + 228*E_2)*M_2*r0_2 + (-1 + 228*E_2)*M*r0_3 + (-23 + 38*E_2)*r0_4))))/(3.*r0_9*(a_2 + r0*(-2*M + r0)));\n    A90240 = 192*L_2*expr1_2;\n    A90260 = (-64*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-12*a_9*E*L*M*(14*M_2 + 11*M*r0 + 2*r0_2) - 2*a*E*L*M*r0_5*(L_2*(358*M - 39*r0)*r0_2 + 6*(34*M - 9*r0)*r0_4 + L_4*(158*M + 9*r0)) - 2*a_3*E*L*M*r0_2*(6*r0_4*(114*M_2 + 59*M*r0 - 19*r0_2) + L_2*r0_2*(964*M_2 + 598*M*r0 - 19*r0_2) + 2*L_4*(164*M_2 + 79*M*r0 + 12*r0_2)) + r0_5*(48*E_2*M*r0_7 - 2*L_6*(8*M_2 + 2*M*r0 - 3*r0_2) + 2*L_4*r0_2*(54*M_2 + (-75 + 44*E_2)*M*r0 + 24*r0_2) + L_2*r0_4*(136*M_2 + 2*(-79 + 70*E_2)*M*r0 + 45*r0_2)) + a_8*M*(48*E_2*r0_p_2M_4 + L_2*(24*(7 + E_2)*M_2 + (125 + 24*E_2)*M*r0 + 3*(7 + 2*E_2)*r0_2)) - 2*a_5*E*L*M*r0*(3*L_4*(4*M + 5*r0) + 6*r0_2*(156*M_3 + 182*M_2*r0 + 51*M*r0_2 - 9*r0_3) + L_2*(1072*M_3 + 980*M_2*r0 + 386*M*r0_2 + 47*r0_3)) + a_2*r0_2*(192*E_2*M*r0_7*(2*M + r0) + L_6*(328*M_3 - 36*M_2*r0 + 32*M*r0_2 + 6*r0_3) + L_4*r0_2*(380*M_3 + 4*(12 + 85*E_2)*M_2*r0 + 7*(-9 + 26*E_2)*M*r0_2 + 63*r0_3) + L_2*r0_4*(340*M_3 + 12*(-5 + 69*E_2)*M_2*r0 + 3*(-61 + 142*E_2)*M*r0_2 + 99*r0_3)) - 2*a_7*E*L*M*(L_2*(24*M_2 + 82*M*r0 + 27*r0_2) + 6*(128*M_4 + 164*M_3*r0 + 106*M_2*r0_2 + 37*M*r0_3 + 3*r0_4)) + a_4*r0*(288*E_2*M*r0_5*r0_p_2M_2 + 12*L_6*M*(M + 2*r0) + L_4*(1072*M_4 + 8*(38 + 41*E_2)*M_3*r0 + 4*(35 + 88*E_2)*M_2*r0_2 + 2*(33 + 50*E_2)*M*r0_3 + 15*r0_4) + L_2*r0_2*(484*M_4 + 8*(43 + 204*E_2)*M_3*r0 + 3*(-1 + 560*E_2)*M_2*r0_2 + 3*(-5 + 146*E_2)*M*r0_3 + 63*r0_4)) + a_6*(192*E_2*M*r0_3*r0_p_2M_3 + L_4*M*(24*M_2 + 4*(35 + 3*E_2)*M*r0 + 3*(15 + 2*E_2)*r0_2) + L_2*(768*M_5 + 16*(27 + 67*E_2)*M_4*r0 + 36*(7 + 46*E_2)*M_3*r0_2 + 6*(29 + 146*E_2)*M_2*r0_3 + (31 + 158*E_2)*M*r0_4 + 9*r0_5))))/(3.*r0_6*(a_2 + r0*(-2*M + r0)));\n    A90420 = 48*L_2*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2));\n    A90440 = (-8*(-72*a_9*E*L*M*(10*M_2 + 9*M*r0 + 2*r0_2) + 4*a*E*L*M*r0_3*(L_6*(8*M - 6*r0) - 30*L_4*r0_2*(6*M + r0) + 18*r0_6*(-14*M + 3*r0) + 3*L_2*r0_4*(-146*M + 9*r0)) - 4*a_3*E*L*M*r0_2*(6*L_6 + 18*r0_4*(54*M_2 + 25*M*r0 - 5*r0_2) + 12*L_4*(32*M_2 + 15*M*r0 + 7*r0_2) + L_2*r0_2*(1266*M_2 + 721*M*r0 + 64*r0_2)) + r0_3*(144*E_2*M*r0_9 + 8*L_8*M*(-2*M + r0) + L_6*r0_2*(-100*M_2 + 4*(-11 + E_2)*M*r0 + 47*r0_2) + L_2*r0_6*(264*M_2 + 2*(-197 + 192*E_2)*M*r0 + 131*r0_2) + L_4*r0_4*(168*M_2 + 2*(-217 + 120*E_2)*M*r0 + 175*r0_2)) + a_8*(144*E_2*M*r0_p_2M_4 + L_2*(144*(5 + E_2)*M_3 + (635 + 144*E_2)*M_2*r0 + 36*(4 + E_2)*M*r0_2 + 3*r0_3)) - 4*a_5*E*L*M*r0*(6*L_4*(8*M + 9*r0) + 18*r0_2*(84*M_3 + 82*M_2*r0 + 25*M*r0_2 + r0_3) + L_2*(1392*M_3 + 1166*M_2*r0 + 572*M*r0_2 + 175*r0_3)) + a_2*r0_2*(12*L_8*M + 576*E_2*M*r0_7*(2*M + r0) + 2*L_6*(384*M_3 - 8*(15 + E_2)*M_2*r0 + 2*(53 + 4*E_2)*M*r0_2 + 25*r0_3) + 2*L_2*r0_4*(420*M_3 + 2*(-157 + 558*E_2)*M_2*r0 + 27*(-5 + 22*E_2)*M*r0_2 + 162*r0_3) + L_4*r0_2*(960*M_3 + 32*(-17 + 27*E_2)*M_2*r0 + 12*(9 + 44*E_2)*M*r0_2 + 287*r0_3)) - 4*a_7*E*L*M*(L_2*(72*M_2 + 221*M*r0 + 84*r0_2) + 18*(64*M_4 + 76*M_3*r0 + 46*M_2*r0_2 + 23*M*r0_3 + 5*r0_4)) + a_4*r0*(864*E_2*M*r0_5*r0_p_2M_2 + 3*L_6*(32*M_2 + 4*(14 + E_2)*M*r0 + r0_2) + 2*L_4*(1392*M_4 + 4*(25 + 96*E_2)*M_3*r0 + 2*(107 + 240*E_2)*M_2*r0_2 + 8*(44 + 21*E_2)*M*r0_3 + 59*r0_4) + L_2*r0_2*(1740*M_4 + 4*(-7 + 1080*E_2)*M_3*r0 + (-161 + 4608*E_2)*M_2*r0_2 + 6*(83 + 210*E_2)*M*r0_3 + 258*r0_4)) + 2*a_6*(288*E_2*M*r0_3*r0_p_2M_3 + L_4*(72*M_3 + 2*(185 + 24*E_2)*M_2*r0 + 6*(25 + 4*E_2)*M*r0_2 + 3*r0_3) + L_2*(1152*M_5 + 48*(9 + 29*E_2)*M_4*r0 + 2*(77 + 1116*E_2)*M_3*r0_2 + 7*(77 + 180*E_2)*M_2*r0_3 + (259 + 246*E_2)*M*r0_4 + 34*r0_5))))/(3.*r0_3*(a_2 + r0*(-2*M + r0)));\n    A90600 = 4*L_2*r0_6;\n    A90620 = (4*(12*a_7*E*L*M*(13*M + 6*r0) + 6*a_5*E*L*M*(128*M_3 + 76*M_2*r0 + 40*M*r0_2 + 22*r0_3 + L_2*(12*M + 17*r0)) + 2*a*E*L*M*r0_2*(-4*L_4*(4*M - 3*r0) + 6*(22*M - 3*r0)*r0_4 + L_2*r0_2*(94*M + 21*r0)) - 3*a_6*(16*E_2*M*r0_p_2M_3 + L_2*(4*(13 + 3*E_2)*M_2 + (25 + 6*E_2)*M*r0 + r0_2)) - r0_2*(48*E_2*M*r0_7 + 8*L_6*M*(-2*M + r0) + 4*L_2*r0_4*(10*M_2 + (-27 + 17*E_2)*M*r0 + 11*r0_2) + L_4*r0_2*(-36*M_2 + 4*(-10 + E_2)*M*r0 + 29*r0_2)) + 4*a_3*E*L*M*r0*(6*L_4 + 3*r0_2*(58*M_2 + 25*M*r0 + 2*r0_2) + L_2*(100*M_2 + 35*M*r0 + 36*r0_2)) - a_2*r0*(12*L_6*M + 144*E_2*M*r0_5*(2*M + r0) + 4*L_4*(50*M_3 - (33 + 4*E_2)*M_2*r0 + 4*(6 + E_2)*M*r0_2 + 8*r0_3) + L_2*r0_2*(140*M_3 + 4*(-50 + 59*E_2)*M_2*r0 + (23 + 154*E_2)*M*r0_2 + 79*r0_3)) - a_4*(144*E_2*M*r0_3*r0_p_2M_2 + 3*L_4*(12*M_2 + 4*(7 + E_2)*M*r0 + r0_2) + 2*L_2*(192*M_4 + 20*(-3 + 5*E_2)*M_3*r0 + 8*(2 + 17*E_2)*M_2*r0_2 + 13*(7 + 4*E_2)*M*r0_3 + 19*r0_4))))/(3.*(a_2 + r0*(-2*M + r0)));\n    A90800 = (r0_3*(48*a_3*E*L*M - 24*L_2*(2*a_2*M + r0_3) + L_2*r0*(-3*a_2 + r0*(-2*M + r0)) - (24*M*expr8_2)/(a_2 + r0*(-2*M + r0)) + (4*L_2*M*(-3*a_4*E_2 + 6*a_3*E*L + 2*a*E*L*r0*(-4*M + 3*r0) + a_2*(-3*L_2 + 4*E_2*(M - r0)*r0) - r0*(E_2*r0_3 + L_2*(-4*M + 2*r0))))/(a_2 + r0*(-2*M + r0))))/6.;\n    A91060 = (128*L*expr1_2*(4*a_5*E*M*(2*M + r0) - a_4*L*M*(8*M + 3*r0) + 6*a*E*M*r0_3*(L_2 + 2*r0_2) - L*(2*M - r0)*r0_3*(2*L_2 + 3*r0_2) + 2*a_3*E*M*r0*(L_2 + 4*r0*(3*M + 2*r0)) - a_2*L*r0*(2*L_2*M + 3*r0*(6*M_2 + M*r0 - r0_2))))/(r0_5*(a_2 + r0*(-2*M + r0)));\n    A91080 = (-64*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-48*a_14*E_2*M_3*r0_p_2M_2 + 4*a_13*E*L*M_2*(96*M_3 + 226*M_2*r0 + 161*M*r0_2 + 36*r0_3) + 2*a*E*L*M*r0_6*(L_4*r0_4*(56*M_2 + 2*(313 + 222*E_2)*M*r0 - 327*r0_2) + 6*L_6*r0_2*(68*M_2 + 4*(14 + 5*E_2)*M*r0 - 45*r0_2) + 2*L_2*r0_6*(58*M_2 + (-23 + 288*E_2)*M*r0 - 3*r0_2) + 60*L_8*(4*M_2 - r0_2) + 12*r0_8*(30*M_2 + (-41 + 24*E_2)*M*r0 + 13*r0_2)) - (2*M - r0)*r0_8*(48*E_2*M*r0_9 - 24*L_8*M*(4*M - (2 + 5*E_2)*r0) + 2*L_6*r0_2*(-64*M_2 + 2*(-5 + 129*E_2)*M*r0 + 21*r0_2) + 2*L_2*r0_6*(92*M_2 + (-118 + 205*E_2)*M*r0 + 36*r0_2) + L_4*r0_4*(116*M_2 + 2*(-134 + 373*E_2)*M*r0 + 105*r0_2)) + 4*a_3*E*L*M*r0_4*(30*L_8*(12*M_2 - 2*M*r0 - r0_2) + L_4*r0_3*(244*M_3 + 2*(649 + 444*E_2)*M_2*r0 + (899 + 618*E_2)*M*r0_2 - 543*r0_3) + L_2*r0_5*(-216*M_3 + 2*(131 + 864*E_2)*M_2*r0 + 2*(337 + 518*E_2)*M*r0_2 - 177*r0_3) + 6*L_6*r0*(84*M_3 + 4*(46 + 5*E_2)*M_2*r0 + 2*(13 + 10*E_2)*M*r0_2 - 45*r0_3) + 2*r0_7*(204*M_3 + 2*(-139 + 288*E_2)*M_2*r0 + (-119 + 320*E_2)*M*r0_2 + 120*r0_3)) + a_2*r0_4*(-240*L_10*M_2*(2*M - r0) + 24*E_2*M*r0_10*(-46*M_2 + 13*M*r0 + 7*r0_2) + 24*L_8*M*r0*(32*M_3 - 2*(27 + 20*E_2)*M_2*r0 + (3 - 20*E_2)*M*r0_2 + 2*(4 + 5*E_2)*r0_3) - 2*L_2*r0_7*(804*M_4 + 4*(-304 + 685*E_2)*M_3*r0 + 5*(21 + 122*E_2)*M_2*r0_2 + (367 - 763*E_2)*M*r0_3 - 108*r0_4) + 2*L_6*r0_3*(1256*M_4 - 12*(43 + 182*E_2)*M_3*r0 - 2*(287 + 516*E_2)*M_2*r0_2 + (217 + 654*E_2)*M*r0_3 + 21*r0_4) + L_4*r0_5*(880*M_4 + 4*(383 - 1890*E_2)*M_3*r0 - 8*(160 + 361*E_2)*M_2*r0_2 + 21*(-13 + 112*E_2)*M*r0_3 + 210*r0_4)) + 2*a_5*E*L*M*r0_3*(6*L_6*(696*M_3 + 8*(39 + 5*E_2)*M_2*r0 + 20*(1 + E_2)*M*r0_2 - 45*r0_3) + L_4*r0*(32*M_4 + 24*(309 + 74*E_2)*M_3*r0 + 144*(49 + 22*E_2)*M_2*r0_2 + 4*(449 + 285*E_2)*M*r0_3 - 1149*r0_4) + L_2*r0_3*(-4236*M_4 + 4*(515 + 1728*E_2)*M_3*r0 + 3*(2395 + 2992*E_2)*M_2*r0_2 + 276*(11 + 10*E_2)*M*r0_3 - 894*r0_4) + 2*r0_5*(-2340*M_4 + 108*(-7 + 32*E_2)*M_3*r0 + (-249 + 3968*E_2)*M_2*r0_2 + 4*(131 + 280*E_2)*M*r0_3 + 240*r0_4)) + 2*a_11*E*L*M*r0*(5*L_2*M*(144*M_2 + 191*M*r0 + 60*r0_2) + 4*(64*(15 + 8*E_2)*M_5 + 4*(481 + 256*E_2)*M_4*r0 + 3*(529 + 256*E_2)*M_3*r0_2 + (683 + 256*E_2)*M_2*r0_3 + (115 + 32*E_2)*M*r0_4 - 6*r0_5)) - a_12*M*(L_2*M*(192*M_3 + 8*(89 + 90*E_2)*M_2*r0 + (587 + 696*E_2)*M*r0_2 + 6*(23 + 28*E_2)*r0_3) + 24*E_2*r0*(160*M_5 + 440*M_4*r0 + 412*M_3*r0_2 + 170*M_2*r0_3 + 33*M*r0_4 + 3*r0_5)) + 2*a_7*E*L*M*r0_2*(144*L_6*M*r0 + 2*L_4*(4128*M_4 + 8*(611 + 87*E_2)*M_3*r0 + 2*(1285 + 348*E_2)*M_2*r0_2 + 6*(90 + 29*E_2)*M*r0_3 - 195*r0_4) + L_2*r0*(-4736*M_5 + 12*(301 + 384*E_2)*M_4*r0 + 8*(1877 + 1380*E_2)*M_3*r0_2 + (12329 + 7584*E_2)*M_2*r0_3 + 8*(334 + 201*E_2)*M*r0_4 - 750*r0_5) + 12*r0_3*(-1164*M_5 + 4*(-215 + 192*E_2)*M_4*r0 + (235 + 1408*E_2)*M_3*r0_2 + (369 + 832*E_2)*M_2*r0_3 + 4*(39 + 40*E_2)*M*r0_4 + 10*r0_5)) - a_6*r0_2*(144*L_8*M_2*r0 - 48*E_2*M*r0_6*(200*M_4 + 68*M_3*r0 - 31*M_2*r0_2 + M*r0_3 - 5*r0_4) + 4*L_6*M*(1376*M_4 + 4*(353 + 522*E_2)*M_3*r0 + 168*(2 + 9*E_2)*M_2*r0_2 + 2*(29 + 156*E_2)*M*r0_3 - 3*(36 + 23*E_2)*r0_4) + L_4*M*r0*(-9344*M_5 + 8*(35 + 1732*E_2)*M_4*r0 + 8*(1095 + 4148*E_2)*M_3*r0_2 + 26*(229 + 932*E_2)*M_2*r0_3 + 2*(597 + 2624*E_2)*M*r0_4 - (1115 + 1088*E_2)*r0_5) + L_2*r0_3*(-11144*M_6 + 4*(-773 + 3824*E_2)*M_5*r0 + 2*(607 + 16744*E_2)*M_4*r0_2 + (2449 + 25488*E_2)*M_3*r0_3 + (1889 + 4936*E_2)*M_2*r0_4 - 4*(97 + 239*E_2)*M*r0_5 - 72*r0_6)) + 2*a_9*E*L*M*r0*(12*L_4*M*r0*(52*M + 31*r0) + L_2*(6144*M_5 + 32*(437 + 86*E_2)*M_4*r0 + 12*(1097 + 344*E_2)*M_3*r0_2 + 3*(2345 + 688*E_2)*M_2*r0_3 + 2*(665 + 172*E_2)*M*r0_4 - 204*r0_5) + 4*r0*(-2112*M_6 + 4*(-787 + 288*E_2)*M_5*r0 + 2*(289 + 1664*E_2)*M_4*r0_2 + (2521 + 3264*E_2)*M_3*r0_3 + 2*(733 + 672*E_2)*M_2*r0_4 + (307 + 200*E_2)*M*r0_5 - 15*r0_6)) - a_10*M*r0*(2*L_4*M*(360*M_2 + (607 + 312*E_2)*M*r0 + 3*(71 + 52*E_2)*r0_2) + L_2*(768*(5 + 16*E_2)*M_5 + 32*(151 + 821*E_2)*M_4*r0 + 2*(1505 + 13176*E_2)*M_3*r0_2 + 3*(493 + 4568*E_2)*M_2*r0_3 + (65 + 2996*E_2)*M*r0_4 + 6*(-23 + 19*E_2)*r0_5) + 24*E_2*r0*(-352*M_6 - 720*M_5*r0 + 24*M_4*r0_2 + 704*M_3*r0_3 + 450*M_2*r0_4 + 111*M*r0_5 + 13*r0_6)) - a_8*M*r0*(48*L_6*M*r0*(13*M + 3*(3 + E_2)*r0) + 48*E_2*r0_4*(-392*M_5 - 436*M_4*r0 + 68*M_3*r0_2 + 174*M_2*r0_3 + 55*M*r0_4 + 10*r0_5) + 2*L_4*(2048*M_5 + 48*(103 + 172*E_2)*M_4*r0 + 20*(153 + 554*E_2)*M_3*r0_2 + (1525 + 6532*E_2)*M_2*r0_3 + 5*(39 + 314*E_2)*M*r0_4 - 3*(71 + 19*E_2)*r0_5) + L_2*r0*(-8448*M_6 + 32*(-247 + 284*E_2)*M_5*r0 + 4*(1173 + 8392*E_2)*M_4*r0_2 + 4*(1367 + 11612*E_2)*M_3*r0_3 + (3221 + 29360*E_2)*M_2*r0_4 + (437 + 6836*E_2)*M*r0_5 + 2*(-194 + 23*E_2)*r0_6)) - a_4*r0_3*(24*E_2*M*r0_8*(28*M_3 + 42*M_2*r0 - 51*M*r0_2 - 5*r0_3) + 2*L_6*M*r0*(-2288*M_4 + 8*(233 + 396*E_2)*M_3*r0 + 12*(135 + 394*E_2)*M_2*r0_2 + 4*(97 + 267*E_2)*M*r0_3 - (443 + 534*E_2)*r0_4) + L_2*r0_5*(-1392*M_5 + 4*(-331 + 3264*E_2)*M_4*r0 + 8*(-263 + 1827*E_2)*M_3*r0_2 + (2245 + 1328*E_2)*M_2*r0_3 + 12*(18 - 167*E_2)*M*r0_4 - 216*r0_5) + L_4*r0_3*(-8008*M_5 + 80*(-1 + 201*E_2)*M_4*r0 + 2*(1439 + 9676*E_2)*M_3*r0_2 + 6*(457 + 636*E_2)*M_2*r0_3 - 6*(149 + 430*E_2)*M*r0_4 - 105*r0_5) + 24*L_8*M*(116*M_3 + (-13 + 10*E_2)*M*r0_2 - (6 + 5*E_2)*r0_3 + 20*M_2*(r0 + 3*E_2*r0)))))/(3.*r0_11*expr2_2);\n    A91240 = (-32*L*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-12*a_5*E*M*(2*M + r0) + a_4*L*(24*M_2 + 12*M*r0 + r0_2) - 12*a*E*M*r0_3*(2*L_2 + 3*r0_2) - 8*a_3*E*M*r0*(L_2 + 9*M*r0 + 6*r0_2) + L*(2*M - r0)*r0_3*(7*L_2 + 9*r0_2) + a_2*L*r0*(L_2*(8*M + r0) + 2*r0*(24*M_2 + 5*M*r0 - 4*r0_2))))/(r0_2*(a_2 + r0*(-2*M + r0)));\n    A91260 = (-16*(-48*a_14*E_2*M_2*r0_p_2M_2*(7*M + 2*r0) + 8*a_13*E*L*M*(2*M + r0)*(24*(7 + E_2)*M_3 + (365 + 24*E_2)*M_2*r0 + 6*(36 + E_2)*M*r0_2 + 36*r0_3) + 2*a*E*L*M*r0_6*(2*L_6*r0_2*(728*M_2 + 2*(241 + 105*E_2)*M*r0 - 423*r0_2) + 3*L_4*r0_4*(16*M_2 + 2*(283 + 256*E_2)*M*r0 - 287*r0_2) + 210*L_8*(4*M_2 - r0_2) + 24*r0_8*(40*M_2 + (-70 + 39*E_2)*M*r0 + 25*r0_2) + 2*L_2*r0_6*(130*M_2 + (-395 + 1008*E_2)*M*r0 + 165*r0_2)) - (2*M - r0)*r0_8*(192*E_2*M*r0_9 + 4*L_8*(-88*M_2 + (32 + 105*E_2)*M*r0 + 6*r0_2) + 2*L_2*r0_6*(272*M_2 + 2*(-203 + 362*E_2)*M*r0 + 135*r0_2) + L_6*r0_2*(-520*M_2 + 2*(-101 + 902*E_2)*M*r0 + 231*r0_2) + 2*L_4*r0_4*(170*M_2 + (-553 + 1314*E_2)*M*r0 + 234*r0_2)) + 4*a_3*E*L*M*r0_4*(105*L_8*(12*M_2 - 2*M*r0 - r0_2) + 2*L_4*r0_3*(-258*M_3 + 2*(1285 + 777*E_2)*M_2*r0 + (1193 + 1063*E_2)*M*r0_2 - 669*r0_3) + 2*L_6*r0*(848*M_3 + 70*(29 + 3*E_2)*M_2*r0 + 2*(53 + 105*E_2)*M*r0_2 - 393*r0_3) + L_2*r0_5*(-3216*M_3 + 4*(218 + 1503*E_2)*M_2*r0 + 21*(83 + 174*E_2)*M*r0_2 + 147*r0_3) + 4*r0_7*(66*M_3 + (-571 + 918*E_2)*M_2*r0 + (-169 + 532*E_2)*M*r0_2 + 264*r0_3)) + 2*a_5*E*L*M*r0_3*(2*L_6*(7368*M_3 + 4*(811 + 105*E_2)*M_2*r0 + 6*(-9 + 35*E_2)*M*r0_2 - 291*r0_3) + L_4*r0*(-2688*M_4 + 8*(3395 + 786*E_2)*M_3*r0 + 8*(3096 + 1379*E_2)*M_2*r0_2 + 2*(2537 + 1942*E_2)*M*r0_3 - 2151*r0_4) + L_2*r0_3*(-29172*M_4 + 4*(799 + 5976*E_2)*M_3*r0 + 3*(8421 + 10504*E_2)*M_2*r0_2 + 12*(909 + 820*E_2)*M*r0_3 - 126*r0_4) + 4*r0_5*(-5076*M_4 + 24*(-116 + 225*E_2)*M_3*r0 + (-513 + 6484*E_2)*M_2*r0_2 + 2*(680 + 961*E_2)*M*r0_3 + 744*r0_4)) + a_2*r0_4*(-840*L_10*M_2*(2*M - r0) + 24*E_2*M*r0_10*(-154*M_2 + 35*M*r0 + 29*r0_2) - 2*L_2*r0_7*(1692*M_4 + 4*(-917 + 2308*E_2)*M_3*r0 + (343 + 2102*E_2)*M_2*r0_2 - 3*(-458 + 893*E_2)*M*r0_3 - 420*r0_4) + 2*L_8*r0*(1448*M_4 - 4*(661 + 420*E_2)*M_3*r0 + 2*(271 - 420*E_2)*M_2*r0_2 + 5*(37 + 84*E_2)*M*r0_3 + 12*r0_4) + 2*L_6*r0_3*(5552*M_4 - 4*(803 + 1933*E_2)*M_3*r0 - 2*(457 + 1734*E_2)*M_2*r0_2 + (266 + 2265*E_2)*M*r0_3 + 150*r0_4) + L_4*r0_5*(6528*M_4 - 100*(-29 + 264*E_2)*M_3*r0 - 4*(715 + 2518*E_2)*M_2*r0_2 + 79*(-27 + 104*E_2)*M*r0_3 + 1011*r0_4)) - a_4*r0_3*(24*E_2*M*r0_8*(52*M_3 + 118*M_2*r0 - 141*M*r0_2 - 25*r0_3) + 4*L_8*M*(2456*M_3 + 4*(106 + 315*E_2)*M_2*r0 + 6*(-82 + 35*E_2)*M*r0_2 + 7*(1 - 15*E_2)*r0_3) + L_4*r0_3*(-37848*M_5 + 4*(1091 + 13224*E_2)*M_4*r0 + 2*(4783 + 34268*E_2)*M_3*r0_2 + 9*(685 + 1516*E_2)*M_2*r0_3 - 6*(45 + 1474*E_2)*M*r0_4 - 618*r0_5) + 2*L_2*r0_5*(-4992*M_5 + 24*(-113 + 731*E_2)*M_4*r0 + 12*(-242 + 2045*E_2)*M_3*r0_2 + 8*(452 + 341*E_2)*M_2*r0_3 + (791 - 3467*E_2)*M*r0_4 - 450*r0_5) + L_6*r0*(-18528*M_5 + 16*(1024 + 1391*E_2)*M_4*r0 + 8*(1318 + 4127*E_2)*M_3*r0_2 + 4*(-188 + 1737*E_2)*M_2*r0_3 - 22*(38 + 165*E_2)*M*r0_4 - 69*r0_5)) - a_12*M*(L_2*(192*(7 + 6*E_2)*M_4 + 16*(341 + 288*E_2)*M_3*r0 + 17*(329 + 216*E_2)*M_2*r0_2 + 3*(713 + 288*E_2)*M*r0_3 + 18*(15 + E_2)*r0_4) + 24*E_2*r0*(544*M_5 + 1640*M_4*r0 + 1684*M_3*r0_2 + 758*M_2*r0_3 + 151*M*r0_4 + 11*r0_5)) + 2*a_11*E*L*M*(L_2*(576*M_4 + 48*(78 + E_2)*M_3*r0 + (5657 + 48*E_2)*M_2*r0_2 + 6*(473 + 2*E_2)*M*r0_3 + 432*r0_4) + 8*r0*(32*(51 + 26*E_2)*M_5 + 8*(405 + 223*E_2)*M_4*r0 + 3*(1021 + 488*E_2)*M_3*r0_2 + 2*(940 + 271*E_2)*M_2*r0_3 + (647 + 76*E_2)*M*r0_4 + 84*r0_5)) + 2*a_7*E*L*M*r0_2*(-24*L_6*(3*M_2 - 20*M*r0 - 6*r0_2) + 2*L_4*(14544*M_4 + 8*(2235 + 307*E_2)*M_3*r0 + 4*(2075 + 599*E_2)*M_2*r0_2 + (2269 + 578*E_2)*M*r0_3 + 48*r0_4) + 24*r0_3*(-2116*M_5 + 168*(-11 + 7*E_2)*M_4*r0 + (181 + 2264*E_2)*M_3*r0_2 + (729 + 1412*E_2)*M_2*r0_3 + 6*(77 + 48*E_2)*M*r0_4 + 100*r0_5) + L_2*r0*(-26432*M_5 + 4*(689 + 3960*E_2)*M_4*r0 + 8*(6781 + 4830*E_2)*M_3*r0_2 + (42983 + 27000*E_2)*M_2*r0_3 + 4*(3299 + 1458*E_2)*M*r0_4 + 618*r0_5)) - a_6*r0_2*(-6*L_8*M*(8*M_2 - 82*M*r0 - 45*r0_2) - 48*E_2*M*r0_6*(728*M_4 + 356*M_3*r0 - 121*M_2*r0_2 - 43*M*r0_3 - 15*r0_4) + 2*L_6*M*(9696*M_4 + 24*(437 + 614*E_2)*M_3*r0 + 4*(59 + 2608*E_2)*M_2*r0_2 + 16*(40 + 127*E_2)*M*r0_3 + (423 - 443*E_2)*r0_4) + L_2*r0_3*(-41192*M_6 + 4*(-3823 + 6752*E_2)*M_5*r0 + 2*(2127 + 48568*E_2)*M_4*r0_2 + 3*(2001 + 30976*E_2)*M_3*r0_3 + (8091 + 20392*E_2)*M_2*r0_4 - 76*(-8 + 41*E_2)*M*r0_5 - 360*r0_6) + L_4*r0*(-40256*M_6 + 8*(-19 + 5604*E_2)*M_5*r0 + 4*(8619 + 28804*E_2)*M_4*r0_2 + 2*(5675 + 43864*E_2)*M_3*r0_3 + 3*(1895 + 6404*E_2)*M_2*r0_4 + (467 - 3516*E_2)*M*r0_5 - 75*r0_6)) - a_10*M*(L_4*(384*M_4 + 288*(12 + E_2)*M_3*r0 + 2*(3965 + 1224*E_2)*M_2*r0_2 + 3*(1597 + 404*E_2)*M*r0_3 + 18*(45 + 2*E_2)*r0_4) + L_2*r0*(768*(17 + 52*E_2)*M_5 + 32*(390 + 2897*E_2)*M_4*r0 + 2*(4257 + 50792*E_2)*M_3*r0_2 + (10973 + 55200*E_2)*M_2*r0_3 + (5755 + 12236*E_2)*M*r0_4 + 2*(410 + 253*E_2)*r0_5) + 24*E_2*r0_2*(-1312*M_6 - 2800*M_5*r0 - 216*M_4*r0_2 + 2624*M_3*r0_3 + 1894*M_2*r0_4 + 489*M*r0_5 + 47*r0_6)) + 2*a_9*E*L*M*r0*(6*L_4*(24*M_3 - 4*(-96 + E_2)*M_2*r0 + (361 - 2*E_2)*M*r0_2 + 72*r0_3) + L_2*(19968*M_5 + 32*(1559 + 303*E_2)*M_4*r0 + 12*(3933 + 1232*E_2)*M_3*r0_2 + 3*(9007 + 2520*E_2)*M_2*r0_3 + 6*(1567 + 216*E_2)*M*r0_4 + 1140*r0_5) + 8*r0*(-3936*M_6 + 4*(-1493 + 432*E_2)*M_5*r0 + 2*(161 + 2644*E_2)*M_4*r0_2 + (4231 + 5520*E_2)*M_3*r0_3 + 42*(77 + 58*E_2)*M_2*r0_4 + (1250 + 391*E_2)*M*r0_5 + 183*r0_6)) - a_8*r0*(6*L_6*M*(16*M_3 - 8*(-47 + 3*E_2)*M_2*r0 + 2*(262 + 37*E_2)*M*r0_2 + 3*(45 + E_2)*r0_3) - 48*E_2*M*r0_4*(1400*M_5 + 1724*M_4*r0 - 124*M_3*r0_2 - 722*M_2*r0_3 - 259*M*r0_4 - 35*r0_5) + L_4*M*(13312*M_5 + 96*(371 + 606*E_2)*M_4*r0 + 8*(2029 + 10014*E_2)*M_3*r0_2 + 2*(4317 + 23924*E_2)*M_2*r0_3 + 13*(627 + 892*E_2)*M*r0_4 + 14*(117 - 16*E_2)*r0_5) + L_2*r0*(-31488*M_7 + 64*(-443 + 235*E_2)*M_6*r0 + 4*(3539 + 22568*E_2)*M_5*r0_2 + 8*(1609 + 20166*E_2)*M_4*r0_3 + (10687 + 113840*E_2)*M_3*r0_4 + (7329 + 27616*E_2)*M_2*r0_5 + 36*(27 + 11*E_2)*M*r0_6 - 30*r0_7))))/(3.*r0_8*expr2_2);\n    A91420 = (8*L*r0*(12*a_5*E*M*(2*M + r0) - a_4*L*(24*M_2 + 15*M*r0 + 2*r0_2) + 6*a*E*M*r0_3*(5*L_2 + 6*r0_2) - L*(2*M - r0)*r0_3*(8*L_2 + 9*r0_2) + 2*a_3*E*M*r0*(5*L_2 + 12*r0*(3*M + 2*r0)) - a_2*L*r0*(2*L_2*(5*M + r0) + r0*(42*M_2 + 11*M*r0 - 7*r0_2))))/(a_2 + r0*(-2*M + r0));\n    A91440 = (-4*(-144*a_12*E_2*M_2*(10*M_2 + 9*M*r0 + 2*r0_2) + 4*a_11*E*L*M*(144*(5 + E_2)*M_3 + (1733 + 144*E_2)*M_2*r0 + 36*(32 + E_2)*M*r0_2 + 231*r0_3) - (2*M - r0)*r0_5*(288*E_2*M*r0_9 + 16*L_8*M*(-2*M + r0) + 2*L_6*r0_2*(-310*M_2 + (71 + 271*E_2)*M*r0 + 42*r0_2) + 6*L_4*r0_4*(-50*M_2 + (-115 + 298*E_2)*M*r0 + 70*r0_2) + 3*L_2*r0_6*(176*M_2 + 14*(-25 + 38*E_2)*M*r0 + 131*r0_2)) + 2*a*E*L*M*r0_5*(3*L_4*r0_2*(52*M_2 + 4*(115 + 43*E_2)*M*r0 - 243*r0_2) + 10*L_6*(104*M_2 + 2*M*r0 - 27*r0_2) + 3*L_2*r0_4*(-356*M_2 + 24*(3 + 20*E_2)*M*r0 + 53*r0_2) + 6*r0_6*(120*M_2 + 2*(-179 + 90*E_2)*M*r0 + 149*r0_2)) + 2*a_3*E*L*M*r0_3*(4*L_6*(804*M_2 - 142*M*r0 - 69*r0_2) + L_4*r0*(384*M_3 + 4*(1793 + 276*E_2)*M_2*r0 + 4*(303 + 251*E_2)*M*r0_2 - 1055*r0_3) + 4*L_2*r0_3*(-2454*M_3 + (1067 + 1404*E_2)*M_2*r0 + (895 + 996*E_2)*M*r0_2 + 104*r0_3) + 2*r0_5*(-2160*M_3 + 72*(-29 + 42*E_2)*M_2*r0 + 4*(-112 + 501*E_2)*M*r0_2 + 1373*r0_3)) - a_10*M*(L_2*(288*(5 + 6*E_2)*M_3 + 4*(1409 + 876*E_2)*M_2*r0 + 3*(1401 + 472*E_2)*M*r0_2 + 3*(287 + 22*E_2)*r0_3) + 72*E_2*r0*(112*M_4 + 324*M_3*r0 + 258*M_2*r0_2 + 71*M*r0_3 + 5*r0_4)) + a_2*r0_3*(2*L_8*M*(-1072*M_2 + 522*M*r0 + 7*r0_2) + 72*E_2*M*r0_8*(-46*M_2 + 9*M*r0 + 11*r0_2) + 2*L_6*r0*(2544*M_4 - 4*(789 + 544*E_2)*M_3*r0 + 16*(45 - 62*E_2)*M_2*r0_2 + (23 + 537*E_2)*M*r0_3 + 44*r0_4) + L_4*r0_3*(11640*M_4 - 4*(1315 + 3042*E_2)*M_3*r0 - 2*(21 + 3706*E_2)*M_2*r0_2 + (-1365 + 4216*E_2)*M*r0_3 + 623*r0_4) + L_2*r0_5*(1032*M_4 - 108*(-39 + 112*E_2)*M_3*r0 - 2*(1 + 3174*E_2)*M_2*r0_2 + (-3001 + 4554*E_2)*M*r0_3 + 910*r0_4)) + 2*a_9*E*L*M*(3*L_2*(288*M_3 + 16*(55 + E_2)*M_2*r0 + 4*(259 + 2*E_2)*M*r0_2 + 309*r0_3) + 2*r0*(192*(21 + 10*E_2)*M_4 + 4*(1409 + 900*E_2)*M_3*r0 + (5485 + 2304*E_2)*M_2*r0_2 + 2*(1891 + 246*E_2)*M*r0_3 + 929*r0_4)) + 2*a_5*E*L*M*r0_2*(-6*L_6*(6*M + r0) + L_4*(12096*M_3 + 8*(897 + 134*E_2)*M_2*r0 + 4*(107 + 119*E_2)*M*r0_2 + 133*r0_3) + 2*L_2*r0*(-6816*M_4 + 4*(743 + 684*E_2)*M_3*r0 + 10*(845 + 492*E_2)*M_2*r0_2 + 2*(1633 + 918*E_2)*M*r0_3 + 785*r0_4) + 2*r0_3*(-11988*M_4 + 36*(-167 + 156*E_2)*M_3*r0 + (491 + 8064*E_2)*M_2*r0_2 + 36*(83 + 78*E_2)*M*r0_3 + 1872*r0_4)) - a_8*(3*L_4*M*(192*M_3 + 16*(49 + 6*E_2)*M_2*r0 + 4*(412 + 65*E_2)*M*r0_2 + 5*(115 + 4*E_2)*r0_3) - 288*E_2*M*r0_2*(76*M_5 + 132*M_4*r0 - 19*M_3*r0_2 - 102*M_2*r0_3 - 41*M*r0_4 - 4*r0_5) + L_2*r0*(1152*(7 + 20*E_2)*M_5 + 16*(-49 + 2724*E_2)*M_4*r0 + 4*(689 + 9582*E_2)*M_3*r0_2 + (8609 + 12372*E_2)*M_2*r0_3 + 8*(341 + 54*E_2)*M*r0_4 + 5*r0_5)) + a_4*r0_2*(6*L_8*M*(4*M + r0) + 144*E_2*M*r0_6*(48*M_3 - 3*M_2*r0 + 4*M*r0_2 + 2*r0_3) - 2*L_6*(4032*M_4 + 12*(121 + 268*E_2)*M_3*r0 + 2*(-729 + 215*E_2)*M_2*r0_2 + (473 - 269*E_2)*M*r0_3 - 2*r0_4) + L_4*r0*(20544*M_5 - 32*(298 + 525*E_2)*M_4*r0 - 4*(1711 + 6928*E_2)*M_3*r0_2 + 4*(437 - 2283*E_2)*M_2*r0_3 + (-2197 + 3008*E_2)*M*r0_4 + 202*r0_5) + L_2*r0_3*(18528*M_5 - 36*(15 + 308*E_2)*M_4*r0 - 4*(-601 + 8034*E_2)*M_3*r0_2 - (3203 + 12948*E_2)*M_2*r0_3 + 12*(-213 + 335*E_2)*M*r0_4 + 636*r0_5)) + 2*a_7*E*L*M*r0*(3*L_4*(48*M_2 - 4*(-59 + E_2)*M*r0 + 153*r0_2) + 4*L_2*(2880*M_4 + 24*(229 + 42*E_2)*M_3*r0 + (3283 + 1068*E_2)*M_2*r0_2 + 3*(571 + 96*E_2)*M*r0_3 + 560*r0_4) + 2*r0*(-10944*M_5 + 12*(-941 + 288*E_2)*M_4*r0 + 8*(283 + 1134*E_2)*M_3*r0_2 + 25*(235 + 288*E_2)*M_2*r0_3 + 24*(208 + 75*E_2)*M*r0_4 + 1644*r0_5)) - a_6*r0*(6*L_6*M*(16*M_2 - 6*(-19 + 2*E_2)*M*r0 - (-143 + E_2)*r0_2) - 144*E_2*M*r0_4*(232*M_4 + 178*M_3*r0 - 64*M_2*r0_2 - 53*M*r0_3 - 7*r0_4) + L_4*(7680*M_5 + 1344*(11 + 18*E_2)*M_4*r0 + 8*(-356 + 2499*E_2)*M_3*r0_2 + 2*(1663 + 3038*E_2)*M_2*r0_3 + 5*(731 - 104*E_2)*M*r0_4 + r0_5) + L_2*r0*(-21888*M_6 + 48*(-149 + 148*E_2)*M_5*r0 + 60*(121 + 644*E_2)*M_4*r0_2 + 16*(-148 + 3465*E_2)*M_3*r0_3 + (6035 + 20748*E_2)*M_2*r0_4 + 6*(543 - 116*E_2)*M*r0_5 - 114*r0_6))))/(3.*r0_5*expr2_2);\n    A91600 = (2*L*r0_4*(4*a_3*E*M + 12*a*E*M*r0_2 + 3*L*r0_2*(-2*M + r0) - a_2*L*(4*M + r0)))/(a_2 + r0*(-2*M + r0));\n    A91620 = (48*a_10*E_2*M_2*(13*M + 6*r0) - 24*a_9*E*L*M*(4*(13 + 3*E_2)*M_2 + 2*(55 + 3*E_2)*M*r0 + 41*r0_2) - 2*a*E*L*M*r0_4*(36*r0_5*(2*(-17 + 7*E_2)*M + 17*r0) + 2*L_4*(140*M_2 + 116*M*r0 - 93*r0_2) + L_2*r0_2*(-1012*M_2 + 8*(85 + 36*E_2)*M*r0 - 87*r0_2)) + (2*M - r0)*r0_4*(-56*L_6*M*(2*M - r0) + 192*E_2*M*r0_7 + 8*L_2*r0_4*(20*M_2 + (-76 + 85*E_2)*M*r0 + 33*r0_2) + L_4*r0_2*(-420*M_2 + 4*(-12 + 79*E_2)*M*r0 + 129*r0_2)) + 3*a_8*(L_2*(16*(13 + 18*E_2)*M_3 + 16*(49 + 18*E_2)*M_2*r0 + (319 + 26*E_2)*M*r0_2 + 3*r0_3) + 8*E_2*M*r0*(88*M_3 + 262*M_2*r0 + 129*M*r0_2 + 9*r0_3)) - 2*a_7*E*L*M*(3*L_2*(144*M_2 + 4*(54 + E_2)*M*r0 + 175*r0_2) + 8*r0*(8*(33 + 14*E_2)*M_3 + (181 + 202*E_2)*M_2*r0 + (337 + 82*E_2)*M*r0_2 + 193*r0_3)) - 2*a_3*E*L*M*r0_2*(2*L_4*(804*M_2 - 64*M*r0 - 81*r0_2) + L_2*r0*(-2768*M_3 + 28*(79 + 18*E_2)*M_2*r0 + 4*(262 + 151*E_2)*M*r0_2 + 191*r0_3) + 8*r0_3*(-510*M_3 + (-169 + 198*E_2)*M_2*r0 + 5*(-9 + 38*E_2)*M*r0_2 + 205*r0_3)) + a_6*(3*L_4*(96*M_3 + 24*(8 + E_2)*M_2*r0 + 4*(81 + 2*E_2)*M*r0_2 + 3*r0_3) + 24*E_2*M*r0_2*(-280*M_4 - 380*M_3*r0 + 152*M_2*r0_2 + 197*M*r0_3 + 19*r0_4) + L_2*r0*(192*(11 + 28*E_2)*M_4 + 8*(-424 + 1049*E_2)*M_3*r0 + 2*(891 + 2434*E_2)*M_2*r0_2 + 3*(807 + 22*E_2)*M*r0_3 + 43*r0_4)) - 2*a_5*E*L*M*r0*(12*L_4*(3*M + 2*r0) + L_2*(2688*M_3 + 8*(443 + 67*E_2)*M_2*r0 + 8*(87 + 41*E_2)*M*r0_2 + 803*r0_3) + 16*r0*(-420*M_4 + (-223 + 72*E_2)*M_3*r0 + (49 + 191*E_2)*M_2*r0_2 + 2*(61 + 50*E_2)*M*r0_3 + 130*r0_4)) - a_2*r0_2*(-8*L_6*M*(134*M_2 - 57*M*r0 - 5*r0_2) + 24*E_2*M*r0_6*(-30*M_2 + M*r0 + 15*r0_2) + L_4*r0*(3920*M_4 - 32*(111 + 56*E_2)*M_3*r0 - 8*(-152 + 181*E_2)*M_2*r0_2 + 4*(-109 + 156*E_2)*M*r0_3 + 113*r0_4) + L_2*r0_3*(2840*M_4 - 4*(125 + 424*E_2)*M_3*r0 + 2*(519 - 1858*E_2)*M_2*r0_2 + (-1563 + 1450*E_2)*M*r0_3 + 407*r0_4)) + a_4*r0*(24*L_6*M*(M + r0) - 24*E_2*M*r0_4*(252*M_3 + 70*M_2*r0 - 63*M*r0_2 - 3*r0_3) + L_4*(1792*M_4 + 8*(241 + 402*E_2)*M_3*r0 + 4*(-541 + 214*E_2)*M_2*r0_2 + 2*(687 - 142*E_2)*M*r0_3 + 25*r0_4) + L_2*r0*(-6720*M_5 + 16*(124 + 43*E_2)*M_4*r0 + 4*(-145 + 1746*E_2)*M_3*r0_2 + 40*(-29 + 159*E_2)*M_2*r0_3 + (2083 - 782*E_2)*M*r0_4 - 109*r0_5)))/(3.*r0_2*expr2_2);\n    A91800 = -(r0*(-96*a_8*E_2*M_2 + 12*a_7*E*L*M*(4*(4 + E_2)*M + 29*r0) + (2*M - r0)*r0_3*(40*L_4*M*(2*M - r0) - 48*E_2*M*r0_5 + L_2*r0_2*(8*M_2 + 2*(67 - 37*E_2)*M*r0 - 69*r0_2)) - 3*a_6*(16*E_2*M*r0*(4*M_2 + 14*M*r0 + r0_2) + L_2*(16*(2 + 3*E_2)*M_2 + 2*(58 + 5*E_2)*M*r0 + 3*r0_2)) + 4*a*E*L*M*r0_3*(L_2*(-80*M_2 + 106*M*r0 - 33*r0_2) + 3*r0_2*(-20*M_2 + 4*(-11 + 3*E_2)*M*r0 + 27*r0_2)) + 4*a_5*E*L*M*(3*L_2*(12*M + 5*r0) + r0*(32*(3 + E_2)*M_2 + 4*(-7 + 19*E_2)*M*r0 + 191*r0_2)) - a_4*(6*L_4*M*(8*M + 5*r0) + 48*E_2*M*r0_2*(-16*M_3 - 16*M_2*r0 + 12*M*r0_2 + r0_3) + L_2*r0*(192*(1 + 2*E_2)*M_3 + 4*(-196 + 149*E_2)*M_2*r0 + 2*(288 - 23*E_2)*M*r0_2 + 29*r0_3)) + 4*a_3*E*L*M*r0*(2*L_2*(48*M_2 + 35*M*r0 - 9*r0_2) + r0*(-384*M_3 - 4*M_2*r0 + 20*(-4 + 5*E_2)*M*r0_2 + 185*r0_3)) + a_2*r0*(48*E_2*M*r0_4*(4*M_2 - 2*M*r0 + r0_2) + 2*L_4*M*(-64*M_2 + 6*M*r0 + 13*r0_2) + L_2*r0*(768*M_4 + 16*(-47 + 10*E_2)*M_3*r0 + 4*(167 - 166*E_2)*M_2*r0_2 + 10*(-34 + 15*E_2)*M*r0_3 + 49*r0_4))))/(12.*expr2_2);\n    A92040 = (192*L_2*expr1_2)/(a_2 + r0*(-2*M + r0));\n    A92060 = (-32*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(-24*a_10*E_2*M_2*r0_p_2M_2 + 48*a_9*E*L*M*(4*M_3 + 4*M_2*r0 + 3*M*r0_2 + r0_3) - 4*a*E*L*M*r0_6*(L_4*(191*M - 18*r0) + L_2*(427*M - 93*r0)*r0_2 + 6*(31*M - 9*r0)*r0_4) - 4*a_3*E*L*M*r0_3*(L_2*r0_2*(1180*M_2 + 655*M*r0 - 157*r0_2) + L_4*(352*M_2 + 185*M*r0 - 27*r0_2) + 6*r0_4*(84*M_2 + 57*M*r0 - 23*r0_2)) + r0_6*(8*L_6*M*(2*M - r0) + 96*E_2*M*r0_7 + 2*L_4*r0_2*(138*M_2 + (-135 + 82*E_2)*M*r0 + 33*r0_2) + L_2*r0_4*(248*M_2 + 2*(-125 + 134*E_2)*M*r0 + 63*r0_2)) + 4*a_5*E*L*M*r0_2*(3*L_4*(4*M + 5*r0) - L_2*(1120*M_3 + 1232*M_2*r0 + 293*M*r0_2 - 91*r0_3) - 18*r0_2*(36*M_3 + 52*M_2*r0 + 13*M*r0_2 - 7*r0_3)) + 2*a_2*r0_3*(12*E_2*M*r0_7*(23*M + 16*r0) + L_6*(352*M_3 + 42*M_2*r0 - 28*M*r0_2 - 6*r0_3) + L_4*r0_2*(530*M_3 + (237 + 340*E_2)*M_2*r0 + (-183 + 158*E_2)*M*r0_2 + 18*r0_3) + L_2*r0_4*(250*M_3 + 15*(9 + 58*E_2)*M_2*r0 + 9*(-23 + 44*E_2)*M*r0_2 + 54*r0_3)) + 4*a_7*E*L*M*r0*(L_2*(12*M_2 + 47*M*r0 + 27*r0_2) - 6*(128*M_4 + 156*M_3*r0 + 88*M_2*r0_2 + 7*M*r0_3 - 9*r0_4)) - a_8*M*(L_2*(96*M_3 + 24*(4 + E_2)*M_2*r0 + (119 + 36*E_2)*M*r0_2 + 6*(7 + 2*E_2)*r0_3) - 96*E_2*r0*(16*M_4 + 26*M_3*r0 + 17*M_2*r0_2 + 6*M*r0_3 + r0_4)) + a_4*r0_2*(-24*L_6*M*(M + 2*r0) + 288*E_2*M*r0_5*(5*M_2 + 6*M*r0 + 2*r0_2) + 2*L_4*(1120*M_4 + 8*(95 + 44*E_2)*M_3*r0 + (59 + 328*E_2)*M_2*r0_2 + 7*(-27 + 10*E_2)*M*r0_3 - 15*r0_4) + L_2*r0_2*(380*M_4 + 4*(355 + 882*E_2)*M_3*r0 + 3*(51 + 1132*E_2)*M_2*r0_2 + 12*(-37 + 64*E_2)*M*r0_3 + 27*r0_4)) + 2*a_6*r0*(-(L_4*M*(12*M_2 + 4*(19 + 3*E_2)*M*r0 + 3*(15 + 2*E_2)*r0_2)) + 24*E_2*M*r0_3*(46*M_3 + 66*M_2*r0 + 37*M*r0_2 + 8*r0_3) + L_2*(768*M_5 + 16*(39 + 70*E_2)*M_4*r0 + 24*(16 + 71*E_2)*M_3*r0_2 + 6*(-26 + 135*E_2)*M_2*r0_3 + (-161 + 116*E_2)*M*r0_4 - 9*r0_5))))/(3.*r0_7*expr2_2);\n    A92220 = (96*L_2*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/(a_2 + r0*(-2*M + r0));\n    A92240 = (-8*(-72*a_10*E_2*M_2*r0_p_2M_2 + 24*a_9*E*L*M*(24*M_3 + 12*M_2*r0 + 2*M*r0_2 + r0_3) + 4*a*E*L*M*r0_4*(4*L_6*M - 9*L_4*(54*M - 5*r0)*r0_2 + 18*r0_6*(-25*M + 6*r0) + 6*L_2*r0_4*(-165*M + 29*r0)) - 4*a_3*E*L*M*r0_3*(L_2*r0_2*(2778*M_2 + 1535*M*r0 - 241*r0_2) + 18*r0_4*(84*M_2 + 47*M*r0 - 15*r0_2) + 6*L_4*(144*M_2 + 78*M*r0 - r0_2)) + r0_4*(288*E_2*M*r0_9 + 4*L_8*M*(-2*M + r0) - 2*L_6*r0_2*(22*M_2 + (37 + 2*E_2)*M*r0 - 24*r0_2) + 12*L_4*r0_4*(36*M_2 + 4*(-14 + 9*E_2)*M*r0 + 19*r0_2) + 3*L_2*r0_6*(152*M_2 + 2*(-97 + 122*E_2)*M*r0 + 59*r0_2)) - 4*a_5*E*L*M*r0_2*(3*L_4*(2*M + r0) + L_2*(2928*M_3 + 2806*M_2*r0 + 784*M*r0_2 - 70*r0_3) + 6*r0_2*(396*M_3 + 432*M_2*r0 + 95*M*r0_2 - 37*r0_3)) + 2*a_2*r0_3*(36*E_2*M*r0_7*(23*M + 16*r0) + L_6*(864*M_3 - 4*(-9 + E_2)*M_2*r0 + (11 - 2*E_2)*M*r0_2 - 3*r0_3) + 9*L_4*r0_2*(128*M_3 + 2*(11 + 51*E_2)*M_2*r0 + (-25 + 48*E_2)*M*r0_2 + 13*r0_3) + 3*L_2*r0_4*(204*M_3 + 2*(-5 + 348*E_2)*M_2*r0 + 3*(-39 + 122*E_2)*M*r0_2 + 61*r0_3)) + 4*a_7*E*L*M*r0*(L_2*(24*M_2 - 19*M*r0 + 3*r0_2) - 6*(384*M_4 + 444*M_3*r0 + 208*M_2*r0_2 + 27*M*r0_3 - 11*r0_4)) + a_8*(288*E_2*M*r0*(16*M_4 + 26*M_3*r0 + 17*M_2*r0_2 + 6*M*r0_3 + r0_4) + L_2*(-288*M_4 - 48*E_2*M_3*r0 + (73 - 24*E_2)*M_2*r0_2 + 18*M*r0_3 + 6*r0_4)) + a_4*r0_2*(6*L_6*(2*M_2 + 2*M*r0 + r0_2) + 864*E_2*M*r0_5*(5*M_2 + 6*M*r0 + 2*r0_2) + 2*L_4*(2928*M_4 + 4*(341 + 216*E_2)*M_3*r0 + 4*(47 + 225*E_2)*M_2*r0_2 + 108*(-1 + 2*E_2)*M*r0_3 + 9*r0_4) + 3*L_2*r0_2*(844*M_4 + 4*(181 + 696*E_2)*M_3*r0 + (-1 + 2808*E_2)*M_2*r0_2 + 12*(-9 + 61*E_2)*M*r0_3 + 69*r0_4)) + 2*a_6*r0*(L_4*(-24*M_3 + 2*(25 + 3*E_2)*M_2*r0 + 15*M*r0_2 + 6*r0_3) + 72*E_2*M*r0_3*(46*M_3 + 66*M_2*r0 + 37*M*r0_2 + 8*r0_3) + L_2*(2304*M_5 + 48*(33 + 61*E_2)*M_4*r0 + 2*(259 + 2124*E_2)*M_3*r0_2 + (-49 + 2112*E_2)*M_2*r0_3 + 3*(-31 + 122*E_2)*M*r0_4 + 12*r0_5))))/(3.*r0_4*expr2_2);\n    A92400 = (12*L_2*r0_6)/(a_2 + r0*(-2*M + r0));\n    A92420 = (-2*(-72*a_8*E_2*M_2*(2*M + r0) + 48*a_7*E*L*M*(6*M_2 - 3*M*r0 - 2*r0_2) + 12*a*E*L*M*r0_3*(L_4*(8*M - 4*r0) + 6*r0_4*(-19*M + 3*r0) + L_2*r0_2*(-119*M + 5*r0)) - 24*a_5*E*L*M*r0*(192*M_3 + 114*M_2*r0 + 25*M*r0_2 + r0_3 + L_2*(5*M + 8*r0)) - 12*a_3*E*L*M*r0_2*(4*L_4 + 12*r0_2*(23*M_2 + 12*M*r0 - 2*r0_2) + L_2*(224*M_2 + 99*M*r0 + 17*r0_2)) + r0_3*(288*E_2*M*r0_7 + 24*L_6*M*(-2*M + r0) + L_4*r0_2*(-132*M_2 - 152*M*r0 + 109*r0_2) + L_2*r0_4*(168*M_2 + 62*(-7 + 6*E_2)*M*r0 + 175*r0_2)) + 3*a_6*(L_2*(-48*M_3 + 4*(18 + 5*E_2)*M_2*r0 + 6*(7 + 2*E_2)*M*r0_2 + 3*r0_3) + 24*E_2*M*r0*(32*M_3 + 36*M_2*r0 + 17*M*r0_2 + 4*r0_3)) + a_2*r0_2*(24*L_6*M + 216*E_2*M*r0_5*(5*M + 4*r0) + 2*L_4*(672*M_3 - 6*(19 + 4*E_2)*M_2*r0 + 6*(17 + 2*E_2)*M*r0_2 + 35*r0_3) + L_2*r0_2*(612*M_3 + 2*(-167 + 654*E_2)*M_2*r0 + 78*(-1 + 10*E_2)*M*r0_2 + 269*r0_3)) + a_4*r0*(3*L_4*(20*M_2 + 4*(13 + 2*E_2)*M*r0 + 3*r0_2) + 216*E_2*M*r0_3*(10*M_2 + 11*M*r0 + 4*r0_2) + L_2*(2304*M_4 + 48*(3 + 28*E_2)*M_3*r0 + 6*(3 + 236*E_2)*M_2*r0_2 + 2*(151 + 222*E_2)*M*r0_3 + 103*r0_4))))/(3.*r0*expr2_2);\n    A92600 = -(r0_2*(-24*a_6*E_2*M_2 + 24*a_5*E*L*M*(2*M - 3*r0) - 24*a_3*E*L*M*r0*(2*L_2 + 32*M_2 + M*r0 + r0_2) + 8*a*E*L*M*r0_2*(2*L_2*(5*M - 3*r0) - 39*M*r0_2) + 3*a_4*(L_2*(-8*M_2 + 8*(3 + E_2)*M*r0 + r0_2) + 16*E_2*M*r0*(8*M_2 + 5*M*r0 + 2*r0_2)) + r0_2*(96*E_2*M*r0_5 + 20*L_4*M*(-2*M + r0) + L_2*r0_2*(-40*M_2 + 2*(-51 + 2*E_2)*M*r0 + 61*r0_2)) + 2*a_2*r0*(12*L_4*M + 12*E_2*M*r0_3*(7*M + 8*r0) + L_2*(192*M_3 - 4*(27 + 5*E_2)*M_2*r0 + (43 + 14*E_2)*M*r0_2 + 32*r0_3))))/(6.*expr2_2);\n    A93040 = (32*L*(a_2*(2*M + r0) + r0*(L_2 + r0_2))*(12*a_5*E*M*(2*M + r0) - a_4*L*(24*M_2 + 12*M*r0 + r0_2) + 12*a*E*M*r0_3*(2*L_2 + 3*r0_2) + 8*a_3*E*M*r0*(L_2 + 9*M*r0 + 6*r0_2) + L*r0_3*(L_2*(-15*M + 8*r0) + r0_2*(-19*M + 10*r0)) - a_2*L*r0*(L_2*(8*M + r0) + r0*(50*M_2 + 9*M*r0 - 9*r0_2))))/(r0_2*expr2_2);\n    A93060 = (-16*(24*a_14*E_2*M_2*r0_p_2M_2*(M + 4*r0) - 4*a_13*E*L*M*(2*M + r0)*(24*M_3 + (-37 + 24*E_2)*M_2*r0 + 12*(1 + E_2)*M*r0_2 + 24*r0_3) + 2*a*E*L*M*r0_6*(L_4*r0_4*(164*M_2 + 4*(562 + 393*E_2)*M*r0 - 1203*r0_2) + 2*L_6*r0_2*(658*M_2 + 6*(106 + 35*E_2)*M*r0 - 489*r0_2) + 210*L_8*(4*M_2 - r0_2) + 6*r0_8*(180*M_2 + 2*(-127 + 72*E_2)*M*r0 + 81*r0_2) + L_2*r0_6*(1176*M_2 + 4*(-233 + 495*E_2)*M*r0 + 141*r0_2)) + 2*a_3*E*L*M*r0_4*(210*L_8*(12*M_2 - 2*M*r0 - r0_2) + 2*L_4*r0_3*(-506*M_3 + (5351 + 3108*E_2)*M_2*r0 + (3245 + 2204*E_2)*M*r0_2 - 1998*r0_3) + L_2*r0_5*(876*M_3 + 2*(797 + 5814*E_2)*M_2*r0 + 12*(271 + 593*E_2)*M*r0_2 - 1035*r0_3) + 4*L_6*r0*(760*M_3 + (1987 + 210*E_2)*M_2*r0 + 42*(8 + 5*E_2)*M*r0_2 - 492*r0_3) + 4*r0_7*(378*M_3 + (-791 + 2088*E_2)*M_2*r0 + (-311 + 950*E_2)*M*r0_2 + 339*r0_3)) + r0_8*(24*E_2*M*r0_9*(-15*M + 7*r0) + 2*L_8*M*(2*M - r0)*(154*M - 3*(27 + 70*E_2)*r0) + L_6*r0_2*(560*M_3 + (46 - 3580*E_2)*M_2*r0 + (-487 + 1794*E_2)*M*r0_2 + 162*r0_3) + L_2*r0_6*(-1236*M_3 + (2198 - 2760*E_2)*M_2*r0 + 2*(-638 + 683*E_2)*M*r0_2 + 243*r0_3) + L_4*r0_4*(-1340*M_3 + 4*(737 - 1279*E_2)*M_2*r0 + (-1961 + 2560*E_2)*M*r0_2 + 411*r0_3)) + 2*a_5*E*L*M*r0_3*(2*L_6*(7296*M_3 + 4*(886 + 105*E_2)*M_2*r0 + 6*(44 + 35*E_2)*M*r0_2 - 531*r0_3) + L_4*r0*(-1888*M_4 + 4*(6037 + 1536*E_2)*M_3*r0 + 8*(3396 + 1397*E_2)*M_2*r0_2 + 8*(775 + 514*E_2)*M*r0_3 - 4473*r0_4) + L_2*r0_3*(-13992*M_4 + 4*(3067 + 5850*E_2)*M_3*r0 + 42*(597 + 724*E_2)*M_2*r0_2 + 6*(1415 + 1576*E_2)*M*r0_3 - 3537*r0_4) + 2*r0_5*(-9972*M_4 + 216*(-11 + 62*E_2)*M_3*r0 + (579 + 14000*E_2)*M_2*r0_2 + 2*(799 + 1634*E_2)*M*r0_3 + 468*r0_4)) + a_2*r0_4*(-840*L_10*M_2*(2*M - r0) - 72*E_2*M*r0_10*(60*M_2 - 19*M*r0 - 8*r0_2) + 2*L_8*r0*(1552*M_4 - 60*(37 + 28*E_2)*M_3*r0 - 30*(1 + 28*E_2)*M_2*r0_2 + (367 + 420*E_2)*M*r0_3 + 12*r0_4) + L_6*r0_3*(10152*M_4 - 4*(1137 + 3826*E_2)*M_3*r0 - 4*(991 + 1814*E_2)*M_2*r0_2 + (1483 + 4540*E_2)*M*r0_3 + 231*r0_4) + L_2*r0_7*(-4200*M_4 + 4*(1777 - 5538*E_2)*M_3*r0 - 4*(162 + 353*E_2)*M_2*r0_2 + (-2345 + 4988*E_2)*M*r0_3 + 723*r0_4) + L_4*r0_5*(2680*M_4 + 4*(1321 - 6546*E_2)*M_3*r0 - 10*(385 + 976*E_2)*M_2*r0_2 + (-1359 + 8000*E_2)*M*r0_3 + 861*r0_4)) - 4*a_11*E*L*M*r0*(L_2*(12*(-53 + 3*E_2)*M_3 + 2*(-404 + 15*E_2)*M_2*r0 + 3*(-33 + 2*E_2)*M*r0_2 + 72*r0_3) - 2*(80*(51 + 22*E_2)*M_5 + 8*(945 + 464*E_2)*M_4*r0 + 21*(251 + 128*E_2)*M_3*r0_2 + 8*(197 + 95*E_2)*M_2*r0_3 + 2*(-7 + 31*E_2)*M*r0_4 - 90*r0_5)) - a_12*M*(L_2*(-96*M_4 + 8*(85 + 123*E_2)*M_3*r0 + 4*(109 + 237*E_2)*M_2*r0_2 + 9*(-17 + 24*E_2)*M*r0_3 - 6*(15 + E_2)*r0_4) + 24*E_2*r0*(680*M_5 + 1604*M_4*r0 + 1314*M_3*r0_2 + 481*M_2*r0_3 + 92*M*r0_4 + 11*r0_5)) + 2*a_7*E*L*M*r0_2*(24*L_6*(3*M_2 + 19*M*r0 - 2*r0_2) + 2*L_4*(14688*M_4 + 4*(4395 + 608*E_2)*M_3*r0 + (9365 + 2492*E_2)*M_2*r0_2 + 4*(355 + 161*E_2)*M*r0_3 - 912*r0_4) + L_2*r0*(-23808*M_5 + 24*(745 + 672*E_2)*M_4*r0 + 4*(15251 + 9474*E_2)*M_3*r0_2 + 18*(2353 + 1432*E_2)*M_2*r0_3 + 2*(2963 + 2736*E_2)*M*r0_4 - 3549*r0_5) - 12*r0_3*(4452*M_5 - 120*(-25 + 24*E_2)*M_4*r0 - (1393 + 5248*E_2)*M_3*r0_2 - (1445 + 2848*E_2)*M_2*r0_3 - (351 + 452*E_2)*M*r0_4 + 43*r0_5)) + a_4*r0_3*(24*E_2*M*r0_8*(-174*M_3 - 145*M_2*r0 + 234*M*r0_2 + 15*r0_3) - 2*L_8*M*(4864*M_3 + 4*(263 + 630*E_2)*M_2*r0 + 420*(-1 + E_2)*M*r0_2 - 7*(49 + 30*E_2)*r0_3) + L_6*r0*(18016*M_5 - 8*(1311 + 2684*E_2)*M_4*r0 - 8*(1805 + 4226*E_2)*M_3*r0_2 - 4*(871 + 1914*E_2)*M_2*r0_3 + 2*(1885 + 1852*E_2)*M*r0_4 + 69*r0_5) + L_4*r0_3*(28492*M_5 + 4*(503 - 13140*E_2)*M_4*r0 - (12553 + 67264*E_2)*M_3*r0_2 - 108*(88 + 115*E_2)*M_2*r0_3 + 21*(173 + 412*E_2)*M*r0_4 + 489*r0_5) + L_2*r0_5*(8364*M_5 - 60*(-83 + 1002*E_2)*M_4*r0 - 63*(-57 + 796*E_2)*M_3*r0_2 + 20*(-349 + 158*E_2)*M_2*r0_3 + (-217 + 6298*E_2)*M*r0_4 + 711*r0_5)) + 4*a_9*E*L*M*r0*(L_4*(-108*M_3 + 6*(139 + 2*E_2)*M_2*r0 + 3*(129 + 2*E_2)*M*r0_2 - 72*r0_3) + L_2*(10560*M_5 + 8*(3409 + 612*E_2)*M_4*r0 + 6*(4067 + 1186*E_2)*M_3*r0_2 + 3*(3591 + 1156*E_2)*M_2*r0_3 + 3*(303 + 190*E_2)*M*r0_4 - 666*r0_5) + r0*(-16128*M_6 + 8*(-2749 + 936*E_2)*M_5*r0 + 4*(2017 + 5984*E_2)*M_4*r0_2 + 2*(10151 + 11760*E_2)*M_3*r0_3 + 276*(33 + 32*E_2)*M_2*r0_4 + 8*(85 + 131*E_2)*M*r0_5 - 447*r0_6)) - a_6*r0_2*(6*L_8*M*(8*M_2 + 74*M*r0 - 15*r0_2) - 24*E_2*M*r0_6*(1320*M_4 + 320*M_3*r0 - 103*M_2*r0_2 + 106*M*r0_3 - 40*r0_4) + 4*L_6*M*(4896*M_4 + 48*(109 + 152*E_2)*M_3*r0 + (1753 + 5510*E_2)*M_2*r0_2 + 2*(10 + 559*E_2)*M*r0_3 - (564 + 241*E_2)*r0_4) + L_2*r0_3*(-43656*M_6 + 4*(-3607 + 16068*E_2)*M_5*r0 + 2*(4865 + 68696*E_2)*M_4*r0_2 + (12817 + 79104*E_2)*M_3*r0_3 + 3*(1471 + 2856*E_2)*M_2*r0_4 - (2243 + 2632*E_2)*M*r0_5 - 225*r0_6) + L_4*r0*(-38784*M_6 + 16*(109 + 2906*E_2)*M_5*r0 + 64*(558 + 1775*E_2)*M_4*r0_2 + 394*(65 + 212*E_2)*M_3*r0_3 + (2097 + 17020*E_2)*M_2*r0_4 - 7*(751 + 508*E_2)*M*r0_5 - 39*r0_6)) + a_10*M*r0*(L_4*(72*(-19 + 6*E_2)*M_3 - 4*(523 + 372*E_2)*M_2*r0 - 3*(41 + 268*E_2)*M*r0_2 + 6*(45 + 2*E_2)*r0_3) - L_2*(960*(17 + 44*E_2)*M_5 + 16*(1374 + 6193*E_2)*M_4*r0 + 2*(5859 + 46232*E_2)*M_3*r0_2 + (2087 + 41976*E_2)*M_2*r0_3 + (-2105 + 8516*E_2)*M*r0_4 + 2*(-463 + 226*E_2)*r0_5) - 24*E_2*r0*(-1344*M_6 - 2416*M_5*r0 + 444*M_4*r0_2 + 2452*M_3*r0_3 + 1366*M_2*r0_4 + 327*M*r0_5 + 48*r0_6)) - a_8*r0*(-6*L_6*M*(24*M_3 - 24*(12 + E_2)*M_2*r0 - 2*(60 + 41*E_2)*M*r0_2 + (45 + E_2)*r0_3) - 24*E_2*M*r0_4*(2808*M_5 + 2772*M_4*r0 - 624*M_3*r0_2 - 1006*M_2*r0_3 - 287*M*r0_4 - 75*r0_5) + L_4*M*(14080*M_5 + 48*(827 + 1224*E_2)*M_4*r0 + 8*(3331 + 9732*E_2)*M_3*r0_2 + (10101 + 44320*E_2)*M_2*r0_3 + 2*(-987 + 5024*E_2)*M*r0_4 - 4*(624 + 89*E_2)*r0_5) + L_2*r0*(-32256*M_7 + 32*(-937 + 660*E_2)*M_6*r0 + 8*(2729 + 16220*E_2)*M_5*r0_2 + 4*(6837 + 43484*E_2)*M_4*r0_3 + (11707 + 92136*E_2)*M_3*r0_4 + (-1673 + 18680*E_2)*M_2*r0_5 + (-2227 + 502*E_2)*M*r0_6 + 6*r0_7))))/(3.*r0_8*expr2_3);\n    A93220 = (16*L*r0*(12*a_5*E*M*(2*M + r0) - a_4*L*(24*M_2 + 15*M*r0 + 2*r0_2) + 6*a*E*M*r0_3*(5*L_2 + 6*r0_2) + 2*a_3*E*M*r0*(5*L_2 + 12*r0*(3*M + 2*r0)) + L*r0_3*(L_2*(-17*M + 9*r0) + r0_2*(-19*M + 10*r0)) - 2*a_2*L*r0*(L_2*(5*M + r0) + r0*(22*M_2 + 5*M*r0 - 4*r0_2))))/expr2_2;\n    A93240 = (-4*(-24*a_12*E_2*M_2*(24*M_2 + 10*M*r0 - r0_2) + 4*a_11*E*L*M*(96*(3 + E_2)*M_3 + (1213 + 48*E_2)*M_2*r0 + 846*M*r0_2 + 150*r0_3) + 4*a*E*L*M*r0_5*(L_4*r0_2*(350*M_2 + (1549 + 534*E_2)*M*r0 - 888*r0_2) + L_6*(1048*M_2 + 6*M*r0 - 267*r0_2) + 3*L_2*r0_4*(-20*M_2 + 4*(5 + 117*E_2)*M*r0 - 17*r0_2) + 9*r0_6*(96*M_2 + 12*(-17 + 9*E_2)*M*r0 + 77*r0_2)) + 4*a_3*E*L*M*r0_3*(L_6*(3180*M_2 - 520*M*r0 - 261*r0_2) + L_4*r0*(928*M_3 + 6*(1057 + 178*E_2)*M_2*r0 + 2*(975 + 532*E_2)*M*r0_2 - 1479*r0_3) + L_2*r0_3*(-5322*M_3 + (4487 + 5454*E_2)*M_2*r0 + (3779 + 3828*E_2)*M*r0_2 - 921*r0_3) + 3*r0_5*(-1176*M_3 + 44*(-17 + 54*E_2)*M_2*r0 + 88*(-2 + 13*E_2)*M*r0_2 + 545*r0_3)) + r0_5*(72*E_2*M*r0_9*(-15*M + 7*r0) + 2*L_8*M*(20*M_2 - 16*M*r0 + 3*r0_2) + 2*L_6*r0_2*(912*M_3 - 10*(79 + 107*E_2)*M_2*r0 + (95 + 537*E_2)*M*r0_2 + 36*r0_3) + 3*L_2*r0_6*(-820*M_3 + 2*(853 - 1004*E_2)*M_2*r0 + 2*(-549 + 499*E_2)*M*r0_2 + 225*r0_3) + L_4*r0_4*(-112*M_3 + (2694 - 6896*E_2)*M_2*r0 + (-2657 + 3464*E_2)*M*r0_2 + 669*r0_3)) - a_10*M*(L_2*(576*(1 + 2*E_2)*M_3 + 4*(1153 + 753*E_2)*M_2*r0 + 3*(1087 + 428*E_2)*M*r0_2 + 18*(31 + 2*E_2)*r0_3) + 24*E_2*r0*(888*M_4 + 1756*M_3*r0 + 1080*M_2*r0_2 + 265*M*r0_3 + 30*r0_4)) + 4*a_5*E*L*M*r0_2*(6*L_6*r0 + L_4*(12240*M_3 + 2*(3411 + 530*E_2)*M_2*r0 + (881 + 530*E_2)*M*r0_2 - 546*r0_3) + L_2*r0*(-11664*M_4 + 2*(5207 + 2808*E_2)*M_3*r0 + 6*(3051 + 1589*E_2)*M_2*r0_2 + (5647 + 3444*E_2)*M*r0_3 - 1014*r0_4) + 3*r0_3*(-8076*M_4 + 12*(-191 + 396*E_2)*M_3*r0 + (1525 + 5784*E_2)*M_2*r0_2 + 6*(223 + 248*E_2)*M*r0_3 + 461*r0_4)) + 4*a_9*E*L*M*(3*L_2*(96*M_3 - 2*(-227 + E_2)*M_2*r0 + 465*M*r0_2 + 99*r0_3) + r0*(96*(111 + 43*E_2)*M_4 + 4*(3361 + 1764*E_2)*M_3*r0 + (7885 + 3576*E_2)*M_2*r0_2 + 6*(533 + 86*E_2)*M*r0_3 + 492*r0_4)) + a_2*r0_3*(10*L_8*M*(-424*M_2 + 210*M*r0 + r0_2) + 72*E_2*M*r0_8*(-120*M_2 + 41*M*r0 + 18*r0_2) + 8*L_6*r0*(1172*M_4 - (1181 + 1063*E_2)*M_3*r0 - (17 + 531*E_2)*M_2*r0_2 + (123 + 265*E_2)*M*r0_3 + 24*r0_4) + 3*L_2*r0_5*(304*M_4 - 4*(-637 + 2710*E_2)*M_3*r0 - 8*(32 + 257*E_2)*M_2*r0_2 + 2*(-753 + 1402*E_2)*M*r0_3 + 495*r0_4) + L_4*r0_3*(18712*M_4 - 8*(804 + 3091*E_2)*M_3*r0 - 6*(597 + 2320*E_2)*M_2*r0_2 + 3*(-223 + 2720*E_2)*M*r0_3 + 969*r0_4)) - a_8*(6*L_4*M*(64*M_3 - 6*(-73 + 2*E_2)*M_2*r0 + 4*(179 + 43*E_2)*M*r0_2 + (185 + 8*E_2)*r0_3) - 24*E_2*M*r0_2*(1872*M_5 + 2496*M_4*r0 - 1020*M_3*r0_2 - 1892*M_2*r0_3 - 623*M*r0_4 - 99*r0_5) + L_2*r0*(576*(37 + 86*E_2)*M_5 + 8*(1454 + 11103*E_2)*M_4*r0 + 3*(2035 + 21028*E_2)*M_3*r0_2 + 2*(2761 + 8838*E_2)*M_2*r0_3 + 6*(155 + 113*E_2)*M*r0_4 + 6*r0_5)) - a_4*r0_2*(12*L_8*M*r0 - 72*E_2*M*r0_6*(108*M_3 - 54*M_2*r0 + 97*M*r0_2 + 2*r0_3) + 2*L_6*(8160*M_4 + 12*(233 + 530*E_2)*M_3*r0 + 2*(-663 + 535*E_2)*M_2*r0_2 - (91 + 517*E_2)*M*r0_3 + 12*r0_4) + 3*L_2*r0_3*(-12132*M_5 + 4*(29 + 4330*E_2)*M_4*r0 + (743 + 23052*E_2)*M_3*r0_2 + 2*(1235 + 1986*E_2)*M_2*r0_3 - 4*(-92 + 603*E_2)*M*r0_4 - 313*r0_5) - 2*L_4*r0*(19440*M_5 - 4*(1853 + 4444*E_2)*M_4*r0 - 2*(4929 + 13514*E_2)*M_3*r0_2 - 6*(215 + 1322*E_2)*M_2*r0_3 + 15*(57 + 196*E_2)*M*r0_4 + 135*r0_5)) + 4*a_7*E*L*M*r0*(L_4*(-18*M_2 + 516*M*r0 + 153*r0_2) + L_2*(12384*M_4 + 12*(1937 + 340*E_2)*M_3*r0 + (13777 + 4026*E_2)*M_2*r0_2 + (4079 + 1020*E_2)*M*r0_3 + 153*r0_4) + 3*r0*(-7488*M_5 + 4*(-1601 + 648*E_2)*M_4*r0 + 32*(118 + 213*E_2)*M_3*r0_2 + 3*(1379 + 1528*E_2)*M_2*r0_3 + 6*(281 + 140*E_2)*M*r0_4 + 261*r0_5)) - a_6*r0*(-12*L_6*M*(2*M_2 - 86*M*r0 - (47 + E_2)*r0_2) - 144*E_2*M*r0_4*(444*M_4 + 226*M_3*r0 - 104*M_2*r0_2 - 39*M*r0_3 - 16*r0_4) + 2*L_4*(8256*M_5 + 12*(1349 + 2040*E_2)*M_4*r0 + 28*(113 + 675*E_2)*M_3*r0_2 + (1359 + 5128*E_2)*M_2*r0_3 - 71*(-5 + 8*E_2)*M*r0_4 + 15*r0_5) + L_2*r0*(-44928*M_6 + 48*(-353 + 486*E_2)*M_5*r0 + 16*(1403 + 6789*E_2)*M_4*r0_2 + 2*(4949 + 50898*E_2)*M_3*r0_3 + (5921 + 28164*E_2)*M_2*r0_4 - 84*(-3 + 14*E_2)*M*r0_5 - 123*r0_6))))/(3.*r0_5*expr2_3);\n    A93400 = (2*L*r0_4*(12*a_3*E*M + 36*a*E*M*r0_2 - 3*a_2*L*(4*M + r0) + L*r0_2*(-19*M + 10*r0)))/expr2_2;\n    A93420 = (24*a_10*E_2*M_2*(27*M + 10*r0) - 12*a_9*E*L*M*(4*(27 + 8*E_2)*M_2 + 4*(86 + 3*E_2)*M*r0 + 131*r0_2) - 4*a*E*L*M*r0_4*(L_4*(592*M_2 + 200*M*r0 - 255*r0_2) + 3*L_2*r0_2*(-268*M_2 + 3*(89 + 46*E_2)*M*r0 - 75*r0_2) + 3*r0_4*(36*M_2 + 2*(-241 + 108*E_2)*M*r0 + 229*r0_2)) + r0_4*(72*E_2*M*(15*M - 7*r0)*r0_7 - 8*L_6*M*(50*M_2 - 51*M*r0 + 13*r0_2) + L_4*r0_2*(-1684*M_3 + 4*(159 + 445*E_2)*M_2*r0 + (679 - 904*E_2)*M*r0_2 - 288*r0_3) + 3*L_2*r0_4*(404*M_3 + 8*(-158 + 157*E_2)*M_2*r0 + 15*(65 - 42*E_2)*M*r0_2 - 222*r0_3)) + 3*a_8*(L_2*(24*(9 + 16*E_2)*M_3 + 36*(36 + 13*E_2)*M_2*r0 + 38*(14 + E_2)*M*r0_2 + 9*r0_3) + 8*E_2*M*r0*(378*M_3 + 619*M_2*r0 + 244*M*r0_2 + 27*r0_3)) - 4*a_7*E*L*M*(3*L_2*(96*M_2 + 2*(99 + E_2)*M*r0 + 145*r0_2) + 2*r0*(12*(189 + 62*E_2)*M_3 + 3*(469 + 344*E_2)*M_2*r0 + 7*(169 + 42*E_2)*M*r0_2 + 572*r0_3)) - 4*a_3*E*L*M*r0_2*(3*L_4*(816*M_2 - 104*M*r0 - 77*r0_2) + L_2*r0*(-3168*M_3 + 12*(293 + 66*E_2)*M_2*r0 + 2*(842 + 423*E_2)*M*r0_2 - 347*r0_3) + 2*r0_3*(-2934*M_3 + 3*(-85 + 504*E_2)*M_2*r0 + (-1 + 906*E_2)*M*r0_2 + 716*r0_3)) + a_6*(3*L_4*(128*M_3 + 12*(31 + 2*E_2)*M_2*r0 + 2*(271 + 8*E_2)*M*r0_2 + 9*r0_3) - 72*E_2*M*r0_2*(288*M_4 + 252*M_3*r0 - 159*M_2*r0_2 - 115*M*r0_3 - 20*r0_4) + L_2*r0*(144*(63 + 124*E_2)*M_4 + 120*(-30 + 187*E_2)*M_3*r0 + 4*(668 + 2559*E_2)*M_2*r0_2 + 3*(1145 + 12*E_2)*M*r0_3 + 73*r0_4)) - 4*a_5*E*L*M*r0*(6*L_4*(3*M + 4*r0) + L_2*(4464*M_3 + 12*(419 + 68*E_2)*M_2*r0 + 3*(379 + 146*E_2)*M*r0_2 + 421*r0_3) + 2*r0*(-5184*M_4 + 54*(-33 + 20*E_2)*M_3*r0 + 8*(203 + 300*E_2)*M_2*r0_2 + 33*(33 + 26*E_2)*M*r0_3 + 748*r0_4)) - a_2*r0_2*(12*L_6*M*(-272*M_2 + 125*M*r0 + 7*r0_2) + 72*E_2*M*r0_6*(-60*M_2 + 25*M*r0 + 12*r0_2) + L_4*r0*(10656*M_4 - 16*(513 + 371*E_2)*M_3*r0 + 4*(249 - 944*E_2)*M_2*r0_2 + 16*(-15 + 112*E_2)*M*r0_3 + 305*r0_4) + L_2*r0_3*(8280*M_4 - 24*(59 + 437*E_2)*M_3*r0 + 4*(284 - 1641*E_2)*M_2*r0_2 + (-3161 + 3972*E_2)*M*r0_3 + 1013*r0_4)) + a_4*r0*(24*L_6*M*(M + 2*r0) - 72*E_2*M*r0_4*(210*M_3 + 13*M_2*r0 + 3*M*r0_2 - 6*r0_3) + L_4*(5952*M_4 + 24*(247 + 408*E_2)*M_3*r0 + 12*(-278 + 167*E_2)*M_2*r0_2 + 5*(335 - 168*E_2)*M*r0_3 + 10*r0_4) + L_2*r0*(-20736*M_5 + 144*(27 + 46*E_2)*M_4*r0 + 4*(857 + 6264*E_2)*M_3*r0_2 + 148*(-4 + 87*E_2)*M_2*r0_3 - 5*(-535 + 432*E_2)*M*r0_4 - 301*r0_5)))/(3.*r0_2*expr2_3);\n    A93600 = -(r0*(-168*a_8*E_2*M_2 + 12*a_7*E*L*M*(4*(7 + 2*E_2)*M + 73*r0) - 3*a_6*(L_2*(8*(7 + 12*E_2)*M_2 + 4*(73 + 6*E_2)*M*r0 + 7*r0_2) + 8*E_2*M*r0*(52*M_2 + 71*M*r0 + 8*r0_2)) + 4*a*E*L*M*r0_3*(L_2*(-156*M_2 + 290*M*r0 - 111*r0_2) + 3*r0_2*(-72*M_2 + 4*(-31 + 9*E_2)*M*r0 + 79*r0_2)) + 4*a_5*E*L*M*(36*L_2*(2*M + r0) + r0*(8*(78 + 19*E_2)*M_2 + 4*(-16 + 49*E_2)*M*r0 + 451*r0_2)) - a_4*(24*L_4*M*(4*M + 3*r0) - 24*E_2*M*r0_2*(132*M_3 + 56*M_2*r0 - 48*M*r0_2 - 9*r0_3) + L_2*r0*(96*(13 + 19*E_2)*M_3 + 280*(-7 + 5*E_2)*M_2*r0 + (1379 - 214*E_2)*M*r0_2 + 64*r0_3)) + r0_3*(24*E_2*M*r0_5*(-15*M + 7*r0) + 2*L_4*M*(228*M_2 - 236*M*r0 + 61*r0_2) + L_2*r0_2*(12*M_3 + 8*(109 - 63*E_2)*M_2*r0 + (-907 + 262*E_2)*M*r0_2 + 234*r0_3)) + 4*a_3*E*L*M*r0*(L_2*(456*M_2 + 112*M*r0 - 75*r0_2) + r0*(-1584*M_3 + 8*(34 + 9*E_2)*M_2*r0 + 8*(-12 + 35*E_2)*M*r0_2 + 445*r0_3)) + a_2*r0*(72*E_2*M*r0_5*(3*M + 2*r0) + 2*L_4*M*(-304*M_2 + 84*M*r0 + 43*r0_2) + L_2*r0*(3168*M_4 - 8*(304 + 15*E_2)*M_3*r0 - 4*(-389 + 452*E_2)*M_2*r0_2 + 2*(-449 + 274*E_2)*M*r0_3 + 191*r0_4))))/(12.*expr2_3);\n    A94020 = (48*L_2*r0_3*(a_2*(2*M + r0) + r0*(L_2 + r0_2)))/expr2_2;\n    A94040 = (-8*(-72*a_10*E_2*M_2*r0_p_2M_2 + 24*a_9*E*L*M*(24*M_3 + 42*M_2*r0 + 29*M*r0_2 + 7*r0_3) - 4*a*E*L*M*r0_4*(L_6*(4*M - 6*r0) + 21*L_4*(15*M - 4*r0)*r0_2 + 3*L_2*(181*M - 46*r0)*r0_4 + 36*(5*M - r0)*r0_6) + r0_4*(4*L_8*M*(2*M - r0) + 144*E_2*M*r0_9 + L_6*r0_2*(71*M_2 - 4*(13 + 2*E_2)*M*r0 + 12*r0_2) + 3*L_2*r0_6*(57*M_2 + 4*(-13 + 29*E_2)*M*r0 + 13*r0_2) + 3*L_4*r0_4*(86*M_2 + 4*(-19 + 16*E_2)*M*r0 + 19*r0_2)) + 4*a_3*E*L*M*r0_3*(6*L_6 - 3*L_4*(160*M_2 + 97*M*r0 - 31*r0_2) + 6*r0_4*(-78*M_2 - 71*M*r0 + 23*r0_2) + L_2*r0_2*(-1494*M_2 - 820*M*r0 + 293*r0_2)) + 4*a_5*E*L*M*r0_2*(L_4*(42*M + 51*r0) - L_2*(1536*M_3 + 1634*M_2*r0 + 215*M*r0_2 - 242*r0_3) - 6*r0_2*(132*M_3 + 182*M_2*r0 + 31*M*r0_2 - 35*r0_3)) + a_2*r0_3*(-12*L_8*M + 72*E_2*M*r0_7*(7*M + 8*r0) + 2*L_6*(480*M_3 + 2*(81 + 2*E_2)*M_2*r0 - 5*(19 + 2*E_2)*M*r0_2 - 36*r0_3) + 3*L_4*r0_2*(408*M_3 + 4*(89 + 81*E_2)*M_2*r0 + 2*(-87 + 56*E_2)*M*r0_2 - 27*r0_3) + 6*L_2*r0_4*(32*M_3 + 18*(7 + 18*E_2)*M_2*r0 + 21*(-3 + 8*E_2)*M*r0_2 + 2*r0_3)) + a_4*r0_2*(864*E_2*M*r0_5*(M + r0)*(M + r0) - 3*L_6*(28*M_2 + 4*(13 + E_2)*M*r0 - r0_2) + 2*L_4*(1536*M_4 + 4*(313 + 120*E_2)*M_3*r0 + (13 + 420*E_2)*M_2*r0_2 + 3*(-161 + 16*E_2)*M*r0_3 - 66*r0_4) + 3*L_2*r0_2*(164*M_4 + 4*(191 + 336*E_2)*M_3*r0 + 3*(49 + 424*E_2)*M_2*r0_2 + 6*(-47 + 52*E_2)*M*r0_3 - 30*r0_4)) + 4*a_7*E*L*M*r0*(L_2*(96*M_2 + 202*M*r0 + 87*r0_2) - 6*(192*M_4 + 212*M_3*r0 + 70*M_2*r0_2 - 39*M*r0_3 - 25*r0_4)) + a_8*(-(L_2*(288*M_4 + 48*(15 + 4*E_2)*M_3*r0 + 2*(281 + 84*E_2)*M_2*r0_2 + 18*(7 + 2*E_2)*M*r0_3 - 3*r0_4)) + 144*E_2*M*r0*(16*M_4 + 20*M_3*r0 + 10*M_2*r0_2 + 4*M*r0_3 + r0_4)) + 2*a_6*r0*(-(L_4*(96*M_3 + 2*(160 + 21*E_2)*M_2*r0 + 3*(45 + 8*E_2)*M*r0_2 - 3*r0_3)) + 72*E_2*M*r0_3*(14*M_3 + 18*M_2*r0 + 13*M*r0_2 + 4*r0_3) + L_2*(1152*M_5 + 48*(23 + 32*E_2)*M_4*r0 + 14*(29 + 144*E_2)*M_3*r0_2 + (-569 + 852*E_2)*M_2*r0_3 + 15*(-25 + 8*E_2)*M*r0_4 - 30*r0_5))))/(3.*r0_4*expr2_3);\n    A94200 = (12*L_2*r0_6)/expr2_2;\n    A94220 = (-4*(-24*a_8*E_2*M_2*(2*M + r0) + 4*a_7*E*L*M*(24*M_2 + 27*M*r0 + 10*r0_2) + 2*a_5*E*L*M*r0*(-384*M_3 - 220*M_2*r0 + 16*M*r0_2 + 58*r0_3 + L_2*(16*M + 19*r0)) + 2*a*E*L*M*r0_4*(4*L_4 + 6*r0_3*(-14*M + r0) + L_2*r0*(-141*M + 28*r0)) + r0_5*(48*E_2*M*r0_5 - L_4*(9*M_2 + (9 + 4*E_2)*M*r0 - 8*r0_2) + L_2*r0_2*(9*M_2 + 2*(-13 + 28*E_2)*M*r0 + 12*r0_2)) + 2*a_3*E*L*M*r0_2*(4*L_4 + 2*r0_2*(-90*M_2 - 73*M*r0 + 22*r0_2) + L_2*(-248*M_2 - 127*M*r0 + 37*r0_2)) + a_6*M*(-(L_2*(48*M_2 + 4*(21 + 4*E_2)*M*r0 + 3*(11 + 2*E_2)*r0_2)) + 24*E_2*r0*(16*M_3 + 12*M_2*r0 + 5*M*r0_2 + 2*r0_3)) + a_2*r0_2*(-4*L_6*M + 72*E_2*M*r0_5*(M + 2*r0) + L_4*(248*M_3 + 54*M_2*r0 - (25 + 8*E_2)*M*r0_2 - 11*r0_3) + L_2*r0_2*(32*M_3 + 4*(29 + 50*E_2)*M_2*r0 + (-37 + 106*E_2)*M*r0_2 + 6*r0_3)) + a_4*r0*(-4*L_4*M*(4*M + (8 + E_2)*r0) + 72*E_2*M*r0_3*(2*M_2 + 3*M*r0 + 2*r0_2) + L_2*(384*M_4 + 8*(19 + 31*E_2)*M_3*r0 + (-13 + 200*E_2)*M_2*r0_2 + 4*(-20 + 11*E_2)*M*r0_3 - 6*r0_4))))/(r0*expr2_3);\n    A94400 = (r0_2*(72*a_6*E_2*M_2 - 72*a_5*E*L*M*(2*M + r0) + 48*a_3*E*L*M*r0*(24*M_2 + M*r0 - 4*r0_2) + 24*a*E*L*M*r0_2*(-2*L_2*M + 3*r0_2*(4*M + r0)) + r0_2*(12*L_4*M*(2*M - r0) - 144*E_2*M*r0_5 + L_2*r0_2*(117*M_2 + 2*(5 + 6*E_2)*M*r0 - 38*r0_2)) + 9*a_4*(-16*E_2*M*r0*(4*M_2 + M*r0 + r0_2) + L_2*(8*M_2 + 8*M*r0 + r0_2)) - 2*a_2*r0*(-36*E_2*M*(M - 4*r0)*r0_3 + L_2*(288*M_3 - 12*(4 + E_2)*M_2*r0 - 6*(-1 + E_2)*M*r0_2 + 19*r0_3))))/(6.*expr2_3);\n    A95020 = (8*L*r0*(12*a_5*E*M*(2*M + r0) - a_4*L*(24*M_2 + 15*M*r0 + 2*r0_2) + 6*a*E*M*r0_3*(5*L_2 + 6*r0_2) + 2*a_3*E*M*r0*(5*L_2 + 12*r0*(3*M + 2*r0)) + L*r0_3*(-2*L_2*(9*M - 5*r0) + r0_2*(-20*M + 11*r0)) - a_2*L*r0*(2*L_2*(5*M + r0) + r0*(46*M_2 + 9*M*r0 - 9*r0_2))))/expr2_3;\n    A95040 = (-4*(24*a_12*E_2*M_2*(36*M_2 + 44*M*r0 + 13*r0_2) - 4*a_11*E*L*M*(48*(9 + E_2)*M_3 + 8*(65 + 12*E_2)*M_2*r0 + 18*(17 + 2*E_2)*M*r0_2 + 81*r0_3) + 2*a*E*L*M*r0_5*(L_4*r0_2*(457*M_2 + 4*(467 + 138*E_2)*M*r0 - 1122*r0_2) + 8*L_6*(126*M_2 + 11*M*r0 - 39*r0_2) + 3*L_2*r0_4*(501*M_2 + 4*(-79 + 114*E_2)*M*r0 + 16*r0_2) + 18*r0_6*(59*M_2 + 6*(-15 + 8*E_2)*M*r0 + 31*r0_2)) + 2*a_3*E*L*M*r0_3*(L_6*(3144*M_2 - 424*M*r0 - 294*r0_2) + L_4*r0*(1424*M_3 + (5491 + 1032*E_2)*M_2*r0 + 2*(1373 + 562*E_2)*M*r0_2 - 1884*r0_3) + 2*L_2*r0_3*(651*M_3 + 2*(755 + 1323*E_2)*M_2*r0 + (1537 + 1836*E_2)*M*r0_2 - 885*r0_3) + 6*r0_5*(-528*M_3 + (35 + 1368*E_2)*M_2*r0 + 2*(-19 + 238*E_2)*M*r0_2 + 105*r0_3)) + r0_5*(72*E_2*M*r0_9*(-7*M + 3*r0) + 2*L_8*M*(12*M_2 - 20*M*r0 + 7*r0_2) + 2*L_6*r0_2*(301*M_3 - (353 + 540*E_2)*M_2*r0 + 2*(60 + 139*E_2)*M*r0_2 - 12*r0_3) + 3*L_2*r0_6*(-476*M_3 + (837 - 940*E_2)*M_2*r0 + (-487 + 462*E_2)*M*r0_2 + 92*r0_3) + L_4*r0_4*(-946*M_3 - 7*(-291 + 476*E_2)*M_2*r0 + (-1355 + 1688*E_2)*M*r0_2 + 276*r0_3)) - 2*a_9*E*L*M*(3*L_2*(96*M_3 + 4*(-7 + 5*E_2)*M_2*r0 + 2*(53 + 4*E_2)*M*r0_2 + 111*r0_3) - 2*r0*(48*(137 + 46*E_2)*M_4 + 4*(1949 + 864*E_2)*M_3*r0 + 2*(1201 + 636*E_2)*M_2*r0_2 + 6*(-95 + 4*E_2)*M*r0_3 - 429*r0_4)) + a_10*M*(L_2*(288*(3 + 2*E_2)*M_3 + 4*(256 + 123*E_2)*M_2*r0 + 6*(157 + 22*E_2)*M*r0_2 + 3*(101 + 10*E_2)*r0_3) - 24*E_2*r0*(548*M_4 + 784*M_3*r0 + 309*M_2*r0_2 + 53*M*r0_3 + 15*r0_4)) + 2*a_5*E*L*M*r0_2*(18*L_6*(2*M + r0) + L_4*(12384*M_3 + 4*(1623 + 262*E_2)*M_2*r0 + 2*(655 + 292*E_2)*M*r0_2 - 1221*r0_3) + L_2*r0*(-9600*M_4 + 10*(1645 + 576*E_2)*M_3*r0 + 3*(6063 + 3076*E_2)*M_2*r0_2 + 4*(1087 + 804*E_2)*M*r0_3 - 3351*r0_4) - 6*r0_3*(4260*M_4 - 180*(-1 + 16*E_2)*M_3*r0 - (1489 + 3096*E_2)*M_2*r0_2 - 12*(25 + 46*E_2)*M*r0_3 + 158*r0_4)) + a_2*r0_3*(-72*E_2*M*r0_8*(71*M_2 - 29*M*r0 - 7*r0_2) + 4*L_8*M*(-524*M_2 + 252*M*r0 + 11*r0_2) + 2*L_6*r0*(2168*M_4 - (1571 + 2052*E_2)*M_3*r0 - (783 + 1192*E_2)*M_2*r0_2 + (407 + 559*E_2)*M*r0_3 + 96*r0_4) + 3*L_2*r0_5*(-78*M_4 + (1137 - 7196*E_2)*M_3*r0 + 4*(-55 + 117*E_2)*M_2*r0_2 + 2*(-268 + 633*E_2)*M*r0_3 + 189*r0_4) + L_4*r0_3*(5458*M_4 + (913 - 12344*E_2)*M_3*r0 - (3685 + 6772*E_2)*M_2*r0_2 + (175 + 3992*E_2)*M*r0_3 + 465*r0_4)) + a_8*(3*L_4*M*(64*M_3 + 4*(-23 + 30*E_2)*M_2*r0 + 12*(18 - 7*E_2)*M*r0_2 + (205 + 4*E_2)*r0_3) + 24*E_2*M*r0_2*(960*M_5 + 936*M_4*r0 - 788*M_3*r0_2 - 688*M_2*r0_3 - 139*M*r0_4 - 51*r0_5) - L_2*r0*(96*(137 + 276*E_2)*M_5 + 16*(773 + 2838*E_2)*M_4*r0 + 648*(5 + 38*E_2)*M_3*r0_2 + 2*(-1477 + 2622*E_2)*M_2*r0_3 + 6*(-287 + 45*E_2)*M*r0_4 + 9*r0_5)) - a_4*r0_2*(6*L_8*M*(4*M + 3*r0) - 72*E_2*M*r0_6*(24*M_3 - 52*M_2*r0 + 81*M*r0_2 - 2*r0_3) + 4*L_6*(2064*M_4 + 6*(113 + 262*E_2)*M_3*r0 + 2*(27 + 166*E_2)*M_2*r0_2 - 2*(137 + 68*E_2)*M*r0_3 + 9*r0_4) + L_4*r0*(-18240*M_5 + 4*(1745 + 4676*E_2)*M_4*r0 + (10747 + 26296*E_2)*M_3*r0_2 + 2*(2165 + 3444*E_2)*M_2*r0_3 - 4*(905 + 733*E_2)*M*r0_4 - 144*r0_5) - 3*L_2*r0_3*(6452*M_5 - 8*(89 + 1804*E_2)*M_4*r0 - 6*(227 + 2024*E_2)*M_3*r0_2 + (-1305 + 992*E_2)*M_2*r0_3 + (425 + 1036*E_2)*M*r0_4 + 99*r0_5)) + 2*a_7*E*L*M*r0*(-3*L_4*(60*M_2 - 4*(27 + E_2)*M*r0 + 51*r0_2) + 2*L_2*(6624*M_4 + 48*(257 + 43*E_2)*M_3*r0 + 70*(101 + 27*E_2)*M_2*r0_2 + 2*(335 + 222*E_2)*M*r0_3 - 933*r0_4) - 6*r0*(3840*M_5 - 4*(-671 + 360*E_2)*M_4*r0 - 24*(129 + 158*E_2)*M_3*r0_2 - 3*(719 + 728*E_2)*M_2*r0_3 + 20*(1 - 12*E_2)*M*r0_4 + 286*r0_5)) - a_6*r0*(-6*L_6*M*(20*M_2 - 2*(29 + 6*E_2)*M*r0 + (49 - 3*E_2)*r0_2) - 48*E_2*M*r0_4*(654*M_4 + 156*M_3*r0 - 139*M_2*r0_2 + 31*M*r0_3 - 27*r0_4) + L_4*(8832*M_5 + 96*(185 + 258*E_2)*M_4*r0 + 8*(1093 + 2229*E_2)*M_3*r0_2 + 20*(-24 + 209*E_2)*M_2*r0_3 - (2789 + 640*E_2)*M*r0_4 + 45*r0_5) + L_2*r0*(-23040*M_6 + 48*(-203 + 340*E_2)*M_5*r0 + 2*(8033 + 35712*E_2)*M_4*r0_2 + (11021 + 45528*E_2)*M_3*r0_3 + 4*(-65 + 1656*E_2)*M_2*r0_4 - 3*(947 + 132*E_2)*M*r0_5 + 3*r0_6))))/(3.*r0_5*expr2_4);\n    A95200 = (2*L*r0_4*(12*a_3*E*M + 36*a*E*M*r0_2 - 3*a_2*L*(4*M + r0) + L*r0_2*(-20*M + 11*r0)))/expr2_3;\n    A95220 = (-192*a_10*E_2*M_2*(3*M + 2*r0) + 48*a_9*E*L*M*(2*(12 + E_2)*M_2 + (-7 + 3*E_2)*M*r0 - 4*r0_2) + 6*a_7*E*L*M*(L_2*(48*M_2 + 4*(-36 + E_2)*M*r0 - 55*r0_2) - 4*r0*(16*(61 + 17*E_2)*M_3 + (569 + 284*E_2)*M_2*r0 + 8*(15 + 4*E_2)*M*r0_2 + r0_3)) + 3*a_8*(L_2*(-96*(2 + E_2)*M_3 + 24*(10 + 3*E_2)*M_2*r0 + (107 - 2*E_2)*M*r0_2 + 9*r0_3) + 8*E_2*M*r0*(488*M_3 + 454*M_2*r0 + 103*M*r0_2 + 27*r0_3)) - 6*a_3*E*L*M*r0_2*(2*L_4*(828*M_2 - 140*M*r0 - 77*r0_2) + L_2*r0*(-1424*M_3 + (2651 + 552*E_2)*M_2*r0 + 2*(483 + 262*E_2)*M*r0_2 - 632*r0_3) + 4*r0_3*(-972*M_3 + (215 + 612*E_2)*M_2*r0 + 8*(9 + 28*E_2)*M*r0_2 + 83*r0_3)) - r0_4*(144*E_2*M*r0_7*(-7*M + 3*r0) + 4*L_6*M*(38*M_2 - 45*M*r0 + 13*r0_2) + 6*L_2*r0_4*(-252*M_3 + (579 - 572*E_2)*M_2*r0 + 2*(-201 + 143*E_2)*M*r0_2 + 86*r0_3) + L_4*r0_2*(590*M_3 + (135 - 1676*E_2)*M_2*r0 + 4*(-161 + 218*E_2)*M*r0_2 + 204*r0_3)) + a_2*r0_2*(72*E_2*M*r0_6*(84*M_2 - 43*M*r0 - 9*r0_2) + 24*L_6*M*(138*M_2 - 67*M*r0 - 3*r0_2) + 3*L_2*r0_3*(-2642*M_4 + (543 + 5692*E_2)*M_3*r0 + (119 + 232*E_2)*M_2*r0_2 - 22*(-27 + 53*E_2)*M*r0_3 - 248*r0_4) + L_4*r0*(-9456*M_4 + 2*(3105 + 3236*E_2)*M_3*r0 + (847 + 3268*E_2)*M_2*r0_2 - 4*(154 + 437*E_2)*M*r0_3 - 228*r0_4)) - 6*a_5*E*L*M*r0*(L_4*(-12*M + 8*r0) + L_2*(3264*M_3 + 4*(793 + 138*E_2)*M_2*r0 + 2*(397 + 128*E_2)*M*r0_2 - 229*r0_3) + 4*r0*(-1776*M_4 + 12*(-25 + 36*E_2)*M_3*r0 + 11*(81 + 76*E_2)*M_2*r0_2 + 2*(109 + 86*E_2)*M*r0_3 - 11*r0_4)) - 3*a_6*(L_4*(32*M_3 + 24*(-7 + E_2)*M_2*r0 - 8*(14 + E_2)*M*r0_2 - 9*r0_3) + 8*E_2*M*r0_2*(888*M_4 + 396*M_3*r0 - 508*M_2*r0_2 - 113*M*r0_3 - 63*r0_4) - L_2*r0*(64*(61 + 102*E_2)*M_4 + 20*(46 + 329*E_2)*M_3*r0 + 8*(10 + 241*E_2)*M_2*r0_2 - (121 + 26*E_2)*M*r0_3 - 5*r0_4)) + 3*a_4*r0*(-8*L_6*M*(M - r0) - 24*E_2*M*r0_4*(180*M_3 - 46*M_2*r0 + 59*M*r0_2 - 9*r0_3) + L_4*(2176*M_4 + 4*(509 + 828*E_2)*M_3*r0 + 4*(-21 + 122*E_2)*M_2*r0_2 - 4*(59 + 71*E_2)*M*r0_3 - 29*r0_4) + L_2*r0*(-7104*M_5 + 16*(48 + 235*E_2)*M_4*r0 + (2593 + 9920*E_2)*M_3*r0_2 + (747 + 1952*E_2)*M_2*r0_3 - 2*(100 + 309*E_2)*M*r0_4 - 90*r0_5)) - 2*a*E*L*M*r0_4*(2*L_4*(752*M_2 + 76*M*r0 - 243*r0_2) + 36*r0_4*(15*M_2 + 2*(-32 + 15*E_2)*M*r0 + 29*r0_2) + 3*L_2*r0_2*(133*M_2 - 102*r0_2 + 88*M*(r0 + 3*E_2*r0))))/(3.*r0_2*expr2_4);\n    A95400 = (r0*(-72*a_8*E_2*M_2 + 36*a_7*E*L*M*(4*M - 15*r0) - 4*a_5*E*L*M*r0*(18*L_2 + 12*(107 + 22*E_2)*M_2 + 6*(-7 + 22*E_2)*M*r0 + 233*r0_2) + 3*a_6*(3*L_2*(-8*M_2 + 4*(15 + E_2)*M*r0 + r0_2) + 8*E_2*M*r0*(107*M_2 + 46*M*r0 + 12*r0_2)) - 12*a*E*L*M*r0_3*(L_2*(8*M_2 + 70*M*r0 - 41*r0_2) + r0_2*(-87*M_2 + 4*(-31 + 9*E_2)*M*r0 + 86*r0_2)) + a_4*r0*(36*L_4*M - 72*E_2*M*r0*(68*M_3 - 6*M_2*r0 - 2*M*r0_2 - 5*r0_3) + L_2*(24*(107 + 132*E_2)*M_3 + 24*(-53 + 25*E_2)*M_2*r0 + 2*(388 - 171*E_2)*M*r0_2 + 23*r0_3)) - 3*r0_3*(24*E_2*M*r0_5*(-7*M + 3*r0) + 2*L_4*M*(64*M_2 - 70*M*r0 + 19*r0_2) + L_2*r0_2*(-28*M_3 + 3*(117 - 68*E_2)*M_2*r0 + 22*(-16 + 5*E_2)*M*r0_2 + 90*r0_3)) - 4*a_3*E*L*M*r0*(3*L_2*(264*M_2 - 32*M*r0 - 35*r0_2) + r0*(-2448*M_3 + 3*(287 + 72*E_2)*M_2*r0 + 2*(61 + 120*E_2)*M*r0_2 + 266*r0_3)) - a_2*r0*(72*E_2*M*r0_4*(-13*M_2 + 14*M*r0 + 2*r0_2) + 6*L_4*M*(-176*M_2 + 76*M*r0 + 13*r0_2) + L_2*r0*(4896*M_4 - 12*(251 + 112*E_2)*M_3*r0 + (991 - 1380*E_2)*M_2*r0_2 + 4*(-181 + 177*E_2)*M*r0_3 + 238*r0_4))))/(12.*expr2_4);\n    A96000 = (4*L_2*r0_6)/expr2_3;\n    A96020 = (2*(72*a_8*E_2*M_2*(2*M + r0) - 96*a_7*E*L*M*(3*M_2 + 5*M*r0 + 2*r0_2) + 8*a*E*L*M*r0_3*(L_4*(4*M - 6*r0) + 8*L_2*(10*M - 3*r0)*r0_2 + 3*r0_4*(7*M + 3*r0)) - 24*a_5*E*L*M*r0*(-64*M_3 - 34*M_2*r0 + 13*M*r0_2 + 19*r0_3 + L_2*(7*M + 9*r0)) + r0_3*(-96*E_2*M*r0_7 + 8*L_6*M*(-2*M + r0) + L_4*r0_2*(3*M_2 + 4*(-9 + 4*E_2)*M*r0 + 12*r0_2) + L_2*r0_4*(43*M_2 - 20*(4 + 5*E_2)*M*r0 + 24*r0_2)) - 8*a_3*E*L*M*r0_2*(6*L_4 + 6*r0_2*(-5*M_2 - 13*M*r0 + 4*r0_2) + L_2*(-136*M_2 - 77*M*r0 + 45*r0_2)) + 3*a_6*(L_2*(48*M_3 + 4*(34 + 7*E_2)*M_2*r0 + 2*(29 + 6*E_2)*M*r0_2 + r0_3) - 8*E_2*M*r0*(32*M_3 + 12*M_2*r0 + 3*M*r0_2 + 4*r0_3)) + a_2*r0_2*(24*L_6*M + 72*E_2*M*(M - 4*r0)*r0_5 - 2*L_4*(272*M_3 + 8*(18 + E_2)*M_2*r0 - (81 + 20*E_2)*M*r0_2 - 36*r0_3) + L_2*r0_2*(158*M_3 - (657 + 364*E_2)*M_2*r0 + 2*(49 - 82*E_2)*M*r0_2 + 84*r0_3)) + a_4*r0*(72*E_2*M*r0_3*(2*M_2 - M*r0 - 4*r0_2) + 3*L_4*(28*M_2 + 4*(15 + 2*E_2)*M*r0 + r0_2) - L_2*(768*M_4 + 16*(33 + 34*E_2)*M_3*r0 + 8*(-4 + 41*E_2)*M_2*r0_2 + 2*(-209 + 14*E_2)*M*r0_3 - 63*r0_4))))/(3.*r0*expr2_4);\n    A96200 = (r0_2*(72*a_6*E_2*M_2 - 24*a_5*E*L*M*(6*M + 7*r0) - 24*a_3*E*L*M*r0*(2*L_2 - 32*M_2 - M*r0 + 11*r0_2) + 8*a*E*L*M*r0_2*(2*L_2*(M - 3*r0) + 3*r0_2*(M + 6*r0)) + 3*a_4*(-16*E_2*M*r0*(8*M_2 - M*r0 + 2*r0_2) + L_2*(24*M_2 + 8*(7 + E_2)*M*r0 + 5*r0_2)) + r0_2*(-96*E_2*M*r0_5 + 4*L_4*M*(-2*M + r0) + L_2*r0_2*(139*M_2 + 4*(-32 + 5*E_2)*M*r0 + 24*r0_2)) + 2*a_2*r0*(12*L_4*M + 12*E_2*M*(11*M - 8*r0)*r0_3 + L_2*(-192*M_3 - 4*(9 + E_2)*M_2*r0 + (29 + 22*E_2)*M*r0_2 + 6*r0_3))))/(6.*expr2_4);\n    A97000 = (2*L*r0_4*(4*a_3*E*M + 12*a*E*M*r0_2 - a_2*L*(4*M + r0) + L*r0_2*(-7*M + 4*r0)))/expr2_4;\n    A97020 = (-24*a_10*E_2*M_2*(25*M + 14*r0) + 12*a_9*E*L*M*(4*(25 + 4*E_2)*M_2 + 12*(8 + E_2)*M*r0 + 33*r0_2) - 4*a*E*L*M*r0_4*(L_4*(300*M_2 - 8*M*r0 - 81*r0_2) + 3*r0_4*(45*M_2 + 8*(-11 + 6*E_2)*M*r0 + 38*r0_2) + L_2*r0_2*(538*M_2 + 2*(-205 + 63*E_2)*M*r0 + 69*r0_2)) + r0_4*(24*E_2*M*(13*M - 5*r0)*r0_7 + 4*L_6*M*(6*M_2 - M*r0 - r0_2) + L_2*r0_4*(611*M_3 + 2*(-517 + 512*E_2)*M_2*r0 + 2*(307 - 253*E_2)*M*r0_2 - 120*r0_3) + L_4*r0_2*(335*M_3 + 24*(-27 + 22*E_2)*M_2*r0 + 2*(221 - 142*E_2)*M*r0_2 - 96*r0_3)) - 4*a_3*E*L*M*r0_2*(L_4*(840*M_2 - 172*M*r0 - 81*r0_2) + 2*L_2*r0*(-176*M_3 + 2*(395 + 72*E_2)*M_2*r0 + (131 + 121*E_2)*M*r0_2 - 246*r0_3) + 2*r0_3*(-1029*M_3 + 2*(299 + 360*E_2)*M_2*r0 + 2*(68 + 73*E_2)*M*r0_2 - 75*r0_3)) - 3*a_8*(L_2*(8*(25 + 24*E_2)*M_3 + 4*(68 + 27*E_2)*M_2*r0 + 2*(53 + 7*E_2)*M*r0_2 - 3*r0_3) - 8*E_2*M*r0*(198*M_3 + 97*M_2*r0 - 12*M*r0_2 + 9*r0_3)) + 8*a_7*E*L*M*(3*L_2*(24*M_2 + (9 + E_2)*M*r0 + 15*r0_2) + r0*(-4*(297 + 74*E_2)*M_3 - 2*(331 + 112*E_2)*M_2*r0 + (149 + 34*E_2)*M*r0_2 + 183*r0_3)) - 4*a_5*E*L*M*r0*(-18*L_4*M + L_2*(1776*M_3 + 2*(751 + 140*E_2)*M_2*r0 + 2*(201 + 55*E_2)*M*r0_2 - 363*r0_3) + r0*(-3648*M_4 + 2*(-19 + 504*E_2)*M_3*r0 + (2517 + 1744*E_2)*M_2*r0_2 + 2*(53 + 58*E_2)*M*r0_3 - 531*r0_4)) + a_2*r0_2*(4*L_6*M*(280*M_2 - 141*M*r0 - 7*r0_2) + 24*E_2*M*r0_6*(102*M_2 - 55*M*r0 - 6*r0_2) + L_2*r0_3*(-2774*M_4 + (1301 + 8284*E_2)*M_3*r0 + (209 - 2152*E_2)*M_2*r0_2 + 8*(11 - 122*E_2)*M*r0_3 - 96*r0_4) + L_4*r0*(-2720*M_4 + 24*(67 + 97*E_2)*M_3*r0 + 5*(111 + 188*E_2)*M_2*r0_2 - 10*(45 + 58*E_2)*M*r0_3 + 12*r0_4)) + a_4*r0*(-24*L_6*M_2 + 24*E_2*M*r0_4*(-162*M_3 + 107*M_2*r0 - 105*M*r0_2 + 12*r0_3) + L_4*(2368*M_4 + 4*(527 + 840*E_2)*M_3*r0 + 4*(230 + 79*E_2)*M_2*r0_2 - (1009 + 296*E_2)*M*r0_3 - 72*r0_4) + L_2*r0*(-7296*M_5 + 8*(41 + 668*E_2)*M_4*r0 + 12*(333 + 974*E_2)*M_3*r0_2 + (1451 - 660*E_2)*M_2*r0_3 - (1171 + 476*E_2)*M*r0_4 - 30*r0_5)) - a_6*(24*E_2*M*r0_2*(304*M_4 + 20*M_3*r0 - 183*M_2*r0_2 + 35*M*r0_3 - 22*r0_4) + L_2*r0*(-48*(99 + 148*E_2)*M_4 - 4*(742 + 1423*E_2)*M_3*r0 + 26*(25 - 16*E_2)*M_2*r0_2 + 3*(459 + 16*E_2)*M*r0_3 + 45*r0_4) + 3*L_4*(64*M_3 + 106*M*r0_2 - 3*r0_3 + 12*M_2*(r0 + 2*E_2*r0))))/(3.*r0_2*expr2_5);\n    A97200 = -(r0*(264*a_8*E_2*M_2 - 12*a_7*E*L*M*((44 + 8*E_2)*M + 13*r0) + 3*a_6*(L_2*((88 + 96*E_2)*M_2 + 4*(13 + 2*E_2)*M*r0 + 3*r0_2) - 8*E_2*M*r0*(90*M_2 - 9*M*r0 + 8*r0_2)) + 4*a*E*L*M*r0_3*(L_2*(196*M_2 - 26*M*r0 - 45*r0_2) + 3*r0_2*(-47*M_2 + 2*(-17 + 6*E_2)*M*r0 + 34*r0_2)) + 4*a_3*E*L*M*r0*(L_2*(600*M_2 - 248*M*r0 - 57*r0_2) + r0*(-1680*M_3 + (881 + 216*E_2)*M_2*r0 + 10*(23 + 4*E_2)*M*r0_2 - 72*r0_3)) + a_4*(24*L_4*M*(4*M + r0) + 24*E_2*M*r0_2*(140*M_3 - 76*M_2*r0 + 36*M*r0_2 - 11*r0_3) + L_2*r0*(-240*(9 + 10*E_2)*M_3 + 8*(-25 + 77*E_2)*M_2*r0 + (281 + 226*E_2)*M*r0_2 + 30*r0_3)) + r0_3*(24*E_2*M*r0_5*(-13*M + 5*r0) + 2*L_4*M*(20*M_2 - 32*M*r0 + 11*r0_2) + L_2*r0_2*(-131*M_3 + (506 - 304*E_2)*M_2*r0 + 10*(-47 + 17*E_2)*M*r0_2 + 120*r0_3)) + a_2*r0*(2*L_4*M*(-400*M_2 + 228*M*r0 + r0_2) + 24*E_2*M*r0_4*(-46*M_2 + 41*M*r0 + 2*r0_2) + L_2*r0*(3360*M_4 - 4*(425 + 422*E_2)*M_3*r0 + (-77 + 8*E_2)*M_2*r0_2 + 12*(-7 + 31*E_2)*M*r0_3 + 96*r0_4)) + 4*a_5*E*L*M*(-12*L_2*(6*M + r0) + r0*(40*(27 + 5*E_2)*M_2 - 123*r0_2 - 4*M*(r0 + 5*E_2*r0)))))/(12.*expr2_5);\n    A98000 = (r0_2*(12*a_6*E_2*M_2 - 12*a_5*E*L*M*(2*M + 3*r0) + 4*a*E*L*M*(2*M - 3*r0)*r0_2*(L_2 - 3*r0_2) - 12*a_3*E*L*M*r0*(L_2 - 8*M_2 + 4*r0_2) + 3*a_4*(-4*E_2*M*r0*(4*M_2 - 2*M*r0 + r0_2) + L_2*(4*M_2 + 2*(6 + E_2)*M*r0 + r0_2)) + r0_2*(-12*E_2*M*r0_5 + 2*L_4*M*(-2*M + r0) + L_2*r0_2*(29*M_2 + 4*(-10 + E_2)*M*r0 + 12*r0_2)) + 2*a_2*r0*(3*L_4*M + 6*E_2*M*(5*M - 2*r0)*r0_3 + L_2*(-24*M_3 - 2*(6 + E_2)*M_2*r0 + (7 + 5*E_2)*M*r0_2 + 3*r0_3))))/(3.*expr2_5);\n    A99000 = -(r0*(60*a_8*E_2*M_2 - 12*a_7*E*L*M*(2*(5 + E_2)*M + 7*r0) + 4*a*E*L*M_2*r0_3*(L_2*(48*M - 26*r0) + 3*r0_2*(-6*M + 5*r0)) + 3*a_6*(L_2*(4*(5 + 6*E_2)*M_2 + (28 + 3*E_2)*M*r0 + r0_2) - 4*E_2*M*r0*(27*M_2 - 12*M*r0 + 2*r0_2)) - M*r0_3*(12*E_2*(3*M - r0)*r0_5 + L_4*(24*M_2 - 22*M*r0 + 5*r0_2) + 2*L_2*r0_2*(9*M_2 + (-5 + 12*E_2)*M*r0 + (1 - 7*E_2)*r0_2)) + 2*a_3*E*L*M*r0*(L_2*(168*M_2 - 110*M*r0 - 9*r0_2) + 2*r0*(-216*M_3 + 4*(37 + 9*E_2)*M_2*r0 + 2*(23 - 5*E_2)*M*r0_2 - 39*r0_3)) + a_4*(3*L_4*M*(8*M + 3*r0) + 12*E_2*M*r0_2*(36*M_3 - 34*M_2*r0 + 18*M*r0_2 - 3*r0_3) + L_2*r0*(-12*(27 + 28*E_2)*M_3 + 2*(-74 + 103*E_2)*M_2*r0 + (127 + 26*E_2)*M*r0_2 + 9*r0_3)) + a_2*M*r0*(36*E_2*M*r0_4*(-5*M + 4*r0) - 2*L_4*(56*M_2 - 39*M*r0 + 4*r0_2) + L_2*r0*(432*M_3 - 8*(23 + 39*E_2)*M_2*r0 + 2*(-45 + 61*E_2)*M*r0_2 + (41 + 31*E_2)*r0_3)) + 2*a_5*E*L*M*(-9*L_2*(4*M + r0) + 2*r0*(2*(81 + 14*E_2)*M_2 - 48*r0_2 + M*(r0 - 16*E_2*r0)))))/(6.*expr2_6);\n  }\n\n  /* rho */\n  alpha20 = r0*r0 / (a*a + r0*(r0-2.0*M));\n  alpha02 = r0*r0;\n  beta    = 4.0*(L*L + r0*r0 + a*a*(r0+2.0*M)/r0);\n}\n"
  },
  {
    "path": "external/Licenses/BlazeCopyright.txt",
    "content": "Copyright (C) 2012-2020 Klaus Iglberger - All Rights Reserved\nhttps://bitbucket.org/blaze-lib/blaze/src/\n"
  },
  {
    "path": "external/Licenses/BlazeLicense.txt",
    "content": "                  LICENSE OF THE BLAZE LIBRARY\n                        11 November 2013\n\n\nThe Blaze library and headers are licensed under the New (Revised) BSD\nlicense. Redistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the {organization} nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "external/Licenses/BoostCopyright.txt",
    "content": "https://www.boost.org/\n"
  },
  {
    "path": "external/Licenses/BoostLicense.txt",
    "content": "Boost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "external/Licenses/BrigandCopyright.txt",
    "content": "Copyright Edouard Alligand and Joel Falcou 2015-2017\nhttps://github.com/edouarda/brigand\n"
  },
  {
    "path": "external/Licenses/BrigandLicense.txt",
    "content": "Boost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "external/Licenses/Catch2Copyright.txt",
    "content": "Copyright Catch2 Authors\nhttps://github.com/catchorg/Catch2\n"
  },
  {
    "path": "external/Licenses/Catch2License.txt",
    "content": "Boost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "external/Licenses/CharmCopyright.txt",
    "content": "Copyright (C) 1990-present University of Illinois, Charmworks, Inc.\nhttps://github.com/charmplusplus/charm/\n"
  },
  {
    "path": "external/Licenses/CharmLicense.txt",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. 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\n   2. 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\n   3. 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\n   4. 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\n   5. 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\n   6. 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\n   7. 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\n   8. 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\n   9. 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\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: 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\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n--- LLVM Exceptions to the Apache 2.0 License ----\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"
  },
  {
    "path": "external/Licenses/GslCopyright.txt",
    "content": "Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\nhttps://www.gnu.org/software/gsl/\n"
  },
  {
    "path": "external/Licenses/GslLicense.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "external/Licenses/Hdf5Copyright.txt",
    "content": "HDF5 (Hierarchical Data Format 5) Software Library and Utilities\nCopyright 2006 by The HDF Group.\n\nNCSA HDF5 (Hierarchical Data Format 5) Software Library and Utilities\nCopyright 1998-2006 by The Board of Trustees of the University of Illinois.\nhttps://github.com/HDFGroup/hdf5\n"
  },
  {
    "path": "external/Licenses/Hdf5License.txt",
    "content": "Copyright Notice and License Terms for\nHDF5 (Hierarchical Data Format 5) Software Library and Utilities\n-----------------------------------------------------------------------------\n\nHDF5 (Hierarchical Data Format 5) Software Library and Utilities\nCopyright 2006 by The HDF Group.\n\nNCSA HDF5 (Hierarchical Data Format 5) Software Library and Utilities\nCopyright 1998-2006 by The Board of Trustees of the University of Illinois.\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted for any purpose (including commercial purposes)\nprovided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice,\n   this list of conditions, and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions, and the following disclaimer in the documentation\n   and/or materials provided with the distribution.\n\n3. Neither the name of The HDF Group, the name of the University, nor the\n   name of any Contributor may be used to endorse or promote products derived\n   from this software without specific prior written permission from\n   The HDF Group, the University, or the Contributor, respectively.\n\nDISCLAIMER:\nTHIS SOFTWARE IS PROVIDED BY THE HDF GROUP AND THE CONTRIBUTORS\n\"AS IS\" WITH NO WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED. IN NO\nEVENT SHALL THE HDF GROUP OR THE CONTRIBUTORS BE LIABLE FOR ANY DAMAGES\nSUFFERED BY THE USERS ARISING OUT OF THE USE OF THIS SOFTWARE, EVEN IF\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nYou are under no obligation whatsoever to provide any bug fixes, patches, or\nupgrades to the features, functionality or performance of the source code\n(\"Enhancements\") to anyone; however, if you choose to make your Enhancements\navailable either publicly, or directly to The HDF Group, without imposing a\nseparate written license agreement for such Enhancements, then you hereby\ngrant the following license: a non-exclusive, royalty-free perpetual license\nto install, use, modify, prepare derivative works, incorporate into other\ncomputer software, distribute, and sublicense such enhancements or derivative\nworks thereof, in binary and source code form.\n\n-----------------------------------------------------------------------------\n-----------------------------------------------------------------------------\n\nLimited portions of HDF5 were developed by Lawrence Berkeley National\nLaboratory (LBNL). LBNL's Copyright Notice and Licensing Terms can be\nfound here: COPYING_LBNL_HDF5 file in this directory or at\nhttps://raw.githubusercontent.com/hdfgroup/hdf5/develop/COPYING_LBNL_HDF5.\n\n-----------------------------------------------------------------------------\n-----------------------------------------------------------------------------\n\nContributors:   National Center for Supercomputing Applications (NCSA) at\nthe University of Illinois, Fortner Software, Unidata Program Center\n(netCDF), The Independent JPEG Group (JPEG), Jean-loup Gailly and Mark Adler\n(gzip), and Digital Equipment Corporation (DEC).\n\n-----------------------------------------------------------------------------\n\nPortions of HDF5 were developed with support from the Lawrence Berkeley\nNational Laboratory (LBNL) and the United States Department of Energy\nunder Prime Contract No. DE-AC02-05CH11231.\n\n-----------------------------------------------------------------------------\n\nPortions of HDF5 were developed with support from Lawrence Livermore\nNational Laboratory and the United States Department of Energy under\nPrime Contract No. DE-AC52-07NA27344.\n\n-----------------------------------------------------------------------------\n\nPortions of HDF5 were developed with support from the University of\nCalifornia, Lawrence Livermore National Laboratory (UC LLNL).\nThe following statement applies to those portions of the product and must\nbe retained in any redistribution of source code, binaries, documentation,\nand/or accompanying materials:\n\n   This work was partially produced at the University of California,\n   Lawrence Livermore National Laboratory (UC LLNL) under contract\n   no. W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy\n   (DOE) and The Regents of the University of California (University)\n   for the operation of UC LLNL.\n\n   DISCLAIMER:\n   THIS WORK WAS PREPARED AS AN ACCOUNT OF WORK SPONSORED BY AN AGENCY OF\n   THE UNITED STATES GOVERNMENT. NEITHER THE UNITED STATES GOVERNMENT NOR\n   THE UNIVERSITY OF CALIFORNIA NOR ANY OF THEIR EMPLOYEES, MAKES ANY\n   WARRANTY, EXPRESS OR IMPLIED, OR ASSUMES ANY LIABILITY OR RESPONSIBILITY\n   FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF ANY INFORMATION,\n   APPARATUS, PRODUCT, OR PROCESS DISCLOSED, OR REPRESENTS THAT ITS USE\n   WOULD NOT INFRINGE PRIVATELY- OWNED RIGHTS. REFERENCE HEREIN TO ANY\n   SPECIFIC COMMERCIAL PRODUCTS, PROCESS, OR SERVICE BY TRADE NAME,\n   TRADEMARK, MANUFACTURER, OR OTHERWISE, DOES NOT NECESSARILY CONSTITUTE\n   OR IMPLY ITS ENDORSEMENT, RECOMMENDATION, OR FAVORING BY THE UNITED\n   STATES GOVERNMENT OR THE UNIVERSITY OF CALIFORNIA. THE VIEWS AND\n   OPINIONS OF AUTHORS EXPRESSED HEREIN DO NOT NECESSARILY STATE OR REFLECT\n   THOSE OF THE UNITED STATES GOVERNMENT OR THE UNIVERSITY OF CALIFORNIA,\n   AND SHALL NOT BE USED FOR ADVERTISING OR PRODUCT ENDORSEMENT PURPOSES.\n\n-----------------------------------------------------------------------------\n"
  },
  {
    "path": "external/Licenses/JemallocCopyright.txt",
    "content": "Copyright (C) 2002-present Jason Evans <jasone@canonware.com>.\nAll rights reserved.\nCopyright (C) 2007-2012 Mozilla Foundation.  All rights reserved.\nCopyright (C) 2009-present Facebook, Inc.  All rights reserved.\n"
  },
  {
    "path": "external/Licenses/JemallocLicense.txt",
    "content": "Unless otherwise specified, files in the jemalloc source distribution are\nsubject to the following license:\n--------------------------------------------------------------------------------\nCopyright (C) 2002-present Jason Evans <jasone@canonware.com>.\nAll rights reserved.\nCopyright (C) 2007-2012 Mozilla Foundation.  All rights reserved.\nCopyright (C) 2009-present Facebook, Inc.  All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n1. Redistributions of source code must retain the above copyright notice(s),\n   this list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice(s),\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY EXPRESS\nOR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO\nEVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE\nOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\nADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n--------------------------------------------------------------------------------\n"
  },
  {
    "path": "external/Licenses/LibbacktraceCopyright.txt",
    "content": "Copyright (C) 2012-2024 Free Software Foundation, Inc.\n"
  },
  {
    "path": "external/Licenses/LibbacktraceLicense.txt",
    "content": "Copyright (C) 2012-2024 Free Software Foundation, Inc.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n    (1) Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n\n    (2) Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in\n    the documentation and/or other materials provided with the\n    distribution.\n\n    (3) The name of the author may not be used to\n    endorse or promote products derived from this software without\n    specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,\nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\nIN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "external/Licenses/LibsharpCopyright.txt",
    "content": "opyright (C) 2006-2013 Max-Planck-Society\nhttps://github.com/Libsharp/libsharp\n"
  },
  {
    "path": "external/Licenses/LibsharpLicense.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "external/Licenses/LibxsmmCopyright.txt",
    "content": "Copyright (c) 2009-2023, Intel Corporation\nCopyright (c) 2017-2022, Friedrich Schiller University Jena\nCopyright (c) 2012-2021, Technische Universitaet Muenchen\nCopyright (c) 2016-2020, Google Inc.\nhttps://github.com/libxsmm/libxsmm\n"
  },
  {
    "path": "external/Licenses/LibxsmmLicense.txt",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2009-2023, Intel Corporation\nCopyright (c) 2017-2022, Friedrich Schiller University Jena\nCopyright (c) 2012-2021, Technische Universitaet Muenchen\nCopyright (c) 2016-2020, Google Inc.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\n\n    Redistributions of source code must retain the above copyright notice, this\n    list of conditions and the following disclaimer.\n\n    Redistributions in binary form must reproduce the above copyright notice,\n    this list of conditions and the following disclaimer in the documentation\n    and/or other materials provided with the distribution.\n\n    Neither the name of the copyright holder nor the names of its contributors\n    may be used to endorse or promote products derived from this software\n    without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\nANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "external/Licenses/OpenBlasCopyright.txt",
    "content": "Copyright (c) 2011-2024, The OpenBLAS Project\nCopyright 2009, 2010 The University of Texas at Austin.\nhttps://github.com/OpenMathLib/OpenBLAS\n"
  },
  {
    "path": "external/Licenses/OpenBlasLicense.txt",
    "content": "Copyright (c) 2011-2014, The OpenBLAS Project\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n   1. Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n\n   2. Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in\n      the documentation and/or other materials provided with the\n      distribution.\n   3. Neither the name of the OpenBLAS project nor the names of\n      its contributors may be used to endorse or promote products\n      derived from this software without specific prior written\n      permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\nUSE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "external/Licenses/Pybind11Copyright.txt",
    "content": "Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n"
  },
  {
    "path": "external/Licenses/Pybind11License.txt",
    "content": "Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\n3. Neither the name of the copyright holder nor the names of its contributors\n   may be used to endorse or promote products derived from this software\n   without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\nPlease also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of\nexternal contributions to this project including patches, pull requests, etc.\n"
  },
  {
    "path": "external/Licenses/SpectreGpl3License.txt",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "external/Licenses/XsimdCopyright.txt",
    "content": "Copyright (c) 2016, Johan Mabille, Sylvain Corlay, Wolf Vollprecht and Martin\nRenou\nCopyright (c) 2016, QuantStack\nCopyright (c) 2018, Serge Guelton\nhttps://github.com/xtensor-stack/xsimd\n"
  },
  {
    "path": "external/Licenses/XsimdLicense.txt",
    "content": "Copyright (c) 2016, Johan Mabille, Sylvain Corlay, Wolf Vollprecht and Martin\nRenou\nCopyright (c) 2016, QuantStack\nCopyright (c) 2018, Serge Guelton\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "external/Licenses/YamlCppCopyright.txt",
    "content": "Copyright (c) 2008-2015 Jesse Beder.\nCopyright 2005-2010, 2013, 2015, 2017-2020 Google Inc.\nCopyright 2007 Neal Norwitz\nhttps://github.com/jbeder/yaml-cpp\n"
  },
  {
    "path": "external/Licenses/YamlCppLicense.txt",
    "content": "Copyright (c) 2008-2015 Jesse Beder.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "external/SPHEREPACK/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SPHEREPACK)\n\nset(LIBRARY_SOURCES\n    alf.f\n    divgs.f\n    gaqd.f\n    gradgs.f\n    hrfft.f\n    shags.f\n    shsgs.f\n    slapgs.f\n    sphcom.f\n    vhags.f\n    vhsgs.f\n    vrtgs.f\n    vtsgs.f\n    )\n\n# Starting in gcc8, gfortran warns about instances of Fortran77 coding style in\n# certain SPHEREPACK files. The warning is:\n#   Warning: Array reference at ... out of bounds ... in loop beginning ...\n#\n# Attempts to silence the warning by use of individual flags were unsuccessful,\n# so instead we silence all warnings for the problematic files.\nset(FILES_WITH_ARRAY_REFERENCES\n    alf.f\n    sphcom.f\n    vhags.f\n    vhsgs.f\n    vtsgs.f\n    )\nset_source_files_properties(${FILES_WITH_ARRAY_REFERENCES}\n    PROPERTIES COMPILE_FLAGS -w)\n\n# Starting in gcc9, gfortran warns about deleted language features in certain\n# SPHEREPACK files. The warnings are,\n# * Warning: Fortran 2018 deleted feature: Arithmetic IF statement at ...\n# * Warning: Fortran 2018 deleted feature: Shared DO termination label at ...\n#\n# We silence these warnings too.\n# An alternative solution would be to specify the compiler flag `-std=legacy`.\nset(FILES_WITH_DELETED_FEATURES\n    gradgs.f\n    hrfft.f\n    shags.f\n    shsgs.f\n    )\nset_source_files_properties(${FILES_WITH_DELETED_FEATURES}\n    PROPERTIES COMPILE_FLAGS -w)\n\nadd_library(${LIBRARY} ${LIBRARY_SOURCES})\n\nif(SPECTRE_Fortran_STATIC_LIBS)\n  if (NOT $gfortran)\n    find_library(gfortran NAMES libgfortran.a)\n  endif()\n  if (NOT $quadmath)\n    find_library(quadmath NAMES libquadmath.a)\n  endif()\n  target_link_libraries(\n    ${LIBRARY}\n    PRIVATE\n    ${gfortran}\n    ${quadmath}\n  )\nendif()\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  SpectreFlags\n)\n"
  },
  {
    "path": "external/SPHEREPACK/LICENSE.txt",
    "content": "Copyright © 2004 the University Corporation for Atmospheric Research\n(\"UCAR\"). All rights reserved. Developed by NCAR's Computational and\nInformation Systems Laboratory, UCAR, www2.cisl.ucar.edu.\n\nRedistribution and use of the Software in source and binary forms,\nwith or without modification, is permitted provided that the following\nconditions are met:\n\nNeither the names of NCAR's Computational and Information Systems\nLaboratory, the University Corporation for Atmospheric Research, nor\nthe names of its sponsors or contributors may be used to endorse or\npromote products derived from this Software without specific prior\nwritten permission.  Redistributions of source code must retain the\nabove copyright notices, this list of conditions, and the disclaimer\nbelow.  Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions, and the disclaimer below in\nthe documentation and/or other materials provided with the\ndistribution.\n\nTHIS SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT\nHOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE\nSOFTWARE.\n\nSPHEREPACK is a product of the Computational & Information Systems\nLaboratory at the National Center for Atmospheric Research (NCAR)\n"
  },
  {
    "path": "external/SPHEREPACK/README.txt",
    "content": "This is a modified version of SPHEREPACK 3.2.\nThe SPHEREPACK 3.2 license can be found in LICENSE.txt.\n\nOnly 13 FORTRAN files, with the functionality we need for SpECTRE,\nhave been retained here.  The unmodified SPHEREPACK contains\nadditional files with more functionality than we need, and we do not\ninclude these files, nor do we include the SPHEREPACK makefiles or\ntests.\n\nA summary of the modifications:\n\n1) We have added our own Spherepack.hpp for #including SPHEREPACK\n   subroutines into C/C++.\n2) All computations were changed to double precision.\n3) divgs, gradgs, shags, shsgs, slapgs, vhags, vhsgs, vrtgs, vtsgs now\n   have the ability to loop over 'offsets' and do multiple\n   computations at once with a single call to a SPHEREPACK function.\n   This is useful if you have a (r,theta,phi) grid, and you need to do\n   spherical transforms on (theta,phi) for multiple values of r.\n4) Bugfix in vtsgs.f, where mmax was computed incorrectly if nlon is even.\n5) shsgs and shags no longer have limitations on the size of the passed-in\n   coefficient arrays 'a' and 'b'.  They also take additional arguments,\n   'mdabmax, 'ndabmax'.  Coefficients that do not fit into the provided\n   storage are ignored / treated as zero / not computed.   The same holds\n   for coefficients with indices above {m,n}dabmax.  This allows for\n   more efficient implementation of definite integrals, where now\n   one can request computation of the l=0 coefficient only.   This also\n   helps in changing resolution, since restriction or prolongation\n   of the coefficient array is no longer (explicitly) needed.\n6) Given that we so far do not need to call SPHEREPACK with symmetries\n   (e.g. equatorial symmetry, octant symmetry, etc) enforced, the above\n   changes were not tested with SPHEREPACK's symmetry options turned on.\n   In order to avoid surprises, SPHEREPACK's symmetry options are disabled.\n   If you need symmetries, please test them.\n"
  },
  {
    "path": "external/SPHEREPACK/alf.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc     file alf.f contains subroutines alfk,lfim,lfim1,lfin,lfin1,lfpt\nc     for computing normalized associated legendre polynomials\nc\nc subroutine alfk (n,m,cp)\nc\nc dimension of           real cp(n/2 + 1)\nc arguments\nc\nc purpose                routine alfk computes single precision fourier\nc                        coefficients in the trigonometric series\nc                        representation of the normalized associated\nc                        legendre function pbar(n,m,theta) for use by\nc                        routines lfp and lfpt in calculating single\nc                        precision pbar(n,m,theta).\nc\nc                        first define the normalized associated\nc                        legendre functions\nc\nc                        pbar(m,n,theta) = sqrt((2*n+1)*factorial(n-m)\nc                        /(2*factorial(n+m)))*sin(theta)**m/(2**n*\nc                        factorial(n)) times the (n+m)th derivative of\nc                        (x**2-1)**n with respect to x=cos(theta)\nc\nc                        where theta is colatitude.\nc\nc                        then subroutine alfk computes the coefficients\nc                        cp(k) in the following trigonometric\nc                        expansion of pbar(m,n,theta).\nc\nc                        1) for n even and m even, pbar(m,n,theta) =\nc                           .5*cp(1) plus the sum from k=1 to k=n/2\nc                           of cp(k+1)*cos(2*k*th)\nc\nc                        2) for n even and m odd, pbar(m,n,theta) =\nc                           the sum from k=1 to k=n/2 of\nc                           cp(k)*sin(2*k*th)\nc\nc                        3) for n odd and m even, pbar(m,n,theta) =\nc                           the sum from k=1 to k=(n+1)/2 of\nc                           cp(k)*cos((2*k-1)*th)\nc\nc                        4) for n odd and m odd,  pbar(m,n,theta) =\nc                           the sum from k=1 to k=(n+1)/2 of\nc                           cp(k)*sin((2*k-1)*th)\nc\nc\nc usage                  call alfk(n,m,cp)\nc\nc arguments\nc\nc on input               n\nc                          nonnegative integer specifying the degree of\nc                          pbar(n,m,theta)\nc\nc                        m\nc                          is the order of pbar(n,m,theta). m can be\nc                          any integer however cp is computed such that\nc                          pbar(n,m,theta) = 0 if abs(m) is greater\nc                          than n and pbar(n,m,theta) = (-1)**m*\nc                          pbar(n,-m,theta) for negative m.\nc\nc on output              cp\nc                          single precision array of length (n/2)+1\nc                          which contains the fourier coefficients in\nc                          the trigonometric series representation of\nc                          pbar(n,m,theta)\nc\nc\nc special conditions     none\nc\nc precision              single\nc\nc algorithm              the highest order coefficient is determined in\nc                        closed form and the remainig coefficients are\nc                        determined as the solution of a backward\nc                        recurrence relation.\nc\nc accuracy               comparison between routines alfk and double\nc                        precision dalfk on the cray1 indicates\nc                        greater accuracy for smaller values\nc                        of input parameter n.  agreement to 14\nc                        places was obtained for n=10 and to 13\nc                        places for n=100.\nc\n      subroutine alfk (n,m,cp)\n      implicit double precision (a-h,o-z)\n      dimension       cp(n/2+1)\n      parameter (sc10=1024.d0)\n      parameter (sc20=sc10*sc10)\n      parameter (sc40=sc20*sc20)\nc\n      cp(1) = 0.d0\n      ma = iabs(m)\n      if(ma .gt. n) return\n      if(n-1) 2,3,5\n    2 cp(1) = dsqrt(2.d0)\n      return\n    3 if(ma .ne. 0) go to 4\n      cp(1) = dsqrt(1.5d0)\n      return\n    4 cp(1) = dsqrt(.75d0)\n      if(m .eq. -1) cp(1) = -cp(1)\n      return\n    5 if(mod(n+ma,2) .ne. 0) go to 10\n      nmms2 = (n-ma)/2\n      fnum = n+ma+1\n      fnmh = n-ma+1\n      pm1 = 1.d0\n      go to 15\n   10 nmms2 = (n-ma-1)/2\n      fnum = n+ma+2\n      fnmh = n-ma+2\n      pm1 = -1.d0\n 15   t1 = 1.d0/sc20\n      nex = 20\n      fden = 2.d0\n      if(nmms2 .lt. 1) go to 20\n      do 18 i=1,nmms2\n      t1 = fnum*t1/fden\n      if(t1 .gt. sc20) then\n      t1 = t1/sc40\n      nex = nex+40\n      end if\n      fnum = fnum+2.d0\n      fden = fden+2.d0\n   18 continue\n   20 t1 = t1/2.d0**(n-1-nex)\n      if(mod(ma/2,2) .ne. 0) t1 = -t1\n      t2 = 1.d0 \n      if(ma .eq. 0) go to 26\n      do 25 i=1,ma\n      t2 = fnmh*t2/(fnmh+pm1)\n      fnmh = fnmh+2.d0\n   25 continue\n   26 cp2 = t1*dsqrt((n+.5d0)*t2)\n      fnnp1 = n*(n+1)\n      fnmsq = fnnp1-2.d0*ma*ma\n      l = (n+1)/2\n      if(mod(n,2) .eq. 0 .and. mod(ma,2) .eq. 0) l = l+1\n      cp(l) = cp2\n      if(m .ge. 0) go to 29\n      if(mod(ma,2) .ne. 0) cp(l) = -cp(l)\n   29 if(l .le. 1) return\n      fk = n\n      a1 = (fk-2.d0)*(fk-1.d0)-fnnp1\n      b1 = 2.d0*(fk*fk-fnmsq)\n      cp(l-1) = b1*cp(l)/a1\n   30 l = l-1\n      if(l .le. 1) return\n      fk = fk-2.d0\n      a1 = (fk-2.d0)*(fk-1.d0)-fnnp1\n      b1 = -2.d0*(fk*fk-fnmsq)\n      c1 = (fk+1.d0)*(fk+2.d0)-fnnp1\n      cp(l-1) = -(b1*cp(l)+c1*cp(l+1))/a1\n      go to 30\n      end\nc subroutine lfim (init,theta,l,n,nm,pb,id,wlfim)\nc\nc dimension of           theta(l),  pb(id,nm+1),  wlfim(4*l*(nm+1))\nc arguments\nc\nc purpose                given n and l, routine lfim calculates\nc                        the normalized associated legendre functions\nc                        pbar(n,m,theta) for m=0,...,n and theta(i)\nc                        for i=1,...,l where\nc\nc                        pbar(m,n,theta) = sqrt((2*n+1)*factorial(n-m)\nc                        /(2*factorial(n+m)))*sin(theta)**m/(2**n*\nc                        factorial(n)) times the (n+m)th derivative of\nc                        (x**2-1)**n with respect to x=cos(theta)\nc\nc usage                  call lfim (init,theta,l,n,nm,pb,id,wlfim)\nc\nc arguments\nc on input               init\nc                        = 0\nc                            initialization only - using parameters\nc                            l, nm and array theta, subroutine lfim\nc                            initializes array wlfim for subsequent\nc                            use in the computation of the associated\nc                            legendre functions pb. initialization\nc                            does not have to be repeated unless\nc                            l, nm, or array theta are changed.\nc                        = 1\nc                            subroutine lfim uses the array wlfim that\nc                            was computed with init = 0 to compute pb.\nc\nc                        theta\nc                          an array that contains the colatitudes\nc                          at which the associated legendre functions\nc                          will be computed. the colatitudes must be\nc                          specified in radians.\nc\nc                        l\nc                          the length of the theta array. lfim is\nc                          vectorized with vector length l.\nc\nc                        n\nc                          nonnegative integer, less than nm, specifying\nc                          degree of pbar(n,m,theta). subroutine lfim\nc                          must be called starting with n=0. n must be\nc                          incremented by one in subsequent calls and\nc                          must not exceed nm.\nc\nc                        nm\nc                          the maximum value of n and m\nc\nc                        id\nc                          the first dimension of the two dimensional\nc                          array pb as it appears in the program that\nc                          calls lfim. (see output parameter pb)\nc\nc                        wlfim\nc                          an array with length 4*l*(nm+1) which\nc                          must be initialized by calling lfim\nc                          with init=0 (see parameter init)  it\nc                          must not be altered between calls to\nc                          lfim.\nc\nc\nc on output              pb\nc                          a two dimensional array with first\nc                          dimension id in the program that calls\nc                          lfim. the second dimension of pb must\nc                          be at least nm+1. starting with n=0\nc                          lfim is called repeatedly with n being\nc                          increased by one between calls. on each\nc                          call, subroutine lfim computes\nc                          = pbar(m,n,theta(i)) for m=0,...,n and\nc                          i=1,...l.\nc\nc                        wlfim\nc                          array containing values which must not\nc                          be altered unless l, nm or the array theta\nc                          are changed in which case lfim must be\nc                          called with init=0 to reinitialize the\nc                          wlfim array.\nc\nc special conditions     n must be increased by one between calls\nc                        of lfim in which n is not zero.\nc\nc precision              single\nc\nc\nc algorithm              routine lfim calculates pbar(n,m,theta) using\nc                        a four term recurrence relation. (unpublished\nc                        notes by paul n. swarztrauber)\nc\n      subroutine lfim (init,theta,l,n,nm,pb,id,wlfim)\n      implicit double precision (a-h,o-z)\n      dimension       theta(1), pb(1)        ,wlfim(1)\nc\nc     total length of wlfim is 4*l*(nm+1)\nc\n      lnx = l*(nm+1)\n      iw1 = lnx+1\n      iw2 = iw1+lnx\n      iw3 = iw2+lnx\n      call lfim1(init,theta,l,n,nm,id,pb,wlfim,wlfim(iw1),\n     1                wlfim(iw2),wlfim(iw3),wlfim(iw2))\n      return\n      end\n      subroutine lfim1(init,theta,l,n,nm,id,p3,phz,ph1,p1,p2,cp)\n      implicit double precision (a-h,o-z)\n      dimension       p1(l,*)    ,p2(l,*)    ,p3(id,*)   ,phz(l,*)   ,\n     1                ph1(l,*)   ,cp(*)      ,theta(*)\n      nmp1 = nm+1\n      if(init .ne. 0) go to 5\n      ssqrt2 = 1.d0/dsqrt(2.d0)\n      do 10 i=1,l\n      phz(i,1) = ssqrt2\n   10 continue\n      do 15 np1=2,nmp1\n      nh = np1-1\n      call alfk(nh,0,cp)\n      do 16 i=1,l\n      call lfpt(nh,0,theta(i),cp,phz(i,np1))\n   16 continue\n      call alfk(nh,1,cp)\n      do 17 i=1,l\n      call lfpt(nh,1,theta(i),cp,ph1(i,np1))\n   17 continue\n   15 continue\n      return\n    5 if(n .gt. 2) go to 60\n      if(n-1)25,30,35\n   25 do 45 i=1,l\n      p3(i,1)=phz(i,1)\n   45 continue\n      return\n   30 do 50 i=1,l\n      p3(i,1) = phz(i,2)\n      p3(i,2) = ph1(i,2)\n   50 continue\n      return\n   35 sq5s6 = dsqrt(5.d0/6.d0)\n      sq1s6 = dsqrt(1.d0/6.d0)\n      do 55 i=1,l\n      p3(i,1) = phz(i,3)\n      p3(i,2) = ph1(i,3)\n      p3(i,3) = sq5s6*phz(i,1)-sq1s6*p3(i,1)\n      p1(i,1) = phz(i,2)\n      p1(i,2) = ph1(i,2)\n      p2(i,1) = phz(i,3)\n      p2(i,2) = ph1(i,3)\n      p2(i,3) = p3(i,3)\n   55 continue\n      return\n   60 nm1 = n-1\n      np1 = n+1\n      fn = dfloat(n)\n      tn = fn+fn\n      cn = (tn+1.d0)/(tn-3.d0)\n      do 65 i=1,l\n      p3(i,1) = phz(i,np1)\n      p3(i,2) = ph1(i,np1)\n   65 continue\n      if(nm1 .lt. 3) go to 71\n      do 70 mp1=3,nm1\n      m = mp1-1\n      fm = dfloat(m)\n      fnpm = fn+fm\n      fnmm = fn-fm\n      temp = fnpm*(fnpm-1.d0)\n      cc = dsqrt(cn*(fnpm-3.d0)*(fnpm-2.d0)/temp)\n      dd = dsqrt(cn*fnmm*(fnmm-1.d0)/temp)\n      ee = dsqrt((fnmm+1.d0)*(fnmm+2.d0)/temp)\n      do 70 i=1,l\n      p3(i,mp1) = cc*p1(i,mp1-2)+dd*p1(i,mp1)-ee*p3(i,mp1-2)\n   70 continue\n   71 fnpm = fn+fn-1.d0\n      temp = fnpm*(fnpm-1.d0)\n      cc = dsqrt(cn*(fnpm-3.d0)*(fnpm-2.d0)/temp)\n      ee = dsqrt(6.d0/temp)\n      do 75 i=1,l\n      p3(i,n) = cc*p1(i,n-2)-ee*p3(i,n-2)\n   75 continue\n      fnpm = fn+fn\n      temp = fnpm*(fnpm-1.d0)\n      cc = dsqrt(cn*(fnpm-3.d0)*(fnpm-2.d0)/temp)\n      ee = dsqrt(2.d0/temp)\n      do 80 i=1,l\n      p3(i,n+1) = cc*p1(i,n-1)-ee*p3(i,n-1)\n   80 continue\n      do 90 mp1=1,np1\n      do 90 i=1,l\n      p1(i,mp1) = p2(i,mp1)\n      p2(i,mp1) = p3(i,mp1)\n   90 continue\n      return\n      end\nc subroutine lfin (init,theta,l,m,nm,pb,id,wlfin)\nc\nc dimension of           theta(l),  pb(id,nm+1),  wlfin(4*l*(nm+1))\nc arguments\nc\nc purpose                given m and l, routine lfin calculates\nc                        the normalized associated legendre functions\nc                        pbar(n,m,theta) for n=m,...,nm and theta(i)\nc                        for i=1,...,l where\nc\nc                        pbar(m,n,theta) = sqrt((2*n+1)*factorial(n-m)\nc                        /(2*factorial(n+m)))*sin(theta)**m/(2**n*\nc                        factorial(n)) times the (n+m)th derivative of\nc                        (x**2-1)**n with respect to x=cos(theta)\nc\nc usage                  call lfin (init,theta,l,m,nm,pb,id,wlfin)\nc\nc arguments\nc on input               init\nc                        = 0\nc                            initialization only - using parameters\nc                            l, nm and the array theta, subroutine lfin\nc                            initializes the array wlfin for subsequent\nc                            use in the computation of the associated\nc                            legendre functions pb. initialization does\nc                            not have to be repeated unless l, nm or\nc                            the array theta are changed.\nc                        = 1\nc                            subroutine lfin uses the array wlfin that\nc                            was computed with init = 0 to compute pb\nc\nc                        theta\nc                          an array that contains the colatitudes\nc                          at which the associated legendre functions\nc                          will be computed. the colatitudes must be\nc                          specified in radians.\nc\nc                        l\nc                          the length of the theta array. lfin is\nc                          vectorized with vector length l.\nc\nc                        m\nc                          nonnegative integer, less than nm, specifying\nc                          degree of pbar(n,m,theta). subroutine lfin\nc                          must be called starting with n=0. n must be\nc                          incremented by one in subsequent calls and\nc                          must not exceed nm.\nc\nc                        nm\nc                          the maximum value of n and m\nc\nc                        id\nc                          the first dimension of the two dimensional\nc                          array pb as it appears in the program that\nc                          calls lfin. (see output parameter pb)\nc\nc                        wlfin\nc                          an array with length 4*l*(nm+1) which\nc                          must be initialized by calling lfin\nc                          with init=0 (see parameter init)  it\nc                          must not be altered between calls to\nc                          lfin.\nc\nc\nc on output              pb\nc                          a two dimensional array with first\nc                          dimension id in the program that calls\nc                          lfin. the second dimension of pb must\nc                          be at least nm+1. starting with m=0\nc                          lfin is called repeatedly with m being\nc                          increased by one between calls. on each\nc                          call, subroutine lfin computes pb(i,n+1)\nc                          = pbar(m,n,theta(i)) for n=m,...,nm and\nc                          i=1,...l.\nc\nc                        wlfin\nc                          array containing values which must not\nc                          be altered unless l, nm or the array theta\nc                          are changed in which case lfin must be\nc                          called with init=0 to reinitialize the\nc                          wlfin array.\nc\nc special conditions     m must be increased by one between calls\nc                        of lfin in which m is not zero.\nc\nc precision              single\nc\nc algorithm              routine lfin calculates pbar(n,m,theta) using\nc                        a four term recurrence relation. (unpublished\nc                        notes by paul n. swarztrauber)\nc\n      subroutine lfin (init,theta,l,m,nm,pb,id,wlfin)\n      implicit double precision (a-h,o-z)\n      dimension       theta(1), pb(1)        ,wlfin(1)\nc\nc     total length of wlfin is 4*l*(nm+1)\nc\n      lnx = l*(nm+1)\n      iw1 = lnx+1\n      iw2 = iw1+lnx\n      iw3 = iw2+lnx\n      call lfin1(init,theta,l,m,nm,id,pb,wlfin,wlfin(iw1),\n     1                wlfin(iw2),wlfin(iw3),wlfin(iw2))\n      return\n      end\n      subroutine lfin1(init,theta,l,m,nm,id,p3,phz,ph1,p1,p2,cp)\n      implicit double precision (a-h,o-z)\n      dimension       p1(l,1)    ,p2(l,1)    ,p3(id,1)   ,phz(l,1)   ,\n     1                ph1(l,1)   ,cp(1)      ,theta(1)\n      nmp1 = nm+1\n      if(init .ne. 0) go to 5\n      ssqrt2 = 1.d0/dsqrt(2.d0)\n      do 10 i=1,l\n      phz(i,1) = ssqrt2\n   10 continue\n      do 15 np1=2,nmp1\n      nh = np1-1\n      call alfk(nh,0,cp)\n      do 16 i=1,l\n      call lfpt(nh,0,theta(i),cp,phz(i,np1))\n   16 continue\n      call alfk(nh,1,cp)\n      do 17 i=1,l\n      call lfpt(nh,1,theta(i),cp,ph1(i,np1))\n   17 continue\n   15 continue\n      return\n    5 mp1 = m+1\n      fm = dfloat(m)\n      tm = fm+fm\n      if(m-1)25,30,35\n   25 do 45 np1=1,nmp1\n      do 45 i=1,l\n      p3(i,np1) = phz(i,np1)\n      p1(i,np1) = phz(i,np1)\n   45 continue\n      return\n   30 do 50 np1=2,nmp1\n      do 50 i=1,l\n      p3(i,np1) = ph1(i,np1)\n      p2(i,np1) = ph1(i,np1)\n   50 continue\n      return\n   35 temp = tm*(tm-1.d0)\n      cc = dsqrt((tm+1.d0)*(tm-2.d0)/temp)\n      ee = dsqrt(2.d0/temp)\n      do 85 i=1,l\n      p3(i,m+1) = cc*p1(i,m-1)-ee*p1(i,m+1)\n   85 continue\n      if(m .eq. nm) return\n      temp = tm*(tm+1.d0)\n      cc = dsqrt((tm+3.d0)*(tm-2.d0)/temp)\n      ee = dsqrt(6.d0/temp)\n      do 70 i=1,l\n      p3(i,m+2) = cc*p1(i,m)-ee*p1(i,m+2)\n   70 continue\n      mp3 = m+3\n      if(nmp1 .lt. mp3) go to 80\n      do 75 np1=mp3,nmp1\n      n = np1-1\n      fn = dfloat(n)\n      tn = fn+fn\n      cn = (tn+1.d0)/(tn-3.d0)\n      fnpm = fn+fm\n      fnmm = fn-fm\n      temp = fnpm*(fnpm-1.d0)\n      cc = dsqrt(cn*(fnpm-3.d0)*(fnpm-2.d0)/temp)\n      dd = dsqrt(cn*fnmm*(fnmm-1.d0)/temp)\n      ee = dsqrt((fnmm+1.d0)*(fnmm+2.d0)/temp)\n      do 75 i=1,l\n      p3(i,np1) = cc*p1(i,np1-2)+dd*p3(i,np1-2)-ee*p1(i,np1)\n   75 continue\n   80 do 90 np1=m,nmp1\n      do 90 i=1,l\n      p1(i,np1) = p2(i,np1)\n      p2(i,np1) = p3(i,np1)\n   90 continue\n      return\n      end\nc subroutine lfpt (n,m,theta,cp,pb)\nc\nc dimension of\nc arguments\nc                        cp((n/2)+1)\nc\nc purpose                routine lfpt uses coefficients computed by\nc                        routine alfk to compute the single precision\nc                        normalized associated legendre function pbar(n,\nc                        m,theta) at colatitude theta.\nc\nc usage                  call lfpt(n,m,theta,cp,pb)\nc\nc arguments\nc\nc on input               n\nc                          nonnegative integer specifying the degree of\nc                          pbar(n,m,theta)\nc                        m\nc                          is the order of pbar(n,m,theta). m can be\nc                          any integer however pbar(n,m,theta) = 0\nc                          if abs(m) is greater than n and\nc                          pbar(n,m,theta) = (-1)**m*pbar(n,-m,theta)\nc                          for negative m.\nc\nc                        theta\nc                          single precision colatitude in radians\nc\nc                        cp\nc                          single precision array of length (n/2)+1\nc                          containing coefficients computed by routine\nc                          alfk\nc\nc on output              pb\nc                          single precision variable containing\nc                          pbar(n,m,theta)\nc\nc special conditions     calls to routine lfpt must be preceded by an\nc                        appropriate call to routine alfk.\nc\nc precision              single\nc\nc algorithm              the trigonometric series formula used by\nc                        routine lfpt to calculate pbar(n,m,th) at\nc                        colatitude th depends on m and n as follows:\nc\nc                           1) for n even and m even, the formula is\nc                              .5*cp(1) plus the sum from k=1 to k=n/2\nc                              of cp(k)*cos(2*k*th)\nc                           2) for n even and m odd. the formula is\nc                              the sum from k=1 to k=n/2 of\nc                              cp(k)*sin(2*k*th)\nc                           3) for n odd and m even, the formula is\nc                              the sum from k=1 to k=(n+1)/2 of\nc                              cp(k)*cos((2*k-1)*th)\nc                           4) for n odd and m odd, the formula is\nc                              the sum from k=1 to k=(n+1)/2 of\nc                              cp(k)*sin((2*k-1)*th)\nc\nc accuracy               comparison between routines lfpt and double\nc                        precision dlfpt on the cray1 indicates greater\nc                        accuracy for greater values on input parameter\nc                        n.  agreement to 13 places was obtained for\nc                        n=10 and to 12 places for n=100.\nc\nc timing                 time per call to routine lfpt is dependent on\nc                        the input parameter n.\nc\n      subroutine lfpt (n,m,theta,cp,pb)\n      implicit double precision (a-h,o-z)\n      dimension       cp(1)\nc\n      pb = 0.d0\n      ma = iabs(m)\n      if(ma .gt. n) return\n      if (n)  10, 10, 30\n   10 if (ma)  20, 20, 30\n   20 pb= dsqrt(.5d0)\n      go to 140\n   30 np1 = n+1\n      nmod = mod(n,2)\n      mmod = mod(ma,2)\n      if (nmod)  40, 40, 90\n   40 if (mmod)  50, 50, 70\n   50 kdo = n/2+1\n      cdt = dcos(theta+theta)\n      sdt = dsin(theta+theta)\n      ct = 1.d0\n      st = 0.d0\n      sum = .5d0*cp(1)\n      do  60 kp1=2,kdo\n         cth = cdt*ct-sdt*st\n         st = sdt*ct+cdt*st\n         ct = cth\n         sum = sum+cp(kp1)*ct\n   60 continue\n      pb= sum\n      go to 140\n   70 kdo = n/2\n      cdt = dcos(theta+theta)\n      sdt = dsin(theta+theta)\n      ct = 1.d0\n      st = 0.d0\n      sum = 0.d0\n      do  80 k=1,kdo\n         cth = cdt*ct-sdt*st\n         st = sdt*ct+cdt*st\n         ct = cth\n         sum = sum+cp(k)*st\n   80 continue\n      pb= sum\n      go to 140\n   90 kdo = (n+1)/2\n      if (mmod) 100,100,120\n  100 cdt = dcos(theta+theta)\n      sdt = dsin(theta+theta)\n      ct = dcos(theta)\n      st = -dsin(theta)\n      sum = 0.d0\n      do 110 k=1,kdo\n         cth = cdt*ct-sdt*st\n         st = sdt*ct+cdt*st\n         ct = cth\n         sum = sum+cp(k)*ct\n  110 continue\n      pb= sum\n      go to 140\n  120 cdt = dcos(theta+theta)\n      sdt = dsin(theta+theta)\n      ct = dcos(theta)\n      st = -dsin(theta)\n      sum = 0.d0\n      do 130 k=1,kdo\n         cth = cdt*ct-sdt*st\n         st = sdt*ct+cdt*st\n         ct = cth\n         sum = sum+cp(k)*st\n  130 continue\n      pb= sum\n  140 return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/divgs.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc\nc ... file divgs.f\nc\nc     this file includes documentation and code for\nc     subroutine divgs          i\nc\nc ... files which must be loaded with divgs.f\nc\nc     sphcom.f, hrfft.f, vhags.f, shsgs.f, gaqd.f\nc\nc\nc     subroutine divgs(nstrg,nstrb,noffsg,noffsb,\nc                      nlat,nlon,isym,nt,divg,idiv,jdiv,br,bi,mdb,ndb,\nc    +                 wshsgs,lshsgs,work,lwork,ierror)\nc\nc     given the vector spherical harmonic coefficients br and bi, precomputed\nc     by subroutine vhags for a vector field (v,w), subroutine divgs\nc     computes the divergence of the vector field in the scalar array divg.\nc     divg(i,j) is the divergence at the gaussian colatitude point theta(i)\nc     (see nlat as input parameter) and east longitude\nc\nc            lambda(j) = (j-1)*2*pi/nlon\nc\nc     on the sphere.  i.e.\nc\nc            dv(i,j) = 1/sint*[ d(sint*v(i,j))/dtheta + d(w(i,j))/dlambda ]\nc\nc     where sint = sin(theta(i)).  w is the east longitudinal and v\nc     is the colatitudinal component of the vector field from which\nc     br,bi were precomputed\nc\nc\nc     input parameters\nc\nc     nstrg,nstrb   strides in g,b\nc     noffsg,noffsb offsets in g,b -- must be between 0 and stride-1\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are computed\nc            in radians in theta(1) <...< theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid point\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than zero. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for isym==0.  For this reason an error code will\nc           be returned if isym!=0.\nc\nc\nc     isym   a parameter which determines whether the divergence is\nc            computed on the full or half sphere as follows:\nc\nc      = 0\nc\nc            the symmetries/antsymmetries described in isym=1,2 below\nc            do not exist in (v,w) about the equator.  in this case the\nc            divergence is neither symmetric nor antisymmetric about\nc            the equator.  the divergence is computed on the entire\nc            sphere.  i.e., in the array divg(i,j) for i=1,...,nlat and\nc            j=1,...,nlon.\nc\nc      = 1\nc\nc            w is antisymmetric and v is symmetric about the equator.\nc            in this case the divergence is antisymmetyric about\nc            the equator and is computed for the northern hemisphere\nc            only.  i.e., if nlat is odd the divergence is computed\nc            in the array divg(i,j) for i=1,...,(nlat+1)/2 and for\nc            j=1,...,nlon.  if nlat is even the divergence is computed\nc            in the array divg(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc      = 2\nc            w is symmetric and v is antisymmetric about the equator\nc            in this case the divergence is symmetyric about the\nc            equator and is computed for the northern hemisphere\nc            only.  i.e., if nlat is odd the divergence is computed\nc            in the array divg(i,j) for i=1,...,(nlat+1)/2 and for\nc            j=1,...,nlon.  if nlat is even the divergence is computed\nc            in the array divg(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc\nc     nt     nt is the number of scalar and vector fields.  some\nc            computational efficiency is obtained for multiple fields.\nc            in the program that calls divgs, the arrays br,bi, and divg\nc            can be three dimensional corresponding to an indexed multiple\nc            vector field.  in this case multiple scalar synthesis will\nc            be performed to compute the divergence for each field.  the\nc            third index is the synthesis index which assumes the values\nc            k=1,...,nt.  for a single synthesis set nt = 1.  the\nc            description of the remaining parameters is simplified by\nc            assuming that nt=1 or that all the arrays are two dimensional.\nc\nc     idiv   the first dimension of the array divg as it appears in\nc            the program that calls divgs. if isym = 0 then idiv\nc            must be at least nlat.  if isym = 1 or 2 and nlat is\nc            even then idiv must be at least nlat/2. if isym = 1 or 2\nc            and nlat is odd then idiv must be at least (nlat+1)/2.\nc\nc     jdiv   the second dimension of the array divg as it appears in\nc            the program that calls divgs. jdiv must be at least nlon.\nc\nc     br,bi  two or three dimensional arrays (see input parameter nt)\nc            that contain vector spherical harmonic coefficients\nc            of the vector field (v,w) as computed by subroutine vhags.\nc     ***    br and bi must be computed by vhags prior to calling\nc            divgs.\nc\nc     mdb    the first dimension of the arrays br and bi as it\nc            appears in the program that calls divgs. mdb must be at\nc            least min0(nlat,nlon/2) if nlon is even or at least\nc            min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc     ndb    the second dimension of the arrays br and bi as it\nc            appears in the program that calls divgs. ndb must be at\nc            least nlat.\nc\nc\nc     wshsgs an array which must be intialized by subroutine shsgsi.\nc            once initialized,\nc            wshsgs can be used repeatedly by divgs as long as nlon\nc            and nlat remain unchanged.  wshsgs must not be altered\nc            between calls of divgs.\nc\nc\nc     lshsgs the dimension of the array wshsgs as it appears in the\nc            program that calls divgs. define\nc\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lshsgs must be at least\nc\nc               nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\nc\nc\nc     work   a work array that does not have to be saved.\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls divgs. define\nc\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2                    if nlat is even or\nc               l2 = (nlat+1)/2                if nlat is odd\nc\nc            if isym = 0 then lwork must be at least\nc\nc               nlat*((nt+1)*nlon+2*nt*l1+1)\nc\nc            if isym > 0 then lwork must be at least\nc\nc               (nt+1)*l2*nlon+nlat*(2*nt*l1+1)\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc\nc    divg   a two or three dimensional array (see input parameter nt)\nc           that contains the divergence of the vector field (v,w)\nc           whose coefficients br,bi where computed by subroutine\nc           vhags.  divg(i,j) is the divergence at the gaussian colatitude\nc           point theta(i) and longitude point lambda(j) = (j-1)*2*pi/nlon.\nc           the index ranges are defined above at the input parameter\nc           isym.\nc\nc\nc    ierror = 0  no errors\nc           = 1  error in the specification of nlat\nc           = 2  error in the specification of nlon\nc           = 3  error in the specification of isym\nc           = 4  error in the specification of nt\nc           = 5  error in the specification of idiv\nc           = 6  error in the specification of jdiv\nc           = 7  error in the specification of mdb\nc           = 8  error in the specification of ndb\nc           = 9  error in the specification of lshsgs\nc           = 10 error in the specification of lwork\nc **********************************************************************\nc                                                                              \nc   \n      subroutine divgs(nstrg,nstrb,noffsg,noffsb,\n     +                 nlat,nlon,isym,nt,divg,idiv,jdiv,br,bi,mdb,ndb,\n     +                 wshsgs,lshsgs,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension divg(nstrg,idiv,jdiv,nt),\n     +          br(nstrb,mdb,ndb,nt),bi(nstrb,mdb,ndb,nt)\n      dimension wshsgs(lshsgs),work(lwork)\nc\nc     check input parameters\nc\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 4) return\n      ierror = 3\nc     if (isym.lt.0 .or. isym.gt.2) return\nc     see note above about symmeries\n      if(isym.ne.0) return\n      ierror = 4\n      if(nt .lt. 0) return\n      ierror = 5\n      imid = (nlat+1)/2\n      if((isym.eq.0 .and. idiv.lt.nlat) .or.\n     1   (isym.gt.0 .and. idiv.lt.imid)) return\n      ierror = 6\n      if(jdiv .lt. nlon) return\n      ierror = 7\n      if(mdb .lt. min0(nlat,(nlon+1)/2)) return\n      mmax = min0(nlat,(nlon+2)/2)\n      ierror = 8\n      if(ndb .lt. nlat) return\n      ierror = 9\n      imid = (nlat+1)/2\n      lpimn = (imid*mmax*(nlat+nlat-mmax+1))/2\nc     check permanent work space length\n      l2 = (nlat+1)/2\n      l1 = min0((nlon+2)/2,nlat)\n      lp=nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\n      if(lshsgs.lt.lp) return\n      ierror = 10\nc\nc     verify unsaved work space (add to what shses requires, file f3)\nc\n      ls = nlat\n      if(isym .gt. 0) ls = imid\n      nln = nt*ls*nlon\nc\nc     set first dimension for a,b (as requried by shses)\nc\n      mab = min0(nlat,nlon/2+1)\n      mn = mab*nlat*nt\n      if(lwork.lt. nln+ls*nlon+2*mn+nlat) return\n      ierror = 11\n      if((noffsg.lt.0)  .or.  (noffsg.ge.nstrg)) return\n      if((noffsb.lt.0)  .or.  (noffsb.ge.nstrb)) return\n      ierror = 0\nc\nc     set work space pointers\nc\n      ia = 1\n      ib = ia+mn\n      is = ib+mn\n      iwk = is+nlat\n      lwk = lwork-2*mn-nlat\n      call divgs1(nstrg,nstrb,noffsg,noffsb,\n     +            nlat,nlon,isym,nt,divg,idiv,jdiv,br,bi,mdb,ndb,\n     +work(ia),work(ib),mab,work(is),wshsgs,lshsgs,work(iwk),lwk,\n     +ierror)\n      return\n      end\n\n      subroutine divgs1(nstrg,nstrb,noffsg,noffsb,\n     +                  nlat,nlon,isym,nt,divg,idiv,jdiv,br,bi,mdb,ndb,\n     +                  a,b,mab,sqnn,wshsgs,lshsgs,wk,lwk,ierror)\n      implicit double precision (a-h,o-z)\n      dimension divg(nstrg,idiv,jdiv,nt),\n     +    br(nstrb,mdb,ndb,nt),bi(nstrb,mdb,ndb,nt)\n      dimension a(mab,nlat,nt),b(mab,nlat,nt),sqnn(nlat)\n      dimension wshsgs(lshsgs),wk(lwk)\n      ncb = 1+noffsb\nc\nc     set coefficient multiplyers\nc\n      do 1 n=2,nlat\n      fn = dfloat(n-1)\n      sqnn(n) = dsqrt(fn*(fn+1.))\n    1 continue\nc\nc     compute divergence scalar coefficients for each vector field\nc\n      do 2 k=1,nt\n      do 3 n=1,nlat\n      do 4 m=1,mab\n      a(m,n,k) = 0.0\n      b(m,n,k) = 0.0\n    4 continue\n    3 continue\nc\nc     compute m=0 coefficients\nc\n      do 5 n=2,nlat\n      a(1,n,k) = -sqnn(n)*br(ncb,1,n,k)\n      b(1,n,k) = -sqnn(n)*bi(ncb,1,n,k)\n    5 continue\nc\nc     compute m>0 coefficients using vector spherepack value for mmax\nc\n      mmax = min0(nlat,(nlon+1)/2)\n      do 6 m=2,mmax\n      do 7 n=m,nlat\n      a(m,n,k) = -sqnn(n)*br(ncb,m,n,k)\n      b(m,n,k) = -sqnn(n)*bi(ncb,m,n,k)\n    7 continue\n    6 continue\n    2 continue\nc\nc     synthesize a,b into divg\nc\n      call shsgs(nstrg,1,noffsg,0,nlat,nlon,isym,nt,divg,idiv,jdiv,a,b,\n     +           mab,nlat,mab,nlat,wshsgs,lshsgs,wk,lwk,ierror)\n      return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/gaqd.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc     This version of gaqd implements the method presented in:\nc     P. N. swarztrauber, Computing the points and weights for \nc     Gauss-Legendre quadrature, SIAM J. Sci. Comput.,\nc     24(2002) pp. 945-954.\nc     \nc     The w and lwork arrays are dummy and included only to\nc     permit a simple pluggable exchange with the\nc     old gaqd in previous versions of SPHEREPACK\nc\nc\nc\n      subroutine gaqd(nlat,theta,wts,w,lwork,ierror)\n      implicit double precision (a-h,o-z)\nc  . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\nc\nc     gauss points and weights are computed using the fourier-newton\nc     described in \"on computing the points and weights for \nc     gauss-legendre quadrature\", paul n. swarztrauber, siam journal \nc     on scientific computing that has been accepted for publication.\nc     This routine is faster and more accurate than older program \nc     with the same name.\nc\nc     subroutine gaqd computes the nlat gaussian colatitudes and weights\nc     in double precision. the colatitudes are in radians and lie in the\nc     in the interval (0,pi).\nc\nc     input parameters\nc\nc     nlat    the number of gaussian colatitudes in the interval (0,pi)\nc             (between the two poles).  nlat must be greater than zero.\nc\nc     w       unused double precision variable that permits a simple \nc             exchange with the old routine with the same name \nc             in spherepack.\nc\nc     lwork   unused variable that permits a simple exchange with the\nc             old routine with the same name in spherepack.\nc\nc     output parameters\nc\nc     theta   a double precision array with length nlat\nc             containing the gaussian colatitudes in\nc             increasing radians on the interval (0,pi).\nc\nc     wts     a double precision array with lenght nlat\nc             containing the gaussian weights.\nc\nc     ierror = 0 no errors\nc            = 1 if nlat.le.0\nc\nc  *****************************************************************\nc\n      double precision theta(nlat),wts(nlat),w,\n     1 x,pi,pis2,dtheta,dthalf,cmax,zprev,zlast,zero,\n     2 zhold,pb,dpb,dcor,sum,cz\nc\nc     check work space length\nc\n      ierror = 1\n      if (nlat.le.0) return\n      ierror = 0\nc\nc     compute weights and points analytically when nlat=1,2\nc\n      if (nlat.eq.1) then\n      theta(1) = dacos(0.0d0)\n      wts(1) = 2.0d0\n      return\n      end if\n      if (nlat.eq.2) then\n      x = dsqrt(1.0d0/3.0d0)\n      theta(1) = dacos(x)\n      theta(2) = dacos(-x)\n      wts(1) = 1.0d0\n      wts(2) = 1.0d0\n      return\n      end if\n      eps = dsqrt(dzeps(1.0d0))\n      eps = eps*dsqrt(eps)\n      pis2 = 2.0d0*datan(1.0d0)\n      pi = pis2+pis2 \n      mnlat = mod(nlat,2)\n      ns2 = nlat/2\n      nhalf = (nlat+1)/2\n      idx = ns2+2\nc\n      call cpdp (nlat,cz,theta(ns2+1),wts(ns2+1))\nc\n      dtheta = pis2/nhalf\n      dthalf = dtheta/2.0d0\n      cmax = .2d0*dtheta\nc\nc     estimate first point next to theta = pi/2\nc\n      if(mnlat.ne.0) then\n      zero = pis2-dtheta\n      zprev = pis2\n      nix = nhalf-1\n      else\n      zero = pis2-dthalf\n      nix = nhalf\n      end if\n 9    it = 0\n 10   it = it+1\n      zlast = zero\nc\nc     newton iterations\nc\n      call tpdp (nlat,zero,cz,theta(ns2+1),wts(ns2+1),pb,dpb)\n      dcor = pb/dpb\n      sgnd = 1.0d0\n      if(dcor .ne. 0.0d0) sgnd = dcor/dabs(dcor)\n      dcor = sgnd*min(dabs(dcor),cmax)\n      zero = zero-dcor\n      if(dabs(zero-zlast).gt.eps*dabs(zero)) go to 10\n      theta(nix) = zero\n      zhold = zero\nc      wts(nix) = (nlat+nlat+1)/(dpb*dpb)\nc    \nc     yakimiw's formula permits using old pb and dpb\nc\n      wts(nix) = (nlat+nlat+1)/(dpb+pb*dcos(zlast)/dsin(zlast))**2\n      nix = nix-1\n      if(nix.eq.0) go to 30\n      if(nix.eq.nhalf-1)  zero = 3.0d0*zero-pi\n      if(nix.lt.nhalf-1)  zero = zero+zero-zprev\n      zprev = zhold\n      go to 9\nc\nc     extend points and weights via symmetries\nc\n 30   if(mnlat.ne.0) then\n      theta(nhalf) = pis2\n      call tpdp (nlat,pis2,cz,theta(ns2+1),wts(ns2+1),pb,dpb)\n      wts(nhalf) = (nlat+nlat+1)/(dpb*dpb)\n      end if\n      do i=1,ns2\n      wts(nlat-i+1) = wts(i)\n      theta(nlat-i+1) = pi-theta(i)\n      end do\n      sum = 0.0d0\n      do i=1,nlat\n      sum = sum+wts(i)\n      end do\n      do i=1,nlat\n      wts(i) = 2.0d0*wts(i)/sum\n      end do\n      return\n      end\n      subroutine cpdp(n,cz,cp,dcp)\n      implicit double precision (a-h,o-z)\nc\nc     computes the fourier coefficients of the legendre\nc     polynomial p_n^0 and its derivative. \nc     n is the degree and n/2 or (n+1)/2\nc     coefficients are returned in cp depending on whether\nc     n is even or odd. The same number of coefficients\nc     are returned in dcp. For n even the constant \nc     coefficient is returned in cz. \nc\n      double precision cp(n/2+1),dcp(n/2+1),\n     1 t1,t2,t3,t4,cz\n      ncp = (n+1)/2\n      t1 = -1.0d0\n      t2 = n+1.0d0\n      t3 = 0.0d0\n      t4 = n+n+1.0d0\n      if(mod(n,2).eq.0) then\n      cp(ncp) = 1.0d0\n      do j = ncp,2,-1\n      t1 = t1+2.0d0\n      t2 = t2-1.0d0\n      t3 = t3+1.0d0\n      t4 = t4-2.0d0\n      cp(j-1) = (t1*t2)/(t3*t4)*cp(j)\n      end do\n      t1 = t1+2.0d0\n      t2 = t2-1.0d0\n      t3 = t3+1.0d0\n      t4 = t4-2.0d0\n      cz = (t1*t2)/(t3*t4)*cp(1)\n      do j=1,ncp\n      dcp(j) = (j+j)*cp(j)\n      end do\n      else\n      cp(ncp) = 1.0d0\n      do j = ncp-1,1,-1\n      t1 = t1+2.0d0\n      t2 = t2-1.0d0\n      t3 = t3+1.0d0\n      t4 = t4-2.0d0\n      cp(j) = (t1*t2)/(t3*t4)*cp(j+1)\n      end do\n      do j=1,ncp\n      dcp(j) = (j+j-1)*cp(j)\n      end do\n      end if\n      return\n      end\n      subroutine tpdp (n,theta,cz,cp,dcp,pb,dpb)\n      implicit double precision (a-h,o-z)\nc\nc     computes pn(theta) and its derivative dpb(theta) with \nc     respect to theta\nc\n      double precision cp(n/2+1),dcp(n/2+1),cz,\n     1  pb,dpb,fn,theta,cdt,sdt,cth,sth,chh\nc\n      fn = n\n      cdt = dcos(theta+theta)\n      sdt = dsin(theta+theta)\n      if(mod(n,2) .eq.0) then\nc\nc     n even\nc\n      kdo = n/2\n      pb = .5d0*cz\n      dpb = 0.0d0\n      if(n .gt. 0) then\n      cth = cdt\n      sth = sdt\n      do 170 k=1,kdo\nc      pb = pb+cp(k)*cos(2*k*theta)\n      pb = pb+cp(k)*cth\nc      dpb = dpb-(k+k)*cp(k)*sin(2*k*theta)\n      dpb = dpb-dcp(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  170 continue\n      end if\n      else\nc\nc     n odd\nc\n      kdo = (n+1)/2\n      pb = 0.0d0\n      dpb = 0.0d0\n      cth = dcos(theta)\n      sth = dsin(theta)\n      do 190 k=1,kdo\nc      pb = pb+cp(k)*cos((2*k-1)*theta)\n      pb = pb+cp(k)*cth\nc      dpb = dpb-(k+k-1)*cp(k)*sin((2*k-1)*theta)\n      dpb = dpb-dcp(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  190 continue\n      end if\n      return\n      end\n      double precision function dzeps (x)\n      implicit double precision (a-h,o-z)\n      double precision  x\nc\nc     estimate unit roundoff in quantities of size x.\nc\n      double precision a,b,c,eps\nc\nc     this program should function properly on all systems\nc     satisfying the following two assumptions,\nc        1.  the base used in representing floating point\nc            numbers is not a power of three.\nc        2.  the quantity  a  in statement 10 is represented to \nc            the accuracy used in floating point variables\nc            that are stored in memory.\nc     the statement number 10 and the go to 10 are intended to\nc     force optimizing compilers to generate code satisfying \nc     assumption 2.\nc     under these assumptions, it should be true that,\nc            a  is not exactly equal to four-thirds,\nc            b  has a zero for its last bit or digit,\nc            c  is not exactly equal to one,\nc            eps  measures the separation of 1.0 from\nc                 the next larger floating point number.\nc     the developers of eispack would appreciate being informed\nc     about any systems where these assumptions do not hold.\nc\nc     this version dated 4/6/83.\nc\n      a = 4.0d0/3.0d0\n   10 b = a - 1.0d0\n      c = b + b + b\n      eps = abs(c-1.0d0)\n      if (eps .eq. 0.0d0) go to 10\n      dzeps = eps*dabs(x)\n      return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/gradgs.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc ... file gradgs.f\nc\nc     this file includes documentation and code for\nc     subroutine gradgs         i\nc\nc ... files which must be loaded with gradgec.f\nc\nc     sphcom.f, hrfft.f, shags.f,vhsgs.f\nc\nc     subroutine gradgs(nstrvw,nstrab,noffsvw,noffsab,\nc                       nlat,nlon,isym,nt,v,w,idvw,jdvw,a,b,mdab,ndab,\nc    +                  wvhsgs,lvhsgs,work,lwork,ierror)\nc\nc     given the scalar spherical harmonic coefficients a and b, precomputed\nc     by subroutine shags for a scalar field sf, subroutine gradgs computes\nc     an irrotational vector field (v,w) such that\nc\nc           gradient(sf) = (v,w).\nc\nc     v is the colatitudinal and w is the east longitudinal component\nc     of the gradient.  i.e.,\nc\nc            v(i,j) = d(sf(i,j))/dtheta\nc\nc     and\nc\nc            w(i,j) = 1/sint*d(sf(i,j))/dlambda\nc\nc     at the gaussian colatitude point theta(i) (see nlat as input\nc     parameter) and longitude lambda(j) = (j-1)*2*pi/nlon where\nc     sint = sin(theta(i)).\nc\nc\nc     input parameters\nc\nc     nstrvw, nstrab   strides of v,w and a,b\nc     noffsvw, noffsab   offset of v,w and a,b -- must be between 0 and stride-1\nc            Alternatively, when the two strides equal, you can set both\nc            to -1 in which case the output will be computed for all\nc            values of offset between 0 and stride-1.\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are computed\nc            in radians in theta(1) <...< theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid point\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater than\nc            3.  the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for isym==0.  For this reason an error code will\nc           be returned if isym!=0.\nc\nc\nc     isym   this has the same value as the isym that was input to\nc            subroutine shags to compute the arrays a and b from the\nc            scalar field sf.  isym determines whether (v,w) are\nc            computed on the full or half sphere as follows:\nc\nc      = 0\nc\nc           sf is not symmetric about the equator. in this case\nc           the vector field (v,w) is computed on the entire sphere.\nc           i.e., in the arrays  v(i,j),w(i,j) for i=1,...,nlat and\nc           j=1,...,nlon.\nc\nc      = 1\nc\nc           sf is antisymmetric about the equator. in this case w is\nc           antisymmetric and v is symmetric about the equator. w\nc           and v are computed on the northern hemisphere only.  i.e.,\nc           if nlat is odd they are computed for i=1,...,(nlat+1)/2\nc           and j=1,...,nlon.  if nlat is even they are computed for\nc           i=1,...,nlat/2 and j=1,...,nlon.\nc\nc      = 2\nc\nc           sf is symmetric about the equator. in this case w is\nc           symmetric and v is antisymmetric about the equator. w\nc           and v are computed on the northern hemisphere only.  i.e.,\nc           if nlat is odd they are computed for i=1,...,(nlat+1)/2\nc           and j=1,...,nlon.  if nlat is even they are computed for\nc           i=1,...,nlat/2 and j=1,...,nlon.\nc\nc\nc     nt     nt is the number of scalar and vector fields.  some\nc            computational efficiency is obtained for multiple fields.\nc            the arrays a,b,v, and w can be three dimensional corresponding\nc            to an indexed multiple array sf.  in this case, multiple\nc            vector synthesis will be performed to compute each vector\nc            field.  the third index for a,b,v, and w is the synthesis\nc            index which assumes the values k = 1,...,nt.  for a single\nc            synthesis set nt = 1.  the description of the remaining\nc            parameters is simplified by assuming that nt=1 or that a,b,v,\nc            and w are two dimensional arrays.\nc\nc     idvw   the first dimension of the arrays v,w as it appears in\nc            the program that calls gradgs. if isym = 0 then idvw\nc            must be at least nlat.  if isym = 1 or 2 and nlat is\nc            even then idvw must be at least nlat/2. if isym = 1 or 2\nc            and nlat is odd then idvw must be at least (nlat+1)/2.\nc\nc     jdvw   the second dimension of the arrays v,w as it appears in\nc            the program that calls gradgs. jdvw must be at least nlon.\nc\nc     a,b    two or three dimensional arrays (see input parameter nt)\nc            that contain scalar spherical harmonic coefficients\nc            of the scalar field array sf as computed by subroutine shags.\nc     ***    a,b must be computed by shags prior to calling gradgs.\nc\nc     mdab   the first dimension of the arrays a and b as it appears in\nc            the program that calls gradgs (and shags). mdab must be at\nc            least min0(nlat,(nlon+2)/2) if nlon is even or at least\nc            min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc     ndab   the second dimension of the arrays a and b as it appears in\nc            the program that calls gradgs (and shags). ndab must be at\nc            least nlat.\nc\nc\nc     wvhsgs an array which must be initialized by subroutine vhsgsi.\nc            once initialized,\nc            wvhsgs can be used repeatedly by gradgs as long as nlon\nc            and nlat remain unchanged.  wvhsgs must not be altered\nc            between calls of gradgs.\nc\nc\nc     lvhsgs the dimension of the array wvhsgs as it appears in the\nc            program that calls grradgs.  define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lvhsgs must be at least\nc\nc                 l1*l2*(nlat+nlat-l1+1)+nlon+15+2*nlat\nc\nc\nc     work   a work array that does not have to be saved.\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls gradgs. define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2                  if nlat is even or\nc               l2 = (nlat+1)/2              if nlat is odd\nc\nc            if isym = 0, lwork must be greater than or equal to\nc\nc               nlat*((2*nt+1)*nlon+2*l1*nt+1).\nc\nc            if isym = 1 or 2, lwork must be greater than or equal to\nc\nc               (2*nt+1)*l2*nlon+nlat*(2*l1*nt+1).\nc\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc\nc     v,w   two or three dimensional arrays (see input parameter nt) that\nc           contain an irrotational vector field such that the gradient of\nc           the scalar field sf is (v,w).  w(i,j) is the east longitude\nc           component and v(i,j) is the colatitudinal component of velocity\nc           at gaussian colatitude and longitude lambda(j) = (j-1)*2*pi/nlon\nc           the indices for v and w are defined at the input parameter\nc           isym.  the vorticity of (v,w) is zero.  note that any nonzero\nc           vector field on the sphere will be multiple valued at the poles\nc           [reference swarztrauber].\nc\nc\nc  ierror   = 0  no errors\nc           = 1  error in the specification of nlat\nc           = 2  error in the specification of nlon\nc           = 3  error in the specification of isym\nc           = 4  error in the specification of nt\nc           = 5  error in the specification of idvw\nc           = 6  error in the specification of jdvw\nc           = 7  error in the specification of mdab\nc           = 8  error in the specification of ndab\nc           = 9  error in the specification of lvhsgs\nc           = 10 error in the specification of lwork\nc **********************************************************************\nc                                                                              \nc   \n      subroutine gradgs(nstrvw,nstrab,noffsvw,noffsab,\n     +nlat,nlon,isym,nt,v,w,idvw,jdvw,a,b,mdab,ndab,\n     +wvhsgs,lvhsgs,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension v(nstrvw,idvw,jdvw,nt),w(nstrvw,idvw,jdvw,nt)\n      dimension a(nstrab,mdab,ndab,nt),b(nstrab,mdab,ndab,nt)\n      dimension wvhsgs(lvhsgs),work(lwork)\nc\nc     check input parameters\nc\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 4) return\n      ierror = 3\n      if(isym.lt.0 .or. isym.gt.2) return\nc     see note above about symmeries\n      if(isym.ne.0) return\n      ierror = 4\n      if(nt .lt. 0) return\n      ierror = 5\n      imid = (nlat+1)/2\n      if((isym.eq.0 .and. idvw.lt.nlat) .or.\n     +   (isym.ne.0 .and. idvw.lt.imid)) return\n      ierror = 6\n      if(jdvw .lt. nlon) return\n      ierror = 7\n      mmax = min0(nlat,(nlon+1)/2)\n      if(mdab .lt. min0(nlat,(nlon+2)/2)) return\n      ierror = 8\n      if(ndab .lt. nlat) return\n      ierror = 9\nc\nc     verify minimum saved work space length\nc\n      idz = (mmax*(nlat+nlat-mmax+1))/2\n      lzimn = idz*imid\n      lgdmin = lzimn+lzimn+nlon+15\n      if(lvhsgs .lt. lgdmin) return\n      ierror = 10\nc\nc     verify minimum unsaved work space length\nc\n      ng = 1;\n      if(noffsvw.eq.-1 .and. noffsab.eq.-1) ng=nstrvw\n      mn = mmax*nlat*nt*ng\n      idv = nlat\n      if (isym.ne.0) idv = imid\n      lnl = nt*idv*nlon\n      lwkmin =  lnl+lnl+idv*nlon+2*mn+nlat\n      if(lwork .lt. lwkmin) return\n      ierror = 11\n      if((noffsvw.eq.-1) .neqv. (noffsab.eq.-1)) return\n      if((noffsvw.eq.-1) .and.  (nstrvw.ne.nstrab)) return\n      if((noffsvw.lt.-1) .or.  (noffsvw.ge.nstrvw)) return\n      if((noffsab.lt.-1) .or.  (noffsab.ge.nstrab)) return\n      ierror = 0\nc\nc     set work space pointers\nc\n      ibr = 1\n      ibi = ibr + mn\n      is = ibi + mn\n      iwk = is + nlat\n      liwk = lwork-2*mn-nlat\n\n      call gradgs1(nstrvw,nstrab,noffsvw,noffsab,\n     +nlat,nlon,isym,nt,v,w,idvw,jdvw,work(ibr),work(ibi),ng,\n     +mmax,work(is),mdab,ndab,a,b,wvhsgs,lvhsgs,work(iwk),liwk,\n     +ierror)\n      return\n      end\n\n      subroutine gradgs1(nstrvw,nstrab,noffsvw,noffsab,\n     +nlat,nlon,isym,nt,v,w,idvw,jdvw,br,bi,ng,mmax,\n     +sqnn,mdab,ndab,a,b,wvhsgs,lvhsgs,wk,lwk,ierror)\n      implicit double precision (a-h,o-z)\n      dimension v(nstrvw,idvw,jdvw,nt),w(nstrvw,idvw,jdvw,nt)\n      dimension br(ng,mmax,nlat,nt),bi(ng,mmax,nlat,nt),sqnn(nlat)\n      dimension a(nstrab,mdab,ndab,nt),b(nstrab,mdab,ndab,nt)\n      dimension wvhsgs(lvhsgs),wk(lwk)\n      if(noffsab.eq.-1) then\n        ncabmin=1\n        ncabmax=nstrab\n      else\n        ncabmin=1+noffsab\n        ncabmax=1+noffsab\n      end if\n\n\nc\nc     preset coefficient multiplyers in vector\nc\n      do 1 n=2,nlat\n      fn = dfloat(n-1)\n      sqnn(n) = dsqrt(fn*(fn+1.d0))\n    1 continue\nc\nc     compute multiple vector fields coefficients\nc\n      do 2 k=1,nt\nc\nc     preset br,bi to 0.0\nc\n      do 3 n=1,nlat\n      do 4 m=1,mmax\n      do 4 ig=1,ng\n      br(ig,m,n,k) = 0.0d0\n      bi(ig,m,n,k) = 0.0d0\n    4 continue\n    3 continue\nc\nc     compute m=0 coefficients\nc\n      do 5 n=2,nlat\n      do 5 ncab=ncabmin,ncabmax\n      ig = ncab-ncabmin+1\n      br(ig,1,n,k) = sqnn(n)*a(ncab,1,n,k)\n      bi(ig,1,n,k) = sqnn(n)*b(ncab,1,n,k)\n    5 continue\nc\nc     compute m>0 coefficients\nc\n      do 6 m=2,mmax\n      do 7 n=m,nlat\n      do 7 ncab=ncabmin,ncabmax\n      ig = ncab-ncabmin+1\n      br(ig,m,n,k) = sqnn(n)*a(ncab,m,n,k)\n      bi(ig,m,n,k) = sqnn(n)*b(ncab,m,n,k)\n    7 continue\n    6 continue\n    2 continue\nc\nc     set ityp for irrotational vector synthesis to compute gradient\nc\n      if (isym.eq.0) then\n      ityp = 1\n      else if (isym.eq.1) then\n      ityp = 4\n      else if (isym.eq.2) then\n      ityp = 7\n      end if\nc\nc     vector sythesize br,bi into (v,w) (cr,ci are dummy variables)\nc\n      call vhsgs(nstrvw,ng,noffsvw,noffsab,\n     +           nlat,nlon,ityp,nt,v,w,idvw,jdvw,br,bi,cr,ci,\n     +           mmax,nlat,wvhsgs,lvhsgs,wk,lwk,ierror)\n  200 continue\n      return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/hrfft.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc ... file hrfft.f\nc\nc     this file contains a multiple fft package for spherepack. it\nc     includes code and documentation for performing fast fourier\nc     transforms (see subroutines hrffti,hrfftf and hrfftb)\nc\nc **********************************************************************\nc\nc     subroutine hrffti(n,wsave)\nc\nc     subroutine hrffti initializes the array wsave which is used in\nc     both hrfftf and hrfftb. the prime factorization of n together \nc     with a tabulation of the trigonometric functions are computed and\nc     stored in wsave.\nc\nc     input parameter\nc\nc     n       the length of the sequence to be transformed.\nc\nc     output parameter\nc\nc     wsave   a work array which must be dimensioned at least 2*n+15.\nc             the same work array can be used for both hrfftf and \nc             hrfftb as long as n remains unchanged. different wsave \nc             arrays are required for different values of n. the \nc             contents of wsave must not be changed between calls \nc             of hrfftf or hrfftb.\nc\nc **********************************************************************\nc\nc     subroutine hrfftf(m,n,r,mdimr,wsave,work)\nc\nc     subroutine hrfftf computes the fourier coefficients of m real\nc     perodic sequences (fourier analysis); i.e. hrfftf computes the\nc     real fft of m sequences each with length n. the transform is \nc     defined below at output parameter r.\nc\nc     input parameters\nc\nc     m       the number of sequences.\nc\nc     n       the length of all m sequences.  the method is most\nc             efficient when n is a product of small primes. n may\nc             change as long as different work arrays are provided\nc\nc     r       r(m,n) is a two dimensional real array that contains m\nc             sequences each with length n.\nc\nc     mdimr   the first dimension of the r array as it appears\nc             in the program that calls hrfftf. mdimr must be\nc             greater than or equal to m.\nc\nc\nc     wsave   a work array with at least least 2*n+15 locations\nc             in the program that calls hrfftf. the wsave array must be\nc             initialized by calling subroutine hrffti(n,wsave) and a\nc             different wsave array must be used for each different\nc             value of n. this initialization does not have to be\nc             repeated so long as n remains unchanged thus subsequent\nc             transforms can be obtained faster than the first.\nc             the same wsave array can be used by hrfftf and hrfftb.\nc\nc     work    a real work array with m*n locations.\nc\nc\nc     output parameters\nc\nc     r      for all j=1,...,m\nc  \nc             r(j,1) = the sum from i=1 to i=n of r(j,i)\nc\nc             if n is even set l =n/2   , if n is odd set l = (n+1)/2\nc\nc               then for k = 2,...,l\nc\nc                  r(j,2*k-2) = the sum from i = 1 to i = n of\nc\nc                       r(j,i)*cos((k-1)*(i-1)*2*pi/n)\nc\nc                  r(j,2*k-1) = the sum from i = 1 to i = n of\nc\nc                      -r(j,i)*sin((k-1)*(i-1)*2*pi/n)\nc\nc             if n is even\nc\nc                  r(j,n) = the sum from i = 1 to i = n of\nc\nc                       (-1)**(i-1)*r(j,i)\nc\nc      *****  note\nc                  this transform is unnormalized since a call of hrfftf\nc                  followed by a call of hrfftb will multiply the input\nc                  sequence by n.\nc\nc     wsave   contains results which must not be destroyed between\nc             calls of hrfftf or hrfftb.\nc\nc     work    a real work array with m*n locations that does\nc             not have to be saved.\nc\nc **********************************************************************\nc\nc     subroutine hrfftb(m,n,r,mdimr,wsave,work)\nc\nc     subroutine hrfftb computes the real perodic sequence of m\nc     sequences from their fourier coefficients (fourier synthesis). \nc     the transform is defined below at output parameter r.\nc\nc     input parameters\nc\nc     m       the number of sequences.\nc\nc     n       the length of all m sequences.  the method is most\nc             efficient when n is a product of small primes. n may\nc             change as long as different work arrays are provided\nc\nc     r       r(m,n) is a two dimensional real array that contains\nc             the fourier coefficients of m sequences each with \nc             length n.\nc\nc     mdimr   the first dimension of the r array as it appears\nc             in the program that calls hrfftb. mdimr must be\nc             greater than or equal to m.\nc\nc     wsave   a work array which must be dimensioned at least 2*n+15.\nc             in the program that calls hrfftb. the wsave array must be\nc             initialized by calling subroutine hrffti(n,wsave) and a\nc             different wsave array must be used for each different\nc             value of n. this initialization does not have to be\nc             repeated so long as n remains unchanged thus subsequent\nc             transforms can be obtained faster than the first.\nc             the same wsave array can be used by hrfftf and hrfftb.\nc\nc     work    a real work array with m*n locations.\nc\nc\nc     output parameters\nc\nc     r      for all j=1,...,m\nc  \nc             for n even and for i = 1,...,n\nc\nc                  r(j,i) = r(j,1)+(-1)**(i-1)*r(j,n)\nc\nc                       plus the sum from k=2 to k=n/2 of\nc\nc                        2.*r(j,2*k-2)*cos((k-1)*(i-1)*2*pi/n)\nc\nc                       -2.*r(j,2*k-1)*sin((k-1)*(i-1)*2*pi/n)\nc\nc             for n odd and for i = 1,...,n\nc\nc                  r(j,i) = r(j,1) plus the sum from k=2 to k=(n+1)/2 of\nc\nc                       2.*r(j,2*k-2)*cos((k-1)*(i-1)*2*pi/n)\nc\nc                      -2.*r(j,2*k-1)*sin((k-1)*(i-1)*2*pi/n)\nc\nc      *****  note\nc                  this transform is unnormalized since a call of hrfftf\nc                  followed by a call of hrfftb will multiply the input\nc                  sequence by n.\nc\nc     wsave   contains results which must not be destroyed between\nc             calls of hrfftb or hrfftf.\nc\nc     work    a real work array with m*n locations that does not\nc             have to be saved\nc\nc **********************************************************************\nc\nc\nc\n      subroutine hrffti (n,wsave)\n      implicit double precision (a-h,o-z)\n      dimension       wsave(n+15)                                              \n      common /hrf/ tfft\n      tfft = 0.d0\n      if (n .eq. 1) return                                                     \n      call hrfti1 (n,wsave(1),wsave(n+1))\n      return                                                                   \n      end                                                                      \n      subroutine hrfti1 (n,wa,fac)\n      implicit double precision (a-h,o-z)\nc                                                                              \nc     a multiple fft package for spherepack\nc                                                                              \n      dimension       wa(n)      ,fac(15)    ,ntryh(4)                         \n      double precision tpi,argh,argld,arg\n      data ntryh(1),ntryh(2),ntryh(3),ntryh(4)/4,2,3,5/                        \n      nl = n                                                                   \n      nf = 0           \n      j = 0            \n  101 j = j+1          \n      if (j-4) 102,102,103                \n  102 ntry = ntryh(j)                     \n      go to 104        \n  103 ntry = ntry+2    \n  104 nq = nl/ntry     \n      nr = nl-ntry*nq                     \n      if (nr) 101,105,101                 \n  105 nf = nf+1        \n      fac(nf+2) = ntry                    \n      nl = nq          \n      if (ntry .ne. 2) go to 107          \n      if (nf .eq. 1) go to 107            \n      do 106 i=2,nf    \n         ib = nf-i+2   \n         fac(ib+2) = fac(ib+1)            \n  106 continue         \n      fac(3) = 2       \n  107 if (nl .ne. 1) go to 104            \n      fac(1) = n       \n      fac(2) = nf      \n      tpi = 8.d0*datan(1.d0)\n      argh = tpi/dfloat(n)                 \n      is = 0           \n      nfm1 = nf-1      \n      l1 = 1           \n      if (nfm1 .eq. 0) return             \n      do 110 k1=1,nfm1                    \n         ip = fac(k1+2)                   \n         ld = 0        \n         l2 = l1*ip    \n         ido = n/l2    \n         ipm = ip-1    \n         do 109 j=1,ipm                   \n            ld = ld+l1                    \n            i = is     \n            argld = dfloat(ld)*argh        \n            fi = 0.d0    \n            do 108 ii=3,ido,2             \n               i = i+2                    \n               fi = fi+1.d0                 \n               arg = fi*argld             \n\t       wa(i-1) = dcos(arg)\n\t       wa(i) = dsin(arg)\n  108       continue   \n            is = is+ido                   \n  109    continue      \n         l1 = l2       \n  110 continue         \n      return           \n      end              \n      subroutine hrfftf (m,n,r,mdimr,whrfft,work)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension       r(mdimr,n)  ,work(1)    ,whrfft(n+15)\n      common /hrf/ tfft\n      if (n .eq. 1) return                \nc     tstart = second(dum)\n      call hrftf1 (m,n,r,mdimr,work,whrfft,whrfft(n+1))\nc     tfft = tfft+second(dum)-tstart\n      return           \n      end              \n      subroutine hrftf1 (m,n,c,mdimc,ch,wa,fac)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension       ch(m,n) ,c(mdimc,n)  ,wa(n)   ,fac(15)\n      nf = fac(2)      \n      na = 1           \n      l2 = n           \n      iw = n           \n      do 111 k1=1,nf   \n         kh = nf-k1    \n         ip = fac(kh+3)                   \n         l1 = l2/ip    \n         ido = n/l2    \n         idl1 = ido*l1                    \n         iw = iw-(ip-1)*ido               \n         na = 1-na     \n         if (ip .ne. 4) go to 102         \n         ix2 = iw+ido                     \n         ix3 = ix2+ido                    \n         if (na .ne. 0) go to 101         \n\t call hradf4 (m,ido,l1,c,mdimc,ch,m,wa(iw),wa(ix2),wa(ix3))\n         go to 110     \n  101    call hradf4 (m,ido,l1,ch,m,c,mdimc,wa(iw),wa(ix2),wa(ix3))\n         go to 110     \n  102    if (ip .ne. 2) go to 104         \n         if (na .ne. 0) go to 103         \n\t call hradf2 (m,ido,l1,c,mdimc,ch,m,wa(iw))\n         go to 110     \n  103    call hradf2 (m,ido,l1,ch,m,c,mdimc,wa(iw))\n         go to 110     \n  104    if (ip .ne. 3) go to 106         \n         ix2 = iw+ido                     \n         if (na .ne. 0) go to 105         \n\t call hradf3 (m,ido,l1,c,mdimc,ch,m,wa(iw),wa(ix2))\n         go to 110     \n  105    call hradf3 (m,ido,l1,ch,m,c,mdimc,wa(iw),wa(ix2))\n         go to 110     \n  106    if (ip .ne. 5) go to 108         \n         ix2 = iw+ido                     \n         ix3 = ix2+ido                    \n         ix4 = ix3+ido                    \n         if (na .ne. 0) go to 107         \n      call hradf5(m,ido,l1,c,mdimc,ch,m,wa(iw),wa(ix2),wa(ix3),wa(ix4))\n         go to 110     \n  107 call hradf5(m,ido,l1,ch,m,c,mdimc,wa(iw),wa(ix2),wa(ix3),wa(ix4))\n         go to 110     \n  108    if (ido .eq. 1) na = 1-na        \n         if (na .ne. 0) go to 109         \n\t call hradfg (m,ido,ip,l1,idl1,c,c,c,mdimc,ch,ch,m,wa(iw))\n         na = 1        \n         go to 110     \n  109    call hradfg (m,ido,ip,l1,idl1,ch,ch,ch,m,c,c,mdimc,wa(iw))\n         na = 0        \n  110    l2 = l1       \n  111 continue         \n      if (na .eq. 1) return\n      do 112 j=1,n     \n      do 112 i=1,m     \n\t c(i,j) = ch(i,j)\n  112 continue         \n      return           \n      end              \n      subroutine hradf4 (mp,ido,l1,cc,mdimcc,ch,mdimch,wa1,wa2,wa3)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension    cc(mdimcc,ido,l1,4)   ,ch(mdimch,ido,4,l1)     ,\n     1             wa1(ido)     ,wa2(ido)     ,wa3(ido)\n      hsqt2=dsqrt(2.d0)/2.d0                   \n      do 101 k=1,l1    \n         do 1001 m=1,mp                   \n         ch(m,1,1,k) = (cc(m,1,k,2)+cc(m,1,k,4))             \n     1      +(cc(m,1,k,1)+cc(m,1,k,3))    \n         ch(m,ido,4,k) = (cc(m,1,k,1)+cc(m,1,k,3))           \n     1      -(cc(m,1,k,2)+cc(m,1,k,4))    \n         ch(m,ido,2,k) = cc(m,1,k,1)-cc(m,1,k,3)             \n         ch(m,1,3,k) = cc(m,1,k,4)-cc(m,1,k,2)               \n 1001    continue      \n  101 continue         \n      if (ido-2) 107,105,102              \n  102 idp2 = ido+2     \n      do 104 k=1,l1    \n         do 103 i=3,ido,2                 \n            ic = idp2-i                   \n            do 1003 m=1,mp                \n            ch(m,i-1,1,k) = ((wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*                 \n     1       cc(m,i,k,2))+(wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*  \n     1       cc(m,i,k,4)))+(cc(m,i-1,k,1)+(wa2(i-2)*cc(m,i-1,k,3)+             \n     1       wa2(i-1)*cc(m,i,k,3)))       \n            ch(m,ic-1,4,k) = (cc(m,i-1,k,1)+(wa2(i-2)*cc(m,i-1,k,3)+           \n     1       wa2(i-1)*cc(m,i,k,3)))-((wa1(i-2)*cc(m,i-1,k,2)+                  \n     1       wa1(i-1)*cc(m,i,k,2))+(wa3(i-2)*cc(m,i-1,k,4)+  \n     1       wa3(i-1)*cc(m,i,k,4)))       \n            ch(m,i,1,k) = ((wa1(i-2)*cc(m,i,k,2)-wa1(i-1)*   \n     1       cc(m,i-1,k,2))+(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4)))+(cc(m,i,k,1)+(wa2(i-2)*cc(m,i,k,3)-               \n     1       wa2(i-1)*cc(m,i-1,k,3)))     \n            ch(m,ic,4,k) = ((wa1(i-2)*cc(m,i,k,2)-wa1(i-1)*  \n     1       cc(m,i-1,k,2))+(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4)))-(cc(m,i,k,1)+(wa2(i-2)*cc(m,i,k,3)-               \n     1       wa2(i-1)*cc(m,i-1,k,3)))     \n            ch(m,i-1,3,k) = ((wa1(i-2)*cc(m,i,k,2)-wa1(i-1)* \n     1       cc(m,i-1,k,2))-(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4)))+(cc(m,i-1,k,1)-(wa2(i-2)*cc(m,i-1,k,3)+           \n     1       wa2(i-1)*cc(m,i,k,3)))       \n            ch(m,ic-1,2,k) = (cc(m,i-1,k,1)-(wa2(i-2)*cc(m,i-1,k,3)+           \n     1       wa2(i-1)*cc(m,i,k,3)))-((wa1(i-2)*cc(m,i,k,2)-wa1(i-1)*           \n     1       cc(m,i-1,k,2))-(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4)))              \n            ch(m,i,3,k) = ((wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)* \n     1       cc(m,i,k,4))-(wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*  \n     1       cc(m,i,k,2)))+(cc(m,i,k,1)-(wa2(i-2)*cc(m,i,k,3)-                 \n     1       wa2(i-1)*cc(m,i-1,k,3)))     \n            ch(m,ic,2,k) = ((wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*                  \n     1       cc(m,i,k,4))-(wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*  \n     1       cc(m,i,k,2)))-(cc(m,i,k,1)-(wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*        \n     1       cc(m,i-1,k,3)))              \n 1003       continue   \n  103    continue      \n  104 continue         \n      if (mod(ido,2) .eq. 1) return       \n  105 continue         \n      do 106 k=1,l1    \n         do 1006 m=1,mp                   \n            ch(m,ido,1,k) = (hsqt2*(cc(m,ido,k,2)-cc(m,ido,k,4)))+              \n     1       cc(m,ido,k,1)                \n            ch(m,ido,3,k) = cc(m,ido,k,1)-(hsqt2*(cc(m,ido,k,2)-                \n     1       cc(m,ido,k,4)))              \n            ch(m,1,2,k) = (-hsqt2*(cc(m,ido,k,2)+cc(m,ido,k,4)))-               \n     1       cc(m,ido,k,3)                \n            ch(m,1,4,k) = (-hsqt2*(cc(m,ido,k,2)+cc(m,ido,k,4)))+               \n     1       cc(m,ido,k,3)                \n 1006    continue      \n  106 continue         \n  107 return           \n      end              \n      subroutine hradf2 (mp,ido,l1,cc,mdimcc,ch,mdimch,wa1)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension   ch(mdimch,ido,2,l1)  ,cc(mdimcc,ido,l1,2)     ,\n     1                wa1(ido)            \n      do 101 k=1,l1    \n         do 1001 m=1,mp                   \n         ch(m,1,1,k) = cc(m,1,k,1)+cc(m,1,k,2)               \n         ch(m,ido,2,k) = cc(m,1,k,1)-cc(m,1,k,2)             \n 1001    continue      \n  101 continue         \n      if (ido-2) 107,105,102              \n  102 idp2 = ido+2     \n      do 104 k=1,l1    \n         do 103 i=3,ido,2                 \n            ic = idp2-i                   \n            do 1003 m=1,mp                \n            ch(m,i,1,k) = cc(m,i,k,1)+(wa1(i-2)*cc(m,i,k,2)- \n     1       wa1(i-1)*cc(m,i-1,k,2))      \n            ch(m,ic,2,k) = (wa1(i-2)*cc(m,i,k,2)-wa1(i-1)*   \n     1       cc(m,i-1,k,2))-cc(m,i,k,1)   \n            ch(m,i-1,1,k) = cc(m,i-1,k,1)+(wa1(i-2)*cc(m,i-1,k,2)+              \n     1       wa1(i-1)*cc(m,i,k,2))        \n            ch(m,ic-1,2,k) = cc(m,i-1,k,1)-(wa1(i-2)*cc(m,i-1,k,2)+             \n     1       wa1(i-1)*cc(m,i,k,2))        \n 1003       continue   \n  103    continue      \n  104 continue         \n      if (mod(ido,2) .eq. 1) return       \n  105 do 106 k=1,l1    \n         do 1006 m=1,mp                   \n         ch(m,1,2,k) = -cc(m,ido,k,2)     \n         ch(m,ido,1,k) = cc(m,ido,k,1)    \n 1006    continue      \n  106 continue         \n  107 return           \n      end              \n      subroutine hradf3 (mp,ido,l1,cc,mdimcc,ch,mdimch,wa1,wa2)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension   ch(mdimch,ido,3,l1)  ,cc(mdimcc,ido,l1,3)     ,\n     1                wa1(ido)     ,wa2(ido)                 \n      arg=2.d0*pimach()/3.d0                  \n      taur=dcos(arg)    \n      taui=dsin(arg)    \n      do 101 k=1,l1    \n         do 1001 m=1,mp                   \n         ch(m,1,1,k) = cc(m,1,k,1)+(cc(m,1,k,2)+cc(m,1,k,3)) \n         ch(m,1,3,k) = taui*(cc(m,1,k,3)-cc(m,1,k,2))        \n         ch(m,ido,2,k) = cc(m,1,k,1)+taur*                   \n     1      (cc(m,1,k,2)+cc(m,1,k,3))     \n 1001    continue      \n  101 continue         \n      if (ido .eq. 1) return              \n      idp2 = ido+2     \n      do 103 k=1,l1    \n         do 102 i=3,ido,2                 \n            ic = idp2-i                   \n            do 1002 m=1,mp                \n            ch(m,i-1,1,k) = cc(m,i-1,k,1)+((wa1(i-2)*cc(m,i-1,k,2)+             \n     1       wa1(i-1)*cc(m,i,k,2))+(wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*            \n     1       cc(m,i,k,3)))                \n            ch(m,i,1,k) = cc(m,i,k,1)+((wa1(i-2)*cc(m,i,k,2)-wa1(i-1)*          \n     1       cc(m,i-1,k,2))+(wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*  \n     1       cc(m,i-1,k,3)))              \n            ch(m,i-1,3,k) = (cc(m,i-1,k,1)+taur*((wa1(i-2)*  \n     1       cc(m,i-1,k,2)+wa1(i-1)*cc(m,i,k,2))+(wa2(i-2)*  \n     1       cc(m,i-1,k,3)+wa2(i-1)*cc(m,i,k,3))))+(taui*((wa1(i-2)*            \n     1       cc(m,i,k,2)-wa1(i-1)*cc(m,i-1,k,2))-(wa2(i-2)*  \n     1       cc(m,i,k,3)-wa2(i-1)*cc(m,i-1,k,3))))           \n            ch(m,ic-1,2,k) = (cc(m,i-1,k,1)+taur*((wa1(i-2)* \n     1       cc(m,i-1,k,2)+wa1(i-1)*cc(m,i,k,2))+(wa2(i-2)*  \n     1       cc(m,i-1,k,3)+wa2(i-1)*cc(m,i,k,3))))-(taui*((wa1(i-2)*            \n     1       cc(m,i,k,2)-wa1(i-1)*cc(m,i-1,k,2))-(wa2(i-2)*  \n     1       cc(m,i,k,3)-wa2(i-1)*cc(m,i-1,k,3))))           \n            ch(m,i,3,k) = (cc(m,i,k,1)+taur*((wa1(i-2)*cc(m,i,k,2)-             \n     1       wa1(i-1)*cc(m,i-1,k,2))+(wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*            \n     1       cc(m,i-1,k,3))))+(taui*((wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*          \n     1       cc(m,i,k,3))-(wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*  \n     1       cc(m,i,k,2))))               \n            ch(m,ic,2,k) = (taui*((wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*             \n     1       cc(m,i,k,3))-(wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*  \n     1       cc(m,i,k,2))))-(cc(m,i,k,1)+taur*((wa1(i-2)*cc(m,i,k,2)-           \n     1       wa1(i-1)*cc(m,i-1,k,2))+(wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*            \n     1       cc(m,i-1,k,3))))             \n 1002       continue   \n  102    continue      \n  103 continue         \n      return           \n      end              \n      subroutine hradf5 (mp,ido,l1,cc,mdimcc,ch,mdimch,\n     1                   wa1,wa2,wa3,wa4)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension  cc(mdimcc,ido,l1,5)    ,ch(mdimch,ido,5,l1)     ,\n     1           wa1(ido)     ,wa2(ido)     ,wa3(ido)     ,wa4(ido)             \n      arg=2.d0*pimach()/5.d0                  \n      tr11=dcos(arg)    \n      ti11=dsin(arg)    \n      tr12=dcos(2.d0*arg)                    \n      ti12=dsin(2.d0*arg)                    \n      do 101 k=1,l1    \n         do 1001 m=1,mp                   \n         ch(m,1,1,k) = cc(m,1,k,1)+(cc(m,1,k,5)+cc(m,1,k,2))+                   \n     1    (cc(m,1,k,4)+cc(m,1,k,3))       \n         ch(m,ido,2,k) = cc(m,1,k,1)+tr11*(cc(m,1,k,5)+cc(m,1,k,2))+            \n     1    tr12*(cc(m,1,k,4)+cc(m,1,k,3))  \n         ch(m,1,3,k) = ti11*(cc(m,1,k,5)-cc(m,1,k,2))+ti12*  \n     1    (cc(m,1,k,4)-cc(m,1,k,3))       \n         ch(m,ido,4,k) = cc(m,1,k,1)+tr12*(cc(m,1,k,5)+cc(m,1,k,2))+            \n     1    tr11*(cc(m,1,k,4)+cc(m,1,k,3))  \n         ch(m,1,5,k) = ti12*(cc(m,1,k,5)-cc(m,1,k,2))-ti11*  \n     1    (cc(m,1,k,4)-cc(m,1,k,3))       \n 1001    continue      \n  101 continue         \n      if (ido .eq. 1) return              \n      idp2 = ido+2     \n      do 103 k=1,l1    \n         do 102 i=3,ido,2                 \n            ic = idp2-i                   \n            do 1002 m=1,mp                \n            ch(m,i-1,1,k) = cc(m,i-1,k,1)+((wa1(i-2)*cc(m,i-1,k,2)+             \n     1       wa1(i-1)*cc(m,i,k,2))+(wa4(i-2)*cc(m,i-1,k,5)+wa4(i-1)*            \n     1       cc(m,i,k,5)))+((wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*                   \n     1       cc(m,i,k,3))+(wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*cc(m,i,k,4)))        \n            ch(m,i,1,k) = cc(m,i,k,1)+((wa1(i-2)*cc(m,i,k,2)-wa1(i-1)*          \n     1       cc(m,i-1,k,2))+(wa4(i-2)*cc(m,i,k,5)-wa4(i-1)*  \n     1       cc(m,i-1,k,5)))+((wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*                   \n     1       cc(m,i-1,k,3))+(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4)))              \n            ch(m,i-1,3,k) = cc(m,i-1,k,1)+tr11*              \n     1      ( wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*cc(m,i,k,2)    \n     1       +wa4(i-2)*cc(m,i-1,k,5)+wa4(i-1)*cc(m,i,k,5))+tr12*                \n     1      ( wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*cc(m,i,k,3)    \n     1       +wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*cc(m,i,k,4))+ti11*                \n     1      ( wa1(i-2)*cc(m,i,k,2)-wa1(i-1)*cc(m,i-1,k,2)    \n     1       -(wa4(i-2)*cc(m,i,k,5)-wa4(i-1)*cc(m,i-1,k,5)))+ti12*              \n     1      ( wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*cc(m,i-1,k,3)    \n     1       -(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*cc(m,i-1,k,4))) \n            ch(m,ic-1,2,k) = cc(m,i-1,k,1)+tr11*             \n     1      ( wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*cc(m,i,k,2)    \n     1       +wa4(i-2)*cc(m,i-1,k,5)+wa4(i-1)*cc(m,i,k,5))+tr12*                \n     1     ( wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*cc(m,i,k,3)     \n     1      +wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*cc(m,i,k,4))-(ti11*                \n     1      ( wa1(i-2)*cc(m,i,k,2)-wa1(i-1)*cc(m,i-1,k,2)    \n     1       -(wa4(i-2)*cc(m,i,k,5)-wa4(i-1)*cc(m,i-1,k,5)))+ti12*              \n     1      ( wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*cc(m,i-1,k,3)    \n     1       -(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*cc(m,i-1,k,4))))                   \n            ch(m,i,3,k) = (cc(m,i,k,1)+tr11*((wa1(i-2)*cc(m,i,k,2)-             \n     1       wa1(i-1)*cc(m,i-1,k,2))+(wa4(i-2)*cc(m,i,k,5)-wa4(i-1)*            \n     1       cc(m,i-1,k,5)))+tr12*((wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*              \n     1       cc(m,i-1,k,3))+(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4))))+(ti11*((wa4(i-2)*cc(m,i-1,k,5)+                   \n     1       wa4(i-1)*cc(m,i,k,5))-(wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*            \n     1       cc(m,i,k,2)))+ti12*((wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*              \n     1       cc(m,i,k,4))-(wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*  \n     1       cc(m,i,k,3))))               \n            ch(m,ic,2,k) = (ti11*((wa4(i-2)*cc(m,i-1,k,5)+wa4(i-1)*             \n     1       cc(m,i,k,5))-(wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*  \n     1       cc(m,i,k,2)))+ti12*((wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*              \n     1       cc(m,i,k,4))-(wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*  \n     1       cc(m,i,k,3))))-(cc(m,i,k,1)+tr11*((wa1(i-2)*cc(m,i,k,2)-           \n     1       wa1(i-1)*cc(m,i-1,k,2))+(wa4(i-2)*cc(m,i,k,5)-wa4(i-1)*            \n     1       cc(m,i-1,k,5)))+tr12*((wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*              \n     1       cc(m,i-1,k,3))+(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4))))             \n            ch(m,i-1,5,k) = (cc(m,i-1,k,1)+tr12*((wa1(i-2)*  \n     1       cc(m,i-1,k,2)+wa1(i-1)*cc(m,i,k,2))+(wa4(i-2)*  \n     1       cc(m,i-1,k,5)+wa4(i-1)*cc(m,i,k,5)))+tr11*((wa2(i-2)*              \n     1       cc(m,i-1,k,3)+wa2(i-1)*cc(m,i,k,3))+(wa3(i-2)*  \n     1       cc(m,i-1,k,4)+wa3(i-1)*cc(m,i,k,4))))+(ti12*((wa1(i-2)*            \n     1       cc(m,i,k,2)-wa1(i-1)*cc(m,i-1,k,2))-(wa4(i-2)*cc(m,i,k,5)-         \n     1       wa4(i-1)*cc(m,i-1,k,5)))-ti11*((wa2(i-2)*cc(m,i,k,3)-              \n     1       wa2(i-1)*cc(m,i-1,k,3))-(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*            \n     1       cc(m,i-1,k,4))))             \n            ch(m,ic-1,4,k) = (cc(m,i-1,k,1)+tr12*((wa1(i-2)* \n     1       cc(m,i-1,k,2)+wa1(i-1)*cc(m,i,k,2))+(wa4(i-2)*  \n     1       cc(m,i-1,k,5)+wa4(i-1)*cc(m,i,k,5)))+tr11*((wa2(i-2)*              \n     1       cc(m,i-1,k,3)+wa2(i-1)*cc(m,i,k,3))+(wa3(i-2)*  \n     1       cc(m,i-1,k,4)+wa3(i-1)*cc(m,i,k,4))))-(ti12*((wa1(i-2)*            \n     1       cc(m,i,k,2)-wa1(i-1)*cc(m,i-1,k,2))-(wa4(i-2)*cc(m,i,k,5)-         \n     1       wa4(i-1)*cc(m,i-1,k,5)))-ti11*((wa2(i-2)*cc(m,i,k,3)-              \n     1       wa2(i-1)*cc(m,i-1,k,3))-(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*            \n     1       cc(m,i-1,k,4))))             \n            ch(m,i,5,k) = (cc(m,i,k,1)+tr12*((wa1(i-2)*cc(m,i,k,2)-             \n     1       wa1(i-1)*cc(m,i-1,k,2))+(wa4(i-2)*cc(m,i,k,5)-wa4(i-1)*            \n     1       cc(m,i-1,k,5)))+tr11*((wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*              \n     1       cc(m,i-1,k,3))+(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4))))+(ti12*((wa4(i-2)*cc(m,i-1,k,5)+                   \n     1       wa4(i-1)*cc(m,i,k,5))-(wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*            \n     1       cc(m,i,k,2)))-ti11*((wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*              \n     1       cc(m,i,k,4))-(wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*  \n     1       cc(m,i,k,3))))               \n            ch(m,ic,4,k) = (ti12*((wa4(i-2)*cc(m,i-1,k,5)+wa4(i-1)*             \n     1       cc(m,i,k,5))-(wa1(i-2)*cc(m,i-1,k,2)+wa1(i-1)*  \n     1       cc(m,i,k,2)))-ti11*((wa3(i-2)*cc(m,i-1,k,4)+wa3(i-1)*              \n     1       cc(m,i,k,4))-(wa2(i-2)*cc(m,i-1,k,3)+wa2(i-1)*  \n     1       cc(m,i,k,3))))-(cc(m,i,k,1)+tr12*((wa1(i-2)*cc(m,i,k,2)-           \n     1       wa1(i-1)*cc(m,i-1,k,2))+(wa4(i-2)*cc(m,i,k,5)-wa4(i-1)*            \n     1       cc(m,i-1,k,5)))+tr11*((wa2(i-2)*cc(m,i,k,3)-wa2(i-1)*              \n     1       cc(m,i-1,k,3))+(wa3(i-2)*cc(m,i,k,4)-wa3(i-1)*  \n     1       cc(m,i-1,k,4))))             \n 1002       continue   \n  102    continue      \n  103 continue         \n      return           \n      end              \n      subroutine hradfg (mp,ido,ip,l1,idl1,cc,c1,c2,mdimcc,\n     1              ch,ch2,mdimch,wa)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension     ch(mdimch,ido,l1,ip)   ,cc(mdimcc,ido,ip,l1)  ,\n     1            c1(mdimcc,ido,l1,ip)    ,c2(mdimcc,idl1,ip),\n     2                ch2(mdimch,idl1,ip)           ,wa(ido)\n      tpi=2.d0*pimach()                     \n      arg = tpi/dfloat(ip)                 \n      dcp = dcos(arg)   \n      dsp = dsin(arg)   \n      ipph = (ip+1)/2                     \n      ipp2 = ip+2      \n      idp2 = ido+2     \n      nbd = (ido-1)/2                     \n      if (ido .eq. 1) go to 119           \n      do 101 ik=1,idl1                    \n         do 1001 m=1,mp                   \n         ch2(m,ik,1) = c2(m,ik,1)         \n 1001    continue      \n  101 continue         \n      do 103 j=2,ip    \n         do 102 k=1,l1                    \n            do 1002 m=1,mp                \n            ch(m,1,k,j) = c1(m,1,k,j)     \n 1002       continue   \n  102    continue      \n  103 continue         \n      if (nbd .gt. l1) go to 107          \n      is = -ido        \n      do 106 j=2,ip    \n         is = is+ido   \n         idij = is     \n         do 105 i=3,ido,2                 \n            idij = idij+2                 \n            do 104 k=1,l1                 \n               do 1004 m=1,mp             \n               ch(m,i-1,k,j) = wa(idij-1)*c1(m,i-1,k,j)+wa(idij)                \n     1           *c1(m,i,k,j)             \n               ch(m,i,k,j) = wa(idij-1)*c1(m,i,k,j)-wa(idij) \n     1           *c1(m,i-1,k,j)           \n 1004          continue                   \n  104       continue   \n  105    continue      \n  106 continue         \n      go to 111        \n  107 is = -ido        \n      do 110 j=2,ip    \n         is = is+ido   \n         do 109 k=1,l1                    \n            idij = is                     \n            do 108 i=3,ido,2              \n               idij = idij+2              \n               do 1008 m=1,mp             \n               ch(m,i-1,k,j) = wa(idij-1)*c1(m,i-1,k,j)+wa(idij)                \n     1           *c1(m,i,k,j)             \n               ch(m,i,k,j) = wa(idij-1)*c1(m,i,k,j)-wa(idij) \n     1           *c1(m,i-1,k,j)           \n 1008          continue                   \n  108       continue   \n  109    continue      \n  110 continue         \n  111 if (nbd .lt. l1) go to 115          \n      do 114 j=2,ipph                     \n         jc = ipp2-j   \n         do 113 k=1,l1                    \n            do 112 i=3,ido,2              \n               do 1012 m=1,mp             \n               c1(m,i-1,k,j) = ch(m,i-1,k,j)+ch(m,i-1,k,jc)  \n               c1(m,i-1,k,jc) = ch(m,i,k,j)-ch(m,i,k,jc)     \n               c1(m,i,k,j) = ch(m,i,k,j)+ch(m,i,k,jc)        \n               c1(m,i,k,jc) = ch(m,i-1,k,jc)-ch(m,i-1,k,j)   \n 1012          continue                   \n  112       continue   \n  113    continue      \n  114 continue         \n      go to 121        \n  115 do 118 j=2,ipph                     \n         jc = ipp2-j   \n         do 117 i=3,ido,2                 \n            do 116 k=1,l1                 \n               do 1016 m=1,mp             \n               c1(m,i-1,k,j) = ch(m,i-1,k,j)+ch(m,i-1,k,jc)  \n               c1(m,i-1,k,jc) = ch(m,i,k,j)-ch(m,i,k,jc)     \n               c1(m,i,k,j) = ch(m,i,k,j)+ch(m,i,k,jc)        \n               c1(m,i,k,jc) = ch(m,i-1,k,jc)-ch(m,i-1,k,j)   \n 1016          continue                   \n  116       continue   \n  117    continue      \n  118 continue         \n      go to 121        \n  119 do 120 ik=1,idl1                    \n         do 1020 m=1,mp                   \n         c2(m,ik,1) = ch2(m,ik,1)         \n 1020    continue      \n  120 continue         \n  121 do 123 j=2,ipph                     \n         jc = ipp2-j   \n         do 122 k=1,l1                    \n            do 1022 m=1,mp                \n            c1(m,1,k,j) = ch(m,1,k,j)+ch(m,1,k,jc)           \n            c1(m,1,k,jc) = ch(m,1,k,jc)-ch(m,1,k,j)          \n 1022       continue   \n  122    continue      \n  123 continue         \nc                      \n      ar1 = 1.d0         \n      ai1 = 0.d0         \n      do 127 l=2,ipph                     \n         lc = ipp2-l   \n         ar1h = dcp*ar1-dsp*ai1           \n         ai1 = dcp*ai1+dsp*ar1            \n         ar1 = ar1h    \n         do 124 ik=1,idl1                 \n            do 1024 m=1,mp                \n            ch2(m,ik,l) = c2(m,ik,1)+ar1*c2(m,ik,2)          \n            ch2(m,ik,lc) = ai1*c2(m,ik,ip)                   \n 1024       continue   \n  124    continue      \n         dc2 = ar1     \n         ds2 = ai1     \n         ar2 = ar1     \n         ai2 = ai1     \n         do 126 j=3,ipph                  \n            jc = ipp2-j                   \n            ar2h = dc2*ar2-ds2*ai2        \n            ai2 = dc2*ai2+ds2*ar2         \n            ar2 = ar2h                    \n            do 125 ik=1,idl1              \n               do 1025 m=1,mp             \n               ch2(m,ik,l) = ch2(m,ik,l)+ar2*c2(m,ik,j)      \n               ch2(m,ik,lc) = ch2(m,ik,lc)+ai2*c2(m,ik,jc)   \n 1025          continue                   \n  125       continue   \n  126    continue      \n  127 continue         \n      do 129 j=2,ipph                     \n         do 128 ik=1,idl1                 \n            do 1028 m=1,mp                \n            ch2(m,ik,1) = ch2(m,ik,1)+c2(m,ik,j)             \n 1028       continue   \n  128    continue      \n  129 continue         \nc                      \n      if (ido .lt. l1) go to 132          \n      do 131 k=1,l1    \n         do 130 i=1,ido                   \n            do 1030 m=1,mp                \n            cc(m,i,1,k) = ch(m,i,k,1)     \n 1030       continue   \n  130    continue      \n  131 continue         \n      go to 135        \n  132 do 134 i=1,ido   \n         do 133 k=1,l1                    \n            do 1033 m=1,mp                \n            cc(m,i,1,k) = ch(m,i,k,1)     \n 1033       continue   \n  133    continue      \n  134 continue         \n  135 do 137 j=2,ipph                     \n         jc = ipp2-j   \n         j2 = j+j      \n         do 136 k=1,l1                    \n            do 1036 m=1,mp                \n            cc(m,ido,j2-2,k) = ch(m,1,k,j)                   \n            cc(m,1,j2-1,k) = ch(m,1,k,jc)                    \n 1036       continue   \n  136    continue      \n  137 continue         \n      if (ido .eq. 1) return              \n      if (nbd .lt. l1) go to 141          \n      do 140 j=2,ipph                     \n         jc = ipp2-j   \n         j2 = j+j      \n         do 139 k=1,l1                    \n            do 138 i=3,ido,2              \n               ic = idp2-i                \n               do 1038 m=1,mp             \n               cc(m,i-1,j2-1,k) = ch(m,i-1,k,j)+ch(m,i-1,k,jc)                  \n               cc(m,ic-1,j2-2,k) = ch(m,i-1,k,j)-ch(m,i-1,k,jc)                 \n               cc(m,i,j2-1,k) = ch(m,i,k,j)+ch(m,i,k,jc)     \n               cc(m,ic,j2-2,k) = ch(m,i,k,jc)-ch(m,i,k,j)    \n 1038          continue                   \n  138       continue   \n  139    continue      \n  140 continue         \n      return           \n  141 do 144 j=2,ipph                     \n         jc = ipp2-j   \n         j2 = j+j      \n         do 143 i=3,ido,2                 \n            ic = idp2-i                   \n            do 142 k=1,l1                 \n               do 1042 m=1,mp             \n               cc(m,i-1,j2-1,k) = ch(m,i-1,k,j)+ch(m,i-1,k,jc)                  \n               cc(m,ic-1,j2-2,k) = ch(m,i-1,k,j)-ch(m,i-1,k,jc)                 \n               cc(m,i,j2-1,k) = ch(m,i,k,j)+ch(m,i,k,jc)     \n               cc(m,ic,j2-2,k) = ch(m,i,k,jc)-ch(m,i,k,j)    \n 1042          continue                   \n  142       continue   \n  143    continue      \n  144 continue         \n      return           \n      end              \n      double precision function pimach()     \n      implicit double precision (a-h,o-z)              \n      pimach=3.14159265358979323846264338328d0\n      return           \n      end              \n      subroutine hrfftb(m,n,r,mdimr,whrfft,work)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension     r(mdimr,n)  ,work(1)    ,whrfft(n+15)\n      common /hrf/ tfft\n      if (n .eq. 1) return                \nc     tstart = second(dum)\n      call hrftb1 (m,n,r,mdimr,work,whrfft,whrfft(n+1))\nc     tfft = tfft+second(dum)-tstart\n      return           \n      end              \n      subroutine hrftb1 (m,n,c,mdimc,ch,wa,fac)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension       ch(m,n), c(mdimc,n), wa(n) ,fac(15)\n      nf = fac(2)      \n      na = 0           \n      l1 = 1           \n      iw = 1           \n      do 116 k1=1,nf   \n         ip = fac(k1+2)                   \n         l2 = ip*l1    \n         ido = n/l2    \n         idl1 = ido*l1                    \n         if (ip .ne. 4) go to 103         \n         ix2 = iw+ido                     \n         ix3 = ix2+ido                    \n         if (na .ne. 0) go to 101         \n\t call hradb4 (m,ido,l1,c,mdimc,ch,m,wa(iw),wa(ix2),wa(ix3))\n         go to 102     \n  101    call hradb4 (m,ido,l1,ch,m,c,mdimc,wa(iw),wa(ix2),wa(ix3))\n  102    na = 1-na     \n         go to 115     \n  103    if (ip .ne. 2) go to 106         \n         if (na .ne. 0) go to 104         \n\t call hradb2 (m,ido,l1,c,mdimc,ch,m,wa(iw))\n         go to 105     \n  104    call hradb2 (m,ido,l1,ch,m,c,mdimc,wa(iw))\n  105    na = 1-na     \n         go to 115     \n  106    if (ip .ne. 3) go to 109         \n         ix2 = iw+ido                     \n         if (na .ne. 0) go to 107         \n\t call hradb3 (m,ido,l1,c,mdimc,ch,m,wa(iw),wa(ix2))\n         go to 108     \n  107    call hradb3 (m,ido,l1,ch,m,c,mdimc,wa(iw),wa(ix2))\n  108    na = 1-na     \n         go to 115     \n  109    if (ip .ne. 5) go to 112         \n         ix2 = iw+ido                     \n         ix3 = ix2+ido                    \n         ix4 = ix3+ido                    \n         if (na .ne. 0) go to 110         \n      call hradb5 (m,ido,l1,c,mdimc,ch,m,wa(iw),wa(ix2),wa(ix3),wa(ix4))\n         go to 111     \n  110 call hradb5 (m,ido,l1,ch,m,c,mdimc,wa(iw),wa(ix2),wa(ix3),wa(ix4))\n  111    na = 1-na     \n         go to 115     \n  112    if (na .ne. 0) go to 113         \n\t call hradbg (m,ido,ip,l1,idl1,c,c,c,mdimc,ch,ch,m,wa(iw))\n         go to 114     \n  113    call hradbg (m,ido,ip,l1,idl1,ch,ch,ch,m,c,c,mdimc,wa(iw))\n  114    if (ido .eq. 1) na = 1-na        \n  115    l1 = l2       \n         iw = iw+(ip-1)*ido               \n  116 continue         \n      if (na .eq. 0) return\n      do 117 j=1,n     \n      do 117 i=1,m     \n\t c(i,j) = ch(i,j)\n  117 continue         \n      return           \n      end              \n      subroutine hradbg (mp,ido,ip,l1,idl1,cc,c1,c2,mdimcc,\n     1          ch,ch2,mdimch,wa)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension    ch(mdimch,ido,l1,ip)    ,cc(mdimcc,ido,ip,l1) ,\n     1           c1(mdimcc,ido,l1,ip)     ,c2(mdimcc,idl1,ip),\n     2                ch2(mdimch,idl1,ip)       ,wa(ido)\n      tpi=2.d0*pimach()                     \n      arg = tpi/dfloat(ip)                 \n      dcp = dcos(arg)   \n      dsp = dsin(arg)   \n      idp2 = ido+2     \n      nbd = (ido-1)/2                     \n      ipp2 = ip+2      \n      ipph = (ip+1)/2                     \n      if (ido .lt. l1) go to 103          \n      do 102 k=1,l1    \n         do 101 i=1,ido                   \n            do 1001 m=1,mp                \n            ch(m,i,k,1) = cc(m,i,1,k)     \n 1001       continue   \n  101    continue      \n  102 continue         \n      go to 106        \n  103 do 105 i=1,ido   \n         do 104 k=1,l1                    \n            do 1004 m=1,mp                \n            ch(m,i,k,1) = cc(m,i,1,k)     \n 1004       continue   \n  104    continue      \n  105 continue         \n  106 do 108 j=2,ipph                     \n         jc = ipp2-j   \n         j2 = j+j      \n         do 107 k=1,l1                    \n            do 1007 m=1,mp                \n            ch(m,1,k,j) = cc(m,ido,j2-2,k)+cc(m,ido,j2-2,k)  \n            ch(m,1,k,jc) = cc(m,1,j2-1,k)+cc(m,1,j2-1,k)     \n 1007       continue   \n  107    continue      \n  108 continue         \n      if (ido .eq. 1) go to 116           \n      if (nbd .lt. l1) go to 112          \n      do 111 j=2,ipph                     \n         jc = ipp2-j   \n         do 110 k=1,l1                    \n            do 109 i=3,ido,2              \n               ic = idp2-i                \n               do 1009 m=1,mp             \n               ch(m,i-1,k,j) = cc(m,i-1,2*j-1,k)+cc(m,ic-1,2*j-2,k)             \n               ch(m,i-1,k,jc) = cc(m,i-1,2*j-1,k)-cc(m,ic-1,2*j-2,k)            \n               ch(m,i,k,j) = cc(m,i,2*j-1,k)-cc(m,ic,2*j-2,k)                   \n               ch(m,i,k,jc) = cc(m,i,2*j-1,k)+cc(m,ic,2*j-2,k)                  \n 1009          continue                   \n  109       continue   \n  110    continue      \n  111 continue         \n      go to 116        \n  112 do 115 j=2,ipph                     \n         jc = ipp2-j   \n         do 114 i=3,ido,2                 \n            ic = idp2-i                   \n            do 113 k=1,l1                 \n               do 1013 m=1,mp             \n               ch(m,i-1,k,j) = cc(m,i-1,2*j-1,k)+cc(m,ic-1,2*j-2,k)             \n               ch(m,i-1,k,jc) = cc(m,i-1,2*j-1,k)-cc(m,ic-1,2*j-2,k)            \n               ch(m,i,k,j) = cc(m,i,2*j-1,k)-cc(m,ic,2*j-2,k)                   \n               ch(m,i,k,jc) = cc(m,i,2*j-1,k)+cc(m,ic,2*j-2,k)                  \n 1013          continue                   \n  113       continue   \n  114    continue      \n  115 continue         \n  116 ar1 = 1.d0         \n      ai1 = 0.d0         \n      do 120 l=2,ipph                     \n         lc = ipp2-l   \n         ar1h = dcp*ar1-dsp*ai1           \n         ai1 = dcp*ai1+dsp*ar1            \n         ar1 = ar1h    \n         do 117 ik=1,idl1                 \n            do 1017 m=1,mp                \n            c2(m,ik,l) = ch2(m,ik,1)+ar1*ch2(m,ik,2)         \n            c2(m,ik,lc) = ai1*ch2(m,ik,ip)                   \n 1017       continue   \n  117    continue      \n         dc2 = ar1     \n         ds2 = ai1     \n         ar2 = ar1     \n         ai2 = ai1     \n         do 119 j=3,ipph                  \n            jc = ipp2-j                   \n            ar2h = dc2*ar2-ds2*ai2        \n            ai2 = dc2*ai2+ds2*ar2         \n            ar2 = ar2h                    \n            do 118 ik=1,idl1              \n               do 1018 m=1,mp             \n               c2(m,ik,l) = c2(m,ik,l)+ar2*ch2(m,ik,j)       \n               c2(m,ik,lc) = c2(m,ik,lc)+ai2*ch2(m,ik,jc)    \n 1018          continue                   \n  118       continue   \n  119    continue      \n  120 continue         \n      do 122 j=2,ipph                     \n         do 121 ik=1,idl1                 \n            do 1021 m=1,mp                \n            ch2(m,ik,1) = ch2(m,ik,1)+ch2(m,ik,j)            \n 1021       continue   \n  121    continue      \n  122 continue         \n      do 124 j=2,ipph                     \n         jc = ipp2-j   \n         do 123 k=1,l1                    \n            do 1023 m=1,mp                \n            ch(m,1,k,j) = c1(m,1,k,j)-c1(m,1,k,jc)           \n            ch(m,1,k,jc) = c1(m,1,k,j)+c1(m,1,k,jc)          \n 1023       continue   \n  123    continue      \n  124 continue         \n      if (ido .eq. 1) go to 132           \n      if (nbd .lt. l1) go to 128          \n      do 127 j=2,ipph                     \n         jc = ipp2-j   \n         do 126 k=1,l1                    \n            do 125 i=3,ido,2              \n               do 1025 m=1,mp             \n               ch(m,i-1,k,j) = c1(m,i-1,k,j)-c1(m,i,k,jc)    \n               ch(m,i-1,k,jc) = c1(m,i-1,k,j)+c1(m,i,k,jc)   \n               ch(m,i,k,j) = c1(m,i,k,j)+c1(m,i-1,k,jc)      \n               ch(m,i,k,jc) = c1(m,i,k,j)-c1(m,i-1,k,jc)     \n 1025          continue                   \n  125       continue   \n  126    continue      \n  127 continue         \n      go to 132        \n  128 do 131 j=2,ipph                     \n         jc = ipp2-j   \n         do 130 i=3,ido,2                 \n            do 129 k=1,l1                 \n               do 1029 m=1,mp             \n               ch(m,i-1,k,j) = c1(m,i-1,k,j)-c1(m,i,k,jc)    \n               ch(m,i-1,k,jc) = c1(m,i-1,k,j)+c1(m,i,k,jc)   \n               ch(m,i,k,j) = c1(m,i,k,j)+c1(m,i-1,k,jc)      \n               ch(m,i,k,jc) = c1(m,i,k,j)-c1(m,i-1,k,jc)     \n 1029          continue                   \n  129       continue   \n  130    continue      \n  131 continue         \n  132 continue         \n      if (ido .eq. 1) return              \n      do 133 ik=1,idl1                    \n         do 1033 m=1,mp                   \n         c2(m,ik,1) = ch2(m,ik,1)         \n 1033    continue      \n  133 continue         \n      do 135 j=2,ip    \n         do 134 k=1,l1                    \n            do 1034 m=1,mp                \n            c1(m,1,k,j) = ch(m,1,k,j)     \n 1034       continue   \n  134    continue      \n  135 continue         \n      if (nbd .gt. l1) go to 139          \n      is = -ido        \n      do 138 j=2,ip    \n         is = is+ido   \n         idij = is     \n         do 137 i=3,ido,2                 \n            idij = idij+2                 \n            do 136 k=1,l1                 \n               do 1036 m=1,mp             \n               c1(m,i-1,k,j) = wa(idij-1)*ch(m,i-1,k,j)-wa(idij)*               \n     1          ch(m,i,k,j)               \n               c1(m,i,k,j) = wa(idij-1)*ch(m,i,k,j)+wa(idij)*                   \n     1          ch(m,i-1,k,j)             \n 1036          continue                   \n  136       continue   \n  137    continue      \n  138 continue         \n      go to 143        \n  139 is = -ido        \n      do 142 j=2,ip    \n         is = is+ido   \n         do 141 k=1,l1                    \n            idij = is                     \n            do 140 i=3,ido,2              \n               idij = idij+2              \n               do 1040 m=1,mp             \n               c1(m,i-1,k,j) = wa(idij-1)*ch(m,i-1,k,j)-wa(idij)*               \n     1          ch(m,i,k,j)               \n               c1(m,i,k,j) = wa(idij-1)*ch(m,i,k,j)+wa(idij)*                   \n     1          ch(m,i-1,k,j)             \n 1040          continue                   \n  140       continue   \n  141    continue      \n  142 continue         \n  143 return           \n      end              \n      subroutine hradb4 (mp,ido,l1,cc,mdimcc,ch,mdimch,wa1,wa2,wa3)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension  cc(mdimcc,ido,4,l1)  ,ch(mdimch,ido,l1,4)    ,\n     1                wa1(ido)  ,wa2(ido)  ,wa3(ido)         \n      sqrt2=dsqrt(2.d0)   \n      do 101 k=1,l1    \n          do 1001 m=1,mp                  \n         ch(m,1,k,3) = (cc(m,1,1,k)+cc(m,ido,4,k))           \n     1   -(cc(m,ido,2,k)+cc(m,ido,2,k))   \n         ch(m,1,k,1) = (cc(m,1,1,k)+cc(m,ido,4,k))           \n     1   +(cc(m,ido,2,k)+cc(m,ido,2,k))   \n         ch(m,1,k,4) = (cc(m,1,1,k)-cc(m,ido,4,k))           \n     1   +(cc(m,1,3,k)+cc(m,1,3,k))       \n         ch(m,1,k,2) = (cc(m,1,1,k)-cc(m,ido,4,k))           \n     1   -(cc(m,1,3,k)+cc(m,1,3,k))       \n 1001     continue     \n  101 continue         \n      if (ido-2) 107,105,102              \n  102 idp2 = ido+2     \n      do 104 k=1,l1    \n         do 103 i=3,ido,2                 \n            ic = idp2-i                   \n               do 1002 m=1,mp             \n            ch(m,i-1,k,1) = (cc(m,i-1,1,k)+cc(m,ic-1,4,k))   \n     1      +(cc(m,i-1,3,k)+cc(m,ic-1,2,k))                  \n            ch(m,i,k,1) = (cc(m,i,1,k)-cc(m,ic,4,k))         \n     1      +(cc(m,i,3,k)-cc(m,ic,2,k))   \n            ch(m,i-1,k,2)=wa1(i-2)*((cc(m,i-1,1,k)-cc(m,ic-1,4,k))              \n     1      -(cc(m,i,3,k)+cc(m,ic,2,k)))-wa1(i-1)            \n     1      *((cc(m,i,1,k)+cc(m,ic,4,k))+(cc(m,i-1,3,k)-cc(m,ic-1,2,k)))        \n            ch(m,i,k,2)=wa1(i-2)*((cc(m,i,1,k)+cc(m,ic,4,k)) \n     1      +(cc(m,i-1,3,k)-cc(m,ic-1,2,k)))+wa1(i-1)        \n     1      *((cc(m,i-1,1,k)-cc(m,ic-1,4,k))-(cc(m,i,3,k)+cc(m,ic,2,k)))        \n            ch(m,i-1,k,3)=wa2(i-2)*((cc(m,i-1,1,k)+cc(m,ic-1,4,k))              \n     1      -(cc(m,i-1,3,k)+cc(m,ic-1,2,k)))-wa2(i-1)        \n     1      *((cc(m,i,1,k)-cc(m,ic,4,k))-(cc(m,i,3,k)-cc(m,ic,2,k)))            \n            ch(m,i,k,3)=wa2(i-2)*((cc(m,i,1,k)-cc(m,ic,4,k)) \n     1      -(cc(m,i,3,k)-cc(m,ic,2,k)))+wa2(i-1)            \n     1      *((cc(m,i-1,1,k)+cc(m,ic-1,4,k))-(cc(m,i-1,3,k)  \n     1      +cc(m,ic-1,2,k)))             \n            ch(m,i-1,k,4)=wa3(i-2)*((cc(m,i-1,1,k)-cc(m,ic-1,4,k))              \n     1      +(cc(m,i,3,k)+cc(m,ic,2,k)))-wa3(i-1)            \n     1     *((cc(m,i,1,k)+cc(m,ic,4,k))-(cc(m,i-1,3,k)-cc(m,ic-1,2,k)))         \n            ch(m,i,k,4)=wa3(i-2)*((cc(m,i,1,k)+cc(m,ic,4,k)) \n     1      -(cc(m,i-1,3,k)-cc(m,ic-1,2,k)))+wa3(i-1)        \n     1      *((cc(m,i-1,1,k)-cc(m,ic-1,4,k))+(cc(m,i,3,k)+cc(m,ic,2,k)))        \n 1002          continue                   \n  103    continue      \n  104 continue         \n      if (mod(ido,2) .eq. 1) return       \n  105 continue         \n      do 106 k=1,l1    \n               do 1003 m=1,mp             \n         ch(m,ido,k,1) = (cc(m,ido,1,k)+cc(m,ido,3,k))       \n     1   +(cc(m,ido,1,k)+cc(m,ido,3,k))   \n         ch(m,ido,k,2) = sqrt2*((cc(m,ido,1,k)-cc(m,ido,3,k))                   \n     1   -(cc(m,1,2,k)+cc(m,1,4,k)))      \n         ch(m,ido,k,3) = (cc(m,1,4,k)-cc(m,1,2,k))           \n     1   +(cc(m,1,4,k)-cc(m,1,2,k))       \n         ch(m,ido,k,4) = -sqrt2*((cc(m,ido,1,k)-cc(m,ido,3,k))                  \n     1   +(cc(m,1,2,k)+cc(m,1,4,k)))      \n 1003          continue                   \n  106 continue         \n  107 return           \n      end              \n      subroutine hradb2 (mp,ido,l1,cc,mdimcc,ch,mdimch,wa1)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension  cc(mdimcc,ido,2,l1)    ,ch(mdimch,ido,l1,2),\n     1                wa1(ido)            \n      do 101 k=1,l1    \n          do 1001 m=1,mp                  \n         ch(m,1,k,1) = cc(m,1,1,k)+cc(m,ido,2,k)             \n         ch(m,1,k,2) = cc(m,1,1,k)-cc(m,ido,2,k)             \n 1001     continue     \n  101 continue         \n      if (ido-2) 107,105,102              \n  102 idp2 = ido+2     \n      do 104 k=1,l1    \n         do 103 i=3,ido,2                 \n            ic = idp2-i                   \n               do 1002 m=1,mp             \n            ch(m,i-1,k,1) = cc(m,i-1,1,k)+cc(m,ic-1,2,k)     \n            ch(m,i,k,1) = cc(m,i,1,k)-cc(m,ic,2,k)           \n            ch(m,i-1,k,2) = wa1(i-2)*(cc(m,i-1,1,k)-cc(m,ic-1,2,k))             \n     1      -wa1(i-1)*(cc(m,i,1,k)+cc(m,ic,2,k))             \n            ch(m,i,k,2) = wa1(i-2)*(cc(m,i,1,k)+cc(m,ic,2,k))+wa1(i-1)          \n     1      *(cc(m,i-1,1,k)-cc(m,ic-1,2,k))                  \n 1002          continue                   \n  103    continue      \n  104 continue         \n      if (mod(ido,2) .eq. 1) return       \n  105 do 106 k=1,l1    \n          do 1003 m=1,mp                  \n         ch(m,ido,k,1) = cc(m,ido,1,k)+cc(m,ido,1,k)         \n         ch(m,ido,k,2) = -(cc(m,1,2,k)+cc(m,1,2,k))          \n 1003     continue     \n  106 continue         \n  107 return           \n      end              \n      subroutine hradb3 (mp,ido,l1,cc,mdimcc,ch,mdimch,wa1,wa2)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension  cc(mdimcc,ido,3,l1)    ,ch(mdimch,ido,l1,3),\n     1                wa1(ido)   ,wa2(ido)                   \n      arg=2.d0*pimach()/3.d0                  \n      taur=dcos(arg)    \n      taui=dsin(arg)    \n      do 101 k=1,l1    \n          do 1001 m=1,mp                  \n         ch(m,1,k,1) = cc(m,1,1,k)+2.d0*cc(m,ido,2,k)          \n         ch(m,1,k,2) = cc(m,1,1,k)+(2.d0*taur)*cc(m,ido,2,k)   \n     1   -(2.d0*taui)*cc(m,1,3,k)           \n         ch(m,1,k,3) = cc(m,1,1,k)+(2.d0*taur)*cc(m,ido,2,k)   \n     1   +2.d0*taui*cc(m,1,3,k)             \n 1001     continue     \n  101 continue         \n      if (ido .eq. 1) return              \n      idp2 = ido+2     \n      do 103 k=1,l1    \n         do 102 i=3,ido,2                 \n            ic = idp2-i                   \n               do 1002 m=1,mp             \n            ch(m,i-1,k,1) = cc(m,i-1,1,k)+(cc(m,i-1,3,k)+cc(m,ic-1,2,k))        \n            ch(m,i,k,1) = cc(m,i,1,k)+(cc(m,i,3,k)-cc(m,ic,2,k))                \n            ch(m,i-1,k,2) = wa1(i-2)*     \n     1 ((cc(m,i-1,1,k)+taur*(cc(m,i-1,3,k)+cc(m,ic-1,2,k)))- \n     * (taui*(cc(m,i,3,k)+cc(m,ic,2,k))))                    \n     2                   -wa1(i-1)*       \n     3 ((cc(m,i,1,k)+taur*(cc(m,i,3,k)-cc(m,ic,2,k)))+       \n     * (taui*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))))                \n            ch(m,i,k,2) = wa1(i-2)*       \n     4 ((cc(m,i,1,k)+taur*(cc(m,i,3,k)-cc(m,ic,2,k)))+       \n     8 (taui*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))))                \n     5                  +wa1(i-1)*        \n     6 ((cc(m,i-1,1,k)+taur*(cc(m,i-1,3,k)+cc(m,ic-1,2,k)))- \n     8 (taui*(cc(m,i,3,k)+cc(m,ic,2,k))))                    \n              ch(m,i-1,k,3) = wa2(i-2)*   \n     7 ((cc(m,i-1,1,k)+taur*(cc(m,i-1,3,k)+cc(m,ic-1,2,k)))+ \n     8 (taui*(cc(m,i,3,k)+cc(m,ic,2,k))))                    \n     8   -wa2(i-1)*    \n     9 ((cc(m,i,1,k)+taur*(cc(m,i,3,k)-cc(m,ic,2,k)))-       \n     8 (taui*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))))                \n            ch(m,i,k,3) = wa2(i-2)*       \n     1 ((cc(m,i,1,k)+taur*(cc(m,i,3,k)-cc(m,ic,2,k)))-       \n     8 (taui*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))))                \n     2                 +wa2(i-1)*         \n     3 ((cc(m,i-1,1,k)+taur*(cc(m,i-1,3,k)+cc(m,ic-1,2,k)))+ \n     8 (taui*(cc(m,i,3,k)+cc(m,ic,2,k))))                    \n 1002          continue                   \n  102    continue      \n  103 continue         \n      return           \n      end              \n      subroutine hradb5 (mp,ido,l1,cc,mdimcc,ch,mdimch,\n     1       wa1,wa2,wa3,wa4)\n      implicit double precision (a-h,o-z)\nc                      \nc     a multiple fft package for spherepack\nc                      \n      dimension  cc(mdimcc,ido,5,l1)    ,ch(mdimch,ido,l1,5),\n     1             wa1(ido)     ,wa2(ido)     ,wa3(ido)     ,wa4(ido)           \n      arg=2.d0*pimach()/5.d0                  \n      tr11=dcos(arg)    \n      ti11=dsin(arg)    \n      tr12=dcos(2.d0*arg)                    \n      ti12=dsin(2.d0*arg)                    \n      do 101 k=1,l1    \n      do 1001 m=1,mp   \n         ch(m,1,k,1) = cc(m,1,1,k)+2.d0*cc(m,ido,2,k)+2.d0*cc(m,ido,4,k)            \n         ch(m,1,k,2) = (cc(m,1,1,k)+tr11*2.d0*cc(m,ido,2,k)    \n     1   +tr12*2.d0*cc(m,ido,4,k))-(ti11*2.d0*cc(m,1,3,k)        \n     1   +ti12*2.d0*cc(m,1,5,k))            \n         ch(m,1,k,3) = (cc(m,1,1,k)+tr12*2.d0*cc(m,ido,2,k)    \n     1   +tr11*2.d0*cc(m,ido,4,k))-(ti12*2.d0*cc(m,1,3,k)        \n     1   -ti11*2.d0*cc(m,1,5,k))            \n         ch(m,1,k,4) = (cc(m,1,1,k)+tr12*2.d0*cc(m,ido,2,k)    \n     1   +tr11*2.d0*cc(m,ido,4,k))+(ti12*2.d0*cc(m,1,3,k)        \n     1   -ti11*2.d0*cc(m,1,5,k))            \n         ch(m,1,k,5) = (cc(m,1,1,k)+tr11*2.d0*cc(m,ido,2,k)    \n     1   +tr12*2.d0*cc(m,ido,4,k))+(ti11*2.d0*cc(m,1,3,k)        \n     1   +ti12*2.d0*cc(m,1,5,k))            \n 1001          continue                   \n  101 continue         \n      if (ido .eq. 1) return              \n      idp2 = ido+2     \n      do 103 k=1,l1    \n         do 102 i=3,ido,2                 \n            ic = idp2-i                   \n      do 1002 m=1,mp   \n            ch(m,i-1,k,1) = cc(m,i-1,1,k)+(cc(m,i-1,3,k)+cc(m,ic-1,2,k))        \n     1      +(cc(m,i-1,5,k)+cc(m,ic-1,4,k))                  \n            ch(m,i,k,1) = cc(m,i,1,k)+(cc(m,i,3,k)-cc(m,ic,2,k))                \n     1      +(cc(m,i,5,k)-cc(m,ic,4,k))   \n            ch(m,i-1,k,2) = wa1(i-2)*((cc(m,i-1,1,k)+tr11*   \n     1      (cc(m,i-1,3,k)+cc(m,ic-1,2,k))+tr12              \n     1      *(cc(m,i-1,5,k)+cc(m,ic-1,4,k)))-(ti11*(cc(m,i,3,k)                 \n     1      +cc(m,ic,2,k))+ti12*(cc(m,i,5,k)+cc(m,ic,4,k)))) \n     1      -wa1(i-1)*((cc(m,i,1,k)+tr11*(cc(m,i,3,k)-cc(m,ic,2,k))             \n     1      +tr12*(cc(m,i,5,k)-cc(m,ic,4,k)))+(ti11*(cc(m,i-1,3,k)              \n     1      -cc(m,ic-1,2,k))+ti12*(cc(m,i-1,5,k)-cc(m,ic-1,4,k))))              \n            ch(m,i,k,2) = wa1(i-2)*((cc(m,i,1,k)+tr11*(cc(m,i,3,k)              \n     1      -cc(m,ic,2,k))+tr12*(cc(m,i,5,k)-cc(m,ic,4,k)))  \n     1      +(ti11*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))+ti12       \n     1      *(cc(m,i-1,5,k)-cc(m,ic-1,4,k))))+wa1(i-1)       \n     1      *((cc(m,i-1,1,k)+tr11*(cc(m,i-1,3,k)             \n     1      +cc(m,ic-1,2,k))+tr12*(cc(m,i-1,5,k)+cc(m,ic-1,4,k)))               \n     1      -(ti11*(cc(m,i,3,k)+cc(m,ic,2,k))+ti12           \n     1      *(cc(m,i,5,k)+cc(m,ic,4,k))))                    \n            ch(m,i-1,k,3) = wa2(i-2)      \n     1      *((cc(m,i-1,1,k)+tr12*(cc(m,i-1,3,k)+cc(m,ic-1,2,k))                \n     1      +tr11*(cc(m,i-1,5,k)+cc(m,ic-1,4,k)))-(ti12*(cc(m,i,3,k)            \n     1      +cc(m,ic,2,k))-ti11*(cc(m,i,5,k)+cc(m,ic,4,k)))) \n     1     -wa2(i-1)   \n     1     *((cc(m,i,1,k)+tr12*(cc(m,i,3,k)-                 \n     1      cc(m,ic,2,k))+tr11*(cc(m,i,5,k)-cc(m,ic,4,k)))   \n     1      +(ti12*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))-ti11       \n     1      *(cc(m,i-1,5,k)-cc(m,ic-1,4,k))))                \n            ch(m,i,k,3) = wa2(i-2)        \n     1     *((cc(m,i,1,k)+tr12*(cc(m,i,3,k)-                 \n     1      cc(m,ic,2,k))+tr11*(cc(m,i,5,k)-cc(m,ic,4,k)))   \n     1      +(ti12*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))-ti11       \n     1      *(cc(m,i-1,5,k)-cc(m,ic-1,4,k))))                \n     1      +wa2(i-1)                     \n     1      *((cc(m,i-1,1,k)+tr12*(cc(m,i-1,3,k)+cc(m,ic-1,2,k))                \n     1      +tr11*(cc(m,i-1,5,k)+cc(m,ic-1,4,k)))-(ti12*(cc(m,i,3,k)            \n     1      +cc(m,ic,2,k))-ti11*(cc(m,i,5,k)+cc(m,ic,4,k)))) \n            ch(m,i-1,k,4) = wa3(i-2)      \n     1      *((cc(m,i-1,1,k)+tr12*(cc(m,i-1,3,k)+cc(m,ic-1,2,k))                \n     1      +tr11*(cc(m,i-1,5,k)+cc(m,ic-1,4,k)))+(ti12*(cc(m,i,3,k)            \n     1      +cc(m,ic,2,k))-ti11*(cc(m,i,5,k)+cc(m,ic,4,k)))) \n     1      -wa3(i-1)                     \n     1     *((cc(m,i,1,k)+tr12*(cc(m,i,3,k)-                 \n     1      cc(m,ic,2,k))+tr11*(cc(m,i,5,k)-cc(m,ic,4,k)))   \n     1      -(ti12*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))-ti11       \n     1      *(cc(m,i-1,5,k)-cc(m,ic-1,4,k))))                \n            ch(m,i,k,4) = wa3(i-2)        \n     1     *((cc(m,i,1,k)+tr12*(cc(m,i,3,k)-                 \n     1      cc(m,ic,2,k))+tr11*(cc(m,i,5,k)-cc(m,ic,4,k)))   \n     1      -(ti12*(cc(m,i-1,3,k)-cc(m,ic-1,2,k))-ti11       \n     1      *(cc(m,i-1,5,k)-cc(m,ic-1,4,k))))                \n     1      +wa3(i-1)                     \n     1      *((cc(m,i-1,1,k)+tr12*(cc(m,i-1,3,k)+cc(m,ic-1,2,k))                \n     1      +tr11*(cc(m,i-1,5,k)+cc(m,ic-1,4,k)))+(ti12*(cc(m,i,3,k)            \n     1      +cc(m,ic,2,k))-ti11*(cc(m,i,5,k)+cc(m,ic,4,k)))) \n            ch(m,i-1,k,5) = wa4(i-2)      \n     1      *((cc(m,i-1,1,k)+tr11*(cc(m,i-1,3,k)+cc(m,ic-1,2,k))                \n     1      +tr12*(cc(m,i-1,5,k)+cc(m,ic-1,4,k)))+(ti11*(cc(m,i,3,k)            \n     1      +cc(m,ic,2,k))+ti12*(cc(m,i,5,k)+cc(m,ic,4,k)))) \n     1      -wa4(i-1)                     \n     1      *((cc(m,i,1,k)+tr11*(cc(m,i,3,k)-cc(m,ic,2,k))   \n     1      +tr12*(cc(m,i,5,k)-cc(m,ic,4,k)))-(ti11*(cc(m,i-1,3,k)              \n     1      -cc(m,ic-1,2,k))+ti12*(cc(m,i-1,5,k)-cc(m,ic-1,4,k))))              \n            ch(m,i,k,5) = wa4(i-2)        \n     1      *((cc(m,i,1,k)+tr11*(cc(m,i,3,k)-cc(m,ic,2,k))   \n     1      +tr12*(cc(m,i,5,k)-cc(m,ic,4,k)))-(ti11*(cc(m,i-1,3,k)              \n     1      -cc(m,ic-1,2,k))+ti12*(cc(m,i-1,5,k)-cc(m,ic-1,4,k))))              \n     1      +wa4(i-1)                     \n     1      *((cc(m,i-1,1,k)+tr11*(cc(m,i-1,3,k)+cc(m,ic-1,2,k))                \n     1      +tr12*(cc(m,i-1,5,k)+cc(m,ic-1,4,k)))+(ti11*(cc(m,i,3,k)            \n     1      +cc(m,ic,2,k))+ti12*(cc(m,i,5,k)+cc(m,ic,4,k)))) \n 1002          continue                   \n  102    continue      \n  103 continue         \n      return           \n      end              \n"
  },
  {
    "path": "external/SPHEREPACK/shags.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc ... file shags.f\nc\nc     this file contains code and documentation for subroutines\nc     shags and shagsi\nc\nc ... files which must be loaded with shags.f\nc\nc     sphcom.f, hrfft.f, gaqd.f\nc\nc     subroutine shags(nstrg,nstrab,noffsg,noffsab,\nc                      nlat,nlon,isym,nt,g,idg,jdg,a,b,mdab,ndab,\nc    1                 mdabmax,ndabmax,\nc    1                    wshags,lshags,work,lwork,ierror)\nc\nc     subroutine shags performs the spherical harmonic analysis\nc     on the array g and stores the result in the arrays a and b.\nc     the analysis is performed on a gaussian grid in colatitude\nc     and an equally spaced grid in longitude.  the associated\nc     legendre functions are stored rather than recomputed as they\nc     are in subroutine shagc.  the analysis is described below\nc     at output parameters a,b.\nc\nc     input parameters\nc\nc     nstrg,nstrab  strides of g,a,b\nc     noffsg,noffsab  offset of g,a,b -- must be between 0 and stride-1\nc            Alternatively, when the two strides equal, you can set both\nc            to -1 in which case the output will be computed for all\nc            values of offset between 0 and stride-1.\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are compu\nc            in radians in theta(1),...,theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid poi\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than or equal to 4. the efficiency of the computation is\nc            improved when nlon is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for isym==0.  For this reason an error code will\nc           be returned if isym!=0.\nc\nc     isym   = 0  no symmetries exist about the equator. the analysis\nc                 is performed on the entire sphere.  i.e. on the\nc                 array g(i,j) for i=1,...,nlat and j=1,...,nlon.\nc                 (see description of g below)\nc\nc            = 1  g is antisymmetric about the equator. the analysis\nc                 is performed on the northern hemisphere only.  i.e.\nc                 if nlat is odd the analysis is performed on the\nc                 array g(i,j) for i=1,...,(nlat+1)/2 and j=1,...,nlon.\nc                 if nlat is even the analysis is performed on the\nc                 array g(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc\nc            = 2  g is symmetric about the equator. the analysis is\nc                 performed on the northern hemisphere only.  i.e.\nc                 if nlat is odd the analysis is performed on the\nc                 array g(i,j) for i=1,...,(nlat+1)/2 and j=1,...,nlon.\nc                 if nlat is even the analysis is performed on the\nc                 array g(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc     nt     the number of analyses.  in the program that calls shags,\nc            the arrays g,a and b can be three dimensional in which\nc            case multiple analyses will be performed.  the third\nc            index is the analysis index which assumes the values\nc            k=1,...,nt.  for a single analysis set nt=1. the\nc            discription of the remaining parameters is simplified\nc            by assuming that nt=1 or that the arrays g,a and b\nc            have only two dimensions.\nc\nc     g      a two or three dimensional array (see input parameter\nc            nt) that contains the discrete function to be analyzed.\nc            g(i,j) contains the value of the function at the gaussian\nc            point theta(i) and longitude point phi(j) = (j-1)*2*pi/nlon\nc            the index ranges are defined above at the input parameter\nc            isym.\nc\nc     idg    the first dimension of the array g as it appears in the\nc            program that calls shags. if isym equals zero then idg\nc            must be at least nlat.  if isym is nonzero then idg must\nc            be at least nlat/2 if nlat is even or at least (nlat+1)/2\nc            if nlat is odd.\nc\nc     jdg    the second dimension of the array g as it appears in the\nc            program that calls shags. jdg must be at least nlon.\nc\nc     mdab   the first dimension of the arrays a and b as it appears\nc            in the program that calls shags. if mdab is at least\nc            min0((nlon+2)/2,nlat) if nlon is even or at least\nc            min0((nlon+1)/2,nlat) if nlon is odd, the output will\nc            contain all coefficients.  Otherwise it will be truncated.\nc\nc     ndab   the second dimension of the arrays a and b as it appears\nc            in the program that calls shags. If ndab is at least nlat,\nc            the the output will  contain all coefficients.  Otherwise\nc            it will be truncated.\nc\nc     mdabmax, ndabmax -- will only compute coefficients a,b up to these\nc\nc     wshags an array which must be initialized by subroutine shagsi.\nc            once initialized, wshags can be used repeatedly by shags\nc            as long as nlat and nlon remain unchanged.  wshags must\nc            not be altered between calls of shags.\nc\nc     lshags the dimension of the array wshags as it appears in the\nc            program that calls shags. define\nc\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lshags must be at least\nc\nc            nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\nc\nc     work   a real work space which need not be saved\nc\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls shags. define\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc\nc            if isym is zero then lwork must be at least\nc\nc                  nlat*nlon*(nt+1)\nc\nc            if isym is nonzero then lwork must be at least\nc\nc                  l2*nlon*(nt+1)\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc     a,b    both a,b are two or three dimensional arrays (see input\nc            parameter nt) that contain the spherical harmonic\nc            coefficients in the representation of g(i,j) given in the\nc            discription of subroutine shags. for isym=0, a(m,n) and\nc            b(m,n) are given by the equations listed below. symmetric\nc            versions are used when isym is greater than zero.\nc\nc     definitions\nc\nc     1. the normalized associated legendre functions\nc\nc     pbar(m,n,theta) = dsqrt((2*n+1)*factorial(n-m)/(2*factorial(n+m)))\nc                       *sin(theta)**m/(2**n*factorial(n)) times the\nc                       (n+m)th derivative of (x**2-1)**n with respect\nc                       to x=cos(theta).\nc\nc     2. the fourier transform of g(i,j).\nc\nc     c(m,i)          = 2/nlon times the sum from j=1 to j=nlon of\nc                       g(i,j)*cos((m-1)*(j-1)*2*pi/nlon)\nc                       (the first and last terms in this sum\nc                       are divided by 2)\nc\nc     s(m,i)          = 2/nlon times the sum from j=2 to j=nlon of\nc                       g(i,j)*sin((m-1)*(j-1)*2*pi/nlon)\nc\nc\nc     3. the gaussian points and weights on the sphere\nc        (computed by subroutine gaqd).\nc\nc        theta(1),...,theta(nlat) (gaussian pts in radians)\nc        wts(1),...,wts(nlat) (corresponding gaussian weights)\nc\nc\nc     4. the maximum (plus one) longitudinal wave number\nc\nc            mmax = min0(nlat,(nlon+2)/2) if nlon is even or\nc            mmax = min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc\nc     then for m=0,...,mmax-1 and n=m,...,nlat-1 the arrays a,b\nc     are given by\nc\nc     a(m+1,n+1)     =  the sum from i=1 to i=nlat of\nc                       c(m+1,i)*wts(i)*pbar(m,n,theta(i))\nc\nc     b(m+1,n+1)      = the sum from i=1 to nlat of\nc                       s(m+1,i)*wts(i)*pbar(m,n,theta(i))\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of isym\nc            = 4  error in the specification of nt\nc            = 5  error in the specification of idg\nc            = 6  error in the specification of jdg\nc            = 9  error in the specification of lshags\nc            = 10 error in the specification of lwork\nc\nc\nc ****************************************************************\nc\nc     subroutine shagsi(nlat,nlon,wshags,lshags,work,lwork,dwork,ldwork,\nc    +                  ierror)\nc\nc     subroutine shagsi initializes the array wshags which can then\nc     be used repeatedly by subroutines shags. it precomputes\nc     and stores in wshags quantities such as gaussian weights,\nc     legendre polynomial coefficients, and fft trigonometric tables.\nc\nc     input parameters\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are compu\nc            in radians in theta(1),...,theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid poi\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than or equal to 4. the efficiency of the computation is\nc            improved when nlon is a product of small prime numbers.\nc\nc     wshags an array which must be initialized by subroutine shagsi.\nc            once initialized, wshags can be used repeatedly by shags\nc            as long as nlat and nlon remain unchanged.  wshags must\nc            not be altered between calls of shags.\nc\nc     lshags the dimension of the array wshags as it appears in the\nc            program that calls shags. define\nc\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lshags must be at least\nc\nc            nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\nc\nc     work   a real work space which need not be saved\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls shagsi. lwork must be at least\nc            4*nlat*(nlat+2)+2 in the routine calling shagsi\nc\nc     dwork   a double precision work array that does not have to be saved.\nc\nc     ldwork  the length of dwork in the calling routine.  ldwork must\nc             be at least nlat*(nlat+4)\nc\nc     output parameter\nc\nc     wshags an array which must be initialized before calling shags or\nc            once initialized, wshags can be used repeatedly by shags or\nc            as long as nlat and nlon remain unchanged.  wshags must not\nc            altered between calls of shasc.\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of lshags\nc            = 4  error in the specification of lwork\nc            = 5  error in the specification of ldwork\nc            = 6  failure in gaqd to compute gaussian points\nc                 (due to failure in eigenvalue routine)\nc\nc\nc ****************************************************************\n      subroutine shags(nstrg,nstrab,noffsg,noffsab,\n     1                 nlat,nlon,mode,nt,g,idg,jdg,a,b,mdab,ndab,\n     1                 mdabmax,ndabmax,wshags,lshags,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\nc     subroutine shags performs the spherical harmonic analysis on\nc     a gaussian grid on the array(s) in g and returns the coefficients\nc     in array(s) a,b. the necessary legendre polynomials are fully\nc     stored in this version.\nc\n      dimension g(nstrg,idg,jdg,1),a(nstrab,mdab,ndab,1),\n     1          b(nstrab,mdab,ndab,1),wshags(lshags),work(lwork)\nc     check input parameters\n      ierror = 1\n      if (nlat.lt.3) return\n      ierror = 2\n      if (nlon.lt.4) return\n      ierror = 3\nc     if (mode.lt.0 .or.mode.gt.2) return\nc     see note about isym above\n      if (mode.ne.0) return\nc     set m limit for pmn\n      l = min0((nlon+2)/2,nlat)\nc     set gaussian point nearest equator pointer\n      late = (nlat+mod(nlat,2))/2\nc     set number of grid points for analysis/synthesis\n      lat = nlat\n      if (mode.ne.0) lat = late\n      ierror = 4\n      if (nt.lt.1) return\n      ierror = 5\n      if (idg.lt.lat) return\n      ierror = 6\n      if (jdg.lt.nlon) return\nc     note that there are no requirements on the minimum values of\nc     mdab, ndab\nc     ierror = 7\nc     if(mdab .lt. l) return\nc     ierror = 8\nc     if(ndab .lt. nlat) return\n      l1 = l\n      l2 = late\n      ierror = 9\nc     check permanent work space length\nc\n      lp= nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\n      if(lshags.lt.lp) return\n      ng = 1;\n      if(noffsg.eq.-1 .and. noffsab.eq.-1) ng=nstrg\n      ierror = 10\nc     check temporary work space length\n      if (mode.eq.0 .and. lwork.lt.ng*nlat*nlon*(nt+1)) return\n      if (mode.ne.0 .and. lwork.lt.ng*l2*nlon*(nt+1)) return\n      ierror = 11\n      if((noffsg.eq.-1) .neqv. (noffsab.eq.-1)) return\n      if((noffsg.eq.-1) .and.  (nstrg.ne.nstrab)) return\n      if((noffsg.lt.-1) .or.  (noffsg.ge.nstrg)) return\n      if((noffsab.lt.-1) .or.  (noffsab.ge.nstrab)) return\n      ierror = 0\nc     set starting address for gaussian wts ,fft values,\nc     and fully stored legendre polys in wshags\n      iwts = 1\n      ifft = nlat+2*nlat*late+3*(l*(l-1)/2+(nlat-l)*(l-1))+1\n      ipmn = ifft+nlon+15\nc     set pointer for internal storage of g\n      iw = ng*lat*nlon*nt+1\n      call shags1(nstrg,nstrab,noffsg,noffsab,nlat,\n     1nlon,l,lat,mode,g,idg,jdg,nt,a,b,mdab,ndab,mdabmax,ndabmax,\n     1wshags(iwts),wshags(ifft),wshags(ipmn),late,work,ng,work(iw))\n      return\n      end\n\n      subroutine shags1(nstrgs,nstrab,noffsg,noffsab,\n     1                  nlat,nlon,l,lat,mode,gs,idg,jdg,nt,a,b,mdab,\n     1               ndab,mdabmax,ndabmax,wts,wfft,pmn,late,g,ng,work)\n      implicit double precision (a-h,o-z)\n      dimension gs(nstrgs,idg,jdg,nt),a(nstrab,mdab,ndab,nt),\n     1          b(nstrab,mdab,ndab,nt),g(ng,lat,nlon,nt)\n      dimension wfft(1),pmn(late,1),wts(nlat),work(1)\n      nlatmax=min0(nlat,ndabmax)\n      if(noffsg.eq.-1) then\n      ioffset0=2\n      ioffset1=1+nstrgs\n      else\n      ioffset0=1\n      ioffset1=1\n      end if\nc     set gs array internally in shags1\n      do 100 k=1,nt\n      do 100 j=1,nlon\n      do 100 i=1,lat\n      do 100 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncgs=noffsg+ioffset\n      g(ig,i,j,k) = gs(ncgs,i,j,k)\n  100 continue\n\nc     do fourier transform\n      do 101 k=1,nt\n      call hrfftf(ng*lat,nlon,g(1,1,1,k),ng*lat,wfft,work)\n  101 continue\n\nc     scale result\n      sfn = 2.0d0/dfloat(nlon)\n      do 102 k=1,nt\n      do 102 j=1,nlon\n      do 102 i=1,lat\n      do 102 ig=1,ng\n      g(ig,i,j,k) = sfn*g(ig,i,j,k)\n  102 continue\n\n\nc     compute using gaussian quadrature\nc     a(n,m) = s (ga(theta,m)*pnm(theta)*sin(theta)*dtheta)\nc     b(n,m) = s (gb(theta,m)*pnm(theta)*sin(theta)*dtheta)\nc     here ga,gb are the cos(phi),sin(phi) coefficients of\nc     the fourier expansion of g(theta,phi) in phi.  as a result\nc     of the above fourier transform they are stored in array\nc     g as follows:\nc     for each theta(i) and k= l-1\nc     ga(0),ga(1),gb(1),ga(2),gb(2),...,ga(k-1),gb(k-1),ga(k)\nc     correspond to\nc     g(i,1),g(i,2),g(i,3),g(i,4),g(i,5),...,g(i,2l-4),g(i,2l-3),g(i,2l-2)\nc     whenever 2*l-2 = nlon exactly\nc     initialize coefficients to zero\nc     do 103 k=1,nt\nc     do 103 np1=1,nlat\nc     do 103 mp1=1,l\nc     do 103 ioffset=ioffset0,ioffset1\nc     ncab=noffsab+ioffset\nc     a(ncab,mp1,np1,k) = 0.0d0\nc     b(ncab,mp1,np1,k) = 0.0d0\nc 103 continue\n      if(noffsab.eq.-1) then\n        a=0.0d0\n        b=0.0d0\n      else\n        a(1+noffsab,:,:,:)=0.0d0\n        b(1+noffsab,:,:,:)=0.0d0\n      end if\nc     set mp1 limit on b(mp1) calculation\n      lm1 = l\n      if (nlon .eq. l+l-2) lm1 = l-1\n      mp1max=min0(lm1,mdabmax)\n\n      if (mode.eq.0) then\nc     for full sphere (mode=0) and even/odd reduction:\nc     overwrite g(i) with (g(i)+g(nlat-i+1))*wts(i)\nc     overwrite g(nlat-i+1) with (g(i)-g(nlat-i+1))*wts(i)\n      nl2 = nlat/2\n      do 104 k=1,nt\n      do 104 j=1,nlon\n      do 105 i=1,nl2\n      do 105 ig=1,ng\n      is = nlat-i+1\n      t1 = g(ig,i,j,k)\n      t2 = g(ig,is,j,k)\n      g(ig,i,j,k) = wts(i)*(t1+t2)\n      g(ig,is,j,k) = wts(i)*(t1-t2)\n  105 continue\nc     adjust equator if necessary(nlat odd)\n      do 104 ig=1,ng\n      if (mod(nlat,2).ne.0) g(ig,late,j,k) = wts(late)*g(ig,late,j,k)\n  104 continue\nc     do 201 ioffset=ioffset0,ioffset1\nc     ig=ioffset-ioffset0+1\nc     ncab=noffsab+ioffset\nc     set m = 0 coefficients first\n      mp1 = 1\n      m = 0\n      mml1 = m*(2*nlat-m-1)/2\n      do 106 k=1,nt\n      do 106 i=1,late\n      is = nlat-i+1\n      do 107 np1=1,nlatmax,2\n      do 107 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\nc     n even\n      a(ncab,1,np1,k) = a(ncab,1,np1,k)\n     1                +g(ig,i,1,k)*pmn(i,mml1+np1)\n  107 continue\n      if(nlatmax.ge.2) then\n      do 108 np1=2,nlatmax,2\n      do 108 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\nc     n odd\n      a(ncab,1,np1,k) = a(ncab,1,np1,k)\n     1                +g(ig,is,1,k)*pmn(i,mml1+np1)\n  108 continue\n      end if\n  106 continue\nc     compute m.ge.1  coefficients next\n      if(mp1max.ge.2) then\n      do 109 mp1=2,mp1max\n      m = mp1-1\n      mml1 = m*(2*nlat-m-1)/2\n      mp2 = mp1+1\n      do 110 k=1,nt\n      do 111 i=1,late\n      is = nlat-i+1\nc     n-m even\n      if(nlatmax.ge.mp1) then\n      do 112 np1=mp1,nlatmax,2\n      do 112 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\n      a(ncab,mp1,np1,k) = a(ncab,mp1,np1,k)\n     1                +g(ig,i,2*m,k)*pmn(i,mml1+np1)\n      b(ncab,mp1,np1,k) = b(ncab,mp1,np1,k)\n     1                +g(ig,i,2*m+1,k)*pmn(i,mml1+np1)\n  112 continue\n      end if\nc     n-m odd\n      if(nlatmax.ge.mp2) then\n      do 113 np1=mp2,nlatmax,2\n      do 113 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\n      a(ncab,mp1,np1,k) = a(ncab,mp1,np1,k)\n     1                +g(ig,is,2*m,k)*pmn(i,mml1+np1)\n      b(ncab,mp1,np1,k) = b(ncab,mp1,np1,k)\n     1                  +g(ig,is,2*m+1,k)*pmn(i,mml1+np1)\n  113 continue\n      end if\n  111 continue\n  110 continue\n  109 continue\n      end if\n      if (nlon .eq. l+l-2) then\n      if(l.le.mdabmax) then\nc     compute m=l-1, n=l-1,l,...,nlat-1 coefficients\n      m = l-1\n      mml1 = m*(2*nlat-m-1)/2\n      do 114 k=1,nt\n      do 114 i=1,late\n      is = nlat-i+1\n      if(nlatmax.ge.l) then\n      do 124 np1=l,nlatmax,2\n      mn = mml1+np1\n      do 124 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\n      a(ncab,l,np1,k) = a(ncab,l,np1,k)+0.5d0*g(ig,i,nlon,k)*pmn(i,mn)\n  124 continue\n      end if\nc     n-m  odd\n      lp1 = l+1\n      if(nlatmax.ge.lp1) then\n      do 125 np1=lp1,nlatmax,2\n      mn = mml1+np1\n      do 125 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\n      a(ncab,l,np1,k) = a(ncab,l,np1,k)+0.5d0*g(ig,is,nlon,k)*pmn(i,mn)\n  125 continue\n      end if\n  114 continue\n      end if\n      end if\n\nc 201 continue\n      else\n\nc     do 202 ioffset=ioffset0,ioffset1\nc     ig=ioffset-ioffset0+1\nc     ncab=noffsab+ioffset\nc     half sphere\nc     overwrite g(i) with wts(i)*(g(i)+g(i)) for i=1,...,nlate/2\n      nl2 = nlat/2\n      do 116  k=1,nt\n      do 116 j=1,nlon\n      do 115 i=1,nl2\n      do 115 ig=1,ng\n      g(ig,i,j,k) = wts(i)*(g(ig,i,j,k)+g(ig,i,j,k))\n  115 continue\nc     adjust equator separately if a grid point\n      do 116 ig=1,ng\n      if (nl2.lt.late) g(ig,late,j,k) = wts(late)*g(ig,late,j,k)\n  116 continue\n\nc     set m = 0 coefficients first\n      mp1 = 1\n      m = 0\n      mml1 = m*(2*nlat-m-1)/2\n      ms = 1\n      if (mode.eq.1) ms = 2\n      do 117 k=1,nt\n      do 117 i=1,late\n      do 117 np1=ms,nlatmax,2\n      do 117 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\n      a(ncab,1,np1,k) = a(ncab,1,np1,k)\n     1                +g(ig,i,1,k)*pmn(i,mml1+np1)\n  117 continue\n\nc     compute m.ge.1  coefficients next\n      if(mp1max.ge.2) then\n      do 118 mp1=2,mp1max\n      m = mp1-1\n      mml1 = m*(2*nlat-m-1)/2\n      ms = mp1\n      if (mode.eq.1) ms = mp1+1\n      if(nlatmax.ge.ms) then\n      do 119 k=1,nt\n      do 119 i=1,late\n      do 119 np1=ms,nlatmax,2\n      do 119 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\n      a(ncab,mp1,np1,k) = a(ncab,mp1,np1,k)\n     1                +g(ig,i,2*m,k)*pmn(i,mml1+np1)\n      b(ncab,mp1,np1,k) = b(ncab,mp1,np1,k)\n     1                +g(ig,i,2*m+1,k)*pmn(i,mml1+np1)\n  119 continue\n      end if\n  118 continue\n      end if\n\n      if (nlon.eq.l+l-2 .and. l.le.mdabmax) then\nc     compute n=m=l-1 coefficients last\n      m = l-1\n      mml1 = m*(2*nlat-m-1)/2\nc     set starting n for mode even\n      ns = l\nc     set starting n for mode odd\n      if (mode.eq.1) ns = l+1\n      if(nlatmax.ge.ns) then\n      do 120 k=1,nt\n      do 120 i=1,late\n      do 120 np1=ns,nlatmax,2\n      mn = mml1+np1\n      do 120 ioffset=ioffset0,ioffset1\n      ig=ioffset-ioffset0+1\n      ncab=noffsab+ioffset\n      a(ncab,l,np1,k) = a(ncab,l,np1,k)+0.5d0*g(ig,i,nlon,k)*pmn(i,mn)\n  120 continue\n      end if\n      end if\n\nc 202 continue\n      end if\n\n      return\n      end\n      subroutine shagsi(nlat,nlon,wshags,lshags,work,lwork,dwork,ldwork,\n     +                  ierror)\n      implicit double precision (a-h,o-z)\nc\nc     this subroutine must be called before calling shags or shsgs with\nc     fixed nlat,nlon. it precomputes the gaussian weights, points\nc     and all necessary legendre polys and stores them in wshags.\nc     these quantities must be preserved when calling shags or shsgs\nc     repeatedly with fixed nlat,nlon.  dwork must be of length at\nc     least nlat*(nlat+4) in the routine calling shagsi.  This is\nc     not checked.  undetectable errors will result if dwork is\nc     smaller than nlat*(nlat+4).\nc\n      dimension wshags(lshags),work(lwork)\n      double precision dwork(ldwork)\n      ierror = 1\n      if (nlat.lt.3) return\n      ierror = 2\n      if (nlon.lt.4) return\nc     set triangular truncation limit for spherical harmonic basis\n      l = min0((nlon+2)/2,nlat)\nc     set equator or nearest point (if excluded) pointer\n      late = (nlat+1)/2\n      l1 = l\n      l2 = late\nc     check permanent work space length\n      ierror = 3\n      lp=nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\n      if(lshags.lt.lp) return\n      ierror = 4\nc     check temporary work space\n      if (lwork.lt.4*nlat*(nlat+2)+2) return\n      ierror = 5\nc     check double precision temporary space\n      if (ldwork .lt. nlat*(nlat+4)) return\n      ierror = 0\nc     set preliminary quantites needed to compute and store legendre polys\n      ldw = nlat*(nlat+4)\n      call shagsp(nlat,nlon,wshags,lshags,dwork,ldwork,ierror)\n      if (ierror.ne.0) return\nc     set legendre poly pointer in wshags\n      ipmnf = nlat+2*nlat*late+3*(l*(l-1)/2+(nlat-l)*(l-1))+nlon+16\n      call shagss1(nlat,l,late,wshags,work,wshags(ipmnf))\n      return\n      end\n      subroutine shagss1(nlat,l,late,w,pmn,pmnf)\n      implicit double precision (a-h,o-z)\n      dimension w(1),pmn(nlat,late,3),pmnf(late,1)\nc     compute and store legendre polys for i=1,...,late,m=0,...,l-1\nc     and n=m,...,l-1\n      do i=1,nlat\n\tdo j=1,late\n\t  do k=1,3\n\t   pmn(i,j,k) = 0.0d0\n\t  end do\n\tend do\n      end do\n      km0=1\n      km1=2\n      km2=3\n      do 100 mp1=1,l\n      m = mp1-1\n      mml1 = m*(2*nlat-m-1)/2\nc     compute pmn for n=m,...,nlat-1 and i=1,...,(l+1)/2\n      mode = 0\n      call legin(mode,l,nlat,m,w,pmn,km,km0,km1,km2)\nc     store above in pmnf\n      do 101 np1=mp1,nlat\n      mn = mml1+np1\n      do 102 i=1,late\n      pmnf(i,mn) = pmn(np1,i,km)\n  102 continue\n  101 continue\n  100 continue\n      return\n      end\n      subroutine shagsp(nlat,nlon,wshags,lshags,dwork,ldwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension wshags(lshags)\n      double precision dwork(ldwork)\n      ierror = 1\n      if (nlat.lt.3) return\n      ierror = 2\n      if (nlon.lt.4) return\nc     set triangular truncation limit for spherical harmonic basis\n      l = min0((nlon+2)/2,nlat)\nc     set equator or nearest point (if excluded) pointer\n      late = (nlat+mod(nlat,2))/2\n      l1 = l\n      l2 = late\n      ierror = 3\nc     check permanent work space length\n      if (lshags .lt. nlat*(2*l2+3*l1-2)+3*l1*(1-l1)/2+nlon+15)return\n      ierror = 4\nc     if (lwork.lt.4*nlat*(nlat+2)+2) return\n      if (ldwork.lt.nlat*(nlat+4))return\n      ierror = 0\nc     set pointers\n      i1 = 1\n      i2 = i1+nlat\n      i3 = i2+nlat*late\n      i4 = i3+nlat*late\n      i5 = i4+l*(l-1)/2 +(nlat-l)*(l-1)\n      i6 = i5+l*(l-1)/2 +(nlat-l)*(l-1)\n      i7 = i6+l*(l-1)/2 +(nlat-l)*(l-1)\nc     set indices in temp work for double precision gaussian wts and pts\n      idth = 1\nc     idwts = idth+2*nlat\nc     iw = idwts+2*nlat\n      idwts = idth+nlat\n      iw = idwts+nlat\n      call shagsp1(nlat,nlon,l,late,wshags(i1),wshags(i2),wshags(i3),\n     1wshags(i4),wshags(i5),wshags(i6),wshags(i7),dwork(idth),\n     2dwork(idwts),dwork(iw),ierror)\n      if (ierror.ne.0) ierror = 6\n      return\n      end\n\n      subroutine shagsp1(nlat,nlon,l,late,wts,p0n,p1n,abel,bbel,cbel,\n     +                   wfft,dtheta,dwts,work,ier)\n      implicit double precision (a-h,o-z)\n      dimension wts(nlat),p0n(nlat,late),p1n(nlat,late),abel(1),bbel(1),\n     1 cbel(1),wfft(1),dtheta(nlat),dwts(nlat)\n      double precision pb,dtheta,dwts,work(*)\n      indx(m,n) = (n-1)*(n-2)/2+m-1\n      imndx(m,n) = l*(l-1)/2+(n-l-1)*(l-1)+m-1\n      call hrffti(nlon,wfft)\n\nc     compute double precision gaussian points and weights\nc     lw = 4*nlat*(nlat+2)\n      lw = nlat*(nlat+2)\n      call gaqd(nlat,dtheta,dwts,work,lw,ier)\n      if (ier.ne.0) return\n\nc     store gaussian weights single precision to save computation\nc     in inner loops in analysis\n      do 100 i=1,nlat\n      wts(i) = dwts(i)\n  100 continue\nc     initialize p0n,p1n using double precision dnlfk,dnlft\n      do 101 np1=1,nlat\n      do 101 i=1,late\n      p0n(np1,i) = 0.0d0\n      p1n(np1,i) = 0.0d0\n  101 continue\nc     compute m=n=0 legendre polynomials for all theta(i)\n      np1 = 1\n      n = 0\n      m = 0\n      call dnlfk(m,n,work)\n      do 103 i=1,late\n      call dnlft(m,n,dtheta(i),work,pb)\n      p0n(1,i) = pb\n  103 continue\nc     compute p0n,p1n for all theta(i) when n.gt.0\n      do 104 np1=2,nlat\n      n = np1-1\n      m = 0\n      call dnlfk(m,n,work)\n      do 105 i=1,late\n      call dnlft(m,n,dtheta(i),work,pb)\n      p0n(np1,i) = pb\n  105 continue\nc     compute m=1 legendre polynomials for all n and theta(i)\n      m = 1\n      call dnlfk(m,n,work)\n      do 106 i=1,late\n      call dnlft(m,n,dtheta(i),work,pb)\n      p1n(np1,i) = pb\n  106 continue\n  104 continue\nc\nc     compute and store swarztrauber recursion coefficients\nc     for 2.le.m.le.n and 2.le.n.le.nlat in abel,bbel,cbel\n      do 107 n=2,nlat\n      mlim = min0(n,l)\n      do 107 m=2,mlim\n      imn = indx(m,n)\n      if (n.ge.l) imn = imndx(m,n)\n      abel(imn)=dsqrt(dfloat((2*n+1)*(m+n-2)*(m+n-3))/\n     1               dfloat(((2*n-3)*(m+n-1)*(m+n))))\n      bbel(imn)=dsqrt(dfloat((2*n+1)*(n-m-1)*(n-m))/\n     1               dfloat(((2*n-3)*(m+n-1)*(m+n))))\n      cbel(imn)=dsqrt(dfloat((n-m+1)*(n-m+2))/\n     1               dfloat(((n+m-1)*(n+m))))\n  107 continue\n      return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/shsgs.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc ... file shsgs.f\nc\nc     this file contains code and documentation for subroutines\nc     shsgs and shsgsi\nc\nc ... files which must be loaded with shsgs.f\nc\nc     sphcom.f, hrfft.f, gaqd.f\nc\nc     subroutine shsgs(nstrg,nstrab,noffsg,noffsab,\nc                      nlat,nlon,isym,nt,g,idg,jdg,a,b,mdab,ndab,\nc    1                 mdabmax,ndabmax,\nc    1                    wshsgs,lshsgs,work,lwork,ierror)\nc\nc     subroutine shsgs performs the spherical harmonic synthesis\nc     on the arrays a and b and stores the result in the array g.\nc     the synthesis is performed on an equally spaced longitude grid\nc     and a gaussian colatitude grid.  the associated legendre functions\nc     are stored rather than recomputed as they are in subroutine\nc     shsgc.  the synthesis is described below at output parameter\nc     g.\nc\nc\nc     input parameters\nc\nc     nstrg,nstrab   stride in g,a,b\nc     noffsg,noffsab offset in g,a,b -- must be between 0 and stride-1\nc            Alternatively, when the two strides equal, you can set both\nc            to -1 in which case the output will be computed for all\nc            values of offset between 0 and stride-1.\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are compu\nc            in radians in theta(1),...,theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid poi\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than or equal to 4. the efficiency of the computation is\nc            improved when nlon is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for isym==0.  For this reason an error code will\nc           be returned if isym!=0.\nc\nc     isym   = 0  no symmetries exist about the equator. the synthesis\nc                 is performed on the entire sphere.  i.e. on the\nc                 array g(i,j) for i=1,...,nlat and j=1,...,nlon.\nc                 (see description of g below)\nc\nc            = 1  g is antisymmetric about the equator. the synthesis\nc                 is performed on the northern hemisphere only.  i.e.\nc                 if nlat is odd the synthesis is performed on the\nc                 array g(i,j) for i=1,...,(nlat+1)/2 and j=1,...,nlon.\nc                 if nlat is even the synthesis is performed on the\nc                 array g(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc\nc            = 2  g is symmetric about the equator. the synthesis is\nc                 performed on the northern hemisphere only.  i.e.\nc                 if nlat is odd the synthesis is performed on the\nc                 array g(i,j) for i=1,...,(nlat+1)/2 and j=1,...,nlon.\nc                 if nlat is even the synthesis is performed on the\nc                 array g(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc     nt     the number of syntheses.  in the program that calls shsgs,\nc            the arrays g,a and b can be three dimensional in which\nc            case multiple synthesis will be performed.  the third\nc            index is the synthesis index which assumes the values\nc            k=1,...,nt.  for a single synthesis set nt=1. the\nc            discription of the remaining parameters is simplified\nc            by assuming that nt=1 or that the arrays g,a and b\nc            have only two dimensions.\nc\nc     idg    the first dimension of the array g as it appears in the\nc            program that calls shagc. if isym equals zero then idg\nc            must be at least nlat.  if isym is nonzero then idg must\nc            be at least nlat/2 if nlat is even or at least (nlat+1)/2\nc            if nlat is odd.\nc\nc     jdg    the second dimension of the array g as it appears in the\nc            program that calls shagc. jdg must be at least nlon.\nc\nc     a,b    two or three dimensional arrays (see the input parameter\nc            nt) that contain the coefficients in the spherical harmonic\nc            expansion of g(i,j) given below at the definition of the\nc            output parameter g.  a(m,n) and b(m,n) are defined for\nc            indices m=1,...,mmax and n=m,...,nlat where mmax is the\nc            maximum (plus one) longitudinal wave number given by\nc            mmax = min0(nlat,(nlon+2)/2) if nlon is even or\nc            mmax = min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc     mdab   the first dimension of the arrays a and b as it appears\nc            in the program that calls shsgs. mdab must be at least\nc            min0((nlon+2)/2,nlat) if nlon is even or at least\nc            min0((nlon+1)/2,nlat) if nlon is odd IF IT CONTAINS\nc            ALL COEFFICIENTS.  If smaller, the higher coefficients\nc            are ignored (i.e., treated as if they were zero).\nc\nc     ndab   the second dimension of the arrays a and b as it appears\nc            in the program that calls shsgs. ndab must be at least nlat\nc            IF IT CONTAINS ALL COEFFICIENTS. Otherwise the higher coefs\nc            are ignored (i.e., treated as if they were zero).\nc\nc     mdabmax, ndabmax -- will ignore coefficients a,b with index higher\nc            than these\nc\nc     wshsgs an array which must be initialized by subroutine shsgsi.\nc            once initialized, wshsgs can be used repeatedly by shsgs\nc            as long as nlat and nlon remain unchanged.  wshsgs must\nc            not be altered between calls of shsgs.\nc\nc     lshsgs the dimension of the array wshsgs as it appears in the\nc            program that calls shsgs. define\nc\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lshsgs must be at least\nc\nc            nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\nc\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls shsgs. define\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc\nc            if isym is zero then lwork must be at least\nc\nc                  nlat*nlon*(nt+1)\nc\nc            if isym is nonzero then lwork must be at least\nc\nc                  l2*nlon*(nt+1)\nc\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc     g      a two or three dimensional array (see input parameter nt)\nc            that contains the discrete function which is synthesized.\nc            g(i,j) contains the value of the function at the gaussian\nc            colatitude point theta(i) and longitude point\nc            phi(j) = (j-1)*2*pi/nlon. the index ranges are defined\nc            above at the input parameter isym.  for isym=0, g(i,j)\nc            is given by the the equations listed below.  symmetric\nc            versions are used when isym is greater than zero.\nc\nc     the normalized associated legendre functions are given by\nc\nc     pbar(m,n,theta) = dsqrt((2*n+1)*factorial(n-m)/(2*factorial(n+m)))\nc                       *sin(theta)**m/(2**n*factorial(n)) times the\nc                       (n+m)th derivative of (x**2-1)**n with respect\nc                       to x=cos(theta)\nc\nc     define the maximum (plus one) longitudinal wave number\nc     as   mmax = min0(nlat,(nlon+2)/2) if nlon is even or\nc          mmax = min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc     then g(i,j) = the sum from n=0 to n=nlat-1 of\nc\nc                   .5*pbar(0,n,theta(i))*a(1,n+1)\nc\nc              plus the sum from m=1 to m=mmax-1 of\nc\nc                   the sum from n=m to n=nlat-1 of\nc\nc              pbar(m,n,theta(i))*(a(m+1,n+1)*cos(m*phi(j))\nc                                    -b(m+1,n+1)*sin(m*phi(j)))\nc\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of isym\nc            = 4  error in the specification of nt\nc            = 5  error in the specification of idg\nc            = 6  error in the specification of jdg\nc            = 9  error in the specification of lshsgs\nc            = 10 error in the specification of lwork\nc\nc\nc ****************************************************************\nc\nc     subroutine shsgsi(nlat,nlon,wshsgs,lshsgs,work,lwork,dwork,ldwork,\nc    +                  ierror)\nc\nc     subroutine shsgsi initializes the array wshsgs which can then\nc     be used repeatedly by subroutines shsgs. it precomputes\nc     and stores in wshsgs quantities such as gaussian weights,\nc     legendre polynomial coefficients, and fft trigonometric tables.\nc\nc     input parameters\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are compu\nc            in radians in theta(1),...,theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid poi\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than or equal to 4. the efficiency of the computation is\nc            improved when nlon is a product of small prime numbers.\nc\nc     wshsgs an array which must be initialized by subroutine shsgsi.\nc            once initialized, wshsgs can be used repeatedly by shsgs\nc            as long as nlat and nlon remain unchanged.  wshsgs must\nc            not be altered between calls of shsgs.\nc\nc     lshsgs the dimension of the array wshsgs as it appears in the\nc            program that calls shsgs. define\nc\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lshsgs must be at least\nc\nc            nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\nc\nc     work   a real work space which need not be saved\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls shsgsi. lwork must be at least\nc            4*nlat*(nlat+2)+2 in the routine calling shsgsi\nc\nc     dwork   a double precision work array that does not have to be saved.\nc\nc     ldwork  the length of dwork in the calling routine.  ldwork must\nc             be at least nlat*(nlat+4)\nc\nc     output parameter\nc\nc     wshsgs an array which must be initialized before calling shsgs or\nc            once initialized, wshsgs can be used repeatedly by shsgs or\nc            as long as nlat and nlon remain unchanged.  wshsgs must not\nc            altered between calls of shsgs.\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of lshsgs\nc            = 4  error in the specification of lwork\nc            = 5  error in the specification of ldwork\nc            = 5  failure in gaqd to compute gaussian points\nc                 (due to failure in eigenvalue routine)\nc\nc\n      subroutine shsgs(nstrg,nstrab,noffsg,noffsab,\n     1                 nlat,nlon,mode,nt,g,idg,jdg,a,b,mdab,ndab,\n     1                 mdabmax,ndabmax,wshsgs,lshsgs,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension g(nstrg,idg,jdg,1),a(nstrab,mdab,ndab,1),\n     1          b(nstrab,mdab,ndab,1),wshsgs(lshsgs),work(lwork)\nc     check input parameters\n      ierror = 1\n      if (nlat.lt.3) return\n      ierror = 2\n      if (nlon.lt.4) return\n      ierror = 3\nc     if (mode.lt.0 .or.mode.gt.2) return\nc     see note about isym above\n      if (mode.ne.0) return\n      ierror = 4\n      if (nt.lt.1) return\nc     set limit on m subscript\n      l = min0((nlon+2)/2,nlat)\nc     set gaussian point nearest equator pointer\n      late = (nlat+mod(nlat,2))/2\nc     set number of grid points for analysis/synthesis\n      lat = nlat\n      if (mode.ne.0) lat = late\n      ierror = 5\n      if (idg.lt.lat) return\n      ierror = 6\n      if (jdg.lt.nlon) return\nc     ierror = 7\nc     if(mdab .lt. l) return\nc     ierror = 8\nc     if(ndab .lt. nlat) return\n      l1 = l\n      l2 = late\n      ierror = 9\nc     check permanent work space length\n      lp=nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\n      if(lshsgs.lt.lp) return\nc     check temporary work space length\n      ng = 1;\n      if(noffsg.eq.-1 .and. noffsab.eq.-1) ng=nstrg\n      ierror = 10\n      if (mode.eq.0 .and. lwork.lt.ng*nlat*nlon*(nt+1)) return\n      if (mode.ne.0 .and. lwork.lt.ng*l2*nlon*(nt+1)) return\n      ierror = 11\n      if((noffsg.eq.-1) .neqv. (noffsab.eq.-1)) return\n      if((noffsg.eq.-1) .and.  (nstrg.ne.nstrab)) return\n      if((noffsg.lt.-1) .or.  (noffsg.ge.nstrg)) return\n      if((noffsab.lt.-1) .or.  (noffsab.ge.nstrab)) return\n      ierror = 0\nc     starting address for fft values and legendre polys in wshsgs\n      ifft = nlat+2*nlat*late+3*(l*(l-1)/2+(nlat-l)*(l-1))+1\n      ipmn = ifft+nlon+15\nc     set pointer for internal storage of g\n      iw = ng*lat*nlon*nt+1\n      call shsgs1(nstrg,nstrab,noffsg,noffsab,\n     1            nlat,nlon,l,lat,mode,g,idg,jdg,nt,a,b,mdab,ndab,\n     1            mdabmax,ndabmax,\n     1            wshsgs(ifft),wshsgs(ipmn),late,work,ng,work(iw))\n      return\n      end\n\n      subroutine shsgs1(nstrgs,nstrab,noffsgs,noffsab,\n     1                  nlat,nlon,l,lat,mode,gs,idg,jdg,nt,a,b,mdab,\n     1                  ndab,mdabmax,ndabmax,wfft,pmn,late,g,ng,work)\n      implicit double precision (a-h,o-z)\n      dimension gs(nstrgs,idg,jdg,nt),\n     1   a(nstrab,mdab,ndab,nt),b(nstrab,mdab,ndab,nt)\n      dimension wfft(1),pmn(late,1),g(ng,lat,nlon,nt),work(1)\n      nlatmax=min0(nlat,ndabmax)\n\nc     ncgs=1+noffsgs\nc     ncab=1+noffsab\n\n      if(noffsgs.eq.-1) then\n      ioffset0=2\n      ioffset1=nstrgs+1\n      else\n      ioffset0=1\n      ioffset1=1\n      end if\n\n\nc     reconstruct fourier coefficients in g on gaussian grid\nc     using coefficients in a,b\n\nc     initialize to zero\n      do 100 k=1,nt\n      do 100 j=1,nlon\n      do 100 i=1,lat\n      do 100 ig=1,ng\n      g(ig,i,j,k) = 0.0d0\n  100 continue\n\n      lm1 = l\n      if (nlon .eq. l+l-2) lm1 = l-1\n\n      mp1max = min0(lm1,mdabmax)\n\n      if (mode.eq.0) then\n\nc     do 201 ioffset=ioffset0,ioffset1\nc     ncab=noffsab+ioffset\nc     ig=ioffset-ioffset0+1\n\nc     set first column in g\n      m = 0\n      mml1 = m*(2*nlat-m-1)/2\n      do 101 k=1,nt\nc     n even\n      do 102 np1=1,nlatmax,2\n      mn = mml1+np1\n      do 102 i=1,late\n      do 102 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,i,1,k) = g(ig,i,1,k)+a(ncab,1,np1,k)*pmn(i,mn)\n  102 continue\nc     n odd\n      nl2 = nlat/2\n      if(nlatmax.ge.2) then\n      do 103 np1=2,nlatmax,2\n      mn = mml1+np1\n      do 103 i=1,nl2\n      is = nlat-i+1\n      do 103 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,is,1,k) = g(ig,is,1,k)+a(ncab,1,np1,k)*pmn(i,mn)\n  103 continue\n      end if\n  101 continue\n\nc     restore m=0 coefficients from odd/even\n      do 112 k=1,nt\n      do 112 i=1,nl2\n      do 112 ig=1,ng\n      is = nlat-i+1\n      t1 = g(ig,i,1,k)\n      t3 = g(ig,is,1,k)\n      g(ig,i,1,k) = t1+t3\n      g(ig,is,1,k) = t1-t3\n  112 continue\n\nc     sweep interior columns of g\n      if(mp1max.ge.2) then\n      do 104 mp1=2,mp1max\n      m = mp1-1\n      mml1 = m*(2*nlat-m-1)/2\n      mp2 = m+2\n      do 105 k=1,nt\nc     for n-m even store (g(i,p,k)+g(nlat-i+1,p,k))/2 in g(i,p,k) p=2*m,2*m+1\nc     for i=1,...,late\n      if(nlatmax.ge.mp1) then\n      do 106 np1=mp1,nlatmax,2\n      mn = mml1+np1\n      do 107 i=1,late\n      do 107 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,i,2*m,k) = g(ig,i,2*m,k)+a(ncab,mp1,np1,k)*pmn(i,mn)\n      g(ig,i,2*m+1,k) = g(ig,i,2*m+1,k)+b(ncab,mp1,np1,k)*pmn(i,mn)\n  107 continue\n  106 continue\n      end if\n\nc     for n-m odd store g(i,p,k)-g(nlat-i+1,p,k) in g(nlat-i+1,p,k)\nc     for i=1,...,nlat/2 (p=2*m,p=2*m+1)\n      if(nlatmax.ge.mp2) then\n      do 108 np1=mp2,nlatmax,2\n      mn = mml1+np1\n      do 109 i=1,nl2\n      is = nlat-i+1\n      do 109 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,is,2*m,k) = g(ig,is,2*m,k)+a(ncab,mp1,np1,k)*pmn(i,mn)\n      g(ig,is,2*m+1,k) = g(ig,is,2*m+1,k)+b(ncab,mp1,np1,k)*pmn(i,mn)\n  109 continue\n  108 continue\n      end if\n\nc     now set fourier coefficients using even-odd reduction above\n      do 110 i=1,nl2\n      do 110 ig=1,ng\n      is = nlat-i+1\n      t1 = g(ig,i,2*m,k)\n      t2 = g(ig,i,2*m+1,k)\n      t3 = g(ig,is,2*m,k)\n      t4 = g(ig,is,2*m+1,k)\n      g(ig,i,2*m,k) = t1+t3\n      g(ig,i,2*m+1,k) = t2+t4\n      g(ig,is,2*m,k) = t1-t3\n      g(ig,is,2*m+1,k) = t2-t4\n  110 continue\n\n  105 continue\n  104 continue\n      end if\n\nc     set last column (using a only) if necessary\n      if (nlon.eq. l+l-2) then\n      if (l.le.mdabmax) then\n      m = l-1\n      mml1 = m*(2*nlat-m-1)/2\n      do 111 k=1,nt\nc     n-m even\n      if(nlatmax.ge.l) then\n      do 131 np1=l,nlatmax,2\n      mn = mml1+np1\n      do 131 i=1,late\n      do 131 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,i,nlon,k) = g(ig,i,nlon,k)+2.0d0*a(ncab,l,np1,k)*pmn(i,mn)\n\n  131 continue\n      end if\n      lp1 = l+1\nc     n-m odd\n      if(nlatmax.ge.lp1) then\n      do 132 np1=lp1,nlatmax,2\n      mn = mml1+np1\n      do 132 i=1,nl2\n      is = nlat-i+1\n      do 132 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,is,nlon,k) = g(ig,is,nlon,k)+2.0d0*a(ncab,l,np1,k)*pmn(i,mn)\n  132 continue\n      end if\n      do 133 i=1,nl2\n      do 133 ig=1,ng\n      is = nlat-i+1\n      t1 = g(ig,i,nlon,k)\n      t3 = g(ig,is,nlon,k)\n      g(ig,i,nlon,k)= t1+t3\n      g(ig,is,nlon,k)= t1-t3\n  133 continue\n  111 continue\n      end if\n      end if\n  201 continue\n\n      else\nc     half sphere (mode.ne.0)\n\nc     set first column in g\n      m = 0\n      mml1 = m*(2*nlat-m-1)/2\n      meo = 1\n      if (mode.eq.1) meo = 2\n      ms = m+meo\n      if(nlatmax.ge.ms) then\n      do 113 k=1,nt\n      do 113 np1=ms,nlatmax,2\n      mn = mml1+np1\n      do 113 i=1,late\n      do 113 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,i,1,k) = g(ig,i,1,k)+a(ncab,1,np1,k)*pmn(i,mn)\n  113 continue\n      end if\n\nc     sweep interior columns of g\n\n      if(mp1max.ge.2) then\n      do 114 mp1=2,mp1max\n      m = mp1-1\n      mml1 = m*(2*nlat-m-1)/2\n      ms = m+meo\n      if(nlatmax.ge.ms) then\n      do 115 k=1,nt\n      do 115 np1=ms,nlatmax,2\n      mn = mml1+np1\n      do 115 i=1,late\n      do 115 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,i,2*m,k) = g(ig,i,2*m,k)+a(ncab,mp1,np1,k)*pmn(i,mn)\n      g(ig,i,2*m+1,k) = g(ig,i,2*m+1,k)+b(ncab,mp1,np1,k)*pmn(i,mn)\n  115 continue\n      end if\n  114 continue\n      end if\n\n      if (nlon.eq.l+l-2 .and. l.le.mdabmax) then\nc     set last column\n      m = l-1\n      mml1 = m*(2*nlat-m-1)/2\n      ns = l\n      if (mode.eq.1) ns = l+1\n      if(nlatmax.ge.ns) then\n      do 116 k=1,nt\n      do 116 np1=ns,nlatmax,2\n      mn = mml1+np1\n      do 116 i=1,late\n      do 116 ioffset=ioffset0,ioffset1\n      ncab=noffsab+ioffset\n      ig=ioffset-ioffset0+1\n      g(ig,i,nlon,k) = g(ig,i,nlon,k)+2.0d0*a(ncab,l,np1,k)*pmn(i,mn)\n  116 continue\n      end if\n      end if\n  202 continue\n\n      end if\n\nc     do inverse fourier transform\n      do 120 k=1,nt\n      call hrfftb(ng*lat,nlon,g(1,1,1,k),ng*lat,wfft,work)\n  120 continue\nc     scale output in gs\n      do 122 k=1,nt\n      do 122 j=1,nlon\n      do 122 i=1,lat\n      do 122 ioffset=ioffset0,ioffset1\n      ncgs=noffsgs+ioffset\n      ig=ioffset-ioffset0+1\n      gs(ncgs,i,j,k) = 0.5d0*g(ig,i,j,k)\n  122 continue\n\n      return\n      end\n      subroutine shsgsi(nlat,nlon,wshsgs,lshsgs,work,lwork,dwork,ldwork,\n     +                  ierror)\n      implicit double precision (a-h,o-z)\nc\nc     this subroutine must be called before calling shags or shsgs with\nc     fixed nlat,nlon. it precomputes the gaussian weights, points\nc     and all necessary legendre polys and stores them in wshsgs.\nc     these quantities must be preserved when calling shsgs\nc     repeatedly with fixed nlat,nlon.\nc\n      dimension wshsgs(lshsgs),work(lwork)\n      double precision dwork(ldwork)\n      ierror = 1\n      if (nlat.lt.3) return\n      ierror = 2\n      if (nlon.lt.4) return\nc     set triangular truncation limit for spherical harmonic basis\n      l = min0((nlon+2)/2,nlat)\nc     set equator or nearest point (if excluded) pointer\n      late = (nlat+1)/2\n      l1 = l\n      l2 = late\nc     check permanent work space length\n      ierror = 3\n      lp=nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\n      if(lshsgs.lt.lp) return\n      ierror = 4\nc     check temporary work space\n      if (lwork.lt.4*nlat*(nlat+2)+2) return\n      ierror = 5\n      if (ldwork .lt. nlat*(nlat+4)) return\n      ierror = 0\nc     set preliminary quantites needed to compute and store legendre polys\n      ldw = nlat*(nlat+4)\n      call shsgsp(nlat,nlon,wshsgs,lshsgs,dwork,ldwork,ierror)\n      if (ierror.ne.0) return\nc     set legendre poly pointer in wshsgs\n      ipmnf = nlat+2*nlat*late+3*(l*(l-1)/2+(nlat-l)*(l-1))+nlon+16\n      call shsgss1(nlat,l,late,wshsgs,work,wshsgs(ipmnf))\n      return\n      end\n      subroutine shsgss1(nlat,l,late,w,pmn,pmnf)\n      implicit double precision (a-h,o-z)\n      dimension w(1),pmn(nlat,late,3),pmnf(late,1)\nc     compute and store legendre polys for i=1,...,late,m=0,...,l-1\nc     and n=m,...,l-1\n      do i=1,nlat\n\tdo j=1,late\n\t  do k=1,3\n\t    pmn(i,j,k) = 0.0d0\n\t  end do\n\t end do\n      end do\n      km0=1\n      km1=2\n      km2=3\n      do 100 mp1=1,l\n      m = mp1-1\n      mml1 = m*(2*nlat-m-1)/2\nc     compute pmn for n=m,...,nlat-1 and i=1,...,(l+1)/2\n      mode = 0\n      call legin(mode,l,nlat,m,w,pmn,km,km0,km1,km2)\nc     store above in pmnf\n      do 101 np1=mp1,nlat\n      mn = mml1+np1\n      do 102 i=1,late\n      pmnf(i,mn) = pmn(np1,i,km)\n  102 continue\n  101 continue\n  100 continue\n      return\n      end\n      subroutine shsgsp(nlat,nlon,wshsgs,lshsgs,dwork,ldwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension wshsgs(lshsgs)\n      double precision dwork(ldwork)\n      ierror = 1\n      if (nlat.lt.3) return\n      ierror = 2\n      if (nlon.lt.4) return\nc     set triangular truncation limit for spherical harmonic basis\n      l = min0((nlon+2)/2,nlat)\nc     set equator or nearest point (if excluded) pointer\n      late = (nlat+mod(nlat,2))/2\n      l1 = l\n      l2 = late\n      ierror = 3\nc     check permanent work space length\n      if (lshsgs .lt. nlat*(2*l2+3*l1-2)+3*l1*(1-l1)/2+nlon+15)return\n      ierror = 4\nc     if (lwork.lt.4*nlat*(nlat+2)+2) return\n      if (ldwork .lt. nlat*(nlat+4)) return\n      ierror = 0\nc     set pointers\n      i1 = 1\n      i2 = i1+nlat\n      i3 = i2+nlat*late\n      i4 = i3+nlat*late\n      i5 = i4+l*(l-1)/2 +(nlat-l)*(l-1)\n      i6 = i5+l*(l-1)/2 +(nlat-l)*(l-1)\n      i7 = i6+l*(l-1)/2 +(nlat-l)*(l-1)\nc     set indices in temp work for double precision gaussian wts and pts\n      idth = 1\nc     idwts = idth+2*nlat\nc     iw = idwts+2*nlat\n      idwts = idth+nlat\n      iw = idwts+nlat\n      call shsgsp1(nlat,nlon,l,late,wshsgs(i1),wshsgs(i2),wshsgs(i3),\n     1wshsgs(i4),wshsgs(i5),wshsgs(i6),wshsgs(i7),dwork(idth),\n     2dwork(idwts),dwork(iw),ierror)\n      if (ierror.ne.0) ierror = 6\n      return\n      end\n      subroutine shsgsp1(nlat,nlon,l,late,wts,p0n,p1n,abel,bbel,cbel,\n     +                   wfft,dtheta,dwts,work,ier)\n      implicit double precision (a-h,o-z)\n      dimension wts(nlat),p0n(nlat,late),p1n(nlat,late),abel(1),bbel(1),\n     1 cbel(1),wfft(1),dtheta(nlat),dwts(nlat)\n      double precision pb,dtheta,dwts,work(*)\n      indx(m,n) = (n-1)*(n-2)/2+m-1\n      imndx(m,n) = l*(l-1)/2+(n-l-1)*(l-1)+m-1\n      call hrffti(nlon,wfft)\nc\nc     compute double precision gaussian points and weights\nc\n      lw = nlat*(nlat+2)\n      call gaqd(nlat,dtheta,dwts,work,lw,ier)\n      if (ier.ne.0) return\n\nc     store gaussian weights single precision to save computation\nc     in inner loops in analysis\n      do 100 i=1,nlat\n      wts(i) = dwts(i)\n  100 continue\n\nc     initialize p0n,p1n using double precision dnlfk,dnlft\n      do 101 np1=1,nlat\n      do 101 i=1,late\n      p0n(np1,i) = 0.0d0\n      p1n(np1,i) = 0.0d0\n  101 continue\nc     compute m=n=0 legendre polynomials for all theta(i)\n      np1 = 1\n      n = 0\n      m = 0\n      call dnlfk(m,n,work)\n      do 103 i=1,late\n      call dnlft(m,n,dtheta(i),work,pb)\n      p0n(1,i) = pb\n  103 continue\nc     compute p0n,p1n for all theta(i) when n.gt.0\n      do 104 np1=2,nlat\n      n = np1-1\n      m = 0\n      call dnlfk(m,n,work)\n      do 105 i=1,late\n      call dnlft(m,n,dtheta(i),work,pb)\n      p0n(np1,i) = pb\n  105 continue\nc     compute m=1 legendre polynomials for all n and theta(i)\n      m = 1\n      call dnlfk(m,n,work)\n      do 106 i=1,late\n      call dnlft(m,n,dtheta(i),work,pb)\n      p1n(np1,i) = pb\n  106 continue\n  104 continue\nc\nc     compute and store swarztrauber recursion coefficients\nc     for 2.le.m.le.n and 2.le.n.le.nlat in abel,bbel,cbel\n      do 107 n=2,nlat\n      mlim = min0(n,l)\n      do 107 m=2,mlim\n      imn = indx(m,n)\n      if (n.ge.l) imn = imndx(m,n)\n      abel(imn)=dsqrt(dfloat((2*n+1)*(m+n-2)*(m+n-3))/\n     1               dfloat(((2*n-3)*(m+n-1)*(m+n))))\n      bbel(imn)=dsqrt(dfloat((2*n+1)*(n-m-1)*(n-m))/\n     1               dfloat(((2*n-3)*(m+n-1)*(m+n))))\n      cbel(imn)=dsqrt(dfloat((n-m+1)*(n-m+2))/\n     1               dfloat(((n+m-1)*(n+m))))\n  107 continue\n      return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/slapgs.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc\nc ... file slapgs.f\nc\nc     this file includes documentation and code for\nc     subroutine slapgs          i\nc\nc ... files which must be loaded with slapgs.f\nc\nc     sphcom.f, hrfft.f, shags.f, shsgs.f\nc\nc\nc\nc     subroutine slapgs(nstrlap,nstrab,noffslap,noffsab,\nc                       nlat,nlon,isym,nt,slap,ids,jds,a,b,\nc    +mdab,ndab,wshsgs,lshsgs,work,lwork,ierror)\nc\nc\nc     given the scalar spherical harmonic coefficients a and b, precomputed\nc     by subroutine shags for a scalar field sf, subroutine slapgs computes\nc     the laplacian of sf in the scalar array slap.  slap(i,j) is the\nc     laplacian of sf at the gaussian colatitude theta(i) (see nlat as\nc     an input parameter) and east longitude lambda(j) = (j-1)*2*pi/nlon\nc     on the sphere.  i.e.\nc\nc         slap(i,j) =\nc\nc                  2                2\nc         [1/sint*d (sf(i,j)/dlambda + d(sint*d(sf(i,j))/dtheta)/dtheta]/sint\nc\nc\nc     where sint = sin(theta(i)).  the scalar laplacian in slap has the\nc     same symmetry or absence of symmetry about the equator as the scalar\nc     field sf.  the input parameters isym,nt,mdab,ndab must have the\nc     same values used by shags to compute a and b for sf. the associated\nc     legendre functions are stored rather than recomputed as they are\nc     in subroutine slapgc.\nc\nc     input parameters\nc\nc     nstrlap, nstrab  strides in lap,a,b,\nc     noffslap, noffsab  offset in lap,a,b, -- must be between 0 and stride-1\n\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are computed\nc            in radians in theta(1) <...< theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid point\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct longitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than zero. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for isym==0.  For this reason an error code will\nc           be returned if isym!=0.\nc\nc     isym   this parameter should have the same value input to subroutine\nc            shags to compute the coefficients a and b for the scalar field\nc            sf.  isym is set as follows:\nc\nc            = 0  no symmetries exist in sf about the equator. scalar\nc                 synthesis is used to compute slap on the entire sphere.\nc                 i.e., in the array slap(i,j) for i=1,...,nlat and\nc                 j=1,...,nlon.\nc\nc           = 1  sf and slap are antisymmetric about the equator. the\nc                synthesis used to compute slap is performed on the\nc                northern hemisphere only.  if nlat is odd, slap(i,j) is\nc                computed for i=1,...,(nlat+1)/2 and j=1,...,nlon.  if\nc                nlat is even, slap(i,j) is computed for i=1,...,nlat/2\nc                and j=1,...,nlon.\nc\nc\nc           = 2  sf and slap are symmetric about the equator. the\nc                synthesis used to compute slap is performed on the\nc                northern hemisphere only.  if nlat is odd, slap(i,j) is\nc                computed for i=1,...,(nlat+1)/2 and j=1,...,nlon.  if\nc                nlat is even, slap(i,j) is computed for i=1,...,nlat/2\nc                and j=1,...,nlon.\nc\nc\nc     nt     the number of analyses.  in the program that calls slapgs\nc            the arrays slap,a, and b can be three dimensional in which\nc            case multiple synthesis will be performed.  the third index\nc            is the synthesis index which assumes the values k=1,...,nt.\nc            for a single analysis set nt=1. the description of the\nc            remaining parameters is simplified by assuming that nt=1\nc            or that all the arrays are two dimensional.\nc\nc   ids      the first dimension of the array slap as it appears in the\nc            program that calls slapgs.  if isym = 0 then ids must be at\nc            least nlat.  if isym > 0 and nlat is even then ids must be\nc            at least nlat/2. if isym > 0 and nlat is odd then ids must\nc            be at least (nlat+1)/2.\nc\nc   jds      the second dimension of the array slap as it appears in the\nc            program that calls slapgs. jds must be at least nlon.\nc\nc\nc   a,b      two or three dimensional arrays (see input parameter nt)\nc            that contain scalar spherical harmonic coefficients\nc            of the scalar field sf as computed by subroutine shags.\nc     ***    a,b must be computed by shags prior to calling slapgs.\nc\nc\nc    mdab    the first dimension of the arrays a and b as it appears\nc            in the program that calls slapgs.  mdab must be at\nc            least min0(nlat,(nlon+2)/2) if nlon is even or at least\nc            min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc    ndab    the second dimension of the arrays a and b as it appears\nc            in the program that calls slapgs. ndbc must be at least\nc            least nlat.\nc\nc            mdab,ndab should have the same values input to shags to\nc            compute the coefficients a and b.\nc\nc\nc    wshsgs  an array which must be initialized by subroutine slapgsi\nc            (or equivalently by shsgsi).  once initialized, wshsgs\nc            can be used repeatedly by slapgs as long as nlat and nlon\nc            remain unchanged.  wshsgs must not be altered between calls\nc            of slapgs.\nc\nc    lshsgs  the dimension of the array wshsgs as it appears in the\nc            program that calls slapgs.  let\nc\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lshsgs must be at least\nc\nc               nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\nc\nc\nc     work   a work array that does not have to be saved.\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls slapgs. define\nc\nc               l2 = nlat/2                    if nlat is even or\nc               l2 = (nlat+1)/2                if nlat is odd\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            if isym is zero then lwork must be at least\nc\nc               (nt+1)*nlat*nlon + nlat*(2*nt*l1+1)\nc\nc            if isym is nonzero lwork must be at least\nc\nc               (nt+1)*l2*nlon + nlat*(2*nt*l1+1)\nc\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc\nc    slap    a two or three dimensional arrays (see input parameter nt) that\nc            contain the scalar laplacian of the scalar field sf.  slap(i,j)\nc            is the scalar laplacian at the gaussian colatitude theta(i)\nc            and longitude lambda(j) = (j-1)*2*pi/nlon for i=1,...,nlat\nc            and j=1,...,nlon.\nc\nc\nc  ierror    a parameter which flags errors in input parameters as follows:\nc\nc            = 0  no errors detected\nc\nc            = 1  error in the specification of nlat\nc\nc            = 2  error in the specification of nlon\nc\nc            = 3  error in the specification of ityp\nc\nc            = 4  error in the specification of nt\nc\nc            = 5  error in the specification of ids\nc\nc            = 6  error in the specification of jds\nc\nc            = 7  error in the specification of mdbc\nc\nc            = 8  error in the specification of ndbc\nc\nc            = 9  error in the specification of lshsgs\nc\nc            = 10 error in the specification of lwork\nc\nc\nc **********************************************************************\nc                                                                              \nc     end of documentation for slapgs\nc\nc **********************************************************************\nc\nc\n      subroutine slapgs(nstrlap,nstrab,noffslap,noffsab,\n     +                  nlat,nlon,isym,nt,slap,ids,jds,a,b,mdab,ndab,\n     +                  wshsgs,lshsgs,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension slap(nstrlap,ids,jds,nt),\n     +   a(nstrab,mdab,ndab,nt),b(nstrab,mdab,ndab,nt)\n      dimension wshsgs(lshsgs),work(lwork)\nc\nc     check input parameters\nc\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 4) return\n      ierror = 3\nc     if (isym.lt.0 .or. isym.gt.2) return\nc     see note above about symmeries\n      if(isym.ne.0) return\n      ierror = 4\n      if(nt .lt. 0) return\n      ierror = 5\n      imid = (nlat+1)/2\n      if((isym.eq.0 .and. ids.lt.nlat) .or.\n     1   (isym.gt.0 .and. ids.lt.imid)) return\n      ierror = 6\n      if(jds .lt. nlon) return\n      ierror = 7\n      mmax = min0(nlat,nlon/2+1)\n      if(mdab .lt. mmax) return\n      ierror = 8\n      if(ndab .lt. nlat) return\n      ierror = 9\nc\nc     set and verify saved work space length\nc\n      imid = (nlat+1)/2\n      l2 = (nlat+mod(nlat,2))/2\n      l1 = min0((nlon+2)/2,nlat)\n      lp=nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\n      if(lshsgs.lt.lp) return\n      ierror = 10\nc\nc     set and verify unsaved work space length\nc\n      ls = nlat\n      if(isym .gt. 0) ls = imid\n      nln = nt*ls*nlon\n      mn = mmax*nlat*nt\nc     lwkmin = nln+ls*nlon+2*mn+nlat\nc     if (lwork .lt. lwkmin) return\n      l2 = (nlat+1)/2\n      l1 = min0(nlat,nlon/2+1)\n      if (isym.eq.0) then\n\tlwkmin = (nt+1)*nlat*nlon + nlat*(2*nt*l1+1)\n      else\n\tlwkmin = (nt+1)*l2*nlon + nlat*(2*nt*l1+1)\n      end if\n      if (lwork .lt. lwkmin) return\n      ierror = 11\n      if((noffslap.lt.0) .or.  (noffslap.ge.nstrlap)) return\n      if((noffsab.lt.0) .or.  (noffsab.ge.nstrab)) return\n      ierror = 0\nc\nc     set work space pointers\nc\n      ia = 1\n      ib = ia+mn\n      ifn = ib+mn\n      iwk = ifn+nlat\n      lwk = lwork-2*mn-nlat\n      call slapgs1(nstrlap,nstrab,noffslap,noffsab,\n     +nlat,nlon,isym,nt,slap,ids,jds,a,b,mdab,ndab,\n     +work(ia),work(ib),mmax,work(ifn),wshsgs,lshsgs,work(iwk),lwk,\n     +ierror)\n      return\n      end\n\n      subroutine slapgs1(nstrlap,nstrab,noffslap,noffsab,\n     +                  nlat,nlon,isym,nt,slap,ids,jds,a,b,mdab,ndab,\n     +                  alap,blap,mmax,fnn,wsave,lsave,wk,lwk,ierror)\n      implicit double precision (a-h,o-z)\n      dimension slap(nstrlap,ids,jds,nt),\n     +    a(nstrab,mdab,ndab,nt),b(nstrab,mdab,ndab,nt)\n      dimension alap(mmax,nlat,nt),blap(mmax,nlat,nt),fnn(nlat)\n      dimension wsave(lsave),wk(lwk)\n      ncab=1+noffsab\nc\nc     set coefficient multiplyers\nc\n      do 1 n=2,nlat\n      fn = dfloat(n-1)\n      fnn(n) = fn*(fn+1.d0)\n    1 continue\nc\nc     compute scalar laplacian coefficients for each vector field\nc\n      do 2 k=1,nt\n      do 3 n=1,nlat\n      do 4 m=1,mmax\n      alap(m,n,k) = 0.0d0\n      blap(m,n,k) = 0.0d0\n    4 continue\n    3 continue\nc\nc     compute m=0 coefficients\nc\n      do 5 n=2,nlat\n      alap(1,n,k) = -fnn(n)*a(ncab,1,n,k)\n      blap(1,n,k) = -fnn(n)*b(ncab,1,n,k)\n    5 continue\nc\nc     compute m>0 coefficients\nc\n      do 6 m=2,mmax\n      do 7 n=m,nlat\n      alap(m,n,k) = -fnn(n)*a(ncab,m,n,k)\n      blap(m,n,k) = -fnn(n)*b(ncab,m,n,k)\n    7 continue\n    6 continue\n    2 continue\nc\nc     synthesize alap,blap into slap\nc\n      call shsgs(nstrlap,1,noffslap,0,\n     +           nlat,nlon,isym,nt,slap,ids,jds,alap,blap,\n     +           mmax,nlat,mmax,nlat,wsave,lsave,wk,lwk,ierror)\n      return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/sphcom.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc ... file sphcom.f\nc\nc     this file must be loaded with all main program files\nc     in spherepack.  it includes undocumented subroutines\nc     called by some or all of main programs\nc\n      subroutine dnlfk (m,n,cp)\n      implicit double precision (a-h,o-z)\nc\nc     cp requires n/2+1 double precision locations\nc\n      double precision cp,fnum,fden,fnmh,a1,b1,c1,cp2,fnnp1,fnmsq,fk,\n     1       t1,t2,pm1,sc10,sc20,sc40\n      dimension       cp(1)\n      parameter (sc10=1024.d0)\n      parameter (sc20=sc10*sc10)\n      parameter (sc40=sc20*sc20)\nc\n      cp(1) = 0.d0\n      ma = iabs(m)\n      if(ma .gt. n) return\n      if(n-1) 2,3,5\n    2 cp(1) = dsqrt(2.d0)\n      return\n    3 if(ma .ne. 0) go to 4\n      cp(1) = dsqrt(1.5d0)\n      return\n    4 cp(1) = dsqrt(.75d0)\n      if(m .eq. -1) cp(1) = -cp(1)\n      return\n    5 if(mod(n+ma,2) .ne. 0) go to 10\n      nmms2 = (n-ma)/2\n      fnum = n+ma+1\n      fnmh = n-ma+1\n      pm1 = 1.d0\n      go to 15\n   10 nmms2 = (n-ma-1)/2\n      fnum = n+ma+2\n      fnmh = n-ma+2\n      pm1 = -1.d0\nc      t1 = 1.\nc      t1 = 2.d0**(n-1)\nc      t1 = 1.d0/t1\n 15   t1 = 1.d0/sc20\n      nex = 20\n      fden = 2.d0\n      if(nmms2 .lt. 1) go to 20\n      do 18 i=1,nmms2\n      t1 = fnum*t1/fden\n      if(t1 .gt. sc20) then\n      t1 = t1/sc40\n      nex = nex+40\n      end if\n      fnum = fnum+2.d0\n      fden = fden+2.d0\n   18 continue\n   20 t1 = t1/2.d0**(n-1-nex)\n      if(mod(ma/2,2) .ne. 0) t1 = -t1\n      t2 = 1.d0 \n      if(ma .eq. 0) go to 26\n      do 25 i=1,ma\n      t2 = fnmh*t2/(fnmh+pm1)\n      fnmh = fnmh+2.d0\n   25 continue\n   26 cp2 = t1*dsqrt((n+.5d0)*t2)\n      fnnp1 = n*(n+1)\n      fnmsq = fnnp1-2.d0*ma*ma\n      l = (n+1)/2\n      if(mod(n,2) .eq. 0 .and. mod(ma,2) .eq. 0) l = l+1\n      cp(l) = cp2\n      if(m .ge. 0) go to 29\n      if(mod(ma,2) .ne. 0) cp(l) = -cp(l)\n   29 if(l .le. 1) return\n      fk = n\n      a1 = (fk-2.d0)*(fk-1.d0)-fnnp1\n      b1 = 2.d0*(fk*fk-fnmsq)\n      cp(l-1) = b1*cp(l)/a1\n   30 l = l-1\n      if(l .le. 1) return\n      fk = fk-2.d0\n      a1 = (fk-2.d0)*(fk-1.d0)-fnnp1\n      b1 = -2.d0*(fk*fk-fnmsq)\n      c1 = (fk+1.d0)*(fk+2.d0)-fnnp1\n      cp(l-1) = -(b1*cp(l)+c1*cp(l+1))/a1\n      go to 30\n      end\n      subroutine dnlft (m,n,theta,cp,pb)\n      implicit double precision (a-h,o-z)\n      double precision cp(*),pb,theta,cdt,sdt,cth,sth,chh\n      cdt = dcos(theta+theta)\n      sdt = dsin(theta+theta)\n      nmod=mod(n,2)\n      mmod=mod(m,2)\n      if(nmod)1,1,2\n1     if(mmod)3,3,4\nc\nc     n even, m even\nc\n3     kdo=n/2\n      pb = .5d0*cp(1)\n      if(n .eq. 0) return\n      cth = cdt\n      sth = sdt\n      do 170 k=1,kdo\nc     pb = pb+cp(k+1)*dcos(2*k*theta)\n      pb = pb+cp(k+1)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  170 continue\n      return\nc\nc     n even, m odd\nc\n    4 kdo = n/2\n      pb = 0.d0\n      cth = cdt\n      sth = sdt\n      do 180 k=1,kdo\nc     pb = pb+cp(k)*dsin(2*k*theta)\n      pb = pb+cp(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  180 continue\n      return\n2     if(mmod)13,13,14\nc\nc     n odd, m even\nc\n13    kdo = (n+1)/2\n      pb = 0.d0\n      cth = dcos(theta)\n      sth = dsin(theta)\n      do 190 k=1,kdo\nc     pb = pb+cp(k)*dcos((2*k-1)*theta)\n      pb = pb+cp(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  190 continue\n      return\nc\nc     n odd, m odd\nc\n   14 kdo = (n+1)/2\n      pb = 0.d0\n      cth = dcos(theta)\n      sth = dsin(theta)\n      do 200 k=1,kdo\nc     pb = pb+cp(k)*dsin((2*k-1)*theta)\n      pb = pb+cp(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  200 continue\n      return\n      end\n      subroutine dnlftd (m,n,theta,cp,pb)\n      implicit double precision (a-h,o-z)\nc\nc     computes the derivative of pmn(theta) with respect to theta\nc\n      dimension cp(1)\n      double precision cp,pb,theta,cdt,sdt,cth,sth,chh\n      cdt = dcos(theta+theta)\n      sdt = dsin(theta+theta)\n      nmod=mod(n,2)\n      mmod=mod(abs(m),2)\n      if(nmod)1,1,2\n1     if(mmod)3,3,4\nc\nc     n even, m even\nc\n3     kdo=n/2\n      pb = 0.d0\n      if(n .eq. 0) return\n      cth = cdt\n      sth = sdt\n      do 170 k=1,kdo\nc     pb = pb+cp(k+1)*dcos(2*k*theta)\n      pb = pb-2.d0*k*cp(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  170 continue\n      return\nc\nc     n even, m odd\nc\n    4 kdo = n/2\n      pb = 0.d0\n      cth = cdt\n      sth = sdt\n      do 180 k=1,kdo\nc     pb = pb+cp(k)*dsin(2*k*theta)\n      pb = pb+2.d0*k*cp(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  180 continue\n      return\n2     if(mmod)13,13,14\nc\nc     n odd, m even\nc\n13    kdo = (n+1)/2\n      pb = 0.d0\n      cth = dcos(theta)\n      sth = dsin(theta)\n      do 190 k=1,kdo\nc     pb = pb+cp(k)*dcos((2*k-1)*theta)\n      pb = pb-(2.d0*k-1)*cp(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  190 continue\n      return\nc\nc     n odd, m odd\nc\n   14 kdo = (n+1)/2\n      pb = 0.d0\n      cth = dcos(theta)\n      sth = dsin(theta)\n      do 200 k=1,kdo\nc     pb = pb+cp(k)*dsin((2*k-1)*theta)\n      pb = pb+(2.d0*k-1)*cp(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  200 continue\n      return\n      end\n      subroutine legin(mode,l,nlat,m,w,pmn,km,km0,km1,km2)\n      implicit double precision (a-h,o-z)\nc     this subroutine computes legendre polynomials for n=m,...,l-1\nc     and  i=1,...,late (late=((nlat+mod(nlat,2))/2)gaussian grid\nc     in pmn(n+1,i,km) using swarztrauber's recursion formula.\nc     the vector w contains quantities precomputed in shigc.\nc     legin must be called in the order m=0,1,...,l-1\nc     (e.g., if m=10 is sought it must be preceded by calls with\nc     m=0,1,2,...,9 in that order)\n      dimension w(1),pmn(1)\nc     set size of pole to equator gaussian grid\n      late = (nlat+mod(nlat,2))/2\nc     partition w (set pointers for p0n,p1n,abel,bbel,cbel,pmn)\n      i1 = 1+nlat\n      i2 = i1+nlat*late\n      i3 = i2+nlat*late\n      i4 = i3+(2*nlat-l)*(l-1)/2\n      i5 = i4+(2*nlat-l)*(l-1)/2\n      call legin1(mode,l,nlat,late,m,w(i1),w(i2),w(i3),w(i4),\n     1            w(i5),pmn,km,km0,km1,km2)\n      return\n      end\n      subroutine legin1(mode,l,nlat,late,m,p0n,p1n,abel,bbel,cbel,\n     1                  pmn,km,km0,km1,km2)\n      implicit double precision (a-h,o-z)\n      dimension p0n(nlat,late),p1n(nlat,late)\n      dimension abel(1),bbel(1),cbel(1),pmn(nlat,late,3)\nc     define index function used in storing triangular\nc     arrays for recursion coefficients (functions of (m,n))\nc     for 2.le.m.le.n-1 and 2.le.n.le.l-1\n      indx(m,n) = (n-1)*(n-2)/2+m-1\nc     for l.le.n.le.nlat and 2.le.m.le.l\n      imndx(m,n) = l*(l-1)/2+(n-l-1)*(l-1)+m-1\n\nc     set do loop indices for full or half sphere\n      ms = m+1\n      ninc = 1\n      if (mode.eq.1) then\nc     only compute pmn for n-m odd\n      ms = m+2\n      ninc = 2\n      else if (mode.eq.2) then\nc     only compute pmn for n-m even\n      ms = m+1\n      ninc = 2\n      end if\n\n\n      if (m.gt.1) then\n      do 100 np1=ms,nlat,ninc\n      n = np1-1\n      imn = indx(m,n)\n      if (n.ge.l) imn = imndx(m,n)\n      do 100 i=1,late\n      pmn(np1,i,km0) = abel(imn)*pmn(n-1,i,km2)\n     1            +bbel(imn)*pmn(n-1,i,km0)\n     2            -cbel(imn)*pmn(np1,i,km2)\n  100 continue\n\n      else if (m.eq.0) then\n      do 101 np1=ms,nlat,ninc\n      do 101 i=1,late\n      pmn(np1,i,km0) = p0n(np1,i)\n  101 continue\n\n      else if (m.eq.1) then\n      do 102 np1=ms,nlat,ninc\n      do 102 i=1,late\n      pmn(np1,i,km0) = p1n(np1,i)\n  102 continue\n      end if\n\nc     permute column indices\nc     km0,km1,km2 store m,m-1,m-2 columns\n      kmt = km0\n      km0 = km2\n      km2 = km1\n      km1 = kmt\nc     set current m index in output param km\n      km = kmt\n      return\n      end\n\n\nc$$$      subroutine zfin (isym,nlat,nlon,m,z,i3,wzfin)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension       z(1)        ,wzfin(1)\nc$$$      imid = (nlat+1)/2\nc$$$      lim = nlat*imid\nc$$$      mmax = min0(nlat,nlon/2+1)\nc$$$      labc = ((mmax-2)*(nlat+nlat-mmax-1))/2\nc$$$      iw1 = lim+1\nc$$$      iw2 = iw1+lim\nc$$$      iw3 = iw2+labc\nc$$$      iw4 = iw3+labc\nc$$$c\nc$$$c     the length of wzfin is 2*lim+3*labc\nc$$$c\nc$$$      call zfin1 (isym,nlat,m,z,imid,i3,wzfin,wzfin(iw1),wzfin(iw2),\nc$$$     1            wzfin(iw3),wzfin(iw4))\nc$$$      return\nc$$$      end\nc$$$      subroutine zfin1 (isym,nlat,m,z,imid,i3,zz,z1,a,b,c)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension       z(imid,nlat,3),zz(imid,1),z1(imid,1),\nc$$$     1                a(1),b(1),c(1)\nc$$$      save i1,i2\nc$$$      ihold = i1\nc$$$      i1 = i2\nc$$$      i2 = i3\nc$$$      i3 = ihold\nc$$$      if(m-1)25,30,35\nc$$$   25 i1 = 1\nc$$$      i2 = 2\nc$$$      i3 = 3\nc$$$      do 45 np1=1,nlat\nc$$$      do 45 i=1,imid\nc$$$      z(i,np1,i3) = zz(i,np1)\nc$$$   45 continue\nc$$$      return\nc$$$   30 do 50 np1=2,nlat\nc$$$      do 50 i=1,imid\nc$$$      z(i,np1,i3) = z1(i,np1)\nc$$$   50 continue\nc$$$      return\nc$$$   35 ns = ((m-2)*(nlat+nlat-m-1))/2+1\nc$$$      if(isym .eq. 1) go to 36\nc$$$      do 85 i=1,imid\nc$$$      z(i,m+1,i3) = a(ns)*z(i,m-1,i1)-c(ns)*z(i,m+1,i1)\nc$$$   85 continue\nc$$$   36 if(m .eq. nlat-1) return\nc$$$      if(isym .eq. 2) go to 71\nc$$$      ns = ns+1\nc$$$      do 70 i=1,imid\nc$$$      z(i,m+2,i3) = a(ns)*z(i,m,i1)-c(ns)*z(i,m+2,i1)\nc$$$   70 continue\nc$$$   71 nstrt = m+3\nc$$$      if(isym .eq. 1) nstrt = m+4\nc$$$      if(nstrt .gt. nlat) go to 80\nc$$$      nstp = 2\nc$$$      if(isym .eq. 0) nstp = 1\nc$$$      do 75 np1=nstrt,nlat,nstp\nc$$$      ns = ns+nstp\nc$$$      do 75 i=1,imid\nc$$$      z(i,np1,i3) = a(ns)*z(i,np1-2,i1)+b(ns)*z(i,np1-2,i3)\nc$$$     1                              -c(ns)*z(i,np1,i1)\nc$$$   75 continue\nc$$$   80 return\nc$$$      end\n      subroutine zfinit (nlat,nlon,wzfin,dwork)\n      implicit double precision (a-h,o-z)\n      dimension       wzfin(*)\n      double precision dwork(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     the length of wzfin is 3*((l-3)*l+2)/2 + 2*l*imid\nc     the length of dwork is nlat+2\nc\n      call zfini1 (nlat,nlon,imid,wzfin,wzfin(iw1),dwork,\n     1                                       dwork(nlat/2+1))\n      return\n      end\n      subroutine zfini1 (nlat,nlon,imid,z,abc,cz,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*((mmax-2)*(nlat+nlat-mmax-1))/2 locations\nc     where mmax = min0(nlat,nlon/2+1)\nc     cz and work must each have nlat+1 locations\nc\n      dimension z(imid,nlat,2),abc(1)\n      double precision pi,dt,th,zh,cz(*),work(*)\n      pi = 4.d0*datan(1.d0)\n      dt = pi/(nlat-1)\n      do 160 mp1=1,2\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dnzfk(nlat,m,n,cz,work)\n      do 165 i=1,imid\n      th = (i-1)*dt\n      call dnzft(nlat,m,n,th,cz,zh)\n      z(i,np1,mp1) = zh\n  165 continue\n      z(1,np1,mp1) = .5d0*z(1,np1,mp1)\n  160 continue\n      call rabcp(nlat,nlon,abc)\n      return\n      end\n      subroutine dnzfk(nlat,m,n,cz,work)\n      implicit double precision (a-h,o-z)\nc\nc     dnzfk computes the coefficients in the trigonometric\nc     expansion of the z functions that are used in spherical\nc     harmonic analysis.\nc\n      dimension  cz(1),work(1)\nc\nc     cz and work must both have nlat/2+1 locations\nc\n      double precision sum,sc1,t1,t2,work,cz\n      lc = (nlat+1)/2\n      sc1 = 2.d0/dfloat(nlat-1)\n      call dnlfk(m,n,work)\n      nmod = mod(n,2)\n      mmod = mod(m,2)\n      if(nmod)1,1,2\n1     if(mmod)3,3,4\nc\nc     n even, m even\nc\n3     kdo = n/2+1\n      do 5 idx=1,lc\n      i = idx+idx-2\n      sum = work(1)/(1.d0-i*i)\n      if(kdo.lt.2) go to 29\n      do 6 kp1=2,kdo\n      k = kp1-1\n      t1 = 1.d0-(k+k+i)**2\n      t2 = 1.d0-(k+k-i)**2\n8     sum = sum+work(kp1)*(t1+t2)/(t1*t2)\n6     continue\n29    cz(idx) = sc1*sum\n5     continue\n      return\nc\nc     n even, m odd\nc\n4     kdo = n/2\n      do 9 idx=1,lc\n      i = idx+idx-2\n      sum = 0.d0\n      do 101 k=1,kdo\n      t1 = 1.d0-(k+k+i)**2\n      t2 = 1.d0-(k+k-i)**2\n12    sum=sum+work(k)*(t1-t2)/(t1*t2)\n101   continue\n      cz(idx) = sc1*sum\n9     continue\n      return\n2     if(mmod)13,13,14\nc\nc     n odd, m even\nc\n13    kdo = (n+1)/2\n      do 15 idx=1,lc\n      i = idx+idx-1\n      sum = 0.d0\n      do 16 k=1,kdo\n      t1 = 1.d0-(k+k-1+i)**2\n      t2 = 1.d0-(k+k-1-i)**2\n18    sum=sum+work(k)*(t1+t2)/(t1*t2)\n16    continue\n      cz(idx)=sc1*sum\n15    continue\n      return\nc\nc     n odd, m odd\nc\n14    kdo = (n+1)/2\n      do 19 idx=1,lc\n      i = idx+idx-3\n      sum=0.d0\n      do 20 k=1,kdo\n      t1 = 1.d0-(k+k-1+i)**2\n      t2 = 1.d0-(k+k-1-i)**2\n22    sum=sum+work(k)*(t1-t2)/(t1*t2)\n20    continue\n      cz(idx)=sc1*sum\n19    continue\n      return\n      end\n      subroutine dnzft(nlat,m,n,th,cz,zh)\n      implicit double precision (a-h,o-z)\n      dimension cz(1)\n      double precision cz,zh,th,cdt,sdt,cth,sth,chh\n      zh = 0.d0\n      cdt = dcos(th+th)\n      sdt = dsin(th+th)\n      lmod = mod(nlat,2)\n      mmod = mod(m,2)\n      nmod = mod(n,2)\n      if(lmod)20,20,10\n   10 lc = (nlat+1)/2\n      lq = lc-1\n      ls = lc-2\n      if(nmod)1,1,2\n    1 if(mmod)3,3,4\nc\nc     nlat odd n even m even\nc\n    3 zh = .5d0*(cz(1)+cz(lc)*dcos(2*lq*th))\n      cth = cdt\n      sth = sdt\n      do 201 k=2,lq\nc     zh = zh+cz(k)*dcos(2*(k-1)*th)\n      zh = zh+cz(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  201 continue\n      return\nc\nc     nlat odd n even m odd\nc\n    4 cth = cdt\n      sth = sdt\n      do 202 k=1,ls\nc     zh = zh+cz(k+1)*dsin(2*k*th)\n      zh = zh+cz(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  202 continue\n      return\nc\nc     nlat odd n odd, m even\nc\n    2 if(mmod)5,5,6\n    5 cth = dcos(th)\n      sth = dsin(th)\n      do 203 k=1,lq\nc     zh = zh+cz(k)*dcos((2*k-1)*th)\n      zh = zh+cz(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  203 continue\n      return\nc\nc     nlat odd n odd m odd\nc\n    6 cth = dcos(th)\n      sth = dsin(th)\n      do 204 k=1,lq\nc     zh = zh+cz(k+1)*dsin((2*k-1)*th)\n      zh = zh+cz(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  204 continue\n      return\n   20 lc = nlat/2\n      lq = lc-1\n      if(nmod)30,30,80\n   30 if(mmod)40,40,60\nc\nc     nlat even n even m even\nc\n   40 zh = .5d0*cz(1)\n      cth = cdt\n      sth = sdt\n      do 50 k=2,lc\nc     zh = zh+cz(k)*dcos(2*(k-1)*th)\n      zh = zh+cz(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   50 continue\n      return\nc\nc     nlat even n even m odd\nc\n   60 cth = cdt\n      sth = sdt\n      do 70 k=1,lq\nc     zh = zh+cz(k+1)*dsin(2*k*th)\n      zh = zh+cz(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   70 continue\n      return\nc\nc     nlat even n odd m even\nc\n   80 if(mmod)90,90,110\n   90 zh = .5d0*cz(lc)*dcos((nlat-1)*th)\n      cth = dcos(th)\n      sth = dsin(th)\n      do 100 k=1,lq\nc     zh = zh+cz(k)*dcos((2*k-1)*th)\n      zh = zh+cz(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  100 continue\n      return\nc\nc     nlat even n odd m odd\nc\n  110 cth = dcos(th)\n      sth = dsin(th)\n      do 120 k=1,lq\nc     zh = zh+cz(k+1)*dsin((2*k-1)*th)\n      zh = zh+cz(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n  120 continue\n      return\n      end\nc$$$      subroutine alin (isym,nlat,nlon,m,p,i3,walin)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension       p(1)        ,walin(1)\nc$$$      imid = (nlat+1)/2\nc$$$      lim = nlat*imid\nc$$$      mmax = min0(nlat,nlon/2+1)\nc$$$      labc = ((mmax-2)*(nlat+nlat-mmax-1))/2\nc$$$      iw1 = lim+1\nc$$$      iw2 = iw1+lim\nc$$$      iw3 = iw2+labc\nc$$$      iw4 = iw3+labc\nc$$$c\nc$$$c     the length of walin is ((5*l-7)*l+6)/2\nc$$$c\nc$$$      call alin1 (isym,nlat,m,p,imid,i3,walin,walin(iw1),walin(iw2),\nc$$$     1            walin(iw3),walin(iw4))\nc$$$      return\nc$$$      end\nc$$$      subroutine alin1 (isym,nlat,m,p,imid,i3,pz,p1,a,b,c)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension       p(imid,nlat,3),pz(imid,1),p1(imid,1),\nc$$$     1                a(1),b(1),c(1)\nc$$$      save i1,i2\nc$$$      ihold = i1\nc$$$      i1 = i2\nc$$$      i2 = i3\nc$$$      i3 = ihold\nc$$$      if(m-1)25,30,35\nc$$$   25 i1 = 1\nc$$$      i2 = 2\nc$$$      i3 = 3\nc$$$      do 45 np1=1,nlat\nc$$$      do 45 i=1,imid\nc$$$      p(i,np1,i3) = pz(i,np1)\nc$$$   45 continue\nc$$$      return\nc$$$   30 do 50 np1=2,nlat\nc$$$      do 50 i=1,imid\nc$$$      p(i,np1,i3) = p1(i,np1)\nc$$$   50 continue\nc$$$      return\nc$$$   35 ns = ((m-2)*(nlat+nlat-m-1))/2+1\nc$$$      if(isym .eq. 1) go to 36\nc$$$      do 85 i=1,imid\nc$$$      p(i,m+1,i3) = a(ns)*p(i,m-1,i1)-c(ns)*p(i,m+1,i1)\nc$$$   85 continue\nc$$$   36 if(m .eq. nlat-1) return\nc$$$      if(isym .eq. 2) go to 71\nc$$$      ns = ns+1\nc$$$      do 70 i=1,imid\nc$$$      p(i,m+2,i3) = a(ns)*p(i,m,i1)-c(ns)*p(i,m+2,i1)\nc$$$   70 continue\nc$$$   71 nstrt = m+3\nc$$$      if(isym .eq. 1) nstrt = m+4\nc$$$      if(nstrt .gt. nlat) go to 80\nc$$$      nstp = 2\nc$$$      if(isym .eq. 0) nstp = 1\nc$$$      do 75 np1=nstrt,nlat,nstp\nc$$$      ns = ns+nstp\nc$$$      do 75 i=1,imid\nc$$$      p(i,np1,i3) = a(ns)*p(i,np1-2,i1)+b(ns)*p(i,np1-2,i3)\nc$$$     1                              -c(ns)*p(i,np1,i1)\nc$$$   75 continue\nc$$$   80 return\nc$$$      end\n      subroutine alinit (nlat,nlon,walin,dwork)\n      implicit double precision (a-h,o-z)\n      dimension       walin(*)\n      double precision dwork(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     the length of walin is 3*((l-3)*l+2)/2 + 2*l*imid\nc     the length of work is nlat+1\nc\n      call alini1 (nlat,nlon,imid,walin,walin(iw1),dwork)\n      return\n      end\n      subroutine alini1 (nlat,nlon,imid,p,abc,cp)\n      implicit double precision (a-h,o-z)\n      dimension p(imid,nlat,2),abc(1),cp(1)\n      double precision pi,dt,th,cp,ph\n      pi = 4.d0*datan(1.d0)\n      dt = pi/(nlat-1)\n      do 160 mp1=1,2\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dnlfk (m,n,cp)\n      do 160 i=1,imid\n      th = (i-1)*dt\n      call dnlft (m,n,th,cp,ph)\n      p(i,np1,mp1) = ph\n  160 continue\n      call rabcp(nlat,nlon,abc)\n      return\n      end\n      subroutine rabcp(nlat,nlon,abc)\n      implicit double precision (a-h,o-z)\nc\nc     subroutine rabcp computes the coefficients in the recurrence\nc     relation for the associated legendre fuctions. array abc\nc     must have 3*((mmax-2)*(nlat+nlat-mmax-1))/2 locations.\nc\n      dimension abc(1)\n      mmax = min0(nlat,nlon/2+1)\n      labc = ((mmax-2)*(nlat+nlat-mmax-1))/2\n      iw1 = labc+1\n      iw2 = iw1+labc\n      call rabcp1(nlat,nlon,abc,abc(iw1),abc(iw2))\n      return\n      end\n      subroutine rabcp1(nlat,nlon,a,b,c)\n      implicit double precision (a-h,o-z)\nc\nc     coefficients a, b, and c for computing pbar(m,n,theta) are\nc     stored in location ((m-2)*(nlat+nlat-m-1))/2+n+1\nc\n      dimension a(1),b(1),c(1)\n      mmax = min0(nlat,nlon/2+1)\n      do 215 mp1=3,mmax\n      m = mp1-1\n      ns = ((m-2)*(nlat+nlat-m-1))/2+1\n      fm = dfloat(m)\n      tm = fm+fm\n      temp = tm*(tm-1.d0)\n      a(ns) = dsqrt((tm+1.d0)*(tm-2.d0)/temp)\n      c(ns) = dsqrt(2.d0/temp)\n      if(m .eq. nlat-1) go to 215\n      ns = ns+1\n      temp = tm*(tm+1.d0)\n      a(ns) = dsqrt((tm+3.d0)*(tm-2.d0)/temp)\n      c(ns) = dsqrt(6.d0/temp)\n      mp3 = m+3\n      if(mp3 .gt. nlat) go to 215\n      do 210 np1=mp3,nlat\n      n = np1-1\n      ns = ns+1\n      fn = dfloat(n)\n      tn = fn+fn\n      cn = (tn+1.d0)/(tn-3.d0)\n      fnpm = fn+fm\n      fnmm = fn-fm\n      temp = fnpm*(fnpm-1.d0)\n      a(ns) = dsqrt(cn*(fnpm-3.d0)*(fnpm-2.d0)/temp)\n      b(ns) = dsqrt(cn*fnmm*(fnmm-1.d0)/temp)\n      c(ns) = dsqrt((fnmm+1.d0)*(fnmm+2.d0)/temp)\n  210 continue\n  215 continue\n      return\n      end\nc$$$      subroutine sea1(nlat,nlon,imid,z,idz,zin,wzfin,dwork)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension z(idz,*),zin(imid,nlat,3),wzfin(*)\nc$$$      double precision dwork(*)\nc$$$      call zfinit(nlat,nlon,wzfin,dwork)\nc$$$      mmax = min0(nlat,nlon/2+1)\nc$$$      do 33 mp1=1,mmax\nc$$$      m = mp1-1\nc$$$      call zfin (0,nlat,nlon,m,zin,i3,wzfin)\nc$$$      do 33 np1=mp1,nlat\nc$$$      mn = m*(nlat-1)-(m*(m-1))/2+np1\nc$$$      do 33 i=1,imid\nc$$$      z(mn,i) = zin(i,np1,i3)\nc$$$   33 continue\nc$$$      return\nc$$$      end\nc$$$      subroutine ses1(nlat,nlon,imid,p,pin,walin,dwork)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension p(imid,*),pin(imid,nlat,3),walin(*)\nc$$$      double precision dwork(*)\nc$$$      call alinit (nlat,nlon,walin,dwork)\nc$$$      mmax = min0(nlat,nlon/2+1)\nc$$$      do 10 mp1=1,mmax\nc$$$      m = mp1-1\nc$$$      call alin(0,nlat,nlon,m,pin,i3,walin)\nc$$$      do 10 np1=mp1,nlat\nc$$$      mn = m*(nlat-1)-(m*(m-1))/2+np1\nc$$$      do 10 i=1,imid\nc$$$      p(i,mn) = pin(i,np1,i3)\nc$$$   10 continue\nc$$$      return\nc$$$      end\n      subroutine zvinit (nlat,nlon,wzvin,dwork)\n      implicit double precision (a-h,o-z)\n      dimension       wzvin(1)\n      double precision dwork(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     the length of wzvin is \nc         2*nlat*imid +3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     the length of dwork is nlat+2\nc\n      call zvini1 (nlat,nlon,imid,wzvin,wzvin(iw1),dwork,\n     1                                    dwork(nlat/2+2))\n      return\n      end\n      subroutine zvini1 (nlat,nlon,imid,zv,abc,czv,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     locations where mmax = min0(nlat,(nlon+1)/2)\nc     czv and work must each have nlat/2+1  locations\nc\n      dimension zv(imid,nlat,2),abc(1)\n      double precision pi,dt,czv(1),zvh,th,work(1)\n      pi = 4.d0*datan(1.d0)\n      dt = pi/(nlat-1)\n      mdo = min0(2,nlat,(nlon+1)/2)\n      do 160 mp1=1,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dzvk(nlat,m,n,czv,work)\n      do 165 i=1,imid\n      th = (i-1)*dt\n      call dzvt(nlat,m,n,th,czv,zvh)\n      zv(i,np1,mp1) = zvh\n  165 continue\n      zv(1,np1,mp1) = .5d0*zv(1,np1,mp1)\n  160 continue\n      call rabcv(nlat,nlon,abc)\n      return\n      end\n      subroutine zwinit (nlat,nlon,wzwin,dwork)\n      implicit double precision (a-h,o-z)\n      dimension       wzwin(1)\n      double precision dwork(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     the length of wzvin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of dwork is nlat+2\nc\n      call zwini1 (nlat,nlon,imid,wzwin,wzwin(iw1),dwork,\n     1                                        dwork(nlat/2+2))\n      return\n      end\n      subroutine zwini1 (nlat,nlon,imid,zw,abc,czw,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     locations where mmax = min0(nlat,(nlon+1)/2)\nc     czw and work must each have nlat+1 locations\nc\n      dimension zw(imid,nlat,2),abc(1)\n      double precision  pi,dt,czw(1),zwh,th,work(1)\n      pi = 4.d0*datan(1.d0)\n      dt = pi/(nlat-1)\n      mdo = min0(3,nlat,(nlon+1)/2)\n      if(mdo .lt. 2) return\n      do 160 mp1=2,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dzwk(nlat,m,n,czw,work)\n      do 165 i=1,imid\n      th = (i-1)*dt\n      call dzwt(nlat,m,n,th,czw,zwh)\n      zw(i,np1,m) = zwh\n  165 continue\n      zw(1,np1,m) = .5d0*zw(1,np1,m)\n  160 continue\n      call rabcw(nlat,nlon,abc)\n      return\n      end\nc$$$      subroutine zvin (ityp,nlat,nlon,m,zv,i3,wzvin)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension       zv(1)        ,wzvin(1)\nc$$$      imid = (nlat+1)/2\nc$$$      lim = nlat*imid\nc$$$      mmax = min0(nlat,(nlon+1)/2)\nc$$$      labc = (max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc$$$      iw1 = lim+1\nc$$$      iw2 = iw1+lim\nc$$$      iw3 = iw2+labc\nc$$$      iw4 = iw3+labc\nc$$$c\nc$$$c     the length of wzvin is 2*lim+3*labc\nc$$$c\nc$$$      call zvin1 (ityp,nlat,m,zv,imid,i3,wzvin,wzvin(iw1),wzvin(iw2),\nc$$$     1            wzvin(iw3),wzvin(iw4))\nc$$$      return\nc$$$      end\nc$$$      subroutine zvin1 (ityp,nlat,m,zv,imid,i3,zvz,zv1,a,b,c)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension       zv(imid,nlat,3),zvz(imid,1),zv1(imid,1),\nc$$$     1                a(1),b(1),c(1)\nc$$$      save i1,i2\nc$$$      ihold = i1\nc$$$      i1 = i2\nc$$$      i2 = i3\nc$$$      i3 = ihold\nc$$$      if(m-1)25,30,35\nc$$$   25 i1 = 1\nc$$$      i2 = 2\nc$$$      i3 = 3\nc$$$      do 45 np1=1,nlat\nc$$$      do 45 i=1,imid\nc$$$      zv(i,np1,i3) = zvz(i,np1)\nc$$$   45 continue\nc$$$      return\nc$$$   30 do 50 np1=2,nlat\nc$$$      do 50 i=1,imid\nc$$$      zv(i,np1,i3) = zv1(i,np1)\nc$$$   50 continue\nc$$$      return\nc$$$   35 ns = ((m-2)*(nlat+nlat-m-1))/2+1\nc$$$      if(ityp .eq. 1) go to 36\nc$$$      do 85 i=1,imid\nc$$$      zv(i,m+1,i3) = a(ns)*zv(i,m-1,i1)-c(ns)*zv(i,m+1,i1)\nc$$$   85 continue\nc$$$   36 if(m .eq. nlat-1) return\nc$$$      if(ityp .eq. 2) go to 71\nc$$$      ns = ns+1\nc$$$      do 70 i=1,imid\nc$$$      zv(i,m+2,i3) = a(ns)*zv(i,m,i1)-c(ns)*zv(i,m+2,i1)\nc$$$   70 continue\nc$$$   71 nstrt = m+3\nc$$$      if(ityp .eq. 1) nstrt = m+4\nc$$$      if(nstrt .gt. nlat) go to 80\nc$$$      nstp = 2\nc$$$      if(ityp .eq. 0) nstp = 1\nc$$$      do 75 np1=nstrt,nlat,nstp\nc$$$      ns = ns+nstp\nc$$$      do 75 i=1,imid\nc$$$      zv(i,np1,i3) = a(ns)*zv(i,np1-2,i1)+b(ns)*zv(i,np1-2,i3)\nc$$$     1                              -c(ns)*zv(i,np1,i1)\nc$$$   75 continue\nc$$$   80 return\nc$$$      end\nc$$$      subroutine zwin (ityp,nlat,nlon,m,zw,i3,wzwin)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension       zw(1)        ,wzwin(1)\nc$$$      imid = (nlat+1)/2\nc$$$      lim = nlat*imid\nc$$$      mmax = min0(nlat,(nlon+1)/2)\nc$$$      labc = (max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc$$$      iw1 = lim+1\nc$$$      iw2 = iw1+lim\nc$$$      iw3 = iw2+labc\nc$$$      iw4 = iw3+labc\nc$$$c\nc$$$c     the length of wzwin is 2*lim+3*labc\nc$$$c\nc$$$      call zwin1 (ityp,nlat,m,zw,imid,i3,wzwin,wzwin(iw1),wzwin(iw2),\nc$$$     1            wzwin(iw3),wzwin(iw4))\nc$$$      return\nc$$$      end\nc$$$      subroutine zwin1 (ityp,nlat,m,zw,imid,i3,zw1,zw2,a,b,c)\nc$$$      implicit double precision (a-h,o-z)\nc$$$      dimension       zw(imid,nlat,3),zw1(imid,1),zw2(imid,1),\nc$$$     1                a(1),b(1),c(1)\nc$$$      save i1,i2\nc$$$      ihold = i1\nc$$$      i1 = i2\nc$$$      i2 = i3\nc$$$      i3 = ihold\nc$$$      if(m-2)25,30,35\nc$$$   25 i1 = 1\nc$$$      i2 = 2\nc$$$      i3 = 3\nc$$$      do 45 np1=2,nlat\nc$$$      do 45 i=1,imid\nc$$$      zw(i,np1,i3) = zw1(i,np1)\nc$$$   45 continue\nc$$$      return\nc$$$   30 do 50 np1=3,nlat\nc$$$      do 50 i=1,imid\nc$$$      zw(i,np1,i3) = zw2(i,np1)\nc$$$   50 continue\nc$$$      return\nc$$$   35 ns = ((m-2)*(nlat+nlat-m-1))/2+1\nc$$$      if(ityp .eq. 1) go to 36\nc$$$      do 85 i=1,imid\nc$$$      zw(i,m+1,i3) = a(ns)*zw(i,m-1,i1)-c(ns)*zw(i,m+1,i1)\nc$$$   85 continue\nc$$$   36 if(m .eq. nlat-1) return\nc$$$      if(ityp .eq. 2) go to 71\nc$$$      ns = ns+1\nc$$$      do 70 i=1,imid\nc$$$      zw(i,m+2,i3) = a(ns)*zw(i,m,i1)-c(ns)*zw(i,m+2,i1)\nc$$$   70 continue\nc$$$   71 nstrt = m+3\nc$$$      if(ityp .eq. 1) nstrt = m+4\nc$$$      if(nstrt .gt. nlat) go to 80\nc$$$      nstp = 2\nc$$$      if(ityp .eq. 0) nstp = 1\nc$$$      do 75 np1=nstrt,nlat,nstp\nc$$$      ns = ns+nstp\nc$$$      do 75 i=1,imid\nc$$$      zw(i,np1,i3) = a(ns)*zw(i,np1-2,i1)+b(ns)*zw(i,np1-2,i3)\nc$$$     1                              -c(ns)*zw(i,np1,i1)\nc$$$   75 continue\nc$$$   80 return\nc$$$      end\n      subroutine vbinit (nlat,nlon,wvbin,dwork)\n      implicit double precision (a-h,o-z)\n      dimension wvbin(1)\n      double precision dwork(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     the length of wvbin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of dwork is nlat+2\nc\n      call vbini1 (nlat,nlon,imid,wvbin,wvbin(iw1),dwork,\n     1                                       dwork(nlat/2+2))\n      return\n      end\n      subroutine vbini1 (nlat,nlon,imid,vb,abc,cvb,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     locations where mmax = min0(nlat,(nlon+1)/2)\nc     cvb and work must each have nlat+1 locations\nc\n      dimension vb(imid,nlat,2),abc(1)\n      double precision pi,dt,cvb(1),th,vbh,work(1)\n      pi = 4.d0*datan(1.d0)\n      dt = pi/(nlat-1)\n      mdo = min0(2,nlat,(nlon+1)/2)\n      do 160 mp1=1,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dvbk(m,n,cvb,work)\n      do 165 i=1,imid\n      th = (i-1)*dt\n      call dvbt(m,n,th,cvb,vbh)\n      vb(i,np1,mp1) = vbh\n  165 continue\n  160 continue\n      call rabcv(nlat,nlon,abc)\n      return\n      end\n      subroutine wbinit (nlat,nlon,wwbin,dwork)\n      implicit double precision (a-h,o-z)\n      dimension       wwbin(1)\n      double precision dwork(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     the length of wwbin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of dwork is nlat+2\nc\n      call wbini1 (nlat,nlon,imid,wwbin,wwbin(iw1),dwork,\n     1                                        dwork(nlat/2+2))\n      return\n      end\n      subroutine wbini1 (nlat,nlon,imid,wb,abc,cwb,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     locations where mmax = min0(nlat,(nlon+1)/2)\nc     cwb and work must each have nlat/2+1 locations\nc\n      dimension wb(imid,nlat,2),abc(1)\n      double precision pi,dt,cwb(1),wbh,th,work(1)\n      pi = 4.d0*datan(1.d0)\n      dt = pi/(nlat-1)\n      mdo = min0(3,nlat,(nlon+1)/2)\n      if(mdo .lt. 2) return\n      do 160 mp1=2,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dwbk(m,n,cwb,work)\n      do 165 i=1,imid\n      th = (i-1)*dt\n      call dwbt(m,n,th,cwb,wbh)\n      wb(i,np1,m) = wbh\n  165 continue\n  160 continue\n      call rabcw(nlat,nlon,abc)\n      return\n      end\n      subroutine vbin (ityp,nlat,nlon,m,vb,i3,wvbin,i1,i2)\n      implicit double precision (a-h,o-z)\n      dimension       vb(1)        ,wvbin(1)\n      imid = (nlat+1)/2\n      lim = nlat*imid\n      mmax = min0(nlat,(nlon+1)/2)\n      labc = (max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\n      iw1 = lim+1\n      iw2 = iw1+lim\n      iw3 = iw2+labc\n      iw4 = iw3+labc\nc\nc     the length of wvbin is 2*lim+3*labc\nc\n      call vbin1 (ityp,nlat,m,vb,imid,i3,wvbin,wvbin(iw1),wvbin(iw2),\n     1            wvbin(iw3),wvbin(iw4),i1,i2)\n      return\n      end\n      subroutine vbin1 (ityp,nlat,m,vb,imid,i3,vbz,vb1,a,b,c,i1,i2)\n      implicit double precision (a-h,o-z)\n      dimension       vb(imid,nlat,3),vbz(imid,1),vb1(imid,1),\n     1                a(1),b(1),c(1)\n      ihold = i1\n      i1 = i2\n      i2 = i3\n      i3 = ihold\n      if(m-1)25,30,35\n   25 i1 = 1\n      i2 = 2\n      i3 = 3\n      do 45 np1=1,nlat\n      do 45 i=1,imid\n      vb(i,np1,i3) = vbz(i,np1)\n   45 continue\n      return\n   30 do 50 np1=2,nlat\n      do 50 i=1,imid\n      vb(i,np1,i3) = vb1(i,np1)\n   50 continue\n      return\n   35 ns = ((m-2)*(nlat+nlat-m-1))/2+1\n      if(ityp .eq. 1) go to 36\n      do 85 i=1,imid\n      vb(i,m+1,i3) = a(ns)*vb(i,m-1,i1)-c(ns)*vb(i,m+1,i1)\n   85 continue\n   36 if(m .eq. nlat-1) return\n      if(ityp .eq. 2) go to 71\n      ns = ns+1\n      do 70 i=1,imid\n      vb(i,m+2,i3) = a(ns)*vb(i,m,i1)-c(ns)*vb(i,m+2,i1)\n   70 continue\n   71 nstrt = m+3\n      if(ityp .eq. 1) nstrt = m+4\n      if(nstrt .gt. nlat) go to 80\n      nstp = 2\n      if(ityp .eq. 0) nstp = 1\n      do 75 np1=nstrt,nlat,nstp\n      ns = ns+nstp\n      do 75 i=1,imid\n      vb(i,np1,i3) = a(ns)*vb(i,np1-2,i1)+b(ns)*vb(i,np1-2,i3)\n     1                              -c(ns)*vb(i,np1,i1)\n   75 continue\n   80 return\n      end\n      subroutine wbin (ityp,nlat,nlon,m,wb,i3,wwbin,i1,i2)\n      implicit double precision (a-h,o-z)\n      dimension       wb(1)        ,wwbin(1)\n      imid = (nlat+1)/2\n      lim = nlat*imid\n      mmax = min0(nlat,(nlon+1)/2)\n      labc = (max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\n      iw1 = lim+1\n      iw2 = iw1+lim\n      iw3 = iw2+labc\n      iw4 = iw3+labc\nc\nc     the length of wwbin is 2*lim+3*labc\nc\n      call wbin1 (ityp,nlat,m,wb,imid,i3,wwbin,wwbin(iw1),wwbin(iw2),\n     1            wwbin(iw3),wwbin(iw4),i1,i2)\n      return\n      end\n      subroutine wbin1 (ityp,nlat,m,wb,imid,i3,wb1,wb2,a,b,c,i1,i2)\n      implicit double precision (a-h,o-z)\n      dimension       wb(imid,nlat,3),wb1(imid,1),wb2(imid,1),\n     1                a(1),b(1),c(1)\n      ihold = i1\n      i1 = i2\n      i2 = i3\n      i3 = ihold\n      if(m-2)25,30,35\n   25 i1 = 1\n      i2 = 2\n      i3 = 3\n      do 45 np1=2,nlat\n      do 45 i=1,imid\n      wb(i,np1,i3) = wb1(i,np1)\n   45 continue\n      return\n   30 do 50 np1=3,nlat\n      do 50 i=1,imid\n      wb(i,np1,i3) = wb2(i,np1)\n   50 continue\n      return\n   35 ns = ((m-2)*(nlat+nlat-m-1))/2+1\n      if(ityp .eq. 1) go to 36\n      do 85 i=1,imid\n      wb(i,m+1,i3) = a(ns)*wb(i,m-1,i1)-c(ns)*wb(i,m+1,i1)\n   85 continue\n   36 if(m .eq. nlat-1) return\n      if(ityp .eq. 2) go to 71\n      ns = ns+1\n      do 70 i=1,imid\n      wb(i,m+2,i3) = a(ns)*wb(i,m,i1)-c(ns)*wb(i,m+2,i1)\n   70 continue\n   71 nstrt = m+3\n      if(ityp .eq. 1) nstrt = m+4\n      if(nstrt .gt. nlat) go to 80\n      nstp = 2\n      if(ityp .eq. 0) nstp = 1\n      do 75 np1=nstrt,nlat,nstp\n      ns = ns+nstp\n      do 75 i=1,imid\n      wb(i,np1,i3) = a(ns)*wb(i,np1-2,i1)+b(ns)*wb(i,np1-2,i3)\n     1                              -c(ns)*wb(i,np1,i1)\n   75 continue\n   80 return\n      end\n      subroutine dzvk(nlat,m,n,czv,work)\n      implicit double precision (a-h,o-z)\nc\nc     subroutine dzvk computes the coefficients in the trigonometric\nc     expansion of the quadrature function zvbar(n,m,theta)\nc\nc     input parameters\nc\nc     nlat      the number of colatitudes including the poles.\nc\nc     n      the degree (subscript) of wbarv(n,m,theta)\nc\nc     m      the order (superscript) of wbarv(n,m,theta)\nc\nc     work   a work array with at least nlat/2+1 locations\nc\nc     output parameter\nc\nc     czv     the fourier coefficients of zvbar(n,m,theta).\nc\n      dimension czv(1),work(1)\n      double precision czv,sc1,sum,work,t1,t2\n      if(n .le. 0) return\n      lc = (nlat+1)/2\n      sc1 = 2.d0/dfloat(nlat-1)\n      call dvbk(m,n,work,czv)\n      nmod = mod(n,2)\n      mmod = mod(m,2)\n      if(nmod .ne. 0) go to 1\n      if(mmod .ne. 0) go to 2\nc\nc     n even, m even\nc\n      kdo = n/2\n      do 9 id=1,lc\n      i = id+id-2\n      sum = 0.d0\n      do 10 k=1,kdo\n      t1 = 1.d0-(k+k+i)**2\n      t2 = 1.d0-(k+k-i)**2\n      sum = sum+work(k)*(t1-t2)/(t1*t2)\n   10 continue\n      czv(id) = sc1*sum\n    9 continue\n      return\nc\nc     n even, m odd\nc\n    2 kdo = n/2\n      do 5 id=1,lc\n      i = id+id-2\n      sum = 0.d0\n      do 6 k=1,kdo\n      t1 = 1.d0-(k+k+i)**2\n      t2 = 1.d0-(k+k-i)**2\n      sum = sum+work(k)*(t1+t2)/(t1*t2)\n    6 continue\n      czv(id) = sc1*sum\n    5 continue\n      return\n    1 if(mmod .ne. 0) go to 3\nc\nc     n odd, m even\nc\n      kdo = (n+1)/2\n      do 19 id=1,lc\n      i = id+id-3\n      sum = 0.d0\n      do 20 k=1,kdo\n      t1 = 1.d0-(k+k-1+i)**2\n      t2 = 1.d0-(k+k-1-i)**2\n      sum = sum+work(k)*(t1-t2)/(t1*t2)\n   20 continue\n      czv(id) = sc1*sum\n   19 continue\n      return\nc\nc     n odd, m odd\nc\n    3 kdo = (n+1)/2\n      do 15 id=1,lc\n      i = id+id-1\n      sum = 0.d0\n      do 16 k=1,kdo\n      t1 = 1.d0-(k+k-1+i)**2\n      t2 = 1.d0-(k+k-1-i)**2\n      sum = sum+work(k)*(t1+t2)/(t1*t2)\n   16 continue\n      czv(id) = sc1*sum\n   15 continue\n      return\n      end\n      subroutine dzvt(nlat,m,n,th,czv,zvh)\n      implicit double precision (a-h,o-z)\nc\nc     subroutine dzvt tabulates the function zvbar(n,m,theta)\nc     at theta = th in double precision\nc\nc     input parameters\nc\nc     nlat      the number of colatitudes including the poles.\nc\nc     n      the degree (subscript) of zvbar(n,m,theta)\nc\nc     m      the order (superscript) of zvbar(n,m,theta)\nc\nc     czv     the fourier coefficients of zvbar(n,m,theta)\nc             as computed by subroutine zwk.\nc\nc     output parameter\nc\nc     zvh     zvbar(m,n,theta) evaluated at theta = th\nc\n      dimension czv(1)\n      double precision th,czv,zvh,cth,sth,cdt,sdt,chh\n      zvh = 0.d0\n      if(n .le. 0) return\n      lc = (nlat+1)/2\n      lq = lc-1\n      ls = lc-2\n      cth = dcos(th)\n      sth = dsin(th)\n      cdt = cth*cth-sth*sth\n      sdt = 2.d0*sth*cth\n      lmod = mod(nlat,2)\n      mmod = mod(m,2)\n      nmod = mod(n,2)\n      if(lmod .eq. 0) go to 50\n      if(nmod .ne. 0) go to 1\n      cth = cdt\n      sth = sdt\n      if(mmod .ne. 0) go to 2\nc\nc     nlat odd  n even  m even\nc\n      do 10 k=1,ls\n      zvh = zvh+czv(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   10 continue\n      return\nc\nc     nlat odd  n even  m odd\nc\n    2 zvh = .5d0*czv(1)\n      do 20 k=2,lq\n      zvh = zvh+czv(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   20 continue\n      zvh = zvh+.5d0*czv(lc)*dcos((nlat-1)*th)\n      return\n    1 if(mmod .ne. 0) go to 3\nc\nc     nlat odd  n odd  m even\nc\n      do 30 k=1,lq\n      zvh = zvh+czv(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   30 continue\n      return\nc\nc     nlat odd  n odd  m odd\nc\n    3 do 40 k=1,lq\n      zvh = zvh+czv(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   40 continue\n      return\n   50 if(nmod .ne. 0) go to 51\n      cth = cdt\n      sth = sdt\n      if(mmod .ne. 0) go to 52\nc\nc     nlat even  n even  m even\nc\n      do 55 k=1,lq\n      zvh = zvh+czv(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   55 continue\n      return\nc\nc     nlat even  n even  m odd\nc\n   52 zvh = .5d0*czv(1)\n      do 57 k=2,lc\n      zvh = zvh+czv(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   57 continue\n      return\n   51 if(mmod .ne. 0) go to 53\nc\nc     nlat even  n odd  m even\nc\n      do 58 k=1,lq\n      zvh = zvh+czv(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   58 continue\n      return\nc\nc     nlat even  n odd  m odd\nc\n   53 zvh = .5d0*czv(lc)*dcos((nlat-1)*th)\n      do 60 k=1,lq\n      zvh = zvh+czv(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   60 continue\n      return\n      end\n      subroutine dzwk(nlat,m,n,czw,work)\n      implicit double precision (a-h,o-z)\nc\nc     subroutine dzwk computes the coefficients in the trigonometric\nc     expansion of the quadrature function zwbar(n,m,theta)\nc\nc     input parameters\nc\nc     nlat      the number of colatitudes including the poles.\nc\nc     n      the degree (subscript) of zwbar(n,m,theta)\nc\nc     m      the order (superscript) of zwbar(n,m,theta)\nc\nc     work   a work array with at least nlat/2+1 locations\nc\nc     output parameter\nc\nc     czw     the fourier coefficients of zwbar(n,m,theta).\nc\n      dimension czw(1),work(1)\n      double precision czw,work,sc1,sum,t1,t2\n      if(n .le. 0) return\n      lc = (nlat+1)/2\n      sc1 = 2.d0/dfloat(nlat-1)\n      call dwbk(m,n,work,czw)\n      nmod = mod(n,2)\n      mmod = mod(m,2)\n      if(nmod .ne. 0) go to 1\n      if(mmod .ne. 0) go to 2\nc\nc     n even, m even\nc\n      kdo = n/2\n      do 19 id=1,lc\n      i = id+id-3\n      sum = 0.d0\n      do 20 k=1,kdo\n      t1 = 1.d0-(k+k-1+i)**2\n      t2 = 1.d0-(k+k-1-i)**2\n      sum = sum+work(k)*(t1-t2)/(t1*t2)\n   20 continue\n      czw(id) = sc1*sum\n   19 continue\n      return\nc\nc     n even, m odd\nc\n    2 kdo = n/2\n      do 15 id=1,lc\n      i = id+id-1\n      sum = 0.d0\n      do 16 k=1,kdo\n      t1 = 1.d0-(k+k-1+i)**2\n      t2 = 1.d0-(k+k-1-i)**2\n      sum = sum+work(k)*(t1+t2)/(t1*t2)\n   16 continue\n      czw(id) = sc1*sum\n   15 continue\n      return\n    1 if(mmod .ne. 0) go to 3\nc\nc     n odd, m even\nc\n      kdo = (n-1)/2\n      do 9 id=1,lc\n      i = id+id-2\n      sum = 0.d0\n      do 10 k=1,kdo\n      t1 = 1.d0-(k+k+i)**2\n      t2 = 1.d0-(k+k-i)**2\n      sum = sum+work(k)*(t1-t2)/(t1*t2)\n   10 continue\n      czw(id) = sc1*sum\n    9 continue\n      return\nc\nc     n odd, m odd\nc\n    3 kdo = (n+1)/2\n      do 5 id=1,lc\n      i = id+id-2\n      sum = work(1)/(1.d0-i*i)\n      if(kdo .lt. 2) go to 29\n      do 6 kp1=2,kdo\n      k = kp1-1\n      t1 = 1.d0-(k+k+i)**2\n      t2 = 1.d0-(k+k-i)**2\n      sum = sum+work(kp1)*(t1+t2)/(t1*t2)\n    6 continue\n   29 czw(id) = sc1*sum\n    5 continue\n      return\n      end\n      subroutine dzwt(nlat,m,n,th,czw,zwh)\n      implicit double precision (a-h,o-z)\nc\nc     subroutine dzwt tabulates the function zwbar(n,m,theta)\nc     at theta = th in double precision\nc\nc     input parameters\nc\nc     nlat      the number of colatitudes including the poles.\nc            nlat must be an odd integer\nc\nc     n      the degree (subscript) of zwbar(n,m,theta)\nc\nc     m      the order (superscript) of zwbar(n,m,theta)\nc\nc     czw     the fourier coefficients of zwbar(n,m,theta)\nc             as computed by subroutine zwk.\nc\nc     output parameter\nc\nc     zwh     zwbar(m,n,theta) evaluated at theta = th\nc\n      dimension czw(1)\n      double precision czw,zwh,th,cth,sth,cdt,sdt,chh\n      zwh = 0.d0\n      if(n .le. 0) return\n      lc = (nlat+1)/2\n      lq = lc-1\n      ls = lc-2\n      cth = dcos(th)\n      sth = dsin(th)\n      cdt = cth*cth-sth*sth\n      sdt = 2.d0*sth*cth\n      lmod = mod(nlat,2)\n      mmod = mod(m,2)\n      nmod = mod(n,2)\n      if(lmod .eq. 0) go to 50\n      if(nmod .ne. 0) go to 1\n      if(mmod .ne. 0) go to 2\nc\nc     nlat odd  n even  m even\nc\n      do 30 k=1,lq\n      zwh = zwh+czw(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   30 continue\n      return\nc\nc     nlat odd  n even  m odd\nc\n    2 do 40 k=1,lq\n      zwh = zwh+czw(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   40 continue\n      return\n    1 cth = cdt\n      sth = sdt\n      if(mmod .ne. 0) go to 3\nc\nc     nlat odd  n odd  m even\nc\n      do 10 k=1,ls\n      zwh = zwh+czw(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   10 continue\n      return\nc\nc     nlat odd  n odd  m odd\nc\n    3 zwh = .5d0*czw(1)\n      do 20 k=2,lq\n      zwh = zwh+czw(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   20 continue\n      zwh = zwh+.5d0*czw(lc)*dcos((nlat-1)*th)\n      return\n   50 if(nmod .ne. 0) go to 51\n      if(mmod .ne. 0) go to 52\nc\nc     nlat even  n even  m even\nc\n      do 55 k=1,lq\n      zwh = zwh+czw(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   55 continue\n      return\nc\nc     nlat even  n even  m odd\nc\n   52 zwh = .5d0*czw(lc)*dcos((nlat-1)*th)\n      do 60 k=1,lq\n      zwh = zwh+czw(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   60 continue\n      return\n   51 cth = cdt\n      sth = sdt\n      if(mmod .ne. 0) go to 53\nc\nc     nlat even  n odd  m even\nc\n      do 65 k=1,lq\n      zwh = zwh+czw(k+1)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   65 continue\n      return\nc\nc     nlat even  n odd  m odd\nc\n   53 zwh = .5d0*czw(1)\n      do 70 k=2,lc\n      zwh = zwh+czw(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   70 continue\n      return\n      end\n      subroutine dvbk(m,n,cv,work)\n      implicit double precision (a-h,o-z)\n      double precision cv(1),work(1),fn,fk,cf\n      cv(1) = 0.d0\n      if(n .le. 0) return\n      fn = n\n      srnp1 = dsqrt(fn*(fn+1.d0))\n      cf = 2.d0*m/srnp1\n      modn = mod(n,2)\n      modm = mod(m,2)\n      call dnlfk(m,n,work)\n      if(modn .ne. 0) go to 70\n      ncv = n/2\n      if(ncv .eq. 0) return\n      fk = 0.d0\n      if(modm .ne. 0) go to 60\nc\nc     n even m even\nc\n      do 55 l=1,ncv\n      fk = fk+2.d0\n      cv(l) = -fk*work(l+1)/srnp1\n   55 continue\n      return\nc\nc     n even m odd\nc\n   60 do 65 l=1,ncv\n      fk = fk+2.d0\n      cv(l) = fk*work(l)/srnp1\n   65 continue\n      return\n   70 ncv = (n+1)/2\n      fk = -1.d0\n      if(modm .ne. 0) go to 80\nc\nc     n odd m even\nc\n      do 75 l=1,ncv\n      fk = fk+2.d0\n      cv(l) = -fk*work(l)/srnp1\n   75 continue\n      return\nc\nc     n odd m odd\nc\n   80 do 85 l=1,ncv\n      fk = fk+2.d0\n      cv(l) = fk*work(l)/srnp1\n   85 continue\n      return\n      end\n      subroutine dwbk(m,n,cw,work)\n      implicit double precision (a-h,o-z)\n      double precision cw(1),work(1),fn,cf,srnp1\n      cw(1) = 0.d0\n      if(n.le.0 .or. m.le.0) return\n      fn = n\n      srnp1 = dsqrt(fn*(fn+1.d0))\n      cf = 2.d0*m/srnp1\n      modn = mod(n,2)\n      modm = mod(m,2)\n      call dnlfk(m,n,work)\n      if(m .eq. 0) go to 50\n      if(modn .ne. 0) go to 30\n      l = n/2\n      if(l .eq. 0) go to 50\n      if(modm .ne. 0) go to 20\nc\nc     n even m even\nc\n      cw(l) = -cf*work(l+1)\n   10 l = l-1\n      if(l .le. 0) go to 50\n      cw(l) = cw(l+1)-cf*work(l+1)\n      go to 10\nc\nc     n even m odd\nc\n   20 cw(l) = cf*work(l)\n   25 l = l-1\n      if(l .le. 0) go to 50\n      cw(l) = cw(l+1)+cf*work(l)\n      go to 25\n   30 if(modm .ne. 0) go to 40\n      l = (n-1)/2\n      if(l .eq. 0) go to 50\nc\nc     n odd m even\nc\n      cw(l) = -cf*work(l+1)\n   35 l = l-1\n      if(l .le. 0) go to 50\n      cw(l) = cw(l+1)-cf*work(l+1)\n      go to 35\nc\nc     n odd m odd\nc\n   40 l = (n+1)/2\n      cw(l) = cf*work(l)\n   45 l = l-1\n      if(l .le. 0) go to 50\n      cw(l) = cw(l+1)+cf*work(l)\n      go to 45\n   50 return\n      end\n      subroutine dvbt(m,n,theta,cv,vh)\n      implicit double precision (a-h,o-z)\n      dimension cv(1)\n      double precision cv,vh,theta,cth,sth,cdt,sdt,chh\n      vh = 0.d0\n      if(n.eq.0) return\n      cth = dcos(theta)\n      sth = dsin(theta)\n      cdt = cth*cth-sth*sth\n      sdt = 2.d0*sth*cth\n      mmod = mod(m,2)\n      nmod = mod(n,2)\n      if(nmod .ne. 0) go to 1\n      cth = cdt\n      sth = sdt\n      if(mmod .ne. 0) go to 2\nc\nc     n even  m even\nc\n      ncv = n/2\n      do 10 k=1,ncv\n      vh = vh+cv(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   10 continue\n      return\nc\nc     n even  m odd\nc\n    2 ncv = n/2\n      do 15 k=1,ncv\n      vh = vh+cv(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   15 continue\n      return\n    1 if(mmod .ne. 0) go to 3\nc\nc     n odd m even\nc\n      ncv = (n+1)/2\n      do 20 k=1,ncv\n      vh = vh+cv(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   20 continue\n      return\nc\nc case m odd and n odd\nc\n    3 ncv = (n+1)/2\n      do 25 k=1,ncv\n      vh = vh+cv(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   25 continue\n      return\n      end\n      subroutine dwbt(m,n,theta,cw,wh)\n      implicit double precision (a-h,o-z)\n      dimension cw(1)\n      double precision theta,cw,wh,cth,sth,cdt,sdt,chh\n      wh = 0.d0\n      if(n.le.0 .or. m.le.0) return\n      cth = dcos(theta)\n      sth = dsin(theta)\n      cdt = cth*cth-sth*sth\n      sdt = 2.d0*sth*cth\n      mmod=mod(m,2)\n      nmod=mod(n,2)\n      if(nmod .ne. 0) go to 1\n      if(mmod .ne. 0) go to 2\nc\nc     n even  m even\nc\n      ncw = n/2\n      do 10 k=1,ncw\n      wh = wh+cw(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   10 continue\n      return\nc\nc     n even  m odd\nc\n    2 ncw = n/2\n      do 8 k=1,ncw\n      wh = wh+cw(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n    8 continue\n      return\n    1 cth = cdt\n      sth = sdt\n      if(mmod .ne. 0) go to 3\nc\nc     n odd m even\nc\n      ncw = (n-1)/2\n      do 20 k=1,ncw\n      wh = wh+cw(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   20 continue\n      return\nc\nc case m odd and n odd\nc\n    3 ncw = (n+1)/2\n      wh = .5d0*cw(1)\n      if(ncw.lt.2) return\n      do 25 k=2,ncw\n      wh = wh+cw(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   25 continue\n      return\n      end\n      subroutine rabcv(nlat,nlon,abc)\n      implicit double precision (a-h,o-z)\nc\nc     subroutine rabcp computes the coefficients in the recurrence\nc     relation for the functions vbar(m,n,theta). array abc\nc     must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2 locations.\nc\n      dimension abc(1)\n      mmax = min0(nlat,(nlon+1)/2)\n      labc = (max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\n      iw1 = labc+1\n      iw2 = iw1+labc\n      call rabcv1(nlat,nlon,abc,abc(iw1),abc(iw2))\n      return\n      end\n      subroutine rabcv1(nlat,nlon,a,b,c)\n      implicit double precision (a-h,o-z)\nc\nc     coefficients a, b, and c for computing vbar(m,n,theta) are\nc     stored in location ((m-2)*(nlat+nlat-m-1))/2+n+1\nc\n      dimension a(1),b(1),c(1)\n      mmax = min0(nlat,(nlon+1)/2)\n      if(mmax .lt. 3) return\n      do 215 mp1=3,mmax\n      m = mp1-1\n      ns = ((m-2)*(nlat+nlat-m-1))/2+1\n      fm = dfloat(m)\n      tm = fm+fm\n      temp = tm*(tm-1.d0)\n      tpn = (fm-2.d0)*(fm-1.d0)/(fm*(fm+1.d0))\n      a(ns) = dsqrt(tpn*(tm+1.d0)*(tm-2.d0)/temp)\n      c(ns) = dsqrt(2.d0/temp)\n      if(m .eq. nlat-1) go to 215\n      ns = ns+1\n      temp = tm*(tm+1.d0)\n      tpn = (fm-1.d0)*fm/((fm+1.d0)*(fm+2.d0))\n      a(ns) = dsqrt(tpn*(tm+3.d0)*(tm-2.d0)/temp)\n      c(ns) = dsqrt(6.d0/temp)\n      mp3 = m+3\n      if(mp3 .gt. nlat) go to 215\n      do 210 np1=mp3,nlat\n      n = np1-1\n      ns = ns+1\n      fn = dfloat(n)\n      tn = fn+fn\n      cn = (tn+1.d0)/(tn-3.d0)\n      tpn = (fn-2.d0)*(fn-1.d0)/(fn*(fn+1.d0))\n      fnpm = fn+fm\n      fnmm = fn-fm\n      temp = fnpm*(fnpm-1.d0)\n      a(ns) = dsqrt(tpn*cn*(fnpm-3.d0)*(fnpm-2.d0)/temp)\n      b(ns) = dsqrt(tpn*cn*fnmm*(fnmm-1.d0)/temp)\n      c(ns) = dsqrt((fnmm+1.d0)*(fnmm+2.d0)/temp)\n  210 continue\n  215 continue\n      return\n      end\n      subroutine rabcw(nlat,nlon,abc)\n      implicit double precision (a-h,o-z)\nc\nc     subroutine rabcw computes the coefficients in the recurrence\nc     relation for the functions wbar(m,n,theta). array abc\nc     must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2 locations.\nc\n      dimension abc(1)\n      mmax = min0(nlat,(nlon+1)/2)\n      labc = (max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\n      iw1 = labc+1\n      iw2 = iw1+labc\n      call rabcw1(nlat,nlon,abc,abc(iw1),abc(iw2))\n      return\n      end\n      subroutine rabcw1(nlat,nlon,a,b,c)\n      implicit double precision (a-h,o-z)\nc\nc     coefficients a, b, and c for computing wbar(m,n,theta) are\nc     stored in location ((m-2)*(nlat+nlat-m-1))/2+n+1\nc\n      dimension a(1),b(1),c(1)\n      mmax = min0(nlat,(nlon+1)/2)\n      if(mmax .lt. 4) return\n      do 215 mp1=4,mmax\n      m = mp1-1\n      ns = ((m-2)*(nlat+nlat-m-1))/2+1\n      fm = dfloat(m)\n      tm = fm+fm\n      temp = tm*(tm-1.d0)\n      tpn = (fm-2.d0)*(fm-1.d0)/(fm*(fm+1.d0))\n      tph = fm/(fm-2.d0)\n      a(ns) = tph*dsqrt(tpn*(tm+1.d0)*(tm-2.d0)/temp)\n      c(ns) = tph*dsqrt(2.d0/temp)\n      if(m .eq. nlat-1) go to 215\n      ns = ns+1\n      temp = tm*(tm+1.d0)\n      tpn = (fm-1.d0)*fm/((fm+1.d0)*(fm+2.d0))\n      tph = fm/(fm-2.d0)\n      a(ns) = tph*dsqrt(tpn*(tm+3.d0)*(tm-2.d0)/temp)\n      c(ns) = tph*dsqrt(6.d0/temp)\n      mp3 = m+3\n      if(mp3 .gt. nlat) go to 215\n      do 210 np1=mp3,nlat\n      n = np1-1\n      ns = ns+1\n      fn = dfloat(n)\n      tn = fn+fn\n      cn = (tn+1.d0)/(tn-3.d0)\n      fnpm = fn+fm\n      fnmm = fn-fm\n      temp = fnpm*(fnpm-1.d0)\n      tpn = (fn-2.d0)*(fn-1.d0)/(fn*(fn+1.d0))\n      tph = fm/(fm-2.d0)\n      a(ns) = tph*dsqrt(tpn*cn*(fnpm-3.d0)*(fnpm-2.d0)/temp)\n      b(ns) = dsqrt(tpn*cn*fnmm*(fnmm-1.d0)/temp)\n      c(ns) = tph*dsqrt((fnmm+1.d0)*(fnmm+2.d0)/temp)\n  210 continue\n  215 continue\n      return\n      end\n      subroutine vtinit (nlat,nlon,wvbin,dwork)\n      implicit double precision (a-h,o-z)\n      dimension       wvbin(*)\n      double precision dwork(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     the length of wvbin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of dwork is nlat+2\nc\n      call vtini1 (nlat,nlon,imid,wvbin,wvbin(iw1),dwork,\n     1                                       dwork(nlat/2+2))\n      return\n      end\n      subroutine vtini1 (nlat,nlon,imid,vb,abc,cvb,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     locations where mmax = min0(nlat,(nlon+1)/2)\nc     cvb and work must each have nlat/2+1 locations\nc\n      dimension vb(imid,nlat,2),abc(1),cvb(1)\n      double precision pi,dt,cvb,th,vbh,work(*)\n      pi = 4.d0*datan(1.d0)\n      dt = pi/(nlat-1)\n      mdo = min0(2,nlat,(nlon+1)/2)\n      do 160 mp1=1,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dvtk(m,n,cvb,work)\n      do 165 i=1,imid\n      th = (i-1)*dt\n      call dvtt(m,n,th,cvb,vbh)\n      vb(i,np1,mp1) = vbh\n  165 continue\n  160 continue\n      call rabcv(nlat,nlon,abc)\n      return\n      end\n      subroutine wtinit (nlat,nlon,wwbin,dwork)\n      implicit double precision (a-h,o-z)\n      dimension       wwbin(1)\n      double precision dwork(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     the length of wwbin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of dwork is nlat+2\nc\n      call wtini1 (nlat,nlon,imid,wwbin,wwbin(iw1),dwork,\n     1                                       dwork(nlat/2+2))\n      return\n      end\n      subroutine wtini1 (nlat,nlon,imid,wb,abc,cwb,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     locations where mmax = min0(nlat,(nlon+1)/2)\nc     cwb and work must each have nlat/2+1 locations\nc\n      dimension wb(imid,nlat,2),abc(1)\n      double precision pi,dt,cwb(*),wbh,th,work(*)\n      pi = 4.d0*datan(1.d0)\n      dt = pi/(nlat-1)\n      mdo = min0(3,nlat,(nlon+1)/2)\n      if(mdo .lt. 2) return\n      do 160 mp1=2,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dwtk(m,n,cwb,work)\n      do 165 i=1,imid\n      th = (i-1)*dt\n      call dwtt(m,n,th,cwb,wbh)\n      wb(i,np1,m) = wbh\n  165 continue\n  160 continue\n      call rabcw(nlat,nlon,abc)\n      return\n      end\n      subroutine vtgint (nlat,nlon,theta,wvbin,work)\n      implicit double precision (a-h,o-z)\n      dimension       wvbin(*)\n      double precision theta(*), work(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     theta is a double precision array with (nlat+1)/2 locations\nc     nlat is the maximum value of n+1\nc     the length of wvbin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of work is nlat+2\nc\n      call vtgit1 (nlat,nlon,imid,theta,wvbin,wvbin(iw1),\n     +                        work,work(nlat/2+2))\n      return\n      end\n      subroutine vtgit1 (nlat,nlon,imid,theta,vb,abc,cvb,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     locations where mmax = min0(nlat,(nlon+1)/2)\nc     cvb and work must each have nlat/2+1   locations\nc\n      dimension vb(imid,nlat,2),abc(*)\n      double precision theta(*),cvb(*),work(*),vbh\n      mdo = min0(2,nlat,(nlon+1)/2)\n      do 160 mp1=1,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dvtk(m,n,cvb,work)\n      do 165 i=1,imid\n      call dvtt(m,n,theta(i),cvb,vbh)\n      vb(i,np1,mp1) = vbh\n  165 continue\n  160 continue\n      call rabcv(nlat,nlon,abc)\n      return\n      end\n      subroutine wtgint (nlat,nlon,theta,wwbin,work)\n      implicit double precision (a-h,o-z)\n      dimension       wwbin(*)\n      double precision theta(*), work(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     theta is a double precision array with (nlat+1)/2 locations\nc     nlat is the maximum value of n+1\nc     the length of wwbin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of work is nlat+2\nc\n      call wtgit1 (nlat,nlon,imid,theta,wwbin,wwbin(iw1),\n     1                        work,work(nlat/2+2))\n      return\n      end\n      subroutine wtgit1 (nlat,nlon,imid,theta,wb,abc,cwb,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*((nlat-3)*nlat+2)/2 locations\nc     cwb and work must each have nlat/2+1 locations\nc\n      dimension wb(imid,nlat,2),abc(1)\n      double precision theta(*), cwb(*), work(*), wbh\n      mdo = min0(3,nlat,(nlon+1)/2)\n      if(mdo .lt. 2) return\n      do 160 mp1=2,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dwtk(m,n,cwb,work)\n      do 165 i=1,imid\n      call dwtt(m,n,theta(i),cwb,wbh)\n      wb(i,np1,m) = wbh\n  165 continue\n  160 continue\n      call rabcw(nlat,nlon,abc)\n      return\n      end\n      subroutine dvtk(m,n,cv,work)\n      implicit double precision (a-h,o-z)\n      double precision cv(*),work(*),fn,fk,cf,srnp1\n      cv(1) = 0.d0\n      if(n .le. 0) return\n      fn = n\n      srnp1 = dsqrt(fn*(fn+1.d0))\n      cf = 2.d0*m/srnp1\n      modn = mod(n,2)\n      modm = mod(m,2)\n      call dnlfk(m,n,work)\n      if(modn .ne. 0) go to 70\n      ncv = n/2\n      if(ncv .eq. 0) return\n      fk = 0.d0\n      if(modm .ne. 0) go to 60\nc\nc     n even m even\nc\n      do 55 l=1,ncv\n      fk = fk+2.d0\n      cv(l) = -fk*fk*work(l+1)/srnp1\n   55 continue\n      return\nc\nc     n even m odd\nc\n   60 do 65 l=1,ncv\n      fk = fk+2.d0\n      cv(l) = -fk*fk*work(l)/srnp1\n   65 continue\n      return\n   70 ncv = (n+1)/2\n      fk = -1.d0\n      if(modm .ne. 0) go to 80\nc\nc     n odd m even\nc\n      do 75 l=1,ncv\n      fk = fk+2.d0\n      cv(l) = -fk*fk*work(l)/srnp1\n   75 continue\n      return\nc\nc     n odd m odd\nc\n   80 do 85 l=1,ncv\n      fk = fk+2.d0\n      cv(l) = -fk*fk*work(l)/srnp1\n   85 continue\n      return\n      end\n      subroutine dwtk(m,n,cw,work)\n      implicit double precision (a-h,o-z)\n      double precision cw(*),work(*),fn,cf,srnp1\n      cw(1) = 0.d0\n      if(n.le.0 .or. m.le.0) return\n      fn = n\n      srnp1 = dsqrt(fn*(fn+1.d0))\n      cf = 2.d0*m/srnp1\n      modn = mod(n,2)\n      modm = mod(m,2)\n      call dnlfk(m,n,work)\n      if(m .eq. 0) go to 50\n      if(modn .ne. 0) go to 30\n      l = n/2\n      if(l .eq. 0) go to 50\n      if(modm .ne. 0) go to 20\nc\nc     n even m even\nc\n      cw(l) = -cf*work(l+1)\n   10 l = l-1\n      if(l .le. 0) go to 50\n      cw(l) = cw(l+1)-cf*work(l+1)\n      cw(l+1) = (l+l+1)*cw(l+1)\n      go to 10\nc\nc     n even m odd\nc\n   20 cw(l) = cf*work(l)\n   25 l = l-1\n      if(l) 50,27,26\n   26 cw(l) = cw(l+1)+cf*work(l)\n   27 cw(l+1) = -(l+l+1)*cw(l+1)\n      go to 25\n   30 if(modm .ne. 0) go to 40\n      l = (n-1)/2\n      if(l .eq. 0) go to 50\nc\nc     n odd m even\nc\n      cw(l) = -cf*work(l+1)\n   35 l = l-1\n      if(l) 50,37,36\n   36 cw(l) = cw(l+1)-cf*work(l+1)\n   37 cw(l+1) = (l+l+2)*cw(l+1)\n      go to 35\nc\nc     n odd m odd\nc\n   40 l = (n+1)/2\n      cw(l) = cf*work(l)\n   45 l = l-1\n      if(l) 50,47,46\n   46 cw(l) = cw(l+1)+cf*work(l)\n   47 cw(l+1) = -(l+l)*cw(l+1)\n      go to 45\n   50 return\n      end\n      subroutine dvtt(m,n,theta,cv,vh)\n      implicit double precision (a-h,o-z)\n      dimension cv(1)\n      double precision cv,vh,theta,cth,sth,cdt,sdt,chh\n      vh = 0.d0\n      if(n.eq.0) return\n      cth = dcos(theta)\n      sth = dsin(theta)\n      cdt = cth*cth-sth*sth\n      sdt = 2.d0*sth*cth\n      mmod = mod(m,2)\n      nmod = mod(n,2)\n      if(nmod .ne. 0) go to 1\n      cth = cdt\n      sth = sdt\n      if(mmod .ne. 0) go to 2\nc\nc     n even  m even\nc\n      ncv = n/2\n      do 10 k=1,ncv\n      vh = vh+cv(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   10 continue\n      return\nc\nc     n even  m odd\nc\n    2 ncv = n/2\n      do 15 k=1,ncv\n      vh = vh+cv(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   15 continue\n      return\n    1 if(mmod .ne. 0) go to 3\nc\nc     n odd m even\nc\n      ncv = (n+1)/2\n      do 20 k=1,ncv\n      vh = vh+cv(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   20 continue\n      return\nc\nc case m odd and n odd\nc\n    3 ncv = (n+1)/2\n      do 25 k=1,ncv\n      vh = vh+cv(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   25 continue\n      return\n      end\n      subroutine dwtt(m,n,theta,cw,wh)\n      implicit double precision (a-h,o-z)\n      dimension cw(1)\n      double precision theta,cw,wh,cth,sth,cdt,sdt,chh\n      wh = 0.d0\n      if(n.le.0 .or. m.le.0) return\n      cth = dcos(theta)\n      sth = dsin(theta)\n      cdt = cth*cth-sth*sth\n      sdt = 2.d0*sth*cth\n      mmod=mod(m,2)\n      nmod=mod(n,2)\n      if(nmod .ne. 0) go to 1\n      if(mmod .ne. 0) go to 2\nc\nc     n even  m even\nc\n      ncw = n/2\n      do 10 k=1,ncw\n      wh = wh+cw(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   10 continue\n      return\nc\nc     n even  m odd\nc\n    2 ncw = n/2\n      do 8 k=1,ncw\n      wh = wh+cw(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n    8 continue\n      return\n    1 cth = cdt\n      sth = sdt\n      if(mmod .ne. 0) go to 3\nc\nc     n odd m even\nc\n      ncw = (n-1)/2\n      do 20 k=1,ncw\n      wh = wh+cw(k)*cth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   20 continue\n      return\nc\nc case m odd and n odd\nc\n    3 ncw = (n+1)/2\n      wh = 0.d0\n      if(ncw.lt.2) return\n      do 25 k=2,ncw\n      wh = wh+cw(k)*sth\n      chh = cdt*cth-sdt*sth\n      sth = sdt*cth+cdt*sth\n      cth = chh\n   25 continue\n      return\n      end\n      subroutine vbgint (nlat,nlon,theta,wvbin,work)\n      implicit double precision (a-h,o-z)\n      dimension       wvbin(1)\n      double precision theta(*),work(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     theta is a double precision array with (nlat+1)/2 locations\nc     nlat is the maximum value of n+1\nc     the length of wvbin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of work is nlat+2\nc\n      call vbgit1 (nlat,nlon,imid,theta,wvbin,wvbin(iw1),\n     +                        work,work(nlat/2+2))\n      return\n      end\n      subroutine vbgit1 (nlat,nlon,imid,theta,vb,abc,cvb,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc     locations where mmax = min0(nlat,(nlon+1)/2)\nc     cvb and work must each have nlat/2+1 locations\nc\n      dimension vb(imid,nlat,2),abc(1)\n      double precision cvb(1),theta(1),vbh,work(1)\n      mdo = min0(2,nlat,(nlon+1)/2)\n      do 160 mp1=1,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dvbk(m,n,cvb,work)\n      do 165 i=1,imid\n      call dvbt(m,n,theta(i),cvb,vbh)\n      vb(i,np1,mp1) = vbh\n  165 continue\n  160 continue\n      call rabcv(nlat,nlon,abc)\n      return\n      end\n      subroutine wbgint (nlat,nlon,theta,wwbin,work)\n      implicit double precision (a-h,o-z)\n      dimension       wwbin(1)\n      double precision work(*),theta(*)\n      imid = (nlat+1)/2\n      iw1 = 2*nlat*imid+1\nc\nc     theta is a double precision array with (nlat+1)/2 locations\nc     nlat is the maximum value of n+1\nc     the length of wwbin is 2*nlat*imid+3*((nlat-3)*nlat+2)/2\nc     the length of work is nlat+2\nc\n      call wbgit1 (nlat,nlon,imid,theta,wwbin,wwbin(iw1),\n     +                        work,work(nlat/2+2))\n      return\n      end\n      subroutine wbgit1 (nlat,nlon,imid,theta,wb,abc,cwb,work)\n      implicit double precision (a-h,o-z)\nc\nc     abc must have 3*((nlat-3)*nlat+2)/2 locations\nc     cwb and work must each have nlat/2+1 locations\nc\n      dimension wb(imid,nlat,2),abc(1)\n      double precision cwb(1),theta(1),wbh,work(1)\n      mdo = min0(3,nlat,(nlon+1)/2)\n      if(mdo .lt. 2) return\n      do 160 mp1=2,mdo\n      m = mp1-1\n      do 160 np1=mp1,nlat\n      n = np1-1\n      call dwbk(m,n,cwb,work)\n      do 165 i=1,imid\n      call dwbt(m,n,theta(i),cwb,wbh)\n      wb(i,np1,m) = wbh\n  165 continue\n  160 continue\n      call rabcw(nlat,nlon,abc)\n      return\n      end\n\n"
  },
  {
    "path": "external/SPHEREPACK/vhags.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc ... file vhags.f\nc\nc     this file contains code and documentation for subroutines\nc     vhags and vhagsi\nc\nc ... files which must be loaded with vhags.f\nc\nc     sphcom.f, hrfft.f, gaqd.f\nc\nc     subroutine vhags(nstrvw,nstrbc,noffsvw,noffsbc,\nc                      nlat,nlon,ityp,nt,v,w,idvw,jdvw,br,bi,cr,ci,\nc    +                 mdab,ndab,wvhags,lvhags,work,lwork,ierror)\nc\nc     subroutine vhags performs the vector spherical harmonic analysis\nc     on the vector field (v,w) and stores the result in the arrays\nc     br, bi, cr, and ci. v(i,j) and w(i,j) are the colatitudinal \nc     (measured from the north pole) and east longitudinal components\nc     respectively, located at the gaussian colatitude point theta(i)\nc     and longitude phi(j) = (j-1)*2*pi/nlon. the spectral\nc     representation of (v,w) is given at output parameters v,w in \nc     subroutine vhses.  \nc\nc     input parameters\nc\nc     nstrvw,nstrbc the strides in v,w and br,bi,cr,ci\nc     noffsvw,noffsbc the offset in v,w and br,bi,cr,ci\nc               -- must be between 0 and stride-1\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are computed\nc            in radians in theta(1) <...< theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid point\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than zero. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for ityp=0,1,2.  For this reason an error code will\nc           be returned if ityp>2.\nc\nc     ityp   = 0  no symmetries exist about the equator. the analysis\nc                 is performed on the entire sphere.  i.e. on the\nc                 arrays v(i,j),w(i,j) for i=1,...,nlat and \nc                 j=1,...,nlon.   \nc\nc            = 1  no symmetries exist about the equator. the analysis\nc                 is performed on the entire sphere.  i.e. on the\nc                 arrays v(i,j),w(i,j) for i=1,...,nlat and \nc                 j=1,...,nlon. the curl of (v,w) is zero. that is, \nc                 (d/dtheta (sin(theta) w) - dv/dphi)/sin(theta) = 0. \nc                 the coefficients cr and ci are zero.\nc\nc            = 2  no symmetries exist about the equator. the analysis\nc                 is performed on the entire sphere.  i.e. on the\nc                 arrays v(i,j),w(i,j) for i=1,...,nlat and \nc                 j=1,...,nlon. the divergence of (v,w) is zero. i.e., \nc                 (d/dtheta (sin(theta) v) + dw/dphi)/sin(theta) = 0. \nc                 the coefficients br and bi are zero.\nc\nc            = 3  v is symmetric and w is antisymmetric about the \nc                 equator. the analysis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the analysis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the analysis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 4  v is symmetric and w is antisymmetric about the \nc                 equator. the analysis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the analysis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the analysis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc                 the curl of (v,w) is zero. that is, \nc                 (d/dtheta (sin(theta) w) - dv/dphi)/sin(theta) = 0. \nc                 the coefficients cr and ci are zero.\nc\nc            = 5  v is symmetric and w is antisymmetric about the \nc                 equator. the analysis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the analysis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the analysis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc                 the divergence of (v,w) is zero. i.e., \nc                 (d/dtheta (sin(theta) v) + dw/dphi)/sin(theta) = 0. \nc                 the coefficients br and bi are zero.\nc\nc            = 6  v is antisymmetric and w is symmetric about the \nc                 equator. the analysis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the analysis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the analysis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 7  v is antisymmetric and w is symmetric about the \nc                 equator. the analysis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the analysis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the analysis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc                 the curl of (v,w) is zero. that is, \nc                 (d/dtheta (sin(theta) w) - dv/dphi)/sin(theta) = 0. \nc                 the coefficients cr and ci are zero.\nc\nc            = 8  v is antisymmetric and w is symmetric about the \nc                 equator. the analysis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the analysis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the analysis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc                 the divergence of (v,w) is zero. i.e., \nc                 (d/dtheta (sin(theta) v) + dw/dphi)/sin(theta) = 0. \nc                 the coefficients br and bi are zero.\nc\nc\nc     nt     the number of analyses.  in the program that calls vhags,\nc            the arrays v,w,br,bi,cr, and ci can be three dimensional\nc            in which case multiple analyses will be performed.\nc            the third index is the analysis index which assumes the \nc            values k=1,...,nt.  for a single analysis set nt=1. the\nc            discription of the remaining parameters is simplified\nc            by assuming that nt=1 or that all the arrays are two\nc            dimensional.\nc\nc     v,w    two or three dimensional arrays (see input parameter nt)\nc            that contain the vector function to be analyzed.\nc            v is the colatitudnal component and w is the east \nc            longitudinal component. v(i,j),w(i,j) contain the\nc            components at the gaussian colatitude point theta(i)\nc            and longitude phi(j) = (j-1)*2*pi/nlon. the index ranges\nc            are defined above at the input parameter ityp.\nc\nc     idvw   the first dimension of the arrays v,w as it appears in\nc            the program that calls vhags. if ityp .le. 2 then idvw\nc            must be at least nlat.  if ityp .gt. 2 and nlat is\nc            even then idvw must be at least nlat/2. if ityp .gt. 2\nc            and nlat is odd then idvw must be at least (nlat+1)/2.\nc\nc     jdvw   the second dimension of the arrays v,w as it appears in\nc            the program that calls vhags. jdvw must be at least nlon.\nc\nc     mdab   the first dimension of the arrays br,bi,cr, and ci as it\nc            appears in the program that calls vhags. mdab must be at\nc            least min0(nlat,nlon/2) if nlon is even or at least\nc            min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc     ndab   the second dimension of the arrays br,bi,cr, and ci as it\nc            appears in the program that calls vhags. ndab must be at\nc            least nlat.\nc\nc     wvhags an array which must be initialized by subroutine vhgsi.\nc            once initialized, wvhags can be used repeatedly by vhags\nc            as long as nlon and nlat remain unchanged.  wvhags must\nc            not be altered between calls of vhags.\nc\nc     lvhags the dimension of the array wvhags as it appears in the\nc            program that calls vhags. define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lvhags must be at least\nc\nc            l1*l2(nlat+nlat-l1+1)+nlon+15\nc\nc        ??? (nlat+1)*(nlat+1)*nlat/2 + nlon + 15\nc\nc\nc\nc     work   a work array that does not have to be saved.\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls vhags. define\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            if ityp .le. 2 then lwork must be at least\nc            the larger of the two quantities\nc\nc               3*nlat*(nlat+1)+2  (required by vhagsi)\nc\nc            and\nc\nc               (2*nt+1)*nlat*nlon\nc\nc            if ityp .gt. 2 then lwork must be at least\nc            the larger of the two quantities\nc\nc               3*nlat*(nlat+1)+2  (required by vhagsi)\nc\nc            and\nc\nc              (2*nt+1)*l2*nlon\nc\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc     br,bi  two or three dimensional arrays (see input parameter nt)\nc     cr,ci  that contain the vector spherical harmonic coefficients\nc            in the spectral representation of v(i,j) and w(i,j) given \nc            in the discription of subroutine vhses. br(mp1,np1),\nc            bi(mp1,np1),cr(mp1,np1), and ci(mp1,np1) are computed \nc            for mp1=1,...,mmax and np1=mp1,...,nlat except for np1=nlat\nc            and odd mp1. mmax=min0(nlat,nlon/2) if nlon is even or \nc            mmax=min0(nlat,(nlon+1)/2) if nlon is odd. \nc      \nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of ityp\nc            = 4  error in the specification of nt\nc            = 5  error in the specification of idvw\nc            = 6  error in the specification of jdvw\nc            = 7  error in the specification of mdab\nc            = 8  error in the specification of ndab\nc            = 9  error in the specification of lvhags\nc            = 10 error in the specification of lwork\nc\nc\nc     subroutine vhagsi(nlat,nlon,wvhags,lvhags,work,lwork,ierror)\nc\nc     subroutine vhagsi initializes the array wvhags which can then be\nc     used repeatedly by subroutine vhags until nlat or nlon is changed.\nc\nc     input parameters\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are computed\nc            in radians in theta(1) <...< theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid point\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than zero. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     lvhags the dimension of the array wvhags as it appears in the\nc            program that calls vhagsi.  define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lvhags must be at least\nc\nc               3*nlat*(nlat+1)+2  (required by vhagsi)\nc\nc     dwork  a double precision work space that does not need to be saved\nc\nc     ldwork the dimension of the array dwork as it appears in the\nc            program that calls vhagsi. ldwork must be at least\nc\nc                   (3*nlat*(nlat+3)+2)/2\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc     wvhags an array which is initialized for use by subroutine vhags.\nc            once initialized, wvhags can be used repeatedly by vhags\nc            as long as nlat and nlon remain unchanged.  wvhags must not\nc            be altered between calls of vhags.\nc\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of lvhags\nc            = 4  error in the specification of ldwork\nc\n      subroutine vhags(nstrvw,nstrbc,noffsvw,noffsbc,\n     1           nlat,nlon,ityp,nt,v,w,idvw,jdvw,br,bi,cr,ci,\n     1           mdab,ndab,wvhags,lvhags,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension v(nstrvw,idvw,jdvw,1),w(nstrvw,idvw,jdvw,1),\n     1          br(nstrbc,mdab,ndab,1),bi(nstrbc,mdab,ndab,1),\n     1          cr(nstrbc,mdab,ndab,1),ci(nstrbc,mdab,ndab,1),\n     2          work(1),wvhags(1)\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 1) return\n      ierror = 3\n      if(ityp.lt.0 .or. ityp.gt.8) return\nc     see note above about symmetries\n      if(ityp.gt.2) return\n      ierror = 4\n      if(nt .lt. 0) return\n      ierror = 5\n      imid = (nlat+1)/2\n      if((ityp.le.2 .and. idvw.lt.nlat) .or.\n     1   (ityp.gt.2 .and. idvw.lt.imid)) return\n      ierror = 6\n      if(jdvw .lt. nlon) return\n      ierror = 7\n      mmax = min0(nlat,(nlon+1)/2)\n      if(mdab .lt. mmax) return\n      ierror = 8\n      if(ndab .lt. nlat) return\n      ierror = 9\n      idz = (mmax*(nlat+nlat-mmax+1))/2\n      lzimn = idz*imid\n      if(lvhags .lt. lzimn+lzimn+nlon+15) return\n      ierror = 10\n      idv = nlat\n      if(ityp .gt. 2) idv = imid\n      lnl = nt*idv*nlon\n      if(lwork .lt. lnl+lnl+idv*nlon) return\n      ierror = 11\n      if((noffsvw.lt.0).or.(noffsvw.ge.nstrvw)) return\n      if((noffsbc.lt.0).or.(noffsbc.ge.nstrbc)) return\n      ierror = 0\n      ist = 0\n      if(ityp .le. 2) ist = imid\nc\nc     set wvhags pointers\nc\n      lmn = nlat*(nlat+1)/2\n      jw1 = 1\n      jw2 = jw1+imid*lmn\n      jw3 = jw2+imid*lmn\nc\nc     set work pointers\nc\n      iw1 = ist+1\n      iw2 = lnl+1\n      iw3 = iw2+ist\n      iw4 = iw2+lnl\n      call vhags1(nstrvw,nstrbc,noffsvw,noffsbc,\n     +     nlat,nlon,ityp,nt,imid,idvw,jdvw,v,w,mdab,ndab,\n     +     br,bi,cr,ci,idv,work,work(iw1),work(iw2),work(iw3),\n     +     work(iw4),idz,wvhags(jw1),wvhags(jw2),wvhags(jw3))\n      return\n      end\n\n      subroutine vhags1(nstrvw,nstrbc,noffsvw,noffsbc,\n     +nlat,nlon,ityp,nt,imid,idvw,jdvw,v,w,mdab,\n     +ndab,br,bi,cr,ci,idv,ve,vo,we,wo,work,idz,vb,wb,wrfft)\n      implicit double precision (a-h,o-z)\n      dimension v(nstrvw,idvw,jdvw,1),w(nstrvw,idvw,jdvw,1),\n     1          br(nstrbc,mdab,ndab,1),bi(nstrbc,mdab,ndab,1),\n     1          cr(nstrbc,mdab,ndab,1),ci(nstrbc,mdab,ndab,1),\n     2          ve(idv,nlon,1),vo(idv,nlon,1),we(idv,nlon,1),\n     3          wo(idv,nlon,1),work(1),\n     4          vb(imid,1),wb(imid,1),wrfft(1)\n      ncvw=1+noffsvw\n      ncbc=1+noffsbc\n      nlp1 = nlat+1\n      tsn = 2.d0/nlon\n      fsn = 4.d0/nlon\n      mlat = mod(nlat,2)\n      mlon = mod(nlon,2)\n      mmax = min0(nlat,(nlon+1)/2)\n      imm1 = imid\n      if(mlat .ne. 0) imm1 = imid-1\n      if(ityp .gt. 2) go to 3  \n      do 5 k=1,nt \n      do 5 i=1,imm1\n      do 5 j=1,nlon\n      ve(i,j,k) = tsn*(v(ncvw,i,j,k)+v(ncvw,nlp1-i,j,k))\n      vo(i,j,k) = tsn*(v(ncvw,i,j,k)-v(ncvw,nlp1-i,j,k))\n      we(i,j,k) = tsn*(w(ncvw,i,j,k)+w(ncvw,nlp1-i,j,k))\n      wo(i,j,k) = tsn*(w(ncvw,i,j,k)-w(ncvw,nlp1-i,j,k))\n    5 continue\n      go to 2\n    3 do 8 k=1,nt\n      do 8 i=1,imm1 \n      do 8 j=1,nlon\n      ve(i,j,k) = fsn*v(ncvw,i,j,k)\n      vo(i,j,k) = fsn*v(ncvw,i,j,k)\n      we(i,j,k) = fsn*w(ncvw,i,j,k)\n      wo(i,j,k) = fsn*w(ncvw,i,j,k)\n    8 continue\n    2 if(mlat .eq. 0) go to 7\n      do 6 k=1,nt \n      do 6 j=1,nlon\n      ve(imid,j,k) = tsn*v(ncvw,imid,j,k)\n      we(imid,j,k) = tsn*w(ncvw,imid,j,k)\n    6 continue\n    7 do 9 k=1,nt\n      call hrfftf(idv,nlon,ve(1,1,k),idv,wrfft,work)\n      call hrfftf(idv,nlon,we(1,1,k),idv,wrfft,work)\n    9 continue \n      ndo1 = nlat\n      ndo2 = nlat\n      if(mlat .ne. 0) ndo1 = nlat-1\n      if(mlat .eq. 0) ndo2 = nlat-1\n      if(ityp.eq.2 .or. ityp.eq.5 .or. ityp.eq.8) go to 11 \n      do 10 k=1,nt\n      do 10 mp1=1,mmax\n      do 10 np1=mp1,nlat\n      br(1,mp1,np1,k)=0.d0\n      bi(1,mp1,np1,k)=0.d0\n   10 continue\n   11 if(ityp.eq.1 .or. ityp.eq.4 .or. ityp.eq.7) go to 13 \n      do 12 k=1,nt\n      do 12 mp1=1,mmax\n      do 12 np1=mp1,nlat\n      cr(ncbc,mp1,np1,k)=0.d0\n      ci(ncbc,mp1,np1,k)=0.d0\n   12 continue\n   13 itypp = ityp+1\n      go to (1,100,200,300,400,500,600,700,800),itypp\nc\nc     case ityp=0 ,  no symmetries\nc\nc     case m=0\nc\n    1 do 15 k=1,nt\n      do 15 i=1,imid\n      do 15 np1=2,ndo2,2\n      br(1,1,np1,k) = br(1,1,np1,k)+vb(i,np1)*ve(i,1,k)\n      cr(ncbc,1,np1,k) = cr(ncbc,1,np1,k)-vb(i,np1)*we(i,1,k)\n   15 continue\n      do 16 k=1,nt\n      do 16 i=1,imm1\n      do 16 np1=3,ndo1,2\n      br(1,1,np1,k) = br(1,1,np1,k)+vb(i,np1)*vo(i,1,k)\n      cr(ncbc,1,np1,k) = cr(ncbc,1,np1,k)-vb(i,np1)*wo(i,1,k)\n   16 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 20 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 17\n      do 23 k=1,nt\n      do 23 i=1,imm1\n      do 23 np1=mp1,ndo1,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)+vb(i,np1+mb)*vo(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*we(i,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)+vb(i,np1+mb)*vo(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*we(i,2*mp1-2,k)\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*wo(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*ve(i,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*wo(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*ve(i,2*mp1-2,k)\n   23 continue\n      if(mlat .eq. 0) go to 17\n      do 24 k=1,nt\n      do 24 np1=mp1,ndo1,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)\n     1                +wb(imid,np1+mb)*we(imid,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)\n     1                -wb(imid,np1+mb)*we(imid,2*mp1-2,k)\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                +wb(imid,np1+mb)*ve(imid,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                -wb(imid,np1+mb)*ve(imid,2*mp1-2,k)\n   24 continue\n   17 if(mp2 .gt. ndo2) go to 20\n      do 21 k=1,nt\n      do 21 i=1,imm1\n      do 21 np1=mp2,ndo2,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)+vb(i,np1+mb)*ve(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*wo(i,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)+vb(i,np1+mb)*ve(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*wo(i,2*mp1-2,k)\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*we(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*vo(i,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*we(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*vo(i,2*mp1-2,k)\n   21 continue\n      if(mlat .eq. 0) go to 20\n      do 22 k=1,nt\n      do 22 np1=mp2,ndo2,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)\n     1                +vb(imid,np1+mb)*ve(imid,2*mp1-2,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)\n     1                +vb(imid,np1+mb)*ve(imid,2*mp1-1,k)\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                -vb(imid,np1+mb)*we(imid,2*mp1-2,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                -vb(imid,np1+mb)*we(imid,2*mp1-1,k)\n   22 continue\n   20 continue\n      return\nc\nc     case ityp=1 ,  no symmetries but cr and ci equal zero\nc\nc     case m=0\nc\n  100 do 115 k=1,nt\n      do 115 i=1,imid\n      do 115 np1=2,ndo2,2\n      br(1,1,np1,k) = br(1,1,np1,k)+vb(i,np1)*ve(i,1,k)\n  115 continue\n      do 116 k=1,nt\n      do 116 i=1,imm1\n      do 116 np1=3,ndo1,2\n      br(1,1,np1,k) = br(1,1,np1,k)+vb(i,np1)*vo(i,1,k)\n  116 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 120 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 117\n      do 123 k=1,nt\n      do 123 i=1,imm1\n      do 123 np1=mp1,ndo1,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)+vb(i,np1+mb)*vo(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*we(i,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)+vb(i,np1+mb)*vo(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*we(i,2*mp1-2,k)\n  123 continue\n      if(mlat .eq. 0) go to 117\n      do 124 k=1,nt\n      do 124 np1=mp1,ndo1,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)\n     1                +wb(imid,np1+mb)*we(imid,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)\n     1                -wb(imid,np1+mb)*we(imid,2*mp1-2,k)\n  124 continue\n  117 if(mp2 .gt. ndo2) go to 120\n      do 121 k=1,nt\n      do 121 i=1,imm1\n      do 121 np1=mp2,ndo2,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)+vb(i,np1+mb)*ve(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*wo(i,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)+vb(i,np1+mb)*ve(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*wo(i,2*mp1-2,k)\n  121 continue\n      if(mlat .eq. 0) go to 120\n      do 122 k=1,nt\n      do 122 np1=mp2,ndo2,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)\n     1                +vb(imid,np1+mb)*ve(imid,2*mp1-2,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)\n     1                +vb(imid,np1+mb)*ve(imid,2*mp1-1,k)\n  122 continue\n  120 continue\n      return\nc\nc     case ityp=2 ,  no symmetries but br and bi equal zero   \nc\nc     case m=0\nc\n  200 do 215 k=1,nt\n      do 215 i=1,imid\n      do 215 np1=2,ndo2,2\n      cr(ncbc,1,np1,k) = cr(ncbc,1,np1,k)-vb(i,np1)*we(i,1,k)\n  215 continue\n      do 216 k=1,nt\n      do 216 i=1,imm1\n      do 216 np1=3,ndo1,2\n      cr(ncbc,1,np1,k) = cr(ncbc,1,np1,k)-vb(i,np1)*wo(i,1,k)\n  216 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 220 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 217\n      do 223 k=1,nt\n      do 223 i=1,imm1\n      do 223 np1=mp1,ndo1,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*wo(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*ve(i,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*wo(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*ve(i,2*mp1-2,k)\n  223 continue\n      if(mlat .eq. 0) go to 217\n      do 224 k=1,nt\n      do 224 np1=mp1,ndo1,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                +wb(imid,np1+mb)*ve(imid,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                -wb(imid,np1+mb)*ve(imid,2*mp1-2,k)\n  224 continue\n  217 if(mp2 .gt. ndo2) go to 220\n      do 221 k=1,nt\n      do 221 i=1,imm1\n      do 221 np1=mp2,ndo2,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*we(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*vo(i,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*we(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*vo(i,2*mp1-2,k)\n  221 continue\n      if(mlat .eq. 0) go to 220\n      do 222 k=1,nt\n      do 222 np1=mp2,ndo2,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                -vb(imid,np1+mb)*we(imid,2*mp1-2,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                -vb(imid,np1+mb)*we(imid,2*mp1-1,k)\n  222 continue\n  220 continue\n      return\nc\nc     case ityp=3 ,  v even , w odd\nc\nc     case m=0\nc\n  300 do 315 k=1,nt\n      do 315 i=1,imid\n      do 315 np1=2,ndo2,2\n      br(1,1,np1,k) = br(1,1,np1,k)+vb(i,np1)*ve(i,1,k)\n  315 continue\n      do 316 k=1,nt\n      do 316 i=1,imm1\n      do 316 np1=3,ndo1,2\n      cr(ncbc,1,np1,k) = cr(ncbc,1,np1,k)-vb(i,np1)*wo(i,1,k)\n  316 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 320 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 317\n      do 323 k=1,nt\n      do 323 i=1,imm1\n      do 323 np1=mp1,ndo1,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*wo(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*ve(i,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*wo(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*ve(i,2*mp1-2,k)\n  323 continue\n      if(mlat .eq. 0) go to 317\n      do 324 k=1,nt\n      do 324 np1=mp1,ndo1,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                +wb(imid,np1+mb)*ve(imid,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                -wb(imid,np1+mb)*ve(imid,2*mp1-2,k)\n  324 continue\n  317 if(mp2 .gt. ndo2) go to 320\n      do 321 k=1,nt\n      do 321 i=1,imm1\n      do 321 np1=mp2,ndo2,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)+vb(i,np1+mb)*ve(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*wo(i,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)+vb(i,np1+mb)*ve(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*wo(i,2*mp1-2,k)\n  321 continue\n      if(mlat .eq. 0) go to 320\n      do 322 k=1,nt\n      do 322 np1=mp2,ndo2,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)\n     1                +vb(imid,np1+mb)*ve(imid,2*mp1-2,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)\n     1                +vb(imid,np1+mb)*ve(imid,2*mp1-1,k)\n  322 continue\n  320 continue\n      return\nc\nc     case ityp=4 ,  v even, w odd, and cr and ci equal 0. \nc\nc     case m=0\nc\n  400 do 415 k=1,nt\n      do 415 i=1,imid\n      do 415 np1=2,ndo2,2\n      br(1,1,np1,k) = br(1,1,np1,k)+vb(i,np1)*ve(i,1,k)\n  415 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 420 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp2 .gt. ndo2) go to 420\n      do 421 k=1,nt\n      do 421 i=1,imm1\n      do 421 np1=mp2,ndo2,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)+vb(i,np1+mb)*ve(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*wo(i,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)+vb(i,np1+mb)*ve(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*wo(i,2*mp1-2,k)\n  421 continue\n      if(mlat .eq. 0) go to 420\n      do 422 k=1,nt\n      do 422 np1=mp2,ndo2,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)\n     1                +vb(imid,np1+mb)*ve(imid,2*mp1-2,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)\n     1                +vb(imid,np1+mb)*ve(imid,2*mp1-1,k)\n  422 continue\n  420 continue\n      return\nc\nc     case ityp=5   v even, w odd, and br and bi equal zero\nc\nc     case m=0\nc\n  500 do 516 k=1,nt\n      do 516 i=1,imm1\n      do 516 np1=3,ndo1,2\n      cr(ncbc,1,np1,k) = cr(ncbc,1,np1,k)-vb(i,np1)*wo(i,1,k)\n  516 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 520 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 520\n      do 523 k=1,nt\n      do 523 i=1,imm1\n      do 523 np1=mp1,ndo1,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*wo(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*ve(i,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*wo(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*ve(i,2*mp1-2,k)\n  523 continue\n      if(mlat .eq. 0) go to 520\n      do 524 k=1,nt\n      do 524 np1=mp1,ndo1,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                +wb(imid,np1+mb)*ve(imid,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                -wb(imid,np1+mb)*ve(imid,2*mp1-2,k)\n  524 continue\n  520 continue\n      return\nc\nc     case ityp=6 ,  v odd , w even\nc\nc     case m=0\nc\n  600 do 615 k=1,nt\n      do 615 i=1,imid\n      do 615 np1=2,ndo2,2\n      cr(ncbc,1,np1,k) = cr(ncbc,1,np1,k)-vb(i,np1)*we(i,1,k)\n  615 continue\n      do 616 k=1,nt\n      do 616 i=1,imm1\n      do 616 np1=3,ndo1,2\n      br(1,1,np1,k) = br(1,1,np1,k)+vb(i,np1)*vo(i,1,k)\n  616 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 620 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 617\n      do 623 k=1,nt\n      do 623 i=1,imm1\n      do 623 np1=mp1,ndo1,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)+vb(i,np1+mb)*vo(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*we(i,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)+vb(i,np1+mb)*vo(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*we(i,2*mp1-2,k)\n  623 continue\n      if(mlat .eq. 0) go to 617\n      do 624 k=1,nt\n      do 624 np1=mp1,ndo1,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)\n     1                +wb(imid,np1+mb)*we(imid,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)\n     1                -wb(imid,np1+mb)*we(imid,2*mp1-2,k)\n  624 continue\n  617 if(mp2 .gt. ndo2) go to 620\n      do 621 k=1,nt\n      do 621 i=1,imm1\n      do 621 np1=mp2,ndo2,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*we(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*vo(i,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*we(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*vo(i,2*mp1-2,k)\n  621 continue\n      if(mlat .eq. 0) go to 620\n      do 622 k=1,nt\n      do 622 np1=mp2,ndo2,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                -vb(imid,np1+mb)*we(imid,2*mp1-2,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                -vb(imid,np1+mb)*we(imid,2*mp1-1,k)\n  622 continue\n  620 continue\n      return\nc\nc     case ityp=7   v odd, w even, and cr and ci equal zero\nc\nc     case m=0\nc\n  700 do 716 k=1,nt\n      do 716 i=1,imm1\n      do 716 np1=3,ndo1,2\n      br(1,1,np1,k) = br(1,1,np1,k)+vb(i,np1)*vo(i,1,k)\n  716 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 720 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 720\n      do 723 k=1,nt\n      do 723 i=1,imm1\n      do 723 np1=mp1,ndo1,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)+vb(i,np1+mb)*vo(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*we(i,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)+vb(i,np1+mb)*vo(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*we(i,2*mp1-2,k)\n  723 continue\n      if(mlat .eq. 0) go to 720\n      do 724 k=1,nt\n      do 724 np1=mp1,ndo1,2\n      br(1,mp1,np1,k) = br(1,mp1,np1,k)\n     1                +wb(imid,np1+mb)*we(imid,2*mp1-1,k)\n      bi(1,mp1,np1,k) = bi(1,mp1,np1,k)\n     1                -wb(imid,np1+mb)*we(imid,2*mp1-2,k)\n  724 continue\n  720 continue\n      return\nc\nc     case ityp=8   v odd, w even, and both br and bi equal zero\nc\nc     case m=0\nc\n  800 do 815 k=1,nt\n      do 815 i=1,imid\n      do 815 np1=2,ndo2,2\n      cr(ncbc,1,np1,k) = cr(ncbc,1,np1,k)-vb(i,np1)*we(i,1,k)\n  815 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) return\n      do 820 mp1=2,mmax\n      m = mp1-1\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp2 .gt. ndo2) go to 820\n      do 821 k=1,nt\n      do 821 i=1,imm1\n      do 821 np1=mp2,ndo2,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*we(i,2*mp1-2,k)\n     1                                 +wb(i,np1+mb)*vo(i,2*mp1-1,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                                 -vb(i,np1+mb)*we(i,2*mp1-1,k)\n     1                                 -wb(i,np1+mb)*vo(i,2*mp1-2,k)\n  821 continue\n      if(mlat .eq. 0) go to 820\n      do 822 k=1,nt\n      do 822 np1=mp2,ndo2,2\n      cr(ncbc,mp1,np1,k) = cr(ncbc,mp1,np1,k)\n     1                -vb(imid,np1+mb)*we(imid,2*mp1-2,k)\n      ci(ncbc,mp1,np1,k) = ci(ncbc,mp1,np1,k)\n     1                -vb(imid,np1+mb)*we(imid,2*mp1-1,k)\n  822 continue\n  820 continue\n      return\n      end\n      subroutine vhagsi(nlat,nlon,wvhags,lvhags,dwork,ldwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension wvhags(lvhags)\n      double precision dwork(ldwork)\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 1) return\n      ierror = 3\n      imid = (nlat+1)/2\n      lmn = (nlat*(nlat+1))/2\n      if(lvhags .lt. 2*(imid*lmn)+nlon+15) return\n      ierror = 4\nc     if (ldwork.lt.nlat*(3*nlat+9)+2) return\n      if (ldwork.lt.(nlat*(3*nlat+9)+2)/2) return\n      ierror = 0\n      jw1 = 1\n      jw2 = jw1+imid*lmn\n      jw3 = jw2+imid*lmn\n      iw1 = 1\n      iw2 = iw1+nlat\n      iw3 = iw2+nlat\n      iw4 = iw3+3*imid*nlat\nc     iw2 = iw1+nlat+nlat\nc     iw3 = iw2+nlat+nlat\nc     iw4 = iw3+6*imid*nlat\n      call vhgai1(nlat,imid,wvhags(jw1),wvhags(jw2),\n     +dwork(iw1),dwork(iw2),dwork(iw3),dwork(iw4))\n      call hrffti(nlon,wvhags(jw3))\n      return\n      end\n      subroutine vhgai1(nlat,imid,vb,wb,dthet,dwts,dpbar,work)\n      implicit double precision (a-h,o-z)\n      dimension vb(imid,*),wb(imid,*)\n      double precision abel,bbel,cbel,ssqr2,dcf\n      double precision dpbar(imid,nlat,3), dthet(*),dwts(*),work(*)\nc     lwk = 4*nlat*(nlat+2)\n      lwk = nlat*(nlat+2)\n      call gaqd(nlat,dthet,dwts,dpbar,lwk,ierror)\nc\nc     compute associated legendre functions\nc\nc     compute m=n=0 legendre polynomials for all theta(i)\nc\n      ssqr2 = 1.d0/dsqrt(2.d0)\n      do 90 i=1,imid\n      dpbar(i,1,1) = ssqr2\n      vb(i,1) = 0.d0\n      wb(i,1) = 0.d0\n   90 continue\nc\nc     main loop for remaining vb, and wb\nc\n      do 100 n=1,nlat-1\n      nm = mod(n-2,3)+1\n      nz = mod(n-1,3)+1\n      np = mod(n,3)+1\nc\nc     compute dpbar for m=0\nc\n      call dnlfk(0,n,work)\n      mn = indx(0,n,nlat)\n      do 105 i=1,imid\n      call dnlft(0,n,dthet(i),work,dpbar(i,1,np))\n  105 continue\nc\nc     compute dpbar for m=1\nc\n      call dnlfk(1,n,work)\n      mn = indx(1,n,nlat)\n      do 106 i=1,imid\n      call dnlft(1,n,dthet(i),work,dpbar(i,2,np))\nc      pbar(i,mn) = dpbar(i,2,np)\n  106 continue\n  104 continue\nc\nc     compute and store dpbar for m=2,n\nc\n      if(n.lt.2) go to 108\n      do 107 m=2,n\n      abel = dsqrt(dfloat((2*n+1)*(m+n-2)*(m+n-3))/\n     1                dfloat((2*n-3)*(m+n-1)*(m+n)))\n      bbel = dsqrt(dfloat((2*n+1)*(n-m-1)*(n-m))/\n     1                dfloat((2*n-3)*(m+n-1)*(m+n)))\n      cbel = dsqrt(dfloat((n-m+1)*(n-m+2))/\n     1                dfloat((m+n-1)*(m+n)))\n      id = indx(m,n,nlat)\n      if (m.ge.n-1) go to 102\n      do 103 i=1,imid\n      dpbar(i,m+1,np) = abel*dpbar(i,m-1,nm)+bbel*dpbar(i,m+1,nm)\n     1                                         -cbel*dpbar(i,m-1,np)\n  103 continue\n      go to 107\n  102 do 101 i=1,imid\n      dpbar(i,m+1,np) = abel*dpbar(i,m-1,nm)-cbel*dpbar(i,m-1,np)\n  101 continue\n  107 continue\nc\nc     compute the derivative of the functions\nc\n  108 continue\n      ix = indx(0,n,nlat)\n      iy = indx(n,n,nlat)\n      do 125 i=1,imid\n      vb(i,ix) = -dpbar(i,2,np)*dwts(i)\n      vb(i,iy) = dpbar(i,n,np)/dsqrt(dfloat(2*(n+1)))*dwts(i)\n125   continue\nc\n      if(n.eq.1) go to 131 \n      dcf = dsqrt(dfloat(4*n*(n+1)))\n      do 130 m=1,n-1\n      ix = indx(m,n,nlat)\n      abel = dsqrt(dfloat((n+m)*(n-m+1)))/dcf\n      bbel = dsqrt(dfloat((n-m)*(n+m+1)))/dcf\n      do 130 i=1,imid\n      vb(i,ix) = (abel*dpbar(i,m,np)-bbel*dpbar(i,m+2,np))*dwts(i)\n130   continue\nc\nc     compute the vector harmonic w(theta) = m*pbar/cos(theta)\nc\nc     set wb=0 for m=0\nc\n  131 continue\n      ix = indx(0,n,nlat)\n      do 220 i=1,imid\n      wb(i,ix) = 0.d0\n220   continue\nc\nc     compute wb for m=1,n\nc\n      dcf = dsqrt(dfloat(n+n+1)/dfloat(4*n*(n+1)*(n+n-1)))\n      do 230 m=1,n\n      ix = indx(m,n,nlat)\n      abel = dcf*dsqrt(dfloat((n+m)*(n+m-1)))\n      bbel = dcf*dsqrt(dfloat((n-m)*(n-m-1)))\n      if(m.ge.n-1) go to 231\n      do 229 i=1,imid\n      wb(i,ix) = (abel*dpbar(i,m,nz) + bbel*dpbar(i,m+2,nz))*dwts(i)\n  229 continue\n      go to 230\n  231 do 228 i=1,imid\n      wb(i,ix) = abel*dpbar(i,m,nz)*dwts(i)\n  228 continue\n  230 continue\n  100 continue\n      return \n      end\n\n      function indx(m,n,nlat)\n      implicit double precision (a-h,o-z)\n      integer indx\n      indx = m*nlat-(m*(m+1))/2+n+1\n      return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/vhsgs.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc ... file vhsgs.f\nc\nc     this file contains code and documentation for subroutines\nc     vhsgs and vhsgsi\nc\nc ... files which must be loaded with vhsgs.f\nc\nc     sphcom.f, hrfft.f, gaqd.f\nc\nc     subroutine vhsgs(nstrvw,nstrbc,noffsvw,noffsbc,\nc                      nlat,nlon,ityp,nt,v,w,idvw,jdvw,br,bi,cr,ci,\nc    +                 mdab,ndab,wvhsgs,lvhsgs,work,lwork,ierror)\nc                                                                              \nc   \nc     subroutine vhsgs performs the vector spherical harmonic synthesis\nc     of the arrays br, bi, cr, and ci and stores the result in the\nc     arrays v and w.  the synthesis is performed on an equally spaced\nc     longitude grid and a gaussian colatitude grid (measured from\nc     the north pole). v(i,j) and w(i,j) are the colatitudinal and\nc     east longitudinal components respectively, located at the i(th)\nc     colatitude gaussian point (see nlat below) and longitude\nc     phi(j) = (j-1)*2*pi/nlon.  the spectral respresentation of (v,w)\nc     is given below at output parameters v,w.\nc\nc     input parameters\nc\nc     nstrvw, nstrbc  strides in v,w and in br,bi,cr,ci\nc     noffsvw, noffsbc  offset in v,w and in br,bi,cr,ci\nc               -- must be between 0 and stride-1\nc            Alternatively, when the two strides equal, you can set both\nc            to -1 in which case the output will be computed for all\nc            values of offset between 0 and stride-1.\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are computed\nc            in radians in theta(1) <...< theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid point\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than zero. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for ityp=0,1,2.  For this reason an error code will\nc           be returned if ityp>2.\nc\nc     ityp   = 0  no symmetries exist about the equator. the synthesis\nc                 is performed on the entire sphere.  i.e. on the\nc                 arrays v(i,j),w(i,j) for i=1,...,nlat and \nc                 j=1,...,nlon.   \nc\nc            = 1  no symmetries exist about the equator. the synthesis\nc                 is performed on the entire sphere.  i.e. on the\nc                 arrays v(i,j),w(i,j) for i=1,...,nlat and \nc                 j=1,...,nlon. the curl of (v,w) is zero. that is, \nc                 (d/dtheta (sin(theta) w) - dv/dphi)/sin(theta) = 0. \nc                 the coefficients cr and ci are zero.\nc\nc            = 2  no symmetries exist about the equator. the synthesis\nc                 is performed on the entire sphere.  i.e. on the\nc                 arrays v(i,j),w(i,j) for i=1,...,nlat and \nc                 j=1,...,nlon. the divergence of (v,w) is zero. i.e., \nc                 (d/dtheta (sin(theta) v) + dw/dphi)/sin(theta) = 0. \nc                 the coefficients br and bi are zero.\nc\nc            = 3  v is symmetric and w is antisymmetric about the \nc                 equator. the synthesis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the synthesis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the synthesis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 4  v is symmetric and w is antisymmetric about the \nc                 equator. the synthesis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the synthesis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the synthesis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc                 the curl of (v,w) is zero. that is, \nc                 (d/dtheta (sin(theta) w) - dv/dphi)/sin(theta) = 0. \nc                 the coefficients cr and ci are zero.\nc\nc            = 5  v is symmetric and w is antisymmetric about the \nc                 equator. the synthesis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the synthesis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the synthesis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc                 the divergence of (v,w) is zero. i.e., \nc                 (d/dtheta (sin(theta) v) + dw/dphi)/sin(theta) = 0. \nc                 the coefficients br and bi are zero.\nc\nc            = 6  v is antisymmetric and w is symmetric about the \nc                 equator. the synthesis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the synthesis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the synthesis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 7  v is antisymmetric and w is symmetric about the \nc                 equator. the synthesis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the synthesis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the synthesis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc                 the curl of (v,w) is zero. that is, \nc                 (d/dtheta (sin(theta) w) - dv/dphi)/sin(theta) = 0. \nc                 the coefficients cr and ci are zero.\nc\nc            = 8  v is antisymmetric and w is symmetric about the \nc                 equator. the synthesis is performed on the northern\nc                 hemisphere only.  i.e., if nlat is odd the synthesis\nc                 is performed on the arrays v(i,j),w(i,j) for \nc                 i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the synthesis is performed on the the arrays\nc                 v(i,j),w(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc                 the divergence of (v,w) is zero. i.e., \nc                 (d/dtheta (sin(theta) v) + dw/dphi)/sin(theta) = 0. \nc                 the coefficients br and bi are zero.\nc\nc\nc     nt     the number of syntheses.  in the program that calls vhsgs,\nc            the arrays v,w,br,bi,cr, and ci can be three dimensional\nc            in which case multiple syntheses will be performed.\nc            the third index is the synthesis index which assumes the \nc            values k=1,...,nt.  for a single synthesis set nt=1. the\nc            discription of the remaining parameters is simplified\nc            by assuming that nt=1 or that all the arrays are two\nc            dimensional.\nc\nc     idvw   the first dimension of the arrays v,w as it appears in\nc            the program that calls vhags. if ityp .le. 2 then idvw\nc            must be at least nlat.  if ityp .gt. 2 and nlat is\nc            even then idvw must be at least nlat/2. if ityp .gt. 2\nc            and nlat is odd then idvw must be at least (nlat+1)/2.\nc\nc     jdvw   the second dimension of the arrays v,w as it appears in\nc            the program that calls vhsgs. jdvw must be at least nlon.\nc\nc     br,bi  two or three dimensional arrays (see input parameter nt)\nc     cr,ci  that contain the vector spherical harmonic coefficients\nc            in the spectral representation of v(i,j) and w(i,j) given\nc            below at the discription of output parameters v and w.\nc\nc     mdab   the first dimension of the arrays br,bi,cr, and ci as it\nc            appears in the program that calls vhsgs. mdab must be at\nc            least min0(nlat,nlon/2) if nlon is even or at least\nc            min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc     ndab   the second dimension of the arrays br,bi,cr, and ci as it\nc            appears in the program that calls vhsgs. ndab must be at\nc            least nlat.\nc\nc     wvhsgs an array which must be initialized by subroutine vhsgsi.\nc            once initialized, wvhsgs can be used repeatedly by vhsgs\nc            as long as nlon and nlat remain unchanged.  wvhsgs must\nc            not be altered between calls of vhsgs.\nc\nc     lvhsgs the dimension of the array wvhsgs as it appears in the\nc            program that calls vhsgs. define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lvhsgs must be at least\nc\nc                 l1*l2*(nlat+nlat-l1+1)+nlon+15+2*nlat\nc\nc\nc     work   a work array that does not have to be saved.\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls vhsgs. define\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            if ityp .le. 2 then lwork must be at least\nc\nc                       (2*nt+1)*nlat*nlon\nc\nc            if ityp .gt. 2 then lwork must be at least\nc\nc                        (2*nt+1)*l2*nlon \nc\nc     **************************************************************\nc\nc     output parameters\nc\nc     v,w    two or three dimensional arrays (see input parameter nt)\nc            in which the synthesis is stored. v is the colatitudinal\nc            component and w is the east longitudinal component. \nc            v(i,j),w(i,j) contain the components at the guassian colatitude\nc            point theta(i) and longitude phi(j) = (j-1)*2*pi/nlon.\nc            the index ranges are defined above at the input parameter\nc            ityp. v and w are computed from the formulas given below.\nc\nc\nc     define\nc\nc     1.  theta is colatitude and phi is east longitude\nc\nc     2.  the normalized associated legendre funnctions\nc\nc         pbar(m,n,theta) = sqrt((2*n+1)*factorial(n-m)\nc                        /(2*factorial(n+m)))*sin(theta)**m/(2**n*\nc                        factorial(n)) times the (n+m)th derivative\nc                        of (x**2-1)**n with respect to x=cos(theta)\nc\nc     3.  vbar(m,n,theta) = the derivative of pbar(m,n,theta) with\nc                           respect to theta divided by the square\nc                           root of n(n+1).\nc\nc         vbar(m,n,theta) is more easily computed in the form\nc\nc         vbar(m,n,theta) = (sqrt((n+m)*(n-m+1))*pbar(m-1,n,theta)\nc         -sqrt((n-m)*(n+m+1))*pbar(m+1,n,theta))/(2*sqrt(n*(n+1)))\nc\nc     4.  wbar(m,n,theta) = m/(sin(theta))*pbar(m,n,theta) divided\nc                           by the square root of n(n+1).\nc\nc         wbar(m,n,theta) is more easily computed in the form\nc\nc         wbar(m,n,theta) = sqrt((2n+1)/(2n-1))*(sqrt((n+m)*(n+m-1))\nc         *pbar(m-1,n-1,theta)+sqrt((n-m)*(n-m-1))*pbar(m+1,n-1,theta))\nc         /(2*sqrt(n*(n+1)))\nc\nc\nc    the colatitudnal dependence of the normalized surface vector\nc                spherical harmonics are defined by\nc\nc     5.    bbar(m,n,theta) = (vbar(m,n,theta),i*wbar(m,n,theta))\nc\nc     6.    cbar(m,n,theta) = (i*wbar(m,n,theta),-vbar(m,n,theta))\nc\nc\nc    the coordinate to index mappings \nc\nc     7.   theta(i) = i(th) gaussian grid point and phi(j) = (j-1)*2*pi/nlon\nc\nc    \nc     the maximum (plus one) longitudinal wave number\nc\nc     8.     mmax = min0(nlat,nlon/2) if nlon is even or\nc            mmax = min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc    if we further define the output vector as\nc\nc     9.    h(i,j) = (v(i,j),w(i,j))\nc\nc    and the complex coefficients\nc\nc     10.   b(m,n) = cmplx(br(m+1,n+1),bi(m+1,n+1))\nc\nc     11.   c(m,n) = cmplx(cr(m+1,n+1),ci(m+1,n+1))\nc\nc\nc    then for i=1,...,nlat and  j=1,...,nlon\nc\nc        the expansion for real h(i,j) takes the form\nc\nc     h(i,j) = the sum from n=1 to n=nlat-1 of the real part of\nc\nc         .5*(b(0,n)*bbar(0,n,theta(i))+c(0,n)*cbar(0,n,theta(i)))\nc\nc     plus the sum from m=1 to m=mmax-1 of the sum from n=m to \nc     n=nlat-1 of the real part of\nc\nc              b(m,n)*bbar(m,n,theta(i))*exp(i*m*phi(j))\nc             +c(m,n)*cbar(m,n,theta(i))*exp(i*m*phi(j))\nc\nc   *************************************************************\nc\nc   in terms of real variables this expansion takes the form\nc\nc             for i=1,...,nlat and  j=1,...,nlon\nc\nc     v(i,j) = the sum from n=1 to n=nlat-1 of\nc\nc               .5*br(ncbc,n+1)*vbar(0,n,theta(i))\nc\nc     plus the sum from m=1 to m=mmax-1 of the sum from n=m to \nc     n=nlat-1 of the real part of\nc\nc       (br(m+1,n+1)*vbar(m,n,theta(i))-ci(m+1,n+1)*wbar(m,n,theta(i)))\nc                                          *cos(m*phi(j))\nc      -(bi(m+1,n+1)*vbar(m,n,theta(i))+cr(m+1,n+1)*wbar(m,n,theta(i)))\nc                                          *sin(m*phi(j))\nc\nc    and for i=1,...,nlat and  j=1,...,nlon\nc\nc     w(i,j) = the sum from n=1 to n=nlat-1 of\nc\nc              -.5*cr(1,n+1)*vbar(0,n,theta(i))\nc\nc     plus the sum from m=1 to m=mmax-1 of the sum from n=m to\nc     n=nlat-1 of the real part of\nc\nc      -(cr(m+1,n+1)*vbar(m,n,theta(i))+bi(m+1,n+1)*wbar(m,n,theta(i)))\nc                                          *cos(m*phi(j))\nc      +(ci(m+1,n+1)*vbar(m,n,theta(i))-br(m+1,n+1)*wbar(m,n,theta(i)))\nc                                          *sin(m*phi(j))\nc\nc\nc      br(m+1,nlat),bi(m+1,nlat),cr(m+1,nlat), and ci(m+1,nlat) are\nc      assumed zero for m even.\nc\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of ityp\nc            = 4  error in the specification of nt\nc            = 5  error in the specification of idvw\nc            = 6  error in the specification of jdvw\nc            = 7  error in the specification of mdab\nc            = 8  error in the specification of ndab\nc            = 9  error in the specification of lvhsgs\nc            = 10 error in the specification of lwork\nc\nc\nc     subroutine vhsgsi(nlat,nlon,wvhsgs,lvhsgs,dwork,ldwork,ierror)\nc\nc     subroutine vhsgsi initializes the array wvhsgs which can then be\nc     used repeatedly by subroutine vhsgs until nlat or nlon is changed.\nc\nc     input parameters\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are computed\nc            in radians in theta(1) <...< theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid point\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than zero. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     lvhsgs the dimension of the array wvhsgs as it appears in the\nc            program that calls vhsgs. define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lvhsgs must be at least\nc\nc                 l1*l2*(nlat+nlat-l1+1)+nlon+15+2*nlat\nc\nc     dwork a double precision work array that does not need to be saved\nc\nc     ldwork the dimension of the array dwork as it appears in the\nc            program that calls vhsgsi. ldwork must be at least\nc\nc                 (3*nlat*(nlat+3)+2)/2\n\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc     wvhsgs an array which is initialized for use by subroutine vhsgs.\nc            once initialized, wvhsgs can be used repeatedly by vhsgs\nc            as long as nlat and nlon remain unchanged.  wvhsgs must not\nc            be altered between calls of vhsgs.\nc\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of lvhsgs\nc            = 4  error in the specification of lwork\nc\n      subroutine vhsgs(nstrvw,nstrbc,noffsvw,noffsbc,\n     +                 nlat,nlon,ityp,nt,v,w,idvw,jdvw,br,bi,cr,ci,\n     +                 mdab,ndab,wvhsgs,lvhsgs,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension v(nstrvw,idvw,jdvw,1),w(nstrvw,idvw,jdvw,1),\n     1          br(nstrbc,mdab,ndab,1),bi(nstrbc,mdab,ndab,1),\n     1          cr(nstrbc,mdab,ndab,1),ci(nstrbc,mdab,ndab,1),\n     2          work(1),wvhsgs(1)\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 1) return\n      ierror = 3\n      if(ityp.lt.0 .or. ityp.gt.8) return\nc     see note above about symmetries\n      if(ityp.gt.2) return\n      ierror = 4\n      if(nt .lt. 0) return\n      ierror = 5\n      imid = (nlat+1)/2\n      if((ityp.le.2 .and. idvw.lt.nlat) .or.\n     1   (ityp.gt.2 .and. idvw.lt.imid)) return\n      ierror = 6\n      if(jdvw .lt. nlon) return\n      ierror = 7\n      mmax = min0(nlat,(nlon+1)/2)\n      if(mdab .lt. mmax) return\n      ierror = 8\n      if(ndab .lt. nlat) return\n      ierror = 9\n      idz = (mmax*(nlat+nlat-mmax+1))/2\n      lzimn = idz*imid\n      if(lvhsgs .lt. lzimn+lzimn+nlon+15) return\n      ierror = 10\n      idv = nlat\n      if(ityp .gt. 2) idv = imid\n      lnl = nt*idv*nlon\n      ng=1\n      if(noffsvw.eq.-1) ng=nstrvw\n      if(lwork .lt. ng*(lnl+lnl+idv*nlon)) return\n      ierror = 11\n      if((noffsvw.eq.-1) .neqv. (noffsbc.eq.-1)) return\n      if((noffsvw.eq.-1) .and.  (nstrvw.ne.nstrbc)) return\n      if((noffsvw.lt.-1) .or.  (noffsvw.ge.nstrvw)) return\n      if((noffsbc.lt.-1) .or.  (noffsbc.ge.nstrbc)) return\n\n      ierror = 0\n      ist = 0\n      if(ityp .le. 2) ist = imid\nc\nc     set wvhsgs pointers\nc\n      lmn = nlat*(nlat+1)/2\n      jw1 = 1\n      jw2 = jw1+imid*lmn\n      jw3 = jw2+imid*lmn\nc\nc     set work pointers\nc\n      iw1 = ng*ist+1\n      iw2 = ng*lnl+1\n      iw3 = iw2+ist*ng\n      iw4 = iw2+lnl*ng\n\n      call vhsgs1(nstrvw,nstrbc,noffsvw,noffsbc,\n     +           nlat,nlon,ityp,nt,imid,idvw,jdvw,v,w,mdab,ndab,\n     +           br,bi,cr,ci,ng,idv,work,work(iw1),work(iw2),work(iw3),\n     +          work(iw4),idz,wvhsgs(jw1),wvhsgs(jw2),wvhsgs(jw3))\n      return\n      end\n\n      subroutine vhsgs1(nstrvw,nstrbc,noffsvw,noffsbc,\n     +   nlat,nlon,ityp,nt,imid,idvw,jdvw,v,w,mdab,\n     1   ndab,br,bi,cr,ci,ng,idv,ve,vo,we,wo,work,idz,vb,wb,wrfft)\n      implicit double precision (a-h,o-z)\n      dimension v(nstrvw,idvw,jdvw,1),w(nstrvw,idvw,jdvw,1),\n     1          br(nstrbc,mdab,ndab,1),bi(nstrbc,mdab,ndab,1),\n     1          cr(nstrbc,mdab,ndab,1),ci(nstrbc,mdab,ndab,1),\n     2          ve(ng,idv,nlon,1),vo(ng,idv,nlon,1),we(ng,idv,nlon,1),\n     3          wo(ng,idv,nlon,1),work(1),wrfft(1),\n     4          vb(imid,1),wb(imid,1)\n      ncvwdiff=noffsvw-noffsbc\n      if(noffsbc.eq.-1) then\n        ncbcmin=1\n        ncbcmax=nstrbc\n      else\n        ncbcmin=1+noffsbc\n        ncbcmax=1+noffsbc\n      end if\nc     do 234 ncbc=ncbcmin,ncbcmax\nc     ncvw=ncvwdiff+ncbc\nc     ig=ncbc-ncbcmin+1\nc     ig=1\n      nlp1 = nlat+1\n      mlat = mod(nlat,2)\n      mlon = mod(nlon,2)\n      mmax = min0(nlat,(nlon+1)/2)\n      imm1 = imid\n      if(mlat .ne. 0) imm1 = imid-1\n      do 10 k=1,nt\n      do 10 j=1,nlon\n      do 10 i=1,idv\n      do 10 ig=1,ng\n      ve(ig,i,j,k) = 0.d0\n      we(ig,i,j,k) = 0.d0\n   10 continue\n      ndo1 = nlat\n      ndo2 = nlat\n      if(mlat .ne. 0) ndo1 = nlat-1\n      if(mlat .eq. 0) ndo2 = nlat-1\n   18 itypp = ityp+1\n      go to (1,100,200,300,400,500,600,700,800),itypp\nc\nc     case ityp=0   no symmetries\nc\nc     case m = 0\nc\n    1 continue\n      do 15 k=1,nt\n      do 15 np1=2,ndo2,2\n      do 15 i=1,imid\n      do 15 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,1,k)=ve(ig,i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n      we(ig,i,1,k)=we(ig,i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n   15 continue\n      do 16 k=1,nt\n      do 16 np1=3,ndo1,2\n      do 16 i=1,imm1\n      do 16 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,1,k)=vo(ig,i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n      wo(ig,i,1,k)=wo(ig,i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n   16 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 30 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 26\n      do 25 k=1,nt\n      do 24 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 23 i=1,imm1\n      do 23 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,2*mp1-2,k)=vo(ig,i,2*mp1-2,k)+br(ncbc,mp1,np1,k)*vb(i,mn)\n      ve(ig,i,2*mp1-2,k)=ve(ig,i,2*mp1-2,k)-ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vo(ig,i,2*mp1-1,k)=vo(ig,i,2*mp1-1,k)+bi(ncbc,mp1,np1,k)*vb(i,mn)\n      ve(ig,i,2*mp1-1,k)=ve(ig,i,2*mp1-1,k)+cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wo(ig,i,2*mp1-2,k)=wo(ig,i,2*mp1-2,k)-cr(ncbc,mp1,np1,k)*vb(i,mn)\n      we(ig,i,2*mp1-2,k)=we(ig,i,2*mp1-2,k)-bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wo(ig,i,2*mp1-1,k)=wo(ig,i,2*mp1-1,k)-ci(ncbc,mp1,np1,k)*vb(i,mn)\n      we(ig,i,2*mp1-1,k)=we(ig,i,2*mp1-1,k)+br(ncbc,mp1,np1,k)*wb(i,mn)\n   23 continue\n      if(mlat .eq. 0) go to 24\n      do 924 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,imid,2*mp1-2,k) = ve(ig,imid,2*mp1-2,k)\n     1                     -ci(ncbc,mp1,np1,k)*wb(imid,mn)\n      ve(ig,imid,2*mp1-1,k) = ve(ig,imid,2*mp1-1,k)\n     1                     +cr(ncbc,mp1,np1,k)*wb(imid,mn)\n      we(ig,imid,2*mp1-2,k) = we(ig,imid,2*mp1-2,k)\n     1                     -bi(ncbc,mp1,np1,k)*wb(imid,mn) \n      we(ig,imid,2*mp1-1,k) = we(ig,imid,2*mp1-1,k)\n     1                     +br(ncbc,mp1,np1,k)*wb(imid,mn)\n  924 continue\n   24 continue\n   25 continue\n   26 if(mp2 .gt. ndo2) go to 30\n      do 29 k=1,nt\n      do 28 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 27 i=1,imm1\n      do 27 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,2*mp1-2,k)=ve(ig,i,2*mp1-2,k)+br(ncbc,mp1,np1,k)*vb(i,mn)\n      vo(ig,i,2*mp1-2,k)=vo(ig,i,2*mp1-2,k)-ci(ncbc,mp1,np1,k)*wb(i,mn)\n      ve(ig,i,2*mp1-1,k)=ve(ig,i,2*mp1-1,k)+bi(ncbc,mp1,np1,k)*vb(i,mn)\n      vo(ig,i,2*mp1-1,k)=vo(ig,i,2*mp1-1,k)+cr(ncbc,mp1,np1,k)*wb(i,mn)\n      we(ig,i,2*mp1-2,k)=we(ig,i,2*mp1-2,k)-cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wo(ig,i,2*mp1-2,k)=wo(ig,i,2*mp1-2,k)-bi(ncbc,mp1,np1,k)*wb(i,mn)\n      we(ig,i,2*mp1-1,k)=we(ig,i,2*mp1-1,k)-ci(ncbc,mp1,np1,k)*vb(i,mn)\n      wo(ig,i,2*mp1-1,k)=wo(ig,i,2*mp1-1,k)+br(ncbc,mp1,np1,k)*wb(i,mn)\n   27 continue\n      if(mlat .eq. 0) go to 28\n      do 928 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,imid,2*mp1-2,k) = ve(ig,imid,2*mp1-2,k)\n     1                     +br(ncbc,mp1,np1,k)*vb(imid,mn) \n      ve(ig,imid,2*mp1-1,k) = ve(ig,imid,2*mp1-1,k)\n     1                     +bi(ncbc,mp1,np1,k)*vb(imid,mn)\n      we(ig,imid,2*mp1-2,k) = we(ig,imid,2*mp1-2,k)\n     1                     -cr(ncbc,mp1,np1,k)*vb(imid,mn)\n      we(ig,imid,2*mp1-1,k) = we(ig,imid,2*mp1-1,k)\n     1                     -ci(ncbc,mp1,np1,k)*vb(imid,mn)\n  928 continue\n   28 continue\n   29 continue\n   30 continue\n      go to 950\nc\nc     case ityp=1   no symmetries,  cr and ci equal zero\nc\nc     case m = 0\nc\n  100 continue\n      do 115 k=1,nt\n      do 115 np1=2,ndo2,2\n      do 115 i=1,imid\n      do 115 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,1,k)=ve(ig,i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  115 continue\n      do 116 k=1,nt\n      do 116 np1=3,ndo1,2\n      do 116 i=1,imm1\n      do 116 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,1,k)=vo(ig,i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  116 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 130 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 126\n      do 125 k=1,nt\n      do 124 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 123 i=1,imm1\n      do 123 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,2*mp1-2,k)=vo(ig,i,2*mp1-2,k)+br(ncbc,mp1,np1,k)*vb(i,mn)\n      vo(ig,i,2*mp1-1,k)=vo(ig,i,2*mp1-1,k)+bi(ncbc,mp1,np1,k)*vb(i,mn)\n      we(ig,i,2*mp1-2,k)=we(ig,i,2*mp1-2,k)-bi(ncbc,mp1,np1,k)*wb(i,mn)\n      we(ig,i,2*mp1-1,k)=we(ig,i,2*mp1-1,k)+br(ncbc,mp1,np1,k)*wb(i,mn)\n  123 continue\n      if(mlat .eq. 0) go to 124\n      do 9124 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      we(ig,imid,2*mp1-2,k) = we(ig,imid,2*mp1-2,k)\n     1                     -bi(ncbc,mp1,np1,k)*wb(imid,mn) \n      we(ig,imid,2*mp1-1,k) = we(ig,imid,2*mp1-1,k)\n     1                     +br(ncbc,mp1,np1,k)*wb(imid,mn)\n 9124 continue\n  124 continue\n  125 continue\n  126 if(mp2 .gt. ndo2) go to 130\n      do 129 k=1,nt\n      do 128 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 127 i=1,imm1\n      do 127 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,2*mp1-2,k)=ve(ig,i,2*mp1-2,k)+br(ncbc,mp1,np1,k)*vb(i,mn)\n      ve(ig,i,2*mp1-1,k)=ve(ig,i,2*mp1-1,k)+bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wo(ig,i,2*mp1-2,k)=wo(ig,i,2*mp1-2,k)-bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wo(ig,i,2*mp1-1,k)=wo(ig,i,2*mp1-1,k)+br(ncbc,mp1,np1,k)*wb(i,mn)\n  127 continue\n      if(mlat .eq. 0) go to 128\n      do 9128 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,imid,2*mp1-2,k) = ve(ig,imid,2*mp1-2,k)\n     1                     +br(ncbc,mp1,np1,k)*vb(imid,mn) \n      ve(ig,imid,2*mp1-1,k) = ve(ig,imid,2*mp1-1,k)\n     1                     +bi(ncbc,mp1,np1,k)*vb(imid,mn)\n 9128 continue\n  128 continue\n  129 continue\n  130 continue\n      go to 950\nc\nc     case ityp=2   no symmetries,  br and bi are equal to zero\nc\nc     case m = 0\nc\n  200 do 215 k=1,nt\n      do 215 np1=2,ndo2,2\n      do 215 i=1,imid\n      do 215 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      we(ig,i,1,k)=we(ig,i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  215 continue\n      do 216 k=1,nt\n      do 216 np1=3,ndo1,2\n      do 216 i=1,imm1\n      do 9216 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      wo(ig,i,1,k)=wo(ig,i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n 9216 continue\n  216 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 230 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 226\n      do 225 k=1,nt\n      do 224 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 223 i=1,imm1\n      do 223 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,2*mp1-2,k)=ve(ig,i,2*mp1-2,k)-ci(ncbc,mp1,np1,k)*wb(i,mn)\n      ve(ig,i,2*mp1-1,k)=ve(ig,i,2*mp1-1,k)+cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wo(ig,i,2*mp1-2,k)=wo(ig,i,2*mp1-2,k)-cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wo(ig,i,2*mp1-1,k)=wo(ig,i,2*mp1-1,k)-ci(ncbc,mp1,np1,k)*vb(i,mn)\n  223 continue\n      if(mlat .eq. 0) go to 224\n      do 9224 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,imid,2*mp1-2,k) = ve(ig,imid,2*mp1-2,k)\n     1                     -ci(ncbc,mp1,np1,k)*wb(imid,mn)\n      ve(ig,imid,2*mp1-1,k) = ve(ig,imid,2*mp1-1,k)\n     1                     +cr(ncbc,mp1,np1,k)*wb(imid,mn)\n 9224 continue\n  224 continue\n  225 continue\n  226 if(mp2 .gt. ndo2) go to 230\n      do 229 k=1,nt\n      do 228 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 227 i=1,imm1\n      do 227 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,2*mp1-2,k)=vo(ig,i,2*mp1-2,k)-ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vo(ig,i,2*mp1-1,k)=vo(ig,i,2*mp1-1,k)+cr(ncbc,mp1,np1,k)*wb(i,mn)\n      we(ig,i,2*mp1-2,k)=we(ig,i,2*mp1-2,k)-cr(ncbc,mp1,np1,k)*vb(i,mn)\n      we(ig,i,2*mp1-1,k)=we(ig,i,2*mp1-1,k)-ci(ncbc,mp1,np1,k)*vb(i,mn)\n  227 continue\n      if(mlat .eq. 0) go to 228\n      do 9228 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      we(ig,imid,2*mp1-2,k) = we(ig,imid,2*mp1-2,k)\n     1                     -cr(ncbc,mp1,np1,k)*vb(imid,mn)\n      we(ig,imid,2*mp1-1,k) = we(ig,imid,2*mp1-1,k)\n     1                     -ci(ncbc,mp1,np1,k)*vb(imid,mn)\n 9228 continue\n  228 continue\n  229 continue\n  230 continue\n      go to 950\nc\nc     case ityp=3   v even,  w odd \nc\nc     case m = 0\nc\n  300 do 315 k=1,nt\n      do 315 np1=2,ndo2,2\n      do 315 i=1,imid\n      do 315 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,1,k)=ve(ig,i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  315 continue\n      do 316 k=1,nt\n      do 316 np1=3,ndo1,2\n      do 316 i=1,imm1\n      do 316 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      wo(ig,i,1,k)=wo(ig,i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  316 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 330 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 326\n      do 325 k=1,nt\n      do 324 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 323 i=1,imm1\n      do 323 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,2*mp1-2,k)=ve(ig,i,2*mp1-2,k)-ci(ncbc,mp1,np1,k)*wb(i,mn)\n      ve(ig,i,2*mp1-1,k)=ve(ig,i,2*mp1-1,k)+cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wo(ig,i,2*mp1-2,k)=wo(ig,i,2*mp1-2,k)-cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wo(ig,i,2*mp1-1,k)=wo(ig,i,2*mp1-1,k)-ci(ncbc,mp1,np1,k)*vb(i,mn)\n  323 continue\n      if(mlat .eq. 0) go to 324\n      do 9324 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,imid,2*mp1-2,k) = ve(ig,imid,2*mp1-2,k)\n     1                     -ci(ncbc,mp1,np1,k)*wb(imid,mn)\n      ve(ig,imid,2*mp1-1,k) = ve(ig,imid,2*mp1-1,k)\n     1                     +cr(ncbc,mp1,np1,k)*wb(imid,mn)\n 9324 continue\n  324 continue\n  325 continue\n  326 if(mp2 .gt. ndo2) go to 330\n      do 329 k=1,nt\n      do 328 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 327 i=1,imm1\n      do 327 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,2*mp1-2,k)=ve(ig,i,2*mp1-2,k)+br(ncbc,mp1,np1,k)*vb(i,mn)\n      ve(ig,i,2*mp1-1,k)=ve(ig,i,2*mp1-1,k)+bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wo(ig,i,2*mp1-2,k)=wo(ig,i,2*mp1-2,k)-bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wo(ig,i,2*mp1-1,k)=wo(ig,i,2*mp1-1,k)+br(ncbc,mp1,np1,k)*wb(i,mn)\n  327 continue\n      if(mlat .eq. 0) go to 328\n      do 9328 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,imid,2*mp1-2,k) = ve(ig,imid,2*mp1-2,k)\n     1                     +br(ncbc,mp1,np1,k)*vb(imid,mn) \n      ve(ig,imid,2*mp1-1,k) = ve(ig,imid,2*mp1-1,k)\n     1                     +bi(ncbc,mp1,np1,k)*vb(imid,mn)\n 9328 continue\n  328 continue\n  329 continue\n  330 continue\n      go to 950\nc\nc     case ityp=4   v even,  w odd, and both cr and ci equal zero \nc\nc     case m = 0\nc\n  400 do 415 k=1,nt\n      do 415 np1=2,ndo2,2\n      do 415 i=1,imid\n      do 415 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,1,k)=ve(ig,i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  415 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 430 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp2 .gt. ndo2) go to 430\n      do 429 k=1,nt\n      do 428 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 427 i=1,imm1\n      do 427 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,2*mp1-2,k)=ve(ig,i,2*mp1-2,k)+br(ncbc,mp1,np1,k)*vb(i,mn)\n      ve(ig,i,2*mp1-1,k)=ve(ig,i,2*mp1-1,k)+bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wo(ig,i,2*mp1-2,k)=wo(ig,i,2*mp1-2,k)-bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wo(ig,i,2*mp1-1,k)=wo(ig,i,2*mp1-1,k)+br(ncbc,mp1,np1,k)*wb(i,mn)\n  427 continue\n      if(mlat .eq. 0) go to 428\n      do 9428 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,imid,2*mp1-2,k) = ve(ig,imid,2*mp1-2,k)\n     1                     +br(ncbc,mp1,np1,k)*vb(imid,mn) \n      ve(ig,imid,2*mp1-1,k) = ve(ig,imid,2*mp1-1,k)\n     1                     +bi(ncbc,mp1,np1,k)*vb(imid,mn)\n 9428 continue\n  428 continue\n  429 continue\n  430 continue\n      go to 950\nc\nc     case ityp=5   v even,  w odd,     br and bi equal zero \nc\nc     case m = 0\nc\n  500 do 516 k=1,nt\n      do 516 np1=3,ndo1,2\n      do 516 i=1,imm1\n      do 516 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      wo(ig,i,1,k)=wo(ig,i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  516 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 530 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 530\n      do 525 k=1,nt\n      do 524 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 523 i=1,imm1\n      do 523 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,i,2*mp1-2,k)=ve(ig,i,2*mp1-2,k)-ci(ncbc,mp1,np1,k)*wb(i,mn)\n      ve(ig,i,2*mp1-1,k)=ve(ig,i,2*mp1-1,k)+cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wo(ig,i,2*mp1-2,k)=wo(ig,i,2*mp1-2,k)-cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wo(ig,i,2*mp1-1,k)=wo(ig,i,2*mp1-1,k)-ci(ncbc,mp1,np1,k)*vb(i,mn)\n  523 continue\n      if(mlat .eq. 0) go to 524\n      do 9524 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ve(ig,imid,2*mp1-2,k) = ve(ig,imid,2*mp1-2,k)\n     1                     -ci(ncbc,mp1,np1,k)*wb(imid,mn)\n      ve(ig,imid,2*mp1-1,k) = ve(ig,imid,2*mp1-1,k)\n     1                     +cr(ncbc,mp1,np1,k)*wb(imid,mn)\n 9524 continue\n  524 continue\n  525 continue\n  530 continue\n      go to 950\nc\nc     case ityp=6   v odd  ,  w even\nc\nc     case m = 0\nc\n  600 do 615 k=1,nt\n      do 615 np1=2,ndo2,2\n      do 615 i=1,imid\n      do 615 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      we(ig,i,1,k)=we(ig,i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  615 continue\n      do 616 k=1,nt\n      do 616 np1=3,ndo1,2\n      do 616 i=1,imm1\n      do 616 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,1,k)=vo(ig,i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  616 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 630 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 626\n      do 625 k=1,nt\n      do 624 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 623 i=1,imm1\n      do 623 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,2*mp1-2,k)=vo(ig,i,2*mp1-2,k)+br(ncbc,mp1,np1,k)*vb(i,mn)\n      vo(ig,i,2*mp1-1,k)=vo(ig,i,2*mp1-1,k)+bi(ncbc,mp1,np1,k)*vb(i,mn)\n      we(ig,i,2*mp1-2,k)=we(ig,i,2*mp1-2,k)-bi(ncbc,mp1,np1,k)*wb(i,mn)\n      we(ig,i,2*mp1-1,k)=we(ig,i,2*mp1-1,k)+br(ncbc,mp1,np1,k)*wb(i,mn)\n  623 continue\n      if(mlat .eq. 0) go to 624\n      do 9624 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      we(ig,imid,2*mp1-2,k) = we(ig,imid,2*mp1-2,k)\n     1                     -bi(ncbc,mp1,np1,k)*wb(imid,mn) \n      we(ig,imid,2*mp1-1,k) = we(ig,imid,2*mp1-1,k)\n     1                     +br(ncbc,mp1,np1,k)*wb(imid,mn)\n 9624 continue\n  624 continue\n  625 continue\n  626 if(mp2 .gt. ndo2) go to 630\n      do 629 k=1,nt\n      do 628 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 627 i=1,imm1\n      do 627 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,2*mp1-2,k)=vo(ig,i,2*mp1-2,k)-ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vo(ig,i,2*mp1-1,k)=vo(ig,i,2*mp1-1,k)+cr(ncbc,mp1,np1,k)*wb(i,mn)\n      we(ig,i,2*mp1-2,k)=we(ig,i,2*mp1-2,k)-cr(ncbc,mp1,np1,k)*vb(i,mn)\n      we(ig,i,2*mp1-1,k)=we(ig,i,2*mp1-1,k)-ci(ncbc,mp1,np1,k)*vb(i,mn)\n  627 continue\n      if(mlat .eq. 0) go to 628\n      do 9628 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      we(ig,imid,2*mp1-2,k) = we(ig,imid,2*mp1-2,k)\n     1                     -cr(ncbc,mp1,np1,k)*vb(imid,mn)\n      we(ig,imid,2*mp1-1,k) = we(ig,imid,2*mp1-1,k)\n     1                     -ci(ncbc,mp1,np1,k)*vb(imid,mn)\n 9628 continue\n  628 continue\n  629 continue\n  630 continue\n      go to 950\nc\nc     case ityp=7   v odd, w even   cr and ci equal zero\nc\nc     case m = 0\nc\n  700 do 716 k=1,nt\n      do 716 np1=3,ndo1,2\n      do 716 i=1,imm1\n      do 716 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,1,k)=vo(ig,i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  716 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 730 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 730\n      do 725 k=1,nt\n      do 724 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 723 i=1,imm1\n      do 723 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,2*mp1-2,k)=vo(ig,i,2*mp1-2,k)+br(ncbc,mp1,np1,k)*vb(i,mn)\n      vo(ig,i,2*mp1-1,k)=vo(ig,i,2*mp1-1,k)+bi(ncbc,mp1,np1,k)*vb(i,mn)\n      we(ig,i,2*mp1-2,k)=we(ig,i,2*mp1-2,k)-bi(ncbc,mp1,np1,k)*wb(i,mn)\n      we(ig,i,2*mp1-1,k)=we(ig,i,2*mp1-1,k)+br(ncbc,mp1,np1,k)*wb(i,mn)\n  723 continue\n      if(mlat .eq. 0) go to 724\n      do 9724 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      we(ig,imid,2*mp1-2,k) = we(ig,imid,2*mp1-2,k)\n     1                     -bi(ncbc,mp1,np1,k)*wb(imid,mn) \n      we(ig,imid,2*mp1-1,k) = we(ig,imid,2*mp1-1,k)\n     1                     +br(ncbc,mp1,np1,k)*wb(imid,mn)\n 9724 continue\n  724 continue\n  725 continue\n  730 continue\n      go to 950\nc\nc     case ityp=8   v odd,  w even   br and bi equal zero\nc\nc     case m = 0\nc\n  800 do 815 k=1,nt\n      do 815 np1=2,ndo2,2\n      do 815 i=1,imid\n      do 815 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      we(ig,i,1,k)=we(ig,i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  815 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 830 mp1=2,mmax\n      m = mp1-1\nc     mb = m*(nlat-1)-(m*(m-1))/2\n      mb = m*nlat-(m*(m+1))/2\n      mp2 = mp1+1\n      if(mp2 .gt. ndo2) go to 830\n      do 829 k=1,nt\n      do 828 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 827 i=1,imm1\n      do 827 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      vo(ig,i,2*mp1-2,k)=vo(ig,i,2*mp1-2,k)-ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vo(ig,i,2*mp1-1,k)=vo(ig,i,2*mp1-1,k)+cr(ncbc,mp1,np1,k)*wb(i,mn)\n      we(ig,i,2*mp1-2,k)=we(ig,i,2*mp1-2,k)-cr(ncbc,mp1,np1,k)*vb(i,mn)\n      we(ig,i,2*mp1-1,k)=we(ig,i,2*mp1-1,k)-ci(ncbc,mp1,np1,k)*vb(i,mn)\n  827 continue\n      if(mlat .eq. 0) go to 828\n      we(ig,imid,2*mp1-2,k) = we(ig,imid,2*mp1-2,k)\n     1                     -cr(ncbc,mp1,np1,k)*vb(imid,mn)\n      we(ig,imid,2*mp1-1,k) = we(ig,imid,2*mp1-1,k)\n     1                     -ci(ncbc,mp1,np1,k)*vb(imid,mn)\n  828 continue\n  829 continue\n  830 continue\n  950 continue\n      do 14 k=1,nt\nc     do 14 ig=1,ng\n      call hrfftb(ng*idv,nlon,ve(1,1,1,k),ng*idv,wrfft,work)\n      call hrfftb(ng*idv,nlon,we(1,1,1,k),ng*idv,wrfft,work)\n   14 continue\n      if(ityp .gt. 2) go to 12\n      do 60 k=1,nt\n      do 60 j=1,nlon\n      do 60 i=1,imm1\n      do 60 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ncvw=ncvwdiff+ncbc\n      v(ncvw,i,j,k) = .5d0*(ve(ig,i,j,k)+vo(ig,i,j,k))\n      w(ncvw,i,j,k) = .5d0*(we(ig,i,j,k)+wo(ig,i,j,k))\n      v(ncvw,nlp1-i,j,k) = .5d0*(ve(ig,i,j,k)-vo(ig,i,j,k))\n      w(ncvw,nlp1-i,j,k) = .5d0*(we(ig,i,j,k)-wo(ig,i,j,k))\n   60 continue\n      go to 13\n   12 do 11 k=1,nt\n      do 11 j=1,nlon\n      do 11 i=1,imm1\n      do 11 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ncvw=ncvwdiff+ncbc\n      v(ncvw,i,j,k) = .5d0*ve(ig,i,j,k)\n      w(ncvw,i,j,k) = .5d0*we(ig,i,j,k)\n   11 continue\n   13 if(mlat .ne. 0) then\n      do 65 k=1,nt\n      do 65 j=1,nlon\n      do 65 ncbc=ncbcmin,ncbcmax\n      ig=ncbc-ncbcmin+1\n      ncvw=ncvwdiff+ncbc\n      ncvw=ncvwdiff+ncbc\n      v(ncvw,imid,j,k) = .5d0*ve(ig,imid,j,k)\n      w(ncvw,imid,j,k) = .5d0*we(ig,imid,j,k)\n   65 continue\n      end if\n  234 continue\n      return\n      end\n      subroutine vhsgsi(nlat,nlon,wvhsgs,lvhsgs,dwork,ldwork,ierror)\n      implicit double precision (a-h,o-z)\nc\nc     subroutine vhsfsi computes the gaussian points theta, gauss \nc     weights wts, and the components vb and wb of the vector \nc     harmonics. all quantities are computed internally in double \nc     precision but returned in single precision and are therfore \nc     accurate to single precision.\nc\nc     set imid = (nlat+1)/2 and lmn=(nlat*(nlat+1))/2 then\nc     wvhsgs must have 2*(imid*lmn+nlat)+nlon+15 locations\nc\nc     double precision array dwork must have\nc       3*nlat*(nlat+1)+5*nlat+1 = nlat*(3*nlat+8)+1 \nc     locations which is determined by the size of dthet, \nc     dwts, dwork, and dpbar in vhsgs1\nc\n      dimension wvhsgs(*)\n      double precision dwork(*)\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 1) return\n      ierror = 3\n      imid = (nlat+1)/2\n      lmn = (nlat*(nlat+1))/2\n      if(lvhsgs .lt. 2*(imid*lmn)+nlon+15) return\n      ierror = 4\n      if (ldwork .lt. (nlat*3*(nlat+3)+2)/2) return\n      ierror = 0\nc\nc     set saved work space pointers\nc\n      jw1 = 1\n      jw2 = jw1+imid*lmn\n      jw3 = jw2+imid*lmn\nc\nc     set unsaved work space pointers\nc\n      iw1 = 1\n      iw2 = iw1+nlat\n      iw3 = iw2+nlat\n      iw4 = iw3+3*imid*nlat\nc     iw2 = iw1+nlat+nlat\nc     iw3 = iw2+nlat+nlat\nc     iw4 = iw3+6*imid*nlat\n      call vhgsi1(nlat,imid,wvhsgs(jw1),wvhsgs(jw2),\n     +dwork(iw1),dwork(iw2),dwork(iw3),dwork(iw4))\n      call hrffti(nlon,wvhsgs(jw3))\n      return\n      end\n      subroutine vhgsi1(nlat,imid,vb,wb,dthet,dwts,dpbar,work)\n      implicit double precision (a-h,o-z)\n      dimension vb(imid,*),wb(imid,*)\n      double precision abel,bbel,cbel,ssqr2,dcf\n      double precision dthet(*),dwts(*),dpbar(imid,nlat,3),work(*)\nc\nc     compute gauss points and weights\nc     use dpbar (length 3*nnlat*(nnlat+1)) as work space for gaqd\nc\n      lwk = nlat*(nlat+2)\n      call gaqd(nlat,dthet,dwts,dpbar,lwk,ierror)\nc\nc     compute associated legendre functions\nc\nc     compute m=n=0 legendre polynomials for all theta(i)\nc\n      ssqr2 = 1.d0/dsqrt(2.d0)\n      do 90 i=1,imid\n      dpbar(i,1,1) = ssqr2\n      vb(i,1) = 0.d0\n      wb(i,1) = 0.d0\n   90 continue\nc\nc     main loop for remaining vb, and wb\nc\n      do 100 n=1,nlat-1\n      nm = mod(n-2,3)+1\n      nz = mod(n-1,3)+1\n      np = mod(n,3)+1\nc\nc     compute dpbar for m=0\nc\n      call dnlfk(0,n,work)\n      mn = indx(0,n,nlat)\n      do 105 i=1,imid\n      call dnlft(0,n,dthet(i),work,dpbar(i,1,np))\nc      pbar(i,mn) = dpbar(i,1,np)\n  105 continue\nc\nc     compute dpbar for m=1\nc\n      call dnlfk(1,n,work)\n      mn = indx(1,n,nlat)\n      do 106 i=1,imid\n      call dnlft(1,n,dthet(i),work,dpbar(i,2,np))\nc      pbar(i,mn) = dpbar(i,2,np)\n  106 continue\n  104 continue\nc\nc     compute and store dpbar for m=2,n\nc\n      if(n.lt.2) go to 108\n      do 107 m=2,n\n      abel = dsqrt(dfloat((2*n+1)*(m+n-2)*(m+n-3))/\n     1                dfloat((2*n-3)*(m+n-1)*(m+n)))\n      bbel = dsqrt(dfloat((2*n+1)*(n-m-1)*(n-m))/\n     1                dfloat((2*n-3)*(m+n-1)*(m+n)))\n      cbel = dsqrt(dfloat((n-m+1)*(n-m+2))/\n     1                dfloat((m+n-1)*(m+n)))\n      id = indx(m,n,nlat)\n      if (m.ge.n-1) go to 102\n      do 103 i=1,imid\n      dpbar(i,m+1,np) = abel*dpbar(i,m-1,nm)+bbel*dpbar(i,m+1,nm)\n     1                                         -cbel*dpbar(i,m-1,np)\nc      pbar(i,id) = dpbar(i,m+1,np)\n  103 continue\n      go to 107\n  102 do 101 i=1,imid\n      dpbar(i,m+1,np) = abel*dpbar(i,m-1,nm)-cbel*dpbar(i,m-1,np)\nc      pbar(i,id) = dpbar(i,m+1,np)\n  101 continue\n  107 continue\nc\nc     compute the derivative of the functions\nc\n  108 ix = indx(0,n,nlat)\n      iy = indx(n,n,nlat)\n      do 125 i=1,imid\n      vb(i,ix) = -dpbar(i,2,np)\n      vb(i,iy) = dpbar(i,n,np)/dsqrt(dfloat(2*(n+1)))\n125   continue\nc\n      if(n.eq.1) go to 131 \n      dcf = dsqrt(dfloat(4*n*(n+1)))\n      do 130 m=1,n-1\n      ix = indx(m,n,nlat)     \n      abel = dsqrt(dfloat((n+m)*(n-m+1)))/dcf\n      bbel = dsqrt(dfloat((n-m)*(n+m+1)))/dcf\n      do 130 i=1,imid\n      vb(i,ix) = abel*dpbar(i,m,np)-bbel*dpbar(i,m+2,np)\n130   continue\nc\nc     compute the vector harmonic w(theta) = m*pbar/cos(theta)\nc\nc     set wb=0 for m=0\nc\n  131 ix = indx(0,n,nlat)\n      do 220 i=1,imid\n      wb(i,ix) = 0.d0\n220   continue\nc\nc     compute wb for m=1,n\nc\n      dcf = dsqrt(dfloat(n+n+1)/dfloat(4*n*(n+1)*(n+n-1)))\n      do 230 m=1,n\n      ix = indx(m,n,nlat)     \n      abel = dcf*dsqrt(dfloat((n+m)*(n+m-1)))\n      bbel = dcf*dsqrt(dfloat((n-m)*(n-m-1)))\n      if(m.ge.n-1) go to 231\n      do 229 i=1,imid\n      wb(i,ix) = abel*dpbar(i,m,nz) + bbel*dpbar(i,m+2,nz)\n  229 continue\n      go to 230\n  231 do 228 i=1,imid\n      wb(i,ix) = abel*dpbar(i,m,nz)\n  228 continue\n  230 continue\n  100 continue\n      return \n      end\n"
  },
  {
    "path": "external/SPHEREPACK/vrtgs.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc ... file vrtgs.f\nc\nc     this file includes documentation and code for\nc     subroutine divgs          i\nc\nc ... files which must be loaded with vrtgs.f\nc\nc     sphcom.f, hrfft.f, vhgsc.f, shsgs.f, gaqd.f\nc\nc     subroutine vrtgs(nstrv,nstrc,noffsv,noffsc,\nc                      nlat,nlon,isym,nt,vort,ivrt,jvrt,cr,ci,mdc,ndc,\nc    +                 wshsgs,lshsgs,work,lwork,ierror)\nc\nc     given the vector spherical harmonic coefficients cr and ci, precomputed\nc     by subroutine vhags for a vector field (v,w), subroutine vrtgs\nc     computes the vorticity of the vector field in the scalar array\nc     vort.  vort(i,j) is the vorticity at the gaussian colatitude\nc     theta(i) (see nlat as input parameter) and longitude\nc     lambda(j) = (j-1)*2*pi/nlon on the sphere.  i.e.,\nc\nc            vort(i,j) =  [-dv/dlambda + d(sint*w)/dtheta]/sint\nc\nc     where sint = sin(theta(i)).  w is the east longitudinal and v\nc     is the colatitudinal component of the vector field from which\nc     cr,ci were precomputed.  required associated legendre polynomials\nc     are stored rather than recomputed as they are in subroutine vrtgc.\nc\nc\nc     input parameters\nc\nc     nstrv the stride in vort\nc     nstrc the stride in ci,cr\nc\nc     noffsv the offset in vort\nc     noffsc the offset in ci,cr\nc               -- must be between 0 and stride-1\nc\nc     nlat   the number of points in the gaussian colatitude grid on the\nc            full sphere. these lie in the interval (0,pi) and are computed\nc            in radians in theta(1) <...< theta(nlat) by subroutine gaqd.\nc            if nlat is odd the equator will be included as the grid point\nc            theta((nlat+1)/2).  if nlat is even the equator will be\nc            excluded as a grid point and will lie half way between\nc            theta(nlat/2) and theta(nlat/2+1). nlat must be at least 3.\nc            note: on the half sphere, the number of grid points in the\nc            colatitudinal direction is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than 3. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for isym==0.  For this reason an error code will\nc           be returned if isym!=0.\nc\nc\nc     isym   a parameter which determines whether the vorticity is\nc            computed on the full or half sphere as follows:\nc\nc      = 0\nc            the symmetries/antsymmetries described in isym=1,2 below\nc            do not exist in (v,w) about the equator.  in this case the\nc            vorticity is neither symmetric nor antisymmetric about\nc            the equator.  the vorticity is computed on the entire\nc            sphere.  i.e., in the array vort(i,j) for i=1,...,nlat and\nc            j=1,...,nlon.\nc\nc      = 1\nc            w is antisymmetric and v is symmetric about the equator.\nc            in this case the vorticity is symmetyric about the\nc            equator and is computed for the northern hemisphere\nc            only.  i.e., if nlat is odd the vorticity is computed\nc            in the array vort(i,j) for i=1,...,(nlat+1)/2 and for\nc            j=1,...,nlon.  if nlat is even the vorticity is computed\nc            in the array vort(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc      = 2\nc            w is symmetric and v is antisymmetric about the equator\nc            in this case the vorticity is antisymmetric about the\nc            equator and is computed for the northern hemisphere\nc            only.  i.e., if nlat is odd the vorticity is computed\nc            in the array vort(i,j) for i=1,...,(nlat+1)/2 and for\nc            j=1,...,nlon.  if nlat is even the vorticity is computed\nc            in the array vort(i,j) for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc\nc      nt    nt is the number of scalar and vector fields.  some\nc            computational efficiency is obtained for multiple fields.\nc            in the program that calls vrtgs, the arrays cr,ci, and vort\nc            can be three dimensional corresponding to an indexed multiple\nc            vector field.  in this case multiple scalar synthesis will\nc            be performed to compute the vorticity for each field.  the\nc            third index is the synthesis index which assumes the values\nc            k=1,...,nt.  for a single synthesis set nt = 1.  the\nc            description of the remaining parameters is simplified by\nc            assuming that nt=1 or that all the arrays are two dimensional.\nc\nc     ivrt   the first dimension of the array vort as it appears in\nc            the program that calls vrtgs. if isym = 0 then ivrt\nc            must be at least nlat.  if isym = 1 or 2 and nlat is\nc            even then ivrt must be at least nlat/2. if isym = 1 or 2\nc            and nlat is odd then ivrt must be at least (nlat+1)/2.\nc\nc     jvrt   the second dimension of the array vort as it appears in\nc            the program that calls vrtgs. jvrt must be at least nlon.\nc\nc    cr,ci   two or three dimensional arrays (see input parameter nt)\nc            that contain vector spherical harmonic coefficients\nc            of the vector field (v,w) as computed by subroutine vhags.\nc     ***    cr and ci must be computed by vhags prior to calling\nc            vrtgs.\nc\nc      mdc   the first dimension of the arrays cr and ci as it\nc            appears in the program that calls vrtgs. mdc must be at\nc            least min0(nlat,nlon/2) if nlon is even or at least\nc            min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc      ndc   the second dimension of the arrays cr and ci as it\nc            appears in the program that calls vrtgs. ndc must be at\nc            least nlat.\nc\nc   wshsgs   an array which must be initialized by subroutine shsgsi.\nc            once initialized,\nc            wshsgs can be used repeatedly by vrtgs as long as nlon\nc            and nlat remain unchanged.  wshsgs must not be altered\nc            between calls of vrtgs\nc\nc   lshsgs   the dimension of the array wshsgs   as it appears in the\nc            program that calls vrtgs. define\nc\nc               l1 = min0(nlat,(nlon+2)/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lshsgs must be at least\nc\nc            nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\nc\nc     work   a work array that does not have to be saved.\nc\nc    lwork   the dimension of the array work as it appears in the\nc            program that calls vrtgs. define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd.\nc\nc            if isym = 0 then lwork must be at least\nc\nc               nlat*((nt+1)*nlon+2*nt*l1+1)\nc\nc            if isym > 0 then lwork must be at least\nc\nc               (nt+1)*l2*nlon+nlat*(2*nt*l1+1)\nc\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc\nc     vort   a two or three dimensional array (see input parameter nt)\nc            that contains the vorticity of the vector field (v,w)\nc            whose coefficients cr,ci where computed by subroutine vhags.\nc            vort(i,j) is the vorticity at the gaussian colatitude point\nc            theta(i) and longitude point lambda(j) = (j-1)*2*pi/nlon.\nc            the index ranges are defined above at the input parameter\nc            isym.\nc\nc\nc   ierror   an error parameter which indicates fatal errors with input\nc            parameters when returned positive.\nc          = 0  no errors\nc          = 1  error in the specification of nlat\nc          = 2  error in the specification of nlon\nc          = 3  error in the specification of isym\nc          = 4  error in the specification of nt\nc          = 5  error in the specification of ivrt\nc          = 6  error in the specification of jvrt\nc          = 7  error in the specification of mdc\nc          = 8  error in the specification of ndc\nc          = 9  error in the specification of lshsgs\nc          = 10 error in the specification of lwork\nc **********************************************************************\nc                                                                              \nc   \n      subroutine vrtgs(nstrv,nstrc,noffsv,noffsc,\n     +                 nlat,nlon,isym,nt,vort,ivrt,jvrt,cr,ci,mdc,ndc,\n     +                 wshsgs,lshsgs,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension vort(nstrv,ivrt,jvrt,nt),\n     +  cr(nstrc,mdc,ndc,nt),ci(nstrc,mdc,ndc,nt)\n      dimension wshsgs(lshsgs),work(lwork)\nc\nc     check input parameters\nc\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 4) return\n      ierror = 3\nc     if (isym.lt.0 .or. isym.gt.2) return\nc     see note above about symmetries\n      if(isym.ne.0) return\n      ierror = 4\n      if(nt .lt. 0) return\n      ierror = 5\n      imid = (nlat+1)/2\n      if((isym.eq.0 .and. ivrt.lt.nlat) .or.\n     1   (isym.gt.0 .and. ivrt.lt.imid)) return\n      ierror = 6\n      if(jvrt .lt. nlon) return\n      ierror = 7\n      if(mdc .lt. min0(nlat,(nlon+1)/2)) return\n      mmax = min0(nlat,(nlon+2)/2)\n      ierror = 8\n      if(ndc .lt. nlat) return\n      ierror = 9\n      imid = (nlat+1)/2\n      lpimn = (imid*mmax*(nlat+nlat-mmax+1))/2\n      l2 = (nlat+mod(nlat,2))/2\n      l1 = min0((nlon+2)/2,nlat)\n      lp=nlat*(3*(l1+l2)-2)+(l1-1)*(l2*(2*nlat-l1)-3*l1)/2+nlon+15\n      if(lshsgs.lt.lp) return\n      ierror = 10\nc\nc     verify unsaved work space (add to what shses requires, file f3)\nc\nc\nc     set first dimension for a,b (as requried by shses)\nc\n      mab = min0(nlat,nlon/2+1)\n      mn = mab*nlat*nt\n      ls = nlat\n      if(isym .gt. 0) ls = imid\n      nln = nt*ls*nlon\n      if(lwork.lt. nln+ls*nlon+2*mn+nlat) return\n      ierror = 11\n      if((noffsv.lt.0).or.(noffsv.ge.nstrv)) return\n      if((noffsc.lt.0).or.(noffsc.ge.nstrc)) return\n      ierror = 0\nc\nc     set work space pointers\nc\n      ia = 1\n      ib = ia+mn\n      is = ib+mn\n      iwk = is+nlat\n      lwk = lwork-2*mn-nlat\n      call vrtgs1(nstrv,nstrc,noffsv,noffsc,\n     1            nlat,nlon,isym,nt,vort,ivrt,jvrt,cr,ci,mdc,ndc,\n     +work(ia),work(ib),mab,work(is),wshsgs,lshsgs,work(iwk),lwk,\n     +ierror)\n      return\n      end\n\n      subroutine vrtgs1(nstrv,nstrc,noffsv,noffsc,\n     +                  nlat,nlon,isym,nt,vort,ivrt,jvrt,cr,ci,mdc,ndc,\n     +                  a,b,mab,sqnn,wsav,lwsav,wk,lwk,ierror)\n      implicit double precision (a-h,o-z)\n      dimension vort(nstrv,ivrt,jvrt,nt),\n     +         cr(nstrc,mdc,ndc,nt),ci(nstrc,mdc,ndc,nt)\n      dimension a(mab,nlat,nt),b(mab,nlat,nt),sqnn(nlat)\n      dimension wsav(lwsav),wk(lwk)\n      ncc=noffsc+1\nc\nc     set coefficient multiplyers\nc\n      do 1 n=2,nlat\n      fn = dfloat(n-1)\n      sqnn(n) = dsqrt(fn*(fn+1.))\n    1 continue\nc\nc     compute divergence scalar coefficients for each vector field\nc\n      do 2 k=1,nt\n      do 3 n=1,nlat\n      do 4 m=1,mab\n      a(m,n,k) = 0.0\n      b(m,n,k) = 0.0\n    4 continue\n    3 continue\nc\nc     compute m=0 coefficients\nc\n      do 5 n=2,nlat\n      a(1,n,k) = sqnn(n)*cr(ncc,1,n,k)\n      b(1,n,k) = sqnn(n)*ci(ncc,1,n,k)\n    5 continue\nc\nc     compute m>0 coefficients\nc\n      mmax = min0(nlat,(nlon+1)/2)\n      do 6 m=2,mmax\n      do 7 n=m,nlat\n      a(m,n,k) = sqnn(n)*cr(ncc,m,n,k)\n      b(m,n,k) = sqnn(n)*ci(ncc,m,n,k)\n    7 continue\n    6 continue\n    2 continue\nc\nc     synthesize a,b into vort\nc\n      call shsgs(nstrv,1,noffsv,0,nlat,nlon,isym,nt,vort,ivrt,jvrt,a,b,\n     +           mab,nlat,mab,nlat,wsav,lwsav,wk,lwk,ierror)\n      return\n      end\n"
  },
  {
    "path": "external/SPHEREPACK/vtsgs.f",
    "content": "c\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc     *                                                               *\nc     *                  copyright (c) 1998 by UCAR                   *\nc     *                                                               *\nc     *       University Corporation for Atmospheric Research         *\nc     *                                                               *\nc     *                      all rights reserved                      *\nc     *                                                               *\nc     *                      SPHEREPACK version 3.2                   *\nc     *                                                               *\nc     *       A Package of Fortran77 Subroutines and Programs         *\nc     *                                                               *\nc     *              for Modeling Geophysical Processes               *\nc     *                                                               *\nc     *                             by                                *\nc     *                                                               *\nc     *                  John Adams and Paul Swarztrauber             *\nc     *                                                               *\nc     *                             of                                *\nc     *                                                               *\nc     *         the National Center for Atmospheric Research          *\nc     *                                                               *\nc     *                Boulder, Colorado  (80307)  U.S.A.             *\nc     *                                                               *\nc     *                   which is sponsored by                       *\nc     *                                                               *\nc     *              the National Science Foundation                  *\nc     *                                                               *\nc     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\nc\nc\nc\nc ... file vtsgs.f\nc\nc     this file includes documentation and code for\nc     subroutines vtsgs and vtsgsi\nc\nc ... files which must be loaded with vtsgs.f\nc\nc     sphcom.f, hrfft.f, vhags.f, vhsgs.f,gaqd.f\nc   \nc   \nc     subroutine vtsgs(nstrvw,nstrbc,noffsvw,noffsbc,\nc    +                 nlat,nlon,ityp,nt,vt,wt,idvw,jdvw,br,bi,cr,ci,\nc    +                 mdab,ndab,wvts,lwvts,work,lwork,ierror)\nc\nc     given the vector harmonic analysis br,bi,cr, and ci (computed\nc     by subroutine vhags) of some vector function (v,w), this \nc     subroutine computes the vector function (vt,wt) which is\nc     the derivative of (v,w) with respect to colatitude theta. vtsgs\nc     is similar to vhsgs except the vector harmonics are replaced by \nc     their derivative with respect to colatitude with the result that\nc     (vt,wt) is computed instead of (v,w). vt(i,j) is the derivative\nc     of the colatitudinal component v(i,j) at the gaussian colatitude\nc     point theta(i) and longitude phi(j) = (j-1)*2*pi/nlon. the\nc     spectral representation of (vt,wt) is given below at output\nc     parameters vt,wt.\nc\nc     input parameters\nc\nc     nstrvw the stride in wt,vt\nc     nstrbc the stride in bi,br,ci,cr\nc     noffsvw the offset in wt,vt\nc     noffsbc the offset in bi,br,ci,cr\nc               -- must be between 0 and stride-1\nc\nc     nlat   the number of gaussian colatitudinal grid points theta(i)\nc            such that 0 < theta(1) <...< theta(nlat) < pi. they are\nc            computed by subroutine gaqd which is called by this\nc            subroutine. if nlat is odd the equator is\nc            theta((nlat+1)/2). if nlat is even the equator lies\nc            half way between theta(nlat/2) and theta(nlat/2+1). nlat\nc            must be at least 3. note: if (v,w) is symmetric about\nc            the equator (see parameter ityp below) the number of\nc            colatitudinal grid points is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than zero. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     NOTE: this is a modified version of spherepack.  Changes from\nc           the original version to the current one have only been\nc           tested for ityp=0,1,2.  For this reason an error code will\nc           be returned if ityp>2.\nc\nc     ityp   = 0  no symmetries exist about the equator. the synthesis\nc                 is performed on the entire sphere. i.e. the arrays\nc                 vt(i,j),wt(i,j) are computed for i=1,...,nlat and\nc                 j=1,...,nlon.   \nc\nc            = 1  no symmetries exist about the equator however the\nc                 the coefficients cr and ci are zero which implies\nc                 that the curl of (v,w) is zero. that is,\nc                 (d/dtheta (sin(theta) w) - dv/dphi)/sin(theta) = 0. \nc                 the calculations are performed on the entire sphere.\nc                 i.e. the arrays vt(i,j),wt(i,j) are computed for \nc                 i=1,...,nlat and j=1,...,nlon.\nc\nc            = 2  no symmetries exist about the equator however the\nc                 the coefficients br and bi are zero which implies\nc                 that the divergence of (v,w) is zero. that is,\nc                 (d/dtheta (sin(theta) v) + dw/dphi)/sin(theta) = 0. \nc                 the calculations are performed on the entire sphere.\nc                 i.e. the arrays vt(i,j),wt(i,j) are computed for \nc                 i=1,...,nlat and j=1,...,nlon.\nc\nc            = 3  vt is odd and wt is even about the equator. the \nc                 synthesis is performed on the northern hemisphere\nc                 only.  i.e., if nlat is odd the arrays vt(i,j)\nc                 and wt(i,j) are computed for i=1,...,(nlat+1)/2\nc                 and j=1,...,nlon. if nlat is even the arrays \nc                 are computed for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 4  vt is odd and wt is even about the equator and the \nc                 coefficients cr and ci are zero. the synthesis is\nc                 performed on the northern hemisphere only. i.e. if\nc                 nlat is odd the arrays vt(i,j),wt(i,j) are computed\nc                 for i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the arrays vt(i,j),wt(i,j) are computed for \nc                 i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 5  vt is odd and wt is even about the equator and the \nc                 coefficients br and bi are zero. the synthesis is\nc                 performed on the northern hemisphere only. i.e. if\nc                 nlat is odd the arrays vt(i,j),wt(i,j) are computed\nc                 for i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the arrays vt(i,j),wt(i,j) are computed for \nc                 i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 6  vt is even and wt is odd about the equator. the \nc                 synthesis is performed on the northern hemisphere\nc                 only.  i.e., if nlat is odd the arrays vt(i,j),wt(i,j)\nc                 are computed for i=1,...,(nlat+1)/2 and j=1,...,nlon.\nc                 if nlat is even the arrays vt(i,j),wt(i,j) are computed \nc                 for i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 7  vt is even and wt is odd about the equator and the \nc                 coefficients cr and ci are zero. the synthesis is\nc                 performed on the northern hemisphere only. i.e. if\nc                 nlat is odd the arrays vt(i,j),wt(i,j) are computed\nc                 for i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the arrays vt(i,j),wt(i,j) are computed for \nc                 i=1,...,nlat/2 and j=1,...,nlon.\nc\nc            = 8  vt is even and wt is odd about the equator and the \nc                 coefficients br and bi are zero. the synthesis is\nc                 performed on the northern hemisphere only. i.e. if\nc                 nlat is odd the arrays vt(i,j),wt(i,j) are computed\nc                 for i=1,...,(nlat+1)/2 and j=1,...,nlon. if nlat is\nc                 even the arrays vt(i,j),wt(i,j) are computed for \nc                 i=1,...,nlat/2 and j=1,...,nlon.\nc\nc     nt     the number of syntheses.  in the program that calls vtsgs,\nc            the arrays vt,wt,br,bi,cr, and ci can be three dimensional\nc            in which case multiple syntheses will be performed.\nc            the third index is the synthesis index which assumes the \nc            values k=1,...,nt.  for a single synthesis set nt=1. the\nc            discription of the remaining parameters is simplified\nc            by assuming that nt=1 or that all the arrays are two\nc            dimensional.\nc\nc     idvw   the first dimension of the arrays vt,wt as it appears in\nc            the program that calls vtsgs. if ityp .le. 2 then idvw\nc            must be at least nlat.  if ityp .gt. 2 and nlat is\nc            even then idvw must be at least nlat/2. if ityp .gt. 2\nc            and nlat is odd then idvw must be at least (nlat+1)/2.\nc\nc     jdvw   the second dimension of the arrays vt,wt as it appears in\nc            the program that calls vtsgs. jdvw must be at least nlon.\nc\nc     br,bi  two or three dimensional arrays (see input parameter nt)\nc     cr,ci  that contain the vector spherical harmonic coefficients\nc            of (v,w) as computed by subroutine vhags.\nc            \nc     mdab   the first dimension of the arrays br,bi,cr, and ci as it\nc            appears in the program that calls vtsgs. mdab must be at\nc            least min0(nlat,nlon/2) if nlon is even or at least\nc            min0(nlat,(nlon+1)/2) if nlon is odd.\nc\nc     ndab   the second dimension of the arrays br,bi,cr, and ci as it\nc            appears in the program that calls vtsgs. ndab must be at\nc            least nlat.\nc\nc     wvts   an array which must be initialized by subroutine vtsgsi.\nc            once initialized, wvts can be used repeatedly by vtsgs\nc            as long as nlon and nlat remain unchanged.  wvts must\nc            not be altered between calls of vtsgs.\nc\nc     lwvts  the dimension of the array wvts as it appears in the\nc            program that calls vtsgs. define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lwvts must be at least\nc\nc                 l1*l2*(nlat+nlat-l1+1)+nlon+15\nc\nc\nc     work   a work array that does not have to be saved.\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls vtsgs. define\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            if ityp .le. 2 then lwork must be at least\nc\nc                       (2*nt+1)*nlat*nlon\nc\nc            if ityp .gt. 2 then lwork must be at least\nc\nc                        (2*nt+1)*l2*nlon \nc\nc     **************************************************************\nc\nc     output parameters\nc\nc     vt,wt  two or three dimensional arrays (see input parameter nt)\nc            in which the derivative of (v,w) with respect to \nc            colatitude theta is stored. vt(i,j),wt(i,j) contain the\nc            derivatives at gaussian colatitude points theta(i) for\nc            i=1,...,nlat and longitude phi(j) = (j-1)*2*pi/nlon. \nc            the index ranges are defined above at the input parameter\nc            ityp. vt and wt are computed from the formulas for v and \nc            w given in subroutine vhsgs but with vbar and wbar replaced\nc           with their derivatives with respect to colatitude. these\nc            derivatives are denoted by vtbar and wtbar. \nc\nc\nc   *************************************************************\nc\nc   in terms of real variables this expansion takes the form\nc\nc             for i=1,...,nlat and  j=1,...,nlon\nc\nc     vt(i,j) = the sum from n=1 to n=nlat-1 of\nc\nc               .5*br(1,n+1)*vtbar(0,n,theta(i))\nc\nc     plus the sum from m=1 to m=mmax-1 of the sum from n=m to \nc     n=nlat-1 of the real part of\nc\nc       (br(m+1,n+1)*vtbar(m,n,theta(i))\nc                   -ci(m+1,n+1)*wtbar(m,n,theta(i)))*cos(m*phi(j))\nc      -(bi(m+1,n+1)*vtbar(m,n,theta(i))\nc                   +cr(m+1,n+1)*wtbar(m,n,theta(i)))*sin(m*phi(j))\nc\nc    and for i=1,...,nlat and  j=1,...,nlon\nc\nc     wt(i,j) = the sum from n=1 to n=nlat-1 of\nc\nc              -.5*cr(1,n+1)*vtbar(0,n,theta(i))\nc\nc     plus the sum from m=1 to m=mmax-1 of the sum from n=m to\nc     n=nlat-1 of the real part of\nc\nc      -(cr(m+1,n+1)*vtbar(m,n,theta(i))\nc                   +bi(m+1,n+1)*wtbar(m,n,theta(i)))*cos(m*phi(j))\nc      +(ci(m+1,n+1)*vtbar(m,n,theta(i))\nc                   -br(m+1,n+1)*wtbar(m,n,theta(i)))*sin(m*phi(j))\nc\nc\nc      br(m+1,nlat),bi(m+1,nlat),cr(m+1,nlat), and ci(m+1,nlat) are\nc      assumed zero for m even.\nc\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of ityp\nc            = 4  error in the specification of nt\nc            = 5  error in the specification of idvw\nc            = 6  error in the specification of jdvw\nc            = 7  error in the specification of mdab\nc            = 8  error in the specification of ndab\nc            = 9  error in the specification of lwvts\nc            = 10 error in the specification of lwork\nc\nc\nc *******************************************************************\nc\nc     subroutine vtsgsi(nlat,nlon,wvts,lwvts,work,lwork,dwork,ldwork,\nc    +                  ierror)\nc\nc     subroutine vtsgsi initializes the array wvts which can then be\nc     used repeatedly by subroutine vtsgs until nlat or nlon is changed.\nc\nc     input parameters\nc\nc     nlat   the number of gaussian colatitudinal grid points theta(i)\nc            such that 0 < theta(1) <...< theta(nlat) < pi. they are\nc            computed by subroutine gaqd which is called by this\nc            subroutine. if nlat is odd the equator is\nc            theta((nlat+1)/2). if nlat is even the equator lies\nc            half way between theta(nlat/2) and theta(nlat/2+1). nlat\nc            must be at least 3. note: if (v,w) is symmetric about\nc            the equator (see parameter ityp below) the number of\nc            colatitudinal grid points is nlat/2 if nlat is even or\nc            (nlat+1)/2 if nlat is odd.\nc\nc     nlon   the number of distinct londitude points.  nlon determines\nc            the grid increment in longitude as 2*pi/nlon. for example\nc            nlon = 72 for a five degree grid. nlon must be greater\nc            than zero. the axisymmetric case corresponds to nlon=1.\nc            the efficiency of the computation is improved when nlon\nc            is a product of small prime numbers.\nc\nc     lwvts  the dimension of the array wvts as it appears in the\nc            program that calls vtsgs. define\nc\nc               l1 = min0(nlat,nlon/2) if nlon is even or\nc               l1 = min0(nlat,(nlon+1)/2) if nlon is odd\nc\nc            and\nc\nc               l2 = nlat/2        if nlat is even or\nc               l2 = (nlat+1)/2    if nlat is odd\nc\nc            then lwvts must be at least\nc\nc                  l1*l2*(nlat+nlat-l1+1)+nlon+15\nc\nc\nc     work   a work array that does not have to be saved.\nc\nc     lwork  the dimension of the array work as it appears in the\nc            program that calls vtsgs. lwork must be at least\nc\nc            3*(max0(l1-2,0)*(nlat+nlat-l1-1))/2+(5*l2+2)*nlat\nc\nc     dwork  a double precision work array that does not have to be saved\nc\nc     ldwork the length of dwork.  ldwork must be at least\nc            3*nlat+2\nc\nc     **************************************************************\nc\nc     output parameters\nc\nc     wvts   an array which is initialized for use by subroutine vtsgs.\nc            once initialized, wvts can be used repeatedly by vtsgs\nc            as long as nlat or nlon remain unchanged.  wvts must not\nc            be altered between calls of vtsgs.\nc\nc\nc     ierror = 0  no errors\nc            = 1  error in the specification of nlat\nc            = 2  error in the specification of nlon\nc            = 3  error in the specification of lwvts\nc            = 4  error in the specification of lwork\nc            = 5  error in the specification of ldwork\nc\n      subroutine vtsgs(nstrvw,nstrbc,noffsvw,noffsbc,\n     1                 nlat,nlon,ityp,nt,vt,wt,idvw,jdvw,br,bi,cr,ci,\n     1           mdab,ndab,wvts,lwvts,work,lwork,ierror)\n      implicit double precision (a-h,o-z)\nc\n      dimension vt(nstrvw,idvw,jdvw,1),wt(nstrvw,idvw,jdvw,1),\n     1          br(nstrbc,mdab,ndab,1),bi(nstrbc,mdab,ndab,1),\n     1          cr(nstrbc,mdab,ndab,1),ci(nstrbc,mdab,ndab,1),\n     2          work(1),wvts(1)\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 1) return\n      ierror = 3\n      if(ityp.lt.0 .or. ityp.gt.8) return\n      ierror = 4\nc     see note above about symmetries\n      if(ityp.gt.2) return\n      if(nt .lt. 0) return\n      ierror = 5\n      imid = (nlat+1)/2\nc     if((ityp.le.2 .and. idvw.lt.nlat) .or.\nc    1   (ityp.gt.2 .and. idvw.lt.imid)) return\nc     see note above about symmetries\n      if(ityp.ne.0) return\n      ierror = 6\n      if(jdvw .lt. nlon) return\n      ierror = 7\n      mmax = min0(nlat,(nlon+1)/2)\n      if(mdab .lt. mmax) return\n      ierror = 8\n      if(ndab .lt. nlat) return\n      ierror = 9\n      idz = (mmax*(nlat+nlat-mmax+1))/2\n      lzimn = idz*imid\n      if(lwvts .lt. lzimn+lzimn+nlon+15) return\n      ierror = 10\n      idv = nlat\n      if(ityp .gt. 2) idv = imid\n      lnl = nt*idv*nlon\n      if(lwork .lt. lnl+lnl+idv*nlon) return\n      ierror = 11\n      if((noffsvw.lt.0).or.(noffsvw.ge.nstrvw)) return\n      if((noffsbc.lt.0).or.(noffsbc.ge.nstrbc)) return\n      ierror = 0\n      ist = 0\n      if(ityp .le. 2) ist = imid\n      iw1 = ist+1\n      iw2 = lnl+1\n      iw3 = iw2+ist\n      iw4 = iw2+lnl\n      jw1 = lzimn+1\n      jw2 = jw1+lzimn\n      call vtsgs1(nstrvw,nstrbc,noffsvw,noffsbc,\n     1     nlat,nlon,ityp,nt,imid,idvw,jdvw,vt,wt,mdab,ndab,\n     1     br,bi,cr,ci,idv,work,work(iw1),work(iw2),work(iw3),\n     2     work(iw4),idz,wvts,wvts(jw1),wvts(jw2))\n      return\n      end\n      subroutine vtsgs1(nstrvw,nstrbc,noffsvw,noffsbc,\n     1   nlat,nlon,ityp,nt,imid,idvw,jdvw,vt,wt,mdab,\n     1   ndab,br,bi,cr,ci,idv,vte,vto,wte,wto,work,idz,vb,wb,wrfft)\n      implicit double precision (a-h,o-z)\n      dimension vt(nstrvw,idvw,jdvw,1),wt(nstrvw,idvw,jdvw,1),\n     1          br(nstrbc,mdab,ndab,1),bi(nstrbc,mdab,ndab,1),\n     1          cr(nstrbc,mdab,ndab,1),ci(nstrbc,mdab,ndab,1),\n     2          vte(idv,nlon,1),vto(idv,nlon,1),wte(idv,nlon,1),\n     3          wto(idv,nlon,1),work(1),wrfft(1),\n     4          vb(imid,1),wb(imid,1)\n      ncvw = noffsvw+1\n      ncbc = noffsbc+1\n      nlp1 = nlat+1\n      mlat = mod(nlat,2)\n      mlon = mod(nlon,2)\n      mmax = min0(nlat,(nlon+1)/2)\n      imm1 = imid\n      if(mlat .ne. 0) imm1 = imid-1\n      do 10 k=1,nt\n      do 10 j=1,nlon\n      do 10 i=1,idv\n      vte(i,j,k) = 0.d0\n      wte(i,j,k) = 0.d0\n   10 continue\n      ndo1 = nlat\n      ndo2 = nlat\n      if(mlat .ne. 0) ndo1 = nlat-1\n      if(mlat .eq. 0) ndo2 = nlat-1\n   18 itypp = ityp+1\n      go to (1,100,200,300,400,500,600,700,800),itypp\nc\nc     case ityp=0   no symmetries\nc\nc     case m = 0\nc\n    1 do 15 k=1,nt\n      do 15 np1=2,ndo2,2\n      do 15 i=1,imm1\n      vto(i,1,k)=vto(i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n      wto(i,1,k)=wto(i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n   15 continue\n      do 16 k=1,nt\n      do 16 np1=3,ndo1,2\n      do 16 i=1,imid\n      vte(i,1,k)=vte(i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n      wte(i,1,k)=wte(i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n   16 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 30 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 26\n      do 25 k=1,nt\n      do 24 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 23 i=1,imm1\n      vte(i,2*mp1-2,k) = vte(i,2*mp1-2,k)\n     1                 +br(ncbc,mp1,np1,k)*vb(i,mn)\n      vto(i,2*mp1-2,k) = vto(i,2*mp1-2,k)\n     1                 -ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vte(i,2*mp1-1,k) = vte(i,2*mp1-1,k)\n     1                 +bi(ncbc,mp1,np1,k)*vb(i,mn)\n      vto(i,2*mp1-1,k) = vto(i,2*mp1-1,k)\n     1                 +cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wte(i,2*mp1-2,k) = wte(i,2*mp1-2,k)\n     1                 -cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wto(i,2*mp1-2,k) = wto(i,2*mp1-2,k)\n     1                 -bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wte(i,2*mp1-1,k) = wte(i,2*mp1-1,k)\n     1                 -ci(ncbc,mp1,np1,k)*vb(i,mn)\n      wto(i,2*mp1-1,k) = wto(i,2*mp1-1,k)\n     1                 +br(ncbc,mp1,np1,k)*wb(i,mn)\n   23 continue\n      if(mlat .eq. 0) go to 24\n      vte(imid,2*mp1-2,k) = vte(imid,2*mp1-2,k)\n     1                     +br(ncbc,mp1,np1,k)*vb(imid,mn)\n      vte(imid,2*mp1-1,k) = vte(imid,2*mp1-1,k)\n     1                     +bi(ncbc,mp1,np1,k)*vb(imid,mn)\n      wte(imid,2*mp1-2,k) = wte(imid,2*mp1-2,k)\n     1                     -cr(ncbc,mp1,np1,k)*vb(imid,mn) \n      wte(imid,2*mp1-1,k) = wte(imid,2*mp1-1,k)\n     1                     -ci(ncbc,mp1,np1,k)*vb(imid,mn)\n   24 continue\n   25 continue\n   26 if(mp2 .gt. ndo2) go to 30\n      do 29 k=1,nt\n      do 28 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 27 i=1,imm1\n      vto(i,2*mp1-2,k) = vto(i,2*mp1-2,k)\n     1                 +br(ncbc,mp1,np1,k)*vb(i,mn)\n      vte(i,2*mp1-2,k) = vte(i,2*mp1-2,k)\n     1                 -ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vto(i,2*mp1-1,k) = vto(i,2*mp1-1,k)\n     1                 +bi(ncbc,mp1,np1,k)*vb(i,mn)\n      vte(i,2*mp1-1,k) = vte(i,2*mp1-1,k)\n     1                 +cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wto(i,2*mp1-2,k) = wto(i,2*mp1-2,k)\n     1                 -cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wte(i,2*mp1-2,k) = wte(i,2*mp1-2,k)\n     1                 -bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wto(i,2*mp1-1,k) = wto(i,2*mp1-1,k)\n     1                 -ci(ncbc,mp1,np1,k)*vb(i,mn)\n      wte(i,2*mp1-1,k) = wte(i,2*mp1-1,k)\n     1                 +br(ncbc,mp1,np1,k)*wb(i,mn)\n   27 continue\n      if(mlat .eq. 0) go to 28\n      vte(imid,2*mp1-2,k) = vte(imid,2*mp1-2,k)\n     1                     -ci(ncbc,mp1,np1,k)*wb(imid,mn) \n      vte(imid,2*mp1-1,k) = vte(imid,2*mp1-1,k)\n     1                     +cr(ncbc,mp1,np1,k)*wb(imid,mn)\n      wte(imid,2*mp1-2,k) = wte(imid,2*mp1-2,k)\n     1                     -bi(ncbc,mp1,np1,k)*wb(imid,mn)\n      wte(imid,2*mp1-1,k) = wte(imid,2*mp1-1,k)\n     1                     +br(ncbc,mp1,np1,k)*wb(imid,mn)\n   28 continue\n   29 continue\n   30 continue\n      go to 950\nc\nc     case ityp=1   no symmetries,  cr and ci equal zero\nc\nc     case m = 0\nc\n  100 do 115 k=1,nt\n      do 115 np1=2,ndo2,2\n      do 115 i=1,imm1\n      vto(i,1,k)=vto(i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  115 continue\n      do 116 k=1,nt\n      do 116 np1=3,ndo1,2\n      do 116 i=1,imid\n      vte(i,1,k)=vte(i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  116 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 130 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 126\n      do 125 k=1,nt\n      do 124 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 123 i=1,imm1\n      vte(i,2*mp1-2,k) = vte(i,2*mp1-2,k)\n     1                 +br(ncbc,mp1,np1,k)*vb(i,mn)\n      vte(i,2*mp1-1,k) = vte(i,2*mp1-1,k)\n     1                 +bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wto(i,2*mp1-2,k) = wto(i,2*mp1-2,k)\n     1                 -bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wto(i,2*mp1-1,k) = wto(i,2*mp1-1,k)\n     1                 +br(ncbc,mp1,np1,k)*wb(i,mn)\n  123 continue\n      if(mlat .eq. 0) go to 124\n      vte(imid,2*mp1-2,k) = vte(imid,2*mp1-2,k)\n     1                     +br(ncbc,mp1,np1,k)*vb(imid,mn)\n      vte(imid,2*mp1-1,k) = vte(imid,2*mp1-1,k)\n     1                     +bi(ncbc,mp1,np1,k)*vb(imid,mn)\n  124 continue\n  125 continue\n  126 if(mp2 .gt. ndo2) go to 130\n      do 129 k=1,nt\n      do 128 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 127 i=1,imm1\n      vto(i,2*mp1-2,k) = vto(i,2*mp1-2,k)\n     1                 +br(ncbc,mp1,np1,k)*vb(i,mn)\n      vto(i,2*mp1-1,k) = vto(i,2*mp1-1,k)\n     1                 +bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wte(i,2*mp1-2,k) = wte(i,2*mp1-2,k)\n     1                 -bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wte(i,2*mp1-1,k) = wte(i,2*mp1-1,k)\n     1                 +br(ncbc,mp1,np1,k)*wb(i,mn)\n  127 continue\n      if(mlat .eq. 0) go to 128\n      wte(imid,2*mp1-2,k) = wte(imid,2*mp1-2,k)\n     1                     -bi(ncbc,mp1,np1,k)*wb(imid,mn)\n      wte(imid,2*mp1-1,k) = wte(imid,2*mp1-1,k)\n     1                     +br(ncbc,mp1,np1,k)*wb(imid,mn)\n  128 continue\n  129 continue\n  130 continue\n      go to 950\nc\nc     case ityp=2   no symmetries,  br and bi are equal to zero\nc\nc     case m = 0\nc\n  200 do 215 k=1,nt\n      do 215 np1=2,ndo2,2\n      do 215 i=1,imm1\n      wto(i,1,k)=wto(i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  215 continue\n      do 216 k=1,nt\n      do 216 np1=3,ndo1,2\n      do 216 i=1,imid\n      wte(i,1,k)=wte(i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  216 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 230 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 226\n      do 225 k=1,nt\n      do 224 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 223 i=1,imm1\n      vto(i,2*mp1-2,k) = vto(i,2*mp1-2,k)\n     1                 -ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vto(i,2*mp1-1,k) = vto(i,2*mp1-1,k)\n     1                 +cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wte(i,2*mp1-2,k) = wte(i,2*mp1-2,k)\n     1                 -cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wte(i,2*mp1-1,k) = wte(i,2*mp1-1,k)\n     1                 -ci(ncbc,mp1,np1,k)*vb(i,mn)\n  223 continue\n      if(mlat .eq. 0) go to 224\n      wte(imid,2*mp1-2,k) = wte(imid,2*mp1-2,k)\n     1                     -cr(ncbc,mp1,np1,k)*vb(imid,mn) \n      wte(imid,2*mp1-1,k) = wte(imid,2*mp1-1,k)\n     1                     -ci(ncbc,mp1,np1,k)*vb(imid,mn)\n  224 continue\n  225 continue\n  226 if(mp2 .gt. ndo2) go to 230\n      do 229 k=1,nt\n      do 228 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 227 i=1,imm1\n      vte(i,2*mp1-2,k) = vte(i,2*mp1-2,k)\n     1                 -ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vte(i,2*mp1-1,k) = vte(i,2*mp1-1,k)\n     1                 +cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wto(i,2*mp1-2,k) = wto(i,2*mp1-2,k)\n     1                 -cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wto(i,2*mp1-1,k) = wto(i,2*mp1-1,k)\n     1                 -ci(ncbc,mp1,np1,k)*vb(i,mn)\n  227 continue\n      if(mlat .eq. 0) go to 228\n      vte(imid,2*mp1-2,k) = vte(imid,2*mp1-2,k)\n     1                     -ci(ncbc,mp1,np1,k)*wb(imid,mn) \n      vte(imid,2*mp1-1,k) = vte(imid,2*mp1-1,k)\n     1                     +cr(ncbc,mp1,np1,k)*wb(imid,mn)\n  228 continue\n  229 continue\n  230 continue\n      go to 950\nc\nc     case ityp=3   v odd,  w even \nc\nc     case m = 0\nc\n  300 do 315 k=1,nt\n      do 315 np1=2,ndo2,2\n      do 315 i=1,imm1\n      vto(i,1,k)=vto(i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  315 continue\n      do 316 k=1,nt\n      do 316 np1=3,ndo1,2\n      do 316 i=1,imid\n      wte(i,1,k)=wte(i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  316 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 330 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 326\n      do 325 k=1,nt\n      do 324 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 323 i=1,imm1\n      vto(i,2*mp1-2,k) = vto(i,2*mp1-2,k)\n     1                 -ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vto(i,2*mp1-1,k) = vto(i,2*mp1-1,k)\n     1                 +cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wte(i,2*mp1-2,k) = wte(i,2*mp1-2,k)\n     1                 -cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wte(i,2*mp1-1,k) = wte(i,2*mp1-1,k)\n     1                 -ci(ncbc,mp1,np1,k)*vb(i,mn)\n  323 continue\n      if(mlat .eq. 0) go to 324\n      wte(imid,2*mp1-2,k) = wte(imid,2*mp1-2,k)\n     1                     -cr(ncbc,mp1,np1,k)*vb(imid,mn) \n      wte(imid,2*mp1-1,k) = wte(imid,2*mp1-1,k)\n     1                     -ci(ncbc,mp1,np1,k)*vb(imid,mn)\n  324 continue\n  325 continue\n  326 if(mp2 .gt. ndo2) go to 330\n      do 329 k=1,nt\n      do 328 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 327 i=1,imm1\n      vto(i,2*mp1-2,k) = vto(i,2*mp1-2,k)\n     1                 +br(ncbc,mp1,np1,k)*vb(i,mn)\n      vto(i,2*mp1-1,k) = vto(i,2*mp1-1,k)\n     1                 +bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wte(i,2*mp1-2,k) = wte(i,2*mp1-2,k)\n     1                 -bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wte(i,2*mp1-1,k) = wte(i,2*mp1-1,k)\n     1                 +br(ncbc,mp1,np1,k)*wb(i,mn)\n  327 continue\n      if(mlat .eq. 0) go to 328\n      wte(imid,2*mp1-2,k) = wte(imid,2*mp1-2,k)\n     1                     -bi(ncbc,mp1,np1,k)*wb(imid,mn)\n      wte(imid,2*mp1-1,k) = wte(imid,2*mp1-1,k)\n     1                     +br(ncbc,mp1,np1,k)*wb(imid,mn)\n  328 continue\n  329 continue\n  330 continue\n      go to 950\nc\nc     case ityp=4   v odd,  w even, and both cr and ci equal zero \nc\nc     case m = 0\nc\n  400 do 415 k=1,nt\n      do 415 np1=2,ndo2,2\n      do 415 i=1,imm1\n      vto(i,1,k)=vto(i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  415 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 430 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp2 .gt. ndo2) go to 430\n      do 429 k=1,nt\n      do 428 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 427 i=1,imm1\n      vto(i,2*mp1-2,k) = vto(i,2*mp1-2,k)\n     1                 +br(ncbc,mp1,np1,k)*vb(i,mn)\n      vto(i,2*mp1-1,k) = vto(i,2*mp1-1,k)\n     1                 +bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wte(i,2*mp1-2,k) = wte(i,2*mp1-2,k)\n     1                 -bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wte(i,2*mp1-1,k) = wte(i,2*mp1-1,k)\n     1                 +br(ncbc,mp1,np1,k)*wb(i,mn)\n  427 continue\n      if(mlat .eq. 0) go to 428\n      wte(imid,2*mp1-2,k) = wte(imid,2*mp1-2,k)\n     1                     -bi(ncbc,mp1,np1,k)*wb(imid,mn)\n      wte(imid,2*mp1-1,k) = wte(imid,2*mp1-1,k)\n     1                     +br(ncbc,mp1,np1,k)*wb(imid,mn)\n  428 continue\n  429 continue\n  430 continue\n      go to 950\nc\nc     case ityp=5   v odd,  w even,     br and bi equal zero \nc\nc     case m = 0\nc\n  500 do 516 k=1,nt\n      do 516 np1=3,ndo1,2\n      do 516 i=1,imid\n      wte(i,1,k)=wte(i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  516 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 530 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 530\n      do 525 k=1,nt\n      do 524 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 523 i=1,imm1\n      vto(i,2*mp1-2,k) = vto(i,2*mp1-2,k)\n     1                 -ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vto(i,2*mp1-1,k) = vto(i,2*mp1-1,k)\n     1                 +cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wte(i,2*mp1-2,k) = wte(i,2*mp1-2,k)\n     1                 -cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wte(i,2*mp1-1,k) = wte(i,2*mp1-1,k)\n     1                 -ci(ncbc,mp1,np1,k)*vb(i,mn)\n  523 continue\n      if(mlat .eq. 0) go to 524\n      wte(imid,2*mp1-2,k) = wte(imid,2*mp1-2,k)\n     1                     -cr(ncbc,mp1,np1,k)*vb(imid,mn) \n      wte(imid,2*mp1-1,k) = wte(imid,2*mp1-1,k)\n     1                     -ci(ncbc,mp1,np1,k)*vb(imid,mn)\n  524 continue\n  525 continue\n  530 continue\n      go to 950\nc\nc     case ityp=6   v even  ,  w odd\nc\nc     case m = 0\nc\n  600 do 615 k=1,nt\n      do 615 np1=2,ndo2,2\n      do 615 i=1,imm1\n      wto(i,1,k)=wto(i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  615 continue\n      do 616 k=1,nt\n      do 616 np1=3,ndo1,2\n      do 616 i=1,imid\n      vte(i,1,k)=vte(i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  616 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 630 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 626\n      do 625 k=1,nt\n      do 624 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 623 i=1,imm1\n      vte(i,2*mp1-2,k) = vte(i,2*mp1-2,k)\n     1                 +br(ncbc,mp1,np1,k)*vb(i,mn)\n      vte(i,2*mp1-1,k) = vte(i,2*mp1-1,k)\n     1                 +bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wto(i,2*mp1-2,k) = wto(i,2*mp1-2,k)\n     1                 -bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wto(i,2*mp1-1,k) = wto(i,2*mp1-1,k)\n     1                 +br(ncbc,mp1,np1,k)*wb(i,mn)\n  623 continue\n      if(mlat .eq. 0) go to 624\n      vte(imid,2*mp1-2,k) = vte(imid,2*mp1-2,k)\n     1                     +br(ncbc,mp1,np1,k)*vb(imid,mn)\n      vte(imid,2*mp1-1,k) = vte(imid,2*mp1-1,k)\n     1                     +bi(ncbc,mp1,np1,k)*vb(imid,mn)\n  624 continue\n  625 continue\n  626 if(mp2 .gt. ndo2) go to 630\n      do 629 k=1,nt\n      do 628 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 627 i=1,imm1\n      vte(i,2*mp1-2,k) = vte(i,2*mp1-2,k)\n     1                 -ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vte(i,2*mp1-1,k) = vte(i,2*mp1-1,k)\n     1                 +cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wto(i,2*mp1-2,k) = wto(i,2*mp1-2,k)\n     1                 -cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wto(i,2*mp1-1,k) = wto(i,2*mp1-1,k)\n     1                 -ci(ncbc,mp1,np1,k)*vb(i,mn)\n  627 continue\n      if(mlat .eq. 0) go to 628\n      vte(imid,2*mp1-2,k) = vte(imid,2*mp1-2,k)\n     1                     -ci(ncbc,mp1,np1,k)*wb(imid,mn) \n      vte(imid,2*mp1-1,k) = vte(imid,2*mp1-1,k)\n     1                     +cr(ncbc,mp1,np1,k)*wb(imid,mn)\n  628 continue\n  629 continue\n  630 continue\n      go to 950\nc\nc     case ityp=7   v even, w odd   cr and ci equal zero\nc\nc     case m = 0\nc\n  700 do 716 k=1,nt\n      do 716 np1=3,ndo1,2\n      do 716 i=1,imid\n      vte(i,1,k)=vte(i,1,k)+br(ncbc,1,np1,k)*vb(i,np1)\n  716 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 730 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp1 .gt. ndo1) go to 730\n      do 725 k=1,nt\n      do 724 np1=mp1,ndo1,2\n      mn = mb+np1\n      do 723 i=1,imm1\n      vte(i,2*mp1-2,k) = vte(i,2*mp1-2,k)\n     1                 +br(ncbc,mp1,np1,k)*vb(i,mn)\n      vte(i,2*mp1-1,k) = vte(i,2*mp1-1,k)\n     1                 +bi(ncbc,mp1,np1,k)*vb(i,mn)\n      wto(i,2*mp1-2,k) = wto(i,2*mp1-2,k)\n     1                 -bi(ncbc,mp1,np1,k)*wb(i,mn)\n      wto(i,2*mp1-1,k) = wto(i,2*mp1-1,k)\n     1                 +br(ncbc,mp1,np1,k)*wb(i,mn)\n  723 continue\n      if(mlat .eq. 0) go to 724\n      vte(imid,2*mp1-2,k) = vte(imid,2*mp1-2,k)\n     1                     +br(ncbc,mp1,np1,k)*vb(imid,mn)\n      vte(imid,2*mp1-1,k) = vte(imid,2*mp1-1,k)\n     1                     +bi(ncbc,mp1,np1,k)*vb(imid,mn)\n  724 continue\n  725 continue\n  730 continue\n      go to 950\nc\nc     case ityp=8   v even,  w odd,   br and bi equal zero\nc\nc     case m = 0\nc\n  800 do 815 k=1,nt\n      do 815 np1=2,ndo2,2\n      do 815 i=1,imm1\n      wto(i,1,k)=wto(i,1,k)-cr(ncbc,1,np1,k)*vb(i,np1)\n  815 continue\nc\nc     case m = 1 through nlat-1\nc\n      if(mmax .lt. 2) go to 950\n      do 830 mp1=2,mmax\n      m = mp1-1\n      mb = m*(nlat-1)-(m*(m-1))/2\n      mp2 = mp1+1\n      if(mp2 .gt. ndo2) go to 830\n      do 829 k=1,nt\n      do 828 np1=mp2,ndo2,2\n      mn = mb+np1\n      do 827 i=1,imm1\n      vte(i,2*mp1-2,k) = vte(i,2*mp1-2,k)\n     1                 -ci(ncbc,mp1,np1,k)*wb(i,mn)\n      vte(i,2*mp1-1,k) = vte(i,2*mp1-1,k)\n     1                 +cr(ncbc,mp1,np1,k)*wb(i,mn)\n      wto(i,2*mp1-2,k) = wto(i,2*mp1-2,k)\n     1                 -cr(ncbc,mp1,np1,k)*vb(i,mn)\n      wto(i,2*mp1-1,k) = wto(i,2*mp1-1,k)\n     1                 -ci(ncbc,mp1,np1,k)*vb(i,mn)\n  827 continue\n      if(mlat .eq. 0) go to 828\n      vte(imid,2*mp1-2,k) = vte(imid,2*mp1-2,k)\n     1                     -ci(ncbc,mp1,np1,k)*wb(imid,mn) \n      vte(imid,2*mp1-1,k) = vte(imid,2*mp1-1,k)\n     1                     +cr(ncbc,mp1,np1,k)*wb(imid,mn)\n  828 continue\n  829 continue\n  830 continue\n  950 do 14 k=1,nt\n      call hrfftb(idv,nlon,vte(1,1,k),idv,wrfft,work)\n      call hrfftb(idv,nlon,wte(1,1,k),idv,wrfft,work)\n   14 continue\n      if(ityp .gt. 2) go to 12\n      do 60 k=1,nt\n      do 60 j=1,nlon\n      do 60 i=1,imm1\n      vt(ncvw,i,j,k) = .5d0*(vte(i,j,k)+vto(i,j,k))\n      wt(ncvw,i,j,k) = .5d0*(wte(i,j,k)+wto(i,j,k))\n      vt(ncvw,nlp1-i,j,k) = .5d0*(vte(i,j,k)-vto(i,j,k))\n      wt(ncvw,nlp1-i,j,k) = .5d0*(wte(i,j,k)-wto(i,j,k))\n   60 continue\n      go to 13\n   12 do 11 k=1,nt\n      do 11 j=1,nlon\n      do 11 i=1,imm1\n      vt(ncvw,i,j,k) = .5d0*vte(i,j,k)\n      wt(ncvw,i,j,k) = .5d0*wte(i,j,k)\n   11 continue\n   13 if(mlat .eq. 0) return\n      do 65 k=1,nt\n      do 65 j=1,nlon\n      vt(ncvw,imid,j,k) = .5d0*vte(imid,j,k)\n      wt(ncvw,imid,j,k) = .5d0*wte(imid,j,k)\n   65 continue\n      return\n      end\n      subroutine vtsgsi(nlat,nlon,wvts,lwvts,work,lwork,dwork,ldwork,\n     +                  ierror)\n      implicit double precision (a-h,o-z)\nc\nc     define imid = (nlat+1)/2 and mmax = min0(nlat,(nlon+1)/2)\nc     the length of wvts is imid*mmax*(nlat+nlat-mmax+1)+nlon+15\nc     and the length of work is labc+5*nlat*imid+2*nlat where\nc     labc = 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\nc\n      dimension wvts(lwvts),work(lwork)\n      double precision dwork(ldwork)\n      ierror = 1\n      if(nlat .lt. 3) return\n      ierror = 2\n      if(nlon .lt. 1) return\n      ierror = 3\nc Fixed SpherePack error: was [ mmax = min0(nlat,nlon/2+1) ]\n      mmax = min0(nlat,(nlon+1)/2)\nc\n      imid = (nlat+1)/2\n      lzimn = (imid*mmax*(nlat+nlat-mmax+1))/2\n      if(lwvts .lt. lzimn+lzimn+nlon+15) return\n      ierror = 4\n      labc = 3*(max0(mmax-2,0)*(nlat+nlat-mmax-1))/2\n      lvin = 3*nlat*imid\n      lwvbin = 2*nlat*imid+labc\n      ltheta = nlat+nlat\n      if(lwork .lt. lvin+lwvbin+ltheta) return\n      ierror = 5\n      if (ldwork .lt. 3*nlat+2) return\n      ierror = 0\n      iw1 = lvin+1\n      iw2 = iw1+lwvbin\n      jw1 = nlat+1\n      jw2 = jw1+nlat\n      CALL VETG1(NLAT,NLON,IMID,WVTS,WVTS(LZIMN+1),WORK,work(IW1),\n     1              DWORK,DWORK(JW1),DWORK(JW2),IERROR)\n      if(ierror .ne. 0) return\n      call hrffti(nlon,wvts(2*lzimn+1))\n      return\n      end\n      subroutine vetg1(nlat,nlon,imid,vb,wb,vin,wvbin,\n     1                       theta,wts,dwork,ierror)\n      implicit double precision (a-h,o-z)\n      dimension vb(imid,*),wb(imid,*),vin(imid,nlat,3),wvbin(*)\n      double precision dwork(*),theta(*),wts(*)\nc Fixed SpherePack error: was [ mmax = min0(nlat,nlon/2+1) ]\n      mmax = min0(nlat,(nlon+1)/2)\nc\n      ldwork = 1\n      call gaqd(nlat,theta,wts,dwork,ldwork,ierr)\n      if(ierr .eq. 0) go to 10\n      ierror = 10+ierr\n      return      \n   10 call vtgint (nlat,nlon,theta,wvbin,dwork)\n      i1=1\n      i2=2\n      do 33 mp1=1,mmax\n      m = mp1-1\n      call vbin (0,nlat,nlon,m,vin,i3,wvbin,i1,i2)\n      do 33 np1=mp1,nlat\n      mn = m*(nlat-1)-(m*(m-1))/2+np1\n      do 33 i=1,imid\n      vb(i,mn) = vin(i,np1,i3)\n   33 continue\n      call wtgint (nlat,nlon,theta,wvbin,dwork)\n      i1=1\n      i2=2\n      do 34 mp1=1,mmax\n      m = mp1-1\n      call wbin (0,nlat,nlon,m,vin,i3,wvbin,i1,i2)\n      do 34 np1=mp1,nlat\n      mn = m*(nlat-1)-(m*(m-1))/2+np1\n      do 34 i=1,imid\n      wb(i,mn) = vin(i,np1,i3)\n   34 continue\n      return\n      end\n\n\n\n\n\n\n\n"
  },
  {
    "path": "external/brigand/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(BRIGAND_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/external/brigand/include)\n\nmessage(STATUS \"Brigand include: ${BRIGAND_INCLUDE_DIR}\")\n\nadd_library(Brigand INTERFACE IMPORTED GLOBAL)\ntarget_include_directories(Brigand SYSTEM INTERFACE ${BRIGAND_INCLUDE_DIR})\n\nadd_interface_lib_headers(\n  TARGET Brigand\n  HEADERS\n  brigand/brigand.hpp\n)\n\nset_property(\n  GLOBAL APPEND PROPERTY SPECTRE_THIRD_PARTY_LIBS\n  Brigand\n)\n"
  },
  {
    "path": "external/brigand/LICENSE",
    "content": "Boost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "external/brigand/README.md",
    "content": "Brigand Meta-programming library\n================================\n\n![A brigand](https://raw.githubusercontent.com/wiki/edouarda/brigand/brigand_small.jpg)\n\n[![Build Status](https://travis-ci.org/edouarda/brigand.svg?branch=master)](https://travis-ci.org/edouarda/brigand)\n\n[![Build Status](https://ci.appveyor.com/api/projects/status/github/edouarda/brigand)](https://ci.appveyor.com/project/edouarda/brigand)\n\n# Introduction\n\nBrigand is a light-weight, fully functional, instant-compile time C++ 11 meta-programming library.\n\nEverything you were doing with Boost.MPL can be done with Brigand. And if that's not the case, open an issue!\n\n# Learning\n\nShould you wish to learn more, feel free to [watch our Meeting C++ 2015 lightning talk](https://www.youtube.com/watch?v=B8XSDhWx7hY)!\n\nWant more? Our [CppCon 2016 presentation](https://www.youtube.com/watch?v=ky0JdPh_LgE) explains in details the library concepts and goes through two concrete usages.\n\nIf you are looking to learn the concepts of metaprogramming, [have a look at our free e-book](http://www.oreilly.com/programming/free/practical-c-plus-plus-metaprogramming.csp).\n\n# Tutorials\n\n * [Introduction](https://github.com/edouarda/brigand/wiki/Introduction)\n * [Algorithms](https://github.com/edouarda/brigand/wiki/Algorithms)\n * [Runtime](https://github.com/edouarda/brigand/wiki/Runtime)\n\n# Documentation\n\nThe documentation is available [here](https://github.com/edouarda/brigand/wiki).\n\n# Contributors\n\nWe'd like to thank the following contributors, in alphabetical order\n\n * Odin Holmes\n * Marek Kurdej\n * Jonathan Poelen\n\n\n# Note:\nThe copy in this directory is based on upstream hash\n9a86130c87479a38659edd7c0dda776a27f7077d.\n"
  },
  {
    "path": "external/brigand/include/brigand/brigand.hpp",
    "content": "/*!\n@file\n@copyright Edouard Alligand and Joel Falcou 2015-2017\n(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)\n\nEdits by SXS:\n- count_if, filter,  and remove_if have an explicit empty base-case for nvcc\n  support.\n*/\n#ifndef BRIGAND_HPP_INCLUDED\n#define BRIGAND_HPP_INCLUDED\nnamespace brigand\n{\ntemplate <typename T, T Value>\nstruct integral_constant\n{\n    static const T value = Value;\n    using value_type = T;\n    using type = integral_constant<T, Value>;\n    operator value_type() const { return (value); }\n    value_type operator()() const { return (value); }\n};\ntemplate <typename T, T Value>\nconst T integral_constant<T, Value>::value;\nusing true_type = ::brigand::integral_constant<bool, true>;\nusing false_type = ::brigand::integral_constant<bool, false>;\n}\nnamespace brigand\n{\ntemplate <class... T>\nstruct list\n{\n};\ntemplate <typename T, T... Values>\nusing integral_list = brigand::list<brigand::integral_constant<T, Values>...>;\nusing empty_sequence = brigand::list<>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class, class>\n    struct dup_append_list;\n    template <template <class...> class List, class... Ts, class... Us>\n    struct dup_append_list<List<Ts...>, List<Us...>>\n    {\n        using type = List<Ts..., Ts..., Us...>;\n    };\n    template <class T, template <class...> class List, unsigned int N>\n    struct filled_list_impl\n        : dup_append_list<typename filled_list_impl<T, List, N / 2>::type,\n                          typename filled_list_impl<T, List, N - N / 2 * 2>::type>\n    {\n    };\n    template <class T, template <class...> class List>\n    struct filled_list_impl<T, List, 1>\n    {\n        using type = List<T>;\n    };\n    template <class T, template <class...> class List>\n    struct filled_list_impl<T, List, 0>\n    {\n        using type = List<>;\n    };\n}\ntemplate <class T, unsigned int N, template <class...> class List = list>\nusing filled_list = typename detail::filled_list_impl<T, List, N>::type;\n}\nnamespace brigand\n{\n  namespace lazy\n  {\n    template <class A, template<class...> class B> struct wrap;\n    template<template<class...> class A, class... T, template<class...> class B>\n    struct wrap<A<T...>, B>\n    {\n      using type = B<T...>;\n    };\n  }\n  template<class A, template<class...> class B>\n  using wrap = typename lazy::wrap<A, B>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <typename... Ts>\n    struct append_impl;\n    template <>\n    struct append_impl<>\n    {\n        using type = brigand::empty_sequence;\n    };\n    template <template <typename...> class L, typename... T>\n    struct append_impl<L<T...>>\n    {\n        using type = L<T...>;\n    };\n    template <template <typename...> class L1, template <typename...> class L2, typename... T1s,\n              typename... T2s, typename... Ts>\n    struct append_impl<L1<T1s...>, L2<T2s...>, Ts...> : append_impl<L1<T1s..., T2s...>, Ts...>\n    {\n    };\n    template <template <typename...> class L, template <typename...> class L1,\n              template <typename...> class L2, template <typename...> class L3,\n              template <typename...> class L4, template <typename...> class L5,\n              template <typename...> class L6, template <typename...> class L7,\n              template <typename...> class L8, template <typename...> class L9,\n              template <typename...> class L10, template <typename...> class L11,\n              template <typename...> class L12, template <typename...> class L13,\n              template <typename...> class L14, template <typename...> class L15,\n              template <typename...> class L16, typename... Ts, typename... T1s, typename... T2s,\n              typename... T3s, typename... T4s, typename... T5s, typename... T6s, typename... T7s,\n              typename... T8s, typename... T9s, typename... T10s, typename... T11s,\n              typename... T12s, typename... T13s, typename... T14s, typename... T15s,\n              typename... T16s, typename... Us>\n    struct append_impl<L<Ts...>, L1<T1s...>, L2<T2s...>, L3<T3s...>, L4<T4s...>, L5<T5s...>,\n                       L6<T6s...>, L7<T7s...>, L8<T8s...>, L9<T9s...>, L10<T10s...>, L11<T11s...>,\n                       L12<T12s...>, L13<T13s...>, L14<T14s...>, L15<T15s...>, L16<T16s...>, Us...>\n        : append_impl<L<Ts..., T1s..., T2s..., T3s..., T4s..., T5s..., T6s..., T7s..., T8s...,\n                        T9s..., T10s..., T11s..., T12s..., T13s..., T14s..., T15s..., T16s...>,\n                      Us...>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename... Ts>\n    using append = detail::append_impl<Ts...>;\n}\ntemplate <typename... Ts>\nusing append = typename detail::append_impl<Ts...>::type;\nnamespace lazy\n{\n    template <typename T>\n    struct join;\n    template <template <typename...> class L, typename... Ts>\n    struct join<L<Ts...>> : ::brigand::detail::append_impl<Ts...>\n    {\n    };\n}\ntemplate <typename T>\nusing join = wrap<T, append>;\n}\nnamespace brigand\n{\n    template <typename First, typename Second>\n    struct pair\n    {\n        using first_type = First;\n        using second_type = Second;\n    };\n}\nnamespace brigand\n{\n    struct no_such_type_ {};\n}\nnamespace brigand\n{\n  template<typename T> struct type_ { using type = T; };\n  template<typename T> using type_from = typename T::type;\n}\n#include <type_traits>\nnamespace brigand\n{\nnamespace lazy\n{\n    template <typename M, typename K>\n    struct lookup_at\n    {\n        using target_t = type_<K>;\n        using type = decltype(M::at(target_t{}));\n    };\n    template <typename M, typename K>\n    struct lookup : lookup_at<M, K>::type\n    {\n    };\n}\ntemplate <typename M, typename K>\nusing lookup = typename lazy::lookup<M, K>::type;\nnamespace detail\n{\n    template <class... T>\n    struct map_impl;\n    template <>\n    struct map_impl<>\n    {\n        template <typename U>\n        static type_<no_such_type_> at(U);\n        template <class K>\n        static brigand::false_type has_key(type_<K>);\n        template <class K>\n        static map_impl erase(type_<K>);\n        template <class P>\n        static map_impl<P> insert(type_<P>);\n    };\n    template <class... Ts>\n    struct map_impl\n    {\n    private:\n        struct Pack : pair<typename Ts::first_type, Ts>...\n        {\n        };\n        template <class K, class P>\n        static type_<typename P::second_type> at_impl(pair<K, P> *);\n    public:\n        template <class K>\n        static decltype(at_impl<K>(static_cast<Pack *>(nullptr))) at(type_<K>);\n        template <class K>\n        static type_<no_such_type_> at(K);\n        template <class K, class = decltype(at_impl<K>(static_cast<Pack *>(nullptr)))>\n        static brigand::true_type has_key(type_<K>);\n        template <class K>\n        static brigand::false_type has_key(K);\n        template <class K, class X>\n        using erase_t = typename std::conditional<std::is_same<K, typename X::first_type>::value,\n                                                  list<>, list<X>>::type;\n        template <class K, typename... Xs>\n        struct erase_return_t\n        {\n            using type = append<map_impl<>, erase_t<K, Xs>...>;\n        };\n        template <class K>\n        static typename erase_return_t<K, Ts...>::type erase(type_<K>);\n        template <class P, class = decltype(at_impl<typename P::first_type>(static_cast<Pack *>(nullptr)))>\n        static map_impl insert(type_<P>);\n        template <class P>\n        static map_impl<Ts..., typename P::type> insert(P);\n    };\n    template <class... Ts>\n    struct make_map : type_<typename Ts::first_type>...\n    {\n        using type = map_impl<Ts...>;\n    };\n}\ntemplate <class... Ts>\nusing map = typename detail::make_map<Ts...>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class T>\n    struct element_at;\n    template <class... Ts>\n    struct element_at<list<Ts...>>\n    {\n        template <class T>\n        type_<T> static at(Ts..., type_<T> *, ...);\n    };\n    template <unsigned int N, typename Seq>\n    struct at_impl;\n    template <unsigned int N, template <typename...> class L, class... Ts>\n    struct at_impl<N, L<Ts...>> : decltype(element_at<brigand::filled_list<void const *, N>>::at(\n                                      static_cast<type_<Ts> *>(nullptr)...))\n    {\n    };\n}\ntemplate <class L, unsigned int Index>\nusing at_c = typename detail::at_impl<Index, L>::type;\nnamespace detail\n{\n    template <typename T>\n    struct has_at_method\n    {\n        struct dummy\n        {\n        };\n        template <typename C, typename P>\n        static auto test(P * p) -> decltype(C::at(*p), brigand::true_type());\n        template <typename, typename>\n        static brigand::false_type test(...);\n        static const bool value =\n            std::is_same<brigand::true_type, decltype(test<T, dummy>(nullptr))>::value;\n    };\n    template <class L, typename Index, bool>\n    struct at_dispatch\n    {\n        using type = at_c<L, Index::value>;\n    };\n    template <class L, typename Index>\n    struct at_dispatch<L, Index, true>\n    {\n        using type = lookup<L, Index>;\n    };\n}\ntemplate <class Seq, typename K>\nusing at = typename detail::at_dispatch<Seq, K, detail::has_at_method<Seq>::value>::type;\n}\nnamespace brigand\n{\n  template <template <typename...> class, typename...>\n  struct bind\n  {\n  };\n}\nnamespace brigand\n{\n  template<unsigned int Index> struct args\n  {\n  };\n  struct _1 {};\n  struct _2 {};\n  using _3 = args<2>;\n  using _4 = args<3>;\n  using _5 = args<4>;\n  using _6 = args<5>;\n  using _7 = args<6>;\n  using _8 = args<7>;\n  using _9 = args<8>;\n  using _state = _1;\n  using _element = _2;\n}\nnamespace brigand\n{\ntemplate <typename T>\nstruct defer\n{\n};\ntemplate <typename T>\nstruct pin\n{\n};\ntemplate <typename T>\nstruct parent\n{\n};\nnamespace detail\n{\n    template <typename T, typename... Ts>\n    struct packaged_lcall\n    {\n    };\n    template <typename T, typename... Ls>\n  struct apply {\n    using type = T;\n  };\n  template <template<typename...> class F, typename...Ts, typename... Args>\n  struct apply<bind<F,Ts...>, Args...>\n  {\n    using type = F<typename apply<Ts, Args...>::type...>;\n  };\n  template <template <typename...> class F, typename... Ts, typename L, typename... Ls>\n  struct apply<F<Ts...>, L, Ls...> : F<typename apply<Ts, L, Ls...>::type...>\n  {\n  };\n  template <typename T, typename... Args, typename...Ls>\n  struct apply<pin<T>, list<Args...>, Ls...>\n  {\n    using type = T;\n  };\n  template <unsigned int N, typename L, typename...Ls>\n  struct apply<args<N>, L, Ls...>\n  {\n    using type = at_c<L, N>;\n  };\n  template <typename T, typename...Ts, typename...Ls>\n  struct apply<_1, list<T, Ts...>, Ls...>\n  {\n    using type = T;\n  };\n  template <typename T, typename U, typename...Ts, typename...Ls>\n  struct apply<_2, list<T, U, Ts...>, Ls...>\n  {\n    using type = U;\n  };\n  template <typename T, typename L, typename...Ls>\n  struct apply<parent<T>, L, Ls...> : apply<T,Ls...>\n  {\n  };\n  template <typename Lambda, typename L, typename...Ls>\n  struct apply<defer<Lambda>, L, Ls...>\n  {\n    using type = packaged_lcall<Lambda, L, Ls...>;\n  };\n  template <typename T, typename... PLs, typename L, typename...Ls>\n  struct apply<packaged_lcall<T, PLs...>, L, Ls...> {\n    using type = T;\n  };\n  template <template <typename...> class Lambda, typename... Ts, typename... PLs, typename L, typename...Ls>\n  struct apply<packaged_lcall<bind<Lambda,Ts...>, PLs...>, L, Ls...>\n  {\n    using type = Lambda<typename apply<Ts, L, PLs...>::type...>;\n  };\n  template <template <typename...> class Lambda, typename... Ts, typename... PLs, typename L, typename...Ls>\n  struct apply<packaged_lcall<Lambda<Ts...>, PLs...>, L, Ls...> : Lambda<typename apply<Ts, L, PLs...>::type...>\n  {\n  };\n  template <typename T, typename... PLs, typename L, typename...Ls>\n  struct apply<packaged_lcall<pin<T>, PLs...>, L, Ls...> {\n    using type = T;\n  };\n  template <unsigned int N, typename... PLs, typename L, typename...Ls>\n  struct apply<packaged_lcall<args<N>, PLs...>, L, Ls...>\n  {\n    using type = at_c<L, N>;\n  };\n  template <typename... PLs, typename T, typename... Ts, typename...Ls>\n  struct apply<packaged_lcall<_1, PLs...>, list<T, Ts...>, Ls...>\n  {\n    using type = T;\n  };\n  template <typename... PLs, typename T, typename U, typename... Ts, typename...Ls>\n  struct apply<packaged_lcall<_2, PLs...>, list<T, U, Ts...>, Ls...>\n  {\n    using type = U;\n  };\n  template <typename T, typename... PLs, typename L, typename...Ls>\n  struct apply<packaged_lcall<parent<T>, PLs...>, L, Ls...> : apply<T, PLs...>\n  {\n  };\n  template <typename Lambda, typename... PLs, typename L, typename...Ls>\n  struct apply<packaged_lcall<defer<Lambda>, PLs...>, L, Ls...>\n  {\n    using type = packaged_lcall<Lambda, L, PLs...>;\n  };\n  template <typename... LcallArgs, typename... PLs, typename L, typename...Ls>\n  struct apply<packaged_lcall<packaged_lcall<LcallArgs...>, PLs...>, L, Ls...> : apply<packaged_lcall<LcallArgs...>, L>\n  {\n  };\n  template<typename T, typename...Ts>\n  using bound_apply = typename apply<T, brigand::list<Ts...>>::type;\n}\ntemplate <typename Lambda, typename... Args>\nusing apply = typename detail::apply<Lambda, brigand::list<Args...>>::type;\n}\n#if defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__)\n#define BRIGAND_COMP_MSVC\n#if _MSC_VER == 1900\n#define BRIGAND_COMP_MSVC_2015\n#elif _MSC_VER == 1800\n#define BRIGAND_COMP_MSVC_2013\n#endif\n#elif __INTEL_COMPILER\n#define BRIGAND_COMP_INTEL\n#elif __GNUC__\n#ifndef __clang__\n#define BRIGAND_COMP_GCC\n#else\n#define BRIGAND_COMP_CLANG\n#endif\n#endif\n#if defined(__CUDACC__)\n#define BRIGAND_COMP_CUDA\n#endif\nnamespace brigand\n{\nnamespace detail\n{\n  template<class L>\n  struct clear_impl;\n  template<template<class...> class L, class... Ts>\n  struct clear_impl<L<Ts...>>\n  {\n    using type = L<>;\n  };\n}\n  template<class L>\n  using clear = typename detail::clear_impl<L>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <bool b, typename O, typename L, unsigned int I>\n    struct split_at_impl;\n#if defined(BRIGAND_COMP_INTEL)\n    template <template <typename...> class S, typename... Os>\n    struct split_at_impl<false, S<Os...>, S<>, 0>\n    {\n        using type = S<S<Os...>, S<>>;\n    };\n#else\n    template <template <typename...> class S, typename... Os, typename... Ts>\n    struct split_at_impl<false, S<Os...>, S<Ts...>, 0>\n    {\n        using type = S<S<Os...>, S<Ts...>>;\n    };\n#endif\n    template <template <typename...> class S, typename... Os, typename T, typename... Ts>\n    struct split_at_impl<false, S<Os...>, S<T, Ts...>, 0>\n    {\n        using type = S<S<Os...>, S<T, Ts...>>;\n    };\n    template <template <typename...> class S, typename... Os, typename T, typename... Ts,\n              unsigned int I>\n    struct split_at_impl<false, S<Os...>, S<T, Ts...>, I>\n        : split_at_impl<false, S<Os..., T>, S<Ts...>, (I - 1)>\n    {\n    };\n    template <template <typename...> class S, typename... Os, typename T1, typename T2, typename T3,\n              typename T4, typename T5, typename T6, typename T7, typename T8, typename T9,\n              typename T10, typename T11, typename T12, typename T13, typename T14, typename T15,\n              typename T16, typename... Ts, unsigned int I>\n    struct split_at_impl<\n        true, S<Os...>,\n        S<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, Ts...>, I>\n        : split_at_impl<((I - 16) > 16), S<Os..., T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,\n                                           T13, T14, T15, T16>,\n                        S<Ts...>, (I - 16)>\n    {\n    };\n    template <typename L, typename I>\n    struct call_split_at_impl : split_at_impl<(I::value > 16), brigand::clear<L>, L, I::value>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename L, typename I>\n    using split_at = ::brigand::detail::call_split_at_impl<L, I>;\n}\ntemplate <typename L, typename I>\nusing split_at = typename ::brigand::lazy::split_at<L, I>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class L, class... T>\n    struct push_front_impl;\n    template <template <class...> class L, class... U, class... T>\n    struct push_front_impl<L<U...>, T...>\n    {\n        using type = L<T..., U...>;\n    };\n}\nnamespace lazy\n{\n    template <class L, class... T>\n    struct push_front : detail::push_front_impl<L, T...>\n    {\n    };\n}\ntemplate <class L, class... T>\nusing push_front = typename detail::push_front_impl<L, T...>::type;\nnamespace detail\n{\n    template <class L>\n    struct front_impl;\n    template <template <class...> class L, class T, class... U>\n    struct front_impl<L<T, U...>>\n    {\n        using type = T;\n    };\n}\ntemplate <class L>\nusing front = typename detail::front_impl<L>::type;\nnamespace detail\n{\n    template <class L, unsigned int N>\n    struct pop_front_impl;\n    template <template <class...> class L, class T, class... U>\n    struct pop_front_impl<L<T, U...>, 1>\n    {\n        using type = L<U...>;\n    };\n    template <template <class...> class L, class>\n    struct pop_front_element;\n    template <template <class...> class L, class... Ts>\n    struct pop_front_element<L, list<Ts...>>\n    {\n        template <class... Us>\n        static L<Us...> impl(Ts..., type_<Us> *...);\n    };\n    template <template <class...> class L, class... Ts, unsigned int N>\n    struct pop_front_impl<L<Ts...>, N>\n    {\n        using type = decltype(pop_front_element<L, filled_list<void const *, N>>::impl(\n            static_cast<type_<Ts> *>(nullptr)...));\n    };\n}\nnamespace lazy\n{\n    template <class L, class N = brigand::integral_constant<unsigned int, 1>>\n    struct pop_front : detail::pop_front_impl<L, N::value>\n    {\n    };\n}\ntemplate <class L, class N = brigand::integral_constant<unsigned int, 1>>\nusing pop_front = typename detail::pop_front_impl<L, N::value>::type;\n}\n#if __cplusplus >= 201402L\n#include <array>\n#endif\nnamespace brigand\n{\n  template<unsigned int Index> struct args;\n  namespace detail\n  {\n    template<typename T, typename List>\n    struct substitute_impl\n    {\n      using type = T;\n    };\n    template<template<class...> class T, typename... Ts, typename List>\n    struct substitute_impl<T<Ts...>,List>\n    {\n      using type = T<typename substitute_impl<Ts,List>::type...>;\n    };\n    template<unsigned int Index, typename List>\n    struct substitute_impl<args<Index>,List>\n    {\n      using type = brigand::at_c<List,Index>;\n    };\n  }\n  template<typename T, typename List>\n  using substitute = typename detail::substitute_impl<T,List>::type;\n}\n#include <cstddef>\n#include <cstdint>\nnamespace brigand\n{\ntemplate <std::int8_t V>\nusing int8_t = brigand::integral_constant<std::int8_t, V>;\ntemplate <std::uint8_t V>\nusing uint8_t = brigand::integral_constant<std::uint8_t, V>;\ntemplate <std::int16_t V>\nusing int16_t = brigand::integral_constant<std::int16_t, V>;\ntemplate <std::uint16_t V>\nusing uint16_t = brigand::integral_constant<std::uint16_t, V>;\ntemplate <std::int32_t V>\nusing int32_t = brigand::integral_constant<std::int32_t, V>;\ntemplate <std::uint32_t V>\nusing uint32_t = brigand::integral_constant<std::uint32_t, V>;\ntemplate <std::int64_t V>\nusing int64_t = brigand::integral_constant<std::int64_t, V>;\ntemplate <std::uint64_t V>\nusing uint64_t = brigand::integral_constant<std::uint64_t, V>;\ntemplate <std::size_t V>\nusing size_t = brigand::integral_constant<std::size_t, V>;\ntemplate <std::ptrdiff_t V>\nusing ptrdiff_t = brigand::integral_constant<std::ptrdiff_t, V>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n#if defined(BRIGAND_COMP_GCC) || defined(BRIGAND_COMP_CLANG)\n#if __cplusplus >= 201402L\n    template <std::size_t length>\n    constexpr unsigned int count_bools(const std::array<bool, length> & s) noexcept\n    {\n        unsigned int number_of_true_elements = 0;\n        for (std::size_t i = 0; i < length; ++i)\n        {\n            if (s[i])\n            {\n                number_of_true_elements++;\n            }\n        }\n        return number_of_true_elements;\n    }\n#else\n    constexpr unsigned int count_bools(bool const * const begin, bool const * const end,\n                                       unsigned int n)\n    {\n        return begin == end ? n : detail::count_bools(begin + 1, end, n + *begin);\n    }\n#endif\n#endif\n    template <bool... Bs>\n    struct template_count_bools\n    {\n        using type = ::brigand::size_t<0>;\n    };\n    template <bool B, bool... Bs>\n    struct template_count_bools<B, Bs...>\n    {\n        using type = ::brigand::size_t<B + template_count_bools<Bs...>::type::value>;\n    };\n    template <bool B1, bool B2, bool B3, bool B4, bool B5, bool B6, bool B7, bool B8, bool B9,\n              bool B10, bool B11, bool B12, bool B13, bool B14, bool B15, bool B16, bool... Bs>\n    struct template_count_bools<B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15,\n                                B16, Bs...>\n    {\n        using type =\n            ::brigand::size_t<B1 + B2 + B3 + B4 + B5 + B6 + B7 + B8 + B9 + B10 + B11 + B12 + B13 +\n                              B14 + B15 + B16 + template_count_bools<Bs...>::type::value>;\n    };\n}\nnamespace lazy\n{\n    template <typename List, typename Pred>\n    struct count_if\n    {\n    };\n    template <template <typename...> class S, typename Pred>\n    struct count_if<S<>, Pred>\n    {\n        using type = ::brigand::size_t<0>;\n    };\n#if defined(BRIGAND_COMP_GCC) || defined(BRIGAND_COMP_CLANG)\n    template <template <typename...> class S, template <typename...> class F>\n    struct count_if<S<>, bind<F, _1>>\n    {\n        using type = ::brigand::size_t<0>;\n    };\n    template <template <typename...> class S, template <typename...> class F>\n    struct count_if<S<>, F<_1>>\n    {\n        using type = ::brigand::size_t<0>;\n    };\n    template <template <typename...> class S, typename T, typename... Ts,\n              typename Pred>\n    struct count_if<S<T, Ts...>, Pred> {\n      static constexpr std::array<bool, 1 + sizeof...(Ts)> s_v{\n          {::brigand::apply<Pred, T>::value,\n           ::brigand::apply<Pred, Ts>::value...}};\n      using type = brigand::size_t<::brigand::detail::count_bools(s_v)>;\n    };\n    template <template <typename...> class S, typename T, typename... Ts,\n              template <typename...> class F>\n    struct count_if<S<T, Ts...>, bind<F, _1>> {\n      static constexpr std::array<bool, sizeof...(Ts) + 1> s_v{\n          {F<T>::value, F<Ts>::value...}};\n      using type = brigand::size_t<::brigand::detail::count_bools(s_v)>;\n    };\n    template <template <typename...> class S, typename T, typename... Ts,\n              template <typename...> class F>\n    struct count_if<S<T, Ts...>, F<_1>> {\n      static constexpr std::array<bool, sizeof...(Ts) + 1> s_v{\n          {F<T>::type::value, F<Ts>::type::value...}};\n      using type = brigand::size_t<::brigand::detail::count_bools(s_v)>;\n    };\n#else\n#if defined(BRIGAND_COMP_MSVC_2015)\n    template <template <typename...> class S, typename... Ts, typename Pred>\n    struct count_if<S<Ts...>, Pred>\n        : ::brigand::detail::template_count_bools<::brigand::apply<Pred, Ts>::value...>\n    {\n    };\n#else\n    template <template <typename...> class S, typename... Ts, typename Pred>\n    struct count_if<S<Ts...>, Pred>\n    {\n        template <typename T>\n        using val_t = brigand::apply<Pred, T>;\n        using type = typename ::brigand::detail::template_count_bools<val_t<Ts>::value...>::type;\n    };\n#endif\n#endif\n}\ntemplate <typename List, typename Pred>\nusing count_if = typename lazy::count_if<List, Pred>::type;\ntemplate <class... T>\nusing count = brigand::integral_constant<unsigned int, sizeof...(T)>;\n}\nnamespace brigand\n{\n  template<class L> using size = wrap<L, count>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class L, class... T>\n    struct push_back_impl;\n    template <template <class...> class L, class... U, class... T>\n    struct push_back_impl<L<U...>, T...>\n    {\n        using type = L<U..., T...>;\n    };\n}\ntemplate <class L, class... T>\nusing push_back = typename detail::push_back_impl<L, T...>::type;\ntemplate <class L>\nusing back = at_c<L, size<L>::value - 1>;\ntemplate <class L, class N = brigand::integral_constant<unsigned int, 1>>\nusing pop_back =\n    front<split_at<L, brigand::integral_constant<unsigned int, size<L>::value - N::value>>>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class L1, class L2>\n    struct rot90;\n    template <class... L1, template <class...> class S1, class... T1, template <class...> class S2,\n              class... T2, template <class...> class S3, class... T3, template <class...> class S4,\n              class... T4, template <class...> class S5, class... T5, template <class...> class S6,\n              class... T6, template <class...> class S7, class... T7, template <class...> class S8,\n              class... T8, class... L2>\n    struct rot90<list<L1...>, list<S1<T1...>, S2<T2...>, S3<T3...>, S4<T4...>, S5<T5...>, S6<T6...>,\n                                   S7<T7...>, S8<T8...>, L2...>>\n        : rot90<list<push_back<L1, T1, T2, T3, T4, T5, T6, T7, T8>...>, list<L2...>>\n    {\n    };\n    template <class... L1, template <class...> class S, class... T, class... L2>\n    struct rot90<list<L1...>, list<S<T...>, L2...>> : rot90<list<push_back<L1, T>...>, list<L2...>>\n    {\n    };\n    template <class L1>\n    struct rot90<L1, list<>>\n    {\n        using type = L1;\n    };\n    template <class Func, class Seq1, class Seq2, class Seqs>\n    struct transform_impl;\n    template <class F, class T1, class T2, class Seq>\n    struct transform_apply;\n    template <class F, class T1, class T2, class... Ts>\n    struct transform_apply<F, T1, T2, list<Ts...>>\n    {\n        using type = brigand::apply<F, T1, T2, Ts...>;\n    };\n    template <class Func, template <class...> class Seq1, class... T1,\n              template <class...> class Seq2, class... T2, class... Seqs>\n    struct transform_impl<Func, Seq1<T1...>, Seq2<T2...>, list<Seqs...>>\n    {\n        using type = Seq1<typename transform_apply<Func, T1, T2, Seqs>::type...>;\n    };\n    template <unsigned int N, class Seq1, class Seq2, class... FuncOrSeqs>\n    struct transform : transform_impl<back<list<FuncOrSeqs...>>, Seq1, Seq2,\n                                      typename rot90<filled_list<list<>, size<Seq1>::value>,\n                                                     pop_back<list<FuncOrSeqs...>>>::type>\n    {\n    };\n    template <template <class...> class Seq, class... T, class Func>\n    struct transform<0, Seq<T...>, Func>\n    {\n        using type = Seq<brigand::apply<Func, T>...>;\n    };\n    template <template <class...> class Seq, class... T, template <typename...> class Func>\n    struct transform<0, Seq<T...>, bind<Func, _1>>\n    {\n        using type = Seq<Func<T>...>;\n    };\n    template <template <class...> class Seq, class... T, template <typename...> class Func>\n    struct transform<0, Seq<T...>, Func<_1>>\n    {\n        using type = Seq<typename Func<T>::type...>;\n    };\n    template <template <class...> class Seq1, class... T1, template <class...> class Seq2,\n              class... T2, class Func>\n    struct transform<1, Seq1<T1...>, Seq2<T2...>, Func>\n    {\n        using type = Seq1<brigand::apply<Func, T1, T2>...>;\n    };\n}\nnamespace lazy\n{\n    template <typename Sequence1, typename OpSeq1, typename... OpSeq2>\n    struct transform : detail::transform<sizeof...(OpSeq2), Sequence1, OpSeq1, OpSeq2...>\n    {\n    };\n}\ntemplate <typename Sequence1, typename OpSeq1, typename... OpSeq2>\nusing transform = typename detail::transform<sizeof...(OpSeq2), Sequence1, OpSeq1, OpSeq2...>::type;\n}\nnamespace brigand\n{\ntemplate <typename T>\nstruct make_integral : brigand::integral_constant<typename T::value_type, T::value>\n{\n};\ntemplate <typename L>\nusing as_integral_list = transform<L, make_integral<brigand::_1>>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <typename L, template <class...> class Sequence>\n    struct as_sequence_impl\n    {\n        using type = wrap<L, Sequence>;\n    };\n}\ntemplate <typename L, template <class...> class Sequence>\nusing as_sequence = typename detail::as_sequence_impl<L, Sequence>::type;\ntemplate <typename L>\nusing as_list = as_sequence<L, brigand::list>;\n}\n#include <utility>\nnamespace brigand\n{\n  template <typename... T>\n  struct pair_wrapper_\n  {\n    static_assert (sizeof...(T) == 2\n                  , \"as_pair requires a type list of exactly two types\"\n                  );\n    using type = no_such_type_;\n  };\n  template <typename T, typename U>\n  struct pair_wrapper_<T,U>\n  {\n      using type = std::pair<T,U>;\n  };\n  template <typename... T>\n  using pair_wrapper = typename pair_wrapper_<T...>::type;\n  template <typename L>\n  using as_pair = wrap<L, pair_wrapper>;\n}\n#include <tuple>\nnamespace brigand\n{\n  template <typename... T>\n  using tuple_wrapper = typename std::tuple<T...>;\n  template <typename L>\n  using as_tuple = wrap<L, tuple_wrapper>;\n}\n#if !defined(BRIGAND_NO_BOOST_SUPPORT)\n#include <boost/fusion/container/vector/vector_fwd.hpp>\n#include <boost/fusion/container/deque/deque_fwd.hpp>\n#include <boost/fusion/container/list/list_fwd.hpp>\n#include <boost/fusion/container/set/set_fwd.hpp>\nnamespace brigand\n{\n  template <typename... T>\n  using fusion_vector_wrapper = boost::fusion::vector<T...>;\n  template <typename... T>\n  using fusion_list_wrapper = boost::fusion::list<T...>;\n  template <typename... T>\n  using fusion_deque_wrapper = boost::fusion::deque<T...>;\n  template <typename... T>\n  using fusion_set_wrapper = boost::fusion::set<T...>;\n  template <typename L> using as_fusion_vector = wrap<L, fusion_vector_wrapper>;\n  template <typename L> using as_fusion_deque  = wrap<L, fusion_deque_wrapper>;\n  template <typename L> using as_fusion_list   = wrap<L, fusion_list_wrapper>;\n  template <typename L> using as_fusion_set    = wrap<L, fusion_set_wrapper>;\n}\n#include <boost/variant.hpp>\nnamespace brigand\n{\n  template <typename... T>\n  using variant_wrapper = typename boost::variant<T...>;\n  template <typename L>\n  using as_variant = wrap<L, variant_wrapper>;\n}\n#endif\nnamespace brigand\n{\ntemplate <bool B>\nusing bool_ = brigand::integral_constant<bool, B>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <typename Args>\n  struct non_null_impl : bool_<Args::value != 0>{};\n    using non_null = non_null_impl<_1>;\n}\n}\n#include <initializer_list>\n#include <type_traits>\nnamespace brigand\n{\n#if defined(BRIGAND_COMP_MSVC_2013) || defined(BRIGAND_COMP_CUDA) || defined(BRIGAND_COMP_INTEL) || (defined(_LIBCPP_VERSION) && __cplusplus < 201402L)\nnamespace detail\n{\n    template <class P, class T>\n    struct all_helper : ::brigand::apply<P, T>\n    {\n    };\n    template <bool...>\n    struct bools_\n    {\n    };\n    template <typename Sequence, typename Predicate, typename... Ts>\n    struct all_impl;\n    template <template <class...> class Sequence, typename Predicate, typename... Ts>\n    struct all_impl<Sequence<Ts...>, Predicate>\n        : std::is_same<bools_<true, all_helper<Predicate, Ts>::value...>,\n                       bools_<all_helper<Predicate, Ts>::value..., true>>\n    {\n    };\n}\n#else\nnamespace detail\n{\n    struct all_same\n    {\n        const bool value = false;\n        constexpr all_same(...) {}\n        template <typename T>\n        constexpr all_same(std::initializer_list<T *>) : value{true}\n        {\n        }\n    };\n    template <typename Sequence, typename Predicate>\n    struct all_impl : bool_<true>\n    {\n    };\n    template <template <class...> class Sequence, typename Predicate, typename T, typename... Ts>\n    struct all_impl<Sequence<T, Ts...>, Predicate>\n    {\n        static constexpr all_same tester{\n            static_cast< ::brigand::apply<Predicate, T> *>(nullptr),\n            static_cast< ::brigand::apply<Predicate, Ts> *>(nullptr)...};\n        using type = bool_<(::brigand::apply<Predicate, T>::value != 0 && tester.value)>;\n    };\n    template <template <class...> class Sequence, template <typename...> class F, typename T,\n              typename... Ts>\n    struct all_impl<Sequence<T, Ts...>, bind<F, _1>>\n    {\n        static constexpr all_same tester{static_cast<F<T> *>(nullptr),\n                                         static_cast<F<Ts> *>(nullptr)...};\n        using type = bool_<(F<T>::value != 0 && tester.value)>;\n    };\n    template <template <class...> class Sequence, template <typename...> class F, typename T,\n              typename... Ts>\n    struct all_impl<Sequence<T, Ts...>, F<_1>>\n    {\n        static constexpr all_same tester{static_cast<typename F<T>::type *>(nullptr),\n                                         static_cast<typename F<Ts>::type *>(nullptr)...};\n        using type = bool_<(F<T>::type::value != 0 && tester.value)>;\n    };\n}\n#endif\ntemplate <typename Sequence, typename Predicate = detail::non_null>\nusing all = typename detail::all_impl<Sequence, Predicate>::type;\n}\nnamespace brigand\n{\n#if defined(BRIGAND_COMP_MSVC_2013) || defined(BRIGAND_COMP_CUDA) || defined(BRIGAND_COMP_INTEL) || (defined(_LIBCPP_VERSION) && __cplusplus < 201402L)\nnamespace detail\n{\n    template <typename Sequence, typename Pred>\n    struct none_impl\n    {\n        template <typename T>\n        struct nope\n        {\n            using that = brigand::apply<Pred, T>;\n            using type = bool_<!that::value>;\n        };\n        using type = all<Sequence, nope<_1>>;\n    };\n}\n#else\nnamespace detail\n{\n    template <typename Sequence, typename Predicate>\n    struct none_impl : bool_<true>\n    {\n    };\n    template <template <class...> class Sequence, typename Predicate, typename T, typename... Ts>\n    struct none_impl<Sequence<T, Ts...>, Predicate>\n    {\n        static constexpr all_same tester{\n            static_cast< ::brigand::apply<Predicate, T> *>(nullptr),\n            static_cast< ::brigand::apply<Predicate, Ts> *>(nullptr)...};\n        using type = bool_<(::brigand::apply<Predicate, T>::value == 0 && tester.value)>;\n    };\n    template <template <class...> class Sequence, template <typename...> class F, typename T,\n              typename... Ts>\n    struct none_impl<Sequence<T, Ts...>, bind<F, _1>>\n    {\n        static constexpr all_same tester{static_cast<F<T> *>(nullptr),\n                                         static_cast<F<Ts> *>(nullptr)...};\n        using type = bool_<(F<T>::value == 0 && tester.value)>;\n    };\n    template <template <class...> class Sequence, template <typename...> class F, typename T,\n              typename... Ts>\n    struct none_impl<Sequence<T, Ts...>, F<_1>>\n    {\n        static constexpr all_same tester{static_cast<typename F<T>::type *>(nullptr),\n                                         static_cast<typename F<Ts>::type *>(nullptr)...};\n        using type = bool_<(F<T>::type::value == 0 && tester.value)>;\n    };\n}\n#endif\ntemplate <typename Sequence, typename Predicate = detail::non_null>\nusing none = typename detail::none_impl<Sequence, Predicate>::type;\n}\nnamespace brigand\n{\n  namespace detail\n  {\n    template< typename Sequence, typename Predicate >\n    struct any_impl : bool_<!none<Sequence,Predicate>::value> {};\n  }\n  template<typename Sequence, typename Predicate = detail::non_null>\n  using any = typename detail::any_impl<Sequence,Predicate>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <template <typename...> class S, template <typename...> class F, typename... Ts>\n    struct finder\n    {\n        template <typename T>\n        using P = F<Ts..., T>;\n        template <bool InNext8, bool Match, typename... Ls>\n        struct find\n        {\n            using type = S<>;\n        };\n        template <typename L>\n        struct find<true, false, L>\n        {\n            using type = S<>;\n        };\n        template <typename L, typename... Ls>\n        struct find<true, true, L, Ls...>\n        {\n            using type = S<L, Ls...>;\n        };\n        template <typename L1, typename L2, typename... Ls>\n        struct find<true, false, L1, L2, Ls...> : find<true, F<Ts..., L2>::value, L2, Ls...>\n        {\n        };\n        template <typename L0, typename L1, typename L2, typename L3, typename L4, typename L5,\n                  typename L6, typename L7, typename L8,\n                  typename... Ls>\n        struct find<false, false, L0, L1, L2, L3, L4, L5, L6, L7, L8, Ls...>\n            : find<true, F<Ts..., L8>::value, L8, Ls...>\n        {\n        };\n        template <typename L1, typename L2, typename L3, typename L4, typename L5, typename L6,\n                  typename L7, typename L8, typename L9, typename L10, typename L11, typename L12,\n                  typename L13, typename L14, typename L15, typename L16,\n                  typename... Ls>\n        struct find<false, false, L1, L2, L3, L4, L5, L6, L7, L8, L9, L10, L11, L12, L13, L14, L15,\n                    L16, Ls...>\n            : find<(P<L9>::value || P<L10>::value || P<L11>::value || P<L12>::value ||\n                    P<L13>::value || P<L14>::value || P<L15>::value || P<L16>::value),\n                   P<L9>::value, L9, L10, L11, L12, L13, L14, L15, L16, Ls...>\n        {\n        };\n    };\n}\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <template<class...> class L, class...>\n    struct reverse_elements;\n    template <template <class...> class L>\n    struct reverse_elements<L>\n    {\n        using type = L<>;\n    };\n    template <template <class...> class L, class T0, class... Ts>\n    struct reverse_elements<L, T0, Ts...>\n    : append_impl<typename reverse_elements<L, Ts...>::type, L<T0>>\n    {\n    };\n    template <template <class...> class L, class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class... Ts>\n    struct reverse_elements<L, T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, Ts...>\n    : append_impl<typename reverse_elements<L, Ts...>::type, L<T15, T14, T13, T12, T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0>>\n    {\n    };\n    template<class L>\n    struct reverse_impl;\n    template<template<class...> class L, class... U>\n    struct reverse_impl<L<U...>>\n    : reverse_elements<L, U...>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename L>\n    using reverse = typename detail::reverse_impl<L>;\n}\n    template <typename L>\n    using reverse = typename detail::reverse_impl<L>::type;\n}\nnamespace brigand\n{\nnamespace lazy\n{\n    template <typename Sequence, typename Predicate = ::brigand::detail::non_null>\n    struct find;\n    template <template <typename...> class Sequence, typename... Ls, typename Pred>\n    struct find<Sequence<Ls...>, Pred>\n        : detail::finder<Sequence, detail::bound_apply, Pred>::template find<\n              false, false, void, void, void, void, void, void, void, void, Ls...>\n    {\n    };\n    template <template <typename...> class Sequence, typename... Ls, template <typename...> class F>\n    struct find<Sequence<Ls...>, bind<F, _1>>\n        : detail::finder<Sequence, F>::template find<false, false, void, void, void, void, void,\n                                                     void, void, void, Ls...>\n    {\n    };\n}\ntemplate <typename Sequence, typename Predicate = brigand::detail::non_null>\nusing find = typename lazy::find<Sequence, Predicate>::type;\nnamespace lazy\n{\n    template <typename Sequence, typename Predicate = detail::non_null>\n    struct reverse_find {\n      using type =\n          ::brigand::reverse< ::brigand::find<brigand::reverse<Sequence>, Predicate>>;\n    };\n}\ntemplate <typename Sequence, typename Predicate = detail::non_null>\nusing reverse_find = typename ::brigand::lazy::reverse_find<Sequence, Predicate>::type;\nnamespace detail\n{\n    template <typename Sequence, typename Predicate>\n    using find_size = size<brigand::find<Sequence, Predicate>>;\n    template <typename Sequence, typename Predicate>\n    using empty_find = bool_<find_size<Sequence, Predicate>::value == 0>;\n    template <typename Sequence, typename Predicate>\n    using non_empty_find = bool_<find_size<Sequence, Predicate>::value != 0>;\n}\ntemplate <typename Sequence, typename Predicate = detail::non_null>\nusing not_found = typename detail::empty_find<Sequence, Predicate>;\ntemplate <typename Sequence, typename Predicate = detail::non_null>\nusing found = typename detail::non_empty_find<Sequence, Predicate>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class L>\n    struct flatten_impl\n    {\n        using type = L;\n    };\n    template <template<class...> class L, class T>\n    struct flatten_element_impl\n    {\n        using type = L<T>;\n    };\n    template <template<class...> class L, class... Ts>\n    struct flatten_element_impl<L, L<Ts...>>\n    : append_impl<typename flatten_element_impl<L, Ts>::type...>\n    {\n    };\n    template <template<class...> class L, class... Ts>\n    struct flatten_impl<L<Ts...>>\n    : flatten_element_impl<L, L<Ts...>>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename Sequence>\n    using flatten = typename detail::flatten_impl<Sequence>;\n}\ntemplate <typename Sequence>\nusing flatten = typename lazy::flatten<Sequence>::type;\n}\nnamespace brigand { namespace detail\n{\n  template<class Functor, class State, class Sequence>\n  struct fold_impl\n  {\n      using type = State;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0>\n  struct fold_impl<Functor, State, Sequence<T0>>\n  {\n      using type = brigand::apply<Functor, State, T0>;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0, class T1>\n  struct fold_impl<Functor, State, Sequence<T0, T1>>\n  {\n      using type = brigand::apply<Functor,\n          brigand::apply<Functor,State, T0>, T1\n      >;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0, class T1, class T2>\n  struct fold_impl<Functor, State, Sequence<T0, T1, T2>>\n  {\n      using type = brigand::apply<Functor,\n          brigand::apply<Functor,\n              brigand::apply<Functor, State, T0>, T1\n          >, T2\n      >;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0, class T1, class T2, class T3>\n  struct fold_impl<Functor, State, Sequence<T0, T1, T2, T3>>\n  {\n      using type = brigand::apply<Functor,\n          brigand::apply<Functor, \n              brigand::apply<Functor,\n                  brigand::apply<Functor, State, T0>, T1\n              >, T2\n          >, T3\n      >;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0, class T1, class T2, class T3, class T4>\n  struct fold_impl<Functor, State, Sequence<T0, T1, T2, T3, T4>>\n  {\n      using type = brigand::apply<Functor,\n          brigand::apply<Functor,\n              brigand::apply<Functor,\n                  brigand::apply<Functor,\n                      brigand::apply<Functor, State, T0>, T1\n                  >, T2\n              >, T3\n          >, T4\n      >;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0, class T1, class T2, class T3, class T4, class T5>\n  struct fold_impl<Functor, State, Sequence<T0, T1, T2, T3, T4, T5>>\n  {\n      using type = brigand::apply<Functor,\n          brigand::apply<Functor,\n              brigand::apply<Functor,\n                  brigand::apply<Functor,\n                      brigand::apply<Functor,\n                          brigand::apply<Functor, State, T0>, T1\n                      >, T2\n                  >, T3\n              >, T4\n          >, T5\n      >;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0, class T1, class T2, class T3, class T4, class T5, class T6>\n  struct fold_impl<Functor, State, Sequence<T0, T1, T2, T3, T4, T5, T6>>\n  {\n      using type = brigand::apply<Functor,\n          brigand::apply<Functor,\n              brigand::apply<Functor,\n                  brigand::apply<Functor,\n                      brigand::apply<Functor,\n                          brigand::apply<Functor,\n                              brigand::apply<Functor, State, T0>, T1\n                          >, T2\n                      >, T3\n                  >, T4\n              >, T5\n          >, T6\n      >;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7>\n  struct fold_impl<Functor, State, Sequence<T0, T1, T2, T3, T4, T5, T6, T7>>\n  {\n      using type = brigand::apply<Functor,\n          brigand::apply<Functor,\n              brigand::apply<Functor,\n                  brigand::apply<Functor,\n                      brigand::apply<Functor,\n                          brigand::apply<Functor,\n                              brigand::apply<Functor,\n                                  brigand::apply<Functor, State, T0>, T1\n                              >, T2\n                          >, T3\n                      >, T4\n                  >, T5\n              >, T6\n          >, T7\n      >;\n  };\n  template<\n      class Functor, class State, template <class...> class Sequence,\n      class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class... T>\n  struct fold_impl<Functor, State, Sequence<T0, T1, T2, T3, T4, T5, T6, T7, T...>>\n  : fold_impl<\n      Functor,\n      brigand::apply<Functor,\n          brigand::apply<Functor,\n              brigand::apply<Functor,\n                  brigand::apply<Functor,\n                      brigand::apply<Functor,\n                          brigand::apply<Functor,\n                              brigand::apply<Functor,\n                                  brigand::apply<Functor,\n                                      State, T0\n                                  >, T1\n                              >, T2\n                          >, T3\n                      >, T4\n                  >, T5\n              >, T6\n          >, T7\n      >,\n      Sequence<T...>\n  >\n  {};\n  template<typename Functor, typename State, typename Sequence>\n  struct reverse_fold_impl\n  {\n    using type = State;\n  };\n  template <typename Functor, typename State, template <typename...> class L, typename T, typename... Ts>\n  struct reverse_fold_impl<Functor, State, L<T, Ts...>>\n  {\n      using type =\n          brigand::apply<Functor, typename reverse_fold_impl<Functor, State, L<Ts...>>::type, T>;\n  };\n  template<\n    typename Functor, typename State, template <typename...> class L,\n    typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename... Ts>\n    struct reverse_fold_impl<Functor, State, L<T0, T1, T2, T3, T4, T5, T6, T7, Ts...>>{\n    using type = brigand::apply<Functor,\n      brigand::apply<Functor,\n      brigand::apply<Functor,\n      brigand::apply<Functor,\n      brigand::apply<Functor,\n      brigand::apply<Functor,\n      brigand::apply<Functor,\n      brigand::apply<Functor,\n      typename reverse_fold_impl<Functor, State, L<Ts...>>::type, T7\n      >, T6\n      >, T5\n      >, T4\n      >, T3\n      >, T2\n      >, T1\n      >, T0\n      >;\n  };\n} }\nnamespace brigand\n{\nnamespace lazy\n{\n    template <class Sequence, class State, class Functor>\n    using fold = typename detail::fold_impl<Functor, State, Sequence>;\n    template <class Sequence, class State, class Functor>\n    using reverse_fold = typename detail::reverse_fold_impl<Functor, State, Sequence>;\n}\ntemplate <class Sequence, class State, class Functor>\nusing fold = typename ::brigand::lazy::fold<Sequence, State, Functor>::type;\ntemplate <class Sequence, class State, class Functor>\nusing reverse_fold = typename ::brigand::lazy::reverse_fold<Sequence, State, Functor>::type;\n}\n#include <initializer_list>\nnamespace brigand\n{\n  template<class F, class...Ts> constexpr F for_each_args(F f, Ts&&...a)\n  {\n    (void)std::initializer_list<int>{((void)f(static_cast<Ts&&>(a)),0)...};\n    return f;\n  }\n}\n#include <utility>\nnamespace brigand\n{\n  namespace detail\n  {\n    template<template<class...> class List, typename... Elements, typename Functor>\n    constexpr Functor for_each_impl( type_<List<Elements...>>&&, Functor f )\n    {\n      return for_each_args( std::move(f), type_<Elements>()... );\n    }\n  }\n  template<typename List, typename Functor> constexpr Functor for_each( Functor f )\n  {\n    return detail::for_each_impl( type_<List>{}, std::move(f) );\n  }\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <bool Found, class Sequence, typename Predicate, typename NotFoundType>\n    struct index_if_impl\n    {\n        using type = ::brigand::size_t<size<Sequence>::value -\n                                       size< ::brigand::find<Sequence, Predicate>>::value>;\n    };\n    template <class Sequence, typename Predicate, typename NotFoundType>\n    struct index_if_impl<false, Sequence, Predicate, NotFoundType>\n    {\n        using type = NotFoundType;\n    };\n}\ntemplate <class Sequence, class Predicate, class NotFoundType = no_such_type_>\nusing index_if = typename detail::index_if_impl< ::brigand::found<Sequence, Predicate>::value,\n                                                Sequence, Predicate, NotFoundType>::type;\ntemplate <class Sequence, typename T>\nusing index_of = index_if<Sequence, std::is_same<typename brigand::pin<T>, ::brigand::_1>>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class T, class, class, T>\n    struct range_cat;\n#ifdef BRIGAND_COMP_MSVC\n    template <class T, T Start, T Int>\n    struct int_plus\n    {\n        using type = brigand::integral_constant<T, Start + Int>;\n    };\n#endif\n    template <class T, class... Ts, T... Ints, T Start>\n    struct range_cat<T, list<Ts...>, list<brigand::integral_constant<T, Ints>...>, Start>\n    {\n#ifdef BRIGAND_COMP_MSVC\n        using type = list<Ts..., typename int_plus<T, Start, Ints>::type...>;\n#else\n        using type = list<Ts..., brigand::integral_constant<T, Start + Ints>...>;\n#endif\n    };\n    template <class T, T Start, unsigned int N>\n    struct range_impl : range_cat<T, typename range_impl<T, Start, N / 2>::type,\n                                  typename range_impl<T, Start, N - N / 2>::type, N / 2>\n    {\n    };\n    template <class T, T Start>\n    struct range_impl<T, Start, 1>\n    {\n        using type = list<brigand::integral_constant<T, Start>>;\n    };\n    template <class T, T Start>\n    struct range_impl<T, Start, 0>\n    {\n        using type = list<>;\n    };\n    template <class T, class, class, T>\n    struct reverse_range_cat;\n#ifdef BRIGAND_COMP_MSVC\n    template <class T, T Start, T Int>\n    struct int_minus\n    {\n        using type = brigand::integral_constant<T, Int - Start>;\n    };\n#endif\n    template <class T, class... Ts, T... Ints, T Start>\n    struct reverse_range_cat<T, list<Ts...>, list<brigand::integral_constant<T, Ints>...>, Start>\n    {\n#ifdef BRIGAND_COMP_MSVC\n        using type = list<Ts..., typename int_minus<T, Start, Ints>::type...>;\n#else\n        using type = list<Ts..., brigand::integral_constant<T, Ints - Start>...>;\n#endif\n    };\n    template <class T, T Start, unsigned int N>\n    struct reverse_range_impl\n        : reverse_range_cat<T, typename reverse_range_impl<T, Start, N / 2>::type,\n                            typename reverse_range_impl<T, Start, N - N / 2>::type, N / 2>\n    {\n    };\n    template <class T, T Start>\n    struct reverse_range_impl<T, Start, 1>\n    {\n        using type = list<brigand::integral_constant<T, Start>>;\n    };\n    template <class T, T Start>\n    struct reverse_range_impl<T, Start, 0>\n    {\n        using type = list<>;\n    };\n    template <class T, T Start, T Stop>\n    struct reverse_range_safe\n    {\n        static_assert(\n            Start >= Stop,\n            \"Invalid parameters. reverse_range<> syntax is reverse_range<type, from, down_to>\");\n        using type = typename reverse_range_impl<T, Start, Start - Stop>::type;\n    };\n}\ntemplate <class T, T Start, T Stop>\nusing range = typename detail::range_impl<T, Start, Stop - Start>::type;\ntemplate <class T, T Start, T Stop>\nusing reverse_range = typename detail::reverse_range_safe<T, Start, Stop>::type;\n}\nnamespace brigand\n{\n#ifdef BRIGAND_COMP_MSVC\nnamespace detail\n{\n    template <class, class T>\n    struct unique_x_t\n    {\n        operator T();\n    };\n    template <class Ints, class... Ts>\n    struct is_set_impl;\n    template <>\n    struct is_set_impl<list<>>\n    {\n        using type = brigand::true_type;\n    };\n    inline brigand::true_type true_fn(...);\n    template <class... Ints, class... Ts>\n    struct is_set_impl<list<Ints...>, Ts...>\n    {\n        struct Pack : unique_x_t<Ints, Ts>...\n        {\n        };\n        template <class... Us>\n        static auto is_set(Us...) -> decltype(true_fn(static_cast<Us>(Pack())...));\n        static brigand::false_type is_set(...);\n        using type = decltype(is_set(Ts()...));\n    };\n    template <class>\n    struct qrref\n    {\n    };\n    template <class>\n    struct qref\n    {\n    };\n    template <class>\n    struct qnoref\n    {\n    };\n    template <class T>\n    struct msvc_quali_ref\n    {\n        using type = qnoref<T>;\n    };\n    template <class T>\n    struct msvc_quali_ref<T &>\n    {\n        using type = qref<T>;\n    };\n    template <class T>\n    struct msvc_quali_ref<T &&>\n    {\n        using type = qrref<T>;\n    };\n}\ntemplate <class... Ts>\nusing is_set = typename detail::is_set_impl<range<unsigned int, 0, sizeof...(Ts)>,\n                                            detail::msvc_quali_ref<Ts>...>::type;\n#else\nnamespace detail\n{\n    template <class, class T>\n    struct unique_x_t : type_<T>\n    {\n    };\n    template <class Ints, class... Ts>\n    struct is_set_cont;\n    template <class... Ints, class... Ts>\n    struct is_set_cont<list<Ints...>, Ts...> : unique_x_t<Ints, Ts>...\n    {\n    };\n}\ntemplate <class... Ts>\nusing is_set = bool_<sizeof(detail::is_set_cont<range<unsigned int, 0, sizeof...(Ts)>, Ts...>) == 1>;\n#endif\n}\n#include <type_traits>\nnamespace brigand\n{\n#if defined(BRIGAND_COMP_GCC) || defined(BRIGAND_COMP_CLANG)\nnamespace lazy\n{\n    template <typename L, typename Pred>\n    struct remove_if;\n    template <template <class...> class L, typename Pred>\n    struct remove_if<L<>, Pred> {\n      using type = L<>;\n    };\n    template <template <class...> class L, typename T, typename... Ts,\n              typename Pred>\n    struct remove_if<L<T, Ts...>, Pred>\n        : ::brigand::detail::append_impl<\n              L<>,\n              typename std::conditional<::brigand::apply<Pred, T>::value,\n                                        list<>, list<T>>::type,\n              typename std::conditional<::brigand::apply<Pred, Ts>::value,\n                                        list<>, list<Ts>>::type...> {};\n    template <template <class...> class L, typename T, typename... Ts,\n              template <typename...> class F>\n    struct remove_if<L<T, Ts...>, bind<F, _1>>\n        : ::brigand::detail::append_impl<\n              L<>,\n              typename std::conditional<F<T>::value, list<>, list<T>>::type,\n              typename std::conditional<F<Ts>::value, list<>,\n                                        list<Ts>>::type...> {};\n    template <template <class...> class L, typename T, typename... Ts,\n              template <typename...> class F>\n    struct remove_if<L<T, Ts...>, F<_1>>\n        : ::brigand::detail::append_impl<\n              L<>,\n              typename std::conditional<F<T>::type::value, list<>,\n                                        list<T>>::type,\n              typename std::conditional<F<Ts>::type::value, list<>,\n                                        list<Ts>>::type...> {};\n}\nnamespace lazy\n{\n    template <typename L, typename T>\n    struct remove;\n    template <template <class...> class L, typename... Ts, typename T>\n    struct remove<L<Ts...>, T>\n        : ::brigand::detail::append_impl<\n              L<>, typename std::conditional<std::is_same<Ts, T>::value, list<>, list<Ts>>::type...>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename L, typename Pred>\n    struct filter;\n    template <template <class...> class L, typename Pred>\n    struct filter<L<>, Pred> {\n      using type = L<>;\n    };\n    template <template <class...> class L, typename T, typename... Ts,\n              typename Pred>\n    struct filter<L<T, Ts...>, Pred>\n        : ::brigand::detail::append_impl<\n              L<>,\n              typename std::conditional<::brigand::apply<Pred, T>::value,\n                                        list<T>, list<>>::type,\n              typename std::conditional<::brigand::apply<Pred, Ts>::value,\n                                        list<Ts>, list<>>::type...> {};\n    template <template <class...> class L, typename T, typename... Ts,\n              template <typename...> class F>\n    struct filter<L<T, Ts...>, bind<F, _1>>\n        : ::brigand::detail::append_impl<\n              L<>,\n              typename std::conditional<F<T>::value, list<T>, list<>>::type,\n              typename std::conditional<F<Ts>::value, list<Ts>,\n                                        list<>>::type...> {};\n    template <template <class...> class L, typename T, typename... Ts,\n              template <typename...> class F>\n    struct filter<L<T, Ts...>, F<_1>>\n        : ::brigand::detail::append_impl<\n              L<>,\n              typename std::conditional<F<T>::type::value, list<T>,\n                                        list<>>::type,\n              typename std::conditional<F<Ts>::type::value, list<Ts>,\n                                        list<>>::type...> {};\n}\n#else\nnamespace detail\n{\n    template <class Pred, class T>\n    struct empty_helper : ::brigand::apply<Pred, T>\n    {\n    };\n    template <typename Pred, typename T, bool B>\n    struct empty_if_true : std::conditional<empty_helper<Pred, T>::value == B, list<>, list<T>>\n    {\n    };\n    template <template <typename...> class F, typename T, bool B>\n    struct empty_if_true<bind<F, _1>, T, B> : std::conditional<F<T>::value == B, list<>, list<T>>\n    {\n    };\n    template <template <typename...> class F, typename T, bool B>\n    struct empty_if_true<F<_1>, T, B> : std::conditional<F<T>::type::value == B, list<>, list<T>>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename L, typename Pred>\n    struct remove_if;\n    template <template <class...> class L, typename... Ts, typename Pred>\n    struct remove_if<L<Ts...>, Pred>\n        : ::brigand::detail::append_impl<\n              L<>, typename ::brigand::detail::empty_if_true<Pred, Ts, true>::type...>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename L, typename T>\n    struct remove;\n    template <template <class...> class L, typename... Ts, typename T>\n    struct remove<L<Ts...>, T>\n        : ::brigand::detail::append_impl<\n              L<>, typename std::conditional<std::is_same<Ts, T>::value, list<>, list<Ts>>::type...>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename L, typename Pred>\n    struct filter;\n    template <template <class...> class L, typename... Ts, typename Pred>\n    struct filter<L<Ts...>, Pred>\n        : ::brigand::detail::append_impl<\n              L<>, typename ::brigand::detail::empty_if_true<Pred, Ts, false>::type...>\n    {\n    };\n}\n#endif\ntemplate <typename L, typename Pred>\nusing remove_if = typename lazy::remove_if<L, Pred>::type;\ntemplate <typename L, typename T>\nusing remove = typename lazy::remove<L, T>::type;\ntemplate <typename L, typename Pred>\nusing filter = typename lazy::filter<L, Pred>::type;\n}\nnamespace brigand\n{\ntemplate <class Seq, class Pred>\nusing partition = pair<filter<Seq, Pred>, remove_if<Seq, Pred>>;\n}\n#include <type_traits>\nnamespace brigand\n{\nnamespace detail\n{\n    template <typename Pred, typename T>\n    struct rep_helper\n    {\n        using type = brigand::apply<Pred, T>;\n    };\n    template <typename T, typename Pred, typename NewType>\n    struct replacer : std::conditional<rep_helper<Pred, T>::type::value, NewType, T>\n    {\n    };\n    template <typename T, template <typename...> class F, typename NewType>\n    struct replacer<T, bind<F, _1>, NewType> : std::conditional<F<T>::value, NewType, T>\n    {\n    };\n    template <typename T, template <typename...> class F, typename NewType>\n    struct replacer<T, F<_1>, NewType> : std::conditional<F<T>::type::value, NewType, T>\n    {\n    };\n}\nnamespace lazy\n{\n    template <typename Sequence, typename Predicate, typename NewType>\n    struct replace_if;\n    template <template <typename...> class S, typename... Ts, typename Predicate, typename NewType>\n    struct replace_if<S<Ts...>, Predicate, NewType>\n    {\n        using type = S<typename detail::replacer<Ts, Predicate, NewType>::type...>;\n    };\n    template <typename Sequence, typename OldType, typename NewType>\n    struct replace : replace_if<Sequence, std::is_same<_1, pin<OldType>>, NewType> {};\n}\ntemplate <typename Sequence, typename Predicate, typename NewType>\nusing replace_if = typename ::brigand::lazy::replace_if<Sequence, Predicate, NewType>::type;\ntemplate <typename Sequence, typename OldType, typename NewType>\nusing replace = typename ::brigand::lazy::replace<Sequence, OldType, NewType>::type;\n}\n#include <type_traits>\n#include <utility>\nnamespace brigand\n{\n  template<typename C, typename T, typename F>\n  inline typename std::enable_if<C::value,T&&>::type select(T&& t, F&&)\n  {\n    return std::forward<T>(t);\n  }\n  template<typename C, typename T, typename F>\n  inline typename std::enable_if<!C::value,F&&>::type select(T&&, F&& f)\n  {\n    return std::forward<F>(f);\n  }\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template<typename TOut, typename TCurrent, typename TDelim, typename... Ts>\n    struct split_impl;\n    template<template<typename...> class L, typename... Os, typename... Cs, typename TDelim, typename T, typename... Ts>\n    struct split_impl<L<Os...>, L<Cs...>, TDelim, T, Ts...> :\n        split_impl<L<Os...>, L<Cs..., T>, TDelim, Ts...> {};\n    template<template<typename...> class L, typename... Os, typename... Cs, typename TDelim, typename T>\n    struct split_impl<L<Os...>, L<Cs...>, TDelim, T> {\n        using type = L<Os..., L<Cs..., T>>;\n    };\n    template<template<typename...> class L, typename... Os, typename... Cs, typename TDelim, typename... Ts>\n    struct split_impl<L<Os...>, L<Cs...>, TDelim, TDelim, Ts...> :\n        split_impl<L<Os..., L<Cs...>>, L<>, TDelim, Ts...> {};\n    template<template<typename...> class L, typename... Os, typename... Cs, typename TDelim>\n    struct split_impl<L<Os...>, L<Cs...>, TDelim, TDelim> {\n        using type = L<Os..., L<Cs...>>;\n    };\n    template<template<typename...> class L, typename... Os, typename TDelim, typename... Ts>\n    struct split_impl<L<Os...>, L<>, TDelim, TDelim, Ts...> :\n        split_impl<L<Os...>, L<>, TDelim, Ts...> {};\n    template<template<typename...> class L, typename... Os, typename TDelim>\n    struct split_impl<L<Os...>, L<>, TDelim, TDelim> {\n        using type = L<Os...>;\n    };\n    template<template<typename...> class L, typename... Os, typename TDelim>\n    struct split_impl<L<Os...>, L<>, TDelim> {\n        using type = L<Os...>;\n    };\n    template<typename TList, typename TDelim>\n    struct split_helper;\n    template<template<typename...> class L, typename T, typename... Ts, typename TDelim>\n    struct split_helper<L<T,Ts...>, TDelim> : split_impl<L<>, L<>, TDelim, T, Ts...>{};\n#if defined(BRIGAND_COMP_INTEL)\n    template<template<typename...> class L, typename TDelim>\n    struct split_helper<L<>, TDelim> {\n        using type = L<>;\n    };\n#else\n    template<template<typename...> class L, typename... T, typename TDelim>\n    struct split_helper<L<T...>, TDelim> {\n        using type = L<>;\n    };\n#endif\n}\nnamespace lazy\n{\n    template<typename TList, typename TDelim>\n    using split = detail::split_helper<TList, TDelim>;\n}\ntemplate<typename TList, typename TDelim>\nusing split = typename lazy::split<TList, TDelim>::type;\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct less : bool_ < (A::value < B::value) > {};\n}\n#include <type_traits>\nnamespace brigand\n{\nnamespace detail\n{\n    template <class Comp, class T1, class U>\n    struct merge_helper : ::brigand::apply<Comp, T1, U>\n    {\n    };\n    template <class L, class Seq1, class Seq2, class Comp>\n    struct merge_impl;\n    template <bool, class L, class Seq1, class Seq2, class Comp>\n    struct merge_insert;\n    template <class... R, class T0, class T1, class... Ts, class U, class... Us, class Comp>\n    struct merge_insert<true, list<R...>, list<T0, T1, Ts...>, list<U, Us...>, Comp>\n        : merge_insert<merge_helper<Comp, T1, U>::value, list<R..., T0>, list<T1, Ts...>,\n                       list<U, Us...>, Comp>\n    {\n    };\n    template <class... R, class T, class U, class... Us, class Comp>\n    struct merge_insert<true, list<R...>, list<T>, list<U, Us...>, Comp>\n    {\n        using list = ::brigand::list<R..., T>;\n        using left = ::brigand::list<>;\n        using right = ::brigand::list<U, Us...>;\n    };\n    template <class... R, class T, class... Ts, class U0, class U1, class... Us, class Comp>\n    struct merge_insert<false, list<R...>, list<T, Ts...>, list<U0, U1, Us...>, Comp>\n        : merge_insert<merge_helper<Comp, T, U1>::value, list<R..., U0>, list<T, Ts...>,\n                       list<U1, Us...>, Comp>\n    {\n    };\n    template <class... R, class T, class... Ts, class U, class Comp>\n    struct merge_insert<false, list<R...>, list<T, Ts...>, list<U>, Comp>\n    {\n        using list = ::brigand::list<R..., U>;\n        using left = ::brigand::list<T, Ts...>;\n        using right = ::brigand::list<>;\n    };\n    template <class... R, class T0, class T1, class T2, class T3, class T4, class T5, class T6,\n              class T7, class T8, class T9, class... Ts, class U0, class U1, class U2, class U3,\n              class U4, class U5, class U6, class U7, class U8, class U9, class... Us, class Comp>\n    struct merge_impl<list<R...>, list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, Ts...>,\n                      list<U0, U1, U2, U3, U4, U5, U6, U7, U8, U9, Us...>, Comp>\n    {\n        using sub = merge_insert<merge_helper<Comp, T0, U0>::value, list<>,\n                                 list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>,\n                                 list<U0, U1, U2, U3, U4, U5, U6, U7, U8, U9>, Comp>;\n        using type = typename merge_impl<append<list<R...>, typename sub::list>,\n                                         append<typename sub::left, list<Ts...>>,\n                                         append<typename sub::right, list<Us...>>, Comp>::type;\n    };\n    template <class... R, class T, class... Ts, class U, class... Us, class Comp>\n    struct merge_impl<list<R...>, list<T, Ts...>, list<U, Us...>, Comp>\n        : std::conditional<merge_helper<Comp, T, U>::value,\n                           merge_impl<list<R..., T>, list<Ts...>, list<U, Us...>, Comp>,\n                           merge_impl<list<R..., U>, list<T, Ts...>, list<Us...>, Comp>>::type\n    {\n    };\n    template <class... R, class... Ts, class Comp>\n    struct merge_impl<list<R...>, list<Ts...>, list<>, Comp>\n    {\n        using type = list<R..., Ts...>;\n    };\n    template <class... R, class... Us, class Comp>\n    struct merge_impl<list<R...>, list<>, list<Us...>, Comp>\n    {\n        using type = list<R..., Us...>;\n    };\n    template <class... R, class Comp>\n    struct merge_impl<list<R...>, list<>, list<>, Comp>\n    {\n        using type = list<R...>;\n    };\n}\ntemplate <class Seq1, class Seq2, class Comp = less<_1, _2>>\nusing merge =\n    append<clear<Seq1>,\n           typename detail::merge_impl<list<>, wrap<Seq1, list>, wrap<Seq2, list>, Comp>::type>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class Ls, class Seq, typename Comp>\n    struct sort_impl;\n    template <class L, class Comp>\n    struct mini_sort;\n    template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7,\n              class T8, class... Ts, class Comp>\n    struct mini_sort<list<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>, Comp>\n        : merge_impl<list<>, typename mini_sort<list<T0, T1, T2, T3, T4, T5, T6, T7>, Comp>::type,\n                     typename mini_sort<list<T8, Ts...>, Comp>::type, Comp>\n    {\n    };\n    template <class T0, class T1, class T2, class T3, class T4, class... Ts, class Comp>\n    struct mini_sort<list<T0, T1, T2, T3, T4, Ts...>, Comp>\n        : merge_impl<list<>, typename mini_sort<list<T0, T1, T2, T3>, Comp>::type,\n                     typename mini_sort<list<T4, Ts...>, Comp>::type, Comp>\n    {\n    };\n    template <class T0, class T1, class T2, class T3, class Comp>\n    struct mini_sort<list<T0, T1, T2, T3>, Comp>\n        : merge_impl<list<>, typename mini_sort<list<T0, T1>, Comp>::type,\n                     typename mini_sort<list<T2, T3>, Comp>::type, Comp>\n    {\n    };\n    template <class T0, class T1, class T2, class Comp>\n    struct mini_sort<list<T0, T1, T2>, Comp>\n        : merge_impl<list<>, typename mini_sort<list<T0, T1>, Comp>::type, list<T2>, Comp>\n    {\n    };\n    template <class T0, class T1, class Comp>\n    struct mini_sort<list<T0, T1>, Comp>\n    {\n        using base = ::brigand::apply<Comp, T0, T1>;\n        using type = typename std::conditional<base::value, list<T0, T1>, list<T1, T0>>::type;\n    };\n    template <class T0, class Comp>\n    struct mini_sort<list<T0>, Comp>\n    {\n        using type = list<T0>;\n    };\n    template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7,\n              class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15,\n              class T16, class T17, class... Ts, typename Comp>\n    struct sort_impl<list<>, list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,\n                                  T15, T16, T17, Ts...>,\n                     Comp>\n        : sort_impl<list<typename mini_sort<list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,\n                                                 T12, T13, T14, T15, T16, T17>,\n                                            Comp>::type>,\n                    list<Ts...>, Comp>\n    {\n    };\n    template <class L0, class T0, class T1, class T2, class T3, class T4, class T5, class T6,\n              class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14,\n              class T15, class T16, class T17, class... Ts, typename Comp>\n    struct sort_impl<list<L0>, list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,\n                                    T15, T16, T17, Ts...>,\n                     Comp>\n        : sort_impl<list<L0, typename mini_sort<list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,\n                                                     T11, T12, T13, T14, T15, T16, T17>,\n                                                Comp>::type>,\n                    list<Ts...>, Comp>\n    {\n    };\n    template <class L0, class L1, class T0, class T1, class T2, class T3, class T4, class T5,\n              class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13,\n              class T14, class T15, class T16, class T17, class... Ts, typename Comp>\n    struct sort_impl<list<L0, L1>, list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,\n                                        T14, T15, T16, T17, Ts...>,\n                     Comp>\n        : sort_impl<list<L0, L1, typename mini_sort<list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9,\n                                                         T10, T11, T12, T13, T14, T15, T16, T17>,\n                                                    Comp>::type>,\n                    list<Ts...>, Comp>\n    {\n    };\n    template <class L0, class L1, class L2, class T0, class T1, class T2, class T3, class T4,\n              class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12,\n              class T13, class T14, class T15, class T16, class T17, class... Ts, typename Comp>\n    struct sort_impl<list<L0, L1, L2>, list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,\n                                            T13, T14, T15, T16, T17, Ts...>,\n                     Comp>\n        : sort_impl<list<merge<L0, L1, Comp>,\n                         merge<typename mini_sort<list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,\n                                                       T11, T12, T13, T14, T15, T16, T17>,\n                                                  Comp>::type,\n                               L2, Comp>>,\n                    list<Ts...>, Comp>\n    {\n    };\n    template <class T, class... Ts, typename Comp>\n    struct sort_impl<list<>, list<T, Ts...>, Comp>\n    {\n        using type = typename mini_sort<list<T, Ts...>, Comp>::type;\n    };\n    template <class L, class T, class... Ts, typename Comp>\n    struct sort_impl<list<L>, list<T, Ts...>, Comp>\n    {\n        using type = merge<typename mini_sort<list<T, Ts...>, Comp>::type, L, Comp>;\n    };\n    template <class L0, class L1, class T, class... Ts, typename Comp>\n    struct sort_impl<list<L0, L1>, list<T, Ts...>, Comp>\n    {\n        using type =\n            merge<L0, merge<typename mini_sort<list<T, Ts...>, Comp>::type, L1, Comp>, Comp>;\n    };\n    template <class L0, class L1, class L2, class T, class... Ts, typename Comp>\n    struct sort_impl<list<L0, L1, L2>, list<T, Ts...>, Comp>\n    {\n        using type = merge<merge<L0, L1, Comp>,\n                           merge<typename mini_sort<list<T, Ts...>, Comp>::type, L2, Comp>, Comp>;\n    };\n    template <class L, typename Comp>\n    struct sort_impl<list<L>, list<>, Comp>\n    {\n        using type = L;\n    };\n    template <class L0, class L1, typename Comp>\n    struct sort_impl<list<L0, L1>, list<>, Comp>\n    {\n        using type = merge<L0, L1, Comp>;\n    };\n    template <class L0, class L1, class L2, typename Comp>\n    struct sort_impl<list<L0, L1, L2>, list<>, Comp>\n    {\n        using type = merge<merge<L0, L1, Comp>, L2, Comp>;\n    };\n    template <typename Comp>\n    struct sort_impl<list<>, list<>, Comp>\n    {\n        using type = list<>;\n    };\n    template <\n        class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8,\n        class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16,\n        class T17, class T18, class T19, class T20, class T21, class T22, class T23, class T24,\n        class T25, class T26, class T27, class T28, class T29, class T30, class T31, class T32,\n        class T33, class T34, class T35, class T36, class T37, class T38, class T39, class T40,\n        class T41, class T42, class T43, class T44, class T45, class T46, class T47, class T48,\n        class T49, class T50, class T51, class T52, class T53, class T54, class T55, class T56,\n        class T57, class T58, class T59, class T60, class T61, class T62, class T63, class T64,\n        class T65, class T66, class T67, class T68, class T69, class T70, class T71, class T72,\n        class T73, class T74, class T75, class T76, class T77, class T78, class T79, class T80,\n        class T81, class T82, class T83, class T84, class T85, class T86, class T87, class T88,\n        class T89, class T90, class T91, class T92, class T93, class T94, class T95, class T96,\n        class T97, class T98, class T99, class T100, class T101, class T102, class T103, class T104,\n        class T105, class T106, class T107, class T108, class T109, class T110, class T111,\n        class T112, class T113, class T114, class T115, class T116, class T117, class T118,\n        class T119, class T120, class T121, class T122, class T123, class T124, class T125,\n        class T126, class T127, class T128, class T129, class T130, class T131, class T132,\n        class T133, class T134, class T135, class T136, class T137, class T138, class T139,\n        class T140, class T141, class T142, class T143, class T144, class T145, class T146,\n        class T147, class T148, class T149, class T150, class T151, class T152, class T153,\n        class T154, class T155, class T156, class T157, class T158, class T159, class T160,\n        class T161, class T162, class T163, class T164, class T165, class T166, class T167,\n        class T168, class T169, class T170, class T171, class T172, class T173, class T174,\n        class T175, class T176, class T177, class T178, class T179, class T180, class T181,\n        class T182, class T183, class T184, class T185, class T186, class T187, class T188,\n        class T189, class T190, class T191, class T192, class T193, class T194, class T195,\n        class T196, class T197, class T198, class T199, class T200, class T201, class T202,\n        class T203, class T204, class T205, class T206, class T207, class T208, class T209,\n        class T210, class T211, class T212, class T213, class T214, class T215, class T216,\n        class T217, class T218, class T219, class T220, class T221, class T222, class T223,\n        class T224, class T225, class T226, class T227, class T228, class T229, class T230,\n        class T231, class T232, class T233, class T234, class T235, class T236, class T237,\n        class T238, class T239, class T240, class T241, class T242, class T243, class T244,\n        class T245, class T246, class T247, class T248, class T249, class T250, class T251,\n        class T252, class T253, class T254, class T255, typename... Ts, typename Comp>\n    struct sort_impl<\n        list<>,\n        list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18,\n             T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31, T32, T33, T34, T35,\n             T36, T37, T38, T39, T40, T41, T42, T43, T44, T45, T46, T47, T48, T49, T50, T51, T52,\n             T53, T54, T55, T56, T57, T58, T59, T60, T61, T62, T63, T64, T65, T66, T67, T68, T69,\n             T70, T71, T72, T73, T74, T75, T76, T77, T78, T79, T80, T81, T82, T83, T84, T85, T86,\n             T87, T88, T89, T90, T91, T92, T93, T94, T95, T96, T97, T98, T99, T100, T101, T102,\n             T103, T104, T105, T106, T107, T108, T109, T110, T111, T112, T113, T114, T115, T116,\n             T117, T118, T119, T120, T121, T122, T123, T124, T125, T126, T127, T128, T129, T130,\n             T131, T132, T133, T134, T135, T136, T137, T138, T139, T140, T141, T142, T143, T144,\n             T145, T146, T147, T148, T149, T150, T151, T152, T153, T154, T155, T156, T157, T158,\n             T159, T160, T161, T162, T163, T164, T165, T166, T167, T168, T169, T170, T171, T172,\n             T173, T174, T175, T176, T177, T178, T179, T180, T181, T182, T183, T184, T185, T186,\n             T187, T188, T189, T190, T191, T192, T193, T194, T195, T196, T197, T198, T199, T200,\n             T201, T202, T203, T204, T205, T206, T207, T208, T209, T210, T211, T212, T213, T214,\n             T215, T216, T217, T218, T219, T220, T221, T222, T223, T224, T225, T226, T227, T228,\n             T229, T230, T231, T232, T233, T234, T235, T236, T237, T238, T239, T240, T241, T242,\n             T243, T244, T245, T246, T247, T248, T249, T250, T251, T252, T253, T254, T255, Ts...>,\n        Comp>\n    {\n        using type = merge<\n            typename sort_impl<\n                list<>,\n                list<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17,\n                     T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31, T32, T33,\n                     T34, T35, T36, T37, T38, T39, T40, T41, T42, T43, T44, T45, T46, T47, T48, T49,\n                     T50, T51, T52, T53, T54, T55, T56, T57, T58, T59, T60, T61, T62, T63, T64, T65,\n                     T66, T67, T68, T69, T70, T71, T72, T73, T74, T75, T76, T77, T78, T79, T80, T81,\n                     T82, T83, T84, T85, T86, T87, T88, T89, T90, T91, T92, T93, T94, T95, T96, T97,\n                     T98, T99, T100, T101, T102, T103, T104, T105, T106, T107, T108, T109, T110,\n                     T111, T112, T113, T114, T115, T116, T117, T118, T119, T120, T121, T122, T123,\n                     T124, T125, T126, T127, T128, T129, T130, T131, T132, T133, T134, T135, T136,\n                     T137, T138, T139, T140, T141, T142, T143, T144, T145, T146, T147, T148, T149,\n                     T150, T151, T152, T153, T154, T155, T156, T157, T158, T159, T160, T161, T162,\n                     T163, T164, T165, T166, T167, T168, T169, T170, T171, T172, T173, T174, T175,\n                     T176, T177, T178, T179, T180, T181, T182, T183, T184, T185, T186, T187, T188,\n                     T189, T190, T191, T192, T193, T194, T195, T196, T197, T198, T199, T200, T201,\n                     T202, T203, T204, T205, T206, T207, T208, T209, T210, T211, T212, T213, T214,\n                     T215, T216, T217, T218, T219, T220, T221, T222, T223, T224, T225, T226, T227,\n                     T228, T229, T230, T231, T232, T233, T234, T235, T236, T237, T238, T239, T240,\n                     T241, T242, T243, T244, T245, T246, T247, T248, T249, T250, T251, T252, T253,\n                     T254>,\n                Comp>::type,\n            typename sort_impl<list<>, list<T255, Ts...>, Comp>::type, Comp>;\n    };\n}\ntemplate <class Seq, class Comp = less<_1, _2>>\nusing sort = append<clear<Seq>, typename detail::sort_impl<list<>, wrap<Seq, list>, Comp>::type>;\n}\nnamespace brigand\n{\ntemplate <typename A>\nstruct complement\n    : brigand::integral_constant<typename A::value_type, typename A::value_type(~A::value)>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct divides\n    : brigand::integral_constant<typename std::decay<decltype(A::value / B::value)>::type,\n                                 A::value / B::value>\n{\n};\n}\nnamespace brigand\n{\n  template<class T>\n  struct identity\n  {\n    using type = T;\n  };\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct max : brigand::integral_constant<\n                 typename std::decay<decltype((A::value < B::value) ? B::value : A::value)>::type,\n                 (A::value < B::value) ? B::value : A::value>\n{};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct min : brigand::integral_constant<\n                 typename std::decay<decltype((A::value < B::value) ? A::value : B::value)>::type,\n                 (A::value < B::value) ? A::value : B::value>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct minus : brigand::integral_constant<typename std::decay<decltype(A::value - B::value)>::type,\n                                          A::value - B::value>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct modulo : brigand::integral_constant<typename std::decay<decltype(A::value % B::value)>::type,\n                                           A::value % B::value>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A>\nstruct negate : brigand::integral_constant<typename A::value_type, -A::value>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A>\nstruct next : brigand::integral_constant<typename A::value_type, A::value + 1>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct plus : brigand::integral_constant<typename std::decay<decltype(A::value + B::value)>::type,\n                                         A::value + B::value>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A>\nstruct prev : brigand::integral_constant<typename A::value_type, A::value - 1>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct times : brigand::integral_constant<typename std::decay<decltype(A::value * B::value)>::type,\n                                          A::value * B::value>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct bitand_ : brigand::integral_constant<typename A::value_type, A::value & B::value>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct bitor_ : brigand::integral_constant<typename A::value_type, A::value | B::value>\n{\n};\n}\nnamespace brigand\n{\ntemplate <typename A, typename B>\nstruct bitxor_ : brigand::integral_constant<typename A::value_type, A::value ^ B::value>\n{\n};\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct shift_left : brigand::integral_constant<typename A::value_type, (A::value << B::value)> {};\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct shift_right : brigand::integral_constant<typename A::value_type, (A::value >> B::value)> {};\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct equal_to : bool_ < (A::value == B::value) > {};\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct greater : bool_<(A::value > B::value) > {};\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct greater_equal : bool_ < (A::value >= B::value) > {};\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct less_equal : bool_ < (A::value <= B::value) > {};\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct not_equal_to : bool_ < (A::value != B::value) > {};\n}\n#include <type_traits>\nnamespace brigand\n{\n  template <typename Condition, typename A, typename B>\n  struct eval_if\n  {\n    using type = typename std::conditional<Condition::value, A, B>::type::type;\n  };\n  template <bool Condition, typename A, typename B>\n  struct eval_if_c\n  {\n    using type = typename std::conditional<Condition, A, B>::type::type;\n  };\n}\n#include <type_traits>\nnamespace brigand\n{\n  template <typename Condition, typename A, typename B>\n  struct if_ : std::conditional<Condition::value, A, B> {};\n  template <bool Condition, typename A, typename B>\n  struct if_c : std::conditional<Condition, A, B> {};\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <unsigned N>\n    struct and_impl;\n    template <>\n    struct and_impl<0>\n    {\n        template <typename T0=bool_<true>, typename T1 = bool_<true>, typename T2 = bool_<true>,\n                  typename T3=bool_<true>, typename T4 = bool_<true>, typename T5 = bool_<true>,\n                  typename T6=bool_<true>, typename T7 = bool_<true>, typename T8 = bool_<true>>\n        using f = bool_<T0::value && T1::value && T2::value && T3::value && T4::value &&\n                        T5::value && T6::value && T7::value && T8::value>;\n    };\n    template<>\n    struct and_impl<1>\n    {\n        template <typename T0=bool_<true>, typename T1 = bool_<true>, typename T2 = bool_<true>,\n                  typename T3=bool_<true>, typename T4 = bool_<true>, typename T5 = bool_<true>,\n                  typename T6=bool_<true>, typename T7 = bool_<true>, typename T8 = bool_<true>,\n                  typename T9=bool_<true>, typename T10 = bool_<true>, typename T11 = bool_<true>,\n                  typename T12=bool_<true>, typename T13 = bool_<true>, typename T14 = bool_<true>,\n                  typename T15=bool_<true>, typename T16 = bool_<true>, typename T17 = bool_<true>,\n                  typename T18=bool_<true>, typename T19 = bool_<true>, typename... Ts>\n        using f = bool_<T0::value && T1::value && T2::value && T3::value && T4::value &&\n                        T5::value && T6::value && T7::value && T8::value && T9::value &&\n                        T10::value && T11::value && T12::value && T13::value && T14::value &&\n                        T15::value && T16::value && T17::value && T18::value && T19::value &&\n                        and_impl<(sizeof...(Ts) / 9) != 0>::template f<Ts...>::value>;\n    };\n}\ntemplate<typename...Ts>\nstruct and_: detail::and_impl<(sizeof...(Ts) / 9) != 0>::template f<Ts...>\n{};\n}\nnamespace brigand\n{\n  template <typename T>\n  struct not_ : brigand::integral_constant<typename T::value_type, !T::value> {};\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <unsigned N>\n    struct or_impl;\n    template <>\n    struct or_impl<0>\n    {\n        template <typename T0=bool_<false>, typename T1 = bool_<false>, typename T2 = bool_<false>,\n                  typename T3=bool_<false>, typename T4 = bool_<false>, typename T5 = bool_<false>,\n                  typename T6=bool_<false>, typename T7 = bool_<false>, typename T8 = bool_<false>>\n        using f = bool_<T0::value || T1::value || T2::value || T3::value || T4::value ||\n                        T5::value || T6::value || T7::value || T8::value>;\n    };\n    template<>\n    struct or_impl<1>\n    {\n        template <typename T0=bool_<false>, typename T1 = bool_<false>, typename T2 = bool_<false>,\n                  typename T3=bool_<false>, typename T4 = bool_<false>, typename T5 = bool_<false>,\n                  typename T6=bool_<false>, typename T7 = bool_<false>, typename T8 = bool_<false>,\n                  typename T9=bool_<false>, typename T10 = bool_<false>, typename T11 = bool_<false>,\n                  typename T12=bool_<false>, typename T13 = bool_<false>, typename T14 = bool_<false>,\n                  typename T15=bool_<false>, typename T16 = bool_<false>, typename T17 = bool_<false>,\n                  typename T18=bool_<false>, typename T19 = bool_<false>, typename... Ts>\n        using f = bool_<T0::value || T1::value || T2::value || T3::value || T4::value ||\n                        T5::value || T6::value || T7::value || T8::value || T9::value ||\n                        T10::value || T11::value || T12::value || T13::value || T14::value ||\n                        T15::value || T16::value || T17::value || T18::value || T19::value ||\n                        or_impl<(sizeof...(Ts) / 9) != 0>::template f<Ts...>::value>;\n    };\n}\ntemplate<typename...Ts>\nstruct or_: detail::or_impl<(sizeof...(Ts) / 9) != 0>::template f<Ts...>\n{};\n}\nnamespace brigand\n{\n  template <typename A, typename B>\n  struct xor_ : brigand::integral_constant<typename A::value_type, A::value != B::value> {};\n}\nnamespace brigand\n{\n  template<class T>\n  struct always\n  {\n     using type = T; \n  };\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template<template<class> class F, unsigned N, class T>\n    struct repeat_impl\n    : repeat_impl<F, N-7, F<F<F<F<F<F<F<T>>>>>>>>\n    {};\n    template<template<class> class F, class T>\n    struct repeat_impl<F, 7, T>\n    {\n        using type = F<F<F<F<F<F<F<T>>>>>>>;\n    };\n    template<template<class> class F, class T>\n    struct repeat_impl<F, 6, T>\n    {\n        using type = F<F<F<F<F<F<T>>>>>>;\n    };\n    template<template<class> class F, class T>\n    struct repeat_impl<F, 5, T>\n    {\n        using type = F<F<F<F<F<T>>>>>;\n    };\n    template<template<class> class F, class T>\n    struct repeat_impl<F, 4, T>\n    {\n        using type = F<F<F<F<T>>>>;\n    };\n    template<template<class> class F, class T>\n    struct repeat_impl<F, 3, T>\n    {\n        using type = F<F<F<T>>>;\n    };\n    template<template<class> class F, class T>\n    struct repeat_impl<F, 2, T>\n    {\n        using type = F<F<T>>;\n    };\n    template<template<class> class F, class T>\n    struct repeat_impl<F, 1, T>\n    {\n        using type = F<T>;\n    };\n    template<template<class> class F, class T>\n    struct repeat_impl<F, 0, T>\n    {\n        using type = T;\n    };\n}\nnamespace lazy\n{\n    template<template<class> class F, class N, class T>\n    using repeat = typename detail::repeat_impl<F, N::value, T>;\n}\n    template<template<class> class F, class N, class T>\n    using repeat = typename ::brigand::lazy::repeat<F, N, T>::type;\n}\nnamespace brigand\n{\ntemplate <typename T>\nstruct sizeof_ : brigand::integral_constant<unsigned int, sizeof(T)>\n{\n};\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template<class C, class K>\n    struct has_key_impl\n    {\n        using type = decltype(C::has_key(type_<K>{}));\n    };\n}\n    template<class L, class K>\n    using has_key = typename detail::has_key_impl<L, K>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template<class Start, unsigned N, class Next, class... E>\n    struct mksq8\n    : mksq8<brigand::apply<Next, Start>, N-1, Next, E..., Start>\n    {};\n    template<class Start, class Next, class... E>\n    struct mksq8<Start, 0, Next, E...>\n    {\n        using type = list<E...>;\n    };\n    template<class Start, class Next, class... E>\n    struct mksq8<Start, 1, Next, E...>\n    {\n        using type = list<E..., Start>;\n    };\n    template<class Start, class Next>\n    struct mksq8<Start, 8, Next>\n    {\n        using t1 = brigand::apply<Next, Start>;\n        using t2 = brigand::apply<Next, t1>;\n        using t3 = brigand::apply<Next, t2>;\n        using t4 = brigand::apply<Next, t3>;\n        using t5 = brigand::apply<Next, t4>;\n        using t6 = brigand::apply<Next, t5>;\n        using t7 = brigand::apply<Next, t6>;\n        using type = list<Start, t1, t2, t3, t4, t5, t6, t7>;\n    };\n    template<template<class...> class List, class Start, unsigned N, class Next, bool, class... L>\n    struct make_sequence_impl\n    : make_sequence_impl<\n        List,\n        brigand::apply<Next, typename mksq8<Start, 8, Next>::t7>,\n        N-8,\n        Next,\n        (N-8<=8),\n        L...,\n        typename mksq8<Start, 8, Next>::type\n    >\n    {};\n    template<template<class...> class List, class Start, unsigned N, class Next, class... L>\n    struct make_sequence_impl<List, Start, N, Next, true, L...>\n    {\n        using type = append<List<>, L..., typename mksq8<Start, N, Next>::type>;\n    };\n}\n    template<class Start, unsigned N, class Next = next<_1>, template<class...> class List = list>\n    using make_sequence = typename detail::make_sequence_impl<List, Start, N, Next, (N<=8)>::type;\n}\nnamespace brigand\n{\ntemplate <class L, unsigned int Index>\nusing erase_c =\n    append<front<split_at<L, brigand::integral_constant<unsigned int, Index>>>,\n           pop_front<back<split_at<L, brigand::integral_constant<unsigned int, Index>>>>>;\nnamespace detail\n{\n    template <typename T>\n    struct has_erase_method\n    {\n        struct dummy\n        {\n        };\n        template <typename C, typename P>\n        static auto test(P * p) -> decltype(C::erase(type_<P>{}), brigand::true_type());\n        template <typename, typename>\n        static brigand::false_type test(...);\n        static const bool value =\n            std::is_same<brigand::true_type, decltype(test<T, dummy>(nullptr))>::value;\n    };\n    template <class L, class I, bool>\n    struct erase_dispatch\n    {\n        using type = erase_c<L, I::value>;\n    };\n    template <class C, class K>\n    struct erase_dispatch<C, K, true>\n    {\n        using type = decltype(C::erase(type_<K>{}));\n    };\n}\ntemplate <class L, class K>\nusing erase = typename detail::erase_dispatch<L, K, detail::has_erase_method<L>::value>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class C, class T>\n    struct insert_impl\n    {\n        using type = decltype(C::insert(type_<T>{}));\n    };\n}\n    template<class L, class T>\n    using insert = typename detail::insert_impl<L, T>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <class L, class K>\n    struct contains_impl\n    {\n        using type = decltype(L::contains(type_<K>{}));\n    };\n}\n    template <class L, class K>\n    using contains = typename detail::contains_impl<L, K>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template<class... Ts>\n    struct make_set;\n    template<class U, class K>\n    struct set_erase_pred_impl\n    {\n        using type = list<U>;\n    };\n    template<class K>\n    struct set_erase_pred_impl<K,K>\n    {\n        using type = list<>;\n    };\n    template <class... T>\n    struct set_impl\n    {\n        template <typename K, typename = decltype(static_cast<type_<K>*>(static_cast<make_set<T...>*>(nullptr)))>\n        static brigand::true_type contains(type_<K>);\n        template <typename K>\n        static brigand::false_type contains(K);\n        template <typename K, typename = decltype(static_cast<type_<K>*>(static_cast<make_set<T...>*>(nullptr)))>\n        static brigand::true_type has_key(type_<K>);\n        template <typename K>\n        static brigand::false_type has_key(K);\n        template <class K>\n        static append<set_impl<>, typename set_erase_pred_impl<T, K>::type...> erase(type_<K>);\n        template<class K, class = decltype(static_cast<type_<K>*>(static_cast<make_set<T...>*>(nullptr)))>\n        static set_impl insert(type_<K>);\n        template<class K>\n        static set_impl<T..., typename K::type> insert(K);\n    };\n    template<class... Ts>\n    struct make_set : type_<Ts>...\n    {\n      using type = set_impl<Ts...>;\n    };\n}\n    template <typename... T>\n    using set_wrapper = typename detail::make_set<T...>::type;\n    template <typename L>\n    using as_set = wrap<L, set_wrapper>;\n    template<class... Ts>\n    using set = typename detail::make_set<Ts...>::type;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <typename Pair>\n  struct get_second {\n    using type = typename Pair::second_type;\n  };\n}\ntemplate <typename Map, template <class...> class Sequence = brigand::list>\nusing values_as_sequence = transform<as_sequence<Map, Sequence>, detail::get_second<_1>>;\n}\nnamespace brigand\n{\nnamespace detail\n{\n    template <typename Pair>\n  struct get_first {\n    using type = typename Pair::first_type;\n  };\n}\ntemplate <typename Map, template <class...> class Sequence = brigand::set>\nusing keys_as_sequence = transform<as_sequence<Map, Sequence>, detail::get_first<_1>>;\n}\nnamespace brigand\n{\n  struct empty_base {};\n}\nnamespace brigand\n{\n  template<typename T, typename R = void > struct has_type\n  {\n    using type = R;\n  };\n}\nnamespace brigand\n{\n  template<typename... Ts> struct inherit;\n  template<typename T> struct inherit<T>\n  {\n    struct type : public T {};\n  };\n  template<> struct inherit<>\n  {\n    using type = empty_base;\n  };\n  template<> struct inherit<empty_base>\n  {\n    using type = empty_base;\n  };\n  template<typename T1, typename T2> struct inherit<T1,T2>\n  {\n    struct type : public T1, T2 {};\n  };\n  template<typename T1> struct inherit<T1,empty_base>\n  {\n    using type = T1;\n  };\n  template<typename T2> struct inherit<empty_base,T2>\n  {\n    using type = T2;\n  };\n  template<> struct inherit<empty_base,empty_base>\n  {\n    using type = empty_base;\n  };\n  template<typename T1, typename T2, typename T3, typename... Ts>\n  struct  inherit<T1, T2, T3, Ts...>\n        : inherit<T1, typename inherit<T2,typename inherit<T3, Ts...>::type>::type>\n  {};\n}\nnamespace brigand\n{\n  namespace lazy\n  {\n    template< typename Types\n      , typename Node\n      , typename Root = brigand::empty_base\n    >\n      struct inherit_linearly;\n  template< typename Types\n      , template<typename...> class Node, typename...Ts\n      , typename Root\n    >\n  struct inherit_linearly<Types,Node<Ts...>,Root>\n    {\n      using type = brigand::fold<Types,Root,bind<Node,Ts...>>;\n    };\n  }\n  template< typename Types\n            , typename Node\n            , typename Root = brigand::empty_base\n            >\n  using inherit_linearly = typename lazy::inherit_linearly<Types,Node,Root>::type;\n}\n#include <cstdint>\n#include <cstring>\nnamespace brigand\n{\n  template<typename RealType, typename Type, Type Value>\n  struct real_ : brigand::integral_constant<Type,Value>\n  {\n    using value_type  = RealType;\n    using parent      = brigand::integral_constant<Type,Value>;\n    inline operator value_type() const\n    {\n      value_type that;\n      std::memcpy(&that, &parent::value, sizeof(value_type));\n      return that;\n    }\n  };\n  template<std::uint32_t Value>\n  struct single_ : real_<float, std::uint32_t, Value> {};\n  template<std::uint64_t Value>\n  struct double_ : real_<double, std::uint64_t,Value> {};\n}\n#endif\n"
  },
  {
    "path": "external/libsharp/.gitignore",
    "content": "*.o\n*.so\n#*\n*~\n*.pyc\n*.pyo\n\n/auto\n/autom4te.cache\n/config.log\n/config.status\n/config/config.auto\n/configure\n/sharp_oracle.inc\n\n/python/libsharp/libsharp.c\n/python/libsharp/libsharp_mpi.c\n"
  },
  {
    "path": "external/libsharp/COPYING",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "external/libsharp/Makefile",
    "content": "SHARP_TARGET?=auto\nifndef SHARP_TARGET\n  SHARP_TARGET:=$(error SHARP_TARGET undefined. Please see README.compilation for help)UNDEFINED\nendif\n\ndefault: compile_all\nSRCROOT:=$(shell pwd)\ninclude $(SRCROOT)/config/config.$(SHARP_TARGET)\ninclude $(SRCROOT)/config/rules.common\n\nall_hdr:=\nall_lib:=\nall_cbin:=\n\nFULL_INCLUDE:=\n\ninclude c_utils/planck.make\ninclude libfftpack/planck.make\ninclude libsharp/planck.make\ninclude docsrc/planck.make\n\nCYTHON_MODULES=python/libsharp/libsharp.so $(if $(MPI_CFLAGS), python/libsharp/libsharp_mpi.so)\n\n$(all_lib): %: | $(LIBDIR)_mkdir\n\t@echo \"#  creating library $*\"\n\t$(ARCREATE) $@ $^\n\n$(all_cbin): %: | $(BINDIR)_mkdir\n\t@echo \"#  linking C binary $*\"\n\t$(CL) -o $@ $^ $(CLFLAGS)\n\ncompile_all: $(all_cbin) hdrcopy\n\nhdrclean:\n\t@if [ -d $(INCDIR) ]; then rm -rf $(INCDIR)/* ; fi\n\nhdrcopy: | $(INCDIR)_mkdir\n\t@if [ \"$(all_hdr)\" ]; then cp -p $(all_hdr) $(INCDIR); fi\n\n$(notdir $(all_cbin)) : % : $(BINDIR)/%\n\ntest: compile_all\n\t$(BINDIR)/sharp_testsuite acctest && \\\n\t$(BINDIR)/sharp_testsuite test healpix 2048 -1 1024 -1 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test fejer1 2047 -1 -1 4096 2 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 2047 -1 -1 4096 0 2\n\nperftest: compile_all\n\t$(BINDIR)/sharp_testsuite test healpix 2048 -1 1024 -1 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 63 -1 -1 128 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 127 -1 -1 256 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 255 -1 -1 512 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 511 -1 -1 1024 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 1023 -1 -1 2048 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 2047 -1 -1 4096 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 4095 -1 -1 8192 0 1 && \\\n\t$(BINDIR)/sharp_testsuite test gauss 8191 -1 -1 16384 0 1\n\n%.c: %.c.in\n# Only do this if the md5sum changed, in order to avoid Python and Jinja\n# dependency when not modifying the c.in file\n\tgrep `md5sum $< | cut -d ' ' -f 1` $@ || ./runjinja.py < $< > $@\n\ngenclean:\n\trm libsharp/sharp_legendre.c || exit 0\n\n$(CYTHON_MODULES): %.so: %.pyx\nifndef PIC_CFLAGS\n\t$(error Python extension must be built using the --enable-pic configure option.)\nendif\n\tcython $<\n\t$(CC) $(DEBUG_CFLAGS) $(OPENMP_CFLAGS) $(PIC_CFLAGS) `python-config --cflags` -I$(INCDIR) -o $(<:.pyx=.o) -c $(<:.pyx=.c)\n\t$(CL) -shared $(<:.pyx=.o) $(OPENMP_CFLAGS) $(CYTHON_OBJ) -L$(LIBDIR) -lsharp -lfftpack -lc_utils -L`python-config --prefix`/lib `python-config --ldflags` -o $@\n\npython: $(all_lib) hdrcopy $(CYTHON_MODULES)\n\n# the following test files are automatic; the sht wrapper test\n# must be run manually and requires MPI at the moment..\npytest: python\n\tcd python && nosetests --nocapture libsharp/tests/test_legendre_table.py libsharp/tests/test_legendre.py\n"
  },
  {
    "path": "external/libsharp/README.md",
    "content": "# Development has moved\n\nThis repository has been archived and is only kept so that packages depending on\nit have a cacnonical place to download the last version.\n\nFor new projects using spherical harmonic transforms we recommend to use\n- `libsharp2` (https://gitlab.mpcdf.mpg.de/mtr/libsharp) if you need MPI\n  functionality, or\n- the `sht` component of https://gitlab.mpcdf.mpg.de/mtr/ducc if your project\n  is written in modern C++ or Python, and you don't need MPI support within\n  the transforms.\n\nBoth of the above libraries are successors to `libsharp` which are\nsignificantly faster than the last `libsharp` version available here. Since the\nswitch to the new algorithms required slight changes to the API, we decided to\ndevelop them in separate repositories and keep the original `libsharp`\nrepository unchanged.\n\n\n# Libsharp\n\n*IMPORTANT NOTE*: It appears that the default branch upon cloning from\ngithub.com/dagss/libsharp was an outdated 'dagss' branch instead of\nthe 'master' branch. To get the latest copy,\nplease do `git checkout master; git pull`. New clones are no longer affected.\n\n## Paper\n\nhttps://arxiv.org/abs/1303.4945\n\n## Compilation\n\nGNU make is required for compilation.\n\nLibsharp compilation has been successfully tested with GNU and Intel compilers.\nWhen using gcc, version 4.x is required [1].\nSince libsharp was written in standard C99, other compilers should work fine,\nbut SSE2/AVX support will most likely be deactivated.\n\nIf you obtained libsharp directly from the git repository, you will also\nneed a copy of the GNU autotools. In this case, run \"autoconf\" in libsharp's\nmain directory before any other steps.\nFor libsharp releases distributed as a .tar.gz file, this step is not necessary.\n\nAfterwards, simply run \"./configure\"; if this fails, please refer to the output\nof \"./configure --help\" for additional hints and, if necessary, provide\nadditional flags to the configure script.\nOnce the script finishes successfully, run \"make\"\n(or \"gmake\"). This should install the compilation products in the\nsubdirectory \"auto/\".\n\nDocumentation can be created by the command \"(g)make doc\".\nHowever this requires the doxygen application to be installed\non your system.\nThe documentation will be created in the subdirectory doc/.\n\n\n[1] Some versions of the gcc 4.4.x release series contain a bug which causes\nthe compiler to crash during libsharp compilation. This appears to be fixed\nin the gcc 4.4.7 release. It is possible to work around this problem by adding\nthe compiler flag \"-fno-tree-fre\" after the other optimization flags - the\nconfigure script should do this automatically.\n"
  },
  {
    "path": "external/libsharp/c_utils/c_utils.c",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n *  Convenience functions\n *\n *  Copyright (C) 2008, 2009, 2010, 2011, 2012 Max-Planck-Society\n *  Author: Martin Reinecke\n */\n\n#include <stdio.h>\n#include \"c_utils.h\"\n\nvoid util_fail_ (const char *file, int line, const char *func, const char *msg)\n  {\n  fprintf(stderr,\"%s, %i (%s):\\n%s\\n\",file,line,func,msg);\n  exit(1);\n  }\nvoid util_warn_ (const char *file, int line, const char *func, const char *msg)\n  {\n  fprintf(stderr,\"%s, %i (%s):\\n%s\\n\",file,line,func,msg);\n  }\n\n/* This function tries to avoid allocations with a total size close to a high\n   power of two (called the \"critical stride\" here), by adding a few more bytes\n   if necssary. This lowers the probability that two arrays differ by a multiple\n   of the critical stride in their starting address, which in turn lowers the\n   risk of cache line contention. */\nstatic size_t manipsize(size_t sz)\n  {\n  const size_t critical_stride=4096, cacheline=64, overhead=32;\n  if (sz < (critical_stride/2)) return sz;\n  if (((sz+overhead)%critical_stride)>(2*cacheline)) return sz;\n  return sz+2*cacheline;\n  }\n\n#ifdef __SSE__\n#include <xmmintrin.h>\nvoid *util_malloc_ (size_t sz)\n  {\n  void *res;\n  if (sz==0) return NULL;\n  res = _mm_malloc(manipsize(sz),16);\n  UTIL_ASSERT(res,\"_mm_malloc() failed\");\n  return res;\n  }\nvoid util_free_ (void *ptr)\n  { if ((ptr)!=NULL) _mm_free(ptr); }\n#else\nvoid *util_malloc_ (size_t sz)\n  {\n  void *res;\n  if (sz==0) return NULL;\n  res = malloc(manipsize(sz));\n  UTIL_ASSERT(res,\"malloc() failed\");\n  return res;\n  }\nvoid util_free_ (void *ptr)\n  { if ((ptr)!=NULL) free(ptr); }\n#endif\n"
  },
  {
    "path": "external/libsharp/c_utils/c_utils.h",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file c_utils.h\n *  Convenience functions\n *\n *  Copyright (C) 2008, 2009, 2010, 2011 Max-Planck-Society\n *  \\author Martin Reinecke\n *  \\note This file should only be included from .c files, NOT from .h files.\n */\n\n#ifndef PLANCK_C_UTILS_H\n#define PLANCK_C_UTILS_H\n\n#include <math.h>\n#include <stdlib.h>\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid util_fail_ (const char *file, int line, const char *func, const char *msg);\nvoid util_warn_ (const char *file, int line, const char *func, const char *msg);\nvoid *util_malloc_ (size_t sz);\nvoid util_free_ (void *ptr);\n\n#if defined (__GNUC__)\n#define UTIL_FUNC_NAME__ __func__\n#else\n#define UTIL_FUNC_NAME__ \"unknown\"\n#endif\n\n/*! \\def UTIL_ASSERT(cond,msg)\n    If \\a cond is false, print an error message containing function name,\n    source file name and line number of the call, as well as \\a msg;\n    then exit the program with an error status. */\n#define UTIL_ASSERT(cond,msg) \\\n  if(!(cond)) util_fail_(__FILE__,__LINE__,UTIL_FUNC_NAME__,msg)\n/*! \\def UTIL_WARN(cond,msg)\n    If \\a cond is false, print an warning containing function name,\n    source file name and line number of the call, as well as \\a msg. */\n#define UTIL_WARN(cond,msg) \\\n  if(!(cond)) util_warn_(__FILE__,__LINE__,UTIL_FUNC_NAME__,msg)\n/*! \\def UTIL_FAIL(msg)\n    Print an error message containing function name,\n    source file name and line number of the call, as well as \\a msg;\n    then exit the program with an error status. */\n#define UTIL_FAIL(msg) \\\n  util_fail_(__FILE__,__LINE__,UTIL_FUNC_NAME__,msg)\n\n/*! \\def ALLOC(ptr,type,num)\n    Allocate space for \\a num objects of type \\a type. Make sure that the\n    allocation succeeded, else stop the program with an error. Return the\n    resulting pointer in \\a ptr. */\n#define ALLOC(ptr,type,num) \\\n  do { (ptr)=(type *)util_malloc_((num)*sizeof(type)); } while (0)\n/*! \\def RALLOC(type,num)\n    Allocate space for \\a num objects of type \\a type. Make sure that the\n    allocation succeeded, else stop the program with an error. Cast the\n    resulting pointer to \\a (type*). */\n#define RALLOC(type,num) \\\n  ((type *)util_malloc_((num)*sizeof(type)))\n/*! \\def DEALLOC(ptr)\n    Deallocate \\a ptr. It must have been allocated using \\a ALLOC or\n    \\a RALLOC. */\n#define DEALLOC(ptr) \\\n  do { util_free_(ptr); (ptr)=NULL; } while(0)\n#define RESIZE(ptr,type,num) \\\n  do { util_free_(ptr); ALLOC(ptr,type,num); } while(0)\n#define GROW(ptr,type,sz_old,sz_new) \\\n  do { \\\n    if ((sz_new)>(sz_old)) \\\n      { RESIZE(ptr,type,2*(sz_new));sz_old=2*(sz_new); } \\\n  } while(0)\n/*! \\def SET_ARRAY(ptr,i1,i2,val)\n    Set the entries \\a ptr[i1] ... \\a ptr[i2-1] to \\a val. */\n#define SET_ARRAY(ptr,i1,i2,val) \\\n  do { \\\n    ptrdiff_t cnt_; \\\n    for (cnt_=(i1);cnt_<(i2);++cnt_) (ptr)[cnt_]=(val); \\\n    } while(0)\n/*! \\def COPY_ARRAY(src,dest,i1,i2)\n    Copy the entries \\a src[i1] ... \\a src[i2-1] to\n    \\a dest[i1] ... \\a dest[i2-1]. */\n#define COPY_ARRAY(src,dest,i1,i2) \\\n  do { \\\n    ptrdiff_t cnt_; \\\n    for (cnt_=(i1);cnt_<(i2);++cnt_) (dest)[cnt_]=(src)[cnt_]; \\\n    } while(0)\n\n#define ALLOC2D(ptr,type,num1,num2) \\\n  do { \\\n    size_t cnt_, num1_=(num1), num2_=(num2); \\\n    ALLOC((ptr),type *,num1_); \\\n    ALLOC((ptr)[0],type,num1_*num2_); \\\n    for (cnt_=1; cnt_<num1_; ++cnt_) \\\n      (ptr)[cnt_]=(ptr)[cnt_-1]+num2_; \\\n    } while(0)\n#define DEALLOC2D(ptr) \\\n  do { if(ptr) DEALLOC((ptr)[0]); DEALLOC(ptr); } while(0)\n\n#define FAPPROX(a,b,eps) \\\n  (fabs((a)-(b))<((eps)*fabs(b)))\n#define ABSAPPROX(a,b,eps) \\\n  (fabs((a)-(b))<(eps))\n#define IMAX(a,b) \\\n  (((a)>(b)) ? (a) : (b))\n#define IMIN(a,b) \\\n  (((a)<(b)) ? (a) : (b))\n\n#define SWAP(a,b,type) \\\n  do { type tmp_=(a); (a)=(b); (b)=tmp_; } while(0)\n\n#define CHECK_STACK_ALIGN(align) \\\n  do { \\\n    double foo; \\\n    UTIL_WARN((((size_t)(&foo))&(align-1))==0, \\\n      \"WARNING: stack not sufficiently aligned!\"); \\\n    } while(0)\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/c_utils/memusage.c",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n *  Functionality for measuring memory consumption\n *\n *  Copyright (C) 2012 Max-Planck-Society\n *  Author: Martin Reinecke\n */\n\n#include <stdio.h>\n#include <string.h>\n#include \"memusage.h\"\n\ndouble residentSetSize(void)\n  {\n  FILE *statm = fopen(\"/proc/self/statm\",\"r\");\n  double res;\n  if (!statm) return -1.0;\n  if (fscanf(statm,\"%*f %lf\",&res))\n      { fclose(statm); return -1.0; }\n  fclose(statm);\n  return (res*4096);\n  }\n\ndouble VmHWM(void)\n  {\n  char word[1024];\n  FILE *f = fopen(\"/proc/self/status\", \"r\");\n  double res;\n  if (!f) return -1.0;\n  while(1)\n    {\n    if (fscanf (f,\"%1023s\",word)<0)\n      { fclose(f); return -1.0; }\n    if (!strncmp(word, \"VmHWM:\", 6))\n      {\n      if (fscanf(f,\"%lf%2s\",&res,word)<0)\n\t{ fclose(f); return -1.0; }\n      if (strncmp(word, \"kB\", 2))\n        { fclose(f); return -1.0; }\n      res *=1024;\n      fclose(f);\n      return res;\n      }\n    }\n  }\n"
  },
  {
    "path": "external/libsharp/c_utils/memusage.h",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file memusage.h\n *  Functionality for measuring memory consumption\n *\n *  Copyright (C) 2012 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_MEMUSAGE_H\n#define PLANCK_MEMUSAGE_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*! Returns the current resident set size in bytes.\n    \\note Currently only supported on Linux. Returns -1 if unsupported. */\ndouble residentSetSize(void);\n\n/*! Returns the high water mark of the resident set size in bytes.\n    \\note Currently only supported on Linux. Returns -1 if unsupported. */\ndouble VmHWM(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/c_utils/planck.make",
    "content": "PKG:=c_utils\n\nSD:=$(SRCROOT)/$(PKG)\nOD:=$(BLDROOT)/$(PKG)\n\nFULL_INCLUDE+= -I$(SD)\n\nHDR_$(PKG):=$(SD)/*.h\nLIB_$(PKG):=$(LIBDIR)/libc_utils.a\n\nOBJ:=c_utils.o walltime_c.o memusage.o\nOBJ:=$(OBJ:%=$(OD)/%)\n\n$(OBJ): $(HDR_$(PKG)) | $(OD)_mkdir\n$(LIB_$(PKG)): $(OBJ)\n\nall_hdr+=$(HDR_$(PKG))\nall_lib+=$(LIB_$(PKG))\n"
  },
  {
    "path": "external/libsharp/c_utils/walltime_c.c",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n *  Functionality for reading wall clock time\n *\n *  Copyright (C) 2010, 2011 Max-Planck-Society\n *  Author: Martin Reinecke\n */\n\n#if defined (_OPENMP)\n#include <omp.h>\n#elif defined (USE_MPI)\n#include \"mpi.h\"\n#else\n#include <sys/time.h>\n#include <stdlib.h>\n#endif\n\n#include \"walltime_c.h\"\n\ndouble wallTime(void)\n  {\n#if defined (_OPENMP)\n  return omp_get_wtime();\n#elif defined (USE_MPI)\n  return MPI_Wtime();\n#else\n  struct timeval t;\n  gettimeofday(&t, NULL);\n  return t.tv_sec + 1e-6*t.tv_usec;\n#endif\n  }\n"
  },
  {
    "path": "external/libsharp/c_utils/walltime_c.h",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file walltime_c.h\n *  Functionality for reading wall clock time\n *\n *  Copyright (C) 2010 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_WALLTIME_C_H\n#define PLANCK_WALLTIME_C_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*! Returns an approximation of the current wall time (in seconds).\n    The first available of the following timers will be used:\n    <ul>\n    <li> \\a omp_get_wtime(), if OpenMP is available\n    <li> \\a MPI_Wtime(), if MPI is available\n    <li> \\a gettimeofday() otherwise\n    </ul>\n    \\note Only useful for measuring time differences.\n    \\note This function has an execution time between 10 and 100 nanoseconds. */\ndouble wallTime(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/config/config.auto.in",
    "content": "@SILENT_RULE@\n\nCC=@CC@\nCL=@CC@\nCCFLAGS_NO_C=@CCFLAGS_NO_C@\nCCFLAGS=$(CCFLAGS_NO_C) -c\nCLFLAGS=-L. -L$(LIBDIR) @LDCCFLAGS@ -lm\nDEBUG_CFLAGS=@DEBUG_CFLAGS@\nMPI_CFLAGS=@MPI_CFLAGS@\nOPENMP_CFLAGS=@OPENMP_CFLAGS@\nPIC_CFLAGS=@PIC_CFLAGS@\nARCREATE=@ARCREATE@\n"
  },
  {
    "path": "external/libsharp/config/rules.common",
    "content": "BLDROOT   = $(SRCROOT)/build.$(SHARP_TARGET)\nPREFIX    = $(SRCROOT)/$(SHARP_TARGET)\nBINDIR    = $(PREFIX)/bin\nINCDIR    = $(PREFIX)/include\nLIBDIR    = $(PREFIX)/lib\nDOCDIR    = $(SRCROOT)/doc\nPYTHONDIR = $(SRCROOT)/python/libsharp\n\n# do not use any suffix rules\n.SUFFIXES:\n# do not use any default rules\n.DEFAULT:\n\necho_config:\n\t@echo using configuration \\'$(SHARP_TARGET)\\'\n\n$(BLDROOT)/%.o : $(SRCROOT)/%.c | echo_config\n\t@echo \"#  compiling $*.c\"\n\tcd $(@D) && $(CC) $(FULL_INCLUDE) -I$(BLDROOT) $(CCFLAGS) $<\n\n$(BLDROOT)/%.o : $(SRCROOT)/%.cc | echo_config\n\t@echo \"#  compiling $*.cc\"\n\tcd $(@D) && $(CXX) $(FULL_INCLUDE) -I$(BLDROOT) $(CXXCFLAGS) $<\n\n%_mkdir:\n\t@if [ ! -d $* ]; then mkdir -p $* ; fi\n\nclean:\n\trm -rf $(BLDROOT) $(PREFIX) $(DOCDIR) autom4te.cache/ config.log config.status\n\trm -rf $(PYTHONDIR)/*.c $(PYTHONDIR)/*.o $(PYTHONDIR)/*.so\n\ndistclean: clean\n\trm -f config/config.auto\n"
  },
  {
    "path": "external/libsharp/configure.ac",
    "content": "AC_INIT(config/config.auto.in)\n\nAC_CHECK_PROG([uname_found],[uname],[1],[0])\nif test $uname_found -eq 0 ; then\n    echo \"No uname found; setting system type to unknown.\"\n    system=\"unknown\"\nelse\n    system=`uname -s`-`uname -r`\nfi\nAC_LANG([C])\n\nAC_TRY_COMPILE([], [@%:@ifndef __INTEL_COMPILER\nchoke me\n@%:@endif], [ICC=[yes]], [ICC=[no]])\n\nif test $ICC = yes; then GCC=no; fi\nCCTYPE=unknown\nif test $GCC = yes; then CCTYPE=gcc; fi\nif test $ICC = yes; then CCTYPE=icc; fi\nAC_OPENMP\n\nSILENT_RULE=\".SILENT:\"\nAC_ARG_ENABLE(noisy-make,\n  [  --enable-noisy-make     enable detailed make output],\n  [if test \"$enableval\" = yes; then\n     SILENT_RULE=\"\"\n   fi])\n\nENABLE_MPI=no\nAC_ARG_ENABLE(mpi,\n  [  --enable-mpi            enable generation of MPI-parallel code],\n  [if test \"$enableval\" = yes; then\n     ENABLE_MPI=yes\n   fi])\n\nENABLE_DEBUG=no\nAC_ARG_ENABLE(debug,\n  [  --enable-debug          enable generation of debugging symbols],\n  [if test \"$enableval\" = yes; then\n     ENABLE_DEBUG=yes\n   fi])\n\nENABLE_PIC=no\nAC_ARG_ENABLE(pic,\n  [  --enable-pic            enable generation of position independent code],\n  [if test \"$enableval\" = yes; then\n     ENABLE_PIC=yes\n   fi])\n\ncase $CCTYPE in\n  gcc)\n    CCFLAGS=\"-O3 -fno-tree-vectorize -ffast-math -fomit-frame-pointer -std=c99 -pedantic -Wextra -Wall -Wno-unknown-pragmas -Wshadow -Wmissing-prototypes -Wfatal-errors -march=native\"\n    GCCVERSION=\"`$CC -dumpversion 2>&1`\"\n    echo \"Using gcc version $GCCVERSION\"\n    AC_SUBST(GCCVERSION)\n    changequote(,)\n    gcc43=`echo $GCCVERSION | grep -c '^4\\.[3456789]'`\n    gcc44=`echo $GCCVERSION | grep -c '^4\\.4'`\n    changequote([,])\n    if test $gcc44 -gt 0; then\n      CCFLAGS=\"$CCFLAGS -fno-tree-fre\"\n    fi\n    ;;\n  icc)\n    CCFLAGS=\"-O3 -xHOST -std=c99 -ip -Wbrief -Wall -vec-report0 -openmp-report0 -wd383,981,1419,1572\"\n    ;;\n  *)\n    CCFLAGS=\"-O2\"\n    # Don't do anything now\n    ;;\nesac\n\ncase $system in\n  Darwin-*)\n    ARCREATE=\"libtool -static -o\"\n    ;;\n  *)\n    ARCREATE=\"ar cr\"\n    ;;\nesac\n\nif test $ENABLE_DEBUG = yes; then\n  DEBUG_CFLAGS=\"-g\"\nfi\n\nif test $ENABLE_PIC = yes; then\n  PIC_CFLAGS=\"-fPIC\"\nfi\n\nif test $ENABLE_MPI = yes; then\n  MPI_CFLAGS=\"-DUSE_MPI\"\nfi\n\nCCFLAGS=\"$CCFLAGS $DEBUG_CFLAGS $OPENMP_CFLAGS $PIC_CFLAGS $MPI_CFLAGS\"\n\nCCFLAGS_NO_C=\"$CCFLAGS $CPPFLAGS\"\n\nLDCCFLAGS=\"$LDFLAGS $CCFLAGS\"\n\nAC_SUBST(SILENT_RULE)\nAC_SUBST(CC)\nAC_SUBST(CCFLAGS_NO_C)\nAC_SUBST(LDCCFLAGS)\nAC_SUBST(DEBUG_CFLAGS)\nAC_SUBST(MPI_CFLAGS)\nAC_SUBST(OPENMP_CFLAGS)\nAC_SUBST(PIC_CFLAGS)\nAC_SUBST(ARCREATE)\n\nAC_OUTPUT(config/config.auto)\n"
  },
  {
    "path": "external/libsharp/docsrc/c_utils.dox",
    "content": "# Doxyfile 1.8.1\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\nDOXYFILE_ENCODING      = UTF-8\nPROJECT_NAME           = \"LevelS C support library\"\nPROJECT_NUMBER         = 0.1\nPROJECT_BRIEF          =\nPROJECT_LOGO           =\nOUTPUT_DIRECTORY       = .\nCREATE_SUBDIRS         = NO\nOUTPUT_LANGUAGE        = English\nBRIEF_MEMBER_DESC      = NO\nREPEAT_BRIEF           = YES\nABBREVIATE_BRIEF       =\nALWAYS_DETAILED_SEC    = NO\nINLINE_INHERITED_MEMB  = NO\nFULL_PATH_NAMES        = NO\nSTRIP_FROM_PATH        =\nSTRIP_FROM_INC_PATH    =\nSHORT_NAMES            = NO\nJAVADOC_AUTOBRIEF      = NO\nQT_AUTOBRIEF           = NO\nMULTILINE_CPP_IS_BRIEF = NO\nINHERIT_DOCS           = YES\nSEPARATE_MEMBER_PAGES  = NO\nTAB_SIZE               = 8\nALIASES                =\nTCL_SUBST              =\nOPTIMIZE_OUTPUT_FOR_C  = YES\nOPTIMIZE_OUTPUT_JAVA   = NO\nOPTIMIZE_FOR_FORTRAN   = NO\nOPTIMIZE_OUTPUT_VHDL   = NO\nEXTENSION_MAPPING      =\nMARKDOWN_SUPPORT       = YES\nBUILTIN_STL_SUPPORT    = NO\nCPP_CLI_SUPPORT        = NO\nSIP_SUPPORT            = NO\nIDL_PROPERTY_SUPPORT   = YES\nDISTRIBUTE_GROUP_DOC   = NO\nSUBGROUPING            = YES\nINLINE_GROUPED_CLASSES = NO\nINLINE_SIMPLE_STRUCTS  = NO\nTYPEDEF_HIDES_STRUCT   = NO\nSYMBOL_CACHE_SIZE      = 0\nLOOKUP_CACHE_SIZE      = 0\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\nEXTRACT_ALL            = NO\nEXTRACT_PRIVATE        = NO\nEXTRACT_PACKAGE        = NO\nEXTRACT_STATIC         = NO\nEXTRACT_LOCAL_CLASSES  = YES\nEXTRACT_LOCAL_METHODS  = NO\nEXTRACT_ANON_NSPACES   = NO\nHIDE_UNDOC_MEMBERS     = YES\nHIDE_UNDOC_CLASSES     = YES\nHIDE_FRIEND_COMPOUNDS  = YES\nHIDE_IN_BODY_DOCS      = NO\nINTERNAL_DOCS          = NO\nCASE_SENSE_NAMES       = YES\nHIDE_SCOPE_NAMES       = NO\nSHOW_INCLUDE_FILES     = YES\nFORCE_LOCAL_INCLUDES   = NO\nINLINE_INFO            = YES\nSORT_MEMBER_DOCS       = NO\nSORT_BRIEF_DOCS        = NO\nSORT_MEMBERS_CTORS_1ST = NO\nSORT_GROUP_NAMES       = NO\nSORT_BY_SCOPE_NAME     = NO\nSTRICT_PROTO_MATCHING  = NO\nGENERATE_TODOLIST      = YES\nGENERATE_TESTLIST      = YES\nGENERATE_BUGLIST       = YES\nGENERATE_DEPRECATEDLIST= YES\nENABLED_SECTIONS       =\nMAX_INITIALIZER_LINES  = 30\nSHOW_USED_FILES        = YES\nSHOW_FILES             = YES\nSHOW_NAMESPACES        = YES\nFILE_VERSION_FILTER    =\nLAYOUT_FILE            =\nCITE_BIB_FILES         =\n#---------------------------------------------------------------------------\n# configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\nQUIET                  = YES\nWARNINGS               = YES\nWARN_IF_UNDOCUMENTED   = YES\nWARN_IF_DOC_ERROR      = YES\nWARN_NO_PARAMDOC       = NO\nWARN_FORMAT            = \"$file:$line: $text\"\nWARN_LOGFILE           =\n#---------------------------------------------------------------------------\n# configuration options related to the input files\n#---------------------------------------------------------------------------\nINPUT                  = ../c_utils\nINPUT_ENCODING         = UTF-8\nFILE_PATTERNS          = *.h \\\n                         *.c \\\n                         *.dox\nRECURSIVE              = YES\nEXCLUDE                =\nEXCLUDE_SYMLINKS       = NO\nEXCLUDE_PATTERNS       =\nEXCLUDE_SYMBOLS        =\nEXAMPLE_PATH           =\nEXAMPLE_PATTERNS       =\nEXAMPLE_RECURSIVE      = NO\nIMAGE_PATH             =\nINPUT_FILTER           =\nFILTER_PATTERNS        =\nFILTER_SOURCE_FILES    = NO\nFILTER_SOURCE_PATTERNS =\n#---------------------------------------------------------------------------\n# configuration options related to source browsing\n#---------------------------------------------------------------------------\nSOURCE_BROWSER         = YES\nINLINE_SOURCES         = NO\nSTRIP_CODE_COMMENTS    = NO\nREFERENCED_BY_RELATION = NO\nREFERENCES_RELATION    = NO\nREFERENCES_LINK_SOURCE = YES\nUSE_HTAGS              = NO\nVERBATIM_HEADERS       = YES\n#---------------------------------------------------------------------------\n# configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\nALPHABETICAL_INDEX     = YES\nCOLS_IN_ALPHA_INDEX    = 5\nIGNORE_PREFIX          =\n#---------------------------------------------------------------------------\n# configuration options related to the HTML output\n#---------------------------------------------------------------------------\nGENERATE_HTML          = YES\nHTML_OUTPUT            = htmldoc\nHTML_FILE_EXTENSION    = .html\nHTML_HEADER            =\nHTML_FOOTER            = footer.html\nHTML_STYLESHEET        =\nHTML_EXTRA_FILES       =\nHTML_COLORSTYLE_HUE    = 220\nHTML_COLORSTYLE_SAT    = 100\nHTML_COLORSTYLE_GAMMA  = 80\nHTML_TIMESTAMP         = YES\nHTML_DYNAMIC_SECTIONS  = NO\nHTML_INDEX_NUM_ENTRIES = 100\nGENERATE_DOCSET        = NO\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\nDOCSET_BUNDLE_ID       = org.doxygen.Project\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\nDOCSET_PUBLISHER_NAME  = Publisher\nGENERATE_HTMLHELP      = NO\nCHM_FILE               =\nHHC_LOCATION           =\nGENERATE_CHI           = NO\nCHM_INDEX_ENCODING     =\nBINARY_TOC             = NO\nTOC_EXPAND             = NO\nGENERATE_QHP           = NO\nQCH_FILE               =\nQHP_NAMESPACE          = org.doxygen.Project\nQHP_VIRTUAL_FOLDER     = doc\nQHP_CUST_FILTER_NAME   =\nQHP_CUST_FILTER_ATTRS  =\nQHP_SECT_FILTER_ATTRS  =\nQHG_LOCATION           =\nGENERATE_ECLIPSEHELP   = NO\nECLIPSE_DOC_ID         = org.doxygen.Project\nDISABLE_INDEX          = NO\nGENERATE_TREEVIEW      = NO\nENUM_VALUES_PER_LINE   = 4\nTREEVIEW_WIDTH         = 250\nEXT_LINKS_IN_WINDOW    = NO\nFORMULA_FONTSIZE       = 10\nFORMULA_TRANSPARENT    = YES\nUSE_MATHJAX            = NO\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\nMATHJAX_EXTENSIONS     =\nSEARCHENGINE           = NO\nSERVER_BASED_SEARCH    = NO\n#---------------------------------------------------------------------------\n# configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\nGENERATE_LATEX         = NO\nLATEX_OUTPUT           = latex\nLATEX_CMD_NAME         = latex\nMAKEINDEX_CMD_NAME     = makeindex\nCOMPACT_LATEX          = YES\nPAPER_TYPE             = a4wide\nEXTRA_PACKAGES         =\nLATEX_HEADER           =\nLATEX_FOOTER           =\nPDF_HYPERLINKS         = YES\nUSE_PDFLATEX           = YES\nLATEX_BATCHMODE        = NO\nLATEX_HIDE_INDICES     = NO\nLATEX_SOURCE_CODE      = NO\nLATEX_BIB_STYLE        = plain\n#---------------------------------------------------------------------------\n# configuration options related to the RTF output\n#---------------------------------------------------------------------------\nGENERATE_RTF           = NO\nRTF_OUTPUT             = rtf\nCOMPACT_RTF            = NO\nRTF_HYPERLINKS         = NO\nRTF_STYLESHEET_FILE    =\nRTF_EXTENSIONS_FILE    =\n#---------------------------------------------------------------------------\n# configuration options related to the man page output\n#---------------------------------------------------------------------------\nGENERATE_MAN           = NO\nMAN_OUTPUT             = man\nMAN_EXTENSION          = .3\nMAN_LINKS              = NO\n#---------------------------------------------------------------------------\n# configuration options related to the XML output\n#---------------------------------------------------------------------------\nGENERATE_XML           = NO\nXML_OUTPUT             = xml\nXML_SCHEMA             =\nXML_DTD                =\nXML_PROGRAMLISTING     = YES\n#---------------------------------------------------------------------------\n# configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\nGENERATE_AUTOGEN_DEF   = NO\n#---------------------------------------------------------------------------\n# configuration options related to the Perl module output\n#---------------------------------------------------------------------------\nGENERATE_PERLMOD       = NO\nPERLMOD_LATEX          = NO\nPERLMOD_PRETTY         = YES\nPERLMOD_MAKEVAR_PREFIX =\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\nENABLE_PREPROCESSING   = YES\nMACRO_EXPANSION        = NO\nEXPAND_ONLY_PREDEF     = NO\nSEARCH_INCLUDES        = YES\nINCLUDE_PATH           =\nINCLUDE_FILE_PATTERNS  =\nPREDEFINED             =\nEXPAND_AS_DEFINED      =\nSKIP_FUNCTION_MACROS   = YES\n#---------------------------------------------------------------------------\n# Configuration::additions related to external references\n#---------------------------------------------------------------------------\nTAGFILES               =\nGENERATE_TAGFILE       = c_utils.tag\nALLEXTERNALS           = NO\nEXTERNAL_GROUPS        = YES\nPERL_PATH              = /usr/bin/perl\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\nCLASS_DIAGRAMS         = YES\nMSCGEN_PATH            =\nHIDE_UNDOC_RELATIONS   = YES\nHAVE_DOT               = NO\nDOT_NUM_THREADS        = 0\nDOT_FONTNAME           = FreeSans\nDOT_FONTSIZE           = 10\nDOT_FONTPATH           =\nCLASS_GRAPH            = YES\nCOLLABORATION_GRAPH    = YES\nGROUP_GRAPHS           = YES\nUML_LOOK               = NO\nUML_LIMIT_NUM_FIELDS   = 10\nTEMPLATE_RELATIONS     = YES\nINCLUDE_GRAPH          = NO\nINCLUDED_BY_GRAPH      = NO\nCALL_GRAPH             = NO\nCALLER_GRAPH           = NO\nGRAPHICAL_HIERARCHY    = NO\nDIRECTORY_GRAPH        = YES\nDOT_IMAGE_FORMAT       = png\nINTERACTIVE_SVG        = NO\nDOT_PATH               =\nDOTFILE_DIRS           =\nMSCFILE_DIRS           =\nDOT_GRAPH_MAX_NODES    = 50\nMAX_DOT_GRAPH_DEPTH    = 0\nDOT_TRANSPARENT        = NO\nDOT_MULTI_TARGETS      = NO\nGENERATE_LEGEND        = YES\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "external/libsharp/docsrc/footer.html",
    "content": "<hr><address style=\"align: right;\"><small>\nGenerated on $datetime for $projectname\n</a> </small></address>\n</body>\n</html>\n"
  },
  {
    "path": "external/libsharp/docsrc/index_code.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<html><head><meta http-equiv=\"Content-Type\" content=\"text/html;charset=iso-8859-1\">\n<title>Libsharp source code documentation</title>\n</head><body>\n<H1>Libsharp source code documentation</H1>\n\n<H2>C interfaces</H2>\n\n<ul>\n<li><a href=\"c_utils/index.html\">C support library</a>\n<li><a href=\"libfftpack/index.html\">FFT interface</a>\n<li><a href=\"libsharp/index.html\">Library for spherical harmonic transforms</a>\n</ul>\n</body>\n</html>\n"
  },
  {
    "path": "external/libsharp/docsrc/libfftpack.dox",
    "content": "# Doxyfile 1.8.1\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\nDOXYFILE_ENCODING      = UTF-8\nPROJECT_NAME           = \"LevelS FFT library\"\nPROJECT_NUMBER         = 0.1\nPROJECT_BRIEF          =\nPROJECT_LOGO           =\nOUTPUT_DIRECTORY       = .\nCREATE_SUBDIRS         = NO\nOUTPUT_LANGUAGE        = English\nBRIEF_MEMBER_DESC      = NO\nREPEAT_BRIEF           = YES\nABBREVIATE_BRIEF       =\nALWAYS_DETAILED_SEC    = NO\nINLINE_INHERITED_MEMB  = NO\nFULL_PATH_NAMES        = NO\nSTRIP_FROM_PATH        =\nSTRIP_FROM_INC_PATH    =\nSHORT_NAMES            = NO\nJAVADOC_AUTOBRIEF      = NO\nQT_AUTOBRIEF           = NO\nMULTILINE_CPP_IS_BRIEF = NO\nINHERIT_DOCS           = YES\nSEPARATE_MEMBER_PAGES  = NO\nTAB_SIZE               = 8\nALIASES                =\nTCL_SUBST              =\nOPTIMIZE_OUTPUT_FOR_C  = YES\nOPTIMIZE_OUTPUT_JAVA   = NO\nOPTIMIZE_FOR_FORTRAN   = NO\nOPTIMIZE_OUTPUT_VHDL   = NO\nEXTENSION_MAPPING      =\nMARKDOWN_SUPPORT       = YES\nBUILTIN_STL_SUPPORT    = NO\nCPP_CLI_SUPPORT        = NO\nSIP_SUPPORT            = NO\nIDL_PROPERTY_SUPPORT   = YES\nDISTRIBUTE_GROUP_DOC   = NO\nSUBGROUPING            = YES\nINLINE_GROUPED_CLASSES = NO\nINLINE_SIMPLE_STRUCTS  = NO\nTYPEDEF_HIDES_STRUCT   = NO\nSYMBOL_CACHE_SIZE      = 0\nLOOKUP_CACHE_SIZE      = 0\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\nEXTRACT_ALL            = NO\nEXTRACT_PRIVATE        = NO\nEXTRACT_PACKAGE        = NO\nEXTRACT_STATIC         = NO\nEXTRACT_LOCAL_CLASSES  = YES\nEXTRACT_LOCAL_METHODS  = NO\nEXTRACT_ANON_NSPACES   = NO\nHIDE_UNDOC_MEMBERS     = YES\nHIDE_UNDOC_CLASSES     = YES\nHIDE_FRIEND_COMPOUNDS  = YES\nHIDE_IN_BODY_DOCS      = NO\nINTERNAL_DOCS          = NO\nCASE_SENSE_NAMES       = YES\nHIDE_SCOPE_NAMES       = NO\nSHOW_INCLUDE_FILES     = YES\nFORCE_LOCAL_INCLUDES   = NO\nINLINE_INFO            = YES\nSORT_MEMBER_DOCS       = NO\nSORT_BRIEF_DOCS        = NO\nSORT_MEMBERS_CTORS_1ST = NO\nSORT_GROUP_NAMES       = NO\nSORT_BY_SCOPE_NAME     = NO\nSTRICT_PROTO_MATCHING  = NO\nGENERATE_TODOLIST      = YES\nGENERATE_TESTLIST      = YES\nGENERATE_BUGLIST       = YES\nGENERATE_DEPRECATEDLIST= YES\nENABLED_SECTIONS       =\nMAX_INITIALIZER_LINES  = 30\nSHOW_USED_FILES        = YES\nSHOW_FILES             = YES\nSHOW_NAMESPACES        = YES\nFILE_VERSION_FILTER    =\nLAYOUT_FILE            =\nCITE_BIB_FILES         =\n#---------------------------------------------------------------------------\n# configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\nQUIET                  = YES\nWARNINGS               = YES\nWARN_IF_UNDOCUMENTED   = YES\nWARN_IF_DOC_ERROR      = YES\nWARN_NO_PARAMDOC       = NO\nWARN_FORMAT            = \"$file:$line: $text\"\nWARN_LOGFILE           =\n#---------------------------------------------------------------------------\n# configuration options related to the input files\n#---------------------------------------------------------------------------\nINPUT                  = ../libfftpack\nINPUT_ENCODING         = UTF-8\nFILE_PATTERNS          = *.h \\\n                         *.c \\\n                         *.dox\nRECURSIVE              = YES\nEXCLUDE                =\nEXCLUDE_SYMLINKS       = NO\nEXCLUDE_PATTERNS       =\nEXCLUDE_SYMBOLS        =\nEXAMPLE_PATH           =\nEXAMPLE_PATTERNS       =\nEXAMPLE_RECURSIVE      = NO\nIMAGE_PATH             =\nINPUT_FILTER           =\nFILTER_PATTERNS        =\nFILTER_SOURCE_FILES    = NO\nFILTER_SOURCE_PATTERNS =\n#---------------------------------------------------------------------------\n# configuration options related to source browsing\n#---------------------------------------------------------------------------\nSOURCE_BROWSER         = YES\nINLINE_SOURCES         = NO\nSTRIP_CODE_COMMENTS    = NO\nREFERENCED_BY_RELATION = NO\nREFERENCES_RELATION    = NO\nREFERENCES_LINK_SOURCE = YES\nUSE_HTAGS              = NO\nVERBATIM_HEADERS       = YES\n#---------------------------------------------------------------------------\n# configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\nALPHABETICAL_INDEX     = YES\nCOLS_IN_ALPHA_INDEX    = 5\nIGNORE_PREFIX          =\n#---------------------------------------------------------------------------\n# configuration options related to the HTML output\n#---------------------------------------------------------------------------\nGENERATE_HTML          = YES\nHTML_OUTPUT            = htmldoc\nHTML_FILE_EXTENSION    = .html\nHTML_HEADER            =\nHTML_FOOTER            = footer.html\nHTML_STYLESHEET        =\nHTML_EXTRA_FILES       =\nHTML_COLORSTYLE_HUE    = 220\nHTML_COLORSTYLE_SAT    = 100\nHTML_COLORSTYLE_GAMMA  = 80\nHTML_TIMESTAMP         = YES\nHTML_DYNAMIC_SECTIONS  = NO\nHTML_INDEX_NUM_ENTRIES = 100\nGENERATE_DOCSET        = NO\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\nDOCSET_BUNDLE_ID       = org.doxygen.Project\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\nDOCSET_PUBLISHER_NAME  = Publisher\nGENERATE_HTMLHELP      = NO\nCHM_FILE               =\nHHC_LOCATION           =\nGENERATE_CHI           = NO\nCHM_INDEX_ENCODING     =\nBINARY_TOC             = NO\nTOC_EXPAND             = NO\nGENERATE_QHP           = NO\nQCH_FILE               =\nQHP_NAMESPACE          = org.doxygen.Project\nQHP_VIRTUAL_FOLDER     = doc\nQHP_CUST_FILTER_NAME   =\nQHP_CUST_FILTER_ATTRS  =\nQHP_SECT_FILTER_ATTRS  =\nQHG_LOCATION           =\nGENERATE_ECLIPSEHELP   = NO\nECLIPSE_DOC_ID         = org.doxygen.Project\nDISABLE_INDEX          = NO\nGENERATE_TREEVIEW      = NO\nENUM_VALUES_PER_LINE   = 4\nTREEVIEW_WIDTH         = 250\nEXT_LINKS_IN_WINDOW    = NO\nFORMULA_FONTSIZE       = 10\nFORMULA_TRANSPARENT    = YES\nUSE_MATHJAX            = NO\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\nMATHJAX_EXTENSIONS     =\nSEARCHENGINE           = NO\nSERVER_BASED_SEARCH    = NO\n#---------------------------------------------------------------------------\n# configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\nGENERATE_LATEX         = NO\nLATEX_OUTPUT           = latex\nLATEX_CMD_NAME         = latex\nMAKEINDEX_CMD_NAME     = makeindex\nCOMPACT_LATEX          = YES\nPAPER_TYPE             = a4wide\nEXTRA_PACKAGES         =\nLATEX_HEADER           =\nLATEX_FOOTER           =\nPDF_HYPERLINKS         = YES\nUSE_PDFLATEX           = YES\nLATEX_BATCHMODE        = NO\nLATEX_HIDE_INDICES     = NO\nLATEX_SOURCE_CODE      = NO\nLATEX_BIB_STYLE        = plain\n#---------------------------------------------------------------------------\n# configuration options related to the RTF output\n#---------------------------------------------------------------------------\nGENERATE_RTF           = NO\nRTF_OUTPUT             = rtf\nCOMPACT_RTF            = NO\nRTF_HYPERLINKS         = NO\nRTF_STYLESHEET_FILE    =\nRTF_EXTENSIONS_FILE    =\n#---------------------------------------------------------------------------\n# configuration options related to the man page output\n#---------------------------------------------------------------------------\nGENERATE_MAN           = NO\nMAN_OUTPUT             = man\nMAN_EXTENSION          = .3\nMAN_LINKS              = NO\n#---------------------------------------------------------------------------\n# configuration options related to the XML output\n#---------------------------------------------------------------------------\nGENERATE_XML           = NO\nXML_OUTPUT             = xml\nXML_SCHEMA             =\nXML_DTD                =\nXML_PROGRAMLISTING     = YES\n#---------------------------------------------------------------------------\n# configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\nGENERATE_AUTOGEN_DEF   = NO\n#---------------------------------------------------------------------------\n# configuration options related to the Perl module output\n#---------------------------------------------------------------------------\nGENERATE_PERLMOD       = NO\nPERLMOD_LATEX          = NO\nPERLMOD_PRETTY         = YES\nPERLMOD_MAKEVAR_PREFIX =\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\nENABLE_PREPROCESSING   = YES\nMACRO_EXPANSION        = NO\nEXPAND_ONLY_PREDEF     = NO\nSEARCH_INCLUDES        = YES\nINCLUDE_PATH           =\nINCLUDE_FILE_PATTERNS  =\nPREDEFINED             =\nEXPAND_AS_DEFINED      =\nSKIP_FUNCTION_MACROS   = YES\n#---------------------------------------------------------------------------\n# Configuration::additions related to external references\n#---------------------------------------------------------------------------\nTAGFILES               = c_utils.tag=../c_utils\nGENERATE_TAGFILE       = libfftpack.tag\nALLEXTERNALS           = NO\nEXTERNAL_GROUPS        = YES\nPERL_PATH              = /usr/bin/perl\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\nCLASS_DIAGRAMS         = YES\nMSCGEN_PATH            =\nHIDE_UNDOC_RELATIONS   = YES\nHAVE_DOT               = NO\nDOT_NUM_THREADS        = 0\nDOT_FONTNAME           = FreeSans\nDOT_FONTSIZE           = 10\nDOT_FONTPATH           =\nCLASS_GRAPH            = YES\nCOLLABORATION_GRAPH    = YES\nGROUP_GRAPHS           = YES\nUML_LOOK               = NO\nUML_LIMIT_NUM_FIELDS   = 10\nTEMPLATE_RELATIONS     = YES\nINCLUDE_GRAPH          = NO\nINCLUDED_BY_GRAPH      = NO\nCALL_GRAPH             = NO\nCALLER_GRAPH           = NO\nGRAPHICAL_HIERARCHY    = NO\nDIRECTORY_GRAPH        = YES\nDOT_IMAGE_FORMAT       = png\nINTERACTIVE_SVG        = NO\nDOT_PATH               =\nDOTFILE_DIRS           =\nMSCFILE_DIRS           =\nDOT_GRAPH_MAX_NODES    = 50\nMAX_DOT_GRAPH_DEPTH    = 0\nDOT_TRANSPARENT        = NO\nDOT_MULTI_TARGETS      = NO\nGENERATE_LEGEND        = YES\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "external/libsharp/docsrc/libsharp.dox",
    "content": "# Doxyfile 1.8.1\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\nDOXYFILE_ENCODING      = UTF-8\nPROJECT_NAME           = \"LevelS SHT library\"\nPROJECT_NUMBER         = 0.1\nPROJECT_BRIEF          =\nPROJECT_LOGO           =\nOUTPUT_DIRECTORY       = .\nCREATE_SUBDIRS         = NO\nOUTPUT_LANGUAGE        = English\nBRIEF_MEMBER_DESC      = NO\nREPEAT_BRIEF           = YES\nABBREVIATE_BRIEF       =\nALWAYS_DETAILED_SEC    = NO\nINLINE_INHERITED_MEMB  = NO\nFULL_PATH_NAMES        = NO\nSTRIP_FROM_PATH        =\nSTRIP_FROM_INC_PATH    =\nSHORT_NAMES            = NO\nJAVADOC_AUTOBRIEF      = NO\nQT_AUTOBRIEF           = NO\nMULTILINE_CPP_IS_BRIEF = NO\nINHERIT_DOCS           = YES\nSEPARATE_MEMBER_PAGES  = NO\nTAB_SIZE               = 8\nALIASES                =\nTCL_SUBST              =\nOPTIMIZE_OUTPUT_FOR_C  = YES\nOPTIMIZE_OUTPUT_JAVA   = NO\nOPTIMIZE_FOR_FORTRAN   = NO\nOPTIMIZE_OUTPUT_VHDL   = NO\nEXTENSION_MAPPING      =\nMARKDOWN_SUPPORT       = YES\nBUILTIN_STL_SUPPORT    = NO\nCPP_CLI_SUPPORT        = NO\nSIP_SUPPORT            = NO\nIDL_PROPERTY_SUPPORT   = YES\nDISTRIBUTE_GROUP_DOC   = NO\nSUBGROUPING            = YES\nINLINE_GROUPED_CLASSES = NO\nINLINE_SIMPLE_STRUCTS  = NO\nTYPEDEF_HIDES_STRUCT   = NO\nSYMBOL_CACHE_SIZE      = 0\nLOOKUP_CACHE_SIZE      = 0\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\nEXTRACT_ALL            = NO\nEXTRACT_PRIVATE        = NO\nEXTRACT_PACKAGE        = NO\nEXTRACT_STATIC         = NO\nEXTRACT_LOCAL_CLASSES  = YES\nEXTRACT_LOCAL_METHODS  = NO\nEXTRACT_ANON_NSPACES   = NO\nHIDE_UNDOC_MEMBERS     = YES\nHIDE_UNDOC_CLASSES     = YES\nHIDE_FRIEND_COMPOUNDS  = YES\nHIDE_IN_BODY_DOCS      = NO\nINTERNAL_DOCS          = NO\nCASE_SENSE_NAMES       = YES\nHIDE_SCOPE_NAMES       = NO\nSHOW_INCLUDE_FILES     = YES\nFORCE_LOCAL_INCLUDES   = NO\nINLINE_INFO            = YES\nSORT_MEMBER_DOCS       = NO\nSORT_BRIEF_DOCS        = NO\nSORT_MEMBERS_CTORS_1ST = NO\nSORT_GROUP_NAMES       = NO\nSORT_BY_SCOPE_NAME     = NO\nSTRICT_PROTO_MATCHING  = NO\nGENERATE_TODOLIST      = YES\nGENERATE_TESTLIST      = YES\nGENERATE_BUGLIST       = YES\nGENERATE_DEPRECATEDLIST= YES\nENABLED_SECTIONS       =\nMAX_INITIALIZER_LINES  = 30\nSHOW_USED_FILES        = YES\nSHOW_FILES             = YES\nSHOW_NAMESPACES        = YES\nFILE_VERSION_FILTER    =\nLAYOUT_FILE            =\nCITE_BIB_FILES         =\n#---------------------------------------------------------------------------\n# configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\nQUIET                  = YES\nWARNINGS               = YES\nWARN_IF_UNDOCUMENTED   = YES\nWARN_IF_DOC_ERROR      = YES\nWARN_NO_PARAMDOC       = NO\nWARN_FORMAT            = \"$file:$line: $text\"\nWARN_LOGFILE           =\n#---------------------------------------------------------------------------\n# configuration options related to the input files\n#---------------------------------------------------------------------------\nINPUT                  = ../libsharp\nINPUT_ENCODING         = UTF-8\nFILE_PATTERNS          = *.h \\\n                         *.c \\\n                         *.dox\nRECURSIVE              = YES\nEXCLUDE                =\nEXCLUDE_SYMLINKS       = NO\nEXCLUDE_PATTERNS       =\nEXCLUDE_SYMBOLS        =\nEXAMPLE_PATH           =\nEXAMPLE_PATTERNS       =\nEXAMPLE_RECURSIVE      = NO\nIMAGE_PATH             =\nINPUT_FILTER           =\nFILTER_PATTERNS        =\nFILTER_SOURCE_FILES    = NO\nFILTER_SOURCE_PATTERNS =\n#---------------------------------------------------------------------------\n# configuration options related to source browsing\n#---------------------------------------------------------------------------\nSOURCE_BROWSER         = YES\nINLINE_SOURCES         = NO\nSTRIP_CODE_COMMENTS    = NO\nREFERENCED_BY_RELATION = NO\nREFERENCES_RELATION    = NO\nREFERENCES_LINK_SOURCE = YES\nUSE_HTAGS              = NO\nVERBATIM_HEADERS       = YES\n#---------------------------------------------------------------------------\n# configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\nALPHABETICAL_INDEX     = YES\nCOLS_IN_ALPHA_INDEX    = 5\nIGNORE_PREFIX          =\n#---------------------------------------------------------------------------\n# configuration options related to the HTML output\n#---------------------------------------------------------------------------\nGENERATE_HTML          = YES\nHTML_OUTPUT            = htmldoc\nHTML_FILE_EXTENSION    = .html\nHTML_HEADER            =\nHTML_FOOTER            = footer.html\nHTML_STYLESHEET        =\nHTML_EXTRA_FILES       =\nHTML_COLORSTYLE_HUE    = 220\nHTML_COLORSTYLE_SAT    = 100\nHTML_COLORSTYLE_GAMMA  = 80\nHTML_TIMESTAMP         = YES\nHTML_DYNAMIC_SECTIONS  = NO\nHTML_INDEX_NUM_ENTRIES = 100\nGENERATE_DOCSET        = NO\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\nDOCSET_BUNDLE_ID       = org.doxygen.Project\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\nDOCSET_PUBLISHER_NAME  = Publisher\nGENERATE_HTMLHELP      = NO\nCHM_FILE               =\nHHC_LOCATION           =\nGENERATE_CHI           = NO\nCHM_INDEX_ENCODING     =\nBINARY_TOC             = NO\nTOC_EXPAND             = NO\nGENERATE_QHP           = NO\nQCH_FILE               =\nQHP_NAMESPACE          = org.doxygen.Project\nQHP_VIRTUAL_FOLDER     = doc\nQHP_CUST_FILTER_NAME   =\nQHP_CUST_FILTER_ATTRS  =\nQHP_SECT_FILTER_ATTRS  =\nQHG_LOCATION           =\nGENERATE_ECLIPSEHELP   = NO\nECLIPSE_DOC_ID         = org.doxygen.Project\nDISABLE_INDEX          = NO\nGENERATE_TREEVIEW      = NO\nENUM_VALUES_PER_LINE   = 4\nTREEVIEW_WIDTH         = 250\nEXT_LINKS_IN_WINDOW    = NO\nFORMULA_FONTSIZE       = 10\nFORMULA_TRANSPARENT    = YES\nUSE_MATHJAX            = NO\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\nMATHJAX_EXTENSIONS     =\nSEARCHENGINE           = NO\nSERVER_BASED_SEARCH    = NO\n#---------------------------------------------------------------------------\n# configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\nGENERATE_LATEX         = NO\nLATEX_OUTPUT           = latex\nLATEX_CMD_NAME         = latex\nMAKEINDEX_CMD_NAME     = makeindex\nCOMPACT_LATEX          = YES\nPAPER_TYPE             = a4wide\nEXTRA_PACKAGES         =\nLATEX_HEADER           =\nLATEX_FOOTER           =\nPDF_HYPERLINKS         = YES\nUSE_PDFLATEX           = YES\nLATEX_BATCHMODE        = NO\nLATEX_HIDE_INDICES     = NO\nLATEX_SOURCE_CODE      = NO\nLATEX_BIB_STYLE        = plain\n#---------------------------------------------------------------------------\n# configuration options related to the RTF output\n#---------------------------------------------------------------------------\nGENERATE_RTF           = NO\nRTF_OUTPUT             = rtf\nCOMPACT_RTF            = NO\nRTF_HYPERLINKS         = NO\nRTF_STYLESHEET_FILE    =\nRTF_EXTENSIONS_FILE    =\n#---------------------------------------------------------------------------\n# configuration options related to the man page output\n#---------------------------------------------------------------------------\nGENERATE_MAN           = NO\nMAN_OUTPUT             = man\nMAN_EXTENSION          = .3\nMAN_LINKS              = NO\n#---------------------------------------------------------------------------\n# configuration options related to the XML output\n#---------------------------------------------------------------------------\nGENERATE_XML           = NO\nXML_OUTPUT             = xml\nXML_SCHEMA             =\nXML_DTD                =\nXML_PROGRAMLISTING     = YES\n#---------------------------------------------------------------------------\n# configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\nGENERATE_AUTOGEN_DEF   = NO\n#---------------------------------------------------------------------------\n# configuration options related to the Perl module output\n#---------------------------------------------------------------------------\nGENERATE_PERLMOD       = NO\nPERLMOD_LATEX          = NO\nPERLMOD_PRETTY         = YES\nPERLMOD_MAKEVAR_PREFIX =\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\nENABLE_PREPROCESSING   = YES\nMACRO_EXPANSION        = NO\nEXPAND_ONLY_PREDEF     = NO\nSEARCH_INCLUDES        = YES\nINCLUDE_PATH           =\nINCLUDE_FILE_PATTERNS  =\nPREDEFINED             =\nEXPAND_AS_DEFINED      =\nSKIP_FUNCTION_MACROS   = YES\n#---------------------------------------------------------------------------\n# Configuration::additions related to external references\n#---------------------------------------------------------------------------\nTAGFILES               = libfftpack.tag=../libfftpack \\\n                         c_utils.tag=../c_utils\nGENERATE_TAGFILE       = libsharp.tag\nALLEXTERNALS           = NO\nEXTERNAL_GROUPS        = YES\nPERL_PATH              = /usr/bin/perl\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\nCLASS_DIAGRAMS         = YES\nMSCGEN_PATH            =\nHIDE_UNDOC_RELATIONS   = YES\nHAVE_DOT               = NO\nDOT_NUM_THREADS        = 0\nDOT_FONTNAME           = FreeSans\nDOT_FONTSIZE           = 10\nDOT_FONTPATH           =\nCLASS_GRAPH            = YES\nCOLLABORATION_GRAPH    = YES\nGROUP_GRAPHS           = YES\nUML_LOOK               = NO\nUML_LIMIT_NUM_FIELDS   = 10\nTEMPLATE_RELATIONS     = YES\nINCLUDE_GRAPH          = NO\nINCLUDED_BY_GRAPH      = NO\nCALL_GRAPH             = NO\nCALLER_GRAPH           = NO\nGRAPHICAL_HIERARCHY    = NO\nDIRECTORY_GRAPH        = YES\nDOT_IMAGE_FORMAT       = png\nINTERACTIVE_SVG        = NO\nDOT_PATH               =\nDOTFILE_DIRS           =\nMSCFILE_DIRS           =\nDOT_GRAPH_MAX_NODES    = 50\nMAX_DOT_GRAPH_DEPTH    = 0\nDOT_TRANSPARENT        = NO\nDOT_MULTI_TARGETS      = NO\nGENERATE_LEGEND        = YES\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "external/libsharp/docsrc/planck.make",
    "content": "PKG:=docsrc\n\ndocsrc_idx: $(DOCDIR)_mkdir\n\tcp $(SRCROOT)/docsrc/index_code.html $(DOCDIR)/index.html\n\ndocsrc_code_doc: $(DOCDIR)_mkdir docsrc_idx\n\tcd $(SRCROOT)/docsrc; \\\n\tfor i in c_utils libfftpack libsharp; do \\\n\t  doxygen $${i}.dox; \\\n\t  rm -rf $(DOCDIR)/$${i}; mv htmldoc $(DOCDIR)/$${i}; \\\n\tdone; \\\n\trm *.tag;\n\ndocsrc_clean:\n\tcd $(SRCROOT)/docsrc; \\\n\trm -f *.tag\n\tcd $(SRCROOT)/docsrc; \\\n\trm -rf htmldoc\n\ndoc: docsrc_code_doc\n"
  },
  {
    "path": "external/libsharp/fortran/sharp.f90",
    "content": "module sharp\n  use iso_c_binding\n  implicit none\n  ! alm_info flags\n  integer, parameter :: SHARP_PACKED = 1\n\n  ! sharp job types\n  enum, bind(c)\n      enumerator :: SHARP_YtW = 0\n      enumerator :: SHARP_Y = 1\n      enumerator :: SHARP_Yt = 2\n      enumerator :: SHARP_WY = 3\n      enumerator :: SHARP_ALM2MAP_DERIV1 = 4\n   end enum\n\n  ! sharp job flags\n  integer, parameter :: SHARP_DP             = ISHFT(1, 4)\n  integer, parameter :: SHARP_ADD            = ISHFT(1, 5)\n  integer, parameter :: SHARP_REAL_HARMONICS = ISHFT(1, 6)\n  integer, parameter :: SHARP_NO_FFT         = ISHFT(1, 7)\n\n  type sharp_geom_info\n     type(c_ptr) :: handle\n     integer(c_intptr_t) :: n_local\n  end type sharp_geom_info\n\n  type sharp_alm_info\n     type(c_ptr) :: handle\n     integer(c_intptr_t) :: n_local\n  end type sharp_alm_info\n\n  interface\n\n     ! alm_info\n     subroutine sharp_make_general_alm_info( &\n         lmax, nm, stride, mval, mvstart, flags, alm_info) bind(c)\n       use iso_c_binding\n       integer(c_int), value, intent(in)    :: lmax, nm, stride, flags\n       integer(c_int), intent(in)           :: mval(nm)\n       integer(c_intptr_t), intent(in)     :: mvstart(nm)\n       type(c_ptr), intent(out)             :: alm_info\n     end subroutine sharp_make_general_alm_info\n\n     subroutine c_sharp_make_mmajor_real_packed_alm_info( &\n         lmax, stride, nm, ms, alm_info) bind(c, name='sharp_make_mmajor_real_packed_alm_info')\n       use iso_c_binding\n       integer(c_int), value, intent(in)    :: lmax, nm, stride\n       integer(c_int), intent(in), optional :: ms(nm)\n       type(c_ptr), intent(out)             :: alm_info\n     end subroutine c_sharp_make_mmajor_real_packed_alm_info\n\n     function c_sharp_alm_count(alm_info) bind(c, name='sharp_alm_count')\n       use iso_c_binding\n       integer(c_intptr_t)           :: c_sharp_alm_count\n       type(c_ptr), value, intent(in) :: alm_info\n     end function c_sharp_alm_count\n\n     subroutine c_sharp_destroy_alm_info(alm_info) bind(c, name='sharp_destroy_alm_info')\n       use iso_c_binding\n       type(c_ptr), value                   :: alm_info\n     end subroutine c_sharp_destroy_alm_info\n\n     ! geom_info\n     subroutine sharp_make_subset_healpix_geom_info ( &\n          nside, stride, nrings, rings, weight, geom_info) bind(c)\n       use iso_c_binding\n       integer(c_int), value, intent(in)    :: nside, stride, nrings\n       integer(c_int), intent(in), optional :: rings(nrings)\n       real(c_double), intent(in), optional :: weight(2 * nside)\n       type(c_ptr), intent(out)             :: geom_info\n     end subroutine sharp_make_subset_healpix_geom_info\n\n     subroutine c_sharp_destroy_geom_info(geom_info) bind(c, name='sharp_destroy_geom_info')\n       use iso_c_binding\n       type(c_ptr), value                   :: geom_info\n     end subroutine c_sharp_destroy_geom_info\n\n     function c_sharp_map_size(info) bind(c, name='sharp_map_size')\n       use iso_c_binding\n       integer(c_intptr_t) :: c_sharp_map_size\n       type(c_ptr), value   :: info\n     end function c_sharp_map_size\n\n\n     ! execute\n     subroutine c_sharp_execute(type, spin, alm, map, geom_info, alm_info, ntrans, &\n                                flags, time, opcnt) bind(c, name='sharp_execute')\n       use iso_c_binding\n       integer(c_int), value                        :: type, spin, ntrans, flags\n       type(c_ptr), value                           :: alm_info, geom_info\n       real(c_double), intent(out), optional        :: time\n       integer(c_long_long), intent(out), optional  :: opcnt\n       type(c_ptr), intent(in)                      :: alm(*), map(*)\n     end subroutine c_sharp_execute\n\n     subroutine c_sharp_execute_mpi(comm, type, spin, alm, map, geom_info, alm_info, ntrans, &\n                                    flags, time, opcnt) bind(c, name='sharp_execute_mpi_fortran')\n       use iso_c_binding\n       integer(c_int), value                        :: comm, type, spin, ntrans, flags\n       type(c_ptr), value                           :: alm_info, geom_info\n       real(c_double), intent(out), optional        :: time\n       integer(c_long_long), intent(out), optional  :: opcnt\n       type(c_ptr), intent(in)                      :: alm(*), map(*)\n     end subroutine c_sharp_execute_mpi\n\n     ! Legendre transforms\n     subroutine c_sharp_legendre_transform(bl, recfac, lmax, x, out, nx) &\n          bind(c, name='sharp_legendre_transform')\n       use iso_c_binding\n       integer(c_intptr_t), value :: lmax, nx\n       real(c_double) :: bl(lmax + 1), x(nx), out(nx)\n       real(c_double), optional :: recfac(lmax + 1)\n     end subroutine c_sharp_legendre_transform\n\n     subroutine c_sharp_legendre_transform_s(bl, recfac, lmax, x, out, nx) &\n          bind(c, name='sharp_legendre_transform_s')\n       use iso_c_binding\n       integer(c_intptr_t), value :: lmax, nx\n       real(c_float) :: bl(lmax + 1), x(nx), out(nx)\n       real(c_float), optional :: recfac(lmax + 1)\n     end subroutine c_sharp_legendre_transform_s\n  end interface\n\n  interface sharp_execute\n     module procedure sharp_execute_d\n  end interface\n\n  interface sharp_legendre_transform\n     module procedure sharp_legendre_transform_d, sharp_legendre_transform_s\n  end interface sharp_legendre_transform\n\ncontains\n  ! alm info\n\n  ! if ms is not passed, we default to using m=0..lmax.\n  subroutine sharp_make_mmajor_real_packed_alm_info(lmax, ms, alm_info)\n    use iso_c_binding\n    integer(c_int), value, intent(in)    :: lmax\n    integer(c_int), intent(in), optional :: ms(:)\n    type(sharp_alm_info), intent(out)    :: alm_info\n    !--\n    integer(c_int), allocatable          :: ms_copy(:)\n    integer(c_int)                       :: nm\n\n    if (present(ms)) then\n       nm = size(ms)\n       allocate(ms_copy(nm))\n       ms_copy = ms\n       call c_sharp_make_mmajor_real_packed_alm_info(lmax, 1, nm, ms_copy, alm_info=alm_info%handle)\n       deallocate(ms_copy)\n    else\n       call c_sharp_make_mmajor_real_packed_alm_info(lmax, 1, lmax + 1, alm_info=alm_info%handle)\n    end if\n    alm_info%n_local = c_sharp_alm_count(alm_info%handle)\n  end subroutine sharp_make_mmajor_real_packed_alm_info\n\n  subroutine sharp_destroy_alm_info(alm_info)\n    use iso_c_binding\n    type(sharp_alm_info), intent(inout) :: alm_info\n    call c_sharp_destroy_alm_info(alm_info%handle)\n    alm_info%handle = c_null_ptr\n  end subroutine sharp_destroy_alm_info\n\n\n  ! geom info\n  subroutine sharp_make_healpix_geom_info(nside, rings, weight, geom_info)\n    integer(c_int), value                :: nside\n    integer(c_int), optional             :: rings(:)\n    real(c_double), intent(in), optional :: weight(2 * nside)\n    type(sharp_geom_info), intent(out)   :: geom_info\n    !--\n    integer(c_int) :: nrings\n    integer(c_int), allocatable :: rings_copy(:)\n\n    if (present(rings)) then\n       nrings = size(rings)\n       allocate(rings_copy(nrings))\n       rings_copy = rings\n       call sharp_make_subset_healpix_geom_info(nside, 1, nrings, rings_copy, &\n                                                weight, geom_info%handle)\n       deallocate(rings_copy)\n    else\n       call sharp_make_subset_healpix_geom_info(nside, 1, nrings=4 * nside - 1, &\n                                                weight=weight, geom_info=geom_info%handle)\n    end if\n    geom_info%n_local = c_sharp_map_size(geom_info%handle)\n  end subroutine sharp_make_healpix_geom_info\n\n  subroutine sharp_destroy_geom_info(geom_info)\n    use iso_c_binding\n    type(sharp_geom_info), intent(inout) :: geom_info\n    call c_sharp_destroy_geom_info(geom_info%handle)\n    geom_info%handle = c_null_ptr\n  end subroutine sharp_destroy_geom_info\n\n\n  ! Currently the only mode supported is stacked (not interleaved) maps.\n  !\n  ! Note that passing the exact dimension of alm/map is necesarry, it\n  ! prevents the caller from doing too crazy slicing prior to pass array\n  ! in...\n  !\n  ! Usage:\n  !\n  ! The alm array must have shape exactly alm(alm_info%n_local, nmaps)\n  ! The maps array must have shape exactly map(map_info%n_local, nmaps).\n  subroutine sharp_execute_d(type, spin, nmaps, alm, alm_info, map, geom_info, &\n                             add, time, opcnt, comm)\n    use iso_c_binding\n    use mpi\n    implicit none\n    integer(c_int), value                        :: type, spin, nmaps\n    integer(c_int), optional                     :: comm\n    logical, value, optional                     :: add  ! should add instead of replace out\n\n    type(sharp_alm_info)                         :: alm_info\n    type(sharp_geom_info)                        :: geom_info\n    real(c_double), intent(out), optional        :: time\n    integer(c_long_long), intent(out), optional  :: opcnt\n    real(c_double), target, intent(inout)        :: alm(0:alm_info%n_local - 1, 1:nmaps)\n    real(c_double), target, intent(inout)        :: map(0:geom_info%n_local - 1, 1:nmaps)\n    !--\n    integer(c_int)         :: mod_flags, ntrans, k\n    type(c_ptr), target    :: alm_ptr(nmaps)\n    type(c_ptr), target    :: map_ptr(nmaps)\n\n    mod_flags = SHARP_DP\n    if (present(add) .and. add) then\n       mod_flags = or(mod_flags, SHARP_ADD)\n    end if\n\n    if (spin == 0) then\n       ntrans = nmaps\n    else\n       ntrans = nmaps / 2\n    end if\n\n    ! Set up pointer table to access maps\n    alm_ptr(:) = c_null_ptr\n    map_ptr(:) = c_null_ptr\n    do k = 1, nmaps\n       if (alm_info%n_local > 0) alm_ptr(k) = c_loc(alm(0, k))\n       if (geom_info%n_local > 0) map_ptr(k) = c_loc(map(0, k))\n    end do\n\n    if (present(comm)) then\n      call c_sharp_execute_mpi(comm, type, spin, alm_ptr, map_ptr, &\n          geom_info=geom_info%handle, &\n          alm_info=alm_info%handle, &\n          ntrans=ntrans, &\n          flags=mod_flags, &\n          time=time, &\n          opcnt=opcnt)\n    else\n      call c_sharp_execute(type, spin, alm_ptr, map_ptr, &\n          geom_info=geom_info%handle, &\n          alm_info=alm_info%handle, &\n          ntrans=ntrans, &\n          flags=mod_flags, &\n          time=time, &\n          opcnt=opcnt)\n   end if\n  end subroutine sharp_execute_d\n\n  subroutine sharp_legendre_transform_d(bl, x, out)\n    use iso_c_binding\n    real(c_double) :: bl(:)\n    real(c_double) :: x(:), out(size(x))\n    !--\n    integer(c_intptr_t) :: lmax, nx\n    call c_sharp_legendre_transform(bl, lmax=int(size(bl) - 1, c_intptr_t), &\n                                    x=x, out=out, nx=int(size(x), c_intptr_t))\n  end subroutine sharp_legendre_transform_d\n\n  subroutine sharp_legendre_transform_s(bl, x, out)\n    use iso_c_binding\n    real(c_float) :: bl(:)\n    real(c_float) :: x(:), out(size(x))\n    !--\n    integer(c_intptr_t) :: lmax, nx\n    call c_sharp_legendre_transform_s(bl, lmax=int(size(bl) - 1, c_intptr_t), &\n                                      x=x, out=out, nx=int(size(x), c_intptr_t))\n  end subroutine sharp_legendre_transform_s\n\n\nend module\n"
  },
  {
    "path": "external/libsharp/fortran/test_sharp.f90",
    "content": "program test_sharp\n  use mpi\n  use sharp\n  use iso_c_binding, only : c_ptr, c_double\n  implicit none\n\n  integer, parameter :: lmax = 2, nside = 2\n  type(sharp_alm_info) :: alm_info\n  type(sharp_geom_info) :: geom_info\n\n  real(c_double), dimension(0:(lmax + 1)**2 - 1, 1:1) :: alm\n  real(c_double), dimension(0:12*nside**2 - 1, 1:1) :: map\n\n  integer(c_int), dimension(1:lmax + 1) :: ms\n  integer(c_int), dimension(1:4 * nside - 1) :: rings\n  integer(c_int) :: nm, m, nrings, iring\n  integer :: nodecount, rank, ierr\n\n  call MPI_Init(ierr)\n  call MPI_Comm_size(MPI_COMM_WORLD, nodecount, ierr)\n  call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)\n\n  nm = 0\n  do m = rank, lmax, nodecount\n     nm = nm + 1\n     ms(nm) = m\n  end do\n\n  nrings = 0\n  do iring = rank + 1, 4 * nside - 1, nodecount\n     nrings = nrings + 1\n     rings(nrings) = iring\n  end do\n\n  alm = 0\n  map = 0\n  if (rank == 0) then\n    alm(0, 1) = 1\n  end if\n\n  print *, ms(1:nm)\n  call sharp_make_mmajor_real_packed_alm_info(lmax, ms=ms(1:nm), alm_info=alm_info)\n  print *, 'alm_info%n_local', alm_info%n_local\n  call sharp_make_healpix_geom_info(nside, rings=rings(1:nrings), geom_info=geom_info)\n  print *, 'geom_info%n_local', geom_info%n_local\n  print *, 'execute'\n  call sharp_execute(SHARP_Y, 0, 1, alm, alm_info, map, geom_info, comm=MPI_COMM_WORLD)\n\n  print *, alm\n  print *, map\n\n  call sharp_destroy_alm_info(alm_info)\n  call sharp_destroy_geom_info(geom_info)\n  print *, 'DONE'\n  call MPI_Finalize(ierr)\n\n  print *, 'LEGENDRE TRANSFORMS'\n\n  call test_legendre_transforms()\n\ncontains\n  subroutine test_legendre_transforms()\n    integer, parameter :: lmax = 20, nx=10\n    real(c_double) :: bl(0:lmax)\n    real(c_double) :: x(nx), out(nx)\n    real(c_float) :: out_s(nx)\n    !--\n    integer :: l, i\n\n    do l = 0, lmax\n       bl(l) = 1.0 / real(l + 1, c_double)\n    end do\n    do i = 1, nx\n       x(i) = 1 / real(i, c_double)\n    end do\n    out = 0\n    call sharp_legendre_transform(bl, x, out)\n    print *, out\n    call sharp_legendre_transform(real(bl, c_float), real(x, c_float), out_s)\n    print *, out_s\n  end subroutine test_legendre_transforms\n\n\nend program test_sharp\n"
  },
  {
    "path": "external/libsharp/libfftpack/README",
    "content": "ls_fft description:\n\nThis package is intended to calculate one-dimensional real or complex FFTs\nwith high accuracy and good efficiency even for lengths containing large\nprime factors.\nThe code is written in C, but a Fortran wrapper exists as well.\n\nBefore any FFT is executed, a plan must be generated for it. Plan creation\nis designed to be fast, so that there is no significant overhead if the\nplan is only used once or a few times.\n\nThe main component of the code is based on Paul N. Swarztrauber's FFTPACK in the\ndouble precision incarnation by Hugh C. Pumphrey\n(http://www.netlib.org/fftpack/dp.tgz).\n\nI replaced the iterative sine and cosine calculations in radfg() and radbg()\nby an exact calculation, which slightly improves the transform accuracy for\nreal FFTs with lengths containing large prime factors.\n\nSince FFTPACK becomes quite slow for FFT lengths with large prime factors\n(in the worst case of prime lengths it reaches O(n*n) complexity), I\nimplemented Bluestein's algorithm, which computes a FFT of length n by\nseveral FFTs of length n2>=2*n-1 and a convolution. Since n2 can be chosen\nto be highly composite, this algorithm is more efficient if n has large\nprime factors. The longer FFTs themselves are then computed using the FFTPACK\nroutines.\nBluestein's algorithm was implemented according to the description at\nhttp://en.wikipedia.org/wiki/Bluestein's_FFT_algorithm.\n\nThread-safety:\nAll routines can be called concurrently; all information needed by ls_fft\nis stored in the plan variable. However, using the same plan variable on\nmultiple threads simultaneously is not supported and will lead to data\ncorruption.\n"
  },
  {
    "path": "external/libsharp/libfftpack/bluestein.c",
    "content": "/*\n *  This file is part of libfftpack.\n *\n *  libfftpack is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libfftpack is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libfftpack; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libfftpack is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n *  Copyright (C) 2005, 2006, 2007, 2008 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include \"fftpack.h\"\n#include \"bluestein.h\"\n\n/* returns the sum of all prime factors of n */\nsize_t prime_factor_sum (size_t n)\n  {\n  size_t result=0,x,limit,tmp;\n  while (((tmp=(n>>1))<<1)==n)\n    { result+=2; n=tmp; }\n\n  limit=(size_t)sqrt(n+0.01);\n  for (x=3; x<=limit; x+=2)\n  while ((tmp=(n/x))*x==n)\n    {\n    result+=x;\n    n=tmp;\n    limit=(size_t)sqrt(n+0.01);\n    }\n  if (n>1) result+=n;\n\n  return result;\n  }\n\n/* returns the smallest composite of 2, 3 and 5 which is >= n */\nstatic size_t good_size(size_t n)\n  {\n  size_t f2, f23, f235, bestfac=2*n;\n  if (n<=6) return n;\n\n  for (f2=1; f2<bestfac; f2*=2)\n    for (f23=f2; f23<bestfac; f23*=3)\n      for (f235=f23; f235<bestfac; f235*=5)\n        if (f235>=n) bestfac=f235;\n  return bestfac;\n  }\n\nvoid bluestein_i (size_t n, double **tstorage, size_t *worksize)\n  {\n  static const double pi=3.14159265358979323846;\n  size_t n2=good_size(n*2-1);\n  size_t m, coeff;\n  double angle, xn2;\n  double *bk, *bkf, *work;\n  double pibyn=pi/n;\n  *worksize=2+2*n+8*n2+16;\n  *tstorage = RALLOC(double,2+2*n+8*n2+16);\n  ((size_t *)(*tstorage))[0]=n2;\n  bk  = *tstorage+2;\n  bkf = *tstorage+2+2*n;\n  work= *tstorage+2+2*(n+n2);\n\n/* initialize b_k */\n  bk[0] = 1;\n  bk[1] = 0;\n\n  coeff=0;\n  for (m=1; m<n; ++m)\n    {\n    coeff+=2*m-1;\n    if (coeff>=2*n) coeff-=2*n;\n    angle = pibyn*coeff;\n    bk[2*m] = cos(angle);\n    bk[2*m+1] = sin(angle);\n    }\n\n/* initialize the zero-padded, Fourier transformed b_k. Add normalisation. */\n  xn2 = 1./n2;\n  bkf[0] = bk[0]*xn2;\n  bkf[1] = bk[1]*xn2;\n  for (m=2; m<2*n; m+=2)\n    {\n    bkf[m]   = bkf[2*n2-m]   = bk[m]   *xn2;\n    bkf[m+1] = bkf[2*n2-m+1] = bk[m+1] *xn2;\n    }\n  for (m=2*n;m<=(2*n2-2*n+1);++m)\n    bkf[m]=0.;\n  cffti (n2,work);\n  cfftf (n2,bkf,work);\n  }\n\nvoid bluestein (size_t n, double *data, double *tstorage, int isign)\n  {\n  size_t n2=*((size_t *)tstorage);\n  size_t m;\n  double *bk, *bkf, *akf, *work;\n  bk  = tstorage+2;\n  bkf = tstorage+2+2*n;\n  work= tstorage+2+2*(n+n2);\n  akf = tstorage+2+2*n+6*n2+16;\n\n/* initialize a_k and FFT it */\n  if (isign>0)\n    for (m=0; m<2*n; m+=2)\n      {\n      akf[m]   = data[m]*bk[m]   - data[m+1]*bk[m+1];\n      akf[m+1] = data[m]*bk[m+1] + data[m+1]*bk[m];\n      }\n  else\n    for (m=0; m<2*n; m+=2)\n      {\n      akf[m]   = data[m]*bk[m]   + data[m+1]*bk[m+1];\n      akf[m+1] =-data[m]*bk[m+1] + data[m+1]*bk[m];\n      }\n  for (m=2*n; m<2*n2; ++m)\n    akf[m]=0;\n\n  cfftf (n2,akf,work);\n\n/* do the convolution */\n  if (isign>0)\n    for (m=0; m<2*n2; m+=2)\n      {\n      double im = -akf[m]*bkf[m+1] + akf[m+1]*bkf[m];\n      akf[m  ]  =  akf[m]*bkf[m]   + akf[m+1]*bkf[m+1];\n      akf[m+1]  = im;\n      }\n  else\n    for (m=0; m<2*n2; m+=2)\n      {\n      double im = akf[m]*bkf[m+1] + akf[m+1]*bkf[m];\n      akf[m  ]  = akf[m]*bkf[m]   - akf[m+1]*bkf[m+1];\n      akf[m+1]  = im;\n      }\n\n\n/* inverse FFT */\n  cfftb (n2,akf,work);\n\n/* multiply by b_k* */\n  if (isign>0)\n    for (m=0; m<2*n; m+=2)\n      {\n      data[m]   = bk[m]  *akf[m] - bk[m+1]*akf[m+1];\n      data[m+1] = bk[m+1]*akf[m] + bk[m]  *akf[m+1];\n      }\n  else\n    for (m=0; m<2*n; m+=2)\n      {\n      data[m]   = bk[m]  *akf[m] + bk[m+1]*akf[m+1];\n      data[m+1] =-bk[m+1]*akf[m] + bk[m]  *akf[m+1];\n      }\n  }\n"
  },
  {
    "path": "external/libsharp/libfftpack/bluestein.h",
    "content": "/*\n *  This file is part of libfftpack.\n *\n *  libfftpack is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libfftpack is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libfftpack; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libfftpack is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n *  Copyright (C) 2005 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_BLUESTEIN_H\n#define PLANCK_BLUESTEIN_H\n\n#include \"c_utils.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nsize_t prime_factor_sum (size_t n);\n\nvoid bluestein_i (size_t n, double **tstorage, size_t *worksize);\nvoid bluestein (size_t n, double *data, double *tstorage, int isign);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libfftpack/fftpack.c",
    "content": "/*\n *  This file is part of libfftpack.\n *\n *  libfftpack is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libfftpack is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libfftpack; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libfftpack is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n  fftpack.c : A set of FFT routines in C.\n  Algorithmically based on Fortran-77 FFTPACK by Paul N. Swarztrauber\n  (Version 4, 1985).\n\n  C port by Martin Reinecke (2010)\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"fftpack.h\"\n\n#define WA(x,i) wa[(i)+(x)*ido]\n#define CH(a,b,c) ch[(a)+ido*((b)+l1*(c))]\n#define CC(a,b,c) cc[(a)+ido*((b)+cdim*(c))]\n#define PM(a,b,c,d) { a=c+d; b=c-d; }\n#define PMC(a,b,c,d) { a.r=c.r+d.r; a.i=c.i+d.i; b.r=c.r-d.r; b.i=c.i-d.i; }\n#define ADDC(a,b,c) { a.r=b.r+c.r; a.i=b.i+c.i; }\n#define SCALEC(a,b) { a.r*=b; a.i*=b; }\n#define CONJFLIPC(a) { double tmp_=a.r; a.r=-a.i; a.i=tmp_; }\n/* (a+ib) = conj(c+id) * (e+if) */\n#define MULPM(a,b,c,d,e,f) { a=c*e+d*f; b=c*f-d*e; }\n\ntypedef struct {\n  double r,i;\n} cmplx;\n\n#define CONCAT(a,b) a ## b\n\n#define X(arg) CONCAT(passb,arg)\n#define BACKWARD\n#include \"fftpack_inc.c\"\n#undef BACKWARD\n#undef X\n\n#define X(arg) CONCAT(passf,arg)\n#include \"fftpack_inc.c\"\n#undef X\n\n#undef CC\n#undef CH\n#define CC(a,b,c) cc[(a)+ido*((b)+l1*(c))]\n#define CH(a,b,c) ch[(a)+ido*((b)+cdim*(c))]\n\nstatic void radf2 (size_t ido, size_t l1, const double *cc, double *ch,\n  const double *wa)\n  {\n  const size_t cdim=2;\n  size_t i, k, ic;\n  double ti2, tr2;\n\n  for (k=0; k<l1; k++)\n    PM (CH(0,0,k),CH(ido-1,1,k),CC(0,k,0),CC(0,k,1))\n  if ((ido&1)==0)\n    for (k=0; k<l1; k++)\n      {\n      CH(    0,1,k) = -CC(ido-1,k,1);\n      CH(ido-1,0,k) =  CC(ido-1,k,0);\n      }\n  if (ido<=2) return;\n  for (k=0; k<l1; k++)\n    for (i=2; i<ido; i+=2)\n      {\n      ic=ido-i;\n      MULPM (tr2,ti2,WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1))\n      PM (CH(i-1,0,k),CH(ic-1,1,k),CC(i-1,k,0),tr2)\n      PM (CH(i  ,0,k),CH(ic  ,1,k),ti2,CC(i  ,k,0))\n      }\n  }\n\nstatic void radf3(size_t ido, size_t l1, const double *cc, double *ch,\n  const double *wa)\n  {\n  const size_t cdim=3;\n  static const double taur=-0.5, taui=0.86602540378443864676;\n  size_t i, k, ic;\n  double ci2, di2, di3, cr2, dr2, dr3, ti2, ti3, tr2, tr3;\n\n  for (k=0; k<l1; k++)\n    {\n    cr2=CC(0,k,1)+CC(0,k,2);\n    CH(0,0,k) = CC(0,k,0)+cr2;\n    CH(0,2,k) = taui*(CC(0,k,2)-CC(0,k,1));\n    CH(ido-1,1,k) = CC(0,k,0)+taur*cr2;\n    }\n  if (ido==1) return;\n  for (k=0; k<l1; k++)\n    for (i=2; i<ido; i+=2)\n      {\n      ic=ido-i;\n      MULPM (dr2,di2,WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1))\n      MULPM (dr3,di3,WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2))\n      cr2=dr2+dr3;\n      ci2=di2+di3;\n      CH(i-1,0,k) = CC(i-1,k,0)+cr2;\n      CH(i  ,0,k) = CC(i  ,k,0)+ci2;\n      tr2 = CC(i-1,k,0)+taur*cr2;\n      ti2 = CC(i  ,k,0)+taur*ci2;\n      tr3 = taui*(di2-di3);\n      ti3 = taui*(dr3-dr2);\n      PM(CH(i-1,2,k),CH(ic-1,1,k),tr2,tr3)\n      PM(CH(i  ,2,k),CH(ic  ,1,k),ti3,ti2)\n      }\n  }\n\nstatic void radf4(size_t ido, size_t l1, const double *cc, double *ch,\n  const double *wa)\n  {\n  const size_t cdim=4;\n  static const double hsqt2=0.70710678118654752440;\n  size_t i, k, ic;\n  double ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;\n\n  for (k=0; k<l1; k++)\n    {\n    PM (tr1,CH(0,2,k),CC(0,k,3),CC(0,k,1))\n    PM (tr2,CH(ido-1,1,k),CC(0,k,0),CC(0,k,2))\n    PM (CH(0,0,k),CH(ido-1,3,k),tr2,tr1)\n    }\n  if ((ido&1)==0)\n    for (k=0; k<l1; k++)\n      {\n      ti1=-hsqt2*(CC(ido-1,k,1)+CC(ido-1,k,3));\n      tr1= hsqt2*(CC(ido-1,k,1)-CC(ido-1,k,3));\n      PM (CH(ido-1,0,k),CH(ido-1,2,k),CC(ido-1,k,0),tr1)\n      PM (CH(    0,3,k),CH(    0,1,k),ti1,CC(ido-1,k,2))\n      }\n  if (ido<=2) return;\n  for (k=0; k<l1; k++)\n    for (i=2; i<ido; i+=2)\n      {\n      ic=ido-i;\n      MULPM(cr2,ci2,WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1))\n      MULPM(cr3,ci3,WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2))\n      MULPM(cr4,ci4,WA(2,i-2),WA(2,i-1),CC(i-1,k,3),CC(i,k,3))\n      PM(tr1,tr4,cr4,cr2)\n      PM(ti1,ti4,ci2,ci4)\n      PM(tr2,tr3,CC(i-1,k,0),cr3)\n      PM(ti2,ti3,CC(i  ,k,0),ci3)\n      PM(CH(i-1,0,k),CH(ic-1,3,k),tr2,tr1)\n      PM(CH(i  ,0,k),CH(ic  ,3,k),ti1,ti2)\n      PM(CH(i-1,2,k),CH(ic-1,1,k),tr3,ti4)\n      PM(CH(i  ,2,k),CH(ic  ,1,k),tr4,ti3)\n      }\n  }\n\nstatic void radf5(size_t ido, size_t l1, const double *cc, double *ch,\n  const double *wa)\n  {\n  const size_t cdim=5;\n  static const double tr11= 0.3090169943749474241, ti11=0.95105651629515357212,\n                      tr12=-0.8090169943749474241, ti12=0.58778525229247312917;\n  size_t i, k, ic;\n  double ci2, di2, ci4, ci5, di3, di4, di5, ci3, cr2, cr3, dr2, dr3,\n         dr4, dr5, cr5, cr4, ti2, ti3, ti5, ti4, tr2, tr3, tr4, tr5;\n\n  for (k=0; k<l1; k++)\n    {\n    PM (cr2,ci5,CC(0,k,4),CC(0,k,1))\n    PM (cr3,ci4,CC(0,k,3),CC(0,k,2))\n    CH(0,0,k)=CC(0,k,0)+cr2+cr3;\n    CH(ido-1,1,k)=CC(0,k,0)+tr11*cr2+tr12*cr3;\n    CH(0,2,k)=ti11*ci5+ti12*ci4;\n    CH(ido-1,3,k)=CC(0,k,0)+tr12*cr2+tr11*cr3;\n    CH(0,4,k)=ti12*ci5-ti11*ci4;\n    }\n  if (ido==1) return;\n  for (k=0; k<l1;++k)\n    for (i=2; i<ido; i+=2)\n      {\n      ic=ido-i;\n      MULPM (dr2,di2,WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1))\n      MULPM (dr3,di3,WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2))\n      MULPM (dr4,di4,WA(2,i-2),WA(2,i-1),CC(i-1,k,3),CC(i,k,3))\n      MULPM (dr5,di5,WA(3,i-2),WA(3,i-1),CC(i-1,k,4),CC(i,k,4))\n      PM(cr2,ci5,dr5,dr2)\n      PM(ci2,cr5,di2,di5)\n      PM(cr3,ci4,dr4,dr3)\n      PM(ci3,cr4,di3,di4)\n      CH(i-1,0,k)=CC(i-1,k,0)+cr2+cr3;\n      CH(i  ,0,k)=CC(i  ,k,0)+ci2+ci3;\n      tr2=CC(i-1,k,0)+tr11*cr2+tr12*cr3;\n      ti2=CC(i  ,k,0)+tr11*ci2+tr12*ci3;\n      tr3=CC(i-1,k,0)+tr12*cr2+tr11*cr3;\n      ti3=CC(i  ,k,0)+tr12*ci2+tr11*ci3;\n      MULPM(tr5,tr4,cr5,cr4,ti11,ti12)\n      MULPM(ti5,ti4,ci5,ci4,ti11,ti12)\n      PM(CH(i-1,2,k),CH(ic-1,1,k),tr2,tr5)\n      PM(CH(i  ,2,k),CH(ic  ,1,k),ti5,ti2)\n      PM(CH(i-1,4,k),CH(ic-1,3,k),tr3,tr4)\n      PM(CH(i  ,4,k),CH(ic  ,3,k),ti4,ti3)\n      }\n  }\n\n#undef CH\n#undef CC\n#define CH(a,b,c) ch[(a)+ido*((b)+l1*(c))]\n#define CC(a,b,c) cc[(a)+ido*((b)+cdim*(c))]\n#define C1(a,b,c) cc[(a)+ido*((b)+l1*(c))]\n#define C2(a,b) cc[(a)+idl1*(b)]\n#define CH2(a,b) ch[(a)+idl1*(b)]\nstatic void radfg(size_t ido, size_t ip, size_t l1, size_t idl1,\n  double *cc, double *ch, const double *wa)\n  {\n  const size_t cdim=ip;\n  static const double twopi=6.28318530717958647692;\n  size_t idij, ipph, i, j, k, l, j2, ic, jc, lc, ik;\n  double ai1, ai2, ar1, ar2, arg;\n  double *csarr;\n  size_t aidx;\n\n  ipph=(ip+1)/ 2;\n  if(ido!=1)\n    {\n    memcpy(ch,cc,idl1*sizeof(double));\n\n    for(j=1; j<ip; j++)\n      for(k=0; k<l1; k++)\n        {\n        CH(0,k,j)=C1(0,k,j);\n        idij=(j-1)*ido+1;\n        for(i=2; i<ido; i+=2,idij+=2)\n          MULPM(CH(i-1,k,j),CH(i,k,j),wa[idij-1],wa[idij],C1(i-1,k,j),C1(i,k,j))\n        }\n\n    for(j=1,jc=ip-1; j<ipph; j++,jc--)\n      for(k=0; k<l1; k++)\n        for(i=2; i<ido; i+=2)\n          {\n          PM(C1(i-1,k,j),C1(i  ,k,jc),CH(i-1,k,jc),CH(i-1,k,j ))\n          PM(C1(i  ,k,j),C1(i-1,k,jc),CH(i  ,k,j ),CH(i  ,k,jc))\n          }\n    }\n  else\n    memcpy(cc,ch,idl1*sizeof(double));\n\n  for(j=1,jc=ip-1; j<ipph; j++,jc--)\n    for(k=0; k<l1; k++)\n      PM(C1(0,k,j),C1(0,k,jc),CH(0,k,jc),CH(0,k,j))\n\n  csarr=RALLOC(double,2*ip);\n  arg=twopi / ip;\n  csarr[0]=1.;\n  csarr[1]=0.;\n  csarr[2]=csarr[2*ip-2]=cos(arg);\n  csarr[3]=sin(arg); csarr[2*ip-1]=-csarr[3];\n  for (i=2; i<=ip/2; ++i)\n    {\n    csarr[2*i]=csarr[2*ip-2*i]=cos(i*arg);\n    csarr[2*i+1]=sin(i*arg);\n    csarr[2*ip-2*i+1]=-csarr[2*i+1];\n    }\n  for(l=1,lc=ip-1; l<ipph; l++,lc--)\n    {\n    ar1=csarr[2*l];\n    ai1=csarr[2*l+1];\n    for(ik=0; ik<idl1; ik++)\n      {\n      CH2(ik,l)=C2(ik,0)+ar1*C2(ik,1);\n      CH2(ik,lc)=ai1*C2(ik,ip-1);\n      }\n    aidx=2*l;\n    for(j=2,jc=ip-2; j<ipph; j++,jc--)\n      {\n      aidx+=2*l;\n      if (aidx>=2*ip) aidx-=2*ip;\n      ar2=csarr[aidx];\n      ai2=csarr[aidx+1];\n      for(ik=0; ik<idl1; ik++)\n        {\n        CH2(ik,l )+=ar2*C2(ik,j );\n        CH2(ik,lc)+=ai2*C2(ik,jc);\n        }\n      }\n    }\n  DEALLOC(csarr);\n\n  for(j=1; j<ipph; j++)\n    for(ik=0; ik<idl1; ik++)\n      CH2(ik,0)+=C2(ik,j);\n\n  for(k=0; k<l1; k++)\n    memcpy(&CC(0,0,k),&CH(0,k,0),ido*sizeof(double));\n  for(j=1; j<ipph; j++)\n    {\n    jc=ip-j;\n    j2=2*j;\n    for(k=0; k<l1; k++)\n      {\n      CC(ido-1,j2-1,k) = CH(0,k,j );\n      CC(0    ,j2  ,k) = CH(0,k,jc);\n      }\n    }\n  if(ido==1) return;\n\n  for(j=1; j<ipph; j++)\n    {\n    jc=ip-j;\n    j2=2*j;\n    for(k=0; k<l1; k++)\n      for(i=2; i<ido; i+=2)\n        {\n        ic=ido-i;\n        PM (CC(i-1,j2,k),CC(ic-1,j2-1,k),CH(i-1,k,j ),CH(i-1,k,jc))\n        PM (CC(i  ,j2,k),CC(ic  ,j2-1,k),CH(i  ,k,jc),CH(i  ,k,j ))\n        }\n    }\n  }\n\n#undef CC\n#undef CH\n#define CH(a,b,c) ch[(a)+ido*((b)+l1*(c))]\n#define CC(a,b,c) cc[(a)+ido*((b)+cdim*(c))]\n\nstatic void radb2(size_t ido, size_t l1, const double *cc, double *ch,\n  const double *wa)\n  {\n  const size_t cdim=2;\n  size_t i, k, ic;\n  double ti2, tr2;\n\n  for (k=0; k<l1; k++)\n    PM (CH(0,k,0),CH(0,k,1),CC(0,0,k),CC(ido-1,1,k))\n  if ((ido&1)==0)\n    for (k=0; k<l1; k++)\n      {\n      CH(ido-1,k,0) =  2*CC(ido-1,0,k);\n      CH(ido-1,k,1) = -2*CC(0    ,1,k);\n      }\n  if (ido<=2) return;\n  for (k=0; k<l1;++k)\n    for (i=2; i<ido; i+=2)\n      {\n      ic=ido-i;\n      PM (CH(i-1,k,0),tr2,CC(i-1,0,k),CC(ic-1,1,k))\n      PM (ti2,CH(i  ,k,0),CC(i  ,0,k),CC(ic  ,1,k))\n      MULPM (CH(i,k,1),CH(i-1,k,1),WA(0,i-2),WA(0,i-1),ti2,tr2)\n      }\n  }\n\nstatic void radb3(size_t ido, size_t l1, const double *cc, double *ch,\n  const double *wa)\n  {\n  const size_t cdim=3;\n  static const double taur=-0.5, taui=0.86602540378443864676;\n  size_t i, k, ic;\n  double ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2;\n\n  for (k=0; k<l1; k++)\n    {\n    tr2=2*CC(ido-1,1,k);\n    cr2=CC(0,0,k)+taur*tr2;\n    CH(0,k,0)=CC(0,0,k)+tr2;\n    ci3=2*taui*CC(0,2,k);\n    PM (CH(0,k,2),CH(0,k,1),cr2,ci3);\n    }\n  if (ido==1) return;\n  for (k=0; k<l1; k++)\n    for (i=2; i<ido; i+=2)\n      {\n      ic=ido-i;\n      tr2=CC(i-1,2,k)+CC(ic-1,1,k);\n      ti2=CC(i  ,2,k)-CC(ic  ,1,k);\n      cr2=CC(i-1,0,k)+taur*tr2;\n      ci2=CC(i  ,0,k)+taur*ti2;\n      CH(i-1,k,0)=CC(i-1,0,k)+tr2;\n      CH(i  ,k,0)=CC(i  ,0,k)+ti2;\n      cr3=taui*(CC(i-1,2,k)-CC(ic-1,1,k));\n      ci3=taui*(CC(i  ,2,k)+CC(ic  ,1,k));\n      PM(dr3,dr2,cr2,ci3)\n      PM(di2,di3,ci2,cr3)\n      MULPM(CH(i,k,1),CH(i-1,k,1),WA(0,i-2),WA(0,i-1),di2,dr2)\n      MULPM(CH(i,k,2),CH(i-1,k,2),WA(1,i-2),WA(1,i-1),di3,dr3)\n      }\n  }\n\nstatic void radb4(size_t ido, size_t l1, const double *cc, double *ch,\n  const double *wa)\n  {\n  const size_t cdim=4;\n  static const double sqrt2=1.41421356237309504880;\n  size_t i, k, ic;\n  double ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;\n\n  for (k=0; k<l1; k++)\n    {\n    PM (tr2,tr1,CC(0,0,k),CC(ido-1,3,k))\n    tr3=2*CC(ido-1,1,k);\n    tr4=2*CC(0,2,k);\n    PM (CH(0,k,0),CH(0,k,2),tr2,tr3)\n    PM (CH(0,k,3),CH(0,k,1),tr1,tr4)\n    }\n  if ((ido&1)==0)\n    for (k=0; k<l1; k++)\n      {\n      PM (ti1,ti2,CC(0    ,3,k),CC(0    ,1,k))\n      PM (tr2,tr1,CC(ido-1,0,k),CC(ido-1,2,k))\n      CH(ido-1,k,0)=tr2+tr2;\n      CH(ido-1,k,1)=sqrt2*(tr1-ti1);\n      CH(ido-1,k,2)=ti2+ti2;\n      CH(ido-1,k,3)=-sqrt2*(tr1+ti1);\n      }\n  if (ido<=2) return;\n  for (k=0; k<l1;++k)\n    for (i=2; i<ido; i+=2)\n      {\n      ic=ido-i;\n      PM (tr2,tr1,CC(i-1,0,k),CC(ic-1,3,k))\n      PM (ti1,ti2,CC(i  ,0,k),CC(ic  ,3,k))\n      PM (tr4,ti3,CC(i  ,2,k),CC(ic  ,1,k))\n      PM (tr3,ti4,CC(i-1,2,k),CC(ic-1,1,k))\n      PM (CH(i-1,k,0),cr3,tr2,tr3)\n      PM (CH(i  ,k,0),ci3,ti2,ti3)\n      PM (cr4,cr2,tr1,tr4)\n      PM (ci2,ci4,ti1,ti4)\n      MULPM (CH(i,k,1),CH(i-1,k,1),WA(0,i-2),WA(0,i-1),ci2,cr2)\n      MULPM (CH(i,k,2),CH(i-1,k,2),WA(1,i-2),WA(1,i-1),ci3,cr3)\n      MULPM (CH(i,k,3),CH(i-1,k,3),WA(2,i-2),WA(2,i-1),ci4,cr4)\n      }\n  }\n\nstatic void radb5(size_t ido, size_t l1, const double *cc, double *ch,\n  const double *wa)\n  {\n  const size_t cdim=5;\n  static const double tr11= 0.3090169943749474241, ti11=0.95105651629515357212,\n                      tr12=-0.8090169943749474241, ti12=0.58778525229247312917;\n  size_t i, k, ic;\n  double ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4,\n         ti2, ti3, ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5;\n\n  for (k=0; k<l1; k++)\n    {\n    ti5=2*CC(0,2,k);\n    ti4=2*CC(0,4,k);\n    tr2=2*CC(ido-1,1,k);\n    tr3=2*CC(ido-1,3,k);\n    CH(0,k,0)=CC(0,0,k)+tr2+tr3;\n    cr2=CC(0,0,k)+tr11*tr2+tr12*tr3;\n    cr3=CC(0,0,k)+tr12*tr2+tr11*tr3;\n    MULPM(ci5,ci4,ti5,ti4,ti11,ti12)\n    PM(CH(0,k,4),CH(0,k,1),cr2,ci5)\n    PM(CH(0,k,3),CH(0,k,2),cr3,ci4)\n    }\n  if (ido==1) return;\n  for (k=0; k<l1;++k)\n    for (i=2; i<ido; i+=2)\n      {\n      ic=ido-i;\n      PM(tr2,tr5,CC(i-1,2,k),CC(ic-1,1,k))\n      PM(ti5,ti2,CC(i  ,2,k),CC(ic  ,1,k))\n      PM(tr3,tr4,CC(i-1,4,k),CC(ic-1,3,k))\n      PM(ti4,ti3,CC(i  ,4,k),CC(ic  ,3,k))\n      CH(i-1,k,0)=CC(i-1,0,k)+tr2+tr3;\n      CH(i  ,k,0)=CC(i  ,0,k)+ti2+ti3;\n      cr2=CC(i-1,0,k)+tr11*tr2+tr12*tr3;\n      ci2=CC(i  ,0,k)+tr11*ti2+tr12*ti3;\n      cr3=CC(i-1,0,k)+tr12*tr2+tr11*tr3;\n      ci3=CC(i  ,0,k)+tr12*ti2+tr11*ti3;\n      MULPM(cr5,cr4,tr5,tr4,ti11,ti12)\n      MULPM(ci5,ci4,ti5,ti4,ti11,ti12)\n      PM(dr4,dr3,cr3,ci4)\n      PM(di3,di4,ci3,cr4)\n      PM(dr5,dr2,cr2,ci5)\n      PM(di2,di5,ci2,cr5)\n      MULPM(CH(i,k,1),CH(i-1,k,1),WA(0,i-2),WA(0,i-1),di2,dr2)\n      MULPM(CH(i,k,2),CH(i-1,k,2),WA(1,i-2),WA(1,i-1),di3,dr3)\n      MULPM(CH(i,k,3),CH(i-1,k,3),WA(2,i-2),WA(2,i-1),di4,dr4)\n      MULPM(CH(i,k,4),CH(i-1,k,4),WA(3,i-2),WA(3,i-1),di5,dr5)\n      }\n  }\n\nstatic void radbg(size_t ido, size_t ip, size_t l1, size_t idl1,\n  double *cc, double *ch, const double *wa)\n  {\n  const size_t cdim=ip;\n  static const double twopi=6.28318530717958647692;\n  size_t idij, ipph, i, j, k, l, j2, ic, jc, lc, ik;\n  double ai1, ai2, ar1, ar2, arg;\n  double *csarr;\n  size_t aidx;\n\n  ipph=(ip+1)/ 2;\n  for(k=0; k<l1; k++)\n    memcpy(&CH(0,k,0),&CC(0,0,k),ido*sizeof(double));\n  for(j=1; j<ipph; j++)\n    {\n    jc=ip-j;\n    j2=2*j;\n    for(k=0; k<l1; k++)\n      {\n      CH(0,k,j )=2*CC(ido-1,j2-1,k);\n      CH(0,k,jc)=2*CC(0    ,j2  ,k);\n      }\n    }\n\n  if(ido!=1)\n    for(j=1,jc=ip-1; j<ipph; j++,jc--)\n      for(k=0; k<l1; k++)\n        for(i=2; i<ido; i+=2)\n          {\n          ic=ido-i;\n          PM (CH(i-1,k,j ),CH(i-1,k,jc),CC(i-1,2*j,k),CC(ic-1,2*j-1,k))\n          PM (CH(i  ,k,jc),CH(i  ,k,j ),CC(i  ,2*j,k),CC(ic  ,2*j-1,k))\n          }\n\n  csarr=RALLOC(double,2*ip);\n  arg=twopi/ip;\n  csarr[0]=1.;\n  csarr[1]=0.;\n  csarr[2]=csarr[2*ip-2]=cos(arg);\n  csarr[3]=sin(arg); csarr[2*ip-1]=-csarr[3];\n  for (i=2; i<=ip/2; ++i)\n    {\n    csarr[2*i]=csarr[2*ip-2*i]=cos(i*arg);\n    csarr[2*i+1]=sin(i*arg);\n    csarr[2*ip-2*i+1]=-csarr[2*i+1];\n    }\n  for(l=1; l<ipph; l++)\n    {\n    lc=ip-l;\n    ar1=csarr[2*l];\n    ai1=csarr[2*l+1];\n    for(ik=0; ik<idl1; ik++)\n      {\n      C2(ik,l)=CH2(ik,0)+ar1*CH2(ik,1);\n      C2(ik,lc)=ai1*CH2(ik,ip-1);\n      }\n    aidx=2*l;\n    for(j=2; j<ipph; j++)\n      {\n      jc=ip-j;\n      aidx+=2*l;\n      if (aidx>=2*ip) aidx-=2*ip;\n      ar2=csarr[aidx];\n      ai2=csarr[aidx+1];\n      for(ik=0; ik<idl1; ik++)\n        {\n        C2(ik,l )+=ar2*CH2(ik,j );\n        C2(ik,lc)+=ai2*CH2(ik,jc);\n        }\n      }\n    }\n  DEALLOC(csarr);\n\n  for(j=1; j<ipph; j++)\n    for(ik=0; ik<idl1; ik++)\n      CH2(ik,0)+=CH2(ik,j);\n\n  for(j=1,jc=ip-1; j<ipph; j++,jc--)\n    for(k=0; k<l1; k++)\n      PM (CH(0,k,jc),CH(0,k,j),C1(0,k,j),C1(0,k,jc))\n\n  if(ido==1)\n    return;\n  for(j=1,jc=ip-1; j<ipph; j++,jc--)\n    for(k=0; k<l1; k++)\n      for(i=2; i<ido; i+=2)\n        {\n        PM (CH(i-1,k,jc),CH(i-1,k,j ),C1(i-1,k,j),C1(i  ,k,jc))\n        PM (CH(i  ,k,j ),CH(i  ,k,jc),C1(i  ,k,j),C1(i-1,k,jc))\n        }\n  memcpy(cc,ch,idl1*sizeof(double));\n\n  for(j=1; j<ip; j++)\n    for(k=0; k<l1; k++)\n      {\n      C1(0,k,j)=CH(0,k,j);\n      idij=(j-1)*ido+1;\n      for(i=2; i<ido; i+=2,idij+=2)\n        MULPM (C1(i,k,j),C1(i-1,k,j),wa[idij-1],wa[idij],CH(i,k,j),CH(i-1,k,j))\n      }\n  }\n\n#undef CC\n#undef CH\n#undef PM\n#undef MULPM\n\n\n/*----------------------------------------------------------------------\n   cfftf1, cfftb1, cfftf, cfftb, cffti1, cffti. Complex FFTs.\n  ----------------------------------------------------------------------*/\n\nstatic void cfft1(size_t n, cmplx c[], cmplx ch[], const cmplx wa[],\n  const size_t ifac[], int isign)\n  {\n  size_t k1, l1=1, nf=ifac[1], iw=0;\n  cmplx *p1=c, *p2=ch;\n\n  for(k1=0; k1<nf; k1++)\n    {\n    size_t ip=ifac[k1+2];\n    size_t l2=ip*l1;\n    size_t ido = n/l2;\n    if(ip==4)\n      (isign>0) ? passb4(ido, l1, p1, p2, wa+iw)\n                : passf4(ido, l1, p1, p2, wa+iw);\n    else if(ip==2)\n      (isign>0) ? passb2(ido, l1, p1, p2, wa+iw)\n                : passf2(ido, l1, p1, p2, wa+iw);\n    else if(ip==3)\n      (isign>0) ? passb3(ido, l1, p1, p2, wa+iw)\n                : passf3(ido, l1, p1, p2, wa+iw);\n    else if(ip==5)\n      (isign>0) ? passb5(ido, l1, p1, p2, wa+iw)\n                : passf5(ido, l1, p1, p2, wa+iw);\n    else if(ip==6)\n      (isign>0) ? passb6(ido, l1, p1, p2, wa+iw)\n                : passf6(ido, l1, p1, p2, wa+iw);\n    else\n      (isign>0) ? passbg(ido, ip, l1, p1, p2, wa+iw)\n                : passfg(ido, ip, l1, p1, p2, wa+iw);\n    SWAP(p1,p2,cmplx *);\n    l1=l2;\n    iw+=(ip-1)*ido;\n    }\n  if (p1!=c)\n    memcpy (c,p1,n*sizeof(cmplx));\n  }\n\nvoid cfftf(size_t n, double c[], double wsave[])\n  {\n  if (n!=1)\n    cfft1(n, (cmplx*)c, (cmplx*)wsave, (cmplx*)(wsave+2*n),\n          (size_t*)(wsave+4*n),-1);\n  }\n\nvoid cfftb(size_t n, double c[], double wsave[])\n  {\n  if (n!=1)\n    cfft1(n, (cmplx*)c, (cmplx*)wsave, (cmplx*)(wsave+2*n),\n          (size_t*)(wsave+4*n),+1);\n  }\n\nstatic void factorize (size_t n, const size_t *pf, size_t npf, size_t *ifac)\n  {\n  size_t nl=n, nf=0, ntry=0, j=0, i;\n\nstartloop:\n  j++;\n  ntry = (j<=npf) ? pf[j-1] : ntry+2;\n  do\n    {\n    size_t nq=nl / ntry;\n    size_t nr=nl-ntry*nq;\n    if (nr!=0)\n      goto startloop;\n    nf++;\n    ifac[nf+1]=ntry;\n    nl=nq;\n    if ((ntry==2) && (nf!=1))\n      {\n      for (i=nf+1; i>2; --i)\n        ifac[i]=ifac[i-1];\n      ifac[2]=2;\n      }\n    }\n  while(nl!=1);\n  ifac[0]=n;\n  ifac[1]=nf;\n  }\n\nstatic void cffti1(size_t n, double wa[], size_t ifac[])\n  {\n  static const size_t ntryh[5]={4,6,3,2,5};\n  static const double twopi=6.28318530717958647692;\n  size_t j, k, fi;\n\n  double argh=twopi/n;\n  size_t i=0, l1=1;\n  factorize (n,ntryh,5,ifac);\n  for(k=1; k<=ifac[1]; k++)\n    {\n    size_t ip=ifac[k+1];\n    size_t ido=n/(l1*ip);\n    for(j=1; j<ip; j++)\n      {\n      size_t is = i;\n      double argld=j*l1*argh;\n      wa[i  ]=1;\n      wa[i+1]=0;\n      for(fi=1; fi<=ido; fi++)\n        {\n        double arg=fi*argld;\n        i+=2;\n        wa[i  ]=cos(arg);\n        wa[i+1]=sin(arg);\n        }\n      if(ip>6)\n        {\n        wa[is  ]=wa[i  ];\n        wa[is+1]=wa[i+1];\n        }\n      }\n    l1*=ip;\n    }\n  }\n\nvoid cffti(size_t n, double wsave[])\n  { if (n!=1) cffti1(n, wsave+2*n,(size_t*)(wsave+4*n)); }\n\n\n/*----------------------------------------------------------------------\n   rfftf1, rfftb1, rfftf, rfftb, rffti1, rffti. Real FFTs.\n  ----------------------------------------------------------------------*/\n\nstatic void rfftf1(size_t n, double c[], double ch[], const double wa[],\n  const size_t ifac[])\n  {\n  size_t k1, l1=n, nf=ifac[1], iw=n-1;\n  double *p1=ch, *p2=c;\n\n  for(k1=1; k1<=nf;++k1)\n    {\n    size_t ip=ifac[nf-k1+2];\n    size_t ido=n / l1;\n    l1 /= ip;\n    iw-=(ip-1)*ido;\n    SWAP (p1,p2,double *);\n    if(ip==4)\n      radf4(ido, l1, p1, p2, wa+iw);\n    else if(ip==2)\n      radf2(ido, l1, p1, p2, wa+iw);\n    else if(ip==3)\n      radf3(ido, l1, p1, p2, wa+iw);\n    else if(ip==5)\n      radf5(ido, l1, p1, p2, wa+iw);\n    else\n      {\n      if (ido==1)\n        SWAP (p1,p2,double *);\n      radfg(ido, ip, l1, ido*l1, p1, p2, wa+iw);\n      SWAP (p1,p2,double *);\n      }\n    }\n  if (p1==c)\n    memcpy (c,ch,n*sizeof(double));\n  }\n\nstatic void rfftb1(size_t n, double c[], double ch[], const double wa[],\n  const size_t ifac[])\n  {\n  size_t k1, l1=1, nf=ifac[1], iw=0;\n  double *p1=c, *p2=ch;\n\n  for(k1=1; k1<=nf; k1++)\n    {\n    size_t ip = ifac[k1+1],\n           ido= n/(ip*l1);\n    if(ip==4)\n      radb4(ido, l1, p1, p2, wa+iw);\n    else if(ip==2)\n      radb2(ido, l1, p1, p2, wa+iw);\n    else if(ip==3)\n      radb3(ido, l1, p1, p2, wa+iw);\n    else if(ip==5)\n      radb5(ido, l1, p1, p2, wa+iw);\n    else\n      {\n      radbg(ido, ip, l1, ido*l1, p1, p2, wa+iw);\n      if (ido!=1)\n        SWAP (p1,p2,double *);\n      }\n    SWAP (p1,p2,double *);\n    l1*=ip;\n    iw+=(ip-1)*ido;\n    }\n  if (p1!=c)\n    memcpy (c,ch,n*sizeof(double));\n  }\n\nvoid rfftf(size_t n, double r[], double wsave[])\n  { if(n!=1) rfftf1(n, r, wsave, wsave+n,(size_t*)(wsave+2*n)); }\n\nvoid rfftb(size_t n, double r[], double wsave[])\n  { if(n!=1) rfftb1(n, r, wsave, wsave+n,(size_t*)(wsave+2*n)); }\n\nstatic void rffti1(size_t n, double wa[], size_t ifac[])\n  {\n  static const size_t ntryh[4]={4,2,3,5};\n  static const double twopi=6.28318530717958647692;\n  size_t i, j, k, fi;\n\n  double argh=twopi/n;\n  size_t is=0, l1=1;\n  factorize (n,ntryh,4,ifac);\n  for (k=1; k<ifac[1]; k++)\n    {\n    size_t ip=ifac[k+1],\n           ido=n/(l1*ip);\n    for (j=1; j<ip; ++j)\n      {\n      double argld=j*l1*argh;\n      for(i=is,fi=1; i<=ido+is-3; i+=2,++fi)\n        {\n        double arg=fi*argld;\n        wa[i  ]=cos(arg);\n        wa[i+1]=sin(arg);\n        }\n      is+=ido;\n      }\n    l1*=ip;\n    }\n  }\n\nvoid rffti(size_t n, double wsave[])\n  { if (n!=1) rffti1(n, wsave+n,(size_t*)(wsave+2*n)); }\n"
  },
  {
    "path": "external/libsharp/libfftpack/fftpack.h",
    "content": "/*\n *  This file is part of libfftpack.\n *\n *  libfftpack is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libfftpack is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libfftpack; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libfftpack is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n  fftpack.h : function declarations for fftpack.c\n  Algorithmically based on Fortran-77 FFTPACK by Paul N. Swarztrauber\n  (Version 4, 1985).\n\n  Pekka Janhunen 23.2.1995\n\n  (reformatted by joerg arndt)\n\n  reformatted and slightly enhanced by Martin Reinecke (2004)\n */\n\n#ifndef PLANCK_FFTPACK_H\n#define PLANCK_FFTPACK_H\n\n#include \"c_utils.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*! forward complex transform */\nvoid cfftf(size_t N, double complex_data[], double wrk[]);\n/*! backward complex transform */\nvoid cfftb(size_t N, double complex_data[], double wrk[]);\n/*! initializer for complex transforms */\nvoid cffti(size_t N, double wrk[]);\n\n/*! forward real transform */\nvoid rfftf(size_t N, double data[], double wrk[]);\n/*! backward real transform */\nvoid rfftb(size_t N, double data[], double wrk[]);\n/*! initializer for real transforms */\nvoid rffti(size_t N, double wrk[]);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libfftpack/fftpack_inc.c",
    "content": "/*\n *  This file is part of libfftpack.\n *\n *  libfftpack is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libfftpack is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libfftpack; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libfftpack is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n  fftpack.c : A set of FFT routines in C.\n  Algorithmically based on Fortran-77 FFTPACK by Paul N. Swarztrauber\n  (Version 4, 1985).\n\n  C port by Martin Reinecke (2010)\n */\n\n#ifdef BACKWARD\n#define PSIGN +\n#define PMSIGNC(a,b,c,d) { a.r=c.r+d.r; a.i=c.i+d.i; b.r=c.r-d.r; b.i=c.i-d.i; }\n/* a = b*c */\n#define MULPMSIGNC(a,b,c) { a.r=b.r*c.r-b.i*c.i; a.i=b.r*c.i+b.i*c.r; }\n#else\n#define PSIGN -\n#define PMSIGNC(a,b,c,d) { a.r=c.r-d.r; a.i=c.i-d.i; b.r=c.r+d.r; b.i=c.i+d.i; }\n/* a = conj(b)*c */\n#define MULPMSIGNC(a,b,c) { a.r=b.r*c.r+b.i*c.i; a.i=b.r*c.i-b.i*c.r; }\n#endif\n\nstatic void X(2) (size_t ido, size_t l1, const cmplx *cc, cmplx *ch,\n  const cmplx *wa)\n  {\n  const size_t cdim=2;\n  size_t k,i;\n  cmplx t;\n  if (ido==1)\n    for (k=0;k<l1;++k)\n      PMC (CH(0,k,0),CH(0,k,1),CC(0,0,k),CC(0,1,k))\n  else\n    for (k=0;k<l1;++k)\n      for (i=0;i<ido;++i)\n        {\n        PMC (CH(i,k,0),t,CC(i,0,k),CC(i,1,k))\n        MULPMSIGNC (CH(i,k,1),WA(0,i),t)\n        }\n  }\n\nstatic void X(3)(size_t ido, size_t l1, const cmplx *cc, cmplx *ch,\n  const cmplx *wa)\n  {\n  const size_t cdim=3;\n  static const double taur=-0.5, taui= PSIGN 0.86602540378443864676;\n  size_t i, k;\n  cmplx c2, c3, d2, d3, t2;\n\n  if (ido==1)\n    for (k=0; k<l1; ++k)\n      {\n      PMC (t2,c3,CC(0,1,k),CC(0,2,k))\n      ADDC (CH(0,k,0),t2,CC(0,0,k))\n      SCALEC(t2,taur)\n      ADDC(c2,CC(0,0,k),t2)\n      SCALEC(c3,taui)\n      CONJFLIPC(c3)\n      PMC(CH(0,k,1),CH(0,k,2),c2,c3)\n      }\n  else\n    for (k=0; k<l1; ++k)\n      for (i=0; i<ido; ++i)\n        {\n        PMC (t2,c3,CC(i,1,k),CC(i,2,k))\n        ADDC (CH(i,k,0),t2,CC(i,0,k))\n        SCALEC(t2,taur)\n        ADDC(c2,CC(i,0,k),t2)\n        SCALEC(c3,taui)\n        CONJFLIPC(c3)\n        PMC(d2,d3,c2,c3)\n        MULPMSIGNC(CH(i,k,1),WA(0,i),d2)\n        MULPMSIGNC(CH(i,k,2),WA(1,i),d3)\n        }\n  }\n\nstatic void X(4)(size_t ido, size_t l1, const cmplx *cc, cmplx *ch,\n  const cmplx *wa)\n  {\n  const size_t cdim=4;\n  size_t i, k;\n  cmplx c2, c3, c4, t1, t2, t3, t4;\n\n  if (ido==1)\n    for (k=0; k<l1; ++k)\n      {\n      PMC(t2,t1,CC(0,0,k),CC(0,2,k))\n      PMC(t3,t4,CC(0,1,k),CC(0,3,k))\n      CONJFLIPC(t4)\n      PMC(CH(0,k,0),CH(0,k,2),t2,t3)\n      PMSIGNC (CH(0,k,1),CH(0,k,3),t1,t4)\n      }\n  else\n    for (k=0; k<l1; ++k)\n      for (i=0; i<ido; ++i)\n        {\n        PMC(t2,t1,CC(i,0,k),CC(i,2,k))\n        PMC(t3,t4,CC(i,1,k),CC(i,3,k))\n        CONJFLIPC(t4)\n        PMC(CH(i,k,0),c3,t2,t3)\n        PMSIGNC (c2,c4,t1,t4)\n        MULPMSIGNC (CH(i,k,1),WA(0,i),c2)\n        MULPMSIGNC (CH(i,k,2),WA(1,i),c3)\n        MULPMSIGNC (CH(i,k,3),WA(2,i),c4)\n        }\n  }\n\nstatic void X(5)(size_t ido, size_t l1, const cmplx *cc, cmplx *ch,\n  const cmplx *wa)\n  {\n  const size_t cdim=5;\n  static const double tr11= 0.3090169943749474241,\n                      ti11= PSIGN 0.95105651629515357212,\n                      tr12=-0.8090169943749474241,\n                      ti12= PSIGN 0.58778525229247312917;\n  size_t i, k;\n  cmplx c2, c3, c4, c5, d2, d3, d4, d5, t2, t3, t4, t5;\n\n  if (ido==1)\n    for (k=0; k<l1; ++k)\n      {\n      PMC (t2,t5,CC(0,1,k),CC(0,4,k))\n      PMC (t3,t4,CC(0,2,k),CC(0,3,k))\n      CH(0,k,0).r=CC(0,0,k).r+t2.r+t3.r;\n      CH(0,k,0).i=CC(0,0,k).i+t2.i+t3.i;\n      c2.r=CC(0,0,k).r+tr11*t2.r+tr12*t3.r;\n      c2.i=CC(0,0,k).i+tr11*t2.i+tr12*t3.i;\n      c3.r=CC(0,0,k).r+tr12*t2.r+tr11*t3.r;\n      c3.i=CC(0,0,k).i+tr12*t2.i+tr11*t3.i;\n      c5.r=ti11*t5.r+ti12*t4.r;\n      c5.i=ti11*t5.i+ti12*t4.i;\n      c4.r=ti12*t5.r-ti11*t4.r;\n      c4.i=ti12*t5.i-ti11*t4.i;\n      CONJFLIPC(c5)\n      PMC(CH(0,k,1),CH(0,k,4),c2,c5)\n      CONJFLIPC(c4)\n      PMC(CH(0,k,2),CH(0,k,3),c3,c4)\n      }\n  else\n    for (k=0; k<l1; ++k)\n      for (i=0; i<ido; ++i)\n        {\n        PMC (t2,t5,CC(i,1,k),CC(i,4,k))\n        PMC (t3,t4,CC(i,2,k),CC(i,3,k))\n        CH(i,k,0).r=CC(i,0,k).r+t2.r+t3.r;\n        CH(i,k,0).i=CC(i,0,k).i+t2.i+t3.i;\n        c2.r=CC(i,0,k).r+tr11*t2.r+tr12*t3.r;\n        c2.i=CC(i,0,k).i+tr11*t2.i+tr12*t3.i;\n        c3.r=CC(i,0,k).r+tr12*t2.r+tr11*t3.r;\n        c3.i=CC(i,0,k).i+tr12*t2.i+tr11*t3.i;\n        c5.r=ti11*t5.r+ti12*t4.r;\n        c5.i=ti11*t5.i+ti12*t4.i;\n        c4.r=ti12*t5.r-ti11*t4.r;\n        c4.i=ti12*t5.i-ti11*t4.i;\n        CONJFLIPC(c5)\n        PMC(d2,d5,c2,c5)\n        CONJFLIPC(c4)\n        PMC(d3,d4,c3,c4)\n        MULPMSIGNC (CH(i,k,1),WA(0,i),d2)\n        MULPMSIGNC (CH(i,k,2),WA(1,i),d3)\n        MULPMSIGNC (CH(i,k,3),WA(2,i),d4)\n        MULPMSIGNC (CH(i,k,4),WA(3,i),d5)\n        }\n  }\n\nstatic void X(6)(size_t ido, size_t l1, const cmplx *cc, cmplx *ch,\n  const cmplx *wa)\n  {\n  const size_t cdim=6;\n  static const double taui= PSIGN 0.86602540378443864676;\n  cmplx ta1,ta2,ta3,a0,a1,a2,tb1,tb2,tb3,b0,b1,b2,d1,d2,d3,d4,d5;\n  size_t i, k;\n\n  if (ido==1)\n    for (k=0; k<l1; ++k)\n      {\n      PMC(ta1,ta3,CC(0,2,k),CC(0,4,k))\n      ta2.r = CC(0,0,k).r - .5*ta1.r;\n      ta2.i = CC(0,0,k).i - .5*ta1.i;\n      SCALEC(ta3,taui)\n      ADDC(a0,CC(0,0,k),ta1)\n      CONJFLIPC(ta3)\n      PMC(a1,a2,ta2,ta3)\n      PMC(tb1,tb3,CC(0,5,k),CC(0,1,k))\n      tb2.r = CC(0,3,k).r - .5*tb1.r;\n      tb2.i = CC(0,3,k).i - .5*tb1.i;\n      SCALEC(tb3,taui)\n      ADDC(b0,CC(0,3,k),tb1)\n      CONJFLIPC(tb3)\n      PMC(b1,b2,tb2,tb3)\n      PMC(CH(0,k,0),CH(0,k,3),a0,b0)\n      PMC(CH(0,k,4),CH(0,k,1),a1,b1)\n      PMC(CH(0,k,2),CH(0,k,5),a2,b2)\n      }\n  else\n    for (k=0; k<l1; ++k)\n      for (i=0; i<ido; ++i)\n        {\n        PMC(ta1,ta3,CC(i,2,k),CC(i,4,k))\n        ta2.r = CC(i,0,k).r - .5*ta1.r;\n        ta2.i = CC(i,0,k).i - .5*ta1.i;\n        SCALEC(ta3,taui)\n        ADDC(a0,CC(i,0,k),ta1)\n        CONJFLIPC(ta3)\n        PMC(a1,a2,ta2,ta3)\n        PMC(tb1,tb3,CC(i,5,k),CC(i,1,k))\n        tb2.r = CC(i,3,k).r - .5*tb1.r;\n        tb2.i = CC(i,3,k).i - .5*tb1.i;\n        SCALEC(tb3,taui)\n        ADDC(b0,CC(i,3,k),tb1)\n        CONJFLIPC(tb3)\n        PMC(b1,b2,tb2,tb3)\n        PMC(CH(i,k,0),d3,a0,b0)\n        PMC(d4,d1,a1,b1)\n        PMC(d2,d5,a2,b2)\n        MULPMSIGNC (CH(i,k,1),WA(0,i),d1)\n        MULPMSIGNC (CH(i,k,2),WA(1,i),d2)\n        MULPMSIGNC (CH(i,k,3),WA(2,i),d3)\n        MULPMSIGNC (CH(i,k,4),WA(3,i),d4)\n        MULPMSIGNC (CH(i,k,5),WA(4,i),d5)\n        }\n  }\n\nstatic void X(g)(size_t ido, size_t ip, size_t l1, const cmplx *cc, cmplx *ch,\n  const cmplx *wa)\n  {\n  const size_t cdim=ip;\n  cmplx *tarr=RALLOC(cmplx,2*ip);\n  cmplx *ccl=tarr, *wal=tarr+ip;\n  size_t i,j,k,l,jc,lc;\n  size_t ipph = (ip+1)/2;\n\n  for (i=1; i<ip; ++i)\n    wal[i]=wa[ido*(i-1)];\n  for (k=0; k<l1; ++k)\n    for (i=0; i<ido; ++i)\n      {\n      cmplx s=CC(i,0,k);\n      ccl[0] = CC(i,0,k);\n      for(j=1,jc=ip-1; j<ipph; ++j,--jc)\n        {\n        PMC (ccl[j],ccl[jc],CC(i,j,k),CC(i,jc,k))\n        ADDC (s,s,ccl[j])\n        }\n      CH(i,k,0) = s;\n      for (j=1, jc=ip-1; j<=ipph; ++j,--jc)\n        {\n        cmplx abr=ccl[0], abi={0.,0.};\n        size_t iang=0;\n        for (l=1,lc=ip-1; l<ipph; ++l,--lc)\n          {\n          iang+=j;\n          if (iang>ip) iang-=ip;\n          abr.r += ccl[l ].r*wal[iang].r;\n          abr.i += ccl[l ].i*wal[iang].r;\n          abi.r += ccl[lc].r*wal[iang].i;\n          abi.i += ccl[lc].i*wal[iang].i;\n          }\n#ifndef BACKWARD\n          { abi.i=-abi.i; abi.r=-abi.r; }\n#endif\n        CONJFLIPC(abi)\n        PMC(CH(i,k,j),CH(i,k,jc),abr,abi)\n        }\n      }\n\n  DEALLOC(tarr);\n\n  if (ido==1) return;\n\n  for (j=1; j<ip; ++j)\n    for (k=0; k<l1; ++k)\n      {\n      size_t idij=(j-1)*ido+1;\n      for(i=1; i<ido; ++i, ++idij)\n        {\n        cmplx t=CH(i,k,j);\n        MULPMSIGNC (CH(i,k,j),wa[idij],t)\n        }\n      }\n  }\n\n#undef PSIGN\n#undef PMSIGNC\n#undef MULPMSIGNC\n"
  },
  {
    "path": "external/libsharp/libfftpack/libfftpack.dox",
    "content": "/*! \\mainpage Libfftpack documentation\n  <ul>\n  <li>\\ref fftgroup \"Programming interface\"\n  </ul>\n */\n"
  },
  {
    "path": "external/libsharp/libfftpack/ls_fft.c",
    "content": "/*\n *  This file is part of libfftpack.\n *\n *  libfftpack is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libfftpack is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libfftpack; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libfftpack is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n *  Copyright (C) 2005 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#include <stdlib.h>\n#include <math.h>\n#include <string.h>\n#include \"bluestein.h\"\n#include \"fftpack.h\"\n#include \"ls_fft.h\"\n\ncomplex_plan make_complex_plan (size_t length)\n  {\n  complex_plan plan = RALLOC(complex_plan_i,1);\n  size_t pfsum = prime_factor_sum(length);\n  double comp1 = (double)(length*pfsum);\n  double comp2 = 2*3*length*log(3.*length);\n  comp2*=3.; /* fudge factor that appears to give good overall performance */\n  plan->length=length;\n  plan->bluestein = (comp2<comp1);\n  if (plan->bluestein)\n    bluestein_i (length,&(plan->work),&(plan->worksize));\n  else\n    {\n    plan->worksize=4*length+15;\n    plan->work=RALLOC(double,4*length+15);\n    cffti(length, plan->work);\n    }\n  return plan;\n  }\n\ncomplex_plan copy_complex_plan (complex_plan plan)\n  {\n  if (!plan) return NULL;\n  {\n  complex_plan newplan = RALLOC(complex_plan_i,1);\n  *newplan = *plan;\n  newplan->work=RALLOC(double,newplan->worksize);\n  memcpy(newplan->work,plan->work,sizeof(double)*newplan->worksize);\n  return newplan;\n  }\n  }\n\nvoid kill_complex_plan (complex_plan plan)\n  {\n  DEALLOC(plan->work);\n  DEALLOC(plan);\n  }\n\nvoid complex_plan_forward (complex_plan plan, double *data)\n  {\n  if (plan->bluestein)\n    bluestein (plan->length, data, plan->work, -1);\n  else\n    cfftf (plan->length, data, plan->work);\n  }\n\nvoid complex_plan_backward (complex_plan plan, double *data)\n  {\n  if (plan->bluestein)\n    bluestein (plan->length, data, plan->work, 1);\n  else\n    cfftb (plan->length, data, plan->work);\n  }\n\n\nreal_plan make_real_plan (size_t length)\n  {\n  real_plan plan = RALLOC(real_plan_i,1);\n  size_t pfsum = prime_factor_sum(length);\n  double comp1 = .5*length*pfsum;\n  double comp2 = 2*3*length*log(3.*length);\n  comp2*=3; /* fudge factor that appears to give good overall performance */\n  plan->length=length;\n  plan->bluestein = (comp2<comp1);\n  if (plan->bluestein)\n    bluestein_i (length,&(plan->work),&(plan->worksize));\n  else\n    {\n    plan->worksize=2*length+15;\n    plan->work=RALLOC(double,2*length+15);\n    rffti(length, plan->work);\n    }\n  return plan;\n  }\n\nreal_plan copy_real_plan (real_plan plan)\n  {\n  if (!plan) return NULL;\n  {\n  real_plan newplan = RALLOC(real_plan_i,1);\n  *newplan = *plan;\n  newplan->work=RALLOC(double,newplan->worksize);\n  memcpy(newplan->work,plan->work,sizeof(double)*newplan->worksize);\n  return newplan;\n  }\n  }\n\nvoid kill_real_plan (real_plan plan)\n  {\n  DEALLOC(plan->work);\n  DEALLOC(plan);\n  }\n\nvoid real_plan_forward_fftpack (real_plan plan, double *data)\n  {\n  if (plan->bluestein)\n    {\n    size_t m;\n    size_t n=plan->length;\n    double *tmp = RALLOC(double,2*n);\n    for (m=0; m<n; ++m)\n      {\n      tmp[2*m] = data[m];\n      tmp[2*m+1] = 0.;\n      }\n    bluestein(n,tmp,plan->work,-1);\n    data[0] = tmp[0];\n    memcpy (data+1, tmp+2, (n-1)*sizeof(double));\n    DEALLOC(tmp);\n    }\n  else\n    rfftf (plan->length, data, plan->work);\n  }\n\nstatic void fftpack2halfcomplex (double *data, size_t n)\n  {\n  size_t m;\n  double *tmp = RALLOC(double,n);\n  tmp[0]=data[0];\n  for (m=1; m<(n+1)/2; ++m)\n    {\n    tmp[m]=data[2*m-1];\n    tmp[n-m]=data[2*m];\n    }\n  if (!(n&1))\n    tmp[n/2]=data[n-1];\n  memcpy (data,tmp,n*sizeof(double));\n  DEALLOC(tmp);\n  }\n\nstatic void halfcomplex2fftpack (double *data, size_t n)\n  {\n  size_t m;\n  double *tmp = RALLOC(double,n);\n  tmp[0]=data[0];\n  for (m=1; m<(n+1)/2; ++m)\n    {\n    tmp[2*m-1]=data[m];\n    tmp[2*m]=data[n-m];\n    }\n  if (!(n&1))\n    tmp[n-1]=data[n/2];\n  memcpy (data,tmp,n*sizeof(double));\n  DEALLOC(tmp);\n  }\n\nvoid real_plan_forward_fftw (real_plan plan, double *data)\n  {\n  real_plan_forward_fftpack (plan, data);\n  fftpack2halfcomplex (data,plan->length);\n  }\n\nvoid real_plan_backward_fftpack (real_plan plan, double *data)\n  {\n  if (plan->bluestein)\n    {\n    size_t m;\n    size_t n=plan->length;\n    double *tmp = RALLOC(double,2*n);\n    tmp[0]=data[0];\n    tmp[1]=0.;\n    memcpy (tmp+2,data+1, (n-1)*sizeof(double));\n    if ((n&1)==0) tmp[n+1]=0.;\n    for (m=2; m<n; m+=2)\n      {\n      tmp[2*n-m]=tmp[m];\n      tmp[2*n-m+1]=-tmp[m+1];\n      }\n    bluestein (n, tmp, plan->work, 1);\n    for (m=0; m<n; ++m)\n      data[m] = tmp[2*m];\n    DEALLOC(tmp);\n    }\n  else\n    rfftb (plan->length, data, plan->work);\n  }\n\nvoid real_plan_backward_fftw (real_plan plan, double *data)\n  {\n  halfcomplex2fftpack (data,plan->length);\n  real_plan_backward_fftpack (plan, data);\n  }\n\nvoid real_plan_forward_c (real_plan plan, double *data)\n  {\n  size_t m;\n  size_t n=plan->length;\n\n  if (plan->bluestein)\n    {\n    for (m=1; m<2*n; m+=2)\n      data[m]=0;\n    bluestein (plan->length, data, plan->work, -1);\n    data[1]=0;\n    for (m=2; m<n; m+=2)\n      {\n      double avg;\n      avg = 0.5*(data[2*n-m]+data[m]);\n      data[2*n-m] = data[m] = avg;\n      avg = 0.5*(data[2*n-m+1]-data[m+1]);\n      data[2*n-m+1] = avg;\n      data[m+1] = -avg;\n      }\n    if ((n&1)==0) data[n+1] = 0.;\n    }\n  else\n    {\n/* using \"m+m\" instead of \"2*m\" to avoid a nasty bug in Intel's compiler */\n    for (m=0; m<n; ++m) data[m+1] = data[m+m];\n    rfftf (n, data+1, plan->work);\n    data[0] = data[1];\n    data[1] = 0;\n    for (m=2; m<n; m+=2)\n      {\n      data[2*n-m]   =  data[m];\n      data[2*n-m+1] = -data[m+1];\n      }\n    if ((n&1)==0) data[n+1] = 0.;\n    }\n  }\n\nvoid real_plan_backward_c (real_plan plan, double *data)\n  {\n  size_t n=plan->length;\n\n  if (plan->bluestein)\n    {\n    size_t m;\n    data[1]=0;\n    for (m=2; m<n; m+=2)\n      {\n      double avg;\n      avg = 0.5*(data[2*n-m]+data[m]);\n      data[2*n-m] = data[m] = avg;\n      avg = 0.5*(data[2*n-m+1]-data[m+1]);\n      data[2*n-m+1] = avg;\n      data[m+1] = -avg;\n      }\n    if ((n&1)==0) data[n+1] = 0.;\n    bluestein (plan->length, data, plan->work, 1);\n    for (m=1; m<2*n; m+=2)\n      data[m]=0;\n    }\n  else\n    {\n    ptrdiff_t m;\n    data[1] = data[0];\n    rfftb (n, data+1, plan->work);\n    for (m=n-1; m>=0; --m)\n      {\n      data[2*m]   = data[m+1];\n      data[2*m+1] = 0.;\n      }\n    }\n  }\n"
  },
  {
    "path": "external/libsharp/libfftpack/ls_fft.h",
    "content": "/*\n *  This file is part of libfftpack.\n *\n *  libfftpack is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libfftpack is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libfftpack; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libfftpack is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file ls_fft.h\n *  Interface for the LevelS FFT package.\n *\n *  Copyright (C) 2004 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_LS_FFT_H\n#define PLANCK_LS_FFT_H\n\n#include \"c_utils.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*!\\defgroup fftgroup FFT interface\nThis package is intended to calculate one-dimensional real or complex FFTs\nwith high accuracy and good efficiency even for lengths containing large\nprime factors.\nThe code is written in C, but a Fortran wrapper exists as well.\n\nBefore any FFT is executed, a plan must be generated for it. Plan creation\nis designed to be fast, so that there is no significant overhead if the\nplan is only used once or a few times.\n\nThe main component of the code is based on Paul N. Swarztrauber's FFTPACK in the\ndouble precision incarnation by Hugh C. Pumphrey\n(http://www.netlib.org/fftpack/dp.tgz).\n\nI replaced the iterative sine and cosine calculations in radfg() and radbg()\nby an exact calculation, which slightly improves the transform accuracy for\nreal FFTs with lengths containing large prime factors.\n\nSince FFTPACK becomes quite slow for FFT lengths with large prime factors\n(in the worst case of prime lengths it reaches \\f$\\mathcal{O}(n^2)\\f$\ncomplexity), I implemented Bluestein's algorithm, which computes a FFT of length\n\\f$n\\f$ by several FFTs of length \\f$n_2\\ge 2n-1\\f$ and a convolution. Since\n\\f$n_2\\f$ can be chosen to be highly composite, this algorithm is more efficient\nif \\f$n\\f$ has large prime factors. The longer FFTs themselves are then computed\nusing the FFTPACK routines.\nBluestein's algorithm was implemented according to the description on Wikipedia\n(<a href=\"http://en.wikipedia.org/wiki/Bluestein%27s_FFT_algorithm\">\nhttp://en.wikipedia.org/wiki/Bluestein%27s_FFT_algorithm</a>).\n\n\\b Thread-safety:\nAll routines can be called concurrently; all information needed by\n<tt>ls_fft</tt> is stored in the plan variable. However, using the same plan\nvariable on multiple threads simultaneously is not supported and will lead to\ndata corruption.\n*/\n/*! \\{ */\n\ntypedef struct\n  {\n  double *work;\n  size_t length, worksize;\n  int bluestein;\n  } complex_plan_i;\n\n/*! The opaque handle type for complex-FFT plans. */\ntypedef complex_plan_i * complex_plan;\n\n/*! Returns a plan for a complex FFT with \\a length elements. */\ncomplex_plan make_complex_plan (size_t length);\n/*! Constructs a copy of \\a plan. */\ncomplex_plan copy_complex_plan (complex_plan plan);\n/*! Destroys a plan for a complex FFT. */\nvoid kill_complex_plan (complex_plan plan);\n/*! Computes a complex forward FFT on \\a data, using \\a plan.\n    \\a Data has the form <tt>r0, i0, r1, i1, ...,\n    r[length-1], i[length-1]</tt>. */\nvoid complex_plan_forward (complex_plan plan, double *data);\n/*! Computes a complex backward FFT on \\a data, using \\a plan.\n    \\a Data has the form <tt>r0, i0, r1, i1, ...,\n    r[length-1], i[length-1]</tt>. */\nvoid complex_plan_backward (complex_plan plan, double *data);\n\ntypedef struct\n  {\n  double *work;\n  size_t length, worksize;\n  int bluestein;\n  } real_plan_i;\n\n/*! The opaque handle type for real-FFT plans. */\ntypedef real_plan_i * real_plan;\n\n/*! Returns a plan for a real FFT with \\a length elements. */\nreal_plan make_real_plan (size_t length);\n/*! Constructs a copy of \\a plan. */\nreal_plan copy_real_plan (real_plan plan);\n/*! Destroys a plan for a real FFT. */\nvoid kill_real_plan (real_plan plan);\n/*! Computes a real forward FFT on \\a data, using \\a plan\n    and assuming the FFTPACK storage scheme:\n    - on entry, \\a data has the form <tt>r0, r1, ..., r[length-1]</tt>;\n    - on exit, it has the form <tt>r0, r1, i1, r2, i2, ...</tt>\n      (a total of \\a length values). */\nvoid real_plan_forward_fftpack (real_plan plan, double *data);\n/*! Computes a real backward FFT on \\a data, using \\a plan\n    and assuming the FFTPACK storage scheme:\n    - on entry, \\a data has the form <tt>r0, r1, i1, r2, i2, ...</tt>\n    (a total of \\a length values);\n    - on exit, it has the form <tt>r0, r1, ..., r[length-1]</tt>. */\nvoid real_plan_backward_fftpack (real_plan plan, double *data);\n/*! Computes a real forward FFT on \\a data, using \\a plan\n    and assuming the FFTW halfcomplex storage scheme:\n    - on entry, \\a data has the form <tt>r0, r1, ..., r[length-1]</tt>;\n    - on exit, it has the form <tt>r0, r1, r2, ..., i2, i1</tt>. */\nvoid real_plan_forward_fftw (real_plan plan, double *data);\n/*! Computes a real backward FFT on \\a data, using \\a plan\n    and assuming the FFTW halfcomplex storage scheme:\n    - on entry, \\a data has the form <tt>r0, r1, r2, ..., i2, i1</tt>.\n    - on exit, it has the form <tt>r0, r1, ..., r[length-1]</tt>. */\nvoid real_plan_backward_fftw (real_plan plan, double *data);\n/*! Computes a real forward FFT on \\a data, using \\a plan\n    and assuming a full-complex storage scheme:\n    - on entry, \\a data has the form <tt>r0, [ignored], r1, [ignored], ...,\n      r[length-1], [ignored]</tt>;\n    - on exit, it has the form <tt>r0, i0, r1, i1, ...,\n      r[length-1], i[length-1]</tt>. */\nvoid real_plan_forward_c (real_plan plan, double *data);\n/*! Computes a real backward FFT on \\a data, using \\a plan\n    and assuming a full-complex storage scheme:\n    - on entry, \\a data has the form <tt>r0, i0, r1, i1, ...,\n      r[length-1], i[length-1]</tt>;\n    - on exit, it has the form <tt>r0, 0, r1, 0, ..., r[length-1], 0</tt>. */\nvoid real_plan_backward_c (real_plan plan, double *data);\n\n/*! \\} */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libfftpack/planck.make",
    "content": "PKG:=libfftpack\n\nSD:=$(SRCROOT)/$(PKG)\nOD:=$(BLDROOT)/$(PKG)\n\nFULL_INCLUDE+= -I$(SD)\n\nHDR_$(PKG):=$(SD)/*.h\nLIB_$(PKG):=$(LIBDIR)/libfftpack.a\nOBJ:=fftpack.o bluestein.o ls_fft.o\nOBJ:=$(OBJ:%=$(OD)/%)\n\nODEP:=$(HDR_$(PKG)) $(HDR_c_utils)\n\n$(OD)/fftpack.o: $(SD)/fftpack_inc.c\n\n$(OBJ): $(ODEP) | $(OD)_mkdir\n$(LIB_$(PKG)): $(OBJ)\n\nall_hdr+=$(HDR_$(PKG))\nall_lib+=$(LIB_$(PKG))\n"
  },
  {
    "path": "external/libsharp/libsharp/libsharp.dox",
    "content": "/*! \\mainpage libsharp documentation\n  <ul>\n  <li>\\ref introduction \"Introduction\"\n  <li><a href=\"modules.html\">Programming interface</a>\n  </ul>\n */\n\n/*! \\page introduction Introduction to libsharp\n\n  \"SHARP\" is an acronym for <i>Spherical HARmonic Package</i>.\n  All user-visible data types and functions in this library start with\n  the prefix \"sharp_\" to avoid pollution of the global C namespace.\n\n  <i>libsharp</i>'s main functionality is the conversion between <i>maps</i>\n  on the sphere and <i>spherical harmonic coefficients</i> (or <i>a_lm</i>).\n  A map is defined as a set of <i>rings</i>, which in turn consist of\n  individual pixels that\n  <ul>\n  <li>all have the same colatitude and</li>\n  <li>are uniformly spaced in azimuthal direction.</li>\n  </ul>\n  Consequently, a ring is completely defined by\n  <ul>\n  <li>its colatitute (in radians)</li>\n  <li>the number of pixels it contains</li>\n  <li>the azimuth (in radians) of the first pixel in the ring</li>\n  <li>the weight that must be multiplied to every pixel during a map\n      analysis (typically the solid angle of a pixel in the ring) </li>\n  <li>the offset of the first ring pixel in the <i>map array</i></li>\n  <li>the stride between consecutive pixels in the ring.</li>\n  </ul>\n  The map array is a one-dimensional array of type <i>float</i> or\n  <i>double</i>, which contains the values of all map pixels. It is assumed\n  that the pixels of every ring are stored inside this array in order of\n  increasing azimuth and with the specified stride. Note however that the rings\n  themselves can be stored in any order inside the array.\n\n  The a_lm array is a one-dimensional array of type <i>complex float</i> or\n  <i>complex double</i>, which contains all spherical harmonic coefficients\n  for a full or partial set of m quantum numbers with 0<=m<=mmax and m<=l<=lmax.\n  There is only one constraint on the internal structure of the array, which is:\n\n  <code>Index[a_l+1,m] = Index[a_l,m] + stride</code>\n\n  That means that coefficients with identical <i>m</i> but different <i>l</i>\n  can be interpreted as a one-dimensional array in <i>l</i> with a unique\n  stride.\n\n  Several functions are provided for efficient index computation in this array;\n  they are documented \\ref almgroup \"here\".\n\n  Information about a pixelisation of the sphere is stored in objects of\n  type sharp_geom_info. It is possible to create such an object for any\n  supported pixelisation by using the function sharp_make_geometry_info();\n  however, several easier-to-use functions are \\ref geominfogroup \"supplied\"\n  for generating often-used pixelisations like ECP grids, Gaussian grids,\n  and Healpix grids.\n\n  Currently, libsharp supports the following kinds of transforms:\n  <ul>\n  <li>scalar a_lm to map</li>\n  <li>scalar map to a_lm</li>\n<!--  <li>polarised a_lm to map</li>\n  <li>polarised map to a_lm</li> !-->\n  <li>spin a_lm to map</li>\n  <li>spin map to a_lm</li>\n  <li>scalar a_lm to maps of first derivatives</li>\n  </ul>\n\n  libsharp supports shared-memory parallelisation via OpenMP; this feature will\n  be automatically enabled if the compiler supports it.\n\n  Libsharp will also make use of SSE2 and AVX instructions when compiled for a\n  platform known to support them.\n\n  Support for MPI-parallel transforms is also available; in this mode,\n  every MPI task must provide a unique subset of the map and a_lm coefficients.\n\n  The spherical harmonic transforms can be executed on double-precision and\n  single-precision maps and a_lm, but for accuracy reasons the computations\n  will always be performed in double precision. As a consequence,\n  single-precision transforms will most likely not be faster than their\n  double-precision counterparts, but they will require significantly less\n  memory.\n*/\n"
  },
  {
    "path": "external/libsharp/libsharp/planck.make",
    "content": "PKG:=libsharp\n\nSD:=$(SRCROOT)/$(PKG)\nOD:=$(BLDROOT)/$(PKG)\n\nFULL_INCLUDE+= -I$(SD)\n\nHDR_$(PKG):=$(SD)/*.h\nLIB_$(PKG):=$(LIBDIR)/libsharp.a\nBIN:=sharp_testsuite\nLIBOBJ:=sharp_ylmgen_c.o sharp.o sharp_announce.o sharp_geomhelpers.o sharp_almhelpers.o sharp_core.o sharp_legendre.o sharp_legendre_roots.o sharp_legendre_table.o\nALLOBJ:=$(LIBOBJ) sharp_testsuite.o\nLIBOBJ:=$(LIBOBJ:%=$(OD)/%)\nALLOBJ:=$(ALLOBJ:%=$(OD)/%)\n\nODEP:=$(HDR_$(PKG)) $(HDR_libfftpack) $(HDR_c_utils)\n$(OD)/sharp_core.o: $(SD)/sharp_core_inchelper.c $(SD)/sharp_core_inc.c $(SD)/sharp_core_inc2.c\n$(OD)/sharp.o: $(SD)/sharp_mpi.c\nBDEP:=$(LIB_$(PKG)) $(LIB_libfftpack) $(LIB_c_utils)\n\n$(LIB_$(PKG)): $(LIBOBJ)\n\n$(ALLOBJ): $(ODEP) | $(OD)_mkdir\nBIN:=$(BIN:%=$(BINDIR)/%)\n$(BIN): $(BINDIR)/% : $(OD)/%.o $(BDEP)\n\nall_hdr+=$(HDR_$(PKG))\nall_lib+=$(LIB_$(PKG))\nall_cbin+=$(BIN)\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp.c\n *  Spherical transform library\n *\n *  Copyright (C) 2006-2013 Max-Planck-Society\n *  \\author Martin Reinecke \\author Dag Sverre Seljebotn\n */\n\n#include <math.h>\n#include \"ls_fft.h\"\n#include \"sharp_ylmgen_c.h\"\n#include \"sharp_internal.h\"\n#include \"c_utils.h\"\n#include \"sharp_core.h\"\n#include \"sharp_vecutil.h\"\n#include \"walltime_c.h\"\n#include \"sharp_almhelpers.h\"\n#include \"sharp_geomhelpers.h\"\n\ntypedef complex double dcmplx;\ntypedef complex float  fcmplx;\n\nstatic const double sqrt_one_half = 0.707106781186547572737310929369;\nstatic const double sqrt_two = 1.414213562373095145474621858739;\n\nstatic int chunksize_min=500, nchunks_max=10;\n\nstatic void get_chunk_info (int ndata, int nmult, int *nchunks, int *chunksize)\n  {\n  *chunksize = (ndata+nchunks_max-1)/nchunks_max;\n  if (*chunksize>=chunksize_min) // use max number of chunks\n    *chunksize = ((*chunksize+nmult-1)/nmult)*nmult;\n  else // need to adjust chunksize and nchunks\n    {\n    *nchunks = (ndata+chunksize_min-1)/chunksize_min;\n    *chunksize = (ndata+(*nchunks)-1)/(*nchunks);\n    if (*nchunks>1)\n      *chunksize = ((*chunksize+nmult-1)/nmult)*nmult;\n    }\n  *nchunks = (ndata+(*chunksize)-1)/(*chunksize);\n  }\n\nint sharp_get_mlim (int lmax, int spin, double sth, double cth)\n  {\n  double ofs=lmax*0.01;\n  if (ofs<100.) ofs=100.;\n  double b = -2*spin*fabs(cth);\n  double t1 = lmax*sth+ofs;\n  double c = (double)spin*spin-t1*t1;\n  double discr = b*b-4*c;\n  if (discr<=0) return lmax;\n  double res=(-b+sqrt(discr))/2.;\n  if (res>lmax) res=lmax;\n  return (int)(res+0.5);\n  }\n\ntypedef struct\n  {\n  double phi0_;\n  dcmplx *shiftarr;\n  int s_shift;\n  real_plan plan;\n  int norot;\n  } ringhelper;\n\nstatic void ringhelper_init (ringhelper *self)\n  {\n  static ringhelper rh_null = { 0, NULL, 0, NULL, 0 };\n  *self = rh_null;\n  }\n\nstatic void ringhelper_destroy (ringhelper *self)\n  {\n  if (self->plan) kill_real_plan(self->plan);\n  DEALLOC(self->shiftarr);\n  ringhelper_init(self);\n  }\n\nstatic void ringhelper_update (ringhelper *self, int nph, int mmax, double phi0)\n  {\n  self->norot = (fabs(phi0)<1e-14);\n  if (!(self->norot))\n    if ((mmax!=self->s_shift-1) || (!FAPPROX(phi0,self->phi0_,1e-12)))\n      {\n      RESIZE (self->shiftarr,dcmplx,mmax+1);\n      self->s_shift = mmax+1;\n      self->phi0_ = phi0;\n      for (int m=0; m<=mmax; ++m)\n        self->shiftarr[m] = cos(m*phi0) + _Complex_I*sin(m*phi0);\n      }\n  if (!self->plan) self->plan=make_real_plan(nph);\n  if (nph!=(int)self->plan->length)\n    {\n    kill_real_plan(self->plan);\n    self->plan=make_real_plan(nph);\n    }\n  }\n\nstatic int ringinfo_compare (const void *xa, const void *xb)\n  {\n  const sharp_ringinfo *a=xa, *b=xb;\n  return (a->sth < b->sth) ? -1 : (a->sth > b->sth) ? 1 : 0;\n  }\nstatic int ringpair_compare (const void *xa, const void *xb)\n  {\n  const sharp_ringpair *a=xa, *b=xb;\n  if (a->r1.nph==b->r1.nph)\n    return (a->r1.phi0 < b->r1.phi0) ? -1 :\n      ((a->r1.phi0 > b->r1.phi0) ? 1 :\n        (a->r1.cth>b->r1.cth ? -1 : 1));\n  return (a->r1.nph<b->r1.nph) ? -1 : 1;\n  }\n\nvoid sharp_make_general_alm_info (int lmax, int nm, int stride, const int *mval,\n  const ptrdiff_t *mstart, int flags, sharp_alm_info **alm_info)\n  {\n  sharp_alm_info *info = RALLOC(sharp_alm_info,1);\n  info->lmax = lmax;\n  info->nm = nm;\n  info->mval = RALLOC(int,nm);\n  info->mvstart = RALLOC(ptrdiff_t,nm);\n  info->stride = stride;\n  info->flags = flags;\n  for (int mi=0; mi<nm; ++mi)\n    {\n    info->mval[mi] = mval[mi];\n    info->mvstart[mi] = mstart[mi];\n    }\n  *alm_info = info;\n  }\n\nvoid sharp_make_alm_info (int lmax, int mmax, int stride,\n  const ptrdiff_t *mstart, sharp_alm_info **alm_info)\n  {\n  int *mval=RALLOC(int,mmax+1);\n  for (int i=0; i<=mmax; ++i)\n    mval[i]=i;\n  sharp_make_general_alm_info (lmax, mmax+1, stride, mval, mstart, 0, alm_info);\n  DEALLOC(mval);\n  }\n\nptrdiff_t sharp_alm_index (const sharp_alm_info *self, int l, int mi)\n  {\n  UTIL_ASSERT(!(self->flags & SHARP_PACKED),\n              \"sharp_alm_index not applicable with SHARP_PACKED alms\");\n  return self->mvstart[mi]+self->stride*l;\n  }\n\nptrdiff_t sharp_alm_count(const sharp_alm_info *self)\n  {\n  ptrdiff_t result=0;\n  for (int im=0; im!=self->nm; ++im)\n    {\n    int m=self->mval[im];\n    ptrdiff_t x=(self->lmax + 1 - m);\n    if ((m!=0)&&((self->flags&SHARP_PACKED)!=0)) result+=2*x;\n    else result+=x;\n    }\n  return result;\n  }\n\nvoid sharp_destroy_alm_info (sharp_alm_info *info)\n  {\n  DEALLOC (info->mval);\n  DEALLOC (info->mvstart);\n  DEALLOC (info);\n  }\n\nvoid sharp_make_geom_info (int nrings, const int *nph, const ptrdiff_t *ofs,\n  const int *stride, const double *phi0, const double *theta,\n  const double *wgt, sharp_geom_info **geom_info)\n  {\n  sharp_geom_info *info = RALLOC(sharp_geom_info,1);\n  sharp_ringinfo *infos = RALLOC(sharp_ringinfo,nrings);\n\n  int pos=0;\n  info->pair=RALLOC(sharp_ringpair,nrings);\n  info->npairs=0;\n  info->nphmax=0;\n  *geom_info = info;\n\n  for (int m=0; m<nrings; ++m)\n    {\n    infos[m].theta = theta[m];\n    infos[m].cth = cos(theta[m]);\n    infos[m].sth = sin(theta[m]);\n    infos[m].weight = (wgt != NULL) ? wgt[m] : 1.;\n    infos[m].phi0 = phi0[m];\n    infos[m].ofs = ofs[m];\n    infos[m].stride = stride[m];\n    infos[m].nph = nph[m];\n    if (info->nphmax<nph[m]) info->nphmax=nph[m];\n    }\n  qsort(infos,nrings,sizeof(sharp_ringinfo),ringinfo_compare);\n  while (pos<nrings)\n    {\n    info->pair[info->npairs].r1=infos[pos];\n    if ((pos<nrings-1) && FAPPROX(infos[pos].cth,-infos[pos+1].cth,1e-12))\n      {\n      if (infos[pos].cth>0)  // make sure northern ring is in r1\n        info->pair[info->npairs].r2=infos[pos+1];\n      else\n        {\n        info->pair[info->npairs].r1=infos[pos+1];\n        info->pair[info->npairs].r2=infos[pos];\n        }\n      ++pos;\n      }\n    else\n      info->pair[info->npairs].r2.nph=-1;\n    ++pos;\n    ++info->npairs;\n    }\n  DEALLOC(infos);\n\n  qsort(info->pair,info->npairs,sizeof(sharp_ringpair),ringpair_compare);\n  }\n\nptrdiff_t sharp_map_size(const sharp_geom_info *info)\n  {\n  ptrdiff_t result = 0;\n  for (int m=0; m<info->npairs; ++m)\n    {\n      result+=info->pair[m].r1.nph;\n      result+=(info->pair[m].r2.nph>=0) ? (info->pair[m].r2.nph) : 0;\n    }\n  return result;\n  }\n\nvoid sharp_destroy_geom_info (sharp_geom_info *geom_info)\n  {\n  DEALLOC (geom_info->pair);\n  DEALLOC (geom_info);\n  }\n\n/* This currently requires all m values from 0 to nm-1 to be present.\n   It might be worthwhile to relax this criterion such that holes in the m\n   distribution are permissible. */\nstatic int sharp_get_mmax (int *mval, int nm)\n  {\n  int *mcheck=RALLOC(int,nm);\n  SET_ARRAY(mcheck,0,nm,0);\n  for (int i=0; i<nm; ++i)\n    {\n    int m_cur=mval[i];\n    UTIL_ASSERT((m_cur>=0) && (m_cur<nm), \"not all m values are present\");\n    UTIL_ASSERT(mcheck[m_cur]==0, \"duplicate m value\");\n    mcheck[m_cur]=1;\n    }\n  DEALLOC(mcheck);\n  return nm-1;\n  }\n\nstatic void ringhelper_phase2ring (ringhelper *self,\n  const sharp_ringinfo *info, double *data, int mmax, const dcmplx *phase,\n  int pstride, int flags)\n  {\n  int nph = info->nph;\n\n  ringhelper_update (self, nph, mmax, info->phi0);\n\n  double wgt = (flags&SHARP_USE_WEIGHTS) ? info->weight : 1.;\n  if (flags&SHARP_REAL_HARMONICS)\n    wgt *= sqrt_one_half;\n\n  if (nph>=2*mmax+1)\n    {\n    for (int m=0; m<=mmax; ++m)\n      {\n      dcmplx tmp = phase[m*pstride]*wgt;\n      if(!self->norot) tmp*=self->shiftarr[m];\n      data[2*m]=creal(tmp);\n      data[2*m+1]=cimag(tmp);\n      }\n    for (int m=2*(mmax+1); m<nph+2; ++m)\n      data[m]=0.;\n    }\n  else\n    {\n    data[0]=creal(phase[0])*wgt;\n    SET_ARRAY(data,1,nph+2,0.);\n\n    int idx1=1, idx2=nph-1;\n    for (int m=1; m<=mmax; ++m)\n      {\n      dcmplx tmp = phase[m*pstride]*wgt;\n      if(!self->norot) tmp*=self->shiftarr[m];\n      if (idx1<(nph+2)/2)\n        {\n        data[2*idx1]+=creal(tmp);\n        data[2*idx1+1]+=cimag(tmp);\n        }\n      if (idx2<(nph+2)/2)\n        {\n        data[2*idx2]+=creal(tmp);\n        data[2*idx2+1]-=cimag(tmp);\n        }\n      if (++idx1>=nph) idx1=0;\n      if (--idx2<0) idx2=nph-1;\n      }\n    }\n  data[1]=data[0];\n  real_plan_backward_fftpack (self->plan, &(data[1]));\n  }\n\nstatic void ringhelper_ring2phase (ringhelper *self,\n  const sharp_ringinfo *info, double *data, int mmax, dcmplx *phase,\n  int pstride, int flags)\n  {\n  int nph = info->nph;\n#if 1\n  int maxidx = mmax; /* Enable this for traditional Healpix compatibility */\n#else\n  int maxidx = IMIN(nph-1,mmax);\n#endif\n\n  ringhelper_update (self, nph, mmax, -info->phi0);\n  double wgt = (flags&SHARP_USE_WEIGHTS) ? info->weight : 1;\n  if (flags&SHARP_REAL_HARMONICS)\n    wgt *= sqrt_two;\n\n  real_plan_forward_fftpack (self->plan, &(data[1]));\n  data[0]=data[1];\n  data[1]=data[nph+1]=0.;\n\n  if (maxidx<=nph/2)\n    {\n    if (self->norot)\n      for (int m=0; m<=maxidx; ++m)\n        phase[m*pstride] = (data[2*m] + _Complex_I*data[2*m+1]) * wgt;\n    else\n      for (int m=0; m<=maxidx; ++m)\n        phase[m*pstride] =\n          (data[2*m] + _Complex_I*data[2*m+1]) * self->shiftarr[m] * wgt;\n    }\n  else\n    {\n    for (int m=0; m<=maxidx; ++m)\n      {\n      int idx=m%nph;\n      dcmplx val;\n      if (idx<(nph-idx))\n        val = (data[2*idx] + _Complex_I*data[2*idx+1]) * wgt;\n      else\n        val = (data[2*(nph-idx)] - _Complex_I*data[2*(nph-idx)+1]) * wgt;\n      if (!self->norot)\n        val *= self->shiftarr[m];\n      phase[m*pstride]=val;\n      }\n    }\n\n  for (int m=maxidx+1;m<=mmax; ++m)\n    phase[m*pstride]=0.;\n  }\n\nstatic void fill_map (const sharp_geom_info *ginfo, void *map, double value,\n  int flags)\n  {\n  if (flags & SHARP_NO_FFT)\n    {\n    for (int j=0;j<ginfo->npairs;++j)\n      {\n      if (flags&SHARP_DP)\n        {\n        for (ptrdiff_t i=0;i<ginfo->pair[j].r1.nph;++i)\n          ((dcmplx *)map)[ginfo->pair[j].r1.ofs+i*ginfo->pair[j].r1.stride]\n            =value;\n        for (ptrdiff_t i=0;i<ginfo->pair[j].r2.nph;++i)\n          ((dcmplx *)map)[ginfo->pair[j].r2.ofs+i*ginfo->pair[j].r2.stride]\n            =value;\n        }\n      else\n        {\n        for (ptrdiff_t i=0;i<ginfo->pair[j].r1.nph;++i)\n          ((fcmplx *)map)[ginfo->pair[j].r1.ofs+i*ginfo->pair[j].r1.stride]\n            =(float)value;\n        for (ptrdiff_t i=0;i<ginfo->pair[j].r2.nph;++i)\n          ((fcmplx *)map)[ginfo->pair[j].r2.ofs+i*ginfo->pair[j].r2.stride]\n            =(float)value;\n        }\n      }\n    }\n  else\n    {\n    for (int j=0;j<ginfo->npairs;++j)\n      {\n      if (flags&SHARP_DP)\n        {\n        for (ptrdiff_t i=0;i<ginfo->pair[j].r1.nph;++i)\n          ((double *)map)[ginfo->pair[j].r1.ofs+i*ginfo->pair[j].r1.stride]\n            =value;\n        for (ptrdiff_t i=0;i<ginfo->pair[j].r2.nph;++i)\n          ((double *)map)[ginfo->pair[j].r2.ofs+i*ginfo->pair[j].r2.stride]\n            =value;\n        }\n      else\n        {\n        for (ptrdiff_t i=0;i<ginfo->pair[j].r1.nph;++i)\n          ((float *)map)[ginfo->pair[j].r1.ofs+i*ginfo->pair[j].r1.stride]\n            =(float)value;\n        for (ptrdiff_t i=0;i<ginfo->pair[j].r2.nph;++i)\n          ((float *)map)[ginfo->pair[j].r2.ofs+i*ginfo->pair[j].r2.stride]\n            =(float)value;\n        }\n      }\n    }\n  }\n\nstatic void clear_alm (const sharp_alm_info *ainfo, void *alm, int flags)\n  {\n#define CLEARLOOP(real_t,body)             \\\n      {                                    \\\n        real_t *talm = (real_t *)alm;      \\\n          for (int l=m;l<=ainfo->lmax;++l) \\\n            body                           \\\n      }\n\n  for (int mi=0;mi<ainfo->nm;++mi)\n    {\n      int m=ainfo->mval[mi];\n      ptrdiff_t mvstart = ainfo->mvstart[mi];\n      ptrdiff_t stride = ainfo->stride;\n      if (!(ainfo->flags&SHARP_PACKED))\n        mvstart*=2;\n      if ((ainfo->flags&SHARP_PACKED)&&(m==0))\n        {\n        if (flags&SHARP_DP)\n          CLEARLOOP(double, talm[mvstart+l*stride] = 0.;)\n        else\n          CLEARLOOP(float, talm[mvstart+l*stride] = 0.;)\n        }\n      else\n        {\n        stride*=2;\n        if (flags&SHARP_DP)\n          CLEARLOOP(double,talm[mvstart+l*stride]=talm[mvstart+l*stride+1]=0.;)\n        else\n          CLEARLOOP(float,talm[mvstart+l*stride]=talm[mvstart+l*stride+1]=0.;)\n        }\n\n#undef CLEARLOOP\n    }\n  }\n\nstatic void init_output (sharp_job *job)\n  {\n  if (job->flags&SHARP_ADD) return;\n  if (job->type == SHARP_MAP2ALM)\n    for (int i=0; i<job->ntrans*job->nalm; ++i)\n      clear_alm (job->ainfo,job->alm[i],job->flags);\n  else\n    for (int i=0; i<job->ntrans*job->nmaps; ++i)\n      fill_map (job->ginfo,job->map[i],0.,job->flags);\n  }\n\nstatic void alloc_phase (sharp_job *job, int nm, int ntheta)\n  {\n  if (job->type==SHARP_MAP2ALM)\n    {\n    if ((nm&1023)==0) nm+=3; // hack to avoid critical strides\n    job->s_m=2*job->ntrans*job->nmaps;\n    job->s_th=job->s_m*nm;\n    }\n  else\n    {\n    if ((ntheta&1023)==0) ntheta+=3; // hack to avoid critical strides\n    job->s_th=2*job->ntrans*job->nmaps;\n    job->s_m=job->s_th*ntheta;\n    }\n  job->phase=RALLOC(dcmplx,2*job->ntrans*job->nmaps*nm*ntheta);\n  }\n\nstatic void dealloc_phase (sharp_job *job)\n  { DEALLOC(job->phase); }\n\nstatic void alloc_almtmp (sharp_job *job, int lmax)\n  { job->almtmp=RALLOC(dcmplx,job->ntrans*job->nalm*(lmax+1)); }\n\nstatic void dealloc_almtmp (sharp_job *job)\n  { DEALLOC(job->almtmp); }\n\nstatic void alm2almtmp (sharp_job *job, int lmax, int mi)\n  {\n\n#define COPY_LOOP(real_t, source_t, expr_of_x)                      \\\n  for (int l=job->ainfo->mval[mi]; l<=lmax; ++l)            \\\n    for (int i=0; i<job->ntrans*job->nalm; ++i)             \\\n      {                                                     \\\n        source_t x = *(source_t *)(((real_t *)job->alm[i])+ofs+l*stride); \\\n        job->almtmp[job->ntrans*job->nalm*l+i] = expr_of_x; \\\n      }\n\n  if (job->type!=SHARP_MAP2ALM)\n    {\n    ptrdiff_t ofs=job->ainfo->mvstart[mi];\n    int stride=job->ainfo->stride;\n    int m=job->ainfo->mval[mi];\n    /* in the case of SHARP_REAL_HARMONICS, phase2ring scales all the\n       coefficients by sqrt_one_half; here we must compensate to avoid scaling\n       m=0 */\n    double norm_m0=(job->flags&SHARP_REAL_HARMONICS) ? sqrt_two : 1.;\n    if (!(job->ainfo->flags&SHARP_PACKED))\n      ofs *= 2;\n    if (!((job->ainfo->flags&SHARP_PACKED)&&(m==0)))\n      stride *= 2;\n    if (job->spin==0)\n      {\n      if (m==0)\n        {\n        if (job->flags&SHARP_DP)\n          COPY_LOOP(double, double, x*norm_m0)\n        else\n          COPY_LOOP(float, float, x*norm_m0)\n        }\n      else\n        {\n        if (job->flags&SHARP_DP)\n          COPY_LOOP(double, dcmplx, x)\n        else\n          COPY_LOOP(float, fcmplx, x)\n        }\n      }\n    else\n      {\n      if (m==0)\n        {\n        if (job->flags&SHARP_DP)\n          COPY_LOOP(double, double, x*job->norm_l[l]*norm_m0)\n        else\n          COPY_LOOP(float, float, x*job->norm_l[l]*norm_m0)\n        }\n      else\n        {\n        if (job->flags&SHARP_DP)\n          COPY_LOOP(double, dcmplx, x*job->norm_l[l])\n        else\n          COPY_LOOP(float, fcmplx, x*job->norm_l[l])\n        }\n      }\n    }\n  else\n    SET_ARRAY(job->almtmp,job->ntrans*job->nalm*job->ainfo->mval[mi],\n              job->ntrans*job->nalm*(lmax+1),0.);\n\n#undef COPY_LOOP\n  }\n\nstatic void almtmp2alm (sharp_job *job, int lmax, int mi)\n  {\n\n#define COPY_LOOP(real_t, target_t, expr_of_x)               \\\n  for (int l=job->ainfo->mval[mi]; l<=lmax; ++l)             \\\n    for (int i=0; i<job->ntrans*job->nalm; ++i)              \\\n      {                                                      \\\n        dcmplx x = job->almtmp[job->ntrans*job->nalm*l+i];   \\\n        *(target_t *)(((real_t *)job->alm[i])+ofs+l*stride) += expr_of_x; \\\n      }\n\n  if (job->type != SHARP_MAP2ALM) return;\n  ptrdiff_t ofs=job->ainfo->mvstart[mi];\n  int stride=job->ainfo->stride;\n  int m=job->ainfo->mval[mi];\n  /* in the case of SHARP_REAL_HARMONICS, ring2phase scales all the\n     coefficients by sqrt_two; here we must compensate to avoid scaling\n     m=0 */\n  double norm_m0=(job->flags&SHARP_REAL_HARMONICS) ? sqrt_one_half : 1.;\n  if (!(job->ainfo->flags&SHARP_PACKED))\n    ofs *= 2;\n  if (!((job->ainfo->flags&SHARP_PACKED)&&(m==0)))\n    stride *= 2;\n  if (job->spin==0)\n    {\n    if (m==0)\n      {\n      if (job->flags&SHARP_DP)\n        COPY_LOOP(double, double, creal(x)*norm_m0)\n      else\n        COPY_LOOP(float, float, crealf(x)*norm_m0)\n      }\n    else\n      {\n      if (job->flags&SHARP_DP)\n        COPY_LOOP(double, dcmplx, x)\n      else\n        COPY_LOOP(float, fcmplx, (fcmplx)x)\n      }\n    }\n  else\n    {\n    if (m==0)\n      {\n      if (job->flags&SHARP_DP)\n        COPY_LOOP(double, double, creal(x)*job->norm_l[l]*norm_m0)\n      else\n        COPY_LOOP(float, fcmplx, (float)(creal(x)*job->norm_l[l]*norm_m0))\n      }\n    else\n      {\n      if (job->flags&SHARP_DP)\n        COPY_LOOP(double, dcmplx, x*job->norm_l[l])\n      else\n        COPY_LOOP(float, fcmplx, (fcmplx)(x*job->norm_l[l]))\n      }\n    }\n\n#undef COPY_LOOP\n  }\n\nstatic void ringtmp2ring (sharp_job *job, sharp_ringinfo *ri, double *ringtmp,\n  int rstride)\n  {\n  double **dmap = (double **)job->map;\n  float  **fmap = (float  **)job->map;\n  for (int i=0; i<job->ntrans*job->nmaps; ++i)\n    for (int m=0; m<ri->nph; ++m)\n      if (job->flags & SHARP_DP)\n        dmap[i][ri->ofs+m*ri->stride] += ringtmp[i*rstride+m+1];\n      else\n        fmap[i][ri->ofs+m*ri->stride] += (float)ringtmp[i*rstride+m+1];\n  }\n\nstatic void ring2ringtmp (sharp_job *job, sharp_ringinfo *ri, double *ringtmp,\n  int rstride)\n  {\n  for (int i=0; i<job->ntrans*job->nmaps; ++i)\n    for (int m=0; m<ri->nph; ++m)\n      ringtmp[i*rstride+m+1] = (job->flags & SHARP_DP) ?\n        ((double *)(job->map[i]))[ri->ofs+m*ri->stride] :\n        ((float  *)(job->map[i]))[ri->ofs+m*ri->stride];\n  }\n\nstatic void ring2phase_direct (sharp_job *job, sharp_ringinfo *ri, int mmax,\n  dcmplx *phase)\n  {\n  if (ri->nph<0)\n    {\n    for (int i=0; i<job->ntrans*job->nmaps; ++i)\n      for (int m=0; m<=mmax; ++m)\n        phase[2*i+job->s_m*m]=0.;\n    }\n  else\n    {\n    UTIL_ASSERT(ri->nph==mmax+1,\"bad ring size\");\n    double wgt = (job->flags&SHARP_USE_WEIGHTS) ? (ri->nph*ri->weight) : 1.;\n    if (job->flags&SHARP_REAL_HARMONICS)\n      wgt *= sqrt_two;\n    for (int i=0; i<job->ntrans*job->nmaps; ++i)\n      for (int m=0; m<=mmax; ++m)\n        phase[2*i+job->s_m*m]= (job->flags & SHARP_DP) ?\n          ((dcmplx *)(job->map[i]))[ri->ofs+m*ri->stride]*wgt :\n          ((fcmplx *)(job->map[i]))[ri->ofs+m*ri->stride]*wgt;\n    }\n  }\nstatic void phase2ring_direct (sharp_job *job, sharp_ringinfo *ri, int mmax,\n  dcmplx *phase)\n  {\n  if (ri->nph<0) return;\n  UTIL_ASSERT(ri->nph==mmax+1,\"bad ring size\");\n  dcmplx **dmap = (dcmplx **)job->map;\n  fcmplx **fmap = (fcmplx **)job->map;\n  double wgt = (job->flags&SHARP_USE_WEIGHTS) ? (ri->nph*ri->weight) : 1.;\n  if (job->flags&SHARP_REAL_HARMONICS)\n    wgt *= sqrt_one_half;\n  for (int i=0; i<job->ntrans*job->nmaps; ++i)\n    for (int m=0; m<=mmax; ++m)\n      if (job->flags & SHARP_DP)\n        dmap[i][ri->ofs+m*ri->stride] += wgt*phase[2*i+job->s_m*m];\n      else\n        fmap[i][ri->ofs+m*ri->stride] += (fcmplx)(wgt*phase[2*i+job->s_m*m]);\n  }\n\n//FIXME: set phase to zero if not SHARP_MAP2ALM?\nstatic void map2phase (sharp_job *job, int mmax, int llim, int ulim)\n  {\n  if (job->type != SHARP_MAP2ALM) return;\n  int pstride = job->s_m;\n  if (job->flags & SHARP_NO_FFT)\n    {\n    for (int ith=llim; ith<ulim; ++ith)\n      {\n      int dim2 = job->s_th*(ith-llim);\n      ring2phase_direct(job,&(job->ginfo->pair[ith].r1),mmax,\n        &(job->phase[dim2]));\n      ring2phase_direct(job,&(job->ginfo->pair[ith].r2),mmax,\n        &(job->phase[dim2+1]));\n      }\n    }\n  else\n    {\n#pragma omp parallel if ((job->flags&SHARP_NO_OPENMP)==0)\n{\n    ringhelper helper;\n    ringhelper_init(&helper);\n    int rstride=job->ginfo->nphmax+2;\n    double *ringtmp=RALLOC(double,job->ntrans*job->nmaps*rstride);\n#pragma omp for schedule(dynamic,1)\n    for (int ith=llim; ith<ulim; ++ith)\n      {\n      int dim2 = job->s_th*(ith-llim);\n      ring2ringtmp(job,&(job->ginfo->pair[ith].r1),ringtmp,rstride);\n      for (int i=0; i<job->ntrans*job->nmaps; ++i)\n        ringhelper_ring2phase (&helper,&(job->ginfo->pair[ith].r1),\n          &ringtmp[i*rstride],mmax,&job->phase[dim2+2*i],pstride,job->flags);\n      if (job->ginfo->pair[ith].r2.nph>0)\n        {\n        ring2ringtmp(job,&(job->ginfo->pair[ith].r2),ringtmp,rstride);\n        for (int i=0; i<job->ntrans*job->nmaps; ++i)\n          ringhelper_ring2phase (&helper,&(job->ginfo->pair[ith].r2),\n           &ringtmp[i*rstride],mmax,&job->phase[dim2+2*i+1],pstride,job->flags);\n        }\n      }\n    DEALLOC(ringtmp);\n    ringhelper_destroy(&helper);\n} /* end of parallel region */\n    }\n  }\n\nstatic void phase2map (sharp_job *job, int mmax, int llim, int ulim)\n  {\n  if (job->type == SHARP_MAP2ALM) return;\n  int pstride = job->s_m;\n  if (job->flags & SHARP_NO_FFT)\n    {\n    for (int ith=llim; ith<ulim; ++ith)\n      {\n      int dim2 = job->s_th*(ith-llim);\n      phase2ring_direct(job,&(job->ginfo->pair[ith].r1),mmax,\n        &(job->phase[dim2]));\n      phase2ring_direct(job,&(job->ginfo->pair[ith].r2),mmax,\n        &(job->phase[dim2+1]));\n      }\n    }\n  else\n    {\n#pragma omp parallel if ((job->flags&SHARP_NO_OPENMP)==0)\n{\n    ringhelper helper;\n    ringhelper_init(&helper);\n    int rstride=job->ginfo->nphmax+2;\n    double *ringtmp=RALLOC(double,job->ntrans*job->nmaps*rstride);\n#pragma omp for schedule(dynamic,1)\n    for (int ith=llim; ith<ulim; ++ith)\n      {\n      int dim2 = job->s_th*(ith-llim);\n      for (int i=0; i<job->ntrans*job->nmaps; ++i)\n        ringhelper_phase2ring (&helper,&(job->ginfo->pair[ith].r1),\n          &ringtmp[i*rstride],mmax,&job->phase[dim2+2*i],pstride,job->flags);\n      ringtmp2ring(job,&(job->ginfo->pair[ith].r1),ringtmp,rstride);\n      if (job->ginfo->pair[ith].r2.nph>0)\n        {\n        for (int i=0; i<job->ntrans*job->nmaps; ++i)\n          ringhelper_phase2ring (&helper,&(job->ginfo->pair[ith].r2),\n            &ringtmp[i*rstride],mmax,&job->phase[dim2+2*i+1],pstride,job->flags);\n        ringtmp2ring(job,&(job->ginfo->pair[ith].r2),ringtmp,rstride);\n        }\n      }\n    DEALLOC(ringtmp);\n    ringhelper_destroy(&helper);\n} /* end of parallel region */\n    }\n  }\n\nstatic void sharp_execute_job (sharp_job *job)\n  {\n  double timer=wallTime();\n  job->opcnt=0;\n  int lmax = job->ainfo->lmax,\n      mmax=sharp_get_mmax(job->ainfo->mval, job->ainfo->nm);\n\n  job->norm_l = (job->type==SHARP_ALM2MAP_DERIV1) ?\n     sharp_Ylmgen_get_d1norm (lmax) :\n     sharp_Ylmgen_get_norm (lmax, job->spin);\n\n/* clear output arrays if requested */\n  init_output (job);\n\n  int nchunks, chunksize;\n  get_chunk_info(job->ginfo->npairs,(job->flags&SHARP_NVMAX)*VLEN,&nchunks,\n    &chunksize);\n  alloc_phase (job,mmax+1,chunksize);\n\n/* chunk loop */\n  for (int chunk=0; chunk<nchunks; ++chunk)\n    {\n    int llim=chunk*chunksize, ulim=IMIN(llim+chunksize,job->ginfo->npairs);\n    int *ispair = RALLOC(int,ulim-llim);\n    int *mlim = RALLOC(int,ulim-llim);\n    double *cth = RALLOC(double,ulim-llim), *sth = RALLOC(double,ulim-llim);\n    for (int i=0; i<ulim-llim; ++i)\n      {\n      ispair[i] = job->ginfo->pair[i+llim].r2.nph>0;\n      cth[i] = job->ginfo->pair[i+llim].r1.cth;\n      sth[i] = job->ginfo->pair[i+llim].r1.sth;\n      mlim[i] = sharp_get_mlim(lmax, job->spin, sth[i], cth[i]);\n      }\n\n/* map->phase where necessary */\n    map2phase (job, mmax, llim, ulim);\n\n#pragma omp parallel if ((job->flags&SHARP_NO_OPENMP)==0)\n{\n    sharp_job ljob = *job;\n    ljob.opcnt=0;\n    sharp_Ylmgen_C generator;\n    sharp_Ylmgen_init (&generator,lmax,mmax,ljob.spin);\n    alloc_almtmp(&ljob,lmax);\n\n#pragma omp for schedule(dynamic,1)\n    for (int mi=0; mi<job->ainfo->nm; ++mi)\n      {\n/* alm->alm_tmp where necessary */\n      alm2almtmp (&ljob, lmax, mi);\n\n      inner_loop (&ljob, ispair, cth, sth, llim, ulim, &generator, mi, mlim);\n\n/* alm_tmp->alm where necessary */\n      almtmp2alm (&ljob, lmax, mi);\n      }\n\n    sharp_Ylmgen_destroy(&generator);\n    dealloc_almtmp(&ljob);\n\n#pragma omp critical\n    job->opcnt+=ljob.opcnt;\n} /* end of parallel region */\n\n/* phase->map where necessary */\n    phase2map (job, mmax, llim, ulim);\n\n    DEALLOC(ispair);\n    DEALLOC(mlim);\n    DEALLOC(cth);\n    DEALLOC(sth);\n    } /* end of chunk loop */\n\n  DEALLOC(job->norm_l);\n  dealloc_phase (job);\n  job->time=wallTime()-timer;\n  }\n\nstatic void sharp_build_job_common (sharp_job *job, sharp_jobtype type,\n  int spin, void *alm, void *map, const sharp_geom_info *geom_info,\n  const sharp_alm_info *alm_info, int ntrans, int flags)\n  {\n  UTIL_ASSERT((ntrans>0)&&(ntrans<=SHARP_MAXTRANS),\n    \"bad number of simultaneous transforms\");\n  if (type==SHARP_ALM2MAP_DERIV1) spin=1;\n  if (type==SHARP_MAP2ALM) flags|=SHARP_USE_WEIGHTS;\n  if (type==SHARP_Yt) type=SHARP_MAP2ALM;\n  if (type==SHARP_WY) { type=SHARP_ALM2MAP; flags|=SHARP_USE_WEIGHTS; }\n\n  UTIL_ASSERT((spin>=0)&&(spin<=alm_info->lmax), \"bad spin\");\n  job->type = type;\n  job->spin = spin;\n  job->norm_l = NULL;\n  job->nmaps = (type==SHARP_ALM2MAP_DERIV1) ? 2 : ((spin>0) ? 2 : 1);\n  job->nalm = (type==SHARP_ALM2MAP_DERIV1) ? 1 : ((spin>0) ? 2 : 1);\n  job->ginfo = geom_info;\n  job->ainfo = alm_info;\n  job->flags = flags;\n  if ((job->flags&SHARP_NVMAX)==0)\n    job->flags|=sharp_nv_oracle (type, spin, ntrans);\n  if (alm_info->flags&SHARP_REAL_HARMONICS)\n    job->flags|=SHARP_REAL_HARMONICS;\n  job->time = 0.;\n  job->opcnt = 0;\n  job->ntrans = ntrans;\n  job->alm=alm;\n  job->map=map;\n  }\n\nvoid sharp_execute (sharp_jobtype type, int spin, void *alm, void *map,\n  const sharp_geom_info *geom_info, const sharp_alm_info *alm_info, int ntrans,\n  int flags, double *time, unsigned long long *opcnt)\n  {\n  sharp_job job;\n  sharp_build_job_common (&job, type, spin, alm, map, geom_info, alm_info,\n    ntrans, flags);\n\n  sharp_execute_job (&job);\n  if (time!=NULL) *time = job.time;\n  if (opcnt!=NULL) *opcnt = job.opcnt;\n  }\n\nvoid sharp_set_chunksize_min(int new_chunksize_min)\n  { chunksize_min=new_chunksize_min; }\nvoid sharp_set_nchunks_max(int new_nchunks_max)\n  { nchunks_max=new_nchunks_max; }\n\nint sharp_get_nv_max (void)\n{ return 6; }\n\nstatic int sharp_oracle (sharp_jobtype type, int spin, int ntrans)\n  {\n  int lmax=511;\n  int mmax=(lmax+1)/2;\n  int nrings=(lmax+1)/4;\n  int ppring=1;\n\n  spin = (spin!=0) ? 2 : 0;\n\n  ptrdiff_t npix=(ptrdiff_t)nrings*ppring;\n  sharp_geom_info *tinfo;\n  sharp_make_gauss_geom_info (nrings, ppring, 0., 1, ppring, &tinfo);\n\n  ptrdiff_t nalms = ((mmax+1)*(mmax+2))/2 + (mmax+1)*(lmax-mmax);\n  int ncomp = ntrans*((spin==0) ? 1 : 2);\n\n  double **map;\n  ALLOC2D(map,double,ncomp,npix);\n  SET_ARRAY(map[0],0,npix*ncomp,0.);\n\n  sharp_alm_info *alms;\n  sharp_make_triangular_alm_info(lmax,mmax,1,&alms);\n\n  dcmplx **alm;\n  ALLOC2D(alm,dcmplx,ncomp,nalms);\n  SET_ARRAY(alm[0],0,nalms*ncomp,0.);\n\n  double time=1e30;\n  int nvbest=-1;\n\n  for (int nv=1; nv<=sharp_get_nv_max(); ++nv)\n    {\n    double time_acc=0.;\n    double jtime;\n    int ntries=0;\n    do\n      {\n      sharp_execute(type,spin,&alm[0],&map[0],tinfo,alms,ntrans,\n        nv|SHARP_DP|SHARP_NO_OPENMP,&jtime,NULL);\n\n      if (jtime<time) { time=jtime; nvbest=nv; }\n      time_acc+=jtime;\n      ++ntries;\n      }\n    while ((time_acc<0.02)&&(ntries<2));\n    }\n\n  DEALLOC2D(map);\n  DEALLOC2D(alm);\n\n  sharp_destroy_alm_info(alms);\n  sharp_destroy_geom_info(tinfo);\n  return nvbest;\n  }\n\nint sharp_nv_oracle (sharp_jobtype type, int spin, int ntrans)\n  {\n  static const int maxtr = 6;\n  static int nv_opt[6][2][5] = {\n    {{0,0,0,0,0},{0,0,0,0,0}},\n    {{0,0,0,0,0},{0,0,0,0,0}},\n    {{0,0,0,0,0},{0,0,0,0,0}},\n    {{0,0,0,0,0},{0,0,0,0,0}},\n    {{0,0,0,0,0},{0,0,0,0,0}},\n    {{0,0,0,0,0},{0,0,0,0,0}} };\n\n  if (type==SHARP_ALM2MAP_DERIV1) spin=1;\n  UTIL_ASSERT(type<5,\"bad type\");\n  UTIL_ASSERT((ntrans>0),\"bad number of simultaneous transforms\");\n  UTIL_ASSERT(spin>=0, \"bad spin\");\n  ntrans=IMIN(ntrans,maxtr);\n\n  if (nv_opt[ntrans-1][spin!=0][type]==0)\n    nv_opt[ntrans-1][spin!=0][type]=sharp_oracle(type,spin,ntrans);\n  return nv_opt[ntrans-1][spin!=0][type];\n  }\n\n#ifdef USE_MPI\n#include \"sharp_mpi.c\"\n\nint sharp_execute_mpi_maybe (void *pcomm, sharp_jobtype type, int spin,\n  void *alm, void *map, const sharp_geom_info *geom_info,\n  const sharp_alm_info *alm_info, int ntrans, int flags, double *time,\n  unsigned long long *opcnt)\n  {\n  MPI_Comm comm = *(MPI_Comm*)pcomm;\n  sharp_execute_mpi((MPI_Comm)comm, type, spin, alm, map, geom_info, alm_info, ntrans,\n    flags, time, opcnt);\n  return 0;\n  }\n\n#else\n\nint sharp_execute_mpi_maybe (void *pcomm, sharp_jobtype type, int spin,\n  void *alm, void *map, const sharp_geom_info *geom_info,\n  const sharp_alm_info *alm_info, int ntrans, int flags, double *time,\n  unsigned long long *opcnt)\n  {\n  /* Suppress unused warning: */\n  (void)pcomm; (void)type; (void)spin; (void)alm; (void)map; (void)geom_info;\n  (void)alm_info; (void)ntrans; (void)flags; (void)time; (void)opcnt;\n  return SHARP_ERROR_NO_MPI;\n  }\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp.h\n *  Interface for the spherical transform library.\n *\n *  Copyright (C) 2006-2012 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_SHARP_H\n#define PLANCK_SHARP_H\n\n#ifdef __cplusplus\n#error This header file cannot be included from C++, only from C\n#endif\n\n#include <complex.h>\n\n#include \"sharp_lowlevel.h\"\n#include \"sharp_legendre.h\"\n#include \"sharp_legendre_roots.h\"\n#include \"sharp_legendre_table.h\"\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_almhelpers.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_almhelpers.c\n *  Spherical transform library\n *\n *  Copyright (C) 2008-2013 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#include \"sharp_almhelpers.h\"\n#include \"c_utils.h\"\n\nvoid sharp_make_triangular_alm_info (int lmax, int mmax, int stride,\n  sharp_alm_info **alm_info)\n  {\n  sharp_alm_info *info = RALLOC(sharp_alm_info,1);\n  info->lmax = lmax;\n  info->nm = mmax+1;\n  info->mval = RALLOC(int,mmax+1);\n  info->mvstart = RALLOC(ptrdiff_t,mmax+1);\n  info->stride = stride;\n  info->flags = 0;\n  ptrdiff_t tval = 2*lmax+1;\n  for (ptrdiff_t m=0; m<=mmax; ++m)\n    {\n    info->mval[m] = m;\n    info->mvstart[m] = stride*((m*(tval-m))>>1);\n    }\n  *alm_info = info;\n  }\n\nvoid sharp_make_rectangular_alm_info (int lmax, int mmax, int stride,\n  sharp_alm_info **alm_info)\n  {\n  sharp_alm_info *info = RALLOC(sharp_alm_info,1);\n  info->lmax = lmax;\n  info->nm = mmax+1;\n  info->mval = RALLOC(int,mmax+1);\n  info->mvstart = RALLOC(ptrdiff_t,mmax+1);\n  info->stride = stride;\n  info->flags = 0;\n  for (ptrdiff_t m=0; m<=mmax; ++m)\n    {\n    info->mval[m] = m;\n    info->mvstart[m] = stride*m*(lmax+1);\n    }\n  *alm_info = info;\n  }\n\nvoid sharp_make_mmajor_real_packed_alm_info (int lmax, int stride,\n  int nm, const int *ms, sharp_alm_info **alm_info)\n  {\n  ptrdiff_t idx;\n  int f;\n  sharp_alm_info *info = RALLOC(sharp_alm_info,1);\n  info->lmax = lmax;\n  info->nm = nm;\n  info->mval = RALLOC(int,nm);\n  info->mvstart = RALLOC(ptrdiff_t,nm);\n  info->stride = stride;\n  info->flags = SHARP_PACKED | SHARP_REAL_HARMONICS;\n  idx = 0;  /* tracks the number of 'consumed' elements so far; need to correct by m */\n  for (int im=0; im!=nm; ++im)\n    {\n    int m=(ms==NULL)?im:ms[im];\n    f = (m==0) ? 1 : 2;\n    info->mval[im] = m;\n    info->mvstart[im] = stride * (idx - f * m);\n    idx += f * (lmax + 1 - m);\n    }\n  *alm_info = info;\n  }\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_almhelpers.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_almhelpers.h\n *  SHARP helper function for the creation of a_lm data structures\n *\n *  Copyright (C) 2008-2011 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_SHARP_ALMHELPERS_H\n#define PLANCK_SHARP_ALMHELPERS_H\n\n#include \"sharp_lowlevel.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*! Initialises an a_lm data structure according to the scheme used by\n    Healpix_cxx.\n    \\ingroup almgroup */\nvoid sharp_make_triangular_alm_info (int lmax, int mmax, int stride,\n  sharp_alm_info **alm_info);\n\n/*! Initialises an a_lm data structure according to the scheme used by\n    Fortran Healpix\n    \\ingroup almgroup */\nvoid sharp_make_rectangular_alm_info (int lmax, int mmax, int stride,\n  sharp_alm_info **alm_info);\n\n/*! Initialises alm_info for mmajor, real, packed spherical harmonics.\n    Pass \\a mmax + 1 to nm and NULL to \\a ms in order to use everything;\n    otherwise you can pick a subset of m to process (should only be used\n    for MPI parallelization).\n    \\ingroup almgroup */\nvoid sharp_make_mmajor_real_packed_alm_info (int lmax, int stride,\n  int nm, const int *ms, sharp_alm_info **alm_info);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_announce.c",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_announce.c\n *  Banner for module startup\n *\n *  Copyright (C) 2012 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#ifdef _OPENMP\n#include <omp.h>\n#endif\n#ifdef USE_MPI\n#include <mpi.h>\n#endif\n\n#include \"sharp_announce.h\"\n#include \"sharp_vecutil.h\"\n\nstatic void OpenMP_status(void)\n  {\n#ifndef _OPENMP\n  printf(\"OpenMP: not supported by this binary\\n\");\n#else\n  int threads = omp_get_max_threads();\n  if (threads>1)\n    printf(\"OpenMP active: max. %d threads.\\n\",threads);\n  else\n    printf(\"OpenMP active, but running with 1 thread only.\\n\");\n#endif\n  }\n\nstatic void MPI_status(void)\n  {\n#ifndef USE_MPI\n  printf(\"MPI: not supported by this binary\\n\");\n#else\n  int tasks;\n  MPI_Comm_size(MPI_COMM_WORLD,&tasks);\n  if (tasks>1)\n    printf(\"MPI active with %d tasks.\\n\",tasks);\n  else\n    printf(\"MPI active, but running with 1 task only.\\n\");\n#endif\n  }\n\nstatic void vecmath_status(void)\n  { printf(\"Supported vector length: %d\\n\",VLEN); }\n\nvoid sharp_announce (const char *name)\n  {\n  size_t m, nlen=strlen(name);\n  printf(\"\\n+-\");\n  for (m=0; m<nlen; ++m) printf(\"-\");\n  printf(\"-+\\n\");\n  printf(\"| %s |\\n\", name);\n  printf(\"+-\");\n  for (m=0; m<nlen; ++m) printf(\"-\");\n  printf(\"-+\\n\\n\");\n  vecmath_status();\n  OpenMP_status();\n  MPI_status();\n  printf(\"\\n\");\n  }\n\nvoid sharp_module_startup (const char *name, int argc, int argc_expected,\n  const char *argv_expected, int verbose)\n  {\n  if (verbose) sharp_announce (name);\n  if (argc==argc_expected) return;\n  if (verbose) fprintf(stderr, \"Usage: %s %s\\n\", name, argv_expected);\n  exit(1);\n  }\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_announce.h",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_announce.h\n *  Banner for module startup\n *\n *  Copyright (C) 2012 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef SHARP_ANNOUNCE_H\n#define SHARP_ANNOUNCE_H\n\nvoid sharp_announce (const char *name);\nvoid sharp_module_startup (const char *name, int argc, int argc_expected,\n  const char *argv_expected, int verbose);\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_complex_hacks.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*  \\file sharp_complex_hacks.h\n *  support for converting vector types and complex numbers\n *\n *  Copyright (C) 2012,2013 Max-Planck-Society\n *  Author: Martin Reinecke\n */\n\n#ifndef SHARP_COMPLEX_HACKS_H\n#define SHARP_COMPLEX_HACKS_H\n\n#ifdef __cplusplus\n#error This header file cannot be included from C++, only from C\n#endif\n\n#include <math.h>\n#include <complex.h>\n#include \"sharp_vecsupport.h\"\n\n#define UNSAFE_CODE\n\n#if (VLEN==1)\n\nstatic inline complex double vhsum_cmplx(Tv a, Tv b)\n  { return a+_Complex_I*b; }\n\nstatic inline void vhsum_cmplx2 (Tv a, Tv b, Tv c, Tv d,\n  complex double * restrict c1, complex double * restrict c2)\n  { *c1 += a+_Complex_I*b; *c2 += c+_Complex_I*d; }\n\n#endif\n\n#if (VLEN==2)\n\nstatic inline complex double vhsum_cmplx (Tv a, Tv b)\n  {\n#if defined(__SSE3__)\n  Tv tmp = _mm_hadd_pd(a,b);\n#else\n  Tv tmp = vadd(_mm_shuffle_pd(a,b,_MM_SHUFFLE2(0,1)),\n                _mm_shuffle_pd(a,b,_MM_SHUFFLE2(1,0)));\n#endif\n  union {Tv v; complex double c; } u;\n  u.v=tmp; return u.c;\n  }\n\nstatic inline void vhsum_cmplx2 (Tv a, Tv b, Tv c,\n  Tv d, complex double * restrict c1, complex double * restrict c2)\n  {\n#ifdef UNSAFE_CODE\n#if defined(__SSE3__)\n  vaddeq(*((__m128d *)c1),_mm_hadd_pd(a,b));\n  vaddeq(*((__m128d *)c2),_mm_hadd_pd(c,d));\n#else\n  vaddeq(*((__m128d *)c1),vadd(_mm_shuffle_pd(a,b,_MM_SHUFFLE2(0,1)),\n                               _mm_shuffle_pd(a,b,_MM_SHUFFLE2(1,0))));\n  vaddeq(*((__m128d *)c2),vadd(_mm_shuffle_pd(c,d,_MM_SHUFFLE2(0,1)),\n                               _mm_shuffle_pd(c,d,_MM_SHUFFLE2(1,0))));\n#endif\n#else\n  union {Tv v; complex double c; } u1, u2;\n#if defined(__SSE3__)\n  u1.v = _mm_hadd_pd(a,b); u2.v=_mm_hadd_pd(c,d);\n#else\n  u1.v = vadd(_mm_shuffle_pd(a,b,_MM_SHUFFLE2(0,1)),\n              _mm_shuffle_pd(a,b,_MM_SHUFFLE2(1,0)));\n  u2.v = vadd(_mm_shuffle_pd(c,d,_MM_SHUFFLE2(0,1)),\n              _mm_shuffle_pd(c,d,_MM_SHUFFLE2(1,0)));\n#endif\n  *c1+=u1.c; *c2+=u2.c;\n#endif\n  }\n\n#endif\n\n#if (VLEN==4)\n\nstatic inline complex double vhsum_cmplx (Tv a, Tv b)\n  {\n  Tv tmp=_mm256_hadd_pd(a,b);\n  Tv tmp2=_mm256_permute2f128_pd(tmp,tmp,1);\n  tmp=_mm256_add_pd(tmp,tmp2);\n#ifdef UNSAFE_CODE\n  complex double ret;\n  *((__m128d *)&ret)=_mm256_extractf128_pd(tmp, 0);\n  return ret;\n#else\n  union {Tv v; complex double c[2]; } u;\n  u.v=tmp; return u.c[0];\n#endif\n  }\n\nstatic inline void vhsum_cmplx2 (Tv a, Tv b, Tv c, Tv d,\n  complex double * restrict c1, complex double * restrict c2)\n  {\n  Tv tmp1=_mm256_hadd_pd(a,b), tmp2=_mm256_hadd_pd(c,d);\n  Tv tmp3=_mm256_permute2f128_pd(tmp1,tmp2,49),\n     tmp4=_mm256_permute2f128_pd(tmp1,tmp2,32);\n  tmp1=vadd(tmp3,tmp4);\n#ifdef UNSAFE_CODE\n  *((__m128d *)c1)=_mm_add_pd(*((__m128d *)c1),_mm256_extractf128_pd(tmp1, 0));\n  *((__m128d *)c2)=_mm_add_pd(*((__m128d *)c2),_mm256_extractf128_pd(tmp1, 1));\n#else\n  union {Tv v; complex double c[2]; } u;\n  u.v=tmp1;\n  *c1+=u.c[0]; *c2+=u.c[1];\n#endif\n  }\n\n#endif\n\n#if (VLEN==8)\n\nstatic inline complex double vhsum_cmplx(Tv a, Tv b)\n  { return _mm512_reduce_add_pd(a)+_Complex_I*_mm512_reduce_add_pd(b); }\n\nstatic inline void vhsum_cmplx2 (Tv a, Tv b, Tv c, Tv d,\n  complex double * restrict c1, complex double * restrict c2)\n  {\n  *c1 += _mm512_reduce_add_pd(a)+_Complex_I*_mm512_reduce_add_pd(b);\n  *c2 += _mm512_reduce_add_pd(c)+_Complex_I*_mm512_reduce_add_pd(d);\n  }\n\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_core.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_core.c\n *  Computational core\n *\n *  Copyright (C) 2012-2013 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#include <complex.h>\n#include <math.h>\n#include <string.h>\n#include \"sharp_vecsupport.h\"\n#include \"sharp_complex_hacks.h\"\n#include \"sharp_ylmgen_c.h\"\n#include \"sharp.h\"\n#include \"sharp_core.h\"\n#include \"c_utils.h\"\n\ntypedef complex double dcmplx;\n\n// must be in the range [0;6]\n#define MAXJOB_SPECIAL 2\n\n#define XCONCAT2(a,b) a##_##b\n#define CONCAT2(a,b) XCONCAT2(a,b)\n#define XCONCAT3(a,b,c) a##_##b##_##c\n#define CONCAT3(a,b,c) XCONCAT3(a,b,c)\n\n#define nvec 1\n#include \"sharp_core_inchelper.c\"\n#undef nvec\n\n#define nvec 2\n#include \"sharp_core_inchelper.c\"\n#undef nvec\n\n#define nvec 3\n#include \"sharp_core_inchelper.c\"\n#undef nvec\n\n#define nvec 4\n#include \"sharp_core_inchelper.c\"\n#undef nvec\n\n#define nvec 5\n#include \"sharp_core_inchelper.c\"\n#undef nvec\n\n#define nvec 6\n#include \"sharp_core_inchelper.c\"\n#undef nvec\n\nvoid inner_loop (sharp_job *job, const int *ispair,const double *cth,\n  const double *sth, int llim, int ulim, sharp_Ylmgen_C *gen, int mi,\n  const int *mlim)\n  {\n  int njobs=job->ntrans, nv=job->flags&SHARP_NVMAX;\n  if (njobs<=MAXJOB_SPECIAL)\n    {\n    switch (njobs*16+nv)\n      {\n#if ((MAXJOB_SPECIAL>=1)&&(SHARP_MAXTRANS>=1))\n      case 0x11:\n        CONCAT3(inner_loop,1,1) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x12:\n        CONCAT3(inner_loop,2,1) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x13:\n        CONCAT3(inner_loop,3,1) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x14:\n        CONCAT3(inner_loop,4,1) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x15:\n        CONCAT3(inner_loop,5,1) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x16:\n        CONCAT3(inner_loop,6,1) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n#endif\n#if ((MAXJOB_SPECIAL>=2)&&(SHARP_MAXTRANS>=2))\n      case 0x21:\n        CONCAT3(inner_loop,1,2) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x22:\n        CONCAT3(inner_loop,2,2) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x23:\n        CONCAT3(inner_loop,3,2) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x24:\n        CONCAT3(inner_loop,4,2) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x25:\n        CONCAT3(inner_loop,5,2) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x26:\n        CONCAT3(inner_loop,6,2) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n#endif\n#if ((MAXJOB_SPECIAL>=3)&&(SHARP_MAXTRANS>=3))\n      case 0x31:\n        CONCAT3(inner_loop,1,3) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x32:\n        CONCAT3(inner_loop,2,3) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x33:\n        CONCAT3(inner_loop,3,3) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x34:\n        CONCAT3(inner_loop,4,3) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x35:\n        CONCAT3(inner_loop,5,3) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x36:\n        CONCAT3(inner_loop,6,3) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n#endif\n#if ((MAXJOB_SPECIAL>=4)&&(SHARP_MAXTRANS>=4))\n      case 0x41:\n        CONCAT3(inner_loop,1,4) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x42:\n        CONCAT3(inner_loop,2,4) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x43:\n        CONCAT3(inner_loop,3,4) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x44:\n        CONCAT3(inner_loop,4,4) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x45:\n        CONCAT3(inner_loop,5,4) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x46:\n        CONCAT3(inner_loop,6,4) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n#endif\n#if ((MAXJOB_SPECIAL>=5)&&(SHARP_MAXTRANS>=5))\n      case 0x51:\n        CONCAT3(inner_loop,1,5) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x52:\n        CONCAT3(inner_loop,2,5) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x53:\n        CONCAT3(inner_loop,3,5) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x54:\n        CONCAT3(inner_loop,4,5) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x55:\n        CONCAT3(inner_loop,5,5) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x56:\n        CONCAT3(inner_loop,6,5) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n#endif\n#if ((MAXJOB_SPECIAL>=6)&&(SHARP_MAXTRANS>=6))\n      case 0x61:\n        CONCAT3(inner_loop,1,6) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x62:\n        CONCAT3(inner_loop,2,6) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x63:\n        CONCAT3(inner_loop,3,6) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x64:\n        CONCAT3(inner_loop,4,6) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x65:\n        CONCAT3(inner_loop,5,6) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n      case 0x66:\n        CONCAT3(inner_loop,6,6) (job, ispair,cth,sth,llim,ulim,gen,mi,mlim);\n        return;\n#endif\n      }\n    }\n#if (SHARP_MAXTRANS>MAXJOB_SPECIAL)\n  else\n    {\n    switch (nv)\n      {\n      case 1:\n        CONCAT2(inner_loop,1)\n          (job, ispair,cth,sth,llim,ulim,gen,mi,mlim,job->ntrans);\n        return;\n      case 2:\n        CONCAT2(inner_loop,2)\n          (job, ispair,cth,sth,llim,ulim,gen,mi,mlim,job->ntrans);\n        return;\n      case 3:\n        CONCAT2(inner_loop,3)\n          (job, ispair,cth,sth,llim,ulim,gen,mi,mlim,job->ntrans);\n        return;\n      case 4:\n        CONCAT2(inner_loop,4)\n          (job, ispair,cth,sth,llim,ulim,gen,mi,mlim,job->ntrans);\n        return;\n      case 5:\n        CONCAT2(inner_loop,5)\n          (job, ispair,cth,sth,llim,ulim,gen,mi,mlim,job->ntrans);\n        return;\n      case 6:\n        CONCAT2(inner_loop,6)\n          (job, ispair,cth,sth,llim,ulim,gen,mi,mlim,job->ntrans);\n        return;\n      }\n    }\n#endif\n  UTIL_FAIL(\"Incorrect vector parameters\");\n  }\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_core.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_core.h\n *  Interface for the computational core\n *\n *  Copyright (C) 2012-2013 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_SHARP_CORE_H\n#define PLANCK_SHARP_CORE_H\n\n#include \"sharp_internal.h\"\n#include \"sharp_ylmgen_c.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid inner_loop (sharp_job *job, const int *ispair,const double *cth,\n  const double *sth, int llim, int ulim, sharp_Ylmgen_C *gen, int mi,\n  const int *mlim);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_core_inc.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_core_inc.c\n *  Type-dependent code for the computational core\n *\n *  Copyright (C) 2012 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\ntypedef struct\n  { Tv v[nvec]; } Tb;\n\ntypedef union\n  { Tb b; double s[VLEN*nvec]; } Y(Tbu);\n\ntypedef struct\n  { Tb r, i; } Y(Tbri);\n\ntypedef struct\n  { Tb qr, qi, ur, ui; } Y(Tbqu);\n\ntypedef struct\n  { double r[VLEN*nvec], i[VLEN*nvec]; } Y(Tsri);\n\ntypedef struct\n  { double qr[VLEN*nvec],qi[VLEN*nvec],ur[VLEN*nvec],ui[VLEN*nvec]; } Y(Tsqu);\n\ntypedef union\n  { Y(Tbri) b; Y(Tsri)s; } Y(Tburi);\n\ntypedef union\n  { Y(Tbqu) b; Y(Tsqu)s; } Y(Tbuqu);\n\nstatic inline Tb Y(Tbconst)(double val)\n  {\n  Tv v=vload(val);\n  Tb res;\n  for (int i=0; i<nvec; ++i) res.v[i]=v;\n  return res;\n  }\n\nstatic inline void Y(Tbmuleq1)(Tb * restrict a, double b)\n  { Tv v=vload(b); for (int i=0; i<nvec; ++i) vmuleq(a->v[i],v); }\n\nstatic inline Tb Y(Tbprod)(Tb a, Tb b)\n  { Tb r; for (int i=0; i<nvec; ++i) r.v[i]=vmul(a.v[i],b.v[i]); return r; }\n\nstatic inline void Y(Tbmuleq)(Tb * restrict a, Tb b)\n  { for (int i=0; i<nvec; ++i) vmuleq(a->v[i],b.v[i]); }\n\nstatic void Y(Tbnormalize) (Tb * restrict val, Tb * restrict scale,\n  double maxval)\n  {\n  const Tv vfsmall=vload(sharp_fsmall), vfbig=vload(sharp_fbig);\n  const Tv vfmin=vload(sharp_fsmall*maxval), vfmax=vload(maxval);\n  for (int i=0;i<nvec; ++i)\n    {\n    Tm mask = vgt(vabs(val->v[i]),vfmax);\n    while (vanyTrue(mask))\n      {\n      vmuleq_mask(mask,val->v[i],vfsmall);\n      vaddeq_mask(mask,scale->v[i],vone);\n      mask = vgt(vabs(val->v[i]),vfmax);\n      }\n    mask = vand_mask(vlt(vabs(val->v[i]),vfmin),vne(val->v[i],vzero));\n    while (vanyTrue(mask))\n      {\n      vmuleq_mask(mask,val->v[i],vfbig);\n      vsubeq_mask(mask,scale->v[i],vone);\n      mask = vand_mask(vlt(vabs(val->v[i]),vfmin),vne(val->v[i],vzero));\n      }\n    }\n  }\n\nstatic void Y(mypow) (Tb val, int npow, Tb * restrict resd,\n  Tb * restrict ress)\n  {\n  Tb scale=Y(Tbconst)(0.), scaleint=Y(Tbconst)(0.), res=Y(Tbconst)(1.);\n\n  Y(Tbnormalize)(&val,&scaleint,sharp_fbighalf);\n\n  do\n    {\n    if (npow&1)\n      {\n      for (int i=0; i<nvec; ++i)\n        {\n        vmuleq(res.v[i],val.v[i]);\n        vaddeq(scale.v[i],scaleint.v[i]);\n        }\n      Y(Tbnormalize)(&res,&scale,sharp_fbighalf);\n      }\n    for (int i=0; i<nvec; ++i)\n      {\n      vmuleq(val.v[i],val.v[i]);\n      vaddeq(scaleint.v[i],scaleint.v[i]);\n      }\n    Y(Tbnormalize)(&val,&scaleint,sharp_fbighalf);\n    }\n  while(npow>>=1);\n\n  *resd=res;\n  *ress=scale;\n  }\n\nstatic inline int Y(rescale) (Tb * restrict lam1, Tb * restrict lam2,\n  Tb * restrict scale)\n  {\n  int did_scale=0;\n  for (int i=0;i<nvec; ++i)\n    {\n    Tm mask = vgt(vabs(lam2->v[i]),vload(sharp_ftol));\n    if (vanyTrue(mask))\n      {\n      did_scale=1;\n      vmuleq_mask(mask,lam1->v[i],vload(sharp_fsmall));\n      vmuleq_mask(mask,lam2->v[i],vload(sharp_fsmall));\n      vaddeq_mask(mask,scale->v[i],vone);\n      }\n    }\n  return did_scale;\n  }\n\nstatic inline int Y(TballLt)(Tb a,double b)\n  {\n  Tv vb=vload(b);\n  Tm res=vlt(a.v[0],vb);\n  for (int i=1; i<nvec; ++i)\n    res=vand_mask(res,vlt(a.v[i],vb));\n  return vallTrue(res);\n  }\nstatic inline int Y(TballGt)(Tb a,double b)\n  {\n  Tv vb=vload(b);\n  Tm res=vgt(a.v[0],vb);\n  for (int i=1; i<nvec; ++i)\n    res=vand_mask(res,vgt(a.v[i],vb));\n  return vallTrue(res);\n  }\nstatic inline int Y(TballGe)(Tb a,double b)\n  {\n  Tv vb=vload(b);\n  Tm res=vge(a.v[0],vb);\n  for (int i=1; i<nvec; ++i)\n    res=vand_mask(res,vge(a.v[i],vb));\n  return vallTrue(res);\n  }\n\nstatic void Y(getCorfac)(Tb scale, Tb * restrict corfac,\n  const double * restrict cf)\n  {\n  Y(Tbu) sc, corf;\n  sc.b=scale;\n  for (int i=0; i<VLEN*nvec; ++i)\n    corf.s[i] = (sc.s[i]<sharp_minscale) ?\n      0. : cf[(int)(sc.s[i])-sharp_minscale];\n  *corfac=corf.b;\n  }\n\nstatic void Y(iter_to_ieee) (const Tb sth, Tb cth, int *l_,\n  Tb * restrict lam_1_, Tb * restrict lam_2_, Tb * restrict scale_,\n  const sharp_Ylmgen_C * restrict gen)\n  {\n  int l=gen->m;\n  Tb lam_1=Y(Tbconst)(0.), lam_2, scale;\n  Y(mypow) (sth,l,&lam_2,&scale);\n  Y(Tbmuleq1) (&lam_2,(gen->m&1) ? -gen->mfac[gen->m]:gen->mfac[gen->m]);\n  Y(Tbnormalize)(&lam_2,&scale,sharp_ftol);\n\n  int below_limit = Y(TballLt)(scale,sharp_limscale);\n  while (below_limit)\n    {\n    if (l+2>gen->lmax) {*l_=gen->lmax+1;return;}\n    Tv r0=vload(gen->rf[l].f[0]),r1=vload(gen->rf[l].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_1.v[i] = vsub(vmul(vmul(cth.v[i],lam_2.v[i]),r0),vmul(lam_1.v[i],r1));\n    r0=vload(gen->rf[l+1].f[0]); r1=vload(gen->rf[l+1].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_2.v[i] = vsub(vmul(vmul(cth.v[i],lam_1.v[i]),r0),vmul(lam_2.v[i],r1));\n    if (Y(rescale)(&lam_1,&lam_2,&scale))\n      below_limit = Y(TballLt)(scale,sharp_limscale);\n    l+=2;\n    }\n  *l_=l; *lam_1_=lam_1; *lam_2_=lam_2; *scale_=scale;\n  }\n\nstatic inline void Y(rec_step) (Tb * restrict rxp, Tb * restrict rxm,\n  Tb * restrict ryp, Tb * restrict rym, const Tb cth,\n  const sharp_ylmgen_dbl3 fx)\n  {\n  Tv fx0=vload(fx.f[0]),fx1=vload(fx.f[1]),fx2=vload(fx.f[2]);\n  for (int i=0; i<nvec; ++i)\n    {\n    rxp->v[i] = vsub(vmul(vsub(cth.v[i],fx1),vmul(fx0,ryp->v[i])),\n                vmul(fx2,rxp->v[i]));\n    rxm->v[i] = vsub(vmul(vadd(cth.v[i],fx1),vmul(fx0,rym->v[i])),\n                vmul(fx2,rxm->v[i]));\n    }\n  }\n\nstatic void Y(iter_to_ieee_spin) (const Tb cth, const Tb sth, int *l_,\n  Tb * rec1p_, Tb * rec1m_, Tb * rec2p_, Tb * rec2m_,\n  Tb * scalep_, Tb * scalem_, const sharp_Ylmgen_C * restrict gen)\n  {\n  const sharp_ylmgen_dbl3 * restrict fx = gen->fx;\n  Tb cth2, sth2;\n  for (int i=0; i<nvec; ++i)\n    {\n    cth2.v[i]=vsqrt(vmul(vadd(vone,cth.v[i]),vload(0.5)));\n    cth2.v[i]=vmax(cth2.v[i],vload(1e-15));\n    sth2.v[i]=vsqrt(vmul(vsub(vone,cth.v[i]),vload(0.5)));\n    sth2.v[i]=vmax(sth2.v[i],vload(1e-15));\n    Tm mask=vlt(sth.v[i],vzero);\n    Tm cmask=vand_mask(mask,vlt(cth.v[i],vzero));\n    vmuleq_mask(cmask,cth2.v[i],vload(-1.));\n    Tm smask=vand_mask(mask,vgt(cth.v[i],vzero));\n    vmuleq_mask(smask,sth2.v[i],vload(-1.));\n    }\n\n  Tb ccp, ccps, ssp, ssps, csp, csps, scp, scps;\n  Y(mypow)(cth2,gen->cosPow,&ccp,&ccps); Y(mypow)(sth2,gen->sinPow,&ssp,&ssps);\n  Y(mypow)(cth2,gen->sinPow,&csp,&csps); Y(mypow)(sth2,gen->cosPow,&scp,&scps);\n\n  Tb rec2p, rec2m, scalep, scalem;\n  Tb rec1p=Y(Tbconst)(0.), rec1m=Y(Tbconst)(0.);\n  Tv prefac=vload(gen->prefac[gen->m]),\n     prescale=vload(gen->fscale[gen->m]);\n  for (int i=0; i<nvec; ++i)\n    {\n    rec2p.v[i]=vmul(prefac,ccp.v[i]);\n    scalep.v[i]=vadd(prescale,ccps.v[i]);\n    rec2m.v[i]=vmul(prefac,csp.v[i]);\n    scalem.v[i]=vadd(prescale,csps.v[i]);\n    }\n  Y(Tbnormalize)(&rec2m,&scalem,sharp_fbighalf);\n  Y(Tbnormalize)(&rec2p,&scalep,sharp_fbighalf);\n  for (int i=0; i<nvec; ++i)\n    {\n    rec2p.v[i]=vmul(rec2p.v[i],ssp.v[i]);\n    scalep.v[i]=vadd(scalep.v[i],ssps.v[i]);\n    rec2m.v[i]=vmul(rec2m.v[i],scp.v[i]);\n    scalem.v[i]=vadd(scalem.v[i],scps.v[i]);\n    if (gen->preMinus_p)\n      rec2p.v[i]=vneg(rec2p.v[i]);\n    if (gen->preMinus_m)\n      rec2m.v[i]=vneg(rec2m.v[i]);\n    if (gen->s&1)\n      rec2p.v[i]=vneg(rec2p.v[i]);\n    }\n  Y(Tbnormalize)(&rec2m,&scalem,sharp_ftol);\n  Y(Tbnormalize)(&rec2p,&scalep,sharp_ftol);\n\n  int l=gen->mhi;\n\n  int below_limit = Y(TballLt)(scalep,sharp_limscale)\n                 && Y(TballLt)(scalem,sharp_limscale);\n  while (below_limit)\n    {\n    if (l+2>gen->lmax) {*l_=gen->lmax+1;return;}\n    Y(rec_step)(&rec1p,&rec1m,&rec2p,&rec2m,cth,fx[l+1]);\n    Y(rec_step)(&rec2p,&rec2m,&rec1p,&rec1m,cth,fx[l+2]);\n    if (Y(rescale)(&rec1p,&rec2p,&scalep) | Y(rescale)(&rec1m,&rec2m,&scalem))\n      below_limit = Y(TballLt)(scalep,sharp_limscale)\n                 && Y(TballLt)(scalem,sharp_limscale);\n    l+=2;\n    }\n\n  *l_=l;\n  *rec1p_=rec1p; *rec2p_=rec2p; *scalep_=scalep;\n  *rec1m_=rec1m; *rec2m_=rec2m; *scalem_=scalem;\n  }\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_core_inc2.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_core_inc2.c\n *  Type-dependent code for the computational core\n *\n *  Copyright (C) 2012-2013 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\nstatic void Z(alm2map_kernel) (const Tb cth, Y(Tbri) * restrict p1,\n  Y(Tbri) * restrict p2, Tb lam_1, Tb lam_2,\n  const sharp_ylmgen_dbl2 * restrict rf, const dcmplx * restrict alm,\n  int l, int lmax NJ1)\n  {\nif (njobs>1)\n  {\n  while (l<lmax-2)\n    {\n    Tb lam_3, lam_4;\n    Tv r0=vload(rf[l].f[0]),r1=vload(rf[l].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_3.v[i] = vsub(vmul(vmul(cth.v[i],lam_2.v[i]),r0),vmul(lam_1.v[i],r1));\n    r0=vload(rf[l+1].f[0]);r1=vload(rf[l+1].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_4.v[i] = vsub(vmul(vmul(cth.v[i],lam_3.v[i]),r0),vmul(lam_2.v[i],r1));\n    r0=vload(rf[l+2].f[0]);r1=vload(rf[l+2].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_1.v[i] = vsub(vmul(vmul(cth.v[i],lam_4.v[i]),r0),vmul(lam_3.v[i],r1));\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv ar2=vload(creal(alm[njobs*l+j])),\n         ai2=vload(cimag(alm[njobs*l+j])),\n         ar4=vload(creal(alm[njobs*(l+2)+j])),\n         ai4=vload(cimag(alm[njobs*(l+2)+j]));\n      for (int i=0; i<nvec; ++i)\n        {\n        vfmaaeq(p1[j].r.v[i],lam_2.v[i],ar2,lam_4.v[i],ar4);\n        vfmaaeq(p1[j].i.v[i],lam_2.v[i],ai2,lam_4.v[i],ai4);\n        }\n      Tv ar3=vload(creal(alm[njobs*(l+1)+j])),\n         ai3=vload(cimag(alm[njobs*(l+1)+j])),\n         ar1=vload(creal(alm[njobs*(l+3)+j])),\n         ai1=vload(cimag(alm[njobs*(l+3)+j]));\n      for (int i=0; i<nvec; ++i)\n        {\n        vfmaaeq(p2[j].r.v[i],lam_3.v[i],ar3,lam_1.v[i],ar1);\n        vfmaaeq(p2[j].i.v[i],lam_3.v[i],ai3,lam_1.v[i],ai1);\n        }\n      }\n    r0=vload(rf[l+3].f[0]);r1=vload(rf[l+3].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_2.v[i] = vsub(vmul(vmul(cth.v[i],lam_1.v[i]),r0),vmul(lam_4.v[i],r1));\n    l+=4;\n    }\n  }\n  while (l<lmax)\n    {\n    Tv r0=vload(rf[l].f[0]),r1=vload(rf[l].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_1.v[i] = vsub(vmul(vmul(cth.v[i],lam_2.v[i]),r0),vmul(lam_1.v[i],r1));\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv ar=vload(creal(alm[njobs*l+j])),\n         ai=vload(cimag(alm[njobs*l+j]));\n      for (int i=0; i<nvec; ++i)\n        {\n        vfmaeq(p1[j].r.v[i],lam_2.v[i],ar);\n        vfmaeq(p1[j].i.v[i],lam_2.v[i],ai);\n        }\n      ar=vload(creal(alm[njobs*(l+1)+j]));\n      ai=vload(cimag(alm[njobs*(l+1)+j]));\n      for (int i=0; i<nvec; ++i)\n        {\n        vfmaeq(p2[j].r.v[i],lam_1.v[i],ar);\n        vfmaeq(p2[j].i.v[i],lam_1.v[i],ai);\n        }\n      }\n    r0=vload(rf[l+1].f[0]);r1=vload(rf[l+1].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_2.v[i] = vsub(vmul(vmul(cth.v[i],lam_1.v[i]),r0),vmul(lam_2.v[i],r1));\n    l+=2;\n    }\n  if (l==lmax)\n    {\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv ar=vload(creal(alm[njobs*l+j])),ai=vload(cimag(alm[njobs*l+j]));\n      for (int i=0; i<nvec; ++i)\n        {\n        vfmaeq(p1[j].r.v[i],lam_2.v[i],ar);\n        vfmaeq(p1[j].i.v[i],lam_2.v[i],ai);\n        }\n      }\n    }\n  }\n\nstatic void Z(map2alm_kernel) (const Tb cth, const Y(Tbri) * restrict p1,\n  const Y(Tbri) * restrict p2, Tb lam_1, Tb lam_2,\n  const sharp_ylmgen_dbl2 * restrict rf, dcmplx * restrict alm, int l, int lmax\n  NJ1)\n  {\n  while (l<lmax)\n    {\n    Tv r0=vload(rf[l].f[0]),r1=vload(rf[l].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_1.v[i] = vsub(vmul(vmul(cth.v[i],lam_2.v[i]),r0),vmul(lam_1.v[i],r1));\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv tr1=vzero, ti1=vzero, tr2=vzero, ti2=vzero;\n      for (int i=0; i<nvec; ++i)\n        {\n        vfmaeq(tr1,lam_2.v[i],p1[j].r.v[i]);\n        vfmaeq(ti1,lam_2.v[i],p1[j].i.v[i]);\n        }\n      for (int i=0; i<nvec; ++i)\n        {\n        vfmaeq(tr2,lam_1.v[i],p2[j].r.v[i]);\n        vfmaeq(ti2,lam_1.v[i],p2[j].i.v[i]);\n        }\n      vhsum_cmplx2(tr1,ti1,tr2,ti2,&alm[l*njobs+j],&alm[(l+1)*njobs+j]);\n      }\n    r0=vload(rf[l+1].f[0]);r1=vload(rf[l+1].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_2.v[i] = vsub(vmul(vmul(cth.v[i],lam_1.v[i]),r0),vmul(lam_2.v[i],r1));\n    l+=2;\n    }\n  if (l==lmax)\n    {\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv tre=vzero, tim=vzero;\n      for (int i=0; i<nvec; ++i)\n        {\n        vfmaeq(tre,lam_2.v[i],p1[j].r.v[i]);\n        vfmaeq(tim,lam_2.v[i],p1[j].i.v[i]);\n        }\n      alm[l*njobs+j]+=vhsum_cmplx(tre,tim);\n      }\n    }\n  }\n\nstatic void Z(calc_alm2map) (const Tb cth, const Tb sth,\n  const sharp_Ylmgen_C *gen, sharp_job *job, Y(Tbri) * restrict p1,\n  Y(Tbri) * restrict p2 NJ1)\n  {\n  int l,lmax=gen->lmax;\n  Tb lam_1,lam_2,scale;\n  Y(iter_to_ieee) (sth,cth,&l,&lam_1,&lam_2,&scale,gen);\n  job->opcnt += (l-gen->m) * 4*VLEN*nvec;\n  if (l>lmax) return;\n  job->opcnt += (lmax+1-l) * (4+4*njobs)*VLEN*nvec;\n\n  Tb corfac;\n  Y(getCorfac)(scale,&corfac,gen->cf);\n  const sharp_ylmgen_dbl2 * restrict rf = gen->rf;\n  const dcmplx * restrict alm=job->almtmp;\n  int full_ieee = Y(TballGe)(scale,sharp_minscale);\n  while (!full_ieee)\n    {\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv ar=vload(creal(alm[njobs*l+j])),ai=vload(cimag(alm[njobs*l+j]));\n      for (int i=0; i<nvec; ++i)\n        {\n        Tv tmp=vmul(lam_2.v[i],corfac.v[i]);\n        vfmaeq(p1[j].r.v[i],tmp,ar);\n        vfmaeq(p1[j].i.v[i],tmp,ai);\n        }\n      }\n    if (++l>lmax) break;\n    Tv r0=vload(rf[l-1].f[0]),r1=vload(rf[l-1].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_1.v[i] = vsub(vmul(vmul(cth.v[i],lam_2.v[i]),r0),vmul(lam_1.v[i],r1));\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv ar=vload(creal(alm[njobs*l+j])),ai=vload(cimag(alm[njobs*l+j]));\n      for (int i=0; i<nvec; ++i)\n        {\n        Tv tmp=vmul(lam_1.v[i],corfac.v[i]);\n        vfmaeq(p2[j].r.v[i],tmp,ar);\n        vfmaeq(p2[j].i.v[i],tmp,ai);\n        }\n      }\n    if (++l>lmax) break;\n    r0=vload(rf[l-1].f[0]); r1=vload(rf[l-1].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_2.v[i] = vsub(vmul(vmul(cth.v[i],lam_1.v[i]),r0),vmul(lam_2.v[i],r1));\n    if (Y(rescale)(&lam_1,&lam_2,&scale))\n      {\n      Y(getCorfac)(scale,&corfac,gen->cf);\n      full_ieee = Y(TballGe)(scale,sharp_minscale);\n      }\n    }\n  if (l>lmax) return;\n\n  Y(Tbmuleq)(&lam_1,corfac); Y(Tbmuleq)(&lam_2,corfac);\n  Z(alm2map_kernel) (cth, p1, p2, lam_1, lam_2, rf, alm, l, lmax NJ2);\n  }\n\nstatic void Z(calc_map2alm) (const Tb cth, const Tb sth,\n  const sharp_Ylmgen_C *gen, sharp_job *job, const Y(Tbri) * restrict p1,\n  const Y(Tbri) * restrict p2 NJ1)\n  {\n  int lmax=gen->lmax;\n  Tb lam_1,lam_2,scale;\n  int l=gen->m;\n  Y(iter_to_ieee) (sth,cth,&l,&lam_1,&lam_2,&scale,gen);\n  job->opcnt += (l-gen->m) * 4*VLEN*nvec;\n  if (l>lmax) return;\n  job->opcnt += (lmax+1-l) * (4+4*njobs)*VLEN*nvec;\n\n  const sharp_ylmgen_dbl2 * restrict rf = gen->rf;\n  Tb corfac;\n  Y(getCorfac)(scale,&corfac,gen->cf);\n  dcmplx * restrict alm=job->almtmp;\n  int full_ieee = Y(TballGe)(scale,sharp_minscale);\n  while (!full_ieee)\n    {\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv tre=vzero, tim=vzero;\n      for (int i=0; i<nvec; ++i)\n        {\n        Tv tmp=vmul(lam_2.v[i],corfac.v[i]);\n        vfmaeq(tre,tmp,p1[j].r.v[i]);\n        vfmaeq(tim,tmp,p1[j].i.v[i]);\n        }\n      alm[l*njobs+j]+=vhsum_cmplx(tre,tim);\n      }\n    if (++l>lmax) return;\n    Tv r0=vload(rf[l-1].f[0]),r1=vload(rf[l-1].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_1.v[i] = vsub(vmul(vmul(cth.v[i],lam_2.v[i]),r0),vmul(lam_1.v[i],r1));\n    for (int j=0; j<njobs; ++j)\n      {\n      Tv tre=vzero, tim=vzero;\n      for (int i=0; i<nvec; ++i)\n        {\n        Tv tmp=vmul(lam_1.v[i],corfac.v[i]);\n        vfmaeq(tre,tmp,p2[j].r.v[i]);\n        vfmaeq(tim,tmp,p2[j].i.v[i]);\n        }\n      alm[l*njobs+j]+=vhsum_cmplx(tre,tim);\n      }\n    if (++l>lmax) return;\n    r0=vload(rf[l-1].f[0]); r1=vload(rf[l-1].f[1]);\n    for (int i=0; i<nvec; ++i)\n      lam_2.v[i] = vsub(vmul(vmul(cth.v[i],lam_1.v[i]),r0),vmul(lam_2.v[i],r1));\n    if (Y(rescale)(&lam_1,&lam_2,&scale))\n      {\n      Y(getCorfac)(scale,&corfac,gen->cf);\n      full_ieee = Y(TballGe)(scale,sharp_minscale);\n      }\n    }\n\n  Y(Tbmuleq)(&lam_1,corfac); Y(Tbmuleq)(&lam_2,corfac);\n  Z(map2alm_kernel) (cth, p1, p2, lam_1, lam_2, rf, alm, l, lmax NJ2);\n  }\n\nstatic inline void Z(saddstep) (Y(Tbqu) * restrict px, Y(Tbqu) * restrict py,\n  const Tb rxp, const Tb rxm, const dcmplx * restrict alm NJ1)\n  {\n  for (int j=0; j<njobs; ++j)\n    {\n    Tv agr=vload(creal(alm[2*j])), agi=vload(cimag(alm[2*j])),\n       acr=vload(creal(alm[2*j+1])), aci=vload(cimag(alm[2*j+1]));\n    for (int i=0; i<nvec; ++i)\n      {\n      Tv lw=vadd(rxp.v[i],rxm.v[i]);\n      vfmaeq(px[j].qr.v[i],agr,lw);\n      vfmaeq(px[j].qi.v[i],agi,lw);\n      vfmaeq(px[j].ur.v[i],acr,lw);\n      vfmaeq(px[j].ui.v[i],aci,lw);\n      }\n    for (int i=0; i<nvec; ++i)\n      {\n      Tv lx=vsub(rxm.v[i],rxp.v[i]);\n      vfmseq(py[j].qr.v[i],aci,lx);\n      vfmaeq(py[j].qi.v[i],acr,lx);\n      vfmaeq(py[j].ur.v[i],agi,lx);\n      vfmseq(py[j].ui.v[i],agr,lx);\n      }\n    }\n  }\n\nstatic inline void Z(saddstepb) (Y(Tbqu) * restrict p1, Y(Tbqu) * restrict p2,\n  const Tb r1p, const Tb r1m, const Tb r2p, const Tb r2m,\n  const dcmplx * restrict alm1, const dcmplx * restrict alm2 NJ1)\n  {\n  for (int j=0; j<njobs; ++j)\n    {\n    Tv agr1=vload(creal(alm1[2*j])), agi1=vload(cimag(alm1[2*j])),\n       acr1=vload(creal(alm1[2*j+1])), aci1=vload(cimag(alm1[2*j+1]));\n    Tv agr2=vload(creal(alm2[2*j])), agi2=vload(cimag(alm2[2*j])),\n       acr2=vload(creal(alm2[2*j+1])), aci2=vload(cimag(alm2[2*j+1]));\n    for (int i=0; i<nvec; ++i)\n      {\n      Tv lw1=vadd(r2p.v[i],r2m.v[i]);\n      Tv lx2=vsub(r1m.v[i],r1p.v[i]);\n      vfmaseq(p1[j].qr.v[i],agr1,lw1,aci2,lx2);\n      vfmaaeq(p1[j].qi.v[i],agi1,lw1,acr2,lx2);\n      vfmaaeq(p1[j].ur.v[i],acr1,lw1,agi2,lx2);\n      vfmaseq(p1[j].ui.v[i],aci1,lw1,agr2,lx2);\n      }\n    for (int i=0; i<nvec; ++i)\n      {\n      Tv lx1=vsub(r2m.v[i],r2p.v[i]);\n      Tv lw2=vadd(r1p.v[i],r1m.v[i]);\n      vfmaseq(p2[j].qr.v[i],agr2,lw2,aci1,lx1);\n      vfmaaeq(p2[j].qi.v[i],agi2,lw2,acr1,lx1);\n      vfmaaeq(p2[j].ur.v[i],acr2,lw2,agi1,lx1);\n      vfmaseq(p2[j].ui.v[i],aci2,lw2,agr1,lx1);\n      }\n    }\n  }\n\nstatic inline void Z(saddstep2) (const Y(Tbqu) * restrict px,\n  const Y(Tbqu) * restrict py, const Tb * restrict rxp,\n  const Tb * restrict rxm, dcmplx * restrict alm NJ1)\n  {\n  for (int j=0; j<njobs; ++j)\n    {\n    Tv agr=vzero, agi=vzero, acr=vzero, aci=vzero;\n    for (int i=0; i<nvec; ++i)\n      {\n      Tv lw=vadd(rxp->v[i],rxm->v[i]);\n      vfmaeq(agr,px[j].qr.v[i],lw);\n      vfmaeq(agi,px[j].qi.v[i],lw);\n      vfmaeq(acr,px[j].ur.v[i],lw);\n      vfmaeq(aci,px[j].ui.v[i],lw);\n      }\n    for (int i=0; i<nvec; ++i)\n      {\n      Tv lx=vsub(rxm->v[i],rxp->v[i]);\n      vfmseq(agr,py[j].ui.v[i],lx);\n      vfmaeq(agi,py[j].ur.v[i],lx);\n      vfmaeq(acr,py[j].qi.v[i],lx);\n      vfmseq(aci,py[j].qr.v[i],lx);\n      }\n    vhsum_cmplx2(agr,agi,acr,aci,&alm[2*j],&alm[2*j+1]);\n    }\n  }\n\nstatic void Z(alm2map_spin_kernel) (Tb cth, Y(Tbqu) * restrict p1,\n  Y(Tbqu) * restrict p2, Tb rec1p, Tb rec1m, Tb rec2p, Tb rec2m,\n  const sharp_ylmgen_dbl3 * restrict fx, const dcmplx * restrict alm, int l,\n  int lmax NJ1)\n  {\n  while (l<lmax)\n    {\n    Tv fx0=vload(fx[l+1].f[0]),fx1=vload(fx[l+1].f[1]),\n       fx2=vload(fx[l+1].f[2]);\n    for (int i=0; i<nvec; ++i)\n      {\n      rec1p.v[i] = vsub(vmul(vsub(cth.v[i],fx1),vmul(fx0,rec2p.v[i])),\n                        vmul(fx2,rec1p.v[i]));\n      rec1m.v[i] = vsub(vmul(vadd(cth.v[i],fx1),vmul(fx0,rec2m.v[i])),\n                        vmul(fx2,rec1m.v[i]));\n      }\n    Z(saddstepb)(p1,p2,rec1p,rec1m,rec2p,rec2m,&alm[2*njobs*l],\n      &alm[2*njobs*(l+1)] NJ2);\n    fx0=vload(fx[l+2].f[0]);fx1=vload(fx[l+2].f[1]);\n    fx2=vload(fx[l+2].f[2]);\n    for (int i=0; i<nvec; ++i)\n      {\n      rec2p.v[i] = vsub(vmul(vsub(cth.v[i],fx1),vmul(fx0,rec1p.v[i])),\n                        vmul(fx2,rec2p.v[i]));\n      rec2m.v[i] = vsub(vmul(vadd(cth.v[i],fx1),vmul(fx0,rec1m.v[i])),\n                        vmul(fx2,rec2m.v[i]));\n      }\n    l+=2;\n    }\n  if (l==lmax)\n    Z(saddstep)(p1, p2, rec2p, rec2m, &alm[2*njobs*l] NJ2);\n  }\n\nstatic void Z(map2alm_spin_kernel) (Tb cth, const Y(Tbqu) * restrict p1,\n  const Y(Tbqu) * restrict p2, Tb rec1p, Tb rec1m, Tb rec2p, Tb rec2m,\n  const sharp_ylmgen_dbl3 * restrict fx, dcmplx * restrict alm, int l, int lmax\n  NJ1)\n  {\n  while (l<lmax)\n    {\n    Tv fx0=vload(fx[l+1].f[0]),fx1=vload(fx[l+1].f[1]),\n       fx2=vload(fx[l+1].f[2]);\n    for (int i=0; i<nvec; ++i)\n      {\n      rec1p.v[i] = vsub(vmul(vsub(cth.v[i],fx1),vmul(fx0,rec2p.v[i])),\n                        vmul(fx2,rec1p.v[i]));\n      rec1m.v[i] = vsub(vmul(vadd(cth.v[i],fx1),vmul(fx0,rec2m.v[i])),\n                        vmul(fx2,rec1m.v[i]));\n      }\n    Z(saddstep2)(p1, p2, &rec2p, &rec2m, &alm[2*njobs*l] NJ2);\n    Z(saddstep2)(p2, p1, &rec1p, &rec1m, &alm[2*njobs*(l+1)] NJ2);\n    fx0=vload(fx[l+2].f[0]);fx1=vload(fx[l+2].f[1]);\n    fx2=vload(fx[l+2].f[2]);\n    for (int i=0; i<nvec; ++i)\n      {\n      rec2p.v[i] = vsub(vmul(vsub(cth.v[i],fx1),vmul(fx0,rec1p.v[i])),\n                        vmul(fx2,rec2p.v[i]));\n      rec2m.v[i] = vsub(vmul(vadd(cth.v[i],fx1),vmul(fx0,rec1m.v[i])),\n                        vmul(fx2,rec2m.v[i]));\n      }\n    l+=2;\n    }\n  if (l==lmax)\n    Z(saddstep2)(p1, p2, &rec2p, &rec2m, &alm[2*njobs*l] NJ2);\n  }\n\nstatic void Z(calc_alm2map_spin) (const Tb cth, const Tb sth,\n  const sharp_Ylmgen_C *gen, sharp_job *job, Y(Tbqu) * restrict p1,\n  Y(Tbqu) * restrict p2 NJ1)\n  {\n  int l, lmax=gen->lmax;\n  Tb rec1p, rec1m, rec2p, rec2m, scalem, scalep;\n  Y(iter_to_ieee_spin)\n    (cth,sth,&l,&rec1p,&rec1m,&rec2p,&rec2m,&scalep,&scalem,gen);\n  job->opcnt += (l-gen->m) * 10*VLEN*nvec;\n  if (l>lmax) return;\n  job->opcnt += (lmax+1-l) * (12+16*njobs)*VLEN*nvec;\n\n  const sharp_ylmgen_dbl3 * restrict fx = gen->fx;\n  Tb corfacp,corfacm;\n  Y(getCorfac)(scalep,&corfacp,gen->cf);\n  Y(getCorfac)(scalem,&corfacm,gen->cf);\n  const dcmplx * restrict alm=job->almtmp;\n  int full_ieee = Y(TballGe)(scalep,sharp_minscale)\n               && Y(TballGe)(scalem,sharp_minscale);\n  while (!full_ieee)\n    {\n    Z(saddstep)(p1, p2, Y(Tbprod)(rec2p,corfacp), Y(Tbprod)(rec2m,corfacm),\n      &alm[2*njobs*l] NJ2);\n    if (++l>lmax) break;\n    Y(rec_step)(&rec1p,&rec1m,&rec2p,&rec2m,cth,fx[l]);\n    Z(saddstep)(p2, p1, Y(Tbprod)(rec1p,corfacp), Y(Tbprod)(rec1m,corfacm),\n      &alm[2*njobs*l] NJ2);\n    if (++l>lmax) break;\n    Y(rec_step)(&rec2p,&rec2m,&rec1p,&rec1m,cth,fx[l]);\n    if (Y(rescale)(&rec1p,&rec2p,&scalep) | Y(rescale)(&rec1m,&rec2m,&scalem))\n      {\n      Y(getCorfac)(scalep,&corfacp,gen->cf);\n      Y(getCorfac)(scalem,&corfacm,gen->cf);\n      full_ieee = Y(TballGe)(scalep,sharp_minscale)\n               && Y(TballGe)(scalem,sharp_minscale);\n      }\n    }\n\n  if (l>lmax) return;\n\n  Y(Tbmuleq)(&rec1p,corfacp); Y(Tbmuleq)(&rec2p,corfacp);\n  Y(Tbmuleq)(&rec1m,corfacm); Y(Tbmuleq)(&rec2m,corfacm);\n  Z(alm2map_spin_kernel) (cth, p1, p2, rec1p, rec1m, rec2p, rec2m, fx, alm, l,\n    lmax NJ2);\n  }\n\nstatic void Z(calc_map2alm_spin) (Tb cth, Tb sth,\n  const sharp_Ylmgen_C * restrict gen, sharp_job *job,\n  const Y(Tbqu) * restrict p1, const Y(Tbqu) * restrict p2 NJ1)\n  {\n  int l, lmax=gen->lmax;\n  Tb rec1p, rec1m, rec2p, rec2m, scalem, scalep;\n  Y(iter_to_ieee_spin)\n    (cth,sth,&l,&rec1p,&rec1m,&rec2p,&rec2m,&scalep,&scalem,gen);\n  job->opcnt += (l-gen->m) * 10*VLEN*nvec;\n  if (l>lmax) return;\n  job->opcnt += (lmax+1-l) * (12+16*njobs)*VLEN*nvec;\n\n  const sharp_ylmgen_dbl3 * restrict fx = gen->fx;\n  Tb corfacp,corfacm;\n  Y(getCorfac)(scalep,&corfacp,gen->cf);\n  Y(getCorfac)(scalem,&corfacm,gen->cf);\n  dcmplx * restrict alm=job->almtmp;\n  int full_ieee = Y(TballGe)(scalep,sharp_minscale)\n               && Y(TballGe)(scalem,sharp_minscale);\n  while (!full_ieee)\n    {\n    Tb t1=Y(Tbprod)(rec2p,corfacp), t2=Y(Tbprod)(rec2m,corfacm);\n    Z(saddstep2)(p1, p2, &t1, &t2, &alm[2*njobs*l] NJ2);\n    if (++l>lmax) return;\n    Y(rec_step)(&rec1p,&rec1m,&rec2p,&rec2m,cth,fx[l]);\n    t1=Y(Tbprod)(rec1p,corfacp); t2=Y(Tbprod)(rec1m,corfacm);\n    Z(saddstep2)(p2, p1, &t1, &t2, &alm[2*njobs*l] NJ2);\n    if (++l>lmax) return;\n    Y(rec_step)(&rec2p,&rec2m,&rec1p,&rec1m,cth,fx[l]);\n    if (Y(rescale)(&rec1p,&rec2p,&scalep) | Y(rescale)(&rec1m,&rec2m,&scalem))\n      {\n      Y(getCorfac)(scalep,&corfacp,gen->cf);\n      Y(getCorfac)(scalem,&corfacm,gen->cf);\n      full_ieee = Y(TballGe)(scalep,sharp_minscale)\n               && Y(TballGe)(scalem,sharp_minscale);\n      }\n    }\n\n  Y(Tbmuleq)(&rec1p,corfacp); Y(Tbmuleq)(&rec2p,corfacp);\n  Y(Tbmuleq)(&rec1m,corfacm); Y(Tbmuleq)(&rec2m,corfacm);\n  Z(map2alm_spin_kernel)(cth,p1,p2,rec1p,rec1m,rec2p,rec2m,fx,alm,l,lmax NJ2);\n  }\n\nstatic inline void Z(saddstep_d) (Y(Tbqu) * restrict px, Y(Tbqu) * restrict py,\n  const Tb rxp, const Tb rxm, const dcmplx * restrict alm NJ1)\n  {\n  for (int j=0; j<njobs; ++j)\n    {\n    Tv ar=vload(creal(alm[j])), ai=vload(cimag(alm[j]));\n    for (int i=0; i<nvec; ++i)\n      {\n      Tv lw=vadd(rxp.v[i],rxm.v[i]);\n      vfmaeq(px[j].qr.v[i],ar,lw);\n      vfmaeq(px[j].qi.v[i],ai,lw);\n      }\n    for (int i=0; i<nvec; ++i)\n      {\n      Tv lx=vsub(rxm.v[i],rxp.v[i]);\n      vfmaeq(py[j].ur.v[i],ai,lx);\n      vfmseq(py[j].ui.v[i],ar,lx);\n      }\n    }\n  }\n\nstatic void Z(alm2map_deriv1_kernel) (Tb cth, Y(Tbqu) * restrict p1,\n  Y(Tbqu) * restrict p2, Tb rec1p, Tb rec1m, Tb rec2p, Tb rec2m,\n  const sharp_ylmgen_dbl3 * restrict fx, const dcmplx * restrict alm, int l,\n  int lmax NJ1)\n  {\n  while (l<lmax)\n    {\n    Tv fx0=vload(fx[l+1].f[0]),fx1=vload(fx[l+1].f[1]),\n       fx2=vload(fx[l+1].f[2]);\n    for (int i=0; i<nvec; ++i)\n      {\n      rec1p.v[i] = vsub(vmul(vsub(cth.v[i],fx1),vmul(fx0,rec2p.v[i])),\n                        vmul(fx2,rec1p.v[i]));\n      rec1m.v[i] = vsub(vmul(vadd(cth.v[i],fx1),vmul(fx0,rec2m.v[i])),\n                        vmul(fx2,rec1m.v[i]));\n      }\n    Z(saddstep_d)(p1,p2,rec2p,rec2m,&alm[njobs*l] NJ2);\n    Z(saddstep_d)(p2,p1,rec1p,rec1m,&alm[njobs*(l+1)] NJ2);\n    fx0=vload(fx[l+2].f[0]);fx1=vload(fx[l+2].f[1]);\n    fx2=vload(fx[l+2].f[2]);\n    for (int i=0; i<nvec; ++i)\n      {\n      rec2p.v[i] = vsub(vmul(vsub(cth.v[i],fx1),vmul(fx0,rec1p.v[i])),\n                        vmul(fx2,rec2p.v[i]));\n      rec2m.v[i] = vsub(vmul(vadd(cth.v[i],fx1),vmul(fx0,rec1m.v[i])),\n                        vmul(fx2,rec2m.v[i]));\n      }\n    l+=2;\n    }\n  if (l==lmax)\n    Z(saddstep_d)(p1, p2, rec2p, rec2m, &alm[njobs*l] NJ2);\n  }\n\nstatic void Z(calc_alm2map_deriv1) (const Tb cth, const Tb sth,\n  const sharp_Ylmgen_C *gen, sharp_job *job, Y(Tbqu) * restrict p1,\n  Y(Tbqu) * restrict p2 NJ1)\n  {\n  int l, lmax=gen->lmax;\n  Tb rec1p, rec1m, rec2p, rec2m, scalem, scalep;\n  Y(iter_to_ieee_spin)\n    (cth,sth,&l,&rec1p,&rec1m,&rec2p,&rec2m,&scalep,&scalem,gen);\n  job->opcnt += (l-gen->m) * 10*VLEN*nvec;\n  if (l>lmax) return;\n  job->opcnt += (lmax+1-l) * (12+8*njobs)*VLEN*nvec;\n\n  const sharp_ylmgen_dbl3 * restrict fx = gen->fx;\n  Tb corfacp,corfacm;\n  Y(getCorfac)(scalep,&corfacp,gen->cf);\n  Y(getCorfac)(scalem,&corfacm,gen->cf);\n  const dcmplx * restrict alm=job->almtmp;\n  int full_ieee = Y(TballGe)(scalep,sharp_minscale)\n               && Y(TballGe)(scalem,sharp_minscale);\n  while (!full_ieee)\n    {\n    Z(saddstep_d)(p1, p2, Y(Tbprod)(rec2p,corfacp), Y(Tbprod)(rec2m,corfacm),\n      &alm[njobs*l] NJ2);\n    if (++l>lmax) break;\n    Y(rec_step)(&rec1p,&rec1m,&rec2p,&rec2m,cth,fx[l]);\n    Z(saddstep_d)(p2, p1, Y(Tbprod)(rec1p,corfacp), Y(Tbprod)(rec1m,corfacm),\n      &alm[njobs*l] NJ2);\n    if (++l>lmax) break;\n    Y(rec_step)(&rec2p,&rec2m,&rec1p,&rec1m,cth,fx[l]);\n    if (Y(rescale)(&rec1p,&rec2p,&scalep) | Y(rescale)(&rec1m,&rec2m,&scalem))\n      {\n      Y(getCorfac)(scalep,&corfacp,gen->cf);\n      Y(getCorfac)(scalem,&corfacm,gen->cf);\n      full_ieee = Y(TballGe)(scalep,sharp_minscale)\n               && Y(TballGe)(scalem,sharp_minscale);\n      }\n    }\n\n  if (l>lmax) return;\n\n  Y(Tbmuleq)(&rec1p,corfacp); Y(Tbmuleq)(&rec2p,corfacp);\n  Y(Tbmuleq)(&rec1m,corfacm); Y(Tbmuleq)(&rec2m,corfacm);\n  Z(alm2map_deriv1_kernel) (cth, p1, p2, rec1p, rec1m, rec2p, rec2m, fx, alm, l,\n    lmax NJ2);\n  }\n\n\n#define VZERO(var) do { memset(&(var),0,sizeof(var)); } while(0)\n\nstatic void Z(inner_loop) (sharp_job *job, const int *ispair,\n  const double *cth_, const double *sth_, int llim, int ulim,\n  sharp_Ylmgen_C *gen, int mi, const int *mlim NJ1)\n  {\n  const int nval=nvec*VLEN;\n  const int m = job->ainfo->mval[mi];\n  sharp_Ylmgen_prepare (gen, m);\n\n  switch (job->type)\n    {\n    case SHARP_ALM2MAP:\n    case SHARP_ALM2MAP_DERIV1:\n      {\n      if (job->spin==0)\n        {\n        for (int ith=0; ith<ulim-llim; ith+=nval)\n          {\n          Y(Tburi) p1[njobs],p2[njobs]; VZERO(p1); VZERO(p2);\n          Y(Tbu) cth, sth;\n\n          int skip=1;\n          for (int i=0; i<nval; ++i)\n            {\n            int itot=i+ith;\n            if (itot>=ulim-llim) itot=ulim-llim-1;\n            if (mlim[itot]>=m) skip=0;\n            cth.s[i]=cth_[itot]; sth.s[i]=sth_[itot];\n            }\n          if (!skip)\n            Z(calc_alm2map) (cth.b,sth.b,gen,job,&p1[0].b,&p2[0].b NJ2);\n\n          for (int i=0; i<nval; ++i)\n            {\n            int itot=i+ith;\n            if (itot<ulim-llim)\n              {\n              for (int j=0; j<njobs; ++j)\n                {\n                int phas_idx = itot*job->s_th + mi*job->s_m + 2*j;\n                complex double r1 = p1[j].s.r[i] + p1[j].s.i[i]*_Complex_I,\n                               r2 = p2[j].s.r[i] + p2[j].s.i[i]*_Complex_I;\n                job->phase[phas_idx] = r1+r2;\n                if (ispair[itot])\n                  job->phase[phas_idx+1] = r1-r2;\n                }\n              }\n            }\n          }\n        }\n      else\n        {\n        for (int ith=0; ith<ulim-llim; ith+=nval)\n          {\n          Y(Tbuqu) p1[njobs],p2[njobs]; VZERO(p1); VZERO(p2);\n          Y(Tbu) cth, sth;\n          int skip=1;\n\n          for (int i=0; i<nval; ++i)\n            {\n            int itot=i+ith;\n            if (itot>=ulim-llim) itot=ulim-llim-1;\n            if (mlim[itot]>=m) skip=0;\n            cth.s[i]=cth_[itot]; sth.s[i]=sth_[itot];\n            }\n          if (!skip)\n            (job->type==SHARP_ALM2MAP) ?\n              Z(calc_alm2map_spin  )\n                (cth.b,sth.b,gen,job,&p1[0].b,&p2[0].b NJ2) :\n              Z(calc_alm2map_deriv1)\n                (cth.b,sth.b,gen,job,&p1[0].b,&p2[0].b NJ2);\n\n          for (int i=0; i<nval; ++i)\n            {\n            int itot=i+ith;\n            if (itot<ulim-llim)\n              {\n              for (int j=0; j<njobs; ++j)\n                {\n                int phas_idx = itot*job->s_th + mi*job->s_m + 4*j;\n                complex double q1 = p1[j].s.qr[i] + p1[j].s.qi[i]*_Complex_I,\n                               q2 = p2[j].s.qr[i] + p2[j].s.qi[i]*_Complex_I,\n                               u1 = p1[j].s.ur[i] + p1[j].s.ui[i]*_Complex_I,\n                               u2 = p2[j].s.ur[i] + p2[j].s.ui[i]*_Complex_I;\n                job->phase[phas_idx] = q1+q2;\n                job->phase[phas_idx+2] = u1+u2;\n                if (ispair[itot])\n                  {\n                  dcmplx *phQ = &(job->phase[phas_idx+1]),\n                         *phU = &(job->phase[phas_idx+3]);\n                  *phQ = q1-q2;\n                  *phU = u1-u2;\n                  if ((gen->mhi-gen->m+gen->s)&1)\n                    { *phQ=-(*phQ); *phU=-(*phU); }\n                  }\n                }\n              }\n            }\n          }\n        }\n      break;\n      }\n    case SHARP_MAP2ALM:\n      {\n      if (job->spin==0)\n        {\n        for (int ith=0; ith<ulim-llim; ith+=nval)\n          {\n          Y(Tburi) p1[njobs], p2[njobs]; VZERO(p1); VZERO(p2);\n          Y(Tbu) cth, sth;\n          int skip=1;\n\n          for (int i=0; i<nval; ++i)\n            {\n            int itot=i+ith;\n            if (itot>=ulim-llim) itot=ulim-llim-1;\n            if (mlim[itot]>=m) skip=0;\n            cth.s[i]=cth_[itot]; sth.s[i]=sth_[itot];\n            if ((i+ith<ulim-llim)&&(mlim[itot]>=m))\n              {\n              for (int j=0; j<njobs; ++j)\n                {\n                int phas_idx = itot*job->s_th + mi*job->s_m + 2*j;\n                dcmplx ph1=job->phase[phas_idx];\n                dcmplx ph2=ispair[itot] ? job->phase[phas_idx+1] : 0.;\n                p1[j].s.r[i]=creal(ph1+ph2); p1[j].s.i[i]=cimag(ph1+ph2);\n                p2[j].s.r[i]=creal(ph1-ph2); p2[j].s.i[i]=cimag(ph1-ph2);\n                }\n              }\n            }\n          if (!skip)\n            Z(calc_map2alm)(cth.b,sth.b,gen,job,&p1[0].b,&p2[0].b NJ2);\n          }\n        }\n      else\n        {\n        for (int ith=0; ith<ulim-llim; ith+=nval)\n          {\n          Y(Tbuqu) p1[njobs], p2[njobs]; VZERO(p1); VZERO(p2);\n          Y(Tbu) cth, sth;\n          int skip=1;\n\n          for (int i=0; i<nval; ++i)\n            {\n            int itot=i+ith;\n            if (itot>=ulim-llim) itot=ulim-llim-1;\n            if (mlim[itot]>=m) skip=0;\n            cth.s[i]=cth_[itot]; sth.s[i]=sth_[itot];\n            if (i+ith<ulim-llim)\n              {\n              for (int j=0; j<njobs; ++j)\n                {\n                int phas_idx = itot*job->s_th + mi*job->s_m + 4*j;\n                dcmplx p1Q=job->phase[phas_idx],\n                       p1U=job->phase[phas_idx+2],\n                       p2Q=ispair[itot] ? job->phase[phas_idx+1]:0.,\n                       p2U=ispair[itot] ? job->phase[phas_idx+3]:0.;\n                if ((gen->mhi-gen->m+gen->s)&1)\n                  { p2Q=-p2Q; p2U=-p2U; }\n                p1[j].s.qr[i]=creal(p1Q+p2Q); p1[j].s.qi[i]=cimag(p1Q+p2Q);\n                p1[j].s.ur[i]=creal(p1U+p2U); p1[j].s.ui[i]=cimag(p1U+p2U);\n                p2[j].s.qr[i]=creal(p1Q-p2Q); p2[j].s.qi[i]=cimag(p1Q-p2Q);\n                p2[j].s.ur[i]=creal(p1U-p2U); p2[j].s.ui[i]=cimag(p1U-p2U);\n                }\n              }\n            }\n          if (!skip)\n            Z(calc_map2alm_spin) (cth.b,sth.b,gen,job,&p1[0].b,&p2[0].b NJ2);\n          }\n        }\n      break;\n      }\n    default:\n      {\n      UTIL_FAIL(\"must not happen\");\n      break;\n      }\n    }\n  }\n\n#undef VZERO\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_core_inchelper.c",
    "content": "#define Tb CONCAT2(Tb,nvec)\n#define Y(arg) CONCAT2(arg,nvec)\n#include \"sharp_core_inc.c\"\n\n#if (SHARP_MAXTRANS>MAXJOB_SPECIAL)\n#define NJ1 , int njobs\n#define NJ2 , njobs\n#define Z(arg) CONCAT2(arg,nvec)\n#include \"sharp_core_inc2.c\"\n#undef Z\n#undef NJ1\n#undef NJ2\n#endif\n\n#define NJ1\n#define NJ2\n\n#if ((MAXJOB_SPECIAL>=1)&&(SHARP_MAXTRANS>=1))\n#define njobs 1\n#define Z(arg) CONCAT3(arg,nvec,njobs)\n#include \"sharp_core_inc2.c\"\n#undef Z\n#undef njobs\n#endif\n\n#if ((MAXJOB_SPECIAL>=2)&&(SHARP_MAXTRANS>=2))\n#define njobs 2\n#define Z(arg) CONCAT3(arg,nvec,njobs)\n#include \"sharp_core_inc2.c\"\n#undef Z\n#undef njobs\n#endif\n\n#if ((MAXJOB_SPECIAL>=3)&&(SHARP_MAXTRANS>=3))\n#define njobs 3\n#define Z(arg) CONCAT3(arg,nvec,njobs)\n#include \"sharp_core_inc2.c\"\n#undef Z\n#undef njobs\n#endif\n\n#if ((MAXJOB_SPECIAL>=4)&&(SHARP_MAXTRANS>=4))\n#define njobs 4\n#define Z(arg) CONCAT3(arg,nvec,njobs)\n#include \"sharp_core_inc2.c\"\n#undef Z\n#undef njobs\n#endif\n\n#if ((MAXJOB_SPECIAL>=5)&&(SHARP_MAXTRANS>=5))\n#define njobs 5\n#define Z(arg) CONCAT3(arg,nvec,njobs)\n#include \"sharp_core_inc2.c\"\n#undef Z\n#undef njobs\n#endif\n\n#if ((MAXJOB_SPECIAL>=6)&&(SHARP_MAXTRANS>=6))\n#define njobs 6\n#define Z(arg) CONCAT3(arg,nvec,njobs)\n#include \"sharp_core_inc2.c\"\n#undef Z\n#undef njobs\n#endif\n\n#undef NJ1\n#undef NJ2\n\n#undef Y\n#undef Tb\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_cxx.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_cxx.h\n *  Spherical transform library\n *\n *  Copyright (C) 2012-2015 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_SHARP_CXX_H\n#define PLANCK_SHARP_CXX_H\n\n#include \"sharp_lowlevel.h\"\n#include \"sharp_geomhelpers.h\"\n#include \"sharp_almhelpers.h\"\n\nclass sharp_base\n  {\n  protected:\n    sharp_alm_info *ainfo;\n    sharp_geom_info *ginfo;\n\n  public:\n    sharp_base()\n      : ainfo(0), ginfo(0) {}\n    ~sharp_base()\n      {\n      sharp_destroy_geom_info(ginfo);\n      sharp_destroy_alm_info(ainfo);\n      }\n\n    void set_general_geometry (int nrings, const int *nph, const ptrdiff_t *ofs,\n      const int *stride, const double *phi0, const double *theta,\n      const double *wgt)\n      {\n      if (ginfo) sharp_destroy_geom_info(ginfo);\n      sharp_make_geom_info (nrings, nph, ofs, stride, phi0, theta, wgt, &ginfo);\n      }\n\n    void set_ECP_geometry (int nrings, int nphi)\n      {\n      if (ginfo) sharp_destroy_geom_info(ginfo);\n      sharp_make_ecp_geom_info (nrings, nphi, 0., 1, nphi, &ginfo);\n      }\n\n    void set_Gauss_geometry (int nrings, int nphi)\n      {\n      if (ginfo) sharp_destroy_geom_info(ginfo);\n      sharp_make_gauss_geom_info (nrings, nphi, 0., 1, nphi, &ginfo);\n      }\n\n    void set_Healpix_geometry (int nside)\n      {\n      if (ginfo) sharp_destroy_geom_info(ginfo);\n      sharp_make_healpix_geom_info (nside, 1, &ginfo);\n      }\n\n    void set_weighted_Healpix_geometry (int nside, const double *weight)\n      {\n      if (ginfo) sharp_destroy_geom_info(ginfo);\n      sharp_make_weighted_healpix_geom_info (nside, 1, weight, &ginfo);\n      }\n\n    void set_triangular_alm_info (int lmax, int mmax)\n      {\n      if (ainfo) sharp_destroy_alm_info(ainfo);\n      sharp_make_triangular_alm_info (lmax, mmax, 1, &ainfo);\n      }\n\n    const sharp_geom_info* get_geom_info() const { return ginfo; }\n    const sharp_alm_info* get_alm_info() const { return ainfo; }\n  };\n\ntemplate<typename T> struct cxxjobhelper__ {};\n\ntemplate<> struct cxxjobhelper__<double>\n  { enum {val=SHARP_DP}; };\n\ntemplate<> struct cxxjobhelper__<float>\n  { enum {val=0}; };\n\n\ntemplate<typename T> class sharp_cxxjob: public sharp_base\n  {\n  private:\n    static void *conv (T *ptr)\n      { return reinterpret_cast<void *>(ptr); }\n    static void *conv (const T *ptr)\n      { return const_cast<void *>(reinterpret_cast<const void *>(ptr)); }\n\n  public:\n    void alm2map (const T *alm, T *map, bool add)\n      {\n      void *aptr=conv(alm), *mptr=conv(map);\n      int flags=cxxjobhelper__<T>::val | (add ? SHARP_ADD : 0);\n      sharp_execute (SHARP_ALM2MAP, 0, &aptr, &mptr, ginfo, ainfo, 1,\n        flags,0,0);\n      }\n    void alm2map_spin (const T *alm1, const T *alm2, T *map1, T *map2,\n      int spin, bool add)\n      {\n      void *aptr[2], *mptr[2];\n      aptr[0]=conv(alm1); aptr[1]=conv(alm2);\n      mptr[0]=conv(map1); mptr[1]=conv(map2);\n      int flags=cxxjobhelper__<T>::val | (add ? SHARP_ADD : 0);\n      sharp_execute (SHARP_ALM2MAP,spin,aptr,mptr,ginfo,ainfo,1,flags,0,0);\n      }\n    void alm2map_der1 (const T *alm, T *map1, T *map2, bool add)\n      {\n      void *aptr=conv(alm), *mptr[2];\n      mptr[0]=conv(map1); mptr[1]=conv(map2);\n      int flags=cxxjobhelper__<T>::val | (add ? SHARP_ADD : 0);\n      sharp_execute (SHARP_ALM2MAP_DERIV1,1,&aptr,mptr,ginfo,ainfo,1,flags,0,0);\n      }\n    void map2alm (const T *map, T *alm, bool add)\n      {\n      void *aptr=conv(alm), *mptr=conv(map);\n      int flags=cxxjobhelper__<T>::val | (add ? SHARP_ADD : 0);\n      sharp_execute (SHARP_MAP2ALM,0,&aptr,&mptr,ginfo,ainfo,1,flags,0,0);\n      }\n    void map2alm_spin (const T *map1, const T *map2, T *alm1, T *alm2,\n      int spin, bool add)\n      {\n      void *aptr[2], *mptr[2];\n      aptr[0]=conv(alm1); aptr[1]=conv(alm2);\n      mptr[0]=conv(map1); mptr[1]=conv(map2);\n      int flags=cxxjobhelper__<T>::val | (add ? SHARP_ADD : 0);\n      sharp_execute (SHARP_MAP2ALM,spin,aptr,mptr,ginfo,ainfo,1,flags,0,0);\n      }\n  };\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_geomhelpers.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_geomhelpers.c\n *  Spherical transform library\n *\n *  Copyright (C) 2006-2012 Max-Planck-Society<br>\n *  Copyright (C) 2007-2008 Pavel Holoborodko (for gauss_legendre_tbl)\n *  \\author Martin Reinecke \\author Pavel Holoborodko\n */\n\n#include <math.h>\n#include \"sharp_geomhelpers.h\"\n#include \"sharp_legendre_roots.h\"\n#include \"c_utils.h\"\n#include \"ls_fft.h\"\n#include <stdio.h>\n\nvoid sharp_make_subset_healpix_geom_info (int nside, int stride, int nrings,\n  const int *rings, const double *weight, sharp_geom_info **geom_info)\n  {\n  const double pi=3.141592653589793238462643383279502884197;\n  ptrdiff_t npix=(ptrdiff_t)nside*nside*12;\n  ptrdiff_t ncap=2*(ptrdiff_t)nside*(nside-1);\n\n  double *theta=RALLOC(double,nrings);\n  double *weight_=RALLOC(double,nrings);\n  int *nph=RALLOC(int,nrings);\n  double *phi0=RALLOC(double,nrings);\n  ptrdiff_t *ofs=RALLOC(ptrdiff_t,nrings);\n  int *stride_=RALLOC(int,nrings);\n  ptrdiff_t curofs=0, checkofs; /* checkofs used for assertion introduced when adding rings arg */\n  for (int m=0; m<nrings; ++m)\n    {\n    int ring = (rings==NULL)? (m+1) : rings[m];\n    ptrdiff_t northring = (ring>2*nside) ? 4*nside-ring : ring;\n    stride_[m] = stride;\n    if (northring < nside)\n      {\n      theta[m] = 2*asin(northring/(sqrt(6.)*nside));\n      nph[m] = 4*northring;\n      phi0[m] = pi/nph[m];\n      checkofs = 2*northring*(northring-1)*stride;\n      }\n    else\n      {\n      double fact1 = (8.*nside)/npix;\n      double costheta = (2*nside-northring)*fact1;\n      theta[m] = acos(costheta);\n      nph[m] = 4*nside;\n      if ((northring-nside) & 1)\n        phi0[m] = 0;\n      else\n        phi0[m] = pi/nph[m];\n      checkofs = (ncap + (northring-nside)*nph[m])*stride;\n      ofs[m] = curofs;\n      }\n    if (northring != ring) /* southern hemisphere */\n      {\n      theta[m] = pi-theta[m];\n      checkofs = (npix - nph[m])*stride - checkofs;\n      ofs[m] = curofs;\n      }\n    weight_[m]=4.*pi/npix*((weight==NULL) ? 1. : weight[northring-1]);\n    if (rings==NULL) {\n        UTIL_ASSERT(curofs==checkofs, \"Bug in computing ofs[m]\");\n    }\n    ofs[m] = curofs;\n    curofs+=nph[m];\n    }\n\n  sharp_make_geom_info (nrings, nph, ofs, stride_, phi0, theta, weight_,\n    geom_info);\n\n  DEALLOC(theta);\n  DEALLOC(weight_);\n  DEALLOC(nph);\n  DEALLOC(phi0);\n  DEALLOC(ofs);\n  DEALLOC(stride_);\n  }\n\nvoid sharp_make_weighted_healpix_geom_info (int nside, int stride,\n  const double *weight, sharp_geom_info **geom_info)\n  {\n  sharp_make_subset_healpix_geom_info(nside, stride, 4 * nside - 1, NULL, weight, geom_info);\n  }\n\nvoid sharp_make_gauss_geom_info (int nrings, int nphi, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info)\n  {\n  const double pi=3.141592653589793238462643383279502884197;\n\n  double *theta=RALLOC(double,nrings);\n  double *weight=RALLOC(double,nrings);\n  int *nph=RALLOC(int,nrings);\n  double *phi0_=RALLOC(double,nrings);\n  ptrdiff_t *ofs=RALLOC(ptrdiff_t,nrings);\n  int *stride_=RALLOC(int,nrings);\n\n  sharp_legendre_roots(nrings,theta,weight);\n  for (int m=0; m<nrings; ++m)\n    {\n    theta[m] = acos(-theta[m]);\n    nph[m]=nphi;\n    phi0_[m]=phi0;\n    ofs[m]=(ptrdiff_t)m*stride_lat;\n    stride_[m]=stride_lon;\n    weight[m]*=2*pi/nphi;\n    }\n\n  sharp_make_geom_info (nrings, nph, ofs, stride_, phi0_, theta, weight,\n    geom_info);\n\n  DEALLOC(theta);\n  DEALLOC(weight);\n  DEALLOC(nph);\n  DEALLOC(phi0_);\n  DEALLOC(ofs);\n  DEALLOC(stride_);\n  }\n\n/* Weights from Waldvogel 2006: BIT Numerical Mathematics 46, p. 195 */\nvoid sharp_make_fejer1_geom_info (int nrings, int ppring, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info)\n  {\n  const double pi=3.141592653589793238462643383279502884197;\n\n  double *theta=RALLOC(double,nrings);\n  double *weight=RALLOC(double,nrings);\n  int *nph=RALLOC(int,nrings);\n  double *phi0_=RALLOC(double,nrings);\n  ptrdiff_t *ofs=RALLOC(ptrdiff_t,nrings);\n  int *stride_=RALLOC(int,nrings);\n\n  weight[0]=2.;\n  for (int k=1; k<=(nrings-1)/2; ++k)\n    {\n    weight[2*k-1]=2./(1.-4.*k*k)*cos((k*pi)/nrings);\n    weight[2*k  ]=2./(1.-4.*k*k)*sin((k*pi)/nrings);\n    }\n  if ((nrings&1)==0) weight[nrings-1]=0.;\n  real_plan plan = make_real_plan(nrings);\n  real_plan_backward_fftpack(plan,weight);\n  kill_real_plan(plan);\n\n  for (int m=0; m<(nrings+1)/2; ++m)\n    {\n    theta[m]=pi*(m+0.5)/nrings;\n    theta[nrings-1-m]=pi-theta[m];\n    nph[m]=nph[nrings-1-m]=ppring;\n    phi0_[m]=phi0_[nrings-1-m]=phi0;\n    ofs[m]=(ptrdiff_t)m*stride_lat;\n    ofs[nrings-1-m]=(ptrdiff_t)((nrings-1-m)*stride_lat);\n    stride_[m]=stride_[nrings-1-m]=stride_lon;\n    weight[m]=weight[nrings-1-m]=weight[m]*2*pi/(nrings*nph[m]);\n    }\n\n  sharp_make_geom_info (nrings, nph, ofs, stride_, phi0_, theta, weight,\n    geom_info);\n\n  DEALLOC(theta);\n  DEALLOC(weight);\n  DEALLOC(nph);\n  DEALLOC(phi0_);\n  DEALLOC(ofs);\n  DEALLOC(stride_);\n  }\n\n/* Weights from Waldvogel 2006: BIT Numerical Mathematics 46, p. 195 */\nvoid sharp_make_cc_geom_info (int nrings, int ppring, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info)\n  {\n  const double pi=3.141592653589793238462643383279502884197;\n\n  double *theta=RALLOC(double,nrings);\n  double *weight=RALLOC(double,nrings);\n  int *nph=RALLOC(int,nrings);\n  double *phi0_=RALLOC(double,nrings);\n  ptrdiff_t *ofs=RALLOC(ptrdiff_t,nrings);\n  int *stride_=RALLOC(int,nrings);\n\n  int n=nrings-1;\n  SET_ARRAY(weight,0,nrings,0.);\n  double dw=-1./(n*n-1.+(n&1));\n  weight[0]=2.+dw;\n  for (int k=1; k<=(n/2-1); ++k)\n    weight[2*k-1]=2./(1.-4.*k*k) + dw;\n  weight[2*(n/2)-1]=(n-3.)/(2*(n/2)-1) -1. -dw*((2-(n&1))*n-1);\n  real_plan plan = make_real_plan(n);\n  real_plan_backward_fftpack(plan,weight);\n  kill_real_plan(plan);\n  weight[n]=weight[0];\n\n  for (int m=0; m<(nrings+1)/2; ++m)\n    {\n    theta[m]=pi*m/(nrings-1.);\n    if (theta[m]<1e-15) theta[m]=1e-15;\n    theta[nrings-1-m]=pi-theta[m];\n    nph[m]=nph[nrings-1-m]=ppring;\n    phi0_[m]=phi0_[nrings-1-m]=phi0;\n    ofs[m]=(ptrdiff_t)m*stride_lat;\n    ofs[nrings-1-m]=(ptrdiff_t)((nrings-1-m)*stride_lat);\n    stride_[m]=stride_[nrings-1-m]=stride_lon;\n    weight[m]=weight[nrings-1-m]=weight[m]*2*pi/(n*nph[m]);\n    }\n\n  sharp_make_geom_info (nrings, nph, ofs, stride_, phi0_, theta, weight,\n    geom_info);\n\n  DEALLOC(theta);\n  DEALLOC(weight);\n  DEALLOC(nph);\n  DEALLOC(phi0_);\n  DEALLOC(ofs);\n  DEALLOC(stride_);\n  }\n\n/* Weights from Waldvogel 2006: BIT Numerical Mathematics 46, p. 195 */\nvoid sharp_make_fejer2_geom_info (int nrings, int ppring, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info)\n  {\n  const double pi=3.141592653589793238462643383279502884197;\n\n  double *theta=RALLOC(double,nrings);\n  double *weight=RALLOC(double,nrings+1);\n  int *nph=RALLOC(int,nrings);\n  double *phi0_=RALLOC(double,nrings);\n  ptrdiff_t *ofs=RALLOC(ptrdiff_t,nrings);\n  int *stride_=RALLOC(int,nrings);\n\n  int n=nrings+1;\n  SET_ARRAY(weight,0,n,0.);\n  weight[0]=2.;\n  for (int k=1; k<=(n/2-1); ++k)\n    weight[2*k-1]=2./(1.-4.*k*k);\n  weight[2*(n/2)-1]=(n-3.)/(2*(n/2)-1) -1.;\n  real_plan plan = make_real_plan(n);\n  real_plan_backward_fftpack(plan,weight);\n  kill_real_plan(plan);\n  for (int m=0; m<nrings; ++m)\n    weight[m]=weight[m+1];\n\n  for (int m=0; m<(nrings+1)/2; ++m)\n    {\n    theta[m]=pi*(m+1)/(nrings+1.);\n    theta[nrings-1-m]=pi-theta[m];\n    nph[m]=nph[nrings-1-m]=ppring;\n    phi0_[m]=phi0_[nrings-1-m]=phi0;\n    ofs[m]=(ptrdiff_t)m*stride_lat;\n    ofs[nrings-1-m]=(ptrdiff_t)((nrings-1-m)*stride_lat);\n    stride_[m]=stride_[nrings-1-m]=stride_lon;\n    weight[m]=weight[nrings-1-m]=weight[m]*2*pi/(n*nph[m]);\n    }\n\n  sharp_make_geom_info (nrings, nph, ofs, stride_, phi0_, theta, weight,\n    geom_info);\n\n  DEALLOC(theta);\n  DEALLOC(weight);\n  DEALLOC(nph);\n  DEALLOC(phi0_);\n  DEALLOC(ofs);\n  DEALLOC(stride_);\n  }\n\nvoid sharp_make_mw_geom_info (int nrings, int ppring, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info)\n  {\n  const double pi=3.141592653589793238462643383279502884197;\n\n  double *theta=RALLOC(double,nrings);\n  int *nph=RALLOC(int,nrings);\n  double *phi0_=RALLOC(double,nrings);\n  ptrdiff_t *ofs=RALLOC(ptrdiff_t,nrings);\n  int *stride_=RALLOC(int,nrings);\n\n  for (int m=0; m<nrings; ++m)\n    {\n    theta[m]=pi*(2.*m+1.)/(2.*nrings-1.);\n    if (theta[m]>pi-1e-15) theta[m]=pi-1e-15;\n    nph[m]=ppring;\n    phi0_[m]=phi0;\n    ofs[m]=(ptrdiff_t)m*stride_lat;\n    stride_[m]=stride_lon;\n    }\n\n  sharp_make_geom_info (nrings, nph, ofs, stride_, phi0_, theta, NULL,\n    geom_info);\n\n  DEALLOC(theta);\n  DEALLOC(nph);\n  DEALLOC(phi0_);\n  DEALLOC(ofs);\n  DEALLOC(stride_);\n  }\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_geomhelpers.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_geomhelpers.h\n *  SHARP helper function for the creation of grid geometries\n *\n *  Copyright (C) 2006-2013 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef PLANCK_SHARP_GEOMHELPERS_H\n#define PLANCK_SHARP_GEOMHELPERS_H\n\n#include \"sharp_lowlevel.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*! Creates a geometry information describing a HEALPix map with an\n    Nside parameter \\a nside. \\a weight contains the relative ring\n    weights and must have \\a 2*nside entries. The rings array contains\n    the indices of the rings, with 1 being the first ring at the north\n    pole; if NULL then we take them to be sequential. Pass 4 * nside - 1\n    as nrings and NULL to rings to get the full HEALPix grid.\n    \\note if \\a weight is a null pointer, all weights are assumed to be 1.\n    \\note if \\a rings is a null pointer, take all rings\n    \\ingroup geominfogroup */\nvoid sharp_make_subset_healpix_geom_info (int nside, int stride, int nrings,\n  const int *rings, const double *weight, sharp_geom_info **geom_info);\n\n/*! Creates a geometry information describing a HEALPix map with an\n    Nside parameter \\a nside. \\a weight contains the relative ring\n    weights and must have \\a 2*nside entries.\n    \\note if \\a weight is a null pointer, all weights are assumed to be 1.\n    \\ingroup geominfogroup */\nvoid sharp_make_weighted_healpix_geom_info (int nside, int stride,\n  const double *weight, sharp_geom_info **geom_info);\n\n/*! Creates a geometry information describing a HEALPix map with an\n    Nside parameter \\a nside.\n    \\ingroup geominfogroup */\nstatic inline void sharp_make_healpix_geom_info (int nside, int stride,\n  sharp_geom_info **geom_info)\n  { sharp_make_weighted_healpix_geom_info (nside, stride, NULL, geom_info); }\n\n/*! Creates a geometry information describing a Gaussian map with \\a nrings\n    iso-latitude rings and \\a nphi pixels per ring. The azimuth of the first\n    pixel in each ring is \\a phi0 (in radians). The index difference between\n    two adjacent pixels in an iso-latitude ring is \\a stride_lon, the index\n    difference between the two start pixels in consecutive iso-latitude rings\n    is \\a stride_lat.\n    \\ingroup geominfogroup */\nvoid sharp_make_gauss_geom_info (int nrings, int nphi, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info);\n\n/*! Creates a geometry information describing an ECP map with \\a nrings\n    iso-latitude rings and \\a nphi pixels per ring. The azimuth of the first\n    pixel in each ring is \\a phi0 (in radians). The index difference between\n    two adjacent pixels in an iso-latitude ring is \\a stride_lon, the index\n    difference between the two start pixels in consecutive iso-latitude rings\n    is \\a stride_lat.\n    \\note The spacing of pixel centers is equidistant in colatitude and\n      longitude.\n    \\note The sphere is pixelized in a way that the colatitude of the first ring\n      is \\a 0.5*(pi/nrings) and the colatitude of the last ring is\n      \\a pi-0.5*(pi/nrings). There are no pixel centers at the poles.\n    \\note This grid corresponds to Fejer's first rule.\n    \\ingroup geominfogroup */\nvoid sharp_make_fejer1_geom_info (int nrings, int nphi, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info);\n\n/*! Old name for sharp_make_fejer1_geom_info()\n    \\ingroup geominfogroup */\nstatic inline void sharp_make_ecp_geom_info (int nrings, int nphi, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info)\n  {\n  sharp_make_fejer1_geom_info (nrings, nphi, phi0, stride_lon, stride_lat,\n  geom_info);\n  }\n\n/*! Creates a geometry information describing an ECP map with \\a nrings\n    iso-latitude rings and \\a nphi pixels per ring. The azimuth of the first\n    pixel in each ring is \\a phi0 (in radians). The index difference between\n    two adjacent pixels in an iso-latitude ring is \\a stride_lon, the index\n    difference between the two start pixels in consecutive iso-latitude rings\n    is \\a stride_lat.\n    \\note The spacing of pixel centers is equidistant in colatitude and\n      longitude.\n    \\note The sphere is pixelized in a way that the colatitude of the first ring\n      is \\a 0 and that of the last ring is \\a pi.\n    \\note This grid corresponds to Clenshaw-Curtis integration.\n    \\ingroup geominfogroup */\nvoid sharp_make_cc_geom_info (int nrings, int ppring, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info);\n\n/*! Creates a geometry information describing an ECP map with \\a nrings\n    iso-latitude rings and \\a nphi pixels per ring. The azimuth of the first\n    pixel in each ring is \\a phi0 (in radians). The index difference between\n    two adjacent pixels in an iso-latitude ring is \\a stride_lon, the index\n    difference between the two start pixels in consecutive iso-latitude rings\n    is \\a stride_lat.\n    \\note The spacing of pixel centers is equidistant in colatitude and\n      longitude.\n    \\note The sphere is pixelized in a way that the colatitude of the first ring\n      is \\a pi/(nrings+1) and that of the last ring is \\a pi-pi/(nrings+1).\n    \\note This grid corresponds to Fejer's second rule.\n    \\ingroup geominfogroup */\nvoid sharp_make_fejer2_geom_info (int nrings, int ppring, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info);\n\n/*! Creates a geometry information describing a map with \\a nrings\n    iso-latitude rings and \\a nphi pixels per ring. The azimuth of the first\n    pixel in each ring is \\a phi0 (in radians). The index difference between\n    two adjacent pixels in an iso-latitude ring is \\a stride_lon, the index\n    difference between the two start pixels in consecutive iso-latitude rings\n    is \\a stride_lat.\n    \\note The spacing of pixel centers is equidistant in colatitude and\n      longitude.\n    \\note The sphere is pixelized in a way that the colatitude of the first ring\n      is \\a pi/(2*nrings-1) and that of the last ring is \\a pi.\n    \\note This is the grid introduced by McEwen & Wiaux 2011.\n    \\note This function does \\e not define any quadrature weights.\n    \\ingroup geominfogroup */\nvoid sharp_make_mw_geom_info (int nrings, int ppring, double phi0,\n  int stride_lon, int stride_lat, sharp_geom_info **geom_info);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_internal.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_internal.h\n *  Internally used functionality for the spherical transform library.\n *\n *  Copyright (C) 2006-2013 Max-Planck-Society\n *  \\author Martin Reinecke \\author Dag Sverre Seljebotn\n */\n\n#ifndef PLANCK_SHARP_INTERNAL_H\n#define PLANCK_SHARP_INTERNAL_H\n\n#ifdef __cplusplus\n#error This header file cannot be included from C++, only from C\n#endif\n\n#include \"sharp.h\"\n\n#define SHARP_MAXTRANS 100\n\ntypedef struct\n  {\n  sharp_jobtype type;\n  int spin;\n  int nmaps, nalm;\n  int flags;\n  void **map;\n  void **alm;\n  int s_m, s_th; // strides in m and theta direction\n  complex double *phase;\n  double *norm_l;\n  complex double *almtmp;\n  const sharp_geom_info *ginfo;\n  const sharp_alm_info *ainfo;\n  double time;\n  int ntrans;\n  unsigned long long opcnt;\n  } sharp_job;\n\nint sharp_get_nv_max (void);\nint sharp_nv_oracle (sharp_jobtype type, int spin, int ntrans);\nint sharp_get_mlim (int lmax, int spin, double sth, double cth);\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_legendre.c",
    "content": "/* DO NOT EDIT. md5sum of source: a8c5c18a7a19c378187dbf461d12eb5c *//*\n\n    NOTE NOTE NOTE\n\n    This file is edited in sharp_legendre.c.in which is then preprocessed.\n    Do not make manual  modifications to sharp_legendre.c.\n\n    NOTE NOTE NOTE\n\n*/\n\n\n/*\n *  This file is part of libsharp.\n *\n * Redistribution and use in source and binary forms, with or without\n * met:\n * \n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * \n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*! \\file sharp_legendre.c.in\n *\n *  Copyright (C) 2015 University of Oslo\n *  \\author Dag Sverre Seljebotn\n */\n\n#ifndef NO_LEGENDRE\n#if (VLEN==8)\n#error This code is not tested with MIC; please compile with -DNO_LEGENDRE\n/* ...or test it (it probably works) and remove this check */\n#endif\n\n#ifndef SHARP_LEGENDRE_CS\n#define SHARP_LEGENDRE_CS 4\n#endif\n\n#define MAX_CS 6\n#if (SHARP_LEGENDRE_CS > MAX_CS)\n#error (SHARP_LEGENDRE_CS > MAX_CS)\n#endif\n\n#include \"sharp_legendre.h\"\n#include \"sharp_vecsupport.h\"\n\n#include <stdlib.h>\n\n\n\nstatic void legendre_transform_vec1(double *recfacs, double *bl, ptrdiff_t lmax,\n                                              double xarr[(1) * VLEN],\n                                              double out[(1) * VLEN]) {\n    \n    Tv P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu(xarr + 0 * VLEN);\n    Pm1_0 = vload(1.0);\n    P_0 = x0;\n    b = vload(*bl);\n    y0 = vmul(Pm1_0, b);\n    \n    \n    b = vload(*(bl + 1));\n    \n    vfmaeq(y0, P_0, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload(*(bl + l));\n        R = vload(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq(P_0, W2, R);\n        vfmaeq(y0, P_0, b);\n        \n\n    }\n    \n    vstoreu(out + 0 * VLEN, y0);\n    \n}\n\nstatic void legendre_transform_vec2(double *recfacs, double *bl, ptrdiff_t lmax,\n                                              double xarr[(2) * VLEN],\n                                              double out[(2) * VLEN]) {\n    \n    Tv P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu(xarr + 0 * VLEN);\n    Pm1_0 = vload(1.0);\n    P_0 = x0;\n    b = vload(*bl);\n    y0 = vmul(Pm1_0, b);\n    \n    x1 = vloadu(xarr + 1 * VLEN);\n    Pm1_1 = vload(1.0);\n    P_1 = x1;\n    b = vload(*bl);\n    y1 = vmul(Pm1_1, b);\n    \n    \n    b = vload(*(bl + 1));\n    \n    vfmaeq(y0, P_0, b);\n    \n    vfmaeq(y1, P_1, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload(*(bl + l));\n        R = vload(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq(P_0, W2, R);\n        vfmaeq(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq(P_1, W2, R);\n        vfmaeq(y1, P_1, b);\n        \n\n    }\n    \n    vstoreu(out + 0 * VLEN, y0);\n    \n    vstoreu(out + 1 * VLEN, y1);\n    \n}\n\nstatic void legendre_transform_vec3(double *recfacs, double *bl, ptrdiff_t lmax,\n                                              double xarr[(3) * VLEN],\n                                              double out[(3) * VLEN]) {\n    \n    Tv P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv P_2, Pm1_2, Pm2_2, x2, y2;\n    \n    Tv W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu(xarr + 0 * VLEN);\n    Pm1_0 = vload(1.0);\n    P_0 = x0;\n    b = vload(*bl);\n    y0 = vmul(Pm1_0, b);\n    \n    x1 = vloadu(xarr + 1 * VLEN);\n    Pm1_1 = vload(1.0);\n    P_1 = x1;\n    b = vload(*bl);\n    y1 = vmul(Pm1_1, b);\n    \n    x2 = vloadu(xarr + 2 * VLEN);\n    Pm1_2 = vload(1.0);\n    P_2 = x2;\n    b = vload(*bl);\n    y2 = vmul(Pm1_2, b);\n    \n    \n    b = vload(*(bl + 1));\n    \n    vfmaeq(y0, P_0, b);\n    \n    vfmaeq(y1, P_1, b);\n    \n    vfmaeq(y2, P_2, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload(*(bl + l));\n        R = vload(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq(P_0, W2, R);\n        vfmaeq(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq(P_1, W2, R);\n        vfmaeq(y1, P_1, b);\n        \n        Pm2_2 = Pm1_2; Pm1_2 = P_2;\n        W1 = vmul(x2, Pm1_2);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_2);\n        P_2 = W1;\n        vfmaeq(P_2, W2, R);\n        vfmaeq(y2, P_2, b);\n        \n\n    }\n    \n    vstoreu(out + 0 * VLEN, y0);\n    \n    vstoreu(out + 1 * VLEN, y1);\n    \n    vstoreu(out + 2 * VLEN, y2);\n    \n}\n\nstatic void legendre_transform_vec4(double *recfacs, double *bl, ptrdiff_t lmax,\n                                              double xarr[(4) * VLEN],\n                                              double out[(4) * VLEN]) {\n    \n    Tv P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv P_2, Pm1_2, Pm2_2, x2, y2;\n    \n    Tv P_3, Pm1_3, Pm2_3, x3, y3;\n    \n    Tv W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu(xarr + 0 * VLEN);\n    Pm1_0 = vload(1.0);\n    P_0 = x0;\n    b = vload(*bl);\n    y0 = vmul(Pm1_0, b);\n    \n    x1 = vloadu(xarr + 1 * VLEN);\n    Pm1_1 = vload(1.0);\n    P_1 = x1;\n    b = vload(*bl);\n    y1 = vmul(Pm1_1, b);\n    \n    x2 = vloadu(xarr + 2 * VLEN);\n    Pm1_2 = vload(1.0);\n    P_2 = x2;\n    b = vload(*bl);\n    y2 = vmul(Pm1_2, b);\n    \n    x3 = vloadu(xarr + 3 * VLEN);\n    Pm1_3 = vload(1.0);\n    P_3 = x3;\n    b = vload(*bl);\n    y3 = vmul(Pm1_3, b);\n    \n    \n    b = vload(*(bl + 1));\n    \n    vfmaeq(y0, P_0, b);\n    \n    vfmaeq(y1, P_1, b);\n    \n    vfmaeq(y2, P_2, b);\n    \n    vfmaeq(y3, P_3, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload(*(bl + l));\n        R = vload(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq(P_0, W2, R);\n        vfmaeq(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq(P_1, W2, R);\n        vfmaeq(y1, P_1, b);\n        \n        Pm2_2 = Pm1_2; Pm1_2 = P_2;\n        W1 = vmul(x2, Pm1_2);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_2);\n        P_2 = W1;\n        vfmaeq(P_2, W2, R);\n        vfmaeq(y2, P_2, b);\n        \n        Pm2_3 = Pm1_3; Pm1_3 = P_3;\n        W1 = vmul(x3, Pm1_3);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_3);\n        P_3 = W1;\n        vfmaeq(P_3, W2, R);\n        vfmaeq(y3, P_3, b);\n        \n\n    }\n    \n    vstoreu(out + 0 * VLEN, y0);\n    \n    vstoreu(out + 1 * VLEN, y1);\n    \n    vstoreu(out + 2 * VLEN, y2);\n    \n    vstoreu(out + 3 * VLEN, y3);\n    \n}\n\nstatic void legendre_transform_vec5(double *recfacs, double *bl, ptrdiff_t lmax,\n                                              double xarr[(5) * VLEN],\n                                              double out[(5) * VLEN]) {\n    \n    Tv P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv P_2, Pm1_2, Pm2_2, x2, y2;\n    \n    Tv P_3, Pm1_3, Pm2_3, x3, y3;\n    \n    Tv P_4, Pm1_4, Pm2_4, x4, y4;\n    \n    Tv W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu(xarr + 0 * VLEN);\n    Pm1_0 = vload(1.0);\n    P_0 = x0;\n    b = vload(*bl);\n    y0 = vmul(Pm1_0, b);\n    \n    x1 = vloadu(xarr + 1 * VLEN);\n    Pm1_1 = vload(1.0);\n    P_1 = x1;\n    b = vload(*bl);\n    y1 = vmul(Pm1_1, b);\n    \n    x2 = vloadu(xarr + 2 * VLEN);\n    Pm1_2 = vload(1.0);\n    P_2 = x2;\n    b = vload(*bl);\n    y2 = vmul(Pm1_2, b);\n    \n    x3 = vloadu(xarr + 3 * VLEN);\n    Pm1_3 = vload(1.0);\n    P_3 = x3;\n    b = vload(*bl);\n    y3 = vmul(Pm1_3, b);\n    \n    x4 = vloadu(xarr + 4 * VLEN);\n    Pm1_4 = vload(1.0);\n    P_4 = x4;\n    b = vload(*bl);\n    y4 = vmul(Pm1_4, b);\n    \n    \n    b = vload(*(bl + 1));\n    \n    vfmaeq(y0, P_0, b);\n    \n    vfmaeq(y1, P_1, b);\n    \n    vfmaeq(y2, P_2, b);\n    \n    vfmaeq(y3, P_3, b);\n    \n    vfmaeq(y4, P_4, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload(*(bl + l));\n        R = vload(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq(P_0, W2, R);\n        vfmaeq(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq(P_1, W2, R);\n        vfmaeq(y1, P_1, b);\n        \n        Pm2_2 = Pm1_2; Pm1_2 = P_2;\n        W1 = vmul(x2, Pm1_2);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_2);\n        P_2 = W1;\n        vfmaeq(P_2, W2, R);\n        vfmaeq(y2, P_2, b);\n        \n        Pm2_3 = Pm1_3; Pm1_3 = P_3;\n        W1 = vmul(x3, Pm1_3);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_3);\n        P_3 = W1;\n        vfmaeq(P_3, W2, R);\n        vfmaeq(y3, P_3, b);\n        \n        Pm2_4 = Pm1_4; Pm1_4 = P_4;\n        W1 = vmul(x4, Pm1_4);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_4);\n        P_4 = W1;\n        vfmaeq(P_4, W2, R);\n        vfmaeq(y4, P_4, b);\n        \n\n    }\n    \n    vstoreu(out + 0 * VLEN, y0);\n    \n    vstoreu(out + 1 * VLEN, y1);\n    \n    vstoreu(out + 2 * VLEN, y2);\n    \n    vstoreu(out + 3 * VLEN, y3);\n    \n    vstoreu(out + 4 * VLEN, y4);\n    \n}\n\nstatic void legendre_transform_vec6(double *recfacs, double *bl, ptrdiff_t lmax,\n                                              double xarr[(6) * VLEN],\n                                              double out[(6) * VLEN]) {\n    \n    Tv P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv P_2, Pm1_2, Pm2_2, x2, y2;\n    \n    Tv P_3, Pm1_3, Pm2_3, x3, y3;\n    \n    Tv P_4, Pm1_4, Pm2_4, x4, y4;\n    \n    Tv P_5, Pm1_5, Pm2_5, x5, y5;\n    \n    Tv W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu(xarr + 0 * VLEN);\n    Pm1_0 = vload(1.0);\n    P_0 = x0;\n    b = vload(*bl);\n    y0 = vmul(Pm1_0, b);\n    \n    x1 = vloadu(xarr + 1 * VLEN);\n    Pm1_1 = vload(1.0);\n    P_1 = x1;\n    b = vload(*bl);\n    y1 = vmul(Pm1_1, b);\n    \n    x2 = vloadu(xarr + 2 * VLEN);\n    Pm1_2 = vload(1.0);\n    P_2 = x2;\n    b = vload(*bl);\n    y2 = vmul(Pm1_2, b);\n    \n    x3 = vloadu(xarr + 3 * VLEN);\n    Pm1_3 = vload(1.0);\n    P_3 = x3;\n    b = vload(*bl);\n    y3 = vmul(Pm1_3, b);\n    \n    x4 = vloadu(xarr + 4 * VLEN);\n    Pm1_4 = vload(1.0);\n    P_4 = x4;\n    b = vload(*bl);\n    y4 = vmul(Pm1_4, b);\n    \n    x5 = vloadu(xarr + 5 * VLEN);\n    Pm1_5 = vload(1.0);\n    P_5 = x5;\n    b = vload(*bl);\n    y5 = vmul(Pm1_5, b);\n    \n    \n    b = vload(*(bl + 1));\n    \n    vfmaeq(y0, P_0, b);\n    \n    vfmaeq(y1, P_1, b);\n    \n    vfmaeq(y2, P_2, b);\n    \n    vfmaeq(y3, P_3, b);\n    \n    vfmaeq(y4, P_4, b);\n    \n    vfmaeq(y5, P_5, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload(*(bl + l));\n        R = vload(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq(P_0, W2, R);\n        vfmaeq(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq(P_1, W2, R);\n        vfmaeq(y1, P_1, b);\n        \n        Pm2_2 = Pm1_2; Pm1_2 = P_2;\n        W1 = vmul(x2, Pm1_2);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_2);\n        P_2 = W1;\n        vfmaeq(P_2, W2, R);\n        vfmaeq(y2, P_2, b);\n        \n        Pm2_3 = Pm1_3; Pm1_3 = P_3;\n        W1 = vmul(x3, Pm1_3);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_3);\n        P_3 = W1;\n        vfmaeq(P_3, W2, R);\n        vfmaeq(y3, P_3, b);\n        \n        Pm2_4 = Pm1_4; Pm1_4 = P_4;\n        W1 = vmul(x4, Pm1_4);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_4);\n        P_4 = W1;\n        vfmaeq(P_4, W2, R);\n        vfmaeq(y4, P_4, b);\n        \n        Pm2_5 = Pm1_5; Pm1_5 = P_5;\n        W1 = vmul(x5, Pm1_5);\n        W2 = W1;\n        W2 = vsub(W2, Pm2_5);\n        P_5 = W1;\n        vfmaeq(P_5, W2, R);\n        vfmaeq(y5, P_5, b);\n        \n\n    }\n    \n    vstoreu(out + 0 * VLEN, y0);\n    \n    vstoreu(out + 1 * VLEN, y1);\n    \n    vstoreu(out + 2 * VLEN, y2);\n    \n    vstoreu(out + 3 * VLEN, y3);\n    \n    vstoreu(out + 4 * VLEN, y4);\n    \n    vstoreu(out + 5 * VLEN, y5);\n    \n}\n\n\n\nstatic void legendre_transform_vec1_s(float *recfacs, float *bl, ptrdiff_t lmax,\n                                              float xarr[(1) * VLEN_s],\n                                              float out[(1) * VLEN_s]) {\n    \n    Tv_s P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv_s W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu_s(xarr + 0 * VLEN_s);\n    Pm1_0 = vload_s(1.0);\n    P_0 = x0;\n    b = vload_s(*bl);\n    y0 = vmul_s(Pm1_0, b);\n    \n    \n    b = vload_s(*(bl + 1));\n    \n    vfmaeq_s(y0, P_0, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload_s(*(bl + l));\n        R = vload_s(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul_s(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq_s(P_0, W2, R);\n        vfmaeq_s(y0, P_0, b);\n        \n\n    }\n    \n    vstoreu_s(out + 0 * VLEN_s, y0);\n    \n}\n\nstatic void legendre_transform_vec2_s(float *recfacs, float *bl, ptrdiff_t lmax,\n                                              float xarr[(2) * VLEN_s],\n                                              float out[(2) * VLEN_s]) {\n    \n    Tv_s P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv_s P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv_s W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu_s(xarr + 0 * VLEN_s);\n    Pm1_0 = vload_s(1.0);\n    P_0 = x0;\n    b = vload_s(*bl);\n    y0 = vmul_s(Pm1_0, b);\n    \n    x1 = vloadu_s(xarr + 1 * VLEN_s);\n    Pm1_1 = vload_s(1.0);\n    P_1 = x1;\n    b = vload_s(*bl);\n    y1 = vmul_s(Pm1_1, b);\n    \n    \n    b = vload_s(*(bl + 1));\n    \n    vfmaeq_s(y0, P_0, b);\n    \n    vfmaeq_s(y1, P_1, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload_s(*(bl + l));\n        R = vload_s(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul_s(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq_s(P_0, W2, R);\n        vfmaeq_s(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul_s(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq_s(P_1, W2, R);\n        vfmaeq_s(y1, P_1, b);\n        \n\n    }\n    \n    vstoreu_s(out + 0 * VLEN_s, y0);\n    \n    vstoreu_s(out + 1 * VLEN_s, y1);\n    \n}\n\nstatic void legendre_transform_vec3_s(float *recfacs, float *bl, ptrdiff_t lmax,\n                                              float xarr[(3) * VLEN_s],\n                                              float out[(3) * VLEN_s]) {\n    \n    Tv_s P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv_s P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv_s P_2, Pm1_2, Pm2_2, x2, y2;\n    \n    Tv_s W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu_s(xarr + 0 * VLEN_s);\n    Pm1_0 = vload_s(1.0);\n    P_0 = x0;\n    b = vload_s(*bl);\n    y0 = vmul_s(Pm1_0, b);\n    \n    x1 = vloadu_s(xarr + 1 * VLEN_s);\n    Pm1_1 = vload_s(1.0);\n    P_1 = x1;\n    b = vload_s(*bl);\n    y1 = vmul_s(Pm1_1, b);\n    \n    x2 = vloadu_s(xarr + 2 * VLEN_s);\n    Pm1_2 = vload_s(1.0);\n    P_2 = x2;\n    b = vload_s(*bl);\n    y2 = vmul_s(Pm1_2, b);\n    \n    \n    b = vload_s(*(bl + 1));\n    \n    vfmaeq_s(y0, P_0, b);\n    \n    vfmaeq_s(y1, P_1, b);\n    \n    vfmaeq_s(y2, P_2, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload_s(*(bl + l));\n        R = vload_s(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul_s(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq_s(P_0, W2, R);\n        vfmaeq_s(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul_s(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq_s(P_1, W2, R);\n        vfmaeq_s(y1, P_1, b);\n        \n        Pm2_2 = Pm1_2; Pm1_2 = P_2;\n        W1 = vmul_s(x2, Pm1_2);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_2);\n        P_2 = W1;\n        vfmaeq_s(P_2, W2, R);\n        vfmaeq_s(y2, P_2, b);\n        \n\n    }\n    \n    vstoreu_s(out + 0 * VLEN_s, y0);\n    \n    vstoreu_s(out + 1 * VLEN_s, y1);\n    \n    vstoreu_s(out + 2 * VLEN_s, y2);\n    \n}\n\nstatic void legendre_transform_vec4_s(float *recfacs, float *bl, ptrdiff_t lmax,\n                                              float xarr[(4) * VLEN_s],\n                                              float out[(4) * VLEN_s]) {\n    \n    Tv_s P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv_s P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv_s P_2, Pm1_2, Pm2_2, x2, y2;\n    \n    Tv_s P_3, Pm1_3, Pm2_3, x3, y3;\n    \n    Tv_s W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu_s(xarr + 0 * VLEN_s);\n    Pm1_0 = vload_s(1.0);\n    P_0 = x0;\n    b = vload_s(*bl);\n    y0 = vmul_s(Pm1_0, b);\n    \n    x1 = vloadu_s(xarr + 1 * VLEN_s);\n    Pm1_1 = vload_s(1.0);\n    P_1 = x1;\n    b = vload_s(*bl);\n    y1 = vmul_s(Pm1_1, b);\n    \n    x2 = vloadu_s(xarr + 2 * VLEN_s);\n    Pm1_2 = vload_s(1.0);\n    P_2 = x2;\n    b = vload_s(*bl);\n    y2 = vmul_s(Pm1_2, b);\n    \n    x3 = vloadu_s(xarr + 3 * VLEN_s);\n    Pm1_3 = vload_s(1.0);\n    P_3 = x3;\n    b = vload_s(*bl);\n    y3 = vmul_s(Pm1_3, b);\n    \n    \n    b = vload_s(*(bl + 1));\n    \n    vfmaeq_s(y0, P_0, b);\n    \n    vfmaeq_s(y1, P_1, b);\n    \n    vfmaeq_s(y2, P_2, b);\n    \n    vfmaeq_s(y3, P_3, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload_s(*(bl + l));\n        R = vload_s(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul_s(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq_s(P_0, W2, R);\n        vfmaeq_s(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul_s(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq_s(P_1, W2, R);\n        vfmaeq_s(y1, P_1, b);\n        \n        Pm2_2 = Pm1_2; Pm1_2 = P_2;\n        W1 = vmul_s(x2, Pm1_2);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_2);\n        P_2 = W1;\n        vfmaeq_s(P_2, W2, R);\n        vfmaeq_s(y2, P_2, b);\n        \n        Pm2_3 = Pm1_3; Pm1_3 = P_3;\n        W1 = vmul_s(x3, Pm1_3);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_3);\n        P_3 = W1;\n        vfmaeq_s(P_3, W2, R);\n        vfmaeq_s(y3, P_3, b);\n        \n\n    }\n    \n    vstoreu_s(out + 0 * VLEN_s, y0);\n    \n    vstoreu_s(out + 1 * VLEN_s, y1);\n    \n    vstoreu_s(out + 2 * VLEN_s, y2);\n    \n    vstoreu_s(out + 3 * VLEN_s, y3);\n    \n}\n\nstatic void legendre_transform_vec5_s(float *recfacs, float *bl, ptrdiff_t lmax,\n                                              float xarr[(5) * VLEN_s],\n                                              float out[(5) * VLEN_s]) {\n    \n    Tv_s P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv_s P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv_s P_2, Pm1_2, Pm2_2, x2, y2;\n    \n    Tv_s P_3, Pm1_3, Pm2_3, x3, y3;\n    \n    Tv_s P_4, Pm1_4, Pm2_4, x4, y4;\n    \n    Tv_s W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu_s(xarr + 0 * VLEN_s);\n    Pm1_0 = vload_s(1.0);\n    P_0 = x0;\n    b = vload_s(*bl);\n    y0 = vmul_s(Pm1_0, b);\n    \n    x1 = vloadu_s(xarr + 1 * VLEN_s);\n    Pm1_1 = vload_s(1.0);\n    P_1 = x1;\n    b = vload_s(*bl);\n    y1 = vmul_s(Pm1_1, b);\n    \n    x2 = vloadu_s(xarr + 2 * VLEN_s);\n    Pm1_2 = vload_s(1.0);\n    P_2 = x2;\n    b = vload_s(*bl);\n    y2 = vmul_s(Pm1_2, b);\n    \n    x3 = vloadu_s(xarr + 3 * VLEN_s);\n    Pm1_3 = vload_s(1.0);\n    P_3 = x3;\n    b = vload_s(*bl);\n    y3 = vmul_s(Pm1_3, b);\n    \n    x4 = vloadu_s(xarr + 4 * VLEN_s);\n    Pm1_4 = vload_s(1.0);\n    P_4 = x4;\n    b = vload_s(*bl);\n    y4 = vmul_s(Pm1_4, b);\n    \n    \n    b = vload_s(*(bl + 1));\n    \n    vfmaeq_s(y0, P_0, b);\n    \n    vfmaeq_s(y1, P_1, b);\n    \n    vfmaeq_s(y2, P_2, b);\n    \n    vfmaeq_s(y3, P_3, b);\n    \n    vfmaeq_s(y4, P_4, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload_s(*(bl + l));\n        R = vload_s(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul_s(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq_s(P_0, W2, R);\n        vfmaeq_s(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul_s(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq_s(P_1, W2, R);\n        vfmaeq_s(y1, P_1, b);\n        \n        Pm2_2 = Pm1_2; Pm1_2 = P_2;\n        W1 = vmul_s(x2, Pm1_2);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_2);\n        P_2 = W1;\n        vfmaeq_s(P_2, W2, R);\n        vfmaeq_s(y2, P_2, b);\n        \n        Pm2_3 = Pm1_3; Pm1_3 = P_3;\n        W1 = vmul_s(x3, Pm1_3);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_3);\n        P_3 = W1;\n        vfmaeq_s(P_3, W2, R);\n        vfmaeq_s(y3, P_3, b);\n        \n        Pm2_4 = Pm1_4; Pm1_4 = P_4;\n        W1 = vmul_s(x4, Pm1_4);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_4);\n        P_4 = W1;\n        vfmaeq_s(P_4, W2, R);\n        vfmaeq_s(y4, P_4, b);\n        \n\n    }\n    \n    vstoreu_s(out + 0 * VLEN_s, y0);\n    \n    vstoreu_s(out + 1 * VLEN_s, y1);\n    \n    vstoreu_s(out + 2 * VLEN_s, y2);\n    \n    vstoreu_s(out + 3 * VLEN_s, y3);\n    \n    vstoreu_s(out + 4 * VLEN_s, y4);\n    \n}\n\nstatic void legendre_transform_vec6_s(float *recfacs, float *bl, ptrdiff_t lmax,\n                                              float xarr[(6) * VLEN_s],\n                                              float out[(6) * VLEN_s]) {\n    \n    Tv_s P_0, Pm1_0, Pm2_0, x0, y0;\n    \n    Tv_s P_1, Pm1_1, Pm2_1, x1, y1;\n    \n    Tv_s P_2, Pm1_2, Pm2_2, x2, y2;\n    \n    Tv_s P_3, Pm1_3, Pm2_3, x3, y3;\n    \n    Tv_s P_4, Pm1_4, Pm2_4, x4, y4;\n    \n    Tv_s P_5, Pm1_5, Pm2_5, x5, y5;\n    \n    Tv_s W1, W2, b, R;\n    ptrdiff_t l;\n\n    \n    x0 = vloadu_s(xarr + 0 * VLEN_s);\n    Pm1_0 = vload_s(1.0);\n    P_0 = x0;\n    b = vload_s(*bl);\n    y0 = vmul_s(Pm1_0, b);\n    \n    x1 = vloadu_s(xarr + 1 * VLEN_s);\n    Pm1_1 = vload_s(1.0);\n    P_1 = x1;\n    b = vload_s(*bl);\n    y1 = vmul_s(Pm1_1, b);\n    \n    x2 = vloadu_s(xarr + 2 * VLEN_s);\n    Pm1_2 = vload_s(1.0);\n    P_2 = x2;\n    b = vload_s(*bl);\n    y2 = vmul_s(Pm1_2, b);\n    \n    x3 = vloadu_s(xarr + 3 * VLEN_s);\n    Pm1_3 = vload_s(1.0);\n    P_3 = x3;\n    b = vload_s(*bl);\n    y3 = vmul_s(Pm1_3, b);\n    \n    x4 = vloadu_s(xarr + 4 * VLEN_s);\n    Pm1_4 = vload_s(1.0);\n    P_4 = x4;\n    b = vload_s(*bl);\n    y4 = vmul_s(Pm1_4, b);\n    \n    x5 = vloadu_s(xarr + 5 * VLEN_s);\n    Pm1_5 = vload_s(1.0);\n    P_5 = x5;\n    b = vload_s(*bl);\n    y5 = vmul_s(Pm1_5, b);\n    \n    \n    b = vload_s(*(bl + 1));\n    \n    vfmaeq_s(y0, P_0, b);\n    \n    vfmaeq_s(y1, P_1, b);\n    \n    vfmaeq_s(y2, P_2, b);\n    \n    vfmaeq_s(y3, P_3, b);\n    \n    vfmaeq_s(y4, P_4, b);\n    \n    vfmaeq_s(y5, P_5, b);\n    \n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload_s(*(bl + l));\n        R = vload_s(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        \n        Pm2_0 = Pm1_0; Pm1_0 = P_0;\n        W1 = vmul_s(x0, Pm1_0);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_0);\n        P_0 = W1;\n        vfmaeq_s(P_0, W2, R);\n        vfmaeq_s(y0, P_0, b);\n        \n        Pm2_1 = Pm1_1; Pm1_1 = P_1;\n        W1 = vmul_s(x1, Pm1_1);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_1);\n        P_1 = W1;\n        vfmaeq_s(P_1, W2, R);\n        vfmaeq_s(y1, P_1, b);\n        \n        Pm2_2 = Pm1_2; Pm1_2 = P_2;\n        W1 = vmul_s(x2, Pm1_2);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_2);\n        P_2 = W1;\n        vfmaeq_s(P_2, W2, R);\n        vfmaeq_s(y2, P_2, b);\n        \n        Pm2_3 = Pm1_3; Pm1_3 = P_3;\n        W1 = vmul_s(x3, Pm1_3);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_3);\n        P_3 = W1;\n        vfmaeq_s(P_3, W2, R);\n        vfmaeq_s(y3, P_3, b);\n        \n        Pm2_4 = Pm1_4; Pm1_4 = P_4;\n        W1 = vmul_s(x4, Pm1_4);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_4);\n        P_4 = W1;\n        vfmaeq_s(P_4, W2, R);\n        vfmaeq_s(y4, P_4, b);\n        \n        Pm2_5 = Pm1_5; Pm1_5 = P_5;\n        W1 = vmul_s(x5, Pm1_5);\n        W2 = W1;\n        W2 = vsub_s(W2, Pm2_5);\n        P_5 = W1;\n        vfmaeq_s(P_5, W2, R);\n        vfmaeq_s(y5, P_5, b);\n        \n\n    }\n    \n    vstoreu_s(out + 0 * VLEN_s, y0);\n    \n    vstoreu_s(out + 1 * VLEN_s, y1);\n    \n    vstoreu_s(out + 2 * VLEN_s, y2);\n    \n    vstoreu_s(out + 3 * VLEN_s, y3);\n    \n    vstoreu_s(out + 4 * VLEN_s, y4);\n    \n    vstoreu_s(out + 5 * VLEN_s, y5);\n    \n}\n\n\n\n\n\nvoid sharp_legendre_transform_recfac(double *r, ptrdiff_t lmax) {\n    /* (l - 1) / l, for l >= 2 */\n    ptrdiff_t l;\n    r[0] = 0;\n    r[1] = 1;\n    for (l = 2; l <= lmax; ++l) {\n        r[l] = (double)(l - 1) / (double)l;\n    }\n}\n\nvoid sharp_legendre_transform_recfac_s(float *r, ptrdiff_t lmax) {\n    /* (l - 1) / l, for l >= 2 */\n    ptrdiff_t l;\n    r[0] = 0;\n    r[1] = 1;\n    for (l = 2; l <= lmax; ++l) {\n        r[l] = (float)(l - 1) / (float)l;\n    }\n}\n\n\n/*\n  Compute sum_l b_l P_l(x_i) for all i. \n */\n\n#define LEN (SHARP_LEGENDRE_CS * VLEN)\n#define LEN_s (SHARP_LEGENDRE_CS * VLEN_s)\n\n\nvoid sharp_legendre_transform(double *bl,\n                                   double *recfac,\n                                   ptrdiff_t lmax,\n                                   double *x, double *out, ptrdiff_t nx) {\n    double xchunk[MAX_CS * VLEN], outchunk[MAX_CS * LEN];\n    int compute_recfac;\n    ptrdiff_t i, j, len;\n\n    compute_recfac = (recfac == NULL);\n    if (compute_recfac) {\n        recfac = malloc(sizeof(double) * (lmax + 1));\n        sharp_legendre_transform_recfac(recfac, lmax);\n    }\n\n    for (j = 0; j != LEN; ++j) xchunk[j] = 0;\n\n    for (i = 0; i < nx; i += LEN) {\n        len = (i + (LEN) <= nx) ? (LEN) : (nx - i);\n        for (j = 0; j != len; ++j) xchunk[j] = x[i + j];\n        switch ((len + VLEN - 1) / VLEN) {\n          case 6: legendre_transform_vec6(recfac, bl, lmax, xchunk, outchunk); break;\n          case 5: legendre_transform_vec5(recfac, bl, lmax, xchunk, outchunk); break;\n          case 4: legendre_transform_vec4(recfac, bl, lmax, xchunk, outchunk); break;\n          case 3: legendre_transform_vec3(recfac, bl, lmax, xchunk, outchunk); break;\n          case 2: legendre_transform_vec2(recfac, bl, lmax, xchunk, outchunk); break;\n          case 1:\n          case 0:\n              legendre_transform_vec1(recfac, bl, lmax, xchunk, outchunk); break;\n        }\n        for (j = 0; j != len; ++j) out[i + j] = outchunk[j];\n    }\n    if (compute_recfac) {\n        free(recfac);\n    }\n}\n\nvoid sharp_legendre_transform_s(float *bl,\n                                   float *recfac,\n                                   ptrdiff_t lmax,\n                                   float *x, float *out, ptrdiff_t nx) {\n    float xchunk[MAX_CS * VLEN_s], outchunk[MAX_CS * LEN_s];\n    int compute_recfac;\n    ptrdiff_t i, j, len;\n\n    compute_recfac = (recfac == NULL);\n    if (compute_recfac) {\n        recfac = malloc(sizeof(float) * (lmax + 1));\n        sharp_legendre_transform_recfac_s(recfac, lmax);\n    }\n\n    for (j = 0; j != LEN_s; ++j) xchunk[j] = 0;\n\n    for (i = 0; i < nx; i += LEN_s) {\n        len = (i + (LEN_s) <= nx) ? (LEN_s) : (nx - i);\n        for (j = 0; j != len; ++j) xchunk[j] = x[i + j];\n        switch ((len + VLEN_s - 1) / VLEN_s) {\n          case 6: legendre_transform_vec6_s(recfac, bl, lmax, xchunk, outchunk); break;\n          case 5: legendre_transform_vec5_s(recfac, bl, lmax, xchunk, outchunk); break;\n          case 4: legendre_transform_vec4_s(recfac, bl, lmax, xchunk, outchunk); break;\n          case 3: legendre_transform_vec3_s(recfac, bl, lmax, xchunk, outchunk); break;\n          case 2: legendre_transform_vec2_s(recfac, bl, lmax, xchunk, outchunk); break;\n          case 1:\n          case 0:\n              legendre_transform_vec1_s(recfac, bl, lmax, xchunk, outchunk); break;\n        }\n        for (j = 0; j != len; ++j) out[i + j] = outchunk[j];\n    }\n    if (compute_recfac) {\n        free(recfac);\n    }\n}\n\n\n#endif"
  },
  {
    "path": "external/libsharp/libsharp/sharp_legendre.c.in",
    "content": "/*\n\n    NOTE NOTE NOTE\n\n    This file is edited in sharp_legendre.c.in which is then preprocessed.\n    Do not make manual  modifications to sharp_legendre.c.\n\n    NOTE NOTE NOTE\n\n*/\n\n\n/*\n *  This file is part of libsharp.\n *\n * Redistribution and use in source and binary forms, with or without\n * met:\n * \n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * \n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*! \\file sharp_legendre.c.in\n *\n *  Copyright (C) 2015 University of Oslo\n *  \\author Dag Sverre Seljebotn\n */\n\n#ifndef NO_LEGENDRE\n#if (VLEN==8)\n#error This code is not tested with MIC; please compile with -DNO_LEGENDRE\n/* ...or test it (it probably works) and remove this check */\n#endif\n\n#ifndef SHARP_LEGENDRE_CS\n#define SHARP_LEGENDRE_CS 4\n#endif\n\n#define MAX_CS 6\n#if (SHARP_LEGENDRE_CS > MAX_CS)\n#error (SHARP_LEGENDRE_CS > MAX_CS)\n#endif\n\n#include \"sharp_legendre.h\"\n#include \"sharp_vecsupport.h\"\n\n#include <stdlib.h>\n\n/*{ for scalar, T in [(\"double\", \"\"), (\"float\", \"_s\")] }*/\n/*{ for cs in range(1, 7) }*/\nstatic void legendre_transform_vec{{cs}}{{T}}({{scalar}} *recfacs, {{scalar}} *bl, ptrdiff_t lmax,\n                                              {{scalar}} xarr[({{cs}}) * VLEN{{T}}],\n                                              {{scalar}} out[({{cs}}) * VLEN{{T}}]) {\n    /*{ for i in range(cs) }*/\n    Tv{{T}} P_{{i}}, Pm1_{{i}}, Pm2_{{i}}, x{{i}}, y{{i}};\n    /*{ endfor }*/\n    Tv{{T}} W1, W2, b, R;\n    ptrdiff_t l;\n\n    /*{ for i in range(cs) }*/\n    x{{i}} = vloadu{{T}}(xarr + {{i}} * VLEN{{T}});\n    Pm1_{{i}} = vload{{T}}(1.0);\n    P_{{i}} = x{{i}};\n    b = vload{{T}}(*bl);\n    y{{i}} = vmul{{T}}(Pm1_{{i}}, b);\n    /*{ endfor }*/\n    \n    b = vload{{T}}(*(bl + 1));\n    /*{ for i in range(cs) }*/\n    vfmaeq{{T}}(y{{i}}, P_{{i}}, b);\n    /*{ endfor }*/\n\n    for (l = 2; l <= lmax; ++l) {\n        b = vload{{T}}(*(bl + l));\n        R = vload{{T}}(*(recfacs + l));\n        \n        /* \n           P = x * Pm1 + recfacs[l] * (x * Pm1 - Pm2)\n        */\n        /*{ for i in range(cs) }*/\n        Pm2_{{i}} = Pm1_{{i}}; Pm1_{{i}} = P_{{i}};\n        W1 = vmul{{T}}(x{{i}}, Pm1_{{i}});\n        W2 = W1;\n        W2 = vsub{{T}}(W2, Pm2_{{i}});\n        P_{{i}} = W1;\n        vfmaeq{{T}}(P_{{i}}, W2, R);\n        vfmaeq{{T}}(y{{i}}, P_{{i}}, b);\n        /*{ endfor }*/\n\n    }\n    /*{ for i in range(cs) }*/\n    vstoreu{{T}}(out + {{i}} * VLEN{{T}}, y{{i}});\n    /*{ endfor }*/\n}\n/*{ endfor }*/\n/*{ endfor }*/\n\n\n/*{ for scalar, T in [(\"double\", \"\"), (\"float\", \"_s\")] }*/\nvoid sharp_legendre_transform_recfac{{T}}({{scalar}} *r, ptrdiff_t lmax) {\n    /* (l - 1) / l, for l >= 2 */\n    ptrdiff_t l;\n    r[0] = 0;\n    r[1] = 1;\n    for (l = 2; l <= lmax; ++l) {\n        r[l] = ({{scalar}})(l - 1) / ({{scalar}})l;\n    }\n}\n/*{ endfor }*/\n\n/*\n  Compute sum_l b_l P_l(x_i) for all i. \n */\n\n#define LEN (SHARP_LEGENDRE_CS * VLEN)\n#define LEN_s (SHARP_LEGENDRE_CS * VLEN_s)\n\n/*{ for scalar, T in [(\"double\", \"\"), (\"float\", \"_s\")] }*/\nvoid sharp_legendre_transform{{T}}({{scalar}} *bl,\n                                   {{scalar}} *recfac,\n                                   ptrdiff_t lmax,\n                                   {{scalar}} *x, {{scalar}} *out, ptrdiff_t nx) {\n    {{scalar}} xchunk[MAX_CS * VLEN{{T}}], outchunk[MAX_CS * LEN{{T}}];\n    int compute_recfac;\n    ptrdiff_t i, j, len;\n\n    compute_recfac = (recfac == NULL);\n    if (compute_recfac) {\n        recfac = malloc(sizeof({{scalar}}) * (lmax + 1));\n        sharp_legendre_transform_recfac{{T}}(recfac, lmax);\n    }\n\n    for (j = 0; j != LEN{{T}}; ++j) xchunk[j] = 0;\n\n    for (i = 0; i < nx; i += LEN{{T}}) {\n        len = (i + (LEN{{T}}) <= nx) ? (LEN{{T}}) : (nx - i);\n        for (j = 0; j != len; ++j) xchunk[j] = x[i + j];\n        switch ((len + VLEN{{T}} - 1) / VLEN{{T}}) {\n          case 6: legendre_transform_vec6{{T}}(recfac, bl, lmax, xchunk, outchunk); break;\n          case 5: legendre_transform_vec5{{T}}(recfac, bl, lmax, xchunk, outchunk); break;\n          case 4: legendre_transform_vec4{{T}}(recfac, bl, lmax, xchunk, outchunk); break;\n          case 3: legendre_transform_vec3{{T}}(recfac, bl, lmax, xchunk, outchunk); break;\n          case 2: legendre_transform_vec2{{T}}(recfac, bl, lmax, xchunk, outchunk); break;\n          case 1:\n          case 0:\n              legendre_transform_vec1{{T}}(recfac, bl, lmax, xchunk, outchunk); break;\n        }\n        for (j = 0; j != len; ++j) out[i + j] = outchunk[j];\n    }\n    if (compute_recfac) {\n        free(recfac);\n    }\n}\n/*{ endfor }*/\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_legendre.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n * Redistribution and use in source and binary forms, with or without\n * met:\n * \n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * \n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*! \\file sharp_legendre.h\n *  Interface for the Legendre transform parts of the spherical transform library.\n *\n *  Copyright (C) 2015 University of Oslo\n *  \\author Dag Sverre Seljebotn\n */\n\n#ifndef SHARP_LEGENDRE_H\n#define SHARP_LEGENDRE_H\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef NO_LEGENDRE\n\nvoid sharp_legendre_transform(double *bl, double *recfac, ptrdiff_t lmax, double *x,\n                              double *out, ptrdiff_t nx);\nvoid sharp_legendre_transform_s(float *bl, float *recfac, ptrdiff_t lmax, float *x,\n                                float *out, ptrdiff_t nx);\nvoid sharp_legendre_transform_recfac(double *r, ptrdiff_t lmax);\nvoid sharp_legendre_transform_recfac_s(float *r, ptrdiff_t lmax);\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_legendre_roots.c",
    "content": "/* Function adapted from GNU GSL file glfixed.c\n   Original author: Pavel Holoborodko (http://www.holoborodko.com)\n\n   Adjustments by M. Reinecke\n    - adjusted interface (keep epsilon internal, return full number of points)\n    - removed precomputed tables\n    - tweaked Newton iteration to obtain higher accuracy */\n\n#include <math.h>\n#include \"sharp_legendre_roots.h\"\n#include \"c_utils.h\"\n\nstatic inline double one_minus_x2 (double x)\n  { return (fabs(x)>0.1) ? (1.+x)*(1.-x) : 1.-x*x; }\n\nvoid sharp_legendre_roots(int n, double *x, double *w)\n  {\n  const double pi = 3.141592653589793238462643383279502884197;\n  const double eps = 3e-14;\n  int m = (n+1)>>1;\n\n  double t0 = 1 - (1-1./n) / (8.*n*n);\n  double t1 = 1./(4.*n+2.);\n\n#pragma omp parallel\n{\n  int i;\n#pragma omp for schedule(dynamic,100)\n  for (i=1; i<=m; ++i)\n    {\n    double x0 = cos(pi * ((i<<2)-1) * t1) * t0;\n\n    int dobreak=0;\n    int j=0;\n    double dpdx;\n    while(1)\n      {\n      double P_1 = 1.0;\n      double P0 = x0;\n      double dx, x1;\n\n      for (int k=2; k<=n; k++)\n        {\n        double P_2 = P_1;\n        P_1 = P0;\n//        P0 = ((2*k-1)*x0*P_1-(k-1)*P_2)/k;\n        P0 = x0*P_1 + (k-1.)/k * (x0*P_1-P_2);\n        }\n\n      dpdx = (P_1 - x0*P0) * n / one_minus_x2(x0);\n\n      /* Newton step */\n      x1 = x0 - P0/dpdx;\n      dx = x0-x1;\n      x0 = x1;\n      if (dobreak) break;\n\n      if (fabs(dx)<=eps) dobreak=1;\n      UTIL_ASSERT(++j<100,\"convergence problem\");\n      }\n\n    x[i-1] = -x0;\n    x[n-i] = x0;\n    w[i-1] = w[n-i] = 2. / (one_minus_x2(x0) * dpdx * dpdx);\n    }\n} // end of parallel region\n  }\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_legendre_roots.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_legendre_roots.h\n *\n *  Copyright (C) 2006-2012 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef SHARP_LEGENDRE_ROOTS_H\n#define SHARP_LEGENDRE_ROOTS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*! Computes roots and Gaussian quadrature weights for Legendre polynomial\n    of degree \\a n.\n    \\param n Order of Legendre polynomial\n    \\param x Array of length \\a n for output (root position)\n    \\param w Array of length \\a w for output (weight for Gaussian quadrature)\n */\nvoid sharp_legendre_roots(int n, double *x, double *w);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_legendre_table.c",
    "content": "/*\n\nThis file originated as a concatenation of files from libpsht. Further refactoring\ncould be carried out to make the code use libsharp conventions instead for SSE etc.;\n\n*/\n\n\n/*\nsse_utils.h\n*/\n/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sse_utils.h\n *  SSE/SSE2/SSE3-related functionality\n *\n *  Copyright (C) 2010,2011 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n\n#if (defined(__SSE__))\n\n#include <xmmintrin.h>\n\n\ntypedef __m128 v4sf; /* vector of 4 floats (SSE1) */\n\ntypedef union {\n  float f[4];\n  v4sf v;\n} V4SF;\n\nstatic inline v4sf build_v4sf (float a, float b, float c, float d)\n  { return _mm_set_ps(d,c,b,a); }\nstatic inline void read_v4sf (v4sf v, float *a, float *b, float *c, float *d)\n  {\n  V4SF tmp;\n  tmp.v = v;\n  if (a) *a=tmp.f[0];\n  if (b) *b=tmp.f[1];\n  if (c) *c=tmp.f[2];\n  if (d) *d=tmp.f[3];\n  }\n\n\n#endif\n\n#if (defined(__SSE2__))\n\n#include <emmintrin.h>\n\n\ntypedef __m128d v2df; /* vector of 2 doubles (SSE2) */\n\ntypedef union {\n  double d[2];\n  v2df v;\n} V2DF;\n\ntypedef struct {\n  v2df a,b;\n} v2df2;\ntypedef struct {\n  V2DF a,b;\n} V2DF2;\n\n#define V2DF_SIGNMASK _mm_set1_pd(-0.0)\n\nstatic inline v2df build_v2df (double a, double b)\n  { return _mm_set_pd(b,a); }\nstatic inline void read_v2df (v2df v, double *a, double *b)\n  { _mm_store_sd(a,v); _mm_storeh_pd(b,v); }\n\nstatic inline int v2df_any_gt (v2df a, v2df b)\n  {\n  return (_mm_movemask_pd(_mm_cmpgt_pd(_mm_andnot_pd(V2DF_SIGNMASK,a),b))!=0);\n  }\nstatic inline int v2df_all_ge (v2df a, v2df b)\n  {\n  return (_mm_movemask_pd(_mm_cmplt_pd(_mm_andnot_pd(V2DF_SIGNMASK,a),b))==0);\n  }\nstatic inline V2DF to_V2DF (v2df x)\n  { V2DF X; X.v=x; return X; }\nstatic inline V2DF2 to_V2DF2 (v2df2 x)\n  { V2DF2 X; X.a.v=x.a; X.b.v=x.b; return X; }\nstatic inline v2df2 to_v2df2 (V2DF2 X)\n  { v2df2 x; x.a=X.a.v; x.b=X.b.v; return x; }\nstatic inline v2df2 zero_v2df2(void)\n  { v2df2 x; x.a=x.b=_mm_setzero_pd(); return x; }\n\n\n#endif\n\n#if (defined(__SSE3__))\n\n#include <pmmintrin.h>\n\n#endif\n\n\n\n/*\nylmgen_c.h\n*/\n/*\n *  This file is part of libpsht.\n *\n *  libpsht is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libpsht is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libpsht; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libpsht is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file ylmgen_c.h\n *  Code for efficient calculation of Y_lm(phi=0,theta)\n *\n *  Copyright (C) 2005-2011 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\ntypedef double ylmgen_dbl2[2];\ntypedef double ylmgen_dbl3[3];\n\ntypedef struct\n  {\n  double cth_crit;\n  int mdist_crit;\n  /* members depending on m and m' */\n  int s, m, mlo, mhi, cosPow, sinPow;\n  long double prefactor;\n  ylmgen_dbl3 *fx;\n  int preMinus_p, preMinus_m;\n  } sylmgen_d;\n\ntypedef struct\n  {\n  double fsmall, fbig, eps, cth_crit;\n  int lmax, mmax, smax, m_cur, ith, nth, m_crit, spinrec;\n  /*! The index of the first non-negligible Y_lm value. */\n  int *firstl;\n  double *cf, *mfac, *t1fac, *t2fac, *th, *cth, *sth, *logsth;\n  ylmgen_dbl2 *recfac;\n  double *lamfact;\n  /*! Points to an array of size [0..lmax] containing the Y_lm values. */\n  double *ylm;\n  /*! Points to an array of size [0..lmax] containing the lambda_w\n      and lambda_x values for spin>0 transforms. */\n  ylmgen_dbl2 **lambda_wx;\n  long double *logsum, *lc05, *ls05;\n  double *flm1, *flm2, *xl;\n\n  sylmgen_d **sylm;\n\n  int *lwx_uptodate;\n  int ylm_uptodate;\n\n#ifdef __SSE2__\n  int ith1, ith2;\n  /*! Points to an array of size [0..lmax] containing the Y_lm values. */\n  v2df *ylm_sse2;\n  /*! Points to an array of size [0..lmax] containing the lambda_w\n      and lambda_x values for spin>0 transforms. */\n  v2df2 **lambda_wx_sse2;\n  int *lwx_uptodate_sse2;\n  int ylm_uptodate_sse2;\n#endif\n\n  int recfac_uptodate, lamfact_uptodate;\n  } Ylmgen_C;\n\n/*! Creates a generator which will calculate Y_lm(theta,phi=0)\n    up to \\a l=l_max and \\a m=m_max. It may regard Y_lm whose absolute\n    magnitude is smaller than \\a epsilon as zero. If \\a spinrec is nonzero,\n    the spin-1 and spin-2 Y_lm will be calculated by recursion from the spin-0\n    ones, otherwise Wigner d matrix elements will be used. */\nstatic void Ylmgen_init (Ylmgen_C *gen, int l_max, int m_max, int s_max, int spinrec,\n   double epsilon);\n\n/*! Passes am array \\a theta of \\a nth colatitudes that will be used in\n    subsequent calls. The individual angles will be referenced by their\n    index in the array, starting with 0.\n    \\note The array can be freed or reused after the call. */\nstatic void Ylmgen_set_theta (Ylmgen_C *gen, const double *theta, int nth);\n\n/*! Deallocates a generator previously initialised by Ylmgen_init(). */\nstatic void Ylmgen_destroy (Ylmgen_C *gen);\n\n/*! Prepares the object for the calculation at \\a theta and \\a m. */\nstatic void Ylmgen_prepare (Ylmgen_C *gen, int ith, int m);\n\n/*! Recalculates (if necessary) the Y_lm values. */\nstatic void Ylmgen_recalc_Ylm (Ylmgen_C *gen);\n/*! Recalculates (if necessary) the lambda_w and lambda_x values for spin >0\n    transforms. */\n/*static void Ylmgen_recalc_lambda_wx (Ylmgen_C *gen, int spin);*/\n\n#ifdef __SSE2__\n/*! Prepares the object for the calculation at \\a theta, \\a theta2 and \\a m. */\nstatic void Ylmgen_prepare_sse2 (Ylmgen_C *gen, int ith1, int ith2, int m);\n\n/*! Recalculates (if necessary) the Y_lm values. */\nstatic void Ylmgen_recalc_Ylm_sse2 (Ylmgen_C *gen);\n/*! Recalculates (if necessary) the lambda_w and lambda_x values for spin >0\n    transforms. */\n/*static void Ylmgen_recalc_lambda_wx_sse2 (Ylmgen_C *gen, int spin);*/\n#endif\n\n/*! Returns a pointer to an array with lmax+1 entries containing normalisation\n    factors that must be applied to Y_lm values computed for \\a spin with the\n    given \\a spinrec flag. The array must be deallocated (using free()) by the\n    user. */\n/*static double *Ylmgen_get_norm (int lmax, int spin, int spinrec);*/\n\n\n/*\nylmgen_c.c\n*/\n\n/*\n *  This file is part of libpsht.\n *\n *  libpsht is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libpsht is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libpsht; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libpsht is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n *  Code for efficient calculation of Y_lm(theta,phi=0)\n *\n *  Copyright (C) 2005-2011 Max-Planck-Society\n *  Author: Martin Reinecke\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include \"c_utils.h\"\n\nenum { large_exponent2=90, minscale=-4, maxscale=11 };\n\n/*static void sylmgen_init (sylmgen_d *gen, const Ylmgen_C *ygen, int spin)\n  {\n  int i;\n  UTIL_ASSERT(spin>=1,\"incorrect spin\");\n  gen->s=spin;\n  gen->m=gen->mlo=gen->mhi=-1234567890;\n  ALLOC(gen->fx,ylmgen_dbl3,ygen->lmax+2);\n\n  for (i=0; i<ygen->lmax+2; ++i)\n    gen->fx[i][0]=gen->fx[i][1]=gen->fx[i][2]=0.;\n\n  gen->cth_crit = 2.;\n  gen->mdist_crit = ygen->lmax+1;\n  }*/\n\nstatic void sylmgen_destroy (sylmgen_d *gen)\n  { DEALLOC(gen->fx); }\n\n/*static void sylmgen_prepare (sylmgen_d *gen, const Ylmgen_C *ygen, int m_)\n  {\n  int mlo_, mhi_, ms_similar, l;\n\n  if (m_==gen->m) return;\n  UTIL_ASSERT(m_>=0,\"incorrect m\");\n\n  mlo_=m_; mhi_=gen->s;\n  if (mhi_<mlo_) SWAP(mhi_,mlo_,int);\n  ms_similar = ((gen->mhi==mhi_) && (gen->mlo==mlo_));\n\n  gen->m=m_;\n  gen->mlo = mlo_; gen->mhi = mhi_;\n\n  if (!ms_similar)\n    {\n    for (l=gen->mhi; l<ygen->lmax; ++l)\n      {\n      double t = ygen->flm1[l+gen->m]*ygen->flm1[l-gen->m]\n                *ygen->flm1[l+gen->s]*ygen->flm1[l-gen->s];\n      double lt = 2*l+1;\n      double l1 = l+1;\n      gen->fx[l+1][0]=l1*lt*t;\n      gen->fx[l+1][1]=gen->m*gen->s*ygen->xl[l]*ygen->xl[l+1];\n      t = ygen->flm2[l+gen->m]*ygen->flm2[l-gen->m]\n         *ygen->flm2[l+gen->s]*ygen->flm2[l-gen->s];\n      gen->fx[l+1][2]=t*l1*ygen->xl[l];\n      }\n    gen->prefactor = 0.5L*(ygen->logsum[2*gen->mhi]\n      -ygen->logsum[gen->mhi+gen->mlo]-ygen->logsum[gen->mhi-gen->mlo]);\n    }\n\n  gen->preMinus_p = gen->preMinus_m = 0;\n  if (gen->mhi==gen->m)\n    {\n    gen->cosPow = gen->mhi+gen->s; gen->sinPow = gen->mhi-gen->s;\n    gen->preMinus_p = gen->preMinus_m = ((gen->mhi-gen->s)&1);\n    }\n  else\n    {\n    gen->cosPow = gen->mhi+gen->m; gen->sinPow = gen->mhi-gen->m;\n    gen->preMinus_m = ((gen->mhi+gen->m)&1);\n    }\n    }*/\n\n/*static void sylmgen_recalc (sylmgen_d *gen, const Ylmgen_C *ygen, int ith,\n  ylmgen_dbl2 *res, int *firstl)\n  {\n  const double ln2     = 0.6931471805599453094172321214581766;\n  const double inv_ln2 = 1.4426950408889634073599246810018921;\n  int l=gen->mhi;\n  int lmax = ygen->lmax;\n  ylmgen_dbl3 *fy = gen->fx;\n  const double fsmall = ygen->fsmall, fbig = ygen->fbig, eps = ygen->eps;\n  const double cth = ygen->cth[ith];\n  long double logvalp = inv_ln2*(gen->prefactor\n    + ygen->lc05[ith]*gen->cosPow + ygen->ls05[ith]*gen->sinPow);\n  long double logvalm = inv_ln2*(gen->prefactor\n    + ygen->lc05[ith]*gen->sinPow + ygen->ls05[ith]*gen->cosPow);\n  int scalep = (int)(logvalp/large_exponent2)-minscale;\n  int scalem = (int)(logvalm/large_exponent2)-minscale;\n  double rec1p=0., rec1m=0.;\n  double rec2p = exp(ln2*(double)(logvalp-(scalep+minscale)*large_exponent2));\n  double rec2m = exp(ln2*(double)(logvalm-(scalem+minscale)*large_exponent2));\n  double corfacp,corfacm;\n  double tp,tm;\n\n  if ((abs(gen->m-gen->s)>=gen->mdist_crit)&&(fabs(cth)>=gen->cth_crit))\n    { *firstl=ygen->lmax+1; return; }\n\n  if (gen->preMinus_p)\n    rec2p=-rec2p;\n  if (gen->preMinus_m)\n    rec2m=-rec2m;\n  if (gen->s&1)\n    rec2p=-rec2p;\n\n  / iterate until we reach the realm of IEEE numbers /\n  while((scalem<0)&&(scalep<0))\n    {\n    if (++l>lmax) break;\n    rec1p = (cth - fy[l][1])*fy[l][0]*rec2p - fy[l][2]*rec1p;\n    rec1m = (cth + fy[l][1])*fy[l][0]*rec2m - fy[l][2]*rec1m;\n    if (++l>lmax) break;\n    rec2p = (cth - fy[l][1])*fy[l][0]*rec1p - fy[l][2]*rec2p;\n    rec2m = (cth + fy[l][1])*fy[l][0]*rec1m - fy[l][2]*rec2m;\n\n    while (fabs(rec2p)>fbig)\n      { rec1p *= fsmall; rec2p *= fsmall; ++scalep; }\n    while (fabs(rec2m)>fbig)\n      { rec1m *= fsmall; rec2m *= fsmall; ++scalem; }\n    }\n\n  corfacp = (scalep<0) ? 0. : ygen->cf[scalep];\n  corfacm = (scalem<0) ? 0. : ygen->cf[scalem];\n\n  if (l<=lmax)\n    {\n    while (1)\n      {\n      if ((fabs(rec2p*corfacp)>eps) || (fabs(rec2m*corfacm)>eps))\n        break;\n      if (++l>lmax) break;\n      rec1p = (cth - fy[l][1])*fy[l][0]*rec2p - fy[l][2]*rec1p;\n      rec1m = (cth + fy[l][1])*fy[l][0]*rec2m - fy[l][2]*rec1m;\n      if ((fabs(rec1p*corfacp)>eps) || (fabs(rec1m*corfacm)>eps))\n        { SWAP(rec1p,rec2p,double); SWAP(rec1m,rec2m,double); break; }\n      if (++l>lmax) break;\n      rec2p = (cth - fy[l][1])*fy[l][0]*rec1p - fy[l][2]*rec2p;\n      rec2m = (cth + fy[l][1])*fy[l][0]*rec1m - fy[l][2]*rec2m;\n\n      if ((fabs(rec2p)>fbig) || (fabs(rec2m)>fbig))\n        {\n        while (fabs(rec2p)>fbig)\n          { rec1p *= fsmall; rec2p *= fsmall; ++scalep; }\n        while (fabs(rec2m)>fbig)\n          { rec1m *= fsmall; rec2m *= fsmall; ++scalem; }\n        corfacp = (scalep<0) ? 0. : ygen->cf[scalep];\n        corfacm = (scalem<0) ? 0. : ygen->cf[scalem];\n        }\n      }\n    }\n\n  *firstl=l;\n  if (l>lmax)\n    {\n    gen->mdist_crit=abs(gen->m-gen->s);\n    gen->cth_crit= fabs(cth);\n    return;\n    }\n\n  tp = rec2p*corfacp; tm = rec2m*corfacm;\n  res[l][0]=tp+tm;\n  res[l][1]=tm-tp;\n\n  while (1)\n    {\n    if ((fabs(tp)>eps) && (fabs(tm)>eps))\n      break;\n    if (++l>lmax) break;\n    rec1p = (cth - fy[l][1])*fy[l][0]*rec2p - fy[l][2]*rec1p;\n    rec1m = (cth + fy[l][1])*fy[l][0]*rec2m - fy[l][2]*rec1m;\n    tp=rec1p*corfacp; tm=rec1m*corfacm;\n    res[l][0]=tp+tm; res[l][1]=tm-tp;\n    if ((fabs(tp)>eps) && (fabs(tm)>eps))\n      { SWAP(rec1p,rec2p,double); SWAP(rec1m,rec2m,double); break; }\n    if (++l>lmax) break;\n    rec2p = (cth - fy[l][1])*fy[l][0]*rec1p - fy[l][2]*rec2p;\n    rec2m = (cth + fy[l][1])*fy[l][0]*rec1m - fy[l][2]*rec2m;\n    tp=rec2p*corfacp; tm=rec2m*corfacm;\n    res[l][0]=tp+tm; res[l][1]=tm-tp;\n\n    if ((fabs(rec2p)>fbig) || (fabs(rec2m)>fbig))\n      {\n      while (fabs(rec2p)>fbig)\n        { rec1p *= fsmall; rec2p *= fsmall; ++scalep; }\n      while (fabs(rec2m)>fbig)\n        { rec1m *= fsmall; rec2m *= fsmall; ++scalem; }\n      corfacp = (scalep<0) ? 0. : ygen->cf[scalep];\n      corfacm = (scalem<0) ? 0. : ygen->cf[scalem];\n      }\n    }\n\n  rec1p *= corfacp; rec2p *= corfacp;\n  rec1m *= corfacm; rec2m *= corfacm;\n\n  for (;l<lmax-1;l+=2)\n    {\n    rec1p = (cth - fy[l+1][1])*fy[l+1][0]*rec2p - fy[l+1][2]*rec1p;\n    rec1m = (cth + fy[l+1][1])*fy[l+1][0]*rec2m - fy[l+1][2]*rec1m;\n    res[l+1][0] = rec1p+rec1m; res[l+1][1] = rec1m-rec1p;\n    rec2p = (cth - fy[l+2][1])*fy[l+2][0]*rec1p - fy[l+2][2]*rec2p;\n    rec2m = (cth + fy[l+2][1])*fy[l+2][0]*rec1m - fy[l+2][2]*rec2m;\n    res[l+2][0] = rec2p+rec2m; res[l+2][1] = rec2m-rec2p;\n    }\n  while (1)\n    {\n    if (++l>lmax) break;\n    rec1p = (cth - fy[l][1])*fy[l][0]*rec2p - fy[l][2]*rec1p;\n    rec1m = (cth + fy[l][1])*fy[l][0]*rec2m - fy[l][2]*rec1m;\n    res[l][0] = rec1p+rec1m; res[l][1] = rec1m-rec1p;\n    if (++l>lmax) break;\n    rec2p = (cth - fy[l][1])*fy[l][0]*rec1p - fy[l][2]*rec2p;\n    rec2m = (cth + fy[l][1])*fy[l][0]*rec1m - fy[l][2]*rec2m;\n    res[l][0] = rec2p+rec2m; res[l][1] = rec2m-rec2p;\n    }\n    }*/\n\n#ifdef __SSE2__\n\n#define ADVANCE(L,ap,am,bp,bm) \\\n  { \\\n  v2df f0=_mm_set1_pd(fy[L][0]), \\\n       f1=_mm_set1_pd(fy[L][1]), \\\n       f2=_mm_set1_pd(fy[L][2]); \\\n  ap = _mm_sub_pd( \\\n       _mm_mul_pd(_mm_sub_pd(cth,f1),_mm_mul_pd(f0,bp)), \\\n       _mm_mul_pd(f2,ap)); \\\n  am = _mm_sub_pd( \\\n       _mm_mul_pd(_mm_add_pd(cth,f1),_mm_mul_pd(f0,bm)), \\\n       _mm_mul_pd(f2,am)); \\\n  }\n\n#define RENORMX(r1,r2,corf,sca,scb) \\\n  do \\\n    { \\\n    double rec1a, rec1b, rec2a, rec2b, corfaca, corfacb; \\\n    read_v2df (r1, &rec1a, &rec1b); read_v2df (r2, &rec2a, &rec2b); \\\n    read_v2df (corf, &corfaca, &corfacb); \\\n    while (fabs(rec2a)>fbig) \\\n      { \\\n      rec1a*=fsmall; rec2a*=fsmall; ++sca; \\\n      corfaca = (sca<0) ? 0. : ygen->cf[sca]; \\\n      } \\\n    while (fabs(rec2b)>fbig) \\\n      { \\\n      rec1b*=fsmall; rec2b*=fsmall; ++scb; \\\n      corfacb = (scb<0) ? 0. : ygen->cf[scb]; \\\n      } \\\n    r1=build_v2df(rec1a,rec1b); r2=build_v2df(rec2a,rec2b); \\\n    corf=build_v2df(corfaca,corfacb); \\\n    } \\\n  while(0)\n\n#define RENORM \\\n  RENORMX(rec1p,rec2p,corfacp,scale1p,scale2p); \\\n  RENORMX(rec1m,rec2m,corfacm,scale1m,scale2m)\n\n/*static void sylmgen_recalc_sse2 (sylmgen_d *gen, const Ylmgen_C *ygen,\n  int ith1, int ith2, v2df2 *res, int *firstl)\n  {\n  const double ln2     = 0.6931471805599453094172321214581766;\n  const double inv_ln2 = 1.4426950408889634073599246810018921;\n  int l=gen->mhi;\n  int lmax = ygen->lmax;\n  ylmgen_dbl3 *fy = gen->fx;\n  const double fbig=ygen->fbig, fsmall=ygen->fsmall;\n  v2df eps2    = build_v2df(ygen->eps,ygen->eps);\n  const double cth1=ygen->cth[ith1], cth2=ygen->cth[ith2];\n  v2df cth = build_v2df(cth1,cth2);\n  long double\n    logval1p = inv_ln2*(gen->prefactor\n             + ygen->lc05[ith1]*gen->cosPow + ygen->ls05[ith1]*gen->sinPow),\n    logval2p = inv_ln2*(gen->prefactor\n             + ygen->lc05[ith2]*gen->cosPow + ygen->ls05[ith2]*gen->sinPow),\n    logval1m = inv_ln2*(gen->prefactor\n             + ygen->lc05[ith1]*gen->sinPow + ygen->ls05[ith1]*gen->cosPow),\n    logval2m = inv_ln2*(gen->prefactor\n             + ygen->lc05[ith2]*gen->sinPow + ygen->ls05[ith2]*gen->cosPow);\n\n  int scale1p = (int)(logval1p/large_exponent2)-minscale,\n      scale2p = (int)(logval2p/large_exponent2)-minscale,\n      scale1m = (int)(logval1m/large_exponent2)-minscale,\n      scale2m = (int)(logval2m/large_exponent2)-minscale;\n\n  v2df rec1p =_mm_setzero_pd(), rec1m=_mm_setzero_pd();\n  v2df rec2p = build_v2df(\n    exp(ln2*(double)(logval1p-(scale1p+minscale)*large_exponent2)),\n    exp(ln2*(double)(logval2p-(scale2p+minscale)*large_exponent2)));\n  v2df rec2m = build_v2df(\n    exp(ln2*(double)(logval1m-(scale1m+minscale)*large_exponent2)),\n    exp(ln2*(double)(logval2m-(scale2m+minscale)*large_exponent2)));\n  v2df corfacp=build_v2df((scale1p<0) ? 0. : ygen->cf[scale1p],\n                          (scale2p<0) ? 0. : ygen->cf[scale2p]),\n       corfacm=build_v2df((scale1m<0) ? 0. : ygen->cf[scale1m],\n                          (scale2m<0) ? 0. : ygen->cf[scale2m]);\n  v2df tp,tm;\n\n  if ((abs(gen->m-gen->s)>=gen->mdist_crit)\n     &&(fabs(ygen->cth[ith1])>=gen->cth_crit)\n     &&(fabs(ygen->cth[ith2])>=gen->cth_crit))\n    { *firstl=ygen->lmax+1; return; }\n\n  if (gen->preMinus_p)\n    rec2p = _mm_xor_pd (rec2p,V2DF_SIGNMASK); / negate /\n  if (gen->preMinus_m)\n    rec2m = _mm_xor_pd (rec2m,V2DF_SIGNMASK); / negate /\n  if (gen->s&1)\n    rec2p = _mm_xor_pd (rec2p,V2DF_SIGNMASK); / negate /\n\n  / iterate until we reach the realm of IEEE numbers /\n  while((scale1m<0)&&(scale1p<0)&&(scale2m<0)&&(scale2p<0))\n    {\n    if (++l>lmax) break;\n    ADVANCE (l,rec1p,rec1m,rec2p,rec2m)\n    if (++l>lmax) break;\n    ADVANCE (l,rec2p,rec2m,rec1p,rec1m)\n\n    RENORM;\n    }\n\n  if (l<=lmax)\n    {\n    while (1)\n      {\n      if (v2df_any_gt(_mm_mul_pd(rec2p,corfacp),eps2) ||\n          v2df_any_gt(_mm_mul_pd(rec2m,corfacm),eps2))\n        break;\n      if (++l>lmax) break;\n      ADVANCE (l,rec1p,rec1m,rec2p,rec2m)\n      if (v2df_any_gt(_mm_mul_pd(rec1p,corfacp),eps2) ||\n          v2df_any_gt(_mm_mul_pd(rec1m,corfacm),eps2))\n        { SWAP(rec1p,rec2p,v2df); SWAP(rec1m,rec2m,v2df); break; }\n      if (++l>lmax) break;\n      ADVANCE (l,rec2p,rec2m,rec1p,rec1m)\n\n      RENORM;\n      }\n    }\n\n  *firstl=l;\n  if (l>lmax)\n    {\n    gen->mdist_crit=abs(gen->m-gen->s);\n    gen->cth_crit= (fabs(cth1)<fabs(cth2)) ? fabs(cth1) : fabs(cth2);\n    return;\n    }\n  tp = _mm_mul_pd(rec2p,corfacp); tm = _mm_mul_pd(rec2m,corfacm);\n  res[l].a=_mm_add_pd(tp,tm);\n  res[l].b=_mm_sub_pd(tm,tp);\n\n  while (1)\n    {\n    if (v2df_all_ge(tp,eps2) && v2df_all_ge(tm,eps2))\n      break;\n    if (++l>lmax) break;\n    ADVANCE(l,rec1p,rec1m,rec2p,rec2m)\n    tp=_mm_mul_pd(rec1p,corfacp); tm=_mm_mul_pd(rec1m,corfacm);\n    res[l].a=_mm_add_pd(tp,tm); res[l].b=_mm_sub_pd(tm,tp);\n    if (v2df_all_ge(tp,eps2) && v2df_all_ge(tm,eps2))\n      { SWAP(rec1p,rec2p,v2df); SWAP(rec1m,rec2m,v2df); break; }\n    if (++l>lmax) break;\n    ADVANCE (l,rec2p,rec2m,rec1p,rec1m)\n    tp=_mm_mul_pd(rec2p,corfacp); tm=_mm_mul_pd(rec2m,corfacm);\n    res[l].a=_mm_add_pd(tp,tm); res[l].b=_mm_sub_pd(tm,tp);\n\n    RENORM;\n    }\n\n  rec1p = _mm_mul_pd(rec1p,corfacp); rec2p = _mm_mul_pd(rec2p,corfacp);\n  rec1m = _mm_mul_pd(rec1m,corfacm); rec2m = _mm_mul_pd(rec2m,corfacm);\n\n  for (;l<lmax-1;l+=2)\n    {\n    ADVANCE(l+1,rec1p,rec1m,rec2p,rec2m)\n    res[l+1].a=_mm_add_pd(rec1p,rec1m); res[l+1].b=_mm_sub_pd(rec1m,rec1p);\n    ADVANCE(l+2,rec2p,rec2m,rec1p,rec1m)\n    res[l+2].a=_mm_add_pd(rec2p,rec2m); res[l+2].b=_mm_sub_pd(rec2m,rec2p);\n    }\n  while (1)\n    {\n    if (++l>lmax) break;\n    ADVANCE(l,rec1p,rec1m,rec2p,rec2m)\n    res[l].a=_mm_add_pd(rec1p,rec1m); res[l].b=_mm_sub_pd(rec1m,rec1p);\n    if (++l>lmax) break;\n    ADVANCE(l,rec2p,rec2m,rec1p,rec1m)\n    res[l].a=_mm_add_pd(rec2p,rec2m); res[l].b=_mm_sub_pd(rec2m,rec2p);\n    }\n  }\n*/\n#endif\n\nstatic void Ylmgen_init (Ylmgen_C *gen, int l_max, int m_max, int s_max, int spinrec,\n  double epsilon)\n  {\n  int m;\n  const double inv_sqrt4pi = 0.2820947917738781434740397257803862929220;\n  const double inv_ln2 = 1.4426950408889634073599246810018921;\n\n  gen->fsmall = ldexp(1.,-large_exponent2);\n  gen->fbig   = ldexp(1., large_exponent2);\n  gen->eps = epsilon;\n  gen->cth_crit = 2.;\n  gen->ith = -1;\n  gen->nth = 0;\n  gen->lmax = l_max;\n  gen->mmax = m_max;\n  gen->smax = s_max;\n  gen->spinrec = spinrec;\n  gen->m_cur = -1;\n  gen->m_crit = gen->mmax+1;\n  gen->firstl = RALLOC(int,gen->smax+1);\n  for (m=0; m<=gen->smax; ++m) gen->firstl[m]=-1;\n  gen->cf = RALLOC(double,maxscale-minscale+1);\n  for (m=0; m<(maxscale-minscale+1); ++m)\n    gen->cf[m] = ldexp(1.,(m+minscale)*large_exponent2);\n  gen->recfac = RALLOC(ylmgen_dbl2,gen->lmax+1);\n  gen->mfac = RALLOC(double,gen->mmax+1);\n  gen->mfac[0] = 1;\n  for (m=1; m<=gen->mmax; ++m)\n    gen->mfac[m] = gen->mfac[m-1]*sqrt((2*m+1.)/(2*m));\n  for (m=0; m<=gen->mmax; ++m)\n    gen->mfac[m] = inv_ln2*log(inv_sqrt4pi*gen->mfac[m]);\n\n  gen->t1fac = RALLOC(double,gen->lmax+1);\n  for (m=0; m<=gen->lmax; ++m)\n    gen->t1fac[m] = sqrt(4.*(m+1)*(m+1)-1.);\n  gen->t2fac = RALLOC(double,gen->lmax+gen->mmax+1);\n  for (m=0; m<=gen->lmax+gen->mmax; ++m)\n    gen->t2fac[m] = 1./sqrt(m+1.);\n\n  gen->lamfact = RALLOC(double,gen->lmax+1);\n  gen->ylm = RALLOC(double,gen->lmax+1);\n  ALLOC(gen->lambda_wx,ylmgen_dbl2 *,gen->smax+1);\n  for (m=0; m<=gen->smax; ++m)\n    gen->lambda_wx[m]=NULL;\n\n  gen->sylm = RALLOC(sylmgen_d *,gen->smax+1);\n  for (m=0; m<=gen->smax; ++m)\n    gen->sylm[m]=NULL;\n\n  gen->ylm_uptodate = 0;\n  gen->lwx_uptodate = RALLOC(int,gen->smax+1);\n  SET_ARRAY(gen->lwx_uptodate,0,gen->smax+1,0);\n  gen->recfac_uptodate = 0;\n  gen->lamfact_uptodate = 0;\n\n  gen->th = gen->cth = gen->sth = gen->logsth = NULL;\n\n#ifdef __SSE2__\n  gen->ith1 = gen->ith2 = -1;\n  gen->ylm_sse2 = RALLOC(v2df,gen->lmax+1);\n  ALLOC(gen->lambda_wx_sse2,v2df2 *,gen->smax+1);\n  for (m=0; m<=gen->smax; ++m)\n    gen->lambda_wx_sse2[m]=NULL;\n  gen->ylm_uptodate_sse2 = 0;\n  gen->lwx_uptodate_sse2 = RALLOC(int,gen->smax+1);\n  SET_ARRAY(gen->lwx_uptodate_sse2,0,gen->smax+1,0);\n#endif\n\n  ALLOC(gen->logsum,long double,2*gen->lmax+1);\n  gen->lc05 = gen->ls05 = NULL;\n  ALLOC(gen->flm1,double,2*gen->lmax+1);\n  ALLOC(gen->flm2,double,2*gen->lmax+1);\n  ALLOC(gen->xl,double,gen->lmax+1);\n\n  gen->logsum[0] = 0.;\n  for (m=1; m<2*gen->lmax+1; ++m)\n    gen->logsum[m] = gen->logsum[m-1]+logl((long double)m);\n  for (m=0; m<2*gen->lmax+1; ++m)\n    {\n    gen->flm1[m] = sqrt(1./(m+1.));\n    gen->flm2[m] = sqrt(m/(m+1.));\n    }\n\n  gen->xl[0]=0;\n  for (m=1; m<gen->lmax+1; ++m) gen->xl[m]=1./m;\n  }\n\nstatic void Ylmgen_destroy (Ylmgen_C *gen)\n  {\n  int m;\n\n  DEALLOC(gen->firstl);\n  DEALLOC(gen->cf);\n  DEALLOC(gen->recfac);\n  DEALLOC(gen->mfac);\n  DEALLOC(gen->t1fac);\n  DEALLOC(gen->t2fac);\n  DEALLOC(gen->lamfact);\n  DEALLOC(gen->ylm);\n  DEALLOC(gen->lwx_uptodate);\n  for (m=0; m<=gen->smax; ++m)\n    DEALLOC(gen->lambda_wx[m]);\n  DEALLOC(gen->lambda_wx);\n  for (m=0; m<=gen->smax; ++m)\n    if (gen->sylm[m])\n      {\n      sylmgen_destroy (gen->sylm[m]);\n      DEALLOC(gen->sylm[m]);\n      }\n  DEALLOC(gen->sylm);\n  DEALLOC(gen->th);\n  DEALLOC(gen->cth);\n  DEALLOC(gen->sth);\n  DEALLOC(gen->logsth);\n  DEALLOC(gen->logsum);\n  DEALLOC(gen->lc05);\n  DEALLOC(gen->ls05);\n  DEALLOC(gen->flm1);\n  DEALLOC(gen->flm2);\n  DEALLOC(gen->xl);\n#ifdef __SSE2__\n  DEALLOC(gen->ylm_sse2);\n  for (m=0; m<=gen->smax; ++m)\n    DEALLOC(gen->lambda_wx_sse2[m]);\n  DEALLOC(gen->lambda_wx_sse2);\n  DEALLOC(gen->lwx_uptodate_sse2);\n#endif\n  }\n\nstatic void Ylmgen_set_theta (Ylmgen_C *gen, const double *theta, int nth)\n  {\n  const double inv_ln2 = 1.4426950408889634073599246810018921;\n  int m;\n  DEALLOC(gen->th);\n  DEALLOC(gen->cth);\n  DEALLOC(gen->sth);\n  DEALLOC(gen->logsth);\n  DEALLOC(gen->lc05);\n  DEALLOC(gen->ls05);\n  gen->th = RALLOC(double,nth);\n  gen->cth = RALLOC(double,nth);\n  gen->sth = RALLOC(double,nth);\n  gen->logsth = RALLOC(double,nth);\n  gen->lc05 = RALLOC(long double,nth);\n  gen->ls05 = RALLOC(long double,nth);\n  for (m=0; m<nth; ++m)\n    {\n    const double pi = 3.141592653589793238462643383279502884197;\n    double th=theta[m];\n    UTIL_ASSERT ((th>=0.)&&(th<=pi),\"bad theta angle specified\");\n    /* tiny adjustments to make sure cos and sin (theta/2) are positive */\n    if (th==0.) th=1e-16;\n    if (ABSAPPROX(th,pi,1e-15)) th=pi-1e-15;\n    gen->th[m] = th;\n    gen->cth[m] = cos(th);\n    gen->sth[m] = sin(th);\n    gen->logsth[m] = inv_ln2*log(gen->sth[m]);\n    gen->lc05[m]=logl(cosl(0.5L*th));\n    gen->ls05[m]=logl(sinl(0.5L*th));\n    }\n\n  gen->nth = nth;\n  gen->ith = -1;\n#ifdef __SSE2__\n  gen->ith1 = gen->ith2 = -1;\n#endif\n  }\n\nstatic void Ylmgen_prepare (Ylmgen_C *gen, int ith, int m)\n  {\n  if ((ith==gen->ith) && (m==gen->m_cur)) return;\n\n  gen->ylm_uptodate = 0;\n  SET_ARRAY(gen->lwx_uptodate,0,gen->smax+1,0);\n\n  gen->ith = ith;\n\n  if (m!=gen->m_cur)\n    {\n    gen->recfac_uptodate = 0;\n    gen->lamfact_uptodate = 0;\n    gen->m_cur = m;\n    }\n  }\n\nstatic void Ylmgen_recalc_recfac (Ylmgen_C *gen)\n  {\n  double f_old=1;\n  int l, m;\n\n  if (gen->recfac_uptodate) return;\n  gen->recfac_uptodate = 1;\n\n  m = gen->m_cur;\n  for (l=m; l<=gen->lmax; ++l)\n    {\n    gen->recfac[l][0] = gen->t1fac[l]*gen->t2fac[l+m]*gen->t2fac[l-m];\n    gen->recfac[l][1] = gen->recfac[l][0]/f_old;\n    f_old = gen->recfac[l][0];\n    }\n  }\n\n/*static void Ylmgen_recalc_lamfact (Ylmgen_C *gen)\n  {\n  int l, m;\n\n  if (gen->lamfact_uptodate) return;\n  gen->lamfact_uptodate = 1;\n\n  m = gen->m_cur;\n  gen->lamfact[m] = 0;\n  for (l=m+1; l<=gen->lmax; ++l)\n    gen->lamfact[l] = sqrt((2*l+1.)/(2*l-1.) * (l*l-m*m));\n    }*/\n\n#define RENORMALIZE_SCALAR \\\n  do \\\n    { \\\n    while (fabs(lam_2)>fbig) \\\n      { lam_1*=fsmall; lam_2*=fsmall; ++scale; } \\\n    corfac = (scale<0) ? 0. : gen->cf[scale]; \\\n    } \\\n  while(0)\n\nstatic void Ylmgen_recalc_Ylm (Ylmgen_C *gen)\n  {\n  const double ln2 = 0.6931471805599453094172321214581766;\n\n  double logval,lam_1,lam_2,corfac;\n  double eps=gen->eps, fbig=gen->fbig, fsmall=gen->fsmall;\n  ylmgen_dbl2 *recfac = gen->recfac;\n  int lmax=gen->lmax;\n  int scale,l;\n  int m = gen->m_cur;\n  double cth=gen->cth[gen->ith], sth=gen->sth[gen->ith];\n  double *result = gen->ylm;\n\n  if (gen->ylm_uptodate) return;\n  gen->ylm_uptodate=1;\n\n  if (((m>=gen->m_crit)&&(fabs(cth)>=gen->cth_crit)) || ((m>0)&&(sth==0)))\n    { gen->firstl[0]=gen->lmax+1; return; }\n\n  Ylmgen_recalc_recfac(gen);\n\n  logval = gen->mfac[m];\n  if (m>0) logval += m*gen->logsth[gen->ith];\n  scale = (int) (logval/large_exponent2)-minscale;\n  corfac = (scale<0) ? 0. : gen->cf[scale];\n\n  lam_1 = 0;\n  lam_2 = exp(ln2*(logval-(scale+minscale)*large_exponent2));\n  if (m&1) lam_2 = -lam_2;\n\n  l=m;\n  if (scale<0)\n    {\n    while (1)\n      {\n      if (++l>lmax) break;\n      lam_1 = cth*lam_2*recfac[l-1][0] - lam_1*recfac[l-1][1];\n      if (++l>lmax) break;\n      lam_2 = cth*lam_1*recfac[l-1][0] - lam_2*recfac[l-1][1];\n      if (fabs(lam_2)>fbig)\n        {\n        RENORMALIZE_SCALAR;\n        if (scale>=0) break;\n        }\n      }\n    }\n\n  lam_1*=corfac;\n  lam_2*=corfac;\n\n  if (l<=lmax)\n    {\n    while (1)\n      {\n      if (fabs(lam_2)>eps) break;\n      if (++l>lmax) break;\n      lam_1 = cth*lam_2*recfac[l-1][0] - lam_1*recfac[l-1][1];\n      if (fabs(lam_1)>eps)\n        { double x=lam_1; lam_1=lam_2; lam_2=x; break; }\n      if (++l>lmax) break;\n      lam_2 = cth*lam_1*recfac[l-1][0] - lam_2*recfac[l-1][1];\n      }\n    }\n\n  gen->firstl[0]=l;\n  if (l>lmax)\n    { gen->m_crit=m; gen->cth_crit=fabs(cth); return; }\n\n  for(;l<lmax-3;l+=4)\n    {\n    result[l]=lam_2;\n    lam_1 = cth*lam_2*recfac[l][0] - lam_1*recfac[l][1];\n    result[l+1] = lam_1;\n    lam_2 = cth*lam_1*recfac[l+1][0] - lam_2*recfac[l+1][1];\n    result[l+2]=lam_2;\n    lam_1 = cth*lam_2*recfac[l+2][0] - lam_1*recfac[l+2][1];\n    result[l+3] = lam_1;\n    lam_2 = cth*lam_1*recfac[l+3][0] - lam_2*recfac[l+3][1];\n    }\n\n  while (1)\n    {\n    result[l]=lam_2;\n    if (++l>lmax) break;\n    lam_1 = cth*lam_2*recfac[l-1][0] - lam_1*recfac[l-1][1];\n    result[l] = lam_1;\n    if (++l>lmax) break;\n    lam_2 = cth*lam_1*recfac[l-1][0] - lam_2*recfac[l-1][1];\n    }\n  }\n\n\n/*\nstatic void Ylmgen_recalc_lambda_wx1 (Ylmgen_C *gen)\n  {\n  if (gen->lwx_uptodate[1]) return;\n  Ylmgen_recalc_Ylm(gen);\n  gen->firstl[1] = gen->firstl[0];\n  if (gen->firstl[1]>gen->lmax) return;\n  Ylmgen_recalc_lamfact(gen);\n  gen->lwx_uptodate[1] = 1;\n\n  {\n  double cth=gen->cth[gen->ith];\n  double xsth=1./(gen->sth[gen->ith]);\n  double m=gen->m_cur;\n  double m_on_sth = m*xsth;\n  double lam_lm=0;\n  ylmgen_dbl2 *lambda_wx = gen->lambda_wx[1];\n  int l;\n  double ell;\n  for (ell=l=gen->firstl[1]; l<=gen->lmax; ++l, ell+=1.)\n    {\n    double lam_lm1m=lam_lm;\n    lam_lm=gen->ylm[l];\n    lambda_wx[l][0] = xsth*(gen->lamfact[l]*lam_lm1m - ell*cth*lam_lm);\n    lambda_wx[l][1] = m_on_sth*lam_lm;\n    }\n  }\n  }\n\nstatic void Ylmgen_recalc_lambda_wx2 (Ylmgen_C *gen)\n  {\n  if (gen->lwx_uptodate[2]) return;\n  Ylmgen_recalc_Ylm(gen);\n  gen->firstl[2] = gen->firstl[0];\n  if (gen->firstl[2]>gen->lmax) return;\n  Ylmgen_recalc_lamfact(gen);\n  gen->lwx_uptodate[2] = 1;\n\n  {\n  double cth=gen->cth[gen->ith];\n  double sth=gen->sth[gen->ith];\n  double m=gen->m_cur;\n  double one_on_s2 = 1./(sth*sth);\n  double two_on_s2 = 2*one_on_s2;\n  double two_c_on_s2 = cth * two_on_s2;\n  double m2 = m*m;\n  double two_m_on_s2 = m*two_on_s2;\n  double lam_lm=0;\n  ylmgen_dbl2 *lambda_wx = gen->lambda_wx[2];\n  int l;\n  double ell;\n  for (ell=l=gen->firstl[2]; l<=gen->lmax; ++l, ell+=1.)\n    {\n    double lam_lm1m=lam_lm;\n    lam_lm=gen->ylm[l];\n    {\n    const double t1  = lam_lm1m*gen->lamfact[l];\n    const double a_w = (m2-ell)*two_on_s2 - ell*(ell-1.);\n    const double a_x = cth*(ell-1.)*lam_lm;\n    lambda_wx[l][0] = a_w*lam_lm + t1*two_c_on_s2;\n    lambda_wx[l][1] = two_m_on_s2 * (t1-a_x);\n    }\n    }\n  }\n  }\n\nvoid Ylmgen_recalc_lambda_wx (Ylmgen_C *gen, int spin)\n  {\n  UTIL_ASSERT ((spin>0) && (spin<=gen->smax),\n    \"invalid spin in Ylmgen_recalc_lambda_wx\");\n\n  if (!gen->lambda_wx[spin])\n    gen->lambda_wx[spin]=RALLOC(ylmgen_dbl2,gen->lmax+1);\n\n  if (gen->spinrec && spin==1) { Ylmgen_recalc_lambda_wx1(gen); return; }\n  if (gen->spinrec && spin==2) { Ylmgen_recalc_lambda_wx2(gen); return; }\n\n  if (!gen->sylm[spin])\n    {\n    gen->sylm[spin]=RALLOC(sylmgen_d,1);\n    sylmgen_init(gen->sylm[spin],gen,spin);\n    }\n  if (gen->lwx_uptodate[spin]) return;\n  sylmgen_prepare(gen->sylm[spin],gen,gen->m_cur);\n  sylmgen_recalc(gen->sylm[spin],gen,gen->ith,gen->lambda_wx[spin],\n    &gen->firstl[spin]);\n  gen->lwx_uptodate[spin] = 1;\n  }\n*/\n#ifdef __SSE2__\n\nstatic void Ylmgen_prepare_sse2 (Ylmgen_C *gen, int ith1, int ith2, int m)\n  {\n  if ((ith1==gen->ith1) && (ith2==gen->ith2) && (m==gen->m_cur)) return;\n\n  gen->ylm_uptodate_sse2 = 0;\n  SET_ARRAY(gen->lwx_uptodate_sse2,0,gen->smax+1,0);\n\n  gen->ith1 = ith1; gen->ith2 = ith2;\n\n  if (m!=gen->m_cur)\n    {\n    gen->recfac_uptodate = gen->lamfact_uptodate = 0;\n    gen->m_cur = m;\n    }\n  }\n\n\n#define RENORMALIZE \\\n  do \\\n    { \\\n    double lam1a, lam1b, lam2a, lam2b, corfaca, corfacb; \\\n    read_v2df (lam_1, &lam1a, &lam1b); read_v2df (lam_2, &lam2a, &lam2b); \\\n    read_v2df (corfac, &corfaca, &corfacb); \\\n    while (fabs(lam2a)>fbig) \\\n      { \\\n      lam1a*=fsmall; lam2a*=fsmall; ++scale1; \\\n      corfaca = (scale1<0) ? 0. : gen->cf[scale1]; \\\n      } \\\n    while (fabs(lam2b)>fbig) \\\n      { \\\n      lam1b*=fsmall; lam2b*=fsmall; ++scale2; \\\n      corfacb = (scale2<0) ? 0. : gen->cf[scale2]; \\\n      } \\\n    lam_1=build_v2df(lam1a,lam1b); lam_2=build_v2df(lam2a,lam2b); \\\n    corfac=build_v2df(corfaca,corfacb); \\\n    } \\\n  while(0)\n#define GETPRE(prea,preb,lv) \\\n  { \\\n  prea=_mm_mul_pd(_mm_set1_pd(recfac[lv][0]),cth); \\\n  preb=_mm_set1_pd(recfac[lv][1]); \\\n  }\n#define NEXTSTEP(prea,preb,prec,pred,reca,recb,lv) \\\n  { \\\n  preb = _mm_mul_pd(preb,reca); \\\n  prea = _mm_mul_pd(prea,recb); \\\n  prec = _mm_set1_pd(recfac[lv][0]); \\\n  pred = _mm_set1_pd(recfac[lv][1]); \\\n  reca = _mm_sub_pd(prea,preb); \\\n  prec = _mm_mul_pd(cth,prec); \\\n  }\n\nstatic void Ylmgen_recalc_Ylm_sse2 (Ylmgen_C *gen)\n  {\n  const double ln2 = 0.6931471805599453094172321214581766;\n\n  v2df lam_1,lam_2,corfac;\n  double logval1,logval2;\n  double eps=gen->eps, fbig=gen->fbig, fsmall=gen->fsmall;\n  v2df eps2=build_v2df(eps,eps);\n  v2df fbig2=build_v2df(fbig,fbig);\n  ylmgen_dbl2 *recfac = gen->recfac;\n  int lmax=gen->lmax;\n  int scale1,scale2,l;\n  int m = gen->m_cur;\n  double cth1=gen->cth[gen->ith1], cth2=gen->cth[gen->ith2];\n  v2df cth=build_v2df(cth1,cth2);\n  v2df *result = gen->ylm_sse2;\n  v2df pre0,pre1,pre2,pre3;\n\n  if (gen->ylm_uptodate_sse2) return;\n  gen->ylm_uptodate_sse2=1;\n\n  if ((m>=gen->m_crit)&&(fabs(cth1)>=gen->cth_crit)&&(fabs(cth2)>=gen->cth_crit))\n    { gen->firstl[0]=gen->lmax+1; return; }\n\n  Ylmgen_recalc_recfac(gen);\n\n  logval1 = logval2 = gen->mfac[m];\n  if (m>0) logval1 += m*gen->logsth[gen->ith1];\n  if (m>0) logval2 += m*gen->logsth[gen->ith2];\n  scale1 = (int) (logval1/large_exponent2)-minscale;\n  scale2 = (int) (logval2/large_exponent2)-minscale;\n  corfac = build_v2df((scale1<0) ? 0. : gen->cf[scale1],\n                      (scale2<0) ? 0. : gen->cf[scale2]);\n\n  lam_1 = _mm_setzero_pd();\n  lam_2 = build_v2df(exp(ln2*(logval1-(scale1+minscale)*large_exponent2)),\n                     exp(ln2*(logval2-(scale2+minscale)*large_exponent2)));\n  if (m&1) lam_2 = _mm_xor_pd (lam_2,V2DF_SIGNMASK); /* negate */\n\n  l=m;\n  if ((scale1<0) && (scale2<0))\n    {\n    GETPRE(pre0,pre1,l)\n    while (1)\n      {\n      if (++l>lmax) break;\n      NEXTSTEP(pre0,pre1,pre2,pre3,lam_1,lam_2,l)\n      if (++l>lmax) break;\n      NEXTSTEP(pre2,pre3,pre0,pre1,lam_2,lam_1,l)\n      if (v2df_any_gt(lam_2,fbig2))\n        {\n        RENORMALIZE;\n        if ((scale1>=0) || (scale2>=0)) break;\n        }\n      }\n    }\n\n  if (l<=lmax)\n    {\n    GETPRE(pre0,pre1,l)\n    while (1)\n      {\n      v2df t1;\n      result[l]=t1=_mm_mul_pd(lam_2,corfac);\n      if (v2df_any_gt(t1,eps2))\n        break;\n      if (++l>lmax) break;\n      NEXTSTEP(pre0,pre1,pre2,pre3,lam_1,lam_2,l)\n\n      result[l]=t1=_mm_mul_pd(lam_1,corfac);\n      if (v2df_any_gt(t1,eps2))\n        { v2df tmp=lam_1;lam_1=lam_2;lam_2=tmp; break; }\n      if (++l>lmax) break;\n      NEXTSTEP(pre2,pre3,pre0,pre1,lam_2,lam_1,l)\n\n      if (v2df_any_gt(lam_2,fbig2))\n        RENORMALIZE;\n      }\n    }\n\n  gen->firstl[0]=l;\n  if (l>lmax)\n    {\n    gen->m_crit=m;\n    gen->cth_crit= (fabs(cth1)<fabs(cth2)) ? fabs(cth1) : fabs(cth2);\n    return;\n    }\n\n  GETPRE(pre0,pre1,l)\n  while (1)\n    {\n    v2df t1;\n    result[l]=t1=_mm_mul_pd(lam_2,corfac);\n    if (v2df_all_ge(t1,eps2))\n      break;\n    if (++l>lmax) return;\n    NEXTSTEP(pre0,pre1,pre2,pre3,lam_1,lam_2,l)\n\n    result[l]=t1=_mm_mul_pd(lam_1,corfac);\n    if (v2df_all_ge(t1,eps2))\n      { v2df tmp=lam_1;lam_1=lam_2;lam_2=tmp; break; }\n    if (++l>lmax) return;\n    NEXTSTEP(pre2,pre3,pre0,pre1,lam_2,lam_1,l)\n\n    if (v2df_any_gt(lam_2,fbig2))\n      RENORMALIZE;\n    }\n\n  lam_1 = _mm_mul_pd (lam_1,corfac);\n  lam_2 = _mm_mul_pd (lam_2,corfac);\n\n  GETPRE(pre0,pre1,l)\n  for(;l<lmax-2;l+=2)\n    {\n    result[l]=lam_2;\n    NEXTSTEP(pre0,pre1,pre2,pre3,lam_1,lam_2,l+1)\n    result[l+1]=lam_1;\n    NEXTSTEP(pre2,pre3,pre0,pre1,lam_2,lam_1,l+2)\n    }\n\n  while (1)\n    {\n    result[l]=lam_2;\n    if (++l>lmax) break;\n    NEXTSTEP(pre0,pre1,pre2,pre3,lam_1,lam_2,l)\n    result[l] = lam_1;\n    if (++l>lmax) break;\n    NEXTSTEP(pre2,pre3,pre0,pre1,lam_2,lam_1,l)\n    }\n  }\n\n/*static void Ylmgen_recalc_lambda_wx1_sse2 (Ylmgen_C *gen)\n  {\n  if (gen->lwx_uptodate_sse2[1]) return;\n  Ylmgen_recalc_Ylm_sse2(gen);\n  gen->firstl[1] = gen->firstl[0];\n  if (gen->firstl[1]>gen->lmax) return;\n  Ylmgen_recalc_lamfact(gen);\n  gen->lwx_uptodate_sse2[1] = 1;\n\n  {\n  v2df cth=build_v2df(gen->cth[gen->ith1],gen->cth[gen->ith2]);\n  v2df xsth=build_v2df(1./gen->sth[gen->ith1],1./gen->sth[gen->ith2]);\n  v2df m=build_v2df(gen->m_cur,gen->m_cur);\n  v2df m_on_sth = _mm_mul_pd(m,xsth);\n  v2df lam_lm=_mm_setzero_pd();\n  v2df2 *lambda_wx = gen->lambda_wx_sse2[1];\n  int l;\n  v2df ell=build_v2df(gen->firstl[1],gen->firstl[1]);\n  v2df uno=_mm_set1_pd(1.);\n  for (l=gen->firstl[1]; l<=gen->lmax; ++l, ell=_mm_add_pd(ell,uno))\n    {\n    v2df lamfact=_mm_load1_pd(&gen->lamfact[l]);\n    v2df lam_lm1m=lam_lm;\n    lam_lm=gen->ylm_sse2[l];\n    lambda_wx[l].a = _mm_mul_pd(xsth,_mm_sub_pd(_mm_mul_pd(lamfact,lam_lm1m),\n                     _mm_mul_pd(_mm_mul_pd(ell,cth),lam_lm)));\n    lambda_wx[l].b = _mm_mul_pd(m_on_sth,lam_lm);\n    }\n  }\n  }\n\nstatic void Ylmgen_recalc_lambda_wx2_sse2 (Ylmgen_C *gen)\n  {\n  if (gen->lwx_uptodate_sse2[2]) return;\n  Ylmgen_recalc_Ylm_sse2(gen);\n  gen->firstl[2] = gen->firstl[0];\n  if (gen->firstl[2]>gen->lmax) return;\n  Ylmgen_recalc_lamfact(gen);\n  gen->lwx_uptodate_sse2[2] = 1;\n\n  {\n  v2df cth=build_v2df(gen->cth[gen->ith1],gen->cth[gen->ith2]);\n  v2df sth=build_v2df(gen->sth[gen->ith1],gen->sth[gen->ith2]);\n  v2df m=build_v2df(gen->m_cur,gen->m_cur);\n  v2df uno=_mm_set1_pd(1.);\n  v2df one_on_s2 = _mm_div_pd(uno,_mm_mul_pd(sth,sth));\n  v2df two_on_s2 = _mm_mul_pd(_mm_set1_pd(2.),one_on_s2);\n  v2df two_c_on_s2 = _mm_mul_pd(cth,two_on_s2);\n  v2df m2 = _mm_mul_pd(m,m);\n  v2df two_m_on_s2 = _mm_mul_pd(m,two_on_s2);\n  v2df lam_lm=_mm_setzero_pd();\n  v2df2 *lambda_wx = gen->lambda_wx_sse2[2];\n  int l;\n  v2df ell=build_v2df(gen->firstl[2],gen->firstl[2]);\n  for (l=gen->firstl[2]; l<=gen->lmax; ++l, ell=_mm_add_pd(ell,uno))\n    {\n    v2df lamfact=_mm_load1_pd(&gen->lamfact[l]);\n    v2df lam_lm1m=lam_lm;\n    lam_lm=gen->ylm_sse2[l];\n    {\n    const v2df t1  = _mm_mul_pd(lam_lm1m,lamfact);\n    const v2df ellm1 = _mm_sub_pd(ell,uno);\n    const v2df a_w = _mm_sub_pd\n      (_mm_mul_pd(_mm_sub_pd(m2,ell),two_on_s2),_mm_mul_pd(ell,ellm1));\n    const v2df a_x = _mm_mul_pd(_mm_mul_pd(cth,ellm1),lam_lm);\n    lambda_wx[l].a =\n      _mm_add_pd(_mm_mul_pd(a_w,lam_lm),_mm_mul_pd(t1,two_c_on_s2));\n    lambda_wx[l].b = _mm_mul_pd(two_m_on_s2,_mm_sub_pd(t1,a_x));\n    }\n    }\n  }\n  }*/\n\n/*static void Ylmgen_recalc_lambda_wx_sse2 (Ylmgen_C *gen, int spin)\n  {\n  UTIL_ASSERT ((spin>0) && (spin<=gen->smax),\n    \"invalid spin in Ylmgen_recalc_lambda_wx_sse2\");\n\n  if (!gen->lambda_wx_sse2[spin])\n    gen->lambda_wx_sse2[spin]=RALLOC(v2df2,gen->lmax+1);\n\n  if (gen->spinrec && spin==1) { Ylmgen_recalc_lambda_wx1_sse2(gen); return; }\n  if (gen->spinrec && spin==2) { Ylmgen_recalc_lambda_wx2_sse2(gen); return; }\n\n  if (!gen->sylm[spin])\n    {\n    gen->sylm[spin]=RALLOC(sylmgen_d,1);\n    sylmgen_init(gen->sylm[spin],gen,spin);\n    }\n  if (gen->lwx_uptodate_sse2[spin]) return;\n  sylmgen_prepare(gen->sylm[spin],gen,gen->m_cur);\n  sylmgen_recalc_sse2(gen->sylm[spin],gen,gen->ith1,gen->ith2,\n    gen->lambda_wx_sse2[spin],&gen->firstl[spin]);\n  gen->lwx_uptodate_sse2[spin] = 1;\n  }*/\n\n#endif /* __SSE2__ */\n\n/*\ndouble *Ylmgen_get_norm (int lmax, int spin, int spinrec)\n  {\n  const double pi = 3.141592653589793238462643383279502884197;\n  double *res=RALLOC(double,lmax+1);\n  int l;\n  double spinsign;\n  / sign convention for H=1 (LensPix paper) /\n#if 1\n  spinsign = (spin>0) ? -1.0 : 1.0;\n#else\n  spinsign = 1.0;\n#endif\n\n  if (spin==0)\n    {\n    for (l=0; l<=lmax; ++l)\n      res[l]=1.;\n    return res;\n    }\n\n  if ((!spinrec) || (spin>=3))\n    {\n    spinsign = (spin&1) ? -spinsign : spinsign;\n    for (l=0; l<=lmax; ++l)\n      res[l] = (l<spin) ? 0. : spinsign*0.5*sqrt((2*l+1)/(4*pi));\n    return res;\n    }\n\n  if (spin==1)\n    {\n    for (l=0; l<=lmax; ++l)\n      res[l] = (l<spin) ? 0. : spinsign*sqrt(1./((l+1.)*l));\n    return res;\n    }\n\n  if (spin==2)\n    {\n    for (l=0; l<=lmax; ++l)\n      res[l] = (l<spin) ? 0. : spinsign*sqrt(1./((l+2.)*(l+1.)*l*(l-1.)));\n    return res;\n    }\n\n  UTIL_FAIL (\"error in Ylmgen_get_norm\");\n  return NULL;\n  }\n*/\n\n\n\n/*\nNew high-level wrapper\n*/\n#include \"sharp_legendre_table.h\"\n#include <stdio.h>\n\nvoid sharp_normalized_associated_legendre_table(\n  ptrdiff_t m,\n  int spin,\n  ptrdiff_t lmax,\n  ptrdiff_t ntheta,\n  double *theta,\n  ptrdiff_t theta_stride,\n  ptrdiff_t l_stride,\n  ptrdiff_t spin_stride,\n  double *out\n) {\n    if (spin != 0) UTIL_FAIL (\"sharp_normalized_associated_legendre_table: only spin=0 has been implemented so far\");\n\n    Ylmgen_C ctx;\n    ptrdiff_t itheta, l, lmin;\n\n    Ylmgen_init(&ctx, lmax, lmax, 0, 0, 1e-300);\n    Ylmgen_set_theta(&ctx, theta, ntheta);\n\n    itheta = 0;\n    #ifdef __SSE2__\n    for (; itheta < ntheta - 1; itheta += 2) {\n        Ylmgen_prepare_sse2(&ctx, itheta, itheta + 1, m);\n        Ylmgen_recalc_Ylm_sse2(&ctx);\n        lmin = IMIN(*ctx.firstl, lmax + 1);\n        for (l = m; l < lmin; ++l) {\n            out[itheta * theta_stride + (l - m) * l_stride + spin * spin_stride] = 0;\n            out[(itheta + 1) * theta_stride + (l - m) * l_stride + spin * spin_stride] = 0;\n        }\n        for (l = IMAX(lmin, m); l <= lmax; ++l) {\n            double v1, v2;\n            read_v2df(ctx.ylm_sse2[l], &v1, &v2);\n            out[itheta * theta_stride + (l - m) * l_stride + spin * spin_stride] = v1;\n            out[(itheta + 1) * theta_stride + (l - m) * l_stride + spin * spin_stride] = v2;\n        }\n    }\n    #endif\n    for (; itheta < ntheta; itheta += 1) {\n        Ylmgen_prepare(&ctx, itheta, m);\n        Ylmgen_recalc_Ylm(&ctx);\n        lmin = IMIN(*ctx.firstl, lmax + 1);\n        for (l = m; l < lmin; ++l) {\n            out[itheta * theta_stride + (l - m) * l_stride + spin * spin_stride] = 0;\n        }\n        for (l = IMAX(lmin, m); l <= lmax; ++l) {\n            out[itheta * theta_stride + (l - m) * l_stride + spin * spin_stride] = ctx.ylm[l];\n        }\n    }\n    Ylmgen_destroy(&ctx);\n}\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_legendre_table.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n * Redistribution and use in source and binary forms, with or without\n * met:\n * \n * 1. Redistributions of source code must retain the above copyright\n * notice, this list of conditions and the following disclaimer.\n * \n * 2. Redistributions in binary form must reproduce the above copyright\n * notice, this list of conditions and the following disclaimer in the\n * documentation and/or other materials provided with the distribution.\n * \n * 3. Neither the name of the copyright holder nor the names of its\n * contributors may be used to endorse or promote products derived from\n * this software without specific prior written permission.\n * \n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/*! \\file sharp_legendre_table.h\n *  Interface for computing tables of the normalized associated Legendre transform\n *\n *  Copyright (C) 2017 Dag Sverre Seljebotn\n *  \\author Dag Sverre Seljebotn\n *\n *  Note: This code was mainly copied from libpsht; only a small high-level wrapper added\n */\n\n#ifndef SHARP_LEGENDRE_TABLE_H\n#define SHARP_LEGENDRE_TABLE_H\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef NO_LEGENDRE_TABLE\n\n\n/*! Returns a table of the normalized associated Legendre polynomials. m is a single\n    fixed argument and a table for multiple l and cos(theta) is provided.\n    (Internally, sin(theta) is also used for part of the computation, making theta\n    the most convenient argument.)\n\n    NOTE: Support for spin-weighted Legendre functions is on the TODO-list. Only spin=0\n    is supported now.\n\n    \\param m The m-value to compute a table for; must be >= 0\n    \\param spin The spin parameter; pass 0 for the regular associated Legendre functions.\n                NOTE: This is present for future compatability, currently only 0 is supported.\n    \\param lmax A table will be provided for l = m .. lmax\n    \\param ntheta How many theta values to evaluate for\n    \\param theta Contiguous 1D array of theta values\n    \\param theta_stride See below\n    \\param l_stride See below\n    \\param spin_stride See below. \"ispin\" will always be 0 if spin==0, or 0 for positive spin\n                       and 1 for the corresponding negative spin otherwise.\n    \\param out Contiguous 3D array that will receive the output. Each output entry\n               is assigned to out[itheta * theta_stride + (l - m) * l_stride + ispin * spin_stride].\n */\nvoid sharp_normalized_associated_legendre_table(\n  ptrdiff_t m,\n  int spin,\n  ptrdiff_t lmax,\n  ptrdiff_t ntheta,\n  /* contiguous 1D array of theta values to compute for,\n     contains ntheta values */\n  double *theta,\n  /* contiguous 2D array, in \"theta-major ordering\". Has `ntheta`\n     rows and `ncols` columns. Indexed as out[itheta * ncols + (l - m)].\n     If `ncols > lmax - m` then those entries are not accessed.\n  */\n  ptrdiff_t theta_stride,\n  ptrdiff_t l_stride,\n  ptrdiff_t spin_stride,\n  double *out\n);\n\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_lowlevel.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_lowlevel.h\n *  Low-level, portable interface for the spherical transform library.\n *\n *  Copyright (C) 2012-2013 Max-Planck-Society\n *  \\author Martin Reinecke \\author Dag Sverre Seljebotn\n */\n\n#ifndef PLANCK_SHARP_LOWLEVEL_H\n#define PLANCK_SHARP_LOWLEVEL_H\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*! \\internal\n    Helper type containing information about a single ring. */\ntypedef struct\n  {\n  double theta, phi0, weight, cth, sth;\n  ptrdiff_t ofs;\n  int nph, stride;\n  } sharp_ringinfo;\n\n/*! \\internal\n    Helper type containing information about a pair of rings with colatitudes\n    symmetric around the equator. */\ntypedef struct\n  {\n  sharp_ringinfo r1,r2;\n  } sharp_ringpair;\n\n/*! \\internal\n    Type holding all required information about a map geometry. */\ntypedef struct\n  {\n  sharp_ringpair *pair;\n  int npairs, nphmax;\n  } sharp_geom_info;\n\n/*! \\defgroup almgroup Helpers for dealing with a_lm */\n/*! \\{ */\n\n/*! \\internal\n    Helper type for index calculation in a_lm arrays. */\ntypedef struct\n  {\n  /*! Maximum \\a l index of the array */\n  int lmax;\n  /*! Number of different \\a m values in this object */\n  int nm;\n  /*! Array with \\a nm entries containing the individual m values */\n  int *mval;\n  /*! Combination of flags from sharp_almflags */\n  int flags;\n  /*! Array with \\a nm entries containing the (hypothetical) indices of\n      the coefficients with quantum numbers 0,\\a mval[i] */\n  ptrdiff_t *mvstart;\n  /*! Stride between a_lm and a_(l+1),m */\n  ptrdiff_t stride;\n  } sharp_alm_info;\n\n/*! alm_info flags */\ntypedef enum { SHARP_PACKED = 1,\n               /*!< m=0-coefficients are packed so that the (zero) imaginary part is\n                    not present. mvstart is in units of *real* float/double for all\n                    m; stride is in units of reals for m=0 and complex for m!=0 */\n               SHARP_REAL_HARMONICS  = 1<<6\n               /*!< Use the real spherical harmonic convention. For\n                    m==0, the alm are treated exactly the same as in\n                    the complex case.  For m!=0, alm[i] represent a\n                    pair (+abs(m), -abs(m)) instead of (real, imag),\n                    and the coefficients are scaled by a factor of\n                    sqrt(2) relative to the complex case.  In other\n                    words, (sqrt(.5) * alm[i]) recovers the\n                    corresponding complex coefficient (when accessed\n                    as complex).\n                */\n             } sharp_almflags;\n\n\n\n/*! Creates an a_lm data structure from the following parameters:\n    \\param lmax maximum \\a l quantum number (>=0)\n    \\param mmax maximum \\a m quantum number (0<= \\a mmax <= \\a lmax)\n    \\param stride the stride between entries with identical \\a m, and \\a l\n      differing by 1.\n    \\param mstart the index of the (hypothetical) coefficient with the\n      quantum numbers 0,\\a m. Must have \\a mmax+1 entries.\n    \\param alm_info will hold a pointer to the newly created data structure\n */\nvoid sharp_make_alm_info (int lmax, int mmax, int stride,\n  const ptrdiff_t *mstart, sharp_alm_info **alm_info);\n/*! Creates an a_lm data structure which from the following parameters:\n    \\param lmax maximum \\a l quantum number (\\a >=0)\n    \\param nm number of different \\a m (\\a 0<=nm<=lmax+1)\n    \\param stride the stride between entries with identical \\a m, and \\a l\n      differing by 1.\n    \\param mval array with \\a nm entries containing the individual m values\n    \\param mvstart array with \\a nm entries containing the (hypothetical)\n      indices of the coefficients with the quantum numbers 0,\\a mval[i]\n    \\param flags a combination of sharp_almflags (pass 0 unless you know you need this)\n    \\param alm_info will hold a pointer to the newly created data structure\n */\nvoid sharp_make_general_alm_info (int lmax, int nm, int stride, const int *mval,\n  const ptrdiff_t *mvstart, int flags, sharp_alm_info **alm_info);\n/*! Returns the index of the coefficient with quantum numbers \\a l,\n    \\a mval[mi].\n    \\note for a \\a sharp_alm_info generated by sharp_make_alm_info() this is\n    the index for the coefficient with the quantum numbers \\a l, \\a mi. */\nptrdiff_t sharp_alm_index (const sharp_alm_info *self, int l, int mi);\n/*! Returns the number of alm coefficients described by \\a self. If the SHARP_PACKED\n    flag is set, this is number of \"real\" coeffecients (for m < 0 and m >= 0),\n    otherwise it is the number of complex coefficients (with m>=0). */\nptrdiff_t sharp_alm_count(const sharp_alm_info *self);\n/*! Deallocates the a_lm info object. */\nvoid sharp_destroy_alm_info (sharp_alm_info *info);\n\n/*! \\} */\n\n/*! \\defgroup geominfogroup Functions for dealing with geometry information */\n/*! \\{ */\n\n/*! Creates a geometry information from a set of ring descriptions.\n    All arrays passed to this function must have \\a nrings elements.\n    \\param nrings the number of rings in the map\n    \\param nph the number of pixels in each ring\n    \\param ofs the index of the first pixel in each ring in the map array\n    \\param stride the stride between consecutive pixels\n    \\param phi0 the azimuth (in radians) of the first pixel in each ring\n    \\param theta the colatitude (in radians) of each ring\n    \\param wgt the pixel weight to be used for the ring in map2alm\n      and adjoint map2alm transforms.\n      Pass NULL to use 1.0 as weight for all rings.\n    \\param geom_info will hold a pointer to the newly created data structure\n */\nvoid sharp_make_geom_info (int nrings, const int *nph, const ptrdiff_t *ofs,\n  const int *stride, const double *phi0, const double *theta,\n  const double *wgt, sharp_geom_info **geom_info);\n\n/*! Counts the number of grid points needed for (the local part of) a map described\n    by \\a info.\n */\nptrdiff_t sharp_map_size(const sharp_geom_info *info);\n\n/*! Deallocates the geometry information in \\a info. */\nvoid sharp_destroy_geom_info (sharp_geom_info *info);\n\n/*! \\} */\n\n/*! \\defgroup lowlevelgroup Low-level libsharp SHT interface */\n/*! \\{ */\n\n/*! Enumeration of SHARP job types. */\ntypedef enum { SHARP_YtW=0,               /*!< analysis */\n               SHARP_MAP2ALM=SHARP_YtW,   /*!< analysis */\n               SHARP_Y=1,                 /*!< synthesis */\n               SHARP_ALM2MAP=SHARP_Y,     /*!< synthesis */\n               SHARP_Yt=2,                /*!< adjoint synthesis */\n               SHARP_WY=3,                /*!< adjoint analysis */\n               SHARP_ALM2MAP_DERIV1=4     /*!< synthesis of first derivatives */\n             } sharp_jobtype;\n\n/*! Job flags */\ntypedef enum { SHARP_DP              = 1<<4,\n               /*!< map and a_lm are in double precision */\n               SHARP_ADD             = 1<<5,\n               /*!< results are added to the output arrays, instead of\n                    overwriting them */\n\n               /* NOTE: SHARP_REAL_HARMONICS, 1<<6, is also available in sharp_jobflags,\n                  but its use here is deprecated in favor of having it in the sharp_alm_info */\n\n               SHARP_NO_FFT          = 1<<7,\n\n               SHARP_USE_WEIGHTS     = 1<<20,    /* internal use only */\n               SHARP_NO_OPENMP       = 1<<21,    /* internal use only */\n               SHARP_NVMAX           = (1<<4)-1 /* internal use only */\n             } sharp_jobflags;\n\n/*! Performs a libsharp SHT job. The interface deliberately does not use\n  the C99 \"complex\" data type, in order to be callable from C89 and C++.\n  \\param type the type of SHT\n  \\param spin the spin of the quantities to be transformed\n  \\param alm contains pointers to the a_lm coefficients. If \\a spin==0,\n    alm[0] points to the a_lm of the first SHT, alm[1] to those of the second\n    etc. If \\a spin>0, alm[0] and alm[1] point to the a_lm of the first SHT,\n    alm[2] and alm[3] to those of the second, etc. The exact data type of \\a alm\n    depends on whether the SHARP_DP flag is set.\n  \\param map contains pointers to the maps. If \\a spin==0,\n    map[0] points to the map of the first SHT, map[1] to that of the second\n    etc. If \\a spin>0, or \\a type is SHARP_ALM2MAP_DERIV1, map[0] and map[1]\n    point to the maps of the first SHT, map[2] and map[3] to those of the\n    second, etc. The exact data type of \\a map depends on whether the SHARP_DP\n    flag is set.\n  \\param geom_info A \\c sharp_geom_info object compatible with the provided\n    \\a map arrays.\n  \\param alm_info A \\c sharp_alm_info object compatible with the provided\n    \\a alm arrays. All \\c m values from 0 to some \\c mmax<=lmax must be present\n    exactly once.\n  \\param ntrans the number of simultaneous SHTs\n  \\param flags See sharp_jobflags. In particular, if SHARP_DP is set, then\n    \\a alm is expected to have the type \"complex double **\" and \\a map is\n    expected to have the type \"double **\"; otherwise, the expected\n    types are \"complex float **\" and \"float **\", respectively.\n  \\param time If not NULL, the wall clock time required for this SHT\n    (in seconds) will be written here.\n  \\param opcnt If not NULL, a conservative estimate of the total floating point\n    operation count for this SHT will be written here. */\nvoid sharp_execute (sharp_jobtype type, int spin, void *alm, void *map,\n  const sharp_geom_info *geom_info, const sharp_alm_info *alm_info, int ntrans,\n  int flags, double *time, unsigned long long *opcnt);\n\nvoid sharp_set_chunksize_min(int new_chunksize_min);\nvoid sharp_set_nchunks_max(int new_nchunks_max);\n\n\ntypedef enum { SHARP_ERROR_NO_MPI = 1,\n               /*!< libsharp not compiled with MPI support */\n              } sharp_errors;\n\n/*! Works like sharp_execute_mpi, but is always present whether or not libsharp\n    is compiled with USE_MPI. This is primarily useful for wrapper code etc.\n\n    Note that \\a pcomm has the type MPI_Comm*, except we declare void* to avoid\n    pulling in MPI headers. I.e., the comm argument of sharp_execute_mpi\n    is *(MPI_Comm*)pcomm.\n\n    Other parameters are the same as sharp_execute_mpi.\n\n    Returns 0 if successful, or SHARP_ERROR_NO_MPI if MPI is not available\n    (in which case nothing is done).\n */\nint sharp_execute_mpi_maybe (void *pcomm, sharp_jobtype type, int spin,\n  void *alm, void *map, const sharp_geom_info *geom_info,\n  const sharp_alm_info *alm_info, int ntrans, int flags, double *time,\n  unsigned long long *opcnt);\n\n\n\n/*! \\} */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_mpi.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_mpi.c\n *  Functionality only needed for MPI-parallel transforms\n *\n *  Copyright (C) 2012-2013 Max-Planck-Society\n *  \\author Martin Reinecke \\author Dag Sverre Seljebotn\n */\n\n#ifdef USE_MPI\n\n#include \"sharp_mpi.h\"\n\ntypedef struct\n  {\n  int ntasks;     /* number of tasks */\n  int mytask;     /* own task number */\n  MPI_Comm comm;  /* communicator to use */\n\n  int *nm;        /* number of m values on every task */\n  int *ofs_m;     /* accumulated nm */\n  int nmtotal;    /* total number of m values (must be mmax+1) */\n  int *mval;      /* array containing all m values of task 0, task 1 etc. */\n  int mmax;\n  int nph;\n\n  int *npair;     /* number of ring pairs on every task */\n  int *ofs_pair;  /* accumulated npair */\n  int npairtotal; /* total number of ring pairs */\n\n  double *theta;  /* theta of first ring of every pair on task 0, task 1 etc. */\n  int *ispair;    /* is this really a pair? */\n\n  int *almcount, *almdisp, *mapcount, *mapdisp; /* for all2all communication */\n  } sharp_mpi_info;\n\nstatic void sharp_make_mpi_info (MPI_Comm comm, const sharp_job *job,\n  sharp_mpi_info *minfo)\n  {\n  minfo->comm = comm;\n  MPI_Comm_size (comm, &minfo->ntasks);\n  MPI_Comm_rank (comm, &minfo->mytask);\n\n  minfo->nm=RALLOC(int,minfo->ntasks);\n  MPI_Allgather ((int *)(&job->ainfo->nm),1,MPI_INT,minfo->nm,1,MPI_INT,comm);\n  minfo->ofs_m=RALLOC(int,minfo->ntasks+1);\n  minfo->ofs_m[0]=0;\n  for (int i=1; i<=minfo->ntasks; ++i)\n    minfo->ofs_m[i] = minfo->ofs_m[i-1]+minfo->nm[i-1];\n  minfo->nmtotal=minfo->ofs_m[minfo->ntasks];\n  minfo->mval=RALLOC(int,minfo->nmtotal);\n  MPI_Allgatherv(job->ainfo->mval, job->ainfo->nm, MPI_INT, minfo->mval,\n    minfo->nm, minfo->ofs_m, MPI_INT, comm);\n\n  minfo->mmax=sharp_get_mmax(minfo->mval,minfo->nmtotal);\n\n  minfo->npair=RALLOC(int,minfo->ntasks);\n  MPI_Allgather ((int *)(&job->ginfo->npairs), 1, MPI_INT, minfo->npair, 1,\n    MPI_INT, comm);\n  minfo->ofs_pair=RALLOC(int,minfo->ntasks+1);\n  minfo->ofs_pair[0]=0;\n  for (int i=1; i<=minfo->ntasks; ++i)\n    minfo->ofs_pair[i] = minfo->ofs_pair[i-1]+minfo->npair[i-1];\n  minfo->npairtotal=minfo->ofs_pair[minfo->ntasks];\n\n  double *theta_tmp=RALLOC(double,job->ginfo->npairs);\n  int *ispair_tmp=RALLOC(int,job->ginfo->npairs);\n  for (int i=0; i<job->ginfo->npairs; ++i)\n    {\n    theta_tmp[i]=job->ginfo->pair[i].r1.theta;\n    ispair_tmp[i]=job->ginfo->pair[i].r2.nph>0;\n    }\n  minfo->theta=RALLOC(double,minfo->npairtotal);\n  minfo->ispair=RALLOC(int,minfo->npairtotal);\n  MPI_Allgatherv(theta_tmp, job->ginfo->npairs, MPI_DOUBLE, minfo->theta,\n    minfo->npair, minfo->ofs_pair, MPI_DOUBLE, comm);\n  MPI_Allgatherv(ispair_tmp, job->ginfo->npairs, MPI_INT, minfo->ispair,\n    minfo->npair, minfo->ofs_pair, MPI_INT, comm);\n  DEALLOC(theta_tmp);\n  DEALLOC(ispair_tmp);\n\n  minfo->nph=2*job->nmaps*job->ntrans;\n\n  minfo->almcount=RALLOC(int,minfo->ntasks);\n  minfo->almdisp=RALLOC(int,minfo->ntasks+1);\n  minfo->mapcount=RALLOC(int,minfo->ntasks);\n  minfo->mapdisp=RALLOC(int,minfo->ntasks+1);\n  minfo->almdisp[0]=minfo->mapdisp[0]=0;\n  for (int i=0; i<minfo->ntasks; ++i)\n    {\n    minfo->almcount[i] = 2*minfo->nph*minfo->nm[minfo->mytask]*minfo->npair[i];\n    minfo->almdisp[i+1] = minfo->almdisp[i]+minfo->almcount[i];\n    minfo->mapcount[i] = 2*minfo->nph*minfo->nm[i]*minfo->npair[minfo->mytask];\n    minfo->mapdisp[i+1] = minfo->mapdisp[i]+minfo->mapcount[i];\n    }\n  }\n\nstatic void sharp_destroy_mpi_info (sharp_mpi_info *minfo)\n  {\n  DEALLOC(minfo->nm);\n  DEALLOC(minfo->ofs_m);\n  DEALLOC(minfo->mval);\n  DEALLOC(minfo->npair);\n  DEALLOC(minfo->ofs_pair);\n  DEALLOC(minfo->theta);\n  DEALLOC(minfo->ispair);\n  DEALLOC(minfo->almcount);\n  DEALLOC(minfo->almdisp);\n  DEALLOC(minfo->mapcount);\n  DEALLOC(minfo->mapdisp);\n  }\n\nstatic void sharp_communicate_alm2map (const sharp_mpi_info *minfo, dcmplx **ph)\n  {\n  dcmplx *phas_tmp = RALLOC(dcmplx,minfo->mapdisp[minfo->ntasks]/2);\n\n  MPI_Alltoallv (*ph,minfo->almcount,minfo->almdisp,MPI_DOUBLE,phas_tmp,\n    minfo->mapcount,minfo->mapdisp,MPI_DOUBLE,minfo->comm);\n\n  DEALLOC(*ph);\n  ALLOC(*ph,dcmplx,minfo->nph*minfo->npair[minfo->mytask]*minfo->nmtotal);\n\n  for (int task=0; task<minfo->ntasks; ++task)\n    for (int th=0; th<minfo->npair[minfo->mytask]; ++th)\n      for (int mi=0; mi<minfo->nm[task]; ++mi)\n        {\n        int m = minfo->mval[mi+minfo->ofs_m[task]];\n        int o1 = minfo->nph*(th*(minfo->mmax+1) + m);\n        int o2 = minfo->mapdisp[task]/2+minfo->nph*(mi+th*minfo->nm[task]);\n        for (int i=0; i<minfo->nph; ++i)\n          (*ph)[o1+i] = phas_tmp[o2+i];\n        }\n  DEALLOC(phas_tmp);\n  }\n\nstatic void sharp_communicate_map2alm (const sharp_mpi_info *minfo, dcmplx **ph)\n  {\n  dcmplx *phas_tmp = RALLOC(dcmplx,minfo->mapdisp[minfo->ntasks]/2);\n\n  for (int task=0; task<minfo->ntasks; ++task)\n    for (int th=0; th<minfo->npair[minfo->mytask]; ++th)\n      for (int mi=0; mi<minfo->nm[task]; ++mi)\n        {\n        int m = minfo->mval[mi+minfo->ofs_m[task]];\n        int o1 = minfo->mapdisp[task]/2+minfo->nph*(mi+th*minfo->nm[task]);\n        int o2 = minfo->nph*(th*(minfo->mmax+1) + m);\n        for (int i=0; i<minfo->nph; ++i)\n          phas_tmp[o1+i] = (*ph)[o2+i];\n        }\n\n  DEALLOC(*ph);\n  ALLOC(*ph,dcmplx,minfo->nph*minfo->nm[minfo->mytask]*minfo->npairtotal);\n\n  MPI_Alltoallv (phas_tmp,minfo->mapcount,minfo->mapdisp,MPI_DOUBLE,\n    *ph,minfo->almcount,minfo->almdisp,MPI_DOUBLE,minfo->comm);\n\n  DEALLOC(phas_tmp);\n  }\n\nstatic void alloc_phase_mpi (sharp_job *job, int nm, int ntheta,\n  int nmfull, int nthetafull)\n  {\n  ptrdiff_t phase_size = (job->type==SHARP_MAP2ALM) ?\n    (ptrdiff_t)(nmfull)*ntheta : (ptrdiff_t)(nm)*nthetafull;\n  job->phase=RALLOC(dcmplx,2*job->ntrans*job->nmaps*phase_size);\n  job->s_m=2*job->ntrans*job->nmaps;\n  job->s_th = job->s_m * ((job->type==SHARP_MAP2ALM) ? nmfull : nm);\n  }\n\nstatic void alm2map_comm (sharp_job *job, const sharp_mpi_info *minfo)\n  {\n  if (job->type != SHARP_MAP2ALM)\n    {\n    sharp_communicate_alm2map (minfo,&job->phase);\n    job->s_th=job->s_m*minfo->nmtotal;\n    }\n  }\n\nstatic void map2alm_comm (sharp_job *job, const sharp_mpi_info *minfo)\n  {\n  if (job->type == SHARP_MAP2ALM)\n    {\n    sharp_communicate_map2alm (minfo,&job->phase);\n    job->s_th=job->s_m*minfo->nm[minfo->mytask];\n    }\n  }\n\nstatic void sharp_execute_job_mpi (sharp_job *job, MPI_Comm comm)\n  {\n  int ntasks;\n  MPI_Comm_size(comm, &ntasks);\n  if (ntasks==1) /* fall back to scalar implementation */\n    { sharp_execute_job (job); return; }\n\n  MPI_Barrier(comm);\n  double timer=wallTime();\n  job->opcnt=0;\n  sharp_mpi_info minfo;\n  sharp_make_mpi_info(comm, job, &minfo);\n\n  if (minfo.npairtotal>minfo.ntasks*300)\n    {\n    int nsub=(minfo.npairtotal+minfo.ntasks*200-1)/(minfo.ntasks*200);\n    for (int isub=0; isub<nsub; ++isub)\n      {\n      sharp_job ljob=*job;\n      // When creating a_lm, every sub-job produces a complete set of\n      // coefficients; they need to be added up.\n      if ((isub>0)&&(job->type==SHARP_MAP2ALM)) ljob.flags|=SHARP_ADD;\n      sharp_geom_info lginfo;\n      lginfo.pair=RALLOC(sharp_ringpair,(job->ginfo->npairs/nsub)+1);\n      lginfo.npairs=0;\n      lginfo.nphmax = job->ginfo->nphmax;\n      while (lginfo.npairs*nsub+isub<job->ginfo->npairs)\n        {\n        lginfo.pair[lginfo.npairs]=job->ginfo->pair[lginfo.npairs*nsub+isub];\n        ++lginfo.npairs;\n        }\n      ljob.ginfo=&lginfo;\n      sharp_execute_job_mpi (&ljob,comm);\n      job->opcnt+=ljob.opcnt;\n      DEALLOC(lginfo.pair);\n      }\n    }\n  else\n    {\n    int lmax = job->ainfo->lmax;\n    job->norm_l = sharp_Ylmgen_get_norm (lmax, job->spin);\n\n    /* clear output arrays if requested */\n    init_output (job);\n\n    alloc_phase_mpi (job,job->ainfo->nm,job->ginfo->npairs,minfo.mmax+1,\n      minfo.npairtotal);\n\n    double *cth = RALLOC(double,minfo.npairtotal),\n          *sth = RALLOC(double,minfo.npairtotal);\n    int *mlim = RALLOC(int,minfo.npairtotal);\n    for (int i=0; i<minfo.npairtotal; ++i)\n      {\n      cth[i] = cos(minfo.theta[i]);\n      sth[i] = sin(minfo.theta[i]);\n      mlim[i] = sharp_get_mlim(lmax, job->spin, sth[i], cth[i]);\n      }\n\n    /* map->phase where necessary */\n    map2phase (job, minfo.mmax, 0, job->ginfo->npairs);\n\n    map2alm_comm (job, &minfo);\n\n#pragma omp parallel if ((job->flags&SHARP_NO_OPENMP)==0)\n{\n    sharp_job ljob = *job;\n    sharp_Ylmgen_C generator;\n    sharp_Ylmgen_init (&generator,lmax,minfo.mmax,ljob.spin);\n    alloc_almtmp(&ljob,lmax);\n\n#pragma omp for schedule(dynamic,1)\n    for (int mi=0; mi<job->ainfo->nm; ++mi)\n      {\n  /* alm->alm_tmp where necessary */\n      alm2almtmp (&ljob, lmax, mi);\n\n  /* inner conversion loop */\n      inner_loop (&ljob, minfo.ispair, cth, sth, 0, minfo.npairtotal,\n        &generator, mi, mlim);\n\n  /* alm_tmp->alm where necessary */\n      almtmp2alm (&ljob, lmax, mi);\n      }\n\n    sharp_Ylmgen_destroy(&generator);\n    dealloc_almtmp(&ljob);\n\n#pragma omp critical\n    job->opcnt+=ljob.opcnt;\n} /* end of parallel region */\n\n    alm2map_comm (job, &minfo);\n\n  /* phase->map where necessary */\n    phase2map (job, minfo.mmax, 0, job->ginfo->npairs);\n\n    DEALLOC(mlim);\n    DEALLOC(cth);\n    DEALLOC(sth);\n    DEALLOC(job->norm_l);\n    dealloc_phase (job);\n    }\n  sharp_destroy_mpi_info(&minfo);\n  job->time=wallTime()-timer;\n  }\n\nvoid sharp_execute_mpi (MPI_Comm comm, sharp_jobtype type, int spin,\n  void *alm, void *map, const sharp_geom_info *geom_info,\n  const sharp_alm_info *alm_info, int ntrans, int flags, double *time,\n  unsigned long long *opcnt)\n  {\n  sharp_job job;\n  sharp_build_job_common (&job, type, spin, alm, map, geom_info, alm_info,\n    ntrans, flags);\n\n  sharp_execute_job_mpi (&job, comm);\n  if (time!=NULL) *time = job.time;\n  if (opcnt!=NULL) *opcnt = job.opcnt;\n  }\n\n/* We declare this only in C file to make symbol available for Fortran wrappers;\n   without declaring it in C header as it should not be available to C code */\nvoid sharp_execute_mpi_fortran(MPI_Fint comm, sharp_jobtype type, int spin,\n  void *alm, void *map, const sharp_geom_info *geom_info,\n  const sharp_alm_info *alm_info, int ntrans, int flags, double *time,\n  unsigned long long *opcnt);\nvoid sharp_execute_mpi_fortran(MPI_Fint comm, sharp_jobtype type, int spin,\n  void *alm, void *map, const sharp_geom_info *geom_info,\n  const sharp_alm_info *alm_info, int ntrans, int flags, double *time,\n  unsigned long long *opcnt)\n  {\n  sharp_execute_mpi(MPI_Comm_f2c(comm), type, spin, alm, map, geom_info,\n                    alm_info, ntrans, flags, time, opcnt);\n  }\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_mpi.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_mpi.h\n *  Interface for the spherical transform library with MPI support.\n *\n *  Copyright (C) 2011,2012 Max-Planck-Society\n *  \\author Martin Reinecke \\author Dag Sverre Seljebotn\n */\n\n#ifndef PLANCK_SHARP_MPI_H\n#define PLANCK_SHARP_MPI_H\n\n#include <mpi.h>\n#include \"sharp_lowlevel.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*! Performs an MPI parallel libsharp SHT job. The interface deliberately does\n  not use the C99 \"complex\" data type, in order to be callable from C.\n  \\param comm the MPI communicator to be used for this SHT\n  \\param type the type of SHT\n  \\param spin the spin of the quantities to be transformed\n  \\param alm contains pointers to the a_lm coefficients. If \\a spin==0,\n    alm[0] points to the a_lm of the first SHT, alm[1] to those of the second\n    etc. If \\a spin>0, alm[0] and alm[1] point to the a_lm of the first SHT,\n    alm[2] and alm[3] to those of the second, etc. The exact data type of \\a alm\n    depends on whether the SHARP_DP flag is set.\n  \\param map contains pointers to the maps. If \\a spin==0,\n    map[0] points to the map of the first SHT, map[1] to that of the second\n    etc. If \\a spin>0, or \\a type is SHARP_ALM2MAP_DERIV1, map[0] and map[1]\n    point to the maps of the first SHT, map[2] and map[3] to those of the\n    second, etc. The exact data type of \\a map depends on whether the SHARP_DP\n    flag is set.\n  \\param geom_info A \\c sharp_geom_info object compatible with the provided\n    \\a map arrays. The total map geometry is the union of all \\a geom_info\n    objects over the participating MPI tasks.\n  \\param alm_info A \\c sharp_alm_info object compatible with the provided\n    \\a alm arrays. All \\c m values from 0 to some \\c mmax<=lmax must be present\n    exactly once in the union of all \\a alm_info objects over the participating\n    MPI tasks.\n  \\param ntrans the number of simultaneous SHTs\n  \\param flags See sharp_jobflags. In particular, if SHARP_DP is set, then\n    \\a alm is expected to have the type \"complex double **\" and \\a map is\n    expected to have the type \"double **\"; otherwise, the expected\n    types are \"complex float **\" and \"float **\", respectively.\n  \\param time If not NULL, the wall clock time required for this SHT\n    (in seconds) will be written here.\n  \\param opcnt If not NULL, a conservative estimate of the total floating point\n    operation count for this SHT will be written here. */\nvoid sharp_execute_mpi (MPI_Comm comm, sharp_jobtype type, int spin,\n  void *alm, void *map, const sharp_geom_info *geom_info,\n  const sharp_alm_info *alm_info, int ntrans, int flags, double *time,\n  unsigned long long *opcnt);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_testsuite.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*  \\file sharp_testsuite.c\n * \n *  Copyright (C) 2012-2013 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#include <stdio.h>\n#include <string.h>\n#ifdef USE_MPI\n#include \"mpi.h\"\n#include \"sharp_mpi.h\"\n#endif\n#ifdef _OPENMP\n#include <omp.h>\n#endif\n#include \"sharp.h\"\n#include \"sharp_internal.h\"\n#include \"sharp_geomhelpers.h\"\n#include \"sharp_almhelpers.h\"\n#include \"c_utils.h\"\n#include \"sharp_announce.h\"\n#include \"memusage.h\"\n#include \"sharp_vecsupport.h\"\n\ntypedef complex double dcmplx;\n\nint ntasks, mytask;\n\nstatic double drand (double min, double max, int *state)\n  {\n  *state = (((*state) * 1103515245) + 12345) & 0x7fffffff;\n  return min + (max-min)*(*state)/(0x7fffffff+1.0);\n  }\n\nstatic void random_alm (dcmplx *alm, sharp_alm_info *helper, int spin, int cnt)\n  {\n#pragma omp parallel\n{\n  int mi;\n#pragma omp for schedule (dynamic,100)\n  for (mi=0;mi<helper->nm; ++mi)\n    {\n    int m=helper->mval[mi];\n    int state=1234567*cnt+8912*m; // random seed\n    for (int l=m;l<=helper->lmax; ++l)\n      {\n      if ((l<spin)&&(m<spin))\n        alm[sharp_alm_index(helper,l,mi)] = 0.;\n      else\n        {\n        double rv = drand(-1,1,&state);\n        double iv = (m==0) ? 0 : drand(-1,1,&state);\n        alm[sharp_alm_index(helper,l,mi)] = rv+_Complex_I*iv;\n        }\n      }\n    }\n} // end of parallel region\n  }\n\nstatic unsigned long long totalops (unsigned long long val)\n  {\n#ifdef USE_MPI\n  unsigned long long tmp;\n  MPI_Allreduce (&val, &tmp,1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD);\n  return tmp;\n#else\n  return val;\n#endif\n  }\n\nstatic double maxTime (double val)\n  {\n#ifdef USE_MPI\n  double tmp;\n  MPI_Allreduce (&val, &tmp,1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);\n  return tmp;\n#else\n  return val;\n#endif\n  }\n\nstatic double allreduceSumDouble (double val)\n  {\n#ifdef USE_MPI\n  double tmp;\n  MPI_Allreduce (&val, &tmp,1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);\n  return tmp;\n#else\n  return val;\n#endif\n  }\n\nstatic double totalMem()\n  {\n#ifdef USE_MPI\n  double tmp, val=VmHWM();\n  MPI_Allreduce (&val, &tmp,1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);\n  return tmp;\n#else\n  return VmHWM();\n#endif\n  }\n\n#ifdef USE_MPI\nstatic void reduce_alm_info(sharp_alm_info *ainfo)\n  {\n  int nmnew=0;\n  ptrdiff_t ofs = 0;\n  for (int i=mytask; i<ainfo->nm; i+=ntasks,++nmnew)\n    {\n    ainfo->mval[nmnew]=ainfo->mval[i];\n    ainfo->mvstart[nmnew]=ofs-ainfo->mval[nmnew];\n    ofs+=ainfo->lmax-ainfo->mval[nmnew]+1;\n    }\n  ainfo->nm=nmnew;\n  }\n\nstatic void reduce_geom_info(sharp_geom_info *ginfo)\n  {\n  int npairsnew=0;\n  ptrdiff_t ofs = 0;\n  for (int i=mytask; i<ginfo->npairs; i+=ntasks,++npairsnew)\n    {\n    ginfo->pair[npairsnew]=ginfo->pair[i];\n    ginfo->pair[npairsnew].r1.ofs=ofs;\n    ofs+=ginfo->pair[npairsnew].r1.nph;\n    ginfo->pair[npairsnew].r2.ofs=ofs;\n    if (ginfo->pair[npairsnew].r2.nph>0) ofs+=ginfo->pair[npairsnew].r2.nph;\n    }\n  ginfo->npairs=npairsnew;\n  }\n#endif\n\nstatic ptrdiff_t get_nalms(const sharp_alm_info *ainfo)\n  {\n  ptrdiff_t res=0;\n  for (int i=0; i<ainfo->nm; ++i)\n    res += ainfo->lmax-ainfo->mval[i]+1;\n  return res;\n  }\n\nstatic ptrdiff_t get_npix(const sharp_geom_info *ginfo)\n  {\n  ptrdiff_t res=0;\n  for (int i=0; i<ginfo->npairs; ++i)\n    {\n    res += ginfo->pair[i].r1.nph;\n    if (ginfo->pair[i].r2.nph>0) res += ginfo->pair[i].r2.nph;\n    }\n  return res;\n  }\n\nstatic double *get_sqsum_and_invert (dcmplx **alm, ptrdiff_t nalms, int ncomp)\n  {\n  double *sqsum=RALLOC(double,ncomp);\n  for (int i=0; i<ncomp; ++i)\n    {\n    sqsum[i]=0;\n    for (ptrdiff_t j=0; j<nalms; ++j)\n      {\n      sqsum[i]+=creal(alm[i][j])*creal(alm[i][j])\n               +cimag(alm[i][j])*cimag(alm[i][j]);\n      alm[i][j]=-alm[i][j];\n      }\n    }\n  return sqsum;\n  }\n\nstatic void get_errors (dcmplx **alm, ptrdiff_t nalms, int ncomp, double *sqsum,\n  double **err_abs, double **err_rel)\n  {\n  long nalms_tot=nalms;\n#ifdef USE_MPI\n  MPI_Allreduce(&nalms,&nalms_tot,1,MPI_LONG,MPI_SUM,MPI_COMM_WORLD);\n#endif\n\n  *err_abs=RALLOC(double,ncomp);\n  *err_rel=RALLOC(double,ncomp);\n  for (int i=0; i<ncomp; ++i)\n    {\n    double sum=0, maxdiff=0, sumtot, sqsumtot, maxdifftot;\n    for (ptrdiff_t j=0; j<nalms; ++j)\n      {\n      double sqr=creal(alm[i][j])*creal(alm[i][j])\n                +cimag(alm[i][j])*cimag(alm[i][j]);\n      sum+=sqr;\n      if (sqr>maxdiff) maxdiff=sqr;\n      }\n   maxdiff=sqrt(maxdiff);\n\n#ifdef USE_MPI\n    MPI_Allreduce(&sum,&sumtot,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);\n    MPI_Allreduce(&sqsum[i],&sqsumtot,1,MPI_DOUBLE,MPI_SUM,MPI_COMM_WORLD);\n    MPI_Allreduce(&maxdiff,&maxdifftot,1,MPI_DOUBLE,MPI_MAX,MPI_COMM_WORLD);\n#else\n    sumtot=sum;\n    sqsumtot=sqsum[i];\n    maxdifftot=maxdiff;\n#endif\n    sumtot=sqrt(sumtot/nalms_tot);\n    sqsumtot=sqrt(sqsumtot/nalms_tot);\n    (*err_abs)[i]=maxdifftot;\n    (*err_rel)[i]=sumtot/sqsumtot;\n    }\n  }\n\nstatic int good_fft_size(int n)\n  {\n  if (n<=6) return n;\n  int bestfac=2*n;\n\n  for (int f2=1; f2<bestfac; f2*=2)\n    for (int f23=f2; f23<bestfac; f23*=3)\n      for (int f235=f23; f235<bestfac; f235*=5)\n        if (f235>=n) bestfac=f235;\n\n  return bestfac;\n  }\n\nstatic void get_infos (const char *gname, int lmax, int *mmax, int *gpar1,\n  int *gpar2, sharp_geom_info **ginfo, sharp_alm_info **ainfo)\n  {\n  UTIL_ASSERT(lmax>=0,\"lmax must not be negative\");\n  if (*mmax<0) *mmax=lmax;\n  UTIL_ASSERT(*mmax<=lmax,\"mmax larger than lmax\");\n\n  if (mytask==0) printf (\"lmax: %d, mmax: %d\\n\",lmax,*mmax);\n\n  sharp_make_triangular_alm_info(lmax,*mmax,1,ainfo);\n#ifdef USE_MPI\n  reduce_alm_info(*ainfo);\n#endif\n\n  if (strcmp(gname,\"healpix\")==0)\n    {\n    if (*gpar1<1) *gpar1=lmax/2;\n    if (*gpar1==0) ++(*gpar1);\n    sharp_make_healpix_geom_info (*gpar1, 1, ginfo);\n    if (mytask==0) printf (\"HEALPix grid, nside=%d\\n\",*gpar1);\n    }\n  else if (strcmp(gname,\"gauss\")==0)\n    {\n    if (*gpar1<1) *gpar1=lmax+1;\n    if (*gpar2<1) *gpar2=2*(*mmax)+1;\n    sharp_make_gauss_geom_info (*gpar1, *gpar2, 0., 1, *gpar2, ginfo);\n    if (mytask==0)\n      printf (\"Gauss-Legendre grid, nlat=%d, nlon=%d\\n\",*gpar1,*gpar2);\n    }\n  else if (strcmp(gname,\"fejer1\")==0)\n    {\n    if (*gpar1<1) *gpar1=2*lmax+1;\n    if (*gpar2<1) *gpar2=2*(*mmax)+1;\n    sharp_make_fejer1_geom_info (*gpar1, *gpar2, 0., 1, *gpar2, ginfo);\n    if (mytask==0) printf (\"Fejer1 grid, nlat=%d, nlon=%d\\n\",*gpar1,*gpar2);\n    }\n  else if (strcmp(gname,\"fejer2\")==0)\n    {\n    if (*gpar1<1) *gpar1=2*lmax+1;\n    if (*gpar2<1) *gpar2=2*(*mmax)+1;\n    sharp_make_fejer2_geom_info (*gpar1, *gpar2, 0., 1, *gpar2, ginfo);\n    if (mytask==0) printf (\"Fejer2 grid, nlat=%d, nlon=%d\\n\",*gpar1,*gpar2);\n    }\n  else if (strcmp(gname,\"cc\")==0)\n    {\n    if (*gpar1<1) *gpar1=2*lmax+1;\n    if (*gpar2<1) *gpar2=2*(*mmax)+1;\n    sharp_make_cc_geom_info (*gpar1, *gpar2, 0., 1, *gpar2, ginfo);\n    if (mytask==0)\n      printf(\"Clenshaw-Curtis grid, nlat=%d, nlon=%d\\n\",*gpar1,*gpar2);\n    }\n  else if (strcmp(gname,\"smallgauss\")==0)\n    {\n    int nlat=*gpar1, nlon=*gpar2;\n    if (nlat<1) nlat=lmax+1;\n    if (nlon<1) nlon=2*(*mmax)+1;\n    *gpar1=nlat; *gpar2=nlon;\n    sharp_make_gauss_geom_info (nlat, nlon, 0., 1, nlon, ginfo);\n    ptrdiff_t npix_o=get_npix(*ginfo);\n    size_t ofs=0;\n    for (int i=0; i<(*ginfo)->npairs; ++i)\n      {\n      sharp_ringpair *pair=&((*ginfo)->pair[i]);\n      int pring=1+2*sharp_get_mlim(lmax,0,pair->r1.sth,pair->r1.cth);\n      if (pring>nlon) pring=nlon;\n      pring=good_fft_size(pring);\n      pair->r1.nph=pring;\n      pair->r1.weight*=nlon*1./pring;\n      pair->r1.ofs=ofs;\n      ofs+=pring;\n      if (pair->r2.nph>0)\n        {\n        pair->r2.nph=pring;\n        pair->r2.weight*=nlon*1./pring;\n        pair->r2.ofs=ofs;\n        ofs+=pring;\n        }\n      }\n    if (mytask==0)\n      {\n      ptrdiff_t npix=get_npix(*ginfo);\n      printf(\"Small Gauss grid, nlat=%d, npix=%ld, savings=%.2f%%\\n\",\n        nlat,(long)npix,(npix_o-npix)*100./npix_o);\n      }\n    }\n  else\n    UTIL_FAIL(\"unknown grid geometry\");\n\n#ifdef USE_MPI\n  reduce_geom_info(*ginfo);\n#endif\n  }\n\nstatic void check_sign_scale(void)\n  {\n  int lmax=50;\n  int mmax=lmax;\n  sharp_geom_info *tinfo;\n  int nrings=lmax+1;\n  int ppring=2*lmax+2;\n  ptrdiff_t npix=(ptrdiff_t)nrings*ppring;\n  sharp_make_gauss_geom_info (nrings, ppring, 0., 1, ppring, &tinfo);\n\n  /* flip theta to emulate the \"old\" Gaussian grid geometry */\n  for (int i=0; i<tinfo->npairs; ++i)\n    {\n    const double pi=3.141592653589793238462643383279502884197;\n    tinfo->pair[i].r1.cth=-tinfo->pair[i].r1.cth;\n    tinfo->pair[i].r2.cth=-tinfo->pair[i].r2.cth;\n    tinfo->pair[i].r1.theta=pi-tinfo->pair[i].r1.theta;\n    tinfo->pair[i].r2.theta=pi-tinfo->pair[i].r2.theta;\n    }\n\n  sharp_alm_info *alms;\n  sharp_make_triangular_alm_info(lmax,mmax,1,&alms);\n  ptrdiff_t nalms = ((mmax+1)*(mmax+2))/2 + (mmax+1)*(lmax-mmax);\n\n  for (int ntrans=1; ntrans<10; ++ntrans)\n    {\n    double **map;\n    ALLOC2D(map,double,2*ntrans,npix);\n\n    dcmplx **alm;\n    ALLOC2D(alm,dcmplx,2*ntrans,nalms);\n    for (int i=0; i<2*ntrans; ++i)\n      for (int j=0; j<nalms; ++j)\n        alm[i][j]=1.+_Complex_I;\n\n    sharp_execute(SHARP_ALM2MAP,0,&alm[0],&map[0],tinfo,alms,ntrans,SHARP_DP,\n      NULL,NULL);\n    for (int it=0; it<ntrans; ++it)\n      {\n      UTIL_ASSERT(FAPPROX(map[it][0     ], 3.588246976618616912e+00,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[it][npix/2], 4.042209792157496651e+01,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[it][npix-1],-1.234675107554816442e+01,1e-12),\n        \"error\");\n      }\n    sharp_execute(SHARP_ALM2MAP,1,&alm[0],&map[0],tinfo,alms,ntrans,SHARP_DP,\n      NULL,NULL);\n    for (int it=0; it<ntrans; ++it)\n      {\n      UTIL_ASSERT(FAPPROX(map[2*it  ][0     ], 2.750897760535633285e+00,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it  ][npix/2], 3.137704477368562905e+01,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it  ][npix-1],-8.405730859837063917e+01,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][0     ],-2.398026536095463346e+00,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][npix/2],-4.961140548331700728e+01,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][npix-1],-1.412765834230440021e+01,1e-12),\n        \"error\");\n      }\n\n    sharp_execute(SHARP_ALM2MAP,2,&alm[0],&map[0],tinfo,alms,ntrans,SHARP_DP,\n      NULL,NULL);\n    for (int it=0; it<ntrans; ++it)\n      {\n      UTIL_ASSERT(FAPPROX(map[2*it  ][0     ],-1.398186224727334448e+00,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it  ][npix/2],-2.456676000884031197e+01,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it  ][npix-1],-1.516249174408820863e+02,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][0     ],-3.173406200299964119e+00,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][npix/2],-5.831327404513146462e+01,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][npix-1],-1.863257892248353897e+01,1e-12),\n        \"error\");\n      }\n\n    sharp_execute(SHARP_ALM2MAP_DERIV1,1,&alm[0],&map[0],tinfo,alms,ntrans,\n      SHARP_DP,NULL,NULL);\n    for (int it=0; it<ntrans; ++it)\n      {\n      UTIL_ASSERT(FAPPROX(map[2*it  ][0     ],-6.859393905369091105e-01,1e-11),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it  ][npix/2],-2.103947835973212364e+02,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it  ][npix-1],-1.092463246472086439e+03,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][0     ],-1.411433220713928165e+02,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][npix/2],-1.146122859381925082e+03,1e-12),\n        \"error\");\n      UTIL_ASSERT(FAPPROX(map[2*it+1][npix-1], 7.821618677689795049e+02,1e-12),\n        \"error\");\n      }\n\n    DEALLOC2D(map);\n    DEALLOC2D(alm);\n    }\n\n  sharp_destroy_alm_info(alms);\n  sharp_destroy_geom_info(tinfo);\n  }\n\nstatic void do_sht (sharp_geom_info *ginfo, sharp_alm_info *ainfo,\n  int spin, int ntrans, int nv, double **err_abs, double **err_rel,\n  double *t_a2m, double *t_m2a, unsigned long long *op_a2m,\n  unsigned long long *op_m2a)\n  {\n  ptrdiff_t nalms = get_nalms(ainfo);\n  int ncomp = ntrans*((spin==0) ? 1 : 2);\n\n  size_t npix = get_npix(ginfo);\n  double **map;\n  ALLOC2D(map,double,ncomp,npix);\n  for (int i=0; i<ncomp; ++i)\n    SET_ARRAY(map[i],0,(int)npix,0);\n\n  dcmplx **alm;\n  ALLOC2D(alm,dcmplx,ncomp,nalms);\n  for (int i=0; i<ncomp; ++i)\n    random_alm(alm[i],ainfo,spin,i+1);\n\n#ifdef USE_MPI\n  sharp_execute_mpi(MPI_COMM_WORLD,SHARP_ALM2MAP,spin,&alm[0],&map[0],ginfo,\n    ainfo,ntrans, SHARP_DP|SHARP_ADD|nv,t_a2m,op_a2m);\n#else\n  sharp_execute(SHARP_ALM2MAP,spin,&alm[0],&map[0],ginfo,ainfo,ntrans,\n    SHARP_DP|nv,t_a2m,op_a2m);\n#endif\n  if (t_a2m!=NULL) *t_a2m=maxTime(*t_a2m);\n  if (op_a2m!=NULL) *op_a2m=totalops(*op_a2m);\n  double *sqsum=get_sqsum_and_invert(alm,nalms,ncomp);\n#ifdef USE_MPI\n  sharp_execute_mpi(MPI_COMM_WORLD,SHARP_MAP2ALM,spin,&alm[0],&map[0],ginfo,\n    ainfo,ntrans,SHARP_DP|SHARP_ADD|nv,t_m2a,op_m2a);\n#else\n  sharp_execute(SHARP_MAP2ALM,spin,&alm[0],&map[0],ginfo,ainfo,ntrans,\n    SHARP_DP|SHARP_ADD|nv,t_m2a,op_m2a);\n#endif\n  if (t_m2a!=NULL) *t_m2a=maxTime(*t_m2a);\n  if (op_m2a!=NULL) *op_m2a=totalops(*op_m2a);\n  get_errors(alm, nalms, ncomp, sqsum, err_abs, err_rel);\n\n  DEALLOC(sqsum);\n  DEALLOC2D(map);\n  DEALLOC2D(alm);\n  }\n\nstatic void check_accuracy (sharp_geom_info *ginfo, sharp_alm_info *ainfo,\n  int spin, int ntrans, int nv)\n  {\n  int ncomp = ntrans*((spin==0) ? 1 : 2);\n  double *err_abs, *err_rel;\n  do_sht (ginfo, ainfo, spin, ntrans, nv, &err_abs, &err_rel, NULL, NULL,\n    NULL, NULL);\n  for (int i=0; i<ncomp; ++i)\n    UTIL_ASSERT((err_rel[i]<1e-10) && (err_abs[i]<1e-10),\"error\");\n  DEALLOC(err_rel);\n  DEALLOC(err_abs);\n  }\n\nstatic void sharp_acctest(void)\n  {\n  if (mytask==0) sharp_module_startup(\"sharp_acctest\",1,1,\"\",1);\n\n  if (mytask==0) printf(\"Checking signs and scales.\\n\");\n  check_sign_scale();\n  if (mytask==0) printf(\"Passed.\\n\\n\");\n\n  if (mytask==0) printf(\"Testing map analysis accuracy.\\n\");\n\n  sharp_geom_info *ginfo;\n  sharp_alm_info *ainfo;\n  int lmax=127, mmax=127, nlat=128, nlon=256;\n  get_infos (\"gauss\", lmax, &mmax, &nlat, &nlon, &ginfo, &ainfo);\n  for (int nv=1; nv<=6; ++nv)\n    for (int ntrans=1; ntrans<=6; ++ntrans)\n      {\n      check_accuracy(ginfo,ainfo,0,ntrans,nv);\n      check_accuracy(ginfo,ainfo,1,ntrans,nv);\n      check_accuracy(ginfo,ainfo,2,ntrans,nv);\n      check_accuracy(ginfo,ainfo,3,ntrans,nv);\n      check_accuracy(ginfo,ainfo,30,ntrans,nv);\n      }\n  sharp_destroy_alm_info(ainfo);\n  sharp_destroy_geom_info(ginfo);\n  if (mytask==0) printf(\"Passed.\\n\\n\");\n  }\n\nstatic void sharp_test (int argc, const char **argv)\n  {\n  if (mytask==0) sharp_announce(\"sharp_test\");\n  UTIL_ASSERT(argc>=9,\"usage: grid lmax mmax geom1 geom2 spin ntrans\");\n  int lmax=atoi(argv[3]);\n  int mmax=atoi(argv[4]);\n  int gpar1=atoi(argv[5]);\n  int gpar2=atoi(argv[6]);\n  int spin=atoi(argv[7]);\n  int ntrans=atoi(argv[8]);\n\n  if (mytask==0) printf(\"Testing map analysis accuracy.\\n\");\n  if (mytask==0) printf(\"spin=%d, ntrans=%d\\n\", spin, ntrans);\n\n  sharp_geom_info *ginfo;\n  sharp_alm_info *ainfo;\n  get_infos (argv[2], lmax, &mmax, &gpar1, &gpar2, &ginfo, &ainfo);\n\n  int ncomp = ntrans*((spin==0) ? 1 : 2);\n  double t_a2m=1e30, t_m2a=1e30;\n  unsigned long long op_a2m, op_m2a;\n  double *err_abs,*err_rel;\n\n  double t_acc=0;\n  int nrpt=0;\n  while(1)\n    {\n    ++nrpt;\n    double ta2m2, tm2a2;\n    do_sht (ginfo, ainfo, spin, ntrans, 0, &err_abs, &err_rel, &ta2m2, &tm2a2,\n      &op_a2m, &op_m2a);\n    if (ta2m2<t_a2m) t_a2m=ta2m2;\n    if (tm2a2<t_m2a) t_m2a=tm2a2;\n    t_acc+=t_a2m+t_m2a;\n    if (t_acc>2.)\n      {\n      if (mytask==0) printf(\"Best of %d runs\\n\",nrpt);\n      break;\n      }\n    DEALLOC(err_abs);\n    DEALLOC(err_rel);\n    }\n\n  if (mytask==0) printf(\"wall time for alm2map: %fs\\n\",t_a2m);\n  if (mytask==0) printf(\"Performance: %fGFLOPs/s\\n\",1e-9*op_a2m/t_a2m);\n  if (mytask==0) printf(\"wall time for map2alm: %fs\\n\",t_m2a);\n  if (mytask==0) printf(\"Performance: %fGFLOPs/s\\n\",1e-9*op_m2a/t_m2a);\n\n  if (mytask==0)\n    for (int i=0; i<ncomp; ++i)\n      printf(\"component %i: rms %e, maxerr %e\\n\",i,err_rel[i], err_abs[i]);\n\n  double iosize = ncomp*(16.*get_nalms(ainfo) + 8.*get_npix(ginfo));\n  iosize = allreduceSumDouble(iosize);\n\n  sharp_destroy_alm_info(ainfo);\n  sharp_destroy_geom_info(ginfo);\n\n  double tmem=totalMem();\n  if (mytask==0)\n    printf(\"\\nMemory high water mark: %.2f MB\\n\",tmem/(1<<20));\n  if (mytask==0)\n    printf(\"Memory overhead: %.2f MB (%.2f%% of working set)\\n\",\n      (tmem-iosize)/(1<<20),100.*(1.-iosize/tmem));\n\n#ifdef _OPENMP\n  int nomp=omp_get_max_threads();\n#else\n  int nomp=1;\n#endif\n\n  double maxerel=0., maxeabs=0.;\n  for (int i=0; i<ncomp; ++i)\n    {\n    if (maxerel<err_rel[i]) maxerel=err_rel[i];\n    if (maxeabs<err_abs[i]) maxeabs=err_abs[i];\n    }\n\n  if (mytask==0)\n    printf(\"%-12s %-10s %2d %d %2d %3d %6d %6d %6d %6d %2d %.2e %7.2f %.2e %7.2f\"\n           \" %9.2f %6.2f %.2e %.2e\\n\",\n      getenv(\"HOST\"),argv[2],spin,VLEN,nomp,ntasks,lmax,mmax,gpar1,gpar2,\n      ntrans,t_a2m,1e-9*op_a2m/t_a2m,t_m2a,1e-9*op_m2a/t_m2a,tmem/(1<<20),\n      100.*(1.-iosize/tmem),maxerel,maxeabs);\n\n  DEALLOC(err_abs);\n  DEALLOC(err_rel);\n  }\n\nstatic void sharp_bench (int argc, const char **argv)\n  {\n  if (mytask==0) sharp_announce(\"sharp_bench\");\n  UTIL_ASSERT(argc>=9,\"usage: grid lmax mmax geom1 geom2 spin ntrans\");\n  int lmax=atoi(argv[3]);\n  int mmax=atoi(argv[4]);\n  int gpar1=atoi(argv[5]);\n  int gpar2=atoi(argv[6]);\n  int spin=atoi(argv[7]);\n  int ntrans=atoi(argv[8]);\n\n  if (mytask==0) printf(\"Testing map analysis accuracy.\\n\");\n  if (mytask==0) printf(\"spin=%d, ntrans=%d\\n\", spin, ntrans);\n\n  sharp_geom_info *ginfo;\n  sharp_alm_info *ainfo;\n  get_infos (argv[2], lmax, &mmax, &gpar1, &gpar2, &ginfo, &ainfo);\n\n  double ta2m_auto=1e30, tm2a_auto=1e30, ta2m_min=1e30, tm2a_min=1e30;\n  unsigned long long opa2m_min=0, opm2a_min=0;\n  int nvmin_a2m=-1, nvmin_m2a=-1;\n  for (int nv=0; nv<=6; ++nv)\n    {\n    int ntries=0;\n    double tacc=0;\n    do\n      {\n      double t_a2m, t_m2a;\n      unsigned long long op_a2m, op_m2a;\n      double *err_abs,*err_rel;\n      do_sht (ginfo, ainfo, spin, ntrans, nv, &err_abs, &err_rel,\n        &t_a2m, &t_m2a, &op_a2m, &op_m2a);\n\n      DEALLOC(err_abs);\n      DEALLOC(err_rel);\n      tacc+=t_a2m+t_m2a;\n      ++ntries;\n      if (nv==0)\n        {\n        if (t_a2m<ta2m_auto) ta2m_auto=t_a2m;\n        if (t_m2a<tm2a_auto) tm2a_auto=t_m2a;\n        }\n      else\n        {\n        if (t_a2m<ta2m_min) { nvmin_a2m=nv; ta2m_min=t_a2m; opa2m_min=op_a2m; }\n        if (t_m2a<tm2a_min) { nvmin_m2a=nv; tm2a_min=t_m2a; opm2a_min=op_m2a; }\n        }\n      } while((ntries<2)||(tacc<3.));\n    }\n  if (mytask==0)\n    {\n    printf(\"a2m: nvmin=%d tmin=%fs speedup=%.2f%% perf=%.2fGFlops/s\\n\",\n      nvmin_a2m,ta2m_min,100.*(ta2m_auto-ta2m_min)/ta2m_auto,\n      1e-9*opa2m_min/ta2m_min);\n    printf(\"m2a: nvmin=%d tmin=%fs speedup=%.2f%% perf=%.2fGFlops/s\\n\",\n      nvmin_m2a,tm2a_min,100.*(tm2a_auto-tm2a_min)/tm2a_auto,\n      1e-9*opm2a_min/tm2a_min);\n    }\n\n  sharp_destroy_alm_info(ainfo);\n  sharp_destroy_geom_info(ginfo);\n  }\n\nint main(int argc, const char **argv)\n  {\n#ifdef USE_MPI\n  MPI_Init(NULL,NULL);\n  MPI_Comm_size(MPI_COMM_WORLD,&ntasks);\n  MPI_Comm_rank(MPI_COMM_WORLD,&mytask);\n#else\n  mytask=0; ntasks=1;\n#endif\n\n  UTIL_ASSERT(argc>=2,\"need at least one command line argument\");\n\n  if (strcmp(argv[1],\"acctest\")==0)\n    sharp_acctest();\n  else if (strcmp(argv[1],\"test\")==0)\n    sharp_test(argc,argv);\n  else if (strcmp(argv[1],\"bench\")==0)\n    sharp_bench(argc,argv);\n  else\n    UTIL_FAIL(\"unknown command\");\n\n#ifdef USE_MPI\n  MPI_Finalize();\n#endif\n  return 0;\n  }\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_vecsupport.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*  \\file sharp_vecsupport.h\n *  Convenience functions for vector arithmetics\n *\n *  Copyright (C) 2012,2013 Max-Planck-Society\n *  Author: Martin Reinecke\n */\n\n#ifndef SHARP_VECSUPPORT_H\n#define SHARP_VECSUPPORT_H\n\n#include <math.h>\n#include \"sharp_vecutil.h\"\n\ntypedef double Ts;\n\n#if (VLEN==1)\n\ntypedef double Tv;\ntypedef float Tv_s;\ntypedef int Tm;\n\n#define vadd(a,b) ((a)+(b))\n#define vadd_s(a,b) ((a)+(b))\n#define vaddeq(a,b) ((a)+=(b))\n#define vaddeq_mask(mask,a,b) if (mask) (a)+=(b);\n#define vsub(a,b) ((a)-(b))\n#define vsub_s(a,b) ((a)-(b))\n#define vsubeq(a,b) ((a)-=(b))\n#define vsubeq_mask(mask,a,b) if (mask) (a)-=(b);\n#define vmul(a,b) ((a)*(b))\n#define vmul_s(a,b) ((a)*(b))\n#define vmuleq(a,b) ((a)*=(b))\n#define vmuleq_mask(mask,a,b) if (mask) (a)*=(b);\n#define vfmaeq(a,b,c) ((a)+=(b)*(c))\n#define vfmaeq_s(a,b,c) ((a)+=(b)*(c))\n#define vfmseq(a,b,c) ((a)-=(b)*(c))\n#define vfmaaeq(a,b,c,d,e) ((a)+=(b)*(c)+(d)*(e))\n#define vfmaseq(a,b,c,d,e) ((a)+=(b)*(c)-(d)*(e))\n#define vneg(a) (-(a))\n#define vload(a) (a)\n#define vload_s(a) (a)\n#define vloadu(p) (*(p))\n#define vloadu_s(p) (*(p))\n#define vabs(a) fabs(a)\n#define vsqrt(a) sqrt(a)\n#define vlt(a,b) ((a)<(b))\n#define vgt(a,b) ((a)>(b))\n#define vge(a,b) ((a)>=(b))\n#define vne(a,b) ((a)!=(b))\n#define vand_mask(a,b) ((a)&&(b))\n#define vstoreu(p, a) (*(p)=a)\n#define vstoreu_s(p, a) (*(p)=a)\n\nstatic inline Tv vmin (Tv a, Tv b) { return (a<b) ? a : b; }\nstatic inline Tv vmax (Tv a, Tv b) { return (a>b) ? a : b; }\n\n#define vanyTrue(a) (a)\n#define vallTrue(a) (a)\n#define vzero 0.\n#define vone 1.\n\n#endif\n\n#if (VLEN==2)\n\n#include <emmintrin.h>\n\n#if defined (__SSE3__)\n#include <pmmintrin.h>\n#endif\n#if defined (__SSE4_1__)\n#include <smmintrin.h>\n#endif\n\ntypedef __m128d Tv;\ntypedef __m128 Tv_s;\ntypedef __m128d Tm;\n\n#if defined(__SSE4_1__)\n#define vblend__(m,a,b) _mm_blendv_pd(b,a,m)\n#else\nstatic inline Tv vblend__(Tv m, Tv a, Tv b)\n  { return _mm_or_pd(_mm_and_pd(a,m),_mm_andnot_pd(m,b)); }\n#endif\n#define vzero _mm_setzero_pd()\n#define vone _mm_set1_pd(1.)\n\n#define vadd(a,b) _mm_add_pd(a,b)\n#define vadd_s(a,b) _mm_add_ps(a,b)\n#define vaddeq(a,b) a=_mm_add_pd(a,b)\n#define vaddeq_mask(mask,a,b) a=_mm_add_pd(a,vblend__(mask,b,vzero))\n#define vsub(a,b) _mm_sub_pd(a,b)\n#define vsub_s(a,b) _mm_sub_ps(a,b)\n#define vsubeq(a,b) a=_mm_sub_pd(a,b)\n#define vsubeq_mask(mask,a,b) a=_mm_sub_pd(a,vblend__(mask,b,vzero))\n#define vmul(a,b) _mm_mul_pd(a,b)\n#define vmul_s(a,b) _mm_mul_ps(a,b)\n#define vmuleq(a,b) a=_mm_mul_pd(a,b)\n#define vmuleq_mask(mask,a,b) a=_mm_mul_pd(a,vblend__(mask,b,vone))\n#define vfmaeq(a,b,c) a=_mm_add_pd(a,_mm_mul_pd(b,c))\n#define vfmaeq_s(a,b,c) a=_mm_add_ps(a,_mm_mul_ps(b,c))\n#define vfmseq(a,b,c) a=_mm_sub_pd(a,_mm_mul_pd(b,c))\n#define vfmaaeq(a,b,c,d,e) \\\n  a=_mm_add_pd(a,_mm_add_pd(_mm_mul_pd(b,c),_mm_mul_pd(d,e)))\n#define vfmaseq(a,b,c,d,e) \\\n  a=_mm_add_pd(a,_mm_sub_pd(_mm_mul_pd(b,c),_mm_mul_pd(d,e)))\n#define vneg(a) _mm_xor_pd(_mm_set1_pd(-0.),a)\n#define vload(a) _mm_set1_pd(a)\n#define vload_s(a) _mm_set1_ps(a)\n#define vabs(a) _mm_andnot_pd(_mm_set1_pd(-0.),a)\n#define vsqrt(a) _mm_sqrt_pd(a)\n#define vlt(a,b) _mm_cmplt_pd(a,b)\n#define vgt(a,b) _mm_cmpgt_pd(a,b)\n#define vge(a,b) _mm_cmpge_pd(a,b)\n#define vne(a,b) _mm_cmpneq_pd(a,b)\n#define vand_mask(a,b) _mm_and_pd(a,b)\n#define vmin(a,b) _mm_min_pd(a,b)\n#define vmax(a,b) _mm_max_pd(a,b);\n#define vanyTrue(a) (_mm_movemask_pd(a)!=0)\n#define vallTrue(a) (_mm_movemask_pd(a)==3)\n#define vloadu(p) _mm_loadu_pd(p)\n#define vloadu_s(p) _mm_loadu_ps(p)\n#define vstoreu(p, v) _mm_storeu_pd(p, v)\n#define vstoreu_s(p, v) _mm_storeu_ps(p, v)\n\n#endif\n\n#if (VLEN==4)\n\n#include <immintrin.h>\n#if (USE_FMA4)\n#include <x86intrin.h>\n#endif\n\ntypedef __m256d Tv;\ntypedef __m256 Tv_s;\ntypedef __m256d Tm;\n\n#define vblend__(m,a,b) _mm256_blendv_pd(b,a,m)\n#define vzero _mm256_setzero_pd()\n#define vone _mm256_set1_pd(1.)\n\n#define vadd(a,b) _mm256_add_pd(a,b)\n#define vadd_s(a,b) _mm256_add_ps(a,b)\n#define vaddeq(a,b) a=_mm256_add_pd(a,b)\n#define vaddeq_mask(mask,a,b) a=_mm256_add_pd(a,vblend__(mask,b,vzero))\n#define vsub(a,b) _mm256_sub_pd(a,b)\n#define vsub_s(a,b) _mm256_sub_ps(a,b)\n#define vsubeq(a,b) a=_mm256_sub_pd(a,b)\n#define vsubeq_mask(mask,a,b) a=_mm256_sub_pd(a,vblend__(mask,b,vzero))\n#define vmul(a,b) _mm256_mul_pd(a,b)\n#define vmul_s(a,b) _mm256_mul_ps(a,b)\n#define vmuleq(a,b) a=_mm256_mul_pd(a,b)\n#define vmuleq_mask(mask,a,b) a=_mm256_mul_pd(a,vblend__(mask,b,vone))\n#if (USE_FMA4)\n#define vfmaeq(a,b,c) a=_mm256_macc_pd(b,c,a)\n#define vfmaeq_s(a,b,c) a=_mm256_macc_ps(b,c,a)\n#define vfmseq(a,b,c) a=_mm256_nmacc_pd(b,c,a)\n#define vfmaaeq(a,b,c,d,e) a=_mm256_macc_pd(d,e,_mm256_macc_pd(b,c,a))\n#define vfmaseq(a,b,c,d,e) a=_mm256_nmacc_pd(d,e,_mm256_macc_pd(b,c,a))\n#else\n#define vfmaeq(a,b,c) a=_mm256_add_pd(a,_mm256_mul_pd(b,c))\n#define vfmaeq_s(a,b,c) a=_mm256_add_ps(a,_mm256_mul_ps(b,c))\n#define vfmseq(a,b,c) a=_mm256_sub_pd(a,_mm256_mul_pd(b,c))\n#define vfmaaeq(a,b,c,d,e) \\\n  a=_mm256_add_pd(a,_mm256_add_pd(_mm256_mul_pd(b,c),_mm256_mul_pd(d,e)))\n#define vfmaseq(a,b,c,d,e) \\\n  a=_mm256_add_pd(a,_mm256_sub_pd(_mm256_mul_pd(b,c),_mm256_mul_pd(d,e)))\n#endif\n#define vneg(a) _mm256_xor_pd(_mm256_set1_pd(-0.),a)\n#define vload(a) _mm256_set1_pd(a)\n#define vload_s(a) _mm256_set1_ps(a)\n#define vabs(a) _mm256_andnot_pd(_mm256_set1_pd(-0.),a)\n#define vsqrt(a) _mm256_sqrt_pd(a)\n#define vlt(a,b) _mm256_cmp_pd(a,b,_CMP_LT_OQ)\n#define vgt(a,b) _mm256_cmp_pd(a,b,_CMP_GT_OQ)\n#define vge(a,b) _mm256_cmp_pd(a,b,_CMP_GE_OQ)\n#define vne(a,b) _mm256_cmp_pd(a,b,_CMP_NEQ_OQ)\n#define vand_mask(a,b) _mm256_and_pd(a,b)\n#define vmin(a,b) _mm256_min_pd(a,b)\n#define vmax(a,b) _mm256_max_pd(a,b)\n#define vanyTrue(a) (_mm256_movemask_pd(a)!=0)\n#define vallTrue(a) (_mm256_movemask_pd(a)==15)\n\n#define vloadu(p) _mm256_loadu_pd(p)\n#define vloadu_s(p) _mm256_loadu_ps(p)\n#define vstoreu(p, v) _mm256_storeu_pd(p, v)\n#define vstoreu_s(p, v) _mm256_storeu_ps(p, v)\n\n#endif\n\n#if (VLEN==8)\n\n#include <immintrin.h>\n\ntypedef __m512d Tv;\ntypedef __mmask8 Tm;\n\n#define vadd(a,b) _mm512_add_pd(a,b)\n#define vaddeq(a,b) a=_mm512_add_pd(a,b)\n#define vaddeq_mask(mask,a,b) a=_mm512_mask_add_pd(a,mask,a,b);\n#define vsub(a,b) _mm512_sub_pd(a,b)\n#define vsubeq(a,b) a=_mm512_sub_pd(a,b)\n#define vsubeq_mask(mask,a,b) a=_mm512_mask_sub_pd(a,mask,a,b);\n#define vmul(a,b) _mm512_mul_pd(a,b)\n#define vmuleq(a,b) a=_mm512_mul_pd(a,b)\n#define vmuleq_mask(mask,a,b) a=_mm512_mask_mul_pd(a,mask,a,b);\n#define vfmaeq(a,b,c) a=_mm512_fmadd_pd(b,c,a)\n#define vfmseq(a,b,c) a=_mm512_fnmadd_pd(b,c,a)\n#define vfmaaeq(a,b,c,d,e) a=_mm512_fmadd_pd(d,e,_mm512_fmadd_pd(b,c,a))\n#define vfmaseq(a,b,c,d,e) a=_mm512_fnmadd_pd(d,e,_mm512_fmadd_pd(b,c,a))\n#define vneg(a) _mm512_mul_pd(a,_mm512_set1_pd(-1.))\n#define vload(a) _mm512_set1_pd(a)\n#define vabs(a) (__m512d)_mm512_andnot_epi64((__m512i)_mm512_set1_pd(-0.),(__m512i)a)\n#define vsqrt(a) _mm512_sqrt_pd(a)\n#define vlt(a,b) _mm512_cmplt_pd_mask(a,b)\n#define vgt(a,b) _mm512_cmpnle_pd_mask(a,b)\n#define vge(a,b) _mm512_cmpnlt_pd_mask(a,b)\n#define vne(a,b) _mm512_cmpneq_pd_mask(a,b)\n#define vand_mask(a,b) ((a)&(b))\n#define vmin(a,b) _mm512_min_pd(a,b)\n#define vmax(a,b) _mm512_max_pd(a,b)\n#define vanyTrue(a) (a!=0)\n#define vallTrue(a) (a==255)\n\n#define vzero _mm512_setzero_pd()\n#define vone _mm512_set1_pd(1.)\n\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_vecutil.h",
    "content": "/*\n *  This file is part of libc_utils.\n *\n *  libc_utils is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libc_utils is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libc_utils; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libc_utils is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_vecutil.h\n *  Functionality related to vector instruction support\n *\n *  Copyright (C) 2012,2013 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef SHARP_VECUTIL_H\n#define SHARP_VECUTIL_H\n\n#ifndef VLEN\n\n#if (defined (__MIC__))\n#define VLEN 8\n#elif (defined (__AVX__))\n#define VLEN 4\n#elif (defined (__SSE2__))\n#define VLEN 2\n#else\n#define VLEN 1\n#endif\n\n#endif\n\n#if (VLEN==1)\n#define VLEN_s 1\n#else\n#define VLEN_s (2*VLEN)\n#endif\n\n#ifndef USE_FMA4\n#ifdef __FMA4__\n#define USE_FMA4 1\n#else\n#define USE_FMA4 0\n#endif\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_ylmgen_c.c",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*\n *  Helper code for efficient calculation of Y_lm(theta,phi=0)\n *\n *  Copyright (C) 2005-2014 Max-Planck-Society\n *  Author: Martin Reinecke\n */\n\n#include <math.h>\n#include <stdlib.h>\n#include \"sharp_ylmgen_c.h\"\n#include \"c_utils.h\"\n\nstatic inline void normalize (double *val, int *scale, double xfmax)\n  {\n  while (fabs(*val)>xfmax) { *val*=sharp_fsmall; ++*scale; }\n  if (*val!=0.)\n    while (fabs(*val)<xfmax*sharp_fsmall) { *val*=sharp_fbig; --*scale; }\n  }\n\nvoid sharp_Ylmgen_init (sharp_Ylmgen_C *gen, int l_max, int m_max, int spin)\n  {\n  const double inv_sqrt4pi = 0.2820947917738781434740397257803862929220;\n\n  gen->lmax = l_max;\n  gen->mmax = m_max;\n  UTIL_ASSERT(spin>=0,\"incorrect spin: must be nonnegative\");\n  UTIL_ASSERT(l_max>=spin,\"incorrect l_max: must be >= spin\");\n  UTIL_ASSERT(l_max>=m_max,\"incorrect l_max: must be >= m_max\");\n  gen->s = spin;\n  UTIL_ASSERT((sharp_minscale<=0)&&(sharp_maxscale>0),\n    \"bad value for min/maxscale\");\n  gen->cf=RALLOC(double,sharp_maxscale-sharp_minscale+1);\n  gen->cf[-sharp_minscale]=1.;\n  for (int m=-sharp_minscale-1; m>=0; --m)\n    gen->cf[m]=gen->cf[m+1]*sharp_fsmall;\n  for (int m=-sharp_minscale+1; m<(sharp_maxscale-sharp_minscale+1); ++m)\n    gen->cf[m]=gen->cf[m-1]*sharp_fbig;\n\n  gen->m = -1;\n  if (spin==0)\n    {\n    gen->rf = RALLOC(sharp_ylmgen_dbl2,gen->lmax+1);\n    gen->mfac = RALLOC(double,gen->mmax+1);\n    gen->mfac[0] = inv_sqrt4pi;\n    for (int m=1; m<=gen->mmax; ++m)\n      gen->mfac[m] = gen->mfac[m-1]*sqrt((2*m+1.)/(2*m));\n    gen->root = RALLOC(double,2*gen->lmax+5);\n    gen->iroot = RALLOC(double,2*gen->lmax+5);\n    for (int m=0; m<2*gen->lmax+5; ++m)\n      {\n      gen->root[m] = sqrt(m);\n      gen->iroot[m] = (m==0) ? 0. : 1./gen->root[m];\n      }\n    }\n  else\n    {\n    gen->m=gen->mlo=gen->mhi=-1234567890;\n    ALLOC(gen->fx,sharp_ylmgen_dbl3,gen->lmax+2);\n    for (int m=0; m<gen->lmax+2; ++m)\n      gen->fx[m].f[0]=gen->fx[m].f[1]=gen->fx[m].f[2]=0.;\n    ALLOC(gen->inv,double,gen->lmax+1);\n    gen->inv[0]=0;\n    for (int m=1; m<gen->lmax+1; ++m) gen->inv[m]=1./m;\n    ALLOC(gen->flm1,double,2*gen->lmax+1);\n    ALLOC(gen->flm2,double,2*gen->lmax+1);\n    for (int m=0; m<2*gen->lmax+1; ++m)\n      {\n      gen->flm1[m] = sqrt(1./(m+1.));\n      gen->flm2[m] = sqrt(m/(m+1.));\n      }\n    ALLOC(gen->prefac,double,gen->mmax+1);\n    ALLOC(gen->fscale,int,gen->mmax+1);\n    double *fac = RALLOC(double,2*gen->lmax+1);\n    int *facscale = RALLOC(int,2*gen->lmax+1);\n    fac[0]=1; facscale[0]=0;\n    for (int m=1; m<2*gen->lmax+1; ++m)\n      {\n      fac[m]=fac[m-1]*sqrt(m);\n      facscale[m]=facscale[m-1];\n      normalize(&fac[m],&facscale[m],sharp_fbighalf);\n      }\n    for (int m=0; m<=gen->mmax; ++m)\n      {\n      int mlo=gen->s, mhi=m;\n      if (mhi<mlo) SWAP(mhi,mlo,int);\n      double tfac=fac[2*mhi]/fac[mhi+mlo];\n      int tscale=facscale[2*mhi]-facscale[mhi+mlo];\n      normalize(&tfac,&tscale,sharp_fbighalf);\n      tfac/=fac[mhi-mlo];\n      tscale-=facscale[mhi-mlo];\n      normalize(&tfac,&tscale,sharp_fbighalf);\n      gen->prefac[m]=tfac;\n      gen->fscale[m]=tscale;\n      }\n    DEALLOC(fac);\n    DEALLOC(facscale);\n    }\n  }\n\nvoid sharp_Ylmgen_destroy (sharp_Ylmgen_C *gen)\n  {\n  DEALLOC(gen->cf);\n  if (gen->s==0)\n    {\n    DEALLOC(gen->rf);\n    DEALLOC(gen->mfac);\n    DEALLOC(gen->root);\n    DEALLOC(gen->iroot);\n    }\n  else\n    {\n    DEALLOC(gen->fx);\n    DEALLOC(gen->prefac);\n    DEALLOC(gen->fscale);\n    DEALLOC(gen->flm1);\n    DEALLOC(gen->flm2);\n    DEALLOC(gen->inv);\n    }\n  }\n\nvoid sharp_Ylmgen_prepare (sharp_Ylmgen_C *gen, int m)\n  {\n  if (m==gen->m) return;\n  UTIL_ASSERT(m>=0,\"incorrect m\");\n  gen->m = m;\n\n  if (gen->s==0)\n    {\n    gen->rf[m].f[0] = gen->root[2*m+3];\n    gen->rf[m].f[1] = 0.;\n    for (int l=m+1; l<=gen->lmax; ++l)\n      {\n      double tmp=gen->root[2*l+3]*gen->iroot[l+1+m]*gen->iroot[l+1-m];\n      gen->rf[l].f[0] = tmp*gen->root[2*l+1];\n      gen->rf[l].f[1] = tmp*gen->root[l+m]*gen->root[l-m]*gen->iroot[2*l-1];\n      }\n    }\n  else\n    {\n    int mlo_=m, mhi_=gen->s;\n    if (mhi_<mlo_) SWAP(mhi_,mlo_,int);\n    int ms_similar = ((gen->mhi==mhi_) && (gen->mlo==mlo_));\n\n    gen->mlo = mlo_; gen->mhi = mhi_;\n\n    if (!ms_similar)\n      {\n      for (int l=gen->mhi; l<gen->lmax; ++l)\n        {\n        double t = gen->flm1[l+gen->m]*gen->flm1[l-gen->m]\n                  *gen->flm1[l+gen->s]*gen->flm1[l-gen->s];\n        double lt = 2*l+1;\n        double l1 = l+1;\n        gen->fx[l+1].f[0]=l1*lt*t;\n        gen->fx[l+1].f[1]=gen->m*gen->s*gen->inv[l]*gen->inv[l+1];\n        t = gen->flm2[l+gen->m]*gen->flm2[l-gen->m]\n           *gen->flm2[l+gen->s]*gen->flm2[l-gen->s];\n        gen->fx[l+1].f[2]=t*l1*gen->inv[l];\n        }\n      }\n\n    gen->preMinus_p = gen->preMinus_m = 0;\n    if (gen->mhi==gen->m)\n      {\n      gen->cosPow = gen->mhi+gen->s; gen->sinPow = gen->mhi-gen->s;\n      gen->preMinus_p = gen->preMinus_m = ((gen->mhi-gen->s)&1);\n      }\n    else\n      {\n      gen->cosPow = gen->mhi+gen->m; gen->sinPow = gen->mhi-gen->m;\n      gen->preMinus_m = ((gen->mhi+gen->m)&1);\n      }\n    }\n  }\n\ndouble *sharp_Ylmgen_get_norm (int lmax, int spin)\n  {\n  const double pi = 3.141592653589793238462643383279502884197;\n  double *res=RALLOC(double,lmax+1);\n  /* sign convention for H=1 (LensPix paper) */\n#if 1\n   double spinsign = (spin>0) ? -1.0 : 1.0;\n#else\n   double spinsign = 1.0;\n#endif\n\n  if (spin==0)\n    {\n    for (int l=0; l<=lmax; ++l)\n      res[l]=1.;\n    return res;\n    }\n\n  spinsign = (spin&1) ? -spinsign : spinsign;\n  for (int l=0; l<=lmax; ++l)\n    res[l] = (l<spin) ? 0. : spinsign*0.5*sqrt((2*l+1)/(4*pi));\n  return res;\n  }\n\ndouble *sharp_Ylmgen_get_d1norm (int lmax)\n  {\n  const double pi = 3.141592653589793238462643383279502884197;\n  double *res=RALLOC(double,lmax+1);\n\n  for (int l=0; l<=lmax; ++l)\n    res[l] = (l<1) ? 0. : 0.5*sqrt(l*(l+1.)*(2*l+1.)/(4*pi));\n  return res;\n  }\n"
  },
  {
    "path": "external/libsharp/libsharp/sharp_ylmgen_c.h",
    "content": "/*\n *  This file is part of libsharp.\n *\n *  libsharp is free software; you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation; either version 2 of the License, or\n *  (at your option) any later version.\n *\n *  libsharp is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with libsharp; if not, write to the Free Software\n *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n/*\n *  libsharp is being developed at the Max-Planck-Institut fuer Astrophysik\n *  and financially supported by the Deutsches Zentrum fuer Luft- und Raumfahrt\n *  (DLR).\n */\n\n/*! \\file sharp_ylmgen_c.h\n *  Code for efficient calculation of Y_lm(phi=0,theta)\n *\n *  Copyright (C) 2005-2012 Max-Planck-Society\n *  \\author Martin Reinecke\n */\n\n#ifndef SHARP_YLMGEN_C_H\n#define SHARP_YLMGEN_C_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum { sharp_minscale=0, sharp_limscale=1, sharp_maxscale=1 };\nstatic const double sharp_fbig=0x1p+800,sharp_fsmall=0x1p-800;\nstatic const double sharp_ftol=0x1p-60;\nstatic const double sharp_fbighalf=0x1p+400;\n\ntypedef struct { double f[2]; } sharp_ylmgen_dbl2;\ntypedef struct { double f[3]; } sharp_ylmgen_dbl3;\n\ntypedef struct\n  {\n/* for public use; immutable during lifetime */\n  int lmax, mmax, s;\n  double *cf;\n\n/* for public use; will typically change after call to Ylmgen_prepare() */\n  int m;\n\n/* used if s==0 */\n  double *mfac;\n  sharp_ylmgen_dbl2 *rf;\n\n/* used if s!=0 */\n  int sinPow, cosPow, preMinus_p, preMinus_m;\n  double *prefac;\n  int *fscale;\n  sharp_ylmgen_dbl3 *fx;\n\n/* internal usage only */\n/* used if s==0 */\n  double *root, *iroot;\n\n/* used if s!=0 */\n  double *flm1, *flm2, *inv;\n  int mlo, mhi;\n  } sharp_Ylmgen_C;\n\n/*! Creates a generator which will calculate helper data for Y_lm calculation\n    up to \\a l=l_max and \\a m=m_max. */\nvoid sharp_Ylmgen_init (sharp_Ylmgen_C *gen, int l_max, int m_max, int spin);\n\n/*! Deallocates a generator previously initialised by Ylmgen_init(). */\nvoid sharp_Ylmgen_destroy (sharp_Ylmgen_C *gen);\n\n/*! Prepares the object for the calculation at \\a m. */\nvoid sharp_Ylmgen_prepare (sharp_Ylmgen_C *gen, int m);\n\n/*! Returns a pointer to an array with \\a lmax+1 entries containing\n    normalisation factors that must be applied to Y_lm values computed for\n    \\a spin. The array must be deallocated (using free()) by the user. */\ndouble *sharp_Ylmgen_get_norm (int lmax, int spin);\n\n/*! Returns a pointer to an array with \\a lmax+1 entries containing\n    normalisation factors that must be applied to Y_lm values computed for\n    first derivatives. The array must be deallocated (using free()) by the\n    user. */\ndouble *sharp_Ylmgen_get_d1norm (int lmax);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "external/libsharp/python/fake_pyrex/Pyrex/Distutils/__init__.py",
    "content": "# work around broken setuptools monkey patching\n"
  },
  {
    "path": "external/libsharp/python/fake_pyrex/Pyrex/Distutils/build_ext.py",
    "content": "build_ext = \"yes, it's there!\"\n"
  },
  {
    "path": "external/libsharp/python/fake_pyrex/Pyrex/__init__.py",
    "content": "# work around broken setuptools monkey patching\n"
  },
  {
    "path": "external/libsharp/python/fake_pyrex/README",
    "content": "This directory is here to fool setuptools into building .pyx files\neven if Pyrex is not installed. See ../setup.py."
  },
  {
    "path": "external/libsharp/python/libsharp/__init__.py",
    "content": "from .libsharp import *\n"
  },
  {
    "path": "external/libsharp/python/libsharp/libsharp.pxd",
    "content": "cdef extern from \"sharp.h\":\n\n    void sharp_legendre_transform_s(float *bl, float *recfac, ptrdiff_t lmax, float *x,\n                                    float *out, ptrdiff_t nx)\n    void sharp_legendre_transform(double *bl, double *recfac, ptrdiff_t lmax, double *x,\n                                  double *out, ptrdiff_t nx)\n    void sharp_legendre_transform_recfac(double *r, ptrdiff_t lmax)\n    void sharp_legendre_transform_recfac_s(float *r, ptrdiff_t lmax)\n    void sharp_legendre_roots(int n, double *x, double *w)\n\n    # sharp_lowlevel.h\n    ctypedef struct sharp_alm_info:\n      # Maximum \\a l index of the array\n      int lmax\n      # Number of different \\a m values in this object\n      int nm\n      # Array with \\a nm entries containing the individual m values\n      int *mval\n      # Combination of flags from sharp_almflags\n      int flags\n      # Array with \\a nm entries containing the (hypothetical) indices of\n      #   the coefficients with quantum numbers 0,\\a mval[i]\n      long *mvstart\n      # Stride between a_lm and a_(l+1),m\n      long stride\n\n    ctypedef struct sharp_geom_info:\n        pass\n\n    void sharp_make_alm_info (int lmax, int mmax, int stride,\n                             ptrdiff_t *mvstart, sharp_alm_info **alm_info)\n\n    void sharp_make_geom_info (int nrings, int *nph, ptrdiff_t *ofs,\n                               int *stride, double *phi0, double *theta,\n                               double *wgt, sharp_geom_info **geom_info)\n\n    void sharp_destroy_alm_info(sharp_alm_info *info)\n    void sharp_destroy_geom_info(sharp_geom_info *info)\n\n    ptrdiff_t sharp_map_size(sharp_geom_info *info)\n    ptrdiff_t sharp_alm_count(sharp_alm_info *self)\n\n\n    ctypedef enum sharp_jobtype:\n        SHARP_YtW\n        SHARP_Yt\n        SHARP_WY\n        SHARP_Y\n\n    ctypedef enum:\n        SHARP_DP\n        SHARP_ADD\n\n    void sharp_execute(sharp_jobtype type_,\n                       int spin,\n                       void *alm,\n                       void *map,\n                       sharp_geom_info *geom_info,\n                       sharp_alm_info *alm_info,\n                       int ntrans,\n                       int flags,\n                       double *time,\n                       unsigned long long *opcnt) nogil\n\n    ctypedef enum:\n        SHARP_ERROR_NO_MPI\n\n    int sharp_execute_mpi_maybe (void *pcomm, sharp_jobtype type, int spin,\n        void *alm, void *map, sharp_geom_info *geom_info,\n        sharp_alm_info *alm_info, int ntrans, int flags, double *time,\n        unsigned long long *opcnt) nogil\n\n    void sharp_normalized_associated_legendre_table(int m, int spin, int lmax, int ntheta,\n        double *theta, int theta_stride, int l_stride, int spin_stride, double *out) nogil\n\n\ncdef extern from \"sharp_geomhelpers.h\":\n    void sharp_make_subset_healpix_geom_info(\n        int nside, int stride, int nrings,\n        int *rings, double *weight, sharp_geom_info **geom_info)\n    void sharp_make_gauss_geom_info(\n        int nrings, int nphi, double phi0,\n        int stride_lon, int stride_lat, sharp_geom_info **geom_info)\n\ncdef extern from \"sharp_almhelpers.h\":\n    void sharp_make_triangular_alm_info (int lmax, int mmax, int stride,\n        sharp_alm_info **alm_info)\n    void sharp_make_rectangular_alm_info (int lmax, int mmax, int stride,\n        sharp_alm_info **alm_info)\n    void sharp_make_mmajor_real_packed_alm_info (int lmax, int stride,\n        int nm, const int *ms, sharp_alm_info **alm_info)\n\n"
  },
  {
    "path": "external/libsharp/python/libsharp/libsharp.pyx",
    "content": "import numpy as np\ncimport numpy as np\ncimport cython\n\n__all__ = ['legendre_transform', 'legendre_roots', 'sht', 'synthesis', 'adjoint_synthesis',\n           'analysis', 'adjoint_analysis', 'healpix_grid', 'triangular_order', 'rectangular_order',\n           'packed_real_order', 'normalized_associated_legendre_table']\n\n\ndef legendre_transform(x, bl, out=None):\n    if out is None:\n        out = np.empty_like(x)\n    if out.shape[0] == 0:\n        return out\n    elif x.dtype == np.float64:\n        if bl.dtype != np.float64:\n            bl = bl.astype(np.float64)\n        return _legendre_transform(x, bl, out=out)\n    elif x.dtype == np.float32:\n        if bl.dtype != np.float32:\n            bl = bl.astype(np.float32)\n        return _legendre_transform_s(x, bl, out=out)\n    else:\n        raise ValueError(\"unsupported dtype\")\n\n\ndef _legendre_transform(double[::1] x, double[::1] bl, double[::1] out):\n    if out.shape[0] != x.shape[0]:\n        raise ValueError('x and out must have same shape')\n    sharp_legendre_transform(&bl[0], NULL, bl.shape[0] - 1, &x[0], &out[0], x.shape[0])\n    return np.asarray(out)\n\n\ndef _legendre_transform_s(float[::1] x, float[::1] bl, float[::1] out):\n    if out.shape[0] != x.shape[0]:\n        raise ValueError('x and out must have same shape')\n    sharp_legendre_transform_s(&bl[0], NULL, bl.shape[0] - 1, &x[0], &out[0], x.shape[0])\n    return np.asarray(out)\n\n\ndef legendre_roots(n):\n    x = np.empty(n, np.double)\n    w = np.empty(n, np.double)\n    cdef double[::1] x_buf = x, w_buf = w\n    if not (x_buf.shape[0] == w_buf.shape[0] == n):\n        raise AssertionError()\n    if n > 0:\n        sharp_legendre_roots(n, &x_buf[0], &w_buf[0])\n    return x, w\n\n\nJOBTYPE_TO_CONST = {\n    'Y': SHARP_Y,\n    'Yt': SHARP_Yt,\n    'WY': SHARP_WY,\n    'YtW': SHARP_YtW\n}\n\ndef sht(jobtype, geom_info ginfo, alm_info ainfo, double[:, :, ::1] input,\n        int spin=0, comm=None, add=False):\n    cdef void *comm_ptr\n    cdef int flags = SHARP_DP | (SHARP_ADD if add else 0)\n    cdef int r\n    cdef sharp_jobtype jobtype_i\n    cdef double[:, :, ::1] output_buf\n    cdef int ntrans = input.shape[0]\n    cdef int ntotcomp = ntrans * input.shape[1]\n    cdef int i, j\n\n    if spin == 0 and input.shape[1] != 1:\n        raise ValueError('For spin == 0, we need input.shape[1] == 1')\n    elif spin != 0 and input.shape[1] != 2:\n        raise ValueError('For spin != 0, we need input.shape[1] == 2')\n\n\n    cdef size_t[::1] ptrbuf = np.empty(2 * ntotcomp, dtype=np.uintp)\n    cdef double **alm_ptrs = <double**>&ptrbuf[0]\n    cdef double **map_ptrs = <double**>&ptrbuf[ntotcomp]\n\n    try:\n        jobtype_i = JOBTYPE_TO_CONST[jobtype]\n    except KeyError:\n        raise ValueError('jobtype must be one of: %s' % ', '.join(sorted(JOBTYPE_TO_CONST.keys())))\n\n    if jobtype_i == SHARP_Y or jobtype_i == SHARP_WY:\n        output = np.empty((input.shape[0], input.shape[1], ginfo.local_size()), dtype=np.float64)\n        output_buf = output\n        for i in range(input.shape[0]):\n            for j in range(input.shape[1]):\n                alm_ptrs[i * input.shape[1] + j] = &input[i, j, 0]\n                map_ptrs[i * input.shape[1] + j] = &output_buf[i, j, 0]\n    else:\n        output = np.empty((input.shape[0], input.shape[1], ainfo.local_size()), dtype=np.float64)\n        output_buf = output\n        for i in range(input.shape[0]):\n            for j in range(input.shape[1]):\n                alm_ptrs[i * input.shape[1] + j] = &output_buf[i, j, 0]\n                map_ptrs[i * input.shape[1] + j] = &input[i, j, 0]\n\n    if comm is None:\n        with nogil:\n            sharp_execute (\n                jobtype_i,\n                geom_info=ginfo.ginfo, alm_info=ainfo.ainfo,\n                spin=spin, alm=alm_ptrs, map=map_ptrs,\n                ntrans=ntrans, flags=flags, time=NULL, opcnt=NULL)\n    else:\n        from mpi4py import MPI\n        if not isinstance(comm, MPI.Comm):\n            raise TypeError('comm must be an mpi4py communicator')\n        from .libsharp_mpi import _addressof\n        comm_ptr = <void*><size_t>_addressof(comm)\n        with nogil:\n            r = sharp_execute_mpi_maybe (\n                comm_ptr, jobtype_i,\n                geom_info=ginfo.ginfo, alm_info=ainfo.ainfo,\n                spin=spin, alm=alm_ptrs, map=map_ptrs,\n                ntrans=ntrans, flags=flags, time=NULL, opcnt=NULL)\n        if r == SHARP_ERROR_NO_MPI:\n            raise Exception('MPI requested, but not available')\n\n    return output\n\n\ndef synthesis(*args, **kw):\n    return sht('Y', *args, **kw)\n\ndef adjoint_synthesis(*args, **kw):\n    return sht('Yt', *args, **kw)\n\ndef analysis(*args, **kw):\n    return sht('YtW', *args, **kw)\n\ndef adjoint_analysis(*args, **kw):\n    return sht('WY', *args, **kw)\n\n\n#\n# geom_info\n#\nclass NotInitializedError(Exception):\n    pass\n\n\ncdef class geom_info:\n    cdef sharp_geom_info *ginfo\n\n    def __cinit__(self, *args, **kw):\n        self.ginfo = NULL\n\n    def local_size(self):\n        if self.ginfo == NULL:\n            raise NotInitializedError()\n        return sharp_map_size(self.ginfo)\n\n    def __dealloc__(self):\n        if self.ginfo != NULL:\n            sharp_destroy_geom_info(self.ginfo)\n        self.ginfo = NULL\n\n\ncdef class healpix_grid(geom_info):\n\n    _weight_cache = {}  # { (nside, 'T'/'Q'/'U') -> numpy array of ring weights cached from file }\n\n    def __init__(self, int nside, stride=1, int[::1] rings=None, double[::1] weights=None):\n        if weights is not None and weights.shape[0] != 2 * nside:\n            raise ValueError('weights must have length 2 * nside')\n        sharp_make_subset_healpix_geom_info(nside, stride,\n                                            nrings=4 * nside - 1 if rings is None else rings.shape[0],\n                                            rings=NULL if rings is None else &rings[0],\n                                            weight=NULL if weights is None else &weights[0],\n                                            geom_info=&self.ginfo)\n\n    @classmethod\n    def load_ring_weights(cls, nside, fields):\n        \"\"\"\n        Loads HEALPix ring weights from file. The environment variable\n        HEALPIX should be set, and this routine will look in the `data`\n        subdirectory.\n\n        Parameters\n        ----------\n\n        nside: int\n            HEALPix nside parameter\n\n        fields: tuple of str\n            Which weights to extract; pass ('T',) to only get scalar\n            weights back, or ('T', 'Q', 'U') to get all the weights\n\n        Returns\n        -------\n\n        List of NumPy arrays, according to fields parameter.\n\n        \"\"\"\n        import os\n        from astropy.io import fits\n        data_path = os.path.join(os.environ['HEALPIX'], 'data')\n        fits_field_names = {\n            'T': 'TEMPERATURE WEIGHTS',\n            'Q': 'Q-POLARISATION WEIGHTS',\n            'U': 'U-POLARISATION WEIGHTS'}\n\n        must_load = [field for field in fields if (nside, field) not in cls._weight_cache]\n\n        if must_load:\n            hdulist = fits.open(os.path.join(data_path, 'weight_ring_n%05d.fits' % nside))\n            try:\n                for field in must_load:\n                    w = hdulist[1].data.field(fits_field_names[field]).ravel().astype(np.double)\n                    w += 1\n                    cls._weight_cache[nside, field] = w\n            finally:\n                hdulist.close()\n        return [cls._weight_cache[(nside, field)].copy() for field in fields]\n\n#\n# alm_info\n#\n\n\ncdef class alm_info:\n    cdef sharp_alm_info *ainfo\n\n    def __cinit__(self, *args, **kw):\n        self.ainfo = NULL\n\n    def local_size(self):\n        if self.ainfo == NULL:\n            raise NotInitializedError()\n        return sharp_alm_count(self.ainfo)\n\n    def mval(self):\n        if self.ainfo == NULL:\n            raise NotInitializedError()\n        return np.asarray(<int[:self.ainfo.nm]> self.ainfo.mval)\n\n    def mvstart(self):\n        if self.ainfo == NULL:\n            raise NotInitializedError()\n        return np.asarray(<long[:self.ainfo.nm]> self.ainfo.mvstart)\n\n    def __dealloc__(self):\n        if self.ainfo != NULL:\n            sharp_destroy_alm_info(self.ainfo)\n        self.ainfo = NULL\n\n    @cython.boundscheck(False)\n    def almxfl(self, np.ndarray[double, ndim=3, mode='c'] alm, np.ndarray[double, ndim=2, mode='c'] fl):\n        \"\"\"Multiply Alm by a Ell based array\n\n\n        Parameters\n        ----------\n        alm : np.ndarray\n            input alm, 3 dimensions = (different signal x polarizations x lm-ordering)\n        fl : np.ndarray\n            either 1 dimension, e.g. gaussian beam, or 2 dimensions e.g. a polarized beam\n\n        Returns\n        -------\n        None, it modifies alms in-place\n\n        \"\"\"\n        cdef int mvstart = 0\n        cdef bint has_multiple_beams = alm.shape[2] > 1 and fl.shape[1] > 1\n        cdef int f, i_m, m, num_ells, i_l, i_signal, i_pol, i_mv\n\n        for i_m in range(self.ainfo.nm):\n            m = self.ainfo.mval[i_m]\n            f = 1 if (m==0) else 2\n            num_ells = self.ainfo.lmax + 1 - m\n\n            if not has_multiple_beams:\n                for i_signal in range(alm.shape[0]):\n                    for i_pol in range(alm.shape[1]):\n                        for i_l in range(num_ells):\n                            l = m + i_l\n                            for i_mv in range(mvstart + f*i_l, mvstart + f*i_l +f):\n                                alm[i_signal, i_pol, i_mv] *= fl[l, 0]\n            else:\n                for i_signal in range(alm.shape[0]):\n                    for i_pol in range(alm.shape[1]):\n                        for i_l in range(num_ells):\n                            l = m + i_l\n                            for i_mv in range(mvstart + f*i_l, mvstart + f*i_l +f):\n                                alm[i_signal, i_pol, i_mv] *= fl[l, i_pol]\n            mvstart += f * num_ells\n\ncdef class triangular_order(alm_info):\n    def __init__(self, int lmax, mmax=None, stride=1):\n        mmax = mmax if mmax is not None else lmax\n        sharp_make_triangular_alm_info(lmax, mmax, stride, &self.ainfo)\n\n\ncdef class rectangular_order(alm_info):\n    def __init__(self, int lmax, mmax=None, stride=1):\n        mmax = mmax if mmax is not None else lmax\n        sharp_make_rectangular_alm_info(lmax, mmax, stride, &self.ainfo)\n\n\ncdef class packed_real_order(alm_info):\n    def __init__(self, int lmax, stride=1, int[::1] ms=None):\n        sharp_make_mmajor_real_packed_alm_info(lmax=lmax, stride=stride,\n                                               nm=lmax + 1 if ms is None else ms.shape[0],\n                                               ms=NULL if ms is None else &ms[0],\n                                               alm_info=&self.ainfo)\n\n#\n# \n#\n\n@cython.boundscheck(False)\ndef normalized_associated_legendre_table(int lmax, int m, theta):\n    cdef double[::1] theta_ = np.ascontiguousarray(theta, dtype=np.double)\n    out = np.zeros((theta_.shape[0], lmax - m + 1), np.double)\n    cdef double[:, ::1] out_ = out\n    if lmax < m:\n        raise ValueError(\"lmax < m\")\n    with nogil:\n        sharp_normalized_associated_legendre_table(m, 0, lmax, theta_.shape[0], &theta_[0], lmax - m + 1, 1, 1, &out_[0,0])\n    return out\n"
  },
  {
    "path": "external/libsharp/python/libsharp/libsharp_mpi.pyx",
    "content": "cdef extern from \"mpi.h\":\n    ctypedef void *MPI_Comm\n\ncdef extern from \"Python.h\":\n    object PyLong_FromVoidPtr(void*)\n\ncdef extern:\n    ctypedef class mpi4py.MPI.Comm [object PyMPICommObject]:\n        cdef MPI_Comm ob_mpi\n        cdef unsigned flags\n\n# For compatibility with mpi4py <= 1.3.1\n# Newer versions could use the MPI._addressof function\ndef _addressof(Comm comm):\n    cdef void *ptr = NULL\n    ptr = <void*>&comm.ob_mpi\n    return PyLong_FromVoidPtr(ptr)\n"
  },
  {
    "path": "external/libsharp/python/libsharp/tests/__init__.py",
    "content": "# empty\n"
  },
  {
    "path": "external/libsharp/python/libsharp/tests/test_legendre.py",
    "content": "import numpy as np\nfrom scipy.special import legendre\nfrom scipy.special import p_roots\nimport libsharp\n\nfrom numpy.testing import assert_allclose\n\n\ndef check_legendre_transform(lmax, ntheta):\n    l = np.arange(lmax + 1)\n    if lmax >= 1:\n        sigma = -np.log(1e-3) / lmax / (lmax + 1)\n        bl = np.exp(-sigma*l*(l+1))\n        bl *= (2 * l + 1)\n    else:\n        bl = np.asarray([1], dtype=np.double)\n\n    theta = np.linspace(0, np.pi, ntheta, endpoint=True)\n    x = np.cos(theta)\n\n    # Compute truth using scipy.special.legendre\n    P = np.zeros((ntheta, lmax + 1))\n    for l in range(lmax + 1):\n        P[:, l] = legendre(l)(x)\n    y0 = np.dot(P, bl)\n\n\n    # double-precision\n    y = libsharp.legendre_transform(x, bl)\n\n    assert_allclose(y, y0, rtol=1e-12, atol=1e-12)\n\n    # single-precision\n    y32 = libsharp.legendre_transform(x.astype(np.float32), bl)\n    assert_allclose(y, y0, rtol=1e-5, atol=1e-5)\n\n\ndef test_legendre_transform():\n    nthetas_to_try = [0, 9, 17, 19] + list(np.random.randint(500, size=20))\n    for ntheta in nthetas_to_try:\n        for lmax in [0, 1, 2, 3, 20] + list(np.random.randint(50, size=4)):\n            yield check_legendre_transform, lmax, ntheta\n\ndef check_legendre_roots(n):\n    xs, ws = ([], []) if n == 0 else p_roots(n) # from SciPy\n    xl, wl = libsharp.legendre_roots(n)\n    assert_allclose(xs, xl, rtol=1e-14, atol=1e-14)\n    assert_allclose(ws, wl, rtol=1e-14, atol=1e-14)\n\ndef test_legendre_roots():\n    \"\"\"\n    Test the Legendre root-finding algorithm from libsharp by comparing it with\n    the SciPy version.\n    \"\"\"\n    yield check_legendre_roots, 0\n    yield check_legendre_roots, 1\n    yield check_legendre_roots, 32\n    yield check_legendre_roots, 33\n"
  },
  {
    "path": "external/libsharp/python/libsharp/tests/test_legendre_table.py",
    "content": "from __future__ import print_function\nimport numpy as np\n\nfrom numpy.testing import assert_almost_equal\nfrom nose.tools import eq_, ok_\n\nfrom libsharp import normalized_associated_legendre_table\nfrom scipy.special import sph_harm, p_roots\n\ndef test_compare_legendre_table_with_scipy():\n    def test(theta, m, lmax):\n        Plm = normalized_associated_legendre_table(lmax, m, theta)\n\n        Plm_p = sph_harm(m, np.arange(m, lmax + 1), 0, theta)[None, :]\n        if not np.allclose(Plm_p, Plm):\n            print(Plm_p)\n            print(Plm)\n        return ok_, np.allclose(Plm_p, Plm)\n\n    yield test(np.pi/2, 0, 10)\n    yield test(np.pi/4, 0, 10)\n    yield test(3 * np.pi/4, 0, 10)\n    yield test(np.pi/4, 1, 4)\n    yield test(np.pi/4, 2, 4)\n    yield test(np.pi/4, 50, 50)\n    yield test(np.pi/2, 49, 50)\n\n\ndef test_legendre_table_wrapper_logic():\n    # tests the SSE 2 logic in the high-level wrapper by using an odd number of thetas\n    theta = np.asarray([np.pi/2, np.pi/4, 3 * np.pi / 4])\n    m = 3\n    lmax = 10\n    Plm = normalized_associated_legendre_table(lmax, m, theta)\n    assert np.allclose(Plm[1, :], normalized_associated_legendre_table(lmax, m, np.pi/4)[0, :])\n    assert np.allclose(Plm[2, :], normalized_associated_legendre_table(lmax, m, 3 * np.pi/4)[0, :])\n"
  },
  {
    "path": "external/libsharp/python/libsharp/tests/test_sht.py",
    "content": "import numpy as np\nfrom numpy.testing import assert_allclose\nimport libsharp\n\nfrom mpi4py import MPI\n\n\ndef test_basic():\n    lmax = 10\n    nside = 8\n    rank = MPI.COMM_WORLD.Get_rank()\n    ms = np.arange(rank, lmax + 1, MPI.COMM_WORLD.Get_size(), dtype=np.int32)\n    \n    order = libsharp.packed_real_order(lmax, ms=ms)\n    grid = libsharp.healpix_grid(nside)\n\n    \n    alm = np.zeros(order.local_size())\n    if rank == 0:\n        alm[0] = 1\n    elif rank == 1:\n        alm[0] = 1\n\n\n    map = libsharp.synthesis(grid, order, np.repeat(alm[None, None, :], 3, 0), comm=MPI.COMM_WORLD)\n    assert np.all(map[2, :] == map[1, :]) and np.all(map[1, :] == map[0, :])\n    map = map[0, 0, :]\n    print(rank, \"shape\", map.shape)\n    print(rank, \"mean\", map.mean())\n\nif __name__==\"__main__\":\n    test_basic()\n"
  },
  {
    "path": "external/libsharp/python/libsharp/tests/test_smoothing_noise_pol_mpi.py",
    "content": "# This test needs to be run with:\n\n# mpirun -np X python test_smoothing_noise_pol_mpi.py\n\nfrom mpi4py import MPI\n\nimport numpy as np\n\nimport healpy as hp\n\nimport libsharp\n\nmpi = True\nrank = MPI.COMM_WORLD.Get_rank()\n\nnside = 256\nnpix = hp.nside2npix(nside)\n\nnp.random.seed(100)\ninput_map = np.random.normal(size=(3, npix))\nfwhm_deg = 10\nlmax = 512\n\nnrings = 4 * nside - 1  # four missing pixels\n\nif rank == 0:\n    print(\"total rings\", nrings)\n\nn_mpi_processes = MPI.COMM_WORLD.Get_size()\nrings_per_process = nrings // n_mpi_processes + 1\n# ring indices are 1-based\n\nring_indices_emisphere = np.arange(2*nside, dtype=np.int32) + 1\nlocal_ring_indices = ring_indices_emisphere[rank::n_mpi_processes]\n\n# to improve performance, simmetric rings north/south need to be in the same rank\n# therefore we use symmetry to create the full ring indexing\n\nif local_ring_indices[-1] == 2 * nside:\n    # has equator ring\n    local_ring_indices = np.concatenate(\n      [local_ring_indices[:-1],\n       nrings - local_ring_indices[::-1] + 1]\n    )\nelse:\n    # does not have equator ring\n    local_ring_indices = np.concatenate(\n      [local_ring_indices,\n       nrings - local_ring_indices[::-1] + 1]\n    )\n\nprint(\"rank\", rank, \"n_rings\", len(local_ring_indices))\n\nif not mpi:\n    local_ring_indices = None\ngrid = libsharp.healpix_grid(nside, rings=local_ring_indices)\n\n# returns start index of the ring and number of pixels\nstartpix, ringpix, _, _, _ = hp.ringinfo(nside, local_ring_indices.astype(np.int64))\n\nlocal_npix = grid.local_size()\n\ndef expand_pix(startpix, ringpix, local_npix):\n    \"\"\"Turn first pixel index and number of pixel in full array of pixels\n\n    to be optimized with cython or numba\n    \"\"\"\n    local_pix = np.empty(local_npix, dtype=np.int64)\n    i = 0\n    for start, num in zip(startpix, ringpix):\n        local_pix[i:i+num] = np.arange(start, start+num)\n        i += num\n    return local_pix\n\nlocal_pix = expand_pix(startpix, ringpix, local_npix)\n\nlocal_map = input_map[:, local_pix]\n\nlocal_hitmap = np.zeros(npix)\nlocal_hitmap[local_pix] = 1\nhp.write_map(\"hitmap_{}.fits\".format(rank), local_hitmap, overwrite=True)\n\nprint(\"rank\", rank, \"npix\", npix, \"local_npix\", local_npix, \"local_map len\", len(local_map), \"unique pix\", len(np.unique(local_pix)))\n\nlocal_m_indices = np.arange(rank, lmax + 1, MPI.COMM_WORLD.Get_size(), dtype=np.int32)\nif not mpi:\n    local_m_indices = None\n\norder = libsharp.packed_real_order(lmax, ms=local_m_indices) \nlocal_nl = order.local_size()\nprint(\"rank\", rank, \"local_nl\", local_nl, \"mval\", order.mval())\n\nmpi_comm = MPI.COMM_WORLD if mpi else None\n\n# map2alm\n# maps in libsharp are 3D, 2nd dimension is IQU, 3rd is pixel\n\nalm_sharp_I = libsharp.analysis(grid, order,\n                                np.ascontiguousarray(local_map[0].reshape((1, 1, -1))),\n                                spin=0, comm=mpi_comm)\nalm_sharp_P = libsharp.analysis(grid, order,\n                                np.ascontiguousarray(local_map[1:].reshape((1, 2, -1))),\n                                spin=2, comm=mpi_comm)\n\nbeam = hp.gauss_beam(fwhm=np.radians(fwhm_deg), lmax=lmax, pol=True)\n\nprint(\"Smooth\")\n# smooth in place (zonca implemented this function)\norder.almxfl(alm_sharp_I, np.ascontiguousarray(beam[:, 0:1]))\norder.almxfl(alm_sharp_P, np.ascontiguousarray(beam[:, (1, 2)]))\n\n# alm2map\n\nnew_local_map_I = libsharp.synthesis(grid, order, alm_sharp_I, spin=0, comm=mpi_comm)\nnew_local_map_P = libsharp.synthesis(grid, order, alm_sharp_P, spin=2, comm=mpi_comm)\n\n# Transfer map to first process for writing\n\nlocal_full_map = np.zeros(input_map.shape, dtype=np.float64)\nlocal_full_map[0, local_pix] = new_local_map_I\nlocal_full_map[1:, local_pix] = new_local_map_P\n\noutput_map = np.zeros(input_map.shape, dtype=np.float64) if rank == 0 else None\nmpi_comm.Reduce(local_full_map, output_map, root=0, op=MPI.SUM)\n\nif rank == 0:\n    # hp.write_map(\"sharp_smoothed_map.fits\", output_map, overwrite=True)\n    # hp_smoothed = hp.alm2map(hp.map2alm(input_map, lmax=lmax), nside=nside) # transform only\n    hp_smoothed = hp.smoothing(input_map, fwhm=np.radians(fwhm_deg), lmax=lmax)\n    std_diff = (hp_smoothed-output_map).std()\n    print(\"Std of difference between libsharp and healpy\", std_diff)\n    # hp.write_map(\n    #     \"healpy_smoothed_map.fits\",\n    #     hp_smoothed,\n    #     overwrite=True\n    # )\n    assert std_diff < 1e-5\n"
  },
  {
    "path": "external/libsharp/python/setup.py",
    "content": "#! /usr/bin/env python\n\ndescr   = \"\"\"Spherical Harmionic transforms package\n\nPython API for the libsharp spherical harmonic transforms library\n\"\"\"\n\nimport os\nimport sys\n\nDISTNAME            = 'libsharp'\nDESCRIPTION         = 'libsharp library for fast Spherical Harmonic Transforms'\nLONG_DESCRIPTION    = descr\nMAINTAINER          = 'Dag Sverre Seljebotn',\nMAINTAINER_EMAIL    = 'd.s.seljebotn@astro.uio.no',\nURL                 = 'http://sourceforge.net/projects/libsharp/'\nLICENSE             = 'GPL'\nDOWNLOAD_URL        = \"http://sourceforge.net/projects/libsharp/\"\nVERSION             = '0.1'\n\n# Add our fake Pyrex at the end of the Python search path\n# in order to fool setuptools into allowing compilation of\n# pyx files to C files. Importing Cython.Distutils then\n# makes Cython the tool of choice for this rather than\n# (the possibly nonexisting) Pyrex.\nproject_path = os.path.split(__file__)[0]\nsys.path.append(os.path.join(project_path, 'fake_pyrex'))\n\nfrom setuptools import setup, find_packages, Extension\nfrom Cython.Build import cythonize\nimport numpy as np\n\nlibsharp = os.environ.get('LIBSHARP', None)\nlibsharp_include = os.environ.get('LIBSHARP_INCLUDE', libsharp and os.path.join(libsharp, 'include'))\nlibsharp_lib = os.environ.get('LIBSHARP_LIB', libsharp and os.path.join(libsharp, 'lib'))\n\nif libsharp_include is None or libsharp_lib is None:\n    sys.stderr.write('Please set LIBSHARP environment variable to the install directly of libsharp, '\n                     'this script will refer to the lib and include sub-directories. Alternatively '\n                     'set LIBSHARP_INCLUDE and LIBSHARP_LIB\\n')\n    sys.exit(1)\n\nif __name__ == \"__main__\":\n    setup(install_requires = ['numpy'],\n          packages = find_packages(),\n          test_suite=\"nose.collector\",\n          # Well, technically zipping the package will work, but since it's\n          # all compiled code it'll just get unzipped again at runtime, which\n          # is pointless:\n          zip_safe = False,\n          name = DISTNAME,\n          version = VERSION,\n          maintainer = MAINTAINER,\n          maintainer_email = MAINTAINER_EMAIL,\n          description = DESCRIPTION,\n          license = LICENSE,\n          url = URL,\n          download_url = DOWNLOAD_URL,\n          long_description = LONG_DESCRIPTION,\n          classifiers =\n            [ 'Development Status :: 3 - Alpha',\n              'Environment :: Console',\n              'Intended Audience :: Developers',\n              'Intended Audience :: Science/Research',\n              'License :: OSI Approved :: GNU General Public License (GPL)',\n              'Topic :: Scientific/Engineering'],\n          ext_modules = cythonize([\n              Extension(\"libsharp.libsharp\",\n                        [\"libsharp/libsharp.pyx\"],\n                        libraries=[\"sharp\", \"fftpack\", \"c_utils\"],\n                        include_dirs=[libsharp_include, np.get_include()],\n                        library_dirs=[libsharp_lib],\n                        extra_link_args=[\"-fopenmp\"],\n              ),\n              Extension(\"libsharp.libsharp_mpi\",\n                        [\"libsharp/libsharp_mpi.pyx\"],\n                        libraries=[\"sharp\", \"fftpack\", \"c_utils\"],\n                        include_dirs=[libsharp_include, np.get_include()],\n                        library_dirs=[libsharp_lib],\n                        extra_link_args=[\"-fopenmp\"],\n              ),\n              ]),\n          )\n"
  },
  {
    "path": "external/libsharp/runjinja.py",
    "content": "#!/usr/bin/env python\n\n\"\"\"\nPreprocesses foo.c.in to foo.c. Reads STDIN and writes STDOUT.\n\"\"\"\n\nimport sys\nimport hashlib\nfrom jinja2 import Template, Environment\n\nenv = Environment(block_start_string='/*{',\n                  block_end_string='}*/',\n                  variable_start_string='{{',\n                  variable_end_string='}}')\n\nextra_vars = dict(len=len)\ninput = sys.stdin.read()\nsys.stdout.write('/* DO NOT EDIT. md5sum of source: %s */' % hashlib.md5(input.encode()).hexdigest())\nsys.stdout.write(env.from_string(input).render(**extra_vars))\n"
  },
  {
    "path": "external/slatec/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY slatec)\n\nset(LIBRARY_SOURCES\n    drc3jj.f\n    )\n\nadd_library(${LIBRARY} ${LIBRARY_SOURCES})\ntarget_compile_options(${LIBRARY} PRIVATE -std=legacy)\n\nif(SPECTRE_Fortran_STATIC_LIBS)\n  if (NOT $gfortran)\n    find_library(gfortran NAMES libgfortran.a)\n  endif()\n  if (NOT $quadmath)\n    find_library(quadmath NAMES libquadmath.a)\n  endif()\n  target_link_libraries(\n    ${LIBRARY}\n    PRIVATE\n    ${gfortran}\n    ${quadmath}\n  )\nendif()\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  SpectreFlags\n)\n"
  },
  {
    "path": "external/slatec/LICENSE.txt",
    "content": "SLATEC Common Mathematical Library, Version 4.1, July 1993\na comprehensive software library containing over\n1400 general purpose mathematical and statistical routines\nwritten in Fortran 77.\n\nThe Library is in the public domain and distributed by the Energy Science\nand Technology Software Center.\n \n               Energy Science and Technology Software Center\n               P.O. Box 1020\n               Oak Ridge, TN  37831\n \n               Telephone  615-576-2606\n               E-mail  estsc%a1.adonis.mrouter@zeus.osti.gov\n \nThe library is available at https://www.netlib.org/slatec/src\n"
  },
  {
    "path": "external/slatec/README.txt",
    "content": "These are routines from the public domain slatec library,\nhttps://www.netlib.org/slatec\n\nWe include only routines that we use, not the entire library.\n"
  },
  {
    "path": "external/slatec/drc3jj.f",
    "content": "*DECK DRC3JJ\n      SUBROUTINE DRC3JJ (L2, L3, M2, M3, L1MIN, L1MAX, THRCOF, NDIM,\n     +   IER,DBLMAX)\nC***BEGIN PROLOGUE  DRC3JJ\nC***PURPOSE  Evaluate the 3j symbol f(L1) = (  L1   L2 L3)\nC                                           (-M2-M3 M2 M3)\nC            for all allowed values of L1, the other parameters\nC            being held fixed.\nC***LIBRARY   SLATEC\nC***CATEGORY  C19\nC***TYPE      DOUBLE PRECISION (RC3JJ-S, DRC3JJ-D)\nC***KEYWORDS  3J COEFFICIENTS, 3J SYMBOLS, CLEBSCH-GORDAN COEFFICIENTS,\nC             RACAH COEFFICIENTS, VECTOR ADDITION COEFFICIENTS,\nC             WIGNER COEFFICIENTS\nC***AUTHOR  Gordon, R. G., Harvard University\nC           Schulten, K., Max Planck Institute\nC***DESCRIPTION\nC\nC *Usage:\nC\nC        DOUBLE PRECISION L2, L3, M2, M3, L1MIN, L1MAX, THRCOF(NDIM)\nC        INTEGER NDIM, IER\nC\nC        CALL DRC3JJ (L2, L3, M2, M3, L1MIN, L1MAX, THRCOF, NDIM, IER)\nC\nC *Arguments:\nC\nC     L2 :IN      Parameter in 3j symbol.\nC\nC     L3 :IN      Parameter in 3j symbol.\nC\nC     M2 :IN      Parameter in 3j symbol.\nC\nC     M3 :IN      Parameter in 3j symbol.\nC\nC     L1MIN :OUT  Smallest allowable L1 in 3j symbol.\nC\nC     L1MAX :OUT  Largest allowable L1 in 3j symbol.\nC\nC     THRCOF :OUT Set of 3j coefficients generated by evaluating the\nC                 3j symbol for all allowed values of L1.  THRCOF(I)\nC                 will contain f(L1MIN+I-1), I=1,2,...,L1MAX+L1MIN+1.\nC\nC     NDIM :IN    Declared length of THRCOF in calling program.\nC\nC     IER :OUT    Error flag.\nC                 IER=0 No errors.\nC                 IER=1 Either L2.LT.ABS(M2) or L3.LT.ABS(M3).\nC                 IER=2 Either L2+ABS(M2) or L3+ABS(M3) non-integer.\nC                 IER=3 L1MAX-L1MIN not an integer.\nC                 IER=4 L1MAX less than L1MIN.\nC                 IER=5 NDIM less than L1MAX-L1MIN+1.\nC\nC *Description:\nC\nC     Although conventionally the parameters of the vector addition\nC  coefficients satisfy certain restrictions, such as being integers\nC  or integers plus 1/2, the restrictions imposed on input to this\nC  subroutine are somewhat weaker. See, for example, Section 27.9 of\nC  Abramowitz and Stegun or Appendix C of Volume II of A. Messiah.\nC  The restrictions imposed by this subroutine are\nC       1. L2 .GE. ABS(M2) and L3 .GE. ABS(M3);\nC       2. L2+ABS(M2) and L3+ABS(M3) must be integers;\nC       3. L1MAX-L1MIN must be a non-negative integer, where\nC          L1MAX=L2+L3 and L1MIN=MAX(ABS(L2-L3),ABS(M2+M3)).\nC  If the conventional restrictions are satisfied, then these\nC  restrictions are met.\nC\nC     The user should be cautious in using input parameters that do\nC  not satisfy the conventional restrictions. For example, the\nC  the subroutine produces values of\nC       f(L1) = ( L1  2.5  5.8)\nC               (-0.3 1.5 -1.2)\nC  for L1=3.3,4.3,...,8.3 but none of the symmetry properties of the 3j\nC  symbol, set forth on page 1056 of Messiah, is satisfied.\nC\nC     The subroutine generates f(L1MIN), f(L1MIN+1), ..., f(L1MAX)\nC  where L1MIN and L1MAX are defined above. The sequence f(L1) is\nC  generated by a three-term recurrence algorithm with scaling to\nC  control overflow. Both backward and forward recurrence are used to\nC  maintain numerical stability. The two recurrence sequences are\nC  matched at an interior point and are normalized from the unitary\nC  property of 3j coefficients and Wigner's phase convention.\nC\nC    The algorithm is suited to applications in which large quantum\nC  numbers arise, such as in molecular dynamics.\nC\nC***REFERENCES  1. Abramowitz, M., and Stegun, I. A., Eds., Handbook\nC                  of Mathematical Functions with Formulas, Graphs\nC                  and Mathematical Tables, NBS Applied Mathematics\nC                  Series 55, June 1964 and subsequent printings.\nC               2. Messiah, Albert., Quantum Mechanics, Volume II,\nC                  North-Holland Publishing Company, 1963.\nC               3. Schulten, Klaus and Gordon, Roy G., Exact recursive\nC                  evaluation of 3j and 6j coefficients for quantum-\nC                  mechanical coupling of angular momenta, J Math\nC                  Phys, v 16, no. 10, October 1975, pp. 1961-1970.\nC               4. Schulten, Klaus and Gordon, Roy G., Semiclassical\nC                  approximations to 3j  and 6j coefficients for\nC                  quantum-mechanical coupling of angular momenta,\nC                  J Math Phys, v 16, no. 10, October 1975,\nC                  pp. 1971-1988.\nC               5. Schulten, Klaus and Gordon, Roy G., Recursive\nC                  evaluation of 3j and 6j coefficients, Computer\nC                  Phys Comm, v 11, 1976, pp. 269-278.\nC***REVISION HISTORY  (YYMMDD)\nC   750101  DATE WRITTEN\nC   880515  SLATEC prologue added by G. C. Nielson, NBS; parameters\nC           HUGE and TINY revised to depend on D1MACH.\nC   891229  Prologue description rewritten; other prologue sections\nC           revised; LMATCH (location of match point for recurrences)\nC           removed from argument list; argument IER changed to serve\nC           only as an error flag (previously, in cases without error,\nC           it returned the number of scalings); number of error codes\nC           increased to provide more precise error information;\nC           program comments revised; SLATEC error handler calls\nC           introduced to enable printing of error messages to meet\nC           SLATEC standards. These changes were done by D. W. Lozier,\nC           M. A. McClain and J. M. Smith of the National Institute\nC           of Standards and Technology, formerly NBS.\nC   910415  Mixed type expressions eliminated; variable C1 initialized;\nC           description of THRCOF expanded. These changes were done by\nC           D. W. Lozier.\nC***END PROLOGUE  DRC3JJ\nC\n      implicit none\n      INTEGER NDIM, IER\n      DOUBLE PRECISION L2, L3, M2, M3, L1MIN, L1MAX, DBLMAX,THRCOF(NDIM)\nC\n      INTEGER I, INDEX, LSTEP, N, NFIN, NFINP1, NFINP2, NFINP3, NLIM,\n     +        NSTEP2\n      DOUBLE PRECISION A1, A1S, A2, A2S, C1, C1OLD, C2, CNORM,\n     +                 DENOM, DV, EPS, HUGE, L1, M1, NEWFAC, OLDFAC,\n     +                 ONE, RATIO, SIGN1, SIGN2, SRHUGE, SRTINY, SUM1,\n     +                 SUM2, SUMBAC, SUMFOR, SUMUNI, THREE, THRESH,\n     +                 TINY, TWO, X, X1, X2, X3, Y, Y1, Y2, Y3, ZERO\nC\n      DATA  ZERO,EPS,ONE,TWO,THREE /0.0D0,0.01D0,1.0D0,2.0D0,3.0D0/\nC\nC***FIRST EXECUTABLE STATEMENT  DRC3JJ\n      IER=0\nC  HUGE is the square root of one twentieth of the largest floating\nC  point number, approximately.\n      HUGE = SQRT(DBLMAX/20.0D0)\n      SRHUGE = SQRT(HUGE)\n      TINY = 1.0D0/HUGE\n      SRTINY = 1.0D0/SRHUGE\nC\nC     LMATCH = ZERO\n      M1 = - M2 - M3\nC\nC  Check error conditions 1 and 2.\n      IF((L2-ABS(M2)+EPS.LT.ZERO).OR.\n     +   (L3-ABS(M3)+EPS.LT.ZERO))THEN\n         IER=1\n         RETURN\n      ELSEIF((MOD(L2+ABS(M2)+EPS,ONE).GE.EPS+EPS).OR.\n     +   (MOD(L3+ABS(M3)+EPS,ONE).GE.EPS+EPS))THEN\n         IER=2\n         RETURN\n      ENDIF\nC\nC\nC\nC  Limits for L1\nC\n      L1MIN = MAX(ABS(L2-L3),ABS(M1))\n      L1MAX = L2 + L3\nC\nC  Check error condition 3.\n      IF(MOD(L1MAX-L1MIN+EPS,ONE).GE.EPS+EPS)THEN\n         IER=3\n         RETURN\n      ENDIF\n      IF(L1MIN.LT.L1MAX-EPS)   GO TO 20\n      IF(L1MIN.LT.L1MAX+EPS)   GO TO 10\nC\nC  Check error condition 4.\n      IER=4\n      RETURN\nC\nC  This is reached in case that L1 can take only one value,\nC  i.e. L1MIN = L1MAX\nC\n   10 CONTINUE\nC     LSCALE = 0\n      THRCOF(1) = (-ONE) ** INT(ABS(L2+M2-L3+M3)+EPS) /\n     1 SQRT(L1MIN + L2 + L3 + ONE)\n      RETURN\nC\nC  This is reached in case that L1 takes more than one value,\nC  i.e. L1MIN < L1MAX.\nC\n   20 CONTINUE\nC     LSCALE = 0\n      NFIN = INT(L1MAX-L1MIN+ONE+EPS)\n      IF(NDIM-NFIN)  21, 23, 23\nC\nC  Check error condition 5.\n   21 IER = 5\n      RETURN\nC\nC\nC  Starting forward recursion from L1MIN taking NSTEP1 steps\nC\n   23 L1 = L1MIN\n      NEWFAC = 0.0D0\n      C1 = 0.0D0\n      THRCOF(1) = SRTINY\n      SUM1 = (L1+L1+ONE) * TINY\nC\nC\n      LSTEP = 1\n   30 LSTEP = LSTEP + 1\n      L1 = L1 + ONE\nC\nC\n      OLDFAC = NEWFAC\n      A1 = (L1+L2+L3+ONE) * (L1-L2+L3) * (L1+L2-L3) * (-L1+L2+L3+ONE)\n      A2 = (L1+M1) * (L1-M1)\n      NEWFAC = SQRT(A1*A2)\n      IF(L1.LT.ONE+EPS)   GO TO 40\nC\nC\n      DV = - L2*(L2+ONE) * M1 + L3*(L3+ONE) * M1 + L1*(L1-ONE) * (M3-M2)\n      DENOM = (L1-ONE) * NEWFAC\nC\n      IF(LSTEP-2)  32, 32, 31\nC\n   31 C1OLD = ABS(C1)\n   32 C1 = - (L1+L1-ONE) * DV / DENOM\n      GO TO 50\nC\nC  If L1 = 1, (L1-1) has to be factored out of DV, hence\nC\n   40 C1 = - (L1+L1-ONE) * L1 * (M3-M2) / NEWFAC\nC\n   50 IF(LSTEP.GT.2)   GO TO 60\nC\nC\nC  If L1 = L1MIN + 1, the third term in the recursion equation vanishes,\nC  hence\n      X = SRTINY * C1\n      THRCOF(2) = X\n      SUM1 = SUM1 + TINY * (L1+L1+ONE) * C1*C1\n      IF(LSTEP.EQ.NFIN)   GO TO 220\n      GO TO 30\nC\nC\n   60 C2 = - L1 * OLDFAC / DENOM\nC\nC  Recursion to the next 3j coefficient X\nC\n      X = C1 * THRCOF(LSTEP-1) + C2 * THRCOF(LSTEP-2)\n      THRCOF(LSTEP) = X\n      SUMFOR = SUM1\n      SUM1 = SUM1 + (L1+L1+ONE) * X*X\n      IF(LSTEP.EQ.NFIN)   GO TO 100\nC\nC  See if last unnormalized 3j coefficient exceeds SRHUGE\nC\n      IF(ABS(X).LT.SRHUGE)   GO TO 80\nC\nC  This is reached if last 3j coefficient larger than SRHUGE,\nC  so that the recursion series THRCOF(1), ... , THRCOF(LSTEP)\nC  has to be rescaled to prevent overflow\nC\nC     LSCALE = LSCALE + 1\n      DO 70 I=1,LSTEP\n      IF(ABS(THRCOF(I)).LT.SRTINY)   THRCOF(I) = ZERO\n   70 THRCOF(I) = THRCOF(I) / SRHUGE\n      SUM1 = SUM1 / HUGE\n      SUMFOR = SUMFOR / HUGE\n      X = X / SRHUGE\nC\nC  As long as ABS(C1) is decreasing, the recursion proceeds towards\nC  increasing 3j values and, hence, is numerically stable.  Once\nC  an increase of ABS(C1) is detected, the recursion direction is\nC  reversed.\nC\n   80 IF(C1OLD-ABS(C1))   100, 100, 30\nC\nC\nC  Keep three 3j coefficients around LMATCH for comparison with\nC  backward recursion.\nC\n  100 CONTINUE\nC     LMATCH = L1 - 1\n      X1 = X\n      X2 = THRCOF(LSTEP-1)\n      X3 = THRCOF(LSTEP-2)\n      NSTEP2 = NFIN - LSTEP + 3\nC\nC\nC\nC\nC  Starting backward recursion from L1MAX taking NSTEP2 steps, so\nC  that forward and backward recursion overlap at three points\nC  L1 = LMATCH+1, LMATCH, LMATCH-1.\nC\n      NFINP1 = NFIN + 1\n      NFINP2 = NFIN + 2\n      NFINP3 = NFIN + 3\n      L1 = L1MAX\n      THRCOF(NFIN) = SRTINY\n      SUM2 = TINY * (L1+L1+ONE)\nC\n      L1 = L1 + TWO\n      LSTEP = 1\n  110 LSTEP = LSTEP + 1\n      L1 = L1 - ONE\nC\n      OLDFAC = NEWFAC\n      A1S = (L1+L2+L3)*(L1-L2+L3-ONE)*(L1+L2-L3-ONE)*(-L1+L2+L3+TWO)\n      A2S = (L1+M1-ONE) * (L1-M1-ONE)\n      NEWFAC = SQRT(A1S*A2S)\nC\n      DV = - L2*(L2+ONE) * M1 + L3*(L3+ONE) * M1 + L1*(L1-ONE) * (M3-M2)\nC\n      DENOM = L1 * NEWFAC\n      C1 = - (L1+L1-ONE) * DV / DENOM\n      IF(LSTEP.GT.2)   GO TO 120\nC\nC  If L1 = L1MAX + 1, the third term in the recursion formula vanishes\nC\n      Y = SRTINY * C1\n      THRCOF(NFIN-1) = Y\n      SUMBAC = SUM2\n      SUM2 = SUM2 + TINY * (L1+L1-THREE) * C1*C1\nC\n      GO TO 110\nC\nC\n  120 C2 = - (L1 - ONE) * OLDFAC / DENOM\nC\nC  Recursion to the next 3j coefficient Y\nC\n      Y = C1 * THRCOF(NFINP2-LSTEP) + C2 * THRCOF(NFINP3-LSTEP)\nC\n      IF(LSTEP.EQ.NSTEP2)   GO TO 200\nC\n      THRCOF(NFINP1-LSTEP) = Y\n      SUMBAC = SUM2\n      SUM2 = SUM2 + (L1+L1-THREE) * Y*Y\nC\nC  See if last unnormalized 3j coefficient exceeds SRHUGE\nC\n      IF(ABS(Y).LT.SRHUGE)   GO TO 110\nC\nC  This is reached if last 3j coefficient larger than SRHUGE,\nC  so that the recursion series THRCOF(NFIN), ... ,THRCOF(NFIN-LSTEP+1)\nC  has to be rescaled to prevent overflow\nC\nC     LSCALE = LSCALE + 1\n      DO 130 I=1,LSTEP\n      INDEX = NFIN - I + 1\n      IF(ABS(THRCOF(INDEX)).LT.SRTINY)   THRCOF(INDEX) = ZERO\n  130 THRCOF(INDEX) = THRCOF(INDEX) / SRHUGE\n      SUM2 = SUM2 / HUGE\n      SUMBAC = SUMBAC / HUGE\nC\nC\n      GO TO 110\nC\nC\nC  The forward recursion 3j coefficients X1, X2, X3 are to be matched\nC  with the corresponding backward recursion values Y1, Y2, Y3.\nC\n  200 Y3 = Y\n      Y2 = THRCOF(NFINP2-LSTEP)\n      Y1 = THRCOF(NFINP3-LSTEP)\nC\nC\nC  Determine now RATIO such that YI = RATIO * XI  (I=1,2,3) holds\nC  with minimal error.\nC\n      RATIO = ( X1*Y1 + X2*Y2 + X3*Y3 ) / ( X1*X1 + X2*X2 + X3*X3 )\n      NLIM = NFIN - NSTEP2 + 1\nC\n      IF(ABS(RATIO).LT.ONE)   GO TO 211\nC\n      DO 210 N=1,NLIM\n  210 THRCOF(N) = RATIO * THRCOF(N)\n      SUMUNI = RATIO * RATIO * SUMFOR + SUMBAC\n      GO TO 230\nC\n  211 NLIM = NLIM + 1\n      RATIO = ONE / RATIO\n      DO 212 N=NLIM,NFIN\n  212 THRCOF(N) = RATIO * THRCOF(N)\n      SUMUNI = SUMFOR + RATIO*RATIO*SUMBAC\n      GO TO 230\nC\n  220 SUMUNI = SUM1\nC\nC\nC  Normalize 3j coefficients\nC\n  230 CNORM = ONE / SQRT(SUMUNI)\nC\nC  Sign convention for last 3j coefficient determines overall phase\nC\n      SIGN1 = SIGN(ONE,THRCOF(NFIN))\n      SIGN2 = (-ONE) ** INT(ABS(L2+M2-L3+M3)+EPS)\n      IF(SIGN1*SIGN2) 235,235,236\n  235 CNORM = - CNORM\nC\n  236 IF(ABS(CNORM).LT.ONE)   GO TO 250\nC\n      DO 240 N=1,NFIN\n  240 THRCOF(N) = CNORM * THRCOF(N)\n      RETURN\nC\n  250 THRESH = TINY / ABS(CNORM)\n      DO 251 N=1,NFIN\n      IF(ABS(THRCOF(N)).LT.THRESH)   THRCOF(N) = ZERO\n  251 THRCOF(N) = CNORM * THRCOF(N)\nC\n      RETURN\n      END\n"
  },
  {
    "path": "pyproject.toml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n[build-system]\nrequires = [\"setuptools\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[tool.black]\nline-length = 80\ntarget-version = [\"py37\", \"py38\", \"py39\", \"py310\", \"py311\"]\nrequired-version = \"23.3.0\"\n# Enable experimental string processing so long strings are wrapped\n# automatically. This is very convenient but not quite stable yet. Stability of\n# this feature is tracked in this issue:\n# https://github.com/psf/black/issues/2188\npreview = true\n# Skip external code since modifying it can be an issue with GPL-licensed code.\nextend-exclude = '''\n(\n  ^/external\n)\n'''\n\n[tool.isort]\nprofile = \"black\"\nline_length = 80\nskip_gitignore = true\nknown_first_party = [\"spectre\"]\n# Skip external code since modifying it can be an issue with GPL-licensed code.\nextend_skip = [\"external\"]\n"
  },
  {
    "path": "setup.cfg",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n[metadata]\nname = spectre\nversion = @SPECTRE_VERSION@\ndescription = Python bindings for SpECTRE\nauthor = SXS collaboration\nurl = @SPECTRE_HOMEPAGE@\nlicense = MIT\n\n[options]\npackages = find:\ninstall_requires = @SPECTRE_PY_DEPS_OUTPUT@\npython_requires = >=3.8\n\n[options.extras_require]\ndev = @SPECTRE_PY_DEV_DEPS_OUTPUT@\n\n[options.package_data]\n# Install Python bindings libs alongside the Python code\n* = *.so\n\n[options.entry_points]\nconsole_scripts =\n    spectre = spectre.__main__:cli\n\n[flake8]\nmax-line-length = 80\nextend-ignore = \"E203\"\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(ControlSystem)\nadd_subdirectory(DataStructures)\nadd_subdirectory(Domain)\nadd_subdirectory(Elliptic)\nadd_subdirectory(Evolution)\nadd_subdirectory(Executables)\nadd_subdirectory(Informer)\nadd_subdirectory(IO)\nadd_subdirectory(NumericalAlgorithms)\nadd_subdirectory(Options)\nadd_subdirectory(Parallel)\nadd_subdirectory(ParallelAlgorithms)\nadd_subdirectory(PointwiseFunctions)\nadd_subdirectory(PythonBindings)\nadd_subdirectory(Time)\nadd_subdirectory(Utilities)\nadd_subdirectory(Visualization)\n"
  },
  {
    "path": "src/ControlSystem/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GridCenters.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GridCenters.hpp\n  Initialization.hpp\n  InitializeMeasurements.hpp\n  LimitTimeStep.hpp\n  PrintCurrentMeasurement.hpp\n  )\n"
  },
  {
    "path": "src/ControlSystem/Actions/GridCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/Actions/GridCenters.hpp\"\n\n#include <array>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace control_system {\nvoid DisableRotationWhen::pup(PUP::er& p) {\n  p | disable_at_separation;\n  p | rotation_decay_timescale;\n}\n\nnamespace Tags {\ncontrol_system::DisableRotationWhen DisableRotationWhen::create_from_options(\n    const control_system::DisableRotationWhen& disable_rotation_when) {\n  return disable_rotation_when;\n}\n}  // namespace Tags\n\nnamespace Actions {\nvoid SwitchGridRotationToSettle::UpdateRotationToSettle::apply(\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        f_of_t_list,\n    const std::string& function_of_time_name,\n    const std::array<DataVector, 3>& initial_func_and_derivs,\n    const double match_time, const double decay_time) {\n  if (not f_of_t_list->contains(function_of_time_name)) {\n    ERROR(\"Cannot find function of time name '\"\n          << function_of_time_name\n          << \"' in the set of functions of time: \" << keys_of(*f_of_t_list));\n  }\n  f_of_t_list->at(function_of_time_name) =\n      std::make_unique<domain::FunctionsOfTime::SettleToConstantQuaternion>(\n          initial_func_and_derivs, match_time, decay_time);\n}\n\nvoid SwitchGridRotationToSettle::DisableControlSystem::apply(\n    const gsl::not_null<std::unordered_map<std::string, bool>*> is_active_map,\n    const std::string& control_system_name) {\n  if (not is_active_map->contains(control_system_name)) {\n    ERROR(\"Cannot find control system '\" << control_system_name\n                                         << \"' in the active control systems, \"\n                                         << keys_of(*is_active_map));\n  }\n  is_active_map->at(control_system_name) = false;\n}\n}  // namespace Actions\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Actions/GridCenters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <limits>\n#include <memory>\n\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"ControlSystem/Tags/OptionTags.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace control_system {\n/*!\n * \\brief Holds options used to control when to start disabling the rotation\n * control system in a BNS simulation.\n */\nstruct DisableRotationWhen {\n  /// The separation at which we start turning off rotation.\n  struct DisableAtSeparation {\n    using type = double;\n    static type lower_bound() { return 2.0; }\n    static constexpr Options::String help{\n        \"The separation at which we start turning off rotation.\"};\n  };\n\n  /// The timescale in code units over which grid rotation is disabled. A\n  /// reasonable value is 30M-60M.\n  struct RotationDecayTimescale {\n    using type = double;\n    static type lower_bound() { return 30.0; }\n    static type upper_bound() { return 200.0; }\n    static constexpr Options::String help{\n        \"The timescale in code units over which grid rotation is disabled. A \"\n        \"reasonable value is 40M-60M.\"};\n  };\n\n  using options = tmpl::list<DisableAtSeparation, RotationDecayTimescale>;\n\n  static constexpr Options::String help{\n      \"Constrols the separation at which the rotation control system is \"\n      \"disabled and the rotation function of time starts settling to a \"\n      \"constant.\"};\n\n  void pup(PUP::er& p);\n\n  double disable_at_separation{std::numeric_limits<double>::signaling_NaN()};\n  double rotation_decay_timescale{std::numeric_limits<double>::signaling_NaN()};\n};\n\nnamespace OptionTags {\n/// Option tag for controlling when and how the rotation map is disabled.\nstruct DisableRotationWhen {\n  static constexpr Options::String help =\n      \"Options for controlling how the rotation control system stops as the \"\n      \"two neutron stars merge.\";\n  using type = control_system::DisableRotationWhen;\n  using group = control_system::OptionTags::ControlSystemGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// Tag for controlling when and how the rotation map is disabled.\nstruct DisableRotationWhen : db::SimpleTag {\n  using type = control_system::DisableRotationWhen;\n  using option_tags = tmpl::list<OptionTags::DisableRotationWhen>;\n\n  static constexpr bool pass_metavariables = false;\n  static control_system::DisableRotationWhen create_from_options(\n      const control_system::DisableRotationWhen& disable_rotation_when);\n};\n}  // namespace Tags\n\nnamespace Actions {\n/*!\n * \\brief Checks if the Rotation function of time has been updated because the\n * separation between the neutron star grid centers is small enough.\n *\n * \\note This is an iterable action that is to be run on the elements. It is\n * only run on the element that satisfies `is_zeroth_element()`.\n *\n * \\warning This action should only ever be run in the\n * `Parallel::Phase::DisableRotationControl` phase.\n *\n * The desired separation is controlled via the\n * control_systems::Tags::DisableRotationWhen tag. The main use for this\n * functionality is to disable the rotation control system in binary neutron\n * star mergers when the stars are sufficiently close because as the stars\n * merge, the dual control system starts to lose anything to lock on to and can\n * even start counter rotating.\n *\n * Tags used and modified:\n * - DataBox:\n *   - `domain::Tags::Element<3>`\n *   - `::Tags::TimeStepId`\n *   - `::Tags::Time`\n * - MutableGlobalCache:\n *  - Uses:\n *   - `domain::Tags::FunctionsOfTime` (\"GridCenters\" and \"Rotation\")\n *  - Modifies:\n *    - `domain::Tags::FunctionsOfTime` (\"Rotation\")\n *    - `control_system::Tags::IsActiveMap` (\"Rotation\")\n */\nstruct SwitchGridRotationToSettle {\n  /// Invokable that changes the rotation function of time to a\n  /// QuaternionSettleToConstant matching the current function of time values\n  /// and derivatives for the rotation and decaying over a specified timescale.\n  ///\n  /// Note that the final orientation of the domain is arbitrary. If we want\n  /// to rotate to a specific angle (e.g. aligning logical and inertial\n  /// coordinate axes), we need a (new) QuaternionSettleToSpecifiedValue\n  /// function of time.\n  struct UpdateRotationToSettle {\n    static void apply(\n        gsl::not_null<std::unordered_map<\n            std::string,\n            std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n            f_of_t_list,\n        const std::string& function_of_time_name,\n        const std::array<DataVector, 3>& initial_func_and_derivs,\n        double match_time, double decay_time);\n  };\n\n  /// Invokable used to disable the rotation control system via a call to\n  /// Parallel::mutate.\n  struct DisableControlSystem {\n    static void apply(\n        gsl::not_null<std::unordered_map<std::string, bool>*> is_active_map,\n        const std::string& control_system_name);\n  };\n\n  using const_global_cache_tags =\n      tmpl::list<control_system::Tags::DisableRotationWhen>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<3>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (not is_zeroth_element(element_id)) {\n      // Only one element needs to modify the control system and function of\n      // time.\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    if (not time_step_id.is_at_slab_boundary()) {\n      ERROR(\n          \"Expected to be at a Slab boundary when changing the Rotation \"\n          \"function of time to a SettleToConstant. Current TimeStepId is \"\n          << time_step_id);\n    }\n\n    const auto& bns_rotation_control =\n        db::get<control_system::Tags::DisableRotationWhen>(box);\n\n    const double time = db::get<::Tags::Time>(box);\n    if (not get<domain::Tags::FunctionsOfTime>(cache).contains(\"GridCenters\")) {\n      ERROR(\n          \"There is no function of time named 'GridCenters', which is required \"\n          \"when disabling the rotation control system since in a binary \"\n          \"neutron star simulation we need to track the grid centers of the \"\n          \"stars to decide when to disable rotation control.\");\n    }\n    const domain::FunctionsOfTime::FunctionOfTime& grid_centers_fot =\n        *get<domain::Tags::FunctionsOfTime>(cache).at(\"GridCenters\");\n    const DataVector grid_centers = grid_centers_fot.func(time)[0];\n    const double separation = sqrt(square(grid_centers[0] - grid_centers[3]) +\n                                   square(grid_centers[1] - grid_centers[4]) +\n                                   square(grid_centers[2] - grid_centers[5]));\n    if (separation > bns_rotation_control.disable_at_separation) {\n      ERROR(\n          \"Disabling the rotation control system should happen when the \"\n          \"separation is less than or equal to \"\n          << bns_rotation_control.disable_at_separation\n          << \" but the separation at time \" << time << \" is calculated to be \"\n          << separation);\n    }\n\n    if (not get<domain::Tags::FunctionsOfTime>(cache).contains(\"Rotation\")) {\n      ERROR(\n          \"There is no function of time named 'Rotation', which means that it \"\n          \"cannot be disabled.\");\n    }\n    const domain::FunctionsOfTime::FunctionOfTime& rotation_fot =\n        *get<domain::Tags::FunctionsOfTime>(cache).at(\"Rotation\");\n    const std::array<DataVector, 3> current_func_and_derivs =\n        rotation_fot.func_and_2_derivs(time);\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateRotationToSettle>(\n        cache, std::string{\"Rotation\"}, current_func_and_derivs, time,\n        bns_rotation_control.rotation_decay_timescale);\n    Parallel::mutate<control_system::Tags::IsActiveMap, DisableControlSystem>(\n        cache, std::string{\"Rotation\"});\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Actions/Initialization.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <unordered_map>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system {\nnamespace Actions {\nnamespace detail {\ntemplate <typename Objects>\nstruct get_center_tags;\n\ntemplate <domain::ObjectLabel... Object>\nstruct get_center_tags<domain::object_list<Object...>> {\n  using type = tmpl::list<domain::Tags::ObjectCenter<Object>...>;\n};\n\ntemplate <>\nstruct get_center_tags<domain::object_list<>> {\n  using type = tmpl::list<>;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\ingroup InitializationGroup\n * \\brief Initialize items related to the control system\n *\n * GlobalCache:\n * - Uses:\n *   - `control_system::Tags::MeasurementTimescales`\n *   - `control_system::Tags::WriteDataToDisk`\n *   - `control_system::Tags::ObserveCenters`\n *   - `control_system::Tags::Verbosity`\n *   - `control_system::Tags::IsActiveMap`\n *   - `control_system::Tags::SystemToCombinedNames`\n *   - `domain::Tags::ObjectCenter<domain::ObjectLabel::A>`\n *   - `domain::Tags::ObjectCenter<domain::ObjectLabel::B>`\n *\n * DataBox:\n * - Uses: Nothing\n * - Adds:\n *   - `control_system::Tags::Averager<ControlSystem>`\n *   - `control_system::Tags::Controller<ControlSystem>`\n *   - `control_system::Tags::TimescaleTuner<ControlSystem>`\n *   - `control_system::Tags::ControlError<ControlSystem>`\n *   - `control_system::Tags::CurrentNumberOfMeasurements`\n *   - `control_system::Tags::UpdateAggregators`\n * - Removes: Nothing\n * - Modifies:\n *   - `control_system::Tags::Averager<ControlSystem>`\n *   - `control_system::Tags::CurrentNumberOfMeasurements`\n *   - `control_system::Tags::UpdateAggregators`\n */\ntemplate <typename Metavariables, typename ControlSystem>\nstruct Initialize {\n  static constexpr size_t deriv_order = ControlSystem::deriv_order;\n\n  using simple_tags_from_options =\n      tmpl::list<control_system::Tags::Averager<ControlSystem>,\n                 control_system::Tags::Controller<ControlSystem>,\n                 control_system::Tags::TimescaleTuner<ControlSystem>,\n                 control_system::Tags::ControlError<ControlSystem>>;\n\n  using simple_tags =\n      tmpl::push_back<typename ControlSystem::simple_tags,\n                      control_system::Tags::UpdateAggregators,\n                      control_system::Tags::CurrentNumberOfMeasurements>;\n\n  using const_global_cache_tags = tmpl::flatten<tmpl::list<\n      control_system::Tags::SystemToCombinedNames,\n      control_system::Tags::MeasurementsPerUpdate,\n      control_system::Tags::WriteDataToDisk,\n      control_system::Tags::ObserveCenters, control_system::Tags::Verbosity,\n      typename detail::get_center_tags<\n          typename ControlSystem::control_error::object_centers>::type>>;\n\n  using mutable_global_cache_tags =\n      tmpl::list<control_system::Tags::MeasurementTimescales,\n                 control_system::Tags::IsActiveMap>;\n\n  using compute_tags = tmpl::list<>;\n\n  using return_tags =\n      tmpl::list<control_system::Tags::Averager<ControlSystem>,\n                 control_system::Tags::CurrentNumberOfMeasurements,\n                 control_system::Tags::UpdateAggregators>;\n\n  using argument_tags = tmpl::list<Parallel::Tags::GlobalCache<Metavariables>>;\n\n  static void apply(\n      const gsl::not_null<::Averager<deriv_order - 1>*> averager,\n      const gsl::not_null<int*> current_number_of_measurements,\n      const gsl::not_null<\n          std::unordered_map<std::string, control_system::UpdateAggregator>*>\n          update_aggregators,\n      const Parallel::GlobalCache<Metavariables>* const& cache) {\n    const auto& measurement_timescales =\n        Parallel::get<control_system::Tags::MeasurementTimescales>(*cache);\n    const std::unordered_map<std::string, std::string>&\n        system_to_combined_names =\n            Parallel::get<control_system::Tags::SystemToCombinedNames>(*cache);\n    const auto& measurement_timescale_func = *(measurement_timescales.at(\n        system_to_combined_names.at(ControlSystem::name())));\n    const double initial_time = measurement_timescale_func.time_bounds()[0];\n    const double measurement_timescale =\n        min(measurement_timescale_func.func(initial_time)[0]);\n\n    averager->assign_time_between_measurements(measurement_timescale);\n    *current_number_of_measurements = 0;\n\n    const std::unordered_map<std::string, bool>& is_active_map =\n        Parallel::get<control_system::Tags::IsActiveMap>(*cache);\n\n    std::unordered_map<std::string, std::unordered_set<std::string>>\n        combined_to_system_names{};\n    for (const auto& [control_system_name, combined_name] :\n         system_to_combined_names) {\n      if (combined_to_system_names.count(combined_name) != 1) {\n        combined_to_system_names[combined_name];\n      }\n\n      // Only add active control systems\n      if (is_active_map.at(control_system_name)) {\n        combined_to_system_names.at(combined_name).insert(control_system_name);\n      }\n    }\n\n    for (auto& [combined_name, control_system_names] :\n         combined_to_system_names) {\n      (*update_aggregators)[combined_name] = control_system::UpdateAggregator{\n          combined_name, std::move(control_system_names)};\n    }\n  }\n};\n}  // namespace Actions\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Actions/InitializeMeasurements.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"ControlSystem/CombinedName.hpp\"\n#include \"ControlSystem/FutureMeasurements.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Tags/FutureMeasurements.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/Trigger.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/ChooseLtsStepSize.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass TimeStepper;\nnamespace Tags {\nstruct EventsAndDenseTriggers;\nstruct TimeStep;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace tuples {\ntemplate <typename... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace control_system::Actions {\n/// \\ingroup ControlSystemGroup\n/// \\brief Set up the element component for control-system measurements.\ntemplate <typename ControlSystems>\nstruct InitializeMeasurements {\n  using control_system_groups =\n      tmpl::transform<metafunctions::measurements_t<ControlSystems>,\n                      metafunctions::control_systems_with_measurement<\n                          tmpl::pin<ControlSystems>, tmpl::_1>>;\n\n  using simple_tags =\n      tmpl::transform<control_system_groups,\n                      tmpl::bind<Tags::FutureMeasurements, tmpl::_1>>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::MeasurementsPerUpdate, Tags::DelayUpdate>;\n  using mutable_global_cache_tags =\n      tmpl::list<control_system::Tags::MeasurementTimescales>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (not db::get<Tags::DelayUpdate>(box) and\n        not db::get<::Tags::TimeStepper<TimeStepper>>(box).monotonic()) {\n      ERROR_NO_TRACE(\n          \"Chosen TimeStepper requires DelayUpdate: true to avoid deadlocks\");\n    }\n\n    const double initial_time = db::get<::Tags::Time>(box);\n    const int measurements_per_update =\n        db::get<Tags::MeasurementsPerUpdate>(box);\n    const auto& timescales = Parallel::get<Tags::MeasurementTimescales>(cache);\n    tmpl::for_each<control_system_groups>([&](auto group_v) {\n      using group = tmpl::type_from<decltype(group_v)>;\n      const bool active =\n          timescales.at(combined_name<group>())->func(initial_time)[0][0] !=\n          std::numeric_limits<double>::infinity();\n      db::mutate<Tags::FutureMeasurements<group>>(\n          [&](const gsl::not_null<FutureMeasurements*> measurements) {\n            if (active) {\n              *measurements = FutureMeasurements(\n                  static_cast<size_t>(measurements_per_update), initial_time);\n            } else {\n              *measurements = FutureMeasurements(\n                  1, std::numeric_limits<double>::infinity());\n            }\n          },\n          make_not_null(&box));\n    });\n\n    db::mutate<::Tags::EventsAndDenseTriggers>(\n        [](const gsl::not_null<EventsAndDenseTriggers*>\n               events_and_dense_triggers) {\n          tmpl::for_each<metafunctions::measurements_t<ControlSystems>>(\n              [&events_and_dense_triggers](auto measurement_v) {\n                using measurement = tmpl::type_from<decltype(measurement_v)>;\n                using control_system_group =\n                    metafunctions::control_systems_with_measurement_t<\n                        ControlSystems, measurement>;\n                using events = tmpl::transform<\n                    typename measurement::submeasurements,\n                    metafunctions::event_from_submeasurement<\n                        tmpl::pin<control_system_group>, tmpl::_1>>;\n                std::vector<std::unique_ptr<::Event>> vector_of_events =\n                    tmpl::as_pack<events>([](auto... events_v) {\n                      return make_vector<std::unique_ptr<::Event>>(\n                          std::make_unique<\n                              tmpl::type_from<decltype(events_v)>>()...);\n                    });\n                events_and_dense_triggers->add_trigger_and_events(\n                    std::make_unique<\n                        control_system::Trigger<control_system_group>>(),\n                    std::move(vector_of_events));\n              });\n        },\n        make_not_null(&box));\n\n    // Ensure that the initial time step is small enough that we don't\n    // need to perform any measurements to complete it.  This is only\n    // necessary for TimeSteppers that use the self-start procedure,\n    // because they cannot adjust their step size on the first step\n    // after self-start, but it isn't harmful to do it in other cases.\n    //\n    // Unlike in the steady-state step-limiting code, we don't do\n    // anything clever looking at measurement times or planning ahead\n    // for future steps.  Avoiding a single non-ideal step isn't worth\n    // the added complexity.\n    double earliest_expiration = std::numeric_limits<double>::infinity();\n    for (const auto& fot :\n         Parallel::get<domain::Tags::FunctionsOfTime>(cache)) {\n      earliest_expiration =\n          std::min(earliest_expiration, fot.second->time_bounds()[1]);\n    }\n    const auto& time_step = db::get<::Tags::TimeStep>(box);\n    const auto start_time = time_step.slab().start();\n    if ((start_time + time_step).value() > earliest_expiration) {\n      db::mutate<::Tags::TimeStep>(\n          [&](const gsl::not_null<TimeDelta*> step) {\n            if constexpr (Metavariables::local_time_stepping) {\n              *step = choose_lts_step_size(\n                  start_time,\n                  0.99 * (earliest_expiration - start_time.value()));\n            } else {\n              *step = Slab(start_time.value(), earliest_expiration).duration();\n            }\n          },\n          make_not_null(&box));\n    }\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace control_system::Actions\n"
  },
  {
    "path": "src/ControlSystem/Actions/LimitTimeStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n\n#include \"ControlSystem/CombinedName.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/PerformAlgorithmOnElement.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp\"\n#include \"Time/ChangeSlabSize/ChangeSlabSize.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/MinimumTimeStep.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nclass TimeStepper;\nnamespace control_system::Tags {\ntemplate <typename ControlSystems>\nstruct FutureMeasurements;\nstruct MeasurementTimescales;\n}  // namespace control_system::Tags\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace tuples {\ntemplate <typename... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace control_system::Actions {\n/// \\ingroup ControlSystemGroup\n/// \\brief Limit the step size in a GTS evolution to prevent deadlocks from\n/// control system measurements.\n///\n/// \\details Most time steppers require evaluations of the coordinates\n/// at several times during the step before they can produce dense\n/// output.  If any of those evaluations require a function-of-time\n/// update depending on a measurement within the step, the evolution\n/// will deadlock.  This action reduces the step size if necessary to\n/// prevent that from happening.\n///\n/// Specifically:\n/// 1. The chosen step will never be longer than the unmodified step,\n///    and will be short enough to avoid relevant function-of-time\n///    expirations.\n/// 2. Given the previous, the step will cover as many control-system\n///    updates as possible.\n/// 3. If the next step is likely to be limited by this action, adjust\n///    the length of the current step so that this step and the next\n///    step will be as close as possible to the same size.\ntemplate <typename ControlSystems>\nstruct LimitTimeStep {\n private:\n  using control_system_groups =\n      tmpl::transform<metafunctions::measurements_t<ControlSystems>,\n                      metafunctions::control_systems_with_measurement<\n                          tmpl::pin<ControlSystems>, tmpl::_1>>;\n\n  template <typename Group>\n  struct GroupExpiration {\n    using type = double;\n  };\n\n public:\n  using const_global_cache_tags = tmpl::list<::Tags::MinimumTimeStep>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& array_index, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(not Metavariables::local_time_stepping,\n                  \"The control system LimitTimeStep action is only for global \"\n                  \"time stepping.\");\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    if (time_step_id.substep() != 0) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const auto& time_stepper = db::get<::Tags::TimeStepper<TimeStepper>>(box);\n    if (time_stepper.monotonic()) {\n      // Monotonic steppers order operations in the same manner at the\n      // control system, so they cannot introduce deadlocks.\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    auto& proxy = ::Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    // Minimum expiration time for any FoT in the measurement group.\n    tmpl::wrap<tmpl::transform<control_system_groups,\n                               tmpl::bind<GroupExpiration, tmpl::_1>>,\n               tuples::TaggedTuple>\n        group_expiration_times{};\n\n    bool ready = true;\n    // Calculate group_expiration_times\n    tmpl::for_each<control_system_groups>([&](auto group_v) {\n      if (not ready) {\n        return;\n      }\n      using group = tmpl::type_from<decltype(group_v)>;\n\n      auto& future_measurements =\n          db::get_mutable_reference<Tags::FutureMeasurements<group>>(\n              make_not_null(&box));\n\n      std::optional<double> group_update = future_measurements.next_update();\n      if (not group_update.has_value()) {\n        Parallel::mutable_cache_item_is_ready<\n            control_system::Tags::MeasurementTimescales>(\n            cache,\n            Parallel::make_array_component_id<ParallelComponent>(array_index),\n            [&](const auto& measurement_timescales) {\n              const auto& group_timescale =\n                  *measurement_timescales.at(combined_name<group>());\n              future_measurements.update(group_timescale);\n              group_update = future_measurements.next_update();\n              ready = group_update.has_value();\n              if constexpr (Parallel::is_dg_element_collection_v<\n                                ParallelComponent>) {\n                // Note: The ArrayComponentId is still created with the\n                // array_index (ElementId) because we only support 1 callback\n                // per ArrayComponentId. This would mean for nodegroups we would\n                // discard a lot of callbacks that we need. Alternatively, the\n                // callback could do a broadcast to all elements on the\n                // nodegroup.\n                const auto element_location = static_cast<int>(\n                    Parallel::local_synchronous_action<\n                        ::Parallel::Actions::GetItemFromDistributedOject<\n                            Parallel::Tags::ElementLocations<Dim>>>(proxy)\n                        ->at(array_index));\n                return ready ? std::unique_ptr<Parallel::Callback>{}\n                             : std::unique_ptr<Parallel::Callback>(\n                                   new Parallel::ThreadedActionCallback<\n                                       Parallel::Actions::\n                                           PerformAlgorithmOnElement<false>,\n                                       decltype(proxy[element_location]),\n                                       std::decay_t<decltype(array_index)>>{\n                                       proxy[element_location], array_index});\n              } else {\n                return ready ? std::unique_ptr<Parallel::Callback>{}\n                             : std::unique_ptr<Parallel::Callback>(\n                                   new Parallel::PerformAlgorithmCallback(\n                                       proxy[array_index]));\n              }\n            });\n        if (not ready) {\n          return;\n        }\n      }\n\n      auto& group_expiration =\n          get<GroupExpiration<group>>(group_expiration_times);\n      group_expiration = std::numeric_limits<double>::infinity();\n\n      if (*group_update == std::numeric_limits<double>::infinity()) {\n        // Control measurement is not active.\n        return;\n      }\n\n      // Calculate group_expiration\n      Parallel::mutable_cache_item_is_ready<domain::Tags::FunctionsOfTime>(\n          cache,\n          Parallel::make_array_component_id<ParallelComponent>(array_index),\n          [&](const auto& functions_of_time) {\n            tmpl::for_each<group>([&](auto system) {\n              using System = tmpl::type_from<decltype(system)>;\n              if (not ready) {\n                return;\n              }\n              const auto& fot = *functions_of_time.at(System::name());\n              ready = fot.time_bounds()[1] > *group_update;\n              if (ready) {\n                group_expiration = std::min(\n                    group_expiration, fot.expiration_after(*group_update));\n              }\n            });\n            if constexpr (Parallel::is_dg_element_collection_v<\n                              ParallelComponent>) {\n              // Note: The ArrayComponentId is still created with the\n              // array_index (ElementId) because we only support 1 callback\n              // per ArrayComponentId. This would mean for nodegroups we would\n              // discard a lot of callbacks that we need. Alternatively, the\n              // callback could do a broadcast to all elements on the\n              // nodegroup.\n              const auto element_location = static_cast<int>(\n                  Parallel::local_synchronous_action<\n                      ::Parallel::Actions::GetItemFromDistributedOject<\n                          Parallel::Tags::ElementLocations<Dim>>>(proxy)\n                      ->at(array_index));\n              return ready\n                         ? std::unique_ptr<Parallel::Callback>{}\n                         : std::unique_ptr<Parallel::Callback>(\n                               new Parallel::ThreadedActionCallback<\n                                   Parallel::Actions::PerformAlgorithmOnElement<\n                                       false>,\n                                   decltype(proxy[element_location]),\n                                   std::decay_t<decltype(array_index)>>{\n                                   proxy[element_location], array_index});\n            } else {\n              return ready ? std::unique_ptr<Parallel::Callback>{}\n                           : std::unique_ptr<Parallel::Callback>(\n                                 new Parallel::PerformAlgorithmCallback(\n                                     proxy[array_index]));\n            }\n          });\n    });\n    if (not ready) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    const double orig_step_start = time_step_id.step_time().value();\n    const double orig_step_end =\n        (time_step_id.step_time() + db::get<::Tags::TimeStep>(box)).value();\n\n    // Smallest of the current step end and the FoT expirations.  We\n    // can't step any farther than this.\n    const double latest_valid_step =\n        tmpl::as_pack<control_system_groups>([&](auto... groups) {\n          return std::min(\n              {orig_step_end,\n               get<GroupExpiration<tmpl::type_from<decltype(groups)>>>(\n                   group_expiration_times)...});\n        });\n\n    if (not tmpl::as_pack<::Tags::get_all_history_tags<DbTagsList>>(\n            [&]<typename... HistoryTags>(tmpl::type_<HistoryTags>... /*meta*/) {\n              return (... and time_stepper.can_change_step_size(\n                                  time_step_id, db::get<HistoryTags>(box)));\n            })) {\n      if (orig_step_end > latest_valid_step) {\n        ERROR(\n            \"Step must be decreased to avoid control-system deadlock, but \"\n            \"time-stepper requires a fixed step size.\");\n      }\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    ASSERT(db::get<::Tags::TimeStep>(box).fraction() == 1,\n           \"Trying to change GTS step, but it isn't a full slab.  Non-slab \"\n           \"steps should only happen during self-start, but the preceding \"\n           \"check should have ended the action if this is self-start.\");\n\n    // The last update that we can perform on the next step.  Don't\n    // shrink the step past this time since that will force another\n    // step to take the measurement.\n    double last_update_time = orig_step_start;\n    // Step time that produces a balanced step with the following\n    // step, ignoring the restrictions on the current step.\n    double preferred_step_time = orig_step_end;\n\n    tmpl::for_each<control_system_groups>([&](auto group_v) {\n      using group = tmpl::type_from<decltype(group_v)>;\n\n      // This was used above, so it is not nullopt.\n      const double group_update =\n          db::get<Tags::FutureMeasurements<group>>(box).next_update().value();\n      if (group_update <= latest_valid_step) {\n        // We've satisfied this measurement.\n        last_update_time = std::max(last_update_time, group_update);\n      } else {\n        // We can't make it far enough to do the final measurement.\n        // Try to avoid a small step by choosing two equal-sized steps\n        // to the expiration time.\n        const double equal_step_time =\n            0.5 * (orig_step_start +\n                   get<GroupExpiration<group>>(group_expiration_times));\n        preferred_step_time = std::min(preferred_step_time, equal_step_time);\n      }\n    });\n\n    const double new_step_end =\n        std::clamp(preferred_step_time, last_update_time, latest_valid_step);\n\n    change_slab_size(make_not_null(&box), new_step_end);\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace control_system::Actions\n"
  },
  {
    "path": "src/ControlSystem/Actions/PrintCurrentMeasurement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace control_system::Actions {\n/*!\n * \\brief Simple action that will print the\n * `control_system::Tags::CurrentNumberOfMeasurements` for whatever control\n * system it is run on.\n */\nstruct PrintCurrentMeasurement {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const std::string& file_name) {\n    const int current_measurement =\n        db::get<control_system::Tags::CurrentNumberOfMeasurements>(box);\n\n    Parallel::fprintf(file_name, \"%s: Current measurement = %d\\n\",\n                      pretty_type::name<ParallelComponent>(),\n                      current_measurement);\n  }\n};\n}  // namespace control_system::Actions\n"
  },
  {
    "path": "src/ControlSystem/Averager.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/Averager.hpp\"\n\n#include <ostream>\n\n#include \"NumericalAlgorithms/FiniteDifference/NonUniform1D.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\ntemplate <size_t DerivOrder>\nAverager<DerivOrder>::Averager(const double avg_timescale_frac,\n                               const bool average_0th_deriv_of_q)\n    : avg_tscale_frac_(avg_timescale_frac),\n      average_0th_deriv_of_q_(average_0th_deriv_of_q) {\n  if (avg_tscale_frac_ <= 0.0) {\n    ERROR(\n        \"The specified avg_timescale_frac, the fraction of the damping \"\n        \"timescale over which averaging is performed, (\"\n        << avg_tscale_frac_ << \") must be positive\");\n  }\n}\n\ntemplate <size_t DerivOrder>\nAverager<DerivOrder>::Averager(Averager&& rhs)\n    : avg_tscale_frac_(std::move(rhs.avg_tscale_frac_)),\n      average_0th_deriv_of_q_(std::move(rhs.average_0th_deriv_of_q_)),\n      averaged_values_(std::move(rhs.averaged_values_)),\n      nullopt_(std::move(rhs.nullopt_)),\n      times_(std::move(rhs.times_)),\n      raw_qs_(std::move(rhs.raw_qs_)),\n      weight_k_(std::move(rhs.weight_k_)),\n      tau_k_(std::move(rhs.tau_k_)),\n      time_between_measurements_(std::move(rhs.time_between_measurements_)) {}\n\ntemplate <size_t DerivOrder>\nAverager<DerivOrder>& Averager<DerivOrder>::operator=(Averager&& rhs) {\n  if (this != &rhs) {\n    avg_tscale_frac_ = std::move(rhs.avg_tscale_frac_);\n    average_0th_deriv_of_q_ = std::move(rhs.average_0th_deriv_of_q_);\n    averaged_values_ = std::move(rhs.averaged_values_);\n    nullopt_ = std::move(rhs.nullopt_);\n    times_ = std::move(rhs.times_);\n    raw_qs_ = std::move(rhs.raw_qs_);\n    weight_k_ = std::move(rhs.weight_k_);\n    tau_k_ = std::move(rhs.tau_k_);\n    time_between_measurements_ = std::move(rhs.time_between_measurements_);\n  }\n  return *this;\n}\n\ntemplate <size_t DerivOrder>\nconst std::optional<std::array<DataVector, DerivOrder + 1>>&\nAverager<DerivOrder>::operator()(const double time) const {\n  if (times_.size() > DerivOrder and time == times_[0]) {\n    return averaged_values_;\n  }\n  return nullopt_;\n}\n\ntemplate <size_t DerivOrder>\nvoid Averager<DerivOrder>::clear() {\n  averaged_values_ = std::nullopt;\n  times_.clear();\n  raw_qs_.clear();\n  weight_k_ = 0.0;\n  tau_k_ = 0.0;\n}\n\ntemplate <size_t DerivOrder>\nvoid Averager<DerivOrder>::update(const double time, const DataVector& raw_q,\n                                  const DataVector& timescales) {\n  if (not raw_qs_.empty()) {\n    if (UNLIKELY(raw_q.size() != raw_qs_[0].size())) {\n      ERROR(\"The number of components in the raw_q provided (\"\n            << raw_q.size()\n            << \") does not match the size of previously supplied raw_q (\"\n            << raw_qs_[0].size() << \").\");\n    }\n  } else {\n    // This is the first call to update: initialize averaged values, weights and\n    // effective time (with proper number of components)\n    averaged_values_ =\n        make_array<DerivOrder + 1>(DataVector(raw_q.size(), 0.0));\n    weight_k_ = 0.0;\n    tau_k_ = 0.0;\n  }\n\n  // Ensure that the number of timescales matches the number of components\n  if (UNLIKELY(timescales.size() != raw_q.size())) {\n    ERROR(\"The number of supplied timescales (\"\n          << timescales.size() << \") does not match the number of components (\"\n          << raw_q.size() << \").\");\n  }\n  // Get the minimum damping time from all component timescales. This will be\n  // used to determine the averaging timescale for ALL components.\n  const double min_timescale = min(timescales);\n\n  // Do not allow updates at or before last update time\n  if (UNLIKELY(not times_.empty() and time <= last_time_updated())) {\n    ERROR(\"The specified time t=\" << time\n                                  << \" is at or before the last time \"\n                                     \"updated, t_update=\"\n                                  << last_time_updated() << \".\");\n  }\n\n  // update deques\n  times_.emplace_front(time);\n  raw_qs_.emplace_front(raw_q);\n  if (times_.size() > DerivOrder + 1) {\n    // get rid of old data once we have a sufficient number of points\n    times_.pop_back();\n    raw_qs_.pop_back();\n  }\n\n  if (times_.size() > DerivOrder) {\n    // we now have enough data points to begin averaging\n    const double tau_avg = min_timescale * avg_tscale_frac_;\n    const double tau_m = times_[0] - times_[1];\n\n    // update the weights and effective time\n    const double old_weight = weight_k_;\n    weight_k_ = (tau_m + old_weight) * tau_avg / (tau_m + tau_avg);\n    tau_k_ = (time * tau_m + tau_k_ * old_weight) * tau_avg /\n             (weight_k_ * (tau_m + tau_avg));\n\n    std::array<DataVector, DerivOrder + 1> raw_derivs = get_derivs();\n    // use raw value if not using `average_0th_deriv_of_q`\n    size_t start_ind = 0;\n    if (not average_0th_deriv_of_q_) {\n      (*averaged_values_)[0] = raw_qs_[0];\n      start_ind = 1;\n    }\n\n    for (size_t i = start_ind; i <= DerivOrder; i++) {\n      gsl::at(*averaged_values_, i) =\n          (gsl::at(raw_derivs, i) * tau_m +\n           gsl::at(*averaged_values_, i) * old_weight) *\n          tau_avg / (weight_k_ * (tau_m + tau_avg));\n    }\n  }\n}\n\ntemplate <size_t DerivOrder>\ndouble Averager<DerivOrder>::last_time_updated() const {\n  if (UNLIKELY(times_.empty())) {\n    ERROR(\"The time history has not been updated yet.\");\n  }\n  return times_[0];\n}\n\ntemplate <size_t DerivOrder>\ndouble Averager<DerivOrder>::average_time(const double time) const {\n  if (LIKELY(times_.size() > DerivOrder and time == times_[0])) {\n    return tau_k_;\n  }\n  ERROR(\n      \"Cannot return averaged values because the averager does not have \"\n      \"up-to-date information.\");\n}\n\ntemplate <size_t DerivOrder>\nstd::array<DataVector, DerivOrder + 1> Averager<DerivOrder>::get_derivs()\n    const {\n  // initialize the finite difference coefs\n  std::array<std::array<double, DerivOrder + 1>, DerivOrder + 1> coefs =\n      fd::non_uniform_1d_weights<DerivOrder + 1>(times_);\n\n  std::array<DataVector, DerivOrder + 1> result =\n      make_array<DerivOrder + 1>(DataVector(raw_qs_[0].size(), 0.0));\n  // compute derivatives\n  for (size_t i = 0; i < DerivOrder + 1; i++) {\n    for (size_t j = 0; j < DerivOrder + 1; j++) {\n      gsl::at(result, i) += raw_qs_[j] * gsl::at(gsl::at(coefs, i), j);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t DerivOrder>\nvoid Averager<DerivOrder>::pup(PUP::er& p) {\n  p | avg_tscale_frac_;\n  p | average_0th_deriv_of_q_;\n  p | averaged_values_;\n  p | times_;\n  p | raw_qs_;\n  p | weight_k_;\n  p | tau_k_;\n  p | time_between_measurements_;\n}\n\ntemplate <size_t DerivOrder>\nbool operator==(const Averager<DerivOrder>& avg1,\n                const Averager<DerivOrder>& avg2) {\n  return (avg1.avg_tscale_frac_ == avg2.avg_tscale_frac_) and\n         (avg1.average_0th_deriv_of_q_ == avg2.average_0th_deriv_of_q_) and\n         (avg1.averaged_values_ == avg2.averaged_values_) and\n         (avg1.times_ == avg2.times_) and (avg1.raw_qs_ == avg2.raw_qs_) and\n         (avg1.weight_k_ == avg2.weight_k_) and (avg1.tau_k_ == avg2.tau_k_) and\n         (avg1.time_between_measurements_ == avg2.time_between_measurements_);\n}\n\ntemplate <size_t DerivOrder>\nbool operator!=(const Averager<DerivOrder>& avg1,\n                const Averager<DerivOrder>& avg2) {\n  return not(avg1 == avg2);\n}\n\n// explicit instantiations for deriv order (1, 2, 3)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                            \\\n  template class Averager<DIM(data)>;                   \\\n  template bool operator==(const Averager<DIM(data)>&,  \\\n                           const Averager<DIM(data)>&); \\\n  template bool operator!=(const Averager<DIM(data)>&,  \\\n                           const Averager<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/ControlSystem/Averager.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <deque>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\n/// \\ingroup ControlSystemGroup\n/// A weighted exponential averager of \\f$Q\\f$ and its derivatives\n/// implementing Appendix A in \\cite Hemberger2012jz\n///\n/// The purpose of the averager is to provide \\f$Q\\f$ and 'smoothed' numerical\n/// derivatives of \\f$Q\\f$ up to the `DerivOrder`'th derivative. The 0th\n/// derivative of \\f$Q\\f$ is not typically averaged, since no differencing is\n/// necessary and no noise is introduced. The `average_0th_deriv_of_q` option\n/// allows for specifying that the 0th derivative should be averaged in addition\n/// to the derivatives. This may be desirable for control systems where\n/// \\f$Q\\f$ contains some 'noise', e.g. size control typically uses an averaged\n/// \\f$Q\\f$ since the raw computed \\f$Q\\f$ is a function of the minimum on a\n/// surface and may jump around discontinuously.\n///\n/// The averager is designed to support DerivOrders 1, 2, and 3. If an\n/// additional DerivOrder is needed, finite differencing needs to be implemented\n/// to specifically handle that order (as it seems like overkill to generalize\n/// the differencing stencil at this time).\ntemplate <size_t DerivOrder>\nclass Averager {\n  static_assert(DerivOrder != 0, \"Cannot average just a function value.\");\n  static_assert(\n      DerivOrder <= 3,\n      \"Averager only instantiated for deriv order less than or equal to 3. If \"\n      \"you want a higher deriv order, you'll need to add it yourself.\");\n\n public:\n  struct AverageTimescaleFraction {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Time scale of exponential averaging\"};\n  };\n\n  struct Average0thDeriv {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether to average the 0th derivative\"};\n  };\n\n  using options = tmpl::list<AverageTimescaleFraction, Average0thDeriv>;\n  static constexpr Options::String help{\n      \"Averager: Performs exponential averaging of the control signal at \"\n      \"multiple times in order to provide smoother derivatives of the control \"\n      \"signal.\"};\n\n  /// `avg_timescale_frac` determines the exponential averaging timescale\n  /// through \\f$\\tau_\\mathrm{avg} = \\f$`avg_timescale_frac`\\f$\\times \\tau\\f$,\n  /// where \\f$\\tau\\f$ is the damping time.\n  /// `avg_timescale_frac` must be positive.\n  /// `average_0th_deriv_of_q` determines whether the call operator returns an\n  /// averaged or unaveraged quantity for the 0th derivative of \\f$Q\\f$.\n  /// `true` returns an averaged 0th derivative of \\f$Q\\f$.\n  /// `false` returns the raw 0th derivative of \\f$Q\\f$.\n  /// The derivatives are always averaged (to reduce noise due to numerical\n  /// differentiation), so the `average_0th_deriv_of_q` option only specifies\n  /// whether to return an averaged value for the 0th derivative piece of the\n  /// function.\n  Averager(double avg_timescale_frac, bool average_0th_deriv_of_q);\n\n  Averager() = default;\n  // Explicitly defined move constructor due to the fact that the std::deque\n  // move constructor is not marked\n  Averager(Averager&& rhs);\n  Averager& operator=(Averager&& rhs);\n  Averager(const Averager&) = default;\n  Averager& operator=(const Averager&) = default;\n  ~Averager() = default;\n\n  template <size_t DDerivOrder>                          // NOLINT\n  friend bool operator==(const Averager<DDerivOrder>&,   // NOLINT\n                         const Averager<DDerivOrder>&);  // NOLINT\n\n  /// Returns \\f$Q\\f$ and its derivatives at \\f$t=\\f$`time`, provided there is\n  /// sufficient data. The averager is limited by the need for at least\n  /// (`DerivOrder` + 1) data points in order to provide the `DerivOrder`'th\n  /// derivative. If sufficient data is available, it returns \\f$Q\\f$ and\n  /// averaged derivatives of \\f$Q\\f$ up to the `DerivOrder`'th derivative, at\n  /// \\f$t=\\f$`time`. If `using_average_0th_deriv_of_q()` is `true`, then the\n  /// returned 0th derivative of \\f$Q\\f$ is also averaged. In the case that\n  /// there is insufficient data, the operator returns an invalid\n  /// `std::optional`.\n  const std::optional<std::array<DataVector, DerivOrder + 1>>& operator()(\n      double time) const;\n  /// A function that allows for resetting the averager.\n  void clear();\n  /// The function responsible for updating the averaged values\n  /// at \\f$t=\\f$`time`. Requires `raw_q` (the raw components of \\f$Q(t)\\f$)\n  /// and `timescales` (the associated damping times for each component).\n  void update(double time, const DataVector& raw_q,\n              const DataVector& timescales);\n  /// Returns the latest time at which the averager has sufficient data to\n  /// return \\f$Q\\f$ and its derivatives.\n  double last_time_updated() const;\n  /// Returns the exponentially averaged time at \\f$t=\\f$`time`. The time is\n  /// averaged along side \\f$Q\\f$ to determine the effective time at which\n  /// the average value is computed. The effective time is retarded, due to the\n  /// weighting of past times.\n  double average_time(double time) const;\n  /// Returns a bool corresponding to whether `average_0th_deriv_of_q`\n  /// is `true`/`false`.\n  bool using_average_0th_deriv_of_q() const { return average_0th_deriv_of_q_; };\n  /// Returns the averaging timescale fraction\n  double avg_timescale_frac() const { return avg_tscale_frac_; }\n\n  /// Assign the minimum of the measurement timescales related to a specific\n  /// control system to time_between_measurements_\n  void assign_time_between_measurements(\n      const double current_time_between_measurements) {\n    time_between_measurements_ = current_time_between_measurements;\n  }\n\n  /// Returns `true` if the averager is ready to receive a measurement\n  bool is_ready(const double time) const {\n    if (UNLIKELY(times_.empty())) {\n      return true;\n    }\n    return time >= times_[0] + time_between_measurements_;\n  }\n\n  void pup(PUP::er& p);\n\n private:\n  /// Returns the function and numerical derivatives up to the\n  /// `DerivOrder`'th derivative using backward finite differencing that\n  /// supports non-uniform time spacing. Since we use a stencil size of\n  /// (`DerivOrder` + 1), the order of the finite difference varies for\n  /// different derivatives. Assuming \\f$M\\lte\\f$`DerivOrder`, the \\f$M\\f$'th\n  /// derivative is of order (`DerivOrder` + 1 - \\f$M\\f$).\n  std::array<DataVector, DerivOrder + 1> get_derivs() const;\n\n  double avg_tscale_frac_;\n  bool average_0th_deriv_of_q_;\n  std::optional<std::array<DataVector, DerivOrder + 1>> averaged_values_{};\n  std::optional<std::array<DataVector, DerivOrder + 1>> nullopt_ = std::nullopt;\n  std::deque<double> times_;\n  std::deque<DataVector> raw_qs_;\n  double weight_k_ = 0.0;\n  double tau_k_ = 0.0;\n  // If this time_between_measurements_ isn't set, the default should just be\n  // that no measurements happen (i.e. infinity)\n  double time_between_measurements_{std::numeric_limits<double>::infinity()};\n};\n\ntemplate <size_t DDerivOrder>\nbool operator!=(const Averager<DDerivOrder>&, const Averager<DDerivOrder>&);\n"
  },
  {
    "path": "src/ControlSystem/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ControlSystem)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Averager.cpp\n  CalculateMeasurementTimescales.cpp\n  CleanFunctionsOfTime.cpp\n  Controller.cpp\n  ExpirationTimes.cpp\n  FutureMeasurements.cpp\n  TimescaleTuner.cpp\n  UpdateFunctionOfTime.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Averager.hpp\n  CalculateMeasurementTimescales.hpp\n  CleanFunctionsOfTime.hpp\n  CombinedName.hpp\n  Component.hpp\n  Controller.hpp\n  ExpirationTimes.hpp\n  FutureMeasurements.hpp\n  IsSize.hpp\n  Metafunctions.hpp\n  NamespaceDocs.hpp\n  RunCallbacks.hpp\n  TimescaleTuner.hpp\n  Trigger.hpp\n  UpdateControlSystem.hpp\n  UpdateFunctionOfTime.hpp\n  UpdateTimescaleTuner.hpp\n  WriteData.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ApparentHorizonFinder\n  Boost::boost\n  DataStructures\n  Domain\n  DomainStructure\n  ErrorHandling\n  FiniteDifference\n  FunctionsOfTime\n  GSL::gsl\n  Interpolation\n  Logging\n  Parallel\n  Printf\n  Serialization\n  Time\n  Utilities\n  INTERFACE\n  Observer\n  PRIVATE\n  GrSurfaces\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(ControlErrors)\nadd_subdirectory(Measurements)\nadd_subdirectory(Protocols)\nadd_subdirectory(Systems)\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/ControlSystem/CalculateMeasurementTimescales.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/CalculateMeasurementTimescales.hpp\"\n\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace control_system {\ntemplate <size_t DerivOrder, bool AllowDecrease>\nDataVector calculate_measurement_timescales(\n    const ::Controller<DerivOrder>& controller,\n    const ::TimescaleTuner<AllowDecrease>& tuner,\n    const int measurements_per_update) {\n  return tuner.current_timescale() * controller.get_update_fraction() /\n         static_cast<double>(measurements_per_update);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define ALLOWDECREASE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                              \\\n  template DataVector calculate_measurement_timescales(   \\\n      const ::Controller<DIM(data)>& controller,          \\\n      const ::TimescaleTuner<ALLOWDECREASE(data)>& tuner, \\\n      const int measurements_per_update);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (true, false))\n\n#undef INSTANTIATE\n#undef DIM\n#undef ALLOWDECREASE\n\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/CalculateMeasurementTimescales.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\ntemplate <size_t DerivOrder>\nclass Controller;\nclass DataVector;\ntemplate <bool AllowDecrease>\nclass TimescaleTuner;\n/// \\endcond\n\nnamespace control_system {\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Calculate the measurement timescale based on the damping timescale,\n * update fraction, and DerivOrder of the control system\n *\n * The update timescale is \\f$\\tau_\\mathrm{update} = \\alpha_\\mathrm{update}\n * \\tau_\\mathrm{damp}\\f$ where \\f$\\tau_\\mathrm{damp}\\f$ is the damping timescale\n * (from the TimescaleTuner) and \\f$\\alpha_\\mathrm{update}\\f$ is the update\n * fraction (from the controller). For an Nth order control system, the averager\n * requires at least N measurements in order to perform its finite\n * differencing to calculate the derivatives of the control error. This implies\n * that the largest the measurement timescale can be is \\f$\\tau_\\mathrm{m} =\n * \\tau_\\mathrm{update} / N\\f$. To ensure that we have sufficient measurements,\n * we calculate the measurement timescales as \\f$\\tau_\\mathrm{m} =\n * \\tau_\\mathrm{update} / N\\f$ where \\f$N\\f$ is `measurements_per_update`.\n */\ntemplate <size_t DerivOrder, bool AllowDecrease>\nDataVector calculate_measurement_timescales(\n    const ::Controller<DerivOrder>& controller,\n    const ::TimescaleTuner<AllowDecrease>& tuner,\n    const int measurements_per_update);\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/CleanFunctionsOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/CleanFunctionsOfTime.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace control_system {\nvoid CleanFunctionsOfTimeAction::CleanFunc::apply(\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        functions,\n    const double time) {\n  for (auto& function : *functions) {\n    function.second->truncate_at_time(time);\n  }\n}\n\nCleanFunctionsOfTime::CleanFunctionsOfTime(CkMigrateMessage* const m)\n    : Event(m) {}\n\nbool CleanFunctionsOfTime::needs_evolved_variables() const { return false; }\n\nPUP::able::PUP_ID CleanFunctionsOfTime::my_PUP_ID = 0;  // NOLINT\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/CleanFunctionsOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace control_system::Tags {\nstruct MeasurementTimescales;\n}  // namespace control_system::Tags\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace control_system {\n/// \\ingroup ControlSystemGroup\n/// Reduction action that deletes function-of-time data from before\n/// the passed time.\n///\n/// \\see CleanFunctionsOfTime\nstruct CleanFunctionsOfTimeAction {\n private:\n  struct CleanFunc {\n    static void apply(\n        gsl::not_null<std::unordered_map<\n            std::string,\n            std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n            functions,\n        double time);\n  };\n\n public:\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const double time) {\n    Parallel::mutate<domain::Tags::FunctionsOfTime, CleanFunc>(cache, time);\n    Parallel::mutate<Tags::MeasurementTimescales, CleanFunc>(cache, time);\n  }\n};\n\n/// \\ingroup ControlSystemGroup\n/// Delete old function-of-time data to save memory.\n///\n/// \\warning Running this event will make the\n/// `global_functions_of_time` data stored in the output files\n/// useless.\nclass CleanFunctionsOfTime : public Event {\n  using ReductionData = Parallel::ReductionData<\n      Parallel::ReductionDatum<double, funcl::AssertEqual<>>>;\n\n public:\n  /// \\cond\n  explicit CleanFunctionsOfTime(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CleanFunctionsOfTime);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help =\n      \"Delete old function-of-time data to save memory.\\n\"\n      \"\\n\"\n      \"WARNING: Running this event will make the global_functions_of_time\\n\"\n      \"data stored in the output files useless.\";\n\n  CleanFunctionsOfTime() = default;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::Time>;\n\n  template <typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(const double time,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {\n      (void)cache;\n      (void)array_index;\n      (void)time;\n      ERROR(\"Reductions not implemented with DgElementCollection.\");\n    } else {\n      // It doesn't matter what individual component the target is.\n      // We use ObserverWriter[0] because it must exist and is easy to\n      // name here.\n      const auto& writer0_proxy = Parallel::get_parallel_component<\n          observers::ObserverWriter<Metavariables>>(cache)[0];\n      const auto& self_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(\n              cache)[array_index];\n      Parallel::contribute_to_reduction<CleanFunctionsOfTimeAction>(\n          ReductionData(time), self_proxy, writer0_proxy);\n    }\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override;\n};\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/CombinedName.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace control_system {\n/*!\n * \\brief Given a `tmpl::list` of control systems, returns a string which is a\n * sorted concatenation of all the control systems' names.\n *\n * \\details As an example, if `ListOfControlSystems` is `tmpl::list<Rotation,\n * Expansion, Translation>`, this function would return\n * `ExpansionRotationTranslation`. Sorting is done using `std::sort`.\n *\n * \\tparam ListOfControlSystems `tmpl::list` of control systems\n */\ntemplate <typename ListOfControlSystems>\nconst std::string& combined_name() {\n  static const std::string combined_name = []() {\n    std::vector<std::string> control_system_names{};\n    control_system_names.reserve(tmpl::size<ListOfControlSystems>::value);\n    std::string local_combined_name{};\n\n    tmpl::for_each<ListOfControlSystems>(\n        [&control_system_names](auto control_system_v) {\n          using control_system = tmpl::type_from<decltype(control_system_v)>;\n          control_system_names.emplace_back(control_system::name());\n        });\n\n    alg::sort(control_system_names);\n\n    for (const std::string& name : control_system_names) {\n      local_combined_name += name;\n    }\n\n    return local_combined_name;\n  }();\n  return combined_name;\n}\n\n/*!\n * \\brief Given a `tmpl::list` of control systems, this returns a map between\n * the name of each control system, and the `control_system::combined_name` of\n * all control systems with the same measurement.\n *\n * \\details All control systems have a type alias `measurement` corresponding to\n * a `control_system::protocols::Measurement`. Some control systems can have the\n * same measurement, while some have different ones. This function splits the\n * template list of control systems into a `tmpl::list` of `tmpl::list`s where\n * each inner list holds control systems with the same measurement. Each of\n * these inner lists is used in `control_system::combined_name` to get the\n * concatenated name for those control systems. This combined name is then used\n * as the value in the resulting map for they key of each control system in the\n * inner list.\n *\n * \\tparam ControlSystems `tmpl::list` of control systems\n */\ntemplate <typename ControlSystems>\nstd::unordered_map<std::string, std::string> system_to_combined_names() {\n  using measurements = metafunctions::measurements_t<ControlSystems>;\n  using control_systems_with_measurements =\n      tmpl::transform<measurements,\n                      metafunctions::control_systems_with_measurement<\n                          tmpl::pin<ControlSystems>, tmpl::_1>>;\n\n  std::unordered_map<std::string, std::string> map_of_names{};\n\n  tmpl::for_each<control_systems_with_measurements>([&map_of_names](\n                                                        auto list_v) {\n    using control_systems_with_measurement = tmpl::type_from<decltype(list_v)>;\n\n    const std::string& combined_name =\n        control_system::combined_name<control_systems_with_measurement>();\n\n    tmpl::for_each<control_systems_with_measurement>(\n        [&map_of_names, &combined_name](auto control_system_v) {\n          using control_system = tmpl::type_from<decltype(control_system_v)>;\n          map_of_names[control_system::name()] = combined_name;\n        });\n  });\n\n  return map_of_names;\n}\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Component.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <tuple>\n\n#include \"ControlSystem/Actions/Initialization.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief The singleton parallel component responsible for managing a single\n * control system.\n *\n * The control system that this component is responsible for is specified by the\n * `ControlSystem` template parameter. This control system must conform to the\n * `control_system::Protocols::ControlSystem` protocol.\n */\ntemplate <class Metavariables, typename ControlSystem>\nstruct ControlComponent {\n  static_assert(tt::assert_conforms_to_v<\n                ControlSystem, control_system::protocols::ControlSystem>);\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using control_system = ControlSystem;\n\n  static std::string name() { return ControlSystem::name(); }\n\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<Initialization::Actions::InitializeItems<\n                     ::control_system::Actions::Initialize<Metavariables,\n                                                           ControlSystem>>,\n                 Parallel::Actions::TerminatePhase>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n          tmpl::list<ControlSystem, typename ControlSystem::measurement>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<\n        ControlComponent<Metavariables, ControlSystem>>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\nnamespace control_system {\n/// \\ingroup ControlSystemGroup\n/// List of control components to be added to the component list of the\n/// metavars\ntemplate <typename Metavariables, typename ControlSystems>\nusing control_components = tmpl::transform<\n    ControlSystems,\n    tmpl::bind<ControlComponent, tmpl::pin<Metavariables>, tmpl::_1>>;\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Size)\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GridCenters.cpp\n  Size.cpp\n  Skew.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Expansion.hpp\n  GridCenters.hpp\n  Rotation.hpp\n  Shape.hpp\n  Size.hpp\n  Skew.hpp\n  Translation.hpp\n  )\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Expansion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstruct Domain;\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace control_system {\nnamespace ControlErrors {\n/*!\n * \\brief Control error in the 3D\n * \\link domain::CoordinateMaps::TimeDependent::CubicScale CubicScale \\endlink\n * coordinate map\n *\n * \\details Computes the error in the map parameter \\f$a(t)\\f$ using Eq. (40)\n * from \\cite Ossokine2013zga (see \\link\n * domain::CoordinateMaps::TimeDependent::CubicScale CubicScale \\endlink for a\n * definition of \\f$a(t)\\f$). The equation is\n *\n * \\f{align}{\n *   \\delta a &= a\\left( \\frac{\\vec{X}\\cdot\\vec{C}}{||\\vec{C}||^2} - 1 \\right)\n * \\\\ \\delta a &= a\\left( \\frac{X_0}{C_0} - 1 \\right) \\f}\n *\n * where \\f$\\vec{X} = \\vec{x}_A - \\vec{x}_B\\f$ and \\f$\\vec{C} = \\vec{c}_A -\n * \\vec{c}_B\\f$. Here, object A is located on the positive x-axis and object B\n * is located on the negative x-axis, \\f$\\vec{X}\\f$ is the difference in\n * positions of the centers of the mapped objects, and\n * \\f$\\vec{C}\\f$ is the difference of the centers of the excision spheres, all\n * in the grid frame. It is assumed that the positions of the excision spheres\n * are exactly along the x-axis, which is why we were able to make the\n * simplification in the second line above.\n *\n * Requirements:\n * - This control error requires that there be exactly two objects in the\n *   simulation\n * - Currently both these objects must be black holes\n * - Currently this control system can only be used with the \\link\n *   control_system::Systems::Expansion Expansion \\endlink control system\n */\nstruct Expansion : tt::ConformsTo<protocols::ControlError> {\n  using object_centers =\n      domain::object_list<domain::ObjectLabel::A, domain::ObjectLabel::B>;\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Computes the control error for expansion control. This should not \"\n      \"take any options.\"};\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  std::optional<double> get_suggested_timescale() const { return std::nullopt; }\n\n  void reset() {}\n\n  void pup(PUP::er& /*p*/) {}\n\n  template <typename Metavariables, typename... TupleTags>\n  DataVector operator()(const ::TimescaleTuner<true>& /*unused*/,\n                        const Parallel::GlobalCache<Metavariables>& cache,\n                        const double time,\n                        const std::string& function_of_time_name,\n                        const tuples::TaggedTuple<TupleTags...>& measurements) {\n    const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n\n    const double current_expansion_factor =\n        functions_of_time.at(function_of_time_name)->func(time)[0][0];\n\n    using center_A = control_system::QueueTags::Center<::domain::ObjectLabel::A,\n                                                       Frame::Grid>;\n    using center_B = control_system::QueueTags::Center<::domain::ObjectLabel::B,\n                                                       Frame::Grid>;\n\n    const double grid_position_of_A =\n        Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::A>>(\n            cache)[0];\n    const double grid_position_of_B =\n        Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::B>>(\n            cache)[0];\n    const double current_position_of_A = get<center_A>(measurements)[0];\n    const double current_position_of_B = get<center_B>(measurements)[0];\n\n    // A is to the right of B in grid frame. To get positive differences,\n    // take A - B\n    const double expected_expansion_factor =\n        current_expansion_factor *\n        (current_position_of_A - current_position_of_B) /\n        (grid_position_of_A - grid_position_of_B);\n\n    return {expected_expansion_factor - current_expansion_factor};\n  }\n};\n}  // namespace ControlErrors\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/GridCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/GridCenters.hpp\"\n\n#include <cstddef>\n\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\nnamespace control_system::ControlErrors {\nGridCenters::GridCenters(const Options::Context& /*context*/) {}\n\nDataVector GridCenters::impl(const DataVector& fot_positions_dv,\n                             const DataVector& measured_grid_position_of_A,\n                             const DataVector& measured_grid_position_of_B) {\n  tnsr::I<DataVector, 3, Frame::Grid> measured_grid_positions_tnsr{2_st, 0.0};\n  for (size_t i = 0; i < 3; i++) {\n    measured_grid_positions_tnsr.get(i)[0] = measured_grid_position_of_A[i];\n    measured_grid_positions_tnsr.get(i)[1] = measured_grid_position_of_B[i];\n  }\n\n  DataVector control_error{6, 0.0};\n  for (size_t i = 0; i < 2; i++) {\n    const auto& measured_grid_position =\n        i == 0 ? measured_grid_position_of_A : measured_grid_position_of_B;\n    for (size_t j = 0; j < 3; j++) {\n      control_error[i * 3 + j] =\n          measured_grid_position[j] - fot_positions_dv[i * 3 + j];\n    }\n  }\n\n  return control_error;\n}\n\nvoid GridCenters::pup(PUP::er& /*p*/) {}\n}  // namespace control_system::ControlErrors\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/GridCenters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace control_system::ControlErrors {\n/*!\n * \\brief Tracks the grid-frame object centers.\n *\n * Tracks the 3d grid-frame centers of the two objects. Tracking the\n * grid-frame centers is better than tracking the inertial-frame centers\n * because the timescale at which the grid frame locations change is much\n * larger than the inertial ones. This is because in the grid frame only the\n * radial inspiral really changes because the rotation control system handles\n * the orbital movement. The orbital time scale is much larger than the\n * Cartesian coordinate location timescale, so splitting the system into\n * angular and radial allows for both to be slowly varying.\n *\n * The intended use case of the output of this control system is for binary\n * neutron star simulations where the rotation control needs to be disabled at\n * merger. It is also necessary for changing from harmonic to damped harmonic\n * gauge near when the neutron stars merge. Finally, it can also be used for\n * triggering a grid change from the inspiral grid to the remnant grid in a\n * BNS merger simulation.\n *\n * Requirements:\n * - This control error requires that there be either one or two objects in the\n *   simulation (typically neutron stars).\n * - Currently this control error can only be used with the \\link\n *   control_system::Systems::GridCenters GridCenters \\endlink control system\n * - There must exist a `PiecewisePolynomial<2>` function of time named\n *   \"GridCenters\". It is assumed that components `[0,2]` are the grid\n *   coordinates of object A and the components `[3,5]`  are the grid\n *   coordinates of object B.\n */\nstruct GridCenters : tt::ConformsTo<protocols::ControlError> {\n  using object_centers =\n      domain::object_list<domain::ObjectLabel::A, domain::ObjectLabel::B>;\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Computes the control error for the grid centers of two objects. \"\n      \"This should not take any options.\"};\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  std::optional<double> get_suggested_timescale() const { return std::nullopt; }\n\n  void reset() {}\n\n  explicit GridCenters(const Options::Context& context = {});\n\n  void pup(PUP::er& p);\n\n  template <typename Metavariables, typename... TupleTags>\n  DataVector operator()(const ::TimescaleTuner<true>& /*unused*/,\n                        const Parallel::GlobalCache<Metavariables>& cache,\n                        const double time,\n                        const std::string& function_of_time_name,\n                        const tuples::TaggedTuple<TupleTags...>& measurements) {\n    using grid_center_A =\n        control_system::QueueTags::Center<::domain::ObjectLabel::A,\n                                          Frame::Grid>;\n    using grid_center_B =\n        control_system::QueueTags::Center<::domain::ObjectLabel::B,\n                                          Frame::Grid>;\n\n    const auto& measured_grid_position_of_A = get<grid_center_A>(measurements);\n    const auto& measured_grid_position_of_B = get<grid_center_B>(measurements);\n\n    return impl(get<domain::Tags::FunctionsOfTime>(cache)\n                    .at(function_of_time_name)\n                    ->func(time)[0],\n                measured_grid_position_of_A, measured_grid_position_of_B);\n  }\n\n private:\n  static DataVector impl(const DataVector& fot_positions_dv,\n                         const DataVector& measured_grid_position_of_A,\n                         const DataVector& measured_grid_position_of_B);\n};\n}  // namespace control_system::ControlErrors\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Rotation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstruct Domain;\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace control_system {\nnamespace ControlErrors {\n/*!\n * \\brief Control error in the 3D\n * \\link domain::CoordinateMaps::TimeDependent::Rotation Rotation \\endlink\n * coordinate map\n *\n * \\details Computes the error in the angle rotated by the system using a\n * slightly modified version of Eq. (41) from \\cite Ossokine2013zga. The\n * equation is\n *\n * \\f[ \\delta\\vec{q} = \\frac{\\vec{C}\\times\\vec{X}}{\\vec{C}\\cdot\\vec{X}} \\f]\n *\n * where \\f$\\vec{X} = \\vec{x}_A - \\vec{x}_B\\f$ and \\f$\\vec{C} = \\vec{c}_A -\n * \\vec{c}_B\\f$. Here, object A is located on the positive x-axis and object B\n * is located on the negative x-axis, \\f$\\vec{X}\\f$ is the difference in\n * positions of the centers of the mapped objects, and\n * \\f$\\vec{C}\\f$ is the difference of the centers of the excision spheres, all\n * in the grid frame.\n *\n * Requirements:\n * - This control error requires that there be exactly two objects in the\n *   simulation\n * - Currently both these objects must be black holes\n * - Currently this control system can only be used with the \\link\n *   control_system::Systems::Rotation Rotation \\endlink control system\n */\nstruct Rotation : tt::ConformsTo<protocols::ControlError> {\n  using object_centers =\n      domain::object_list<domain::ObjectLabel::A, domain::ObjectLabel::B>;\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Computes the control error for rotation control. This should not \"\n      \"take any options.\"};\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  std::optional<double> get_suggested_timescale() const { return std::nullopt; }\n\n  void reset() {}\n\n  void pup(PUP::er& /*p*/) {}\n\n  template <typename Metavariables, typename... TupleTags>\n  DataVector operator()(const ::TimescaleTuner<true>& /*unused*/,\n                        const Parallel::GlobalCache<Metavariables>& cache,\n                        const double /*time*/,\n                        const std::string& /*function_of_time_name*/,\n                        const tuples::TaggedTuple<TupleTags...>& measurements) {\n    using center_A = control_system::QueueTags::Center<::domain::ObjectLabel::A,\n                                                       Frame::Grid>;\n    using center_B = control_system::QueueTags::Center<::domain::ObjectLabel::B,\n                                                       Frame::Grid>;\n\n    const tnsr::I<double, 3, Frame::Grid>& grid_position_of_A =\n        Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::A>>(\n            cache);\n    const tnsr::I<double, 3, Frame::Grid>& grid_position_of_B =\n        Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::B>>(\n            cache);\n    const DataVector& current_position_of_A = get<center_A>(measurements);\n    const DataVector& current_position_of_B = get<center_B>(measurements);\n\n    // A is to the right of B in grid frame. To get positive differences,\n    // take A - B\n    const auto grid_diff_tnsr = tenex::evaluate<ti::I>(\n        grid_position_of_A(ti::I) - grid_position_of_B(ti::I));\n    const DataVector grid_diff{\n        {grid_diff_tnsr[0], grid_diff_tnsr[1], grid_diff_tnsr[2]}};\n    const DataVector current_diff =\n        current_position_of_A - current_position_of_B;\n\n    const double grid_dot_current = dot(grid_diff, current_diff);\n    const DataVector grid_cross_current = cross(grid_diff, current_diff);\n\n    return grid_cross_current / grid_dot_current;\n  }\n};\n}  // namespace ControlErrors\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Shape.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstruct Domain;\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace Frame {\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::ControlErrors {\nnamespace detail {\ntemplate <::domain::ObjectLabel Horizon>\nstd::string excision_sphere_name() {\n  return \"ExcisionSphere\"s + ::domain::name(Horizon);\n}\n\ntemplate <::domain::ObjectLabel Horizon>\nstd::string size_name() {\n  return \"Size\"s + ::domain::name(Horizon);\n}\n}  // namespace detail\n\n/*!\n * \\brief Control error in the\n * \\link domain::CoordinateMaps::TimeDependent::Shape Shape \\endlink coordinate\n * map\n *\n * \\details Computes the error for each \\f$ l,m \\f$ mode of the shape map except\n * for \\f$ l=0 \\f$ and \\f$ l=1 \\f$. This is because the \\f$ l=0 \\f$ mode is\n * controlled by characteristic speed control, and the \\f$ l=1 \\f$ mode is\n * redundant with the \\link\n * domain::CoordinateMaps::TimeDependent::Translation Translation \\endlink map.\n * The equation for the control error is Eq. (77) from \\cite Hemberger2012jz\n * which is\n *\n * \\f{align}\n * Q_{lm} &= -\\frac{r_\\mathrm{EB} -\n * Y_{00}\\lambda_{00}(t)}{\\sqrt{\\frac{\\pi}{2}} Y_{00}S_{00}} S_{lm} -\n * \\lambda_{lm}(t), \\quad l>=2 \\label{eq:shape_control_error}\n * \\f}\n *\n * where \\f$ r_\\mathrm{EB} \\f$ is the radius of the excision boundary in the\n * grid frame, \\f$ \\lambda_{00}(t) \\f$ is the size map parameter, \\f$\n * \\lambda_{lm}(t) \\f$ for \\f$ l>=2 \\f$ are the shape map parameters, and \\f$\n * S_{lm}\\f$ are the coefficients of the harmonic expansion of the apparent\n * horizon. The coefficients \\f$ \\lambda_{lm}(t) \\f$ (*not* including \\f$ l=0\n * \\f$) and \\f$ S_{lm}\\f$ (including \\f$ l=0 \\f$) are stored as the real-valued\n * coefficients \\f$ a_{lm} \\f$ and \\f$ b_{lm} \\f$ of the SPHEREPACK\n * spherical-harmonic expansion (in ylm::Spherepack). The $\\lambda_{00}(t)$\n * coefficient, on the other hand, is stored as the complex coefficient \\f$\n * A_{00} \\f$ of the standard \\f$ Y_{lm} \\f$ decomposition. Because \\f$ a_{00} =\n * \\sqrt{\\frac{2}{\\pi}}A_{00} \\f$, there is an extra factor of \\f$\n * \\sqrt{\\frac{\\pi}{2}} \\f$ in the above formula in the denominator of the\n * fraction multiplying the \\f$ S_{00}\\f$ component so it is represented in the\n * \\f$ Y_{lm} \\f$ decomposition just like \\f$ r_{EB} \\f$ and \\f$ \\lambda_{00}\n * \\f$ are). That way, we ensure the numerator and denominator are represented\n * in the same way before we take their ratio.\n *\n * Requirements:\n * - This control error requires that there be at least one excision surface in\n *   the simulation\n * - Currently this control error can only be used with the \\link\n *   control_system::Systems::Shape Shape \\endlink control system\n */\ntemplate <::domain::ObjectLabel Object>\nstruct Shape : tt::ConformsTo<protocols::ControlError> {\n  // Shape needs an excision sphere\n  using object_centers = domain::object_list<Object>;\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Computes the control error for shape control. This should not take any \"\n      \"options.\"};\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  std::optional<double> get_suggested_timescale() const { return std::nullopt; }\n\n  void reset() {}\n\n  void pup(PUP::er& /*p*/) {}\n\n  template <typename Metavariables, typename... TupleTags>\n  DataVector operator()(const ::TimescaleTuner<true>& /*unused*/,\n                        const Parallel::GlobalCache<Metavariables>& cache,\n                        const double time,\n                        const std::string& function_of_time_name,\n                        const tuples::TaggedTuple<TupleTags...>& measurements) {\n    const auto& domain = get<domain::Tags::Domain<3>>(cache);\n    const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n    const DataVector lambda_lm_coefs =\n        functions_of_time.at(function_of_time_name)->func(time)[0];\n    const double lambda_00_coef =\n        functions_of_time.at(detail::size_name<Object>())->func(time)[0][0];\n\n    const size_t spectral_size = lambda_lm_coefs.size();\n    // Note: l_max == m_max for the shape map, so the spectral size is\n    // always 2 * (l_max + 1) * (l_max + 1).\n    ASSERT(spectral_size % 2 == 0, \"Spectral size \"\n                                       << spectral_size << \" should be even \"\n                                       << \" for shape map coefficients.\");\n    const auto l_max_plus_one = static_cast<size_t>(\n        sqrt(static_cast<double>(spectral_size / 2)));  // NOLINT\n    ASSERT(2 * l_max_plus_one * l_max_plus_one == spectral_size,\n           \"Shape map spectral size \" << spectral_size\n                                      << \" cannot be represented by a \"\n                                      << \"Spherepack with l_max == m_max.\");\n    const size_t l_max = l_max_plus_one - 1;\n\n    const auto& ah =\n        get<control_system::QueueTags::Horizon<Frame::Distorted, Object>>(\n            measurements);\n    if (UNLIKELY(ah.l_max() > l_max)) {\n      ERROR(\"Horizon \" << ::domain::name(Object)\n                       << \" has l_max = \" << ah.l_max()\n                       << \" but the shape map has l_max = \" << l_max\n                       << \". The horizon l_max must be less than or equal to \"\n                          \"the shape map l_max.\");\n    }\n    const auto& ah_coefs = ah.coefficients();\n\n    const DataVector ah_coefs_ref{};\n    if (lambda_lm_coefs.size() != ah_coefs.size()) {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n      const_cast<DataVector&>(ah_coefs_ref) =\n          ylm::Spherepack::prolong_or_restrict(ah_coefs, ah.l_max(), ah.m_max(),\n                                               l_max, l_max);\n    } else {\n      make_const_view(make_not_null(&ah_coefs_ref), ah_coefs, 0,\n                      ah_coefs.size());\n    }\n\n    const auto& excision_spheres = domain.excision_spheres();\n\n    ASSERT(domain.excision_spheres().count(\n               detail::excision_sphere_name<Object>()) == 1,\n           \"Excision sphere \" << detail::excision_sphere_name<Object>()\n                              << \" not in the domain but is needed to \"\n                                 \"compute Shape control error.\");\n\n    const double radius_excision_sphere_grid_frame =\n        excision_spheres.at(detail::excision_sphere_name<Object>()).radius();\n\n    const double Y00 = sqrt(0.25 / M_PI);\n    ylm::SpherepackIterator iter{l_max, l_max};\n    // See above docs for why we have the sqrt(pi/2) in the denominator\n    const double relative_size_factor =\n        (radius_excision_sphere_grid_frame / Y00 - lambda_00_coef) /\n        (sqrt(0.5 * M_PI) * ah_coefs_ref[iter.set(0, 0)()]);\n\n    // The map parameters are in terms of SPHEREPACK coefficients (just like\n    // strahlkorper coefficients), *not* spherical harmonic coefficients, thus\n    // the control error for each l,m is in terms of SPHEREPACK coefficients\n    // and no extra factors of sqrt(2/pi) are needed\n    DataVector Q = -relative_size_factor * ah_coefs_ref - lambda_lm_coefs;\n\n    // Shape control is only for l > 1 so enforce that Q=0 for l=0,l=1. These\n    // components of the control error won't be 0 automatically because the AH\n    // can freely have nonzero l=0 and l=1 coefficients so we have to set the\n    // control error components to be 0 manually.\n    Q[iter.set(0, 0)()] = 0.0;\n    Q[iter.set(1, -1)()] = 0.0;\n    Q[iter.set(1, 0)()] = 0.0;\n    Q[iter.set(1, 1)()] = 0.0;\n\n    return Q;\n  }\n};\n}  // namespace control_system::ControlErrors\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/AhSpeed.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <sstream>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace control_system::size::States {\n\nstd::unique_ptr<State> AhSpeed::get_clone() const {\n  return std::make_unique<AhSpeed>(*this);\n}\n\nstd::string AhSpeed::update(const gsl::not_null<Info*> info,\n                            const StateUpdateArgs& update_args,\n                            const CrossingTimeInfo& crossing_time_info) const {\n  const double min_char_speed = update_args.min_char_speed;\n  const double min_comoving_char_speed = update_args.min_comoving_char_speed;\n\n  // This factor is present in SpEC, and it is used to prevent\n  // oscillations between states.  The value was chosen in SpEC, but\n  // nothing should be sensitive to small changes in this value as\n  // long as it is slightly greater than unity.\n  constexpr double non_oscillation_drift_inward_factor = 1.1;\n\n  // Note that delta_radius_is_in_danger and char_speed_is_in_danger\n  // can be different for different States.\n  //\n  // The value of 20 causes the control system to panic easily if\n  // delta_radius is decreasing quickly.  The value 20 was chosen\n  // by trial-and-error in SpEC.\n  constexpr double time_tolerance_for_delta_r_in_danger = 20.0;\n  const bool delta_radius_is_in_danger =\n      crossing_time_info.horizon_will_hit_excision_boundary_first and\n      crossing_time_info.t_delta_radius.value_or(\n          std::numeric_limits<double>::infinity()) <\n          info->damping_time * time_tolerance_for_delta_r_in_danger;\n\n  const bool char_speed_is_in_danger = [&crossing_time_info, &info,\n                                        &delta_radius_is_in_danger,\n                                        &min_char_speed]() {\n    // speed_tolerance_for_char_speed_in_danger is slightly greater\n    // than unity so that we don't panic if the actual char speed is\n    // large enough compared to the target char speed.  The value 1.1\n    // was chosen in SpEC and we haven't had to change it; the behavior\n    // should not be sensitive to small changes in this value.\n    constexpr double speed_tolerance_for_char_speed_in_danger = 1.1;\n    // We don't want to panic unless crossing time is less than\n    // the current damping time. The value 0.99 was chosen in SpEC and we\n    // haven't had to change it; the behavior should not be sensitive to\n    // small changes in this value.\n    constexpr double time_tolerance_for_char_speed_in_danger = 0.99;\n    return (not delta_radius_is_in_danger) and\n           (crossing_time_info.char_speed_will_hit_zero_first and\n            crossing_time_info.t_char_speed.value_or(\n                std::numeric_limits<double>::infinity()) <\n                info->damping_time * time_tolerance_for_char_speed_in_danger and\n            min_char_speed < info->target_char_speed *\n                                 speed_tolerance_for_char_speed_in_danger);\n  }();\n\n  const bool comoving_decreasing_slower_than_char_speeds = not(\n      crossing_time_info.t_char_speed.has_value() and\n      crossing_time_info.t_comoving_char_speed.has_value() and\n      update_args.min_comoving_char_speed > 0.0 and\n      update_args.min_comoving_char_speed /\n              crossing_time_info.t_comoving_char_speed.value() >\n          update_args.min_char_speed / crossing_time_info.t_char_speed.value());\n  // The value of 5.0 was chosen by trial and error in SpEC\n  constexpr double comoving_char_speed_to_damping_time_limit = 5.0;\n\n  std::stringstream ss{};\n\n  if (char_speed_is_in_danger) {\n    ss << \"Current state AhSpeed. Char speed in danger. Staying in \"\n          \"AhSpeed.\\n\";\n    if (info->target_char_speed < min_char_speed) {\n      // The value of 1.01 below was chosen by trial and error in\n      // SpEC.  The value needs to be greater than unity, and something\n      // that doesn't drive min_char_speed too quickly.\n      constexpr double min_char_speed_increase_factor = 1.01;\n      // We are already in state AhSpeed, and we are in danger.\n      // But target_char_speed is less than min_char_speed, so we don't want\n      // to continue to push min_char_speed downward.  Instead, increase\n      // target_char_speed to be above min_char_speed.\n      // We don't do this if delta_radius_is_in_danger, because for that\n      // case we might need to drive min_char_speed to a smaller value.\n      info->target_char_speed = min_char_speed * min_char_speed_increase_factor;\n      ss << \" Target char speed = \" << info->target_char_speed << \"\\n\";\n    }\n    info->suggested_time_scale = crossing_time_info.t_char_speed;\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (delta_radius_is_in_danger) {\n    // The values of target_speed_decrease_factor and\n    // time_tolerance_for_delta_r were chosen by trial and error in SpEC.\n    constexpr double target_speed_decrease_factor = 0.125;\n    constexpr double time_tolerance_for_delta_r =\n        0.25 * time_tolerance_for_delta_r_in_danger;\n    ss << \"Current state AhSpeed. Delta radius in danger.\";\n    if ((crossing_time_info.t_char_speed.has_value() and\n         crossing_time_info.t_delta_radius.value_or(-1.0) >\n             info->damping_time * time_tolerance_for_delta_r) or\n        update_args.min_comoving_char_speed < 0.0) {\n      info->discontinuous_change_has_occurred = true;\n      info->target_char_speed = min_char_speed * target_speed_decrease_factor;\n      info->suggested_time_scale = std::min(\n          info->damping_time, crossing_time_info.t_delta_radius.value_or(\n                                  std::numeric_limits<double>::infinity()));\n\n      ss << \" Staying in AhSpeed.\\n\";\n      if (update_args.min_comoving_char_speed < 0.0) {\n        ss << \" Min comoving char speed \" << update_args.min_comoving_char_speed\n           << \" < 0.0\\n\";\n      } else {\n        ss << \" Char speed X time \" << crossing_time_info.t_char_speed.value()\n           << \" exists and delta radius X time \"\n           << crossing_time_info.t_delta_radius.value()\n           << \" > damping time * tolerance \"\n           << info->damping_time * time_tolerance_for_delta_r << \"\\n\";\n      }\n      ss << \" Target char speed = \" << info->target_char_speed << \"\\n\";\n      ss << \" Suggested timescale = \" << info->suggested_time_scale;\n    } else if (should_activate_inward_drift(update_args)) {\n      info->discontinuous_change_has_occurred = true;\n      info->state = std::make_unique<States::DeltaRDriftInward>();\n      info->suggested_time_scale = crossing_time_info.t_delta_radius;\n      info->target_char_speed = target_speed_for_inward_drift(\n          update_args.avg_distorted_normal_dot_unit_coord_vector,\n          update_args.min_char_speed,\n          update_args.inward_drift_velocity.value());\n      ss << \" Switching to DeltaRDriftInward.\\n\";\n      ss << \" Suggested timescale = \" << info->suggested_time_scale;\n    } else {\n      info->discontinuous_change_has_occurred = true;\n      info->state = std::make_unique<States::DeltaR>();\n      info->suggested_time_scale = crossing_time_info.t_delta_radius;\n      info->target_char_speed = 0.0;\n      ss << \" Switching to DeltaR.\\n\";\n      ss << \" Suggested timescale = \" << info->suggested_time_scale;\n    }\n  } else if (update_args.min_comoving_char_speed > 0.0 and\n             update_args.min_char_speed > 0.0 and\n             (not crossing_time_info.t_comoving_char_speed.has_value() or\n              (crossing_time_info.t_comoving_char_speed.value() >\n                   comoving_char_speed_to_damping_time_limit *\n                       info->damping_time and\n               comoving_decreasing_slower_than_char_speeds)) and\n             (update_args.min_char_speed >= info->target_char_speed or\n              min_comoving_char_speed > min_char_speed)) {\n    const bool drift_inward = should_activate_inward_drift(update_args);\n    info->discontinuous_change_has_occurred = true;\n    if (drift_inward) {\n      info->state = std::make_unique<States::DeltaRDriftInward>();\n      info->target_char_speed = target_speed_for_inward_drift(\n          update_args.avg_distorted_normal_dot_unit_coord_vector,\n          update_args.min_char_speed,\n          update_args.inward_drift_velocity.value());\n    } else {\n      info->state = std::make_unique<States::DeltaR>();\n    }\n    ss << \"Current state AhSpeed. Switching to \"\n       << (drift_inward ? \"DeltaRDriftInward\" : \"DeltaR\") << \".\\n\";\n    ss << \" Min char speed \" << update_args.min_char_speed << \" > 0\\n\";\n    ss << \" Min comoving char speed \" << update_args.min_comoving_char_speed\n       << \" > 0\\n\";\n    if (crossing_time_info.t_comoving_char_speed.has_value()) {\n      ss << \" Comoving char speed X time \"\n         << crossing_time_info.t_comoving_char_speed.value()\n         << \" > damping time * limit \"\n         << comoving_char_speed_to_damping_time_limit * info->damping_time\n         << \"\\n\";\n      ss << \" Comoving char speeds decreasing slower than char speeds\\n\";\n    }\n    if (min_comoving_char_speed > min_char_speed) {\n      ss << \" Min comoving char speed \" << min_comoving_char_speed\n         << \" > min char speed \" << min_char_speed << \"\\n\";\n    } else {\n      ss << \" Min char speed \" << update_args.min_char_speed\n         << \" >= target char speed \" << info->target_char_speed << \"\\n\";\n    }\n    if (not drift_inward) {  // Must happen after we print the value above\n      info->target_char_speed = 0.0;\n    }\n  } else if (update_args.average_radial_distance.has_value() and\n             update_args.average_radial_distance.value() >\n                 non_oscillation_drift_inward_factor *\n                     update_args.max_allowed_radial_distance.value_or(\n                         std::numeric_limits<double>::infinity()) and\n             not crossing_time_info.t_char_speed.has_value()) {\n    info->discontinuous_change_has_occurred = true;\n    ss << \"Current state AhSpeed. We have drifted too far in, so \"\n          \"we are switching to DeltaRDriftOutward.\\n\";\n    info->state = std::make_unique<States::DeltaRDriftOutward>();\n  } else {\n    ss << \"Current state AhSpeed. No change necessary. Staying in AhSpeed.\";\n  }\n  // If no 'if's are encountered above, then all the info parameters stay\n  // the same as they were.\n\n  return ss.str();\n}\n\ndouble AhSpeed::control_error(\n    const Info& info, const ControlErrorArgs& control_error_args) const {\n  const double Y00 = sqrt(0.25 / M_PI);\n  return (info.target_char_speed - control_error_args.min_char_speed) /\n         (Y00 * control_error_args.avg_distorted_normal_dot_unit_coord_vector);\n}\n\nPUP::able::PUP_ID AhSpeed::my_PUP_ID = 0;\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/AhSpeed.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::size::States {\nclass AhSpeed : public State {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Controls the velocity of the excision surface to avoid incoming \"\n      \"characteristic speeds. This is state 1 in SpEC.\"};\n  AhSpeed() = default;\n  std::string name() const override { return \"AhSpeed\"; }\n  size_t number() const override { return 1; }\n  std::unique_ptr<State> get_clone() const override;\n  std::string update(const gsl::not_null<Info*> info,\n                     const StateUpdateArgs& update_args,\n                     const CrossingTimeInfo& crossing_time_info) const override;\n  /// The return value is Q from Eq. 92 of \\cite Hemberger2012jz.\n  double control_error(\n      const Info& info,\n      const ControlErrorArgs& control_error_args) const override;\n\n  WRAPPED_PUPable_decl_template(AhSpeed);  // NOLINT\n  explicit AhSpeed(CkMigrateMessage* const /*msg*/) {}\n};\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ComovingCharSpeedDerivative.cpp\n  Info.cpp\n  AhSpeed.cpp\n  DeltaR.cpp\n  DeltaRDriftInward.cpp\n  DeltaRDriftOutward.cpp\n  DeltaRNoDrift.cpp\n  Error.cpp\n  Initial.cpp\n  RegisterDerivedWithCharm.cpp\n  StateHistory.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComovingCharSpeedDerivative.hpp\n  Info.hpp\n  State.hpp\n  StateHistory.hpp\n  AhSpeed.hpp\n  DeltaR.hpp\n  DeltaRDriftInward.hpp\n  DeltaRDriftOutward.hpp\n  DeltaRNoDrift.hpp\n  Error.hpp\n  Factory.hpp\n  Initial.hpp\n  RegisterDerivedWithCharm.hpp\n  Update.hpp\n  )\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/ComovingCharSpeedDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/ComovingCharSpeedDerivative.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace control_system::size {\nvoid comoving_char_speed_derivative(\n    const gsl::not_null<Scalar<DataVector>*> result, const double lambda_00,\n    const double dt_lambda_00, const double horizon_00,\n    const double dt_horizon_00, const double grid_frame_excision_sphere_radius,\n    const tnsr::i<DataVector, 3, Frame::Distorted>& excision_rhat,\n    const tnsr::i<DataVector, 3, Frame::Distorted>& excision_normal_one_form,\n    const Scalar<DataVector>& one_over_excision_normal_one_form_norm,\n    const tnsr::I<DataVector, 3, Frame::Distorted>&\n        distorted_components_of_grid_shift,\n    const tnsr::II<DataVector, 3, Frame::Distorted>&\n        inverse_spatial_metric_on_excision_boundary,\n    const tnsr::Ijj<DataVector, 3, Frame::Distorted>&\n        spatial_christoffel_second_kind,\n    const tnsr::i<DataVector, 3, Frame::Distorted>& deriv_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Distorted>& deriv_of_distorted_shift,\n    const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Distorted>&\n        inverse_jacobian_grid_to_distorted) {\n  const double Y00 = 0.25 * M_2_SQRTPI;\n\n  // Define temporary storage.\n  using excision_normal_vector_tag =\n      ::Tags::TempI<1, 3, Frame::Distorted, DataVector>;\n  using deriv_normal_one_form_tag =\n      ::Tags::Tempi<2, 3, Frame::Distorted, DataVector>;\n  TempBuffer<tmpl::list<excision_normal_vector_tag, deriv_normal_one_form_tag>>\n      buffer(get<0>(excision_rhat).size());\n  auto& excision_normal_vector = get<excision_normal_vector_tag>(buffer);\n  auto& deriv_normal_one_form = get<deriv_normal_one_form_tag>(buffer);\n\n  // excision_rhat is a tnsr:i when it is returned from a Strahlkorper.\n  // But excision_rhat is a coordinate quantity, not a physical tensor, so\n  // it can also be used as a tnsr::I.  Here we create a tnsr::I called\n  // excision_rhat_vector that points into excision_rhat.\n  const tnsr::I<DataVector, 3, Frame::Distorted> excision_rhat_vector{};\n  for (size_t i = 0; i < 3; ++i) {\n    // Is there a way to do this without the const_casts?\n    // Note that excision_rhat_vector must be non-const because\n    // we are changing it (by calling set_data_ref).\n    // And set_data_ref expects a non-const argument.\n    const_cast<DataVector*>(&excision_rhat_vector.get(i))  // NOLINT\n        ->set_data_ref(\n            const_cast<DataVector*>(&excision_rhat.get(i)));  // NOLINT\n  }\n\n  tenex::evaluate<ti::I>(\n      make_not_null(&excision_normal_vector),\n      excision_normal_one_form(ti::j) *\n          inverse_spatial_metric_on_excision_boundary(ti::J, ti::I));\n\n  // Fill result temporarily with all the terms in d/dlambda00 (n_hati)\n  // that are proportional to n_hati.\n  //   First, fill result with s_p s_j gamma^{pk} xi^i Gamma^j_{ki},\n  //   which is (almost) the last term in d/dlambda00(n_hati).\n  tenex::evaluate<>(\n      result, excision_normal_vector(ti::K) * excision_normal_one_form(ti::j) *\n                  excision_rhat_vector(ti::I) *\n                  spatial_christoffel_second_kind(ti::J, ti::k, ti::i));\n  //   Second, add to result s^k s_j InvJac^j_k / r_EB, which is\n  //   (almost) the second term in d/dlambda00(n_hati)\n  //     Note that the for the contraction s_j InvJac^j_k, the j on the s is\n  //     a distorted-frame index but the j on the InvJac is a grid-frame index.\n  //     This is really weird but it happens because some of the things are\n  //     not tensors. (In particular, the map itself looks like\n  //     x^{i_distorted} = x^{i_grid} * stuff, which equates grid and\n  //     distorted incides).\n  for (size_t j = 0; j < 3; ++j) {\n    for (size_t k = 0; k < 3; ++k) {\n      get(*result) += excision_normal_vector.get(k) *\n                      excision_normal_one_form.get(j) *\n                      inverse_jacobian_grid_to_distorted.get(j, k) /\n                      grid_frame_excision_sphere_radius;\n    }\n  }\n  //   Third, scale by norm^3 so that result contains\n  //   1/a (n^k n_j InvJac^j_k / r_EB + n_p n_j gamma^{pk} xi^i Gamma^j_{ki}),\n  //   which is (almost) the last two terms of d/dlambda00(n_hati).\n  get(*result) *= cube(get(one_over_excision_normal_one_form_norm));\n\n  // Set deriv_normal_one_form to the first two terms of d/dlambda00 (n_hati).\n  // Possible memory optimization: excision_normal_vector isn't used anymore,\n  // so that storage could be used for deriv_normal_one_form.\n  tenex::evaluate<ti::i>(make_not_null(&deriv_normal_one_form),\n                         -Y00 * (*result)() * excision_normal_one_form(ti::i));\n\n  // Add the first term to deriv_normal_one_form, so that deriv_normal_one_form\n  // contains the entire d/dlambda00 (n_hati).\n  //     Note that the for the contraction s_j InvJac^j_i, the j on the s is\n  //     a distorted-frame index but the j on the InvJac is a grid-frame index.\n  //     This is really weird but it happens because some of the things are\n  //     not tensors. (In particular, the map itself looks like\n  //     x^{i_distorted} = x^{i_grid} * stuff, which equates grid and\n  //     distorted incides).\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      deriv_normal_one_form.get(i) +=\n          excision_normal_one_form.get(j) *\n          inverse_jacobian_grid_to_distorted.get(j, i) * Y00 *\n          get(one_over_excision_normal_one_form_norm) /\n          grid_frame_excision_sphere_radius;\n    }\n  }\n\n  // Now put the actual result, i.e. d/dlambda00 (v_c), into result.\n  // Do this term by term.  Ignore the overall factor of Y00 until the\n  // end.\n  //   First do the dt_horizon_00 term (without the normalization)\n  tenex::evaluate<>(result, -excision_normal_one_form(ti::i) *\n                                excision_rhat_vector(ti::I) * dt_horizon_00 /\n                                horizon_00);\n  //   Next do the shift term (again without the normalization)\n  tenex::update<>(result,\n                  (*result)() - excision_normal_one_form(ti::i) *\n                                    excision_rhat_vector(ti::J) *\n                                    deriv_of_distorted_shift(ti::j, ti::I));\n\n  // Put in the norm factor.\n  get(*result) *= get(one_over_excision_normal_one_form_norm);\n\n  // Add the dlapse term (without the Y00 factor).\n  tenex::update<>(\n      result, (*result)() + deriv_lapse(ti::i) * excision_rhat_vector(ti::I));\n\n  // Put in the Y00 factor.\n  get(*result) *= Y00;\n\n  // Add the final term to result\n  tenex::update<>(\n      result,\n      (*result)() +\n          deriv_normal_one_form(ti::i) *\n              (Y00 * dt_lambda_00 * excision_rhat_vector(ti::I) +\n               distorted_components_of_grid_shift(ti::I) -\n               excision_rhat_vector(ti::I) * (dt_horizon_00 / horizon_00) *\n                   (Y00 * lambda_00 - grid_frame_excision_sphere_radius)));\n}\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/ComovingCharSpeedDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::size {\n\n/*!\n * \\brief Computes the derivative of the comoving characteristic speed\n * with respect to the size map parameter.\n *\n * \\param result the derivative of the comoving char speed\n *        \\f$d v_c/d\\lambda_{00}\\f$, which is computed here using\n *        Eq. (\\f$\\ref{eq:result}\\f$).\n * \\param lambda_00 the map parameter \\f$\\lambda_{00}\\f$. This is the usual\n *        spherical harmonic coefficient, not a Spherepack value.\n * \\param dt_lambda_00 the time derivative of the map parameter\n * \\param horizon_00 the average coefficient of the horizon \\f$\\hat{S}_{00}\\f$.\n *        This is the usual spherical harmonic coefficient, not a Spherepack\n *        value.\n * \\param dt_horizon_00 the time derivative of horizon_00\n * \\param grid_frame_excision_sphere_radius radius of the excision boundary\n *        in the grid frame, \\f$r_{\\mathrm{EB}}\\f$.\n * \\param excision_rhat the direction cosine \\f$\\xi_\\hat{i}\\f$. Not a\n *        spacetime tensor: it is raised/lowered with \\f$\\delta_{ij}\\f$\n * \\param excision_normal_one_form the unnormalized one-form\n *        \\f$\\hat{s}_\\hat{i}\\f$\n * \\param one_over_excision_normal_one_form_norm one over the norm of the\n *        one-form \\f$a\\f$\n * \\param distorted_components_of_grid_shift the quantity\n *        \\f$\\beta^i \\frac{\\partial x^\\hat{i}}{\\partial x_i}\\f$\n *        evaluated on the excision boundary.  This is not the shift in\n *        the distorted frame.\n * \\param inverse_spatial_metric_on_excision_boundary metric in\n *        the distorted frame.\n * \\param spatial_christoffel_second_kind the Christoffel symbols\n *        \\f$\\Gamma^\\hat{k}_{\\hat{i}\\hat{j}}\\f$\n * \\param deriv_lapse the spatial derivative of the lapse\n *        \\f$\\partial_\\hat{i} \\alpha\\f$\n * \\param deriv_of_distorted_shift the spatial derivative of the shift in the\n *        distorted frame\n *        \\f$\\partial_\\hat{j} \\hat{\\beta}^\\hat{i}\\f$. This is not the\n *        derivative of distorted_components_of_grid_shift.\n * \\param inverse_jacobian_grid_to_distorted the quantity\n *        \\f$J^i_\\hat{k}=\\partial_\\hat{k} x^i\\f$,\n *        where \\f$x^i\\f$ are the grid frame coordinates and\n *        \\f$x^{\\hat k}\\f$ are the distorted frame coordinates.\n * ## Background\n *\n * The characteristic speed on the excision boundary is\n * \\f{align}\n *     v &= -\\alpha + n_i\\beta^i\n * \\f}\n * where \\f$\\alpha\\f$ is the lapse (invariant under frame transformations),\n * \\f$\\beta^i\\f$ is the grid-frame shift, and \\f$n_i\\f$ is the metric-normalized\n * **outward-pointing** (i.e. pointing out of the black hole,\n * toward larger radius)\n * normal one-form to the excision boundary in the grid frame.\n * (Note that the usual expression for the characteristic speed, as in\n * eq. 87 of \\cite Hemberger2012jz, has\n * a minus sign and defines \\f$n_i\\f$ as the inward-pointing (i.e. out of the\n * computational domain) normal; here\n * we have a plus sign and we define \\f$n_i\\f$ as outward-pointing because\n * the outward-pointing normal is passed into comoving_char_speed_derivative.)\n *\n * The size/shape map at the excision boundary is given by Eq. 72 of\n * \\cite Hemberger2012jz :\n * \\f{align}\n *   \\hat{x}^i &= \\frac{x^i}{r_{\\mathrm{EB}}}\n *      \\left(1 - \\lambda_{00} Y_{00}\n *      -\\sum_{\\ell>0} Y_{\\ell m} \\lambda_{\\ell m}\\right),\n *   \\label{eq:map}\n * \\f}\n * where \\f$\\hat{x}^i\\f$ are the distorted-frame coordinates and \\f$x^i\\f$\n * are the grid-frame coordinates, and where we have separated the \\f$\\ell=0\\f$\n * piece from the sum.\n * Here \\f$Y_{\\ell m}\\f$ are\n * spherical harmonics, \\f$\\lambda_{\\ell m}\\f$ are\n * the map parameters, and \\f$r_{\\mathrm{EB}}\\f$ is the radius of the\n * excision boundary in the\n * grid frame (where the excision boundary is a sphere). The final term with\n * the sum over $\\ell>0$ is independent of \\f$\\lambda_{00}\\f$,\n * and will not be important\n * because below we will be differentiating the map with respect\n * to \\f$\\lambda_{00}\\f$.\n *\n * The comoving characteristic speed is given by rewriting Eq. 98\n * of \\cite Hemberger2012jz in terms of the distorted-frame shift:\n * \\f{align}\n *     v_c &= -\\alpha +\\hat{n}_\\hat{i}\\hat{\\beta}^\\hat{i}\n *           - Y_{00} \\hat{n}_{\\hat i} \\xi^{\\hat i}\n *           \\left[ \\dot{\\hat{S}}_{00} (\\lambda_{00}\n *                   - r_{\\mathrm{EB}}/Y_{00}) / \\hat{S}_{00}\n *           + \\frac{1}{Y_{00}} \\sum_{\\ell>0} Y_{\\ell m} \\dot{\\lambda}_{\\ell m}\n *         \\right], \\\\\n *         &= -\\alpha +\\hat{n}_\\hat{i}\\beta^\\hat{i}\n *           - Y_{00} \\hat{n}_{\\hat i} \\xi^{\\hat i}\n *           \\left[ \\dot{\\hat{S}}_{00} (\\lambda_{00}\n *                   - r_{\\mathrm{EB}}/Y_{00}) / \\hat{S}_{00}\n *           -\\dot{\\lambda}_{00} \\right], \\label{eq:comovingspeed}\n * \\f}\n * where in the last line we have rewritten $\\hat{\\beta}^\\hat{i}$\n * in terms of $\\beta^\\hat{i}$ (see Eq. (\\f$\\ref{eq:framecompsshiftdef}\\f$)\n * below) and we have substituted\n * the time derivative of Eq. (\\f$\\ref{eq:map}\\f$).\n * Here \\f$\\dot{\\lambda}_{00}\\f$ is the time derivative of\n * \\f$\\lambda_{00}\\f$, and\n * \\f$\\hat{S}_{00}\\f$ is the constant spherical-harmonic coefficient of the\n * horizon and \\f$\\dot{\\hat{S}}_{00}\\f$ is its time derivative.\n * The symbol \\f$\\xi^{\\hat i}\\f$ is\n * a direction cosine, i.e. \\f$x^i/r_{\\mathrm{EB}}\\f$ evaluated on the\n * excision boundary, which is the same as\n * \\f$\\hat{x}^i/\\hat{r}_{\\mathrm{EB}}\\f$ evaluated on the excision boundary\n * because the size and shape maps preserve angles.  Note that\n * \\f$r_{\\mathrm{EB}}\\f$ is a constant but \\f$\\hat{r}_{\\mathrm{EB}}\\f$ is\n * a function of angles.  Note also\n * that \\f$\\xi^{\\hat i}\\f$ is **not** a vector; it\n * is a coordinate quantity. In particular,\n * the lower-index \\f$\\xi_{\\hat i}\\f$ is \\f$\\delta_{ij}x^j/r_{\\mathrm{EB}}\\f$.\n * The non-vectorness of \\f$\\xi^{\\hat i}\\f$ (and of \\f$x^i\\f$ itself\n * in Eq. (\\f$\\ref{eq:map}\\f$)) might cause some confusion when using the\n * Einstein summation convention; we attempt to alleviate that confusion by\n * never using the lower-index \\f$\\xi_{\\hat i}\\f$ and by keeping\n * \\f$\\delta_{ij}\\f$ in formulas below.\n * The normal\n * \\f$\\hat{n}_\\hat{i}\\f$ is the same as $n_i$\n * transformed into the distorted frame, that is\n * \\f$\\hat{n}_\\hat{i} = n_j \\partial x^j/\\partial\\hat{x}^\\hat{i}\\f$.\n * We have put a hat on \\f$\\hat{n}\\f$ in addition to putting a hat on\n * its index\n * (despite the usual convention that tensors have\n * decorations on indices and not on the tensors themselves)\n * to reduce later ambiguities\n * in notation that arise because\n * Eq. (\\f$\\ref{eq:map}\\f$) has the same index on both sides of the equation\n * and because \\f$\\xi^{\\hat i}\\f$ and \\f$x^i\\f$ are not tensors.\n * The quantity \\f$\\beta^\\hat{i}\\f$ in Eq. (\\f$\\ref{eq:comovingspeed}\\f$)\n * is the distorted-frame\n * component of the grid-frame shift, defined by\n * \\f{align}\n * \\beta^\\hat{i} &= \\beta^i \\frac{\\partial \\hat{x}^\\hat{i}}{\\partial x^i}.\n * \\label{eq:shiftyquantity}\n * \\f}\n * This is **not** the shift in the distorted frame \\f$\\hat{\\beta}^\\hat{i}\\f$,\n * because the shift does\n * not transform like a spatial tensor under the maps.\n *\n * If the comoving characteristic speed \\f$v_c\\f$ is negative and remains\n * negative forever, then size control will fail.  Therefore \\f$v_c\\f$ is\n * used in making decisions in size control. We care about\n * \\f$d v_c/d\\lambda_{00}\\f$ because the sign of that quantity tells us\n * whether \\f$v_c\\f$ will increase or decrease when we increase or decrease\n * the map parameter \\f$\\lambda_{00}\\f$; this information will be used to\n * decide whether to transition between different size control states.\n *\n * ## Derivation of derivative of comoving characteristic speed\n *\n * This function computes\n * \\f$d v_c/d\\lambda_{00}\\f$ on the excision boundary, where the total\n * derivative means that all other map parameters\n * (like \\f$\\lambda_{\\ell m}\\f$ for \\f$\\ell>0\\f$) are held fixed, and the\n * coordinates of the excision boundary (the grid coordinates) are held fixed.\n * We also hold fixed \\f$\\dot{\\lambda}_{00}\\f$ because we are interested\n * in how $v_c$ changes from a configuration with a given\n * \\f$\\lambda_{00}\\f$ and \\f$\\dot{\\lambda}_{00}\\sim 0\\f$ to another\n * configuration with a different nearby \\f$\\lambda_{00}\\f$ and also with\n * \\f$\\dot{\\lambda}_{00}\\sim 0\\f$.\n *\n * Here we derive an expression for \\f$d v_c/d\\lambda_{00}\\f$.\n * This expression will be\n * complicated, mostly because of the normals \\f$\\hat{n}_\\hat{i}\\f$ that appear\n * in Eq. (\\f$\\ref{eq:comovingspeed}\\f$) and because of the Jacobians.\n *\n * ### Derivative of the Jacobian\n *\n * First, note that by differentiating Eq. (\\f$\\ref{eq:map}\\f$) we obtain\n * \\f{align}\n *   \\frac{d\\hat{x}^i}{d\\lambda_{00}}\n *      &= - \\frac{x^i Y_{00}}{r_{\\mathrm{EB}}} \\\\\n *      &= -\\xi^i Y_{00},\n * \\f}\n * where the last line follows from the definition of the direction cosines.\n *\n * The Jacobian of the map is\n * \\f{align}\n *  \\frac{\\partial \\hat{x}^i}{\\partial x^j}\n *    &= (1 - \\lambda_{00} Y_{00}/r_{\\mathrm{EB}} + B) \\delta^i_j\n *    + x^i \\frac{\\partial B}{\\partial x^j},\n * \\f}\n * where \\f$B\\f$ represents the term with the sum over \\f$\\ell>0\\f$\n * in Eq. (\\f$\\ref{eq:map}\\f$); this term is independent\n * of \\f$\\lambda_{00}\\f$.\n * Therefore, we have\n * \\f{align}\n *  \\frac{d}{d\\lambda_{00}}\\frac{\\partial \\hat{x}^i}{\\partial x^j} &=\n *  -\\frac{Y_{00}}{r_{\\mathrm{EB}}} \\delta^i_j.\n * \\label{eq:derivjacobian}\n * \\f}\n *\n * But we want the derivative of the inverse Jacobian, not the forward\n * Jacobian. By taking the derivative of the identity\n * \\f{align}\n *  \\frac{\\partial \\hat{x}^\\hat{i}}{\\partial x^k}\n *  \\frac{\\partial x^k}{\\partial \\hat{x}^\\hat{j}} &= \\delta^\\hat{i}_\\hat{j}\n * \\f}\n * and by using Eq. (\\f$\\ref{eq:derivjacobian}\\f$) we can derive\n * \\f{align}\n *  \\frac{d}{d\\lambda_{00}}\\frac{\\partial x^i}{\\partial \\hat{x}^j} &=\n *  +\\frac{Y_{00}}{r_{\\mathrm{EB}}}\n *   \\frac{\\partial x^i}{\\partial \\hat{x}^k}\n *   \\frac{\\partial x^k}{\\partial \\hat{x}^j}.\n *   \\label{eq:strangederivjacobian}\n * \\f}\n * Note that the right-hand side of Eq. (\\f$\\ref{eq:strangederivjacobian}\\f$)\n * has two inverse Jacobians contracted with each other, which is not\n * the same as \\f$\\delta^i_j\\f$.\n *\n * ### Derivative of a function of space\n *\n * Assume we have an arbitrary function of space \\f$f(\\hat{x}^i)\\f$.\n * Here we treat \\f$f\\f$ as a function of the distorted-frame\n * coordinates \\f$\\hat{x}^i\\f$ and not a function of the grid-frame\n * coordinates.  This is because we consider the metric functions to be defined\n * in the inertial frame (and equivalently for our purposes the functions are\n * defined in the distorted frame because the distorted-to-inertial map\n * is independent of \\f$\\lambda_{00}\\f$), and we consider \\f$\\lambda_{00}\\f$\n * a parameter in a map that moves the grid with respect to these\n * distorted-frame metric functions.\n * The derivative of \\f$f\\f$ can be written\n * \\f{align}\n *  \\frac{d}{d\\lambda_{00}}f &= \\frac{\\partial f}{\\partial \\hat{x}^i}\n *  \\frac{d \\hat{x}^i}{d\\lambda_{00}}\\\\\n *  &= -\\xi^\\hat{i} Y_{00} \\frac{\\partial f}{\\partial \\hat{x}^i}.\n * \\label{eq:derivf}\n * \\f}\n * This is how we will evaluate derivatives of metric functions like\n * the lapse.\n *\n * ### Derivative of the distorted-frame components of the grid-frame shift.\n *\n * To differentiate the quantity defined by Eq. (\\f$\\ref{eq:shiftyquantity}\\f$)\n * note that\n * \\f{align}\n * \\beta^\\hat{i} &=\n *   \\beta^i \\frac{\\partial \\hat{x}^\\hat{i}}{\\partial x^i} \\\\\n *   &= \\hat{\\beta}^\\hat{i} + \\frac{\\partial \\hat{x}^\\hat{i}}{\\partial t},\n *  \\label{eq:framecompsshiftdef}\n * \\f}\n * where \\f$\\hat{\\beta}^\\hat{i} \\equiv \\alpha^2 g^{\\hat{0}\\hat{i}}\\f$ is\n * the shift in the distorted frame.\n * From the map, Eq. (\\f$\\ref{eq:map}\\f$), we see that\n * \\f{align}\n *  \\frac{d}{d\\lambda_{00}} \\frac{\\partial \\hat{x}^\\hat{i}}{\\partial t} &=0,\n * \\f}\n * because there is no remaining \\f$\\lambda_{00}\\f$ in\n * \\f$\\frac{\\partial \\hat{x}^\\hat{i}}{\\partial t}\\f$.\n * So\n * \\f{align}\n * \\frac{d}{d\\lambda_{00}}\\beta^\\hat{i} &=\n * \\frac{d}{d\\lambda_{00}} \\hat{\\beta}^\\hat{i} \\\\\n * &= -\\xi^\\hat{j} Y_{00} \\partial_\\hat{j} \\hat{\\beta}^\\hat{i},\n * \\f}\n * where we have used Eq. (\\f$\\ref{eq:derivf}\\f$) in the last line.\n * Note that we cannot use Eq. (\\f$\\ref{eq:derivf}\\f$) on\n * \\f$\\beta^\\hat{i}\\f$ directly,\n * because \\f$\\beta^\\hat{i}\\f$ depends in a complicated\n * way on the grid-to-distorted map. In particular, we will be evaluating\n * \\f$\\partial_\\hat{j} \\hat{\\beta}^\\hat{i}\\f$ numerically, and numerical\n * spatial derivatives \\f$\\partial_\\hat{j} \\hat{\\beta}^\\hat{i}\\f$ are not\n * the same as numerical spatial derivatives\n * \\f$\\partial_\\hat{j} \\beta^\\hat{i}\\f$.\n *\n * ### Derivative of the normal one-form\n *\n * The normal to the surface is the most complicated expression in\n * Eq. (\\f$\\ref{eq:comovingspeed}\\f$), because of how it depends on the\n * map and on the metric.\n * The grid-frame un-normalized outward-pointing one-form\n * to the excision boundary is\n * \\f{align}\n *    s_i &= \\xi^j \\delta_{ij},\n * \\f}\n * because the excision boundary is a sphere of fixed radius in the\n * grid frame. Therefore\n * \\f$s_i\\f$ doesn't depend on \\f$\\lambda_{00}\\f$.\n *\n * The normalized one-form \\f$\\hat{n}_\\hat{i}\\f$ is given by\n * \\f{align}\n *   \\hat{n}_\\hat{i} &= \\frac{\\hat{s}_{\\hat i}}{a},\n * \\f}\n * where\n * \\f{align}\n *   \\hat{s}_{\\hat i} &= s_i \\frac{\\partial x^i}{\\partial \\hat{x}^{\\hat i}},\\\\\n *   a^2 &= \\hat{s}_{\\hat i} \\hat{s}_{\\hat j} \\gamma^{\\hat{i} \\hat{j}}.\n * \\f}\n * Here \\f$\\gamma^{\\hat{i} \\hat{j}}\\f$ is the inverse 3-metric in the\n * distorted frame.  Again, to avoid ambiguity later,\n * we have put hats on \\f$n\\f$ and \\f$s\\f$, despite\n * the usual convention that when transforming tensors one puts\n * hats on the indices and not on the tensors.\n *\n * Now\n * \\f{align}\n *  \\frac{d}{d\\lambda_{00}} \\hat{s}_{\\hat i} &=\n *  \\frac{Y_{00}}{r_{\\mathrm{EB}}}\n *  \\hat{s}_k\\frac{\\partial x^k}{\\partial \\hat{x}^\\hat{i}}, \\\\\n *  \\frac{d}{d\\lambda_{00}} a^2 &= 2 \\frac{Y_{00}}{r_{\\mathrm{EB}}}\n *  \\hat{s}_k\\frac{\\partial x^k}{\\partial \\hat{x}^\\hat{i}}\n *  \\hat{s}_{\\hat j} \\gamma^{\\hat{i} \\hat{j}}\n *  + \\hat{s}_{\\hat i} \\hat{s}_{\\hat j}\n *  \\gamma^{\\hat{i} \\hat{k}} \\gamma^{\\hat{j} \\hat{l}}\n *  \\xi^\\hat{m} Y_{00} \\partial_{\\hat m} \\gamma_{\\hat{k} \\hat{l}}.\n * \\f}\n * Here we have used Eq. (\\f$\\ref{eq:strangederivjacobian}\\f$) to differentiate\n * the Jacobian, and Eq. (\\f$\\ref{eq:derivf}\\f$) to differentiate the 3-metric.\n * We have also refrained from raising and lowering indices\n * on \\f$\\hat{n}_\\hat{i}\\f$, \\f$\\hat{s}_\\hat{i}\\f$, and \\f$\\xi^\\hat{i}\\f$\n * to alleviate potential confusion over whether to raise or lower using\n * \\f$\\gamma_{\\hat{i} \\hat{j}}\\f$ or using \\f$\\delta_{\\hat{i}\\hat{j}}\\f$.\n * The factor \\f$\\hat{s}_k \\partial x^k/\\partial \\hat{x}^\\hat{i}\\f$\n * is unusal and is not a tensor\n * (\\f$\\hat{s}_k\\f$ is a tensor but the Jacobian it is being multiplied by\n * is the inverse of the one that would transform it into a different frame);\n * this factor arises because some quantities being differentiated are\n * not tensors.\n *\n * Given the above, the derivative of the normalized normal one-form is\n * \\f{align}\n *  \\frac{d}{d\\lambda_{00}} \\hat{n}_{\\hat i} &=\n *  \\frac{1}{a}\\frac{d}{d\\lambda_{00}} \\hat{s}_{\\hat i}\n *  - \\hat{s}_{\\hat i} \\frac{1}{2a^3}\n *  \\frac{d}{d\\lambda_{00}} a^2\\\\\n *  &=\n *  \\hat{s}_i\\frac{Y_{00}}{a r_{\\mathrm{EB}}}\n *    \\frac{\\partial x^i}{\\partial \\hat{x}^\\hat{i}}\n *  - \\hat{s}_{\\hat i} \\frac{1}{a^3} \\hat{s}_i\\frac{Y_{00}}{r_{\\mathrm{EB}}}\n *    \\frac{\\partial x^i}{\\partial \\hat{x}^\\hat{k}}\n *    \\hat{s}_{\\hat j} \\gamma^{\\hat{k} \\hat{j}}\n *  - \\hat{s}_{\\hat i} \\frac{Y_{00}}{2a^3} \\hat{s}_{\\hat p}\n *  \\hat{s}_{\\hat j} \\gamma^{\\hat{p} \\hat{k}}\n *  \\gamma^{\\hat{j} \\hat{l}}\n *  \\xi^\\hat{m} \\partial_{\\hat m} \\gamma_{\\hat{k} \\hat{l}} \\\\\n *  &=\n *  \\hat{n}_i\n *  \\frac{\\partial x^i}{\\partial \\hat{x}^\\hat{k}}\n *  \\frac{Y_{00}}{r_{\\mathrm{EB}}}\n *  (\\delta^\\hat{k}_\\hat{i} - \\hat{n}^\\hat{k} \\hat{n}_\\hat{i})\n *  - \\hat{s}_{\\hat i} \\frac{Y_{00}}{2a^3} \\hat{s}_{\\hat p}\n *  \\hat{s}_{\\hat j} \\gamma^{\\hat{p} \\hat{k}}\n *  \\gamma^{\\hat{j} \\hat{l}}\n *  \\xi^\\hat{m} \\partial_{\\hat m} \\gamma_{\\hat{k} \\hat{l}}\n *  \\label{eq:dnormal} \\\\\n *  &=\n *  \\hat{n}_i\n *  \\frac{\\partial x^i}{\\partial \\hat{x}^\\hat{k}}\n *  \\frac{Y_{00}}{r_{\\mathrm{EB}}}\n *  (\\delta^\\hat{k}_\\hat{i} - \\hat{n}^\\hat{k} \\hat{n}_\\hat{i})\n *  - Y_{00} \\hat{n}_{\\hat i} \\hat{n}_{\\hat p}\n *  \\hat{n}_{\\hat j} \\gamma^{\\hat{p} \\hat{k}}\n *  \\xi^\\hat{m} \\Gamma^\\hat{j}_{\\hat{k} \\hat{m}}\n *  \\label{eq:dnormalgamma},\n * \\f}\n * where we have eliminated \\f$\\hat{s}_{\\hat i}\\f$ and \\f$a\\f$ in favor\n * of \\f$\\hat{n}_{\\hat i}\\f$\n * and we have substituted 3-Christoffel symbols for\n * spatial derivatives of the 3-metric (and the factor of 2 on the penultimate\n * line has been absorbed into the 3-Christoffel symbol on the last line).\n * Note that the last term in Eq.\n * (\\f$\\ref{eq:dnormalgamma}\\f$) could also be derived by differentiating\n * \\f$\\hat{n}_\\hat{i}\\hat{n}_\\hat{j}\\gamma^{\\hat{i}\\hat{j}}=1\\f$.\n * The first term in Eq. (\\f$\\ref{eq:dnormalgamma}\\f$) is strange because\n * the inverse Jacobian (as opposed to the forward Jacobian) is contracted\n * with \\f$\\hat{n}_i\\f$, so that is not a tensor transformation.\n *\n * We can now differentiate Eq. (\\f$\\ref{eq:comovingspeed}\\f$) to obtain\n * \\f{align}\n *  \\frac{d}{d\\lambda_{00}} v_c &=\n *  \\xi^\\hat{i} Y_{00} \\partial_\\hat{i} \\alpha\n *   +\\left[ \\beta^\\hat{i}\n *   - Y_{00} \\xi^\\hat{i} \\dot{\\hat{S}}_{00} (\\lambda_{00}\n *                   - r_{\\mathrm{EB}}/Y_{00}) / \\hat{S}_{00}\n *           + Y_{00} \\xi^\\hat{i}\\dot{\\lambda}_{00} \\right]\n *  \\frac{d}{d\\lambda_{00}} \\hat{n}_{\\hat i} \\nonumber \\\\\n *  &- \\hat{n}_{\\hat i} \\xi^\\hat{j} Y_{00} \\partial_\\hat{j} \\hat{\\beta}^\\hat{i}\n *  - Y_{00} \\hat{n}_{\\hat i} \\xi^{\\hat i} \\dot{\\hat{S}}_{00}/\\hat{S}_{00},\n * \\label{eq:result}\n * \\f}\n * where \\f$\\frac{d}{d\\lambda_{00}} \\hat{n}_{\\hat i}\\f$ is given by\n * Eq. (\\f$\\ref{eq:dnormalgamma}\\f$).\n */\nvoid comoving_char_speed_derivative(\n    gsl::not_null<Scalar<DataVector>*> result, double lambda_00,\n    double dt_lambda_00, double horizon_00, double dt_horizon_00,\n    double grid_frame_excision_sphere_radius,\n    const tnsr::i<DataVector, 3, Frame::Distorted>& excision_rhat,\n    const tnsr::i<DataVector, 3, Frame::Distorted>& excision_normal_one_form,\n    const Scalar<DataVector>& one_over_excision_normal_one_form_norm,\n    const tnsr::I<DataVector, 3, Frame::Distorted>&\n        distorted_components_of_grid_shift,\n    const tnsr::II<DataVector, 3, Frame::Distorted>&\n        inverse_spatial_metric_on_excision_boundary,\n    const tnsr::Ijj<DataVector, 3, Frame::Distorted>&\n        spatial_christoffel_second_kind,\n    const tnsr::i<DataVector, 3, Frame::Distorted>& deriv_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Distorted>& deriv_of_distorted_shift,\n    const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Distorted>&\n        inverse_jacobian_grid_to_distorted);\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/DeltaR.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n\n#include <memory>\n#include <optional>\n#include <sstream>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace control_system::size::States {\n\nstd::unique_ptr<State> DeltaR::get_clone() const {\n  return std::make_unique<DeltaR>(*this);\n}\n\nstd::string DeltaR::update(const gsl::not_null<Info*> info,\n                           const StateUpdateArgs& update_args,\n                           const CrossingTimeInfo& crossing_time_info) const {\n  // If update_args.control_error_delta_r is larger than\n  // delta_r_control_error_threshold (and neither char speed nor\n  // delta radius is in danger), then the timescale is decreased to\n  // keep the control error small. This behavior is similar to what\n  // TimecaleTuners do, but is triggered only in some situations. The\n  // value of 1e-3 was chosen by trial and error in SpEC but it might\n  // be helpful to decrease this value in the future if size control\n  // needs to be very tight.\n  constexpr double delta_r_control_error_threshold = 1.e-3;\n\n  // Note that delta_radius_is_in_danger and char_speed_is_in_danger\n  // can be different for different States.\n\n  // The value of 0.99 was chosen by trial and error in SpEC.\n  // It should be slightly less than unity but nothing should be\n  // sensitive to small changes in this value.\n  constexpr double time_tolerance_for_delta_r_in_danger = 0.99;\n  const bool delta_radius_is_in_danger =\n      crossing_time_info.horizon_will_hit_excision_boundary_first and\n      crossing_time_info.t_delta_radius.value_or(\n          std::numeric_limits<double>::infinity()) <\n          info->damping_time * time_tolerance_for_delta_r_in_danger;\n  const bool char_speed_is_in_danger =\n      crossing_time_info.char_speed_will_hit_zero_first and\n      crossing_time_info.t_char_speed.value_or(\n          std::numeric_limits<double>::infinity()) < info->damping_time and\n      not delta_radius_is_in_danger;\n\n  // inward_drift_limit_in_danger means we are about\n  // to cross the maximum allowed deltaR, and we are not in\n  // the situation where we should exit state DeltaRNoDrift.\n  // (the latter is important because we don't want to keep entering\n  //  and exiting DeltaRNoDrift repeatedly)\n  // In SpEC this variable is called \"State2MaxLimitInDanger\".\n  const bool inward_drift_limit_in_danger =\n      crossing_time_info.t_drift_limit_delta_radius.value_or(\n          std::numeric_limits<double>::infinity()) < info->damping_time and\n      update_args.min_allowed_radial_distance.has_value() and\n      not ok_to_return_to_state_deltar(update_args);\n\n  // This factor is present in SpEC, and it is used to prevent\n  // oscillations between states.  The value was chosen in SpEC, but\n  // nothing should be sensitive to small changes in this value as\n  // long as it is slightly greater than unity.\n  constexpr double non_oscillation_drift_outward_factor = 1.1;\n\n  std::stringstream ss{};\n\n  if (char_speed_is_in_danger) {\n    ss << \"Current state DeltaR. Char speed in danger.\";\n    if (crossing_time_info.t_comoving_char_speed.has_value() or\n        update_args.min_comoving_char_speed < 0.0) {\n      // Comoving char speed is negative or threatening to cross zero, so\n      // staying in DeltaR mode will not work.  So switch to AhSpeed mode.\n\n      // This factor prevents oscillating between states Initial and\n      // AhSpeed.  It needs to be slightly greater than unity, but the\n      // control system should not be sensitive to the exact\n      // value. The value of 1.01 was chosen arbitrarily in SpEC and\n      // never needed to be changed.\n      constexpr double non_oscillation_factor = 1.01;\n      info->discontinuous_change_has_occurred = true;\n      info->state = std::make_unique<States::AhSpeed>();\n      info->target_char_speed =\n          update_args.min_char_speed * non_oscillation_factor;\n      ss << \" Switching to AhSpeed.\\n\";\n      ss << \" Target char speed = \" << info->target_char_speed << \"\\n\";\n    } else {\n      ss << \" Staying in DeltaR.\\n\";\n    }\n    // If the comoving char speed is positive and is not about to\n    // cross zero, staying in DeltaR mode will rescue the speed\n    // automatically (since it drives char speed to comoving char\n    // speed).  But we should decrease the timescale in any case.\n    info->suggested_time_scale = crossing_time_info.t_char_speed;\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (delta_radius_is_in_danger) {\n    info->suggested_time_scale = crossing_time_info.t_delta_radius;\n    ss << \"Current state DeltaR. Delta radius in danger. Staying in DeltaR.\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (update_args.min_comoving_char_speed > 0.0 and\n             std::abs(update_args.control_error_delta_r) >\n                 delta_r_control_error_threshold) {\n    // delta_r_state_decrease_factor should be slightly less than unity.\n    // The value of 0.99 below was chosen arbitrarily in SpEC and never\n    // needed to be changed.\n    constexpr double delta_r_state_decrease_factor = 0.99;\n    info->suggested_time_scale =\n        info->damping_time * delta_r_state_decrease_factor;\n    ss << \"Current state DeltaR. Min comoving char speed \"\n       << update_args.min_comoving_char_speed\n       << \" > 0 and abs(control_error_delta_r) \"\n       << std::abs(update_args.control_error_delta_r) << \" > threshold \"\n       << delta_r_control_error_threshold << \". Staying in DeltaR.\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (should_transition_from_state_delta_r_to_inward_drift(\n                 crossing_time_info.t_drift_limit, info->damping_time,\n                 update_args)) {\n    info->discontinuous_change_has_occurred = true;\n    info->state = std::make_unique<States::DeltaRDriftInward>();\n    info->target_char_speed = target_speed_for_inward_drift(\n        update_args.avg_distorted_normal_dot_unit_coord_vector,\n        update_args.min_char_speed, update_args.inward_drift_velocity.value());\n    ss << \"Current state DeltaR. \"\n          \"Horizon too close to excision boundary. Switching to \"\n          \"DeltaRDriftInward\";\n  } else if (inward_drift_limit_in_danger) {\n    info->suggested_time_scale = crossing_time_info.t_drift_limit_delta_radius;\n    ss << \"Current state DeltaR. Inward drift limit in danger. Staying \"\n          \"in DeltaR.\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (update_args.average_radial_distance.has_value() and\n             update_args.average_radial_distance.value() >\n                 non_oscillation_drift_outward_factor *\n                     update_args.max_allowed_radial_distance.value_or(\n                         std::numeric_limits<double>::infinity())) {\n    info->discontinuous_change_has_occurred = true;\n    info->state = std::make_unique<States::DeltaRDriftOutward>();\n    ss << \"Current state DeltaR. \"\n          \"Horizon too far from excision boundary. Switching to \"\n          \"DeltaRDriftOutward\";\n  } else {\n    ss << \"Current state DeltaR. No change necessary. Staying in DeltaR.\";\n  }\n\n  return ss.str();\n}\n\ndouble DeltaR::control_error(const Info& /*info*/,\n                             const ControlErrorArgs& control_error_args) const {\n  return control_error_args.control_error_delta_r;\n}\n\nPUP::able::PUP_ID DeltaR::my_PUP_ID = 0;\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/DeltaR.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::size::States {\nclass DeltaR : public State {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Controls the velocity of the excision surface to maintain a constant \"\n      \"separation between the excision surface and the horizon surface. This \"\n      \"is state 2 in SpEC.\"};\n  DeltaR() = default;\n  std::string name() const override { return \"DeltaR\"; }\n  size_t number() const override { return 2; }\n  std::unique_ptr<State> get_clone() const override;\n  std::string update(const gsl::not_null<Info*> info,\n                     const StateUpdateArgs& update_args,\n                     const CrossingTimeInfo& crossing_time_info) const override;\n  /// The return value is Q from Eq. 96 of \\cite Hemberger2012jz.\n  double control_error(\n      const Info& info,\n      const ControlErrorArgs& control_error_args) const override;\n\n  WRAPPED_PUPable_decl_template(DeltaR);  // NOLINT\n  explicit DeltaR(CkMigrateMessage* const /*msg*/) {}\n};\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/DeltaRDriftInward.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <sstream>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRNoDrift.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace control_system::size::States {\n\nstd::unique_ptr<State> DeltaRDriftInward::get_clone() const {\n  return std::make_unique<DeltaRDriftInward>(*this);\n}\n\nstd::string DeltaRDriftInward::update(\n    const gsl::not_null<Info*> info, const StateUpdateArgs& update_args,\n    const CrossingTimeInfo& crossing_time_info) const {\n  const double Y00 = 0.25 * M_2_SQRTPI;\n\n  // This factor is present in SpEC, and it is used to prevent\n  // oscillations between states.  The value was chosen in SpEC, but\n  // nothing should be sensitive to small changes in this value as\n  // long as it is slightly greater than unity.\n  constexpr double non_oscillation_drift_inward_factor = 1.1;\n\n  // Note that delta_radius_is_in_danger and char_speed_is_in_danger\n  // can be different for different States.\n\n  // The value of 0.99 was chosen by trial and error in SpEC.\n  // It should be slightly less than unity but nothing should be\n  // sensitive to small changes in this value.\n  constexpr double time_tolerance_for_delta_r_in_danger = 0.99;\n  const bool delta_radius_is_in_danger =\n      crossing_time_info.horizon_will_hit_excision_boundary_first and\n      crossing_time_info.t_delta_radius.value_or(\n          std::numeric_limits<double>::infinity()) <\n          info->damping_time * time_tolerance_for_delta_r_in_danger;\n  const bool char_speed_is_in_danger =\n      crossing_time_info.char_speed_will_hit_zero_first and\n      crossing_time_info.t_char_speed.value_or(\n          std::numeric_limits<double>::infinity()) < info->damping_time and\n      not delta_radius_is_in_danger;\n\n  // spherepack_factor is needed because horizon_00 is a\n  // spherepack coefficient, not a spherical harmonic coefficient.\n  const double spherepack_factor = sqrt(0.5 * M_PI);\n\n  std::stringstream ss{};\n\n  if (char_speed_is_in_danger) {\n    ss << \"Current state DeltaRDriftInward. Char speed in danger.\"\n       << \" Switching to AhSpeed.\\n\";\n    // Switch to AhSpeed mode. Note that we don't check ComovingCharSpeed\n    // like we do in state DeltaR; this behavior agrees with SpEC.\n\n    // This factor prevents oscillations between\n    // DeltaR/DeltaRInward/DeltaRNoDrift/DeltaROutward and AhSpeed.\n    // It needs to be slightly greater than unity, but the control\n    // system should not be sensitive to the exact value. The value of\n    // 1.01 was chosen arbitrarily in SpEC and never needed to be\n    // changed.\n    constexpr double non_oscillation_factor = 1.01;\n    info->discontinuous_change_has_occurred = true;\n    info->state = std::make_unique<States::AhSpeed>();\n    info->target_char_speed =\n        update_args.min_char_speed * non_oscillation_factor;\n    ss << \" Target char speed = \" << info->target_char_speed << \"\\n\";\n    // If the comoving char speed is positive and is not about to\n    // cross zero, staying in DeltaRDriftInward mode will rescue the\n    // speed automatically (since it drives char speed to comoving\n    // char speed, plus a small difference).  But we should decrease\n    // the timescale in any case.\n    info->suggested_time_scale = crossing_time_info.t_char_speed.value();\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (delta_radius_is_in_danger) {\n    info->suggested_time_scale = crossing_time_info.t_delta_radius.value();\n    ss << \"Current state DeltaRDriftInward. Delta radius in danger. Staying \"\n          \"in DeltaRDriftInward.\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (should_transition_from_state_inward_drift_to_delta_r_no_drift(\n                 crossing_time_info.t_drift_limit, info->damping_time,\n                 update_args)) {\n    ss << \"Current state DeltaRDriftInward. Switching to DeltaRNoDrift.\\n\";\n    info->discontinuous_change_has_occurred = true;\n    info->state = std::make_unique<States::DeltaRNoDrift>();\n    info->suggested_time_scale = crossing_time_info.t_drift_limit;\n  } else if (crossing_time_info.t_delta_radius.has_value() and\n             info->damping_time >\n                 2.0 * spherepack_factor * update_args.horizon_00 * Y00) {\n    // Explaination of the above 'if':\n    //\n    // If crossing_time_info.t_delta_radius has a value, this means\n    // that delta_radius is decreasing.  But the entire point of state\n    // DeltaRDriftInward is to make delta_radius increase, not\n    // decrease.  So if we are in state DeltaRDriftInward and\n    // crossing_time_info.t_delta_radius has a value\n    // (i.e. delta_radius is decreasing), something is wrong.\n    //\n    // The thing that is usually wrong is that damping_time is too\n    // large, and hence DeltaRDriftInward doesn't have time to make\n    // delta_radius increase.  So the fix is to decrease the damping\n    // time (a.k.a. suggested_time_scale below).  But we stop\n    // decreasing the damping time if it is less than twice the\n    // average horizon radius, which is the same criterion SpEC\n    // uses. (Here we are assuming that timescales and length scales\n    // have the same units, which should be true for horizons).\n    ss << \"Current state DeltaRDriftInward. RelativeDeltaR is decreasing, \"\n          \"which is probably because timescale is too big (DeltaRDriftInward \"\n          \"should be increasing RelativeDeltaR if control system is working \"\n          \"properly). Decreasing timescale and staying in DeltaRDriftInward.\\n\";\n    // delta_r_drift_inward_decrease_factor is an arbitrary factor\n    // chosen by trial and error in SpEC. If this factor is too close\n    // to 1, then the timescale does not decrease fast enough.  If\n    // this factor is too far from 1, then repeated calls of\n    // DeltaRDriftInward::update will decrease the timescale to\n    // 2*average_horizon_radius too quickly.\n    constexpr double delta_r_drift_inward_decrease_factor = 0.99;\n    info->suggested_time_scale =\n        info->damping_time * delta_r_drift_inward_decrease_factor;\n    info->target_char_speed = target_speed_for_inward_drift(\n        update_args.avg_distorted_normal_dot_unit_coord_vector,\n        update_args.min_char_speed, update_args.inward_drift_velocity.value());\n    ss << \" Target char speed = \" << info->target_char_speed << \"\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (update_args.average_radial_distance.has_value() and\n             update_args.average_radial_distance.value() >\n                 non_oscillation_drift_inward_factor *\n                     update_args.max_allowed_radial_distance.value_or(\n                         std::numeric_limits<double>::infinity())) {\n    info->discontinuous_change_has_occurred = true;\n    ss << \"Current state DeltaRDriftInward. We have drifted too far, so \"\n          \"we are switching to DeltaRDriftOutward.\\n\";\n    info->state = std::make_unique<States::DeltaRDriftOutward>();\n  } else {\n    ss << \"Current state DeltaRDriftInward. No change necessary. Staying in \"\n          \"DeltaRDriftInward.\";\n  }\n\n  return ss.str();\n}\n\ndouble DeltaRDriftInward::control_error(\n    const Info& info, const ControlErrorArgs& control_error_args) const {\n  // We increase the control error by the target speed, so as to make\n  // control_error_delta_r more negative, which gives a negative velocity\n  // to delta_r (i.e. a positive velocity to the excision boundary).\n  return control_error_args.control_error_delta_r + info.target_char_speed;\n}\n\n// cppcoreguidelines-avoid-non-const-global-variables\nPUP::able::PUP_ID DeltaRDriftInward::my_PUP_ID = 0;  // NOLINT\n\ndouble target_speed_for_inward_drift(\n    const double avg_distorted_normal_dot_unit_coord_vector,\n    const double min_char_speed, const double inward_drift_velocity) {\n  // TargetSpeed should be > 0 (we want DeltaR to increase).  And\n  // TargetSpeed must be <\n  // min_char_speed/avg_distorted_normal_dot_unit_coord_vector, because\n  // going into DriftInward will make min_char_speed decrease by\n  // TargetSpeed*avg_distorted_normal_dot_unit_coord_vector. The time\n  // it takes v to cross zero (assuming v decreases linearly, only a\n  // rough approximation) is\n  // Tau*min_char_speed/avg_distorted_normal_dot_unit_coord_vector*TargetSpeed,\n  // where Tau is the damping timescale.  Therefore choosing\n  // TargetSpeed < fudge *\n  // min_char_speed/avg_distorted_normal_dot_unit_coord_vector should make\n  // v decrease only by a factor of fudge, and it should make the\n  // crossing time fudge*Tau.\n  constexpr double fudge = 0.5;\n  return std::min(\n      inward_drift_velocity,\n      fudge * min_char_speed / avg_distorted_normal_dot_unit_coord_vector);\n}\n\nbool should_transition_from_state_delta_r_to_inward_drift(\n    const std::optional<double>& crossing_time_drift_limit,\n    const double damping_time, const StateUpdateArgs& update_args) {\n  // This function is called ShouldEnterState3FromState2 in SpEC.\n  if (update_args.inward_drift_velocity.has_value() and\n      crossing_time_drift_limit.has_value() and\n      crossing_time_drift_limit.value() < damping_time) {\n    return false;\n  }\n  return should_activate_inward_drift(update_args);\n}\n\nbool should_transition_from_state_inward_drift_to_delta_r_no_drift(\n    const std::optional<double>& crossing_time_drift_limit,\n    const double damping_time, const StateUpdateArgs& update_args) {\n  return (not should_transition_from_state_delta_r_to_inward_drift(\n      crossing_time_drift_limit, damping_time, update_args));\n}\n\nbool should_activate_inward_drift(const StateUpdateArgs& update_args) {\n  // This function is called PreferState3OverState2 in SpEC.\n\n  // This drift factor was chosen in SpEC arbitrarily to be 0.9.\n  constexpr double inward_drift_limit_buffer_factor = 0.9;\n\n  // The idea of these variables is to check whether either DeltaR or\n  // char speed are close to going above the\n  // min_average_radial_distance or min_allowed_char_speed values.  If\n  // so, then we don't need state DeltaRDriftInward at the moment.\n  // For reference, in SpEC these variables are called\n  // \"DeltaRAlmostAboveState3Limit\" and\n  // \"CharSpeedAlmostAboveState3Limit\".\n  const bool delta_r_almost_above_inward_drift_limit =\n      update_args.min_allowed_radial_distance.has_value() and\n      update_args.average_radial_distance.value() >\n          inward_drift_limit_buffer_factor *\n              update_args.min_allowed_radial_distance.value();\n  const bool char_speed_almost_above_inward_drift_limit =\n      update_args.min_allowed_char_speed.has_value() and\n      update_args.min_char_speed >\n          inward_drift_limit_buffer_factor *\n              update_args.min_allowed_char_speed.value();\n\n  return (update_args.inward_drift_velocity.has_value() and\n          update_args.comoving_char_speed_increasing_inward and\n          (update_args.min_allowed_char_speed.has_value() or\n           update_args.min_allowed_radial_distance.has_value()) and\n          (not delta_r_almost_above_inward_drift_limit) and\n          (not char_speed_almost_above_inward_drift_limit));\n}\n\nbool ok_to_return_to_state_deltar(const StateUpdateArgs& update_args) {\n  // The purpose of delta_r_large_enough_to_stop_inward_drift and\n  // char_speed_large_enough_to_stop_inward_drift below is to stop\n  // the scenario in which either the CharSpeed or DeltaR approaches\n  // the limit \"min_allowed_radial_distance\" and the timescale gets\n  // cut down to a ridiculously small value.  When that scenario\n  // happens, we simply exit state DeltaRNoDrift and go back to DeltaR.\n  // These variables are called \"DeltaRLargeEnoughToExitState4\"\n  // and \"CharSpeedLargeEnoughToExitState4\" in SpEC.\n  constexpr double stop_inward_drift_buffer_factor = 0.99;\n  const bool delta_r_large_enough_to_stop_inward_drift =\n      update_args.min_allowed_radial_distance.has_value() and\n      update_args.average_radial_distance.value() >\n          stop_inward_drift_buffer_factor *\n              update_args.min_allowed_radial_distance.value();\n  const bool char_speed_large_enough_to_stop_inward_drift =\n      update_args.min_allowed_char_speed.has_value() and\n      update_args.min_char_speed >\n          stop_inward_drift_buffer_factor *\n              update_args.min_allowed_char_speed.value();\n  return delta_r_large_enough_to_stop_inward_drift or\n         char_speed_large_enough_to_stop_inward_drift;\n}\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::size::States {\nclass DeltaRDriftInward : public State {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Controls the velocity of the excision surface to maintain a constant \"\n      \"separation between the excision surface and the horizon surface with a \"\n      \"small inward radial velocity. This is state 3 in SpEC.\"};\n  DeltaRDriftInward() = default;\n  std::string name() const override { return \"DeltaRDriftInward\"; }\n  size_t number() const override { return 3; }\n  std::unique_ptr<State> get_clone() const override;\n  std::string update(gsl::not_null<Info*> info,\n                     const StateUpdateArgs& update_args,\n                     const CrossingTimeInfo& crossing_time_info) const override;\n  /// The return value is Q from Eq. 96 of \\cite Hemberger2012jz, plus\n  /// an inward velocity term.\n  double control_error(\n      const Info& info,\n      const ControlErrorArgs& control_error_args) const override;\n\n  WRAPPED_PUPable_decl_template(DeltaRDriftInward);  // NOLINT\n  explicit DeltaRDriftInward(CkMigrateMessage* const /*msg*/) {}\n};\n\n// The following are helper functions that are used in many\n// of the states, for transitions to/from DeltaRDriftInward.\n\n/// Value of target_char_speed when state DeltaRDriftInward is in effect.\ndouble target_speed_for_inward_drift(\n    double avg_distorted_normal_dot_unit_coord_vector, double min_char_speed,\n    double inward_drift_velocity);\n\n/// Returs true if we should transition from state DeltaR to state\n/// DeltaRDriftInward.\nbool should_transition_from_state_delta_r_to_inward_drift(\n    const std::optional<double>& crossing_time_drift_limit, double damping_time,\n    const StateUpdateArgs& update_args);\n\n/// Returns true if we should transition from state DeltaRDriftInward\n/// to state DeltaRNoDrift.\nbool should_transition_from_state_inward_drift_to_delta_r_no_drift(\n    const std::optional<double>& crossing_time_drift_limit, double damping_time,\n    const StateUpdateArgs& update_args);\n\n/// Returns true if we should transition to DeltaRDriftInward rather than\n/// to DeltaR.\nbool should_activate_inward_drift(const StateUpdateArgs& update_args);\n\n/// Returns true if either CharSpeed approaches min_allowed_char_speed\n/// or DeltaR approaches min_allowed_radial_distance close enough\n/// that it would be ok to turn off state DeltaRNoDrift.\nbool ok_to_return_to_state_deltar(const StateUpdateArgs& update_args);\n\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/DeltaRDriftOutward.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n\n#include <limits>\n#include <memory>\n#include <optional>\n#include <sstream>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace control_system::size::States {\n\nstd::unique_ptr<State> DeltaRDriftOutward::get_clone() const {\n  return std::make_unique<DeltaRDriftOutward>(*this);\n}\n\nstd::string DeltaRDriftOutward::update(\n    const gsl::not_null<Info*> info, const StateUpdateArgs& update_args,\n    const CrossingTimeInfo& crossing_time_info) const {\n  // Note that delta_radius_is_in_danger and char_speed_is_in_danger\n  // can be different for different States.\n\n  // The value of 0.99 was chosen by trial and error in SpEC.\n  // It should be slightly less than unity but nothing should be\n  // sensitive to small changes in this value.\n  constexpr double time_tolerance_for_delta_r_in_danger = 0.99;\n  const bool delta_radius_is_in_danger =\n      crossing_time_info.horizon_will_hit_excision_boundary_first and\n      crossing_time_info.t_delta_radius.value_or(\n          std::numeric_limits<double>::infinity()) <\n          info->damping_time * time_tolerance_for_delta_r_in_danger;\n  const bool char_speed_is_in_danger =\n      crossing_time_info.char_speed_will_hit_zero_first and\n      crossing_time_info.t_char_speed.value_or(\n          std::numeric_limits<double>::infinity()) < info->damping_time and\n      not delta_radius_is_in_danger;\n\n  std::stringstream ss{};\n\n  if (char_speed_is_in_danger) {\n    ss << \"Current state DeltaRDriftOutward. Char speed in danger.\"\n       << \" Switching to AhSpeed.\\n\";\n    // Switch to AhSpeed mode. Note that we don't check ComovingCharSpeed\n    // like we do in state DeltaR; this behavior agrees with SpEC.\n\n    // This factor prevents oscillations between\n    // DeltaR/DeltaRInward/DeltaRNoDrift/DeltaROutward and AhSpeed.\n    // It needs to be slightly greater than unity, but the control\n    // system should not be sensitive to the exact value. The value of\n    // 1.01 was chosen arbitrarily in SpEC and never needed to be\n    // changed.\n    constexpr double non_oscillation_factor = 1.01;\n    info->discontinuous_change_has_occurred = true;\n    info->state = std::make_unique<States::AhSpeed>();\n    info->target_char_speed =\n        update_args.min_char_speed * non_oscillation_factor;\n    ss << \" Target char speed = \" << info->target_char_speed << \"\\n\";\n    // If the comoving char speed is positive and is not about to\n    // cross zero, staying in DeltaRDriftOutward mode will rescue the speed\n    // automatically (since it drives char speed to comoving char\n    // speed).  But we should decrease the timescale in any case.\n    info->suggested_time_scale = crossing_time_info.t_char_speed;\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (delta_radius_is_in_danger) {\n    info->suggested_time_scale = crossing_time_info.t_delta_radius;\n    ss << \"Current state DeltaRDriftOutward. Delta radius in danger. Staying \"\n          \"in DeltaRDriftOutward.\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (update_args.average_radial_distance.has_value() and\n             update_args.average_radial_distance.value() <\n                 update_args.max_allowed_radial_distance.value_or(\n                     std::numeric_limits<double>::infinity())) {\n    ss << \"Current state DeltaRDriftOutward. Switching to DeltaR.\";\n    info->discontinuous_change_has_occurred = true;\n    info->state = std::make_unique<States::DeltaR>();\n  } else {\n    ss << \"Current state DeltaRDriftOutward. No change necessary. Staying in \"\n          \"DeltaRDriftOutward.\";\n  }\n\n  return ss.str();\n}\n\ndouble DeltaRDriftOutward::control_error(\n    const Info& /*info*/, const ControlErrorArgs& control_error_args) const {\n  return control_error_args.control_error_delta_r_outward.value_or(\n      std::numeric_limits<double>::signaling_NaN());\n}\n\nPUP::able::PUP_ID DeltaRDriftOutward::my_PUP_ID = 0;\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::size::States {\nclass DeltaRDriftOutward : public State {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Controls the velocity of the excision surface to maintain a constant \"\n      \"separation between the excision surface and the horizon surface with a \"\n      \"small outward radial velocity. This is state 5 in SpEC.\"};\n  DeltaRDriftOutward() = default;\n  std::string name() const override { return \"DeltaRDriftOutward\"; }\n  size_t number() const override { return 5; }\n  std::unique_ptr<State> get_clone() const override;\n  std::string update(const gsl::not_null<Info*> info,\n                     const StateUpdateArgs& update_args,\n                     const CrossingTimeInfo& crossing_time_info) const override;\n  /// The return value is Q from Eq. 96 of \\cite Hemberger2012jz, plus\n  /// an outward velocity term.\n  double control_error(\n      const Info& info,\n      const ControlErrorArgs& control_error_args) const override;\n\n  WRAPPED_PUPable_decl_template(DeltaRDriftOutward);  // NOLINT\n  explicit DeltaRDriftOutward(CkMigrateMessage* const /*msg*/) {}\n};\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/DeltaRNoDrift.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/DeltaRNoDrift.hpp\"\n\n#include <memory>\n#include <optional>\n#include <sstream>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace control_system::size::States {\n\nstd::unique_ptr<State> DeltaRNoDrift::get_clone() const {\n  return std::make_unique<DeltaRNoDrift>(*this);\n}\n\nstd::string DeltaRNoDrift::update(\n    const gsl::not_null<Info*> info, const StateUpdateArgs& update_args,\n    const CrossingTimeInfo& crossing_time_info) const {\n  // This factor is present in SpEC, and it is used to prevent\n  // oscillations between states.  The value was chosen in SpEC, but\n  // nothing should be sensitive to small changes in this value as\n  // long as it is slightly greater than unity.\n  constexpr double non_oscillation_drift_inward_factor = 1.1;\n\n  // Note that delta_radius_is_in_danger and char_speed_is_in_danger\n  // can be different for different States.\n\n  // The value of 0.99 was chosen by trial and error in SpEC.\n  // It should be slightly less than unity but nothing should be\n  // sensitive to small changes in this value.\n  constexpr double time_tolerance_for_delta_r_in_danger = 0.99;\n  const bool delta_radius_is_in_danger =\n      crossing_time_info.horizon_will_hit_excision_boundary_first and\n      crossing_time_info.t_delta_radius.value_or(\n          std::numeric_limits<double>::infinity()) <\n          info->damping_time * time_tolerance_for_delta_r_in_danger;\n  const bool char_speed_is_in_danger =\n      crossing_time_info.char_speed_will_hit_zero_first and\n      crossing_time_info.t_char_speed.value_or(\n          std::numeric_limits<double>::infinity()) < info->damping_time and\n      not delta_radius_is_in_danger;\n\n  std::stringstream ss{};\n\n  if (char_speed_is_in_danger) {\n    ss << \"Current state DeltaRNoDrift. Char speed in danger.\";\n    if (crossing_time_info.t_comoving_char_speed.has_value() or\n        update_args.min_comoving_char_speed < 0.0) {\n      // Comoving char speed is negative or threatening to cross zero, so\n      // staying in DeltaRNoDrift mode will not work.  So switch to AhSpeed\n      // mode.\n\n      // This factor prevents oscillations between\n      // DeltaR/DeltaRInward/DeltaRNoDrift/DeltaROutward and AhSpeed.\n      // It needs to be slightly greater than unity, but the control\n      // system should not be sensitive to the exact value. The value of\n      // 1.01 was chosen arbitrarily in SpEC and never needed to be\n      // changed.\n      constexpr double non_oscillation_factor = 1.01;\n      info->discontinuous_change_has_occurred = true;\n      info->state = std::make_unique<States::AhSpeed>();\n      info->target_char_speed =\n          update_args.min_char_speed * non_oscillation_factor;\n      ss << \" Switching to AhSpeed.\\n\";\n      ss << \" Target char speed = \" << info->target_char_speed << \"\\n\";\n    } else {\n      ss << \" Staying in DeltaRNoDrift.\\n\";\n    }\n    // If the comoving char speed is positive and is not about to\n    // cross zero, staying in DeltaRNoDrift mode will rescue the speed\n    // automatically (since it drives char speed to comoving char\n    // speed).  But we should decrease the timescale in any case.\n    info->suggested_time_scale = crossing_time_info.t_char_speed;\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (delta_radius_is_in_danger) {\n    info->suggested_time_scale = crossing_time_info.t_delta_radius;\n    ss << \"Current state DeltaRNoDrift. Delta radius in danger. Staying in \"\n          \"DeltaRNoDrift.\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if ((not crossing_time_info.t_drift_limit.has_value()) or\n             ok_to_return_to_state_deltar(update_args)) {\n    info->state = std::make_unique<States::DeltaR>();\n    ss << \"Current state DeltaRNoDrift, but safe to exit. \"\n          \"Going to state DeltaR.\\n\";\n  } else if (crossing_time_info.t_drift_limit.value_or(\n                 std::numeric_limits<double>::infinity()) <\n                 info->damping_time and\n             (update_args.min_allowed_char_speed.has_value() or\n              update_args.min_allowed_radial_distance.has_value())) {\n    info->suggested_time_scale = crossing_time_info.t_drift_limit;\n    ss << \"Current state DeltaRNoDrift. Inward drift limit in danger. Staying \"\n          \"in DeltaRNoDrift.\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (update_args.average_radial_distance.has_value() and\n             update_args.average_radial_distance.value() >\n                 non_oscillation_drift_inward_factor *\n                     update_args.max_allowed_radial_distance.value_or(\n                         std::numeric_limits<double>::infinity())) {\n    info->discontinuous_change_has_occurred = true;\n    ss << \"Current state DeltaRNoDrift. We have drifted too far, so \"\n          \"we are switching to DeltaRDriftOutward.\\n\";\n    info->state = std::make_unique<States::DeltaRDriftOutward>();\n  } else {\n    ss << \"Current state DeltaRNoDrift. No change necessary. Staying in \"\n          \"DeltaRNoDrift.\";\n  }\n\n  return ss.str();\n}\n\ndouble DeltaRNoDrift::control_error(\n    const Info& /*info*/, const ControlErrorArgs& control_error_args) const {\n  return control_error_args.control_error_delta_r;\n}\n\n// cppcoreguidelines-avoid-non-const-global-variables\nPUP::able::PUP_ID DeltaRNoDrift::my_PUP_ID = 0; // NOLINT\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/DeltaRNoDrift.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::size::States {\nclass DeltaRNoDrift : public State {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Controls the velocity of the excision surface to maintain a constant \"\n      \"separation between the excision surface and the horizon surface. \"\n      \"This is a transition from DeltaRDriftInward to DeltaR, and otherwise \"\n      \"acts very much like state DeltaR (e.g. it has the same control error). \"\n      \"This is state 4 in SpEC.\"};\n  DeltaRNoDrift() = default;\n  std::string name() const override { return \"DeltaRNoDrift\"; }\n  size_t number() const override { return 4; }\n  std::unique_ptr<State> get_clone() const override;\n  std::string update(gsl::not_null<Info*> info,\n                     const StateUpdateArgs& update_args,\n                     const CrossingTimeInfo& crossing_time_info) const override;\n  /// The return value is Q from Eq. 96 of \\cite Hemberger2012jz.\n  double control_error(\n      const Info& info,\n      const ControlErrorArgs& control_error_args) const override;\n\n  WRAPPED_PUPable_decl_template(DeltaRNoDrift);  // NOLINT\n  explicit DeltaRNoDrift(CkMigrateMessage* const /*msg*/) {}\n};\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/Error.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/Error.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"NumericalAlgorithms/Interpolation/ZeroCrossingPredictor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TagsTypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/AreaElement.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/RadialDistance.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfScalar.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace control_system::size {\n\ntemplate <typename Frame>\nErrorDiagnostics control_error(\n    const gsl::not_null<Info*> info,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*> predictor_char_speed,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_comoving_char_speed,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*> predictor_delta_radius,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_drift_limit_char_speed,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_drift_limit_delta_radius,\n    const double time, const double control_error_delta_r,\n    const std::optional<double> control_error_delta_r_outward,\n    const std::optional<double> max_allowed_radial_distance,\n    const std::optional<double> inward_drift_velocity,\n    const std::optional<double> min_allowed_radial_distance,\n    const std::optional<double> min_allowed_char_speed, const double horizon_00,\n    const double dt_lambda_00, const ylm::Strahlkorper<Frame>& apparent_horizon,\n    const ylm::Strahlkorper<Frame>& excision_boundary,\n    const Scalar<DataVector>& lapse_on_excision_boundary,\n    const tnsr::I<DataVector, 3, Frame>& frame_components_of_grid_shift,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric_on_excision_boundary,\n    const tnsr::II<DataVector, 3, Frame>&\n        inverse_spatial_metric_on_excision_boundary,\n    const Scalar<DataVector>& deriv_comoving_char_speed) {\n  const double Y00 = 0.25 * M_2_SQRTPI;\n\n  // Define various quantities on excision boundary.\n  // Declare a TempBuffer to do this with a single memory allocation.\n  using excision_theta_phi_tag =\n      ::Tags::Tempi<0, 2, ::Frame::Spherical<Frame>, DataVector>;\n  using excision_radius_tag = ::Tags::TempScalar<0, DataVector>;\n  using excision_rhat_tag = ::Tags::Tempi<1, 3, Frame, DataVector>;\n  using excision_normal_one_form_tag = ::Tags::Tempi<2, 3, Frame, DataVector>;\n  using excision_jacobian_tag =\n      ::Tags::TempTensor<0, ylm::Tags::aliases::Jacobian<Frame>>;\n  using excision_inv_jacobian_tag =\n      ::Tags::TempTensor<1, ylm::Tags::aliases::InvJacobian<Frame>>;\n  using excision_dx_radius_tag = Tags::Tempi<3, 3, Frame, DataVector>;\n  using area_element_tag = ::Tags::TempScalar<1, DataVector>;\n  using distorted_normal_dot_unit_coord_vector_tag =\n      ::Tags::TempScalar<3, DataVector>;\n  using comoving_char_speed_tag = ::Tags::TempScalar<4, DataVector>;\n  using radial_distance_tag = ::Tags::TempScalar<5, DataVector>;\n  using excision_normal_one_form_norm_tag = ::Tags::TempScalar<6, DataVector>;\n  using unity_tag = ::Tags::TempScalar<7, DataVector>;\n  using char_speed_tag = ::Tags::TempScalar<8, DataVector>;\n\n  TempBuffer<\n      tmpl::list<excision_theta_phi_tag, excision_radius_tag, excision_rhat_tag,\n                 excision_normal_one_form_tag, excision_jacobian_tag,\n                 excision_inv_jacobian_tag, excision_dx_radius_tag,\n                 area_element_tag, distorted_normal_dot_unit_coord_vector_tag,\n                 comoving_char_speed_tag, radial_distance_tag,\n                 excision_normal_one_form_norm_tag, unity_tag, char_speed_tag>>\n      buffer(excision_boundary.ylm_spherepack().physical_size());\n  auto& excision_radius = get<excision_radius_tag>(buffer);\n  auto& excision_theta_phi = get<excision_theta_phi_tag>(buffer);\n  auto& excision_rhat = get<excision_rhat_tag>(buffer);\n  auto& excision_normal_one_form = get<excision_normal_one_form_tag>(buffer);\n  auto& excision_jacobian = get<excision_jacobian_tag>(buffer);\n  auto& excision_inv_jacobian = get<excision_inv_jacobian_tag>(buffer);\n  auto& excision_dx_radius = get<excision_dx_radius_tag>(buffer);\n  auto& area_element = get<area_element_tag>(buffer);\n  auto& distorted_normal_dot_unit_coord_vector =\n      get<distorted_normal_dot_unit_coord_vector_tag>(buffer);\n  auto& comoving_char_speed = get<comoving_char_speed_tag>(buffer);\n  auto& radial_distance = get<radial_distance_tag>(buffer);\n  auto& excision_normal_one_form_norm =\n      get<excision_normal_one_form_norm_tag>(buffer);\n  auto& unity = get<unity_tag>(buffer);\n  auto& characteristic_speed_on_excision_boundary = get<char_speed_tag>(buffer);\n\n  // Compute the quantities on the excision boundary.\n  ylm::theta_phi(make_not_null(&excision_theta_phi), excision_boundary);\n  ylm::radius(make_not_null(&excision_radius), excision_boundary);\n  // rhat is x^i/r\n  ylm::rhat(make_not_null(&excision_rhat), excision_theta_phi);\n  ylm::jacobian(make_not_null(&excision_jacobian), excision_theta_phi);\n  ylm::inv_jacobian(make_not_null(&excision_inv_jacobian), excision_theta_phi);\n  ylm::cartesian_derivs_of_scalar(make_not_null(&excision_dx_radius),\n                                  excision_radius, excision_boundary,\n                                  excision_radius, excision_inv_jacobian);\n  ylm::normal_one_form(make_not_null(&excision_normal_one_form),\n                       excision_dx_radius, excision_rhat);\n  magnitude(make_not_null(&excision_normal_one_form_norm),\n            excision_normal_one_form,\n            inverse_spatial_metric_on_excision_boundary);\n  gr::surfaces::area_element(make_not_null(&area_element),\n                             spatial_metric_on_excision_boundary,\n                             excision_jacobian, excision_normal_one_form,\n                             excision_radius, excision_rhat);\n\n  // distorted_normal_dot_unit_coord_vector is nhat_i x^i/r where\n  // nhat_i is the distorted-frame unit normal to the excision\n  // boundary (pointing INTO the hole, i.e. out of the domain), and\n  // x^i/r is the distorted-frame (or equivalently the grid frame\n  // because it is invariant between these two frames because of the\n  // required limiting behavior of the map we choose) Euclidean normal\n  // vector from the center of the excision-boundary Strahlkorper to\n  // each point on the excision-boundary Strahlkorper.\n  //\n  // Minus sign is because we want the normal pointing into the hole,\n  // not out of the hole.\n  //\n  // Note that distorted_normal_dot_unit_coord_vector is negative.\n  get(distorted_normal_dot_unit_coord_vector) =\n      -get<0>(excision_normal_one_form) * get<0>(excision_rhat);\n  for (size_t i = 1; i < 3; ++i) {\n    get(distorted_normal_dot_unit_coord_vector) -=\n        excision_normal_one_form.get(i) * excision_rhat.get(i);\n  }\n  get(distorted_normal_dot_unit_coord_vector) /=\n      get(excision_normal_one_form_norm);\n\n  // Average value of distorted_normal_dot_unit_coord_vector on the excision\n  // boundary.  Compute the average by integrating.\n  //\n  // Note that avg_distorted_normal_dot_unit_coord_vector is negative.\n  get(unity) = 1.0;\n  const double avg_distorted_normal_dot_unit_coord_vector =\n      gr::surfaces::surface_integral_of_scalar(\n          area_element, distorted_normal_dot_unit_coord_vector,\n          excision_boundary) /\n      gr::surfaces::surface_integral_of_scalar(area_element, unity,\n                                               excision_boundary);\n\n  // Compute char speed on excision boundary, Eq. 87 in ArXiv:1211.6079\n  //\n  // Note that characteristic_speed_on_excision_boundary should normally\n  // be positive, and one of the control system states (AhSpeed) is designed\n  // to keep it positive.\n  get(characteristic_speed_on_excision_boundary) =\n      -get(lapse_on_excision_boundary);\n  for (size_t i = 0; i < 3; ++i) {\n    // Plus sign here is because we want the normal pointing into the hole,\n    // not out of the hole, and excision_normal_one_form points out of the hole.\n    get(characteristic_speed_on_excision_boundary) +=\n        frame_components_of_grid_shift.get(i) *\n        excision_normal_one_form.get(i) / get(excision_normal_one_form_norm);\n  }\n\n  // Minimum char speed on the excision boundary\n  const double min_char_speed =\n      min(get(characteristic_speed_on_excision_boundary));\n\n  // comoving_char_speed is the quantity v_c, Eq. 98 in ArXiv:1211.6079\n  // (but implemented in a simpler way).\n  get(comoving_char_speed) =\n      get(characteristic_speed_on_excision_boundary) +\n      control_error_delta_r * Y00 * get(distorted_normal_dot_unit_coord_vector);\n\n  // Minimum of the comoving char speed on the excision boundary.\n  const double min_comoving_char_speed = min(get(comoving_char_speed));\n\n  const bool comoving_char_speed_increasing_inward =\n      min(get(deriv_comoving_char_speed)) > 0.0;\n\n  // Difference between horizon and excision boundary.\n  if (UNLIKELY(apparent_horizon.l_max() > excision_boundary.l_max() or\n               (apparent_horizon.l_max() == excision_boundary.l_max() and\n                apparent_horizon.m_max() > excision_boundary.m_max()))) {\n    ERROR(\n        \"Size control assumes the excision boundary resolution is at least \"\n        \"as high as the horizon resolution, but the horizon has l_max = \"\n        << apparent_horizon.l_max() << \" and m_max = \"\n        << apparent_horizon.m_max() << \" while the excision boundary has \"\n        << \"l_max = \" << excision_boundary.l_max()\n        << \" and m_max = \" << excision_boundary.m_max() << \".\");\n  }\n  gr::surfaces::radial_distance(make_not_null(&radial_distance),\n                                apparent_horizon, excision_boundary);\n\n  // Update zero-crossing predictors.\n  predictor_char_speed->add(time,\n                            get(characteristic_speed_on_excision_boundary));\n  predictor_comoving_char_speed->add(time, get(comoving_char_speed));\n  predictor_delta_radius->add(time, get(radial_distance));\n  if (min_allowed_char_speed.has_value()) {\n    predictor_drift_limit_char_speed->add(\n        time, min_allowed_char_speed.value() -\n                  get(characteristic_speed_on_excision_boundary));\n  }\n  if (min_allowed_radial_distance.has_value()) {\n    predictor_drift_limit_delta_radius->add(\n        time, min_allowed_radial_distance.value() - get(radial_distance));\n  }\n\n  // Compute crossing times for state-change logic.\n  const std::optional<double> char_speed_crossing_time =\n      predictor_char_speed->min_positive_zero_crossing_time(time);\n  const std::optional<double> comoving_char_speed_crossing_time =\n      predictor_comoving_char_speed->min_positive_zero_crossing_time(time);\n  const std::optional<double> delta_radius_crossing_time =\n      predictor_delta_radius->min_positive_zero_crossing_time(time);\n  const std::optional<double> drift_limit_delta_radius_crossing_time =\n      min_allowed_radial_distance.has_value()\n          ? predictor_drift_limit_delta_radius->min_positive_zero_crossing_time(\n                time)\n          : std::nullopt;\n  const std::optional<double> drift_limit_char_speed_crossing_time =\n      min_allowed_char_speed.has_value()\n          ? predictor_drift_limit_char_speed->min_positive_zero_crossing_time(\n                time)\n          : std::nullopt;\n\n  // Compute average radial distance for state DeltaRDriftOutward.\n  // NOTE: This choice corresponds to SpEC's \"DeltaRPolicy=Absolute\"\n  // and SpEC's \"FunctionVsTimeMinDeltaRNoLam00=<NONE>\".\n  // However, the default in SpEC is \"DeltaRPolicy=Relative\" and\n  // \"FunctionVsTimeMinDeltaRNoLam00\" being an actual FunctionOfTime.\n  //\n  // If we were to make this change here, it means reinterpreting the\n  // meaning of max_allowed_radial_distance and\n  // min_allowed_radial_distance and using a different formula for\n  // average_radial_distance, but all the logic other than those\n  // changes remains unchanged.\n  // Such a change is possible to make, but we do not (yet) make it here.\n  const std::optional<double> average_radial_distance =\n      (max_allowed_radial_distance.has_value() or\n       min_allowed_radial_distance.has_value())\n          ? std::optional<double>(\n                gr::surfaces::surface_integral_of_scalar(\n                    area_element, radial_distance, excision_boundary) /\n                gr::surfaces::surface_integral_of_scalar(area_element, unity,\n                                                         excision_boundary))\n          : std::nullopt;\n\n  // Update the info, possibly changing the state inside of info.\n  std::string update_message = info->state->get_clone()->update(\n      info,\n      StateUpdateArgs{min_char_speed, min_comoving_char_speed, horizon_00,\n                      control_error_delta_r, average_radial_distance,\n                      max_allowed_radial_distance,\n                      avg_distorted_normal_dot_unit_coord_vector,\n                      inward_drift_velocity, min_allowed_radial_distance,\n                      min_allowed_char_speed,\n                      comoving_char_speed_increasing_inward},\n      CrossingTimeInfo{\n          char_speed_crossing_time, comoving_char_speed_crossing_time,\n          delta_radius_crossing_time, drift_limit_char_speed_crossing_time,\n          drift_limit_delta_radius_crossing_time});\n\n  const ControlErrorArgs control_error_args{\n      min_char_speed, control_error_delta_r, control_error_delta_r_outward,\n      avg_distorted_normal_dot_unit_coord_vector, dt_lambda_00};\n\n  const double control_error =\n      info->state->control_error(*info, control_error_args);\n\n  return ErrorDiagnostics{\n      control_error,\n      info->state->number(),\n      min(get(radial_distance)),\n      min(get(radial_distance)) / apparent_horizon.average_radius(),\n      min_comoving_char_speed,\n      char_speed_crossing_time.value_or(0.0),\n      comoving_char_speed_crossing_time.value_or(0.0),\n      delta_radius_crossing_time.value_or(0.0),\n      info->target_char_speed,\n      info->suggested_time_scale.value_or(0.0),\n      info->damping_time,\n      control_error_args,\n      std::move(update_message),\n      info->discontinuous_change_has_occurred};\n}\n}  // namespace control_system::size\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template control_system::size::ErrorDiagnostics                              \\\n  control_system::size::control_error(                                         \\\n      const gsl::not_null<control_system::size::Info*> info,                   \\\n      const gsl::not_null<intrp::ZeroCrossingPredictor*> predictor_char_speed, \\\n      const gsl::not_null<intrp::ZeroCrossingPredictor*>                       \\\n          predictor_comoving_char_speed,                                       \\\n      const gsl::not_null<intrp::ZeroCrossingPredictor*>                       \\\n          predictor_delta_radius,                                              \\\n      const gsl::not_null<intrp::ZeroCrossingPredictor*>                       \\\n          predictor_drift_limit_char_speed,                                    \\\n      const gsl::not_null<intrp::ZeroCrossingPredictor*>                       \\\n          predictor_drift_limit_delta_radius,                                  \\\n      double time, double control_error_delta_r,                               \\\n      std::optional<double> control_error_delta_r_outward,                     \\\n      std::optional<double> max_allowed_radial_distance,                       \\\n      std::optional<double> inward_drift_velocity,                             \\\n      std::optional<double> min_allowed_radial_distance,                       \\\n      std::optional<double> min_allowed_char_speed, double horizon_00,         \\\n      double dt_lambda_00,                                                     \\\n      const ylm::Strahlkorper<FRAME(data)>& apparent_horizon,                  \\\n      const ylm::Strahlkorper<FRAME(data)>& excision_boundary,                 \\\n      const Scalar<DataVector>& lapse_on_excision_boundary,                    \\\n      const tnsr::I<DataVector, 3, FRAME(data)>&                               \\\n          frame_components_of_grid_shift,                                      \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>&                              \\\n          spatial_metric_on_excision_boundary,                                 \\\n      const tnsr::II<DataVector, 3, FRAME(data)>&                              \\\n          inverse_spatial_metric_on_excision_boundary,                         \\\n      const Scalar<DataVector>& deriv_comoving_char_speed);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (::Frame::Distorted, ::Frame::Inertial))\n\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/Error.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace control_system::size {\nclass Info;\n}  // namespace control_system::size\nnamespace intrp {\nclass ZeroCrossingPredictor;\n}  // namespace intrp\n/// \\endcond\n\nnamespace control_system::size {\n/*!\n * \\brief A simple struct to hold diagnostic information about computing the\n * size control error.\n */\nstruct ErrorDiagnostics {\n  double control_error;\n  size_t state_number;\n  double min_delta_r;\n  double min_relative_delta_r;\n  double min_comoving_char_speed;\n  double char_speed_crossing_time;\n  double comoving_char_speed_crossing_time;\n  double delta_r_crossing_time;\n  double target_char_speed;\n  double suggested_timescale;\n  double damping_timescale;\n  ControlErrorArgs control_error_args;\n  std::string update_message;\n  bool discontinuous_change_has_occurred;\n};\n\n/*!\n * \\brief Computes the size control error, updating the stored info.\n *\n * \\tparam Frame should be ::Frame::Distorted if ::Frame::Distorted exists.\n * \\param info struct containing parameters that will be used/filled. Some of\n *        the fields in info will guide the behavior of other control system\n *        components like the averager and (possibly) the time step.\n * \\param predictor_char_speed ZeroCrossingPredictor for the characteristic\n *          speed.\n * \\param predictor_comoving_char_speed ZeroCrossingPredictor for the\n *        comoving characteristic speed.\n * \\param predictor_delta_radius ZeroCrossingPredictor for the difference\n *        in radius between the horizon and the excision boundary.\n * \\param predictor_drift_limit_char_speed ZeroCrossingPredictor for the\n *        difference between the characteristic speed and\n *        min_allowed_char_speed.\n * \\param predictor_drift_limit_delta_radius ZeroCrossingPredictor for the\n *        difference between delta_radius and min_allowed_radial_distance.\n * \\param time the current time.\n * \\param control_error_delta_r the control error for the DeltaR state. This is\n *        used in other states as well.\n * \\param control_error_delta_r_outward the control error for the\n *        DeltaRDriftOutward state.  If std::nullopt, then DeltaRDriftOutward\n *        will not be used.\n * \\param max_allowed_radial_distance the maximum average radial distance\n *        between the horizon and the excision boundary that is allowed without\n *        triggering the DeltaRDriftOutward state.  If std::nullopt, then\n *        DeltaRDriftOutward will not be used.\n * \\param inward_drift_velocity a velocity that determines how fast the\n *        excision boundary drifts inward in state DeltaRDriftInward. If\n *        std::nullopt, then DeltaRDriftInward will not be used.\n * \\param min_allowed_radial_distance the minimum average radial distance\n *        between the horizon and the excision boundary that is allowed without\n *        triggering the DeltaRDriftInward state.  If std::nullopt, then\n *        DeltaRDriftInward will not be used.\n * \\param min_allowed_char_speed the minimum characteristic speed on the\n *        excision boundary that is allowed without triggering the\n *        DeltaRDriftInward state.  If std::nullopt, then\n *        DeltaRDriftInward will not be used.\n * \\param horizon_00 The l=0,m=0 component of the spherepack decomposition\n *        of the apparent horizon.  This is passed separately from the\n *        full apparent_horizon below because horizon_00 is time-averaged.\n * \\param dt_lambda_00 the time derivative of the map parameter lambda_00\n * \\param apparent_horizon the current horizon in frame Frame.\n * \\param excision_boundary a Strahlkorper representing the excision\n *        boundary in frame Frame.  Note that the excision boundary is assumed\n *        to be a sphere in the grid frame.\n * \\param lapse_on_excision_boundary Lapse on the excision boundary.\n * \\param frame_components_of_grid_shift The quantity\n *        \\f$\\beta^i \\frac{\\partial x^\\hat{i}}{\\partial x_i}\\f$ (see below)\n *        evaluated on the excision boundary.  This is a tensor in frame\n *        Frame.\n * \\param spatial_metric_on_excision_boundary metric in frame Frame.\n * \\param inverse_spatial_metric_on_excision_boundary metric in frame Frame.\n * \\param deriv_comoving_char_speed the derivative of the comoving char\n *        speed with respect to the map parameter lambda_00.\n * \\return Returns an `ErrorDiagnostics` object which, in addition to the actual\n *         control error, holds a lot of diagnostic information about how the\n *         control error was calculated. This information could be used to print\n *         to a file if desired.\n *\n * The characteristic speed that is needed here is\n * \\f{align}\n *     v &= -\\alpha -n_i\\beta^i \\\\\n *     v &= -\\alpha -n_\\hat{i}\\hat{\\beta}^\\hat{i}\n *           - n_\\hat{i}\\frac{\\partial x^\\hat{i}}{\\partial t} \\\\\n *     v &= -\\alpha -n_\\bar{i}\\bar{\\beta}^\\bar{i}\n *           - n_\\bar{i}\\frac{\\partial x^\\bar{i}}{\\partial t} \\\\\n *     v &= -\\alpha - n_\\hat{i}\n *          \\frac{\\partial x^\\hat{i}}{\\partial x^i} \\beta^i,\n *  \\f}\n *  where we have written many equivalent forms in terms of quantities\n *  defined in different frames.\n *\n *  Here \\f$\\alpha\\f$ is the lapse, which is invariant under frame\n *  transformations, \\f$n_i\\f$, \\f$n_\\hat{i}\\f$, and \\f$n_\\bar{i}\\f$\n *  are the metric-normalized normal one-form to the Strahlkorper in the\n *  grid, distorted, and inertial frames, and\n *  \\f$\\beta^i\\f$, \\f$\\hat{\\beta}^\\hat{i}\\f$, and \\f$\\bar{\\beta}^\\bar{i}\\f$\n *  are the shift in the grid, distorted, and inertial frames.\n *\n *  Note that we decorate the shift with hats and bars in addition to\n *  decorating its index, because the shift transforms in a non-obvious\n *  way under frame transformations so it is easy to make mistakes.\n *  To be clear, these different shifts are defined by\n * \\f{align}\n *   \\beta^i &= \\alpha^2 g^{0i},\\\\\n *   \\hat{\\beta}^\\hat{i} &= \\alpha^2 g^{\\hat{0}\\hat{i}},\\\\\n *   \\bar{\\beta}^\\bar{i} &= \\alpha^2 g^{\\bar{0}\\bar{i}},\n * \\f}\n *  where \\f$g^{ab}\\f$ is the spacetime metric, and they transform like\n * \\f{align}\n * \\hat{\\beta}^\\hat{i} &= \\beta^i \\frac{\\partial x^\\hat{i}}{\\partial x^i}-\n *  \\frac{\\partial x^\\hat{i}}{\\partial t}.\n * \\f}\n *\n * The quantity we pass as frame_components_of_grid_shift is\n * \\f{align}\n * \\beta^i \\frac{\\partial x^\\hat{i}}{\\partial x^i}\n * &= \\hat{\\beta}^\\hat{i} + \\frac{\\partial x^\\hat{i}}{\\partial t} \\\\\n * &= \\bar{\\beta}^\\bar{j}\\frac{\\partial x^\\hat{i}}{\\partial x^i}\n *    \\frac{\\partial x^i}{\\partial x^\\bar{j}} +\n *    \\frac{\\partial x^\\hat{i}}{\\partial x^\\bar{j}}\n *    \\frac{\\partial x^\\bar{j}}{\\partial t},\n * \\f}\n * where we have listed several equivalent formulas that involve quantities\n * in different frames.\n */\ntemplate <typename Frame>\nErrorDiagnostics control_error(\n    gsl::not_null<Info*> info,\n    gsl::not_null<intrp::ZeroCrossingPredictor*> predictor_char_speed,\n    gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_comoving_char_speed,\n    gsl::not_null<intrp::ZeroCrossingPredictor*> predictor_delta_radius,\n    gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_drift_limit_char_speed,\n    gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_drift_limit_delta_radius,\n    double time, double control_error_delta_r,\n    std::optional<double> control_error_delta_r_outward,\n    std::optional<double> max_allowed_radial_distance,\n    std::optional<double> inward_drift_velocity,\n    std::optional<double> min_allowed_radial_distance,\n    std::optional<double> min_allowed_char_speed, double horizon_00,\n    double dt_lambda_00, const ylm::Strahlkorper<Frame>& apparent_horizon,\n    const ylm::Strahlkorper<Frame>& excision_boundary,\n    const Scalar<DataVector>& lapse_on_excision_boundary,\n    const tnsr::I<DataVector, 3, Frame>& frame_components_of_grid_shift,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric_on_excision_boundary,\n    const tnsr::II<DataVector, 3, Frame>&\n        inverse_spatial_metric_on_excision_boundary,\n    const Scalar<DataVector>& deriv_comoving_char_speed);\n\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRNoDrift.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Initial.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::size::States {\nusing factory_creatable_states =\n    tmpl::list<AhSpeed, DeltaR, DeltaRDriftInward, DeltaRDriftOutward,\n               DeltaRNoDrift, Initial>;\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/Info.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace control_system::size {\nInfo::Info(std::unique_ptr<State> in_state, double in_damping_time,\n           double in_target_char_speed, double in_target_drift_velocity,\n           std::optional<double> in_suggested_time_scale,\n           bool in_discontinuous_change_has_occurred)\n    : state(std::move(in_state)),\n      damping_time(in_damping_time),\n      target_char_speed(in_target_char_speed),\n      target_drift_velocity(in_target_drift_velocity),\n      suggested_time_scale(in_suggested_time_scale),\n      discontinuous_change_has_occurred(in_discontinuous_change_has_occurred) {}\n\n// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,-warnings-as-errors)\nInfo::Info(const Info& rhs) {\n  set_all_but_state(rhs);\n  state = rhs.state->get_clone();\n}\n\nInfo& Info::operator=(const Info& rhs) {\n  set_all_but_state(rhs);\n  state = rhs.state->get_clone();\n  return *this;\n}\n\nvoid Info::pup(PUP::er& p) {\n  p | state;\n  p | damping_time;\n  p | target_char_speed;\n  p | target_drift_velocity;\n  p | suggested_time_scale;\n  p | discontinuous_change_has_occurred;\n}\n\nvoid Info::reset() {\n  suggested_time_scale = std::nullopt;\n  discontinuous_change_has_occurred = false;\n  // Currently nothing actually sets this, but we may want to reset it in the\n  // future when we add more States\n  // target_drift_velocity = 0.0;\n}\n\nvoid Info::set_all_but_state(const Info& info) {\n  damping_time = info.damping_time;\n  target_char_speed = info.target_char_speed;\n  target_drift_velocity = info.target_drift_velocity;\n  suggested_time_scale = info.suggested_time_scale;\n  discontinuous_change_has_occurred = info.discontinuous_change_has_occurred;\n}\n\nCrossingTimeInfo::CrossingTimeInfo(\n    const std::optional<double>& char_speed_crossing_time,\n    const std::optional<double>& comoving_char_speed_crossing_time,\n    const std::optional<double>& delta_radius_crossing_time,\n    const std::optional<double>& drift_limit_char_speed_crossing_time,\n    const std::optional<double>& drift_limit_delta_radius_crossing_time)\n    : t_char_speed(char_speed_crossing_time),\n      t_comoving_char_speed(comoving_char_speed_crossing_time),\n      t_delta_radius(delta_radius_crossing_time),\n      t_drift_limit_delta_radius(drift_limit_delta_radius_crossing_time),\n      t_drift_limit((drift_limit_char_speed_crossing_time.has_value() or\n                     drift_limit_delta_radius_crossing_time.has_value())\n                        ? std::optional<double>(std::min(\n                              drift_limit_char_speed_crossing_time.value_or(\n                                  std::numeric_limits<double>::max()),\n                              drift_limit_delta_radius_crossing_time.value_or(\n                                  std::numeric_limits<double>::max())))\n                        : std::nullopt) {\n  if (t_char_speed.value_or(-1.0) > 0.0) {\n    if (t_delta_radius.value_or(-1.0) > 0.0 and\n        t_delta_radius.value() <= t_char_speed.value()) {\n      horizon_will_hit_excision_boundary_first = true;\n    } else {\n      char_speed_will_hit_zero_first = true;\n    }\n  } else if (t_delta_radius.value_or(-1.0) > 0.0) {\n    horizon_will_hit_excision_boundary_first = true;\n  }\n}\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/Info.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n/// \\cond\nnamespace control_system::size {\nstruct State;\n}  // namespace control_system::size\n/// \\endcond\n\nnamespace control_system::size {\n\n/// Holds information that is saved between calls of SizeControl.\nstruct Info {\n  Info() = default;\n  Info(const Info& rhs);\n  Info& operator=(const Info& rhs);\n  Info(Info&& rhs) = default;\n  Info& operator=(Info&& rhs) = default;\n\n  Info(std::unique_ptr<State> in_state, double in_damping_time,\n       double in_target_char_speed, double in_target_drift_velocity,\n       std::optional<double> in_suggested_time_scale,\n       bool in_discontinuous_change_has_occurred);\n\n  // Info needs to be serializable because it will be\n  // stored inside of a ControlError.\n  void pup(PUP::er& p);\n\n  /// The current state of size control.\n  std::unique_ptr<State> state;\n  /// The current damping time associated with size control.\n  double damping_time;\n  /// target_char_speed is what the characteristic speed is driven\n  /// toward in state Label::AhSpeed.\n  double target_char_speed;\n  /// target_drift_velocity is what dr/dt (where r and t are distorted frame\n  /// variables) of the excision boundary is driven toward in state\n  /// Label::Initial.\n  double target_drift_velocity;\n  /// Sometimes State::update will request that damping_time\n  /// be changed; the new suggested value is suggested_time_scale. If it is a\n  /// `std::nullopt` then there is no suggestion.\n  std::optional<double> suggested_time_scale;\n  /// discontinuous_change_has_occurred is set to true by\n  /// State::update if it changes anything in such a way that\n  /// the control signal jumps discontinuously in time.\n  bool discontinuous_change_has_occurred;\n\n  /// Reset `discontinuous_change_has_occurred` and `suggested_time_scale`\n  void reset();\n\n private:\n  void set_all_but_state(const Info& info);\n};\n\n/// Holds information about crossing times, as computed by\n/// ZeroCrossingPredictors.\nstruct CrossingTimeInfo {\n  CrossingTimeInfo(\n      const std::optional<double>& char_speed_crossing_time,\n      const std::optional<double>& comoving_char_speed_crossing_time,\n      const std::optional<double>& delta_radius_crossing_time,\n      const std::optional<double>& drift_limit_char_speed_crossing_time,\n      const std::optional<double>& drift_limit_delta_radius_crossing_time);\n  /// t_char_speed is the time (relative to the current time) when the\n  /// minimum characteristic speed is predicted to cross zero (or nullopt if\n  /// the minimum characteristic speed is increasing).\n  std::optional<double> t_char_speed;\n  /// t_comoving_char_speed is the time (relative to the current time) when the\n  /// minimum comoving characteristic speed is predicted to cross zero\n  /// (or nullopt if the minimum comoving characteristic speed is increasing).\n  std::optional<double> t_comoving_char_speed;\n  /// t_delta_radius is the time (relative to the current time) when the\n  /// minimum distance between the horizon and the excision boundary is\n  /// predicted to cross zero (or nullopt if the minimum distance is\n  /// increasing).\n  std::optional<double> t_delta_radius;\n  /// t_drift_limit_delta_radius is the time (relative to the current time) when\n  /// the minimum distance between the horizon and the excision boundary is\n  /// predicted to cross the min_allowed_radial_distance associated with the\n  /// state DeltaRDriftInward (or nullopt if the minimum distance is\n  /// decreasing).\n  /// In SpEC this quantity is called MaxDeltaRXTime\n  std::optional<double> t_drift_limit_delta_radius;\n  /// t_drift_limit is a convenient variable that is\n  /// the minimum of t_drift_limit_delta_radius and t_drift_limit_char_speed,\n  /// or nullopt if both drift limits are nullopt.\n  /// In SpEC this quantity is called State3XTime\n  std::optional<double> t_drift_limit;\n  /// Extra variables to simplify the logic; these indicate whether\n  /// the characteristic speed or the excision boundary (or neither) are\n  /// expected to cross zero soon.\n  bool char_speed_will_hit_zero_first{false};\n  bool horizon_will_hit_excision_boundary_first{false};\n};\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/Initial.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/Initial.hpp\"\n\n#include <memory>\n#include <optional>\n#include <sstream>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace control_system::size::States {\n\nstd::unique_ptr<State> Initial::get_clone() const {\n  return std::make_unique<Initial>(*this);\n}\n\nstd::string Initial::update(const gsl::not_null<Info*> info,\n                            const StateUpdateArgs& update_args,\n                            const CrossingTimeInfo& crossing_time_info) const {\n  // Note that delta_radius_is_in_danger and char_speed_is_in_danger\n  // can be different for different States.\n  const bool char_speed_is_in_danger =\n      crossing_time_info.char_speed_will_hit_zero_first and\n      crossing_time_info.t_char_speed.value_or(\n          std::numeric_limits<double>::infinity()) < info->damping_time;\n  const bool delta_radius_is_in_danger =\n      crossing_time_info.horizon_will_hit_excision_boundary_first and\n      crossing_time_info.t_delta_radius.value_or(\n          std::numeric_limits<double>::infinity()) < info->damping_time and\n      not char_speed_is_in_danger;\n\n  // This factor is present in SpEC, but it probably isn't necessary\n  // (but it doesn't hurt either).  We keep it here to facilitate\n  // comparison with SpEC.  The value of 1.01 was chosen in SpEC, but\n  // nothing should be sensitive to small changes in this value as long\n  // as it is something slightly greater than unity.\n  constexpr double non_oscillation_factor = 1.01;\n\n  // This factor is present in SpEC, and it is used to prevent\n  // oscillations between states.  The value was chosen in SpEC, but\n  // nothing should be sensitive to small changes in this value as\n  // long as it is slightly greater than unity.\n  constexpr double non_oscillation_drift_outward_factor = 1.1;\n\n  std::stringstream ss{};\n\n  if (char_speed_is_in_danger) {\n    info->discontinuous_change_has_occurred = true;\n    info->state = std::make_unique<States::AhSpeed>();\n    info->target_char_speed =\n        update_args.min_char_speed * non_oscillation_factor;\n    info->suggested_time_scale = crossing_time_info.t_char_speed;\n    ss << \"Current state Initial. Char speed in danger. Switching to \"\n          \"AhSpeed.\\n\";\n    ss << \" Target char speed = \" << info->target_char_speed << \"\\n\";\n    ss << \" Suggested timescale = \" << info->suggested_time_scale;\n  } else if (delta_radius_is_in_danger\n             or update_args.min_comoving_char_speed > 0.0) {\n    info->discontinuous_change_has_occurred = true;\n    if (delta_radius_is_in_danger) {\n      info->suggested_time_scale = crossing_time_info.t_delta_radius;\n    }\n    const bool drift_inward = should_activate_inward_drift(update_args);\n    if (drift_inward) {\n      info->state = std::make_unique<States::DeltaRDriftInward>();\n      info->target_char_speed = target_speed_for_inward_drift(\n          update_args.avg_distorted_normal_dot_unit_coord_vector,\n          update_args.min_char_speed,\n          update_args.inward_drift_velocity.value());\n    } else {\n      info->state = std::make_unique<States::DeltaR>();\n    }\n    ss << \"Current state Initial. \"\n       << (delta_radius_is_in_danger ? \"DeltaR is in danger\"\n                                     : \"Comoving char speed positive\")\n       << \". Switching to \" << (drift_inward ? \"DeltaRDriftInward.\" : \"DeltaR.\")\n       << \"\\n\";\n    ss << \" Target char speed = \" << info->target_char_speed;\n  } else if (update_args.average_radial_distance.has_value() and\n             update_args.average_radial_distance.value() >\n                 non_oscillation_drift_outward_factor *\n                     update_args.max_allowed_radial_distance.value_or(\n                         std::numeric_limits<double>::infinity())) {\n    info->discontinuous_change_has_occurred = true;\n    info->state = std::make_unique<States::DeltaRDriftOutward>();\n    ss << \"Current state Initial. \"\n          \"Horizon too far from excision boundary. Switching to \"\n          \"DeltaRDriftOutward\";\n  } else {\n    ss << \"Current state Initial. No change necessary. Staying in Initial.\";\n  }\n  // Otherwise, no change.\n\n  return ss.str();\n}\n\ndouble Initial::control_error(\n    const Info& info, const ControlErrorArgs& control_error_args) const {\n  // The return value is the Q that directly controls the speed of the\n  // excision boundary in the distorted frame relative to the grid frame.\n  return info.target_drift_velocity -\n         control_error_args.time_deriv_of_lambda_00;\n}\n\nPUP::able::PUP_ID Initial::my_PUP_ID = 0;\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/Initial.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::size::States {\nclass Initial : public State {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A temporary state for the beginning of a simulation. This is state 0 in \"\n      \"SpEC.\"};\n  Initial() = default;\n  std::string name() const override { return \"Initial\"; }\n  size_t number() const override { return 0; }\n  std::unique_ptr<State> get_clone() const override;\n  std::string update(const gsl::not_null<Info*> info,\n                     const StateUpdateArgs& update_args,\n                     const CrossingTimeInfo& crossing_time_info) const override;\n  double control_error(\n      const Info& info,\n      const ControlErrorArgs& control_error_args) const override;\n\n  WRAPPED_PUPable_decl_template(Initial);  // NOLINT\n  explicit Initial(CkMigrateMessage* const /*msg*/) {}\n};\n}  // namespace control_system::size::States\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/RegisterDerivedWithCharm.hpp\"\n\n#include <memory>\n#include <pup.h>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRNoDrift.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Initial.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace control_system::size {\nvoid register_derived_with_charm() {\n  register_classes_with_charm<States::Initial, States::AhSpeed, States::DeltaR,\n                              States::DeltaRDriftInward, States::DeltaRNoDrift,\n                              States::DeltaRDriftOutward>();\n}\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace control_system::size {\nvoid register_derived_with_charm();\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/State.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\cond\nnamespace control_system::size {\nstruct Info;\nstruct CrossingTimeInfo;\n}  // namespace control_system::size\n/// \\endcond\n\nnamespace control_system::size {\n\n/*!\n * \\brief Packages some of the inputs to the State::update, so that\n * State::update doesn't need a large number of arguments.\n */\nstruct StateUpdateArgs {\n  /// min_char_speed is the minimum over the excision boundary\n  /// of Eq. 89 of \\cite Hemberger2012jz.\n  double min_char_speed;\n  /// min_comoving_char_speed is the minimum over the excision boundary\n  /// of Eq. 28 of \\cite Hemberger2012jz.\n  double min_comoving_char_speed;\n  /// horizon_00 is the spherepack coefficient of the apparent\n  /// horizon.\n  double horizon_00;\n  /// control_error_delta_r is the control error when the control system\n  /// is in state Label::DeltaR.\n  /// This is Q in Eq. 96 of \\cite Hemberger2012jz.\n  double control_error_delta_r;\n  /// average_radial_distance is the average distance between the horizon and\n  /// the excision boundary. Used only for state DeltaRDriftOutward.\n  /// If std::nullopt, then DeltaRDriftOutward will not be used.\n  std::optional<double> average_radial_distance;\n  /// max_allowed_radial_distance is the minimum distance between the horizon\n  /// and the excision boundary that will trigger state\n  /// Label::DeltaRDriftOutward.  If std::nullopt, then DeltaRDriftOutward\n  /// will never be triggered.\n  std::optional<double> max_allowed_radial_distance;\n  /// avg_distorted_normal_dot_unit_coord_vector is the same quantity as\n  /// ControlErrorArgs::avg_distorted_normal_dot_unit_coord_vector.\n  double avg_distorted_normal_dot_unit_coord_vector;\n  /// inward_drift_velocity is a positive quantity that represents how\n  /// fast the horizon and the excision boundary move apart in state\n  /// DeltaRDriftInward.  If std::nullopt, then DeltaRDriftInward will\n  /// never be triggered.\n  std::optional<double> inward_drift_velocity;\n  /// min_allowed_radial_distance is the minimum distance between the horizon\n  /// and the excision boundary that will trigger state\n  /// Label::DeltaRDriftInward.\n  std::optional<double> min_allowed_radial_distance;\n  /// min_allowed_char_speed is the minimum char speed that will\n  /// trigger state DeltaRDriftInward.  If both\n  /// min_allowed_radial_distance and min_allowed_char_speed are\n  /// std::nullopt, then DeltaRDriftInward will never be triggered.\n  std::optional<double> min_allowed_char_speed;\n  /// comoving_char_speed_increasing_inward is true if the comoving char speed\n  /// increases as the excision boundary is moved inward.\n  bool comoving_char_speed_increasing_inward;\n};\n\n/*!\n * \\brief Packages some of the inputs to the State::control_error, so that\n * State::control_error doesn't need a large number of arguments.\n */\nstruct ControlErrorArgs {\n  double min_char_speed;\n  double control_error_delta_r;\n  /*!\n   * \\brief control_error_delta_r_outward is the control error in the\n   * case DeltaRDriftOutward, the state where there is an outward drift\n   * caused by too large separation between the horizon and the excision\n   * boundary.  If std::nullopt, then state DeltaRDriftOutward will not be used.\n   */\n  std::optional<double> control_error_delta_r_outward;\n  /*!\n   * \\brief avg_distorted_normal_dot_unit_coord_vector is the average of\n   * distorted_normal_dot_unit_coord_vector over the excision\n   * boundary.\n   *\n   * \\details Here distorted_normal_dot_unit_coord_vector is Eq. 93\n   * of \\cite Hemberger2012jz.  distorted_normal_dot_unit_coord_vector is\n   * \\f$\\hat{n}_i x^i/r\\f$ where \\f$\\nat{n}_i\\f$ is the\n   * distorted-frame unit normal to the excision boundary (pointing\n   * INTO the hole, i.e. out of the domain), and \\f$x^i/r\\f$ is the\n   * distorted-frame (or equivalently the grid frame because it is\n   * invariant between these two frames because of the required\n   * limiting behavior of the map we choose) Euclidean normal vector\n   * from the center of the excision-boundary Strahlkorper to each\n   * point on the excision-boundary Strahlkorper.\n   */\n  double avg_distorted_normal_dot_unit_coord_vector;\n  /*!\n   * \\brief time_deriv_of_lambda_00 is the time derivative of the quantity\n   * lambda_00 that appears in \\cite Hemberger2012jz.\n   *\n   * \\details time_deriv_of_lambda_00 is (minus) the radial velocity of the\n   * excision boundary in the distorted frame with respect to the grid frame.\n   */\n  double time_deriv_of_lambda_00;\n};\n\n/*!\n * \\brief Represents a 'state' of the size control system.\n *\n * \\details Each 'state' of the size control system has a different control\n * signal, which has a different purpose, even though each state\n * controls the same map quantity, namely the Y00 coefficient of the\n * shape map.  For example, state Label::AhSpeed controls\n * the Y00 coefficient of the shape map so that the minimum\n * characteristic speed is driven towards a target value, and state\n * Label::DeltaR controls the Y00 coefficient of the shape\n * map (or the Y00 coefficient of a separate spherically-symmetric size\n * map) so that the minimum difference between the horizon radius and\n * the excision boundary radius is driven towards a constant.\n *\n * Each state has its own logic (the 'update' function) that\n * determines values of certain parameters (i.e. the things in\n * Info), including whether the control system should\n * transition to a different state.\n *\n * The different states are:\n * - Initial: drives dr/dt of the excision boundary to\n *   Info::target_drift_velocity.\n * - AhSpeed: drives the minimum characteristic speed on the excision boundary\n *   to Info::target_char_speed.\n * - DeltaR: drives the minimum distance between the horizon and the excision\n *   boundary to be constant in time.\n * - DeltaRDriftInward: Same as DeltaR but the excision boundary has a small\n *   velocity inward.  This state is triggered when it is deemed that the\n *   excision boundary and the horizon are too close to each other; the\n *   small velocity makes the excision boundary and the horizon drift apart.\n * - DeltaRDriftOutward: Same as DeltaR but the excision boundary has a small\n *   velocity outward.  This state is triggered when it is deemed that the\n *   excision boundary and the horizon are too far apart.\n * - DeltaRNoDrift: Same as DeltaR except for the logic that\n *   determines how DeltaRNoDrift changes to other states.\n *   DeltaRNoDrift is allowed (under some circumstances) to change\n *   to state DeltaR, but DeltaRDriftOutward and DeltaRDriftInward\n *   are never allowed to change to state DeltaR.  Instead\n *   DeltaRDriftOutward and DeltaRDriftInward are allowed (under\n *   some circumstances) to change to state DeltaRNoDrift.\n *\n * The reason that DeltaRDriftInward, DeltaRDriftOutward, and\n * DeltaRNoDrift are separate states is to simplify the logic.  In\n * principle, all 3 of those states could be merged with state\n * DeltaR, because the control error is the same for all four states\n * (except for a velocity term that could be set to zero).  But if that\n * were done, then there would need to be additional complicated\n * logic in determining transitions between different states, and\n * that logic would depend not only on the current state, but also on\n * the previous state.\n */\nclass State : public PUP::able {\n public:\n  State() = default;\n  State(const State& /*rhs*/) = default;\n  State& operator=(const State& /*rhs*/) = default;\n  State(State&& /*rhs*/) = default;\n  State& operator=(State&& /*rhs*/) = default;\n  virtual ~State() override = default;\n\n  /// Name of this state\n  virtual std::string name() const = 0;\n\n  /// Return a size_t that corresponds to the state number in SpEC\n  virtual size_t number() const = 0;\n\n  virtual std::unique_ptr<State> get_clone() const = 0;\n  /*!\n   * \\brief Updates the Info in `info`.\n   *\n   * \\note Notice that `info` includes a state, which might be different than\n   * the current state upon return. It is the caller's responsibility to check\n   * if the current state has changed.\n   *\n   * \\return The return string is used as a helpful diagnostic that may be\n   * printed to determine what logic decisions the state is making (depends on\n   * the `control_system::Tags::Verbosity` flag).\n   */\n  virtual std::string update(\n      const gsl::not_null<Info*> info, const StateUpdateArgs& update_args,\n      const CrossingTimeInfo& crossing_time_info) const = 0;\n  /// Returns the control signal, but does not modify the state or any\n  /// parameters.\n  virtual double control_error(\n      const Info& info, const ControlErrorArgs& control_error_args) const = 0;\n\n  WRAPPED_PUPable_abstract(State);  // NOLINT\n  explicit State(CkMigrateMessage* msg) : PUP::able(msg) {}\n};\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/StateHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size/StateHistory.hpp\"\n\n#include <deque>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <unordered_map>\n#include <utility>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRNoDrift.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Initial.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n\nnamespace control_system::size {\nStateHistory::StateHistory() { initialize_stored_control_errors(); }\n\nStateHistory::StateHistory(const size_t num_times_to_store)\n    : num_times_to_store_(num_times_to_store) {\n  initialize_stored_control_errors();\n}\n\nvoid StateHistory::initialize_stored_control_errors() {\n  stored_control_errors_[States::Initial{}.number()];\n  stored_control_errors_[States::DeltaR{}.number()];\n  stored_control_errors_[States::AhSpeed{}.number()];\n  stored_control_errors_[States::DeltaRDriftInward{}.number()];\n  stored_control_errors_[States::DeltaRNoDrift{}.number()];\n  stored_control_errors_[States::DeltaRDriftOutward{}.number()];\n}\n\nvoid StateHistory::store(double time, const Info& info,\n                         const ControlErrorArgs& control_error_args) {\n  const auto store_state = [this, &time, &info,\n                            &control_error_args](auto state) {\n    const double control_error = state.control_error(info, control_error_args);\n    std::deque<std::pair<double, double>>& history =\n        stored_control_errors_.at(state.number());\n    history.emplace_back(time, control_error);\n    while (history.size() > num_times_to_store_) {\n      history.pop_front();\n    }\n  };\n\n  store_state(States::Initial{});\n  store_state(States::DeltaR{});\n  store_state(States::AhSpeed{});\n  store_state(States::DeltaRDriftInward{});\n  store_state(States::DeltaRNoDrift{});\n  store_state(States::DeltaRDriftOutward{});\n}\n\nconst std::deque<std::pair<double, double>>& StateHistory::state_history(\n    const size_t state_number) const {\n  return stored_control_errors_.at(state_number);\n}\n\nvoid StateHistory::pup(PUP::er& p) {\n  p | num_times_to_store_;\n  p | stored_control_errors_;\n}\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/StateHistory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <deque>\n#include <pup.h>\n#include <unordered_map>\n#include <utility>\n\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n\nnamespace control_system::size {\n/*!\n * \\brief A struct for holding a history of control errors for each state in the\n * `control_system::Systems::Size` control system.\n */\nstruct StateHistory {\n  StateHistory();\n\n  /// \\brief Only keep `num_times_to_store` entries in the state_history\n  StateHistory(size_t num_times_to_store);\n\n  /*!\n   * \\brief Store the control errors for all `control_system::size::State`s.\n   *\n   * \\param time Time to store control errors at\n   * \\param info `control_system::size::Info`\n   * \\param control_error_args `control_system::size::ControlErrorArgs`\n   */\n  void store(double time, const Info& info,\n             const ControlErrorArgs& control_error_args);\n\n  /*!\n   * \\brief Return a const reference to the stored control errors from all the\n   * states.\n   *\n   * \\param state_number `size_t` corresponding to the\n   * `control_system::size::State::number()` of a state.\n   * \\return std::deque<std::pair<double, double>> The `std::pair` holds\n   * the time and control error, respectively. The `std::deque` is ordered with\n   * earlier times at the \"front\" and later times at the \"back\". This is to make\n   * iteration over the deque easier as we typically want to start with earlier\n   * times.\n   */\n  const std::deque<std::pair<double, double>>& state_history(\n      size_t state_number) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  void initialize_stored_control_errors();\n\n  size_t num_times_to_store_{};\n  std::unordered_map<size_t, std::deque<std::pair<double, double>>>\n      stored_control_errors_{};\n};\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size/Update.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/ControlErrors/Size.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace control_system::size {\n/*!\n * \\brief Updates the Averager with information from the \\link\n * control_system::ControlErrors::Size Size control error \\endlink.\n *\n * \\details After we calculate the control error, we check if a discontinuous\n * change has occurred (the internal `control_system::size::State` changed). If\n * no discontinuous change has occurred, then we do nothing. If one did occur,\n * we get a history of control errors from the \\link\n * control_system::ControlErrors::Size Size control error \\endlink and use that\n * to repopulate the Averager history.\n *\n * \\note See `control_system::Tags::Averager` for why the Averager is templated\n * on `DerivOrder - 1`.\n */\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon,\n          typename Metavariables>\nvoid update_averager(\n    const gsl::not_null<Averager<DerivOrder - 1>*> averager,\n    const gsl::not_null<ControlErrors::Size<DerivOrder, Horizon>*>\n        control_error,\n    const Parallel::GlobalCache<Metavariables>& cache, const double time,\n    const DataVector& current_timescale,\n    const std::string& function_of_time_name, const int current_measurement) {\n  if (control_error->discontinuous_change_has_occurred()) {\n    control_error->reset();\n    averager->clear();\n\n    const std::deque<std::pair<double, double>> control_error_history =\n        control_error->control_error_history();\n\n    if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n      std::vector<double> history_times{};\n      for (const auto& history : control_error_history) {\n        history_times.push_back(history.first);\n      }\n      Parallel::printf(\n          \"%s, time = %.16f: current measurement = %d, Discontinuous \"\n          \"change has occurred. Repopulating averager with control error \"\n          \"history from times: %s\\n\",\n          function_of_time_name, time, current_measurement, history_times);\n    }\n\n    for (const auto& [stored_time, stored_control_error] :\n         control_error_history) {\n      // Size 1 because it's size control\n      averager->update(stored_time, DataVector{1, stored_control_error},\n                       current_timescale);\n    }\n  }\n}\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Size.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Initial.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"NumericalAlgorithms/Interpolation/ZeroCrossingPredictor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace control_system {\nnamespace size {\ndouble control_error_delta_r(const double horizon_00,\n                             const double dt_horizon_00, const double lambda_00,\n                             const double dt_lambda_00,\n                             const double grid_frame_excision_sphere_radius) {\n  const double Y00 = 0.25 * M_2_SQRTPI;\n\n  // This corresponds to 'DeltaRPolicy=Relative' in SpEC.\n  // Notice that both horizon_00 and dt_horizon_00 are actually spherepack\n  // coefs, not spherical harmonic coefs. However, they only show up in this\n  // expression as a ratio, so the spherepack factor cancels out, thus we don't\n  // add it in here.\n  return dt_horizon_00 * (lambda_00 - grid_frame_excision_sphere_radius / Y00) /\n             horizon_00 -\n         dt_lambda_00;\n}\n}  // namespace size\n\nnamespace ControlErrors {\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nSize<DerivOrder, Horizon>::Size(\n    const int max_times, const double smooth_avg_timescale_frac,\n    TimescaleTuner<true> smoother_tuner,\n    std::unique_ptr<size::State> initial_state,\n    std::optional<DeltaRDriftOutwardOptions> delta_r_drift_outward_options,\n    std::optional<DeltaRDriftInwardOptions> delta_r_drift_inward_options)\n    : smoother_tuner_(std::move(smoother_tuner)),\n      delta_r_drift_outward_options_(delta_r_drift_outward_options),\n      delta_r_drift_inward_options_(delta_r_drift_inward_options) {\n  if (not smoother_tuner_.timescales_have_been_set()) {\n    smoother_tuner_.resize_timescales(1);\n  }\n  const auto max_times_size_t = static_cast<size_t>(max_times);\n  horizon_coef_averager_ =\n      Averager<DerivOrder>{smooth_avg_timescale_frac, true};\n  info_.state = std::move(initial_state);\n  char_speed_predictor_ = intrp::ZeroCrossingPredictor{3, max_times_size_t};\n  comoving_char_speed_predictor_ =\n      intrp::ZeroCrossingPredictor{3, max_times_size_t};\n  delta_radius_predictor_ = intrp::ZeroCrossingPredictor{3, max_times_size_t};\n  drift_limit_char_speed_predictor_ =\n      intrp::ZeroCrossingPredictor{3, max_times_size_t};\n  drift_limit_delta_radius_predictor_ =\n      intrp::ZeroCrossingPredictor{3, max_times_size_t};\n  state_history_ = size::StateHistory{DerivOrder + 1};\n  legend_ = std::vector<std::string>{\"Time\",\n                                     \"ControlError\",\n                                     \"StateNumber\",\n                                     \"DiscontinuousChangeHasOccurred\",\n                                     \"FunctionOfTime\",\n                                     \"DtFunctionOfTime\",\n                                     \"HorizonCoef00\",\n                                     \"AveragedDtHorizonCoef00\",\n                                     \"RawDtHorizonCoef00\",\n                                     \"SmootherTimescale\",\n                                     \"MinDeltaR\",\n                                     \"MinRelativeDeltaR\",\n                                     \"AvgDeltaR\",\n                                     \"AvgRelativeDeltaR\",\n                                     \"ControlErrorDeltaR\",\n                                     \"TargetCharSpeed\",\n                                     \"MinCharSpeed\",\n                                     \"MinComovingCharSpeed\",\n                                     \"CharSpeedCrossingTime\",\n                                     \"ComovingCharSpeedCrossingTime\",\n                                     \"DeltaRCrossingTime\",\n                                     \"SuggestedTimescale\",\n                                     \"DampingTime\"};\n  subfile_name_ = \"/ControlSystems/Size\" + get_output(Horizon) + \"/Diagnostics\";\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nSize<DerivOrder, Horizon>::Size(const Size<DerivOrder, Horizon>& rhs) {\n  *this = rhs;\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nSize<DerivOrder, Horizon>& Size<DerivOrder, Horizon>::operator=(\n    const Size<DerivOrder, Horizon>& rhs) {\n  smoother_tuner_ = rhs.smoother_tuner_;\n  horizon_coef_averager_ = rhs.horizon_coef_averager_;\n  info_ = rhs.info_;\n  char_speed_predictor_ = rhs.char_speed_predictor_;\n  comoving_char_speed_predictor_ = rhs.comoving_char_speed_predictor_;\n  delta_radius_predictor_ = rhs.delta_radius_predictor_;\n  state_history_ = rhs.state_history_;\n  legend_ = rhs.legend_;\n  subfile_name_ = rhs.subfile_name_;\n  delta_r_drift_outward_options_ = rhs.delta_r_drift_outward_options_;\n\n  return *this;\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nstd::optional<double> Size<DerivOrder, Horizon>::get_suggested_timescale()\n    const {\n  return info_.suggested_time_scale;\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nbool Size<DerivOrder, Horizon>::discontinuous_change_has_occurred() const {\n  return info_.discontinuous_change_has_occurred;\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nvoid Size<DerivOrder, Horizon>::reset() {\n  info_.reset();\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nstd::deque<std::pair<double, double>>\nSize<DerivOrder, Horizon>::control_error_history() const {\n  std::deque<std::pair<double, double>> history =\n      state_history_.state_history(info_.state->number());\n  // pop back so we don't include the current time, otherwise the averager\n  // will error\n  history.pop_back();\n  return history;\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nvoid Size<DerivOrder, Horizon>::pup(PUP::er& p) {\n  p | smoother_tuner_;\n  p | horizon_coef_averager_;\n  p | info_;\n  p | char_speed_predictor_;\n  p | comoving_char_speed_predictor_;\n  p | delta_radius_predictor_;\n  p | drift_limit_char_speed_predictor_;\n  p | drift_limit_delta_radius_predictor_;\n  p | state_history_;\n  p | legend_;\n  p | subfile_name_;\n  p | delta_r_drift_outward_options_;\n  p | delta_r_drift_inward_options_;\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nSize<DerivOrder,\n     Horizon>::DeltaRDriftOutwardOptions::DeltaRDriftOutwardOptions() = default;\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nSize<DerivOrder, Horizon>::DeltaRDriftOutwardOptions::DeltaRDriftOutwardOptions(\n    double max_allowed_radial_distance_in, double outward_drift_velocity_in,\n    double outward_drift_timescale_in)\n    : max_allowed_radial_distance(max_allowed_radial_distance_in),\n      outward_drift_velocity(outward_drift_velocity_in),\n      outward_drift_timescale(outward_drift_timescale_in) {}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nvoid Size<DerivOrder, Horizon>::DeltaRDriftOutwardOptions::pup(PUP::er& p) {\n  p | max_allowed_radial_distance;\n  p | outward_drift_velocity;\n  p | outward_drift_timescale;\n}\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nSize<DerivOrder,\n     Horizon>::DeltaRDriftInwardOptions::DeltaRDriftInwardOptions() = default;\n\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nSize<DerivOrder, Horizon>::DeltaRDriftInwardOptions::DeltaRDriftInwardOptions(\n    double min_allowed_radial_distance_in, double min_allowed_char_speed_in,\n    double inward_drift_velocity_in)\n    : min_allowed_radial_distance(min_allowed_radial_distance_in),\n      min_allowed_char_speed(min_allowed_char_speed_in),\n      inward_drift_velocity(inward_drift_velocity_in) {}\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nvoid Size<DerivOrder, Horizon>::DeltaRDriftInwardOptions::pup(PUP::er& p) {\n  p | min_allowed_radial_distance;\n  p | min_allowed_char_speed;\n  p | inward_drift_velocity;\n}\n\n#define DERIV_ORDER(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define HORIZON(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data) \\\n  template struct Size<DERIV_ORDER(data), HORIZON(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3),\n                        (::domain::ObjectLabel::A, ::domain::ObjectLabel::B,\n                         ::domain::ObjectLabel::None))\n\n#undef INSTANTIATE\n#undef HORIZON\n#undef DERIV_ORDER\n}  // namespace ControlErrors\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Size.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/ControlErrors/Size/ComovingCharSpeedDerivative.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Error.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"ControlSystem/ControlErrors/Size/StateHistory.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"NumericalAlgorithms/Interpolation/ZeroCrossingPredictor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system {\nnamespace size {\n/*!\n * \\brief Function that computes the control error for\n * `control_system::size::States::DeltaR`.\n *\n * This is helpful to have calculated separately because other control errors\n * may make use of this quantity. The equation for the control error is given in\n * Eq. 96 in \\cite Hemberger2012jz.\n *\n * \\param horizon_00 The $l=0,m=0$ coefficient of the apparent horizon in the\n * distorted frame.\n * \\param dt_horizon_00 The $l=0,m=0$ coefficient of the time derivative of the\n * apparent horizon in the distorted frame, where the derivative is taken in the\n * distorted frame as well.\n * \\param lambda_00 The $l=0,m=0$ component of the function of time for the time\n * dependent map\n * \\param dt_lambda_00 The $l=0,m=0$ component of the time derivative of the\n * function of time for the time dependent map\n * \\param grid_frame_excision_sphere_radius Radius of the excision sphere in the\n * grid frame\n */\ndouble control_error_delta_r(const double horizon_00,\n                             const double dt_horizon_00, const double lambda_00,\n                             const double dt_lambda_00,\n                             const double grid_frame_excision_sphere_radius);\n}  // namespace size\n\nnamespace ControlErrors {\n/*!\n * \\brief Control error in the for the \\f$l=0\\f$ component of the\n * `domain::CoordinateMaps::TimeDependent::Shape` map.\n *\n * \\details The goal of this control error is\n *\n * 1. Keep the excision sphere inside the horizon\n * 2. Maintain a fixed distance between the excision surface and the horizon\n * surface.\n * 3. Prevent the characteristic field \\f$ u^-_{ab} \\f$ associated with the\n * characteristic speed \\f$ v_- \\f$ in `gh::characteristic_speeds` from coming\n * into the domain.\n *\n * For a more detailed account of how this is accomplished, see\n * `control_system::size::State` and `control_system::size::control_error` which\n * this class calls.\n *\n * This class holds a `control_system::size::Info` and three different\n * `intrp::ZeroCrossingPredictor`s internally which are needed to calculate the\n * `control_system::size::control_error`. Additionally, this class stores a\n * history of control errors for all `control_system::size::State`s using a\n * `control_system::size::StateHistory`. This is useful for when a discontinuous\n * change happens (switching `control_system::size::State`s) and we need to\n * repopulate the `Averager` with a history of the control error. It also\n * conforms to the `control_system::protocols::ControlError` protocol.\n *\n * In order to calculate the control error, we need the $\\ell = 0, m = 0$\n * coefficient of the horizon and its time derivative. However, because we will\n * be finding the horizon fairly often, the value of the coefficient and its\n * derivative won't change smoothly because of horizon finder noise (different\n * number of iterations). But we expect these quantities to be smooth when\n * making state decisions. So to account for this, we use an `Averager` and a\n * `TimescaleTuner` to smooth out the horizon coefficient and get its\n * derivative. Every measurement, we update this smoothing averager with the\n * $\\ell = 0, m = 0$ coefficient of the horizon and the current smoothing\n * timescale. Then, once we have enough measurements, we use the\n * `Averager::operator()` to get the averaged coefficient and its time\n * derivative. Since `Averager%s` calculate the average at an \"averaged time\",\n * we have to account for this small offset from the current time with a simple\n * Taylor expansion. Then we use this newly averaged and corrected coefficient\n * (and time derivative) in our calculation of the control error. The timescale\n * in the smoothing `TimescaleTuner` is then updated using the difference\n * between the averaged and un-averaged coefficient (and its time derivative).\n *\n * In addition to calculating the control error, if the\n * `control_system::Tags::WriteDataToDisk` tag inside the\n * `Parallel::GlobalCache` is true, then a diagnostic file named\n * `Diagnostics.dat` is also written to the same group that\n * `control_system::write_components_to_disk` would write the standard control\n * system output (`/ControlSystems/Size/`). The columns of this diagnostic file\n * are as follows (with a small explanation if the name isn't clear):\n *\n * - %Time\n * - ControlError\n * - StateNumber: Result of `control_system::size::State::number()`\n * - DiscontinuousChangeHasOccurred: 1.0 for true, 0.0 for false.\n * - FunctionOfTime\n * - DtFunctionOfTime\n * - HorizonCoef00\n * - AveragedDtHorizonCoef00: The averaged 00 component of the horizon\n *   (averaging scheme detailed above.)\n * - RawDtHorizonCoef00: The raw 00 component of the horizon passed in to the\n *   control error.\n * - SmootherTimescale: Damping timescale for the averaging of DtHorizonCoef00.\n * - MinDeltaR: The minimum of the `gr::surfaces::radial_distance` between the\n *   horizon and the excision surfaces.\n * - MinRelativeDeltaR: MinDeltaR divided by the\n *   `ylm::Strahlkorper::average_radius` of the horizon\n * - AvgDeltaR: Same as MinDeltaR except it's the average radii.\n * - AvgRelativeDeltaR: AvgDeltaR divided by the average radius of the horizon\n * - ControlErrorDeltaR: \\f$ \\dot{S}_{00} (\\lambda_{00} -\n *   r_{\\mathrm{excision}}^{\\mathrm{grid}} / Y_{00}) / S_{00} -\n *   \\dot{\\lambda}_{00} \\f$\n * - TargetCharSpeed\n * - MinCharSpeed\n * - MinComovingCharSpeed: Eq. 98 in \\cite Hemberger2012jz\n * - CharSpeedCrossingTime: %Time at which the min char speed is predicted to\n *   cross zero and become negative (or 0.0 if that time is in the past).\n * - ComovingCharSpeedCrossingTime: %Time at which the min comoving char speed\n *   is predicted to cross zero and become negative (or 0.0 if that time is in\n *   the past).\n * - DeltaRCrossingTime: %Time at which the distance between the excision and\n *   horizon surfaces is predicted to be zero (or 0.0 if that time is in the\n *   past).\n * - SuggestedTimescale: A timescale for the `TimescaleTuner` suggested by one\n *   of the State%s (or 0.0 if no timescale was suggested)\n * - DampingTime\n */\ntemplate <size_t DerivOrder, ::domain::ObjectLabel Horizon>\nstruct Size : tt::ConformsTo<protocols::ControlError> {\n  using object_centers = domain::object_list<Horizon>;\n\n  struct MaxNumTimesForZeroCrossingPredictor {\n    // Int so we get proper bounds checking\n    using type = int;\n    static constexpr Options::String help{\n        \"The maximum number of times used to calculate the zero crossing of \"\n        \"the char speeds.\"};\n    static int lower_bound() { return 3; }\n  };\n\n  struct SmoothAvgTimescaleFraction {\n    using type = double;\n    static constexpr Options::String help{\n        \"Average timescale fraction for smoothing horizon measurements.\"};\n  };\n\n  struct SmootherTuner {\n    using type = TimescaleTuner<true>;\n    static constexpr Options::String help{\n        \"TimescaleTuner for smoothing horizon measurements.\"};\n  };\n\n  struct InitialState {\n    using type = std::unique_ptr<size::State>;\n    static constexpr Options::String help{\"Initial state to start in.\"};\n  };\n\n  struct DeltaRDriftOutwardOptions {\n    using type =\n        Options::Auto<DeltaRDriftOutwardOptions, Options::AutoLabel::None>;\n    static constexpr Options::String help{\n        \"Options for State DeltaRDriftOutward. Specify 'None' to disable State \"\n        \"DeltaRDriftOutward.\"};\n    struct MaxAllowedRadialDistance {\n      using type = double;\n      static constexpr Options::String help{\n          \"Drift excision boundary outward if distance from horizon to \"\n          \"excision exceeds this.\"};\n    };\n    struct OutwardDriftVelocity {\n      using type = double;\n      static constexpr Options::String help{\n          \"Constant drift velocity term, if triggered by \"\n          \"MaxAllowedRadialDistance.\"};\n    };\n    struct OutwardDriftTimescale {\n      using type = double;\n      static constexpr Options::String help{\n          \"Denominator in non-constant drift velocity term, if triggered by \"\n          \"MaxAllowedRadialDistance.\"};\n    };\n    using options = tmpl::list<MaxAllowedRadialDistance, OutwardDriftVelocity,\n                               OutwardDriftTimescale>;\n    DeltaRDriftOutwardOptions();\n    DeltaRDriftOutwardOptions(double max_allowed_radial_distance_in,\n                              double outward_drift_velocity_in,\n                              double outward_drift_timescale_in);\n    void pup(PUP::er& p);\n\n    double max_allowed_radial_distance{};\n    double outward_drift_velocity{};\n    double outward_drift_timescale{};\n  };\n\n  struct DeltaRDriftInwardOptions {\n    using type =\n        Options::Auto<DeltaRDriftInwardOptions, Options::AutoLabel::None>;\n    static constexpr Options::String help{\n        \"Options for State DeltaRDriftInward. Specify 'None' to disable State \"\n        \"DeltaRDriftInward.\"};\n    struct MinAllowedRadialDistance {\n      using type = double;\n      static constexpr Options::String help{\n          \"Drift excision boundary inward if distance from horizon to \"\n          \"excision is less than this.\"};\n    };\n    struct MinAllowedCharSpeed {\n      using type = double;\n      static constexpr Options::String help{\n          \"Drift excision boundary inward if min char speed is less than \"\n          \"this.\"};\n    };\n    struct InwardDriftVelocity {\n      using type = double;\n      static constexpr Options::String help{\n          \"Maximum value of drift velocity term, if State DeltaRDriftInward is \"\n          \"triggered by MinAllowedRadialDistance or MinAllowedCharSpeed.\"};\n    };\n    using options = tmpl::list<MinAllowedRadialDistance, MinAllowedCharSpeed,\n                               InwardDriftVelocity>;\n    DeltaRDriftInwardOptions();\n    DeltaRDriftInwardOptions(double min_allowed_radial_distance_in,\n                             double min_allowed_char_speed_in,\n                             double inward_drift_velocity_in);\n    void pup(PUP::er& p);\n\n    double min_allowed_radial_distance{};\n    double min_allowed_char_speed{};\n    double inward_drift_velocity{};\n  };\n\n  using options =\n      tmpl::list<MaxNumTimesForZeroCrossingPredictor,\n                 SmoothAvgTimescaleFraction, SmootherTuner, InitialState,\n                 DeltaRDriftOutwardOptions, DeltaRDriftInwardOptions>;\n  static constexpr Options::String help{\n      \"Computes the control error for size control. Will also write a \"\n      \"diagnostics file if the control systems are allowed to write data to \"\n      \"disk.\"};\n\n  Size() = default;\n  Size(const Size& rhs);\n  Size& operator=(const Size& rhs);\n  Size(Size&& /*rhs*/) = default;\n  Size& operator=(Size&& /*rhs*/) = default;\n  virtual ~Size() = default;\n\n  /*!\n   * \\brief Initializes the `intrp::ZeroCrossingPredictor`s and the horizon\n   * smoothing `Averager` and `TimescaleTuner`.\n   *\n   * \\details All `intrp::ZeroCrossingPredictor`s are initialized with a minimum\n   * number of times 3 and a maximum number of times `max_times`. The internal\n   * `control_system::size::Info::state` is initialized to\n   * `control_system::size::States::Initial`. The smoothing `Averager` uses the\n   * input average timescale fraction and always smooths the \"0th\" deriv (aka\n   * the horizon coefficients themselves). The input smoothing `TimescaleTuner`\n   * is moved inside this class.\n   */\n  Size(const int max_times, const double smooth_avg_timescale_frac,\n       TimescaleTuner<true> smoother_tuner,\n       std::unique_ptr<size::State> initial_state,\n       std::optional<DeltaRDriftOutwardOptions> delta_r_drift_outward_options,\n       std::optional<DeltaRDriftInwardOptions> delta_r_drift_inward_options);\n\n  /// Returns the internal `control_system::size::Info::suggested_time_scale`. A\n  /// std::nullopt means that no timescale is suggested.\n  std::optional<double> get_suggested_timescale() const;\n\n  /*!\n   * \\brief Check if the `control_system::size::control_error` has decided to\n   * switch states. Returns the internal\n   * `control_system::size::Info::discontinuous_change_has_occurred`.\n   */\n  bool discontinuous_change_has_occurred() const;\n\n  /*!\n   * \\brief Reset the internal `control_system::size::Info` using\n   * `control_system::size::Info::reset`.\n   */\n  void reset();\n\n  /*!\n   * \\brief Get a history of the control errors for the past few measurements.\n   *\n   * \\return std::deque<std::pair<double, double>> This returns up to\n   * `DerivOrder` entries, not including the most recent time. \\see\n   * `control_system::size::StateHistory::state_history`\n   */\n  std::deque<std::pair<double, double>> control_error_history() const;\n\n  void pup(PUP::er& p);\n\n  /*!\n   * \\brief Actually computes the control error.\n   *\n   * \\details The internal `control_system::size::Info::damping_time` is updated\n   * to the minimum of the `TimescaleTuner::current_timescale()` that is passed\n   * in. Also expects these queue tags to be in the `measurements` argument:\n   *\n   * - `ylm::Tags::Strahlkorper<Frame::Distorted>`\n   * - `QueueTags::ExcisionSurface<Frame::Distorted>`\n   * - `::Tags::dt<ylm::Tags::Strahlkorper<Frame::Distorted>>`\n   * - `QueueTags::LapseOnExcisionSurface`\n   * - `QueueTags::ShiftyQuantity<Frame::Distorted>`\n   * - `QueueTags::SpatialMetricOnExcisionSurface<Frame::Distorted>`\n   * - `QueueTags::InverseSpatialMetricOnExcisionSurface<Frame::Distorted>`\n   *\n   * \\return DataVector should be of size 1\n   */\n  template <typename Metavariables, typename... TupleTags>\n  DataVector operator()(const ::TimescaleTuner<false>& tuner,\n                        const Parallel::GlobalCache<Metavariables>& cache,\n                        const double time,\n                        const std::string& function_of_time_name,\n                        const tuples::TaggedTuple<TupleTags...>& measurements) {\n    const Domain<3>& domain = get<domain::Tags::Domain<3>>(cache);\n    const auto& excision_spheres = domain.excision_spheres();\n    const auto& excision_sphere =\n        excision_spheres.at(\"ExcisionSphere\" + get_output(Horizon));\n    const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n\n    const auto& excision_quantities =\n        tuples::get<QueueTags::SizeExcisionQuantities<Frame::Distorted>>(\n            measurements);\n    const auto& horizon_quantities =\n        tuples::get<QueueTags::SizeHorizonQuantities<Frame::Distorted>>(\n            measurements);\n\n    const double grid_frame_excision_sphere_radius = excision_sphere.radius();\n    const ylm::Strahlkorper<Frame::Distorted>& apparent_horizon =\n        tuples::get<ylm::Tags::Strahlkorper<Frame::Distorted>>(\n            horizon_quantities);\n    const ylm::Strahlkorper<Frame::Distorted>& excision_surface =\n        tuples::get<QueueTags::ExcisionSurface<Frame::Distorted>>(\n            excision_quantities);\n    const ylm::Strahlkorper<Frame::Distorted>& time_deriv_apparent_horizon =\n        tuples::get<::Tags::dt<ylm::Tags::Strahlkorper<Frame::Distorted>>>(\n            horizon_quantities);\n    const Scalar<DataVector>& lapse =\n        tuples::get<QueueTags::LapseOnExcisionSurface>(excision_quantities);\n    const tnsr::I<DataVector, 3, Frame::Distorted>& shifty_quantity =\n        tuples::get<QueueTags::ShiftyQuantity<Frame::Distorted>>(\n            excision_quantities);\n    const tnsr::ii<DataVector, 3, Frame::Distorted>&\n        spatial_metric_on_excision = tuples::get<\n            QueueTags::SpatialMetricOnExcisionSurface<Frame::Distorted>>(\n            excision_quantities);\n    const tnsr::II<DataVector, 3, Frame::Distorted>&\n        inverse_spatial_metric_on_excision = tuples::get<\n            QueueTags::InverseSpatialMetricOnExcisionSurface<Frame::Distorted>>(\n            excision_quantities);\n    const tnsr::Ijj<DataVector, 3, Frame::Distorted>& spatial_christoffel =\n        tuples::get<QueueTags::SpatialChristoffelSecondKind<Frame::Distorted>>(\n            excision_quantities);\n    const tnsr::i<DataVector, 3, Frame::Distorted>& deriv_lapse =\n        tuples::get<QueueTags::DerivLapse<Frame::Distorted>>(\n            excision_quantities);\n    const tnsr::iJ<DataVector, 3, Frame::Distorted>& deriv_shift =\n        tuples::get<QueueTags::DerivShift<Frame::Distorted>>(\n            excision_quantities);\n    const ::InverseJacobian<DataVector, 3, Frame::Grid,\n                            Frame::Distorted>& inv_jac_grid_to_distorted =\n        tuples::get<QueueTags::InverseJacobian<Frame::Grid, Frame::Distorted>>(\n            excision_quantities);\n\n    db::mutate<gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Distorted>,\n               ylm::Tags::Strahlkorper<Frame::Distorted>>(\n        [&inverse_spatial_metric_on_excision, &excision_surface](\n            const gsl::not_null<tnsr::II<DataVector, 3, Frame::Distorted>*>\n                inv_spatial_metric,\n            const gsl::not_null<ylm::Strahlkorper<Frame::Distorted>*>\n                strahlkorper) {\n          for (size_t i = 0; i < inv_spatial_metric->size(); i++) {\n            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n            (*inv_spatial_metric)[i].set_data_ref(const_cast<DataVector*>(\n                &inverse_spatial_metric_on_excision[i]));\n          }\n          *strahlkorper = excision_surface;\n        },\n        make_not_null(&char_speed_box_));\n\n    const double Y00 = 0.25 * M_2_SQRTPI;\n\n    horizon_coef_averager_.update(time, {apparent_horizon.coefficients()[0]},\n                                  smoother_tuner_.current_timescale());\n\n    const std::optional<std::array<DataVector, DerivOrder + 1>>&\n        averaged_horizon_coef_at_average_time = horizon_coef_averager_(time);\n\n    // lambda_00 is the quantity of the same name in ArXiv:1211.6079,\n    // and dt_lambda_00 is its time derivative.\n    // This is the map parameter that maps the excision boundary in the grid\n    // frame to the excision boundary in the distorted frame.\n    const auto map_lambda_and_deriv =\n        functions_of_time.at(function_of_time_name)->func_and_deriv(time);\n    const double lambda_00 = map_lambda_and_deriv[0][0];\n    const double dt_lambda_00 = map_lambda_and_deriv[1][0];\n\n    // horizon_00 is \\hat{S}_00 in ArXiv:1211.6079,\n    // and dt_horizon_00 is its time derivative.\n    // These are coefficients of the horizon in the distorted frame. However, we\n    // want them averaged\n    double horizon_00 = apparent_horizon.coefficients()[0];\n    double dt_horizon_00 = time_deriv_apparent_horizon.coefficients()[0];\n\n    // Only for the first few measurements will this not have a value\n    if (LIKELY(averaged_horizon_coef_at_average_time.has_value())) {\n      // We need to get the averaged time and evaluate the averaged coefs at\n      // that time, not the time passed in\n      const double averaged_time = horizon_coef_averager_.average_time(time);\n\n      horizon_00 = averaged_horizon_coef_at_average_time.value()[0][0];\n      dt_horizon_00 = averaged_horizon_coef_at_average_time.value()[1][0];\n      const double d2t_horizon_00 =\n          averaged_horizon_coef_at_average_time.value()[2][0];\n\n      // Must account for time offset of averaged time. Do a simple Taylor\n      // expansion\n      const double time_diff = time - averaged_time;\n      horizon_00 += time_diff * dt_horizon_00;\n      dt_horizon_00 += 0.5 * square(time_diff) * d2t_horizon_00;\n\n      // The \"control error\" for the averaged horizon coefficients is just the\n      // averaged coefs minus the actual coef and time derivative from\n      // apparent_horizon and time_deriv_apparent_horizon\n      smoother_tuner_.update_timescale(\n          std::array{\n              DataVector{averaged_horizon_coef_at_average_time.value()[0][0]},\n              DataVector{averaged_horizon_coef_at_average_time.value()[1][0]}} -\n          std::array{\n              DataVector{apparent_horizon.coefficients()[0]},\n              DataVector{time_deriv_apparent_horizon.coefficients()[0]}});\n    }\n\n    // This is needed because the horizon_00 (and dt) are spherepack coefs, not\n    // spherical harmonic coefs.\n    const double spherepack_factor = sqrt(0.5 * M_PI);\n\n    // This is needed for every state\n    const double control_error_delta_r = size::control_error_delta_r(\n        horizon_00, dt_horizon_00, lambda_00, dt_lambda_00,\n        grid_frame_excision_sphere_radius);\n    const std::optional<double> control_error_delta_r_outward =\n        delta_r_drift_outward_options_.has_value()\n            ? std::optional<double>(control_error_delta_r -\n                                    delta_r_drift_outward_options_.value()\n                                        .outward_drift_velocity -\n                                    (lambda_00 +\n                                     spherepack_factor * horizon_00 -\n                                     grid_frame_excision_sphere_radius / Y00) /\n                                        delta_r_drift_outward_options_.value()\n                                            .outward_drift_timescale)\n            : std::nullopt;\n    const std::optional<double> inward_drift_velocity =\n        delta_r_drift_inward_options_.has_value()\n            ? std::optional<double>(\n                  delta_r_drift_inward_options_.value().inward_drift_velocity)\n            : std::nullopt;\n    const std::optional<double> min_allowed_radial_distance =\n        delta_r_drift_inward_options_.has_value()\n            ? std::optional<double>(delta_r_drift_inward_options_.value()\n                                        .min_allowed_radial_distance)\n            : std::nullopt;\n    const std::optional<double> min_allowed_char_speed =\n        delta_r_drift_inward_options_.has_value()\n            ? std::optional<double>(\n                  delta_r_drift_inward_options_.value().min_allowed_char_speed)\n            : std::nullopt;\n\n    // Currently we don't do anything with the derivative of the comoving char\n    // speed. Eventually, we will pass it to the computation of the control\n    // error below\n    Scalar<DataVector> deriv_comoving_char_speed{};\n    {\n      const tnsr::i<DataVector, 3, Frame::Distorted>& excision_normal_one_form =\n          db::get<ylm::Tags::NormalOneForm<Frame::Distorted>>(char_speed_box_);\n      const DataVector& one_over_excision_normal_one_form_norm_dv =\n          db::get<ylm::Tags::OneOverOneFormMagnitude>(char_speed_box_);\n      Scalar<DataVector> one_over_excision_normal_one_form_norm{};\n      get(one_over_excision_normal_one_form_norm)\n          // NOLINTNEXTLINE\n          .set_data_ref(const_cast<DataVector*>(\n              &one_over_excision_normal_one_form_norm_dv));\n\n      const tnsr::i<DataVector, 3, Frame::Distorted>& rhat =\n          db::get<ylm::Tags::Rhat<Frame::Distorted>>(char_speed_box_);\n\n      size::comoving_char_speed_derivative(\n          make_not_null(&deriv_comoving_char_speed), lambda_00, dt_lambda_00,\n          horizon_00, dt_horizon_00, grid_frame_excision_sphere_radius, rhat,\n          excision_normal_one_form, one_over_excision_normal_one_form_norm,\n          shifty_quantity, inverse_spatial_metric_on_excision,\n          spatial_christoffel, deriv_lapse, deriv_shift,\n          inv_jac_grid_to_distorted);\n    }\n\n    info_.damping_time = min(tuner.current_timescale());\n\n    const size::ErrorDiagnostics error_diagnostics = size::control_error(\n        make_not_null(&info_), make_not_null(&char_speed_predictor_),\n        make_not_null(&comoving_char_speed_predictor_),\n        make_not_null(&delta_radius_predictor_),\n        make_not_null(&drift_limit_char_speed_predictor_),\n        make_not_null(&drift_limit_delta_radius_predictor_), time,\n        control_error_delta_r, control_error_delta_r_outward,\n        delta_r_drift_outward_options_.has_value()\n            ? std::optional<double>(delta_r_drift_outward_options_.value()\n                                        .max_allowed_radial_distance)\n            : std::nullopt,\n        inward_drift_velocity, min_allowed_radial_distance,\n        min_allowed_char_speed, horizon_00, dt_lambda_00, apparent_horizon,\n        excision_surface, lapse, shifty_quantity, spatial_metric_on_excision,\n        inverse_spatial_metric_on_excision, deriv_comoving_char_speed);\n\n    state_history_.store(time, info_, error_diagnostics.control_error_args);\n\n    if (Parallel::get<control_system::Tags::WriteDataToDisk>(cache)) {\n      auto& observer_writer_proxy = Parallel::get_parallel_component<\n          observers::ObserverWriter<Metavariables>>(cache);\n\n      // \\Delta R = < R_ah > - < R_ex >\n      // < R_ah > = S_00 * Y_00\n      // < R_ex > = R_ex^grid - \\lambda_00 * Y_00\n      // < \\Delta R > = \\Delta R / < R_ah >\n      const double avg_delta_r =\n          (spherepack_factor * horizon_00 + lambda_00) * Y00 -\n          grid_frame_excision_sphere_radius;\n      const double avg_relative_delta_r =\n          avg_delta_r / (spherepack_factor * horizon_00 * Y00);\n\n      Parallel::threaded_action<\n          observers::ThreadedActions::WriteReductionDataRow>(\n          observer_writer_proxy[0], subfile_name_, legend_,\n          std::make_tuple(\n              time, error_diagnostics.control_error,\n              static_cast<double>(error_diagnostics.state_number),\n              error_diagnostics.discontinuous_change_has_occurred ? 1.0 : 0.0,\n              lambda_00, dt_lambda_00, horizon_00, dt_horizon_00,\n              time_deriv_apparent_horizon.coefficients()[0],\n              smoother_tuner_.current_timescale()[0],\n              error_diagnostics.min_delta_r,\n              error_diagnostics.min_relative_delta_r, avg_delta_r,\n              avg_relative_delta_r,\n              error_diagnostics.control_error_args.control_error_delta_r,\n              error_diagnostics.target_char_speed,\n              error_diagnostics.control_error_args.min_char_speed,\n              error_diagnostics.min_comoving_char_speed,\n              error_diagnostics.char_speed_crossing_time,\n              error_diagnostics.comoving_char_speed_crossing_time,\n              error_diagnostics.delta_r_crossing_time,\n              error_diagnostics.suggested_timescale,\n              error_diagnostics.damping_timescale));\n    }\n\n    if (Parallel::get<control_system::Tags::Verbosity>(cache) >=\n        ::Verbosity::Verbose) {\n      Parallel::printf(\"%s: %s\\n\", function_of_time_name,\n                       error_diagnostics.update_message);\n    }\n\n    return DataVector{1, error_diagnostics.control_error};\n  }\n\n private:\n  TimescaleTuner<true> smoother_tuner_{};\n  Averager<DerivOrder> horizon_coef_averager_{};\n  size::Info info_{};\n  intrp::ZeroCrossingPredictor char_speed_predictor_{};\n  intrp::ZeroCrossingPredictor comoving_char_speed_predictor_{};\n  intrp::ZeroCrossingPredictor delta_radius_predictor_{};\n  intrp::ZeroCrossingPredictor drift_limit_char_speed_predictor_{};\n  intrp::ZeroCrossingPredictor drift_limit_delta_radius_predictor_{};\n  size::StateHistory state_history_{};\n  std::vector<std::string> legend_{};\n  std::string subfile_name_{};\n  std::optional<DeltaRDriftOutwardOptions> delta_r_drift_outward_options_{};\n  std::optional<DeltaRDriftInwardOptions> delta_r_drift_inward_options_{};\n  db::compute_databox_type<tmpl::list<\n      gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Distorted>,\n      ylm::Tags::Strahlkorper<Frame::Distorted>,\n      ylm::Tags::ThetaPhiCompute<Frame::Distorted>,\n      ylm::Tags::InvJacobianCompute<Frame::Distorted>,\n      ylm::Tags::RadiusCompute<Frame::Distorted>,\n      ylm::Tags::RhatCompute<Frame::Distorted>,\n      ylm::Tags::DxRadiusCompute<Frame::Distorted>,\n      ylm::Tags::NormalOneFormCompute<Frame::Distorted>,\n      ylm::Tags::OneOverOneFormMagnitudeCompute<DataVector, 3,\n                                                Frame::Distorted>>>\n      char_speed_box_{};\n};\n}  // namespace ControlErrors\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Skew.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ControlErrors/Skew.hpp\"\n\n#include <optional>\n#include <pup.h>\n\nnamespace control_system::ControlErrors {\nSkew::Skew(const Skew& rhs) : suggested_timescale_(rhs.suggested_timescale_) {}\n\nSkew& Skew::operator=(const Skew& rhs) {\n  suggested_timescale_ = rhs.get_suggested_timescale();\n  return *this;\n}\n\nstd::optional<double> Skew::get_suggested_timescale() const {\n  return suggested_timescale_;\n}\n\nvoid Skew::reset() { suggested_timescale_ = std::nullopt; }\n\nvoid Skew::pup(PUP::er& p) {\n  // No need to pup the DataBoxes or the inclination angles. Their data is\n  // always temporary\n  p | suggested_timescale_;\n}\n}  // namespace control_system::ControlErrors\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Skew.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <utility>\n\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::ControlErrors {\n/*!\n * \\brief Control error in the for the\n * `domain::CoordinateMaps::TimeDependent::Skew` map.\n *\n * \\details Computes the error in the map parameters $F_y(t)$ and $F_z(t)$ of\n * the \\link domain::CoordinateMaps::TimeDependent::Skew Skew \\endlink using\n * a modified version of Eq. (71) from \\cite Hemberger2012jz. This modified\n * control error is\n *\n * \\begin{equation}\n * Q_{F_j} = g\\left(\\frac{w_A \\Theta_A^j + w_B \\Theta_B^j}{w_A + w_B}\\right)\n *     - (1-g)F_j\n * \\end{equation}\n *\n * where the falloff function is assumed to be $W(\\vec{x}) \\approx 1$ from the\n * \\link domain::CoordinateMaps::TimeDependent::Skew Skew \\endlink map,\n * $\\Theta_H^j$ are the inclination angles between the $x$-axis and the normal\n * to the horizon at the intersection point $x_H^\\textrm{Int}$ between the\n * $x$-axis and the horizon, $w_A$ and $w_B$ are averaging weights defined by\n *\n * \\begin{equation}\n * w_H = \\exp{\\left(-\\frac{x^0_C - x_H^\\textrm{Int}}{x^0_C - C^0_H}\\right)}\n * \\end{equation}\n *\n * with $C^0_H$ being the centers of the excision boundaries, and finally with\n *\n * \\begin{equation}\n * g = \\frac{1}{2}\\left(1-\\tanh\\left(10\\frac{x_A^\\textrm{Int} -\n * x_B^\\textrm{Int}}{C^0_A - C^0_B} - 5\\right)\\right).\n * \\end{equation}\n *\n * This transition function $g$ is meant to only activate skew control when the\n * black holes are close to merger to avoid adverse effects with junk radiation.\n * If $g < 0.0025$, then we turn skew control off completely and the control\n * error just becomes\n *\n * \\begin{equation}\n * Q_{F_j} = -F_j.\n * \\end{equation}\n *\n * This threshold value of $g = 0.0025$ was chosen in SpEC and seems to work\n * well.\n *\n * Requirements:\n * - This control error requires that there be exactly two objects in the\n *   simulation\n * - Currently both these objects must be black holes\n * - Currently this control system can only be used with the \\link\n *   control_system::Systems::Skew Skew \\endlink control system\n */\nstruct Skew : tt::ConformsTo<protocols::ControlError> {\n  using object_centers = domain::object_list<>;\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Computes the control error for skew control.\"};\n\n  // Explicitly defined copy constructor because DataBox isn't copyable\n  Skew() = default;\n  Skew(Skew&& rhs) = default;\n  Skew& operator=(Skew&& rhs) = default;\n  Skew(const Skew&);\n  Skew& operator=(const Skew&);\n  ~Skew() = default;\n\n  /*!\n   * \\brief Returns the internal suggested timescale. A std::nullopt means that\n   * no timescale is suggested.\n   */\n  std::optional<double> get_suggested_timescale() const;\n\n  /*!\n   * \\brief Resets the internal suggested timescale to nullopt.\n   */\n  void reset();\n\n  void pup(PUP::er& p);\n\n  template <typename Metavariables, typename... TupleTags>\n  DataVector operator()(const ::TimescaleTuner<true>& /*tuner*/,\n                        const Parallel::GlobalCache<Metavariables>& cache,\n                        const double time,\n                        const std::string& function_of_time_name,\n                        const tuples::TaggedTuple<TupleTags...>& measurements) {\n    const ylm::Strahlkorper<Frame::Distorted>& horizon_a = tuples::get<\n        QueueTags::Horizon<Frame::Distorted, ::domain::ObjectLabel::A>>(\n        measurements);\n    const ylm::Strahlkorper<Frame::Distorted>& horizon_b = tuples::get<\n        QueueTags::Horizon<Frame::Distorted, ::domain::ObjectLabel::B>>(\n        measurements);\n\n    // Copy the horizon into the box so the compute tags use the correct\n    // strahlkorper\n    const auto set_horizon =\n        [](const gsl::not_null<ylm::Strahlkorper<Frame::Distorted>*>\n               horizon_ptr,\n           const ylm::Strahlkorper<Frame::Distorted>& horizon) {\n          *horizon_ptr = horizon;\n        };\n\n    db::mutate<ylm::Tags::Strahlkorper<Frame::Distorted>>(\n        set_horizon, make_not_null(&box_a_), horizon_a);\n    db::mutate<ylm::Tags::Strahlkorper<Frame::Distorted>>(\n        set_horizon, make_not_null(&box_b_), horizon_b);\n\n    const auto& normal_one_form_a =\n        db::get<ylm::Tags::NormalOneForm<Frame::Distorted>>(box_a_);\n    const auto& cartesian_coords_a =\n        db::get<ylm::Tags::CartesianCoords<Frame::Distorted>>(box_a_);\n    const auto& center_a =\n        Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::A>>(\n            cache);\n\n    const auto& normal_one_form_b =\n        db::get<ylm::Tags::NormalOneForm<Frame::Distorted>>(box_b_);\n    const auto& cartesian_coords_b =\n        db::get<ylm::Tags::CartesianCoords<Frame::Distorted>>(box_b_);\n    const auto& center_b =\n        Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::B>>(\n            cache);\n\n    // The translation of the cutting plane in the grid frame is just the\n    // average of the two centers.\n    const double cut_x = 0.5 * (center_a[0] + center_b[0]);\n\n    // For AhA since it always has x_center > cut_x, we want the point which is\n    // closest to the cutting plane, which in terms of theta, phi = pi/2, pi.\n    // For AhB since it always has x_center < cut_x, we want the point which is\n    // closest to the cutting plane, which in terms of theta, phi = pi/2, 0.\n    // To get the coord and normal one form at these theta/phi, we do an\n    // interpolation for both Ah's\n    const auto& ylm_a = horizon_a.ylm_spherepack();\n    const ylm::Spherepack::InterpolationInfo<double> interpolation_info_a =\n        ylm_a.set_up_interpolation_info(std::array{M_PI_2, M_PI});\n    const auto& ylm_b = horizon_b.ylm_spherepack();\n    const ylm::Spherepack::InterpolationInfo<double> interpolation_info_b =\n        ylm_b.set_up_interpolation_info(std::array{M_PI_2, 0.0});\n\n    const auto set_intersection =\n        [](const gsl::not_null<DataVector*> inclination_angle,\n           const gsl::not_null<std::array<double, 3>*> intersection_coord,\n           const ylm::Spherepack& ylm,\n           const ylm::Spherepack::InterpolationInfo<double>& interpolation_info,\n           const auto& normal_one_form, const auto& cartesian_coords) {\n          std::array<double, 3> intersection_normal_one_form{};\n          for (size_t i = 0; i < 3; i++) {\n            ylm.interpolate(make_not_null(&gsl::at(*intersection_coord, i)),\n                            make_not_null(cartesian_coords.get(i).data()),\n                            interpolation_info);\n            ylm.interpolate(\n                make_not_null(&gsl::at(intersection_normal_one_form, i)),\n                make_not_null(normal_one_form.get(i).data()),\n                interpolation_info);\n          }\n\n          for (size_t i = 0; i < 2; ++i) {\n            (*inclination_angle)[i] =\n                atan2(gsl::at(intersection_normal_one_form, i + 1),\n                      (intersection_normal_one_form)[0]);\n            if ((*inclination_angle)[i] < -0.5 * M_PI) {\n              (*inclination_angle)[i] += M_PI;\n            } else if ((*inclination_angle)[i] > +0.5 * M_PI) {\n              (*inclination_angle)[i] -= M_PI;\n            }\n          }\n        };\n\n    // These are the values we need at the theta/phi described above\n    std::array<double, 3> intersection_coord_a{};\n    std::array<double, 3> intersection_coord_b{};\n\n    set_intersection(make_not_null(&inclination_angle_a_),\n                     make_not_null(&intersection_coord_a), ylm_a,\n                     interpolation_info_a, normal_one_form_a,\n                     cartesian_coords_a);\n    set_intersection(make_not_null(&inclination_angle_b_),\n                     make_not_null(&intersection_coord_b), ylm_b,\n                     interpolation_info_b, normal_one_form_b,\n                     cartesian_coords_b);\n\n    const double relative_delta_x =\n        (intersection_coord_a[0] - intersection_coord_b[0]) /\n        (center_a[0] - center_b[0]);\n    // Hardcoded function used in SpEC\n    const double temporal_transition_function =\n        0.5 * (1.0 - tanh(10.0 * relative_delta_x - 5.0));\n\n    // Hardcoded value used in SpEC\n    constexpr double activation_threshold = 0.0025;\n\n    const auto& function_of_time =\n        Parallel::get<domain::Tags::FunctionsOfTime>(cache).at(\n            function_of_time_name);\n\n    // Only activate if BHs are close enough. Otherwise control to zero\n    DataVector func = std::move(function_of_time->func(time)[0]);\n    DataVector& control_error = func;\n    if (temporal_transition_function > activation_threshold) {\n      suggested_timescale_ =\n          std::max(std::min(abs(center_a[0]), abs(center_b[0])),\n                   std::min(abs(cut_x - intersection_coord_a[0]),\n                            abs(cut_x - intersection_coord_b[0])));\n\n      const double weight_a =\n          exp(-(cut_x - intersection_coord_a[0]) / (cut_x - center_a[0]));\n      const double weight_b =\n          exp(-(cut_x - intersection_coord_b[0]) / (cut_x - center_b[0]));\n\n      control_error = temporal_transition_function *\n                          (weight_a * inclination_angle_a_ +\n                           weight_b * inclination_angle_b_) /\n                          (weight_a + weight_b) -\n                      func;\n    } else {\n      control_error *= -1.0;\n    }\n\n    return std::move(control_error);\n  }\n\n private:\n  std::optional<double> suggested_timescale_;\n  // not pupped. These are allocated here to avoid memory allocations during\n  // the operator() call. All their data is temporary\n  DataVector inclination_angle_a_{2, 0.0};\n  DataVector inclination_angle_b_{2, 0.0};\n  db::compute_databox_type<\n      tmpl::append<ylm::Tags::items_tags<Frame::Distorted>,\n                   ylm::Tags::compute_items_tags<Frame::Distorted>>>\n      box_a_;\n  db::compute_databox_type<\n      tmpl::append<ylm::Tags::items_tags<Frame::Distorted>,\n                   ylm::Tags::compute_items_tags<Frame::Distorted>>>\n      box_b_;\n};\n}  // namespace control_system::ControlErrors\n"
  },
  {
    "path": "src/ControlSystem/ControlErrors/Translation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/math/quaternion.hpp>\n#include <cstddef>\n#include <pup.h>\n\n#include \"ControlSystem/ControlErrors/Expansion.hpp\"\n#include \"ControlSystem/ControlErrors/Rotation.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionHelpers.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstruct Domain;\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace control_system {\nnamespace ControlErrors {\n/*!\n * \\brief Control error in the 3D \\link\n * domain::CoordinateMaps::TimeDependent::Translation Translation \\endlink\n * coordinate map\n *\n * \\details Computes the error in how much the system has translated. When there\n * are two excisions, it does this by using a modified version of Eq. (42) from\n * \\cite Ossokine2013zga. The equation is\n *\n * \\begin{equation}\n * \\left(0, \\delta\\vec{T}\\right) = a\\mathbf{q}\\left(\\frac{1}{2}(\\mathbf{x}_A\n * + \\mathbf{x}_B - \\frac{1}{2}(\\mathbf{c}_A + \\mathbf{c}_B)) - \\mathbf{\\delta\n * q}\\wedge\\frac{1}{2}(\\mathbf{c}_A + \\mathbf{c}_B) - \\frac{\\delta\n * a}{a}\\frac{1}{2}(\\mathbf{c}_A + \\mathbf{c}_B) \\right)\\mathbf{q}^*\n * \\end{equation}\n *\n * where object A is located on the positive x-axis in the grid frame, bold face\n * letters are quaternions, vectors are promoted to quaternions as \\f$\n * \\mathbf{v} = (0, \\vec{v}) \\f$, \\f$ \\mathbf{q} \\f$ is the quaternion from the\n * \\link domain::CoordinateMaps::TimeDependent::Rotation Rotation \\endlink map,\n * \\f$ a \\f$ is the function \\f$ a(t) \\f$ from the \\link\n * domain::CoordinateMaps::TimeDependent::CubicScale CubicScale \\endlink map,\n * \\f$ \\mathbf{\\delta q}\\wedge\\mathbf{c}_A \\equiv (0, \\delta\\vec{q} \\times\n * \\vec{c}_A) \\f$, \\f$ \\delta\\vec{q} \\f$ is the \\link\n * control_system::ControlErrors::Rotation Rotation \\endlink control error, and\n * \\f$ \\delta a\\f$ is the \\link control_system::ControlErrors::Expansion\n * Expansion \\endlink control error.\n *\n * When there is only a single excision, the control error assumes that the\n * center of the excision is at the origin. Thus, the control error is just\n * taken to be the current center of the horizon mapped through the expansion\n * and rotation maps if there are any.\n *\n * Requirements:\n * - This control error requires that there be either one or two objects in the\n *   simulation\n * - Currently this control error can only be used with the \\link\n *   control_system::Systems::Translation Translation \\endlink control system\n * - There must exist an expansion map and a quaternion rotation map in the\n *   coordinate map with names \"Expansion\" and \"Rotation\", respectively if there\n *   are two object. If there is a single object, then the \"Expansion\" and\n *   \"Rotation\" maps may exist, but don't need to.\n */\ntemplate <size_t NumberOfObjects>\nstruct Translation : tt::ConformsTo<protocols::ControlError> {\n  static_assert(NumberOfObjects == 1 or NumberOfObjects == 2,\n                \"Translation control can only work with 1 or 2 objects.\");\n\n  using object_centers = tmpl::conditional_t<\n      NumberOfObjects == 1, domain::object_list<domain::ObjectLabel::None>,\n      domain::object_list<domain::ObjectLabel::A, domain::ObjectLabel::B>>;\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Computes the control error for translation control. This should not \"\n      \"take any options.\"};\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  std::optional<double> get_suggested_timescale() const { return std::nullopt; }\n\n  void reset() {}\n\n  void pup(PUP::er& /*p*/) {}\n\n  template <typename Metavariables, typename... TupleTags>\n  DataVector operator()(const ::TimescaleTuner<true>& /*unused*/,\n                        const Parallel::GlobalCache<Metavariables>& cache,\n                        const double time,\n                        const std::string& /*function_of_time_name*/,\n                        const tuples::TaggedTuple<TupleTags...>& measurements) {\n    const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n\n    if constexpr (NumberOfObjects == 2) {\n      using quat = boost::math::quaternion<double>;\n\n      const quat quaternion = datavector_to_quaternion(\n          functions_of_time.at(\"Rotation\")->func(time)[0]);\n      const double expansion_factor =\n          functions_of_time.at(\"Expansion\")->func(time)[0][0];\n\n      using center_A =\n          control_system::QueueTags::Center<::domain::ObjectLabel::A,\n                                            Frame::Grid>;\n      using center_B =\n          control_system::QueueTags::Center<::domain::ObjectLabel::B,\n                                            Frame::Grid>;\n\n      const tnsr::I<double, 3, Frame::Grid>& grid_position_of_A_tnsr =\n          Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::A>>(\n              cache);\n      const DataVector grid_position_of_A{{grid_position_of_A_tnsr[0],\n                                           grid_position_of_A_tnsr[1],\n                                           grid_position_of_A_tnsr[2]}};\n      const DataVector& current_position_of_A = get<center_A>(measurements);\n\n      const tnsr::I<double, 3, Frame::Grid>& grid_position_of_B_tnsr =\n          Parallel::get<domain::Tags::ObjectCenter<domain::ObjectLabel::B>>(\n              cache);\n      const DataVector grid_position_of_B{{grid_position_of_B_tnsr[0],\n                                           grid_position_of_B_tnsr[1],\n                                           grid_position_of_B_tnsr[2]}};\n      const DataVector& current_position_of_B = get<center_B>(measurements);\n\n      const DataVector grid_position_average =\n          0.5 * (grid_position_of_A + grid_position_of_B);\n      const DataVector current_position_average =\n          0.5 * (current_position_of_A + current_position_of_B);\n\n      const DataVector grid_separation =\n          grid_position_of_A - grid_position_of_B;\n      const DataVector current_separation =\n          current_position_of_A - current_position_of_B;\n\n      // These quantities come from the translation control implementation in\n      // SpEC\n      const double current_separation_dot_grid_separation =\n          dot(current_separation, grid_separation);\n      const double current_separation_dot_grid_average =\n          dot(current_separation, grid_position_average);\n      const double grid_separation_dot_grid_average =\n          dot(grid_separation, grid_position_average);\n      const double grid_separation_dot_grid_separation =\n          dot(grid_separation, grid_separation);\n\n      // From eq. 42 in 1304.3067 where the grid and current position are\n      // swapped from only object A to the average grid and current positions of\n      // both objects.\n      const DataVector translation_control =\n          expansion_factor *\n          (grid_separation_dot_grid_separation * current_position_average -\n           current_separation_dot_grid_separation * grid_position_average -\n           grid_separation_dot_grid_average * current_separation +\n           current_separation_dot_grid_average * grid_separation) /\n          grid_separation_dot_grid_separation;\n      const quat middle_expression =\n          datavector_to_quaternion(translation_control);\n\n      // Because we are converting from a quaternion to a DataVector, there will\n      // be four components in the DataVector. However, translation control only\n      // requires three (the latter three to be exact, because the first\n      // component should be 0. We ASSERT this also.)\n      const DataVector result_with_four_components = quaternion_to_datavector(\n          quaternion * middle_expression * conj(quaternion));\n      ASSERT(equal_within_roundoff(result_with_four_components[0], 0.0),\n             \"Error in computing translation control error. First component of \"\n             \"resulting quaternion should be 0.0, but is \" +\n                 get_output(result_with_four_components[0]) + \" instead.\");\n\n      return {result_with_four_components[1], result_with_four_components[2],\n              result_with_four_components[3]};\n    } else {\n      const DataVector& current_position =\n          get<control_system::QueueTags::Center<::domain::ObjectLabel::None>>(\n              measurements);\n      DataVector result{3, 0.0};\n\n      double expansion_factor = 1.0;\n      if (functions_of_time.count(\"Expansion\") == 1) {\n        expansion_factor = functions_of_time.at(\"Expansion\")->func(time)[0][0];\n      }\n\n      if (functions_of_time.count(\"Rotation\") == 1) {\n        const Matrix rot_matrix =\n            rotation_matrix<3>(time, *functions_of_time.at(\"Rotation\").get());\n\n        for (size_t i = 0; i < 3; i++) {\n          for (size_t j = 0; j < 3; j++) {\n            result[i] += rot_matrix(i, j) * current_position[j];\n          }\n        }\n      } else {\n        result = current_position;\n      }\n\n      result *= expansion_factor;\n\n      return result;\n    }\n  }\n};\n}  // namespace ControlErrors\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Controller.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/Controller.hpp\"\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t DerivOrder>\nDataVector Controller<DerivOrder>::operator()(\n    const double time, const DataVector& timescales,\n    const std::array<DataVector, DerivOrder>& q_and_derivs,\n    const double q_time_offset, const double deriv_time_offset) {\n  last_update_time_ = time;\n  assign_time_between_updates(min(timescales));\n\n  // The coefficients a0_{k} are chosen such that the system is critically\n  // damped, so they are given by: a0_{k} = (N choose k) timescales^{k-N}\n  std::array<DataVector, DerivOrder> coefs0;\n  DataVector inv_timescales = 1.0 / timescales;\n  DataVector tau = inv_timescales;\n  for (size_t i = DerivOrder; i-- > 0;) {\n    gsl::at(coefs0, i) = binomial(DerivOrder, i) * tau;\n    // add another power of timescales\n    tau *= inv_timescales;\n  }\n\n  // correct for time offset\n  std::array<DataVector, DerivOrder> coefs;\n  for (size_t i = 0; i < DerivOrder; i++) {\n    gsl::at(coefs, i) = gsl::at(coefs0, i);\n    double q_dt = q_time_offset;\n    double deriv_dt = deriv_time_offset;\n    double fact_denom = 1.0;\n    for (size_t j = i; j-- > 0;) {\n      // update the factorial coefficient of this term in the series expansion\n      fact_denom *= static_cast<double>(i - j);\n      const double t_offset = (j == 0 ? q_dt : deriv_dt);\n      gsl::at(coefs, i) += gsl::at(coefs0, j) * t_offset / fact_denom;\n      // update the time coefficients for the next term in the series expansion\n      q_dt *= q_time_offset;\n      deriv_dt *= deriv_time_offset;\n    }\n  }\n\n  // compute denominator associated with correcting for time offset\n  // reuse tau DataVector allocation and properly initialize\n  DataVector& denom = tau;\n  denom = 1.0;\n  double q_dt = q_time_offset;\n  double deriv_dt = deriv_time_offset;\n  double fact_denom = 1.0;\n  for (size_t i = DerivOrder; i-- > 0;) {\n    // update the factorial coefficient of this term in the series expansion\n    fact_denom *= static_cast<double>(DerivOrder - i);\n    const double t_offset = (i == 0 ? q_dt : deriv_dt);\n    denom += gsl::at(coefs0, i) * t_offset / fact_denom;\n    // update the time coefficients for the next term in the series expansion\n    q_dt *= q_time_offset;\n    deriv_dt *= deriv_time_offset;\n  }\n\n  // compute control signal\n  // reuse inv_timescales DataVector allocation and properly initialize\n  DataVector& control_signal = inv_timescales;\n  control_signal = coefs[0] * q_and_derivs[0];\n  for (size_t i = 1; i < DerivOrder; i++) {\n    control_signal += gsl::at(coefs, i) * gsl::at(q_and_derivs, i);\n  }\n\n  return control_signal / denom;\n}\n\ntemplate <size_t DerivOrder>\nbool operator==(const Controller<DerivOrder>& lhs,\n                const Controller<DerivOrder>& rhs) {\n  return lhs.update_fraction_ == rhs.update_fraction_ and\n         lhs.time_between_updates_ == rhs.time_between_updates_ and\n         lhs.last_update_time_ == rhs.last_update_time_;\n}\n\ntemplate <size_t DerivOrder>\nbool operator!=(const Controller<DerivOrder>& lhs,\n                const Controller<DerivOrder>& rhs) {\n  return not(lhs == rhs);\n}\n\n// explicit instantiations for deriv order (2, 3)\n// Currently we only support control systems with DerivOrder=2 or 3. If we want\n// a higher order control system, we'll need to add the instantiation later.\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                              \\\n  template class Controller<DIM(data)>;                   \\\n  template bool operator==(const Controller<DIM(data)>&,  \\\n                           const Controller<DIM(data)>&); \\\n  template bool operator!=(const Controller<DIM(data)>&,  \\\n                           const Controller<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/ControlSystem/Controller.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Options/String.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup ControlSystemGroup\n/// A PND (proportional to Q and N derivatives of Q) controller that computes\n/// the control signal:\n/// \\f[ U(t) = \\sum_{k=0}^{N} a_{k} \\frac{d^kQ}{dt^k} \\f]\n/// where N is specified by the template parameter `DerivOrder`.\n///\n/// If an averager is used for `q_and_derivs` (as we typically do), there is an\n/// induced time offset, \\f$\\Delta t\\f$, due to the time-weighted averaging.\n/// Therefore, the `q_and_derivs` that we have in hand are at some time\n/// \\f$t_{0}\\f$. However, we desire `q_and_derivs` at the current time\n/// \\f$t = t_{0} + \\Delta t\\f$ to determine the appropriate control\n/// signal. We accomplish this by Taylor expanding\n/// \\f$Q(t_{0} + \\Delta t)\\f$. The averager allows for averaging of\n/// \\f$Q\\f$ and its derivatives OR to not average \\f$Q\\f$ while still averaging\n/// the derivatives (the derivatives are always averaged in order to reduce\n/// noise due to numerical differentiation). When they are both averaged, the\n/// time offset will be identical for \\f$Q\\f$ and the derivatives,\n/// i.e. `q_time_offset` = `deriv_time_offset`. If an unaveraged \\f$Q\\f$ is\n/// used, then the time offset associated with \\f$Q\\f$ is zero,\n/// i.e. `q_time_offset`=0. and the derivative time offset, `deriv_time_offset`,\n/// remains non-zero.\ntemplate <size_t DerivOrder>\nclass Controller {\n public:\n  struct UpdateFraction {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fraction of damping timescale used to determine how often to update \"\n        \"functions of time.\"};\n  };\n\n  using options = tmpl::list<UpdateFraction>;\n  static constexpr Options::String help{\n      \"Computes control signal used to reset highest derivative of a function \"\n      \"of time. Also determines when a function of time needs to be updated \"\n      \"next.\"};\n\n  Controller(const double update_fraction)\n      : update_fraction_(update_fraction) {}\n\n  Controller() = default;\n  Controller(Controller&&) = default;\n  Controller& operator=(Controller&&) = default;\n  Controller(const Controller&) = default;\n  Controller& operator=(const Controller&) = default;\n  ~Controller() = default;\n\n  DataVector operator()(const double time, const DataVector& timescales,\n                        const std::array<DataVector, DerivOrder>& q_and_derivs,\n                        double q_time_offset, double deriv_time_offset);\n\n  /// Takes the current minimum of all timescales for a specific control system\n  /// and uses that to set the time between updates\n  void assign_time_between_updates(const double current_min_timescale) {\n    time_between_updates_ = update_fraction_ * current_min_timescale;\n  }\n\n  /// Returns `true` if the controller is ready to calculate the control signal\n  bool is_ready(const double time) {\n    return time >= last_update_time_ + time_between_updates_;\n  }\n\n  /// Calculates the new expiration time as the old expiration time plus the\n  /// time between updates\n  double next_expiration_time(const double current_expiration_time) {\n    return current_expiration_time + time_between_updates_;\n  }\n\n  /// Sets the last time updated to the initial time\n  void set_initial_update_time(const double initial_time) {\n    last_update_time_ = initial_time;\n  }\n\n  /// Return the update fraction\n  double get_update_fraction() const { return update_fraction_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | update_fraction_;\n    p | time_between_updates_;\n    p | last_update_time_;\n  }\n\n  template <size_t LocalDerivOrder>\n  // NOLINTNEXTLINE(readability-redundant-declaration) false positive\n  friend bool operator==(const Controller<LocalDerivOrder>& lhs,\n                         const Controller<LocalDerivOrder>& rhs);\n\n private:\n  // If update_fraction_ isn't set we need to error\n  double update_fraction_{std::numeric_limits<double>::signaling_NaN()};\n  // If this time_between_updates_ isn't set, the default should just be that\n  // the functions of time are never updated (i.e. infinity)\n  double time_between_updates_{std::numeric_limits<double>::infinity()};\n  // Set to a large negative number to signify that we haven't updated yet and\n  // should as early as possible\n  double last_update_time_{-std::numeric_limits<double>::max()};\n};\n\ntemplate <size_t DerivOrder>\nbool operator!=(const Controller<DerivOrder>& lhs,\n                const Controller<DerivOrder>& rhs);\n"
  },
  {
    "path": "src/ControlSystem/ExpirationTimes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/ExpirationTimes.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n\nnamespace control_system {\ndouble function_of_time_expiration_time(\n    const double time, const DataVector& old_measurement_timescales,\n    const DataVector& new_measurement_timescales,\n    const int measurements_per_update, const bool delay_update) {\n  const double min_new_timescale = min(new_measurement_timescales);\n  double expiration = time;\n  if (delay_update) {\n    expiration += min(old_measurement_timescales);\n  }\n  // Mathematically, this is just += measurements_per_update *\n  // min_new_timescale, but that can differ by roundoff from repeated\n  // addition.  Particularly with delay_update = false, it is critical\n  // that the expiration time not be before the measurement time.  The\n  // actual measurement times are calculated by adding the timescale\n  // to the previous time, so we do that here to match.\n  for (int i = 0; i < measurements_per_update; ++i) {\n    expiration += min_new_timescale;\n  }\n  return expiration;\n}\n\ndouble function_of_time_initial_expiration_time(\n    const double time, const DataVector& measurement_timescales,\n    const int measurements_per_update, const bool delay_update) {\n  return function_of_time_expiration_time(\n      time, DataVector{}, measurement_timescales,\n      delay_update ? measurements_per_update : measurements_per_update - 1,\n      false);\n}\n\ndouble measurement_expiration_time(const double time,\n                                   const DataVector& old_measurement_timescales,\n                                   const DataVector& new_measurement_timescales,\n                                   const int measurements_per_update,\n                                   const bool delay_update) {\n  return function_of_time_expiration_time(\n             time, old_measurement_timescales, new_measurement_timescales,\n             measurements_per_update, delay_update) -\n         0.5 * min(new_measurement_timescales);\n}\n\ndouble measurement_initial_expiration_time(\n    const double time, const DataVector& new_measurement_timescales,\n    const int measurements_per_update, const bool delay_update) {\n  return function_of_time_initial_expiration_time(\n             time, new_measurement_timescales, measurements_per_update,\n             delay_update) -\n         0.5 * min(new_measurement_timescales);\n}\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/ExpirationTimes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"ControlSystem/CalculateMeasurementTimescales.hpp\"\n#include \"ControlSystem/CombinedName.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system {\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Calculate the next expiration time for the FunctionsOfTime.\n *\n * If \\p delay_update is false, this returns\n *\n * \\f{equation}\n * T_\\mathrm{expr}^\\mathrm{FoT} = t + N * \\tau_\\mathrm{m}^\\mathrm{new},\n * \\f}\n *\n * and if it is true,\n *\n * \\f{equation}\n * T_\\mathrm{expr}^\\mathrm{FoT} = t + \\tau_\\mathrm{m}^\\mathrm{old}\n *      + N * \\tau_\\mathrm{m}^\\mathrm{new},\n * \\f}\n *\n * where \\f$T_\\mathrm{expr}^\\mathrm{FoT}\\f$ is the expiration time for the\n * FunctionsOfTime, \\f$t\\f$ is the update time,\n * \\f$\\tau_\\mathrm{m}^\\mathrm{old/new}\\f$ is the measurement timescale, and\n * \\f$N\\f$ is the number of measurements per update.\n *\n * If \\p delay_update is true, we update the functions of time\n * one (old) measurement before they actually expire.\n *\n * The choice of having the functions of time expire exactly one old measurement\n * after they are updated is arbitrary. They could expire any time between the\n * update time and one old measurement after the update. This decision was made\n * to minimize time spent waiting for the functions of time to be valid.\n *\n * Since functions of time are valid at their expiration time, we are actually\n * able to do the next measurement if the expiration time is at that\n * measurement. Thus we delay any potential waiting that may happen until the\n * subsequent measurement (and by that time, most, if not all, functions of time\n * should have been updated because an entire horizon find happened in the\n * meantime). If the expiration time was earlier than the next measurement, we'd\n * have to pause the Algorithm on the DG elements and wait until all the\n * functions of time have been updated.\n *\n * If \\p delay_update is false, we update the functions of time\n * immediately, which allows for less parallelization but potentially\n * makes the control system more responsive.\n */\ndouble function_of_time_expiration_time(\n    double time, const DataVector& old_measurement_timescales,\n    const DataVector& new_measurement_timescales, int measurements_per_update,\n    bool delay_update);\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Calculate the first expiration time for the FunctionsOfTime.\n *\n * The first expiration time is\n *\n * \\f{equation}\n * T_\\mathrm{expr}^\\mathrm{FoT} = t + N * \\tau_\\mathrm{m},\n * \\f}\n *\n * where $t$ is the initial time, $\\tau_\\mathrm{m}$ is the measurement\n * timescale, and \\f$N\\f$ is the number of measurements per update if\n * \\p delay_update is true, and one less if it is false.\n */\ndouble function_of_time_initial_expiration_time(\n    double time, const DataVector& measurement_timescales,\n    int measurements_per_update, bool delay_update);\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Calculate the next expiration time for the MeasurementTimescales.\n *\n * \\f{align}\n * T_\\mathrm{expr}^\\mathrm{m} &= T_\\mathrm{expr}^\\mathrm{FoT} - \\frac{1}{2}\n *   \\tau_\\mathrm{m}^\\mathrm{new} \\\\\n * \\f}\n *\n * where \\f$T_\\mathrm{expr}^\\mathrm{m}\\f$ is the measurement expiration time,\n * \\f$T_\\mathrm{expr}^\\mathrm{FoT}\\f$ is the function of time expiration time as\n * calculate from `function_of_time_expiration_time()`, and\n * \\f$\\tau_\\mathrm{m}^\\mathrm{new}\\f$ is the new measurement timescale. The\n * reason for the factor of a half is as follows:\n *\n * We update the functions of time one (old) measurement before the expiration\n * time. Based on how dense triggers are set up, which control_system::Trigger\n * is a dense trigger, you calculate the next trigger (measurement) time at the\n * current measurement time. However, at the function of time expiration time we\n * need updated damping timescales from all control systems in order to\n * calculate when the next measurement is going to be (and in turn, the next\n * measurement expiration time). Thus, at the measurement that occurs at the\n * function of time expiration time, our measurement timescales can't be valid\n * and we must wait for updated ones. To achieve this, we set the measurement\n * expiration time *before* the function of time expiration time, but *after*\n * the previous measurement (the update measurement). The factor of one half is\n * just to guarantee we are more than epsilon before the function of time\n * expiration time and more than epsilon after the update measurement.\n */\ndouble measurement_expiration_time(double time,\n                                   const DataVector& old_measurement_timescales,\n                                   const DataVector& new_measurement_timescales,\n                                   int measurements_per_update,\n                                   bool delay_update);\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Calculate the first expiration time for the MeasurementTimescales.\n *\n * This is related to `function_of_time_initial_expiration_time()` in\n * the same way that `measurement_expiration_time()` is related to\n * `function_of_time_expiration_time()`.\n */\ndouble measurement_initial_expiration_time(\n    double time, const DataVector& measurement_timescales,\n    int measurements_per_update, bool delay_update);\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Construct the initial expiration times for functions of time that are\n * controlled by a control system\n *\n * The expiration times are constructed using inputs from control system\n * OptionHolders as an unordered map from the name of the function of time being\n * controlled to the expiration time.\n *\n * The expiration time for each individual function of time is computed as\n * $\\tau_\\mathrm{exp} = \\alpha_\\mathrm{update} \\tau_\\mathrm{damp}$ where\n * $\\alpha_\\mathrm{update}$ is the update fraction supplied as input to the\n * Controller and $\\tau_\\mathrm{damp}$ is/are the damping timescales\n * supplied from the TimescaleTuner ($\\tau_\\mathrm{damp}$ is a DataVector\n * with as many components as the corresponding function of time, thus\n * $\\tau_\\mathrm{exp}$ will also be a DataVector of the same length).\n *\n * However, this expiration time calculated above is not necessarily the\n * expiration that is returned by this function. We group functions of time by\n * the `control_system::protocols::Measurement` that their corresponding control\n * systems use. This is because one measurement may be used to update many\n * functions of time. So the actual expiration time that is used for all the\n * functions of time in this group is the *minimum* of the $\\tau_\\mathrm{exp}$\n * of each function of time in the group.\n *\n * These groups are calculated using `control_system::system_to_combined_names`\n * where the list of control systems comes from the passed in `OptionHolder`s.\n *\n * If the control system isn't active then expiration time is\n * `std::numeric_limits<double>::infinity()`, regardless of what the groups'\n * expiration time is.\n */\ntemplate <size_t Dim, typename... OptionHolders>\nstd::unordered_map<std::string, double> initial_expiration_times(\n    const double initial_time, const int measurements_per_update,\n    const bool delay_update,\n    const std::unique_ptr<::DomainCreator<Dim>>& domain_creator,\n    const std::optional<OptionHolders>&... option_holders) {\n  std::unordered_map<std::string, double> initial_expiration_times{};\n\n  using control_systems = tmpl::list<typename OptionHolders::control_system...>;\n\n  // First string is name of each control system. Second string is combination\n  // of control systems with same measurement\n  std::unordered_map<std::string, std::string> map_of_names =\n      system_to_combined_names<control_systems>();\n\n  std::unordered_map<std::string, double> combined_expiration_times{};\n  std::unordered_set<std::string> infinite_expiration_times{};\n  for (const auto& [control_system_name, combined_name] : map_of_names) {\n    (void)control_system_name;\n    if (combined_expiration_times.count(combined_name) != 1) {\n      combined_expiration_times[combined_name] =\n          std::numeric_limits<double>::infinity();\n    }\n  }\n\n  [[maybe_unused]] const auto combine_expiration_times =\n      [&initial_time, &measurements_per_update, &delay_update, &domain_creator,\n       &map_of_names, &combined_expiration_times,\n       &infinite_expiration_times](const auto& option_holder) {\n        const std::string& control_system_name = std::decay_t<\n            decltype(option_holder)>::value_type::control_system::name();\n        const std::string& combined_name = map_of_names[control_system_name];\n\n        // If not active, leave the expiration time as infinity\n        if (not option_holder.has_value()) {\n          infinite_expiration_times.insert(control_system_name);\n          return;\n        }\n\n        auto tuner = option_holder->tuner;\n        Tags::detail::initialize_tuner(make_not_null(&tuner), domain_creator,\n                                       initial_time, control_system_name);\n\n        const auto& controller = option_holder->controller;\n        const DataVector measurement_timescales =\n            calculate_measurement_timescales(controller, tuner,\n                                             measurements_per_update);\n        const double min_measurement_timescale = min(measurement_timescales);\n\n        const double initial_expiration_time =\n            function_of_time_initial_expiration_time(\n                initial_time, DataVector{1, min_measurement_timescale},\n                measurements_per_update, delay_update);\n\n        combined_expiration_times[combined_name] = std::min(\n            combined_expiration_times[combined_name], initial_expiration_time);\n      };\n\n  EXPAND_PACK_LEFT_TO_RIGHT(combine_expiration_times(option_holders));\n\n  // Set all functions of time for a given measurement to the same expiration\n  // time\n  for (const auto& [control_system_name, combined_name] : map_of_names) {\n    initial_expiration_times[control_system_name] =\n        infinite_expiration_times.contains(control_system_name)\n            ? std::numeric_limits<double>::infinity()\n            : combined_expiration_times[combined_name];\n  }\n\n  return initial_expiration_times;\n}\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/FutureMeasurements.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/FutureMeasurements.hpp\"\n\n#include <limits>\n#include <pup_stl.h>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace control_system {\nFutureMeasurements::FutureMeasurements(const size_t measurements_per_update,\n                                       const double first_measurement_time)\n    : measurements_{std::numeric_limits<double>::signaling_NaN(),\n                    first_measurement_time},\n      measurements_until_update_(measurements_per_update - 1),\n      measurements_per_update_(measurements_per_update) {\n  ASSERT(measurements_per_update > 0, \"Cannot update without measurements.\");\n}\n\nstd::optional<double> FutureMeasurements::next_measurement() const {\n  if (measurements_.size() <= 1) {\n    return std::nullopt;\n  }\n  // First entry is an old measurement.\n  return measurements_[1];\n}\n\nstd::optional<double> FutureMeasurements::next_update() const {\n  if (measurements_.size() <= measurements_until_update_ + 1) {\n    return std::nullopt;\n  }\n  return {measurements_[measurements_until_update_ + 1]};\n}\n\nvoid FutureMeasurements::pop_front() {\n  ASSERT(measurements_.size() > 1, \"Popped an empty container.\");\n  measurements_.pop_front();\n  if (measurements_until_update_ == 0) {\n    measurements_until_update_ = measurements_per_update_ - 1;\n  } else {\n    --measurements_until_update_;\n  }\n}\n\nvoid FutureMeasurements::update(\n    const domain::FunctionsOfTime::FunctionOfTime& measurement_timescale) {\n  ASSERT(not measurements_.empty(), \"Don't know current measurement time.\");\n  // Limit the number of stored measurements to a few cycles.  We\n  // shouldn't need more than one cycle, but tuning the exact size\n  // isn't necessary.\n  while (measurement_timescale.time_bounds()[1] >= measurements_.back() and\n         measurements_.size() < 3 * measurements_per_update_) {\n    measurements_.emplace_back(\n        measurements_.back() +\n        measurement_timescale.func(measurements_.back())[0][0]);\n  }\n}\n\nvoid FutureMeasurements::pup(PUP::er& p) {\n  p | measurements_;\n  p | measurements_until_update_;\n  p | measurements_per_update_;\n}\n\nbool operator==(const FutureMeasurements& lhs, const FutureMeasurements& rhs) {\n  return lhs.measurements_ == rhs.measurements_ and\n         lhs.measurements_until_update_ == rhs.measurements_until_update_ and\n         lhs.measurements_per_update_ == rhs.measurements_per_update_;\n}\n\nbool operator!=(const FutureMeasurements& lhs, const FutureMeasurements& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/FutureMeasurements.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <deque>\n#include <optional>\n#include <pup.h>\n#include <utility>\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n/// \\endcond\n\nnamespace control_system {\n/// Class for computing the upcoming measurement times for a control\n/// system measurement.\n///\n/// At any time, the \\ref control_system::Tags::MeasurementTimescales\n/// \"measurement timescales\" can be queried to find the spacing\n/// between control system measurements.  Most uses, however, require\n/// the actual measurement times, and sometimes need to know times of\n/// control-system update triggers.  This class calculates these\n/// quantities from the timescales.\nclass FutureMeasurements {\n public:\n  FutureMeasurements() = default;\n\n  FutureMeasurements(size_t measurements_per_update,\n                     double first_measurement_time);\n\n  /// Next measurement time, if known.\n  std::optional<double> next_measurement() const;\n  /// Next measurement that triggers an update, if known.\n  std::optional<double> next_update() const;\n\n  /// Remove the earliest measurement form the list, generally because\n  /// it has been performed.\n  void pop_front();\n\n  /// Calculate and store measurement times up through the expiration\n  /// time of the argument.\n  ///\n  /// Given a measurement time \\f$t_i\\f$, the next measurement will\n  /// occur at \\f$t_{i+1} = t_i + \\tau_m(t_i)\\f$, where\n  /// \\f$\\tau_m(t)\\f$ is the measurement timescale at time \\f$t\\f$.\n  void update(\n      const domain::FunctionsOfTime::FunctionOfTime& measurement_timescale);\n\n  void pup(PUP::er& p);\n\n  friend bool operator==(const FutureMeasurements& lhs,\n                         const FutureMeasurements& rhs);\n\n private:\n  // This stores the most recent (or current) measurement time,\n  // followed by some future measurement times.  We need to keep one\n  // old entry that isn't returned so that we can use it to calculate\n  // a new first entry if we need to update an \"empty\" list.  The\n  // bookkeeping is simpler if we keep it around even when we don't\n  // need it.\n  std::deque<double> measurements_{};\n  size_t measurements_until_update_{};\n  size_t measurements_per_update_{};\n};\n\nbool operator!=(const FutureMeasurements& lhs, const FutureMeasurements& rhs);\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/IsSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/Structure/ObjectLabel.hpp\"\n\n/// \\cond\nnamespace control_system::Systems {\ntemplate <::domain::ObjectLabel Horizon, size_t DerivOrder>\nstruct Size;\n}  // namespace control_system::Systems\n/// \\endcond\n\nnamespace control_system::size {\n// tt::is_a doesn't work because of domain::ObjectLabel and size_t\ntemplate <typename T>\nstruct is_size : std::false_type {};\n\ntemplate <domain::ObjectLabel Label, size_t DerivOrder>\nstruct is_size<Systems::Size<Label, DerivOrder>> : std::true_type {};\n\n/// Check whether a control system is the `control_system::Systems::Size` system\n/// or not\ntemplate <typename T>\nconstexpr bool is_size_v = is_size<T>::value;\n}  // namespace control_system::size\n"
  },
  {
    "path": "src/ControlSystem/Measurements/BNSCenterOfMass.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"BNSCenterOfMass.hpp\"\n\nnamespace control_system::measurements {\n\nvoid center_of_mass_integral_on_element(\n    const gsl::not_null<double*> mass_a, const gsl::not_null<double*> mass_b,\n    const gsl::not_null<std::array<double, 3>*> first_moment_a,\n    const gsl::not_null<std::array<double, 3>*> first_moment_b,\n    const Mesh<3>& mesh, const Scalar<DataVector>& inv_det_jacobian,\n    const Scalar<DataVector>& tilde_d,\n    const tnsr::I<DataVector, 3, Frame::Grid>& x_grid) {\n  // Get Jacobian and mask for left/right stars\n  const DataVector det_jacobian = 1. / get(inv_det_jacobian);\n  const DataVector positive_x = step_function(get<0>(x_grid));\n  const DataVector negative_x = (1.0 - positive_x);\n\n  // Integrals of the density and its first moment (on local element).\n  // Suffix A/B for positive/negative x-coordinate (proxy for stars A and B)\n  const DataVector integrand_a = det_jacobian * positive_x * get(tilde_d);\n  const DataVector integrand_b = det_jacobian * negative_x * get(tilde_d);\n\n  (*mass_a) = definite_integral(integrand_a, mesh);\n  (*mass_b) = definite_integral(integrand_b, mesh);\n  for (size_t i = 0; i < 3; i++) {\n    gsl::at(*first_moment_a, i) =\n        definite_integral(integrand_a * x_grid.get(i), mesh);\n    gsl::at(*first_moment_b, i) =\n        definite_integral(integrand_b * x_grid.get(i), mesh);\n  }\n}\n}  // namespace control_system::measurements\n"
  },
  {
    "path": "src/ControlSystem/Measurements/BNSCenterOfMass.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"ControlSystem/RunCallbacks.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace control_system::Tags {\nstruct WriteDataToDisk;\n}  // namespace control_system::Tags\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstruct Mesh;\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\nnamespace grmhd::ValenciaDivClean::Tags {\nstruct TildeD;\n}  // namespace grmhd::ValenciaDivClean::Tags\nnamespace Tags {\nstruct PreviousTriggerTime;\n}  // namespace Tags\n/// \\endcond\n\nnamespace control_system {\nnamespace measurements {\n/// \\cond\ntemplate <typename ControlSystems>\nstruct PostReductionSendBNSStarCentersToControlSystem;\n/// \\endcond\n\n/*!\n * \\brief  Factored Center of Mass calculation (for easier testing)\n *\n * \\details This function computes the integral of tildeD (assumed to be the\n * conservative baryon density in the inertial frame), as well as its first\n * moment in the grid frame. The integrals are limited to \\f$x>0\\f$ (label A) or\n * \\f$x<0\\f$ (label B).\n *\n * \\param mass_a Integral of tildeD (x > 0)\n * \\param mass_b Integral of tildeD (x < 0)\n * \\param first_moment_A First moment of integral of tildeD (x > 0)\n * \\param first_moment_B First moment of integral of tildeD (x < 0)\n * \\param mesh The mesh\n * \\param inv_det_jacobian The inverse determinant of the jacobian of the map\n * between logical and inertial coordinates \\param tilde_d TildeD on the mesh\n * \\param x_grid The coordinates in the grid frame\n */\nvoid center_of_mass_integral_on_element(\n    const gsl::not_null<double*> mass_a, const gsl::not_null<double*> mass_b,\n    const gsl::not_null<std::array<double, 3>*> first_moment_A,\n    const gsl::not_null<std::array<double, 3>*> first_moment_B,\n    const Mesh<3>& mesh, const Scalar<DataVector>& inv_det_jacobian,\n    const Scalar<DataVector>& tilde_d,\n    const tnsr::I<DataVector, 3, Frame::Grid>& x_grid);\n}  // namespace measurements\n\n/*!\n * \\brief An `::Event` that computes the center of mass for $x > 0$ and $x < 0$\n * where $x$ is the in the `Frame::Grid`.\n *\n * \\details See\n * `control_system::measurements::grid_center_of_mass_integral_on_element` for\n * the calculation of the CoM. This event then does a reduction and calls\n * `control_system::PostReductionSendBNSStarCentersToControlSystem` as a post\n * reduction callback.\n *\n * \\tparam ControlSystems `tmpl::list` of all control systems that use this\n * event.\n */\ntemplate <typename ControlSystems>\nclass BNSEvent : public ::Event {\n public:\n  /// \\cond\n  // LCOV_EXCL_START\n  explicit BNSEvent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BNSEvent);  // NOLINT\n  // LCOV_EXCL_STOP\n  /// \\endcond\n\n  // This event is created during control system initialization, not\n  // from the input file.\n  static constexpr bool factory_creatable = false;\n  BNSEvent() = default;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::Time, ::Tags::PreviousTriggerTime,\n                 ::Events::Tags::ObserverMesh<3>,\n                 ::Events::Tags::ObserverDetInvJacobian<Frame::ElementLogical,\n                                                        Frame::Inertial>,\n                 grmhd::ValenciaDivClean::Tags::TildeD,\n                 ::Events::Tags::ObserverCoordinates<3, Frame::Grid>>;\n\n  template <typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(const double time, const std::optional<double>& previous_time,\n                  const Mesh<3>& mesh,\n                  const Scalar<DataVector>& inv_det_jacobian,\n                  const Scalar<DataVector>& tilde_d,\n                  const tnsr::I<DataVector, 3, Frame::Grid>& x_grid,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    const LinkedMessageId<double> measurement_id{time, previous_time};\n\n    // Initialize integrals and perform local calculations\n    double mass_a = 0.;\n    double mass_b = 0.;\n    std::array<double, 3> first_moment_a = {0., 0., 0.};\n    std::array<double, 3> first_moment_b = {0., 0., 0.};\n    measurements::center_of_mass_integral_on_element(\n        &mass_a, &mass_b, &first_moment_a, &first_moment_b, mesh,\n        inv_det_jacobian, tilde_d, x_grid);\n\n    // Reduction\n    auto my_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index];\n    // We need a place to run RunCallback on... this does not need to be\n    // the control system using the CoM data.\n    auto& reduction_target_proxy = Parallel::get_parallel_component<\n        ControlComponent<Metavariables, tmpl::front<ControlSystems>>>(cache);\n    Parallel::ReductionData<\n        Parallel::ReductionDatum<LinkedMessageId<double>, funcl::AssertEqual<>>,\n        Parallel::ReductionDatum<double, funcl::Plus<>>,\n        Parallel::ReductionDatum<double, funcl::Plus<>>,\n        Parallel::ReductionDatum<std::array<double, 3>, funcl::Plus<>>,\n        Parallel::ReductionDatum<std::array<double, 3>, funcl::Plus<>>>\n        reduction_data{measurement_id, mass_a, mass_b, first_moment_a,\n                       first_moment_b};\n    Parallel::contribute_to_reduction<\n        measurements::PostReductionSendBNSStarCentersToControlSystem<\n            ControlSystems>>(std::move(reduction_data), my_proxy,\n                             reduction_target_proxy);\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*component*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n};\n\n/// \\cond\ntemplate <typename ControlSystems>\nPUP::able::PUP_ID BNSEvent<ControlSystems>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\nnamespace measurements {\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag for location of neutron star center (or more accurately, center\n/// of mass of the matter in the x>0 (label A) or x<0 (label B) region, in grid\n/// coordinates).\ntemplate <::domain::ObjectLabel Center, typename Fr>\nstruct NeutronStarCenter : db::SimpleTag {\n  using type = std::array<double, 3>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag for location of center of mass of all of the matter.\ntemplate <typename Fr>\nstruct SystemCenterOfMass : db::SimpleTag {\n  using type = std::array<double, 3>;\n};\n}  // namespace Tags\n\n/*!\n * \\brief Measurement providing the location of the center of mass of the matter\n * in the \\f$x>0\\f$ and \\f$x<0\\f$ regions (assumed to correspond to the center\n * of mass of the two neutron stars in a BNS merger).\n *\n * \\details We use Events::Tags::ObserverXXX for tags that might need to be\n * retrieved from either the Subcell or DG grid.\n */\nstruct BothNSCenters : tt::ConformsTo<protocols::Measurement> {\n  struct FindTwoCenters : tt::ConformsTo<protocols::Submeasurement> {\n    static std::string name() { return \"BothNSCenters::FindTwoCenters\"; }\n    /// Unused aliases needed to conform to the submeasurement protocol.\n    template <typename ControlSystems>\n    using interpolation_target_tag = void;\n    template <typename ControlSystems>\n    using horizon_metavars = void;\n\n    template <typename ControlSystems>\n    using event = BNSEvent<ControlSystems>;\n  };\n  /// List of submeasurements used by this measurement -- only FindTwoCenters\n  /// here.\n  using submeasurements = tmpl::list<FindTwoCenters>;\n};\n\n/*!\n * \\brief Simple action called after reduction of the center of mass data.\n *\n * \\details `mass_a`, `mass_b`, `first_moment_a`, and `first_moment_b` will\n * contain the reduced data for the integral of the density (and its first\n * moment) in the x>=0 (A label) and x<0 (B label) regions. This action\n * calculates the center of mass in each region, and sends the result to the\n * control system.\n *\n * If the `control_system::Tags::WriteDataToDisk` tag is true, then this will\n * also write the centers of mass to the `/ControlSystems/BnsCenters` subfile of\n * the reductions h5 file. The columns of the file are\n *\n * - %Time\n * - Center_A_x\n * - Center_A_y\n * - Center_A_z\n * - Center_B_x\n * - Center_B_y\n * - Center_B_z\n */\ntemplate <typename ControlSystems>\nstruct PostReductionSendBNSStarCentersToControlSystem {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const LinkedMessageId<double>& measurement_id,\n                    const double& mass_a, const double& mass_b,\n                    const std::array<double, 3>& first_moment_a,\n                    const std::array<double, 3>& first_moment_b) {\n    // Function called after reduction of the CoM data.\n    // Calculate CoM from integrals\n    std::array<double, 3> grid_center_a = first_moment_a / mass_a;\n    std::array<double, 3> grid_center_b = first_moment_b / mass_b;\n    std::array<double, 3> grid_center_total =\n        (first_moment_a + first_moment_b) / (mass_a + mass_b);\n\n    // To convert grid coords to inertial coords, we must find the block that\n    // these coords are in and use that grid to inertial map\n    const Domain<3>& domain = Parallel::get<domain::Tags::Domain<3>>(cache);\n    const domain::FunctionsOfTimeMap& functions_of_time =\n        Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n    tnsr::I<DataVector, 3, Frame::Grid> grid_tnsr_center{};\n    for (size_t i = 0; i < 3; i++) {\n      grid_tnsr_center.get(i) =\n          DataVector{gsl::at(grid_center_a, i), gsl::at(grid_center_b, i),\n                     gsl::at(grid_center_total, i)};\n    }\n\n    const auto block_logical_coords = block_logical_coordinates(\n        domain, grid_tnsr_center, measurement_id.id, functions_of_time);\n\n    ASSERT(alg::all_of(block_logical_coords,\n                       [](const auto& logical_coord_holder) {\n                         return logical_coord_holder.has_value();\n                       }),\n           \"Grid centers of BNS (\"\n               << grid_center_a << \", \" << grid_center_b << \", \"\n               << grid_center_total\n               << \") could not be mapped to the logical frame.\");\n\n    const auto& blocks = domain.blocks();\n\n    ASSERT(block_logical_coords.size() == 3,\n           \"There should be exactly 3 block logical coordinates for the two \"\n           \"centers of the BNS plus the system COM, but instead there are \"\n               << block_logical_coords.size());\n\n    std::array<double, 3> inertial_center_a{};\n    std::array<double, 3> inertial_center_b{};\n    std::array<double, 3> inertial_center_total{};\n\n    for (size_t n = 0; n < block_logical_coords.size(); n++) {\n      const auto& logical_coord_holder = block_logical_coords[n];\n      const auto& block_id = logical_coord_holder.value().id;\n\n      const auto& block = blocks[block_id.get_index()];\n      const auto& grid_to_inertial_map =\n          block.moving_mesh_grid_to_inertial_map();\n\n      const auto inertial_center = grid_to_inertial_map(\n          tnsr::I<double, 3, Frame::Grid>{\n              n == 0 ? grid_center_a\n                     : (n == 1 ? grid_center_b : grid_center_total)},\n          measurement_id.id, functions_of_time);\n\n      auto& center_to_set =\n          n == 0 ? inertial_center_a\n                 : (n == 1 ? inertial_center_b : inertial_center_total);\n      for (size_t i = 0; i < 3; i++) {\n        gsl::at(center_to_set, i) = inertial_center.get(i);\n      }\n    }\n\n    const auto center_databox = db::create<db::AddSimpleTags<\n        Tags::NeutronStarCenter<::domain::ObjectLabel::A, Frame::Grid>,\n        Tags::NeutronStarCenter<::domain::ObjectLabel::B, Frame::Grid>,\n        Tags::NeutronStarCenter<::domain::ObjectLabel::A, Frame::Inertial>,\n        Tags::NeutronStarCenter<::domain::ObjectLabel::B, Frame::Inertial>,\n        Tags::SystemCenterOfMass<Frame::Grid>,\n        Tags::SystemCenterOfMass<Frame::Inertial>>>(\n        grid_center_a, grid_center_b, inertial_center_a, inertial_center_b,\n        grid_center_total, inertial_center_total);\n\n    // Send results to the control system(s)\n    RunCallbacks<BothNSCenters::FindTwoCenters, ControlSystems>::apply(\n        center_databox, cache, measurement_id);\n\n    if (Parallel::get<control_system::Tags::WriteDataToDisk>(cache)) {\n      std::vector<double> grid_data_to_write{\n          measurement_id.id,   grid_center_a[0],     grid_center_a[1],\n          grid_center_a[2],    grid_center_b[0],     grid_center_b[1],\n          grid_center_b[2],    grid_center_total[0], grid_center_total[1],\n          grid_center_total[2]};\n\n      std::vector<double> inertial_data_to_write{measurement_id.id};\n      inertial_data_to_write.insert(inertial_data_to_write.end(),\n                                    inertial_center_a.begin(),\n                                    inertial_center_a.end());\n      inertial_data_to_write.insert(inertial_data_to_write.end(),\n                                    inertial_center_b.begin(),\n                                    inertial_center_b.end());\n      inertial_data_to_write.insert(inertial_data_to_write.end(),\n                                    inertial_center_total.begin(),\n                                    inertial_center_total.end());\n\n      auto& writer_proxy = Parallel::get_parallel_component<\n          observers::ObserverWriter<Metavariables>>(cache);\n\n      Parallel::threaded_action<\n          observers::ThreadedActions::WriteReductionDataRow>(\n          // Node 0 is always the writer\n          writer_proxy[0], grid_subfile_path_, legend_,\n          std::make_tuple(std::move(grid_data_to_write)));\n      Parallel::threaded_action<\n          observers::ThreadedActions::WriteReductionDataRow>(\n          // Node 0 is always the writer\n          writer_proxy[0], inertial_subfile_path_, legend_,\n          std::make_tuple(std::move(inertial_data_to_write)));\n    }\n  }\n\n private:\n  const static inline std::vector<std::string> legend_{\n      \"Time\",           \"Center_A_x\",      \"Center_A_y\",\n      \"Center_A_z\",     \"Center_B_x\",      \"Center_B_y\",\n      \"Center_B_z\",     \"Center_System_x\", \"Center_System_y\",\n      \"Center_System_z\"};\n  const static inline std::string grid_subfile_path_{\n      \"/ControlSystems/BnsGridCenters\"};\n  const static inline std::string inertial_subfile_path_{\n      \"/ControlSystems/BnsInertialCenters\"};\n};\n}  // namespace measurements\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Measurements/BothHorizons.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"ControlSystem/Measurements/NonFactoryCreatable.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"ControlSystem/RunCallbacks.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveCenters.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstruct Mesh;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace control_system::measurements {\n/*!\n * \\brief A `control_system::protocols::Measurement` that relies on two apparent\n * horizons.\n */\nstruct BothHorizons : tt::ConformsTo<protocols::Measurement> {\n  /*!\n   * \\brief A `control_system::protocols::Submeasurement` that starts the\n   * interpolation to the interpolation target in order to find the apparent\n   * horizon given by the template parameter `Horizon`.\n   */\n  template <::domain::ObjectLabel Horizon>\n  struct FindHorizon : tt::ConformsTo<protocols::Submeasurement> {\n    static std::string name() {\n      return \"BothHorizons::FindHorizon\" + ::domain::name(Horizon);\n    }\n\n   private:\n    template <typename ControlSystems>\n    struct HorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n      static std::string name() {\n        return \"ControlSystemAh\" + ::domain::name(Horizon);\n      }\n\n      using time_tag = ::Tags::TimeAndPrevious<0>;\n\n      using frame = ::Frame::Distorted;\n\n      using horizon_find_callbacks =\n          tmpl::list<control_system::RunCallbacks<FindHorizon, ControlSystems>,\n                     ::ah::callbacks::ObserveCenters<HorizonMetavars>>;\n      using horizon_find_failure_callbacks =\n          tmpl::list<ah::callbacks::FailedHorizonFind<HorizonMetavars, false>>;\n\n      using compute_tags_on_element =\n          tmpl::list<::Tags::TimeAndPreviousCompute<0>>;\n\n      static constexpr ah::Destination destination =\n          ah::Destination::ControlSystem;\n    };\n\n   public:\n    template <typename ControlSystems>\n    using interpolation_target_tag = void;\n    template <typename ControlSystems>\n    using horizon_metavars = HorizonMetavars<ControlSystems>;\n\n    template <typename ControlSystems>\n    using event = NonFactoryCreatableWrapper<\n        ah::Events::FindApparentHorizon<HorizonMetavars<ControlSystems>>>;\n  };\n\n  using submeasurements = tmpl::list<FindHorizon<::domain::ObjectLabel::A>,\n                                     FindHorizon<::domain::ObjectLabel::B>>;\n};\n}  // namespace control_system::measurements\n"
  },
  {
    "path": "src/ControlSystem/Measurements/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ControlSystemMeasurements)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BNSCenterOfMass.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BNSCenterOfMass.hpp\n  BothHorizons.hpp\n  CharSpeed.hpp\n  Measurements.hpp\n  NonFactoryCreatable.hpp\n  SingleHorizon.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Actions\n  ApparentHorizonFinder\n  ControlSystem\n  DataStructures\n  GeneralRelativity\n  GeneralizedHarmonic\n  Observer\n  ParallelInterpolation\n  Utilities\n  )\n"
  },
  {
    "path": "src/ControlSystem/Measurements/CharSpeed.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"ControlSystem/Measurements/NonFactoryCreatable.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"ControlSystem/RunCallbacks.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstruct Mesh;\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace control_system::measurements {\n/*!\n * \\brief A `control_system::protocols::Measurement` that relies on one\n * apparent horizon, the template parameter `Object`, and one excision surface.\n */\ntemplate <::domain::ObjectLabel Object>\nstruct CharSpeed : tt::ConformsTo<protocols::Measurement> {\n  static std::string name() { return \"CharSpeed\" + ::domain::name(Object); }\n\n  /*!\n   * \\brief A `control_system::protocols::Submeasurement` that does an\n   * interpolation to the excision boundary for this `Object` from the elements.\n   *\n   * This does not go through the interpolation framework.\n   */\n  struct Excision : tt::ConformsTo<protocols::Submeasurement> {\n    static std::string name() { return CharSpeed::name() + \"::Excision\"; }\n\n   private:\n    template <typename ControlSystems>\n    struct InterpolationTarget\n        : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n      static std::string name() {\n        return \"ControlSystemCharSpeedExcision\" + ::domain::name(Object);\n      }\n\n      static constexpr size_t index =\n          Object == ::domain::ObjectLabel::A ? 1_st : 2_st;\n\n      using temporal_id = ::Tags::TimeAndPrevious<index>;\n\n      using vars_to_interpolate_to_target = tmpl::list<\n          gr::Tags::Lapse<DataVector>,\n          ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        Frame::Distorted>,\n          gr::Tags::Shift<DataVector, 3, Frame::Distorted>,\n          ::Tags::deriv<gr::Tags::Shift<DataVector, 3, Frame::Distorted>,\n                        tmpl::size_t<3>, Frame::Distorted>,\n          gr::Tags::ShiftyQuantity<DataVector, 3, Frame::Distorted>,\n          gr::Tags::SpatialMetric<DataVector, 3, Frame::Distorted>,\n          ::domain::Tags::InverseJacobian<3, Frame::Grid, Frame::Distorted>,\n          gr::Tags::SpatialChristoffelSecondKind<DataVector, 3,\n                                                 Frame::Distorted>>;\n      using compute_vars_to_interpolate =\n          intrp::ComputeExcisionBoundaryVolumeQuantities;\n      using compute_items_on_source =\n          tmpl::list<::Tags::TimeAndPreviousCompute<index>>;\n      using compute_items_on_target =\n          tmpl::list<gr::Tags::DetAndInverseSpatialMetricCompute<\n              DataVector, 3, Frame::Distorted>>;\n      using compute_target_points =\n          intrp::TargetPoints::Sphere<InterpolationTarget, ::Frame::Grid>;\n      using post_interpolation_callbacks =\n          tmpl::list<control_system::RunCallbacks<Excision, ControlSystems>>;\n\n      template <typename Metavariables>\n      using interpolating_component =\n          typename Metavariables::gh_dg_element_array;\n    };\n\n   public:\n    template <typename ControlSystems>\n    using interpolation_target_tag = InterpolationTarget<ControlSystems>;\n    template <typename ControlSystems>\n    using horizon_metavars = void;\n\n    template <typename ControlSystems>\n    using event = NonFactoryCreatableWrapper<\n        intrp::Events::InterpolateWithoutInterpComponent<\n            3, InterpolationTarget<ControlSystems>, ::ah::source_vars<3>>>;\n  };\n\n  /*!\n   * \\brief A `control_system::protocols::Submeasurement` that starts the\n   * interpolation to the interpolation target in order to find the apparent\n   * horizon.\n   */\n  struct Horizon : tt::ConformsTo<protocols::Submeasurement> {\n    static std::string name() { return CharSpeed::name() + \"::Horizon\"; }\n\n   private:\n    template <typename ControlSystems>\n    struct HorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n     private:\n      static constexpr size_t index =\n          Object == ::domain::ObjectLabel::A ? 1_st : 2_st;\n\n     public:\n      static std::string name() {\n        return \"ControlSystemCharSpeedAh\" + ::domain::name(Object);\n      }\n\n      // Separate time tags for each object\n      using time_tag = ::Tags::TimeAndPrevious<index>;\n\n      using frame = ::Frame::Distorted;\n\n      using horizon_find_callbacks =\n          tmpl::list<control_system::RunCallbacks<Horizon, ControlSystems>>;\n      using horizon_find_failure_callbacks =\n          tmpl::list<ah::callbacks::FailedHorizonFind<HorizonMetavars, false>>;\n\n      using compute_tags_on_element =\n          tmpl::list<::Tags::TimeAndPreviousCompute<index>>;\n\n      static constexpr ah::Destination destination =\n          ah::Destination::ControlSystem;\n    };\n\n   public:\n    template <typename ControlSystems>\n    using interpolation_target_tag = void;\n    template <typename ControlSystems>\n    using horizon_metavars = HorizonMetavars<ControlSystems>;\n\n    template <typename ControlSystems>\n    using event = NonFactoryCreatableWrapper<\n        ah::Events::FindApparentHorizon<HorizonMetavars<ControlSystems>>>;\n  };\n\n  using submeasurements = tmpl::list<Horizon, Excision>;\n};\n}  // namespace control_system::measurements\n"
  },
  {
    "path": "src/ControlSystem/Measurements/Measurements.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\brief Utilities for doing measurements needed by the control system\n// NOLINTNEXTLINE(modernize-concat-nested-namespaces)\nnamespace control_system::measurements {\n/// \\brief Tags used for control system measurements\nnamespace Tags {}\n}  // namespace control_system::measurements\n"
  },
  {
    "path": "src/ControlSystem/Measurements/NonFactoryCreatable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/*!\n * \\brief Simple struct that changes a class from being factory creatable, to\n * non-factory creatable.\n *\n * It does this by defining and setting a static constexpr bool\n * `factory_creatable = false`.\n *\n * \\tparam FactoryCreatableClass Any class that is already factory creatable.\n */\ntemplate <typename FactoryCreatableClass>\nstruct NonFactoryCreatableWrapper : public FactoryCreatableClass {\n  static constexpr bool factory_creatable = false;\n  using FactoryCreatableClass::FactoryCreatableClass;\n\n  using factory_creatable_class = FactoryCreatableClass;\n\n  /// \\cond\n  explicit NonFactoryCreatableWrapper(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NonFactoryCreatableWrapper);  // NOLINT\n  /// \\endcond\n};\n\n/// \\cond\n// NOLINTBEGIN\ntemplate <typename FactoryCreatableClass>\nPUP::able::PUP_ID NonFactoryCreatableWrapper<FactoryCreatableClass>::my_PUP_ID =\n    0;\n// NOLINTEND\n/// \\endcond\n"
  },
  {
    "path": "src/ControlSystem/Measurements/SingleHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"ControlSystem/Measurements/NonFactoryCreatable.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"ControlSystem/RunCallbacks.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstruct Mesh;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace control_system::measurements {\n/*!\n * \\brief A `control_system::protocols::Measurement` that relies on only one\n * apparent horizon; the template parameter `Horizon`.\n */\ntemplate <::domain::ObjectLabel Horizon>\nstruct SingleHorizon : tt::ConformsTo<protocols::Measurement> {\n  static std::string name() {\n    return \"SingleHorizon\" + ::domain::name(Horizon);\n  }\n\n  /*!\n   * \\brief A `control_system::protocols::Submeasurement` that starts the\n   * interpolation to the interpolation target in order to find the apparent\n   * horizon.\n   */\n  struct Submeasurement : tt::ConformsTo<protocols::Submeasurement> {\n    static std::string name() { return SingleHorizon::name(); }\n\n   private:\n    template <typename ControlSystems>\n    struct HorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n      static std::string name() {\n        return \"ControlSystemSingleAh\" + ::domain::name(Horizon);\n      }\n\n      using time_tag = ::Tags::TimeAndPrevious<0>;\n\n      using frame = ::Frame::Distorted;\n\n      using horizon_find_callbacks = tmpl::list<\n          control_system::RunCallbacks<Submeasurement, ControlSystems>>;\n      using horizon_find_failure_callbacks =\n          tmpl::list<ah::callbacks::FailedHorizonFind<HorizonMetavars, false>>;\n\n      using compute_tags_on_element =\n          tmpl::list<::Tags::TimeAndPreviousCompute<0>>;\n\n      static constexpr ah::Destination destination =\n          ah::Destination::ControlSystem;\n    };\n\n   public:\n    template <typename ControlSystems>\n    using interpolation_target_tag = void;\n    template <typename ControlSystems>\n    using horizon_metavars = HorizonMetavars<ControlSystems>;\n\n    template <typename ControlSystems>\n    using event = NonFactoryCreatableWrapper<\n        ah::Events::FindApparentHorizon<HorizonMetavars<ControlSystems>>>;\n  };\n\n  using submeasurements = tmpl::list<Submeasurement>;\n};\n}  // namespace control_system::measurements\n"
  },
  {
    "path": "src/ControlSystem/Metafunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\ntemplate <class Metavariables, typename ControlSystem>\nstruct ControlComponent;\n/// \\endcond\n\nnamespace control_system {\n/// \\ingroup ControlSystemGroup\n/// Metafunctions associated with the control systems\nnamespace metafunctions {\n/// Extract the `measurement` alias from a control system struct.\ntemplate <typename ControlSystem>\nstruct measurement {\n  using type = typename ControlSystem::measurement;\n};\n\n/// Given a list of control systems, obtain a list of distinct control\n/// system measurement structs used by them.\n/// @{\ntemplate <typename ControlSystems>\nstruct measurements {\n  using type = tmpl::remove_duplicates<\n      tmpl::transform<ControlSystems, measurement<tmpl::_1>>>;\n};\n\ntemplate <typename ControlSystems>\nusing measurements_t = typename measurements<ControlSystems>::type;\n/// @}\n\n/// Given a list of control systems, extract those using a given\n/// measurement.\n/// @{\ntemplate <typename ControlSystems, typename Measurement>\nstruct control_systems_with_measurement\n    : tmpl::lazy::filter<ControlSystems, std::is_same<measurement<tmpl::_1>,\n                                                      tmpl::pin<Measurement>>> {\n};\n\ntemplate <typename ControlSystems, typename Measurement>\nusing control_systems_with_measurement_t =\n    typename control_systems_with_measurement<ControlSystems,\n                                              Measurement>::type;\n/// @}\n\n/// Given a measurement, obtain a list of its submeasurements (i.e.,\n/// `Measurement::submeasurements`).\n/// @{\ntemplate <typename Measurement>\nstruct submeasurements {\n  using type = typename Measurement::submeasurements;\n};\n\ntemplate <typename Measurement>\nusing submeasurements_t = typename submeasurements<Measurement>::type;\n/// @}\n\nnamespace detail {\ntemplate <typename Submeasurement, typename ControlSystems>\nstruct interpolation_target_tags_for_submeasurement {\n private:\n  using declared_type =\n      typename Submeasurement::template interpolation_target_tag<\n          ControlSystems>;\n\n public:\n  using type = tmpl::conditional_t<std::is_same_v<declared_type, void>,\n                                   tmpl::list<>, tmpl::list<declared_type>>;\n};\n\ntemplate <typename Submeasurement, typename ControlSystems>\nstruct horizon_metavars_for_submeasurement {\n private:\n  using declared_type =\n      typename Submeasurement::template horizon_metavars<ControlSystems>;\n\n public:\n  using type = tmpl::conditional_t<std::is_same_v<declared_type, void>,\n                                   tmpl::list<>, tmpl::list<declared_type>>;\n};\n}  // namespace detail\n\n/// Extract the `interpolation_target_tag` aliases from all\n/// submeasurements for the list of control systems.  This is intended\n/// for use in constructing the global list of interpolation target\n/// tags in the metavariables.\ntemplate <typename ControlSystems>\nusing interpolation_target_tags = tmpl::flatten<tmpl::transform<\n    measurements_t<ControlSystems>,\n    tmpl::lazy::transform<\n        submeasurements<tmpl::_1>,\n        tmpl::defer<detail::interpolation_target_tags_for_submeasurement<\n            tmpl::_1,\n            control_systems_with_measurement<tmpl::pin<ControlSystems>,\n                                             tmpl::parent<tmpl::_1>>>>>>>;\n\ntemplate <typename ControlSystems>\nusing horizon_metavars = tmpl::flatten<tmpl::transform<\n    measurements_t<ControlSystems>,\n    tmpl::lazy::transform<\n        submeasurements<tmpl::_1>,\n        tmpl::defer<detail::horizon_metavars_for_submeasurement<\n            tmpl::_1,\n            control_systems_with_measurement<tmpl::pin<ControlSystems>,\n                                             tmpl::parent<tmpl::_1>>>>>>>;\n\nnamespace detail {\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(component_being_mocked)\n\ntemplate <typename Metavariables>\nusing all_components = typename Metavariables::component_list;\n\ntemplate <typename Metavariables>\nusing all_not_mocked_components =\n    tmpl::transform<all_components<Metavariables>,\n                    get_component_being_mocked_or_default<tmpl::_1, tmpl::_1>>;\n}  // namespace detail\n\n/// Get all ControlComponent%s from the metavariables, even if they are mocked.\ntemplate <typename Metavariables>\nusing all_control_components =\n    tmpl::filter<detail::all_not_mocked_components<Metavariables>,\n                 tt::is_a<ControlComponent, tmpl::_1>>;\n\ntemplate <typename ControlSystems, typename Submeasurement>\nstruct event_from_submeasurement {\n  using type = typename Submeasurement::template event<ControlSystems>;\n};\n\ntemplate <typename ControlSystems, typename Submeasurement>\nusing event_from_submeasurement_t =\n    typename event_from_submeasurement<ControlSystems, Submeasurement>::type;\n\nnamespace detail {\ntemplate <typename AllControlSystems, typename Measurement>\nstruct events_from_measurement {\n  using submeasurements = submeasurements_t<Measurement>;\n  using control_systems_with_measurement =\n      control_systems_with_measurement_t<AllControlSystems, Measurement>;\n\n  using type = tmpl::transform<\n      submeasurements,\n      event_from_submeasurement<tmpl::pin<control_systems_with_measurement>,\n                                tmpl::_1>>;\n};\ntemplate <typename AllControlSystems, typename Measurement>\nusing events_from_measurement_t =\n    typename events_from_measurement<AllControlSystems, Measurement>::type;\n}  // namespace detail\n\n/// \\ingroup ControlSystemGroup\n/// The list of events needed for measurements for a list of control\n/// systems.\ntemplate <typename ControlSystems>\nusing control_system_events = tmpl::flatten<tmpl::transform<\n    metafunctions::measurements_t<ControlSystems>,\n    detail::events_from_measurement<tmpl::pin<ControlSystems>, tmpl::_1>>>;\n}  // namespace metafunctions\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup ControlSystemGroup\n/// Control systems and related functionality\nnamespace control_system {\n/// \\ingroup ControlSystemGroup\n/// All Actions related to the control system\nnamespace Actions {}\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief All control errors that will be used in control systems.\n *\n * \\details A control error is a struct that conforms to the\n * control_system::protocols::ControlError protocol. Control errors compute the\n * error between current map parameters and what they are expected to be. See\n * an example of a control error here:\n * \\snippet Helpers/ControlSystem/Examples.hpp ControlError\n */\nnamespace ControlErrors {}\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief All control systems.\n *\n * \\details A control system is a struct that conforms to the\n * control_system::protocols::ControlSystem protocol. They are used to control\n * the time dependent coordinate maps in an evolution. See an example of a\n * control system here:\n * \\snippet Helpers/ControlSystem/Examples.hpp ControlSystem\n */\nnamespace Systems {}\n\n/// \\ingroup ControlSystemGroup\n/// Classes and functions used in implementation of size control\nnamespace size {}\n\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ControlError.hpp\n  ControlSystem.hpp\n  Measurement.hpp\n  NamespaceDocs.hpp\n  Submeasurement.hpp\n  )\n"
  },
  {
    "path": "src/ControlSystem/Protocols/ControlError.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\ntemplate <bool AllowDecrease>\nstruct TimescaleTuner;\n\nnamespace control_system::protocols {\nnamespace detail {\n\nstruct DummyMetavariables {\n  using component_list = tmpl::list<>;\n};\nstruct DummyTupleTags {\n  using type = int;\n};\n\ntemplate <typename T, bool AllowDecrease>\nstruct has_signature\n    : std::is_invocable_r<DataVector, T, const ::TimescaleTuner<AllowDecrease>&,\n                          const Parallel::GlobalCache<DummyMetavariables>&,\n                          const double, const std::string&,\n                          const tuples::TaggedTuple<DummyTupleTags>&> {};\nCREATE_IS_CALLABLE(get_suggested_timescale)\nCREATE_IS_CALLABLE_V(get_suggested_timescale)\nCREATE_IS_CALLABLE(reset)\nCREATE_IS_CALLABLE_V(reset)\n}  // namespace detail\n/// \\brief Definition of a control error\n///\n/// A control error is used within a control system to compute how far off the\n/// the value you are controlling is from its expected value.\n///\n/// A conforming type must specify:\n///\n/// - a call operator that returns a DataVector with a signature the same as in\n///   the example shown here:\n/// - a type alias `object_centers` to a `domain::object_list` of\n///   `domain::ObjectLabel`s. These are the objects that will require the\n///   `domain::Tags::ObjectCenter`s tags to be in the GlobalCache for this\n///   control system to work.\n/// - a function with signature `std::optional<double> get_suggested_timescale()\n///   const;` which returns a potential suggested timescale. To use the\n///   timescale from the timescale tuner, return `std::nullopt`.\n/// - a function with signature `void reset();` which will reset the control\n///   error after `get_suggested_timescale()` is called.\n///\n/// \\note The TimescaleTuner can have it's template parameter be either `true`\n/// or `false`.\n///\n///   \\snippet Helpers/ControlSystem/Examples.hpp ControlError\nstruct ControlError {\n  template <typename ConformingType>\n  struct test {\n    using object_centers = typename ConformingType::object_centers;\n\n    static_assert(detail::has_signature<ConformingType, true>::value or\n                  detail::has_signature<ConformingType, false>::value);\n\n    static_assert(\n        detail::is_get_suggested_timescale_callable_v<ConformingType>);\n    static_assert(detail::is_reset_callable_v<ConformingType>);\n  };\n};\n}  // namespace control_system::protocols\n"
  },
  {
    "path": "src/ControlSystem/Protocols/ControlSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <type_traits>\n\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::protocols {\n/// \\brief Definition of a control system\n///\n/// Defines a control system for controlling a FunctionOfTime.\n///\n/// A conforming class must provide the following functionality:\n///\n/// - a static function `name` returning a `std::string`.  This\n///   corresponds to the name of the FunctionOfTime controlled by this\n///   system.\n///\n/// - a static function `component_name` returning a\n///   `std::optional<std::string>`.  This gives a name associated to each\n///   component of the FunctionOfTime that is being controlled. E.g. a\n///   FunctionOfTime controlling translation will have three components with\n///   names \"X\", \"Y\", and \"Z\". This is useful when writing data to disk. The\n///   reason this is a `std::optional` rather than just a `std::string` is\n///   because of shape control. Since the vector of coefficients of the shape\n///   map is stored in ylm::Spherepack order, not all components of this vector\n///   correspond to an actual l,m pair (see ylm::Spherepack for why this is).\n///   Thus, this is a `std::optional` to signify if a component of the\n///   FunctionOfTime isn't actually used and shouldn't be written to disk.\n///\n/// - a type alias `measurement` to a struct implementing the\n///   Measurement protocol.\n///\n/// - a type alias `simple_tags` to a `tmpl::list` of simple tags needed for the\n///   control system. These tags will be added to the DataBox of the\n///   ControlComponent. The list may be empty.\n///\n/// - a type alias `control_error` to a struct that conforms to the ControlError\n///   protocol\n///\n/// - a static constexpr size_t `deriv_order` which is the order of the highest\n///   derivative of a FunctionOfTime that you wish to control. Typically, this\n///   is the order of the FunctionOfTime itself.\n///\n/// - a member struct (or type alias) `process_measurement`, defining\n///   the following:\n///\n///   - a templated type alias `argument_tags`, which must produce a\n///     `tmpl::list` of tags when instantiated for any submeasurement\n///     of the control system's `measurement`.\n///\n///   - a static function `apply` that accepts as arguments:\n///\n///     - any submeasurement of the control system's `measurement`.\n///       For measurements with multiple submeasurements, this can be\n///       accomplished either by a template parameter or by\n///       overloading the function.\n///\n///     - the values corresponding to `argument_tags` instantiated with the\n///       type of the first argument.\n///\n///     - the global cache `Parallel::GlobalCache<Metavariables>&`\n///\n///     - a `const LinkedMessageId<double>&` identifying the\n///       measurement, with the `id` field being the measurement time.\n///\n///   The `apply` function will be called once for each submeasurement\n///   of the control system's `measurement` using data from the\n///   `DataBox` passed to `RunCallbacks`.  It should communicate any\n///   necessary data to the control system singleton.\n///\n/// Here's an example for a class conforming to this protocol:\n///\n/// \\snippet Helpers/ControlSystem/Examples.hpp ControlSystem\nstruct ControlSystem {\n  template <typename ConformingType>\n  struct test {\n    static_assert(std::is_same_v<std::decay_t<decltype(ConformingType::name())>,\n                                 std::string>);\n\n    static_assert(std::is_same_v<decltype(ConformingType::component_name(\n                                     std::declval<const size_t>(),\n                                     std::declval<const size_t>())),\n                                 std::optional<std::string>>);\n\n    using measurement = typename ConformingType::measurement;\n    static_assert(tt::assert_conforms_to_v<measurement, Measurement>);\n\n    template <typename Submeasurement>\n    struct check_process_measurement_argument_tags {\n      using type =\n          typename ConformingType::process_measurement::template argument_tags<\n              Submeasurement>;\n    };\n\n    using simple_tags = typename ConformingType::simple_tags;\n\n    using control_error = typename ConformingType::control_error;\n    static_assert(tt::assert_conforms_to_v<control_error, ControlError>);\n\n    static constexpr size_t deriv_order = ConformingType::deriv_order;\n\n    using process_measurement_argument_tags =\n        tmpl::transform<typename measurement::submeasurements,\n                        check_process_measurement_argument_tags<tmpl::_1>>;\n\n    // We can't check the apply operator, because the tags may be\n    // incomplete types, so we don't know what the argument types are.\n  };\n};\n}  // namespace control_system::protocols\n"
  },
  {
    "path": "src/ControlSystem/Protocols/Measurement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::protocols {\n/*!\n * \\brief Definition of a measurement for the control systems\n *\n * A class conforming to this protocol is referenced from each control\n * system to define measurements to be made.  Multiple control systems\n * can share the same measurement.\n *\n * A conforming class must provide a `submeasurements` type alias to a\n * `tmpl::list` of structs conforming to the Submeasurement protocol.\n *\n * Here's an example for a class conforming to this protocol:\n *\n * \\snippet Helpers/ControlSystem/Examples.hpp Measurement\n *\n * A measurement may optionally specify a `const_global_cache_tags` type alias\n * that is a list of the tags to add to the const GlobalCache.\n */\nstruct Measurement {\n  template <typename ConformingType>\n  struct test {\n    using submeasurements = typename ConformingType::submeasurements;\n\n    static_assert(\n        tmpl::all<submeasurements,\n                  tt::assert_conforms_to<tmpl::_1, Submeasurement>>::value);\n  };\n};\n}  // namespace control_system::protocols\n"
  },
  {
    "path": "src/ControlSystem/Protocols/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup ControlSystemGroup\n/// Protocols for control systems\nnamespace control_system::protocols {}\n"
  },
  {
    "path": "src/ControlSystem/Protocols/Submeasurement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::protocols {\n/*!\n * \\brief Definition of a portion of a measurement for the control\n * systems\n *\n * These structs are referenced from structs conforming to the\n * Measurement protocol.  They define independent parts of a control\n * system measurement, such as individual horizon-finds in a\n * two-horizon measurement.\n *\n * A conforming struct must provide\n *\n * - An `interpolation_target_tag` type alias templated on the \\ref\n *   ControlSystem \"control systems\" using this submeasurement.  (This template\n *   parameter must be used in the call to `RunCallbacks` discussed below.) This\n *   alias may be `void` if the submeasurement does not use an interpolation\n *   target tag.  This is only used to collect the tags that must be registered\n *   in the metavariables.\n * - A `horizon_metavars` type alias templated on the \\ref ControlSystem\n *   \"control systems\" using this submeasurement.  (This template parameter must\n *   be used in the call to `RunCallbacks` discussed below.) This alias may be\n *   `void` if the submeasurement does not find a horizon. This is only used to\n *   collect the tags that must be registered in the metavariables.\n * - An `event` type alias also templated on the \\ref ControlSystem \"control\n *   systems\" using this submeasurement which is an `::Event`. It is templated\n *   on the control systems because the event usually takes the\n *   `interpolation_target_tag` or `horizon_metavars` as a template parameter.\n *   Currently, this event must be fully functional when it is default\n *   constructed. It will not be constructed with any arguments.\n *\n * The `event` will be run on every element, and they must collectively\n * result in a single call on one chare (which need not be one of the element\n * chares) to `control_system::RunCallbacks<ConformingStructBeingDefinedHere,\n * ControlSystems>::apply`. This will almost always require performing a\n * reduction. The `ControlSystems` template parameter passed to `RunCallbacks`\n * here must be the same type that was passed to the `interpolation_target_tag`\n * (or `horizon_metavars`) and `event` type aliases. The `ControlSystems`\n * template parameter will be a list of all control systems that use the same\n * Submeasurement.\n *\n * Here's an example for a class conforming to this protocol:\n *\n * \\snippet Helpers/ControlSystem/Examples.hpp Submeasurement\n */\nstruct Submeasurement {\n  template <typename ConformingType>\n  struct test {\n    struct DummyControlSystem;\n\n    using interpolation_target_tag =\n        typename ConformingType::template interpolation_target_tag<\n            tmpl::list<DummyControlSystem>>;\n\n    using horizon_metavars = typename ConformingType::template horizon_metavars<\n        tmpl::list<DummyControlSystem>>;\n\n    using event =\n        typename ConformingType::template event<tmpl::list<DummyControlSystem>>;\n  };\n};\n}  // namespace control_system::protocols\n"
  },
  {
    "path": "src/ControlSystem/RunCallbacks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace control_system::Tags {\nstruct Verbosity;\n}  // namespace control_system::Tags\n/// \\endcond\n\nnamespace control_system {\n/*!\n * \\ingroup ControlSystemGroup\n * \\details Apply the `process_measurement` struct of each of the \\p\n * ControlSystems to the result of the \\p Submeasurement.\n *\n * The submeasurement results are supplied as a `db::DataBox` in\n * order to allow individual control systems to select the portions\n * of the submeasurement that they are interested in.\n *\n * In addition to being manually called, struct can be called as a callback and\n * conform to the `intrp::protocols::PostInterpolationCallback` and\n * `ah::protocols::Callback` protocols.\n */\ntemplate <typename Submeasurement, typename ControlSystems>\nstruct RunCallbacks\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback>,\n      tt::ConformsTo<ah::protocols::Callback> {\n private:\n  static_assert(\n      tt::assert_conforms_to_v<Submeasurement, protocols::Submeasurement>);\n  static_assert(\n      tmpl::all<\n          ControlSystems,\n          tt::assert_conforms_to<tmpl::_1, protocols::ControlSystem>>::value);\n\n public:\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const TemporalId& measurement_id) {\n    static_assert(\n        std::is_same_v<TemporalId, LinkedMessageId<double>>,\n        \"RunCallbacks expects a LinkedMessageId<double> as its temporal id\");\n    tmpl::for_each<ControlSystems>(\n        [&box, &cache, &measurement_id](auto control_system_v) {\n          using ControlSystem = tmpl::type_from<decltype(control_system_v)>;\n          db::apply<typename ControlSystem::process_measurement::\n                        template argument_tags<Submeasurement>>(\n              [&cache, &measurement_id](const auto&... args) {\n                ControlSystem::process_measurement::apply(\n                    Submeasurement{}, args..., cache, measurement_id);\n              },\n              box);\n        });\n\n    if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n      Parallel::printf(\n          \"time = %.16f: For the '%s' measurement, calling process_measurement \"\n          \"for the following control systems: (%s).\\n\",\n          measurement_id.id, pretty_type::name<Submeasurement>(),\n          pretty_type::list_of_names<ControlSystems>());\n    }\n  }\n\n  template <typename DbTags, typename Metavariables>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const FastFlow::Status /*status*/) {\n    const auto& measurement_id = db::get<ah::Tags::CurrentTime>(box).value();\n    tmpl::for_each<ControlSystems>(\n        [&box, &cache, &measurement_id](auto control_system_v) {\n          using ControlSystem = tmpl::type_from<decltype(control_system_v)>;\n          db::apply<typename ControlSystem::process_measurement::\n                        template argument_tags<Submeasurement>>(\n              [&cache, &measurement_id](const auto&... args) {\n                ControlSystem::process_measurement::apply(\n                    Submeasurement{}, args..., cache, measurement_id);\n              },\n              box);\n        });\n\n    if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n      Parallel::printf(\n          \"time = %.16f: For the '%s' measurement, calling process_measurement \"\n          \"for the following control systems: (%s).\\n\",\n          measurement_id.id, pretty_type::name<Submeasurement>(),\n          pretty_type::list_of_names<ControlSystems>());\n    }\n  }\n};\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Systems/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Expansion.hpp\n  GridCenters.hpp\n  Rotation.hpp\n  Shape.hpp\n  Size.hpp\n  Skew.hpp\n  Translation.hpp\n  )\n"
  },
  {
    "path": "src/ControlSystem/Systems/Expansion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <type_traits>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Expansion.hpp\"\n#include \"ControlSystem/Measurements/BNSCenterOfMass.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::Systems {\n/*!\n * \\brief Controls the 3D \\link\n * domain::CoordinateMaps::TimeDependent::CubicScale CubicScale \\endlink map\n *\n * \\details Controls the function \\f$a(t)\\f$ (a FunctionOfTime) in the\n * \\link domain::CoordinateMaps::TimeDependent::CubicScale\n * CubicScale \\endlink coordinate map, while the function \\f$b(t)\\f$ is\n * typically an analytic function (usually a FixedSpeedCubic). See \\link\n * domain::CoordinateMaps::TimeDependent::CubicScale CubicScale \\endlink for\n * definitions of both \\f$a(t)\\f$ and \\f$b(t)\\f$.\n *\n * Requirements:\n * - This control system requires that there be exactly two objects in the\n *   simulation\n * - Currently both these objects must be black holes\n * - Currently this control system can only be used with the \\link\n *   control_system::measurements::BothHorizons BothHorizons \\endlink\n * measurement\n * - Currently this control system can only be used with the \\link\n *   control_system::ControlErrors::Expansion Expansion \\endlink control error\n */\ntemplate <size_t DerivOrder, typename Measurement>\nstruct Expansion : tt::ConformsTo<protocols::ControlSystem> {\n  static constexpr size_t deriv_order = DerivOrder;\n\n  static std::string name() {\n    return pretty_type::short_name<Expansion<DerivOrder, Measurement>>();\n  }\n\n  // Expansion only has one component so just make it \"Expansion\"\n  static std::optional<std::string> component_name(\n      const size_t /*i*/, const size_t num_components) {\n    ASSERT(num_components == 1,\n           \"Expansion control expects 1 component but there are \"\n               << num_components << \" instead.\");\n    return name();\n  }\n\n  using measurement = Measurement;\n  static_assert(\n      tt::conforms_to_v<measurement, control_system::protocols::Measurement>);\n\n  using control_error = ControlErrors::Expansion;\n  static_assert(tt::conforms_to_v<control_error,\n                                  control_system::protocols::ControlError>);\n\n  // tag goes in control component\n  struct MeasurementQueue : db::SimpleTag {\n    using type = LinkedMessageQueue<\n        double,\n        tmpl::append<\n            tmpl::list<\n                QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n                QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>>,\n            tmpl::conditional_t<\n                std::is_same_v<measurement, measurements::BothNSCenters>,\n                tmpl::list<QueueTags::Center<::domain::ObjectLabel::A,\n                                             Frame::Inertial>,\n                           QueueTags::Center<::domain::ObjectLabel::B,\n                                             Frame::Inertial>>,\n                tmpl::list<>>>>;\n  };\n\n  using simple_tags = tmpl::list<MeasurementQueue>;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::conditional_t<\n        std::is_same_v<Submeasurement,\n                       measurements::BothNSCenters::FindTwoCenters>,\n        tmpl::list<measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::A, Frame::Grid>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::B, Frame::Grid>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::A, Frame::Inertial>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::B, Frame::Inertial>>,\n        tmpl::list<ylm::Tags::Strahlkorper<Frame::Distorted>>>;\n\n    template <::domain::ObjectLabel Horizon, typename Metavariables>\n    static void apply(\n        measurements::BothHorizons::FindHorizon<Horizon> submeasurement,\n        const ylm::Strahlkorper<Frame::Distorted>& horizon_strahlkorper,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Expansion<DerivOrder, Measurement>>>(\n          cache);\n\n      DataVector center(horizon_strahlkorper.physical_center());\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Expansion>,\n          QueueTags::Center<Horizon>>>(control_sys_proxy, measurement_id,\n                                       std::move(center));\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n\n    template <typename Metavariables>\n    static void apply(\n        measurements::BothNSCenters::FindTwoCenters submeasurement,\n        const std::array<double, 3> grid_center_a,\n        const std::array<double, 3> grid_center_b,\n        const std::array<double, 3> inertial_center_a,\n        const std::array<double, 3> inertial_center_b,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Expansion<DerivOrder, Measurement>>>(\n          cache);\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Expansion>,\n          QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::A, Frame::Inertial>,\n          QueueTags::Center<::domain::ObjectLabel::B, Frame::Inertial>>>(\n          control_sys_proxy, measurement_id, DataVector(grid_center_a),\n          DataVector(grid_center_b), DataVector(inertial_center_a),\n          DataVector(inertial_center_b));\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n  };\n};\n}  // namespace control_system::Systems\n"
  },
  {
    "path": "src/ControlSystem/Systems/GridCenters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <type_traits>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/GridCenters.hpp\"\n#include \"ControlSystem/Measurements/BNSCenterOfMass.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::Systems {\n/*!\n * \\brief Tracks the centers of two neutron stars.\n *\n * Controls the function of time with the same name which can be used to\n * determine (until merger) the centers of the two stars.\n *\n * Requirements:\n * - This control system requires that there be two objects in the simulation\n * - Can only be used with the control_system::ControlErrors::GridCenters\n *   control error.\n */\ntemplate <size_t DerivOrder, typename Measurement>\nstruct GridCenters : tt::ConformsTo<protocols::ControlSystem> {\n  static constexpr size_t deriv_order = DerivOrder;\n\n  static std::string name() { return \"GridCenters\"; }\n\n  static std::optional<std::string> component_name(\n      const size_t component, const size_t num_components) {\n    ASSERT(num_components == 6,\n           \"GridCenters control expects 6 components but there are \"\n               << num_components << \" instead.\");\n\n    const bool a_or_b = component < 3;\n    const size_t index = component % 3;\n    const std::string component_name =\n        (a_or_b ? \"A\" : \"B\") +\n        (index == 0 ? \"_x\"s : (index == 1 ? \"_y\" : \"_z\"));\n\n    return {component_name};\n  }\n\n  using measurement = Measurement;\n  static_assert(\n      tt::conforms_to_v<measurement, control_system::protocols::Measurement>);\n  static_assert(\n      std::is_same_v<measurement, measurements::BothNSCenters>,\n      \"GridCenters only accepts BothNSCenters measurement currently.\");\n\n  using control_error = ControlErrors::GridCenters;\n  static_assert(tt::conforms_to_v<control_error,\n                                  control_system::protocols::ControlError>);\n\n  // tag goes in control component\n  struct MeasurementQueue : db::SimpleTag {\n    using type = LinkedMessageQueue<\n        double,\n        tmpl::list<\n            QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n            QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>,\n            QueueTags::Center<::domain::ObjectLabel::A, Frame::Inertial>,\n            QueueTags::Center<::domain::ObjectLabel::B, Frame::Inertial>>>;\n  };\n\n  using simple_tags = tmpl::list<MeasurementQueue>;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<\n        measurements::Tags::NeutronStarCenter<::domain::ObjectLabel::A,\n                                              Frame::Grid>,\n        measurements::Tags::NeutronStarCenter<::domain::ObjectLabel::B,\n                                              Frame::Grid>,\n        measurements::Tags::NeutronStarCenter<::domain::ObjectLabel::A,\n                                              Frame::Inertial>,\n        measurements::Tags::NeutronStarCenter<::domain::ObjectLabel::B,\n                                              Frame::Inertial>>;\n\n    template <typename Metavariables>\n    static void apply(\n        measurements::BothNSCenters::FindTwoCenters submeasurement,\n        const std::array<double, 3> grid_center_a,\n        const std::array<double, 3> grid_center_b,\n        const std::array<double, 3> inertial_center_a,\n        const std::array<double, 3> inertial_center_b,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, GridCenters>>(cache);\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<GridCenters>,\n          QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::A, Frame::Inertial>,\n          QueueTags::Center<::domain::ObjectLabel::B, Frame::Inertial>>>(\n          control_sys_proxy, measurement_id, DataVector(grid_center_a),\n          DataVector(grid_center_b), DataVector(inertial_center_a),\n          DataVector(inertial_center_b));\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n  };\n};\n}  // namespace control_system::Systems\n"
  },
  {
    "path": "src/ControlSystem/Systems/Rotation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Rotation.hpp\"\n#include \"ControlSystem/Measurements/BNSCenterOfMass.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::Systems {\n/*!\n * \\brief Controls the 3D \\link domain::CoordinateMaps::TimeDependent::Rotation\n * Rotation \\endlink map\n *\n * \\details Controls the quaternion in the 3D \\link\n * domain::CoordinateMaps::TimeDependent::Rotation Rotation \\endlink map by\n * updating a \\link domain::FunctionsOfTime::QuaternionFunctionOfTime\n * QuaternionFunctionOfTime \\endlink.\n *\n * Requirements:\n * - The function of time this control system controls must be a\n *   QuaternionFunctionOfTime.\n * - This control system requires that there be exactly two objects in the\n *   simulation\n * - Currently both these objects must be black holes\n * - Currently this control system can only be used with the \\link\n *   control_system::measurements::BothHorizons BothHorizons \\endlink\n * measurement\n * - Currently this control system can only be used with the \\link\n *   control_system::ControlErrors::Rotation Rotation \\endlink control error\n *\n * \\note Internally, QuaternionFunctionOfTime holds a PiecewisePolynomial\n * representing the angle about each axis that the system has rotated through.\n * The \\link control_system::ControlErrors::Rotation rotation control error\n * \\endlink is technically for this internal PiecewisePolynomial, not the\n * quaternion itself. However, the user doesn't need to know this. The\n * `QuaternionFunctionOfTime::update()` function takes care of everything\n * automatically.\n */\ntemplate <size_t DerivOrder, typename Measurement>\nstruct Rotation : tt::ConformsTo<protocols::ControlSystem> {\n  static constexpr size_t deriv_order = DerivOrder;\n\n  static std::string name() {\n    return pretty_type::short_name<Rotation<DerivOrder, Measurement>>();\n  }\n\n  static std::optional<std::string> component_name(\n      const size_t component, const size_t num_components) {\n    ASSERT(num_components == 3,\n           \"Rotation control expects 3 components but there are \"\n               << num_components << \" instead.\");\n    return component == 0 ? \"x\" : component == 1 ? \"y\" : \"z\";\n  }\n\n  using measurement = Measurement;\n  static_assert(\n      tt::conforms_to_v<measurement, control_system::protocols::Measurement>);\n\n  using control_error = ControlErrors::Rotation;\n  static_assert(tt::conforms_to_v<control_error,\n                                  control_system::protocols::ControlError>);\n\n  // tag goes in control component\n  struct MeasurementQueue : db::SimpleTag {\n    using type = LinkedMessageQueue<\n        double,\n        tmpl::append<\n            tmpl::list<\n                QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n                QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>>,\n            tmpl::conditional_t<\n                std::is_same_v<measurement, measurements::BothNSCenters>,\n                tmpl::list<QueueTags::Center<::domain::ObjectLabel::A,\n                                             Frame::Inertial>,\n                           QueueTags::Center<::domain::ObjectLabel::B,\n                                             Frame::Inertial>>,\n                tmpl::list<>>>>;\n  };\n\n  using simple_tags = tmpl::list<MeasurementQueue>;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::conditional_t<\n        std::is_same_v<Submeasurement,\n                       measurements::BothNSCenters::FindTwoCenters>,\n        tmpl::list<measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::A, Frame::Grid>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::B, Frame::Grid>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::A, Frame::Inertial>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::B, Frame::Inertial>>,\n        tmpl::list<ylm::Tags::Strahlkorper<Frame::Distorted>>>;\n\n    template <::domain::ObjectLabel Horizon, typename Metavariables>\n    static void apply(\n        measurements::BothHorizons::FindHorizon<Horizon> submeasurement,\n        const ylm::Strahlkorper<Frame::Distorted>& strahlkorper,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Rotation<DerivOrder, Measurement>>>(\n          cache);\n\n      DataVector center(strahlkorper.physical_center());\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Rotation>,\n          QueueTags::Center<Horizon>>>(control_sys_proxy, measurement_id,\n                                       std::move(center));\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n\n    template <typename Metavariables>\n    static void apply(\n        measurements::BothNSCenters::FindTwoCenters submeasurement,\n        const std::array<double, 3> grid_center_a,\n        const std::array<double, 3> grid_center_b,\n        const std::array<double, 3> inertial_center_a,\n        const std::array<double, 3> inertial_center_b,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Rotation<DerivOrder, Measurement>>>(\n          cache);\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Rotation>,\n          QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::A, Frame::Inertial>,\n          QueueTags::Center<::domain::ObjectLabel::B, Frame::Inertial>>>(\n          control_sys_proxy, measurement_id, DataVector(grid_center_a),\n          DataVector(grid_center_b), DataVector(inertial_center_a),\n          DataVector(inertial_center_b));\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n  };\n};\n}  // namespace control_system::Systems\n"
  },
  {
    "path": "src/ControlSystem/Systems/Shape.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Shape.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Measurements/SingleHorizon.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::Systems {\n/*!\n * \\brief Controls the \\link domain::CoordinateMaps::TimeDependent::Shape Shape\n * \\endlink map\n *\n * \\details Controls the functions \\f$ \\lambda_{lm}(t) \\f$ in the \\link\n * domain::CoordinateMaps::TimeDependent::Shape Shape \\endlink map to match the\n * shape of the excision sphere to the shape of the horizon.\n *\n * Requirements:\n * - This control system requires that there be at least one excision surface in\n *   the simulation\n * - Currently this control system can only be used with the \\link\n *   control_system::measurements::BothHorizons BothHorizons \\endlink\n * measurement\n * - Currently this control system can only be used with the \\link\n *   control_system::ControlErrors::Shape Shape \\endlink control error\n */\ntemplate <::domain::ObjectLabel Object, size_t DerivOrder, typename Measurement>\nstruct Shape : tt::ConformsTo<protocols::ControlSystem> {\n  static constexpr size_t deriv_order = DerivOrder;\n\n  static std::string name() { return \"Shape\"s + ::domain::name(Object); }\n\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t num_components) {\n    // num_components = 2 * (l_max + 1)**2 if l_max == m_max which it is for the\n    // shape map. This is why we can divide by 2 and take the sqrt without\n    // worrying about odd numbers or non-perfect squares\n    const size_t l_max = -1 + sqrt(num_components / 2);\n    ylm::SpherepackIterator iter(l_max, l_max);\n    const auto compact_index = iter.compact_index(i);\n    if (compact_index.has_value()) {\n      iter.set(compact_index.value());\n      const int m = iter.coefficient_array() ==\n                            ylm::SpherepackIterator::CoefficientArray::a\n                        ? static_cast<int>(iter.m())\n                        : -static_cast<int>(iter.m());\n      return {\"l\"s + get_output(iter.l()) + \"m\"s + get_output(m)};\n    } else {\n      return std::nullopt;\n    }\n  }\n\n  using measurement = Measurement;\n  static_assert(\n      std::is_same_v<measurement, measurements::SingleHorizon<Object>> or\n          std::is_same_v<measurement, measurements::BothHorizons>,\n      \"Must use either SingleHorizon or BothHorizon measurement for Shape \"\n      \"control system.\");\n  static_assert(\n      tt::conforms_to_v<measurement, control_system::protocols::Measurement>);\n\n  using control_error = ControlErrors::Shape<Object>;\n  static_assert(tt::conforms_to_v<control_error,\n                                  control_system::protocols::ControlError>);\n\n  // tag goes in control component\n  struct MeasurementQueue : db::SimpleTag {\n    using type = LinkedMessageQueue<\n        double, tmpl::list<QueueTags::Horizon<Frame::Distorted, Object>>>;\n  };\n\n  using simple_tags = tmpl::list<MeasurementQueue>;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<ylm::Tags::Strahlkorper<Frame::Distorted>>;\n\n    template <typename Metavariables>\n    static void apply(typename measurements::SingleHorizon<\n                          Object>::Submeasurement submeasurement,\n                      const ylm::Strahlkorper<Frame::Distorted>& strahlkorper,\n                      Parallel::GlobalCache<Metavariables>& cache,\n                      const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Shape>>(cache);\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Shape>,\n          QueueTags::Horizon<Frame::Distorted, Object>>>(\n          control_sys_proxy, measurement_id, strahlkorper);\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n\n    template <::domain::ObjectLabel MeasureHorizon, typename Metavariables>\n    static void apply(\n        measurements::BothHorizons::FindHorizon<MeasureHorizon> submeasurement,\n        const ylm::Strahlkorper<Frame::Distorted>& strahlkorper,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      // The measurement event will call this for both horizons, but we only\n      // need one of the horizons. So if it is called for the wrong horizon,\n      // just do nothing.\n      if constexpr (MeasureHorizon == Object) {\n        auto& control_sys_proxy = Parallel::get_parallel_component<\n            ControlComponent<Metavariables, Shape>>(cache);\n\n        Parallel::simple_action<::Actions::UpdateMessageQueue<\n            MeasurementQueue, UpdateControlSystem<Shape>,\n            QueueTags::Horizon<Frame::Distorted, Object>>>(\n            control_sys_proxy, measurement_id, strahlkorper);\n\n        if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n          Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                           name(), measurement_id.id,\n                           pretty_type::name(submeasurement));\n        }\n      } else {\n        (void)submeasurement;\n        (void)strahlkorper;\n        (void)cache;\n        (void)measurement_id;\n      }\n    }\n  };\n};\n}  // namespace control_system::Systems\n"
  },
  {
    "path": "src/ControlSystem/Systems/Size.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Size.hpp\"\n#include \"ControlSystem/Measurements/CharSpeed.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/StrahlkorperTransformations.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\n}  // namespace Frame\nnamespace control_system {\ntemplate <typename ControlSystem>\nstruct UpdateControlSystem;\n}\n/// \\endcond\n\nnamespace control_system::Systems {\n/*!\n * \\brief Controls the \\f$l=0\\f$ component of the \\link\n * domain::CoordinateMaps::TimeDependent::Shape Shape \\endlink map\n *\n * Requirements:\n * - This control system requires that there be at least one object in the\n *   simulation\n * - This object must be a black hole (have an excision)\n * - Currently this control system can only be used with the \\link\n *   control_system::measurements::CharSpeed CharSpeed \\endlink\n * measurement\n * - Currently this control system can only be used with the \\link\n *   control_system::ControlErrors::Size Size \\endlink control error\n */\ntemplate <::domain::ObjectLabel Horizon, size_t DerivOrder>\nstruct Size : tt::ConformsTo<protocols::ControlSystem> {\n  static constexpr size_t deriv_order = DerivOrder;\n\n  static std::string name() { return \"Size\"s + ::domain::name(Horizon); }\n\n  static std::optional<std::string> component_name(\n      const size_t /*i*/, const size_t /*num_components*/) {\n    return \"Size\";\n  }\n\n  using measurement = control_system::measurements::CharSpeed<Horizon>;\n  static_assert(\n      tt::conforms_to_v<measurement, control_system::protocols::Measurement>);\n\n  using control_error = ControlErrors::Size<deriv_order, Horizon>;\n  static_assert(tt::conforms_to_v<control_error,\n                                  control_system::protocols::ControlError>);\n\n  // tag goes in control component\n  struct MeasurementQueue : db::SimpleTag {\n    using type = LinkedMessageQueue<\n        double, tmpl::list<QueueTags::SizeExcisionQuantities<Frame::Distorted>,\n                           QueueTags::SizeHorizonQuantities<Frame::Distorted>>>;\n  };\n\n  using simple_tags = tmpl::list<MeasurementQueue>;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::conditional_t<\n        std::is_same_v<Submeasurement,\n                       typename measurements::CharSpeed<Horizon>::Excision>,\n        tmpl::list<\n            ylm::Tags::Strahlkorper<Frame::Grid>, gr::Tags::Lapse<DataVector>,\n            gr::Tags::ShiftyQuantity<DataVector, 3, Frame::Distorted>,\n            gr::Tags::SpatialMetric<DataVector, 3, Frame::Distorted>,\n            gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Distorted>,\n            gr::Tags::SpatialChristoffelSecondKind<DataVector, 3,\n                                                   Frame::Distorted>,\n            ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                          Frame::Distorted>,\n            ::Tags::deriv<gr::Tags::Shift<DataVector, 3, Frame::Distorted>,\n                          tmpl::size_t<3>, Frame::Distorted>,\n            domain::Tags::InverseJacobian<3, Frame::Grid, Frame::Distorted>>,\n        tmpl::list<ylm::Tags::Strahlkorper<Frame::Distorted>,\n                   ylm::Tags::TimeDerivStrahlkorper<Frame::Distorted>>>;\n\n    template <typename Metavariables>\n    static void apply(\n        typename measurements::CharSpeed<Horizon>::Excision /*meta*/,\n        const ylm::Strahlkorper<Frame::Grid>& grid_excision_surface,\n        const Scalar<DataVector>& lapse,\n        const tnsr::I<DataVector, 3, Frame::Distorted>& shifty_quantity,\n        const tnsr::ii<DataVector, 3, Frame::Distorted>&\n            spatial_metric_on_excision_surface,\n        const tnsr::II<DataVector, 3, Frame::Distorted>&\n            inverse_spatial_metric_on_excision_surface,\n        const tnsr::Ijj<DataVector, 3, Frame::Distorted>& spatial_christoffel,\n        const tnsr::i<DataVector, 3, Frame::Distorted>& deriv_lapse,\n        const tnsr::iJ<DataVector, 3, Frame::Distorted>& deriv_shift,\n        const ::InverseJacobian<DataVector, 3, Frame::Grid, Frame::Distorted>&\n            inv_jac_grid_to_distorted,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Size>>(cache);\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %s: Received excision measurement\\n\",\n                         name(), measurement_id);\n      }\n\n      ylm::Strahlkorper<Frame::Distorted> distorted_excision_surface{};\n      strahlkorper_in_different_frame_aligned(\n          make_not_null(&distorted_excision_surface), grid_excision_surface,\n          Parallel::get<domain::Tags::Domain<3>>(cache),\n          Parallel::get<domain::Tags::FunctionsOfTime>(cache),\n          measurement_id.id);\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Size>,\n          QueueTags::SizeExcisionQuantities<Frame::Distorted>>>(\n          control_sys_proxy, measurement_id,\n          QueueTags::SizeExcisionQuantities<Frame::Distorted>::type{\n              std::move(distorted_excision_surface), lapse, shifty_quantity,\n              spatial_metric_on_excision_surface,\n              inverse_spatial_metric_on_excision_surface, spatial_christoffel,\n              deriv_lapse, deriv_shift, inv_jac_grid_to_distorted});\n    }\n\n    template <typename Metavariables>\n    static void apply(\n        typename measurements::CharSpeed<Horizon>::Horizon /*meta*/,\n        const ylm::Strahlkorper<Frame::Distorted>& horizon,\n        const ylm::Strahlkorper<Frame::Distorted>& time_deriv_horizon,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Size>>(cache);\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %s: Received horizon measurement\\n\",\n                         name(), measurement_id);\n      }\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Size>,\n          QueueTags::SizeHorizonQuantities<Frame::Distorted>>>(\n          control_sys_proxy, measurement_id,\n          QueueTags::SizeHorizonQuantities<Frame::Distorted>::type{\n              horizon, time_deriv_horizon});\n    }\n  };\n};\n}  // namespace control_system::Systems\n"
  },
  {
    "path": "src/ControlSystem/Systems/Skew.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Skew.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::Systems {\n/*!\n * \\brief Controls the 3D \\link\n * domain::CoordinateMaps::TimeDependent::Skew Skew \\endlink map\n *\n * \\details Controls the map parameters $F_y(t)$ and $F_z(t)$ from the \\link\n * domain::CoordinateMaps::TimeDependent::Skew Skew \\endlink map.\n *\n * Requirements:\n * - This control system requires that there be exactly two objects in the\n *   simulation\n * - Currently both these objects must be black holes\n * - Currently this control system can only be used with the \\link\n *   control_system::measurements::BothHorizons BothHorizons \\endlink\n *   measurement\n * - Currently this control system can only be used with the \\link\n *   control_system::ControlErrors::Skew Skew \\endlink control error\n */\ntemplate <size_t DerivOrder, typename Measurement>\nstruct Skew : tt::ConformsTo<protocols::ControlSystem> {\n  static constexpr size_t deriv_order = DerivOrder;\n\n  static std::string name() {\n    return pretty_type::short_name<Skew<DerivOrder, Measurement>>();\n  }\n\n  // Skew has two components, Y and Z\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t num_components) {\n    ASSERT(num_components == 2,\n           \"Skew control expects 2 component but there are \" << num_components\n                                                             << \" instead.\");\n    return i == 0 ? \"Y\" : \"Z\";\n  }\n\n  using measurement = Measurement;\n  static_assert(\n      tt::conforms_to_v<measurement, control_system::protocols::Measurement>);\n\n  using control_error = ControlErrors::Skew;\n  static_assert(tt::conforms_to_v<control_error,\n                                  control_system::protocols::ControlError>);\n\n  // tag goes in control component\n  struct MeasurementQueue : db::SimpleTag {\n    using type = LinkedMessageQueue<\n        double,\n        tmpl::list<\n            QueueTags::Horizon<Frame::Distorted, ::domain::ObjectLabel::A>,\n            QueueTags::Horizon<Frame::Distorted, ::domain::ObjectLabel::B>>>;\n  };\n\n  using simple_tags = tmpl::list<MeasurementQueue>;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<ylm::Tags::Strahlkorper<Frame::Distorted>>;\n\n    template <::domain::ObjectLabel Horizon, typename Metavariables>\n    static void apply(\n        measurements::BothHorizons::FindHorizon<Horizon> submeasurement,\n        const ylm::Strahlkorper<Frame::Distorted>& horizon_strahlkorper,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Skew<DerivOrder, Measurement>>>(\n          cache);\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Skew>,\n          QueueTags::Horizon<Frame::Distorted, Horizon>>>(\n          control_sys_proxy, measurement_id, horizon_strahlkorper);\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n  };\n};\n}  // namespace control_system::Systems\n"
  },
  {
    "path": "src/ControlSystem/Systems/Translation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Translation.hpp\"\n#include \"ControlSystem/Measurements/BNSCenterOfMass.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Measurements/SingleHorizon.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\nstruct Distorted;\n}  // namespace Frame\n/// \\endcond\n\nnamespace control_system::Systems {\n/*!\n * \\brief Controls the 3D \\link\n * domain::CoordinateMaps::TimeDependent::Translation Translation \\endlink map\n *\n * \\details Controls the function \\f$ \\vec{T}(t) \\f$ in the \\link\n * domain::CoordinateMaps::TimeDependent::Translation Translation \\endlink map.\n *\n * Requirements:\n * - This control system requires that there be either one or two objects in the\n *   simulation\n * - This control system can only be used with the \\link\n *   control_system::measurements::BothHorizons BothHorizons \\endlink\n *   measurement, the \\link\n *   control_system::measurements::SingleHorizon<::domain::ObjectLabel::None>\n *   \\endlink measurement, or the \\link\n *   measurements::BothNSCenters::FindTwoCenters \\endlink measurement.\n * - Currently this control system can only be used with the \\link\n *   control_system::ControlErrors::Translation Translation \\endlink control\n *   error\n */\ntemplate <size_t DerivOrder, typename Measurement, size_t NumberOfObjects>\nstruct Translation : tt::ConformsTo<protocols::ControlSystem> {\n public:\n  static constexpr size_t deriv_order = DerivOrder;\n\n  static std::string name() { return pretty_type::short_name<Translation>(); }\n\n  static std::optional<std::string> component_name(\n      const size_t component, const size_t num_components) {\n    ASSERT(num_components == 3,\n           \"Translation control expects 3 components but there are \"\n               << num_components << \" instead.\");\n    return component == 0 ? \"x\" : component == 1 ? \"y\" : \"z\";\n  }\n\n  using measurement = Measurement;\n  static_assert(\n      tt::conforms_to_v<measurement, control_system::protocols::Measurement>);\n\n  // For BNS we only control the Translation using the CoM, not the individual\n  // objects like for BBH.\n  using control_error = ControlErrors::Translation<\n      std::is_same_v<measurement, measurements::BothNSCenters>\n          ? 1\n          : NumberOfObjects>;\n  static_assert(tt::conforms_to_v<control_error,\n                                  control_system::protocols::ControlError>);\n\n  // tag goes in control component\n  struct MeasurementQueue : db::SimpleTag {\n    using type = LinkedMessageQueue<\n        double,\n        tmpl::conditional_t<\n            NumberOfObjects == 1,\n            tmpl::list<QueueTags::Center<::domain::ObjectLabel::None>>,\n            tmpl::append<\n                tmpl::list<\n                    QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n                    QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>>,\n                tmpl::conditional_t<\n                    std::is_same_v<measurement, measurements::BothNSCenters>,\n                    tmpl::list<QueueTags::Center<::domain::ObjectLabel::A,\n                                                 Frame::Inertial>,\n                               QueueTags::Center<::domain::ObjectLabel::B,\n                                                 Frame::Inertial>,\n                               QueueTags::Center<::domain::ObjectLabel::None,\n                                                 Frame::Grid>,\n                               QueueTags::Center<::domain::ObjectLabel::None,\n                                                 Frame::Inertial>>,\n                    tmpl::list<>>>>>;\n  };\n\n  using simple_tags = tmpl::list<MeasurementQueue>;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::conditional_t<\n        std::is_same_v<Submeasurement,\n                       measurements::BothNSCenters::FindTwoCenters>,\n        tmpl::list<measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::A, Frame::Grid>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::B, Frame::Grid>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::A, Frame::Inertial>,\n                   measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::B, Frame::Inertial>,\n                   measurements::Tags::SystemCenterOfMass<Frame::Grid>,\n                   measurements::Tags::SystemCenterOfMass<Frame::Inertial>>,\n        tmpl::list<ylm::Tags::Strahlkorper<Frame::Distorted>>>;\n\n    template <typename Metavariables>\n    static void apply(\n        measurements::SingleHorizon<::domain::ObjectLabel::None>::Submeasurement\n            submeasurement,\n        const ylm::Strahlkorper<Frame::Distorted>& strahlkorper,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Translation>>(cache);\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Translation>,\n          QueueTags::Center<::domain::ObjectLabel::None>>>(\n          control_sys_proxy, measurement_id,\n          DataVector{strahlkorper.physical_center()});\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n\n    template <::domain::ObjectLabel Horizon, typename Metavariables>\n    static void apply(\n        measurements::BothHorizons::FindHorizon<Horizon> submeasurement,\n        const ylm::Strahlkorper<Frame::Distorted>& strahlkorper,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Translation>>(cache);\n\n      DataVector center(strahlkorper.physical_center());\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Translation>,\n          QueueTags::Center<Horizon>>>(control_sys_proxy, measurement_id,\n                                       std::move(center));\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n\n    template <typename Metavariables>\n    static void apply(\n        measurements::BothNSCenters::FindTwoCenters submeasurement,\n        const std::array<double, 3> grid_center_a,\n        const std::array<double, 3> grid_center_b,\n        const std::array<double, 3> inertial_center_a,\n        const std::array<double, 3> inertial_center_b,\n        const std::array<double, 3> grid_system_center_of_mass,\n        const std::array<double, 3> inertial_system_center_of_mass,\n        Parallel::GlobalCache<Metavariables>& cache,\n        const LinkedMessageId<double>& measurement_id) {\n      auto& control_sys_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, Translation>>(cache);\n\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, UpdateControlSystem<Translation>,\n          QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::A, Frame::Inertial>,\n          QueueTags::Center<::domain::ObjectLabel::B, Frame::Inertial>,\n          // Use None to mark \"one object\" since we want translation to track\n          // the system CoM.\n          QueueTags::Center<::domain::ObjectLabel::None, Frame::Grid>,\n          QueueTags::Center<::domain::ObjectLabel::None, Frame::Inertial>>>(\n          control_sys_proxy, measurement_id, DataVector(grid_center_a),\n          DataVector(grid_center_b), DataVector(inertial_center_a),\n          DataVector(inertial_center_b), DataVector(grid_system_center_of_mass),\n          DataVector(inertial_system_center_of_mass));\n\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\"%s, time = %.16f: Received measurement '%s'.\\n\",\n                         name(), measurement_id.id,\n                         pretty_type::name(submeasurement));\n      }\n    }\n  };\n};\n}  // namespace control_system::Systems\n"
  },
  {
    "path": "src/ControlSystem/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FunctionsOfTimeInitialize.cpp\n  SystemTags.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FunctionsOfTimeInitialize.hpp\n  FutureMeasurements.hpp\n  IsActiveMap.hpp\n  MeasurementTimescales.hpp\n  OptionTags.hpp\n  QueueTags.hpp\n  SystemTags.hpp\n  )\n"
  },
  {
    "path": "src/ControlSystem/Tags/FunctionsOfTimeInitialize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/Tags/FunctionsOfTimeInitialize.hpp\"\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace control_system::Tags::detail {\n\nvoid check_expiration_time_consistency(\n    const std::unordered_map<std::string, double>& initial_expiration_times,\n    const std::unordered_map<std::string, bool>& is_active_map,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  for (const auto& expiration_entry : initial_expiration_times) {\n    const auto& name = expiration_entry.first;\n    const auto& expr_time = expiration_entry.second;\n    // Only need to check the expiration times if the control system is active\n    if (is_active_map.at(name)) {\n      if (functions_of_time.count(name) == 0) {\n        ERROR(\n            \"The control system '\"\n            << name\n            << \"' is not controlling a function of time. Check that the \"\n               \"DomainCreator you have chosen uses all of the control \"\n               \"systems in the executable. The existing functions of time are: \"\n            << keys_of(functions_of_time));\n      }\n\n      if (functions_of_time.at(name)->time_bounds()[1] != expr_time) {\n        ERROR(\"The expiration time for the function of time '\"\n              << name << \"' has been set improperly. It is supposed to be \"\n              << expr_time << \" but is currently set to \"\n              << functions_of_time.at(name)->time_bounds()[1]\n              << \". It is possible that the DomainCreator you are using isn't \"\n                 \"compatible with the control systems.\");\n      }\n    }\n  }\n}\n}  // namespace control_system::Tags::detail\n"
  },
  {
    "path": "src/ControlSystem/Tags/FunctionsOfTimeInitialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"ControlSystem/ExpirationTimes.hpp\"\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"ControlSystem/Tags/OptionTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Time/OptionTags/InitialTime.hpp\"\n#include \"Time/OptionTags/InitialTimeStep.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\ntemplate <class Metavariables, typename ControlSystem>\nstruct ControlComponent;\n/// \\endcond\n\nnamespace control_system::Tags {\nnamespace detail {\n// Check that all control systems are actually controlling a function of\n// time, and that the expiration times have been set appropriately. If there\n// exists a control system that isn't controlling a function of time, or the\n// expiration times were set improperly, this is an error and we shouldn't\n// continue.\nvoid check_expiration_time_consistency(\n    const std::unordered_map<std::string, double>& initial_expiration_times,\n    const std::unordered_map<std::string, bool>& is_active_map,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time);\n}  // namespace detail\n\n/// \\ingroup ControlSystemGroup\n/// The FunctionsOfTime initialized from a DomainCreator, initial time, and\n/// control system OptionHolders.\nstruct FunctionsOfTimeInitialize : domain::Tags::FunctionsOfTime {\n  using base = domain::Tags::FunctionsOfTime;\n\n  static constexpr bool pass_metavariables = true;\n\n  static std::string name() { return \"FunctionsOfTime\"; }\n\n  template <typename Metavariables>\n  using option_holders = control_system::inputs<\n      tmpl::transform<tmpl::filter<typename Metavariables::component_list,\n                                   tt::is_a<ControlComponent, tmpl::_1>>,\n                      tmpl::bind<tmpl::back, tmpl::_1>>>;\n\n  template <typename Metavariables>\n  static constexpr bool metavars_has_control_systems =\n      tmpl::size<option_holders<Metavariables>>::value > 0;\n\n  template <typename Metavariables>\n  using option_tags = tmpl::push_front<\n      tmpl::conditional_t<\n          metavars_has_control_systems<Metavariables>,\n          tmpl::flatten<tmpl::list<\n              control_system::OptionTags::MeasurementsPerUpdate,\n              control_system::OptionTags::DelayUpdate,\n              ::OptionTags::InitialTime, option_holders<Metavariables>>>,\n          tmpl::list<>>,\n      domain::OptionTags::DomainCreator<Metavariables::volume_dim>>;\n\n  /// @{\n  /// This version of create_from_options is used if the metavariables did\n  /// define control systems\n  template <typename Metavariables, typename... OptionHolders>\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator,\n      const int measurements_per_update, const bool delay_update,\n      const double initial_time,\n      const Options::Auto<OptionHolders,\n                          Options::AutoLabel::None>&... option_holders) {\n    return create_from_options<Metavariables>(\n        domain_creator, measurements_per_update, delay_update, initial_time,\n        static_cast<const std::optional<OptionHolders>&>(option_holders)...);\n  }\n\n  template <typename Metavariables, typename... OptionHolders>\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator,\n      const int measurements_per_update, const bool delay_update,\n      const double initial_time,\n      const std::optional<OptionHolders>&... option_holders) {\n    const auto initial_expiration_times =\n        control_system::initial_expiration_times(\n            initial_time, measurements_per_update, delay_update, domain_creator,\n            option_holders...);\n\n    // We need to check the expiration times so we can ensure a proper domain\n    // creator was chosen from options.\n    auto functions_of_time =\n        domain_creator->functions_of_time(initial_expiration_times);\n\n    const auto is_active_map = detail::create_is_active_map(option_holders...);\n\n    detail::check_expiration_time_consistency(initial_expiration_times,\n                                              is_active_map, functions_of_time);\n\n    return functions_of_time;\n  }\n  /// @}\n\n  /// This version of create_from_options is used if the metavariables did not\n  /// define control systems\n  template <typename Metavariables>\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator) {\n    return domain_creator->functions_of_time();\n  }\n};\n}  // namespace control_system::Tags\n"
  },
  {
    "path": "src/ControlSystem/Tags/FutureMeasurements.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"ControlSystem/FutureMeasurements.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace control_system::Tags {\n/// Measurement times for a set of control systems sharing a\n/// measurement.\ntemplate <typename ControlSystems>\nstruct FutureMeasurements : db::SimpleTag {\n  using type = control_system::FutureMeasurements;\n};\n}  // namespace control_system::Tags\n"
  },
  {
    "path": "src/ControlSystem/Tags/IsActiveMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <map>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Tags/OptionTags.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename Metavariables, typename ControlSystem>\nstruct ControlComponent;\nnamespace domain::FunctionsOfTime::OptionTags {\nstruct FunctionOfTimeFile;\nstruct FunctionOfTimeNameMap;\n}  // namespace domain::FunctionsOfTime::OptionTags\n/// \\endcond\n\nnamespace control_system::Tags {\nnamespace detail {\ntemplate <typename... OptionHolders>\nstd::unordered_map<std::string, bool> create_is_active_map(\n    const std::optional<OptionHolders>&... option_holders) {\n  std::unordered_map<std::string, bool> result{};\n\n  [[maybe_unused]] const auto add_to_result =\n      [&result](const auto& option_holder) {\n        using control_system = typename std::decay_t<\n            decltype(option_holder)>::value_type::control_system;\n        result[control_system::name()] = option_holder.has_value();\n      };\n\n  EXPAND_PACK_LEFT_TO_RIGHT(add_to_result(option_holders));\n\n  return result;\n}\n}  // namespace detail\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\ingroup ControlSystemGroup\n * \\brief Tag that holds a map between control system name and whether that\n * control system is active. Can be used in the GlobalCache.\n *\n * This effectively lets us choose control systems at runtime. The OptionHolder\n * has an option for whether the control system is active.\n */\nstruct IsActiveMap : db::SimpleTag {\n  using type = std::unordered_map<std::string, bool>;\n  static constexpr bool pass_metavariables = true;\n\n  template <typename Component>\n  struct system {\n    using type = typename Component::control_system;\n  };\n\n  template <typename Metavariables>\n  using option_tags = control_system::inputs<tmpl::transform<\n      metafunctions::all_control_components<Metavariables>, system<tmpl::_1>>>;\n\n  template <typename Metavariables, typename... OptionHolders>\n  static type create_from_options(\n      const Options::Auto<OptionHolders,\n                          Options::AutoLabel::None>&... option_holders) {\n    return create_from_options<Metavariables>(\n        static_cast<const std::optional<OptionHolders>>(option_holders)...);\n  }\n\n  template <typename Metavariables, typename... OptionHolders>\n  static type create_from_options(\n      const std::optional<OptionHolders>&... option_holders) {\n    return detail::create_is_active_map(option_holders...);\n  }\n};\n}  // namespace control_system::Tags\n"
  },
  {
    "path": "src/ControlSystem/Tags/MeasurementTimescales.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"ControlSystem/CalculateMeasurementTimescales.hpp\"\n#include \"ControlSystem/CombinedName.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/ExpirationTimes.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Tags/OptionTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Time/OptionTags/InitialTime.hpp\"\n#include \"Time/OptionTags/InitialTimeStep.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\ntemplate <class Metavariables, typename ControlSystem>\nstruct ControlComponent;\n/// \\endcond\n\nnamespace control_system::Tags {\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\ingroup ControlSystemGroup\n * \\brief The measurement timescales associated with\n * domain::Tags::FunctionsOfTime.\n *\n * We group measurement timescales by\n * the `control_system::protocols::Measurement` that their corresponding control\n * systems use. This is because one measurement may be used to update many\n * functions of time. Each group of functions of time associated with a\n * particular measurement has a corresponding timescale here, represented as\n * `PiecewisePolynomial<0>` with a single entry. That single entry is the\n * minimum of all `control_system::calculate_measurement_timescales` for every\n * control system in that group.\n *\n * The name of a measurement timescale is calculated using\n * `control_system::system_to_combined_names` for every group of control systems\n * with the same measurement.\n *\n * If all control systems that use the same measurement aren't active, then the\n * measurement timescale and expiration time are\n * `std::numeric_limits<double>::infinity()`.\n */\nstruct MeasurementTimescales : db::SimpleTag {\n  using type = std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;\n\n  template <typename Metavariables>\n  using option_holders = control_system::inputs<\n      tmpl::transform<tmpl::filter<typename Metavariables::component_list,\n                                   tt::is_a<ControlComponent, tmpl::_1>>,\n                      tmpl::bind<tmpl::back, tmpl::_1>>>;\n\n  static constexpr bool pass_metavariables = true;\n\n  template <typename Metavariables>\n  using option_tags = tmpl::push_front<\n      option_holders<Metavariables>,\n      control_system::OptionTags::MeasurementsPerUpdate,\n      control_system::OptionTags::DelayUpdate,\n      domain::OptionTags::DomainCreator<Metavariables::volume_dim>,\n      ::OptionTags::InitialTime, ::OptionTags::InitialTimeStep>;\n\n  template <typename Metavariables, typename... OptionHolders>\n  static type create_from_options(\n      const int measurements_per_update, const bool delay_update,\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator,\n      const double initial_time, const double initial_time_step,\n      const Options::Auto<OptionHolders,\n                          Options::AutoLabel::None>&... option_holders) {\n    return create_from_options<Metavariables>(\n        measurements_per_update, delay_update, domain_creator, initial_time,\n        initial_time_step,\n        static_cast<std::optional<OptionHolders>>(option_holders)...);\n  }\n\n  template <typename Metavariables, typename... OptionHolders>\n  static type create_from_options(\n      const int measurements_per_update, const bool delay_update,\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator,\n      const double initial_time, const double initial_time_step,\n      const std::optional<OptionHolders>&... option_holders) {\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        timescales{};\n\n    using control_systems =\n        tmpl::list<typename OptionHolders::control_system...>;\n\n    // First string is name of each control system. Second string is combination\n    // of control systems with same measurement\n    std::unordered_map<std::string, std::string> map_of_names =\n        system_to_combined_names<control_systems>();\n\n    // Initialize all measurements to infinity so we can take the min later\n    std::unordered_map<std::string, double> min_measurement_timescales{};\n    std::unordered_map<std::string, double> expiration_times{};\n    for (const auto& [control_system_name, combined_name] : map_of_names) {\n      (void)control_system_name;\n      if (min_measurement_timescales.count(combined_name) != 1) {\n        min_measurement_timescales[combined_name] =\n            std::numeric_limits<double>::infinity();\n        expiration_times[combined_name] =\n            std::numeric_limits<double>::infinity();\n      }\n    }\n\n    [[maybe_unused]] const auto combine_measurement_timescales =\n        [&initial_time, &initial_time_step, &domain_creator,\n         &measurements_per_update, &delay_update, &map_of_names,\n         &min_measurement_timescales,\n         &expiration_times](const auto& option_holder) {\n          // This check is intentionally inside the lambda so that it will not\n          // trigger for domains without control systems.\n          if (initial_time_step <= 0.0) {\n            ERROR(\n                \"Control systems can only be used in forward-in-time \"\n                \"evolutions.\");\n          }\n\n          const std::string& control_system_name = std::decay_t<\n              decltype(option_holder)>::value_type::control_system::name();\n          const std::string& combined_name = map_of_names[control_system_name];\n\n          // If the control system isn't active, set measurement timescale and\n          // expiration time to be infinity.\n          double min_measurement_timescale =\n              std::numeric_limits<double>::infinity();\n\n          if (option_holder.has_value()) {\n            auto tuner = option_holder->tuner;\n            ::control_system::Tags::detail::initialize_tuner(\n                make_not_null(&tuner), domain_creator, initial_time,\n                control_system_name);\n\n            const auto& controller = option_holder->controller;\n            DataVector measurement_timescales =\n                calculate_measurement_timescales(controller, tuner,\n                                                 measurements_per_update);\n\n            min_measurement_timescale = min(measurement_timescales);\n          }\n\n          min_measurement_timescales[combined_name] =\n              std::min(min_measurement_timescales[combined_name],\n                       min_measurement_timescale);\n\n          if (min_measurement_timescales[combined_name] !=\n              std::numeric_limits<double>::infinity()) {\n            const double expiration_time = measurement_initial_expiration_time(\n                initial_time,\n                DataVector{1_st, min_measurement_timescales[combined_name]},\n                measurements_per_update, delay_update);\n            expiration_times[combined_name] =\n                std::min(expiration_times[combined_name], expiration_time);\n          }\n        };\n\n    EXPAND_PACK_LEFT_TO_RIGHT(combine_measurement_timescales(option_holders));\n\n    for (const auto& [combined_name, min_measurement_timescale] :\n         min_measurement_timescales) {\n      double expiration_time = expiration_times[combined_name];\n      timescales.emplace(\n          combined_name,\n          std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n              initial_time,\n              std::array{DataVector{1, min_measurement_timescale}},\n              expiration_time));\n    }\n\n    return timescales;\n  }\n};\n}  // namespace control_system::Tags\n"
  },
  {
    "path": "src/ControlSystem/Tags/OptionTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/IsSize.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system {\n/// \\ingroup ControlSystemGroup\n/// Holds all options for a single control system\n///\n/// This struct collects all the options for a given control system during\n/// option parsing. Then during initialization, the options can be retrieved via\n/// their public member names and assigned to their corresponding DataBox tags.\n///\n/// This class should only be used if a control system is active.\ntemplate <typename ControlSystem>\nstruct OptionHolder {\n private:\n  static constexpr bool is_size =\n      ::control_system::size::is_size_v<ControlSystem>;\n\n public:\n  static_assert(tt::assert_conforms_to_v<\n                ControlSystem, control_system::protocols::ControlSystem>);\n  using control_system = ControlSystem;\n  static constexpr size_t deriv_order = control_system::deriv_order;\n\n  struct Averager {\n    using type = ::Averager<deriv_order - 1>;\n    static constexpr Options::String help = {\n        \"Averages the derivatives of the control error and possibly the \"\n        \"control error itself.\"};\n  };\n\n  struct Controller {\n    using type = ::Controller<deriv_order>;\n    static constexpr Options::String help = {\n        \"Computes the control signal which will be used to reset the functions \"\n        \"of time.\"};\n  };\n\n  struct TimescaleTuner {\n    using type = ::TimescaleTuner<not is_size>;\n    static constexpr Options::String help = {\n        \"Keeps track of the damping timescales for the control system upon \"\n        \"which other timescales are based of off.\"};\n  };\n\n  struct ControlError {\n    using type = typename ControlSystem::control_error;\n    static constexpr Options::String help = {\n        \"Computes the control error for the control system based on quantities \"\n        \"in the simulation.\"};\n  };\n\n  using options =\n      tmpl::list<Averager, Controller, TimescaleTuner, ControlError>;\n  static constexpr Options::String help = {\"Options for a control system.\"};\n\n  OptionHolder(::Averager<deriv_order - 1> input_averager,\n               ::Controller<deriv_order> input_controller,\n               ::TimescaleTuner<not is_size> input_tuner,\n               typename ControlSystem::control_error input_control_error)\n      : averager(std::move(input_averager)),\n        controller(std::move(input_controller)),\n        tuner(std::move(input_tuner)),\n        control_error(std::move(input_control_error)) {}\n\n  OptionHolder() = default;\n  OptionHolder(const OptionHolder& /*rhs*/) = default;\n  OptionHolder& operator=(const OptionHolder& /*rhs*/) = default;\n  OptionHolder(OptionHolder&& /*rhs*/) = default;\n  OptionHolder& operator=(OptionHolder&& /*rhs*/) = default;\n  ~OptionHolder() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | averager;\n    p | controller;\n    p | tuner;\n    p | control_error;\n  };\n\n  // These members are specifically made public for easy access during\n  // initialization\n  ::Averager<deriv_order - 1> averager{};\n  ::Controller<deriv_order> controller{};\n  ::TimescaleTuner<not is_size> tuner{};\n  typename ControlSystem::control_error control_error{};\n};\n\n/// \\ingroup ControlSystemGroup\n/// All option tags related to the control system\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup ControlSystemGroup\n/// Options group for all control system options\nstruct ControlSystemGroup {\n  static std::string name() { return \"ControlSystems\"; }\n  static constexpr Options::String help = {\n      \"Options for all control systems used in a simulation.\"};\n};\n\n/// \\ingroup OptionTagsGroup\n/// \\ingroup ControlSystemGroup\n/// Option tag for each individual control system. The name of this option is\n/// the name of the \\p ControlSystem struct it is templated on. This way all\n/// control systems will have a unique name.\n///\n/// \\details If `None` is specified, this control system is not active.\ntemplate <typename ControlSystem>\nstruct ControlSystemInputs {\n  using type = Options::Auto<control_system::OptionHolder<ControlSystem>,\n                             Options::AutoLabel::None>;\n  static constexpr Options::String help{\"Options for a control system.\"};\n  static std::string name() { return ControlSystem::name(); }\n  using group = ControlSystemGroup;\n};\n\n/// \\ingroup OptionTagsGroup\n/// \\ingroup ControlSystemGroup\n/// Option tag on whether to write data to disk.\nstruct WriteDataToDisk {\n  using type = bool;\n  static constexpr Options::String help = {\n      \"Whether control system data should be saved during an evolution.\"};\n  using group = ControlSystemGroup;\n};\n\n/// \\ingroup OptionTagsGroup\n/// \\ingroup ControlSystemGroup\n/// Option tag that determines how many measurements will occur per control\n/// system update.\nstruct MeasurementsPerUpdate {\n  using type = int;\n  static constexpr Options::String help = {\n      \"How many AH measurements are to be done between control system \"\n      \"updates.\"};\n  static int lower_bound() { return 1; }\n  using group = ControlSystemGroup;\n};\n\n/// \\ingroup OptionTagsGroup\n/// \\ingroup ControlSystemGroup\n/// Option tag on whether to delay FunctionOfTime updates by one\n/// measurement to improve parallelization.\nstruct DelayUpdate {\n  using type = bool;\n  static constexpr Options::String help = {\n      \"Whether FunctionOfTime updates are delayed by one measurement to \"\n      \"improve parallelization.\"};\n  using group = ControlSystemGroup;\n};\n\n/// \\ingroup OptionTagsGroup\n/// \\ingroup ControlSystemGroup\n/// Verbosity tag for printing diagnostics about the control system algorithm.\n/// This does not control when data is written to disk.\nstruct Verbosity {\n  using type = ::Verbosity;\n  static constexpr Options::String help = {\n      \"Verbosity of control system algorithm. Determines verbosity for all \"\n      \"control systems.\"};\n  using group = ControlSystemGroup;\n};\n}  // namespace OptionTags\n\n/// \\ingroup ControlSystemGroup\n/// Alias to get all the option holders from a list of control systems. This is\n/// useful in the `option_tags` alias of simple tags for getting all the options\n/// from control systems.\ntemplate <typename ControlSystems>\nusing inputs =\n    tmpl::transform<ControlSystems,\n                    tmpl::bind<OptionTags::ControlSystemInputs, tmpl::_1>>;\n\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/Tags/QueueTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\nnamespace domain {\nenum class ObjectLabel;\n}  // namespace domain\n/// \\endcond\n\n/// \\ingroup ControlSystemGroup\n/// All tags that will be used in the LinkedMessageQueue's within control\n/// systems.\n///\n/// These tags will be used to retrieve the results of the measurements that\n/// were sent to the control system which have been placed inside a\n/// LinkedMessageQueue.\nnamespace control_system::QueueTags {\n/// \\ingroup ControlSystemGroup\n/// Holds the centers of each horizon from measurements as DataVectors\ntemplate <::domain::ObjectLabel Horizon, typename = Frame::Grid>\nstruct Center {\n  using type = DataVector;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds a full strahlkorper from measurements that represents a horizon\ntemplate <typename Frame, ::domain::ObjectLabel Object>\nstruct Horizon {\n  using type = ylm::Strahlkorper<Frame>;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds a full strahlkorper from measurements for the excision surface\ntemplate <typename Frame>\nstruct ExcisionSurface {\n  using type = ylm::Strahlkorper<Frame>;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds the lapse on the `ExcisionSurface`\nstruct LapseOnExcisionSurface {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Holds a quantity that's similar to the shift, but isn't the shift, on\n * the `ExcisionSurface`.\n *\n * \\details This holds\n *\n * \\f{equation}{\n * \\beta^i \\frac{\\partial x^\\hat{i}}{\\partial x^i} =\n * \\hat{beta}^\\hat{i} + \\frac{\\partial x^\\hat{i}}{\\partial t}\n * \\f}\n *\n * where hatted quantities are in the distorted frame and non-hatted quantities\n * are in the grid frame.\n */\ntemplate <typename Frame>\nstruct ShiftyQuantity {\n  using type = tnsr::I<DataVector, 3, Frame>;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds the spatial metric on the `ExcisionSurface`\ntemplate <typename Frame>\nstruct SpatialMetricOnExcisionSurface {\n  using type = tnsr::ii<DataVector, 3, Frame>;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds the inverse spatial metric on the `ExcisionSurface`\ntemplate <typename Frame>\nstruct InverseSpatialMetricOnExcisionSurface {\n  using type = tnsr::II<DataVector, 3, Frame>;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds the spatial christoffel symbol on the `ExcisionSurface`\ntemplate <typename Frame>\nstruct SpatialChristoffelSecondKind {\n  using type = tnsr::Ijj<DataVector, 3, Frame>;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds the spatial derivative of the lapse on the `ExcisionSurface`\ntemplate <typename Frame>\nstruct DerivLapse {\n  using type = tnsr::i<DataVector, 3, Frame>;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds the spatial derivative of the `Frame` shift on the `ExcisionSurface`\ntemplate <typename Frame>\nstruct DerivShift {\n  using type = tnsr::iJ<DataVector, 3, Frame>;\n};\n\n/// \\ingroup ControlSystemGroup\n/// Holds the inverse jacobian between frames on the `ExcisionSurface`\ntemplate <typename SrcFrame, typename DestFrame>\nstruct InverseJacobian {\n  using type = ::InverseJacobian<DataVector, 3, SrcFrame, DestFrame>;\n};\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief A queue tag that holds a TaggedTuple of all quantities needed for the\n * excision measurement of size control.\n *\n * \\details Holds the following queue tags in a TaggedTuple in order:\n *\n * - `control_system::QueueTags::ExcisionSurface`\n * - `control_system::QueueTags::LapseOnExcisionSurface`\n * - `control_system::QueueTags::ShiftyQuantity`\n * - `control_system::QueueTags::SpatialMetricOnExcisionSurface`\n * - `control_system::QueueTags::InverseSpatialMetricOnExcisionSurface`\n * - `control_system::QueueTags::SpatialChristoffelSecondKind`\n * - `control_system::QueueTags::DerivLapse`\n * - `control_system::QueueTags::DerivShift`\n * - `control_system::QueueTags::InverseJacobian<::Frame::Grid, Frame>`\n */\ntemplate <typename Frame>\nstruct SizeExcisionQuantities {\n  using type = tuples::TaggedTuple<\n      ExcisionSurface<Frame>, LapseOnExcisionSurface, ShiftyQuantity<Frame>,\n      SpatialMetricOnExcisionSurface<Frame>,\n      InverseSpatialMetricOnExcisionSurface<Frame>,\n      SpatialChristoffelSecondKind<Frame>, DerivLapse<Frame>, DerivShift<Frame>,\n      InverseJacobian<::Frame::Grid, Frame>>;\n};\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief A queue tag that holds a TaggedTuple of all quantities needed for the\n * horizon measurement of size control.\n *\n * \\details Holds the following queue tags in a TaggedTuple in order:\n *\n * - `ylm::Tags::Strahlkorper`\n * - `::Tags::dt<ylm::Tags::Strahlkorper>`\n */\ntemplate <typename Frame>\nstruct SizeHorizonQuantities {\n  using type = tuples::TaggedTuple<ylm::Tags::Strahlkorper<Frame>,\n                                   ::Tags::dt<ylm::Tags::Strahlkorper<Frame>>>;\n};\n}  // namespace control_system::QueueTags\n"
  },
  {
    "path": "src/ControlSystem/Tags/SystemTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace control_system::Tags::detail {\ntemplate <bool AllowDecrease, size_t Dim>\nvoid initialize_tuner(\n    const gsl::not_null<::TimescaleTuner<AllowDecrease>*> tuner,\n    const std::unique_ptr<::DomainCreator<Dim>>& domain_creator,\n    const double initial_time, const std::string& name) {\n  // We get the functions of time in order to get the number of components\n  // so we can resize the number of timescales. Since we are only concerned\n  // with the number of components, we don't care about the initial\n  // expiration times. Rotation is special because the number of components in\n  // the function of time is 4 (quaternion) but the number of components\n  // controlled is 3 (omega), so we hardcode this value.\n  const auto functions_of_time = domain_creator->functions_of_time();\n\n  // The only reason the functions of time wouldn't have this control system is\n  // if the control system is inactive. Once we remove the ability to read in\n  // SpEC control systems, this can be handled outside of this function\n  if (functions_of_time.count(name) == 1) {\n    if (not tuner->timescales_have_been_set()) {\n      const auto& function_of_time = functions_of_time.at(name);\n\n      const auto* casted_quat_fot_2 =\n          dynamic_cast<domain::FunctionsOfTime::QuaternionFunctionOfTime<2>*>(\n              function_of_time.get());\n      const auto* casted_quat_fot_3 =\n          dynamic_cast<domain::FunctionsOfTime::QuaternionFunctionOfTime<3>*>(\n              function_of_time.get());\n\n      size_t num_components = 0;\n      if (casted_quat_fot_2 != nullptr or casted_quat_fot_3 != nullptr) {\n        num_components = 3;\n      } else {\n        num_components = function_of_time->func(initial_time)[0].size();\n      }\n\n      tuner->resize_timescales(num_components);\n    }\n  } else {\n    if (not tuner->timescales_have_been_set()) {\n      // The control system isn't active so just set it to one component\n      tuner->resize_timescales(1);\n    }\n  }\n}\n\n#define ALLOWDECREASE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template void initialize_tuner(                                        \\\n      const gsl::not_null<::TimescaleTuner<ALLOWDECREASE(data)>*> tuner, \\\n      const std::unique_ptr<::DomainCreator<DIM(data)>>& domain_creator, \\\n      const double initial_time, const std::string& name);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (true, false), (1, 2, 3))\n\n#undef INSTANTIATE\n#undef ALLOWDECREASE\n#undef DIM\n\n}  // namespace control_system::Tags::detail\n"
  },
  {
    "path": "src/ControlSystem/Tags/SystemTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/CombinedName.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/IsSize.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Tags/OptionTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Time/OptionTags/InitialTime.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateHasStaticMemberVariable.hpp\"\n\n/// \\cond\ntemplate <class Metavariables, typename ControlSystem>\nstruct ControlComponent;\nnamespace control_system {\ntemplate <typename ControlSystem>\nstruct OptionHolder;\n}  // namespace control_system\nnamespace domain::OptionTags {\ntemplate <size_t Dim>\nstruct DomainCreator;\n}  // namespace domain::OptionTags\nnamespace OptionTags {\nstruct InitialTime;\n}  // namespace OptionTags\n/// \\endcond\n\n/// \\ingroup ControlSystemGroup\n/// All DataBox tags related to the control system\nnamespace control_system::Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag for writing control system data to disk\nstruct WriteDataToDisk : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::WriteDataToDisk>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n\nusing ObserveCenters = ah::Tags::ObserveCenters;\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag for the averager\n///\n/// To compute the `deriv_order`th derivative of a control error, the max\n/// derivative we need from the averager is the `deriv_order - 1`st derivative.\n///\n/// If the option holder is nullopt, constructs a default Averager.\ntemplate <typename ControlSystem>\nstruct Averager : db::SimpleTag {\n  using type = ::Averager<ControlSystem::deriv_order - 1>;\n\n  using option_tags =\n      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const std::optional<control_system::OptionHolder<ControlSystem>>&\n          option_holder) {\n    return option_holder.has_value() ? option_holder->averager : type{};\n  }\n};\n\nnamespace detail {\ntemplate <bool AllowDecrease, size_t Dim>\nvoid initialize_tuner(\n    gsl::not_null<::TimescaleTuner<AllowDecrease>*> tuner,\n    const std::unique_ptr<::DomainCreator<Dim>>& domain_creator,\n    const double initial_time, const std::string& name);\n}  // namespace detail\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag for the timescale tuner\n///\n/// If the option holder is nullopt, constructs a default TimescaleTuner.\ntemplate <typename ControlSystem>\nstruct TimescaleTuner : db::SimpleTag {\n private:\n  static constexpr bool is_size =\n      control_system::size::is_size_v<ControlSystem>;\n\n public:\n  using type = ::TimescaleTuner<not is_size>;\n\n  template <typename Metavariables>\n  using option_tags =\n      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>,\n                 domain::OptionTags::DomainCreator<Metavariables::volume_dim>,\n                 ::OptionTags::InitialTime>;\n  static constexpr bool pass_metavariables = true;\n\n  template <typename Metavariables>\n  static type create_from_options(\n      const std::optional<control_system::OptionHolder<ControlSystem>>&\n          option_holder,\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator,\n      const double initial_time) {\n    if (not option_holder.has_value()) {\n      return type{};\n    }\n\n    auto tuner = option_holder->tuner;\n    detail::initialize_tuner(make_not_null(&tuner), domain_creator,\n                             initial_time, ControlSystem::name());\n    return tuner;\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag for the controller\n///\n/// If the option holder is nullopt, constructs a default Controller.\ntemplate <typename ControlSystem>\nstruct Controller : db::SimpleTag {\n  using type = ::Controller<ControlSystem::deriv_order>;\n\n  template <typename Metavariables>\n  using option_tags =\n      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>,\n                 domain::OptionTags::DomainCreator<Metavariables::volume_dim>,\n                 ::OptionTags::InitialTime>;\n  static constexpr bool pass_metavariables = true;\n\n  template <typename Metavariables>\n  static type create_from_options(\n      const std::optional<control_system::OptionHolder<ControlSystem>>&\n          option_holder,\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator,\n      const double initial_time) {\n    if (not option_holder.has_value()) {\n      return type{};\n    }\n\n    type controller = option_holder->controller;\n    auto tuner = option_holder->tuner;\n    detail::initialize_tuner(make_not_null(&tuner), domain_creator,\n                             initial_time, ControlSystem::name());\n\n    controller.set_initial_update_time(initial_time);\n    controller.assign_time_between_updates(min(tuner.current_timescale()));\n\n    return controller;\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag for the control error\n///\n/// If the option holder is nullopt, constructs a default ControlError.\ntemplate <typename ControlSystem>\nstruct ControlError : db::SimpleTag {\n  using type = typename ControlSystem::control_error;\n\n  using option_tags =\n      tmpl::list<OptionTags::ControlSystemInputs<ControlSystem>>;\n  static constexpr bool pass_metavariables = false;\n\n  static type create_from_options(\n      const std::optional<control_system::OptionHolder<ControlSystem>>&\n          option_holder) {\n    return option_holder.has_value() ? option_holder->control_error : type{};\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// Tag that determines how many measurements will occur per control\n/// system update. This will usually be stored in the global cache.\nstruct MeasurementsPerUpdate : db::SimpleTag {\n  using type = int;\n\n  using option_tags = tmpl::list<OptionTags::MeasurementsPerUpdate>;\n  static constexpr bool pass_metavariables = false;\n  static int create_from_options(const int measurements_per_update) {\n    return measurements_per_update;\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// Tag that determines whether FunctionOfTime updates will be delayed\n/// by one measurement.  This will usually be stored in the global\n/// cache.\nstruct DelayUpdate : db::SimpleTag {\n  using type = bool;\n\n  using option_tags = tmpl::list<OptionTags::DelayUpdate>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const bool delay_update) {\n    return delay_update;\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag that keeps track of which measurement we are on.\nstruct CurrentNumberOfMeasurements : db::SimpleTag {\n  using type = int;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ControlSystemGroup\n/// DataBox tag that holds the verbosity used to print info about the control\n/// system algorithm.\nstruct Verbosity : db::SimpleTag {\n  using type = ::Verbosity;\n\n  using option_tags = tmpl::list<OptionTags::Verbosity>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const ::Verbosity verbosity) {\n    return verbosity;\n  }\n};\n\n/*!\n * \\brief Tag meant to be stored in the GlobalCache that stores a map between\n * names of control systems and the \"combined\" name that that control system is\n * part of.\n *\n * \\details The \"combined\" name for each control system is computed using\n * `control_system::system_to_combined_names` where the list of control systems\n * is taken from the `component_list` type alias of the metavariables. Each\n * \"combined\" name corresponds to a different\n * `control_system::protocols::Measurement`.\n */\nstruct SystemToCombinedNames : db::SimpleTag {\n  using type = std::unordered_map<std::string, std::string>;\n\n  template <typename Metavariables>\n  using option_tags = tmpl::list<>;\n  static constexpr bool pass_metavariables = true;\n\n private:\n  template <typename Component>\n  using system = typename Component::control_system;\n\n public:\n  template <typename Metavariables>\n  static type create_from_options() {\n    using all_control_components =\n        metafunctions::all_control_components<Metavariables>;\n    using all_control_systems =\n        tmpl::transform<all_control_components, tmpl::bind<system, tmpl::_1>>;\n\n    return system_to_combined_names<all_control_systems>();\n  }\n};\n\n/*!\n * \\brief Map between \"combined\" names and the\n * `control_system::UpdateAggregator`s that go with each.\n */\nstruct UpdateAggregators : db::SimpleTag {\n  using type =\n      std::unordered_map<std::string, control_system::UpdateAggregator>;\n};\n}  // namespace control_system::Tags\n"
  },
  {
    "path": "src/ControlSystem/TimescaleTuner.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/TimescaleTuner.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <ostream>\n#include <pup.h>\n#include <tuple>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nstruct TimescaleCreator {\n  std::pair<DataVector, bool> operator()(const double timescale) {\n    return std::make_pair(DataVector{1, timescale}, false);\n  }\n\n  std::pair<DataVector, bool> operator()(\n      const std::vector<double>& input_timescales) {\n    DataVector timescales{input_timescales.size()};\n    for (size_t i = 0; i < input_timescales.size(); i++) {\n      timescales[i] = input_timescales[i];\n    }\n    return std::make_pair(std::move(timescales), true);\n  }\n};\n}  // namespace\n\ntemplate <bool AllowDecrease>\nTimescaleTuner<AllowDecrease>::TimescaleTuner(\n    const typename InitialTimescales::type& initial_timescale,\n    const double max_timescale, const double min_timescale,\n    const double increase_timescale_threshold, const double increase_factor,\n    const double decrease_timescale_threshold, const double decrease_factor)\n    : max_timescale_{max_timescale},\n      min_timescale_{min_timescale},\n      decrease_timescale_threshold_{decrease_timescale_threshold},\n      increase_timescale_threshold_{increase_timescale_threshold},\n      increase_factor_{increase_factor},\n      decrease_factor_{decrease_factor} {\n  std::tie(timescale_, timescales_have_been_set_) =\n      std::visit(TimescaleCreator{}, initial_timescale);\n\n  if (timescales_have_been_set_) {\n    initial_timescale_ = min(timescale_);\n  } else {\n    initial_timescale_ = timescale_[0];\n    timescale_ = DataVector{};\n  }\n\n  for (const auto& t_scale : timescale_) {\n    if (t_scale <= 0.0) {\n      ERROR(\"Initial timescale must be > 0\");\n    }\n  }\n\n  if (not AllowDecrease and decrease_factor_ != 1.0) {\n    ERROR(\n        \"If 'AllowDecrease' is false, then the specified decrease_factor must \"\n        \"be 1.0\");\n  }\n\n  if (decrease_factor_ > 1.0 or decrease_factor <= 0.0) {\n    ERROR(\"The specified decrease_factor \"\n          << decrease_factor_ << \" must satisfy 0 < decrease_factor <= 1\");\n  }\n  if (increase_factor_ < 1.0) {\n    ERROR(\"The specified increase factor \" << increase_factor_\n                                           << \" must be >= 1.0\");\n  }\n  if (min_timescale_ <= 0.0) {\n    ERROR(\"The specified minimum timescale \" << min_timescale_\n                                             << \" must be > 0\");\n  }\n  if (max_timescale_ <= min_timescale_) {\n    ERROR(\"The maximum timescale \"\n          << max_timescale_\n          << \" must be > than the specified minimum timescale \"\n          << min_timescale_);\n  }\n  if (increase_timescale_threshold_ <= 0.0) {\n    ERROR(\"The specified increase-timescale threshold \"\n          << increase_timescale_threshold_ << \" must be > 0\");\n  }\n  if (decrease_timescale_threshold_ <= increase_timescale_threshold_) {\n    ERROR(\"The decrease-timescale threshold \"\n          << decrease_timescale_threshold_\n          << \" must be > than the specified increase-timescale threshold \"\n          << increase_timescale_threshold_);\n  }\n}\n\ntemplate <bool AllowDecrease>\nconst DataVector& TimescaleTuner<AllowDecrease>::current_timescale() const {\n  check_if_timescales_have_been_set();\n  return timescale_;\n}\n\ntemplate <bool AllowDecrease>\nvoid TimescaleTuner<AllowDecrease>::resize_timescales(\n    const size_t num_timescales, const std::optional<double>& fill_value) {\n  ASSERT(num_timescales > 0,\n         \"Damping timescales must have a non-zero number of components.\");\n  timescale_ = DataVector{num_timescales};\n\n  set_timescale_if_in_allowable_range(fill_value.value_or(initial_timescale_));\n}\n\ntemplate <bool AllowDecrease>\nvoid TimescaleTuner<AllowDecrease>::set_timescale_if_in_allowable_range(\n    const double suggested_timescale) {\n  for (auto& t_scale : timescale_) {\n    t_scale = std::clamp(suggested_timescale, min_timescale_, max_timescale_);\n  }\n\n  timescales_have_been_set_ = true;\n}\n\ntemplate <bool AllowDecrease>\nvoid TimescaleTuner<AllowDecrease>::update_timescale(\n    const std::array<DataVector, 2>& q_and_dtq) {\n  check_if_timescales_have_been_set();\n  ASSERT(q_and_dtq[0].size() == timescale_.size() and\n             q_and_dtq[1].size() == timescale_.size(),\n         \"One or both of the number of components in q_and_dtq(\"\n             << q_and_dtq[0].size() << \",\" << q_and_dtq[1].size()\n             << \") is inconsistent with the number of timescales(\"\n             << timescale_.size() << \")\");\n\n  const DataVector& q = gsl::at(q_and_dtq, 0);\n  const DataVector& dtq = gsl::at(q_and_dtq, 1);\n\n  for (size_t i = 0; i < q.size(); i++) {\n    // check whether we need to decrease the timescale:\n    if (AllowDecrease and\n        (fabs(q[i]) > decrease_timescale_threshold_ or\n         fabs(dtq[i] * timescale_[i]) > decrease_timescale_threshold_) and\n        (dtq[i] * q[i] > 0.0 or\n         fabs(dtq[i]) * timescale_[i] < 0.5 * fabs(q[i]))) {\n      // the first check is if Q `or` dtQ are above the maximum tolerance.\n      // the second condition of the `and` is\n      // that Q and dtQ are the same sign (the error is growing)\n      // `or` that Q is not expected to drop to half of its current value in\n      // one timescale (not decreasing fast enough)\n      timescale_[i] *= decrease_factor_;\n    }\n    // check whether we need to increase the timescale:\n    else if (fabs(q[i]) < increase_timescale_threshold_ and\n             fabs(dtq[i] * timescale_[i]) < increase_timescale_threshold_) {\n      // if Q `and` dtQ are below the minimum required threshold\n      timescale_[i] *= increase_factor_;\n    }\n\n    // make sure the timescale has not increased(decreased) above(below) the\n    // maximum(minimum) value.\n    timescale_[i] = std::clamp(timescale_[i], min_timescale_, max_timescale_);\n  }\n}\n\ntemplate <bool AllowDecrease>\nvoid TimescaleTuner<AllowDecrease>::check_if_timescales_have_been_set() const {\n  ASSERT(timescales_have_been_set_,\n         \"Damping timescales in the TimescaleTuner have not been set yet.\");\n}\n\ntemplate <bool AllowDecrease>\nvoid TimescaleTuner<AllowDecrease>::pup(PUP::er& p) {\n  p | timescale_;\n  p | timescales_have_been_set_;\n  p | initial_timescale_;\n  p | max_timescale_;\n  p | min_timescale_;\n  p | decrease_timescale_threshold_;\n  p | increase_timescale_threshold_;\n  p | increase_factor_;\n  p | decrease_factor_;\n}\n\ntemplate <bool AllowDecrease>\nbool operator==(const TimescaleTuner<AllowDecrease>& lhs,\n                const TimescaleTuner<AllowDecrease>& rhs) {\n  return (lhs.timescale_ == rhs.timescale_) and\n         (lhs.max_timescale_ == rhs.max_timescale_) and\n         (lhs.min_timescale_ == rhs.min_timescale_) and\n         (lhs.timescales_have_been_set_ == rhs.timescales_have_been_set_) and\n         (lhs.initial_timescale_ == rhs.initial_timescale_) and\n         (lhs.decrease_timescale_threshold_ ==\n          rhs.decrease_timescale_threshold_) and\n         (lhs.increase_timescale_threshold_ ==\n          rhs.increase_timescale_threshold_) and\n         (lhs.increase_factor_ == rhs.increase_factor_) and\n         (lhs.decrease_factor_ == rhs.decrease_factor_);\n}\n\ntemplate <bool AllowDecrease>\nbool operator!=(const TimescaleTuner<AllowDecrease>& lhs,\n                const TimescaleTuner<AllowDecrease>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define ALLOWDECREASE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template class TimescaleTuner<ALLOWDECREASE(data)>;                       \\\n  template bool operator==(const TimescaleTuner<ALLOWDECREASE(data)>& lhs,  \\\n                           const TimescaleTuner<ALLOWDECREASE(data)>& rhs); \\\n  template bool operator!=(const TimescaleTuner<ALLOWDECREASE(data)>& lhs,  \\\n                           const TimescaleTuner<ALLOWDECREASE(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (true, false))\n\n#undef INSTANTIATE\n#undef ALLOWDECREASE\n"
  },
  {
    "path": "src/ControlSystem/TimescaleTuner.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Options/String.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}\n/// \\endcond\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Manages control system timescales\n *\n * The TimescaleTuner adjusts the damping timescale, \\f$\\tau\\f$, of the control\n * system.\\n The damping timescale is restricted to\n * `min_timescale`\\f$\\le\\tau\\le\\f$`max_timescale`\n *\n * The damping time is adjusted according to the following criteria:\n *\n * **Decrease** the timescale by a factor of `decrease_factor` if either \\n\n * - the error is too large: \\f$|Q| >\\f$ `decrease_timescale_threshold`\n * OR\n * the error is changing quickly: \\f$|\\dot{Q}|\\tau >\\f$\n * `decrease_timescale_threshold`,\\n\n * AND \\n\n * - the error is growing: \\f$\\dot{Q}Q > 0\\f$\n * OR\n * the expected change in \\f$Q\\f$ is less than half its current value:\n * \\f$|\\dot{Q}|\\tau < |Q|/2\\f$\n *\n * **Increase** the timescale by a factor of `increase_factor` if \\n\n * - the error is sufficiently small: \\f$|Q|<\\f$ `increase_timescale_threshold`\n * \\n\n * AND \\n\n * - the expected change in \\f$Q\\f$ is less than the threshold:\n * \\f$|\\dot{Q}|\\tau < \\f$ `increase_timescale_threshold`\n *\n * If the template bool \\p AllowDecrease is false, then the check for decreasing\n * the timescale will be ignored. This can be used if something else will be\n * controlling the decrease of the timescale.\n */\ntemplate <bool AllowDecrease>\nclass TimescaleTuner {\n public:\n  static constexpr Options::String help{\n      \"TimescaleTuner: stores and dynamically updates the timescales for each \"\n      \"component of a particular control system.\"};\n  struct InitialTimescales {\n    using type = std::variant<double, std::vector<double>>;\n    static constexpr Options::String help = {\n        \"Initial timescales for each function of time. Can either be a single \"\n        \"value which will be used for all components of a function of time, or \"\n        \"a vector of values. The vector must have the same number of \"\n        \"components as the function of time.\"};\n  };\n\n  struct MinTimescale {\n    using type = double;\n    static constexpr Options::String help = {\"Minimum timescale\"};\n  };\n\n  struct MaxTimescale {\n    using type = double;\n    static constexpr Options::String help = {\"Maximum timescale\"};\n  };\n\n  struct DecreaseThreshold {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Threshold for decrease of timescale\"};\n  };\n  struct IncreaseThreshold {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Threshold for increase of timescale\"};\n  };\n  struct IncreaseFactor {\n    using type = double;\n    static constexpr Options::String help = {\"Factor to increase timescale\"};\n  };\n  struct DecreaseFactor {\n    using type = double;\n    static constexpr Options::String help = {\"Factor to decrease timescale\"};\n  };\n\n  using options = tmpl::append<\n      tmpl::list<InitialTimescales, MaxTimescale, MinTimescale,\n                 IncreaseThreshold, IncreaseFactor>,\n      tmpl::conditional_t<AllowDecrease,\n                          tmpl::list<DecreaseThreshold, DecreaseFactor>,\n                          tmpl::list<>>>;\n\n  TimescaleTuner(\n      const typename InitialTimescales::type& initial_timescale,\n      double max_timescale, double min_timescale,\n      double increase_timescale_threshold, double increase_factor,\n      double decrease_timescale_threshold = std::numeric_limits<double>::max(),\n      double decrease_factor = 1.0);\n\n  TimescaleTuner() = default;\n  TimescaleTuner(TimescaleTuner&&) = default;\n  TimescaleTuner& operator=(TimescaleTuner&&) = default;\n  TimescaleTuner(const TimescaleTuner&) = default;\n  TimescaleTuner& operator=(const TimescaleTuner&) = default;\n  ~TimescaleTuner() = default;\n\n  /// Returns the current timescale for each component of a FunctionOfTime\n  const DataVector& current_timescale() const;\n  /// Manually sets all timescales to a specified value, unless the value is\n  /// outside of the specified minimum and maximum timescale bounds, in which\n  /// case it is set to the nearest bounded value.\n  void set_timescale_if_in_allowable_range(double suggested_timescale);\n  /// The update function responsible for modifying the timescale based on\n  /// the control system errors\n  void update_timescale(const std::array<DataVector, 2>& q_and_dtq);\n\n  /// Return whether the timescales have been set\n  bool timescales_have_been_set() const { return timescales_have_been_set_; }\n\n  /// \\brief Destructively resize the DataVector of timescales. All previous\n  /// timescale information will be lost.\n  /// \\param num_timescales Number of components to resize to. Can be larger or\n  /// smaller than the previous size. Must be greater than 0.\n  /// \\param fill_value Optional of what value to use to fill the new\n  /// timescales. `std::nullopt` signifies to use the minimum of the initial\n  /// timescales. Default is `std::nullopt`.\n  void resize_timescales(\n      size_t num_timescales,\n      const std::optional<double>& fill_value = std::nullopt);\n\n  void pup(PUP::er& p);\n\n  template <bool LocalAllowDecrease>\n  friend bool operator==(const TimescaleTuner<LocalAllowDecrease>& lhs,\n                         const TimescaleTuner<LocalAllowDecrease>& rhs);\n\n private:\n  void check_if_timescales_have_been_set() const;\n\n  DataVector timescale_;\n  bool timescales_have_been_set_{false};\n  double initial_timescale_{0.0};\n  double max_timescale_{0.0};\n  double min_timescale_{0.0};\n  double decrease_timescale_threshold_{0.0};\n  double increase_timescale_threshold_{0.0};\n  double increase_factor_{0.0};\n  double decrease_factor_{0.0};\n};\n\ntemplate <bool AllowDecrease>\nbool operator!=(const TimescaleTuner<AllowDecrease>& lhs,\n                const TimescaleTuner<AllowDecrease>& rhs);\n"
  },
  {
    "path": "src/ControlSystem/Trigger.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"ControlSystem/CombinedName.hpp\"\n#include \"ControlSystem/FutureMeasurements.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/PerformAlgorithmOnElement.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace control_system::Tags {\ntemplate <typename ControlSystems>\nstruct FutureMeasurements;\nstruct MeasurementTimescales;\nstruct Verbosity;\n}  // namespace control_system::Tags\n/// \\endcond\n\nnamespace control_system {\n/// \\ingroup ControlSystemGroup\n/// \\ingroup EventsAndTriggersGroup\n/// Trigger for control system measurements.\n///\n/// This trigger is only intended to be used with the\n/// `control_system::Event` event.  A specialization of this trigger\n/// will be created during control system initialization for each\n/// unique \\ref control_system::protocols::Measurement \"measurement\".\n///\n/// These triggers must be added to the \\ref\n/// Options::protocols::FactoryCreation \"factory_creation\" struct in\n/// the metavariables, even though they cannot be created from the\n/// input file.  The `control_system::control_system_triggers`\n/// metafunction provides the list of triggers to include.\ntemplate <typename ControlSystems>\nclass Trigger : public DenseTrigger {\n  static_assert(tmpl::size<ControlSystems>::value > 0);\n  using measurement = typename tmpl::front<ControlSystems>::measurement;\n  static_assert(tmpl::all<ControlSystems,\n                          std::is_same<metafunctions::measurement<tmpl::_1>,\n                                       tmpl::pin<measurement>>>::value);\n\n public:\n  /// \\cond\n  // LCOV_EXCL_START\n  explicit Trigger(CkMigrateMessage* const msg) : DenseTrigger(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Trigger);  // NOLINT\n  // LCOV_EXCL_STOP\n  /// \\endcond\n\n  // This trigger is created during control system initialization, not\n  // from the input file.\n  static constexpr bool factory_creatable = false;\n  Trigger() = default;\n\n  using is_triggered_return_tags = tmpl::list<>;\n  using is_triggered_argument_tags =\n      tmpl::list<::Tags::Time,\n                 control_system::Tags::FutureMeasurements<ControlSystems>>;\n\n  template <typename Metavariables, size_t Dim, typename Component>\n  std::optional<bool> is_triggered(\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& array_index, const Component* /*component*/,\n      const double time,\n      const control_system::FutureMeasurements& measurement_times) {\n    const auto next_measurement = measurement_times.next_measurement();\n    ASSERT(next_measurement.has_value(),\n           \"Checking trigger without knowing next time.\");\n    const bool triggered = time == *next_measurement;\n\n    if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) {\n      Parallel::printf(\n          \"%s, time = %.16f: Trigger for control systems (%s) is%s \"\n          \"triggered.\\n\",\n          get_output(array_index), time,\n          pretty_type::list_of_names<ControlSystems>(),\n          (triggered ? \"\" : \" not\"));\n    }\n\n    return triggered;\n  }\n\n  using next_check_time_return_tags =\n      tmpl::list<control_system::Tags::FutureMeasurements<ControlSystems>>;\n  using next_check_time_argument_tags = tmpl::list<::Tags::Time>;\n\n  template <typename Metavariables, size_t Dim, typename Component>\n  std::optional<double> next_check_time(\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& array_index, const Component* /*component*/,\n      const gsl::not_null<control_system::FutureMeasurements*>\n          measurement_times,\n      const double time) {\n    if (measurement_times->next_measurement() == std::optional(time)) {\n      measurement_times->pop_front();\n    }\n\n    if (not measurement_times->next_measurement().has_value()) {\n      auto& proxy = ::Parallel::get_parallel_component<Component>(cache);\n      const bool is_ready = Parallel::mutable_cache_item_is_ready<\n          control_system::Tags::MeasurementTimescales>(\n          cache, Parallel::make_array_component_id<Component>(array_index),\n          [&](const auto& measurement_timescales) {\n            const std::string& measurement_name =\n                control_system::combined_name<ControlSystems>();\n            ASSERT(measurement_timescales.count(measurement_name) == 1,\n                   \"Control system trigger expects a measurement timescale \"\n                   \"with the name '\"\n                       << measurement_name\n                       << \"' but could not find one. Available names are: \"\n                       << keys_of(measurement_timescales));\n            measurement_times->update(\n                *measurement_timescales.at(measurement_name));\n            if (not measurement_times->next_measurement().has_value()) {\n              if constexpr (Parallel::is_dg_element_collection_v<Component>) {\n                // Note: The ArrayComponentId is still created with the\n                // array_index (ElementId) because we only support 1 callback\n                // per ArrayComponentId. This would mean for nodegroups we would\n                // discard a lot of callbacks that we need. Alternatively, the\n                // callback could do a broadcast to all elements on the\n                // nodegroup.\n                const auto element_location = static_cast<int>(\n                    Parallel::local_synchronous_action<\n                        Parallel::Actions::GetItemFromDistributedOject<\n                            Parallel::Tags::ElementLocations<Dim>>>(\n                        Parallel::get_parallel_component<Component>(cache))\n                        ->at(array_index));\n                return std::unique_ptr<Parallel::Callback>(\n                    new Parallel::ThreadedActionCallback<\n                        Parallel::Actions::PerformAlgorithmOnElement<false>,\n                        decltype(proxy[element_location]),\n                        std::decay_t<decltype(array_index)>>{\n                        proxy[element_location], array_index});\n              } else {\n                return std::unique_ptr<Parallel::Callback>(\n                    new Parallel::PerformAlgorithmCallback(proxy[array_index]));\n              }\n            }\n            return std::unique_ptr<Parallel::Callback>{};\n          });\n\n      if (not is_ready) {\n        if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) {\n          Parallel::printf(\n              \"%s, time = %.16f: Trigger for control systems (%s) - Cannot \"\n              \"calculate next_check_time\\n\",\n              get_output(array_index), time,\n              pretty_type::list_of_names<ControlSystems>());\n        }\n        return std::nullopt;\n      }\n    }\n\n    const double next_trigger = *measurement_times->next_measurement();\n    ASSERT(next_trigger > time,\n           \"Next trigger is in the past: \" << next_trigger << \" > \" << time);\n\n    if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Debug) {\n      Parallel::printf(\n          \"%s, time = %.16f: Trigger for control systems (%s) - next check \"\n          \"time is %.16f\\n\",\n          get_output(array_index), time,\n          pretty_type::list_of_names<ControlSystems>(), next_trigger);\n    }\n\n    return {next_trigger};\n  }\n};\n\n/// \\cond\ntemplate <typename ControlSystems>\nPUP::able::PUP_ID Trigger<ControlSystems>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n// This metafunction is tested in Test_EventTriggerMetafunctions.cpp\n\n/// \\ingroup ControlSystemGroup\n/// The list of triggers needed for measurements for a list of control\n/// systems.\ntemplate <typename ControlSystems>\nusing control_system_triggers = tmpl::transform<\n    metafunctions::measurements_t<ControlSystems>,\n    tmpl::bind<Trigger, metafunctions::control_systems_with_measurement<\n                            tmpl::pin<ControlSystems>, tmpl::_1>>>;\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/UpdateControlSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/CalculateMeasurementTimescales.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Update.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/ExpirationTimes.hpp\"\n#include \"ControlSystem/IsSize.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"ControlSystem/UpdateTimescaleTuner.hpp\"\n#include \"ControlSystem/WriteData.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace control_system {\n/*!\n * \\brief Functor for updating control systems when they are ready.\n *\n * \\details The apply operator of this struct is meant to be called by the\n * UpdateMessageQueue action once an entire measurement has been made.\n *\n * Requires a few tags to be in the DataBox of the ControlComponent that this\n * is running on:\n * - \\link Tags::Averager Averager \\endlink\n * - \\link Tags::Controller Controller \\endlink\n * - \\link Tags::TimescaleTuner TimescaleTuner \\endlink\n * - \\link Tags::ControlError ControlError \\endlink\n * - \\link Tags::WriteDataToDisk WriteDataToDisk \\endlink\n * - \\link Tags::CurrentNumberOfMeasurements CurrentNumberOfMeasurements\n *   \\endlink\n *\n * And the \\link control_system::Tags::MeasurementsPerUpdate \\endlink must be in\n * the GlobalCache. If these tags are not present, a build error will occur.\n *\n * The algorithm to determine whether or not to update the functions of time is\n * as follows:\n * 1. Ensure this control system is active. If it isn't, end here and don't\n *    process the measurements.\n * 2. Increment the current measurement stored by\n *    `control_system::Tags::CurrentNumberOfMeasurements`. This keeps track of\n *    which measurement we are on out of\n *    `control_system::Tags::MeasurementsPerUpdate`.\n * 3. Calculate the control error and update the averager (store the current\n *    measurement). If the averager doesn't have enough information to determine\n *    the derivatives of the control error, exit now (this usually only happens\n *    for the first couple measurements of a simulation). If this is \\link\n *    control_system::Systems::Size size \\endlink control, an extra step happens\n *    after we calculate the control error, but before we update the averager.\n *    See `control_system::size::update_averager` for this step.\n * 4. If the `control_system::Tags::WriteDataToDisk` tag is set to `true`, write\n *    the time, function of time values, and the control error and its\n *    derivative to disk.\n * 5. Determine if we need to update. We only want to update when the current\n *    measurement is equal to the number of measurements per update. If it's not\n *    time to update, exit now. Once we determine that it is time to update, set\n *    the current measurement back to 0.\n * 6. Compute the control signal using the control error and its derivatives and\n *    update the damping timescales in the TimescaleTuner. If this control\n *    system can suggest a damping timescale, there is an extra step after we\n *    update the damping timescale. See `control_system::update_timescale_tuner`\n *    for this step.\n * 7. Calculate the new measurement timescale based off the updated damping\n *    timescales and the number of measurements per update.\n * 8. Determine the new expiration times for the\n *    `::domain::Tags::FunctionsOfTime` and\n *    `control_system::Tags::MeasurementTimescales`. Call the\n *    `control_system::AggregateUpdate` simple action on the first control\n *    system in the `component_list` of the metavariables. This simple action\n *    will mutate the global cache tags when it has enough data.\n */\ntemplate <typename ControlSystem>\nstruct UpdateControlSystem {\n  static constexpr size_t deriv_order = ControlSystem::deriv_order;\n\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename... TupleTags>\n  static void apply(const gsl::not_null<db::DataBox<DbTags>*> box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const double time,\n                    tuples::TaggedTuple<TupleTags...> data) {\n    const std::string& function_of_time_name = ControlSystem::name();\n\n    // Begin step 1\n    // If this control system isn't active, don't do anything\n    ASSERT(Parallel::get<control_system::Tags::IsActiveMap>(cache).contains(\n               function_of_time_name),\n           \"Couldn't find function of time '\"\n               << function_of_time_name\n               << \"' in the control_system::Tags::IsActiveMap tag\");\n    if (not Parallel::get<control_system::Tags::IsActiveMap>(cache).at(\n            function_of_time_name)) {\n      return;\n    }\n\n    // Begin step 2\n    const int measurements_per_update =\n        get<control_system::Tags::MeasurementsPerUpdate>(cache);\n    const bool delay_update = get<control_system::Tags::DelayUpdate>(cache);\n    int& current_measurement = db::get_mutable_reference<\n        control_system::Tags::CurrentNumberOfMeasurements>(box);\n\n    ++current_measurement;\n\n    const auto& functions_of_time =\n        Parallel::get<::domain::Tags::FunctionsOfTime>(cache);\n    const auto& function_of_time = functions_of_time.at(function_of_time_name);\n\n    // Begin step 3\n    // Get the averager, controller, tuner, and control error from the box\n    auto& averager = db::get_mutable_reference<\n        control_system::Tags::Averager<ControlSystem>>(box);\n    auto& controller = db::get_mutable_reference<\n        control_system::Tags::Controller<ControlSystem>>(box);\n    auto& tuner = db::get_mutable_reference<\n        control_system::Tags::TimescaleTuner<ControlSystem>>(box);\n    auto& control_error = db::get_mutable_reference<\n        control_system::Tags::ControlError<ControlSystem>>(box);\n    const DataVector& current_timescale = tuner.current_timescale();\n\n    // Compute control error\n    const DataVector Q =\n        control_error(tuner, cache, time, function_of_time_name, data);\n\n    if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n      Parallel::printf(\n          \"%s, time = %.16f: current measurement = %d, control error = %s\\n\",\n          function_of_time_name, time, current_measurement, Q);\n    }\n\n    if constexpr (size::is_size_v<ControlSystem>) {\n      // This function must be called after we update the control error because\n      // it uses the new state in its logic (to repopulate the averager).\n      size::update_averager(make_not_null(&averager),\n                            make_not_null(&control_error), cache, time,\n                            tuner.current_timescale(), function_of_time_name,\n                            current_measurement);\n    }\n\n    // Update the averager. We do this for every measurement because we still\n    // want the averager to be up-to-date even if we aren't updating at this\n    // time\n    averager.update(time, Q, current_timescale);\n\n    // Get the averaged values of the control error and its derivatives\n    const auto& opt_avg_values = averager(time);\n\n    if (not opt_avg_values.has_value()) {\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\n            \"%s, time = %.16f: current measurement = %d, Averager does not \"\n            \"have enough data.\\n\",\n            function_of_time_name, time, current_measurement);\n      }\n      return;\n    }\n\n    // Begin step 4\n    // Write data for every measurement\n    std::array<DataVector, 2> q_and_dtq{{Q, {Q.size(), 0.0}}};\n    q_and_dtq[0] = (*opt_avg_values)[0];\n    if constexpr (deriv_order > 1) {\n      q_and_dtq[1] = (*opt_avg_values)[1];\n    }\n\n    if (Parallel::get<control_system::Tags::WriteDataToDisk>(cache)) {\n      // LCOV_EXCL_START\n      write_components_to_disk<ControlSystem>(time, cache, function_of_time,\n                                              q_and_dtq, current_timescale);\n      // LCOV_EXCL_STOP\n    }\n\n    // Begin step 5\n    // Check if it is time to update\n    if (current_measurement != measurements_per_update) {\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n        Parallel::printf(\n            \"%s, time = %.16f: current measurement = %d is not at \"\n            \"measurements_per_update = %d. Waiting for more data.\\n\",\n            function_of_time_name, time, current_measurement,\n            measurements_per_update);\n      }\n      return;\n    }\n\n    // Set current measurement back to 0 because we're updating now\n    current_measurement = 0;\n\n    // Begin step 6\n    const double time_offset =\n        averager.last_time_updated() - averager.average_time(time);\n    const double time_offset_0th =\n        averager.using_average_0th_deriv_of_q() ? time_offset : 0.0;\n\n    // Calculate the control signal which will be used to update the highest\n    // derivative of the FunctionOfTime\n    const DataVector control_signal =\n        controller(time, current_timescale, opt_avg_values.value(),\n                   time_offset_0th, time_offset);\n\n    tuner.update_timescale(q_and_dtq);\n\n    update_timescale_tuner(make_not_null(&tuner), make_not_null(&control_error),\n                           Parallel::get<Tags::Verbosity>(cache), time,\n                           function_of_time_name);\n\n    // Begin step 7\n    // Calculate new measurement timescales with updated damping timescales\n    const DataVector new_measurement_timescale =\n        calculate_measurement_timescales(controller, tuner,\n                                         measurements_per_update);\n\n    const auto& measurement_timescales =\n        Parallel::get<Tags::MeasurementTimescales>(cache);\n    const auto& system_to_combined_names =\n        Parallel::get<Tags::SystemToCombinedNames>(cache);\n    const auto& measurement_timescale = measurement_timescales.at(\n        system_to_combined_names.at(function_of_time_name));\n    const double current_fot_expiration_time =\n        function_of_time->time_bounds()[1];\n    const double current_measurement_expiration_time =\n        measurement_timescale->time_bounds()[1];\n    const DataVector old_measurement_timescale =\n        measurement_timescale->func(current_measurement_expiration_time)[0];\n\n    // Begin step 8\n    // Calculate the next expiration times for both the functions of time and\n    // the measurement timescales based on the current time. Then, actually\n    // update the functions of time and measurement timescales\n    const double new_fot_expiration_time = function_of_time_expiration_time(\n        time, old_measurement_timescale, new_measurement_timescale,\n        measurements_per_update, delay_update);\n\n    const double new_measurement_expiration_time = measurement_expiration_time(\n        time, old_measurement_timescale, new_measurement_timescale,\n        measurements_per_update, delay_update);\n\n    if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Verbose) {\n      Parallel::printf(\"%s, time = %.16f: Control signal = %s\\n\",\n                       function_of_time_name, time, control_signal);\n      Parallel::printf(\n          \"%s, time = %.16f: Updating the functions of time.\\n\"\n          \" min(old_measure_timescale) = %.16f\\n\"\n          \" min(new_measure_timescale) = %.16f\\n\"\n          \" old_measure_expr_time = %.16f\\n\"\n          \" new_measure_expr_time = %.16f\\n\"\n          \" old_function_expr_time = %.16f\\n\"\n          \" new_function_expr_time = %.16f\\n\",\n          function_of_time_name, time, min(old_measurement_timescale),\n          min(new_measurement_timescale), current_measurement_expiration_time,\n          new_measurement_expiration_time, current_fot_expiration_time,\n          new_fot_expiration_time);\n    }\n\n    using first_control_component =\n        tmpl::front<metafunctions::all_control_components<Metavariables>>;\n\n    auto& first_control_system_proxy =\n        Parallel::get_parallel_component<first_control_component>(cache);\n\n    Parallel::simple_action<AggregateUpdate<ControlSystem>>(\n        first_control_system_proxy, new_measurement_timescale,\n        current_measurement_expiration_time, new_measurement_expiration_time,\n        control_signal, current_fot_expiration_time, new_fot_expiration_time);\n  }\n};\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/UpdateFunctionOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace control_system {\nvoid UpdateSingleFunctionOfTime::apply(\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        f_of_t_list,\n    const std::string& f_of_t_name, const double update_time,\n    DataVector update_deriv, const double new_expiration_time) {\n  (*f_of_t_list)\n      .at(f_of_t_name)\n      ->update(update_time, std::move(update_deriv), new_expiration_time);\n}\n\nvoid UpdateMultipleFunctionsOfTime::apply(\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        f_of_t_list,\n    const double update_time,\n    const std::unordered_map<std::string, std::pair<DataVector, double>>&\n        update_args) {\n  for (auto& [f_of_t_name, update_deriv_and_expr_time] : update_args) {\n    UpdateSingleFunctionOfTime::apply(f_of_t_list, f_of_t_name, update_time,\n                                      update_deriv_and_expr_time.first,\n                                      update_deriv_and_expr_time.second);\n  }\n}\n\nUpdateAggregator::UpdateAggregator(\n    std::string combined_name,\n    std::unordered_set<std::string> active_control_system_names)\n    : active_names_(std::move(active_control_system_names)),\n      combined_name_(std::move(combined_name)) {}\n\nvoid UpdateAggregator::insert(const std::string& control_system_name,\n                              const DataVector& new_measurement_timescale,\n                              const double new_measurement_expiration_time,\n                              DataVector control_signal,\n                              const double new_fot_expiration_time) {\n  ASSERT(expiration_times_.count(control_system_name) == 0,\n         \"Already received expiration time data for control system '\"\n             << control_system_name << \"'.\");\n  ASSERT(active_names_.count(control_system_name) == 1,\n         \"Received expiration time data for a non-active control system '\"\n             << control_system_name << \"'. Active control systems are \"\n             << active_names_);\n\n  expiration_times_[control_system_name] = std::make_pair(\n      std::make_pair(std::move(control_signal), new_fot_expiration_time),\n      std::make_pair(min(new_measurement_timescale),\n                     new_measurement_expiration_time));\n}\n\nbool UpdateAggregator::is_ready(\n    const std::unordered_map<std::string, bool>& active_map) const {\n  // Short circuit if one name isn't ready\n  for (const std::string& control_system_name : active_names_) {\n    if (not active_map.at(control_system_name)) {\n      continue;\n    }\n    if (expiration_times_.count(control_system_name) != 1) {\n      return false;\n    }\n  }\n  return true;\n}\n\nconst std::string& UpdateAggregator::combined_name() const {\n  return combined_name_;\n}\n\nstd::unordered_map<std::string, std::pair<DataVector, double>>\nUpdateAggregator::combined_fot_expiration_times(\n    const std::unordered_map<std::string, bool>& active_map) const {\n  ASSERT(is_ready(active_map),\n         \"Trying to get combined expiration times, but have not received \"\n         \"data from all control systems.\");\n\n  std::unordered_map<std::string, std::pair<DataVector, double>> result{};\n\n  double min_expiration_time = std::numeric_limits<double>::infinity();\n  for (const auto& control_system_name : active_names_) {\n    if (not active_map.at(control_system_name)) {\n      continue;\n    }\n    min_expiration_time =\n        std::min(min_expiration_time,\n                 expiration_times_.at(control_system_name).first.second);\n  }\n\n  for (const auto& control_system_name : active_names_) {\n    if (not active_map.at(control_system_name)) {\n      continue;\n    }\n    result[control_system_name] =\n        expiration_times_.at(control_system_name).first;\n    result[control_system_name].second = min_expiration_time;\n  }\n\n  return result;\n}\n\nstd::pair<double, double>\nUpdateAggregator::combined_measurement_expiration_time(\n    const std::unordered_map<std::string, bool>& active_map) {\n  ASSERT(is_ready(active_map),\n         \"Trying to get combined expiration times, but have not received \"\n         \"data from all control systems.\");\n\n  double min_measurement_timescale = std::numeric_limits<double>::infinity();\n  double min_expiration_time = std::numeric_limits<double>::infinity();\n\n  for (const auto& control_system_name : active_names_) {\n    if (not active_map.at(control_system_name)) {\n      continue;\n    }\n    min_measurement_timescale =\n        std::min(min_measurement_timescale,\n                 expiration_times_[control_system_name].second.first);\n    min_expiration_time =\n        std::min(min_expiration_time,\n                 expiration_times_[control_system_name].second.second);\n  }\n\n  expiration_times_.clear();\n\n  return std::make_pair(min_measurement_timescale, min_expiration_time);\n}\n\nvoid UpdateAggregator::pup(PUP::er& p) {\n  p | expiration_times_;\n  p | active_names_;\n  p | combined_name_;\n}\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/UpdateFunctionOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace control_system::Tags {\nstruct UpdateAggregators;\nstruct SystemToCombinedNames;\nstruct MeasurementTimescales;\n}  // namespace control_system::Tags\n/// \\endcond\n\nnamespace control_system {\n/// \\ingroup ControlSystemGroup\n/// Updates a FunctionOfTime in the global cache. Intended to be used in\n/// Parallel::mutate.\nstruct UpdateSingleFunctionOfTime {\n  static void apply(\n      gsl::not_null<std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n          f_of_t_list,\n      const std::string& f_of_t_name, double update_time,\n      DataVector update_deriv, double new_expiration_time);\n};\n\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Updates several FunctionOfTimes in the global cache at once. Intended\n * to be used in Parallel::mutate.\n *\n * \\details All functions of time are updated at the same `update_time`. For the\n * `update_args`, the keys of the map are the names of the functions of time.\n * The value `std::pair<DataVector, double>` for each key is the updated\n * derivative for the function of time and the new expiration time,\n * respectively.\n */\nstruct UpdateMultipleFunctionsOfTime {\n  static void apply(\n      gsl::not_null<std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n          f_of_t_list,\n      double update_time,\n      const std::unordered_map<std::string, std::pair<DataVector, double>>&\n          update_args);\n};\n\n/*!\n * \\brief A class for collecting and storing information related to updating\n * functions of time and measurement timescales.\n *\n * \\details This class also determines if enough data has been received in order\n * for the functions of time and measurement timescales to be updated. There\n * should be one `UpdateAggregator` for every group of control systems that have\n * the same `control_system::protocols::Measurement`.\n */\nstruct UpdateAggregator {\n  UpdateAggregator() = default;\n\n  /*!\n   * \\brief Construct a new UpdateAggregator using a set of active control\n   * system names and the combined name for the measurement.\n   *\n   * It is expected that all the control systems in this set use the same\n   * `control_system::protocols::Measurement`.\n   */\n  UpdateAggregator(std::string combined_name,\n                   std::unordered_set<std::string> active_control_system_names);\n\n  /*!\n   * \\brief Inserts and stores information for one of the control systems that\n   * this class was constructed with.\n   *\n   * \\param control_system_name Name of control system to add information for\n   * \\param new_measurement_timescale DataVector of new measurement timescales\n   * calculated from a control system update.\n   * \\param new_measurement_expiration_time New measurement expiration time\n   * calculated during that update.\n   * \\param control_signal New highest derivative for the function of time\n   * calculated during that update (will be `std::move`ed).\n   * \\param new_fot_expiration_time New function of time expiration time\n   * calculated for during that update\n   */\n  void insert(const std::string& control_system_name,\n              const DataVector& new_measurement_timescale,\n              double new_measurement_expiration_time, DataVector control_signal,\n              double new_fot_expiration_time);\n\n  /*!\n   * \\brief Checks if `insert` has been called for all control systems that this\n   * class was constructed with.\n   *\n   * The `active_map` is used to inform the aggregator which controls systems\n   * are currently active. This is separate so that individual control\n   * systems can be enabled and disabled during an evolution.\n   */\n  bool is_ready(const std::unordered_map<std::string, bool>& active_map) const;\n\n  /*!\n   * \\brief Returns a sorted concatenation of the control system names this\n   * class was constructed with.\n   */\n  const std::string& combined_name() const;\n\n  /*!\n   * \\brief Once `is_ready` is true, returns a map between the control system\n   * name and a `std::pair` containing the `control_signal` that was passed to\n   * `insert` and the minimum of all the `new_fot_expiration_time`s passed to\n   * `insert`.\n   *\n   * \\details This function is expected to only be called when `is_ready` is\n   * true. It also must be called before `combined_measurement_expiration_time`.\n   *\n   * The `active_map` is used to inform the aggregator which controls systems\n   * are currently active. This is separate so that individual controls\n   * systems can be enabled and disabled during an evolution.\n   */\n  std::unordered_map<std::string, std::pair<DataVector, double>>\n  combined_fot_expiration_times(\n      const std::unordered_map<std::string, bool>& active_map) const;\n\n  /*!\n   * \\brief Once `is_ready` is true, returns a `std::pair` containing the\n   * minimum of all `new_measurement_timescale`s passed to `insert` and the\n   * minimum of all `new_measurement_expiration_time`s passed to `insert`.\n   *\n   * \\details This function is expected to be called only when `is_ready` is\n   * true and only a single time once all control active control\n   * systems for this measurement have computed their update values. It also\n   * must be called after `combined_fot_expiration_times`. This function clears\n   * all stored data when it is called.\n   *\n   * The `active_map` is used to inform the aggregator which controls systems\n   * are currently active. This is separate so that individual controls\n   * systems can be enabled and disabled during an evolution.\n   */\n  std::pair<double, double> combined_measurement_expiration_time(\n      const std::unordered_map<std::string, bool>& active_map);\n\n  /// \\cond\n  void pup(PUP::er& p);\n  /// \\endcond\n\n private:\n  std::unordered_map<std::string, std::pair<std::pair<DataVector, double>,\n                                            std::pair<double, double>>>\n      expiration_times_{};\n  std::unordered_set<std::string> active_names_{};\n  std::string combined_name_{};\n};\n\n/*!\n * \\brief Simple action that updates the appropriate `UpdateAggregator` for the\n * templated `ControlSystem`.\n *\n * \\details The `new_measurement_timescale`, `new_measurement_expiration_time`,\n * `control_signal`, and `new_fot_expiration_time` are passed along to the\n * `UpdateAggregator::insert` function. The `old_measurement_expiration_time`\n * and `old_fot_expiration_time` are only used when the\n * `UpdateAggregator::is_ready` in order to update the functions of time and the\n * measurement timescale.\n *\n * When the `UpdateAggregator::is_ready`, the measurement timescale is mutated\n * with `UpdateSingleFunctionOfTime` and the functions of time are mutated with\n * `UpdateMultipleFunctionsOfTime`, both using `Parallel::mutate`.\n *\n * The \"appropriate\" `UpdateAggregator` is chosen from the\n * `control_system::Tags::SystemToCombinedNames` for the templated\n * `ControlSystem`.\n */\ntemplate <typename ControlSystem>\nstruct AggregateUpdate {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const DataVector& new_measurement_timescale,\n                    const double old_measurement_expiration_time,\n                    const double new_measurement_expiration_time,\n                    DataVector control_signal,\n                    const double old_fot_expiration_time,\n                    const double new_fot_expiration_time) {\n    auto& aggregators =\n        db::get_mutable_reference<Tags::UpdateAggregators>(make_not_null(&box));\n    const auto& system_to_combined_names =\n        Parallel::get<Tags::SystemToCombinedNames>(cache);\n    const std::string& control_system_name = ControlSystem::name();\n    ASSERT(system_to_combined_names.count(control_system_name) == 1,\n           \"Expected name '\" << control_system_name\n                             << \"' to be in map of system-to-combined names, \"\n                                \"but it wasn't. Keys are: \"\n                             << keys_of(system_to_combined_names));\n    const std::string& combined_name =\n        system_to_combined_names.at(control_system_name);\n    ASSERT(aggregators.count(combined_name) == 1,\n           \"Expected combined name '\" << combined_name\n                                      << \"' to be in map of aggregators, \"\n                                         \"but it wasn't. Keys are: \"\n                                      << keys_of(aggregators));\n\n    UpdateAggregator& aggregator = aggregators.at(combined_name);\n\n    aggregator.insert(control_system_name, new_measurement_timescale,\n                      new_measurement_expiration_time,\n                      std::move(control_signal), new_fot_expiration_time);\n\n    if (aggregator.is_ready(get<Tags::IsActiveMap>(cache))) {\n      std::unordered_map<std::string, std::pair<DataVector, double>>\n          combined_fot_expiration_times =\n              aggregator.combined_fot_expiration_times(\n                  get<Tags::IsActiveMap>(cache));\n      const std::pair<double, double> combined_measurement_expiration_time =\n          aggregator.combined_measurement_expiration_time(\n              get<Tags::IsActiveMap>(cache));\n\n      Parallel::mutate<Tags::MeasurementTimescales, UpdateSingleFunctionOfTime>(\n          cache, combined_name, old_measurement_expiration_time,\n          DataVector{1, combined_measurement_expiration_time.first},\n          combined_measurement_expiration_time.second);\n\n      Parallel::mutate<::domain::Tags::FunctionsOfTime,\n                       UpdateMultipleFunctionsOfTime>(\n          cache, old_fot_expiration_time,\n          std::move(combined_fot_expiration_times));\n    }\n  }\n};\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/UpdateTimescaleTuner.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <string>\n\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace control_system {\n/*!\n * \\brief Updates the TimescaleTuner with information from the ControlError, if\n * possible.\n *\n * \\details We check for a suggested timescale from the ControlError. If one is\n * suggested and it is smaller than the current damping timescale, we set the\n * timescale in the TimescaleTuner to this suggested value. Otherwise, we let\n * the TimescaleTuner adjust the timescale. Regardless of whether a timescale\n * was suggested or not, we always reset the control error.\n */\ntemplate <bool AllowDecrease, typename ControlError>\nvoid update_timescale_tuner(\n    const gsl::not_null<TimescaleTuner<AllowDecrease>*> tuner,\n    const gsl::not_null<ControlError*> control_error, ::Verbosity verbosity,\n    const double time, const std::string& function_of_time_name) {\n  static_assert(\n      tt::assert_conforms_to_v<ControlError, protocols::ControlError>);\n\n  const std::optional<double>& suggested_timescale =\n      control_error->get_suggested_timescale();\n  const double old_timescale = min(tuner->current_timescale());\n\n  if (suggested_timescale.value_or(std::numeric_limits<double>::infinity()) <\n      old_timescale) {\n    tuner->set_timescale_if_in_allowable_range(suggested_timescale.value());\n  }\n\n  if (verbosity >= ::Verbosity::Verbose) {\n    using ::operator<<;\n    Parallel::printf(\n        \"%s, time = %.16f:\\n\"\n        \" old_timescale = %.16f\\n\"\n        \" suggested_timescale = %s\\n\"\n        \" new_timescale = %.16f\\n\",\n        function_of_time_name, time, old_timescale,\n        MakeString{} << suggested_timescale, min(tuner->current_timescale()));\n  }\n\n  // This reset call is ok because if there was a discontinuous change\n  // above after calculating the control error, the control error class was\n  // already reset so this reset won't do anything. If there wasn't a\n  // discontinuous change, then this will only affect the suggested\n  // timescale, which we always want to reset.\n  control_error->reset();\n}\n}  // namespace control_system\n"
  },
  {
    "path": "src/ControlSystem/WriteData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n\nnamespace control_system {\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Writes all components of a function of time to disk at a specific time\n * from a control system after it updates the functions of time.\n *\n * \\details The columns of data written are:\n * - %Time\n * - FunctionOfTime\n * - dtFunctionOfTime\n * - d2tFunctionOfTime\n * - ControlError\n * - dtControlError\n * - DampingTimescale\n *\n * Data will be stored in the reduction file. All subfiles for the control\n * system within the H5 file will be under the group \"/ControlSystems\".\n * Within this group, there will be one group for each control system. The name\n * of each group will be the result of the `name()` function from each control\n * system. An example would look like\n *\n * - /ControlSystems/SystemA\n * - /ControlSystems/SystemB\n * - /ControlSystems/SystemC\n *\n * Then, within each system group, there will be one subfile for each component\n * of the function of time that is being controlled. The name of this subfile is\n * the name of the component. The name of each component will be the result of\n * the `component_name(i)` function from the control system, where `i` is the\n * index of the component. For example, if \"SystemA\" has 3 components with names\n * \"X\", \"Y\", and \"Z\", then the subfiles would look like\n *\n * - /ControlSystems/SystemA/X.dat\n * - /ControlSystems/SystemA/Y.dat\n * - /ControlSystems/SystemA/Z.dat\n */\ntemplate <typename ControlSystem, typename Metavariables>\nvoid write_components_to_disk(\n    const double time, Parallel::GlobalCache<Metavariables>& cache,\n    const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>&\n        function_of_time,\n    const std::array<DataVector, 2>& q_and_dtq, const DataVector& timescales) {\n  auto& observer_writer_proxy = Parallel::get_parallel_component<\n      observers::ObserverWriter<Metavariables>>(cache);\n\n  constexpr size_t deriv_order = ControlSystem::deriv_order;\n  std::array<DataVector, 3> function_at_current_time{};\n  const auto* const quat_func_of_time = dynamic_cast<\n      const domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>*>(\n      function_of_time.get());\n  if (quat_func_of_time == nullptr) {\n    // Just call the usual `func_and_2_derivs` member.\n    function_at_current_time = function_of_time->func_and_2_derivs(time);\n  } else {\n    // If we are working with a QuaternionFunctionOfTime, we aren't actually\n    // controlling a quaternion. We are controlling a small change in angle\n    // associated with the angular velocity in each direction. Because of this,\n    // we want to write the component data of the thing we are actually\n    // controlling. This is accessed by the `angle_func_and_2_derivs` member of\n    // a QuaternionFunctionOfTime. Since `angle_func_and_2_derivs` is not a\n    // virtual function of the FunctionOfTime base class, we need to down cast\n    // the original function to a QuaternionFunctionOfTime.\n    function_at_current_time = quat_func_of_time->angle_func_and_2_derivs(time);\n  }\n\n  // Each control system is its own group under the overarching `ControlSystems`\n  // group. There is a different subfile for each component, so loop over them.\n  // Eg. translation files will look like\n  //\n  // ControlSystems/Translation/X.dat\n  // ControlSystems/Translation/Y.dat\n  // ControlSystems/Translation/Z.dat\n  const size_t num_components = function_at_current_time[0].size();\n  for (size_t i = 0; i < num_components; ++i) {\n    const std::optional<std::string> component_name_opt =\n        ControlSystem::component_name(i, num_components);\n    if (not component_name_opt) {\n      continue;\n    }\n    // Currently all reduction data is written to the reduction file so preface\n    // everything with ControlSystems/\n    const std::string subfile_name{\"/ControlSystems/\" + ControlSystem::name() +\n                                   \"/\" + *component_name_opt};\n    std::vector<std::string> legend{\"Time\",\n                                    \"FunctionOfTime\",\n                                    \"dtFunctionOfTime\",\n                                    \"d2tFunctionOfTime\",\n                                    \"ControlError\",\n                                    \"dtControlError\",\n                                    \"DampingTimescale\"};\n\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        // Node 0 is always the writer\n        observer_writer_proxy[0], subfile_name, std::move(legend),\n        std::make_tuple(\n            // clang-format off\n            time,\n            function_at_current_time[0][i],\n            function_at_current_time[1][i],\n            function_at_current_time[2][i],\n            q_and_dtq[0][i],\n            q_and_dtq[1][i],\n            timescales[i])\n        // clang-format on\n    );\n  }\n}\n}  // namespace control_system\n"
  },
  {
    "path": "src/DataStructures/ApplyMatrices.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace {\nvoid multiply_in_first_dimension(const gsl::not_null<double*> result,\n                                 const gsl::not_null<size_t*> data_size,\n                                 const Matrix& matrix, const double* data) {\n  *data_size /= matrix.columns();\n  dgemm_<true>('N', 'N',\n               matrix.rows(),     // rows of matrix and result\n               *data_size,        // columns of result and u\n               matrix.columns(),  // columns of matrix and rows of u\n               1.0,               // overall multiplier\n               matrix.data(),     // matrix\n               matrix.spacing(),  // rows of matrix including padding\n               data,              // u\n               matrix.columns(),  // rows of u\n               0.0,               // multiplier for unused term\n               result.get(),      // result\n               matrix.rows());    // rows of result\n  *data_size *= matrix.rows();\n}\n\nvoid do_transpose(const gsl::not_null<double*> result, const double* const data,\n                  const size_t data_size, const size_t chunk_size) {\n  raw_transpose(result, data, chunk_size, data_size / chunk_size);\n}\n\nstruct Scratch {\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  std::unique_ptr<double[]> buffer;\n  double* a;\n  double* b;\n};\n\n// This does not take into account the order that the matrices are\n// applied in and gives the largest amount of space that could be\n// required for any application order.\ntemplate <typename MatrixType, size_t Dim>\nScratch get_scratch(const std::array<MatrixType, Dim>& matrices,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n  size_t size = number_of_independent_components;\n  for (size_t i = 0; i < Dim; ++i) {\n    const auto& matrix = gsl::at(matrices, i);\n    if (dereference_wrapper(matrix) == Matrix{}) {\n      size *= extents[i];\n    } else {\n      size *= std::max(dereference_wrapper(matrix).rows(),\n                       dereference_wrapper(matrix).columns());\n    }\n  }\n  Scratch result{};\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  result.buffer = cpp20::make_unique_for_overwrite<double[]>(2 * size);\n  result.a = &result.buffer[0];\n  result.b = &result.buffer[size];\n  return result;\n}\n\n// Produce the array of the number of rows in each matrix.  Empty\n// matrices are processed as if they were identity matrices.\ntemplate <size_t Dim, typename MatrixType>\nstd::array<size_t, Dim> matrix_rows(const std::array<MatrixType, Dim>& matrices,\n                                    const Index<Dim>& extents) {\n  std::array<size_t, Dim> result{};\n  for (size_t i = 0; i < Dim; ++i) {\n    if (dereference_wrapper(gsl::at(matrices, i)) == Matrix{}) {\n      gsl::at(result, i) = extents[i];\n    } else {\n      gsl::at(result, i) = dereference_wrapper(gsl::at(matrices, i)).rows();\n    }\n  }\n  return result;\n}\n}  // namespace\n\nnamespace apply_matrices_detail {\ntemplate <typename ElementType, size_t Dim, bool... DimensionIsIdentity>\ntemplate <typename MatrixType>\nvoid Impl<ElementType, Dim, DimensionIsIdentity...>::apply(\n    const gsl::not_null<ElementType*> result,\n    const std::array<MatrixType, Dim>& matrices, const ElementType* const data,\n    const Index<Dim>& extents, const size_t number_of_independent_components) {\n  if (dereference_wrapper(matrices[sizeof...(DimensionIsIdentity)]) ==\n      Matrix{}) {\n    Impl<ElementType, Dim, DimensionIsIdentity..., true>::apply(\n        result, matrices, data, extents, number_of_independent_components);\n  } else {\n    Impl<ElementType, Dim, DimensionIsIdentity..., false>::apply(\n        result, matrices, data, extents, number_of_independent_components);\n  }\n}\n\ntemplate <typename ElementType>\nstruct Impl<ElementType, 0> {\n  static constexpr const size_t Dim = 0;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<ElementType*> result,\n                    const std::array<MatrixType, Dim>& /*matrices*/,\n                    const ElementType* const data,\n                    const Index<Dim>& /*extents*/,\n                    const size_t number_of_independent_components) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    std::copy(data, data + number_of_independent_components, result.get());\n  }\n};\n\ntemplate <>\nstruct Impl<double, 1, false> {\n  static constexpr const size_t Dim = 1;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    size_t data_size = number_of_independent_components * extents.product();\n    multiply_in_first_dimension(result, &data_size, matrices[0], data);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 1, false> {\n  static constexpr const size_t Dim = 1;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.a, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    multiply_in_first_dimension(scratch.b, &data_size, matrices[0], scratch.a);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.b, data_size, data_size / 2);\n  }\n};\n\ntemplate <typename ElementType>\nstruct Impl<ElementType, 1, true> {\n  static constexpr const size_t Dim = 1;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<ElementType*> result,\n                    const std::array<MatrixType, Dim>& /*matrices*/,\n                    const ElementType* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    std::copy(data, data + number_of_independent_components * extents.product(),\n              result.get());\n  }\n};\n\ntemplate <>\nstruct Impl<double, 2, false, false> {\n  static constexpr size_t Dim = 2;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components);\n\n    size_t data_size = number_of_independent_components * extents.product();\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[0], data);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    do_transpose(result, scratch.a, data_size,\n                 number_of_independent_components * rows[1]);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 2, false, false> {\n  static constexpr size_t Dim = 2;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.a, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    multiply_in_first_dimension(scratch.b, &data_size, matrices[0], scratch.a);\n    do_transpose(scratch.a, scratch.b, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.b, &data_size, matrices[1], scratch.a);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.b, data_size,\n                 number_of_independent_components * rows[1]);\n  }\n};\n\ntemplate <>\nstruct Impl<double, 2, false, true> {\n  static constexpr size_t Dim = 2;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    size_t data_size = number_of_independent_components * extents.product();\n    multiply_in_first_dimension(result, &data_size, matrices[0], data);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 2, false, true> {\n  static constexpr size_t Dim = 2;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.b, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[0], scratch.b);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.a, data_size, data_size / 2);\n  }\n};\n\ntemplate <>\nstruct Impl<double, 2, true, false> {\n  static constexpr size_t Dim = 2;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components);\n\n    size_t data_size = number_of_independent_components * extents.product();\n    do_transpose(scratch.b, data, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    do_transpose(result, scratch.a, data_size,\n                 number_of_independent_components * rows[1]);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 2, true, false> {\n  static constexpr size_t Dim = 2;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.a, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.a, data_size,\n                 number_of_independent_components * rows[1]);\n  }\n};\n\ntemplate <typename ElementType>\nstruct Impl<ElementType, 2, true, true> {\n  static constexpr size_t Dim = 2;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<ElementType*> result,\n                    const std::array<MatrixType, Dim>& /*matrices*/,\n                    const ElementType* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    std::copy(data, data + number_of_independent_components * extents.product(),\n              result.get());\n  }\n};\n\ntemplate <>\nstruct Impl<double, 3, false, false, false> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components);\n\n    size_t data_size = number_of_independent_components * extents.product();\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[0], data);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    do_transpose(scratch.b, scratch.a, data_size, rows[1]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[2], scratch.b);\n    do_transpose(result, scratch.a, data_size,\n                 number_of_independent_components * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 3, false, false, false> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.b, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[0], scratch.b);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    do_transpose(scratch.b, scratch.a, data_size, rows[1]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[2], scratch.b);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.a, data_size,\n                 number_of_independent_components * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<double, 3, false, false, true> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components);\n\n    size_t data_size = number_of_independent_components * extents.product();\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[0], data);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    do_transpose(result, scratch.a, data_size,\n                 number_of_independent_components * rows[1] * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 3, false, false, true> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.b, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[0], scratch.b);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.a, data_size,\n                 number_of_independent_components * rows[1] * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<double, 3, false, true, false> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components);\n\n    size_t data_size = number_of_independent_components * extents.product();\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[0], data);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0] * rows[1]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[2], scratch.b);\n    do_transpose(result, scratch.a, data_size,\n                 number_of_independent_components * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 3, false, true, false> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.b, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[0], scratch.b);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0] * rows[1]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[2], scratch.b);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.a, data_size,\n                 number_of_independent_components * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<double, 3, false, true, true> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    size_t data_size = number_of_independent_components * extents.product();\n    multiply_in_first_dimension(result, &data_size, matrices[0], data);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 3, false, true, true> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.a, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    multiply_in_first_dimension(scratch.b, &data_size, matrices[0], scratch.a);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.b, data_size, data_size / 2);\n  }\n};\n\ntemplate <>\nstruct Impl<double, 3, true, false, false> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components);\n\n    size_t data_size = number_of_independent_components * extents.product();\n    do_transpose(scratch.b, data, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    do_transpose(scratch.b, scratch.a, data_size, rows[1]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[2], scratch.b);\n    do_transpose(result, scratch.a, data_size,\n                 number_of_independent_components * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 3, true, false, false> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.a, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    do_transpose(scratch.b, scratch.a, data_size, rows[1]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[2], scratch.b);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.a, data_size,\n                 number_of_independent_components * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<double, 3, true, false, true> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components);\n\n    size_t data_size = number_of_independent_components * extents.product();\n    do_transpose(scratch.b, data, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    do_transpose(result, scratch.a, data_size,\n                 number_of_independent_components * rows[1] * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 3, true, false, true> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.a, reinterpret_cast<const double* const>(data),\n                 data_size, 2);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[1], scratch.b);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.a, data_size,\n                 number_of_independent_components * rows[1] * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<double, 3, true, true, false> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<double*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const double* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components);\n\n    size_t data_size = number_of_independent_components * extents.product();\n    do_transpose(scratch.b, data, data_size, rows[0] * rows[1]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[2], scratch.b);\n    do_transpose(result, scratch.a, data_size,\n                 number_of_independent_components * rows[2]);\n  }\n};\n\ntemplate <>\nstruct Impl<std::complex<double>, 3, true, true, false> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<std::complex<double>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const std::complex<double>* const data,\n                    const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    const auto rows = matrix_rows(matrices, extents);\n    auto scratch =\n        get_scratch(matrices, extents, number_of_independent_components * 2);\n\n    // complex values will be treated as pairs of doubles for the LAPACK call,\n    // as we will typically be applying a real matrix to a complex vector. To\n    // treat the complex values as an additional 'dimension' to transpose, a\n    // reinterpret cast is required.\n    size_t data_size = number_of_independent_components * extents.product() * 2;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(scratch.a, reinterpret_cast<const double*>(data), data_size,\n                 2);\n    do_transpose(scratch.b, scratch.a, data_size, rows[0] * rows[1]);\n    multiply_in_first_dimension(scratch.a, &data_size, matrices[2], scratch.b);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    do_transpose(make_not_null(reinterpret_cast<double*>(result.get())),\n                 scratch.a, data_size,\n                 number_of_independent_components * rows[2]);\n  }\n};\n\ntemplate <typename ElementType>\nstruct Impl<ElementType, 3, true, true, true> {\n  static constexpr size_t Dim = 3;\n  template <typename MatrixType>\n  static void apply(const gsl::not_null<ElementType*> result,\n                    const std::array<MatrixType, Dim>& /*matrices*/,\n                    const ElementType* const data, const Index<Dim>& extents,\n                    const size_t number_of_independent_components) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    std::copy(data, data + number_of_independent_components * extents.product(),\n              result.get());\n  }\n};\n\n#define ELEMENTTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define MATRIX(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define INSTANTIATE(_, data)                               \\\n  template void Impl<ELEMENTTYPE(data), DIM(data)>::apply( \\\n      const gsl::not_null<ELEMENTTYPE(data)*>,             \\\n      const std::array<MATRIX(data), DIM(data)>&,          \\\n      const ELEMENTTYPE(data)* const, const Index<DIM(data)>&, const size_t);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, std::complex<double>),\n                        (0, 1, 2, 3),\n                        (Matrix, std::reference_wrapper<const Matrix>))\n\n#undef DIM\n#undef MATRIX\n#undef ELEMENTTYPE\n#undef INSTANTIATE\n}  // namespace apply_matrices_detail\n"
  },
  {
    "path": "src/DataStructures/ApplyMatrices.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <ostream>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Index;\n\n/// \\endcond\n\nnamespace apply_matrices_detail {\ntemplate <typename ElementType, size_t Dim, bool... DimensionIsIdentity>\nstruct Impl {\n  template <typename MatrixType>\n  static void apply(gsl::not_null<ElementType*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const ElementType* data, const Index<Dim>& extents,\n                    size_t number_of_independent_components);\n};\n\ntemplate <typename MatrixType, size_t Dim>\nsize_t result_size(const std::array<MatrixType, Dim>& matrices,\n                   const Index<Dim>& extents) {\n  size_t num_points_result = 1;\n  for (size_t d = 0; d < Dim; ++d) {\n    const size_t cols = dereference_wrapper(gsl::at(matrices, d)).columns();\n    if (cols == 0) {\n      // An empty matrix is treated as the identity.\n      num_points_result *= extents[d];\n    } else {\n      ASSERT(cols == extents[d],\n             \"Matrix \" << d << \" has wrong number of columns: \" << cols\n                       << \" (expected \" << extents[d] << \")\");\n      num_points_result *= dereference_wrapper(gsl::at(matrices, d)).rows();\n    }\n  }\n  return num_points_result;\n}\n}  // namespace apply_matrices_detail\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Multiply by matrices in each dimension\n///\n/// Multiplies each stripe in the first dimension of `u` by\n/// `matrices[0]`, each stripe in the second dimension of `u` by\n/// `matrices[1]`, and so on.  If any of the matrices are empty they\n/// will be treated as the identity, but the matrix multiplications\n/// will be skipped for increased efficiency.\n///\n/// \\note The element type stored in the vectors to be transformed may be either\n/// `double` or `std::complex<double>`. The matrix, however, must be real. In\n/// the case of acting on a vector of complex values, the matrix is treated as\n/// having zero imaginary part. This is chosen for efficiency in all\n/// use-cases for spectral matrix arithmetic so far encountered.\ntemplate <typename VariableTags, typename MatrixType, size_t Dim>\nvoid apply_matrices(const gsl::not_null<Variables<VariableTags>*> result,\n                    const std::array<MatrixType, Dim>& matrices,\n                    const Variables<VariableTags>& u,\n                    const Index<Dim>& extents) {\n  ASSERT(u.number_of_grid_points() == extents.product(),\n         \"Mismatch between extents (\" << extents.product()\n                                      << \") and variables (\"\n                                      << u.number_of_grid_points() << \").\");\n  ASSERT(result->number_of_grid_points() ==\n             apply_matrices_detail::result_size(matrices, extents),\n         \"result has wrong size.  Expected \"\n             << apply_matrices_detail::result_size(matrices, extents)\n             << \", received \" << result->number_of_grid_points());\n  apply_matrices_detail::Impl<typename Variables<VariableTags>::value_type,\n                              Dim>::apply(result->data(), matrices, u.data(),\n                                          extents,\n                                          u.number_of_independent_components);\n}\n\ntemplate <typename VariableTags, typename MatrixType, size_t Dim>\nVariables<VariableTags> apply_matrices(\n    const std::array<MatrixType, Dim>& matrices,\n    const Variables<VariableTags>& u, const Index<Dim>& extents) {\n  Variables<VariableTags> result(\n      apply_matrices_detail::result_size(matrices, extents));\n  apply_matrices(make_not_null(&result), matrices, u, extents);\n  return result;\n}\n\n// clang tidy mistakenly fails to identify this as a function definition\ntemplate <typename ResultType, typename MatrixType, typename VectorType,\n          size_t Dim>\nvoid apply_matrices(const gsl::not_null<ResultType*> result,  // NOLINT\n                    const std::array<MatrixType, Dim>& matrices,\n                    const VectorType& u, const Index<Dim>& extents) {\n  const size_t number_of_independent_components = u.size() / extents.product();\n  ASSERT(u.size() == number_of_independent_components * extents.product(),\n         \"The size of the vector u (\"\n             << u.size()\n             << \") must be a multiple of the number of grid points (\"\n             << extents.product() << \").\");\n  ASSERT(result->size() ==\n             number_of_independent_components *\n                 apply_matrices_detail::result_size(matrices, extents),\n         \"result has wrong size.  Expected \"\n             << number_of_independent_components *\n                    apply_matrices_detail::result_size(matrices, extents)\n             << \", received \" << result->size());\n  apply_matrices_detail::Impl<typename VectorType::ElementType, Dim>::apply(\n      result->data(), matrices, u.data(), extents,\n      number_of_independent_components);\n}\n\ntemplate <typename MatrixType, typename VectorType, size_t Dim>\nVectorType apply_matrices(const std::array<MatrixType, Dim>& matrices,\n                          const VectorType& u, const Index<Dim>& extents) {\n  const size_t number_of_independent_components = u.size() / extents.product();\n  VectorType result(number_of_independent_components *\n                    apply_matrices_detail::result_size(matrices, extents));\n  apply_matrices(make_not_null(&result), matrices, u, extents);\n  return result;\n}\n\ntemplate <typename ResultType, typename MatrixType, typename VectorType,\n          size_t Dim>\nResultType apply_matrices(const std::array<MatrixType, Dim>& matrices,\n                          const VectorType& u, const Index<Dim>& extents) {\n  const size_t number_of_independent_components = u.size() / extents.product();\n  ResultType result(number_of_independent_components *\n                    apply_matrices_detail::result_size(matrices, extents));\n  apply_matrices(make_not_null(&result), matrices, u, extents);\n  return result;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Blaze/Blaze.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief Code for interoperating and extending Blaze.\n */\nnamespace blaze {}\n"
  },
  {
    "path": "src/DataStructures/Blaze/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Blaze.hpp\n  IntegerPow.hpp\n  StepFunction.hpp\n  )\n"
  },
  {
    "path": "src/DataStructures/Blaze/IntegerPow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <blaze/math/DenseVector.h>\n#include <blaze/math/constraints/SIMDPack.h>\n#include <blaze/math/simd/BasicTypes.h>\n#include <blaze/system/Inline.h>\n#include <blaze/system/Vectorization.h>\n\n#include \"Utilities/Math.hpp\"\n\nnamespace blaze {\n\nnamespace detail {\ntemplate <typename T>\nBLAZE_ALWAYS_INLINE SIMDdouble integer_pow_impl(const SIMDf64<T>& x,\n                                                const int e) {\n  ASSERT(e >= 0, \"Negative powers are not implemented\");\n  int ecount = e;\n  int bitcount = 1;\n  while (ecount >>= 1) {\n    ++bitcount;\n  }\n  SIMDdouble result = blaze::set(1.0);\n  while (bitcount) {\n    result *= result;\n    if ((e >> --bitcount) & 0x1) {\n      result *= x;\n    }\n  }\n  return result;\n}\n}  // namespace detail\n// This vectorized implementation of the integer pow function is necessary\n// because blaze does not offer its own version of a integer pow function.\ntemplate <typename T>\nBLAZE_ALWAYS_INLINE SIMDdouble integer_pow(const SIMDf64<T>& b, const int e) {\n  switch (e) {\n    case 0:\n      return blaze::set(1.0);\n    case 1:\n      return b;\n    case 2:\n      return b * b;\n    case 3:\n      return b * b * b;\n    case 4: {\n      const SIMDdouble b2 = b * b;\n      return b2 * b2;\n    }\n    case 5: {\n      const SIMDdouble b2 = b * b;\n      return b2 * b2 * b;\n    }\n    case 6: {\n      const SIMDdouble b2 = b * b;\n      return b2 * b2 * b2;\n    }\n    case 7: {\n      const SIMDdouble b2 = b * b;\n      return b2 * b2 * b2 * b;\n    }\n    case 8: {\n      const SIMDdouble b2 = b * b;\n      const SIMDdouble b4 = b2 * b2;\n      return b4 * b4;\n    }\n    default:\n      return detail::integer_pow_impl(b, e);\n  }\n}\n\nstruct IntegerPow {\n  int exponent;\n  explicit inline IntegerPow(const int e) : exponent(e) {}\n\n  BLAZE_ALWAYS_INLINE double operator()(const double a) const {\n    return ::integer_pow(a, exponent);\n  }\n\n  template <typename T>\n  BLAZE_ALWAYS_INLINE decltype(auto) load(const T& a) const {\n    BLAZE_CONSTRAINT_MUST_BE_SIMD_PACK(T);\n    return integer_pow(a, exponent);\n  }\n};\n}  // namespace blaze\n\ntemplate <typename VT, bool TF>\nBLAZE_ALWAYS_INLINE decltype(auto) integer_pow(\n    const blaze::DenseVector<VT, TF>& vec, const int e) {\n  return map(*vec, blaze::IntegerPow{e});\n}\n\ntemplate <typename VT, bool TF>\nBLAZE_ALWAYS_INLINE decltype(auto) IntegerPow(\n    const blaze::DenseVector<VT, TF>& vec, const int e) {\n  return map(*vec, blaze::IntegerPow{e});\n}\n"
  },
  {
    "path": "src/DataStructures/Blaze/StepFunction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <blaze/math/DenseVector.h>\n#include <blaze/math/constraints/SIMDPack.h>\n#include <blaze/math/simd/BasicTypes.h>\n#include <blaze/system/Inline.h>\n#include <blaze/system/Vectorization.h>\n\nnamespace blaze {\n// This vectorized implementation of the step function is necessary because\n// blaze does not offer its own version of a vectorized step function.\ntemplate <typename T>\nBLAZE_ALWAYS_INLINE SIMDdouble step_function(const SIMDf64<T>& v)\n#if BLAZE_AVX512F_MODE || BLAZE_MIC_MODE\n{\n  return _mm512_set_pd((*v).eval().value[7] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[6] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[5] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[4] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[3] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[2] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[1] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[0] < 0.0 ? 0.0 : 1.0);\n}\n#elif BLAZE_AVX_MODE\n{\n  return _mm256_set_pd((*v).eval().value[3] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[2] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[1] < 0.0 ? 0.0 : 1.0,\n                       (*v).eval().value[0] < 0.0 ? 0.0 : 1.0);\n}\n#elif BLAZE_SSE2_MODE\n{\n  return _mm_set_pd((*v).eval().value[1] < 0.0 ? 0.0 : 1.0,\n                    (*v).eval().value[0] < 0.0 ? 0.0 : 1.0);\n}\n#else\n{\n  return SIMDdouble{(*v).value < 0.0 ? 0.0 : 1.0};\n}\n#endif\n\nBLAZE_ALWAYS_INLINE double step_function(const double v) {\n  return v < 0.0 ? 0.0 : 1.0;\n}\n\nstruct StepFunction {\n  explicit inline StepFunction() = default;\n\n  template <typename T>\n  BLAZE_ALWAYS_INLINE decltype(auto) operator()(const T& a) const {\n    return step_function(a);\n  }\n\n  template <typename T>\n  BLAZE_ALWAYS_INLINE decltype(auto) load(const T& a) const {\n    BLAZE_CONSTRAINT_MUST_BE_SIMD_PACK(T);\n    return step_function(a);\n  }\n};\n}  // namespace blaze\n\ntemplate <typename VT, bool TF>\nBLAZE_ALWAYS_INLINE decltype(auto) step_function(\n    const blaze::DenseVector<VT, TF>& vec) {\n  return map(*vec, blaze::StepFunction{});\n}\n\ntemplate <typename VT, bool TF>\nBLAZE_ALWAYS_INLINE decltype(auto) StepFunction(\n    const blaze::DenseVector<VT, TF>& vec) {\n  return map(*vec, blaze::StepFunction{});\n}\n"
  },
  {
    "path": "src/DataStructures/BoostMultiArray.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/config.hpp>\n#include <cstddef>\n\n// We need to change the index type from std::ptrdiff_t to std::size_t so that\n// it matches the STL and we don't get unsigned cast warnings everywhere.\nnamespace boost {\nnamespace detail {\nnamespace multi_array{\nusing size_type = std::size_t;\nusing index = std::size_t;\n} // namespace multi_array\n} // namespace detail\n} // namespace boost\n\n#include <boost/multi_array.hpp>\n\n"
  },
  {
    "path": "src/DataStructures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DataStructures)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ApplyMatrices.cpp\n  CompressedMatrix.cpp\n  CompressedVector.cpp\n  DynamicBuffer.cpp\n  DynamicMatrix.cpp\n  DynamicVector.cpp\n  FloatingPointType.cpp\n  Index.cpp\n  IndexIterator.cpp\n  LeviCivitaIterator.cpp\n  SimpleSparseMatrix.cpp\n  SliceIterator.cpp\n  SparseMatrixFiller.cpp\n  StripeIterator.cpp\n  Transpose.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApplyMatrices.hpp\n  BoostMultiArray.hpp\n  CachedTempBuffer.hpp\n  CircularDeque.hpp\n  ComplexDataVector.hpp\n  ComplexDiagonalModalOperator.hpp\n  ComplexModalVector.hpp\n  CompressedMatrix.hpp\n  CompressedVector.hpp\n  DataVector.hpp\n  DiagonalModalOperator.hpp\n  DynamicBuffer.hpp\n  DynamicMatrix.hpp\n  DynamicVector.hpp\n  ExtractPoint.hpp\n  FixedHashMap.hpp\n  FloatingPointType.hpp\n  IdPair.hpp\n  Index.hpp\n  IndexIterator.hpp\n  LeviCivitaIterator.hpp\n  LinkedMessageId.hpp\n  LinkedMessageQueue.hpp\n  MathWrapper.hpp\n  Matrix.hpp\n  ModalVector.hpp\n  SimpleSparseMatrix.hpp\n  SliceIterator.hpp\n  SliceTensorToVariables.hpp\n  SliceVariables.hpp\n  SparseMatrixFiller.hpp\n  SpinWeighted.hpp\n  StaticDeque.hpp\n  StaticMatrix.hpp\n  StaticVector.hpp\n  StripeIterator.hpp\n  TaggedContainers.hpp\n  TaggedTuple.hpp\n  TaggedVariant.hpp\n  Tags.hpp\n  TempBuffer.hpp\n  Transpose.hpp\n  Variables.hpp\n  VariablesTag.hpp\n  VectorImpl.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Autodiff\n  Blaze\n  Boost::boost\n  Charmxx::pup\n  ErrorHandling\n  Options\n  Serialization\n  Utilities\n  )\n\nadd_subdirectory(Blaze)\nadd_subdirectory(DataBox)\nadd_subdirectory(Python)\nadd_subdirectory(Tags)\nadd_subdirectory(Tensor)\n"
  },
  {
    "path": "src/DataStructures/CachedTempBuffer.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup DataStructuresGroup\n * A temporary buffer with contents computed on demand.\n *\n * When an entry in the buffer is first requested, it is computed by the\n * `computer` that is passed to the `get_var` function. Subsequent requests just\n * return the cached value.  The computer can itself request data from the cache\n * to use in its computations.\n *\n * For the cache\n * \\snippet Test_CachedTempBuffer.cpp alias\n * the function used to compute `Tags::Scalar2<DataType>` is\n * \\snippet Test_CachedTempBuffer.cpp compute_func\n */\ntemplate <typename... Tags>\nclass CachedTempBuffer {\n public:\n  using tags_list = tmpl::list<Tags...>;\n\n  /// Construct the buffer with the given computer.  `size` is passed\n  /// to the underlying `TempBuffer` constructor.\n  CachedTempBuffer(const size_t size) : data_(size) {}\n\n  /// Obtain a value from the buffer, computing it if necessary.\n  template <typename Computer, typename Tag>\n  const typename Tag::type& get_var(const Computer& computer, Tag /*meta*/) {\n    static_assert(tmpl::list_contains_v<tmpl::list<Tags...>, Tag>,\n                  \"The requested tag is not available. See the template \"\n                  \"parameters of 'CachedTempBuffer' for the computer type and \"\n                  \"the available tags, and the template parameter of the \"\n                  \"'get_var' function for the requested tag.\");\n    // This function can't be called \"get\" because that interferes\n    // with the ADL needed to access data_.\n    if (not get<Computed<Tag>>(computed_flags_)) {\n      computer(make_not_null(&get<Tag>(data_)), make_not_null(this), Tag{});\n      get<Computed<Tag>>(computed_flags_) = true;\n    }\n    return get<Tag>(data_);\n  }\n  size_t number_of_grid_points() const { return data_.number_of_grid_points(); }\n\n private:\n  template <typename Tag>\n  struct Computed {\n    using type = bool;\n  };\n\n  TempBuffer<tmpl::list<Tags...>> data_;\n  tuples::TaggedTuple<Computed<Tags>...> computed_flags_{\n      ((void)Tags{}, false)...};\n};\n\n/// Instantiate a `CachedTempBuffer` from a typelist instead of a parameter pack\ntemplate <typename Tags>\nusing cached_temp_buffer_from_typelist = tmpl::wrap<Tags, CachedTempBuffer>;\n"
  },
  {
    "path": "src/DataStructures/CircularDeque.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/StaticDeque.hpp\"\n\n// This class is tested in Test_StaticDeque.cpp\n\n/// A class implementing the std::deque interface using a circular\n/// buffer to avoid allocations when the size does not exceed a\n/// previous allocated capacity.\n///\n/// The class is optimized for a small number of elements with many\n/// balanced insertions and removals.  As such, the capacity is not\n/// increased beyond the size required when inserting elements in\n/// order to save memory in the steady-state.\n///\n/// Differences from std::deque:\n/// * Insertions (including during construction) are O(n) if the\n///   previous capacity is exceeded and invalidate all references and\n///   iterators.  Some cases where multiple insertions happen in the\n///   same method are optimized to perform only one reallocation.\n/// * Erasing elements from the front of the queue invalidates all\n///   iterators (but not references).\n///\n/// This last point is not a fundamental limitation, but could be\n/// corrected with a more complicated iterator implementation if the\n/// standard behavior is found to be useful.\n///\n/// \\note This class does not behave like a standard circular buffer,\n/// in that insertion operations never overwrite existing elements.\n/// The circularness is only a reference to the implementation using\n/// circular internal storage to avoid allocations.\ntemplate <typename T>\nclass CircularDeque\n    : public StaticDeque<T, std::numeric_limits<size_t>::max()> {\n  using StaticDeque<T, std::numeric_limits<size_t>::max()>::StaticDeque;\n};\n"
  },
  {
    "path": "src/DataStructures/ComplexDataVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <blaze/math/GroupTag.h>\n#include <complex>\n#include <cstddef>\n#include <functional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(ComplexDataVector);\n}  // namespace blaze\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Stores a collection of complex function values.\n *\n * \\details Use ComplexDataVector to represent function values on the\n * computational domain. Note that interpreting the data also requires knowledge\n * of the points that these function values correspond to.\n *\n * A ComplexDataVector holds an array of contiguous data. The ComplexDataVector\n * can be owning, meaning the array is deleted when the ComplexDataVector goes\n * out of scope, or non-owning, meaning it just has a pointer to an array.\n *\n * Refer to the \\ref DataStructuresGroup documentation for a list of other\n * available types.\n *\n * ComplexDataVectors support a variety of mathematical operations that are\n * applicable contiguous data. In addition to common arithmetic operations such\n * as element-wise addition, subtraction, multiplication and division,\n * element-wise operations between `ComplexDataVector` and `DataVector` are\n * supported. See [blaze-wiki/Vector_Operations]\n * (https://bitbucket.org/blaze-lib/blaze/wiki/Vector%20Operations).\n *\n * in addition, support is provided for:\n * - abs  (returns DataVector, as the result is real)\n * - conj\n * - imag  (returns DataVector, as the result is real)\n * - real  (returns DataVector, as the result is real)\n */\nclass ComplexDataVector\n    : public VectorImpl<std::complex<double>, ComplexDataVector> {\n public:\n  ComplexDataVector() = default;\n  ComplexDataVector(const ComplexDataVector&) = default;\n  ComplexDataVector(ComplexDataVector&&) = default;\n  ComplexDataVector& operator=(const ComplexDataVector&) = default;\n  ComplexDataVector& operator=(ComplexDataVector&&) = default;\n  ~ComplexDataVector() = default;\n\n  using BaseType = VectorImpl<std::complex<double>, ComplexDataVector>;\n\n  using BaseType::operator=;\n  using BaseType::VectorImpl;\n};\n\nnamespace blaze {\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(ComplexDataVector);\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ALL_MAP_TRAITS(ComplexDataVector);\ntemplate <>\nstruct MapTrait<ComplexDataVector, blaze::Real> {\n  using Type = DataVector;\n};\ntemplate <>\nstruct MapTrait<ComplexDataVector, blaze::Imag> {\n  using Type = DataVector;\n};\ntemplate <>\nstruct MapTrait<DataVector, blaze::Real> {\n  using Type = DataVector;\n};\ntemplate <>\nstruct MapTrait<DataVector, blaze::Imag> {\n  using Type = DataVector;\n};\ntemplate <>\nstruct MapTrait<ComplexDataVector, blaze::Abs> {\n  using Type = DataVector;\n};\ntemplate <>\nstruct MapTrait<ComplexDataVector, blaze::SqrAbs> {\n  using Type = DataVector;\n};\n\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, DataVector,\n                                               AddTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, DataVector,\n                                               DivTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, DataVector,\n                                               MultTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, DataVector,\n                                               SubTrait, ComplexDataVector);\n\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, double,\n                                               AddTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, double,\n                                               DivTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, double,\n                                               MultTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDataVector, double,\n                                               SubTrait, ComplexDataVector);\n\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(DataVector, std::complex<double>,\n                                               AddTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(DataVector, std::complex<double>,\n                                               DivTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(DataVector, std::complex<double>,\n                                               MultTrait, ComplexDataVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(DataVector, std::complex<double>,\n                                               SubTrait, ComplexDataVector);\n\ntemplate <typename Operator>\nstruct MapTrait<DataVector, ComplexDataVector, Operator> {\n  using Type = ComplexDataVector;\n};\ntemplate <typename Operator>\nstruct MapTrait<ComplexDataVector, DataVector, Operator> {\n  using Type = ComplexDataVector;\n};\n}  // namespace blaze\n\nMAKE_STD_ARRAY_VECTOR_BINOPS(ComplexDataVector)\n\n/// \\cond HIDDEN_SYMBOLS\nDEFINE_STD_ARRAY_BINOP(ComplexDataVector, ComplexDataVector,\n                       DataVector, operator+, std::plus<>())\nDEFINE_STD_ARRAY_BINOP(ComplexDataVector, DataVector,\n                       ComplexDataVector, operator+, std::plus<>())\nDEFINE_STD_ARRAY_BINOP(ComplexDataVector, ComplexDataVector, double, operator+,\n                       std::plus<>())\nDEFINE_STD_ARRAY_BINOP(ComplexDataVector, double, ComplexDataVector, operator+,\n                       std::plus<>())\n\nDEFINE_STD_ARRAY_BINOP(ComplexDataVector, ComplexDataVector,\n                       DataVector, operator-, std::minus<>())\nDEFINE_STD_ARRAY_BINOP(ComplexDataVector, DataVector,\n                       ComplexDataVector, operator-, std::minus<>())\nDEFINE_STD_ARRAY_BINOP(ComplexDataVector, ComplexDataVector, double, operator-,\n                       std::minus<>())\nDEFINE_STD_ARRAY_BINOP(ComplexDataVector, double, ComplexDataVector, operator-,\n                       std::minus<>())\n\nDEFINE_STD_ARRAY_INPLACE_BINOP(ComplexDataVector, DataVector, operator+=,\n                               std::plus<>())\nDEFINE_STD_ARRAY_INPLACE_BINOP(ComplexDataVector, DataVector, operator-=,\n                               std::minus<>())\nDEFINE_STD_ARRAY_INPLACE_BINOP(ComplexDataVector, double, operator+=,\n                               std::plus<>())\nDEFINE_STD_ARRAY_INPLACE_BINOP(ComplexDataVector, double, operator-=,\n                               std::minus<>())\n/// \\endcond\n\nMAKE_WITH_VALUE_IMPL_DEFINITION_FOR(ComplexDataVector)\n"
  },
  {
    "path": "src/DataStructures/ComplexDiagonalModalOperator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DiagonalModalOperator.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DiagonalModalOperator;\nclass ModalVector;\nclass ComplexDiagonalModalOperator;\n/// \\endcond\n\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(ComplexDiagonalModalOperator);\n}  // namespace blaze\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A class for an element-wise complex multiplier of modal coefficients.\n *\n * \\details A `ComplexDiagonalModalOperator` holds an array of factors to\n * multiply by spectral coefficients, and can be either owning (the array is\n * deleted when the `ComplexDiagonalModalOperator` goes out of scope) or\n * non-owning, meaning it just has a pointer to an array.\n *\n * `ComplexDiagonalModalOperator`s are intended to represent a diagonal matrix\n * that can operate (via the `*` operator) on spectral coefficients represented\n * by `ComplexModalVector`s easily. Only basic mathematical operations are\n * supported with `ComplexDiagonalModalOperator`s.\n * `ComplexDiagonalModalOperator`s may be added, subtracted, multiplied, or\n * divided, and all arithmetic operations are similarly supported between\n * `ComplexDiagonalModalOperator`s and `DiagonalModalOperator`s. In addition,\n * the following operations with modal data structures are supported:\n * - multiplication of a `ComplexDiagonalModalOperator` and a\n *   `ComplexModalVector` resulting in a `ComplexModalVector`\n * - multiplication of a `ComplexDiagonalModalOperator` and a `ModalVector`\n *   resulting in a `ComplexModalVector`\n * - multiplication of a `DiagonalModalOperator` and a `ComplexModalVector`\n *   resulting in a `ComplexModalVector`\n * All of these multiplication operations are commutative, supporting the\n * interpretation of the modal data structure as either a 'row' or a 'column'\n * vector.\n *\n * The following unary operations are supported with\n * `ComplexDiagonalModalOperator`s:\n * - conj\n * - imag (results in a `DiagonalModalOperator`)\n * - real (results in a `DiagonalModalOperator`)\n *\n * Also, addition, subtraction, multiplication and division of\n * `DiagonalModalOperator`s with `std::complex<double>`s or `double`s is\n * supported.\n */\nclass ComplexDiagonalModalOperator\n    : public VectorImpl<std::complex<double>, ComplexDiagonalModalOperator> {\n public:\n  ComplexDiagonalModalOperator() = default;\n  ComplexDiagonalModalOperator(const ComplexDiagonalModalOperator&) = default;\n  ComplexDiagonalModalOperator(ComplexDiagonalModalOperator&&) = default;\n  ComplexDiagonalModalOperator& operator=(const ComplexDiagonalModalOperator&) =\n      default;\n  ComplexDiagonalModalOperator& operator=(ComplexDiagonalModalOperator&&) =\n      default;\n  ~ComplexDiagonalModalOperator() = default;\n\n  using BaseType =\n      VectorImpl<std::complex<double>, ComplexDiagonalModalOperator>;\n\n  using BaseType::operator=;\n  using BaseType::VectorImpl;\n};\n\nnamespace blaze {\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(ComplexDiagonalModalOperator);\n\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDiagonalModalOperator,\n                                               DiagonalModalOperator, AddTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDiagonalModalOperator,\n                                               DiagonalModalOperator, DivTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDiagonalModalOperator,\n                                               DiagonalModalOperator, MultTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDiagonalModalOperator,\n                                               DiagonalModalOperator, SubTrait,\n                                               ComplexDiagonalModalOperator);\n\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDiagonalModalOperator,\n                                               double, AddTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDiagonalModalOperator,\n                                               double, DivTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDiagonalModalOperator,\n                                               double, MultTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexDiagonalModalOperator,\n                                               double, SubTrait,\n                                               ComplexDiagonalModalOperator);\n\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(DiagonalModalOperator,\n                                               std::complex<double>, AddTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(DiagonalModalOperator,\n                                               std::complex<double>, DivTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(DiagonalModalOperator,\n                                               std::complex<double>, MultTrait,\n                                               ComplexDiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(DiagonalModalOperator,\n                                               std::complex<double>, SubTrait,\n                                               ComplexDiagonalModalOperator);\n\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexModalVector,\n                                               DiagonalModalOperator,\n                                               MultTrait, ComplexModalVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexModalVector,\n                                               ComplexDiagonalModalOperator,\n                                               MultTrait, ComplexModalVector);\ntemplate <>\nstruct MultTrait<ModalVector, ComplexDiagonalModalOperator> {\n  using Type = ComplexModalVector;\n};\ntemplate <>\nstruct MultTrait<ComplexDiagonalModalOperator, ModalVector> {\n  using Type = ComplexModalVector;\n};\n\ntemplate <typename Operator>\nstruct MapTrait<ComplexDiagonalModalOperator, Operator> {\n  // Selectively allow unary operations for spectral coefficient operators\n  static_assert(tmpl::list_contains_v<\n                    tmpl::list<blaze::Conj, blaze::Sqrt,\n                               blaze::Bind1st<blaze::Add, std::complex<double>>,\n                               blaze::Bind2nd<blaze::Add, std::complex<double>>,\n                               blaze::Bind1st<blaze::Div, std::complex<double>>,\n                               blaze::Bind2nd<blaze::Div, std::complex<double>>,\n                               blaze::Bind1st<blaze::Sub, std::complex<double>>,\n                               blaze::Bind2nd<blaze::Sub, std::complex<double>>,\n                               blaze::Bind1st<blaze::Add, double>,\n                               blaze::Bind2nd<blaze::Add, double>,\n                               blaze::Bind1st<blaze::Div, double>,\n                               blaze::Bind2nd<blaze::Div, double>,\n                               blaze::Bind1st<blaze::Sub, double>,\n                               blaze::Bind2nd<blaze::Sub, double>>,\n                    Operator>,\n                \"This unary operation is not permitted on a \"\n                \"ComplexDiagonalModalOperator\");\n  using Type = ComplexDiagonalModalOperator;\n};\ntemplate <>\nstruct MapTrait<ComplexDiagonalModalOperator, blaze::Imag> {\n  using Type = DiagonalModalOperator;\n};\ntemplate <>\nstruct MapTrait<ComplexDiagonalModalOperator, blaze::Real> {\n  using Type = DiagonalModalOperator;\n};\ntemplate <>\nstruct MapTrait<DiagonalModalOperator, blaze::Imag> {\n  using Type = DiagonalModalOperator;\n};\ntemplate <>\nstruct MapTrait<DiagonalModalOperator, blaze::Real> {\n  using Type = DiagonalModalOperator;\n};\n\ntemplate <typename Operator>\nstruct MapTrait<ComplexDiagonalModalOperator, ComplexDiagonalModalOperator,\n                Operator> {\n  // Forbid math operations in this specialization of BinaryMap traits for\n  // ComplexDiagonalModalOperator that are unlikely to be used on spectral\n  // coefficients. Currently no non-arithmetic binary operations are supported.\n  static_assert(tmpl::list_contains_v<tmpl::list<>, Operator>,\n                \"This binary operation is not permitted on a \"\n                \"ComplexDiagonalModalOperator.\");\n  using Type = ComplexDiagonalModalOperator;\n};\n}  // namespace blaze\n\nMAKE_STD_ARRAY_VECTOR_BINOPS(ComplexDiagonalModalOperator)\n\nMAKE_WITH_VALUE_IMPL_DEFINITION_FOR(ComplexDiagonalModalOperator)\n"
  },
  {
    "path": "src/DataStructures/ComplexModalVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <blaze/math/GroupTag.h>\n\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ModalVector;\nclass ComplexModalVector;\n/// \\endcond\n\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(ComplexModalVector);\n}  // namespace blaze\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A class for storing complex spectral coefficients on a spectral grid.\n *\n * \\details A ComplexModalVector holds an array of spectral coefficients\n * represented as `std::complex<double>`s, and can be either owning (the array\n * is deleted when the ComplexModalVector goes out of scope) or non-owning,\n * meaning it just has a pointer to an array.\n *\n * Only basic mathematical operations are supported with\n * `ComplexModalVector`s. `ComplexModalVector`s may be added or subtracted and\n * may be added or subtracted with `ModalVector`s, and the following unary\n * operations are supported:\n * - conj\n * - imag (which returns a `ModalVector`)\n * - real (which returns a `ModalVector`)\n *\n * Also multiplication is supported with `std::complex<double>`s or doubles and\n * `ComplexModalVector`s, and `ComplexModalVector`s can be divided by\n * `std::complex<double>`s or `double`s. Multiplication is supported with\n * `ComplexDiagonalModalOperator`s and `ComplexModalVector`s.\n */\nclass ComplexModalVector\n    : public VectorImpl<std::complex<double>, ComplexModalVector> {\n public:\n  ComplexModalVector() = default;\n  ComplexModalVector(const ComplexModalVector&) = default;\n  ComplexModalVector(ComplexModalVector&&) = default;\n  ComplexModalVector& operator=(const ComplexModalVector&) = default;\n  ComplexModalVector& operator=(ComplexModalVector&&) = default;\n  ~ComplexModalVector() = default;\n\n  using BaseType = VectorImpl<std::complex<double>, ComplexModalVector>;\n\n  using BaseType::operator=;\n  using BaseType::VectorImpl;\n};\n\nnamespace blaze {\ntemplate <>\nstruct TransposeFlag<ComplexModalVector>\n    : BoolConstant<ComplexModalVector::transpose_flag> {};\ntemplate <>\nstruct AddTrait<ComplexModalVector, ComplexModalVector> {\n  using Type = ComplexModalVector;\n};\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexModalVector, ModalVector,\n                                               AddTrait, ComplexModalVector);\ntemplate <>\nstruct SubTrait<ComplexModalVector, ComplexModalVector> {\n  using Type = ComplexModalVector;\n};\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexModalVector, ModalVector,\n                                               SubTrait, ComplexModalVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexModalVector,\n                                               std::complex<double>, MultTrait,\n                                               ComplexModalVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ModalVector,\n                                               std::complex<double>, MultTrait,\n                                               ComplexModalVector);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ComplexModalVector, double,\n                                               MultTrait, ComplexModalVector);\ntemplate <>\nstruct DivTrait<ComplexModalVector, std::complex<double>> {\n  using Type = ComplexModalVector;\n};\ntemplate <>\nstruct DivTrait<ComplexModalVector, double> {\n  using Type = ComplexModalVector;\n};\n\ntemplate <typename Operator>\nstruct MapTrait<ComplexModalVector, Operator> {\n  // Selectively allow unary operations for spectral coefficients\n  static_assert(tmpl::list_contains_v<\n                    tmpl::list<blaze::Conj,\n                               // Following 3 reqd. by operator(+,+=), (-,-=),\n                               // (-) w/doubles\n                               blaze::Bind1st<blaze::Add, std::complex<double>>,\n                               blaze::Bind2nd<blaze::Add, std::complex<double>>,\n                               blaze::Bind1st<blaze::Sub, std::complex<double>>,\n                               blaze::Bind2nd<blaze::Sub, std::complex<double>>,\n                               blaze::Bind1st<blaze::Add, double>,\n                               blaze::Bind2nd<blaze::Add, double>,\n                               blaze::Bind1st<blaze::Sub, double>,\n                               blaze::Bind2nd<blaze::Sub, double>>,\n                    Operator>,\n                \"Only unary operations permitted on a ComplexModalVector are:\"\n                \" conj, imag, real, abs\");\n  using Type = ComplexModalVector;\n};\ntemplate <>\nstruct MapTrait<ComplexModalVector, blaze::Imag> {\n  using Type = ModalVector;\n};\ntemplate <>\nstruct MapTrait<ComplexModalVector, blaze::Real> {\n  using Type = ModalVector;\n};\ntemplate <>\nstruct MapTrait<ModalVector, blaze::Imag> {\n  using Type = ModalVector;\n};\ntemplate <>\nstruct MapTrait<ModalVector, blaze::Real> {\n  using Type = ModalVector;\n};\ntemplate <>\nstruct MapTrait<ComplexModalVector, blaze::Abs> {\n  using Type = ModalVector;\n};\ntemplate <>\nstruct MapTrait<ComplexModalVector, blaze::SqrAbs> {\n  using Type = ModalVector;\n};\n\ntemplate <typename Operator>\nstruct MapTrait<ComplexModalVector, ComplexModalVector, Operator> {\n  // Forbid math operations in this specialization of BinaryMap traits for\n  // ComplexModalVector that are unlikely to be used on spectral coefficients.\n  // Currently no non-arithmetic binary operations are supported.\n  static_assert(\n      tmpl::list_contains_v<tmpl::list<>, Operator>,\n      \"This binary operation is not permitted on a ComplexModalVector.\");\n  using Type = ComplexModalVector;\n};\n}  // namespace blaze\n\n/// \\cond\nDEFINE_STD_ARRAY_BINOP(ComplexModalVector, ComplexModalVector,\n                       ComplexModalVector, operator+, std::plus<>())\nDEFINE_STD_ARRAY_BINOP(ComplexModalVector, ComplexModalVector,\n                       ComplexModalVector, operator-, std::minus<>())\nDEFINE_STD_ARRAY_INPLACE_BINOP(ComplexModalVector,\n                               ComplexModalVector, operator+=, std::plus<>())\nDEFINE_STD_ARRAY_INPLACE_BINOP(ComplexModalVector,\n                               ComplexModalVector, operator-=, std::minus<>())\n/// \\endcond\nMAKE_WITH_VALUE_IMPL_DEFINITION_FOR(ComplexModalVector)\n"
  },
  {
    "path": "src/DataStructures/CompressedMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/CompressedMatrix.hpp\"\n\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace CompressedMatrix_detail {\n// Avoid including the entire option parser in a low-level header.\ntemplate <typename Type>\nstd::vector<std::vector<Type>> parse_to_vectors(\n    const Options::Option& options) {\n  return options.parse_as<std::vector<std::vector<Type>>>();\n}\n\n#define TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                      \\\n  template std::vector<std::vector<TYPE(data)>> parse_to_vectors( \\\n      const Options::Option& options);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double))\n\n#undef INSTANTIATE\n#undef TYPE\n}  // namespace CompressedMatrix_detail\n"
  },
  {
    "path": "src/DataStructures/CompressedMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// `blaze::CompressedMatrix` is a general-purpose sparse matrix type. This file\n/// implements interoperability of `blaze::CompressedMatrix` with our data\n/// structures.\n\n#pragma once\n\n#include <blaze/math/CompressedMatrix.h>\n#include <cstddef>\n#include <pup.h>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace PUP {\n/// @{\n/// Serialization of blaze::CompressedMatrix\ntemplate <typename Type, bool SO, typename Tag>\nvoid pup(er& p, blaze::CompressedMatrix<Type, SO, Tag>& t) {\n  size_t rows = t.rows();\n  size_t columns = t.columns();\n  p | rows;\n  p | columns;\n  const size_t first_dimension = (SO == blaze::rowMajor) ? rows : columns;\n  size_t num_non_zeros = t.nonZeros();\n  p | num_non_zeros;\n  // blaze::CompressedMatrix has no `.data()` access, so we use the low-level\n  // `append` mechanism for serialization instead of `PUParray`. Maybe there's\n  // an even faster way using PUPbytes.\n  size_t index;\n  if (p.isUnpacking()) {\n    t.resize(rows, columns);\n    t.reserve(num_non_zeros);\n    Type value;\n    for (size_t i = 0; i < first_dimension; ++i) {\n      p | num_non_zeros;\n      for (size_t j = 0; j < num_non_zeros; ++j) {\n        p | index;\n        p | value;\n        if constexpr (SO == blaze::rowMajor) {\n          t.append(i, index, value);\n        } else {\n          t.append(index, i, value);\n        }\n      }\n      t.finalize(i);\n    }\n  } else {\n    for (size_t i = 0; i < first_dimension; ++i) {\n      num_non_zeros = t.nonZeros(i);\n      p | num_non_zeros;\n      for (auto it = t.begin(i); it != t.end(i); ++it) {\n        index = it->index();\n        p | index;\n        p | it->value();\n      }\n    }\n  }\n}\ntemplate <typename Type, bool SO, typename Tag>\nvoid operator|(er& p, blaze::CompressedMatrix<Type, SO, Tag>& t) {\n  pup(p, t);\n}\n/// @}\n}  // namespace PUP\n\nnamespace CompressedMatrix_detail {\ntemplate <typename Type>\nstd::vector<std::vector<Type>> parse_to_vectors(const Options::Option& options);\n}  // namespace CompressedMatrix_detail\n\ntemplate <typename Type, bool SO, typename Tag>\nstruct Options::create_from_yaml<blaze::CompressedMatrix<Type, SO, Tag>> {\n  template <typename Metavariables>\n  static blaze::CompressedMatrix<Type, SO, Tag> create(\n      const Options::Option& options) {\n    const auto data = CompressedMatrix_detail::parse_to_vectors<Type>(options);\n    const size_t num_rows = data.size();\n    size_t num_cols = 0;\n    if (num_rows > 0) {\n      num_cols = data[0].size();\n    }\n    blaze::CompressedMatrix<Type, SO, Tag> result(num_rows, num_cols);\n    for (size_t i = 0; i < num_rows; i++) {\n      const auto& row = gsl::at(data, i);\n      if (row.size() != num_cols) {\n        PARSE_ERROR(options.context(),\n                    \"All matrix rows must have the same size.\");\n      }\n      for (size_t j = 0; j < num_cols; j++) {\n        if (gsl::at(row, j) != 0.) {\n          result(i, j) = gsl::at(row, j);\n        }\n      }\n    }\n    return result;\n  }\n};\n"
  },
  {
    "path": "src/DataStructures/CompressedVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/CompressedVector.hpp\"\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace CompressedVector_detail {\n// Avoid including the entire option parser in a low-level header.\ntemplate <typename T>\nstd::vector<T> parse_to_vector(const Options::Option& options) {\n  return options.parse_as<std::vector<T>>();\n}\n\n#define TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                        \\\n  template std::vector<TYPE(data)> parse_to_vector( \\\n      const Options::Option& options);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double))\n\n#undef INSTANTIATE\n#undef TYPE\n}  // namespace CompressedVector_detail\n"
  },
  {
    "path": "src/DataStructures/CompressedVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// `blaze::CompressedVector` is a general-purpose sparse vector type. This file\n/// implements interoperability of `blaze::CompressedVector` with our data\n/// structures.\n\n#pragma once\n\n#include <blaze/math/CompressedVector.h>\n#include <cstddef>\n#include <pup.h>\n#include <vector>\n\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace Options {\nstruct Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace PUP {\n/// @{\n/// Serialization of blaze::CompressedVector\ntemplate <typename T, bool TF, typename Tag>\nvoid pup(er& p, blaze::CompressedVector<T, TF, Tag>& t) {\n  size_t size = t.size();\n  p | size;\n  size_t num_non_zeros = t.nonZeros();\n  p | num_non_zeros;\n  // blaze::CompressedVector has no `.data()` access, so we use the low-level\n  // `append` mechanism for serialization instead of `PUParray`. Maybe there's\n  // an even faster way using PUPbytes.\n  size_t index;\n  if (p.isUnpacking()) {\n    t.resize(size);\n    t.reserve(num_non_zeros);\n    T value;\n    for (size_t i = 0; i < num_non_zeros; ++i) {\n      p | index;\n      p | value;\n      t.append(index, value);\n    }\n  } else {\n    for (auto it = t.begin(); it != t.end(); ++it) {\n      index = it->index();\n      p | index;\n      p | it->value();\n    }\n  }\n}\ntemplate <typename T, bool TF, typename Tag>\nvoid operator|(er& p, blaze::CompressedVector<T, TF, Tag>& t) {\n  pup(p, t);\n}\n/// @}\n}  // namespace PUP\n\nnamespace CompressedVector_detail {\ntemplate <typename T>\nstd::vector<T> parse_to_vector(const Options::Option& options);\n}  // namespace CompressedVector_detail\n\ntemplate <typename T, bool TF, typename Tag>\nstruct Options::create_from_yaml<blaze::CompressedVector<T, TF, Tag>> {\n  template <typename Metavariables>\n  static blaze::CompressedVector<T, TF, Tag> create(\n      const Options::Option& options) {\n    const auto data = CompressedVector_detail::parse_to_vector<T>(options);\n    blaze::CompressedVector<T, TF, Tag> result(data.size());\n    // Insert only non-zero elements. Can't use iterators and `std::copy`\n    // because for sparse types the iterators only run over non-zero elements.\n    // There's probably a faster way to do this construction using the low-level\n    // `append` function, but it's probably not worth the effort for small\n    // matrices created in input files.\n    for (size_t i = 0; i < data.size(); ++i) {\n      if (gsl::at(data, i) != 0.) {\n        result[i] = gsl::at(data, i);\n      }\n    }\n    return result;\n  }\n};\n"
  },
  {
    "path": "src/DataStructures/DataBox/Access.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/IsApplyCallable.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Utilities/CleanupRoutine.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsCallable.hpp\"\n\nnamespace db {\n/*!\n * \\brief A class for retrieving items from a DataBox without needing to know\n * all the tags in the box.\n *\n * Retrieval is handled using a virtual function call but still uses Tags\n * rather than strings to ensure automatic handling of casting to the expected\n * type.\n */\nclass Access {\n public:\n  virtual ~Access() = default;\n\n  /// Print the expanded type aliases of the derived `db::DataBox`\n  virtual std::string print_tags() const = 0;\n\n  /// Print the expanded type aliases of the derived `db::DataBox`\n  virtual std::string print_types() const = 0;\n\n private:\n  template <typename Tag>\n  friend const auto& get(const Access& box);\n  template <typename... MutateTags, typename Invokable, typename... Args>\n  friend decltype(auto) mutate(Invokable&& invokable,\n                               gsl::not_null<Access*> box, Args&&... args);\n  virtual void* mutate_item_by_name(const std::string& tag_name) = 0;\n  virtual const void* get_item_by_name(const std::string& tag_name) const = 0;\n  virtual bool lock_box_for_mutate() = 0;\n  virtual void unlock_box_after_mutate() = 0;\n  virtual void mutate_mutable_subitems(const std::string& tag_name) = 0;\n  virtual void reset_compute_items_after_mutate(\n      const std::string& tag_name) = 0;\n\n  template <typename Tag>\n  auto mutate() -> gsl::not_null<typename Tag::type*> {\n    static const std::string tag_name = pretty_type::get_name<Tag>();\n    return {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n        reinterpret_cast<typename Tag::type*>(mutate_item_by_name(tag_name))};\n  }\n};\n\n/// \\brief Retrieve a tag from a `db::Access`\ntemplate <typename Tag>\nSPECTRE_ALWAYS_INLINE const auto& get(const Access& box) {\n  static const std::string tag_name = pretty_type::get_name<Tag>();\n  if constexpr (tt::is_a_v<std::unique_ptr, typename Tag::type>) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    return *reinterpret_cast<const typename Tag::type::element_type*>(\n        box.get_item_by_name(tag_name));\n  } else {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    return *reinterpret_cast<const typename Tag::type*>(\n        box.get_item_by_name(tag_name));\n  }\n}\n\n/// \\brief Mutate a tag in a `db::Access`.\ntemplate <typename... MutateTags, typename Invokable, typename... Args>\ndecltype(auto) mutate(Invokable&& invokable, const gsl::not_null<Access*> box,\n                      Args&&... args) {\n  if (UNLIKELY(box->lock_box_for_mutate())) {\n    ERROR(\n        \"Unable to mutate a DataBox that is already being mutated. This \"\n        \"error occurs when mutating a DataBox from inside the invokable \"\n        \"passed to the mutate function.\");\n  }\n\n  const CleanupRoutine unlock_box = [&box]() {\n    box->unlock_box_after_mutate();\n  };\n\n  // Check that the arguments are fetchable before setting up the\n  // cleanup so we don't trigger an error during stack unwinding.\n  const std::tuple mutate_pointers{box->template mutate<MutateTags>()...};\n\n  const CleanupRoutine reset_post_mutate = [&box]() {\n    (void)box;\n    EXPAND_PACK_LEFT_TO_RIGHT([&box]() {\n      static const std::string tag_name = pretty_type::get_name<MutateTags>();\n      box->mutate_mutable_subitems(tag_name);\n      box->reset_compute_items_after_mutate(tag_name);\n    }());\n  };\n  return std::apply(\n      invokable,\n      std::tuple_cat(mutate_pointers,\n                     std::forward_as_tuple(std::forward<Args>(args)...)));\n}\n\nnamespace detail {\ntemplate <typename... ReturnTags, typename... ArgumentTags, typename F,\n          typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(\n    F&& f, const gsl::not_null<Access*> box, tmpl::list<ReturnTags...> /*meta*/,\n    tmpl::list<ArgumentTags...> /*meta*/, Args&&... args) {\n  static_assert(not(... or std::is_same_v<ArgumentTags, Tags::DataBox>),\n                \"Cannot pass Tags::DataBox to mutate_apply when mutating \"\n                \"since the db::get won't work inside mutate_apply.\");\n  if constexpr (detail::is_apply_callable_v<\n                    F, const gsl::not_null<typename ReturnTags::type*>...,\n                    const_item_type<ArgumentTags, tmpl::list<>>..., Args...>) {\n    return ::db::mutate<ReturnTags...>(\n        [](const gsl::not_null<typename ReturnTags::type*>... mutated_items,\n           const_item_type<ArgumentTags, tmpl::list<>>... args_items,\n           decltype(std::forward<Args>(args))... l_args) {\n          return std::decay_t<F>::apply(mutated_items..., args_items...,\n                                        std::forward<Args>(l_args)...);\n        },\n        box, db::get<ArgumentTags>(*box)..., std::forward<Args>(args)...);\n  } else if constexpr (::tt::is_callable_v<\n                           F,\n                           const gsl::not_null<typename ReturnTags::type*>...,\n                           const_item_type<ArgumentTags, tmpl::list<>>...,\n                           Args...>) {\n    return ::db::mutate<ReturnTags...>(f, box, db::get<ArgumentTags>(*box)...,\n                                       std::forward<Args>(args)...);\n  } else {\n    error_function_not_callable<F, gsl::not_null<typename ReturnTags::type*>...,\n                                const_item_type<ArgumentTags, tmpl::list<>>...,\n                                Args...>();\n  }\n}\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Apply the invokable `f` mutating items `MutateTags` and taking as\n * additional arguments `ArgumentTags` and `args`.\n *\n * \\details\n * `f` must either be invokable with the arguments of type\n * `gsl::not_null<db::item_type<MutateTags>*>...,\n * db::const_item_type<ArgumentTags>..., Args...`\n * where the first two pack expansions are over the elements in the typelists\n * `MutateTags` and `ArgumentTags`, or have a static `apply` function that is\n * callable with the same types. If the type of `f` specifies `return_tags` and\n * `argument_tags` typelists, these are used for the `MutateTags` and\n * `ArgumentTags`, respectively.\n *\n * Any return values of the invokable `f` are forwarded as returns to the\n * `mutate_apply` call.\n *\n * \\example\n * An example of using `mutate_apply` with a lambda:\n * \\snippet Test_DataBox.cpp mutate_apply_lambda_example\n *\n * An example of a class with a static `apply` function\n * \\snippet Test_DataBox.cpp mutate_apply_struct_definition_example\n * and how to use `mutate_apply` with the above class\n * \\snippet Test_DataBox.cpp mutate_apply_struct_example_stateful\n * Note that the class exposes `return_tags` and `argument_tags` typelists, so\n * we don't specify the template parameters explicitly.\n * If the class `F` has no state, like in this example,\n * \\snippet Test_DataBox.cpp mutate_apply_struct_definition_example\n * you can also use the stateless overload of `mutate_apply`:\n * \\snippet Test_DataBox.cpp mutate_apply_struct_example_stateless\n *\n * \\tparam MutateTags typelist of Tags to mutate\n * \\tparam ArgumentTags typelist of additional items to retrieve from the\n * `Access`\n * \\tparam F The invokable to apply\n */\ntemplate <typename MutateTags, typename ArgumentTags, typename F,\n          typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(\n    F&& f, const gsl::not_null<Access*> box, Args&&... args) {\n  return detail::mutate_apply(std::forward<F>(f), box, MutateTags{},\n                              ArgumentTags{}, std::forward<Args>(args)...);\n}\n\ntemplate <typename F, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(\n    F&& f, const gsl::not_null<Access*> box, Args&&... args) {\n  return mutate_apply<typename std::decay_t<F>::return_tags,\n                      typename std::decay_t<F>::argument_tags>(\n      std::forward<F>(f), box, std::forward<Args>(args)...);\n}\n\ntemplate <typename F, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(\n    const gsl::not_null<Access*> box, Args&&... args) {\n  return mutate_apply(F{}, box, std::forward<Args>(args)...);\n}\n/// @}\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/AsAccess.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n\nnamespace db {\n/// @{\n/// \\brief Convert a `db::DataBox` to a `db::Access`.\ntemplate <typename TagsList>\nconst Access& as_access(const DataBox<TagsList>& box) {\n  return static_cast<const Access&>(box);\n}\n\ntemplate <typename TagsList>\nAccess& as_access(DataBox<TagsList>& box) {\n  return static_cast<Access&>(box);\n}\n/// @}\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Access.hpp\n  AsAccess.hpp\n  DataBox.hpp\n  DataBoxTag.hpp\n  DataOnSlice.hpp\n  IsApplyCallable.hpp\n  Item.hpp\n  MetavariablesTag.hpp\n  ObservationBox.hpp\n  PrefixHelpers.hpp\n  Prefixes.hpp\n  SubitemTag.hpp\n  Subitems.hpp\n  Tag.hpp\n  TagName.hpp\n  TagTraits.hpp\n  ValidateSelection.hpp\n  )\n\nadd_subdirectory(Protocols)\n"
  },
  {
    "path": "src/DataStructures/DataBox/DataBox.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines classes and functions used for manipulating DataBox's\n\n#pragma once\n\n#include <cstddef>\n#include <exception>\n#include <functional>\n#include <initializer_list>\n#include <map>\n#include <ostream>\n#include <pup.h>\n#include <sstream>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/IsApplyCallable.hpp\"\n#include \"DataStructures/DataBox/Item.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/SubitemTag.hpp\"\n#include \"DataStructures/DataBox/Subitems.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/CleanupRoutine.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/StaticAssert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/PrintHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsCallable.hpp\"\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Namespace for DataBox related things\n */\nnamespace db {\n// Forward declarations\n/// \\cond\ntemplate <typename TagsList>\nclass DataBox;\n/// \\endcond\n\n/// @{\n/// \\ingroup DataBoxGroup\n/// Equal to `true` if `Tag` can be retrieved from a `DataBox` of type\n/// `DataBoxType`.\ntemplate <typename Tag, typename DataBoxType>\nusing tag_is_retrievable =\n    tmpl::or_<std::is_same<Tag, ::Tags::DataBox>,\n              tmpl::any<typename DataBoxType::tags_list,\n                        std::is_base_of<tmpl::pin<Tag>, tmpl::_1>>>;\n\ntemplate <typename Tag, typename DataBoxType>\nconstexpr bool tag_is_retrievable_v =\n    tag_is_retrievable<Tag, DataBoxType>::value;\n/// @}\n\nnamespace detail {\ntemplate <typename TagsList, typename Tag>\nstruct creation_tag_impl {\n  DEBUG_STATIC_ASSERT(\n      not has_no_matching_tag_v<TagsList, Tag>,\n      \"Found no tags in the DataBox that match the tag being retrieved.\");\n  DEBUG_STATIC_ASSERT(\n      has_unique_matching_tag_v<TagsList, Tag>,\n      \"Found more than one tag in the DataBox that matches the tag \"\n      \"being retrieved. This happens because more than one tag with the same \"\n      \"base (class) tag was added to the DataBox.\");\n  using normalized_tag = first_matching_tag<TagsList, Tag>;\n  // A list with 0 or 1 elements.  This uses `Tag` rather than\n  // `normalized_tag` because subitems of compute tags normalize to\n  // `Tags::Subitem<...>`, which is not what is in the `Subitems`\n  // list.\n  using parent_of_subitem = tmpl::filter<\n      TagsList, tmpl::lazy::list_contains<Subitems<tmpl::_1>, tmpl::pin<Tag>>>;\n  using type = tmpl::front<tmpl::push_back<parent_of_subitem, normalized_tag>>;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief The tag added to \\p Box referred to by \\p Tag.  This\n * resolves base tags and converts subitems to full items.\n */\ntemplate <typename Tag, typename Box>\nusing creation_tag =\n    typename detail::creation_tag_impl<typename Box::tags_list, Tag>::type;\n\nnamespace detail {\ntemplate <typename Tag>\nusing has_subitems =\n    tmpl::not_<std::is_same<typename Subitems<Tag>::type, tmpl::list<>>>;\n\ntemplate <typename Tag>\nconstexpr bool has_subitems_v = has_subitems<Tag>::value;\n\ntemplate <typename Tag, typename ParentTag>\nstruct make_subitem_tag {\n  using type = ::Tags::Subitem<Tag, ParentTag>;\n};\n\ntemplate <typename Tag>\nstruct append_subitem_tags {\n  using type = tmpl::push_front<typename db::Subitems<Tag>::type, Tag>;\n};\n\ntemplate <typename ParentTag>\n  requires(has_subitems_v<ParentTag> and is_immutable_item_tag_v<ParentTag>)\nstruct append_subitem_tags<ParentTag> {\n  using type = tmpl::push_front<\n      tmpl::transform<typename Subitems<ParentTag>::type,\n                      make_subitem_tag<tmpl::_1, tmpl::pin<ParentTag>>>,\n      ParentTag>;\n};\n\ntemplate <typename Tag>\n  requires(not has_subitems_v<Tag>)\nstruct append_subitem_tags<Tag> {\n  using type = tmpl::list<Tag>;\n};\n\ntemplate <typename TagsList>\nusing expand_subitems =\n    tmpl::flatten<tmpl::transform<TagsList, append_subitem_tags<tmpl::_1>>>;\n\ntemplate <typename ComputeTag, typename ArgumentTag,\n          typename FoundArgumentTagInBox>\nstruct report_missing_compute_item_argument {\n  static_assert(std::is_same_v<ComputeTag, void>,\n                \"A compute item's argument could not be found in the DataBox \"\n                \"or was found multiple times.  See the first template argument \"\n                \"of report_missing_compute_item_argument for the compute item \"\n                \"and the second for the missing (or duplicated) argument.\");\n};\n\ntemplate <typename ComputeTag, typename ArgumentTag>\nstruct report_missing_compute_item_argument<ComputeTag, ArgumentTag,\n                                            std::true_type> {\n  using type = void;\n};\n\ntemplate <typename TagsList, typename ComputeTag>\nstruct create_compute_tag_argument_edges {\n#ifdef SPECTRE_DEBUG\n  using argument_check_assertion = tmpl::transform<\n      typename ComputeTag::argument_tags,\n      report_missing_compute_item_argument<\n          tmpl::pin<ComputeTag>, tmpl::_1,\n          has_unique_matching_tag<tmpl::pin<TagsList>, tmpl::_1>>>;\n#endif  // SPECTRE_DEBUG\n  // These edges record that a compute item's value depends on the\n  // values of it's arguments.\n  using type = tmpl::transform<\n      typename ComputeTag::argument_tags,\n      tmpl::bind<tmpl::edge,\n                 tmpl::bind<first_matching_tag, tmpl::pin<TagsList>, tmpl::_1>,\n                 tmpl::pin<ComputeTag>>>;\n};\n\ntemplate <typename TagsList, typename ImmutableItemTagsList>\nstruct create_dependency_graph {\n  using immutable_item_argument_edges =\n      tmpl::join<tmpl::transform<ImmutableItemTagsList,\n                                 detail::create_compute_tag_argument_edges<\n                                     tmpl::pin<TagsList>, tmpl::_1>>>;\n  using type = immutable_item_argument_edges;\n};\n\n// Get the base if it is present\ntemplate <typename Tag, typename = std::void_t<>>\nstruct get_base_impl {\n  using type = Tag;\n};\ntemplate <typename Tag>\nstruct get_base_impl<Tag, std::void_t<typename Tag::base>> {\n  using type = typename Tag::base;\n};\n\ntemplate <typename Tag>\nusing get_base = typename get_base_impl<Tag>::type;\n}  // namespace detail\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief A DataBox stores objects that can be retrieved by using Tags\n * \\warning\n * The order of the tags in DataBoxes returned by db::create depends on\n * implementation-defined behavior, and therefore should not be specified in\n * source files. If explicitly naming a DataBox type is necessary they should be\n * generated using db::compute_databox_type.\n *\n * \\see db::create\n *\n * @tparam Tags list of DataBoxTag's\n */\ntemplate <typename... Tags>\nclass DataBox<tmpl::list<Tags...>> : public Access,\n                                     private detail::Item<Tags>... {\n public:\n  /*!\n   * \\brief A typelist (`tmpl::list`) of Tags that the DataBox holds\n   */\n  using tags_list = tmpl::list<Tags...>;\n\n  /// A list of all the immutable item tags, including their subitems.\n  /// Immutable items are compute tags and reference tags.\n  using immutable_item_tags =\n      tmpl::filter<tags_list, db::is_immutable_item_tag<tmpl::_1>>;\n\n  /// A list of all the immutable item tags used to create the DataBox\n  ///\n  /// \\note This does not include subitems of immutable items\n  using immutable_item_creation_tags =\n      tmpl::remove_if<immutable_item_tags, tt::is_a<::Tags::Subitem, tmpl::_1>>;\n\n  /// A list of all the mutable item tags, including their subitems\n  using mutable_item_tags =\n      tmpl::filter<tags_list, db::is_mutable_item_tag<tmpl::_1>>;\n\n  /// A list of the expanded simple subitems, not including the main Subitem\n  /// tags themselves.\n  ///\n  /// Specifically, if there is a `Variables<Tag0, Tag1>`, then this list would\n  /// contain `Tag0, Tag1`.\n  using mutable_subitem_tags = tmpl::flatten<\n      tmpl::transform<mutable_item_tags, db::Subitems<tmpl::_1>>>;\n\n  /// A list of all the mutable item tags used to create the DataBox\n  ///\n  /// \\note This does not include subitems of mutable items\n  using mutable_item_creation_tags =\n      tmpl::list_difference<mutable_item_tags, mutable_subitem_tags>;\n\n  /// A list of all the mutable tags that have subitems\n  using mutable_item_parent_tags =\n      tmpl::filter<mutable_item_creation_tags,\n                   tmpl::bind<detail::has_subitems, tmpl::_1>>;\n\n  /// A list of all the compute item tags\n  using compute_item_tags =\n      tmpl::filter<immutable_item_tags, db::is_compute_tag<tmpl::_1>>;\n\n  /// A list of all the compute tags that have subitems\n  using compute_item_parent_tags =\n      tmpl::filter<compute_item_tags,\n                   tmpl::bind<detail::has_subitems, tmpl::_1>>;\n\n  /// A list of all the reference tags\n  using reference_item_tags =\n      tmpl::filter<immutable_item_tags, db::is_reference_tag<tmpl::_1>>;\n\n  /// A list of all the reference tags that have subitems\n  using reference_item_parent_tags =\n      tmpl::filter<reference_item_tags,\n                   tmpl::bind<detail::has_subitems, tmpl::_1>>;\n\n  /// If it exists, the Parallel::Tags::MetavariablesImpl tag, otherwise\n  /// NoSuchType\n  ///\n  /// This allows the type-erased Parallel::Tags::Metavariables\n  /// to be used to fetch the metavariables\n  using metavars_tag =\n      typename detail::metavars_tag_impl<mutable_item_tags>::type;\n\n  /// \\cond\n  /*!\n   * \\note the default constructor is only used for serialization\n   */\n  DataBox() = default;\n\n  constexpr DataBox(DataBox&& rhs) : detail::Item<Tags>(std::move(rhs))... {\n    reset_all_subitems();\n  }\n  constexpr DataBox& operator=(DataBox&& rhs) {\n    if (&rhs != this) {\n      ::expand_pack(\n          (get_item<Tags>() = std::move(rhs.template get_item<Tags>()))...);\n      reset_all_subitems();\n    }\n    return *this;\n  }\n  DataBox(const DataBox& rhs) = delete;\n  DataBox& operator=(const DataBox& rhs) = delete;\n  ~DataBox() override = default;\n\n  /// \\endcond\n\n  /// Print the expanded type aliases\n  std::string print_tags() const override;\n\n  /// Print the expanded type aliases\n  std::string print_types() const override;\n\n  /// Print the items\n  template <bool PrintImmutableItems = true>\n  std::string print_items() const;\n\n  /// The size in bytes of each item (excluding reference items)\n  std::map<std::string, size_t> size_of_items() const;\n\n  /// Retrieve the tag `Tag`, should be called by the free function db::get\n  template <typename Tag>\n  const auto& get() const;\n\n  /// \\brief Copy the items with tags `TagsOfItemsToCopy` from the DataBox\n  /// into a TaggedTuple, should be called by the free function db::copy_items\n  template <typename... TagsOfItemsToCopy>\n  tuples::TaggedTuple<TagsOfItemsToCopy...> copy_items() const;\n\n  /// Retrieve a mutable reference to the tag `Tag`, should be called\n  /// by the free function db::get_mutable_reference\n  template <typename Tag>\n  auto& get_mutable_reference();\n\n  /// Check whether a tags depends on another tag.  Should be called\n  /// through the metafunction db::tag_depends_on.\n  template <typename Consumer, typename Provider>\n  constexpr static bool tag_depends_on();\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    // Only the mutable creation items are serialized.  Compute items are\n    // initialized to unevaluated, and mutable subitems are reinitialized\n    // cheaply.\n    pup_impl(p, mutable_item_creation_tags{});\n  }\n\n  template <typename... AddMutableItemTags, typename AddImmutableItemTagsList,\n            typename... Args>\n  constexpr DataBox(tmpl::list<AddMutableItemTags...> /*meta*/,\n                    AddImmutableItemTagsList /*meta*/, Args&&... args);\n\n private:\n  template <typename CopiedItemsTagList, typename DbTagList>\n  // clang-tidy: redundant declaration\n  friend auto copy_items(const DataBox<DbTagList>& box);\n\n  template <typename... MutateTags, typename TagList, typename Invokable,\n            typename... Args>\n  // clang-tidy: redundant declaration\n  friend decltype(auto) mutate(Invokable&& invokable,\n                               gsl::not_null<DataBox<TagList>*> box,  // NOLINT\n                               Args&&... args);                       // NOLINT\n\n  // evaluates the compute item corresponding to ComputeTag passing along\n  // items fetched via ArgumentTags\n  template <typename ComputeTag, typename... ArgumentTags>\n  void evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const;\n\n  // retrieves the reference of the ReferenceTag passing along items fetched via\n  // ArgumentTags\n  template <typename ReferenceTag, typename... ArgumentTags>\n  const auto& get_reference_item(tmpl::list<ArgumentTags...> /*meta*/) const;\n\n  // get a constant reference to the item corresponding to Tag\n  template <typename Tag>\n  const auto& get_item() const {\n    return static_cast<const detail::Item<Tag>&>(*this);\n  }\n\n  // get a mutable reference to the item corresponding to Tag\n  template <typename Tag>\n  auto& get_item() {\n    return static_cast<detail::Item<Tag>&>(*this);\n  }\n\n  ////////////////////////////////////////////////////////////////\n  // Data structure for runtime retrieval and mutation of tags.\n  struct TagGraphs {\n    std::unordered_map<std::string, const void* (DataBox::*)() const>\n        tag_retrieval_functions{};\n    std::unordered_map<std::string, std::vector<std::string>>\n        tags_and_dependents{};\n    std::unordered_map<std::string, bool (DataBox::*)()>\n        tags_and_reset_functions{};\n    std::unordered_map<std::string, std::vector<std::string>>\n        parent_to_subitem_tags{};\n    std::unordered_map<std::string, std::string> subitem_to_parent_tag{};\n    std::unordered_map<std::string, void* (DataBox::*)()>\n        tag_mutate_functions{};\n    std::unordered_map<std::string, void (DataBox::*)()>\n        mutate_mutable_subitems_functions{};\n    std::unordered_map<std::string, void (DataBox::*)()>\n        reset_compute_items_after_mutate_functions{};\n  };\n\n  /// \\cond\n  static const TagGraphs tag_graphs_;\n  /// \\endcond\n  static TagGraphs compute_tag_graphs();\n  void reset_parent(const std::string& item_name,\n                    const std::string& skip_this_subitem);\n  void reset_compute_items(const std::string& item_name);\n  template <typename MutatedTag>\n  void reset_compute_items_after_mutate();\n  void mutate_mutable_subitems(const std::string& tag_name) override;\n  void reset_compute_items_after_mutate(const std::string& tag_name) override;\n  template <typename Tag>\n  const void* get_item_as_void_pointer() const;\n  template <typename Tag>\n  void* get_item_as_void_pointer_for_mutate();\n  const void* get_item_by_name(const std::string& tag_name) const override;\n  void* mutate_item_by_name(const std::string& tag_name) override;\n  bool lock_box_for_mutate() override;\n  void unlock_box_after_mutate() override;\n  // End of runtime retrieval code\n  ////////////////////////////////////////////////////////////////\n\n  // copy item correspond to Tag\n  template <typename Tag>\n  auto copy_item() const;\n\n  // copy items corresponding to CopiedItemsTags\n  // from the DataBox to a TaggedTuple\n  template <typename... CopiedItemsTags>\n  tuples::TaggedTuple<CopiedItemsTags...> copy_items(\n      tmpl::list<CopiedItemsTags...> /*meta*/) const;\n\n  template <typename ParentTag>\n  constexpr void add_mutable_subitems_to_box(tmpl::list<> /*meta*/) {}\n\n  // add mutable items for the subitems of the item corresponding to ParentTag\n  template <typename ParentTag, typename... SubitemTags>\n  constexpr void add_mutable_subitems_to_box(\n      tmpl::list<SubitemTags...> /*meta*/);\n\n  // sets the mutable item corresponding to Tag with the ArgsIndex object in\n  // items\n  template <size_t ArgsIndex, typename Tag, typename... Ts>\n  constexpr char add_mutable_item_to_box(std::tuple<Ts...>& items);\n\n  // set the mutable items corresponding to AddMutableItemTags to the\n  // appropriate objects from items, and checks the dependencies of the\n  // immutable items corresponding to AddImmutableItemTags\n  template <typename... Ts, typename... AddMutableItemTags,\n            typename... AddImmutableItemTags, size_t... Is>\n  void add_items_to_box(std::tuple<Ts...>& items,\n                        tmpl::list<AddMutableItemTags...> /*meta*/,\n                        std::index_sequence<Is...> /*meta*/,\n                        tmpl::list<AddImmutableItemTags...> /*meta*/);\n\n  // clang-tidy: no non-const references\n  template <typename... MutableItemCreationTags>\n  void pup_impl(PUP::er& p,  // NOLINT\n                tmpl::list<MutableItemCreationTags...> /*meta*/);\n\n  // Mutating items in the DataBox\n  template <typename ParentTag>\n  constexpr void mutate_mutable_subitems(tmpl::list<> /*meta*/) {}\n\n  template <typename ParentTag, typename... Subtags>\n  constexpr void mutate_mutable_subitems(tmpl::list<Subtags...> /*meta*/);\n\n  template <typename ParentTag>\n  void mutate_mutable_subitems() {\n    return mutate_mutable_subitems<ParentTag>(\n        typename Subitems<ParentTag>::type{});\n  }\n\n  constexpr void reset_all_subitems();\n\n  /// Returns `true` if the compute tag was evaluated before reset, `false` if\n  /// it was not.\n  template <typename ImmutableItemTag>\n  bool reset_compute_item();\n  // End mutating items in the DataBox\n\n  using edge_list =\n      typename detail::create_dependency_graph<tags_list,\n                                               immutable_item_tags>::type;\n\n  bool mutate_locked_box_{false};\n};\n\n/// \\cond\ntemplate <typename... Tags>\nconst typename DataBox<tmpl::list<Tags...>>::TagGraphs\n    DataBox<tmpl::list<Tags...>>::tag_graphs_ =\n        DataBox<tmpl::list<Tags...>>::compute_tag_graphs();\n\ntemplate <typename... Tags>\nstd::string DataBox<tmpl::list<Tags...>>::print_tags() const {\n  std::ostringstream os;\n  os << \"Simple tags(\"\n     << tmpl::size<mutable_item_creation_tags>::value << \")  = [\\n\";\n  tmpl::for_each<mutable_item_creation_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"    \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"Simple tags subitems(\" << tmpl::size<mutable_subitem_tags>::value\n     << \") = [\\n\";\n  tmpl::for_each<mutable_subitem_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"    \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"Compute tags(\" << tmpl::size<compute_item_tags>::value\n     << \") = [\\n\";\n  tmpl::for_each<compute_item_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"    \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"Reference tags(\" << tmpl::size<reference_item_tags>::value\n     << \") = [\\n\";\n  tmpl::for_each<reference_item_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"    \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  return os.str();\n}\n\ntemplate <typename... Tags>\nstd::string DataBox<tmpl::list<Tags...>>::print_types() const {\n  std::ostringstream os;\n  os << \"DataBox type aliases:\\n\";\n  os << \"using tags_list = [\";\n  tmpl::for_each<tags_list>([&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n    os << \"  \" << pretty_type::get_name<Tag>() << \",\\n\";\n  });\n  os << \"];\\n\";\n  os << \"using immutable_item_tags = [\";\n  tmpl::for_each<immutable_item_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"  \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"using immutable_item_creation_tags = [\";\n  tmpl::for_each<immutable_item_creation_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"  \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"using mutable_item_tags = [\";\n  tmpl::for_each<mutable_item_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"  \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"using mutable_subitem_tags = [\";\n  tmpl::for_each<mutable_subitem_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"  \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"using compute_item_tags = [\";\n  tmpl::for_each<compute_item_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"  \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"using reference_item_tags = [\";\n  tmpl::for_each<reference_item_tags>(\n      [&os]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        os << \"  \" << pretty_type::get_name<Tag>() << \",\\n\";\n      });\n  os << \"];\\n\";\n  os << \"using edge_list = \" << pretty_type::get_name<edge_list>() << \";\\n\";\n  return os.str();\n}\n\ntemplate <typename... Tags>\ntemplate <bool PrintImmutableItems>\nstd::string DataBox<tmpl::list<Tags...>>::print_items() const {\n  std::ostringstream os;\n  os << \"Items:\\n\";\n  const auto print_item = [this, &os](auto tag_v) {\n    (void)this;\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    using type = typename tag::type;\n    os << \"----------\\n\";\n    os << \"Name:  \" << pretty_type::get_name<tag>() << \"\\n\";\n    os << \"Type:  \" << pretty_type::get_name<type>() << \"\\n\";\n    os << \"Value: \";\n    print_value(os, this->get<tag>());\n    os << \"\\n\";\n  };\n  tmpl::for_each<mutable_item_creation_tags>(print_item);\n  if constexpr (PrintImmutableItems) {\n    tmpl::for_each<immutable_item_creation_tags>(print_item);\n  }\n  return os.str();\n}\n\ntemplate <typename... Tags>\nstd::map<std::string, size_t> DataBox<tmpl::list<Tags...>>::size_of_items()\n    const {\n  std::map<std::string, size_t> result{};\n  const auto add_item_size = [this, &result](auto tag_v) {\n    (void)this;\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    if constexpr (not db::is_reference_tag_v<tag>) {\n      // For item of ItemType::Compute, this will not evaluate its function\n      // (i.e. if the item has never been evaluated its size will be that of a\n      // default initialized object)\n      const auto& item = get_item<tag>().get();\n      if constexpr (tt::is_a_v<std::unique_ptr, typename tag::type>) {\n        if (item == nullptr) {\n          result[pretty_type::get_name<tag>()] = 0;\n        } else {\n          result[pretty_type::get_name<tag>()] = size_of_object_in_bytes(*item);\n        }\n      } else {\n        result[pretty_type::get_name<tag>()] = size_of_object_in_bytes(item);\n      }\n    }\n  };\n  tmpl::for_each<mutable_item_creation_tags>(add_item_size);\n  tmpl::for_each<immutable_item_creation_tags>(add_item_size);\n  return result;\n}\n\nnamespace detail {\n// This function exists so that the user can look at the template\n// arguments to find out what triggered the static_assert.\ntemplate <typename ImmutableItemTag, typename ArgumentTag, typename TagsList>\nconstexpr char check_immutable_item_tag_dependency() {\n  using immutable_item_tag_index = tmpl::index_of<TagsList, ImmutableItemTag>;\n  static_assert(\n      std::is_same_v<ArgumentTag, Parallel::Tags::Metavariables> or\n          tmpl::less<\n              tmpl::index_if<TagsList,\n                             std::is_same<tmpl::pin<ArgumentTag>, tmpl::_1>,\n                             immutable_item_tag_index>,\n              immutable_item_tag_index>::value,\n      \"The argument_tags of an immutable item tag must be added before itself. \"\n      \"This is done to ensure no cyclic dependencies arise.  See the first and \"\n      \"second template arguments of check_immutable_item_tag_dependency for \"\n      \"the immutable item tag and its missing (or incorrectly added) argument \"\n      \"tag.  The third template argument is the TagsList of the DataBox (in \"\n      \"which the argument tag should precede the immutable item tag)\");\n  return '0';\n}\n\ntemplate <typename ImmutableItemTag, typename TagsList,\n          typename... ArgumentsTags>\nSPECTRE_ALWAYS_INLINE constexpr void check_immutable_item_tag_dependencies_impl(\n    tmpl::list<ArgumentsTags...> /*meta*/) {\n  DEBUG_STATIC_ASSERT(\n      tmpl2::flat_all_v<is_tag_v<ArgumentsTags>...>,\n      \"Cannot have non-DataBoxTag arguments to a ComputeItem or ReferenceItem. \"\n      \"Please make sure all the specified argument_tags derive from \"\n      \"db::SimpleTag or are the special tag Parallel::Tags::Metavariables.\");\n  DEBUG_STATIC_ASSERT(\n      not tmpl2::flat_any_v<std::is_same_v<ArgumentsTags, ImmutableItemTag>...>,\n      \"A ComputeItem cannot take its own Tag as an argument.\");\n  expand_pack(detail::check_immutable_item_tag_dependency<\n              ImmutableItemTag, ArgumentsTags, TagsList>()...);\n}\n\ntemplate <typename ImmutableItemTag, typename TagsList>\nSPECTRE_ALWAYS_INLINE constexpr void check_immutable_item_tag_dependencies() {\n  check_immutable_item_tag_dependencies_impl<ImmutableItemTag, TagsList>(\n      tmpl::transform<typename ImmutableItemTag::argument_tags,\n                      tmpl::bind<detail::first_matching_tag,\n                                 tmpl::pin<TagsList>, tmpl::_1>>{});\n}\n}  // namespace detail\n\ntemplate <typename... Tags>\ntemplate <typename ParentTag, typename... SubitemTags>\nSPECTRE_ALWAYS_INLINE constexpr void\ndb::DataBox<tmpl::list<Tags...>>::add_mutable_subitems_to_box(\n    tmpl::list<SubitemTags...> /*meta*/) {\n  const auto add_mutable_subitem_to_box = [this](auto tag_v) {\n    (void)this;  // Compiler bug warns this is unused\n    using subitem_tag = decltype(tag_v);\n    get_item<subitem_tag>() =\n        detail::Item<subitem_tag>(typename subitem_tag::type{});\n    Subitems<ParentTag>::template create_item<subitem_tag>(\n        make_not_null(&get_item<ParentTag>().mutate()),\n        make_not_null(&get_item<subitem_tag>().mutate()));\n  };\n\n  EXPAND_PACK_LEFT_TO_RIGHT(add_mutable_subitem_to_box(SubitemTags{}));\n}\n\ntemplate <typename... Tags>\ntemplate <size_t ArgsIndex, typename MutableItemTag, typename... Ts>\nSPECTRE_ALWAYS_INLINE constexpr char\ndb::DataBox<tmpl::list<Tags...>>::add_mutable_item_to_box(\n    std::tuple<Ts...>& items) {\n  if constexpr (sizeof...(Ts) > 0) {\n    using ArgType = std::tuple_element_t<ArgsIndex, std::tuple<Ts...>>;\n    get_item<MutableItemTag>() = detail::Item<MutableItemTag>(\n        std::forward<ArgType>(std::get<ArgsIndex>(items)));\n  }\n  add_mutable_subitems_to_box<MutableItemTag>(\n      typename Subitems<MutableItemTag>::type{});\n  return '0';  // must return in constexpr function\n}\n\n// Add items or compute items to the TaggedDeferredTuple `data`. If\n// `AddItemTags...` is an empty pack then only compute items are added, while if\n// `AddComputeTags...` is an empty pack only items are added. Items are\n// always added before compute items.\ntemplate <typename... Tags>\ntemplate <typename... Ts, typename... AddMutableItemTags,\n          typename... AddImmutableItemTags, size_t... Is>\nSPECTRE_ALWAYS_INLINE void DataBox<tmpl::list<Tags...>>::add_items_to_box(\n    std::tuple<Ts...>& items, tmpl::list<AddMutableItemTags...> /*meta*/,\n    std::index_sequence<Is...> /*meta*/,\n    tmpl::list<AddImmutableItemTags...> /*meta*/) {\n  expand_pack(add_mutable_item_to_box<Is, AddMutableItemTags>(items)...);\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      detail::check_immutable_item_tag_dependencies<AddImmutableItemTags,\n                                                    tags_list>());\n}\n\nnamespace detail {\n// This function (and its unused template argument) exist so that\n// users can see what tag has the wrong type when the static_assert\n// fails.\ntemplate <typename Tag, typename TagType, typename SuppliedType>\nconstexpr int check_initialization_argument_type() {\n  static_assert(std::is_same_v<TagType, SuppliedType>,\n                \"The type of each Tag must be the same as the type being \"\n                \"passed into the function creating the new DataBox.  See the \"\n                \"template parameters of check_initialization_argument_type for \"\n                \"the tag, expected type, and supplied type.\");\n  return 0;\n}\n}  // namespace detail\n\ntemplate <typename... Tags>\ntemplate <typename... AddMutableItemTags, typename AddImmutableItemTagsList,\n          typename... Args>\nconstexpr DataBox<tmpl::list<Tags...>>::DataBox(\n    tmpl::list<AddMutableItemTags...> /*meta*/,\n    AddImmutableItemTagsList /*meta*/, Args&&... args) {\n  DEBUG_STATIC_ASSERT(\n      sizeof...(Args) == 0 or sizeof...(Args) == sizeof...(AddMutableItemTags),\n      \"Must pass in as many arguments as AddTags, or none to \"\n      \"default-construct them.\");\n#ifdef SPECTRE_DEBUG\n  if constexpr (sizeof...(Args) > 0) {\n    // The check_argument_type call is very expensive compared to the majority\n    // of DataBox\n    expand_pack(detail::check_initialization_argument_type<\n                AddMutableItemTags, typename AddMutableItemTags::type,\n                std::decay_t<Args>>()...);\n  }\n#endif  // SPECTRE_DEBUG\n\n  std::tuple<Args&&...> args_tuple(std::forward<Args>(args)...);\n  add_items_to_box(args_tuple, tmpl::list<AddMutableItemTags...>{},\n                   std::make_index_sequence<sizeof...(AddMutableItemTags)>{},\n                   AddImmutableItemTagsList{});\n}\n\n////////////////////////////////////////////////////////////////\n// Serialization of DataBox\n\ntemplate <typename... Tags>\ntemplate <typename... MutableItemCreationTags>\nvoid DataBox<tmpl::list<Tags...>>::pup_impl(\n    PUP::er& p, tmpl::list<MutableItemCreationTags...> /*meta*/) {\n  const auto pup_simple_item = [&p, this](auto current_tag) {\n    (void)this;  // Compiler bug warning this capture is not used\n    using tag = decltype(current_tag);\n    get_item<tag>().pup(p);\n    if (p.isUnpacking()) {\n      add_mutable_subitems_to_box<tag>(typename Subitems<tag>::type{});\n    }\n  };\n  (void)pup_simple_item;  // Silence GCC warning about unused variable\n  EXPAND_PACK_LEFT_TO_RIGHT(pup_simple_item(MutableItemCreationTags{}));\n}\n\n////////////////////////////////////////////////////////////////\n// Runtime tag retrieval\ntemplate <typename... Tags>\nauto DataBox<tmpl::list<Tags...>>::compute_tag_graphs() -> TagGraphs {\n  TagGraphs result{};\n  // Compute graphs for retrieving tags\n  EXPAND_PACK_LEFT_TO_RIGHT([&result]() {\n    using tag = detail::get_base<Tags>;\n    const std::string tag_name = pretty_type::get_name<tag>();\n    if (result.tag_retrieval_functions.find(tag_name) ==\n        result.tag_retrieval_functions.end()) {\n      result.tag_retrieval_functions[tag_name] =\n          &DataBox::template get_item_as_void_pointer<Tags>;\n    } else if (result.tag_retrieval_functions[tag_name] !=\n               &DataBox::template get_item_as_void_pointer<Tags>) {\n      result.tag_retrieval_functions[tag_name] = nullptr;\n    }\n  }());\n\n  // Compute graphs for resetting compute tags\n  //\n  //\n  // Subitems are a bit tricky. We need:\n  // - If a parent tag is mutated, need to track through all subitems\n  // - If a subitem is mutated, need to track through parent tag and all\n  //   subitems\n  tmpl::for_each<\n      tmpl::append<mutable_item_parent_tags, reference_item_parent_tags,\n                   compute_item_parent_tags>>([&result](auto tag_v) {\n    // We choose to always work with the associated simple tags, so we need\n    // to get the base tag _if_ we have a compute tag.\n    using parent_tag_compute = tmpl::type_from<decltype(tag_v)>;\n    using parent_tag = detail::get_base<tmpl::type_from<decltype(tag_v)>>;\n    using subitems = typename Subitems<parent_tag_compute>::type;\n    const std::string parent_tag_name = pretty_type::get_name<parent_tag>();\n    const std::vector<std::string> subitem_tag_names =\n        pretty_type::vector_of_get_names(subitems{});\n    for (const std::string& subitem_name : subitem_tag_names) {\n      result.parent_to_subitem_tags[parent_tag_name].push_back(subitem_name);\n      ASSERT(result.subitem_to_parent_tag.find(subitem_name) ==\n                 result.subitem_to_parent_tag.end(),\n             \"Trying to insert subitem tag \"\n                 << subitem_name\n                 << \" for a second time. This is an \"\n                    \"internal inconsistency bug.\\n\");\n      result.subitem_to_parent_tag[subitem_name] = parent_tag_name;\n    }\n  });\n\n  tmpl::for_each<tmpl::append<reference_item_tags, compute_item_tags>>(\n      [&result](auto tag_v) {\n        using compute_tag = tmpl::type_from<decltype(tag_v)>;\n        using associated_simple_tag = typename compute_tag::base;\n        const std::string simple_tag =\n            pretty_type::get_name<associated_simple_tag>();\n        const std::vector<std::string> argument_tags =\n            pretty_type::vector_of_get_names(\n                typename compute_tag::argument_tags{});\n        for (const std::string& argument_tag : argument_tags) {\n          result.tags_and_dependents[argument_tag].push_back(simple_tag);\n        }\n        // If this compute tag is a subitem of another compute tag, then we\n        // need to make the parent aware that this tag depends on it.\n        if (result.subitem_to_parent_tag.find(simple_tag) !=\n            result.subitem_to_parent_tag.end()) {\n          result.tags_and_dependents[result.subitem_to_parent_tag[simple_tag]]\n              .push_back(simple_tag);\n        }\n        result.tags_and_reset_functions[simple_tag] =\n            &DataBox::template reset_compute_item<compute_tag>;\n      });\n\n  // Set mutation function\n  tmpl::for_each<mutable_item_tags>([&result]<typename Tag>(\n                                        tmpl::type_<Tag> /*meta*/) {\n    using tag = detail::get_base<Tag>;\n    const std::string tag_name = pretty_type::get_name<tag>();\n    if (result.tag_mutate_functions.find(tag_name) ==\n        result.tag_mutate_functions.end()) {\n      result.tag_mutate_functions[tag_name] =\n          &DataBox::template get_item_as_void_pointer_for_mutate<tag>;\n    } else if (result.tag_mutate_functions[tag_name] !=\n               &DataBox::template get_item_as_void_pointer_for_mutate<tag>) {\n      result.tag_mutate_functions[tag_name] = nullptr;\n    }\n\n    if (result.mutate_mutable_subitems_functions.find(tag_name) ==\n        result.mutate_mutable_subitems_functions.end()) {\n      result.mutate_mutable_subitems_functions[tag_name] =\n          static_cast<void (DataBox::*)()>(\n              &DataBox::template mutate_mutable_subitems<tag>);\n    } else if (result.mutate_mutable_subitems_functions[tag_name] !=\n               static_cast<void (DataBox::*)()>(\n                   &DataBox::template mutate_mutable_subitems<tag>)) {\n      result.mutate_mutable_subitems_functions[tag_name] = nullptr;\n    }\n\n    if (result.reset_compute_items_after_mutate_functions.find(tag_name) ==\n        result.reset_compute_items_after_mutate_functions.end()) {\n      result.reset_compute_items_after_mutate_functions[tag_name] =\n          &DataBox::template reset_compute_items_after_mutate<tag>;\n    } else if (result.reset_compute_items_after_mutate_functions[tag_name] !=\n               &DataBox::template reset_compute_items_after_mutate<tag>) {\n      result.reset_compute_items_after_mutate_functions[tag_name] = nullptr;\n    }\n  });\n  return result;\n}\n\ntemplate <typename... Tags>\nvoid DataBox<tmpl::list<Tags...>>::reset_parent(\n    const std::string& item_name, const std::string& skip_this_subitem) {\n  if (const auto parent_tag_it =\n          tag_graphs_.parent_to_subitem_tags.find(item_name);\n      parent_tag_it != tag_graphs_.parent_to_subitem_tags.end()) {\n    for (const auto& subitem_tag : parent_tag_it->second) {\n      if (subitem_tag == skip_this_subitem) {\n        continue;\n      }\n      const auto dependent_items_it =\n          tag_graphs_.tags_and_dependents.find(subitem_tag);\n      if (dependent_items_it == tag_graphs_.tags_and_dependents.end()) {\n        continue;\n      }\n      for (const std::string& dependent_item_name :\n           dependent_items_it->second) {\n        ASSERT(tag_graphs_.tags_and_reset_functions.find(dependent_item_name) !=\n                   tag_graphs_.tags_and_reset_functions.end(),\n               \"Item \" << dependent_item_name\n                       << \" does not have a reset function.\");\n        if ((this->*(tag_graphs_.tags_and_reset_functions)\n                        .at(dependent_item_name))()) {\n          if (tag_graphs_.tags_and_dependents.find(dependent_item_name) !=\n              tag_graphs_.tags_and_dependents.end()) {\n            // If the compute tag was evaluated, then we need to check the\n            // items that depend on it.\n            reset_compute_items(dependent_item_name);\n          }\n          reset_parent(dependent_item_name, \"\");\n          if (const auto parent_it =\n                  tag_graphs_.subitem_to_parent_tag.find(dependent_item_name);\n              parent_it != tag_graphs_.subitem_to_parent_tag.end()) {\n            reset_parent(parent_it->second, dependent_item_name);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename... Tags>\nvoid DataBox<tmpl::list<Tags...>>::reset_compute_items(\n    const std::string& item_name) {\n  // If the compute tag was evaluated before reset, then we reset dependent\n  // compute tags.\n  ASSERT(tag_graphs_.tags_and_dependents.find(item_name) !=\n             tag_graphs_.tags_and_dependents.end(),\n         \"Item \" << item_name << \" does not have any dependents.\");\n  for (const std::string& dependent_item_name :\n       tag_graphs_.tags_and_dependents.at(item_name)) {\n    ASSERT(\n        tag_graphs_.tags_and_reset_functions.find(dependent_item_name) !=\n            tag_graphs_.tags_and_reset_functions.end(),\n        \"Item \" << dependent_item_name << \" does not have a reset function.\");\n    if ((this->*(tag_graphs_.tags_and_reset_functions)\n                    .at(dependent_item_name))()) {\n      reset_parent(dependent_item_name, \"\");\n      if (const auto parent_it =\n              tag_graphs_.subitem_to_parent_tag.find(dependent_item_name);\n          parent_it != tag_graphs_.subitem_to_parent_tag.end()) {\n        reset_parent(parent_it->second, dependent_item_name);\n      }\n      if (tag_graphs_.tags_and_dependents.find(dependent_item_name) !=\n          tag_graphs_.tags_and_dependents.end()) {\n        // If the compute tag was evaluated, then we need to check the items\n        // that depend on it.\n        reset_compute_items(dependent_item_name);\n      }\n    }\n  }\n  // If this tag is a parent tag, reset subitems and their dependents\n  reset_parent(item_name, \"\");\n  // If this tag is a subitem, reset parent and other subitem dependents\n  if (const auto parent_it = tag_graphs_.subitem_to_parent_tag.find(item_name);\n      parent_it != tag_graphs_.subitem_to_parent_tag.end()) {\n    reset_parent(parent_it->second, item_name);\n  }\n}\n\ntemplate <typename... Tags>\ntemplate <typename MutatedTag>\nvoid DataBox<tmpl::list<Tags...>>::reset_compute_items_after_mutate() {\n  using tag = detail::get_base<MutatedTag>;\n  static const std::string mutated_tag = pretty_type::get_name<tag>();\n  if (tag_graphs_.tags_and_dependents.find(mutated_tag) !=\n      tag_graphs_.tags_and_dependents.end()) {\n    reset_compute_items(mutated_tag);\n  }\n\n  // Handled subitems\n  if constexpr (detail::has_subitems<MutatedTag>::value) {\n    ASSERT(tag_graphs_.parent_to_subitem_tags.find(mutated_tag) !=\n               tag_graphs_.parent_to_subitem_tags.end(),\n           \"The parent tag \"\n               << mutated_tag\n               << \" is not in the set of parent_to_subitem_tags_. This is \"\n                  \"an internal inconsistency bug.\\n\");\n    for (const auto& subitem_name :\n         tag_graphs_.parent_to_subitem_tags.at(mutated_tag)) {\n      if (tag_graphs_.tags_and_dependents.find(subitem_name) !=\n          tag_graphs_.tags_and_dependents.end()) {\n        reset_compute_items(subitem_name);\n      }\n    }\n  }\n  // Handle parent tags\n  if constexpr (tmpl::list_contains_v<mutable_subitem_tags, MutatedTag>) {\n    ASSERT(tag_graphs_.subitem_to_parent_tag.find(mutated_tag) !=\n               tag_graphs_.subitem_to_parent_tag.end(),\n           \"Expected to find a parent tag of \"\n               << mutated_tag\n               << \" but did not. This is an internal inconsistency bug.\");\n    const auto& parent_tag_name =\n        tag_graphs_.subitem_to_parent_tag.at(mutated_tag);\n    if (tag_graphs_.tags_and_dependents.find(parent_tag_name) !=\n        tag_graphs_.tags_and_dependents.end()) {\n      reset_compute_items(parent_tag_name);\n    }\n    for (const auto& subitem_name :\n         tag_graphs_.parent_to_subitem_tags.at(parent_tag_name)) {\n      if (tag_graphs_.tags_and_dependents.find(subitem_name) !=\n              tag_graphs_.tags_and_dependents.end() and\n          subitem_name != mutated_tag) {\n        reset_compute_items(subitem_name);\n      }\n    }\n  }\n}\n\ntemplate <typename... Tags>\nvoid DataBox<tmpl::list<Tags...>>::mutate_mutable_subitems(\n    const std::string& tag_name) {\n  ASSERT(tag_graphs_.mutate_mutable_subitems_functions.find(tag_name) !=\n             tag_graphs_.mutate_mutable_subitems_functions.end(),\n         \"Can't find tag \" << tag_name\n                           << \" when mutating. Either the tag is not mutable \"\n                              \"or this is an internal inconsistency bug.\\n\");\n  (this->*tag_graphs_.mutate_mutable_subitems_functions.at(tag_name))();\n}\n\ntemplate <typename... Tags>\nvoid DataBox<tmpl::list<Tags...>>::reset_compute_items_after_mutate(\n    const std::string& tag_name) {\n  ASSERT(\n      tag_graphs_.reset_compute_items_after_mutate_functions.find(tag_name) !=\n          tag_graphs_.reset_compute_items_after_mutate_functions.end(),\n      \"Can't find tag \" << tag_name\n                        << \" when mutating. Either the tag is not mutable \"\n                           \"or this is an internal inconsistency bug.\\n\");\n  (this->*tag_graphs_.reset_compute_items_after_mutate_functions.at(\n              tag_name))();\n}\n\ntemplate <typename... Tags>\ntemplate <typename Tag>\nconst void* DataBox<tmpl::list<Tags...>>::get_item_as_void_pointer() const {\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  return reinterpret_cast<const void*>(&this->template get<Tag>());\n}\n\ntemplate <typename... Tags>\ntemplate <typename Tag>\nvoid* DataBox<tmpl::list<Tags...>>::get_item_as_void_pointer_for_mutate() {\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  return reinterpret_cast<void*>(\n      &this->template get_item<\n          detail::first_matching_tag<tmpl::list<Tags...>, Tag>>());\n}\n\ntemplate <typename... Tags>\nconst void* DataBox<tmpl::list<Tags...>>::get_item_by_name(\n    const std::string& tag_name) const {\n  ASSERT(tag_graphs_.tag_retrieval_functions.find(tag_name) !=\n             tag_graphs_.tag_retrieval_functions.end(),\n         \"Tag with name \" << tag_name\n                          << \" is not found in the DataBox. Known tags are:\\n\"\n                          << keys_of(tag_graphs_.tag_retrieval_functions));\n  ASSERT(tag_graphs_.tag_retrieval_functions.at(tag_name) != nullptr,\n         \"Tag with name \" << tag_name\n                          << \" is in the DataBox more than once and so \"\n                             \"cannot be retrieved by name.\");\n\n  return (this->*tag_graphs_.tag_retrieval_functions.at(tag_name))();\n}\n\ntemplate <typename... Tags>\nvoid* DataBox<tmpl::list<Tags...>>::mutate_item_by_name(\n    const std::string& tag_name) {\n  ASSERT(tag_graphs_.tag_mutate_functions.find(tag_name) !=\n             tag_graphs_.tag_mutate_functions.end(),\n         \"Cannot mutate tag: \" << tag_name << \".  Known tags are:\\n\"\n                               << keys_of(tag_graphs_.tag_mutate_functions));\n  return (this->*tag_graphs_.tag_mutate_functions.at(tag_name))();\n}\n\ntemplate <typename... Tags>\nbool DataBox<tmpl::list<Tags...>>::lock_box_for_mutate() {\n  const bool temp = mutate_locked_box_;\n  mutate_locked_box_ = true;\n  return temp;\n}\n\ntemplate <typename... Tags>\nvoid DataBox<tmpl::list<Tags...>>::unlock_box_after_mutate() {\n  mutate_locked_box_ = false;\n}\n\n////////////////////////////////////////////////////////////////\n// Mutating items in the DataBox\n// Classes and functions necessary for db::mutate to work\n\ntemplate <typename... Tags>\ntemplate <typename ImmutableItemTag>\nSPECTRE_ALWAYS_INLINE bool DataBox<tmpl::list<Tags...>>::reset_compute_item() {\n  // reference items do not need to be reset, but it still needs to be handled\n  // here. Partly just so we get an error if we are calling this function\n  // incorrectly and because we need to call this function during resets\n  // to get dependency tracking correct when compute tags depend on reference\n  // tags.\n  if constexpr (db::is_compute_tag_v<ImmutableItemTag>) {\n    const bool was_evaluated = get_item<ImmutableItemTag>().evaluated();\n    get_item<ImmutableItemTag>().reset();\n    return was_evaluated;\n  } else if constexpr (db::is_reference_tag_v<ImmutableItemTag>) {\n    return true;\n  }\n  ERROR(\n      \"Trying to reset a tag that is neither a compute or reference tag. This \"\n      \"is an implementation bug. A decision needs to be made as to whether the \"\n      \"tag was previously evaluated or not. If it was not, then anything that \"\n      \"depends on this tag will not be reset.\");\n  return false;\n}\n\ntemplate <typename... Tags>\ntemplate <typename ParentTag, typename... Subtags>\nSPECTRE_ALWAYS_INLINE constexpr void\ndb::DataBox<tmpl::list<Tags...>>::mutate_mutable_subitems(\n    tmpl::list<Subtags...> /*meta*/) {\n  const auto helper = [this](auto tag_v) {\n    (void)this;  // Compiler bug warns about unused this capture\n    using tag = decltype(tag_v);\n    Subitems<ParentTag>::template create_item<tag>(\n        make_not_null(&get_item<ParentTag>().mutate()),\n        make_not_null(&get_item<tag>().mutate()));\n  };\n\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(Subtags{}));\n}\n\ntemplate <typename... Tags>\nSPECTRE_ALWAYS_INLINE constexpr void\ndb::DataBox<tmpl::list<Tags...>>::reset_all_subitems() {\n  tmpl::for_each<mutable_item_tags>([this](auto tag) {\n    using Tag = tmpl::type_from<decltype(tag)>;\n    this->mutate_mutable_subitems<Tag>(typename Subitems<Tag>::type{});\n  });\n}\n/// \\endcond\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Allows changing the state of one or more non-computed elements in\n * the DataBox\n *\n * `mutate()`'s second argument is the DataBox from which to retrieve the tags\n * `MutateTags`. The objects corresponding to the `MutateTags` are then passed\n * to `invokable`, which is a lambda or a function object taking as many\n * arguments as there are `MutateTags` and with the arguments being of types\n * `gsl::not_null<db::item_type<MutateTags>*>...`. Inside the `invokable` no\n * items can be retrieved from the DataBox `box`. This is to avoid confusing\n * subtleties with order of evaluation of compute items, as well as dangling\n * references. If an `invokable` needs read access to items in `box` they should\n * be passed as additional arguments to `mutate`. Capturing them by reference in\n * a lambda does not work because of a bug in GCC 6.3 and earlier. For a\n * function object the read-only items can also be stored as const references\n * inside the object by passing `db::get<TAG>(t)` to the constructor.\n *\n * \\example\n * \\snippet Test_DataBox.cpp databox_mutate_example\n *\n * The `invokable` may have function return values, and any returns are\n * forwarded as returns to the `db::mutate` call.\n *\n * For convenience in generic programming, if `::Tags::DataBox` is\n * passed as the sole `MutateTags` parameter, this function will\n * simply call its first argument with its remaining arguments.\n * Except for this case, `::Tags::DataBox` is not usable with this\n * function.\n *\n * \\warning Using `db::mutate` returns to obtain non-const references or\n * pointers to box items is potentially very dangerous. The \\ref DataBoxGroup\n * \"DataBox\" cannot track any subsequent changes to quantities that have been\n * \"unsafely\" extracted in this manner, so we consider it undefined behavior to\n * return pointers or references to \\ref DataBoxGroup \"DataBox\" contents.\n */\ntemplate <typename... MutateTags, typename TagList, typename Invokable,\n          typename... Args>\ndecltype(auto) mutate(Invokable&& invokable,\n                      const gsl::not_null<DataBox<TagList>*> box,\n                      Args&&... args) {\n  if constexpr ((... or std::is_same_v<MutateTags, Tags::DataBox>)) {\n    // This branch doesn't directly access the box, so no locking or\n    // resetting is necessary.\n    static_assert(\n        std::is_same_v<tmpl::list<MutateTags...>, tmpl::list<Tags::DataBox>>,\n        \"Cannot mutate other tags when obtaining the mutable DataBox.\");\n    return invokable(box, std::forward<Args>(args)...);\n  } else {\n    static_assert(\n        tmpl2::flat_all_v<\n            detail::has_unique_matching_tag_v<TagList, MutateTags>...>,\n        \"One of the tags being mutated could not be found in the DataBox or \"\n        \"is a base tag identifying more than one tag.\");\n    static_assert(tmpl2::flat_all_v<tmpl::list_contains_v<\n                      typename DataBox<TagList>::mutable_item_tags,\n                      detail::first_matching_tag<TagList, MutateTags>>...>,\n                  \"Can only mutate mutable items\");\n    if (UNLIKELY(box->mutate_locked_box_)) {\n      ERROR(\n          \"Unable to mutate a DataBox that is already being mutated. This \"\n          \"error occurs when mutating a DataBox from inside the invokable \"\n          \"passed to the mutate function.\");\n    }\n\n    const CleanupRoutine unlock_box = [&box]() {\n      box->mutate_locked_box_ = false;\n      EXPAND_PACK_LEFT_TO_RIGHT(\n          box->template mutate_mutable_subitems<MutateTags>());\n      EXPAND_PACK_LEFT_TO_RIGHT(\n          box->template reset_compute_items_after_mutate<\n              detail::first_matching_tag<TagList, MutateTags>>());\n    };\n    box->mutate_locked_box_ = true;\n    return invokable(\n        make_not_null(&box->template get_item<\n                              detail::first_matching_tag<TagList, MutateTags>>()\n                           .mutate())...,\n        std::forward<Args>(args)...);\n  }\n}\n\n////////////////////////////////////////////////////////////////\n// Retrieving items from the DataBox\n\n/// \\cond\ntemplate <typename... Tags>\ntemplate <typename ComputeTag, typename... ArgumentTags>\nvoid DataBox<tmpl::list<Tags...>>::evaluate_compute_item(\n    tmpl::list<ArgumentTags...> /*meta*/) const {\n  get_item<ComputeTag>().evaluate(get<ArgumentTags>()...);\n}\n\ntemplate <typename... Tags>\ntemplate <typename ReferenceTag, typename... ArgumentTags>\nconst auto& DataBox<tmpl::list<Tags...>>::get_reference_item(\n    tmpl::list<ArgumentTags...> /*meta*/) const {\n  return ReferenceTag::get(get<ArgumentTags>()...);\n}\n\ntemplate <typename... Tags>\ntemplate <typename Tag>\nconst auto& DataBox<tmpl::list<Tags...>>::get() const {\n  if constexpr (std::is_same_v<Tag, ::Tags::DataBox>) {\n    if (UNLIKELY(mutate_locked_box_)) {\n      ERROR(\n          \"Unable to retrieve a (compute) item 'DataBox' from the DataBox from \"\n          \"within a call to mutate. You must pass these either through the \"\n          \"capture list of the lambda or the constructor of a class, this \"\n          \"restriction exists to avoid complexity.\");\n    }\n    return *this;\n  } else if constexpr (std::is_same_v<Tag, Parallel::Tags::Metavariables>) {\n    static_assert(not std::is_same_v<metavars_tag, NoSuchType>);\n    return get_item<metavars_tag>().get();\n  } else {\n    DEBUG_STATIC_ASSERT(\n        not detail::has_no_matching_tag_v<tags_list, Tag>,\n        \"Found no tags in the DataBox that match the tag being retrieved.\");\n    DEBUG_STATIC_ASSERT(\n        detail::has_unique_matching_tag_v<tags_list, Tag>,\n        \"Found more than one tag in the DataBox that matches the tag \"\n        \"being retrieved. This happens because more than one tag with the same \"\n        \"base (class) tag was added to the DataBox.\");\n    using item_tag = detail::first_matching_tag<tags_list, Tag>;\n    if (UNLIKELY(mutate_locked_box_)) {\n      ERROR(\"Unable to retrieve a (compute) item '\"\n            << db::tag_name<item_tag>()\n            << \"' from the DataBox from within a \"\n               \"call to mutate. You must pass these either through the capture \"\n               \"list of the lambda or the constructor of a class, this \"\n               \"restriction exists to avoid complexity.\");\n    }\n    if constexpr (detail::Item<item_tag>::item_type ==\n                  detail::ItemType::Reference) {\n      return get_reference_item<item_tag>(typename item_tag::argument_tags{});\n    } else {\n      if constexpr (detail::Item<item_tag>::item_type ==\n                    detail::ItemType::Compute) {\n        if (not get_item<item_tag>().evaluated()) {\n          evaluate_compute_item<item_tag>(typename item_tag::argument_tags{});\n        }\n      }\n      if constexpr (tt::is_a_v<std::unique_ptr, typename item_tag::type>) {\n        return *(get_item<item_tag>().get());\n      } else {\n        return get_item<item_tag>().get();\n      }\n    }\n  }\n}\n/// \\endcond\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Retrieve the item with tag `Tag` from the DataBox\n * \\requires Type `Tag` is one of the Tags corresponding to an object stored in\n * the DataBox\n *\n * \\return The object corresponding to the tag `Tag`\n */\ntemplate <typename Tag, typename TagList>\nSPECTRE_ALWAYS_INLINE const auto& get(const DataBox<TagList>& box) {\n  return box.template get<Tag>();\n}\n\n////////////////////////////////////////////////////////////////\n// Copy mutable creation items from the DataBox\n\n/// \\cond\ntemplate <typename... Tags>\ntemplate <typename Tag>\nSPECTRE_ALWAYS_INLINE auto DataBox<tmpl::list<Tags...>>::copy_item() const {\n  using item_tag = detail::first_matching_tag<tags_list, Tag>;\n  using item_type = typename item_tag::type;\n  static_assert(tmpl::list_contains_v<mutable_item_creation_tags, item_tag>,\n                \"Can only copy mutable creation items\");\n  return serialize_and_deserialize<item_type>(get_item<item_tag>().get());\n}\n\ntemplate <typename... DbTags>\ntemplate <typename... CopiedItemsTags>\ntuples::TaggedTuple<CopiedItemsTags...>\nDataBox<tmpl::list<DbTags...>>::copy_items(\n    tmpl::list<CopiedItemsTags...> /*meta*/) const {\n  return tuples::TaggedTuple<CopiedItemsTags...>{\n      copy_item<CopiedItemsTags>()...};\n}\n/// \\endcond\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Copy the items from the DataBox into a TaggedTuple\n *\n * \\return The objects corresponding to CopiedItemsTagList\n *\n * \\note The tags in CopiedItemsTagList must be a subset of\n * the mutable_item_creation_tags of the DataBox\n */\ntemplate <typename CopiedItemsTagList, typename DbTagList>\nSPECTRE_ALWAYS_INLINE auto copy_items(const DataBox<DbTagList>& box) {\n  return box.copy_items(CopiedItemsTagList{});\n}\n\n////////////////////////////////////////////////////////////////\n// Get mutable reference from the DataBox\n/// \\cond\ntemplate <typename... Tags>\ntemplate <typename Tag>\nauto& DataBox<tmpl::list<Tags...>>::get_mutable_reference() {\n  DEBUG_STATIC_ASSERT(\n      not detail::has_no_matching_tag_v<tmpl::list<Tags...>, Tag>,\n      \"Found no tags in the DataBox that match the tag being retrieved.\");\n  DEBUG_STATIC_ASSERT(\n      detail::has_unique_matching_tag_v<tmpl::list<Tags...>, Tag>,\n      \"Found more than one tag in the DataBox that matches the tag \"\n      \"being retrieved. This happens because more than one tag with the same \"\n      \"base (class) tag was added to the DataBox.\");\n\n  using item_tag = detail::first_matching_tag<tmpl::list<Tags...>, Tag>;\n\n  DEBUG_STATIC_ASSERT(tmpl::list_contains_v<mutable_item_tags, item_tag>,\n                      \"Can only mutate mutable items\");\n\n  DEBUG_STATIC_ASSERT(\n      not (... or\n           tmpl::list_contains_v<typename Subitems<Tags>::type, item_tag>),\n      \"Cannot extract references to subitems\");\n  DEBUG_STATIC_ASSERT(not detail::has_subitems_v<item_tag>,\n                      \"Cannot extract references to items with subitems.\");\n\n  DEBUG_STATIC_ASSERT(\n      tmpl::none<edge_list, std::is_same<tmpl::pin<item_tag>,\n                                         tmpl::get_source<tmpl::_1>>>::value,\n      \"Cannot extract references to items used by compute items.\");\n\n  return get_item<item_tag>().mutate();\n}\n\ntemplate <typename... Tags>\ntemplate <typename Consumer, typename Provider>\nconstexpr bool DataBox<tmpl::list<Tags...>>::tag_depends_on() {\n  // We need to check for things depending on the passed tag, any\n  // subitems, and the parent item if we were passed a subitem.  These\n  // dependencies are handled internally by the mutation functions and\n  // not encoded in the graph.\n  using provider_aliases =\n      tmpl::push_front<typename Subitems<Provider>::type,\n                       creation_tag<Provider, DataBox<tmpl::list<Tags...>>>>;\n\n  // We have to replace subitems with their parents here because\n  // subitems of compute tags sometimes get graph edges from their\n  // parents and sometimes do not, depending on if they have\n  // dependencies.\n  using consumer_tag_to_check =\n      creation_tag<Consumer, DataBox<tmpl::list<Tags...>>>;\n\n  // We cannot recursively call the function we are in, because we\n  // need to avoid the normalization done above.  Otherwise we could\n  // end up in a loop when destination of an edge normalizes to its\n  // source.\n\n  // Lambdas cannot capture themselves, but they can take themselves\n  // as an argument.\n  auto check_dependents = [](auto&& recurse,\n                             auto node_depending_on_provider_v) {\n    using node_depending_on_provider = decltype(node_depending_on_provider_v);\n    if (std::is_same_v<node_depending_on_provider, consumer_tag_to_check>) {\n      return true;\n    }\n\n    using next_nodes_to_check = tmpl::transform<\n        tmpl::filter<\n            edge_list,\n            tmpl::has_source<tmpl::_1, tmpl::pin<node_depending_on_provider>>>,\n        tmpl::get_destination<tmpl::_1>>;\n\n    return tmpl::as_pack<next_nodes_to_check>([&](auto... nodes) {\n      return (... or recurse(recurse, tmpl::type_from<decltype(nodes)>{}));\n    });\n  };\n\n  return tmpl::as_pack<provider_aliases>([&](auto... aliases) {\n    return (... or check_dependents(check_dependents,\n                                    tmpl::type_from<decltype(aliases)>{}));\n  });\n}\n/// \\endcond\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Retrieve a mutable reference to the item with tag `Tag` from the\n * DataBox.\n *\n * The tag retrieved cannot be used by any compute tags, cannot have\n * subitems, and cannot itself be a subitem.  These requirements\n * prevent changes to the retrieved item from affecting any other tags\n * in the DataBox, so it can safely be modified without causing\n * internal inconsistencies.\n */\ntemplate <typename Tag, typename TagList>\nSPECTRE_ALWAYS_INLINE auto& get_mutable_reference(\n    const gsl::not_null<DataBox<TagList>*> box) {\n  return box->template get_mutable_reference<Tag>();\n}\n\n/// @{\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Check whether the tag \\p Consumer depends on the tag \\p\n * Provider in a given \\p Box.\n *\n * This is equivalent to the question of whether changing \\p Provider\n * (through `db::mutate`, updating one of its dependencies, etc.)\n * could change the value of `db::get<Consumer>`.  Tags depend on\n * themselves, and an item and its subitems all depend on one another.\n */\ntemplate <typename Consumer, typename Provider, typename Box>\nusing tag_depends_on =\n    std::bool_constant<Box::template tag_depends_on<Consumer, Provider>()>;\n\ntemplate <typename Consumer, typename Provider, typename Box>\nconstexpr bool tag_depends_on_v =\n    tag_depends_on<Consumer, Provider, Box>::value;\n/// @}\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief List of Tags to add to the DataBox\n */\ntemplate <typename... Tags>\nusing AddSimpleTags = tmpl::flatten<tmpl::list<Tags...>>;\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief List of Compute Item Tags to add to the DataBox\n */\ntemplate <typename... Tags>\nusing AddComputeTags = tmpl::flatten<tmpl::list<Tags...>>;\n\nnamespace detail {\ntemplate <class TagList>\nstruct compute_dbox_type {\n  using immutable_item_tags = detail::expand_subitems<\n      tmpl::filter<TagList, db::is_immutable_item_tag<tmpl::_1>>>;\n  using mutable_item_tags = detail::expand_subitems<\n      tmpl::filter<TagList, db::is_mutable_item_tag<tmpl::_1>>>;\n  using type = DataBox<tmpl::append<mutable_item_tags, immutable_item_tags>>;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Returns the type of the DataBox that would be constructed from the\n * `TagList` of tags.\n */\ntemplate <typename TagList>\nusing compute_databox_type = typename detail::compute_dbox_type<TagList>::type;\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Create a new DataBox\n *\n * \\details\n * Creates a new DataBox holding types Tags::type filled with the arguments\n * passed to the function. Compute and reference items must be added so that\n * their dependencies are added before themselves. For example, say you have\n * compute items `A` and `B` where `B` depends on `A`, then you must add them\n * using `db::AddImmutableItemTags<A, B>`.\n *\n * \\example\n * \\snippet Test_DataBox.cpp create_databox\n *\n * \\see create_from\n *\n * \\tparam AddMutableItemTags the tags of the mutable items that are being added\n * \\tparam AddImmutableItemTags list of \\ref ComputeTag \"compute item tags\" and\n *         \\ref ReferenceTag \"refernce item tags\" to add to the DataBox\n * \\param args the initial values for the mutable items to add to the DataBox\n */\ntemplate <typename AddMutableItemTags,\n          typename AddImmutableItemTags = tmpl::list<>, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr auto create(Args&&... args) {\n  static_assert(tt::is_a_v<tmpl::list, AddImmutableItemTags>,\n                \"AddImmutableItemTags must be a tmpl::list\");\n  static_assert(tt::is_a_v<tmpl::list, AddMutableItemTags>,\n                \"AddMutableItemTags must be a tmpl::list\");\n  static_assert(\n      tmpl::all<AddMutableItemTags, is_creation_tag<tmpl::_1>>::value and\n          tmpl::all<AddImmutableItemTags, is_creation_tag<tmpl::_1>>::value,\n      \"Can only add tags derived from db::SimpleTag.\");\n  static_assert(\n      tmpl::all<AddMutableItemTags, is_mutable_item_tag<tmpl::_1>>::value,\n      \"Cannot add any ComputeTags or ReferenceTags in the AddMutableTags list, \"\n      \"must use the AddImmutableItemTags list.\");\n  static_assert(\n      tmpl::all<AddImmutableItemTags, is_immutable_item_tag<tmpl::_1>>::value,\n      \"Cannot add any SimpleTags in the AddImmutableItemTags list, must use \"\n      \"the \"\n      \"AddMutableItemTags list.\");\n\n  using mutable_item_tags = detail::expand_subitems<AddMutableItemTags>;\n  using immutable_item_tags = detail::expand_subitems<AddImmutableItemTags>;\n\n  return db::DataBox<tmpl::append<mutable_item_tags, immutable_item_tags>>(\n      AddMutableItemTags{}, AddImmutableItemTags{},\n      std::forward<Args>(args)...);\n}\n\nnamespace detail {\ntemplate <typename DataBoxTags, typename... TagsToRetrieve>\nconstexpr bool check_tags_are_in_databox(\n    DataBoxTags /*meta*/, tmpl::list<TagsToRetrieve...> /*meta*/) {\n  static_assert(\n      (tag_is_retrievable_v<TagsToRetrieve, DataBox<DataBoxTags>> and ...),\n      \"A desired tag is not in the DataBox.  See the first template \"\n      \"argument of tag_is_retrievable_v for the missing tag, and the \"\n      \"second for the available tags.\");\n  return true;\n}\n\ntemplate <typename... ArgumentTags, typename F, typename BoxTags,\n          typename... Args>\nstatic constexpr auto apply(F&& f, const DataBox<BoxTags>& box,\n                            tmpl::list<ArgumentTags...> /*meta*/,\n                            Args&&... args) {\n  if constexpr (is_apply_callable_v<\n                    F,\n                    tmpl::conditional_t<\n                        std::is_same_v<ArgumentTags, ::Tags::DataBox>,\n                        const DataBox<BoxTags>&,\n                        const_item_type<ArgumentTags, BoxTags>>...,\n                    Args...>) {\n    return F::apply(::db::get<ArgumentTags>(box)...,\n                    std::forward<Args>(args)...);\n  } else if constexpr (::tt::is_callable_v<\n                           std::remove_pointer_t<F>,\n                           tmpl::conditional_t<\n                               std::is_same_v<ArgumentTags, ::Tags::DataBox>,\n                               const DataBox<BoxTags>&,\n                               const_item_type<ArgumentTags, BoxTags>>...,\n                           Args...>) {\n    return std::forward<F>(f)(::db::get<ArgumentTags>(box)...,\n                              std::forward<Args>(args)...);\n  } else {\n    error_function_not_callable<\n        std::remove_pointer_t<F>,\n        tmpl::conditional_t<std::is_same_v<ArgumentTags, ::Tags::DataBox>,\n                            const DataBox<BoxTags>&,\n                            const_item_type<ArgumentTags, BoxTags>>...,\n        Args...>();\n  }\n}\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Apply the invokable `f` with argument Tags `TagsList` from\n * DataBox `box`\n *\n * \\details\n * `f` must either be invokable with the arguments of type\n * `db::const_item_type<TagsList>..., Args...` where the first pack expansion\n * is over the elements in the type list `ArgumentTags`, or have a static\n * `apply` function that is callable with the same types.\n * If the class that implements the static `apply` functions also provides an\n * `argument_tags` typelist, then it is used and no explicit `ArgumentTags`\n * template parameter should be specified.\n *\n * \\usage\n * Given a function `func` that takes arguments of types\n * `T1`, `T2`, `A1` and `A2`. Let the Tags for the quantities of types `T1` and\n * `T2` in the DataBox `box` be `Tag1` and `Tag2`, and objects `a1` of type\n * `A1` and `a2` of type `A2`, then\n * \\code\n * auto result = db::apply<tmpl::list<Tag1, Tag2>>(func, box, a1, a2);\n * \\endcode\n * \\return `decltype(func(db::get<Tag1>(box), db::get<Tag2>(box), a1, a2))`\n *\n * \\semantics\n * For tags `Tags...` in a DataBox `box`, and a function `func` that takes\n * `sizeof...(Tags)` arguments of types `db::const_item_type<Tags>...`,  and\n * `sizeof...(Args)` arguments of types `Args...`,\n * \\code\n * result = func(box, db::get<Tags>(box)..., args...);\n * \\endcode\n *\n * \\example\n * \\snippet Test_DataBox.cpp apply_example\n * Using a struct with an `apply` method:\n * \\snippet Test_DataBox.cpp apply_struct_example\n * If the class `F` has no state, you can also use the stateless overload of\n * `apply`: \\snippet Test_DataBox.cpp apply_stateless_struct_example\n *\n * \\see DataBox\n * \\tparam ArgumentTags typelist of Tags in the order that they are to be passed\n * to `f`\n * \\tparam F The invokable to apply\n */\ntemplate <typename ArgumentTags, typename F, typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr auto apply(F&& f, const DataBox<BoxTags>& box,\n                                           Args&&... args) {\n  detail::check_tags_are_in_databox(\n      BoxTags{}, tmpl::remove<ArgumentTags, ::Tags::DataBox>{});\n  return detail::apply(std::forward<F>(f), box, ArgumentTags{},\n                       std::forward<Args>(args)...);\n}\n\ntemplate <typename F, typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr auto apply(F&& f, const DataBox<BoxTags>& box,\n                                           Args&&... args) {\n  return apply<typename std::decay_t<F>::argument_tags>(\n      std::forward<F>(f), box, std::forward<Args>(args)...);\n}\n\ntemplate <typename F, typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr auto apply(const DataBox<BoxTags>& box,\n                                           Args&&... args) {\n  return apply(F{}, box, std::forward<Args>(args)...);\n}\n/// @}\n\nnamespace detail {\ntemplate <typename Tag, typename BoxTags>\nusing tag_return_type =\n    tmpl::conditional_t<std::is_same_v<Tag, ::Tags::DataBox>,\n                        db::DataBox<BoxTags>, typename Tag::type>;\n\ntemplate <typename... ReturnTags, typename... ArgumentTags, typename F,\n          typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(\n    F&& f, const gsl::not_null<db::DataBox<BoxTags>*> box,\n    tmpl::list<ReturnTags...> /*meta*/, tmpl::list<ArgumentTags...> /*meta*/,\n    Args&&... args) {\n  if constexpr (sizeof...(ReturnTags) == 0) {\n    return apply<tmpl::list<ArgumentTags...>>(std::forward<F>(f), *box,\n                                              std::forward<Args>(args)...);\n  } else {\n    detail::check_tags_are_in_databox(BoxTags{}, tmpl::list<ReturnTags...>{});\n    detail::check_tags_are_in_databox(BoxTags{}, tmpl::list<ArgumentTags...>{});\n    static_assert(not(... or std::is_same_v<ArgumentTags, Tags::DataBox>),\n                  \"Cannot pass Tags::DataBox to mutate_apply when mutating \"\n                  \"since the db::get won't work inside mutate_apply.\");\n    if constexpr (is_apply_callable_v<\n                      F,\n                      const gsl::not_null<\n                          tag_return_type<ReturnTags, BoxTags>*>...,\n                      const_item_type<ArgumentTags, BoxTags>..., Args...>) {\n      return ::db::mutate<ReturnTags...>(\n          [](const gsl::not_null<\n                 tag_return_type<ReturnTags, BoxTags>*>... mutated_items,\n             const_item_type<ArgumentTags, BoxTags>... args_items,\n             decltype(std::forward<Args>(args))... l_args) {\n            return std::decay_t<F>::apply(mutated_items..., args_items...,\n                                          std::forward<Args>(l_args)...);\n          },\n          box, db::get<ArgumentTags>(*box)..., std::forward<Args>(args)...);\n    } else if constexpr (\n        ::tt::is_callable_v<\n            F, const gsl::not_null<tag_return_type<ReturnTags, BoxTags>*>...,\n            const_item_type<ArgumentTags, BoxTags>..., Args...>) {\n      return ::db::mutate<ReturnTags...>(f, box, db::get<ArgumentTags>(*box)...,\n                                         std::forward<Args>(args)...);\n    } else {\n      error_function_not_callable<\n          F, gsl::not_null<tag_return_type<ReturnTags, BoxTags>*>...,\n          const_item_type<ArgumentTags, BoxTags>..., Args...>();\n    }\n  }\n}\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Apply the invokable `f` mutating items `MutateTags` and taking as\n * additional arguments `ArgumentTags` and `args`.\n *\n * \\details\n * `f` must either be invokable with the arguments of type\n * `gsl::not_null<db::item_type<MutateTags>*>...,\n * db::const_item_type<ArgumentTags>..., Args...`\n * where the first two pack expansions are over the elements in the typelists\n * `MutateTags` and `ArgumentTags`, or have a static `apply` function that is\n * callable with the same types. If the type of `f` specifies `return_tags` and\n * `argument_tags` typelists, these are used for the `MutateTags` and\n * `ArgumentTags`, respectively.\n *\n * Any return values of the invokable `f` are forwarded as returns to the\n * `mutate_apply` call.\n *\n * \\note If `MutateTags` is empty this will forward to `db::apply`, so\n * `::Tags::DataBox` will be available.  Otherwise it will call\n * `db::mutate`.  See those functions for more details on retrieving\n * the entire box.\n *\n * \\example\n * An example of using `mutate_apply` with a lambda:\n * \\snippet Test_DataBox.cpp mutate_apply_lambda_example\n *\n * An example of a class with a static `apply` function\n * \\snippet Test_DataBox.cpp mutate_apply_struct_definition_example\n * and how to use `mutate_apply` with the above class\n * \\snippet Test_DataBox.cpp mutate_apply_struct_example_stateful\n * Note that the class exposes `return_tags` and `argument_tags` typelists, so\n * we don't specify the template parameters explicitly.\n * If the class `F` has no state, like in this example,\n * \\snippet Test_DataBox.cpp mutate_apply_struct_definition_example\n * you can also use the stateless overload of `mutate_apply`:\n * \\snippet Test_DataBox.cpp mutate_apply_struct_example_stateless\n *\n * \\tparam MutateTags typelist of Tags to mutate\n * \\tparam ArgumentTags typelist of additional items to retrieve from the\n * DataBox\n * \\tparam F The invokable to apply\n */\ntemplate <typename MutateTags, typename ArgumentTags, typename F,\n          typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(\n    F&& f, const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) {\n  return detail::mutate_apply(std::forward<F>(f), box, MutateTags{},\n                              ArgumentTags{}, std::forward<Args>(args)...);\n}\n\ntemplate <typename F, typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(\n    F&& f, const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) {\n  return mutate_apply<typename std::decay_t<F>::return_tags,\n                      typename std::decay_t<F>::argument_tags>(\n      std::forward<F>(f), box, std::forward<Args>(args)...);\n}\n\ntemplate <typename F, typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) mutate_apply(\n    const gsl::not_null<DataBox<BoxTags>*> box, Args&&... args) {\n  return mutate_apply(F{}, box, std::forward<Args>(args)...);\n}\n/// @}\n}  // namespace db\n\ntemplate <typename TagsList>\nstd::ostream& operator<<(std::ostream& os, const db::DataBox<TagsList>& box) {\n  os << box.print_types() << \"\\n\";\n  os << box.print_items() << \"\\n\";\n  return os;\n}\n"
  },
  {
    "path": "src/DataStructures/DataBox/DataBoxTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace Tags {\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\brief Tag used to retrieve the DataBox from the `db::get` function\n *\n * The main use of this tag is to allow fetching the DataBox from itself. The\n * primary use case is to allow an invokable to take a DataBox as an argument\n * when called through `db::apply`.\n *\n * \\snippet Test_DataBox.cpp databox_self_tag_example\n */\nstruct DataBox {\n  // Trick to get friend function declaration to compile but a const\n  // NoSuchtype****& is rather useless\n  using type = NoSuchType****;\n};\n}  // namespace Tags\n\nnamespace db {\n\nnamespace detail {\ntemplate <typename TagsList,\n          typename MatchingTagsList = tmpl::filter<\n              TagsList, tt::is_a<Parallel::Tags::MetavariablesImpl, tmpl::_1>>>\nstruct metavars_tag_impl {\n  static_assert(tmpl::size<MatchingTagsList>::value == 1);\n  using type = tmpl::front<MatchingTagsList>;\n};\n\ntemplate <typename TagsList>\nstruct metavars_tag_impl<TagsList, tmpl::list<>> {\n  using type = NoSuchType;\n};\n\ntemplate <typename TagList, typename Tag>\nusing list_of_matching_tags = tmpl::conditional_t<\n    std::is_same_v<Tag, ::Tags::DataBox>, tmpl::list<::Tags::DataBox>,\n    tmpl::conditional_t<\n        std::is_same_v<Tag, Parallel::Tags::Metavariables>,\n        tmpl::list<Parallel::Tags::Metavariables>,\n        tmpl::filter<TagList, std::is_base_of<tmpl::pin<Tag>, tmpl::_1>>>>;\n\ntemplate <typename Tag, typename TagList,\n          typename MatchingTagsList = list_of_matching_tags<TagList, Tag>>\nstruct first_matching_tag_impl {\n  using type = tmpl::front<MatchingTagsList>;\n};\n\ntemplate <typename Tag, typename TagList>\nstruct first_matching_tag_impl<Tag, TagList, tmpl::list<>> {\n  static_assert(std::is_same<Tag, NoSuchType>::value,\n                \"Could not find the DataBox tag in the list of DataBox tags. \"\n                \"The first template parameter of 'first_matching_tag_impl' is \"\n                \"the tag that cannot be found and the second is the list of \"\n                \"tags being searched.\");\n  using type = NoSuchType;\n};\n\ntemplate <typename TagList, typename Tag>\nusing first_matching_tag = typename first_matching_tag_impl<Tag, TagList>::type;\n\ntemplate <typename TagList, typename Tag>\nconstexpr auto number_of_matching_tags =\n    tmpl::size<list_of_matching_tags<TagList, Tag>>::value;\n\ntemplate <typename TagList, typename Tag>\nstruct has_unique_matching_tag\n    : std::integral_constant<bool, number_of_matching_tags<TagList, Tag> == 1> {\n};\n\ntemplate <typename TagList, typename Tag>\nusing has_unique_matching_tag_t =\n    typename has_unique_matching_tag<TagList, Tag>::type;\n\ntemplate <typename TagList, typename Tag>\nconstexpr bool has_unique_matching_tag_v =\n    has_unique_matching_tag<TagList, Tag>::value;\n\ntemplate <typename TagList, typename Tag>\nstruct has_no_matching_tag\n    : std::integral_constant<bool, number_of_matching_tags<TagList, Tag> == 0> {\n};\n\ntemplate <typename TagList, typename Tag>\nusing has_no_matching_tag_t = typename has_no_matching_tag<TagList, Tag>::type;\n\ntemplate <typename TagList, typename Tag>\nconstexpr bool has_no_matching_tag_v = has_no_matching_tag<TagList, Tag>::value;\n\ntemplate <typename T>\nstruct ConvertToConst {\n  using type = const T&;\n};\n\ntemplate <typename T>\nstruct ConvertToConst<std::unique_ptr<T>> {\n  using type = const T&;\n};\n\ntemplate <typename Tag, typename TagsList>\nstruct const_item_type_impl {\n  using type = typename db::detail::ConvertToConst<\n      std::decay_t<typename Tag::type>>::type;\n};\n\ntemplate <typename TagsList>\nstruct const_item_type_impl<Parallel::Tags::Metavariables, TagsList> {\n  using type = const typename detail::metavars_tag_impl<TagsList>::type::type&;\n};\n}  // namespace detail\n\ntemplate <typename Tag, typename TagsList>\nusing const_item_type =\n    typename detail::const_item_type_impl<Tag, TagsList>::type;\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/DataOnSlice.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/range/combine.hpp>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/SliceTensorToVariables.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace db {\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Slices volume `Tensor`s from a `DataBox` into a `Variables`\n *\n * The slice has a constant logical coordinate in direction `sliced_dim`,\n * slicing the volume at `fixed_index` in that dimension.  For\n * example, to get the lower boundary of `sliced_dim`, pass `0` for\n * `fixed_index`; to get the upper boundary, pass\n * `extents[sliced_dim] - 1`. The last argument to the function is the typelist\n * holding the tags to slice.\n *\n * \\snippet Test_DataBox.cpp data_on_slice\n */\ntemplate <size_t VolumeDim, typename TagsList, typename... TagsToSlice>\nVariables<tmpl::list<TagsToSlice...>> data_on_slice(\n    const db::DataBox<TagsList>& box, const Index<VolumeDim>& element_extents,\n    const size_t sliced_dim, const size_t fixed_index,\n    tmpl::list<TagsToSlice...> /*meta*/) {\n  return data_on_slice<TagsToSlice...>(element_extents, sliced_dim, fixed_index,\n                                       db::get<TagsToSlice>(box)...);\n}\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/IsApplyCallable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace db::detail {\nCREATE_IS_CALLABLE(apply)\nCREATE_IS_CALLABLE_V(apply)\n\ntemplate <typename Func, typename... Args>\nconstexpr void error_function_not_callable() {\n  static_assert(\n      std::is_same_v<Func, void>,\n      \"The function is not callable with the expected arguments.  \"\n      \"See the first template parameter of \"\n      \"error_function_not_callable for the function or object type and \"\n      \"the remaining arguments for the parameters that cannot be \"\n      \"passed. If all the argument types match, it could be that you \"\n      \"have a template parameter that cannot be deduced.\"\n      \"Note that for most DataBox functions, you must pass either \"\n      \"a function pointer, a lambda, or a class with a call operator \"\n      \"or static apply function, and this error will also arise if \"\n      \"the provided entity does not satisfy that requirement (e.g. \"\n      \"if the provided class defines a function with the incorrect name).\");\n}\n}  // namespace db::detail\n"
  },
  {
    "path": "src/DataStructures/DataBox/Item.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace db::detail {\n\n// Used to label the different types of items in a DataBox (which are described\n// in detail below for each specialization of Item).\nenum class ItemType { Mutable, Compute, Reference, Invalid };\n\n// A unique item in a DataBox labeled by Tag\ntemplate <typename Tag, ItemType = db::is_mutable_item_tag_v<Tag>\n                                       ? ItemType::Mutable\n                                       : (db::is_compute_tag_v<Tag>\n                                              ? ItemType::Compute\n                                              : (db::is_reference_tag_v<Tag>\n                                                     ? ItemType::Reference\n                                                     : ItemType::Invalid))>\nclass Item {\n  static_assert(\n      std::is_base_of_v<db::SimpleTag, Tag>,\n      \"The Tag of an Item in the Databox must be derived from db::SimpleTag\");\n};\n\n// A mutable item in a DataBox\n//\n// A mutable item is an item in a DataBox that is initialized when the DataBox\n// is constructed using either db::create\n//\n// Its value may be fetched by calling db::get (which calls get)\n//\n// Its value may be changed by calling db::mutate (which calls mutate)\ntemplate <typename Tag>\nclass Item<Tag, ItemType::Mutable> {\n public:\n  static constexpr ItemType item_type = ItemType::Mutable;\n\n  using value_type = typename Tag::type;\n\n  constexpr Item() = default;\n  constexpr Item(Item const&) = default;\n  constexpr Item(Item&&) = default;\n  constexpr Item& operator=(Item const&) = default;\n  constexpr Item& operator=(Item&&) = default;\n  ~Item() = default;\n\n  explicit Item(value_type value) : value_(std::move(value)) {}\n\n  const value_type& get() const { return value_; }\n\n  value_type& mutate() { return value_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | value_; }\n\n private:\n  value_type value_{};\n};\n\n// A compute item in a DataBox\n//\n// A compute item is an item in a DataBox whose value depends upon other items\n// in the DataBox.  It is lazily evaluated (i.e. not computed until it is\n// retrieved) and will be reevaluated if any of the items upon which it depends\n// is changed.\n//\n// A compute item is default constructed when the DataBox is constructed.\n//\n// When a compute item is fetched via db::get, db::get will check whether it has\n// already been evaluated (by calling evaluated).  If it has been evaluated,\n// db::get fetches its value (via get).  If it has not been evaluated (either\n// initially or after reset has been called), db::get will call evaluate which\n// will compute the value using tag::function.\n//\n// When db::mutate is called on a mutable item, all compute items that depend\n// (directly or indirectly) on the mutated item will have their reset function\n// called.\n//\n// A compute item may not be directly mutated (its value only changes after one\n// of its dependencies changes and it is fetched again)\ntemplate <typename Tag>\nclass Item<Tag, ItemType::Compute> {\n public:\n  static constexpr ItemType item_type = ItemType::Compute;\n\n  using value_type = typename Tag::type;\n\n  constexpr Item() = default;\n  constexpr Item(Item const&) = default;\n  constexpr Item(Item&&) = default;\n  constexpr Item& operator=(Item const&) = default;\n  constexpr Item& operator=(Item&&) = default;\n  ~Item() = default;\n\n  const value_type& get() const { return value_; }\n\n  bool evaluated() const { return evaluated_; }\n\n  void reset() { evaluated_ = false; }\n\n  template <typename... Args>\n  void evaluate(const Args&... args) const {\n    Tag::function(make_not_null(&value_), args...);\n    evaluated_ = true;\n  }\n\n private:\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable value_type value_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable bool evaluated_{false};\n};\n\n// A reference item in the DataBox\n//\n// A reference item is an item in a DataBox used as a const reference to a\n// subitem of another item (called the parent item) contained in the DataBox\n//\n// Its value may be fetched via db::get (by calling Tag::get directly)\n//\n// A reference item cannot be used to mutate its value.\ntemplate <typename Tag>\nclass Item<Tag, ItemType::Reference> {\n public:\n  static constexpr ItemType item_type = ItemType::Reference;\n\n  constexpr Item() = default;\n  constexpr Item(Item const&) = default;\n  constexpr Item(Item&&) = default;\n  constexpr Item& operator=(Item const&) = default;\n  constexpr Item& operator=(Item&&) = default;\n  ~Item() = default;\n};\n}  // namespace db::detail\n/// \\endcond\n"
  },
  {
    "path": "src/DataStructures/DataBox/MetavariablesTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace Parallel::Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ParallelGroup\n/// \\brief Tag to retrieve the `Metavariables` from the DataBox.\n///\n/// \\details To insert the metavariables into the DataBox use\n/// `Parallel::Tags::MetavariablesImpl<metavariables>`\nstruct Metavariables {};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Tag to insert Metavars into the DataBox\n///\n/// \\details Can be retrieved via `Parallel::Tags::Metavariables` (i.e. without\n/// the template parameter)\ntemplate <typename Metavars>\nstruct MetavariablesImpl : Metavariables, db::SimpleTag {\n  using type = Metavars;\n  static std::string name() { return \"Metavariables\"; }\n};\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/DataStructures/DataBox/ObservationBox.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Item.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Utilities/CleanupRoutine.hpp\"\n#include \"Utilities/ErrorHandling/StaticAssert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename ComputeTagsList, typename DataBoxType>\nclass ObservationBox;\n/// \\endcond\n\nnamespace Tags {\n/*!\n * \\brief Tag used to retrieve the ObservationBox from the `get()` function\n *\n * The main use of this tag is to allow fetching the ObservationBox from itself.\n * The intended primary use case is for Events to be able to retrieve the\n * ObservationBox and then do runtime retrieval of tags to avoid computing\n * quantities that aren't needed.\n */\nstruct ObservationBox {\n  // Trick to get friend function declaration to compile but a const\n  // NoSuchtype****& is rather useless\n  using type = NoSuchType****;\n};\n}  // namespace Tags\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Used for adding compute items to a `DataBox` without copying or moving\n * any data from the original `DataBox`\n *\n * The intended use-case for this class is during IO/observing where additional\n * compute tags are needed only for observation. The memory used by those\n * compute tags does not need to be persistent and so we'd like a light-weight\n * class to handle the on-demand computation.\n */\ntemplate <typename DataBoxType, typename... ComputeTags>\nclass ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>\n    : private db::detail::Item<ComputeTags>... {\n public:\n  /// A list of all the compute item tags\n  using compute_item_tags = tmpl::list<ComputeTags...>;\n  /// A list of all tags\n  using tags_list =\n      tmpl::push_back<typename DataBoxType::tags_list, ComputeTags...>;\n\n  ObservationBox() = default;\n  ObservationBox(const ObservationBox& rhs) = default;\n  ObservationBox& operator=(const ObservationBox& rhs) = default;\n  ObservationBox(ObservationBox&& rhs) = default;\n  ObservationBox& operator=(ObservationBox&& rhs) = default;\n  ~ObservationBox() = default;\n\n  /// Create an `ObservationBox` that can also retrieve things out of the\n  /// `databox` passed in.\n  explicit ObservationBox(gsl::not_null<DataBoxType*> databox);\n\n  /// Retrieve the tag `Tag`, should be called by the free function db::get\n  template <typename Tag>\n  const auto& get() const;\n\n  /// @{\n  /// Retrieve the underlying DataBox.\n  DataBoxType& databox() { return *databox_; }\n  const DataBoxType& databox() const { return *databox_; }\n  /// @}\n\n  /// Reset all the compute items, forcing reevaluation.\n  void reset();\n\n private:\n  template <typename Tag>\n  const auto& get_item() const {\n    return static_cast<const db::detail::Item<Tag>&>(*this);\n  }\n\n  template <typename ComputeTag, typename... ArgumentTags>\n  void evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const;\n\n  template <typename ReferenceTag, typename... ArgumentTags>\n  const auto& get_reference_item(tmpl::list<ArgumentTags...> /*meta*/) const;\n\n  gsl::not_null<DataBoxType*> databox_;\n};\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Retrieve a `Tag` from the `ObservationBox`.\n */\ntemplate <typename Tag, typename DataBoxType, typename... ComputeTags>\nconst auto& get(\n    const ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>& box) {\n  return box.template get<Tag>();\n}\n\n/// \\cond\ntemplate <typename DataBoxType, typename... ComputeTags>\nObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::ObservationBox(\n    const gsl::not_null<DataBoxType*> databox)\n    : databox_(databox) {\n  DEBUG_STATIC_ASSERT(\n      (db::is_immutable_item_tag_v<ComputeTags> and ...),\n      \"All tags passed to ObservationBox must be compute tags.\");\n}\n/// \\endcond\n\ntemplate <typename DataBoxType, typename... ComputeTags>\ntemplate <typename Tag>\nconst auto& ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::get()\n    const {\n  if constexpr (std::is_same_v<Tag, ::Tags::DataBox>) {\n    return *databox_;\n  } else if constexpr (std::is_same_v<Tag, ::Tags::ObservationBox>) {\n    return *this;\n  } else {\n    DEBUG_STATIC_ASSERT(\n        not db::detail::has_no_matching_tag_v<tags_list, Tag>,\n        \"Found no tags in the ObservationBox that match the tag \"\n        \"being retrieved.\");\n    DEBUG_STATIC_ASSERT(\n        db::detail::has_unique_matching_tag_v<tags_list, Tag>,\n        \"Found more than one tag in the ObservationBox that matches the tag \"\n        \"being retrieved. This happens because more than one tag with the same \"\n        \"base (class) tag was added to the ObservationBox, or because you add \"\n        \"a compute tag that is already in the DataBox.\");\n\n    if constexpr (db::tag_is_retrievable_v<Tag, DataBoxType>) {\n      return db::get<Tag>(*databox_);\n    } else {\n      using item_tag = db::detail::first_matching_tag<compute_item_tags, Tag>;\n      if constexpr (db::detail::Item<item_tag>::item_type ==\n                    db::detail::ItemType::Reference) {\n        return get_reference_item<item_tag>(typename item_tag::argument_tags{});\n      } else {\n        if (not get_item<item_tag>().evaluated()) {\n          evaluate_compute_item<item_tag>(typename item_tag::argument_tags{});\n        }\n        if constexpr (tt::is_a_v<std::unique_ptr, typename item_tag::type>) {\n          return *(get_item<item_tag>().get());\n        } else {\n          return get_item<item_tag>().get();\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataBoxType, typename... ComputeTags>\nvoid ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::reset() {\n  tmpl::for_each<\n      tmpl::filter<tmpl::list<ComputeTags...>, db::is_compute_tag<tmpl::_1>>>(\n      [this](auto tag) {\n        static_cast<db::detail::Item<tmpl::type_from<decltype(tag)>>&>(*this)\n            .reset();\n      });\n}\n\ntemplate <typename DataBoxType, typename... ComputeTags>\ntemplate <typename ComputeTag, typename... ArgumentTags>\nvoid ObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::\n    evaluate_compute_item(tmpl::list<ArgumentTags...> /*meta*/) const {\n  get_item<ComputeTag>().evaluate(get<ArgumentTags>()...);\n}\n\ntemplate <typename DataBoxType, typename... ComputeTags>\ntemplate <typename ReferenceTag, typename... ArgumentTags>\nconst auto&\nObservationBox<tmpl::list<ComputeTags...>, DataBoxType>::get_reference_item(\n    tmpl::list<ArgumentTags...> /*meta*/) const {\n  return ReferenceTag::get(get<ArgumentTags>()...);\n}\n\ntemplate <typename ComputeTagsList, typename DataBoxType>\nauto make_observation_box(const gsl::not_null<DataBoxType*> databox) {\n  return ObservationBox<db::detail::expand_subitems<ComputeTagsList>,\n                        DataBoxType>{databox};\n}\n\nnamespace observation_box_detail {\ntemplate <typename DataBoxType, typename ComputeTagsList, typename... Args,\n          typename F, typename... ArgumentTags>\nauto apply(F&& f, tmpl::list<ArgumentTags...> /*meta*/,\n           const ObservationBox<ComputeTagsList, DataBoxType>& observation_box,\n           Args&&... args) {\n  if constexpr (db::detail::is_apply_callable_v<\n                    F,\n                    std::decay_t<decltype(\n                        get<ArgumentTags>(observation_box))>...,\n                    Args...>) {\n    return std::decay_t<F>::apply(get<ArgumentTags>(observation_box)...,\n                    std::forward<Args>(args)...);\n  } else if constexpr (::tt::is_callable_v<\n                 F,\n                 std::decay_t<decltype(get<ArgumentTags>(observation_box))>...,\n                 Args...>) {\n    return std::forward<F>(f)(get<ArgumentTags>(observation_box)...,\n                              std::forward<Args>(args)...);\n  } else {\n    db::detail::error_function_not_callable<\n        F, std::decay_t<decltype(get<ArgumentTags>(observation_box))>...,\n        Args...>();\n  }\n}\n\ntemplate <typename DataBoxType, typename ComputeTagsList, typename... Args,\n          typename F, typename... ReturnTags, typename... ArgumentTags>\ndecltype(auto) mutate_apply(\n    F&& f, tmpl::list<ReturnTags...> /*meta*/,\n    tmpl::list<ArgumentTags...> /*meta*/,\n    const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>\n        observation_box,\n    Args&&... args) {\n  const CleanupRoutine reset_items = [&]() {\n    if constexpr (sizeof...(ReturnTags) != 0) {\n      // Not ideal, but doing a more granular reset is not worth the\n      // trouble.\n      observation_box->reset();\n    }\n  };\n  return db::mutate_apply<tmpl::list<ReturnTags...>, tmpl::list<>>(\n      f, make_not_null(&observation_box->databox()),\n      get<ArgumentTags>(*observation_box)..., std::forward<Args>(args)...);\n}\n}  // namespace observation_box_detail\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Apply the function object `f` using its nested `argument_tags` list of\n * tags.\n */\ntemplate <typename DataBoxType, typename ComputeTagsList, typename... Args,\n          typename F>\nauto apply(F&& f,\n           const ObservationBox<ComputeTagsList, DataBoxType>& observation_box,\n           Args&&... args) {\n  return observation_box_detail::apply(\n      std::forward<F>(f), typename std::decay_t<F>::argument_tags{},\n      observation_box, std::forward<Args>(args)...);\n}\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Apply the function object `f` using its nested `return_tags`\n * and `argument_tags` list of tags.  Modifications are made to the\n * underlying DataBox.\n */\n/// @{\ntemplate <typename DataBoxType, typename ComputeTagsList, typename... Args,\n          typename F>\nauto mutate_apply(\n    F&& f,\n    const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>\n        observation_box,\n    Args&&... args) {\n  return observation_box_detail::mutate_apply(\n      std::forward<F>(f), typename std::decay_t<F>::return_tags{},\n      typename std::decay_t<F>::argument_tags{}, observation_box,\n      std::forward<Args>(args)...);\n}\n\ntemplate <typename ReturnTags, typename ArgumentTags, typename DataBoxType,\n          typename ComputeTagsList, typename... Args, typename F>\nauto mutate_apply(\n    F&& f,\n    const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*>\n        observation_box,\n    Args&&... args) {\n  return observation_box_detail::mutate_apply(std::forward<F>(f), ReturnTags{},\n                                              ArgumentTags{}, observation_box,\n                                              std::forward<Args>(args)...);\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/DataBox/PrefixHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename TagsList>\nclass Variables;\nnamespace db {\nstruct PrefixTag;\nstruct SimpleTag;\n}  // namespace db\n\nnamespace Tags {\ntemplate <typename TagsList>\nstruct Variables;\n}  // namespace Tags\n/// \\endcond\n\nnamespace db {\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Create a new `tmpl::list` of tags by wrapping each tag in `TagList`\n/// in `Wrapper<_, Args...>`.\ntemplate <template <typename...> class Wrapper, typename TagList,\n          typename... Args>\nusing wrap_tags_in =\n    tmpl::transform<TagList, tmpl::bind<Wrapper, tmpl::_1, tmpl::pin<Args>...>>;\n\nnamespace detail {\ntemplate <template <typename...> class Prefix, typename Tag, typename... Args>\nstruct add_tag_prefix_impl {\n  using type = Prefix<Tag, Args...>;\n};\n\ntemplate <template <typename...> class Prefix, typename TagList,\n          typename... Args>\nstruct add_tag_prefix_impl<Prefix, Tags::Variables<TagList>, Args...> {\n  using type = Tags::Variables<wrap_tags_in<Prefix, TagList, Args...>>;\n};\n}  // namespace detail\n\n/// \\ingroup DataBoxTagsGroup\n/// Wrap `Tag` in `Prefix<_, Args...>`, unless `Tag` is a Tags::Variables,\n/// in which case this creates a new Tags::Variables, wrapping each tag in\n/// `Tag::tags_list` with `Prefix<_, Args...>`.\ntemplate <template <typename...> class Prefix, typename Tag, typename... Args>\nusing add_tag_prefix =\n    typename detail::add_tag_prefix_impl<Prefix, Tag, Args...>::type;\n\nnamespace detail {\ntemplate <typename>\nstruct remove_tag_prefix_impl;\n\ntemplate <typename WrappedTag, template <typename...> class Prefix,\n          typename... Args>\nstruct remove_tag_prefix_impl<Prefix<WrappedTag, Args...>> {\n  using type = WrappedTag;\n};\n\ntemplate <typename TagList>\nstruct remove_tag_prefix_impl<Tags::Variables<TagList>> {\n  using type = Tags::Variables<\n      tmpl::transform<TagList, remove_tag_prefix_impl<tmpl::_1>>>;\n};\n}  // namespace detail\n\n/// \\ingroup DataBoxTagsGroup\n/// Remove the outer prefix from a prefixed tag `Tag`, or remove the outer\n/// prefix of each tag in `Tag::tags_list` if `Tag` is a Tags::Variables.\ntemplate <typename Tag>\nusing remove_tag_prefix = typename detail::remove_tag_prefix_impl<Tag>::type;\n\nnamespace detail {\n\ntemplate <typename Tag>\nstruct remove_all_prefixes_impl {\n  using type = Tag;\n};\n\ntemplate <typename Tag>\n  requires(std::is_base_of_v<db::PrefixTag, Tag>)\nstruct remove_all_prefixes_impl<Tag> {\n  using type = typename remove_all_prefixes_impl<typename Tag::tag>::type;\n};\n\ntemplate <typename TagList>\nstruct remove_all_prefixes_impl<Tags::Variables<TagList>> {\n  using type = Tags::Variables<\n      tmpl::transform<TagList, remove_all_prefixes_impl<tmpl::_1>>>;\n};\n}  // namespace detail\n\n/// \\ingroup DataBoxTagsGroup\n/// Completely remove all prefix tags from a Tag, or all prefixes from\n/// the tags in `Tag::tags_list` if `Tag` is a Tags::Variables.\ntemplate <typename Tag>\nusing remove_all_prefixes =\n    typename detail::remove_all_prefixes_impl<Tag>::type;\n\nnamespace detail {\ntemplate <template <typename...> typename Wrapper, typename T, typename... Args>\nstruct prefix_variables {\n  using type = T;\n};\n\ntemplate <template <typename...> typename Wrapper, typename Tags,\n          typename... Args>\nstruct prefix_variables<Wrapper, Variables<Tags>, Args...> {\n  using type = Variables<::db::wrap_tags_in<Wrapper, Tags, Args...>>;\n};\n}  // namespace detail\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Add a prefix to all tags in a Variables, leaving the\n/// argument unchanged if it is not a Variables.\n///\n/// \\see unprefix_variables, wrap_tags_in\ntemplate <template <typename...> class Wrapper, typename T, typename... Args>\nusing prefix_variables =\n    typename detail::prefix_variables<Wrapper, T, Args...>::type;\n\nnamespace detail {\ntemplate <typename T>\nstruct unprefix_variables {\n  using type = T;\n};\n\ntemplate <typename... Tags>\nstruct unprefix_variables<Variables<tmpl::list<Tags...>>> {\n  using type = Variables<tmpl::list<tmpl::front<Tags>...>>;\n};\n}  // namespace detail\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Remove the outer prefix from all tags in a Variables,\n/// leaving the argument unchanged if it is not a Variables.\n///\n/// \\see prefix_variables\ntemplate <typename T>\nusing unprefix_variables = typename detail::unprefix_variables<T>::type;\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/Prefixes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Define prefixes for DataBox tags\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\ntemplate <class>\nclass Variables;\n/// \\endcond\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating a time derivative\n///\n/// \\snippet Test_DataBoxPrefixes.cpp dt_name\ntemplate <typename Tag>\nstruct dt : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\brief Prefix indicating spatial derivatives\n *\n * Prefix indicating the spatial derivatives of a Tensor.\n *\n * \\tparam Tag The tag to wrap\n * \\tparam Dim The volume dim as a type (e.g. `tmpl::size_t<Dim>`)\n * \\tparam Frame The frame of the derivative index\n *\n * \\see Tags::DerivCompute\n */\ntemplate <typename Tag, typename Dim, typename Frame>\nstruct deriv;\n\n/// \\cond\ntemplate <typename Tag, typename Dim, typename Frame>\n  requires(tt::is_a_v<Tensor, typename Tag::type>)\nstruct deriv<Tag, Dim, Frame> : db::PrefixTag, db::SimpleTag {\n  using type =\n      TensorMetafunctions::prepend_spatial_index<typename Tag::type, Dim::value,\n                                                 UpLo::Lo, Frame>;\n  using tag = Tag;\n};\n/// \\endcond\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\brief Prefix indicating symmetric second spatial derivatives\n *\n * Prefix indicating the symmetric second spatial derivatives of a Tensor.\n *\n * \\tparam Tag The tag to wrap\n * \\tparam Dim The volume dim as a type (e.g. `tmpl::size_t<Dim>`)\n * \\tparam Frame The frame of the derivative index\n *\n * \\see Tags::DerivCompute\n */\ntemplate <typename Tag, typename Dim, typename Frame>\nstruct second_deriv;\n\n/// \\cond\ntemplate <typename Tag, typename Dim, typename Frame>\n  requires(tt::is_a_v<Tensor, typename Tag::type>)\nstruct second_deriv<Tag, Dim, Frame> : db::PrefixTag, db::SimpleTag {\n  using type = TensorMetafunctions::prepend_two_symmetric_spatial_indices<\n      typename Tag::type, Dim::value, UpLo::Lo, Frame>;\n  using tag = Tag;\n};\n/// \\endcond\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\brief Prefix indicating spacetime derivatives\n *\n * Prefix indicating the spacetime derivatives of a Tensor or that a Variables\n * contains spatial derivatives of Tensors.\n *\n * \\tparam Tag The tag to wrap\n * \\tparam Dim The volume dim as a type (e.g. `tmpl::size_t<Dim>`)\n * \\tparam Frame The frame of the derivative index\n */\ntemplate <typename Tag, typename Dim, typename Frame>\nstruct spacetime_deriv;\n\n/// \\cond\ntemplate <typename Tag, typename Dim, typename Frame>\n  requires(tt::is_a_v<Tensor, typename Tag::type>)\nstruct spacetime_deriv<Tag, Dim, Frame> : db::PrefixTag, db::SimpleTag {\n  using type =\n      TensorMetafunctions::prepend_spacetime_index<typename Tag::type,\n                                                   Dim::value, UpLo::Lo, Frame>;\n  using tag = Tag;\n};\n/// \\endcond\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating a flux\n///\n/// \\snippet Test_DataBoxPrefixes.cpp flux_name\ntemplate <typename Tag, typename VolumeDim, typename Fr>\nstruct Flux;\n\n/// \\cond\ntemplate <typename Tag, typename VolumeDim, typename Fr>\n  requires(tt::is_a_v<Tensor, typename Tag::type>)\nstruct Flux<Tag, VolumeDim, Fr> : db::PrefixTag, db::SimpleTag {\n  using type = TensorMetafunctions::prepend_spatial_index<\n      typename Tag::type, VolumeDim::value, UpLo::Up, Fr>;\n  using tag = Tag;\n};\n\ntemplate <typename Tag, typename VolumeDim, typename Fr>\n  requires(tt::is_a_v<::Variables, typename Tag::type>)\nstruct Flux<Tag, VolumeDim, Fr> : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n/// \\endcond\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating a source term\n///\n/// \\snippet Test_DataBoxPrefixes.cpp source_name\ntemplate <typename Tag>\nstruct Source : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating a source term that is independent of dynamic\n/// variables\ntemplate <typename Tag>\nstruct FixedSource : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating the initial value of a quantity\n///\n/// \\snippet Test_DataBoxPrefixes.cpp initial_name\ntemplate <typename Tag>\nstruct Initial : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating a boundary unit normal vector dotted into\n/// the flux\n///\n/// \\snippet Test_DataBoxPrefixes.cpp normal_dot_flux_name\ntemplate <typename Tag>\nstruct NormalDotFlux : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating a boundary unit normal vector dotted into\n/// the numerical flux\n///\n/// \\snippet Test_DataBoxPrefixes.cpp normal_dot_numerical_flux_name\ntemplate <typename Tag>\nstruct NormalDotNumericalFlux : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating the value a quantity took in the previous iteration\n/// of the algorithm.\ntemplate <typename Tag>\nstruct Previous : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating the value a quantity will take on the\n/// next iteration of the algorithm.\n///\n/// \\snippet Test_DataBoxPrefixes.cpp next_name\ntemplate <typename Tag>\nstruct Next : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n}  // namespace Tags\n"
  },
  {
    "path": "src/DataStructures/DataBox/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Mutator.hpp\n  )\n"
  },
  {
    "path": "src/DataStructures/DataBox/Protocols/Mutator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/ProtocolHelpers.hpp\"\n\n/// \\brief Protocols for the DataBox\nnamespace db::protocols {\n\n/// \\brief A DataBox mutator\n///\n/// A class conforming to this protocol can be used as the template argument for\n/// a call to db::mutate_apply(const gsl::not_null<DataBox<BoxTags>*> box,\n/// Args&&... args).  The conforming class must provide the following:\n///\n/// - `return_tags`: A type list of tags corresponding to mutable items in the\n///   DataBox passed to `db::mutate_apply` that may be modified.\n/// - `argument_tags`: A type list of tags corresponding to items in the DataBox\n///   passed to `db::mutate_apply` that may not be modified.\n/// - `apply`: A static function whose return value is returned by\n///   `db::mutate_apply`, and that takes as arguments:\n///      - A `const gsl::not_null<Tag::type*>` for each `Tag` in `return_tags`\n///      - A `const db::const_item_type<Tag, BoxTags>` for each `Tag` in\n///        `argument_tags`\n///      - The additional arguments passed to `db::mutate_apply`\n///\n///   Note: use the explicit type whenever possible, not the type aliases.\n///   `db::const_item_type` will usually be `Tag::type` unless that is a\n///   `std::unique_ptr<T>`, in which case it will be `T`\n///\n/// Here is an example for a class conforming to this protocol:\n///\n/// \\snippet DataBox/Examples.hpp mutator_protocol\nstruct Mutator {\n  template <typename ConformingType>\n  struct test {\n    using argument_tags = typename ConformingType::argument_tags;\n    using return_tags = typename ConformingType::return_tags;\n  };\n};\n}  // namespace db::protocols\n"
  },
  {
    "path": "src/DataStructures/DataBox/SubitemTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Subitems.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\n\n/// \\cond\n// Declaration of a tag for a subitem\n// Unless specialized, it will be the reference tag below,\ntemplate <typename Tag, typename ParentTag, typename = std::nullptr_t>\nstruct Subitem;\n/// \\endcond\n\n/// \\brief a reference tag that refers to a particular Tag that is a subitem of\n/// an item tagged with ParentTag\ntemplate <typename Tag, typename ParentTag, typename>\nstruct Subitem : Tag, db::ReferenceTag {\n  using base = Tag;\n  using argument_tags = tmpl::list<typename ParentTag::base>;\n  static const auto& get(\n      const typename ParentTag::type& parent_value) {\n    return ::db::Subitems<ParentTag>::template create_compute_item<base>(\n        parent_value);\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/DataStructures/DataBox/Subitems.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace db {\n/// \\ingroup DataBoxGroup\n/// Struct that can be specialized to allow DataBox items to have\n/// subitems.  Specializations must define:\n/// * `using type = tmpl::list<...>` listing the subtags of `Tag`\n/// * A static member function to initialize a subitem of a simple\n///   item:\n///   ```\n///   template <typename Subtag>\n///   static void create_item(\n///       const gsl::not_null<typename Tag::type*> parent_value,\n///       const gsl::not_null<typename Subtag::type*> sub_value);\n///   ```\n///   Mutating the subitems must also modify the main item.\n/// * A static member function evaluating a subitem of a compute\n///   item:\n///   ```\n///   template <typename Subtag>\n///   static typename Subtag::type create_compute_item(\n///       typename Tag::type& parent_value);\n///   ```\ntemplate <typename Tag, typename = std::nullptr_t>\nstruct Subitems {\n  using type = tmpl::list<>;\n};\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/Tag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace db {\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Mark a struct as a simple tag by inheriting from this.\n *\n * \\details\n * A simple tag is used to uniquely identify an item in a tagged container such\n * as a `DataBox`, `Variables`, or tuples::TaggedTuple.\n *\n * A simple tag may be the base class of a compute or reference tag. In such\n * a case, the simple tag can be used to fetch the item corresponding to the\n * compute or reference tag from a DataBox.\n *\n * A simple tag may be the base class of another simple tag.  This should be\n * done rarely, usually as a means of specifying an alternative way to create\n * the simple item from input-file options.  In such a case, the base simple\n * tag can be used to fetch the item, while either the base simple tag or one\n * othe derived simple tags can be used to insert the item into the DataBox\n * Te derived simple tags should have a type alias `base` that is the base\n * simple tag.\n *\n * \\derivedrequires\n * - type alias `type` of the type of the item corresponding to the simple tag\n *\n * A simple tag may optionally specify a static `std::string name()` method to\n * override the default name produced by db::tag_name.\n *\n * \\example\n * \\snippet Test_DataBox.cpp databox_tag_example\n *\n * \\see DataBoxGroup ComputeTag PrefixTag ReferenceTag\n */\nstruct SimpleTag {};\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Mark a struct as a prefix tag by inheriting from this.\n *\n * \\details\n * A prefix tag is used to create a new simple tag from another simple tag, and\n * therefore should also be marked as a simple tag by inheriting from\n * db::SimpleTag.\n *\n * The primary reason to mark a simple tag as a prefix tag is that the name\n * generated by db::tag_name will be constructed from the names of the prefix\n * tag and the wrapped simple tag.\n *\n * \\derivedrequires\n * - type alias `tag` of the wrapped simple tag\n * - type alias `type` of the type of the item corresponding to the prefix tag\n *\n * A prefix tag may optionally specify a static `std::string name()` method to\n * override the default name produced by db::tag_name.\n *\n * \\warning A prefix tag should only be derived from db::PrefixTag and\n * db::SimpleTag.\n *\n * \\warning A prefix tag should only wrap a single tag.  This is assumed by all\n * metafunctions that make use of the `tag` type alias of a prefix tag.\n *\n * \\example\n * A PrefixTag tag has the structure:\n * \\snippet Test_DataBox.cpp databox_prefix_tag_example\n *\n * \\see SimpleTag\n */\nstruct PrefixTag {};\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Mark a struct as a compute tag by inheriting from this.\n *\n * \\details\n * A compute tag is used to identify an item in a DataBox that will be computed\n * on-demand from other items in the DataBox.  A compute tag must be derived\n * from a simple tag corresponding to the type of object that is computed.  This\n * simple tag can be used to fetch the item corresponding to the compute tag\n * from a DataBox.\n *\n * A compute tag contains a member named `function` that is either a static\n * constexpr function pointer, or a static function. The compute tag must also\n * have a a type alias `argument_tags` that is a typelist of the tags that will\n * be retrieved from the DataBox and whose data will be passed to the function\n * (pointer). Compute tags must also contain a type alias named `return_type`\n * that is the type the function is mutating. The type must be default\n * constructible.\n *\n * By convention, the name of a compute tag should be the name of the simple tag\n * that it derives from, appended by `Compute`.\n *\n * \\derivedrequires\n * - type alias `return_type` of the type of the item computed\n * - type alias `base` that is the simple tag from which it is derived\n * - member `function`that is either a function pointer, or a static constexpr\n *   function that is used to compute the item from the `argument_tags` fetched\n *   from the DataBox\n * - type alias `argument_tags` that is a `tmpl::list` of the tags of the items\n *   that will be passed (in order) to the function specified by `function`\n *\n * A compute tag may optionally specify a static `std::string name()` method to\n * override the default name produced by db::tag_name.\n *\n * \\warning A compute tag should only be derived from a simple tag and\n * db::ComputeTag.\n *\n * \\example\n * Compute tags are of the form:\n * \\snippet Test_DataBox.cpp databox_mutating_compute_item_tag\n * where the function is:\n * \\snippet Test_DataBox.cpp databox_mutating_compute_item_function\n *\n * You can also have `function` be a function instead of a function pointer,\n * which offers a lot of simplicity for very simple compute items.\n * \\snippet Test_DataBox.cpp compute_item_tag_function\n *\n * Note that the arguments can be empty:\n * \\snippet Test_DataBox.cpp compute_item_tag_no_tags\n *\n * \\see DataBoxGroup SimpleTag\n */\nstruct ComputeTag {};\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Mark a struct as a reference tag by inheriting from this.\n *\n * \\details\n * A reference tag is used to identify an item in a DataBox that is a const\n * reference to a sub-item of another item (such as a Variables or GlobalCache)\n * in the DataBox\n *\n * \\derivedrequires\n * - type alias `base` that is the simple tag from which the reference tag is\n *   derived\n * - type alias `argument_tags` that lists tags needed to evaluate `get`\n * - static function `get` that, given the items fetched by `argument_tags`,\n *   returns a const reference to the sub-item\n *\n * A reference tag may optionally specify a static `std::string name()` method\n * to override the default name produced by db::tag_name.\n *\n * \\warning A reference tag should only be derived from a simple tag and\n * db::ReferenceTag.\n *\n * \\example\n * \\snippet Test_DataBox.cpp databox_reference_tag_example\n *\n * \\see DataBoxGroup SimpleTag\n */\nstruct ReferenceTag {};\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/TagName.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\n/// \\cond\nnamespace db {\nstruct PrefixTag;\n}  // namespace db\n/// \\endcond\n\nnamespace db {\n\nnamespace detail {\nCREATE_HAS_TYPE_ALIAS(base)\nCREATE_HAS_TYPE_ALIAS_V(base)\nCREATE_IS_CALLABLE(name)\nCREATE_IS_CALLABLE_V(name)\n}  // namespace detail\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Get the name of a DataBox tag, including prefixes\n *\n * \\details\n * Given a DataBox tag returns the name of the DataBox tag as a std::string. If\n * the DataBox tag is also a PrefixTag then the prefix is added.\n *\n * \\tparam Tag the DataBox tag whose name to get\n * \\return string holding the DataBox tag's name\n */\ntemplate <typename Tag>\nstd::string tag_name() {\n  if constexpr (detail::is_name_callable_v<Tag>) {\n    return Tag::name();\n  } else if constexpr (detail::has_base_v<Tag>) {\n    return tag_name<typename Tag::base>();\n  } else if constexpr (std::is_base_of_v<db::PrefixTag, Tag>) {\n    return pretty_type::short_name<Tag>() + \"(\" +\n           tag_name<typename Tag::tag>() + \")\";\n  } else {\n    return pretty_type::short_name<Tag>();\n  }\n}\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/TagTraits.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <concepts>\n#include <type_traits>\n\n/// \\cond\nnamespace db {\nstruct SimpleTag;\nstruct ComputeTag;\nstruct ReferenceTag;\n}  // namespace db\nnamespace Parallel::Tags {\nstruct Metavariables;\n}  // namespace Parallel::Tags\n/// \\endcond\n\nnamespace db {\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Check if `Tag` derives off of db::ComputeTag.\n *\n * \\see is_compute_tag_v ComputeTag\n */\ntemplate <typename Tag>\nstruct is_compute_tag : std::is_base_of<db::ComputeTag, Tag> {};\n\n/// \\ingroup DataBoxGroup\n/// \\brief True if `Tag` derives from db::ComputeTag.\ntemplate <typename Tag>\nconstexpr bool is_compute_tag_v = is_compute_tag<Tag>::value;\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Check if `Tag` derives off of db::ReferenceTag.\n *\n * \\see is_reference_tag_v ReferenceTag\n */\ntemplate <typename Tag>\nstruct is_reference_tag : std::is_base_of<db::ReferenceTag, Tag> {};\n\n/// \\ingroup DataBoxGroup\n/// \\brief True if `Tag` derives from db::ReferenceTag.\ntemplate <typename Tag>\nconstexpr bool is_reference_tag_v = is_reference_tag<Tag>::value;\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Check if `Tag` is a DataBox tag for an immutable item, i.e. a\n * ComputeTag or ReferenceTag\n *\n * \\see is_immutable_item_tag_v\n */\ntemplate <typename Tag>\nstruct is_immutable_item_tag\n    : std::bool_constant<std::is_base_of_v<db::ReferenceTag, Tag> or\n                         std::is_base_of_v<db::ComputeTag, Tag>> {};\n\n/// \\ingroup DataBoxGroup\n/// \\brief True if `Tag` is a DataBox tag for an immutable item, i.e. a\n/// ComputeTag or ReferenceTag.\ntemplate <typename Tag>\nconstexpr bool is_immutable_item_tag_v = is_immutable_item_tag<Tag>::value;\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Check if `Tag` is a DataBox tag for a mutable item, i.e. a SimpleTag\n *\n * \\see is_mutable_item_tag_v\n */\ntemplate <typename Tag>\nstruct is_mutable_item_tag\n    : std::bool_constant<std::is_base_of_v<db::SimpleTag, Tag> and\n                         not is_compute_tag_v<Tag> and\n                         not is_reference_tag_v<Tag>> {};\n\n/// \\ingroup DataBoxGroup\n/// \\brief True if `Tag` is a DataBox tag for a mutable item, i.e. a SimpleTag\ntemplate <typename Tag>\nconstexpr bool is_mutable_item_tag_v = is_mutable_item_tag<Tag>::value;\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Check if `Tag` is a simple tag.\n *\n * \\details This is done by deriving from std::true_type if `Tag` is derived\n * from db::SimpleTag, but not from db::ComputeTag or db::ReferenceTag.\n *\n * \\see is_simple_tag_v SimpleTag\n */\ntemplate <typename Tag>\nstruct is_simple_tag\n    : std::bool_constant<std::is_base_of_v<db::SimpleTag, Tag> and\n                         not is_compute_tag_v<Tag> and\n                         not is_reference_tag_v<Tag>> {};\n\n/// \\ingroup DataBoxGroup\n/// \\brief True if `Tag` is a simple tag.\ntemplate <typename Tag>\nconstexpr bool is_simple_tag_v = is_simple_tag<Tag>::value;\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Check if `Tag` is a simple, compute, or reference tag.\n *\n * \\see is_non_base_tag_v BaseTag\n */\ntemplate <typename Tag>\nstruct is_creation_tag : std::is_base_of<db::SimpleTag, Tag> {};\n\n/// \\ingroup DataBoxGroup\n/// \\brief True if `Tag` is not a base tag.\ntemplate <typename Tag>\nconstexpr bool is_creation_tag_v = is_creation_tag<Tag>::value;\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Check if `Tag` is a DataBox tag, i.e. a SimpleTag, ComputeTag,\n * ReferenceTag, or the special tag Parallel::Tags::Metavariables.\n *\n * \\see is_tag_v\n */\ntemplate <typename Tag>\nstruct is_tag\n    : std::bool_constant<std::is_base_of_v<db::SimpleTag, Tag> or\n                         std::is_same_v<Tag, Parallel::Tags::Metavariables>> {};\n\n/// \\ingroup DataBoxGroup\n/// \\brief True if `Tag` is a DataBox tag.\ntemplate <typename Tag>\nconstexpr bool is_tag_v = is_tag<Tag>::value;\n\n/*!\n * \\ingroup DataBoxGroup\n * Concept for a SimpleTag.\n *\n * \\see is_simple_tag\n */\ntemplate <typename Tag>\nconcept simple_tag = is_simple_tag_v<Tag>;\n\n/*!\n * \\ingroup DataBoxGroup\n * Concept for a ComputeTag.\n *\n * \\see is_compute_tag\n */\ntemplate <typename Tag>\nconcept compute_tag = is_compute_tag_v<Tag>;\n\n/*!\n * \\ingroup DataBoxGroup\n * Concept for a ReferenceTag.\n *\n * \\see is_reference_tag\n */\ntemplate <typename Tag>\nconcept reference_tag = is_reference_tag_v<Tag>;\n\n/*!\n * \\ingroup DataBoxGroup\n * Concept for an immutable item tag.\n *\n * \\see is_immutable_item_tag\n */\ntemplate <typename Tag>\nconcept immutable_item_tag = reference_tag<Tag> or compute_tag<Tag>;\n\n/*!\n * \\ingroup DataBoxGroup\n * Concept for a mutable item tag.\n *\n * \\see is_mutable_item_tag\n */\ntemplate <typename Tag>\nconcept mutable_item_tag = simple_tag<Tag>;\n\n/*!\n * \\ingroup DataBoxGroup\n * Concept for a DataBox tag.\n *\n * \\see is_tag\n */\ntemplate <typename Tag>\nconcept tag = immutable_item_tag<Tag> or mutable_item_tag<Tag> or\n              std::same_as<Tag, Parallel::Tags::Metavariables>;\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataBox/ValidateSelection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace db {\n\n/*!\n * \\brief Validate that the selected names are a subset of the given tags.\n *\n * The possible choices for the selection are the `db::tag_name`s of the `Tags`.\n *\n * \\tparam Tags List of possible tags.\n * \\param selected_names Names to validate.\n * \\param context Options context for error reporting.\n */\ntemplate <typename Tags>\nvoid validate_selection(const std::vector<std::string>& selected_names,\n                        const Options::Context& context) {\n  using ::operator<<;\n  std::unordered_set<std::string> valid_names{};\n  tmpl::for_each<Tags>([&valid_names](auto tag_v) {\n    using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n    valid_names.insert(db::tag_name<tag>());\n  });\n  for (const auto& name : selected_names) {\n    if (valid_names.find(name) == valid_names.end()) {\n      PARSE_ERROR(context, \"Invalid selection: \" << name\n                                                 << \". Possible choices are: \"\n                                                 << valid_names << \".\");\n    }\n    if (alg::count(selected_names, name) != 1) {\n      PARSE_ERROR(context, name << \" specified multiple times\");\n    }\n  }\n}\n\n}  // namespace db\n"
  },
  {
    "path": "src/DataStructures/DataVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(DataVector);\n}  // namespace blaze\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Stores a collection of function values.\n *\n * \\details Use DataVector to represent function values on the computational\n * domain. Note that interpreting the data also requires knowledge of the points\n * that these function values correspond to.\n *\n * A DataVector holds an array of contiguous data. The DataVector can be owning,\n * meaning the array is deleted when the DataVector goes out of scope, or\n * non-owning, meaning it just has a pointer to an array.\n *\n * Refer to the \\ref DataStructuresGroup documentation for a list of other\n * available types. In particular, to represent a generic vector that supports\n * common vector and matrix operations and whose meaning may not be of function\n * values at points, use any of the\n * [Blaze vector types](https://bitbucket.org/blaze-lib/blaze/wiki/Vector%20Types)\n * instead.\n *\n * DataVectors support a variety of mathematical operations that are applicable\n * to nodal coefficients. In addition to common arithmetic operations such as\n * elementwise addition, subtraction, multiplication and division, the\n * elementwise operations on blaze vectors of doubles are supported. See\n * [blaze-wiki/Vector_Operations]\n * (https://bitbucket.org/blaze-lib/blaze/wiki/Vector%20Operations).\n *\n * In addition, the Heaviside step function `step_function` is supported for\n * DataVectors.\n */\nclass DataVector : public VectorImpl<double, DataVector> {\n public:\n  DataVector() = default;\n  DataVector(const DataVector&) = default;\n  DataVector(DataVector&&) = default;\n  DataVector& operator=(const DataVector&) = default;\n  DataVector& operator=(DataVector&&) = default;\n  ~DataVector() = default;\n\n  using BaseType = VectorImpl<double, DataVector>;\n\n  using BaseType::operator=;\n  using BaseType::VectorImpl;\n};\n\n// Specialize the Blaze type traits to correctly handle DataVector\nnamespace blaze {\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(DataVector);\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ALL_MAP_TRAITS(DataVector);\n// Only specialize cross product for DataVector because it is unclear what a\n// cross product of other vector types is. This is why this is here and not in\n// VectorImpl.hpp\nBLAZE_TRAIT_SPECIALIZE_BINARY_TRAIT(DataVector, CrossTrait);\n}  // namespace blaze\n\nSPECTRE_ALWAYS_INLINE auto fabs(const DataVector& t) { return abs(*t); }\n\nMAKE_STD_ARRAY_VECTOR_BINOPS(DataVector)\n\nMAKE_WITH_VALUE_IMPL_DEFINITION_FOR(DataVector)\n"
  },
  {
    "path": "src/DataStructures/DiagonalModalOperator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ModalVector;\nclass DiagonalModalOperator;\n/// \\endcond\n\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(DiagonalModalOperator);\n}  // namespace blaze\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A class for an element-wise multiplier of modal coefficients.\n *\n * \\details A `DiagonalModalOperator` holds an array of factors to multiply by\n * spectral coefficients, and can be either owning (the array is deleted when\n * the `DiagonalModalOperator` goes out of scope) or non-owning, meaning it just\n * has a pointer to an array.\n *\n * `DiagonalModalOperator`s are intended to represent a diagonal matrix that can\n * operate (via the `*` operator) on spectral coefficients represented by\n * `ModalVector`s easily. Only basic mathematical operations are supported with\n * `DiagonalModalOperator`s. `DiagonalModalOperator`s may be added, subtracted,\n * multiplied, or divided, and may be multiplied with a `ModalVector`, which\n * results in a new `ModalVector`. This multiplication is commutative,\n * supporting the interpretation of the `ModalVector` as either a 'row' or a\n * 'column' vector.\n *\n * Also, addition, subtraction, multiplication and division of\n * `DiagonalModalOperator`s with doubles is supported.\n */\nclass DiagonalModalOperator : public VectorImpl<double, DiagonalModalOperator> {\n public:\n  DiagonalModalOperator() = default;\n  DiagonalModalOperator(const DiagonalModalOperator&) = default;\n  DiagonalModalOperator(DiagonalModalOperator&&) = default;\n  DiagonalModalOperator& operator=(const DiagonalModalOperator&) = default;\n  DiagonalModalOperator& operator=(DiagonalModalOperator&&) = default;\n  ~DiagonalModalOperator() = default;\n\n  using BaseType = VectorImpl<double, DiagonalModalOperator>;\n\n  using BaseType::operator=;\n  using BaseType::VectorImpl;\n};\n\nnamespace blaze {\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(DiagonalModalOperator);\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ModalVector,\n                                               DiagonalModalOperator,\n                                               MultTrait, ModalVector);\n\ntemplate <typename Operator>\nstruct MapTrait<DiagonalModalOperator, Operator> {\n  // Selectively allow unary operations for spectral coefficient operators\n  static_assert(\n      tmpl::list_contains_v<\n          tmpl::list<blaze::Bind1st<blaze::Add, double>,\n                     blaze::Bind2nd<blaze::Add, double>,\n                     blaze::Bind1st<blaze::Div, double>,\n                     blaze::Bind2nd<blaze::Div, double>,\n                     blaze::Bind1st<blaze::Sub, double>,\n                     blaze::Bind2nd<blaze::Sub, double>,\n                     blaze::Bind1st<blaze::Add, std::complex<double>>,\n                     blaze::Bind2nd<blaze::Add, std::complex<double>>,\n                     blaze::Bind1st<blaze::Div, std::complex<double>>,\n                     blaze::Bind2nd<blaze::Div, std::complex<double>>,\n                     blaze::Bind1st<blaze::Sub, std::complex<double>>,\n                     blaze::Bind2nd<blaze::Sub, std::complex<double>>>,\n          Operator>,\n      \"This unary operation is not permitted on a DiagonalModalOperator\");\n  using Type = DiagonalModalOperator;\n};\n\ntemplate <typename Operator>\nstruct MapTrait<DiagonalModalOperator, DiagonalModalOperator, Operator> {\n  // Forbid math operations in this specialization of BinaryMap traits for\n  // DiagonalModalOperator that are unlikely to be used on spectral\n  // coefficients. Currently no non-arithmetic binary operations are supported.\n  static_assert(\n      tmpl::list_contains_v<tmpl::list<>, Operator>,\n      \"This binary operation is not permitted on a DiagonalModalOperator.\");\n  using Type = DiagonalModalOperator;\n};\n}  // namespace blaze\n\nMAKE_STD_ARRAY_VECTOR_BINOPS(DiagonalModalOperator)\n\nMAKE_WITH_VALUE_IMPL_DEFINITION_FOR(DiagonalModalOperator)\n"
  },
  {
    "path": "src/DataStructures/DynamicBuffer.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DynamicBuffer.hpp\"\n\n#include <pup_stl.h>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\ntemplate <typename T>\nDynamicBuffer<T>::DynamicBuffer(const size_t number_of_vectors,\n                                const size_t number_of_grid_points)\n    : number_of_grid_points_(number_of_grid_points), data_(number_of_vectors) {\n  if constexpr (is_data_vector_type) {\n    buffer_.resize(number_of_vectors * number_of_grid_points);\n    set_references();\n  } else {\n    static_assert(\n        std::is_fundamental_v<T>,\n        \"T was found to be neither a DataVector nor a fundamental type. \"\n        \"`DynamicBuffer` is not implemented yet for complex numbers.\");\n    if (number_of_grid_points != 1) {\n      ERROR(\n          \"DynamicBuffer must have number_of_grid_points == 1 when T is a \"\n          \"fundamental type but has number_of_grid_points = \"\n          << number_of_grid_points_);\n    }\n  }\n}\n\ntemplate <typename T>\nDynamicBuffer<T>::DynamicBuffer(const DynamicBuffer<T>& other)\n    : number_of_grid_points_(other.number_of_grid_points_) {\n  if constexpr (is_data_vector_type) {\n    data_.resize(other.size());\n    buffer_ = other.buffer_;\n    set_references();\n  } else {\n    data_ = other.data_;\n  }\n}\n\ntemplate <typename T>\nDynamicBuffer<T>& DynamicBuffer<T>::operator=(const DynamicBuffer& other) {\n  if (this == &other) {\n    return *this;\n  }\n  number_of_grid_points_ = other.number_of_grid_points_;\n  if constexpr (is_data_vector_type) {\n    data_.resize(other.size());\n    buffer_ = other.buffer_;\n    set_references();\n  } else {\n    data_ = other.data_;\n  }\n  return *this;\n}\n\ntemplate <typename T>\nvoid DynamicBuffer<T>::pup(PUP::er& p) {\n  p | number_of_grid_points_;\n  p | buffer_;\n  if constexpr (is_data_vector_type) {\n    if (p.isUnpacking()) {\n      data_.resize(buffer_.size() / number_of_grid_points_);\n      set_references();\n    }\n  } else {\n    p | data_;\n  }\n}\n\ntemplate <typename T>\nvoid DynamicBuffer<T>::set_references() {\n  if constexpr (is_data_vector_type) {\n    for (size_t i = 0; i < size(); ++i) {\n      data_[i].set_data_ref(&buffer_[number_of_grid_points_ * i],\n                            number_of_grid_points_);\n    }\n  }\n}\n\ntemplate <typename T>\nbool operator==(const DynamicBuffer<T>& lhs, const DynamicBuffer<T>& rhs) {\n  return lhs.data_ == rhs.data_ and\n         lhs.number_of_grid_points_ == rhs.number_of_grid_points_;\n}\n\ntemplate <typename T>\nbool operator!=(const DynamicBuffer<T>& lhs, const DynamicBuffer<T>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GEN_OP(op, type)                                    \\\n  template bool operator op(const DynamicBuffer<type>& lhs, \\\n                            const DynamicBuffer<type>& rhs);\n#define INSTANTIATE(_, data)                 \\\n  template class DynamicBuffer<DTYPE(data)>; \\\n  GEN_OP(==, DTYPE(data))                    \\\n  GEN_OP(!=, DTYPE(data))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef GEN_OP\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/DataStructures/DynamicBuffer.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/**\n * \\ingroup DataStructuresGroup\n *\n * \\brief A dynamically sized vector of `DataVector`s. For convenience it can\n * also be instantiated for fundamental types.\n *\n * \\details This class is useful when one wants to create a `std::vector<T>`\n * with a size that is unknown at compile time. It allocates all `DataVector`s\n * in a single memory chunk rather than allocating each individually. If the\n * size of the vector is known at compile time, a `TempBuffer` object should be\n * used instead.\n *\n * Currently this can only be used for `DataVector`s, but if needed, it should\n * be fairly straightforward to generalize to `ComplexDataVector`.\n */\ntemplate <typename T>\nclass DynamicBuffer {\n public:\n  // Only vector type supported is DataVector\n  static constexpr bool is_data_vector_type = std::is_same_v<DataVector, T>;\n\n  DynamicBuffer() = default;\n\n  /*!\n   * Constructs a `DynamicBuffer`. The `number_of_vectors` corresponds to the\n   * number of `DataVector`s which are saved inside, each of which has size\n   * `number_of_grid_points`. `number_of_grid_points` has to be 1 if T is a\n   * fundamental type.\n   */\n  DynamicBuffer(size_t number_of_vectors, size_t number_of_grid_points);\n  ~DynamicBuffer() = default;\n  DynamicBuffer(DynamicBuffer&& other) = default;\n  DynamicBuffer& operator=(DynamicBuffer&& other) = default;\n\n  DynamicBuffer(const DynamicBuffer& other);\n\n  DynamicBuffer& operator=(const DynamicBuffer& other);\n\n  T& operator[](size_t index) { return data_[index]; }\n  T& at(size_t index) { return data_.at(index); }\n  const T& operator[](size_t index) const { return data_[index]; }\n  const T& at(size_t index) const { return data_.at(index); }\n\n  auto begin() { return data_.begin(); }\n  auto end() { return data_.end(); }\n  auto begin() const { return data_.begin(); }\n  auto end() const { return data_.end(); }\n\n  size_t size() const { return data_.size(); }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  // sets data references for all `data_` into `buffer_`\n  void set_references();\n\n  template <typename LocalT>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const DynamicBuffer<LocalT>& lhs,\n                         const DynamicBuffer<LocalT>& rhs);\n\n  size_t number_of_grid_points_;\n  // vector of non-owning DataVectors pointing into `buffer_`. In case of\n  // fundamental type T the data is saved in `data_` directly.\n  std::vector<T> data_;\n  // memory buffer for all DataVectors. Unused in case of fundamental type T.\n  std::vector<double> buffer_;\n};\n\ntemplate <typename T>\nbool operator!=(const DynamicBuffer<T>& lhs, const DynamicBuffer<T>& rhs);\n"
  },
  {
    "path": "src/DataStructures/DynamicMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DynamicMatrix.hpp\"\n\n#include <complex>\n\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/StdComplex.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace DynamicMatrix_detail {\n// Avoid including the entire option parser in a low-level header.\ntemplate <typename Type>\nstd::vector<std::vector<Type>> parse_to_vectors(\n    const Options::Option& options) {\n  return options.parse_as<std::vector<std::vector<Type>>>();\n}\n\n#define TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                      \\\n  template std::vector<std::vector<TYPE(data)>> parse_to_vectors( \\\n      const Options::Option& options);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, std::complex<double>))\n\n#undef INSTANTIATE\n#undef TYPE\n}  // namespace DynamicMatrix_detail\n"
  },
  {
    "path": "src/DataStructures/DynamicMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// `blaze::DynamicMatrix` is a general-purpose dynamically sized matrix type.\n/// This file implements interoperability of `blaze::DynamicMatrix` with our\n/// data structures.\n\n#pragma once\n\n#include <blaze/math/DynamicMatrix.h>\n#include <cstddef>\n#include <pup.h>\n#include <type_traits>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace PUP {\n/// @{\n/// Serialization of blaze::DynamicMatrix\ntemplate <typename Type, bool SO, typename Alloc, typename Tag>\nvoid pup(er& p, blaze::DynamicMatrix<Type, SO, Alloc, Tag>& t) {\n  size_t rows = t.rows();\n  size_t columns = t.columns();\n  p | rows;\n  p | columns;\n  if (p.isUnpacking()) {\n    t.resize(rows, columns);\n  }\n  const size_t spacing = t.spacing();\n  const size_t data_size = spacing * (SO == blaze::rowMajor ? rows : columns);\n  if (std::is_fundamental_v<Type>) {\n    PUParray(p, t.data(), data_size);\n  } else {\n    for (size_t i = 0; i < data_size; ++i) {\n      p | t.data()[i];\n    }\n  }\n}\ntemplate <typename Type, bool SO, typename Alloc, typename Tag>\nvoid operator|(er& p, blaze::DynamicMatrix<Type, SO, Alloc, Tag>& t) {\n  pup(p, t);\n}\n/// @}\n}  // namespace PUP\n\nnamespace DynamicMatrix_detail {\ntemplate <typename Type>\nstd::vector<std::vector<Type>> parse_to_vectors(const Options::Option& options);\n}  // namespace DynamicMatrix_detail\n\ntemplate <typename Type, bool SO, typename Alloc, typename Tag>\nstruct Options::create_from_yaml<blaze::DynamicMatrix<Type, SO, Alloc, Tag>> {\n  template <typename Metavariables>\n  static blaze::DynamicMatrix<Type, SO, Alloc, Tag> create(\n      const Options::Option& options) {\n    const auto data = DynamicMatrix_detail::parse_to_vectors<Type>(options);\n    const size_t num_rows = data.size();\n    size_t num_cols = 0;\n    if (num_rows > 0) {\n      num_cols = data[0].size();\n    }\n    blaze::DynamicMatrix<Type, SO, Alloc, Tag> result(num_rows, num_cols);\n    for (size_t i = 0; i < num_rows; i++) {\n      const auto& row = gsl::at(data, i);\n      if (row.size() != num_cols) {\n        PARSE_ERROR(options.context(),\n                    \"All matrix rows must have the same size.\");\n      }\n      std::copy(row.begin(), row.end(), blaze::row(result, i).begin());\n    }\n    return result;\n  }\n};\n\n/// Write a `blaze::DynamicMatrix` to a CSV file.\ntemplate <typename Type, bool SO, typename Alloc, typename Tag>\nstd::ostream& write_csv(\n    std::ostream& os, const blaze::DynamicMatrix<Type, SO, Alloc, Tag>& matrix,\n    const std::string& delimiter = \",\") {\n  for (size_t i = 0; i < matrix.rows(); ++i) {\n    for (size_t j = 0; j < matrix.columns(); ++j) {\n      os << matrix(i, j);\n      if (j + 1 != matrix.columns()) {\n        os << delimiter;\n      }\n    }\n    os << '\\n';\n  }\n  return os;\n}\n"
  },
  {
    "path": "src/DataStructures/DynamicVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DynamicVector.hpp\"\n\n#include <complex>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/StdComplex.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace DynamicVector_detail {\n// Avoid including the entire option parser in a low-level header.\ntemplate <typename T>\nstd::vector<T> parse_to_vector(const Options::Option& options) {\n  return options.parse_as<std::vector<T>>();\n}\n\n#define TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                        \\\n  template std::vector<TYPE(data)> parse_to_vector( \\\n      const Options::Option& options);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, std::complex<double>))\n\n#undef INSTANTIATE\n#undef TYPE\n}  // namespace DynamicVector_detail\n"
  },
  {
    "path": "src/DataStructures/DynamicVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// `blaze::DynamicVector` is a general-purpose dynamically sized vector type.\n/// This file implements interoperability of `blaze::DynamicVector` with our\n/// data structures.\n\n#pragma once\n\n#include <blaze/math/DynamicVector.h>\n#include <pup.h>\n#include <type_traits>\n#include <vector>\n\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\n/// \\cond\nnamespace Options {\nstruct Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace PUP {\n/// @{\n/// Serialization of blaze::DynamicVector\ntemplate <typename T, bool TF, typename Tag>\nvoid pup(er& p, blaze::DynamicVector<T, TF, Tag>& t) {\n  size_t size = t.size();\n  p | size;\n  if (p.isUnpacking()) {\n    t.resize(size);\n  }\n  if (std::is_fundamental_v<T>) {\n    PUParray(p, t.data(), size);\n  } else {\n    for (T& element : t) {\n      p | element;\n    }\n  }\n}\ntemplate <typename T, bool TF, typename Tag>\nvoid operator|(er& p, blaze::DynamicVector<T, TF, Tag>& t) {\n  pup(p, t);\n}\n/// @}\n}  // namespace PUP\n\nnamespace MakeWithValueImpls {\ntemplate <typename T, bool TF, typename Tag>\nstruct NumberOfPoints<blaze::DynamicVector<T, TF, Tag>> {\n  static SPECTRE_ALWAYS_INLINE size_t\n  apply(const blaze::DynamicVector<T, TF, Tag>& input) {\n    return input.size();\n  }\n};\n\ntemplate <typename T, bool TF, typename Tag>\nstruct MakeWithSize<blaze::DynamicVector<T, TF, Tag>> {\n  static SPECTRE_ALWAYS_INLINE blaze::DynamicVector<T, TF, Tag> apply(\n      const size_t size, const T& value) {\n    return blaze::DynamicVector<T, TF, Tag>(size, value);\n  }\n};\n}  // namespace MakeWithValueImpls\n\ntemplate <typename T, bool TF, typename Tag>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n    blaze::DynamicVector<T, TF, Tag>> {\n  static constexpr bool is_trivial = false;\n  static SPECTRE_ALWAYS_INLINE void apply(\n      const gsl::not_null<blaze::DynamicVector<T, TF, Tag>*> result,\n      const size_t size) {\n    result->resize(size);\n  }\n};\n\nnamespace DynamicVector_detail {\ntemplate <typename T>\nstd::vector<T> parse_to_vector(const Options::Option& options);\n}  // namespace DynamicVector_detail\n\ntemplate <typename T, bool TF, typename Tag>\nstruct Options::create_from_yaml<blaze::DynamicVector<T, TF, Tag>> {\n  template <typename Metavariables>\n  static blaze::DynamicVector<T, TF, Tag> create(\n      const Options::Option& options) {\n    const auto data = DynamicVector_detail::parse_to_vector<T>(options);\n    blaze::DynamicVector<T, TF, Tag> result(data.size());\n    std::copy(std::begin(data), std::end(data), result.begin());\n    return result;\n  }\n};\n"
  },
  {
    "path": "src/DataStructures/ExtractPoint.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup DataStructuresGroup\n/// Copy a given index of each component of a `Tensor<DataVector>` or\n/// `Variables<DataVector>` into a `Tensor<double>`, single point\n/// `Tensor<DataVector>`, or single-point `Variables<DataVector>`.\n/// Also works with `ComplexDataVector`.\n///\n/// \\note There is no by-value overload extracting to a\n/// `Tensor<DataVector>`.  This is both for the practical reason that\n/// it would be ambiguous with the `Tensor<double>` overload and\n/// because allocating multiple `DataVector`s for the return type\n/// would usually be very inefficient.\n///\n/// \\see overwrite_point\n/// @{\ntemplate <typename VectorType, typename... Structure>\nvoid extract_point(\n    const gsl::not_null<Tensor<typename VectorType::value_type, Structure...>*>\n        destination,\n    const Tensor<VectorType, Structure...>& source, const size_t index) {\n  for (size_t i = 0; i < destination->size(); ++i) {\n    (*destination)[i] = source[i][index];\n  }\n}\n\ntemplate <typename VectorType, typename... Structure>\nTensor<typename VectorType::value_type, Structure...> extract_point(\n    const Tensor<VectorType, Structure...>& tensor, const size_t index) {\n  Tensor<typename VectorType::value_type, Structure...> result;\n  extract_point(make_not_null(&result), tensor, index);\n  return result;\n}\n\ntemplate <typename VectorType, typename... Structure>\nvoid extract_point(\n    const gsl::not_null<Tensor<VectorType, Structure...>*> destination,\n    const Tensor<VectorType, Structure...>& source, const size_t index) {\n  ASSERT(destination->begin()->size() == 1,\n         \"Output tensor components have wrong size: \"\n         << destination->begin()->size());\n  for (size_t i = 0; i < destination->size(); ++i) {\n    (*destination)[i][0] = source[i][index];\n  }\n}\n\ntemplate <typename... Tags>\nvoid extract_point(\n    const gsl::not_null<Variables<tmpl::list<Tags...>>*> result,\n    const Variables<tmpl::list<Tags...>>& variables, const size_t index) {\n  result->initialize(1);\n  expand_pack((extract_point(\n      make_not_null(&get<Tags>(*result)), get<Tags>(variables), index), 0)...);\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>> extract_point(\n    const Variables<tmpl::list<Tags...>>& variables, const size_t index) {\n  Variables<tmpl::list<Tags...>> result(1);\n  extract_point(make_not_null(&result), variables, index);\n  return result;\n}\n/// @}\n\n/// \\ingroup DataStructuresGroup\n/// Copy a `Tensor<double>`, single point `Tensor<DataVector>`, or\n/// single-point `Variables<DataVector>` into the given index of each\n/// component of a `Tensor<DataVector>` or `Variables<DataVector>`.\n///\n/// \\see extract_point\n/// @{\ntemplate <typename VectorType, typename... Structure>\nvoid overwrite_point(\n    const gsl::not_null<Tensor<VectorType, Structure...>*> destination,\n    const Tensor<typename VectorType::value_type, Structure...>& source,\n    const size_t index) {\n  for (size_t i = 0; i < destination->size(); ++i) {\n    (*destination)[i][index] = source[i];\n  }\n}\n\ntemplate <typename VectorType, typename... Structure>\nvoid overwrite_point(\n    const gsl::not_null<Tensor<VectorType, Structure...>*> destination,\n    const Tensor<VectorType, Structure...>& source, const size_t index) {\n  ASSERT(source.begin()->size() == 1,\n         \"Cannot overwrite with \" << source.begin()->size() << \" points.\");\n  for (size_t i = 0; i < destination->size(); ++i) {\n    (*destination)[i][index] = source[i][0];\n  }\n}\n\ntemplate <typename... Tags>\nvoid overwrite_point(\n    const gsl::not_null<Variables<tmpl::list<Tags...>>*> destination,\n    const Variables<tmpl::list<Tags...>>& source, const size_t index) {\n  ASSERT(source.number_of_grid_points() == 1,\n         \"Must overwrite with a single point.\");\n  expand_pack((overwrite_point(make_not_null(&get<Tags>(*destination)),\n                               extract_point(get<Tags>(source), 0), index),\n               0)...);\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/FixedHashMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <memory>  // std::addressof\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <stdexcept>\n#include <type_traits>\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/PrintHelpers.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nclass FixedHashMapIterator;\n/// \\endcond\n\nnamespace FixedHashMap_detail {\ntemplate <typename T, typename = std::void_t<>>\nstruct has_is_perfect_member : std::false_type {};\n\ntemplate <typename T>\nstruct has_is_perfect_member<T,\n                             std::void_t<decltype(T::template is_perfect<0>)>>\n    : std::true_type {};\n\ntemplate <typename T>\nconstexpr bool has_is_perfect_member_v = has_is_perfect_member<T>::value;\n\ntemplate <class T, size_t MaxSize,\n          bool HasIsPerfect = has_is_perfect_member_v<T>>\nconstexpr bool is_perfect = T::template is_perfect<MaxSize>;\n\ntemplate <class T, size_t MaxSize>\nconstexpr bool is_perfect<T, MaxSize, false> = false;\n}  // namespace FixedHashMap_detail\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A hash table with a compile-time specified maximum size and ability to\n * efficiently handle perfect hashes.\n *\n * There are a few requirements on the types passed to `FixedHashMap`. These\n * are:\n * - `Key` is CopyConstructible for copy and move semantics, as well as\n *   serialization\n * - `ValueType` is MoveConstructible\n * - If `Hash` is a perfect hash for some values of `MaxSize` then in order to\n *   use the perfect hashing improvements `Hash` must have a member variable\n *   template `is_perfect` that takes as its only template parameter a `size_t`\n *   equal to `MaxSize`. A perfect hash must map each `Key` to `[0, MaxSize)`\n *   uniquely.\n * - Keys are distinct.\n *\n * The interface is similar to std::unordered_map, so see the documentation for\n * that for details.\n *\n * #### Implementation Details\n *\n * - Uses linear probing for non-perfect hashes.\n * - The hash `Hash` is assumed to be a perfect hash for `MaxSize` if\n *   `Hash::template is_perfect<MaxSize>` is true.\n */\ntemplate <size_t MaxSize, class Key, class ValueType,\n          class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>\nclass FixedHashMap {\n  static constexpr bool hash_is_perfect =\n      FixedHashMap_detail::is_perfect<Hash, MaxSize>;\n\n public:\n  using key_type = Key;\n  using mapped_type = ValueType;\n  using value_type = std::pair<const key_type, mapped_type>;\n  using size_type = size_t;\n  using difference_type = ptrdiff_t;\n  using hasher = Hash;\n  using key_equal = KeyEqual;\n  using reference = value_type&;\n  using const_reference = const value_type&;\n  using pointer = value_type*;\n  using const_pointer = const value_type*;\n  using iterator =\n      FixedHashMapIterator<MaxSize, key_type, value_type, hasher, key_equal>;\n  using const_iterator =\n      FixedHashMapIterator<MaxSize, key_type, const value_type, hasher,\n                           key_equal>;\n\n  FixedHashMap() = default;\n  FixedHashMap(std::initializer_list<value_type> init);\n  FixedHashMap(const FixedHashMap&) = default;\n  FixedHashMap& operator=(const FixedHashMap& other);\n  FixedHashMap(FixedHashMap&&) = default;\n  FixedHashMap& operator=(FixedHashMap&& other);\n  ~FixedHashMap() = default;\n\n  iterator begin() { return unconst(cbegin()); }\n  const_iterator begin() const {\n    return const_iterator(data_.begin(), data_.end());\n  }\n  const_iterator cbegin() const { return begin(); }\n\n  iterator end() { return unconst(cend()); }\n  const_iterator end() const {\n    return const_iterator(data_.end(), data_.end());\n  }\n  const_iterator cend() const { return end(); }\n\n  bool empty() const { return size_ == 0; }\n  size_t size() const { return size_; }\n\n  void clear();\n\n  /// @{\n  /// Inserts the element if it does not exists.\n  std::pair<iterator, bool> insert(const value_type& value) {\n    return insert(value_type(value));\n  }\n  std::pair<iterator, bool> insert(value_type&& value) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n    return insert_or_assign_impl<false>(const_cast<key_type&&>(value.first),\n                                        std::move(value.second));\n  }\n  template <typename... Args>\n  std::pair<iterator, bool> emplace(Args&&... args) {\n    return insert(value_type(std::forward<Args>(args)...));\n  }\n  /// @}\n  /// @{\n  /// Inserts the element if it does not exists, otherwise assigns to it the new\n  /// value.\n  template <class M>\n  std::pair<iterator, bool> insert_or_assign(const key_type& key, M&& obj) {\n    return insert_or_assign(key_type{key}, std::forward<M>(obj));\n  }\n  template <class M>\n  std::pair<iterator, bool> insert_or_assign(key_type&& key, M&& obj) {\n    return insert_or_assign_impl<true>(std::move(key), std::forward<M>(obj));\n  }\n  /// @}\n\n  iterator erase(const const_iterator& pos);\n  size_t erase(const key_type& key);\n\n  mapped_type& at(const key_type& key);\n  const mapped_type& at(const key_type& key) const;\n  mapped_type& operator[](const key_type& key);\n\n  size_t count(const key_type& key) const;\n  iterator find(const key_type& key) {\n    return unconst(std::as_const(*this).find(key));\n  }\n  const_iterator find(const key_type& key) const {\n    auto it = get_data_entry(key);\n    return it != data_.end() and is_set(*it) ? const_iterator(&*it, data_.end())\n                                             : end();\n  }\n\n  /// Check if `key` is in the map\n  bool contains(const key_type& key) const { return find(key) != end(); }\n\n  /// Get key equal function object\n  key_equal key_eq() const { return key_equal{}; }\n  /// Get hash function object\n  hasher hash_function() const { return hasher{}; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <size_t FMaxSize, class FKey, class FValueType, class FHash,\n            class FKeyEqual>\n  // NOLINTNEXTLINE(readability-redundant-declaration) false positive\n  friend bool operator==(\n      const FixedHashMap<FMaxSize, FKey, FValueType, FHash, FKeyEqual>& a,\n      const FixedHashMap<FMaxSize, FKey, FValueType, FHash, FKeyEqual>& b);\n\n  template <bool Assign, class M>\n  std::pair<iterator, bool> insert_or_assign_impl(key_type&& key, M&& obj);\n\n  using storage_type = std::array<std::optional<value_type>, MaxSize>;\n\n  SPECTRE_ALWAYS_INLINE size_type hash(const key_type& key) const {\n    if constexpr (hash_is_perfect) {\n      return Hash{}(key);\n    } else {\n      return Hash{}(key) % MaxSize;\n    }\n  }\n\n  template <bool IsInserting>\n  typename storage_type::iterator get_data_entry(const Key& key);\n\n  typename storage_type::const_iterator get_data_entry(const Key& key) const {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n    return const_cast<FixedHashMap&>(*this).get_data_entry<false>(key);\n  }\n\n  iterator unconst(const const_iterator& it) {\n    return iterator(&it.get_optional() - data_.begin() + data_.begin(),\n                    data_.end());\n  }\n\n  SPECTRE_ALWAYS_INLINE static bool is_set(\n      const std::optional<value_type>& opt) {\n    return static_cast<bool>(opt);\n  }\n\n  storage_type data_{};\n  tmpl::conditional_t<(MaxSize <= 255), uint8_t, size_t> size_ = 0;\n};\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nvoid FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::pup(PUP::er& p) {\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  const uint8_t current_version = 1;\n  if (p.isUnpacking()) {\n    uint8_t version_read{0};\n    p | version_read;\n    if (LIKELY(version_read > 0)) {\n      p | data_;\n      p | size_;\n    } else {\n      // Read next 3 bytes.\n      p | version_read;\n      p | version_read;\n      p | version_read;\n      // Read \"bottom\" of version\n      uint32_t old_version{0};\n      p | old_version;\n      if (UNLIKELY(old_version > 0)) {\n        ERROR(\n            \"Incompatible version format for Direction. Expected to receive \"\n            \"version 0 but got \"\n            << old_version);\n      }\n      p | data_;\n      if constexpr (MaxSize <= 255) {\n        size_t read_size{0};\n        p | read_size;\n        size_ = static_cast<uint8_t>(read_size);\n      } else {\n        p | size_;\n      }\n    }\n  } else {\n    uint8_t version_write = current_version;\n    p | version_write;\n    p | data_;\n    p | size_;\n  }\n}\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nclass FixedHashMapIterator {\n public:\n  using iterator_category = std::forward_iterator_tag;\n  using value_type = std::remove_const_t<ValueType>;\n  using difference_type = ptrdiff_t;\n  using pointer = ValueType*;\n  using reference = ValueType&;\n\n  FixedHashMapIterator() = default;\n\n  /// Implicit conversion from mutable to const iterator.\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  operator FixedHashMapIterator<MaxSize, Key, const ValueType, Hash, KeyEqual>()\n      const {\n    return {entry_, end_};\n  }\n\n  reference operator*() const {\n    ASSERT(entry_ != nullptr, \"Invalid FixedHashMapIterator\");\n    ASSERT(is_set(*entry_),\n           \"FixedHashMapIterator points to an invalid value in the map.\");\n    // deference pointer, then dereference std::optional\n    return entry_->value();\n  }\n  pointer operator->() const {\n    ASSERT(entry_ != nullptr, \"Invalid FixedHashMapIterator\");\n    ASSERT(is_set(*entry_),\n           \"FixedHashMapIterator points to an invalid value in the map.\");\n    return std::addressof(entry_->value());\n  }\n\n  FixedHashMapIterator& operator++();\n  FixedHashMapIterator operator++(int);\n\n private:\n  friend class FixedHashMap<MaxSize, Key, typename value_type::second_type,\n                            Hash, KeyEqual>;\n  friend class FixedHashMapIterator<MaxSize, Key, value_type, Hash, KeyEqual>;\n\n  friend bool operator==(const FixedHashMapIterator& a,\n                         const FixedHashMapIterator& b) {\n    return a.entry_ == b.entry_;\n  }\n  friend bool operator<(const FixedHashMapIterator& a,\n                        const FixedHashMapIterator& b) {\n    return a.entry_ < b.entry_;\n  }\n\n  // The rest of the comparison operators don't need access to the\n  // class internals, but they need to be instantiated for each\n  // instantiation of the class.\n  friend bool operator!=(const FixedHashMapIterator& a,\n                         const FixedHashMapIterator& b) {\n    return not(a == b);\n  }\n  friend bool operator>(const FixedHashMapIterator& a,\n                        const FixedHashMapIterator& b) {\n    return b < a;\n  }\n  friend bool operator<=(const FixedHashMapIterator& a,\n                         const FixedHashMapIterator& b) {\n    return not(b < a);\n  }\n  friend bool operator>=(const FixedHashMapIterator& a,\n                         const FixedHashMapIterator& b) {\n    return not(a < b);\n  }\n\n  using map_optional_type = std::optional<std::remove_const_t<ValueType>>;\n  using optional_type =\n      tmpl::conditional_t<std::is_const<ValueType>::value,\n                          const map_optional_type, map_optional_type>;\n\n  SPECTRE_ALWAYS_INLINE static bool is_set(const optional_type& opt) {\n    return static_cast<bool>(opt);\n  }\n\n  FixedHashMapIterator(optional_type* const entry, optional_type* const end)\n      : entry_(entry), end_(end) {\n    if (entry_ != end_ and not*entry_) {\n      operator++();\n    }\n  }\n\n  optional_type& get_optional() const { return *entry_; }\n\n  optional_type* entry_{nullptr};\n  optional_type* end_{nullptr};\n};\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nFixedHashMapIterator<MaxSize, Key, ValueType, Hash, KeyEqual>&\nFixedHashMapIterator<MaxSize, Key, ValueType, Hash, KeyEqual>::operator++() {\n  ASSERT(entry_ != end_,\n         \"Tried to increment an end iterator, which is undefined behavior.\");\n  // Move to next element, which might not be the next element in the std::array\n  do {\n    ++entry_;  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  } while (entry_ < end_ and not is_set(*entry_));\n  return *this;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nFixedHashMapIterator<MaxSize, Key, ValueType, Hash, KeyEqual>\nFixedHashMapIterator<MaxSize, Key, ValueType, Hash, KeyEqual>::operator++(int) {\n  const auto ret = *this;\n  operator++();\n  return ret;\n}\n/// \\endcond\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nFixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>&\nFixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::operator=(\n    const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& other) {\n  if (this == &other) {\n    return *this;\n  }\n  clear();\n  size_ = other.size_;\n  for (size_t i = 0; i < data_.size(); ++i) {\n    const auto& other_optional = gsl::at(other.data_, i);\n    if (is_set(other_optional)) {\n      // The std::optionals cannot be assigned to because they\n      // contain the map keys, which are const, so we have to replace\n      // the contents instead, since that counts as a destroy +\n      // initialize.\n      gsl::at(data_, i).emplace(*other_optional);\n    }\n  }\n  return *this;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nFixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>&\nFixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::operator=(\n    FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>&& other) {\n  if (this == &other) {\n    return *this;\n  }\n  clear();\n  size_ = other.size_;\n  for (size_t i = 0; i < data_.size(); ++i) {\n    auto& other_optional = gsl::at(other.data_, i);\n    if (is_set(other_optional)) {\n      // The std::optionals cannot be assigned to because they\n      // contain the map keys, which are const, so we have to replace\n      // the contents instead, since that counts as a destroy +\n      // initialize.\n      gsl::at(data_, i).emplace(std::move(*other_optional));\n    }\n  }\n  return *this;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nFixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::FixedHashMap(\n    std::initializer_list<value_type> init) {\n  // size_ is set by calling insert\n  for (const auto& entry : init) {\n    insert(entry);\n  }\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nvoid FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::clear() {\n  for (auto& entry : data_) {\n    entry.reset();\n  }\n  size_ = 0;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\ntemplate <bool Assign, class M>\nauto FixedHashMap<MaxSize, Key, ValueType, Hash,\n                  KeyEqual>::insert_or_assign_impl(key_type&& key, M&& obj)\n    -> std::pair<iterator, bool> {\n  auto data_it = get_data_entry<true>(key);\n  if (UNLIKELY(data_it == data_.end())) {\n    ERROR(\"Unable to insert element into FixedHashMap of maximum size \"\n          << MaxSize << \" because it is full. If the current size (\" << size_\n          << \") is not equal to the maximum size then please file an issue \"\n             \"with the code you used to produce this error. \"\n          << (hash_is_perfect ? \" If a perfect hash is used and it hashes to \"\n                                \"past the last element stored, then this \"\n                                \"error may also be triggered.\"\n                              : \"\"));\n  }\n  // data_it points to either the existing element or a new bucket to place the\n  // element.\n  const auto is_new_entry = not is_set(*data_it);\n  if (is_new_entry or Assign) {\n    if (is_new_entry) {\n      ++size_;\n    }\n    data_it->emplace(std::move(key), std::forward<M>(obj));\n  }\n  return std::make_pair(iterator{data_it, data_.end()}, is_new_entry);\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nauto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::erase(\n    const const_iterator& pos) -> iterator {\n  auto next_it = &(unconst(pos).get_optional());\n  --size_;\n  next_it->reset();\n  // pos now points to an unset entry, advance to next valid one\n  do {\n    ++next_it;\n    if (next_it == data_.end()) {\n      next_it = data_.begin();\n    }\n    if (is_set(*next_it)) {\n      return {next_it, data_.end()};\n    }\n  } while (next_it != &pos.get_optional());\n  return end();\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nsize_t FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::erase(\n    const key_type& key) {\n  auto it = get_data_entry<false>(key);\n  if (it == data_.end() or not is_set(*it)) {\n    return 0;\n  }\n  it->reset();\n  --size_;\n  return 1;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nauto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::at(\n    const key_type& key) -> mapped_type& {\n  auto it = get_data_entry<false>(key);\n  if (it == data_.end() or not is_set(*it)) {\n    // Use `ERROR_AS` instead of `throw` to print a backtrace.\n    ERROR_AS(get_output(key) + \" not in FixedHashMap\", std::out_of_range);\n  }\n  return it->value().second;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nauto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::at(\n    const key_type& key) const -> const mapped_type& {\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n  return const_cast<FixedHashMap&>(*this).at(key);\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nauto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::operator[](\n    const key_type& key) -> mapped_type& {\n  auto it = get_data_entry<true>(key);\n  if (it == data_.end()) {\n    ERROR(\"Unable to insert element into FixedHashMap of maximum size \"\n          << MaxSize << \" because it is full. If the current size (\" << size_\n          << \") is not equal to the maximum size then please file an issue \"\n             \"with the code you used to produce this error. \"\n          << (hash_is_perfect ? \" If a perfect hash is used and it hashes to \"\n                                \"past the last element stored, then this \"\n                                \"error may also be triggered.\"\n                              : \"\"));\n  }\n  if (not is_set(*it)) {\n    ++size_;\n    it->emplace(key, mapped_type{});\n  }\n  return it->value().second;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nsize_t FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::count(\n    const key_type& key) const {\n  const auto it = get_data_entry(key);\n  return it == data_.end() or not is_set(*it) ? 0 : 1;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\ntemplate <bool IsInserting>\nauto FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>::get_data_entry(\n    const Key& key) -> typename storage_type::iterator {\n  const auto hashed_key = hash(key);\n  auto it = data_.begin() + hashed_key;\n  if constexpr (not hash_is_perfect) {\n    // First search for an existing key.\n    while (not is_set(*it) or it->value().first != key) {\n      if (++it == data_.end()) {\n        it = data_.begin();\n      }\n      if (UNLIKELY(it == data_.begin() + hashed_key)) {\n        // not found, return end iterator to storage_type\n        it = data_.end();\n        break;\n      }\n    }\n    // If we are inserting an element but couldn't find an existing key, then\n    // find the first available bucket.\n    if (IsInserting and it == data_.end()) {\n      it = data_.begin() + hashed_key;\n      while (is_set(*it)) {\n        if (++it == data_.end()) {\n          it = data_.begin();\n        }\n        if (UNLIKELY(it == data_.begin() + hashed_key)) {\n          // could not find any open bucket\n          return data_.end();\n        }\n      }\n    }\n  }\n  return it;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nbool operator==(\n    const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& a,\n    const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& b) {\n  if (a.size_ != b.size_) {\n    return false;\n  }\n  for (const auto& [key, value] : a) {\n    const auto found_in_b = b.find(key);\n    if (found_in_b == b.end() or found_in_b->second != value) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nbool operator!=(\n    const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& a,\n    const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& b) {\n  return not(a == b);\n}\n\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nstd::ostream& operator<<(\n    std::ostream& os,\n    const FixedHashMap<MaxSize, Key, ValueType, Hash, KeyEqual>& m) {\n  unordered_print_helper(\n      os, std::begin(m), std::end(m),\n      [](std::ostream& out,\n         const typename FixedHashMap<MaxSize, Key, ValueType, Hash,\n                                     KeyEqual>::value_type& entry) {\n        out << \"[\";\n        print_value(out, entry.first);\n        out << \",\";\n        print_value(out, entry.second);\n        out << \"]\";\n      });\n  return os;\n}\n"
  },
  {
    "path": "src/DataStructures/FloatingPointType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/FloatingPointType.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nstd::ostream& operator<<(std::ostream& os, const FloatingPointType& t) {\n  switch (t) {\n    case FloatingPointType::Float:\n      return os << \"Float\";\n    case FloatingPointType::Double:\n      return os << \"Double\";\n    default:\n      ERROR(\"Unknown floating point type, must be Float or Double\");\n  }\n}\n\ntemplate <>\nFloatingPointType Options::create_from_yaml<FloatingPointType>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (type_read == get_output(FloatingPointType::Float)) {\n    return FloatingPointType::Float;\n  } else if (type_read == get_output(FloatingPointType::Double)) {\n    return FloatingPointType::Double;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read << \"\\\" to FloatingPointType. Must be one of '\"\n                  << get_output(FloatingPointType::Float) << \"' or '\"\n                  << get_output(FloatingPointType::Double) << \"'.\");\n}\n"
  },
  {
    "path": "src/DataStructures/FloatingPointType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\n/// \\ingroup DataStructuresGroup\n/// \\brief Which floating point type to use\n///\n/// An example use-case is for specifying in an input file to what precision\n/// data is written to disk, since most simulations will not have full double\n/// precision accuracy on volume data and we don't need all digits to visualize\n/// the data.\nenum class FloatingPointType { Float, Double };\n\nstd::ostream& operator<<(std::ostream& os, const FloatingPointType& t);\n\ntemplate <>\nstruct Options::create_from_yaml<FloatingPointType> {\n  template <typename Metavariables>\n  static FloatingPointType create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nFloatingPointType Options::create_from_yaml<FloatingPointType>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/DataStructures/IdPair.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n#include <pup.h>\n#include <type_traits>\n#include <utility>\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A data structure that contains an ID and data associated with that ID.\n */\ntemplate <typename IdType, typename DataType>\nstruct IdPair {\n  IdPair();\n  IdPair(IdType id_in, DataType data_in);\n\n  IdType id{};\n  DataType data{};\n};\n\ntemplate <typename IdType, typename DataType>\nIdPair<IdType, DataType>::IdPair() = default;\n\ntemplate <typename IdType, typename DataType>\nIdPair<IdType, DataType>::IdPair(IdType id_in, DataType data_in)\n    : id(std::move(id_in)), data(std::move(data_in)) {}\n\ntemplate <typename IdType, typename DataType>\nIdPair<std::decay_t<IdType>, std::decay_t<DataType>> make_id_pair(\n    IdType&& id, DataType&& data) {\n  return {std::forward<IdType>(id), std::forward<DataType>(data)};\n}\n\n/// \\cond\n// We write the pup function as a free function to keep IdPair a POD\n// clang-tidy: no non-const references\ntemplate <typename IdType, typename DataType>\nvoid pup(PUP::er& p, IdPair<IdType, DataType>& t) {  // NOLINT\n  p | t.id;\n  p | t.data;\n}\n\n// clang-tidy: no non-const references\ntemplate <typename IdType, typename DataType>\nvoid operator|(PUP::er& p, IdPair<IdType, DataType>& t) {  // NOLINT\n  pup(p, t);\n}\n\ntemplate <typename IdType, typename DataType>\nbool operator==(const IdPair<IdType, DataType>& lhs,\n                const IdPair<IdType, DataType>& rhs) {\n  return lhs.id == rhs.id and lhs.data == rhs.data;\n}\n\ntemplate <typename IdType, typename DataType>\nbool operator!=(const IdPair<IdType, DataType>& lhs,\n                const IdPair<IdType, DataType>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename IdType, typename DataType>\nstd::ostream& operator<<(std::ostream& os, const IdPair<IdType, DataType>& t) {\n  return os << '(' << t.id << ',' << t.data << ')';\n}\n/// \\endcond\n"
  },
  {
    "path": "src/DataStructures/Index.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Index.hpp\"\n\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\ntemplate <size_t Dim>\nvoid Index<Dim>::pup(PUP::er& p) {\n  p | indices_;\n}\n\ntemplate <size_t N>\nstd::ostream& operator<<(std::ostream& os, const Index<N>& i) {\n  return os << i.indices_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const Index<Dim>& lhs, const Index<Dim>& rhs) {\n  return std::equal(lhs.begin(), lhs.end(), rhs.begin());\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Index<Dim>& lhs, const Index<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GEN_OP(op, dim) \\\n  template bool operator op(const Index<dim>& lhs, const Index<dim>& rhs);\n#define INSTANTIATE(_, data)                          \\\n  template class Index<DIM(data)>;                    \\\n  GEN_OP(==, DIM(data))                               \\\n  GEN_OP(!=, DIM(data))                               \\\n  template std::ostream& operator<<(std::ostream& os, \\\n                                    const Index<DIM(data)>& i);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2, 3, 4))\n\n#undef DIM\n#undef GEN_OP\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/DataStructures/Index.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class template Index.\n\n#pragma once\n\n#include <array>\n#include <concepts>\n#include <cstddef>\n#include <limits>\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\n/// \\ingroup DataStructuresGroup\n/// An integer multi-index.\n///\n/// \\tparam Dim the number of integers in the Index.\ntemplate <size_t Dim>\nclass Index {\n public:\n  /// Construct with each element set to the same value.\n  explicit Index(const size_t i0 = std::numeric_limits<size_t>::max())\n      : indices_(make_array<Dim>(i0)) {}\n\n  /// Construct specifying value in each dimension\n  template <std::integral... I>\n    requires(sizeof...(I) == Dim and sizeof...(I) > 1)\n  explicit Index(I... i) : indices_(make_array(static_cast<size_t>(i)...)) {}\n\n  explicit Index(std::array<size_t, Dim> i) : indices_(std::move(i)) {}\n\n  size_t operator[](const size_t d) const { return gsl::at(indices_, d); }\n  size_t& operator[](const size_t d) { return gsl::at(indices_, d); }\n\n  typename std::array<size_t, Dim>::iterator begin() {\n    return indices_.begin();\n  }\n  typename std::array<size_t, Dim>::const_iterator begin() const {\n    return indices_.begin();\n  }\n\n  typename std::array<size_t, Dim>::iterator end() { return indices_.end(); }\n  typename std::array<size_t, Dim>::const_iterator end() const {\n    return indices_.end();\n  }\n\n  size_t size() const { return Dim; }\n\n  /// The product of the indices.\n  /// If Dim = 0, the product is defined as 1.\n  template <size_t N = Dim>\n    requires(N > 0)\n  constexpr size_t product() const {\n    return indices_[N - 1] * product<N - 1>();\n  }\n  /// \\cond\n  // Specialization for N = 0 to stop recursion\n  template <size_t N = Dim>\n    requires(N == 0)\n  constexpr size_t product() const {\n    return 1;\n  }\n  /// \\endcond\n\n  /// Return a smaller Index with the d-th element removed.\n  ///\n  /// \\param d the element to remove.\n  template <size_t N = Dim>\n    requires(N > 0)\n  Index<Dim - 1> slice_away(const size_t d) const {\n    ASSERT(d < Dim,\n           \"Can't slice dimension \" << d << \" from an Index<\" << Dim << \">\");\n    std::array<size_t, Dim - 1> t{};\n    for (size_t i = 0; i < d; ++i) {\n      gsl::at(t, i) = gsl::at(indices_, i);\n    }\n    for (size_t i = d + 1; i < Dim; ++i) {\n      gsl::at(t, i - 1) = gsl::at(indices_, i);\n    }\n    return Index<Dim - 1>(t);\n  }\n\n  /// \\cond\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n  /// \\endcond\n\n  template <size_t N>\n  friend std::ostream& operator<<(std::ostream& os,  // NOLINT\n                                  const Index<N>& i);\n\n  const size_t* data() const { return indices_.data(); }\n  size_t* data() { return indices_.data(); }\n\n  const std::array<size_t, Dim>& indices() const { return indices_; }\n\n private:\n  std::array<size_t, Dim> indices_;\n};\n\n/// \\ingroup DataStructuresGroup\n/// Get the collapsed index into a 1D array of the data corresponding to this\n/// Index. Note that the first dimension of the Index varies fastest when\n/// computing the collapsed index.\ntemplate <size_t N>\nsize_t collapsed_index(const Index<N>& index, const Index<N>& extents);\n\n/// \\ingroup DataStructuresGroup\n/// This is the inverse function of collapsed_index. Given a collapsed (1D)\n/// index and the extents of the array, return the Index corresponding to that\n/// collapsed index. Note that the first dimension of the Index varies fastest\n/// when computing the collapsed index.\ntemplate <size_t Dim>\nIndex<Dim> expanded_index(size_t index, const Index<Dim>& extents);\n\ntemplate <size_t N>\nstd::ostream& operator<<(std::ostream& os, const Index<N>& i);\n\n/// \\cond HIDDEN_SYMBOLS\n#ifdef SPECTRE_DEBUG\nnamespace Index_detail {\ntemplate <size_t Dim>\nvoid collapsed_index_check(const Index<Dim>& index, const Index<Dim>& extents) {\n  for (size_t d = 0; d < Dim; ++d) {\n    ASSERT(index[d] < extents[d], \"The requested index in the dimension \"\n                                      << d << \" with value \" << index[d]\n                                      << \" exceeds the number of grid \"\n                                         \"points \"\n                                      << extents[d]);\n  }\n}\n}  // namespace Index_detail\n#endif\n\n// the specializations are in the header file so they can be inlined. We use\n// specializations to avoid having loops since this computation is very\n// straightforward.\ntemplate <>\nSPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<0>& /*index*/,\n                                             const Index<0>& /*extents*/) {\n  return 0;\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<1>& index,\n                                             const Index<1>& extents) {\n  (void)extents;\n#ifdef SPECTRE_DEBUG\n  Index_detail::collapsed_index_check(index, extents);\n#endif\n  return index[0];\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<2>& index,\n                                             const Index<2>& extents) {\n#ifdef SPECTRE_DEBUG\n  Index_detail::collapsed_index_check(index, extents);\n#endif\n  return index[0] + extents[0] * index[1];\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<3>& index,\n                                             const Index<3>& extents) {\n#ifdef SPECTRE_DEBUG\n  Index_detail::collapsed_index_check(index, extents);\n#endif\n  return index[0] + extents[0] * (index[1] + extents[1] * index[2]);\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE size_t collapsed_index(const Index<4>& index,\n                                             const Index<4>& extents) {\n#ifdef SPECTRE_DEBUG\n  Index_detail::collapsed_index_check(index, extents);\n#endif\n  return index[0] +\n         extents[0] *\n             (index[1] + extents[1] * (index[2] + extents[2] * index[3]));\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE Index<1> expanded_index(size_t index,\n                                              const Index<1>& extents) {\n  (void)extents;\n  return Index<1>{index};\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE Index<2> expanded_index(size_t index,\n                                              const Index<2>& extents) {\n  const size_t ix = index % extents[0];\n  const size_t iy = index / extents[0];\n  return Index<2>{{ix, iy}};\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE Index<3> expanded_index(size_t index,\n                                              const Index<3>& extents) {\n  const size_t ix = index % extents[0];\n  const size_t iy = (index / extents[0]) % extents[1];\n  const size_t iz = (index / extents[0]) / extents[1];\n  return Index<3>{{ix, iy, iz}};\n}\n\ntemplate <size_t N>\nbool operator==(const Index<N>& lhs, const Index<N>& rhs);\n\ntemplate <size_t N>\nbool operator!=(const Index<N>& lhs, const Index<N>& rhs);\n/// \\endcond\n"
  },
  {
    "path": "src/DataStructures/IndexIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/IndexIterator.hpp\"\n\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t Dim>\nIndexIterator<Dim>::IndexIterator(Index<Dim> extents)\n    : extents_(std::move(extents)),\n      index_(0),\n      collapsed_index_(0),\n      valid_(true) {}\n\ntemplate <size_t Dim>\nIndexIterator<Dim>& IndexIterator<Dim>::operator++() {\n  for (size_t d = 0;; ++d) {\n    ++index_[d];\n    if (extents_[d] > index_[d]) {\n      break;\n    }\n    index_[d] = 0;\n    if (UNLIKELY(Dim == d + 1)) {\n      valid_ = false;\n      break;\n    }\n  }\n  ++collapsed_index_;\n  return *this;\n}\n\ntemplate <>\nIndexIterator<0>& IndexIterator<0>::operator++() {\n  valid_ = false;\n  return *this;\n}\n\ntemplate class IndexIterator<0>;\ntemplate class IndexIterator<1>;\ntemplate class IndexIterator<2>;\ntemplate class IndexIterator<3>;\ntemplate class IndexIterator<4>;\n"
  },
  {
    "path": "src/DataStructures/IndexIterator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines IndexIterator.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n\n/// \\ingroup DataStructuresGroup\n/// IndexIterator iterates over a unique set of Index.\n///\n/// \\example\n/// \\snippet Test_IndexIterator.cpp index_iterator_example\n///\n/// Each integer of the Index will vary from 0 to extents[d] - 1,\n/// with the lowest dimension varying the fastest.\ntemplate <std::size_t Dim>\nclass IndexIterator {\n public:\n  /// Construct from an Index\n  explicit IndexIterator(Index<Dim> extents);\n  /// It does not make sense to default construct an IndexIterator\n  IndexIterator() = delete;\n  /// \\cond HIDDEN_SYMBOLS\n  ~IndexIterator() = default;\n  /// @{\n  /// No copy or move semantics\n  IndexIterator(const IndexIterator<Dim>&) = delete;\n  IndexIterator(IndexIterator<Dim>&&) = delete;\n  IndexIterator<Dim>& operator=(const IndexIterator<Dim>&) = delete;\n  IndexIterator<Dim>& operator=(IndexIterator<Dim>&&) = delete;\n  /// @}\n  /// \\endcond\n\n  /// Returns false if the end of the Index iteration is reached\n  explicit operator bool() const { return valid_; }\n\n  /// Advance to next Index.\n  IndexIterator<Dim>& operator++();\n\n  const Index<Dim>& operator*() const { return index_; }\n  const Index<Dim>* operator->() const { return &index_; }\n\n  /// Returns an index representing the (i, j, ...)th values that the iterator\n  /// currently represents\n  const Index<Dim>& operator()() const { return index_; }\n\n  /// Get the collapsed index into a 1D array of the data corresponding to the\n  /// current Index of the IndexIterator. Note that the first dimension of the\n  /// Index varies fastest when computing the collapsed index.\n  size_t collapsed_index() const { return collapsed_index_; }\n\n private:\n  const Index<Dim> extents_{};\n  Index<Dim> index_{};\n  size_t collapsed_index_{0};\n  bool valid_{false};\n};\n"
  },
  {
    "path": "src/DataStructures/LeviCivitaIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n\n// We don't expect to use the Dim==0 case, so only instantiate Dim==1,2,3,4\ntemplate class LeviCivitaIterator<1>;\ntemplate class LeviCivitaIterator<2>;\ntemplate class LeviCivitaIterator<3>;\ntemplate class LeviCivitaIterator<4>;\n"
  },
  {
    "path": "src/DataStructures/LeviCivitaIterator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\n/*!\n * \\ingroup DataStructuresGroup\n *\n * \\brief Iterate over all nonzero index permutations for a Levi-Civita\n * symbol.\n *\n * \\details This class provides an iterator that allows you to loop over\n * only the nonzero index permutations of a Levi-Civita symbol of dimension\n * `dimension`. Inside the loop, the operator `()`\n * returns an `std::array` containing an ordered list of the indices of this\n * permutation, the operator `[]` returns a specific index from the same\n * `std::array`, and the function `sign()` returns the sign of the\n * Levi-Civita symbol for this permutation.\n *\n * Note that the Levi-Civita symbol in odd dimensions (like 3) is invariant\n * under cyclic permutations of its indices, but in even dimensions (like 4)\n * it changes sign under cyclic permutations.\n *\n * \\example\n * \\snippet Test_LeviCivitaIterator.cpp levi_civita_iterator_example\n */\ntemplate <size_t Dim>\nclass LeviCivitaIterator {\n public:\n  explicit LeviCivitaIterator() = default;\n\n  /// \\cond HIDDEN_SYMBOLS\n  ~LeviCivitaIterator() = default;\n  /// @{\n  /// No copy or move semantics\n  LeviCivitaIterator(const LeviCivitaIterator<Dim>&) = delete;\n  LeviCivitaIterator(LeviCivitaIterator<Dim>&&) = delete;\n  LeviCivitaIterator<Dim>& operator=(const LeviCivitaIterator<Dim>&) = delete;\n  LeviCivitaIterator<Dim>& operator=(LeviCivitaIterator<Dim>&&) = delete;\n  /// @}\n  /// \\endcond\n\n  // Return false if the end of the loop is reached\n  explicit operator bool() const { return valid_; }\n\n  // Increment the current permutation\n  LeviCivitaIterator& operator++() {\n    ++permutation_;\n    if (permutation_ >= signs_.size()) {\n      valid_ = false;\n    }\n    return *this;\n  }\n\n  /// Return a `std::array` containing the current multi-index, an ordered list\n  /// of indices for the current permutation.\n  const std::array<size_t, Dim> operator()() const {\n    return static_cast<std::array<size_t, Dim>>(indexes_[permutation_]);\n  }\n\n  /// Return a specific index from the multi-index of the current permutation.\n  const size_t& operator[](const size_t i) const {\n    return indexes_[permutation_][i];\n  }\n\n  /// Return the sign of the Levi-Civita symbol for the current permutation.\n  int sign() const { return signs_[permutation_]; };\n\n private:\n  static constexpr cpp20::array<cpp20::array<size_t, Dim>, factorial(Dim)>\n  indexes() {\n    cpp20::array<cpp20::array<size_t, Dim>, factorial(Dim)> indexes{};\n    cpp20::array<size_t, Dim> index{};\n    cpp2b::iota(index.begin(), index.end(), size_t(0));\n\n    indexes[0] = index;\n\n    // cpp20::next_permutation generates the different permutations of index\n    // loop over them to fill in the rest of the permutations in indexes\n    size_t permutation = 1;\n    while (cpp20::next_permutation(index.begin(), index.end())) {\n      indexes[permutation] = index;\n      ++permutation;\n    }\n    return indexes;\n  };\n\n  static constexpr cpp20::array<int, factorial(Dim)> signs() {\n    cpp20::array<int, factorial(Dim)> signs{};\n    // By construction, the sign of the first permutation is +1\n    signs[0] = 1;\n\n    // How do you know whether the corresponding Levi Civita symbol is\n    // +1 or -1? To find out, compute the number, in the factoradic number\n    // system, corresponding to each permutation, and sum the digits. If the sum\n    // is even (odd), the corresponding Levi-Civita symbol will be +1 (-1). This\n    // works because there are Dim! unique permutations of the Levi Civita\n    // symbol indices, so each one can be represented by a\n    // (`Dim`)-digit factorial-number-system number. For more on the\n    // factoradic number system, see\n    // https://en.wikipedia.org/wiki/Factorial_number_system\n    auto factoradic_counter = cpp20::array<size_t, Dim>();\n    for (size_t permutation = 1; permutation < factorial(Dim); ++permutation) {\n      for (size_t i = 0; i < Dim; ++i) {\n        factoradic_counter[i]++;\n        if (factoradic_counter[i] < i + 1) {\n          break;\n        } else {\n          factoradic_counter[i] = 0;\n        }\n      }\n      signs[permutation] =\n          (cpp2b::accumulate(factoradic_counter.begin(),\n                             factoradic_counter.end(), size_t(0)) %\n           2) == 0\n              ? 1\n              : -1;\n    }\n    return signs;\n  };\n\n  // Note: here and throughout, use cpp20::array, which is constexpr\n  // (unlike std::array), to enable constexpr signs_ and indexes_.\n  static constexpr cpp20::array<cpp20::array<size_t, Dim>, factorial(Dim)>\n      indexes_ = indexes();\n  static constexpr cpp20::array<int, factorial(Dim)> signs_{signs()};\n  size_t permutation_{0};\n  bool valid_{true};\n};\n"
  },
  {
    "path": "src/DataStructures/LinkedMessageId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\n/// \\ingroup DataStructuresGroup\n/// An identifier for an element in a sequence\n///\n/// The struct contains an ID and the ID for the previous element of\n/// the sequence, if any.  This allows the sequence to be\n/// reconstructed even when elements are received out-of-order.\n///\n/// \\see LinkedMessageQueue\ntemplate <typename Id>\nstruct LinkedMessageId {\n  Id id;\n  std::optional<Id> previous;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | id;\n    p | previous;\n  }\n};\n\ntemplate <typename Id>\nbool operator==(const LinkedMessageId<Id>& a, const LinkedMessageId<Id>& b) {\n  return a.id == b.id and a.previous == b.previous;\n}\n\ntemplate <typename Id>\nbool operator!=(const LinkedMessageId<Id>& a, const LinkedMessageId<Id>& b) {\n  return not(a == b);\n}\n\ntemplate <typename Id>\nstd::ostream& operator<<(std::ostream& s, const LinkedMessageId<Id>& id) {\n  return s << id.id << \" (\" << id.previous << \")\";\n}\n\ntemplate <typename Id>\nstruct std::hash<LinkedMessageId<Id>> {\n  size_t operator()(const LinkedMessageId<Id>& id) const {\n    // For normal use, any particular .id should only appear with one\n    // .previous, and vice versa, so hashing only one is fine.\n    return std::hash<Id>{}(id.id);\n  }\n};\n\ntemplate <typename Id>\nstruct LinkedMessageIdLessComparator {\n  using is_transparent = std::true_type;\n  bool operator()(const Id& id,\n                  const LinkedMessageId<Id>& linked_message_id) const {\n    return id < linked_message_id.id;\n  }\n  bool operator()(const LinkedMessageId<Id>& lhs,\n                  const LinkedMessageId<Id>& rhs) const {\n    return lhs.id < rhs.id or\n           (lhs.id == rhs.id and lhs.previous < rhs.previous);\n  }\n  bool operator()(const LinkedMessageId<Id>& linked_message_id,\n                  const Id& id) const {\n    return linked_message_id.id < id;\n  }\n};\n\ntemplate <typename Id>\nbool operator<(const LinkedMessageId<Id>& lhs, const LinkedMessageId<Id>& rhs) {\n  return LinkedMessageIdLessComparator<Id>{}(lhs, rhs);\n}\n"
  },
  {
    "path": "src/DataStructures/LinkedMessageQueue.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename Id, typename QueueTags>\nclass LinkedMessageQueue;\n/// \\endcond\n\n/// \\ingroup DataStructuresGroup\n/// A collection of queues of asynchronously received data\n///\n/// This class is designed to collect asynchronously received\n/// messages, possibly from multiple sources, until sufficient data\n/// has been received for processing.  Each tag in \\p QueueTags\n/// defines a queue of messages of type `Tag::type`.\ntemplate <typename Id, typename... QueueTags>\nclass LinkedMessageQueue<Id, tmpl::list<QueueTags...>> {\n  static_assert(sizeof...(QueueTags) > 0,\n                \"Must have at least one message source.\");\n\n public:\n  using IdType = Id;\n  using queue_tags_list = tmpl::list<QueueTags...>;\n\n  /// Insert data into a given queue at a given ID.  All queues must\n  /// receive data with the same collection of \\p id_and_previous, but\n  /// are not required to receive them in the same order.\n  template <typename Tag>\n  void insert(const LinkedMessageId<Id>& id_and_previous,\n              typename Tag::type message);\n\n  /// Insert multiple data at once into a given queue at a given ID.  All queues\n  /// must receive data with the same collection of \\p id_and_previous, but are\n  /// not required to receive them in the same order.\n  ///\n  /// \\details Tags are inserted in the order they are passed in. Duplicate tags\n  /// are not allowed.\n  template <typename Tag1, typename Tag2, typename... Tags>\n  void insert(const LinkedMessageId<Id>& id_and_previous,\n              typename Tag1::type message_1, typename Tag2::type message_2,\n              typename Tags::type... messages);\n\n  /// The next ID in the received sequence, if all queues have\n  /// received messages at that ID.\n  std::optional<Id> next_ready_id() const;\n\n  /// All the messages received at the time indicated by\n  /// `next_ready_id()`.  It is an error to call this function if\n  /// `next_ready_id()` would return an empty optional.  This function\n  /// removes the stored messages from the queues, advancing the\n  /// internal sequence ID.\n  tuples::TaggedTuple<QueueTags...> extract();\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | previous_id_;\n    p | messages_;\n  }\n\n private:\n  template <typename Tag>\n  struct Optional {\n    using type = std::optional<typename Tag::type>;\n  };\n\n  using OptionalTuple = tuples::TaggedTuple<Optional<QueueTags>...>;\n\n  std::optional<Id> previous_id_{};\n  std::unordered_map<std::optional<Id>, std::pair<Id, OptionalTuple>>\n      messages_{};\n};\n\ntemplate <typename Id, typename... QueueTags>\ntemplate <typename Tag>\nvoid LinkedMessageQueue<Id, tmpl::list<QueueTags...>>::insert(\n    const LinkedMessageId<Id>& id_and_previous, typename Tag::type message) {\n  static_assert((... or std::is_same_v<Tag, QueueTags>), \"Unrecognized tag.\");\n  const auto entry =\n      messages_\n          .insert({id_and_previous.previous,\n                   std::pair{id_and_previous.id, OptionalTuple{}}})\n          .first;\n  auto& [id, tuple] = entry->second;\n  ASSERT(id_and_previous.id == id, \"Received messages with different ids (\"\n                                       << id << \" and \" << id_and_previous.id\n                                       << \") but the same previous id (\"\n                                       << id_and_previous.previous << \").\");\n  ASSERT(not tuples::get<Optional<Tag>>(tuple).has_value(),\n         \"Received duplicate messages at id \"\n             << id << \" and previous id \" << id_and_previous.previous << \".\");\n  tuples::get<Optional<Tag>>(tuple) = std::move(message);\n}\n\ntemplate <typename Id, typename... QueueTags>\ntemplate <typename Tag1, typename Tag2, typename... Tags>\nvoid LinkedMessageQueue<Id, tmpl::list<QueueTags...>>::insert(\n    const LinkedMessageId<Id>& id_and_previous, typename Tag1::type message_1,\n    typename Tag2::type message_2, typename Tags::type... messages) {\n  static_assert(\n      tmpl::size<\n          tmpl::remove_duplicates<tmpl::list<Tag1, Tag2, Tags...>>>::value ==\n          sizeof...(Tags) + 2,\n      \"Must have unique tags in LinkedMessageQueue insert.\");\n  insert<Tag1>(id_and_previous, std::move(message_1));\n  insert<Tag2>(id_and_previous, std::move(message_2));\n\n  [[maybe_unused]] const auto insert_pack =\n      [this, &id_and_previous](const auto& tag_v, auto message) {\n        (void)this;\n        using tag = std::decay_t<decltype(tag_v)>;\n        insert<tag>(id_and_previous, std::move(message));\n      };\n\n  EXPAND_PACK_LEFT_TO_RIGHT(insert_pack(Tags{}, std::move(messages)));\n}\n\ntemplate <typename Id, typename... QueueTags>\nstd::optional<Id>\nLinkedMessageQueue<Id, tmpl::list<QueueTags...>>::next_ready_id() const {\n  const auto current_entry = messages_.find(previous_id_);\n  if (current_entry == messages_.end()) {\n    return {};\n  }\n  const auto& current_value = current_entry->second;\n  const auto& [current_id, current_messages] = current_value;\n  if ((... and\n       tuples::get<Optional<QueueTags>>(current_messages).has_value())) {\n    return current_id;\n  } else {\n    return {};\n  }\n}\n\ntemplate <typename Id, typename... QueueTags>\ntuples::TaggedTuple<QueueTags...>\nLinkedMessageQueue<Id, tmpl::list<QueueTags...>>::extract() {\n  ASSERT(next_ready_id().has_value(),\n         \"Cannot extract before all messages have been received.\");\n  const auto current_entry = messages_.find(previous_id_);\n  auto& current_value = current_entry->second;\n  auto& [current_id, current_messages] = current_value;\n  tuples::TaggedTuple<QueueTags...> result{};\n  (void)(..., (tuples::get<QueueTags>(result) = std::move(\n                   *tuples::get<Optional<QueueTags>>(current_messages))));\n  previous_id_ = current_id;\n  messages_.erase(current_entry);\n  return result;\n}\n"
  },
  {
    "path": "src/DataStructures/MathWrapper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup DataStructuresGroup\n/// A comma-separated list of valid template arguments to MathWrapper.\n/// Useful for explicit instantiations.\n///\n/// \\snippet Helpers/DataStructures/MathWrapperDetail.cpp MATH_WRAPPER_TYPES_instantiate\n#define MATH_WRAPPER_TYPES \\\n  double, std::complex<double>, DataVector, ComplexDataVector\n\n/// \\ingroup DataStructuresGroup\n/// Type-erased data for performing math on.\n///\n/// This class can only be instantiated with possibly const-qualified\n/// types from \\ref MATH_WRAPPER_TYPES, which can be assumed to\n/// support the mathematical operations of a linear-algebra vector.\n/// Instances of this class with those template arguments can be\n/// created using overloads of `make_math_wrapper` (passing a `const\n/// T&` for const versions and a `gsl::not_null<T*>` for mutable\n/// versions).  Other data structures (such as `Variables`) can add\n/// additional overloads implemented on top of these basic ones.\n///\n/// \\snippet Test_MathWrapper.cpp MathWrapper\ntemplate <typename T>\nclass MathWrapper {\n private:\n  using MutableT = std::remove_const_t<T>;\n\n  static_assert(\n      tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>, MutableT>);\n\n  template <typename U = T,\n            bool IsVector =\n                not(std::is_same_v<std::decay_t<T>, double> or\n                    std::is_same_v<std::decay_t<T>, std::complex<double>>),\n            bool IsConst = std::is_const_v<T>>\n  struct Impl {\n    using scalar_type = std::remove_const_t<U>;\n    T& data;\n    Impl(const gsl::not_null<T*> data_in) : data(*data_in) {}\n  };\n\n  template <typename U>\n  struct Impl<U, true, false> {\n    using scalar_type = typename U::value_type;\n    // NOLINTNEXTLINE(spectre-mutable)\n    mutable T data;\n    Impl(const gsl::not_null<T*> data_in) : data(std::move(*data_in)) {}\n  };\n\n  template <typename U>\n  struct Impl<U, true, true> {\n    using scalar_type = typename U::value_type;\n    const T data;\n    // Need to invoke the move-from-mutable constructor on DataVector, etc.\n    Impl(const gsl::not_null<MutableT*> data_in) : data(std::move(*data_in)) {}\n  };\n\n  explicit MathWrapper(const gsl::not_null<MutableT*> data) : data_(data) {}\n\n  friend MathWrapper<T> make_math_wrapper(\n      tmpl::conditional_t<std::is_const_v<T>, T&, gsl::not_null<T*>>);\n\n public:\n  /// The class's template parameter.\n  using value_type = T;\n  /// Scalar type for linear-algebra operations.  Either double or\n  /// std::complex<double>.\n  using scalar_type = typename Impl<>::scalar_type;\n\n  T& operator*() const { return data_.data; }\n\n  MathWrapper(MathWrapper&&) = default;\n\n  MathWrapper() = delete;\n  MathWrapper(const MathWrapper&) = delete;\n  MathWrapper& operator=(const MathWrapper&) = delete;\n  MathWrapper& operator=(MathWrapper&&) = delete;\n\n  /// Convert MathWrapper wrapping a mutable value to one wrapping a\n  /// const one.\n  ///\n  /// These methods will fail to compile if called on a MathWrapper\n  /// wrapping a const value.  The `to_const` method is useful because\n  /// C++ fails to resolve the implicit conversion in many cases.\n  /// @{\n  operator MathWrapper<const T>() const;\n\n  MathWrapper<const T> to_const() const {\n    return static_cast<MathWrapper<const T>>(*this);\n  }\n  /// @}\n\n private:\n  Impl<> data_;\n};\n\n/// \\ingroup DataStructuresGroup\n/// A fundamental overload of the MathWrapper construction functions.\n///\n/// Additional overloads can be implemented in terms of the\n/// fundamental overloads.\n/// @{\ninline MathWrapper<double> make_math_wrapper(\n    const gsl::not_null<double*> data) {\n  return MathWrapper<double>(data);\n}\n\ninline MathWrapper<const double> make_math_wrapper(const double& data) {\n  return MathWrapper<const double>(const_cast<double*>(&data));\n}\n\ninline MathWrapper<std::complex<double>> make_math_wrapper(\n    const gsl::not_null<std::complex<double>*> data) {\n  return MathWrapper<std::complex<double>>(data);\n}\n\ninline MathWrapper<const std::complex<double>> make_math_wrapper(\n    const std::complex<double>& data) {\n  return MathWrapper<const std::complex<double>>(\n      const_cast<std::complex<double>*>(&data));\n}\n\ninline MathWrapper<DataVector> make_math_wrapper(\n    const gsl::not_null<DataVector*> data) {\n  DataVector referencing(data->data(), data->size());\n  return MathWrapper<DataVector>(&referencing);\n}\n\ninline MathWrapper<const DataVector> make_math_wrapper(const DataVector& data) {\n  DataVector referencing(const_cast<double*>(data.data()), data.size());\n  return MathWrapper<const DataVector>(&referencing);\n}\n\ninline MathWrapper<ComplexDataVector> make_math_wrapper(\n    const gsl::not_null<ComplexDataVector*> data) {\n  ComplexDataVector referencing(data->data(), data->size());\n  return MathWrapper<ComplexDataVector>(&referencing);\n}\n\ninline MathWrapper<const ComplexDataVector> make_math_wrapper(\n    const ComplexDataVector& data) {\n  ComplexDataVector referencing(const_cast<std::complex<double>*>(data.data()),\n                                data.size());\n  return MathWrapper<const ComplexDataVector>(&referencing);\n}\n/// @}\n\ntemplate <typename T>\nMathWrapper<T>::operator MathWrapper<const T>() const {\n  return make_math_wrapper(data_.data);\n}\n\ntemplate <typename T, size_t N>\nauto make_math_wrapper(const gsl::not_null<std::array<T, N>*> array) {\n  DataVector referencing(array->data(), array->size());\n  return make_math_wrapper(&referencing);\n}\n\ntemplate <typename T, size_t N>\nauto make_math_wrapper(const std::array<T, N>& array) {\n  const DataVector referencing(const_cast<double*>(array.data()), array.size());\n  return make_math_wrapper(referencing);\n}\n\n/// \\ingroup DataStructuresGroup\n/// The `value_type` for a MathWrapper wrapping `T`.\ntemplate <typename T>\nusing math_wrapper_type = typename decltype(make_math_wrapper(\n    std::declval<tmpl::conditional_t<std::is_const_v<T>, const T&,\n                                     gsl::not_null<T*>>>()))::value_type;\n\n/// \\ingroup DataStructuresGroup\n/// A fundamental overload for owning type-erasure.  Returns its argument\n/// unchanged.\n///\n/// Additional overloads should always return the `math_wrapper_type`\n/// of their argument, and should be implemented to avoid allocations\n/// and copying whenever possible.\n/// @{\ninline double into_math_wrapper_type(double&& data) { return data; }\n\ninline std::complex<double> into_math_wrapper_type(\n    std::complex<double>&& data) {\n  return data;\n}\n\ninline DataVector into_math_wrapper_type(DataVector&& data) {\n  return std::move(data);\n}\n\ninline ComplexDataVector into_math_wrapper_type(ComplexDataVector&& data) {\n  return std::move(data);\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Matrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class Matrix.\n\n#pragma once\n\n#include \"DataStructures/DynamicMatrix.hpp\"\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A dynamically sized matrix of `double`s with column-major storage.\n *\n * \\note This is a thin wrapper around `blaze::DynamicMatrix`. Please refer to\n * [Blaze documentation](https://bitbucket.org/blaze-lib/blaze/wiki/Matrices)\n * for information on how to use it.\n */\nclass Matrix : public blaze::DynamicMatrix<double, blaze::columnMajor> {\n public:\n  // Inherit constructors\n  using blaze::DynamicMatrix<double, blaze::columnMajor>::DynamicMatrix;\n};\n"
  },
  {
    "path": "src/DataStructures/ModalVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ModalVector;\n/// \\endcond\n\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(ModalVector);\n}  // namespace blaze\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A class for storing spectral coefficients on a spectral grid.\n *\n * \\details A ModalVector holds an array of spectral coefficients, and can be\n * either owning (the array is deleted when the ModalVector goes out of scope)\n * or non-owning, meaning it just has a pointer to an array.\n *\n * Only basic mathematical operations are supported with\n * ModalVectors. ModalVectors may be added or subtracted, and the following\n * unary operations are supported:\n * - abs\n *\n * Also multiplication is supported with doubles and `ModalVector`s, and\n * `ModalVector`s can be divided by doubles. Multiplication is supported with\n * `DiagonalModalOperator`s and `ModalVector`s.\n */\nclass ModalVector : public VectorImpl<double, ModalVector> {\n public:\n  ModalVector() = default;\n  ModalVector(const ModalVector&) = default;\n  ModalVector(ModalVector&&) = default;\n  ModalVector& operator=(const ModalVector&) = default;\n  ModalVector& operator=(ModalVector&&) = default;\n  ~ModalVector() = default;\n\n  using BaseType = VectorImpl<double, ModalVector>;\n\n  using BaseType::operator=;\n  using BaseType::VectorImpl;\n};\n\nnamespace blaze {\ntemplate <>\nstruct TransposeFlag<ModalVector> : BoolConstant<ModalVector::transpose_flag> {\n};\ntemplate <>\nstruct AddTrait<ModalVector, ModalVector> {\n  using Type = ModalVector;\n};\ntemplate <>\nstruct SubTrait<ModalVector, ModalVector> {\n  using Type = ModalVector;\n};\nBLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(ModalVector, double, MultTrait,\n                                               ModalVector);\ntemplate <>\nstruct DivTrait<ModalVector, double> {\n  using Type = ModalVector;\n};\n\ntemplate <typename Operator>\nstruct MapTrait<ModalVector, Operator> {\n  // Selectively allow unary operations for spectral coefficients\n  static_assert(\n      tmpl::list_contains_v<\n          tmpl::list<blaze::Abs, blaze::Bind1st<blaze::Add, double>,\n                     blaze::Bind2nd<blaze::Add, double>,\n                     blaze::Bind1st<blaze::Sub, double>,\n                     blaze::Bind2nd<blaze::Sub, double>,\n                     blaze::Bind1st<blaze::Add, std::complex<double>>,\n                     blaze::Bind2nd<blaze::Add, std::complex<double>>,\n                     blaze::Bind1st<blaze::Sub, std::complex<double>>,\n                     blaze::Bind2nd<blaze::Sub, std::complex<double>>>,\n          Operator>,\n      \"The only unary operation permitted on a ModalVector is:\"\n      \" abs.\");\n  using Type = ModalVector;\n};\n\ntemplate <typename Operator>\nstruct MapTrait<ModalVector, ModalVector, Operator> {\n  // Forbid math operations in this specialization of BinaryMap traits for\n  // ModalVector that are unlikely to be used on spectral coefficients.\n  // Currently no non-arithmetic binary operations are supported.\n  static_assert(tmpl::list_contains_v<tmpl::list<>, Operator>,\n                \"This binary operation is not permitted on a ModalVector.\");\n  using Type = ModalVector;\n};\n}  // namespace blaze\n\n/// \\cond\nDEFINE_STD_ARRAY_BINOP(ModalVector, ModalVector, ModalVector, operator+,\n                       std::plus<>())\nDEFINE_STD_ARRAY_BINOP(ModalVector, ModalVector, ModalVector, operator-,\n                       std::minus<>())\nDEFINE_STD_ARRAY_INPLACE_BINOP(ModalVector, ModalVector, operator+=,\n                               std::plus<>())\nDEFINE_STD_ARRAY_INPLACE_BINOP(ModalVector, ModalVector, operator-=,\n                               std::minus<>())\n/// \\endcond\nMAKE_WITH_VALUE_IMPL_DEFINITION_FOR(ModalVector)\n"
  },
  {
    "path": "src/DataStructures/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Python/DataVector.hpp\"\n#include \"DataStructures/Python/Matrix.hpp\"\n#include \"DataStructures/Python/ModalVector.hpp\"\n#include \"PythonBindings/BoundChecks.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_index_impl(py::module& m) {  // NOLINT\n  py::class_<Index<Dim>>(m, (\"Index\" + std::to_string(Dim) + \"D\").c_str())\n      .def(py::init<const size_t>(), py::arg(\"i0\"))\n      .def(py::init<std::array<size_t, Dim>>(), py::arg(\"i\"))\n      .def(\n          \"__iter__\",\n          [](const Index<Dim>& t) {\n            return py::make_iterator(t.begin(), t.end());\n          },\n          // Keep object alive while iterator exists\n          py::keep_alive<0, 1>())\n      // __len__ is for being able to write len(my_data_vector) in python\n      .def(\"__len__\", [](const Index<Dim>& t) { return t.size(); })\n      // __getitem__ and __setitem__ are the subscript operators (operator[]).\n      // To define (and overload) operator() use __call__\n      .def(\"__getitem__\",\n           [](const Index<Dim>& t, const size_t i) {\n             py_bindings::bounds_check(t, i);\n             return t[i];\n           })\n      .def(\"__setitem__\",\n           [](Index<Dim>& t, const size_t i, const double v) {\n             py_bindings::bounds_check(t, i);\n             t[i] = v;\n           })\n      // Need __str__ for converting to string/printing\n      .def(\n          \"__str__\", [](const Index<Dim>& t) { return get_output(t); })\n      // repr allows you to output the object in an interactive python terminal\n      // using obj to get the \"string REPResenting the object\".\n      .def(\n          \"__repr__\", [](const Index<Dim>& t) { return get_output(t); })\n      .def(\"product\", &Index<Dim>::template product<Dim>,\n           \"The product of the extents.\")\n      .def(\n          \"slice_away\",\n          [](const Index<Dim>& t, const size_t d) {\n            if constexpr (Dim == 0) {\n              (void)d;\n              (void)t;\n              return Index<0>{};\n            } else {\n              return t.slice_away(d);\n            }\n          },\n          py::arg(\"d\"), \"Slice away the specified dimension.\")\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self)\n      .def(py::pickle(\n          [](const Index<Dim>& index) {\n            return py::make_tuple(index.indices());\n          },\n          [](const py::tuple& state) {\n            if (state.size() != 1) {\n              throw std::runtime_error(\"Invalid state for Index!\");\n            }\n            return Index<Dim>(state[0].cast<std::array<size_t, Dim>>());\n          }));\n  m.def(\"collapsed_index\", &collapsed_index<Dim>, py::arg(\"index\"),\n        py::arg(\"extents\"));\n}\n\ntemplate <size_t Dim>\nvoid bind_impl(py::module& m) {  // NOLINT\n  bind_index_impl<Dim>(m);\n}\n}  // namespace\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py_bindings::bind_datavector(m);\n  py_bindings::bind_matrix(m);\n  py_bindings::bind_modalvector(m);\n  bind_impl<0>(m);\n  bind_impl<1>(m);\n  bind_impl<2>(m);\n  bind_impl<3>(m);\n}\n"
  },
  {
    "path": "src/DataStructures/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyDataStructures\")\n\nspectre_python_add_module(\n  DataStructures\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  DataVector.cpp\n  Matrix.cpp\n  ModalVector.cpp\n  PYTHON_FILES\n  __init__.py\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  DataVector.hpp\n  Matrix.hpp\n  ModalVector.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  pybind11::module\n  PythonBindings\n  Utilities\n  )\n"
  },
  {
    "path": "src/DataStructures/Python/DataVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Python/DataVector.hpp\"\n\n#include <memory>\n#include <pybind11/numpy.h>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"PythonBindings/BoundChecks.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_datavector(py::module& m) {\n  // Wrapper for basic DataVector operations\n  py::class_<DataVector>(m, \"DataVector\", py::buffer_protocol())\n      .def(py::init<size_t>(), py::arg(\"size\"))\n      .def(py::init<size_t, double>(), py::arg(\"size\"), py::arg(\"fill\"))\n      .def(py::init([](const std::vector<double>& values) {\n             DataVector result{values.size()};\n             std::copy(values.begin(), values.end(), result.begin());\n             return result;\n           }),\n           py::arg(\"values\"))\n      .def(py::init([](const py::buffer& buffer, const bool copy) {\n             py::buffer_info info = buffer.request();\n             // Sanity-check the buffer\n             if (info.format != py::format_descriptor<double>::format()) {\n               throw std::runtime_error(\n                   \"Incompatible format: expected a double array.\");\n             }\n             if (info.ndim != 1) {\n               throw std::runtime_error(\"Incompatible dimension.\");\n             }\n             const auto size = static_cast<size_t>(info.shape[0]);\n             auto data = static_cast<double*>(info.ptr);\n             if (copy) {\n               DataVector result{size};\n               std::copy_n(data, result.size(), result.begin());\n               return result;\n             } else {\n               // Create a non-owning DataVector from the buffer\n               return DataVector{data, size};\n             }\n           }),\n           py::arg(\"buffer\"), py::arg(\"copy\") = true)\n      // Expose the data as a Python buffer so it can be cast into Numpy arrays\n      .def_buffer([](DataVector& data_vector) {\n        return py::buffer_info(data_vector.data(),\n                               // Size of one scalar\n                               sizeof(double),\n                               py::format_descriptor<double>::format(),\n                               // Number of dimensions\n                               1,\n                               // Size of the buffer\n                               {data_vector.size()},\n                               // Stride for each index (in bytes)\n                               {sizeof(double)});\n      })\n      .def(\n          \"__iter__\",\n          [](const DataVector& t) {\n            return py::make_iterator(t.begin(), t.end());\n          },\n          // Keep object alive while iterator exists\n          py::keep_alive<0, 1>())\n      // __len__ is for being able to write len(my_data_vector) in python\n      .def(\"__len__\", [](const DataVector& t) { return t.size(); })\n      // __getitem__ and __setitem__ are the subscript operators (operator[]).\n      // To define (and overload) operator() use __call__\n      .def(\"__getitem__\",\n           +[](const DataVector& t, const size_t i) {\n             bounds_check(t, i);\n             return t[i];\n           })\n      .def(\"__setitem__\",\n           +[](DataVector& t, const size_t i, const double v) {\n             bounds_check(t, i);\n             t[i] = v;\n           })\n      // Need __str__ for converting to string/printing\n      .def(\"__str__\", +[](const DataVector& t) { return get_output(t); })\n      // repr allows you to output the object in an interactive python terminal\n      // using obj to get the \"string REPResenting the object\".\n      .def(\"__repr__\", +[](const DataVector& t) { return get_output(t); })\n      .def(py::self += py::self)\n      // Need to do math explicitly converting to DataVector because we don't\n      // want to represent all the possible expression template types\n      .def(\"abs\", +[](const DataVector& t) { return DataVector{abs(t)}; })\n      .def(\"acos\", +[](const DataVector& t) { return DataVector{acos(t)}; })\n      .def(\"acosh\", +[](const DataVector& t) { return DataVector{acosh(t)}; })\n      .def(\"asin\", +[](const DataVector& t) { return DataVector{asin(t)}; })\n      .def(\"asinh\", +[](const DataVector& t) { return DataVector{asinh(t)}; })\n      .def(\"atan\", +[](const DataVector& t) { return DataVector{atan(t)}; })\n      .def(\"atan2\",\n           +[](const DataVector& y, const DataVector& x) {\n             return DataVector{atan2(y, x)};\n           })\n      .def(\"atanh\", +[](const DataVector& t) { return DataVector{atanh(t)}; })\n      .def(\"cbrt\", +[](const DataVector& t) { return DataVector{cbrt(t)}; })\n      .def(\"cos\", +[](const DataVector& t) { return DataVector{cos(t)}; })\n      .def(\"cosh\", +[](const DataVector& t) { return DataVector{cosh(t)}; })\n      .def(\"erf\", +[](const DataVector& t) { return DataVector{erf(t)}; })\n      .def(\"erfc\", +[](const DataVector& t) { return DataVector{erfc(t)}; })\n      .def(\"exp\", +[](const DataVector& t) { return DataVector{exp(t)}; })\n      .def(\"exp2\", +[](const DataVector& t) { return DataVector{exp2(t)}; })\n      .def(\"exp10\", +[](const DataVector& t) { return DataVector{exp10(t)}; })\n      .def(\"fabs\", +[](const DataVector& t) { return DataVector{fabs(t)}; })\n      .def(\"hypot\",\n           +[](const DataVector& x, const DataVector& y) {\n             return DataVector{hypot(x, y)};\n           })\n      .def(\"invcbrt\",\n           +[](const DataVector& t) { return DataVector{invcbrt(t)}; })\n      .def(\"invsqrt\",\n           +[](const DataVector& t) { return DataVector{invsqrt(t)}; })\n      .def(\"log\", +[](const DataVector& t) { return DataVector{log(t)}; })\n      .def(\"log2\", +[](const DataVector& t) { return DataVector{log2(t)}; })\n      .def(\"log10\", +[](const DataVector& t) { return DataVector{log10(t)}; })\n      .def(\"max\", +[](const DataVector& t) { return double{max(t)}; })\n      .def(\"min\", +[](const DataVector& t) { return double{min(t)}; })\n      .def(\"pow\", +[](const DataVector& base,\n                      double exp) { return DataVector{pow(base, exp)}; })\n      .def(\"sin\", +[](const DataVector& t) { return DataVector{sin(t)}; })\n      .def(\"sinh\", +[](const DataVector& t) { return DataVector{sinh(t)}; })\n      .def(\"sqrt\", +[](const DataVector& t) { return DataVector{sqrt(t)}; })\n      .def(\"step_function\",\n           +[](const DataVector& t) { return DataVector{step_function(t)}; })\n      .def(\"tan\", +[](const DataVector& t) { return DataVector{tan(t)}; })\n      .def(\"tanh\", +[](const DataVector& t) { return DataVector{tanh(t)}; })\n      .def(\"__pow__\",\n           +[](const DataVector& base, const double exp) {\n             return DataVector{pow(base, exp)};\n           })\n      .def(\"__add__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{self + other};\n           })\n      .def(\"__radd__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{other + self};\n           })\n      .def(\"__sub__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{self - other};\n           })\n      .def(\"__rsub__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{other - self};\n           })\n      .def(\"__mul__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{self * other};\n           })\n      .def(\"__rmul__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{other * self};\n           })\n      .def(\"__truediv__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{self / other};\n           })\n      .def(\"__rdiv__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{other / self};\n           })\n      .def(\"__rtruediv__\",\n           +[](const DataVector& self, const double other) {\n             return DataVector{other / self};\n           })\n\n      // DataVector-DataVector math\n      .def(\"__add__\",\n           +[](const DataVector& self, const DataVector& other) {\n             return DataVector{self + other};\n           })\n      .def(\"__sub__\",\n           +[](const DataVector& self, const DataVector& other) {\n             return DataVector{self - other};\n           })\n      .def(\"__mul__\",\n           +[](const DataVector& self, const DataVector& other) {\n             return DataVector{self * other};\n           })\n      .def(\"__div__\",\n           +[](const DataVector& self, const DataVector& other) {\n             return DataVector{self / other};\n           })\n      .def(\"__truediv__\",\n           +[](const DataVector& self, const DataVector& other) {\n             return DataVector{self / other};\n           })\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self)\n      .def(\"__neg__\", +[](const DataVector& t) { return DataVector{-t}; });\n  py::implicitly_convertible<py::array, DataVector>();\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Python/DataVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_datavector(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Python/Matrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Python/Matrix.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <sstream>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"PythonBindings/BoundChecks.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\n\nvoid bind_matrix(py::module& m) {\n  // Wrapper for basic Matrix operations\n  py::class_<Matrix>(m, \"Matrix\", py::buffer_protocol())\n      .def(py::init<size_t, size_t>(), py::arg(\"rows\"), py::arg(\"columns\"))\n      .def(py::init([](const py::buffer& buffer) {\n             py::buffer_info info = buffer.request();\n             // Sanity-check the buffer\n             if (info.format != py::format_descriptor<double>::format()) {\n               throw std::runtime_error(\n                   \"Incompatible format: expected a double array.\");\n             }\n             if (info.ndim != 2) {\n               throw std::runtime_error(\"Incompatible dimension.\");\n             }\n             const auto rows = static_cast<size_t>(info.shape[0]);\n             const auto columns = static_cast<size_t>(info.shape[1]);\n             auto data = static_cast<double*>(info.ptr);\n             return Matrix(rows, columns, data);\n           }),\n           py::arg(\"buffer\"))\n      // Expose the data as a Python buffer so it can be cast into Numpy arrays\n      .def_buffer([](Matrix& matrix) {\n        return py::buffer_info(\n            matrix.data(),\n            // Size of one scalar\n            sizeof(double), py::format_descriptor<double>::format(),\n            // Number of dimensions\n            2,\n            // Size of the buffer\n            {matrix.rows(), matrix.columns()},\n            // Stride for each index (in bytes). Data is stored\n            // in column-major layout (see `Matrix.hpp`).\n            {sizeof(double), sizeof(double) * matrix.spacing()});\n      })\n      .def_property_readonly(\n          \"shape\",\n          +[](const Matrix& self) {\n            return std::tuple<size_t, size_t>(self.rows(), self.columns());\n          })\n      // __getitem__ and __setitem__ are the subscript operators (M[*,*]).\n      .def(\n          \"__getitem__\",\n          +[](const Matrix& self, const std::tuple<size_t, size_t>& x) {\n            matrix_bounds_check(self, std::get<0>(x), std::get<1>(x));\n            return self(std::get<0>(x), std::get<1>(x));\n          })\n      // Need __str__ for converting to string/printing\n      .def(\n          \"__str__\",\n          +[](const Matrix& self) { return std::string(MakeString{} << self); })\n      .def(\n          \"__setitem__\", +[](Matrix& self, const std::tuple<size_t, size_t>& x,\n                             const double val) {\n            matrix_bounds_check(self, std::get<0>(x), std::get<1>(x));\n            self(std::get<0>(x), std::get<1>(x)) = val;\n          });\n}\n\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Python/Matrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_matrix(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Python/ModalVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Python/ModalVector.hpp\"\n\n#include <memory>\n#include <pybind11/numpy.h>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/ModalVector.hpp\"\n#include \"PythonBindings/BoundChecks.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_modalvector(py::module& m) {\n  // Wrapper for basic ModalVector operations\n  py::class_<ModalVector>(m, \"ModalVector\", py::buffer_protocol())\n      .def(py::init<size_t>(), py::arg(\"size\"))\n      .def(py::init<size_t, double>(), py::arg(\"size\"), py::arg(\"fill\"))\n      .def(py::init([](const std::vector<double>& values) {\n             ModalVector result{values.size()};\n             std::copy(values.begin(), values.end(), result.begin());\n             return result;\n           }),\n           py::arg(\"values\"))\n      .def(py::init([](const py::buffer& buffer, const bool copy) {\n             py::buffer_info info = buffer.request();\n             // Sanity-check the buffer\n             if (info.format != py::format_descriptor<double>::format()) {\n               throw std::runtime_error(\n                   \"Incompatible format: expected a double array.\");\n             }\n             if (info.ndim != 1) {\n               throw std::runtime_error(\"Incompatible dimension.\");\n             }\n             const auto size = static_cast<size_t>(info.shape[0]);\n             auto data = static_cast<double*>(info.ptr);\n             if (copy) {\n               ModalVector result{size};\n               std::copy_n(data, result.size(), result.begin());\n               return result;\n             } else {\n               // Create a non-owning ModalVector from the buffer\n               return ModalVector{data, size};\n             }\n           }),\n           py::arg(\"buffer\"), py::arg(\"copy\") = true)\n      // Expose the data as a Python buffer so it can be cast into Numpy arrays\n      .def_buffer([](ModalVector& data_vector) {\n        return py::buffer_info(data_vector.data(),\n                               // Size of one scalar\n                               sizeof(double),\n                               py::format_descriptor<double>::format(),\n                               // Number of dimensions\n                               1,\n                               // Size of the buffer\n                               {data_vector.size()},\n                               // Stride for each index (in bytes)\n                               {sizeof(double)});\n      })\n      .def(\n          \"__iter__\",\n          [](const ModalVector& t) {\n            return py::make_iterator(t.begin(), t.end());\n          },\n          // Keep object alive while iterator exists\n          py::keep_alive<0, 1>())\n      // __len__ is for being able to write len(my_data_vector) in python\n      .def(\"__len__\", [](const ModalVector& t) { return t.size(); })\n      // __getitem__ and __setitem__ are the subscript operators (operator[]).\n      // To define (and overload) operator() use __call__\n      .def(\n          \"__getitem__\",\n          +[](const ModalVector& t, const size_t i) {\n            bounds_check(t, i);\n            return t[i];\n          })\n      .def(\n          \"__setitem__\",\n          +[](ModalVector& t, const size_t i, const double v) {\n            bounds_check(t, i);\n            t[i] = v;\n          })\n      // Need __str__ for converting to string/printing\n      .def(\n          \"__str__\", +[](const ModalVector& t) { return get_output(t); })\n      // repr allows you to output the object in an interactive python terminal\n      // using obj to get the \"string REPResenting the object\".\n      .def(\n          \"__repr__\", +[](const ModalVector& t) { return get_output(t); })\n      .def(py::self += py::self)\n      // Need to do math explicitly converting to ModalVector because we don't\n      // want to represent all the possible expression template types\n      .def(\n          \"abs\", +[](const ModalVector& t) { return ModalVector{abs(t)}; })\n      .def(\n          \"__mul__\",\n          +[](const ModalVector& self, const double other) {\n            return ModalVector{self * other};\n          })\n      .def(\n          \"__rmul__\",\n          +[](const ModalVector& self, const double other) {\n            return ModalVector{other * self};\n          })\n      .def(\n          \"__div__\",\n          +[](const ModalVector& self, const double other) {\n            return ModalVector{self / other};\n          })\n      .def(\n          \"__truediv__\",\n          +[](const ModalVector& self, const double other) {\n            return ModalVector{self / other};\n          })\n\n      // ModalVector-ModalVector math\n      .def(\n          \"__add__\",\n          +[](const ModalVector& self, const ModalVector& other) {\n            return ModalVector{self + other};\n          })\n      .def(\n          \"__sub__\",\n          +[](const ModalVector& self, const ModalVector& other) {\n            return ModalVector{self - other};\n          })\n\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self)\n      .def(\n          \"__neg__\", +[](const ModalVector& t) { return ModalVector{-t}; });\n  py::implicitly_convertible<py::array, ModalVector>();\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Python/ModalVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_modalvector(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n\nIndex = {0: Index0D, 1: Index1D, 2: Index2D, 3: Index3D}\n"
  },
  {
    "path": "src/DataStructures/SimpleSparseMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <numeric>\n#include <vector>\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\ndouble SimpleSparseMatrix::operator()(const size_t row_dest_index,\n                                      const size_t column_src_index) const {\n  bool found_dest = false;\n  const auto dest_it =\n      alg::find_if(row_dest_indices_,\n                   [&found_dest, row_dest_index](const size_t dest_index) {\n                     found_dest = row_dest_index == dest_index;\n                     return found_dest or row_dest_index < dest_index;\n                   });\n  if (not found_dest) {\n    return 0.0;\n  }\n  const auto dest_index =\n      static_cast<size_t>(std::distance(row_dest_indices_.begin(), dest_it));\n\n  const size_t src_index = std::accumulate(\n      num_columns_per_row_.begin(),\n      num_columns_per_row_.begin() + (dest_it - row_dest_indices_.begin()),\n      0_st);\n\n  bool found_src = false;\n  const auto src_it = std::find_if(\n      column_src_indices_.begin() +\n          static_cast<std::vector<size_t>::difference_type>(src_index),\n      column_src_indices_.begin() +\n          static_cast<std::vector<size_t>::difference_type>(\n              src_index + num_columns_per_row_[dest_index]),\n      [&found_src, column_src_index](const size_t val) {\n        found_src = column_src_index == val;\n        return found_src or column_src_index < val;\n      });\n  if (not found_src) {\n    return 0.0;\n  }\n  return matrix_elements_[static_cast<size_t>(src_it -\n                                              column_src_indices_.begin())];\n}\n\nvoid SimpleSparseMatrix::fill(const std::vector<SparseMatrixElement>& data) {\n  if (data.empty()) {\n    // Nothing to do.\n    return;\n  }\n\n  // Figure out number of rows with nonzero matrix elements.  Note that data has\n  // already been sorted by row.\n  size_t num_used_rows = 1;\n  for (size_t i = 1; i < data.size(); ++i) {\n    if (data[i].row_dest != data[i - 1].row_dest) {\n      ++num_used_rows;\n    }\n  }\n\n  num_columns_per_row_.resize(num_used_rows, 0);\n  row_dest_indices_.resize(num_used_rows);\n  matrix_elements_.resize(data.size());\n  column_src_indices_.resize(data.size());\n\n  // Current_row_index is the index of the current row in row_dest_indices\n  // and num_columns_per_row.\n  // Current_row_index goes from zero to num_used_rows - 1\n  size_t current_row_index = 0;\n  // current_row is the actual row number in the full matrix.\n  size_t current_row = data[0].row_dest;\n  row_dest_indices_[0] = current_row;\n  for (const auto& d : data) {\n    if (d.row_dest != current_row) {\n      ++current_row_index;\n      current_row = d.row_dest;\n      row_dest_indices_[current_row_index] = current_row;\n    }\n    ++num_columns_per_row_[current_row_index];\n  }\n  for (size_t i = 0; i < data.size(); ++i) {\n    matrix_elements_[i] = data[i].value;\n    column_src_indices_[i] = data[i].column_src;\n  }\n}\n\ntemplate <typename T>\nvoid SimpleSparseMatrix::increment_multiply_on_right(\n    const gsl::not_null<T*> a, const size_t a_offset, const size_t a_stride,\n    const T& b, const size_t b_offset, const size_t b_stride) const {\n  if (matrix_elements_.empty()) {\n    // Nothing to do\n    return;\n  }\n\n  ASSERT(matrix_elements_.size() == column_src_indices_.size(),\n         \"Size mismatch between the size of matrix_elements_: \"\n             << matrix_elements_.size()\n             << \" and column_src_indices_: \" << column_src_indices_.size());\n  ASSERT(num_columns_per_row_.size() == row_dest_indices_.size(),\n         \"Size mismatch between the size of num_columns_per_row_: \"\n             << num_columns_per_row_.size()\n             << \" and row_dest_indices_: \" << row_dest_indices_.size());\n\n  // This particular implementation (with raw pointers and pointer\n  // arithmetic) was found in SpEC to be fastest, back when we\n  // tested several variations of this implementation with SpEC.\n  const double* mat = matrix_elements_.data();\n  const size_t* src_ind = column_src_indices_.data();\n  const size_t* num_inner = num_columns_per_row_.data();\n  const size_t* dest_ind = row_dest_indices_.data();\n\n  // num_comps_a is the number of components of 'a' that we will touch.\n  // (the matrix is sparse, so we don't touch all components of 'a')\n  size_t num_comps_a = num_columns_per_row_.size();\n  while (num_comps_a-- > 0) {\n    // num_comps_b is the number of components of 'b' that we will touch,\n    // for this component of 'a'.\n    // (the matrix is sparse, so we don't touch all components of 'b')\n    size_t num_comps_b = *num_inner;\n    std::advance(num_inner, 1);\n    double sum = 0.0;\n    while (num_comps_b-- > 0) {\n      sum += *mat * b[b_offset + (*src_ind) * b_stride];\n      std::advance(mat, 1);\n      std::advance(src_ind, 1);\n    }\n    (*a)[a_offset + (*dest_ind) * a_stride] += sum;\n    std::advance(dest_ind, 1);\n  }\n}\n\n// Explicit instantiations\ntemplate void SimpleSparseMatrix::increment_multiply_on_right(\n    const gsl::not_null<std::vector<double>*> a, const size_t a_offset,\n    const size_t a_stride, const std::vector<double>& b, const size_t b_offset,\n    const size_t b_stride) const;\ntemplate void SimpleSparseMatrix::increment_multiply_on_right(\n    const gsl::not_null<gsl::span<double>*> a, const size_t a_offset,\n    const size_t a_stride, const gsl::span<double>& b, const size_t b_offset,\n    const size_t b_stride) const;\n"
  },
  {
    "path": "src/DataStructures/SimpleSparseMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n#include \"Utilities/Gsl.hpp\"\n\n/// Struct used to fill SimpleSparsematrix\nstruct SparseMatrixElement {\n  SparseMatrixElement(const size_t row, const size_t column, const double val)\n      : row_dest(row), column_src(column), value(val) {}\n  size_t row_dest, column_src;\n  double value;\n};\n\n/*!\n * \\brief A simple fast sparse matrix.\n *\n * Supports only a limited number of operations to keep it simple:\n * filling the matrix, accessing elements, and multiplication by\n * a vector on the right.\n *\n * We use SimpleSparseMatrix because filling a Blaze sparse matrix is\n * enormously slow. For example, here are timings for creating a\n * rank-one TensorYlmFilter sparse matrix using SimpleSparseMatrix\n * (column labeled \"this\") and using blaze::CompressedMatrix (column\n * labeled \"Blaze\").  The column \"Blaze-wo-blaze\" is the same as the\n * \"Blaze\" column (which includes timing of all the TensorYlmFilter\n * code that doesn't explicitly depend on blaze) minus the timings of\n * the blaze calls that 1. resize the matrix, 2. reserve elements, and\n * 3. fill the blaze matrix.  The timings for those individual blaze\n * calls are in subsequent columns.  Note that \"Blaze-wo-blaze\" is\n * roughly equivalent to \"this\" in speed.  Also note that the \"this\"\n * timings were done in SpEC, which uses SimpleSparseMatrix [which was\n * ported from SpEC to SpECTRE and simplified here].\n *\n * l_max this Blaze Blaze-wo-blaze blaze.resize blaze.reserve blaze.fill\n * ----------------------------------------------------------------------------\n *  8    0ms     5ms     1ms              1ms           2ms          0ms\n * 18    1ms   101ms     2ms             39ms          51ms          8ms\n * 28    3ms   479ms     3ms            181ms         246ms         48ms\n * 38    4ms  1555ms     4ms            571ms         822ms        157ms\n * 48    5ms  3668ms     6ms           1395ms        1873ms        393ms\n * 58    6ms  7604ms     6ms           2908ms        3855ms        832ms\n * 68    7ms 14176ms     8ms           5416ms        7214ms       1536ms\n * 78    8ms 24234ms     8ms           9288ms       12304ms       2632ms\n *\n */\nclass SimpleSparseMatrix {\n public:\n  SimpleSparseMatrix() = default;\n\n  /// Fills with data.\n  /// Normally called only by SparseMatrixFiller.\n  /// ASSUMES that data has already been sorted by row, then column.\n  void fill(const std::vector<SparseMatrixElement>& data);\n\n  /// Does a += m*b where m is the sparse matrix.\n  /// T is a container of doubles, like a std::vector<double>.\n  /// Assumes b points into contiguous storage that allows one\n  /// to access b[i] for b_offset <= i <= b_offset+max(column_indices).\n  /// Assumes a points into contiguous storage that allows one\n  /// to access a[i] for a_offset <= a <= a_offset+max(row_indices).\n  ///\n  /// Both source and destination are allowed to have a stride.\n  template <typename T>\n  void increment_multiply_on_right(gsl::not_null<T*> a, size_t a_offset,\n                                   size_t a_stride, const T& b, size_t b_offset,\n                                   size_t b_stride) const;\n\n  /// Number of nonzero elements.\n  size_t size() const { return matrix_elements_.size(); }\n\n  /// Obtains element at (destindex,srcindex).\n  /// Slow, should not use very often in critical situations.\n  double operator()(size_t row_dest_index, size_t column_src_index) const;\n\n private:\n  // Holds all nonzero matrix elements.\n  std::vector<double> matrix_elements_;\n  // Holds indices of all rows of the matrix that have nonzero matrix elements.\n  std::vector<size_t> row_dest_indices_;\n  // Holds the number of columns in every row that has nonzero matrix elements.\n  // row_dest_indices_ and num_columns_per_row_ have the same size (which is the\n  // number of rows with nonzero matrix elements).\n  // The sum of all the num_columns_per_row_ is the size of matrix_elements_.\n  std::vector<size_t> num_columns_per_row_;\n  // Holds the indices of all columns that have nonzero matrix elements.\n  // column_indices_ has the same size as matrix_elements_, so many\n  // of the column_src_indices_ might be repeated.\n  std::vector<size_t> column_src_indices_;\n};\n"
  },
  {
    "path": "src/DataStructures/SliceIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/SliceIterator.hpp\"\n\n#include <functional>\n#include <numeric>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\ntemplate <size_t Dim>\nSliceIterator::SliceIterator(const Index<Dim>& extents, const size_t fixed_dim,\n                             const size_t fixed_index)\n    : size_(extents.product()),\n      stride_(std::accumulate(extents.begin(), extents.begin() + fixed_dim,\n                              1_st, std::multiplies<size_t>())),\n      stride_count_(0),\n      jump_((extents[fixed_dim] - 1) * stride_),\n      initial_offset_(fixed_index * stride_),\n      volume_offset_(initial_offset_),\n      slice_offset_(0) {}\n\nSliceIterator& SliceIterator::operator++() {\n  ++volume_offset_;\n  ++slice_offset_;\n  ++stride_count_;\n  if (stride_count_ == stride_) {\n    volume_offset_ += jump_;\n    stride_count_ = 0;\n  }\n  return *this;\n}\n\nvoid SliceIterator::reset() {\n  volume_offset_ = initial_offset_;\n  slice_offset_ = 0;\n  stride_count_ = 0;\n}\n\ntemplate <size_t VolumeDim>\n// NOLINTNEXTLINE(modernize-avoid-c-arrays)\nstd::pair<std::unique_ptr<std::pair<size_t, size_t>[]>,\n          std::array<std::pair<gsl::span<std::pair<size_t, size_t>>,\n                               gsl::span<std::pair<size_t, size_t>>>,\n                     VolumeDim>>\nvolume_and_slice_indices(const Index<VolumeDim>& extents) {\n  // array over dim, pair over lower/upper, span<pair> over volume/boundary\n  std::array<std::pair<gsl::span<std::pair<size_t, size_t>>,\n                       gsl::span<std::pair<size_t, size_t>>>,\n             VolumeDim>\n      volume_and_slice_indices{};\n\n  const size_t half_number_boundary_points =\n      alg::accumulate(alg::iota(std::array<size_t, VolumeDim>{{}}, 0_st), 0_st,\n                      [&extents](const size_t state, const size_t d) {\n                        return state + extents.slice_away(d).product();\n                      });\n  ASSERT(half_number_boundary_points != 0,\n         \"If you encounter this assert you've found a bug in the \"\n         \"'volume_and_slice_indices' function. Please file an issue describing \"\n         \"the necessary steps to reproduce this error. Thank you!\");\n  auto indices_buffer =\n      // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n      cpp20::make_unique_for_overwrite<std::pair<size_t, size_t>[]>(\n          half_number_boundary_points * 2);\n  size_t alloc_offset = 0;\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const auto boundary_extents = extents.slice_away(d);\n    const auto num_points = boundary_extents.product();\n    gsl::at(volume_and_slice_indices, d).first =\n        gsl::make_span(indices_buffer.get() + alloc_offset, num_points);\n    alloc_offset += num_points;\n    gsl::at(volume_and_slice_indices, d).second =\n        gsl::make_span(indices_buffer.get() + alloc_offset, num_points);\n    alloc_offset += num_points;\n\n    size_t index = 0;\n    // si_lower is a macro in some standard libs, so name sli_\n    for (SliceIterator sli_lower(extents, d, 0),\n         sli_upper(extents, d, extents[d] - 1);\n         sli_lower and sli_upper;\n         (void)++sli_lower, (void)++sli_upper, (void)++index) {\n      gsl::at(gsl::at(volume_and_slice_indices, d).first, index).first =\n          sli_lower.volume_offset();\n      gsl::at(gsl::at(volume_and_slice_indices, d).first, index).second =\n          sli_lower.slice_offset();\n\n      gsl::at(gsl::at(volume_and_slice_indices, d).second, index).first =\n          sli_upper.volume_offset();\n      gsl::at(gsl::at(volume_and_slice_indices, d).second, index).second =\n          sli_upper.slice_offset();\n    }\n  }\n\n  return {std::move(indices_buffer), std::move(volume_and_slice_indices)};\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                                 \\\n  template SliceIterator::SliceIterator(const Index<DIM(data)>&, const size_t, \\\n                                        const size_t);                         \\\n  template std::pair<                                                          \\\n      std::unique_ptr<std::pair<size_t, size_t>[]>,                            \\\n      std::array<std::pair<gsl::span<std::pair<size_t, size_t>>,               \\\n                           gsl::span<std::pair<size_t, size_t>>>,              \\\n                 DIM(data)>>                                                   \\\n  volume_and_slice_indices<DIM(data)>(const Index<DIM(data)>& extents);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/DataStructures/SliceIterator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t>\nclass Index;\n/// \\endcond\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Iterate over a (dim-1)-dimensional slice\n */\nclass SliceIterator {\n public:\n  /*!\n   * @param extents the number of grid points in each dimension\n   * @param fixed_dim the dimension to slice in\n   * @param fixed_index the index of the `fixed_dim` to slice at\n   */\n  template <size_t Dim>\n  SliceIterator(const Index<Dim>& extents, size_t fixed_dim,\n                size_t fixed_index);\n\n  /// Returns `true` if the iterator is valid\n  explicit operator bool() const { return volume_offset_ < size_; }\n\n  /// Step to the next grid point\n  SliceIterator& operator++();\n\n  /// Offset into a Dim-dimensional DataVector at the current gridpoint.\n  /// Note that the size of the DataVector is assumed to be the product of the\n  /// extents used to construct this SliceIterator\n  size_t volume_offset() const { return volume_offset_; }\n\n  /// Offset into a (Dim-1)-dimensional DataVector at the current gridpoint.\n  /// Note that the size of the DataVector is assumed to be the product of the\n  /// extents used to construct this SliceIterator divided by the extent in\n  /// the fixed_dim used to construct this SliceIterator\n  size_t slice_offset() const { return slice_offset_; }\n\n  /// Reset the iterator\n  void reset();\n\n private:\n  size_t size_ = std::numeric_limits<size_t>::max();\n  size_t stride_ = std::numeric_limits<size_t>::max();\n  size_t stride_count_ = std::numeric_limits<size_t>::max();\n  size_t jump_ = std::numeric_limits<size_t>::max();\n  size_t initial_offset_ = std::numeric_limits<size_t>::max();\n  size_t volume_offset_ = std::numeric_limits<size_t>::max();\n  size_t slice_offset_ = std::numeric_limits<size_t>::max();\n};\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Get the mapping between volume and boundary slice indices\n *\n * SliceIterator is used to map between the index of a point on a slice in the\n * volume data and the index in the corresponding sliced data. Repeatedly\n * applying the SliceIterator on various components of a tensor becomes very\n * expensive and so precomputing the index map is sometimes advantageous. This\n * function computes the index map onto all boundary slices of volume mesh with\n * extents `extents`.\n *\n * The `unique_ptr` is where the volume and slice indices are stored in memory,\n * the array holds views into the memory buffer. The index of the array is the\n * fixed dimension, the outer `pair` holds the indices for the lower and upper\n * side,  respectively, while the `pair`s in the `span`s hold the volume and\n * slice indices, respectively.\n */\ntemplate <size_t VolumeDim>\nauto volume_and_slice_indices(const Index<VolumeDim>& extents)\n    -> std::pair<std::unique_ptr<std::pair<size_t, size_t>[]>,\n                 std::array<std::pair<gsl::span<std::pair<size_t, size_t>>,\n                                      gsl::span<std::pair<size_t, size_t>>>,\n                            VolumeDim>>;\n"
  },
  {
    "path": "src/DataStructures/SliceTensorToVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/range/combine.hpp>\n#include <boost/tuple/tuple.hpp>\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// @{\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Slices volume `Tensor`s into a `Variables`\n *\n * The slice has a constant logical coordinate in direction `sliced_dim`,\n * slicing the volume at `fixed_index` in that dimension.  For\n * example, to get the lower boundary of `sliced_dim`, pass `0` for\n * `fixed_index`; to get the upper boundary, pass\n * `extents[sliced_dim] - 1`.\n */\ntemplate <typename... TagsToSlice, size_t VolumeDim>\nvoid data_on_slice(\n    const gsl::not_null<Variables<tmpl::list<TagsToSlice...>>*> interface_vars,\n    const Index<VolumeDim>& element_extents, const size_t sliced_dim,\n    const size_t fixed_index, const typename TagsToSlice::type&... tensors) {\n  const size_t interface_grid_points =\n      element_extents.slice_away(sliced_dim).product();\n  if (interface_vars->number_of_grid_points() != interface_grid_points) {\n    *interface_vars =\n        Variables<tmpl::list<TagsToSlice...>>(interface_grid_points);\n  }\n  for (SliceIterator si(element_extents, sliced_dim, fixed_index); si; ++si) {\n    const auto lambda = [&si](auto& interface_tensor,\n                              const auto& volume_tensor) {\n      for (decltype(auto) interface_and_volume_tensor_components :\n           boost::combine(interface_tensor, volume_tensor)) {\n        boost::get<0>(\n            interface_and_volume_tensor_components)[si.slice_offset()] =\n            boost::get<1>(\n                interface_and_volume_tensor_components)[si.volume_offset()];\n      }\n      return '0';\n    };\n    expand_pack(lambda(get<TagsToSlice>(*interface_vars), tensors)...);\n  }\n}\n\ntemplate <typename... TagsToSlice, size_t VolumeDim>\nVariables<tmpl::list<TagsToSlice...>> data_on_slice(\n    const Index<VolumeDim>& element_extents, const size_t sliced_dim,\n    const size_t fixed_index, const typename TagsToSlice::type&... tensors) {\n  Variables<tmpl::list<TagsToSlice...>> interface_vars(\n      element_extents.slice_away(sliced_dim).product());\n  data_on_slice<TagsToSlice...>(make_not_null(&interface_vars), element_extents,\n                                sliced_dim, fixed_index, tensors...);\n  return interface_vars;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/SliceVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <ostream>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// @{\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Slices the data within `vars` to a codimension 1 slice. The\n * slice has a constant logical coordinate in direction `sliced_dim`,\n * slicing the volume at `fixed_index` in that dimension.  For\n * example, to get the lower boundary of `sliced_dim`, pass `0` for\n * `fixed_index`; to get the upper boundary, pass\n * `extents[sliced_dim] - 1`.\n *\n * \\see add_slice_to_data\n *\n * Returns Variables class sliced to a hypersurface.\n */\ntemplate <std::size_t VolumeDim, typename TagsList>\nvoid data_on_slice(const gsl::not_null<Variables<TagsList>*> interface_vars,\n                   const Variables<TagsList>& vars,\n                   const Index<VolumeDim>& element_extents,\n                   const size_t sliced_dim, const size_t fixed_index) {\n  const size_t interface_grid_points =\n      element_extents.slice_away(sliced_dim).product();\n  const size_t volume_grid_points = vars.number_of_grid_points();\n  constexpr const size_t number_of_independent_components =\n      Variables<TagsList>::number_of_independent_components;\n\n  if (interface_vars->number_of_grid_points() != interface_grid_points) {\n    *interface_vars = Variables<TagsList>(interface_grid_points);\n  }\n  using value_type = typename Variables<TagsList>::value_type;\n  const value_type* vars_data = vars.data();\n  value_type* interface_vars_data = interface_vars->data();\n  for (SliceIterator si(element_extents, sliced_dim, fixed_index); si; ++si) {\n    for (size_t i = 0; i < number_of_independent_components; ++i) {\n      // clang-tidy: do not use pointer arithmetic\n      interface_vars_data[si.slice_offset() +                      // NOLINT\n                          i * interface_grid_points] =             // NOLINT\n          vars_data[si.volume_offset() + i * volume_grid_points];  // NOLINT\n    }\n  }\n}\n\ntemplate <std::size_t VolumeDim, typename TagsList>\nVariables<TagsList> data_on_slice(const Variables<TagsList>& vars,\n                                  const Index<VolumeDim>& element_extents,\n                                  const size_t sliced_dim,\n                                  const size_t fixed_index) {\n  Variables<TagsList> interface_vars(\n      element_extents.slice_away(sliced_dim).product());\n  data_on_slice(make_not_null(&interface_vars), vars, element_extents,\n                sliced_dim, fixed_index);\n  return interface_vars;\n}\n/// @}\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Adds data on a codimension 1 slice to a volume quantity. The\n * slice has a constant logical coordinate in direction `sliced_dim`,\n * slicing the volume at `fixed_index` in that dimension.  For\n * example, to add to the lower boundary of `sliced_dim`, pass `0` for\n * `fixed_index`; to add to the upper boundary, pass\n * `extents[sliced_dim] - 1`.\n *\n * \\see data_on_slice\n */\ntemplate <std::size_t VolumeDim, typename... VolumeTags, typename... SliceTags>\nvoid add_slice_to_data(\n    const gsl::not_null<Variables<tmpl::list<VolumeTags...>>*> volume_vars,\n    const Variables<tmpl::list<SliceTags...>>& vars_on_slice,\n    const Index<VolumeDim>& extents, const size_t sliced_dim,\n    const size_t fixed_index) {\n  static_assert((std::is_same_v<db::remove_all_prefixes<VolumeTags>,\n                                db::remove_all_prefixes<SliceTags>> and\n                 ...));\n  static_assert(\n      (std::is_same_v<typename VolumeTags::type, typename SliceTags::type> and\n       ...),\n      \"Tensor types do not match.\");\n  constexpr const size_t number_of_independent_components =\n      Variables<tmpl::list<VolumeTags...>>::number_of_independent_components;\n  const size_t volume_grid_points = extents.product();\n  const size_t slice_grid_points = extents.slice_away(sliced_dim).product();\n  ASSERT(volume_vars->number_of_grid_points() == volume_grid_points,\n         \"volume_vars has wrong number of grid points.  Expected \"\n             << volume_grid_points << \", got \"\n             << volume_vars->number_of_grid_points());\n  ASSERT(vars_on_slice.number_of_grid_points() == slice_grid_points,\n         \"vars_on_slice has wrong number of grid points.  Expected \"\n             << slice_grid_points << \", got \"\n             << vars_on_slice.number_of_grid_points());\n  using value_type = typename Variables<tmpl::list<VolumeTags...>>::value_type;\n  value_type* const volume_data = volume_vars->data();\n  const value_type* const slice_data = vars_on_slice.data();\n  for (SliceIterator si(extents, sliced_dim, fixed_index); si; ++si) {\n    for (size_t i = 0; i < number_of_independent_components; ++i) {\n      // clang-tidy: do not use pointer arithmetic\n      volume_data[si.volume_offset() + i * volume_grid_points] +=  // NOLINT\n          slice_data[si.slice_offset() + i * slice_grid_points];   // NOLINT\n    }\n  }\n}\n"
  },
  {
    "path": "src/DataStructures/SparseMatrixFiller.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/SparseMatrixFiller.hpp\"\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSparseMatrixFiller::SparseMatrixFiller(const size_t num_cols,\n                                       const bool use_map_method,\n                                       const double scale)\n    : num_rows_(num_cols),\n      num_cols_(num_cols),\n      use_map_method_(use_map_method),\n      scale_(scale) {\n  if (not use_map_method) {\n    matrix_elements_.assign(square(num_cols), 0.0);\n  }\n}\n\nvoid SparseMatrixFiller::add(const double element, const size_t dest_index,\n                             const size_t src_index) {\n  if (not use_map_method_) {\n    matrix_elements_[src_index + num_cols_ * dest_index] += element;\n  } else {\n    const std::pair index{dest_index, src_index};\n    if (const auto iter = element_index_.find(index);\n        iter != element_index_.end()) {\n      matrix_elements_[iter->second] += element;\n    } else {\n      element_index_.insert({index, matrix_elements_.size()});\n      matrix_elements_.push_back(element);\n      dest_indices_.push_back(dest_index);\n      src_indices_.push_back(src_index);\n    }\n  }\n}\n\nvoid SparseMatrixFiller::fill_sparse_matrix_elements(\n    const gsl::not_null<std::vector<SparseMatrixElement>*> data) const {\n  // First fill data for sorting.\n  if (not use_map_method_) {\n    const auto num_rows =\n        static_cast<size_t>(sqrt(double(matrix_elements_.size())));\n    ASSERT(num_rows * num_rows == matrix_elements_.size(),\n           \"Size should be a perfect square, not \" << matrix_elements_.size());\n    // For this method, many elements are zero so filter them out here.\n    const auto num_zeros =\n        std::count(matrix_elements_.begin(), matrix_elements_.end(), 0.0);\n    data->reserve(matrix_elements_.size() - static_cast<size_t>(num_zeros));\n    size_t indx = 0;\n    for (size_t i_dest = 0; i_dest < num_rows; ++i_dest) {\n      for (size_t j_src = 0; j_src < num_rows; ++j_src, ++indx) {\n        const double element = matrix_elements_[indx];\n        if (element != 0.0) {\n          data->emplace_back(i_dest, j_src, element);\n        }\n      }\n    }\n  } else {\n    ASSERT(matrix_elements_.size() == dest_indices_.size(),\n           \"Size mismatch value \" << matrix_elements_.size() << \" vs dest \"\n                                  << dest_indices_.size());\n    ASSERT(matrix_elements_.size() == src_indices_.size(),\n           \"Size mismatch value \" << matrix_elements_.size() << \" vs src \"\n                                  << src_indices_.size());\n    data->reserve(matrix_elements_.size());\n    for (size_t i = 0; i < dest_indices_.size(); ++i) {\n      data->emplace_back(dest_indices_[i], src_indices_[i],\n                         matrix_elements_[i]);\n    }\n  }\n\n  // Remove all the zero or nearly-zero elements.\n  // Removes elements that are <10*epsilon*scale_.\n  data->erase(std::remove_if(\n                  data->begin(), data->end(),\n                  [this](const SparseMatrixElement& x) {\n                    return std::abs(x.value) <\n                           10 * scale_ * std::numeric_limits<double>::epsilon();\n                  }),\n              data->end());\n\n  // Now sort the data by row and column, so we can fill in required order.\n  std::sort(data->begin(), data->end(),\n            [](const SparseMatrixElement& a, const SparseMatrixElement& b) {\n              return a.row_dest == b.row_dest ? a.column_src < b.column_src\n                                              : a.row_dest < b.row_dest;\n            });\n}\n\nvoid SparseMatrixFiller::fill(\n    const gsl::not_null<blaze::CompressedMatrix<double, blaze::rowMajor>*>\n        matrix) const {\n  // fill the elements and sort them.\n  std::vector<SparseMatrixElement> data;\n  fill_sparse_matrix_elements(make_not_null(&data));\n\n  // Fill the matrix.\n  // Do this by reserving a size, and appending elements row by row\n  // (which must be done in order or else blaze dox say undefined\n  // behavior), which is the reason for the above sort.\n  // Note that this filling order assumes a rowMajor matrix; for columnMajor\n  // matrices (not treated here) we must fill columns in order.\n  // We need to explicitly call finalize on every row, *even empty ones*.\n  matrix->resize(num_rows_, num_cols_, false);\n  matrix->reserve(data.size());\n  size_t current_row = 0;\n  for (const auto& d : data) {\n    while (d.row_dest != current_row) {\n      matrix->finalize(current_row++);\n    }\n    matrix->append(d.row_dest, d.column_src, d.value);\n  }\n  // After the last data point, we need to finalize the last row(s).\n  while (current_row < num_rows_) {\n    matrix->finalize(current_row++);\n  }\n}\n\nvoid SparseMatrixFiller::fill(\n    const gsl::not_null<SimpleSparseMatrix*> matrix) const {\n  // fill the elements and sort them.\n  std::vector<SparseMatrixElement> data;\n  fill_sparse_matrix_elements(make_not_null(&data));\n  // Now fill.\n  matrix->fill(data);\n}\n"
  },
  {
    "path": "src/DataStructures/SparseMatrixFiller.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <blaze/math/CompressedMatrix.h>\n#include <cstddef>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace sparse_matrix_filler_detail {\n/// Hash on a pair of size_ts\nstruct PairHasher {\n  size_t operator()(const std::pair<size_t, size_t>& key) const {\n    ASSERT(key.first < 4294967295, \"Key.first too large: \" << key.first);\n    ASSERT(key.second < 4294967295, \"Key.second too large: \" << key.second);\n    return key.first bitor (key.second << 32);\n  }\n};\n}  // namespace sparse_matrix_filler_detail\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A data structure used to fill sparse matrices.\n *\n * We normally use blaze::CompressedMatrix to hold sparse matrices and to\n * operate on them.\n *\n * Unfortunately to fill a blaze::CompressedMatrix efficiently you\n * need to fill nonzero matrix elements in a well-defined order\n * (i.e. fill all nonzero elements in the top row in the order of\n * nonzero columns, and repeat for each other row, in order) using\n * CompressedMatrix's reserve(), append(), and finalize() functions.\n *\n * However, we are interested in a use case where:\n *\n *   1. We are filling the matrix elements using a complicated\n *      algorithm that computes (as opposed to systematically loops\n *      over) the matrix element indices to be filled, so we are\n *      effectively filling matrix elements in a random order.\n *\n *   2. The algorithm can traverse the same matrix element hundreds\n *      of times, meaning that the first time an element is traversed\n *      it should be filled, but subsequent times it is traversed it\n *      should be incremented.\n *\n * SparseMatrixFiller provides a function to fill matrix elements, keeping\n * in mind the above two issues.  And it provides a function to copy its\n * sparse matrix into blaze::CompressedMatrix efficiently.\n *\n * We consider two ways to keep track of unique elements:\n *\n * 1. Store matrix elements in a full vector that is indexed by\n *    matrix_elements[src_index+max_src_index*dest_index].  Set this\n *    vector to zero initially, and increment it each time 'add' is called.\n *    This is simple, but has a major issue: memory.\n *    In SpEC, I (Mark Scheel) have measured that computing TensorYlm\n *    filtering matrices using this method, after l_max=40 , the\n *    slope of CPU-h vs l_max for this method has a kink, and CPU-h\n *    increases like l_max^2 instead of l_max.  Presumably this is\n *    because of cache misses when accessing the very large\n *    matrix_elements vector at random locations.\n *\n * 2. Have a std::unordered_map<std::pair<size_t,size_t>, size_t>>\n *    called `element_index` that keeps track of the unique elements.\n *    Also have three vectors `dest_indices`, `src_indices`,\n *    and `matrix_elements` that keep the nonzero elements.\n *    element_index is indexed by\n *    element_index[{dest_index,src_index}] = index-into-matrix-elements\n *    So each time we add a new element, we check element_index to see\n *    if that element has not already been added, and if not, we push_back\n *    the three vectors.  If it has already been added, we simply do\n *    matrix_elements[index-into-matrix-elements] += new_element.\n *\n * I (Mark Scheel) have found in SpEC for computing TensorYlm filtering\n * matrices that the CPU cost of algorithm 2. above scales linearly\n * with l_max at least up to l_max=140, but for l_max < 40 or so,\n * algorithm 2. is about a factor of 3 slower than algorithm 1.\n * So SpEC uses both algorithms, depending on the value of l_max.\n *\n * For SpECTRE, we plan to do the same: for small l_max we will use\n * algorithm 1 and for large l_max we will use algorithm 2.  The\n * cutoff for l_max will depend on other factors (like the rank of the\n * Tensor being filtered) and will be determined by whoever calls\n * SparseMatrixFiller.\n */\nclass SparseMatrixFiller {\n public:\n  /// Assume num_rows and num_cols are equal.\n  /// If use_map_method is true, uses algorithm 2.\n  /// Scale is the overall scale of the matrix; scale is used to remove\n  /// elements that are smaller than roundoff.\n  SparseMatrixFiller(size_t num_cols, bool use_map_method, double scale);\n\n  /// Add (or increment) an element such that for v = M x, where M is\n  /// the matrix and v and x are vectors, we compute the contribution\n  /// to v[dest_index] from x[src_index].\n  void add(double element, size_t dest_index, size_t src_index);\n\n  /// Fills the matrix.\n  /// Note that the order in which blaze matrices must be filled depends\n  /// on whether the matrix is rowMajor or columnMajor, so changing\n  /// the template parameter here will fail without changing the code.\n  void fill(gsl::not_null<blaze::CompressedMatrix<double, blaze::rowMajor>*>\n                matrix) const;\n\n  /// Fills a SimpleSparseMatrix.\n  void fill(gsl::not_null<SimpleSparseMatrix*> matrix) const;\n\n private:\n  void fill_sparse_matrix_elements(\n      gsl::not_null<std::vector<SparseMatrixElement>*> data) const;\n  size_t num_rows_{0}, num_cols_{0};\n  bool use_map_method_{true};\n  double scale_{1.0};\n  std::vector<double> matrix_elements_{};\n  std::unordered_map<std::pair<size_t, size_t>, size_t,\n                     sparse_matrix_filler_detail::PairHasher>\n      element_index_{};\n  std::vector<size_t> dest_indices_{};\n  std::vector<size_t> src_indices_{};\n};\n"
  },
  {
    "path": "src/DataStructures/SpinWeighted.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details\n\n#pragma once\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// @{\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Make a spin-weighted type `T` with spin-weight `Spin`. Mathematical\n * operators are restricted to addition, subtraction, multiplication and\n * division, with spin-weights checked for validity.\n *\n * \\details For a spin-weighted object, we limit operations to those valid for a\n * pair of spin-weighted quantities - i.e. addition only makes sense when the\n * two summands possess the same spin weight, and multiplication (or division)\n * result in a summed (or subtracted) spin weight.\n */\ntemplate <typename T, int Spin, bool is_vector = is_derived_of_vector_impl_v<T>>\nstruct SpinWeighted;\n\ntemplate <typename T, int Spin>\nstruct SpinWeighted<T, Spin, false> {\n  using value_type = T;\n  constexpr static int spin = Spin;\n\n  SpinWeighted() = default;\n  SpinWeighted(const SpinWeighted&) = default;\n  SpinWeighted(SpinWeighted&&) = default;\n  SpinWeighted& operator=(const SpinWeighted&) = default;\n  SpinWeighted& operator=(SpinWeighted&&) = default;\n  ~SpinWeighted() = default;\n\n  // clang-tidy asks that these be marked explicit, but we actually do not want\n  // them explicit for use in the math operations below.\n  template <typename Rhs>\n  SpinWeighted(const SpinWeighted<Rhs, Spin>& rhs)  // NOLINT\n      : data_{rhs.data()} {}\n\n  template <typename Rhs>\n  SpinWeighted(SpinWeighted<Rhs, Spin>&& rhs)  // NOLINT\n      : data_{std::move(rhs.data())} {}\n\n  SpinWeighted(const T& rhs) : data_{rhs} {}        // NOLINT\n  SpinWeighted(T&& rhs) : data_{std::move(rhs)} {}  // NOLINT\n  explicit SpinWeighted(const size_t size) : data_{size} {}\n  template <typename U>\n  SpinWeighted(const size_t size, const U& val) : data_{size, val} {}\n\n  template <typename Rhs>\n  SpinWeighted& operator=(const SpinWeighted<Rhs, Spin>& rhs) {\n    data_ = rhs.data();\n    return *this;\n  }\n\n  template <typename Rhs>\n  SpinWeighted& operator=(SpinWeighted<Rhs, Spin>&& rhs) {\n    data_ = std::move(rhs.data());\n    return *this;\n  }\n\n  SpinWeighted& operator=(const T& rhs) {\n    data_ = rhs;\n    return *this;\n  }\n  SpinWeighted& operator=(T&& rhs) {\n    data_ = std::move(rhs);\n    return *this;\n  }\n\n  template <typename Rhs>\n  auto& operator+=(const SpinWeighted<Rhs, Spin>& rhs) {\n    data_ += rhs.data();\n    return *this;\n  }\n\n  auto& operator+=(const T& rhs) {\n    data_ += rhs;\n    return *this;\n  }\n\n  template <typename Rhs>\n  auto& operator-=(const SpinWeighted<Rhs, Spin>& rhs) {\n    data_ -= rhs.data();\n    return *this;\n  }\n\n  auto& operator-=(const T& rhs) {\n    data_ -= rhs;\n    return *this;\n  }\n\n  T& data() { return data_; }\n  const T& data() const { return data_; }\n\n  size_t size() const { return data_.size(); }\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  T data_;\n};\n\ntemplate <typename T, int Spin>\nstruct SpinWeighted<T, Spin, true> {\n  using value_type = T;\n  constexpr static int spin = Spin;\n\n  void set_data_ref(const gsl::not_null<T*> rhs) { data_.set_data_ref(rhs); }\n\n  // needed for invoking the check in `Variables.hpp` that ensures that\n  // default-constructed `Variables` are never used.\n  void set_data_ref(const std::nullptr_t null, const size_t size) {\n    data_.set_data_ref(null, size);\n  }\n\n  void set_data_ref(const gsl::not_null<SpinWeighted<T, spin>*> rhs) {\n    data_.set_data_ref(make_not_null(&(rhs->data_)));\n  }\n\n  template <typename ValueType>\n  void set_data_ref(ValueType* const start, const size_t set_size) {\n    data_.set_data_ref(start, set_size);\n  }\n\n  void destructive_resize(const size_t new_size) {\n    data_.destructive_resize(new_size);\n  }\n\n  SpinWeighted() = default;\n  SpinWeighted(const SpinWeighted&) = default;\n  SpinWeighted(SpinWeighted&&) = default;\n  SpinWeighted& operator=(const SpinWeighted&) = default;\n  SpinWeighted& operator=(SpinWeighted&&) = default;\n  ~SpinWeighted() = default;\n\n  // clang-tidy asks that these be marked explicit, but we actually do not want\n  // them explicit for use in the math operations below.\n  template <typename Rhs>\n  SpinWeighted(const SpinWeighted<Rhs, Spin>& rhs)  // NOLINT\n      : data_{rhs.data()} {}\n\n  template <typename Rhs>\n  SpinWeighted(SpinWeighted<Rhs, Spin>&& rhs)  // NOLINT\n      : data_{std::move(rhs.data())} {}\n\n  SpinWeighted(const T& rhs) : data_{rhs} {}        // NOLINT\n  SpinWeighted(T&& rhs) : data_{std::move(rhs)} {}  // NOLINT\n  explicit SpinWeighted(const size_t size) : data_{size} {}\n  template <typename U>\n  SpinWeighted(const size_t size, const U& val) : data_{size, val} {}\n\n  template <typename Rhs>\n  SpinWeighted& operator=(const SpinWeighted<Rhs, Spin>& rhs) {\n    data_ = rhs.data();\n    return *this;\n  }\n\n  template <typename Rhs>\n  SpinWeighted& operator=(SpinWeighted<Rhs, Spin>&& rhs) {\n    data_ = std::move(rhs.data());\n    return *this;\n  }\n\n  SpinWeighted& operator=(const T& rhs) {\n    data_ = rhs;\n    return *this;\n  }\n  SpinWeighted& operator=(T&& rhs) {\n    data_ = std::move(rhs);\n    return *this;\n  }\n\n  template <typename Rhs>\n  auto& operator+=(const SpinWeighted<Rhs, Spin>& rhs) {\n    data_ += rhs.data();\n    return *this;\n  }\n\n  auto& operator+=(const T& rhs) {\n    data_ += rhs;\n    return *this;\n  }\n\n  template <typename Rhs>\n  auto& operator-=(const SpinWeighted<Rhs, Spin>& rhs) {\n    data_ -= rhs.data();\n    return *this;\n  }\n\n  auto& operator-=(const T& rhs) {\n    data_ -= rhs;\n    return *this;\n  }\n\n  T& data() { return data_; }\n  const T& data() const { return data_; }\n\n  size_t size() const { return data_.size(); }\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  T data_;\n};\n/// @}\n\ntemplate <typename T, int Spin>\nvoid SpinWeighted<T, Spin, true>::pup(PUP::er& p) {\n  p | data_;\n}\n\ntemplate <typename T, int Spin>\nvoid SpinWeighted<T, Spin, false>::pup(PUP::er& p) {\n  p | data_;\n}\n\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\ingroup DataStructuresGroup\n/// This is a `std::true_type` if the provided type is a `SpinWeighted` of any\n/// type and spin, otherwise is a `std::false_type`.\ntemplate <typename T>\nstruct is_any_spin_weighted : std::false_type {};\n\ntemplate <typename T, int S>\nstruct is_any_spin_weighted<SpinWeighted<T, S>> : std::true_type {};\n/// @}\n\ntemplate <typename T>\nconstexpr bool is_any_spin_weighted_v = is_any_spin_weighted<T>::value;\n\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\ingroup DataStructuresGroup\n/// This is a `std::true_type` if the provided type `T` is a `SpinWeighted` of\n/// `InternalType` and any spin, otherwise is a `std::false_type`.\ntemplate <typename InternalType, typename T>\nstruct is_spin_weighted_of : std::false_type {};\n\ntemplate <typename InternalType, int S>\nstruct is_spin_weighted_of<InternalType, SpinWeighted<InternalType, S>>\n    : std::true_type {};\n/// @}\n\ntemplate <typename InternalType, typename T>\nconstexpr bool is_spin_weighted_of_v =\n    is_spin_weighted_of<InternalType, T>::value;\n\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\ingroup DataStructuresGroup\n/// This is a `std::true_type` if the provided type `T1` is a `SpinWeighted` and\n/// `T2` is a `SpinWeighted`, and both have the same internal type, but any\n/// combination of spin weights.\ntemplate <typename T1, typename T2>\nstruct is_spin_weighted_of_same_type : std::false_type {};\n\ntemplate <typename T, int Spin1, int Spin2>\nstruct is_spin_weighted_of_same_type<SpinWeighted<T, Spin1>,\n                                     SpinWeighted<T, Spin2>> : std::true_type {\n};\n/// @}\n\ntemplate <typename T1, typename T2>\nconstexpr bool is_spin_weighted_of_same_type_v =\n    is_spin_weighted_of_same_type<T1, T2>::value;\n\n/// @{\n/// \\brief Add two spin-weighted quantities if the types are compatible and\n/// spins are the same. Un-weighted quantities are assumed to be spin 0.\n// These overloads are designed to allow SpinWeighted to wrap Blaze expression\n// templates to ensure efficient math operations, necessitating the\n// `decltype(declval<T>() ...` syntax\ntemplate <typename T1, typename T2, int Spin>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T1>() + std::declval<T2>()), Spin>\n    operator+(const SpinWeighted<T1, Spin>& lhs,\n              const SpinWeighted<T2, Spin>& rhs) {\n  return {lhs.data() + rhs.data()};\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T>() + std::declval<T>()), 0>\n    operator+(const SpinWeighted<T, 0>& lhs, const T& rhs) {\n  return {lhs.data() + rhs};\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<T>() + std::declval<get_vector_element_type_t<T>>()),\n    0>\noperator+(const SpinWeighted<T, 0>& lhs,\n          const get_vector_element_type_t<T>& rhs) {\n  return {lhs.data() + rhs};\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T>() + std::declval<T>()), 0>\n    operator+(const T& lhs, const SpinWeighted<T, 0>& rhs) {\n  return {lhs + rhs.data()};\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<get_vector_element_type_t<T>>() + std::declval<T>()),\n    0>\noperator+(const get_vector_element_type_t<T>& lhs,\n          const SpinWeighted<T, 0>& rhs) {\n  return {lhs + rhs.data()};\n}\n/// @}\n\n/// @{\n/// \\brief Subtract two spin-weighted quantities if the types are compatible and\n/// spins are the same. Un-weighted quantities are assumed to be spin 0.\n// These overloads are designed to allow SpinWeighted to wrap Blaze expression\n// templates to ensure efficient math operations, necessitating the\n// `decltype(declval<T>() ...` syntax\ntemplate <typename T1, typename T2, int Spin>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T1>() - std::declval<T2>()), Spin>\n    operator-(const SpinWeighted<T1, Spin>& lhs,\n              const SpinWeighted<T2, Spin>& rhs) {\n  return {lhs.data() - rhs.data()};\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T>() - std::declval<T>()), 0>\n    operator-(const SpinWeighted<T, 0>& lhs, const T& rhs) {\n  return {lhs.data() - rhs};\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<T>() - std::declval<get_vector_element_type_t<T>>()),\n    0>\noperator-(const SpinWeighted<T, 0>& lhs,\n          const get_vector_element_type_t<T>& rhs) {\n  return {lhs.data() - rhs};\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T>() - std::declval<T>()), 0>\n    operator-(const T& lhs, const SpinWeighted<T, 0>& rhs) {\n  return {lhs - rhs.data()};\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<get_vector_element_type_t<T>>() - std::declval<T>()),\n    0>\noperator-(const get_vector_element_type_t<T>& lhs,\n          const SpinWeighted<T, 0>& rhs) {\n  return {lhs - rhs.data()};\n}\n/// @}\n\n/// Negation operator preserves spin\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE SpinWeighted<decltype(-std::declval<T>()), Spin>\noperator-(const SpinWeighted<T, Spin>& operand) {\n  return {-operand.data()};\n}\n\n/// Unary `+` operator preserves spin\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE SpinWeighted<decltype(+std::declval<T>()), Spin>\noperator+(const SpinWeighted<T, Spin>& operand) {\n  return {+operand.data()};\n}\n\n/// @{\n/// \\brief Multiply two spin-weighted quantities if the types are compatible and\n/// add the spins. Un-weighted quantities are assumed to be spin 0.\n// These overloads are designed to allow SpinWeighted to wrap Blaze expression\n// templates to ensure efficient math operations, necessitating the\n// `decltype(declval<T>() ...` syntax\ntemplate <typename T1, typename T2, int Spin1, int Spin2>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<T1>() * std::declval<T2>()), Spin1 + Spin2>\noperator*(const SpinWeighted<T1, Spin1>& lhs,\n          const SpinWeighted<T2, Spin2>& rhs) {\n  return {lhs.data() * rhs.data()};\n}\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T>() * std::declval<T>()), Spin>\n    operator*(const SpinWeighted<T, Spin>& lhs, const T& rhs) {\n  return {lhs.data() * rhs};\n}\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<T>() * std::declval<get_vector_element_type_t<T>>()),\n    Spin>\noperator*(const SpinWeighted<T, Spin>& lhs,\n          const get_vector_element_type_t<T>& rhs) {\n  return {lhs.data() * rhs};\n}\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T>() * std::declval<T>()), Spin>\n    operator*(const T& lhs, const SpinWeighted<T, Spin>& rhs) {\n  return {lhs * rhs.data()};\n}\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<get_vector_element_type_t<T>>() * std::declval<T>()),\n    Spin>\noperator*(const get_vector_element_type_t<T>& lhs,\n          const SpinWeighted<T, Spin>& rhs) {\n  return {lhs * rhs.data()};\n}\n/// @}\n\n/// @{\n/// \\brief Divide two spin-weighted quantities if the types are compatible and\n/// subtract the spins. Un-weighted quantities are assumed to be spin 0.\n// These overloads are designed to allow SpinWeighted to wrap Blaze expression\n// templates to ensure efficient math operations, necessitating the\n// `decltype(declval<T>() ...` syntax\ntemplate <typename T1, typename T2, int Spin1, int Spin2>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<T1>() / std::declval<T2>()), Spin1 - Spin2>\noperator/(const SpinWeighted<T1, Spin1>& lhs,\n          const SpinWeighted<T2, Spin2>& rhs) {\n  return {lhs.data() / rhs.data()};\n}\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T>() / std::declval<T>()), Spin>\n    operator/(const SpinWeighted<T, Spin>& lhs, const T& rhs) {\n  return {lhs.data() / rhs};\n}\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<T>() / std::declval<get_vector_element_type_t<T>>()),\n    Spin>\noperator/(const SpinWeighted<T, Spin>& lhs,\n          const get_vector_element_type_t<T>& rhs) {\n  return {lhs.data() / rhs};\n}\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE\n    SpinWeighted<decltype(std::declval<T>() / std::declval<T>()), -Spin>\n    operator/(const T& lhs, const SpinWeighted<T, Spin>& rhs) {\n  return {lhs / rhs.data()};\n}\ntemplate <\n    typename T, int Spin,\n    Requires<not std::is_same_v<T, get_vector_element_type_t<T>>> = nullptr>\nSPECTRE_ALWAYS_INLINE SpinWeighted<\n    decltype(std::declval<get_vector_element_type_t<T>>() / std::declval<T>()),\n    -Spin>\noperator/(const get_vector_element_type_t<T>& lhs,\n          const SpinWeighted<T, Spin>& rhs) {\n  return {lhs / rhs.data()};\n}\n/// @}\n\n/// conjugate the spin-weighted quantity, inverting the spin\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE SpinWeighted<decltype(conj(std::declval<T>())), -Spin>\nconj(const SpinWeighted<T, Spin>& value) {\n  return {conj(value.data())};\n}\n\n/// Take the exponential of the spin-weighted quantity; only valid for\n/// spin-weight = 0\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE SpinWeighted<decltype(exp(std::declval<T>())), 0> exp(\n    const SpinWeighted<T, 0>& value) {\n  return {exp(value.data())};\n}\n\n/// Take the square-root of the spin-weighted quantity; only valid for\n/// spin-weight = 0\ntemplate <typename T, int Spin>\nSPECTRE_ALWAYS_INLINE SpinWeighted<decltype(sqrt(std::declval<T>())), 0> sqrt(\n    const SpinWeighted<T, Spin>& value) {\n  return {sqrt(value.data())};\n}\n\n/// @{\n/// \\brief Test equivalence of spin-weighted quantities if the types are\n/// compatible and spins are the same. Un-weighted quantities are assumed to\n/// be spin 0.\ntemplate <typename T1, typename T2, int Spin>\nSPECTRE_ALWAYS_INLINE bool operator==(const SpinWeighted<T1, Spin>& lhs,\n                                      const SpinWeighted<T2, Spin>& rhs) {\n  return lhs.data() == rhs.data();\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE bool operator==(const SpinWeighted<T, 0>& lhs,\n                                      const T& rhs) {\n  return lhs.data() == rhs;\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE bool operator==(const T& lhs,\n                                      const SpinWeighted<T, 0>& rhs) {\n  return lhs == rhs.data();\n}\n/// @}\n\n/// @{\n/// \\brief Test inequivalence of spin-weighted quantities if the types are\n/// compatible and spins are the same. Un-weighted quantities are assumed to be\n/// spin 0.\ntemplate <typename T1, typename T2, int Spin>\nSPECTRE_ALWAYS_INLINE bool operator!=(const SpinWeighted<T1, Spin>& lhs,\n                                      const SpinWeighted<T2, Spin>& rhs) {\n  return not(lhs == rhs);\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE bool operator!=(const SpinWeighted<T, 0>& lhs,\n                                      const T& rhs) {\n  return not(lhs == rhs);\n}\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE bool operator!=(const T& lhs,\n                                      const SpinWeighted<T, 0>& rhs) {\n  return not(lhs == rhs);\n}\n/// @}\n\n/// \\ingroup DataStructuresGroup\n/// Make the input `view` a `const` view of the const data `spin_weighted`, at\n/// offset `offset` and length `extent`.\n///\n/// \\warning This DOES modify the (const) input `view`. The reason `view` is\n/// taken by const pointer is to try to insist that the object to be a `const`\n/// view is actually const. Of course, there are ways of subverting this\n/// intended functionality and editing the data pointed into by `view` after\n/// this function is called; doing so is highly discouraged and results in\n/// undefined behavior.\ntemplate <typename SpinWeightedType,\n          Requires<is_any_spin_weighted_v<SpinWeightedType> and\n                   is_derived_of_vector_impl_v<\n                       typename SpinWeightedType::value_type>> = nullptr>\nvoid make_const_view(const gsl::not_null<const SpinWeightedType*> view,\n                     const SpinWeightedType& spin_weighted, const size_t offset,\n                     const size_t extent) {\n  const_cast<SpinWeightedType*>(view.get())  // NOLINT\n      ->set_data_ref(const_cast<             // NOLINT\n                         typename SpinWeightedType::value_type::value_type*>(\n                         spin_weighted.data().data()) +  // NOLINT\n                         offset,\n                     extent);\n}\n\n/// Stream operator simply forwards\ntemplate <typename T, int Spin>\nstd::ostream& operator<<(std::ostream& os, const SpinWeighted<T, Spin>& d) {\n  return os << d.data();\n}\n\nnamespace MakeWithValueImpls {\ntemplate <int Spin, typename SpinWeightedType>\nstruct NumberOfPoints<SpinWeighted<SpinWeightedType, Spin>> {\n  static SPECTRE_ALWAYS_INLINE size_t\n  apply(const SpinWeighted<SpinWeightedType, Spin>& input) {\n    return number_of_points(input.data());\n  }\n};\n\ntemplate <int Spin, typename SpinWeightedType>\nstruct MakeWithSize<SpinWeighted<SpinWeightedType, Spin>> {\n  template <typename ValueType>\n  static SPECTRE_ALWAYS_INLINE SpinWeighted<SpinWeightedType, Spin> apply(\n      const size_t size, const ValueType value) {\n    return SpinWeighted<SpinWeightedType, Spin>{\n        make_with_value<SpinWeightedType>(size, value)};\n  }\n};\n}  // namespace MakeWithValueImpls\n\ntemplate <int Spin, typename SpinWeightedType>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n    SpinWeighted<SpinWeightedType, Spin>> {\n  static constexpr bool is_trivial =\n      SetNumberOfGridPointsImpl<SpinWeightedType>::is_trivial;\n  static SPECTRE_ALWAYS_INLINE void apply(\n      const gsl::not_null<SpinWeighted<SpinWeightedType, Spin>*> result,\n      const size_t size) {\n    set_number_of_grid_points(make_not_null(&result->data()), size);\n  }\n};\n"
  },
  {
    "path": "src/DataStructures/StaticDeque.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <type_traits>\n\n#include \"Utilities/PrintHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/StlBoilerplate.hpp\"\n\nnamespace static_deque_detail {\n// \"The extent to which an implementation determines that a type\n// cannot be an input iterator is unspecified, except that as a\n// minimum integral types shall not qualify as input iterators.\"\n//   -- N4659 26.2.1.17\ntemplate <typename U>\nconstexpr static bool is_input_iterator = not std::is_integral_v<U>;\n}  // namespace static_deque_detail\n\n/// A class implementing the std::deque interface with static storage.\n///\n/// Differences from std::deque:\n/// * The size of the container cannot be increased above \\p Capacity.\n/// * Operations moving the entire container are \\f$O(n)\\f$ instead of\n///   \\f$O(1)\\f$ in the size and invalidate all references and\n///   iterators.\n/// * Erasing elements from the front of the queue invalidates all\n///   iterators (but not references).\n///\n/// This last point is not a fundamental limitation, but could be\n/// corrected with a more complicated iterator implementation if the\n/// standard behavior is found to be useful.\ntemplate <typename T, size_t Capacity>\nclass StaticDeque\n    : public stl_boilerplate::RandomAccessSequence<StaticDeque<T, Capacity>,\n                                                   T, true> {\n  // Capacity = size_t max is special and dynamically allocates, but\n  // we don't advertise that here and only intend it to be used\n  // through CircularDeque.\n public:\n  // Aliases needed in the class definition.  The rest can just be\n  // inherited.\n  using size_type = typename StaticDeque::size_type;\n  using difference_type = typename StaticDeque::difference_type;\n  using iterator = typename StaticDeque::iterator;\n  using const_iterator = typename StaticDeque::const_iterator;\n\n  StaticDeque() = default;\n  explicit StaticDeque(size_type count);\n  StaticDeque(size_type n, const T& value);\n  template <\n      typename InputIterator,\n      Requires<static_deque_detail::is_input_iterator<InputIterator>> = nullptr>\n  StaticDeque(InputIterator first, InputIterator last);\n  StaticDeque(const StaticDeque& other);\n  StaticDeque(StaticDeque&& other);\n  StaticDeque(std::initializer_list<T> init);\n\n  ~StaticDeque();\n\n  StaticDeque& operator=(const StaticDeque& other);\n  StaticDeque& operator=(StaticDeque&& other);\n  StaticDeque& operator=(std::initializer_list<T> init);\n\n  template <\n      typename InputIterator,\n      Requires<static_deque_detail::is_input_iterator<InputIterator>> = nullptr>\n  void assign(InputIterator first, InputIterator last);\n  void assign(size_type count, const T& value);\n  void assign(std::initializer_list<T> init);\n\n  size_type size() const { return size_; }\n  size_type capacity() const { return data_.capacity; }\n  static constexpr size_type max_size() { return Capacity; }\n  void reserve(size_type count);\n  void resize(size_type count);\n  void resize(size_type count, const T& value);\n  void shrink_to_fit();\n\n  T& operator[](size_type n) { return *entry_address(n); }\n  const T& operator[](size_type n) const { return *entry_address(n); }\n\n  template <typename... Args>\n  T& emplace_front(Args&&... args);\n  template <typename... Args>\n  T& emplace_back(Args&&... args);\n  template <typename... Args>\n  iterator emplace(const_iterator position, Args&&... args);\n\n  void push_front(const T& x) { emplace_front(x); }\n  void push_front(T&& x) { emplace_front(std::move(x)); }\n  void push_back(const T& x) { emplace_back(x); }\n  void push_back(T&& x) { emplace_back(std::move(x)); }\n\n  iterator insert(const_iterator position, const T& x);\n  iterator insert(const_iterator position, T&& x);\n  iterator insert(const_iterator position, size_type n, const T& x);\n  template <\n      typename InputIterator,\n      Requires<static_deque_detail::is_input_iterator<InputIterator>> = nullptr>\n  iterator insert(const_iterator position, InputIterator first,\n                  InputIterator last);\n  iterator insert(const_iterator position, std::initializer_list<T> init);\n\n  void pop_front();\n  void pop_back();\n\n  iterator erase(const_iterator position);\n  iterator erase(const_iterator first, const_iterator last);\n\n  void swap(StaticDeque& other);\n  void clear();\n\n  void pup(PUP::er& p);\n\n private:\n  struct WithCapacityTag {};\n  StaticDeque(WithCapacityTag /*meta*/, size_t capacity);\n\n  // Operations on the middle of the deque are supposed to shift as\n  // few elements as possible.\n  bool should_operate_on_back(const const_iterator& first,\n                              const const_iterator& last) const {\n    return (this->end() - last) <= (first - this->begin());\n  }\n\n  // Index into the internal array of T of logical index n\n  size_type internal_index(size_type n) const {\n    auto index = start_position_ + n;\n    if (index >= capacity()) {\n      index -= capacity();\n    }\n    ASSERT(index < capacity(), \"Calculated out-of-range index: \" << index);\n    return index;\n  }\n\n  const T* entry_address(size_type n) const {\n    return reinterpret_cast<const T*>(data_.get()) + internal_index(n);\n  }\n\n  T* entry_address(size_type n) {\n    return reinterpret_cast<T*>(data_.get()) + internal_index(n);\n  }\n\n  void change_capacity(size_t new_capacity);\n\n  // This array contains the memory used to store the elements.  It is\n  // treated as an array of T, except that some elements may be\n  // missing, i.e., be segments of memory not containing objects.\n  // Elements are created using placement new and removed by calling\n  // their destructors.\n  //\n  // Logically, the array is considered to be a periodic structure.\n  // The existing elements are stored contiguously in the logical\n  // structure, which may correspond to a contiguous portion of the\n  // actual memory or a segment at the end and a segment at the\n  // beginning.  This segment can grow or shrink at either end,\n  // allowing insertions and removals at the ends without needing to\n  // move any other entries.\n  //\n  // The start_position_ variable stores the index of the start of the\n  // portion of the array that is in use, in the range [0, capacity).\n  // Care must be taken to keep this value in range when the start of\n  // the used memory crosses the end of the storage.  The size_\n  // variable stores the number of existing elements, which must be in\n  // the range [0, capacity].  (This could not be implemented using a\n  // \"one past the end\" index instead of the size, because that index\n  // is the same for size=0 and size=capacity.)\n  template <size_t Cap, typename = void>\n  struct Data {\n    static constexpr bool variable_capacity = false;\n    alignas(T) std::byte data[sizeof(T) * Cap];\n    static constexpr size_t capacity = Cap;\n\n    std::byte* get() { return data; }\n    const std::byte* get() const { return data; }\n  };\n\n  // gcc improperly forbids full specializations\n  // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85282)\n  template <typename Void>\n  struct Data<std::numeric_limits<size_t>::max(), Void> {\n    static constexpr bool variable_capacity = true;\n    // There doesn't seem to be any alignment guarantee on the\n    // contents of a vector<byte>, so we use operator new directly,\n    // which guarantees alignment suitable for any normal type.\n    std::unique_ptr<std::byte[]> data;\n    size_t capacity = 0;\n\n    std::byte* get() { return data.get(); }\n    const std::byte* get() const { return data.get(); }\n  };\n\n  Data<Capacity> data_;\n  size_type start_position_{0};\n  size_type size_{0};\n};\n\ntemplate <typename T, size_t Capacity>\nStaticDeque<T, Capacity>::StaticDeque(const size_type count) {\n  resize(count);\n}\n\ntemplate <typename T, size_t Capacity>\nStaticDeque<T, Capacity>::StaticDeque(const size_type n, const T& value) {\n  resize(n, value);\n}\n\ntemplate <typename T, size_t Capacity>\ntemplate <typename InputIterator,\n          Requires<static_deque_detail::is_input_iterator<InputIterator>>>\nStaticDeque<T, Capacity>::StaticDeque(const InputIterator first,\n                                      const InputIterator last) {\n  assign(first, last);\n}\n\ntemplate <typename T, size_t Capacity>\nStaticDeque<T, Capacity>::StaticDeque(const StaticDeque& other)\n    : StaticDeque(other.begin(), other.end()) {}\n\ntemplate <typename T, size_t Capacity>\nStaticDeque<T, Capacity>::StaticDeque(StaticDeque&& other) {\n  *this = std::move(other);\n}\n\ntemplate <typename T, size_t Capacity>\nStaticDeque<T, Capacity>::StaticDeque(const std::initializer_list<T> init)\n    : StaticDeque(init.begin(), init.end()) {}\n\ntemplate <typename T, size_t Capacity>\nStaticDeque<T, Capacity>::~StaticDeque() {\n  clear();\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::operator=(const StaticDeque& other)\n    -> StaticDeque& {\n  if (size() > other.size()) {\n    resize(other.size());\n  }\n  size_t i = 0;\n  for (; i < size(); ++i) {\n    (*this)[i] = other[i];\n  }\n  for (; i < other.size(); ++i) {\n    push_back(other[i]);\n  }\n  return *this;\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::operator=(StaticDeque&& other) -> StaticDeque& {\n  if constexpr (decltype(data_)::variable_capacity) {\n    using std::swap;\n    swap(data_, other.data_);\n    swap(start_position_, other.start_position_);\n    swap(size_, other.size_);\n  } else {\n    if (size() > other.size()) {\n      resize(other.size());\n    }\n    size_t i = 0;\n    for (; i < size(); ++i) {\n      (*this)[i] = std::move(other[i]);\n    }\n    for (; i < other.size(); ++i) {\n      push_back(std::move(other[i]));\n    }\n  }\n  return *this;\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::operator=(const std::initializer_list<T> init)\n    -> StaticDeque& {\n  assign(init);\n  return *this;\n}\n\ntemplate <typename T, size_t Capacity>\ntemplate <typename InputIterator,\n          Requires<static_deque_detail::is_input_iterator<InputIterator>>>\nvoid StaticDeque<T, Capacity>::assign(InputIterator first,\n                                      const InputIterator last) {\n  // This could be slightly more efficient if this object is not\n  // already empty by assigning to existing objects.\n  clear();\n  if constexpr (std::is_base_of_v<std::forward_iterator_tag,\n                                  typename std::iterator_traits<\n                                      InputIterator>::iterator_category>) {\n    reserve(static_cast<size_type>(std::distance(first, last)));\n  }\n  while (first != last) {\n    push_back(*first++);\n  }\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::assign(const size_type count, const T& value) {\n  ASSERT(count <= max_size(), count << \" exceeds max size \" << max_size());\n  // This could be slightly more efficient if this object is not\n  // already empty by assigning to existing objects.\n  clear();\n  resize(count, value);\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::assign(const std::initializer_list<T> init) {\n  assign(init.begin(), init.end());\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::reserve(const size_type count) {\n  if constexpr (decltype(data_)::variable_capacity) {\n    if (count > capacity()) {\n      change_capacity(count);\n    }\n  } else {\n    ASSERT(count <= capacity(), \"Cannot enlarge a StaticDeque.\");\n  }\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::resize(const size_type count) {\n  ASSERT(count <= max_size(), count << \" exceeds max size \" << max_size());\n  while (size() > count) {\n    pop_back();\n  }\n  reserve(count);\n  while (size() < count) {\n    emplace_back();\n  }\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::resize(const size_type count, const T& value) {\n  ASSERT(count <= max_size(), count << \" exceeds max size \" << max_size());\n  while (size() > count) {\n    pop_back();\n  }\n  reserve(count);\n  while (size() < count) {\n    push_back(value);\n  }\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::shrink_to_fit() {\n  if constexpr (decltype(data_)::variable_capacity) {\n    change_capacity(size());\n  }\n}\n\ntemplate <typename T, size_t Capacity>\ntemplate <typename... Args>\nT& StaticDeque<T, Capacity>::emplace_front(Args&&... args) {\n  ASSERT(size() < max_size(), \"Container is full.\");\n  if constexpr (decltype(data_)::variable_capacity) {\n    if (UNLIKELY(size() == capacity())) {\n      change_capacity(capacity() + 1);\n    }\n  }\n  auto* const new_entry =\n      new (entry_address(capacity() - 1)) T(std::forward<Args>(args)...);\n  // This must be done last in case T's constructor throws an\n  // exception.\n  if (start_position_ == 0) {\n    start_position_ = capacity() - 1;\n  } else {\n    --start_position_;\n  }\n  ++size_;\n  return *new_entry;\n}\n\ntemplate <typename T, size_t Capacity>\ntemplate <typename... Args>\nT& StaticDeque<T, Capacity>::emplace_back(Args&&... args) {\n  ASSERT(size() < max_size(), \"Container is full.\");\n  if constexpr (decltype(data_)::variable_capacity) {\n    if (UNLIKELY(size() == capacity())) {\n      change_capacity(capacity() + 1);\n    }\n  }\n  auto* const new_entry =\n      new (entry_address(size())) T(std::forward<Args>(args)...);\n  // This must be done last in case T's constructor throws an\n  // exception.\n  ++size_;\n  return *new_entry;\n}\n\ntemplate <typename T, size_t Capacity>\ntemplate <typename... Args>\nauto StaticDeque<T, Capacity>::emplace(const const_iterator position,\n                                       Args&&... args) -> iterator {\n  ASSERT(size() < max_size(), \"Container is full.\");\n  if constexpr (decltype(data_)::variable_capacity) {\n    if (UNLIKELY(size() == capacity())) {\n      change_capacity(capacity() + 1);\n    }\n  }\n\n  if (position == this->begin()) {\n    emplace_front(std::forward<Args>(args)...);\n    return this->begin();\n  } else if (position == this->end()) {\n    emplace_back(std::forward<Args>(args)...);\n    return this->end() - 1;\n  }\n\n  const auto element_to_assign = position - this->begin();\n  if (should_operate_on_back(position, position)) {\n    emplace_back(std::move(this->back()));\n    std::move_backward(this->begin() + element_to_assign, this->end() - 2,\n                       this->end() - 1);\n  } else {\n    emplace_front(std::move(this->front()));\n    std::move(this->begin() + 2, this->begin() + element_to_assign + 1,\n              this->begin() + 1);\n  }\n\n  const auto new_element = this->begin() + element_to_assign;\n  // emplace in the middle does not construct in place.  STL\n  // containers also have this behavior.\n  *new_element = T(std::forward<Args>(args)...);\n  return new_element;\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::insert(const const_iterator position, const T& x)\n    -> iterator {\n  return emplace(position, x);\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::insert(const const_iterator position, T&& x)\n    -> iterator {\n  return emplace(position, std::move(x));\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::insert(const const_iterator position,\n                                      const size_type n, const T& x)\n    -> iterator {\n  reserve(size() + n);\n  // In theory we could insert these in place and save a bunch of\n  // moves, but keeping track of when elements must be assigned\n  // vs. constructed would be a pain, and this can't be that common an\n  // operation.\n  if (should_operate_on_back(position, position)) {\n    // We are about to invalidate position.\n    const auto insert_index = position - this->begin();\n    const auto old_size = static_cast<difference_type>(size());\n    for (size_type i = 0; i < n; ++i) {\n      emplace_back(x);\n    }\n    const auto new_position = this->begin() + insert_index;\n    const auto old_end = this->begin() + old_size;\n    std::rotate(new_position, old_end, this->end());\n    return new_position;\n  } else {\n    // We are about to invalidate position.\n    const auto insert_index = position - this->begin();\n    const auto insert_back_index = this->end() - position;\n    const auto old_size = static_cast<difference_type>(size());\n    for (size_type i = 0; i < n; ++i) {\n      emplace_front(x);\n    }\n    const auto new_position = this->begin() + insert_index;\n    const auto new_position_end = this->end() - insert_back_index;\n    const auto old_begin = this->end() - old_size;\n    std::rotate(this->begin(), old_begin, new_position_end);\n    return new_position;\n  }\n}\n\ntemplate <typename T, size_t Capacity>\ntemplate <typename InputIterator,\n          Requires<static_deque_detail::is_input_iterator<InputIterator>>>\nauto StaticDeque<T, Capacity>::insert(const const_iterator position,\n                                      InputIterator first,\n                                      const InputIterator last) -> iterator {\n  if constexpr (std::is_base_of_v<std::forward_iterator_tag,\n                                  typename std::iterator_traits<\n                                      InputIterator>::iterator_category>) {\n    reserve(size() + static_cast<size_type>(std::distance(first, last)));\n  }\n  // The iterators could be single-pass, so we can't clear space and\n  // then insert the new elements.  We also can't do repeated single\n  // insertions because that's too inefficient.  Instead, we have to\n  // create the new elements at the end and then rotate them into\n  // place.\n  if (should_operate_on_back(position, position)) {\n    // We are about to invalidate position.\n    const auto insert_index = position - this->begin();\n    const auto old_size = static_cast<difference_type>(size());\n    while (first != last) {\n      emplace_back(*first++);\n    }\n    const auto new_position = this->begin() + insert_index;\n    const auto old_end = this->begin() + old_size;\n    std::rotate(new_position, old_end, this->end());\n    return new_position;\n  } else {\n    // We are about to invalidate position.\n    const auto insert_index = position - this->begin();\n    const auto insert_back_index = this->end() - position;\n    const auto old_size = static_cast<difference_type>(size());\n    while (first != last) {\n      emplace_front(*first++);\n    }\n    const auto new_position = this->begin() + insert_index;\n    const auto new_position_end = this->end() - insert_back_index;\n    const auto old_begin = this->end() - old_size;\n    std::reverse(this->begin(), old_begin);\n    std::rotate(this->begin(), old_begin, new_position_end);\n    return new_position;\n  }\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::insert(const const_iterator position,\n                                      const std::initializer_list<T> init)\n    -> iterator {\n  return insert(position, init.begin(), init.end());\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::pop_front() {\n  ASSERT(not this->empty(), \"Container is empty.\");\n  if (start_position_ == capacity() - 1) {\n    start_position_ = 0;\n  } else {\n    ++start_position_;\n  }\n  --size_;\n  // Calling the destructor is sufficient to remove the element.\n  // There is no special \"placement delete\".\n  entry_address(capacity() - 1)->~T();\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::pop_back() {\n  ASSERT(not this->empty(), \"Container is empty.\");\n  --size_;\n  // Calling the destructor is sufficient to remove the element.\n  // There is no special \"placement delete\".\n  entry_address(size())->~T();\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::erase(const const_iterator position)\n    -> iterator {\n  return erase(position, position + 1);\n}\n\ntemplate <typename T, size_t Capacity>\nauto StaticDeque<T, Capacity>::erase(const const_iterator first,\n                                     const const_iterator last) -> iterator {\n  const auto result_offset = first - this->begin();\n  auto num_to_remove = last - first;\n  if (num_to_remove == 0) {\n    return last - this->begin() + this->begin();\n  }\n  if (should_operate_on_back(first, last)) {\n    std::move(last, this->cend(), first - this->begin() + this->begin());\n    while (num_to_remove-- != 0) {\n      pop_back();\n    }\n  } else {\n    std::move_backward(this->cbegin(), first,\n                       last - this->begin() + this->begin());\n    while (num_to_remove-- != 0) {\n      pop_front();\n    }\n  }\n  return this->begin() + result_offset;\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::swap(StaticDeque& other) {\n  using std::swap;\n  swap(*this, other);\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::clear() {\n  while (not this->empty()) {\n    pop_back();\n  }\n}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::pup(PUP::er& p) {\n  // If performance when serializing simple types becomes an issue, we\n  // can just send the array of bytes and the other member variables,\n  // which will probably perform better (unless, perhaps, the\n  // container is nearly empty).  That can't be done for types that\n  // are not trivially copyable, which would still need to use\n  // something like the current implementation.\n  size_type sz = size();\n  p | sz;\n  resize(sz);\n  for (auto& entry : *this) {\n    p | entry;\n  }\n}\n\ntemplate <typename T, size_t Capacity>\nStaticDeque<T, Capacity>::StaticDeque(\n    StaticDeque<T, Capacity>::WithCapacityTag /*meta*/, const size_t capacity)\n    : data_{std::unique_ptr<std::byte[]>(new std::byte[capacity * sizeof(T)]),\n            capacity} {}\n\ntemplate <typename T, size_t Capacity>\nvoid StaticDeque<T, Capacity>::change_capacity(const size_t new_capacity) {\n  ASSERT(decltype(data_)::variable_capacity, \"Trying to resize an array.\");\n  ASSERT(new_capacity >= size(), \"Insufficient space for existing elements.\");\n  if constexpr (decltype(data_)::variable_capacity) {\n    if (new_capacity == capacity()) {\n      return;\n    }\n    if (new_capacity == 0) {\n      data_.data.reset();\n      data_.capacity = 0;\n      return;\n    }\n\n    StaticDeque new_deque(WithCapacityTag{}, new_capacity);\n    new_deque.assign(std::make_move_iterator(this->begin()),\n                     std::make_move_iterator(this->end()));\n    *this = std::move(new_deque);\n  }\n}\n\ntemplate <typename T, size_t Capacity>\nvoid swap(StaticDeque<T, Capacity>& x, StaticDeque<T, Capacity>& y) {\n  // swap on CircularBuffers will prefer the generic one in std over\n  // this one, but this still gets called by some member functions.\n  // The generic implementation will correctly swap the allocations\n  // instead of working with individual elements.\n  if constexpr (Capacity == std::numeric_limits<size_t>::max()) {\n    std::swap(x, y);\n    return;\n  }\n\n  const auto initial_size = x.size();\n  if (initial_size > y.size()) {\n    return swap(y, x);\n  }\n  for (size_t i = 0; i < initial_size; ++i) {\n    using std::swap;\n    swap(x[i], y[i]);\n  }\n  const auto to_move =\n      y.begin() +\n      static_cast<typename StaticDeque<T, Capacity>::difference_type>(\n          initial_size);\n  x.insert(x.end(), std::move_iterator(to_move), std::move_iterator(y.end()));\n  y.erase(to_move, y.end());\n}\n\ntemplate <typename T, size_t Capacity>\nstd::ostream& operator<<(std::ostream& s,\n                         const StaticDeque<T, Capacity>& deque) {\n  sequence_print_helper(s, deque.begin(), deque.end());\n  return s;\n}\n"
  },
  {
    "path": "src/DataStructures/StaticMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// `blaze::StaticMatrix` is a general-purpose fixed size matrix type. This file\n/// implements interoperability of `blaze::StaticMatrix` with our data\n/// structures.\n\n#pragma once\n\n#include <array>\n#include <blaze/math/StaticMatrix.h>\n#include <cstddef>\n#include <pup.h>\n#include <type_traits>\n\n#include \"Options/ParseOptions.hpp\"\n\nnamespace PUP {\n/// @{\n/// Serialization of blaze::StaticMatrix\ntemplate <typename Type, size_t M, size_t N, bool SO, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nvoid pup(er& p, blaze::StaticMatrix<Type, M, N, SO, AF, PF, Tag>& t) {\n  const size_t spacing = t.spacing();\n  const size_t data_size = spacing * (SO == blaze::rowMajor ? M : N);\n  if (std::is_fundamental_v<Type>) {\n    PUParray(p, t.data(), data_size);\n  } else {\n    for (size_t i = 0; i < data_size; ++i) {\n      p | t.data()[i];\n    }\n  }\n}\ntemplate <typename Type, size_t M, size_t N, bool SO, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nvoid operator|(er& p, blaze::StaticMatrix<Type, M, N, SO, AF, PF, Tag>& t) {\n  pup(p, t);\n}\n/// @}\n}  // namespace PUP\n\ntemplate <typename Type, size_t M, size_t N, bool SO, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nstruct Options::create_from_yaml<\n    blaze::StaticMatrix<Type, M, N, SO, AF, PF, Tag>> {\n  template <typename Metavariables>\n  static blaze::StaticMatrix<Type, M, N, SO, AF, PF, Tag> create(\n      const Options::Option& options) {\n    return {options.parse_as<std::array<std::array<Type, N>, M>>()};\n  }\n};\n"
  },
  {
    "path": "src/DataStructures/StaticVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// `blaze::StaticVector` is a general-purpose fixed size vector type. This file\n/// implements interoperability of `blaze::StaticVector` with our data\n/// structures.\n\n#pragma once\n\n#include <array>\n#include <blaze/math/StaticVector.h>\n#include <pup.h>\n#include <type_traits>\n\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace PUP {\n/// @{\n/// Serialization of blaze::StaticVector\ntemplate <typename T, size_t N, bool TF, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nvoid pup(er& p, blaze::StaticVector<T, N, TF, AF, PF, Tag>& t) {\n  if (std::is_fundamental_v<T>) {\n    PUParray(p, t.data(), N);\n  } else {\n    for (T& element : t) {\n      p | element;\n    }\n  }\n}\ntemplate <typename T, size_t N, bool TF, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nvoid operator|(er& p, blaze::StaticVector<T, N, TF, AF, PF, Tag>& t) {\n  pup(p, t);\n}\n/// @}\n}  // namespace PUP\n\nnamespace MakeWithValueImpls {\ntemplate <typename T, size_t N, bool TF, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nstruct NumberOfPoints<blaze::StaticVector<T, N, TF, AF, PF, Tag>> {\n  static constexpr size_t apply(\n      const blaze::StaticVector<T, N, TF, AF, PF, Tag>& /*input*/) {\n    return N;\n  }\n};\n\ntemplate <typename T, size_t N, bool TF, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nstruct MakeWithSize<blaze::StaticVector<T, N, TF, AF, PF, Tag>> {\n  static SPECTRE_ALWAYS_INLINE blaze::StaticVector<T, N, TF, AF, PF, Tag> apply(\n      const size_t size, const T& value) {\n    ASSERT(size == N, \"Size mismatch for StaticVector: Expected \"\n                          << N << \", got \" << size << \".\");\n    return blaze::StaticVector<T, N, TF, AF, PF, Tag>(value);\n  }\n};\n}  // namespace MakeWithValueImpls\n\ntemplate <typename T, size_t N, bool TF, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n    blaze::StaticVector<T, N, TF, AF, PF, Tag>> {\n  static constexpr bool is_trivial = false;\n  static SPECTRE_ALWAYS_INLINE void apply(\n      const gsl::not_null<blaze::StaticVector<T, N, TF, AF, PF, Tag>*>\n      /*result*/,\n      const size_t size) {\n    ERROR(\"Tried to resize a StaticVector to \" << size);\n  }\n};\n\ntemplate <typename T, size_t N, bool TF, blaze::AlignmentFlag AF,\n          blaze::PaddingFlag PF, typename Tag>\nstruct Options::create_from_yaml<blaze::StaticVector<T, N, TF, AF, PF, Tag>> {\n  template <typename Metavariables>\n  static blaze::StaticVector<T, N, TF, AF, PF, Tag> create(\n      const Options::Option& options) {\n    return {options.parse_as<std::array<T, N>>()};\n  }\n};\n"
  },
  {
    "path": "src/DataStructures/StripeIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/StripeIterator.hpp\"\n\n#include <functional>\n#include <numeric>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n\ntemplate <size_t Dim>\nStripeIterator::StripeIterator(const Index<Dim>& extents,\n                               const size_t stripe_dim)\n    : offset_(0),\n      size_(extents.product()),\n      stride_(std::accumulate(extents.begin(), extents.begin() + stripe_dim,\n                              1_st, std::multiplies<size_t>())),\n      stride_count_(0),\n      jump_((extents[stripe_dim] - 1) * stride_) {}\n\ntemplate StripeIterator::StripeIterator(const Index<1>&, const size_t);\ntemplate StripeIterator::StripeIterator(const Index<2>&, const size_t);\ntemplate StripeIterator::StripeIterator(const Index<3>&, const size_t);\n"
  },
  {
    "path": "src/DataStructures/StripeIterator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t>\nclass Index;\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Iterates over the 1-dimensional stripes with info on how to\n * iterate over the current stripe\n */\nclass StripeIterator {\n public:\n  /// Construct from the grid points in each direction and which dimension the\n  /// stripes are in.\n  template <size_t Dim>\n  StripeIterator(const Index<Dim>& extents, size_t stripe_dim);\n\n  /// Returns `true` if the iterator is valid\n  explicit operator bool() const { return offset_ < size_; }\n\n  /// Increment to the next stripe.\n  StripeIterator& operator++() {\n    ++offset_;\n    ++stride_count_;\n    if (UNLIKELY(stride_count_ == stride_)) {\n      offset_ += jump_;\n      stride_count_ = 0;\n    }\n    return *this;\n  }\n\n  /// Offset into DataVector for first element of stripe.\n  size_t offset() const { return offset_; }\n\n  /// Stride of elements in DataVector for the stripe.\n  size_t stride() const { return stride_; }\n\n private:\n  size_t offset_ = std::numeric_limits<size_t>::max();\n  size_t size_ = std::numeric_limits<size_t>::max();\n  size_t stride_ = std::numeric_limits<size_t>::max();\n  size_t stride_count_ = std::numeric_limits<size_t>::max();\n  size_t jump_ = std::numeric_limits<size_t>::max();\n};\n"
  },
  {
    "path": "src/DataStructures/TaggedContainers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// @{\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Retrieves a desired tag from data structures containing tags.\n *\n * \\details Given multiple containers retrieve a desired tag from the first\n * container in which it is found. The containers are searched in the order\n * in which they are supplied (i.e. `first_vars` is checked before `vars`).\n * An error will be emitted if the tag cannot be found in any container.\n */\ntemplate <typename Tag, typename... TagsOrTagsList, typename... Ts,\n          template <class...> class U,\n          Requires<(not tt::is_a_v<gsl::not_null, U<TagsOrTagsList...>>)and not(\n              ... and tt::is_a_v<gsl::not_null, std::decay_t<Ts>>)> = nullptr>\nconst typename Tag::type& get(const U<TagsOrTagsList...>& first_vars,\n                              const Ts&... vars) {\n  if constexpr (tt::is_a_v<db::DataBox, U<TagsOrTagsList...>>) {\n    if constexpr (not db::tag_is_retrievable_v<Tag, U<TagsOrTagsList...>> and\n                  sizeof...(Ts) != 0) {\n      return get<Tag>(vars...);\n    } else {\n      return get<Tag>(first_vars);\n    }\n  } else {\n    if constexpr (not tmpl::list_contains_v<\n                      tmpl::flatten<tmpl::list<TagsOrTagsList...>>, Tag> and\n                  sizeof...(Ts) != 0) {\n      return get<Tag>(vars...);\n    } else {\n      return get<Tag>(first_vars);\n    }\n  }\n}\n\ntemplate <typename Tag, typename... TagsOrTagsList, typename... Ts,\n          template <class...> class U>\ngsl::not_null<typename Tag::type*> get(\n    const gsl::not_null<U<TagsOrTagsList...>*> first_vars,\n    const gsl::not_null<Ts>... vars) {\n  if constexpr (tt::is_a_v<db::DataBox, U<TagsOrTagsList...>>) {\n    if constexpr (not db::tag_is_retrievable_v<Tag, U<TagsOrTagsList...>> and\n                  sizeof...(Ts) != 0) {\n      if constexpr (sizeof...(Ts) == 1) {\n        return make_not_null(&get<Tag>(*vars...));\n      } else {\n        return get<Tag>(vars...);\n      }\n    } else {\n      return make_not_null(&get<Tag>(*first_vars));\n    }\n  } else {\n    if constexpr (not tmpl::list_contains_v<\n                      tmpl::flatten<tmpl::list<TagsOrTagsList...>>, Tag> and\n                  sizeof...(Ts) != 0) {\n      (void)first_vars;\n      if constexpr (sizeof...(Ts) == 1) {\n        return make_not_null(&get<Tag>(*vars...));\n      } else {\n        return get<Tag>(vars...);\n      }\n    } else {\n      return make_not_null(&get<Tag>(*first_vars));\n    }\n  }\n}\n/// @}\n\nnamespace detail {\ntemplate <typename Callable, typename T, typename... Args,\n          typename... ReturnTags, typename... ArgumentTags>\nauto apply_impl([[maybe_unused]] const gsl::not_null<T*> return_args,\n                tmpl::list<ReturnTags...> /*meta*/,\n                tmpl::list<ArgumentTags...> /*meta*/, Callable&& fn,\n                const Args&... args) {\n  return fn.apply(get<ReturnTags>(return_args)...,\n                  get<ArgumentTags>(args...)...);\n}\n\ntemplate <typename Callable, typename T, typename... Args,\n          typename... ReturnTags, typename... ArgumentTags>\nauto invoke_impl([[maybe_unused]] const gsl::not_null<T*> return_args,\n                 tmpl::list<ReturnTags...> /*meta*/,\n                 tmpl::list<ArgumentTags...> /*meta*/, Callable&& fn,\n                 const Args&... args) {\n  return fn(get<ReturnTags>(return_args)..., get<ArgumentTags>(args...)...);\n}\n}  // namespace detail\n\n/*!\n * \\brief Call\n * `fn.apply(get<Callable::return_tags>(return_args)...,\n * get<Callable::argument_tags>(args...))`\n */\ntemplate <typename Callable, typename T, typename... Args>\nauto apply(const gsl::not_null<T*> return_args, Callable&& fn,\n           const Args&... args) {\n  return detail::apply_impl(return_args, typename Callable::return_tags{},\n                            typename Callable::argument_tags{},\n                            std::forward<Callable>(fn), args...);\n}\n\n/*!\n * \\brief Call\n * `fn(get<Callable::return_tags>(return_args)...,\n * get<Callable::argument_tags>(args...))`\n */\ntemplate <typename Callable, typename T, typename... Args>\nauto invoke(const gsl::not_null<T*> return_args, Callable&& fn,\n            const Args&... args) {\n  return detail::invoke_impl(return_args, typename Callable::return_tags{},\n                             typename Callable::argument_tags{},\n                             std::forward<Callable>(fn), args...);\n}\n"
  },
  {
    "path": "src/DataStructures/TaggedTuple.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <initializer_list>\n#include <ostream>\n#include <stack>\n#include <string>\n#include <utility>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/PrintHelpers.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/*!\n * \\brief Contains utilities for working with tuples\n */\nnamespace tuples {\n\n#if __cplusplus >= 201402L\n#define TUPLES_LIB_CONSTEXPR_CXX_14 constexpr\n#else\n#define TUPLES_LIB_CONSTEXPR_CXX_14\n#endif\n\nnamespace tuples_detail {\n\ntemplate <class T>\ninline constexpr T&& forward(typename std::remove_reference<T>::type& t) {\n  return static_cast<T&&>(t);\n}\n\ntemplate <class T>\ninline constexpr T&& forward(typename std::remove_reference<T>::type&& t) {\n  static_assert(!std::is_lvalue_reference<T>::value,\n                \"cannot forward an rvalue as an lvalue\");\n  return static_cast<T&&>(t);\n}\n\ntemplate <class T, T...>\nstruct value_list {};\n\ntemplate <class...>\nstruct typelist {};\n\ntemplate <bool... Bs>\nusing all = typename std::is_same<\n    value_list<bool, Bs...>,\n    value_list<bool, (static_cast<void>(Bs), true)...>>::type;\n\nstruct no_such_type {\n  no_such_type() = delete;\n  no_such_type(no_such_type const& /*unused*/) = delete;\n  no_such_type(no_such_type&& /*unused*/) = delete;\n  ~no_such_type() = delete;\n  no_such_type& operator=(no_such_type const& /*unused*/) = delete;\n  no_such_type operator=(no_such_type&& /*unused*/) = delete;\n};\n\nnamespace detail {\nusing std::swap;\n\ntemplate <class T, class S,\n          bool = not std::is_void<T>::value and not std::is_void<S>::value>\nstruct is_swappable_with {\n  template <class L, class R>\n  static auto test_swap(int)\n      -> decltype(swap(std::declval<L&>(), std::declval<R&>()));\n  template <class L, class R>\n  static tuples::tuples_detail::no_such_type test_swap(...);\n\n  static const bool value =\n      not std::is_same<decltype(test_swap<T, S>(0)),\n                       tuples::tuples_detail::no_such_type>::value and\n      not std::is_same<decltype(test_swap<S, T>(0)),\n                       tuples::tuples_detail::no_such_type>::value;\n};\n\ntemplate <class T, class S>\nstruct is_swappable_with<T, S, false> : std::false_type {};\n}  // namespace detail\n\ntemplate <class T, class S>\nusing is_swappable_with = detail::is_swappable_with<T, S>;\n\ntemplate <typename... Ts>\nconstexpr char expand_pack(Ts&&... /*unused*/) {\n  return '0';\n}\n}  // namespace tuples_detail\n\nnamespace tuples_detail {\ntemplate <class Tag, bool Ebo = std::is_empty<typename Tag::type>::value &&\n                                !__is_final(typename Tag::type)>\nclass TaggedTupleLeaf;\n\ntemplate <class T, bool B>\nvoid swap(TaggedTupleLeaf<T, B>& lhs, TaggedTupleLeaf<T, B>& rhs) {\n  using std::swap;\n  swap(lhs.get_data(), rhs.get_data());\n}\n\ntemplate <class Tag>\nclass TaggedTupleLeaf<Tag, false> {\n  using value_type = typename Tag::type;\n  value_type value_;\n\n  template <class T>\n  static constexpr bool can_bind_reference() {\n    using rem_ref_value_type = typename std::remove_reference<value_type>::type;\n    using rem_ref_T = typename std::remove_reference<T>::type;\n    using is_lvalue_type = std::integral_constant<\n        bool,\n        std::is_lvalue_reference<T>::value or\n            std::is_same<std::reference_wrapper<rem_ref_value_type>,\n                         rem_ref_T>::value or\n            std::is_same<std::reference_wrapper<typename std::remove_const<\n                             rem_ref_value_type>::type>,\n                         rem_ref_T>::value>;\n    return not std::is_reference<value_type>::value or\n           (std::is_lvalue_reference<value_type>::value and\n            is_lvalue_type::value) or\n           (std::is_rvalue_reference<value_type>::value and\n            not std::is_lvalue_reference<T>::value);\n  }\n\n public:\n  // Tested in constexpr context in Unit.TaggedTuple.Ebo\n  constexpr TaggedTupleLeaf() : value_() {\n    static_assert(\n        !std::is_reference<value_type>::value,\n        \"Cannot default construct a reference element in a TaggedTuple\");\n  }\n\n  // clang-tidy: forwarding references can be bad\n  template <\n      class T,\n      typename std::enable_if<\n          !std::is_same<typename std::decay<T>::type, TaggedTupleLeaf>::value &&\n          std::is_constructible<value_type, T>::value>::type* = nullptr>\n  constexpr explicit TaggedTupleLeaf(T&& t)\n      : value_(tuples_detail::forward<T>(t)) {\n    static_assert(can_bind_reference<T>(),\n                  \"Cannot construct an lvalue reference with an rvalue\");\n  }\n\n  constexpr TaggedTupleLeaf(TaggedTupleLeaf const& /*rhs*/) = default;\n  constexpr TaggedTupleLeaf(TaggedTupleLeaf&& /*rhs*/) = default;\n  constexpr TaggedTupleLeaf& operator=(TaggedTupleLeaf const& /*rhs*/) =\n      default;\n  constexpr TaggedTupleLeaf& operator=(TaggedTupleLeaf&& /*rhs*/) = default;\n\n  ~TaggedTupleLeaf() = default;\n\n  // Note: name get_data instead of get to enable structured binding support.\n#if __cplusplus < 201402L\n  value_type& get_data() { return value_; }\n#else\n  constexpr value_type& get_data() { return value_; }\n#endif\n  constexpr const value_type& get_data() const { return value_; }\n\n  bool swap(TaggedTupleLeaf& t) {\n    using std::swap;\n    swap(*this, t);\n    return false;\n  }\n\n  // clang-tidy: runtime-references\n  void pup(PUP::er& p) { p | value_; }  // NOLINT\n};\n\ntemplate <class Tag>\nclass TaggedTupleLeaf<Tag, true> : private Tag::type {\n  using value_type = typename Tag::type;\n\n public:\n  constexpr TaggedTupleLeaf() : value_type{} {}\n\n  template <\n      class T,\n      typename std::enable_if<\n          !std::is_same<typename std::decay<T>::type, TaggedTupleLeaf>::value &&\n          std::is_constructible<value_type, T&&>::value>::type* = nullptr>\n  constexpr explicit TaggedTupleLeaf(T&& t)\n      : value_type(tuples_detail::forward<T>(t)) {}\n\n  constexpr TaggedTupleLeaf(TaggedTupleLeaf const& /*rhs*/) = default;\n  constexpr TaggedTupleLeaf(TaggedTupleLeaf&& /*rhs*/) = default;\n  constexpr TaggedTupleLeaf& operator=(TaggedTupleLeaf const& /*rhs*/) =\n      default;\n  constexpr TaggedTupleLeaf& operator=(TaggedTupleLeaf&& /*rhs*/) = default;\n\n  ~TaggedTupleLeaf() = default;\n\n  // Note: name get_data instead of get to enable structured binding support.\n#if __cplusplus < 201402L\n  value_type& get_data() { return static_cast<value_type&>(*this); }\n#else\n  constexpr value_type& get_data() { return static_cast<value_type&>(*this); }\n#endif\n\n  constexpr const value_type& get_data() const {\n    return static_cast<const value_type&>(*this);\n  }\n\n  bool swap(TaggedTupleLeaf& t) {\n    using std::swap;\n    swap(*this, t);\n    return false;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | static_cast<typename Tag::type&>(*this); }\n};\n\nstruct disable_constructors {\n  static constexpr bool enable_default() { return false; }\n  static constexpr bool enable_explicit() { return false; }\n  static constexpr bool enable_implicit() { return false; }\n};\n}  // namespace tuples_detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief An associative container that is indexed by structs\n *\n * A data structure that is indexed by Tags. A Tag is a struct that contains\n * a type alias named `type`, which is the type of the object stored with\n * index Tag.\n *\n * \\tparam Tags the tags of the objects to be placed in the tuple\n */\ntemplate <class... Tags>\nclass TaggedTuple;\n\ntemplate <class Tag, class... Tags>\nconstexpr const typename Tag::type& get(const TaggedTuple<Tags...>& t);\ntemplate <class Tag, class... Tags>\nconstexpr typename Tag::type& get(TaggedTuple<Tags...>& t);\ntemplate <class Tag, class... Tags>\nconstexpr const typename Tag::type&& get(const TaggedTuple<Tags...>&& t);\ntemplate <class Tag, class... Tags>\nconstexpr typename Tag::type&& get(TaggedTuple<Tags...>&& t);\n\n/*!\n * \\brief Returns the type of the Tag\n */\ntemplate <class Tag>\nusing tag_type = typename Tag::type;\n\n// clang-tidy: class does not define copy or move assignment (it does)\ntemplate <class... Tags>\nclass TaggedTuple : private tuples_detail::TaggedTupleLeaf<Tags>... {  // NOLINT\n  template <class... Args>\n  struct pack_is_TaggedTuple : std::false_type {};\n  template <class... Args>\n  struct pack_is_TaggedTuple<TaggedTuple<Args...>> : std::true_type {};\n\n  template <bool EnableConstructor, class Dummy = void>\n  struct args_constructor : tuples_detail::disable_constructors {};\n\n  template <class Dummy>\n  struct args_constructor<true, Dummy> {\n    static constexpr bool enable_default() {\n      return tuples_detail::all<\n          std::is_default_constructible<tag_type<Tags>>::value...>::value;\n    }\n\n    template <class... Ts>\n    static constexpr bool enable_explicit() {\n      return tuples_detail::all<std::is_constructible<\n                 tuples_detail::TaggedTupleLeaf<Tags>, Ts>::value...>::value and\n             not tuples_detail::all<\n                 std::is_convertible<Ts, tag_type<Tags>>::value...>::value;\n    }\n    template <class... Ts>\n    static constexpr bool enable_implicit() {\n      return tuples_detail::all<std::is_constructible<\n                 tuples_detail::TaggedTupleLeaf<Tags>, Ts>::value...>::value and\n             tuples_detail::all<\n                 std::is_convertible<Ts, tag_type<Tags>>::value...>::value;\n    }\n  };\n\n  // C++17 Draft 23.5.3.2 Assignment - helper aliases\n  using is_copy_assignable =\n      tuples_detail::all<std::is_copy_assignable<tag_type<Tags>>::value...>;\n  using is_nothrow_copy_assignable = tuples_detail::all<\n      std::is_nothrow_copy_assignable<tag_type<Tags>>::value...>;\n  using is_move_assignable =\n      tuples_detail::all<std::is_move_assignable<tag_type<Tags>>::value...>;\n  using is_nothrow_move_assignable = tuples_detail::all<\n      std::is_nothrow_move_assignable<tag_type<Tags>>::value...>;\n\n  // clang-tidy: redundant declaration\n  template <class Tag, class... LTags>\n  friend constexpr const typename Tag::type& get(  // NOLINT\n      const TaggedTuple<LTags...>& t);\n  template <class Tag, class... LTags>\n  friend constexpr typename Tag::type& get(  // NOLINT\n      TaggedTuple<LTags...>& t);\n  template <class Tag, class... LTags>\n  friend constexpr const typename Tag::type&& get(  // NOLINT\n      const TaggedTuple<LTags...>&& t);\n  template <class Tag, class... LTags>\n  friend constexpr typename Tag::type&& get(  // NOLINT\n      TaggedTuple<LTags...>&& t);\n\n public:\n  using tags_list = tmpl::list<Tags...>;\n\n  static constexpr size_t size() { return sizeof...(Tags); }\n\n  // clang-tidy: runtime-references\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    static_cast<void>(std::initializer_list<char>{\n        (tuples_detail::TaggedTupleLeaf<Tags>::pup(p), '0')...});\n  }\n\n  // C++17 Draft 23.5.3.1 Construction\n  template <bool Dummy = true, typename std::enable_if<args_constructor<\n                                   Dummy>::enable_default()>::type* = nullptr>\n  constexpr TaggedTuple() {}\n\n  TaggedTuple(TaggedTuple const& /*rhs*/) = default;\n  TaggedTuple(TaggedTuple&& /*rhs*/) = default;\n\n  /*!\n   * \\brief Construct a TaggedTuple with Args\n   * \\requires `std::is_convertible_v<Us, typename Tags::type>...` is `true`\n   *\n   * \\example\n   * \\snippet Test_TaggedTuple.cpp construction_example\n   */\n  template <class... Us,\n            typename std::enable_if<\n                args_constructor<not pack_is_TaggedTuple<Us...>::value and\n                                 sizeof...(Us) == sizeof...(Tags)>::\n                    template enable_explicit<Us...>()>::type* = nullptr>\n  constexpr explicit TaggedTuple(Us&&... us)\n      : tuples_detail::TaggedTupleLeaf<Tags>(\n            tuples_detail::forward<Us>(us))... {}\n\n  /*!\n   * \\brief Construct a TaggedTuple with Args\n   * \\requires `std::is_convertible_v<Us, typename Tags::type>...` is `true`\n   *\n   * \\example\n   * \\snippet Test_TaggedTuple.cpp construction_example\n   */\n  template <class... Us,\n            typename std::enable_if<\n                args_constructor<not pack_is_TaggedTuple<Us...>::value and\n                                 sizeof...(Us) == sizeof...(Tags)>::\n                    template enable_implicit<Us...>()>::type* = nullptr>\n  // clang-tidy: mark explicit\n  constexpr TaggedTuple(Us&&... us)\n      : tuples_detail::TaggedTupleLeaf<Tags>(\n            tuples_detail::forward<Us>(us))... {}\n\n  template <\n      class... UTags,\n      typename std::enable_if<\n          sizeof...(Tags) == sizeof...(UTags) and\n          tuples_detail::all<std::is_constructible<\n              tag_type<Tags>, const tag_type<UTags>&>::value...>::value and\n          not tuples_detail::all<std::is_same<Tags, UTags>::value...>::value>::\n          type* = nullptr>\n  constexpr explicit TaggedTuple(TaggedTuple<UTags...> const& t)\n      : tuples_detail::TaggedTupleLeaf<Tags>(get<UTags>(t))... {}\n\n  template <class... UTags,\n            typename std::enable_if<\n                sizeof...(Tags) == sizeof...(UTags) and\n                tuples_detail::all<std::is_constructible<\n                    tag_type<Tags>, tag_type<UTags>&&>::value...>::value and\n                not tuples_detail::all<std::is_same<Tags, UTags>::value...>::\n                    value>::type* = nullptr>\n  constexpr explicit TaggedTuple(TaggedTuple<UTags...>&& t)\n      : tuples_detail::TaggedTupleLeaf<Tags>(std::move(get<UTags>(t)))... {}\n\n  ~TaggedTuple() = default;\n\n  // C++17 Draft 23.5.3.2 Assignment\n  TaggedTuple& operator=(\n      tmpl::conditional_t<is_copy_assignable::value, TaggedTuple,\n                          tuples_detail::no_such_type> const& t) {\n    static_cast<void>(\n        tuples_detail::expand_pack((get<Tags>(*this) = get<Tags>(t))...));\n    return *this;\n  }\n\n  TaggedTuple& operator=(\n      tmpl::conditional_t<is_move_assignable::value, TaggedTuple,\n                          tuples_detail::no_such_type>&& t) {\n    static_cast<void>(tuples_detail::expand_pack(\n        (get<Tags>(*this) =\n             tuples_detail::forward<tag_type<Tags>>(get<Tags>(t)))...));\n    return *this;\n  }\n\n  template <class... UTags,\n            typename std::enable_if<\n                sizeof...(Tags) == sizeof...(UTags) and\n                tuples_detail::all<std::is_assignable<\n                    tag_type<Tags>&,\n                    tag_type<UTags> const&>::value...>::value>::type* = nullptr>\n  TaggedTuple& operator=(TaggedTuple<UTags...> const& t) {\n    static_cast<void>(\n        tuples_detail::expand_pack((get<Tags>(*this) = get<UTags>(t))...));\n    return *this;\n  }\n\n  template <\n      class... UTags,\n      typename std::enable_if<\n          sizeof...(Tags) == sizeof...(UTags) and\n          tuples_detail::all<std::is_assignable<\n              tag_type<Tags>&, tag_type<UTags>&&>::value...>::value>::type* =\n          nullptr>\n  TaggedTuple& operator=(TaggedTuple<UTags...>&& t) {\n    static_cast<void>(tuples_detail::expand_pack(\n        (get<Tags>(*this) =\n             tuples_detail::forward<tag_type<UTags>>(get<UTags>(t)))...));\n    return *this;\n  }\n\n  // C++17 Draft 23.5.3.3 swap\n  void swap(TaggedTuple& t) {\n    tuples_detail::expand_pack(tuples_detail::TaggedTupleLeaf<Tags>::swap(\n        static_cast<tuples_detail::TaggedTupleLeaf<Tags>&>(t))...);\n  }\n};\n\ntemplate <>\nclass TaggedTuple<> {\n public:\n  using tags_list = tmpl::list<>;\n  static constexpr size_t size() { return 0; }\n  TaggedTuple() = default;\n  void swap(TaggedTuple& /*unused*/) {}\n  // clang-tidy: runtime-references\n  void pup(PUP::er& /*p*/) {}  // NOLINT\n};\n\n// C++17 Draft 23.5.3.6 Tuple helper classes\ntemplate <class T>\nstruct tuple_size;\n\ntemplate <class... Tags>\nstruct tuple_size<TaggedTuple<Tags...>>\n    : std::integral_constant<size_t, sizeof...(Tags)> {};\ntemplate <class... Tags>\nstruct tuple_size<const TaggedTuple<Tags...>>\n    : tuple_size<TaggedTuple<Tags...>> {};\ntemplate <class... Tags>\nstruct tuple_size<volatile TaggedTuple<Tags...>>\n    : tuple_size<TaggedTuple<Tags...>> {};\ntemplate <class... Tags>\nstruct tuple_size<const volatile TaggedTuple<Tags...>>\n    : tuple_size<TaggedTuple<Tags...>> {};\n\n// C++17 Draft 23.5.3.7 Element access\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Retrieve the element of `Tag` in the TaggedTuple\n */\ntemplate <class Tag, class... Tags>\ninline constexpr const typename Tag::type& get(const TaggedTuple<Tags...>& t) {\n  static_assert(std::is_base_of<tuples_detail::TaggedTupleLeaf<Tag>,\n                                TaggedTuple<Tags...>>::value,\n                \"Could not retrieve Tag from TaggedTuple. See the first \"\n                \"template parameter of the instantiation for what Tag is being \"\n                \"retrieved and the remaining template parameters for what Tags \"\n                \"are available.\");\n  return static_cast<const tuples_detail::TaggedTupleLeaf<Tag>&>(t).get_data();\n}\ntemplate <class Tag, class... Tags>\ninline constexpr typename Tag::type& get(TaggedTuple<Tags...>& t) {\n  static_assert(std::is_base_of<tuples_detail::TaggedTupleLeaf<Tag>,\n                                TaggedTuple<Tags...>>::value,\n                \"Could not retrieve Tag from TaggedTuple. See the first \"\n                \"template parameter of the instantiation for what Tag is being \"\n                \"retrieved and the remaining template parameters for what Tags \"\n                \"are available.\");\n  return static_cast<tuples_detail::TaggedTupleLeaf<Tag>&>(t).get_data();\n}\ntemplate <class Tag, class... Tags>\ninline constexpr const typename Tag::type&& get(\n    const TaggedTuple<Tags...>&& t) {\n  static_assert(std::is_base_of<tuples_detail::TaggedTupleLeaf<Tag>,\n                                TaggedTuple<Tags...>>::value,\n                \"Could not retrieve Tag from TaggedTuple. See the first \"\n                \"template parameter of the instantiation for what Tag is being \"\n                \"retrieved and the remaining template parameters for what Tags \"\n                \"are available.\");\n  return static_cast<const typename Tag::type&&>(\n      static_cast<const tuples_detail::TaggedTupleLeaf<Tag>&&>(t).get_data());\n}\ntemplate <class Tag, class... Tags>\ninline constexpr typename Tag::type&& get(TaggedTuple<Tags...>&& t) {\n  static_assert(std::is_base_of<tuples_detail::TaggedTupleLeaf<Tag>,\n                                TaggedTuple<Tags...>>::value,\n                \"Could not retrieve Tag from TaggedTuple. See the first \"\n                \"template parameter of the instantiation for what Tag is being \"\n                \"retrieved and the remaining template parameters for what Tags \"\n                \"are available.\");\n  return static_cast<typename Tag::type&&>(\n      static_cast<tuples_detail::TaggedTupleLeaf<Tag>&&>(t).get_data());\n}\n/// @}\n\ntemplate <size_t I, class... Tags>\ninline constexpr typename tmpl::at_c<tmpl::list<Tags...>, I>::type&& get(\n    TaggedTuple<Tags...>&& t) {\n  return get<tmpl::at_c<tmpl::list<Tags...>, I>>(t);\n}\n\ntemplate <size_t I, class... Tags>\ninline constexpr const typename tmpl::at_c<tmpl::list<Tags...>, I>::type& get(\n    const TaggedTuple<Tags...>& t) {\n  return get<tmpl::at_c<tmpl::list<Tags...>, I>>(t);\n}\n\ntemplate <size_t I, class... Tags>\ninline constexpr typename tmpl::at_c<tmpl::list<Tags...>, I>::type& get(\n    TaggedTuple<Tags...>& t) {\n  return get<tmpl::at_c<tmpl::list<Tags...>, I>>(t);\n}\n\n// C++17 Draft 23.5.3.8 Relational operators\nnamespace tuples_detail {\nstruct equal {\n  template <class T, class U>\n  static TUPLES_LIB_CONSTEXPR_CXX_14 void apply(T const& lhs, U const& rhs,\n                                                bool* result) {\n    *result = *result and lhs == rhs;\n  }\n};\n\ntemplate <class... LTags, class... RTags>\nTUPLES_LIB_CONSTEXPR_CXX_14 bool tuple_equal_impl(\n    TaggedTuple<LTags...> const& lhs, TaggedTuple<RTags...> const& rhs) {\n  bool equal = true;\n  // This short circuits in the sense that the operator== is only evaluated if\n  // the result thus far is true\n  static_cast<void>(std::initializer_list<char>{\n      (equal::apply(get<LTags>(lhs), get<RTags>(rhs), &equal), '0')...});\n  return equal;\n}\n}  // namespace tuples_detail\n\ntemplate <class... LTags, class... RTags,\n          typename std::enable_if<sizeof...(LTags) == sizeof...(RTags)>::type* =\n              nullptr>\nTUPLES_LIB_CONSTEXPR_CXX_14 bool operator==(TaggedTuple<LTags...> const& lhs,\n                                            TaggedTuple<RTags...> const& rhs) {\n  return tuples_detail::tuple_equal_impl(lhs, rhs);\n}\n\ntemplate <class... LTags, class... RTags,\n          typename std::enable_if<sizeof...(LTags) == sizeof...(RTags)>::type* =\n              nullptr>\nTUPLES_LIB_CONSTEXPR_CXX_14 bool operator!=(TaggedTuple<LTags...> const& lhs,\n                                            TaggedTuple<RTags...> const& rhs) {\n  return not(lhs == rhs);\n}\n\nnamespace tuples_detail {\nstruct less {\n  template <class T, class U>\n  static TUPLES_LIB_CONSTEXPR_CXX_14 void apply(T const& lhs, U const& rhs,\n                                                bool* last_rhs_less_lhs,\n                                                bool* result) {\n    if (*result or *last_rhs_less_lhs) {\n      return;\n    }\n    *result = lhs < rhs;\n    if (*result) {\n      return;\n    }\n    *last_rhs_less_lhs = rhs < lhs;\n  }\n};\n\ntemplate <class... LTags, class... RTags>\nTUPLES_LIB_CONSTEXPR_CXX_14 bool tuple_less_impl(\n    TaggedTuple<LTags...> const& lhs, TaggedTuple<RTags...> const& rhs) {\n  bool result = false;\n  bool last_rhs_less_lhs = false;\n  static_cast<void>(\n      std::initializer_list<char>{(less::apply(get<LTags>(lhs), get<RTags>(rhs),\n                                               &last_rhs_less_lhs, &result),\n                                   '0')...});\n  return result;\n}\n}  // namespace tuples_detail\n\ntemplate <class... LTags, class... RTags,\n          typename std::enable_if<sizeof...(LTags) == sizeof...(RTags)>::type* =\n              nullptr>\nTUPLES_LIB_CONSTEXPR_CXX_14 bool operator<(TaggedTuple<LTags...> const& lhs,\n                                           TaggedTuple<RTags...> const& rhs) {\n  return tuples_detail::tuple_less_impl(lhs, rhs);\n}\n\ntemplate <class... LTags, class... RTags,\n          typename std::enable_if<sizeof...(LTags) == sizeof...(RTags)>::type* =\n              nullptr>\nTUPLES_LIB_CONSTEXPR_CXX_14 bool operator>(TaggedTuple<LTags...> const& lhs,\n                                           TaggedTuple<RTags...> const& rhs) {\n  return rhs < lhs;\n}\n\ntemplate <class... LTags, class... RTags,\n          typename std::enable_if<sizeof...(LTags) == sizeof...(RTags)>::type* =\n              nullptr>\nTUPLES_LIB_CONSTEXPR_CXX_14 bool operator<=(TaggedTuple<LTags...> const& lhs,\n                                            TaggedTuple<RTags...> const& rhs) {\n  return not(rhs < lhs);\n}\n\ntemplate <class... LTags, class... RTags,\n          typename std::enable_if<sizeof...(LTags) == sizeof...(RTags)>::type* =\n              nullptr>\nTUPLES_LIB_CONSTEXPR_CXX_14 bool operator>=(TaggedTuple<LTags...> const& lhs,\n                                            TaggedTuple<RTags...> const& rhs) {\n  return not(lhs < rhs);\n}\n\n// C++17 Draft 23.5.3.3 swap\ntemplate <\n    class... Tags,\n    typename std::enable_if<tuples_detail::all<tuples_detail::is_swappable_with<\n        tuples_detail::TaggedTupleLeaf<Tags>,\n        tuples_detail::TaggedTupleLeaf<Tags>>::value...>::value>::type* =\n        nullptr>\nvoid swap(TaggedTuple<Tags...>& lhs, TaggedTuple<Tags...>& rhs) {\n  lhs.swap(rhs);\n}\n\nnamespace TaggedTuple_detail {\ntemplate <typename T>\nstruct tagged_tuple_typelist_impl;\n\ntemplate <template <typename...> class List, typename... Tags>\nstruct tagged_tuple_typelist_impl<List<Tags...>> {\n  using type = TaggedTuple<Tags...>;\n};\n}  // namespace TaggedTuple_detail\n\n/// \\ingroup UtilitiesGroup\ntemplate <typename T>\nusing tagged_tuple_from_typelist =\n    typename TaggedTuple_detail::tagged_tuple_typelist_impl<T>::type;\n\nnamespace TaggedTuple_detail {\ntemplate <typename... InputTags, typename... OutputTags>\nTaggedTuple<OutputTags...> reorder_impl(TaggedTuple<InputTags...>&& input,\n                                        tmpl::list<OutputTags...> /*meta*/) {\n  static_assert(\n      std::is_same_v<tmpl::list_difference<tmpl::list<OutputTags...>,\n                                           tmpl::list<InputTags...>>,\n                     tmpl::list<>> and\n          std::is_same_v<tmpl::list_difference<tmpl::list<InputTags...>,\n                                               tmpl::list<OutputTags...>>,\n                         tmpl::list<>>,\n      \"The input and output TaggedTuples must be the same except \"\n      \"for ordering.\");\n  return TaggedTuple<OutputTags...>(std::move(get<OutputTags>(input))...);\n}\n}  // namespace TaggedTuple_detail\n\n/// Given an input TaggedTuple, produce an output TaggedTuple\n/// with the tags in a different order.  All tags must be the same\n/// except for ordering.\n/// \\example\n/// \\snippet Test_TaggedTuple.cpp reorder_example\ntemplate <typename ReturnedTaggedTuple, typename... Tags>\nReturnedTaggedTuple reorder(TaggedTuple<Tags...> input) {\n  return TaggedTuple_detail::reorder_impl(\n      std::move(input), typename ReturnedTaggedTuple::tags_list{});\n}\n\n/// Stream operator for TaggedTuple\nusing ::operator<<;\ntemplate <class... Tags>\nstd::ostream& operator<<(std::ostream& os, const TaggedTuple<Tags...>& t) {\n  os << \"TaggedTuple:\\n\";\n  const auto print_item = [&os, &t](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    using type = typename tag::type;\n    os << \"----------\\n\";\n    os << \"Name:  \" << pretty_type::get_name<tag>() << \"\\n\";\n    os << \"Type:  \" << pretty_type::get_name<type>() << \"\\n\";\n    os << \"Value: \";\n    print_value(os, get<tag>(t));\n    os << \"\\n\";\n  };\n  tmpl::for_each<tmpl::list<Tags...>>(print_item);\n  return os;\n}\n\nnamespace TaggedTuple_detail {\n\ntemplate <typename F, typename... Tags, typename... ApplyTags>\nconstexpr decltype(auto) apply_impl(F&& f, const TaggedTuple<Tags...>& t,\n                                    tmpl::list<ApplyTags...> /* meta */) {\n  return std::forward<F>(f)(get<ApplyTags>(t)...);\n}\n\n}  // namespace TaggedTuple_detail\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Invoke `f` with the `ApplyTags` taken from `t` expanded in a parameter\n * pack\n *\n * `ApplyTags` defaults to the full list of tags in `t`.\n *\n * Here is an example how to use the function:\n *\n * \\snippet Test_TaggedTuple.cpp expand_tuple_example\n *\n * This is the function being called in the above example:\n *\n * \\snippet Test_TaggedTuple.cpp expand_tuple_example_function\n *\n * \\see std::apply\n */\ntemplate <typename ApplyTags, typename F, typename... Tags>\nconstexpr decltype(auto) apply(F&& f, const TaggedTuple<Tags...>& t) {\n  return TaggedTuple_detail::apply_impl(std::forward<F>(f), t, ApplyTags{});\n}\n\ntemplate <typename F, typename... Tags>\nconstexpr decltype(auto) apply(F&& f, const TaggedTuple<Tags...>& t) {\n  return TaggedTuple_detail::apply_impl(\n      std::forward<F>(f), t, typename TaggedTuple<Tags...>::tags_list{});\n}\n/// @}\n\n/// @{\n/*!\n * Constructs a TaggedTuple that is a concatenation of two TaggedTuples.\n * \\example\n * \\snippet Test_TaggedTuple.cpp tagged_tuple_cat_example\n */\ntemplate <typename... Tags1, typename... Tags2>\ntuples::TaggedTuple<Tags1..., Tags2...> tagged_tuple_cat(\n    const tuples::TaggedTuple<Tags1...>& tuple1,\n    const tuples::TaggedTuple<Tags2...>& tuple2) {\n  return {get<Tags1>(tuple1)..., get<Tags2>(tuple2)...};\n}\n\ntemplate <typename... Tags1, typename... Tags2>\ntuples::TaggedTuple<Tags1..., Tags2...> tagged_tuple_cat(\n    tuples::TaggedTuple<Tags1...>&& tuple1,\n    tuples::TaggedTuple<Tags2...>&& tuple2) {\n  return {std::move(get<Tags1>(tuple1))..., std::move(get<Tags2>(tuple2))...};\n}\n/// @}\n\n}  // namespace tuples\n\nnamespace MakeWithValueImpls {\n/// \\brief Makes a `TaggedTuple`; each element of the `TaggedTuple`\n/// must be `make_with_value`-creatable from a `T`.\ntemplate <typename... Tags, typename T>\nstruct MakeWithValueImpl<tuples::TaggedTuple<Tags...>, T> {\n  template <typename ValueType>\n  static SPECTRE_ALWAYS_INLINE tuples::TaggedTuple<Tags...> apply(\n      const T& input, const ValueType value) {\n    return tuples::TaggedTuple<Tags...>(\n        make_with_value<typename Tags::type>(input, value)...);\n  }\n};\n\ntemplate <typename Tag, typename... Tags>\nstruct NumberOfPoints<tuples::TaggedTuple<Tag, Tags...>> {\n  static SPECTRE_ALWAYS_INLINE size_t apply(\n      const tuples::TaggedTuple<Tag, Tags...>& input) {\n    const size_t points = number_of_points(tuples::get<Tag>(input));\n    ASSERT((... and (number_of_points(tuples::get<Tags>(input)) == points)),\n           \"Inconsistent number of points in tuple entries.\");\n    return points;\n  }\n};\n}  // namespace MakeWithValueImpls\n\ntemplate <typename... Tags>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n    tuples::TaggedTuple<Tags...>> {\n  static constexpr bool is_trivial =\n      (... and SetNumberOfGridPointsImpl<typename Tags::type>::is_trivial);\n  static void apply(const gsl::not_null<tuples::TaggedTuple<Tags...>*> result,\n                    const size_t size) {\n    expand_pack((set_number_of_grid_points(\n                     make_not_null(&tuples::get<Tags>(*result)), size),\n                 0)...);\n  }\n};\n\nnamespace std {\ntemplate <typename... Tags>\nstruct tuple_size<tuples::TaggedTuple<Tags...>>\n    : std::integral_constant<int, sizeof...(Tags)> {};\ntemplate <size_t I, typename... Tags>\nstruct tuple_element<I, tuples::TaggedTuple<Tags...>> {\n  using type = typename tmpl::at_c<tmpl::list<Tags...>, I>::type;\n};\n}  // namespace std\n"
  },
  {
    "path": "src/DataStructures/TaggedVariant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <type_traits>\n#include <utility>\n#include <variant>\n\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Options {\ntemplate <typename... AlternativeLists>\nstruct Alternatives;\n}  // namespace Options\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup DataStructuresGroup\n///\n/// TaggedVariant and related functionality.\nnamespace variants {\nnamespace TaggedVariant_detail {\ntemplate <typename R, typename Variant, typename Tag, typename Visitor>\nstruct VisitAlternative;\n}  // namespace TaggedVariant_detail\n\n/// \\cond\ntemplate <typename... Tags>\nclass TaggedVariant;\ntemplate <typename... Tags>\nconstexpr bool operator==(const TaggedVariant<Tags...>& a,\n                          const TaggedVariant<Tags...>& b);\ntemplate <typename... Tags>\nconstexpr bool operator<(const TaggedVariant<Tags...>& a,\n                         const TaggedVariant<Tags...>& b);\n/// \\endcond\n\n/// \\ingroup DataStructuresGroup\n///\n/// A class similar to `std::variant`, but indexed by tag structs.\n///\n/// \\see variants::get, variants::get_if, variants::holds_alternative,\n/// variants::visit\ntemplate <typename... Tags>\nclass TaggedVariant {\n private:\n  static_assert(sizeof...(Tags) > 0);\n  static_assert(\n      std::is_same_v<tmpl::remove_duplicates<TaggedVariant>, TaggedVariant>,\n      \"TaggedVariant cannot have duplicate tags.\");\n\n  template <typename Tag>\n  static constexpr size_t data_index =\n      tmpl::index_of<TaggedVariant, Tag>::value;\n\n public:\n  /// A default constructed instance has the first tag active.\n  TaggedVariant() = default;\n  TaggedVariant(const TaggedVariant&) = default;\n  TaggedVariant(TaggedVariant&&) = default;\n  TaggedVariant& operator=(const TaggedVariant&) = default;\n  TaggedVariant& operator=(TaggedVariant&&) = default;\n  ~TaggedVariant() = default;\n\n  /// Construct with \\p Tag active, using \\p args to construct the\n  /// contained object.\n  ///\n  /// \\snippet DataStructures/Test_TaggedVariant.cpp construct in_place_type\n  template <\n      typename Tag, typename... Args,\n      Requires<(... or std::is_same_v<Tag, Tags>) and\n               std::is_constructible_v<typename Tag::type, Args...>> = nullptr>\n  constexpr explicit TaggedVariant(std::in_place_type_t<Tag> /*meta*/,\n                                   Args&&... args)\n      : data_(std::in_place_index<data_index<Tag>>,\n              std::forward<Args>(args)...) {}\n\n  /// Construct the contained object from \\p args.  Only available if\n  /// the TaggedVariant only has one tag.\n  ///\n  /// \\snippet DataStructures/Test_TaggedVariant.cpp construct single\n  template <typename... Args,\n            Requires<sizeof...(Tags) == 1 and\n                     std::is_constructible_v<\n                         typename tmpl::front<TaggedVariant>::type, Args...>> =\n                nullptr>\n  constexpr explicit TaggedVariant(Args&&... args)\n      : TaggedVariant(std::in_place_type<tmpl::front<TaggedVariant>>,\n                      std::forward<Args>(args)...) {}\n\n  /// A TaggedVariant can be implicitly move-converted to another\n  /// variant with a superset of the tags.\n  ///\n  /// \\snippet DataStructures/Test_TaggedVariant.cpp convert\n  /// @{\n  template <typename... OtherTags,\n            Requires<tmpl::size<tmpl::list_difference<\n                         TaggedVariant<OtherTags...>, TaggedVariant>>::value ==\n                     0> = nullptr>\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  constexpr TaggedVariant(TaggedVariant<OtherTags...>&& other)\n      : TaggedVariant(visit(\n            []<typename Tag>(\n                std::pair<tmpl::type_<Tag>, typename Tag::type&&> entry) {\n              return TaggedVariant(std::in_place_type<Tag>,\n                                   std::move(entry.second));\n            },\n            std::move(other))) {}\n\n  template <typename... OtherTags,\n            Requires<tmpl::size<tmpl::list_difference<\n                         TaggedVariant<OtherTags...>, TaggedVariant>>::value ==\n                     0> = nullptr>\n  constexpr TaggedVariant& operator=(TaggedVariant<OtherTags...>&& other) {\n    visit(\n        [&]<typename Tag>(\n            std::pair<tmpl::type_<Tag>, typename Tag::type&&> entry) {\n          emplace<Tag>(std::move(entry.second));\n        },\n        std::move(other));\n    return *this;\n  }\n  /// @}\n\n  /// The index into the `Tags...` of the active object.\n  constexpr size_t index() const { return data_.index(); }\n\n  /// See `std::variant::valueless_by_exception`.\n  constexpr bool valueless_by_exception() const {\n    return data_.valueless_by_exception();\n  }\n\n  /// Destroys the contained object and actives \\p Tag, constructing a\n  /// new value from \\p args.\n  ///\n  /// \\snippet DataStructures/Test_TaggedVariant.cpp emplace\n  template <\n      typename Tag, typename... Args,\n      Requires<(... or std::is_same_v<Tag, Tags>) and\n               std::is_constructible_v<typename Tag::type, Args...>> = nullptr>\n  constexpr typename Tag::type& emplace(Args&&... args) {\n    return data_.template emplace<data_index<Tag>>(std::forward<Args>(args)...);\n  }\n\n  constexpr void swap(TaggedVariant& other) noexcept(noexcept(\n      (... and (std::is_nothrow_move_constructible_v<typename Tags::type> and\n                std::is_nothrow_swappable_v<typename Tags::type>)))) {\n    data_.swap(other.data_);\n  }\n\n  void pup(PUP::er& p) { p | data_; }\n\n  /// A TaggedVariant over option tags can be parsed as any of them.\n  /// @{\n  static constexpr Options::String help = \"One of multiple options\";\n  using options = tmpl::list<Options::Alternatives<tmpl::list<Tags>...>>;\n  template <typename Tag>\n  explicit TaggedVariant(tmpl::list<Tag> /*meta*/, typename Tag::type value)\n      : TaggedVariant(std::in_place_type<Tag>, std::move(value)) {}\n  /// @}\n\n private:\n  template <typename R, typename Variant, typename Tag, typename Visitor>\n  friend struct TaggedVariant_detail::VisitAlternative;\n\n  template <typename Tag, typename... Tags2>\n  friend constexpr typename Tag::type& get(TaggedVariant<Tags2...>& variant);\n  template <typename Tag, typename... Tags2>\n  friend constexpr const typename Tag::type& get(\n      const TaggedVariant<Tags2...>& variant);\n  template <typename Tag, typename... Tags2>\n  friend constexpr typename Tag::type&& get(TaggedVariant<Tags2...>&& variant);\n  template <typename Tag, typename... Tags2>\n  friend constexpr const typename Tag::type&& get(\n      const TaggedVariant<Tags2...>&& variant);\n\n  friend constexpr bool operator== <Tags...>(const TaggedVariant<Tags...>& a,\n                                             const TaggedVariant<Tags...>& b);\n  friend constexpr bool operator< <Tags...>(const TaggedVariant<Tags...>& a,\n                                            const TaggedVariant<Tags...>& b);\n\n  friend struct std::hash<TaggedVariant>;\n\n  std::variant<typename Tags::type...> data_;\n};\n\nnamespace TaggedVariant_detail {\ntemplate <typename... Tags>\nconstexpr bool is_variant_or_derived(const TaggedVariant<Tags...>* /*meta*/) {\n  return true;\n}\n// NOLINTNEXTLINE(cert-dcl50-cpp) - variadic function\nconstexpr bool is_variant_or_derived(...) { return false; }\n\ntemplate <typename Tag, typename Value>\nconstexpr std::pair<tmpl::type_<Tag>, Value&&> make_visitor_pair(\n    Value&& value) {\n  return {tmpl::type_<Tag>{}, std::forward<Value>(value)};\n}\n\nstruct DeduceReturn;\n\ntemplate <typename R, typename Variant, typename Tag, typename Visitor>\nstruct VisitAlternative {\n  static constexpr R apply(Variant&& variant, const Visitor& visitor) {\n    return std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(\n        get<std::decay_t<Variant>::template data_index<Tag>>(\n            std::forward<Variant>(variant).data_)));\n  }\n};\n\ntemplate <typename Variant, typename Tag, typename Visitor>\nstruct VisitAlternative<void, Variant, Tag, Visitor> {\n  static constexpr void apply(Variant&& variant, const Visitor& visitor) {\n    std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(\n        get<std::decay_t<Variant>::template data_index<Tag>>(\n            std::forward<Variant>(variant).data_)));\n  }\n};\n\ntemplate <typename Variant, typename Tag, typename Visitor>\nstruct VisitAlternative<DeduceReturn, Variant, Tag, Visitor> {\n  static constexpr decltype(auto) apply(Variant&& variant,\n                                        const Visitor& visitor) {\n    return std::forward<Visitor>(visitor)(make_visitor_pair<Tag>(\n        get<std::decay_t<Variant>::template data_index<Tag>>(\n            std::forward<Variant>(variant).data_)));\n  }\n};\n\ntemplate <typename... Tags>\nconstexpr TaggedVariant<Tags...>& as_variant(TaggedVariant<Tags...>& variant) {\n  return variant;\n}\ntemplate <typename... Tags>\nconstexpr const TaggedVariant<Tags...>& as_variant(\n    const TaggedVariant<Tags...>& variant) {\n  return variant;\n}\ntemplate <typename... Tags>\nconstexpr TaggedVariant<Tags...>&& as_variant(\n    TaggedVariant<Tags...>&& variant) {\n  return std::move(variant);\n}\ntemplate <typename... Tags>\nconstexpr const TaggedVariant<Tags...>&& as_variant(\n    const TaggedVariant<Tags...>&& variant) {\n  return std::move(variant);\n}\n\ntemplate <typename R, typename Visitor, typename... Variants>\nconstexpr decltype(auto) visit_impl(Visitor&& visitor) {\n  return std::forward<Visitor>(visitor)();\n}\n\ntemplate <typename R, typename Variant, typename Visitor,\n          typename DecayedVariant = std::decay_t<Variant>>\nstruct VisitJumpTable;\n\ntemplate <typename R, typename Variant, typename Visitor, typename... Tags>\nstruct VisitJumpTable<R, Variant, Visitor, TaggedVariant<Tags...>> {\n  static constexpr std::array value{\n      VisitAlternative<R, Variant, Tags, Visitor>::apply...};\n};\n\ntemplate <typename R, typename Visitor, typename FirstVariant,\n          typename... Variants>\nconstexpr decltype(auto) visit_impl(Visitor&& visitor,\n                                    FirstVariant&& first_variant,\n                                    Variants&&... variants) {\n  const auto recurse = [&]<typename Arg>(Arg&& first_arg) {\n    return visit_impl<R>(\n        [&]<typename... Rest>(Rest&&... rest) {\n          return std::forward<Visitor>(visitor)(std::forward<Arg>(first_arg),\n                                                std::forward<Rest>(rest)...);\n        },\n        std::forward<Variants>(variants)...);\n  };\n  if (UNLIKELY(first_variant.valueless_by_exception())) {\n    throw std::bad_variant_access{};\n  }\n  return gsl::at(VisitJumpTable<R, FirstVariant, decltype(recurse)>::value,\n                 first_variant.index())(\n      std::forward<FirstVariant>(first_variant), recurse);\n}\n}  // namespace TaggedVariant_detail\n\n/// Call \\p visitor with the contents of one or more variants.\n///\n/// Calls \\p visitor with the contents of each variant as arguments,\n/// passed as `std::pair<tmpl::type_<Tag>, typename Tag::type ref>`,\n/// where `Tag` is the active tag of the variant and `ref` is a\n/// reference qualifier matching that of the passed variant.\n///\n/// If the template parameter \\p R is supplied, the result is\n/// implicitly converted to that type (which may be `void`).\n/// Otherwise it is deduced from the return type of \\p visitor, which\n/// must be the same for all tags in the variant.\n///\n/// \\warning Unlike `visit` for `std::variant`, the types of the\n/// visitor arguments do not allow for implicit conversions between\n/// reference types.  If the visitor expects, for example,\n/// `std::pair<tmpl::type_<Tag>, const typename Tag::type&>`, the caller must\n/// ensure that the passed variant is a const lvalue.\n///\n/// \\snippet DataStructures/Test_TaggedVariant.cpp visit\n/// @{\ntemplate <typename Visitor, typename... Variants,\n          Requires<(... and TaggedVariant_detail::is_variant_or_derived(\n                                std::add_pointer_t<std::remove_reference_t<\n                                    Variants>>{}))> = nullptr>\nconstexpr decltype(auto) visit(Visitor&& visitor, Variants&&... variants) {\n  return TaggedVariant_detail::visit_impl<TaggedVariant_detail::DeduceReturn>(\n      visitor,\n      TaggedVariant_detail::as_variant(std::forward<Variants>(variants))...);\n}\n\ntemplate <typename R, typename Visitor, typename... Variants,\n          Requires<(... and TaggedVariant_detail::is_variant_or_derived(\n                                std::add_pointer_t<std::remove_reference_t<\n                                    Variants>>{}))> = nullptr>\nconstexpr R visit(Visitor&& visitor, Variants&&... variants) {\n  return TaggedVariant_detail::visit_impl<R>(\n      visitor,\n      TaggedVariant_detail::as_variant(std::forward<Variants>(variants))...);\n}\n/// @}\n\n/// Check whether \\p Tag is active.\ntemplate <typename Tag, typename... Tags>\nconstexpr bool holds_alternative(const TaggedVariant<Tags...>& variant) {\n  return variant.index() == tmpl::index_of<TaggedVariant<Tags...>, Tag>::value;\n}\n\n/// Access the contained object.  Throws `std::bad_variant_access` if\n/// \\p Tag is not active.\n/// @{\ntemplate <typename Tag, typename... Tags>\nconstexpr typename Tag::type& get(TaggedVariant<Tags...>& variant) {\n  return get<TaggedVariant<Tags...>::template data_index<Tag>>(variant.data_);\n}\ntemplate <typename Tag, typename... Tags>\nconstexpr const typename Tag::type& get(const TaggedVariant<Tags...>& variant) {\n  return get<TaggedVariant<Tags...>::template data_index<Tag>>(variant.data_);\n}\ntemplate <typename Tag, typename... Tags>\nconstexpr typename Tag::type&& get(TaggedVariant<Tags...>&& variant) {\n  return get<TaggedVariant<Tags...>::template data_index<Tag>>(\n      std::move(variant.data_));\n}\ntemplate <typename Tag, typename... Tags>\nconstexpr const typename Tag::type&& get(\n    const TaggedVariant<Tags...>&& variant) {\n  return get<TaggedVariant<Tags...>::template data_index<Tag>>(\n      std::move(variant.data_));\n}\n/// @}\n\n/// Returns a pointer to the contained object if \\p variant is a\n/// non-null pointer and \\p Tag is active.  Otherwise, returns\n/// `nullptr`.\n/// @{\ntemplate <typename Tag, typename... Tags>\nconstexpr const typename Tag::type* get_if(\n    const TaggedVariant<Tags...>* variant) {\n  if (variant != nullptr and holds_alternative<Tag>(*variant)) {\n    return &get<Tag>(*variant);\n  } else {\n    return nullptr;\n  }\n}\ntemplate <typename Tag, typename... Tags>\nconstexpr typename Tag::type* get_if(TaggedVariant<Tags...>* variant) {\n  if (variant != nullptr and holds_alternative<Tag>(*variant)) {\n    return &get<Tag>(*variant);\n  } else {\n    return nullptr;\n  }\n}\n/// @}\n\ntemplate <typename... Tags>\nconstexpr bool operator==(const TaggedVariant<Tags...>& a,\n                          const TaggedVariant<Tags...>& b) {\n  return a.data_ == b.data_;\n}\ntemplate <typename... Tags>\nconstexpr bool operator!=(const TaggedVariant<Tags...>& a,\n                          const TaggedVariant<Tags...>& b) {\n  return not(a == b);\n}\ntemplate <typename... Tags>\nconstexpr bool operator<(const TaggedVariant<Tags...>& a,\n                         const TaggedVariant<Tags...>& b) {\n  return a.data_ < b.data_;\n}\ntemplate <typename... Tags>\nconstexpr bool operator>(const TaggedVariant<Tags...>& a,\n                         const TaggedVariant<Tags...>& b) {\n  return b < a;\n}\ntemplate <typename... Tags>\nconstexpr bool operator<=(const TaggedVariant<Tags...>& a,\n                          const TaggedVariant<Tags...>& b) {\n  return not(b < a);\n}\ntemplate <typename... Tags>\nconstexpr bool operator>=(const TaggedVariant<Tags...>& a,\n                          const TaggedVariant<Tags...>& b) {\n  return not(a < b);\n}\n\n/// Comparison operators against single-tag variants.  Primarily useful\n/// in tests.\n/// @{\ntemplate <typename... Tags, typename Tag,\n          Requires<(sizeof...(Tags) > 1 and\n                    (... or std::is_same_v<Tags, Tag>))> = nullptr>\nconstexpr bool operator==(const TaggedVariant<Tags...>& a,\n                          const TaggedVariant<Tag>& b) {\n  return holds_alternative<Tag>(a) and get<Tag>(a) == get<Tag>(b);\n}\ntemplate <typename... Tags, typename Tag,\n          Requires<(sizeof...(Tags) > 1 and\n                    (... or std::is_same_v<Tags, Tag>))> = nullptr>\nconstexpr bool operator==(const TaggedVariant<Tag>& a,\n                          const TaggedVariant<Tags...>& b) {\n  return b == a;\n}\ntemplate <typename... Tags, typename Tag,\n          Requires<(sizeof...(Tags) > 1 and\n                    (... or std::is_same_v<Tags, Tag>))> = nullptr>\nconstexpr bool operator!=(const TaggedVariant<Tags...>& a,\n                          const TaggedVariant<Tag>& b) {\n  return not(a == b);\n}\ntemplate <typename... Tags, typename Tag,\n          Requires<(sizeof...(Tags) > 1 and\n                    (... or std::is_same_v<Tags, Tag>))> = nullptr>\nconstexpr bool operator!=(const TaggedVariant<Tag>& a,\n                          const TaggedVariant<Tags...>& b) {\n  return not(a == b);\n}\n/// @}\n\ntemplate <\n    typename... Tags,\n    Requires<(... and (std::is_move_constructible_v<typename Tags::type> and\n                       std::is_swappable_v<typename Tags::type>))> = nullptr>\nconstexpr void swap(TaggedVariant<Tags...>& a,\n                    TaggedVariant<Tags...>& b) noexcept(noexcept(a.swap(b))) {\n  a.swap(b);\n}\n}  // namespace variants\n\nnamespace std {\ntemplate <typename... Tags>\n// https://github.com/llvm/llvm-project/issues/45454\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nstruct hash<::variants::TaggedVariant<Tags...>> {\n  size_t operator()(const ::variants::TaggedVariant<Tags...>& variant) const {\n    return std::hash<decltype(variant.data_)>{}(variant.data_);\n  }\n};\n}  // namespace std\n"
  },
  {
    "path": "src/DataStructures/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  TempTensor.hpp\n  )\n"
  },
  {
    "path": "src/DataStructures/Tags/TempTensor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\n/*!\n * \\brief Contains objects related to Tags\n */\nnamespace Tags {\ntemplate <size_t N, typename T>\nstruct TempTensor : db::SimpleTag {\n  using type = T;\n  static std::string name() {\n    return std::string(\"TempTensor\") + std::to_string(N);\n  }\n};\n\n/*!\n * \\brief A struct that constructs a `TempTensor` given a label \\p N and type\n * \\p TensorType. Call with `make_temp_tensor<N>::apply<Type>`\n *\n * \\see convert_to_temp_tensors\n */\ntemplate <size_t N>\nstruct make_temp_tensor {\n  template <typename TensorType>\n  struct apply {\n    using type = ::Tags::TempTensor<N, TensorType>;\n  };\n};\n\n/*!\n * \\brief Takes a `tmpl::list` of `Tensor` types and a label \\p N which is\n * applied to all `TempTensor`s in the resulting list.\n *\n * \\see make_temp_tensor\n */\ntemplate <typename List, size_t N>\nusing convert_to_temp_tensors =\n    tmpl::transform<List,\n                    typename make_temp_tensor<N>::template apply<tmpl::_1>>;\n\n/// @{\n/// \\ingroup PeoGroup\n/// Variables Tags for temporary tensors inside a function.\ntemplate <size_t N, typename DataType = DataVector>\nusing TempScalar = TempTensor<N, Scalar<DataType>>;\n\n// Rank 1\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempa = TempTensor<N, tnsr::a<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempA = TempTensor<N, tnsr::A<DataType, SpatialDim, Fr>>;\n\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempi = TempTensor<N, tnsr::i<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempI = TempTensor<N, tnsr::I<DataType, SpatialDim, Fr>>;\n\n// Rank 2\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempab = TempTensor<N, tnsr::ab<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempaB = TempTensor<N, tnsr::aB<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempAb = TempTensor<N, tnsr::Ab<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempAB = TempTensor<N, tnsr::AB<DataType, SpatialDim, Fr>>;\n\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempij = TempTensor<N, tnsr::ij<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempiJ = TempTensor<N, tnsr::iJ<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempIj = TempTensor<N, tnsr::Ij<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempIJ = TempTensor<N, tnsr::IJ<DataType, SpatialDim, Fr>>;\n\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempia = TempTensor<N, tnsr::ia<DataType, SpatialDim, Fr>>;\n\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempaa = TempTensor<N, tnsr::aa<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempAA = TempTensor<N, tnsr::AA<DataType, SpatialDim, Fr>>;\n\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempii = TempTensor<N, tnsr::ii<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempII = TempTensor<N, tnsr::II<DataType, SpatialDim, Fr>>;\n\n// Rank 3\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempijj = TempTensor<N, tnsr::ijj<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempIjj = TempTensor<N, tnsr::Ijj<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempijk = TempTensor<N, tnsr::ijk<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempiJk = TempTensor<N, tnsr::iJk<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempijK = TempTensor<N, tnsr::ijK<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempiii = TempTensor<N, tnsr::iii<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempiaa = TempTensor<N, tnsr::iaa<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempIaa = TempTensor<N, tnsr::Iaa<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempiaB = TempTensor<N, tnsr::iaB<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempabb = TempTensor<N, tnsr::abb<DataType, SpatialDim, Fr>>;\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing TempabC = TempTensor<N, tnsr::abC<DataType, SpatialDim, Fr>>;\n\n// Rank 4\ntemplate <size_t N, size_t SpatialDim, typename Fr = Frame::Inertial,\n          typename DataType = DataVector>\nusing Tempijaa = TempTensor<N, tnsr::ijaa<DataType, SpatialDim, Fr>>;\n/// @}\n}  // namespace Tags\n"
  },
  {
    "path": "src/DataStructures/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nclass ModalVector;\nclass ComplexDataVector;\nclass ComplexModalVector;\n/// \\endcond\n\nnamespace Tags {\n/// Given the `Tag` holding a `Tensor<DataVector, ...>`, swap the\n/// `DataVector` with a `double`.\ntemplate <typename Tag>\nstruct Mean : db::SimpleTag, db::PrefixTag {\n  using tag = Tag;\n  static_assert(std::is_same_v<typename Tag::type::type, DataVector>,\n                \"The Mean tag should only be used on tags that hold \"\n                \"Tensors of DataVectors\");\n  using type = TensorMetafunctions::swap_type<double, typename Tag::type>;\n};\n\n/// Given the `NodalTag` holding a `Tensor<DataVector, ...>`, swap the\n/// `DataVector` with a `ModalVector`.\ntemplate <typename NodalTag>\nstruct Modal : db::SimpleTag, db::PrefixTag {\n  using tag = NodalTag;\n  static_assert(std::is_same_v<typename NodalTag::type::type, DataVector>,\n                \"The Modal tag should only be used on tags that hold \"\n                \"Tensors of DataVectors\");\n  using type =\n      TensorMetafunctions::swap_type<ModalVector, typename NodalTag::type>;\n};\n\n/// Given a Tag with a `type` of `Tensor<VectorType, ...>`, acts as a new\n/// version of the tag with `type` of `Tensor<SpinWeighted<VectorType,\n/// SpinConstant::value>, ...>`, which is the preferred tensor type associated\n/// with spin-weighted quantities. Here, `SpinConstant` must be a\n/// `std::integral_constant` or similar type wrapper for a compile-time\n/// constant, in order to work properly with DataBox utilities.\n/// \\note There are restrictions of which vector types can be wrapped\n/// by SpinWeighted in a Tensor, so some `Tag`s that have valid `type`s may\n/// give rise to compilation errors when used as `Tags::SpinWeighted<Tag,\n/// SpinConstant>`. If you find such trouble, consult the whitelist of possible\n/// Tensor storage types in `Tensor.hpp`.\ntemplate <typename Tag, typename SpinConstant>\nstruct SpinWeighted : db::PrefixTag, db::SimpleTag {\n  static_assert(not is_any_spin_weighted_v<typename Tag::type::type>,\n                \"The SpinWeighted tag should only be used to create a \"\n                \"spin-weighted version of a non-spin-weighted tag. The \"\n                \"provided tag already has a spin-weighted type\");\n  using type = TensorMetafunctions::swap_type<\n      ::SpinWeighted<typename Tag::type::type, SpinConstant::value>,\n      typename Tag::type>;\n  using tag = Tag;\n  static std::string name() {\n    return \"SpinWeighted(\" + db::tag_name<Tag>() + \", \" +\n           std::to_string(SpinConstant::value) + \")\";\n  }\n};\n\n/// Succinct alias for often-used nodal spin-weighted temp tag\ntemplate <size_t index, int spin>\nusing TempSpinWeightedScalar =\n    SpinWeighted<TempScalar<index, ComplexDataVector>,\n                 std::integral_constant<int, spin>>;\n\n/// Succinct alias for often-used modal spin-weighted temp tag\ntemplate <size_t index, int spin>\nusing ModalTempSpinWeightedScalar =\n    SpinWeighted<TempScalar<index, ComplexModalVector>,\n                 std::integral_constant<int, spin>>;\n\nnamespace detail {\ntemplate <typename LhsType, typename RhsType>\nusing product_t =\n    Scalar<::SpinWeighted<ComplexDataVector,\n                          LhsType::type::spin + RhsType::type::spin>>;\n}  // namespace detail\n\n/// A prefix tag representing the product of two other tags. Note that if\n/// non-spin-weighted types are needed in this tag, the type alias\n/// `detail::product_t` should be generalized to give a reasonable type for\n/// those cases, or template specializations should be constructed for this tag.\ntemplate <typename LhsTag, typename RhsTag>\nstruct Multiplies : db::PrefixTag, db::SimpleTag {\n  using type = detail::product_t<typename LhsTag::type, typename RhsTag::type>;\n  using tag = LhsTag;\n  static std::string name() {\n    return \"Multiplies(\" + db::tag_name<LhsTag>() + \", \" +\n           db::tag_name<RhsTag>() + \")\";\n  }\n};\n\n}  // namespace Tags\n"
  },
  {
    "path": "src/DataStructures/TempBuffer.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A TempBuffer holds a set of `Tensor<DataType>`s, where\n * DataType is either a `DataVector` (or similar type) or a\n * fundamental type, in a way that minimizes allocations.\n *\n * The user gets references to Tensors inside of the TempBuffer using,\n * e.g., `auto& variable = get<Tag>(temp_buffer)`, where `Tag` is one\n * of the Tags in the `TagList`.\n *\n * If DataType is a DataVector or similar, than TempBuffer is a\n * Variables.  If DataType is a fundamental type, then TempBuffer is a\n * TaggedTuple.\n *\n */\ntemplate <typename TagList,\n          bool is_fundamental = std::is_fundamental_v<\n              typename tmpl::front<TagList>::type::value_type>>\nstruct TempBuffer;\n\ntemplate <typename TagList>\nstruct TempBuffer<TagList, true> : tuples::tagged_tuple_from_typelist<TagList> {\n  explicit TempBuffer(const size_t /*size*/)\n      : tuples::tagged_tuple_from_typelist<TagList>::TaggedTuple() {}\n\n  static size_t number_of_grid_points() { return 1; }\n};\n\ntemplate <typename TagList>\nstruct TempBuffer<TagList, false> : Variables<TagList> {\n  using Variables<TagList>::Variables;\n};\n"
  },
  {
    "path": "src/DataStructures/Tensor/AtIndex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <concepts>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Kokkos/KokkosCore.hpp\"\n\n/*!\n * \\brief Get the `Tensor` at a specific grid point index.\n *\n * Works with a `Tensor` of `Kokkos::View`s. Can also be made to work with\n * `DataVector` if needed (use `operator[]` instead of `operator()`).\n */\ntemplate <\n    typename TensorType, std::integral... Is,\n    typename ValueType = typename TensorType::type::value_type,\n    typename ResultType = TensorMetafunctions::swap_type<ValueType, TensorType>>\nKOKKOS_FUNCTION ResultType make_at_index(const TensorType& tensor,\n                                         const Is&... i) {\n  ResultType result{};\n  for (size_t component = 0; component < TensorType::size(); ++component) {\n    result[component] = tensor[component](i...);\n  }\n  return result;\n}\n\n/*!\n * \\brief Set the `Tensor` at a specific grid point index.\n *\n * Works with a `Tensor` of `Kokkos::View`s. Can also be made to work with\n * `DataVector` if needed (use `operator[]` instead of `operator()`).\n */\ntemplate <typename TensorType, std::integral... Is,\n          typename ValueType = typename TensorType::type::value_type>\nKOKKOS_FUNCTION void set_at_index(\n    const gsl::not_null<TensorType*> tensor,\n    const TensorMetafunctions::swap_type<ValueType, TensorType>& value,\n    const Is&... i) {\n  for (size_t component = 0; component < value.size(); ++component) {\n    (*tensor)[component](i...) = value[component];\n  }\n}\n\nnamespace Tags {\n\n/*!\n * \\brief Tag representing a specific grid point index of a tensor.\n *\n * This tag replaces the `Tensor` data type with `double` so that it can be\n * used with pointwise operations, e.g. in a `Kokkos::parallel_for` kernel.\n */\ntemplate <typename Tag>\nstruct AtIndex : db::PrefixTag {\n private:\n  using TensorType = typename Tag::type;\n  using ValueType = typename TensorType::type::value_type;\n\n public:\n  using tag = Tag;\n  using type = TensorMetafunctions::swap_type<ValueType, TensorType>;\n};\n\n}  // namespace Tags\n"
  },
  {
    "path": "src/DataStructures/Tensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AtIndex.hpp\n  CombineSpacetimeView.hpp\n  ContractFirstNIndices.hpp\n  Identity.hpp\n  IndexType.hpp\n  Metafunctions.hpp\n  Slice.hpp\n  Structure.hpp\n  Symmetry.hpp\n  Tensor.hpp\n  TypeAliases.hpp\n  )\n\nadd_subdirectory(EagerMath)\nadd_subdirectory(Expressions)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/DataStructures/Tensor/CombineSpacetimeView.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup TensorGroup\n/// \\brief Combines a time component of a tensor with spatial components to\n/// produce a spacetime tensor.\n///\n/// \\details Combines a time component of a tensor with spatial components to\n/// produce a spacetime tensor. Specifically, the components of the result\n/// are views to the inputs. Can do so for a tensor of any rank, but\n/// requires that the new index is the first index of the resulting tensor,\n/// replacing the position of the spatial index in the input spatial tensor.\n/// For instance, it may combine \\f$ \\phi \\f$ with \\f$ A^i \\f$ into\n/// \\f$ A^a = \\left(\\phi, A^i\\right) \\f$, or it may combine \\f$ A^a{}_b{}_c \\f$\n/// with \\f$ B_i{}^a{}_b{}_c \\f$ into\n/// \\f$ C_a{}^b{}_c{}_d = \\left(A^b{}_c{}_d, B_i{}^b{}_c{}_d\\right)\\f$,\n/// but it may not combine \\f$ A^i{}_a \\f$ with \\f$ B^i{}_j{}_a \\f$ to produce\n/// a tensor of the form \\f$ C^i{}_a{}_b \\f$.\n///\n/// \\tparam SpatialDim the number of spatial dimensions in the input and output\n///         tensors\n/// \\tparam Ul whether the new index is covariant or contravariant (must match\n///         that of the spatial index of the input spatial tensor)\n/// \\tparam Frame the frame of the new spacetime index (must match that of the\n///         spatial index of the input spatial tensor)\ntemplate <size_t SpatialDim, UpLo Ul, typename Frame, typename DataType,\n          typename SymmList, typename IndexList>\nvoid combine_spacetime_view(\n    gsl::not_null<TensorMetafunctions::prepend_spacetime_index<\n        Tensor<DataType, SymmList, IndexList>, SpatialDim, Ul, Frame>*>\n        spacetime_tensor,\n    const Tensor<DataType, SymmList, IndexList>& time_tensor,\n    const TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, SpatialDim, Ul, Frame>&\n        spatial_tensor) {\n  for (size_t storage_index = 0;\n       storage_index < Tensor<DataVector, SymmList, IndexList>::size();\n       ++storage_index) {\n    const auto u_multi_index =\n        Tensor<DataVector, SymmList,\n               IndexList>::structure::get_canonical_tensor_index(storage_index);\n    if constexpr (std::is_same_v<DataType, DataVector>) {\n      const auto dtu_multi_index = prepend(u_multi_index, 0_st);\n      make_const_view(\n          make_not_null(&std::as_const(spacetime_tensor->get(dtu_multi_index))),\n          time_tensor.get(u_multi_index), 0,\n          time_tensor.get(u_multi_index).size());\n      for (size_t i = 0; i < SpatialDim; i++) {\n        const auto du_multi_index = prepend(u_multi_index, i + 1);\n        const auto diu_multi_index = prepend(u_multi_index, i);\n        make_const_view(make_not_null(&std::as_const(\n                            spacetime_tensor->get(du_multi_index))),\n                        spatial_tensor.get(diu_multi_index), 0,\n                        spatial_tensor.get(diu_multi_index).size());\n      }\n    } else {\n      const auto dtu_multi_index = prepend(u_multi_index, 0_st);\n      spacetime_tensor->get(dtu_multi_index) = time_tensor.get(u_multi_index);\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        const auto du_multi_index = prepend(u_multi_index, i + 1);\n        const auto diu_multi_index = prepend(u_multi_index, i);\n        spacetime_tensor->get(du_multi_index) =\n            spatial_tensor.get(diu_multi_index);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/DataStructures/Tensor/ContractFirstNIndices.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n\nnamespace detail {\n// Get the values that encode the generic tensor indices for the first operand,\n// second operand, and result tensor so that the first NumIndicesToContract\n// indices will contract and the TensorExpression written with them will be\n// valid\n//\n// Note: Implementation assumes that the indices in the index pairs to contract\n// have the same type (spatial or spacetime)\ntemplate <size_t NumIndicesToContract, size_t NumIndices1, size_t NumIndices2>\nconstexpr auto\nget_tensor_index_values_for_tensors_to_contract_and_result_tensor(\n    const std::array<bool, NumIndices1>& index_type_is_spacetime1,\n    const std::array<bool, NumIndices1>& valence_is_lower1,\n    const std::array<bool, NumIndices2>& index_type_is_spacetime2,\n    const std::array<bool, NumIndices2>& valence_is_lower2) {\n  constexpr size_t num_result_indices =\n      NumIndices1 + NumIndices2 - 2 * NumIndicesToContract;\n\n  // (first op index values, second op index values, result tensor index values)\n  std::tuple<std::array<size_t, NumIndices1>, std::array<size_t, NumIndices2>,\n             std::array<size_t, num_result_indices>>\n      tensor_index_values{};\n\n  // the next lower spacetime index value that we have not yet used\n  size_t next_lower_spacetime_value = 0;\n  // the next lower spatial index value that we have not yet used\n  size_t next_lower_spatial_value = tenex::TensorIndex_detail::spatial_sentinel;\n\n  // assign first operand's tensor index values\n  for (size_t i = 0; i < NumIndices1; i++) {\n    const bool index1_is_spacetime = gsl::at(index_type_is_spacetime1, i);\n    const bool index1_is_lower = gsl::at(valence_is_lower1, i);\n    if (index1_is_spacetime) {\n      gsl::at(std::get<0>(tensor_index_values), i) =\n          index1_is_lower ? next_lower_spacetime_value\n                          : tenex::get_tensorindex_value_with_opposite_valence(\n                                next_lower_spacetime_value);\n      next_lower_spacetime_value++;\n    } else {\n      gsl::at(std::get<0>(tensor_index_values), i) =\n          index1_is_lower ? next_lower_spatial_value\n                          : tenex::get_tensorindex_value_with_opposite_valence(\n                                next_lower_spatial_value);\n      next_lower_spatial_value++;\n    }\n  }\n\n  // assign the first NumIndicesToContract index values of the second operand so\n  // that they will contract with the first NumIndicesToContract indices of the\n  // first operand\n  for (size_t i = 0; i < NumIndicesToContract; i++) {\n    gsl::at(std::get<1>(tensor_index_values), i) =\n        tenex::get_tensorindex_value_with_opposite_valence(\n            gsl::at(std::get<0>(tensor_index_values), i));\n  }\n\n  // assign the remaining index values of the second operand so that they are\n  // not duplicates of any previously-used indices\n  for (size_t i = NumIndicesToContract; i < NumIndices2; i++) {\n    const bool index2_is_spacetime = gsl::at(index_type_is_spacetime2, i);\n    const bool index2_is_lower = gsl::at(valence_is_lower2, i);\n    if (index2_is_spacetime) {\n      gsl::at(std::get<1>(tensor_index_values), i) =\n          index2_is_lower ? next_lower_spacetime_value\n                          : tenex::get_tensorindex_value_with_opposite_valence(\n                                next_lower_spacetime_value);\n      next_lower_spacetime_value++;\n    } else {\n      gsl::at(std::get<1>(tensor_index_values), i) =\n          index2_is_lower ? next_lower_spatial_value\n                          : tenex::get_tensorindex_value_with_opposite_valence(\n                                next_lower_spatial_value);\n      next_lower_spatial_value++;\n    }\n  }\n\n  // assign the free indices of the first operand to the result tensor indices\n  for (size_t i = 0; i < NumIndices1 - NumIndicesToContract; i++) {\n    gsl::at(std::get<2>(tensor_index_values), i) =\n        gsl::at(std::get<0>(tensor_index_values), i + NumIndicesToContract);\n  }\n\n  // assign the free indices of the second operand to the result tensor indices\n  for (size_t i = 0; i < NumIndices2 - NumIndicesToContract; i++) {\n    gsl::at(std::get<2>(tensor_index_values),\n            i + NumIndices1 - NumIndicesToContract) =\n        gsl::at(std::get<1>(tensor_index_values), i + NumIndicesToContract);\n  }\n\n  return tensor_index_values;\n}\n\n// \\brief Helper struct for contracting the first `NumIndicesToContract`\n// indices of two `Tensor`s\n//\n// \\tparam NumIndicesToContract the number of indices to contract\n// \\param T1 the type of the first `Tensor` of the two to contract\n// \\param T2 the type of the second `Tensor` of the two to contract\ntemplate <size_t NumIndicesToContract, typename T1, typename T2,\n          size_t NumIndices1 = tmpl::size<typename T1::symmetry>::value,\n          size_t NumIndices2 = tmpl::size<typename T2::symmetry>::value,\n          size_t NumResultIndices =\n              NumIndices1 + NumIndices2 - 2 * NumIndicesToContract,\n          typename ContractedIndicesSeq =\n              std::make_index_sequence<NumIndicesToContract>,\n          typename TensorIndices1Seq = std::make_index_sequence<NumIndices1>,\n          typename TensorIndices2Seq = std::make_index_sequence<NumIndices2>,\n          typename ResultIndicesSeq =\n              std::make_index_sequence<NumResultIndices>>\nstruct contract_first_n_indices_impl;\n\ntemplate <size_t NumIndicesToContract, typename X1, typename Symm1,\n          typename... Indices1, typename X2, typename Symm2,\n          typename... Indices2, size_t NumIndices1, size_t NumIndices2,\n          size_t NumResultIndices, size_t... ContractedIndicesInts,\n          size_t... Ints1, size_t... Ints2, size_t... ResultInts>\nstruct contract_first_n_indices_impl<\n    NumIndicesToContract, Tensor<X1, Symm1, tmpl::list<Indices1...>>,\n    Tensor<X2, Symm2, tmpl::list<Indices2...>>, NumIndices1, NumIndices2,\n    NumResultIndices, std::index_sequence<ContractedIndicesInts...>,\n    std::index_sequence<Ints1...>, std::index_sequence<Ints2...>,\n    std::index_sequence<ResultInts...>> {\n  static_assert(NumIndicesToContract <= sizeof...(Indices1) and\n                    NumIndicesToContract <= sizeof...(Indices2),\n                \"Cannot request to contract more indices than indices in \"\n                \"either of the two Tensors to contract.\");\n\n  static constexpr std::array<bool, NumIndices1> valence_is_lower1 = {\n      {(Indices1::ul == UpLo::Lo)...}};\n  static constexpr std::array<bool, NumIndices2> valence_is_lower2 = {\n      {(Indices2::ul == UpLo::Lo)...}};\n  static constexpr std::array<bool, NumIndices1> index_type_is_spacetime1 = {\n      {(Indices1::index_type == IndexType::Spacetime)...}};\n  static constexpr std::array<bool, NumIndices2> index_type_is_spacetime2 = {\n      {(Indices2::index_type == IndexType::Spacetime)...}};\n\n  // if this is removed and support for automatic contraction of a spatial index\n  // with a spacetime index is wanted, the implementation of\n  // get_tensor_index_values_for_tensors_to_contract_and_result_tensor() also\n  // needs to be updated to support this\n  static_assert(\n      (... and (index_type_is_spacetime1[ContractedIndicesInts] ==\n                index_type_is_spacetime2[ContractedIndicesInts])),\n      \"You are trying to automatically contract a spatial index with a \"\n      \"spacetime index, but this is not supported for \"\n      \"contract_first_n_indices().\");\n\n  // the values of the generic indices (i.e. `TensorIndex::value`s) that\n  // uniquely identify different generic indices\n  static constexpr auto tensor_index_values =\n      get_tensor_index_values_for_tensors_to_contract_and_result_tensor<\n          NumIndicesToContract>(index_type_is_spacetime1, valence_is_lower1,\n                                index_type_is_spacetime2, valence_is_lower2);\n  static constexpr std::array<size_t, NumIndices1> tensor_index_values1 =\n      std::get<0>(tensor_index_values);\n  static constexpr std::array<size_t, NumIndices2> tensor_index_values2 =\n      std::get<1>(tensor_index_values);\n  static constexpr std::array<size_t, NumResultIndices>\n      result_tensor_index_values = std::get<2>(tensor_index_values);\n\n  using lhs_tensorindex_list =\n      tmpl::list<TensorIndex<result_tensor_index_values[ResultInts]>...>;\n\n  // \\brief Contract first N indices by evaluating a `TensorExpression`\n  //\n  // \\param lhs_tensor the result LHS `Tensor`\n  // \\param tensor1 the first `Tensor` of the two to contract\n  // \\param tensor2 the second `Tensor` of the two to contract\n  template <typename LhsTensor>\n  static void apply(const gsl::not_null<LhsTensor*> lhs_tensor,\n                    const Tensor<X1, Symm1, tmpl::list<Indices1...>>& tensor1,\n                    const Tensor<X2, Symm2, tmpl::list<Indices2...>>& tensor2) {\n    constexpr bool evaluate_subtrees =\n        decltype(tensor1(TensorIndex<tensor_index_values1[Ints1]>{}...) *\n                 tensor2(TensorIndex<tensor_index_values2[Ints2]>{}...))::\n            primary_subtree_contains_primary_start;\n    // Calls `evaluate_impl()` instead of `evaluate()` because `evaluate()`\n    // takes `TensorIndex` lvalue references as template parameters, but here we\n    // have `TensorIndex` types, and `evaluate()` cannot be overloaded to accept\n    // both the former and the latter, so we simply circumvent the \"top level\"\n    // `evaluate()` call that is normally used when evaluating the result of a\n    // `TensorExpression`\n    tenex::detail::evaluate_impl<\n        evaluate_subtrees,\n        TensorIndex<result_tensor_index_values[ResultInts]>...>(\n        lhs_tensor,\n        tensor1(TensorIndex<tensor_index_values1[Ints1]>{}...) *\n            tensor2(TensorIndex<tensor_index_values2[Ints2]>{}...),\n        std::make_index_sequence<NumResultIndices>{});\n  }\n\n  // \\brief Contract first N indices by evaluating a `TensorExpression`\n  //\n  // \\param tensor1 the first `Tensor` of the two to contract\n  // \\param tensor2 the second `Tensor` of the two to contract\n  static auto apply(const Tensor<X1, Symm1, tmpl::list<Indices1...>>& tensor1,\n                    const Tensor<X2, Symm2, tmpl::list<Indices2...>>& tensor2) {\n    using rhs_expression =\n        decltype(tensor1(TensorIndex<tensor_index_values1[Ints1]>{}...) *\n                 tensor2(TensorIndex<tensor_index_values2[Ints2]>{}...));\n    using rhs_tensorindex_list = typename rhs_expression::args_list;\n    using rhs_symmetry = typename rhs_expression::symmetry;\n    using rhs_tensorindextype_list = typename rhs_expression::index_list;\n\n    using lhs_tensor_symm_and_indices =\n        tenex::LhsTensorSymmAndIndices<rhs_tensorindex_list,\n                                       lhs_tensorindex_list, rhs_symmetry,\n                                       rhs_tensorindextype_list>;\n\n    Tensor<typename rhs_expression::type,\n           typename lhs_tensor_symm_and_indices::symmetry,\n           typename lhs_tensor_symm_and_indices::tensorindextype_list>\n        lhs_tensor{};\n\n    apply(make_not_null(&lhs_tensor), tensor1, tensor2);\n\n    return lhs_tensor;\n  }\n};\n}  // namespace detail\n\n/// \\ingroup TensorGroup\n/// \\brief Contract the first N indices of two `Tensor`s\n///\n/// \\details\n/// The indices of `lhs_tensor` should be the concatenation of the uncontracted\n/// indices of `tensor1` and the uncontracted indices of `tensor2`, in this\n/// order. For example, if `tensor1` is rank 3, `tensor2` is rank 4, and we want\n/// to contract the first two indices, the indices of `lhs_tensor` need to be\n/// the last index of `tensor1` followed by the 3rd index and then the 4th index\n/// of `tensor2`.\n///\n/// The index types (spatial or spacetime) must be the same for the two indices\n/// in a pair of indices being contracted. Support can be added to this function\n/// to automatically contract the spatial indices of a spacetime index with a\n/// spatial index.\n///\n/// \\tparam NumIndicesToContract the number of indices to contract\n/// \\param lhs_tensor the result LHS `Tensor`\n/// \\param tensor1 the first `Tensor` of the two to contract\n/// \\param tensor2 the second `Tensor` of the two to contract\ntemplate <size_t NumIndicesToContract, typename LhsTensor, typename T1,\n          typename T2>\nvoid contract_first_n_indices(const gsl::not_null<LhsTensor*> lhs_tensor,\n                              const T1& tensor1, const T2& tensor2) {\n  detail::contract_first_n_indices_impl<NumIndicesToContract, T1, T2>::apply(\n      lhs_tensor, tensor1, tensor2);\n}\n\n/// \\ingroup TensorGroup\n/// \\brief Contract the first N indices of two `Tensor`s\n///\n/// \\details\n/// The indices of the returned `Tensor` will be the concatenation of the\n/// uncontracted indices of `tensor1` and the uncontracted indices of `tensor2`,\n/// in this order. For example, if `tensor1` is rank 3, `tensor2` is rank 4, and\n/// we want to contract the first two indices, the indices of the returned\n/// `Tensor` will be the last index of `tensor1` followed by the 3rd index and\n/// then the 4th index of `tensor2`.\n///\n/// The index types (spatial or spacetime) must be the same for the two indices\n/// in a pair of indices being contracted. Support can be added to this function\n/// to automatically contract the spatial indices of a spacetime index with a\n/// spatial index.\n///\n/// \\tparam NumIndicesToContract the number of indices to contract\n/// \\param tensor1 the first `Tensor` of the two to contract\n/// \\param tensor2 the second `Tensor` of the two to contract\ntemplate <size_t NumIndicesToContract, typename T1, typename T2>\nauto contract_first_n_indices(const T1& tensor1, const T2& tensor2) {\n  return detail::contract_first_n_indices_impl<NumIndicesToContract, T1,\n                                               T2>::apply(tensor1, tensor2);\n}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CartesianToSpherical.cpp\n  FrameTransform.cpp\n  GramSchmidtOrthonormalize.cpp\n  OrthonormalOneform.cpp\n  RaiseOrLowerIndex.cpp\n  Trace.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CartesianToSpherical.hpp\n  CrossProduct.hpp\n  Determinant.hpp\n  DeterminantAndInverse.hpp\n  DotProduct.hpp\n  FrameTransform.hpp\n  GramSchmidtOrthonormalize.hpp\n  Magnitude.hpp\n  Norms.hpp\n  OrthonormalOneform.hpp\n  OuterProduct.hpp\n  RaiseOrLowerIndex.hpp\n  Trace.hpp\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/CartesianToSpherical.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/EagerMath/CartesianToSpherical.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\nvoid cartesian_to_spherical(\n    const gsl::not_null<tnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>>*>\n        result,\n    const tnsr::I<DataType, Dim, CoordsFrame>& x) {\n  if constexpr (Dim == 1) {\n    get<0>(*result) = get<0>(x);\n  } else if constexpr (Dim == 2) {\n    get<0>(*result) = hypot(get<0>(x), get<1>(x));\n    get<1>(*result) = atan2(get<1>(x), get<0>(x));\n  } else if constexpr (Dim == 3) {\n    get<0>(*result) =\n        sqrt(square(get<0>(x)) + square(get<1>(x)) + square(get<2>(x)));\n    get<1>(*result) = atan2(hypot(get<0>(x), get<1>(x)), get<2>(x));\n    get<2>(*result) = atan2(get<1>(x), get<0>(x));\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\ntnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>> cartesian_to_spherical(\n    const tnsr::I<DataType, Dim, CoordsFrame>& x) {\n  tnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>> result{};\n  cartesian_to_spherical(make_not_null(&result), x);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\nvoid spherical_to_cartesian(\n    const gsl::not_null<tnsr::I<DataType, Dim, CoordsFrame>*> result,\n    const tnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>>& x) {\n  const auto& r = get<0>(x);\n  if constexpr (Dim == 1) {\n    get<0>(*result) = r;\n  } else if constexpr (Dim == 2) {\n    const auto& phi = get<1>(x);\n    get<0>(*result) = r * cos(phi);\n    get<1>(*result) = r * sin(phi);\n  } else if constexpr (Dim == 3) {\n    const auto& theta = get<1>(x);\n    const auto& phi = get<2>(x);\n    get<0>(*result) = r * sin(theta) * cos(phi);\n    get<1>(*result) = r * sin(theta) * sin(phi);\n    get<2>(*result) = r * cos(theta);\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\ntnsr::I<DataType, Dim, CoordsFrame> spherical_to_cartesian(\n    const tnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>>& x) {\n  tnsr::I<DataType, Dim, CoordsFrame> result{};\n  spherical_to_cartesian(make_not_null(&result), x);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template void cartesian_to_spherical(                                     \\\n      gsl::not_null<                                                        \\\n          tnsr::I<DTYPE(data), DIM(data), Frame::Spherical<FRAME(data)>>*>  \\\n          result,                                                           \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x);               \\\n  template tnsr::I<DTYPE(data), DIM(data), Frame::Spherical<FRAME(data)>>   \\\n  cartesian_to_spherical(                                                   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x);               \\\n  template void spherical_to_cartesian(                                     \\\n      gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*> result,  \\\n      const tnsr::I<DTYPE(data), DIM(data), Frame::Spherical<FRAME(data)>>& \\\n          x);                                                               \\\n  template tnsr::I<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  spherical_to_cartesian(const tnsr::I<DTYPE(data), DIM(data),              \\\n                                       Frame::Spherical<FRAME(data)>>& x);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector), (1, 2, 3),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef DTYPE\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/CartesianToSpherical.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// @{\n/*!\n * \\brief Convert between Cartesian and spherical coordinates (r, theta, phi)\n *\n * In 2D the order is (r, phi), and in 3D the order is (r, theta, phi), as\n * defined by `Frame::Spherical`. The conventions for the angles are as follows:\n *\n * - phi is the azimuthal angle (-pi, pi], measuring the angle from the x-axis\n * - theta is the polar angle [0, pi], measuring the angle from the z-axis\n */\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\nvoid cartesian_to_spherical(\n    gsl::not_null<tnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>>*>\n        result,\n    const tnsr::I<DataType, Dim, CoordsFrame>& x);\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\ntnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>> cartesian_to_spherical(\n    const tnsr::I<DataType, Dim, CoordsFrame>& x);\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\nvoid spherical_to_cartesian(\n    gsl::not_null<tnsr::I<DataType, Dim, CoordsFrame>*> result,\n    const tnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>>& x);\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\ntnsr::I<DataType, Dim, CoordsFrame> spherical_to_cartesian(\n    const tnsr::I<DataType, Dim, Frame::Spherical<CoordsFrame>>& x);\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/CrossProduct.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions `cross_product` for flat and curved-space cross products.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the Euclidean cross product of two vectors or one forms\n *\n * \\details\n * Returns \\f$A^j B^k \\epsilon_{ljk} \\delta^{il}\\f$ for input vectors \\f$A^j\\f$\n * and \\f$B^k\\f$ or \\f$A_j B_k \\epsilon^{ljk} \\delta_{il}\\f$ for input one\n * forms \\f$A_j\\f$ and \\f$B_k\\f$.\n */\ntemplate <typename DataType, typename Index>\nTensor<DataType, Symmetry<1>, index_list<Index>> cross_product(\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_b) {\n  static_assert(Index::dim == 3, \"cross_product vectors must have dimension 3\");\n  static_assert(Index::index_type == IndexType::Spatial,\n                \"cross_product vectors must be spatial\");\n\n  auto cross_product =\n      make_with_value<Tensor<DataType, Symmetry<1>, index_list<Index>>>(\n          vector_a, 0.0);\n  for (LeviCivitaIterator<3> it; it; ++it) {\n    cross_product.get(it[0]) +=\n        it.sign() * vector_a.get(it[1]) * vector_b.get(it[2]);\n  }\n  return cross_product;\n}\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the Euclidean cross product of a vector and a one form\n *\n * \\details\n * Returns \\f$A^j B_l \\delta^{lk} \\epsilon_{ijk}\\f$ for input vector \\f$A^j\\f$\n * and input one form \\f$B_l\\f$\n * or \\f$A_j B^l \\delta_{lk} \\epsilon^{ijk}\\f$ for input one form \\f$A_j\\f$ and\n * input vector \\f$B^l\\f$. Note that this function returns a vector if\n * `vector_b` is a vector and a one form if `vector_b` is a one form.\n */\ntemplate <typename DataType, typename Index>\nTensor<DataType, Symmetry<1>, index_list<change_index_up_lo<Index>>>\ncross_product(const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_a,\n              const Tensor<DataType, Symmetry<1>,\n                           index_list<change_index_up_lo<Index>>>& vector_b) {\n  static_assert(Index::dim == 3, \"cross_product vectors must have dimension 3\");\n  static_assert(Index::index_type == IndexType::Spatial,\n                \"cross_product vectors must be spatial\");\n\n  auto cross_product = make_with_value<\n      Tensor<DataType, Symmetry<1>, index_list<change_index_up_lo<Index>>>>(\n      vector_b, 0.0);\n  for (LeviCivitaIterator<3> it; it; ++it) {\n    cross_product.get(it[0]) +=\n        it.sign() * vector_a.get(it[1]) * vector_b.get(it[2]);\n  }\n  return cross_product;\n}\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the cross product of two vectors or one forms\n *\n * \\details\n * Returns \\f$\\sqrt{g} g^{li} A^j B^k \\epsilon_{ljk}\\f$, where\n * \\f$A^j\\f$ and \\f$B^k\\f$ are vectors and \\f$g^{li}\\f$ and \\f$g\\f$\n * are the inverse and determinant, respectively, of\n * the spatial metric (computed via `determinant_and_inverse`). In this case,\n * the arguments `vector_a` and `vector_b` should be vectors,\n * the argument `metric_or_inverse_metric` should be the inverse spatial\n * metric  \\f$g^{ij}\\f$, and the argument `metric_determinant` should be the\n * determinant of the spatial metric \\f$\\det(g_{ij})\\f$.\n * Or, returns \\f$\\sqrt{g}^{-1} g_{li} A_j B_k \\epsilon^{ljk}\\f$, where\n * \\f$A_j\\f$ and \\f$B_k\\f$ are one forms and \\f$g_{li}\\f$ and \\f$g\\f$\n * are the spatial metric and its determinant. In this case,\n * the arguments `vector_a` and `vector_b` should be one forms,\n * the argument `metric_or_inverse_metric` should be the spatial metric\n * \\f$g_{ij}\\f$, and the argument `metric_determinant` should be the\n * determinant of the spatial metric \\f$\\det(g_{ij})\\f$.\n */\ntemplate <typename DataType, typename Index>\nTensor<DataType, Symmetry<1>, index_list<Index>> cross_product(\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_b,\n    const Tensor<DataType, Symmetry<1, 1>, index_list<Index, Index>>&\n        metric_or_inverse_metric,\n    const Scalar<DataType>& metric_determinant) {\n  static_assert(Index::dim == 3, \"cross_product vectors must have dimension 3\");\n  static_assert(Index::index_type == IndexType::Spatial,\n                \"cross_product vectors must be spatial\");\n\n  auto cross_product =\n      make_with_value<Tensor<DataType, Symmetry<1>, index_list<Index>>>(\n          vector_a, 0.);\n  for (size_t i = 0; i < Index::dim; ++i) {\n    for (LeviCivitaIterator<3> it; it; ++it) {\n      cross_product.get(i) += it.sign() * vector_a.get(it[1]) *\n                              vector_b.get(it[2]) *\n                              metric_or_inverse_metric.get(it[0], i);\n    }\n    if (Index::ul == UpLo::Up) {\n      cross_product.get(i) *= sqrt(get(metric_determinant));\n    } else {\n      cross_product.get(i) /= sqrt(get(metric_determinant));\n    }\n  }\n  return cross_product;\n}\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the cross product of a vector and a one form\n *\n * \\details\n * Returns \\f$\\sqrt{g} A^j B_l g^{lk} \\epsilon_{ijk}\\f$ for input vector\n * \\f$A^j\\f$ and input one form \\f$B_l\\f$. In this case,\n * the argument `vector_a` should be a vector, `vector_b` should be a one form,\n * `metric_or_inverse_metric` should be the inverse spatial\n * metric  \\f$g^{ij}\\f$, and `metric_determinant` should be the\n * determinant of the spatial metric \\f$\\det(g_{ij})\\f$.\n * Or, returns \\f$\\sqrt{g}^{-1} A_j B^l g_{lk}\n * \\epsilon^{ijk}\\f$ for input one form \\f$A_j\\f$ and input vector \\f$B^l\\f$.\n * In this case,\n * the argument `vector_a` should be a one form, `vector_b` should be a vector,\n * `metric_or_inverse_metric` should be the spatial metric\n * \\f$g_{ij}\\f$, and `metric_determinant` should be the\n * determinant of the spatial metric \\f$\\det(g_{ij})\\f$.\n * Note that this function returns a vector if `vector_b` is a vector and a\n * one form if `vector_b` is a one form.\n */\ntemplate <typename DataType, typename Index>\nTensor<DataType, Symmetry<1>, index_list<change_index_up_lo<Index>>>\ncross_product(const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_a,\n              const Tensor<DataType, Symmetry<1>,\n                           index_list<change_index_up_lo<Index>>>& vector_b,\n              const Tensor<DataType, Symmetry<1, 1>, index_list<Index, Index>>&\n                  metric_or_inverse_metric,\n              const Scalar<DataType>& metric_determinant) {\n  static_assert(Index::dim == 3, \"cross_product vectors must have dimension 3\");\n  static_assert(Index::index_type == IndexType::Spatial,\n                \"cross_product vectors must be spatial\");\n\n  auto cross_product = make_with_value<\n      Tensor<DataType, Symmetry<1>, index_list<change_index_up_lo<Index>>>>(\n      vector_b, 0.0);\n  for (LeviCivitaIterator<3> it; it; ++it) {\n    if (Index::ul == UpLo::Up) {\n      for (size_t l = 0; l < Index::dim; ++l) {\n        cross_product.get(it[0]) += it.sign() * vector_a.get(it[1]) *\n                                    vector_b.get(l) *\n                                    metric_or_inverse_metric.get(it[2], l);\n      }\n    } else {\n      for (size_t l = 0; l < Index::dim; ++l) {\n        cross_product.get(it[0]) += it.sign() * vector_a.get(l) *\n                                    vector_b.get(it[2]) *\n                                    metric_or_inverse_metric.get(it[1], l);\n      }\n    }\n  }\n\n  for (size_t i = 0; i < Index::dim; ++i) {\n    if (Index::ul == UpLo::Up) {\n      cross_product.get(i) *= sqrt(get(metric_determinant));\n    } else {\n      cross_product.get(i) /= sqrt(get(metric_determinant));\n    }\n  }\n  return cross_product;\n}\n\n/*!\n * \\brief Compute the cross product of three spacetime vectors or one forms\n *\n * \\details\n * Returns $\\sqrt{g} g^{ed} A^a B^b C^c \\epsilon_{abcd}$, where $\\{A,B,C\\}$ are\n * the input vectors and $g^{ab}$ and $g$ are the inverse spacetime metric and\n * the spacetime metric determinant. Or, returns $-\\sqrt{g}^{-1} g_{ed} A_a B_b\n * C_c \\epsilon^{abcd}$, where $\\{A,B,C\\}$ are one forms and $g_{ab}$ and $g$\n * are the spacetime metric and the spacetime metric determinant.\n * See \\cite Misner1973 page 202 for details.\n *\n * The output buffer must be sized correctly on entry.\n */\ntemplate <typename DataType, typename Index>\nvoid cross_product(\n    const gsl::not_null<Tensor<DataType, Symmetry<1>, index_list<Index>>*>\n        result,\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_b,\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector_c,\n    const Tensor<DataType, Symmetry<1, 1>, index_list<Index, Index>>&\n        metric_or_inverse_metric,\n    const Scalar<DataType>& metric_determinant) {\n  static_assert(Index::dim == 4,\n                \"cross_product vectors must have spacetime dimension 4\");\n  static_assert(Index::index_type == IndexType::Spacetime,\n                \"cross product of 3 vectors must be spacetime\");\n  for (size_t i = 0; i < Index::dim; ++i) {\n    result->get(i) = 0.0;\n    for (LeviCivitaIterator<4> it; it; ++it) {\n      result->get(i) += it.sign() * vector_a.get(it[0]) * vector_b.get(it[1]) *\n                        vector_c.get(it[2]) *\n                        metric_or_inverse_metric.get(it[3], i);\n    }\n    if (Index::ul == UpLo::Up) {\n      result->get(i) *= sqrt(abs(get(metric_determinant)));\n    } else {\n      result->get(i) /= -sqrt(abs(get(metric_determinant)));\n    }\n  }\n}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Determinant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function for taking the determinant of a rank-2 tensor\n\n#pragma once\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace detail {\ntemplate <typename Symm, typename Index, typename = std::nullptr_t>\nstruct DeterminantImpl;\n\ntemplate <typename Symm, typename Index>\nstruct DeterminantImpl<Symm, Index, Requires<Index::dim == 1>> {\n  template <typename T>\n  static typename T::type apply(const T& tensor) {\n    return get<0, 0>(tensor);\n  }\n};\n\ntemplate <typename Symm, typename Index>\nstruct DeterminantImpl<Symm, Index, Requires<Index::dim == 2>> {\n  template <typename T>\n  static typename T::type apply(const T& tensor) {\n    const auto& t00 = get<0, 0>(tensor);\n    const auto& t01 = get<0, 1>(tensor);\n    const auto& t10 = get<1, 0>(tensor);\n    const auto& t11 = get<1, 1>(tensor);\n    return t00 * t11 - t01 * t10;\n  }\n};\n\ntemplate <typename Index>\nstruct DeterminantImpl<Symmetry<2, 1>, Index, Requires<Index::dim == 3>> {\n  template <typename T>\n  static typename T::type apply(const T& tensor) {\n    const auto& t00 = get<0, 0>(tensor);\n    const auto& t01 = get<0, 1>(tensor);\n    const auto& t02 = get<0, 2>(tensor);\n    const auto& t10 = get<1, 0>(tensor);\n    const auto& t11 = get<1, 1>(tensor);\n    const auto& t12 = get<1, 2>(tensor);\n    const auto& t20 = get<2, 0>(tensor);\n    const auto& t21 = get<2, 1>(tensor);\n    const auto& t22 = get<2, 2>(tensor);\n    return t00 * (t11 * t22 - t12 * t21) - t01 * (t10 * t22 - t12 * t20) +\n           t02 * (t10 * t21 - t11 * t20);\n  }\n};\n\ntemplate <typename Index>\nstruct DeterminantImpl<Symmetry<1, 1>, Index, Requires<Index::dim == 3>> {\n  template <typename T>\n  static typename T::type apply(const T& tensor) {\n    const auto& t00 = get<0, 0>(tensor);\n    const auto& t01 = get<0, 1>(tensor);\n    const auto& t02 = get<0, 2>(tensor);\n    const auto& t11 = get<1, 1>(tensor);\n    const auto& t12 = get<1, 2>(tensor);\n    const auto& t22 = get<2, 2>(tensor);\n    return t00 * (t11 * t22 - t12 * t12) - t01 * (t01 * t22 - t12 * t02) +\n           t02 * (t01 * t12 - t11 * t02);\n  }\n};\n\ntemplate <typename Index>\nstruct DeterminantImpl<Symmetry<2, 1>, Index, Requires<Index::dim == 4>> {\n  template <typename T>\n  static typename T::type apply(const T& tensor) {\n    const auto& t00 = get<0, 0>(tensor);\n    const auto& t01 = get<0, 1>(tensor);\n    const auto& t02 = get<0, 2>(tensor);\n    const auto& t03 = get<0, 3>(tensor);\n    const auto& t10 = get<1, 0>(tensor);\n    const auto& t11 = get<1, 1>(tensor);\n    const auto& t12 = get<1, 2>(tensor);\n    const auto& t13 = get<1, 3>(tensor);\n    const auto& t20 = get<2, 0>(tensor);\n    const auto& t21 = get<2, 1>(tensor);\n    const auto& t22 = get<2, 2>(tensor);\n    const auto& t23 = get<2, 3>(tensor);\n    const auto& t30 = get<3, 0>(tensor);\n    const auto& t31 = get<3, 1>(tensor);\n    const auto& t32 = get<3, 2>(tensor);\n    const auto& t33 = get<3, 3>(tensor);\n    const auto minor1 = t22 * t33 - t23 * t32;\n    const auto minor2 = t21 * t33 - t23 * t31;\n    const auto minor3 = t20 * t33 - t23 * t30;\n    const auto minor4 = t21 * t32 - t22 * t31;\n    const auto minor5 = t20 * t32 - t22 * t30;\n    const auto minor6 = t20 * t31 - t21 * t30;\n    return t00 * (t11 * minor1 - t12 * minor2 + t13 * minor4) -\n           t01 * (t10 * minor1 - t12 * minor3 + t13 * minor5) +\n           t02 * (t10 * minor2 - t11 * minor3 + t13 * minor6) -\n           t03 * (t10 * minor4 - t11 * minor5 + t12 * minor6);\n  }\n};\n\ntemplate <typename Index>\nstruct DeterminantImpl<Symmetry<1, 1>, Index, Requires<Index::dim == 4>> {\n  template <typename T>\n  static typename T::type apply(const T& tensor) {\n    const auto& t00 = get<0, 0>(tensor);\n    const auto& t01 = get<0, 1>(tensor);\n    const auto& t02 = get<0, 2>(tensor);\n    const auto& t03 = get<0, 3>(tensor);\n    const auto& t11 = get<1, 1>(tensor);\n    const auto& t12 = get<1, 2>(tensor);\n    const auto& t13 = get<1, 3>(tensor);\n    const auto& t22 = get<2, 2>(tensor);\n    const auto& t23 = get<2, 3>(tensor);\n    const auto& t33 = get<3, 3>(tensor);\n    const auto minor1 = t22 * t33 - t23 * t23;\n    const auto minor2 = t12 * t33 - t23 * t13;\n    const auto minor3 = t02 * t33 - t23 * t03;\n    const auto minor4 = t12 * t23 - t22 * t13;\n    const auto minor5 = t02 * t23 - t22 * t03;\n    const auto minor6 = t02 * t13 - t12 * t03;\n    return t00 * (t11 * minor1 - t12 * minor2 + t13 * minor4) -\n           t01 * (t01 * minor1 - t12 * minor3 + t13 * minor5) +\n           t02 * (t01 * minor2 - t11 * minor3 + t13 * minor6) -\n           t03 * (t01 * minor4 - t11 * minor5 + t12 * minor6);\n  }\n};\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Computes the determinant of a rank-2 Tensor `tensor`.\n *\n * \\requires That `tensor` be a rank-2 Tensor, with both indices sharing the\n *           same dimension and type.\n */\ntemplate <typename T, typename Symm, typename Index0, typename Index1>\nvoid determinant(const gsl::not_null<Scalar<T>*> det_tensor,\n                 const Tensor<T, Symm, index_list<Index0, Index1>>& tensor) {\n  static_assert(Index0::dim == Index1::dim,\n                \"Cannot take the determinant of a Tensor whose Indices are not \"\n                \"of the same dimensionality.\");\n  static_assert(Index0::index_type == Index1::index_type,\n                \"Taking the determinant of a mixed Spatial and Spacetime index \"\n                \"Tensor is not allowed since it's not clear what that means.\");\n  get(*det_tensor) = detail::DeterminantImpl<Symm, Index0>::apply(tensor);\n}\n\ntemplate <typename T, typename Symm, typename Index0, typename Index1>\nScalar<T> determinant(\n    const Tensor<T, Symm, index_list<Index0, Index1>>& tensor) {\n  Scalar<T> result{};\n  determinant(make_not_null(&result), tensor);\n  return result;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function computing the determinant and inverse of a tensor.\n\n#pragma once\n\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace determinant_and_inverse_detail {\n// Helps to shorten some repeated code:\ntemplate <typename Index0, typename Index1>\nusing inverse_indices =\n    tmpl::list<change_index_up_lo<Index1>, change_index_up_lo<Index0>>;\n\ntemplate <typename Symm, typename Index0, typename Index1,\n          typename = std::nullptr_t>\nstruct DetAndInverseImpl;\n\ntemplate <typename Symm, typename Index0, typename Index1>\nstruct DetAndInverseImpl<Symm, Index0, Index1, Requires<Index0::dim == 1>> {\n  template <typename T>\n  static void apply(\n      const gsl::not_null<Scalar<T>*> det,\n      const gsl::not_null<Tensor<T, Symm, inverse_indices<Index0, Index1>>*>\n          inv,\n      const Tensor<T, Symm, tmpl::list<Index0, Index1>>& tensor) {\n    const T& t00 = get<0, 0>(tensor);\n    // inv is non-const so that it can be moved into the std::pair:\n    get(*det) = t00;\n    get<0, 0>(*inv) = 1.0 / t00;\n  }\n};\n\n// The inverse of a 2x2 tensor is computed from Cramer's rule.\ntemplate <typename Index0, typename Index1>\nstruct DetAndInverseImpl<Symmetry<2, 1>, Index0, Index1,\n                         Requires<Index0::dim == 2>> {\n  template <typename T>\n  static void apply(\n      const gsl::not_null<Scalar<T>*> det,\n      const gsl::not_null<\n          Tensor<T, Symmetry<2, 1>, inverse_indices<Index0, Index1>>*>\n          inv,\n      const Tensor<T, Symmetry<2, 1>, tmpl::list<Index0, Index1>>& tensor) {\n    const T& t00 = get<0, 0>(tensor);\n    const T& t01 = get<0, 1>(tensor);\n    const T& t10 = get<1, 0>(tensor);\n    const T& t11 = get<1, 1>(tensor);\n\n    get(*det) = t00 * t11 - t01 * t10;\n    const T one_over_det = make_with_value<T>(*det, 1.0) / get(*det);\n    get<0, 0>(*inv) = t11 * one_over_det;\n    get<0, 1>(*inv) = -t01 * one_over_det;\n    get<1, 0>(*inv) = -t10 * one_over_det;\n    get<1, 1>(*inv) = t00 * one_over_det;\n  }\n};\n\ntemplate <typename Index0>\nstruct DetAndInverseImpl<Symmetry<1, 1>, Index0, Index0,\n                         Requires<Index0::dim == 2>> {\n  template <typename T>\n  static void apply(\n      const gsl::not_null<Scalar<T>*> det,\n      const gsl::not_null<\n          Tensor<T, Symmetry<1, 1>, inverse_indices<Index0, Index0>>*>\n          inv,\n      const Tensor<T, Symmetry<1, 1>, tmpl::list<Index0, Index0>>& tensor) {\n    const T& t00 = get<0, 0>(tensor);\n    const T& t01 = get<0, 1>(tensor);\n    const T& t11 = get<1, 1>(tensor);\n\n    get(*det) = t00 * t11 - t01 * t01;\n    const T one_over_det = make_with_value<T>(*det, 1.0) / get(*det);\n    get<0, 0>(*inv) = t11 * one_over_det;\n    get<0, 1>(*inv) = -t01 * one_over_det;\n    get<1, 1>(*inv) = t00 * one_over_det;\n  }\n};\n\n// The inverse of a 3x3 tensor is computed from Cramer's rule. By reusing some\n// terms, the determinant is computed efficiently at the same time.\ntemplate <typename Index0, typename Index1>\nstruct DetAndInverseImpl<Symmetry<2, 1>, Index0, Index1,\n                         Requires<Index0::dim == 3>> {\n  template <typename T>\n  static void apply(\n      const gsl::not_null<Scalar<T>*> det,\n      const gsl::not_null<\n          Tensor<T, Symmetry<2, 1>, inverse_indices<Index0, Index1>>*>\n          inv,\n      const Tensor<T, Symmetry<2, 1>, tmpl::list<Index0, Index1>>& tensor) {\n    const T& t00 = get<0, 0>(tensor);\n    const T& t01 = get<0, 1>(tensor);\n    const T& t02 = get<0, 2>(tensor);\n    const T& t10 = get<1, 0>(tensor);\n    const T& t11 = get<1, 1>(tensor);\n    const T& t12 = get<1, 2>(tensor);\n    const T& t20 = get<2, 0>(tensor);\n    const T& t21 = get<2, 1>(tensor);\n    const T& t22 = get<2, 2>(tensor);\n    const T a = t11 * t22 - t12 * t21;\n    const T b = t12 * t20 - t10 * t22;\n    const T c = t10 * t21 - t11 * t20;\n\n    get(*det) = t00 * a + t01 * b + t02 * c;\n    const T one_over_det = make_with_value<T>(*det, 1.0) / get(*det);\n    get<0, 0>(*inv) = a * one_over_det;\n    get<0, 1>(*inv) = (t21 * t02 - t22 * t01) * one_over_det;\n    get<0, 2>(*inv) = (t01 * t12 - t02 * t11) * one_over_det;\n    get<1, 0>(*inv) = b * one_over_det;\n    get<1, 1>(*inv) = (t22 * t00 - t20 * t02) * one_over_det;\n    get<1, 2>(*inv) = (t02 * t10 - t00 * t12) * one_over_det;\n    get<2, 0>(*inv) = c * one_over_det;\n    get<2, 1>(*inv) = (t20 * t01 - t21 * t00) * one_over_det;\n    get<2, 2>(*inv) = (t00 * t11 - t01 * t10) * one_over_det;\n  }\n};\n\ntemplate <typename Index0>\nstruct DetAndInverseImpl<Symmetry<1, 1>, Index0, Index0,\n                         Requires<Index0::dim == 3>> {\n  template <typename T>\n  static void apply(\n      const gsl::not_null<Scalar<T>*> det,\n      const gsl::not_null<\n          Tensor<T, Symmetry<1, 1>, inverse_indices<Index0, Index0>>*>\n          inv,\n      const Tensor<T, Symmetry<1, 1>, tmpl::list<Index0, Index0>>& tensor) {\n    const T& t00 = get<0, 0>(tensor);\n    const T& t01 = get<0, 1>(tensor);\n    const T& t02 = get<0, 2>(tensor);\n    const T& t11 = get<1, 1>(tensor);\n    const T& t12 = get<1, 2>(tensor);\n    const T& t22 = get<2, 2>(tensor);\n    const T a = t11 * t22 - t12 * t12;\n    const T b = t12 * t02 - t01 * t22;\n    const T c = t01 * t12 - t11 * t02;\n\n    get(*det) = t00 * a + t01 * b + t02 * c;\n    const T one_over_det = make_with_value<T>(*det, 1.0) / get(*det);\n    get<0, 0>(*inv) = (t11 * t22 - t12 * t12) * one_over_det;\n    get<0, 1>(*inv) = (t12 * t02 - t22 * t01) * one_over_det;\n    get<0, 2>(*inv) = (t01 * t12 - t02 * t11) * one_over_det;\n    get<1, 1>(*inv) = (t22 * t00 - t02 * t02) * one_over_det;\n    get<1, 2>(*inv) = (t02 * t01 - t00 * t12) * one_over_det;\n    get<2, 2>(*inv) = (t00 * t11 - t01 * t01) * one_over_det;\n  }\n};\n\n// The 4x4 tensor inverse is implemented using the formula for inverting a\n// partitioned matrix (here, partitioned into 2x2 blocks). This is more\n// efficient than Cramer's rule for matrices larger than 3x3.\n//\n// In this algorithm, the 4x4 input matrix is partitioned into the 2x2 blocks:\n//   P Q\n//   R S\n// The 4x4 inverse matrix is partitioned into the 2x2 blocks:\n//   U V\n//   W X\n// Each inverse block is obtained from the blocks {P,Q,R,S} of the input matrix\n// as follows:\n//   X = inv[S - R inv(P) Q] (i.e. inv(X) is the Schur complement of P)\n//   W = - X R inv(P)\n//   V = - inv(P) Q X\n//   U = inv(P) + inv(P) Q X R inv(P) = inv(P) - inv(P) Q W\ntemplate <typename Index0, typename Index1>\nstruct DetAndInverseImpl<Symmetry<2, 1>, Index0, Index1,\n                         Requires<Index0::dim == 4>> {\n  template <typename T>\n  static void apply(\n      const gsl::not_null<Scalar<T>*> det,\n      const gsl::not_null<\n          Tensor<T, Symmetry<2, 1>, inverse_indices<Index0, Index1>>*>\n          inv,\n      const Tensor<T, Symmetry<2, 1>, tmpl::list<Index0, Index1>>& tensor) {\n    const T& p00 = get<0, 0>(tensor);\n    const T& p01 = get<0, 1>(tensor);\n    const T& p10 = get<1, 0>(tensor);\n    const T& p11 = get<1, 1>(tensor);\n    const T& q00 = get<0, 2>(tensor);\n    const T& q01 = get<0, 3>(tensor);\n    const T& q10 = get<1, 2>(tensor);\n    const T& q11 = get<1, 3>(tensor);\n    const T& r00 = get<2, 0>(tensor);\n    const T& r01 = get<2, 1>(tensor);\n    const T& r10 = get<3, 0>(tensor);\n    const T& r11 = get<3, 1>(tensor);\n    const T& s00 = get<2, 2>(tensor);\n    const T& s01 = get<2, 3>(tensor);\n    const T& s10 = get<3, 2>(tensor);\n    const T& s11 = get<3, 3>(tensor);\n\n    T& u00 = get<0, 0>(*inv);\n    T& u01 = get<0, 1>(*inv);\n    T& u10 = get<1, 0>(*inv);\n    T& u11 = get<1, 1>(*inv);\n    T& v00 = get<0, 2>(*inv);\n    T& v01 = get<0, 3>(*inv);\n    T& v10 = get<1, 2>(*inv);\n    T& v11 = get<1, 3>(*inv);\n    T& w00 = get<2, 0>(*inv);\n    T& w01 = get<2, 1>(*inv);\n    T& w10 = get<3, 0>(*inv);\n    T& w11 = get<3, 1>(*inv);\n    T& x00 = get<2, 2>(*inv);\n    T& x01 = get<2, 3>(*inv);\n    T& x10 = get<3, 2>(*inv);\n    T& x11 = get<3, 3>(*inv);\n\n    // Temporarily store det(P) in det\n    get(*det) = p00 * p11 - p01 * p10;\n    const T& det_p = get(*det);\n    const T one_over_det_p = make_with_value<T>(det_p, 1.0) / det_p;\n    const T inv_p00 = p11 * one_over_det_p;\n    const T inv_p01 = -p01 * one_over_det_p;\n    const T inv_p10 = -p10 * one_over_det_p;\n    const T inv_p11 = p00 * one_over_det_p;\n\n    const T r_inv_p00 = r00 * inv_p00 + r01 * inv_p10;\n    const T r_inv_p01 = r00 * inv_p01 + r01 * inv_p11;\n    const T r_inv_p10 = r10 * inv_p00 + r11 * inv_p10;\n    const T r_inv_p11 = r10 * inv_p01 + r11 * inv_p11;\n\n    const T inv_p_q00 = inv_p00 * q00 + inv_p01 * q10;\n    const T inv_p_q01 = inv_p00 * q01 + inv_p01 * q11;\n    const T inv_p_q10 = inv_p10 * q00 + inv_p11 * q10;\n    const T inv_p_q11 = inv_p10 * q01 + inv_p11 * q11;\n\n    const T inv_x00 = s00 - (r_inv_p00 * q00 + r_inv_p01 * q10);\n    const T inv_x01 = s01 - (r_inv_p00 * q01 + r_inv_p01 * q11);\n    const T inv_x10 = s10 - (r_inv_p10 * q00 + r_inv_p11 * q10);\n    const T inv_x11 = s11 - (r_inv_p10 * q01 + r_inv_p11 * q11);\n\n    const T det_inv_x = inv_x00 * inv_x11 - inv_x01 * inv_x10;\n    const T one_over_det_inv_x = make_with_value<T>(det_inv_x, 1.0) / det_inv_x;\n    x00 = inv_x11 * one_over_det_inv_x;\n    x01 = -inv_x01 * one_over_det_inv_x;\n    x10 = -inv_x10 * one_over_det_inv_x;\n    x11 = inv_x00 * one_over_det_inv_x;\n\n    w00 = -x00 * r_inv_p00 - x01 * r_inv_p10;\n    w01 = -x00 * r_inv_p01 - x01 * r_inv_p11;\n    w10 = -x10 * r_inv_p00 - x11 * r_inv_p10;\n    w11 = -x10 * r_inv_p01 - x11 * r_inv_p11;\n\n    v00 = -inv_p_q00 * x00 - inv_p_q01 * x10;\n    v01 = -inv_p_q00 * x01 - inv_p_q01 * x11;\n    v10 = -inv_p_q10 * x00 - inv_p_q11 * x10;\n    v11 = -inv_p_q10 * x01 - inv_p_q11 * x11;\n\n    u00 = inv_p00 - (inv_p_q00 * w00 + inv_p_q01 * w10);\n    u01 = inv_p01 - (inv_p_q00 * w01 + inv_p_q01 * w11);\n    u10 = inv_p10 - (inv_p_q10 * w00 + inv_p_q11 * w10);\n    u11 = inv_p11 - (inv_p_q10 * w01 + inv_p_q11 * w11);\n\n    get(*det) *= det_inv_x;\n  }\n};\n\ntemplate <typename Index0>\nstruct DetAndInverseImpl<Symmetry<1, 1>, Index0, Index0,\n                         Requires<Index0::dim == 4>> {\n  template <typename T>\n  static void apply(\n      const gsl::not_null<Scalar<T>*> det,\n      const gsl::not_null<\n          Tensor<T, Symmetry<1, 1>, inverse_indices<Index0, Index0>>*>\n          inv,\n      const Tensor<T, Symmetry<1, 1>, tmpl::list<Index0, Index0>>& tensor) {\n    const T& p00 = get<0, 0>(tensor);\n    const T& p01 = get<0, 1>(tensor);\n    const T& p11 = get<1, 1>(tensor);\n    const T& q00 = get<0, 2>(tensor);\n    const T& q01 = get<0, 3>(tensor);\n    const T& q10 = get<1, 2>(tensor);\n    const T& q11 = get<1, 3>(tensor);\n    const T& s00 = get<2, 2>(tensor);\n    const T& s01 = get<2, 3>(tensor);\n    const T& s11 = get<3, 3>(tensor);\n\n    T& u00 = get<0, 0>(*inv);\n    T& u01 = get<0, 1>(*inv);\n    T& u11 = get<1, 1>(*inv);\n    T& v00 = get<0, 2>(*inv);\n    T& v01 = get<0, 3>(*inv);\n    T& v10 = get<1, 2>(*inv);\n    T& v11 = get<1, 3>(*inv);\n    T& x00 = get<2, 2>(*inv);\n    T& x01 = get<2, 3>(*inv);\n    T& x11 = get<3, 3>(*inv);\n\n    // Temporarily store det(P) in det\n    get(*det) = p00 * p11 - p01 * p01;\n    const T& det_p = get(*det);\n    const T one_over_det_p = make_with_value<T>(det_p, 1.0) / det_p;\n    const T inv_p00 = p11 * one_over_det_p;\n    const T inv_p01 = -p01 * one_over_det_p;\n    const T inv_p11 = p00 * one_over_det_p;\n\n    const T r_inv_p00 = q00 * inv_p00 + q10 * inv_p01;\n    const T r_inv_p01 = q00 * inv_p01 + q10 * inv_p11;\n    const T r_inv_p10 = q01 * inv_p00 + q11 * inv_p01;\n    const T r_inv_p11 = q01 * inv_p01 + q11 * inv_p11;\n\n    const T inv_x00 = s00 - (r_inv_p00 * q00 + r_inv_p01 * q10);\n    const T inv_x01 = s01 - (r_inv_p00 * q01 + r_inv_p01 * q11);\n    const T inv_x11 = s11 - (r_inv_p10 * q01 + r_inv_p11 * q11);\n\n    const T det_inv_x = inv_x00 * inv_x11 - inv_x01 * inv_x01;\n    const T one_over_det_inv_x = make_with_value<T>(det_inv_x, 1.0) / det_inv_x;\n    x00 = inv_x11 * one_over_det_inv_x;\n    x01 = -inv_x01 * one_over_det_inv_x;\n    x11 = inv_x00 * one_over_det_inv_x;\n\n    v00 = -x00 * r_inv_p00 - x01 * r_inv_p10;\n    v10 = -x00 * r_inv_p01 - x01 * r_inv_p11;\n    v01 = -x01 * r_inv_p00 - x11 * r_inv_p10;\n    v11 = -x01 * r_inv_p01 - x11 * r_inv_p11;\n\n    u00 = inv_p00 - (r_inv_p00 * v00 + r_inv_p10 * v01);\n    u01 = inv_p01 - (r_inv_p00 * v10 + r_inv_p10 * v11);\n    u11 = inv_p11 - (r_inv_p01 * v10 + r_inv_p11 * v11);\n\n    get(*det) *= det_inv_x;\n  }\n};\n}  // namespace determinant_and_inverse_detail\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Computes the determinant and inverse of a rank-2 Tensor.\n *\n * Computes the determinant and inverse together, because this leads to\n * fewer operations compared to computing the determinant independently.\n *\n * \\details\n * Treats the input rank-2 tensor as a matrix. The first (second) index\n * of the tensor corresponds to the rows (columns) of the matrix. The\n * determinant is a scalar tensor. The inverse is a rank-2 tensor whose\n * indices are reversed and of opposite valence relative to the input\n * tensor, i.e. given \\f$T_a^b\\f$ returns \\f$(Tinv)_b^a\\f$.\n *\n * \\note\n * When inverting a 4x4 spacetime metric, it is typically more efficient\n * to use the 3+1 decomposition of the 4-metric in terms of lapse,\n * shift, and spatial 3-metric, in which only the spatial 3-metric needs\n * to be inverted.\n */\ntemplate <typename T, typename Symm, typename Index0, typename Index1>\nvoid determinant_and_inverse(\n    const gsl::not_null<Scalar<T>*> det,\n    const gsl::not_null<Tensor<\n        T, Symm,\n        tmpl::list<change_index_up_lo<Index1>, change_index_up_lo<Index0>>>*>\n        inv,\n    const Tensor<T, Symm, tmpl::list<Index0, Index1>>& tensor) {\n  static_assert(Index0::dim == Index1::dim,\n                \"Cannot take the inverse of a Tensor whose Indices are not \"\n                \"of the same dimensionality.\");\n  static_assert(Index0::index_type == Index1::index_type,\n                \"Taking the inverse of a mixed Spatial and Spacetime index \"\n                \"Tensor is not allowed since it's not clear what that means.\");\n  static_assert(not std::is_integral<T>::value, \"Can't invert a Tensor<int>.\");\n\n  set_number_of_grid_points(det, tensor);\n  set_number_of_grid_points(inv, tensor);\n  determinant_and_inverse_detail::DetAndInverseImpl<Symm, Index0,\n                                                    Index1>::apply(det, inv,\n                                                                   tensor);\n}\n\ntemplate <typename T, typename Symm, typename Index0, typename Index1>\nauto determinant_and_inverse(\n    const Tensor<T, Symm, tmpl::list<Index0, Index1>>& tensor)\n    -> std::pair<Scalar<T>, Tensor<T, Symm,\n                                   tmpl::list<change_index_up_lo<Index1>,\n                                              change_index_up_lo<Index0>>>> {\n  std::pair<Scalar<T>, Tensor<T, Symm,\n                              tmpl::list<change_index_up_lo<Index1>,\n                                         change_index_up_lo<Index0>>>>\n      result{};\n  determinant_and_inverse_detail::DetAndInverseImpl<\n      Symm, Index0, Index1>::apply(make_not_null(&result.first),\n                                   make_not_null(&result.second), tensor);\n  return result;\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Computes the determinant and inverse of a rank-2 Tensor.\n *\n * Computes the determinant and inverse together, because this leads to fewer\n * operations compared to computing the determinant independently.\n *\n * \\tparam DetTag the Tag for the determinant of input Tensor.\n * \\tparam InvTag the Tag for the inverse of input Tensor.\n *\n * \\details\n * See determinant_and_inverse().\n */\ntemplate <typename DetTag, typename InvTag, typename T, typename Symm,\n          typename Index0, typename Index1>\nvoid determinant_and_inverse(\n    const gsl::not_null<Variables<tmpl::list<DetTag, InvTag>>*> det_and_inv,\n    const Tensor<T, Symm, tmpl::list<Index0, Index1>>& tensor) {\n  static_assert(std::is_same_v<typename DetTag::type, Scalar<T>>,\n                \"Type of first return tag must correspond to that of input's \"\n                \"determinant.\");\n  static_assert(\n      std::is_same_v<typename InvTag::type,\n                     Tensor<T, Symm,\n                            tmpl::list<change_index_up_lo<Index1>,\n                                       change_index_up_lo<Index0>>>>,\n      \"Type of second return tag must correspond to that of input's inverse.\");\n  const auto number_of_grid_points = get<0, 0>(tensor).size();\n  if (UNLIKELY(number_of_grid_points != det_and_inv->number_of_grid_points())) {\n    det_and_inv->initialize(number_of_grid_points);\n  }\n  determinant_and_inverse_detail::DetAndInverseImpl<\n      Symm, Index0, Index1>::apply(make_not_null(&get<DetTag>(*det_and_inv)),\n                                   make_not_null(&get<InvTag>(*det_and_inv)),\n                                   tensor);\n}\n\ntemplate <typename DetTag, typename InvTag, typename T, typename Symm,\n          typename Index0, typename Index1>\nauto determinant_and_inverse(\n    const Tensor<T, Symm, tmpl::list<Index0, Index1>>& tensor)\n    -> Variables<tmpl::list<DetTag, InvTag>> {\n  static_assert(std::is_same_v<typename DetTag::type, Scalar<T>>,\n                \"Type of first return tag must correspond to that of input's \"\n                \"determinant.\");\n  static_assert(std::is_same_v<typename InvTag::type,\n                               Tensor<T, Symm,\n                                      tmpl::list<change_index_up_lo<Index1>,\n                                                 change_index_up_lo<Index0>>>>,\n                \"Type of second return tag must correspond to that of input's \"\n                \"inverse.\");\n  Variables<tmpl::list<DetTag, InvTag>> result(get<0, 0>(tensor).size());\n  determinant_and_inverse_detail::DetAndInverseImpl<\n      Symm, Index0, Index1>::apply(make_not_null(&get<DetTag>(result)),\n                                   make_not_null(&get<InvTag>(result)), tensor);\n  return result;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/DotProduct.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions euclidean dot_product and dot_product with a metric\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the Euclidean dot product of two vectors or one forms\n *\n * \\details\n * Returns \\f$A^a B^b \\delta_{ab}\\f$ for input vectors \\f$A^a\\f$ and \\f$B^b\\f$\n * or \\f$A_a B_b \\delta^{ab}\\f$ for input one forms \\f$A_a\\f$ and \\f$B_b\\f$.\n */\ntemplate <typename DataTypeLhs, typename DataTypeRhs, typename Index,\n          typename DataTypeResult = decltype(blaze::evaluate(DataTypeLhs() *\n                                                             DataTypeRhs()))>\nvoid dot_product(\n    const gsl::not_null<Scalar<DataTypeResult>*> dot_product,\n    const Tensor<DataTypeLhs, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataTypeRhs, Symmetry<1>, index_list<Index>>& vector_b) {\n  get(*dot_product) = get<0>(vector_a) * get<0>(vector_b);\n  for (size_t d = 1; d < Index::dim; ++d) {\n    get(*dot_product) += vector_a.get(d) * vector_b.get(d);\n  }\n}\n\ntemplate <typename DataTypeLhs, typename DataTypeRhs, typename Index,\n          typename DataTypeResult = decltype(blaze::evaluate(DataTypeLhs() *\n                                                             DataTypeRhs()))>\nScalar<DataTypeResult> dot_product(\n    const Tensor<DataTypeLhs, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataTypeRhs, Symmetry<1>, index_list<Index>>& vector_b) {\n  Scalar<DataTypeResult> dot_product(get_size(get<0>(vector_a)));\n  ::dot_product(make_not_null(&dot_product), vector_a, vector_b);\n  return dot_product;\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the dot product of a vector and a one form\n *\n * \\details\n * Returns \\f$A^a B_b \\delta_{a}^b\\f$ for input vector \\f$A^a\\f$ and\n * input one form \\f$B_b\\f$\n * or \\f$A_a B^b \\delta^a_b\\f$ for input one form \\f$A_a\\f$ and\n * input vector \\f$B^b\\f$.\n */\ntemplate <typename DataTypeLhs, typename DataTypeRhs, typename Index,\n          typename DataTypeResult = decltype(blaze::evaluate(DataTypeLhs() *\n                                                             DataTypeRhs()))>\nvoid dot_product(\n    // NOLINTNEXTLINE(readability-avoid-const-params-in-decls) false positive\n    const gsl::not_null<Scalar<DataTypeResult>*> dot_product,\n    const Tensor<DataTypeLhs, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataTypeRhs, Symmetry<1>,\n                 index_list<change_index_up_lo<Index>>>& vector_b) {\n  get(*dot_product) = get<0>(vector_a) * get<0>(vector_b);\n  for (size_t d = 1; d < Index::dim; ++d) {\n    get(*dot_product) += vector_a.get(d) * vector_b.get(d);\n  }\n}\n\ntemplate <typename DataTypeLhs, typename DataTypeRhs, typename Index,\n          typename DataTypeResult = decltype(blaze::evaluate(DataTypeLhs() *\n                                                             DataTypeRhs()))>\nScalar<DataTypeResult> dot_product(\n    const Tensor<DataTypeLhs, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataTypeRhs, Symmetry<1>,\n                 index_list<change_index_up_lo<Index>>>& vector_b) {\n  Scalar<DataTypeResult> dot_product(get_size(get<0>(vector_a)));\n  ::dot_product(make_not_null(&dot_product), vector_a, vector_b);\n  return dot_product;\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the dot_product of two vectors or one forms\n *\n * \\details\n * Returns \\f$g_{ab} A^a B^b\\f$, where \\f$g_{ab}\\f$ is the metric,\n * \\f$A^a\\f$ is vector_a, and \\f$B^b\\f$ is vector_b.\n * Or, returns \\f$g^{ab} A_a B_b\\f$ when given one forms \\f$A_a\\f$\n * and \\f$B_b\\f$ with an inverse metric \\f$g^{ab}\\f$.\n */\ntemplate <typename DataTypeLhs, typename DataTypeRhs, typename DataTypeMetric,\n          typename Index,\n          typename DataTypeResult = decltype(blaze::evaluate(\n              DataTypeLhs() * DataTypeRhs() * DataTypeMetric()))>\nvoid dot_product(\n    const gsl::not_null<Scalar<DataTypeResult>*> dot_product,\n    const Tensor<DataTypeLhs, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataTypeRhs, Symmetry<1>, index_list<Index>>& vector_b,\n    const Tensor<DataTypeMetric, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index>,\n                            change_index_up_lo<Index>>>& metric) {\n  if constexpr (Index::dim == 1) {\n    get(*dot_product) = get<0>(vector_a) * get<0>(vector_b) * get<0, 0>(metric);\n  } else {\n    if (&vector_a == &vector_b) {\n      get(*dot_product) =\n          get<0>(vector_a) * get<1>(vector_a) * get<0, 1>(metric);\n      for (size_t j = 2; j < Index::dim; ++j) {\n        get(*dot_product) +=\n            vector_a.get(0) * vector_a.get(j) * metric.get(0, j);\n      }\n      for (size_t i = 1; i < Index::dim; ++i) {\n        for (size_t j = i + 1; j < Index::dim; ++j) {\n          get(*dot_product) +=\n              vector_a.get(i) * vector_a.get(j) * metric.get(i, j);\n        }\n      }\n      get(*dot_product) *= 2.0;\n\n      for (size_t i = 0; i < Index::dim; ++i) {\n        get(*dot_product) += square(vector_a.get(i)) * metric.get(i, i);\n      }\n    } else {\n      get(*dot_product) =\n          get<0>(vector_a) * get<0>(vector_b) * get<0, 0>(metric);\n      for (size_t b = 1; b < Index::dim; ++b) {\n        get(*dot_product) +=\n            get<0>(vector_a) * vector_b.get(b) * metric.get(0, b);\n      }\n\n      for (size_t a = 1; a < Index::dim; ++a) {\n        for (size_t b = 0; b < Index::dim; ++b) {\n          get(*dot_product) +=\n              vector_a.get(a) * vector_b.get(b) * metric.get(a, b);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataTypeLhs, typename DataTypeRhs, typename DataTypeMetric,\n          typename Index,\n          typename DataTypeResult = decltype(blaze::evaluate(\n              DataTypeLhs() * DataTypeRhs() * DataTypeMetric()))>\nScalar<DataTypeResult> dot_product(\n    const Tensor<DataTypeLhs, Symmetry<1>, index_list<Index>>& vector_a,\n    const Tensor<DataTypeRhs, Symmetry<1>, index_list<Index>>& vector_b,\n    const Tensor<DataTypeMetric, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index>,\n                            change_index_up_lo<Index>>>& metric) {\n  Scalar<DataTypeResult> dot_product(get_size(get<0>(vector_a)));\n  ::dot_product(make_not_null(&dot_product), vector_a, vector_b, metric);\n  return dot_product;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/FrameTransform.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/EagerMath/FrameTransform.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace transform {\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::ii<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::ii<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = i; j < VolumeDim; ++j) {  // symmetry\n      // To avoid initializing to zero, split out k=0 here, and start\n      // k loop below at k=1.\n      dest->get(i, j) = jacobian.get(0, i) * jacobian.get(0, j) * src.get(0, 0);\n      for (size_t p = 1; p < VolumeDim; ++p) {\n        dest->get(i, j) +=\n            jacobian.get(0, i) * jacobian.get(p, j) * src.get(0, p);\n      }\n      for (size_t k = 1; k < VolumeDim; ++k) {\n        for (size_t p = 0; p < VolumeDim; ++p) {\n          dest->get(i, j) +=\n              jacobian.get(k, i) * jacobian.get(p, j) * src.get(k, p);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::ii<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian)\n    -> tnsr::ii<DataType, VolumeDim, DestFrame> {\n  auto dest = make_with_value<tnsr::ii<DataType, VolumeDim, DestFrame>>(\n      src, std::numeric_limits<double>::signaling_NaN());\n  to_different_frame(make_not_null(&dest), src, jacobian);\n  return dest;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<Scalar<DataType>*> dest, const Scalar<DataType>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& /*jacobian*/,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n    /*inv_jacobian*/) {\n  *dest = src;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    Scalar<DataType> src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& /*jacobian*/,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n    /*inv_jacobian*/) -> Scalar<DataType> {\n  return src;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::I<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::I<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& /*jacobian*/,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    dest->get(i) = inv_jacobian.get(i, 0) * src.get(0);\n    for (size_t p = 1; p < VolumeDim; ++p) {\n      dest->get(i) += inv_jacobian.get(i, p) * src.get(p);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::I<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::I<DataType, VolumeDim, DestFrame> {\n  auto dest = make_with_value<tnsr::I<DataType, VolumeDim, DestFrame>>(\n      src, std::numeric_limits<double>::signaling_NaN());\n  to_different_frame(make_not_null(&dest), src, jacobian, inv_jacobian);\n  return dest;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::i<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::i<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n    /*inv_jacobian*/) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    dest->get(i) = jacobian.get(0, i) * src.get(0);\n    for (size_t p = 1; p < VolumeDim; ++p) {\n      dest->get(i) += jacobian.get(p, i) * src.get(p);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::i<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::i<DataType, VolumeDim, DestFrame> {\n  auto dest = make_with_value<tnsr::i<DataType, VolumeDim, DestFrame>>(\n      src, std::numeric_limits<double>::signaling_NaN());\n  to_different_frame(make_not_null(&dest), src, jacobian, inv_jacobian);\n  return dest;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::iJ<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::iJ<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      // To avoid initializing to zero, split out k=0 here, and start\n      // k loop below at k=1.\n      dest->get(i, j) =\n          jacobian.get(0, i) * inv_jacobian.get(j, 0) * src.get(0, 0);\n      for (size_t p = 1; p < VolumeDim; ++p) {\n        dest->get(i, j) +=\n            jacobian.get(0, i) * inv_jacobian.get(j, p) * src.get(0, p);\n      }\n      for (size_t k = 1; k < VolumeDim; ++k) {\n        for (size_t p = 0; p < VolumeDim; ++p) {\n          dest->get(i, j) +=\n              jacobian.get(k, i) * inv_jacobian.get(j, p) * src.get(k, p);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::iJ<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::iJ<DataType, VolumeDim, DestFrame> {\n  auto dest = make_with_value<tnsr::iJ<DataType, VolumeDim, DestFrame>>(\n      src, std::numeric_limits<double>::signaling_NaN());\n  to_different_frame(make_not_null(&dest), src, jacobian, inv_jacobian);\n  return dest;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::ii<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::ii<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n    /*inv_jacobian*/) {\n  to_different_frame(dest, src, jacobian);\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::ii<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::ii<DataType, VolumeDim, DestFrame> {\n  auto dest = make_with_value<tnsr::ii<DataType, VolumeDim, DestFrame>>(\n      src, std::numeric_limits<double>::signaling_NaN());\n  to_different_frame(make_not_null(&dest), src, jacobian, inv_jacobian);\n  return dest;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::II<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::II<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& /*jacobian*/,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = i; j < VolumeDim; ++j) {  // symmetry\n      // To avoid initializing to zero, split out k=0 here, and start\n      // k loop below at k=1.\n      dest->get(i, j) =\n          inv_jacobian.get(i, 0) * inv_jacobian.get(j, 0) * src.get(0, 0);\n      for (size_t p = 1; p < VolumeDim; ++p) {\n        dest->get(i, j) +=\n            inv_jacobian.get(i, 0) * inv_jacobian.get(j, p) * src.get(0, p);\n      }\n      for (size_t k = 1; k < VolumeDim; ++k) {\n        for (size_t p = 0; p < VolumeDim; ++p) {\n          dest->get(i, j) +=\n              inv_jacobian.get(i, k) * inv_jacobian.get(j, p) * src.get(k, p);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::II<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::II<DataType, VolumeDim, DestFrame> {\n  auto dest = make_with_value<tnsr::II<DataType, VolumeDim, DestFrame>>(\n      src, std::numeric_limits<double>::signaling_NaN());\n  to_different_frame(make_not_null(&dest), src, jacobian, inv_jacobian);\n  return dest;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::ijj<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::ijj<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n    /*inv_jacobian*/) {\n  for (size_t q = 0; q < VolumeDim; ++q) {\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      for (size_t j = i; j < VolumeDim; ++j) {  // symmetry\n        dest->get(q, i, j) = jacobian.get(0, q) * jacobian.get(0, i) *\n                             jacobian.get(0, j) * src.get(0, 0, 0);\n        for (size_t r = 0; r < VolumeDim; ++r) {\n          for (size_t k = 0; k < VolumeDim; ++k) {\n            for (size_t p = 0; p < VolumeDim; ++p) {\n              if (r == 0 and k == 0 and p == 0) {\n                continue;\n              }\n              dest->get(q, i, j) += jacobian.get(r, q) * jacobian.get(k, i) *\n                                    jacobian.get(p, j) * src.get(r, k, p);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::ijj<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::ijj<DataType, VolumeDim, DestFrame> {\n  auto dest = make_with_value<tnsr::ijj<DataType, VolumeDim, DestFrame>>(\n      src, std::numeric_limits<double>::signaling_NaN());\n  to_different_frame(make_not_null(&dest), src, jacobian, inv_jacobian);\n  return dest;\n}\n\ntemplate <typename ResultTensor, typename InputTensor, typename DataType,\n          size_t Dim, typename SourceFrame, typename TargetFrame>\nvoid first_index_to_different_frame(\n    const gsl::not_null<ResultTensor*> result, const InputTensor& input,\n    const InverseJacobian<DataType, Dim, SourceFrame, TargetFrame>&\n        inv_jacobian) {\n  static_assert(InputTensor::rank() > 0,\n                \"Cannot transform scalar to different frame\");\n  static_assert(\n      std::is_same_v<TensorMetafunctions::remove_first_index<ResultTensor>,\n                     TensorMetafunctions::remove_first_index<InputTensor>>,\n      \"The input and result tensors must be the same except for the first \"\n      \"index\");\n  using first_index = tmpl::front<typename InputTensor::index_list>;\n  static_assert(\n      std::is_same_v<first_index, SpatialIndex<Dim, UpLo::Up, TargetFrame>>,\n      \"This function is currently only tested for transforming an upper \"\n      \"spatial index but can be generalized.\");\n  for (size_t storage_index = 0; storage_index < ResultTensor::size();\n       ++storage_index) {\n    const auto result_index = ResultTensor::get_tensor_index(storage_index);\n    auto input_index = result_index;\n    input_index[0] = 0;\n    result->get(result_index) =\n        input.get(input_index) * inv_jacobian.get(result_index[0], 0);\n    for (size_t d = 1; d < Dim; ++d) {\n      input_index[0] = d;\n      result->get(result_index) +=\n          input.get(input_index) * inv_jacobian.get(result_index[0], d);\n    }\n  }\n}\n\ntemplate <typename InputTensor, typename DataType, size_t Dim,\n          typename SourceFrame, typename TargetFrame, typename ResultTensor>\nResultTensor first_index_to_different_frame(\n    const InputTensor& input,\n    const InverseJacobian<DataType, Dim, SourceFrame, TargetFrame>&\n        inv_jacobian) {\n  ResultTensor result{};\n  first_index_to_different_frame(make_not_null(&result), input, inv_jacobian);\n  return result;\n}\n\n}  // namespace transform\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define SRCFRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DESTFRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void transform::to_different_frame(                                 \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), DESTFRAME(data)>*>  \\\n          dest,                                                                \\\n      const tnsr::ii<DTYPE(data), DIM(data), SRCFRAME(data)>& src,             \\\n      const Jacobian<DTYPE(data), DIM(data), DESTFRAME(data), SRCFRAME(data)>& \\\n          jacobian);                                                           \\\n  template auto transform::to_different_frame(                                 \\\n      const tnsr::ii<DTYPE(data), DIM(data), SRCFRAME(data)>& src,             \\\n      const Jacobian<DTYPE(data), DIM(data), DESTFRAME(data), SRCFRAME(data)>& \\\n          jacobian)                                                            \\\n      ->tnsr::ii<DTYPE(data), DIM(data), DESTFRAME(data)>;                     \\\n  template void transform::to_different_frame(                                 \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> dest,                          \\\n      const Scalar<DTYPE(data)>& src,                                          \\\n      const Jacobian<DTYPE(data), DIM(data), DESTFRAME(data), SRCFRAME(data)>& \\\n          jacobian,                                                            \\\n      const InverseJacobian<DTYPE(data), DIM(data), DESTFRAME(data),           \\\n                            SRCFRAME(data)>& inv_jacobian);                    \\\n  template auto transform::to_different_frame(                                 \\\n      Scalar<DTYPE(data)> src,                                                 \\\n      const Jacobian<DTYPE(data), DIM(data), DESTFRAME(data), SRCFRAME(data)>& \\\n          jacobian,                                                            \\\n      const InverseJacobian<DTYPE(data), DIM(data), DESTFRAME(data),           \\\n                            SRCFRAME(data)>& inv_jacobian)                     \\\n      ->Scalar<DTYPE(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3),\n                        (Frame::BlockLogical, Frame::ElementLogical,\n                         Frame::Grid, Frame::Distorted),\n                        (Frame::Inertial), (double, DataVector))\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Inertial),\n                        (Frame::BlockLogical, Frame::ElementLogical,\n                         Frame::Grid, Frame::Distorted),\n                        (double, DataVector))\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Inertial),\n                        (Frame::NoFrame), (double, DataVector))\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::NoFrame),\n                        (Frame::Inertial), (double, DataVector))\n#undef INSTANTIATE\n\n#define TENSOR(data) BOOST_PP_TUPLE_ELEM(4, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void transform::to_different_frame(                                 \\\n      const gsl::not_null<tnsr::TENSOR(data) < DTYPE(data), DIM(data),         \\\n                          DESTFRAME(data)>* > dest,                            \\\n      const tnsr::TENSOR(data) < DTYPE(data), DIM(data),                       \\\n      SRCFRAME(data) > &src,                                                   \\\n      const Jacobian<DTYPE(data), DIM(data), DESTFRAME(data), SRCFRAME(data)>& \\\n          jacobian,                                                            \\\n      const InverseJacobian<DTYPE(data), DIM(data), DESTFRAME(data),           \\\n                            SRCFRAME(data)>& inv_jacobian);                    \\\n  template auto transform::to_different_frame(                                 \\\n      const tnsr::TENSOR(data) < DTYPE(data), DIM(data),                       \\\n      SRCFRAME(data) > &src,                                                   \\\n      const Jacobian<DTYPE(data), DIM(data), DESTFRAME(data), SRCFRAME(data)>& \\\n          jacobian,                                                            \\\n      const InverseJacobian<DTYPE(data), DIM(data), DESTFRAME(data),           \\\n                            SRCFRAME(data)>& inv_jacobian)                     \\\n      ->tnsr::TENSOR(data)<DTYPE(data), DIM(data), DESTFRAME(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3),\n                        (Frame::BlockLogical, Frame::ElementLogical,\n                         Frame::Grid, Frame::Distorted),\n                        (Frame::Inertial), (double, DataVector),\n                        (I, i, iJ, ii, II, ijj))\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Inertial),\n                        (Frame::BlockLogical, Frame::ElementLogical,\n                         Frame::Grid, Frame::Distorted),\n                        (double, DataVector), (I, i, iJ, ii, II, ijj))\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Inertial),\n                        (Frame::NoFrame), (double, DataVector),\n                        (I, i, iJ, ii, II, ijj))\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::NoFrame),\n                        (Frame::Inertial), (double, DataVector),\n                        (I, i, iJ, ii, II, ijj))\n\n#undef INSTANTIATE\n\n// Maps complex<double> -> double and ComplexDataVector -> DataVector\n// to create real Jacobian types in the instantiations below\ntemplate <typename T>\nusing make_real_t = std::conditional_t<\n    std::is_same_v<T, ComplexDataVector>, DataVector,\n    std::conditional_t<std::is_same_v<T, std::complex<double>>, double, T>>;\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void transform::first_index_to_different_frame(                     \\\n      const gsl::not_null<TensorMetafunctions::prepend_spatial_index<          \\\n                              TensorMetafunctions::remove_first_index<         \\\n                                  tnsr::TENSOR(data) < DTYPE(data), DIM(data), \\\n                                  DESTFRAME(data)>>,                           \\\n                          DIM(data), UpLo::Up, SRCFRAME(data)>* > result,      \\\n      const tnsr::TENSOR(data) < DTYPE(data), DIM(data),                       \\\n      DESTFRAME(data) > &input,                                                \\\n      const InverseJacobian<make_real_t<DTYPE(data)>, DIM(data),               \\\n                            SRCFRAME(data), DESTFRAME(data)>& inv_jacobian);   \\\n  template auto transform::first_index_to_different_frame(                     \\\n      const tnsr::TENSOR(data) < DTYPE(data), DIM(data),                       \\\n      DESTFRAME(data) > &input,                                                \\\n      const InverseJacobian<make_real_t<DTYPE(data)>, DIM(data),               \\\n                            SRCFRAME(data), DESTFRAME(data)>&inv_jacobian)     \\\n      ->TensorMetafunctions::prepend_spatial_index<                            \\\n          TensorMetafunctions::remove_first_index<                             \\\n              tnsr::TENSOR(data) < DTYPE(data), DIM(data), DESTFRAME(data)>>,  \\\n      DIM(data), UpLo::Up, SRCFRAME(data) > ;\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::ElementLogical),\n                        (Frame::Inertial),\n                        (double, std::complex<double>, DataVector,\n                         ComplexDataVector),\n                        (I, II, Ij, IJ, Iaa))\n\ntemplate void transform::first_index_to_different_frame(\n    gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3, Frame::Inertial>, 2, UpLo::Up,\n        Frame::ElementLogical>*>\n        result,\n    const TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3, Frame::Inertial>, 2, UpLo::Up,\n        Frame::Inertial>& input,\n    const InverseJacobian<DataVector, 2, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian);\n\ntemplate auto transform::first_index_to_different_frame(\n    const TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3, Frame::Inertial>, 2, UpLo::Up,\n        Frame::Inertial>& input,\n    const InverseJacobian<DataVector, 2, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian)\n    -> TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3, Frame::Inertial>, 2, UpLo::Up,\n        Frame::ElementLogical>;\n\n#undef DIM\n#undef SRCFRAME\n#undef DESTFRAME\n#undef TENSOR\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/FrameTransform.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\n/// Holds functions related to transforming between frames.\nnamespace transform {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * Transforms tensor to different frame.\n *\n * The formula for transforming \\f$T_{ij}\\f$ is\n * \\f{align}\n *   T_{\\bar{\\imath}\\bar{\\jmath}} &= T_{ij}\n *      \\frac{\\partial x^i}{\\partial x^{\\bar{\\imath}}}\n *      \\frac{\\partial x^j}{\\partial x^{\\bar{\\jmath}}}\n * \\f}\n * where \\f$x^i\\f$ are the source coordinates and\n * \\f$x^{\\bar{\\imath}}\\f$ are the destination coordinates.\n *\n * Note that `Jacobian<DestFrame,SrcFrame>` is the same type as\n * `InverseJacobian<SrcFrame,DestFrame>` and represents\n * \\f$\\partial x^i/\\partial x^{\\bar{\\jmath}}\\f$.\n */\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::ii<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::ii<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian);\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::ii<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian)\n    -> tnsr::ii<DataType, VolumeDim, DestFrame>;\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Transforms a tensor to a different frame.\n *\n * The tensor being transformed is always assumed to have density zero. In\n * particular `Scalar` is assumed to be invariant under transformations.\n *\n * \\warning The \\p jacobian argument is the derivative of the *source*\n * coordinates with respect to the *destination* coordinates.\n */\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<Scalar<DataType>*> dest, const Scalar<DataType>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian);\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    Scalar<DataType> src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> Scalar<DataType>;\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::I<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::I<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian);\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::I<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::I<DataType, VolumeDim, DestFrame>;\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::i<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::i<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian);\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::i<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::i<DataType, VolumeDim, DestFrame>;\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::iJ<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::iJ<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian);\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::iJ<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::iJ<DataType, VolumeDim, DestFrame>;\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::ii<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::ii<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian);\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::ii<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::ii<DataType, VolumeDim, DestFrame>;\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::II<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::II<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian);\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::II<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::II<DataType, VolumeDim, DestFrame>;\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nvoid to_different_frame(\n    const gsl::not_null<tnsr::ijj<DataType, VolumeDim, DestFrame>*> dest,\n    const tnsr::ijj<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian);\n\ntemplate <typename DataType, size_t VolumeDim, typename SrcFrame,\n          typename DestFrame>\nauto to_different_frame(\n    const tnsr::ijj<DataType, VolumeDim, SrcFrame>& src,\n    const Jacobian<DataType, VolumeDim, DestFrame, SrcFrame>& jacobian,\n    const InverseJacobian<DataType, VolumeDim, DestFrame, SrcFrame>&\n        inv_jacobian) -> tnsr::ijj<DataType, VolumeDim, DestFrame>;\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * Transforms only the first index to different frame.\n *\n * ## Examples for transforming some tensors\n *\n * ### Flux vector to logical coordinates\n *\n * A common example is taking the divergence of a flux vector $F^i$, where the\n * index $i$ is in inertial coordinates $x^i$ and the divergence is taken in\n * logical coordinates $\\xi^\\hat{i}$. So to transform the flux vector to logical\n * coordinates before taking the divergence we do this:\n *\n * \\f{align}\n *   F^\\hat{i} &= F^i \\frac{\\partial x^\\hat{i}}{\\partial x^i}\n * \\f}\n *\n * Here, $\\frac{\\partial x^\\hat{i}}{\\partial x^i}$ is the inverse Jacobian (see\n * definitions in TypeAliases.hpp).\n *\n * Currently, this function is tested for any tensor with a first upper spatial\n * index. It can be extended/generalized to other tensor types if needed.\n */\ntemplate <typename ResultTensor, typename InputTensor, typename DataType,\n          size_t Dim, typename SourceFrame, typename TargetFrame>\nvoid first_index_to_different_frame(\n    gsl::not_null<ResultTensor*> result, const InputTensor& input,\n    const InverseJacobian<DataType, Dim, SourceFrame, TargetFrame>&\n        inv_jacobian);\n\ntemplate <typename InputTensor, typename DataType, size_t Dim,\n          typename SourceFrame, typename TargetFrame,\n          typename ResultTensor = TensorMetafunctions::prepend_spatial_index<\n              TensorMetafunctions::remove_first_index<InputTensor>, Dim,\n              UpLo::Up, SourceFrame>>\nResultTensor first_index_to_different_frame(\n    const InputTensor& input,\n    const InverseJacobian<DataType, Dim, SourceFrame, TargetFrame>&\n        inv_jacobian);\n/// @}\n}  // namespace transform\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/GramSchmidtOrthonormalize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/EagerMath/GramSchmidtOrthonormalize.hpp\"\n\n#include <array>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n\ntemplate <typename DataType, typename Index, size_t NumVectors>\nvoid gram_schmidt_orthonormalize(\n    const std::array<\n        gsl::not_null<Tensor<DataType, Symmetry<1>, index_list<Index>>*>,\n        NumVectors>& basis,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index>,\n                            change_index_up_lo<Index>>>& metric) {\n  Scalar<DataType> buffer{};\n  // Keep track of the sign of each basis vector for spacetime metrics. Avoid\n  // unnecessary memory allocation for spatial metrics by using simple sign type\n  // (will not be used, so the type doesn't matter).\n  using sign_dtype =\n      std::conditional_t<Index::index_type == IndexType::Spacetime, DataType,\n                         int>;\n  std::array<sign_dtype, NumVectors> basis_signs{};\n  const auto normalize = [&metric, &norm = buffer](\n                             auto& v, [[maybe_unused]] auto& v_sign) {\n    dot_product(make_not_null(&norm), v, v, metric);\n    if constexpr (Index::index_type == IndexType::Spacetime) {\n      v_sign = sgn(get(norm));\n      get(norm) = 1.0 / sqrt(abs(get(norm)));\n    } else {\n      get(norm) = 1.0 / sqrt(get(norm));\n    }\n    for (size_t k = 0; k < v.size(); ++k) {\n      v.get(k) *= get(norm);\n    }\n  };\n  // Normalize the first vector.\n  normalize(*basis.at(0), basis_signs.at(0));\n  // Orthogonalize the remaining vectors\n  auto& projection = buffer;\n  for (size_t i = 1; i < basis.size(); ++i) {\n    auto& v = *basis.at(i);\n    for (size_t j = 0; j < i; ++j) {\n      auto& w = *basis.at(j);\n      dot_product(make_not_null(&projection), v, w, metric);\n      // w is already normalized, but need to account for sign in spacetime case\n      if constexpr (Index::index_type == IndexType::Spacetime) {\n        get(projection) *= basis_signs.at(j);\n      }\n      for (size_t k = 0; k < v.size(); ++k) {\n        v.get(k) -= get(projection) * w.get(k);\n      }\n    }\n    normalize(v, basis_signs.at(i));\n  }\n}\n\n// Instantiate for double and DataVector\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define UPLO(data) BOOST_PP_TUPLE_ELEM(3, data)\n#define INDEXTYPE(data) BOOST_PP_TUPLE_ELEM(4, data)\n#define NUMVEC(data) BOOST_PP_TUPLE_ELEM(5, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template void gram_schmidt_orthonormalize(                                \\\n      const std::array<                                                     \\\n          gsl::not_null<Tensor<                                             \\\n              DTYPE(data), Symmetry<1>,                                     \\\n              index_list<Tensor_detail::TensorIndexType<                    \\\n                  DIM(data), UPLO(data), FRAME(data), INDEXTYPE(data)>>>*>, \\\n          NUMVEC(data)>& basis,                                             \\\n      const Tensor<                                                         \\\n          DTYPE(data), Symmetry<1, 1>,                                      \\\n          index_list<                                                       \\\n              change_index_up_lo<Tensor_detail::TensorIndexType<            \\\n                  DIM(data), UPLO(data), FRAME(data), INDEXTYPE(data)>>,    \\\n              change_index_up_lo<Tensor_detail::TensorIndexType<            \\\n                  DIM(data), UPLO(data), FRAME(data), INDEXTYPE(data)>>>>&  \\\n          metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (double), (3), (Frame::Inertial),\n                        (UpLo::Lo, UpLo::Up),\n                        (IndexType::Spacetime, IndexType::Spatial), (3))\n\n#undef DTYPE\n#undef DIM\n#undef FRAME\n#undef UPLO\n#undef INDEXTYPE\n#undef NUMVEC\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/GramSchmidtOrthonormalize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/*!\n * \\brief Orthonormalize a set of vectors using the modified Gram-Schmidt\n * process.\n *\n * The first vector in `basis` is normalized, then each subsequent vector is\n * orthogonalized against all previous vectors and then normalized.\n */\ntemplate <typename DataType, typename Index, size_t NumVectors>\nvoid gram_schmidt_orthonormalize(\n    const std::array<\n        gsl::not_null<Tensor<DataType, Symmetry<1>, index_list<Index>>*>,\n        NumVectors>& basis,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index>,\n                            change_index_up_lo<Index>>>& metric);\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Magnitude.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the Euclidean magnitude of a rank-1 tensor\n *\n * \\details\n * Computes the square root of the sum of the squares of the components of\n * the rank-1 tensor.\n */\ntemplate <typename DataType, typename Index>\nScalar<DataType> magnitude(\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector) {\n  return Scalar<DataType>{sqrt(get(dot_product(vector, vector)))};\n}\n\ntemplate <typename DataType, typename Index>\nvoid magnitude(const gsl::not_null<Scalar<DataType>*> magnitude,\n               const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector) {\n  set_number_of_grid_points(magnitude, vector);\n  dot_product(magnitude, vector, vector);\n  get(*magnitude) = sqrt(get(*magnitude));\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute the magnitude of a rank-1 tensor\n *\n * \\details\n * Returns the square root of the input tensor contracted twice with the given\n * metric. The absolute value is taken of the dot product to ensure a real\n * result for spacetime (co-)vectors.\n */\ntemplate <typename DataType, typename Index>\nScalar<DataType> magnitude(\n    const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index>,\n                            change_index_up_lo<Index>>>& metric) {\n  Scalar<DataType> local_magnitude{get_size(get<0>(vector))};\n  magnitude(make_not_null(&local_magnitude), vector, metric);\n  return local_magnitude;\n}\n\ntemplate <typename DataType, typename Index>\nvoid magnitude(const gsl::not_null<Scalar<DataType>*> magnitude,\n               const Tensor<DataType, Symmetry<1>, index_list<Index>>& vector,\n               const Tensor<DataType, Symmetry<1, 1>,\n                            index_list<change_index_up_lo<Index>,\n                                       change_index_up_lo<Index>>>& metric) {\n  dot_product(magnitude, vector, vector, metric);\n  if constexpr (Index::index_type == IndexType::Spacetime) {\n    // Spacetime dot product can be negative, so take the absolute value to\n    // define the magnitude\n    get(*magnitude) = sqrt(abs(get(*magnitude)));\n  } else {\n    get(*magnitude) = sqrt(get(*magnitude));\n  }\n}\n/// @}\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup DataStructuresGroup\n/// The magnitude of a (co)vector\ntemplate <typename Tag>\nstruct Magnitude : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = Scalar<DataVector>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup DataStructuresGroup\n/// The Euclidean magnitude of a (co)vector\n///\n/// This tag inherits from `Tags::Magnitude<Tag>`\ntemplate <typename Tag>\nstruct EuclideanMagnitude : Magnitude<Tag>, db::ComputeTag {\n  using base = Magnitude<Tag>;\n  using return_type = typename base::type;\n  static constexpr auto function =\n      static_cast<void (*)(const gsl::not_null<return_type*>,\n                           const typename Tag::type&)>(&magnitude);\n  using argument_tags = tmpl::list<Tag>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup DataStructuresGroup\n/// The magnitude of a (co)vector with respect to a specific metric\n///\n/// This tag inherits from `Tags::Magnitude<Tag>`\ntemplate <typename Tag, typename MetricTag>\nstruct NonEuclideanMagnitude : Magnitude<Tag>, db::ComputeTag {\n  using base = Magnitude<Tag>;\n  using return_type = typename base::type;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<return_type*>, const typename Tag::type&,\n      const typename MetricTag::type&)>(&magnitude);\n  using argument_tags = tmpl::list<Tag, MetricTag>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup DataStructuresGroup\n/// The normalized (co)vector represented by Tag\ntemplate <typename Tag>\nstruct Normalized : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = typename Tag::type;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup DataStructuresGroup\n/// Normalizes the (co)vector represented by Tag\n///\n/// This tag inherits from `Tags::Normalized<Tag>`\ntemplate <typename Tag>\nstruct NormalizedCompute : Normalized<Tag>, db::ComputeTag {\n  using base = Normalized<Tag>;\n  using return_type = typename base::type;\n  static void function(const gsl::not_null<return_type*> normalized_vector,\n                       const typename Tag::type& vector_in,\n                       const typename Magnitude<Tag>::type& magnitude) {\n    *normalized_vector = vector_in;\n    for (size_t d = 0; d < normalized_vector->index_dim(0); ++d) {\n      normalized_vector->get(d) /= get(magnitude);\n    }\n  }\n  using argument_tags = tmpl::list<Tag, Magnitude<Tag>>;\n};\n\n}  // namespace Tags\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Norms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace L2Norm_detail {\ntemplate <typename DataType, typename Symm, typename IndexList>\nScalar<DataType> pointwise_l2_norm_square(\n    const Tensor<DataType, Symm, IndexList>& tensor) {\n  auto pointwise_l2_normsq = make_with_value<Scalar<DataType>>(tensor, 0.);\n  for (auto tensor_element = tensor.begin(); tensor_element != tensor.end();\n       ++tensor_element) {\n    // In order to handle tensory symmetries, we multiply the square of each\n    // stored component with its multiplicity\n    get(pointwise_l2_normsq) +=\n        tensor.multiplicity(tensor_element) * square(*tensor_element);\n  }\n  return pointwise_l2_normsq;\n}\n}  // namespace L2Norm_detail\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute point-wise Euclidean \\f$L^2\\f$-norm of arbitrary Tensors.\n *\n * \\details\n * At each grid point \\f$p\\f$ in the element, this function computes the\n * point-wise Frobenius norm of a given Tensor with arbitrary rank. If the\n * Tensor \\f$A\\f$ has rank \\f$n\\f$ and dimensionality \\f$D\\f$, then its\n * Frobenius norm at point \\f$p\\f$ is computed as:\n *\n * \\f{equation}\n * ||A||_2(p) =\n *    \\left(\\sum^{D-1}_{i_1=0}\\sum^{D-1}_{i_2=0}\\cdots \\sum^{D-1}_{i_n=0}\n *          |A_{i_1 i_2 \\cdots i_n}(p)|^2 \\right)^{1/2},\n * \\f}\n *\n * where both contra-variant and co-variant indices are shown as lower indices.\n */\ntemplate <typename DataType, typename Symm, typename IndexList>\nScalar<DataType> pointwise_l2_norm(\n    const Tensor<DataType, Symm, IndexList>& tensor) {\n  return Scalar<DataType>{\n      sqrt(get(L2Norm_detail::pointwise_l2_norm_square(tensor)))};\n}\ntemplate <typename DataType, typename Symm, typename IndexList>\nvoid pointwise_l2_norm(const gsl::not_null<Scalar<DataType>*> norm,\n                       const Tensor<DataType, Symm, IndexList>& tensor) {\n  get(*norm) = sqrt(get(L2Norm_detail::pointwise_l2_norm_square(tensor)));\n}\n/// @}\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Compute Euclidean \\f$L^2\\f$-norm of arbitrary Tensors reduced over an\n * element.\n *\n * \\details\n * Computes the RMS value of the point-wise Frobenius norm of a given Tensor\n * with arbitrary rank over all grid points in an element. If the Tensor \\f$A\\f$\n * has rank \\f$n\\f$ and dimensionality \\f$D\\f$, and the element (of order\n * \\f$N\\f$) has \\f$N+1\\f$ points, then its element-reduced Frobenius norm is\n * computed as:\n *\n * \\f{equation}\n *  ||A||_2 =\n *     \\left(\\frac{1}{N+1}\\sum^{N}_{p=0}\n *          \\left(\\sum^{D-1}_{i_1=0}\\sum^{D-1}_{i_2=0}\\cdots\n *                \\sum^{D-1}_{i_n=0} |A^p_{i_1 i_2 \\cdots i_n}|^2 \\right)\n * \\right)^{1/2},\n * \\f}\n *\n * where both contra-variant and co-variant indices are shown as lower indices,\n * and \\f$p\\f$ indexes grid points in the element.\n *\n * \\warning This function reduces the Frobenius norm over the element, not the\n * whole domain.\n */\ntemplate <typename DataType, typename Symm, typename IndexList>\ndouble l2_norm(const Tensor<DataType, Symm, IndexList>& tensor) {\n  const auto pointwise_l2_normsq =\n      L2Norm_detail::pointwise_l2_norm_square(tensor);\n  using Plus = funcl::Plus<funcl::Identity>;\n  return sqrt(alg::accumulate(get(pointwise_l2_normsq), 0., Plus{}) /\n              tensor.begin()->size());\n}\n\nnamespace Tags {\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\ingroup DataStructuresGroup\n * Point-wise Euclidean \\f$L^2\\f$-norm of a Tensor.\n * \\see `pointwise_l2_norm()` for details.\n */\ntemplate <typename Tag>\nstruct PointwiseL2Norm : db::SimpleTag {\n  using type = Scalar<typename Tag::type::type>;\n  static std::string name() {\n    return \"PointwiseL2Norm(\" + db::tag_name<Tag>() + \")\";\n  }\n};\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\ingroup DataStructuresGroup\n * Computes the point-wise Euclidean \\f$L^2\\f$-norm of a Tensor.\n * \\see `pointwise_l2_norm()` for details.\n */\ntemplate <typename Tag>\nstruct PointwiseL2NormCompute : PointwiseL2Norm<Tag>, db::ComputeTag {\n  using base = PointwiseL2Norm<Tag>;\n  using return_type = typename base::type;\n  static constexpr auto function =\n      static_cast<void (*)(const gsl::not_null<return_type*>,\n                           const typename Tag::type&)>(&pointwise_l2_norm);\n  using argument_tags = tmpl::list<Tag>;\n};\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\ingroup DataStructuresGroup\n * Euclidean \\f$L^2\\f$-norm of a Tensor, RMS over grid points in element.\n * \\see `l2_norm()` for details.\n */\ntemplate <typename Tag>\nstruct L2Norm : db::SimpleTag {\n  using type = double;\n  static std::string name() { return \"L2Norm(\" + db::tag_name<Tag>() + \")\"; }\n};\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\ingroup DataStructuresGroup\n * Computes the Euclidean \\f$L^2\\f$-norm of a Tensor, RMS over grid points in\n * element. \\see `l2_norm()` for details.\n *\n * \\warning This compute tag reduces the Frobenius norm over the element, not\n * the whole domain.\n */\ntemplate <typename Tag>\nstruct L2NormCompute : L2Norm<Tag>, db::ComputeTag {\n  using base = L2Norm<Tag>;\n  using return_type = typename base::type;\n  static void function(const gsl::not_null<return_type*> norm,\n                       const typename Tag::type& tensor) {\n    *norm = l2_norm(tensor);\n  }\n  using argument_tags = tmpl::list<Tag>;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/OrthonormalOneform.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/EagerMath/OrthonormalOneform.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/CrossProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid orthonormal_oneform(\n    const gsl::not_null<tnsr::i<DataType, VolumeDim, Frame>*> orthonormal_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& unit_form,\n    const tnsr::II<DataType, VolumeDim, Frame>& inv_spatial_metric) {\n  *orthonormal_form = unit_form;\n  const size_t number_of_points = get_size(get<0>(unit_form));\n  for (size_t s = 0; s < number_of_points; ++s) {\n    size_t min_index = 0;\n    for (size_t i = 1; i < VolumeDim; ++i) {\n      if (std::abs(get_element(unit_form.get(i), s)) <\n          std::abs(get_element(unit_form.get(min_index), s))) {\n        min_index = i;\n      }\n    }\n\n    double proj = 0.0;\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      proj += (get_element(inv_spatial_metric.get(min_index, i), s) *\n               get_element(unit_form.get(i), s));\n    }\n    const double inv_magnitude =\n        1.0 /\n        sqrt(get_element(inv_spatial_metric.get(min_index, min_index), s) -\n             square(proj));\n\n    proj *= -inv_magnitude;\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      get_element(orthonormal_form->get(i), s) *= proj;\n      if (i == min_index) {\n        get_element(orthonormal_form->get(i), s) += inv_magnitude;\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::i<DataType, VolumeDim, Frame> orthonormal_oneform(\n    const tnsr::i<DataType, VolumeDim, Frame>& unit_form,\n    const tnsr::II<DataType, VolumeDim, Frame>& inv_spatial_metric) {\n  tnsr::i<DataType, VolumeDim, Frame> orthonormal_form{};\n  orthonormal_oneform(make_not_null(&orthonormal_form), unit_form,\n                      inv_spatial_metric);\n  return orthonormal_form;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid orthonormal_oneform(\n    gsl::not_null<tnsr::i<DataType, 3, Frame>*> orthonormal_form,\n    const tnsr::i<DataType, 3, Frame>& first_unit_form,\n    const tnsr::i<DataType, 3, Frame>& second_unit_form,\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const Scalar<DataType>& det_spatial_metric) {\n  *orthonormal_form = cross_product(first_unit_form, second_unit_form,\n                                    spatial_metric, det_spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::i<DataType, 3, Frame> orthonormal_oneform(\n    const tnsr::i<DataType, 3, Frame>& first_unit_form,\n    const tnsr::i<DataType, 3, Frame>& second_unit_form,\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const Scalar<DataType>& det_spatial_metric) {\n  tnsr::i<DataType, 3, Frame> orthonormal_form{};\n  orthonormal_oneform(make_not_null(&orthonormal_form), first_unit_form,\n                      second_unit_form, spatial_metric, det_spatial_metric);\n  return orthonormal_form;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE_FIRST_FORM(_, data)                                      \\\n  template void orthonormal_oneform(                                         \\\n      const gsl::not_null<tnsr::i<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          orthonormal_form,                                                  \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& unit_form,         \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inv_spatial_metric);                                               \\\n  template tnsr::i<DTYPE(data), DIM(data), FRAME(data)> orthonormal_oneform( \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& unit_form,         \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inv_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_FIRST_FORM, (double, DataVector),\n                        (Frame::Inertial), (2, 3))\n\n#define INSTANTIATE_SECOND_FORM(_, data)                             \\\n  template void orthonormal_oneform(                                 \\\n      const gsl::not_null<tnsr::i<DTYPE(data), 3, FRAME(data)>*>     \\\n          orthonormal_form,                                          \\\n      const tnsr::i<DTYPE(data), 3, FRAME(data)>& first_unit_form,   \\\n      const tnsr::i<DTYPE(data), 3, FRAME(data)>& second_unit_form,  \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& spatial_metric,   \\\n      const Scalar<DTYPE(data)>& det_spatial_metric);                \\\n  template tnsr::i<DTYPE(data), 3, FRAME(data)> orthonormal_oneform( \\\n      const tnsr::i<DTYPE(data), 3, FRAME(data)>& first_unit_form,   \\\n      const tnsr::i<DTYPE(data), 3, FRAME(data)>& second_unit_form,  \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& spatial_metric,   \\\n      const Scalar<DTYPE(data)>& det_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SECOND_FORM, (double, DataVector),\n                        (Frame::Inertial))\n\n#undef INSTANTIATE_SECOND_FORM\n#undef INSTANTIATE_FIRST_FORM\n#undef DIM\n#undef FRAME\n#undef DTYPE\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/OrthonormalOneform.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n *\n * \\brief Compute a spatial one-form orthonormal to the given unit form\n *\n * Given a unit spatial one-form \\f$s_i\\f$, compute a new form \\f$t_i\\f$\n * which is orthonormal to \\f$s_i\\f$, in the sense that\n * \\f$\\gamma^{ij}s_i t_j = 0\\f$, for the given inverse spatial metric\n * \\f$\\gamma^{ij}\\f$. The normalization of \\f$t_i\\f$ is such that\n * \\f$\\gamma^{ij}t_it_j = 1\\f$.\n *\n * \\details The new form is obtained via Gram-Schmidt process, starting\n * from a form whose components are \\f$t_i = \\delta_i^I\\f$, where \\f$I\\f$ is\n * the index of the component of \\f$s_i\\f$ with the smallest absolute value.\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid orthonormal_oneform(\n    gsl::not_null<tnsr::i<DataType, VolumeDim, Frame>*> orthonormal_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& unit_form,\n    const tnsr::II<DataType, VolumeDim, Frame>& inv_spatial_metric);\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::i<DataType, VolumeDim, Frame> orthonormal_oneform(\n    const tnsr::i<DataType, VolumeDim, Frame>& unit_form,\n    const tnsr::II<DataType, VolumeDim, Frame>& inv_spatial_metric);\n/// @}\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n *\n * \\brief Compute a spatial one-form orthonormal to two given unit forms.\n *\n * Given a unit spatial one-form \\f$s_i\\f$ and another form \\f$t_i\\f$ that is\n * orthonormal to \\f$s_i\\f$, compute a new form \\f$u_i\\f$ which is orthonormal\n * to both \\f$s_i\\f$ and \\f$t_i\\f$, in the sense that\n * \\f$\\gamma^{ij}s_i u_j = \\gamma^{ij}t_i u_j = 0\\f$, for the given\n * inverse spatial metric \\f$\\gamma^{ij}\\f$. The normalization of \\f$u_i\\f$\n * is such that \\f$\\gamma^{ij}u_iu_j = 1\\f$.\n *\n * \\details The new form is obtained by taking the covariant cross product\n * of \\f$s_i\\f$ and \\f$ t_i\\f$, for which the spatial metric as well as\n * its determinant must be provided.\n */\ntemplate <typename DataType, typename Frame>\nvoid orthonormal_oneform(\n    gsl::not_null<tnsr::i<DataType, 3, Frame>*> orthonormal_form,\n    const tnsr::i<DataType, 3, Frame>& first_unit_form,\n    const tnsr::i<DataType, 3, Frame>& second_unit_form,\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const Scalar<DataType>& det_spatial_metric);\n\ntemplate <typename DataType, typename Frame>\ntnsr::i<DataType, 3, Frame> orthonormal_oneform(\n    const tnsr::i<DataType, 3, Frame>& first_unit_form,\n    const tnsr::i<DataType, 3, Frame>& second_unit_form,\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const Scalar<DataType>& det_spatial_metric);\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/OuterProduct.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\brief The `Tensor` type resulting from the outer product of two tensors\n *\n * \\see outer_product\n */\ntemplate <typename DataType, typename SymmLhs, typename IndicesLhs,\n          typename SymmRhs, typename IndicesRhs>\nusing OuterProductResultTensor = Tensor<\n    DataType,\n    tmpl::append<\n        tmpl::transform<\n            SymmLhs,\n            tmpl::plus<tmpl::_1,\n                       tmpl::fold<SymmRhs, tmpl::int32_t<0>,\n                                  tmpl::max<tmpl::_state, tmpl::_element>>>>,\n        SymmRhs>,\n    tmpl::append<IndicesLhs, IndicesRhs>>;\n\n/// @{\n/*!\n * \\ingroup TensorGroup\n * \\brief The outer product (or tensor product) of two tensors\n *\n * \\details\n * Computes $A_{i\\ldots j\\ldots} = B_{i\\ldots} C_{j\\ldots}$ for two tensors\n * $B_{i\\ldots}$ and $C_{j\\ldots}$. Both tensors can have arbitrary indices and\n * symmetries.\n */\ntemplate <typename DataTypeLhs, typename SymmLhs, typename IndicesLhs,\n          typename DataTypeRhs, typename SymmRhs, typename IndicesRhs,\n          typename DataTypeResult = decltype(blaze::evaluate(DataTypeLhs() *\n                                                             DataTypeRhs()))>\nvoid outer_product(\n    const gsl::not_null<OuterProductResultTensor<\n        DataTypeResult, SymmLhs, IndicesLhs, SymmRhs, IndicesRhs>*>\n        result,\n    const Tensor<DataTypeLhs, SymmLhs, IndicesLhs>& lhs,\n    const Tensor<DataTypeRhs, SymmRhs, IndicesRhs>& rhs) {\n  for (auto it_lhs = lhs.begin(); it_lhs != lhs.end(); ++it_lhs) {\n    for (auto it_rhs = rhs.begin(); it_rhs != rhs.end(); ++it_rhs) {\n      const auto result_indices = concatenate(lhs.get_tensor_index(it_lhs),\n                                              rhs.get_tensor_index(it_rhs));\n      result->get(result_indices) = *it_lhs * *it_rhs;\n    }\n  }\n}\n\ntemplate <typename DataTypeLhs, typename SymmLhs, typename IndicesLhs,\n          typename DataTypeRhs, typename SymmRhs, typename IndicesRhs,\n          typename DataTypeResult = decltype(blaze::evaluate(DataTypeLhs() *\n                                                             DataTypeRhs()))>\nauto outer_product(const Tensor<DataTypeLhs, SymmLhs, IndicesLhs>& lhs,\n                   const Tensor<DataTypeRhs, SymmRhs, IndicesRhs>& rhs)\n    -> OuterProductResultTensor<DataTypeResult, SymmLhs, IndicesLhs, SymmRhs,\n                                IndicesRhs> {\n  OuterProductResultTensor<DataTypeResult, SymmLhs, IndicesLhs, SymmRhs,\n                           IndicesRhs>\n      result{};\n  ::outer_product(make_not_null(&result), lhs, rhs);\n  return result;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"DataStructures/Tensor/EagerMath/Python/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Python/Magnitude.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py_bindings::bind_determinant(m);\n  py_bindings::bind_magnitude(m);\n}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyTensorEagerMath\")\n\nspectre_python_add_module(\n  EagerMath\n  MODULE_PATH \"DataStructures/Tensor\"\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  Determinant.cpp\n  Magnitude.cpp\n  PYTHON_FILES\n  __init__.py\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Determinant.hpp\n  Magnitude.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  pybind11::module\n  PythonBindings\n  Utilities\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Python/Determinant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/EagerMath/Python/Determinant.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\n\nnamespace {\ntemplate <typename TensorType>\nvoid bind_determinant_impl(py::module& m) {  // NOLINT\n  m.def(\n      \"determinant\",\n      [](const TensorType& tensor) { return determinant(tensor); },\n      py::arg(\"tensor\"));\n  m.def(\n      \"determinant_and_inverse\",\n      [](const TensorType& tensor) { return determinant_and_inverse(tensor); },\n      py::arg(\"tensor\"));\n}\n}  // namespace\n\nvoid bind_determinant(py::module& m) {\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define TENSOR(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE_TNSR(_, data)                                      \\\n  bind_determinant_impl < tnsr::TENSOR(data) < DTYPE(data), DIM(data), \\\n      FRAME(data) >> (m);\n#define INSTANTIATE_JAC(_, data)                                 \\\n  bind_determinant_impl < TENSOR(data) < DTYPE(data), DIM(data), \\\n      Frame::ElementLogical, FRAME(data) >> (m);\n\n  GENERATE_INSTANTIATIONS(INSTANTIATE_TNSR, (double, DataVector), (1, 2, 3),\n                          (Frame::Inertial), (ij, ii, II))\n  GENERATE_INSTANTIATIONS(INSTANTIATE_JAC, (double, DataVector), (1, 2, 3),\n                          (Frame::Inertial), (Jacobian, InverseJacobian))\n\n#undef INSTANTIATE_TNSR\n#undef INSTANTIATE_JAC\n#undef DTYPE\n#undef DIM\n#undef FRAME\n#undef TENSOR\n}\n\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Python/Determinant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_determinant(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Python/Magnitude.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/EagerMath/Python/Magnitude.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\n\nnamespace {\ntemplate <typename TensorType>\nvoid bind_magnitude_impl(py::module& m) {  // NOLINT\n  m.def(\n      \"magnitude\", [](const TensorType& vector) { return magnitude(vector); },\n      py::arg(\"vector\"));\n  using MetricIndex =\n      change_index_up_lo<tmpl::front<typename TensorType::index_list>>;\n  using MetricType = Tensor<typename TensorType::type, Symmetry<1, 1>,\n                            index_list<MetricIndex, MetricIndex>>;\n  m.def(\n      \"magnitude\",\n      [](const TensorType& vector, const MetricType& metric) {\n        return magnitude(vector, metric);\n      },\n      py::arg(\"vector\"), py::arg(\"metric\"));\n}\n}  // namespace\n\nvoid bind_magnitude(py::module& m) {  // NOLINT\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define TENSOR(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE_TNSR(_, data)                                    \\\n  bind_magnitude_impl < tnsr::TENSOR(data) < DTYPE(data), DIM(data), \\\n      FRAME(data) >> (m);\n\n  GENERATE_INSTANTIATIONS(INSTANTIATE_TNSR, (double, DataVector), (1, 2, 3),\n                          (Frame::Inertial), (i, I))\n\n#undef INSTANTIATE_TNSR\n#undef DTYPE\n#undef DIM\n#undef FRAME\n#undef TENSOR\n}\n\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Python/Magnitude.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_magnitude(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n\ntemplate <typename DataType, typename Index0, typename Index1>\nvoid raise_or_lower_first_index(\n    const gsl::not_null<\n        Tensor<DataType, Symmetry<2, 1, 1>,\n               index_list<change_index_up_lo<Index0>, Index1, Index1>>*>\n        result,\n    const Tensor<DataType, Symmetry<2, 1, 1>,\n                 index_list<Index0, Index1, Index1>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric) {\n  constexpr auto dimension = Index0::dim;\n\n  for (size_t i = 0; i < dimension; ++i) {\n    for (size_t j = 0; j < dimension; ++j) {\n      for (size_t k = j; k < dimension; ++k) {\n        result->get(i, j, k) = tensor.get(0, j, k) * metric.get(i, 0);\n        for (size_t m = 1; m < dimension; ++m) {\n          result->get(i, j, k) += tensor.get(m, j, k) * metric.get(i, m);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataTypeTensor, typename DataTypeMetric, typename Index0>\nvoid raise_or_lower_index(\n    const gsl::not_null<Tensor<DataTypeTensor, Symmetry<1>,\n                               index_list<change_index_up_lo<Index0>>>*>\n        result,\n    const Tensor<DataTypeTensor, Symmetry<1>, index_list<Index0>>& tensor,\n    const Tensor<DataTypeMetric, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric) {\n  constexpr auto dimension = Index0::dim;\n\n  for (size_t i = 0; i < dimension; ++i) {\n    result->get(i) = get<0>(tensor) * metric.get(i, 0);\n    for (size_t m = 1; m < dimension; ++m) {\n      result->get(i) += tensor.get(m) * metric.get(i, m);\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define INDEXTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n#define UPORLO0(data) BOOST_PP_TUPLE_ELEM(4, data)\n#define UPORLO1(data) BOOST_PP_TUPLE_ELEM(5, data)\n\n// Note: currently only instantiate these functions for the cases\n// where Index0 and Index1 have the same spatial dimension, Frame and\n// IndexType.  The functions above are generic and can handle any of\n// them being different, if these cases are needed.\n#define INDEX0(data) INDEXTYPE(data)<DIM(data), UPORLO0(data), FRAME(data)>\n#define INDEX1(data) INDEXTYPE(data)<DIM(data), UPORLO1(data), FRAME(data)>\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void raise_or_lower_first_index(                                   \\\n      const gsl::not_null<Tensor<DTYPE(data), Symmetry<2, 1, 1>,              \\\n                                 index_list<change_index_up_lo<INDEX0(data)>, \\\n                                            INDEX1(data), INDEX1(data)>>*>    \\\n          result,                                                             \\\n      const Tensor<DTYPE(data), Symmetry<2, 1, 1>,                            \\\n                   index_list<INDEX0(data), INDEX1(data), INDEX1(data)>>&     \\\n          tensor,                                                             \\\n      const Tensor<DTYPE(data), Symmetry<1, 1>,                               \\\n                   index_list<change_index_up_lo<INDEX0(data)>,               \\\n                              change_index_up_lo<INDEX0(data)>>>& metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial,\n                         Frame::Spherical<Frame::Inertial>,\n                         Frame::Spherical<Frame::Distorted>,\n                         Frame::Spherical<Frame::Grid>),\n                        (SpatialIndex, SpacetimeIndex), (UpLo::Lo, UpLo::Up),\n                        (UpLo::Lo, UpLo::Up))\n\n#undef FRAME1\n#undef UPORLO1\n#undef INDEXTYPE1\n#undef INDEX1\n#undef INSTANTIATE\n\n// Maps complex<double> -> double and ComplexDataVector -> DataVector\n// to create real metric types in the instantiations below\ntemplate <typename T>\nusing make_real_t = std::conditional_t<\n    std::is_same_v<T, ComplexDataVector>, DataVector,\n    std::conditional_t<std::is_same_v<T, std::complex<double>>, double, T>>;\n\n#define INSTANTIATE2(_, data)                                           \\\n  template void raise_or_lower_index(                                   \\\n      const gsl::not_null<                                              \\\n          Tensor<DTYPE(data), Symmetry<1>,                              \\\n                 index_list<change_index_up_lo<INDEX0(data)>>>*>        \\\n          trace_of_tensor,                                              \\\n      const Tensor<DTYPE(data), Symmetry<1>, index_list<INDEX0(data)>>& \\\n          tensor,                                                       \\\n      const Tensor<make_real_t<DTYPE(data)>, Symmetry<1, 1>,            \\\n                   index_list<change_index_up_lo<INDEX0(data)>,         \\\n                              change_index_up_lo<INDEX0(data)>>>& metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE2, (1, 2, 3),\n                        (double, DataVector, std::complex<double>,\n                         ComplexDataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial),\n                        (SpatialIndex, SpacetimeIndex), (UpLo::Lo, UpLo::Up))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME0\n#undef UPORLO0\n#undef INDEXTYPE0\n#undef INDEX0\n#undef INSTANTIATE2\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Raises or lowers the first index of a rank 3 tensor which is symmetric\n * in the last two indices.\n *\n * \\details If \\f$T_{abc}\\f$ is a tensor with \\f$T_{abc} = T_{acb}\\f$ and the\n * indices \\f$a,b,c,...\\f$ can represent either spatial or spacetime indices,\n * then the tensor \\f$ T^a_{bc} = g^{ad} T_{abc} \\f$ is computed, where \\f$\n * g^{ab}\\f$ is the inverse metric, which is either a spatial or spacetime\n * metric. If a tensor \\f$ S^a_{bc} \\f$ is passed as an argument than the\n * corresponding tensor \\f$ S_{abc} \\f$ is calculated with respect to the metric\n * \\f$g_{ab}\\f$.  You may have to add a new instantiation of this template if\n * you need a new use case.\n */\ntemplate <typename DataType, typename Index0, typename Index1>\nvoid raise_or_lower_first_index(\n    gsl::not_null<\n        Tensor<DataType, Symmetry<2, 1, 1>,\n               index_list<change_index_up_lo<Index0>, Index1, Index1>>*>\n        result,\n    const Tensor<DataType, Symmetry<2, 1, 1>,\n                 index_list<Index0, Index1, Index1>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric);\n\ntemplate <typename DataType, typename Index0, typename Index1>\nTensor<DataType, Symmetry<2, 1, 1>,\n       index_list<change_index_up_lo<Index0>, Index1, Index1>>\nraise_or_lower_first_index(\n    const Tensor<DataType, Symmetry<2, 1, 1>,\n                 index_list<Index0, Index1, Index1>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric) {\n  auto result = make_with_value<\n      Tensor<DataType, Symmetry<2, 1, 1>,\n             index_list<change_index_up_lo<Index0>, Index1, Index1>>>(metric,\n                                                                      0.);\n  raise_or_lower_first_index(make_not_null(&result), tensor, metric);\n  return result;\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Raises or lowers the index of a rank 1 tensor.\n *\n * \\details If \\f$T_{a}\\f$ is a tensor and the\n * index \\f$a\\f$ can represent either a spatial or spacetime index,\n * then the tensor \\f$ T^a = g^{ad} T_{d} \\f$ is computed, where \\f$\n * g^{ab}\\f$ is the inverse metric, which is either a spatial or spacetime\n * metric. If a tensor \\f$ S^a \\f$ is passed as an argument than the\n * corresponding tensor \\f$ S_{a} \\f$ is calculated with respect to the metric\n * \\f$g_{ab}\\f$.\n */\ntemplate <typename DataTypeTensor, typename DataTypeMetric, typename Index0>\nvoid raise_or_lower_index(\n    gsl::not_null<Tensor<DataTypeTensor, Symmetry<1>,\n                         index_list<change_index_up_lo<Index0>>>*>\n        result,\n    const Tensor<DataTypeTensor, Symmetry<1>, index_list<Index0>>& tensor,\n    const Tensor<DataTypeMetric, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric);\n\ntemplate <typename DataTypeTensor, typename DataTypeMetric, typename Index0>\nTensor<DataTypeTensor, Symmetry<1>, index_list<change_index_up_lo<Index0>>>\nraise_or_lower_index(\n    const Tensor<DataTypeTensor, Symmetry<1>, index_list<Index0>>& tensor,\n    const Tensor<DataTypeMetric, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric) {\n  auto result = make_with_value<Tensor<DataTypeTensor, Symmetry<1>,\n                                       index_list<change_index_up_lo<Index0>>>>(\n      metric, 0.);\n  raise_or_lower_index(make_not_null(&result), tensor, metric);\n  return result;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Trace.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\ntemplate <typename DataType, typename Index0, typename Index1>\nvoid trace_last_indices(\n    const gsl::not_null<Tensor<DataType, Symmetry<1>, index_list<Index0>>*>\n        trace_of_tensor,\n    const Tensor<DataType, Symmetry<2, 1, 1>,\n                 index_list<Index0, Index1, Index1>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index1>,\n                            change_index_up_lo<Index1>>>& metric) {\n  constexpr auto dimension_of_trace = Index0::dim;\n  constexpr auto dimension_of_metric = Index1::dim;\n  for (size_t i = 0; i < dimension_of_trace; ++i) {\n    trace_of_tensor->get(i) = tensor.get(i, 0, 0) * get<0, 0>(metric);\n    for (size_t j = 0; j < dimension_of_metric; ++j) {\n      if (j != 0) {\n        trace_of_tensor->get(i) += tensor.get(i, j, j) * metric.get(j, j);\n      }\n      for (size_t k = j + 1; k < dimension_of_metric; ++k) {\n        trace_of_tensor->get(i) += 2.0 * tensor.get(i, j, k) * metric.get(j, k);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Index0>\nvoid trace(\n    const gsl::not_null<Scalar<DataType>*> trace,\n    const Tensor<DataType, Symmetry<1, 1>, index_list<Index0, Index0>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric) {\n  constexpr auto dimension = Index0::dim;\n  get(*trace) = get<0, 0>(tensor) * get<0, 0>(metric);\n  for (size_t i = 0; i < dimension; ++i) {\n    if (i != 0) {\n      get(*trace) += tensor.get(i, i) * metric.get(i, i);\n    }\n    for (size_t j = i + 1; j < dimension; ++j) {  // symmetry\n      get(*trace) += 2.0 * tensor.get(i, j) * metric.get(i, j);\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define INDEXTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n#define UPORLO0(data) BOOST_PP_TUPLE_ELEM(4, data)\n#define UPORLO1(data) BOOST_PP_TUPLE_ELEM(5, data)\n\n// Note: currently only instantiate these functions for the cases\n// where Index0 and Index1 have the same spatial dimension, Frame and\n// IndexType.  The functions above are generic and can handle any of\n// them being different, if these cases are needed.\n#define INDEX0(data) INDEXTYPE(data)<DIM(data), UPORLO0(data), FRAME(data)>\n#define INDEX1(data) INDEXTYPE(data)<DIM(data), UPORLO1(data), FRAME(data)>\n\n#define INSTANTIATE(_, data)                                              \\\n  template void trace_last_indices(                                       \\\n      const gsl::not_null<                                                \\\n          Tensor<DTYPE(data), Symmetry<1>, index_list<INDEX0(data)>>*>    \\\n          result,                                                         \\\n      const Tensor<DTYPE(data), Symmetry<2, 1, 1>,                        \\\n                   index_list<INDEX0(data), INDEX1(data), INDEX1(data)>>& \\\n          tensor,                                                         \\\n      const Tensor<DTYPE(data), Symmetry<1, 1>,                           \\\n                   index_list<change_index_up_lo<INDEX1(data)>,           \\\n                              change_index_up_lo<INDEX1(data)>>>& metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial,\n                         Frame::Spherical<Frame::Inertial>,\n                         Frame::Spherical<Frame::Distorted>,\n                         Frame::Spherical<Frame::Grid>),\n                        (SpatialIndex, SpacetimeIndex), (UpLo::Lo, UpLo::Up),\n                        (UpLo::Lo, UpLo::Up))\n\n#undef FRAME1\n#undef UPORLO1\n#undef INDEXTYPE1\n#undef INDEX1\n#undef INSTANTIATE\n\n#define INSTANTIATE2(_, data)                                       \\\n  template void trace(                                              \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> trace,              \\\n      const Tensor<DTYPE(data), Symmetry<1, 1>,                     \\\n                   index_list<INDEX0(data), INDEX0(data)>>& tensor, \\\n      const Tensor<DTYPE(data), Symmetry<1, 1>,                     \\\n                   index_list<change_index_up_lo<INDEX0(data)>,     \\\n                              change_index_up_lo<INDEX0(data)>>>& metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE2, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial),\n                        (SpatialIndex, SpacetimeIndex), (UpLo::Lo, UpLo::Up))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME0\n#undef UPORLO0\n#undef INDEXTYPE0\n#undef INDEX0\n#undef INSTANTIATE2\n"
  },
  {
    "path": "src/DataStructures/Tensor/EagerMath/Trace.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes trace of a rank 3 tensor, which is symmetric in its last two\n * indices, tracing the symmetric indices.\n *\n * \\details For example, if \\f$ T_{abc} \\f$ is a tensor such that \\f$T_{abc} =\n * T_{acb} \\f$ then \\f$ T_a = g^{bc}T_{abc} \\f$ is computed, where \\f$ g^{bc}\n * \\f$ is the inverse metric.  Note that indices \\f$a,b,c,...\\f$ can represent\n * either spatial or spacetime indices, and can have either valence.  You may\n * have to add a new instantiation of this template if you need a new use case.\n */\ntemplate <typename DataType, typename Index0, typename Index1>\nvoid trace_last_indices(\n    gsl::not_null<Tensor<DataType, Symmetry<1>, index_list<Index0>>*>\n        trace_of_tensor,\n    const Tensor<DataType, Symmetry<2, 1, 1>,\n                 index_list<Index0, Index1, Index1>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index1>,\n                            change_index_up_lo<Index1>>>& metric);\n\ntemplate <typename DataType, typename Index0, typename Index1>\nTensor<DataType, Symmetry<1>, index_list<Index0>> trace_last_indices(\n    const Tensor<DataType, Symmetry<2, 1, 1>,\n                 index_list<Index0, Index1, Index1>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index1>,\n                            change_index_up_lo<Index1>>>& metric) {\n  auto trace_of_tensor =\n      make_with_value<Tensor<DataType, Symmetry<1>, index_list<Index0>>>(metric,\n                                                                         0.);\n  trace_last_indices(make_not_null(&trace_of_tensor), tensor, metric);\n  return trace_of_tensor;\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes trace of a rank-2 symmetric tensor.\n * \\details Computes \\f$g^{ab}T_{ab}\\f$ or \\f$g_{ab}T^{ab}\\f$ where \\f$(a,b)\\f$\n * can be spatial or spacetime indices.\n */\ntemplate <typename DataType, typename Index0>\nvoid trace(\n    gsl::not_null<Scalar<DataType>*> trace,\n    const Tensor<DataType, Symmetry<1, 1>, index_list<Index0, Index0>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric);\n\ntemplate <typename DataType, typename Index0>\nScalar<DataType> trace(\n    const Tensor<DataType, Symmetry<1, 1>, index_list<Index0, Index0>>& tensor,\n    const Tensor<DataType, Symmetry<1, 1>,\n                 index_list<change_index_up_lo<Index0>,\n                            change_index_up_lo<Index0>>>& metric) {\n  Scalar<DataType> trace{};\n  ::trace(make_not_null(&trace), tensor, metric);\n  return trace;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/AddSubtract.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines ET for adding and subtracting tensors\n\n#pragma once\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/DataTypeSupport.hpp\"\n#include \"DataStructures/Tensor/Expressions/IndexPropertyCheck.hpp\"\n#include \"DataStructures/Tensor/Expressions/NumberAsExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/SpatialSpacetimeIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tenex {\ntemplate <typename T1, typename T2, typename ArgsList1, typename ArgsList2,\n          int Sign>\nstruct AddSub;\n}  // namespace tenex\ntemplate <typename Derived, typename DataType, typename Symm,\n          typename IndexList, typename Args, typename ReducedArgs>\nstruct TensorExpression;\n/// \\endcond\n\nnamespace tenex {\nnamespace detail {\n/// \\brief Computes the rearranged symmetry of one operand according to the\n/// generic index order of the other operand\n///\n/// \\details\n/// Here is an example of what the algorithm does:\n///\n/// Given \\f$R_{abc} + S_{cab}\\f$, reorder \\f$S\\f$' symmetry according to\n/// \\f$R\\f$'s index order\n/// `tensorindex_transformation`:\n/// \\code\n/// {1, 2, 0} // positions of R's indices {a, b, c} in S' indices {c, a, b}\n/// \\endcode\n/// `input_symm`:\n/// \\code\n/// {2, 2, 1} // S' symmetry, where c and a are symmetric indices\n/// \\endcode\n/// returned equivalent `output_symm`:\n/// \\code\n/// {2, 1, 2} // S' symmetry (input_symm) rearranged to R's index order (abc)\n/// \\endcode\n///\n/// One special case scenario to note is when concrete time indices are\n/// involved in the transformation. Consider transforming the symmetry for\n/// some tensor \\f$S_{ab}\\f$ to the index order of another tensor\n/// \\f$R_{btat}\\f$. This would be necessary in an expression such as\n/// \\f$R_{btat} + S_{ab}\\f$. The transformation of the symmetry for \\f$S\\f$\n/// according to the index order of \\f$R\\f$ cannot simply be the list of\n/// positions of \\f$R\\f$'s indices in \\f$S\\f$' indices, as \\f$S\\f$ does not\n/// contain all of \\f$R\\f$'s indices, because it has no time indices. To handle\n/// cases like this, a placeholder value for the position of any time index\n/// must be substituted for an actual position, since one may not exist. In this\n/// example, the proper input transformation (`tensorindex_transformation`)\n/// would need to be `{1, PLACEHOLDER_VALUE, 0, PLACEHOLDER_VALUE}`, where\n/// `PLACEHOLDER_VALUE` is defined by\n/// `TensorIndexTransformation_detail::time_index_position_placeholder`. `1` and\n/// `0` are the positions of \\f$b\\f$ and \\f$a\\f$ in \\f$S\\f$, and the placeholder\n/// is used for the positions of time indices. In computing the output\n/// transformed symmetry, the function will insert a `0` at each position where\n/// this placeholder is found in the transformation. For example, if\n/// `input_symm` is `{2, 1}`, the returned output multi-index will be\n/// `{1, 0, 2, 0}`. Note that the symmetry returned by this function is not\n/// necessarily in the canonical form defined by ::Symmetry. This example with\n/// time indices is an example of this, as `0` is not a permitted ::Symmetry\n/// value, and the canonical form would have increasing symmetry values from\n/// right to left. In addition, even though the time indices in the rearranged\n/// symmetry will have the same symmetry value (`0`), this bears no impact on\n/// `get_addsub_symm`'s computation of the symmetry of the tensor resulting\n/// from the addition or subtraction.\n///\n/// \\tparam NumIndicesIn the number of indices in the operand whose symmetry is\n/// being transformed\n/// \\tparam NumIndicesOut the number of indices in the other operand whose index\n/// order is the order the input operand symmetry is being transformed to\n/// \\param input_symm the input operand symmetry to transform\n/// \\param tensorindex_transformation the positions of the other operand's\n/// generic indices in the generic indices of the operand whose symmetry is\n/// being transformed (see details)\n/// \\return the input operand symmetry rearranged according to the generic index\n/// order of the other operand\ntemplate <size_t NumIndicesIn, size_t NumIndicesOut>\nSPECTRE_ALWAYS_INLINE constexpr std::array<std::int32_t, NumIndicesOut>\ntransform_addsub_symm(\n    const std::array<std::int32_t, NumIndicesIn>& input_symm,\n    const std::array<size_t, NumIndicesOut>& tensorindex_transformation) {\n  std::array<std::int32_t, NumIndicesOut> output_symm =\n      make_array<NumIndicesOut, std::int32_t>(0);\n  for (size_t i = 0; i < NumIndicesOut; i++) {\n    gsl::at(output_symm, i) =\n        (gsl::at(tensorindex_transformation, i) ==\n         TensorIndexTransformation_detail::time_index_position_placeholder)\n            ? 0\n            : gsl::at(input_symm, gsl::at(tensorindex_transformation, i));\n  }\n  return output_symm;\n}\n\n/// @{\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the canonical symmetry of the tensor resulting from\n/// adding or subtracting two tensors, according to their symmetries\n///\n/// \\details The canonical symmetry returned follows the convention defined by\n/// ::Symmetry: symmetry values are in ascending order from right to left. If\n/// the convention implemented by ::Symmetry changes, this function will also\n/// need to be updated to match the new convention. The ::Symmetry metafunction\n/// could instead be used on the result of this function, but that would\n/// introduce avoidable and unnecessary extra computations, so it is not used.\n///\n/// This function treats the two input symmetries as aligned (i.e. each position\n/// of `symm1` and `symm2` corresponds to a shared generic index at that\n/// position). The resultant symmetry is determined as follows: indices that are\n/// symmetric in both input symmetries are also symmetric in the resultant\n/// tensor.\n///\n/// \\param symm1 the symmetry of the first tensor being added or subtracted\n/// \\param symm2 the symmetry of the second tensor being added or subtracted\n/// \\return the canonical symmetry of the tensor resulting from adding or\n/// subtracting two tensors\ntemplate <size_t NumIndices, Requires<(NumIndices >= 2)> = nullptr>\nconstexpr std::array<std::int32_t, NumIndices> get_addsub_symm(\n    const std::array<std::int32_t, NumIndices>& symm1,\n    const std::array<std::int32_t, NumIndices>& symm2) {\n  constexpr std::int32_t max_int = std::numeric_limits<std::int32_t>::max();\n  std::array<std::int32_t, NumIndices> addsub_symm =\n      make_array<NumIndices>(max_int);\n  size_t right_index = NumIndices - 1;\n  std::int32_t symm_value_to_set = 1;\n\n  while (right_index < NumIndices) {\n    std::int32_t symm1_value_to_find = symm1[right_index];\n    std::int32_t symm2_value_to_find = symm2[right_index];\n    // if we haven't yet set right_index for the resultant symmetry\n    if (addsub_symm[right_index] == max_int) {\n      addsub_symm[right_index] = symm_value_to_set;\n      for (size_t left_index = right_index - 1; left_index < NumIndices;\n           left_index--) {\n        // if left_index of the resultant symmetry is not yet set and we've\n        // found a common symmetry between symm1 and symm2 at this index\n        if (addsub_symm[left_index] == max_int and\n            symm1[left_index] == symm1_value_to_find and\n            symm2[left_index] == symm2_value_to_find) {\n          addsub_symm[left_index] = symm_value_to_set;\n        }\n      }\n      symm_value_to_set++;\n    }\n    right_index--;\n  }\n\n  return addsub_symm;\n}\n\ntemplate <size_t NumIndices, Requires<(NumIndices == 1)> = nullptr>\nconstexpr std::array<std::int32_t, NumIndices> get_addsub_symm(\n    const std::array<std::int32_t, NumIndices>& /*symm1*/,\n    const std::array<std::int32_t, NumIndices>& /*symm2*/) {\n  // return {{1}} instead of symm1 in case symm1 is not in the canonical form\n  return {{1}};\n}\n\ntemplate <size_t NumIndices, Requires<(NumIndices == 0)> = nullptr>\nconstexpr std::array<std::int32_t, NumIndices> get_addsub_symm(\n    const std::array<std::int32_t, NumIndices>& symm1,\n    const std::array<std::int32_t, NumIndices>& /*symm2*/) {\n  return symm1;\n}\n/// @}\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Helper struct for computing the canonical symmetry of the tensor\n/// resulting from adding or subtracting two tensors, according to their\n/// symmetries and generic index orders\n///\n/// \\details The resultant symmetry (`type`) values correspond to the index\n/// order of the first tensor operand being added or subtracted:\n/// `TensorIndexList1`.\n///\n/// \\tparam SymmList1 the ::Symmetry of the first operand\n/// \\tparam SymmList2 the ::Symmetry of the second operand\n/// \\tparam TensorIndexList1 the generic indices of the first operand\n/// \\tparam TensorIndexList2 the generic indices of the second operand\ntemplate <typename SymmList1, typename SymmList2, typename TensorIndexList1,\n          typename TensorIndexList2,\n          size_t NumIndices1 = tmpl::size<SymmList1>::value,\n          size_t NumIndices2 = tmpl::size<SymmList2>::value,\n          typename IndexSequence1 = std::make_index_sequence<NumIndices1>>\nstruct AddSubSymmetry;\n\ntemplate <\n    template <typename...> class SymmList1, typename... Symm1,\n    template <typename...> class SymmList2, typename... Symm2,\n    template <typename...> class TensorIndexList1, typename... TensorIndices1,\n    template <typename...> class TensorIndexList2, typename... TensorIndices2,\n    size_t NumIndices1, size_t NumIndices2, size_t... Ints1>\nstruct AddSubSymmetry<SymmList1<Symm1...>, SymmList2<Symm2...>,\n                      TensorIndexList1<TensorIndices1...>,\n                      TensorIndexList2<TensorIndices2...>, NumIndices1,\n                      NumIndices2, std::index_sequence<Ints1...>> {\n  static constexpr std::array<size_t, NumIndices1> tensorindex_values1 = {\n      {TensorIndices1::value...}};\n  static constexpr std::array<size_t, NumIndices2> tensorindex_values2 = {\n      {TensorIndices2::value...}};\n  // positions of tensorindex_values1 in tensorindex_values2\n  static constexpr std::array<size_t, NumIndices1> op2_to_op1_map =\n      ::tenex::compute_tensorindex_transformation(tensorindex_values2,\n                                                  tensorindex_values1);\n\n  static constexpr std::array<std::int32_t, NumIndices1> symm1 = {\n      {Symm1::value...}};\n  static constexpr std::array<std::int32_t, NumIndices2> symm2 = {\n      {Symm2::value...}};\n  // 2nd argument is symm2 rearranged according to `TensorIndexList1` order\n  // so that the two symmetry arguments to `get_addsub_symm` are aligned\n  // w.r.t. their generic index orders\n  static constexpr std::array<std::int32_t, NumIndices1> addsub_symm =\n      get_addsub_symm(symm1, transform_addsub_symm(symm2, op2_to_op1_map));\n\n  using type = tmpl::integral_list<std::int32_t, addsub_symm[Ints1]...>;\n};\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Helper struct for defining the symmetry, index list, and\n/// generic index list of the tensor resulting from adding or\n/// subtracting two tensor expressions\n///\n/// \\tparam T1 the first tensor expression operand\n/// \\tparam T2 the second tensor expression operand\ntemplate <typename T1, typename T2>\nstruct AddSubType {\n  static_assert(std::is_base_of_v<Expression, T1> and\n                    std::is_base_of_v<Expression, T2>,\n                \"Parameters to AddSubType must be TensorExpressions\");\n  using type =\n      typename get_binop_datatype<typename T1::type, typename T2::type>::type;\n  using symmetry =\n      typename AddSubSymmetry<typename T1::symmetry, typename T2::symmetry,\n                                   typename T1::args_list,\n                                   typename T2::args_list>::type;\n  using index_list = typename T1::index_list;\n  using tensorindex_list = typename T1::args_list;\n};\n}  // namespace detail\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Defines the tensor expression representing the addition or\n/// subtraction of two tensor expressions\n///\n/// \\details\n/// For details on aliases and members defined in this class, as well as general\n/// `TensorExpression` terminology used in its members' documentation, see\n/// documentation for `TensorExpression`.\n///\n/// \\tparam T1 the left operand expression\n/// \\tparam T2 the right operand expression\n/// \\tparam ArgsList1 generic `TensorIndex`s of the left operand\n/// \\tparam ArgsList2 generic `TensorIndex`s of the right operand\n/// \\tparam Sign the sign of the operation selected, 1 for addition or -1 for\n/// subtraction\ntemplate <typename T1, typename T2, typename ArgsList1, typename ArgsList2,\n          int Sign>\nstruct AddSub;\n\ntemplate <typename T1, typename T2, template <typename...> class ArgsList1,\n          template <typename...> class ArgsList2, typename... Args1,\n          typename... Args2, int Sign>\nstruct AddSub<T1, T2, ArgsList1<Args1...>, ArgsList2<Args2...>, Sign>\n    : public TensorExpression<\n          AddSub<T1, T2, ArgsList1<Args1...>, ArgsList2<Args2...>, Sign>,\n          typename detail::AddSubType<T1, T2>::type,\n          typename detail::AddSubType<T1, T2>::symmetry,\n          typename detail::AddSubType<T1, T2>::index_list,\n          typename detail::AddSubType<T1, T2>::tensorindex_list> {\n  static_assert(\n      detail::tensorexpression_binop_datatypes_are_supported_v<T1, T2>,\n      \"Cannot add or subtract the given TensorExpression types with the given \"\n      \"data types. This can occur from e.g. trying to add a Tensor with data \"\n      \"type double and a Tensor with data type DataVector.\");\n  static_assert(\n      not((std::is_same_v<T1, NumberAsExpression<std::complex<double>>> and\n           std::is_same_v<typename T2::type, DataVector>) or\n          (std::is_same_v<T2, NumberAsExpression<std::complex<double>>> and\n           std::is_same_v<typename T1::type, DataVector>)),\n      \"Cannot perform addition and subtraction between a std::complex<double> \"\n      \"and a TensorExpression whose data type is DataVector because Blaze does \"\n      \"not support addition and subtraction between std::complex<double> and \"\n      \"DataVector.\");\n  static_assert(\n      detail::IndexPropertyCheck<typename T1::index_list,\n                                 typename T2::index_list, ArgsList1<Args1...>,\n                                 ArgsList2<Args2...>>::value,\n      \"You are attempting to add indices of different types, e.g. T^a_b + \"\n      \"S^b_a, which doesn't make sense. The indices may also be in different \"\n      \"frames, different types (spatial vs. spacetime) or of different \"\n      \"dimension.\");\n  static_assert(Sign == 1 or Sign == -1,\n                \"Invalid Sign provided for addition or subtraction of Tensor \"\n                \"elements. Sign must be 1 (addition) or -1 (subtraction).\");\n\n  // === Index properties ===\n  /// The type of the data being stored in the result of the expression\n  using type = typename detail::AddSubType<T1, T2>::type;\n  /// The ::Symmetry of the result of the expression\n  using symmetry = typename detail::AddSubType<T1, T2>::symmetry;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using index_list = typename detail::AddSubType<T1, T2>::index_list;\n  /// The list of generic `TensorIndex`s of the result of the\n  /// expression\n  using args_list = typename T1::args_list;\n  /// The number of tensor indices in the result of the expression. This also\n  /// doubles as the left operand's number of indices.\n  static constexpr auto num_tensor_indices = tmpl::size<index_list>::value;\n  /// The number of tensor indices in the right operand expression\n  static constexpr auto num_tensor_indices_op2 = sizeof...(Args2);\n  /// Mapping from the left operand's index order to the right operand's index\n  /// order\n  static constexpr std::array<size_t, num_tensor_indices_op2>\n      operand_index_transformation =\n          compute_tensorindex_transformation<num_tensor_indices,\n                                             num_tensor_indices_op2>(\n              {{Args1::value...}}, {{Args2::value...}});\n  /// Positions of indices in first operand where generic spatial indices are\n  /// used for spacetime indices\n  static constexpr auto op1_spatial_spacetime_index_positions =\n      detail::get_spatial_spacetime_index_positions<typename T1::index_list,\n                                                    ArgsList1<Args1...>>();\n  /// Positions of indices in second operand where generic spatial indices are\n  /// used for spacetime indices\n  static constexpr auto op2_spatial_spacetime_index_positions =\n      detail::get_spatial_spacetime_index_positions<typename T2::index_list,\n                                                    ArgsList2<Args2...>>();\n\n  /// Whether or not the two operands have the same `TensorIndex`s in the same\n  /// order (including concrete time indices)\n  static constexpr bool ops_have_generic_indices_at_same_positions =\n      generic_indices_at_same_positions<tmpl::list<Args1...>,\n                                        tmpl::list<Args2...>>::value;\n\n  // === Expression subtree properties ===\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// left operand\n  static constexpr size_t num_ops_left_child = T1::num_ops_subtree;\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// right operand\n  static constexpr size_t num_ops_right_child = T2::num_ops_subtree;\n  // This helps ensure we are minimizing breadth in the overall tree when we\n  // have addition (subtraction is not commutative)\n  static_assert(Sign == -1 or num_ops_left_child >= num_ops_right_child,\n                \"The left operand of an AddSub expression performing addition \"\n                \"should be a subtree with equal or more tensor operations than \"\n                \"the right operand's subtree.\");\n  /// The total number of arithmetic tensor operations done in this expression's\n  /// whole subtree\n  static constexpr size_t num_ops_subtree =\n      num_ops_left_child + num_ops_right_child + 1;\n  /// The height of this expression's node in the expression tree relative to\n  /// the closest `TensorAsExpression` leaf in its subtree\n  static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree =\n      T2::height_relative_to_closest_tensor_leaf_in_subtree <=\n              T1::height_relative_to_closest_tensor_leaf_in_subtree\n          ? (T2::height_relative_to_closest_tensor_leaf_in_subtree !=\n                     std::numeric_limits<size_t>::max()\n                 ? T2::height_relative_to_closest_tensor_leaf_in_subtree + 1\n                 : T2::height_relative_to_closest_tensor_leaf_in_subtree)\n          : T1::height_relative_to_closest_tensor_leaf_in_subtree + 1;\n\n  // === Properties for splitting up subexpressions along the primary path ===\n  // These definitions only have meaning if this expression actually ends up\n  // being along the primary path that is taken when evaluating the whole tree.\n  // See documentation for `TensorExpression` for more details.\n  /// If on the primary path, whether or not the expression is an ending point\n  /// of a leg\n  static constexpr bool is_primary_end = T1::is_primary_start;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the subtree of the child along the\n  /// primary path, given that we will have already computed the whole subtree\n  /// at the next lowest leg's starting point.\n  static constexpr size_t num_ops_to_evaluate_primary_left_child =\n      is_primary_end ? 0 : T1::num_ops_to_evaluate_primary_subtree;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the right operand's subtree. No\n  /// splitting is currently done, so this is just `num_ops_right_child`.\n  static constexpr size_t num_ops_to_evaluate_primary_right_child =\n      num_ops_right_child;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done for this expression's subtree, given that\n  /// we will have already computed the subtree at the next lowest leg's\n  /// starting point\n  static constexpr size_t num_ops_to_evaluate_primary_subtree =\n      num_ops_to_evaluate_primary_left_child +\n      num_ops_to_evaluate_primary_right_child + 1;\n  /// If on the primary path, whether or not the expression is a starting point\n  /// of a leg\n  static constexpr bool is_primary_start =\n      num_ops_to_evaluate_primary_subtree >=\n      detail::max_num_ops_in_sub_expression<type>;\n  /// When evaluating along a primary path, whether each operand's subtrees\n  /// should be evaluated separately. Since `DataVector` expression runtime\n  /// scales poorly with increased number of operations, evaluating the two\n  /// expression subtrees separately like this is beneficial when at least one\n  /// of the subtrees contains a large number of operations.\n  static constexpr bool evaluate_children_separately =\n      is_primary_start and (num_ops_to_evaluate_primary_left_child >=\n                                detail::max_num_ops_in_sub_expression<type> or\n                            num_ops_to_evaluate_primary_right_child >=\n                                detail::max_num_ops_in_sub_expression<type>);\n  /// If on the primary path, whether or not the expression's child along the\n  /// primary path is a subtree that contains a starting point of a leg along\n  /// the primary path\n  static constexpr bool primary_child_subtree_contains_primary_start =\n      T1::primary_subtree_contains_primary_start;\n  /// If on the primary path, whether or not this subtree contains a starting\n  /// point of a leg along the primary path\n  static constexpr bool primary_subtree_contains_primary_start =\n      is_primary_start or primary_child_subtree_contains_primary_start;\n\n  AddSub(T1 t1, T2 t2) : t1_(std::move(t1)), t2_(std::move(t2)) {}\n  ~AddSub() override = default;\n\n  /// \\brief Assert that the LHS tensor of the equation does not also appear in\n  /// this expression's subtree\n  template <typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensor_not_in_rhs_expression(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T1>) {\n      t1_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T2>) {\n      t2_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n  }\n\n  /// \\brief Assert that each instance of the LHS tensor in the RHS tensor\n  /// expression uses the same generic index order that the LHS uses\n  ///\n  /// \\tparam LhsTensorIndices the list of generic `TensorIndex`s of the LHS\n  /// result `Tensor` being computed\n  /// \\param lhs_tensor the LHS result `Tensor` being computed\n  template <typename LhsTensorIndices, typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensorindices_same_in_rhs(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T1>) {\n      t1_.template assert_lhs_tensorindices_same_in_rhs<LhsTensorIndices>(\n          lhs_tensor);\n    }\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T2>) {\n      t2_.template assert_lhs_tensorindices_same_in_rhs<LhsTensorIndices>(\n          lhs_tensor);\n    }\n  }\n\n  /// \\brief Get the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  ///\n  /// \\return the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  SPECTRE_ALWAYS_INLINE size_t get_rhs_tensor_component_size() const {\n    if constexpr (T1::height_relative_to_closest_tensor_leaf_in_subtree <=\n                  T2::height_relative_to_closest_tensor_leaf_in_subtree) {\n      return t1_.get_rhs_tensor_component_size();\n    } else {\n      return t2_.get_rhs_tensor_component_size();\n    }\n  }\n\n  /// \\brief Return the second operand's multi-index given the first operand's\n  /// multi-index\n  ///\n  /// \\param op1_multi_index the multi-index of the left operand\n  /// \\return the second operand's multi-index\n  SPECTRE_ALWAYS_INLINE std::array<size_t, num_tensor_indices_op2>\n  get_op2_multi_index(\n      const std::array<size_t, num_tensor_indices>& op1_multi_index) const {\n    if constexpr (ops_have_generic_indices_at_same_positions) {\n      if constexpr (op1_spatial_spacetime_index_positions.size() != 0 or\n                    op2_spatial_spacetime_index_positions.size() != 0) {\n        // Operands have the same generic index order, but at least one of them\n        // has at least one spacetime index where a spatial index has been used,\n        // so we need to compute the 2nd operand's (possibly) shifted\n        // multi-index values\n        constexpr std::array<std::int32_t, num_tensor_indices>\n            spatial_spacetime_index_transformation =\n                detail::spatial_spacetime_index_transformation_from_positions<\n                    num_tensor_indices>(op1_spatial_spacetime_index_positions,\n                                        op2_spatial_spacetime_index_positions);\n        std::array<size_t, num_tensor_indices> op2_multi_index =\n            op1_multi_index;\n        for (size_t i = 0; i < num_tensor_indices; i++) {\n          gsl::at(op2_multi_index, i) = static_cast<size_t>(\n              static_cast<std::int32_t>(gsl::at(op2_multi_index, i)) +\n              gsl::at(spatial_spacetime_index_transformation, i));\n        }\n        return op2_multi_index;\n      } else {\n        // Operands have the same generic index order and neither of them has\n        // a spacetime index where a spatial index has been used, so\n        // both operands have the same multi-index\n        return op1_multi_index;\n      }\n    } else {\n      if constexpr (op1_spatial_spacetime_index_positions.size() != 0 or\n                    op2_spatial_spacetime_index_positions.size() != 0) {\n        // Operands don't have the same generic index order and at least one of\n        // them has at least one spacetime index where a spatial index has been\n        // used, so we need to compute the 2nd operand's (possibly) shifted\n        // multi-index values and reorder them with respect to the 2nd operand's\n        // generic index order\n\n        // The list of positions where generic spatial indices were used for\n        // spacetime indices in the second operand, but rearranged in terms of\n        // the first operand's generic index order.\n        constexpr std::array<size_t,\n                             op2_spatial_spacetime_index_positions.size()>\n            transformed_op2_spatial_spacetime_index_positions = []() {\n              std::array<size_t, op2_spatial_spacetime_index_positions.size()>\n                  transformed_positions{};\n              for (size_t i = 0;\n                   i < op2_spatial_spacetime_index_positions.size(); i++) {\n                gsl::at(transformed_positions, i) =\n                    gsl::at(operand_index_transformation,\n                            gsl::at(op2_spatial_spacetime_index_positions, i));\n              }\n              return transformed_positions;\n            }();\n\n        // According to the transformed positions above, compute the value shift\n        // needed to convert from multi-indices of the first operand to\n        // multi-indices of the 2nd operand (with the generic index order of the\n        // first)\n        constexpr std::array<std::int32_t, num_tensor_indices>\n            spatial_spacetime_index_transformation =\n                detail::spatial_spacetime_index_transformation_from_positions<\n                    num_tensor_indices>(\n                    op1_spatial_spacetime_index_positions,\n                    transformed_op2_spatial_spacetime_index_positions);\n        std::array<size_t, num_tensor_indices> op2_multi_index =\n            op1_multi_index;\n        for (size_t i = 0; i < num_tensor_indices; i++) {\n          gsl::at(op2_multi_index, i) = static_cast<size_t>(\n              static_cast<std::int32_t>(gsl::at(op2_multi_index, i)) +\n              gsl::at(spatial_spacetime_index_transformation, i));\n        }\n        return transform_multi_index(op2_multi_index,\n                                     operand_index_transformation);\n      } else {\n        // Operands don't have the same generic index order, but neither of them\n        // has a spacetime index where a spatial index has been used, so we just\n        // need to reorder the 2nd operand's multi_index according to its\n        // generic index order\n        return transform_multi_index(op1_multi_index,\n                                     operand_index_transformation);\n      }\n    }\n  }\n\n  /// \\brief Helper function for computing the sum of or difference between\n  /// components at given multi-indices from both operands of the expression\n  ///\n  /// \\details Both multi-index arguments must be ordered according to their\n  /// operand's respective generic index ordering\n  ///\n  /// \\param op1_multi_index the multi-index of the component of the first\n  /// operand\n  /// \\param op2_multi_index the multi-index of the component of the second\n  /// operand\n  /// \\return the sum of or difference between the two components' values\n  SPECTRE_ALWAYS_INLINE decltype(auto) add_or_subtract(\n      const std::array<size_t, num_tensor_indices>& op1_multi_index,\n      const std::array<size_t, num_tensor_indices_op2>& op2_multi_index) const {\n    if constexpr (Sign == 1) {\n      return t1_.get(op1_multi_index) + t2_.get(op2_multi_index);\n    } else {\n      return t1_.get(op1_multi_index) - t2_.get(op2_multi_index);\n    }\n  }\n\n  /// \\brief Return the value of the component at the given multi-index of the\n  /// tensor resulting from addition or subtraction\n  ///\n  /// \\details One important detail to note about the type of the `AddSub`\n  /// expression is that its two operands may have (i) different generic index\n  /// orders, and/or (ii) different indices in their `index_list`s if where one\n  /// operand uses a generic spatial index for a spacetime index, the other\n  /// tensor may use that generic spatial index for a spatial index of the same\n  /// dimension, valence, and frame. Therefore, there are four possible cases\n  /// for an `AddSub` expression that are considered in the implementation:\n  /// - same generic index order, spatial spacetime indices in expression\n  /// - same generic index order, spatial spacetime indices not in expression\n  /// - different generic index order, spatial spacetime indices in expression\n  /// - different generic index order, spatial spacetime indices not in\n  /// expression\n  ///\n  /// This means that for expressions where the generic index orders differ, a\n  /// multi-index for a component of one operand is a (possible) rearrangement\n  /// of the equivalent multi-index for a component in the other operand. This\n  /// also means that for expressions where (at least once) a generic spatial\n  /// index is used for a spacetime index, then, after accounting\n  /// for potential reordering due to different generic index orders, a\n  /// multi-index's values for a component of one operand are (possibly) shifted\n  /// by one, compared to the multi-index's values for a component in the other\n  /// operand.\n  ///\n  /// For example, given \\f$R_{ij} + S_{ji}\\f$, let \\f$R\\f$'s first index be\n  /// a spacetime index, but \\f$R\\f$'s second index and both of \\f$S\\f$' indices\n  /// be spatial indices. If \\f$i = 2\\f$ and \\f$j = 0\\f$, then when we compute\n  /// \\f$R_{20} + S_{02}\\f$, the multi-index for \\f$R_{20}\\f$ is\n  /// `{2 + 1, 0} = {3, 0}` (first value shifted because it is a spacetime\n  /// index) and the multi-index for \\f$S_{02}\\f$ is `[0, 2]`. Because the first\n  /// operand of an `AddSub` expresion propagates its generic index order and\n  /// index list ( \\ref SpacetimeIndex \"TensorIndexType\"s) as the `AddSub`'s own\n  /// generic index order and index list, the `result_multi_index` is equivalent\n  /// to the multi-index for the first operand. Thus, we need only compute the\n  /// second operand's multi-index as a transformation of the first: reorder and\n  /// shift the values of the first operand to compute the equivalent\n  /// multi-index for the second operand.\n  ///\n  /// \\param result_multi_index the multi-index of the component of the result\n  /// tensor to retrieve\n  /// \\return the value of the component at `result_multi_index` in the result\n  /// tensor\n  SPECTRE_ALWAYS_INLINE decltype(auto) get(\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    return add_or_subtract(result_multi_index,\n                           get_op2_multi_index(result_multi_index));\n  }\n\n  /// \\brief Helper for evaluating the LHS Tensor's result component at this\n  /// subtree by evaluating the two operand's subtrees separately and adding or\n  /// subtracting them\n  ///\n  /// \\details\n  /// The left and right operands' subtrees are evaluated successively with\n  /// two separate assignments to the LHS result component. Since `DataVector`\n  /// expression runtime scales poorly with increased number of operations,\n  /// evaluating the two expression subtrees separately like this is beneficial\n  /// when at least one of the subtrees contains a large number of operations.\n  /// Instead of evaluating a larger expression with their combined total number\n  /// of operations, we evaluate two smaller ones.\n  ///\n  /// This function also differs from `add_or_subtract` in that it takes into\n  /// account whether we have already computed part of the result component at a\n  /// lower subtree. In recursively computing this sum/difference, the current\n  /// result component will be substituted in for the most recent (highest)\n  /// subtree below it that has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param op1_multi_index the multi-index of the component of the first\n  /// operand of the sum or difference to evaluate\n  /// \\param op2_multi_index the multi-index of the component of the second\n  /// operand of the sum or difference to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void add_or_subtract_primary_children(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& op1_multi_index,\n      const std::array<size_t, num_tensor_indices_op2>& op2_multi_index) const {\n    if constexpr (Sign == 1) {\n      // We're performing addition\n      if constexpr (is_primary_end) {\n        (void)op1_multi_index;\n        // We've already computed the whole child subtree on the primary path,\n        // so just add the result of the other child's subtree to the current\n        // result\n        result_component += t2_.get(op2_multi_index);\n      } else {\n        // We haven't yet evaluated the whole subtree of the primary child, so\n        // first assign the result component to be the result of computing the\n        // primary child's subtree\n        result_component = t1_.get_primary(result_component, op1_multi_index);\n        // Now that the primary child's subtree has been computed, add the\n        // result of evaluating the other child's subtree to the current result\n        result_component += t2_.get(op2_multi_index);\n      }\n    } else {\n      // We're performing subtraction\n      if constexpr (is_primary_end) {\n        (void)op1_multi_index;\n        // We've already computed the whole child subtree on the primary path,\n        // so just subtract the result of the other child's subtree from the\n        // current result\n        result_component -= t2_.get(op2_multi_index);\n      } else {\n        // We haven't yet evaluated the whole subtree of the primary child, so\n        // first assign the result component to be the result of computing the\n        // primary child's subtree\n        result_component = t1_.get_primary(result_component, op1_multi_index);\n        // Now that the primary child's subtree has been computed, subtract the\n        // result of evaluating the other child's subtree from the current\n        // result\n        result_component -= t2_.get(op2_multi_index);\n      }\n    }\n  }\n\n  /// \\brief Evaluate the LHS Tensor's result component at this subtree by\n  /// evaluating the two operand's subtrees separately and adding or subtracting\n  /// them\n  ///\n  /// \\details\n  /// See `add_or_subtract_primary_children` for more details\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param result_multi_index the multi-index of the component of the result\n  /// tensor to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_children(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    add_or_subtract_primary_children(result_component, result_multi_index,\n                                     get_op2_multi_index(result_multi_index));\n  }\n\n  /// \\brief Helper function for returning the sum of or difference between\n  /// components at given multi-indices from both operands of the expression\n  ///\n  /// \\details\n  /// This function differs from `add_or_subtract` in that it takes into account\n  /// whether we have already computed part of the result component at a lower\n  /// subtree. In recursively computing this sum/difference, the current result\n  /// component will be substituted in for the most recent (highest) subtree\n  /// below it that has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param op1_multi_index the multi-index of the component of the first\n  /// operand\n  /// \\param op2_multi_index the multi-index of the component of the second\n  /// operand\n  /// \\return the sum of or difference between the two components' values\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) add_or_subtract_primary(\n      const ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& op1_multi_index,\n      const std::array<size_t, num_tensor_indices_op2>& op2_multi_index) const {\n    if constexpr (Sign == 1) {\n      // We're performing addition\n      if constexpr (is_primary_end) {\n        (void)op1_multi_index;\n        // We've already computed the whole child subtree on the primary path,\n        // so just add the result of the other child's subtree to the current\n        // result\n        return result_component + t2_.get(op2_multi_index);\n      } else {\n        // We haven't yet evaluated the whole subtree for this expression, so\n        // return the sum of the results of the two operands' subtrees\n        return t1_.get_primary(result_component, op1_multi_index) +\n               t2_.get(op2_multi_index);\n      }\n    } else {\n      // We're performing subtraction\n      if constexpr (is_primary_end) {\n        (void)op1_multi_index;\n        // We've already computed the whole child subtree on the primary path,\n        // so just subtract the result of the other child's subtree from the\n        // current result\n        return result_component - t2_.get(op2_multi_index);\n      } else {\n        // We haven't yet evaluated the whole subtree for this expression, so\n        // return the difference between the results of the two operands'\n        // subtrees\n        return t1_.get_primary(result_component, op1_multi_index) -\n               t2_.get(op2_multi_index);\n      }\n    }\n  }\n\n  /// \\brief Return the value of the component at the given multi-index of the\n  /// tensor resulting from addition or subtraction\n  ///\n  /// \\details\n  /// This function differs from `get` in that it takes into account whether we\n  /// have already computed part of the result component at a lower subtree.\n  /// In recursively computing this sum/difference, the current result component\n  /// will be substituted in for the most recent (highest) subtree below it that\n  /// has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param result_multi_index the multi-index of the component of the result\n  /// tensor to retrieve\n  /// \\return the value of the component at `result_multi_index` in the result\n  /// tensor\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(\n      const ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    return add_or_subtract_primary(result_component, result_multi_index,\n                                   get_op2_multi_index(result_multi_index));\n  }\n\n  /// \\brief Successively evaluate the LHS Tensor's result component at each\n  /// leg in this expression's subtree\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// sum/difference, the current result component will be substituted in for\n  /// the most recent (highest) subtree below it that has already been\n  /// evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param result_multi_index the multi-index of the component of the result\n  /// tensor to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_subtree(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    if constexpr (primary_child_subtree_contains_primary_start) {\n      // The primary child's subtree contains at least one leg, so recurse down\n      // and evaluate that first\n      t1_.evaluate_primary_subtree(result_component, result_multi_index);\n    }\n\n    if constexpr (is_primary_start) {\n      // We want to evaluate the subtree for this expression\n      if constexpr (evaluate_children_separately) {\n        // Evaluate operand's subtrees separately\n        evaluate_primary_children(result_component, result_multi_index);\n      } else {\n        // Evaluate whole subtree as one expression\n        result_component = get_primary(result_component, result_multi_index);\n      }\n    }\n  }\n\n private:\n  /// Left operand expression\n  T1 t1_;\n  /// Right operand expression\n  T2 t2_;\n};\n}  // namespace tenex\n\n/*!\n * \\ingroup TensorExpressionsGroup\n */\ntemplate <typename T1, typename T2, typename X1, typename X2, typename Symm1,\n          typename Symm2, typename IndexList1, typename IndexList2,\n          typename Args1, typename Args2>\nSPECTRE_ALWAYS_INLINE auto operator+(\n    const TensorExpression<T1, X1, Symm1, IndexList1, Args1>& t1,\n    const TensorExpression<T2, X2, Symm2, IndexList2, Args2>& t2) {\n  using op1_generic_indices =\n      typename tenex::detail::remove_time_indices<Args1>::type;\n  using op2_generic_indices =\n      typename tenex::detail::remove_time_indices<Args2>::type;\n  static_assert(tmpl::size<op1_generic_indices>::value ==\n                    tmpl::size<op2_generic_indices>::value,\n                \"Tensor addition is only possible when the same number of \"\n                \"generic indices are used with both operands.\");\n  static_assert(\n      tmpl::equal_members<op1_generic_indices, op2_generic_indices>::value,\n      \"The generic indices when adding two tensors must be equal. This error \"\n      \"occurs from expressions like R(ti::a, ti::b) + S(ti::c, ti::a)\");\n  if constexpr (T1::num_ops_subtree >= T2::num_ops_subtree) {\n    return tenex::AddSub<T1, T2, Args1, Args2, 1>(~t1, ~t2);\n  } else {\n    return tenex::AddSub<T2, T1, Args2, Args1, 1>(~t2, ~t1);\n  }\n}\n\n/// @{\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the sum of a tensor\n/// expression and a number\n///\n/// \\details\n/// The tensor expression operand must represent an expression that, when\n/// evaluated, would be a rank 0 tensor. For example, if `R` and `S` are\n/// Tensors, here is a non-exhaustive list of some of the acceptable forms that\n/// the tensor expression operand could take:\n/// - `R()`\n/// - `R(ti::A, ti::a)`\n/// - `(R(ti::A, ti::B) * S(ti::a, ti::b))`\n/// - `R(ti::t, ti::t)`\n///\n/// \\param t the tensor expression operand of the sum\n/// \\param number the numeric operand of the sum\n/// \\return the tensor expression representing the sum of the tensor expression\n/// and the number\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args, typename N,\n          Requires<std::is_arithmetic_v<N>> = nullptr>\nSPECTRE_ALWAYS_INLINE auto operator+(\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t,\n    const N number) {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only add a number to a tensor expression that evaluates to a rank 0\"\n      \"tensor.\");\n  return t + tenex::NumberAsExpression(number);\n}\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args, typename N,\n          Requires<std::is_arithmetic_v<N>> = nullptr>\nSPECTRE_ALWAYS_INLINE auto operator+(\n    const N number,\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t) {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only add a number to a tensor expression that evaluates to a rank 0\"\n      \"tensor.\");\n  return t + tenex::NumberAsExpression(number);\n}\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args, typename N>\nSPECTRE_ALWAYS_INLINE auto operator+(\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t,\n    const std::complex<N>& number) {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only add a number to a tensor expression that evaluates to a rank 0\"\n      \"tensor.\");\n  return t + tenex::NumberAsExpression(number);\n}\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args, typename N>\nSPECTRE_ALWAYS_INLINE auto operator+(\n    const std::complex<N>& number,\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t) {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only add a number to a tensor expression that evaluates to a rank 0\"\n      \"tensor.\");\n  return t + tenex::NumberAsExpression(number);\n}\n/// @}\n\n/*!\n * \\ingroup TensorExpressionsGroup\n */\ntemplate <typename T1, typename T2, typename X1, typename X2, typename Symm1,\n          typename Symm2, typename IndexList1, typename IndexList2,\n          typename Args1, typename Args2>\nSPECTRE_ALWAYS_INLINE auto operator-(\n    const TensorExpression<T1, X1, Symm1, IndexList1, Args1>& t1,\n    const TensorExpression<T2, X2, Symm2, IndexList2, Args2>& t2) {\n  using op1_generic_indices =\n      typename tenex::detail::remove_time_indices<Args1>::type;\n  using op2_generic_indices =\n      typename tenex::detail::remove_time_indices<Args2>::type;\n  static_assert(tmpl::size<op1_generic_indices>::value ==\n                    tmpl::size<op2_generic_indices>::value,\n                \"Tensor subtraction is only possible when the same number of \"\n                \"generic indices are used with both operands.\");\n  static_assert(\n      tmpl::equal_members<op1_generic_indices, op2_generic_indices>::value,\n      \"The generic indices when subtracting two tensors must be equal. This \"\n      \"error occurs from expressions like R(ti::a, ti::b) - S(ti::c, ti::a)\");\n  return tenex::AddSub<T1, T2, Args1, Args2, -1>(~t1, ~t2);\n}\n\n/// @{\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the difference of a tensor\n/// expression and a number\n///\n/// \\details\n/// The tensor expression operand must represent an expression that, when\n/// evaluated, would be a rank 0 tensor. For example, if `R` and `S` are\n/// Tensors, here is a non-exhaustive list of some of the acceptable forms that\n/// the tensor expression operand could take:\n/// - `R()`\n/// - `R(ti::A, ti::a)`\n/// - `(R(ti::A, ti::B) * S(ti::a, ti::b))`\n/// - `R(ti::t, ti::t)`\n///\n/// \\param t the tensor expression operand of the difference\n/// \\param number the numeric operand of the difference\n/// \\return the tensor expression representing the difference of the tensor\n/// expression and the number\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args, typename N,\n          Requires<std::is_arithmetic_v<N>> = nullptr>\nSPECTRE_ALWAYS_INLINE auto operator-(\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t,\n    const N number) {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only subtract a number from a tensor expression that evaluates to a \"\n      \"rank 0 tensor.\");\n  return t + tenex::NumberAsExpression<N>(-number);\n}\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args, typename N,\n          Requires<std::is_arithmetic_v<N>> = nullptr>\nSPECTRE_ALWAYS_INLINE auto operator-(\n    const N number,\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t) {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only subtract a number from a tensor expression that evaluates to a \"\n      \"rank 0 tensor.\");\n  return tenex::NumberAsExpression<N>(number) - t;\n}\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args, typename N>\nSPECTRE_ALWAYS_INLINE auto operator-(\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t,\n    const std::complex<N>& number) {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only subtract a number from a tensor expression that evaluates to a \"\n      \"rank 0 tensor.\");\n  return t + tenex::NumberAsExpression(-number);\n}\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args, typename N>\nSPECTRE_ALWAYS_INLINE auto operator-(\n    const std::complex<N>& number,\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t) {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only subtract a number from a tensor expression that evaluates to a \"\n      \"rank 0 tensor.\");\n  return tenex::NumberAsExpression<N>(number) - t;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AddSubtract.hpp\n  Contract.hpp\n  DataTypeSupport.hpp\n  Divide.hpp\n  Evaluate.hpp\n  IndexPropertyCheck.hpp\n  LhsTensorSymmAndIndices.hpp\n  Negate.hpp\n  NumberAsExpression.hpp\n  Product.hpp\n  SpatialSpacetimeIndex.hpp\n  SquareRoot.hpp\n  TensorAsExpression.hpp\n  TensorExpression.hpp\n  TensorIndex.hpp\n  TensorIndexTransformation.hpp\n  TimeIndex.hpp\n  )\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/Contract.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines Expression Templates for contracting tensor indices on a single\n/// Tensor\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/Tensor/Expressions/NumberAsExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * Holds all possible TensorExpressions currently implemented\n */\nnamespace tenex {\nnamespace detail {\ntemplate <typename I1, typename I2>\nusing indices_contractible = std::bool_constant<\n    I1::ul != I2::ul and\n    std::is_same_v<typename I1::Frame, typename I2::Frame> and\n    ((I1::index_type == I2::index_type and I1::dim == I2::dim) or\n     // If one index is spacetime and the other is spatial, the indices can\n     // be contracted if they have the same number of spatial dimensions\n     (I1::index_type == IndexType::Spacetime and I1::dim == I2::dim + 1) or\n     (I2::index_type == IndexType::Spacetime and I1::dim + 1 == I2::dim))>;\n\ntemplate <size_t NumUncontractedIndices>\nconstexpr size_t get_num_contracted_index_pairs(\n    const std::array<size_t, NumUncontractedIndices>&\n        uncontracted_tensor_index_values) {\n  size_t count = 0;\n  for (size_t i = 0; i < NumUncontractedIndices; i++) {\n    const size_t current_value = gsl::at(uncontracted_tensor_index_values, i);\n    // Concrete time indices are not contracted\n    if (not detail::is_time_index_value(current_value)) {\n      const size_t opposite_value_to_find =\n          get_tensorindex_value_with_opposite_valence(current_value);\n      for (size_t j = i + 1; j < NumUncontractedIndices; j++) {\n        if (opposite_value_to_find ==\n            gsl::at(uncontracted_tensor_index_values, j)) {\n          // We found both the lower and upper version of a generic index in the\n          // list of generic indices, so we return this pair's positions\n          count++;\n        }\n      }\n    }\n  }\n\n  return count;\n}\n\n/// \\brief Computes the mapping from the positions of indices in the resultant\n/// contracted tensor to their positions in the operand uncontracted tensor, as\n/// well as the positions of the index pairs in the operand uncontracted tensor\n/// that we wish to contract\n///\n/// \\details\n/// Both quantities returned are computed in and returned from the same\n/// function so as to not repeat overlapping necessary work that would be in two\n/// separate functions\n///\n/// \\tparam NumContractedIndexPairs the number of pairs of indices that will be\n/// contracted\n/// \\tparam NumUncontractedIndices the number of indices in the operand\n/// expression that we wish to contract\n/// \\param uncontracted_tensor_index_values the values of the `TensorIndex`s\n/// used to generically represent the uncontracted operand expression\n/// \\return the mapping from the positions of indices in the resultant\n/// contracted tensor to their positions in the operand uncontracted tensor, as\n/// well as the positions of the index pairs in the operand uncontracted tensor\n/// that we wish to contract\ntemplate <size_t NumContractedIndexPairs, size_t NumUncontractedIndices>\nconstexpr std::pair<\n    std::array<size_t, NumUncontractedIndices - NumContractedIndexPairs * 2>,\n    std::array<std::pair<size_t, size_t>, NumContractedIndexPairs>>\nget_index_transformation_and_contracted_pair_positions(\n    const std::array<size_t, NumUncontractedIndices>&\n        uncontracted_tensor_index_values) {\n  static_assert(NumUncontractedIndices >= 2,\n                \"There should be at least 2 indices\");\n  // Positions of indices in the result tensor (ones that are not contracted)\n  // mapped to their locations in the uncontracted operand expression\n  std::array<size_t, NumUncontractedIndices - NumContractedIndexPairs * 2>\n      index_transformation{};\n  // Positions of contracted index pairs in the uncontracted operand expression\n  std::array<std::pair<size_t, size_t>, NumContractedIndexPairs>\n      contracted_index_pair_positions{};\n\n  // Marks whether or not we have already paired an index in the uncontracted\n  // operand expression with an index to contract it with\n  std::array<bool, NumUncontractedIndices> index_mapping_set =\n      make_array<NumUncontractedIndices, bool>(false);\n\n  // Index of `contracted_index_pair_positions` that we're currently assigning\n  size_t contracted_map_index_to_assign = 0;\n  // Index of `index_transformation` that we're currently assigning\n  size_t not_contracted_map_index_to_assign =\n      NumUncontractedIndices - NumContractedIndexPairs * 2 - 1;\n  // Iteration is performed backwards here for the reasons below, but note that\n  // no benchmarking has been done to confirm the backwards iteration order\n  // makes a meaningful improvement in runtime vs. iterating forward:\n  //\n  // Here, we iterate backwards to find the \"rightmost\" contracted indices and\n  // proceed leftwards to find the other contracted index pairs so that the\n  // \"rightmost\" pairs in the expression to contract appear first in the list of\n  // contracted index pairs (`contracted_index_pair_positions`). Then, when we\n  // later iterate over all of the multi-indices to sum, each time we go to grab\n  // the next multi-index to sum and we need to compute what that next\n  // multi-index is, we can choose to increment the concrete values of the\n  // rightmost index pair.\n  //\n  // This has not been benchmarked to confirm, but the thought with making this\n  // choice to order the contracted pairs from right to left is that this may\n  // help with spatial locality for caching when we are contracting a single\n  // tensor (`TensorAsExpression`) that is non-symmetric. For example, let's say\n  // we are contracting `R(ti::A, ti::B, ti::a, ti::b)` and the current\n  // multi-index we just accessed (one of the components to sum) is\n  // `{0, 0, 0, 0}`, representing \\f$R^{00}{}_{00}\\f$. To find a next\n  // multi-index to sum, we can simply increase one of the pairs' concrete\n  // values by 1, e.g. the next multi-index to access could be `{0, 1, 0, 1}`\n  // for \\f$R^{01}{}_{01}\\f$ or `{1, 0, 1, 0}` \\f$R^{10}{}_{10}\\f$. In this\n  // case, the idea is that choosing to increment the concrete values of the\n  // rightmost pair could provide better spatial locality, as `{0, 1, 0, 1}` is\n  // closer in memory to `{0, 0, 0, 0}` than `{1, 0, 1, 0}` is. It's important\n  // to note that this, of course, depends on the implementation of\n  // `Tensor_detail::Structure` - specifically, the order in which the\n  // components are laid out in memory.\n  //\n  // Note: the loop terminates when underflow causes `i` to wrap back around to\n  // the maximum `size_t` value. If we use the condition `i > 0`, we miss the\n  // final iteration, and if we use `i >= 0`, we never terminate because `i`\n  // is always positive.\n  for (size_t i = NumUncontractedIndices - 1; i < NumUncontractedIndices; i--) {\n    if (not gsl::at(index_mapping_set, i)) {\n      const size_t current_value = gsl::at(uncontracted_tensor_index_values, i);\n      // Concrete time indices are not contracted\n      if (not detail::is_time_index_value(current_value)) {\n        const size_t opposite_value_to_find =\n            get_tensorindex_value_with_opposite_valence(current_value);\n        for (size_t j = i - 1; j < NumUncontractedIndices; j--) {\n          if (opposite_value_to_find ==\n              gsl::at(uncontracted_tensor_index_values, j)) {\n            // We found both the lower and upper version of a generic index in\n            // the list of generic indices, pair them up\n            gsl::at(contracted_index_pair_positions,\n                    contracted_map_index_to_assign)\n                .first = i;\n            gsl::at(contracted_index_pair_positions,\n                    contracted_map_index_to_assign)\n                .second = j;\n            contracted_map_index_to_assign++;\n            // Mark that we've found contraction partners for these two indices\n            gsl::at(index_mapping_set, i) = true;\n            gsl::at(index_mapping_set, j) = true;\n            break;\n          }\n        }\n      }\n      if (not gsl::at(index_mapping_set, i)) {\n        // If we haven't assigned this index to a partner, it is not an index\n        // that is contracted, so record its position mapping from contracted to\n        // uncontracted tensor indices\n        gsl::at(index_transformation, not_contracted_map_index_to_assign) = i;\n        not_contracted_map_index_to_assign--;\n      }\n    }\n  }\n\n  return std::pair{index_transformation, contracted_index_pair_positions};\n}\n\n/// \\brief Computes type information for the tensor expression that results from\n/// a contraction, as well as information internally useful for carrying out the\n/// contraction\n///\n/// \\tparam UncontractedTensorExpression the operand uncontracted\n/// `TensorExpression` being contracted\n/// \\tparam DataType the data type of the `Tensor` components\n/// \\tparam UncontractedSymm the ::Symmetry of the operand uncontracted\n/// `TensorExpression`\n/// \\tparam UncontractedIndexList the list of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s of the operand uncontracted\n/// `TensorExpression`\n/// \\tparam UncontractedTensorIndexList the list of generic `TensorIndex`s used\n/// for the the operand uncontracted `TensorExpression`\n/// \\tparam NumContractedIndices the number of indices in the resultant tensor\n/// after contracting\n/// \\tparam NumIndexPairsToContract the number of pairs of indices that will be\n/// contracted\ntemplate <typename UncontractedTensorExpression, typename DataType,\n          typename UncontractedSymm, typename UncontractedIndexList,\n          typename UncontractedTensorIndexList, size_t NumContractedIndices,\n          size_t NumIndexPairsToContract,\n          typename ContractedIndexSequence =\n              std::make_index_sequence<NumContractedIndices>,\n          typename IndexPairsToContractSequence =\n              std::make_index_sequence<NumIndexPairsToContract>>\nstruct ContractedType;\n\ntemplate <typename UncontractedTensorExpression, typename DataType,\n          template <typename...> class UncontractedSymmList,\n          typename... UncontractedSymm,\n          template <typename...> class UncontractedIndexList,\n          typename... UncontractedIndices,\n          template <typename...> class UncontractedTensorIndexList,\n          typename... UncontractedTensorIndices, size_t NumContractedIndices,\n          size_t NumIndexPairsToContract, size_t... ContractedInts,\n          size_t... IndexPairsToContractInts>\nstruct ContractedType<UncontractedTensorExpression, DataType,\n                      UncontractedSymmList<UncontractedSymm...>,\n                      UncontractedIndexList<UncontractedIndices...>,\n                      UncontractedTensorIndexList<UncontractedTensorIndices...>,\n                      NumContractedIndices, NumIndexPairsToContract,\n                      std::index_sequence<ContractedInts...>,\n                      std::index_sequence<IndexPairsToContractInts...>> {\n  static constexpr size_t num_uncontracted_tensor_indices =\n      sizeof...(UncontractedTensorIndices);\n  static constexpr std::array<size_t, num_uncontracted_tensor_indices>\n      uncontracted_tensorindex_values = {{UncontractedTensorIndices::value...}};\n  static constexpr size_t num_indices_to_contract =\n      num_uncontracted_tensor_indices - NumContractedIndices;\n  static constexpr size_t num_contracted_index_pairs =\n      num_indices_to_contract / 2;\n  // First item in pair:\n  // - index transformation: mapping from the positions of indices in the\n  // resultant contracted tensor to their positions in the operand\n  // uncontracted tensor\n  // Second item in pair:\n  // contracted index pair positions: positions of the index pairs in the\n  // operand uncontracted tensor that we wish to contract\n  static constexpr inline std::pair<\n      std::array<size_t, NumContractedIndices>,\n      std::array<std::pair<size_t, size_t>, num_contracted_index_pairs>>\n      index_transformation_and_contracted_pair_positions =\n          get_index_transformation_and_contracted_pair_positions<\n              num_contracted_index_pairs, num_uncontracted_tensor_indices>(\n              uncontracted_tensorindex_values);\n\n  // Make sure it's mathematically legal to perform the requested contraction\n  static_assert(((... and\n                  (indices_contractible<\n                      typename tmpl::at_c<\n                          tmpl::list<UncontractedIndices...>,\n                          index_transformation_and_contracted_pair_positions\n                              .second[IndexPairsToContractInts]\n                              .first>,\n                      typename tmpl::at_c<\n                          tmpl::list<UncontractedIndices...>,\n                          index_transformation_and_contracted_pair_positions\n                              .second[IndexPairsToContractInts]\n                              .second>>::value))),\n                \"Cannot contract the requested indices.\");\n\n  static constexpr inline std::array<IndexType, num_uncontracted_tensor_indices>\n      uncontracted_index_types = {{UncontractedIndices::index_type...}};\n\n  // First concrete values of contracted indices to sum. This is to handle\n  // cases when we have generic spatial `TensorIndex`s used for spacetime\n  // indices, as the first concrete index value to contract will be 1 (first\n  // spatial index) instead of 0 (the time index). Contracted index pairs will\n  // have different \"starting\" concrete indices when one index in the pair is a\n  // spatial spacetime index and the other is not.\n  static constexpr inline std::array<std::pair<size_t, size_t>,\n                                     num_contracted_index_pairs>\n      contracted_index_first_values = []() {\n        std::array<std::pair<size_t, size_t>, num_contracted_index_pairs>\n            first_values{};\n        for (size_t i = 0; i < num_contracted_index_pairs; i++) {\n          // Assign the value for first index in a pair to be the smallest value\n          // used in the terms being summed: assign to 1 if we have a spacetime\n          // index where a generic spatial index has been used, otherwise assign\n          // to 0.\n          gsl::at(first_values, i).first = static_cast<size_t>(\n              gsl::at(\n                  uncontracted_index_types,\n                  gsl::at(\n                      index_transformation_and_contracted_pair_positions.second,\n                      i)\n                      .first) == IndexType::Spacetime and\n              gsl::at(\n                  uncontracted_tensorindex_values,\n                  gsl::at(\n                      index_transformation_and_contracted_pair_positions.second,\n                      i)\n                      .first) >= TensorIndex_detail::spatial_sentinel);\n          // Assign the value for second index in a pair to be the smallest\n          // value used in the terms being summed (assigned with same logic\n          // described above for the first index in the pair)\n          gsl::at(first_values, i).second = static_cast<size_t>(\n              gsl::at(\n                  uncontracted_index_types,\n                  gsl::at(\n                      index_transformation_and_contracted_pair_positions.second,\n                      i)\n                      .second) == IndexType::Spacetime and\n              gsl::at(\n                  uncontracted_tensorindex_values,\n                  gsl::at(\n                      index_transformation_and_contracted_pair_positions.second,\n                      i)\n                      .second) >= TensorIndex_detail::spatial_sentinel);\n        }\n        return first_values;\n      }();\n\n  static constexpr inline std::array<size_t, num_uncontracted_tensor_indices>\n      uncontracted_index_dims = {{UncontractedIndices::dim...}};\n\n  // The number of terms to sum for this expression's contraction\n  static constexpr size_t num_terms_summed = []() {\n    size_t num_terms =\n        gsl::at(\n            uncontracted_index_dims,\n            gsl::at(index_transformation_and_contracted_pair_positions.second,\n                    0)\n                .first) -\n        gsl::at(contracted_index_first_values, 0).first;\n    for (size_t i = 1; i < num_contracted_index_pairs; i++) {\n      num_terms *=\n          gsl::at(\n              uncontracted_index_dims,\n              gsl::at(index_transformation_and_contracted_pair_positions.second,\n                      i)\n                  .first) -\n          gsl::at(contracted_index_first_values, i).first;\n    }\n    return num_terms;\n  }();\n  static_assert(num_terms_summed > 0,\n                \"There should be a non-zero number of components to sum in the \"\n                \"contraction.\");\n  // The ::Symmetry of the result of the contraction\n  using symmetry =\n      Symmetry<tmpl::at_c<UncontractedSymmList<UncontractedSymm...>,\n                          index_transformation_and_contracted_pair_positions\n                              .first[ContractedInts]>::value...>;\n  // The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  // contraction\n  using index_list =\n      tmpl::list<tmpl::at_c<UncontractedIndexList<UncontractedIndices...>,\n                            index_transformation_and_contracted_pair_positions\n                                .first[ContractedInts]>...>;\n  // The list of generic `TensorIndex`s of the result of the contraction\n  using tensorindex_list = tmpl::list<\n      tmpl::at_c<UncontractedTensorIndexList<UncontractedTensorIndices...>,\n                 index_transformation_and_contracted_pair_positions\n                     .first[ContractedInts]>...>;\n  // The `TensorExpression` type that results from performing the contraction\n  using type = TensorExpression<UncontractedTensorExpression, DataType,\n                                symmetry, index_list, tensorindex_list>;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup TensorExpressionsGroup\n */\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename ArgsList, size_t NumContractedIndices>\nstruct TensorContract\n    : public TensorExpression<\n          TensorContract<T, X, Symm, IndexList, ArgsList, NumContractedIndices>,\n          X,\n          typename detail::ContractedType<\n              T, X, Symm, IndexList, ArgsList, NumContractedIndices,\n              (tmpl::size<Symm>::value - NumContractedIndices) /\n                  2>::type::symmetry,\n          typename detail::ContractedType<\n              T, X, Symm, IndexList, ArgsList, NumContractedIndices,\n              (tmpl::size<Symm>::value - NumContractedIndices) /\n                  2>::type::index_list,\n          typename detail::ContractedType<\n              T, X, Symm, IndexList, ArgsList, NumContractedIndices,\n              (tmpl::size<Symm>::value - NumContractedIndices) /\n                  2>::type::args_list> {\n  /// Stores internally useful information regarding the contraction. See\n  /// `detail::ContractedType` for more details\n  using contracted_type = typename detail::ContractedType<\n      T, X, Symm, IndexList, ArgsList, NumContractedIndices,\n      (tmpl::size<Symm>::value - NumContractedIndices) / 2>;\n  /// The `TensorExpression` type that results from performing the contraction\n  using new_type = typename contracted_type::type;\n\n  // === Index properties ===\n  /// The type of the data being stored in the result of the expression\n  using type = X;\n  /// The ::Symmetry of the result of the expression\n  using symmetry = typename new_type::symmetry;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using index_list = typename new_type::index_list;\n  /// The list of generic `TensorIndex`s of the result of the expression\n  using args_list = typename new_type::args_list;\n  /// The number of tensor indices in the result of the expression\n  static constexpr size_t num_tensor_indices = NumContractedIndices;\n  /// The number of tensor indices in the operand expression being contracted\n  static constexpr size_t num_uncontracted_tensor_indices =\n      tmpl::size<Symm>::value;\n  /// The number of tensor indices in the operand expression that will be\n  /// contracted\n  static constexpr size_t num_indices_to_contract =\n      contracted_type::num_indices_to_contract;\n  static_assert(num_indices_to_contract > 0,\n                \"There are no indices to contract that were found.\");\n  static_assert(num_indices_to_contract % 2 == 0,\n                \"Cannot contract an odd number of indices.\");\n  /// The number of tensor index pairs in the operand expression that will be\n  /// contracted\n  static constexpr size_t num_contracted_index_pairs =\n      contracted_type::num_contracted_index_pairs;\n  /// Mapping from the positions of indices in the resultant contracted tensor\n  /// to their positions in the operand uncontracted tensor\n  static constexpr inline std::array<size_t, NumContractedIndices>\n      index_transformation =\n          contracted_type::index_transformation_and_contracted_pair_positions\n              .first;\n  /// Positions of the index pairs in the operand uncontracted tensor that we\n  /// wish to contract\n  static constexpr inline std::array<std::pair<size_t, size_t>,\n                                     num_contracted_index_pairs>\n      contracted_index_pair_positions =\n          contracted_type::index_transformation_and_contracted_pair_positions\n              .second;\n  /// First concrete values of contracted indices to sum. This is to handle\n  /// cases when we have generic spatial `TensorIndex`s used for spacetime\n  /// indices, as the first concrete index value to contract will be 1 (first\n  /// spatial index) instead of 0 (the time index). Contracted index pairs will\n  /// have different \"starting\" concrete indices when one index in the pair is a\n  /// spatial spacetime index and the other is not.\n  static constexpr inline std::array<std::pair<size_t, size_t>,\n                                     num_contracted_index_pairs>\n      contracted_index_first_values =\n          contracted_type::contracted_index_first_values;\n  /// The dimensions of the indices in the uncontracted operand expression\n  static constexpr inline std::array<size_t, num_uncontracted_tensor_indices>\n      uncontracted_index_dims = contracted_type::uncontracted_index_dims;\n  /// The number of terms to sum for this expression's contraction\n  static constexpr size_t num_terms_summed = contracted_type::num_terms_summed;\n\n  // === Expression subtree properties ===\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// left operand\n  static constexpr size_t num_ops_left_child =\n      T::num_ops_subtree * num_terms_summed + num_terms_summed - 1;\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// right operand. This is 0 because this expression represents a unary\n  /// operation.\n  static constexpr size_t num_ops_right_child = 0;\n  /// The total number of arithmetic tensor operations done in this expression's\n  /// whole subtree\n  static constexpr size_t num_ops_subtree = num_ops_left_child;\n  /// The height of this expression's node in the expression tree relative to\n  /// the closest `TensorAsExpression` leaf in its subtree\n  static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree =\n      T::height_relative_to_closest_tensor_leaf_in_subtree !=\n              std::numeric_limits<size_t>::max()\n          ? T::height_relative_to_closest_tensor_leaf_in_subtree + 1\n          : T::height_relative_to_closest_tensor_leaf_in_subtree;\n\n  // === Properties for splitting up subexpressions along the primary path ===\n  // These definitions only have meaning if this expression actually ends up\n  // being along the primary path that is taken when evaluating the whole tree.\n  // See documentation for `TensorExpression` for more details.\n  /// If on the primary path, whether or not the expression is an ending point\n  /// of a leg\n  static constexpr bool is_primary_end = T::is_primary_start;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the subtree of the child along the\n  /// primary path, given that we will have already computed the whole subtree\n  /// at the next lowest leg's starting point.\n  static constexpr size_t num_ops_to_evaluate_primary_left_child =\n      is_primary_end\n          ? num_ops_subtree - T::num_ops_subtree\n          : T::num_ops_subtree * (num_terms_summed - 1) +\n                T::num_ops_to_evaluate_primary_subtree + num_terms_summed - 1;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the right operand's subtree. No\n  /// splitting is currently done, so this is just `num_ops_right_child`.\n  static constexpr size_t num_ops_to_evaluate_primary_right_child =\n      num_ops_right_child;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done for this expression's subtree, given that\n  /// we will have already computed the subtree at the next lowest leg's\n  /// starting point\n  static constexpr size_t num_ops_to_evaluate_primary_subtree =\n      num_ops_to_evaluate_primary_left_child +\n      num_ops_to_evaluate_primary_right_child;\n  /// If on the primary path, whether or not the expression is a starting point\n  /// of a leg\n  static constexpr bool is_primary_start =\n      num_ops_to_evaluate_primary_subtree >\n      // Multiply by 2 because each term has a + and * operation, while other\n      // arithmetic expression types do one operation\n      2 * detail::max_num_ops_in_sub_expression<type>;\n  /// If on the primary path, whether or not the expression's child along the\n  /// primary path is a subtree that contains a starting point of a leg along\n  /// the primary path\n  static constexpr bool primary_child_subtree_contains_primary_start =\n      T::primary_subtree_contains_primary_start;\n  /// If on the primary path, whether or not this subtree contains a starting\n  /// point of a leg along the primary path\n  static constexpr bool primary_subtree_contains_primary_start =\n      is_primary_start or primary_child_subtree_contains_primary_start;\n  /// Number of arithmetic tensor operations done in the subtree of the operand\n  /// expression being contracted\n  static constexpr size_t num_ops_subexpression = T::num_ops_subtree;\n  /// In the subtree for this contraction, how many terms we sum together for\n  /// each leg of the contraction\n  static constexpr size_t leg_length = []() {\n    if constexpr (not is_primary_start) {\n      // If we're not even stopping at the beginning of the contraction, it's\n      // because there weren't enough terms to justify any splitting, so the\n      // leg_length is just the total number of terms to sum\n      return num_terms_summed;\n    } else if constexpr (num_ops_subexpression >=\n                         detail::max_num_ops_in_sub_expression<type>) {\n      // If the subexpression itself has more than the max # of ops\n      return 0;\n    } else {\n      // Otherwise, find how many terms to sum in each leg\n      size_t length = 1;\n      while (2 * (length * (num_ops_subexpression + 1) - 1) <=\n             detail::max_num_ops_in_sub_expression<type>) {\n        length *= 2;\n      }\n      return length;\n    }\n  }();\n  /// After dividing up the contraction subtree into legs, the number of legs\n  /// whose length is equal to `leg_length`\n  static constexpr size_t num_full_legs =\n      leg_length == 0 ? num_terms_summed : num_terms_summed / leg_length;\n  /// After dividing up the contraction subtree into legs of even length, the\n  /// number of terms we still have left to sum\n  static constexpr size_t last_leg_length =\n      leg_length == 0 ? 0 : num_terms_summed % leg_length;\n  /// When evaluating along a primary path, whether each term's subtrees should\n  /// be evaluated separately. Since `DataVector` expression runtime scales\n  /// poorly with increased number of operations, evaluating individual terms'\n  /// subtrees separately like this is beneficial when each term, itself,\n  /// involves many tensor operations.\n  static constexpr bool evaluate_terms_separately = leg_length == 0;\n\n  explicit TensorContract(\n      const TensorExpression<T, X, Symm, IndexList, ArgsList>& t)\n      : t_(~t) {}\n  ~TensorContract() override = default;\n\n  /// \\brief Assert that the LHS tensor of the equation does not also appear in\n  /// this expression's subtree\n  template <typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensor_not_in_rhs_expression(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T>) {\n      t_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n  }\n\n  /// \\brief Assert that each instance of the LHS tensor in the RHS tensor\n  /// expression uses the same generic index order that the LHS uses\n  ///\n  /// \\tparam LhsTensorIndices the list of generic `TensorIndex`s of the LHS\n  /// result `Tensor` being computed\n  /// \\param lhs_tensor the LHS result `Tensor` being computed\n  template <typename LhsTensorIndices, typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensorindices_same_in_rhs(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T>) {\n      t_.template assert_lhs_tensorindices_same_in_rhs<LhsTensorIndices>(\n          lhs_tensor);\n    }\n  }\n\n  /// \\brief Get the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  ///\n  /// \\return the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  SPECTRE_ALWAYS_INLINE size_t get_rhs_tensor_component_size() const {\n    return t_.get_rhs_tensor_component_size();\n  }\n\n  /// \\brief Return the highest multi-index between the components being summed\n  /// in the contraction\n  ///\n  /// \\details\n  /// Example:\n  /// We have expression `R(ti::A, ti::b, ti::a)` to represent the contraction\n  /// \\f$L_b = R^{a}{}_{ba}\\f$. If the `contracted_multi_index` is `{1}`, which\n  /// represents \\f$L_1 = R^{a}{}_{1a}\\f$, and the dimension of \\f$a\\f$ is 3,\n  /// then we will need to sum the following terms: \\f$R^{0}{}_{10}\\f$,\n  /// \\f$R^{1}{}_{11}\\f$, and \\f$R^{2}{}_{12}\\f$. Between the terms being\n  /// summed, the multi-index whose values are the largest is\n  /// \\f$R^{2}{}_{12}\\f$, so this function would return `{2, 1, 2}`.\n  ///\n  /// \\param contracted_multi_index the multi-index of a component of the\n  /// contracted expression\n  /// \\return the highest multi-index between the components being summed in\n  /// the contraction\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<\n      size_t, num_uncontracted_tensor_indices>\n  get_highest_multi_index_to_sum(\n      const std::array<size_t, num_tensor_indices>& contracted_multi_index) {\n    // Initialize with placeholders for debugging\n    auto highest_multi_index = make_array<num_uncontracted_tensor_indices>(\n        std::numeric_limits<size_t>::max());\n\n    // Fill uncontracted indices\n    for (size_t i = 0; i < num_tensor_indices; i++) {\n      gsl::at(highest_multi_index, gsl::at(index_transformation, i)) =\n          gsl::at(contracted_multi_index, i);\n    }\n\n    // Fill contracted indices\n    for (size_t i = 0; i < num_contracted_index_pairs; i++) {\n      const size_t first_index_position_in_pair =\n          gsl::at(contracted_index_pair_positions, i).first;\n      const size_t second_index_position_in_pair =\n          gsl::at(contracted_index_pair_positions, i).second;\n      gsl::at(highest_multi_index, first_index_position_in_pair) =\n          gsl::at(uncontracted_index_dims, first_index_position_in_pair) - 1;\n      gsl::at(highest_multi_index, second_index_position_in_pair) =\n          gsl::at(uncontracted_index_dims, second_index_position_in_pair) - 1;\n    }\n\n    return highest_multi_index;\n  }\n\n  /// \\brief Return the lowest multi-index between the components being summed\n  /// in the contraction\n  ///\n  /// \\details\n  /// Example:\n  /// We have expression `R(ti::A, ti::b, ti::a)` to represent the contraction\n  /// \\f$L_b = R^{a}{}_{ba}\\f$. If the `contracted_multi_index` is `{1}`, which\n  /// represents \\f$L_1 = R^{a}{}_{1a}\\f$, and the dimension of \\f$a\\f$ is 3,\n  /// then we will need to sum the following terms: \\f$R^{0}{}_{10}\\f$,\n  /// \\f$R^{1}{}_{11}\\f$, and \\f$R^{2}{}_{12}\\f$. Between the terms being\n  /// summed, the multi-index whose values are the smallest is\n  /// \\f$R^{0}{}_{10}\\f$, so this function would return `{0, 1, 0}`.\n  ///\n  /// \\param contracted_multi_index the multi-index of a component of the\n  /// contracted expression\n  /// \\return the lowest multi-index between the components being summed in\n  /// the contraction\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<\n      size_t, num_uncontracted_tensor_indices>\n  get_lowest_multi_index_to_sum(\n      const std::array<size_t, num_tensor_indices>& contracted_multi_index) {\n    // Initialize with placeholders for debugging\n    auto lowest_multi_index = make_array<num_uncontracted_tensor_indices>(\n        std::numeric_limits<size_t>::max());\n\n    // Fill uncontracted indices\n    for (size_t i = 0; i < num_tensor_indices; i++) {\n      gsl::at(lowest_multi_index, gsl::at(index_transformation, i)) =\n          gsl::at(contracted_multi_index, i);\n    }\n\n    // Fill contracted indices\n    for (size_t i = 0; i < num_contracted_index_pairs; i++) {\n      const size_t first_index_position_in_pair =\n          gsl::at(contracted_index_pair_positions, i).first;\n      const size_t second_index_position_in_pair =\n          gsl::at(contracted_index_pair_positions, i).second;\n      gsl::at(lowest_multi_index, first_index_position_in_pair) =\n          gsl::at(contracted_index_first_values, i).first;\n      gsl::at(lowest_multi_index, second_index_position_in_pair) =\n          gsl::at(contracted_index_first_values, i).second;\n    }\n\n    return lowest_multi_index;\n  }\n\n  /// \\brief Given the multi-index of one term being summed in the contraction,\n  /// return the next highest multi-index of a component being summed\n  ///\n  /// \\details\n  /// What is meant by \"next highest\" is implementation defined, but generally\n  /// means, of the components being summed, return the multi-index that results\n  /// from lowering one of the contracted index pairs' values by one.\n  ///\n  /// Example:\n  /// We have expression `R(ti::A, ti::b, ti::a)` to represent the contraction\n  /// \\f$L_b = R^{a}{}_{ba}\\f$. If we are evaluating \\f$L_1 = R^{a}{}_{1a}\\f$\n  ///  and the dimension of \\f$a\\f$ is 3, then we will need to sum the following\n  /// terms: \\f$R^{0}{}_{10}\\f$, \\f$R^{1}{}_{11}\\f$, and \\f$R^{2}{}_{12}\\f$.\n  /// If `uncontracted_multi_index` is `{1, 1, 1}`, then the \"next highest\"\n  /// multi-index is the result of lowering the values of the \\f$a\\f$ indices by\n  /// 1. The component with that resulting multi-index is \\f$R^{0}{}_{10}\\f$, so\n  /// this function would return `{0, 1, 0}`.\n  ///\n  /// Note: this function should perform the inverse functionality of\n  /// `get_next_highest_multi_index_to_sum`. If the implementation of this\n  /// function or the other changes what is meant by \"next highest\" or \"next\n  /// lowest,\" the other function should be updated in accordance.\n  ///\n  /// \\param uncontracted_multi_index the multi-index of one of the components\n  /// of the uncontracted operand expression to sum\n  /// \\return the next highest multi-index between the components being summed\n  /// in the contraction\n  SPECTRE_ALWAYS_INLINE static std::array<size_t,\n                                          num_uncontracted_tensor_indices>\n  get_next_highest_multi_index_to_sum(\n      const std::array<size_t, num_uncontracted_tensor_indices>&\n          uncontracted_multi_index) {\n    std::array<size_t, num_uncontracted_tensor_indices>\n        next_highest_uncontracted_multi_index = uncontracted_multi_index;\n\n    size_t i = 0;\n    while (i < num_contracted_index_pairs) {\n      // the position of the first index in a pair being contracted\n      const size_t current_index_first_position =\n          gsl::at(contracted_index_pair_positions, i).first;\n      // the position of the second index in a pair being contracted\n      const size_t current_index_second_position =\n          gsl::at(contracted_index_pair_positions, i).second;\n      // of the values being summed over, the lowest concrete value of the first\n      // index in the contracted pair\n      const size_t current_index_first_first_value =\n          gsl::at(contracted_index_first_values, i).first;\n\n      // decrement the current index pair's values\n      gsl::at(next_highest_uncontracted_multi_index,\n              current_index_first_position)--;\n      gsl::at(next_highest_uncontracted_multi_index,\n              current_index_second_position)--;\n\n      // If the index values of the index pair being contracted aren't lower\n      // than the minimum values included in the summation, then we're done\n      // computing this next multi-index\n      if (not(gsl::at(next_highest_uncontracted_multi_index,\n                      current_index_first_position) <\n                  current_index_first_first_value or\n              gsl::at(next_highest_uncontracted_multi_index,\n                      current_index_first_position) >\n                  gsl::at(uncontracted_index_dims,\n                          current_index_first_position))) {\n        break;\n      }\n      // Otherwise, we've wrapped around the lowest value being summed over for\n      // this index, so we need to set it back to the maximum values being\n      // summed and \"carry\" the decrementing over to the next contracted pair's\n      // values\n      gsl::at(next_highest_uncontracted_multi_index,\n              current_index_first_position) =\n          gsl::at(uncontracted_index_dims, current_index_first_position) - 1;\n      gsl::at(next_highest_uncontracted_multi_index,\n              current_index_second_position) =\n          gsl::at(uncontracted_index_dims, current_index_second_position) - 1;\n\n      i++;\n    }\n\n    return next_highest_uncontracted_multi_index;\n  }\n\n  /// \\brief Given the multi-index of one term being summed in the contraction,\n  /// return the next lowest multi-index of a component being summed\n  ///\n  /// \\details\n  /// What is meant by \"next lowest\" is implementation defined, but generally\n  /// means, of the components being summed, return the multi-index that results\n  /// from raising one of the contracted index pairs' values by one.\n  ///\n  /// Example:\n  /// We have expression `R(ti::A, ti::b, ti::a)` to represent the contraction\n  /// \\f$L_b = R^{a}{}_{ba}\\f$. If we are evaluating \\f$L_1 = R^{a}{}_{1a}\\f$\n  ///  and the dimension of \\f$a\\f$ is 3, then we will need to sum the following\n  /// terms: \\f$R^{0}{}_{10}\\f$, \\f$R^{1}{}_{11}\\f$, and \\f$R^{2}{}_{12}\\f$.\n  /// If `uncontracted_multi_index` is `{1, 1, 1}`, then the \"next lowest\"\n  /// multi-index is the result of raising the values of the \\f$a\\f$ indices by\n  /// 1. The component with that resulting multi-index is \\f$R^{2}{}_{12}\\f$, so\n  /// this function would return `{2, 1, 2}`.\n  ///\n  /// Note: this function should perform the inverse functionality of\n  /// `get_next_lowest_multi_index_to_sum`. If the implementation of this\n  /// function or the other changes what is meant by \"next highest\" or \"next\n  /// lowest,\" the other function should be updated in accordance.\n  ///\n  /// \\param uncontracted_multi_index the multi-index of one of the components\n  /// of the uncontracted operand expression to sum\n  /// \\return the next lowest multi-index between the components being summed in\n  /// the contraction\n  SPECTRE_ALWAYS_INLINE static std::array<size_t,\n                                          num_uncontracted_tensor_indices>\n  get_next_lowest_multi_index_to_sum(\n      const std::array<size_t, num_uncontracted_tensor_indices>&\n          uncontracted_multi_index) {\n    std::array<size_t, num_uncontracted_tensor_indices>\n        next_lowest_uncontracted_multi_index = uncontracted_multi_index;\n\n    size_t i = 0;\n    while (i < num_contracted_index_pairs) {\n      // the position of the first index in a pair being contracted\n      const size_t current_index_first_position =\n          gsl::at(contracted_index_pair_positions, i).first;\n      // the position of the second index in a pair being contracted\n      const size_t current_index_second_position =\n          gsl::at(contracted_index_pair_positions, i).second;\n\n      // increment the current index pair's values\n      gsl::at(next_lowest_uncontracted_multi_index,\n              current_index_first_position)++;\n      gsl::at(next_lowest_uncontracted_multi_index,\n              current_index_second_position)++;\n\n      // if the previous index value is > dim, then we've wrapped around\n      // and we need to go again\n      // If the index values of the index pair being contracted aren't higher\n      // than the maximum values included in the summation, then we're done\n      // computing this next multi-index\n      if (not(gsl::at(next_lowest_uncontracted_multi_index,\n                      current_index_first_position) >\n              gsl::at(uncontracted_index_dims, current_index_first_position) -\n                  1)) {\n        break;\n      }\n      // Otherwise, we've wrapped around the highest value being summed over for\n      // this index, so we need to set it back to the minimum values being\n      // summed and \"carry\" the incrementing over to the next contracted pair's\n      // values\n      gsl::at(next_lowest_uncontracted_multi_index,\n              current_index_first_position) =\n          gsl::at(contracted_index_first_values, i).first;\n      gsl::at(next_lowest_uncontracted_multi_index,\n              current_index_second_position) =\n          gsl::at(contracted_index_first_values, i).second;\n\n      i++;\n    }\n\n    return next_lowest_uncontracted_multi_index;\n  }\n\n  /// \\brief Computes the value of a component in the resultant contracted\n  /// tensor\n  ///\n  /// \\details\n  /// The contraction is computed by recursively adding up each component in the\n  /// summation, across all index pairs being contracted in the operand\n  /// expression. This function is called `Iteration = num_terms_summed` times,\n  /// once for each uncontracted tensor component being summed. It should\n  /// externally be called for the first time with `Iteration == 0` and\n  /// `current_multi_index == <highest multi index to sum>` (see\n  /// `get_next_highest_multi_index_to_sum` for details).\n  ///\n  /// In performing the recursive summation, the recursion is\n  /// specifically done \"to the left,\" in that this function returns\n  /// `compute_contraction(next index) + get(this_index)` as opposed to\n  /// `get(this_index) + compute_contraction`. Benchmarking has shown that\n  /// increased breadth in an equation's expression tree can slow down runtime.\n  /// By \"recursing left\" here, we  minimize breadth in the overall tree for an\n  /// equation, as both `AddSub` addition and `OuterProduct` (other expressions\n  /// with two children) make efforts to make their operands with larger\n  /// subtrees be their left operand.\n  ///\n  /// \\tparam Iteration the nth term to sum, where n is between\n  /// [0, num_terms_summed)\n  /// \\param t the expression contained within this contraction expression\n  /// \\param current_multi_index the multi-index of the uncontracted tensor\n  /// component to retrieve\n  /// \\return the value of a component of the resulant contracted tensor\n  template <size_t Iteration>\n  SPECTRE_ALWAYS_INLINE static decltype(auto) compute_contraction(\n      const T& t, const std::array<size_t, num_uncontracted_tensor_indices>&\n                      current_multi_index) {\n    if constexpr (Iteration < num_terms_summed - 1) {\n      // We have more than one component left to sum\n      return compute_contraction<Iteration + 1>(\n                 t, get_next_highest_multi_index_to_sum(current_multi_index)) +\n             t.get(current_multi_index);\n    } else {\n      // We only have one final component to sum\n      return t.get(current_multi_index);\n    }\n  }\n\n  /// \\brief Return the value of the component of the resultant contracted\n  /// tensor at a given multi-index\n  ///\n  /// \\param contracted_multi_index the multi-index of the resultant contracted\n  /// tensor component to retrieve\n  /// \\return the value of the component at `contracted_multi_index` in the\n  /// resultant contracted tensor\n  decltype(auto) get(const std::array<size_t, num_tensor_indices>&\n                         contracted_multi_index) const {\n    return compute_contraction<0>(\n        t_, get_highest_multi_index_to_sum(contracted_multi_index));\n  }\n\n  /// \\brief Computes the result of an internal leg of the contraction\n  ///\n  /// \\details\n  /// This function differs from `compute_contraction` and\n  /// `compute_contraction_primary` in that it only computes one leg of the\n  /// whole contraction, as opposed to the whole contraction.\n  ///\n  /// The leg being summed is defined by the `current_multi_index` and\n  /// `Iteration` passed in from the inital external call: consecutive terms\n  /// will be summed until the base case `Iteration == 0` is reached.\n  ///\n  /// \\tparam Iteration the nth term in the leg to sum, where n is between\n  /// [0, leg_length)\n  /// \\param t the expression contained within this contraction expression\n  /// \\param current_multi_index the multi-index of the uncontracted tensor\n  /// component to retrieve as part of this leg's summation\n  /// \\param next_leg_starting_multi_index in the final iteration, the\n  /// multi-index to update to be the next leg's starting multi-index\n  /// \\return the result of summing up the terms in the given leg\n  template <size_t Iteration>\n  SPECTRE_ALWAYS_INLINE static decltype(auto) compute_contraction_leg(\n      const T& t,\n      const std::array<size_t, num_uncontracted_tensor_indices>&\n          current_multi_index,\n      std::array<size_t, num_uncontracted_tensor_indices>&\n          next_leg_starting_multi_index) {\n    if constexpr (Iteration != 0) {\n      // We have more than one component left to sum\n      (void)next_leg_starting_multi_index;\n      return compute_contraction_leg<Iteration - 1>(\n                 t, get_next_highest_multi_index_to_sum(current_multi_index),\n                 next_leg_starting_multi_index) +\n             t.get(current_multi_index);\n    } else {\n      // We only have one final component to sum\n      next_leg_starting_multi_index =\n          get_next_highest_multi_index_to_sum(current_multi_index);\n      return t.get(current_multi_index);\n    }\n  }\n\n  /// \\brief Computes the value of a component in the resultant contracted\n  /// tensor\n  ///\n  /// \\details\n  /// First see `compute_contraction` for details on basic functionality.\n  ///\n  /// This function differs from `compute_contraction` in that it takes into\n  /// account whether we have already computed part of the result component at a\n  /// lower subtree. In recursively computing this contraction, the current\n  /// result component will be substituted in for the most recent (highest)\n  /// subtree below it that has already been evaluated.\n  ///\n  /// \\tparam Iteration the nth term to sum, where n is between\n  /// [0, num_terms_summed)\n  /// \\param t the expression contained within this contraction expression\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param current_multi_index the multi-index of the uncontracted tensor\n  /// component to retrieve\n  /// \\return the value of a component of the resulant contracted tensor\n  template <size_t Iteration>\n  SPECTRE_ALWAYS_INLINE static decltype(auto) compute_contraction_primary(\n      const T& t, const type& result_component,\n      const std::array<size_t, num_uncontracted_tensor_indices>&\n          current_multi_index) {\n    if constexpr (is_primary_end) {\n      // We've already computed the whole subtree of the term being summed that\n      // is at the lowest depth in the tree\n      if constexpr (Iteration < num_terms_summed - 1) {\n        // We have more than one component left to sum\n        return compute_contraction_primary<Iteration + 1>(\n                   t, result_component,\n                   get_next_highest_multi_index_to_sum(current_multi_index)) +\n               t.get(current_multi_index);\n      } else {\n        // The deepest term in the contraction subtree that is being summed is\n        // just our current result, so return it\n        return result_component;\n      }\n    } else {\n      // We've haven't yet computed the whole subtree of the term being summed\n      // that is at the lowest depth in the tree\n      if constexpr (Iteration < num_terms_summed - 1) {\n        // We have more than one component left to sum\n        return compute_contraction_primary<Iteration + 1>(\n                   t, result_component,\n                   get_next_highest_multi_index_to_sum(current_multi_index)) +\n               t.get(current_multi_index);\n      } else {\n        // We only have one final component to sum\n        return t.get_primary(result_component, current_multi_index);\n      }\n    }\n  }\n\n  /// \\brief Return the value of the component of the resultant contracted\n  /// tensor at a given multi-index\n  ///\n  /// \\details\n  /// This function differs from `get` in that it takes into account whether we\n  /// have already computed part of the result component at a lower subtree.\n  /// In recursively computing this contraction, the current result component\n  /// will be substituted in for the most recent (highest) subtree below it that\n  /// has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param contracted_multi_index the multi-index of the resultant contracted\n  /// tensor component to retrieve\n  /// \\return the value of the component at `contracted_multi_index` in the\n  /// resultant contracted tensor\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(\n      const ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& contracted_multi_index)\n      const {\n    return compute_contraction_primary<0>(\n        t_, result_component,\n        get_highest_multi_index_to_sum(contracted_multi_index));\n  }\n\n  /// \\brief Successively evaluate the LHS Tensor's result component at each\n  /// leg of summations within the contraction expression\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// contraction, the current result component will be substituted in for the\n  /// most recent (highest) subtree below it that has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param contracted_multi_index the multi-index of the component of the\n  /// contracted result tensor to evaluate\n  /// \\param lowest_multi_index the lowest multi-index between the components\n  /// being summed in the contraction (see `get_lowest_multi_index_to_sum`)\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_contraction(\n      type& result_component,\n      const std::array<size_t, num_tensor_indices>& contracted_multi_index,\n      const std::array<size_t, num_uncontracted_tensor_indices>&\n          lowest_multi_index) const {\n    if constexpr (not is_primary_end) {\n      // We need to first evaluate the subtree of the term being summed that\n      // is deepest in the tree\n      result_component = t_.get_primary(result_component, lowest_multi_index);\n    }\n\n    if constexpr (evaluate_terms_separately) {\n      // Case 1: Evaluate all of the remaining terms, one TERM at a time\n      (void)contracted_multi_index;\n      std::array<size_t, num_uncontracted_tensor_indices> current_multi_index =\n          lowest_multi_index;\n      for (size_t i = 1; i < num_terms_summed; i++) {\n        const std::array<size_t, num_uncontracted_tensor_indices>\n            next_lowest_multi_index_to_sum =\n                get_next_lowest_multi_index_to_sum(current_multi_index);\n        result_component += t_.get(next_lowest_multi_index_to_sum);\n        current_multi_index = next_lowest_multi_index_to_sum;\n      }\n    } else {\n      // Case 2: Evaluate all of the remaining terms, one LEG at a time\n      (void)lowest_multi_index;\n      std::array<size_t, num_uncontracted_tensor_indices>\n          next_leg_starting_multi_index =\n              get_highest_multi_index_to_sum(contracted_multi_index);\n      if constexpr (last_leg_length > 0) {\n        // Case 2a: We have a remainder of terms that don't make up a full leg\n        // length\n\n        // Evaluate all the full-length legs\n        for (size_t i = 0; i < num_full_legs; i++) {\n          const std::array<size_t, num_uncontracted_tensor_indices>\n              current_multi_index = next_leg_starting_multi_index;\n          result_component += compute_contraction_leg<leg_length - 1>(\n              t_, current_multi_index, next_leg_starting_multi_index);\n        }\n        if constexpr (last_leg_length > 1) {\n          // Get rest of the deepest (partial-length) leg if there are more\n          // terms in it than just the one deepest term we already computed\n          const std::array<size_t, num_uncontracted_tensor_indices>\n              current_multi_index = next_leg_starting_multi_index;\n          result_component +=\n              // start at last_leg_length - 2 because we already computed one of\n              // the terms in this deepest leg (the deepest term)\n              compute_contraction_leg<last_leg_length - 2>(\n                  t_, current_multi_index, next_leg_starting_multi_index);\n        }\n      } else {\n        // Case 2b: We don't have remaining terms that only make up a\n        // partial leg length (i.e. we only have full-length legs)\n\n        // Evaluate all but the deepest leg\n        for (size_t i = 1; i < num_full_legs; i++) {\n          const std::array<size_t, num_uncontracted_tensor_indices>\n              current_multi_index = next_leg_starting_multi_index;\n\n          result_component += compute_contraction_leg<leg_length - 1>(\n              t_, current_multi_index, next_leg_starting_multi_index);\n        }\n\n        if constexpr (leg_length > 1) {\n          // Get rest of the deepest leg if there are more terms in it than\n          // just the one deepest term we already computed\n          const std::array<size_t, num_uncontracted_tensor_indices>\n              current_multi_index = next_leg_starting_multi_index;\n          result_component +=\n              // start at leg_length - 2 because we already computed one of the\n              // terms in this deepest leg (the deepest term)\n              compute_contraction_leg<leg_length - 2>(\n                  t_, current_multi_index, next_leg_starting_multi_index);\n        }\n      }\n    }\n  }\n\n  /// \\brief Successively evaluate the LHS Tensor's result component at each\n  /// leg in this expression's subtree\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// contraction, the current result component will be substituted in for the\n  /// most recent (highest) subtree below it that has already been evaluated.\n  ///\n  /// If this contraction expression is the beginning of a leg,\n  /// `evaluate_primary_contraction` is called to evaluate each individual\n  /// leg of summations within the contraction.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param contracted_multi_index the multi-index of the component of the\n  /// contracted result tensor to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_subtree(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& contracted_multi_index)\n      const {\n    const auto lowest_multi_index_to_sum =\n        get_lowest_multi_index_to_sum(contracted_multi_index);\n    if constexpr (primary_child_subtree_contains_primary_start) {\n      // The primary child's subtree contains at least one leg, so recurse down\n      // and evaluate that first. Here, we evaluate the lowest multi-index\n      // because, according to `compute_contraction`, the lowest multi-index is\n      // the one in the last/leaf/final call to `compute_contraction` (i.e. the\n      // multi-index of the final term to sum)\n      t_.evaluate_primary_subtree(result_component, lowest_multi_index_to_sum);\n    }\n    if constexpr (is_primary_start) {\n      // We want to evaluate the subtree for this expression, one leg of\n      // summations at a time\n      evaluate_primary_contraction(result_component, contracted_multi_index,\n                                   lowest_multi_index_to_sum);\n    }\n  }\n\n private:\n  /// Operand expression being contracted\n  T t_;\n};\n\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... TensorIndices>\nSPECTRE_ALWAYS_INLINE static constexpr auto contract(\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<TensorIndices...>>&\n        t) {\n  // Number of indices in the tensor expression we're wanting to contract\n  constexpr size_t num_uncontracted_indices = sizeof...(TensorIndices);\n  // The number of index pairs to contract\n  constexpr size_t num_contracted_index_pairs =\n      detail::get_num_contracted_index_pairs<num_uncontracted_indices>(\n          {{TensorIndices::value...}});\n\n  if constexpr (num_contracted_index_pairs == 0) {\n    // There aren't any indices to contract, so we just return the input\n    return ~t;\n  } else {\n    // We have at least one pair of indices to contract\n    return TensorContract<T, X, Symm, IndexList, tmpl::list<TensorIndices...>,\n                          num_uncontracted_indices -\n                              (num_contracted_index_pairs * 2)>{t};\n  }\n}\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/DataTypeSupport.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines which types are allowed, whether operations with certain types are\n/// allowed, and other type-specific properties and configuration for\n/// `TensorExpression`s\n///\n/// \\details\n/// To add support for a data type, modify the templates in this file and the\n/// arithmetic operator overloads as necessary. Then, add tests as appropriate.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n\nnamespace tenex {\ntemplate <typename DataType>\nstruct NumberAsExpression;\n\nnamespace detail {\n/// @{\n/// \\brief Whether or not `TensorExpression`s supports using a given type as a\n/// numeric term\n///\n/// \\details\n/// To make it possible to use a new numeric data type as a term in\n/// `TensorExpression`s, add the type to this alias and adjust other templates\n/// in this file, as necessary.\n///\n/// \\tparam X the arithmetic data type\ntemplate <typename X>\nstruct is_supported_number_datatype\n    : std::disjunction<std::is_same<X, double>,\n                       std::is_same<X, std::complex<double>>> {};\n\ntemplate <typename X>\nconstexpr bool is_supported_number_datatype_v =\n    is_supported_number_datatype<X>::value;\n/// @}\n\n/// @{\n/// \\brief Whether or not `Tensor`s with the given data type are currently\n/// supported by `TensorExpression`s\n///\n/// \\details\n/// To make it possible to use a new data type in a `Tensor` term in\n/// `TensorExpression`s, add the type to this alias and adjust other templates\n/// in this file, as necessary.\n///\n/// \\tparam X the `Tensor` data type\ntemplate <typename X>\nstruct is_supported_tensor_datatype\n    : std::disjunction<\n          std::is_same<X, double>, std::is_same<X, std::complex<double>>,\n#ifdef SPECTRE_AUTODIFF\n          std::is_same<X, autodiff::HigherOrderDual<2, simd::batch<double>>>,\n          std::is_same<X, autodiff::HigherOrderDual<2, double>>,\n#endif\n          std::is_same<X, DataVector>, std::is_same<X, ComplexDataVector>> {};\n\ntemplate <typename X>\nconstexpr bool is_supported_tensor_datatype_v =\n    is_supported_tensor_datatype<X>::value;\n/// @}\n\n/// \\brief If the given type is a derived `VectorImpl` type, get the base\n/// `VectorImpl` type, else return the given type\n///\n/// \\tparam T the given type\n/// \\tparam IsVector whether or not the given type is a `VectorImpl` type\ntemplate <typename T, bool IsVector = is_derived_of_vector_impl_v<T>>\nstruct upcast_if_derived_vector_type;\n\n/// If `T` is not a `VectorImpl`, the `type` is just the input\ntemplate <typename T>\nstruct upcast_if_derived_vector_type<T, false> {\n  using type = T;\n};\n/// If `T` is a `VectorImpl`, the `type` is the base `VectorImpl` type\ntemplate <typename T>\nstruct upcast_if_derived_vector_type<T, true> {\n  using upcasted_type = typename T::BaseType;\n  // if we have a derived VectorImpl, get base type, else T is a base VectorImpl\n  // and we use that\n  using type =\n      tmpl::conditional_t<std::is_base_of_v<MarkAsVectorImpl, upcasted_type>,\n                          upcasted_type, T>;\n};\n\n/// \\brief Get the complex-valued partner type to a given type\n///\n/// \\details\n/// This is used to define pairings between real-valued types and their\n/// complex-valued counterparts. For example, `double`'s complex-valued partner\n/// is `std::complex<double>` and `DataVector`'s complex-valued partner is\n/// `ComplexDataVector`. Keeping track of this is useful in determining which\n/// operations can and can't be performed in `TensorExpression`s.\n///\n/// To make `TensorExpression`s aware of a new pairing, modify a current\n/// template specialization or add a new one.\n///\n/// \\tparam X the given type\ntemplate <typename X, bool IsArithmetic = std::is_arithmetic_v<X>>\nstruct get_complex_datatype;\n\n/// If the type is not arithmetic, the complex partner to this type is not\n/// known\ntemplate <typename X>\nstruct get_complex_datatype<X, false> {\n  using type = NoSuchType;\n};\n/// If the type is arithmetic, the complex partner to `X` is `std::complex<X>`\ntemplate <typename X>\nstruct get_complex_datatype<X, true> {\n  using type = std::complex<X>;\n};\n/// The complex partner to `DataVector` is `ComplexDataVector`\ntemplate <>\nstruct get_complex_datatype<DataVector> {\n  using type = ComplexDataVector;\n};\n\n/// @{\n/// \\brief Whether or not a given type is the complex-valued partner to another\n/// given type\n///\n/// \\details\n/// See `get_complex_datatype` for which pairings are defined\n///\n/// \\tparam MaybeComplexDataType the given type to check for being the complex\n/// partner to the other type\n/// \\tparam OtherDataType the other type\ntemplate <typename MaybeComplexDataType, typename OtherDataType>\nstruct is_complex_datatype_of\n    : std::is_same<typename get_complex_datatype<OtherDataType>::type,\n                   MaybeComplexDataType> {};\ntemplate <typename OtherDataType>\nstruct is_complex_datatype_of<NoSuchType, OtherDataType> : std::false_type {};\n\ntemplate <typename MaybeComplexDataType, typename OtherDataType>\nconstexpr bool is_complex_datatype_of_v =\n    is_complex_datatype_of<MaybeComplexDataType, OtherDataType>::value;\n/// @}\n\n/// \\brief Whether or not a given type is assignable to another within\n/// `TensorExpression`s\n///\n/// \\details\n/// This is used to define which types can be assigned to which when evaluating\n/// the result of a `TensorExpression`. For example, you can assign a\n/// `DataVector` to a `double`, but not vice versa.\n///\n/// To enable assignment between two types that is not yet supported, modify a\n/// current template specialization or add a new one.\n///\n/// \\tparam LhsDataType the type being assigned\n/// \\tparam RhsDataType the type to assign the `LhsDataType` to\ntemplate <typename LhsDataType, typename RhsDataType>\nstruct is_assignable;\n\n/// Can assign a type to itself\ntemplate <typename LhsDataType, typename RhsDataType>\nstruct is_assignable : std::is_same<LhsDataType, RhsDataType> {};\n/// Can assign a complex numeric type to its underlying real-valued numeric type\ntemplate <typename X>\nstruct is_assignable<std::complex<X>, X> : std::true_type {};\n/// Can assign the LHS `VectorImpl` to the RHS `VectorImpl` if `VectorImpl`\n/// allows it\ntemplate <typename ValueType1, typename VectorType1, size_t StaticSize1,\n          typename ValueType2, typename VectorType2, size_t StaticSize2>\nstruct is_assignable<VectorImpl<ValueType1, VectorType1, StaticSize1>,\n                     VectorImpl<ValueType2, VectorType2, StaticSize2>>\n    : ::VectorImpl_detail::is_assignable<VectorType1, VectorType2> {};\n/// Can assign a `VectorImpl` to its value type, e.g. can assign a `DataVector`\n/// to a `double`\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct is_assignable<VectorImpl<ValueType, VectorType, StaticSize>, ValueType>\n    : std::true_type {};\n/// Can assign a complex-valued `VectorImpl` to its real component's type, e.g.\n/// can assign a `ComplexDataVector` to a `double` because the underlying type\n/// of `ComplexDataVector` is `std::complex<double>`, whose real component is a\n/// `double`\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct is_assignable<\n    VectorImpl<std::complex<ValueType>, VectorType, StaticSize>, ValueType>\n    : std::true_type {};\n\n/// \\brief Whether or not a given type is assignable to another within\n/// `TensorExpression`s\n///\n/// \\details\n/// See `is_assignable` for which assignments are permitted\ntemplate <typename LhsDataType, typename RhsDataType>\nconstexpr bool is_assignable_v = is_assignable<\n    typename upcast_if_derived_vector_type<LhsDataType>::type,\n    typename upcast_if_derived_vector_type<RhsDataType>::type>::value;\n\n/// \\brief Get the data type of a binary operation between two data types\n/// that may occur in a `TensorExpression`\n///\n/// \\details\n/// This is used to define the resulting types of binary arithmetic operations\n/// within `TensorExpression`s, e.g. `double OP double = double` and\n/// `double OP DataVector = DataVector`.\n///\n/// To enable binary operations between two types that is not yet supported,\n/// modify a current template specialization or add a new one.\n///\n/// \\tparam X1 the data type of one operand\n/// \\tparam X2 the data type of the other operand\ntemplate <typename X1, typename X2>\nstruct get_binop_datatype_impl;\n\n/// No template specialization was matched, so it's not a known pairing\ntemplate <typename X1, typename X2>\nstruct get_binop_datatype_impl {\n  using type = NoSuchType;\n};\n/// A binary operation between two terms of the same type will yield a result\n/// with that type\ntemplate <typename X>\nstruct get_binop_datatype_impl<X, X> {\n  using type = X;\n};\n/// A binary operation between two `VectorImpl`s of the same type will\n/// yield the shared derived `VectorImpl`, e.g.\n/// `DataVector OP DataVector = DataVector`\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct get_binop_datatype_impl<VectorImpl<ValueType, VectorType, StaticSize>,\n                               VectorImpl<ValueType, VectorType, StaticSize>> {\n  using type = VectorType;\n};\n/// @{\n/// A binary operation between a type `T` and `std::complex<T>` yields a\n/// `std::complex<T>`\ntemplate <typename T>\nstruct get_binop_datatype_impl<T, std::complex<T>> {\n  using type = std::complex<T>;\n};\ntemplate <typename T>\nstruct get_binop_datatype_impl<std::complex<T>, T> {\n  using type = std::complex<T>;\n};\n/// @}\n/// @{\n/// A binary operation between a `VectorImpl` and its underlying value type\n/// yields the `VectorImpl`, e.g. `DataVector OP double = DataVector`\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct get_binop_datatype_impl<VectorImpl<ValueType, VectorType, StaticSize>,\n                               ValueType> {\n  using type = VectorType;\n};\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct get_binop_datatype_impl<ValueType,\n                               VectorImpl<ValueType, VectorType, StaticSize>> {\n  using type = VectorType;\n};\n/// @}\n/// @{\n/// A binary operation between a complex-valued `VectorImpl` and its real\n/// component's type yields the `VectorImpl`, e.g.\n/// `ComplexDataVector OP double = ComplexDataVector`\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct get_binop_datatype_impl<\n    VectorImpl<std::complex<ValueType>, VectorType, StaticSize>, ValueType> {\n  using type = VectorType;\n};\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct get_binop_datatype_impl<\n    ValueType, VectorImpl<std::complex<ValueType>, VectorType, StaticSize>> {\n  using type = VectorType;\n};\n/// @}\n/// @{\n/// A binary operation between a real-valued `VectorImpl` and the complex-valued\n/// partner to the `VectorImpl`'s underlying type yields the complex partner\n/// type of the `VectorImpl`, e.g.\n/// `std::complex<double> OP DataVector = ComplexDataVector`\n///\n/// \\note Blaze supports multiplication between a `std::complex<double>` and a\n/// `DataVector`, but does not support addition, subtraction, or division\n/// between these two types. This specialization of `get_binop_datatype_impl`\n/// simply defines that the result of any of the binary operations should be\n/// `ComplexDataVector`. Because Blaze doesn't support addition, subtraction,\n/// and division between these two types, the `AddSub` and `Divide` classes\n/// disallow this type combination in their class definitions to prevent these\n/// operations. That way, if Blaze support is later added for e.g. division,\n/// we simply need to remove the assert in `Divide` that prevents it.\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct get_binop_datatype_impl<VectorImpl<ValueType, VectorType, StaticSize>,\n                               std::complex<ValueType>> {\n  using type = typename get_complex_datatype<VectorType>::type;\n};\ntemplate <typename ValueType, typename VectorType, size_t StaticSize>\nstruct get_binop_datatype_impl<std::complex<ValueType>,\n                               VectorImpl<ValueType, VectorType, StaticSize>> {\n  using type = typename get_complex_datatype<VectorType>::type;\n};\n/// @}\n/// @{\n/// A binary operation between a `DataVector` and a `ComplexDataVector` yields a\n/// `ComplexDataVector`\ntemplate <>\nstruct get_binop_datatype_impl<typename ComplexDataVector::BaseType,\n                               typename DataVector::BaseType> {\n  using type = ComplexDataVector;\n};\ntemplate <>\nstruct get_binop_datatype_impl<typename DataVector::BaseType,\n                               typename ComplexDataVector::BaseType> {\n  using type = ComplexDataVector;\n};\n/// @}\n\n/// \\brief Get the data type of a binary operation between two data types\n/// that may occur in a `TensorExpression`\n///\n/// \\details\n/// See `get_binop_datatype_impl` for which data type combinations have a\n/// defined result type\n///\n/// \\tparam X1 the data type of one operand\n/// \\tparam X2 the data type of the other operand\ntemplate <typename X1, typename X2>\nstruct get_binop_datatype {\n  using type = typename get_binop_datatype_impl<\n      typename upcast_if_derived_vector_type<X1>::type,\n      typename upcast_if_derived_vector_type<X2>::type>::type;\n\n  static_assert(\n      not std::is_same_v<type, NoSuchType>,\n      \"You are attempting to perform a binary arithmetic operation between \"\n      \"two data types, but the data type of the result is not known within \"\n      \"TensorExpressions. See tenex::detail::get_binop_datatype_impl.\");\n};\n\n/// @{\n/// \\brief Whether or not it is permitted to perform binary arithmetic\n/// operations with the given types within `TensorExpression`\n///\n/// \\details\n/// See `get_binop_datatype_impl` for which data type combinations have a\n/// defined result type\n///\n/// \\tparam X1 the data type of one operand\n/// \\tparam X2 the data type of the other operand\ntemplate <typename X1, typename X2>\nstruct binop_datatypes_are_supported\n    : std::negation<std::is_same<\n          typename get_binop_datatype_impl<\n              typename upcast_if_derived_vector_type<X1>::type,\n              typename upcast_if_derived_vector_type<X2>::type>::type,\n          NoSuchType>> {};\n\ntemplate <typename X1, typename X2>\nconstexpr bool binop_datatypes_are_supported_v =\n    binop_datatypes_are_supported<X1, X2>::value;\n/// @}\n\n/// \\brief Whether or not it is permitted to perform binary arithmetic\n/// operations with `Tensor`s with the given types within `TensorExpression`s\n///\n/// \\details\n/// This is used to define which data types can be contained by the two\n/// `Tensor`s in a binary operation, e.g.\n/// `Tensor<ComplexDataVector>() OP Tensor<DataVector>()` is permitted, but\n/// `Tensor<DataVector>() OP Tensor<double>()` is not.\n///\n/// To enable binary operations between `Tensor`s with types that are not yet\n/// supported, modify a current template specialization or add a new one.\n///\n/// \\tparam X1 the data type of one `Tensor` operand\n/// \\tparam X2 the data type of the other `Tensor` operand\ntemplate <typename X1, typename X2>\nstruct tensor_binop_datatypes_are_supported_impl;\n\n/// Can only do `Tensor<X1>() OP Tensor<X2>()` if `X1 == X2` or if `X1` and\n/// `X2` are real/complex partners like `DataVector` and `ComplexDataVector`\n/// (see `is_complex_datatype_of`)\ntemplate <typename X1, typename X2>\nstruct tensor_binop_datatypes_are_supported_impl\n    : std::disjunction<std::is_same<X1, X2>, is_complex_datatype_of<X1, X2>,\n                       is_complex_datatype_of<X2, X1>> {};\n\n/// @{\n/// \\brief Whether or not it is permitted to perform binary arithmetic\n/// operations with `Tensor`s with the given types within `TensorExpression`s\n///\n/// \\details\n/// See `tensor_binop_datatypes_are_supported_impl` for which data type\n/// combinations are permitted\n///\n/// \\tparam X1 the data type of one `Tensor` operand\n/// \\tparam X2 the data type of the other `Tensor` operand\ntemplate <typename X1, typename X2>\nstruct tensor_binop_datatypes_are_supported\n    : tensor_binop_datatypes_are_supported_impl<X1, X2> {\n  static_assert(\n      is_supported_tensor_datatype_v<X1> and is_supported_tensor_datatype_v<X2>,\n      \"Cannot perform binary operations between the two Tensors with the \"\n      \"given data types because at least one of the data types is not \"\n      \"supported by TensorExpressions. See \"\n      \"tenex::detail::is_supported_tensor_datatype.\");\n};\n\ntemplate <typename X1, typename X2>\nconstexpr bool tensor_binop_datatypes_are_supported_v =\n    tensor_binop_datatypes_are_supported<X1, X2>::value;\n/// @}\n\n/// \\brief Whether or not it is permitted to perform binary arithmetic\n/// operations with `TensorExpression`s, based on their data types\n///\n/// \\details\n/// This is used to define which data types can be contained by the two\n/// `TensorExpression`s in a binary operation, e.g.\n/// `Tensor<DataVector>() OP double` and\n/// `Tensor<ComplexDataVector>() OP Tensor<DataVector>()` are permitted, but\n/// `Tensor<DataVector>() OP Tensor<double>()` is not. This differs from\n/// `tensor_binop_datatypes_are_supported` in that\n/// `tensorexpression_binop_datatypes_are_supported_impl` handles all derived\n/// `TensorExpression` types, whether they represent `Tensor`s or numbers.\n/// `tensor_binop_datatypes_are_supported` only handles the cases where both\n/// `TensorExpression`s represent `Tensor`s.\n///\n/// To enable binary operations between `TensorExpression`s with types that\n/// are not yet supported, modify a current template specialization or add a\n/// new one.\n///\n/// \\tparam T1 the first `TensorExpression` operand\n/// \\tparam T2 the second `TensorExpression` operand\ntemplate <typename T1, typename T2>\nstruct tensorexpression_binop_datatypes_are_supported_impl;\n\n/// Since `T1` and `T2` represent `Tensor`s, check if we can do\n/// `Tensor<T1::type>() OP Tensor<T2::type>()`\ntemplate <typename T1, typename T2>\nstruct tensorexpression_binop_datatypes_are_supported_impl\n    : tensor_binop_datatypes_are_supported_impl<typename T1::type,\n                                                typename T2::type> {};\n/// @{\n/// Can do `Tensor<X>() OP NUMBER` if we can do `X OP NUMBER`\ntemplate <typename TensorExpressionType, typename NumberType>\nstruct tensorexpression_binop_datatypes_are_supported_impl<\n    TensorExpressionType, NumberAsExpression<NumberType>>\n    : binop_datatypes_are_supported<typename TensorExpressionType::type,\n                                    NumberType> {\n  static_assert(\n      is_supported_tensor_datatype_v<typename TensorExpressionType::type> and\n          is_supported_number_datatype_v<NumberType>,\n      \"Cannot perform binary operations between Tensor and number with the \"\n      \"given data types because at least one of the data types is not \"\n      \"supported by TensorExpressions. See \"\n      \"tenex::detail::is_supported_number_datatype and \"\n      \"tenex::detail::is_supported_tensor_datatype.\");\n};\ntemplate <typename NumberType, typename TensorExpressionType>\nstruct tensorexpression_binop_datatypes_are_supported_impl<\n    NumberAsExpression<NumberType>, TensorExpressionType>\n    : tensorexpression_binop_datatypes_are_supported_impl<\n          TensorExpressionType, NumberAsExpression<NumberType>> {};\n/// @}\n\n/// @{\n/// \\brief Whether or not it is permitted to perform binary arithmetic\n/// operations with `TensorExpression`s, based on their data types\n///\n/// \\details\n/// See `tensorexpression_binop_datatypes_are_supported_impl` for which data\n/// type combinations are permitted\n///\n/// \\tparam T1 the first `TensorExpression` operand\n/// \\tparam T2 the second `TensorExpression` operand\ntemplate <typename T1, typename T2>\nstruct tensorexpression_binop_datatypes_are_supported\n    : tensorexpression_binop_datatypes_are_supported_impl<T1, T2> {\n  static_assert(\n      std::is_base_of_v<Expression, T1> and std::is_base_of_v<Expression, T2>,\n      \"Template arguments to \"\n      \"tenex::detail::tensorexpression_binop_datatypes_are_supported must be \"\n      \"TensorExpressions.\");\n};\n\ntemplate <typename X1, typename X2>\nconstexpr bool tensorexpression_binop_datatypes_are_supported_v =\n    tensorexpression_binop_datatypes_are_supported<X1, X2>::value;\n/// @}\n\n/// \\brief The maximum number of arithmetic tensor operations allowed in a\n/// `TensorExpression` subtree before having it be a splitting point in the\n/// overall RHS expression, according to the data type held by the `Tensor`s in\n/// the expression\n///\n/// \\details\n/// To enable splitting for `TensorExpression`s with data type, define a\n/// template specialization below for your data type and set the `value`.\n///\n/// Before defining a max operations cap for some data type, the change should\n/// first be justified by benchmarking many different tensor expressions before\n/// and after introducing the new cap. The optimal cap will likely be\n/// hardware-dependent, so fine-tuning this would ideally involve benchmarking\n/// on each hardware architecture and then controling the value based on the\n/// hardware.\ntemplate <typename DataType>\nstruct max_num_ops_in_sub_expression_impl {\n  // effectively, no splitting for any unspecialized template type\n  static constexpr size_t value = std::numeric_limits<size_t>::max();\n};\n\n/// \\brief When the data type of the result of a `TensorExpression` is\n/// `DataVector`, the maximum number of arithmetic tensor operations allowed in\n/// a subtree before having it be a splitting point in the overall RHS\n/// expression\n///\n/// \\details\n/// The current value set for when the data type is `DataVector` was benchmarked\n/// by compiling with clang-10 Release and running on Intel(R) Xeon(R)\n/// CPU E5-2630 v4 @ 2.20GHz.\ntemplate <>\nstruct max_num_ops_in_sub_expression_impl<DataVector> {\n  static constexpr size_t value = 8;\n};\n\n/// \\brief When the data type of the result of a `TensorExpression` is\n/// `ComplexDataVector`, the maximum number of arithmetic tensor operations\n/// allowed in a subtree before having it be a splitting point in the overall\n/// RHS expression\n///\n/// \\details\n/// The current value set for when the data type is `ComplexDataVector` is set\n/// to the value for `DataVector`, but the best `value` for `ComplexDataVector`\n/// should also be investigated and fine-tuned.\ntemplate <>\nstruct max_num_ops_in_sub_expression_impl<ComplexDataVector> {\n  static constexpr size_t value =\n      max_num_ops_in_sub_expression_impl<DataVector>::value;\n};\n\n/// \\brief Get maximum number of arithmetic tensor operations allowed in a\n/// `TensorExpression` subtree before having it be a splitting point in the\n/// overall RHS expression, according to the `DataType` held by the `Tensor`s in\n/// the expression\ntemplate <typename DataType>\ninline constexpr size_t max_num_ops_in_sub_expression =\n    max_num_ops_in_sub_expression_impl<DataType>::value;\n}  // namespace detail\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/Divide.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines ET for tensor division by scalars\n\n#pragma once\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/Tensor/Expressions/DataTypeSupport.hpp\"\n#include \"DataStructures/Tensor/Expressions/NumberAsExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Defines the tensor expression representing the quotient of one tensor\n/// expression divided by another tensor expression that evaluates to a rank 0\n/// tensor\n///\n/// \\details\n/// For details on aliases and members defined in this class, as well as general\n/// `TensorExpression` terminology used in its members' documentation, see\n/// documentation for `TensorExpression`.\n///\n/// \\tparam T1 the numerator operand expression of the division expression\n/// \\tparam T2 the denominator operand expression of the division expression\n/// \\tparam Args2 the generic indices of the denominator expression\ntemplate <typename T1, typename T2, typename... Args2>\nstruct Divide\n    : public TensorExpression<Divide<T1, T2, Args2...>,\n                              typename detail::get_binop_datatype<\n                                  typename T1::type, typename T2::type>::type,\n                              typename T1::symmetry, typename T1::index_list,\n                              typename T1::args_list> {\n  static_assert(\n      detail::tensorexpression_binop_datatypes_are_supported_v<T1, T2>,\n      \"Cannot divide the given TensorExpressions with the given data types. \"\n      \"This can occur from e.g. trying to divide a Tensor with data type \"\n      \"double and a Tensor with data type DataVector.\");\n  static_assert(\n      not((std::is_same_v<T1, NumberAsExpression<std::complex<double>>> and\n           std::is_same_v<typename T2::type, DataVector>) or\n          (std::is_same_v<T2, NumberAsExpression<std::complex<double>>> and\n           std::is_same_v<typename T1::type, DataVector>)),\n      \"Cannot perform division between a std::complex<double> and a \"\n      \"TensorExpression whose data type is DataVector because Blaze does not \"\n      \"support division between std::complex<double> and DataVector.\");\n  static_assert((... and tt::is_time_index<Args2>::value),\n                \"Can only divide a tensor expression by a number or a tensor \"\n                \"expression that evaluates to \"\n                \"a rank 0 tensor.\");\n\n  // === Index properties ===\n  /// The type of the data being stored in the result of the expression\n  using type = typename detail::get_binop_datatype<typename T1::type,\n                                                   typename T2::type>::type;\n  /// The ::Symmetry of the result of the expression\n  using symmetry = typename T1::symmetry;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using index_list = typename T1::index_list;\n  /// The list of generic `TensorIndex`s of the result of the expression\n  using args_list = typename T1::args_list;\n  /// The number of tensor indices in the result of the expression\n  static constexpr auto num_tensor_indices =\n      tmpl::size<typename T1::index_list>::value;\n  /// The number of tensor indices in the left operand expression\n  static constexpr auto op2_num_tensor_indices =\n      tmpl::size<typename T2::index_list>::value;\n  /// The multi-index for the denominator\n  static constexpr auto op2_multi_index =\n      make_array<op2_num_tensor_indices, size_t>(0);\n\n  // === Expression subtree properties ===\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// left operand\n  static constexpr size_t num_ops_left_child = T1::num_ops_subtree;\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// right operand\n  static constexpr size_t num_ops_right_child = T2::num_ops_subtree;\n  /// The total number of arithmetic tensor operations done in this expression's\n  /// whole subtree\n  static constexpr size_t num_ops_subtree =\n      num_ops_left_child + num_ops_right_child + 1;\n  /// The height of this expression's node in the expression tree relative to\n  /// the closest `TensorAsExpression` leaf in its subtree\n  static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree =\n      T2::height_relative_to_closest_tensor_leaf_in_subtree <=\n              T1::height_relative_to_closest_tensor_leaf_in_subtree\n          ? (T2::height_relative_to_closest_tensor_leaf_in_subtree !=\n                     std::numeric_limits<size_t>::max()\n                 ? T2::height_relative_to_closest_tensor_leaf_in_subtree + 1\n                 : T2::height_relative_to_closest_tensor_leaf_in_subtree)\n          : T1::height_relative_to_closest_tensor_leaf_in_subtree + 1;\n\n  // === Properties for splitting up subexpressions along the primary path ===\n  // These definitions only have meaning if this expression actually ends up\n  // being along the primary path that is taken when evaluating the whole tree.\n  // See documentation for `TensorExpression` for more details.\n  /// If on the primary path, whether or not the expression is an ending point\n  /// of a leg\n  static constexpr bool is_primary_end = T1::is_primary_start;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the subtree of the child along the\n  /// primary path, given that we will have already computed the whole subtree\n  /// at the next lowest leg's starting point.\n  static constexpr size_t num_ops_to_evaluate_primary_left_child =\n      is_primary_end ? 0 : T1::num_ops_to_evaluate_primary_subtree;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the right operand's subtree. No\n  /// splitting is currently done, so this is just `num_ops_right_child`.\n  static constexpr size_t num_ops_to_evaluate_primary_right_child =\n      num_ops_right_child;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done for this expression's subtree, given that\n  /// we will have already computed the subtree at the next lowest leg's\n  /// starting point\n  static constexpr size_t num_ops_to_evaluate_primary_subtree =\n      num_ops_to_evaluate_primary_left_child +\n      num_ops_to_evaluate_primary_right_child + 1;\n  /// If on the primary path, whether or not the expression is a starting point\n  /// of a leg\n  static constexpr bool is_primary_start =\n      num_ops_to_evaluate_primary_subtree >=\n      detail::max_num_ops_in_sub_expression<type>;\n  /// When evaluating along a primary path, whether each operand's subtrees\n  /// should be evaluated separately. Since `DataVector` expression runtime\n  /// scales poorly with increased number of operations, evaluating the two\n  /// expression subtrees separately like this is beneficial when at least one\n  /// of the subtrees contains a large number of operations.\n  static constexpr bool evaluate_children_separately =\n      is_primary_start and (num_ops_to_evaluate_primary_left_child >=\n                                detail::max_num_ops_in_sub_expression<type> or\n                            num_ops_to_evaluate_primary_right_child >=\n                                detail::max_num_ops_in_sub_expression<type>);\n  /// If on the primary path, whether or not the expression's child along the\n  /// primary path is a subtree that contains a starting point of a leg along\n  /// the primary path\n  static constexpr bool primary_child_subtree_contains_primary_start =\n      T1::primary_subtree_contains_primary_start;\n  /// If on the primary path, whether or not this subtree contains a starting\n  /// point of a leg along the primary path\n  static constexpr bool primary_subtree_contains_primary_start =\n      is_primary_start or primary_child_subtree_contains_primary_start;\n\n  Divide(T1 t1, T2 t2) : t1_(std::move(t1)), t2_(std::move(t2)) {}\n  ~Divide() override = default;\n\n  /// \\brief Assert that the LHS tensor of the equation does not also appear in\n  /// this expression's subtree\n  template <typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensor_not_in_rhs_expression(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T1>) {\n      t1_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T2>) {\n      t2_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n  }\n\n  /// \\brief Assert that each instance of the LHS tensor in the RHS tensor\n  /// expression uses the same generic index order that the LHS uses\n  ///\n  /// \\tparam LhsTensorIndices the list of generic `TensorIndex`s of the LHS\n  /// result `Tensor` being computed\n  /// \\param lhs_tensor the LHS result `Tensor` being computed\n  template <typename LhsTensorIndices, typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensorindices_same_in_rhs(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T1>) {\n      t1_.assert_lhs_tensorindices_same_in_rhs(lhs_tensor);\n    }\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T2>) {\n      t2_.assert_lhs_tensorindices_same_in_rhs(lhs_tensor);\n    }\n  }\n\n  /// \\brief Get the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  ///\n  /// \\return the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  SPECTRE_ALWAYS_INLINE size_t get_rhs_tensor_component_size() const {\n    if constexpr (T1::height_relative_to_closest_tensor_leaf_in_subtree <=\n                  T2::height_relative_to_closest_tensor_leaf_in_subtree) {\n      return t1_.get_rhs_tensor_component_size();\n    } else {\n      return t2_.get_rhs_tensor_component_size();\n    }\n  }\n\n  /// \\brief Return the value of the component of the quotient tensor at a given\n  /// multi-index\n  ///\n  /// \\param result_multi_index the multi-index of the component of the quotient\n  //// tensor to retrieve\n  /// \\return the value of the component in the quotient tensor at\n  /// `result_multi_index`\n  SPECTRE_ALWAYS_INLINE decltype(auto) get(\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    return t1_.get(result_multi_index) / t2_.get(op2_multi_index);\n  }\n\n  /// \\brief Return the value of the component of the quotient tensor at a given\n  /// multi-index\n  ///\n  /// \\details\n  /// This function differs from `get` in that it takes into account whether we\n  /// have already computed part of the result component at a lower subtree.\n  /// In recursively computing this quotient, the current result component will\n  /// be substituted in for the most recent (highest) subtree below it that has\n  /// already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param result_multi_index the multi-index of the component of the quotient\n  //// tensor to retrieve\n  /// \\return the value of the component in the quotient tensor at\n  /// `result_multi_index`\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(\n      const ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    if constexpr (is_primary_end) {\n      (void)result_multi_index;\n      // We've already computed the whole child subtree on the primary path, so\n      // just return the quotient of the current result component and the result\n      // of the other child's subtree\n      return result_component / t2_.get(op2_multi_index);\n    } else {\n      // We haven't yet evaluated the whole subtree for this expression, so\n      // return the quotient of the results of the two operands' subtrees\n      return t1_.get_primary(result_component, result_multi_index) /\n             t2_.get(op2_multi_index);\n    }\n  }\n\n  /// \\brief Evaluate the LHS Tensor's result component at this subtree by\n  /// evaluating the two operand's subtrees separately and dividing\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// quotient, the current result component will be substituted in for the most\n  /// recent (highest) subtree below it that has already been evaluated.\n  ///\n  /// The left and right operands' subtrees are evaluated successively with\n  /// two separate assignments to the LHS result component. Since `DataVector`\n  /// expression runtime scales poorly with increased number of operations,\n  /// evaluating the two expression subtrees separately like this is beneficial\n  /// when at least one of the subtrees contains a large number of operations.\n  /// Instead of evaluating a larger expression with their combined total number\n  /// of operations, we evaluate two smaller ones.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param result_multi_index the multi-index of the component of the result\n  /// tensor to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_children(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    if constexpr (is_primary_end) {\n      (void)result_multi_index;\n      // We've already computed the whole child subtree on the primary path, so\n      // just divide the current result by the result of the other child's\n      // subtree\n      result_component /= t2_.get(op2_multi_index);\n    } else {\n      // We haven't yet evaluated the whole subtree of the primary child, so\n      // first assign the result component to be the result of computing the\n      // primary child's subtree\n      result_component = t1_.get_primary(result_component, result_multi_index);\n      // Now that the primary child's subtree has been computed, divide the\n      // current result by the result of evaluating the other child's subtree\n      result_component /= t2_.get(op2_multi_index);\n    }\n  }\n\n  /// \\brief Successively evaluate the LHS Tensor's result component at each\n  /// leg in this expression's subtree\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// quotient, the current result component will be substituted in for the most\n  /// recent (highest) subtree below it that has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param result_multi_index the multi-index of the component of the result\n  /// tensor to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_subtree(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    if constexpr (primary_child_subtree_contains_primary_start) {\n      // The primary child's subtree contains at least one leg, so recurse down\n      // and evaluate that first\n      t1_.evaluate_primary_subtree(result_component, result_multi_index);\n    }\n    if constexpr (is_primary_start) {\n      // We want to evaluate the subtree for this expression\n      if constexpr (evaluate_children_separately) {\n        // Evaluate operand's subtrees separately\n        evaluate_primary_children(result_component, result_multi_index);\n      } else {\n        // Evaluate whole subtree as one expression\n        result_component = get_primary(result_component, result_multi_index);\n      }\n    }\n  }\n\n private:\n  /// Left operand (numerator)\n  T1 t1_;\n  /// Right operand (denominator)\n  T2 t2_;\n};\n}  // namespace tenex\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the quotient of one tensor\n/// expression over another tensor expression that evaluates to a rank 0 tensor\n///\n/// \\details\n/// `t2` must be an expression that, when evaluated, would be a rank 0 tensor.\n/// For example, if `R` and `S` are Tensors, here is a non-exhaustive list of\n/// some of the acceptable forms that `t2` could take:\n/// - `R()`\n/// - `R(ti::A, ti::a)`\n/// - `(R(ti::A, ti::B) * S(ti::a, ti::b))`\n/// - `R(ti::t, ti::t) + 1.0`\n///\n/// \\param t1 the tensor expression numerator\n/// \\param t2 the rank 0 tensor expression denominator\ntemplate <typename T1, typename T2, typename... Args2>\nSPECTRE_ALWAYS_INLINE auto operator/(\n    const TensorExpression<T1, typename T1::type, typename T1::symmetry,\n                           typename T1::index_list, typename T1::args_list>& t1,\n    const TensorExpression<T2, typename T2::type, typename T2::symmetry,\n                           typename T2::index_list, tmpl::list<Args2...>>& t2) {\n  return tenex::Divide<T1, T2, Args2...>(~t1, ~t2);\n}\n\n/// @{\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the quotient of a tensor\n/// expression over a number\n///\n/// \\note The implementation instead uses the operation, `t * (1.0 / number)`\n///\n/// \\param t the tensor expression operand of the quotient\n/// \\param number the numeric operand of the quotient\n/// \\return the tensor expression representing the quotient of the tensor\n/// expression over the number\ntemplate <typename T, typename N, Requires<std::is_arithmetic_v<N>> = nullptr>\nSPECTRE_ALWAYS_INLINE auto operator/(\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t,\n    const N number) {\n  return t * tenex::NumberAsExpression(1.0 / number);\n}\ntemplate <typename T, typename N>\nSPECTRE_ALWAYS_INLINE auto operator/(\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t,\n    const std::complex<N>& number) {\n  return t * tenex::NumberAsExpression(1.0 / number);\n}\n/// @}\n\n/// @{\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the quotient of a number\n/// over a tensor expression that evaluates to a rank 0 tensor\n///\n/// \\param number the numeric numerator of the quotient\n/// \\param t the tensor expression denominator of the quotient\n/// \\return the tensor expression representing the quotient of the number over\n/// the tensor expression\ntemplate <typename T, typename N, Requires<std::is_arithmetic_v<N>> = nullptr>\nSPECTRE_ALWAYS_INLINE auto operator/(\n    const N number,\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t) {\n  return tenex::NumberAsExpression(number) / t;\n}\ntemplate <typename T, typename N>\nSPECTRE_ALWAYS_INLINE auto operator/(\n    const std::complex<N>& number,\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t) {\n  return tenex::NumberAsExpression(number) / t;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/Evaluate.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions for evaluating `TensorExpression`s\n\n#pragma once\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/DataTypeSupport.hpp\"\n#include \"DataStructures/Tensor/Expressions/IndexPropertyCheck.hpp\"\n#include \"DataStructures/Tensor/Expressions/LhsTensorSymmAndIndices.hpp\"\n#include \"DataStructures/Tensor/Expressions/SpatialSpacetimeIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\nnamespace detail {\ntemplate <size_t NumIndices>\nconstexpr bool contains_indices_to_contract(\n    const std::array<size_t, NumIndices>& tensorindices) {\n  if constexpr (NumIndices < 2) {\n    return false;\n  } else {\n    for (size_t i = 0; i < NumIndices - 1; i++) {\n      for (size_t j = i + 1; j < NumIndices; j++) {\n        const size_t current_tensorindex = gsl::at(tensorindices, i);\n        // Concrete time indices are not contracted\n        if ((not is_time_index_value(current_tensorindex)) and\n            current_tensorindex == get_tensorindex_value_with_opposite_valence(\n                                       gsl::at(tensorindices, j))) {\n          return true;\n        }\n      }\n    }\n    return false;\n  }\n}\n\n/// \\brief Given the list of the positions of the LHS tensor's spacetime indices\n/// where a generic spatial index is used and the list of positions where a\n/// concrete time index is used, determine whether or not the component at the\n/// given LHS multi-index should be computed\n///\n/// \\details\n/// Not all of the LHS tensor's components may need to be computed. Cases when\n/// the component at a LHS multi-index should not be not evaluated:\n/// - If a generic spatial index is used for a spacetime index on the LHS,\n/// the components for which that index's concrete index is the time index\n/// should not be computed\n/// - If a concrete time index is used for a spacetime index on the LHS, the\n/// components for which that index's concrete index is a spatial index should\n/// not be computed\n///\n/// \\param lhs_multi_index the multi-index of the LHS tensor to check\n/// \\param lhs_spatial_spacetime_index_positions the positions of the LHS\n/// tensor's spacetime indices where a generic spatial index is used\n/// \\param lhs_time_index_positions the positions of the LHS tensor's spacetime\n/// indices where a concrete time index is used\n/// \\return Whether or not `lhs_multi_index` is a multi-index of a component of\n/// the LHS tensor that should be computed\ntemplate <size_t NumLhsIndices, size_t NumLhsSpatialSpacetimeIndices,\n          size_t NumLhsConcreteTimeIndices>\nconstexpr bool is_evaluated_lhs_multi_index(\n    const std::array<size_t, NumLhsIndices>& lhs_multi_index,\n    const std::array<size_t, NumLhsSpatialSpacetimeIndices>&\n        lhs_spatial_spacetime_index_positions,\n    const std::array<size_t, NumLhsConcreteTimeIndices>&\n        lhs_time_index_positions) {\n  for (size_t i = 0; i < lhs_spatial_spacetime_index_positions.size(); i++) {\n    if (gsl::at(lhs_multi_index,\n                gsl::at(lhs_spatial_spacetime_index_positions, i)) == 0) {\n      return false;\n    }\n  }\n  for (size_t i = 0; i < lhs_time_index_positions.size(); i++) {\n    if (gsl::at(lhs_multi_index, gsl::at(lhs_time_index_positions, i)) != 0) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename SymmList>\nstruct CheckNoLhsAntiSymmetries;\n\ntemplate <template <typename...> class SymmList, typename... Symm>\nstruct CheckNoLhsAntiSymmetries<SymmList<Symm...>> {\n  static constexpr bool value = (... and (Symm::value > 0));\n};\n\n/// \\brief Given a tensor and its list of tensor indices, return the\n/// canonicalized order of the tensor indices according to the tensor's symmetry\n///\n/// \\details\n/// The canonical ordering of a `Tensor`'s `TensorIndex`s\n/// (e.g. `ti::a`, `ti::b`, ti::c) is relevant to sets of indices that are\n/// symmetric. Within each set of symmetric indices, the `TensorIndex` used for\n/// each index can be freely reordered. Given a set of symmetric indices, this\n/// function defines the canonical order of the `TensorIndex`s assigned to them\n/// to be such that the lowest index positions take any generic spatial tensor\n/// indices (e.g. `ti::i`, `ti::j`), the next lowest index positions take any\n/// generic spacetime indices (e.g. `ti::a`, `ti::b`), and the highest index\n/// positions take any concrete time indices (e.g. `ti::t`, `ti::T`). We can\n/// imagine the canonical ordering of symmetric indices to look generally like:\n/// `[spatial indices ... | spacetime indices... | time indices...]`.\n///\n/// Within the subsets of spatial, spacetime, and time indices, the\n/// `TensorIndex`s in each will be ordered such that lowercase indices come\n/// before uppercase, where both are ordered alphabetically. Another way of\n/// saying this is that if we had a rank N `Tensor` that was fully symmetric,\n/// its canonical ordering would take the following form:\n///\n/// ```\n/// [ti::i, ti::j, ti::k, ..., ti::I, ti::J, ti::K, ...,  // spatial\n///  ti::a, ti::b, ti::c, ..., ti::A, ti::B, ti::C, ...,  // spacetime\n///  ti::t, ti::t, ti::t, ..., ti::T, ti::T, ti::T, ...]  // time\n/// ```\n///\n/// Here are some examples:\n///\n/// ```\n/// symmetry: <1, 1, 1>\n/// set of `TensorIndex`s: {ti::t, ti::i, ti::a}\n/// canonical ordering: [ti::i, ti::a, ti::t]\n///\n/// symmetry: <1, 1, 1>\n/// set of `TensorIndex`s: {ti::A, ti::a, ti::b}\n/// canonical ordering: [ti::a, ti::b, ti::A]\n/// ```\n///\n/// When a `Tensor` is not fully symmetric, the `TensorIndex` labels for any\n/// indices that do not have symmetry with any other will simply keep their\n/// label because it cannot be swapped:\n///\n/// ```\n/// symmetry: <1, 2, 1>\n/// set of `TensorIndex`s: {ti::a, ti::b, ti::i}\n/// canonical ordering: [ti::i, ti::b, ti::a]\n///\n/// symmetry: <2, 1, 1>\n/// set of `TensorIndex`s: {ti::t, ti::k, ti::j}\n/// canonical ordering: [ti::t, ti::j, ti::k]\n/// ```\n///\n/// If there is more than one set of symmetric indices, each of the subsets are\n/// individually reordered:\n///\n/// ```\n/// symmetry: <1, 2, 2, 1>\n/// set of `TensorIndex`s: {ti::a, ti::b, ti::t, ti::i}\n/// canonical ordering: [ti::i, ti::b, ti::t, ti::a]\n/// ```\n///\n/// The motivation for this specific canonical reordering is to quickly assess\n/// which components to assign to and which ones to skip when generic spatial\n/// and/or concrete time indices are used for symmetric spacetime indices in the\n/// resulting left hand side tensor when using `TensorExpression`s.\n///\n/// Let's take the spacetime metric \\f$g_{ab}\\f$ as our motivating example. This\n/// tensor has symmetric spacetime indices, and let's say we only want to assign\n/// to \\f$g_{ti}\\f$. We want to loop over all 10 independent components of\n/// \\f$g_{ab}\\f$ and skip components outside of \\f$g_{ti}\\f$, e.g. \\f$g_{xy}\\f$\n/// (or \\f$g_{12}\\f$) and \\f$g_{tt}\\f$ (or \\f$g_{00}\\f$). To do so, when we see\n/// a multi-index like `{2, 1}` in our loop, we align `{2, 1}` with `{t, i}` and\n/// ask if the `2` is a valid index for `t` and if the `1` is a valid index for\n/// `i`. `1` is valid for `i`, but `2` is not valid for `t`, so we correctly\n/// skip over `{2, 1}` and don't assign to this component.\n///\n/// However, this simple logic can lead to false positives or negatives when the\n/// indices are symmetric. What if the multi-index we're asking about is\n/// `{0, 1}` (\\f$g_{01}\\f$ or \\f$g_{tx}\\f$)? This logic would correctly\n/// determine that this is one of the components we want to assign to. But what\n/// if the multi-index was `{1, 0}`? The logic would incorrectly say to skip\n/// over and not assign to this multi-index because `1` is not a valid index for\n/// `t` and `0` is not valid for `i`. However, because \\f$g_{ab}\\f$ is\n/// symmetric, both `{0, 1}` and `{1, 0}` should give the same result, but\n/// `{1, 0}` gives us a false negative. Moreover and more generally, assigning\n/// to \\f$g_{ti}\\f$ and \\f$g_{it}\\f$ should yield the same behavior (assign to\n/// the same set of components).\n///\n/// One way to address this would be to check the multi-indices for all\n/// permutations of symmetric index values, e.g. is `{0, 1}` *or* `{1, 0}`\n/// valid? And if so, then evaluate it. However, this adds work at runtime and\n/// the number of permutations to check increases as we increase the number of\n/// symmetric indices.\n///\n/// The canonical reordering done by *this* function solves this problem by\n/// reordering the `TensorIndex`s to align nicely with the canonical multi-index\n/// ordering implemented by\n/// `::Tensor_detail::Structure::get_canonical_tensor_index`.\n/// `get_canonical_tensor_index` takes a storage index (which corresponds to an\n/// independent tensor component) and returns a canonical multi-index. This\n/// canonical multi-index is such that index values for symmetric indices will\n/// be ordered to increase from right to left. For example, for a rank 2\n/// symmetric tensor, `{1, 0}` is the canonical multi-index corresponding to the\n/// dependent multi-indices `{0, 1}` and `{1, 0}`. Therefore, `{1, 0}` would be\n/// the multi-index returned by `get_canonical_tensor_index` that corresponds to\n/// the single independent component. In other words, when we loop over the\n/// independent canonical multi-indices, we are looping over the multi-index\n/// permutations that are in the lower triangle of the N-dimensional matrix\n/// containing all multi-index permutations. The canonical reordering of LHS\n/// `TensorIndex`s for symmetric indices that is done by *this* function is\n/// implemented to match this: by making time indices the rightmost, then\n/// spacetime the next rightmost, and then spatial indices leftmost, we\n/// guarantee that looping over the lower triangle permutations given by\n/// `get_canonical_tensor_index` will not produce false positives or negatives\n/// using the earlier simple logic to check for valid multi-indices.\n///\n/// We can use the spacetime metric as an example to demonstrate this. The\n/// lower triangle multi-indices that are looped over are ordered with index\n/// values increasing right to left, e.g. `{0, 0}`, `{1, 0}`, `{2, 0}`,\n/// `{2, 1}`, etc. If a user wants to  assign to \\f$g_{ti}\\f$, then after this\n/// function internally reorders the LHS indices to \\f$g_{it}\\f$, when we loop\n/// and encounter `{1, 0}`, we correctly get that we should evaluate this\n/// component without having to check its other permutation. Likewise, if a user\n/// wants to assign to \\f$g_{it}\\f$, no reordering is done and we get the same\n/// correct behavior.\n///\n/// This function works in general because:\n/// - any `0`s in the symmetric indices will \"first be dealt\" to any time\n///   `TensorIndex`s in the rightmost index positions and then any spacetime\n///   `TensorIndex`s, where `0` is correctly valid for both, but\n/// - if there are more `0`s than time + spacetime `TensorIndex`s, they will be\n///   \"dealt\" to spatial indices, which is always correctly invalid, and\n/// - if there are more time `TensorIndex`s than `0`s, values `> 0` will be\n///   \"dealt\" to time indices, which is also always correctly invalid.\n///\n/// In this way, we don't ever have to check other permutations of sets of\n/// symmetric index values.\n///\n/// \\tparam LhsTensorIndices the `TensorIndex`s of the `Tensor`, e.g. `ti::a`,\n/// `ti::b`, `ti::c`\n/// \\param canonical_symmetry the canonicalized symmetry values of the tensor\n/// (see `Symmetry` for definition of the canonical ordering of symmetry values)\n/// \\return reordered values of `LhsTensorIndices::value...`\ntemplate <typename... LhsTensorIndices, size_t NumIndices>\nconstexpr std::array<size_t, NumIndices> get_reordered_tensorindex_values(\n    const std::array<std::int32_t, NumIndices>& canonical_symmetry) {\n  constexpr std::array<size_t, NumIndices> lhs_tensorindex_values = {\n      {LhsTensorIndices::value...}};\n  if constexpr (NumIndices < 2) {\n    return lhs_tensorindex_values;\n  } else {\n    const auto compare = [](const size_t tensorindex_value1,\n                            const size_t tensorindex_value2) {\n      // clang-tidy thinks these two branches are the same but they aren't:\n      //   if (tensorindex_value2 == ti::T.value)\n      //   if (tensorindex_value2 == ti::t.value)\n      // NOLINTNEXTLINE (clang-tidy: bugprone-branch-clone)\n      if (tensorindex_value2 == ti::T.value) {\n        return false;\n      } else if (is_time_index_value(tensorindex_value1)) {\n        return true;\n      } else if (tensorindex_value2 == ti::t.value) {\n        return false;\n      }\n\n      return (is_generic_spacetime_index_value(tensorindex_value1) and\n              is_generic_spatial_index_value(tensorindex_value2)) or\n             (tensorindex_value1 > tensorindex_value2 and\n              is_generic_spacetime_index_value(tensorindex_value1) ==\n                  is_generic_spacetime_index_value(tensorindex_value2));\n    };\n\n    std::array<size_t, NumIndices> reordered_lhs_tensorindex_values =\n        lhs_tensorindex_values;\n\n    std::int32_t max_symm_value = *alg::max_element(canonical_symmetry);\n    std::int32_t symm_value_to_find = 1;\n    while (symm_value_to_find <= max_symm_value) {\n      size_t i = NumIndices - 1;\n      while (true) {\n        // skip forward until we get to the position with the value we want\n        while (i > 0 and canonical_symmetry[i] != symm_value_to_find) {\n          i--;\n        }\n        if (i == 0) {\n          break;\n        }\n\n        size_t max_tensorindex_value = reordered_lhs_tensorindex_values[i];\n        size_t max_index = i;\n\n        size_t j = i - 1;\n        // note: because we need to hit 0 and size_t wraps around to max size_t\n        while (j < NumIndices) {\n          const std::int32_t compare_symm_value = canonical_symmetry[j];\n          const size_t compare_tensorindex_value =\n              reordered_lhs_tensorindex_values[j];\n          if (compare_symm_value == symm_value_to_find and\n              compare(compare_tensorindex_value, max_tensorindex_value)) {\n            max_tensorindex_value = compare_tensorindex_value;\n            max_index = j;\n          }\n          j--;\n        }\n        reordered_lhs_tensorindex_values[max_index] =\n            reordered_lhs_tensorindex_values[i];\n        reordered_lhs_tensorindex_values[i] = max_tensorindex_value;\n        i--;\n      }\n      symm_value_to_find++;\n    }\n\n    return reordered_lhs_tensorindex_values;\n  }\n}\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Evaluate subtrees of the RHS expression or the RHS expression as a\n * whole and assign the result to the LHS tensor\n *\n * \\details This is for internal use only and should never be directly called.\n * See `tenex::evaluate` and use it, instead.\n *\n * `EvaluateSubtrees` controls whether we wish to evaluate RHS subtrees or the\n * entire RHS expression as one expression. See`TensorExpression` documentation\n * on equation splitting for more details on what this means.\n *\n * If `EvaluateSubtrees == false`, then it's safe if the LHS tensor is used in\n * the RHS expression, so long as the generic index orders are the same. This\n * means that the callee of this function needs to first verify this is true\n * before calling this function. Under these conditions, this is a safe\n * operation because the implementation modifies each LHS component once and\n * does not revisit and access any LHS components after they've been updated.\n * For example, say we do `tenex::evaluate<ti_a, ti_b>(make_not_null(&L),\n * 5.0 * L(ti_a, ti_b));`. This function will first compute the RHS for some\n * concrete LHS, e.g. \\f$L_{00}\\f$. To compute this, it accesses \\f$L_{00}\\f$\n * in the RHS tree, multiplies it by `5.0`, then updates \\f$L_{00}\\f$ to be the\n * result of this multiplication. Next, it might compute \\f$L_{01}\\f$, where\n * only \\f$L_{01}\\f$ is accessed, and which hasn't yet been modified. Then the\n * next component is computed and updated, and so forth. These steps are\n * performed once for each unique LHS index. Therefore, it is important to note\n * that this kind of operation being safe to perform is\n * implementation-dependent. Specifically, the safety of the operation depends\n * on the order of LHS component access and assignment.\n *\n * \\note `LhsTensorIndices` must be passed by reference because non-type\n * template parameters cannot be class types until C++20.\n *\n * @tparam EvaluateSubtrees whether or not to evaluate subtrees of RHS\n * expression\n * @tparam LhsTensorIndices the `TensorIndex`s of the `Tensor` on the LHS of the\n * tensor expression, e.g. `ti::a`, `ti::b`, `ti::c`\n * @param lhs_tensor pointer to the resultant LHS `Tensor` to fill\n * @param rhs_tensorexpression the RHS TensorExpression to be evaluated\n */\ntemplate <bool EvaluateSubtrees, typename... LhsTensorIndices,\n          typename LhsDataType, typename LhsSymmetry, typename LhsIndexList,\n          typename Derived, typename RhsDataType, typename RhsSymmetry,\n          typename RhsIndexList, typename... RhsTensorIndices,\n          size_t... LhsInts>\nvoid evaluate_impl(\n    const gsl::not_null<Tensor<LhsDataType, LhsSymmetry, LhsIndexList>*>\n        lhs_tensor,\n    const TensorExpression<Derived, RhsDataType, RhsSymmetry, RhsIndexList,\n                           tmpl::list<RhsTensorIndices...>>&\n        rhs_tensorexpression,\n    const std::index_sequence<LhsInts...>& /*lhs_ints*/) {\n  constexpr size_t num_lhs_indices = sizeof...(LhsTensorIndices);\n  constexpr size_t num_rhs_indices = sizeof...(RhsTensorIndices);\n\n  using lhs_tensorindex_list = tmpl::list<LhsTensorIndices...>;\n  using rhs_tensorindex_list = tmpl::list<RhsTensorIndices...>;\n\n  using lhs_tensor_type = typename std::decay_t<decltype(*lhs_tensor)>;\n\n  static_assert(is_supported_tensor_datatype_v<LhsDataType> and\n                    is_supported_tensor_datatype_v<RhsDataType>,\n                \"TensorExpressions currently only support Tensors whose data \"\n                \"type is double, std::complex<double>, DataVector, or \"\n                \"ComplexDataVector. It is possible to add support for other \"\n                \"data types that are supported by Tensor.\");\n  static_assert(\n      is_assignable_v<LhsDataType, RhsDataType>,\n      \"Assignment of the LHS Tensor's data type to the RHS TensorExpression's \"\n      \"data type is not supported. This happens from doing something like e.g. \"\n      \"trying to assign a Tensor<double> to a Tensor<DataVector> or a \"\n      \"Tensor<DataVector> to a Tensor<ComplexDataVector>.\");\n  // `Symmetry` currently prevents this because antisymmetries are not currently\n  // supported for `Tensor`s. This check is repeated here because if\n  // antisymmetries are later supported for `Tensor`, using antisymmetries in\n  // `TensorExpression`s will not automatically work. The implementations of the\n  // derived `TensorExpression` types assume no antisymmetries (assume positive\n  // `Symmetry` values), so support for antisymmetries in `TensorExpression`s\n  // will still need to be implemented.\n  static_assert(CheckNoLhsAntiSymmetries<LhsSymmetry>::value,\n                \"Anti-symmetric Tensors are not currently supported by \"\n                \"TensorExpressions.\");\n  static_assert(\n      tmpl::equal_members<\n          typename remove_time_indices<lhs_tensorindex_list>::type,\n          typename remove_time_indices<rhs_tensorindex_list>::type>::value,\n      \"The generic indices on the LHS of a tensor equation (that is, the \"\n      \"template parameters specified in evaluate<...>) must match the generic \"\n      \"indices of the RHS TensorExpression. This error occurs as a result of a \"\n      \"call like evaluate<ti::a, ti::b>(R(ti::A, ti::b) * S(ti::a, ti::c)), \"\n      \"where the generic indices of the evaluated RHS expression are ti::b and \"\n      \"ti::c, but the generic indices provided for the LHS are ti::a and \"\n      \"ti::b.\");\n  static_assert(\n      tensorindex_list_is_valid<lhs_tensorindex_list>::value,\n      \"Cannot assign a tensor expression to a LHS tensor with a repeated \"\n      \"generic index, e.g. evaluate<ti::a, ti::a>. (Note that the concrete \"\n      \"time indices (ti::T and ti::t) can be repeated.)\");\n  static_assert(\n      not contains_indices_to_contract<num_lhs_indices>(\n          {{LhsTensorIndices::value...}}),\n      \"Cannot assign a tensor expression to a LHS tensor with generic \"\n      \"indices that would be contracted, e.g. evaluate<ti::A, ti::a>.\");\n  // `IndexPropertyCheck` does also check that valence (Up/Lo) of indices that\n  // correspond in the RHS and LHS tensors are equal, but the assertion message\n  // below does not mention this because a mismatch in valence should have been\n  // caught due to the combination of (i) the Tensor::operator() assertion\n  // checking that generic indices' valences match the tensor's indices'\n  // valences and (ii) the above assertion that RHS and LHS generic indices\n  // match\n  static_assert(\n      IndexPropertyCheck<LhsIndexList, RhsIndexList, lhs_tensorindex_list,\n                         rhs_tensorindex_list>::value,\n      \"At least one index of the tensor evaluated from the RHS expression \"\n      \"cannot be evaluated to its corresponding index in the LHS tensor. This \"\n      \"is due to a difference in number of spatial dimensions or Frame type \"\n      \"between the index on the RHS and LHS. \"\n      \"e.g. evaluate<ti::a, ti::b>(L, R(ti::b, ti::a));, where R's first \"\n      \"index has 2 spatial dimensions but L's second index has 3 spatial \"\n      \"dimensions. Check RHS and LHS indices that use the same generic index.\");\n  static_assert(Derived::height_relative_to_closest_tensor_leaf_in_subtree <\n                    std::numeric_limits<size_t>::max(),\n                \"Either no Tensors were found in the RHS TensorExpression or \"\n                \"the depth of the tree exceeded the maximum size_t value (very \"\n                \"unlikely). If there is indeed a Tensor in the RHS expression \"\n                \"and assuming the tree's height is not actually the maximum \"\n                \"size_t value, then there is a flaw in the logic for computing \"\n                \"the derived TensorExpression types' member, \"\n                \"height_relative_to_closest_tensor_leaf_in_subtree.\");\n\n  if constexpr (EvaluateSubtrees) {\n    // Make sure the LHS tensor doesn't also appear in the RHS tensor expression\n    (~rhs_tensorexpression).assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    // If the LHS data type is a vector, size the LHS tensor components if their\n    // size does not match the size from a `Tensor` in the RHS expression\n    if constexpr (is_derived_of_vector_impl_v<LhsDataType>) {\n      const size_t rhs_component_size =\n          (~rhs_tensorexpression).get_rhs_tensor_component_size();\n      if (rhs_component_size != (*lhs_tensor)[0].size()) {\n        for (auto& lhs_component : *lhs_tensor) {\n          lhs_component = LhsDataType(rhs_component_size);\n        }\n      }\n    }\n  }\n\n  constexpr std::array<std::int32_t, num_lhs_indices> lhs_symmetry = {\n      {tmpl::at_c<LhsSymmetry, LhsInts>::value...}};\n  constexpr std::array<size_t, num_lhs_indices> reordered_tensorindex_values =\n      get_reordered_tensorindex_values<LhsTensorIndices...>(lhs_symmetry);\n  using reordered_lhs_tensorindex_list =\n      tmpl::list<TensorIndex<reordered_tensorindex_values[LhsInts]>...>;\n\n  constexpr std::array<size_t, num_rhs_indices> index_transformation =\n      compute_tensorindex_transformation<num_lhs_indices, num_rhs_indices>(\n          reordered_tensorindex_values, {{RhsTensorIndices::value...}});\n\n  // positions of indices in LHS tensor where generic spatial indices are used\n  // for spacetime indices\n  constexpr auto lhs_spatial_spacetime_index_positions =\n      get_spatial_spacetime_index_positions<LhsIndexList,\n                                            reordered_lhs_tensorindex_list>();\n  // positions of indices in RHS tensor where generic spatial indices are used\n  // for spacetime indices\n  constexpr auto rhs_spatial_spacetime_index_positions =\n      get_spatial_spacetime_index_positions<RhsIndexList,\n                                            rhs_tensorindex_list>();\n\n  // positions of indices in LHS tensor where concrete time indices are used\n  constexpr auto lhs_time_index_positions =\n      get_time_index_positions<reordered_lhs_tensorindex_list>();\n\n  using rhs_expression_type =\n      typename std::decay_t<decltype(~rhs_tensorexpression)>;\n\n  for (size_t i = 0; i < lhs_tensor_type::size(); i++) {\n    auto lhs_multi_index =\n        lhs_tensor_type::structure::get_canonical_tensor_index(i);\n    if (is_evaluated_lhs_multi_index(lhs_multi_index,\n                                     lhs_spatial_spacetime_index_positions,\n                                     lhs_time_index_positions)) {\n      for (size_t j = 0; j < lhs_spatial_spacetime_index_positions.size();\n           j++) {\n        gsl::at(lhs_multi_index,\n                gsl::at(lhs_spatial_spacetime_index_positions, j)) -= 1;\n      }\n      auto rhs_multi_index =\n          transform_multi_index(lhs_multi_index, index_transformation);\n      for (size_t j = 0; j < rhs_spatial_spacetime_index_positions.size();\n           j++) {\n        gsl::at(rhs_multi_index,\n                gsl::at(rhs_spatial_spacetime_index_positions, j)) += 1;\n      }\n\n      // The expression will either be evaluated as one whole expression\n      // or it will be split up into subtrees that are evaluated one at a time.\n      // See the section on splitting in the documentation for the\n      // `TensorExpression` class to understand the logic and terminology used\n      // in this control flow below.\n      if constexpr (EvaluateSubtrees) {\n        // the expression is split up, so evaluate subtrees at splits\n        (~rhs_tensorexpression)\n            .evaluate_primary_subtree((*lhs_tensor)[i], rhs_multi_index);\n        if constexpr (not rhs_expression_type::is_primary_start) {\n          // the root expression type is not the starting point of a leg, so it\n          // has not yet been evaluated, so now we evaluate this last leg of the\n          // expression at the root of the tree\n          (*lhs_tensor)[i] =\n              (~rhs_tensorexpression)\n                  .get_primary((*lhs_tensor)[i], rhs_multi_index);\n        }\n      } else {\n        // the expression is not split up, so evaluate full expression\n        (*lhs_tensor)[i] = (~rhs_tensorexpression).get(rhs_multi_index);\n      }\n    }\n  }\n}\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Assign a value to components of the LHS tensor\n *\n * \\details This is for internal use only and should never be directly called.\n * See `tenex::evaluate` and use it, instead.\n *\n * \\note `LhsTensorIndices` must be passed by reference because non-type\n * template parameters cannot be class types until C++20.\n *\n * @tparam LhsTensorIndices the `TensorIndex`s of the `Tensor` on the LHS of the\n * tensor expression, e.g. `ti::a`, `ti::b`, `ti::c`\n * @param lhs_tensor pointer to the resultant LHS `Tensor` to fill\n * @param rhs_value the RHS value to assigned\n */\ntemplate <typename... LhsTensorIndices, typename X, typename LhsSymmetry,\n          typename LhsIndexList, typename NumberType, size_t... LhsInts>\nvoid evaluate_impl(\n    const gsl::not_null<Tensor<X, LhsSymmetry, LhsIndexList>*> lhs_tensor,\n    const NumberType& rhs_value,\n    const std::index_sequence<LhsInts...>& /*lhs_ints*/) {\n  using lhs_tensor_type = typename std::decay_t<decltype(*lhs_tensor)>;\n  constexpr size_t num_lhs_indices = sizeof...(LhsTensorIndices);\n  using lhs_tensorindex_list = tmpl::list<LhsTensorIndices...>;\n\n  static_assert(is_supported_tensor_datatype_v<X> and\n                \"TensorExpressions currently only support Tensors whose data \"\n                \"type is double, std::complex<double>, DataVector, or \"\n                \"ComplexDataVector. It is possible to add support for other \"\n                \"data types that are supported by Tensor.\");\n  static_assert(\n      is_assignable_v<X, NumberType>,\n      \"Assignment of the LHS Tensor's data type to the RHS number's data type \"\n      \"is not supported within TensorExpressions. This happens from doing \"\n      \"something like e.g. trying to assign a double to a DataVector or a \"\n      \"DataVector to a ComplexDataVector.\");\n  // `Symmetry` currently prevents this because antisymmetries are not currently\n  // supported for `Tensor`s. This check is repeated here because if\n  // antisymmetries are later supported for `Tensor`, using antisymmetries in\n  // `TensorExpression`s will not automatically work. The implementations of the\n  // derived `TensorExpression` types assume no antisymmetries (assume positive\n  // `Symmetry` values), so support for antisymmetries in `TensorExpression`s\n  // will still need to be implemented.\n  static_assert(CheckNoLhsAntiSymmetries<LhsSymmetry>::value,\n                \"Anti-symmetric Tensors are not currently supported by \"\n                \"TensorExpressions.\");\n  static_assert(\n      tensorindex_list_is_valid<lhs_tensorindex_list>::value,\n      \"Cannot assign a tensor expression to a LHS tensor with a repeated \"\n      \"generic index, e.g. evaluate<ti::a, ti::a>. (Note that the concrete \"\n      \"time indices (ti::T and ti::t) can be repeated.)\");\n  static_assert(\n      not contains_indices_to_contract<num_lhs_indices>(\n          {{LhsTensorIndices::value...}}),\n      \"Cannot assign a tensor expression to a LHS tensor with generic \"\n      \"indices that would be contracted, e.g. evaluate<ti::A, ti::a>.\");\n\n  if constexpr (is_derived_of_vector_impl_v<X>) {\n    ASSERT(get_size((*lhs_tensor)[0]) > 0,\n           \"Tensors with vector components must be sized before calling \"\n           \"\\ntenex::evaluate<...>(\"\n           \"\\n\\tgsl::not_null<Tensor<VectorType, ...>*>, number).\");\n  }\n\n  constexpr std::array<std::int32_t, num_lhs_indices> lhs_symmetry = {\n      {tmpl::at_c<LhsSymmetry, LhsInts>::value...}};\n  constexpr std::array<size_t, num_lhs_indices> reordered_tensorindex_values =\n      get_reordered_tensorindex_values<LhsTensorIndices...>(lhs_symmetry);\n  (void)reordered_tensorindex_values;  // silence false unused variable warning\n  using reordered_lhs_tensorindex_list =\n      tmpl::list<TensorIndex<reordered_tensorindex_values[LhsInts]>...>;\n\n  // positions of indices in LHS tensor where generic spatial indices are used\n  // for spacetime indices\n  constexpr auto lhs_spatial_spacetime_index_positions =\n      get_spatial_spacetime_index_positions<LhsIndexList,\n                                            reordered_lhs_tensorindex_list>();\n\n  // positions of indices in LHS tensor where concrete time indices are used\n  constexpr auto lhs_time_index_positions =\n      get_time_index_positions<reordered_lhs_tensorindex_list>();\n\n  for (size_t i = 0; i < lhs_tensor_type::size(); i++) {\n    auto lhs_multi_index =\n        lhs_tensor_type::structure::get_canonical_tensor_index(i);\n    if (is_evaluated_lhs_multi_index(lhs_multi_index,\n                                     lhs_spatial_spacetime_index_positions,\n                                     lhs_time_index_positions)) {\n      (*lhs_tensor)[i] = rhs_value;\n    }\n  }\n}\n}  // namespace detail\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Assign the result of a RHS tensor expression to a tensor with the LHS\n * index order set in the template parameters\n *\n * \\details Uses the right hand side (RHS) TensorExpression's index ordering\n * (`RhsTE::args_list`) and the desired left hand side (LHS) tensor's index\n * ordering (`LhsTensorIndices`) to fill the provided LHS Tensor with that LHS\n * index ordering. This can carry out the evaluation of a RHS tensor expression\n * to a LHS tensor with the same index ordering, such as \\f$L_{ab} = R_{ab}\\f$,\n * or different ordering, such as \\f$L_{ba} = R_{ab}\\f$.\n *\n * The symmetry of the provided LHS Tensor need not match the symmetry\n * determined from evaluating the RHS TensorExpression according to its order of\n * operations. This allows one to specify LHS symmetries (via `lhs_tensor`) that\n * may not be preserved by the RHS expression's order of operations, which\n * depends on how the expression is written and implemented.\n *\n * The LHS `Tensor` cannot be part of the RHS expression, e.g.\n * `evaluate(make_not_null(&L), L() + R());`, because the LHS `Tensor` will\n * generally not be computed correctly when the RHS `TensorExpression` is split\n * up and the LHS tensor components are computed by accumulating the result of\n * subtrees (see the section on splitting in the documentation for the\n * `TensorExpression` class). If you need to use the LHS `Tensor` on the RHS,\n * use `tenex::update` instead.\n *\n * ### Example usage\n * Given `Tensor`s `R`, `S`, `T`, `G`, and `H`, we can compute the LHS tensor\n * \\f$L\\f$ in the equation \\f$L_{a} = R_{ab} S^{b} + G_{a} - H_{ba}{}^{b} T\\f$\n * by doing:\n *\n * \\snippet Test_MixedOperations.cpp use_evaluate_with_result_as_arg\n *\n * \\note `LhsTensorIndices` must be passed by reference because non-type\n * template parameters cannot be class types until C++20.\n *\n * @tparam LhsTensorIndices the `TensorIndex`s of the `Tensor` on the LHS of the\n * tensor expression, e.g. `ti::a`, `ti::b`, `ti::c`\n * @param lhs_tensor pointer to the resultant LHS `Tensor` to fill\n * @param rhs_tensorexpression the RHS TensorExpression to be evaluated\n */\ntemplate <auto&... LhsTensorIndices, typename LhsDataType, typename LhsSymmetry,\n          typename LhsIndexList, typename Derived, typename RhsDataType,\n          typename RhsSymmetry, typename RhsIndexList,\n          typename... RhsTensorIndices>\nvoid evaluate(\n    const gsl::not_null<Tensor<LhsDataType, LhsSymmetry, LhsIndexList>*>\n        lhs_tensor,\n    const TensorExpression<Derived, RhsDataType, RhsSymmetry, RhsIndexList,\n                           tmpl::list<RhsTensorIndices...>>&\n        rhs_tensorexpression) {\n  using rhs_expression_type =\n      typename std::decay_t<decltype(~rhs_tensorexpression)>;\n  constexpr bool evaluate_subtrees =\n      rhs_expression_type::primary_subtree_contains_primary_start;\n  detail::evaluate_impl<evaluate_subtrees,\n                        std::decay_t<decltype(LhsTensorIndices)>...>(\n      lhs_tensor, rhs_tensorexpression,\n      std::make_index_sequence<sizeof...(LhsTensorIndices)>{});\n}\n\n/// @{\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Assign a number to components of a tensor with the LHS index order\n * set in the template parameters\n *\n * \\details\n * Example usage:\n * \\snippet Test_MixedOperations.cpp assign_double_to_index_subsets\n *\n * \\note The components of the LHS `Tensor` passed in must already be sized\n * because there is no way to infer component size from the RHS\n *\n * \\note `LhsTensorIndices` must be passed by reference because non-type\n * template parameters cannot be class types until C++20.\n *\n * @tparam LhsTensorIndices the `TensorIndex`s of the `Tensor` on the LHS of the\n * tensor expression, e.g. `ti::a`, `ti::b`, `ti::c`\n * @param lhs_tensor pointer to the resultant LHS `Tensor` to fill\n * @param rhs_value the RHS value to assign\n */\ntemplate <auto&... LhsTensorIndices, typename X, typename LhsSymmetry,\n          typename LhsIndexList, typename N,\n          Requires<std::is_arithmetic_v<N>> = nullptr>\nvoid evaluate(\n    const gsl::not_null<Tensor<X, LhsSymmetry, LhsIndexList>*> lhs_tensor,\n    const N rhs_value) {\n  detail::evaluate_impl<std::decay_t<decltype(LhsTensorIndices)>...>(\n      lhs_tensor, rhs_value,\n      std::make_index_sequence<sizeof...(LhsTensorIndices)>{});\n}\ntemplate <auto&... LhsTensorIndices, typename X, typename LhsSymmetry,\n          typename LhsIndexList, typename N>\nvoid evaluate(\n    const gsl::not_null<Tensor<X, LhsSymmetry, LhsIndexList>*> lhs_tensor,\n    const std::complex<N>& rhs_value) {\n  detail::evaluate_impl<std::decay_t<decltype(LhsTensorIndices)>...>(\n      lhs_tensor, rhs_value,\n      std::make_index_sequence<sizeof...(LhsTensorIndices)>{});\n}\n/// @}\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Assign the result of a RHS tensor expression to a tensor with the LHS\n * index order set in the template parameters\n *\n * \\details Uses the right hand side (RHS) TensorExpression's index ordering\n * (`RhsTE::args_list`) and the desired left hand side (LHS) tensor's index\n * ordering (`LhsTensorIndices`) to construct a LHS Tensor with that LHS index\n * ordering. This can carry out the evaluation of a RHS tensor expression to a\n * LHS tensor with the same index ordering, such as \\f$L_{ab} = R_{ab}\\f$, or\n * different ordering, such as \\f$L_{ba} = R_{ab}\\f$.\n *\n * The symmetry of the returned LHS Tensor depends on the order of operations in\n * the RHS TensorExpression, i.e. how the expression is written. If you would\n * like to specify the symmetry of the LHS Tensor instead of it being determined\n * by the order of operations in the RHS expression, please use the other\n * `tenex::evaluate` overload that takes an empty LHS Tensor as its first\n * argument.\n *\n * ### Example usage\n * Given `Tensor`s `R`, `S`, `T`, `G`, and `H`, we can compute the LHS tensor\n * \\f$L\\f$ in the equation \\f$L_{a} = R_{ab} S^{b} + G_{a} - H_{ba}{}^{b} T\\f$\n * by doing:\n *\n * \\snippet Test_MixedOperations.cpp use_evaluate_to_return_result\n *\n * \\parblock\n * \\note If a generic spatial index is used for a spacetime index in the RHS\n * tensor, its corresponding index in the LHS tensor type will be a spatial\n * index with the same valence, frame, and number of spatial dimensions. If a\n * concrete time index is used for a spacetime index in the RHS tensor, the\n * index will not appear in the LHS tensor (i.e. there will NOT be a\n * corresponding LHS index where only the time index of that index has been\n * computed and its spatial indices are empty).\n * \\endparblock\n *\n * \\parblock\n * \\note `LhsTensorIndices` must be passed by reference because non-type\n * template parameters cannot be class types until C++20.\n * \\endparblock\n *\n * @tparam LhsTensorIndices the TensorIndexs of the Tensor on the LHS of the\n * tensor expression, e.g. `ti::a`, `ti::b`, `ti::c`\n * @param rhs_tensorexpression the RHS TensorExpression to be evaluated\n * @return the resultant LHS Tensor with index order specified by\n * LhsTensorIndices\n */\ntemplate <auto&... LhsTensorIndices, typename RhsTE,\n          Requires<std::is_base_of_v<Expression, RhsTE>> = nullptr>\nauto evaluate(const RhsTE& rhs_tensorexpression) {\n  using lhs_tensorindex_list =\n      tmpl::list<std::decay_t<decltype(LhsTensorIndices)>...>;\n  using rhs_tensorindex_list = typename RhsTE::args_list;\n  using rhs_symmetry = typename RhsTE::symmetry;\n  using rhs_tensorindextype_list = typename RhsTE::index_list;\n\n  // Stores (potentially reordered) symmetry and indices needed for constructing\n  // the LHS tensor, with index order specified by LhsTensorIndices\n  using lhs_tensor_symm_and_indices =\n      LhsTensorSymmAndIndices<rhs_tensorindex_list, lhs_tensorindex_list,\n                              rhs_symmetry, rhs_tensorindextype_list>;\n\n  Tensor<typename RhsTE::type, typename lhs_tensor_symm_and_indices::symmetry,\n         typename lhs_tensor_symm_and_indices::tensorindextype_list>\n      lhs_tensor{};\n\n  evaluate<LhsTensorIndices...>(make_not_null(&lhs_tensor),\n                                rhs_tensorexpression);\n  return lhs_tensor;\n}\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief If the LHS tensor is used in the RHS expression, this should be used\n * to assign a LHS tensor to the result of a RHS tensor expression that contains\n * it\n *\n * \\details See documentation for `tenex::evaluate` for basic functionality.\n *\n * `tenex::update` differs from `tenex::evaluate` in that `tenex::update` should\n * be used when some LHS `Tensor` has been partially computed, and now we would\n * like to update it with a RHS expression that contains it. In other words,\n * this should be used when we would like to emulate assignment operations like\n * `LHS +=`, `LHS -=`, `LHS *=`, etc.\n *\n * One important difference to note with `tenex::update` is that it cannot split\n * up the RHS expression and evaluate subtrees, while `tenex::evaluate` can (see\n * `TensorExpression` documentation). From benchmarking, it was found that the\n * runtime of `DataVector` expressions scales poorly as we increase the number\n * of operations. For this reason, when the data type held by the tensors in the\n * expression is `DataVector`, it's best to avoid passing RHS expressions with a\n * large number of operations (e.g. an inner product that sums over many terms).\n *\n * ### Example usage\n * In implementing a large equation with many operations, we can manually break\n * up the equation and evaluate different subexpressions at a time by making one\n * initial call to `tenex::evaluate` followed by any number of calls to\n * `tenex::update` that use the LHS tensor in the RHS expression and will\n * compute the rest of the equation:\n *\n * \\snippet Test_MixedOperations.cpp use_update\n *\n * \\note `LhsTensorIndices` must be passed by reference because non-type\n * template parameters cannot be class types until C++20.\n *\n * @tparam LhsTensorIndices the TensorIndexs of the Tensor on the LHS of the\n * tensor expression, e.g. `ti_a`, `ti_b`, `ti_c`\n * @param lhs_tensor pointer to the resultant LHS Tensor to fill\n * @param rhs_tensorexpression the RHS TensorExpression to be evaluated\n */\ntemplate <auto&... LhsTensorIndices, typename LhsDataType, typename RhsDataType,\n          typename LhsSymmetry, typename LhsIndexList, typename Derived,\n          typename RhsSymmetry, typename RhsIndexList,\n          typename... RhsTensorIndices>\nvoid update(\n    const gsl::not_null<Tensor<LhsDataType, LhsSymmetry, LhsIndexList>*>\n        lhs_tensor,\n    const TensorExpression<Derived, RhsDataType, RhsSymmetry, RhsIndexList,\n                           tmpl::list<RhsTensorIndices...>>&\n        rhs_tensorexpression) {\n  using lhs_tensorindex_list =\n      tmpl::list<std::decay_t<decltype(LhsTensorIndices)>...>;\n  // Assert that each instance of the LHS tensor in the RHS tensor expression\n  // uses the same generic index order that the LHS uses\n  (~rhs_tensorexpression)\n      .template assert_lhs_tensorindices_same_in_rhs<lhs_tensorindex_list>(\n          lhs_tensor);\n\n  detail::evaluate_impl<false, std::decay_t<decltype(LhsTensorIndices)>...>(\n      lhs_tensor, rhs_tensorexpression,\n      std::make_index_sequence<sizeof...(LhsTensorIndices)>{});\n}\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/IndexPropertyCheck.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\nnamespace detail {\n/// @{\n/// \\brief Helper struct for checking that one tensor's index is either a\n/// spacetime index where a concrete time index has been used or can be\n/// assigned to, added to, or subtracted from its corresponding index in another\n/// tensor\n///\n/// \\details\n/// Indices in one tensor correspond to those in another that use the same\n/// generic index, such as `ti::a`. For it to be possible to add, subtract, or\n/// assign one index to another, this checks that the following is true for the\n/// index and its corresponding index in another tensor:\n/// - has the same valence (`UpLo`)\n/// - has the same `Frame` type\n/// - has the same number of spatial dimensions (allowing for expressions that\n///   use generic spatial indices for spacetime indices on either side)\n///\n/// \\tparam IndexList1 the first tensor's \\ref SpacetimeIndex \"TensorIndexType\"\n/// list\n/// \\tparam IndexList2 the second tensor's \\ref SpacetimeIndex \"TensorIndexType\"\n/// list\n/// \\tparam TensorIndexList1 the first tensor's generic index list\n/// \\tparam TensorIndexList2 the second tensor's generic index list\n/// \\tparam CurrentTensorIndex1 the current generic index of the first tensor\n/// that is being checked, e.g. the type of `ti::a`\n/// \\tparam Iteration the position of the current index of the first tensor\n/// being checked, e.g. the position of `ti::a` in the first tensor\ntemplate <typename IndexList1, typename IndexList2, typename TensorIndexList1,\n          typename TensorIndexList2, typename CurrentTensorIndex1,\n          typename Iteration>\nstruct IndexPropertyCheckImpl {\n  using index1 = tmpl::at<IndexList1, Iteration>;\n  using index2 =\n      tmpl::at<IndexList2,\n               tmpl::index_of<TensorIndexList2, CurrentTensorIndex1>>;\n\n  using type = std::bool_constant<\n      index1::ul == index2::ul and\n      std::is_same_v<typename index1::Frame, typename index2::Frame> and\n      ((index1::index_type == index2::index_type and\n        index1::dim == index2::dim) or\n       (index1::index_type == IndexType::Spacetime and\n        index1::dim == index2::dim + 1) or\n       (index2::index_type == IndexType::Spacetime and\n        index1::dim + 1 == index2::dim))>;\n};\n\ntemplate <typename IndexList1, typename IndexList2, typename TensorIndexList1,\n          typename TensorIndexList2, typename Iteration>\nstruct IndexPropertyCheckImpl<IndexList1, IndexList2, TensorIndexList1,\n                              TensorIndexList2, std::decay_t<decltype(ti::T)>,\n                              Iteration> {\n  using index1 = tmpl::at<IndexList1, Iteration>;\n  using type = std::bool_constant<index1::index_type == IndexType::Spacetime>;\n};\n\ntemplate <typename IndexList1, typename IndexList2, typename TensorIndexList1,\n          typename TensorIndexList2, typename Iteration>\nstruct IndexPropertyCheckImpl<IndexList1, IndexList2, TensorIndexList1,\n                              TensorIndexList2, std::decay_t<decltype(ti::t)>,\n                              Iteration> {\n  using index1 = tmpl::at<IndexList1, Iteration>;\n  using type = std::bool_constant<index1::index_type == IndexType::Spacetime>;\n};\n/// @}\n\n/// \\brief Helper struct for checking that one tensor's indices can be\n/// mathematically assigned to, added to, or subtracted from their\n/// corresponding indices in another tensor\n///\n/// \\details\n/// This struct checks that:\n/// (1) The shared generic indices between the two index lists can be\n/// mathematically assigned to, added to, or subtracted from one another\n/// (2) Any non-shared indices are spacetime indices where a concrete time index\n/// has been used\n///\n/// This struct checks that (2) is true for the second tensor's time indices and\n/// calls `IndexPropertyCheckImpl` to check that (2) is true for the first\n/// tensor's time indices and that (1) is true. To see more details regarding\n/// how (1) is checked, see `IndexPropertyCheckImpl`.\n///\n/// \\tparam IndexList1 the first tensor's \\ref SpacetimeIndex \"TensorIndexType\"\n/// list\n/// \\tparam IndexList2 the second tensor's \\ref SpacetimeIndex \"TensorIndexType\"\n/// list\n/// \\tparam TensorIndexList1 the first tensor's generic index list\n/// \\tparam TensorIndexList2 the second tensor's generic index list\ntemplate <typename IndexList1, typename IndexList2, typename TensorIndexList1,\n          typename TensorIndexList2>\nstruct IndexPropertyCheckHelper;\n\ntemplate <typename IndexList1, typename... RhsIndices,\n          typename TensorIndexList1, typename... RhsTensorIndices>\nstruct IndexPropertyCheckHelper<IndexList1, tmpl::list<RhsIndices...>,\n                                TensorIndexList1,\n                                tmpl::list<RhsTensorIndices...>> {\n  static constexpr bool value =\n      // Check that second tensor's concrete time indices are used with\n      // spacetime indices\n      (... and ((not tt::is_time_index<RhsTensorIndices>::value) or\n                (tt::is_time_index<RhsTensorIndices>::value and\n                 RhsIndices::index_type == IndexType::Spacetime))) and\n      // Check that:\n      // - the first tensor's concrete time indices are used with spacetime\n      // indices\n      // - shared generic indices between the two tensors can be mathematically\n      // assigned to, added to, or subtracted from one another\n      (tmpl::enumerated_fold<\n          TensorIndexList1, tmpl::bool_<true>,\n          tmpl::and_<\n              tmpl::_state,\n              IndexPropertyCheckImpl<tmpl::pin<IndexList1>,\n                                     tmpl::pin<tmpl::list<RhsIndices...>>,\n                                     tmpl::pin<TensorIndexList1>,\n                                     tmpl::pin<tmpl::list<RhsTensorIndices...>>,\n                                     tmpl::_element, tmpl::_3>>,\n          tmpl::size_t<0>>::value);\n};\n\n/// \\brief Check that one tensor's indices can be mathematically assigned to,\n/// added to, or subtracted from their corresponding indices in another tensor\n///\n/// \\details\n/// For more details on what is checked, see `IndexPropertyCheckImpl` followed\n/// by `IndexPropertyCheckHelper`\n///\n/// \\tparam IndexList1 the first tensor's \\ref SpacetimeIndex \"TensorIndexType\"\n/// list\n/// \\tparam IndexList2 the second tensor's \\ref SpacetimeIndex \"TensorIndexType\"\n/// list\n/// \\tparam TensorIndexList1 the first tensor's generic index list\n/// \\tparam TensorIndexList2 the second tensor's generic index list\ntemplate <typename IndexList1, typename IndexList2, typename TensorIndexList1,\n          typename TensorIndexList2>\nusing IndexPropertyCheck =\n    IndexPropertyCheckHelper<IndexList1, IndexList2, TensorIndexList1,\n                             TensorIndexList2>;\n}  // namespace detail\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/LhsTensorSymmAndIndices.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/Tensor/Expressions/SpatialSpacetimeIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Determines and stores a LHS tensor's symmetry and index list from a\n * RHS tensor expression and desired LHS index order\n *\n * \\details Given the generic index order of a RHS TensorExpression and the\n * generic index order of the desired LHS Tensor, this creates a mapping between\n * the two that is then used to determine the (potentially reordered) ordering\n * of the elements of the desired LHS Tensor`s ::Symmetry, typelist of\n * \\ref SpacetimeIndex \"TensorIndexType\"s, and Tensor_detail::Structure. This\n * struct is used to determine the LHS Tensor's properties when they are not\n * supplied by the user: when a user uses the `evaluate` overload that returns\n * the LHS as opposed to the overload that takes an empty LHS Tensor as an\n * argument.\n *\n * Note: If a generic spatial index is used for a spacetime index in the RHS\n * tensor, its corresponding index in the LHS tensor type will be a spatial\n * index with the same valence, frame, and number of spatial dimensions. If a\n * concrete time index is used for a spacetime index in the RHS tensor, the\n * index will not appear in the LHS tensor (i.e. there will NOT be a\n * corresponding LHS index where only the time index of that index has been\n * computed and its spatial indices are empty). Therefore, the\n * `LhsTensorIndexList` may not contain time indices (`ti::t` nor `ti::T`).\n *\n * @tparam RhsTensorIndexList the typelist of TensorIndex of the RHS\n * TensorExpression\n * @tparam LhsTensorIndexList the typelist of TensorIndexs of the desired LHS\n * tensor\n * @tparam RhsSymmetry the ::Symmetry of the RHS indices\n * @tparam RhsTensorIndexTypeList the RHS TensorExpression's typelist of\n * \\ref SpacetimeIndex \"TensorIndexType\"s\n */\ntemplate <typename RhsTensorIndexList, typename LhsTensorIndexList,\n          typename RhsSymmetry, typename RhsTensorIndexTypeList,\n          size_t NumLhsIndices = tmpl::size<LhsTensorIndexList>::value,\n          size_t NumRhsIndices = tmpl::size<RhsTensorIndexList>::value,\n          typename LhsIndexSequence = std::make_index_sequence<NumLhsIndices>>\nstruct LhsTensorSymmAndIndices;\n\ntemplate <typename... RhsTensorIndices, typename... LhsTensorIndices,\n          std::int32_t... RhsSymm, typename RhsTensorIndexTypeList,\n          size_t NumLhsIndices, size_t NumRhsIndices, size_t... LhsInts>\nstruct LhsTensorSymmAndIndices<\n    tmpl::list<RhsTensorIndices...>, tmpl::list<LhsTensorIndices...>,\n    tmpl::integral_list<std::int32_t, RhsSymm...>, RhsTensorIndexTypeList,\n    NumLhsIndices, NumRhsIndices, std::index_sequence<LhsInts...>> {\n  static_assert((... and (not tt::is_time_index<LhsTensorIndices>::value)),\n                \"LHS generic indices cannot contain the concrete time index.\");\n  // LHS generic indices, RHS generic indices, and the mapping between them\n  static constexpr std::array<size_t, NumLhsIndices> lhs_tensorindex_values = {\n      {LhsTensorIndices::value...}};\n  static constexpr std::array<size_t, NumRhsIndices> rhs_tensorindex_values = {\n      {RhsTensorIndices::value...}};\n  static constexpr std::array<size_t, NumLhsIndices> rhs_to_lhs_map =\n      compute_tensorindex_transformation(rhs_tensorindex_values,\n                                         lhs_tensorindex_values);\n\n  // Compute symmetry of RHS after spacetime indices using generic spatial\n  // indices are swapped for spatial indices\n  static constexpr std::array<std::int32_t, NumRhsIndices> rhs_symmetry = {\n      {RhsSymm...}};\n  using rhs_spatial_spacetime_index_positions_ =\n      detail::spatial_spacetime_index_positions<\n          RhsTensorIndexTypeList, tmpl::list<RhsTensorIndices...>>;\n  using make_list_type = std::conditional_t<\n      tmpl::size<rhs_spatial_spacetime_index_positions_>::value == 0, size_t,\n      rhs_spatial_spacetime_index_positions_>;\n  static constexpr auto rhs_spatial_spacetime_index_positions =\n      make_array_from_list<make_list_type>();\n  static constexpr std::array<std::int32_t, NumRhsIndices>\n      rhs_spatial_spacetime_index_symmetry =\n          detail::get_spatial_spacetime_index_symmetry(\n              rhs_symmetry, rhs_spatial_spacetime_index_positions);\n\n  // Compute index list of RHS after spacetime indices using generic spatial\n  // indices are made nonsymmetric to other indices\n  using rhs_spatial_spacetime_tensorindextype_list =\n      detail::replace_spatial_spacetime_indices<\n          RhsTensorIndexTypeList, rhs_spatial_spacetime_index_positions_>;\n\n  // Desired LHS Tensor's Symmetry, typelist of TensorIndexTypes, and Structure\n  using symmetry = Symmetry<\n      rhs_spatial_spacetime_index_symmetry[rhs_to_lhs_map[LhsInts]]...>;\n  using tensorindextype_list =\n      tmpl::list<tmpl::at_c<rhs_spatial_spacetime_tensorindextype_list,\n                            rhs_to_lhs_map[LhsInts]>...>;\n  using structure =\n      Tensor_detail::Structure<symmetry,\n                               tmpl::at_c<tensorindextype_list, LhsInts>...>;\n};\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/Negate.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the tensor expression representing negation\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/Tensor/Expressions/NumberAsExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Defines the tensor expression representing the negation of a tensor\n/// expression\n///\n/// \\details\n/// For details on aliases and members defined in this class, as well as general\n/// `TensorExpression` terminology used in its members' documentation, see\n/// documentation for `TensorExpression`.\n///\n/// \\tparam T the type of tensor expression being negated\ntemplate <typename T>\nstruct Negate\n    : public TensorExpression<Negate<T>, typename T::type, typename T::symmetry,\n                              typename T::index_list, typename T::args_list> {\n  // === Index properties ===\n  /// The type of the data being stored in the result of the expression\n  using type = typename T::type;\n  /// The ::Symmetry of the result of the expression\n  using symmetry = typename T::symmetry;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using index_list = typename T::index_list;\n  /// The list of generic `TensorIndex`s of the result of the expression\n  using args_list = typename T::args_list;\n  /// The number of tensor indices in the result of the expression\n  static constexpr auto num_tensor_indices = tmpl::size<index_list>::value;\n\n  // === Expression subtree properties ===\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// left operand\n  static constexpr size_t num_ops_left_child = T::num_ops_subtree;\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// right operand. This is 0 because this expression represents a unary\n  /// operation.\n  static constexpr size_t num_ops_right_child = 0;\n  /// The total number of arithmetic tensor operations done in this expression's\n  /// whole subtree\n  static constexpr size_t num_ops_subtree = num_ops_left_child + 1;\n  /// The height of this expression's node in the expression tree relative to\n  /// the closest `TensorAsExpression` leaf in its subtree\n  static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree =\n      T::height_relative_to_closest_tensor_leaf_in_subtree !=\n              std::numeric_limits<size_t>::max()\n          ? T::height_relative_to_closest_tensor_leaf_in_subtree + 1\n          : T::height_relative_to_closest_tensor_leaf_in_subtree;\n\n  // === Properties for splitting up subexpressions along the primary path ===\n  // These definitions only have meaning if this expression actually ends up\n  // being along the primary path that is taken when evaluating the whole tree.\n  // See documentation for `TensorExpression` for more details.\n  /// If on the primary path, whether or not the expression is an ending point\n  /// of a leg\n  static constexpr bool is_primary_end = T::is_primary_start;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the subtree of the child along the\n  /// primary path, given that we will have already computed the whole subtree\n  /// at the next lowest leg's starting point.\n  static constexpr size_t num_ops_to_evaluate_primary_left_child =\n      is_primary_end ? 0 : T::num_ops_to_evaluate_primary_subtree;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the right operand's subtree. No\n  /// splitting is currently done, so this is just `num_ops_right_child`.\n  static constexpr size_t num_ops_to_evaluate_primary_right_child =\n      num_ops_right_child;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done for this expression's subtree, given that\n  /// we will have already computed the subtree at the next lowest leg's\n  /// starting point\n  static constexpr size_t num_ops_to_evaluate_primary_subtree =\n      num_ops_to_evaluate_primary_left_child +\n      num_ops_to_evaluate_primary_right_child + 1;\n  /// If on the primary path, whether or not the expression is a starting point\n  /// of a leg\n  static constexpr bool is_primary_start =\n      num_ops_to_evaluate_primary_subtree >=\n      detail::max_num_ops_in_sub_expression<type>;\n  /// If on the primary path, whether or not the expression's child along the\n  /// primary path is a subtree that contains a starting point of a leg along\n  /// the primary path\n  static constexpr bool primary_child_subtree_contains_primary_start =\n      T::primary_subtree_contains_primary_start;\n  /// If on the primary path, whether or not this subtree contains a starting\n  /// point of a leg along the primary path\n  static constexpr bool primary_subtree_contains_primary_start =\n      is_primary_start or primary_child_subtree_contains_primary_start;\n\n  Negate(T t) : t_(std::move(t)) {}\n  ~Negate() override = default;\n\n  /// \\brief Assert that the LHS tensor of the equation does not also appear in\n  /// this expression's subtree\n  template <typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensor_not_in_rhs_expression(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T>) {\n      t_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n  }\n\n  /// \\brief Assert that each instance of the LHS tensor in the RHS tensor\n  /// expression uses the same generic index order that the LHS uses\n  ///\n  /// \\tparam LhsTensorIndices the list of generic `TensorIndex`s of the LHS\n  /// result `Tensor` being computed\n  /// \\param lhs_tensor the LHS result `Tensor` being computed\n  template <typename LhsTensorIndices, typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensorindices_same_in_rhs(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T>) {\n      t_.template assert_lhs_tensorindices_same_in_rhs<LhsTensorIndices>(\n          lhs_tensor);\n    }\n  }\n\n  /// \\brief Get the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  ///\n  /// \\return the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  SPECTRE_ALWAYS_INLINE size_t get_rhs_tensor_component_size() const {\n    return t_.get_rhs_tensor_component_size();\n  }\n\n  /// \\brief Return the value of the component of the negated tensor expression\n  /// at a given multi-index\n  ///\n  /// \\param multi_index the multi-index of the component to retrieve from the\n  /// negated tensor expression\n  /// \\return the value of the component at `multi_index` in the negated tensor\n  /// expression\n  SPECTRE_ALWAYS_INLINE decltype(auto) get(\n      const std::array<size_t, num_tensor_indices>& multi_index) const {\n    return -t_.get(multi_index);\n  }\n\n  /// \\brief Return the value of the component of the negated tensor expression\n  /// at a given multi-index\n  ///\n  /// \\details\n  /// This function differs from `get` in that it takes into account whether we\n  /// have already computed part of the result component at a lower subtree.\n  /// In recursively computing this negation, the current result component will\n  /// be substituted in for the most recent (highest) subtree below it that has\n  /// already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param multi_index the multi-index of the component to retrieve from the\n  /// negated tensor expression\n  /// \\return the value of the component at `multi_index` in the negated tensor\n  /// expression\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(\n      const ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& multi_index) const {\n    if constexpr (is_primary_end) {\n      (void)multi_index;\n      // We've already computed the whole child subtree on the primary path, so\n      // just return the negation of the current result component\n      return -result_component;\n    } else {\n      // We haven't yet evaluated the whole subtree for this expression, so\n      // return the negation of this expression's subtree\n      return -t_.get_primary(result_component, multi_index);\n    }\n  }\n\n  /// \\brief Successively evaluate the LHS Tensor's result component at each\n  /// leg in this expression's subtree\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// negation, the current result component will be substituted in for the most\n  /// recent (highest) subtree below it that has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param multi_index the multi-index of the component of the result tensor\n  /// to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_subtree(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& multi_index) const {\n    if constexpr (primary_child_subtree_contains_primary_start) {\n      // The primary child's subtree contains at least one leg, so recurse down\n      // and evaluate that first\n      t_.evaluate_primary_subtree(result_component, multi_index);\n    }\n    if constexpr (is_primary_start) {\n      // We want to evaluate the subtree for this expression\n      result_component = get_primary(result_component, multi_index);\n    }\n  }\n\n private:\n  /// Operand expression\n  T t_;\n};\n}  // namespace tenex\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the negation of a tensor\n/// expression\n///\n/// \\param t the tensor expression\n/// \\return the tensor expression representing the negation of `t`\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE auto operator-(\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t) {\n  return tenex::Negate<T>(~t);\n}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/NumberAsExpression.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/Expressions/DataTypeSupport.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Marks a class as being a `NumberAsExpression<DataType>`\n///\n/// \\details\n/// The empty base class provides a simple means for checking if a type is a\n/// `NumberAsExpression<DataType>`.\nstruct MarkAsNumberAsExpression {};\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Defines an expression representing a number\n///\n/// \\details\n/// For details on aliases and members defined in this class, as well as general\n/// `TensorExpression` terminology used in its members' documentation, see\n/// documentation for `TensorExpression`.\ntemplate <typename DataType>\nstruct NumberAsExpression\n    : public TensorExpression<NumberAsExpression<DataType>, DataType,\n                              tmpl::list<>, tmpl::list<>, tmpl::list<>>,\n      MarkAsNumberAsExpression {\n  static_assert(detail::is_supported_number_datatype_v<DataType>,\n                \"TensorExpressions currently only support numeric terms whose \"\n                \"type is double or std::complex<double>. It is possible to add \"\n                \"support for more numeric types.\");\n\n  // === Index properties ===\n  /// The type of the data being stored in the result of the expression\n  using type = DataType;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using symmetry = tmpl::list<>;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using index_list = tmpl::list<>;\n  /// The list of generic `TensorIndex`s of the result of the expression\n  using args_list = tmpl::list<>;\n  /// The number of tensor indices in the result of the expression\n  static constexpr auto num_tensor_indices = 0;\n\n  // === Expression subtree properties ===\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// left operand, which is 0 because this is a leaf expression\n  static constexpr size_t num_ops_left_child = 0;\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// right operand, which is 0 because this is a leaf expression\n  static constexpr size_t num_ops_right_child = 0;\n  /// The total number of arithmetic tensor operations done in this expression's\n  /// whole subtree, which is 0 because this is a leaf expression\n  static constexpr size_t num_ops_subtree = 0;\n  /// The height of this expression's node in the expression tree relative to\n  /// the closest `TensorAsExpression` leaf in its subtree. Because this\n  /// expression type is leaf, the height for this type is set to the maximum\n  /// `size_t` value to encode a sense of maximal height.\n  static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree =\n      std::numeric_limits<size_t>::max();\n\n  // === Properties for splitting up subexpressions along the primary path ===\n  // These definitions only have meaning if this expression actually ends up\n  // being along the primary path that is taken when evaluating the whole tree.\n  // See documentation for `TensorExpression` for more details.\n  /// If on the primary path, whether or not the expression is an ending point\n  /// of a leg\n  static constexpr bool is_primary_end = true;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the subtree of the child along the\n  /// primary path, given that we will have already computed the whole subtree\n  /// at the next lowest leg's starting point. This is just 0 because this\n  /// expression is a leaf.\n  static constexpr size_t num_ops_to_evaluate_primary_left_child = 0;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the right operand's subtree. This is\n  /// just 0 because this expression is a leaf.\n  static constexpr size_t num_ops_to_evaluate_primary_right_child = 0;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done for this expression's subtree, given that\n  /// we will have already computed the subtree at the next lowest leg's\n  /// starting point. This is just 0 because this expression is a leaf.\n  static constexpr size_t num_ops_to_evaluate_primary_subtree = 0;\n  /// If on the primary path, whether or not the expression is a starting point\n  /// of a leg\n  static constexpr bool is_primary_start = false;\n  /// If on the primary path, whether or not the expression's child along the\n  /// primary path is a subtree that contains a starting point of a leg along\n  /// the primary path. This is always falls because this expression is a leaf.\n  static constexpr bool primary_child_subtree_contains_primary_start = false;\n  /// If on the primary path, whether or not this subtree contains a starting\n  /// point of a leg along the primary path\n  static constexpr bool primary_subtree_contains_primary_start =\n      is_primary_start;\n\n  NumberAsExpression(const type& number) : number_(number) {}\n  ~NumberAsExpression() override = default;\n\n  // This expression does not represent a tensor, nor does it have any children,\n  // so we should never need to assert that the LHS `Tensor` is not equal to the\n  // number stored by this expression\n  template <typename LhsTensor>\n  void assert_lhs_tensor_not_in_rhs_expression(\n      const gsl::not_null<LhsTensor*>) const = delete;\n  // This expression does not represent a tensor, nor does it have any children,\n  // so we should never need to assert that instances of the LHS Tensor in this\n  // expression's subtree have the same generic index order\n  template <typename LhsTensorIndices, typename LhsTensor>\n  void assert_lhs_tensorindices_same_in_rhs(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const = delete;\n  // This expression is a non-`Tensor` leaf, so we should never try to get the\n  // size of a `Tensor` component from this expression.\n  size_t get_rhs_tensor_component_size() const = delete;\n\n  /// \\brief Returns the number represented by the expression\n  ///\n  /// \\return the number represented by this expression\n  SPECTRE_ALWAYS_INLINE type\n  get(const std::array<size_t, num_tensor_indices>& /*multi_index*/) const {\n    return number_;\n  }\n\n  /// \\brief Returns the number represented by the expression\n  ///\n  /// \\return the number represented by this expression\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE type get_primary(\n      const ResultType& /*result_component*/,\n      const std::array<size_t, num_tensor_indices>& /*multi_index*/) const {\n    return number_;\n  }\n\n  // This expression is a leaf and therefore will never be the start of a leg\n  // to evaluate in a split tree, which is enforced by\n  // `is_primary_start == false`. Therefore, this function should never be\n  // called on this expression type.\n  template <typename ResultType>\n  void evaluate_primary_subtree(\n      ResultType&,\n      const std::array<size_t, num_tensor_indices>&) const = delete;\n\n private:\n  /// Number represented by this expression\n  type number_;\n};\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/Product.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines ET for tensor inner and outer products\n\n#pragma once\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/Tensor/Expressions/Contract.hpp\"\n#include \"DataStructures/Tensor/Expressions/DataTypeSupport.hpp\"\n#include \"DataStructures/Tensor/Expressions/NumberAsExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\nnamespace detail {\ntemplate <typename T1, typename T2, typename SymmList1 = typename T1::symmetry,\n          typename SymmList2 = typename T2::symmetry>\nstruct OuterProductType;\n\ntemplate <typename T1, typename T2, template <typename...> class SymmList1,\n          typename... Symm1, template <typename...> class SymmList2,\n          typename... Symm2>\nstruct OuterProductType<T1, T2, SymmList1<Symm1...>, SymmList2<Symm2...>> {\n  using type =\n      typename get_binop_datatype<typename T1::type, typename T2::type>::type;\n  using symmetry =\n      Symmetry<(Symm1::value + sizeof...(Symm2))..., Symm2::value...>;\n  using index_list =\n      tmpl::append<typename T1::index_list, typename T2::index_list>;\n  using tensorindex_list =\n      tmpl::append<typename T1::args_list, typename T2::args_list>;\n};\n}  // namespace detail\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Defines the tensor expression representing the outer product of two\n/// tensor expressions\n///\n/// \\details\n/// For details on aliases and members defined in this class, as well as general\n/// `TensorExpression` terminology used in its members' documentation, see\n/// documentation for `TensorExpression`.\n///\n/// \\tparam T1 the left operand expression of the outer product expression\n/// \\tparam T2 the right operand expression of the outer product expression\ntemplate <typename T1, typename T2,\n          typename IndexList1 = typename T1::index_list,\n          typename IndexList2 = typename T2::index_list,\n          typename ArgsList1 = typename T1::args_list,\n          typename ArgsList2 = typename T2::args_list>\nstruct OuterProduct;\n\ntemplate <typename T1, typename T2, template <typename...> class IndexList1,\n          typename... Indices1, template <typename...> class IndexList2,\n          typename... Indices2, template <typename...> class ArgsList1,\n          typename... Args1, template <typename...> class ArgsList2,\n          typename... Args2>\nstruct OuterProduct<T1, T2, IndexList1<Indices1...>, IndexList2<Indices2...>,\n                    ArgsList1<Args1...>, ArgsList2<Args2...>>\n    : public TensorExpression<\n          OuterProduct<T1, T2>, typename detail::OuterProductType<T1, T2>::type,\n          typename detail::OuterProductType<T1, T2>::symmetry,\n          typename detail::OuterProductType<T1, T2>::index_list,\n          typename detail::OuterProductType<T1, T2>::tensorindex_list> {\n  static_assert(\n      detail::tensorexpression_binop_datatypes_are_supported_v<T1, T2>,\n      \"Cannot multiply the given TensorExpressions with the given data types. \"\n      \"This can occur from e.g. trying to multiply a Tensor with data type \"\n      \"double and a Tensor with data type DataVector.\");\n  // === Index properties ===\n  /// The type of the data being stored in the result of the expression\n  using type = typename detail::OuterProductType<T1, T2>::type;\n  /// The ::Symmetry of the result of the expression\n  using symmetry = typename detail::OuterProductType<T1, T2>::symmetry;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using index_list = typename detail::OuterProductType<T1, T2>::index_list;\n  /// The list of generic `TensorIndex`s of the result of the\n  /// expression\n  using args_list = typename detail::OuterProductType<T1, T2>::tensorindex_list;\n  /// The number of tensor indices in the result of the expression\n  static constexpr auto num_tensor_indices = tmpl::size<index_list>::value;\n  /// The number of tensor indices in the left operand expression\n  static constexpr auto op1_num_tensor_indices =\n      tmpl::size<typename T1::index_list>::value;\n  /// The number of tensor indices in the right operand expression\n  static constexpr auto op2_num_tensor_indices =\n      num_tensor_indices - op1_num_tensor_indices;\n\n  // === Expression subtree properties ===\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// left operand\n  static constexpr size_t num_ops_left_child = T1::num_ops_subtree;\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// right operand\n  static constexpr size_t num_ops_right_child = T2::num_ops_subtree;\n  // This helps ensure we are minimizing breadth in the overall tree\n  static_assert(num_ops_left_child >= num_ops_right_child,\n                \"The left operand should be a subtree with equal or more \"\n                \"tensor operations than the right operand's subtree.\");\n  /// The total number of arithmetic tensor operations done in this expression's\n  /// whole subtree\n  static constexpr size_t num_ops_subtree =\n      num_ops_left_child + num_ops_right_child + 1;\n  /// The height of this expression's node in the expression tree relative to\n  /// the closest `TensorAsExpression` leaf in its subtree\n  static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree =\n      T2::height_relative_to_closest_tensor_leaf_in_subtree <=\n              T1::height_relative_to_closest_tensor_leaf_in_subtree\n          ? (T2::height_relative_to_closest_tensor_leaf_in_subtree !=\n                     std::numeric_limits<size_t>::max()\n                 ? T2::height_relative_to_closest_tensor_leaf_in_subtree + 1\n                 : T2::height_relative_to_closest_tensor_leaf_in_subtree)\n          : T1::height_relative_to_closest_tensor_leaf_in_subtree + 1;\n\n  // === Properties for splitting up subexpressions along the primary path ===\n  // These definitions only have meaning if this expression actually ends up\n  // being along the primary path that is taken when evaluating the whole tree.\n  // See documentation for `TensorExpression` for more details.\n  /// If on the primary path, whether or not the expression is an ending point\n  /// of a leg\n  static constexpr bool is_primary_end = T1::is_primary_start;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the subtree of the child along the\n  /// primary path, given that we will have already computed the whole subtree\n  /// at the next lowest leg's starting point.\n  static constexpr size_t num_ops_to_evaluate_primary_left_child =\n      is_primary_end ? 0 : T1::num_ops_to_evaluate_primary_subtree;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the right operand's subtree. No\n  /// splitting is currently done, so this is just `num_ops_right_child`.\n  static constexpr size_t num_ops_to_evaluate_primary_right_child =\n      num_ops_right_child;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done for this expression's subtree, given that\n  /// we will have already computed the subtree at the next lowest leg's\n  /// starting point\n  static constexpr size_t num_ops_to_evaluate_primary_subtree =\n      num_ops_to_evaluate_primary_left_child +\n      num_ops_to_evaluate_primary_right_child + 1;\n  /// If on the primary path, whether or not the expression is a starting point\n  /// of a leg\n  static constexpr bool is_primary_start =\n      num_ops_to_evaluate_primary_subtree >=\n      detail::max_num_ops_in_sub_expression<type>;\n  /// When evaluating along a primary path, whether each operand's subtrees\n  /// should be evaluated separately. Since `DataVector` expression runtime\n  /// scales poorly with increased number of operations, evaluating the two\n  /// expression subtrees separately like this is beneficial when at least one\n  /// of the subtrees contains a large number of operations.\n  static constexpr bool evaluate_children_separately =\n      is_primary_start and (num_ops_to_evaluate_primary_left_child >=\n                                detail::max_num_ops_in_sub_expression<type> or\n                            num_ops_to_evaluate_primary_right_child >=\n                                detail::max_num_ops_in_sub_expression<type>);\n  /// If on the primary path, whether or not the expression's child along the\n  /// primary path is a subtree that contains a starting point of a leg along\n  /// the primary path\n  static constexpr bool primary_child_subtree_contains_primary_start =\n      T1::primary_subtree_contains_primary_start;\n  /// If on the primary path, whether or not this subtree contains a starting\n  /// point of a leg along the primary path\n  static constexpr bool primary_subtree_contains_primary_start =\n      is_primary_start or primary_child_subtree_contains_primary_start;\n\n  OuterProduct(T1 t1, T2 t2) : t1_(std::move(t1)), t2_(std::move(t2)) {}\n  ~OuterProduct() override = default;\n\n  /// \\brief Assert that the LHS tensor of the equation does not also appear in\n  /// this expression's subtree\n  template <typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensor_not_in_rhs_expression(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T1>) {\n      t1_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T2>) {\n      t2_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n  }\n\n  /// \\brief Assert that each instance of the LHS tensor in the RHS tensor\n  /// expression uses the same generic index order that the LHS uses\n  ///\n  /// \\tparam LhsTensorIndices the list of generic `TensorIndex`s of the LHS\n  /// result `Tensor` being computed\n  /// \\param lhs_tensor the LHS result `Tensor` being computed\n  template <typename LhsTensorIndices, typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensorindices_same_in_rhs(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T1>) {\n      t1_.template assert_lhs_tensorindices_same_in_rhs<LhsTensorIndices>(\n          lhs_tensor);\n    }\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T2>) {\n      t2_.template assert_lhs_tensorindices_same_in_rhs<LhsTensorIndices>(\n          lhs_tensor);\n    }\n  }\n\n  /// \\brief Get the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  ///\n  /// \\return the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  SPECTRE_ALWAYS_INLINE size_t get_rhs_tensor_component_size() const {\n    if constexpr (T1::height_relative_to_closest_tensor_leaf_in_subtree <=\n                  T2::height_relative_to_closest_tensor_leaf_in_subtree) {\n      return t1_.get_rhs_tensor_component_size();\n    } else {\n      return t2_.get_rhs_tensor_component_size();\n    }\n  }\n\n  /// \\brief Return the first operand's multi-index given the outer product's\n  /// multi-index\n  ///\n  /// \\param result_multi_index the multi-index of the component of the outer\n  /// product tensor\n  /// \\return the first operand's multi-index\n  constexpr SPECTRE_ALWAYS_INLINE std::array<size_t, op1_num_tensor_indices>\n  get_op1_multi_index(\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    std::array<size_t, op1_num_tensor_indices> op1_multi_index{};\n    for (size_t i = 0; i < op1_num_tensor_indices; i++) {\n      gsl::at(op1_multi_index, i) = gsl::at(result_multi_index, i);\n    }\n    return op1_multi_index;\n  }\n\n  /// \\brief Return the second operand's multi-index given the outer product's\n  /// multi-index\n  ///\n  /// \\param result_multi_index the multi-index of the component of the outer\n  /// product tensor\n  /// \\return the second operand's multi-index\n  constexpr SPECTRE_ALWAYS_INLINE std::array<size_t, op2_num_tensor_indices>\n  get_op2_multi_index(\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    std::array<size_t, op2_num_tensor_indices> op2_multi_index{};\n    for (size_t i = 0; i < op2_num_tensor_indices; i++) {\n      gsl::at(op2_multi_index, i) =\n          gsl::at(result_multi_index, op1_num_tensor_indices + i);\n    }\n    return op2_multi_index;\n  }\n\n  /// \\brief Return the value of the component of the outer product tensor at a\n  /// given multi-index\n  ///\n  /// \\details\n  /// This function takes the multi-index of some component of the resultant\n  /// outer product to compute. The function first computes the multi-indices of\n  /// the pair of components in the two operand expressions, then multiplies the\n  /// values at these multi-indices to obtain the value of the resultant outer\n  /// product component. For example, say we are evaluating\n  /// \\f$L_abc = R_{b} * S_{ca}\\f$. Let `result_multi_index == {0, 1, 2}`, which\n  /// refers to the component \\f$L_{012}\\f$, the component we wish to compute.\n  /// This function will compute the multi-indices of the operands that\n  /// correspond to \\f$R_{1}\\f$ and \\f$S_{20}\\f$, retrieve their values, and\n  /// return their product.\n  ///\n  /// \\param result_multi_index the multi-index of the component of the outer\n  /// product tensor to retrieve\n  /// \\return the value of the component at `result_multi_index` in the outer\n  /// product tensor\n  SPECTRE_ALWAYS_INLINE decltype(auto) get(\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    return t1_.get(get_op1_multi_index(result_multi_index)) *\n           t2_.get(get_op2_multi_index(result_multi_index));\n  }\n\n  /// \\brief Return the product of the components at the given multi-indices of\n  /// the left and right operands\n  ///\n  /// \\details\n  /// This function differs from `get` in that it takes into account whether we\n  /// have already computed part of the result component at a lower subtree.\n  /// In recursively computing this product, the current result component will\n  /// be substituted in for the most recent (highest) subtree below it that has\n  /// already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param op1_multi_index the multi-index of the component of the first\n  /// operand of the product to retrieve\n  /// \\param op2_multi_index the multi-index of the component of the second\n  /// operand of the product to retrieve\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(\n      const ResultType& result_component,\n      const std::array<size_t, op1_num_tensor_indices>& op1_multi_index,\n      const std::array<size_t, op2_num_tensor_indices>& op2_multi_index) const {\n    if constexpr (is_primary_end) {\n      (void)op1_multi_index;\n      // We've already computed the whole child subtree on the primary path, so\n      // just return the product of the current result component and the result\n      // of the other child's subtree\n      return result_component * t2_.get(op2_multi_index);\n    } else {\n      // We haven't yet evaluated the whole subtree for this expression, so\n      // return the product of the results of the two operands' subtrees\n      return t1_.get_primary(result_component, op1_multi_index) *\n             t2_.get(op2_multi_index);\n    }\n  }\n\n  /// \\brief Return the value of the component of the outer product tensor at a\n  /// given multi-index\n  ///\n  /// \\details\n  /// This function differs from `get` in that it takes into account whether we\n  /// have already computed part of the result component at a lower subtree.\n  /// In recursively computing this product, the current result component will\n  /// be substituted in for the most recent (highest) subtree below it that has\n  /// already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param result_multi_index the multi-index of the component of the outer\n  /// product tensor to retrieve\n  /// \\return the value of the component at `result_multi_index` in the outer\n  /// product tensor\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(\n      const ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    return get_primary(result_component,\n                       get_op1_multi_index(result_multi_index),\n                       get_op2_multi_index(result_multi_index));\n  }\n\n  /// \\brief Evaluate the LHS Tensor's result component at this subtree by\n  /// evaluating the two operand's subtrees separately and multiplying\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// product, the current result component will be substituted in for the most\n  /// recent (highest) subtree below it that has already been evaluated.\n  ///\n  /// The left and right operands' subtrees are evaluated successively with\n  /// two separate assignments to the LHS result component. Since `DataVector`\n  /// expression runtime scales poorly with increased number of operations,\n  /// evaluating the two expression subtrees separately like this is beneficial\n  /// when at least one of the subtrees contains a large number of operations.\n  /// Instead of evaluating a larger expression with their combined total number\n  /// of operations, we evaluate two smaller ones.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param op1_multi_index the multi-index of the component of the first\n  /// operand of the product to evaluate\n  /// \\param op2_multi_index the multi-index of the component of the second\n  /// operand of the product to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_children(\n      ResultType& result_component,\n      const std::array<size_t, op1_num_tensor_indices>& op1_multi_index,\n      const std::array<size_t, op2_num_tensor_indices>& op2_multi_index) const {\n    if constexpr (is_primary_end) {\n      (void)op1_multi_index;\n      // We've already computed the whole child subtree on the primary path, so\n      // just multiply the current result by the result of the other child's\n      // subtree\n      result_component *= t2_.get(op2_multi_index);\n    } else {\n      // We haven't yet evaluated the whole subtree of the primary child, so\n      // first assign the result component to be the result of computing the\n      // primary child's subtree\n      result_component = t1_.get_primary(result_component, op1_multi_index);\n      // Now that the primary child's subtree has been computed, multiply the\n      // current result by the result of evaluating the other child's subtree\n      result_component *= t2_.get(op2_multi_index);\n    }\n  }\n\n  /// \\brief Successively evaluate the LHS Tensor's result component at each\n  /// leg in this expression's subtree\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// product, the current result component will be substituted in for the most\n  /// recent (highest) subtree below it that has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param result_multi_index the multi-index of the component of the outer\n  /// product tensor to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_subtree(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& result_multi_index) const {\n    const std::array<size_t, op1_num_tensor_indices> op1_multi_index =\n        get_op1_multi_index(result_multi_index);\n    if constexpr (primary_child_subtree_contains_primary_start) {\n      // The primary child's subtree contains at least one leg, so recurse down\n      // and evaluate that first\n      t1_.evaluate_primary_subtree(result_component, op1_multi_index);\n    }\n\n    if constexpr (is_primary_start) {\n      // We want to evaluate the subtree for this expression\n      if constexpr (evaluate_children_separately) {\n        // Evaluate operand's subtrees separately\n        evaluate_primary_children(result_component, op1_multi_index,\n                                  get_op2_multi_index(result_multi_index));\n      } else {\n        // Evaluate whole subtree as one expression\n        result_component = get_primary(result_component, op1_multi_index,\n                                       get_op2_multi_index(result_multi_index));\n      }\n    }\n  }\n\n private:\n  /// Left operand expression\n  T1 t1_;\n  /// Right operand expression\n  T2 t2_;\n};\n}  // namespace tenex\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the product of two tensor\n/// expressions\n///\n/// \\details\n/// If the two operands have N pairs of indices that need to be contracted, the\n/// returned expression will be an `OuterProduct` expression nested inside N\n/// `TensorContract` expressions. This represents computing the inner product\n/// of the outer product of the two operands. If the operands do not have any\n/// indices to be contracted, the returned expression will be an `OuterProduct`.\n///\n/// The two arguments are expressions that contain the two operands of the\n/// product, where the types of the operands are `T1` and `T2`.\n///\n/// \\tparam T1 the derived TensorExpression type of the first operand of the\n/// product\n/// \\tparam T2 the derived TensorExpression type of the second operand of the\n/// product\n/// \\tparam ArgsList1 the TensorIndexs of the first operand\n/// \\tparam ArgsList2 the TensorIndexs of the second operand\n/// \\param t1 first operand expression of the product\n/// \\param t2 the second operand expression of the product\n/// \\return the tensor expression representing the product of two tensor\n/// expressions\ntemplate <typename T1, typename T2, typename ArgsList1, typename ArgsList2>\nSPECTRE_ALWAYS_INLINE auto operator*(\n    const TensorExpression<T1, typename T1::type, typename T1::symmetry,\n                           typename T1::index_list, ArgsList1>& t1,\n    const TensorExpression<T2, typename T2::type, typename T2::symmetry,\n                           typename T2::index_list, ArgsList2>& t2) {\n  if constexpr (T1::num_ops_subtree >= T2::num_ops_subtree) {\n    return tenex::contract(tenex::OuterProduct<T1, T2>(~t1, ~t2));\n  } else {\n    return tenex::contract(tenex::OuterProduct<T2, T1>(~t2, ~t1));\n  }\n}\n\n/// @{\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the product of a tensor\n/// expression and a number\n///\n/// \\param t the tensor expression operand of the product\n/// \\param number the numeric operand of the product\n/// \\return the tensor expression representing the product of the tensor\n/// expression and the number\ntemplate <typename T, typename N, Requires<std::is_arithmetic_v<N>> = nullptr>\nSPECTRE_ALWAYS_INLINE auto operator*(\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t,\n    const N number) {\n  return t * tenex::NumberAsExpression(number);\n}\ntemplate <typename T, typename N, Requires<std::is_arithmetic_v<N>> = nullptr>\nSPECTRE_ALWAYS_INLINE auto operator*(\n    const N number,\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t) {\n  return t * tenex::NumberAsExpression(number);\n}\ntemplate <typename T, typename N>\nSPECTRE_ALWAYS_INLINE auto operator*(\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t,\n    const std::complex<N>& number) {\n  return t * tenex::NumberAsExpression(number);\n}\ntemplate <typename T, typename N>\nSPECTRE_ALWAYS_INLINE auto operator*(\n    const std::complex<N>& number,\n    const TensorExpression<T, typename T::type, typename T::symmetry,\n                           typename T::index_list, typename T::args_list>& t) {\n  return t * tenex::NumberAsExpression(number);\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/SpatialSpacetimeIndex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\file\n/// Defines functions and metafunctions used for helping evaluate\n/// TensorExpression equations where generic spatial indices are used for\n/// spacetime indices\n\nnamespace tenex {\nnamespace detail {\n/// \\brief Returns whether or not the provided value is a TensorIndex value\n/// that encodes a generic spatial index\n///\n/// \\param value the value to check\n/// \\return whether or not the value encodes a generic spatial index\nconstexpr bool is_generic_spatial_index_value(const size_t value) {\n  return value >= tenex::TensorIndex_detail::spatial_sentinel;\n}\n\n/// \\brief Returns whether or not the provided value is a TensorIndex value\n/// that encodes a generic spacetime index\n///\n/// \\param value the value to check\n/// \\return whether or not the value encodes a generic spacetime index\nconstexpr bool is_generic_spacetime_index_value(const size_t value) {\n  return value < tenex::TensorIndex_detail::spatial_sentinel and\n         not is_time_index_value(value);\n}\n\ntemplate <typename TensorIndexType, typename TensorIndex>\nconstexpr bool is_spatial_spacetime_index() {\n  return TensorIndexType::index_type == IndexType::Spacetime and\n         not TensorIndex::is_spacetime;\n}\n\ntemplate <typename State, typename Element, typename Iteration,\n          typename TensorIndexList>\nstruct spatial_spacetime_index_positions_impl {\n  using type = typename std::conditional_t<\n      is_spatial_spacetime_index<Element,\n                                 tmpl::at<TensorIndexList, Iteration>>(),\n      tmpl::push_back<State, tmpl::integral_constant<size_t, Iteration::value>>,\n      State>;\n};\n\n/// \\brief Given a generic index list and tensor index list, returns the list of\n/// positions where the generic index is spatial and the tensor index is\n/// spacetime\n///\n/// \\tparam TensorIndexList the generic index list\n/// \\tparam TensorIndexTypeList the list of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\ntemplate <typename TensorIndexTypeList, typename TensorIndexList>\nusing spatial_spacetime_index_positions = tmpl::enumerated_fold<\n    TensorIndexTypeList, tmpl::list<>,\n    spatial_spacetime_index_positions_impl<\n        tmpl::_state, tmpl::_element, tmpl::_3, tmpl::pin<TensorIndexList>>,\n    tmpl::size_t<0>>;\n\n/// \\brief Given a generic index list and tensor index list, returns the list of\n/// positions where the generic index is spatial and the tensor index is\n/// spacetime\n///\n/// \\tparam TensorIndexList the generic index list\n/// \\tparam TensorIndexTypeList the list of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\n/// \\return the list of positions where the generic index is spatial and the\n/// tensor index is spacetime\ntemplate <typename TensorIndexTypeList, typename TensorIndexList>\nconstexpr auto get_spatial_spacetime_index_positions() {\n  using spatial_spacetime_index_positions_ =\n      spatial_spacetime_index_positions<TensorIndexTypeList, TensorIndexList>;\n  using make_list_type = std::conditional_t<\n      tmpl::size<spatial_spacetime_index_positions_>::value == 0, size_t,\n      spatial_spacetime_index_positions_>;\n  return make_array_from_list<make_list_type>();\n}\n\n/// @{\n/// \\brief Given a tensor symmetry and the positions of indices where a generic\n/// spatial index is used for a spacetime index, this returns the symmetry\n/// after making those indices nonsymmetric with others\n///\n/// \\details\n/// Example: If `symmetry` is `[2, 1, 1, 1]` and\n/// `spatial_spacetime_index_positions` is `[1]`, then position 1 is the only\n/// position where a generic spatial index is used for a spacetime index. The\n/// resulting symmetry will make the index at position 1 no longer be symmetric\n/// with the indices at positions 2 and 3. Therefore, the resulting symmetry\n/// will be equivalent to the form of `[3, 2, 1, 1]`.\n///\n/// Note: the symmetry returned by this function is not necessarily in the\n/// canonical form specified by ::Symmetry. In reality, for the example above,\n/// this function would return `[2, 3, 1, 1]`.\n///\n/// \\param symmetry the input tensor symmetry to transform\n/// \\param spatial_spacetime_index_positions the positions of the indices of the\n/// tensor where a generic spatial index is used for a spacetime index\n/// \\return the symmetry after making the `spatial_spacetime_index_positions` of\n/// `symmetry` nonsymmetric with other indices\ntemplate <\n    size_t NumIndices, size_t NumSpatialSpacetimeIndices,\n    Requires<(NumIndices >= 2 and NumSpatialSpacetimeIndices != 0)> = nullptr>\nconstexpr std::array<std::int32_t, NumIndices>\nget_spatial_spacetime_index_symmetry(\n    const std::array<std::int32_t, NumIndices>& symmetry,\n    const std::array<size_t, NumSpatialSpacetimeIndices>&\n        spatial_spacetime_index_positions) {\n  std::array<std::int32_t, NumIndices> spatial_spacetime_index_symmetry{};\n  const std::int32_t max_symm_value =\n      static_cast<std::int32_t>(*alg::max_element(symmetry));\n  for (size_t i = 0; i < NumIndices; i++) {\n    gsl::at(spatial_spacetime_index_symmetry, i) = gsl::at(symmetry, i);\n  }\n  for (size_t i = 0; i < NumSpatialSpacetimeIndices; i++) {\n    gsl::at(spatial_spacetime_index_symmetry,\n            gsl::at(spatial_spacetime_index_positions, i)) += max_symm_value;\n  }\n\n  return spatial_spacetime_index_symmetry;\n}\n\ntemplate <\n    size_t NumIndices, size_t NumSpatialSpacetimeIndices,\n    Requires<(NumIndices < 2 or NumSpatialSpacetimeIndices == 0)> = nullptr>\nconstexpr std::array<std::int32_t, NumIndices>\nget_spatial_spacetime_index_symmetry(\n    const std::array<std::int32_t, NumIndices>& symmetry,\n    const std::array<size_t, NumSpatialSpacetimeIndices>&\n    /*spatial_spacetime_index_positions*/) {\n  return symmetry;\n}\n/// @}\n\ntemplate <typename S, typename E>\nstruct replace_spatial_spacetime_indices_helper {\n  using type = tmpl::replace_at<S, E, change_index_type<tmpl::at<S, E>>>;\n};\n\n// The list of indices resulting from taking `TensorIndexTypeList` and\n// replacing the spacetime indices at positions `SpatialSpacetimeIndexPositions`\n// with spatial indices\ntemplate <typename TensorIndexTypeList, typename SpatialSpacetimeIndexPositions>\nusing replace_spatial_spacetime_indices = tmpl::fold<\n    SpatialSpacetimeIndexPositions, TensorIndexTypeList,\n    replace_spatial_spacetime_indices_helper<tmpl::_state, tmpl::_element>>;\n\n/// \\brief Given a number of tensor indices of two tensors and the positions of\n/// each tensor's spacetime indices for which a generic spatial index was used,\n/// compute the shift in the multi-index values from the first tensor's\n/// multi-indices to the second's\n///\n/// \\details\n/// Example: If we have \\f$R_{ijk} + S_{ijk}\\f$, where  \\f$R\\f$'s first and\n/// 2nd indices are spacetime and \\f$S\\f$' first index and third index are\n/// spacetime, let \\f$i = 0\\f$, \\f$j = 1\\f$, and \\f$k = 2\\f$. The multi-index\n/// that represents  \\f$R_{012}\\f$ is `{0 + 1, 1 + 1, 2} = {1, 2, 2}` and the\n/// multi-index that represents \\f$S_{012}\\f$ is\n/// `{0 + 1, 1, 2 + 1} = {1, 1, 3}`. The function returns the element-wise\n/// shift that is applied to convert the first multi-index to the other, which,\n/// in this case, would be: `{1, 1, 3} - {1, 2, 2} = {0, -1, 1}`.\n///\n/// \\tparam NumIndices number of indices of the two operands\n/// \\param positions1 first operand's index positions where a generic spatial\n/// index is used for a spacetime index\n/// \\param positions2 second operand's index positions where a generic spatial\n/// index is used for a spacetime index\n/// \\return the element-wise multi-index shift from the first operand's\n/// multi-indices to the second's\ntemplate <size_t NumIndices, size_t NumPositions1, size_t NumPositions2>\nconstexpr std::array<std::int32_t, NumIndices>\nspatial_spacetime_index_transformation_from_positions(\n    const std::array<size_t, NumPositions1>& positions1,\n    const std::array<size_t, NumPositions2>& positions2) {\n  std::array<std::int32_t, NumIndices> transformation =\n      make_array<NumIndices, std::int32_t>(0);\n  for (size_t i = 0; i < NumPositions1; i++) {\n    gsl::at(transformation, gsl::at(positions1, i))--;\n  }\n  for (size_t i = 0; i < NumPositions2; i++) {\n    gsl::at(transformation, gsl::at(positions2, i))++;\n  }\n  return transformation;\n}\n}  // namespace detail\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/SquareRoot.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/Tensor/Expressions/NumberAsExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Defines the tensor expression representing the square root of a\n/// tensor expression that evaluates to a rank 0 tensor\n///\n/// \\details The expression can have a non-zero number of indices as long as\n/// all indices are concrete time indices, as this represents a rank 0 tensor.\n///\n/// For details on aliases and members defined in this class, as well as general\n/// `TensorExpression` terminology used in its members' documentation, see\n/// documentation for `TensorExpression`.\n///\n/// \\tparam T the type of the tensor expression of which to take the square\n/// root\n/// \\tparam Args the TensorIndexs of the expression\ntemplate <typename T, typename... Args>\nstruct SquareRoot\n    : public TensorExpression<SquareRoot<T, Args...>, typename T::type,\n                              typename T::symmetry, typename T::index_list,\n                              tmpl::list<Args...>> {\n  static_assert(\n      (... and tt::is_time_index<Args>::value),\n      \"Can only take the square root of a tensor expression that evaluates to \"\n      \"a rank 0 tensor.\");\n\n  // === Index properties ===\n  /// The type of the data being stored in the result of the expression\n  using type = typename T::type;\n  /// The ::Symmetry of the result of the expression\n  using symmetry = typename T::symmetry;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using index_list = typename T::index_list;\n  /// The list of generic `TensorIndex`s of the result of the expression\n  using args_list = tmpl::list<Args...>;\n  /// The number of tensor indices in the result of the expression\n  static constexpr auto num_tensor_indices = sizeof...(Args);\n\n  // === Expression subtree properties ===\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// left operand\n  static constexpr size_t num_ops_left_child = T::num_ops_subtree;\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// right operand. This is 0 because this expression represents a unary\n  /// operation.\n  static constexpr size_t num_ops_right_child = 0;\n  /// The total number of arithmetic tensor operations done in this expression's\n  /// whole subtree\n  static constexpr size_t num_ops_subtree = num_ops_left_child + 1;\n  /// The height of this expression's node in the expression tree relative to\n  /// the closest `TensorAsExpression` leaf in its subtree\n  static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree =\n      T::height_relative_to_closest_tensor_leaf_in_subtree !=\n              std::numeric_limits<size_t>::max()\n          ? T::height_relative_to_closest_tensor_leaf_in_subtree + 1\n          : T::height_relative_to_closest_tensor_leaf_in_subtree;\n\n  // === Properties for splitting up subexpressions along the primary path ===\n  // These definitions only have meaning if this expression actually ends up\n  // being along the primary path that is taken when evaluating the whole tree.\n  // See documentation for `TensorExpression` for more details.\n  /// If on the primary path, whether or not the expression is an ending point\n  /// of a leg\n  static constexpr bool is_primary_end = T::is_primary_start;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the subtree of the child along the\n  /// primary path, given that we will have already computed the whole subtree\n  /// at the next lowest leg's starting point.\n  static constexpr size_t num_ops_to_evaluate_primary_left_child =\n      is_primary_end ? 0 : T::num_ops_to_evaluate_primary_subtree;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the right operand's subtree. No\n  /// splitting is currently done, so this is just `num_ops_right_child`.\n  static constexpr size_t num_ops_to_evaluate_primary_right_child =\n      num_ops_right_child;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done for this expression's subtree, given that\n  /// we will have already computed the subtree at the next lowest leg's\n  /// starting point\n  static constexpr size_t num_ops_to_evaluate_primary_subtree =\n      num_ops_to_evaluate_primary_left_child +\n      num_ops_to_evaluate_primary_right_child + 1;\n  /// If on the primary path, whether or not the expression is a starting point\n  /// of a leg\n  static constexpr bool is_primary_start =\n      num_ops_to_evaluate_primary_subtree >=\n      detail::max_num_ops_in_sub_expression<type>;\n  /// If on the primary path, whether or not the expression's child along the\n  /// primary path is a subtree that contains a starting point of a leg along\n  /// the primary path\n  static constexpr bool primary_child_subtree_contains_primary_start =\n      T::primary_subtree_contains_primary_start;\n  /// If on the primary path, whether or not this subtree contains a starting\n  /// point of a leg along the primary path\n  static constexpr bool primary_subtree_contains_primary_start =\n      is_primary_start or primary_child_subtree_contains_primary_start;\n\n  SquareRoot(T t) : t_(std::move(t)) {}\n  ~SquareRoot() override = default;\n\n  /// \\brief Assert that the LHS tensor of the equation does not also appear in\n  /// this expression's subtree\n  template <typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensor_not_in_rhs_expression(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T>) {\n      t_.assert_lhs_tensor_not_in_rhs_expression(lhs_tensor);\n    }\n  }\n\n  /// \\brief Assert that each instance of the LHS tensor in the RHS tensor\n  /// expression uses the same generic index order that the LHS uses\n  ///\n  /// \\tparam LhsTensorIndices the list of generic `TensorIndex`s of the LHS\n  /// result `Tensor` being computed\n  /// \\param lhs_tensor the LHS result `Tensor` being computed\n  template <typename LhsTensorIndices, typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensorindices_same_in_rhs(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if constexpr (not std::is_base_of_v<MarkAsNumberAsExpression, T>) {\n      t_.template assert_lhs_tensorindices_same_in_rhs<LhsTensorIndices>(\n          lhs_tensor);\n    }\n  }\n\n  /// \\brief Get the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  ///\n  /// \\return the size of a component from a `Tensor` in this expression's\n  /// subtree of the RHS `TensorExpression`\n  SPECTRE_ALWAYS_INLINE size_t get_rhs_tensor_component_size() const {\n    return t_.get_rhs_tensor_component_size();\n  }\n\n  /// \\brief Returns the square root of the component of the tensor evaluated\n  /// from the contained tensor expression\n  ///\n  /// \\details\n  /// SquareRoot only supports tensor expressions that evaluate to a rank 0\n  /// Tensor. This is why `multi_index` is always an array of size 0.\n  ///\n  /// \\param multi_index the multi-index of the component of which to take the\n  /// square root\n  /// \\return the square root of the component of the tensor evaluated from the\n  /// contained tensor expression\n  SPECTRE_ALWAYS_INLINE decltype(auto) get(\n      const std::array<size_t, num_tensor_indices>& multi_index) const {\n    return sqrt(t_.get(multi_index));\n  }\n\n  /// \\brief Returns the square root of the component of the tensor evaluated\n  /// from the contained tensor expression\n  ///\n  /// \\details\n  /// SquareRoot only supports tensor expressions that evaluate to a rank 0\n  /// Tensor. This is why `multi_index` is always an array of size 0.\n  ///\n  /// This function differs from `get` in that it takes into account whether we\n  /// have already computed part of the result component at a lower subtree.\n  /// In recursively computing this square root, the current result component\n  /// will be substituted in for the most recent (highest) subtree below it that\n  /// has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param multi_index the multi-index of the component of which to take the\n  /// square root\n  /// \\return the square root of the component of the tensor evaluated from the\n  /// contained tensor expression\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(\n      const ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& multi_index) const {\n    if constexpr (is_primary_end) {\n      (void)multi_index;\n      // We've already computed the whole child subtree on the primary path, so\n      // just return the square root of the current result component\n      return sqrt(result_component);\n    } else {\n      // We haven't yet evaluated the whole subtree for this expression, so\n      // return the square root of this expression's subtree\n      return sqrt(t_.get_primary(result_component, multi_index));\n    }\n  }\n\n  /// \\brief Successively evaluate the LHS Tensor's result component at each\n  /// leg in this expression's subtree\n  ///\n  /// \\details\n  /// This function takes into account whether we have already computed part of\n  /// the result component at a lower subtree. In recursively computing this\n  /// square root, the current result component will be substituted in for the\n  /// most recent (highest) subtree below it that has already been evaluated.\n  ///\n  /// \\param result_component the LHS tensor component to evaluate\n  /// \\param multi_index the multi-index of the component of the result tensor\n  /// to evaluate\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_subtree(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>& multi_index) const {\n    if constexpr (primary_child_subtree_contains_primary_start) {\n      // The primary child's subtree contains at least one leg, so recurse down\n      // and evaluate that first\n      t_.evaluate_primary_subtree(result_component, multi_index);\n    }\n\n    if constexpr (is_primary_start) {\n      // We want to evaluate the subtree for this expression\n      result_component = get_primary(result_component, multi_index);\n    }\n  }\n\n private:\n  /// Operand expression\n  T t_;\n};\n}  // namespace tenex\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Returns the tensor expression representing the square root of a\n/// tensor expression that evaluates to a rank 0 tensor\n///\n/// \\details\n/// `t` must be an expression that, when evaluated, would be a rank 0 tensor.\n/// For example, if `R` and `S` are Tensors, here is a non-exhaustive list of\n/// some of the acceptable forms that `t` could take:\n/// - `R()`\n/// - `R(ti::A, ti::a)`\n/// - `(R(ti::A, ti::B) * S(ti::a, ti::b))`\n/// - `R(ti::t, ti::t) + 1.0`\n///\n/// \\param t the tensor expression of which to take the square root\ntemplate <typename T, typename X, typename Symm, typename IndexList,\n          typename... Args>\nSPECTRE_ALWAYS_INLINE auto sqrt(\n    const TensorExpression<T, X, Symm, IndexList, tmpl::list<Args...>>& t) {\n  return tenex::SquareRoot<T, Args...>(~t);\n}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/TensorAsExpression.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines expressions that represent tensors\n\n#pragma once\n\n#include <array>\n#include <cassert>\n#include <cstddef>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/Tensor/Expressions/DataTypeSupport.hpp\"\n#include \"DataStructures/Tensor/Expressions/SpatialSpacetimeIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\nnamespace detail {\n/// @{\n/// \\brief Given a tensor symmetry and the positions of indices where a generic\n/// spatial index is used for a spacetime index and the positions where a\n/// concrete time index is used, this returns the symmetry after making those\n/// indices nonsymmetric with others that do not do the same\n///\n/// \\details\n/// Example: Let `symmetry` be `{1, 2, 2, 2, 1}`. Let\n/// `spatial_spacetime_index_positions` be `{1, 2}`: the 2nd and 3rd indices\n/// of the tensor are spacetime indices where a generic spatial index is used.\n/// Let `time_index_positions` be `{4}`: the 5th (last) index is a spacetime\n/// index where a concrete time index is used. The resulting symmetry will\n/// reflect that the 2nd and 3rd indices are still symmetric with each other,\n/// but no longer symmetric with the 4th index, and the 5th index will no\n/// longer be symmetric with the 1st index. Therefore, the returned symmetry\n/// will be equivalent to the form of `{4, 3, 3, 2, 1}`.\n///\n/// Note: the symmetry returned by this function is not necessarily in the\n/// canonical form specified by ::Symmetry. For example, the actual array\n/// returned in the example above is `{1, 4, 4, 2, 5}`. As such, this array\n/// still encodes which indices are symmetric, but it is not in the canonical\n/// form specified by ::Symmetry, which is `{4, 3, 3, 2, 1}`.\n///\n/// \\param symmetry the input tensor symmetry to transform\n/// \\param spatial_spacetime_index_positions the positions of the indices of the\n/// tensor where a generic spatial index is used for a spacetime index\n/// \\param time_index_positions the positions of the indices of the tensor where\n/// a concrete time index is used for a spacetime index\n/// \\return the symmetry after making the `spatial_spacetime_index_positions`\n/// and `time_index_positions` of `symmetry` nonsymmetric with indices at other\n/// positions\ntemplate <\n    size_t NumIndices, size_t NumSpatialSpacetimeIndices,\n    size_t NumConcreteTimeIndices,\n    Requires<(NumIndices >= 2 and (NumSpatialSpacetimeIndices != 0 or\n                                   NumConcreteTimeIndices != 0))> = nullptr>\nSPECTRE_ALWAYS_INLINE constexpr std::array<std::int32_t, NumIndices>\nget_transformed_spacetime_symmetry(\n    const std::array<std::int32_t, NumIndices>& symmetry,\n    const std::array<size_t, NumSpatialSpacetimeIndices>&\n        spatial_spacetime_index_positions,\n    const std::array<size_t, NumConcreteTimeIndices>& time_index_positions) {\n  std::array<std::int32_t, NumIndices> transformed_spacetime_symmetry{};\n  const std::int32_t max_symm_value =\n      static_cast<std::int32_t>(*alg::max_element(symmetry));\n  for (size_t i = 0; i < NumIndices; i++) {\n    gsl::at(transformed_spacetime_symmetry, i) = gsl::at(symmetry, i);\n  }\n  for (size_t i = 0; i < NumSpatialSpacetimeIndices; i++) {\n    gsl::at(transformed_spacetime_symmetry,\n            gsl::at(spatial_spacetime_index_positions, i)) += max_symm_value;\n  }\n  for (size_t i = 0; i < NumConcreteTimeIndices; i++) {\n    gsl::at(transformed_spacetime_symmetry, gsl::at(time_index_positions, i)) +=\n        2 * max_symm_value;\n  }\n\n  return transformed_spacetime_symmetry;\n}\n\ntemplate <size_t NumIndices, size_t NumSpatialSpacetimeIndices,\n          size_t NumConcreteTimeIndices,\n          Requires<(NumIndices < 2 or (NumSpatialSpacetimeIndices == 0 and\n                                       NumConcreteTimeIndices == 0))> = nullptr>\nSPECTRE_ALWAYS_INLINE constexpr std::array<std::int32_t, NumIndices>\nget_transformed_spacetime_symmetry(\n    const std::array<std::int32_t, NumIndices>& symmetry,\n    const std::array<size_t, NumSpatialSpacetimeIndices>&\n    /*spatial_spacetime_index_positions*/,\n    const std::array<size_t, NumConcreteTimeIndices>&\n    /*time_index_positions*/) {\n  return symmetry;\n}\n/// @}\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Helper struct for computing the new canonical symmetry of a tensor\n/// after generic spatial indices are used for any of the tensor's spacetime\n/// indices\n///\n/// \\details This is relevant in cases where a tensor has spacetime indices that\n/// are symmetric but generic spatial indices are used for a non-empty subset of\n/// those symmetric spacetime indices. For example, if we have some rank 3\n/// tensor with the first index being spatial and the 2nd and third indices\n/// spacetime and symmetric, but a generic spatial index is used for the 2nd\n/// index, the \"result\" of the single tensor expression, \\f$R_{ija}\\f$, is a\n/// rank 3 tensor whose 2nd and 3rd indices are no longer symmetric.\n\n/// Given that some `Tensor` named `R` that represents the tensor in the above\n/// example, the symmetry of the `Tensor` is `[2, 1, 1]`, but the computed\n/// symmetry of the `TensorAsExpression` that represents it will have symmetry\n/// `[3, 2, 1]` to reflect this loss of symmetry.\n///\n/// Evaluating the \"result\" symmetry here in `TensorAsExpression`, at the leaves\n/// of the expression tree, enables the propagation of this symmetry up the tree\n/// to the other expression types. By determining each tensor's \"result\"\n/// symmetry at the leaves, the expressions at internal nodes of the tree can\n/// have their individual symmetries determined without having to each consider\n/// whether their operand(s) are expression(s) that have spacetime indices where\n/// generic spatial indices were used.\n///\n/// \\tparam SymmList the ::Symmetry of the Tensor represented by the expression\n/// \\tparam TensorIndexTypeList the \\ref SpacetimeIndex \"TensorIndexType\"'s of\n/// the Tensor represented by the expression\n/// \\tparam TensorIndexList the generic indices of the Tensor represented by the\n/// expression\ntemplate <typename SymmList, typename TensorIndexTypeList,\n          typename TensorIndexList,\n          size_t NumIndices = tmpl::size<SymmList>::value,\n          typename IndexSequence = std::make_index_sequence<NumIndices>>\nstruct TensorAsExpressionSymm;\n\ntemplate <template <typename...> class SymmList, typename... Symm,\n          typename TensorIndexTypeList, typename TensorIndexList,\n          size_t NumIndices, size_t... Ints>\nstruct TensorAsExpressionSymm<SymmList<Symm...>, TensorIndexTypeList,\n                              TensorIndexList, NumIndices,\n                              std::index_sequence<Ints...>> {\n  static constexpr auto symm = get_transformed_spacetime_symmetry<NumIndices>(\n      {{Symm::value...}},\n      get_spatial_spacetime_index_positions<TensorIndexTypeList,\n                                            TensorIndexList>(),\n      get_time_index_positions<TensorIndexList>());\n  using type = Symmetry<symm[Ints]...>;\n};\n}  // namespace detail\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Defines an expression representing a Tensor\n///\n/// \\details\n/// In order to represent a tensor as an expression, instead of having Tensor\n/// derive off of TensorExpression, a TensorAsExpression derives off of\n/// TensorExpression and contains a pointer to a Tensor. The reason having\n/// Tensor derive off of TensorExpression is problematic is that the index\n/// structure is part of the type of the TensorExpression, so every possible\n/// permutation and combination of indices must be derived from. For a rank 3\n/// tensor, this is already over 500 base classes, which the Intel compiler\n/// takes too long to compile.\n///\n/// For details on aliases and members defined in this class, as well as general\n/// `TensorExpression` terminology used in its members' documentation, see\n/// documentation for `TensorExpression`.\n///\n/// \\tparam T the type of Tensor being represented as an expression\n/// \\tparam ArgsList the tensor indices, e.g. `_a` and `_b` in `F(_a, _b)`\ntemplate <typename T, typename ArgsList>\nstruct TensorAsExpression;\n\ntemplate <typename X, template <typename...> class Symm, typename... SymmValues,\n          template <typename...> class IndexList, typename... Indices,\n          template <typename...> class ArgsList, typename... Args>\nstruct TensorAsExpression<Tensor<X, Symm<SymmValues...>, IndexList<Indices...>>,\n                          ArgsList<Args...>>\n    : public TensorExpression<TensorAsExpression<Tensor<X, Symm<SymmValues...>,\n                                                        IndexList<Indices...>>,\n                                                 ArgsList<Args...>>,\n                              X,\n                              typename detail::TensorAsExpressionSymm<\n                                  Symm<SymmValues...>, IndexList<Indices...>,\n                                  ArgsList<Args...>>::type,\n                              IndexList<Indices...>, ArgsList<Args...>> {\n  static_assert(detail::is_supported_tensor_datatype_v<X>,\n                \"TensorExpressions currently only support Tensors whose data \"\n                \"type is double, std::complex<double>, DataVector, or \"\n                \"ComplexDataVector. It is possible to add support for other \"\n                \"data types that are supported by Tensor.\");\n  // `Symmetry` currently prevents this because antisymmetries are not currently\n  // supported for `Tensor`s. This check is repeated here because if\n  // antisymmetries are later supported for `Tensor`, using antisymmetries in\n  // `TensorExpression`s will not automatically work. The implementations of the\n  // derived `TensorExpression` types assume no antisymmetries (assume positive\n  // `Symmetry` values), so support for antisymmetries in `TensorExpression`s\n  // will still need to be implemented.\n  static_assert((... and (SymmValues::value > 0)),\n                \"Anti-symmetric Tensors are currently not supported by \"\n                \"TensorExpressions.\");\n\n  // === Index properties ===\n  /// The type of the data being stored in the result of the expression\n  using type = X;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using symmetry = typename detail::TensorAsExpressionSymm<\n      Symm<SymmValues...>, IndexList<Indices...>, ArgsList<Args...>>::type;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s of the result of the\n  /// expression\n  using index_list = IndexList<Indices...>;\n  /// The list of generic `TensorIndex`s of the result of the expression\n  using args_list = ArgsList<Args...>;\n  /// The number of tensor indices in the result of the expression\n  static constexpr auto num_tensor_indices = tmpl::size<index_list>::value;\n\n  // === Expression subtree properties ===\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// left operand, which is 0 because this is a leaf expression\n  static constexpr size_t num_ops_left_child = 0;\n  /// The number of arithmetic tensor operations done in the subtree for the\n  /// right operand, which is 0 because this is a leaf expression\n  static constexpr size_t num_ops_right_child = 0;\n  /// The total number of arithmetic tensor operations done in this expression's\n  /// whole subtree, which is 0 because this is a leaf expression\n  static constexpr size_t num_ops_subtree = 0;\n  /// The height of this expression's node in the expression tree relative to\n  /// the closest `TensorAsExpression` leaf in its subtree. Because this\n  /// expression type is that leaf, the height for this type will always be 0.\n  static constexpr size_t height_relative_to_closest_tensor_leaf_in_subtree = 0;\n\n  // === Properties for splitting up subexpressions along the primary path ===\n  // These definitions only have meaning if this expression actually ends up\n  // being along the primary path that is taken when evaluating the whole tree.\n  // See documentation for `TensorExpression` for more details.\n  /// If on the primary path, whether or not the expression is an ending point\n  /// of a leg\n  static constexpr bool is_primary_end = true;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the subtree of the child along the\n  /// primary path, given that we will have already computed the whole subtree\n  /// at the next lowest leg's starting point. This is just 0 because this\n  /// expression is a leaf.\n  static constexpr size_t num_ops_to_evaluate_primary_left_child = 0;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done in the right operand's subtree. This is\n  /// just 0 because this expression is a leaf.\n  static constexpr size_t num_ops_to_evaluate_primary_right_child = 0;\n  /// If on the primary path, this is the remaining number of arithmetic tensor\n  /// operations that need to be done for this expression's subtree, given that\n  /// we will have already computed the subtree at the next lowest leg's\n  /// starting point. This is just 0 because this expression is a leaf.\n  static constexpr size_t num_ops_to_evaluate_primary_subtree = 0;\n  /// If on the primary path, whether or not the expression is a starting point\n  /// of a leg\n  static constexpr bool is_primary_start = false;\n  /// If on the primary path, whether or not the expression's child along the\n  /// primary path is a subtree that contains a starting point of a leg along\n  /// the primary path. This is always falls because this expression is a leaf.\n  static constexpr bool primary_child_subtree_contains_primary_start = false;\n  /// If on the primary path, whether or not this subtree contains a starting\n  /// point of a leg along the primary path\n  static constexpr bool primary_subtree_contains_primary_start =\n      is_primary_start;\n\n  /// Construct an expression from a Tensor\n  explicit TensorAsExpression(\n      const Tensor<X, Symm<SymmValues...>, IndexList<Indices...>>& t)\n      : t_(&t) {}\n  ~TensorAsExpression() override = default;\n\n  /// \\brief Assert that the LHS tensor of the equation is not equal to the\n  /// `Tensor` represented by this expression\n  template <typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensor_not_in_rhs_expression(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    (void)lhs_tensor;\n    ASSERT(static_cast<const void*>(&(*t_)) != &(*lhs_tensor),\n           \"The LHS Tensor cannot also be in the RHS expression in the call to \"\n           \"tenex::evaluate(). Use tenex::update() instead.\");\n  }\n\n  /// \\brief If the LHS tensor is the tensor represented by this expression,\n  /// assert that the order of the generic indices are the same\n  ///\n  /// \\tparam LhsTensorIndices the list of generic `TensorIndex`s of the LHS\n  /// result `Tensor` being computed\n  /// \\param lhs_tensor the LHS result `Tensor` being computed\n  template <typename LhsTensorIndices, typename LhsTensor>\n  SPECTRE_ALWAYS_INLINE void assert_lhs_tensorindices_same_in_rhs(\n      const gsl::not_null<LhsTensor*> lhs_tensor) const {\n    if (static_cast<const void*>(&(*t_)) == &(*lhs_tensor)) {\n      ASSERT(\n          (std::is_same_v<LhsTensorIndices, args_list>),\n          \"The LHS Tensor was also found in the RHS tensor expression, but the \"\n          \"generic index order used for it on the RHS does not match the order \"\n          \"used for it on the LHS.\");\n    }\n  }\n\n  /// \\brief Get the size of a component from the `Tensor` contained by this\n  /// expression\n  ///\n  /// \\return the size of a component from the `Tensor` contained by this\n  /// expression\n  SPECTRE_ALWAYS_INLINE size_t get_rhs_tensor_component_size() const {\n    return get_size((*t_)[0]);\n  }\n\n  /// \\brief Returns the value of the contained tensor's multi-index\n  ///\n  /// \\param multi_index the multi-index of the tensor component to retrieve\n  /// \\return the value of the component at `multi_index` in the tensor\n  SPECTRE_ALWAYS_INLINE decltype(auto) get(\n      const std::array<size_t, num_tensor_indices>& multi_index) const {\n    return t_->get(multi_index);\n  }\n\n  /// \\brief Returns the value of the contained tensor's multi-index\n  ///\n  /// \\param multi_index the multi-index of the tensor component to retrieve\n  /// \\return the value of the component at `multi_index` in the tensor\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE decltype(auto) get_primary(\n      const ResultType& /*result_component*/,\n      const std::array<size_t, num_tensor_indices>& multi_index) const {\n    return t_->get(multi_index);\n  }\n\n  // This expression is a leaf and therefore will never be the start of a leg\n  // to evaluate in a split tree, which is enforced by\n  // `is_primary_start == false`. Therefore, this function should never be\n  // called on this expression type.\n  template <typename ResultType>\n  SPECTRE_ALWAYS_INLINE void evaluate_primary_subtree(\n      ResultType& result_component,\n      const std::array<size_t, num_tensor_indices>&) const = delete;\n\n  /// Retrieve the i'th entry of the Tensor being held\n  SPECTRE_ALWAYS_INLINE type operator[](const size_t i) const {\n    return t_->operator[](i);\n  }\n\n private:\n  /// `Tensor` represented by this expression\n  const Tensor<X, Symm<SymmValues...>, IndexList<Indices...>>* t_ = nullptr;\n};\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/TensorExpression.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines base class for all tensor expressions\n\n#pragma once\n\n#include <limits>\n\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Marks a class as being a TensorExpression\n///\n/// \\details\n/// The empty base class provides a simple means for checking if a type is a\n/// TensorExpression.\nstruct Expression {};\n\n/// @{\n/// \\ingroup TensorExpressionsGroup\n/// \\brief The base class all tensor expression implementations derive from\n///\n/// \\details\n/// ## Tensor equation construction\n/// Each derived `TensorExpression` class should be thought of as an expression\n/// tree that represents some operation done on or between tensor expressions.\n/// Arithmetic operators and other mathematical functions of interest\n/// (e.g. `sqrt`) have overloads defined that accept `TensorExpression`s and\n/// return a new `TensorExpression` representing the result tensor of such an\n/// operation. In this way, an equation written with `TensorExpression`s will\n/// generate an expression tree where the internal and leaf nodes are instances\n/// of the derived `TensorExpression` classes. For example, `tenex::AddSub`\n/// defines an internal node for handling the addition and subtraction\n/// operations between tensors expressions, while `tenex::TensorAsExpression`\n/// defines a leaf node that represents a single `Tensor` that appears in the\n/// equation.\n///\n/// ## Tensor equation evaluation\n/// The overall tree for an equation and the order in which we traverse the tree\n/// define the order of operations done to compute the resulting LHS `Tensor`.\n/// The evaluation is done by `tenex::evaluate`, which traverses the whole tree\n/// once for each unique LHS component in order to evaluate the full LHS\n/// `Tensor`. There are two different traversals currently implemented that are\n/// chosen from, depending on the tensor equation being evaluated:\n/// 1. **Evaluate the whole tree as one expression** using in-order traversal.\n/// This is like generating and solving a one-liner of the whole equation.\n/// 2. **Split up the tree into subexpressions** that are each evaluated with\n/// in-order traversal to successively \"accumulate\" a LHS result component of\n/// the equation. This is like splitting the equation up and solving pieces of\n/// it at a time with multiple lines of assignments/updates (see details below).\n///\n/// ## Equation splitting details\n/// Splitting up the tree and evaluating subexpressions is beneficial when we\n/// believe it to lead to a better runtime than if we were to compute the whole\n/// expression as a one-liner. One important use case is when the `Tensor`s in\n/// the equation hold components whose data type is `DataVector`. From\n/// benchmarking, it was found that the runtime of `DataVector` expressions\n/// scales poorly as we increase the number of operations. For example, for an\n/// inner product with 256 sums of products, instead of adding 256 `DataVector`\n/// products in one line (e.g. `result = A*B + C*D + E*F + ...;`), it's much\n/// faster to, say, set the result to be the sum of the first 8 products, then\n/// `+=` the next 8, and so forth. This is what is meant by \"accumulating\" the\n/// LHS result tensor, and what the `TensorExpression` splitting emulates. Note\n/// that while 8 is the number used in this example, the exact optimal number of\n/// operations will be hardware-dependent, but probably not something we need to\n/// really worry about fine-tuning. However, a ballpark estimate for a \"good\"\n/// number of operations may vary greatly depending on the data type of the\n/// components (e.g. `double` vs. `DataVector`), which is something important\n/// to at least coarsely tune.\n///\n/// ### How the tree is split up\n/// Let's define the **primary path** to be the path in the tree going from the\n/// root node to the leftmost leaf. The overall tree contains subtrees\n/// represented by different `TensorExpression`s in the equation. Certain\n/// subtrees are marked as the starting and/or ending points of these \"pieces\"\n/// of the equation. Let's define a **leg** to be a \"segment\" along the primary\n/// path delineated by a starting and ending expression subtree. These\n/// delineations are made where we decide there are enough operations in a\n/// subtree that it would be wise to split at that point. What is considered to\n/// be \"enough\" operations is specialized based on the data type held by the\n/// `Tensor`s in the expression (see `tenex::max_num_ops_in_sub_expression`).\n///\n/// ### How a split tree is traversed and evaluated\n/// We recurse down the primary path, visiting each expression subtree until we\n/// reach the start of the lowest leg, then initialize the LHS result component\n/// we're wanting to compute to be the result of this lowest expression. Then,\n/// we recurse back up to the expression subtree that is starting point of the\n/// leg \"above\" it and compute that subtree. This time, however, when\n/// recursively evaluating this higher subtree, we substitute in the current LHS\n/// result for that lower subtree that we have already computed. This is\n/// repeated as we \"climb up\" the primary path to successively accumulate the\n/// result component.\n///\n/// **Note:** The primary path is currently implemented as the path specified\n/// above, but there's no reason it couldn't be reimplemented to be a different\n/// path. The idea with the current implementation is to select a path from root\n/// to leaf that is long so we have more flexibility in splitting, should we\n/// want to. When evaluating, we *could* implement the traversal to take a\n/// different path, but currently, derived `TensorExpression`s that represent\n/// commutative binary operations are instantiated with the larger subtree being\n/// the left child and the smaller subtree being the right child. By\n/// constructing it this way, we elongate the leftmost path, which will allow\n/// for increased splitting.\n///\n/// ## Requirements for derived `TensorExpression` classes\n/// Each derived `TensorExpression` class must define the following aliases and\n/// members:\n/// - `private` variables that store its operands' derived `TensorExpression`s.\n/// We make these non-`const` to allow for move construction.\n/// - Constructor that initializes the above `private` operand members\n/// - alias `type`: The data type of the data being stored in the result of the\n/// expression, e.g. `double`, `DataVector`\n/// - alias `symmetry`: The ::Symmetry of the result of the expression\n/// - alias `index_list`: The list of \\ref SpacetimeIndex \"TensorIndexType\"s of\n/// the result of the expression\n/// - alias `args_list`: The list of generic `TensorIndex`s of the result of the\n/// expression\n/// - variable `static constexpr size_t num_tensor_indices`: The number of\n/// tensor indices in the result of the expression\n/// - variable `static constexpr size_t num_ops_left_child`: The number of\n/// arithmetic tensor operations done in the subtree for the expression's left\n/// operand. If the expression represents a unary operation, their only child is\n/// considered the left child. If the expression is a leaf node, then this value\n/// should be set to 0 since retrieving a value at the leaf involves 0\n/// arithmetic tensor operations.\n/// - variable `static constexpr size_t num_ops_right_child`: The number of\n/// arithmetic tensor operations done in the expression's right operand. If the\n/// expression represents a unary operation or is leaf node, this should be set\n/// to 0 because there is no right child.\n/// - variable `static constexpr size_t num_ops_subtree`: The number of\n/// arithmetic tensor operations done in the subtree represented by the\n/// expression. For `AddSub`, for example, this is\n/// `num_ops_left_child + num_ops_right_child + 1`, the sum of the number of\n/// operations in each operand's subtrees plus one for the operation done for\n/// the expression, itself.\n/// - variable `static constexpr size_t\n/// height_relative_to_closest_tensor_leaf_in_subtree` : The height of an\n/// expression's node in the overall expression tree relative to the closest\n/// `TensorAsExpression` leaf in its subtree. This is stored so that we can\n/// traverse from the root along the shortest path to a `Tensor` when\n/// retrieving the size of a component from the RHS expression (see\n/// `get_rhs_tensor_component_size()` below). Non-`Tensor` leaves (e.g.\n/// `NumberAsExpression`) are defined to have maximum height\n/// `std::numeric_limits<size_t>::max()` to encode that they are maximally\n/// far away from their nearest `Tensor` descendant, since the expression's\n/// subtree (a leaf) can never have a `TensorAsExpression` descedant from it.\n/// This maximal height is leveraged by `get_rhs_tensor_component_size()` so\n/// that in traversing the expression tree to find a `Tensor`, it will never\n/// take the path that ends in a non-`Tensor` leaf because it is the worst path\n/// option.\n/// - function `decltype(auto) get(const std::array<size_t, num_tensor_indices>&\n/// result_multi_index) const`: Accepts a multi-index for the result tensor\n/// represented by the expression and returns the computed result of the\n/// expression at that multi-index. This should call the operands' `get`\n/// functions in order to recursively compute the result of the expression.\n/// - function template\n/// `template <typename LhsTensor> void assert_lhs_tensor_not_in_rhs_expression(\n/// const gsl::not_null<LhsTensor*> lhs_tensor) const`: Asserts that the LHS\n/// `Tensor` we're computing does not also appear in the RHS `TensorExpression`.\n/// We define this because if a tree is split up, then the LHS `Tensor` will\n/// generally not be computed correctly because the LHS components will be\n/// updated as we traverse the split tree.\n/// - function template\n/// \\code\n/// template <typename LhsTensorIndices, typename LhsTensor>\n/// void assert_lhs_tensorindices_same_in_rhs(\n///     const gsl::not_null<LhsTensor*> lhs_tensor) const;\n/// \\endcode\n/// Asserts that any instance of the LHS `Tensor` in the RHS `TensorExpression`\n/// uses the same generic index order that the LHS uses. We define this because\n/// if a tree is not split up, it's safe to use the LHS `Tensor` on the RHS if\n/// the generic index order is the same. In these cases, `tenex::update` should\n/// be used instead of `tenex::evaluate`. See the documentation for\n/// `tenex::update` for more details and `tenex::detail::evaluate_impl` for why\n/// this is safe to do.\n/// - function `size_t get_rhs_tensor_component_size() const`: Gets the size of\n/// a component from a `Tensor` in an expression's subtree of the RHS\n/// expression. This is used to size LHS components, if needed. Utilizes\n/// `height_relative_to_closest_tensor_leaf_in_subtree` to recursively find the\n/// nearest `TensorAsExpression` descendant leaf.\n///\n/// Each derived `TensorExpression` class must also define the following\n/// members, which have real meaning for the expression *only* if it ends up\n/// belonging to the primary path of the tree that is traversed:\n/// - variable `static constexpr bool is_primary_start`: If on the primary path,\n/// whether or not the expression is a starting point of a leg. This is true\n/// when there are enough operations to warrant splitting (see\n/// `tenex::max_num_ops_in_sub_expression`).\n/// - variable `static constexpr bool is_primary_end`: If on the primary path,\n/// whether or not the expression is an ending point of a leg. This is true when\n/// the expression's child along the primary path is a starting point of a leg.\n/// - variable `static constexpr size_t num_ops_to_evaluate_primary_left_child`:\n/// If on the primary path, this is the remaining number of arithmetic tensor\n/// operations that need to be done in the subtree of the child along the\n/// primary path, given that we will have already computed the whole subtree at\n/// the next lowest leg's starting point.\n/// - variable\n/// `static constexpr size_t num_ops_to_evaluate_primary_right_child`:\n/// If on the primary path, this is the remaining number of arithmetic tensor\n/// operations that need to be done in the right operand's subtree. Because\n/// the branches off of the primary path currently are not split up in any way,\n/// this currently should simply be equal to `num_ops_right_child`. If logic is\n/// added to split up these branches, logic will need to be added to compute\n/// this remaining number of operations in the right subtree.\n/// - variable `static constexpr size_t num_ops_to_evaluate_primary_subtree`:\n/// If on the primary path, this is the remaining number of arithmetic tensor\n/// operations that need to be done for this expression's subtree, given that we\n/// will have already computed the subtree at the next lowest leg's starting\n/// point. For example, for `tenex::AddSub`, this is just\n/// `num_ops_to_evaluate_primary_left_child +\n/// num_ops_to_evaluate_primary_right_child + 1` (the extra 1 for the `+` or `-`\n/// operation itself).\n/// - variable\n/// `static constexpr bool primary_child_subtree_contains_primary_start`:\n/// If on the primary path, whether or not the expression's child along the\n/// primary path is a subtree that contains a starting point of a leg along the\n/// primary path. In other words, whether or not there is a split on the primary\n/// path lower than this expression. When evaluating a split tree, this is\n/// useful because it tells us we need to keep recursing down to a lower leg and\n/// evaluate that lower subtree first before evaluating the current subtree.\n/// - variable `static constexpr bool primary_subtree_contains_primary_start`:\n/// If on the primary path, whether or not this subtree contains a starting\n/// point of a leg along the primary path. In other words, whether or not there\n/// is a split on the primary path at this expression or beneath it.\n/// - function template\n/// \\code\n/// template <typename ResultType>\n/// decltype(auto) get_primary(\n///     ResultType& result_component,\n///     const std::array<size_t, num_tensor_indices>& result_multi_index) const\n/// \\endcode\n/// This is similar to the required `get` function described above, but this\n/// should be used when the tree is split up. The main difference with this\n/// function is that it takes the current result component (that we're\n/// computing) as an argument, and when we hit the starting point of the next\n/// lowest leg on the primary path when recursively evaluating the current leg,\n/// we substitute in the current LHS result for the subtree that we have already\n/// computed. This function should call `get_primary` on the child on the\n/// primary path and `get` on the other child, if one exists.\n/// - function template\n/// \\code\n/// template <typename ResultType>\n/// void evaluate_primary_subtree(\n///     ResultType& result_component,\n///     const std::array<size_t, num_tensor_indices>& result_multi_index) const\n/// \\endcode\n/// This should first recursively evaluate the legs beneath it on the primary\n/// path, then if the expression itself is the start of a leg, it should\n/// evaluate this leg by calling the expression's own `get_primary` to compute\n/// it and update the result component being accumulated. `tenex::evaluate`\n/// should call this function on the root node for the whole tree if there is\n/// determined to be any splits in the tree.\n///\n/// ## Data type support\n/// Which types can be used, which operations with which types can be performed,\n/// and other type-specific support and configuration can be found in\n/// `DataStructures/Tensor/Expressions/DataTypeSupport.hpp`. To add support for\n/// equation terms with a certain type or to modify the configuration for a\n/// type that is already supported, see the contents of that file and modify\n/// settings as necessary.\n///\n/// ## Current advice for improving and extending `TensorExpression`s\n/// - Derived `TensorExpression` classes (or the overloads that produce them)\n/// should include `static_assert`s for ensuring mathematical correctness\n/// wherever reasonable\n/// - Minimize breadth in the tree where possible because benchmarking inner\n/// products has shown that increased tree breadth can cause slower runtimes.\n/// In addition, more breadth means a decreased ability to split up the tree\n/// along the primary path.\n/// - Minimize the number of multi-index transformations that need to be done\n/// when evaluating the tree. For some operations like addition, the associated\n/// multi-indices of the two operands needs to be computed from the multi-index\n/// of the result, which may involve reordering and/or shifting the values of\n/// the result index. It's good to minimize the number of these kinds of\n/// transformations from result to operand multi-index where we can.\n/// - Unless the implementation of Tensor_detail::Structure changes, it's not\n/// advised for the derived `TensorExpression` classes to have anything that\n/// would instantiate the Tensor_detail::Structure of the tensor that would\n/// result from the expression. This is really only a problem when the result of\n/// the expression would be a tensor with many components, because the compile\n/// time of the mapping between storage indices and multi-indices within\n/// Tensor_detail::Structure scales very poorly with the number of components.\n/// It's important to keep in mind that while SpECTRE currently only supports\n/// creating `Tensor`s up to rank 4, there is nothing preventing the represented\n/// result tensor of a expression being higher rank, e.g.\n/// `R(ti_j, ti_b, ti_A) * (S(ti_d, ti_a, ti_B, ti_C) * T(ti_J, ti_k, ti_l))`\n/// contains an intermediate outer product expression\n/// `S(ti_d, ti_a, ti_B, ti_C) * T(ti_J, ti_k, ti_l)` that represents a rank 7\n/// tensor, even though a rank 7 `Tensor` is never instantiated. Having the\n/// outer product expression instantiate the Tensor_detail::Structure of this\n/// intermediate result currently leads to an unreasonable compile time.\n///\n/// \\tparam Derived the derived class needed for\n/// [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)\n/// \\tparam DataType the type of the data being stored in the `Tensor`s\n/// \\tparam Symm the ::Symmetry of the Derived class\n/// \\tparam IndexList the list of \\ref SpacetimeIndex \"TensorIndexType\"s\n/// \\tparam Args typelist of the tensor indices, e.g. types of `ti::a` and\n/// `ti::b` in `F(ti::a, ti::b)`\ntemplate <typename Derived, typename DataType, typename Symm,\n          typename IndexList, typename Args = tmpl::list<>,\n          typename ReducedArgs = tmpl::list<>>\nstruct TensorExpression;\n\ntemplate <typename Derived, typename DataType, typename Symm,\n          typename... Indices, template <typename...> class ArgsList,\n          typename... Args>\nstruct TensorExpression<Derived, DataType, Symm, tmpl::list<Indices...>,\n                        ArgsList<Args...>> : public Expression {\n  static_assert(sizeof...(Args) == 0 or sizeof...(Args) == sizeof...(Indices),\n                \"the number of Tensor indices must match the number of \"\n                \"components specified in an expression.\");\n  /// The type of the data being stored in the `Tensor`s\n  using type = DataType;\n  /// The ::Symmetry of the `Derived` class\n  using symmetry = Symm;\n  /// The list of \\ref SpacetimeIndex \"TensorIndexType\"s\n  using index_list = tmpl::list<Indices...>;\n  /// Typelist of the tensor indices, e.g. types of `ti_a` and `ti_b`\n  /// in `F(ti_a, ti_b)`\n  using args_list = ArgsList<Args...>;\n  /// The number of tensor indices of the `Derived` class\n  static constexpr auto num_tensor_indices = tmpl::size<index_list>::value;\n\n  virtual ~TensorExpression() = 0;\n\n  /// @{\n  /// Derived is casted down to the derived class. This is enabled by the\n  /// [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)\n  ///\n  /// \\returns const TensorExpression<Derived, DataType, Symm, IndexList,\n  /// ArgsList<Args...>>&\n  SPECTRE_ALWAYS_INLINE const auto& operator~() const {\n    return static_cast<const Derived&>(*this);\n  }\n  /// @}\n};\n\ntemplate <typename Derived, typename DataType, typename Symm,\n          typename... Indices, template <typename...> class ArgsList,\n          typename... Args>\nTensorExpression<Derived, DataType, Symm, tmpl::list<Indices...>,\n                 ArgsList<Args...>>::~TensorExpression() = default;\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/TensorIndex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines generic tensor indices used in `TensorExpression`s\n\n#pragma once\n\n#include <array>\n#include <cassert>\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tenex {\nnamespace TensorIndex_detail {\n// The below values are used to separate upper indices from lower indices and\n// spatial indices from spacetime indices.\n//\n// Tensor expressions perform as many calculations as possible in a constexpr\n// context, which means working with fundamental types, specifically integer\n// types, is easiest. By using sentinel values defined in one location we can\n// easily control the encoding without having magic values floating around in\n// many places. Furthermore, encoding all the information in the `size_t` means\n// that when a failure occurs in one of the constexpr calculations it is\n// reasonably easy to debug because, while encoded, the full type information is\n// present. This approach can effectively be thought of as using specific bits\n// in the `size_t` to mark information, using the size_t more as a bitfield than\n// anything else. For human readability, we use base-10 numbers instead of\n// base-2 values that would truly set individual bits.\n//\n// Spacetime indices are represented by values [0, `spatial_sentinel`) and\n// spatial indices are represented by values\n// [`spatial_sentinel`, `max_sentinel`). Lower spacetime indices are represented\n// by values [0, `upper_sentinel`), and upper spacetime indices are represented\n// by values [`upper_sentinel`, `spatial_sentinel`). Lower spatial indices are\n// represented by values\n// [`spatial_sentinel`, `spatial_sentinel` + `upper_sentinel`), and upper\n// spatial indices are represented by values\n// [`spatial_sentinel` + `upper_sentinel`, `max_sentinel`). Values equal to or\n// above `max_sentinel` are considered invalid for representing an index.\nstatic constexpr size_t spatial_sentinel = 1000;\nstatic constexpr size_t upper_sentinel = 500;\nstatic constexpr size_t upper_spatial_sentinel =\n    spatial_sentinel + upper_sentinel;\nstatic constexpr size_t max_sentinel = 2000;\n}  // namespace TensorIndex_detail\n}  // namespace tenex\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Represents the geeric indices in a TensorExpression\n *\n * \\details\n * Used to denote a tensor index in a tensor slot. This allows the following\n * type of expressions to work:\n * \\code{.cpp}\n * auto T = evaluate<ti::a, ti::b>(F(ti::a, ti::b) + S(ti::b, ti::a));\n * \\endcode\n * where `decltype(ti::a) == TensorIndex<0>` and\n * `decltype(ti::b) == TensorIndex<1>`. That is, `ti::a` and `ti::b` are\n * placeholders for objects of type `TensorIndex<0>` and `TensorIndex<1>`,\n * respectively.\n */\ntemplate <std::size_t I,\n          Requires<(I < tenex::TensorIndex_detail::max_sentinel)> = nullptr>\nstruct TensorIndex {\n  using value_type = std::size_t;\n  using type = TensorIndex<I>;\n  static constexpr value_type value = I;\n  static constexpr UpLo valence =\n      ((I < tenex::TensorIndex_detail::upper_sentinel) or\n       (I >= tenex::TensorIndex_detail::spatial_sentinel and\n        I < tenex::TensorIndex_detail::upper_spatial_sentinel))\n          ? UpLo::Lo\n          : UpLo::Up;\n  static constexpr bool is_spacetime =\n      I < tenex::TensorIndex_detail::spatial_sentinel;\n};\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Contains definitions for the available `TensorIndex`s to use in a\n * `TensorExpression`\n */\nnamespace ti {\n/// @{\n/*!\n * \\brief The available `TensorIndex`s to use in a `TensorExpression`\n *\n * \\details The suffix following `ti::` indicates index properties:\n * - Uppercase: contravariant/upper index\n * - Lowercase: covariant/lower index\n * - A/a - H/h: generic spacetime index\n * - I/i - M/m: generic spatial index\n * - T/t: concrete time index (defined as a spacetime `TensorIndex`)\n *\n * \\snippet Test_AddSubtract.cpp use_tensor_index\n *\n * If you want to support a new generic index, definitions for the upper and\n * lower versions of the index must be added as unique `TensorIndex` types, e.g.\n * \\code\n * static constexpr TensorIndex<UNIQUE_INTEGER_IN_PROPER_RANGE_LOWER> ti::x{};\n * static constexpr TensorIndex<UNIQUE_INTEGER_IN_PROPER_RANGE_UPPER> ti::X{};\n * \\endcode\n * where `UNIQUE_INTEGER_IN_PROPER_RANGE_LOWER` and\n * `UNIQUE_INTEGER_IN_PROPER_RANGE_UPPER` are unique, but related integers that\n * fall in the integer ranges that properly encode the index's properties\n * according to the `_sentinel` values defined at the top of\n * `src/DataStructures/Tensor/Expressions/TensorIndex.hpp`. This enables the new\n * index to be distinguishable from others and for the upper and lower versions\n * to be recognized as related by opposite valence. See comments there on these\n * integer ranges to properly encode the new index (both upper and lower\n * definitions) that you wish to add. In short, you should simply be able to\n * continue the pattern used for the existing `TensorIndex` types that are\n * already defined. For example, if `ti::M`/`ti::m` is the highest-valued\n * generic spatial index currently defined and you want to add `ti::N`/`ti::n`\n * as a new generic spatial index, you can simply define `ti::N` and `ti::n`'s\n * unique integer values to be `INTEGER_VALUE_FOR_M + 1` and\n * `INTEGER_VALUE_FOR_m + 1`, respectively. For adding a new generic spacetime\n * index, you should be able to do the same thing with respect to the upper and\n * lower versions of the highest-valued currently defined generic spacetime\n * `TensorIndex`.\n */\nstatic constexpr TensorIndex<0> a{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel> A{};\nstatic constexpr TensorIndex<1> b{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 1> B{};\nstatic constexpr TensorIndex<2> c{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 2> C{};\nstatic constexpr TensorIndex<3> d{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 3> D{};\nstatic constexpr TensorIndex<4> e{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 4> E{};\nstatic constexpr TensorIndex<5> f{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 5> F{};\nstatic constexpr TensorIndex<6> g{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 6> G{};\nstatic constexpr TensorIndex<7> h{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel + 7> H{};\n\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_sentinel - 1> t{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel - 1>\n    T{};\n\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel> i{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel>\n    I{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 1>\n    j{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +\n                             1>\n    J{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 2>\n    k{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +\n                             2>\n    K{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 3>\n    l{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +\n                             3>\n    L{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 4>\n    m{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +\n                             4>\n    M{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::spatial_sentinel + 5>\n    n{};\nstatic constexpr TensorIndex<tenex::TensorIndex_detail::upper_spatial_sentinel +\n                             5>\n    N{};\n/// @}\n}  // namespace ti\n\nnamespace tenex {\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Returns the TensorIndex value of with opposite valence.\n *\n * \\details The input value represents a TensorIndex value, which encodes\n * both the valence of the index and whether the index is spacetime or\n * spatial. This function returns the value that corresponds to the encoding of\n * the TensorIndex with the same index type, but opposite valence.\n *\n * For example, 0 is the TensorIndex value for `ti::a`. If `i == 0`, then 500\n * will be returned, which is the TensorIndex value for `ti::A`. If `i == 500`\n * (representing `ti::A`), then 0 (representing `ti::a`) is returned.\n *\n * @param i a TensorIndex value that represents a generic index\n * @return the TensorIndex value that encodes the generic index with the\n * opposite valence\n */\nSPECTRE_ALWAYS_INLINE static constexpr size_t\nget_tensorindex_value_with_opposite_valence(const size_t i) {\n  assert(i < TensorIndex_detail::max_sentinel);  // NOLINT\n  if ((i >= TensorIndex_detail::upper_sentinel and\n       i < TensorIndex_detail::spatial_sentinel) or\n      (i >= TensorIndex_detail::upper_spatial_sentinel)) {\n    // `i` represents an upper index, so return the lower index's encoding\n    return i - TensorIndex_detail::upper_sentinel;\n  } else {\n    // `i` represents a lower index, so return the upper index's encoding\n    return i + TensorIndex_detail::upper_sentinel;\n  }\n}\n}  //  namespace tenex\n\nnamespace tt {\n/*!\n * \\ingroup TypeTraitsGroup TensorExpressionsGroup\n * \\brief Check if a type `T` is a TensorIndex used in TensorExpressions\n */\ntemplate <typename T>\nstruct is_tensor_index : std::false_type {};\ntemplate <size_t I>\nstruct is_tensor_index<TensorIndex<I>> : std::true_type {};\n\ntemplate <typename T>\nstruct is_time_index;\n}  // namespace tt\n\nnamespace tenex {\nnamespace detail {\ntemplate <auto&... TensorIndices>\nstruct make_tensorindex_list_impl {\n  static_assert(\n      (... and\n       tt::is_tensor_index<std::decay_t<decltype(TensorIndices)>>::value),\n      \"Template parameters of make_tensorindex_list must be TensorIndex \"\n      \"objects.\");\n  using type = tmpl::list<std::decay_t<decltype(TensorIndices)>...>;\n};\n\ntemplate <typename TensorIndexList>\nstruct tensorindex_list_is_valid_impl;\n\ntemplate <typename... TensorIndices>\nstruct tensorindex_list_is_valid_impl<tmpl::list<TensorIndices...>> {\n  static_assert(\n      (... and tt::is_tensor_index<TensorIndices>::value),\n      \"Template parameters of tensorindex_list_is_valid must be TensorIndex \"\n      \"types.\");\n  static constexpr bool value = tmpl::is_set<TensorIndices...>::value;\n};\n\ntemplate <typename TensorIndexList1, typename TensorIndexList2,\n          bool ListsSameSize>\nstruct generic_indices_at_same_positions_impl;\n\ntemplate <typename... TensorIndices1, typename... TensorIndices2>\nstruct generic_indices_at_same_positions_impl<\n    tmpl::list<TensorIndices1...>, tmpl::list<TensorIndices2...>, true> {\n  static_assert((... and (tt::is_tensor_index<TensorIndices1>::value and\n                          tt::is_tensor_index<TensorIndices2>::value)),\n                \"Template parameters of generic_indices_at_same_positions_impl \"\n                \"must be lists containing TensorIndex types.\");\n  using type = std::bool_constant<(\n      ... and (std::is_same_v<TensorIndices1, TensorIndices2> or\n               (tt::is_time_index<TensorIndices1>::value and\n                tt::is_time_index<TensorIndices2>::value)))>;\n};\n\ntemplate <typename... TensorIndices1, typename... TensorIndices2>\nstruct generic_indices_at_same_positions_impl<\n    tmpl::list<TensorIndices1...>, tmpl::list<TensorIndices2...>, false> {\n  static_assert((... and (tt::is_tensor_index<TensorIndices1>::value)),\n                \"The first template parameter of \"\n                \"generic_indices_at_same_positions_impl must be a list \"\n                \"containing TensorIndex types.\");\n  static_assert((... and (tt::is_tensor_index<TensorIndices2>::value)),\n                \"The second template parameter of \"\n                \"generic_indices_at_same_positions_impl must be a list \"\n                \"containing TensorIndex types.\");\n  using type = std::bool_constant<false>;\n};\n\ntemplate <typename TensorIndexList>\nstruct remove_time_indices;\n}  // namespace detail\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Determine whether or not a given list of TensorIndexs is valid\n * to be used with a tensor\n *\n * \\details A list of TensorIndexs is considered valid if the subset of generic\n * indices are a set. Indices with opposite valences are unique, e.g. one\n * instance each of `ti::a` and `ti::A` is valid. An arbitrary number of\n * concrete time indices, regardless of valence, is also valid.\n *\n * @tparam TensorIndexList list of generic index types, e.g. the types of\n * `ti::a, ti::b`\n */\ntemplate <typename TensorIndexList>\nstruct tensorindex_list_is_valid;\n\ntemplate <typename... TensorIndices>\nstruct tensorindex_list_is_valid<tmpl::list<TensorIndices...>> {\n  static constexpr bool value = detail::tensorindex_list_is_valid_impl<\n      typename detail::remove_time_indices<\n          tmpl::list<TensorIndices...>>::type>::value;\n};\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Determine whether or not two lists of TensorIndexs contain the same\n * generic indices at the same positions\n *\n * \\tparam TensorIndexList1 the first TensorIndex list\n * \\tparam TensorIndexList2 the second TensorIndex list\n */\ntemplate <typename TensorIndexList1, typename TensorIndexList2>\nusing generic_indices_at_same_positions =\n    typename detail::generic_indices_at_same_positions_impl<\n        TensorIndexList1, TensorIndexList2,\n        tmpl::size<TensorIndexList1>::value ==\n            tmpl::size<TensorIndexList2>::value>::type;\n}  // namespace tenex\n\n/*!\n * \\ingroup TensorExpressionsGroup\n * \\brief Creates a TensorIndex type list from a list of TensorIndex objects\n *\n * @tparam TensorIndices list of generic index objects, e.g. `ti::a, ti::b`\n */\ntemplate <auto&... TensorIndices>\nusing make_tensorindex_list =\n    typename tenex::detail::make_tensorindex_list_impl<TensorIndices...>::type;\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions useful for transforming tensor multi-indices according to\n/// a different generic index order\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace tenex {\nnamespace TensorIndexTransformation_detail {\nstatic constexpr size_t time_index_position_placeholder =\n    std::numeric_limits<size_t>::max();\n}  // namespace TensorIndexTransformation_detail\n\n/// \\brief Computes a transformation from one generic tensor index order to\n/// another\n///\n/// \\details\n/// In most cases, the elements of the transformation are simply the positions\n/// of the second list of generic indices in the first list of generic indices.\n/// Put another way, for some `i`,\n/// `tensorindices2[i] == tensorindices1[index_transformation[i]]`.\n///\n/// Here is an example of what the algorithm does:\n///\n/// Transformation between (1) \\f$R_{cab}\\f$ and (2) \\f$S_{abc}\\f$\n/// `tensorindices1`:\n/// \\code\n/// {2, 0, 1} // TensorIndex values for {c, a, b}\n/// \\endcode\n/// `tensorindices2`:\n/// \\code\n/// {0, 1, 2} // TensorIndex values for {a, b, c}\n/// \\endcode\n/// returned `tensorindex_transformation`:\n/// \\code\n/// {1, 2, 0} // positions of S' indices {a, b, c} in R's indices {c, a, b}\n/// \\endcode\n///\n/// One special case scenario to note is when concrete time indices are\n/// involved in the transformation. Consider transforming a multi-index for\n/// some tensor \\f$R_{ab}\\f$ to another tensor \\f$S_{btat}\\f$. This would be\n/// necessary for evaluating the LHS of a simple equation such as\n/// \\f$R_{ab} = S_{btat}\\f$. The transformation between a multi-index for\n/// \\f$R\\f$ to the equivalent multi-index for \\f$S\\f$ cannot simply be the list\n/// of positions of \\f$S\\f$' indices in \\f$R\\f$'s indices, as \\f$R\\f$ does not\n/// contain all of \\f$S\\f$' indices, because it has no time indices. To handle\n/// cases like this, a placeholder value for the position of any time index is\n/// substituted for an actual position, since one may not exist. In this\n/// example, the transformation would be\n/// `{1, PLACEHOLDER_VALUE, 0, PLACEHOLDER_VALUE}`, where `PLACEHOLDER_VALUE` is\n/// defined by\n/// `TensorIndexTransformation_detail::time_index_position_placeholder`. `1` and\n/// `0` are the positions of \\f$b\\f$ and \\f$a\\f$ in \\f$R\\f$, and the placeholder\n/// is used for the positions of time indices.\n///\n/// \\tparam NumIndices1 the number of indices for the first generic index order\n/// \\tparam NumIndices2 the number of indices for the second generic index order\n/// \\param tensorindices1 the TensorIndex values of the first generic index\n/// order\n/// \\param tensorindices2 the TensorIndex values of the second generic index\n/// order\n/// \\return a transformation from the first generic index order to the second\ntemplate <size_t NumIndices1, size_t NumIndices2>\nSPECTRE_ALWAYS_INLINE constexpr std::array<size_t, NumIndices2>\ncompute_tensorindex_transformation(\n    const std::array<size_t, NumIndices1>& tensorindices1,\n    const std::array<size_t, NumIndices2>& tensorindices2) {\n  std::array<size_t, NumIndices2> tensorindex_transformation{};\n  for (size_t i = 0; i < NumIndices2; i++) {\n    gsl::at(tensorindex_transformation, i) =\n        detail::is_time_index_value(gsl::at(tensorindices2, i))\n            ? TensorIndexTransformation_detail::time_index_position_placeholder\n            : static_cast<size_t>(std::distance(\n                  tensorindices1.begin(),\n                  alg::find(tensorindices1, gsl::at(tensorindices2, i))));\n  }\n  return tensorindex_transformation;\n}\n\n/// \\brief Computes the tensor multi-index that is equivalent to a given tensor\n/// multi-index, according to the differences in their generic index orders\n///\n/// \\details\n/// Here is an example of what the algorithm does:\n///\n/// Transform (input) multi-index of \\f$R_{cab}\\f$ to the equivalent (output)\n/// multi-index of \\f$S_{abc}\\f$\n/// `tensorindex_transformation`:\n/// \\code\n/// {1, 2, 0} // positions of S' indices {a, b, c} in R's indices {c, a, b}\n/// \\endcode\n/// `input_multi_index`:\n/// \\code\n/// {3, 4, 5} // i.e. c = 3, a = 4, b = 5\n/// \\endcode\n/// returned equivalent `output_multi_index`:\n/// \\code\n/// {4, 5, 3} // i.e. a = 4, b = 5, c = 3\n/// \\endcode\n///\n/// One special case scenario to note is when concrete time indices are\n/// involved in the transformation. Consider transforming a multi-index for\n/// some tensor \\f$R_{ab}\\f$ to another tensor \\f$S_{btat}\\f$. This would be\n/// necessary for evaluating the LHS of a simple equation such as\n/// \\f$R_{ab} = S_{btat}\\f$. The transformation between a multi-index for\n/// \\f$R\\f$ to the equivalent multi-index for \\f$S\\f$ cannot simply be the list\n/// of positions of \\f$S\\f$' indices in \\f$R\\f$'s indices, as \\f$R\\f$ does not\n/// contain all of \\f$S\\f$' indices, because it has no time indices. To handle\n/// cases like this, a placeholder value for the position of any time index\n/// must be substituted for an actual position, since one may not exist. In this\n/// example, the proper input transformation (`tensorindex_transformation`)\n/// would need to be\n/// `{1, PLACEHOLDER_VALUE, 0, PLACEHOLDER_VALUE}`, where `PLACEHOLDER_VALUE` is\n/// defined by\n/// `TensorIndexTransformation_detail::time_index_position_placeholder`. `1` and\n/// `0` are the positions of \\f$b\\f$ and \\f$a\\f$ in \\f$R\\f$, and the placeholder\n/// is used for the positions of time indices. In computing the output\n/// transformed multi-index, the function will insert a `0` at each position\n/// where this placeholder is found in the transformation. For example, if\n/// `input_multi_index` is `{1, 2}`, representing \\f$R_{12}\\f$, the returned\n/// output multi-index will be `{2, 0, 1, 0}`, representing \\f$S_{2010}\\f$.\n///\n/// \\tparam NumIndicesIn the number of indices\n/// \\tparam NumIndicesOut the number of indices\n/// \\param input_multi_index the input tensor multi-index to transform\n/// \\param tensorindex_transformation the positions of the output's generic\n/// indices in the input's generic indices (see example in details)\n/// \\return the output tensor multi-index that is equivalent to\n/// `input_multi_index`, according to generic index order differences\n// (`tensorindex_transformation`)\ntemplate <size_t NumIndicesIn, size_t NumIndicesOut>\nSPECTRE_ALWAYS_INLINE constexpr std::array<size_t, NumIndicesOut>\ntransform_multi_index(\n    const std::array<size_t, NumIndicesIn>& input_multi_index,\n    const std::array<size_t, NumIndicesOut>& tensorindex_transformation) {\n  std::array<size_t, NumIndicesOut> output_multi_index =\n      make_array<NumIndicesOut, size_t>(0);\n  for (size_t i = 0; i < NumIndicesOut; i++) {\n    gsl::at(output_multi_index, i) =\n        // Check that the index is not a time index instead of checking that it\n        // is, because we expect it to not be a time index most of the time\n        (gsl::at(tensorindex_transformation, i) !=\n         TensorIndexTransformation_detail::time_index_position_placeholder)\n            ? gsl::at(input_multi_index, gsl::at(tensorindex_transformation, i))\n            : 0;\n  }\n  return output_multi_index;\n}\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Expressions/TimeIndex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\file\n/// Defines functions and metafunctions used for helping evaluate\n/// TensorExpression equations where concrete time indices are used for\n/// spacetime indices\n\nnamespace tt {\n/// \\ingroup TypeTraitsGroup TensorExpressionsGroup\n/// \\brief Check if a type `T` is a TensorIndex representing a concrete time\n/// index\ntemplate <typename T>\nstruct is_time_index : std::false_type {};\ntemplate <>\nstruct is_time_index<std::decay_t<decltype(ti::t)>> : std::true_type {};\ntemplate <>\nstruct is_time_index<std::decay_t<decltype(ti::T)>> : std::true_type {};\n}  // namespace tt\n\nnamespace tenex {\nnamespace detail {\n/// \\brief Returns whether or not the provided value is the TensorIndex value\n/// that encodes the upper or lower concrete time index (`ti::T` or `ti::t`)\n///\n/// \\param value the value to check\n/// \\return whether or not the value encodes the upper or lower concrete time\n/// index\nconstexpr bool is_time_index_value(const size_t value) {\n  return value == ti::t.value or value == ti::T.value;\n}\n\ntemplate <typename State, typename Element>\nstruct remove_time_indices_impl {\n  using type =\n      typename std::conditional_t<not tt::is_time_index<Element>::value,\n                                  tmpl::push_back<State, Element>, State>;\n};\n\n/// \\brief Given a TensorIndex list, returns the TensorIndex list with time\n/// indices removed\n///\n/// \\tparam TensorIndexList the generic index list\ntemplate <typename TensorIndexList>\nstruct remove_time_indices {\n  using type =\n      tmpl::fold<TensorIndexList, tmpl::list<>,\n                 remove_time_indices_impl<tmpl::_state, tmpl::_element>>;\n};\n\ntemplate <typename State, typename Element, typename Iteration>\nstruct time_index_positions_impl {\n  using type =\n      typename std::conditional_t<tt::is_time_index<Element>::value,\n                                  tmpl::push_back<State, Iteration>, State>;\n};\n\n/// \\brief Given a TensorIndex list, returns the list of positions of concrete\n/// time indices\n///\n/// \\tparam TensorIndexList the TensorIndex list\ntemplate <typename TensorIndexList>\nusing time_index_positions = tmpl::enumerated_fold<\n    TensorIndexList, tmpl::list<>,\n    time_index_positions_impl<tmpl::_state, tmpl::_element, tmpl::_3>,\n    tmpl::size_t<0>>;\n\n/// \\brief Given a TensorIndex list, returns the list of positions of concrete\n/// time indices\n///\n/// \\tparam TensorIndexList the TensorIndex list\n/// \\return the list of positions of concrete time indices\ntemplate <typename TensorIndexList>\nconstexpr auto get_time_index_positions() {\n  using time_index_positions_ = time_index_positions<TensorIndexList>;\n  using make_list_type =\n      std::conditional_t<tmpl::size<time_index_positions_>::value == 0, size_t,\n                         time_index_positions_>;\n  return make_array_from_list<make_list_type>();\n}\n}  // namespace detail\n}  // namespace tenex\n"
  },
  {
    "path": "src/DataStructures/Tensor/Identity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\n/// \\ingroup TensorGroup\n/// \\brief returns the Identity matrix\ntemplate <size_t Dim, typename DataType>\ntnsr::Ij<DataType, Dim, Frame::NoFrame> identity(\n    const DataType& used_for_type) {\n  auto identity_matrix{make_with_value<tnsr::Ij<DataType, Dim, Frame::NoFrame>>(\n      used_for_type, 0.0)};\n\n  for (size_t i = 0; i < Dim; ++i) {\n    identity_matrix.get(i, i) = 1.0;\n  }\n  return identity_matrix;\n}\n"
  },
  {
    "path": "src/DataStructures/Tensor/IndexType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines classes representing tensor indices\n\n#pragma once\n\n#include <cstddef>\n#include <ostream>\n#include <string>\n#include <type_traits>\n\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup TensorGroup\n/// Whether a \\ref SpacetimeIndex \"TensorIndexType\" is covariant or\n/// contravariant\nenum class UpLo {\n  /// Contravariant, or Upper index\n  Up,\n  /// Covariant, or Lower index\n  Lo\n};\n\n/// \\cond HIDDEN_SYMBOLS\ninline std::ostream& operator<<(std::ostream& os, const UpLo& ul) {\n  return os << (ul == UpLo::Up ? \"Up\"s : \"Lo\"s);\n}\n/// \\endcond\n\n/// \\ingroup TensorGroup\n/// Indicates the ::Frame that a \\ref SpacetimeIndex \"TensorIndexType\"\n/// is in.\nnamespace Frame {\n/// \\ingroup TensorGroup\n/// Marks a Frame as being \"physical\" in the sense that it is meaningful to\n/// evaluate an analytic solution in that frame.\nstruct FrameIsPhysical {};\n\nstruct BlockLogical {};\nstruct ElementLogical {};\nstruct Fluid {};\nstruct Grid {};\nstruct Inertial : FrameIsPhysical {};\nstruct Distorted {};\n/// Represents an index that is not in a known frame, e.g. some internal\n/// intermediate frame that is irrelevant to the interface.\nstruct NoFrame {};\n\n/// Represents a spherical-coordinate frame that is associated with a\n/// Cartesian frame, e.g. \\f$(r,\\theta,\\phi)\\f$ associated with the Inertial\n/// frame, as used on an apparent horizon or a wave-extraction surface.\ntemplate <typename CartesianFrame>\nstruct Spherical {};\n\n/// \\ingroup TensorGroup\n/// Returns std::true_type if the frame is \"physical\" in the sense that it is\n/// meaningful to evaluate an analytic solution in that frame.\n/// \\example\n/// \\snippet Test_Tensor.cpp is_frame_physical\ntemplate <typename CheckFrame>\nusing is_frame_physical =\n    std::integral_constant<bool,\n                           std::is_base_of<FrameIsPhysical, CheckFrame>::value>;\n\n/// \\ingroup TensorGroup\n/// Returns true if the frame is \"physical\" in the sense that it is\n/// meaningful to evaluate an analytic solution in that frame.\n/// \\example\n/// \\snippet Test_Tensor.cpp is_frame_physical\ntemplate <typename CheckFrame>\nconstexpr bool is_frame_physical_v = is_frame_physical<CheckFrame>::value;\n\n/// \\cond HIDDEN_SYMBOLS\ninline std::ostream& operator<<(std::ostream& os,\n                                const Frame::BlockLogical& /*meta*/) {\n  return os << \"BlockLogical\";\n}\ninline std::ostream& operator<<(std::ostream& os,\n                                const Frame::ElementLogical& /*meta*/) {\n  return os << \"ElementLogical\";\n}\ninline std::ostream& operator<<(std::ostream& os,\n                                const Frame::Fluid& /*meta*/) {\n  return os << \"Fluid\";\n}\ninline std::ostream& operator<<(std::ostream& os,\n                                const Frame::Grid& /*meta*/) {\n  return os << \"Grid\";\n}\ninline std::ostream& operator<<(std::ostream& os,\n                                const Frame::Inertial& /*meta*/) {\n  return os << \"Inertial\";\n}\ninline std::ostream& operator<<(std::ostream& os,\n                                const Frame::Distorted& /*meta*/) {\n  return os << \"Distorted\";\n}\ninline std::ostream& operator<<(std::ostream& os,\n                                const Frame::NoFrame& /*meta*/) {\n  return os << \"NoFrame\";\n}\n/// \\endcond\n\n/// \\ingroup TensorGroup\n/// The frame-dependent prefix used when constructing the string returned by the\n/// name function of a tag.\n///\n/// For Frame::Inertial it is the empty string, otherwise, it is the name of\n/// the Frame followed by an underscore (as the name will be used in I/O).\n/// \\example\n/// \\snippet Hydro/Test_Tags.cpp prefix_example\ntemplate <typename Fr>\ninline std::string prefix();\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <>\ninline std::string prefix<Frame::BlockLogical>() {\n  return \"BlockLogical_\";\n}\n\ntemplate <>\ninline std::string prefix<Frame::ElementLogical>() {\n  return \"ElementLogical_\";\n}\n\ntemplate <>\ninline std::string prefix<Frame::Fluid>() {\n  return \"Fluid_\";\n}\n\ntemplate <>\ninline std::string prefix<Frame::Grid>() {\n  return \"Grid_\";\n}\n\ntemplate <>\ninline std::string prefix<Frame::Inertial>() {\n  return \"\";\n}\n\ntemplate <>\ninline std::string prefix<Frame::Distorted>() {\n  return \"Distorted_\";\n}\n/// \\endcond\n}  // namespace Frame\n\n/// \\ingroup TensorGroup\n/// Indicates whether the \\ref SpacetimeIndex \"TensorIndexType\" is\n/// Spatial or Spacetime\nenum class IndexType : char {\n  /// The \\ref SpatialIndex \"TensorIndexType\" is purely spatial\n  Spatial,\n  /// The \\ref SpacetimeIndex \"TensorIndexType\" is a spacetime index\n  Spacetime\n};\n\n/// \\cond HIDDEN_SYMBOLS\ninline std::ostream& operator<<(std::ostream& os, const IndexType& index_type) {\n  return os << (index_type == IndexType::Spatial ? \"Spatial\" : \"Spacetime\");\n}\n/// \\endcond\n\nnamespace Tensor_detail {\n/// \\ingroup TensorGroup\n/// A ::TensorIndexType holds information about what type of index an index of a\n/// Tensor is. It holds the information about the number of spatial dimensions,\n/// whether the index is covariant or contravariant (::UpLo), the ::Frame the\n/// index is in, and whether the Index is Spatial or Spacetime.\n/// \\tparam SpatialDim the spatial dimensionality of the TensorIndex\n/// \\tparam Ul either UpLo::Up or UpLo::Lo for contra or covariant\n/// \\tparam Fr the Frame the TensorIndex is in\n/// \\tparam Index either IndexType::Spatial or IndexType::Spacetime\ntemplate <size_t SpatialDim, UpLo Ul, typename Fr, IndexType Index>\nstruct TensorIndexType {\n  static_assert(SpatialDim > 0,\n                \"Cannot have a spatial dimensionality less than 1 (one) in a \"\n                \"TensorIndexType\");\n  using value_type = decltype(SpatialDim);\n  static constexpr value_type dim =\n      Index == IndexType::Spatial ? SpatialDim\n                                  : SpatialDim + static_cast<value_type>(1);\n  // value is here just so that some generic metafunctions can retrieve the dim\n  // easily\n  static constexpr value_type value = dim;\n  static constexpr UpLo ul = Ul;\n  using Frame = Fr;\n  static constexpr IndexType index_type = Index;\n};\n}  // namespace Tensor_detail\n\n/// \\ingroup TensorGroup\n/// A SpatialIndex holds information about the number of spatial\n/// dimensions, whether the index is covariant or contravariant (::UpLo), and\n/// the ::Frame the index is in.\n/// \\tparam SpatialDim the spatial dimensionality of the \\ref\n/// SpatialIndex \"TensorIndexType\"\n/// \\tparam Ul either UpLo::Up or UpLo::Lo for contra or covariant\n/// \\tparam Fr the ::Frame the \\ref SpatialIndex \"TensorIndexType\"\n/// is in\ntemplate <size_t SpatialDim, UpLo Ul, typename Fr>\nusing SpatialIndex =\n    Tensor_detail::TensorIndexType<SpatialDim, Ul, Fr, IndexType::Spatial>;\n\n/// \\ingroup TensorGroup\n/// A SpacetimeIndex holds information about the number of spatial\n/// dimensions, whether the index is covariant or contravariant (::UpLo), and\n/// the ::Frame the index is in.\n///\n/// \\tparam SpatialDim the spatial dimensionality of the \\ref\n/// SpacetimeIndex \"TensorIndexType\"\n/// \\tparam Ul either UpLo::Up or UpLo::Lo for contra or covariant\n/// \\tparam Fr the ::Frame the \\ref SpacetimeIndex \"TensorIndexType\"\n/// is in\ntemplate <size_t SpatialDim, UpLo Ul, typename Fr>\nusing SpacetimeIndex =\n    Tensor_detail::TensorIndexType<SpatialDim, Ul, Fr, IndexType::Spacetime>;\n\nnamespace tt {\n/// @{\n/// \\ingroup TensorGroup TypeTraitsGroup\n/// Inherits from std::true_type if T is a \\ref SpacetimeIndex\n/// \"TensorIndexType\"\n/// \\tparam T the type to check\ntemplate <typename T>\nstruct is_tensor_index_type : std::false_type {};\n/// \\cond HIDDEN_SYMBOLS\ntemplate <size_t SpatialDim, UpLo Ul, typename Fr, IndexType Index>\nstruct is_tensor_index_type<\n    Tensor_detail::TensorIndexType<SpatialDim, Ul, Fr, Index>>\n    : std::true_type {};\n/// \\endcond\n/// \\see is_tensor_index_type\ntemplate <typename T>\nusing is_tensor_index_type_t = typename is_tensor_index_type<T>::type;\n/// @}\n}  // namespace tt\n\n/// \\ingroup TensorGroup\n/// Change the \\ref SpacetimeIndex \"TensorIndexType\" to be covariant\n/// if it's contravariant and vice-versa\n///\n/// Here is an example of how to use ::change_index_up_lo\n/// \\snippet Test_Tensor.cpp change_up_lo\n///\n/// \\tparam Index the \\ref SpacetimeIndex \"TensorIndexType\" to change\ntemplate <typename Index>\nusing change_index_up_lo = Tensor_detail::TensorIndexType<\n    Index::index_type == IndexType::Spatial ? Index::value : Index::value - 1,\n    Index::ul == UpLo::Up ? UpLo::Lo : UpLo::Up, typename Index::Frame,\n    Index::index_type>;\n\ntemplate <typename... Ts>\nusing index_list = tmpl::list<Ts...>;\n\n/// \\ingroup TensorGroup\n/// Change the \\ref SpacetimeIndex \"TensorIndexType\" to be spacetime\n/// if it's spatial and vice versa\n///\n/// \\tparam Index the \\ref SpacetimeIndex \"TensorIndexType\" to change\ntemplate <typename Index>\nusing change_index_type = Tensor_detail::TensorIndexType<\n    Index::index_type == IndexType::Spatial ? Index::dim : Index::dim - 1,\n    Index::ul, typename Index::Frame,\n    Index::index_type == IndexType::Spatial ? IndexType::Spacetime\n                                            : IndexType::Spatial>;\n"
  },
  {
    "path": "src/DataStructures/Tensor/Metafunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines metafunctions used by Tensor\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\n/// \\ingroup TensorGroup\n/// Contains all metafunctions related to Tensor manipulations\nnamespace TensorMetafunctions {\nnamespace detail {\ntemplate <unsigned>\nstruct check_index_symmetry_impl;\n// empty typelist or only had a vector to start with\ntemplate <>\nstruct check_index_symmetry_impl<0> {\n  template <typename...>\n  using f = std::true_type;\n};\n\n// found incorrect symmetric index\ntemplate <>\nstruct check_index_symmetry_impl<1> {\n  template <typename...>\n  using f = std::false_type;\n};\n\n// recurse the list\ntemplate <>\nstruct check_index_symmetry_impl<2> {\n  template <typename Symm, typename IndexSymm, typename Index0,\n            typename... IndexPack>\n  using f = typename check_index_symmetry_impl<\n      tmpl::has_key<IndexSymm, tmpl::front<Symm>>::value and\n              not std::is_same<Index0,\n                               tmpl::at<IndexSymm, tmpl::front<Symm>>>::value\n          ? 1\n          : tmpl::size<Symm>::value == 1 ? 0 : 2>::\n      template f<tmpl::pop_front<Symm>,\n                 tmpl::insert<IndexSymm, tmpl::pair<tmpl::front<Symm>, Index0>>,\n                 IndexPack...>;\n};\n\n// helper function for prepending indices\ntemplate <typename TheTensor>\nusing new_sym_index =\n    tmpl::int32_t<1 +\n                  tmpl::fold<typename TheTensor::symmetry, tmpl::int32_t<0>,\n                             tmpl::max<tmpl::_state, tmpl::_element>>::value>;\n}  // namespace detail\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Check that each of symmetric indices is in the same frame and have the\n * same dimensionality.\n */\ntemplate <typename Symm, typename... IndexPack>\nusing check_index_symmetry = typename detail::check_index_symmetry_impl<\n    tmpl::size<Symm>::value == 0 or tmpl::size<Symm>::value == 1 ? 0 : 2>::\n    template f<Symm, tmpl::map<>, IndexPack...>;\ntemplate <typename Symm, typename... IndexPack>\nconstexpr bool check_index_symmetry_v =\n    check_index_symmetry<Symm, IndexPack...>::value;\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Add a spatial index to the front of a Tensor\n *\n * \\tparam TheTensor the tensor type to which the new index is prepended\n * \\tparam VolumeDim the volume dimension of the tensor index to prepend\n * \\tparam Fr the ::Frame of the tensor index to prepend\n */\ntemplate <typename TheTensor, std::size_t VolumeDim, UpLo Ul, typename Fr>\nusing prepend_spatial_index =\n    ::Tensor<typename TheTensor::type,\n             tmpl::push_front<typename TheTensor::symmetry,\n                              detail::new_sym_index<TheTensor>>,\n             tmpl::push_front<typename TheTensor::index_list,\n                              SpatialIndex<VolumeDim, Ul, Fr>>>;\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Add two symmetric spatial indices to the front of a Tensor (e.g. when\n * doing second derivatives)\n *\n * \\tparam TheTensor the tensor type to which the new indices is prepended\n * \\tparam VolumeDim the volume dimension of the tensor indices to prepend\n * \\tparam Fr the ::Frame of the tensor indices to prepend\n */\ntemplate <typename TheTensor, std::size_t VolumeDim, UpLo Ul, typename Fr>\nusing prepend_two_symmetric_spatial_indices = ::Tensor<\n    typename TheTensor::type,\n    tmpl::push_front<tmpl::push_front<typename TheTensor::symmetry,\n                                      detail::new_sym_index<TheTensor>>,\n                     detail::new_sym_index<TheTensor>>,\n    tmpl::push_front<tmpl::push_front<typename TheTensor::index_list,\n                                      SpatialIndex<VolumeDim, Ul, Fr>>,\n                     SpatialIndex<VolumeDim, Ul, Fr>>>;\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Add a spacetime index to the front of a Tensor\n *\n * \\tparam TheTensor the tensor type to which the new index is prepended\n * \\tparam VolumeDim the volume dimension of the tensor index to prepend\n * \\tparam Fr the ::Frame of the tensor index to prepend\n */\ntemplate <typename TheTensor, std::size_t VolumeDim, UpLo Ul, typename Fr>\nusing prepend_spacetime_index =\n    ::Tensor<typename TheTensor::type,\n             tmpl::push_front<typename TheTensor::symmetry,\n                              detail::new_sym_index<TheTensor>>,\n             tmpl::push_front<typename TheTensor::index_list,\n                              SpacetimeIndex<VolumeDim, Ul, Fr>>>;\n\n/// \\ingroup TensorGroup\n/// \\brief remove the first index of a tensor\n/// \\tparam TheTensor the tensor type whose first index is removed\ntemplate <typename TheTensor>\nusing remove_first_index =\n    ::Tensor<typename TheTensor::type,\n             tmpl::pop_front<typename TheTensor::symmetry>,\n             tmpl::pop_front<typename TheTensor::index_list>>;\n\n/// \\ingroup TensorGroup\n/// \\brief Swap the valences of all indices on a Tensor\ntemplate <typename TheTensor>\nusing change_all_valences =\n    ::Tensor<typename TheTensor::type, typename TheTensor::symmetry,\n             tmpl::transform<typename TheTensor::index_list,\n                             tmpl::bind<change_index_up_lo, tmpl::_1>>>;\n\n/// \\ingroup TensorGroup\n/// \\brief Swap the data type of a tensor for a new type\n/// \\tparam NewType the new data type\n/// \\tparam TheTensor the tensor from which to keep symmetry and index\n/// information\ntemplate <typename NewType, typename TheTensor>\nusing swap_type = ::Tensor<NewType, typename TheTensor::symmetry,\n                           typename TheTensor::index_list>;\n\nnamespace detail {\ntemplate <typename T, typename Frame>\nusing frame_is_the_same = std::is_same<typename T::Frame, Frame>;\n}  // namespace detail\n\n/// \\ingroup TensorGroup\n/// \\brief Return tmpl::true_type if any indices of the Tensor are in the\n/// frame Frame.\ntemplate <typename TheTensor, typename Frame>\nusing any_index_in_frame =\n    tmpl::any<typename TheTensor::index_list,\n              tmpl::bind<detail::frame_is_the_same, tmpl::_1, Frame>>;\n\n/// \\ingroup TensorGroup\n/// \\brief Return true if any indices of the Tensor are in the\n/// frame Frame.\ntemplate <typename TheTensor, typename Frame>\nconstexpr bool any_index_in_frame_v =\n    any_index_in_frame<TheTensor, Frame>::value;\n\n}  // namespace TensorMetafunctions\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"DataStructures/Tensor/Python/Tensor.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py_bindings::bind_scalar(m);\n  py_bindings::bind_tensor<1>(m);\n  py_bindings::bind_tensor<2>(m);\n  py_bindings::bind_tensor<3>(m);\n}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyTensor\")\n\nspectre_python_add_module(\n  Tensor\n  MODULE_PATH \"DataStructures\"\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  # Tensor instantiations are split into multiple files so they can be compiled\n  # in parallel and use less memory\n  InstantiateScalar.cpp\n  InstantiateTensor1d.cpp\n  InstantiateTensor2d.cpp\n  InstantiateTensor3d.cpp\n  PYTHON_FILES\n  __init__.py\n  Frame.py\n  tnsr.py\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Tensor.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  pybind11::module\n  PythonBindings\n  Utilities\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  )\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/Frame.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\"\"\"Defines the 'Frame' enum\n\n'Frame' is a namespace with separate structs in C++. In Python, we represent it\nas an enum instead. Since the frame is used as a template parameter in C++, it\nis part of the name of bound classes in Python. The mapping for the 'Tensor'\nclass is done in `tnsr.py`.\n\"\"\"\n\nfrom enum import Enum, auto\n\n\nclass Frame(Enum):\n    BlockLogical = auto()\n    ElementLogical = auto()\n    Grid = auto()\n    Inertial = auto()\n    Distorted = auto()\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/InstantiateScalar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/Python/Tensor.tpp\"\n\nnamespace py_bindings {\n\nvoid bind_scalar(pybind11::module& m) {\n  bind_tensor_impl<Scalar<DataVector>, TensorKind::Scalar>(m, \"Scalar\");\n  bind_tensor_impl<Scalar<double>, TensorKind::Scalar>(m, \"Scalar\");\n}\n\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/InstantiateTensor1d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/Python/Tensor.tpp\"\n\nnamespace py_bindings {\ntemplate void bind_tensor<1>(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/InstantiateTensor2d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/Python/Tensor.tpp\"\n\nnamespace py_bindings {\ntemplate void bind_tensor<2>(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/InstantiateTensor3d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Tensor/Python/Tensor.tpp\"\n\nnamespace py_bindings {\ntemplate void bind_tensor<3>(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/Tensor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_scalar(pybind11::module& m);\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_tensor(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/Tensor.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/Python/Tensor.hpp\"\n\n#include <memory>\n#include <pybind11/numpy.h>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PythonBindings/BoundChecks.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\n\nnamespace {\n\ntemplate <typename DataType>\nstd::string dtype_to_name() {\n  if constexpr (std::is_same_v<DataType, DataVector>) {\n    return \"DV\";\n  } else if constexpr (std::is_same_v<DataType, double>) {\n    return \"D\";\n  } else {\n    return \"\";\n  }\n}\n\n// Used to generate Python class names for Tensor types\nenum class TensorKind { Scalar, Tnsr, Jacobian };\n\ntemplate <typename TensorType, TensorKind Kind>\nstd::string class_name(const std::string& name) {\n  if constexpr (Kind == TensorKind::Scalar) {\n    return name + dtype_to_name<typename TensorType::type>();\n  } else if constexpr (Kind == TensorKind::Tnsr) {\n    return \"Tensor\" + name + dtype_to_name<typename TensorType::type>() +\n           std::to_string(\n               TensorType::index_dim(0) -\n               (TensorType::index_types()[0] == IndexType::Spacetime ? 1 : 0)) +\n           get_output(get<0>(TensorType::index_frames()));\n  } else if constexpr (Kind == TensorKind::Jacobian) {\n    // Jacobians have different frames, so handle them separately to other\n    // Tensor instantiations where all indices are in the same frame\n    return name + dtype_to_name<typename TensorType::type>() +\n           std::to_string(TensorType::index_dim(0)) +\n           get_output(get<1>(TensorType::index_frames())) + \"To\" +\n           get_output(get<0>(TensorType::index_frames()));\n  }\n}\n\ntemplate <typename TensorType,\n          typename Is = std::make_integer_sequence<size_t, TensorType::rank()>>\nstruct GetImpl;\n\ntemplate <typename TensorType, size_t... Is>\nstruct GetImpl<TensorType, std::integer_sequence<size_t, Is...>> {\n  template <size_t I>\n  struct make_size_t {\n    using type = size_t;\n  };\n  static constexpr typename TensorType::const_reference get(\n      const TensorType& tensor, typename make_size_t<Is>::type... args) {\n    return tensor.get(args...);\n  }\n\n  static constexpr size_t get_storage_index(\n      const TensorType& tensor, typename make_size_t<Is>::type... args) {\n    return tensor.get_storage_index(args...);\n  }\n};\n\ntemplate <typename TensorType, TensorKind Kind>\nvoid bind_tensor_impl(py::module& m, const std::string& name) {  // NOLINT\n  auto tensor =\n      py::class_<TensorType>(m, class_name<TensorType, Kind>(name).c_str(),\n                             py::buffer_protocol())\n          .def_property_readonly_static(\n              \"rank\",\n              [](const py::object& /*t*/) { return TensorType::rank(); })\n          .def_property_readonly_static(\n              \"size\",\n              [](const py::object& /*t*/) { return TensorType::size(); })\n          .def_property_readonly_static(\n              \"dim\",\n              [](const py::object& /*t*/) {\n                if constexpr (TensorType::rank() == 0) {\n                  return py::none{};\n                } else {\n                  // We currently bind only Tensors where all indices have the\n                  // same dimension, so just get the first one\n                  return TensorType::index_dim(0);\n                }\n              })\n          .def(py::init())\n          .def(\"__str__\", [](const TensorType& t) { return get_output(t); })\n          .def(\n              \"__iter__\",\n              [](const TensorType& t) {\n                return py::make_iterator(t.begin(), t.end());\n              },\n              // Keep object alive while iterator exists\n              py::keep_alive<0, 1>())\n          .def(\"__len__\", [](const TensorType& t) { return t.size(); })\n          .def(\"__getitem__\",\n               [](const TensorType& t, const size_t i) {\n                 bounds_check(t, i);\n                 return t[i];\n               })\n          .def(\"__setitem__\",\n               [](TensorType& t, const size_t i,\n                  const typename TensorType::type& v) {\n                 bounds_check(t, i);\n                 t[i] = v;\n               })\n          .def(\n              \"multiplicity\",\n              [](const TensorType& t, const size_t storage_index) {\n                return t.multiplicity(storage_index);\n              },\n              py::arg(\"storage_index\"))\n          .def(\n              \"component_suffix\",\n              [](const TensorType& t, const size_t storage_index) {\n                return t.component_suffix(storage_index);\n              },\n              py::arg(\"storage_index\"))\n          .def(\"get\", &GetImpl<TensorType>::get)\n          .def(\"get_storage_index\", &GetImpl<TensorType>::get_storage_index)\n          // NOLINTNEXTLINE(misc-redundant-expression)\n          .def(py::self == py::self)\n          // NOLINTNEXTLINE(misc-redundant-expression)\n          .def(py::self != py::self);\n\n  if constexpr (std::is_same_v<typename TensorType::type, DataVector>) {\n    tensor.def(py::init<size_t>(), py::arg(\"num_points\"))\n        .def(py::init<size_t, double>(), py::arg(\"num_points\"), py::arg(\"fill\"))\n        // Support Python buffer protocol to cast to and from Numpy arrays\n        .def(py::init([](const py::buffer& buffer, const bool copy) {\n               py::buffer_info info = buffer.request();\n               // Sanity-check the buffer\n               if (info.format != py::format_descriptor<double>::format()) {\n                 throw std::runtime_error(\n                     \"Incompatible format: expected a double array.\");\n               }\n               // 1D arrays are allowed to construct scalars. Higher-rank\n               // tensors require 2D arrays.\n               if (not(info.ndim == 2 or\n                       (info.ndim == 1 and TensorType::rank() == 0))) {\n                 throw std::runtime_error(\n                     \"Tensor data is expected to be 2D with shape (size, \"\n                     \"num_points). Scalars can be 1D.\");\n               }\n               const size_t size =\n                   info.ndim == 1 ? 1 : static_cast<size_t>(info.shape[0]);\n               if (size != TensorType::size()) {\n                 throw std::runtime_error(\n                     \"This tensor type has \" +\n                     std::to_string(TensorType::size()) +\n                     \" independent components, but data has first dimension \" +\n                     std::to_string(size) + \".\");\n               }\n               const auto num_points =\n                   static_cast<size_t>(info.shape[info.ndim == 1 ? 0 : 1]);\n               auto data = static_cast<double*>(info.ptr);\n               const std::array<size_t, 2> strides{\n                   {info.ndim == 1\n                        ? 1\n                        : static_cast<size_t>(info.strides[0] / info.itemsize),\n                    static_cast<size_t>(info.strides[info.ndim == 1 ? 0 : 1] /\n                                        info.itemsize)}};\n               if (copy) {\n                 TensorType result{num_points};\n                 for (size_t i = 0; i < size; ++i) {\n                   for (size_t j = 0; j < num_points; ++j) {\n                     // NOLINTNEXTLINE\n                     result[i][j] = data[i * strides[0] + j * strides[1]];\n                   }\n                 }\n                 return result;\n               } else {\n                 if (strides[1] != 1) {\n                   throw std::runtime_error(\n                       \"Non-owning DataVectors only work with a stride of 1, \"\n                       \"but stride is \" +\n                       std::to_string(strides[1]) + \".\");\n                 }\n                 TensorType result{};\n                 for (size_t i = 0; i < size; ++i) {\n                   // NOLINTNEXTLINE\n                   result[i].set_data_ref(data + i * strides[0], num_points);\n                 }\n                 return result;\n               }\n             }),\n             py::arg(\"buffer\"), py::arg(\"copy\") = true);\n  } else if constexpr (std::is_same_v<typename TensorType::type, double>) {\n    tensor.def(py::init<double>(), py::arg(\"fill\"));\n  }\n\n  if constexpr (TensorType::rank() <= 1) {\n    // Scalars and vectors have an unambiguous storage order for their\n    // components: a scalar has only a single component, and a vector has Dim\n    // components in an obvious order. Therefore, they can be constructed from\n    // the underlying std::array (Python list) of DataVectors and also\n    // implicitly converted from Numpy arrays. Component ordering for\n    // higher-rank tensors isn't obvious, so we don't enable the conversion.\n    tensor.def(py::init<typename TensorType::storage_type>());\n    py::implicitly_convertible<py::array, TensorType>();\n  }\n}\n}  // namespace\n\ntemplate <size_t Dim>\nvoid bind_tensor(py::module& m) {\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TENSOR(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE_TNSR(_, data)                                       \\\n  bind_tensor_impl<tnsr::TENSOR(data) < DTYPE(data), Dim, FRAME(data)>, \\\n      TensorKind::Tnsr > (m, BOOST_PP_STRINGIZE(TENSOR(data)));\n#define INSTANTIATE_JAC(_, data)                                             \\\n  bind_tensor_impl<                                                          \\\n      Jacobian<DTYPE(data), Dim, Frame::ElementLogical, FRAME(data)>,        \\\n      TensorKind::Jacobian>(m, \"Jacobian\");                                  \\\n  bind_tensor_impl<                                                          \\\n      InverseJacobian<DTYPE(data), Dim, Frame::ElementLogical, FRAME(data)>, \\\n      TensorKind::Jacobian>(m, \"Jacobian\");\n#define INSTANTIATE_LOGICAL_DERIV(_, data)                                  \\\n  bind_tensor_impl<TensorMetafunctions::prepend_spatial_index<              \\\n                       tnsr::TENSOR(data) < DTYPE(data), Dim, FRAME(data)>, \\\n                   Dim, UpLo::Lo, Frame::ElementLogical>,                   \\\n      TensorKind::Tnsr >                                                    \\\n          (m, std::string{std::string{\"ELi\"} +                              \\\n                          std::string{BOOST_PP_STRINGIZE(TENSOR(data))}});\n\n  // Only tnsr::I and tnsr::i need to be instantiated for all frames currently,\n  // so to reduce compile time and library size, we're choosing to only\n  // instantiate other tensors in Frame::Inertial\n  GENERATE_INSTANTIATIONS(INSTANTIATE_TNSR, (double, DataVector),\n                          (Frame::ElementLogical, Frame::BlockLogical,\n                           Frame::Grid, Frame::Distorted, Frame::Inertial),\n                          (I, i))\n\n  GENERATE_INSTANTIATIONS(INSTANTIATE_TNSR, (double, DataVector),\n                          (Frame::Inertial),\n                          (a, A, ii, aa, II, AA, ij, ab, Ij, Ab, iJ, aB, ijj,\n                           iJJ, abb, Ijj, Abb, iaa, ijaa, iJkk, aBcc))\n\n  GENERATE_INSTANTIATIONS(INSTANTIATE_JAC, (double, DataVector),\n                          (Frame::Grid, Frame::Inertial))\n\n  GENERATE_INSTANTIATIONS(INSTANTIATE_LOGICAL_DERIV, (double, DataVector),\n                          (Frame::Inertial),\n                          (i, I, a, A, ii, II, aa, AA))\n\n#undef INSTANTIATE_LOGICAL_DERIV\n#undef INSTANTIATE_TNSR\n#undef INSTANTIATE_JAC\n#undef DTYPE\n#undef FRAME\n#undef TENSOR\n}\n\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Define Tensor type lookup tables\nimport itertools\n\nfrom spectre.DataStructures import DataVector\n\nfrom ._Pybindings import *\nfrom .Frame import Frame\n\n\ndef _dtype_to_name(dtype: type):\n    if dtype is DataVector:\n        return \"DV\"\n    elif dtype is float:\n        return \"D\"\n    else:\n        raise NotImplementedError\n\n\nclass TensorMeta:\n    def __init__(self, name: str):\n        self.name = name\n\n    def _getitem(self, dtype: type, dim: int, frame: Frame = Frame.Inertial):\n        return globals()[\n            f\"Tensor{self.name}{_dtype_to_name(dtype)}{dim}{frame.name}\"\n        ]\n\n    def __getitem__(self, key):\n        try:\n            return self._getitem(*key)\n        except TypeError:\n            return self._getitem(key)\n\n\nclass JacobianMeta(TensorMeta):\n    def __init__(self, inverse: bool):\n        self.inverse = inverse\n\n    def _getitem(self, dtype: type, dim: int, frame: Frame = Frame.Inertial):\n        return globals()[\n            f\"Jacobian{_dtype_to_name(dtype)}{dim}\"\n            + (\n                f\"{frame.name}ToElementLogical\"\n                if self.inverse\n                else f\"ElementLogicalTo{frame.name}\"\n            )\n        ]\n\n\n# Define Tensor types that aren't in 'tnsr.py'\nScalar = {DataVector: ScalarDV, float: ScalarD}\nJacobian = JacobianMeta(inverse=False)\nInverseJacobian = JacobianMeta(inverse=True)\n\n# Define a type annotation that means \"any tensor\". This should really be a\n# common superclass of all Tensor types, but we currently don't have that in C++\n# so we use a type alias to `typing.Any` as a workaround.\nfrom typing import Any\n\nTensor = Any\n"
  },
  {
    "path": "src/DataStructures/Tensor/Python/tnsr.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\"\"\"Defines the 'Tensor.tnsr' for shortcuts to Tensor classes\n\nUse this module like this:\n\n    from spectre.DataStructures.Tensor import tnsr, Frame, Scalar\n    from spectre.DataStructures import DataVector\n\n    scalar = Scalar[DataVector]()\n    vector = tnsr.I[DataVector, 3, Frame.Inertial]()\n\"\"\"\n\nfrom spectre.DataStructures.Tensor import TensorMeta\n\na = TensorMeta(\"a\")\nA = TensorMeta(\"A\")\nab = TensorMeta(\"ab\")\naB = TensorMeta(\"aB\")\nAb = TensorMeta(\"Ab\")\naa = TensorMeta(\"aa\")\nAA = TensorMeta(\"AA\")\nabb = TensorMeta(\"abb\")\nAbb = TensorMeta(\"Abb\")\naBcc = TensorMeta(\"aBcc\")\n\ni = TensorMeta(\"i\")\nI = TensorMeta(\"I\")\nij = TensorMeta(\"ij\")\niJ = TensorMeta(\"iJ\")\nIj = TensorMeta(\"Ij\")\nii = TensorMeta(\"ii\")\nII = TensorMeta(\"II\")\nijj = TensorMeta(\"ijj\")\niJJ = TensorMeta(\"iJJ\")\nIjj = TensorMeta(\"Ijj\")\niJkk = TensorMeta(\"iJkk\")\n\niaa = TensorMeta(\"iaa\")\nijaa = TensorMeta(\"ijaa\")\n"
  },
  {
    "path": "src/DataStructures/Tensor/Slice.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/range/combine.hpp>\n#include <boost/tuple/tuple.hpp>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// @{\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Slices the data within `volume_tensor` to a codimension 1 slice. The\n * slice has a constant logical coordinate in direction `sliced_dim`,\n * slicing the volume at `fixed_index` in that dimension.  For\n * example, to get the lower boundary of `sliced_dim`, pass `0` for\n * `fixed_index`; to get the upper boundary, pass\n * `extents[sliced_dim] - 1`.\n *\n * \\see add_slice_to_data\n *\n * Returns Tensor class sliced to a hypersurface.\n */\ntemplate <std::size_t VolumeDim, typename VectorType, typename... Structure>\nvoid data_on_slice(\n    const gsl::not_null<Tensor<VectorType, Structure...>*> interface_tensor,\n    const Tensor<VectorType, Structure...>& volume_tensor,\n    const Index<VolumeDim>& element_extents, const size_t sliced_dim,\n    const size_t fixed_index) {\n  const size_t interface_grid_points =\n      element_extents.slice_away(sliced_dim).product();\n  if (interface_tensor->begin()->size() != interface_grid_points) {\n    *interface_tensor = Tensor<VectorType, Structure...>(interface_grid_points);\n  }\n\n  for (SliceIterator si(element_extents, sliced_dim, fixed_index); si; ++si) {\n    for (decltype(auto) interface_and_volume_tensor_components :\n         boost::combine(*interface_tensor, volume_tensor)) {\n      boost::get<0>(interface_and_volume_tensor_components)[si.slice_offset()] =\n          boost::get<1>(\n              interface_and_volume_tensor_components)[si.volume_offset()];\n    }\n  }\n}\n\ntemplate <std::size_t VolumeDim, typename VectorType, typename... Structure>\nTensor<VectorType, Structure...> data_on_slice(\n    const Tensor<VectorType, Structure...>& volume_tensor,\n    const Index<VolumeDim>& element_extents, const size_t sliced_dim,\n    const size_t fixed_index) {\n  Tensor<VectorType, Structure...> interface_tensor(\n      element_extents.slice_away(sliced_dim).product());\n  data_on_slice(make_not_null(&interface_tensor), volume_tensor,\n                element_extents, sliced_dim, fixed_index);\n  return interface_tensor;\n}\n\ntemplate <std::size_t VolumeDim, typename VectorType, typename... Structure>\nvoid data_on_slice(\n    const gsl::not_null<std::optional<Tensor<VectorType, Structure...>>*>\n        interface_tensor,\n    const std::optional<Tensor<VectorType, Structure...>>& volume_tensor,\n    const Index<VolumeDim>& element_extents, const size_t sliced_dim,\n    const size_t fixed_index) {\n  if (volume_tensor.has_value()) {\n    if (not(*interface_tensor)) {\n      *interface_tensor = Tensor<VectorType, Structure...>{\n          element_extents.slice_away(sliced_dim).product()};\n    }\n    data_on_slice(make_not_null(&**interface_tensor), *volume_tensor,\n                  element_extents, sliced_dim, fixed_index);\n  } else {\n    *interface_tensor = std::nullopt;\n  }\n}\n\ntemplate <std::size_t VolumeDim, typename VectorType, typename... Structure>\nstd::optional<Tensor<VectorType, Structure...>> data_on_slice(\n    const std::optional<Tensor<VectorType, Structure...>>& volume_tensor,\n    const Index<VolumeDim>& element_extents, const size_t sliced_dim,\n    const size_t fixed_index) {\n  if (volume_tensor.has_value()) {\n    Tensor<VectorType, Structure...> interface_tensor(\n        element_extents.slice_away(sliced_dim).product());\n    data_on_slice(make_not_null(&interface_tensor), *volume_tensor,\n                  element_extents, sliced_dim, fixed_index);\n    return interface_tensor;\n  }\n  return std::nullopt;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Tensor/Structure.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class TensorStructure<Symmetry, Indices...>\n\n#pragma once\n\n#include <array>\n#include <limits>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tensor_detail {\ntemplate <size_t Size>\nconstexpr size_t number_of_independent_components(\n    const std::array<int, Size>& symm, const std::array<size_t, Size>& dims) {\n  if constexpr (Size == 0) {\n    (void)symm;\n    (void)dims;\n\n    return 1;\n  } else if constexpr (Size == 1) {\n    (void)symm;\n\n    return dims[0];\n  } else {\n    size_t max_element = 0;\n    for (size_t i = 0; i < Size; ++i) {\n      // clang-tidy: internals of assert(), don't need gsl::at in constexpr\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay,cppcoreguidelines-pro-bounds-constant-array-index)\n      assert(symm[i] > 0);\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)\n      max_element = std::max(static_cast<size_t>(ce_abs(symm[i])), max_element);\n    }\n    assert(max_element > 0);  // NOLINT\n    size_t total_independent_components = 1;\n    for (size_t symm_index = 1; symm_index <= max_element; ++symm_index) {\n      size_t number_of_indices_with_symm = 0;\n      size_t dim_of_index = 0;\n      for (size_t i = 0; i < Size; ++i) {\n        if (static_cast<size_t>(symm[i]) == symm_index) {  // NOLINT\n          ++number_of_indices_with_symm;\n          dim_of_index = dims[i];  // NOLINT\n        }\n      }\n      assert(dim_of_index > 0);                 // NOLINT\n      assert(number_of_indices_with_symm > 0);  // NOLINT\n      if (dim_of_index - 1 > number_of_indices_with_symm) {\n        total_independent_components *=\n            falling_factorial(dim_of_index + number_of_indices_with_symm - 1,\n                              number_of_indices_with_symm) /\n            factorial(number_of_indices_with_symm);\n      } else {\n        total_independent_components *=\n            falling_factorial(dim_of_index + number_of_indices_with_symm - 1,\n                              dim_of_index - 1) /\n            factorial(dim_of_index - 1);\n      }\n    }\n    return total_independent_components;\n  }\n}\n\ntemplate <size_t Size>\nconstexpr size_t number_of_components(const std::array<size_t, Size>& dims) {\n  size_t number = 1;\n  for (size_t i = 0; i < Size; ++i) {\n    // clang-tidy: use gsl::at\n    number *= dims[i];  // NOLINT\n  }\n  return number;\n}\n\ntemplate <typename T, typename S, size_t Size>\nconstexpr void increment_tensor_index(cpp20::array<T, Size>& tensor_index,\n                                      const cpp20::array<S, Size>& dims) {\n  for (size_t i = 0; i < Size; ++i) {\n    if (++tensor_index[i] < static_cast<T>(dims[i])) {\n      return;\n    }\n    tensor_index[i] = 0;\n  }\n}\n\n// index_to_swap_with takes the last two arguments as opposed to just one of\n// them so that when the max constexpr steps is reached on clang it is reached\n// in this function rather than in array.\ntemplate <size_t Rank>\nconstexpr size_t index_to_swap_with(\n    const cpp20::array<size_t, Rank>& tensor_index,\n    const cpp20::array<int, Rank>& sym, size_t index_to_swap_with,\n    const size_t current_index) {\n  // If you encounter infinite loop compilation errors here you are\n  // constructing very large Tensor's. If you are sure Tensor is\n  // the correct data structure you can extend the compiler limit\n  // by passing the flag -fconstexpr-steps=<SOME LARGER VALUE>\n  while (index_to_swap_with < Rank) {  // See comment on line above for fix\n    if (tensor_index[current_index] > tensor_index[index_to_swap_with] and\n        sym[current_index] == sym[index_to_swap_with]) {\n      return index_to_swap_with;\n    }\n    index_to_swap_with--;\n  }\n  return current_index;\n}\n\n// \\brief Reorders a tensor multi-index to a canonical form based on its\n// symmetries\n//\n// \\details Reorders the values of the symmetric indices of a multi-index such\n// that each symmetric subset has values descending from left to right in the\n// multi-index. For example, if `tensor_index` is `[1, 2, 3, 4]` and `symm` is\n// `[2, 2, 1, 1]`, the returned canonical multi-index is `[2, 1, 4, 3]`.\n//\n// \\param tensor_index the multi-index to canonicalize\n// \\tparam symm the canonical symmetry of the tensor\n// (see `Symmetry` for the form of canonical symmetries)\n// \\return the reordered, canonical form of `tensor_index`\ntemplate <size_t Rank>\nconstexpr cpp20::array<size_t, Rank> canonicalize_tensor_index(\n    cpp20::array<size_t, Rank> tensor_index,\n    const cpp20::array<int, Rank>& symm) {\n  for (size_t i = 1; i < Rank; ++i) {\n    for (size_t j = i; j < Rank; --j) {\n      const size_t temp = tensor_index[j];\n      const size_t swap = index_to_swap_with(tensor_index, symm, j, j);\n      tensor_index[j] = tensor_index[swap];\n      tensor_index[swap] = temp;\n    }\n  }\n  return tensor_index;\n}\n\n// \\brief Reorders a tensor multi-index to a canonical form based on its\n// symmetries\n//\n// \\details See other overload for detaiils\n//\n// \\tparam Symm the symmetry of the tensor\n// \\param tensor_index the multi-index to canonicalize\n// \\return the reordered, canonical form of `tensor_index`\ntemplate <typename Symm, size_t Rank>\nconstexpr cpp20::array<size_t, Rank> canonicalize_tensor_index(\n    cpp20::array<size_t, Rank> tensor_index) {\n  static_assert(tmpl::size<Symm>::value == Rank,\n                \"Symm and tensor_index have different ranks\");\n\n  if constexpr (Rank < 2) {\n    return tensor_index;\n  } else {\n    constexpr auto symm = make_cpp20_array_from_list<Symm>();\n    constexpr auto max_symm_value = *alg::max_element(symm);\n    static_assert(*alg::min_element(symm) > 0,\n                  \"canonicalize_tensor_index assumes symmetry values are > 0\");\n    static_assert(\n        max_symm_value <= Rank,\n        \"canonicalize_tensor_index assumes symmetry values are <= Rank\");\n\n    if constexpr (max_symm_value == Rank) {\n      return tensor_index;\n    } else {\n      return canonicalize_tensor_index(tensor_index, symm);\n    }\n  }\n}\n\ntemplate <size_t Rank>\nconstexpr size_t compute_collapsed_index(\n    const cpp20::array<size_t, Rank>& tensor_index,\n    const cpp20::array<size_t, Rank> dims) {\n  size_t collapsed_index = 0;\n  for (size_t i = Rank - 1; i < Rank; --i) {\n    collapsed_index = tensor_index[i] + dims[i] * collapsed_index;\n  }\n  return collapsed_index;\n}\n\n/// \\brief Computes a mapping from a collapsed_index to its storage_index\n///\n/// \\details\n/// Because each collapsed_index corresponds to a unique tensor_index, this map\n/// also effectively relates each unique tensor_index to its storage_index.\n/// While each index of the returned map corresponds to a unique tensor_index,\n/// the element stored at each index is a storage_index that may or may not be\n/// unique. If symmetries are present, this map will not be 1-1, as\n/// collapsed_indices that correspond to tensor_indices with the same canonical\n/// form will map to the same storage_index. Provided that any tensor_index is\n/// first converted to its corresponding collapsed_index, this map can be used\n/// to retrieve the storage_index of that tensor_index, canonicalized or not.\n///\n/// \\tparam Symm the Symmetry of the tensor\n/// \\tparam NumberOfComponents the total number of components in the tensor\n/// \\param index_dimensions the dimensions of the tensor's indices\n/// \\return a mapping from a collapsed_index to its storage_index\ntemplate <typename Symm, size_t NumberOfComponents>\nconstexpr auto compute_collapsed_to_storage(\n    const cpp20::array<size_t, tmpl::size<Symm>::value>& index_dimensions) {\n  if constexpr (tmpl::size<Symm>::value != 0) {\n    constexpr size_t rank = tmpl::size<Symm>::value;\n    constexpr auto symm = make_cpp20_array_from_list<Symm>();\n    constexpr std::int32_t max_symm_value = *alg::max_element(symm);\n    static_assert(\n        *alg::min_element(symm) > 0,\n        \"compute_collapsed_to_storage assumes symmetry values are > 0\");\n    static_assert(\n        max_symm_value <= rank,\n        \"compute_collapsed_to_storage assumes symmetry values are <= rank\");\n\n    if constexpr (max_symm_value == rank) {\n      const size_t first_storage_index{0};\n      cpp20::array<size_t, NumberOfComponents> collapsed_to_storage{};\n      return alg::iota(collapsed_to_storage, first_storage_index);\n    } else {\n      cpp20::array<size_t, NumberOfComponents> collapsed_to_storage{};\n      auto tensor_index = convert_to_cpp20_array(\n          make_array<tmpl::size<Symm>::value>(size_t{0}));\n      size_t count{0};\n      for (auto& current_storage_index : collapsed_to_storage) {\n        // Compute canonical tensor_index, which, for symmetric get_tensor_index\n        // is in decreasing numerical order, e.g. (3,2) rather than (2,3).\n        const auto canonical_tensor_index =\n            canonicalize_tensor_index<Symm>(tensor_index);\n        // If the tensor_index was already in the canonical form, then it must\n        // be a new unique entry  and we add it to collapsed_to_storage_ as a\n        // new integer, thus increasing the size_. Else, the StorageIndex has\n        // already been determined so we look it up in the existing\n        // collapsed_to_storage table.\n        if (tensor_index == canonical_tensor_index) {\n          current_storage_index = count;\n          ++count;\n        } else {\n          current_storage_index = collapsed_to_storage[compute_collapsed_index(\n              canonical_tensor_index, index_dimensions)];\n        }\n        // Move to the next tensor_index.\n        increment_tensor_index(tensor_index, index_dimensions);\n      }\n      return collapsed_to_storage;\n    }\n  } else {\n    (void)index_dimensions;\n\n    return cpp20::array<size_t, 1>{{0}};\n  }\n}\n\n/// \\brief Computes a 1-1 mapping from a storage_index to its canonical\n/// tensor_index\n///\n/// \\details\n/// When symmetries are present, not all unique tensor_indices can be retrieved\n/// from this map, as some tensor_indices will share the same canonical form.\n/// Otherwise, if no symmetries are present, each unique tensor_index is already\n/// in the canonical form, and one that is not shared by another tensor_index,\n/// so this would equivalently mean a 1-1 mapping from a storage_index to a\n/// tensor_index. This means that when no symmetries are present, all unique\n/// tensor_indices of a tensor can be retrieved from this map.\n///\n/// \\tparam Symm the Symmetry of the tensor\n/// \\tparam NumIndComps the number of independent components in the tensor, i.e.\n/// components equivalent due to symmetry counted only once\n/// \\tparam NumComps the total number of components in the tensor\n/// \\param collapsed_to_storage a mapping from a collapsed_index to its\n/// storage_index, which is only 1-1 if there are no symmetries\n/// \\param index_dimensions the dimensions of the tensor's indices\n/// \\return a 1-1 mapping from a storage_index to its canonical tensor_index\ntemplate <typename Symm, size_t NumIndComps, size_t NumComps>\nconstexpr auto compute_storage_to_tensor(\n    const cpp20::array<size_t, NumComps>& collapsed_to_storage,\n    const cpp20::array<size_t, tmpl::size<Symm>::value>& index_dimensions) {\n  if constexpr (tmpl::size<Symm>::value != 0) {\n    constexpr size_t rank = tmpl::size<Symm>::value;\n    cpp20::array<cpp20::array<size_t, rank>, NumIndComps> storage_to_tensor{};\n    cpp20::array<size_t, rank> tensor_index =\n        convert_to_cpp20_array(make_array<rank>(size_t{0}));\n    for (const auto& current_storage_index : collapsed_to_storage) {\n      storage_to_tensor[current_storage_index] =\n          canonicalize_tensor_index<Symm>(tensor_index);\n      increment_tensor_index(tensor_index, index_dimensions);\n    }\n    return storage_to_tensor;\n  } else {\n    (void)collapsed_to_storage;\n    (void)index_dimensions;\n\n    return cpp20::array<cpp20::array<size_t, 1>, 1>{\n        {cpp20::array<size_t, 1>{{0}}}};\n  }\n}\n\ntemplate <size_t NumIndComps, typename T, size_t NumComps>\nconstexpr cpp20::array<size_t, NumIndComps> compute_multiplicity(\n    const cpp20::array<T, NumComps>& collapsed_to_storage) {\n  cpp20::array<size_t, NumIndComps> multiplicity =\n      convert_to_cpp20_array(make_array<NumIndComps>(size_t{0}));\n  for (const auto& current_storage_index : collapsed_to_storage) {\n    ++multiplicity[current_storage_index];\n  }\n  return multiplicity;\n}\n\ntemplate <size_t NumIndices>\nstruct ComponentNameImpl {\n  template <typename Structure, typename T>\n  static std::string apply(\n      const std::array<T, NumIndices>& tensor_index,\n      const std::array<std::string, NumIndices>& axis_labels) {\n    const size_t storage_index = Structure::get_storage_index(tensor_index);\n    std::array<std::string, Structure::rank()> labels = axis_labels;\n    constexpr auto index_dim = Structure::dims();\n    for (size_t i = 0; i < Structure::rank(); ++i) {\n      if (gsl::at(labels, i).length() == 0) {\n        if (gsl::at(Structure::index_types(), i) == IndexType::Spacetime) {\n          switch (gsl::at(index_dim, i)) {\n            case 2:\n              gsl::at(labels, i) = \"tx\";\n              break;\n            case 3:\n              gsl::at(labels, i) = \"txy\";\n              break;\n            case 4:\n              gsl::at(labels, i) = \"txyz\";\n              break;\n            default:\n              ERROR(\"Tensor dim[\"\n                    << i\n                    << \"] must be 1,2,3, or 4 for default axis_labels. \"\n                       \"Either pass a string or extend the function.\");\n          }\n        } else {\n          switch (gsl::at(index_dim, i)) {\n            case 1:\n              gsl::at(labels, i) = \"x\";\n              break;\n            case 2:\n              gsl::at(labels, i) = \"xy\";\n              break;\n            case 3:\n              gsl::at(labels, i) = \"xyz\";\n              break;\n            default:\n              ERROR(\"Tensor dim[\"\n                    << i\n                    << \"] must be 1,2, or 3 for default axis_labels. \"\n                       \"Either pass a string or extend the function.\");\n          }\n        }\n      } else {\n        if (gsl::at(axis_labels, i).length() != gsl::at(index_dim, i)) {\n          ERROR(\"Dimension mismatch: Tensor has dim = \"\n                << gsl::at(index_dim, i) << \", but you specified \"\n                << gsl::at(axis_labels, i).length() << \" different labels in \"\n                << gsl::at(axis_labels, i));\n        }\n      }\n    }\n    // Create string labeling get_tensor_index\n    std::stringstream ss;\n    const auto canonical_tensor_index =\n        Structure::get_canonical_tensor_index(storage_index);\n    for (size_t r = 0; r < Structure::rank(); ++r) {\n      ss << gsl::at(labels, r)[gsl::at(canonical_tensor_index, r)];\n    }\n    return ss.str();\n  }\n};\n\ntemplate <>\nstruct ComponentNameImpl<0> {\n  template <typename Structure, typename T>\n  static std::string apply(const std::array<T, 0>& /*tensor_index*/,\n                           const std::array<std::string, 0>& /*axis_labels*/) {\n    return \"Scalar\";\n  }\n};\n\n/// \\ingroup TensorGroup\n/// A lookup table between each tensor_index and storage_index\n///\n/// 1. tensor_index: (a, b, c,...). There are Dim^rank tensor_index's\n/// 2. collapsed_index: a + Dim * (b + Dim * (c + ...)), there are Dim^rank\n///                     unique collapsed indices and there is a 1-1 map between\n///                     a tensor_index and a collapsed_index.\n/// 3. storage_index: index into the storage vector of the Tensor. This depends\n///                   on symmetries of the tensor, rank, and dimensionality. If\n///                   the Tensor has symmetries, tensor_indices that are\n///                   equivalent due to symmetry will have the same\n///                   storage_index and canonical form. This means that the\n///                   mapping between tensor_indices and storage_indices is 1-1\n///                   only if no symmetries are present, but there is a 1-1\n///                   mapping between canonical tensor_indices and\n///                   storage_indices, regardless of symmetry.\n/// \\tparam Symm the symmetry of the Tensor\n/// \\tparam Indices list of tensor_index's giving the dimensionality and frame\n/// of the index\ntemplate <typename Symm, typename... Indices>\nstruct Structure {\n  static_assert(\n      TensorMetafunctions::check_index_symmetry_v<Symm, Indices...>,\n      \"Cannot construct a Tensor with a symmetric pair that are not the same.\");\n  static_assert(tmpl::size<Symm>::value == sizeof...(Indices),\n                \"The number of indices in Symmetry do not match the number of \"\n                \"indices given to the Structure.\");\n  static_assert(\n      tmpl2::flat_all_v<tt::is_tensor_index_type<Indices>::value...>,\n      \"All Indices passed to Structure must be of type TensorIndexType.\");\n\n  using index_list = tmpl::list<Indices...>;\n  using symmetry = Symm;\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t rank() {\n    return sizeof...(Indices);\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t size() {\n    constexpr auto number_of_independent_components =\n        ::Tensor_detail::number_of_independent_components(\n            make_array_from_list<\n                tmpl::conditional_t<sizeof...(Indices) != 0, Symm, int>>(),\n            make_array_from_list<tmpl::conditional_t<sizeof...(Indices) != 0,\n                                                     index_list, size_t>>());\n    return number_of_independent_components;\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t number_of_components() {\n    constexpr auto number_of_components = ::Tensor_detail::number_of_components(\n        make_array_from_list<tmpl::conditional_t<sizeof...(Indices) != 0,\n                                                 index_list, size_t>>());\n    return number_of_components;\n  }\n\n  /// A mapping between each collapsed_index and its storage_index. See\n  /// compute_collapsed_to_storage for details.\n  static constexpr auto collapsed_to_storage_ =\n      compute_collapsed_to_storage<Symm, number_of_components()>(\n          make_cpp20_array_from_list<tmpl::conditional_t<\n              sizeof...(Indices) == 0, size_t, index_list>>());\n  /// A 1-1 mapping between each storage_index and its canonical tensor_index.\n  /// See compute_storage_to_tensor for details.\n  static constexpr auto storage_to_tensor_ = compute_storage_to_tensor<Symm,\n                                                                       size()>(\n      collapsed_to_storage_,\n      make_cpp20_array_from_list<\n          tmpl::conditional_t<sizeof...(Indices) == 0, size_t, index_list>>());\n  static constexpr auto multiplicity_ =\n      compute_multiplicity<size()>(collapsed_to_storage_);\n\n  // Retrieves the dimensionality of the I'th index\n  template <int I>\n  SPECTRE_ALWAYS_INLINE static constexpr size_t dim() {\n    static_assert(sizeof...(Indices),\n                  \"A scalar does not have any indices from which you can \"\n                  \"retrieve the dimensionality.\");\n    return tmpl::at<index_list, tmpl::int32_t<I>>::value;\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t, sizeof...(Indices)>\n  dims() {\n    constexpr auto dims = make_array_from_list<\n        tmpl::conditional_t<sizeof...(Indices) != 0, index_list, size_t>>();\n    return dims;\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<int, sizeof...(Indices)>\n  symmetries() {\n    return make_array_from_list<\n        tmpl::conditional_t<0 != sizeof...(Indices), Symm, int>>();\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<IndexType,\n                                                    sizeof...(Indices)>\n  index_types() {\n    return std::array<IndexType, sizeof...(Indices)>{{Indices::index_type...}};\n  }\n\n  /// Return array of the valence of each index\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<UpLo, sizeof...(Indices)>\n  index_valences() {\n    return std::array<UpLo, sizeof...(Indices)>{{Indices::ul...}};\n  }\n\n  /// Return array of the frame of each index\n  SPECTRE_ALWAYS_INLINE static constexpr auto index_frames() {\n    return std::tuple<typename Indices::Frame...>{};\n  }\n\n  /// \\brief Get the canonical tensor_index array of a storage_index\n  ///\n  /// \\details\n  /// For a symmetric tensor \\f$T_{(ab)}\\f$ with an associated symmetry list\n  /// `Symmetry<1, 1>`, this will return, e.g. `{{3, 2}}` rather than `{{2, 3}}`\n  /// for that particular index. Note that the canonical ordering is\n  /// implementation-defined.\n  ///\n  /// As `storage_to_tensor_` is a computed 1-1 mapping between a storage_index\n  /// and canonical tensor_index, we simply retrieve the canonical tensor_index\n  /// from this map.\n  ///\n  /// \\param storage_index the storage_index of which to get the canonical\n  /// tensor_index\n  /// \\return the canonical tensor_index array of a storage_index\n  template <size_t Rank = sizeof...(Indices)>\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t, Rank>\n  get_canonical_tensor_index(const size_t storage_index) {\n    if constexpr (Rank != 0) {\n      constexpr auto storage_to_tensor = storage_to_tensor_;\n      return gsl::at(storage_to_tensor, storage_index);\n    } else {\n      (void)storage_index;\n\n      return std::array<size_t, 0>{};\n    }\n  }\n\n  /// \\brief Get the storage_index of a tensor_index\n  ///\n  /// \\details\n  /// This first computes the collapsed_index of the given tensor_index (this is\n  /// a 1-1 mapping), then retrieves the storage_index from\n  /// collapsed_to_storage_.\n  ///\n  /// \\param args comma separated list of the tensor_index of which to get the\n  /// storage_index\n  /// \\return the storage_index of a tensor_index\n  template <typename... N>\n  SPECTRE_ALWAYS_INLINE static constexpr std::size_t get_storage_index(\n      const N... args) {\n    static_assert(sizeof...(Indices) == sizeof...(N),\n                  \"the number arguments must be equal to rank_\");\n    constexpr auto collapsed_to_storage = collapsed_to_storage_;\n    return gsl::at(\n        collapsed_to_storage,\n        compute_collapsed_index(\n            cpp20::array<size_t, sizeof...(N)>{{static_cast<size_t>(args)...}},\n            make_cpp20_array_from_list<tmpl::conditional_t<\n                0 != sizeof...(Indices), index_list, size_t>>()));\n  }\n\n  /// \\brief Get the storage_index of a tensor_index\n  ///\n  /// \\details\n  /// This first computes the collapsed_index of the given tensor_index (this is\n  /// a 1-1 mapping), then retrieves the storage_index from\n  /// collapsed_to_storage_.\n  ///\n  /// \\param tensor_index the tensor_index of which to get the storage_index\n  /// \\return the storage_index of a tensor_index\n  template <typename I>\n  SPECTRE_ALWAYS_INLINE static constexpr std::size_t get_storage_index(\n      const std::array<I, sizeof...(Indices)>& tensor_index) {\n    constexpr auto collapsed_to_storage = collapsed_to_storage_;\n    return gsl::at(collapsed_to_storage,\n                   compute_collapsed_index(\n                       convert_to_cpp20_array(tensor_index),\n                       make_cpp20_array_from_list<tmpl::conditional_t<\n                           0 != sizeof...(Indices), index_list, size_t>>()));\n  }\n\n  /// \\brief Get the storage_index of a tensor_index\n  ///\n  /// \\details\n  /// This first computes the collapsed_index of the given tensor_index (this is\n  /// a 1-1 mapping), then retrieves the storage_index from\n  /// collapsed_to_storage_.\n  ///\n  /// \\tparam N the comma separated list of the tensor_index of which to get the\n  /// storage_index\n  /// \\return the storage_index of a tensor_index\n  template <int... N, Requires<(sizeof...(N) > 0)> = nullptr>\n  SPECTRE_ALWAYS_INLINE static constexpr std::size_t get_storage_index() {\n    static_assert(sizeof...(Indices) == sizeof...(N),\n                  \"the number arguments must be equal to rank_\");\n    constexpr std::size_t storage_index =\n        collapsed_to_storage_[compute_collapsed_index(\n            cpp20::array<size_t, sizeof...(N)>{{N...}},\n            make_cpp20_array_from_list<index_list>())];\n    return storage_index;\n  }\n\n  /// Get the multiplicity of the storage_index\n  /// \\param storage_index the storage_index of which to get the multiplicity\n  SPECTRE_ALWAYS_INLINE static constexpr size_t multiplicity(\n      const size_t storage_index) {\n    constexpr auto multiplicity = multiplicity_;\n    return gsl::at(multiplicity, storage_index);\n  }\n\n  /// Get the array of collapsed index to storage_index\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t,\n                                                    number_of_components()>\n  collapsed_to_storage() {\n    constexpr auto collapsed_to_storage = collapsed_to_storage_;\n    return collapsed_to_storage;\n  }\n\n  /// Get the storage_index for the specified collapsed index\n  SPECTRE_ALWAYS_INLINE static constexpr int collapsed_to_storage(\n      const size_t i) {\n    constexpr auto collapsed_to_storage = collapsed_to_storage_;\n    return gsl::at(collapsed_to_storage, i);\n  }\n\n  /// Get the array of tensor_index's corresponding to the storage_index's.\n  SPECTRE_ALWAYS_INLINE static constexpr const cpp20::array<\n      cpp20::array<size_t, sizeof...(Indices) == 0 ? 1 : sizeof...(Indices)>,\n      size()>\n  storage_to_tensor_index() {\n    constexpr auto storage_to_tensor = storage_to_tensor_;\n    return storage_to_tensor;\n  }\n\n  template <typename T>\n  SPECTRE_ALWAYS_INLINE static std::string component_name(\n      const std::array<T, rank()>& tensor_index,\n      const std::array<std::string, rank()>& axis_labels) {\n    return ComponentNameImpl<sizeof...(Indices)>::template apply<Structure>(\n        tensor_index, axis_labels);\n  }\n};\n}  // namespace Tensor_detail\n"
  },
  {
    "path": "src/DataStructures/Tensor/Symmetry.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines metafunctions used to compute the Symmetry<...> for a Tensor\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace detail {\ntemplate <size_t Size>\nconstexpr int find_reduced_index(\n    const cpp20::array<std::pair<int, int>, Size>& t, const int value) {\n  for (size_t i = 0; i < Size; ++i) {\n    if (t[i].first == value) {\n      return t[i].second;\n    }\n  }\n  return 0;\n}\n\ntemplate <size_t Size>\nconstexpr cpp20::array<int, Size> symmetry(\n    const std::array<int, Size>& input_symm) {\n  cpp20::array<int, Size> output_symm{};\n  int next_symm_entry = 1;\n  cpp20::array<std::pair<int, int>, Size> input_to_output_map{};\n  size_t input_to_output_map_size = 0;\n  for (size_t i = Size - 1; i < Size; --i) {\n    // clang-tidy: use gsl::at\n    int found_symm_entry =\n        find_reduced_index(input_to_output_map, input_symm[i]);  // NOLINT\n    if (found_symm_entry == 0) {\n      output_symm[i] = next_symm_entry;  // NOLINT\n      input_to_output_map[input_to_output_map_size].first =\n          input_symm[i];  // NOLINT\n      input_to_output_map[input_to_output_map_size].second = output_symm[i];\n      input_to_output_map_size++;\n      next_symm_entry++;\n    } else {\n      output_symm[i] = found_symm_entry;\n    }\n  }\n  return output_symm;\n}\n\ntemplate <typename IndexSequence, typename SymmetrySequence>\nstruct SymmetryImpl;\n\ntemplate <size_t... Is, std::int32_t... Ss>\nstruct SymmetryImpl<std::index_sequence<Is...>,\n                    tmpl::integral_list<std::int32_t, Ss...>> {\n  static_assert((... and (Ss > 0)),\n                \"Symmetry values must be positive integers.\");\n  static constexpr cpp20::array<int, sizeof...(Is)> t =\n      symmetry(std::array<int, sizeof...(Is)>{{Ss...}});\n  using type = tmpl::integral_list<std::int32_t, t[Is]...>;\n};\n}  // namespace detail\n\n/// \\ingroup TensorGroup\n/// \\brief Computes the canonical symmetry from the integers `T`\n///\n/// \\details\n/// Compute the canonical symmetry typelist given a set of integers, T. The\n/// resulting typelist is in ascending order of the integers, from right to\n/// left. For example, the result of `Symmetry<1, 2, 1, 3>` is\n/// `integral_list<int32_t, 2, 3, 2, 1>`. Anti-symmetries are not currently\n/// supported.\n///\n/// \\tparam T the integers denoting the symmetry of the Tensor\ntemplate <std::int32_t... T>\nusing Symmetry = typename detail::SymmetryImpl<\n    std::make_index_sequence<sizeof...(T)>,\n    tmpl::integral_list<std::int32_t, T...>>::type;\n"
  },
  {
    "path": "src/DataStructures/Tensor/Tensor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines classes for Tensor\n\n#pragma once\n\n#include <concepts>\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"DataStructures/Tensor/Expressions/AddSubtract.hpp\"\n#include \"DataStructures/Tensor/Expressions/Contract.hpp\"\n#include \"DataStructures/Tensor/Expressions/DataTypeSupport.hpp\"\n#include \"DataStructures/Tensor/Expressions/Divide.hpp\"\n#include \"DataStructures/Tensor/Expressions/Evaluate.hpp\"\n#include \"DataStructures/Tensor/Expressions/IndexPropertyCheck.hpp\"\n#include \"DataStructures/Tensor/Expressions/LhsTensorSymmAndIndices.hpp\"\n#include \"DataStructures/Tensor/Expressions/Negate.hpp\"\n#include \"DataStructures/Tensor/Expressions/NumberAsExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/Product.hpp\"\n#include \"DataStructures/Tensor/Expressions/SpatialSpacetimeIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/SquareRoot.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorAsExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/Simd/Simd.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsStreamable.hpp\"\n// Not all the TensorExpression includes are necessary, but we include them so\n// that Tensor.hpp provides a uniform interface to Tensors and TensorExpressions\n\n/// \\cond\ntemplate <typename X, typename Symm = Symmetry<>,\n          typename IndexList = index_list<>>\nclass Tensor;\n/// \\endcond\n\nnamespace Tensor_detail {\ntemplate <typename T, typename = void>\nstruct is_tensor : std::false_type {};\ntemplate <typename X, typename Symm, typename IndexList>\nstruct is_tensor<Tensor<X, Symm, IndexList>> : std::true_type {};\n}  // namespace Tensor_detail\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Represents an object with multiple components\n *\n * \\details\n * Tensor is a container that represents indexable geometric objects. Each index\n * has its own dimension, valence, and frame and must be either spatial or\n * spacetime. Note that the dimension passed to `SpatialIndex` and\n * `SpacetimeIndex` is always the spatial dimension of the index. Tensors with\n * symmetric indices are stored only once and must be of the same\n * type. A list of available type aliases can be found in the ::tnsr namespace\n * where the adopted conventions are:\n *\n * 1. Upper case for contravariant or upper indices, lower case for covariant or\n * lower indices.\n *\n * 2. `a, b, c, d` are used for spacetime indices while `i, j, k, l` are used\n * for spatial indices.\n *\n * 3. `::Scalar` is not inside the `::tnsr` namespace but is used to represent\n * a scalar with no indices.\n *\n * \\example\n * \\snippet Test_Tensor.cpp scalar\n * \\snippet Test_Tensor.cpp spatial_vector\n * \\snippet Test_Tensor.cpp spacetime_vector\n * \\snippet Test_Tensor.cpp rank_3_122\n *\n * \\tparam X the type held\n * \\tparam Symm the ::Symmetry of the indices\n * \\tparam IndexList a typelist of \\ref SpacetimeIndex \"TensorIndexType\"'s\n */\ntemplate <typename X, typename Symm, template <typename...> class IndexList,\n          typename... Indices>\nclass Tensor<X, Symm, IndexList<Indices...>> {\n  static_assert(sizeof...(Indices) < 5,\n                \"If you are sure you need rank 5 or higher Tensor's please \"\n                \"file an issue on GitHub or discuss with a core developer of \"\n                \"SpECTRE.\");\n public:\n  /// The type of the sequence that holds the data\n  using storage_type =\n      std::array<X, Tensor_detail::Structure<Symm, Indices...>::size()>;\n  /// The type that is stored by the Tensor\n  using type = X;\n  /// Typelist of the symmetry of the Tensor\n  ///\n  /// \\details\n  /// For a rank-3 tensor symmetric in the last two indices,\n  /// \\f$T_{a(bc)}\\f$, the ::Symmetry is `<2, 1, 1>`. For a non-symmetric rank-2\n  /// tensor the ::Symmetry is `<2, 1>`.\n  using symmetry = Symm;\n  /// Typelist of the \\ref SpacetimeIndex \"TensorIndexType\"'s that the\n  /// Tensor has\n  using index_list = tmpl::list<Indices...>;\n  /// The number of indices that the Tensor has\n  static constexpr size_t num_tensor_indices = sizeof...(Indices);\n  /// The Tensor_detail::Structure for the particular tensor index structure\n  ///\n  /// Each tensor index structure, e.g. \\f$T_{ab}\\f$, \\f$T_a{}^b\\f$ or\n  /// \\f$T^{ab}\\f$ has its own Tensor_detail::TensorStructure that holds\n  /// information about how the data is stored, what the multiplicity of the\n  /// stored indices are, the number of (independent) components, etc.\n  using structure = Tensor_detail::Structure<Symm, Indices...>;\n  /// The type of the TensorExpression that would represent this Tensor in a\n  /// tensor expression.\n  template <typename ArgsList>\n  using TE = tenex::TensorAsExpression<Tensor<X, Symm, IndexList<Indices...>>,\n                                       ArgsList>;\n\n  Tensor() = default;\n  ~Tensor() = default;\n  Tensor(const Tensor&) = default;\n  Tensor(Tensor&&) = default;\n  Tensor& operator=(const Tensor&) = default;\n  Tensor& operator=(Tensor&&) = default;\n\n  /// Initialize a vector or scalar from an array\n  ///\n  /// \\example\n  /// \\snippet Test_Tensor.cpp init_vector\n  /// \\param data the values of the individual components of the Vector\n  explicit constexpr Tensor(storage_type data)\n    requires(sizeof...(Indices) <= 1);\n\n  /// Constructor that passes \"args\" to constructor of X and initializes each\n  /// component to be the same\n  template <typename Arg0, typename... Args>\n  explicit constexpr Tensor(Arg0&& arg0, Args&&... args)\n      // NOLINTNEXTLINE(readability-simplify-boolean-expr)\n    requires(not(sizeof...(Args) == 0 and\n                 std::same_as<std::decay_t<Arg0>, Tensor>) and\n             std::constructible_from<X, Arg0, Args...>);\n\n  using value_type = typename storage_type::value_type;\n  using reference = typename storage_type::reference;\n  using const_reference = typename storage_type::const_reference;\n  using iterator = typename storage_type::iterator;\n  using const_iterator = typename storage_type::const_iterator;\n  using pointer = typename storage_type::pointer;\n  using const_pointer = typename storage_type::const_pointer;\n  using reverse_iterator = typename storage_type::reverse_iterator;\n  using const_reverse_iterator = typename storage_type::const_reverse_iterator;\n\n  constexpr iterator begin() { return data_.begin(); }\n  constexpr const_iterator begin() const { return data_.begin(); }\n  constexpr const_iterator cbegin() const { return data_.begin(); }\n\n  constexpr iterator end() { return data_.end(); }\n  constexpr const_iterator end() const { return data_.end(); }\n  constexpr const_iterator cend() const { return data_.end(); }\n\n  constexpr reverse_iterator rbegin() { return data_.rbegin(); }\n  constexpr const_reverse_iterator rbegin() const { return data_.rbegin(); }\n  constexpr const_reverse_iterator crbegin() const { return data_.rbegin(); }\n\n  constexpr reverse_iterator rend() { return data_.rend(); }\n  constexpr const_reverse_iterator rend() const { return data_.rend(); }\n  constexpr const_reverse_iterator crend() const { return data_.rend(); }\n\n  /// @{\n  /// Get data entry using an array representing a tensor index\n  ///\n  /// \\details\n  /// Let \\f$T_{abc}\\f$ be a Tensor.\n  /// Then `get({{0, 2, 1}})` returns the \\f$T_{0 2 1}\\f$ component.\n  /// \\param tensor_index the index at which to get the data\n  template <typename T>\n  SPECTRE_ALWAYS_INLINE constexpr reference get(\n      const std::array<T, sizeof...(Indices)>& tensor_index) {\n    return gsl::at(data_, structure::get_storage_index(tensor_index));\n  }\n  template <typename T>\n  SPECTRE_ALWAYS_INLINE constexpr const_reference get(\n      const std::array<T, sizeof...(Indices)>& tensor_index) const {\n    return gsl::at(data_, structure::get_storage_index(tensor_index));\n  }\n  /// @}\n  /// @{\n  /// Get data entry using a list of integers representing a tensor index\n  ///\n  /// \\details\n  /// Let \\f$T_{abc}\\f$ be a Tensor.\n  /// Then `get(0, 2, 1)` returns the \\f$T_{0 2 1}\\f$ component.\n  /// \\param n the index at which to get the data\n  template <typename... N>\n  constexpr reference get(N... n) {\n    static_assert(\n        sizeof...(Indices) == sizeof...(N),\n        \"the number of tensor indices specified must match the rank of \"\n        \"the tensor\");\n    return gsl::at(data_, structure::get_storage_index(n...));\n  }\n  template <typename... N>\n  constexpr const_reference get(N... n) const {\n    static_assert(\n        sizeof...(Indices) == sizeof...(N),\n        \"the number of tensor indices specified must match the rank of \"\n        \"the tensor\");\n    return gsl::at(data_, structure::get_storage_index(n...));\n  }\n  /// @}\n\n  /// @{\n  /// Retrieve the index `N...` by computing the storage index at compile time\n  // clang-tidy: redundant declaration (bug in clang-tidy)\n  template <int... N, typename... Args>\n  friend SPECTRE_ALWAYS_INLINE constexpr typename Tensor<Args...>::reference\n  get(Tensor<Args...>& t);  // NOLINT\n  // clang-tidy: redundant declaration (bug in clang-tidy)\n  template <int... N, typename... Args>\n  friend SPECTRE_ALWAYS_INLINE constexpr\n      typename Tensor<Args...>::const_reference\n      get(const Tensor<Args...>& t);  // NOLINT\n                                      /// @}\n\n  /// @{\n  /// Retrieve a TensorExpression object with the index structure passed in\n  template <typename... TensorIndices>\n  SPECTRE_ALWAYS_INLINE constexpr auto operator()(\n      TensorIndices... /*meta*/) const {\n    static_assert((... and tt::is_tensor_index<TensorIndices>::value),\n                  \"The tensor expression must be created using TensorIndex \"\n                  \"objects to represent generic indices, e.g. ti::a, ti::b, \"\n                  \"etc.\");\n    static_assert(\n        tenex::tensorindex_list_is_valid<tmpl::list<TensorIndices...>>::value,\n        \"Cannot create a tensor expression with a repeated generic index. \"\n        \"(Note that the concrete time indices, ti::T and ti::t, can be \"\n        \"repeated.) If you intend to contract, ensure that the indices to \"\n        \"contract have opposite valences.\");\n    static_assert(\n        std::is_same_v<tmpl::integral_list<UpLo, TensorIndices::valence...>,\n                       tmpl::integral_list<UpLo, Indices::ul...>>,\n        \"The valences of the generic indices in the expression do \"\n        \"not match the valences of the indices in the Tensor.\");\n    static_assert((... and (not(TensorIndices::is_spacetime and\n                                (Indices::index_type == IndexType::Spatial)))),\n                  \"Cannot use a spacetime index for a spatial index. e.g. \"\n                  \"Cannot do R(ti::a), where R's index is spatial, because \"\n                  \"ti::a denotes a generic spacetime index.\");\n    return tenex::contract(TE<tmpl::list<TensorIndices...>>{*this});\n  }\n  /// @}\n\n  /// @{\n  /// Return i'th component of storage vector\n  constexpr reference operator[](const size_t storage_index) {\n    return gsl::at(data_, storage_index);\n  }\n  constexpr const_reference operator[](const size_t storage_index) const {\n    return gsl::at(data_, storage_index);\n  }\n  /// @}\n\n  /// Return the number of independent components of the Tensor\n  ///\n  /// \\details\n  /// Returns the number of independent components of the Tensor taking into\n  /// account symmetries. For example, let \\f$T_{ab}\\f$ be a n-dimensional\n  /// rank-2 symmetric tensor, then the number of independent components is\n  /// \\f$n(n+1)/2\\f$.\n  SPECTRE_ALWAYS_INLINE static constexpr size_t size() {\n    return structure::size();\n  }\n\n  /// Returns the rank of the Tensor\n  ///\n  /// \\details\n  /// The rank of a tensor is the number of indices it has. For example, the\n  /// tensor \\f$v^a\\f$ is rank-1, the tensor \\f$\\phi\\f$ is rank-0, and the\n  /// tensor \\f$T_{abc}\\f$ is rank-3.\n  SPECTRE_ALWAYS_INLINE static constexpr size_t rank() {\n    return sizeof...(Indices);\n  }\n\n  /// @{\n  /// Given an iterator or storage index, get the canonical tensor index.\n  /// For scalars this is defined to be std::array<int, 1>{{0}}\n  SPECTRE_ALWAYS_INLINE constexpr std::array<size_t, sizeof...(Indices)>\n  get_tensor_index(const const_iterator& iter) const {\n    return structure::get_canonical_tensor_index(\n        static_cast<size_t>(iter - begin()));\n  }\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t, sizeof...(Indices)>\n  get_tensor_index(const size_t storage_index) {\n    return structure::get_canonical_tensor_index(storage_index);\n  }\n  /// @}\n\n  /// @{\n  /// Get the storage index of the tensor index. Should only be used when\n  /// optimizing code in which computing the storage index is a bottleneck.\n  template <typename... N>\n  SPECTRE_ALWAYS_INLINE static constexpr size_t get_storage_index(\n      const N... args) {\n    return structure::get_storage_index(args...);\n  }\n  template <typename I>\n  SPECTRE_ALWAYS_INLINE static constexpr size_t get_storage_index(\n      const std::array<I, sizeof...(Indices)>& tensor_index) {\n    return structure::get_storage_index(tensor_index);\n  }\n  /// @}\n\n  /// @{\n  /// Given an iterator or storage index, get the multiplicity of an index\n  ///\n  /// \\see TensorMetafunctions::compute_multiplicity\n  SPECTRE_ALWAYS_INLINE constexpr size_t multiplicity(\n      const const_iterator& iter) const {\n    return structure::multiplicity(static_cast<size_t>(iter - begin()));\n  }\n  SPECTRE_ALWAYS_INLINE static constexpr size_t multiplicity(\n      const size_t storage_index) {\n    return structure::multiplicity(storage_index);\n  }\n  /// @}\n\n  /// @{\n  /// Get dimensionality of i'th tensor index\n  ///\n  /// \\snippet Test_Tensor.cpp index_dim\n  /// \\see ::index_dim\n  SPECTRE_ALWAYS_INLINE static constexpr size_t index_dim(const size_t i) {\n    static_assert(sizeof...(Indices),\n                  \"A scalar does not have any indices from which you can \"\n                  \"retrieve the dimensionality.\");\n    return gsl::at(structure::dims(), i);\n  }\n  /// @}\n\n  /// @{\n  /// Return an array corresponding to the ::Symmetry of the Tensor\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<int, sizeof...(Indices)>\n  symmetries() {\n    return structure::symmetries();\n  }\n  /// @}\n\n  /// @{\n  /// Return array of the ::IndexType's (spatial or spacetime)\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<IndexType,\n                                                    sizeof...(Indices)>\n  index_types() {\n    return structure::index_types();\n  }\n  /// @}\n\n  /// @{\n  /// Return array of dimensionality of each index\n  ///\n  /// \\snippet Test_Tensor.cpp index_dim\n  /// \\see index_dim ::index_dim\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<size_t, sizeof...(Indices)>\n  index_dims() {\n    return structure::dims();\n  }\n  /// @}\n\n  /// @{\n  /// Return array of the valence of each index (::UpLo)\n  SPECTRE_ALWAYS_INLINE static constexpr std::array<UpLo, sizeof...(Indices)>\n  index_valences() {\n    return structure::index_valences();\n  }\n  /// @}\n\n  /// @{\n  /// Returns std::tuple of the ::Frame of each index\n  SPECTRE_ALWAYS_INLINE static constexpr auto index_frames() {\n    return Tensor_detail::Structure<Symm, Indices...>::index_frames();\n  }\n  /// @}\n\n  /// @{\n  /// \\brief Given a tensor index, get the canonical label associated with the\n  /// canonical \\ref SpacetimeIndex \"TensorIndexType\"\n  ///\n  /// \\param tensor_index The index of the tensor component to label\n  /// \\param axis_labels The labels for the indices. Defaults to \"t\", \"x\", \"y\"\n  /// and \"z\" for spacetime indices and \"x\", \"y\" and \"z\" for spatial indices.\n  /// Note that a tensor can have indices of different types, so we specify\n  /// labels for each index individually.\n  template <typename T = int>\n  static std::string component_name(\n      const std::array<T, rank()>& tensor_index = std::array<T, rank()>{},\n      const std::array<std::string, rank()>& axis_labels =\n          make_array<rank()>(std::string(\"\"))) {\n    return structure::component_name(tensor_index, axis_labels);\n  }\n  /// @}\n\n  /// @{\n  /// \\brief Suffix to append to the tensor name that indicates the component\n  ///\n  /// The suffix is empty for scalars, otherwise it is an underscore followed by\n  /// the `Tensor::component_name` of either the `tensor_index` or the canonical\n  /// tensor index obtained from the `storage_index`. Use `axis_labels` to\n  /// overwrite the default labels for each component (see\n  /// `Tensor::component_name`).\n  ///\n  /// An example use case for the suffix is to label tensor components in\n  /// data files.\n  ///\n  /// \\see Tensor::component_name\n  template <typename IndexType = int>\n  static std::string component_suffix(\n      const std::array<IndexType, rank()>& tensor_index =\n          std::array<IndexType, rank()>{},\n      const std::array<std::string, rank()>& axis_labels =\n          make_array<rank()>(std::string(\"\"))) {\n    return rank() == 0 ? \"\" : \"_\" + component_name(tensor_index, axis_labels);\n  }\n\n  static std::string component_suffix(\n      const size_t storage_index,\n      const std::array<std::string, rank()>& axis_labels) {\n    return component_suffix(get_tensor_index(storage_index), axis_labels);\n  }\n\n  static std::string component_suffix(size_t storage_index);\n  /// @}\n\n  /// Copy tensor data into an `std::vector<X>` along with the\n  /// component names into a `std::vector<std::string>`\n  /// \\requires `std::is_same<X, DataVector>::%value` is true\n  std::pair<std::vector<std::string>, std::vector<X>> get_vector_of_data()\n      const;\n\n  /// \\cond HIDDEN_SYMBOLS\n  /// Serialization function used by Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | data_; }\n  /// \\endcond\n\n private:\n  // clang-tidy: redundant declaration\n  /// \\cond\n  template <int I, class... Ts>\n  friend SPECTRE_ALWAYS_INLINE constexpr size_t index_dim(  // NOLINT\n      const Tensor<Ts...>& /*t*/);\n  /// \\endcond\n\n  storage_type data_;\n};\n\n// ================================================================\n// Template Definitions - Variadic templates must be in header\n// ================================================================\n\ntemplate <typename X, typename Symm, template <typename...> class IndexList,\n          typename... Indices>\n// Implementation note: we explicitly prevent inlining and mark the function\n// as used so that GDB's pretty printing facilities have access to this\n// function.\n__attribute__((noinline)) __attribute__((used)) std::string\nTensor<X, Symm, IndexList<Indices...>>::component_suffix(\n    const size_t storage_index) {\n  return component_suffix(get_tensor_index(storage_index),\n                          make_array<rank()>(std::string(\"\")));\n}\n\ntemplate <typename X, typename Symm, template <typename...> class IndexList,\n          typename... Indices>\nconstexpr Tensor<X, Symm, IndexList<Indices...>>::Tensor(storage_type data)\n  requires(sizeof...(Indices) <= 1)\n    : data_(std::move(data)) {}\n\ntemplate <typename X, typename Symm, template <typename...> class IndexList,\n          typename... Indices>\ntemplate <typename Arg0, typename... Args>\nconstexpr Tensor<X, Symm, IndexList<Indices...>>::Tensor(Arg0&& arg0,\n                                                         Args&&... args)\n    // NOLINTNEXTLINE(readability-simplify-boolean-expr)\n  requires(not(sizeof...(Args) == 0 and\n               std::same_as<std::decay_t<Arg0>, Tensor>) and\n           std::constructible_from<X, Arg0, Args...>)\n    : data_(make_array<size(), X>(std::forward<Arg0>(arg0),\n                                  std::forward<Args>(args)...)) {}\n\ntemplate <typename X, typename Symm, template <typename...> class IndexList,\n          typename... Indices>\nstd::pair<std::vector<std::string>, std::vector<X>>\nTensor<X, Symm, IndexList<Indices...>>::get_vector_of_data() const {\n  std::vector<value_type> serialized_tensor(size());\n  std::vector<std::string> component_names(size());\n  for (size_t i = 0; i < data_.size(); ++i) {\n    component_names[i] = component_name(get_tensor_index(i));\n    serialized_tensor[i] = gsl::at(data_, i);\n  }\n  return std::make_pair(component_names, serialized_tensor);\n}\n\ntemplate <int... N, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr typename Tensor<Args...>::reference get(\n    Tensor<Args...>& t) {\n  static_assert(Tensor<Args...>::rank() == sizeof...(N),\n                \"the number of tensor indices specified must match the rank \"\n                \"of the tensor\");\n  return gsl::at(\n      t.data_, Tensor<Args...>::structure::template get_storage_index<N...>());\n}\n\ntemplate <int... N, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr typename Tensor<Args...>::const_reference get(\n    const Tensor<Args...>& t) {\n  static_assert(Tensor<Args...>::rank() == sizeof...(N),\n                \"the number of tensor indices specified must match the rank \"\n                \"of the tensor\");\n  return gsl::at(\n      t.data_, Tensor<Args...>::structure::template get_storage_index<N...>());\n}\n\ntemplate <typename X, typename Symm, template <typename...> class IndexList,\n          typename... Indices>\nbool operator==(const Tensor<X, Symm, IndexList<Indices...>>& lhs,\n                const Tensor<X, Symm, IndexList<Indices...>>& rhs) {\n  return std::equal(lhs.begin(), lhs.end(), rhs.begin());\n}\ntemplate <typename X, typename Symm, template <typename...> class IndexList,\n          typename... Indices>\nbool operator!=(const Tensor<X, Symm, IndexList<Indices...>>& lhs,\n                const Tensor<X, Symm, IndexList<Indices...>>& rhs) {\n  return not(lhs == rhs);\n}\n\n/// \\ingroup TensorGroup\n/// Get dimensionality of i'th tensor index\n///\n/// \\snippet Test_Tensor.cpp index_dim\ntemplate <int I, class... Ts>\nSPECTRE_ALWAYS_INLINE constexpr size_t index_dim(const Tensor<Ts...>& /*t*/) {\n  return Tensor<Ts...>::structure::template dim<I>();\n}\n\n// We place the stream operators in the header file so they do not need to be\n// explicitly instantiated.\ntemplate <typename X, typename Symm, template <typename...> class IndexList,\n          typename... Indices>\nstd::ostream& operator<<(std::ostream& os,\n                         const Tensor<X, Symm, IndexList<Indices...>>& x) {\n  static_assert(tt::is_streamable_v<decltype(os), X>,\n                \"operator<< is not defined for the type you are trying to \"\n                \"stream in Tensor\");\n  for (size_t i = 0; i < x.size() - 1; ++i) {\n    os << \"T\" << x.get_tensor_index(i) << \"=\" << x[i] << \"\\n\";\n  }\n  size_t i = x.size() - 1;\n  os << \"T\" << x.get_tensor_index(i) << \"=\" << x[i];\n  return os;\n}\n\nnamespace MakeWithValueImpls {\ntemplate <typename T, typename... Structure>\nstruct NumberOfPoints<Tensor<T, Structure...>> {\n  static SPECTRE_ALWAYS_INLINE size_t\n  apply(const Tensor<T, Structure...>& input) {\n    return number_of_points(*input.begin());\n  }\n};\n\ntemplate <typename T, typename... Structure>\nstruct MakeWithSize<Tensor<T, Structure...>> {\n  template <typename U>\n  static SPECTRE_ALWAYS_INLINE Tensor<T, Structure...> apply(const size_t size,\n                                                             const U value) {\n    return Tensor<T, Structure...>(make_with_value<T>(size, value));\n  }\n};\n\ntemplate <typename... Structure, typename T>\nstruct MakeWithValueImpl<Tensor<double, Structure...>, T> {\n  static SPECTRE_ALWAYS_INLINE Tensor<double, Structure...> apply(\n      const T& /*input*/, const double value) {\n    return Tensor<double, Structure...>(value);\n  }\n};\n\ntemplate <typename... Structure, typename T>\nstruct MakeWithValueImpl<Tensor<std::complex<double>, Structure...>, T> {\n  static SPECTRE_ALWAYS_INLINE Tensor<std::complex<double>, Structure...> apply(\n      const T& /*input*/, const std::complex<double> value) {\n    return Tensor<std::complex<double>, Structure...>(value);\n  }\n};\n\n#ifdef SPECTRE_AUTODIFF\ntemplate <typename... Structure, typename T>\nstruct MakeWithValueImpl<\n    Tensor<autodiff::HigherOrderDual<2, double>, Structure...>, T> {\n  static SPECTRE_ALWAYS_INLINE\n      Tensor<autodiff::HigherOrderDual<2, double>, Structure...>\n      apply(const T& /*input*/, const double value) {\n    return Tensor<autodiff::HigherOrderDual<2, double>, Structure...>(value);\n  }\n};\n\ntemplate <typename... Structure, typename T>\nstruct MakeWithValueImpl<\n    Tensor<autodiff::HigherOrderDual<2, simd::batch<double>>, Structure...>,\n    T> {\n  static SPECTRE_ALWAYS_INLINE\n      Tensor<autodiff::HigherOrderDual<2, simd::batch<double>>, Structure...>\n      apply(const T& /*input*/, const double value) {\n    return Tensor<autodiff::HigherOrderDual<2, simd::batch<double>>,\n                  Structure...>(simd::batch<double>::broadcast(value));\n  }\n};\n#endif  // SPECTRE_AUTODIFF\n}  // namespace MakeWithValueImpls\n\ntemplate <typename T, typename... Structure>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n    Tensor<T, Structure...>> {\n  static constexpr bool is_trivial = SetNumberOfGridPointsImpl<T>::is_trivial;\n  static SPECTRE_ALWAYS_INLINE void apply(\n      const gsl::not_null<Tensor<T, Structure...>*> result, const size_t size) {\n    for (auto& component : *result) {\n      set_number_of_grid_points(make_not_null(&component), size);\n    }\n  }\n};\n"
  },
  {
    "path": "src/DataStructures/Tensor/TypeAliases.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines a list of useful type aliases for tensors\n\n#pragma once\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\n/// \\ingroup TensorGroup\n/// Scalar type\ntemplate <typename T>\nusing Scalar = Tensor<T, Symmetry<>, index_list<>>;\n\n/*!\n * \\ingroup TensorGroup\n * \\brief Type aliases to construct common Tensors\n *\n * Lower case letters represent covariant indices and upper case letters\n * represent contravariant indices. Letters a, b, c, d represent spacetime\n * indices and i, j, k, l represent spatial indices.\n */\nnamespace tnsr {\n// Rank 1\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing a = Tensor<DataType, tmpl::integral_list<std::int32_t, 1>,\n                 index_list<Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo,\n                                                           Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing A = Tensor<DataType, tmpl::integral_list<std::int32_t, 1>,\n                 index_list<Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up,\n                                                           Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing i = Tensor<DataType, tmpl::integral_list<std::int32_t, 1>,\n                 index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing I = Tensor<DataType, tmpl::integral_list<std::int32_t, 1>,\n                 index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\n\n// Rank 2\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing ab = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing Ab = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing aB = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing AB = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing aI =\n    Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n           index_list<Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr,\n                                                     IndexType::Spacetime>,\n                      Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr,\n                                                     IndexType::Spatial>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing ij = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n                  index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                             SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iJ = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n                  index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                             SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing Ij = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n                  index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                             SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing IJ = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n                  index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                             SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iA = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n                  index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                             SpacetimeIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing ia = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n                  index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                             SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\n\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing aa = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 1, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing AA = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 1, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing ii = Tensor<DataType, tmpl::integral_list<std::int32_t, 1, 1>,\n                  index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                             SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing II = Tensor<DataType, tmpl::integral_list<std::int32_t, 1, 1>,\n                  index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                             SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\n\n// Rank 3 - spacetime\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing abc = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing abC = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing aaB = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing aBc = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing Abc = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing aBC = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing AbC = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing ABc = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing ABC = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing abb = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing Abb = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing aBB = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing ABB = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>>>;\n\n// Rank 3 - spatial\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iii = Tensor<DataType, tmpl::integral_list<std::int32_t, 1, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing ijk = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing ijK = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iiJ = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iJk = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing Ijk = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iJK = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing IjK = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing IJk = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing IJK = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing ijj = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing Ijj = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iJJ = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing IJJ = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing III = Tensor<DataType, tmpl::integral_list<std::int32_t, 1, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\n\n// Rank 3 - mixed spacetime spatial\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iab = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iaB = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iAb = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iAB = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iaa = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing aia = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 2>,\n                   index_list<SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iAA = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing Iaa = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n                   index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                              SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\n\n// Rank 4 - Mixed\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing ijaa = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iiaa = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iiAA = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Up, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Up, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iAbb = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Up, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iabb = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing Ijaa = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing aaBB = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 2, 1, 1>,\n                    index_list<SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Up, Fr>,\n                               SpacetimeIndex<SpatialDim, UpLo::Up, Fr>>>;\n\n// Rank 4 - generic (default spacetime)\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing abcc = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing aBcc = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 3, 2, 1, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Up, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\n\n// Rank 4 - spatial\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing ijkk = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iJkk = Tensor<DataType, tmpl::integral_list<std::int32_t, 3, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\n\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iijj = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>>>;\n\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial>\nusing iiJJ = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 2, 1, 1>,\n                    index_list<SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Lo, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Up, Fr>,\n                               SpatialIndex<SpatialDim, UpLo::Up, Fr>>>;\n}  // namespace tnsr\n\n/*!\n * \\brief Inverse Jacobian $(J^{-1})^\\hat{i}_j =\n * \\frac{\\partial \\xi^\\hat{i}}{\\partial x^j}$\n *\n * The first (upper) index is in the \\p SourceFrame with coordinates\n * $\\xi^\\hat{i}$ (often \"logical\"), and the second (lower) index is in the\n * \\p TargetFrame with coordinates $x^j$ (often \"inertial\").\n */\ntemplate <typename DataType, size_t Dim, typename SourceFrame,\n          typename TargetFrame>\nusing InverseJacobian =\n    Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n           index_list<SpatialIndex<Dim, UpLo::Up, SourceFrame>,\n                      SpatialIndex<Dim, UpLo::Lo, TargetFrame>>>;\n\n/*!\n * \\brief Jacobian $J^i_\\hat{j} = \\frac{\\partial x^i}{\\partial \\xi^\\hat{j}}$\n *\n * The first (upper) index is in the \\p TargetFrame with coordinates $x^i$\n * (often \"inertial\"), and the second (lower) index is in the \\p SourceFrame\n * with coordinates $\\xi^\\hat{j}$ (often \"logical\").\n *\n * \\note The indices produced by the `logical_partial_derivative` function\n * are in the opposite order.\n */\ntemplate <typename DataType, size_t Dim, typename SourceFrame,\n          typename TargetFrame>\nusing Jacobian = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1>,\n                        index_list<SpatialIndex<Dim, UpLo::Up, TargetFrame>,\n                                   SpatialIndex<Dim, UpLo::Lo, SourceFrame>>>;\n\n/*!\n * \\brief Inverse Hessian $(H^{-1})^\\hat{i}_{jk} =\n * \\frac{\\partial \\xi^\\hat{i}}{\\partial x^j \\partial x^k}$\n *\n * The first (upper) index is in the \\p SourceFrame with coordinates\n * $\\xi^\\hat{i}$ (often \"logical\"), and the second and third (lower) indices are\n * in the \\p TargetFrame with coordinates $x^j$ (often \"inertial\").\n */\ntemplate <typename DataType, size_t Dim, typename SourceFrame,\n          typename TargetFrame>\nusing InverseHessian =\n    Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n           index_list<SpatialIndex<Dim, UpLo::Up, SourceFrame>,\n                      SpatialIndex<Dim, UpLo::Lo, TargetFrame>,\n                      SpatialIndex<Dim, UpLo::Lo, TargetFrame>>>;\n\n/*!\n * \\brief Hessian $H^i_{\\hat{j}\\hat{k}} =\n * \\frac{\\partial x^i}{\\partial \\xi^\\hat{j} \\partial \\xi^\\hat{k}}$\n *\n * The first (upper) index is in the \\p TargetFrame with coordinates $x^i$\n * (often \"inertial\"), and the second and third (lower) indices are in the\n * \\p SourceFrame with coordinates $\\xi^\\hat{j}$ (often \"logical\").\n */\ntemplate <typename DataType, size_t Dim, typename SourceFrame,\n          typename TargetFrame>\nusing Hessian = Tensor<DataType, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n                       index_list<SpatialIndex<Dim, UpLo::Up, TargetFrame>,\n                                  SpatialIndex<Dim, UpLo::Lo, SourceFrame>,\n                                  SpatialIndex<Dim, UpLo::Lo, SourceFrame>>>;\n"
  },
  {
    "path": "src/DataStructures/Transpose.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/Transpose.hpp\"\n#include <type_traits>\n\n#if defined(__SSE2__)\n#include <emmintrin.h>\n#endif\n#if defined(__AVX__)\n#include <immintrin.h>\n#endif\n\nnamespace {\n// We assume matrix points to the start of the sub matrix.\n//\n// Streaming writes didn't improve things with AVX2 on Zen2\n// architecture. For AVX-512 streaming might be more useful, but AVX-512 is\n// usually not recommended as of 2023 because the CPUs down clock so much\n// that all non-math work also suffers.\ntemplate <size_t RowsInBlock, size_t ColumnsInBlock>\nvoid transpose_block(double* __restrict__ matrix_transpose,\n                     const double* __restrict__ matrix, int32_t columns,\n                     int32_t rows);\n\n// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n#if defined(__AVX__)\ntemplate <>\nvoid transpose_block<4, 4>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256d row0 = _mm256_loadu_pd(matrix + 0 * columns);\n  const __m256d row1 = _mm256_loadu_pd(matrix + 1 * columns);\n  const __m256d row2 = _mm256_loadu_pd(matrix + 2 * columns);\n  const __m256d row3 = _mm256_loadu_pd(matrix + 3 * columns);\n\n  const __m256d tmp0 = _mm256_shuffle_pd((row0), (row1), 0b0000);\n  const __m256d tmp2 = _mm256_shuffle_pd((row0), (row1), 0b1111);\n  const __m256d tmp1 = _mm256_shuffle_pd((row2), (row3), 0b0000);\n  const __m256d tmp3 = _mm256_shuffle_pd((row2), (row3), 0b1111);\n\n  _mm256_storeu_pd(matrix_transpose + 0 * rows,\n                   _mm256_permute2f128_pd(tmp0, tmp1, 0x20));\n  _mm256_storeu_pd(matrix_transpose + 1 * rows,\n                   _mm256_permute2f128_pd(tmp2, tmp3, 0x20));\n  _mm256_storeu_pd(matrix_transpose + 2 * rows,\n                   _mm256_permute2f128_pd(tmp0, tmp1, 0x31));\n  _mm256_storeu_pd(matrix_transpose + 3 * rows,\n                   _mm256_permute2f128_pd(tmp2, tmp3, 0x31));\n}\n\ntemplate <>\nvoid transpose_block<3, 4>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256i mask = _mm256_set_epi64x(0, -1, -1, -1);\n  const __m256d row0 = _mm256_loadu_pd(matrix + 0 * columns);\n  const __m256d row1 = _mm256_loadu_pd(matrix + 1 * columns);\n  const __m256d row2 = _mm256_loadu_pd(matrix + 2 * columns);\n\n  const __m256d tmp0 = _mm256_shuffle_pd((row0), (row1), 0b0000);\n  const __m256d tmp2 = _mm256_shuffle_pd((row0), (row1), 0b1111);\n  const __m256d tmp1 = _mm256_shuffle_pd((row2), (row2), 0b0000);\n  const __m256d tmp3 = _mm256_shuffle_pd((row2), (row2), 0b1111);\n\n  _mm256_maskstore_pd(matrix_transpose + 0 * rows, mask,\n                      _mm256_permute2f128_pd(tmp0, tmp1, 0x20));\n  _mm256_maskstore_pd(matrix_transpose + 1 * rows, mask,\n                      _mm256_permute2f128_pd(tmp2, tmp3, 0x20));\n  _mm256_maskstore_pd(matrix_transpose + 2 * rows, mask,\n                      _mm256_permute2f128_pd(tmp0, tmp1, 0x31));\n  _mm256_maskstore_pd(matrix_transpose + 3 * rows, mask,\n                      _mm256_permute2f128_pd(tmp2, tmp3, 0x31));\n}\n\ntemplate <>\nvoid transpose_block<2, 4>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256d row0 = _mm256_loadu_pd(matrix + 0 * columns);\n  const __m256d row1 = _mm256_loadu_pd(matrix + 1 * columns);\n\n  const __m256d tmp0 = _mm256_shuffle_pd((row0), (row1), 0b0000);\n  const __m256d tmp1 = _mm256_shuffle_pd((row0), (row1), 0b1111);\n\n  _mm_storeu_pd(matrix_transpose + 0 * rows, _mm256_extractf128_pd(tmp0, 0));\n  _mm_storeu_pd(matrix_transpose + 1 * rows, _mm256_extractf128_pd(tmp1, 0));\n  _mm_storeu_pd(matrix_transpose + 2 * rows, _mm256_extractf128_pd(tmp0, 1));\n  _mm_storeu_pd(matrix_transpose + 3 * rows, _mm256_extractf128_pd(tmp1, 1));\n}\n\ntemplate <>\nvoid transpose_block<1, 4>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t /*columns*/, const int32_t rows) {\n  const __m256d row0 = _mm256_loadu_pd(matrix);\n\n  const __m256d tmp0 = _mm256_shuffle_pd(row0, row0, 0b0000);\n  const __m256d tmp1 = _mm256_shuffle_pd(row0, row0, 0b1111);\n\n  const __m128i store_mask = _mm_set_epi64x(0, -1);\n  _mm_maskstore_pd(matrix_transpose + 0 * rows, store_mask,\n                   _mm256_castpd256_pd128(tmp0));\n  _mm_maskstore_pd(matrix_transpose + 1 * rows, store_mask,\n                   _mm256_castpd256_pd128(tmp1));\n  _mm_maskstore_pd(matrix_transpose + 2 * rows, store_mask,\n                   _mm256_extractf128_pd(tmp0, 1));\n  _mm_maskstore_pd(matrix_transpose + 3 * rows, store_mask,\n                   _mm256_extractf128_pd(tmp1, 1));\n}\n\ntemplate <>\nvoid transpose_block<4, 3>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256i mask = _mm256_set_epi64x(0, -1, -1, -1);\n  const __m256d row0 = _mm256_maskload_pd(matrix + 0 * columns, mask);\n  const __m256d row1 = _mm256_maskload_pd(matrix + 1 * columns, mask);\n  const __m256d row2 = _mm256_maskload_pd(matrix + 2 * columns, mask);\n  const __m256d row3 = _mm256_maskload_pd(matrix + 3 * columns, mask);\n\n  const __m256d tmp0 = _mm256_shuffle_pd((row0), (row1), 0b0000);\n  const __m256d tmp2 = _mm256_shuffle_pd((row0), (row1), 0b1111);\n  const __m256d tmp1 = _mm256_shuffle_pd((row2), (row3), 0b0000);\n  const __m256d tmp3 = _mm256_shuffle_pd((row2), (row3), 0b1111);\n\n  _mm256_storeu_pd(matrix_transpose + 0 * rows,\n                   _mm256_permute2f128_pd(tmp0, tmp1, 0x20));\n  _mm256_storeu_pd(matrix_transpose + 1 * rows,\n                   _mm256_permute2f128_pd(tmp2, tmp3, 0x20));\n  _mm256_storeu_pd(matrix_transpose + 2 * rows,\n                   _mm256_permute2f128_pd(tmp0, tmp1, 0x31));\n}\n\ntemplate <>\nvoid transpose_block<4, 2>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256d rows0_1 = _mm256_permute2f128_pd(\n      _mm256_castpd128_pd256(_mm_loadu_pd(matrix + 0 * columns)),\n      _mm256_castpd128_pd256(_mm_loadu_pd(matrix + 2 * columns)), 0b00100000);\n  const __m256d rows2_3 = _mm256_permute2f128_pd(\n      _mm256_castpd128_pd256(_mm_loadu_pd(matrix + 1 * columns)),\n      _mm256_castpd128_pd256(_mm_loadu_pd(matrix + 3 * columns)), 0b00100000);\n\n  _mm256_storeu_pd(matrix_transpose + 0 * rows,\n                   _mm256_unpacklo_pd(rows0_1, rows2_3));\n  _mm256_storeu_pd(matrix_transpose + 1 * rows,\n                   _mm256_unpackhi_pd(rows0_1, rows2_3));\n}\n\ntemplate <>\nvoid transpose_block<4, 1>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  // We load the 4 rows into SSE registers, and then combine them into a\n  // single AVX register for write.\n  const __m128d row0 = _mm_load_pd1(matrix + 0 * columns);\n  const __m128d row1 = _mm_load_pd1(matrix + 1 * columns);\n  const __m128d row2 = _mm_load_pd1(matrix + 2 * columns);\n  const __m128d row3 = _mm_load_pd1(matrix + 3 * columns);\n\n  _mm256_storeu_pd(matrix_transpose + 0 * rows,\n                   _mm256_insertf128_pd(\n                       _mm256_castpd128_pd256(_mm_shuffle_pd(row0, row1, 0b00)),\n                       _mm_shuffle_pd(row2, row3, 0b00), 1));\n}\n\ntemplate <>\nvoid transpose_block<3, 3>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256i mask = _mm256_set_epi64x(0, -1, -1, -1);\n  const __m256d row0 = _mm256_maskload_pd(matrix + 0 * columns, mask);\n  const __m256d row1 = _mm256_maskload_pd(matrix + 1 * columns, mask);\n  const __m256d row2 = _mm256_maskload_pd(matrix + 2 * columns, mask);\n\n  const __m256d tmp0 = _mm256_shuffle_pd((row0), (row1), 0b0000);\n  const __m256d tmp2 = _mm256_shuffle_pd((row0), (row1), 0b1111);\n  const __m256d tmp1 = _mm256_shuffle_pd((row2), (row2), 0b0000);\n  const __m256d tmp3 = _mm256_shuffle_pd((row2), (row2), 0b1111);\n\n  _mm256_maskstore_pd(matrix_transpose + 0 * rows, mask,\n                      _mm256_permute2f128_pd(tmp0, tmp1, 0x20));\n  _mm256_maskstore_pd(matrix_transpose + 1 * rows, mask,\n                      _mm256_permute2f128_pd(tmp2, tmp3, 0x20));\n  _mm256_maskstore_pd(matrix_transpose + 2 * rows, mask,\n                      _mm256_permute2f128_pd(tmp0, tmp1, 0x31));\n}\n\ntemplate <>\nvoid transpose_block<3, 2>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256i mask = _mm256_set_epi64x(0, -1, -1, -1);\n  const __m256d row0 =\n      _mm256_castpd128_pd256(_mm_loadu_pd(matrix + 0 * columns));\n  const __m256d row1 =\n      _mm256_castpd128_pd256(_mm_loadu_pd(matrix + 1 * columns));\n  const __m256d row2 =\n      _mm256_castpd128_pd256(_mm_loadu_pd(matrix + 2 * columns));\n\n  const __m256d tmp0 = _mm256_shuffle_pd((row0), (row1), 0b0000);\n  const __m256d tmp2 = _mm256_shuffle_pd((row0), (row1), 0b1111);\n  const __m256d tmp1 = _mm256_shuffle_pd((row2), (row2), 0b0000);\n  const __m256d tmp3 = _mm256_shuffle_pd((row2), (row2), 0b1111);\n\n  _mm256_maskstore_pd(matrix_transpose + 0 * rows, mask,\n                      _mm256_permute2f128_pd(tmp0, tmp1, 0x20));\n  _mm256_maskstore_pd(matrix_transpose + 1 * rows, mask,\n                      _mm256_permute2f128_pd(tmp2, tmp3, 0x20));\n}\n\ntemplate <>\nvoid transpose_block<3, 1>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256i mask = _mm256_set_epi64x(0, -1, -1, -1);\n  // We load the 3 rows into SSE registers, and then combine them into a\n  // single AVX register for write.\n  const __m128d row0 = _mm_load_pd1(matrix + 0 * columns);\n  const __m128d row1 = _mm_load_pd1(matrix + 1 * columns);\n  const __m128d row2 = _mm_load_pd1(matrix + 2 * columns);\n\n  _mm256_maskstore_pd(\n      matrix_transpose + 0 * rows, mask,\n      _mm256_insertf128_pd(\n          _mm256_castpd128_pd256(_mm_shuffle_pd(row0, row1, 0b00)), row2, 1));\n}\n\ntemplate <>\nvoid transpose_block<2, 3>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256i mask = _mm256_set_epi64x(0, -1, -1, -1);\n  const __m256d row0 = _mm256_maskload_pd(matrix + 0 * columns, mask);\n  const __m256d row1 = _mm256_maskload_pd(matrix + 1 * columns, mask);\n\n  const __m256d tmp0 = _mm256_shuffle_pd((row0), (row1), 0b0000);\n  const __m256d tmp1 = _mm256_shuffle_pd((row0), (row1), 0b1111);\n\n  _mm_storeu_pd(matrix_transpose + 0 * rows, _mm256_castpd256_pd128(tmp0));\n  _mm_storeu_pd(matrix_transpose + 1 * rows, _mm256_castpd256_pd128(tmp1));\n  _mm_storeu_pd(matrix_transpose + 2 * rows, _mm256_extractf128_pd(tmp0, 1));\n}\n\ntemplate <>\nvoid transpose_block<1, 3>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m256i mask = _mm256_set_epi64x(0, -1, -1, -1);\n  const __m256d row0 = _mm256_maskload_pd(matrix + 0 * columns, mask);\n\n  const __m256d tmp0 = _mm256_shuffle_pd(row0, row0, 0b0000);\n  const __m256d tmp1 = _mm256_shuffle_pd(row0, row0, 0b1111);\n\n  const __m128i store_mask = _mm_set_epi64x(0, -1);\n  _mm_maskstore_pd(matrix_transpose + 0 * rows, store_mask,\n                   _mm256_castpd256_pd128(tmp0));\n  _mm_maskstore_pd(matrix_transpose + 1 * rows, store_mask,\n                   _mm256_castpd256_pd128(tmp1));\n  _mm_maskstore_pd(matrix_transpose + 2 * rows, store_mask,\n                   _mm256_extractf128_pd(tmp0, 1));\n}\n#endif\n\n#if defined(__SSE2__)\ntemplate <>\nvoid transpose_block<2, 2>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t rows) {\n  const __m128d row0 = _mm_loadu_pd(matrix);\n  const __m128d row1 = _mm_loadu_pd(matrix + columns);\n\n  const __m128d tmp0 = _mm_shuffle_pd(row0, row1, 0b00);\n  const __m128d tmp1 = _mm_shuffle_pd(row0, row1, 0b11);\n\n  _mm_storeu_pd(matrix_transpose, tmp0);\n  _mm_storeu_pd(matrix_transpose + rows, tmp1);\n}\n\ntemplate <>\nvoid transpose_block<2, 1>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t columns, const int32_t /*rows*/) {\n  const __m128d row0 = _mm_load_pd1(matrix + 0 * columns);\n  const __m128d row1 = _mm_load_pd1(matrix + 1 * columns);\n\n  const __m128d tmp0 = _mm_shuffle_pd(row0, row1, 0b00);\n\n  _mm_storeu_pd(matrix_transpose, tmp0);\n}\n\ntemplate <>\nvoid transpose_block<1, 2>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t /*columns*/, const int32_t rows) {\n#if defined(__AVX__)\n  const __m128d row = _mm_loadu_pd(matrix);\n  _mm_maskstore_pd(matrix_transpose + 0 * rows, _mm_set_epi64x(0, -1), row);\n  _mm_maskstore_pd(matrix_transpose + 1 * rows - 1, _mm_set_epi64x(-1, 0), row);\n#else\n  matrix_transpose[0] = matrix[0];\n  matrix_transpose[rows] = matrix[1];\n#endif\n}\n#endif\n// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n\ntemplate <>\nvoid transpose_block<1, 1>(double* __restrict__ matrix_transpose,\n                           const double* __restrict__ const matrix,\n                           const int32_t /*columns*/, const int32_t /*rows*/) {\n  *matrix_transpose = *matrix;\n}\n\ntemplate <int32_t BlockSize, int32_t RowExcess, int32_t ColumnExcess>\nvoid transpose_impl(double* __restrict__ matrix_transpose,  //\n                    const double* __restrict__ const matrix,\n                    const int32_t in_number_of_rows,\n                    const int32_t in_number_of_columns) {\n  const int32_t bound_on_rows = in_number_of_rows - RowExcess;\n  const int32_t bound_on_columns = in_number_of_columns - ColumnExcess;\n\n  for (int32_t row_index = 0UL; row_index < bound_on_rows;\n       row_index += BlockSize) {\n    for (int32_t column_index = 0UL; column_index < bound_on_columns;\n         column_index += BlockSize) {\n      if constexpr (BlockSize != 1) {\n        transpose_block<BlockSize, BlockSize>(\n            matrix_transpose + row_index + in_number_of_rows * column_index,\n            matrix + column_index + in_number_of_columns * row_index,\n            in_number_of_columns, in_number_of_rows);\n      } else {\n        static_assert(BlockSize == 1);\n        static_assert(RowExcess == 0);\n        static_assert(ColumnExcess == 0);\n        transpose_block<1, 1>(\n            matrix_transpose + row_index + in_number_of_rows * column_index,\n            matrix + column_index + in_number_of_columns * row_index,\n            in_number_of_columns, in_number_of_rows);\n      }\n    }\n    // Handle remainder in row, that is, deal with extra columns.\n    if constexpr (BlockSize > 1 and ColumnExcess != 0) {\n      const int32_t column_index = bound_on_columns;\n      transpose_block<BlockSize, ColumnExcess>(\n          matrix_transpose + row_index + in_number_of_rows * column_index,\n          matrix + column_index + in_number_of_columns * row_index,\n          in_number_of_columns, in_number_of_rows);\n    }\n  }\n\n  // Now deal with excess in either the columns or rows.\n  //\n  // We have the choice of either having the extra loops of the inner index\n  // (currently row_index)  inside the main loop above or down below. This is a\n  // tradeoff between data cache and instruction cache.\n  if constexpr (BlockSize > 1 and RowExcess != 0) {\n    const int32_t row_index = bound_on_rows;\n    for (int32_t column_index = 0UL; column_index < bound_on_columns;\n         column_index += BlockSize) {\n      transpose_block<RowExcess, BlockSize>(\n          matrix_transpose + row_index + in_number_of_rows * column_index,\n          matrix + column_index + in_number_of_columns * row_index,\n          in_number_of_columns, in_number_of_rows);\n    }\n    if constexpr (ColumnExcess != 0) {\n      const int32_t column_index = bound_on_columns;\n      transpose_block<RowExcess, ColumnExcess>(\n          matrix_transpose + row_index + in_number_of_rows * column_index,\n          matrix + column_index + in_number_of_columns * row_index,\n          in_number_of_columns, in_number_of_rows);\n    }\n  }\n}\n}  // namespace\n\nnamespace detail {\nvoid transpose_impl(double* matrix_transpose, const double* const matrix,\n                    const int32_t number_of_rows,\n                    const int32_t number_of_columns) {\n  constexpr size_t block_size =\n#if defined(__AVX__)\n      4\n#elif defined(__SSE2__)\n      2\n#else\n      1\n#endif\n      ;\n  const auto forward_to_impl = [&](auto row_excess_v) {\n    constexpr size_t row_excess = decltype(row_excess_v)::value;\n    switch (number_of_columns % static_cast<int32_t>(block_size)) {\n#if defined(__AVX__)\n      case 3:\n        ::transpose_impl<block_size, row_excess, 3>(\n            matrix_transpose, matrix, number_of_rows, number_of_columns);\n        break;\n      case 2:\n        ::transpose_impl<block_size, row_excess, 2>(\n            matrix_transpose, matrix, number_of_rows, number_of_columns);\n        break;\n#endif\n#if defined(__SSE2__) or defined(__AVX__)\n      case 1:\n        ::transpose_impl<block_size, row_excess, 1>(\n            matrix_transpose, matrix, number_of_rows, number_of_columns);\n        break;\n#endif\n      case 0:\n        ::transpose_impl<block_size, row_excess, 0>(\n            matrix_transpose, matrix, number_of_rows, number_of_columns);\n        break;\n      default:\n        ERROR(\"Can't determine the excess number of columns.\");\n    };\n  };\n  switch (number_of_rows % static_cast<int32_t>(block_size)) {\n#if defined(__AVX__)\n    case 3:\n      forward_to_impl(std::integral_constant<uint32_t, 3>{});\n      break;\n    case 2:\n      forward_to_impl(std::integral_constant<uint32_t, 2>{});\n      break;\n#endif\n#if defined(__SSE2__) or defined(__AVX__)\n    case 1:\n      forward_to_impl(std::integral_constant<uint32_t, 1>{});\n      break;\n#endif\n    case 0:\n      forward_to_impl(std::integral_constant<uint32_t, 0>{});\n      break;\n    default:\n      ERROR(\"Can't determine the excess number of rows.\");\n  };\n}\n}  // namespace detail\n"
  },
  {
    "path": "src/DataStructures/Transpose.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function transpose\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace detail {\nvoid transpose_impl(double* matrix_transpose, const double* matrix,\n                    int32_t number_of_rows, int32_t number_of_columns);\n}  // namespace detail\n\n\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Function to compute transposed data.\n///\n/// Transpose the data pointed to by `data`, writing the result to the\n/// location pointed to by `result`.  See the ::transpose function for\n/// a safer interface and for the meaning of the other arguments.\n// This is not an overload of transpose because overload resolution\n// would make non-intuitive choices of the return-by-pointer version\n// below over this function.\ntemplate <typename T>\nvoid raw_transpose(const gsl::not_null<T*> result, const T* const data,\n                   const size_t chunk_size, const size_t number_of_chunks) {\n  if constexpr (std::is_same_v<double, T>) {\n    detail::transpose_impl(result, data, static_cast<int32_t>(number_of_chunks),\n                           static_cast<int32_t>(chunk_size));\n  } else {\n    // The i outside loop order is faster, but that could be architecture\n    // dependent and so may need updating in the future. Changing this made the\n    // logical derivatives in 3D with 50 variables 20% faster.\n    for (size_t i = 0; i < chunk_size; ++i) {\n      for (size_t j = 0; j < number_of_chunks; ++j) {\n        // clang-tidy: pointer arithmetic\n        result.get()[j + number_of_chunks * i] =  // NOLINT\n            data[i + chunk_size * j];             // NOLINT\n      }\n    }\n  }\n}\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Function to compute transposed data.\n///\n/// The primary use of this function is to rearrange the memory layout so that\n/// another function can operate on contiguous chunks of memory.\n///\n/// \\requires `result.size()` to be the product of `number_of_chunks` and\n/// `chunk_size`, `u.size()` to be equal or greater than `result.size()`,\n/// and that both `result` and `u` have a `data()` member function.\n///\n/// \\details The container `u` holds a contiguous array of data,\n/// treated as a sequence of `number_of_chunks` contiguous sets of\n/// entries of size `chunk_size`.  The output `result` has its data\n/// arranged such that the first `number_of_chunks` elements in\n/// `result` will be the first element of each chunk of `u`.  The\n/// last `number_of_chunks` elements in `result` will be the last\n/// (i.e.  `chunk_size`-th) element of each chunk of `u`.  If\n/// `u.size()` is greater than `result.size()` the extra elements\n/// of `u` are ignored.\n///\n/// \\note This is equivalent to treating the first part of `u` as a matrix and\n/// filling `result` (or the returned object) with the transpose of that\n/// matrix.\n///\n/// If `u` represents a block of data indexed by\n/// \\f$(x, y, z, \\ldots)\\f$ with the first index varying fastest,\n/// transpose serves to rotate the indices.  If the extents are\n/// \\f$(X, Y, Z, \\ldots)\\f$, with product \\f$N\\f$, `transpose(u, X,\n/// N/X)` reorders the data to be indexed \\f$(y, z, \\ldots, x)\\f$,\n/// `transpose(u, X*Y, N/X/Y)` reorders the data to be indexed\n/// \\f$(z, \\ldots, x, y)\\f$, etc.\n///\n/// \\example\n/// \\snippet Test_Transpose.cpp transpose_matrix\n/// \\snippet Test_Transpose.cpp transpose_by_not_null_example\n/// \\snippet Test_Transpose.cpp return_transpose_example\n/// \\snippet Test_Transpose.cpp partial_transpose_example\n///\n/// \\tparam U the type of data to be transposed\n/// \\tparam T the type of the transposed data\ntemplate <typename U, typename T>\nvoid transpose(const gsl::not_null<T*> result, const U& u,\n               const size_t chunk_size, const size_t number_of_chunks) {\n  ASSERT(chunk_size * number_of_chunks == result->size(),\n         \"chunk_size = \" << chunk_size << \", number_of_chunks = \"\n                         << number_of_chunks << \", size = \" << result->size());\n  ASSERT(result->size() <= u.size(),\n         \"result.size = \" << result->size() << \", u.size = \" << u.size());\n  raw_transpose(make_not_null(result->data()), u.data(), chunk_size,\n                number_of_chunks);\n}\n\ntemplate <typename U, typename T = U>\nT transpose(const U& u, const size_t chunk_size,\n            const size_t number_of_chunks) {\n  T t = make_with_value<T>(u, 0.0);\n  transpose(make_not_null(&t), u, chunk_size, number_of_chunks);\n  return t;\n}\n/// @}\n"
  },
  {
    "path": "src/DataStructures/Variables/FrameTransform.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/EagerMath/FrameTransform.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace transform {\n\n/// Tags to represent the result of frame-transforming Variables\nnamespace Tags {\n/// The `Tag` with the first index transformed to a different frame\ntemplate <typename Tag, typename FirstIndexFrame>\nstruct TransformedFirstIndex : db::SimpleTag {\n  using type = TensorMetafunctions::prepend_spatial_index<\n      TensorMetafunctions::remove_first_index<typename Tag::type>,\n      tmpl::front<typename Tag::type::index_list>::dim, UpLo::Up,\n      FirstIndexFrame>;\n};\n}  // namespace Tags\n\n/// @{\n/// Transforms the first index of all tensors in the Variables to a different\n/// frame\n///\n/// See single-Tensor overload for details.\ntemplate <typename... ResultTags, typename... InputTags, size_t Dim,\n          typename SourceFrame, typename TargetFrame>\nvoid first_index_to_different_frame(\n    const gsl::not_null<Variables<tmpl::list<ResultTags...>>*> result,\n    const Variables<tmpl::list<InputTags...>>& input,\n    const InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&\n        inv_jacobian) {\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      first_index_to_different_frame(make_not_null(&get<ResultTags>(*result)),\n                                     get<InputTags>(input), inv_jacobian));\n}\n\ntemplate <typename... InputTags, size_t Dim, typename SourceFrame,\n          typename TargetFrame,\n          typename ResultVars = Variables<tmpl::list<\n              Tags::TransformedFirstIndex<InputTags, SourceFrame>...>>>\nResultVars first_index_to_different_frame(\n    const Variables<tmpl::list<InputTags...>>& input,\n    const InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&\n        inv_jacobian) {\n  ResultVars result{input.number_of_grid_points()};\n  first_index_to_different_frame(make_not_null(&result), input, inv_jacobian);\n  return result;\n}\n/// @}\n\n}  // namespace transform\n"
  },
  {
    "path": "src/DataStructures/Variables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class Variables\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <blaze/math/AlignmentFlag.h>\n#include <blaze/math/CustomVector.h>\n#include <blaze/math/DenseVector.h>\n#include <blaze/math/GroupTag.h>\n#include <blaze/math/PaddingFlag.h>\n#include <blaze/math/TransposeFlag.h>\n#include <blaze/math/Vector.h>\n#include <limits>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Subitems.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeSignalingNan.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\ntemplate <typename TagsList>\nclass Variables;\ntemplate <typename T, typename VectorType, size_t StaticSize>\nclass VectorImpl;\nnamespace Tags {\ntemplate <typename TagsList>\nclass Variables;\n}  // namespace Tags\n/// \\endcond\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief A Variables holds a contiguous memory block with Tensors pointing\n * into it.\n *\n * The `Tags` are `struct`s that must have a public type alias `type` whose\n * value must be a `Tensor<DataVector, ...>`, a `static' method `name()` that\n * returns a `std::string` of the tag name, and must derive off of\n * `db::SimpleTag`. In general, they should be DataBoxTags that are not compute\n * items. For example,\n *\n * \\snippet Helpers/DataStructures/TestTags.hpp simple_variables_tag\n *\n * Prefix tags can also be stored and their format is:\n *\n * \\snippet Helpers/DataStructures/TestTags.hpp prefix_variables_tag\n *\n * #### Design Decisions\n *\n * The `Variables` class is designed to hold several different `Tensor`s\n * performing one memory allocation for all the `Tensor`s. The advantage is that\n * memory allocations are quite expensive, especially in a parallel environment.\n *\n * If the macro `SPECTRE_NAN_INIT` is defined, the contents are\n * initialized with `NaN`s.\n */\ntemplate <typename... Tags>\nclass Variables<tmpl::list<Tags...>> {\n public:\n  using size_type = size_t;\n  using difference_type = std::ptrdiff_t;\n  static constexpr auto transpose_flag = blaze::defaultTransposeFlag;\n\n  /// A typelist of the Tags whose variables are held\n  using tags_list = tmpl::list<Tags...>;\n  static_assert(sizeof...(Tags) > 0,\n                \"You must provide at least one tag to the Variables \"\n                \"for type inference\");\n  static_assert((db::is_simple_tag_v<Tags> and ...));\n  static_assert(tmpl2::flat_all_v<tt::is_a_v<Tensor, typename Tags::type>...>);\n\n private:\n  using first_tensors_type = typename tmpl::front<tags_list>::type::type;\n\n public:\n  static_assert(tmpl2::flat_all_v<std::is_same_v<typename Tags::type::type,\n                                                 first_tensors_type>...> or\n                    tmpl2::flat_all_v<is_spin_weighted_of_same_type_v<\n                        typename Tags::type::type, first_tensors_type>...>,\n                \"All tensors stored in a single Variables must \"\n                \"have the same internal storage type.\");\n\n  using vector_type =\n      tmpl::conditional_t<is_any_spin_weighted_v<first_tensors_type>,\n                          typename first_tensors_type::value_type,\n                          first_tensors_type>;\n  using value_type = typename vector_type::value_type;\n  using pointer = value_type*;\n  using const_pointer = const value_type*;\n  using allocator_type = std::allocator<value_type>;\n  using pointer_type =\n      blaze::CustomVector<value_type, blaze::AlignmentFlag::unaligned,\n                          blaze::PaddingFlag::unpadded, transpose_flag,\n                          blaze::GroupTag<0>, vector_type>;\n\n  static_assert(\n      std::is_fundamental_v<value_type> or tt::is_a_v<std::complex, value_type>,\n      \"`value_type` of the Variables (so the storage type of the vector type \"\n      \"within the tensors in the Variables) must be either a fundamental type \"\n      \"or a std::complex. If this constraint is relaxed, the value_type \"\n      \"should be handled differently in the Variables, including pass by \"\n      \"reference.\");\n\n  /// The number of variables of the Variables object is holding. E.g.\n  /// \\f$g_{ab}\\f$ would be counted as one variable.\n  static constexpr auto number_of_variables = sizeof...(Tags);\n\n  /// The total number of independent components of all the variables. E.g.\n  /// a rank-2 symmetric spacetime Tensor \\f$g_{ab}\\f$ in 3 spatial\n  /// dimensions would have 10 independent components.\n  static constexpr size_t number_of_independent_components =\n      (... + Tags::type::size());\n\n  /// Default construct an empty Variables class, Charm++ needs this\n  Variables();\n\n  explicit Variables(size_t number_of_grid_points);\n\n  Variables(size_t number_of_grid_points, value_type value);\n\n  /// Construct a non-owning Variables that points to `start`. `size` is the\n  /// size of the allocation, which must be\n  /// `number_of_grid_points * Variables::number_of_independent_components`\n  Variables(pointer start, size_t size);\n\n  /// Construct an owning Variables using the storage from an existing vector.\n  explicit Variables(vector_type vector);\n\n  Variables(Variables&& rhs);\n  Variables& operator=(Variables&& rhs);\n\n  Variables(const Variables& rhs);\n  Variables& operator=(const Variables& rhs);\n\n  /// @{\n  /// Copy and move semantics for wrapped variables\n  template <typename... WrappedTags,\n            Requires<tmpl2::flat_all<std::is_same<\n                db::remove_all_prefixes<WrappedTags>,\n                db::remove_all_prefixes<Tags>>::value...>::value> = nullptr>\n  explicit Variables(Variables<tmpl::list<WrappedTags...>>&& rhs);\n  template <typename... WrappedTags,\n            Requires<tmpl2::flat_all<std::is_same<\n                db::remove_all_prefixes<WrappedTags>,\n                db::remove_all_prefixes<Tags>>::value...>::value> = nullptr>\n  Variables& operator=(Variables<tmpl::list<WrappedTags...>>&& rhs);\n\n  template <typename... WrappedTags,\n            Requires<tmpl2::flat_all<std::is_same<\n                db::remove_all_prefixes<WrappedTags>,\n                db::remove_all_prefixes<Tags>>::value...>::value> = nullptr>\n  explicit Variables(const Variables<tmpl::list<WrappedTags...>>& rhs);\n  template <typename... WrappedTags,\n            Requires<tmpl2::flat_all<std::is_same<\n                db::remove_all_prefixes<WrappedTags>,\n                db::remove_all_prefixes<Tags>>::value...>::value> = nullptr>\n  Variables& operator=(const Variables<tmpl::list<WrappedTags...>>& rhs);\n  /// @}\n\n  /// \\cond HIDDEN_SYMBOLS\n  ~Variables() = default;\n  /// \\endcond\n\n  /// @{\n  /// Initialize a Variables to the state it would have after calling\n  /// the constructor with the same arguments.\n  // this should be updated if we ever use a variables which has a `value_type`\n  // larger than ~2 doubles in size.\n  void initialize(size_t number_of_grid_points);\n  void initialize(size_t number_of_grid_points, value_type value);\n  /// @}\n\n  /// @{\n  /// Set the VectorImpl to be a reference to another VectorImpl object\n  void set_data_ref(const gsl::not_null<Variables*> rhs) {\n    set_data_ref(rhs->data(), rhs->size());\n  }\n\n  void set_data_ref(pointer const start, const size_t size) {\n    variable_data_impl_dynamic_.clear();\n    if (start == nullptr) {\n      variable_data_ = pointer_type{};\n      size_ = 0;\n      number_of_grid_points_ = 0;\n    } else {\n      size_ = size;\n      variable_data_.reset(start, size_);\n      ASSERT(\n          size_ % number_of_independent_components == 0,\n          \"The size (\" << size_\n                       << \") must be a multiple of the number of independent \"\n                          \"components (\"\n                       << number_of_independent_components\n                       << \") since we calculate the number of grid points from \"\n                          \"the size and number of independent components.\");\n      number_of_grid_points_ = size_ / number_of_independent_components;\n    }\n    owning_ = false;\n    add_reference_variable_data();\n  }\n  /// @}\n\n  constexpr SPECTRE_ALWAYS_INLINE size_t number_of_grid_points() const {\n    return number_of_grid_points_;\n  }\n\n  /// Number of grid points * number of independent components\n  constexpr SPECTRE_ALWAYS_INLINE size_type size() const { return size_; }\n\n  /// @{\n  /// Access pointer to underlying data\n  pointer data() { return variable_data_.data(); }\n  const_pointer data() const { return variable_data_.data(); }\n  /// @}\n\n  /// Take ownership of the data used by this Variables as a DataVector or\n  /// similar type.  The Variables must be owning.  This performs no\n  /// allocations or copies unless `number_of_grid_points()` is 1.\n  vector_type release() &&;\n\n  /// \\cond HIDDEN_SYMBOLS\n  /// Needed because of limitations and inconsistency between compiler\n  /// implementations of friend function templates with auto return type of\n  /// class templates\n  const auto& get_variable_data() const { return variable_data_; }\n  /// \\endcond\n\n  /// Returns true if the class owns the data\n  bool is_owning() const { return owning_; }\n\n  // clang-tidy: redundant-declaration\n  template <typename Tag, typename TagList>\n  friend constexpr typename Tag::type& get(  // NOLINT\n      Variables<TagList>& v);\n  template <typename Tag, typename TagList>\n  friend constexpr const typename Tag::type& get(  // NOLINT\n      const Variables<TagList>& v);\n\n  /// Serialization for Charm++.\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// @{\n  /// \\brief Assign a subset of the `Tensor`s from another Variables or a\n  /// tuples::TaggedTuple. Any tags that aren't in both containers are\n  /// ignored.\n  ///\n  /// \\note There is no need for an rvalue overload because we need to copy into\n  /// the contiguous array anyway\n  template <typename... SubsetOfTags>\n  void assign_subset(const Variables<tmpl::list<SubsetOfTags...>>& vars) {\n    EXPAND_PACK_LEFT_TO_RIGHT([this, &vars]() {\n      if constexpr (tmpl::list_contains_v<tmpl::list<Tags...>, SubsetOfTags>) {\n        get<SubsetOfTags>(*this) = get<SubsetOfTags>(vars);\n      } else {\n        (void)this;\n        (void)vars;\n      }\n    }());\n  }\n\n  template <typename... SubsetOfTags>\n  void assign_subset(const tuples::TaggedTuple<SubsetOfTags...>& vars) {\n    EXPAND_PACK_LEFT_TO_RIGHT([this, &vars]() {\n      if constexpr (tmpl::list_contains_v<tmpl::list<Tags...>, SubsetOfTags>) {\n        get<SubsetOfTags>(*this) = get<SubsetOfTags>(vars);\n      } else {\n        (void)this;\n        (void)vars;\n      }\n    }());\n  }\n  /// @}\n\n  /// Create a Variables from a subset of the `Tensor`s in this\n  /// Variables\n  template <typename SubsetOfTags>\n  Variables<SubsetOfTags> extract_subset() const {\n    Variables<SubsetOfTags> sub_vars(number_of_grid_points());\n    tmpl::for_each<SubsetOfTags>([&](const auto tag_v) {\n      using tag = tmpl::type_from<decltype(tag_v)>;\n      get<tag>(sub_vars) = get<tag>(*this);\n    });\n    return sub_vars;\n  }\n\n  /// Create a non-owning Variables referencing a subset of the\n  /// `Tensor`s in this Variables.  The referenced tensors must be\n  /// consecutive in this Variables's tags list.\n  ///\n  /// \\warning As with other appearances of non-owning variables, this\n  /// method can cast away constness.\n  template <typename SubsetOfTags>\n  Variables<SubsetOfTags> reference_subset() const {\n    if constexpr (std::is_same_v<SubsetOfTags, tmpl::list<>>) {\n      return {};\n    } else {\n      using subset_from_tags_list = tmpl::reverse_find<\n          tmpl::find<\n              tags_list,\n              std::is_same<tmpl::_1, tmpl::pin<tmpl::front<SubsetOfTags>>>>,\n          std::is_same<tmpl::_1, tmpl::pin<tmpl::back<SubsetOfTags>>>>;\n      static_assert(std::is_same_v<subset_from_tags_list, SubsetOfTags>,\n                    \"Tags passed to reference_subset must be consecutive tags \"\n                    \"in the original tags_list.\");\n\n      using preceeding_tags = tmpl::pop_back<tmpl::reverse_find<\n          tags_list,\n          std::is_same<tmpl::_1, tmpl::pin<tmpl::front<SubsetOfTags>>>>>;\n\n      static constexpr auto count_components = [](auto... tags) {\n        return (0_st + ... + tmpl::type_from<decltype(tags)>::type::size());\n      };\n      static constexpr size_t number_of_preceeding_components =\n          tmpl::as_pack<preceeding_tags>(count_components);\n      static constexpr size_t number_of_components_in_subset =\n          tmpl::as_pack<SubsetOfTags>(count_components);\n\n      return {const_cast<value_type*>(data()) +\n                  number_of_grid_points() * number_of_preceeding_components,\n              number_of_grid_points() * number_of_components_in_subset};\n    }\n  }\n\n  /// Create a non-owning version of this Variables with different\n  /// prefixes on the tensors.  Both sets of prefixes must have the\n  /// same tensor types.\n  ///\n  /// \\warning As with other appearances of non-owning variables, this\n  /// method can cast away constness.\n  template <typename WrappedVariables>\n  WrappedVariables reference_with_different_prefixes() const {\n    static_assert(\n        tmpl::all<tmpl::transform<\n            typename WrappedVariables::tags_list, tmpl::list<Tags...>,\n            std::is_same<tmpl::bind<db::remove_all_prefixes, tmpl::_1>,\n                         tmpl::bind<db::remove_all_prefixes, tmpl::_2>>>>::\n            value,\n        \"Unprefixed tags do not match!\");\n    static_assert(\n        tmpl::all<tmpl::transform<\n            typename WrappedVariables::tags_list, tmpl::list<Tags...>,\n            std::is_same<tmpl::bind<tmpl::type_from, tmpl::_1>,\n                         tmpl::bind<tmpl::type_from, tmpl::_2>>>>::value,\n        \"Tensor types do not match!\");\n    return {const_cast<value_type*>(data()), size()};\n  }\n\n  /// Converting constructor for an expression to a Variables class\n  // clang-tidy: mark as explicit (we want conversion to Variables)\n  template <typename VT, bool VF>\n  Variables(const blaze::DenseVector<VT, VF>& expression);  // NOLINT\n\n  template <typename VT, bool VF>\n  Variables& operator=(const blaze::DenseVector<VT, VF>& expression);\n\n  // Prevent the previous two declarations from implicitly converting\n  // DataVectors and similar to Variables.\n  template <typename T, typename VectorType, size_t StaticSize>\n  Variables(const VectorImpl<T, VectorType, StaticSize>&) = delete;\n  template <typename T, typename VectorType, size_t StaticSize>\n  Variables& operator=(const VectorImpl<T, VectorType, StaticSize>&) = delete;\n\n  template <typename... WrappedTags,\n            Requires<tmpl2::flat_all<std::is_same_v<\n                db::remove_all_prefixes<WrappedTags>,\n                db::remove_all_prefixes<Tags>>...>::value> = nullptr>\n  SPECTRE_ALWAYS_INLINE Variables& operator+=(\n      const Variables<tmpl::list<WrappedTags...>>& rhs) {\n    static_assert(\n        (std::is_same_v<typename Tags::type, typename WrappedTags::type> and\n         ...),\n        \"Tensor types do not match!\");\n    variable_data_ += rhs.variable_data_;\n    return *this;\n  }\n  template <typename VT, bool VF>\n  SPECTRE_ALWAYS_INLINE Variables& operator+=(\n      const blaze::Vector<VT, VF>& rhs) {\n    variable_data_ += rhs;\n    return *this;\n  }\n\n  template <typename... WrappedTags,\n            Requires<tmpl2::flat_all<std::is_same_v<\n                db::remove_all_prefixes<WrappedTags>,\n                db::remove_all_prefixes<Tags>>...>::value> = nullptr>\n  SPECTRE_ALWAYS_INLINE Variables& operator-=(\n      const Variables<tmpl::list<WrappedTags...>>& rhs) {\n    static_assert(\n        (std::is_same_v<typename Tags::type, typename WrappedTags::type> and\n         ...),\n        \"Tensor types do not match!\");\n    variable_data_ -= rhs.variable_data_;\n    return *this;\n  }\n  template <typename VT, bool VF>\n  SPECTRE_ALWAYS_INLINE Variables& operator-=(\n      const blaze::Vector<VT, VF>& rhs) {\n    variable_data_ -= rhs;\n    return *this;\n  }\n\n  SPECTRE_ALWAYS_INLINE Variables& operator*=(const value_type& rhs) {\n    variable_data_ *= rhs;\n    return *this;\n  }\n\n  SPECTRE_ALWAYS_INLINE Variables& operator/=(const value_type& rhs) {\n    variable_data_ /= rhs;\n    return *this;\n  }\n\n  template <\n      typename... WrappedTags,\n      Requires<\n          sizeof...(WrappedTags) == sizeof...(Tags) and\n          tmpl2::flat_all_v<std::is_same_v<db::remove_all_prefixes<WrappedTags>,\n                                           db::remove_all_prefixes<Tags>>...>\n#ifdef __CUDACC__\n          and tmpl2::flat_all_v<std::is_same_v<typename WrappedTags::type,\n                                               typename Tags::type>...>\n#endif\n          > = nullptr>\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator+(\n      const Variables<tmpl::list<WrappedTags...>>& lhs, const Variables& rhs) {\n#ifndef __CUDACC__\n    // nvcc incorrectly hits this static_assert and variants of it, but using\n    // the additional requires part above is fine.\n    static_assert(\n        (std::is_same_v<typename Tags::type, typename WrappedTags::type> and\n         ...),\n        \"Tensor types do not match!\");\n#endif\n    return lhs.get_variable_data() + rhs.variable_data_;\n  }\n  template <typename VT, bool VF>\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator+(\n      const blaze::DenseVector<VT, VF>& lhs, const Variables& rhs) {\n    return *lhs + rhs.variable_data_;\n  }\n  template <typename VT, bool VF>\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator+(\n      const Variables& lhs, const blaze::DenseVector<VT, VF>& rhs) {\n    return lhs.variable_data_ + *rhs;\n  }\n\n  template <\n      typename... WrappedTags,\n      Requires<\n          sizeof...(WrappedTags) == sizeof...(Tags) and\n          tmpl2::flat_all_v<std::is_same_v<db::remove_all_prefixes<WrappedTags>,\n                                           db::remove_all_prefixes<Tags>>...>\n#ifdef __CUDACC__\n          and tmpl2::flat_all_v<std::is_same_v<typename WrappedTags::type,\n                                               typename Tags::type>...>\n#endif\n          > = nullptr>\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator-(\n      const Variables<tmpl::list<WrappedTags...>>& lhs, const Variables& rhs) {\n#ifndef __CUDACC__\n    // nvcc incorrectly hits this static_assert and variants of it, but using\n    // the additional requires part above is fine.\n    static_assert(\n        (std::is_same_v<typename Tags::type, typename WrappedTags::type> and\n         ...),\n        \"Tensor types do not match!\");\n#endif\n    return lhs.get_variable_data() - rhs.variable_data_;\n  }\n  template <typename VT, bool VF>\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator-(\n      const blaze::DenseVector<VT, VF>& lhs, const Variables& rhs) {\n    return *lhs - rhs.variable_data_;\n  }\n  template <typename VT, bool VF>\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator-(\n      const Variables& lhs, const blaze::DenseVector<VT, VF>& rhs) {\n    return lhs.variable_data_ - *rhs;\n  }\n\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator*(const Variables& lhs,\n                                                        const value_type& rhs) {\n    return lhs.variable_data_ * rhs;\n  }\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator*(const value_type& lhs,\n                                                        const Variables& rhs) {\n    return lhs * rhs.variable_data_;\n  }\n\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator/(const Variables& lhs,\n                                                        const value_type& rhs) {\n    return lhs.variable_data_ / rhs;\n  }\n\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator-(const Variables& lhs) {\n    return -lhs.variable_data_;\n  }\n  friend SPECTRE_ALWAYS_INLINE decltype(auto) operator+(const Variables& lhs) {\n    return lhs.variable_data_;\n  }\n\n private:\n  /// @{\n  /*!\n   * \\brief Subscript operator\n   *\n   * The subscript operator is private since it should not be used directly.\n   * Mathematical operations should be done using the math operators provided.\n   * Since the internal ordering of variables is implementation defined there\n   * is no safe way to perform any operation that is not a linear combination of\n   * Variables. Retrieving a Tensor must be done via the `get()` function.\n   *\n   *  \\requires `i >= 0 and i < size()`\n   */\n  SPECTRE_ALWAYS_INLINE value_type& operator[](const size_type i) {\n    return variable_data_[i];\n  }\n  SPECTRE_ALWAYS_INLINE const value_type& operator[](const size_type i) const {\n    return variable_data_[i];\n  }\n  /// @}\n\n  void add_reference_variable_data();\n\n  friend bool operator==(const Variables& lhs, const Variables& rhs) {\n    return blaze::equal<blaze::strict>(lhs.variable_data_, rhs.variable_data_);\n  }\n\n  template <typename VT, bool TF>\n  friend bool operator==(const Variables& lhs,\n                         const blaze::DenseVector<VT, TF>& rhs) {\n    return blaze::equal<blaze::strict>(lhs.variable_data_, *rhs);\n  }\n\n  template <typename VT, bool TF>\n  friend bool operator==(const blaze::DenseVector<VT, TF>& lhs,\n                         const Variables& rhs) {\n    return blaze::equal<blaze::strict>(*lhs, rhs.variable_data_);\n  }\n\n  template <class FriendTags>\n  friend class Variables;\n\n  std::array<value_type, number_of_independent_components>\n      variable_data_impl_static_;\n  vector_type variable_data_impl_dynamic_{};\n  bool owning_{true};\n  size_t size_ = 0;\n  size_t number_of_grid_points_ = 0;\n\n  pointer_type variable_data_;\n  tuples::TaggedTuple<Tags...> reference_variable_data_;\n\n#if defined(__GNUC__) and not defined(__clang__) and __GNUC__ < 13\n  // This works around a linker error with old GCC versions producing\n  // undefined references to unique_ptr constructors.  (10 and 11 are\n  // known affected, 12 is untested.)\n  std::unique_ptr<value_type[]> unused_gcc_bug_{};\n#endif\n};\n\n// The above Variables implementation doesn't work for an empty parameter pack,\n// so specialize here.\ntemplate <>\nclass Variables<tmpl::list<>> {\n public:\n  using tags_list = tmpl::list<>;\n  static constexpr size_t number_of_independent_components = 0;\n  Variables() = default;\n  explicit Variables(const size_t /*number_of_grid_points*/) {}\n  template <typename T>\n  Variables(const T* /*pointer*/, const size_t /*size*/) {}\n  static constexpr size_t size() { return 0; }\n  static constexpr size_t number_of_grid_points() { return 0; }\n  void assign_subset(const Variables<tmpl::list<>>& /*unused*/) {}\n  void assign_subset(const tuples::TaggedTuple<>& /*unused*/) {}\n  // Initialization for empty variables should ignore any input.\n  void initialize(size_t /*number_of_grid_points*/) {}\n};\n\n// gcc8 screams when the empty Variables has pup as a member function, so we\n// declare pup as a free function here.\n// clang-tidy: runtime-references\nSPECTRE_ALWAYS_INLINE void pup(\n    PUP::er& /*p*/,                           // NOLINT\n    Variables<tmpl::list<>>& /* unused */) {  // NOLINT\n}\nSPECTRE_ALWAYS_INLINE void operator|(\n    PUP::er& /*p*/, Variables<tmpl::list<>>& /* unused */) {  // NOLINT\n}\n\nSPECTRE_ALWAYS_INLINE bool operator==(const Variables<tmpl::list<>>& /*lhs*/,\n                                      const Variables<tmpl::list<>>& /*rhs*/) {\n  return true;\n}\nSPECTRE_ALWAYS_INLINE bool operator!=(const Variables<tmpl::list<>>& /*lhs*/,\n                                      const Variables<tmpl::list<>>& /*rhs*/) {\n  return false;\n}\n\ninline std::ostream& operator<<(std::ostream& os,\n                                const Variables<tmpl::list<>>& /*d*/) {\n  return os << \"{}\";\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>::Variables() {\n  add_reference_variable_data();\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>::Variables(const size_t number_of_grid_points) {\n  initialize(number_of_grid_points);\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>::Variables(const size_t number_of_grid_points,\n                                          const value_type value) {\n  initialize(number_of_grid_points, value);\n}\n\ntemplate <typename... Tags>\nvoid Variables<tmpl::list<Tags...>>::initialize(\n    const size_t number_of_grid_points) {\n  if (number_of_grid_points_ == number_of_grid_points) {\n    return;\n  }\n  if (UNLIKELY(not is_owning())) {\n    ERROR(\"Variables::initialize cannot be called on a non-owning Variables.  \"\n          \"This likely happened because of an attempted resize.  The current \"\n          \"number of grid points is \" << number_of_grid_points_ << \" and the \"\n          \"requested number is \" << number_of_grid_points << \".\");\n  }\n  number_of_grid_points_ = number_of_grid_points;\n  size_ = number_of_grid_points * number_of_independent_components;\n  if (number_of_grid_points_ <= 1) {\n    variable_data_impl_dynamic_.clear();\n  } else {\n    variable_data_impl_dynamic_.destructive_resize(size_);\n  }\n  add_reference_variable_data();\n#if defined(SPECTRE_NAN_INIT)\n  std::fill(variable_data_.data(), variable_data_.data() + size_,\n            make_signaling_NaN<value_type>());\n#endif  // SPECTRE_NAN_INIT\n}\n\ntemplate <typename... Tags>\nvoid Variables<tmpl::list<Tags...>>::initialize(\n    const size_t number_of_grid_points, const value_type value) {\n  initialize(number_of_grid_points);\n  std::fill(variable_data_.data(), variable_data_.data() + size_, value);\n}\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>::Variables(\n    const Variables<tmpl::list<Tags...>>& rhs) {\n  initialize(rhs.number_of_grid_points());\n  variable_data_ =\n      static_cast<const blaze::Vector<pointer_type, transpose_flag>&>(\n          rhs.variable_data_);\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>& Variables<tmpl::list<Tags...>>::operator=(\n    const Variables<tmpl::list<Tags...>>& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  initialize(rhs.number_of_grid_points());\n  variable_data_ =\n      static_cast<const blaze::Vector<pointer_type, transpose_flag>&>(\n          rhs.variable_data_);\n  return *this;\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>::Variables(Variables<tmpl::list<Tags...>>&& rhs)\n    : variable_data_impl_dynamic_(std::move(rhs.variable_data_impl_dynamic_)),\n      owning_(rhs.owning_),\n      size_(rhs.size()),\n      number_of_grid_points_(rhs.number_of_grid_points()),\n      variable_data_(std::move(rhs.variable_data_)) {\n  if (number_of_grid_points_ == 1) {\n    variable_data_impl_static_ = std::move(rhs.variable_data_impl_static_);\n  }\n  rhs.variable_data_impl_dynamic_.clear();\n  rhs.owning_ = true;\n  rhs.size_ = 0;\n  rhs.number_of_grid_points_ = 0;\n  add_reference_variable_data();\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>& Variables<tmpl::list<Tags...>>::operator=(\n    Variables<tmpl::list<Tags...>>&& rhs) {\n  if (this == &rhs) {\n    return *this;\n  }\n  owning_ = rhs.owning_;\n  size_ = rhs.size_;\n  number_of_grid_points_ = std::move(rhs.number_of_grid_points_);\n  variable_data_ = std::move(rhs.variable_data_);\n  variable_data_impl_dynamic_ = std::move(rhs.variable_data_impl_dynamic_);\n  if (number_of_grid_points_ == 1) {\n    variable_data_impl_static_ = std::move(rhs.variable_data_impl_static_);\n  }\n\n  rhs.variable_data_impl_dynamic_.clear();\n  rhs.owning_ = true;\n  rhs.size_ = 0;\n  rhs.number_of_grid_points_ = 0;\n  add_reference_variable_data();\n  return *this;\n}\n\ntemplate <typename... Tags>\ntemplate <typename... WrappedTags,\n          Requires<tmpl2::flat_all<\n              std::is_same<db::remove_all_prefixes<WrappedTags>,\n                           db::remove_all_prefixes<Tags>>::value...>::value>>\nVariables<tmpl::list<Tags...>>::Variables(\n    const Variables<tmpl::list<WrappedTags...>>& rhs) {\n  static_assert(\n      (std::is_same_v<typename Tags::type, typename WrappedTags::type> and ...),\n      \"Tensor types do not match!\");\n  initialize(rhs.number_of_grid_points());\n  variable_data_ =\n      static_cast<const blaze::Vector<pointer_type, transpose_flag>&>(\n          rhs.variable_data_);\n}\n\ntemplate <typename... Tags>\ntemplate <typename... WrappedTags,\n          Requires<tmpl2::flat_all<\n              std::is_same<db::remove_all_prefixes<WrappedTags>,\n                           db::remove_all_prefixes<Tags>>::value...>::value>>\nVariables<tmpl::list<Tags...>>& Variables<tmpl::list<Tags...>>::operator=(\n    const Variables<tmpl::list<WrappedTags...>>& rhs) {\n  static_assert(\n      (std::is_same_v<typename Tags::type, typename WrappedTags::type> and ...),\n      \"Tensor types do not match!\");\n  initialize(rhs.number_of_grid_points());\n  variable_data_ =\n      static_cast<const blaze::Vector<pointer_type, transpose_flag>&>(\n          rhs.variable_data_);\n  return *this;\n}\n\ntemplate <typename... Tags>\ntemplate <typename... WrappedTags,\n          Requires<tmpl2::flat_all<\n              std::is_same<db::remove_all_prefixes<WrappedTags>,\n                           db::remove_all_prefixes<Tags>>::value...>::value>>\nVariables<tmpl::list<Tags...>>::Variables(\n    Variables<tmpl::list<WrappedTags...>>&& rhs)\n    : variable_data_impl_dynamic_(std::move(rhs.variable_data_impl_dynamic_)),\n      owning_(rhs.owning_),\n      size_(rhs.size()),\n      number_of_grid_points_(rhs.number_of_grid_points()),\n      variable_data_(std::move(rhs.variable_data_)) {\n  static_assert(\n      (std::is_same_v<typename Tags::type, typename WrappedTags::type> and ...),\n      \"Tensor types do not match!\");\n  if (number_of_grid_points_ == 1) {\n    variable_data_impl_static_ = std::move(rhs.variable_data_impl_static_);\n  }\n  rhs.variable_data_impl_dynamic_.clear();\n  rhs.size_ = 0;\n  rhs.owning_ = true;\n  rhs.number_of_grid_points_ = 0;\n  add_reference_variable_data();\n}\n\ntemplate <typename... Tags>\ntemplate <typename... WrappedTags,\n          Requires<tmpl2::flat_all<\n              std::is_same<db::remove_all_prefixes<WrappedTags>,\n                           db::remove_all_prefixes<Tags>>::value...>::value>>\nVariables<tmpl::list<Tags...>>& Variables<tmpl::list<Tags...>>::operator=(\n    Variables<tmpl::list<WrappedTags...>>&& rhs) {\n  static_assert(\n      (std::is_same_v<typename Tags::type, typename WrappedTags::type> and ...),\n      \"Tensor types do not match!\");\n  variable_data_ = std::move(rhs.variable_data_);\n  owning_ = rhs.owning_;\n  size_ = rhs.size_;\n  number_of_grid_points_ = std::move(rhs.number_of_grid_points_);\n  variable_data_impl_dynamic_ = std::move(rhs.variable_data_impl_dynamic_);\n  if (number_of_grid_points_ == 1) {\n    variable_data_impl_static_ = std::move(rhs.variable_data_impl_static_);\n  }\n\n  rhs.variable_data_impl_dynamic_.clear();\n  rhs.size_ = 0;\n  rhs.owning_ = true;\n  rhs.number_of_grid_points_ = 0;\n  add_reference_variable_data();\n  return *this;\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>::Variables(const pointer start,\n                                          const size_t size) {\n  set_data_ref(start, size);\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>::Variables(vector_type vector)\n    : variable_data_impl_dynamic_(std::move(vector)),\n      size_(variable_data_impl_dynamic_.size()),\n      number_of_grid_points_(size_ / number_of_independent_components) {\n  ASSERT(variable_data_impl_dynamic_.is_owning(),\n         \"Cannot use a non-owning vector as Variables storage.  To create \"\n         \"a non-owning variables, use the (pointer, size) constructor.\");\n  ASSERT(size_ % number_of_independent_components == 0,\n         \"The data size (\"\n             << size_\n             << \") must be a multiple of the number of independent components (\"\n             << number_of_independent_components << \").\");\n  if (number_of_grid_points_ == 1) {\n    std::copy(variable_data_impl_dynamic_.begin(),\n              variable_data_impl_dynamic_.end(),\n              variable_data_impl_static_.begin());\n    variable_data_impl_dynamic_.clear();\n  }\n  add_reference_variable_data();\n}\n\ntemplate <typename... Tags>\nauto Variables<tmpl::list<Tags...>>::release() && -> vector_type {\n  ASSERT(is_owning(), \"Cannot release storage from a non-owning Variables.\");\n  auto result = std::move(variable_data_impl_dynamic_);\n  if (number_of_grid_points_ == 1) {\n    result = decltype(result)(variable_data_impl_static_);\n  }\n  initialize(0);\n  return result;\n}\n\ntemplate <typename... Tags>\nvoid Variables<tmpl::list<Tags...>>::pup(PUP::er& p) {\n  ASSERT(\n      owning_,\n      \"Cannot pup a non-owning Variables! It may be reasonable to pack a \"\n      \"non-owning Variables, but not to unpack one. This should be discussed \"\n      \"in an issue with the core devs if the feature seems necessary.\");\n  size_t number_of_grid_points = number_of_grid_points_;\n  p | number_of_grid_points;\n  if (p.isUnpacking()) {\n    initialize(number_of_grid_points);\n  }\n  PUParray(p, variable_data_.data(), size_);\n}\n/// \\endcond\n\n/// @{\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Return Tag::type pointing into the contiguous array\n *\n * \\tparam Tag the variable to return\n */\ntemplate <typename Tag, typename TagList>\nconstexpr typename Tag::type& get(Variables<TagList>& v) {\n  static_assert(tmpl::list_contains_v<TagList, Tag>,\n                \"Could not retrieve Tag from Variables. See the first \"\n                \"template parameter of the instantiation for what Tag is \"\n                \"being retrieved and the second template parameter for \"\n                \"what Tags are available.\");\n  return tuples::get<Tag>(v.reference_variable_data_);\n}\ntemplate <typename Tag, typename TagList>\nconstexpr const typename Tag::type& get(const Variables<TagList>& v) {\n  static_assert(tmpl::list_contains_v<TagList, Tag>,\n                \"Could not retrieve Tag from Variables. See the first \"\n                \"template parameter of the instantiation for what Tag is \"\n                \"being retrieved and the second template parameter for \"\n                \"what Tags are available.\");\n  return tuples::get<Tag>(v.reference_variable_data_);\n}\n/// @}\n\ntemplate <typename... Tags>\ntemplate <typename VT, bool VF>\nVariables<tmpl::list<Tags...>>::Variables(\n    const blaze::DenseVector<VT, VF>& expression) {\n  ASSERT((*expression).size() % number_of_independent_components == 0,\n         \"Invalid size \" << (*expression).size() << \" for a Variables with \"\n         << number_of_independent_components << \" components.\");\n  initialize((*expression).size() / number_of_independent_components);\n  variable_data_ = expression;\n}\n\n/// \\cond\ntemplate <typename... Tags>\ntemplate <typename VT, bool VF>\nVariables<tmpl::list<Tags...>>& Variables<tmpl::list<Tags...>>::operator=(\n    const blaze::DenseVector<VT, VF>& expression) {\n  ASSERT((*expression).size() % number_of_independent_components == 0,\n         \"Invalid size \" << (*expression).size() << \" for a Variables with \"\n         << number_of_independent_components << \" components.\");\n  initialize((*expression).size() / number_of_independent_components);\n  variable_data_ = expression;\n  return *this;\n}\n/// \\endcond\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename... Tags>\nvoid Variables<tmpl::list<Tags...>>::add_reference_variable_data() {\n  if (is_owning()) {\n    if (number_of_grid_points_ == 0) {\n      variable_data_.clear();\n    } else if (number_of_grid_points_ == 1) {\n      variable_data_.reset(variable_data_impl_static_.data(), size_);\n    } else {\n      variable_data_.reset(variable_data_impl_dynamic_.data(), size_);\n    }\n  }\n  ASSERT(variable_data_.size() == size_ and\n             size_ == number_of_grid_points_ * number_of_independent_components,\n         \"Size mismatch: variable_data_.size() = \"\n             << variable_data_.size() << \" size_ = \" << size_ << \" should be: \"\n             << number_of_grid_points_ * number_of_independent_components\n             << \"\\nThis is an internal inconsistency bug in Variables. Please \"\n                \"file an issue.\");\n  size_t variable_offset = 0;\n  tmpl::for_each<tags_list>([this, &variable_offset](auto tag_v) {\n    using Tag = tmpl::type_from<decltype(tag_v)>;\n    auto& var = tuples::get<Tag>(reference_variable_data_);\n    for (size_t i = 0; i < Tag::type::size(); ++i) {\n      if (LIKELY(number_of_grid_points_ != 0)) {\n        var[i].set_data_ref(\n            &variable_data_[variable_offset++ * number_of_grid_points_],\n            number_of_grid_points_);\n      } else {\n        var[i].set_data_ref(nullptr, 0);\n      }\n    }\n  });\n}\n/// \\endcond\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>& operator*=(\n    Variables<tmpl::list<Tags...>>& lhs,\n    const typename Variables<tmpl::list<Tags...>>::vector_type& rhs) {\n  using value_type = typename Variables<tmpl::list<Tags...>>::value_type;\n  ASSERT(lhs.number_of_grid_points() == rhs.size(),\n         \"Size mismatch in multiplication: \" << lhs.number_of_grid_points()\n                                             << \" and \" << rhs.size());\n  value_type* const lhs_data = lhs.data();\n  const value_type* const rhs_data = rhs.data();\n  for (size_t c = 0; c < lhs.number_of_independent_components; ++c) {\n    for (size_t s = 0; s < lhs.number_of_grid_points(); ++s) {\n      // clang-tidy: do not use pointer arithmetic\n      lhs_data[c * lhs.number_of_grid_points() + s] *= rhs_data[s];  // NOLINT\n    }\n  }\n  return lhs;\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>> operator*(\n    const Variables<tmpl::list<Tags...>>& lhs,\n    const typename Variables<tmpl::list<Tags...>>::vector_type& rhs) {\n  auto result = lhs;\n  result *= rhs;\n  return result;\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>> operator*(\n    const typename Variables<tmpl::list<Tags...>>::vector_type& lhs,\n    const Variables<tmpl::list<Tags...>>& rhs) {\n  auto result = rhs;\n  result *= lhs;\n  return result;\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>>& operator/=(\n    Variables<tmpl::list<Tags...>>& lhs,\n    const typename Variables<tmpl::list<Tags...>>::vector_type& rhs) {\n  ASSERT(lhs.number_of_grid_points() == rhs.size(),\n         \"Size mismatch in multiplication: \" << lhs.number_of_grid_points()\n                                             << \" and \" << rhs.size());\n  using value_type = typename Variables<tmpl::list<Tags...>>::value_type;\n  value_type* const lhs_data = lhs.data();\n  const value_type* const rhs_data = rhs.data();\n  for (size_t c = 0; c < lhs.number_of_independent_components; ++c) {\n    for (size_t s = 0; s < lhs.number_of_grid_points(); ++s) {\n      // clang-tidy: do not use pointer arithmetic\n      lhs_data[c * lhs.number_of_grid_points() + s] /= rhs_data[s];  // NOLINT\n    }\n  }\n  return lhs;\n}\n\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>> operator/(\n    const Variables<tmpl::list<Tags...>>& lhs,\n    const typename Variables<tmpl::list<Tags...>>::vector_type& rhs) {\n  auto result = lhs;\n  result /= rhs;\n  return result;\n}\n\ntemplate <typename... Tags, Requires<sizeof...(Tags) != 0> = nullptr>\nstd::ostream& operator<<(std::ostream& os,\n                         const Variables<tmpl::list<Tags...>>& d) {\n  size_t count = 0;\n  const auto helper = [&os, &d, &count](auto tag_v) {\n    count++;\n    using Tag = typename decltype(tag_v)::type;\n    os << db::tag_name<Tag>() << \":\\n\";\n    os << get<Tag>(d);\n    if (count < sizeof...(Tags)) {\n      os << \"\\n\\n\";\n    }\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(tmpl::type_<Tags>{}));\n  return os;\n}\n\ntemplate <typename TagsList>\nbool operator!=(const Variables<TagsList>& lhs,\n                const Variables<TagsList>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename... TagsLhs, typename... TagsRhs,\n          Requires<not std::is_same<tmpl::list<TagsLhs...>,\n                                    tmpl::list<TagsRhs...>>::value> = nullptr>\nvoid swap(Variables<tmpl::list<TagsLhs...>>& lhs,\n          Variables<tmpl::list<TagsRhs...>>& rhs) {\n  Variables<tmpl::list<TagsLhs...>> temp{std::move(lhs)};\n  lhs = std::move(rhs);\n  rhs = std::move(temp);\n}\n\n/// \\ingroup DataStructuresGroup\n/// Construct a variables from the `Tensor`s in a `TaggedTuple`.\ntemplate <typename... Tags>\nVariables<tmpl::list<Tags...>> variables_from_tagged_tuple(\n    const tuples::TaggedTuple<Tags...>& tuple) {\n  auto result = make_with_value<Variables<tmpl::list<Tags...>>>(\n      get<tmpl::front<tmpl::list<Tags...>>>(tuple), 0.0);\n  result.assign_subset(tuple);\n  return result;\n}\n\nnamespace MakeWithValueImpls {\ntemplate <typename TagList>\nstruct MakeWithSize<Variables<TagList>> {\n  static SPECTRE_ALWAYS_INLINE Variables<TagList> apply(\n      const size_t size, const typename Variables<TagList>::value_type value) {\n    return Variables<TagList>(size, value);\n  }\n};\n\ntemplate <typename TagList>\nstruct NumberOfPoints<Variables<TagList>> {\n  static SPECTRE_ALWAYS_INLINE size_t apply(const Variables<TagList>& input) {\n    return input.number_of_grid_points();\n  }\n};\n}  // namespace MakeWithValueImpls\n\ntemplate <typename TagList>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n    Variables<TagList>> {\n  static constexpr bool is_trivial = false;\n  static SPECTRE_ALWAYS_INLINE void apply(\n      const gsl::not_null<Variables<TagList>*> result, const size_t size) {\n    result->initialize(size);\n  }\n};\n\nnamespace EqualWithinRoundoffImpls {\n// It would be nice to use `blaze::equal` in these implementations, but it\n// doesn't currently allow to specify a tolerance. See upstream issue:\n// https://bitbucket.org/blaze-lib/blaze/issues/417/adjust-relaxed-equal-accuracy\ntemplate <typename TagList, typename Floating>\nstruct EqualWithinRoundoffImpl<Variables<TagList>, Floating,\n                               Requires<std::is_floating_point_v<Floating>>> {\n  static bool apply(const Variables<TagList>& lhs, const Floating& rhs,\n                    const double eps, const double scale) {\n    for (size_t i = 0; i < lhs.size(); ++i) {\n      if (not equal_within_roundoff(lhs.data()[i], rhs, eps, scale)) {\n        return false;\n      }\n    }\n    return true;\n  }\n};\ntemplate <typename TagList, typename Floating>\nstruct EqualWithinRoundoffImpl<Floating, Variables<TagList>,\n                               Requires<std::is_floating_point_v<Floating>>> {\n  static SPECTRE_ALWAYS_INLINE bool apply(const Floating& lhs,\n                                          const Variables<TagList>& rhs,\n                                          const double eps,\n                                          const double scale) {\n    return equal_within_roundoff(rhs, lhs, eps, scale);\n  }\n};\ntemplate <typename LhsTagList, typename RhsTagList>\nstruct EqualWithinRoundoffImpl<Variables<LhsTagList>, Variables<RhsTagList>> {\n  static bool apply(const Variables<LhsTagList>& lhs,\n                    const Variables<RhsTagList>& rhs, const double eps,\n                    const double scale) {\n    ASSERT(lhs.size() == rhs.size(),\n           \"Can only compare two Variables of the same size, but lhs has size \"\n               << lhs.size() << \" and rhs has size \" << rhs.size() << \".\");\n    for (size_t i = 0; i < lhs.size(); ++i) {\n      if (not equal_within_roundoff(lhs.data()[i], rhs.data()[i], eps, scale)) {\n        return false;\n      }\n    }\n    return true;\n  }\n};\n}  // namespace EqualWithinRoundoffImpls\n\nnamespace db {\n// Enable subitems for ::Tags::Variables and derived tags (e.g. compute tags).\n// Other tags that hold a `Variables` don't expose the constituent tensors as\n// subitems by default.\nnamespace Variables_detail {\n// Check if the argument is a `::Tags::Variables`, or derived from it. Can't use\n// `tt:is_a_v` because we also want to match derived classes. Can't use\n// `std::is_base_of` because `::Tags::Variables` is a template.\ntemplate <typename TagsList>\nconstexpr std::true_type is_a_variables_tag(::Tags::Variables<TagsList>&&) {\n  return {};\n}\nconstexpr std::false_type is_a_variables_tag(...) { return {}; }\ntemplate <typename Tag>\nstatic constexpr bool is_a_variables_tag_v =\n    decltype(is_a_variables_tag(std::declval<Tag>()))::value;\n}  // namespace Variables_detail\ntemplate <typename Tag>\nstruct Subitems<Tag, Requires<Variables_detail::is_a_variables_tag_v<Tag>>> {\n  using type = typename Tag::type::tags_list;\n\n  template <typename Subtag, typename LocalTag = Tag>\n  static void create_item(\n      const gsl::not_null<typename LocalTag::type*> parent_value,\n      const gsl::not_null<typename Subtag::type*> sub_value) {\n    auto& vars = get<Subtag>(*parent_value);\n    // Only update the Tensor if the Variables has changed its allocation\n    if constexpr (not is_any_spin_weighted_v<typename Subtag::type::type>) {\n      if (vars.begin()->data() != sub_value->begin()->data()) {\n        for (auto vars_it = vars.begin(), sub_var_it = sub_value->begin();\n             vars_it != vars.end(); ++vars_it, ++sub_var_it) {\n          sub_var_it->set_data_ref(make_not_null(&*vars_it));\n        }\n      }\n    } else {\n      if (vars.begin()->data().data() != sub_value->begin()->data().data()) {\n        for (auto vars_it = vars.begin(), sub_var_it = sub_value->begin();\n             vars_it != vars.end(); ++vars_it, ++sub_var_it) {\n          sub_var_it->set_data_ref(make_not_null(&*vars_it));\n        }\n      }\n    }\n  }\n\n  template <typename Subtag>\n  static const typename Subtag::type& create_compute_item(\n      const typename Tag::type& parent_value) {\n    return get<Subtag>(parent_value);\n  }\n};\n}  // namespace db\n\nnamespace Variables_detail {\ntemplate <typename Tags>\nusing MathWrapperVectorType = tmpl::conditional_t<\n    std::is_same_v<typename Variables<Tags>::value_type, double>, DataVector,\n    ComplexDataVector>;\n}  // namespace Variables_detail\n\ntemplate <typename Tags>\nauto make_math_wrapper(const gsl::not_null<Variables<Tags>*> data) {\n  using Vector = Variables_detail::MathWrapperVectorType<Tags>;\n  Vector referencing(data->data(), data->size());\n  return make_math_wrapper(&referencing);\n}\n\ntemplate <typename Tags>\nauto make_math_wrapper(const Variables<Tags>& data) {\n  using Vector = Variables_detail::MathWrapperVectorType<Tags>;\n  const Vector referencing(\n      const_cast<typename Vector::value_type*>(data.data()), data.size());\n  return make_math_wrapper(referencing);\n}\n\ntemplate <typename Tags>\nauto into_math_wrapper_type(Variables<Tags>&& data) {\n  return into_math_wrapper_type(std::move(data).release());\n}\n\n/// \\cond\ntemplate <size_t I, class... Tags>\ninline constexpr typename tmpl::at_c<tmpl::list<Tags...>, I>::type&& get(\n    Variables<tmpl::list<Tags...>>&& t) {\n  return get<tmpl::at_c<tmpl::list<Tags...>, I>>(t);\n}\n\ntemplate <size_t I, class... Tags>\ninline constexpr const typename tmpl::at_c<tmpl::list<Tags...>, I>::type& get(\n    const Variables<tmpl::list<Tags...>>& t) {\n  return get<tmpl::at_c<tmpl::list<Tags...>, I>>(t);\n}\n\ntemplate <size_t I, class... Tags>\ninline constexpr typename tmpl::at_c<tmpl::list<Tags...>, I>::type& get(\n    Variables<tmpl::list<Tags...>>& t) {\n  return get<tmpl::at_c<tmpl::list<Tags...>, I>>(t);\n}\n/// \\endcond\n\nnamespace std {\ntemplate <typename... Tags>\nstruct tuple_size<Variables<tmpl::list<Tags...>>>\n    : std::integral_constant<int, sizeof...(Tags)> {};\ntemplate <size_t I, typename... Tags>\nstruct tuple_element<I, Variables<tmpl::list<Tags...>>> {\n  using type = typename tmpl::at_c<tmpl::list<Tags...>, I>::type;\n};\n}  // namespace std\n\ntemplate <typename TagsList>\nbool contains_allocations(const Variables<TagsList>& value) {\n  return value.number_of_grid_points() > 1;\n}\n\ninline bool contains_allocations(const Variables<tmpl::list<>>& /*value*/) {\n  return false;\n}\n"
  },
  {
    "path": "src/DataStructures/VariablesTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace Tags {\ntemplate <typename TagsList>\nstruct Variables : db::SimpleTag {\n  static_assert(tt::is_a<tmpl::list, TagsList>::value,\n                \"The TagsList passed to Tags::Variables is not a typelist\");\n  using tags_list = TagsList;\n  using type = ::Variables<TagsList>;\n  static std::string name() {\n    std::string tag_name{\"Variables(\"};\n    size_t iter = 0;\n    tmpl::for_each<TagsList>([&tag_name, &iter](auto tag) {\n      tag_name += db::tag_name<tmpl::type_from<decltype(tag)>>();\n      if (iter + 1 != tmpl::size<TagsList>::value) {\n        tag_name += \",\";\n      }\n      iter++;\n    });\n    return tag_name + \")\";\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/DataStructures/VectorImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <blaze/math/AlignmentFlag.h>\n#include <blaze/math/CustomVector.h>\n#include <blaze/math/DenseVector.h>\n#include <blaze/math/GroupTag.h>\n#include <blaze/math/PaddingFlag.h>\n#include <blaze/math/TransposeFlag.h>\n#include <cstddef>\n#include <cstring>\n#include <functional>\n#include <initializer_list>\n#include <limits>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/Blaze/StepFunction.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n#include \"Utilities/PrintHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TypeTraits/IsComplexOfFundamental.hpp\"\n#include \"Utilities/TypeTraits/IsStdArray.hpp\"\n\nclass ComplexDataVector;\nclass ComplexModalVector;\nclass DataVector;\nclass ModalVector;\n\nnamespace VectorImpl_detail {\n/// \\brief Whether or not a given vector type is assignable to another\n///\n/// \\details\n/// This is used to define which types can be assigned to one another. For\n/// example, you can assign a `ComplexDataVector` to a `DataVector`, but not\n/// vice versa.\n///\n/// To enable assignments between more types, modify a current template\n/// specialization or add a new one.\n///\n/// \\tparam LhsDataType the type being assigned\n/// \\tparam RhsDataType the type to convert to `LhsDataType`\ntemplate <typename LhsDataType, typename RhsDataType>\nstruct is_assignable;\n\n/// No template specialization was matched, so LHS is not assignable to RHS\ntemplate <typename LhsDataType, typename RhsDataType>\nstruct is_assignable : std::false_type {};\n/// Can assign a type to itself\ntemplate <typename RhsDataType>\nstruct is_assignable<RhsDataType, RhsDataType> : std::true_type {};\n/// Can assign a `ComplexDataVector` to a `DataVector`\ntemplate <>\nstruct is_assignable<ComplexDataVector, DataVector> : std::true_type {};\n/// Can assign a `ComplexModalVector` to a `ModalVector`\ntemplate <>\nstruct is_assignable<ComplexModalVector, ModalVector> : std::true_type {};\n\n/// \\brief Whether or not a given vector type is assignable to another\n///\n/// \\details\n/// See `is_assignable` for which assignments are permitted\ntemplate <typename LhsDataType, typename RhsDataType>\nconstexpr bool is_assignable_v = is_assignable<LhsDataType, RhsDataType>::value;\n}  // namespace VectorImpl_detail\n\n/// \\ingroup TensorExpressionsGroup\n/// \\brief Marks a class as being a `VectorImpl`\n///\n/// \\details\n/// The empty base class provides a simple means for checking if a type is a\n/// `VectorImpl`\nstruct MarkAsVectorImpl {};\n\n/// \\ingroup DataStructuresGroup\n/// Default static size for vector impl\nconstexpr size_t default_vector_impl_static_size = 0;\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Base class template for various DataVector and related types\n *\n * \\details The `VectorImpl` class is the generic parent class for vectors\n * representing collections of related function values, such as `DataVector`s\n * for contiguous data over a computational domain.\n *\n * The `VectorImpl` does not itself define any particular mathematical\n * operations on the contained values. The `VectorImpl` template class and the\n * macros defined in `VectorImpl.hpp` assist in the construction of various\n * derived classes supporting a chosen set of mathematical operations.\n *\n * In addition, the equivalence operator `==` is inherited from the underlying\n * `blaze::CustomVector` type, and returns true if and only if the size and\n * contents of the two compared vectors are equivalent.\n *\n * Template parameters:\n * - `T` is the underlying stored type, e.g. `double`, `std::complex<double>`,\n *   `float`, etc.\n * - `VectorType` is the type that should be associated with the VectorImpl\n *    during mathematical computations. In most cases, inherited types should\n *    have themselves as the second template argument, e.g.\n *  ```\n *  class DataVector : VectorImpl<double, DataVector> {\n *  ```\n * - `StaticSize` is the size for the static part of the vector. If the vector\n *   is constructed or resized with a size that is less than or equal to this\n *   StaticSize, no heap allocations will be done. It will instead use the stack\n *   allocation. Default is `default_vector_impl_static_size`.\n *\n *  The second template parameter communicates arithmetic type restrictions to\n *  the underlying Blaze framework. For example, if `VectorType` is\n *  `DataVector`, then the underlying architecture will prevent addition with a\n *  vector type whose `ResultType` (which is aliased to its `VectorType`) is\n *  `ModalVector`.  Since `DataVector`s and `ModalVector`s represent data in\n *  different spaces, we wish to forbid several operations between them. This\n *  vector-type-tracking through an expression prevents accidental mixing of\n *  vector types in math expressions.\n *\n * \\note\n * - If `SPECTRE_NAN_INIT` is defined, then the `VectorImpl` is default\n *   initialized to `signaling_NaN()`. Otherwise, the vector is filled with\n *   uninitialized memory for performance.\n */\ntemplate <typename T, typename VectorType,\n          size_t StaticSize = default_vector_impl_static_size>\nclass VectorImpl\n    : public blaze::CustomVector<\n          T, blaze::AlignmentFlag::unaligned, blaze::PaddingFlag::unpadded,\n          blaze::defaultTransposeFlag, blaze::GroupTag<0>, VectorType>,\n      MarkAsVectorImpl {\n public:\n  using value_type = T;\n  using size_type = size_t;\n  using difference_type = std::ptrdiff_t;\n  using BaseType = blaze::CustomVector<\n      T, blaze::AlignmentFlag::unaligned, blaze::PaddingFlag::unpadded,\n      blaze::defaultTransposeFlag, blaze::GroupTag<0>, VectorType>;\n  static constexpr bool transpose_flag = blaze::defaultTransposeFlag;\n  static constexpr size_t static_size = StaticSize;\n\n  using ElementType = T;\n  using TransposeType = VectorImpl<T, VectorType, StaticSize>;\n  using CompositeType = const VectorImpl<T, VectorType, StaticSize>&;\n  using iterator = typename BaseType::Iterator;\n  using const_iterator = typename BaseType::ConstIterator;\n\n  using BaseType::operator[];\n  using BaseType::begin;\n  using BaseType::cbegin;\n  using BaseType::cend;\n  using BaseType::data;\n  using BaseType::end;\n  using BaseType::size;\n\n  /// @{\n  /// Upcast to `BaseType`\n  /// \\attention\n  /// upcast should only be used when implementing a derived vector type, not in\n  /// calling code\n  const BaseType& operator*() const {\n    return static_cast<const BaseType&>(*this);\n  }\n  BaseType& operator*() { return static_cast<BaseType&>(*this); }\n  /// @}\n\n  /// Create with the given size. In debug mode, the vector is initialized to\n  /// 'NaN' by default. If not initialized to 'NaN', the memory is allocated but\n  /// not initialized.\n  ///\n  /// - `set_size` number of values\n  explicit VectorImpl(size_t set_size)\n      : owned_data_(heap_alloc_if_necessary(set_size)) {\n    reset_pointer_vector(set_size);\n#if defined(SPECTRE_NAN_INIT)\n    std::fill(data(), data() + set_size,\n              std::numeric_limits<value_type>::signaling_NaN());\n#endif  // SPECTRE_NAN_INIT\n  }\n\n  /// Create with the given size and value.\n  ///\n  /// - `set_size` number of values\n  /// - `value` the value to initialize each element\n  VectorImpl(size_t set_size, T value)\n      : owned_data_(heap_alloc_if_necessary(set_size)) {\n    reset_pointer_vector(set_size);\n    std::fill(data(), data() + set_size, value);\n  }\n\n  /// Create from a copy of the given container\n  ///\n  /// \\param container A container with a `value_type` that is the same as `T`.\n  /// Currently restricted to `std::vector<T>` and `std::array<T>`.\n  template <\n      typename Container,\n      Requires<std::is_same_v<typename Container::value_type, T>> = nullptr>\n  explicit VectorImpl(const Container& container)\n      : owned_data_(heap_alloc_if_necessary(container.size())) {\n    static_assert(std::is_same_v<Container, std::vector<T>> or\n                      tt::is_std_array_v<Container>,\n                  \"This constructor is currently restricted to std::vector and \"\n                  \"std::array out of caution.\");\n    reset_pointer_vector(container.size());\n    std::copy(container.begin(), container.end(), data());\n  }\n\n  /// Create a non-owning VectorImpl that points to `start`\n  VectorImpl(T* start, size_t set_size)\n      : BaseType(start, set_size), owning_(false) {}\n\n  /// Create from an initializer list of `T`.\n  template <class U, Requires<std::is_same_v<U, T>> = nullptr>\n  VectorImpl(std::initializer_list<U> list)\n      : owned_data_(heap_alloc_if_necessary(list.size())) {\n    reset_pointer_vector(list.size());\n    // Note: can't use memcpy with an initializer list.\n    std::copy(list.begin(), list.end(), data());\n  }\n\n  /// Empty VectorImpl\n  VectorImpl() = default;\n  /// \\cond HIDDEN_SYMBOLS\n  ~VectorImpl() = default;\n\n  VectorImpl(const VectorImpl<T, VectorType, StaticSize>& rhs);\n  VectorImpl& operator=(const VectorImpl<T, VectorType, StaticSize>& rhs);\n  VectorImpl(VectorImpl<T, VectorType, StaticSize>&& rhs);\n  VectorImpl& operator=(VectorImpl<T, VectorType, StaticSize>&& rhs);\n\n  // This is a converting constructor. clang-tidy complains that it's not\n  // explicit, but we want it to allow conversion.\n  // clang-tidy: mark as explicit (we want conversion to VectorImpl type)\n  template <typename VT, bool VF,\n            Requires<VectorImpl_detail::is_assignable_v<\n                VectorType, typename VT::ResultType>> = nullptr>\n  VectorImpl(const blaze::DenseVector<VT, VF>& expression);  // NOLINT\n\n  template <typename VT, bool VF>\n  VectorImpl& operator=(const blaze::DenseVector<VT, VF>& expression);\n  /// \\endcond\n\n  VectorImpl& operator=(const T& rhs);\n\n  decltype(auto) SPECTRE_ALWAYS_INLINE operator[](const size_t index) {\n    ASSERT(index < size(), \"Out-of-range access to element \"\n                               << index << \" of a size \" << size()\n                               << \" Blaze vector.\");\n    return BaseType::operator[](index);\n  }\n\n  decltype(auto) SPECTRE_ALWAYS_INLINE operator[](const size_t index) const {\n    ASSERT(index < size(), \"Out-of-range access to element \"\n                               << index << \" of a size \" << size()\n                               << \" Blaze vector.\");\n    return BaseType::operator[](index);\n  }\n\n  /// @{\n  /// Set the VectorImpl to be a reference to another VectorImpl object\n  void set_data_ref(gsl::not_null<VectorType*> rhs) {\n    set_data_ref(rhs->data(), rhs->size());\n  }\n\n  void set_data_ref(T* const start, const size_t set_size) {\n    clear();\n    if (start != nullptr) {\n      (**this).reset(start, set_size);\n    }\n    owning_ = false;\n  }\n  /// @}\n\n  /*!\n   * \\brief A common operation for checking the size and resizing a memory\n   * buffer if needed to ensure that it has the desired size. This operation is\n   * not permitted on a non-owning vector.\n   *\n   * \\note This utility should NOT be used when it is anticipated that the\n   *   supplied buffer will typically be the wrong size (in that case, suggest\n   *   either manual checking or restructuring so that resizing is less common).\n   *   This uses `UNLIKELY` to perform the check most quickly when the buffer\n   *   needs no resizing, but will be slower when resizing is common.\n   */\n  void SPECTRE_ALWAYS_INLINE destructive_resize(const size_t new_size) {\n    if (UNLIKELY(size() != new_size)) {\n      ASSERT(owning_,\n             MakeString{}\n                 << \"Attempting to resize a non-owning vector from size: \"\n                 << size() << \" to size: \" << new_size\n                 << \" but we may not destructively resize a non-owning vector\");\n      owned_data_ = heap_alloc_if_necessary(new_size);\n      reset_pointer_vector(new_size);\n    }\n  }\n\n  /// Returns true if the class owns the data\n  bool is_owning() const { return owning_; }\n\n  /// Put the class in the default-constructed state.\n  void clear();\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n protected:\n  std::unique_ptr<value_type[]> owned_data_{};\n  std::array<T, StaticSize> static_owned_data_{};\n  bool owning_{true};\n\n  // This should only be called if we are owning. If we are not owning, then\n  // neither owned_data_ or static_owned_data_ actually has the data we want.\n  SPECTRE_ALWAYS_INLINE void reset_pointer_vector(const size_t set_size) {\n    if (set_size == 0) {\n      BaseType::clear();\n      return;\n    }\n    if (owned_data_ == nullptr and set_size > StaticSize) {\n      ERROR(\n          \"VectorImpl::reset_pointer_vector cannot be called when owned_data_ \"\n          \"is nullptr.\");\n    }\n\n    if (set_size <= StaticSize) {\n      this->reset(static_owned_data_.data(), set_size);\n      // Free memory if downsizing\n      owned_data_ = nullptr;\n    } else {\n      this->reset(owned_data_.get(), set_size);\n    }\n  }\n\n  SPECTRE_ALWAYS_INLINE std::unique_ptr<value_type[]> heap_alloc_if_necessary(\n      const size_t set_size) {\n    return set_size > StaticSize\n               ? cpp20::make_unique_for_overwrite<value_type[]>(set_size)\n               : nullptr;\n  }\n};\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T, typename VectorType, size_t StaticSize>\nVectorImpl<T, VectorType, StaticSize>::VectorImpl(\n    const VectorImpl<T, VectorType, StaticSize>& rhs)\n    : BaseType{rhs}, owned_data_(heap_alloc_if_necessary(rhs.size())) {\n  reset_pointer_vector(rhs.size());\n  std::memcpy(data(), rhs.data(), size() * sizeof(value_type));\n}\n\ntemplate <typename T, typename VectorType, size_t StaticSize>\nVectorImpl<T, VectorType, StaticSize>&\nVectorImpl<T, VectorType, StaticSize>::operator=(\n    const VectorImpl<T, VectorType, StaticSize>& rhs) {\n  if (this != &rhs) {\n    if (owning_) {\n      if (size() != rhs.size()) {\n        owned_data_.reset();\n        owned_data_ = heap_alloc_if_necessary(rhs.size());\n      }\n      reset_pointer_vector(rhs.size());\n    } else {\n      ASSERT(rhs.size() == size(), \"Must copy into same size, not \"\n                                       << rhs.size() << \" into \" << size());\n    }\n    if (LIKELY(data() != rhs.data())) {\n      std::memcpy(data(), rhs.data(), size() * sizeof(value_type));\n    }\n  }\n  return *this;\n}\n\ntemplate <typename T, typename VectorType, size_t StaticSize>\nVectorImpl<T, VectorType, StaticSize>::VectorImpl(\n    VectorImpl<T, VectorType, StaticSize>&& rhs) {\n  owned_data_ = std::move(rhs.owned_data_);\n  static_owned_data_ = std::move(rhs.static_owned_data_);\n  **this = std::move(*rhs);\n  owning_ = rhs.owning_;\n  if (owning_) {\n    reset_pointer_vector(size());\n  } else {\n    this->reset(data(), size());\n  }\n  rhs.clear();\n}\n\ntemplate <typename T, typename VectorType, size_t StaticSize>\nVectorImpl<T, VectorType, StaticSize>&\nVectorImpl<T, VectorType, StaticSize>::operator=(\n    VectorImpl<T, VectorType, StaticSize>&& rhs) {\n  ASSERT(rhs.is_owning(),\n         \"Cannot move assign from a non-owning vector, because the correct \"\n         \"behavior is unclear.\");\n  if (this != &rhs) {\n    if (owning_) {\n      owned_data_ = std::move(rhs.owned_data_);\n      static_owned_data_ = std::move(rhs.static_owned_data_);\n      **this = std::move(*rhs);\n      reset_pointer_vector(size());\n      rhs.clear();\n    } else {\n      ASSERT(rhs.size() == size(), \"Must move into same size, not \"\n                                       << rhs.size() << \" into \" << size());\n      if (LIKELY(data() != rhs.data())) {\n        std::memcpy(data(), rhs.data(), size() * sizeof(value_type));\n        rhs.clear();\n      }\n    }\n  }\n  return *this;\n}\n\n// This is a converting constructor. clang-tidy complains that it's not\n// explicit, but we want it to allow conversion.\n// clang-tidy: mark as explicit (we want conversion to VectorImpl)\ntemplate <typename T, typename VectorType, size_t StaticSize>\ntemplate <typename VT, bool VF,\n          Requires<VectorImpl_detail::is_assignable_v<VectorType,\n                                                      typename VT::ResultType>>>\nVectorImpl<T, VectorType, StaticSize>::VectorImpl(\n    const blaze::DenseVector<VT, VF>& expression)  // NOLINT\n    : owned_data_(heap_alloc_if_necessary((*expression).size())) {\n  static_assert(\n      VectorImpl_detail::is_assignable_v<VectorType, typename VT::ResultType>,\n      \"Cannot construct the VectorImpl type from the given expression type.\");\n  reset_pointer_vector((*expression).size());\n  **this = expression;\n}\n\ntemplate <typename T, typename VectorType, size_t StaticSize>\ntemplate <typename VT, bool VF>\nVectorImpl<T, VectorType, StaticSize>&\nVectorImpl<T, VectorType, StaticSize>::operator=(\n    const blaze::DenseVector<VT, VF>& expression) {\n  static_assert(\n      VectorImpl_detail::is_assignable_v<VectorType, typename VT::ResultType>,\n      \"Cannot assign to the VectorImpl type from the given expression type.\");\n  if (owning_ and (*expression).size() != size()) {\n    owned_data_ = heap_alloc_if_necessary((*expression).size());\n    reset_pointer_vector((*expression).size());\n  } else if (not owning_) {\n    ASSERT((*expression).size() == size(), \"Must assign into same size, not \"\n                                               << (*expression).size()\n                                               << \" into \" << size());\n  }\n  **this = expression;\n  return *this;\n}\n/// \\endcond\n\n// The case of assigning a type apart from the same VectorImpl or a\n// `blaze::DenseVector` forwards the assignment to the `blaze::CustomVector`\n// base type. In the case of a single compatible value, this fills the vector\n// with that value.\ntemplate <typename T, typename VectorType, size_t StaticSize>\nVectorImpl<T, VectorType, StaticSize>&\nVectorImpl<T, VectorType, StaticSize>::operator=(const T& rhs) {\n  **this = rhs;\n  return *this;\n}\n\ntemplate <typename T, typename VectorType, size_t StaticSize>\nvoid VectorImpl<T, VectorType, StaticSize>::clear() {\n  BaseType::clear();\n  owning_ = true;\n  owned_data_.reset();\n  // The state of static_owned_data_ doesn't matter.\n}\n\ntemplate <typename T, typename VectorType, size_t StaticSize>\nvoid VectorImpl<T, VectorType, StaticSize>::pup(PUP::er& p) {  // NOLINT\n  if (not owning_ and p.isSizing()) {\n    return;\n  }\n  ASSERT(owning_, \"Cannot pup a non-owning vector!\");\n  auto my_size = size();\n  p | my_size;\n  if (my_size > 0) {\n    if (p.isUnpacking()) {\n      owning_ = true;\n      owned_data_ = heap_alloc_if_necessary(my_size);\n      reset_pointer_vector(my_size);\n    }\n    PUParray(p, data(), size());\n  }\n}\n\n/// Output operator for VectorImpl\ntemplate <typename T, typename VectorType, size_t StaticSize>\nstd::ostream& operator<<(std::ostream& os,\n                         const VectorImpl<T, VectorType, StaticSize>& d) {\n  sequence_print_helper(os, d.begin(), d.end());\n  return os;\n}\n\n#define DECLARE_GENERAL_VECTOR_BLAZE_TRAITS(VECTOR_TYPE)         \\\n  template <>                                                    \\\n  struct IsDenseVector<VECTOR_TYPE> : public blaze::TrueType {}; \\\n                                                                 \\\n  template <>                                                    \\\n  struct IsVector<VECTOR_TYPE> : public blaze::TrueType {};      \\\n                                                                 \\\n  template <>                                                    \\\n  struct CustomTransposeType<VECTOR_TYPE> {                      \\\n    using Type = VECTOR_TYPE;                                    \\\n  }\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Instructs Blaze to provide the appropriate vector result type after\n * math operations. This is accomplished by specializing Blaze's type traits\n * that are used for handling return type deduction and specifying the `using\n * Type =` nested type alias in the traits.\n *\n * \\param VECTOR_TYPE The vector type, which matches the type of the operation\n * result (e.g. `DataVector`)\n *\n * \\param BLAZE_MATH_TRAIT The blaze trait/expression for which you want to\n * specify the return type (e.g. `AddTrait`).\n */\n#define BLAZE_TRAIT_SPECIALIZE_BINARY_TRAIT(VECTOR_TYPE, BLAZE_MATH_TRAIT) \\\n  template <>                                                              \\\n  struct BLAZE_MATH_TRAIT<VECTOR_TYPE, VECTOR_TYPE> {                      \\\n    using Type = VECTOR_TYPE;                                              \\\n  };                                                                       \\\n  template <>                                                              \\\n  struct BLAZE_MATH_TRAIT<VECTOR_TYPE, VECTOR_TYPE::value_type> {          \\\n    using Type = VECTOR_TYPE;                                              \\\n  };                                                                       \\\n  template <>                                                              \\\n  struct BLAZE_MATH_TRAIT<VECTOR_TYPE::value_type, VECTOR_TYPE> {          \\\n    using Type = VECTOR_TYPE;                                              \\\n  }\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Instructs Blaze to provide the appropriate vector result type of an\n * operator between `VECTOR_TYPE` and `COMPATIBLE`, where the operation is\n * represented by `BLAZE_MATH_TRAIT`\n *\n * \\param VECTOR_TYPE The vector type, which matches the type of the operation\n * result (e.g. `ComplexDataVector`)\n *\n * \\param COMPATIBLE the type for which you want math operations to work with\n * `VECTOR_TYPE` smoothly (e.g. `DataVector`)\n *\n * \\param BLAZE_MATH_TRAIT The blaze trait for which you want declare the Type\n * field (e.g. `AddTrait`)\n *\n * \\param RESULT_TYPE The type which should be used as the 'return' type for the\n * binary operation\n */\n#define BLAZE_TRAIT_SPECIALIZE_COMPATIBLE_BINARY_TRAIT(     \\\n    VECTOR_TYPE, COMPATIBLE, BLAZE_MATH_TRAIT, RESULT_TYPE) \\\n  template <>                                               \\\n  struct BLAZE_MATH_TRAIT<VECTOR_TYPE, COMPATIBLE> {        \\\n    using Type = RESULT_TYPE;                               \\\n  };                                                        \\\n  template <>                                               \\\n  struct BLAZE_MATH_TRAIT<COMPATIBLE, VECTOR_TYPE> {        \\\n    using Type = RESULT_TYPE;                               \\\n  }\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Instructs Blaze to provide the appropriate vector result type of\n * arithmetic operations for `VECTOR_TYPE`. This is accomplished by specializing\n * Blaze's type traits that are used for handling return type deduction.\n *\n * \\details Type definitions here are suitable for contiguous data\n * (e.g. `DataVector`), but this macro might need to be tweaked for other types\n * of data, for instance Fourier coefficients.\n *\n * \\param VECTOR_TYPE The vector type, which for the arithmetic operations is\n * the type of the operation result (e.g. `DataVector`)\n */\n#define VECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(VECTOR_TYPE) \\\n  template <>                                                        \\\n  struct TransposeFlag<VECTOR_TYPE>                                  \\\n      : BoolConstant<VECTOR_TYPE::transpose_flag> {};                \\\n  BLAZE_TRAIT_SPECIALIZE_BINARY_TRAIT(VECTOR_TYPE, AddTrait);        \\\n  BLAZE_TRAIT_SPECIALIZE_BINARY_TRAIT(VECTOR_TYPE, SubTrait);        \\\n  BLAZE_TRAIT_SPECIALIZE_BINARY_TRAIT(VECTOR_TYPE, MultTrait);       \\\n  BLAZE_TRAIT_SPECIALIZE_BINARY_TRAIT(VECTOR_TYPE, DivTrait)\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Instructs Blaze to provide the appropriate vector result type of `Map`\n * operations (unary and binary) acting on `VECTOR_TYPE`. This is accomplished\n * by specializing Blaze's type traits that are used for handling return type\n * deduction.\n *\n * \\details Type declarations here are suitable for contiguous data (e.g.\n * `DataVector`), but this macro might need to be tweaked for other types of\n * data, for instance Fourier coefficients.\n *\n * \\param VECTOR_TYPE The vector type, which for the `Map` operations is\n * the type of the operation result (e.g. `DataVector`)\n */\n#define VECTOR_BLAZE_TRAIT_SPECIALIZE_ALL_MAP_TRAITS(VECTOR_TYPE) \\\n  template <typename Operator>                                    \\\n  struct MapTrait<VECTOR_TYPE, Operator> {                        \\\n    using Type = VECTOR_TYPE;                                     \\\n  };                                                              \\\n  template <typename Operator>                                    \\\n  struct MapTrait<VECTOR_TYPE, VECTOR_TYPE, Operator> {           \\\n    using Type = VECTOR_TYPE;                                     \\\n  }\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Defines the set of binary operations often supported for\n * `std::array<VECTOR_TYPE, size>`, for arbitrary `size`.\n *\n *  \\param VECTOR_TYPE The vector type (e.g. `DataVector`)\n */\n#define MAKE_STD_ARRAY_VECTOR_BINOPS(VECTOR_TYPE)                            \\\n  DEFINE_STD_ARRAY_BINOP(VECTOR_TYPE, VECTOR_TYPE::value_type,               \\\n                         VECTOR_TYPE, operator+, std::plus<>())              \\\n  DEFINE_STD_ARRAY_BINOP(VECTOR_TYPE, VECTOR_TYPE,                           \\\n                         VECTOR_TYPE::value_type, operator+, std::plus<>())  \\\n  DEFINE_STD_ARRAY_BINOP(VECTOR_TYPE, VECTOR_TYPE, VECTOR_TYPE, operator+,   \\\n                         std::plus<>())                                      \\\n                                                                             \\\n  DEFINE_STD_ARRAY_BINOP(VECTOR_TYPE, VECTOR_TYPE::value_type,               \\\n                         VECTOR_TYPE, operator-, std::minus<>())             \\\n  DEFINE_STD_ARRAY_BINOP(VECTOR_TYPE, VECTOR_TYPE,                           \\\n                         VECTOR_TYPE::value_type, operator-, std::minus<>()) \\\n  DEFINE_STD_ARRAY_BINOP(VECTOR_TYPE, VECTOR_TYPE, VECTOR_TYPE, operator-,   \\\n                         std::minus<>())                                     \\\n                                                                             \\\n  DEFINE_STD_ARRAY_INPLACE_BINOP(VECTOR_TYPE, VECTOR_TYPE, operator-=,       \\\n                                 std::minus<>())                             \\\n  DEFINE_STD_ARRAY_INPLACE_BINOP(                                            \\\n      VECTOR_TYPE, VECTOR_TYPE::value_type, operator-=, std::minus<>())      \\\n  DEFINE_STD_ARRAY_INPLACE_BINOP(VECTOR_TYPE, VECTOR_TYPE, operator+=,       \\\n                                 std::plus<>())                              \\\n  DEFINE_STD_ARRAY_INPLACE_BINOP(                                            \\\n      VECTOR_TYPE, VECTOR_TYPE::value_type, operator+=, std::plus<>())\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Defines the `MakeWithValueImpl` `apply` specialization\n *\n * \\details The `MakeWithValueImpl<VECTOR_TYPE, VECTOR_TYPE>` member\n * `apply(VECTOR_TYPE, VECTOR_TYPE::value_type)` specialization defined by this\n * macro produces an object with the same size as the `input` argument,\n * initialized with the `value` argument in every entry.\n *\n * \\param VECTOR_TYPE The vector type (e.g. `DataVector`)\n */\n#define MAKE_WITH_VALUE_IMPL_DEFINITION_FOR(VECTOR_TYPE)                      \\\n  namespace MakeWithValueImpls {                                              \\\n  template <>                                                                 \\\n  struct NumberOfPoints<VECTOR_TYPE> {                                        \\\n    static SPECTRE_ALWAYS_INLINE size_t apply(const VECTOR_TYPE& input) {     \\\n      return input.size();                                                    \\\n    }                                                                         \\\n  };                                                                          \\\n  template <>                                                                 \\\n  struct MakeWithSize<VECTOR_TYPE> {                                          \\\n    static SPECTRE_ALWAYS_INLINE VECTOR_TYPE                                  \\\n    apply(const size_t size, const VECTOR_TYPE::value_type value) {           \\\n      return VECTOR_TYPE(size, value);                                        \\\n    }                                                                         \\\n  };                                                                          \\\n  } /* namespace MakeWithValueImpls */                                        \\\n  template <>                                                                 \\\n  struct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<VECTOR_TYPE> { \\\n    static constexpr bool is_trivial = false;                                 \\\n    static SPECTRE_ALWAYS_INLINE void apply(                                  \\\n        const gsl::not_null<VECTOR_TYPE*> result, const size_t size) {        \\\n      result->destructive_resize(size);                                       \\\n    }                                                                         \\\n  };\n\n/// @{\n/*!\n * \\ingroup DataStructuresGroup\n * \\ingroup TypeTraitsGroup\n * \\brief Helper struct to determine the element type of a VectorImpl or\n * container of VectorImpl\n *\n * \\details Extracts the element type of a `VectorImpl`, a std::array of\n * `VectorImpl`, or a reference or pointer to a `VectorImpl`. In any of these\n * cases, the `type` member is defined as the `ElementType` of the `VectorImpl`\n * in question. If, instead, `get_vector_element_type` is passed an arithmetic\n * or complex arithemetic type, the `type` member is defined as the passed type.\n *\n * \\snippet DataStructures/Test_VectorImpl.cpp get_vector_element_type_example\n */\n// cast to bool needed to avoid the compiler mistaking the type to be determined\n// by T\ntemplate <typename T,\n          bool = static_cast<bool>(tt::is_complex_of_fundamental_v<T> or\n                                   std::is_fundamental_v<T>)>\nstruct get_vector_element_type;\ntemplate <typename T>\nstruct get_vector_element_type<T, true> {\n  using type = T;\n};\ntemplate <typename T>\nstruct get_vector_element_type<const T, false> {\n  using type = typename get_vector_element_type<T>::type;\n};\ntemplate <typename T>\nstruct get_vector_element_type<T, false> {\n  using type = typename get_vector_element_type<\n      typename T::ResultType::ElementType>::type;\n};\ntemplate <typename T>\nstruct get_vector_element_type<T*, false> {\n  using type = typename get_vector_element_type<T>::type;\n};\ntemplate <typename T>\nstruct get_vector_element_type<T&, false> {\n  using type = typename get_vector_element_type<T>::type;\n};\ntemplate <typename T, size_t S>\nstruct get_vector_element_type<std::array<T, S>, false> {\n  using type = typename get_vector_element_type<T>::type;\n};\n/// @}\n\ntemplate <typename T>\nusing get_vector_element_type_t = typename get_vector_element_type<T>::type;\n\nnamespace detail {\ntemplate <typename T, typename VectorType, size_t StaticSize>\nstd::true_type is_derived_of_vector_impl_impl(\n    const VectorImpl<T, VectorType, StaticSize>*);\n\nstd::false_type is_derived_of_vector_impl_impl(...);\n}  // namespace detail\n\n/// \\ingroup TypeTraitsGroup\n/// This is `std::true_type` if the provided type possesses an implicit\n/// conversion to any `VectorImpl`, which is the primary feature of SpECTRE\n/// vectors generally. Otherwise, it is `std::false_type`.\ntemplate <typename T>\nusing is_derived_of_vector_impl =\n    decltype(detail::is_derived_of_vector_impl_impl(std::declval<T*>()));\n\ntemplate <typename T>\nconstexpr bool is_derived_of_vector_impl_v =\n    is_derived_of_vector_impl<T>::value;\n\n// impose strict equality for derived classes of VectorImpl; note that this\n// overrides intended behavior in blaze for comparison operators to use\n// approximate equality in favor of equality between containers being\n// appropriately recursive. This form primarily works by using templates to\n// ensure that our comparison operator is resolved with higher priority than the\n// blaze form as of blaze 3.8\ntemplate <\n    typename Lhs, typename Rhs,\n    Requires<(is_derived_of_vector_impl_v<Lhs> or\n              is_derived_of_vector_impl_v<\n                  Rhs>)and not(std::is_base_of_v<blaze::Computation, Lhs> or\n                               std::is_base_of_v<blaze::Computation, Rhs>) and\n             not(std::is_same_v<Rhs, typename Lhs::ElementType> or\n                 std::is_same_v<Lhs, typename Rhs::ElementType>)> = nullptr>\nbool operator==(const Lhs& lhs, const Rhs& rhs) {\n  return blaze::equal<blaze::strict>(lhs, rhs);\n}\n\ntemplate <\n    typename Lhs, typename Rhs,\n    Requires<(is_derived_of_vector_impl_v<Lhs> or\n              is_derived_of_vector_impl_v<\n                  Rhs>)and not(std::is_base_of_v<blaze::Computation, Lhs> or\n                               std::is_base_of_v<blaze::Computation, Rhs>) and\n             not(std::is_same_v<Rhs, typename Lhs::ElementType> or\n                 std::is_same_v<Lhs, typename Lhs::ElementType>)> = nullptr>\nbool operator!=(const Lhs& lhs, const Rhs& rhs) {\n  return not(lhs == rhs);\n}\n\n// Impose strict equality for any expression templates; note that\n// this overrides intended behavior in blaze for comparison\n// operators to use approximate equality in favor of equality\n// between containers being appropriately recursive. This form\n// primarily works by using templates to ensure that our\n// comparison operator is resolved with higher priority than the\n// blaze form as of blaze 3.8\ntemplate <typename Lhs, typename Rhs,\n          Requires<std::is_base_of_v<blaze::Computation, Lhs> or\n                   std::is_base_of_v<blaze::Computation, Rhs>> = nullptr>\nbool operator==(const Lhs& lhs, const Rhs& rhs) {\n  return blaze::equal<blaze::strict>(lhs, rhs);\n}\n\ntemplate <typename Lhs, typename Rhs,\n          Requires<std::is_base_of_v<blaze::Computation, Lhs> or\n                   std::is_base_of_v<blaze::Computation, Rhs>> = nullptr>\nbool operator!=(const Lhs& lhs, const Rhs& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Lhs, Requires<is_derived_of_vector_impl_v<Lhs>> = nullptr>\nbool operator==(const Lhs& lhs, const typename Lhs::ElementType& rhs) {\n  for (const auto& element : lhs) {\n    if (element != rhs) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename Lhs, Requires<is_derived_of_vector_impl_v<Lhs>> = nullptr>\nbool operator!=(const Lhs& lhs, const typename Lhs::ElementType& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Rhs, Requires<is_derived_of_vector_impl_v<Rhs>> = nullptr>\nbool operator==(const typename Rhs::ElementType& lhs, const Rhs& rhs) {\n  return rhs == lhs;\n}\n\ntemplate <typename Rhs, Requires<is_derived_of_vector_impl_v<Rhs>> = nullptr>\nbool operator!=(const typename Rhs::ElementType& lhs, const Rhs& rhs) {\n  return not(lhs == rhs);\n}\n\n/// \\ingroup DataStructuresGroup\n/// Make the input `view` a `const` view of the const data `vector`, at\n/// offset `offset` and length `extent`.\n///\n/// \\warning This DOES modify the (const) input `view`. The reason `view` is\n/// taken by const pointer is to try to insist that the object to be a `const`\n/// view is actually const. Of course, there are ways of subverting this\n/// intended functionality and editing the data pointed into by `view` after\n/// this function is called; doing so is highly discouraged and results in\n/// undefined behavior.\ntemplate <typename VectorType,\n          Requires<is_derived_of_vector_impl_v<VectorType>> = nullptr>\nvoid make_const_view(const gsl::not_null<const VectorType*> view,\n                     const VectorType& vector, const size_t offset,\n                     const size_t extent) {\n  const_cast<VectorType*>(view.get())  // NOLINT\n      ->set_data_ref(\n          const_cast<typename VectorType::value_type*>(vector.data())  // NOLINT\n              + offset,                                                // NOLINT\n          extent);\n}\n\ntemplate <typename T, typename VectorType, size_t StaticSize>\ninline bool contains_allocations(\n    const VectorImpl<T, VectorType, StaticSize>& value) {\n  return value.size() > StaticSize and value.is_owning();\n}\n"
  },
  {
    "path": "src/Domain/Amr/Amr.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup AmrGroup\n/// \\brief Items for adaptive mesh refinement\nnamespace amr {}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Amr)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Flag.cpp\n  Helpers.cpp\n  Info.cpp\n  NeighborsOfChild.cpp\n  NeighborsOfParent.cpp\n  NewNeighborIds.cpp\n  UpdateAmrDecision.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Amr.hpp\n  Flag.hpp\n  Helpers.hpp\n  Info.hpp\n  NeighborsOfChild.hpp\n  NeighborsOfParent.hpp\n  NewNeighborIds.hpp\n  UpdateAmrDecision.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Domain\n  DomainStructure\n  ErrorHandling\n  Utilities\n  PUBLIC\n  Boost::boost\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/Domain/Amr/Flag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Amr/Flag.hpp\"\n\n#include <ostream>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\nstd::vector<amr::Flag> known_amr_flags() {\n  return std::vector{amr::Flag::Undefined,          amr::Flag::Join,\n                     amr::Flag::DecreaseResolution, amr::Flag::DoNothing,\n                     amr::Flag::IncreaseResolution, amr::Flag::Split};\n}\n}  // namespace\n\nnamespace amr {\n\nstd::ostream& operator<<(std::ostream& os, const Flag& flag) {\n  switch (flag) {\n    case Flag::Undefined:\n      os << \"Undefined\";\n      break;\n    case Flag::Join:\n      os << \"Join\";\n      break;\n    case Flag::DecreaseResolution:\n      os << \"DecreaseResolution\";\n      break;\n    case Flag::DoNothing:\n      os << \"DoNothing\";\n      break;\n    case Flag::IncreaseResolution:\n      os << \"IncreaseResolution\";\n      break;\n    case Flag::Split:\n      os << \"Split\";\n      break;\n    default:\n      ERROR(\"An unknown flag was passed to the stream operator. \"\n            << static_cast<int>(flag));\n  }\n  return os;\n}\n}  // namespace amr\n\ntemplate <>\namr::Flag Options::create_from_yaml<amr::Flag>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  for (const auto flag : known_amr_flags()) {\n    if (type_read == get_output(flag)) {\n      return flag;\n    }\n  }\n  using ::operator<<;\n  PARSE_ERROR(options.context(), \"Failed to convert \\\"\"\n                                     << type_read\n                                     << \"\\\" to amr::Flag.\\nMust be one of \"\n                                     << known_amr_flags() << \".\");\n}\n"
  },
  {
    "path": "src/Domain/Amr/Flag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines enum class amr::Flag.\n\n#pragma once\n\n#include <iosfwd>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace amr {\n\n/// \\ingroup AmrGroup\n/// \\brief Flags that represent decisions about mesh refinement\n///\n/// In order to support anisotropic mesh refinement, a flag is specified for\n/// each dimension.\nenum class Flag {\n  Undefined,          /**< used to initialize flags before a decision is made */\n  Join,               /**< join the sibling of an Element */\n  DecreaseResolution, /**< decrease number of points in an Element */\n  DoNothing,          /**< stay the same */\n  IncreaseResolution, /**< increase number of points in an Element */\n  Split               /**< split the Element into two smaller elements */\n};\n\n/// Output operator for a Flag.\nstd::ostream& operator<<(std::ostream& os, const Flag& flag);\n}  // namespace amr\n\ntemplate <>\nstruct Options::create_from_yaml<amr::Flag> {\n  template <typename Metavariables>\n  static amr::Flag create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\namr::Flag Options::create_from_yaml<amr::Flag>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Domain/Amr/Helpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Amr/Helpers.hpp\"\n\n#include <array>\n#include <boost/rational.hpp>\n#include <cstddef>\n#include <deque>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace amr {\ntemplate <size_t VolumeDim>\nstd::array<size_t, VolumeDim> desired_refinement_levels(\n    const ElementId<VolumeDim>& id, const std::array<Flag, VolumeDim>& flags) {\n  std::array<size_t, VolumeDim> result = id.refinement_levels();\n\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    ASSERT(Flag::Undefined != gsl::at(flags, d),\n           \"Undefined Flag in dimension \" << d);\n    if (Flag::Join == gsl::at(flags, d)) {\n      --gsl::at(result, d);\n    } else if (Flag::Split == gsl::at(flags, d)) {\n      ++gsl::at(result, d);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nstd::array<size_t, VolumeDim> desired_refinement_levels_of_neighbor(\n    const ElementId<VolumeDim>& neighbor_id,\n    const std::array<Flag, VolumeDim>& neighbor_flags,\n    const OrientationMap<VolumeDim>& orientation) {\n  if (orientation.is_aligned()) {\n    return desired_refinement_levels(neighbor_id, neighbor_flags);\n  }\n  std::array<size_t, VolumeDim> result =\n      orientation.permute_from_neighbor(neighbor_id.refinement_levels());\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    ASSERT(Flag::Undefined != gsl::at(neighbor_flags, d),\n           \"Undefined Flag in dimension \" << d);\n    const size_t mapped_dim = orientation(d);\n    if (Flag::Join == gsl::at(neighbor_flags, mapped_dim)) {\n      --gsl::at(result, d);\n    } else if (Flag::Split == gsl::at(neighbor_flags, mapped_dim)) {\n      ++gsl::at(result, d);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nboost::rational<size_t> fraction_of_block_volume(\n    const ElementId<VolumeDim>& element_id) {\n  return {1, two_to_the(alg::accumulate(element_id.refinement_levels(), 0_st))};\n}\n\ntemplate <size_t VolumeDim>\nbool has_potential_sibling(const ElementId<VolumeDim>& element_id,\n                           const Direction<VolumeDim>& direction) {\n  const SegmentId segment_id = element_id.segment_id(direction.dimension());\n  if (segment_id.refinement_level() == 0) {\n    return false;\n  }\n  return direction.side() == segment_id.side_of_sibling();\n}\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim> id_of_parent(const ElementId<VolumeDim>& element_id,\n                                  const std::array<Flag, VolumeDim>& flags) {\n  using ::operator<<;\n  ASSERT(alg::count(flags, Flag::Join) > 0,\n         \"Element \" << element_id << \" is not joining given flags \" << flags);\n  ASSERT(alg::count(flags, Flag::Split) == 0,\n         \"Splitting and joining an Element is not supported\");\n  auto segment_ids = element_id.segment_ids();\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    if (gsl::at(flags, d) == Flag::Join) {\n      gsl::at(segment_ids, d) = element_id.segment_id(d).id_of_parent();\n    }\n  }\n  return {element_id.block_id(), std::move(segment_ids),\n          element_id.grid_index()};\n}\n\ntemplate <size_t VolumeDim>\nstd::vector<ElementId<VolumeDim>> ids_of_children(\n    const ElementId<VolumeDim>& element_id,\n    const std::array<amr::Flag, VolumeDim>& flags,\n    const std::optional<size_t>& child_grid_index) {\n  using ::operator<<;\n  ASSERT(alg::count(flags, Flag::Split) > 0,\n         \"Element \" << element_id << \" has no children given flags \" << flags);\n  ASSERT(alg::count(flags, Flag::Join) == 0,\n         \"Splitting and joining an Element is not supported\");\n  const size_t block_id = element_id.block_id();\n  const size_t grid_index = child_grid_index.value_or(element_id.grid_index());\n  if constexpr (VolumeDim == 1) {\n    return {{block_id,\n             {{element_id.segment_id(0).id_of_child(Side::Lower)}},\n             grid_index},\n            {block_id,\n             {{element_id.segment_id(0).id_of_child(Side::Upper)}},\n             grid_index}};\n  } else {\n    const auto segment_ids = element_id.segment_ids();\n    std::array<std::vector<SegmentId>, VolumeDim> child_segment_ids;\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      const SegmentId& segment_id = gsl::at(segment_ids, d);\n      gsl::at(child_segment_ids, d) =\n          gsl::at(flags, d) == Flag::Split\n              ? std::vector{segment_id.id_of_child(Side::Lower),\n                            segment_id.id_of_child(Side::Upper)}\n              : std::vector{segment_id};\n    }\n\n    std::vector<ElementId<VolumeDim>> result{};\n    for (const auto segment_id_xi : child_segment_ids[0]) {\n      for (const auto segment_id_eta : child_segment_ids[1]) {\n        if constexpr (VolumeDim == 2) {\n          result.emplace_back(\n              block_id, std::array{segment_id_xi, segment_id_eta}, grid_index);\n        } else {\n          for (const auto segment_id_zeta : child_segment_ids[2]) {\n            result.emplace_back(\n                block_id,\n                std::array{segment_id_xi, segment_id_eta, segment_id_zeta},\n                grid_index);\n          }\n        }\n      }\n    }\n    return result;\n  }\n}\n\ntemplate <size_t VolumeDim>\nstd::deque<ElementId<VolumeDim>> ids_of_joining_neighbors(\n    const Element<VolumeDim>& element,\n    const std::array<Flag, VolumeDim>& flags) {\n  using ::operator<<;\n  ASSERT(alg::count(flags, Flag::Join) > 0,\n         \"Element \" << element.id() << \" is not joining given flags \" << flags);\n  ASSERT(alg::count(flags, Flag::Split) == 0,\n         \"Splitting and joining an Element is not supported\");\n  std::deque<ElementId<VolumeDim>> result;\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    if (gsl::at(flags, d) == Flag::Join) {\n      const Direction<VolumeDim> sibling_direction{\n          d, element.id().segment_id(d).side_of_sibling()};\n      const auto& neighbor_sibling_ids_in_this_dim =\n          element.neighbors().at(sibling_direction).ids();\n      for (auto sibling_id : neighbor_sibling_ids_in_this_dim) {\n        result.emplace_back(sibling_id);\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nbool is_child_that_creates_parent(const ElementId<VolumeDim>& element_id,\n                                  const std::array<Flag, VolumeDim>& flags) {\n  using ::operator<<;\n  ASSERT(alg::count(flags, Flag::Join) > 0,\n         \"Element \" << element_id << \" is not joining given flags \" << flags);\n  ASSERT(alg::count(flags, Flag::Split) == 0,\n         \"Splitting and joining an Element is not supported\");\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    if (gsl::at(flags, d) == Flag::Join and\n        element_id.segment_id(d).index() % 2 == 1) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <size_t VolumeDim>\nbool prevent_element_from_joining_while_splitting(\n    const gsl::not_null<std::array<Flag, VolumeDim>*> flags) {\n  bool flags_changed = false;\n  if (alg::any_of(*flags,\n                  [](amr::Flag flag) { return flag == amr::Flag::Split; })) {\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      if (gsl::at(*flags, d) == amr::Flag::Join) {\n        gsl::at(*flags, d) = amr::Flag::DoNothing;\n        flags_changed = true;\n      }\n    }\n  }\n  return flags_changed;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::array<size_t, DIM(data)> desired_refinement_levels<DIM(data)>( \\\n      const ElementId<DIM(data)>&, const std::array<Flag, DIM(data)>&);        \\\n  template std::array<size_t, DIM(data)>                                       \\\n  desired_refinement_levels_of_neighbor<DIM(data)>(                            \\\n      const ElementId<DIM(data)>&, const std::array<Flag, DIM(data)>&,         \\\n      const OrientationMap<DIM(data)>&);                                       \\\n  template boost::rational<size_t> fraction_of_block_volume<DIM(data)>(        \\\n      const ElementId<DIM(data)>& element_id);                                 \\\n  template bool has_potential_sibling(const ElementId<DIM(data)>& element_id,  \\\n                                      const Direction<DIM(data)>& direction);  \\\n  template ElementId<DIM(data)> id_of_parent(                                  \\\n      const ElementId<DIM(data)>& element_id,                                  \\\n      const std::array<amr::Flag, DIM(data)>& flags);                          \\\n  template std::vector<ElementId<DIM(data)>> ids_of_children(                  \\\n      const ElementId<DIM(data)>& element_id,                                  \\\n      const std::array<amr::Flag, DIM(data)>& flags,                           \\\n      const std::optional<size_t>& child_grid_index);                          \\\n  template std::deque<ElementId<DIM(data)>> ids_of_joining_neighbors(          \\\n      const Element<DIM(data)>& element,                                       \\\n      const std::array<Flag, DIM(data)>& flags);                               \\\n  template bool is_child_that_creates_parent(                                  \\\n      const ElementId<DIM(data)>& element_id,                                  \\\n      const std::array<Flag, DIM(data)>& flags);                               \\\n  template bool prevent_element_from_joining_while_splitting(                  \\\n      const gsl::not_null<std::array<Flag, DIM(data)>*> flags);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/Helpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Functions used for adaptive mesh refinement decisions.\n\n#pragma once\n\n#include <array>\n#include <boost/rational.hpp>\n#include <cstddef>\n#include <deque>\n#include <optional>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Direction;\n\ntemplate <size_t VolumeDim>\nclass Element;\n\ntemplate <size_t VolumeDim>\nclass ElementId;\n\ntemplate <size_t VolumeDim>\nclass OrientationMap;\n\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}\n/// \\endcond\n\nnamespace amr {\n/// \\ingroup AmrGroup\n/// \\brief Computes the desired refinement level of the Element with ElementId\n/// `id` given the desired amr::Flag%s `flags`\ntemplate <size_t VolumeDim>\nstd::array<size_t, VolumeDim> desired_refinement_levels(\n    const ElementId<VolumeDim>& id, const std::array<Flag, VolumeDim>& flags);\n\n/// \\ingroup AmrGroup\n/// \\brief Computes the desired refinement level of a neighboring Element with\n/// ElementId `neighbor_id` given its desired amr::Flag%s `neighbor_flags`\n/// taking into account the OrientationMap `orientation` of the neighbor\n///\n/// \\details The OrientationMap `orientation` is that from the Element that has\n/// a neighbor with ElementId `neighbor_id`\ntemplate <size_t VolumeDim>\nstd::array<size_t, VolumeDim> desired_refinement_levels_of_neighbor(\n    const ElementId<VolumeDim>& neighbor_id,\n    const std::array<Flag, VolumeDim>& neighbor_flags,\n    const OrientationMap<VolumeDim>& orientation);\n\n/// \\ingroup AmrGroup\n/// Fraction of the logical volume of a block covered by an element\n///\n/// \\note The sum of this over all the elements of a block should be one\ntemplate <size_t VolumeDim>\nboost::rational<size_t> fraction_of_block_volume(\n    const ElementId<VolumeDim>& element_id);\n\n/// \\ingroup AmrGroup\n/// \\brief Whether or not the Element with `element_id` can have a sibling\n/// in the given `direction`\ntemplate <size_t VolumeDim>\nbool has_potential_sibling(const ElementId<VolumeDim>& element_id,\n                           const Direction<VolumeDim>& direction);\n\n/// \\ingroup AmrGroup\n/// \\brief Returns the ElementId of the parent of the Element with `element_id`\n/// using the refinement `flags` associated with `element_id`\n///\n/// \\details Note that at least one flag of `flags` must be Flag::Join and\n/// none of the `flags` can be Flag::Split.  The parent ElementId is computed\n/// by looping over the SegmentId%s of `element_id` and using either the\n/// SegmentId or its parent depending upon whether or not the corresponding Flag\n/// is Flag::Join.\ntemplate <size_t VolumeDim>\nElementId<VolumeDim> id_of_parent(const ElementId<VolumeDim>& element_id,\n                                  const std::array<Flag, VolumeDim>& flags);\n\n/// \\ingroup AmrGroup\n/// \\brief Returns the ElementIds of the children of the Element with\n/// `element_id` using the refinement `flags` associated with `element_id`\n///\n/// \\details Note that at least one flag of `flags` must be Flag::Split and\n/// none of the `flags` can be Flag::Join.  The child ElementId%s are computed\n/// by looping over the SegmentId%s of `element_id` and using either the\n/// SegmentId or its children depending upon whether or not the corresponding\n/// flag is Flag::Split.\ntemplate <size_t VolumeDim>\nstd::vector<ElementId<VolumeDim>> ids_of_children(\n    const ElementId<VolumeDim>& element_id,\n    const std::array<Flag, VolumeDim>& flags,\n    const std::optional<size_t>& child_grid_index = std::nullopt);\n\n/// \\ingroup AmrGroup\n/// \\brief The ElementIds of the neighbors of `element` that will join with it\n/// given refinement `flags`\n///\n/// \\note This function only returns the face neighbors of `element` that will\n/// join with it, and not the joining corner neighbors\ntemplate <size_t VolumeDim>\nstd::deque<ElementId<VolumeDim>> ids_of_joining_neighbors(\n    const Element<VolumeDim>& element,\n    const std::array<Flag, VolumeDim>& flags);\n\n/// \\ingroup AmrGroup\n/// \\brief Whether or not the Element is the child that should create the parent\n/// Element when joining elements\n///\n/// \\details This returns true if the Element is the lower sibling segment in\n/// all dimensions that are joining\ntemplate <size_t VolumeDim>\nbool is_child_that_creates_parent(const ElementId<VolumeDim>& element_id,\n                                  const std::array<Flag, VolumeDim>& flags);\n\n/// \\ingroup AmrGroup\n/// \\brief Prevent an Element from splitting in one dimension, while joining in\n/// another\n///\n/// \\details If `flags` (the AMR decisions of an Element) contains both\n/// amr::Flag::Split and  amr::Flag::Join, then all change Join\n/// to amr::Flag::DoNothing.\n///\n/// \\returns true if any flag is changed\n///\n/// \\note This restriction could be relaxed, but it would greatly complicate\n/// the AMR algorithm.  As a Join flag has the lowest priority, it causes\n/// no problems to replace it with a DoNothing flag.\ntemplate <size_t VolumeDim>\nbool prevent_element_from_joining_while_splitting(\n    gsl::not_null<std::array<Flag, VolumeDim>*> flags);\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/Info.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Amr/Flag.hpp\"\n\n#include <array>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Domain/Amr/Info.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace amr {\ntemplate <size_t VolumeDim>\nInfo<VolumeDim>::Info()\n    : flags(make_array<VolumeDim>(Flag::Undefined)), new_mesh{} {}\n\ntemplate <size_t VolumeDim>\nInfo<VolumeDim>::Info(std::array<Flag, VolumeDim> flags_in,\n                      Mesh<VolumeDim> new_mesh_in)\n    : flags(std::move(flags_in)), new_mesh(std::move(new_mesh_in)) {}\n\ntemplate <size_t VolumeDim>\nvoid Info<VolumeDim>::pup(PUP::er& p) {\n  p | flags;\n  p | new_mesh;\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const amr::Info<VolumeDim>& info) {\n  using ::operator<<;\n  os << \"Flags: \" << info.flags << \" New mesh: \" << info.new_mesh;\n  return os;\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const Info<VolumeDim>& lhs, const Info<VolumeDim>& rhs) {\n  return lhs.flags == rhs.flags and lhs.new_mesh == rhs.new_mesh;\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const amr::Info<VolumeDim>& lhs,\n                const amr::Info<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GEN_OP(op, dim) \\\n  template bool operator op(const Info<dim>& lhs, const Info<dim>& rhs);\n#define INSTANTIATE(_, data)                          \\\n  template struct Info<DIM(data)>;                    \\\n  GEN_OP(==, DIM(data))                               \\\n  GEN_OP(!=, DIM(data))                               \\\n  template std::ostream& operator<<(std::ostream& os, \\\n                                    const Info<DIM(data)>& info);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n#undef INSTANTIATE\n#undef GEN_OP\n#undef DIM\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/Info.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iosfwd>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace amr {\n/// \\brief Information about an element that is communicated by AMR actions\n///\n/// \\details amr::Actions::EvaluateRefinementCriteria and\n/// amr::Actions::UpdateAmrDecision communicate the desired\n/// amr::Flag%s and Mesh of an element.\ntemplate <size_t VolumeDim>\nstruct Info {\n  Info();\n  Info(std::array<Flag, VolumeDim> flags_in, Mesh<VolumeDim> new_mesh_in);\n\n  std::array<Flag, VolumeDim> flags;\n  Mesh<VolumeDim> new_mesh;\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n\n/// Output operator for an Info.\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const Info<VolumeDim>& info);\n\ntemplate <size_t VolumeDim>\nbool operator==(const Info<VolumeDim>& lhs, const Info<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Info<VolumeDim>& lhs, const Info<VolumeDim>& rhs);\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/NeighborsOfChild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Amr/NeighborsOfChild.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/NewNeighborIds.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace amr {\ntemplate <size_t VolumeDim>\nstd::pair<DirectionMap<VolumeDim, Neighbors<VolumeDim>>,\n          DirectionalIdMap<VolumeDim, Mesh<VolumeDim>>>\nneighbors_of_child(\n    const Element<VolumeDim>& parent, const Info<VolumeDim>& parent_info,\n    const std::unordered_map<ElementId<VolumeDim>, Info<VolumeDim>>&\n        parent_neighbor_info,\n    const ElementId<VolumeDim>& child_id) {\n  std::pair<DirectionMap<VolumeDim, Neighbors<VolumeDim>>,\n            DirectionalIdMap<VolumeDim, Mesh<VolumeDim>>>\n      result;\n\n  const auto sibling_id = [&child_id](const size_t dim) {\n    auto sibling_segment_ids = child_id.segment_ids();\n    auto& segment_id_to_change = gsl::at(sibling_segment_ids, dim);\n    segment_id_to_change = segment_id_to_change.id_of_sibling();\n    return ElementId<VolumeDim>{child_id.block_id(), sibling_segment_ids,\n                                child_id.grid_index()};\n  };\n\n  for (const auto& [direction, old_neighbors] : parent.neighbors()) {\n    const auto dim = direction.dimension();\n    if (gsl::at(parent_info.flags, dim) == Flag::Split and\n        has_potential_sibling(child_id, direction)) {\n      const auto id = sibling_id(dim);\n      result.first.emplace(\n          direction, Neighbors<VolumeDim>{\n                         {id}, OrientationMap<VolumeDim>::create_aligned()});\n      result.second.insert({{direction, id}, parent_info.new_mesh});\n    } else {\n      const auto new_neighbor_ids_and_meshes = amr::new_neighbor_ids(\n          child_id, direction, old_neighbors, parent_neighbor_info);\n      std::unordered_set<ElementId<VolumeDim>> new_neighbor_ids;\n      for (const auto& [id, mesh] : new_neighbor_ids_and_meshes) {\n        result.second.insert_or_assign({direction, id}, mesh);\n        new_neighbor_ids.insert(id);\n      }\n\n      result.first.emplace(\n          direction,\n          Neighbors<VolumeDim>{new_neighbor_ids, old_neighbors.orientations(),\n                               old_neighbors.are_conforming()});\n    }\n  }\n\n  for (const auto& direction : parent.external_boundaries()) {\n    const auto dim = direction.dimension();\n    if (gsl::at(parent_info.flags, dim) == Flag::Split and\n        has_potential_sibling(child_id, direction)) {\n      const auto id = sibling_id(dim);\n      result.first.emplace(\n          direction, Neighbors<VolumeDim>{\n                         {id}, OrientationMap<VolumeDim>::create_aligned()});\n      result.second.insert({{direction, id}, parent_info.new_mesh});\n    }\n  }\n\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template std::pair<DirectionMap<DIM(data), Neighbors<DIM(data)>>,         \\\n                     DirectionalIdMap<DIM(data), Mesh<DIM(data)>>>          \\\n  neighbors_of_child(                                                       \\\n      const Element<DIM(data)>& parent, const Info<DIM(data)>& parent_info, \\\n      const std::unordered_map<ElementId<DIM(data)>, Info<DIM(data)>>&      \\\n          parent_neighbor_info,                                             \\\n      const ElementId<DIM(data)>& child_id);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/NeighborsOfChild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n\n#include \"Domain/Amr/Flag.hpp\"\n\n/// \\cond\nnamespace amr {\ntemplate <size_t VolumeDim>\nstruct Info;\n}  // namespace amr\ntemplate <size_t VolumeDim, typename T>\nclass DirectionalIdMap;\ntemplate <size_t VolumeDim, typename T>\nclass DirectionMap;\ntemplate <size_t VolumeDim>\nclass Element;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass Mesh;\ntemplate <size_t VolumeDim, typename IdType>\nclass Neighbors;\n/// \\endcond\n\nnamespace amr {\n/*!\n * \\ingroup AmrGroup\n * \\brief Determine the new neighbors of an element during AMR, and the\n * neighbors' meshes.\n *\n * Can be used for both h-refinement and p-refinement.\n *\n * \\param parent The parent element that is being refined. For h-refinement,\n * this is the element that is being split into children. For p-refinement,\n * this is the element that is being increased in resolution.\n * \\param parent_info The AMR info of the parent element.\n * \\param parent_neighbor_info The AMR info of the parent element's neighbors.\n * \\param child_id The ID of the child element that is being created. For\n * h-refinement, this is the ID of the new child element. For p-refinement,\n * this is the same as the ID of the parent element.\n */\ntemplate <size_t VolumeDim>\nstd::pair<DirectionMap<VolumeDim, Neighbors<VolumeDim, ElementId<VolumeDim>>>,\n          DirectionalIdMap<VolumeDim, Mesh<VolumeDim>>>\nneighbors_of_child(\n    const Element<VolumeDim>& parent, const Info<VolumeDim>& parent_info,\n    const std::unordered_map<ElementId<VolumeDim>, Info<VolumeDim>>&\n        parent_neighbor_info,\n    const ElementId<VolumeDim>& child_id);\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/NeighborsOfParent.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Amr/NeighborsOfParent.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <tuple>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/NewNeighborIds.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace amr {\ntemplate <size_t VolumeDim>\nstd::pair<DirectionMap<VolumeDim, Neighbors<VolumeDim>>,\n          DirectionalIdMap<VolumeDim, Mesh<VolumeDim>>>\nneighbors_of_parent(\n    const ElementId<VolumeDim>& parent_id,\n    const std::vector<std::tuple<\n        const Element<VolumeDim>&,\n        const std::unordered_map<ElementId<VolumeDim>, Info<VolumeDim>>&>>&\n        children_elements_and_neighbor_info) {\n  std::pair<DirectionMap<VolumeDim, Neighbors<VolumeDim>>,\n            DirectionalIdMap<VolumeDim, Mesh<VolumeDim>>>\n      result;\n\n  std::vector<ElementId<VolumeDim>> children_ids;\n  children_ids.reserve(children_elements_and_neighbor_info.size());\n  alg::transform(\n      children_elements_and_neighbor_info, std::back_inserter(children_ids),\n      [](const auto& child_items) { return std::get<0>(child_items).id(); });\n\n  const auto is_child = [&children_ids](const ElementId<VolumeDim>& id) {\n    return alg::find(children_ids, id) != children_ids.end();\n  };\n\n  for (const auto& [child, child_neighbor_info] :\n       children_elements_and_neighbor_info) {\n    for (const auto& [direction, child_neighbors] : child.neighbors()) {\n      if (has_potential_sibling(child.id(), direction) and\n          is_child(*(child_neighbors.ids().begin()))) {\n        continue;  // neighbor in this direction was joined sibling\n      }\n      const auto new_neighbor_ids_and_meshes = amr::new_neighbor_ids(\n          parent_id, direction, child_neighbors, child_neighbor_info);\n      std::unordered_set<ElementId<VolumeDim>> new_neighbor_ids;\n      for (const auto& [id, mesh] : new_neighbor_ids_and_meshes) {\n        result.second.insert_or_assign({direction, id}, mesh);\n        new_neighbor_ids.insert(id);\n      }\n      if (0 == result.first.count(direction)) {\n        result.first.emplace(\n            direction, Neighbors<VolumeDim>{new_neighbor_ids,\n                                            child_neighbors.orientations(),\n                                            child_neighbors.are_conforming()});\n      } else {\n        result.first.at(direction).add_ids(new_neighbor_ids);\n      }\n    }\n  }\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template std::pair<DirectionMap<DIM(data), Neighbors<DIM(data)>>,           \\\n                     DirectionalIdMap<DIM(data), Mesh<DIM(data)>>>            \\\n  neighbors_of_parent(                                                        \\\n      const ElementId<DIM(data)>& parent_id,                                  \\\n      const std::vector<std::tuple<                                           \\\n          const Element<DIM(data)>&,                                          \\\n          const std::unordered_map<ElementId<DIM(data)>, Info<DIM(data)>>&>>& \\\n          children_elements_and_neighbor_info);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/NeighborsOfParent.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n#include <unordered_map>\n#include <vector>\n\n/// \\cond\nnamespace amr {\ntemplate <size_t VolumeDim>\nstruct Info;\n}  // namespace amr\ntemplate <size_t VolumeDim, typename T>\nclass DirectionalIdMap;\ntemplate <size_t VolumeDim, typename T>\nclass DirectionMap;\ntemplate <size_t VolumeDim>\nclass Element;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass Mesh;\ntemplate <size_t VolumeDim, typename IdType>\nclass Neighbors;\n/// \\endcond\n\nnamespace amr {\n/// \\ingroup AmrGroup\n/// \\brief returns the neighbors and their Mesh%es of the Element with ElementId\n/// `parent_id`, that is created from its `children_elements_and_neighbor_info`\ntemplate <size_t VolumeDim>\nstd::pair<DirectionMap<VolumeDim, Neighbors<VolumeDim, ElementId<VolumeDim>>>,\n          DirectionalIdMap<VolumeDim, Mesh<VolumeDim>>>\nneighbors_of_parent(\n    const ElementId<VolumeDim>& parent_id,\n    const std::vector<std::tuple<\n        const Element<VolumeDim>&,\n        const std::unordered_map<ElementId<VolumeDim>, Info<VolumeDim>>&>>&\n        children_elements_and_neighbor_info);\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/NewNeighborIds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Amr/NewNeighborIds.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\nbool overlapping_within_one_level(const SegmentId& segment1,\n                                  const SegmentId& segment2) {\n  if (0 == segment1.refinement_level()) {\n    return (2 > segment2.refinement_level());\n  }\n  if (0 == segment2.refinement_level()) {\n    return (2 > segment1.refinement_level());\n  }\n  return (segment1 == segment2 or segment1.id_of_parent() == segment2 or\n          segment1 == segment2.id_of_parent());\n}\n\n}  // namespace\n\nnamespace amr {\ntemplate <size_t VolumeDim>\nstd::unordered_map<ElementId<VolumeDim>, Mesh<VolumeDim>> new_neighbor_ids(\n    const ElementId<VolumeDim>& my_id, const Direction<VolumeDim>& direction,\n    const Neighbors<VolumeDim>& previous_neighbors_in_direction,\n    const std::unordered_map<ElementId<VolumeDim>, Info<VolumeDim>>&\n        previous_neighbors_amr_info) {\n  std::unordered_map<ElementId<VolumeDim>, Mesh<VolumeDim>>\n      new_neighbors_in_direction;\n  const size_t grid_index = my_id.grid_index();\n\n  // Only one neighbor in 1D in a given direction\n  if constexpr (VolumeDim == 1) {\n    const ElementId<1>& previous_neighbor_id =\n        *(previous_neighbors_in_direction.ids().begin());\n    const OrientationMap<VolumeDim>& orientation_map_from_me_to_neighbors =\n        previous_neighbors_in_direction.orientation(previous_neighbor_id);\n    const Direction<VolumeDim> direction_to_me_in_neighbor_frame =\n        orientation_map_from_me_to_neighbors(direction.opposite());\n\n    const amr::Flag neighbor_flag =\n        previous_neighbors_amr_info.at(previous_neighbor_id).flags[0];\n    SegmentId previous_segment_id = previous_neighbor_id.segment_ids()[0];\n    SegmentId new_segment_id =\n        (amr::Flag::Join == neighbor_flag\n             ? previous_segment_id.id_of_parent()\n             : (amr::Flag::Split == neighbor_flag\n                    ? previous_segment_id.id_of_child(\n                          direction_to_me_in_neighbor_frame.side())\n                    : previous_segment_id));\n    new_neighbors_in_direction.try_emplace(\n        {previous_neighbor_id.block_id(),\n         std::array<SegmentId, 1>({{new_segment_id}}), grid_index},\n        previous_neighbors_amr_info.at(previous_neighbor_id).new_mesh);\n    return new_neighbors_in_direction;\n  } else {\n    for (const auto& previous_neighbor_id :\n         previous_neighbors_in_direction.ids()) {\n      const OrientationMap<VolumeDim>& orientation_map_from_me_to_neighbors =\n          previous_neighbors_in_direction.orientation(previous_neighbor_id);\n      const Direction<VolumeDim> direction_to_me_in_neighbor_frame =\n          orientation_map_from_me_to_neighbors(direction.opposite());\n      const size_t dim_of_direction_to_me_in_neighbor_frame =\n          direction_to_me_in_neighbor_frame.dimension();\n      const std::array<SegmentId, VolumeDim> my_segment_ids_in_neighbor_frame =\n          orientation_map_from_me_to_neighbors(my_id.segment_ids());\n\n      // a neighbor_segment_id is valid if it touches me in the normal\n      // direction (which is in dim_of_direction_to_me_in_neighbor_frame) or\n      // overlaps with me in the transverse directions\n      std::array<std::vector<SegmentId>, VolumeDim> valid_neighbor_segment_ids;\n      // it is possible that a previous neighbor (or its children) will not be\n      // a neighbor of me (Note: the previous_neighbors may be those of my\n      // parent or children if I am the result of h-refinement)\n      bool there_is_no_neighbor = false;\n      const auto neighbor_segment_ids = previous_neighbor_id.segment_ids();\n      const auto new_neighbor_mesh =\n          orientation_map_from_me_to_neighbors.inverse_map()(\n              previous_neighbors_amr_info.at(previous_neighbor_id).new_mesh);\n      for (size_t d = 0; d < VolumeDim; ++d) {\n        const amr::Flag neighbor_flag =\n            previous_neighbors_amr_info.at(previous_neighbor_id).flags.at(d);\n        const SegmentId neighbor_segment_id = gsl::at(neighbor_segment_ids, d);\n        if (dim_of_direction_to_me_in_neighbor_frame == d) {\n          // This is the normal direction.  I know my previous neighbor touched\n          // me so there is exactly one valid segment.\n          gsl::at(valid_neighbor_segment_ids, d)\n              .push_back(\n                  amr::Flag::Join == neighbor_flag\n                      ? neighbor_segment_id.id_of_parent()\n                      : (amr::Flag::Split == neighbor_flag\n                             ? neighbor_segment_id.id_of_child(\n                                   direction_to_me_in_neighbor_frame.side())\n                             : neighbor_segment_id));\n        } else {\n          // This is a transverse direction.  Since we keep refinement levels\n          // within one, there can be 0 to 2 valid segments.\n          if (amr::Flag::Join == neighbor_flag) {\n            if (overlapping_within_one_level(\n                    neighbor_segment_id.id_of_parent(),\n                    gsl::at(my_segment_ids_in_neighbor_frame, d))) {\n              gsl::at(valid_neighbor_segment_ids, d)\n                  .push_back(neighbor_segment_id.id_of_parent());\n            }\n          } else if (amr::Flag::Split == neighbor_flag) {\n            if (overlapping_within_one_level(\n                    neighbor_segment_id.id_of_child(Side::Lower),\n                    gsl::at(my_segment_ids_in_neighbor_frame, d))) {\n              gsl::at(valid_neighbor_segment_ids, d)\n                  .push_back(neighbor_segment_id.id_of_child(Side::Lower));\n            }\n            if (overlapping_within_one_level(\n                    neighbor_segment_id.id_of_child(Side::Upper),\n                    gsl::at(my_segment_ids_in_neighbor_frame, d))) {\n              gsl::at(valid_neighbor_segment_ids, d)\n                  .push_back(neighbor_segment_id.id_of_child(Side::Upper));\n            }\n          } else {\n            if (overlapping_within_one_level(\n                    neighbor_segment_id,\n                    gsl::at(my_segment_ids_in_neighbor_frame, d))) {\n              gsl::at(valid_neighbor_segment_ids, d)\n                  .push_back(neighbor_segment_id);\n            }\n          }  // if-elseif-else on neighbor_flag\n\n          if (gsl::at(valid_neighbor_segment_ids, d).empty()) {\n            there_is_no_neighbor = true;\n          }\n        }  // if-else on normal vs transverse dimension\n\n        if (there_is_no_neighbor) {\n          // No need to loop over the remaining dimensions\n          break;\n        }\n      }  // loop over dimensions\n\n      if (there_is_no_neighbor) {\n        // This previous neighbor produced no new neighbors, continue with the\n        // next neighbor\n        continue;\n      }\n\n      for (const auto segment_id_xi : valid_neighbor_segment_ids[0]) {\n        for (const auto segment_id_eta : valid_neighbor_segment_ids[1]) {\n          if constexpr (VolumeDim == 2) {\n            // multiple previous neighbors may have joined to form a new\n            // neighbor; it is assumed they have the same new_mesh\n            new_neighbors_in_direction.try_emplace(\n                {previous_neighbor_id.block_id(),\n                 std::array{segment_id_xi, segment_id_eta}, grid_index},\n                new_neighbor_mesh);\n          } else if constexpr (VolumeDim == 3) {\n            for (const auto segment_id_zeta : valid_neighbor_segment_ids[2]) {\n              // multiple previous neighbors may have joined to form a new\n              // neighbor; it is assumed they have the same new_mesh\n              new_neighbors_in_direction.try_emplace(\n                  {previous_neighbor_id.block_id(),\n                   std::array{segment_id_xi, segment_id_eta, segment_id_zeta},\n                   grid_index},\n                  new_neighbor_mesh);\n            }\n          }\n        }\n      }\n    }  // loop over previous neighbors\n  }\n\n  return new_neighbors_in_direction;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template std::unordered_map<ElementId<DIM(data)>, Mesh<DIM(data)>>   \\\n  new_neighbor_ids(                                                    \\\n      const ElementId<DIM(data)>& my_id,                               \\\n      const Direction<DIM(data)>& direction,                           \\\n      const Neighbors<DIM(data)>& previous_neighbors_in_direction,     \\\n      const std::unordered_map<ElementId<DIM(data)>, Info<DIM(data)>>& \\\n          previous_neighbors_amr_info);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/NewNeighborIds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n\n/// \\cond\nnamespace amr {\ntemplate <size_t VolumeDim>\nstruct Info;\n}  // namespace amr\ntemplate <size_t VolumeDim>\nclass Direction;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass Mesh;\ntemplate <size_t VolumeDim, typename IdType>\nclass Neighbors;\n/// \\endcond\n\nnamespace amr {\n/// \\ingroup AmrGroup\n/// \\brief returns the ElementId and Mesh of the new neighbors in the given\n/// `direction` of the Element whose ElementId is `my_id` given the\n/// `previous_neighbors_in_direction` and their amr::Info\\.\n///\n/// The new meshes are given with respect to the host element's orientation.\n///\n/// \\note `previous_neighbors_in_direction` should be from the parent (or a\n/// child) of the Element with `my_id` if `my_id` corresponds to a newly created\n/// child (or parent) Element.\n///\n/// \\note `previous_neighbors_amr_info` may contain info from neighbors in\n/// directions other than `direction`\ntemplate <size_t VolumeDim>\nstd::unordered_map<ElementId<VolumeDim>, Mesh<VolumeDim>> new_neighbor_ids(\n    const ElementId<VolumeDim>& my_id, const Direction<VolumeDim>& direction,\n    const Neighbors<VolumeDim, ElementId<VolumeDim>>&\n        previous_neighbors_in_direction,\n    const std::unordered_map<ElementId<VolumeDim>, Info<VolumeDim>>&\n        previous_neighbors_amr_info);\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Amr)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Flags.hpp\n  NeighborFlags.hpp\n  Tags.hpp\n  )\n"
  },
  {
    "path": "src/Domain/Amr/Tags/Flags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\n/// \\cond\nnamespace amr {\ntemplate <size_t VolumeDim>\nstruct Info;\n}  // namespace amr\n/// \\endcond\n\nnamespace amr::Tags {\n/// amr::Info for an Element.\ntemplate <size_t VolumeDim>\nstruct Info : db::SimpleTag {\n  using type = amr::Info<VolumeDim>;\n};\n\n}  // namespace amr::Tags\n"
  },
  {
    "path": "src/Domain/Amr/Tags/NeighborFlags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\n/// \\cond\nnamespace amr {\ntemplate <size_t VolumeDim>\nstruct Info;\n}  // namespace amr\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\nnamespace amr::Tags {\n/// \\brief amr::Info for the neighbors of an Element.\n///\n/// Note that the `amr::Info` is stored in the orientation of the neighbor,\n/// i.e., dimensions are not reoriented to the orientation of the element.\ntemplate <size_t VolumeDim>\nstruct NeighborInfo : db::SimpleTag {\n  using type = std::unordered_map<ElementId<VolumeDim>, amr::Info<VolumeDim>>;\n};\n\n}  // namespace amr::Tags\n"
  },
  {
    "path": "src/Domain/Amr/Tags/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup AmrGroup\nnamespace amr {\n/// \\brief %Tags for adaptive mesh refinement\nnamespace Tags {}\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/UpdateAmrDecision.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Amr/UpdateAmrDecision.hpp\"\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t VolumeDim>\nclass OrientationMap;\n\nnamespace amr {\n\ntemplate <size_t VolumeDim>\nbool update_amr_decision(\n    const gsl::not_null<std::array<Flag, VolumeDim>*> my_current_amr_flags,\n    const Element<VolumeDim>& element, const ElementId<VolumeDim>& neighbor_id,\n    const std::array<Flag, VolumeDim>& neighbor_amr_flags,\n    bool enforce_two_to_one_balance_in_normal_direction) {\n  const auto& element_id = element.id();\n  bool my_amr_decision_changed = false;\n  bool neighbor_found = false;\n  std::array<size_t, VolumeDim> my_desired_levels =\n      desired_refinement_levels(element_id, *my_current_amr_flags);\n\n  for (const auto& direction_neighbors : element.neighbors()) {\n    const Neighbors<VolumeDim>& neighbors_in_dir = direction_neighbors.second;\n\n    if (1 == neighbors_in_dir.ids().count(neighbor_id)) {\n      // finding the same neighbor twice (which can happen with periodic\n      // domains) is okay, and may be needed when examining a Join\n      neighbor_found = true;\n      const Direction<VolumeDim> direction_to_neighbor =\n          direction_neighbors.first;\n      const OrientationMap<VolumeDim>& orientation_of_neighbor =\n          neighbors_in_dir.orientation(neighbor_id);\n      const std::array<size_t, VolumeDim> neighbor_desired_levels =\n          desired_refinement_levels_of_neighbor(\n              neighbor_id, neighbor_amr_flags, orientation_of_neighbor);\n\n      // update flags if my element wants to be two or more levels\n      // coarser than the neighbor in any dimension (unless it is not\n      // required in the direction to the neighbor)\n      for (size_t d = 0; d < VolumeDim; ++d) {\n        if (d == direction_to_neighbor.dimension() and\n            not enforce_two_to_one_balance_in_normal_direction) {\n          continue;\n        }\n        if (Flag::Split == gsl::at(*my_current_amr_flags, d) or\n            gsl::at(my_desired_levels, d) >=\n                gsl::at(neighbor_desired_levels, d)) {\n          continue;\n        }\n        const size_t difference =\n            gsl::at(neighbor_desired_levels, d) - gsl::at(my_desired_levels, d);\n        ASSERT(difference < 4 and difference > 0,\n               \"neighbor level = \" << gsl::at(neighbor_desired_levels, d)\n                                   << \", my level = \"\n                                   << gsl::at(my_desired_levels, d));\n        if (Flag::Join == gsl::at(*my_current_amr_flags, d)) {\n          if (3 == difference) {\n            // My split neighbor wants to split, so I need to split to keep\n            // refinement levels within one.\n            gsl::at(*my_current_amr_flags, d) = Flag::Split;\n            gsl::at(my_desired_levels, d) += 2;\n            my_amr_decision_changed = true;\n          } else if (2 == difference) {\n            // My split neighbor wants to stay the same, or my neighbor\n            // split, so I need to stay the same to keep refinement levels\n            // within one.\n            gsl::at(*my_current_amr_flags, d) = Flag::DoNothing;\n            ++gsl::at(my_desired_levels, d);\n            my_amr_decision_changed = true;\n          }\n        } else {\n          if (2 == difference) {\n            // my split neighbor wants to split, so I need to split to\n            // keep refinement levels within one\n            gsl::at(*my_current_amr_flags, d) = Flag::Split;\n            ++gsl::at(my_desired_levels, d);\n            my_amr_decision_changed = true;\n          }\n        }\n      }\n\n      // update flags if the neighbor is a potential sibling that my element\n      // cannot join\n      const size_t dimension_of_direction_to_neighbor =\n          direction_to_neighbor.dimension();\n      if (has_potential_sibling(element_id, direction_to_neighbor) and\n          Flag::Join == gsl::at(*my_current_amr_flags,\n                                     dimension_of_direction_to_neighbor) and\n          (my_desired_levels != neighbor_desired_levels)) {\n        gsl::at(*my_current_amr_flags, dimension_of_direction_to_neighbor) =\n            Flag::DoNothing;\n        ++gsl::at(my_desired_levels, dimension_of_direction_to_neighbor);\n        my_amr_decision_changed = true;\n      }\n    }\n  }\n  ASSERT(neighbor_found, \"Could not find neighbor \" << neighbor_id);\n\n  // An element cannot join if it is splitting in another dimension\n  const bool flag_changed =\n      prevent_element_from_joining_while_splitting(my_current_amr_flags);\n\n  return my_amr_decision_changed or flag_changed;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template bool update_amr_decision(                                          \\\n      const gsl::not_null<std::array<Flag, DIM(data)>*> my_current_amr_flags, \\\n      const Element<DIM(data)>& element,                                      \\\n      const ElementId<DIM(data)>& neighbor_id,                                \\\n      const std::array<Flag, DIM(data)>& neighbor_amr_flags,                  \\\n      bool enforce_two_to_one_balance_in_normal_direction);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/Amr/UpdateAmrDecision.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n/// \\cond\nnamespace amr {\nenum class Flag;\n}  // namespace amr\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\ntemplate <size_t VolumeDim>\nclass Element;\n\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\nnamespace amr {\n/// \\ingroup AmrGroup\n/// \\brief Updates the AMR decisions `my_current_amr_flags` of the Element\n/// `element` based on the AMR decisions `neighbor_amr_flags` of a neighbor\n/// Element with ElementId `neighbor_id`.\n///\n/// \\details  This function is called by each element when it receives the AMR\n/// decisions of one of its neighbors.  If any of its flags are updated, the\n/// element should send its new decisions to each of its neighbors.  The\n/// following changes are made to the current flags of the element:\n/// - If the neighbor wants to be two or more refinement levels higher than\n///   the element, the flag is updated to bring the element to within one level\n///   (unless this occurs in the direction to the neighbor and\n///   `enforce_two_to_one_balance_in_normal_direction` is set to false).\n/// - If the element wants to join, and the neighbor is a potential sibling but\n///   wants to be at a different refinement level in any dimension, the flag is\n///   updated to not do h-refinement.\n/// - An Element that is splitting in one dimension is not allowed to join\n///   in another dimension.  If this is occurs when updating the decision,\n///   the decision to join is changed to do nothing.\n///\n/// \\returns true if any flag is changed\n///\n/// \\note Modifies `my_current_amr_flags` which are the AMR decisions of\n/// `element`.\ntemplate <size_t VolumeDim>\nbool update_amr_decision(\n    gsl::not_null<std::array<Flag, VolumeDim>*> my_current_amr_flags,\n    const Element<VolumeDim>& element, const ElementId<VolumeDim>& neighbor_id,\n    const std::array<Flag, VolumeDim>& neighbor_amr_flags,\n    bool enforce_two_to_one_balance_in_normal_direction);\n}  // namespace amr\n"
  },
  {
    "path": "src/Domain/AreaElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/AreaElement.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid euclidean_area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Scalar<DataVector>& inverse_jacobian_determinant_face,\n    const Direction<VolumeDim>& direction) {\n  ASSERT(inverse_jacobian_face.get(0, 0).size() ==\n             get(inverse_jacobian_determinant_face).size(),\n         \"InverseJacobian and determinant are expected to have the same number \"\n         \"of grid points but have \"\n             << inverse_jacobian_face.get(0, 0).size() << \" and \"\n             << get(inverse_jacobian_determinant_face).size());\n  get(*result) = square(inverse_jacobian_face.get(direction.dimension(), 0));\n  for (size_t d = 1; d < VolumeDim; ++d) {\n    get(*result) += square(inverse_jacobian_face.get(direction.dimension(), d));\n  }\n  get(*result) = sqrt(get(*result)) / get(inverse_jacobian_determinant_face);\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid euclidean_area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Direction<VolumeDim>& direction) {\n  euclidean_area_element(result, inverse_jacobian_face,\n                         determinant(inverse_jacobian_face), direction);\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nScalar<DataVector> euclidean_area_element(\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Scalar<DataVector>& inverse_jacobian_determinant_face,\n    const Direction<VolumeDim>& direction) {\n  Scalar<DataVector> result{};\n  euclidean_area_element(make_not_null(&result), inverse_jacobian_face,\n                         inverse_jacobian_determinant_face, direction);\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nScalar<DataVector> euclidean_area_element(\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Direction<VolumeDim>& direction) {\n  Scalar<DataVector> result{};\n  euclidean_area_element(make_not_null(&result), inverse_jacobian_face,\n                         determinant(inverse_jacobian_face), direction);\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Scalar<DataVector>& inverse_jacobian_determinant_face,\n    const Direction<VolumeDim>& direction,\n    const tnsr::II<DataVector, VolumeDim, TargetFrame>& inverse_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  ASSERT(inverse_jacobian_face.get(0, 0).size() ==\n             get(inverse_jacobian_determinant_face).size(),\n         \"InverseJacobian and determinant are expected to have the same number \"\n         \"of grid points but have \"\n             << inverse_jacobian_face.get(0, 0).size() << \" and \"\n             << get(inverse_jacobian_determinant_face).size());\n  result->get() = DataVector(get(inverse_jacobian_determinant_face).size(), 0.);\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      get(*result) += inverse_spatial_metric.get(i, j) *\n                      inverse_jacobian_face.get(direction.dimension(), i) *\n                      inverse_jacobian_face.get(direction.dimension(), j);\n    }\n  }\n  get(*result) = sqrt(get(*result)) * get(sqrt_det_spatial_metric) /\n                 get(inverse_jacobian_determinant_face);\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Direction<VolumeDim>& direction,\n    const tnsr::II<DataVector, VolumeDim, TargetFrame>& inverse_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  area_element(result, inverse_jacobian_face,\n               determinant(inverse_jacobian_face), direction,\n               inverse_spatial_metric, sqrt_det_spatial_metric);\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nScalar<DataVector> area_element(\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Scalar<DataVector>& inverse_jacobian_determinant_face,\n    const Direction<VolumeDim>& direction,\n    const tnsr::II<DataVector, VolumeDim, TargetFrame>& inverse_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  Scalar<DataVector> result{};\n  area_element(make_not_null(&result), inverse_jacobian_face,\n               inverse_jacobian_determinant_face, direction,\n               inverse_spatial_metric, sqrt_det_spatial_metric);\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nScalar<DataVector> area_element(\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Direction<VolumeDim>& direction,\n    const tnsr::II<DataVector, VolumeDim, TargetFrame>& inverse_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  Scalar<DataVector> result{};\n  area_element(make_not_null(&result), inverse_jacobian_face,\n               determinant(inverse_jacobian_face), direction,\n               inverse_spatial_metric, sqrt_det_spatial_metric);\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void euclidean_area_element(                                   \\\n      const gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian_face,          \\\n      const Scalar<DataVector>& inverse_jacobian_determinant_face,        \\\n      const Direction<DIM(data)>& direction);                             \\\n  template void euclidean_area_element(                                   \\\n      const gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian_face,          \\\n      const Direction<DIM(data)>& direction);                             \\\n  template Scalar<DataVector> euclidean_area_element(                     \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian_face,          \\\n      const Scalar<DataVector>& inverse_jacobian_determinant_face,        \\\n      const Direction<DIM(data)>& direction);                             \\\n  template Scalar<DataVector> euclidean_area_element(                     \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian_face,          \\\n      const Direction<DIM(data)>& direction);                             \\\n  template void area_element(                                             \\\n      const gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian_face,          \\\n      const Scalar<DataVector>& inverse_jacobian_determinant_face,        \\\n      const Direction<DIM(data)>& direction,                              \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                 \\\n          inverse_spatial_metric,                                         \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric);                 \\\n  template void area_element(                                             \\\n      const gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian_face,          \\\n      const Direction<DIM(data)>& direction,                              \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                 \\\n          inverse_spatial_metric,                                         \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric);                 \\\n  template Scalar<DataVector> area_element(                               \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian_face,          \\\n      const Scalar<DataVector>& inverse_jacobian_determinant_face,        \\\n      const Direction<DIM(data)>& direction,                              \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                 \\\n          inverse_spatial_metric,                                         \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric);                 \\\n  template Scalar<DataVector> area_element(                               \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian_face,          \\\n      const Direction<DIM(data)>& direction,                              \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                 \\\n          inverse_spatial_metric,                                         \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric);\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3), (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Domain/AreaElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// @{\n/*!\n * \\brief Compute the Euclidean area element from the inverse jacobian.\n *\n * \\details The Euclidean area element of a surface \\f$\\Sigma\\f$ with\n * constant logical coordinate \\f$\\xi^i\\f$ is given by:\n *\n * \\f{equation}\n * J^\\Sigma = J \\sqrt{\\delta^{jk} (J^{-1})^i_j (J^{-1})^i_k}\n * \\f}\n *\n * where \\f$J^i_j = \\partial x^i / \\xi^j\\f$ is the volume Jacobian with\n * determinant \\f$J\\f$ and inverse \\f$(J^{-1})^i_j = \\partial \\xi^i / \\partial\n * x^j\\f$. The determinant of the inverse Jacobian can be passed as an argument\n * as an overload, otherwise it will be calculated.\n *\n * \\note Time dependent maps are not implemented yet but can be added on\n * demand.\n *\n * \\param inverse_jacobian_face The inverse Jacobian from the ElementLogical\n * frame to the Target frame sliced to the surface.\n * \\param inverse_jacobian_determinant_face The determinant of the inverse\n * Jacobian sliced onto the surface. If not availabe, there exists an overload\n * that will calculate it from the inverse Jacobian.\n * \\param direction The direction of the surface in the element.\n * */\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid euclidean_area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Scalar<DataVector>& inverse_jacobian_determinant_face,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid euclidean_area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nScalar<DataVector> euclidean_area_element(\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Scalar<DataVector>& inverse_jacobian_determinant_face,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nScalar<DataVector> euclidean_area_element(\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Direction<VolumeDim>& direction);\n/// @}\n\n/// @{\n/*!\n * \\brief Compute the curved area element from the inverse jacobian.\n *\n * \\details The area element of a surface \\f$\\Sigma\\f$ with\n * constant logical coordinate \\f$\\xi^i\\f$ is given by:\n *\n * \\f{equation}\n * J^\\Sigma = J \\sqrt{\\gamma} \\sqrt{\\gamma^{jk} (J^{-1})^i_j (J^{-1})^i_k}\n * \\f}\n *\n * where \\f$\\sqrt{\\gamma}\\f$ is the determinant of the spatial metric and\n * \\f$J^i_j = \\partial x^i / \\xi^j\\f$ is the volume Jacobian with determinant\n * \\f$J\\f$ and inverse \\f$(J^{-1})^i_j = \\partial \\xi^i / \\partial x^j\\f$. Note\n * that the square root in the expression above is the magnitude of the\n * unnormalized face normal, where \\f$\\gamma^{jk}\\f$ is the inverse spatial\n * metric. The determinant of the inverse Jacobian can be passed as an argument\n * as an overload, otherwise it will be calculated.\n *\n * \\param inverse_jacobian_face The inverse Jacobian from the ElementLogical\n * frame to the Target frame sliced to the surface.\n * \\param inverse_jacobian_determinant_face The determinant of the inverse\n * Jacobian sliced onto the surface. If not availabe, there exists an overload\n * that will calculate it from the inverse Jacobian.\n * \\param direction The direction of the surface in the element.\n * \\param inverse_spatial_metric The inverse spatial metric sliced to the\n * surface.\n * \\param sqrt_det_spatial_metric The square root of the determinant of the\n * spatial metric, see \\ref gr::Tags::SqrtDetSpatialMetric.\n */\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Scalar<DataVector>& inverse_jacobian_determinant_face,\n    const Direction<VolumeDim>& direction,\n    const tnsr::II<DataVector, VolumeDim, TargetFrame>& inverse_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Direction<VolumeDim>& direction,\n    const tnsr::II<DataVector, VolumeDim, TargetFrame>& inverse_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nScalar<DataVector> area_element(\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Scalar<DataVector>& inverse_jacobian_determinant_face,\n    const Direction<VolumeDim>& direction,\n    const tnsr::II<DataVector, VolumeDim, TargetFrame>& inverse_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nScalar<DataVector> area_element(\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inverse_jacobian_face,\n    const Direction<VolumeDim>& direction,\n    const tnsr::II<DataVector, VolumeDim, TargetFrame>& inverse_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric);\n/// @}\n"
  },
  {
    "path": "src/Domain/Block.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Block.hpp\"\n\n#include <cstddef>\n#include <ios>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n#include <typeinfo>\n#include <utility>\n\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/HasBoundary.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\ntemplate <size_t VolumeDim>\nBlock<VolumeDim>::Block(\n    std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, Frame::Inertial, VolumeDim>>&& stationary_map,\n    const size_t id,\n    DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>> neighbors,\n    std::string name, std::array<domain::Topology, VolumeDim> topologies)\n    : stationary_map_(std::move(stationary_map)),\n      id_(id),\n      neighbors_(std::move(neighbors)),\n      name_(std::move(name)),\n      topologies_(std::move(topologies)) {\n  // Loop over Directions to search which Directions were not set to neighbors_,\n  // set these Directions to external_boundaries_.\n  for (const auto& direction : Direction<VolumeDim>::all_directions()) {\n    const bool has_boundary_in_this_direction = has_boundary(\n        gsl::at(topologies_, direction.dimension()), direction.side());\n    if (neighbors_.find(direction) == neighbors_.end()) {\n      if (has_boundary_in_this_direction) {\n        external_boundaries_.emplace(direction);\n      }\n    } else {\n      ASSERT(has_boundary_in_this_direction,\n             \"Cannot specify a neighbor in a direction with no boundary.  \"\n             \"Topologies: \"\n                 << topologies_ << \"; Direction: \" << direction);\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\nconst domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial,\n                                VolumeDim>&\nBlock<VolumeDim>::stationary_map() const {\n  ASSERT(stationary_map_ != nullptr,\n         \"The stationary map is set to nullptr and so cannot be retrieved. \"\n         \"This is because the domain is time-dependent and so there are two \"\n         \"maps: the Logical to Grid map and the Grid to Inertial map.\");\n  return *stationary_map_;\n}\n\ntemplate <size_t VolumeDim>\nconst domain::CoordinateMapBase<Frame::BlockLogical, Frame::Grid, VolumeDim>&\nBlock<VolumeDim>::moving_mesh_logical_to_grid_map() const {\n  ASSERT(moving_mesh_logical_to_grid_map_ != nullptr,\n         \"The moving mesh Logical to Grid map is set to nullptr and so cannot \"\n         \"be retrieved. This is because the domain is time-independent and so \"\n         \"only the stationary map exists.\");\n  return *moving_mesh_logical_to_grid_map_;\n}\n\ntemplate <size_t VolumeDim>\nconst domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\nBlock<VolumeDim>::moving_mesh_grid_to_inertial_map() const {\n  ASSERT(moving_mesh_grid_to_inertial_map_ != nullptr,\n         \"The moving mesh Grid to Inertial map is set to nullptr and so cannot \"\n         \"be retrieved. This is because the domain is time-independent and so \"\n         \"only the stationary map exists.\");\n  return *moving_mesh_grid_to_inertial_map_;\n}\n\ntemplate <size_t VolumeDim>\nconst domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, VolumeDim>&\nBlock<VolumeDim>::moving_mesh_grid_to_distorted_map() const {\n  ASSERT(\n      moving_mesh_grid_to_distorted_map_ != nullptr,\n      \"The moving mesh Grid to Distorted map is set to nullptr and so cannot \"\n      \"be retrieved. This is because there is no map from the Grid to the \"\n      \"Distorted Frame.\");\n  return *moving_mesh_grid_to_distorted_map_;\n}\n\ntemplate <size_t VolumeDim>\nconst domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, VolumeDim>&\nBlock<VolumeDim>::moving_mesh_distorted_to_inertial_map() const {\n  ASSERT(\n      moving_mesh_distorted_to_inertial_map_ != nullptr,\n      \"The moving mesh Distorted to Inertial map is set to nullptr and so \"\n      \"cannot \"\n      \"be retrieved. This is because there is no map from the Distorted to the \"\n      \"Inertial Frame.\");\n  return *moving_mesh_distorted_to_inertial_map_;\n}\n\ntemplate <size_t VolumeDim>\nvoid Block<VolumeDim>::inject_time_dependent_map(\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n        moving_mesh_grid_to_inertial_map,\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, VolumeDim>>\n        moving_mesh_grid_to_distorted_map,\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, VolumeDim>>\n        moving_mesh_distorted_to_inertial_map) {\n  ASSERT(stationary_map_ != nullptr,\n         \"Cannot inject time-dependent map into a block that already has a \"\n         \"time-dependent map.\");\n  moving_mesh_grid_to_inertial_map_ =\n      std::move(moving_mesh_grid_to_inertial_map);\n  moving_mesh_logical_to_grid_map_ = stationary_map_->get_to_grid_frame();\n  moving_mesh_grid_to_distorted_map_ =\n      std::move(moving_mesh_grid_to_distorted_map);\n  moving_mesh_distorted_to_inertial_map_ =\n      std::move(moving_mesh_distorted_to_inertial_map);\n  stationary_map_ = nullptr;\n}\n\ntemplate <size_t VolumeDim>\nvoid Block<VolumeDim>::pup(PUP::er& p) {\n  size_t version = 3;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | stationary_map_;\n    p | moving_mesh_logical_to_grid_map_;\n    p | moving_mesh_grid_to_inertial_map_;\n    p | moving_mesh_grid_to_distorted_map_;\n    p | moving_mesh_distorted_to_inertial_map_;\n    p | id_;\n    p | neighbors_;\n    p | external_boundaries_;\n  }\n  if (version >= 1) {\n    p | name_;\n  }\n  if (version == 2) {\n    auto basis = make_array<VolumeDim>(Spectral::Basis::Uninitialized);\n    p | basis;\n  }\n  if (version < 3) {\n    topologies_ = make_array<VolumeDim>(domain::Topology::I1);\n  } else {\n    p | topologies_;\n  }\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const Block<VolumeDim>& block) {\n  os << \"Block \" << block.id() << \" (\" << block.name() << \"):\\n\";\n  os << \"Topology: \" << block.topologies() << '\\n';\n  os << \"Neighbors: \" << block.neighbors() << '\\n';\n  os << \"External boundaries: \" << block.external_boundaries() << '\\n';\n  os << \"Is time dependent: \" << std::boolalpha << block.is_time_dependent();\n  return os;\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const Block<VolumeDim>& lhs, const Block<VolumeDim>& rhs) {\n  bool blocks_are_equal =\n      (lhs.id() == rhs.id() and lhs.neighbors() == rhs.neighbors() and\n       lhs.external_boundaries() == rhs.external_boundaries() and\n       lhs.name() == rhs.name() and lhs.topologies() == rhs.topologies() and\n       lhs.is_time_dependent() == rhs.is_time_dependent() and\n       lhs.has_distorted_frame() == rhs.has_distorted_frame());\n\n  if (lhs.is_time_dependent() and not lhs.has_distorted_frame()) {\n    blocks_are_equal =\n        blocks_are_equal and (lhs.moving_mesh_logical_to_grid_map() ==\n                                  rhs.moving_mesh_logical_to_grid_map() and\n                              lhs.moving_mesh_grid_to_inertial_map() ==\n                                  rhs.moving_mesh_grid_to_inertial_map());\n  } else if (lhs.is_time_dependent() and lhs.has_distorted_frame()) {\n    blocks_are_equal = blocks_are_equal and\n                       (lhs.moving_mesh_logical_to_grid_map() ==\n                        rhs.moving_mesh_logical_to_grid_map()) and\n                       (lhs.moving_mesh_grid_to_distorted_map() ==\n                            rhs.moving_mesh_grid_to_distorted_map() and\n                        lhs.moving_mesh_distorted_to_inertial_map() ==\n                            rhs.moving_mesh_distorted_to_inertial_map());\n  } else {\n    blocks_are_equal =\n        blocks_are_equal and (lhs.stationary_map() == rhs.stationary_map());\n  }\n  return blocks_are_equal;\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Block<VolumeDim>& lhs, const Block<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                          \\\n  template class Block<GET_DIM(data)>;                                  \\\n  template std::ostream& operator<<(std::ostream& os,                   \\\n                                    const Block<GET_DIM(data)>& block); \\\n  template bool operator==(const Block<GET_DIM(data)>& lhs,             \\\n                           const Block<GET_DIM(data)>& rhs);            \\\n  template bool operator!=(const Block<GET_DIM(data)>& lhs,             \\\n                           const Block<GET_DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/Block.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class template Block.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iosfwd>\n#include <memory>\n#include <string>\n#include <unordered_set>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct BlockLogical;\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup ComputationalDomainGroup\n/// A Block<VolumeDim> is a region of a VolumeDim-dimensional computational\n/// domain that defines the root node of a tree which is used to construct the\n/// Elements that cover a region of the computational domain.\n///\n/// Each codimension 1 boundary of a Block<VolumeDim> is either an external\n/// boundary or identical to a boundary of one or more Blocks.\n///\n/// A Block has logical coordinates that depend upon the topology in each\n/// dimension.  The global coordinates are obtained from the logical\n/// coordinates from the Coordinatemap:  CoordinateMap::operator() takes\n/// Points in the BlockLogical Frame (i.e., block logical coordinates) and\n/// returns Points in the Inertial Frame (i.e., the global coordinate frame in\n/// which the problem to be solved is set up).\ntemplate <size_t VolumeDim>\nclass Block {\n public:\n  /// \\param stationary_map the CoordinateMap.\n  /// \\param id a unique ID.\n  /// \\param neighbors info about the Blocks that share a codimension 1\n  /// boundary with this Block.\n  /// \\param name Human-readable name for the block\n  /// \\param topologies domain::Topology in each dimension (default value is\n  /// domain::Topology::I1)\n  Block(std::unique_ptr<domain::CoordinateMapBase<\n            Frame::BlockLogical, Frame::Inertial, VolumeDim>>&& stationary_map,\n        size_t id, DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>> neighbors,\n        std::string name = \"\",\n        std::array<domain::Topology, VolumeDim> topologies =\n            domain::topologies::hypercube<VolumeDim>);\n\n  Block() = default;\n  ~Block() = default;\n  Block(const Block&) = delete;\n  Block(Block&&) = default;\n  Block& operator=(const Block&) = delete;\n  Block& operator=(Block&&) = default;\n\n  /// \\brief The map used when the coordinate map is time-independent.\n  ///\n  /// \\see is_time_dependent()\n  const domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial,\n                                  VolumeDim>&\n  stationary_map() const;\n\n  /// \\brief The map going from the block logical frame to the last time\n  /// independent frame. Only used when the coordinate map is time-dependent.\n  ///\n  /// \\see is_time_dependent() moving_mesh_grid_to_inertial_map()\n  const domain::CoordinateMapBase<Frame::BlockLogical, Frame::Grid, VolumeDim>&\n  moving_mesh_logical_to_grid_map() const;\n\n  /// \\brief The map going from the last time independent frame to the frame in\n  /// which the equations are solved. Only used when the coordinate map is\n  /// time-dependent.\n  ///\n  /// \\see is_time_dependent() moving_mesh_logical_to_grid_map()\n  const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n  moving_mesh_grid_to_inertial_map() const;\n\n  /// \\brief The map going from the last time independent frame to the\n  /// distorted frame. Only used when the coordinate map is\n  /// time-dependent. See \\ref domain_concepts to see how the distorted\n  /// frame is defined.\n  ///\n  /// \\see is_time_dependent() moving_mesh_distorted_to_grid_map()\n  const domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, VolumeDim>&\n  moving_mesh_grid_to_distorted_map() const;\n\n  /// \\brief The map going from the distorted frame to the frame in\n  /// which the equations are solved. Only used when the coordinate map is\n  /// time-dependent. See \\ref domain_concepts to see how the distorted\n  /// frame is defined.\n  ///\n  /// \\see is_time_dependent() moving_mesh_grid_to_distorted_map()\n  const domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, VolumeDim>&\n  moving_mesh_distorted_to_inertial_map() const;\n\n  /// \\brief Returns `true` if the block has time-dependent maps.\n  bool is_time_dependent() const { return stationary_map_ == nullptr; }\n\n  /// \\brief Returns `true` if the block has a distorted frame.\n  ///\n  /// If a block has a distorted frame, then\n  ///   - moving_mesh_grid_to_distorted_map() is non-null\n  ///   - moving_mesh_distorted_to_inertial_map() is non-null\n  ///   - moving_mesh_grid_to_inertial_map() is non-null\n  /// Note in particular the last point above:  If the block is time-dependent,\n  /// then the block must have a grid_to_inertial map independent of whether\n  /// it has a distorted frame.  This allows us to write more efficient maps.\n  /// In particular, we often care only about the grid_to_inertial map, so we\n  /// can code that map directly instead of composing\n  /// grid_to_distorted + distorted_to_inertial maps at runtime.\n  ///\n  /// If a block does not have a distorted frame, then\n  ///   - moving_mesh_grid_to_distorted_map() is null\n  ///   - moving_mesh_distorted_to_inertial_map() is null\n  ///   - moving_mesh_grid_to_inertial_map() is non-null\n  ///   - If we ever find ourselves needing ::Frame::Distorted coordinates\n  ///     in that block, we can assume that ::Frame::Distorted and ::Frame::Grid\n  ///     are the same.  Usually this case will not occur.\n  bool has_distorted_frame() const {\n    return moving_mesh_grid_to_distorted_map_ != nullptr and\n           moving_mesh_distorted_to_inertial_map_ != nullptr;\n  }\n\n  /// \\brief Given a Block that has a time-independent map, injects the\n  /// time-dependent map into the Block.\n  void inject_time_dependent_map(\n      std::unique_ptr<\n          domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n          moving_mesh_grid_to_inertial_map,\n      std::unique_ptr<\n          domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, VolumeDim>>\n          moving_mesh_grid_to_distorted_map = nullptr,\n      std::unique_ptr<domain::CoordinateMapBase<Frame::Distorted,\n                                                Frame::Inertial, VolumeDim>>\n          moving_mesh_distorted_to_inertial_map = nullptr);\n\n  /// A unique identifier for the Block that is in the range\n  /// [0, number_of_blocks -1] where number_of_blocks is the number\n  /// of Blocks that cover the computational domain.\n  size_t id() const { return id_; }\n\n  /// Information about the neighboring Blocks.\n  const DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>& neighbors() const {\n    return neighbors_;\n  }\n\n  /// The directions of the faces of the Block that are external boundaries.\n  const std::unordered_set<Direction<VolumeDim>>& external_boundaries() const {\n    return external_boundaries_;\n  }\n\n  const std::string& name() const { return name_; }\n\n  /// The topology in each dimensio of this Block\n  const std::array<domain::Topology, VolumeDim>& topologies() const {\n    return topologies_;\n  }\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <size_t LocalVolumeDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Block<LocalVolumeDim>& lhs,\n                         const Block<LocalVolumeDim>& rhs);\n\n  std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,\n                                            Frame::Inertial, VolumeDim>>\n      stationary_map_{nullptr};\n  std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, Frame::Grid, VolumeDim>>\n      moving_mesh_logical_to_grid_map_{nullptr};\n  std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n      moving_mesh_grid_to_inertial_map_{nullptr};\n  std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, VolumeDim>>\n      moving_mesh_grid_to_distorted_map_{nullptr};\n  std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, VolumeDim>>\n      moving_mesh_distorted_to_inertial_map_{nullptr};\n\n  size_t id_{0};\n  DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>> neighbors_;\n  std::unordered_set<Direction<VolumeDim>> external_boundaries_;\n  std::string name_;\n  std::array<domain::Topology, VolumeDim> topologies_;\n};\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const Block<VolumeDim>& block);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Block<VolumeDim>& lhs, const Block<VolumeDim>& rhs);\n"
  },
  {
    "path": "src/Domain/BlockLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/IdPair.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/BlockId.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\ntemplate <size_t Dim, typename Fr>\nstd::optional<tnsr::I<double, Dim, ::Frame::BlockLogical>>\nblock_logical_coordinates_single_point(\n    const tnsr::I<double, Dim, Fr>& input_point, const Block<Dim>& block,\n    const double time, const domain::FunctionsOfTimeMap& functions_of_time) {\n  std::optional<tnsr::I<double, Dim, ::Frame::BlockLogical>> logical_point{};\n  if (block.is_time_dependent()) {\n    if constexpr (std::is_same_v<Fr, ::Frame::Inertial>) {\n      // Point is in the inertial frame, so we need to map to the grid\n      // frame and then the logical frame.\n      const auto moving_inv = block.moving_mesh_grid_to_inertial_map().inverse(\n          input_point, time, functions_of_time);\n      if (not moving_inv.has_value()) {\n        return std::nullopt;\n      }\n      // logical to grid map is time-independent.\n      logical_point =\n          block.moving_mesh_logical_to_grid_map().inverse(moving_inv.value());\n    } else if constexpr (std::is_same_v<Fr, ::Frame::Distorted>) {\n      // Point is in the distorted frame, so we need to map to the grid\n      // frame and then the logical frame.\n      if (not block.has_distorted_frame()) {\n        // Note that block.has_distorted_frame() can be different for\n        // different Blocks.  However, the template parameter Frame is\n        // compile-time and is the same for all Blocks.\n        //\n        // Explanation of the logic here:\n        // 1. Recall that block_logical_coordinates loops through all the\n        //    Blocks, and skips all the Blocks except for the first Block\n        //    it finds that contains the point x.\n        // 2. If Frame is ::Frame::Distorted but\n        //    block.has_distorted_frame() is false, then this block\n        //    cannot contain the point x. Therefore, we should simply\n        //    skip this block.  If it turns out that no blocks contain\n        //    the point x, then we will get an error later.\n        //    (Note that our primary use case for ::Frame::Distorted is to\n        //    find an apparent horizon in the distorted frame. In that\n        //    case, only the Blocks near a horizon have a distorted frame\n        //    because only those Blocks have distortion maps. Thus,\n        //    the Blocks that are skipped here are those that are far\n        //    from horizons).\n        return std::nullopt;  // Not in this block\n      }\n      const auto moving_inv = block.moving_mesh_grid_to_distorted_map().inverse(\n          input_point, time, functions_of_time);\n      if (not moving_inv.has_value()) {\n        return std::nullopt;  // Not in this block\n      }\n      // logical to grid map is time-independent.\n      logical_point =\n          block.moving_mesh_logical_to_grid_map().inverse(moving_inv.value());\n    } else {\n      // frame is different than ::Frame::Inertial or ::Frame::Distorted.\n      // Currently 'time' is unused in this branch.\n      // To make the compiler happy, need to trick it to think that\n      // 'time' is used.\n      (void)time;\n      // Currently we only support Grid, Distorted and Inertial\n      // frames in the block, so make sure Frame is\n      // ::Frame::Grid. (The Inertial and Distorted cases were\n      // handled above.)\n      static_assert(std::is_same_v<Fr, ::Frame::Grid>,\n                    \"Cannot convert from given frame to Grid frame\");\n\n      // Point is in the grid frame, just map to logical frame.\n      logical_point =\n          block.moving_mesh_logical_to_grid_map().inverse(input_point);\n    }\n  } else {  // not block.is_time_dependent()\n    if constexpr (std::is_same_v<Fr, ::Frame::Inertial>) {\n      logical_point = block.stationary_map().inverse(input_point);\n    } else {\n      // If the map is time-independent, then the grid, distorted, and\n      // inertial frames are the same.  So if we are in the grid\n      // or distorted frames, convert to the inertial frame\n      // (this conversion is just a type conversion).\n      // Otherwise throw a static_assert.\n      static_assert(std::is_same_v<Fr, ::Frame::Grid> or\n                        std::is_same_v<Fr, ::Frame::Distorted>,\n                    \"Cannot convert from given frame to Inertial frame\");\n      tnsr::I<double, Dim, ::Frame::Inertial> x_inertial(0.0);\n      for (size_t d = 0; d < Dim; ++d) {\n        x_inertial.get(d) = input_point.get(d);\n      }\n      logical_point = block.stationary_map().inverse(x_inertial);\n    }\n  }\n\n  if (not logical_point.has_value()) {\n    return std::nullopt;\n  }\n\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto topology = gsl::at(block.topologies(), d);\n    if (topology == domain::Topology::I1 or\n        topology == domain::Topology::B2Radial or\n        topology == domain::Topology::B3Radial) {\n      // Map inverses may report logical coordinates outside [-1, 1] due to\n      // numerical roundoff error. In that case we clamp them to -1 or 1 so\n      // that a consistent block is chosen here independent of roundoff error.\n      // Without this correction, points on block boundaries where both blocks\n      // report logical coordinates outside [-1, 1] by roundoff error would\n      // not be assigned to any block at all, even though they lie in the\n      // domain.\n      if (equal_within_roundoff(logical_point->get(d), 1.0)) {\n        logical_point->get(d) = 1.0;\n        continue;\n      }\n      if (equal_within_roundoff(logical_point->get(d), -1.0)) {\n        logical_point->get(d) = -1.0;\n        continue;\n      }\n      if (abs(logical_point->get(d)) > 1.0) {\n        return std::nullopt;\n      }\n      // Also snap the center of the block to exactly 0.0 in case it is slightly\n      // off due to roundoff error. This is important for the case of a point\n      // that is exactly at the center of a block, e.g. for deterministic\n      // interpolation.\n      if (equal_within_roundoff(logical_point->get(d), 0.)) {\n        logical_point->get(d) = 0.;\n      }\n    }\n  }\n\n  return logical_point;\n}\n\ntemplate <size_t Dim, typename Fr>\nBlockLogicalCoords<Dim> block_logical_coordinates_single_point(\n    const tnsr::I<double, Dim, Fr>& input_point, const Domain<Dim>& domain,\n    const double time, const domain::FunctionsOfTimeMap& functions_of_time,\n    const std::optional<gsl::not_null<std::vector<size_t>*>> block_order) {\n  // Check which block this point is in. Each point will be in one\n  // and only one block, unless it is on a shared boundary.  In that\n  // case, choose the first matching block (and this block will have\n  // the smallest block_id).\n  // In case a block_order is provided, it is no longer guaranteed that the\n  // block with the smallest block_id is chosen.\n  if (block_order.has_value() and block_order.value()->empty()) {\n    // If the block order is empty, fill it with the list of blocks in the\n    // domain\n    block_order.value()->resize(domain.blocks().size());\n    alg::iota(*block_order.value(), 0_st);\n  }\n  const size_t num_blocks = block_order.has_value()\n                                ? block_order.value()->size()\n                                : domain.blocks().size();\n  ASSERT(num_blocks <= domain.blocks().size(),\n         \"The block order has more entries than the domain has blocks.\");\n  for (size_t i = 0; i < num_blocks; ++i) {\n    const size_t block_id =\n        block_order.has_value() ? (*block_order.value())[i] : i;\n    ASSERT(block_id < domain.blocks().size(),\n           \"Block ID \" << block_id << \" is out of bounds.\");\n    const auto& block = domain.blocks()[block_id];\n    auto x_logical = block_logical_coordinates_single_point(\n        input_point, block, time, functions_of_time);\n    if (x_logical.has_value()) {\n      if (block_order.has_value()) {\n        // Push this block to the front of the priority order\n        auto& order = *block_order.value();\n        const auto found = std::find(order.begin(), order.end(), block_id);\n        if (found != order.end()) {\n          order.erase(found);\n          order.insert(order.begin(), block_id);\n        }\n      }\n      return make_id_pair(domain::BlockId(block.id()),\n                          std::move(x_logical.value()));\n    }\n  }\n  return std::nullopt;\n}\n\ntemplate <size_t Dim, typename Fr>\nstd::vector<BlockLogicalCoords<Dim>> block_logical_coordinates(\n    const Domain<Dim>& domain, const tnsr::I<DataVector, Dim, Fr>& x,\n    const double time, const domain::FunctionsOfTimeMap& functions_of_time,\n    const std::optional<gsl::not_null<std::vector<size_t>*>> block_order) {\n  const size_t num_pts = get<0>(x).size();\n  std::vector<BlockLogicalCoords<Dim>> block_coord_holders(num_pts);\n  for (size_t s = 0; s < num_pts; ++s) {\n    tnsr::I<double, Dim, Fr> x_frame(0.0);\n    for (size_t d = 0; d < Dim; ++d) {\n      x_frame.get(d) = x.get(d)[s];\n    }\n    block_coord_holders[s] = block_logical_coordinates_single_point(\n        x_frame, domain, time, functions_of_time, block_order);\n  }\n  return block_coord_holders;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::optional<tnsr::I<double, DIM(data), ::Frame::BlockLogical>>    \\\n  block_logical_coordinates_single_point(                                      \\\n      const tnsr::I<double, DIM(data), FRAME(data)>& input_point,              \\\n      const Block<DIM(data)>& block, const double time,                        \\\n      const domain::FunctionsOfTimeMap& functions_of_time);                    \\\n  template BlockLogicalCoords<DIM(data)>                                       \\\n  block_logical_coordinates_single_point(                                      \\\n      const tnsr::I<double, DIM(data), FRAME(data)>& input_point,              \\\n      const Domain<DIM(data)>& domain, const double time,                      \\\n      const domain::FunctionsOfTimeMap& functions_of_time,                     \\\n      std::optional<gsl::not_null<std::vector<size_t>*>> block_order);         \\\n  template std::vector<BlockLogicalCoords<DIM(data)>>                          \\\n  block_logical_coordinates(                                                   \\\n      const Domain<DIM(data)>& domain,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& x, const double time, \\\n      const domain::FunctionsOfTimeMap& functions_of_time,                     \\\n      std::optional<gsl::not_null<std::vector<size_t>*>> block_order);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3),\n                        (::Frame::Grid, ::Frame::Distorted, ::Frame::Inertial))\n\n#undef FRAME\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Domain/BlockLogicalCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/IdPair.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/BlockId.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Domain;\ntemplate <size_t VolumeDim>\nclass Block;\n/// \\endcond\n\ntemplate <size_t Dim>\nusing BlockLogicalCoords = std::optional<\n    IdPair<domain::BlockId, tnsr::I<double, Dim, Frame::BlockLogical>>>;\n\n/// @{\n/// \\ingroup ComputationalDomainGroup\n///\n/// Computes the block logical coordinates and the containing `BlockId` of\n/// a set of points, given coordinates in a particular frame.\n///\n/// \\details Returns a std::vector<std::optional<IdPair<BlockId,coords>>>,\n/// where the vector runs over the points and is indexed in the same order as\n/// the input coordinates `x`. For each point, the `IdPair` holds the\n/// block logical coords of that point and the `BlockId` of the `Block` that\n/// contains that point.\n/// The std::optional is invalid if the point is not in any Block.\n/// If a point is on a shared boundary of two or more `Block`s, it is\n/// returned only once, and is considered to belong to the `Block`\n/// with the smaller `BlockId`.\n///\n/// The `block_logical_coordinates_single_point` function will search the passed\n/// in block for the passed in coordinate and return the logical coordinates of\n/// that point. It will return a `std::nullopt` if it can't find the point in\n/// that block.\n///\n/// \\warning Since map inverses can involve numerical roundoff error, care must\n/// be taken with points on shared block boundaries. They will be assigned to\n/// the first block (by block ID) that contains the point _within roundoff\n/// error_. Therefore, be advised to use the logical coordinates returned by\n/// this function, which are guaranteed to be in [-1, 1] and can be safely\n/// passed along to `element_logical_coordinates`.\n///\n/// \\warning `block_logical_coordinates` with x in\n/// `::Frame::Distorted` ignores all `Block`s that lack a distorted\n/// frame, and it will return std::nullopt for points that lie outside\n/// all distorted-frame-endowed `Block`s. This is what is expected for\n/// typical use cases.  This means that `block_logical_coordinates`\n/// does not assume that grid and distorted frames are equal in\n/// `Block`s that lack a distorted frame.\ntemplate <size_t Dim, typename Fr>\nauto block_logical_coordinates(\n    const Domain<Dim>& domain, const tnsr::I<DataVector, Dim, Fr>& x,\n    double time = std::numeric_limits<double>::signaling_NaN(),\n    const domain::FunctionsOfTimeMap& functions_of_time = {},\n    std::optional<gsl::not_null<std::vector<size_t>*>> block_order =\n        std::nullopt) -> std::vector<BlockLogicalCoords<Dim>>;\n\ntemplate <size_t Dim, typename Fr>\nstd::optional<tnsr::I<double, Dim, ::Frame::BlockLogical>>\nblock_logical_coordinates_single_point(\n    const tnsr::I<double, Dim, Fr>& input_point, const Block<Dim>& block,\n    double time = std::numeric_limits<double>::signaling_NaN(),\n    const domain::FunctionsOfTimeMap& functions_of_time = {});\n\n/// A `block_order` can be provided to this overload to speed up the search for\n/// the block containing the point. When the point is found, the containing\n/// block will be pushed to the front of the list. If an empty block order is\n/// provided, it will be initially filled with the list of blocks in the domain\n/// and then updated. Note that when a block order is provided, the block with\n/// the smallest `BlockId` is no longer guaranteed to be chosen.\ntemplate <size_t Dim, typename Fr>\nBlockLogicalCoords<Dim> block_logical_coordinates_single_point(\n    const tnsr::I<double, Dim, Fr>& input_point, const Domain<Dim>& domain,\n    double time = std::numeric_limits<double>::signaling_NaN(),\n    const domain::FunctionsOfTimeMap& functions_of_time = {},\n    std::optional<gsl::not_null<std::vector<size_t>*>> block_order =\n        std::nullopt);\n/// @}\n"
  },
  {
    "path": "src/Domain/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief %Domain support for applying boundary conditions\nnamespace domain::BoundaryConditions {\n/*!\n * \\brief Base class from which all system-specific base classes must inherit.\n */\nclass BoundaryCondition : public PUP::able {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* const msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(BoundaryCondition);  // NOLINT\n\n  virtual auto get_clone() const -> std::unique_ptr<BoundaryCondition> = 0;\n};\n}  // namespace domain::BoundaryConditions\n"
  },
  {
    "path": "src/Domain/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DomainBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  None.cpp\n  Periodic.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  GetBoundaryConditionsBase.hpp\n  None.hpp\n  Periodic.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Options\n  Serialization\n  Utilities\n  INTERFACE\n  ErrorHandling\n  )\n"
  },
  {
    "path": "src/Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n\nnamespace domain::BoundaryConditions {\nCREATE_HAS_TYPE_ALIAS(boundary_conditions_base)\nCREATE_HAS_TYPE_ALIAS_V(boundary_conditions_base)\n\nnamespace detail {\n// used as passive error message\nstruct TheSystemHasNoBoundaryConditionsBaseTypeAlias {};\n\ntemplate <typename T, typename = std::void_t<>>\nstruct get_boundary_conditions_base {\n  using type = TheSystemHasNoBoundaryConditionsBaseTypeAlias;\n};\n\ntemplate <typename T>\nstruct get_boundary_conditions_base<\n    T, std::void_t<typename T::boundary_conditions_base>> {\n  using type = typename T::boundary_conditions_base;\n};\n}  // namespace detail\n\n/// Returns `T::boundary_condition_base` or a placeholder class.\ntemplate <typename T>\nusing get_boundary_conditions_base =\n    typename detail::get_boundary_conditions_base<T>::type;\n}  // namespace domain::BoundaryConditions\n"
  },
  {
    "path": "src/Domain/BoundaryConditions/None.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/BoundaryConditions/None.hpp\"\n\nnamespace domain::BoundaryConditions {\nMarkAsNone::~MarkAsNone() = default;\n\nbool is_none(const std::unique_ptr<BoundaryCondition>& boundary_condition) {\n  return dynamic_cast<const MarkAsNone* const>(boundary_condition.get()) !=\n         nullptr;\n}\n}  // namespace domain::BoundaryConditions\n"
  },
  {
    "path": "src/Domain/BoundaryConditions/None.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::BoundaryConditions {\nclass MarkAsNone {\n public:\n  MarkAsNone() = default;\n  MarkAsNone(MarkAsNone&&) = default;\n  MarkAsNone& operator=(MarkAsNone&&) = default;\n  MarkAsNone(const MarkAsNone&) = default;\n  MarkAsNone& operator=(const MarkAsNone&) = default;\n  virtual ~MarkAsNone() = 0;\n};\n\n/*!\n * \\brief None boundary conditions.\n *\n * This boundary condition doesn't actually do anything, and gets pretty much\n * completely ignored by everything but the domain creator internals. The domain\n * creator internals can use `None` as a way of specifying \"boundary conditions\"\n * without a system. It can also be used in cases like the BinaryCompactObject\n * domain where there may be no excision boundaries, and so the excision\n * boundary condition must be `None` in that case so the domain creator can be\n * sure the domain is set in a consistent state.\n *\n * To use with a specific system add:\n *\n * \\code\n *  domain::BoundaryConditions::None<your::system::BoundaryConditionBase>\n * \\endcode\n *\n * to the list of creatable classes.\n *\n * \\warning if you want an outflow-type boundary condition, you must implement\n * one, not use `None.\n */\ntemplate <typename SystemBoundaryConditionBaseClass>\nstruct None final : public SystemBoundaryConditionBaseClass, public MarkAsNone {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"None boundary condition. Used only during domain creation to ensure a \"\n      \"consistent state to the domain.\"};\n  static std::string name() { return \"None\"; }\n\n  None() = default;\n  None(None&&) = default;\n  None& operator=(None&&) = default;\n  None(const None&) = default;\n  None& operator=(const None&) = default;\n  ~None() override = default;\n\n  explicit None(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, None);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  void pup(PUP::er& p) override;\n};\n\ntemplate <typename SystemBoundaryConditionBaseClass>\nNone<SystemBoundaryConditionBaseClass>::None(CkMigrateMessage* const msg)\n    : SystemBoundaryConditionBaseClass(msg) {}\n\ntemplate <typename SystemBoundaryConditionBaseClass>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nNone<SystemBoundaryConditionBaseClass>::get_clone() const {\n  return std::make_unique<None>(*this);\n}\n\ntemplate <typename SystemBoundaryConditionBaseClass>\nvoid None<SystemBoundaryConditionBaseClass>::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n}\n\n/// \\cond\ntemplate <typename SystemBoundaryConditionBaseClass>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID None<SystemBoundaryConditionBaseClass>::my_PUP_ID = 0;\n/// \\endcond\n\n/// Check if a boundary condition inherits from `MarkAsNone`, which\n/// constitutes as it being marked as a none boundary condition.\nbool is_none(const std::unique_ptr<BoundaryCondition>& boundary_condition);\n}  // namespace domain::BoundaryConditions\n"
  },
  {
    "path": "src/Domain/BoundaryConditions/Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n\nnamespace domain::BoundaryConditions {\nMarkAsPeriodic::~MarkAsPeriodic() = default;\n\nbool is_periodic(const std::unique_ptr<BoundaryCondition>& boundary_condition) {\n  return dynamic_cast<const MarkAsPeriodic* const>(boundary_condition.get()) !=\n         nullptr;\n}\n}  // namespace domain::BoundaryConditions\n"
  },
  {
    "path": "src/Domain/BoundaryConditions/Periodic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::BoundaryConditions {\n/// Mark a boundary condition as being periodic.\n///\n/// Periodic boundary conditions shouldn't require any implementation outside of\n/// a check in the domain creator using the `is_periodic()` function to\n/// determine what boundaries are periodic. Across each matching pair of\n/// periodic boundary conditions, the domain creator should specify that the DG\n/// elements are neighbors of each other.\nclass MarkAsPeriodic {\n public:\n  MarkAsPeriodic() = default;\n  MarkAsPeriodic(MarkAsPeriodic&&) = default;\n  MarkAsPeriodic& operator=(MarkAsPeriodic&&) = default;\n  MarkAsPeriodic(const MarkAsPeriodic&) = default;\n  MarkAsPeriodic& operator=(const MarkAsPeriodic&) = default;\n  virtual ~MarkAsPeriodic() = 0;\n};\n\n/*!\n * \\brief Periodic boundary conditions.\n *\n * To use with a specific system add:\n *\n * \\code\n *  domain::BoundaryConditions::Periodic<your::system::BoundaryConditionBase>\n * \\endcode\n *\n * to the list of creatable classes.\n *\n * \\note Not all domain creators will allow you to specify periodic boundary\n * conditions since they may not make sense.\n */\ntemplate <typename SystemBoundaryConditionBaseClass>\nstruct Periodic final : public SystemBoundaryConditionBaseClass,\n                        public MarkAsPeriodic {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Periodic boundary conditions.\\n\\nNote: Not all domain creators will \"\n      \"allow you to specify periodic boundary conditions since they may not \"\n      \"make sense.\"};\n  static std::string name() { return \"Periodic\"; }\n\n  Periodic() = default;\n  Periodic(Periodic&&) = default;\n  Periodic& operator=(Periodic&&) = default;\n  Periodic(const Periodic&) = default;\n  Periodic& operator=(const Periodic&) = default;\n  ~Periodic() override = default;\n\n  explicit Periodic(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, Periodic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  void pup(PUP::er& p) override;\n};\n\ntemplate <typename SystemBoundaryConditionBaseClass>\nPeriodic<SystemBoundaryConditionBaseClass>::Periodic(\n    CkMigrateMessage* const msg)\n    : SystemBoundaryConditionBaseClass(msg) {}\n\ntemplate <typename SystemBoundaryConditionBaseClass>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nPeriodic<SystemBoundaryConditionBaseClass>::get_clone() const {\n  return std::make_unique<Periodic>(*this);\n}\n\ntemplate <typename SystemBoundaryConditionBaseClass>\nvoid Periodic<SystemBoundaryConditionBaseClass>::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n}\n\n/// \\cond\ntemplate <typename SystemBoundaryConditionBaseClass>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Periodic<SystemBoundaryConditionBaseClass>::my_PUP_ID = 0;\n/// \\endcond\n\n/// Check if a boundary condition inherits from `MarkAsPeriodic`, which\n/// constitutes as it being marked as a periodic boundary condition.\nbool is_periodic(const std::unique_ptr<BoundaryCondition>& boundary_condition);\n}  // namespace domain::BoundaryConditions\n"
  },
  {
    "path": "src/Domain/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Domain)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AreaElement.cpp\n  Block.cpp\n  BlockLogicalCoordinates.cpp\n  CoordsToDifferentFrame.cpp\n  CreateInitialElement.cpp\n  Domain.cpp\n  DomainHelpers.cpp\n  ElementDistribution.cpp\n  ElementLogicalCoordinates.cpp\n  ElementMap.cpp\n  ElementToBlockLogicalMap.cpp\n  ExcisionSphere.cpp\n  FaceNormal.cpp\n  FlatLogicalMetric.cpp\n  InterfaceLogicalCoordinates.cpp\n  JacobianDiagnostic.cpp\n  MinimumGridSpacing.cpp\n  RadiallyCompressedCoordinates.cpp\n  SizeOfElement.cpp\n  StrahlkorperTransformations.cpp\n  TagsTimeDependent.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AreaElement.hpp\n  Block.hpp\n  BlockLogicalCoordinates.hpp\n  CoordsToDifferentFrame.hpp\n  CreateInitialElement.hpp\n  Domain.hpp\n  DomainHelpers.hpp\n  ElementDistribution.hpp\n  ElementLogicalCoordinates.hpp\n  ElementMap.hpp\n  ElementToBlockLogicalMap.hpp\n  ExcisionSphere.hpp\n  FaceNormal.hpp\n  FlatLogicalMetric.hpp\n  InterfaceComputeTags.hpp\n  InterfaceHelpers.hpp\n  InterfaceLogicalCoordinates.hpp\n  JacobianDiagnostic.hpp\n  MinimumGridSpacing.hpp\n  RadiallyCompressedCoordinates.hpp\n  SizeOfElement.hpp\n  StrahlkorperTransformations.hpp\n  Tags.hpp\n  TagsCharacteristicSpeeds.hpp\n  TagsTimeDependent.hpp\n)\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Autodiff\n  Boost::boost\n  CoordinateMaps\n  DataStructures\n  DomainBoundaryConditions\n  DomainStructure\n  ErrorHandling\n  FunctionsOfTime\n  Options\n  Spectral\n  Utilities\n  PRIVATE\n  LinearOperators\n  RootFinding\n  SphericalHarmonics\n)\n\nadd_subdirectory(Amr)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(CoordinateMaps)\nadd_subdirectory(Creators)\nadd_subdirectory(FunctionsOfTime)\nadd_subdirectory(Python)\nadd_subdirectory(Structure)\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Affine.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nAffine::Affine(const double A, const double B, const double a, const double b)\n    : A_(A),\n      B_(B),\n      a_(a),\n      b_(b),\n      length_of_domain_(B - A),\n      length_of_range_(b - a),\n      jacobian_([A, B, a, b]() {\n        ASSERT(A != B and a != b,\n               \"The left and right boundaries for both source and target \"\n               \"interval must differ, but are; [\"\n                   << A << \", \" << B << \"] -> [\" << a << \", \" << b << \"]\");\n        return (b - a) / (B - A);\n      }()),\n      inverse_jacobian_(length_of_domain_ / length_of_range_),\n      is_identity_(A == a and B == b) {}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 1> Affine::operator()(\n    const std::array<T, 1>& source_coords) const {\n  return {{(length_of_range_ * source_coords[0] + a_ * B_ - b_ * A_) /\n           length_of_domain_}};\n}\n\nstd::optional<std::array<double, 1>> Affine::inverse(\n    const std::array<double, 1>& target_coords) const {\n  return {{{(length_of_domain_ * target_coords[0] - a_ * B_ + b_ * A_) /\n            length_of_range_}}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> Affine::jacobian(\n    const std::array<T, 1>& source_coords) const {\n  return make_with_value<\n      tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame>>(\n      dereference_wrapper(source_coords[0]), jacobian_);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> Affine::inv_jacobian(\n    const std::array<T, 1>& source_coords) const {\n  return make_with_value<\n      tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame>>(\n      dereference_wrapper(source_coords[0]), inverse_jacobian_);\n}\n\nvoid Affine::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | A_;\n    p | B_;\n    p | a_;\n    p | b_;\n    p | length_of_domain_;\n    p | length_of_range_;\n    p | jacobian_;\n    p | inverse_jacobian_;\n    p | is_identity_;\n  }\n}\n\nbool operator==(const CoordinateMaps::Affine& lhs,\n                const CoordinateMaps::Affine& rhs) {\n  return lhs.A_ == rhs.A_ and lhs.B_ == rhs.B_ and lhs.a_ == rhs.a_ and\n         lhs.b_ == rhs.b_ and lhs.length_of_domain_ == rhs.length_of_domain_ and\n         lhs.length_of_range_ == rhs.length_of_range_ and\n         lhs.jacobian_ == rhs.jacobian_ and\n         lhs.inverse_jacobian_ == rhs.inverse_jacobian_ and\n         lhs.is_identity_ == rhs.is_identity_;\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 1>               \\\n  Affine::operator()(const std::array<DTYPE(data), 1>& source_coords) const; \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 1, Frame::NoFrame> \\\n  Affine::jacobian(const std::array<DTYPE(data), 1>& source_coords) const;   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 1, Frame::NoFrame> \\\n  Affine::inv_jacobian(const std::array<DTYPE(data), 1>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, MAP_AUTODIFF_TYPES)\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Affine.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class Affine.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n * \\brief Affine map from \\f$\\xi \\in [A, B]\\rightarrow x \\in [a, b]\\f$.\n *\n * The formula for the mapping is...\n * \\f[\n * x = \\frac{b}{B-A} (\\xi-A) +\\frac{a}{B-A}(B-\\xi)\n * \\f]\n * \\f[\n * \\xi =\\frac{B}{b-a} (x-a) +\\frac{A}{b-a}(b-x)\n * \\f]\n */\nclass Affine {\n public:\n  static constexpr size_t dim = 1;\n\n  Affine(double A, double B, double a, double b);\n\n  Affine() = default;\n  ~Affine() = default;\n  Affine(const Affine&) = default;\n  Affine(Affine&&) = default;  // NOLINT\n  Affine& operator=(const Affine&) = default;\n  Affine& operator=(Affine&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 1> operator()(\n      const std::array<T, 1>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 1>> inverse(\n      const std::array<double, 1>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> jacobian(\n      const std::array<T, 1>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 1>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  friend bool operator==(const Affine& lhs, const Affine& rhs);\n\n  double A_{-1.0};\n  double B_{1.0};\n  double a_{-1.0};\n  double b_{1.0};\n  double length_of_domain_{2.0};  // B-A\n  double length_of_range_{2.0};   // b-a\n  double jacobian_{length_of_range_ / length_of_domain_};\n  double inverse_jacobian_{length_of_domain_ / length_of_range_};\n  bool is_identity_{false};\n};\n\ninline bool operator!=(const CoordinateMaps::Affine& lhs,\n                       const CoordinateMaps::Affine& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n\n#ifdef SPECTRE_AUTODIFF\n#define MAP_AUTODIFF_TYPES                                  \\\n  (autodiff::SecondOrderDual, autodiff::SecondOrderDualNum, \\\n   std::reference_wrapper<const autodiff::SecondOrderDual>, \\\n   std::reference_wrapper<const autodiff::SecondOrderDualNum>)\n#else\n#define MAP_AUTODIFF_TYPES\n#endif\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/BulgedCube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/BulgedCube.hpp\"\n\n#include <cmath>\n#include <exception>\n#include <functional>\n#include <limits>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\nclass RootFunction {\n public:\n  RootFunction(const double radius, const double sphericity,\n               const double physical_r_squared, const double x_sq,\n               const double y_sq, const double z_sq)\n      : radius_(radius),\n        sphericity_(sphericity),\n        physical_r_squared_(physical_r_squared),\n        x_sq_(x_sq),\n        y_sq_(y_sq),\n        z_sq_(z_sq) {\n    ASSERT(physical_r_squared_ > 0.0,\n           \"The RootFunction for the inverse map of BulgedCube is only valid \"\n           \"for a radius greater than zero. The zero-radius case should be \"\n           \"handled explicitly without call the root finder.\");\n  }\n\n  double operator()(const double rho) const {\n    const double x_sq_over_r_sq = x_sq_ / physical_r_squared_;\n    const double y_sq_over_r_sq = y_sq_ / physical_r_squared_;\n    const double z_sq_over_r_sq = z_sq_ / physical_r_squared_;\n    return sqrt(physical_r_squared_) -\n           radius_ * rho *\n               (1.0 / sqrt(3.0) +\n                sphericity_ *\n                    (1.0 / sqrt(1.0 + square(rho) *\n                                          (x_sq_over_r_sq + y_sq_over_r_sq)) +\n                     1.0 / sqrt(1.0 + square(rho) *\n                                          (x_sq_over_r_sq + z_sq_over_r_sq)) +\n                     1.0 / sqrt(1.0 + square(rho) *\n                                          (y_sq_over_r_sq + z_sq_over_r_sq)) -\n                     1.0 / sqrt(2.0 + square(rho) * x_sq_over_r_sq) -\n                     1.0 / sqrt(2.0 + square(rho) * y_sq_over_r_sq) -\n                     1.0 / sqrt(2.0 + square(rho) * z_sq_over_r_sq)));\n  }\n  double get_r_sq() const { return physical_r_squared_; }\n\n private:\n  const double radius_;\n  const double sphericity_;\n  const double physical_r_squared_;\n  const double x_sq_;\n  const double y_sq_;\n  const double z_sq_;\n};\n\nstd::optional<double> scaling_factor(RootFunction&& rootfunction) {\n  const double physical_r_squared = rootfunction.get_r_sq();\n  try {\n    constexpr double tol = 10.0 * std::numeric_limits<double>::epsilon();\n    // Use a small nonzero number since the inverse map is singular at r==0 and\n    // that case is handled separately.\n    constexpr double lower_bound = std::numeric_limits<double>::min();\n    // upper_bound = sqrt(3) + tol\n    constexpr double upper_bound = 1.7320508075688772 + tol;\n    double rho =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(rootfunction, lower_bound, upper_bound, tol, tol);\n    rho /= sqrt(physical_r_squared);\n    return rho;\n  } catch (std::exception& exception) {\n    return std::nullopt;\n  }\n}\n}  // namespace\n\nnamespace domain::CoordinateMaps {\nBulgedCube::BulgedCube(const double radius, const double sphericity,\n                       const bool use_equiangular_map)\n    : radius_(radius),\n      sphericity_(sphericity),\n      use_equiangular_map_(use_equiangular_map),\n      is_identity_(radius_ == sqrt(3.0) and sphericity_ == 0.0 and\n                   not use_equiangular_map_) {\n  ASSERT(radius > 0.0, \"The radius of the cube must be greater than zero\");\n  ASSERT(sphericity >= 0.0 and sphericity < 1.0,\n         \"The sphericity must be strictly less than one.\");\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> BulgedCube::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const auto physical_coordinates = [this](const ReturnType& cap_xi,\n                                           const ReturnType& cap_eta,\n                                           const ReturnType& cap_zeta) {\n    const auto one_over_rho_xi = 1.0 / sqrt(2.0 + square(cap_xi));\n    const auto one_over_rho_eta = 1.0 / sqrt(2.0 + square(cap_eta));\n    const auto one_over_rho_zeta = 1.0 / sqrt(2.0 + square(cap_zeta));\n    const auto one_over_rho_xi_eta =\n        1.0 / sqrt(1.0 + square(cap_xi) + square(cap_eta));\n    const auto one_over_rho_xi_zeta =\n        1.0 / sqrt(1.0 + square(cap_xi) + square(cap_zeta));\n    // Making one_over_rho_eta_zeta a ReturnType object instead of an expression\n    // works around a weird issue in GCC-10 release mode (either a bug in the\n    // optimizer or an unusual edge-case in Blaze) where the expressions in\n    // radial_scaling_vector somehow have their internal pointers that should\n    // point to cap_xi, cap_eta, and cap_zeta invalidated. Since the pointer\n    // variable is optimized out it's unclear whether the pointer is set to\n    // nullptr or something else goes wrong.\n    // Note: we don't use a const ReturnType so that we can reuse the allocation\n    // below.\n    ReturnType one_over_rho_eta_zeta =\n        1.0 / sqrt(1.0 + square(cap_eta) + square(cap_zeta));\n    // Note: we don't use a const ReturnType for radial_scaling_factor so that\n    // we can reuse the allocation below.\n    ReturnType radial_scaling_factor =\n        radius_ * (1.0 / sqrt(3.0) +\n                   sphericity_ * (one_over_rho_eta_zeta + one_over_rho_xi_zeta +\n                                  one_over_rho_xi_eta - one_over_rho_xi -\n                                  one_over_rho_eta - one_over_rho_zeta));\n\n    ReturnType& physical_x = one_over_rho_eta_zeta;\n    physical_x = radial_scaling_factor * cap_xi;\n    ReturnType physical_y = radial_scaling_factor * cap_eta;\n    ReturnType& physical_z = radial_scaling_factor;\n    physical_z *= cap_zeta;\n    return std::array<ReturnType, 3>{\n        {std::move(physical_x), std::move(physical_y), std::move(physical_z)}};\n  };\n\n  if (use_equiangular_map_) {\n    return physical_coordinates(\n        tan(M_PI_4 * dereference_wrapper(source_coords[0])),\n        tan(M_PI_4 * dereference_wrapper(source_coords[1])),\n        tan(M_PI_4 * dereference_wrapper(source_coords[2])));\n  }\n  return physical_coordinates(dereference_wrapper(source_coords[0]),\n                              dereference_wrapper(source_coords[1]),\n                              dereference_wrapper(source_coords[2]));\n}\n\nstd::optional<std::array<double, 3>> BulgedCube::inverse(\n    const std::array<double, 3>& target_coords) const {\n  const double& physical_x = target_coords[0];\n  const double& physical_y = target_coords[1];\n  const double& physical_z = target_coords[2];\n  const double x_sq = square(physical_x);\n  const double y_sq = square(physical_y);\n  const double z_sq = square(physical_z);\n  const double physical_r_squared = x_sq + y_sq + z_sq;\n  // For physical_r_squared==0 we know that we are at the origin x=y=z=0, and we\n  // know analytically that this map maps the origin to itself. Handling the\n  // case r==0 case separately simplifies the root finding procedure for the\n  // scaling_factor.\n  if (UNLIKELY(physical_r_squared == 0.0)) {\n    return {{{0.0, 0.0, 0.0}}};\n  }\n\n  // We are not at the origin, find the scaling factor (does a root-find)\n  const auto scaling_factor =\n      // NOLINTNEXTLINE(clang-analyzer-core)\n      ::scaling_factor(\n      RootFunction{radius_, sphericity_, physical_r_squared, x_sq, y_sq, z_sq});\n  if (not scaling_factor.has_value()) {\n    return std::nullopt;\n  }\n  if (use_equiangular_map_) {\n    return {{{2.0 * M_2_PI * atan(physical_x * scaling_factor.value()),\n              2.0 * M_2_PI * atan(physical_y * scaling_factor.value()),\n              2.0 * M_2_PI * atan(physical_z * scaling_factor.value())}}};\n  }\n  return {{{physical_x * scaling_factor.value(),\n            physical_y * scaling_factor.value(),\n            physical_z * scaling_factor.value()}}};\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> BulgedCube::xi_derivative(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const auto derivative_lambda = [this](\n      const ReturnType& cap_xi, const ReturnType& cap_eta,\n      const ReturnType& cap_zeta, const auto& cap_xi_deriv) {\n    const ReturnType one_over_rho_xi_cubed =\n        pow<3>(1.0 / sqrt(2.0 + square(cap_xi)));\n    const ReturnType one_over_rho_eta = 1.0 / sqrt(2.0 + square(cap_eta));\n    const ReturnType one_over_rho_zeta = 1.0 / sqrt(2.0 + square(cap_zeta));\n    const ReturnType one_over_rho_xi_eta_cubed =\n        pow<3>(1.0 / sqrt(1.0 + square(cap_xi) + square(cap_eta)));\n    const ReturnType one_over_rho_xi_zeta_cubed =\n        pow<3>(1.0 / sqrt(1.0 + square(cap_xi) + square(cap_zeta)));\n    const ReturnType one_over_rho_eta_zeta =\n        1.0 / sqrt(1.0 + square(cap_eta) + square(cap_zeta));\n    const ReturnType common_factor =\n        sphericity_ * radius_ * cap_xi * cap_xi_deriv *\n        (one_over_rho_xi_cubed - one_over_rho_xi_eta_cubed -\n         one_over_rho_xi_zeta_cubed);\n\n    const ReturnType physical_x =\n        radius_ * cap_xi_deriv *\n        (1.0 / sqrt(3.0) +\n         sphericity_ *\n             (((1.0 + square(cap_eta)) * one_over_rho_xi_eta_cubed +\n               (1.0 + square(cap_zeta)) * one_over_rho_xi_zeta_cubed -\n               2.0 * one_over_rho_xi_cubed) +\n              one_over_rho_eta_zeta - one_over_rho_eta - one_over_rho_zeta));\n    const ReturnType physical_y = cap_eta * common_factor;\n    const ReturnType physical_z = cap_zeta * common_factor;\n\n    return std::array<ReturnType, 3>{{physical_x, physical_y, physical_z}};\n  };\n  if (use_equiangular_map_) {\n    return derivative_lambda(\n        tan(M_PI_4 * dereference_wrapper(source_coords[0])),\n        tan(M_PI_4 * dereference_wrapper(source_coords[1])),\n        tan(M_PI_4 * dereference_wrapper(source_coords[2])),\n        ReturnType{M_PI_4 *\n                   (1.0 + square(tan(M_PI_4 *\n                                     dereference_wrapper(source_coords[0]))))});\n  }\n  return derivative_lambda(dereference_wrapper(source_coords[0]),\n                           dereference_wrapper(source_coords[1]),\n                           dereference_wrapper(source_coords[2]), 1.0);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> BulgedCube::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  const auto dX_dxi = xi_derivative(source_coords);\n  const auto dX_deta = xi_derivative(\n      std::array<std::reference_wrapper<const tt::remove_cvref_wrap_t<T>>, 3>{\n          {std::cref(dereference_wrapper(source_coords[1])),\n           std::cref(dereference_wrapper(source_coords[0])),\n           std::cref(dereference_wrapper(source_coords[2]))}});\n  const auto dX_dzeta = xi_derivative(\n      std::array<std::reference_wrapper<const tt::remove_cvref_wrap_t<T>>, 3>{\n          {std::cref(dereference_wrapper(source_coords[2])),\n           std::cref(dereference_wrapper(source_coords[1])),\n           std::cref(dereference_wrapper(source_coords[0]))}});\n  auto jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  get<0, 0>(jacobian_matrix) = dX_dxi[0];\n  get<0, 1>(jacobian_matrix) = dX_deta[1];\n  get<0, 2>(jacobian_matrix) = dX_dzeta[2];\n  get<1, 0>(jacobian_matrix) = dX_dxi[1];\n  get<1, 1>(jacobian_matrix) = dX_deta[0];\n  get<1, 2>(jacobian_matrix) = dX_dzeta[1];\n  get<2, 0>(jacobian_matrix) = dX_dxi[2];\n  get<2, 1>(jacobian_matrix) = dX_deta[2];\n  get<2, 2>(jacobian_matrix) = dX_dzeta[0];\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nBulgedCube::inv_jacobian(const std::array<T, 3>& source_coords) const {\n  const auto jac = jacobian(source_coords);\n  return determinant_and_inverse(jac).second;\n}\n\nvoid BulgedCube::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | radius_;\n    p | sphericity_;\n    p | use_equiangular_map_;\n    p | is_identity_;\n  }\n}\n\nbool operator==(const BulgedCube& lhs, const BulgedCube& rhs) {\n  return lhs.radius_ == rhs.radius_ and lhs.sphericity_ == rhs.sphericity_ and\n         lhs.use_equiangular_map_ == rhs.use_equiangular_map_ and\n         lhs.is_identity_ == rhs.is_identity_;\n}\n\nbool operator!=(const BulgedCube& lhs, const BulgedCube& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  BulgedCube::operator()(const std::array<DTYPE(data), 3>& source_coords)      \\\n      const;                                                                   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  BulgedCube::jacobian(const std::array<DTYPE(data), 3>& source_coords) const; \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  BulgedCube::inv_jacobian(const std::array<DTYPE(data), 3>& source_coords)    \\\n      const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (double, DataVector,\n                  std::reference_wrapper<const double>,\n                  std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, MAP_AUTODIFF_TYPES)\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/BulgedCube.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Defines the class BulgedCube.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/*!\n *  \\ingroup CoordinateMapsGroup\n *\n *  \\brief Three dimensional map from the cube to a bulged cube.\n *  The cube is shaped such that the surface is compatible\n *  with the inner surface of Wedge<3>.\n *  The shape of the object can be chosen to be cubical,\n *  if the sphericity is set to 0, or to a sphere, if\n *  the sphericity is set to 1. The sphericity can\n *  be set to any number between 0 and 1 for a bulged cube.\n *\n *  \\details The volume map from the cube to a bulged cube is obtained by\n *  interpolating between six surface maps, twelve bounding curves, and\n *  eight corners. The surface map for the upper +z axis is obtained by\n *  interpolating between a cubical surface and a spherical surface. The\n *  two surfaces are chosen such that the latter circumscribes the former.\n *\n *  We make a choice here as to whether we wish to use the logical coordinates\n *  parameterizing these surface as they are, in which case we have the\n *  equidistant choice of coordinates, or whether to apply a tangent map to them\n *  which leads us to the equiangular choice of coordinates. In terms of the\n *  logical coordinates, the equiangular coordinates are:\n *\n *  \\f[\\textrm{equiangular xi} : \\Xi(\\xi) = \\textrm{tan}(\\xi\\pi/4)\\f]\n *\n *  \\f[\\textrm{equiangular eta}  : \\mathrm{H}(\\eta) = \\textrm{tan}(\\eta\\pi/4)\\f]\n *\n *  With derivatives:\n *\n *  \\f[\\Xi'(\\xi) = \\frac{\\pi}{4}(1+\\Xi^2)\\f]\n *\n *  \\f[\\mathrm{H}'(\\eta) = \\frac{\\pi}{4}(1+\\mathrm{H}^2)\\f]\n *\n *  The equidistant coordinates are:\n *\n *  \\f[ \\textrm{equidistant xi}  : \\Xi = \\xi\\f]\n *\n *  \\f[ \\textrm{equidistant eta}  : \\mathrm{H} = \\eta\\f]\n *\n *  with derivatives:\n *\n *  <center>\\f$\\Xi'(\\xi) = 1\\f$, and \\f$\\mathrm{H}'(\\eta) = 1\\f$</center>\n *\n *  We also define the variable \\f$\\rho\\f$, given by:\n *\n *  \\f[\\rho = \\sqrt{1+\\Xi^2+\\mathrm{H}^2}\\f]\n *\n *  ### The Spherical Face Map\n *  The surface map for the spherical face of radius \\f$R\\f$ lying in the\n *  \\f$+z\\f$\n *  direction in either choice of coordinates is then given by:\n *\n *  \\f[\n *  \\vec{\\sigma}_{spherical}(\\xi,\\eta) =\n *  \\begin{bmatrix}\n *  x(\\xi,\\eta)\\\\\n *  y(\\xi,\\eta)\\\\\n *  z(\\xi,\\eta)\\\\\n *  \\end{bmatrix}  = \\frac{R}{\\rho}\n *  \\begin{bmatrix}\n *  \\Xi\\\\\n *  \\mathrm{H}\\\\\n *  1\\\\\n *  \\end{bmatrix}\n *  \\f]\n *\n *  ### The Cubical Face Map\n *  The surface map for the cubical face of side length \\f$2L\\f$ lying in the\n *  \\f$+z\\f$ direction is given by:\n *\n *  \\f[\n *  \\vec{\\sigma}_{cubical}(\\xi,\\eta) =\n *  \\begin{bmatrix}\n *  x(\\xi,\\eta)\\\\\n *  y(\\xi,\\eta)\\\\\n *  L\\\\\n *  \\end{bmatrix}  = L\n *  \\begin{bmatrix}\n *  \\Xi\\\\\n *  \\mathrm{H}\\\\\n *  1\\\\\n *  \\end{bmatrix}\n *  \\f]\n *\n *  ### The Bulged Face Map\n *  To construct the bulged map we interpolate between a cubical face map of\n *  side length \\f$2L\\f$ and a spherical face map of radius \\f$R\\f$, with the\n *  interpolation parameter being \\f$s\\f$, the `sphericity`.\n *  The surface map for the bulged face lying in the \\f$+z\\f$ direction is then\n *  given by:\n *\n *  \\f[\n *  \\vec{\\sigma}_{+\\zeta}(\\xi,\\eta) = \\left\\{(1-s)L + \\frac{sR}{\\rho}\\right\\}\n *  \\begin{bmatrix}\n *  \\Xi\\\\\n *  \\mathrm{H}\\\\\n *  1\\\\\n *  \\end{bmatrix}\n *  \\f]\n *\n *  This equation defines the upper-z map \\f$\\vec{\\sigma}_{+\\zeta}\\f$, and we\n *  similarly define the other five surface maps \\f$\\vec{\\sigma}_{+\\eta}\\f$,\n *  \\f$\\vec{\\sigma}_{+\\xi}\\f$, and so on by appropriate rotations.\n *  We constrain L by demanding that the spherical face circumscribe the cube.\n *  With this condition, we have \\f$L = R/\\sqrt3\\f$.\n *\n *  ### The General Formula for 3D Isoparametric Maps\n *  The general formula is given by Eq. 1 in section 2.1 of Hesthaven's paper\n *  \"A Stable Penalty Method For The Compressible Navier-Stokes Equations III.\n *  Multidimensional Domain Decomposition Schemes\" available\n *  <a href=\"\n *  http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.699.1161&rep=rep1&type=pdf\n *  \"> here </a>.\n *\n *  Hesthaven's formula is general in the degree of the shape functions used,\n *  so for our purposes we take the special case where the shape functions are\n *  linear in the interpolation variable, and define new variables accordingly.\n *  However, our interpolation variables do not necessarily have to be the\n *  logical coordinates themselves, though they often are. To make this\n *  distinction clear, we will define the new interpolation variables\n *  \\f$\\{\\tilde{\\xi},\\tilde{\\eta},\\tilde{\\zeta}\\}\\f$, which may either be the\n *  logical coordinates themselves or a invertible transformation of them. For\n *  the purposes of the bulged cube map, this transformation will be the same\n *  transformation that takes the logical coordinates into the equiangular\n *  coordinates. We will later see how this choice can lead to simplifications\n *  in the final map.\n *\n *  We define the following variables for\n *  \\f$\\alpha, \\beta, \\gamma \\in\\{\\tilde{\\xi},\\tilde{\\eta},\\tilde{\\zeta}\\}\\f$:\n *\n *  \\f[\n *  f^{\\pm}_{\\alpha} = \\frac{1}{2}(1\\pm\\alpha)\\\\\n *  f^{\\pm\\pm}_{\\alpha \\ \\beta} = \\frac{1}{4}(1\\pm\\alpha)(1\\pm\\beta)\\\\\n *  f^{\\pm\\pm\\pm}_{\\alpha \\ \\beta \\ \\gamma} =\n *  \\frac{1}{8}(1\\pm\\alpha)(1\\pm\\beta)(1\\pm\\gamma)\n *  \\f]\n *\n *  The formula involves six surfaces, which we will denote by\n *  \\f$\\vec{\\sigma}\\f$, twelve curves, denoted by \\f$\\vec{\\Gamma}\\f$, and eight\n *  vertices, denoted by \\f$\\vec{\\pi}\\f$, with the subscripts denoting which\n *  face(s) these objects belong to. The full volume map is given by:\n *\n *  \\f{align*}\n *  \\vec{x}(\\xi,\\eta,\\zeta) = &\n *  f^{+}_{\\tilde{\\zeta}}\\vec{\\sigma}_{+\\zeta}(\\xi, \\eta)+\n *  f^{-}_{\\tilde{\\zeta}}\\vec{\\sigma}_{-\\zeta}(\\xi, \\eta)\\\\\n *  &+ f^{+}_{\\tilde{\\eta}}\\vec{\\sigma}_{+\\eta}(\\xi, \\zeta)+\n *  f^{-}_{\\tilde{\\eta}}\\vec{\\sigma}_{-\\eta}(\\xi, \\zeta)+\n *  f^{+}_{\\tilde{\\xi}}\\vec{\\sigma}_{+\\xi}(\\eta, \\zeta)+\n *  f^{-}_{\\tilde{\\xi}}\\vec{\\sigma}_{-\\xi}(\\eta, \\zeta)\\\\\n *  &- f^{++}_{\\tilde{\\xi} \\ \\tilde{\\eta}}\\vec{\\Gamma}_{+\\xi +\\eta}(\\zeta)-\n *  f^{-+}_{\\tilde{\\xi} \\ \\tilde{\\eta}}\\vec{\\Gamma}_{-\\xi +\\eta}(\\zeta)-\n *  f^{+-}_{\\tilde{\\xi} \\ \\tilde{\\eta}}\\vec{\\Gamma}_{+\\xi -\\eta}(\\zeta)-\n *  f^{--}_{\\tilde{\\xi} \\ \\tilde{\\eta}}\\vec{\\Gamma}_{-\\xi -\\eta}(\\zeta)\\\\\n *  &- f^{++}_{\\tilde{\\xi} \\ \\tilde{\\zeta}}\\vec{\\Gamma}_{+\\xi +\\zeta}(\\eta)-\n *  f^{-+}_{\\tilde{\\xi} \\ \\tilde{\\zeta}}\\vec{\\Gamma}_{-\\xi +\\zeta}(\\eta)-\n *  f^{+-}_{\\tilde{\\xi} \\ \\tilde{\\zeta}}\\vec{\\Gamma}_{+\\xi -\\zeta}(\\eta)-\n *  f^{--}_{\\tilde{\\xi} \\ \\tilde{\\zeta}}\\vec{\\Gamma}_{-\\xi -\\zeta}(\\eta)\\\\\n *  &- f^{++}_{\\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\Gamma}_{+\\eta +\\zeta}(\\xi)-\n *  f^{-+}_{\\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\Gamma}_{-\\eta +\\zeta}(\\xi)-\n *  f^{+-}_{\\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\Gamma}_{+\\eta -\\zeta}(\\xi)-\n *  f^{--}_{\\tilde{\\eta} \\tilde{\\zeta}}\\vec{\\Gamma}_{-\\eta -\\zeta}(\\xi)\\\\\n *  &+ f^{+++}_{\\tilde{\\xi} \\ \\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\pi}_{+\\xi +\\eta\n * +\\zeta}+ f^{-++}_{\\tilde{\\xi} \\ \\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\pi}_{-\\xi\n * +\\eta +\\zeta}+ f^{+-+}_{\\tilde{\\xi} \\ \\tilde{\\eta} \\\n * \\tilde{\\zeta}}\\vec{\\pi}_{+\\xi -\\eta +\\zeta}+\n *  f^{--+}_{\\tilde{\\xi} \\ \\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\pi}_{-\\xi -\\eta\n * +\\zeta}\\\\\n *  &+ f^{++-}_{\\tilde{\\xi} \\ \\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\pi}_{+\\xi +\\eta\n * -\\zeta}+ f^{-+-}_{\\tilde{\\xi} \\ \\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\pi}_{-\\xi\n * +\\eta -\\zeta}+ f^{+--}_{\\tilde{\\xi} \\ \\tilde{\\eta} \\\n * \\tilde{\\zeta}}\\vec{\\pi}_{+\\xi -\\eta -\\zeta}+ f^{---}_{\\tilde{\\xi} \\\n * \\tilde{\\eta} \\ \\tilde{\\zeta}}\\vec{\\pi}_{-\\xi -\\eta -\\zeta} \\f}\n *\n *\n *  ### The Special Case for Octahedral Symmetry\n *  The general formula is for the case in which there are six independently\n *  specified bounding surfaces. In our case, the surfaces are obtained by\n *  rotations and reflections of the upper-\\f$\\zeta\\f$ face.\n *\n * We define the matrices corresponding to these transformations to be:\n *\n * \\f[\n * S_{xy} =\n *  \\begin{bmatrix}\n *  0 & 1 & 0\\\\\n *  1 & 0 & 0\\\\\n *  0 & 0 & 1\\\\\n *  \\end{bmatrix},\\\n *\n * S_{xz} =\n *  \\begin{bmatrix}\n *  0 & 0 & 1\\\\\n *  0 & 1 & 0\\\\\n *  1 & 0 & 0\\\\\n *  \\end{bmatrix},\\\n *\n * S_{yz} =\n *  \\begin{bmatrix}\n *  1 & 0 & 0\\\\\n *  0 & 0 & 1\\\\\n *  0 & 1 & 0\\\\\n *  \\end{bmatrix}\\f]\n *\n * \\f[C_{zxy} =\n *  \\begin{bmatrix}\n *  0 & 0 & 1\\\\\n *  1 & 0 & 0\\\\\n *  0 & 1 & 0\\\\\n *  \\end{bmatrix},\\\n *\n * C_{yzx} =\n *  \\begin{bmatrix}\n *  0 & 1 & 0\\\\\n *  0 & 0 & 1\\\\\n *  1 & 0 & 0\\\\\n *  \\end{bmatrix}\\f]\n *\n * \\f[N_{x} =\n *  \\begin{bmatrix}\n *  -1 & 0 & 0\\\\\n *  0 & 1 & 0\\\\\n *  0 & 0 & 1\\\\\n *  \\end{bmatrix},\\\n *\n * N_{y} =\n *  \\begin{bmatrix}\n *  1 & 0 & 0\\\\\n *  0 & -1 & 0\\\\\n *  0 & 0 & 1\\\\\n *  \\end{bmatrix},\\\n *\n * N_{z} =\n *  \\begin{bmatrix}\n *  1 & 0 & 0\\\\\n *  0 & 1 & 0\\\\\n *  0 & 0 & -1\\\\\n *  \\end{bmatrix}\n *  \\f]\n *\n * The surface maps can now all be written in terms of\n * \\f$\\vec{\\sigma}_{+\\zeta}\\f$ and these matrices:\n * <center>\n * \\f$\\vec{\\sigma}_{-\\zeta}(\\xi, \\eta) = N_z\\vec{\\sigma}_{+\\zeta}(\\xi, \\eta)\\\\\n * \\vec{\\sigma}_{+\\eta}(\\xi, \\zeta) = S_{yz}\\vec{\\sigma}_{+\\zeta}(\\xi, \\zeta)\\\\\n * \\vec{\\sigma}_{-\\eta}(\\xi, \\zeta) = N_yS_{yz}\\vec{\\sigma}_{+\\zeta}(\\xi,\n * \\zeta)\\\\\n * \\vec{\\sigma}_{+\\xi}(\\eta, \\zeta) = C_{zxy}\\vec{\\sigma}_{+\\zeta}(\\eta,\n * \\zeta)\\\\\n * \\vec{\\sigma}_{-\\xi}(\\eta, \\zeta) = N_xC_{zyx}\\vec{\\sigma}_{+\\zeta}(\\eta,\n * \\zeta)\\f$\n * </center>\n *\n * The four bounding curves \\f$\\vec{\\Gamma}\\f$ on the \\f$+\\zeta\\f$ face are\n * given by:\n *\n * <center>\n * \\f$\\vec{\\Gamma}_{+\\xi,+\\zeta}(\\eta) = \\vec{\\sigma}_{+\\zeta}(+1,\\eta)\\\\\n * \\vec{\\Gamma}_{-\\xi,+\\zeta}(\\eta) = \\vec{\\sigma}_{+\\zeta}(-1,\\eta)\n * = N_x\\vec{\\sigma}_{+\\zeta}(+1, \\eta)\\\\\n * \\vec{\\Gamma}_{+\\eta,+\\zeta}(\\xi) = \\vec{\\sigma}_{+\\zeta}(\\xi,+1)\n * = S_{xy}\\vec{\\sigma}_{+\\zeta}(+1, \\xi)\\\\\n * \\vec{\\Gamma}_{-\\eta,+\\zeta}(\\xi) = \\vec{\\sigma}_{+\\zeta}(\\xi,-1)\n * = N_yS_{xy}\\vec{\\sigma}_{+\\zeta}(+1,\\xi)\\f$\n * </center>\n *\n * The bounding curves on the other surfaces can be obtained by transformations\n * on the \\f$+\\zeta\\f$ face:\n *\n * <center>\n * \\f$\\vec{\\Gamma}_{+\\xi,-\\zeta}(\\eta) = N_z\\vec{\\sigma}_{+\\zeta}(+1,\\eta)\\\\\n * \\vec{\\Gamma}_{-\\xi,-\\zeta}(\\eta) = N_z\\vec{\\sigma}_{+\\zeta}(-1,\\eta)\n * = N_zN_x\\vec{\\sigma}_{+\\zeta}(+1,\\eta)\\\\\n * \\vec{\\Gamma}_{+\\eta,-\\zeta}(\\xi) = N_z\\vec{\\sigma}_{+\\zeta}(\\xi,+1)\n * = N_zS_{xy}\\vec{\\sigma}_{+\\zeta}(+1, \\xi)\\\\\n * \\vec{\\Gamma}_{-\\eta,-\\zeta}(\\xi) = N_z\\vec{\\sigma}_{+\\zeta}(\\xi,-1)\n * = N_zN_yS_{xy}\\vec{\\sigma}_{+\\zeta}(+1, \\xi)\\\\\n * \\vec{\\Gamma}_{+\\xi,+\\eta}(\\zeta) =\n * C_{zxy}\\vec{\\sigma}_{+\\zeta}(+1,\\zeta)\\\\\n * \\vec{\\Gamma}_{-\\xi,+\\eta}(\\zeta) =\n * N_xC_{zxy}\\vec{\\sigma}_{+\\zeta}(+1,\\zeta)\\\\\n * \\vec{\\Gamma}_{+\\xi,-\\eta}(\\zeta) = C_{zxy}\\vec{\\sigma}_{+\\zeta}(-1,\\zeta)\n * = C_{zxy}N_x\\vec{\\sigma}_{+\\zeta}(+1,\\zeta)\\\\\n * \\vec{\\Gamma}_{-\\xi,-\\eta}(\\zeta) = N_xC_{zxy}\\vec{\\sigma}_{+\\zeta}(-1,\\zeta)\n * = N_xC_{zxy}N_x\\vec{\\sigma}_{+\\zeta}(+1,\\zeta)\\f$\n * </center>\n *\n * Now we can write the volume map in terms of\n * \\f$\\vec{\\sigma}_{+\\zeta}\\f$ only:\n * \\f{align*}\\vec{x}(\\xi,\\eta,\\zeta) = &\n * (f^{+}_{\\tilde{\\zeta}} + f^{-}_{\\tilde{\\zeta}}N_z)\n * \\vec{\\sigma}_{+\\zeta}(\\xi, \\eta)\\\\\n * &+ (f^{+}_{\\tilde{\\eta}} + f^{-}_{\\tilde{\\eta}}N_y)\n * S_{yz}\\vec{\\sigma}_{+\\zeta}(\\xi, \\zeta)\\\\\n * &+ (f^{+}_{\\tilde{\\xi}} + f^{-}_{\\tilde{\\xi}}N_x)\n * C_{zxy}\\vec{\\sigma}_{+\\zeta}(\\eta, \\zeta)\\\\\n * &- (f^{+}_{\\tilde{\\xi}}+f^{-}_{\\tilde{\\xi}}N_x)\n * (f^{+}_{\\tilde{\\eta}}+f^{-}_{\\tilde{\\eta}}N_y)\n * C_{zxy}\\vec{\\sigma}_{+\\zeta}(+1, \\zeta)\\\\\n * &- (f^{+}_{\\tilde{\\zeta}}+f^{-}_{\\tilde{\\zeta}}N_z)\\left\\{\n * (f^{+}_{\\tilde{\\xi}}+f^{-}_{\\tilde{\\xi}}N_x)\\vec{\\sigma}_{+\\zeta}(+1, \\eta)+\n * (f^{+}_{\\tilde{\\eta}}+f^{-}_{\\tilde{\\eta}}N_y)S_{xy}\\vec{\\sigma}_{+\\zeta}(+1,\n * \\xi)\\right\\}\\\\\n * &+ \\frac{r}{\\sqrt{3}}\\vec{\\tilde{\\xi}}\n *  \\f}\n *\n * Note that we can now absorb all of the \\f$f\\f$s into the matrix prefactors\n * in the above equation and obtain a final set of matrices. We define the\n * following *blending matrices*:\n *\n *  \\f[\n *  B_{\\tilde{\\xi}} =\n *  \\begin{bmatrix}\n *  0 & 0 & \\tilde{\\xi}\\\\\n *  1 & 0 & 0\\\\\n *  0 & 1 & 0\\\\\n *  \\end{bmatrix},\\\n *\n *  B_{\\tilde{\\eta}} =\n *  \\begin{bmatrix}\n *  1 & 0 & 0\\\\\n *  0 & 0 & \\tilde{\\eta}\\\\\n *  0 & 1 & 0\\\\\n *  \\end{bmatrix},\\\n *\n *  B_{\\tilde{\\zeta}} =\n *  \\begin{bmatrix}\n *  1 & 0 & 0\\\\\n *  0 & 1 & 0\\\\\n *  0 & 0 & \\tilde{\\zeta}\\\\\n *  \\end{bmatrix}\\\\\n *\n *  B_{\\tilde{\\xi}\\tilde{\\eta}} =\n *  \\begin{bmatrix}\n *  0 & 0 & \\tilde{\\xi}\\\\\n *  \\tilde{\\eta} & 0 & 0\\\\\n *  0 & 1 & 0\\\\\n *  \\end{bmatrix},\\\n *\n *  B_{\\tilde{\\xi}\\tilde{\\zeta}} =\n *  \\begin{bmatrix}\n *  \\tilde{\\xi} & 0 & 0\\\\\n *  0 & 1 & 0\\\\\n *  0 & 0 & \\tilde{\\zeta}\\\\\n *  \\end{bmatrix},\\\n *\n *  B_{\\tilde{\\eta}\\tilde{\\zeta}} =\n *  \\begin{bmatrix}\n *  0 & 1 & 0\\\\\n *  \\tilde{\\eta} & 0 & 0\\\\\n *  0 & 0 & \\tilde{\\zeta}\\\\\n *  \\end{bmatrix}\\\\\n *\n *  B_{\\tilde{\\xi}\\tilde{\\eta}\\tilde{\\zeta}} =\n *  \\begin{bmatrix}\n *  \\tilde{\\xi} & 0 & 0\\\\\n *  0 & \\tilde{\\eta} & 0\\\\\n *  0 & 0 & \\tilde{\\zeta}\\\\\n *  \\end{bmatrix}\n *  \\f]\n *\n *  Now we can write the volume map in these terms:\n *\n * \\f{align*}\n * \\vec{x}(\\xi,\\eta,\\zeta) = &\n * B_{\\tilde{\\zeta}}\n * \\vec{\\sigma}_{+\\zeta}(\\xi, \\eta)\\\\& +\n * B_{\\tilde{\\eta}}\n * \\vec{\\sigma}_{+\\zeta}(\\xi, \\zeta)+\n * B_{\\tilde{\\xi}}\n * \\vec{\\sigma}_{+\\zeta}(\\eta, \\zeta)\\\\& -\n * B_{\\tilde{\\xi} \\tilde{\\eta}}\n * \\vec{\\sigma}_{+\\zeta}(+1, \\zeta)-\n * B_{\\tilde{\\xi} \\tilde{\\zeta}}\n * \\vec{\\sigma}_{+\\zeta}(+1, \\eta)+\n * B_{\\tilde{\\eta} \\tilde{\\zeta}}\n * \\vec{\\sigma}_{+\\zeta}(+1, \\xi)\\\\& +\n * B_{\\tilde{\\xi} \\tilde{\\eta} \\tilde{\\zeta}}\n * \\vec{\\sigma}_{+\\zeta}(+1, +1)\n * \\f}\n *\n * ### The Bulged Cube Map\n * We now use the result above to provide the mapping for the bulged cube.\n * First we will define the variables \\f$\\rho_A\\f$ and \\f$\\rho_{AB}\\f$, for\n * \\f$A, B \\in \\{\\Xi,\\mathrm{H}, \\mathrm{Z}\\} \\f$, where \\f$\\mathrm{Z}\\f$\n * is \\f$\\tan(\\zeta\\pi/4)\\f$ in the equiangular case and \\f$\\zeta\\f$ in the\n * equidistant case:\n *\n * \\f[\n * \\rho_A = \\sqrt{2 + A^2}\\\\\n * \\rho_{AB} = \\sqrt{1 + A^2 + B^2}\n * \\f]\n * The final mapping is then:\n * \\f[\n * \\vec{x}(\\xi,\\eta,\\zeta) = \\frac{(1-s)R}{\\sqrt{3}}\n * \\begin{bmatrix}\n * \\Xi\\\\\n * \\mathrm{H}\\\\\n * \\mathrm{Z}\\\\\n * \\end{bmatrix} +\n * \\frac{sR}{\\sqrt{3}}\n * \\begin{bmatrix}\n * \\tilde{\\xi}\\\\\n * \\tilde{\\eta}\\\\\n * \\tilde{\\zeta}\\\\\n * \\end{bmatrix} + sR\n * \\begin{bmatrix}\n * \\tilde{\\xi} & \\Xi & \\Xi\\\\\n * \\mathrm{H} & \\tilde{\\eta} &\\mathrm{H}\\\\\n * \\mathrm{Z} & \\mathrm{Z} & \\tilde{\\zeta}\\\\\n * \\end{bmatrix}\n * \\begin{bmatrix}\n * 1/\\rho_{\\mathrm{H}\\mathrm{Z}}\\\\\n * 1/\\rho_{\\Xi\\mathrm{Z}}\\\\\n * 1/\\rho_{\\Xi\\mathrm{H}}\\\\\n * \\end{bmatrix} - sR\n * \\begin{bmatrix}\n * \\Xi & \\tilde{\\xi} & \\tilde{\\xi}\\\\\n * \\tilde{\\eta} & \\mathrm{H} &\\tilde{\\eta}\\\\\n * \\tilde{\\zeta} & \\tilde{\\zeta} & \\mathrm{Z}\\\\\n * \\end{bmatrix}\n * \\begin{bmatrix}\n * 1/\\rho_{\\Xi}\\\\\n * 1/\\rho_{\\mathrm{H}}\\\\\n * 1/\\rho_{\\mathrm{Z}}\\\\\n * \\end{bmatrix}\n * \\f]\n *\n * Recall that the lower case Greek letters with tildes are the variables\n * used for the linear interpolation between the six bounding surfaces, and\n * that the upper case Greek letters are the coordinates along these surfaces -\n * both of which can be specified to be either\n * equidistant or equiangular. In the case where the\n * interpolation variable is chosen to match that of the\n * coordinates along the surface, we have \\f$\\tilde{\\xi} = \\Xi\\f$, etc. In this\n * case, the formula reduces further. The reduced formula below is the one used\n * for this CoordinateMap. It is given by:\n *\n * \\f[\n * \\vec{x}(\\xi,\\eta,\\zeta) =\n * \\left\\{\n * \\frac{R}{\\sqrt{3}}\n * + sR\n * \\left(\n * 1/\\rho_{\\mathrm{H}\\mathrm{Z}}+\n * 1/\\rho_{\\Xi\\mathrm{Z}}+\n * 1/\\rho_{\\Xi\\mathrm{H}}-\n * 1/\\rho_{\\Xi}-\n * 1/\\rho_{\\mathrm{H}}-\n * 1/\\rho_{\\mathrm{Z}}\n * \\right)\n * \\right\\}\n * \\begin{bmatrix}\n * \\Xi\\\\\n * \\mathrm{H}\\\\\n * \\mathrm{Z}\\\\\n * \\end{bmatrix}\n * \\f]\n *\n * The inverse mapping is analytic in the angular directions. A root find\n * must be performed for the inverse mapping in the radial direction. This\n * one-dimensional formula is obtained by taking the magnitude of both sides\n * of the mapping, and changing variables from \\f$\\xi, \\eta, \\zeta\\f$ to\n * \\f$x, y, z\\f$ and introducing \\f$\\rho^2 := \\sqrt{\\xi^2+\\eta^2+\\zeta^2}\\f$.\n */\nclass BulgedCube {\n public:\n  static constexpr size_t dim = 3;\n  BulgedCube(double radius, double sphericity, bool use_equiangular_map);\n  BulgedCube() = default;\n  ~BulgedCube() = default;\n  BulgedCube(BulgedCube&&) = default;\n  BulgedCube(const BulgedCube&) = default;\n  BulgedCube& operator=(const BulgedCube&) = default;\n  BulgedCube& operator=(BulgedCube&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> xi_derivative(\n      const std::array<T, 3>& source_coords) const;\n  friend bool operator==(const BulgedCube& lhs, const BulgedCube& rhs);\n\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  double sphericity_{std::numeric_limits<double>::signaling_NaN()};\n  bool use_equiangular_map_ = false;\n  bool is_identity_ = false;\n};\n\nbool operator!=(const BulgedCube& lhs, const BulgedCube& rhs);\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY CoordinateMaps)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Affine.cpp\n  BulgedCube.cpp\n  Composition.cpp\n  CylindricalEndcap.cpp\n  CylindricalEndcapHelpers.cpp\n  CylindricalFlatEndcap.cpp\n  CylindricalFlatSide.cpp\n  CylindricalSide.cpp\n  DiscreteRotation.cpp\n  Distribution.cpp\n  EquatorialCompression.cpp\n  Equiangular.cpp\n  FlatOffsetSphericalWedge.cpp\n  FlatOffsetWedge.cpp\n  FocallyLiftedEndcap.cpp\n  FocallyLiftedFlatEndcap.cpp\n  FocallyLiftedFlatSide.cpp\n  FocallyLiftedMap.cpp\n  FocallyLiftedMapHelpers.cpp\n  FocallyLiftedSide.cpp\n  Frustum.cpp\n  Identity.cpp\n  Interval.cpp\n  KerrHorizonConforming.cpp\n  PolarToCartesian.cpp\n  Rotation.cpp\n  SpecialMobius.cpp\n  SphericalToCartesianPfaffian.cpp\n  UniformCylindricalEndcap.cpp\n  UniformCylindricalFlatEndcap.cpp\n  UniformCylindricalSide.cpp\n  Wedge.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Affine.hpp\n  AutodiffInstantiationTypes.hpp\n  BulgedCube.hpp\n  Composition.hpp\n  CoordinateMap.hpp\n  CoordinateMap.tpp\n  CoordinateMapHelpers.hpp\n  CylindricalEndcap.hpp\n  CylindricalEndcapHelpers.hpp\n  CylindricalFlatEndcap.hpp\n  CylindricalFlatSide.hpp\n  CylindricalSide.hpp\n  DiscreteRotation.hpp\n  Distribution.hpp\n  EquatorialCompression.hpp\n  Equiangular.hpp\n  FlatOffsetSphericalWedge.hpp\n  FlatOffsetWedge.hpp\n  FocallyLiftedEndcap.hpp\n  FocallyLiftedFlatEndcap.hpp\n  FocallyLiftedFlatSide.hpp\n  FocallyLiftedMap.hpp\n  FocallyLiftedMapHelpers.hpp\n  FocallyLiftedSide.hpp\n  Frustum.hpp\n  Identity.hpp\n  Interval.hpp\n  KerrHorizonConforming.hpp\n  MapInstantiationMacros.hpp\n  PolarToCartesian.hpp\n  ProductMaps.hpp\n  ProductMaps.tpp\n  Rotation.hpp\n  SpecialMobius.hpp\n  SphericalToCartesianPfaffian.hpp\n  Tags.hpp\n  TimeDependentHelpers.hpp\n  UniformCylindricalEndcap.hpp\n  UniformCylindricalFlatEndcap.hpp\n  UniformCylindricalSide.hpp\n  Wedge.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  RootFinding\n  PUBLIC\n  Boost::boost\n  DataStructures\n  DomainStructure\n  ErrorHandling\n  FunctionsOfTime\n  GSL::gsl\n  Logging\n  MathFunctions\n  Serialization\n  SphericalHarmonics\n  )\n\nadd_subdirectory(Python)\nadd_subdirectory(TimeDependent)\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Composition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n\n#include <tuple>\n#include <type_traits>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nnamespace Composition_detail {\n// Helper function to generate error message for unsupported autodiff maps\ntemplate <typename Frames, size_t Dim, size_t... Is>\nstd::string get_unsupported_autodiff_maps_error(\n    const std::tuple<std::unique_ptr<\n        CoordinateMapBase<tmpl::at<Frames, tmpl::size_t<Is>>,\n                          tmpl::at<Frames, tmpl::size_t<Is + 1>>, Dim>>...>&\n        maps) {\n  std::string unsupported_maps;\n  const auto check_map = [&unsupported_maps, &maps](const auto index_v) {\n    constexpr size_t index = decltype(index_v)::value;\n    if (not get<index>(maps)->supports_hessian()) {\n      if (not unsupported_maps.empty()) {\n        unsupported_maps += \", \";\n      }\n      unsupported_maps +=\n          \"Map \" + std::to_string(index) + \" (\" +\n          pretty_type::get_runtime_type_name(*get<index>(maps)) + \")\";\n    }\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(check_map(tmpl::size_t<Is>{}));\n  return unsupported_maps;\n}\n}  // namespace Composition_detail\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nComposition<Frames, Dim, std::index_sequence<Is...>>::Composition(\n    std::unique_ptr<\n        CoordinateMapBase<tmpl::at<frames, tmpl::size_t<Is>>,\n                          tmpl::at<frames, tmpl::size_t<Is + 1>>, Dim>>... maps)\n    : maps_{std::move(maps)...},\n      function_of_time_names_(CoordinateMap_detail::initialize_names(\n          maps_, std::index_sequence<Is...>{})) {}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nComposition<Frames, Dim, std::index_sequence<Is...>>&\nComposition<Frames, Dim, std::index_sequence<Is...>>::operator=(\n    const Composition& rhs) {\n  expand_pack((get<Is>(maps_) = get<Is>(rhs.maps_)->get_clone())...);\n  return *this;\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nbool Composition<Frames, Dim, std::index_sequence<Is...>>::is_identity() const {\n  bool result = true;\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      (result = result and get<Is>(maps_)->is_identity()));\n  return result;\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nbool Composition<Frames, Dim, std::index_sequence<Is...>>::supports_hessian()\n    const {\n  return (... and get<Is>(maps_)->supports_hessian());\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nbool Composition<Frames, Dim,\n                 std::index_sequence<Is...>>::inv_jacobian_is_time_dependent()\n    const {\n  bool result = false;\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      (result = result or get<Is>(maps_)->inv_jacobian_is_time_dependent()));\n  return result;\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nbool Composition<Frames, Dim,\n                 std::index_sequence<Is...>>::jacobian_is_time_dependent()\n    const {\n  bool result = false;\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      (result = result or get<Is>(maps_)->jacobian_is_time_dependent()));\n  return result;\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\ntnsr::I<double, Dim, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::operator()(\n    tnsr::I<double, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  return call_impl(std::move(source_point), time, functions_of_time);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\ntnsr::I<DataVector, Dim, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::operator()(\n    tnsr::I<DataVector, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  return call_impl(std::move(source_point), time, functions_of_time);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nstd::optional<tnsr::I<double, Dim, tmpl::front<Frames>>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inverse(\n    tnsr::I<double, Dim, tmpl::back<Frames>> target_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  return inverse_impl(std::move(target_point), time, functions_of_time);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nInverseJacobian<double, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inv_jacobian(\n    tnsr::I<double, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  return inv_jacobian_impl(std::move(source_point), time, functions_of_time);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nInverseJacobian<DataVector, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inv_jacobian(\n    tnsr::I<DataVector, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  return inv_jacobian_impl(std::move(source_point), time, functions_of_time);\n}\n\n#ifdef SPECTRE_AUTODIFF\ntemplate <typename Frames, size_t Dim, size_t... Is>\nInverseJacobian<autodiff::HigherOrderDual<2, double>, Dim, tmpl::front<Frames>,\n                tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inv_jacobian(\n    tnsr::I<autodiff::HigherOrderDual<2, double>, Dim, SourceFrame>\n        source_point,\n    const double time, const FuncOfTimeMap& functions_of_time) const {\n  if (supports_hessian()) {\n    return inv_jacobian_impl(std::move(source_point), time, functions_of_time);\n  } else {\n    ERROR(\"At least one of the Maps does not support autodiff: \"\n          << (Composition_detail::get_unsupported_autodiff_maps_error<\n                 Frames, Dim, Is...>(maps_)));\n  }\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nInverseHessian<double, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inv_hessian(\n    tnsr::I<double, Dim, SourceFrame> source_point,\n    const InverseJacobian<double, Dim, SourceFrame, TargetFrame>& inverse_jac,\n    const double time, const FuncOfTimeMap& functions_of_time) const {\n  if (supports_hessian()) {\n    return inv_hessian_impl(std::move(source_point), inverse_jac, time,\n                            functions_of_time);\n  } else {\n    ERROR(\"At least one of the Maps does not support autodiff: \"\n          << (Composition_detail::get_unsupported_autodiff_maps_error<\n                 Frames, Dim, Is...>(maps_)));\n  }\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nInverseHessian<DataVector, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inv_hessian(\n    tnsr::I<DataVector, Dim, SourceFrame> source_point,\n    const InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&\n        inverse_jac,\n    const double time, const FuncOfTimeMap& functions_of_time) const {\n  if (supports_hessian()) {\n    return inv_hessian_impl(std::move(source_point), inverse_jac, time,\n                            functions_of_time);\n  } else {\n    ERROR(\"At least one of the Maps does not support autodiff: \"\n          << (Composition_detail::get_unsupported_autodiff_maps_error<\n                 Frames, Dim, Is...>(maps_)));\n  }\n}\n#endif  // SPECTRE_AUTODIFF\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nJacobian<double, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::jacobian(\n    tnsr::I<double, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  return jacobian_impl(std::move(source_point), time, functions_of_time);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nJacobian<DataVector, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::jacobian(\n    tnsr::I<DataVector, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  return jacobian_impl(std::move(source_point), time, functions_of_time);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\n[[noreturn]] std::tuple<\n    tnsr::I<double, Dim, tmpl::back<Frames>>,\n    InverseJacobian<double, Dim, tmpl::front<Frames>, tmpl::back<Frames>>,\n    Jacobian<double, Dim, tmpl::front<Frames>, tmpl::back<Frames>>,\n    tnsr::I<double, Dim, tmpl::back<Frames>>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::\n    coords_frame_velocity_jacobians(\n        tnsr::I<double, Dim, SourceFrame> /*source_point*/,\n        const double /*time*/,\n        const FuncOfTimeMap& /*functions_of_time*/) const {\n  ERROR(\n      \"coords_frame_velocity_jacobians is not yet implemented in \"\n      \"'Composition'. Please implement this function if you need it.\");\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\n[[noreturn]] std::tuple<\n    tnsr::I<DataVector, Dim, tmpl::back<Frames>>,\n    InverseJacobian<DataVector, Dim, tmpl::front<Frames>, tmpl::back<Frames>>,\n    Jacobian<DataVector, Dim, tmpl::front<Frames>, tmpl::back<Frames>>,\n    tnsr::I<DataVector, Dim, tmpl::back<Frames>>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::\n    coords_frame_velocity_jacobians(\n        tnsr::I<DataVector, Dim, SourceFrame> /*source_point*/,\n        const double /*time*/,\n        const FuncOfTimeMap& /*functions_of_time*/) const {\n  ERROR(\n      \"coords_frame_velocity_jacobians is not yet implemented in \"\n      \"'Composition'. Please implement this function if you need it.\");\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\n[[noreturn]] std::unique_ptr<\n    CoordinateMapBase<tmpl::front<Frames>, Frame::Grid, Dim>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::get_to_grid_frame()\n    const {\n  // We probably don't need this.\n  ERROR(\n      \"get_to_grid_frame is not implemented in 'Composition'. \"\n      \"Just create a composition to the grid frame, or implement this \"\n      \"function if you need it.\");\n}\n\n// NOLINTNEXTLINE(google-runtime-references)\ntemplate <typename Frames, size_t Dim, size_t... Is>\nvoid Composition<Frames, Dim, std::index_sequence<Is...>>::pup(PUP::er& p) {\n  Base::pup(p);\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | maps_;\n  }\n\n  // No need to pup this because it is uniquely determined by the maps\n  if (p.isUnpacking()) {\n    function_of_time_names_ = CoordinateMap_detail::initialize_names(\n        maps_, std::index_sequence<Is...>{});\n  }\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\ntemplate <typename DataType>\ntnsr::I<DataType, Dim, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::call_impl(\n    tnsr::I<DataType, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  std::tuple<tnsr::I<DataType, Dim, SourceFrame>,\n             tnsr::I<DataType, Dim, tmpl::at<frames, tmpl::size_t<Is + 1>>>...>\n      points{};\n  get<0>(points) = std::move(source_point);\n  const auto apply = [&points, &time, &functions_of_time,\n                      this](const auto index_v) {\n    constexpr size_t index = decltype(index_v)::value;\n    const auto& map = *get<index>(maps_);\n    if (UNLIKELY(map.is_identity())) {\n      for (size_t d = 0; d < Dim; ++d) {\n        get<index + 1>(points).get(d) = std::move(get<index>(points).get(d));\n      }\n    } else {\n      get<index + 1>(points) =\n          map(std::move(get<index>(points)), time, functions_of_time);\n    }\n    return '0';\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(apply(tmpl::size_t<Is>{}));\n  return get<tnsr::I<DataType, Dim, TargetFrame>>(points);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\ntemplate <typename DataType>\nstd::optional<tnsr::I<DataType, Dim, tmpl::front<Frames>>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inverse_impl(\n    tnsr::I<DataType, Dim, TargetFrame> target_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  std::tuple<std::optional<tnsr::I<DataType, Dim, SourceFrame>>,\n             std::optional<tnsr::I<DataType, Dim,\n                                   tmpl::at<frames, tmpl::size_t<Is + 1>>>>...>\n      points{};\n  get<num_frames - 1>(points) = std::move(target_point);\n  const auto apply_inverse = [&points, &time, &functions_of_time,\n                              this](const auto index_v) {\n    constexpr size_t index = decltype(index_v)::value;\n    // index runs from 0 to num_frames - 2. We evaluate maps in reverse order.\n    auto& local_target_point = get<num_frames - index - 1>(points);\n    if (local_target_point.has_value()) {\n      auto& local_source_point = get<num_frames - index - 2>(points);\n      const auto& map = *get<num_frames - index - 2>(maps_);\n      if (UNLIKELY(map.is_identity())) {\n        local_source_point =\n            tnsr::I<DataType, Dim,\n                    tmpl::at<frames, tmpl::size_t<num_frames - index - 2>>>{};\n        for (size_t d = 0; d < Dim; ++d) {\n          local_source_point->get(d) = std::move(local_target_point->get(d));\n        }\n      } else {\n        local_source_point = map.inverse(std::move(local_target_point.value()),\n                                         time, functions_of_time);\n      }\n    }\n    return '0';\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(apply_inverse(tmpl::size_t<Is>{}));\n  return get<0>(points);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\ntemplate <typename DataType>\nInverseJacobian<DataType, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inv_jacobian_impl(\n    tnsr::I<DataType, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  std::tuple<tnsr::I<DataType, Dim, tmpl::at<frames, tmpl::size_t<Is>>>...>\n      source_points{};\n  std::tuple<InverseJacobian<DataType, Dim, SourceFrame,\n                             tmpl::at<frames, tmpl::size_t<Is + 1>>>...>\n      inv_jacobians{};\n  get<0>(source_points) = std::move(source_point);\n  const auto apply_inv_jacobian = [&source_points, &inv_jacobians, &time,\n                                   &functions_of_time,\n                                   this](const auto index_v) {\n    constexpr size_t index = decltype(index_v)::value;\n    const auto& map = *get<index>(maps_);\n    auto& local_source_point = get<index>(source_points);\n    if constexpr (index == 0) {\n      get<0>(inv_jacobians) =\n          map.inv_jacobian(local_source_point, time, functions_of_time);\n    } else {\n      auto& prev_inv_jacobian = get<index - 1>(inv_jacobians);\n      if (UNLIKELY(map.is_identity())) {\n        for (size_t i = 0; i < Dim; ++i) {\n          for (size_t j = 0; j < Dim; ++j) {\n            get<index>(inv_jacobians).get(i, j) =\n                std::move(prev_inv_jacobian.get(i, j));\n          }\n        }\n      } else {\n        // Compose inverse Jacobians\n        const auto next_inv_jacobian =\n            map.inv_jacobian(local_source_point, time, functions_of_time);\n        get<index>(inv_jacobians) = tenex::evaluate<ti::I, ti::j>(\n            prev_inv_jacobian(ti::I, ti::k) * next_inv_jacobian(ti::K, ti::j));\n      }\n    }\n    // Map next source point\n    if constexpr (index < num_frames - 2) {\n      if (UNLIKELY(map.is_identity())) {\n        for (size_t d = 0; d < Dim; ++d) {\n          get<index + 1>(source_points).get(d) =\n              std::move(local_source_point.get(d));\n        }\n      } else {\n        get<index + 1>(source_points) =\n            map(std::move(local_source_point), time, functions_of_time);\n      }\n    }\n    return '0';\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(apply_inv_jacobian(tmpl::size_t<Is>{}));\n  return get<InverseJacobian<DataType, Dim, SourceFrame, TargetFrame>>(\n      inv_jacobians);\n}\n\n#ifdef SPECTRE_AUTODIFF\ntemplate <typename Frames, size_t Dim, size_t... Is>\ntemplate <typename DataType>\nInverseHessian<DataType, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::inv_hessian_impl(\n    tnsr::I<DataType, Dim, SourceFrame> source_point,\n    const ::InverseJacobian<DataType, Dim, SourceFrame, TargetFrame>&\n        inverse_jac,\n    const double time, const FuncOfTimeMap& functions_of_time) const {\n  // first compute hessian\n  using BatchType = simd::batch<double>;\n  using SecondOrderDual = autodiff::HigherOrderDual<2, BatchType>;\n  using SecondOrderDualNum = autodiff::HigherOrderDual<2, double>;\n\n  // NOLINTNEXTLINE(misc-const-correctness)\n  size_t num_pts = 1;\n  // NOLINTNEXTLINE(misc-const-correctness)\n  size_t vec_end = 0;\n  if constexpr (std::is_same_v<DataType, DataVector>) {\n    num_pts = get<0>(source_point).size();\n    vec_end = (num_pts / BatchType::size) * BatchType::size;\n  }\n  ::Hessian<DataType, Dim, SourceFrame, TargetFrame> hessian{num_pts};\n  ::InverseHessian<DataType, Dim, SourceFrame, TargetFrame> inverse_hessian{\n      num_pts};\n\n  // manual vectorization with xsimd\n  if constexpr (std::is_same_v<DataType, DataVector>) {\n    for (size_t pts_index = 0; pts_index < vec_end;\n         pts_index += BatchType::size) {\n      for (size_t i = 0; i < Dim; ++i) {\n        for (size_t j = i; j < Dim; ++j) {\n          tnsr::I<SecondOrderDual, Dim, SourceFrame> dual_source_coords;\n\n          [&]<std::size_t... Ins>(std::index_sequence<Ins...>) {\n            ((get<Ins>(dual_source_coords) = BatchType::load_unaligned(\n                  &(get<Ins>(source_point))[pts_index])),\n             ...);\n          }\n          (std::make_index_sequence<Dim>{});\n\n          autodiff::seed<1>(dual_source_coords.get(i), 1.0);\n          autodiff::seed<2>(dual_source_coords.get(j), 1.0);\n\n          const auto dual_target_coords =\n              call_impl(std::move(dual_source_coords), time, functions_of_time);\n          for (size_t k = 0; k < Dim; ++k) {\n            const auto deriv_kij =\n                autodiff::derivative<2>(dual_target_coords.get(k));\n            deriv_kij.store_unaligned(&hessian.get(k, i, j)[pts_index]);\n          }\n        }\n      }\n    }\n  }\n  // dealing with the tail\n  for (size_t pts_index = vec_end; pts_index < num_pts; ++pts_index) {\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = i; j < Dim; ++j) {\n        tnsr::I<SecondOrderDualNum, Dim, SourceFrame> dual_source_coords;\n\n        if constexpr (std::is_same_v<DataType, double>) {\n          [&]<std::size_t... Ins>(std::index_sequence<Ins...>) {\n            ((get<Ins>(dual_source_coords) = get<Ins>(source_point)), ...);\n          }\n          (std::make_index_sequence<Dim>{});\n        } else {\n          [&]<std::size_t... Ins>(std::index_sequence<Ins...>) {\n            ((get<Ins>(dual_source_coords) =\n                  gsl::at(get<Ins>(source_point), pts_index)),\n             ...);\n          }\n          (std::make_index_sequence<Dim>{});\n        }\n\n        autodiff::seed<1>(dual_source_coords.get(i), 1.0);\n        autodiff::seed<2>(dual_source_coords.get(j), 1.0);\n\n        const auto dual_target_coords =\n            call_impl(std::move(dual_source_coords), time, functions_of_time);\n        for (size_t k = 0; k < Dim; ++k) {\n          if constexpr (std::is_same_v<DataType, double>) {\n            hessian.get(k, i, j) =\n                autodiff::derivative<2>(dual_target_coords.get(k));\n          } else {\n            hessian.get(k, i, j)[pts_index] =\n                autodiff::derivative<2>(dual_target_coords.get(k));\n          }\n        }\n      }\n    }\n  }\n\n  // piece together the inverse hessian from hessian and inverse jacobian\n  ::tenex::evaluate<ti::I, ti::m, ti::n>(\n      make_not_null(&inverse_hessian),\n      -1.0 * inverse_jac(ti::I, ti::j) * inverse_jac(ti::K, ti::m) *\n          inverse_jac(ti::L, ti::n) * hessian(ti::J, ti::k, ti::l));\n\n  return inverse_hessian;\n}\n#endif  // SPECTRE_AUTODIFF\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\ntemplate <typename DataType>\nJacobian<DataType, Dim, tmpl::front<Frames>, tmpl::back<Frames>>\nComposition<Frames, Dim, std::index_sequence<Is...>>::jacobian_impl(\n    tnsr::I<DataType, Dim, SourceFrame> source_point, const double time,\n    const FuncOfTimeMap& functions_of_time) const {\n  std::tuple<tnsr::I<DataType, Dim, tmpl::at<frames, tmpl::size_t<Is>>>...>\n      source_points{};\n  std::tuple<Jacobian<DataType, Dim, SourceFrame,\n                      tmpl::at<frames, tmpl::size_t<Is + 1>>>...>\n      jacobians{};\n  get<0>(source_points) = std::move(source_point);\n  const auto apply_jacobian = [&source_points, &jacobians, &time,\n                               &functions_of_time, this](const auto index_v) {\n    constexpr size_t index = decltype(index_v)::value;\n    const auto& map = *get<index>(maps_);\n    auto& local_source_point = get<index>(source_points);\n    if constexpr (index == 0) {\n      get<0>(jacobians) =\n          map.jacobian(local_source_point, time, functions_of_time);\n    } else {\n      auto& prev_jacobian = get<index - 1>(jacobians);\n      if (UNLIKELY(map.is_identity())) {\n        for (size_t i = 0; i < Dim; ++i) {\n          for (size_t j = 0; j < Dim; ++j) {\n            get<index>(jacobians).get(i, j) =\n                std::move(prev_jacobian.get(i, j));\n          }\n        }\n      } else {\n        // Compose Jacobians\n        const auto next_jacobian =\n            map.jacobian(local_source_point, time, functions_of_time);\n        get<index>(jacobians) = tenex::evaluate<ti::I, ti::j>(\n            next_jacobian(ti::I, ti::k) * prev_jacobian(ti::K, ti::j));\n      }\n    }\n    // Map next source point\n    if constexpr (index < num_frames - 2) {\n      if (UNLIKELY(map.is_identity())) {\n        for (size_t d = 0; d < Dim; ++d) {\n          get<index + 1>(source_points).get(d) =\n              std::move(local_source_point.get(d));\n        }\n      } else {\n        get<index + 1>(source_points) =\n            map(std::move(local_source_point), time, functions_of_time);\n      }\n    }\n    return '0';\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(apply_jacobian(tmpl::size_t<Is>{}));\n  return get<Jacobian<DataType, Dim, SourceFrame, TargetFrame>>(jacobians);\n}\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nbool Composition<Frames, Dim, std::index_sequence<Is...>>::is_equal_to(\n    const CoordinateMapBase<SourceFrame, TargetFrame, Dim>& other) const {\n  const auto& cast_of_other = dynamic_cast<const Composition&>(other);\n\n  return (... and (*get<Is>(maps_) == *get<Is>(cast_of_other.maps_)));\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template class Composition<                                                  \\\n      tmpl::list<Frame::BlockLogical, Frame::Grid, Frame::Inertial>,           \\\n      DIM(data)>;                                                              \\\n  template class Composition<                                                  \\\n      tmpl::list<Frame::BlockLogical, Frame::Grid, Frame::Distorted>,          \\\n      DIM(data)>;                                                              \\\n  template class Composition<tmpl::list<Frame::BlockLogical, Frame::Grid,      \\\n                                        Frame::Distorted, Frame::Inertial>,    \\\n                             DIM(data)>;                                       \\\n  template class Composition<                                                  \\\n      tmpl::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Inertial>, \\\n      DIM(data)>;                                                              \\\n  template class Composition<                                                  \\\n      tmpl::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid>,     \\\n      DIM(data)>;                                                              \\\n  template class Composition<                                                  \\\n      tmpl::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid,      \\\n                 Frame::Inertial>,                                             \\\n      DIM(data)>;                                                              \\\n  template class Composition<                                                  \\\n      tmpl::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid,      \\\n                 Frame::Distorted>,                                            \\\n      DIM(data)>;                                                              \\\n  template class Composition<                                                  \\\n      tmpl::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid,      \\\n                 Frame::Distorted, Frame::Inertial>,                           \\\n      DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n\n#if defined(__clang__) && __clang_major__ >= 15 && __clang_major__ < 17\n#define INSTANTIATE2(_, data)                                                  \\\n  template domain::CoordinateMaps::Composition<                                \\\n      brigand::list<Frame::BlockLogical, Frame::Grid, Frame::Distorted>,       \\\n      DIM(data), std::integer_sequence<unsigned long, 0ul, 1ul>>::             \\\n      Composition(                                                             \\\n          std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,       \\\n                                                    Frame::Grid, DIM(data)>,   \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::BlockLogical, Frame::Grid, DIM(data)>>>,  \\\n          std::unique_ptr<domain::CoordinateMapBase<                           \\\n                              Frame::Grid, Frame::Distorted, DIM(data)>,       \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::Grid, Frame::Distorted, DIM(data)>>>);    \\\n  template domain::CoordinateMaps::Composition<                                \\\n      brigand::list<Frame::BlockLogical, Frame::Grid, Frame::Inertial>,        \\\n      DIM(data), std::integer_sequence<unsigned long, 0ul, 1ul>>::             \\\n      Composition(                                                             \\\n          std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,       \\\n                                                    Frame::Grid, DIM(data)>,   \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::BlockLogical, Frame::Grid, DIM(data)>>>,  \\\n          std::unique_ptr<domain::CoordinateMapBase<                           \\\n                              Frame::Grid, Frame::Inertial, DIM(data)>,        \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::Grid, Frame::Inertial, DIM(data)>>>);     \\\n  template domain::CoordinateMaps::Composition<                                \\\n      brigand::list<Frame::BlockLogical, Frame::Grid, Frame::Distorted,        \\\n                    Frame::Inertial>,                                          \\\n      DIM(data), std::integer_sequence<unsigned long, 0ul, 1ul, 2ul>>::        \\\n      Composition(                                                             \\\n          std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,       \\\n                                                    Frame::Grid, DIM(data)>,   \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::BlockLogical, Frame::Grid, DIM(data)>>>,  \\\n          std::unique_ptr<domain::CoordinateMapBase<                           \\\n                              Frame::Grid, Frame::Distorted, DIM(data)>,       \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::Grid, Frame::Distorted, DIM(data)>>>,     \\\n          std::unique_ptr<                                                     \\\n              domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial,     \\\n                                        DIM(data)>,                            \\\n              std::default_delete<domain::CoordinateMapBase<                   \\\n                  Frame::Distorted, Frame::Inertial, DIM(data)>>>);            \\\n  template domain::CoordinateMaps::Composition<                                \\\n      brigand::list<Frame::ElementLogical, Frame::BlockLogical,                \\\n                    Frame::Inertial>,                                          \\\n      DIM(data), std::integer_sequence<unsigned long, 0ul, 1ul>>::             \\\n      Composition(                                                             \\\n          std::unique_ptr<                                                     \\\n              domain::CoordinateMapBase<Frame::ElementLogical,                 \\\n                                        Frame::BlockLogical, DIM(data)>,       \\\n              std::default_delete<domain::CoordinateMapBase<                   \\\n                  Frame::ElementLogical, Frame::BlockLogical, DIM(data)>>>,    \\\n          std::unique_ptr<                                                     \\\n              domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial,  \\\n                                        DIM(data)>,                            \\\n              std::default_delete<domain::CoordinateMapBase<                   \\\n                  Frame::BlockLogical, Frame::Inertial, DIM(data)>>>);         \\\n  template domain::CoordinateMaps::Composition<                                \\\n      brigand::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid>,  \\\n      DIM(data), std::integer_sequence<unsigned long, 0ul, 1ul>>::             \\\n      Composition(                                                             \\\n          std::unique_ptr<                                                     \\\n              domain::CoordinateMapBase<Frame::ElementLogical,                 \\\n                                        Frame::BlockLogical, DIM(data)>,       \\\n              std::default_delete<domain::CoordinateMapBase<                   \\\n                  Frame::ElementLogical, Frame::BlockLogical, DIM(data)>>>,    \\\n          std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,       \\\n                                                    Frame::Grid, DIM(data)>,   \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::BlockLogical, Frame::Grid, DIM(data)>>>); \\\n  template domain::CoordinateMaps::Composition<                                \\\n      brigand::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid,   \\\n                    Frame::Distorted>,                                         \\\n      DIM(data), std::integer_sequence<unsigned long, 0ul, 1ul, 2ul>>::        \\\n      Composition(                                                             \\\n          std::unique_ptr<                                                     \\\n              domain::CoordinateMapBase<Frame::ElementLogical,                 \\\n                                        Frame::BlockLogical, DIM(data)>,       \\\n              std::default_delete<domain::CoordinateMapBase<                   \\\n                  Frame::ElementLogical, Frame::BlockLogical, DIM(data)>>>,    \\\n          std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,       \\\n                                                    Frame::Grid, DIM(data)>,   \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::BlockLogical, Frame::Grid, DIM(data)>>>,  \\\n          std::unique_ptr<domain::CoordinateMapBase<                           \\\n                              Frame::Grid, Frame::Distorted, DIM(data)>,       \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::Grid, Frame::Distorted, DIM(data)>>>);    \\\n  template domain::CoordinateMaps::Composition<                                \\\n      brigand::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid,   \\\n                    Frame::Inertial>,                                          \\\n      DIM(data), std::integer_sequence<unsigned long, 0ul, 1ul, 2ul>>::        \\\n      Composition(                                                             \\\n          std::unique_ptr<                                                     \\\n              domain::CoordinateMapBase<Frame::ElementLogical,                 \\\n                                        Frame::BlockLogical, DIM(data)>,       \\\n              std::default_delete<domain::CoordinateMapBase<                   \\\n                  Frame::ElementLogical, Frame::BlockLogical, DIM(data)>>>,    \\\n          std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,       \\\n                                                    Frame::Grid, DIM(data)>,   \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::BlockLogical, Frame::Grid, DIM(data)>>>,  \\\n          std::unique_ptr<domain::CoordinateMapBase<                           \\\n                              Frame::Grid, Frame::Inertial, DIM(data)>,        \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::Grid, Frame::Inertial, DIM(data)>>>);     \\\n  template domain::CoordinateMaps::Composition<                                \\\n      brigand::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid,   \\\n                    Frame::Distorted, Frame::Inertial>,                        \\\n      DIM(data), std::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul>>::   \\\n      Composition(                                                             \\\n          std::unique_ptr<                                                     \\\n              domain::CoordinateMapBase<Frame::ElementLogical,                 \\\n                                        Frame::BlockLogical, DIM(data)>,       \\\n              std::default_delete<domain::CoordinateMapBase<                   \\\n                  Frame::ElementLogical, Frame::BlockLogical, DIM(data)>>>,    \\\n          std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,       \\\n                                                    Frame::Grid, DIM(data)>,   \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::BlockLogical, Frame::Grid, DIM(data)>>>,  \\\n          std::unique_ptr<domain::CoordinateMapBase<                           \\\n                              Frame::Grid, Frame::Distorted, DIM(data)>,       \\\n                          std::default_delete<domain::CoordinateMapBase<       \\\n                              Frame::Grid, Frame::Distorted, DIM(data)>>>,     \\\n          std::unique_ptr<                                                     \\\n              domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial,     \\\n                                        DIM(data)>,                            \\\n              std::default_delete<domain::CoordinateMapBase<                   \\\n                  Frame::Distorted, Frame::Inertial, DIM(data)>>>);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE2, (1, 2, 3))\n\n#undef INSTANTIATE2\n#endif /* defined(__clang__) && __clang_major__ >= 16 */\n\n#undef DIM\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Composition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/Simd/Simd.hpp\"\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\brief A composition of coordinate maps at runtime\n *\n * Composes a sequence of `domain::CoordinateMapBase` that step through the\n * `Frames`. The result is another `domain::CoordinateMapBase`. This is\n * different to `domain::CoordinateMap`, which does the composition at compile\n * time. The use cases are different:\n *\n * - Use `domain::CoordinateMap` to compose maps at compile time to go from one\n *   (named) frame to another using any number of coordinate transformation. The\n *   coordinate transformations are concatenated to effectively form a single\n *   map, and intermediate frames have no meaning. This has the performance\n *   benefit that looking up pointers and calling into virtual member functions\n *   of intermediate maps is avoided, and it has the semantic benefit that\n *   intermediate frames without meaning are not named or even accessible.\n *   **Example:** A static BlockLogical -> Grid map that deforms the logical\n *   cube to a wedge, applies a radial redistribution of grid points, and\n *   translates + rotates the wedge.\n * - Use `domain::CoordinateMaps::Composition` (this class) to compose maps at\n *   runtime to step through a sequence of (named) frames.\n *   **Example:** A time-dependent ElementLogical -> BlockLogical -> Grid ->\n *   Inertial map that applies an affine transformation between the\n *   ElementLogical and BlockLogical frames (see\n *   domain::element_to_block_logical_map), then deforms the logical cube to a\n *   wedge using the map described above (Grid frame), and then rotates the grid\n *   with a time-dependent rotation map (Inertial frame).\n *\n * \\warning Think about performance implications before using this Composition\n * class. In an evolution it's usually advantageous to keep at least some of the\n * maps separate, e.g. to avoid reevaluating the static maps in every time step.\n * You can also access individual components of the composition in this class.\n *\n * \\tparam Frames The list of frames in the composition, as a tmpl::list<>.\n * The first entry in the list is the source frame, and the last entry is the\n * target frame of the composition. Maps in the composition step through the\n * `Frames`. For example, if `Frames = tmpl::list<Frame::ElementLogical,\n * Frame::BlockLogical, Frame::Inertial>`, then the composition has two maps:\n * ElementLogical -> BlockLogical and BlockLogical -> Inertial.\n *\n * ## Note on inverse Hessian computation\n * Below we work out the algebra for computing the inverse Hessian\n * used in this struct (if SPECTRE_AUTODIFF=ON). Suppose we have\n * a composition of maps \\f$ \\xi^i \\longrightarrow \\cdots \\longrightarrow x^i\n * \\longrightarrow y^i \\f$, where \\f$ \\xi^i \\f$ are the source coordinates, \\f$\n * x^i \\f$ are some intermediate coordinates, and \\f$ y^i \\f$ are the final\n * target coordinates,  We compute inverse Hessian by first\n * propagating autodiff dual types through the composed `call_impl` function to\n * automatically get the Hessian \\f$ \\frac{\\partial^2 y^i}{\\partial\\xi^j\n * \\partial\\xi^k} \\f$ Then the inverse Hessian is computed by the identity\n * \\f[ \\frac{\\partial^2\\xi^i}{\\partial y^m \\partial y^n} =\n * -\\frac{\\partial\\xi^i}{\\partial y^j}\\frac{\\partial\\xi^k}{\\partial y^m}\n * \\frac{\\partial\\xi^l}{\\partial y^n}\\frac{\\partial^2 y^j}{\\partial\\xi^k\n * \\partial\\xi^l}, \\f] where the inverse Jacobian is passed in as a function\n * argument.\n *\n * See Test_Composition.cpp for a different implementation. The current\n * implementation is chosen in production as it is faster when we have\n * the inverse Jacobian already, and in most cases we do.\n *\n * ## Note on auto differentiation\n * We use forward mode autodiff here as it is simpler to implement and\n * has better optimization than the reverse mode. Reverse mode in\n * the [Autodiff](https://github.com/autodiff/autodiff) library\n * has higher cost per propagation and does not support taping.\n * Also see this\n * [github issue](https://github.com/autodiff/autodiff/issues/332).\n */\ntemplate <typename Frames, size_t Dim,\n          typename Is = std::make_index_sequence<tmpl::size<Frames>::value - 1>>\nstruct Composition;\n\ntemplate <typename Frames, size_t Dim, size_t... Is>\nstruct Composition<Frames, Dim, std::index_sequence<Is...>>\n    : public CoordinateMapBase<tmpl::front<Frames>, tmpl::back<Frames>, Dim> {\n  using frames = Frames;\n  using SourceFrame = tmpl::front<Frames>;\n  using TargetFrame = tmpl::back<Frames>;\n  static constexpr size_t num_frames = tmpl::size<Frames>::value;\n  using Base = CoordinateMapBase<SourceFrame, TargetFrame, Dim>;\n  using FuncOfTimeMap = std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;\n  using Base::operator();\n\n  Composition() = default;\n  Composition(const Composition& rhs) { *this = rhs; }\n  Composition& operator=(const Composition& rhs);\n  Composition(Composition&& /*rhs*/) = default;\n  Composition& operator=(Composition&& /*rhs*/) = default;\n  ~Composition() override = default;\n\n  std::unique_ptr<Base> get_clone() const override {\n    return std::make_unique<Composition>(*this);\n  }\n\n  /// \\cond\n  explicit Composition(CkMigrateMessage* /*m*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Composition);  // NOLINT\n  /// \\endcond\n\n  Composition(std::unique_ptr<CoordinateMapBase<\n                  tmpl::at<frames, tmpl::size_t<Is>>,\n                  tmpl::at<frames, tmpl::size_t<Is + 1>>, Dim>>... maps);\n\n  const std::tuple<std::unique_ptr<\n      CoordinateMapBase<tmpl::at<frames, tmpl::size_t<Is>>,\n                        tmpl::at<frames, tmpl::size_t<Is + 1>>, Dim>>...>&\n  maps() const {\n    return maps_;\n  }\n\n  template <typename LocalSourceFrame, typename LocalTargetFrame>\n  const CoordinateMapBase<LocalSourceFrame, LocalTargetFrame, Dim>&\n  get_component() const {\n    return *get<std::unique_ptr<\n        CoordinateMapBase<LocalSourceFrame, LocalTargetFrame, Dim>>>(maps_);\n  }\n\n  bool is_identity() const override;\n\n  bool inv_jacobian_is_time_dependent() const override;\n\n  bool jacobian_is_time_dependent() const override;\n\n  const std::unordered_set<std::string>& function_of_time_names()\n      const override {\n    return function_of_time_names_;\n  }\n\n  bool supports_hessian() const override;\n\n  tnsr::I<double, Dim, TargetFrame> operator()(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  tnsr::I<DataVector, Dim, TargetFrame> operator()(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  std::optional<tnsr::I<double, Dim, SourceFrame>> inverse(\n      tnsr::I<double, Dim, TargetFrame> target_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  InverseJacobian<double, Dim, SourceFrame, TargetFrame> inv_jacobian(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame> inv_jacobian(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n#ifdef SPECTRE_AUTODIFF\n  InverseJacobian<autodiff::HigherOrderDual<2, double>, Dim, SourceFrame,\n                  TargetFrame>\n  inv_jacobian(tnsr::I<autodiff::HigherOrderDual<2, double>, Dim, SourceFrame>\n                   source_point,\n               double time = std::numeric_limits<double>::signaling_NaN(),\n               const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  // Compute inverse hessian by calling operator()\n  InverseHessian<double, Dim, SourceFrame, TargetFrame> inv_hessian(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      const InverseJacobian<double, Dim, SourceFrame, TargetFrame>& inverse_jac,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  // Compute inverse hessian by calling operator()\n  InverseHessian<DataVector, Dim, SourceFrame, TargetFrame> inv_hessian(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      const InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&\n          inverse_jac,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n#endif  // SPECTRE_AUTODIFF\n\n  Jacobian<double, Dim, SourceFrame, TargetFrame> jacobian(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  Jacobian<DataVector, Dim, SourceFrame, TargetFrame> jacobian(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  [[noreturn]] std::tuple<\n      tnsr::I<double, Dim, TargetFrame>,\n      InverseJacobian<double, Dim, SourceFrame, TargetFrame>,\n      Jacobian<double, Dim, SourceFrame, TargetFrame>,\n      tnsr::I<double, Dim, TargetFrame>>\n  coords_frame_velocity_jacobians(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  [[noreturn]] std::tuple<\n      tnsr::I<DataVector, Dim, TargetFrame>,\n      InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>,\n      Jacobian<DataVector, Dim, SourceFrame, TargetFrame>,\n      tnsr::I<DataVector, Dim, TargetFrame>>\n  coords_frame_velocity_jacobians(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const override;\n\n  [[noreturn]] std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, Dim>>\n  get_to_grid_frame() const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  template <typename DataType>\n  tnsr::I<DataType, Dim, TargetFrame> call_impl(\n      tnsr::I<DataType, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const;\n\n  template <typename DataType>\n  std::optional<tnsr::I<DataType, Dim, SourceFrame>> inverse_impl(\n      tnsr::I<DataType, Dim, TargetFrame> target_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const;\n\n  template <typename DataType>\n  InverseJacobian<DataType, Dim, SourceFrame, TargetFrame> inv_jacobian_impl(\n      tnsr::I<DataType, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const;\n\n  template <typename DataType>\n  InverseHessian<DataType, Dim, SourceFrame, TargetFrame> inv_hessian_impl(\n      tnsr::I<DataType, Dim, SourceFrame> source_point,\n      const ::InverseJacobian<DataType, Dim, SourceFrame, TargetFrame>&\n          inverse_jac,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const;\n\n  template <typename DataType>\n  Jacobian<DataType, Dim, SourceFrame, TargetFrame> jacobian_impl(\n      tnsr::I<DataType, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FuncOfTimeMap& functions_of_time = {}) const;\n\n  bool is_equal_to(const CoordinateMapBase<SourceFrame, TargetFrame, Dim>&\n                       other) const override;\n\n  std::tuple<std::unique_ptr<\n      CoordinateMapBase<tmpl::at<frames, tmpl::size_t<Is>>,\n                        tmpl::at<frames, tmpl::size_t<Is + 1>>, Dim>>...>\n      maps_;\n  std::unordered_set<std::string> function_of_time_names_;\n};\n\n// Template deduction guide\ntemplate <typename FirstMap, typename... Maps>\nComposition(std::unique_ptr<FirstMap>, std::unique_ptr<Maps>... maps)\n    -> Composition<tmpl::list<typename FirstMap::source_frame,\n                              typename FirstMap::target_frame,\n                              typename Maps::target_frame...>,\n                   FirstMap::dim>;\n\n/// \\cond\ntemplate <typename Frames, size_t Dim, size_t... Is>\nPUP::able::PUP_ID\n    Composition<Frames, Dim, std::index_sequence<Is...>>::my_PUP_ID = 0;\n/// \\endcond\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CoordinateMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class CoordinateMap\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <typeinfo>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Simd/Simd.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace domain {\n/// Contains all coordinate maps.\nnamespace CoordinateMaps {\n/// Contains the time-dependent coordinate maps\nnamespace TimeDependent {}\ntemplate <typename FirstMap, typename... Maps>\nconstexpr size_t map_dim = FirstMap::dim;\n}  // namespace CoordinateMaps\n\nnamespace CoordinateMap_detail {\nCREATE_IS_CALLABLE(function_of_time_names)\nCREATE_IS_CALLABLE_V(function_of_time_names)\n\ntemplate <typename T>\nstruct map_type {\n  using type = T;\n};\n\ntemplate <typename T>\nstruct map_type<std::unique_ptr<T>> {\n  using type = T;\n};\n\ntemplate <typename T>\nusing map_type_t = typename map_type<T>::type;\n\ntemplate <typename... Maps, size_t... Is>\nstd::unordered_set<std::string> initialize_names(\n    const std::tuple<Maps...>& maps, std::index_sequence<Is...> /*meta*/) {\n  std::unordered_set<std::string> function_of_time_names{};\n  const auto add_names = [&function_of_time_names, &maps](auto index) {\n    const auto& map = std::get<decltype(index)::value>(maps);\n    using TupleMap = std::decay_t<decltype(map)>;\n    constexpr bool map_is_unique_ptr = tt::is_a_v<std::unique_ptr, TupleMap>;\n    using Map = map_type_t<TupleMap>;\n    if constexpr (is_function_of_time_names_callable_v<Map>) {\n      if constexpr (map_is_unique_ptr) {\n        const auto& names = map->function_of_time_names();\n        function_of_time_names.insert(names.begin(), names.end());\n      } else {\n        const auto& names = map.function_of_time_names();\n        function_of_time_names.insert(names.begin(), names.end());\n      }\n    } else {\n      (void)function_of_time_names;\n    }\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(add_names(std::integral_constant<size_t, Is>{}));\n\n  return function_of_time_names;\n}\n\ntemplate <typename... Maps>\nstd::string get_unsupported_autodiff_maps_error() {\n  std::string unsupported_maps;\n  size_t index = 0;\n  (\n      [&]() {\n        if constexpr (not Maps::supports_hessian) {\n          if (not unsupported_maps.empty()) {\n            unsupported_maps += \", \";\n          }\n          unsupported_maps += \"Map \" + std::to_string(index) + \" (\" +\n                              pretty_type::get_name<Maps>() + \")\";\n        }\n        ++index;\n      }(),\n      ...);\n  return unsupported_maps;\n}\n\n}  // namespace CoordinateMap_detail\n\n/*!\n * \\ingroup CoordinateMapsGroup\n * \\brief Abstract base class for CoordinateMap\n */\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim>\nclass CoordinateMapBase : public PUP::able {\n public:\n  static constexpr size_t dim = Dim;\n  using source_frame = SourceFrame;\n  using target_frame = TargetFrame;\n\n  WRAPPED_PUPable_abstract(CoordinateMapBase);  // NOLINT\n\n  CoordinateMapBase() = default;\n  CoordinateMapBase(const CoordinateMapBase& /*rhs*/) = default;\n  CoordinateMapBase& operator=(const CoordinateMapBase& /*rhs*/) = default;\n  CoordinateMapBase(CoordinateMapBase&& /*rhs*/) = default;\n  CoordinateMapBase& operator=(CoordinateMapBase&& /*rhs*/) = default;\n  ~CoordinateMapBase() override = default;\n\n  virtual std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, Dim>>\n  get_clone() const = 0;\n\n  /// \\brief Retrieve the same map but going from `SourceFrame` to\n  /// `Frame::Grid`.\n  ///\n  /// This functionality is needed when composing time-dependent maps with\n  /// time-independent maps, where the target frame of the time-independent map\n  /// is `Frame::Grid`.\n  virtual std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, Dim>>\n  get_to_grid_frame() const = 0;\n\n  /// Returns `true` if the map is the identity\n  virtual bool is_identity() const = 0;\n\n  /// Returns `true` if the inverse Jacobian depends on time.\n  virtual bool inv_jacobian_is_time_dependent() const = 0;\n\n  /// Returns `true` if the Jacobian depends on time.\n  virtual bool jacobian_is_time_dependent() const = 0;\n\n  /// Get a set of all FunctionOfTime names used in this mapping\n  virtual const std::unordered_set<std::string>& function_of_time_names()\n      const = 0;\n\n  /// Returns `true` if this CoordinateMap supports autodiff\n  virtual bool supports_hessian() const = 0;\n\n  /// @{\n  /// Apply the `Maps` to the point(s) `source_point`\n  virtual tnsr::I<double, Dim, TargetFrame> operator()(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n  virtual tnsr::I<DataVector, Dim, TargetFrame> operator()(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n  /// @}\n\n#ifdef SPECTRE_AUTODIFF\n  /// @{\n  /// Apply the `Maps` to the point(s) `source_point`\n  ///\n  /// \\note Require SPECTRE_AUTODIFF=ON.\n  virtual tnsr::I<autodiff::HigherOrderDual<2, double>, Dim, TargetFrame>\n  operator()(tnsr::I<autodiff::HigherOrderDual<2, double>, Dim,\n                     SourceFrame> /*source_point*/,\n             double /*time*/ = std::numeric_limits<double>::signaling_NaN(),\n             const FunctionsOfTimeMap& /*functions_of_time*/ = {}) const {\n    ERROR(\"Call operator for autodiff types must be overriden.\");\n  }\n  virtual tnsr::I<autodiff::HigherOrderDual<2, simd::batch<double>>, Dim,\n                  TargetFrame>\n  operator()(tnsr::I<autodiff::HigherOrderDual<2, simd::batch<double>>, Dim,\n                     SourceFrame> /*source_point*/,\n             double /*time*/ = std::numeric_limits<double>::signaling_NaN(),\n             const FunctionsOfTimeMap& /*functions_of_time*/ = {}) const {\n    ERROR(\"Call operator for autodiff types must be overriden.\");\n  }\n  /// @}\n#endif  // SPECTRE_AUTODIFF\n\n  /// @{\n  /// Apply the inverse `Maps` to the point(s) `target_point`.\n  /// The returned std::optional is invalid if the map is not invertible\n  /// at `target_point`, or if `target_point` can be easily determined to not\n  /// make sense for the map.  An example of the latter is passing a\n  /// point with a negative value of z into a positive-z Wedge<3> inverse map.\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  virtual std::optional<tnsr::I<double, Dim, SourceFrame>> inverse(\n      tnsr::I<double, Dim, TargetFrame> target_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n  /// @}\n\n  /// @{\n  /// Compute the inverse Jacobian of the `Maps` at the point(s)\n  /// `source_point`\n  virtual InverseJacobian<double, Dim, SourceFrame, TargetFrame> inv_jacobian(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n  virtual InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>\n  inv_jacobian(tnsr::I<DataVector, Dim, SourceFrame> source_point,\n               double time = std::numeric_limits<double>::signaling_NaN(),\n               const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n#ifdef SPECTRE_AUTODIFF\n  virtual InverseJacobian<autodiff::HigherOrderDual<2, double>, Dim,\n                          SourceFrame, TargetFrame>\n  inv_jacobian(tnsr::I<autodiff::HigherOrderDual<2, double>, Dim, SourceFrame>\n                   source_point,\n               double time = std::numeric_limits<double>::signaling_NaN(),\n               const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n#endif  // SPECTRE_AUTODIFF\n  /// @}\n\n#ifdef SPECTRE_AUTODIFF\n  /// @{\n  /// Compute the inverse Hessian of the `Maps` at the point(s)\n  /// `source_point`\n  ///\n  /// \\note Require SPECTRE_AUTODIFF=ON.\n  virtual InverseHessian<double, Dim, SourceFrame, TargetFrame> inv_hessian(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      const InverseJacobian<double, Dim, SourceFrame, TargetFrame>& inverse_jac,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n  virtual InverseHessian<DataVector, Dim, SourceFrame, TargetFrame> inv_hessian(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      const InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&\n          inverse_jac,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n  /// @}\n#endif  // SPECTRE_AUTODIFF\n\n  /// @{\n  /// Compute the Jacobian of the `Maps` at the point(s) `source_point`\n  virtual Jacobian<double, Dim, SourceFrame, TargetFrame> jacobian(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n  virtual Jacobian<DataVector, Dim, SourceFrame, TargetFrame> jacobian(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const = 0;\n  /// @}\n\n  /// @{\n  /// Compute the mapped coordinates, frame velocity, Jacobian, and inverse\n  /// Jacobian\n  virtual auto coords_frame_velocity_jacobians(\n      tnsr::I<double, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const\n      -> std::tuple<tnsr::I<double, Dim, TargetFrame>,\n                    InverseJacobian<double, Dim, SourceFrame, TargetFrame>,\n                    Jacobian<double, Dim, SourceFrame, TargetFrame>,\n                    tnsr::I<double, Dim, TargetFrame>> = 0;\n  virtual auto coords_frame_velocity_jacobians(\n      tnsr::I<DataVector, Dim, SourceFrame> source_point,\n      double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const\n      -> std::tuple<tnsr::I<DataVector, Dim, TargetFrame>,\n                    InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>,\n                    Jacobian<DataVector, Dim, SourceFrame, TargetFrame>,\n                    tnsr::I<DataVector, Dim, TargetFrame>> = 0;\n  /// @}\n\n private:\n  virtual bool is_equal_to(const CoordinateMapBase& other) const = 0;\n  friend bool operator==(const CoordinateMapBase& lhs,\n                         const CoordinateMapBase& rhs) {\n    return typeid(lhs) == typeid(rhs) and lhs.is_equal_to(rhs);\n  }\n  friend bool operator!=(const CoordinateMapBase& lhs,\n                         const CoordinateMapBase& rhs) {\n    return not(lhs == rhs);\n  }\n};\n\n/*!\n * \\ingroup CoordinateMapsGroup\n * \\brief A coordinate map or composition of coordinate maps\n *\n * Maps coordinates from the `SourceFrame` to the `TargetFrame` using the\n * coordinate maps `Maps...`. The individual maps are applied left to right\n * from the source to the target Frame. The inverse map, as well as Jacobian\n * and inverse Jacobian are also provided. The `CoordinateMap` class must\n * be used even if just wrapping a single coordinate map. It is designed to\n * be an extremely minimal interface to the underlying coordinate maps. For\n * a list of all coordinate maps see the CoordinateMaps group or namespace.\n *\n * Each coordinate map must contain a `static constexpr size_t dim` variable\n * that is equal to the dimensionality of the map. The Coordinatemap class\n * contains a member `static constexpr size_t dim`, a type alias `source_frame`,\n * a type alias `target_frame` and `typelist of the `Maps...`.\n */\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap\n    : public CoordinateMapBase<SourceFrame, TargetFrame,\n                               CoordinateMaps::map_dim<Maps...>> {\n  static_assert(sizeof...(Maps) > 0, \"Must have at least one map\");\n  static_assert(\n      tmpl::all<tmpl::integral_list<size_t, Maps::dim...>,\n                std::is_same<tmpl::integral_constant<\n                                 size_t, CoordinateMaps::map_dim<Maps...>>,\n                             tmpl::_1>>::value,\n      \"All Maps passed to CoordinateMap must be of the same dimensionality.\");\n\n public:\n  static constexpr size_t dim = CoordinateMaps::map_dim<Maps...>;\n  using source_frame = SourceFrame;\n  using target_frame = TargetFrame;\n  using maps_list = tmpl::list<Maps...>;\n\n  /// Used for Charm++ serialization\n  CoordinateMap() = default;\n\n  CoordinateMap(const CoordinateMap& /*rhs*/) = default;\n  CoordinateMap& operator=(const CoordinateMap& /*rhs*/) = default;\n  CoordinateMap(CoordinateMap&& /*rhs*/) = default;\n  CoordinateMap& operator=(CoordinateMap&& /*rhs*/) = default;\n  ~CoordinateMap() override = default;\n\n  explicit CoordinateMap(Maps... maps);\n\n  std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, dim>> get_clone()\n      const override {\n    return std::make_unique<CoordinateMap>(*this);\n  }\n\n  std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, dim>>\n  get_to_grid_frame() const override {\n    return get_to_grid_frame_impl(std::make_index_sequence<sizeof...(Maps)>{});\n  }\n\n  /// Returns `true` if the map is the identity\n  bool is_identity() const override;\n\n  /// Returns `true` if the inverse Jacobian depends on time.\n  bool inv_jacobian_is_time_dependent() const override;\n\n  /// Returns `true` if the Jacobian depends on time.\n  bool jacobian_is_time_dependent() const override;\n\n  /// Get a set of all FunctionOfTime names from `Maps`\n  const std::unordered_set<std::string>& function_of_time_names()\n      const override {\n    return function_of_time_names_;\n  }\n\n  /// Returns `true` if this coordinate map supports hessian\n  bool supports_hessian() const override {\n    return (Maps::supports_hessian && ...);\n  }\n\n  /// @{\n  /// Apply the `Maps...` to the point(s) `source_point`\n  tnsr::I<double, dim, TargetFrame> operator()(\n      tnsr::I<double, dim, SourceFrame> source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    return call_impl(std::move(source_point), time, functions_of_time,\n                     std::make_index_sequence<sizeof...(Maps)>{});\n  }\n  tnsr::I<DataVector, dim, TargetFrame> operator()(\n      tnsr::I<DataVector, dim, SourceFrame> source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    return call_impl(std::move(source_point), time, functions_of_time,\n                     std::make_index_sequence<sizeof...(Maps)>{});\n  }\n  /// @}\n\n#ifdef SPECTRE_AUTODIFF\n  /// @{\n  /// Apply the `Maps...` to the point(s) `source_point`\n  ///\n  /// \\note Require SPECTRE_AUTODIFF=ON.\n  tnsr::I<autodiff::HigherOrderDual<2, double>, dim, TargetFrame> operator()(\n      tnsr::I<autodiff::HigherOrderDual<2, double>, dim, SourceFrame>\n          source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    if constexpr ((Maps::supports_hessian && ...)) {\n      return call_impl(std::move(source_point), time, functions_of_time,\n                       std::make_index_sequence<sizeof...(Maps)>{});\n    } else {\n      ERROR(\"At least one of the Maps does not support autodiff: \"\n            << CoordinateMap_detail::get_unsupported_autodiff_maps_error<\n                   Maps...>());\n    }\n  }\n  tnsr::I<autodiff::HigherOrderDual<2, simd::batch<double>>, dim, TargetFrame>\n  operator()(tnsr::I<autodiff::HigherOrderDual<2, simd::batch<double>>, dim,\n                     SourceFrame>\n                 source_point,\n             const double time = std::numeric_limits<double>::signaling_NaN(),\n             const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    if constexpr ((Maps::supports_hessian && ...)) {\n      return call_impl(std::move(source_point), time, functions_of_time,\n                       std::make_index_sequence<sizeof...(Maps)>{});\n    } else {\n      ERROR(\"At least one of the Maps does not support autodiff: \"\n            << CoordinateMap_detail::get_unsupported_autodiff_maps_error<\n                   Maps...>());\n    }\n  }\n  /// @}\n#endif  // SPECTRE_AUTODIFF\n\n  /// @{\n  /// Apply the inverse `Maps...` to the point(s) `target_point`\n  std::optional<tnsr::I<double, dim, SourceFrame>> inverse(\n      tnsr::I<double, dim, TargetFrame> target_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    return inverse_impl(std::move(target_point), time, functions_of_time,\n                        std::make_index_sequence<sizeof...(Maps)>{});\n  }\n  /// @}\n\n  /// @{\n  /// Compute the inverse Jacobian of the `Maps...` at the point(s)\n  /// `source_point`\n  InverseJacobian<double, dim, SourceFrame, TargetFrame> inv_jacobian(\n      tnsr::I<double, dim, SourceFrame> source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    return inv_jacobian_impl(std::move(source_point), time, functions_of_time);\n  }\n  InverseJacobian<DataVector, dim, SourceFrame, TargetFrame> inv_jacobian(\n      tnsr::I<DataVector, dim, SourceFrame> source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    return inv_jacobian_impl(std::move(source_point), time, functions_of_time);\n  }\n#ifdef SPECTRE_AUTODIFF\n  InverseJacobian<autodiff::HigherOrderDual<2, double>, dim, SourceFrame,\n                  TargetFrame>\n  inv_jacobian(\n      tnsr::I<autodiff::HigherOrderDual<2, double>, dim, SourceFrame>\n          source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    if constexpr ((Maps::supports_hessian && ...)) {\n      return inv_jacobian_impl(std::move(source_point), time,\n                               functions_of_time);\n    } else {\n      ERROR(\"At least one of the Maps does not support autodiff: \"\n            << CoordinateMap_detail::get_unsupported_autodiff_maps_error<\n                   Maps...>());\n    }\n  }\n#endif  // SPECTRE_AUTODIFF\n  /// @}\n\n#ifdef SPECTRE_AUTODIFF\n  /// @{\n  /*!\n   * Compute the inverse Hessian of the `Maps...` at the point(s)\n   * `source_point` by computing the Hessian of the `Maps...` and\n   * compose it with inverse Jacobians of the `Maps...`.\n   *\n   * This function propagates autodiff dual types\n   * through the `call_impl` function to automatically get the\n   * Hessian \\f$ \\frac{\\partial^2 x^i}{\\partial\\xi^j \\partial\\xi^k} \\f$\n   * where \\f$ x^i \\f$ are the target coordinates and \\f$ \\xi^i \\f$ are the\n   * source coordinates. Then the inverse Hessian is computed by the\n   * identity\n   * \\f[\\frac{\\partial^2 \\xi^i}{\\partial x^m \\partial x^n} =\n   * -\\frac{\\partial \\xi^i}{\\partial x^j} \\frac{\\partial \\xi^k}{\\partial x^m}\n   * \\frac{\\partial \\xi^l}{\\partial x^n} \\frac{\\partial^2 x^j}{\\partial \\xi^k\n   * \\partial \\xi^l}, \\f] where the inverse Jacobian is passed in as a function\n   * argument.\n   *\n   * See Test_CoordinateMap.cpp for a different implementation. The current\n   * implementation is chosen in production as it is faster when we have\n   * the inverse Jacobian already, and in most cases we do.\n   *\n   * We use forward mode autodiff here as it is simpler to implement and\n   * has better optimization than the reverse mode. Reverse mode in\n   * the [Autodiff](https://github.com/autodiff/autodiff) library\n   * has higher cost per propagation and does not support taping.\n   * Also see this\n   * [github issue](https://github.com/autodiff/autodiff/issues/332).\n   *\n   * \\note Require SPECTRE_AUTODIFF=ON.\n   */\n  InverseHessian<double, dim, SourceFrame, TargetFrame> inv_hessian(\n      tnsr::I<double, dim, SourceFrame> source_point,\n      const ::InverseJacobian<double, dim, SourceFrame, TargetFrame>&\n          inverse_jac,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    if constexpr ((Maps::supports_hessian && ...)) {\n      return inv_hessian_impl(std::move(source_point), inverse_jac, time,\n                              functions_of_time);\n    } else {\n      ERROR(\"At least one of the Maps does not support autodiff: \"\n            << CoordinateMap_detail::get_unsupported_autodiff_maps_error<\n                   Maps...>());\n    }\n  }\n  InverseHessian<DataVector, dim, SourceFrame, TargetFrame> inv_hessian(\n      tnsr::I<DataVector, dim, SourceFrame> source_point,\n      const ::InverseJacobian<DataVector, dim, SourceFrame, TargetFrame>&\n          inverse_jac,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    if constexpr ((Maps::supports_hessian && ...)) {\n      return inv_hessian_impl(std::move(source_point), inverse_jac, time,\n                              functions_of_time);\n    } else {\n      ERROR(\"At least one of the Maps does not support autodiff: \"\n            << CoordinateMap_detail::get_unsupported_autodiff_maps_error<\n                   Maps...>());\n    }\n  }\n  /// @}\n#endif  // SPECTRE_AUTODIFF\n\n  /// @{\n  /// Compute the Jacobian of the `Maps...` at the point(s) `source_point`\n  Jacobian<double, dim, SourceFrame, TargetFrame> jacobian(\n      tnsr::I<double, dim, SourceFrame> source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    return jacobian_impl(std::move(source_point), time, functions_of_time);\n  }\n  Jacobian<DataVector, dim, SourceFrame, TargetFrame> jacobian(\n      tnsr::I<DataVector, dim, SourceFrame> source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const override {\n    return jacobian_impl(std::move(source_point), time, functions_of_time);\n  }\n  /// @}\n\n  /// @{\n  /// Compute the mapped coordinates, frame velocity, Jacobian, and inverse\n  /// Jacobian. The inverse Jacobian is computed by numerically inverting the\n  /// Jacobian as this was measured to be quicker than computing it directly for\n  /// more complex map concatenations.\n  auto coords_frame_velocity_jacobians(\n      tnsr::I<double, dim, SourceFrame> source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const\n      -> std::tuple<tnsr::I<double, dim, TargetFrame>,\n                    InverseJacobian<double, dim, SourceFrame, TargetFrame>,\n                    Jacobian<double, dim, SourceFrame, TargetFrame>,\n                    tnsr::I<double, dim, TargetFrame>> override {\n    return coords_frame_velocity_jacobians_impl(std::move(source_point), time,\n                                                functions_of_time);\n  }\n  auto coords_frame_velocity_jacobians(\n      tnsr::I<DataVector, dim, SourceFrame> source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const FunctionsOfTimeMap& functions_of_time = {}) const\n      -> std::tuple<tnsr::I<DataVector, dim, TargetFrame>,\n                    InverseJacobian<DataVector, dim, SourceFrame, TargetFrame>,\n                    Jacobian<DataVector, dim, SourceFrame, TargetFrame>,\n                    tnsr::I<DataVector, dim, TargetFrame>> override {\n    return coords_frame_velocity_jacobians_impl(std::move(source_point), time,\n                                                functions_of_time);\n  }\n  /// @}\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(CoordinateMapBase<SourceFrame, TargetFrame, dim>),\n      CoordinateMap);\n\n  explicit CoordinateMap(CkMigrateMessage* /*unused*/) {}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    size_t version = 0;\n    p | version;\n    // Remember to increment the version number when making changes to this\n    // function. Retain support for unpacking data written by previous versions\n    // whenever possible. See `Domain` docs for details.\n    if (version >= 0) {\n      CoordinateMapBase<SourceFrame, TargetFrame, dim>::pup(p);\n      p | maps_;\n    }\n\n    // No need to pup this because it is uniquely determined by the maps\n    if (p.isUnpacking()) {\n      function_of_time_names_ = CoordinateMap_detail::initialize_names(\n          maps_, std::make_index_sequence<sizeof...(Maps)>{});\n    }\n  }\n\n private:\n  friend bool operator==(const CoordinateMap& lhs, const CoordinateMap& rhs) {\n    return lhs.maps_ == rhs.maps_;\n  }\n\n  template <typename NewMap, typename LocalSourceFrame,\n            typename LocalTargetFrame, typename... LocalMaps, size_t... Is>\n  friend CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps..., NewMap>\n  // NOLINTNEXTLINE(readability-redundant-declaration,-warnings-as-errors)\n  push_back_impl(\n      CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps...>&& old_map,\n      NewMap new_map, std::index_sequence<Is...> /*meta*/);\n\n  template <typename... NewMaps, typename LocalSourceFrame,\n            typename LocalTargetFrame, typename... LocalMaps, size_t... Is,\n            size_t... Js>\n  friend CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps...,\n                       NewMaps...>\n  // NOLINTNEXTLINE(readability-redundant-declaration,-warnings-as-errors)\n  push_back_impl(\n      CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps...>&& old_map,\n      CoordinateMap<LocalSourceFrame, LocalTargetFrame, NewMaps...> new_map,\n      std::index_sequence<Is...> /*meta*/, std::index_sequence<Js...> /*meta*/);\n\n  template <typename NewMap, typename LocalSourceFrame,\n            typename LocalTargetFrame, typename... LocalMaps, size_t... Is>\n  friend CoordinateMap<LocalSourceFrame, LocalTargetFrame, NewMap, LocalMaps...>\n  // NOLINTNEXTLINE(readability-redundant-declaration,-warnings-as-errors)\n  push_front_impl(\n      CoordinateMap<LocalSourceFrame, LocalTargetFrame, LocalMaps...>&& old_map,\n      NewMap new_map, std::index_sequence<Is...> /*meta*/);\n\n  template <size_t... Is>\n  std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, dim>>\n      get_to_grid_frame_impl(std::index_sequence<Is...> /*meta*/) const;\n\n  bool is_equal_to(const CoordinateMapBase<SourceFrame, TargetFrame, dim>&\n                       other) const override {\n    const auto& cast_of_other = dynamic_cast<const CoordinateMap&>(other);\n    return *this == cast_of_other;\n  }\n\n  void check_functions_of_time(\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T, size_t... Is>\n  tnsr::I<T, dim, TargetFrame> call_impl(\n      tnsr::I<T, dim, SourceFrame>&& source_point, double time,\n      const FunctionsOfTimeMap& functions_of_time,\n      std::index_sequence<Is...> /*meta*/) const;\n\n  template <typename T, size_t... Is>\n  std::optional<tnsr::I<T, dim, SourceFrame>> inverse_impl(\n      tnsr::I<T, dim, TargetFrame>&& target_point, double time,\n      const FunctionsOfTimeMap& functions_of_time,\n      std::index_sequence<Is...> /*meta*/) const;\n\n  template <typename T>\n  InverseJacobian<T, dim, SourceFrame, TargetFrame> inv_jacobian_impl(\n      tnsr::I<T, dim, SourceFrame>&& source_point, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  InverseHessian<T, dim, SourceFrame, TargetFrame> inv_hessian_impl(\n      tnsr::I<T, dim, SourceFrame>&& source_point,\n      const ::InverseJacobian<T, dim, SourceFrame, TargetFrame>& inverse_jac,\n      double time, const FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  Jacobian<T, dim, SourceFrame, TargetFrame> jacobian_impl(\n      tnsr::I<T, dim, SourceFrame>&& source_point, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  std::tuple<tnsr::I<T, dim, TargetFrame>,\n             InverseJacobian<T, dim, SourceFrame, TargetFrame>,\n             Jacobian<T, dim, SourceFrame, TargetFrame>,\n             tnsr::I<T, dim, TargetFrame>>\n  coords_frame_velocity_jacobians_impl(\n      tnsr::I<T, dim, SourceFrame> source_point, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  std::tuple<Maps...> maps_;\n  std::unordered_set<std::string> function_of_time_names_;\n};\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Creates a `CoordinateMap` of `maps...`\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nauto make_coordinate_map(Maps&&... maps)\n    -> CoordinateMap<SourceFrame, TargetFrame, std::decay_t<Maps>...>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Creates a `std::unique_ptr<CoordinateMapBase>` of `maps...`\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nauto make_coordinate_map_base(Maps&&... maps)\n    -> std::unique_ptr<CoordinateMapBase<\n        SourceFrame, TargetFrame,\n        CoordinateMap<SourceFrame, TargetFrame, std::decay_t<Maps>...>::dim>>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Creates a `std::vector<std::unique_ptr<CoordinateMapBase>>`\n/// containing the result of `make_coordinate_map_base` applied to each\n/// argument passed in.\ntemplate <typename SourceFrame, typename TargetFrame, typename Arg0,\n          typename... Args>\nauto make_vector_coordinate_map_base(Arg0&& arg_0, Args&&... remaining_args)\n    -> std::vector<std::unique_ptr<\n        CoordinateMapBase<SourceFrame, TargetFrame, std::decay_t<Arg0>::dim>>>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Creates a `std::vector<std::unique_ptr<CoordinateMapBase>>`\n/// containing the result of `make_coordinate_map_base` applied to each\n/// element of the vector of maps composed with the rest of the arguments\n/// passed in.\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim, typename Map,\n          typename... Maps>\nauto make_vector_coordinate_map_base(std::vector<Map> maps,\n                                     const Maps&... remaining_maps)\n    -> std::vector<\n        std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, Dim>>>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Creates a `CoordinateMap` by appending the new map to the end of the\n/// old maps\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps,\n          typename NewMap>\nCoordinateMap<SourceFrame, TargetFrame, Maps..., NewMap> push_back(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...> old_map, NewMap new_map);\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Creates a `CoordinateMap` by prepending the new map to the beginning\n/// of the old maps\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps,\n          typename NewMap>\nCoordinateMap<SourceFrame, TargetFrame, NewMap, Maps...> push_front(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...> old_map, NewMap new_map);\n\n/// \\cond\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nPUP::able::PUP_ID\n    CoordinateMap<SourceFrame, TargetFrame, Maps...>::my_PUP_ID =  // NOLINT\n    0;\n/// \\endcond\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CoordinateMap.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <typeinfo>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMapHelpers.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependentHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/Tuple.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMap_detail {\ntemplate <size_t Dim, typename T>\nusing combined_coords_frame_velocity_jacs_t =\n    decltype(std::declval<T>().coords_frame_velocity_jacobian(\n        std::declval<gsl::not_null<std::array<DataVector, Dim>*>>(),\n        std::declval<gsl::not_null<std::array<DataVector, Dim>*>>(),\n        std::declval<\n            gsl::not_null<tnsr::Ij<DataVector, Dim, Frame::NoFrame>*>>(),\n        std::declval<const double>(),\n        std::declval<const FunctionsOfTimeMap&>()));\n\ntemplate <size_t Dim, typename T, typename = std::void_t<>>\nstruct has_combined_coords_frame_velocity_jacs : std::false_type {};\n\ntemplate <size_t Dim, typename T>\nstruct has_combined_coords_frame_velocity_jacs<\n    Dim, T, std::void_t<combined_coords_frame_velocity_jacs_t<Dim, T>>>\n    : std::true_type {};\n\ntemplate <size_t Dim, typename T>\ninline constexpr bool has_combined_coords_frame_velocity_jacs_v =\n    has_combined_coords_frame_velocity_jacs<Dim, T>::value;\n}  // namespace CoordinateMap_detail\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nCoordinateMap<SourceFrame, TargetFrame, Maps...>::CoordinateMap(Maps... maps)\n    : maps_(std::move(maps)...),\n      function_of_time_names_(CoordinateMap_detail::initialize_names(\n          maps_, std::make_index_sequence<sizeof...(Maps)>{})) {}\n\nnamespace CoordinateMap_detail {\ntemplate <typename... Maps, size_t... Is>\nbool is_identity_impl(const std::tuple<Maps...>& maps,\n                      std::index_sequence<Is...> /*meta*/) {\n  bool is_identity = true;\n  const auto check_map_is_identity = [&is_identity, &maps](auto index) {\n    if (is_identity) {\n      is_identity = std::get<decltype(index)::value>(maps).is_identity();\n    }\n    return '0';\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      check_map_is_identity(std::integral_constant<size_t, Is>{}));\n  return is_identity;\n}\n}  // namespace CoordinateMap_detail\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nbool CoordinateMap<SourceFrame, TargetFrame, Maps...>::is_identity() const {\n  return CoordinateMap_detail::is_identity_impl(\n      maps_, std::make_index_sequence<sizeof...(Maps)>{});\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nbool CoordinateMap<SourceFrame, TargetFrame,\n                   Maps...>::inv_jacobian_is_time_dependent() const {\n  return tmpl2::flat_any_v<\n      domain::is_jacobian_time_dependent_t<Maps, double>::value...>;\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nbool CoordinateMap<SourceFrame, TargetFrame,\n                   Maps...>::jacobian_is_time_dependent() const {\n  return inv_jacobian_is_time_dependent();\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nvoid CoordinateMap<SourceFrame, TargetFrame, Maps...>::check_functions_of_time(\n    [[maybe_unused]] const FunctionsOfTimeMap& functions_of_time) const {\n// Even though an assert is already in debug mode, we also want to avoid the\n// loop\n#ifdef SPECTRE_DEBUG\n  for (const std::string& name : function_of_time_names_) {\n    ASSERT(functions_of_time.count(name) == 1,\n           \"The function of time '\" << name\n                                    << \"' is not one of the known functions of \"\n                                       \"time. The known functions of time are: \"\n                                    << keys_of(functions_of_time));\n  }\n#endif\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\ntemplate <typename T, size_t... Is>\ntnsr::I<T, CoordinateMap<SourceFrame, TargetFrame, Maps...>::dim, TargetFrame>\nCoordinateMap<SourceFrame, TargetFrame, Maps...>::call_impl(\n    tnsr::I<T, dim, SourceFrame>&& source_point, const double time,\n    const FunctionsOfTimeMap& functions_of_time,\n    std::index_sequence<Is...> /*meta*/) const {\n  check_functions_of_time(functions_of_time);\n  std::array<T, dim> mapped_point = make_array<T, dim>(std::move(source_point));\n\n  EXPAND_PACK_LEFT_TO_RIGHT([](const auto& the_map, std::array<T, dim>& point,\n                               const double t,\n                               const FunctionsOfTimeMap& funcs_of_time) {\n    if constexpr (domain::is_map_time_dependent_t<decltype(the_map)>{}) {\n      point = the_map(point, t, funcs_of_time);\n    } else {\n      (void)t;\n      (void)funcs_of_time;\n      if (LIKELY(not the_map.is_identity())) {\n        point = the_map(point);\n      }\n    }\n  }(std::get<Is>(maps_), mapped_point, time, functions_of_time));\n\n  return tnsr::I<T, dim, TargetFrame>(std::move(mapped_point));\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\ntemplate <typename T, size_t... Is>\nstd::optional<tnsr::I<T, CoordinateMap<SourceFrame, TargetFrame, Maps...>::dim,\n                      SourceFrame>>\nCoordinateMap<SourceFrame, TargetFrame, Maps...>::inverse_impl(\n    tnsr::I<T, dim, TargetFrame>&& target_point, const double time,\n    const FunctionsOfTimeMap& functions_of_time,\n    std::index_sequence<Is...> /*meta*/) const {\n  check_functions_of_time(functions_of_time);\n  std::optional<std::array<T, dim>> mapped_point(\n      make_array<T, dim>(std::move(target_point)));\n\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      [](const auto& the_map, std::optional<std::array<T, dim>>& point,\n         const double t, const FunctionsOfTimeMap& funcs_of_time) {\n        if constexpr (domain::is_map_time_dependent_t<decltype(the_map)>{}) {\n          if (point.has_value()) {\n            point = the_map.inverse(point.value(), t, funcs_of_time);\n          }\n        } else {\n          (void)t;\n          (void)funcs_of_time;\n          if (point.has_value()) {\n            if (LIKELY(not the_map.is_identity())) {\n              point = the_map.inverse(point.value());\n            }\n          }\n        }\n        // this is the inverse function, so the iterator sequence below is\n        // reversed\n      }(std::get<sizeof...(Maps) - 1 - Is>(maps_), mapped_point, time,\n        functions_of_time));\n\n  return mapped_point\n             ? tnsr::I<T, dim, SourceFrame>(std::move(mapped_point.value()))\n             : std::optional<tnsr::I<T, dim, SourceFrame>>{};\n}\n\nnamespace detail {\ntemplate <typename T, typename Map, size_t Dim>\nvoid get_jacobian(\n    const gsl::not_null<tnsr::Ij<T, Dim, Frame::NoFrame>*> no_frame_jac,\n    const Map& the_map, const std::array<T, Dim>& point, const double /*t*/,\n    const FunctionsOfTimeMap&\n    /*funcs_of_time*/,\n    std::false_type /*jacobian_is_time_dependent*/) {\n  if (LIKELY(not the_map.is_identity())) {\n    *no_frame_jac = the_map.jacobian(point);\n  } else {\n    *no_frame_jac = identity<Dim>(point[0]);\n  }\n}\n\ntemplate <typename T, typename Map, size_t Dim>\nvoid get_jacobian(\n    const gsl::not_null<tnsr::Ij<T, Dim, Frame::NoFrame>*> no_frame_jac,\n    const Map& the_map, const std::array<T, Dim>& point, const double t,\n    const FunctionsOfTimeMap& funcs_of_time,\n    std::true_type /*jacobian_is_time_dependent*/) {\n  *no_frame_jac = the_map.jacobian(point, t, funcs_of_time);\n}\n\ntemplate <typename T, typename Map, size_t Dim>\nvoid get_inv_jacobian(\n    const gsl::not_null<tnsr::Ij<T, Dim, Frame::NoFrame>*> no_frame_inv_jac,\n    const Map& the_map, const std::array<T, Dim>& point, const double /*t*/,\n    const FunctionsOfTimeMap&\n    /*funcs_of_time*/,\n    std::false_type /*jacobian_is_time_dependent*/) {\n  if (LIKELY(not the_map.is_identity())) {\n    *no_frame_inv_jac = the_map.inv_jacobian(point);\n  } else {\n    *no_frame_inv_jac = identity<Dim>(point[0]);\n  }\n}\n\ntemplate <typename T, typename Map, size_t Dim>\nvoid get_inv_jacobian(\n    const gsl::not_null<tnsr::Ij<T, Dim, Frame::NoFrame>*> no_frame_inv_jac,\n    const Map& the_map, const std::array<T, Dim>& point, const double t,\n    const FunctionsOfTimeMap& funcs_of_time,\n    std::true_type /*jacobian_is_time_dependent*/) {\n  *no_frame_inv_jac = the_map.inv_jacobian(point, t, funcs_of_time);\n}\n\ntemplate <typename T, size_t Dim, typename SourceFrame, typename TargetFrame>\nvoid multiply_jacobian(\n    const gsl::not_null<Jacobian<T, Dim, SourceFrame, TargetFrame>*> jac,\n    const tnsr::Ij<T, Dim, Frame::NoFrame>& noframe_jac) {\n  std::array<T, Dim> temp{};\n  for (size_t source = 0; source < Dim; ++source) {\n    for (size_t target = 0; target < Dim; ++target) {\n      gsl::at(temp, target) = noframe_jac.get(target, 0) * jac->get(0, source);\n      for (size_t dummy = 1; dummy < Dim; ++dummy) {\n        gsl::at(temp, target) +=\n            noframe_jac.get(target, dummy) * jac->get(dummy, source);\n      }\n    }\n    for (size_t target = 0; target < Dim; ++target) {\n      jac->get(target, source) = std::move(gsl::at(temp, target));\n    }\n  }\n}\n\ntemplate <typename T, size_t Dim, typename SourceFrame, typename TargetFrame>\nvoid multiply_inv_jacobian(\n    const gsl::not_null<Jacobian<T, Dim, SourceFrame, TargetFrame>*> inv_jac,\n    const tnsr::Ij<T, Dim, Frame::NoFrame>& noframe_inv_jac) {\n  std::array<T, Dim> temp{};\n  for (size_t source = 0; source < Dim; ++source) {\n    for (size_t target = 0; target < Dim; ++target) {\n      gsl::at(temp, target) =\n          inv_jac->get(source, 0) * noframe_inv_jac.get(0, target);\n      for (size_t dummy = 1; dummy < Dim; ++dummy) {\n        gsl::at(temp, target) +=\n            inv_jac->get(source, dummy) * noframe_inv_jac.get(dummy, target);\n      }\n    }\n    for (size_t target = 0; target < Dim; ++target) {\n      inv_jac->get(source, target) = std::move(gsl::at(temp, target));\n    }\n  }\n}\n}  // namespace detail\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\ntemplate <typename T>\nauto CoordinateMap<SourceFrame, TargetFrame, Maps...>::inv_jacobian_impl(\n    tnsr::I<T, dim, SourceFrame>&& source_point, const double time,\n    const FunctionsOfTimeMap& functions_of_time) const\n    -> InverseJacobian<T, dim, SourceFrame, TargetFrame> {\n  check_functions_of_time(functions_of_time);\n  std::array<T, dim> mapped_point = make_array<T, dim>(std::move(source_point));\n\n  InverseJacobian<T, dim, SourceFrame, TargetFrame> inv_jac{};\n\n  tuple_transform(maps_, [&inv_jac, &mapped_point, time, &functions_of_time](\n                             const auto& map, auto index) {\n    constexpr size_t count = decltype(index)::value;\n    using Map = std::decay_t<decltype(map)>;\n\n    tnsr::Ij<T, dim, Frame::NoFrame> noframe_inv_jac{};\n\n    if (UNLIKELY(count == 0)) {\n      ::domain::detail::get_inv_jacobian(\n          make_not_null(&noframe_inv_jac), map, mapped_point, time,\n          functions_of_time, domain::is_jacobian_time_dependent_t<Map, T>{});\n      for (size_t source = 0; source < dim; ++source) {\n        for (size_t target = 0; target < dim; ++target) {\n          inv_jac.get(source, target) =\n              std::move(noframe_inv_jac.get(source, target));\n        }\n      }\n    } else if (LIKELY(not map.is_identity())) {\n      ::domain::detail::get_inv_jacobian(\n          make_not_null(&noframe_inv_jac), map, mapped_point, time,\n          functions_of_time, domain::is_jacobian_time_dependent_t<Map, T>{});\n      ::domain::detail::multiply_inv_jacobian(make_not_null(&inv_jac),\n                                              noframe_inv_jac);\n    }\n\n    // Compute the source coordinates for the next map, only if we are not\n    // the last map and the map is not the identity.\n    if (not map.is_identity() and count + 1 != sizeof...(Maps)) {\n      CoordinateMap_detail::apply_map(\n          make_not_null(&mapped_point), map, time, functions_of_time,\n          domain::is_map_time_dependent_t<decltype(map)>{});\n    }\n  });\n  return inv_jac;\n}\n\n#ifdef SPECTRE_AUTODIFF\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\ntemplate <typename T>\nauto CoordinateMap<SourceFrame, TargetFrame, Maps...>::inv_hessian_impl(\n    tnsr::I<T, dim, SourceFrame>&& source_point,\n    const ::InverseJacobian<T, dim, SourceFrame, TargetFrame>& inverse_jac,\n    const double time, const FunctionsOfTimeMap& functions_of_time) const\n    -> InverseHessian<T, dim, SourceFrame, TargetFrame> {\n  // first compute hessian\n  using BatchType = simd::batch<double>;\n  using SecondOrderDual = autodiff::HigherOrderDual<2, BatchType>;\n  using SecondOrderDualNum = autodiff::HigherOrderDual<2, double>;\n\n  size_t num_pts = 1;\n  size_t vec_end = 0;\n  if constexpr (std::is_same_v<T, DataVector>) {\n    num_pts = get<0>(source_point).size();\n    vec_end = (num_pts / BatchType::size) * BatchType::size;\n  }\n  ::Hessian<T, dim, SourceFrame, TargetFrame> hessian{num_pts};\n  ::InverseHessian<T, dim, SourceFrame, TargetFrame> inverse_hessian{num_pts};\n\n  // manual vectorization with xsimd\n  if constexpr (std::is_same_v<T, DataVector>) {\n    for (size_t pts_index = 0; pts_index < vec_end;\n         pts_index += BatchType::size) {\n      tnsr::I<SecondOrderDual, dim, SourceFrame> dual_source_coords;\n\n      for (size_t i = 0; i < dim; ++i) {\n        for (size_t j = i; j < dim; ++j) {\n          [&]<std::size_t... Is>(std::index_sequence<Is...>) {\n            ((get<Is>(dual_source_coords) =\n                  BatchType::load_unaligned(\n                    &(get<Is>(source_point))[pts_index])),\n             ...);\n          }(std::make_index_sequence<dim>{});\n\n          // In forward mode, we use seed to initialize the variable\n          // w.r.t. to which we want to differentiate. Seeding\n          // both first and second order duals computes\n          // second derivatives, which can be extracted by derivative<2>.\n          // For minimal examples, see Test_Autodiff.cpp.\n          autodiff::seed<1>(dual_source_coords.get(i), 1.0);\n          autodiff::seed<2>(dual_source_coords.get(j), 1.0);\n\n          const auto dual_target_coords =\n              call_impl(std::move(dual_source_coords), time, functions_of_time,\n                        std::make_index_sequence<sizeof...(Maps)>{});\n          for (size_t k = 0; k < dim; ++k) {\n            const auto deriv_kij =\n                autodiff::derivative<2>(dual_target_coords.get(k));\n            deriv_kij.store_unaligned(&hessian.get(k, i, j)[pts_index]);\n          }\n        }\n      }\n    }\n  }\n  // dealing with the tail\n  for (size_t pts_index = vec_end; pts_index < num_pts; ++pts_index) {\n    tnsr::I<SecondOrderDualNum, dim, SourceFrame> dual_source_coords;\n\n    for (size_t i = 0; i < dim; ++i) {\n      for (size_t j = i; j < dim; ++j) {\n        if constexpr (std::is_same_v<T, double>) {\n          [&]<std::size_t... Is>(std::index_sequence<Is...>) {\n            ((get<Is>(dual_source_coords) = get<Is>(source_point)), ...);\n          }(std::make_index_sequence<dim>{});\n        } else {\n          [&]<std::size_t... Is>(std::index_sequence<Is...>) {\n            ((get<Is>(dual_source_coords) =\n                  gsl::at(get<Is>(source_point), pts_index)),\n             ...);\n          }(std::make_index_sequence<dim>{});\n        }\n\n        autodiff::seed<1>(dual_source_coords.get(i), 1.0);\n        autodiff::seed<2>(dual_source_coords.get(j), 1.0);\n\n        const auto dual_target_coords =\n            call_impl(std::move(dual_source_coords), time, functions_of_time,\n                      std::make_index_sequence<sizeof...(Maps)>{});\n        for (size_t k = 0; k < dim; ++k) {\n          if constexpr (std::is_same_v<T, double>) {\n            hessian.get(k, i, j) =\n                autodiff::derivative<2>(dual_target_coords.get(k));\n          } else {\n            hessian.get(k, i, j)[pts_index] =\n                autodiff::derivative<2>(dual_target_coords.get(k));\n          }\n        }\n      }\n    }\n  }\n\n  // piece together the inverse hessian from hessian and inverse jacobian\n  ::tenex::evaluate<ti::I, ti::m, ti::n>(\n      make_not_null(&inverse_hessian),\n      -1.0 * inverse_jac(ti::I, ti::j) * inverse_jac(ti::K, ti::m) *\n          inverse_jac(ti::L, ti::n) * hessian(ti::J, ti::k, ti::l));\n\n  return inverse_hessian;\n}\n#endif  // SPECTRE_AUTODIFF\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\ntemplate <typename T>\nauto CoordinateMap<SourceFrame, TargetFrame, Maps...>::jacobian_impl(\n    tnsr::I<T, dim, SourceFrame>&& source_point, const double time,\n    const FunctionsOfTimeMap& functions_of_time) const\n    -> Jacobian<T, dim, SourceFrame, TargetFrame> {\n  check_functions_of_time(functions_of_time);\n  std::array<T, dim> mapped_point = make_array<T, dim>(std::move(source_point));\n  Jacobian<T, dim, SourceFrame, TargetFrame> jac{};\n\n  tuple_transform(maps_, [&jac, &mapped_point, time, &functions_of_time](\n                             const auto& map, auto index) {\n    constexpr size_t count = decltype(index)::value;\n    using Map = std::decay_t<decltype(map)>;\n\n    tnsr::Ij<T, dim, Frame::NoFrame> noframe_jac{};\n\n    if (UNLIKELY(count == 0)) {\n      ::domain::detail::get_jacobian(\n          make_not_null(&noframe_jac), map, mapped_point, time,\n          functions_of_time, domain::is_jacobian_time_dependent_t<Map, T>{});\n      for (size_t target = 0; target < dim; ++target) {\n        for (size_t source = 0; source < dim; ++source) {\n          jac.get(target, source) = std::move(noframe_jac.get(target, source));\n        }\n      }\n    } else if (LIKELY(not map.is_identity())) {\n      ::domain::detail::get_jacobian(\n          make_not_null(&noframe_jac), map, mapped_point, time,\n          functions_of_time, domain::is_jacobian_time_dependent_t<Map, T>{});\n      ::domain::detail::multiply_jacobian(make_not_null(&jac), noframe_jac);\n    }\n\n    // Compute the source coordinates for the next map, only if we are not\n    // the last map and the map is not the identity.\n    if (not map.is_identity() and count + 1 != sizeof...(Maps)) {\n      CoordinateMap_detail::apply_map(\n          make_not_null(&mapped_point), map, time, functions_of_time,\n          domain::is_map_time_dependent_t<decltype(map)>{});\n    }\n  });\n  return jac;\n}\n\nnamespace detail {\ntemplate <typename T, TimeIndependentMap Map, size_t Dim>\nstd::array<T, Dim> get_frame_velocity(\n    const Map& /*the_map*/, const std::array<T, Dim>& /*point*/,\n    const double /*t*/, const FunctionsOfTimeMap& /*funcs_of_time*/) {\n  return std::array<T, Dim>{};\n}\n\ntemplate <typename T, TimeDependentMap Map, size_t Dim>\nstd::array<T, Dim> get_frame_velocity(const Map& the_map,\n                                      const std::array<T, Dim>& point,\n                                      const double t,\n                                      const FunctionsOfTimeMap& funcs_of_time) {\n  return the_map.frame_velocity(point, t, funcs_of_time);\n}\n}  // namespace detail\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\ntemplate <typename T>\nauto CoordinateMap<SourceFrame, TargetFrame, Maps...>::\n    coords_frame_velocity_jacobians_impl(\n        tnsr::I<T, dim, SourceFrame> source_point, const double time,\n        const FunctionsOfTimeMap& functions_of_time) const\n    -> std::tuple<tnsr::I<T, dim, TargetFrame>,\n                  InverseJacobian<T, dim, SourceFrame, TargetFrame>,\n                  Jacobian<T, dim, SourceFrame, TargetFrame>,\n                  tnsr::I<T, dim, TargetFrame>> {\n  check_functions_of_time(functions_of_time);\n  std::array<T, dim> mapped_point = make_array<T, dim>(std::move(source_point));\n  Jacobian<T, dim, SourceFrame, TargetFrame> jac{};\n  tnsr::I<T, dim, TargetFrame> frame_velocity{};\n\n  tuple_transform(\n      maps_,\n      [&frame_velocity, &jac, &mapped_point, time, &functions_of_time](\n          const auto& map, auto index, const std::tuple<Maps...>& /*maps*/) {\n        constexpr size_t count = decltype(index)::value;\n        using Map = std::decay_t<decltype(map)>;\n        static constexpr bool is_time_dependent =\n            domain::is_map_time_dependent_v<Map>;\n        static constexpr bool use_combined_call =\n            CoordinateMap_detail::has_combined_coords_frame_velocity_jacs_v<\n                dim, Map> and\n            std::is_same_v<T, DataVector>;\n\n        [[maybe_unused]] std::array<T, dim> noframe_frame_velocity{};\n        tnsr::Ij<T, dim, Frame::NoFrame> noframe_jac{};\n        if constexpr (use_combined_call) {\n          map.coords_frame_velocity_jacobian(\n              make_not_null(&mapped_point),\n              make_not_null(&noframe_frame_velocity),\n              make_not_null(&noframe_jac), time, functions_of_time);\n        } else {\n          // if the map is the identity we do not compute it unless it is the\n          // first map, then it is used for initialization\n          if (not(map.is_identity() and count != 0)) {\n            ::domain::detail::get_jacobian(\n                make_not_null(&noframe_jac), map, mapped_point, time,\n                functions_of_time,\n                domain::is_jacobian_time_dependent_t<Map, T>{});\n            if constexpr (is_time_dependent) {\n              noframe_frame_velocity = domain::detail::get_frame_velocity(\n                  map, mapped_point, time, functions_of_time);\n            }\n            CoordinateMap_detail::apply_map(\n                make_not_null(&mapped_point), map, time, functions_of_time,\n                domain::is_map_time_dependent_t<decltype(map)>{});\n          }\n        }\n        if constexpr (count == 0) {\n          for (size_t target = 0; target < dim; ++target) {\n            for (size_t source = 0; source < dim; ++source) {\n              jac.get(target, source) =\n                  std::move(noframe_jac.get(target, source));\n            }\n          }\n          // Set frame velocity\n          if constexpr (is_time_dependent) {\n            for (size_t i = 0; i < dim; ++i) {\n              frame_velocity.get(i) =\n                  std::move(gsl::at(noframe_frame_velocity, i));\n            }\n          } else {\n            // If the first map is time-independent the velocity is\n            // initialized to zero\n            for (size_t i = 0; i < frame_velocity.size(); ++i) {\n              frame_velocity[i] = make_with_value<T>(get<0, 0>(jac), 0.0);\n            }\n          }\n        } else if (LIKELY(not map.is_identity())) {\n          // WARNING: we have assumed that if the map is the identity the frame\n          // velocity is also zero. That is, we do not optimize for the map\n          // being instantaneously zero.\n          ::domain::detail::multiply_jacobian(make_not_null(&jac), noframe_jac);\n\n          if constexpr (is_time_dependent) {\n            for (size_t target_frame_index = 0; target_frame_index < dim;\n                 ++target_frame_index) {\n              for (size_t source_frame_index = 0; source_frame_index < dim;\n                   ++source_frame_index) {\n                gsl::at(noframe_frame_velocity, target_frame_index) +=\n                    noframe_jac.get(target_frame_index, source_frame_index) *\n                    frame_velocity.get(source_frame_index);\n              }\n            }\n          } else {\n            for (size_t target_frame_index = 0; target_frame_index < dim;\n                 ++target_frame_index) {\n              size_t source_frame_index = 0;\n              gsl::at(noframe_frame_velocity, target_frame_index) =\n                  noframe_jac.get(target_frame_index, source_frame_index) *\n                  frame_velocity.get(source_frame_index);\n              for (source_frame_index = 1; source_frame_index < dim;\n                   ++source_frame_index) {\n                gsl::at(noframe_frame_velocity, target_frame_index) +=\n                    noframe_jac.get(target_frame_index, source_frame_index) *\n                    frame_velocity.get(source_frame_index);\n              }\n            }\n          }\n          for (size_t target_frame_index = 0; target_frame_index < dim;\n               ++target_frame_index) {\n            using std::swap;\n            swap(gsl::at(noframe_frame_velocity, target_frame_index),\n                 frame_velocity.get(target_frame_index));\n          }\n        }\n      },\n      maps_);\n  return std::tuple<tnsr::I<T, dim, TargetFrame>,\n                    InverseJacobian<T, dim, SourceFrame, TargetFrame>,\n                    Jacobian<T, dim, SourceFrame, TargetFrame>,\n                    tnsr::I<T, dim, TargetFrame>>{\n      tnsr::I<T, dim, TargetFrame>(std::move(mapped_point)),\n      determinant_and_inverse(jac).second, std::move(jac),\n      std::move(frame_velocity)};\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\ntemplate <size_t... Is>\nauto CoordinateMap<SourceFrame, TargetFrame, Maps...>::get_to_grid_frame_impl(\n    std::index_sequence<Is...> /*meta*/) const\n    -> std::unique_ptr<CoordinateMapBase<SourceFrame, Frame::Grid, dim>> {\n  return std::make_unique<CoordinateMap<SourceFrame, Frame::Grid, Maps...>>(\n      std::get<Is>(maps_)...);\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nbool operator!=(const CoordinateMap<SourceFrame, TargetFrame, Maps...>& lhs,\n                const CoordinateMap<SourceFrame, TargetFrame, Maps...>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nauto make_coordinate_map(Maps&&... maps)\n    -> CoordinateMap<SourceFrame, TargetFrame, std::decay_t<Maps>...> {\n  return CoordinateMap<SourceFrame, TargetFrame, std::decay_t<Maps>...>(\n      std::forward<Maps>(maps)...);\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nauto make_coordinate_map_base(Maps&&... maps)\n    -> std::unique_ptr<CoordinateMapBase<\n        SourceFrame, TargetFrame,\n        CoordinateMap<SourceFrame, TargetFrame, std::decay_t<Maps>...>::dim>> {\n  return std::make_unique<\n      CoordinateMap<SourceFrame, TargetFrame, std::decay_t<Maps>...>>(\n      std::forward<Maps>(maps)...);\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename Arg0,\n          typename... Args>\nauto make_vector_coordinate_map_base(Arg0&& arg_0, Args&&... remaining_args)\n    -> std::vector<std::unique_ptr<\n        CoordinateMapBase<SourceFrame, TargetFrame, std::decay_t<Arg0>::dim>>> {\n  std::vector<std::unique_ptr<\n      CoordinateMapBase<SourceFrame, TargetFrame, std::decay_t<Arg0>::dim>>>\n      return_vector;\n  return_vector.reserve(sizeof...(Args) + 1);\n  return_vector.emplace_back(make_coordinate_map_base<SourceFrame, TargetFrame>(\n      std::forward<Arg0>(arg_0)));\n  EXPAND_PACK_LEFT_TO_RIGHT(return_vector.emplace_back(\n      make_coordinate_map_base<SourceFrame, TargetFrame>(\n          std::forward<Args>(remaining_args))));\n  return return_vector;\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim, typename Map,\n          typename... Maps>\nauto make_vector_coordinate_map_base(std::vector<Map> maps,\n                                     const Maps&... remaining_maps)\n    -> std::vector<\n        std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, Dim>>> {\n  std::vector<std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, Dim>>>\n      return_vector;\n  return_vector.reserve(sizeof...(Maps) + 1);\n  for (auto& map : maps) {\n    return_vector.emplace_back(\n        make_coordinate_map_base<SourceFrame, TargetFrame>(std::move(map),\n                                                           remaining_maps...));\n  }\n  return return_vector;\n}\n\ntemplate <typename NewMap, typename SourceFrame, typename TargetFrame,\n          typename... Maps, size_t... Is>\nCoordinateMap<SourceFrame, TargetFrame, Maps..., NewMap> push_back_impl(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...>&& old_map, NewMap new_map,\n    std::index_sequence<Is...> /*meta*/) {\n  return CoordinateMap<SourceFrame, TargetFrame, Maps..., NewMap>{\n      std::move(std::get<Is>(old_map.maps_))..., std::move(new_map)};\n}\n\ntemplate <typename... NewMaps, typename SourceFrame, typename TargetFrame,\n          typename... Maps, size_t... Is, size_t... Js>\nCoordinateMap<SourceFrame, TargetFrame, Maps..., NewMaps...> push_back_impl(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...>&& old_map,\n    CoordinateMap<SourceFrame, TargetFrame, NewMaps...> new_map,\n    std::index_sequence<Is...> /*meta*/, std::index_sequence<Js...> /*meta*/) {\n  return CoordinateMap<SourceFrame, TargetFrame, Maps..., NewMaps...>{\n      std::move(std::get<Is>(old_map.maps_))...,\n      std::move(std::get<Js>(new_map.maps_))...};\n}\n\ntemplate <typename NewMap, typename SourceFrame, typename TargetFrame,\n          typename... Maps, size_t... Is>\nCoordinateMap<SourceFrame, TargetFrame, NewMap, Maps...> push_front_impl(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...>&& old_map, NewMap new_map,\n    std::index_sequence<Is...> /*meta*/) {\n  return CoordinateMap<SourceFrame, TargetFrame, Maps..., NewMap>{\n      std::move(new_map), std::move(std::get<Is>(old_map.maps_))...};\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps,\n          typename NewMap>\nCoordinateMap<SourceFrame, TargetFrame, Maps..., NewMap> push_back(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...> old_map, NewMap new_map) {\n  return push_back_impl(std::move(old_map), std::move(new_map),\n                        std::make_index_sequence<sizeof...(Maps)>{});\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps,\n          typename... NewMaps>\nCoordinateMap<SourceFrame, TargetFrame, Maps..., NewMaps...> push_back(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...> old_map,\n    CoordinateMap<SourceFrame, TargetFrame, NewMaps...> new_map) {\n  return push_back_impl(std::move(old_map), std::move(new_map),\n                        std::make_index_sequence<sizeof...(Maps)>{},\n                        std::make_index_sequence<sizeof...(NewMaps)>{});\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps,\n          typename NewMap>\nCoordinateMap<SourceFrame, TargetFrame, NewMap, Maps...> push_front(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...> old_map, NewMap new_map) {\n  return push_front_impl(std::move(old_map), std::move(new_map),\n                         std::make_index_sequence<sizeof...(Maps)>{});\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps,\n          typename... NewMaps>\nCoordinateMap<SourceFrame, TargetFrame, NewMaps..., Maps...> push_front(\n    CoordinateMap<SourceFrame, TargetFrame, Maps...> old_map,\n    CoordinateMap<SourceFrame, TargetFrame, NewMaps...> new_map) {\n  return push_back_impl(std::move(new_map), std::move(old_map),\n                        std::make_index_sequence<sizeof...(NewMaps)>{},\n                        std::make_index_sequence<sizeof...(Maps)>{});\n}\n}  // namespace domain\n/// \\endcond\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CoordinateMapHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace domain {\nnamespace CoordinateMap_detail {\n/// @{\n/// Call the map passing in the time and FunctionsOfTime if the map is\n/// time-dependent\ntemplate <typename T, size_t Dim, typename Map>\nvoid apply_map(\n    const gsl::not_null<std::array<T, Dim>*> t_map_point, const Map& the_map,\n    const double /*t*/,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/,\n    const std::false_type /*is_time_independent*/) {\n  if (LIKELY(not the_map.is_identity())) {\n    *t_map_point = the_map(*t_map_point);\n  }\n}\n\ntemplate <typename T, size_t Dim, typename Map>\nvoid apply_map(\n    const gsl::not_null<std::array<T, Dim>*> t_map_point, const Map& the_map,\n    const double t,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::true_type\n    /*is_time_dependent*/) {\n  ASSERT(not functions_of_time.empty(),\n         \"A function of time must be present if the maps are time-dependent.\");\n  ASSERT(\n      [t]() {\n        const ScopedFpeState disable_fpes(false);\n        return not std::isnan(t);\n      }(),\n      \"The time must not be NaN for time-dependent maps.\");\n  *t_map_point = the_map(*t_map_point, t, functions_of_time);\n}\n\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_map(\n    const Map& the_map, const std::array<T, Dim>& source_points,\n    const double /*t*/,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/,\n    const std::false_type /*is_time_independent*/) {\n  if (LIKELY(not the_map.is_identity())) {\n    return the_map(source_points);\n  }\n  std::decay_t<decltype(the_map(source_points))> result{};\n  for (size_t i = 0; i < result.size(); ++i) {\n    gsl::at(result, i) = gsl::at(source_points, i);\n  }\n  return result;\n}\n\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_map(\n    const Map& the_map, const std::array<T, Dim>& source_points, const double t,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::true_type\n    /*is_time_dependent*/) {\n  // Note: We don't forward to the return-by-not-null version to avoid\n  // additional allocations of the target points array. That is, we would\n  // allocate the target points array once here, and then again inside the call\n  // to the coordinate map.\n  ASSERT(not functions_of_time.empty(),\n         \"A function of time must be present if the maps are time-dependent.\");\n  ASSERT(\n      [t]() {\n        const ScopedFpeState disable_fpes(false);\n        return not std::isnan(t);\n      }(),\n      \"The time must not be NaN for time-dependent maps.\");\n  return the_map(source_points, t, functions_of_time);\n}\n/// @}\n\n/// @{\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_inverse_map(\n    const Map& the_map, const std::array<T, Dim>& target_points,\n    const double /*t*/,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/,\n    const std::false_type /*is_time_independent*/) {\n  if (LIKELY(not the_map.is_identity())) {\n    return the_map.inverse(target_points);\n  }\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  std::decay_t<decltype(the_map.inverse(target_points))> result{\n      std::array<UnwrappedT, Dim>{}};\n  for (size_t i = 0; i < target_points.size(); ++i) {\n    gsl::at(*result, i) = gsl::at(target_points, i);\n  }\n  return result;\n}\n\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_inverse_map(\n    const Map& the_map, const std::array<T, Dim>& target_points, const double t,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::true_type\n    /*is_time_dependent*/) {\n  ASSERT(not functions_of_time.empty(),\n         \"A function of time must be present if the maps are time-dependent.\");\n  ASSERT(\n      [t]() {\n        const ScopedFpeState disable_fpes(false);\n        return not std::isnan(t);\n      }(),\n      \"The time must not be NaN for time-dependent maps.\");\n  return the_map.inverse(target_points, t, functions_of_time);\n}\n/// @}\n\n/// @{\n/// Compute the frame velocity\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_frame_velocity(\n    const Map& /*the_map*/, const std::array<T, Dim>& source_points,\n    const double /*t*/,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/,\n    const std::false_type /*is_time_independent*/) {\n  return make_array<Map::dim, tt::remove_cvref_wrap_t<T>>(\n      make_with_value<tt::remove_cvref_wrap_t<T>>(\n          dereference_wrapper(source_points[0]), 0.0));\n}\n\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_frame_velocity(\n    const Map& the_map, const std::array<T, Dim>& source_points, const double t,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::true_type\n    /*is_time_dependent*/) {\n  ASSERT(not functions_of_time.empty(),\n         \"A function of time must be present if the maps are time-dependent.\");\n  ASSERT(\n      [t]() {\n        const ScopedFpeState disable_fpes(false);\n        return not std::isnan(t);\n      }(),\n      \"The time must not be NaN for time-dependent maps.\");\n  return the_map.frame_velocity(source_points, t, functions_of_time);\n}\n/// @}\n\n/// @{\n/// Compute the Jacobian\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_jacobian(\n    const Map& the_map, const std::array<T, Dim>& source_points,\n    const double /*t*/,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/,\n    const std::false_type /*is_time_independent*/) {\n  if (LIKELY(not the_map.is_identity())) {\n    return the_map.jacobian(source_points);\n  }\n  return identity<Dim>(dereference_wrapper(source_points[0]));\n}\n\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_jacobian(\n    const Map& the_map, const std::array<T, Dim>& source_points, const double t,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::true_type\n    /*is_time_dependent*/) {\n  ASSERT(not functions_of_time.empty(),\n         \"A function of time must be present if the maps are time-dependent.\");\n  ASSERT(\n      [t]() {\n        const ScopedFpeState disable_fpes(false);\n        return not std::isnan(t);\n      }(),\n      \"The time must not be NaN for time-dependent maps.\");\n  if (LIKELY(not the_map.is_identity())) {\n    return the_map.jacobian(source_points, t, functions_of_time);\n  }\n  return identity<Dim>(dereference_wrapper(source_points[0]));\n}\n/// @}\n\n/// @{\n/// Compute the Jacobian\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_inverse_jacobian(\n    const Map& the_map, const std::array<T, Dim>& source_points,\n    const double /*t*/,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/,\n    const std::false_type /*is_time_independent*/) {\n  if (LIKELY(not the_map.is_identity())) {\n    return the_map.inv_jacobian(source_points);\n  }\n  return identity<Dim>(dereference_wrapper(source_points[0]));\n}\n\ntemplate <typename T, size_t Dim, typename Map>\nauto apply_inverse_jacobian(\n    const Map& the_map, const std::array<T, Dim>& source_points, const double t,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::true_type\n    /*is_time_dependent*/) {\n  ASSERT(not functions_of_time.empty(),\n         \"A function of time must be present if the maps are time-dependent.\");\n  ASSERT(\n      [t]() {\n        const ScopedFpeState disable_fpes(false);\n        return not std::isnan(t);\n      }(),\n      \"The time must not be NaN for time-dependent maps.\");\n  if (LIKELY(not the_map.is_identity())) {\n    return the_map.inv_jacobian(source_points, t, functions_of_time);\n  }\n  return identity<Dim>(dereference_wrapper(source_points[0]));\n}\n/// @}\n}  // namespace CoordinateMap_detail\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/CylindricalEndcap.hpp\"\n\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedEndcap.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nCylindricalEndcap::CylindricalEndcap(const std::array<double, 3>& center_one,\n                                     const std::array<double, 3>& center_two,\n                                     const std::array<double, 3>& proj_center,\n                                     double radius_one, double radius_two,\n                                     double z_plane)\n    : impl_(\n          center_two, proj_center, radius_two,\n          [&center_one, &center_two, &radius_one, &radius_two]() {\n            const double dist_spheres =\n                sqrt(square(center_one[0] - center_two[0]) +\n                     square(center_one[1] - center_two[1]) +\n                     square(center_one[2] - center_two[2]));\n            // If sphere 1 is contained in sphere 2, then the source (sphere 1)\n            // is always between the projection point and the target (sphere 2).\n            // Otherwise, if sphere 2 is contained in sphere 1, then the source\n            // (sphere 1) is not between the projection point and the target\n            // (sphere 2).\n            //\n            // Note that below we ASSERT that sphere 1 is contained in sphere 2\n            // or vice versa.\n            return dist_spheres + radius_one < radius_two;\n          }(),\n          FocallyLiftedInnerMaps::Endcap(center_one, radius_one, z_plane)) {\n#ifdef SPECTRE_DEBUG\n  // There are two types of sanity checks here on the map parameters.\n  // 1) ASSERTS that guarantee that the map is invertible.\n  // 2) ASSERTS that guarantee that the map parameters fall within\n  //    the range tested by the unit tests (which is the range in which\n  //    the map is expected to be used).\n  //\n  // There are two reasons why 1) and 2) are not the same:\n  //\n  // a) It is possible to choose parameters such that the map is\n  //    invertible but the resulting geometry has very sharp angles,\n  //    very large or ill-conditioned Jacobians, or both.  We want to\n  //    avoid such cases.\n  // b) We do not want to waste effort testing the map for parameters\n  //    that we don't expect to be used.  For example, we demand here\n  //    that sphere_one is contained within sphere_two or vice versa,\n  //    but the map should still be valid for some choices of\n  //    parameters where sphere_one and sphere_two are disjoint;\n  //    allowing those parameter choices would involve much more\n  //    complicated logic to determine whether the map produces shapes\n  //    with sharp angles or large jacobians, and it would involve\n  //    more complicated unit tests to cover those possibilities.\n\n  // First test for invertibility.\n\n  // Consider the intersection of sphere_one and the plane formed by\n  // z_plane.  Call it circle_one.  Consider the cone with apex\n  // center_one that intersects sphere_one on circle_one. This cone\n  // has an opening angle 2*theta.  Call the cone 'cone_one'.\n  const double cos_theta = (z_plane - center_one[2]) / radius_one;\n\n  // Now consider a different cone, called 'invertibility_cone', constructed so\n  // that invertibility_cone and cone_one intersect each other on circle_one at\n  // right angles.  The apex of invertibility_cone is invertibility_cone_apex,\n  // defined as follows:\n  const std::array<double, 3> invertibility_cone_apex = {\n      center_one[0], center_one[1], center_one[2] + radius_one / cos_theta};\n  // invertibility_cone opens in the -z direction with opening angle\n  // 2*(pi/2-theta). invertibility_cone_apex is labeled 'S' in the 'Allowed\n  // Region' figure in CylindricalEndcap.hpp\n\n  // A necessary condition for invertibility is that proj_center lies\n  // either inside of invertibility_cone or inside of the reflection of\n  // invertibility_cone (a cone with apex invertibility_cone_apex but opening in\n  // the +x direction with opening angle 2*(pi/2-theta)).\n\n  // Determine the angle of proj_center relative to invertibility_cone_apex.\n  // Call this angle alpha.\n  const double dist_cone_proj =\n      sqrt(square(invertibility_cone_apex[0] - proj_center[0]) +\n           square(invertibility_cone_apex[1] - proj_center[1]) +\n           square(invertibility_cone_apex[2] - proj_center[2]));\n  const double cos_alpha =\n      (invertibility_cone_apex[2] - proj_center[2]) / dist_cone_proj;\n\n  // Now make sure that alpha < pi/2-theta.\n  // The cone on either side of invertibility_cone_apex is ok, so we use abs\n  // below.\n  ASSERT(acos(abs(cos_alpha)) < abs(asin(cos_theta)),\n         \"The arguments passed into the CylindricalEndcap constructor \"\n         \"yield a noninvertible map.\");\n\n  // Another necessary condition for invertibility is that proj_center\n  // cannot lie between sphere_one and invertibility_cone_apex.\n  const double proj_radius_one_squared =\n      square(center_one[0] - proj_center[0]) +\n      square(center_one[1] - proj_center[1]) +\n      square(center_one[2] - proj_center[2]);\n  ASSERT(proj_center[2] > invertibility_cone_apex[2] or\n             proj_center[2] < z_plane or\n             proj_radius_one_squared < square(radius_one),\n         \"The arguments passed into the CylindricalEndcap constructor \"\n         \"yield a noninvertible map.\");\n\n  // Other sanity checks that may be relaxed if there is more logic\n  // added and more unit tests to test these cases.\n  // We put these sanity checks in a separate place precisely because\n  // they may be relaxed in the future, whereas the above sanity checks\n  // are hard limits that shouldn't be touched.\n\n  ASSERT(proj_center[2] < z_plane,\n         \"The map hasn't been tested for the \"\n         \"case where proj_center is at larger z than the z_plane. \"\n         \"The map may still be invertible, but further \"\n         \"testing would be needed to ensure that jacobians are not \"\n         \"ill-conditioned. Here z_plane = \"\n             << z_plane << \" and proj_center[2] = \" << proj_center[2]);\n\n  ASSERT(abs(cos_theta) <= 0.95,\n         \"z_plane is too far from the center of sphere_one. \"\n             << \"cos_theta = \" << cos_theta\n             << \". If |cos_theta| > 1 the map is singular.  If 0.95 < \"\n                \"|cos_theta| < 1 then the map is not singular, but the \"\n                \"jacobians are likely to be large and the map has not been \"\n                \"tested for these parameters.\");\n  ASSERT(\n      abs(cos_theta) >= 0.15,\n      \"z_plane is too close to the center of sphere_one. \"\n          << \"cos_theta = \" << cos_theta\n          << \". The map is not singular, but the jacobians are likely to be \"\n             \"large \"\n             \"and the map has not been tested for this choice of parameters.\");\n\n  const double dist_spheres = sqrt(square(center_one[0] - center_two[0]) +\n                                   square(center_one[1] - center_two[1]) +\n                                   square(center_one[2] - center_two[2]));\n  ASSERT(dist_spheres + radius_one < radius_two or\n             dist_spheres + radius_two < radius_one,\n         \"The map has been tested only for the case when \"\n         \"sphere_one is contained inside sphere_two or sphere_two is contained \"\n         \"inside sphere_one. radius_one = \"\n             << radius_one << \", radius_two = \" << radius_two\n             << \", dist_spheres = \" << dist_spheres);\n\n  const double proj_radius_two_squared =\n      square(center_two[0] - proj_center[0]) +\n      square(center_two[1] - proj_center[1]) +\n      square(center_two[2] - proj_center[2]);\n  ASSERT(proj_radius_two_squared < square(radius_two),\n         \"The map has been tested only for the case when \"\n         \"proj_center is contained inside sphere_two. We have \"\n             << proj_radius_two_squared << \" vs \" << square(radius_two)\n             << \", diff = \" << proj_radius_two_squared - square(radius_two));\n\n  if (dist_spheres + radius_two < radius_one) {\n    // sphere_two is contained in sphere_one\n\n    // Check if we are too close to singular.  We do this check\n    // separately from the earlier similar check because this check\n    // may be changed in the future based on further testing and\n    // further logic that may be added (for example we may want to\n    // change the 0.85 to some other number), but the previous check\n    // (without the 0.85) is a hard limit that should always be\n    // obeyed.  We also use a different threshold for sphere_two\n    // contained inside sphere_one than we do for sphere_one contained\n    // inside sphere_two.\n    ASSERT(acos(abs(cos_alpha)) < abs(0.85 * asin(cos_theta)),\n           \"Parameters are close to where the map becomes non-invertible. \"\n           \"acos(abs(cos_alpha)) = \"\n               << acos(abs(cos_alpha)) << \" and abs(asin(cos_theta)) = \"\n               << abs(asin(cos_theta)) << \", so the ratio is \"\n               << acos(abs(cos_alpha)) / abs(asin(cos_theta))\n               << \". The map has not been tested for this \"\n                  \"case.\");\n\n    // We keep this as a separate condition because we may change the\n    // number 0.85 in the future if we ever have reason to do so.\n    ASSERT(radius_two <= 0.85 * (radius_one - dist_spheres) and\n               radius_two >= 0.1 * radius_one,\n           \"If sphere_two is contained in sphere_one, the \"\n           \"map has been tested only for the case when sphere_two is not \"\n           \"too small or two large. Here radius_two=\"\n               << radius_two << \", radius_one=\" << radius_one << \", distance=\"\n               << dist_spheres << \", and the requirement is that \"\n               << 0.85 * (radius_one - dist_spheres) - radius_two\n               << \" is positive and that \" << radius_two - 0.1 * radius_one\n               << \" is positive.\");\n\n    // We keep this as a separate condition because we may change the\n    // number 0.1 in the future if we have reason to do so.\n    ASSERT(\n        proj_radius_two_squared <= square(0.1 * radius_two),\n        \"The map has been tested only for the case when \"\n        \"proj_center is sufficiently contained inside sphere_two. We have \"\n            << proj_radius_two_squared << \" vs \" << square(0.1 * radius_two)\n            << \", diff = \" << proj_radius_two_squared - square(0.1 * radius_two)\n            << \". Here center_two=\" << center_two << \", proj_center=\"\n            << proj_center << \", radius_two=\" << radius_two);\n  } else {\n    // sphere_one is contained in sphere_two.\n\n    // Check if we are too close to singular.  We do this check\n    // separately from the earlier similar check because this check\n    // may be changed in the future based on further testing and\n    // further logic that may be added (for example we may want to\n    // change the 0.75 to some other number), but the previous check\n    // (without the 0.75) is a hard limit that should always be\n    // obeyed.  We also use a different threshold for sphere_two\n    // contained inside sphere_one than we do for sphere_one contained\n    // inside sphere_two.\n    ASSERT(acos(abs(cos_alpha)) < abs(0.75 * asin(cos_theta)),\n           \"Parameters are close to where the map becomes non-invertible. \"\n           \"acos(abs(cos_alpha)) = \"\n               << acos(abs(cos_alpha)) << \" and abs(asin(cos_theta)) = \"\n               << abs(asin(cos_theta)) << \", so the ratio is \"\n               << acos(abs(cos_alpha)) / abs(asin(cos_theta))\n               << \". The map has not been tested for this \"\n                  \"case.\");\n\n    ASSERT(1.01 * (dist_spheres + radius_one) <= radius_two,\n           \"The map has been tested only for the case when \"\n           \"sphere_one is suficiently contained inside sphere_two, without the \"\n           \"two spheres almost touching. \"\n               << radius_one << \", radius_two = \" << radius_two\n               << \", dist_spheres = \" << dist_spheres);\n\n    // Check if opening angle is small enough.  (Note that this check\n    // is not needed if sphere_two is contained in sphere_one; in that\n    // case, the condition that proj_center is contained in sphere_two\n    // was found to be sufficient for preventing large map errors.)\n    const double max_opening_angle = M_PI / 3.0;\n    const double max_proj_center_z =\n        z_plane -\n        radius_one * sqrt(1.0 - square(cos_theta)) / tan(max_opening_angle);\n    ASSERT(proj_center[2] < max_proj_center_z,\n           \"proj_center \" << proj_center[2] << \" is too close to z_plane \"\n                          << z_plane\n                          << \". max_proj_center_z = \" << max_proj_center_z\n                          << \" and radius_one = \" << radius_one\n                          << \". The map has not been tested for this case.\");\n    const double tan_beta = sqrt(square(center_one[0] - proj_center[0]) +\n                                 square(center_one[1] - proj_center[1])) /\n                            (max_proj_center_z - proj_center[2]);\n    ASSERT(tan_beta < tan(max_opening_angle),\n           \"opening angle is too large. The map has not \"\n           \"been tested for this case.\");\n  }\n#endif\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> CylindricalEndcap::operator()(\n    const std::array<T, 3>& source_coords) const {\n  return impl_.operator()(source_coords);\n}\n\nstd::optional<std::array<double, 3>> CylindricalEndcap::inverse(\n    const std::array<double, 3>& target_coords) const {\n  return impl_.inverse(target_coords);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nCylindricalEndcap::jacobian(const std::array<T, 3>& source_coords) const {\n  return impl_.jacobian(source_coords);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nCylindricalEndcap::inv_jacobian(const std::array<T, 3>& source_coords) const {\n  return impl_.inv_jacobian(source_coords);\n}\n\nvoid CylindricalEndcap::pup(PUP::er& p) { p | impl_; }\n\nbool operator==(const CylindricalEndcap& lhs, const CylindricalEndcap& rhs) {\n  return lhs.impl_ == rhs.impl_;\n}\nbool operator!=(const CylindricalEndcap& lhs, const CylindricalEndcap& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  CylindricalEndcap::operator()(                                               \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  CylindricalEndcap::jacobian(const std::array<DTYPE(data), 3>& source_coords) \\\n      const;                                                                   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  CylindricalEndcap::inv_jacobian(                                             \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalEndcap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class CylindricalEndcap.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedEndcap.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from 3D unit right cylinder to a volume that connects\n *  portions of two spherical surfaces.\n *\n * \\image html CylindricalEndcap.svg \"A cylinder maps to the shaded region.\"\n *\n * \\details Consider two spheres with centers \\f$C_1\\f$ and \\f$C_2\\f$,\n * and radii \\f$R_1\\f$ and \\f$R_2\\f$. Let sphere 1 be intersected by a\n * plane normal to the \\f$z\\f$ axis and located at \\f$z = z_\\mathrm{P}\\f$.\n * Also let there be a projection point \\f$P\\f$.\n *\n * CylindricalEndcap maps a 3D unit right cylinder (with coordinates\n * \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that \\f$-1\\leq\\bar{z}\\leq 1\\f$\n * and \\f$\\bar{x}^2+\\bar{y}^2 \\leq 1\\f$) to the shaded area\n * in the figure above (with coordinates \\f$(x,y,z)\\f$).  The \"bottom\"\n * of the cylinder \\f$\\bar{z}=-1\\f$ is mapped to the portion of sphere\n * 1 that has \\f$z \\geq z_\\mathrm{P}\\f$.  Curves of constant\n * \\f$(\\bar{x},\\bar{y})\\f$ are mapped to portions of lines that pass\n * through \\f$P\\f$. Along each of these curves, \\f$\\bar{z}=-1\\f$ is\n * mapped to a point on sphere 1 and \\f$\\bar{z}=+1\\f$ is mapped to a\n * point on sphere 2.\n *\n * Note that Sphere 1 and Sphere 2 are not equivalent, because the\n * mapped portion of Sphere 1 is bounded by a plane of constant\n * \\f$z\\f$ but the mapped portion of Sphere 2 is not (except for\n * special choices of \\f$C_1\\f$, \\f$C_2\\f$, and \\f$P\\f$).\n *\n * CylindricalEndcap is intended to be composed with `Wedge<2>` maps to\n * construct a portion of a cylindrical domain for a binary system.\n *\n * CylindricalEndcap is described briefly in the Appendix of\n * \\cite Buchman:2012dw.\n * CylindricalEndcap is used to construct the blocks labeled 'CA\n * wedge', 'EA wedge', 'CB wedge', 'EE wedge', and 'EB wedge' in\n * Figure 20 of that paper.  Note that 'CA wedge', 'CB wedge', and\n * 'EE wedge' have Sphere 1 contained in Sphere 2, and 'EA wedge'\n * and 'EB wedge' have Sphere 2 contained in Sphere 1.\n *\n * CylindricalEndcap is implemented using `FocallyLiftedMap`\n * and `FocallyLiftedInnerMaps::Endcap`; see those classes for details.\n *\n * ### Restrictions on map parameters.\n *\n * We demand that either Sphere 1 is fully contained inside Sphere 2, or\n * that Sphere 2 is fully contained inside Sphere 1. It is\n * possible to construct a valid map without this assumption, but the\n * assumption simplifies the code, and the expected use cases obey\n * this restriction.\n *\n * We also demand that \\f$z_\\mathrm{P} > C_1^2\\f$, that is, the plane\n * in the above and below figures lies to the right of \\f$C_1^2\\f$.\n * This restriction not strictly necessary but is made for simplicity.\n *\n * The map is invertible only for some choices of the projection point\n * \\f$P\\f$.  Given the above restrictions, the allowed values of\n * \\f$P\\f$ are illustrated by the following diagram:\n *\n * \\image html CylindricalEndcap_Allowed.svg \"Allowed region for P.\" width=75%\n *\n * The plane \\f$z=z_\\mathrm{P}\\f$ intersects sphere 1 on a circle. The\n * cone with apex \\f$C_1\\f$ that intersects that circle has opening\n * angle \\f$2\\theta\\f$ as shown in the above figure. Construct another\n * cone, the \"invertibility cone\", with apex \\f$S\\f$ chosen such that\n * the two cones intersect at right angles on the circle; thus the\n * opening angle of the invertibility cone is \\f$\\pi-2\\theta\\f$.  A\n * necessary condition for invertibility is that the projection point\n * \\f$P\\f$ lies inside the invertibility cone, but not between \\f$S\\f$\n * and sphere 1.  (If \\f$P\\f$ does not obey this condition, then from the\n * diagram one can find at least one line through \\f$P\\f$\n * that twice intersects the surface of sphere 1 with \\f$z>z_\\mathrm{P}\\f$;\n * the inverse map is thus double-valued at those intersection points.)\n * Placing the projection point \\f$P\\f$ to the\n * right of \\f$S\\f$ (but inside the invertibility cone) is ok for\n * invertibility.\n *\n * In addition to invertibility and the two additional restrictions\n * already mentioned above, we demand a few more restrictions on the\n * map parameters to simplify the logic for the expected use cases and\n * to ensure that jacobians do not get too large. The numbers in the\n * restrictions below were chosen empirically so that the unit tests pass\n * with errors less than 100 times machine roundoff; we do not expect to\n * run into these restrictions in normal usage. We demand:\n *\n * - \\f$P\\f$ is not too close to the edge of the invertibility cone.\n *   Here we demand that the angle between \\f$P\\f$ and \\f$S\\f$\n *   is less than \\f$0.85 (\\pi/2-\\theta)\\f$ (note that if this\n *   angle is exactly \\f$\\pi/2-\\theta\\f$ it is exactly on the\n *   invertibility cone); The 0.85 was chosen empirically based on\n *   unit tests.\n * - \\f$z_\\mathrm{P}\\f$ is not too close to the center or the edge of sphere 1.\n *   Here we demand that \\f$0.15 \\leq \\cos(\\theta) \\leq 0.95\\f$, where\n *   the values 0.15 and 0.95 were chosen empirically based on unit tests.\n * - \\f$P\\f$ is contained in sphere 2.\n * - If sphere 2 is contained in sphere 1, then\n *   - \\f$0.1 R_1 \\leq R_2 \\leq 0.85 (R_1 - |C_1-C_2|)\\f$,\n *     This prevents the two spheres from having a very narrow space between\n *     them, and it prevents sphere 2 from being very small.\n *   - \\f$|P - C_2| < 0.1 R_2\\f$, i.e. \\f$P\\f$ is near the center of sphere 2.\n * - If sphere 1 is contained in sphere 2, then\n *   - \\f$ R_2 \\geq 1.01 (R_1 + |C_1-C_2|)\\f$, where the 1.01\n *     prevents the spheres from (barely) touching.\n *   - If a line segment is drawn between \\f$P\\f$ and any point on the\n *     intersection circle (the circle where sphere 1 intersects the\n *     plane \\f$z=z_\\mathrm{P}\\f$), the angle between the line segment\n *     and the z-axis is smaller than \\f$\\pi/3\\f$.\n */\nclass CylindricalEndcap {\n public:\n  static constexpr size_t dim = 3;\n  CylindricalEndcap(const std::array<double, 3>& center_one,\n                    const std::array<double, 3>& center_two,\n                    const std::array<double, 3>& proj_center, double radius_one,\n                    double radius_two, double z_plane);\n\n  CylindricalEndcap() = default;\n  ~CylindricalEndcap() = default;\n  CylindricalEndcap(CylindricalEndcap&&) = default;\n  CylindricalEndcap(const CylindricalEndcap&) = default;\n  CylindricalEndcap& operator=(const CylindricalEndcap&) = default;\n  CylindricalEndcap& operator=(CylindricalEndcap&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const CylindricalEndcap& lhs,\n                         const CylindricalEndcap& rhs);\n  FocallyLiftedMap<FocallyLiftedInnerMaps::Endcap> impl_;\n};\nbool operator!=(const CylindricalEndcap& lhs, const CylindricalEndcap& rhs);\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalEndcapHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"CylindricalEndcapHelpers.hpp\"\n\n#include <cmath>\n#include <limits>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace cylindrical_endcap_helpers {\n\ndouble sin_ax_over_x(const double x, const double a) {\n  // sin(ax)/x returns the right thing except if x is zero, so\n  // we need to treat only that case as special.\n  return x == 0.0 ? a : sin(a * x) / x;\n}\nDataVector sin_ax_over_x(const DataVector& x, const double a) {\n  DataVector result(x);\n  for (size_t i = 0; i < x.size(); ++i) {\n    result[i] = sin_ax_over_x(x[i], a);\n  }\n  return result;\n}\ndouble one_over_x_d_sin_ax_over_x(const double x, const double a) {\n  // Evaluates (1/x) d/dx [ sin(ax)/x ], which is the quantity that\n  // approaches a finite limit as x approaches zero.\n  //\n  // Here we need to worry about roundoff. Note that we can expand\n  // (1/x) d/dx [ sin(ax)/x ] =\n  // a/x^2 [ 1 - 1 - 2(ax)^2/3! + 4(ax)^4/5! - 6(ax)^6/7! + ...],\n  // where I kept the \"1 - 1\" above as a reminder that when evaluating\n  // this function directly as (a * cos(ax) - sin(ax) / x) / square(x)\n  // there can be significant roundoff because of the \"1\" in each of the\n  // two terms that are subtracted.\n  //\n  // The relative error in the above expression, if evaluated directly,\n  // is 3*eps/(ax)^2 where eps is machine epsilon.  (That expression comes\n  // from replacing \"1 - 1\" with eps and noting that the correct answer\n  // is the 2(ax)^2/3! term and eps is an error contribution).\n  // This means the error is 100% if (ax)^2 is 3*eps.\n  //\n  // The solution is to evaluate the series if (ax) is small enough.\n  // Suppose we keep up to and including the (ax)^(2n) term in the\n  // series.  Then the series is accurate if the (ax)^{2n+2} term (the\n  // next term in the series) is small, i.e. if\n  // (2n+2)(ax)^{2n+2}/(2n+3)! < eps.\n  //\n  // For the worst case of (2n+2)(ax)^{2n+2}/(2n+3)! == eps, the direct\n  // evaluation still has a relative error of 3*eps/(ax)^2, which evaluates to\n  // error = 3*eps* eps^{-1/(n+1)} * ((2n+2)/(2n+3)!)^{1/(n+1)}.\n  // This can be rewritten as\n  // error = 3 * [eps^n*((2n+2)/(2n+3)!)]^{1/(n+1)}.\n  //\n  // For certain values of n:\n  // n=1    error=3*sqrt(eps/30)               ~ 5e-9\n  // n=2    error=3*(eps^2/840)^(1/3)          ~ 7e-12\n  // n=3    error=3*(eps^3/45360)^(1/4)        ~ 2e-13\n  // n=4    error=3*(eps^4/3991680)^(1/5)      ~ 2e-14\n  // n=5    error=3*(eps^5/518918400)^(1/6)    ~ 5e-15\n  // n=6    error=3*(eps^6/93405312000)^(1/7)  ~ 1e-15\n  //\n  // We gain less and less with each order.\n  //\n  // So here we choose n=3.\n  // Then the series above can be rewritten\n  // 1/x d/dx [ sin(ax)/x ] = a/x^2 [- 2(ax)^2/3! + 4(ax)^4/5! - 6(ax)^6/7!]\n  //                        = -a^3/3 [ 1 - 4*3*(ax)^2/5! + 6*3*(ax)^4/7!]\n  const double ax = a * x;\n  return pow<8>(ax) < 45360.0 * std::numeric_limits<double>::epsilon()\n             ? (-cube(a) / 3.0) *\n                   (1.0 + square(ax) * (-0.1 + square(ax) / 280.0))\n             : (a * cos(ax) - sin(ax) / x) / square(x);\n}\nDataVector one_over_x_d_sin_ax_over_x(const DataVector& x, const double a) {\n  DataVector result(x);\n  for (size_t i = 0; i < x.size(); ++i) {\n    result[i] = one_over_x_d_sin_ax_over_x(x[i], a);\n  }\n  return result;\n}\n\n}  // namespace cylindrical_endcap_helpers\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalEndcapHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n\n/// Functions used in more than one cylindrical_endcap map\nnamespace cylindrical_endcap_helpers {\n/// @{\n/// Returns \\f$\\sin(ax)/x\\f$\ndouble sin_ax_over_x(const double x, const double a);\nDataVector sin_ax_over_x(const DataVector& x, const double a);\n/// @}\n\n/// @{\n/// Returns \\f$\\frac{1}{x} \\frac{d}{dx}\\left( \\frac{\\sin(ax)}{x} \\right)\\f$,\n/// which approaches a finite limit as \\f$x\\f$ approaches zero.\ndouble one_over_x_d_sin_ax_over_x(const double x, const double a);\nDataVector one_over_x_d_sin_ax_over_x(const DataVector& x,\n                                      const double a);\n/// @}\n}  // namespace cylindrical_endcap_helpers\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalFlatEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/CylindricalFlatEndcap.hpp\"\n\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedFlatEndcap.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nCylindricalFlatEndcap::CylindricalFlatEndcap(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two,\n    const std::array<double, 3>& proj_center, double radius_one,\n    double radius_two)\n    : impl_(center_two, proj_center, radius_two, false,\n            FocallyLiftedInnerMaps::FlatEndcap(center_one, radius_one)) {\n#ifdef SPECTRE_DEBUG\n  // There are two types of sanity checks here on the map parameters.\n  // 1) ASSERTS that guarantee that the map is invertible.\n  // 2) ASSERTS that guarantee that the map parameters fall within\n  //    the range tested by the unit tests (which is the range in which\n  //    the map is expected to be used).\n  //\n  // There are two reasons why 1) and 2) are not the same:\n  //\n  // a) It is possible to choose parameters such that the map is\n  //    invertible but the resulting geometry has very sharp angles,\n  //    very large or ill-conditioned Jacobians, or both.  We want to\n  //    avoid such cases.\n  // b) We do not want to waste effort testing the map for parameters\n  //    that we don't expect to be used.\n  ASSERT(center_one[2] <= center_two[2] - 1.05 * radius_two and\n             center_one[2] >= center_two[2] - 6.0 * radius_two,\n         \"The map has only been tested for the case when the plane containing \"\n         \"the circle is below the sphere, by an amount at least 5% of the \"\n         \"sphere radius but not more than 5 times the sphere radius.\");\n\n  const double dist_proj = sqrt(square(center_two[0] - proj_center[0]) +\n                                square(center_two[1] - proj_center[1]) +\n                                square(center_two[2] - proj_center[2]));\n  ASSERT(dist_proj <= 0.95 * radius_two,\n         \"The map has been tested only for the case \"\n         \"when proj_center is contained inside the sphere, and no closer than \"\n         \"95% of the way to the surface of the sphere\");\n\n  ASSERT(radius_one / radius_two <= 10.0 and radius_two / radius_one <= 10.0,\n         \"The map has been tested only for the case when the ratio of \"\n         \"radius_one to radius_two is between 10 and 1/10\");\n\n  ASSERT(\n      abs(center_one[0] - center_two[0]) <= radius_one + radius_two and\n          abs(center_one[1] - center_two[1]) <= radius_one + radius_two,\n      \"The map has been tested only when the center of the sphere and the \"\n      \"center of the circle are not too far apart in the x and y directions\");\n#endif\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> CylindricalFlatEndcap::operator()(\n    const std::array<T, 3>& source_coords) const {\n  return impl_.operator()(source_coords);\n}\n\nstd::optional<std::array<double, 3>> CylindricalFlatEndcap::inverse(\n    const std::array<double, 3>& target_coords) const {\n  return impl_.inverse(target_coords);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nCylindricalFlatEndcap::jacobian(const std::array<T, 3>& source_coords) const {\n  return impl_.jacobian(source_coords);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nCylindricalFlatEndcap::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  return impl_.inv_jacobian(source_coords);\n}\n\nvoid CylindricalFlatEndcap::pup(PUP::er& p) { p | impl_; }\n\nbool operator==(const CylindricalFlatEndcap& lhs,\n                const CylindricalFlatEndcap& rhs) {\n  return lhs.impl_ == rhs.impl_;\n}\nbool operator!=(const CylindricalFlatEndcap& lhs,\n                const CylindricalFlatEndcap& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  CylindricalFlatEndcap::operator()(                                         \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  CylindricalFlatEndcap::jacobian(                                           \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  CylindricalFlatEndcap::inv_jacobian(                                       \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalFlatEndcap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class CylindricalFlatEndcap.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedFlatEndcap.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from 3D unit right cylinder to a volume that connects\n *  a portion of a circle to a portion of a spherical surface.\n *\n * \\image html CylindricalFlatEndcap.svg \"A cylinder maps to the shaded region.\"\n *\n * \\details Consider a 2D circle in 3D space that is normal to the\n * \\f$z\\f$ axis and has (3D) center \\f$C_1\\f$ and radius \\f$R_1\\f$.\n * Also consider a sphere with center \\f$C_2\\f$, and radius \\f$R_2\\f$.\n * Also let there be a projection point \\f$P\\f$.\n *\n * CylindricalFlatEndcap maps a 3D unit right cylinder (with coordinates\n * \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that \\f$-1\\leq\\bar{z}\\leq 1\\f$\n * and \\f$\\bar{x}^2+\\bar{y}^2 \\leq 1\\f$) to the shaded area\n * in the figure above (with coordinates \\f$(x,y,z)\\f$).  The \"bottom\"\n * of the cylinder \\f$\\bar{z}=-1\\f$ is mapped to the interior of the\n * circle of radius \\f$R_1\\f$.  Curves of constant\n * \\f$(\\bar{x},\\bar{y})\\f$ are mapped to portions of lines that pass\n * through \\f$P\\f$. Along each of these curves, \\f$\\bar{z}=-1\\f$ is\n * mapped to a point on the circle and \\f$\\bar{z}=+1\\f$ is mapped to a\n * point on the sphere.\n *\n * CylindricalFlatEndcap is intended to be composed with Wedge2D maps to\n * construct a portion of a cylindrical domain for a binary system.\n *\n * CylindricalFlatEndcap is described briefly in the Appendix of\n * \\cite Buchman:2012dw.\n * CylindricalFlatEndcap is used to construct the blocks labeled 'MA\n * wedge' and 'MB wedge' in Figure 20 of that paper.\n *\n * CylindricalFlatEndcap is implemented using `FocallyLiftedMap`\n * and `FocallyLiftedInnerMaps::FlatEndcap`; see those classes for\n * details.\n *\n * ### Restrictions on map parameters.\n *\n * The following restrictions are made so that the map is not singular\n * or close to singular. It is possible to construct a valid map\n * without these assumptions, but the assumptions simplify the code and\n * avoid problematic edge cases, and the expected use cases obey\n * these restrictions.\n *\n * We demand that\n * - The plane containing the circle is below (i.e. at a smaller value\n *   of \\f$z\\f$ than) the sphere, by an amount at least 5% of the sphere\n *   radius \\f$R_2\\f$ but not more than 5 times the sphere radius \\f$R_2\\f$.\n * - \\f$P\\f$ is inside the sphere but not too close to its surface;\n *   specifically, we demand that \\f$|P-C_2|\\leq 0.95 R_2\\f$.\n * - The ratio \\f$R_1/R_2\\f$ is between 10 and 1/10, inclusive.\n * - The x and y components of \\f$C_2-C_1\\f$ both have magnitudes\n *   smaller than or equal to \\f$R_1+R_2\\f$.\n *\n */\nclass CylindricalFlatEndcap {\n public:\n  static constexpr size_t dim = 3;\n  CylindricalFlatEndcap(const std::array<double, 3>& center_one,\n                        const std::array<double, 3>& center_two,\n                        const std::array<double, 3>& proj_center,\n                        double radius_one, double radius_two);\n\n  CylindricalFlatEndcap() = default;\n  ~CylindricalFlatEndcap() = default;\n  CylindricalFlatEndcap(CylindricalFlatEndcap&&) = default;\n  CylindricalFlatEndcap(const CylindricalFlatEndcap&) = default;\n  CylindricalFlatEndcap& operator=(const CylindricalFlatEndcap&) = default;\n  CylindricalFlatEndcap& operator=(CylindricalFlatEndcap&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const CylindricalFlatEndcap& lhs,\n                         const CylindricalFlatEndcap& rhs);\n  FocallyLiftedMap<FocallyLiftedInnerMaps::FlatEndcap> impl_;\n};\nbool operator!=(const CylindricalFlatEndcap& lhs,\n                const CylindricalFlatEndcap& rhs);\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalFlatSide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/CylindricalFlatSide.hpp\"\n\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedFlatSide.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nCylindricalFlatSide::CylindricalFlatSide(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two,\n    const std::array<double, 3>& proj_center, const double inner_radius,\n    const double outer_radius, const double radius_two)\n    : impl_(center_two, proj_center, radius_two, false,\n            FocallyLiftedInnerMaps::FlatSide(center_one, inner_radius,\n                                             outer_radius)) {\n#ifdef SPECTRE_DEBUG\n  // There are two types of sanity checks here on the map parameters.\n  // 1) ASSERTS that guarantee that the map is invertible.\n  // 2) ASSERTS that guarantee that the map parameters fall within\n  //    the range tested by the unit tests (which is the range in which\n  //    the map is expected to be used).\n  //\n  // There are two reasons why 1) and 2) are not the same:\n  //\n  // a) It is possible to choose parameters such that the map is\n  //    invertible but the resulting geometry has very sharp angles,\n  //    very large or ill-conditioned Jacobians, or both.  We want to\n  //    avoid such cases.\n  // b) We do not want to waste effort testing the map for parameters\n  //    that we don't expect to be used.\n  ASSERT(center_one[2] < center_two[2] - radius_two * 1.05,\n         \"CylindricalFlatSide: The map has only been tested for the case \"\n         \"when the sphere is contained at larger z-coordinate than the \"\n         \"plane that contains the annulus, and the sphere is not too \"\n         \"close to the annulus.\");\n  const double dist_proj = sqrt(square(center_two[0] - proj_center[0]) +\n                                square(center_two[1] - proj_center[1]) +\n                                square(center_two[2] - proj_center[2]));\n  ASSERT(dist_proj < 0.85 * radius_two,\n         \"CylindricalFlatSide: The map has been tested only for the case when \"\n         \"proj_center is contained inside the sphere and not too close to the \"\n         \"surface of the sphere\");\n  const double dist_xy_annulus = sqrt(square(center_two[0] - center_one[0]) +\n                                      square(center_two[1] - center_one[1]));\n  ASSERT(dist_xy_annulus < 0.9 * radius_two,\n         \"CylindricalFlatSide: The map has been tested only for the case when \"\n         \"the center of the annulus is contained in a circle that is 90% of \"\n         \"the size of the projection of the sphere onto the z-axis\");\n  const double dist_annulus_proj = sqrt(square(center_one[0] - proj_center[0]) +\n                                        square(center_one[1] - proj_center[1]) +\n                                        square(center_one[2] - proj_center[2]));\n  ASSERT(outer_radius > 0.1 * dist_annulus_proj,\n         \"CylindricalFlatSide: The map has been tested only for the case when \"\n         \"the annulus is not too small\");\n  ASSERT(inner_radius < 0.9 * outer_radius,\n         \"CylindricalFlatSide: The map has been tested only for the case when \"\n         \"the annulus is not too thin\");\n  const double min_inner_radius_fac =\n      std::max(0.1, dist_annulus_proj * 0.03 / outer_radius);\n  ASSERT(inner_radius > min_inner_radius_fac * outer_radius,\n         \"CylindricalFlatSide: The map has been tested only for the case when \"\n         \"the inner radius is not too small\");\n#endif\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> CylindricalFlatSide::operator()(\n    const std::array<T, 3>& source_coords) const {\n  return impl_.operator()(source_coords);\n}\n\nstd::optional<std::array<double, 3>> CylindricalFlatSide::inverse(\n    const std::array<double, 3>& target_coords) const {\n  return impl_.inverse(target_coords);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nCylindricalFlatSide::jacobian(const std::array<T, 3>& source_coords) const {\n  return impl_.jacobian(source_coords);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nCylindricalFlatSide::inv_jacobian(const std::array<T, 3>& source_coords) const {\n  return impl_.inv_jacobian(source_coords);\n}\n\nvoid CylindricalFlatSide::pup(PUP::er& p) { p | impl_; }\n\nbool operator==(const CylindricalFlatSide& lhs,\n                const CylindricalFlatSide& rhs) {\n  return lhs.impl_ == rhs.impl_;\n}\nbool operator!=(const CylindricalFlatSide& lhs,\n                const CylindricalFlatSide& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  CylindricalFlatSide::operator()(                                           \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  CylindricalFlatSide::jacobian(                                             \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  CylindricalFlatSide::inv_jacobian(                                         \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalFlatSide.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class CylindricalFlatSide.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedFlatSide.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from 3D unit right cylindrical shell to a volume that connects\n *  a portion of an annulus to a portion of a spherical surface.\n *\n * \\image html CylindricalFlatSide.svg \"A cylinder maps to the shaded region.\"\n *\n * \\details Consider a 2D annulus in 3D space that is normal to the\n * \\f$z\\f$ axis and has (3D) center \\f$C_1\\f$, inner radius\n * \\f$R_\\mathrm{in}\\f$ and outer radius \\f$R_\\mathrm{out}\\f$\n * Also consider a sphere with center \\f$C_2\\f$, and radius \\f$R_2\\f$.\n * Also let there be a projection point \\f$P\\f$.\n *\n * CylindricalFlatSide maps a 3D unit right cylindrical shell (with\n * coordinates \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that\n * \\f$-1\\leq\\bar{z}\\leq 1\\f$ and \\f$1 \\leq \\bar{x}^2+\\bar{y}^2 \\leq\n * 4\\f$) to the shaded area in the figure above (with coordinates\n * \\f$(x,y,z)\\f$).  The \"bottom\" of the cylinder \\f$\\bar{z}=-1\\f$ is\n * mapped to the interior of the annulus with radii\n * \\f$R_\\mathrm{in}\\f$ and \\f$R_\\mathrm{out}\\f$.  Curves of constant\n * \\f$(\\bar{x},\\bar{y})\\f$ are mapped to portions of lines that pass\n * through \\f$P\\f$. Along each of these curves, \\f$\\bar{z}=-1\\f$ is\n * mapped to a point inside the annulus and \\f$\\bar{z}=+1\\f$ is mapped to a\n * point on the sphere.\n *\n * CylindricalFlatSide is intended to be composed with Wedge2D maps to\n * construct a portion of a cylindrical domain for a binary system.\n *\n * CylindricalFlatSide is described briefly in the Appendix of\n * \\cite Buchman:2012dw.\n * CylindricalFlatSide is used to construct the blocks labeled 'ME\n * cylinder' in Figure 20 of that paper.\n *\n * CylindricalFlatSide is implemented using `FocallyLiftedMap`\n * and `FocallyLiftedInnerMaps::FlatSide`; see those classes for\n * details.\n *\n * ### Restrictions on map parameters.\n *\n * We demand that:\n * - The sphere is at a larger value of \\f$z\\f$ (plus 5\n *   percent of the sphere radius) than the plane containing the\n *   annulus.\n * - The projection point \\f$z_\\mathrm{P}\\f$ is\n *   inside the sphere and more than 15 percent away from the boundary\n *   of the sphere.\n * - The center of the annulus is contained in the circle that results from\n *   projecting the sphere into the \\f$xy\\f$ plane.\n * - The outer radius of the annulus is larger than 5 percent of the distance\n *   between the center of the annulus and the projection point.\n * - The inner radius of the annulus is less than 95 percent of the outer\n *   radius, larger than 5 percent of the outer radius, and larger than one\n *   percent of the distance between the center of the annulus and the\n *   projection point.  The last condition means that the angle subtended by\n *   the inner radius with respect to the projection point is not too small.\n *\n * It is possible to construct a valid map without these assumptions,\n * but some of these assumptions simplify the code and others eliminate\n * edge cases where Jacobians become large or small.\n *\n */\nclass CylindricalFlatSide {\n public:\n  static constexpr size_t dim = 3;\n  CylindricalFlatSide(const std::array<double, 3>& center_one,\n                      const std::array<double, 3>& center_two,\n                      const std::array<double, 3>& proj_center,\n                      const double inner_radius, const double outer_radius,\n                      const double radius_two);\n\n  CylindricalFlatSide() = default;\n  ~CylindricalFlatSide() = default;\n  CylindricalFlatSide(CylindricalFlatSide&&) = default;\n  CylindricalFlatSide(const CylindricalFlatSide&) = default;\n  CylindricalFlatSide& operator=(const CylindricalFlatSide&) = default;\n  CylindricalFlatSide& operator=(CylindricalFlatSide&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const CylindricalFlatSide& lhs,\n                         const CylindricalFlatSide& rhs);\n  FocallyLiftedMap<FocallyLiftedInnerMaps::FlatSide> impl_;\n};\nbool operator!=(const CylindricalFlatSide& lhs, const CylindricalFlatSide& rhs);\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalSide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/CylindricalSide.hpp\"\n\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedSide.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nCylindricalSide::CylindricalSide(const std::array<double, 3>& center_one,\n                                 const std::array<double, 3>& center_two,\n                                 const std::array<double, 3>& proj_center,\n                                 const double radius_one,\n                                 const double radius_two, const double z_lower,\n                                 const double z_upper)\n    : impl_(\n          center_two, proj_center, radius_two,\n          [&center_one, &center_two, &radius_one, &radius_two]() {\n            const double dist_spheres =\n                sqrt(square(center_one[0] - center_two[0]) +\n                     square(center_one[1] - center_two[1]) +\n                     square(center_one[2] - center_two[2]));\n            // If sphere 1 is contained in sphere 2, then the source (sphere 1)\n            // is always between the projection point and the target (sphere 2).\n            // Otherwise, if sphere 2 is contained in sphere 1, then the source\n            // (sphere 1) is not between the projection point and the target\n            // (sphere 2).\n            //\n            // Note that below we ASSERT that sphere 1 is contained in sphere 2\n            // or vice versa.\n            return dist_spheres + radius_one < radius_two;\n          }(),\n          FocallyLiftedInnerMaps::Side(center_one, radius_one, z_lower,\n                                       z_upper)) {\n#ifdef SPECTRE_DEBUG\n  // There are two types of sanity checks here on the map parameters.\n  // 1) ASSERTS that guarantee that the map is invertible.\n  // 2) ASSERTS that guarantee that the map parameters fall within\n  //    the range tested by the unit tests (which is the range in which\n  //    the map is expected to be used).\n  //\n  // There are two reasons why 1) and 2) are not the same:\n  //\n  // a) It is possible to choose parameters such that the map is\n  //    invertible but the resulting geometry has very sharp angles,\n  //    very large or ill-conditioned Jacobians, or both.  We want to\n  //    avoid such cases.\n  // b) We do not want to waste effort testing the map for parameters\n  //    that we don't expect to be used.  For example, we demand\n  //    here that sphere_one is contained within sphere_two, or vice versa,\n  //    but the map should still be valid for some choices\n  //    of parameters where sphere_one and sphere_two are disjoint;\n  //    allowing those parameter choices would involve much more\n  //    complicated logic to determine whether the map produces shapes\n  //    with sharp angles or large jacobians, and it would involve more\n  //    complicated unit tests to cover those possibilities.\n\n  const double dist_spheres = sqrt(square(center_one[0] - center_two[0]) +\n                                   square(center_one[1] - center_two[1]) +\n                                   square(center_one[2] - center_two[2]));\n  ASSERT(dist_spheres + radius_one < radius_two or\n             dist_spheres + radius_two < radius_one,\n         \"CylindricalSide: The map has been tested only for the case when \"\n         \"sphere_one is contained inside sphere_two or vice versa.\");\n\n  const double dist_proj_one = sqrt(square(center_one[0] - proj_center[0]) +\n                                    square(center_one[1] - proj_center[1]) +\n                                    square(center_one[2] - proj_center[2]));\n  const double dist_proj_two = sqrt(square(center_two[0] - proj_center[0]) +\n                                    square(center_two[1] - proj_center[1]) +\n                                    square(center_two[2] - proj_center[2]));\n\n  ASSERT(dist_proj_one < radius_one,\n         \"CylindricalSide: The map has been tested only for the case when \"\n         \"proj_center is contained inside sphere_one\");\n\n  ASSERT(proj_center[2] >= z_lower and proj_center[2] <= z_upper,\n         \"CylindricalSide: The map has been tested only for the case when \"\n         \"proj_center is contained inside z_lower and z_upper: \"\n             << z_lower << \" \" << proj_center[2] << \" \" << z_upper);\n\n  // We have three different usages for this map, and each usage\n  // is tested for slightly different ranges of parameters.\n  // - sphere_two is contained in sphere_one\n  // - sphere_one is contained in sphere_two, and projection_point is on/near\n  //   the center of one of the z planes.\n  // - sphere_one is contained in sphere_two, and projection_point is away\n  //   from the z planes.\n\n  if (dist_spheres + radius_one < radius_two) {\n    // Sphere_one is contained in sphere_two\n    ASSERT(dist_proj_two < radius_two,\n           \"CylindricalSide: The map has been tested only for the case when \"\n           \"proj_center is contained inside sphere_two. dist_proj_two=\"\n               << dist_proj_two << \", radius_two=\" << radius_two);\n    ASSERT(center_one[2] - z_lower <= 0.9 * radius_one and\n               z_upper - center_one[2] <= 0.9 * radius_one,\n           \"CylindricalSide: The map has been tested only when z_lower and \"\n           \"z_upper are sufficently far from the edge of sphere_one\");\n    ASSERT(center_one[2] - z_lower >= 0.02 * radius_one and\n               z_upper - center_one[2] >= 0.02 * radius_one,\n           \"CylindricalSide: The map has been tested only when z_lower and \"\n           \"z_upper are sufficently far from the center of sphere_one\");\n    // rho_sphere_one is the radius of the circle formed by the intersection\n    // of sphere_one and a plane at coordinate proj_center[2].\n    const double rho_sphere_one =\n        radius_one *\n        sqrt(1.0 - square((proj_center[2] - center_one[2]) / radius_one));\n    const double rho = sqrt(square(center_one[0] - proj_center[0]) +\n                            square(center_one[1] - proj_center[1]));\n    // Here \"projection point away from the z planes\" means that\n    // proj_center[2] is between z_min_close and z_max_close.\n    const double z_min_close = z_lower + 1e-15 * (z_upper - z_lower);\n    const double z_max_close = z_upper - 1e-15 * (z_upper - z_lower);\n    if (proj_center[2] <= z_min_close or proj_center[2] >= z_max_close) {\n      // Projection point is near the center of one of the z planes.\n      ASSERT(center_one[0] == center_two[0] and center_one[1] == center_two[1],\n             \"When projection point is at a z plane, the two spheres must be \"\n             \"centered in x and y\");\n      ASSERT(center_one[2] - z_lower <= 0.75 * radius_one and\n                 z_upper - center_one[2] <= 0.75 * radius_one,\n             \"CylindricalSide: The map has been tested only when z_lower and \"\n             \"z_upper are sufficently far from the edge of sphere_one\");\n      ASSERT(radius_one >= dist_spheres+0.1 and\n                 radius_two <= 4.01 * (radius_one + dist_spheres),\n             \"The map has not been tested for either radius_one this small or \"\n             \"for radius_two this large. radius_one = \"\n                 << radius_one << \", radius_two = \" << radius_two\n                 << \", dist_spheres = \" << dist_spheres);\n      ASSERT(rho <= 1.e-14,\n             \"The map has been tested only for the case when the projection \"\n             \"point is not too close to sphere_one. rho = \"\n                 << rho << \"\\nz_lower = \" << z_lower\n                 << \"\\nz_upper = \" << z_upper << \"\\nradius_one = \" << radius_one\n                 << \"\\nradius_two = \" << radius_two << \"\\nproj_center = (\"\n                 << proj_center[0] << \",\" << proj_center[1] << \",\"\n                 << proj_center[2] << \")\\ncenter_one = (\" << center_one[0]\n                 << \",\" << center_one[1] << \",\" << center_one[2]\n                 << \")\\ncenter_two = (\" << center_two[0] << \",\" << center_two[1]\n                 << \",\" << center_two[2] << \")\\n\");\n    } else {\n      // Projection point is not near the center of one of the z planes.\n      ASSERT(\n          proj_center[2] - z_lower >= 0.1 * (z_upper - z_lower) and\n              z_upper - proj_center[2] >= 0.1 * (z_upper - z_lower),\n          \"CylindricalSide: The map has been tested only for the case when \"\n          \"proj_center is sufficiently contained inside z_lower and z_upper: \"\n              << z_lower << \" \" << proj_center[2] << \" \" << z_upper);\n      ASSERT(radius_one >= 0.3 * dist_spheres and\n                 radius_two <= 2.01 * (radius_one + dist_spheres),\n             \"The map has not been tested for either radius_one this small or \"\n             \"for radius_two this large. radius_one = \"\n                 << radius_one << \", radius_two = \" << radius_two\n                 << \", dist_spheres = \" << dist_spheres);\n      ASSERT(rho <= rho_sphere_one * 0.85,\n             \"The map has been tested only for the case when the projection \"\n             \"point is not too close to sphere_one. rho = \"\n                 << rho << \", rho_sphere_one = \" << rho_sphere_one\n                 << \"\\nz_lower = \" << z_lower << \"\\nz_upper = \" << z_upper\n                 << \"\\nradius_one = \" << radius_one << \"\\nradius_two = \"\n                 << radius_two << \"\\nproj_center = (\" << proj_center[0] << \",\"\n                 << proj_center[1] << \",\" << proj_center[2]\n                 << \")\\ncenter_one = (\" << center_one[0] << \",\" << center_one[1]\n                 << \",\" << center_one[2] << \")\\ncenter_two = (\" << center_two[0]\n                 << \",\" << center_two[1] << \",\" << center_two[2] << \")\\n\");\n    }\n  } else {\n    // Sphere_two is contained in sphere_one\n    ASSERT(dist_proj_two < 0.9 * radius_two,\n           \"CylindricalSide: The map has been tested only for the case when \"\n           \"proj_center is contained inside sphere_two. dist_proj_two=\"\n               << dist_proj_two << \", radius_two=\" << radius_two);\n    ASSERT(center_one[2] - z_lower <= 0.92 * radius_one and\n               z_upper - center_one[2] <= 0.92 * radius_one,\n           \"CylindricalSide: The map has been tested only when z_lower and \"\n           \"z_upper are sufficently far from the edge of sphere_one\");\n    ASSERT(center_one[2] - z_lower >= 0.2 * radius_one and\n               z_upper - center_one[2] >= 0.2 * radius_one,\n           \"CylindricalSide: The map has been tested only when z_lower and \"\n           \"z_upper are sufficently far from the center of sphere_one\");\n    const double rho = sqrt(square(center_one[0] - center_two[0]) +\n                            square(center_one[1] - center_two[1]));\n    const double rho_max = 0.85 * sqrt(square(radius_one - radius_two) -\n                                      square(center_one[2] - center_two[2]));\n    ASSERT(rho <= rho_max,\n           \"sphere_one and sphere_two are too close to each other\");\n  }\n#endif\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> CylindricalSide::operator()(\n    const std::array<T, 3>& source_coords) const {\n  return impl_.operator()(source_coords);\n}\n\nstd::optional<std::array<double, 3>> CylindricalSide::inverse(\n    const std::array<double, 3>& target_coords) const {\n  return impl_.inverse(target_coords);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nCylindricalSide::jacobian(const std::array<T, 3>& source_coords) const {\n  return impl_.jacobian(source_coords);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nCylindricalSide::inv_jacobian(const std::array<T, 3>& source_coords) const {\n  return impl_.inv_jacobian(source_coords);\n}\n\nvoid CylindricalSide::pup(PUP::er& p) { p | impl_; }\n\nbool operator==(const CylindricalSide& lhs, const CylindricalSide& rhs) {\n  return lhs.impl_ == rhs.impl_;\n}\nbool operator!=(const CylindricalSide& lhs, const CylindricalSide& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  CylindricalSide::operator()(const std::array<DTYPE(data), 3>& source_coords) \\\n      const;                                                                   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  CylindricalSide::jacobian(const std::array<DTYPE(data), 3>& source_coords)   \\\n      const;                                                                   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  CylindricalSide::inv_jacobian(                                               \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/CylindricalSide.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class CylindricalSide.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedSide.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from a 3D unit right cylindrical shell to a volume that connects\n *  portions of two spherical surfaces.\n *\n * \\image html CylindricalSide.svg \"2D slice showing mapped (shaded) region.\"\n *\n * \\details Consider two spheres with centers \\f$C_1\\f$ and \\f$C_2\\f$,\n * and radii \\f$R_1\\f$ and \\f$R_2\\f$. Let sphere 1 be intersected by two\n * planes normal to the \\f$z\\f$ axis and located at \\f$z = z_\\mathrm{L}\\f$\n * and \\f$z = z_\\mathrm{U}\\f$, with \\f$z_\\mathrm{L} < z_\\mathrm{U}\\f$.\n * Also let there be a projection point \\f$P\\f$.\n *\n * Note that Sphere 1 and Sphere 2 are not equivalent, because the\n * mapped portion of Sphere 1 is bounded by planes of constant\n * \\f$z\\f$ but the mapped portion of Sphere 2 is not (except for\n * special choices of \\f$C_1\\f$, \\f$C_2\\f$, and \\f$P\\f$).\n *\n * CylindricalSide maps a 3D unit right cylindrical shell (with\n * coordinates \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that\n * \\f$-1\\leq\\bar{z}\\leq 1\\f$ and \\f$1 \\leq \\bar{x}^2+\\bar{y}^2 \\leq\n * 4\\f$) to the shaded area in each panel of the figure above (with\n * coordinates \\f$(x,y,z)\\f$).  The figure shows two different allowed\n * possibilities: \\f$R_1 > R_2\\f$ and \\f$R_2 > R_1\\f$. Note that the\n * two portions of the shaded region in each panel of the figure\n * represent different portions of the same block; each panel of the\n * figure is to be understood as rotated around the \\f$z\\f$ axis. The\n * inner boundary of the cylindrical shell \\f$\\bar{x}^2+\\bar{y}^2=1\\f$\n * is mapped to the portion of sphere 1 that has \\f$z_\\mathrm{L} \\leq\n * z \\leq z_\\mathrm{U}\\f$.  Curves of constant \\f$(\\bar{z})\\f$ along\n * the vector \\f$(\\bar{x},\\bar{y})\\f$ are mapped to portions of lines\n * that pass through \\f$P\\f$. Along each of these curves,\n * \\f$\\bar{x}^2+\\bar{y}^2=1\\f$ is mapped to a point on sphere 1 and\n * \\f$\\bar{x}^2+\\bar{y}^2=4\\f$ is mapped to a point on sphere 2.\n *\n * CylindricalSide is described briefly in the Appendix of\n * \\cite Buchman:2012dw.  CylindricalSide is used to construct the blocks\n * labeled 'CA cylinder', 'EA cylinder', 'CB cylinder', 'EE cylinder',\n * and 'EB cylinder' in Figure 20 of that paper.  Note that 'CA\n * cylinder', 'CB cylinder', and 'EE cylinder' have Sphere 1 contained\n * in Sphere2, and 'EA cylinder' and 'EB cylinder' have Sphere 2\n * contained in Sphere 1.\n *\n * CylindricalSide is implemented using `FocallyLiftedMap`\n * and `FocallyLiftedInnerMaps::Side`; see those classes for\n * details.\n *\n * ### Restrictions on map parameters.\n *\n * We demand that:\n * - Either Sphere 1 is fully contained inside Sphere 2, or\n *   Sphere 2 is fully contained inside Sphere 1.\n * - \\f$P\\f$ is contained inside the smaller sphere, and\n *   between (or on) the planes defined by \\f$z_\\mathrm{L}\\f$ and\n *   \\f$z_\\mathrm{U}\\f$.\n * - If sphere 1 is contained in sphere 2:\n *   - \\f$C_1^z - 0.95 R_1 \\leq z_\\mathrm{L}\\f$\n *   - \\f$z_\\mathrm{U} \\leq C_1^z + 0.95 R_1\\f$\n * - If sphere 2 is contained in sphere 1:\n *   - \\f$C_1^z - 0.95 R_1 \\leq z_\\mathrm{L} \\leq C_1^z - 0.2 R_1\\f$\n *   - \\f$C_1^z + 0.2 R_1 \\leq z_\\mathrm{U} \\leq C_1^z + 0.95 R_1\\f$\n *\n */\nclass CylindricalSide {\n public:\n  static constexpr size_t dim = 3;\n  CylindricalSide(const std::array<double, 3>& center_one,\n                  const std::array<double, 3>& center_two,\n                  const std::array<double, 3>& proj_center, double radius_one,\n                  double radius_two, double z_lower, double z_upper);\n\n  CylindricalSide() = default;\n  ~CylindricalSide() = default;\n  CylindricalSide(CylindricalSide&&) = default;\n  CylindricalSide(const CylindricalSide&) = default;\n  CylindricalSide& operator=(const CylindricalSide&) = default;\n  CylindricalSide& operator=(CylindricalSide&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const CylindricalSide& lhs,\n                         const CylindricalSide& rhs);\n  FocallyLiftedMap<FocallyLiftedInnerMaps::Side> impl_;\n};\nbool operator!=(const CylindricalSide& lhs, const CylindricalSide& rhs);\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/DiscreteRotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\ntemplate <size_t VolumeDim>\nDiscreteRotation<VolumeDim>::DiscreteRotation(\n    OrientationMap<VolumeDim> orientation)\n    : orientation_(std::move(orientation)),\n      is_identity_(orientation_.is_aligned()) {\n  if constexpr (VolumeDim > 1) {\n    // We allow reversing the direction of the axes in 1d to make testing\n    // non-aligned blocks possible in 1d, which is easier than testing them in\n    // 2d and 3d.\n    ASSERT(get(determinant(discrete_rotation_jacobian(orientation_))) > 0.0,\n           \"Discrete rotations must be done in such a manner that the sign of \"\n           \"the determinant of the discrete rotation is positive. This is to \"\n           \"preserve handedness of the coordinates.\");\n  }\n}\n\ntemplate <size_t VolumeDim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, VolumeDim>\nDiscreteRotation<VolumeDim>::operator()(\n    const std::array<T, VolumeDim>& source_coords) const {\n  return discrete_rotation(orientation_, source_coords);\n}\n\ntemplate <size_t VolumeDim>\nstd::optional<std::array<double, VolumeDim>>\nDiscreteRotation<VolumeDim>::inverse(\n    const std::array<double, VolumeDim>& target_coords) const {\n  return discrete_rotation(orientation_.inverse_map(), target_coords);\n}\n\ntemplate <size_t VolumeDim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, VolumeDim, Frame::NoFrame>\nDiscreteRotation<VolumeDim>::jacobian(\n    const std::array<T, VolumeDim>& source_coords) const {\n  auto jacobian_matrix = make_with_value<\n      tnsr::Ij<tt::remove_cvref_wrap_t<T>, VolumeDim, Frame::NoFrame>>(\n      dereference_wrapper(source_coords[0]), 0.0);\n  for (size_t d = 0; d < VolumeDim; d++) {\n    const auto new_direction =\n        orientation_(Direction<VolumeDim>(d, Side::Upper));\n    jacobian_matrix.get(d, orientation_(d)) =\n        new_direction.side() == Side::Upper ? 1.0 : -1.0;\n  }\n  return jacobian_matrix;\n}\n\ntemplate <size_t VolumeDim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, VolumeDim, Frame::NoFrame>\nDiscreteRotation<VolumeDim>::inv_jacobian(\n    const std::array<T, VolumeDim>& source_coords) const {\n  auto inv_jacobian_matrix = make_with_value<\n      tnsr::Ij<tt::remove_cvref_wrap_t<T>, VolumeDim, Frame::NoFrame>>(\n      dereference_wrapper(source_coords[0]), 0.0);\n  for (size_t d = 0; d < VolumeDim; d++) {\n    const auto new_direction =\n        orientation_(Direction<VolumeDim>(d, Side::Upper));\n    inv_jacobian_matrix.get(orientation_(d), d) =\n        new_direction.side() == Side::Upper ? 1.0 : -1.0;\n  }\n  return inv_jacobian_matrix;\n}\n\ntemplate <size_t VolumeDim>\nvoid DiscreteRotation<VolumeDim>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | orientation_;\n    p | is_identity_;\n  }\n}\n\ntemplate class DiscreteRotation<1>;\ntemplate class DiscreteRotation<2>;\ntemplate class DiscreteRotation<3>;\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)> \\\n  DiscreteRotation<DIM(data)>::operator()(                             \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  DiscreteRotation<DIM(data)>::jacobian(                               \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  DiscreteRotation<DIM(data)>::inv_jacobian(                           \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (1, 2, 3),\n    (double, DataVector,\n     std::reference_wrapper<const double>,\n     std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), MAP_AUTODIFF_TYPES)\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/DiscreteRotation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n * \\brief A CoordinateMap that swaps/negates the coordinate\n * axes.\n *\n * Providing an OrientationMap to the constructor allows for\n * the resulting map to have different orientations.\n */\ntemplate <size_t VolumeDim>\nclass DiscreteRotation {\n public:\n  static constexpr size_t dim = VolumeDim;\n\n  explicit DiscreteRotation(OrientationMap<VolumeDim> orientation =\n                                OrientationMap<VolumeDim>::create_aligned());\n  ~DiscreteRotation() = default;\n  DiscreteRotation(const DiscreteRotation&) = default;\n  DiscreteRotation(DiscreteRotation&&) = default;  // NOLINT\n  DiscreteRotation& operator=(const DiscreteRotation&) = default;\n  DiscreteRotation& operator=(DiscreteRotation&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, VolumeDim> operator()(\n      const std::array<T, VolumeDim>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, VolumeDim>> inverse(\n      const std::array<double, VolumeDim>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, VolumeDim, Frame::NoFrame> jacobian(\n      const std::array<T, VolumeDim>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, VolumeDim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, VolumeDim>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  friend bool operator==(const DiscreteRotation& lhs,\n                         const DiscreteRotation& rhs) {\n    return lhs.orientation_ == rhs.orientation_ and\n           lhs.is_identity_ == rhs.is_identity_;\n  }\n\n  OrientationMap<VolumeDim> orientation_ =\n      OrientationMap<VolumeDim>::create_aligned();\n  bool is_identity_ = false;\n};\n\ntemplate <size_t VolumeDim>\ninline bool operator!=(const CoordinateMaps::DiscreteRotation<VolumeDim>& lhs,\n                       const CoordinateMaps::DiscreteRotation<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Distribution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nstd::ostream& operator<<(std::ostream& os, const Distribution distribution) {\n  switch (distribution) {\n    case Distribution::Linear:\n      return os << \"Linear\";\n    case Distribution::Equiangular:\n      return os << \"Equiangular\";\n    case Distribution::Logarithmic:\n      return os << \"Logarithmic\";\n    case Distribution::Inverse:\n      return os << \"Inverse\";\n    case Distribution::Projective:\n      return os << \"Projective\";\n    default:\n      ERROR(\"Unknown domain::CoordinateMaps::Distribution type\");\n  }\n}\n\nbool operator==(const DistributionAndSingularityPosition& lhs,\n                const DistributionAndSingularityPosition& rhs) {\n  return lhs.distribution == rhs.distribution and\n         lhs.singularity_position == rhs.singularity_position;\n}\n\n}  // namespace domain::CoordinateMaps\n\ntemplate <>\ndomain::CoordinateMaps::Distribution\nOptions::create_from_yaml<domain::CoordinateMaps::Distribution>::create<void>(\n    const Options::Option& options) {\n  const auto distribution = options.parse_as<std::string>();\n  if (distribution == \"Linear\") {\n    return domain::CoordinateMaps::Distribution::Linear;\n  } else if (distribution == \"Equiangular\") {\n    return domain::CoordinateMaps::Distribution::Equiangular;\n  } else if (distribution == \"Logarithmic\") {\n    return domain::CoordinateMaps::Distribution::Logarithmic;\n  } else if (distribution == \"Inverse\") {\n    return domain::CoordinateMaps::Distribution::Inverse;\n  } else if (distribution == \"Projective\") {\n    return domain::CoordinateMaps::Distribution::Projective;\n  }\n  PARSE_ERROR(options.context(),\n              \"Distribution must be 'Linear', 'Equiangular', 'Logarithmic', \"\n              \"'Inverse', or 'Projective'.\");\n}\n\n// The helper classes exist to create a nested level of options like this:\n// Distribution:\n//   Logarithmic:\n//     SingularityPosition: 0.\nnamespace domain::CoordinateMaps::detail {\n\nstruct SingularityPositionImpl {\n  static constexpr Options::String help = {\n      \"Position of coordinate singularity.\"};\n  struct SingularityPosition {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Position of coordinate singularity. \"\n        \"Must be outside the domain. \"\n        \"A singularity position close to the lower or upper bound of the \"\n        \"interval leads to very small grid spacing near that end, and \"\n        \"placing the singularity further away from the domain increases the \"\n        \"grid spacing. See the documentation of \"\n        \"'domain::CoordinateMap::Distribution' for details.\"};\n  };\n  using options = tmpl::list<SingularityPosition>;\n  double value;\n};\n\ntemplate <Distribution Dist>\nstruct DistAndSingularityPositionImpl {\n  static_assert(Dist == Distribution::Logarithmic or\n                    Dist == Distribution::Inverse,\n                \"Singularity position is only required for 'Logarithmic' and \"\n                \"'Inverse' grid point distributions.\");\n  static constexpr Options::String help = {\n      \"The singularity position for the 'Logarithmic' or 'Inverse' \"\n      \"distribution.\"};\n  struct DistAndSingularityPos {\n    static std::string name() {\n      if constexpr (Dist == Distribution::Logarithmic) {\n        return \"Logarithmic\";\n\n      } else {\n        return \"Inverse\";\n      }\n    }\n    using type = SingularityPositionImpl;\n    static constexpr Options::String help{\n        \"The singularity position for the 'Logarithmic' or 'Inverse' \"\n        \"distribution.\"};\n  };\n  using options = tmpl::list<DistAndSingularityPos>;\n  SingularityPositionImpl singularity_position;\n};\n\n}  // namespace domain::CoordinateMaps::detail\n\ntemplate <>\ndomain::CoordinateMaps::DistributionAndSingularityPosition\nOptions::create_from_yaml<\n    domain::CoordinateMaps::DistributionAndSingularityPosition>::\n    create<void>(const Options::Option& options) {\n  const auto dist = options.parse_as<std::variant<\n      domain::CoordinateMaps::Distribution,\n      domain::CoordinateMaps::detail::DistAndSingularityPositionImpl<\n          domain::CoordinateMaps::Distribution::Logarithmic>,\n      domain::CoordinateMaps::detail::DistAndSingularityPositionImpl<\n          domain::CoordinateMaps::Distribution::Inverse>>>();\n  if (std::holds_alternative<domain::CoordinateMaps::Distribution>(dist)) {\n    const auto distribution =\n        std::get<domain::CoordinateMaps::Distribution>(dist);\n    if (distribution == domain::CoordinateMaps::Distribution::Linear or\n        distribution == domain::CoordinateMaps::Distribution::Projective or\n        distribution == domain::CoordinateMaps::Distribution::Equiangular) {\n      return {distribution, std::nullopt};\n    } else {\n      PARSE_ERROR(\n          options.context(),\n          \"The distribution '\"\n              << distribution\n              << \"' requires a singularity position. Specify it like this:\\n  \"\n              << distribution << \":\\n    SingularityPosition: 0.0\");\n    }\n  } else if (std::holds_alternative<\n                 domain::CoordinateMaps::detail::DistAndSingularityPositionImpl<\n                     domain::CoordinateMaps::Distribution::Logarithmic>>(\n                 dist)) {\n    const auto& dist_and_singularity_position =\n        std::get<domain::CoordinateMaps::detail::DistAndSingularityPositionImpl<\n            domain::CoordinateMaps::Distribution::Logarithmic>>(dist);\n    return {domain::CoordinateMaps::Distribution::Logarithmic,\n            dist_and_singularity_position.singularity_position.value};\n  } else if (std::holds_alternative<\n                 domain::CoordinateMaps::detail::DistAndSingularityPositionImpl<\n                     domain::CoordinateMaps::Distribution::Inverse>>(dist)) {\n    const auto& dist_and_singularity_position =\n        std::get<domain::CoordinateMaps::detail::DistAndSingularityPositionImpl<\n            domain::CoordinateMaps::Distribution::Inverse>>(dist);\n    return {domain::CoordinateMaps::Distribution::Inverse,\n            dist_and_singularity_position.singularity_position.value};\n  } else {\n    PARSE_ERROR(options.context(),\n                \"Failed to parse distribution. Specify either a distribution \"\n                \"such as 'Linear', or a distribution with its singularity \"\n                \"position such as 'Logarithmic: SingularityPosition: 0.0'.\");\n  }\n}\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Distribution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <ostream>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\brief Distribution of grid points in one dimension\n *\n * Used to select a distribution of grid points in the input file.\n *\n * \\see domain::CoordinateMaps::Wedge\n */\nenum class Distribution {\n  Linear,\n  Equiangular,\n  Logarithmic,\n  Inverse,\n  Projective\n};\n\nstd::ostream& operator<<(std::ostream& os, Distribution distribution);\n\n/*!\n * \\brief A `Distribution` and the corresponding singularity position\n *\n * The `singularity_position` is only meaningful for `Distribution::Logarithmic`\n * and `Distribution::Inverse`.\n *\n * This class can be option-created like so:\n * - Just the name of the distribution for `Linear`, `Equiangular`, and\n *  `Projective`.\n * - The name of the distribution and the singularity position for\n *   `Logarithmic` and `Inverse`:\n *\n * ```yaml\n * Logarithmic:\n *   SingularityPosition: 0.0\n * ```\n */\nstruct DistributionAndSingularityPosition {\n  Distribution distribution = Distribution::Linear;\n  std::optional<double> singularity_position = std::nullopt;\n};\n\nbool operator==(const DistributionAndSingularityPosition& lhs,\n                const DistributionAndSingularityPosition& rhs);\n\n}  // namespace domain::CoordinateMaps\n\ntemplate <>\nstruct Options::create_from_yaml<domain::CoordinateMaps::Distribution> {\n  template <typename Metavariables>\n  static domain::CoordinateMaps::Distribution create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\ndomain::CoordinateMaps::Distribution\nOptions::create_from_yaml<domain::CoordinateMaps::Distribution>::create<void>(\n    const Options::Option& options);\n\ntemplate <>\nstruct Options::create_from_yaml<\n    domain::CoordinateMaps::DistributionAndSingularityPosition> {\n  template <typename Metavariables>\n  static domain::CoordinateMaps::DistributionAndSingularityPosition create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\ndomain::CoordinateMaps::DistributionAndSingularityPosition\nOptions::create_from_yaml<\n    domain::CoordinateMaps::DistributionAndSingularityPosition>::\n    create<void>(const Options::Option& options);\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/EquatorialCompression.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nEquatorialCompression::EquatorialCompression(const double aspect_ratio,\n                                             const size_t index_pole_axis)\n    : aspect_ratio_(aspect_ratio),\n      inverse_aspect_ratio_(1.0 / aspect_ratio),\n      is_identity_(aspect_ratio_ == 1.0),\n      index_pole_axis_(index_pole_axis) {\n  ASSERT(aspect_ratio > 0.0, \"The aspect_ratio must be greater than zero.\");\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3>\nEquatorialCompression::angular_distortion(const std::array<T, 3>& coords,\n                                          const double inverse_alpha) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& x = coords[0];\n  const ReturnType& y = coords[1];\n  const ReturnType& z = coords[2];\n  const ReturnType rho =\n      sqrt(square((index_pole_axis_ == 0) ? inverse_alpha * x : x) +\n           square((index_pole_axis_ == 1) ? inverse_alpha * y : y) +\n           square((index_pole_axis_ == 2) ? inverse_alpha * z : z));\n\n  // While radius_over_rho is set to sqrt(square(x) + square(y) + square(z)),\n  // and this is only the radius, the division by rho is handled in the next\n  // line.\n  ReturnType radius_over_rho = sqrt(square(x) + square(y) + square(z));\n  for (size_t i = 0; i < get_size(rho); i++) {\n    if (LIKELY(get_element(rho, i) != 0.0)) {\n      get_element(radius_over_rho, i) /= get_element(rho, i);\n    }\n    // There is no 'else' covering the case rho==0.  The only way that\n    // rho can be zero is if x=y=z=0 (because inverse_alpha is\n    // nonzero).  So in that case, what value do we choose for radius_over_rho?\n    // Note that radius_over_rho^2 = (x^2+y^2+z^2)/(x^2+y^2+inverse_alpha^2 z^2)\n    // does not tend to a limit at the origin. (The limit is\n    // 1/inverse_alpha^2 if you approach the origin along the z axis;\n    // the limit is 1 if you approach the origin along any path in the\n    // xy plane).  But all is ok: notice that radius_over_rho is\n    // finite at the origin, and notice that the value returned by this\n    // function is multiplied by (x,y,z) below so it will be zero at the\n    // origin. Therefore we just leave radius_over_rho unchanged (with\n    // a value of zero) in the case rho==0.\n  }\n  return std::array<ReturnType, 3>{\n      {radius_over_rho * ((index_pole_axis_ == 0) ? inverse_alpha * x : x),\n       radius_over_rho * ((index_pole_axis_ == 1) ? inverse_alpha * y : y),\n       radius_over_rho * ((index_pole_axis_ == 2) ? inverse_alpha * z : z)}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nEquatorialCompression::angular_distortion_jacobian(\n    const std::array<T, 3>& coords, const double inverse_alpha) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& x = coords[0];\n  const ReturnType& y = coords[1];\n  const ReturnType& z = coords[2];\n  const ReturnType radius = sqrt(square(x) + square(y) + square(z));\n  const ReturnType rho =\n      sqrt(square((index_pole_axis_ == 0) ? inverse_alpha * x : x) +\n           square((index_pole_axis_ == 1) ? inverse_alpha * y : y) +\n           square((index_pole_axis_ == 2) ? inverse_alpha * z : z));\n\n  auto jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(coords[0]), 0.0);\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      const size_t k = (index_pole_axis_ + j) % 3;\n      jacobian_matrix.get(i, k) = gsl::at(coords, i) * gsl::at(coords, k) *\n                                  (square(inverse_alpha) - 1.0);\n      if (j == 0) {\n        jacobian_matrix.get(i, k) *=\n            (square(gsl::at(coords, index_pole_axis_)) - square(radius));\n      } else {\n        jacobian_matrix.get(i, k) *= square(gsl::at(coords, index_pole_axis_));\n      }\n      if (i == k) {\n        jacobian_matrix.get(i, k) += square(rho * radius);\n      }\n      if (i == index_pole_axis_) {\n        jacobian_matrix.get(i, k) *= inverse_alpha;\n      }\n    }\n  }\n\n  for (size_t i = 0; i < get_size(radius); i++) {\n    if (LIKELY(not equal_within_roundoff(get_element(radius, i), 0.0))) {\n      const double rho_cubed_i = cube(get_element(rho, i));\n      const double radial_factor = 1.0 / (get_element(radius, i) * rho_cubed_i);\n      get_element(get<0, 0>(jacobian_matrix), i) *= radial_factor;\n      get_element(get<1, 0>(jacobian_matrix), i) *= radial_factor;\n      get_element(get<2, 0>(jacobian_matrix), i) *= radial_factor;\n      get_element(get<0, 1>(jacobian_matrix), i) *= radial_factor;\n      get_element(get<1, 1>(jacobian_matrix), i) *= radial_factor;\n      get_element(get<2, 1>(jacobian_matrix), i) *= radial_factor;\n      get_element(get<0, 2>(jacobian_matrix), i) *= radial_factor;\n      get_element(get<1, 2>(jacobian_matrix), i) *= radial_factor;\n      get_element(get<2, 2>(jacobian_matrix), i) *= radial_factor;\n    } else {\n      // Let the jacobian of this map be the identity at the origin:\n      get_element(get<0, 0>(jacobian_matrix), i) = 1.0;\n      get_element(get<1, 0>(jacobian_matrix), i) = 0.0;\n      get_element(get<2, 0>(jacobian_matrix), i) = 0.0;\n      get_element(get<0, 1>(jacobian_matrix), i) = 0.0;\n      get_element(get<1, 1>(jacobian_matrix), i) = 1.0;\n      get_element(get<2, 1>(jacobian_matrix), i) = 0.0;\n      get_element(get<0, 2>(jacobian_matrix), i) = 0.0;\n      get_element(get<1, 2>(jacobian_matrix), i) = 0.0;\n      get_element(get<2, 2>(jacobian_matrix), i) = 1.0;\n    }\n  }\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> EquatorialCompression::operator()(\n    const std::array<T, 3>& source_coords) const {\n  return angular_distortion(source_coords, inverse_aspect_ratio_);\n}\n\nstd::optional<std::array<double, 3>> EquatorialCompression::inverse(\n    const std::array<double, 3>& target_coords) const {\n  return angular_distortion(target_coords, aspect_ratio_);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nEquatorialCompression::jacobian(const std::array<T, 3>& source_coords) const {\n  return angular_distortion_jacobian(source_coords, inverse_aspect_ratio_);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nEquatorialCompression::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  return angular_distortion_jacobian((*this)(source_coords), aspect_ratio_);\n}\n\nvoid EquatorialCompression::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | aspect_ratio_;\n    p | inverse_aspect_ratio_;\n    p | index_pole_axis_;\n    p | is_identity_;\n  }\n}\n\nbool operator==(const EquatorialCompression& lhs,\n                const EquatorialCompression& rhs) {\n  return lhs.aspect_ratio_ == rhs.aspect_ratio_ and\n         lhs.inverse_aspect_ratio_ == rhs.inverse_aspect_ratio_ and\n         lhs.index_pole_axis_ == rhs.index_pole_axis_ and\n         lhs.is_identity_ == rhs.is_identity_;\n}\n\nbool operator!=(const EquatorialCompression& lhs,\n                const EquatorialCompression& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  EquatorialCompression::operator()(                                         \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  EquatorialCompression::jacobian(                                           \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  EquatorialCompression::inv_jacobian(                                       \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/EquatorialCompression.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Redistributes gridpoints on the sphere.\n * \\image html EquatorialCompression.png \"A sphere with an `aspect_ratio` of 3.\"\n *\n * \\details A mapping from the sphere to itself which redistributes points\n * towards (or away from) a user-specifed axis, indicated by `index_pole_axis_`.\n * Once the axis is selected, the map is determined by a single parameter,\n * the `aspect_ratio` \\f$\\alpha\\f$, which is the ratio of the distance\n * perpendicular to the polar axis to the distance along the polar axis for\n * a given point. This parameter name was chosen because points with\n * \\f$\\tan \\theta = 1\\f$ get mapped to points with \\f$\\tan \\theta' = \\alpha\\f$.\n * In general, gridpoints located at an angle \\f$\\theta\\f$ from the pole are\n * mapped to a new angle\n * \\f$\\theta'\\f$ satisfying \\f$\\tan \\theta' = \\alpha \\tan \\theta\\f$.\n *\n * For an `aspect_ratio` greater than one, the gridpoints are mapped towards\n * the equator, leading to an equatorially compressed grid. For an\n * `aspect_ratio` less than one, the gridpoints are mapped towards the poles.\n * Note that the aspect ratio must be positive.\n *\n * Suppose the polar axis were the z-axis, given by `index_pole_axis_ == 2`.\n * We can then define the auxiliary variables \\f$ r := \\sqrt{x^2 + y^2 +z^2}\\f$\n * and \\f$ \\rho := \\sqrt{x^2 + y^2 + \\alpha^{-2} z^2}\\f$.\n *\n * The map corresponding to this transformation in cartesian coordinates\n * is then given by:\n *\n * \\f[\\vec{x}'(x,y,z) =\n * \\frac{r}{\\rho}\\begin{bmatrix}\n * x\\\\\n * y\\\\\n * \\alpha^{-1} z\\\\\n * \\end{bmatrix}.\\f]\n *\n * The mappings for polar axes along the x and y axes are similarly obtained.\n */\nclass EquatorialCompression {\n public:\n  static constexpr size_t dim = 3;\n  explicit EquatorialCompression(double aspect_ratio,\n                                 size_t index_pole_axis = 2);\n  EquatorialCompression() = default;\n  ~EquatorialCompression() = default;\n  EquatorialCompression(EquatorialCompression&&) = default;\n  EquatorialCompression(const EquatorialCompression&) = default;\n  EquatorialCompression& operator=(const EquatorialCompression&) = default;\n  EquatorialCompression& operator=(EquatorialCompression&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> angular_distortion(\n      const std::array<T, 3>& coords, double inverse_alpha) const;\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\n  angular_distortion_jacobian(const std::array<T, 3>& coords,\n                              double inverse_alpha) const;\n  friend bool operator==(const EquatorialCompression& lhs,\n                         const EquatorialCompression& rhs);\n\n  double aspect_ratio_{std::numeric_limits<double>::signaling_NaN()};\n  double inverse_aspect_ratio_{std::numeric_limits<double>::signaling_NaN()};\n  bool is_identity_{false};\n  size_t index_pole_axis_{};\n};\nbool operator!=(const EquatorialCompression& lhs,\n                const EquatorialCompression& rhs);\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Equiangular.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nEquiangular::Equiangular(const double A, const double B, const double a,\n                         const double b)\n    : A_(A),\n      B_(B),\n      a_(a),\n      b_(b),\n      length_of_domain_over_m_pi_4_((B - A) / M_PI_4),\n      length_of_range_(b - a),\n      m_pi_4_over_length_of_domain_(1.0 / length_of_domain_over_m_pi_4_),\n      one_over_length_of_range_(1.0 / length_of_range_),\n      linear_jacobian_times_m_pi_4_(length_of_range_ /\n                                    length_of_domain_over_m_pi_4_),\n      linear_inverse_jacobian_over_m_pi_4_(length_of_domain_over_m_pi_4_ /\n                                           length_of_range_) {}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 1> Equiangular::operator()(\n    const std::array<T, 1>& source_coords) const {\n  return {\n      {0.5 * (a_ + b_ +\n              length_of_range_ * tan(m_pi_4_over_length_of_domain_ *\n                                     (-B_ - A_ + 2.0 * source_coords[0])))}};\n}\n\nstd::optional<std::array<double, 1>> Equiangular::inverse(\n    const std::array<double, 1>& target_coords) const {\n  return {{{0.5 * (A_ + B_ +\n                   length_of_domain_over_m_pi_4_ *\n                       atan(one_over_length_of_range_ *\n                            (-a_ - b_ + 2.0 * target_coords[0])))}}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> Equiangular::jacobian(\n    const std::array<T, 1>& source_coords) const {\n  const tt::remove_cvref_wrap_t<T> tan_variable =\n      tan(m_pi_4_over_length_of_domain_ * (-B_ - A_ + 2.0 * source_coords[0]));\n  auto jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n  get<0, 0>(jacobian_matrix) =\n      linear_jacobian_times_m_pi_4_ * (1.0 + square(tan_variable));\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame>\nEquiangular::inv_jacobian(const std::array<T, 1>& source_coords) const {\n  const tt::remove_cvref_wrap_t<T> tan_variable =\n      tan(m_pi_4_over_length_of_domain_ * (-B_ - A_ + 2.0 * source_coords[0]));\n  auto inv_jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n  get<0, 0>(inv_jacobian_matrix) =\n      linear_inverse_jacobian_over_m_pi_4_ / (1.0 + square(tan_variable));\n  return inv_jacobian_matrix;\n}\n\nvoid Equiangular::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | A_;\n    p | B_;\n    p | a_;\n    p | b_;\n    p | length_of_domain_over_m_pi_4_;\n    p | length_of_range_;\n    p | m_pi_4_over_length_of_domain_;\n    p | one_over_length_of_range_;\n    p | linear_jacobian_times_m_pi_4_;\n    p | linear_inverse_jacobian_over_m_pi_4_;\n  }\n}\n\nbool operator==(const CoordinateMaps::Equiangular& lhs,\n                const CoordinateMaps::Equiangular& rhs) {\n  return lhs.A_ == rhs.A_ and lhs.B_ == rhs.B_ and lhs.a_ == rhs.a_ and\n         lhs.b_ == rhs.b_ and\n         lhs.length_of_domain_over_m_pi_4_ ==\n             rhs.length_of_domain_over_m_pi_4_ and\n         lhs.length_of_range_ == rhs.length_of_range_ and\n         lhs.m_pi_4_over_length_of_domain_ ==\n             rhs.m_pi_4_over_length_of_domain_ and\n         lhs.one_over_length_of_range_ == rhs.one_over_length_of_range_ and\n         lhs.linear_jacobian_times_m_pi_4_ ==\n             rhs.linear_jacobian_times_m_pi_4_ and\n         lhs.linear_inverse_jacobian_over_m_pi_4_ ==\n             rhs.linear_inverse_jacobian_over_m_pi_4_;\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 1>               \\\n  Equiangular::operator()(const std::array<DTYPE(data), 1>& source_coords)   \\\n      const;                                                                 \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 1, Frame::NoFrame> \\\n  Equiangular::jacobian(const std::array<DTYPE(data), 1>& source_coords)     \\\n      const;                                                                 \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 1, Frame::NoFrame> \\\n  Equiangular::inv_jacobian(const std::array<DTYPE(data), 1>& source_coords) \\\n      const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (double, DataVector,\n                  std::reference_wrapper<const double>,\n                  std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, MAP_AUTODIFF_TYPES)\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Equiangular.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class Equiangular.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n * \\brief Non-linear map from \\f$\\xi \\in [A, B]\\rightarrow x \\in [a, b]\\f$.\n *\n * The formula for the mapping is:\n * \\f{align}\n * x &= \\frac{a}{2} \\left(1-\\mathrm{tan}\\left(\n *      \\frac{\\pi(2\\xi-B-A)}{4(B-A)}\\right)\\right) +\n *      \\frac{b}{2} \\left(1+\\mathrm{tan}\\left(\n *      \\frac{\\pi(2\\xi-B-A)}{4(B-A)}\\right)\\right)\\\\\n * \\xi &= \\frac{A}{2} \\left(1-\\frac{4}{\\pi}\\mathrm{arctan}\\left(\n *        \\frac{2x-a-b}{b-a}\\right)\\right)+\n *        \\frac{B}{2} \\left(1+\\frac{4}{\\pi}\\mathrm{arctan}\\left(\n *        \\frac{2x-a-b}{b-a}\\right)\\right)\n * \\f}\n *\n * \\note The intermediate step in which a tangent map is applied can be more\n * clearly understood if we define the coordinates:\n * \\f{align}\n * \\xi_{logical} &:= \\frac{2\\xi-B-A}{B-A} \\in [-1, 1]\\\\\n * \\Xi &:= \\mathrm{tan}\\left(\\frac{\\pi\\xi_{logical}}{4}\\right) \\in [-1, 1]\n * \\f}\n *\n * This map is intended to be used with the `Wedge` map when equiangular\n * coordinates are chosen for those maps. For more information on this choice\n * of coordinates, see the documentation for `Wedge`.\n */\nclass Equiangular {\n public:\n  static constexpr size_t dim = 1;\n\n  Equiangular(double A, double B, double a, double b);\n\n  Equiangular() = default;\n  ~Equiangular() = default;\n  Equiangular(const Equiangular&) = default;\n  Equiangular(Equiangular&&) = default;\n  Equiangular& operator=(const Equiangular&) = default;\n  Equiangular& operator=(Equiangular&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 1> operator()(\n      const std::array<T, 1>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 1>> inverse(\n      const std::array<double, 1>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> jacobian(\n      const std::array<T, 1>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 1>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  friend bool operator==(const Equiangular& lhs, const Equiangular& rhs);\n\n  double A_{-1.0};\n  double B_{1.0};\n  double a_{-1.0};\n  double b_{1.0};\n  double length_of_domain_over_m_pi_4_{(B_ - A_) / M_PI_4};  // 4(B-A)/\\pi\n  double length_of_range_{2.0};                              // b-a\n  double m_pi_4_over_length_of_domain_{M_PI_4 / (B_ - A_)};\n  double one_over_length_of_range_{0.5};\n  // The jacobian for the affine map with the same parameters.\n  double linear_jacobian_times_m_pi_4_{length_of_range_ /\n                                       length_of_domain_over_m_pi_4_};\n  // The inverse jacobian for the affine map with the same parameters.\n  double linear_inverse_jacobian_over_m_pi_4_{length_of_domain_over_m_pi_4_ /\n                                              length_of_range_};\n};\n\ninline bool operator!=(const CoordinateMaps::Equiangular& lhs,\n                       const CoordinateMaps::Equiangular& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FlatOffsetSphericalWedge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/FlatOffsetSphericalWedge.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <sstream>\n#include <utility>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nFlatOffsetSphericalWedge::FlatOffsetSphericalWedge(double lower_face_x_width,\n                                                   double inner_radius,\n                                                   double outer_radius)\n    : lower_face_x_width_(lower_face_x_width),\n      inner_radius_(inner_radius),\n      outer_radius_(outer_radius) {\n  // The equal_within_roundoffs below have an implicit scale of 1,\n  // so the ASSERTs may trigger in the case where we really\n  // want an entire domain that is very small.\n  ASSERT(not equal_within_roundoff(lower_face_x_width, 0.0),\n         \"Cannot have zero lower_face_x_width\");\n  ASSERT(lower_face_x_width > 0.0, \"Cannot have negative lower_face_x_width\");\n  ASSERT(not equal_within_roundoff(inner_radius, 0.0),\n         \"Cannot have zero inner_radius\");\n  ASSERT(inner_radius > 0.0, \"Cannot have negative inner_radius\");\n  ASSERT(not equal_within_roundoff(outer_radius, 0.0),\n         \"Cannot have zero outer_radius\");\n  ASSERT(outer_radius > 0.0, \"Cannot have negative outer_radius\");\n\n  // The following two ASSERTs (and the ones above) are the strict\n  // requirements for the map to be nonsingular.\n  // Below we will have further restrictions that prevent the\n  // map from going nearly singular and losing accuracy.\n  ASSERT(lower_face_x_width < inner_radius,\n         \"Must have lower_face_x_width < inner_radius. Here inner_radius=\"\n             << inner_radius << \", lower_face_x_width=\" << lower_face_x_width);\n  ASSERT(\n      square(outer_radius) > square(lower_face_x_width) + square(inner_radius),\n      \"Must have (outer_radius)^2 > (inner_radius)^2 + (lower_face_x_width)^2.\"\n      \"Here inner_radius=\"\n          << inner_radius << \", lower_face_x_width=\" << lower_face_x_width\n          << \", outer_radius=\" << outer_radius);\n\n  // Here we arbitrarily restrict the parameters of the map to make\n  // our lives easier. The idea is that we don't want a map that is\n  // epsilon away from being singular, since then the map will have\n  // very large Jacobians (even though it is technically nonsingular)\n  // and this may cause numerical problems.  In the unit tests, we\n  // will stick to maps that have parameters that obey the\n  // restrictions below.\n  //\n  // The magic number epsilon here is chosen arbitrarily,\n  // but based on what we think a sensible user would want.\n  //\n  // Turns out there is no restriction on epsilon other than epsilon < 1.\n  // We could use two different small numbers if we wanted to, but we\n  // choose a single epsilon for simplicity.\n  const double epsilon = 0.1;\n  ASSERT(inner_radius >= epsilon * outer_radius and\n             inner_radius <= (1 - epsilon) * outer_radius,\n         \"The map is not tested if inner_radius < epsilon*outer_radius \"\n         \"or if inner_radius > (1-epsilon)*outer_radius. Here epsilon=\"\n             << epsilon << \", outer_radius=\" << outer_radius\n             << \", inner_radius=\" << inner_radius);\n  ASSERT(lower_face_x_width >= epsilon * inner_radius and\n             lower_face_x_width <= (1 - epsilon) * inner_radius,\n         \"The map is not tested if lower_face_x_width < epsilon*inner_radius \"\n         \"or if lower_face_x_width > (1-epsilon)*inner_radius. Here epsilon=\"\n             << epsilon << \", inner_radius=\" << inner_radius\n             << \", lower_face_x_width=\" << lower_face_x_width);\n  ASSERT(lower_face_x_width <= (1.0 - epsilon) * sqrt(square(outer_radius) -\n                                                      square(inner_radius)),\n         \"The map is not tested if D^2 < (1-epsilon)^2(R_2^2-R_1^2). Where \"\n         \"D is lower_face_x_width, and R_1 and R_2 are inner_radius and \"\n         \"outer_radius. Here epsilon=\"\n             << epsilon << \", lower_face_x_width=\" << lower_face_x_width\n             << \", outer_radius=\" << outer_radius\n             << \", inner_radius=\" << inner_radius);\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> FlatOffsetSphericalWedge::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xi = source_coords[0];\n  const ReturnType& eta = source_coords[1];\n  const ReturnType& zeta = source_coords[2];\n  std::array<ReturnType, 3> target_coords{};\n  ReturnType& x = target_coords[0];\n  ReturnType& y = target_coords[1];\n  ReturnType& z = target_coords[2];\n\n  const double q = 0.5 * lower_face_x_width_ / inner_radius_;\n  const double v = inner_radius_ / outer_radius_;\n\n  // Use x, y as temporary storage so we avoid memory allocations.\n  // x is set to P here (where P is the quantity in the dox).\n  x = inner_radius_ *\n      sqrt((1.0 - square(q * (xi - 1.0))) / (1.0 + square(eta)));\n  // y is set to W here (where W is the quantity in the dox).\n  y = outer_radius_ *\n      sqrt((1.0 - square(q * v * (xi + 1.0))) / (1.0 + square(eta)));\n\n  // Now fill the coordinates using x, y as temporaries.\n  z = 0.5 * (x * (1.0 - zeta) + y * (1.0 + zeta));\n  y = eta * z;\n  x = (0.5 * lower_face_x_width_) * (xi + 1.0);\n\n  return target_coords;\n}\n\nstd::optional<std::array<double, 3>> FlatOffsetSphericalWedge::inverse(\n    const std::array<double, 3>& target_coords) const {\n  const double& x = target_coords[0];\n  const double& y = target_coords[1];\n  const double& z = target_coords[2];\n\n  const double xi = 2.0 * x / lower_face_x_width_ - 1.0;\n\n  // Check for point out of range.\n  // Allow out of range by roundoff.\n  const double abs_xi = std::abs(xi);\n  if (abs_xi > 1.0 and not equal_within_roundoff(abs_xi, 1.0)) {\n    return std::nullopt;\n  }\n\n  // If z is zero, we are out of range (even if y is zero).\n  if (z == 0.0) {\n    return std::nullopt;\n  }\n  const double eta = y / z;\n\n  // Check for point out of range.\n  // Allow out of range by roundoff.\n  const double abs_eta = std::abs(eta);\n  if (abs_eta > 1.0 and not equal_within_roundoff(abs_eta, 1.0)) {\n    return std::nullopt;\n  }\n\n  // Since we know that xi and eta are in range, the following sqrts\n  // will always have positive arguments.\n  const double P =\n      inner_radius_ *\n      sqrt((1.0 - square((x - lower_face_x_width_) / inner_radius_)) /\n           (1.0 + square(eta)));\n  const double W = outer_radius_ * sqrt((1.0 - square(x / outer_radius_)) /\n                                        (1.0 + square(eta)));\n  const double zeta = (2.0 * z - P - W) / (W - P);\n\n  // Check for point out of range.\n  // Allow out of range by roundoff.\n  const double abs_zeta = std::abs(zeta);\n  if (abs_zeta > 1.0 and not equal_within_roundoff(abs_zeta, 1.0)) {\n    return std::nullopt;\n  }\n\n  return {{xi, eta, zeta}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nFlatOffsetSphericalWedge::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xi = source_coords[0];\n  const ReturnType& eta = source_coords[1];\n  const ReturnType& zeta = source_coords[2];\n\n  auto jac =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  const double q = 0.5 * lower_face_x_width_ / inner_radius_;\n  const double v = inner_radius_ / outer_radius_;\n\n  // Use Jacobian components as temporary storage to avoid extra\n  // memory allocations.\n\n  // temporarily jac(0,0) = P (where P is the quantity in the dox)\n  get<0, 0>(jac) = inner_radius_ *\n                   sqrt((1.0 - square(q * (xi - 1.0))) / (1.0 + square(eta)));\n\n  // temporarily jac(1,2) = W (where W is the quantity in the dox)\n  get<1, 2>(jac) = outer_radius_ * sqrt((1.0 - square(q * v * (xi + 1.0))) /\n                                        (1.0 + square(eta)));\n\n  // temporarily jac(1,1) = z\n  get<1, 1>(jac) =\n      0.5 * (get<0, 0>(jac) * (1.0 - zeta) + get<1, 2>(jac) * (1.0 + zeta));\n\n  // Fill in correct jac(2,1)\n  get<2, 1>(jac) = -get<1, 1>(jac) * eta / (1.0 + square(eta));\n  // Now use that to get correct jac(1,1), overwriting temporary in jac(1,1)\n  get<1, 1>(jac) += eta * get<2, 1>(jac);\n\n  // Fill in correct jac(2,0) and jac(1,0)\n  get<2, 0>(jac) = 0.5 * square(q) *\n                   (get<0, 0>(jac) * (1.0 - zeta) * (1.0 - xi) /\n                        (1.0 - square(q * (1.0 - xi))) -\n                    get<1, 2>(jac) * square(v) * (1.0 + zeta) * (1.0 + xi) /\n                        (1.0 - square(q * v * (1.0 + xi))));\n  get<1, 0>(jac) = eta * get<2, 0>(jac);\n\n  // Fill in correct jac(2,2) and jac(1,2),\n  // overwriting the temporary that was in jac(1,2)\n  get<2, 2>(jac) = 0.5 * (get<1, 2>(jac) - get<0, 0>(jac));\n  get<1, 2>(jac) = eta * get<2, 2>(jac);\n\n  // Now set jac(0,0) to its real value instead of the temporary.\n  get<0, 0>(jac) = 0.5 * lower_face_x_width_;\n\n  return jac;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nFlatOffsetSphericalWedge::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  return determinant_and_inverse(jacobian(source_coords)).second;\n}\n\nvoid FlatOffsetSphericalWedge::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | outer_radius_;\n    p | lower_face_x_width_;\n    p | inner_radius_;\n  }\n}\n\nbool operator==(const FlatOffsetSphericalWedge& lhs,\n                const FlatOffsetSphericalWedge& rhs) {\n  return lhs.outer_radius_ == rhs.outer_radius_ and\n         lhs.lower_face_x_width_ == rhs.lower_face_x_width_ and\n         lhs.inner_radius_ == rhs.inner_radius_;\n}\n\nbool operator!=(const FlatOffsetSphericalWedge& lhs,\n                const FlatOffsetSphericalWedge& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  FlatOffsetSphericalWedge::operator()(                                      \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  FlatOffsetSphericalWedge::jacobian(                                        \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  FlatOffsetSphericalWedge::inv_jacobian(                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FlatOffsetSphericalWedge.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class FlatOffsetSphericalWedge.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from a cube to a volume that connects four planes and\n * portions of two spherical surfaces.\n *\n * \\image html FlatOffsetSphericalWedge.svg \"Slices of FlatOffsetSphericalWedge\"\n *\n * \\details A cube is mapped to the volume shown in the figure.\n *\n * A $y=0$ slice through the volume is shown in the left\n * panel of the figure, and a $x=$const slice through the volume is shown\n * in the right panel of the figure.\n *\n * The lower-$z$ face of the volume is a portion of a spherical surface\n * of radius $R_1$ centered about point $C$ in the figure, which is along\n * the $x$-axis a distance $D$ from the origin (point $O$ in the figure).\n * The upper-$z$ face of the volume is a portion of a spherical surface\n * of radius $R_2$ centered about the origin.\n *\n * The lower-$x$ face of the volume is a portion of the plane at constant\n * $x=0$, and the upper-$x$ face of the volume is a portion of the plane\n * at constant $x=D$. See the $x$ extents of the left panel of the\n * figure.\n *\n * Every constant-$x$ cross section of the volume looks like the right\n * panel of the figure: it is a two-dimensional wedge with 45 degree\n * opening angle.  Both the inner and outer radii of this wedge vary\n * with $x$: The inner radius of the wedge is $R_1$ at $x=D$ and\n * $\\sqrt{R_1^2-D^2}$ at $x=0$, and the outer radius of the wedge is\n * $R_2$ at $x=0$ and $\\sqrt{R_2^2-D^2}$ at $x=D$.\n *\n * ### Relation to FlatOffsetWedge\n *\n * The lower-$z$ face of this FlatOffsetSphericalWedge is the same as\n * the upper-$z$ face of the similar map FlatOffsetWedge; Blocks using\n * these two maps are meant to abut at this surface.  The parameter\n * $D$ means the same thing for the two maps, and the parameter $R$ in\n * FlatOffsetWedge is the same as what we call $R_1$ here.\n * For abutting blocks, the corresponding parameters of the two maps should\n * be equal, and both maps should have the same origin.\n *\n * The formulas below will be similar to those of the FlatOffsetWedge map,\n * but slightly more complicated.\n *\n * ### Restrictions\n *\n * We require $D<R_1$ or else the lower-$x$ face of the mapped\n * volume lies outside of the sphere of radius $R_1$.\n * We also require that $R_2^2 > R_1^2+D^2$ or else the outer sphere\n * and inner sphere will intersect at the upper-$x$ face and the map\n * will be singular.\n *\n * ## Equations for the map\n * Given our cube coordinates $\\xi,\\eta,\\zeta$, each taking on values from\n * $-1$ to $+1$, we can derive the formulas for the map.\n *\n * Define\n * \\begin{align}\n *    q &\\equiv \\frac{D}{2R_1},\\\\\n *    v &\\equiv \\frac{R_1}{R_2}.\n * \\end{align}\n *\n * Notice that $q<1/2$, because of the restriction $D<R_1$.  Also we\n * must have $v < \\left(1+4q^2\\right)^{-1/2}$ because of the\n * restriction $R_2^2 > R_1^2+D^2$.\n *\n * Note that $x$ is a function of $\\xi$ only, for all points in the volume:\n * \\begin{align}\n *  x(\\xi,\\eta,\\zeta) &= q R (\\xi+1).\n * \\end{align}\n *\n * ### Surface map for bottom and top surfaces\n * The $y$ and $z$ coordinates of the bottom surface,\n * $\\zeta=-1$, of the volume are given by\n * \\begin{align}\n *   \\begin{bmatrix}\n *      y(\\xi,\\eta,+1)\\\\\n *      z(\\xi,\\eta,+1)\n *   \\end{bmatrix} =\n *     \\frac{R_1}{\\sqrt{1+\\eta^2}}\n *               \\sqrt{1-q^2(\\xi-1)^2}\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}.\n * \\end{align}\n * This is the same formula as for the upper face of the similar\n * FlatOffsetWedge map.\n *\n * The $y$ and $z$ coordinates for the mapped $\\zeta=+1$ surface are\n * \\begin{align}\n *   \\begin{bmatrix}\n *      y(\\xi,\\eta,+1)\\\\\n *      z(\\xi,\\eta,+1)\n *   \\end{bmatrix} =\n *     \\frac{R_2}{\\sqrt{1+\\eta^2}}\n *              \\sqrt{1-v^2q^2(\\xi+1)^2}\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}.\n * \\end{align}\n * Because of the above restrictions on $v$ and $q$, and because $\\xi$ and\n * $\\eta$ range between $-1$ and $+1$, the arguments of the square\n * roots above are always positive.\n *\n * ### Full volume map\n * Adding the $\\zeta$ dependence by linear interpolation gives us\n * the full volume map for the $y$ and $z$ coordinates:\n *\n * \\begin{align}\n *   \\begin{bmatrix}\n *      y(\\xi,\\eta,\\zeta)\\\\\n *      z(\\xi,\\eta,\\zeta)\n *   \\end{bmatrix} =\n *      \\left[\\frac{1+\\zeta}{2}\n *        \\frac{R_2}{\\sqrt{1+\\eta^2}}\n *                   \\sqrt{1-v^2q^2(\\xi+1)^2}\n *        + \\frac{1-\\zeta}{2}\n *          \\frac{R_1}{\\sqrt{1+\\eta^2}}\n *           \\sqrt{1-q^2(\\xi-1)^2}\\right]\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}.\n * \\end{align}\n *\n * ## Inverse\n *\n * The map can be inverted analytically:\n *\n * \\begin{align}\n *    \\xi   &= \\frac{x}{qR}-1\\\\\n *    \\eta  &= \\frac{y}{z}\\\\\n *    \\zeta &= \\frac{2z - W - P}{W-P},\n *              \\label{eq:zetaFromxyz}\n * \\end{align}\n * where\n * \\begin{align}\n *   P &\\equiv R_1\\frac{\\sqrt{1-(x-D)^2/R_1^2}}{\\sqrt{1+y^2/z^2}}\\\\\n *     &= R_1\\frac{\\sqrt{1-q^2(\\xi-1)^2}}{\\sqrt{1+\\eta^2}},\n *              \\label{eq:Pdefinition} \\\\\n *   W &\\equiv R_2\\frac{\\sqrt{1-x^2/R_2^2}}{\\sqrt{1+y^2/z^2}}\\\\\n *     &= R_2\\frac{\\sqrt{1-v^2q^2(\\xi+1)^2}}{\\sqrt{1+\\eta^2}}.\n *              \\label{eq:Wdefinition}\n * \\end{align}\n *\n * It is easy to determine whether a point $(x,y,z)$ lies within the volume:\n * First compute $\\xi$ and $\\eta$ and check that they are both in $[-1,1]$.\n * If so, then the arguments of the square roots in\n * Eq. ($\\ref{eq:Pdefinition}$) and Eq. ($\\ref{eq:Wdefinition}$) are\n * guaranteed positive, and the denominator\n * of Eq. ($\\ref{eq:zetaFromxyz}$) is guaranteed positive\n * by our condition $v < 1/\\sqrt{1+4q^2}$.\n * Then it is straightforward to\n * compute $\\zeta$ and then check if it is in $[-1,1]$.\n *\n * ## Jacobian\n *\n * Straightforward differentiation gives\n *\n * \\begin{align}\n *    \\frac{\\partial x}{\\partial \\xi}   &= qR_1,\\\\\n *    \\frac{\\partial x}{\\partial \\eta}   &= 0,\\\\\n *    \\frac{\\partial x}{\\partial \\zeta}   &= 0,\\\\\n *   \\partial_\\zeta \\begin{bmatrix}\n *      y\\\\\n *      z\n *   \\end{bmatrix} &= \\frac{W-P}{2}\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}, \\\\\n *   \\partial_\\xi \\begin{bmatrix}\n *      y\\\\\n *      z\n *   \\end{bmatrix} &=\n *    \\frac{q^2}{2}\\left[\n *    P\\frac{(1-\\zeta)(1-\\xi)}{1-q^2(1-\\xi)^2}\n *    -W\\frac{v^2(1+\\zeta)(1+\\xi)}{1-v^2q^2(1+\\xi)^2}\n *    \\right]\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}, \\\\\n *   \\partial_\\eta \\begin{bmatrix}\n *      y\\\\\n *      z\n *   \\end{bmatrix} &=\n *       -\\frac{\\eta}{2(1+\\eta^2)}\\left[\n *        W(1+\\zeta)\n *       +P(1-\\zeta)\\right]\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}+\n *   \\begin{bmatrix}\n *      z \\\\\n *      0\n *   \\end{bmatrix}.\n * \\end{align}\n *\n * Although it is straightforward to construct the inverse jacobian\n * analytically, for now we compute the inverse jacobian by numerically taking\n * the matrix inverse of the jacobian.\n *\n */\nclass FlatOffsetSphericalWedge {\n public:\n  static constexpr size_t dim = 3;\n  /*!\n   * \\brief Constructs a FlatOffsetSphericalWedge.\n   *\n   * \\param lower_face_x_width The width $D$ of the lower face in\n   * the $x$ direction.\n   * \\param inner_radius The inner radius $R_1$.\n   * \\param outer_radius The outer radius $R_2$.\n   */\n  FlatOffsetSphericalWedge(double lower_face_x_width, double inner_radius,\n                           double outer_radius);\n\n  FlatOffsetSphericalWedge() = default;\n  ~FlatOffsetSphericalWedge() = default;\n  FlatOffsetSphericalWedge(FlatOffsetSphericalWedge&&) = default;\n  FlatOffsetSphericalWedge(const FlatOffsetSphericalWedge&) = default;\n  FlatOffsetSphericalWedge& operator=(const FlatOffsetSphericalWedge&) =\n      default;\n  FlatOffsetSphericalWedge& operator=(FlatOffsetSphericalWedge&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // clang-tidy: google runtime references\n  void pup(PUP::er& p);  // NOLINT\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const FlatOffsetSphericalWedge& lhs,\n                         const FlatOffsetSphericalWedge& rhs);\n  double lower_face_x_width_{std::numeric_limits<double>::signaling_NaN()};\n  double inner_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double outer_radius_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const FlatOffsetSphericalWedge& lhs,\n                const FlatOffsetSphericalWedge& rhs);\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FlatOffsetWedge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/FlatOffsetWedge.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <sstream>\n#include <utility>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nFlatOffsetWedge::FlatOffsetWedge(double lower_face_y_half_width,\n                                 double lower_face_x_width, double outer_radius)\n    : lower_face_y_half_width_(lower_face_y_half_width),\n      lower_face_x_width_(lower_face_x_width),\n      outer_radius_(outer_radius) {\n  // The equal_within_roundoffs below have an implicit scale of 1,\n  // so the ASSERTs may trigger in the case where we really\n  // want an entire domain that is very small.\n  ASSERT(not equal_within_roundoff(lower_face_y_half_width, 0.0),\n         \"Cannot have zero lower_face_y_half_width\");\n  ASSERT(lower_face_y_half_width > 0.0,\n         \"Cannot have negative lower_face_y_half_width\");\n  ASSERT(not equal_within_roundoff(lower_face_x_width, 0.0),\n         \"Cannot have zero lower_face_x_width\");\n  ASSERT(lower_face_x_width > 0.0, \"Cannot have negative lower_face_x_width\");\n  ASSERT(not equal_within_roundoff(outer_radius, 0.0),\n         \"Cannot have zero outer_radius\");\n  ASSERT(outer_radius > 0.0, \"Cannot have negative outer_radius\");\n\n  // The following ASSERT (and the ones above) are the strict\n  // requirements for the map to be nonsingular.\n  // Below we will have further restrictions that prevent the\n  // map from going nearly singular and losing accuracy.\n  ASSERT(square(outer_radius) - square(lower_face_x_width) >\n             2.0 * square(lower_face_y_half_width),\n         \"Must have R^2-D^2 > 2 L^2 or else map is singular.  \"\n         \"Here R = \"\n             << outer_radius << \", D = \" << lower_face_x_width\n             << \", L = \" << lower_face_y_half_width);\n\n  // Here we arbitrarily restrict the parameters of the map to make\n  // our lives easier. The idea is that we don't want a map that is\n  // epsilon away from being singular, since then the map will have\n  // very large Jacobians (even though it is technically nonsingular)\n  // and this may cause numerical problems.  In the unit tests, we\n  // will stick to maps that have parameters that obey the\n  // restrictions below.\n  //\n  // The magic number epsilon here is chosen arbitrarily,\n  // but based on what we think a sensible user would want.\n  const double epsilon = 0.1;\n  // However, there is a restriction on epsilon.\n  // max(lower_face_y_half_width) - min(lower_face_y_half_with) must\n  // be positive, or else there are no possible values of\n  // lower_face_y_half_width.\n  // With the choices below, the smallest possible value of\n  // max(lower_face_y_half_width) - min(lower_face_y_half_with) turns out\n  // to be outer_radius*epsilon*(2-7*epsilon+O(epsilon)^2).  So we should\n  // choose epsilon < 2/7.\n  //\n  // Note that it is possible to choose multiple magic numbers,\n  // e.g. one value of epsilon for restrictions on lower_face_x_width\n  // and another value of epsilon for restrictions on\n  // lower_face_y_half_width, or even different magic numbers for the\n  // minimum and maximum values of each parameter. If we need to do\n  // such a thing later we can do so, but for now keep only one value\n  // of epsilon for simplicity.\n  ASSERT(lower_face_x_width >= epsilon * outer_radius and\n             lower_face_x_width <= (1 - epsilon) * outer_radius,\n         \"The map is not tested if lower_face_x_width < epsilon*outer_radius \"\n         \"or if lower_face_x_width > (1-epsilon)*outer_radius. Here epsilon=\"\n             << epsilon << \",lower_face_x_width=\" << lower_face_x_width\n             << \", outer_radius=\" << outer_radius);\n  ASSERT(lower_face_y_half_width >= epsilon * outer_radius,\n         \"The map is not tested if lower_face_y_half_width < \"\n         \"epsilon*outer_radius. Here epsilon = \"\n             << epsilon << \",lower_face_y_half_width=\"\n             << lower_face_y_half_width << \", outer_radius=\" << outer_radius);\n  ASSERT((square(outer_radius) - square(lower_face_x_width)) *\n                 square(1 - epsilon) >=\n             2.0 * square(lower_face_y_half_width),\n         \"The map is not tested if 2L^2 > (1-epsilon)^2(R^2-D^2). Here R = \"\n             << outer_radius << \", D = \" << lower_face_x_width << \", L = \"\n             << lower_face_y_half_width << \",epsilon = \" << epsilon);\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> FlatOffsetWedge::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xi = source_coords[0];\n  const ReturnType& eta = source_coords[1];\n  const ReturnType& zeta = source_coords[2];\n  std::array<ReturnType, 3> target_coords{};\n  ReturnType& x = target_coords[0];\n  ReturnType& y = target_coords[1];\n  ReturnType& z = target_coords[2];\n\n  const double q = 0.5 * lower_face_x_width_ / outer_radius_;\n\n  // Use x as temporary storage so we avoid memory allocations.\n  // x is set to P here (where P is the quantity in the dox).\n  x = outer_radius_ *\n      sqrt((1.0 - square(q * (xi - 1.0))) / (1.0 + square(eta)));\n\n  // Now fill the coordinates using x as temporary.\n  z = 0.5 *\n      (x + lower_face_y_half_width_ + zeta * (x - lower_face_y_half_width_));\n  y = eta * z;\n  x = (0.5 * lower_face_x_width_) * (xi + 1.0);\n\n  return target_coords;\n}\n\nstd::optional<std::array<double, 3>> FlatOffsetWedge::inverse(\n    const std::array<double, 3>& target_coords) const {\n  const double& x = target_coords[0];\n  const double& y = target_coords[1];\n  const double& z = target_coords[2];\n\n  const double xi = 2.0 * x / lower_face_x_width_ - 1.0;\n\n  // Check for point out of range.\n  // Allow out of range by roundoff.\n  const double abs_xi = std::abs(xi);\n  if (abs_xi > 1.0 and not equal_within_roundoff(abs_xi, 1.0)) {\n    return std::nullopt;\n  }\n\n  // If z is zero, we are out of range (even if y is zero).\n  if (z == 0.0) {\n    return std::nullopt;\n  }\n  const double eta = y / z;\n\n  // Check for point out of range.\n  // Allow out of range by roundoff.\n  const double abs_eta = std::abs(eta);\n  if (abs_eta > 1.0 and not equal_within_roundoff(abs_eta, 1.0)) {\n    return std::nullopt;\n  }\n\n  // Since we know that xi and eta are in range, the following sqrts\n  // will always have positive arguments.\n  const double P =\n      outer_radius_ *\n      sqrt((1.0 - square((x - lower_face_x_width_) / outer_radius_)) /\n           (1.0 + square(eta)));\n  const double zeta =\n      (2.0 * z - P - lower_face_y_half_width_) / (P - lower_face_y_half_width_);\n\n  // Check for point out of range.\n  // Allow out of range by roundoff.\n  const double abs_zeta = std::abs(zeta);\n  if (abs_zeta > 1.0 and not equal_within_roundoff(abs_zeta, 1.0)) {\n    return std::nullopt;\n  }\n\n  return {{xi, eta, zeta}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nFlatOffsetWedge::jacobian(const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xi = source_coords[0];\n  const ReturnType& eta = source_coords[1];\n  const ReturnType& zeta = source_coords[2];\n\n  auto jac =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  const double q = 0.5 * lower_face_x_width_ / outer_radius_;\n\n  // Use Jacobian components as temporary storage to avoid extra\n  // memory allocations.\n\n  // temporarily jac(0,0) = P (where P is the quantity in the dox)\n  get<0, 0>(jac) = outer_radius_ *\n                   sqrt((1.0 - square(q * (xi - 1.0))) / (1.0 + square(eta)));\n\n  // temporarily jac(1,1) = z\n  get<1, 1>(jac) = 0.5 * (get<0, 0>(jac) + lower_face_y_half_width_ +\n                          zeta * (get<0, 0>(jac) - lower_face_y_half_width_));\n\n  // Fill in correct jac(2,1)\n  get<2, 1>(jac) =\n      -get<0, 0>(jac) * 0.5 * eta * (1.0 + zeta) / (1.0 + square(eta));\n  // Now use that to get correct jac(1,1)\n  get<1, 1>(jac) += eta * get<2, 1>(jac);\n\n  // Fill in correct jac(2,0) and jac(1,0)\n  get<2, 0>(jac) = 0.5 * get<0, 0>(jac) * square(q) * (1.0 + zeta) *\n                   (1.0 - xi) / (1.0 - square(q * (xi - 1.0)));\n  get<1, 0>(jac) = eta * get<2, 0>(jac);\n\n  // Fill in correct jac(2,2) and jac(1,2)\n  get<2, 2>(jac) = 0.5 * (get<0, 0>(jac) - lower_face_y_half_width_);\n  get<1, 2>(jac) = eta * get<2, 2>(jac);\n\n  // Now set jac(0,0) to its real value instead of the temporary.\n  get<0, 0>(jac) = 0.5 * lower_face_x_width_;\n\n  return jac;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nFlatOffsetWedge::inv_jacobian(const std::array<T, 3>& source_coords) const {\n  return determinant_and_inverse(jacobian(source_coords)).second;\n}\n\nvoid FlatOffsetWedge::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | lower_face_y_half_width_;\n    p | lower_face_x_width_;\n    p | outer_radius_;\n  }\n}\n\nbool operator==(const FlatOffsetWedge& lhs, const FlatOffsetWedge& rhs) {\n  return lhs.lower_face_y_half_width_ == rhs.lower_face_y_half_width_ and\n         lhs.lower_face_x_width_ == rhs.lower_face_x_width_ and\n         lhs.outer_radius_ == rhs.outer_radius_;\n}\n\nbool operator!=(const FlatOffsetWedge& lhs, const FlatOffsetWedge& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  FlatOffsetWedge::operator()(const std::array<DTYPE(data), 3>& source_coords) \\\n      const;                                                                   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  FlatOffsetWedge::jacobian(const std::array<DTYPE(data), 3>& source_coords)   \\\n      const;                                                                   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  FlatOffsetWedge::inv_jacobian(                                               \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FlatOffsetWedge.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class FlattOffsetWedge.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from a cube to a volume that connects three planes and\n * a portion of one spherical surface.\n *\n * \\image html FlatOffsetWedge.svg \"Two slices through a FlatOffsetWedge.\"\n *\n * \\details A cube is mapped to the volume shown in the figure.\n *\n * A $y=0$ slice through the volume is shown in the left\n * panel of the figure, and a $x=$const slice through the volume is shown\n * in the right panel of the figure.\n *\n * The upper-$z$ face of the volume is a portion of a spherical surface\n * of radius $R$ centered about point $C$ in the figure, which is along\n * the $x$-axis a distance $D$ from the origin (point $O$ in the figure).\n * The lower-$z$ face of the volume is a two-dimensional rectangle of\n * constant $z$, located a distance $L$ above the origin.\n * This rectangle extends from\n * $0 \\leq x \\leq D$ in the $x$ direction, and from\n * $-L \\leq y \\leq+L$ in the $y$ direction.\n *\n * The lower-$x$ face of the volume is a portion of the plane at constant\n * $x=0$, and the upper-$x$ face of the volume is a portion of the plane\n * at constant $x=D$. See the $x$ extents of the left panel of the\n * figure.\n *\n * Every constant-$x$ cross section of the volume looks like the right panel\n * of the figure: it is a two-dimensional wedge with 45 degree\n * opening angle and a base of\n * width $2L$.  The outer radius of this wedge varies with $x$ (this radius\n * is $R$ at $x=D$ and $\\sqrt{R^2-D^2}$ at $x=0$).\n *\n * ### Restrictions\n *\n * We require $D^2 + L^2 < R^2$ or else the lower-$x$ face of the mapped\n * volume lies outside of the sphere of radius $R$.  This implies $L<R$\n * and $D<R$.\n * However, the condition that the upper-$z$ and lower-$z$ surface don't touch\n * each other at the $\\xi=-1$, $\\eta=\\pm 1$ corners is more stringent:\n * $D^2 + 2L^2 < R^2$.  This implies that $L<R/\\sqrt{2}$.\n *\n * ## Equations for the map\n * Given our cube coordinates $\\xi,\\eta,\\zeta$, each taking on values from\n * $-1$ to $+1$, we can derive the formulas for the map.\n *\n * Define\n * \\begin{equation}\n *    q \\equiv \\frac{D}{2R}.\n * \\end{equation}\n *\n * Notice that $q<1/2$, because of the restriction $D<R$.\n *\n * First, note that $x$ is a function of $\\xi$ only, for all points in\n * the cube:\n * \\begin{align}\n *  x(\\xi,\\eta,\\zeta) &= q R (\\xi+1).\n * \\end{align}\n *\n * ### Surface map for bottom and top surfaces\n * The $y$ and $z$ coordinates for the mapped $\\zeta=-1$ surface are\n * \\begin{align}\n *   \\begin{bmatrix}\n *      y(\\xi,\\eta,-1)\\\\\n *      z(\\xi,\\eta,-1)\n *   \\end{bmatrix} = L\n *   \\begin{bmatrix}\n *      \\eta\\\\\n *      1\n *   \\end{bmatrix}.\n * \\end{align}\n *\n * The $y$ and $z$ coordinates of the top surface,\n * $\\zeta=+1$, of the volume are given by\n * \\begin{align}\n *   \\begin{bmatrix}\n *      y(\\xi,\\eta,+1)\\\\\n *      z(\\xi,\\eta,+1)\n *   \\end{bmatrix} =\n *     \\frac{R}{\\sqrt{1+\\eta^2}}\n *              \\sqrt{1-q^2(\\xi-1)^2}\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}.\n * \\end{align}\n *\n * ### Full volume map\n * Adding the $\\zeta$ dependence by linear interpolation gives us\n * the full volume map for the $y$ and $z$ coordinates:\n *\n * \\begin{align}\n *   \\begin{bmatrix}\n *      y(\\xi,\\eta,\\zeta)\\\\\n *      z(\\xi,\\eta,\\zeta)\n *   \\end{bmatrix} =\n *      \\left[L \\frac{1-\\zeta}{2}\n *        + \\frac{1+\\zeta}{2}\n *          \\frac{R}{\\sqrt{1+\\eta^2}}\n *           \\sqrt{1-q^2(\\xi-1)^2}\\right]\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}.\n * \\end{align}\n *\n * ## Inverse\n *\n * The map can be inverted analytically:\n *\n * \\begin{align}\n *    \\xi   &= \\frac{x}{qR}-1\\\\\n *    \\eta  &= \\frac{y}{z}\\\\\n *    \\zeta &= \\frac{2z - L - P}{P-L},\n *              \\label{eq:zetaFromxyz}\n * \\end{align}\n * where\n * \\begin{align}\n *   P &\\equiv R\\frac{\\sqrt{1-(x-D)^2/R^2}}{\\sqrt{1+y^2/z^2}}\\\\\n *     &= R\\frac{\\sqrt{1-q^2(\\xi-1)^2}}{\\sqrt{1+\\eta^2}}.\n *              \\label{eq:Pdefinition}\n * \\end{align}\n *\n * It is easy to determine whether a point $(x,y,z)$ lies within the volume:\n * First compute $\\xi$ and $\\eta$ and check that they are both in $[-1,1]$.\n * If so, then the arguments of the square roots in\n * Eq. ($\\ref{eq:Pdefinition}$) are guaranteed positive, and the denominator\n * of Eq. ($\\ref{eq:zetaFromxyz}$) is guaranteed positive\n * by our condition $R^2>2L^2+D^2$,\n * so it is straightforward to\n * compute $\\zeta$ and then check if it is in $[-1,1]$.\n *\n * ## Jacobian\n *\n * Straightforward differentiation gives\n *\n * \\begin{align}\n *    \\frac{\\partial x}{\\partial \\xi}   &= qR\\\\\n *    \\frac{\\partial x}{\\partial \\eta}   &= 0\\\\\n *    \\frac{\\partial x}{\\partial \\zeta}   &= 0,\n * \\end{align}\n *\n * \\begin{align}\n *   \\partial_\\zeta \\begin{bmatrix}\n *      y\\\\\n *      z\n *   \\end{bmatrix} &= \\frac{1}{2}\n *    \\left[\\frac{R}{\\sqrt{1+\\eta^2}}\\sqrt{1-q^2(\\xi-1)^2}\n *    -L\\right]\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}, \\\\\n *   \\partial_\\xi \\begin{bmatrix}\n *      y\\\\\n *      z\n *   \\end{bmatrix} &=\n *    \\left[\\frac{Rq^2 (1+\\zeta)(1-\\xi)}{2\\sqrt{1+\\eta^2}}\n *    \\left(1-q^2(\\xi-1)^2\\right)^{-1/2}\\right]\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}, \\\\\n *   \\partial_\\eta \\begin{bmatrix}\n *      y\\\\\n *      z\n *   \\end{bmatrix} &=\n *       -\\left[\\frac{R\\eta(1+\\zeta)}{2(1+\\eta^2)^{3/2}}\n *        \\sqrt{1-q^2(\\xi-1)^2}\\right]\n *   \\begin{bmatrix}\n *      \\eta \\\\\n *      1\n *   \\end{bmatrix}+\n *   \\begin{bmatrix}\n *      z \\\\\n *      0\n *   \\end{bmatrix}.\n * \\end{align}\n *\n * ## Inverse Jacobian\n *\n * Let\n * \\begin{align}\n *   \\Xi &\\equiv P\\frac{1+\\zeta}{P-L}.\n * \\end{align}\n * Then\n * \\begin{align}\n *    \\frac{\\partial \\zeta}{\\partial x}  &=\n *        \\Xi q\\frac{\\xi-1}{R\\sqrt{1-q^2(\\xi-1)^2}},\\\\\n *    \\frac{\\partial \\zeta}{\\partial y}  &=\n *        \\Xi \\frac{\\eta}{z\\sqrt{1+\\eta^2}},\\\\\n *    \\frac{\\partial \\zeta}{\\partial z}  &=\n *        -\\Xi \\eta\\frac{\\eta}{z\\sqrt{1+\\eta^2}}\n *     +\\frac{2}{P-L},\\\\\n *    \\frac{\\partial \\xi}{\\partial x}   &= \\frac{1}{qR}\\\\\n *    \\frac{\\partial \\xi}{\\partial y}   &= 0\\\\\n *    \\frac{\\partial \\xi}{\\partial z}   &= 0\\\\\n *    \\frac{\\partial \\eta}{\\partial x}  &= 0\\\\\n *    \\frac{\\partial \\eta}{\\partial y}  &= \\frac{1}{z}\\\\\n *    \\frac{\\partial \\eta}{\\partial z}  &= -\\frac{\\eta}{z}.\n * \\end{align}\n *\n */\nclass FlatOffsetWedge {\n public:\n  static constexpr size_t dim = 3;\n  /*!\n   * \\brief Constructs a FlatOffsetWedge.\n   *\n   * \\param lower_face_y_half_width The half-width $L$ of the lower face in\n   * the $y$ direction.\n   * \\param lower_face_x_width The width $D$ of the lower face in\n   * the $x$ direction.\n   * \\param outer_radius The outer radius $R$.\n   */\n  FlatOffsetWedge(double lower_face_y_half_width, double lower_face_x_width,\n                  double outer_radius);\n\n  FlatOffsetWedge() = default;\n  ~FlatOffsetWedge() = default;\n  FlatOffsetWedge(FlatOffsetWedge&&) = default;\n  FlatOffsetWedge(const FlatOffsetWedge&) = default;\n  FlatOffsetWedge& operator=(const FlatOffsetWedge&) = default;\n  FlatOffsetWedge& operator=(FlatOffsetWedge&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // clang-tidy: google runtime references\n  void pup(PUP::er& p);  // NOLINT\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const FlatOffsetWedge& lhs,\n                         const FlatOffsetWedge& rhs);\n  double lower_face_y_half_width_{std::numeric_limits<double>::signaling_NaN()};\n  double lower_face_x_width_{std::numeric_limits<double>::signaling_NaN()};\n  double outer_radius_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const FlatOffsetWedge& lhs, const FlatOffsetWedge& rhs);\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/FocallyLiftedEndcap.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CylindricalEndcapHelpers.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMapHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace domain::CoordinateMaps::FocallyLiftedInnerMaps {\n\nEndcap::Endcap(const std::array<double, 3>& center, const double radius,\n               const double z_plane)\n    : center_(center),\n      radius_([&radius]() {\n        // The equal_within_roundoff below has an implicit scale of 1,\n        // so the ASSERT may trigger in the case where we really\n        // want an entire domain that is very small.\n        ASSERT(not equal_within_roundoff(radius, 0.0),\n               \"Cannot have zero radius\");\n        return radius;\n      }()),\n      theta_max_([&center, &radius, &z_plane]() {\n        const double cos_theta_max = (z_plane - center[2]) / radius;\n        ASSERT(abs(cos_theta_max) < 1.0,\n               \"Plane must intersect sphere, and at more than one point\");\n        return acos(cos_theta_max);\n      }()) {}\n\ntemplate <typename T>\nvoid Endcap::forward_map(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        target_coords,\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  ReturnType& x = (*target_coords)[0];\n  ReturnType& y = (*target_coords)[1];\n  ReturnType& z = (*target_coords)[2];\n  // Use z and y as temporary storage to avoid allocations,\n  // before setting them to their actual values.\n  z = sqrt(square(xbar) + square(ybar));\n  y = cylindrical_endcap_helpers::sin_ax_over_x(z, theta_max_) * radius_;\n  x = y * xbar + center_[0];\n  y = y * ybar + center_[1];\n  z = radius_ * cos(z * theta_max_) + center_[2];\n}\n\ntemplate <typename T>\nvoid Endcap::jacobian(const gsl::not_null<tnsr::Ij<tt::remove_cvref_wrap_t<T>,\n                                                   3, Frame::NoFrame>*>\n                          jacobian_out,\n                      const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n\n  set_number_of_grid_points(jacobian_out, source_coords);\n  // Most of the jacobian components are zero.\n  for (auto& jac_component : *jacobian_out) {\n    jac_component = 0.0;\n  }\n\n  // Use parts of Jacobian as temp storage to reduce allocations.\n  get<1, 1>(*jacobian_out) = sqrt(square(xbar) + square(ybar));\n  get<1, 0>(*jacobian_out) =\n      radius_ * cylindrical_endcap_helpers::sin_ax_over_x(\n                    get<1, 1>(*jacobian_out), theta_max_);\n  // 1/rhobar d/d(rhobar) [(sin (rhobar theta_max)/rhobar)]\n  get<0, 1>(*jacobian_out) =\n      radius_ * cylindrical_endcap_helpers::one_over_x_d_sin_ax_over_x(\n                    get<1, 1>(*jacobian_out), theta_max_);\n\n  // dy/dybar\n  get<1, 1>(*jacobian_out) =\n      get<0, 1>(*jacobian_out) * square(ybar) + get<1, 0>(*jacobian_out);\n  // dx/dxbar\n  get<0, 0>(*jacobian_out) =\n      get<0, 1>(*jacobian_out) * square(xbar) + get<1, 0>(*jacobian_out);\n\n  // Still a temporary variable here.\n  get<1, 0>(*jacobian_out) *= -theta_max_;\n\n  // dz/dxbar\n  get<2, 0>(*jacobian_out) = get<1, 0>(*jacobian_out) * xbar;\n  // dz/dybar\n  get<2, 1>(*jacobian_out) = get<1, 0>(*jacobian_out) * ybar;\n\n  // dx/dybar\n  get<0, 1>(*jacobian_out) *= xbar * ybar;\n  // dy/dxbar\n  get<1, 0>(*jacobian_out) = get<0, 1>(*jacobian_out);\n}\n\ntemplate <typename T>\nvoid Endcap::inv_jacobian(\n    const gsl::not_null<\n        tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>*>\n        inv_jacobian_out,\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n\n  set_number_of_grid_points(inv_jacobian_out, source_coords);\n  // Most of the inverse jacobian components are zero.\n  for (auto& jac_component : *inv_jacobian_out) {\n    jac_component = 0.0;\n  }\n\n  // Use parts of Jacobian as temp storage to reduce allocations.\n  // We comment temporary quantities to make the code more understandable.\n\n  // rhobar, Eq. 7 in the documentation.\n  get<1, 0>(*inv_jacobian_out) = sqrt(square(xbar) + square(ybar));\n\n  // q = sin(rhobar theta)/rhobar as defined in the documentation, Eq. 17.\n  get<0, 1>(*inv_jacobian_out) = cylindrical_endcap_helpers::sin_ax_over_x(\n      get<1, 0>(*inv_jacobian_out), theta_max_);\n\n  // 1/rhobar dq/d(rhobar)\n  get<1, 1>(*inv_jacobian_out) =\n      cylindrical_endcap_helpers::one_over_x_d_sin_ax_over_x(\n          get<1, 0>(*inv_jacobian_out), theta_max_);\n\n  // Right-hand side of Eq. 23, without the factor of\n  // xbar ybar/rq or the minus sign.\n  get<1, 0>(*inv_jacobian_out) =\n      get<1, 1>(*inv_jacobian_out) /\n      (get<0, 1>(*inv_jacobian_out) +\n       square(get<1, 0>(*inv_jacobian_out)) * get<1, 1>(*inv_jacobian_out));\n\n  // 1/(r q), i.e. first term in Eq. 22.\n  get<0, 0>(*inv_jacobian_out) = 1.0 / (get<0, 1>(*inv_jacobian_out) * radius_);\n\n  // Right-hand side of Eq. 23, without the factor of\n  // xbar ybar or the minus sign.\n  get<1, 0>(*inv_jacobian_out) *= get<0, 0>(*inv_jacobian_out);\n\n  // dybar/dy\n  get<1, 1>(*inv_jacobian_out) = get<0, 0>(*inv_jacobian_out) -\n                                 square(ybar) * get<1, 0>(*inv_jacobian_out);\n  // dxbar/dx\n  get<0, 0>(*inv_jacobian_out) -= square(xbar) * get<1, 0>(*inv_jacobian_out);\n  // dybar/dx\n  get<1, 0>(*inv_jacobian_out) *= -xbar * ybar;\n  // dxbar/dy\n  get<0, 1>(*inv_jacobian_out) = get<1, 0>(*inv_jacobian_out);\n}\n\nstd::optional<std::array<double, 3>> Endcap::inverse(\n    const std::array<double, 3>& target_coords, const double sigma_in) const {\n  const double x = target_coords[0] - center_[0];\n  const double y = target_coords[1] - center_[1];\n  const double z = target_coords[2] - center_[2];\n\n  // Are we in the range of the map?\n  // The equal_within_roundoff below has an implicit scale of 1,\n  // so the inverse may fail if radius_ is very small on purpose,\n  // e.g. if we really want a tiny tiny domain for some reason.\n  const double r = sqrt(square(x) + square(y) + square(z));\n  if (not equal_within_roundoff(r, radius_)) {\n    return std::optional<std::array<double, 3>>{};\n  }\n\n  // Compute zbar and check if we are in the range of the map.\n  const double zbar = 2.0 * sigma_in - 1.0;\n  if (abs(zbar) > 1.0 and not equal_within_roundoff(abs(zbar), 1.0)) {\n    return std::optional<std::array<double, 3>>{};\n  }\n\n  const double rho = sqrt(square(x) + square(y));\n  if (UNLIKELY(rho == 0.0)) {\n    // If x and y are zero, so are xbar and ybar,\n    // so we are done.\n    return std::array<double, 3>{{0.0, 0.0, zbar}};\n  }\n\n  // Note: theta_max_ cannot be zero for a nonsingular map.\n  const double rhobar = atan2(rho, z) / theta_max_;\n\n  // Check if we are outside the range of the map.\n  if (rhobar > 1.0 and not equal_within_roundoff(rhobar, 1.0)) {\n    return std::optional<std::array<double, 3>>{};\n  }\n\n  const double xbar = x * rhobar / rho;\n  const double ybar = y * rhobar / rho;\n  return std::array<double, 3>{{xbar, ybar, zbar}};\n}\n\ntemplate <typename T>\nvoid Endcap::sigma(const gsl::not_null<tt::remove_cvref_wrap_t<T>*> sigma_out,\n                   const std::array<T, 3>& source_coords) const {\n  *sigma_out = 0.5 * (source_coords[2] + 1.0);\n}\n\ntemplate <typename T>\nvoid Endcap::deriv_sigma(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        deriv_sigma_out,\n    const std::array<T, 3>& source_coords) const {\n  set_number_of_grid_points(deriv_sigma_out, source_coords);\n  (*deriv_sigma_out)[0] = 0.0;\n  (*deriv_sigma_out)[1] = 0.0;\n  (*deriv_sigma_out)[2] = 0.5;\n}\n\ntemplate <typename T>\nvoid Endcap::dxbar_dsigma(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        dxbar_dsigma_out,\n    const std::array<T, 3>& source_coords) const {\n  set_number_of_grid_points(dxbar_dsigma_out, source_coords);\n  (*dxbar_dsigma_out)[0] = 0.0;\n  (*dxbar_dsigma_out)[1] = 0.0;\n  (*dxbar_dsigma_out)[2] = 2.0;\n}\n\nstd::optional<double> Endcap::lambda_tilde(\n    const std::array<double, 3>& parent_mapped_target_coords,\n    const std::array<double, 3>& projection_point,\n    const bool source_is_between_focus_and_target) const {\n  // Try to find lambda_tilde going from target_coords to sphere.\n  // If the target surface is outside the sphere (that is,\n  // source_is_between_focus_and_target is true), then lambda_tilde should be\n  // positive and less than or equal to unity. If the target surface is inside\n  // the sphere, then lambda_tilde should be greater than or equal\n  // to unity. If there are two such roots, we choose based on where the points\n  // are.\n  const bool choose_larger_root =\n      parent_mapped_target_coords[2] > projection_point[2];\n  return FocallyLiftedMapHelpers::try_scale_factor(\n      parent_mapped_target_coords, projection_point, center_, radius_,\n      choose_larger_root, not source_is_between_focus_and_target);\n}\n\ntemplate <typename T>\nvoid Endcap::deriv_lambda_tilde(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        deriv_lambda_tilde_out,\n    const std::array<T, 3>& target_coords, const T& lambda_tilde,\n    const std::array<double, 3>& projection_point) const {\n  FocallyLiftedMapHelpers::d_scale_factor_d_src_point(\n      deriv_lambda_tilde_out, target_coords, projection_point, center_,\n      lambda_tilde);\n}\n\nvoid Endcap::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | center_;\n    p | radius_;\n    p | theta_max_;\n  }\n}\n\nbool operator==(const Endcap& lhs, const Endcap& rhs) {\n  return lhs.center_ == rhs.center_ and lhs.radius_ == rhs.radius_ and\n         lhs.theta_max_ == rhs.theta_max_;\n}\n\nbool operator!=(const Endcap& lhs, const Endcap& rhs) {\n  return not(lhs == rhs);\n}\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void Endcap::forward_map(                                          \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          target_coords,                                                      \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Endcap::jacobian(                                             \\\n      const gsl::not_null<                                                    \\\n          tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>*> \\\n          jacobian_out,                                                       \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Endcap::inv_jacobian(                                         \\\n      const gsl::not_null<                                                    \\\n          tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>*> \\\n          inv_jacobian_out,                                                   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Endcap::sigma(                                                \\\n      const gsl::not_null<tt::remove_cvref_wrap_t<DTYPE(data)>*> sigma_out,   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Endcap::deriv_sigma(                                          \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          deriv_sigma_out,                                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Endcap::dxbar_dsigma(                                         \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          dxbar_dsigma_out,                                                   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Endcap::deriv_lambda_tilde(                                   \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          deriv_lambda_tilde_out,                                             \\\n      const std::array<DTYPE(data), 3>& target_coords,                        \\\n      const DTYPE(data) & lambda_tilde,                                       \\\n      const std::array<double, 3>& projection_point) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef INSTANTIATE\n#undef DTYPE\n\n}  // namespace domain::CoordinateMaps::FocallyLiftedInnerMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedEndcap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// Contains FocallyLiftedInnerMaps\nnamespace domain::CoordinateMaps::FocallyLiftedInnerMaps {\n/*!\n * \\brief A FocallyLiftedInnerMap that maps a 3D unit right cylinder\n *  to a volume that connects portions of two spherical surfaces.\n *\n * Because FocallyLiftedEndcap is a FocallyLiftedInnerMap, it is meant\n * to be a template parameter of FocallyLiftedMap, and its member functions\n * are meant to be used by FocallyLiftedMap. See FocallyLiftedMap for further\n * documentation.\n *\n * \\image html FocallyLiftedEndcap.svg \"Focally Lifted Endcap.\"\n *\n * \\details The domain of the map is a 3D unit right cylinder with\n * coordinates \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that\n * \\f$-1\\leq\\bar{z}\\leq 1\\f$ and \\f$\\bar{x}^2+\\bar{y}^2 \\leq\n * 1\\f$.  The range of the map has coordinates \\f$(x,y,z)\\f$.\n *\n * Consider a sphere with center \\f$C^i\\f$ and radius \\f$R\\f$ that is\n * intersected by a plane normal to the \\f$z\\f$ axis and located at\n * \\f$z = z_\\mathrm{P}\\f$.  In the figure above, every point\n * \\f$\\bar{x}^i\\f$ in the blue region \\f$\\sigma=0\\f$ maps to a point\n * \\f$x_0^i\\f$ on a portion of the surface of the sphere.\n *\n * `Endcap` provides the following functions:\n *\n * ### forward_map\n * `forward_map` maps \\f$(\\bar{x},\\bar{y},\\bar{z}=-1)\\f$ to the portion of\n * the sphere with \\f$z \\geq z_\\mathrm{P}\\f$.  The arguments to `forward_map`\n * are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n * `forward_map` returns \\f$x_0^i\\f$,\n * the 3D coordinates on that sphere, which are given by\n *\n * \\f{align}\n * x_0^0 &= R \\frac{\\sin(\\bar{\\rho} \\theta_\\mathrm{max})\n *              \\bar{x}}{\\bar{\\rho}} + C^0,\\\\\n * x_0^1 &= R \\frac{\\sin(\\bar{\\rho} \\theta_\\mathrm{max})\n *              \\bar{y}}{\\bar{\\rho}} + C^1,\\\\\n * x_0^2 &= R \\cos(\\bar{\\rho} \\theta_\\mathrm{max}) + C^2.\n * \\f}\n *\n * Here \\f$\\bar{\\rho}^2 \\equiv (\\bar{x}^2+\\bar{y}^2)/\\bar{R}^2\\f$, where\n * \\f$\\bar{R}\\f$ is the radius of the cylinder in barred coordinates,\n * which is always unity,\n * and where\n * \\f$\\theta_\\mathrm{max}\\f$ is defined by\n * \\f$\\cos(\\theta_\\mathrm{max}) = (z_\\mathrm{P}-C^2)/R\\f$.\n * Note that when \\f$\\bar{\\rho}=0\\f$, we must evaluate\n * \\f$\\sin(\\bar{\\rho}\\theta_\\mathrm{max})/\\bar{\\rho}\\f$\n * as \\f$\\theta_\\mathrm{max}\\f$.\n *\n * ### sigma\n *\n * \\f$\\sigma\\f$ is a function that is zero at \\f$\\bar{z}=-1\\f$\n * (which maps onto the sphere \\f$x^i=x_0^i\\f$) and\n * unity at \\f$\\bar{z}=+1\\f$ (corresponding to the\n * upper surface of the FocallyLiftedMap). We define\n *\n * \\f{align}\n *  \\sigma &= \\frac{\\bar{z}+1}{2}.\n * \\f}\n *\n * ### deriv_sigma\n *\n * `deriv_sigma` returns\n *\n * \\f{align}\n *  \\frac{\\partial \\sigma}{\\partial \\bar{x}^j} &= (0,0,1/2).\n * \\f}\n *\n * ### jacobian\n *\n * `jacobian` returns \\f$\\partial x_0^k/\\partial \\bar{x}^j\\f$.\n * The arguments to `jacobian`\n * are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n *\n * Differentiating Eqs.(1--3) above yields\n *\n * \\f{align*}\n * \\frac{\\partial x_0^2}{\\partial \\bar{x}} &=\n * - R \\theta_\\mathrm{max}\n * \\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}}\\bar{x}\\\\\n * \\frac{\\partial x_0^2}{\\partial \\bar{y}} &=\n * - R \\theta_\\mathrm{max}\n * \\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}}\\bar{y}\\\\\n * \\frac{\\partial x_0^0}{\\partial \\bar{x}} &=\n * R \\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}} +\n * R \\frac{1}{\\bar{\\rho}}\\frac{d}{d\\bar{\\rho}}\n * \\left(\\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}}\\right)\n * \\bar{x}^2\\\\\n * \\frac{\\partial x_0^0}{\\partial \\bar{y}} &=\n * R \\frac{1}{\\bar{\\rho}}\\frac{d}{d\\bar{\\rho}}\n * \\left(\\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}}\\right)\n * \\bar{x}\\bar{y}\\\\\n * \\frac{\\partial x_0^1}{\\partial \\bar{x}} &=\n * R \\frac{1}{\\bar{\\rho}}\\frac{d}{d\\bar{\\rho}}\n * \\left(\\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}}\\right)\n * \\bar{x}\\bar{y}\\\\\n * \\frac{\\partial x_0^1}{\\partial \\bar{y}} &=\n * R \\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}} +\n * R \\frac{1}{\\bar{\\rho}}\\frac{d}{d\\bar{\\rho}}\n * \\left(\\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}}\\right)\n * \\bar{y}^2\\\\\n * \\frac{\\partial x_0^i}{\\partial \\bar{z}} &=0.\n * \\f}\n *\n * ### Evaluating sinc functions\n *\n * Note that \\f$\\sin(\\bar{\\rho}\\theta_\\mathrm{max})/\\bar{\\rho}\\f$ and\n * its derivative appear in the above equations.  We evaluate\n * \\f$\\sin(ax)/x\\f$ in a straightforward way, except we are careful\n * to evaluate \\f$\\sin(ax)/x = a\\f$ for the special case \\f$x=0\\f$.\n *\n * The derivative of the sync function is more complicated to evaluate\n * because of roundoff. Note that we can expand\n *\n * \\f{align*}\n *  \\frac{1}{x}\\frac{d}{dx}\\left(\\frac{\\sin(ax)}{x}\\right) &=\n *  \\frac{a}{x^2}\\left(1 - 1 - \\frac{2 (ax)^2}{3!} +\n *  \\frac{4(ax)^4}{5!} - \\frac{5(ax)^6}{7!} + \\cdots \\right),\n * \\f}\n *\n * where we kept the \"1 - 1\" above as a reminder that when evaluating\n * this function directly as \\f$(a \\cos(ax)/x^2 - \\sin(ax)/x^3)\\f$, there\n * can be significant roundoff because of the \"1\" in each of the two\n * terms that are subtracted. The relative error in direct evaluation is\n * \\f$3\\epsilon/(ax)^2\\f$, where \\f$\\epsilon\\f$ is machine precision\n * (This expression comes from replacing \"1 - 1\" with \\f$\\epsilon\\f$\n * and comparing the lowest-order contribution to the correct answer, i.e.\n * the \\f$2(ax)^2/3!\\f$ term, with \\f$\\epsilon\\f$, the error contribution).\n * This means the error is 100% if \\f$(ax)^2=3\\epsilon\\f$.\n *\n * To avoid roundoff, we evaluate the series if \\f$ax\\f$ is small\n * enough.  Suppose we keep terms up to and including the \\f$(ax)^{2n}\\f$\n * term in the series.  Then we evaluate the series if the\n * next term, the \\f$(ax)^{2n+2}\\f$ term, is roundoff,\n * i.e. if \\f$(2n+2)(ax)^{2n+2}/(2n+3)! < \\epsilon\\f$.  In this case,\n * the direct evaluation has the maximum error if\n * \\f$(2n+2)(ax)^{2n+2}/(2n+3)! = \\epsilon\\f$.  We showed above that the\n * relative error in direct evaluation is \\f$3\\epsilon/(ax)^2\\f$,\n * which evaluates to \\f$(\\epsilon^n (2n+2)/(2n+3)!)^{1/(n+1)}\\f$.\n *\n * \\f{align*}\n *   n=1 \\qquad& \\mathrm{error}=3(\\epsilon/30)^{1/2} &\\qquad\n *               \\sim \\mathrm{5e-09}\\\\\n *   n=2 \\qquad& \\mathrm{error}=3(\\epsilon^2/840)^{1/3} &\\qquad\n *               \\sim \\mathrm{7e-12}\\\\\n *   n=3 \\qquad& \\mathrm{error}=3(\\epsilon^3/45360)^{1/4} &\\qquad\n *               \\sim \\mathrm{2e-13}\\\\\n *   n=4 \\qquad& \\mathrm{error}=3(\\epsilon^4/3991680)^{1/5} &\\qquad\n *               \\sim \\mathrm{2e-14}\\\\\n *   n=5 \\qquad& \\mathrm{error}=3(\\epsilon^5/518918400)^{1/6} &\\qquad\n *               \\sim \\mathrm{5e-15}\\\\\n *   n=6 \\qquad& \\mathrm{error}=3(\\epsilon^6/93405312000)^{1/7} &\\qquad\n *               \\sim \\mathrm{1e-15}\n * \\f}\n * We gain less and less with each order, so we choose \\f$n=3\\f$.\n * Then the series above can be rewritten to this order in the form\n * \\f{align*}\n *  \\frac{1}{x}\\frac{d}{dx}\\left(\\frac{\\sin(ax)}{x}\\right) &=\n *  -\\frac{a^3}{3}\\left(1 - \\frac{3\\cdot 4 (ax)^2}{5!} +\n *  \\frac{3 \\cdot 6(ax)^4}{7!}\\right).\n * \\f}\n *\n * ### inverse\n *\n * `inverse` takes \\f$x_0^i\\f$ and \\f$\\sigma\\f$ as arguments, and\n * returns \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, or boost::none if\n * \\f$x_0^i\\f$ or \\f$\\sigma\\f$ are outside the range of the map.\n * For example, if \\f$x_0^i\\f$ does not lie on the sphere,\n * we return boost::none.\n *\n * The easiest to compute is \\f$\\bar{z}\\f$, which is given by inverting\n * Eq. (4):\n *\n * \\f{align}\n *  \\bar{z} &= 2\\sigma - 1.\n * \\f}\n *\n * If \\f$\\bar{z}\\f$ is outside the range \\f$[-1,1]\\f$ then we return\n * boost::none.\n *\n * To get \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$,\n * we invert\n * Eqs (1--3).  If \\f$x_0^0=x_0^1=0\\f$, then \\f$\\bar{x}=\\bar{y}=0\\f$.\n * Otherwise, we compute\n *\n * \\f{align}\n *   \\bar{\\rho} = \\theta_\\mathrm{max}^{-1}\n *   \\tan^{-1}\\left(\\frac{\\rho}{x_0^2-C^2}\\right),\n * \\f}\n *\n * where \\f$\\rho^2 = (x_0^0-C^0)^2+(x_0^1-C^1)^2\\f$. Then\n *\n * \\f{align}\n * \\bar{x} &= (x_0^0-C^0)\\frac{\\bar{\\rho}}{\\rho},\\\\\n * \\bar{y} &= (x_0^1-C^1)\\frac{\\bar{\\rho}}{\\rho}.\n * \\f}\n *\n * Note that if \\f$\\bar{x}^2+\\bar{y}^2 > 1\\f$, the original point is outside\n * the range of the map so we return boost::none.\n *\n * ### lambda_tilde\n *\n * `lambda_tilde` takes as arguments a point \\f$x^i\\f$ and a projection point\n *  \\f$P^i\\f$, and computes \\f$\\tilde{\\lambda}\\f$, the solution to\n *\n * \\f{align} x_0^i = P^i + (x^i - P^i) \\tilde{\\lambda}.\\f}\n *\n * Since \\f$x_0^i\\f$ must lie on the sphere, \\f$\\tilde{\\lambda}\\f$ is the\n * solution of the quadratic equation\n *\n * \\f{align}\n * |P^i + (x^i - P^i) \\tilde{\\lambda} - C^i |^2 - R^2 = 0.\n * \\f}\n *\n * In solving the quadratic, we demand a root that is positive and\n * less than or equal to unity, since \\f$x_0^i\\f$ is always between\n * the projection point and \\f$x^i\\f$. If there are two suitable\n * roots, this means that the entire sphere lies between \\f$P^i\\f$ and\n * \\f$x^i\\f$; in this case if \\f$x^2 \\geq z_\\mathrm{P}\\f$ we choose the\n * larger root, otherwise we choose the smaller one: this gives\n * us the root with \\f$x_0^2 \\geq z_\\mathrm{P}\\f$, the portion of the sphere\n * that is the range of `Endcap`. If there is no suitable root,\n * this means that the point \\f$x^i\\f$ is not in the range of the map\n * so we return a default-constructed std::optional.\n *\n * ### deriv_lambda_tilde\n *\n * `deriv_lambda_tilde` takes as arguments \\f$x_0^i\\f$, a projection point\n *  \\f$P^i\\f$, and \\f$\\tilde{\\lambda}\\f$, and\n *  returns \\f$\\partial \\tilde{\\lambda}/\\partial x^i\\f$.\n * By differentiating Eq. (11), we find\n *\n * \\f{align}\n * \\frac{\\partial\\tilde{\\lambda}}{\\partial x^j} &=\n * \\tilde{\\lambda}^2 \\frac{C^j - x_0^j}{|x_0^i - P^i|^2\n * + (x_0^i - P^i)(P_i - C_{i})}.\n * \\f}\n *\n * ### inv_jacobian\n *\n * `inv_jacobian` returns \\f$\\partial \\bar{x}^i/\\partial x_0^k\\f$,\n *  where \\f$\\sigma\\f$ is held fixed.\n *  The arguments to `inv_jacobian`\n *  are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n *\n * Note that \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$ can be considered to\n * depend only on \\f$x_0^0\\f$ and \\f$x_0^1\\f$ but not on \\f$x_0^2\\f$,\n * because the point \\f$x_0^i\\f$ is constrained to lie on a sphere of\n * radius \\f$R\\f$.  Note that there is an alternative way to compute\n * Eqs. (8) and (9) using only \\f$x_0^0\\f$ and \\f$x_0^1\\f$. To do\n * this, define\n *\n * \\f{align}\n * \\upsilon \\equiv \\sin(\\bar{\\rho}\\theta_\\mathrm{max})\n *        &= \\sqrt{\\frac{(x_0^0-C^0)^2+(x_0^1-C^1)^2}{R^2}}.\n * \\f}\n *\n * Then we can write\n *\n * \\f{align}\n * \\frac{1}{\\bar{\\rho}}\\sin(\\bar{\\rho}\\theta_\\mathrm{max})\n * &= \\frac{\\theta_\\mathrm{max}\\upsilon}{\\arcsin(\\upsilon)},\n * \\f}\n *\n * so that\n *\n * \\f{align}\n * \\bar{x} &= \\frac{x_0^0-C^0}{R}\\left(\\frac{1}{\\bar{\\rho}}\n *             \\sin(\\bar{\\rho}\\theta_\\mathrm{max})\\right)^{-1} \\\\\n * \\bar{y} &= \\frac{x_0^1-C^1}{R}\\left(\\frac{1}{\\bar{\\rho}}\n *             \\sin(\\bar{\\rho}\\theta_\\mathrm{max})\\right)^{-1}.\n * \\f}\n *\n * We will compute \\f$\\partial \\bar{x}^i/\\partial\n * x_0^k\\f$ by differentiating Eqs. (15) and (16).  Because those equations\n * involve \\f$\\bar{\\rho}\\f$, we first establish some relations\n * involving derivatives of \\f$\\bar{\\rho}\\f$.  For ease of notation, we define\n *\n * \\f{align}\n * q \\equiv \\frac{\\sin(\\bar{\\rho}\\theta_\\mathrm{max})}{\\bar{\\rho}}.\n * \\f}\n *\n * First observe that\n * \\f{align}\n * \\frac{dq}{d\\upsilon}\n * = \\frac{dq}{d\\bar{\\rho}}\n * \\left(\\bar{\\rho} \\frac{dq}{d\\bar{\\rho}} + q\\right)^{-1},\n * \\f}\n *\n * where \\f$\\upsilon\\f$ is the quantity defined by Eq. (13).  Therefore\n *\n * \\f{align}\n * \\frac{\\partial q}{\\partial x_0^0} &=\n * \\frac{\\bar{x}}{\\bar{\\rho}R}\\frac{dq}{d\\bar{\\rho}}\n * \\left(\\bar{\\rho} \\frac{dq}{d\\bar{\\rho}} + q\\right)^{-1},\\\\\n * \\frac{\\partial q}{\\partial x_0^1} &=\n * \\frac{\\bar{y}}{\\bar{\\rho}R}\\frac{dq}{d\\bar{\\rho}}\n * \\left(\\bar{\\rho} \\frac{dq}{d\\bar{\\rho}} + q\\right)^{-1},\n * \\f}\n *\n * where we have differentiated Eq. (13), and where we have\n * used Eqs. (15) and (16) to eliminate \\f$x_0^0\\f$ and\n * \\f$x_0^1\\f$ in favor of \\f$\\bar{x}\\f$ and\n * \\f$\\bar{y}\\f$ in the final result.\n *\n * Let\n * \\f{align}\n * \\Sigma \\equiv \\frac{1}{\\bar\\rho} \\frac{dq}{d\\bar{\\rho}},\n * \\f}\n * since that combination will appear frequently in formulas below. Note that\n * \\f$\\Sigma\\f$ has a finite limit as \\f$\\bar{\\rho}\\to 0\\f$, and it is evaluated\n * according to the section on evaluating sinc functions above.\n *\n * By differentiating Eqs. (15) and (16), and using Eqs. (19) and (20), we\n * find\n *\n * \\f{align}\n * \\frac{\\partial \\bar{x}}{\\partial x_0^0} &=\n * \\frac{1}{R q}\n * - \\frac{\\bar{x}^2 \\Sigma}{R q}\n * \\left(\\bar{\\rho}^2 \\Sigma + q\\right)^{-1},\\\\\n * \\frac{\\partial \\bar{x}}{\\partial x_0^1} &=\n * - \\frac{\\bar{x}\\bar{y} \\Sigma}{R q}\n * \\left(\\bar{\\rho}^2 \\Sigma + q\\right)^{-1},\\\\\n * \\frac{\\partial \\bar{x}}{\\partial x_0^2} &= 0,\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^0} &=\n * \\frac{\\partial \\bar{x}}{\\partial x_0^1},\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^1} &=\n * \\frac{1}{R q}\n * - \\frac{\\bar{y}^2 \\Sigma}{R q}\n * \\left(\\bar{\\rho}^2 \\Sigma + q\\right)^{-1},\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^2} &= 0,\\\\\n * \\frac{\\partial \\bar{z}}{\\partial x_0^i} &= 0.\n * \\f}\n * Note that care must be taken to evaluate\n * \\f$q = \\sin(\\bar{\\rho}\\theta_\\mathrm{max})/\\bar{\\rho}\\f$ and its\n * derivative \\f$\\Sigma\\f$ near \\f$\\bar{\\rho}=0\\f$; see the discussion above on\n * evaluating sinc functions.\n *\n * ### dxbar_dsigma\n *\n * `dxbar_dsigma` returns \\f$\\partial \\bar{x}^i/\\partial \\sigma\\f$,\n *  where \\f$x_0^i\\f$ is held fixed.\n *\n * From Eq. (6) we have\n *\n * \\f{align}\n * \\frac{\\partial \\bar{x}^i}{\\partial \\sigma} &= (0,0,2).\n * \\f}\n *\n */\nclass Endcap {\n public:\n  Endcap(const std::array<double, 3>& center, double radius, double z_plane);\n\n  Endcap() = default;\n  ~Endcap() = default;\n  Endcap(Endcap&&) = default;\n  Endcap(const Endcap&) = default;\n  Endcap& operator=(const Endcap&) = default;\n  Endcap& operator=(Endcap&&) = default;\n\n  template <typename T>\n  void forward_map(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          target_coords,\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords, double sigma_in) const;\n\n  template <typename T>\n  void jacobian(const gsl::not_null<\n                    tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>*>\n                    jacobian_out,\n                const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void inv_jacobian(const gsl::not_null<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3,\n                                                 Frame::NoFrame>*>\n                        inv_jacobian_out,\n                    const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void sigma(const gsl::not_null<tt::remove_cvref_wrap_t<T>*> sigma_out,\n             const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void deriv_sigma(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          deriv_sigma_out,\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void dxbar_dsigma(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          dxbar_dsigma_out,\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<double> lambda_tilde(\n      const std::array<double, 3>& parent_mapped_target_coords,\n      const std::array<double, 3>& projection_point,\n      bool source_is_between_focus_and_target) const;\n\n  template <typename T>\n  void deriv_lambda_tilde(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          deriv_lambda_tilde_out,\n      const std::array<T, 3>& target_coords, const T& lambda_tilde,\n      const std::array<double, 3>& projection_point) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const Endcap& lhs, const Endcap& rhs);\n  std::array<double, 3> center_{};\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  double theta_max_{std::numeric_limits<double>::signaling_NaN()};\n};\nbool operator!=(const Endcap& lhs, const Endcap& rhs);\n}  // namespace domain::CoordinateMaps::FocallyLiftedInnerMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedFlatEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/FocallyLiftedFlatEndcap.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace domain::CoordinateMaps::FocallyLiftedInnerMaps {\n\nFlatEndcap::FlatEndcap(const std::array<double, 3>& center, double radius)\n    : center_(center), radius_([&radius]() {\n        ASSERT(\n            not equal_within_roundoff(radius, 0.0),\n            \"Cannot have zero radius.  Note that this ASSERT implicitly \"\n            \"assumes that the radius has a scale of roughly unity.  Therefore, \"\n            \"this ASSERT may trigger in the case where we intentionally want \"\n            \"an entire domain that is very small.  If we really want small \"\n            \"domains, then this ASSERT should be modified.\");\n        return radius;\n      }()) {}\n\ntemplate <typename T>\nvoid FlatEndcap::forward_map(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        target_coords,\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n\n  if constexpr (not std::is_same_v<ReturnType, double>) {\n    (*target_coords)[2].destructive_resize(\n        static_cast<ReturnType>(source_coords[0]).size());\n  }\n\n  (*target_coords)[0] = radius_ * xbar + center_[0];\n  (*target_coords)[1] = radius_ * ybar + center_[1];\n  (*target_coords)[2] = center_[2];\n}\n\ntemplate <typename T>\nvoid FlatEndcap::jacobian(\n    const gsl::not_null<\n        tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>*>\n        jacobian_out,\n    const std::array<T, 3>& source_coords) const {\n  set_number_of_grid_points(jacobian_out, source_coords);\n  // Most of the jacobian components are zero.\n  for (auto& jac_component : *jacobian_out) {\n    jac_component = 0.0;\n  }\n\n  // dx/dxbar\n  get<0, 0>(*jacobian_out) = radius_;\n  // dy/dybar\n  get<1, 1>(*jacobian_out) = radius_;\n}\n\ntemplate <typename T>\nvoid FlatEndcap::inv_jacobian(\n    const gsl::not_null<\n        tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>*>\n        inv_jacobian_out,\n    const std::array<T, 3>& source_coords) const {\n  set_number_of_grid_points(inv_jacobian_out, source_coords);\n  // Most of the inverse jacobian components are zero.\n  for (auto& jac_component : *inv_jacobian_out) {\n    jac_component = 0.0;\n  }\n\n  // dxbar/dx\n  get<0, 0>(*inv_jacobian_out) = 1.0 / radius_;\n  // dybar/dy\n  get<1, 1>(*inv_jacobian_out) = 1.0 / radius_;\n}\n\nstd::optional<std::array<double, 3>> FlatEndcap::inverse(\n    const std::array<double, 3>& target_coords, const double sigma_in) const {\n  const double xbar = (target_coords[0] - center_[0]) / radius_;\n  const double ybar = (target_coords[1] - center_[1]) / radius_;\n  const double zbar = 2.0 * sigma_in - 1.0;\n\n  if (abs(zbar) > 1.0 and not equal_within_roundoff(abs(zbar), 1.0)) {\n    return {};\n  }\n  const double rho_squared = square(xbar) + square(ybar);\n  if (rho_squared > 1.0 and not equal_within_roundoff(rho_squared, 1.0)) {\n    return {};\n  }\n\n  return std::array<double, 3>{{xbar, ybar, zbar}};\n}\n\ntemplate <typename T>\nvoid FlatEndcap::sigma(\n    const gsl::not_null<tt::remove_cvref_wrap_t<T>*> sigma_out,\n    const std::array<T, 3>& source_coords) const {\n  *sigma_out = 0.5 * (source_coords[2] + 1.0);\n}\n\ntemplate <typename T>\nvoid FlatEndcap::deriv_sigma(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        deriv_sigma_out,\n    const std::array<T, 3>& source_coords) const {\n  set_number_of_grid_points(deriv_sigma_out, source_coords);\n  (*deriv_sigma_out)[0] = 0.0;\n  (*deriv_sigma_out)[1] = 0.0;\n  (*deriv_sigma_out)[2] = 0.5;\n}\n\ntemplate <typename T>\nvoid FlatEndcap::dxbar_dsigma(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        dxbar_dsigma_out,\n    const std::array<T, 3>& source_coords) const {\n  set_number_of_grid_points(dxbar_dsigma_out, source_coords);\n  (*dxbar_dsigma_out)[0] = 0.0;\n  (*dxbar_dsigma_out)[1] = 0.0;\n  (*dxbar_dsigma_out)[2] = 2.0;\n}\n\nstd::optional<double> FlatEndcap::lambda_tilde(\n    const std::array<double, 3>& parent_mapped_target_coords,\n    const std::array<double, 3>& projection_point,\n    const bool /*source_is_between_focus_and_target*/) const {\n  const double result = (center_[2] - projection_point[2]) /\n                        (parent_mapped_target_coords[2] - projection_point[2]);\n  if (result < 1.0) {\n    return {};\n  }\n  return result;\n}\n\ntemplate <typename T>\nvoid FlatEndcap::deriv_lambda_tilde(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        deriv_lambda_tilde_out,\n    const std::array<T, 3>& target_coords, const T& lambda_tilde,\n    const std::array<double, 3>& projection_point) const {\n  set_number_of_grid_points(deriv_lambda_tilde_out, target_coords);\n\n  (*deriv_lambda_tilde_out)[0] = 0.0;\n  (*deriv_lambda_tilde_out)[1] = 0.0;\n  (*deriv_lambda_tilde_out)[2] =\n      -square(lambda_tilde) / (center_[2] - projection_point[2]);\n}\n\nvoid FlatEndcap::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | center_;\n    p | radius_;\n  }\n}\n\nbool operator==(const FlatEndcap& lhs, const FlatEndcap& rhs) {\n  return lhs.center_ == rhs.center_ and lhs.radius_ == rhs.radius_;\n}\n\nbool operator!=(const FlatEndcap& lhs, const FlatEndcap& rhs) {\n  return not(lhs == rhs);\n}\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void FlatEndcap::forward_map(                                      \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          target_coords,                                                      \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatEndcap::jacobian(                                         \\\n      const gsl::not_null<                                                    \\\n          tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>*> \\\n          jacobian_out,                                                       \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatEndcap::inv_jacobian(                                     \\\n      const gsl::not_null<                                                    \\\n          tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>*> \\\n          inv_jacobian_out,                                                   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatEndcap::sigma(                                            \\\n      const gsl::not_null<tt::remove_cvref_wrap_t<DTYPE(data)>*> sigma_out,   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatEndcap::deriv_sigma(                                      \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          deriv_sigma_out,                                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatEndcap::dxbar_dsigma(                                     \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          dxbar_dsigma_out,                                                   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatEndcap::deriv_lambda_tilde(                               \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          deriv_lambda_tilde_out,                                             \\\n      const std::array<DTYPE(data), 3>& target_coords,                        \\\n      const DTYPE(data) & lambda_tilde,                                       \\\n      const std::array<double, 3>& projection_point) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef INSTANTIATE\n#undef DTYPE\n}  // namespace domain::CoordinateMaps::FocallyLiftedInnerMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedFlatEndcap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// Contains FocallyLiftedInnerMaps\nnamespace domain::CoordinateMaps::FocallyLiftedInnerMaps {\n/*!\n * \\brief A FocallyLiftedInnerMap that maps a 3D unit right cylinder\n *  to a volume that connects a portion of a plane and a spherical\n *  surface.\n *\n * \\details The domain of the map is a 3D unit right cylinder with\n * coordinates \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that\n * \\f$-1\\leq\\bar{z}\\leq 1\\f$ and \\f$\\bar{x}^2+\\bar{y}^2 \\leq\n * 1\\f$.  The range of the map has coordinates \\f$(x,y,z)\\f$.\n *\n * Consider a 2D circle in 3D space that is normal to the \\f$z\\f$ axis\n * and has (3D) center \\f$C^i\\f$ and radius \\f$R\\f$.  `FlatEndcap`\n * provides the following functions:\n *\n * ### forward_map()\n * `forward_map()` maps \\f$(\\bar{x},\\bar{y},\\bar{z}=-1)\\f$ to the interior\n * of the circle.  The arguments to `forward_map()`\n * are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n * `forward_map()` returns \\f$x_0^i\\f$,\n * the 3D coordinates on the circle, which are given by\n *\n * \\f{align}\n * x_0^0 &= R \\bar{x} + C^0,\\\\\n * x_0^1 &= R \\bar{y} + C^1,\\\\\n * x_0^2 &= C^2.\n * \\f}\n *\n * ### sigma\n *\n * \\f$\\sigma\\f$ is a function that is zero on the plane\n * \\f$x^i=x_0^i\\f$ and unity at \\f$\\bar{z}=+1\\f$ (corresponding to the\n * upper surface of the FocallyLiftedMap). We define\n *\n * \\f{align}\n *  \\sigma &= \\frac{\\bar{z}+1}{2}.\n * \\f}\n *\n * ### deriv_sigma\n *\n * `deriv_sigma` returns\n *\n * \\f{align}\n *  \\frac{\\partial \\sigma}{\\partial \\bar{x}^j} &= (0,0,1/2).\n * \\f}\n *\n * ### jacobian\n *\n * `jacobian` returns \\f$\\partial x_0^k/\\partial \\bar{x}^j\\f$.\n * The arguments to `jacobian`\n * are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n *\n * Differentiating Eqs.(1--3) above yields\n *\n * \\f{align*}\n * \\frac{\\partial x_0^0}{\\partial \\bar{x}} &= R,\\\\\n * \\frac{\\partial x_0^1}{\\partial \\bar{y}} &= R,\n * \\f}\n * and all other components are zero.\n *\n * ### inverse\n *\n * `inverse` takes \\f$x_0^i\\f$ and \\f$\\sigma\\f$ as arguments, and\n * returns \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, or a default-constructed\n * `std::optional<std::array<double, 3>>` if\n * \\f$x_0^i\\f$ or \\f$\\sigma\\f$ are outside the range of the map.\n * The formula for the inverse is straightforward:\n *\n * \\f{align}\n *  \\bar{x} &= \\frac{x_0^0-C^0}{R},\\\\\n *  \\bar{y} &= \\frac{x_0^1-C^1}{R},\\\\\n *  \\bar{z} &= 2\\sigma - 1.\n * \\f}\n *\n * If \\f$\\bar{z}\\f$ is outside the range \\f$[-1,1]\\f$ or\n * if \\f$\\bar{x}^2+\\bar{y}^2 > 1\\f$ then we return\n * a default-constructed `std::optional<std::array<double, 3>>`\n *\n * ### lambda_tilde\n *\n * `lambda_tilde` takes as arguments a point \\f$x^i\\f$ and a projection point\n *  \\f$P^i\\f$, and computes \\f$\\tilde{\\lambda}\\f$, the solution to\n *\n * \\f{align} x_0^i = P^i + (x^i - P^i) \\tilde{\\lambda}.\\f}\n *\n * Since \\f$x_0^i\\f$ must lie on the plane \\f$x_0^3=C^3\\f$,\n *\n * \\f{align} \\tilde{\\lambda} &= \\frac{C^3-P^3}{x^3-P^3}.\\f}\n *\n * For `FocallyLiftedInnerMaps::FlatEndcap`, \\f$x^i\\f$ is always between\n * \\f$P^i\\f$ and \\f$x_0^i\\f$, so \\f$\\tilde{\\lambda}\\ge 1\\f$.\n * Therefore a default-constructed `std::optional<double>` is returned\n * if \\f$\\tilde{\\lambda}\\f$ is less than unity (meaning that \\f$x^i\\f$\n * is outside the range of the map).\n *\n * ### deriv_lambda_tilde\n *\n * `deriv_lambda_tilde` takes as arguments \\f$x_0^i\\f$, a projection point\n *  \\f$P^i\\f$, and \\f$\\tilde{\\lambda}\\f$, and\n *  returns \\f$\\partial \\tilde{\\lambda}/\\partial x^i\\f$. We have\n *\n * \\f{align}\n * \\frac{\\partial\\tilde{\\lambda}}{\\partial x^3} =\n * -\\frac{C^3-P^3}{(x^3-P^3)^2} = -\\frac{\\tilde{\\lambda}^2}{C^3-P^3},\n * \\f}\n * and other components are zero.\n *\n * ### inv_jacobian\n *\n * `inv_jacobian` returns \\f$\\partial \\bar{x}^i/\\partial x_0^k\\f$,\n *  where \\f$\\sigma\\f$ is held fixed.\n *  The arguments to `inv_jacobian`\n *  are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n *\n * The nonzero components are\n * \\f{align}\n * \\frac{\\partial \\bar{x}}{\\partial x_0^0} &= \\frac{1}{R},\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^1} &= \\frac{1}{R}.\n * \\f}\n *\n * ### dxbar_dsigma\n *\n * `dxbar_dsigma` returns \\f$\\partial \\bar{x}^i/\\partial \\sigma\\f$,\n *  where \\f$x_0^i\\f$ is held fixed.\n *\n * From Eq. (6) we have\n *\n * \\f{align}\n * \\frac{\\partial \\bar{x}^i}{\\partial \\sigma} &= (0,0,2).\n * \\f}\n *\n */\nclass FlatEndcap {\n public:\n  FlatEndcap(const std::array<double, 3>& center, double radius);\n\n  FlatEndcap() = default;\n  ~FlatEndcap() = default;\n  FlatEndcap(FlatEndcap&&) = default;\n  FlatEndcap(const FlatEndcap&) = default;\n  FlatEndcap& operator=(const FlatEndcap&) = default;\n  FlatEndcap& operator=(FlatEndcap&&) = default;\n\n  template <typename T>\n  void forward_map(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          target_coords,\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords, double sigma_in) const;\n\n  template <typename T>\n  void jacobian(const gsl::not_null<\n                    tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>*>\n                    jacobian_out,\n                const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void inv_jacobian(const gsl::not_null<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3,\n                                                 Frame::NoFrame>*>\n                        inv_jacobian_out,\n                    const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void sigma(const gsl::not_null<tt::remove_cvref_wrap_t<T>*> sigma_out,\n             const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void deriv_sigma(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          deriv_sigma_out,\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void dxbar_dsigma(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          dxbar_dsigma_out,\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<double> lambda_tilde(\n      const std::array<double, 3>& parent_mapped_target_coords,\n      const std::array<double, 3>& projection_point,\n      bool source_is_between_focus_and_target) const;\n\n  template <typename T>\n  void deriv_lambda_tilde(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          deriv_lambda_tilde_out,\n      const std::array<T, 3>& target_coords, const T& lambda_tilde,\n      const std::array<double, 3>& projection_point) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const FlatEndcap& lhs, const FlatEndcap& rhs);\n  std::array<double, 3> center_{};\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n};\nbool operator!=(const FlatEndcap& lhs, const FlatEndcap& rhs);\n}  // namespace domain::CoordinateMaps::FocallyLiftedInnerMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedFlatSide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/FocallyLiftedFlatSide.hpp\"\n\n#include <array>\n#include <cmath>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace domain::CoordinateMaps::FocallyLiftedInnerMaps {\n\nFlatSide::FlatSide(const std::array<double, 3>& center,\n                   const double inner_radius, const double outer_radius)\n    : center_(center),\n      inner_radius_(inner_radius),\n      outer_radius_(outer_radius) {\n  ASSERT(not equal_within_roundoff(inner_radius, 0.0),\n         \"Cannot have zero radius.  Note that this ASSERT implicitly \"\n         \"assumes that the radius has a scale of roughly unity.  Therefore, \"\n         \"this ASSERT may trigger in the case where we intentionally want \"\n         \"an entire domain that is very small.  If we really want small \"\n         \"domains, then this ASSERT should be modified.\");\n  ASSERT(outer_radius > inner_radius,\n         \"Outer radius should be larger than inner radius. Inner radius: \"\n             << inner_radius << \" outer radius: \" << outer_radius);\n}\n\ntemplate <typename T>\nvoid FlatSide::forward_map(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        target_coords,\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  ReturnType& x = (*target_coords)[0];\n  ReturnType& y = (*target_coords)[1];\n  ReturnType& z = (*target_coords)[2];\n\n  // Use z and y as temporary storage to avoid allocations,\n  // before setting them to their actual values.\n  z = sqrt(square(xbar) + square(ybar));\n  y = (inner_radius_ + (outer_radius_ - inner_radius_) * (z - 1.0)) / z;\n\n  x = y * xbar + center_[0];\n  y = y * ybar + center_[1];\n  z = center_[2];\n}\n\ntemplate <typename T>\nvoid FlatSide::jacobian(const gsl::not_null<tnsr::Ij<tt::remove_cvref_wrap_t<T>,\n                                                     3, Frame::NoFrame>*>\n                            jacobian_out,\n                        const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n\n  set_number_of_grid_points(jacobian_out, source_coords);\n\n  // Use part of Jacobian as temp storage to reduce allocations.\n  get<1, 1>(*jacobian_out) = 1.0 / cube(sqrt(square(xbar) + square(ybar)));\n\n  // dx/dxbar\n  get<0, 0>(*jacobian_out) = (2.0 * inner_radius_ - outer_radius_) *\n                                 square(ybar) * get<1, 1>(*jacobian_out) +\n                             (outer_radius_ - inner_radius_);\n  // dx/dybar\n  get<0, 1>(*jacobian_out) = (outer_radius_ - 2.0 * inner_radius_) * xbar *\n                             ybar * get<1, 1>(*jacobian_out);\n  // dy/dxbar\n  get<1, 0>(*jacobian_out) = get<0, 1>(*jacobian_out);\n  // dy/dybar\n  get<1, 1>(*jacobian_out) = (2.0 * inner_radius_ - outer_radius_) *\n                                 square(xbar) * get<1, 1>(*jacobian_out) +\n                             (outer_radius_ - inner_radius_);\n\n  // Set remaining components to zero\n  for (size_t i = 0; i < 3; ++i) {\n    jacobian_out->get(i, 2) = 0.0;\n  }\n  for (size_t i = 0; i < 2; ++i) {\n    jacobian_out->get(2, i) = 0.0;\n  }\n}\n\ntemplate <typename T>\nvoid FlatSide::inv_jacobian(\n    const gsl::not_null<\n        tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>*>\n        inv_jacobian_out,\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n\n  set_number_of_grid_points(inv_jacobian_out, source_coords);\n\n  // Use part of inverse Jacobian for temp storage to avoid allocations.\n  const double tmp =\n      (2.0 * inner_radius_ - outer_radius_) / (outer_radius_ - inner_radius_);\n  // Denominator in the next line is guaranteed to be nonzero.\n  get<0, 1>(*inv_jacobian_out) = 1.0 / sqrt(square(xbar) + square(ybar));\n  get<0, 1>(*inv_jacobian_out) =\n      cube(get<0, 1>(*inv_jacobian_out)) * tmp /\n      ((outer_radius_ - inner_radius_) +\n       (2.0 * inner_radius_ - outer_radius_) * get<0, 1>(*inv_jacobian_out));\n\n  // dxbar/dx\n  get<0, 0>(*inv_jacobian_out) = -square(ybar) * get<0, 1>(*inv_jacobian_out) +\n                                 1.0 / (outer_radius_ - inner_radius_);\n  // dybar/dy\n  get<1, 1>(*inv_jacobian_out) = -square(xbar) * get<0, 1>(*inv_jacobian_out) +\n                                 1.0 / (outer_radius_ - inner_radius_);\n  // dxbar/dy\n  get<0, 1>(*inv_jacobian_out) *= xbar * ybar;\n  // dybar/dx\n  get<1, 0>(*inv_jacobian_out) = get<0, 1>(*inv_jacobian_out);\n\n  // Set remaining components to zero\n  for (size_t i = 0; i < 3; ++i) {\n    inv_jacobian_out->get(i, 2) = 0.0;\n  }\n  for (size_t i = 0; i < 2; ++i) {\n    inv_jacobian_out->get(2, i) = 0.0;\n  }\n}\n\nstd::optional<std::array<double, 3>> FlatSide::inverse(\n    const std::array<double, 3>& target_coords, const double sigma_in) const {\n  const double x = (target_coords[0] - center_[0]);\n  const double y = (target_coords[1] - center_[1]);\n  const double rho = sqrt(square(x) + square(y));\n  const double rhobar =\n      (rho - inner_radius_) / (outer_radius_ - inner_radius_) + 1.0;\n  if (rhobar < 1.0 and not equal_within_roundoff(rhobar, 1.0)) {\n    return {};\n  }\n  if (rhobar > 2.0 and not equal_within_roundoff(rhobar, 2.0)) {\n    return {};\n  }\n  const double xbar = rhobar * x / rho;\n  const double ybar = rhobar * y / rho;\n  const double zbar = 2.0 * sigma_in - 1.0;\n\n  if (abs(zbar) > 1.0 and not equal_within_roundoff(abs(zbar), 1.0)) {\n    return {};\n  }\n\n  return std::array<double, 3>{{xbar, ybar, zbar}};\n}\n\ntemplate <typename T>\nvoid FlatSide::sigma(const gsl::not_null<tt::remove_cvref_wrap_t<T>*> sigma_out,\n                     const std::array<T, 3>& source_coords) const {\n  *sigma_out = 0.5 * (source_coords[2] + 1.0);\n}\n\ntemplate <typename T>\nvoid FlatSide::deriv_sigma(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        deriv_sigma_out,\n    const std::array<T, 3>& source_coords) const {\n  set_number_of_grid_points(deriv_sigma_out, source_coords);\n  (*deriv_sigma_out)[0] = 0.0;\n  (*deriv_sigma_out)[1] = 0.0;\n  (*deriv_sigma_out)[2] = 0.5;\n}\n\ntemplate <typename T>\nvoid FlatSide::dxbar_dsigma(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        dxbar_dsigma_out,\n    const std::array<T, 3>& source_coords) const {\n  set_number_of_grid_points(dxbar_dsigma_out, source_coords);\n  (*dxbar_dsigma_out)[0] = 0.0;\n  (*dxbar_dsigma_out)[1] = 0.0;\n  (*dxbar_dsigma_out)[2] = 2.0;\n}\n\nstd::optional<double> FlatSide::lambda_tilde(\n    const std::array<double, 3>& parent_mapped_target_coords,\n    const std::array<double, 3>& projection_point,\n    const bool /*source_is_between_focus_and_target*/) const {\n  const double result = (center_[2] - projection_point[2]) /\n                        (parent_mapped_target_coords[2] - projection_point[2]);\n  if (result < 1.0) {\n    return {};\n  }\n  return result;\n}\n\ntemplate <typename T>\nvoid FlatSide::deriv_lambda_tilde(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        deriv_lambda_tilde_out,\n    const std::array<T, 3>& target_coords, const T& lambda_tilde,\n    const std::array<double, 3>& projection_point) const {\n  set_number_of_grid_points(deriv_lambda_tilde_out, target_coords);\n  (*deriv_lambda_tilde_out)[0] = 0.0;\n  (*deriv_lambda_tilde_out)[1] = 0.0;\n  (*deriv_lambda_tilde_out)[2] =\n      -square(lambda_tilde) / (center_[2] - projection_point[2]);\n}\n\nvoid FlatSide::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | center_;\n    p | inner_radius_;\n    p | outer_radius_;\n  }\n}\n\nbool operator==(const FlatSide& lhs, const FlatSide& rhs) {\n  return lhs.center_ == rhs.center_ and\n         lhs.inner_radius_ == rhs.inner_radius_ and\n         lhs.outer_radius_ == rhs.outer_radius_;\n}\n\nbool operator!=(const FlatSide& lhs, const FlatSide& rhs) {\n  return not(lhs == rhs);\n}\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void FlatSide::forward_map(                                        \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          target_coords,                                                      \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatSide::jacobian(                                           \\\n      const gsl::not_null<                                                    \\\n          tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>*> \\\n          jacobian_out,                                                       \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatSide::inv_jacobian(                                       \\\n      const gsl::not_null<                                                    \\\n          tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>*> \\\n          inv_jacobian_out,                                                   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatSide::sigma(                                              \\\n      const gsl::not_null<tt::remove_cvref_wrap_t<DTYPE(data)>*> sigma_out,   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatSide::deriv_sigma(                                        \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          deriv_sigma_out,                                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatSide::dxbar_dsigma(                                       \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          dxbar_dsigma_out,                                                   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void FlatSide::deriv_lambda_tilde(                                 \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          deriv_lambda_tilde_out,                                             \\\n      const std::array<DTYPE(data), 3>& target_coords,                        \\\n      const DTYPE(data) & lambda_tilde,                                       \\\n      const std::array<double, 3>& projection_point) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef INSTANTIATE\n#undef DTYPE\n}  // namespace domain::CoordinateMaps::FocallyLiftedInnerMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedFlatSide.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// Contains FocallyLiftedInnerMaps\nnamespace domain::CoordinateMaps::FocallyLiftedInnerMaps {\n/*!\n * \\brief A FocallyLiftedInnerMap that maps a 3D unit right cylinder\n *  to a volume that connects a 2D annulus to a spherical\n *  surface.\n *\n * \\details The domain of the map is a 3D unit right cylinder with\n * coordinates \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that\n * \\f$-1\\leq\\bar{z}\\leq 1\\f$ and \\f$1\\leq \\bar{x}^2+\\bar{y}^2 \\leq\n * 4\\f$.  The range of the map has coordinates \\f$(x,y,z)\\f$.\n *\n * Consider a 2D annulus in 3D space\n * oriented normal to the \\f$z\\f$ axis.  The inner and outer radii\n * of the annulus are \\f$R_\\mathrm{in}\\f$ and \\f$R_\\mathrm{out}\\f$, and the\n * (3D) center of the annulus is \\f$C^i\\f$.\n * `FlatSide` provides the following functions:\n *\n * ### forward_map()\n * `forward_map()` maps \\f$(\\bar{x},\\bar{y},\\bar{z}=-1)\\f$ to the interior\n * of the annulus.  The arguments to `forward_map()`\n * are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n * `forward_map()` returns \\f$x_0^i\\f$,\n * the 3D coordinates on the annulus, which are given by\n *\n * \\f{align}\n * x_0^0 &= \\left(R_\\mathrm{in}+(R_\\mathrm{out}-R_\\mathrm{in})\n *    (\\bar{\\rho}-1)\\right)\n *    \\frac{\\bar{x}}{\\bar{\\rho}} + C^0, \\label{eq:forward_map_x}\\\\\n * x_0^1 &= \\left(R_\\mathrm{in}+(R_\\mathrm{out}-R_\\mathrm{in})\n *    (\\bar{\\rho}-1)\\right)\n *    \\frac{\\bar{y}}{\\bar{\\rho}} + C^1,\\\\\n * x_0^2 &= C^2 \\label{eq:forward_map_z},\n * \\f}\n *\n * where\n *\n * \\f{align} \\bar{\\rho} = \\sqrt{\\bar{x}^2+\\bar{y}^2}.\\label{eq:rhobar}\\f}\n *\n * ### sigma\n *\n * \\f$\\sigma\\f$ is a function that is zero on the sphere\n * \\f$x^i=x_0^i\\f$ and unity at \\f$\\bar{z}=+1\\f$ (corresponding to the\n * upper surface of the FocallyLiftedMap). We define\n *\n * \\f{align}\n *  \\sigma &= \\frac{\\bar{z}+1}{2}.\n * \\f}\n *\n * ### deriv_sigma\n *\n * `deriv_sigma` returns\n *\n * \\f{align}\n *  \\frac{\\partial \\sigma}{\\partial \\bar{x}^j} &= (0,0,1/2).\n *  \\label{eq:deriv_sigma}\n * \\f}\n *\n * ### jacobian\n *\n * `jacobian` returns \\f$\\partial x_0^k/\\partial \\bar{x}^j\\f$.\n * The arguments to `jacobian`\n * are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n *\n * Differentiating\n * Eqs. (\\f$\\ref{eq:forward_map_x}\\f$--\\f$\\ref{eq:forward_map_z}\\f$)\n * above yields\n *\n * \\f{align*}\n * \\frac{\\partial x_0^0}{\\partial \\bar{x}} &=\n * R_\\mathrm{out}-R_\\mathrm{in} + (2 R_\\mathrm{in}-R_\\mathrm{out})\n * \\frac{\\bar{y}^2}{\\bar{\\rho}^3},\\\\\n * \\frac{\\partial x_0^0}{\\partial \\bar{y}} &=\n * -(2 R_\\mathrm{in}-R_\\mathrm{out})\n * \\frac{\\bar{x}\\bar{y}}{\\bar{\\rho}^3},\\\\\n * \\frac{\\partial x_0^1}{\\partial \\bar{x}} &=\n * -(2 R_\\mathrm{in}-R_\\mathrm{out})\n * \\frac{\\bar{x}\\bar{y}}{\\bar{\\rho}^3},\\\\\n * \\frac{\\partial x_0^1}{\\partial \\bar{y}} &=\n * R_\\mathrm{out}-R_\\mathrm{in} + (2 R_\\mathrm{in}-R_\\mathrm{out})\n * \\frac{\\bar{x}^2}{\\bar{\\rho}^3},\\\\\n * \\f}\n * and all other components are zero.\n *\n * ### inverse\n *\n * `inverse` takes \\f$x_0^i\\f$ and \\f$\\sigma\\f$ as arguments, and\n * returns \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, or a default-constructed\n * `std::optional<std::array<double, 3>>` if\n * \\f$x_0^i\\f$ or \\f$\\sigma\\f$ are outside the range of the map.\n *\n * Let\n * \\f{align}\n *  \\rho = \\sqrt{(x_0^0-C^0)^2+(x_0^1-C^1)^2}.\n *  \\label{eq:rho}\n * \\f}\n *\n * Then\n * \\f{align}\n *  \\bar{x} &= \\frac{x_0^0-C^0}{\\rho}\n *  \\frac{\\rho+R_\\mathrm{out}-2 R_\\mathrm{in}}{R_\\mathrm{out}-R_\\mathrm{in}},\\\\\n *  \\bar{y} &= \\frac{x_0^1-C^1}{\\rho}\n *  \\frac{\\rho+R_\\mathrm{out}-2 R_\\mathrm{in}}{R_\\mathrm{out}-R_\\mathrm{in}},\\\\\n *  \\bar{z} &= 2\\sigma - 1.\n * \\f}\n *\n * Note that \\f$\\rho\\f$ in Eq. (\\f$\\ref{eq:rho}\\f$) can be written\n * \\f{align}\n * \\rho = R_\\mathrm{in}+(R_\\mathrm{out}-R_\\mathrm{in})(\\bar{\\rho}-1),\n * \\label{eq:rho_from_rhobar}\n * \\f}\n * where \\f$\\bar{\\rho}\\f$ is given by Eq. (\\f$\\ref{eq:rhobar}\\f$).\n *\n * If \\f$\\bar{z}\\f$ is outside the range \\f$[-1,1]\\f$ or\n * if \\f$\\bar{x}^2+\\bar{y}^2\\f$ is less than 1 or greater than 4\n * then we return a default-constructed\n * `std::optional<std::array<double, 3>>`.\n *\n * ### lambda_tilde\n *\n * `lambda_tilde` takes as arguments a point \\f$x^i\\f$ and a projection point\n *  \\f$P^i\\f$, and computes \\f$\\tilde{\\lambda}\\f$, the solution to\n *\n * \\f{align} x_0^i = P^i + (x^i - P^i) \\tilde{\\lambda}.\\f}\n *\n * Since \\f$x_0^i\\f$ must lie on the plane \\f$x_0^3=C^3\\f$,\n *\n * \\f{align} \\tilde{\\lambda} &= \\frac{C^3-P^3}{x^3-P^3}.\\f}\n *\n * If \\f$\\tilde{\\lambda}\\f$ is less than unity (indicating that the\n * supplied point is outside the range of the map), then a\n * default-constructed `std::optional<double>` is returned.\n *\n * ### deriv_lambda_tilde\n *\n * `deriv_lambda_tilde` takes as arguments \\f$x_0^i\\f$, a projection point\n *  \\f$P^i\\f$, and \\f$\\tilde{\\lambda}\\f$, and\n *  returns \\f$\\partial \\tilde{\\lambda}/\\partial x^i\\f$. We have\n *\n * \\f{align}\n * \\frac{\\partial\\tilde{\\lambda}}{\\partial x^3} =\n * -\\frac{C^3-P^3}{(x^3-P^3)^2} = -\\frac{\\tilde{\\lambda}^2}{C^3-P^3},\n * \\f}\n * and other components are zero.\n *\n * ### inv_jacobian\n *\n * `inv_jacobian` returns \\f$\\partial \\bar{x}^i/\\partial x_0^k\\f$,\n *  where \\f$\\sigma\\f$ is held fixed.\n *  The arguments to `inv_jacobian`\n *  are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, but \\f$\\bar{z}\\f$ is ignored.\n *\n * The nonzero components are\n * \\f{align}\n * \\frac{\\partial \\bar{x}}{\\partial x_0^0} &=\n *  \\frac{1}{R_\\mathrm{out}-R_\\mathrm{in}} + \\frac{\\bar{y}^2}{\\bar{\\rho}^2\\rho}\n *  \\frac{R_\\mathrm{out}-2 R_\\mathrm{in}}{R_\\mathrm{out}-R_\\mathrm{in}},\\\\\n * \\frac{\\partial \\bar{x}}{\\partial x_0^1} &=\n *  - \\frac{\\bar{x}\\bar{y}}{\\bar{\\rho}^2\\rho}\n *  \\frac{R_\\mathrm{out}-2 R_\\mathrm{in}}{R_\\mathrm{out}-R_\\mathrm{in}},\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^1} &=\n *  \\frac{1}{R_\\mathrm{out}-R_\\mathrm{in}} + \\frac{\\bar{x}^2}{\\bar{\\rho}^2\\rho}\n *  \\frac{R_\\mathrm{out}-2 R_\\mathrm{in}}{R_\\mathrm{out}-R_\\mathrm{in}},\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^0} &=\n *  - \\frac{\\bar{x}\\bar{y}}{\\bar{\\rho}^2\\rho}\n *  \\frac{R_\\mathrm{out}-2 R_\\mathrm{in}}{R_\\mathrm{out}-R_\\mathrm{in}},\n * \\f}\n * where \\f$\\rho\\f$ is computed from Eq. (\\f$\\ref{eq:rho_from_rhobar}\\f$).\n *\n * ### dxbar_dsigma\n *\n * `dxbar_dsigma` returns \\f$\\partial \\bar{x}^i/\\partial \\sigma\\f$,\n *  where \\f$x_0^i\\f$ is held fixed.\n *\n * From Eq. (\\f$\\ref{eq:deriv_sigma}\\f$) we have\n *\n * \\f{align}\n * \\frac{\\partial \\bar{x}^i}{\\partial \\sigma} &= (0,0,2).\n * \\f}\n *\n */\nclass FlatSide {\n public:\n  FlatSide(const std::array<double, 3>& center, const double inner_radius,\n           const double outer_radius);\n\n  FlatSide() = default;\n  ~FlatSide() = default;\n  FlatSide(FlatSide&&) = default;\n  FlatSide(const FlatSide&) = default;\n  FlatSide& operator=(const FlatSide&) = default;\n  FlatSide& operator=(FlatSide&&) = default;\n\n  template <typename T>\n  void forward_map(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          target_coords,\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords, double sigma_in) const;\n\n  template <typename T>\n  void jacobian(const gsl::not_null<\n                    tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>*>\n                    jacobian_out,\n                const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void inv_jacobian(const gsl::not_null<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3,\n                                                 Frame::NoFrame>*>\n                        inv_jacobian_out,\n                    const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void sigma(const gsl::not_null<tt::remove_cvref_wrap_t<T>*> sigma_out,\n             const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void deriv_sigma(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          deriv_sigma_out,\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void dxbar_dsigma(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          dxbar_dsigma_out,\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<double> lambda_tilde(\n      const std::array<double, 3>& parent_mapped_target_coords,\n      const std::array<double, 3>& projection_point,\n      bool source_is_between_focus_and_target) const;\n\n  template <typename T>\n  void deriv_lambda_tilde(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          deriv_lambda_tilde_out,\n      const std::array<T, 3>& target_coords, const T& lambda_tilde,\n      const std::array<double, 3>& projection_point) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const FlatSide& lhs, const FlatSide& rhs);\n  std::array<double, 3> center_{};\n  double inner_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double outer_radius_{std::numeric_limits<double>::signaling_NaN()};\n};\nbool operator!=(const FlatSide& lhs, const FlatSide& rhs);\n}  // namespace domain::CoordinateMaps::FocallyLiftedInnerMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/FocallyLiftedMap.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedEndcap.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedFlatEndcap.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedFlatSide.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMapHelpers.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedSide.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\n\ntemplate <typename InnerMap>\nFocallyLiftedMap<InnerMap>::FocallyLiftedMap(\n    const std::array<double, 3>& center,\n    const std::array<double, 3>& proj_center, double radius,\n    const bool source_is_between_focus_and_target, InnerMap inner_map)\n    : center_(center),\n      proj_center_(proj_center),\n      radius_(radius),\n      source_is_between_focus_and_target_(source_is_between_focus_and_target),\n      inner_map_(std::move(inner_map)) {}\n\ntemplate <typename InnerMap>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3>\nFocallyLiftedMap<InnerMap>::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  // Temporary variable that will be re-used to avoid extra\n  // allocations.\n  ReturnType temp_scalar{};\n\n  // lower_coords are the mapped coords on the surface.\n  std::array<ReturnType, 3> lower_coords{};\n  inner_map_.forward_map(&lower_coords, source_coords);\n\n  // upper_coords are the mapped coords on the surface of the sphere.\n  ReturnType& lambda = temp_scalar;\n  FocallyLiftedMapHelpers::scale_factor(\n      &lambda, lower_coords, proj_center_, center_, radius_,\n      source_is_between_focus_and_target_);\n\n  std::array<ReturnType, 3> upper_coords{};\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(upper_coords, i) =\n        gsl::at(proj_center_, i) +\n        (gsl::at(lower_coords, i) - gsl::at(proj_center_, i)) * lambda;\n  }\n\n  // mapped_coords goes linearly from lower_coords to upper_coords\n  // as sigma goes from 0 to 1.\n  ReturnType& sigma = temp_scalar; // sigma shares memory with lambda.\n  inner_map_.sigma(&sigma, source_coords);\n\n  // Use upper_coords to store result, so as to save an allocation.\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(upper_coords, i) =\n        gsl::at(lower_coords, i) +\n        (gsl::at(upper_coords, i) - gsl::at(lower_coords, i)) * sigma;\n  }\n  return upper_coords;\n}\n\ntemplate <typename InnerMap>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nFocallyLiftedMap<InnerMap>::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  // Use these variables to reduce allocations.\n  std::array<ReturnType, 3> temp_vector_one{};\n  std::array<ReturnType, 3> temp_vector_two{};\n\n  auto jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  // lower_coords are the mapped coords on the surface.\n  std::array<ReturnType, 3> lower_coords{};\n  inner_map_.forward_map(&lower_coords, source_coords);\n\n  ReturnType lambda{};\n  FocallyLiftedMapHelpers::scale_factor(\n      &lambda, lower_coords, proj_center_, center_, radius_,\n      source_is_between_focus_and_target_);\n\n  // upper_coords are the mapped coords on the surface of the sphere.\n  std::array<ReturnType, 3>& upper_coords = temp_vector_one;\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(upper_coords, i) =\n        gsl::at(proj_center_, i) +\n        (gsl::at(lower_coords, i) - gsl::at(proj_center_, i)) * lambda;\n  }\n\n  std::array<ReturnType, 3>& d_lambda_d_lower_coords = temp_vector_two;\n  FocallyLiftedMapHelpers::d_scale_factor_d_src_point<ReturnType>(\n      &d_lambda_d_lower_coords, upper_coords, proj_center_, center_, lambda);\n\n  // Put the inner map's jacobian temporarily into jacobian_matrix.\n  // This saves an allocation.\n  inner_map_.jacobian(&jacobian_matrix, source_coords);\n\n  // Re-use memory of upper_coords in computing sigma_d_lambda_d_xbar.\n  // Don't multiply by sigma yet because we don't know it.\n  std::array<ReturnType, 3>& sigma_d_lambda_d_xbar = temp_vector_one;\n  for (size_t j = 0; j < 3; ++j) {\n    gsl::at(sigma_d_lambda_d_xbar, j) =\n        d_lambda_d_lower_coords[0] * jacobian_matrix.get(0, j);\n    for (size_t k = 1; k < 3; ++k) {  // First iteration split out above.\n      gsl::at(sigma_d_lambda_d_xbar, j) +=\n          gsl::at(d_lambda_d_lower_coords, k) * jacobian_matrix.get(k, j);\n    }\n  }\n\n  // temp_vector_two isn't used anymore, so use its memory for sigma.\n  ReturnType& sigma = temp_vector_two[0];\n  inner_map_.sigma(&sigma, source_coords);\n\n  // Now complete computation of sigma_d_lambda_d_xbar.\n  for (size_t j = 0; j < 3; ++j) {\n    gsl::at(sigma_d_lambda_d_xbar, j) *= sigma;\n  }\n\n  // Do the easiest of the terms involving the inner map,\n  // i.e. the first term in Eq. (6) in the documentation.\n  ReturnType& lambda_factor = temp_vector_two[1];\n  lambda_factor = 1.0 - sigma + lambda * sigma;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      jacobian_matrix.get(i, j) *= lambda_factor;\n    }\n  }\n\n  // Do the deriv sigma term, the third term in Eq. (6) in the\n  // documentation.\n  // Note that we explicitly substitute for upper_coords below\n  // because we have re-used its memory.\n  std::array<ReturnType, 3>& d_sigma = temp_vector_two;\n  inner_map_.deriv_sigma(&d_sigma, source_coords);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      jacobian_matrix.get(i, j) +=\n          gsl::at(d_sigma, j) *\n          (gsl::at(proj_center_, i) +\n           (gsl::at(lower_coords, i) - gsl::at(proj_center_, i)) * lambda -\n           gsl::at(lower_coords, i));\n    }\n  }\n\n  // Do the second term in Eq. (6) in the documentation.\n  for (size_t j = 0; j < 3; ++j) {\n    for (size_t i = 0; i < 3; ++i) {\n      jacobian_matrix.get(i, j) +=\n          gsl::at(sigma_d_lambda_d_xbar, j) *\n          (gsl::at(lower_coords, i) - gsl::at(proj_center_, i));\n    }\n  }\n\n  return jacobian_matrix;\n}\n\ntemplate <typename InnerMap>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nFocallyLiftedMap<InnerMap>::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  // lower_coords are the mapped coords on the surface.\n  std::array<ReturnType, 3> lower_coords{};\n  inner_map_.forward_map(&lower_coords, source_coords);\n\n  ReturnType lambda{};\n  FocallyLiftedMapHelpers::scale_factor(\n      &lambda, lower_coords, proj_center_, center_, radius_,\n      source_is_between_focus_and_target_);\n\n  // upper_coords are the mapped coords on the surface of the sphere.\n  std::array<ReturnType, 3> upper_coords{};\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(upper_coords, i) =\n        gsl::at(proj_center_, i) +\n        (gsl::at(lower_coords, i) - gsl::at(proj_center_, i)) * lambda;\n  }\n\n  // Derivative of lambda\n  std::array<ReturnType, 3> d_lambda_d_lower_coords{};\n  FocallyLiftedMapHelpers::d_scale_factor_d_src_point<ReturnType>(\n      &d_lambda_d_lower_coords, upper_coords, proj_center_, center_, lambda);\n\n  // Lambda_tilde is the scale factor between mapped coords and lower coords.\n  // We can compute it with a shortcut because there is a relationship\n  // between lambda, lambda_tilde, and sigma.\n  ReturnType sigma {};\n  inner_map_.sigma(&sigma, source_coords);\n  const ReturnType lambda_tilde = 1.0 / (1.0 - sigma * (1.0 - lambda));\n\n  // Derivative of lambda_tilde\n  std::array<ReturnType, 3> d_lambda_tilde_d_mapped_coords{};\n  inner_map_.deriv_lambda_tilde(&d_lambda_tilde_d_mapped_coords, lower_coords,\n                                lambda_tilde, proj_center_);\n\n  // Deriv of x_0 with respect to x\n  auto dx_inner_dx =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      dx_inner_dx.get(i, j) = gsl::at(d_lambda_tilde_d_mapped_coords, j) *\n                         (gsl::at(lower_coords, i) - gsl::at(proj_center_, i)) /\n                         lambda_tilde;\n    }\n    dx_inner_dx.get(i, i) += lambda_tilde;\n  }\n\n  // Deriv of sigma with respect to x,y,z\n  auto d_sigma_d_mapped_coords =\n      make_with_value<tnsr::i<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          sigma, 0.0);\n  ReturnType tmp{};\n  for (size_t i = 0; i < 3; ++i) {\n    tmp = d_lambda_d_lower_coords[0] * dx_inner_dx.get(0, i);\n    for (size_t j = 1; j < 3; ++j) {  // first iteration factored out above.\n      tmp += gsl::at(d_lambda_d_lower_coords, j) * dx_inner_dx.get(j, i);\n    }\n    d_sigma_d_mapped_coords.get(i) =\n        (sigma * tmp +\n         gsl::at(d_lambda_tilde_d_mapped_coords, i) / square(lambda_tilde)) /\n        (1.0 - lambda);\n  }\n\n  tnsr::Ij<ReturnType, 3, Frame::NoFrame> dxbar_dx_inner{};\n  inner_map_.inv_jacobian(&dxbar_dx_inner, source_coords);\n  std::array<tt::remove_cvref_wrap_t<T>, 3> dxbar_dsigma{};\n  inner_map_.dxbar_dsigma(&dxbar_dsigma, source_coords);\n\n  auto inv_jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        inv_jacobian_matrix.get(i, j) +=\n            dxbar_dx_inner.get(i, k) * dx_inner_dx.get(k, j);\n      }\n      inv_jacobian_matrix.get(i, j) +=\n          gsl::at(dxbar_dsigma, i) * d_sigma_d_mapped_coords.get(j);\n    }\n  }\n\n  return inv_jacobian_matrix;\n}\n\ntemplate <typename InnerMap>\nstd::optional<std::array<double, 3>> FocallyLiftedMap<InnerMap>::inverse(\n    const std::array<double, 3>& target_coords) const {\n  // Scale factor taking target_coords to lower_coords.\n  const auto lambda_tilde = inner_map_.lambda_tilde(\n      target_coords, proj_center_, source_is_between_focus_and_target_);\n\n  // Cannot find scale factor, so we are out of range of the map.\n  if (not lambda_tilde) {\n    return std::optional<std::array<double, 3>>{};\n  }\n\n  // Try to find lambda_bar going from target_coords to sphere.\n  const auto lambda_bar = FocallyLiftedMapHelpers::try_scale_factor(\n      target_coords, proj_center_, center_, radius_,\n      not source_is_between_focus_and_target_,\n      source_is_between_focus_and_target_);\n\n  // Cannot find scale factor, so we are out of range of the map.\n  if (not lambda_bar) {\n    return std::optional<std::array<double, 3>>{};\n  }\n\n  // compute sigma in a roundoff-friendly way.\n  double sigma = 0.0;\n  if (equal_within_roundoff(*lambda_tilde, 1.0, 1.e-5)) {\n    // Get sigma correct for sigma near 0\n    sigma =\n        (*lambda_tilde - 1.0) / (*lambda_tilde - *lambda_bar);\n  } else {\n    // Get sigma correct for sigma near 1\n    sigma = (*lambda_bar - 1.0) / (*lambda_tilde - *lambda_bar) +\n            1.0;\n  }\n\n  std::array<double, 3> lower_coords{};\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(lower_coords, i) =\n        gsl::at(proj_center_, i) +\n        (gsl::at(target_coords, i) - gsl::at(proj_center_, i)) *\n            lambda_tilde.value();\n  }\n\n  std::optional<std::array<double, 3>> orig_coords =\n      inner_map_.inverse(lower_coords, sigma);\n\n  // Root polishing.\n  // Here we do a single Newton iteration to get the\n  // inverse to agree with the forward map to the level of machine\n  // roundoff that is required by the unit tests.\n  // Without the root polishing, the unit tests occasionally fail\n  // the 'inverse(map(x))=x' test at a level slightly above roundoff.\n  if (orig_coords) {\n    const auto inv_jac = inv_jacobian(*orig_coords);\n    const auto mapped_coords = operator()(*orig_coords);\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        gsl::at(*orig_coords, i) +=\n            (gsl::at(target_coords, j) - gsl::at(mapped_coords, j)) *\n            inv_jac.get(i, j);\n      }\n    }\n  }\n\n  return orig_coords;\n}\n\ntemplate <typename InnerMap>\nvoid FocallyLiftedMap<InnerMap>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | center_;\n    p | proj_center_;\n    p | radius_;\n    p | source_is_between_focus_and_target_;\n    p | inner_map_;\n  }\n}\n\ntemplate <typename InnerMap>\nbool operator!=(const FocallyLiftedMap<InnerMap>& lhs,\n                const FocallyLiftedMap<InnerMap>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename InnerMap>\nbool operator==(const FocallyLiftedMap<InnerMap>& lhs,\n                const FocallyLiftedMap<InnerMap>& rhs) {\n  return lhs.center_ == rhs.center_ and lhs.proj_center_ == rhs.proj_center_ and\n         lhs.radius_ == rhs.radius_ and\n         lhs.source_is_between_focus_and_target_ ==\n             rhs.source_is_between_focus_and_target_ and\n         lhs.inner_map_ == rhs.inner_map_;\n}\n\n// Explicit instantiations\n#define IMAP(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template class FocallyLiftedMap<IMAP(data)>;                       \\\n  template bool operator==(const FocallyLiftedMap<IMAP(data)>& lhs,  \\\n                           const FocallyLiftedMap<IMAP(data)>& rhs); \\\n  template bool operator!=(const FocallyLiftedMap<IMAP(data)>& lhs,  \\\n                           const FocallyLiftedMap<IMAP(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (FocallyLiftedInnerMaps::Endcap,\n                                      FocallyLiftedInnerMaps::FlatEndcap,\n                                      FocallyLiftedInnerMaps::FlatSide,\n                                      FocallyLiftedInnerMaps::Side))\n\n#undef INSTANTIATE\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  FocallyLiftedMap<IMAP(data)>::operator()(                                  \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  FocallyLiftedMap<IMAP(data)>::jacobian(                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  FocallyLiftedMap<IMAP(data)>::inv_jacobian(                                \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (FocallyLiftedInnerMaps::Endcap,\n                         FocallyLiftedInnerMaps::FlatEndcap,\n                         FocallyLiftedInnerMaps::FlatSide,\n                         FocallyLiftedInnerMaps::Side),\n                        (double, DataVector,\n                         std::reference_wrapper<const double>,\n                         std::reference_wrapper<const DataVector>))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef IMAP\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\ntemplate <typename InnerMap>\nclass FocallyLiftedMap;\n\ntemplate <typename InnerMap>\nbool operator==(const FocallyLiftedMap<InnerMap>& lhs,\n                const FocallyLiftedMap<InnerMap>& rhs);\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ to the volume\n * contained between a 2D surface and the surface of a 2-sphere.\n *\n *\n * \\image html FocallyLifted.svg \"2D representation of focally lifted map.\"\n *\n * \\details We are given the radius \\f$R\\f$ and\n * center \\f$C^i\\f$ of a sphere, and a projection point \\f$P^i\\f$.  Also,\n * through the class defined by the `InnerMap` template parameter,\n * we are given the functions\n * \\f$f^i(\\bar{x},\\bar{y},\\bar{z})\\f$ and\n * \\f$\\sigma(\\bar{x},\\bar{y},\\bar{z})\\f$ defined below; these\n * functions define the mapping from \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$\n * to the 2D surface.\n *\n * The above figure is a 2D representation of the focally lifted map,\n * where the shaded region in \\f$\\bar{x}^i\\f$ coordinates on the left\n * is mapped to the shaded region in the \\f$x^i\\f$ coordinates on the\n * right.  Shown is an arbitrary point \\f$\\bar{x}^i\\f$ that is mapped\n * to \\f$x^i\\f$; for that point, the corresponding \\f$x_0^i\\f$ and\n * \\f$x_1^i\\f$ (defined below) are shown on the right, as is the\n * projection point \\f$P^i\\f$.  Also shown are the level surfaces\n * \\f$\\sigma=0\\f$ and \\f$\\sigma=1\\f$.\n *\n * The input coordinates are labeled \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$.\n * Let \\f$x_0^i = f^i(\\bar{x},\\bar{y},\\bar{z})\\f$ be the three coordinates\n * of the 2D surface in 3D space.\n *\n * Now let \\f$x_1^i\\f$ be a point on the surface of the sphere,\n * constructed so that \\f$P^i\\f$, \\f$x_0^i\\f$, and \\f$x_1^i\\f$ are\n * co-linear.  In particular, \\f$x_1^i\\f$ is determined by the\n * equation\n *\n * \\f{align} x_1^i = P^i + (x_0^i - P^i) \\lambda,\\f}\n *\n * where \\f$\\lambda\\f$ is a scalar factor that depends on \\f$x_0^i\\f$ and\n * that can be computed by solving a quadratic equation.  This quadratic\n * equation is derived by demanding that \\f$x_1^i\\f$ lies on the sphere:\n *\n * \\f{align}\n * |P^i + (x_0^i - P^i) \\lambda - C^i |^2 - R^2 = 0,\n * \\f}\n *\n * where \\f$|A^i|^2\\f$ means \\f$\\delta_{ij} A^i A^j\\f$.\n *\n * The quadratic equation, Eq. (2),\n * takes the usual form \\f$a\\lambda^2+b\\lambda+c=0\\f$,\n * with\n *\n * \\f{align*}\n *  a &= |x_0^i-P^i|^2,\\\\\n *  b &= 2(x_0^i-P^i)(P^j-C^j)\\delta_{ij},\\\\\n *  c &= |P^i-C^i|^2 - R^2.\n * \\f}\n *\n * Now assume that \\f$x_0^i\\f$ lies on a level surface defined\n * by some scalar function \\f$\\sigma(\\bar{x}^i)=0\\f$,\n * where \\f$\\sigma\\f$ is normalized so that \\f$\\sigma=1\\f$ on the sphere.\n * Then, once \\f$\\lambda\\f$ has been computed and \\f$x_1^i\\f$ has\n * been determined, the map is given by\n *\n * \\f{align}x^i = x_0^i + (x_1^i - x_0^i) \\sigma(\\bar{x}^i).\\f}\n *\n * Note that classes using FocallyLiftedMap will place restrictions on\n * \\f$P^i\\f$, \\f$C^i\\f$, \\f$x_0^i\\f$, and \\f$R\\f$.  For example, we\n * demand that \\f$P^i\\f$ does not lie on either the \\f$\\sigma=0\\f$ or\n * \\f$\\sigma=1\\f$ surfaces depicted in the right panel of the figure,\n * and we demand that the \\f$\\sigma=0\\f$ and \\f$\\sigma=1\\f$ surfaces do\n * not intersect; otherwise the map is singular.\n *\n * Also note that the quadratic Eq. (2) typically has more than one\n * root, corresponding to two intersections of the sphere.  The\n * boolean parameter `source_is_between_focus_and_target` that is\n * passed into the constructor of `FocallyLiftedMap` is used to choose the\n * appropriate root, or to error if a suitable\n * root is not found. `source_is_between_focus_and_target` should be\n * true if the source point lies between the projection point\n * \\f$P^i\\f$ and the sphere.  `source_is_between_focus_and_target` is\n * known only by each particular `CoordinateMap` that uses\n * `FocallyLiftedMap`.\n *\n * ### Jacobian\n *\n * Differentiating Eq. (1) above yields\n *\n * \\f{align}\n * \\frac{\\partial x_1^i}{\\partial x_0^j} = \\lambda \\delta^i_j +\n *  (x_0^i - P^i) \\frac{\\partial \\lambda}{\\partial x_0^j}.\n * \\f}\n *\n * and differentiating Eq. (2) and then inserting Eq. (1) yields\n *\n * \\f{align}\n * \\frac{\\partial\\lambda}{\\partial x_0^j} &=\n * \\lambda^2 \\frac{C_j - x_1^j}{|x_1^i - P^i|^2\n * + (x_1^i - P^i)(P_i - C_{i})}.\n * \\f}\n *\n * The Jacobian can be found by differentiating Eq. (3) above and using the\n * chain rule, recognizing that \\f$x_1^i\\f$ depends on \\f$\\bar{x}^i\\f$ only\n * through its dependence on \\f$x_0^i\\f$; this is because there is no explicit\n * dependence on \\f$\\bar{x}^i\\f$ in Eq. (1) (which determines \\f$x_1^i\\f$)\n * or Eq. (2) (which determines \\f$\\lambda\\f$).\n *\n * \\f{align}\n * \\frac{\\partial x^i}{\\partial \\bar{x}^j} &=\n * \\sigma \\frac{\\partial x_1^i}{\\partial x_0^k}\n * \\frac{\\partial x_0^k}{\\partial \\bar{x}^j} +\n * (1-\\sigma)\\frac{\\partial x_0^i}{\\partial \\bar{x}^j}\n * + \\frac{\\partial \\sigma}{\\partial \\bar{x}^j} (x_1^i - x_0^i),\n * \\nonumber \\\\\n * &= (1-\\sigma+\\lambda\\sigma) \\frac{\\partial x_0^i}{\\partial \\bar{x}^j} +\n * \\sigma (x_0^i - P^i) \\frac{\\partial \\lambda}{\\partial x_0^k}\n * \\frac{\\partial x_0^k}{\\partial \\bar{x}^j}\n * + \\frac{\\partial \\sigma}{\\partial \\bar{x}^j} (x_1^i - x_0^i),\n * \\f}\n * where in the last line we have substituted Eq. (4).\n *\n * The class defined by the `InnerMap` template parameter\n * provides the function `deriv_sigma`, which returns\n * \\f$\\partial \\sigma/\\partial \\bar{x}^j\\f$, and the function `jacobian`,\n * which returns \\f$\\partial x_0^k/\\partial \\bar{x}^j\\f$.\n *\n * ### Inverse map.\n *\n * Given \\f$x^i\\f$, we wish to compute \\f$\\bar{x}^i\\f$.\n *\n * We first find the coordinates \\f$x_0^i\\f$ that lie on the 2-surface\n * and are defined such that \\f$P^i\\f$, \\f$x_0^i\\f$,\n * and \\f$x^i\\f$ are co-linear.\n * See the right panel of the above figure.\n * \\f$x_0^i\\f$ is determined by the equation\n *\n * \\f{align} x_0^i = P^i + (x^i - P^i) \\tilde{\\lambda},\\f}\n *\n * where \\f$\\tilde{\\lambda}\\f$ is a scalar factor that depends on\n * \\f$x^i\\f$ and is determined by the class defined by the `InnerMap` template\n * parameter.  `InnerMap`\n * provides a function `lambda_tilde` that takes \\f$x^i\\f$ and\n * \\f$P^i\\f$ as arguments and returns \\f$\\tilde{\\lambda}\\f$ (or\n * a default-constructed `std::optional` if the appropriate\n * \\f$\\tilde{\\lambda}\\f$ cannot be\n * found; this default-constructed value indicates that the point \\f$x^i\\f$ is\n * outside the range of the map).\n *\n * Now consider the coordinates \\f$x_1^i\\f$ that lie on the sphere and\n * are defined such that \\f$P^i\\f$, \\f$x_1^i\\f$, and \\f$x^i\\f$ are\n * co-linear.  See the right panel of the figure.\n * \\f$x_1^i\\f$ is determined by the equation\n *\n * \\f{align} x_1^i = P^i + (x^i - P^i) \\bar{\\lambda},\\f}\n *\n * where \\f$\\bar{\\lambda}\\f$ is a scalar factor that depends on \\f$x^i\\f$ and\n * is the solution of a quadratic\n * that is derived by demanding that \\f$x_1^i\\f$ lies on the sphere:\n *\n * \\f{align}\n * |P^i + (x^i - P^i) \\bar{\\lambda} - C^i |^2 - R^2 = 0.\n * \\f}\n *\n * Eq. (9) is a quadratic equation that\n * takes the usual form \\f$a\\bar{\\lambda}^2+b\\bar{\\lambda}+c=0\\f$,\n * with\n *\n * \\f{align*}\n *  a &= |x^i-P^i|^2,\\\\\n *  b &= 2(x^i-P^i)(P^j-C^j)\\delta_{ij},\\\\\n *  c &= |P^i-C^i|^2 - R^2.\n * \\f}\n *\n * Note that we don't actually need to compute \\f$x_1^i\\f$. Instead, we\n * can determine \\f$\\sigma\\f$ by the relation (obtained by solving Eq. (3)\n * for \\f$\\sigma\\f$ and then inserting Eqs. (7) and (8) to eliminate\n * \\f$x_1^i\\f$ and \\f$x_0^i\\f$)\n *\n * \\f{align}\n * \\sigma = \\frac{\\tilde{\\lambda}-1}{\\tilde{\\lambda}-\\bar{\\lambda}}.\n * \\f}\n *\n * The denominator of Eq. (10) is nonzero for nonsingular maps:\n * From Eqs. (7) and (8), \\f$\\bar{\\lambda}=\\tilde{\\lambda}\\f$ means\n * that \\f$x_1^i=x_0^i\\f$, which means that\n * the \\f$\\sigma=0\\f$ and \\f$\\sigma=1\\f$ surfaces intersect, i.e.\n * the map is singular.\n *\n * Once we have \\f$x_0^i\\f$ and \\f$\\sigma\\f$, the point\n * \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ is uniquely determined by `InnerMap`.\n * The `inverse` function of `InnerMap` takes \\f$x_0^i\\f$ and \\f$\\sigma\\f$\n * as arguments, and returns\n * \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, or a default-constructed `std::optional`\n * if \\f$x_0^i\\f$ or\n * \\f$\\sigma\\f$ are outside the range of the map.\n *\n * #### Root polishing\n *\n * The inverse function described above will sometimes have errors that\n * are noticeably larger than roundoff.  Therefore we apply a single\n * Newton-Raphson iteration to refine the result of the inverse map:\n * Suppose we are given \\f$x^i\\f$, and we have computed \\f$\\bar{x}^i\\f$\n * by the above procedure.  We then correct \\f$\\bar{x}^i\\f$ by adding\n *\n * \\f{align}\n * \\delta \\bar{x}^i = \\left(x^j - x^j(\\bar{x})\\right)\n * \\frac{\\partial \\bar{x}^i}{\\partial x^j},\n * \\f}\n *\n * where \\f$x^j(\\bar{x})\\f$ is the result of applying the forward map\n * to \\f$\\bar{x}^i\\f$ and \\f$\\partial \\bar{x}^i/\\partial x^j\\f$ is the\n * inverse jacobian.\n *\n * ### Inverse jacobian\n *\n * We write the inverse Jacobian as\n *\n * \\f{align}\n * \\frac{\\partial \\bar{x}^i}{\\partial x^j} =\n * \\frac{\\partial \\bar{x}^i}{\\partial x_0^k}\n * \\frac{\\partial x_0^k}{\\partial x^j}\n * + \\frac{\\partial \\bar{x}^i}{\\partial \\sigma}\n *   \\frac{\\partial \\sigma}{\\partial x^j},\n * \\f}\n *\n * where we have recognized that \\f$\\bar{x}^i\\f$ depends both on\n * \\f$x_0^k\\f$ (the corresponding point on the 2-surface) and on\n * \\f$\\sigma\\f$ (encoding the distance away from the 2-surface).\n *\n * We now evaluate Eq. (12). The `InnerMap` class provides a function\n * `inv_jacobian` that returns \\f$\\partial \\bar{x}^i/\\partial x_0^k\\f$\n * (where \\f$\\sigma\\f$ is held fixed), and a function `dxbar_dsigma`\n * that returns \\f$\\partial \\bar{x}^i/\\partial \\sigma\\f$ (where\n * \\f$x_0^i\\f$ is held fixed).  The factor \\f$\\partial x_0^j/\\partial\n * x^i\\f$ can be computed by differentiating Eq. (7), which yields\n *\n * \\f{align}\n * \\frac{\\partial x_0^j}{\\partial x^i} &= \\tilde{\\lambda} \\delta_i^j\n * + \\frac{x_0^j-P^j}{\\tilde{\\lambda}}\n *   \\frac{\\partial\\tilde{\\lambda}}{\\partial x^i},\n * \\f}\n *\n * where \\f$\\partial \\tilde{\\lambda}/\\partial x^i\\f$ is provided by\n * the `deriv_lambda_tilde` function of `InnerMap`.  Note that for\n * nonsingular maps there is no worry that \\f$\\tilde{\\lambda}\\f$ is\n * zero in the denominator of the second term of Eq. (13); if\n * \\f$\\tilde{\\lambda}=0\\f$ then \\f$x_0^i=P^i\\f$ by Eq. (7), and therefore\n * the map is singular.\n *\n * To evaluate the remaining unknown factor in Eq. (12),\n * \\f$\\partial \\sigma/\\partial x^j\\f$,\n * note that [combining Eqs. (1), (7), and (8)]\n * \\f$\\bar{\\lambda}=\\tilde{\\lambda}\\lambda\\f$.\n * Therefore Eq. (10) is equivalent to\n *\n * \\f{align}\n * \\sigma &= \\frac{\\tilde{\\lambda}-1}{\\tilde{\\lambda}(1-\\lambda)}.\n * \\f}\n *\n * Differentiating this expression yields\n *\n * \\f{align}\n * \\frac{\\partial \\sigma}{\\partial x^i} &=\n * \\frac{\\partial \\sigma}{\\partial \\lambda}\n * \\frac{\\partial \\lambda}{\\partial x_0^j}\n * \\frac{\\partial x_0^j}{\\partial x^i}\n * + \\frac{\\partial \\sigma}{\\partial \\tilde\\lambda}\n *   \\frac{\\partial \\tilde\\lambda}{\\partial x^i}\\\\\n * &=\n * \\frac{\\sigma}{1-\\lambda}\n * \\frac{\\partial \\lambda}{\\partial x_0^j}\n * \\frac{\\partial x_0^j}{\\partial x^i}\n * +\n * \\frac{1}{\\tilde{\\lambda}^2(1-\\lambda)}\n * \\frac{\\partial \\tilde\\lambda}{\\partial x^i},\n * \\f}\n *\n * where the second factor in the first term can be evaluated using\n * Eq. (5), the third factor in the first term can be evaluated using\n * Eq. (13), and the second factor in the second term is provided by\n * `InnerMap`s function `deriv_lambda_tilde`.\n *\n */\ntemplate <typename InnerMap>\nclass FocallyLiftedMap {\n public:\n  static constexpr size_t dim = 3;\n  FocallyLiftedMap(const std::array<double, 3>& center,\n                   const std::array<double, 3>& proj_center, double radius,\n                   bool source_is_between_focus_and_target, InnerMap inner_map);\n\n  FocallyLiftedMap() = default;\n  ~FocallyLiftedMap() = default;\n  FocallyLiftedMap(FocallyLiftedMap&&) = default;\n  FocallyLiftedMap(const FocallyLiftedMap&) = default;\n  FocallyLiftedMap& operator=(const FocallyLiftedMap&) = default;\n  FocallyLiftedMap& operator=(FocallyLiftedMap&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==<InnerMap>(const FocallyLiftedMap<InnerMap>& lhs,\n                                   const FocallyLiftedMap<InnerMap>& rhs);\n  std::array<double, 3> center_{}, proj_center_{};\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  bool source_is_between_focus_and_target_;\n  InnerMap inner_map_;\n};\ntemplate <typename InnerMap>\nbool operator!=(const FocallyLiftedMap<InnerMap>& lhs,\n                const FocallyLiftedMap<InnerMap>& rhs);\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedMapHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/FocallyLiftedMapHelpers.hpp\"\n\n#include <cmath>\n#include <gsl/gsl_poly.h>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps::FocallyLiftedMapHelpers {\n\ntemplate <typename T>\nvoid scale_factor(const gsl::not_null<tt::remove_cvref_wrap_t<T>*>& result,\n                  const std::array<T, 3>& src_point,\n                  const std::array<double, 3>& proj_center,\n                  const std::array<double, 3>& sphere_center, double radius,\n                  const bool src_is_between_proj_and_target) {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  // quadratic equation is\n  // a scale_factor^2 + b scale_factor + c = 0\n  //\n  // For this case\n  // a = |x_0^i-P^i|^2\n  // b = 2(x_0^i-P^i)(P^j-C^j)\\delta_{ij}\n  // c = |P^i-C^i|^2 - R^2\n  //\n  const ReturnType a = square(src_point[0] - proj_center[0]) +\n                       square(src_point[1] - proj_center[1]) +\n                       square(src_point[2] - proj_center[2]);\n  const ReturnType b =\n      2.0 *\n      ((src_point[0] - proj_center[0]) * (proj_center[0] - sphere_center[0]) +\n       (src_point[1] - proj_center[1]) * (proj_center[1] - sphere_center[1]) +\n       (src_point[2] - proj_center[2]) * (proj_center[2] - sphere_center[2]));\n  const double c = square(sphere_center[0] - proj_center[0]) +\n                   square(sphere_center[1] - proj_center[1]) +\n                   square(sphere_center[2] - proj_center[2]) - square(radius);\n  if (src_is_between_proj_and_target) {\n    // Here we assume that src_point is between proj_center and\n    // target_point.  There are three cases: 1) src and proj are both\n    // inside the sphere, 2) src and proj are both outside the sphere,\n    // and 3) proj is outside the sphere and src is inside the sphere.\n    // Note that a root of zero corresponds to target_point = proj_center,\n    // and a root of unity corresponds to target_point = src_point.\n    // To cover all 3 cases, we choose the smallest root that is\n    // greater than or equal to unity. This means for case 2) we are\n    // choosing the point closest to src.\n    *result = smallest_root_greater_than_value_within_roundoff(\n        a, b, make_with_value<ReturnType>(a, c), 1.0);\n  } else {\n    // Here we assume that target_point is between proj_center and\n    // src_point. There are three cases: 1) proj is inside the sphere\n    // and src is outside the sphere 2) src is inside the sphere and proj\n    // is outside the sphere, and 3) the sphere is between src and proj.\n    // Note that a root of zero corresponds to target_point = proj_center,\n    // and a root of unity corresponds to target_point = src_point.\n    // To cover all 3 cases, we choose the largest root that is less than\n    // or equal to unity, and we require that this root is positive.\n    // This means that for 3) we are choosing the point closest to src.\n    *result = largest_root_between_values_within_roundoff(\n        a, b, make_with_value<ReturnType>(a, c), 0.0, 1.0);\n  }\n}\n\nstd::optional<double> try_scale_factor(\n    const std::array<double, 3>& src_point,\n    const std::array<double, 3>& proj_center,\n    const std::array<double, 3>& sphere_center, double radius,\n    const bool pick_larger_root, const bool pick_root_greater_than_one,\n    const bool solve_for_root_minus_one) {\n  double x0 = std::numeric_limits<double>::signaling_NaN();\n  double x1 = std::numeric_limits<double>::signaling_NaN();\n  int num_real_roots = 0;\n  if (solve_for_root_minus_one) {\n    // We solve the quadratic for (scale_factor-1) instead of\n    // scale_factor to avoid roundoff problems when scale_factor is very\n    // nearly equal to unity.  Note that scale_factor==1 will occur in\n    // the inverse map when src_point (which is x^i) is\n    // on the sphere.\n    //\n    // Roundoff is not a problem when forward-mapping and solving only\n    // for lambda.\n\n    // If the original quadratic equation is\n    //\n    // A scale_factor^2 + B scale_factor + C = 0,\n    //\n    // and if x = scale_factor-1, then the quadratic equation for x is\n    //\n    // a x^2 + b x + c = 0\n    //\n    // where\n    //\n    // a = A\n    // b = 2A + B\n    // c = A + B + C\n    //\n    // For this case\n    // A = |x_0^i-P^i|^2\n    // B = 2(x_0^i-P^i)(P^j-C^j)\\delta_{ij}\n    // C = |P^i-C^i|^2 - R^2\n    //\n    // Note that A + B + C is |x_0^i-P^i+P^i-C^i|^2 - R^2\n    // and 2A+B is 2(x_0^i-P^i)(x_0^j-P^j+P^j-C^j), so\n    //\n    // a = |x_0^i-P^i|^2\n    // b = 2(x_0^i-P^i)(x_0^j-C^j)\\delta_{ij}\n    // c = |x_0^i-C^i|^2 - R^2\n\n    const double a = square(src_point[0] - proj_center[0]) +\n                     square(src_point[1] - proj_center[1]) +\n                     square(src_point[2] - proj_center[2]);\n    const double b =\n        2.0 *\n        ((src_point[0] - proj_center[0]) * (src_point[0] - sphere_center[0]) +\n         (src_point[1] - proj_center[1]) * (src_point[1] - sphere_center[1]) +\n         (src_point[2] - proj_center[2]) * (src_point[2] - sphere_center[2]));\n    const double c = square(sphere_center[0] - src_point[0]) +\n                     square(sphere_center[1] - src_point[1]) +\n                     square(sphere_center[2] - src_point[2]) - square(radius);\n    num_real_roots = gsl_poly_solve_quadratic(a, b, c, &x0, &x1);\n  } else {\n    const double a = square(src_point[0] - proj_center[0]) +\n                     square(src_point[1] - proj_center[1]) +\n                     square(src_point[2] - proj_center[2]);\n    const double b =\n        2.0 *\n        ((src_point[0] - proj_center[0]) * (proj_center[0] - sphere_center[0]) +\n         (src_point[1] - proj_center[1]) * (proj_center[1] - sphere_center[1]) +\n         (src_point[2] - proj_center[2]) * (proj_center[2] - sphere_center[2]));\n    const double c = square(proj_center[0] - sphere_center[0]) +\n                     square(proj_center[1] - sphere_center[1]) +\n                     square(proj_center[2] - sphere_center[2]) - square(radius);\n    num_real_roots = gsl_poly_solve_quadratic(a, b, c, &x0, &x1);\n  }\n  if (num_real_roots == 2) {\n    if(solve_for_root_minus_one) {\n      // We solved for scale_factor-1 above, so add 1 to get scale_factor.\n      x0 += 1.0;\n      x1 += 1.0;\n    }\n    if (UNLIKELY(equal_within_roundoff(x0, 1.0))) {\n      x0 = 1.0;\n    }\n    if (UNLIKELY(equal_within_roundoff(x1, 1.0))) {\n      x1 = 1.0;\n    }\n    if (pick_root_greater_than_one) {\n      // For the inverse map, we want the a scale_factor s such that\n      // s >= 1. Note that gsl_poly_solve_quadratic returns x0 < x1.\n      // have three cases:\n      //  a) x0 < x1 < 1         ->   no root\n      //  b) x0 < 1 <= x1        ->   Choose x1\n      //  c) 1 <= x0 < x1        ->   choose based on pick_larger_root\n      if (x0 >= 1.0 and not pick_larger_root) {\n        return x0;\n      } else if (x1 >= 1.0) {\n        return x1;\n      } else {\n        return std::optional<double>{};\n      }\n    } else {\n      // For the inverse map, we want a scale_factor s such that 0 < s <= 1.\n      // Note that gsl_poly_solve_quadratic returns x0 < x1.\n      // So we have six cases:\n      //  a) x0 < x1 <= 0        ->   no root\n      //  b) x0 <= 0 < x1 <= 1   ->   Choose x1\n      //  c) x0 <= 0 and x1 > 1  ->   no root\n      //  d) 0 < x0 < x1 <= 1      ->   Choose according to pick_larger_root\n      //  e) 0 < x0 <= 1 < x1      ->   Choose x0\n      //  f) 1 < x0 < x1           ->   no root\n      if (x0 <= 0.0) {\n        if (x1 > 0.0 and x1 <= 1.0) {\n          return x1;  // b)\n        } else {\n          return std::optional<double>{};  // a) and c)\n        }\n      } else if (x0 <= 1.0) {\n        if (x1 > 1.0) {\n          return x0;  // e)\n        } else {\n          return pick_larger_root ? x1 : x0;  // d)\n        }\n      } else {\n        return std::optional<double>{};  // f)\n      }\n    }\n  } else if (num_real_roots == 1) {\n    if(solve_for_root_minus_one) {\n      // We solved for scale_factor-1 above, so add 1 to get scale_factor.\n      x0 += 1.0;\n    }\n    if (equal_within_roundoff(x0, 1.0)) {\n      x0 = 1.0;\n    }\n    if (pick_root_greater_than_one) {\n      if (x0 < 1.0) {\n        return std::optional<double>{};\n      } else {\n        return x0;\n      }\n    } else {\n      if (x0 <= 0.0 or x0 > 1.0) {\n        return std::optional<double>{};\n      } else {\n        return x0;\n      }\n    }\n  } else {\n    return std::optional<double>{};\n  }\n}\n\ntemplate <typename T>\nvoid d_scale_factor_d_src_point(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>& result,\n    const std::array<T, 3>& intersection_point,\n    const std::array<double, 3>& proj_center,\n    const std::array<double, 3>& sphere_center, const T& lambda) {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  ASSERT(not(intersection_point[0] == proj_center[0] and\n             intersection_point[1] == proj_center[1] and\n             intersection_point[2] == proj_center[2]),\n         \"d_scale_factor_d_src_point: trying to divide by zero.  If this\"\n         \"happens, then the map is singular.\");\n  const ReturnType lambda_squared_over_denominator =\n      square(lambda) / (square(intersection_point[0] - proj_center[0]) +\n                        square(intersection_point[1] - proj_center[1]) +\n                        square(intersection_point[2] - proj_center[2]) +\n                        ((intersection_point[0] - proj_center[0]) *\n                             (proj_center[0] - sphere_center[0]) +\n                         (intersection_point[1] - proj_center[1]) *\n                             (proj_center[1] - sphere_center[1]) +\n                         (intersection_point[2] - proj_center[2]) *\n                             (proj_center[2] - sphere_center[2])));\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(*result, i) =\n        lambda_squared_over_denominator *\n        (gsl::at(sphere_center, i) - gsl::at(intersection_point, i));\n  }\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void scale_factor<DTYPE(data)>(                                \\\n      const gsl::not_null<tt::remove_cvref_wrap_t<DTYPE(data)>*>& result, \\\n      const std::array<DTYPE(data), 3>& src_point,                        \\\n      const std::array<double, 3>& proj_center,                           \\\n      const std::array<double, 3>& sphere_center, double radius,          \\\n      bool src_is_between_proj_and_target);                               \\\n  template void d_scale_factor_d_src_point<DTYPE(data)>(                  \\\n      const gsl::not_null<                                                \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>& result,  \\\n      const std::array<DTYPE(data), 3>& intersection_point,               \\\n      const std::array<double, 3>& proj_center,                           \\\n      const std::array<double, 3>& sphere_center, const DTYPE(data) & lambda);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n#undef INSTANTIATE\n#undef DTYPE\n\n}  // namespace domain::CoordinateMaps::FocallyLiftedMapHelpers\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedMapHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// Holds helper functions for use with\n/// domain::CoordinateMaps::FocallyLiftedMap.\nnamespace domain::CoordinateMaps::FocallyLiftedMapHelpers {\n\n/*!\n * \\brief Finds how long to extend a line segment to have it intersect\n * a point on a 2-sphere.\n *\n * \\details Consider a 2-sphere with center \\f$C^i\\f$ and radius \\f$R\\f$, and\n * and let \\f$P^i\\f$ and \\f$x_0^i\\f$ be two arbitrary 3D points.\n *\n * Consider the line passing through \\f$P^i\\f$ and \\f$x_0^i\\f$.\n * If this line intersects the sphere at a point \\f$x_1^i\\f$, then we can write\n *\n * \\f[\n *  x_1^i = P^i + (x_0^i-P^i) \\lambda,\n * \\f]\n *\n * where \\f$\\lambda\\f$ is a scale factor.\n *\n * `scale_factor` computes and returns \\f$\\lambda\\f$.\n *\n * ### Even more detail:\n *\n * To solve for \\f$\\lambda\\f$, we note that \\f$x_1^i\\f$ is on the surface of\n * the sphere, so\n *\n * \\f[\n *  |x_1^i-C^i|^2 = R^2,\n * \\f]\n *\n * (where \\f$|A^i|^2\\f$ means \\f$\\delta_{ij} A^i A^j\\f$),\n *\n *  or equivalently\n *\n * \\f[\n *  | P^i-C^i + (x_0^i-P^i)\\lambda |^2 = R^2.\n * \\f]\n *\n * This is a quadratic equation for \\f$\\lambda\\f$\n * and it generally has more than one real root.\n * It takes the usual form \\f$a\\lambda^2+b\\lambda+c=0\\f$,\n * with\n *\n * \\f{align*}\n *  a &= |x_0^i-P^i|^2,\\\\\n *  b &= 2(x_0^i-P^i)(P^j-C^j)\\delta_{ij},\\\\\n *  c &= |P^i-C^i|^2 - R^2,\n * \\f}\n *\n * So how do we choose between multiple roots?  Some of the maps that\n * use `scale_factor` assume that *for all points*, \\f$x_0^i\\f$ is\n * between \\f$P^i\\f$ and \\f$x_1^i\\f$.  Those maps should set the parameter\n * `src_is_between_proj_and_target` to true. Other maps assume that\n * *for all points*, \\f$x^i\\f$ is always between \\f$x_0^i\\f$\n * and \\f$P^i\\f$. Those maps should set the parameter\n * `src_is_between_proj_and_target` to false.\n *\n * \\warning If we ever add maps where\n * `src_is_between_proj_and_target` can change from point to point,\n * the logic of `scale_factor` needs to be changed.\n *\n * In the arguments to the function below, `src_point` is  \\f$x_0^i\\f$,\n * `proj_center` is \\f$P^i\\f$, `sphere_center` is \\f$C^i\\f$, and\n * `radius` is \\f$R\\f$.\n *\n */\ntemplate <typename T>\nvoid scale_factor(const gsl::not_null<tt::remove_cvref_wrap_t<T>*>& result,\n                  const std::array<T, 3>& src_point,\n                  const std::array<double, 3>& proj_center,\n                  const std::array<double, 3>& sphere_center, double radius,\n                  bool src_is_between_proj_and_target);\n\n/*!\n *  Solves a problem of the same form as `scale_factor`, but is used\n *  only by the inverse function to compute \\f$\\tilde{\\lambda}\\f$ and\n *  \\f$\\bar{\\lambda}\\f$. `try_scale_factor` is used in two contexts:\n *\n *  `try_scale_factor` is used to determine \\f$\\bar{\\lambda}\\f$\n *  given \\f$x^i\\f$. \\f$\\bar{\\lambda}\\f$ is defined by\n *  \\f{align*} x_1^i = P^i + (x^i - P^i) \\bar{\\lambda}.\\f}\n *\n *  `try_scale_factor` is used by the `lambda_tilde` functions of some\n *  of the `InnerMap` classes (namely those `InnerMap` classes where\n *  \\f$x_0^i\\f$ is a spherical surface) to solve for\n *  \\f$\\tilde{\\lambda}\\f$ given\\f$x^i\\f$.  \\f$\\tilde{\\lambda}\\f$\n *  is defined by\n *  \\f{align*} x_0^i = P^i + (x^i - P^i) \\tilde{\\lambda}.\\f}\n *\n *  In both of these contexts, the input parameter `src_point` is\n *  \\f$x^i\\f$, a point that is supposed to be in the range of the\n *  `FocallyLiftedMap`. Because the inverse function can be and is\n *  called for an arbitrary \\f$x^i\\f$ that might not be in the range\n *  of the `FocallyLiftedMap`, `try_scale_factor` returns a\n *  std::optional, with a default-constructed std::optional if the roots it\n *  finds are not as expected (i.e. if the inverse map was called for\n *  a point not in the range of the map).\n *\n *  Because `try_scale_factor` can be called in different situations,\n *  it has additional boolean arguments `pick_larger_root` and\n *  `pick_root_greater_than_one` that allow the caller to choose which\n *  root to return.\n *\n *  Furthermore, to reduce roundoff errors near\n *  \\f$\\tilde{\\lambda}=1\\f$, the default behavior is to solve the\n *  quadratic equation for \\f$\\tilde{\\lambda}-1\\f$ (and then add\n *  \\f$1\\f$ to the solution). If instead one wants to solve the\n *  quadratic equation directly for \\f$\\tilde{\\lambda}\\f$ so as to\n *  obtain slightly different roundoff behavior, then one should\n *  specify the argument `solve_for_root_minus_one` to be `false`.\n *\n * `try_scale_factor` is not templated\n *  on type because it is used only by the inverse function, which\n *  works only on doubles.\n *\n */\nstd::optional<double> try_scale_factor(\n    const std::array<double, 3>& src_point,\n    const std::array<double, 3>& proj_center,\n    const std::array<double, 3>& sphere_center, double radius,\n    bool pick_larger_root, bool pick_root_greater_than_one,\n    bool solve_for_root_minus_one = true);\n\n/*!\n * Computes \\f$\\partial \\lambda/\\partial x_0^i\\f$, where \\f$\\lambda\\f$\n * is the quantity returned by `scale_factor` and `x_0` is `src_point` in\n * the `scale_factor` function.\n *\n * The formula (see `FocallyLiftedMap`) is\n * \\f{align*}\n * \\frac{\\partial\\lambda}{\\partial x_0^j} &=\n * \\lambda^2 \\frac{C_j - x_1^j}{|x_1^i - P^i|^2\n * + (x_1^i - P^i)(P_i - C_i)}.\n * \\f}\n *\n * Note that it takes `intersection_point` and not `src_point` as a\n * parameter.\n *\n * In the arguments to the function below, `intersection_point` is \\f$x_1\\f$,\n * `proj_center` is \\f$P^i\\f$, `sphere_center` is \\f$C^i\\f$, and\n * `radius` is \\f$R\\f$.\n */\ntemplate <typename T>\nvoid d_scale_factor_d_src_point(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>& result,\n    const std::array<T, 3>& intersection_point,\n    const std::array<double, 3>& proj_center,\n    const std::array<double, 3>& sphere_center, const T& lambda);\n\n}  // namespace domain::CoordinateMaps::FocallyLiftedMapHelpers\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedSide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/FocallyLiftedSide.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/FocallyLiftedMapHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace domain::CoordinateMaps::FocallyLiftedInnerMaps {\n\nSide::Side(const std::array<double, 3>& center, const double radius,\n           const double z_lower, const double z_upper)\n    : center_(center),\n      radius_([&radius]() {\n        ASSERT(\n            not equal_within_roundoff(radius, 0.0),\n            \"Cannot have zero radius.  Note that this ASSERT implicitly \"\n            \"assumes that the radius has a scale of roughly unity.  Therefore, \"\n            \"this ASSERT may trigger in the case where we intentionally want \"\n            \"an entire domain that is very small.  If we really want small \"\n            \"domains, then this ASSERT should be modified.\");\n        return radius;\n      }()),\n      theta_min_([&z_upper, &center, &radius]() {\n        ASSERT(abs(z_upper - center[2]) < radius,\n               \"Upper plane must intersect sphere, and it must do \"\n               \"so at more than one point.\");\n        return acos((z_upper - center[2]) / radius);\n      }()),\n      theta_max_([&z_lower, &center, &radius]() {\n        ASSERT(abs(z_lower - center[2]) < radius,\n               \"Lower plane must intersect sphere, and it must do \"\n               \"so at more than one point.\");\n        return acos((z_lower - center[2]) / radius);\n      }()) {\n  // Note that theta decreases with increasing z, which is why\n  // theta_min_ above uses z_upper and theta_max_ uses z_lower.\n  ASSERT(z_lower < z_upper, \"Lower plane must be below upper plane\");\n}\n\ntemplate <typename T>\nvoid Side::forward_map(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        target_coords,\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n  ReturnType& x = (*target_coords)[0];\n  ReturnType& y = (*target_coords)[1];\n  ReturnType& z = (*target_coords)[2];\n  // Use z and y as temporary storage to avoid allocations,\n  // before setting them to their actual values.\n  z = theta_max_ + 0.5 * (theta_min_ - theta_max_) * (1.0 + zbar);\n  // Note: denominator of the next line is guaranteed != 0 because for\n  // this map xbar and ybar are coordinates of points on the *sides*\n  // of a right cylinder, i.e. away from the zbar-axis.\n  y = radius_ * sin(z) / sqrt(square(xbar) + square(ybar));\n  x = y * xbar + center_[0];\n  y = y * ybar + center_[1];\n  z = radius_ * cos(z) + center_[2];\n}\n\ntemplate <typename T>\nvoid Side::jacobian(const gsl::not_null<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3,\n                                                 Frame::NoFrame>*>\n                        jacobian_out,\n                    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n\n  set_number_of_grid_points(jacobian_out, source_coords);\n\n  // Use (1,1) (2,2), and (1,2) parts of Jacobian as temp storage to\n  // reduce allocations.\n  get<1, 1>(*jacobian_out) =\n      theta_max_ + 0.5 * (theta_min_ - theta_max_) * (1.0 + zbar);\n  get<2, 2>(*jacobian_out) = radius_ * sin(get<1, 1>(*jacobian_out));\n  get<1, 2>(*jacobian_out) =\n      radius_ * 0.5 * (theta_min_ - theta_max_) * cos(get<1, 1>(*jacobian_out));\n  // Denominator of next line is guaranteed to not be zero.\n  get<1, 1>(*jacobian_out) = 1.0 / sqrt(square(xbar) + square(ybar));\n  get<1, 2>(*jacobian_out) *= get<1, 1>(*jacobian_out);\n  get<1, 1>(*jacobian_out) =\n      get<2, 2>(*jacobian_out) * cube(get<1, 1>(*jacobian_out));\n\n  // dx/dxbar\n  get<0, 0>(*jacobian_out) = get<1, 1>(*jacobian_out) * square(ybar);\n  // dx/dybar\n  get<0, 1>(*jacobian_out) = -get<1, 1>(*jacobian_out) * xbar * ybar;\n  // dx/dzbar\n  get<0, 2>(*jacobian_out) = get<1, 2>(*jacobian_out) * xbar;\n  // dy/dxbar\n  get<1, 0>(*jacobian_out) = -get<1, 1>(*jacobian_out) * xbar * ybar;\n  // dy/dybar\n  get<1, 1>(*jacobian_out) *= square(xbar);\n  // dy/dzbar\n  get<1, 2>(*jacobian_out) *= ybar;\n  // dz/dzbar\n  get<2, 2>(*jacobian_out) *= -0.5 * (theta_min_ - theta_max_);\n\n  // The rest of the components are zero\n  get<2, 0>(*jacobian_out) = 0.0;\n  get<2, 1>(*jacobian_out) = 0.0;\n}\n\ntemplate <typename T>\nvoid Side::inv_jacobian(const gsl::not_null<tnsr::Ij<tt::remove_cvref_wrap_t<T>,\n                                                     3, Frame::NoFrame>*>\n                            inv_jacobian_out,\n                        const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n\n  set_number_of_grid_points(inv_jacobian_out, source_coords);\n\n  // Use parts of inverse Jacobian as temp storage to reduce allocations.\n  const double theta_factor = 0.5 * (theta_min_ - theta_max_);\n  get<2, 2>(*inv_jacobian_out) =\n      1.0 / (radius_ * sin(theta_max_ + theta_factor * (1.0 + zbar)));\n  // Denominator of next line is guaranteed to be nonzero.\n  get<1, 1>(*inv_jacobian_out) =\n      get<2, 2>(*inv_jacobian_out) / sqrt(square(xbar) + square(ybar));\n\n  // dxbar/dx\n  get<0, 0>(*inv_jacobian_out) = square(ybar) * get<1, 1>(*inv_jacobian_out);\n  // dxbar/dy\n  get<0, 1>(*inv_jacobian_out) = -xbar * ybar * get<1, 1>(*inv_jacobian_out);\n  // dybar/dx\n  get<1, 0>(*inv_jacobian_out) = get<0, 1>(*inv_jacobian_out);\n  // dybar/dy\n  get<1, 1>(*inv_jacobian_out) *= square(xbar);\n  // dzbar/dz\n  get<2, 2>(*inv_jacobian_out) /= -theta_factor;\n\n  // The rest of the components are zero\n  get<0, 2>(*inv_jacobian_out) = 0.0;\n  get<1, 2>(*inv_jacobian_out) = 0.0;\n  get<2, 0>(*inv_jacobian_out) = 0.0;\n  get<2, 1>(*inv_jacobian_out) = 0.0;\n}\n\nstd::optional<std::array<double, 3>> Side::inverse(\n    const std::array<double, 3>& target_coords, const double sigma_in) const {\n  if ((sigma_in < 0.0 and not equal_within_roundoff(sigma_in, 0.0)) or\n      (sigma_in > 1.0 and not equal_within_roundoff(sigma_in, 1.0))) {\n    return {};\n  }\n\n  const double x = target_coords[0] - center_[0];\n  const double y = target_coords[1] - center_[1];\n  const double z = target_coords[2] - center_[2];\n  const double rho = sqrt(square(x) + square(y));\n\n  const double zbar =\n      2.0 * ((acos(z / radius_) - theta_max_) / (theta_min_ - theta_max_)) -\n      1.0;\n  if ((zbar < -1.0 and not equal_within_roundoff(zbar, -1.0)) or\n      (zbar > 1.0 and not equal_within_roundoff(zbar, 1.0))) {\n    return {};\n  }\n\n  const double xbar = (1.0 + sigma_in) * x / rho;\n  const double ybar = (1.0 + sigma_in) * y / rho;\n  return std::array<double, 3>{{xbar, ybar, zbar}};\n}\n\ntemplate <typename T>\nvoid Side::sigma(const gsl::not_null<tt::remove_cvref_wrap_t<T>*> sigma_out,\n                 const std::array<T, 3>& source_coords) const {\n  *sigma_out = sqrt(square(source_coords[0]) + square(source_coords[1])) - 1.0;\n}\n\ntemplate <typename T>\nvoid Side::deriv_sigma(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        deriv_sigma_out,\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  set_number_of_grid_points(deriv_sigma_out, source_coords);\n\n  // Use as temp storage to reduce allocations.\n  // Note that denominator is guaranteed to be nonzero.\n  (*deriv_sigma_out)[1] = 1.0 / sqrt(square(xbar) + square(ybar));\n\n  (*deriv_sigma_out)[0] = xbar * (*deriv_sigma_out)[1];\n  (*deriv_sigma_out)[1] *= ybar;\n  (*deriv_sigma_out)[2] = 0.0;\n}\n\ntemplate <typename T>\nvoid Side::dxbar_dsigma(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        dxbar_dsigma_out,\n    const std::array<T, 3>& source_coords) const {\n  deriv_sigma(dxbar_dsigma_out, source_coords);\n}\n\nstd::optional<double> Side::lambda_tilde(\n    const std::array<double, 3>& parent_mapped_target_coords,\n    const std::array<double, 3>& projection_point,\n    const bool source_is_between_focus_and_target) const {\n  return FocallyLiftedMapHelpers::try_scale_factor(\n      parent_mapped_target_coords, projection_point, center_, radius_,\n      source_is_between_focus_and_target,\n      not source_is_between_focus_and_target, false);\n}\n\ntemplate <typename T>\nvoid Side::deriv_lambda_tilde(\n    const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n        deriv_lambda_tilde_out,\n    const std::array<T, 3>& target_coords, const T& lambda_tilde,\n    const std::array<double, 3>& projection_point) const {\n  FocallyLiftedMapHelpers::d_scale_factor_d_src_point(\n      deriv_lambda_tilde_out, target_coords, projection_point, center_,\n      lambda_tilde);\n}\n\nvoid Side::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | center_;\n    p | radius_;\n    p | theta_min_;\n    p | theta_max_;\n  }\n}\n\nbool operator==(const Side& lhs, const Side& rhs) {\n  return lhs.center_ == rhs.center_ and lhs.radius_ == rhs.radius_ and\n         lhs.theta_min_ == rhs.theta_min_ and lhs.theta_max_ == rhs.theta_max_;\n}\n\nbool operator!=(const Side& lhs, const Side& rhs) { return not(lhs == rhs); }\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void Side::forward_map(                                            \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          target_coords,                                                      \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Side::jacobian(                                               \\\n      const gsl::not_null<                                                    \\\n          tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>*> \\\n          jacobian_out,                                                       \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Side::inv_jacobian(                                           \\\n      const gsl::not_null<                                                    \\\n          tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>*> \\\n          inv_jacobian_out,                                                   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Side::sigma(                                                  \\\n      const gsl::not_null<tt::remove_cvref_wrap_t<DTYPE(data)>*> sigma_out,   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Side::deriv_sigma(                                            \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          deriv_sigma_out,                                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Side::dxbar_dsigma(                                           \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          dxbar_dsigma_out,                                                   \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                 \\\n  template void Side::deriv_lambda_tilde(                                     \\\n      const gsl::not_null<                                                    \\\n          std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>*>               \\\n          deriv_lambda_tilde_out,                                             \\\n      const std::array<DTYPE(data), 3>& target_coords,                        \\\n      const DTYPE(data) & lambda_tilde,                                       \\\n      const std::array<double, 3>& projection_point) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef INSTANTIATE\n#undef DTYPE\n\n}  // namespace domain::CoordinateMaps::FocallyLiftedInnerMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/FocallyLiftedSide.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps::FocallyLiftedInnerMaps {\n/*!\n * \\brief A FocallyLiftedInnerMap that maps a 3D unit right cylindrical shell\n *  to a volume that connects portions of two spherical surfaces.\n *\n * \\details The domain of the map is a 3D unit right cylinder with\n * coordinates \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that\n * \\f$-1\\leq\\bar{z}\\leq 1\\f$ and \\f$1\\leq \\bar{x}^2+\\bar{y}^2 \\leq\n * 4\\f$.  The range of the map has coordinates \\f$(x,y,z)\\f$.\n *\n * Consider a sphere with center \\f$C^i\\f$ and radius \\f$R\\f$ that is\n * intersected by two planes normal to the \\f$z\\f$ axis located at\n * \\f$z = z_\\mathrm{L}\\f$ and \\f$z = z_\\mathrm{U}\\f$, with\n * \\f$z_\\mathrm{L} < z_\\mathrm{U}\\f$.\n * `Side` provides the following functions:\n *\n * ### forward_map()\n * `forward_map()` maps \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ to a point on the inner\n * surface\n * \\f$\\bar{x}^2+\\bar{y}^2=1\\f$ by dividing \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$\n * by \\f$(1+\\sigma)\\f$, where \\f$\\sigma\\f$ is the function given by Eq. (7)\n * below.\n * Then it maps that point to a point on the portion of the sphere with\n * \\f$z_\\mathrm{L} \\leq z \\leq z_\\mathrm{U}\\f$.\n * `forward_map()` returns\n * \\f$x_0^i\\f$, the 3D coordinates on that sphere, which are given by\n *\n * \\f{align}\n * x_0^0 &= R \\sin\\theta \\frac{\\bar{x}}{1+\\sigma} + C^0,\\\\\n * x_0^1 &= R \\sin\\theta \\frac{\\bar{y}}{1+\\sigma} + C^1,\\\\\n * x_0^2 &= R \\cos\\theta + C^2.\\\\\n * \\f}\n *\n * Here\n * \\f{align}\n * \\theta = \\theta_\\mathrm{max} +\n * (\\theta_\\mathrm{min}-\\theta_\\mathrm{max}) \\frac{\\bar{z}+1}{2},\n * \\f}\n *\n * where\n * \\f{align}\n * \\cos(\\theta_\\mathrm{max}) &= (z_\\mathrm{L}-C^2)/R,\\\\\n * \\cos(\\theta_\\mathrm{min}) &= (z_\\mathrm{U}-C^2)/R.\n * \\f}\n *\n * Note that \\f$\\theta\\f$ decreases with increasing \\f$\\bar{z}\\f$,\n * which is the usual convention for a polar angle but might otherwise\n * cause confusion.\n *\n * ### sigma\n *\n * \\f$\\sigma\\f$ is a function that is zero on the sphere\n * \\f$x^i=x_0^i\\f$ and unity at \\f$\\bar{x}^2+\\bar{y}^2=4\\f$\n * (corresponding to the upper surface of the FocallyLiftedMap). We define\n *\n * \\f{align}\n *  \\sigma &= \\sqrt{\\bar{x}^2+\\bar{y}^2}-1.\n * \\f}\n *\n * ### deriv_sigma\n *\n * `deriv_sigma` returns\n *\n * \\f{align}\n *  \\frac{\\partial \\sigma}{\\partial \\bar{x}^j} &=\n * \\left(\\frac{\\bar{x}}{1+\\sigma},\n *       \\frac{\\bar{y}}{1+\\sigma},0\\right).\n * \\f}\n *\n * ### jacobian\n *\n * `jacobian` returns \\f$\\partial x_0^k/\\partial \\bar{x}^j\\f$.\n * The arguments to `jacobian` are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$.\n * Differentiating Eqs.(1--4) above yields\n *\n * \\f{align*}\n * \\frac{\\partial x_0^0}{\\partial \\bar{x}} &= R \\sin\\theta\n * \\frac{\\bar{y}^2}{(1+\\sigma)^3}, \\\\\n * \\frac{\\partial x_0^0}{\\partial \\bar{y}} &= -R \\sin\\theta\n * \\frac{\\bar{x}\\bar{y}}{(1+\\sigma)^3}, \\\\\n * \\frac{\\partial x_0^0}{\\partial \\bar{z}} &=\n * R \\cos\\theta \\frac{\\theta_\\mathrm{min}-\\theta_\\mathrm{max}}{2(1+\\sigma)}\n *   \\bar{x},\\\\\n * \\frac{\\partial x_0^1}{\\partial \\bar{x}} &= -R \\sin\\theta\n * \\frac{\\bar{x}\\bar{y}}{(1+\\sigma)^3}, \\\\\n * \\frac{\\partial x_0^1}{\\partial \\bar{y}} &= R \\sin\\theta\n * \\frac{\\bar{x}^2}{(1+\\sigma)^3}, \\\\\n * \\frac{\\partial x_0^1}{\\partial \\bar{z}} &=\n * R \\cos\\theta \\frac{\\theta_\\mathrm{min}-\\theta_\\mathrm{max}}{2(1+\\sigma)}\n *   \\bar{y},\\\\\n * \\frac{\\partial x_0^2}{\\partial \\bar{x}} &= 0,\\\\\n * \\frac{\\partial x_0^2}{\\partial \\bar{y}} &= 0,\\\\\n * \\frac{\\partial x_0^2}{\\partial \\bar{z}} &=\n * - R \\sin\\theta \\frac{\\theta_\\mathrm{min}-\\theta_\\mathrm{max}}{2}.\n * \\f}\n *\n * ### inverse\n *\n * `inverse` takes \\f$x_0^i\\f$ and \\f$\\sigma\\f$ as arguments, and\n * returns \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$, or a default-constructed\n * `std::optional<std::array<double, 3>>` if \\f$x_0^i\\f$ or \\f$\\sigma\\f$ are\n * outside the range of the map.\n *\n * If \\f$\\sigma\\f$ is outside the range \\f$[0,1]\\f$ then we return\n * a default-constructed `std::optional<std::array<double, 3>>`.\n *\n * To get \\f$\\bar{z}\\f$ we invert Eq. (4):\n * \\f{align}\n * \\bar{z} &= 2\\frac{\\acos\\left((x_0^2-C^2)/R\\right)-\\theta_\\mathrm{max}}\n *            {\\theta_\\mathrm{min}-\\theta_\\mathrm{max}} - 1.\n * \\f}\n *\n * If \\f$\\bar{z}\\f$ is outside the range \\f$[-1,1]\\f$ then we return\n * a default-constructed `std::optional<std::array<double, 3>>`.\n *\n * To compute \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$, we invert Eqs. (1--3) and\n * use \\f$\\sigma\\f$:\n *\n * \\f{align}\n *  \\bar{x} &= \\frac{(x_0^0-C^0) (1+\\sigma)}{\\rho},\\\\\n *  \\bar{y} &= \\frac{(x_0^1-C^1) (1+\\sigma)}{\\rho},\n * \\f}\n *\n * where\n *\n * \\f{align}\n * \\rho = \\sqrt{(x_0^0-C^0)^2+(x_0^1-C^1)^2}.\n * \\f}\n *\n * ### lambda_tilde\n *\n * `lambda_tilde` takes as arguments a point \\f$x^i\\f$ and a projection point\n *  \\f$P^i\\f$, and computes \\f$\\tilde{\\lambda}\\f$, the solution to\n *\n * \\f{align} x_0^i = P^i + (x^i - P^i) \\tilde{\\lambda}.\\f}\n *\n * Since \\f$x_0^i\\f$ must lie on the sphere, \\f$\\tilde{\\lambda}\\f$ is the\n * solution of the quadratic equation\n *\n * \\f{align}\n * |P^i + (x^i - P^i) \\tilde{\\lambda} - C^i |^2 - R^2 = 0.\n * \\f}\n *\n * In solving the quadratic, we choose the larger root if\n * \\f$x^2>z_\\mathrm{P}\\f$ and the smaller root otherwise. We demand\n * that the root is greater than unity.  If there is no such root,\n * this means that the point \\f$x^i\\f$ is not in the range of the map\n * so we return a default-constructed `std::optional<double>`.\n *\n * ### deriv_lambda_tilde\n *\n * `deriv_lambda_tilde` takes as arguments \\f$x_0^i\\f$, a projection point\n *  \\f$P^i\\f$, and \\f$\\tilde{\\lambda}\\f$, and\n *  returns \\f$\\partial \\tilde{\\lambda}/\\partial x^i\\f$.\n * By differentiating Eq. (14), we find\n *\n * \\f{align}\n * \\frac{\\partial\\tilde{\\lambda}}{\\partial x^j} &=\n * \\tilde{\\lambda}^2 \\frac{C^j - x_0^j}{\n * (x_0^i - P^i)(x_{0i} - C_{i})} \\nonumber \\\\\n * &= \\tilde{\\lambda}^2 \\frac{C^j - x_0^j}{|x_0^i - P^i|^2\n * + (x_0^i - P^i)(P_i - C_{i})}.\n * \\f}\n *\n * ### inv_jacobian\n *\n * `inv_jacobian` returns \\f$\\partial \\bar{x}^i/\\partial x_0^k\\f$,\n *  where \\f$\\sigma\\f$ is held fixed.\n * The arguments to `inv_jacobian` are \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$.\n *\n * Note from Eqs. (9--12) that \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$\n * depend only on \\f$x_0^0\\f$ and \\f$x_0^1\\f$ but not on \\f$x_0^2\\f$.\n *\n * By differentiating Eqs. (9--12), we find\n *\n * \\f{align*}\n * \\frac{\\partial \\bar{x}}{\\partial x_0^0} &=\n * \\frac{\\bar{y}^2}{(1+\\sigma)\\rho},\\\\\n * \\frac{\\partial \\bar{x}}{\\partial x_0^1} &=\n * - \\frac{\\bar{x}\\bar{y}}{(1+\\sigma)\\rho},\\\\\n * \\frac{\\partial \\bar{x}}{\\partial x_0^2} &= 0,\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^0} &=\n * - \\frac{\\bar{x}\\bar{y}}{(1+\\sigma)\\rho},\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^1} &=\n * \\frac{\\bar{x}^2}{(1+\\sigma)\\rho},\\\\\n * \\frac{\\partial \\bar{y}}{\\partial x_0^2} &= 0,\\\\\n * \\frac{\\partial \\bar{z}}{\\partial x_0^0} &= 0,\\\\\n * \\frac{\\partial \\bar{z}}{\\partial x_0^1} &= 0,\\\\\n * \\frac{\\partial \\bar{z}}{\\partial x_0^2} &=\n * -\\frac{2}{\\rho(\\theta_\\mathrm{min}-\\theta_\\mathrm{max})},\n * \\f}\n *\n * where\n *\n * \\f[\n *   \\rho = R \\sin\\theta = R\\sin\\left(\\theta_\\mathrm{max} +\n * (\\theta_\\mathrm{min}-\\theta_\\mathrm{max}) \\frac{\\bar{z}+1}{2}\\right),\n * \\f]\n *\n * which is also equal to the quantity in Eq. (12).\n *\n * ### dxbar_dsigma\n *\n * `dxbar_dsigma` returns \\f$\\partial \\bar{x}^i/\\partial \\sigma\\f$,\n *  where \\f$x_0^i\\f$ is held fixed.\n *\n * From Eqs. (10) and (11) we have\n *\n * \\f{align}\n * \\frac{\\partial \\bar{x}^i}{\\partial \\sigma} &=\n * \\left(\\frac{\\bar{x}}{\\sqrt{\\bar{x}^2+\\bar{y}^2}},\n *       \\frac{\\bar{y}}{\\sqrt{\\bar{x}^2+\\bar{y}^2}},0\\right).\n * \\f}\n *\n */\nclass Side {\n public:\n  static constexpr size_t dim = 3;\n  Side(const std::array<double, 3>& center, const double radius,\n       const double z_lower, const double z_upper);\n\n  Side() = default;\n  ~Side() = default;\n  Side(Side&&) = default;\n  Side(const Side&) = default;\n  Side& operator=(const Side&) = default;\n  Side& operator=(Side&&) = default;\n\n  template <typename T>\n  void forward_map(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          target_coords,\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords, double sigma_in) const;\n\n  template <typename T>\n  void jacobian(const gsl::not_null<\n                    tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>*>\n                    jacobian_out,\n                const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void inv_jacobian(const gsl::not_null<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3,\n                                                 Frame::NoFrame>*>\n                        inv_jacobian_out,\n                    const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void sigma(const gsl::not_null<tt::remove_cvref_wrap_t<T>*> sigma_out,\n             const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void deriv_sigma(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          deriv_sigma_out,\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  void dxbar_dsigma(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          dxbar_dsigma_out,\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<double> lambda_tilde(\n      const std::array<double, 3>& parent_mapped_target_coords,\n      const std::array<double, 3>& projection_point,\n      bool source_is_between_focus_and_target) const;\n\n  template <typename T>\n  void deriv_lambda_tilde(\n      const gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*>\n          deriv_lambda_tilde_out,\n      const std::array<T, 3>& target_coords, const T& lambda_tilde,\n      const std::array<double, 3>& projection_point) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const Side& lhs, const Side& rhs);\n  std::array<double, 3> center_{\n      make_array<3>(std::numeric_limits<double>::signaling_NaN())};\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  double theta_min_{std::numeric_limits<double>::signaling_NaN()};\n  double theta_max_{std::numeric_limits<double>::signaling_NaN()};\n};\nbool operator!=(const Side& lhs, const Side& rhs);\n}  // namespace domain::CoordinateMaps::FocallyLiftedInnerMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Frustum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/RootFinding/GslMultiRoot.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nFrustum::Frustum(const std::array<std::array<double, 2>, 4>& face_vertices,\n                 const double lower_bound, const double upper_bound,\n                 OrientationMap<3> orientation_of_frustum,\n                 bool equiangular_map_at_outer, bool equiangular_map_at_inner,\n                 const Distribution zeta_distribution,\n                 const std::optional<double> distribution_value,\n                 const double sphericity, const double transition_phi,\n                 const double opening_angle)\n    // clang-tidy: trivially copyable\n    : orientation_of_frustum_(std::move(orientation_of_frustum)),  // NOLINT\n      equiangular_map_at_outer_{equiangular_map_at_outer},\n      equiangular_map_at_inner_{equiangular_map_at_inner},\n      is_identity_(\n          face_vertices ==\n              std::array<std::array<double, 2>, 4>{{{{-1.0, -1.0}},\n                                                    {{1.0, 1.0}},\n                                                    {{-1.0, -1.0}},\n                                                    {{1.0, 1.0}}}} and\n          lower_bound == -1.0 and upper_bound == 1.0 and\n          orientation_of_frustum_.is_aligned() and\n          not(equiangular_map_at_inner_ or equiangular_map_at_outer_) and\n          zeta_distribution == Distribution::Linear),\n      zeta_distribution_(zeta_distribution),\n      sphericity_(sphericity) {\n  ASSERT(sphericity_ >= 0.0 and sphericity_ <= 1.0,\n         \"The sphericity must be set between 0.0, corresponding to a flat \"\n         \"surface, and 1.0, corresponding to a spherical surface, inclusive. \"\n         \"It is currently set to \"\n             << sphericity << \".\");\n  const double& lower_x_lower_base = face_vertices[0][0];\n  const double& lower_y_lower_base = face_vertices[0][1];\n  const double& upper_x_lower_base = face_vertices[1][0];\n  const double& upper_y_lower_base = face_vertices[1][1];\n  const double& lower_x_upper_base = face_vertices[2][0];\n  const double& lower_y_upper_base = face_vertices[2][1];\n  const double& upper_x_upper_base = face_vertices[3][0];\n  const double& upper_y_upper_base = face_vertices[3][1];\n  ASSERT(upper_x_lower_base > lower_x_lower_base,\n         \"The lower bound for a coordinate must be numerically less\"\n         \" than the upper bound for that coordinate.\");\n  ASSERT(upper_y_lower_base > lower_y_lower_base,\n         \"The lower bound for a coordinate must be numerically less\"\n         \" than the upper bound for that coordinate.\");\n  ASSERT(upper_x_upper_base > lower_x_upper_base,\n         \"The lower bound for a coordinate must be numerically less\"\n         \" than the upper bound for that coordinate.\");\n  ASSERT(upper_y_upper_base > lower_y_upper_base,\n         \"The lower bound for a coordinate must be numerically less\"\n         \" than the upper bound for that coordinate.\");\n  ASSERT(upper_bound > lower_bound,\n         \"The lower bound for a coordinate must be numerically less\"\n         \" than the upper bound for that coordinate.\");\n  ASSERT(get(determinant(discrete_rotation_jacobian(orientation_of_frustum_))) >\n             0.0,\n         \"Frustum rotations must be done in such a manner that the sign of \"\n         \"the determinant of the discrete rotation is positive. This is to \"\n         \"preserve handedness of the coordinates.\");\n  sigma_x_ = 0.25 * (lower_x_upper_base + upper_x_upper_base +\n                     lower_x_lower_base + upper_x_lower_base);\n  delta_x_zeta_ = 0.25 * (lower_x_upper_base + upper_x_upper_base -\n                          lower_x_lower_base - upper_x_lower_base);\n  delta_x_xi_ = 0.25 * (upper_x_upper_base - lower_x_upper_base +\n                        upper_x_lower_base - lower_x_lower_base);\n  delta_x_xi_zeta_ = 0.25 * (upper_x_upper_base - lower_x_upper_base -\n                             upper_x_lower_base + lower_x_lower_base);\n  sigma_y_ = 0.25 * (lower_y_upper_base + upper_y_upper_base +\n                     lower_y_lower_base + upper_y_lower_base);\n  delta_y_zeta_ = 0.25 * (lower_y_upper_base + upper_y_upper_base -\n                          lower_y_lower_base - upper_y_lower_base);\n  delta_y_eta_ = 0.25 * (upper_y_upper_base - lower_y_upper_base +\n                         upper_y_lower_base - lower_y_lower_base);\n  delta_y_eta_zeta_ = 0.25 * (upper_y_upper_base - lower_y_upper_base -\n                              upper_y_lower_base + lower_y_lower_base);\n  sigma_z_ = 0.5 * (upper_bound + lower_bound);\n  delta_z_zeta_ = 0.5 * (upper_bound - lower_bound);\n  phi_ = transition_phi;\n  half_opening_angle_ = 0.5 * opening_angle;\n  one_over_tan_half_opening_angle_ = 1.0 / tan(half_opening_angle_);\n  // The radius is taken to be the distance from the origin to the vertex of\n  // the Frustum that is furthest away from the origin. For the rectangular\n  // BinaryCompactObject Domain, this vertex is assumed to lie on a rectangular\n  // prism centered at the origin made up of ten Frustums. The radius is then\n  // that of a sphere that circumscribes the prism. For information on how\n  // other Domains use the Frustum map, please see that particular Domain's\n  // documentation.\n  radius_ = sqrt(\n      square(std::max({abs(upper_x_upper_base), abs(upper_x_lower_base),\n                       abs(lower_x_upper_base), abs(lower_x_lower_base)})) +\n      square(std::max({abs(upper_y_upper_base), abs(upper_y_lower_base),\n                       abs(lower_y_upper_base), abs(lower_y_lower_base)})) +\n      square(std::max({abs(upper_bound), abs(lower_bound)})));\n  inner_radius_ = sqrt(\n      square(std::max({abs(upper_x_lower_base), abs(lower_x_lower_base)})) +\n      square(std::max({abs(upper_y_lower_base), abs(lower_y_lower_base)})) +\n      square(lower_bound));\n\n  if (zeta_distribution_ == Distribution::Projective) {\n    double w_delta = distribution_value.has_value()\n                         ? distribution_value.value()\n                         : sqrt(((upper_x_lower_base - lower_x_lower_base) *\n                                 (upper_y_lower_base - lower_y_lower_base)) /\n                                ((upper_x_upper_base - lower_x_upper_base) *\n                                 (upper_y_upper_base - lower_y_upper_base)));\n    w_plus_ = w_delta + 1.0;\n    w_minus_ = w_delta - 1.0;\n  } else {\n    if (zeta_distribution_ == Distribution::Logarithmic) {\n      zeta_distribution_value_ = distribution_value.value_or(\n          -(radius_ + inner_radius_) / (radius_ - inner_radius_));\n    }\n    w_plus_ = 2.0;\n    w_minus_ = 0.0;\n  }\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> Frustum::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  const ReturnType& xi = source_coords[0];\n  const ReturnType& eta = source_coords[1];\n  const ReturnType& zeta = source_coords[2];\n  ReturnType cap_zeta;\n  if (zeta_distribution_ == Distribution::Projective) {\n    cap_zeta = (w_minus_ + w_plus_ * zeta) / (w_plus_ + w_minus_ * zeta);\n  } else if (zeta_distribution_ == Distribution::Linear) {\n    cap_zeta = zeta;\n  } else if (zeta_distribution_ == Distribution::Logarithmic) {\n    ASSERT(\n        zeta_distribution_value_.has_value(),\n        \"Cannot us a logarithmic map without setting zeta_distribution_value_\");\n    const Interval log_mapping{-1.0,\n                               1.0,\n                               -1.0,\n                               1.0,\n                               zeta_distribution_,\n                               zeta_distribution_value_.value()};\n    cap_zeta = log_mapping(std::array<T, 1>{{zeta}})[0];\n  } else {\n    ERROR(\n        \"Only the distributions Linear, Projective, and Logarithmic are \"\n        \"supported.\");\n  }\n  const ReturnType& cap_xi_zero =\n      equiangular_map_at_inner_ ? tan(M_PI_4 * xi) : xi;\n  const double one_plus_phi_square = 1.0 + phi_ * phi_;\n  const ReturnType& cap_xi_upper =\n      equiangular_map_at_outer_\n          ? one_plus_phi_square * one_over_tan_half_opening_angle_ *\n                    tan(half_opening_angle_ * (xi + phi_) /\n                        one_plus_phi_square) -\n                phi_\n          : xi;\n  const ReturnType& cap_xi_transition = 0.5 * (1.0 + cap_zeta) * cap_xi_upper +\n                                        0.5 * (1.0 - cap_zeta) * cap_xi_zero;\n\n  const ReturnType& cap_eta_zero =\n      equiangular_map_at_inner_ ? tan(M_PI_4 * eta) : eta;\n  const ReturnType& cap_eta_upper =\n      equiangular_map_at_outer_ ? tan(M_PI_4 * eta) : eta;\n  const ReturnType& cap_eta_transition =\n      0.5 * (1.0 + cap_zeta) * cap_eta_upper +\n      0.5 * (1.0 - cap_zeta) * cap_eta_zero;\n\n  ReturnType physical_x =\n      sigma_x_ + delta_x_xi_ * cap_xi_transition +\n      (delta_x_zeta_ + delta_x_xi_zeta_ * cap_xi_transition) * cap_zeta;\n  ReturnType physical_y =\n      sigma_y_ + delta_y_eta_ * cap_eta_transition +\n      (delta_y_zeta_ + delta_y_eta_zeta_ * cap_eta_transition) * cap_zeta;\n  ReturnType physical_z = sigma_z_ + delta_z_zeta_ * cap_zeta;\n  if (sphericity_ > 0.0) {\n    const ReturnType upper_surface_x =\n        sigma_x_ + delta_x_xi_ * cap_xi_upper +\n        (delta_x_zeta_ + delta_x_xi_zeta_ * cap_xi_upper);\n    const ReturnType upper_surface_y =\n        sigma_y_ + delta_y_eta_ * cap_eta_upper +\n        (delta_y_zeta_ + delta_y_eta_zeta_ * cap_eta_upper);\n    const double upper_surface_z = sigma_z_ + delta_z_zeta_;\n    const ReturnType upper_surface_r =\n        sqrt(square(upper_surface_x) + square(upper_surface_y) +\n             (square(upper_surface_z)));\n    const ReturnType correction_coefficient = 0.5 * sphericity_ *\n                                              (1.0 + cap_zeta) *\n                                              (radius_ / upper_surface_r - 1.0);\n    physical_x += correction_coefficient * upper_surface_x;\n    physical_y += correction_coefficient * upper_surface_y;\n    physical_z += correction_coefficient * upper_surface_z;\n  }\n  std::array<ReturnType, 3> physical_coords{\n      {std::move(physical_x), std::move(physical_y), std::move(physical_z)}};\n  return discrete_rotation(orientation_of_frustum_, std::move(physical_coords));\n}\n\nstd::optional<std::array<double, 3>> Frustum::inverse(\n    const std::array<double, 3>& target_coords) const {\n  // physical coords {x,y,z}\n  std::array<double, 3> physical_coords =\n      discrete_rotation(orientation_of_frustum_.inverse_map(), target_coords);\n\n  // logical coords {xi,eta,zeta}\n  std::array<double, 3> logical_coords{};\n\n  logical_coords[2] = (physical_coords[2] - sigma_z_) / delta_z_zeta_;\n  const auto denom0 = delta_x_xi_ + delta_x_xi_zeta_ * logical_coords[2];\n  const auto denom1 = delta_y_eta_ + delta_y_eta_zeta_ * logical_coords[2];\n  // denom0 and denom1 are always positive inside the frustum.\n  if (denom0 < 0.0 or equal_within_roundoff(denom0, 0.0) or denom1 < 0.0 or\n      equal_within_roundoff(denom1, 0.0)) {\n    return std::nullopt;\n  }\n\n  logical_coords[0] =\n      (physical_coords[0] - sigma_x_ - delta_x_zeta_ * logical_coords[2]) /\n      denom0;\n  logical_coords[1] =\n      (physical_coords[1] - sigma_y_ - delta_y_zeta_ * logical_coords[2]) /\n      denom1;\n  if (equiangular_map_at_inner_ or equiangular_map_at_outer_) {\n    logical_coords[0] = atan(logical_coords[0]) / M_PI_4;\n    logical_coords[1] = atan(logical_coords[1]) / M_PI_4;\n  }\n  if (zeta_distribution_ == Distribution::Projective) {\n    logical_coords[2] = (-w_minus_ + w_plus_ * logical_coords[2]) /\n                        (w_plus_ - w_minus_ * logical_coords[2]);\n  } else if (zeta_distribution_ == Distribution::Logarithmic) {\n    ASSERT(\n        zeta_distribution_value_.has_value(),\n        \"Cannot us a logarithmic map without setting zeta_distribution_value_\");\n    const Interval log_mapping{-1.0,\n                               1.0,\n                               -1.0,\n                               1.0,\n                               zeta_distribution_,\n                               zeta_distribution_value_.value()};\n    logical_coords[2] =\n        log_mapping.inverse(std::array<double, 1>{{logical_coords[2]}})\n            .value()[0];\n  } else {\n    ASSERT(zeta_distribution_ == Distribution::Linear,\n           \"Only the \"\n           \"distributions Linear, Projective, and Logarithmic are supported.\");\n  }\n  if (sphericity_ > 0.0 or phi_ != 0.0) {\n    // The physical_coords sometimes have magnitudes slightly\n    // larger than radius_ due to roundoff error, this 1.0e-4 margin\n    // allows these points to still be inverted. Points much further outside\n    // of this value are likely to not be invertible, so we return\n    // std::nullopt instead. Also, points below the lower bound are likely not\n    // invertible, so return std::nullopt in that case as well.\n    if (magnitude(physical_coords) > radius_ + 1.0e-4 or\n        physical_coords[2] < (sigma_z_ - delta_z_zeta_) - 1.0e-4) {\n      return std::nullopt;\n    }\n    const double absolute_tolerance = 1.0e-12;\n    const double max_absolute_tolerance = 1.0e-10;\n    const int maximum_iterations = 20;\n    const Verbosity verbosity = Verbosity::Silent;\n    const auto method = RootFinder::Method::Newton;\n    const double relative_tolerance = 1.0e-12;\n    const auto condition = RootFinder::StoppingConditions::Convergence(\n        absolute_tolerance, relative_tolerance);\n    struct {\n      std::array<double, 3> operator()(\n          const std::array<double, 3>& source_coords) const {\n        // Terminate the rootfind when it diverges too far away from the logical\n        // coordinate bounds of [-1, 1]. In this case the target coordinates are\n        // likely outside of the bulged frustum. It would be better if we found\n        // a way to handle this case more cleanly before the rootfind diverges.\n        // Either way, logical coordinates too far outside of [-1, 1] lead to a\n        // singular Jacobian, so we have to terminate here anyway.\n        if (abs(source_coords[0]) > 3. or abs(source_coords[1]) > 3. or\n            abs(source_coords[2]) > 3.) {\n          throw convergence_error{\n              \"Logical coordinates are too far outside of [-1., 1], so the \"\n              \"rootfind is likely diverging.\"};\n        }\n        return map(source_coords) - target_coords;\n      }\n      std::array<std::array<double, 3>, 3> jacobian(\n          const std::array<double, 3>& source_coords) const {\n        std::array<std::array<double, 3>, 3> jacobian_matrix_array{};\n        const auto jacobian_matrix_tnsr = map.jacobian(source_coords);\n        for (size_t i = 0; i < 3; i++) {\n          for (size_t j = 0; j < 3; j++) {\n            gsl::at(gsl::at(jacobian_matrix_array, i), j) =\n                jacobian_matrix_tnsr.get(i, j);\n          }\n        }\n        return jacobian_matrix_array;\n      }\n      const Frustum& map;\n      const std::array<double, 3>& target_coords;\n    } const rootfunction{*this, target_coords};\n\n    // The `logical_coords` computed above are computed using the inverse map\n    // to the flat frustum map, which is known analytically.\n    // These `logical_coords` are then used as the initial guess for the\n    // inverse of the bulged frustum map. This reduces the number of iterations\n    // needed by the root finder. The initial guess is constructed to lie within\n    // the logical cube.\n    for (auto& coord : logical_coords) {\n      if (abs(coord) > 1.0) {\n        coord /= abs(coord);\n      }\n    }\n    try {\n      logical_coords = RootFinder::gsl_multiroot(\n          rootfunction, logical_coords, condition, maximum_iterations,\n          verbosity, max_absolute_tolerance, method);\n\n    } catch (const convergence_error& e) {\n      return std::nullopt;\n    }\n  }\n  return logical_coords;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> Frustum::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xi = source_coords[0];\n  const ReturnType& eta = source_coords[1];\n  const ReturnType& zeta = source_coords[2];\n  ReturnType cap_zeta;\n  ReturnType cap_zeta_deriv;\n  if (zeta_distribution_ == Distribution::Projective) {\n    cap_zeta = (w_minus_ + w_plus_ * zeta) / (w_plus_ + w_minus_ * zeta);\n    cap_zeta_deriv = (square(w_plus_) - square(w_minus_)) /\n                     square(w_plus_ + zeta * w_minus_);\n  } else if (zeta_distribution_ == Distribution::Linear) {\n    cap_zeta = zeta;\n    cap_zeta_deriv = make_with_value<ReturnType>(zeta, 1.0);\n  } else if (zeta_distribution_ == Distribution::Logarithmic) {\n    ASSERT(\n        zeta_distribution_value_.has_value(),\n        \"Cannot us a logarithmic map without setting zeta_distribution_value_\");\n    const Interval log_mapping{-1.0,\n                               1.0,\n                               -1.0,\n                               1.0,\n                               zeta_distribution_,\n                               zeta_distribution_value_.value()};\n    cap_zeta = log_mapping(std::array<T, 1>{{zeta}})[0];\n    cap_zeta_deriv = get<0, 0>(log_mapping.jacobian(std::array<T, 1>{{zeta}}));\n  } else {\n    ERROR(\n        \"Only the distributions Linear, Projective, and Logarithmic are \"\n        \"supported.\");\n  }\n  const ReturnType& cap_xi_zero =\n      equiangular_map_at_inner_ ? tan(M_PI_4 * xi) : xi;\n  const double one_plus_phi_square = 1.0 + phi_ * phi_;\n  const ReturnType& cap_xi_upper =\n      equiangular_map_at_outer_\n          ? one_plus_phi_square * one_over_tan_half_opening_angle_ *\n                    tan(half_opening_angle_ * (xi + phi_) /\n                        one_plus_phi_square) -\n                phi_\n          : xi;\n  const ReturnType& cap_xi_transition =\n      (equiangular_map_at_outer_ or equiangular_map_at_inner_)\n          ? 0.5 * (1.0 + cap_zeta) * cap_xi_upper +\n                0.5 * (1.0 - cap_zeta) * cap_xi_zero\n          : xi;\n\n  const ReturnType& cap_eta_zero =\n      equiangular_map_at_inner_ ? tan(M_PI_4 * eta) : eta;\n  const ReturnType& cap_eta_upper =\n      equiangular_map_at_outer_ ? tan(M_PI_4 * eta) : eta;\n  const ReturnType& cap_eta_transition =\n      0.5 * (1.0 + cap_zeta) * cap_eta_upper +\n      0.5 * (1.0 - cap_zeta) * cap_eta_zero;\n\n  const ReturnType& cap_xi_zero_deriv =\n      equiangular_map_at_inner_ ? M_PI_4 * (1.0 + square(cap_xi_zero))\n                                : make_with_value<ReturnType>(xi, 1.0);\n  const ReturnType& cap_xi_upper_deriv =\n      equiangular_map_at_outer_\n          ? one_over_tan_half_opening_angle_ * half_opening_angle_ *\n                (1.0 + square(tan(half_opening_angle_ * (xi + phi_) /\n                                  one_plus_phi_square)))\n          : make_with_value<ReturnType>(xi, 1.0);\n\n  const ReturnType& cap_xi_transition_deriv =\n      (equiangular_map_at_inner_ or equiangular_map_at_outer_)\n          ? 0.5 * (1.0 + cap_zeta) * cap_xi_upper_deriv +\n                0.5 * (1.0 - cap_zeta) * cap_xi_zero_deriv\n          : make_with_value<ReturnType>(xi, 1.0);\n\n  const ReturnType& cap_eta_zero_deriv =\n      equiangular_map_at_inner_ ? M_PI_4 * (1.0 + square(cap_eta_zero))\n                                : make_with_value<ReturnType>(eta, 1.0);\n  const ReturnType& cap_eta_upper_deriv =\n      equiangular_map_at_outer_ ? M_PI_4 * (1.0 + square(cap_eta_upper))\n                                : make_with_value<ReturnType>(eta, 1.0);\n  const ReturnType& cap_eta_transition_deriv =\n      (equiangular_map_at_inner_ or equiangular_map_at_outer_)\n          ? 0.5 * (1.0 + cap_zeta) * cap_eta_upper_deriv +\n                0.5 * (1.0 - cap_zeta) * cap_eta_zero_deriv\n          : make_with_value<ReturnType>(eta, 1.0);\n\n  auto jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  // dX_dxi\n  const auto mapped_xi =\n      orientation_of_frustum_.inverse_map()(Direction<3>::upper_xi());\n  const size_t mapped_dim_0 = orientation_of_frustum_.inverse_map()(0);\n  jacobian_matrix.get(mapped_dim_0, 0) =\n      delta_x_xi_ + delta_x_xi_zeta_ * cap_zeta;\n  if (equiangular_map_at_inner_ or equiangular_map_at_outer_) {\n    jacobian_matrix.get(mapped_dim_0, 0) *= cap_xi_transition_deriv;\n  }\n  if (mapped_xi.side() == Side::Lower) {\n    jacobian_matrix.get(mapped_dim_0, 0) *= -1.0;\n  }\n\n  // dX_deta\n  const auto mapped_eta =\n      orientation_of_frustum_.inverse_map()(Direction<3>::upper_eta());\n  const size_t mapped_dim_1 = orientation_of_frustum_.inverse_map()(1);\n  jacobian_matrix.get(mapped_dim_1, 1) =\n      delta_y_eta_ + delta_y_eta_zeta_ * cap_zeta;\n  if (equiangular_map_at_inner_ or equiangular_map_at_outer_) {\n    jacobian_matrix.get(mapped_dim_1, 1) *= cap_eta_transition_deriv;\n  }\n  if (mapped_eta.side() == Side::Lower) {\n    jacobian_matrix.get(mapped_dim_1, 1) *= -1.0;\n  }\n\n  // dX_dzeta\n  std::array<ReturnType, 3> dX_dzeta = discrete_rotation(\n      orientation_of_frustum_,\n      std::array<ReturnType, 3>{\n          {delta_x_zeta_ + delta_x_xi_zeta_ * cap_xi_transition +\n               (delta_x_xi_ + delta_x_xi_zeta_ * cap_zeta) * 0.5 *\n                   (cap_xi_upper - cap_xi_zero),\n           delta_y_zeta_ + delta_y_eta_zeta_ * cap_eta_transition +\n               (delta_y_eta_ + delta_y_eta_zeta_ * cap_zeta) * 0.5 *\n                   (cap_eta_upper - cap_eta_zero),\n           make_with_value<ReturnType>(zeta, delta_z_zeta_)}});\n\n  if (zeta_distribution_ != Distribution::Linear) {\n    dX_dzeta[0] *= cap_zeta_deriv;\n    dX_dzeta[1] *= cap_zeta_deriv;\n    dX_dzeta[2] *= cap_zeta_deriv;\n  }\n\n  get<0, 2>(jacobian_matrix) = dX_dzeta[0];\n  get<1, 2>(jacobian_matrix) = dX_dzeta[1];\n  get<2, 2>(jacobian_matrix) = dX_dzeta[2];\n\n  if (sphericity_ > 0.0) {\n    const ReturnType flat_frustum_x = sigma_x_ + delta_x_xi_ * cap_xi_upper +\n                                      delta_x_zeta_ +\n                                      delta_x_xi_zeta_ * cap_xi_upper;\n\n    const ReturnType flat_frustum_y = sigma_y_ + delta_y_eta_ * cap_eta_upper +\n                                      delta_y_zeta_ +\n                                      delta_y_eta_zeta_ * cap_eta_upper;\n\n    const double flat_frustum_z = sigma_z_ + delta_z_zeta_;\n\n    const ReturnType one_over_mag_flat =\n        1.0 / sqrt(square(flat_frustum_x) + square(flat_frustum_y) +\n                   square(flat_frustum_z));\n\n    const ReturnType flat_frustum_x_hat = flat_frustum_x * one_over_mag_flat;\n\n    const ReturnType flat_frustum_y_hat = flat_frustum_y * one_over_mag_flat;\n\n    const ReturnType flat_frustum_z_hat = flat_frustum_z * one_over_mag_flat;\n\n    const ReturnType r_over_mag_flat = radius_ * one_over_mag_flat;\n\n    const double s_over_two = 0.5 * sphericity_;\n\n    // delta_dX_dxi\n    std::array<ReturnType, 3> delta_dX_dxi = discrete_rotation(\n        orientation_of_frustum_,\n        s_over_two * (1.0 + cap_zeta) * (delta_x_xi_ + delta_x_xi_zeta_) *\n            std::array<ReturnType, 3>{\n                {(r_over_mag_flat * (1.0 - square(flat_frustum_x_hat)) - 1.0),\n                 -1.0 * r_over_mag_flat * flat_frustum_y_hat *\n                     flat_frustum_x_hat,\n                 -1.0 * r_over_mag_flat * flat_frustum_z_hat *\n                     flat_frustum_x_hat}});\n\n    if (equiangular_map_at_inner_ or equiangular_map_at_outer_) {\n      delta_dX_dxi[0] *= cap_xi_upper_deriv;\n      delta_dX_dxi[1] *= cap_xi_upper_deriv;\n      delta_dX_dxi[2] *= cap_xi_upper_deriv;\n    }\n\n    get<0, 0>(jacobian_matrix) += delta_dX_dxi[0];\n    get<1, 0>(jacobian_matrix) += delta_dX_dxi[1];\n    get<2, 0>(jacobian_matrix) += delta_dX_dxi[2];\n\n    // delta_dX_deta\n    std::array<ReturnType, 3> delta_dX_deta = discrete_rotation(\n        orientation_of_frustum_,\n        s_over_two * (1.0 + cap_zeta) * (delta_y_eta_ + delta_y_eta_zeta_) *\n            std::array<ReturnType, 3>{\n                {-1.0 * r_over_mag_flat * flat_frustum_x_hat *\n                     flat_frustum_y_hat,\n                 r_over_mag_flat * (1.0 - square(flat_frustum_y_hat)) - 1.0,\n                 -1.0 * r_over_mag_flat * flat_frustum_z_hat *\n                     flat_frustum_y_hat}});\n\n    if (equiangular_map_at_inner_ or equiangular_map_at_outer_) {\n      delta_dX_deta[0] *= cap_eta_upper_deriv;\n      delta_dX_deta[1] *= cap_eta_upper_deriv;\n      delta_dX_deta[2] *= cap_eta_upper_deriv;\n    }\n\n    get<0, 1>(jacobian_matrix) += delta_dX_deta[0];\n    get<1, 1>(jacobian_matrix) += delta_dX_deta[1];\n    get<2, 1>(jacobian_matrix) += delta_dX_deta[2];\n\n    // delta_dX_dzeta\n    std::array<ReturnType, 3> delta_dX_dzeta = discrete_rotation(\n        orientation_of_frustum_,\n        s_over_two * (r_over_mag_flat - 1.0) *\n            std::array<ReturnType, 3>{\n                {flat_frustum_x, flat_frustum_y,\n                 make_with_value<ReturnType>(zeta, flat_frustum_z)}});\n\n    if (zeta_distribution_ != Distribution::Linear) {\n      delta_dX_dzeta[0] *= cap_zeta_deriv;\n      delta_dX_dzeta[1] *= cap_zeta_deriv;\n      delta_dX_dzeta[2] *= cap_zeta_deriv;\n    }\n\n    get<0, 2>(jacobian_matrix) += delta_dX_dzeta[0];\n    get<1, 2>(jacobian_matrix) += delta_dX_dzeta[1];\n    get<2, 2>(jacobian_matrix) += delta_dX_dzeta[2];\n  }\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> Frustum::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  const auto jac = jacobian(source_coords);\n  return determinant_and_inverse(jac).second;\n}\n\nvoid Frustum::pup(PUP::er& p) {\n  size_t version = 5;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | orientation_of_frustum_;\n    if (version < 5) {\n      bool with_equiangular_map{false};\n      p | with_equiangular_map;\n      if (p.isUnpacking()) {\n        equiangular_map_at_inner_ = with_equiangular_map;\n        equiangular_map_at_outer_ = with_equiangular_map;\n      }\n    } else {\n      p | equiangular_map_at_inner_;\n      p | equiangular_map_at_outer_;\n    }\n    p | is_identity_;\n    if (version < 2 /*is unpacking*/) {\n      bool with_projective_map = false;  // unused\n      p | with_projective_map;\n      zeta_distribution_ =\n          with_projective_map ? Distribution::Projective : Distribution::Linear;\n    }\n    p | sigma_x_;\n    p | delta_x_zeta_;\n    p | delta_x_xi_;\n    p | delta_x_xi_zeta_;\n    p | sigma_y_;\n    p | delta_y_zeta_;\n    p | delta_y_eta_;\n    p | delta_y_eta_zeta_;\n    p | sigma_z_;\n    p | delta_z_zeta_;\n    p | w_plus_;\n    p | w_minus_;\n    p | sphericity_;\n    p | radius_;\n    p | phi_;\n  }\n  if (version >= 1) {\n    p | half_opening_angle_;\n    p | one_over_tan_half_opening_angle_;\n  } else {\n    half_opening_angle_ = M_PI_4;\n    one_over_tan_half_opening_angle_ = 1.0;\n  }\n  if (version >= 2) {\n    p | inner_radius_;\n    p | zeta_distribution_;\n  } else {\n    // Only `Distribution::Logarithmic` uses `inner_radius`, which\n    // was not implemented until version 2, so setting inner_radius = NaN\n    // should not affect maps unpacked from old versions. `inner_radius` is\n    // computed from frustum vertices so it is not necessary to compare\n    // `inner_radius` values between maps in the equality operator. Note that\n    // this means that a Frustum will have `inner_radius` == NaN when unpacking\n    // data from an old version.\n    inner_radius_ = std::numeric_limits<double>::signaling_NaN();\n  }\n  if (version >= 4) {\n    p | zeta_distribution_value_;\n  } else if (version == 3) {\n    double zeta_file = zeta_distribution_value_.value_or(0.0);\n    p | zeta_file;\n    if (zeta_distribution_ == CoordinateMaps::Distribution::Logarithmic) {\n      zeta_distribution_value_ = zeta_file;\n    } else {\n      zeta_distribution_value_ = std::nullopt;\n    }\n  } else {\n    if (zeta_distribution_ == CoordinateMaps::Distribution::Logarithmic) {\n      zeta_distribution_value_ =\n          -(radius_ + inner_radius_) / (radius_ - inner_radius_);\n    }\n  }\n}\n\nbool operator==(const Frustum& lhs, const Frustum& rhs) {\n  // Note that the inner radii are not compared, as `inner_radius` is computed\n  // the Frustum vertices and as quantities derived from\n  // vertices (e.g. `sigma_x`, `delta_x_xi`, etc.) are compared between the\n  // Frustums and equality between these derived quantities implies equality\n  // of their vertices. `inner_radius` might sometimes be NaN when unpacking\n  // Frustums from old data. For more details see the comment in Frustum::pup\n  // above.\n  // `radius` is also not compared for similar reasons.\n  return lhs.orientation_of_frustum_ == rhs.orientation_of_frustum_ and\n         lhs.equiangular_map_at_inner_ == rhs.equiangular_map_at_inner_ and\n         lhs.equiangular_map_at_outer_ == rhs.equiangular_map_at_outer_ and\n         lhs.is_identity_ == rhs.is_identity_ and\n         lhs.zeta_distribution_ == rhs.zeta_distribution_ and\n         lhs.sigma_x_ == rhs.sigma_x_ and\n         lhs.delta_x_zeta_ == rhs.delta_x_zeta_ and\n         lhs.delta_x_xi_ == rhs.delta_x_xi_ and\n         lhs.delta_x_xi_zeta_ == rhs.delta_x_xi_zeta_ and\n         lhs.sigma_y_ == rhs.sigma_y_ and\n         lhs.delta_y_zeta_ == rhs.delta_y_zeta_ and\n         lhs.delta_y_eta_ == lhs.delta_y_eta_ and\n         lhs.delta_y_eta_zeta_ == lhs.delta_y_eta_zeta_ and\n         lhs.sigma_z_ == rhs.sigma_z_ and\n         lhs.delta_z_zeta_ == rhs.delta_z_zeta_ and\n         lhs.w_plus_ == rhs.w_plus_ and lhs.w_minus_ == rhs.w_minus_ and\n         lhs.sphericity_ == rhs.sphericity_ and lhs.phi_ == rhs.phi_ and\n         lhs.half_opening_angle_ == rhs.half_opening_angle_ and\n         lhs.one_over_tan_half_opening_angle_ ==\n             rhs.one_over_tan_half_opening_angle_ and\n         lhs.zeta_distribution_value_ == rhs.zeta_distribution_value_;\n}\n\nbool operator!=(const Frustum& lhs, const Frustum& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                \\\n  Frustum::operator()(const std::array<DTYPE(data), 3>& source_coords) const; \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>  \\\n  Frustum::jacobian(const std::array<DTYPE(data), 3>& source_coords) const;   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>  \\\n  Frustum::inv_jacobian(const std::array<DTYPE(data), 3>& source_coords)      \\\n      const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (double, DataVector,\n                  std::reference_wrapper<const double>,\n                  std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, MAP_AUTODIFF_TYPES)\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Frustum.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief A reorientable map from the cube to a frustum.\n * \\image html frustum_table1.png \"A frustum with rectangular bases.\"\n *\n * ### Description and Specifiable Parameters\n * A map from the logical cube to the volume determined by interpolating between\n * two parallel rectangles each perpendicular to the \\f$z\\f$-axis. A Frustum map\n * \\f$\\vec{x}(\\xi,\\eta,\\zeta)\\f$ is determined by specifying two\n * heights \\f$z_1 = \\f$ `lower_bound` and \\f$z_2 = \\f$ `upper_bound` for the\n * positions of the rectangular bases of the frustum along the \\f$z-\\f$axis,\n * and the sizes of the rectangles are determined by the eight values passed to\n * `face_vertices`:\n *\n * \\f{align*}{\n * &\\textrm{lower x upper base} : x^{+\\zeta}_{-\\xi,-\\eta} = x(-1,-1,1)\\\\\n * &\\textrm{lower x lower base} : x^{-\\zeta}_{-\\xi,-\\eta} = x(-1,-1,-1)\\\\\n * &\\textrm{upper x upper base} : x^{+\\zeta}_{+\\xi,+\\eta} = x(1,1,1)\\\\\n * &\\textrm{upper x lower base} : x^{-\\zeta}_{+\\xi,+\\eta} = x(1,1,-1)\\\\\n * &\\textrm{lower y upper base} : y^{+\\zeta}_{-\\xi,-\\eta} = y(-1,-1,1)\\\\\n * &\\textrm{lower y lower base} : y^{-\\zeta}_{-\\xi,-\\eta} = y(-1,-1,-1)\\\\\n * &\\textrm{upper y upper base} : y^{+\\zeta}_{+\\xi,+\\eta} = y(1,1,1)\\\\\n * &\\textrm{upper y lower base} : y^{-\\zeta}_{+\\xi,+\\eta} = y(1,1,-1)\\f}\n *\n * \\note As an example, consider a frustum along the z-axis, with the lower base\n * starting at (x,y) = (-2.0,3.0) and extending to\n * (2.0,5.0), and with the upper base extending from (0.0,1.0) to (1.0,3.0).\n * The corresponding value for `face_vertices` is `{{{{-2.0,3.0}}, {{2.0,5.0}},\n * {{0.0,1.0}}, {{1.0,3.0}}}}`.\n *\n * In the case where the two rectangles are geometrically similar, the volume\n * formed is a geometric frustum. However, this coordinate map generalizes to\n * rectangles which need not be similar.\n * The user may reorient the frustum by passing an OrientationMap to the\n * constructor. If `with_equiangular_map` is true, then this coordinate map\n * applies a tangent function mapping to the logical \\f$\\xi\\f$ and \\f$\\eta\\f$\n * coordinates. We then refer to the generalized\n * logical coordinates as \\f$\\Xi\\f$ and \\f$\\mathrm{H}\\f$. If\n * `projective_scale_factor` is set to a quantity other than unity, then this\n * coordinate map applies a rational function mapping to the logical \\f$\\zeta\\f$\n * coordinate. This generalized logical coordinate is referred to as\n * \\f$\\mathrm{Z}\\f$. If `auto_projective_scale_factor` is `true`, the\n * user-specified `projective_scale_factor` is ignored and an appropriate value\n * for `projective_scale_factor` is computed based on the values passed to\n * `face_vertices`. See the\n * [page on redistributing gridpoints](@ref redistributing_gridpoints)\n * to see more detailed information on equiangular variables and projective\n * scaling.\n *\n * ### Coordinate Map and Jacobian\n * In terms of the `face_vertices` variables, we define the\n * following auxiliary variables:\n *\n * \\f{align*}{\\Sigma^x &= \\frac{1}{4}(x^{+\\zeta}_{-\\xi,-\\eta} +\n * x^{+\\zeta}_{+\\xi,+\\eta} + x^{-\\zeta}_{-\\xi,-\\eta} + x^{-\\zeta}_{+\\xi,+\\eta})\n * \\\\\n * \\Delta^x_{\\zeta} &= \\frac{1}{4}(x^{+\\zeta}_{-\\xi,-\\eta} +\n * x^{+\\zeta}_{+\\xi,+\\eta} - x^{-\\zeta}_{-\\xi,-\\eta} - x^{-\\zeta}_{+\\xi,+\\eta})\n * \\\\\n * \\Delta^x_{\\xi} &= \\frac{1}{4}(x^{+\\zeta}_{+\\xi,+\\eta} -\n * x^{+\\zeta}_{-\\xi,-\\eta} + x^{-\\zeta}_{+\\xi,+\\eta} - x^{-\\zeta}_{-\\xi,-\\eta})\n * \\\\\n * \\Delta^x_{\\xi\\zeta} &= \\frac{1}{4}(x^{+\\zeta}_{+\\xi,+\\eta} -\n * x^{+\\zeta}_{-\\xi,-\\eta} - x^{-\\zeta}_{+\\xi,+\\eta} + x^{-\\zeta}_{-\\xi,-\\eta})\n * \\\\\n * \\Sigma^y &= \\frac{1}{4}(y^{+\\zeta}_{-\\xi,-\\eta} +\n * y^{+\\zeta}_{+\\xi,+\\eta} + y^{-\\zeta}_{-\\xi,-\\eta} + y^{-\\zeta}_{+\\xi,+\\eta})\n * \\\\\n * \\Delta^y_{\\zeta} &= \\frac{1}{4}(y^{+\\zeta}_{-\\xi,-\\eta} +\n * y^{+\\zeta}_{+\\xi,+\\eta} - y^{-\\zeta}_{-\\xi,-\\eta} - y^{-\\zeta}_{+\\xi,+\\eta})\n * \\\\\n * \\Delta^y_{\\eta} &= \\frac{1}{4}(y^{+\\zeta}_{+\\xi,+\\eta} -\n * y^{+\\zeta}_{-\\xi,-\\eta} + y^{-\\zeta}_{+\\xi,+\\eta} - y^{-\\zeta}_{-\\xi,-\\eta})\n * \\\\\n * \\Delta^y_{\\eta\\zeta} &= \\frac{1}{4}(y^{+\\zeta}_{+\\xi,+\\eta} -\n * y^{+\\zeta}_{-\\xi,-\\eta} - y^{-\\zeta}_{+\\xi,+\\eta} + y^{-\\zeta}_{-\\xi,-\\eta})\n * \\\\\n * \\Sigma^z &= \\frac{z_1 + z_2}{2}\\\\\n * \\Delta^z &= \\frac{z_2 - z_1}{2}\n * \\f}\n *\n * The full map is then given by:\n * \\f[\\vec{x}(\\xi,\\eta,\\zeta) = \\begin{bmatrix}\n * \\Sigma^x + \\Delta^x_{\\xi}\\Xi + (\\Delta^x_{\\zeta} +\n * \\Delta^x_{\\xi\\zeta}\\Xi)\\mathrm{Z}\\\\\n * \\Sigma^y + \\Delta^y_{\\eta}\\mathrm{H} + (\\Delta^y_{\\zeta} +\n * \\Delta^y_{\\eta\\zeta}\\mathrm{H})\\mathrm{Z}\\\\\n * \\Sigma^z + \\Delta^z\\mathrm{Z}\\\\\n * \\end{bmatrix}\\f]\n *\n * With Jacobian: \\f[J =\n * \\begin{bmatrix}\n * (\\Delta^x_{\\xi} + \\Delta^x_{\\xi\\zeta}\\mathrm{Z})\\Xi' &\n * 0 & (\\Delta^x_{\\zeta}+ \\Delta^x_{\\xi\\zeta}\\Xi)\\mathrm{Z}' \\\\\n * 0 & (\\Delta^y_{\\eta} + \\Delta^y_{\\eta\\zeta}\\mathrm{Z})\\mathrm{H}' &\n * (\\Delta^y_{\\zeta} + \\Delta^y_{\\eta\\zeta}\\mathrm{H})\\mathrm{Z}'\\\\\n * 0 & 0 & \\Delta^z\\mathrm{Z}' \\\\\n * \\end{bmatrix}\n * \\f]\n *\n * ### Suggested values for the projective scale factor\n * \\note This section assumes familiarity with projective scaling as discussed\n * on the [page on redistributing gridpoints](@ref redistributing_gridpoints).\n *\n * When constructing a Frustum map, it is not immediately obvious what value of\n * \\f$w_{\\delta}\\f$ to use in the projective map; here we present a choice of\n * \\f$w_{\\delta}\\f$ that will produce minimal grid distortion. We cover two\n * cases: the first is the special case in which the two rectangular Frustum\n * bases are related by a simple scale factor; the second is the general case.\n *\n * \\image html ProjectionOfCube.png \"Trapezoids from squares. (Davide Cervone)\"\n *\n * As seen in Cervone's [Cubes and Hypercubes Rotating]\n * (http://www.math.union.edu/~dpvc/math/4D/rotation/welcome.html), there is a\n * special case in which the inverse projection of a trapezoid is not another\n * trapezoid, but a rectangle where the bases are congruent.\n * Most often one will want to use the special value of \\f$w_{\\delta}\\f$ where\n * this occurs. This value of \\f$w_{\\delta}\\f$ corresponds to the projective\n * transformation mapping a rectangular prism in an ambient 4D space with\n * congruent faces at \\f$w=1\\f$ and \\f$w=w_{\\delta}\\f$ to the frustum in the\n * plane \\f$w=1\\f$:\n *\n * \\f[w_{\\delta} = \\frac{L_1}{L_2}\\f]\n *\n * where \\f$\\frac{L_1}{L_2}\\f$ is the ratio between any pair of\n * corresponding side lengths of the \\f$z_1\\f$ and \\f$z_2\\f$-bases of the\n * frustum, respectively.\n *\n * For the general case one will want to use the value:\n * \\f[w_{\\delta} =\n * \\frac{\\sqrt{(x^{-\\zeta}_{+\\xi,+\\eta} - x^{-\\zeta}_{-\\xi,+\\eta})\n *             (y^{-\\zeta}_{+\\xi,+\\eta} - y^{-\\zeta}_{+\\xi,-\\eta})}}\n * {\\sqrt{(x^{+\\zeta}_{+\\xi,+\\eta} - x^{+\\zeta}_{-\\xi,+\\eta})\n *        (y^{+\\zeta}_{+\\xi,+\\eta} - y^{+\\zeta}_{+\\xi,-\\eta})}}\\f]\n *\n * This is the value for \\f$w_{\\delta}\\f$ used by this CoordinateMap when\n * `auto_projective_scale_factor` is `true`.\n *\n * ### Bulged Frustum Coordinate Map and Jacobian\n * Each of the frustum faces in the frustum map given above are flat, but the\n * upper +z face of the frustum can be bulged out by setting a non-zero value\n * for the `sphericity`, where a value of `0.0` corresponds to the usual flat-\n * face, and a value of `1.0` corresponds to a value of fully spherical. Using\n * OrientationMaps allows the user to create a set of frustums that fully cover\n * a spherical surface. The radius of the sphere is determined by the corner of\n * the frustum that is furthest from the origin.\n *\n * The full map is given by:\n * \\f[\\vec{x}(\\xi,\\eta,\\zeta) = \\begin{bmatrix}\n * \\Sigma^x + \\Delta^x_{\\xi}\\Xi + (\\Delta^x_{\\zeta} +\n * \\Delta^x_{\\xi\\zeta}\\Xi)\\mathrm{Z}\\\\\n * \\Sigma^y + \\Delta^y_{\\eta}\\mathrm{H} + (\\Delta^y_{\\zeta} +\n * \\Delta^y_{\\eta\\zeta}\\mathrm{H})\\mathrm{Z}\\\\\n * \\Sigma^z + \\Delta^z\\mathrm{Z}\\\\\n * \\end{bmatrix}\n * + s \\frac{1 +\n * \\mathrm{Z}}{2}\\left(\\frac{R}{|\\vec{\\sigma}_{\\mathrm{+z}}|}-1\\right)\n * \\vec{\\sigma}_{\\mathrm{+z}}\n * \\f]\n *\n * where \\f$R\\f$ is the radius, \\f$s\\f$ is the `sphericity`, and\n * \\f$\\vec{\\sigma}_{\\mathrm{+z}}\\f$ is given by:\n * \\f[\n * \\vec{\\sigma}_{\\mathrm{+z}} =\n * \\begin{bmatrix}\n * \\Sigma^x + \\Delta^x_{\\xi}\\Xi + \\Delta^x_{\\zeta} +\n * \\Delta^x_{\\xi\\zeta}\\Xi\\\\\n * \\Sigma^y + \\Delta^y_{\\eta}\\mathrm{H} + \\Delta^y_{\\zeta} +\n * \\Delta^y_{\\eta\\zeta}\\mathrm{H}\\\\\n * \\Sigma^z + \\Delta^z\\\\\n * \\end{bmatrix}.\n * \\f]\n *\n * The Jacobian is:\n * \\f[J =\n * \\begin{bmatrix}\n * (\\Delta^x_{\\xi} + \\Delta^x_{\\xi\\zeta}\\mathrm{Z})\\Xi' &\n * 0 & (\\Delta^x_{\\zeta}+ \\Delta^x_{\\xi\\zeta}\\Xi)\\mathrm{Z}' \\\\\n * 0 & (\\Delta^y_{\\eta} + \\Delta^y_{\\eta\\zeta}\\mathrm{Z})\\mathrm{H}' &\n * (\\Delta^y_{\\zeta} + \\Delta^y_{\\eta\\zeta}\\mathrm{H})\\mathrm{Z}'\\\\\n * 0 & 0 & \\Delta^z\\mathrm{Z}' \\\\\n * \\end{bmatrix}\n * +\n * \\frac{s}{2} \\left\\{ \\left(\\frac{R}{|\\vec{\\sigma}_{\\mathrm{+z}}|}-1\\right)\n * \\vec{\\sigma}_{\\mathrm{+z}}\\hat{\\zeta}^{\\intercal}\\mathrm{Z}'+\n * (1+\\mathrm{Z})\\left(\\frac{R}{|\\vec{\\sigma}_{\\mathrm{+z}}|}\\left(\n * \\mathbb{1}-\\frac{\\vec{\\sigma}_{\\mathrm{+z}}\n * \\vec{\\sigma}_{\\mathrm{+z}}^{\\intercal}}\n * {|\\vec{\\sigma}_{\\mathrm{+z}}|^2}\\right)-\\mathbb{1}\\right)\n * J_{\\sigma}\\right\\},\n * \\f]\n *\n * where \\f$\\hat{\\zeta}\\f$ is the row vector \\f$[0, 0, 1]\\f$, and\n * \\f$J_{\\sigma}\\f$ is the Jacobian of the upper +z surface, given by:\n * \\f[\n * J_{\\sigma} =\n * \\begin{bmatrix}\n * (\\Delta^x_{\\xi} + \\Delta^x_{\\xi\\zeta})\\Xi' &\n * 0 & 0 \\\\\n * 0 & (\\Delta^y_{\\eta} + \\Delta^y_{\\eta\\zeta})\\mathrm{H}' &\n * 0 \\\\\n * 0 & 0 & 0 \\\\\n * \\end{bmatrix}.\n * \\f]\n *\n * ### Using the Frustum Transition between Equiangular Maps\n * Using the parameter \\f$\\phi\\f$, the user can specify whether to use the\n * full equiangular map on the upper +z face, or whether to use only the upper\n * or lower half of the equiangular map on the upper +z face. This capability\n * is used in the BinaryCompactObject Domain, where two equiangular maps around\n * each spherical object must be transitioned into a single equiangular map at\n * the outer boundary. The transition region that interpolates between these\n * two regions is the layer of Blocks with the Frustum maps.\n *\n * The full map is obtained by setting\n * \\f$\\Xi = \\frac{1 + \\mathrm{Z}}{2}\\Xi_{\\mathrm{upper}}(\\xi,\\zeta) +\n *          \\frac{1 - \\mathrm{Z}}{2}\\Xi_{0}(\\xi)\\f$\n *\n * for the volume map \\f$\\vec{x}(\\xi,\\eta,\\zeta)\\f$, and\n * \\f$\\Xi = \\Xi_{\\mathrm{upper}}\\f$ for the surface map\n * \\f$\\vec{\\sigma}(\\xi,\\eta)\\f$, where \\f$\\Xi_{\\mathrm{upper}}\\f$ is the\n * \\f$\\phi\\f$-dependent generalized equiangular map, which corresponds to the\n * lower half of the equiangular map when \\f$\\phi = -1\\f$,\n * and to the upper half of the equiangular map when \\f$\\phi = 1 \\f$.\n * It is given by:\n *\n * \\f[\\Xi_{\\mathrm{upper}} = (1 +\n * \\phi^2)\\tan(\\frac{\\pi}{4}\\frac{\\xi+\\phi}{1+\\phi^2})-\\phi\\f]\n *\n * For \\f$\\phi=0\\f$ this generalized map simplifies to the original equiangular\n * map \\f$\\Xi_0 = \\tan(\\frac{\\pi}{4}\\xi)\\f$.\n *\n * This function is compatible with the ability to bulge out the Frustum; the\n * map and Jacobian remain unchanged, modulo this substitution.\n */\nclass Frustum {\n public:\n  static constexpr size_t dim = 3;\n  /*!\n   * Constructs a frustum.\n   * \\param face_vertices An array of four 2D vertices that define the (x, y)\n   * coordinates of the upper and lower base of the frustum.\n   * \\param lower_bound z distance from the origin to the lower base of the\n   * frustum.\n   * \\param upper_bound z distance from the origin to the upper base of the\n   * frustum.\n   * \\param orientation_of_frustum The orientation of the frustum in 3D space.\n   * \\param equiangular_map_at_outer Determines whether to apply a tangent\n   * function mapping to the logical coordinates (true) or not (false) at the\n   * larger side of the frustum.\n   * \\param equiangular_map_at_inner Determines whether to apply a tangent\n   * function mapping to the logical coordinates (true) or not (false) at the\n   * smaller side of the frustum.\n   * \\param zeta_distribution Whether to apply a linear, logarithmic, or\n   * projective mapping to the logical zeta coordinate.\n   * \\param distribution_value In the case of a projective map, the projective\n   * scale factor, otherwise unused.\n   * \\param sphericity Value between 0 and 1 which determines whether the\n   * surface of the upper base of the Frustum is flat (value of 0), spherical\n   * (value of 1), or somewhere in between.\n   * \\param transition_phi Determines whether the equiangular map used conforms\n   * to a lower half wedge (value of -1), an upper half wedge (value of +1), or\n   * a full wedge (value of 0).\n   * \\param opening_angle The opening angle of the wedge the frustum will\n   * conform to (have conforming boundaries with).\n   */\n  Frustum(const std::array<std::array<double, 2>, 4>& face_vertices,\n          double lower_bound, double upper_bound,\n          OrientationMap<3> orientation_of_frustum,\n          bool equiangular_map_at_outer = false,\n          bool equiangular_map_at_inner = false,\n          Distribution zeta_distribution = Distribution::Linear,\n          std::optional<double> distribution_value = std::nullopt,\n          double sphericity = 0.0, double transition_phi = 0.0,\n          double opening_angle = M_PI_2);\n  Frustum() = default;\n  ~Frustum() = default;\n  Frustum(Frustum&&) = default;\n  Frustum(const Frustum&) = default;\n  Frustum& operator=(const Frustum&) = default;\n  Frustum& operator=(Frustum&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// Returns std::nullopt if \\f$z\\f$ is at or beyond the \\f$z\\f$-coordinate of\n  /// the apex of the pyramid, tetrahedron, or triangular prism that is\n  /// formed by extending the `Frustum` (for a\n  /// \\f$z\\f$-oriented `Frustum`).\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  friend bool operator==(const Frustum& lhs, const Frustum& rhs);\n\n  OrientationMap<3> orientation_of_frustum_ =\n      OrientationMap<3>::create_aligned();\n  bool equiangular_map_at_outer_{false};\n  bool equiangular_map_at_inner_{false};\n  bool is_identity_{false};\n  Distribution zeta_distribution_ = Distribution::Linear;\n  std::optional<double> zeta_distribution_value_{std::nullopt};\n  double sigma_x_{std::numeric_limits<double>::signaling_NaN()};\n  double delta_x_zeta_{std::numeric_limits<double>::signaling_NaN()};\n  double delta_x_xi_{std::numeric_limits<double>::signaling_NaN()};\n  double delta_x_xi_zeta_{std::numeric_limits<double>::signaling_NaN()};\n  double sigma_y_{std::numeric_limits<double>::signaling_NaN()};\n  double delta_y_zeta_{std::numeric_limits<double>::signaling_NaN()};\n  double delta_y_eta_{std::numeric_limits<double>::signaling_NaN()};\n  double delta_y_eta_zeta_{std::numeric_limits<double>::signaling_NaN()};\n  double sigma_z_{std::numeric_limits<double>::signaling_NaN()};\n  double delta_z_zeta_{std::numeric_limits<double>::signaling_NaN()};\n  double w_plus_{std::numeric_limits<double>::signaling_NaN()};\n  double w_minus_{std::numeric_limits<double>::signaling_NaN()};\n  double sphericity_{std::numeric_limits<double>::signaling_NaN()};\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  double phi_{std::numeric_limits<double>::signaling_NaN()};\n  double half_opening_angle_{std::numeric_limits<double>::signaling_NaN()};\n  double one_over_tan_half_opening_angle_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double inner_radius_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const Frustum& lhs, const Frustum& rhs);\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Identity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain::CoordinateMaps {\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Identity<Dim>::operator()(\n    const std::array<T, Dim>& source_coords) const {\n  return make_array<tt::remove_cvref_wrap_t<T>, Dim>(source_coords);\n}\n\ntemplate <size_t Dim>\nstd::optional<std::array<double, Dim>> Identity<Dim>::inverse(\n    const std::array<double, Dim>& target_coords) const {\n  return make_array<double, Dim>(target_coords);\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nIdentity<Dim>::jacobian(const std::array<T, Dim>& source_coords) const {\n  return identity<Dim>(dereference_wrapper(source_coords[0]));\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nIdentity<Dim>::inv_jacobian(const std::array<T, Dim>& source_coords) const {\n  return identity<Dim>(dereference_wrapper(source_coords[0]));\n}\n\ntemplate class Identity<1>;\ntemplate class Identity<2>;\ntemplate class Identity<3>;\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)> \\\n  Identity<DIM(data)>::operator()(                                     \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  Identity<DIM(data)>::jacobian(                                       \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  Identity<DIM(data)>::inv_jacobian(                                   \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (1, 2, 3),\n    (double, DataVector,\n     std::reference_wrapper<const double>,\n     std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), MAP_AUTODIFF_TYPES)\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Identity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class Identity.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/// \\ingroup CoordinateMapsGroup\n/// Identity  map from \\f$\\xi \\rightarrow x\\f$.\ntemplate <size_t Dim>\nclass Identity {\n public:\n  static constexpr size_t dim = Dim;\n\n  Identity() = default;\n  ~Identity() = default;\n  Identity(const Identity&) = default;\n  Identity(Identity&&) = default;  // NOLINT\n  Identity& operator=(const Identity&) = default;\n  Identity& operator=(Identity&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> operator()(\n      const std::array<T, Dim>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, Dim>> inverse(\n      const std::array<double, Dim>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jacobian(\n      const std::array<T, Dim>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, Dim>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  bool is_identity() const { return true; }\n\n  static constexpr bool supports_hessian{true};\n};\n\ntemplate <size_t Dim>\ninline constexpr bool operator==(const CoordinateMaps::Identity<Dim>& /*lhs*/,\n                                 const CoordinateMaps::Identity<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\ninline constexpr bool operator!=(const CoordinateMaps::Identity<Dim>& lhs,\n                                 const CoordinateMaps::Identity<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Interval.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n\n#include <cmath>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nInterval::Interval(const double A, const double B, const double a,\n                   const double b, const Distribution distribution,\n                   const std::optional<double> singularity_pos)\n    : A_(A),\n      B_(B),\n      a_(a),\n      b_(b),\n      distribution_(distribution),\n      singularity_pos_(singularity_pos) {\n  ASSERT(\n      A != B and a != b,\n      \"The left and right boundaries for both source and target interval must \"\n      \"differ, but are; [\"\n          << A << \", \" << B << \"] -> [\" << a << \", \" << b << \"]\");\n  if (distribution == domain::CoordinateMaps::Distribution::Logarithmic or\n      distribution == domain::CoordinateMaps::Distribution::Inverse) {\n    ASSERT(singularity_pos.has_value(),\n           \"Both the logarithmic and inverse distribution require \"\n           \"`singularity_pos` to be specified explicitly.\");\n    ASSERT(std::min(a, b) > singularity_pos.value() or\n               std::max(a, b) < singularity_pos.value(),\n           \"The singularity for the logarithmic and inverse Interval falls \"\n           \"inside the domain with [\"\n               << std::min(a, b) << \", \" << std::max(a, b) << \"], but is\"\n               << singularity_pos.value());\n  }\n  is_identity_ =\n      distribution == domain::CoordinateMaps::Distribution::Linear and\n      A == a and B == b;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 1> Interval::operator()(\n    const std::array<T, 1>& source_coords) const {\n  switch (distribution_) {\n    case Distribution::Linear: {\n      return {{((b_ - a_) * source_coords[0] + a_ * B_ - b_ * A_) / (B_ - A_)}};\n    }\n    case Distribution::Equiangular: {\n      return {\n          {0.5 * (a_ + b_ +\n                  (b_ - a_) * tan(M_PI_4 * (2.0 * source_coords[0] - B_ - A_) /\n                                  (B_ - A_)))}};\n    }\n    case Distribution::Logarithmic: {\n      const double singularity_pos = singularity_pos_.value();\n      const double logarithmic_zero =\n          0.5 * (log((b_ - singularity_pos) * (a_ - singularity_pos)));\n      const double logarithmic_rate =\n          0.5 * (log((b_ - singularity_pos) / (a_ - singularity_pos)));\n      const double singularity_sign =\n          std::min(a_, b_) > singularity_pos ? 1. : -1.;\n      return {{singularity_sign *\n                   exp(logarithmic_zero +\n                       logarithmic_rate * (2.0 * source_coords[0] - B_ - A_) /\n                           (B_ - A_)) +\n               singularity_pos}};\n    }\n    case Distribution::Inverse: {\n      const double singularity_pos = singularity_pos_.value();\n      return {\n          {2.0 * (a_ - singularity_pos) * (b_ - singularity_pos) /\n               (a_ + b_ - 2.0 * singularity_pos -\n                (b_ - a_) / (B_ - A_) * (2.0 * source_coords[0] - B_ - A_)) +\n           singularity_pos}};\n    }\n    default:\n      ERROR(\"Unknown domain::CoordinateMaps::Distribution type for Interval\");\n  }\n}\n\nstd::optional<std::array<double, 1>> Interval::inverse(\n    const std::array<double, 1>& target_coords) const {\n  switch (distribution_) {\n    case Distribution::Linear: {\n      return {\n          {{((B_ - A_) * target_coords[0] - a_ * B_ + b_ * A_) / (b_ - a_)}}};\n    }\n    case Distribution::Equiangular: {\n      return {\n          {{0.5 * (A_ + B_ +\n                   (B_ - A_) / M_PI_4 *\n                       atan((2.0 * target_coords[0] - a_ - b_) / (b_ - a_)))}}};\n    }\n    case Distribution::Logarithmic: {\n      const double singularity_pos = singularity_pos_.value();\n      const double logarithmic_zero =\n          0.5 * (log((b_ - singularity_pos) * (a_ - singularity_pos)));\n      const double logarithmic_rate =\n          0.5 * (log((b_ - singularity_pos) / (a_ - singularity_pos)));\n      const double singularity_sign =\n          std::min(a_, b_) > singularity_pos ? 1. : -1.;\n      return {{{0.5 * ((B_ - A_) *\n                           (log(singularity_sign *\n                                (target_coords[0] - singularity_pos)) -\n                            logarithmic_zero) /\n                           logarithmic_rate +\n                       B_ + A_)}}};\n    }\n    case Distribution::Inverse: {\n      const double singularity_pos = singularity_pos_.value();\n      return {\n          {{0.5 * (A_ + B_ +\n                   (B_ - A_) / (b_ - a_) *\n                       ((a_ - singularity_pos) + (b_ - singularity_pos) -\n                        2. * (a_ - singularity_pos) * (b_ - singularity_pos) /\n                            (target_coords[0] - singularity_pos)))}}};\n    }\n    default:\n      ERROR(\"Unknown domain::CoordinateMaps::Distribution type for Interval\");\n  }\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> Interval::jacobian(\n    const std::array<T, 1>& source_coords) const {\n  auto jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n  switch (distribution_) {\n    case Distribution::Linear: {\n      get<0, 0>(jacobian_matrix) = (b_ - a_) / (B_ - A_);\n      return jacobian_matrix;\n    }\n    case Distribution::Equiangular: {\n      const tt::remove_cvref_wrap_t<T> tan_variable =\n          tan((2.0 * source_coords[0] - B_ - A_) * M_PI_4 / (B_ - A_));\n      get<0, 0>(jacobian_matrix) =\n          M_PI_4 * (b_ - a_) / (B_ - A_) * (1.0 + square(tan_variable));\n      return jacobian_matrix;\n    }\n    case Distribution::Logarithmic: {\n      const double singularity_pos = singularity_pos_.value();\n      const double logarithmic_zero =\n          0.5 * (log((b_ - singularity_pos) * (a_ - singularity_pos)));\n      const double logarithmic_rate =\n          0.5 * (log((b_ - singularity_pos) / (a_ - singularity_pos)));\n      const double singularity_sign =\n          std::min(a_, b_) > singularity_pos ? 1. : -1.;\n      get<0, 0>(jacobian_matrix) =\n          2.0 / (B_ - A_) * logarithmic_rate * singularity_sign *\n          exp(logarithmic_zero + logarithmic_rate *\n                                     (2.0 * source_coords[0] - B_ - A_) /\n                                     (B_ - A_));\n      return jacobian_matrix;\n    }\n    case Distribution::Inverse: {\n      const double singularity_pos = singularity_pos_.value();\n      get<0, 0>(jacobian_matrix) =\n          (a_ - singularity_pos) * (b_ - singularity_pos) * (b_ - a_) *\n          (B_ - A_) /\n          square((b_ - a_) * source_coords[0] + a_ * A_ - b_ * B_ +\n                 singularity_pos * (B_ - A_));\n      return jacobian_matrix;\n    }\n    default:\n      ERROR(\"Unknown domain::CoordinateMaps::Distribution type for Interval\");\n  }\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> Interval::inv_jacobian(\n    const std::array<T, 1>& source_coords) const {\n  auto inv_jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n  switch (distribution_) {\n    case Distribution::Linear: {\n      get<0, 0>(inv_jacobian_matrix) = (B_ - A_) / (b_ - a_);\n      return inv_jacobian_matrix;\n    }\n    case Distribution::Equiangular: {\n      const tt::remove_cvref_wrap_t<T> tan_variable =\n          tan(M_PI_4 * (2.0 * source_coords[0] - B_ - A_) / (B_ - A_));\n      get<0, 0>(inv_jacobian_matrix) =\n          (B_ - A_) / (M_PI_4 * (b_ - a_) * (1.0 + square(tan_variable)));\n      return inv_jacobian_matrix;\n    }\n    case Distribution::Logarithmic: {\n      const double singularity_pos = singularity_pos_.value();\n      const double logarithmic_zero =\n          0.5 * (log((b_ - singularity_pos) * (a_ - singularity_pos)));\n      const double logarithmic_rate =\n          0.5 * (log((b_ - singularity_pos) / (a_ - singularity_pos)));\n      const double singularity_sign =\n          std::min(a_, b_) > singularity_pos ? 1. : -1.;\n      get<0, 0>(inv_jacobian_matrix) =\n          0.5 * (B_ - A_) / logarithmic_rate * singularity_sign *\n          exp(-logarithmic_zero - logarithmic_rate *\n                                      (2.0 * source_coords[0] - B_ - A_) /\n                                      (B_ - A_));\n      return inv_jacobian_matrix;\n    }\n    case Distribution::Inverse: {\n      const double singularity_pos = singularity_pos_.value();\n      get<0, 0>(inv_jacobian_matrix) =\n          square((b_ - a_) * source_coords[0] + a_ * A_ - b_ * B_ +\n                 singularity_pos * (B_ - A_)) /\n          ((a_ - singularity_pos) * (b_ - singularity_pos) * (b_ - a_) *\n           (B_ - A_));\n      return inv_jacobian_matrix;\n    }\n    default:\n      ERROR(\"Unknown domain::CoordinateMaps::Distribution type for Interval\");\n  }\n}\n\nvoid Interval::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | A_;\n    p | B_;\n    p | a_;\n    p | b_;\n    p | distribution_;\n    p | singularity_pos_;\n    p | is_identity_;\n  }\n}\n\nbool operator==(const CoordinateMaps::Interval& lhs,\n                const CoordinateMaps::Interval& rhs) {\n  return lhs.A_ == rhs.A_ and lhs.B_ == rhs.B_ and lhs.a_ == rhs.a_ and\n         lhs.b_ == rhs.b_ and lhs.distribution_ == rhs.distribution_ and\n         lhs.singularity_pos_ == rhs.singularity_pos_;\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 1>                 \\\n  Interval::operator()(const std::array<DTYPE(data), 1>& source_coords) const; \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 1, Frame::NoFrame>   \\\n  Interval::jacobian(const std::array<DTYPE(data), 1>& source_coords) const;   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 1, Frame::NoFrame>   \\\n  Interval::inv_jacobian(const std::array<DTYPE(data), 1>& source_coords)      \\\n      const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (double, DataVector,\n                  std::reference_wrapper<const double>,\n                  std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, MAP_AUTODIFF_TYPES)\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Interval.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n * \\brief Maps \\f$\\xi\\f$ in the 1D interval \\f$[A, B]\\f$ to \\f$x\\f$ in the\n * interval \\f$[a, b]\\f$ according to a `domain::CoordinateMaps::Distribution`.\n *\n * \\details The mapping takes a `domain::CoordinateMaps::Distribution` and\n * distributes the grid points accordingly.\n *\n * The formula for the mapping is, in case of a `Linear` distribution\n * \\f{align}\n * x &= \\frac{b}{B-A} (\\xi-A) + \\frac{a}{B-A} (B-\\xi)\\\\\n * \\xi &=\\frac{B}{b-a} (x-a) + \\frac{A}{b-a} (b-x)\n * \\f}\n *\n * For every other distribution we use this linear mapping to map onto an\n * interval \\f$[-1, 1]\\f$, so we define:\n * \\f{align}\n * f(\\xi) &:= \\frac{A+B-2\\xi}{A-B} \\in [-1, 1]\\\\\n * g(x) &:= \\frac{a+b-2x}{a-b} \\in [-1, 1]\n * \\f}\n *\n * With this an `Equiangular` distribution is described by\n *\n * \\f{align}\n * x &= \\frac{a}{2} \\left(1-\\mathrm{tan}\\left(\\frac{\\pi}{4}f(\\xi)\\right)\\right)\n * + \\frac{b}{2} \\left(1+\\mathrm{tan}\\left(\\frac{\\pi}{4}f(\\xi)\\right)\\right)\\\\\n * \\xi &=\n * \\frac{A}{2} \\left(1-\\frac{4}{\\pi}\\mathrm{arctan}\\left(g(x)\\right)\\right) +\n * \\frac{B}{2} \\left(1+\\frac{4}{\\pi}\\mathrm{arctan}\\left(g(x)\\right)\\right)\n * \\f}\n *\n * \\note The equiangular distribution is intended to be used with the `Wedge`\n * map when equiangular coordinates are chosen for those maps. For more\n * information on this choice of coordinates, see the documentation for `Wedge`.\n *\n *\n * For both the `Logarithmic` and `Inverse` distribution, we first specify a\n * position for the singularity \\f$c:=\\f$`singularity_pos` outside the target\n * interval \\f$[a, b]\\f$.\n *\n * The `Logarithmic` distribution further requires the introduction of a\n * variable \\f$\\sigma\\f$ dependent on whether \\f$c\\f$ is left (\\f$ \\sigma =\n * 1\\f$) or right (\\f$ \\sigma = -1\\f$) of the target interval. With this, the\n * `Logarithmic` distribution is described by\n *\n * \\f{align}\n * x &= \\sigma\\, {\\rm exp}\\left(\\frac{\\ln(b-c)+\\ln(a-c)}{2} + f(\\xi)\n * \\frac{\\ln(b-c)-\\ln(a-c)}{2}\\right) + c\\\\\n * \\xi &= \\frac{B-A}{2}\\frac{2\\ln(\\sigma [x-c]) - \\ln(b-c) - \\ln(a-c)}{\\ln(b-c)\n * - \\ln(a-c)} + \\frac{B+A}{2} \\f}\n *\n * and the `Inverse` distribution by\n *\n * \\f{align}\n * x &= \\frac{2(a-c)(b-c)}{a+b-2c+(a-b)f(\\xi)} + c\\\\\n * \\xi &= -\\frac{A-B}{2(a-b)} \\left(\\frac{2(a-c)(b-c)}{x-c} - (a-c) -\n * (b-c)\\right) + \\frac{A+B}{2}\n * \\f}\n */\nclass Interval {\n public:\n  static constexpr size_t dim = 1;\n\n  Interval(double A, double B, double a, double b, Distribution distribution,\n           std::optional<double> singularity_pos = std::nullopt);\n\n  Interval() = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 1> operator()(\n      const std::array<T, 1>& source_coords) const;\n\n  std::optional<std::array<double, 1>> inverse(\n      const std::array<double, 1>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> jacobian(\n      const std::array<T, 1>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 1, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 1>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  friend bool operator==(const Interval& lhs, const Interval& rhs);\n\n  double A_{std::numeric_limits<double>::signaling_NaN()};\n  double B_{std::numeric_limits<double>::signaling_NaN()};\n  double a_{std::numeric_limits<double>::signaling_NaN()};\n  double b_{std::numeric_limits<double>::signaling_NaN()};\n  Distribution distribution_{Distribution::Linear};\n  std::optional<double> singularity_pos_{std::nullopt};\n  bool is_identity_{false};\n};\n\ninline bool operator!=(const CoordinateMaps::Interval& lhs,\n                       const CoordinateMaps::Interval& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/KerrHorizonConforming.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/KerrHorizonConforming.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nKerrHorizonConforming::KerrHorizonConforming(\n    const double mass, const std::array<double, 3> dimensionless_spin)\n    : spin_parameter_(mass * dimensionless_spin),\n      spin_mag_sq_(square(mass) * dot(dimensionless_spin, dimensionless_spin)) {\n  ASSERT(magnitude(dimensionless_spin) < 1.,\n         \"Dimensionless spin magnitude must be < 1. Given dimensionless spin: \"\n             << dimensionless_spin << \" with magnitude \"\n             << magnitude(dimensionless_spin));\n  ASSERT(mass > 0., \"Mass must be positive. Given mass: \" << mass);\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> KerrHorizonConforming::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  std::array<ReturnType, 3> result{};\n  ReturnType& stretch_fac = get<2>(result);\n  stretch_factor_square(make_not_null(&stretch_fac), source_coords);\n  stretch_fac = sqrt(stretch_fac);\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(result, i) = gsl::at(source_coords, i) * stretch_fac;\n  }\n  return result;\n}\n\nstd::optional<std::array<double, 3>> KerrHorizonConforming::inverse(\n    const std::array<double, 3>& target_coords) const {\n  const auto coords_mag_sq = dot(target_coords, target_coords);\n  const auto coords_sq_min_spin_sq = coords_mag_sq - spin_mag_sq_;\n  const auto coords_dot_spin = dot(target_coords, spin_parameter_);\n  const auto fac = (coords_sq_min_spin_sq +\n                    sqrt(coords_sq_min_spin_sq * coords_sq_min_spin_sq +\n                         4. * square(coords_dot_spin))) /\n                   (2. * coords_mag_sq);\n  // this is the way it was written in spec, but I dont think `fac` can\n  // ever be smaller than 0\n  return fac >= 0.\n             ? std::optional<std::array<double, 3>>(target_coords * sqrt(fac))\n             : std::nullopt;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nKerrHorizonConforming::jacobian(const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  tnsr::Ij<ReturnType, 3, Frame::NoFrame> jac(\n      get_size(dereference_wrapper(source_coords[0])));\n\n  // use allocations from `jac` for auxiliaries\n  ReturnType& fac = get<0, 0>(jac);\n  ReturnType& source_coords_sq = get<0, 1>(jac);\n  ReturnType& coords_dot_spin = get<0, 2>(jac);\n  ReturnType& subexpr_1 = get<1, 0>(jac);\n  ReturnType& subexpr_2 = get<1, 1>(jac);\n\n  std::array<ReturnType, 3> dfac_dx{};\n  if constexpr (std::is_same_v<ReturnType, DataVector>) {\n    dfac_dx[0].set_data_ref(&get<2, 0>(jac));\n    dfac_dx[1].set_data_ref(&get<2, 1>(jac));\n    dfac_dx[2].set_data_ref(&get<2, 2>(jac));\n  }\n\n  stretch_factor_square(make_not_null(&fac), source_coords);\n  source_coords_sq = dot(source_coords, source_coords);\n  coords_dot_spin = dot(source_coords, spin_parameter_);\n  subexpr_1 = 4. * source_coords_sq * (1. - fac);\n  subexpr_2 = 2. * fac * coords_dot_spin;\n\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(dfac_dx, i) = subexpr_1 * gsl::at(source_coords, i) +\n                          2. * spin_mag_sq_ * gsl::at(source_coords, i) -\n                          subexpr_2 * gsl::at(spin_parameter_, i);\n  }\n  dfac_dx = dfac_dx / (square(source_coords_sq) + square(coords_dot_spin));\n\n  const ReturnType sqrt_fac = sqrt(fac);\n\n  // not mathematically a part of `dfac_dx` but can be absorbed to avoid\n  // allocation for temporary\n  dfac_dx = dfac_dx / (2. * sqrt_fac);\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      jac.get(i, j) = gsl::at(dfac_dx, j) * gsl::at(source_coords, i);\n    }\n    jac.get(i, i) += sqrt_fac;\n  }\n  return jac;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nKerrHorizonConforming::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  tnsr::Ij<ReturnType, 3, Frame::NoFrame> inv_jac(\n      get_size(dereference_wrapper(source_coords[0])));\n\n  ReturnType& mapped_mag_sq = get<0, 0>(inv_jac);\n  ReturnType& mapped_mag = get<0, 1>(inv_jac);\n  ReturnType& mapped_dot_spin = get<0, 2>(inv_jac);\n  ReturnType& mapped_sq_min_spin_sq = get<1, 0>(inv_jac);\n  ReturnType& r = get<1, 1>(inv_jac);\n  ReturnType& fac = get<1, 2>(inv_jac);\n  std::array<ReturnType, 3> dr_dx{};\n  if constexpr (std::is_same_v<ReturnType, DataVector>) {\n    dr_dx[0].set_data_ref(&get<2, 0>(inv_jac));\n    dr_dx[1].set_data_ref(&get<2, 1>(inv_jac));\n    dr_dx[2].set_data_ref(&get<2, 2>(inv_jac));\n  }\n\n  auto mapped = operator()(source_coords);\n\n  mapped_mag_sq = dot(mapped, mapped);\n  mapped_mag = sqrt(mapped_mag_sq);\n  mapped_dot_spin = dot(mapped, spin_parameter_);\n  mapped_sq_min_spin_sq = mapped_mag_sq - spin_mag_sq_;\n  r = sqrt(0.5 * (mapped_sq_min_spin_sq + sqrt(square(mapped_sq_min_spin_sq) +\n                                               4 * square(mapped_dot_spin))));\n  fac = 1. / (2. * cube(r) - mapped_sq_min_spin_sq * r);\n\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(dr_dx, i) = (square(r) * mapped.at(i) +\n                         mapped_dot_spin * gsl::at(spin_parameter_, i)) *\n                        fac;\n  }\n\n  // normalized from this point\n  mapped = mapped / mapped_mag;\n  const ReturnType r_by_mapped = r / mapped_mag;\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      inv_jac.get(i, j) = gsl::at(dr_dx, j) * gsl::at(mapped, i) -\n                          r_by_mapped * gsl::at(mapped, i) * gsl::at(mapped, j);\n    }\n    inv_jac.get(i, i) += r_by_mapped;\n  }\n  return inv_jac;\n}\n\ntemplate <typename T>\nvoid KerrHorizonConforming::stretch_factor_square(\n    const gsl::not_null<tt::remove_cvref_wrap_t<T>*> result,\n    const std::array<T, 3>& source_coords) const {\n  auto& source_coords_sq = *result;\n  source_coords_sq = dot(source_coords, source_coords);\n  *result = source_coords_sq * (source_coords_sq + spin_mag_sq_) /\n            (source_coords_sq * source_coords_sq +\n             square(dot(source_coords, spin_parameter_)));\n}\n\nvoid KerrHorizonConforming::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | spin_parameter_;\n    p | spin_mag_sq_;\n  }\n}\n\nbool operator==(const KerrHorizonConforming& lhs,\n                const KerrHorizonConforming& rhs) {\n  return lhs.spin_parameter_ == rhs.spin_parameter_;\n}\n\nbool operator!=(const KerrHorizonConforming& lhs,\n                const KerrHorizonConforming& rhs) {\n  return not(lhs == rhs);\n}\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  KerrHorizonConforming::operator()(                                         \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  KerrHorizonConforming::jacobian(                                           \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  KerrHorizonConforming::inv_jacobian(                                       \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/KerrHorizonConforming.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\brief Distorts cartesian coordinates \\f$x^i\\f$ such that a coordinate sphere\n * \\f$\\delta_{ij}x^ix^j=C^2\\f$ is mapped to an ellipsoid of constant\n * Kerr-Schild radius \\f$r=C\\f$.\n *\n * The Kerr-Schild radius \\f$r\\f$ is defined as the largest positive\n * root of\n *\n *  \\f{equation*}{\n *  r^4 - r^2 (x^2 - a^2) - (\\vec{a}\\cdot \\vec{x})^2 = 0.\n *  \\f}\n *\n * In this equation, \\f$\\vec{x}\\f$ are the coordinates of the (distorted)\n * surface, and \\f$\\vec{a}=\\vec{S}/M\\f$ is the spin-parameter of the black hole.\n * This is equivalent to the implicit definition of \\f$r\\f$ in Eq. (47) of\n * \\cite Lovelace2008tw. The black hole is assumed to be at the origin.\n *\n * \\details Given a spin vector \\f$\\vec{a}\\f$, we define the Kerr-Schild radius:\n *\n * \\f{equation}{r(\\vec{x}) = \\sqrt{\\frac{x^2 - a^2 + \\sqrt{(x^2 -\n * a^2)^2 + 4 (\\vec{x} \\cdot \\vec{a})^2}}{2}} \\f}\n *\n * We also define the auxiliary variable:\n *\n * \\f{equation}{s(\\vec{x}) = \\frac{x^2(x^2 + a^2)}{x^4 + (\\vec{x}\n * \\cdot \\vec{a})^2} \\f}\n *\n * The map is then given by:\n *\n * \\f{equation}{\\vec{x}(\\vec{\\xi}) = \\vec{\\xi} \\sqrt{s(\\vec{\\xi})} \\f}\n *\n * The inverse map is given by:\n * \\f{equation}{\\vec{\\xi}(\\vec{x}) = \\vec{x} \\frac{r(\\vec{x})}{|\\vec{x}|}\n * \\f}\n *\n * The jacobian is:\n * \\f{equation}{ \\frac{\\partial \\xi^i}{\\partial x^j} =\n * \\delta_{ij} \\sqrt{s(\\vec{\\xi})} +\n * \\frac{\\xi_j}{2 \\sqrt{s(\\vec{\\xi})}} \\frac{4\\xi^2(1 -\n * s(\\vec{\\xi})) \\xi_j + 2a^2\\xi_i + 2 s(\\vec{\\xi})\n * (\\vec{\\xi} \\cdot \\vec{a}) a_i}{\\xi^4 + (\\vec{\\xi} \\cdot\n * \\vec{a})^2} \\f}\n *\n * The inverse jacobian is:\n * \\f{equation}{\\frac{\\partial x^i}{\\partial \\xi^j} =\n * \\delta_{ij}\\frac{r(\\vec{x})}{|\\vec{x}|}\n * + \\frac{r(\\vec{x})^2 x_i + (\\vec{x} \\cdot \\vec{a})a_i}{2r(\\vec{x})^3\n * - r(\\vec{x}) (x^2 - a^2)}\\frac{x_j}{|\\vec{x}|} - \\frac{r(\\vec{x})\n * x_i x_j}{x^3} \\f}\n */\nclass KerrHorizonConforming {\n public:\n  KerrHorizonConforming() = default;\n  static constexpr size_t dim = 3;\n  /*!\n   * \\brief Constructs a Kerr horizon conforming map.\n   *\n   * \\param mass The Kerr mass parameter $M$\n   * \\param dimensionless_spin The dimensionless spin $\\vec{\\chi} = \\vec{a} / M\n   * = \\vec{S} / M^2$, where $M$ is the Kerr mass parameter, $\\vec{S}$ is the\n   * angular momentum or quasilocal spin, and $\\vec{a}$ is the Kerr spin\n   * parameter.\n   *\n   * \\note The horizon depends only on the dimensionful spin parameter $\\vec{a}\n   * = M \\vec{\\chi}$. This constructor takes $M$ and $\\vec{\\chi}$ separately for\n   * consistency with other code such as gr::Solutions::KerrSchild, and hence to\n   * avoid bugs where the wrong spin quantity is used accidentally.\n   */\n  explicit KerrHorizonConforming(const double mass,\n                                 std::array<double, 3> dimensionless_spin);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  bool is_identity() const {\n    return spin_parameter_ == std::array<double, 3>{0., 0., 0.};\n  }\n\n  static constexpr bool supports_hessian{false};\n\n  friend bool operator==(const KerrHorizonConforming& lhs,\n                         const KerrHorizonConforming& rhs);\n\n  void pup(PUP::er& p);\n\n private:\n  template <typename T>\n  void stretch_factor_square(\n      const gsl::not_null<tt::remove_cvref_wrap_t<T>*> result,\n      const std::array<T, 3>& source_coords) const;\n\n  std::array<double, 3> spin_parameter_;\n  double spin_mag_sq_;\n};\nbool operator!=(const KerrHorizonConforming& lhs,\n                const KerrHorizonConforming& rhs);\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/MapInstantiationMacros.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/punctuation/comma_if.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/elem.hpp>\n#include <boost/preprocessor/tuple/enum.hpp>\n#include <boost/preprocessor/tuple/size.hpp>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS(data) \\\n  BOOST_PP_TUPLE_ENUM(BOOST_PP_TUPLE_ELEM(0, data))\n#define INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data)                     \\\n  BOOST_PP_TUPLE_ELEM(BOOST_PP_TUPLE_SIZE(BOOST_PP_TUPLE_ELEM(0, data)), 0, \\\n                      BOOST_PP_TUPLE_ELEM(0, data))::dim\n#define INSTANTIATE_COORD_MAP_DETAILINSERT_SIZE(z, n, _) BOOST_PP_COMMA_IF(n) n\n\n#define INSTANTIATE_COORD_MAP_DETAIL_GET_FILLED_INDEX_SEQUENCE(data) \\\n  BOOST_PP_REPEAT(BOOST_PP_TUPLE_SIZE(BOOST_PP_TUPLE_ELEM(0, data)), \\\n                  INSTANTIATE_COORD_MAP_DETAILINSERT_SIZE, _)\n\n#define INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data) \\\n  BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data) \\\n  BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Generate instantiations of member functions of the `CoordinateMap`\n * class template.\n *\n * Called as follows:\n *\n * \\code\n *  using Affine2d =\n *      domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine>;\n *  using Affine3d =\n *      domain::CoordinateMaps::ProductOf3Maps<domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine>;\n *\n *  GENERATE_INSTANTIATIONS(INSTANTIATE_MAPS_SIMPLE_FUNCTIONS,\n *                          ((Affine2d), (Affine3d)), (Frame::BlockLogical),\n *                          (Frame::Grid, Frame::Inertial))\n * \\endcode\n *\n * The first tuple passed to `GENERATE_INSTANTIATIONS` has a bunch of tuples in\n * it that is the list of maps being composed. The reason for defining the type\n * aliases `Affine2d` and `Affine3d` is that otherwise the number of maps being\n * composed is calculated incorrectly. The second tuple contains the source\n * frames for the map. The third tuple passed to `GENERATE_INSTANTIATIONS`\n * contains the target frames to instantiate for, typically `Frame::Grid` and\n * `Frame::Inertial`.\n *\n * Instantiates:\n * - `get_to_grid_frame_impl`\n * - `inverse_impl`\n * - `class CoordinateMap`\n */\n#define INSTANTIATE_MAPS_SIMPLE_FUNCTIONS(_, data)                           \\\n  template std::unique_ptr<domain::CoordinateMapBase<                        \\\n      INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data), Frame::Grid,      \\\n      INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data)>>                      \\\n      domain::CoordinateMap<                                                 \\\n          INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data),               \\\n          INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data),               \\\n          INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS(data)>::                     \\\n          get_to_grid_frame_impl(                                            \\\n              std::integer_sequence<                                         \\\n                  unsigned long,                                             \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_FILLED_INDEX_SEQUENCE(    \\\n                      data)>) const;                                         \\\n  template std::optional<                                                    \\\n      tnsr::I<double, INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),       \\\n              INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data)>>          \\\n  domain::CoordinateMap<INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS(data)>::       \\\n      inverse_impl(                                                          \\\n          tnsr::I<double, INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),   \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data)>&&,    \\\n          double,                                                            \\\n          const std::unordered_map<                                          \\\n              std::string,                                                   \\\n              std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&,    \\\n          std::index_sequence<                                               \\\n              INSTANTIATE_COORD_MAP_DETAIL_GET_FILLED_INDEX_SEQUENCE(        \\\n                  data)> /*meta*/) const;                                    \\\n  template class domain::CoordinateMap<                                      \\\n      INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data),                   \\\n      INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data),                   \\\n      INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS(data)>;\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Generate instantiations of member functions of the `CoordinateMap`\n * class template.\n *\n * Called as follows:\n *\n * \\code\n *  using Affine2d =\n *      domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine>;\n *  using Affine3d =\n *      domain::CoordinateMaps::ProductOf3Maps<domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine>;\n *\n *  GENERATE_INSTANTIATIONS(INSTANTIATE_MAPS_DATA_TYPE_FUNCTIONS,\n *                          ((Affine2d), (Affine3d)), (Frame::BlockLogical),\n *                          (Frame::Grid, Frame::Inertial),\n *                          (double, DataVector))\n * \\endcode\n *\n * The first tuple passed to `GENERATE_INSTANTIATIONS` has a bunch of tuples in\n * it that is the list of maps being composed. The reason for defining the type\n * aliases `Affine2d` and `Affine3d` is that otherwise the number of maps being\n * composed is calculated incorrectly. The second tuple contains the source\n * frames for the map. The third tuple passed to  `GENERATE_INSTANTIATIONS`\n * contains the target frames to instantiate for, typically `Frame::Grid` and\n * `Frame::Inertial`. The last tuple is the data types for which to instantiate\n * the functions, usually `double` and `DataVector`.\n *\n * Instantiates:\n * - `call_impl`\n * - `inv_jacobian_impl`\n * - `jacobian_impl`\n * - `coords_frame_velocity_jacobians_impl`\n */\n#define INSTANTIATE_MAPS_DATA_TYPE_FUNCTIONS(_, data)                        \\\n  template tnsr::I<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                 \\\n                   INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),          \\\n                   INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data)>      \\\n  domain::CoordinateMap<INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS(data)>::       \\\n      call_impl(                                                             \\\n          tnsr::I<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                  \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),           \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data)>&&,    \\\n          double,                                                            \\\n          const std::unordered_map<                                          \\\n              std::string,                                                   \\\n              std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&,    \\\n          std::integer_sequence<                                             \\\n              unsigned long,                                                 \\\n              INSTANTIATE_COORD_MAP_DETAIL_GET_FILLED_INDEX_SEQUENCE(data)>) \\\n          const;                                                             \\\n  template InverseJacobian<                                                  \\\n      INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                              \\\n      INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),                       \\\n      INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data),                   \\\n      INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data)>                   \\\n  domain::CoordinateMap<INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS(data)>::       \\\n      inv_jacobian_impl(                                                     \\\n          tnsr::I<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                  \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),           \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data)>&&,    \\\n          double,                                                            \\\n          const std::unordered_map<                                          \\\n              std::string,                                                   \\\n              std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&)    \\\n          const;                                                             \\\n  template Jacobian<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                \\\n                    INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),         \\\n                    INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data),     \\\n                    INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data)>     \\\n  domain::CoordinateMap<INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS(data)>::       \\\n      jacobian_impl(                                                         \\\n          tnsr::I<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                  \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),           \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data)>&&,    \\\n          double,                                                            \\\n          const std::unordered_map<                                          \\\n              std::string,                                                   \\\n              std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&)    \\\n          const;                                                             \\\n  template std::tuple<                                                       \\\n      tnsr::I<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                      \\\n              INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),               \\\n              INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data)>,          \\\n      InverseJacobian<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),              \\\n                      INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),       \\\n                      INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data),   \\\n                      INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data)>,  \\\n      Jacobian<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                     \\\n               INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),              \\\n               INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data),          \\\n               INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data)>,         \\\n      tnsr::I<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                      \\\n              INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),               \\\n              INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data)>>          \\\n  domain::CoordinateMap<INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_TARGET_FRAME(data), \\\n                        INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS(data)>::       \\\n      coords_frame_velocity_jacobians_impl(                                  \\\n          tnsr::I<INSTANTIATE_COORD_MAP_DETAIL_DTYPE(data),                  \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_MAPS_DIM(data),           \\\n                  INSTANTIATE_COORD_MAP_DETAIL_GET_SOURCE_FRAME(data)>,      \\\n          double,                                                            \\\n          const std::unordered_map<                                          \\\n              std::string,                                                   \\\n              std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&)    \\\n          const;\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Generate instantiations of member functions of the `CoordinateMap`\n * class template.\n *\n * Called as follows:\n *\n * \\code\n *  using Affine2d =\n *      domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine>;\n *  using Affine3d =\n *      domain::CoordinateMaps::ProductOf3Maps<domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine,\n *                                             domain::CoordinateMaps::Affine>;\n *\n *  INSTANTIATE_MAPS_FUNCTIONS(((Affine2d), (Affine3d)), (Frame::BlockLogical),\n *                             (Frame::Grid, Frame::Inertial),\n *                             (double, DataVector))\n * \\endcode\n *\n * The first tuple passed to `GENERATE_INSTANTIATIONS` has a bunch of tuples in\n * it that is the list of maps being composed. The reason for defining the type\n * aliases `Affine2d` and `Affine3d` is that otherwise the number of maps being\n * composed is calculated incorrectly. The second tuple contains the source\n * frames for the map. The third tuple passed to `GENERATE_INSTANTIATIONS`\n * contains the frames to instantiate for, typically `Frame::Grid` and\n * `Frame::Inertial`.\n *\n * Instantiates:\n * - `get_to_grid_frame_impl`\n * - `inverse_impl`\n * - `class CoordinateMap`\n * - `call_impl`\n * - `inv_jacobian_impl`\n * - `jacobian_impl`\n * - `coords_frame_velocity_jacobians_impl`\n */\n#define INSTANTIATE_MAPS_FUNCTIONS(MAPS_TUPLE, SOURCE_FRAME,                \\\n                                   TARGET_FRAMES_TUPLE, TYPES_TUPLE)        \\\n  GENERATE_INSTANTIATIONS(INSTANTIATE_MAPS_SIMPLE_FUNCTIONS, MAPS_TUPLE,    \\\n                          SOURCE_FRAME, TARGET_FRAMES_TUPLE)                \\\n  GENERATE_INSTANTIATIONS(INSTANTIATE_MAPS_DATA_TYPE_FUNCTIONS, MAPS_TUPLE, \\\n                          SOURCE_FRAME, TARGET_FRAMES_TUPLE, TYPES_TUPLE)\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/PolarToCartesian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/PolarToCartesian.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\nPolarToCartesian::PolarToCartesian() = default;\nPolarToCartesian::PolarToCartesian(PolarToCartesian&&) = default;\nPolarToCartesian::PolarToCartesian(const PolarToCartesian&) = default;\nPolarToCartesian& PolarToCartesian::operator=(const PolarToCartesian&) =\n    default;\nPolarToCartesian& PolarToCartesian::operator=(PolarToCartesian&&) = default;\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 2> PolarToCartesian::operator()(\n    const std::array<T, 2>& source_coords) const {\n  const auto& [r, phi] = source_coords;\n  return {{r * cos(phi), r * sin(phi)}};\n}\n\n// NOLINTNEXTLINE(readability-convert-member-functions-to-static)\nstd::optional<std::array<double, 2>> PolarToCartesian::inverse(\n    const std::array<double, 2>& target_coords) const {\n  const auto& [x, y] = target_coords;\n  if (UNLIKELY(y == 0.0 and x == 0.0)) {\n    return std::array{0.0, 0.0};\n  } else {\n    const double r = std::hypot(x, y);\n    const double phi = atan2(y, x);\n    return std::array{r, phi < 0.0 ? phi + 2.0 * M_PI : phi};\n  }\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame>\nPolarToCartesian::jacobian(const std::array<T, 2>& source_coords) const {\n  const auto& [r, phi] = source_coords;\n  using DataType = tt::remove_cvref_wrap_t<T>;\n  tnsr::Ij<DataType, 2, Frame::NoFrame> jacobian_matrix{\n      make_with_value<DataType>(dereference_wrapper(r), 0.0)};\n  const auto& cos_phi = get<0, 0>(jacobian_matrix) = cos(phi);\n  const auto& sin_phi = get<1, 0>(jacobian_matrix) = sin(phi);\n  get<0, 1>(jacobian_matrix) = -r * sin_phi;\n  get<1, 1>(jacobian_matrix) = r * cos_phi;\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame>\nPolarToCartesian::inv_jacobian(const std::array<T, 2>& source_coords) const {\n  const auto& [r, phi] = source_coords;\n  using DataType = tt::remove_cvref_wrap_t<T>;\n  tnsr::Ij<DataType, 2, Frame::NoFrame> inv_jacobian_matrix{\n      make_with_value<DataType>(dereference_wrapper(r), 0.0)};\n  const auto& cos_phi = get<0, 0>(inv_jacobian_matrix) = cos(phi);\n  const auto& sin_phi = get<0, 1>(inv_jacobian_matrix) = sin(phi);\n  const auto& one_over_r = get<1, 1>(inv_jacobian_matrix) = 1.0 / r;\n  get<1, 0>(inv_jacobian_matrix) = -one_over_r * sin_phi;\n  get<1, 1>(inv_jacobian_matrix) *= cos_phi;\n  return inv_jacobian_matrix;\n}\n\nvoid PolarToCartesian::pup(PUP::er& /*p*/) {}\n\nbool operator==(const PolarToCartesian& /*lhs*/,\n                const PolarToCartesian& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const PolarToCartesian& lhs, const PolarToCartesian& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE_DTYPE(_, data)                                            \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 2>                \\\n  PolarToCartesian::operator()(                                               \\\n      const std::array<DTYPE(data), 2>& source_coords) const;                 \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 2, Frame::NoFrame>  \\\n  PolarToCartesian::jacobian(const std::array<DTYPE(data), 2>& source_coords) \\\n      const;                                                                  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 2, Frame::NoFrame>  \\\n  PolarToCartesian::inv_jacobian(                                             \\\n      const std::array<DTYPE(data), 2>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_DTYPE,\n                        (double, DataVector,\n                         std::reference_wrapper<const double>,\n                         std::reference_wrapper<const DataVector>))\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/PolarToCartesian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Transformation from polar to Cartesian coordinates.\n *\n * \\details This is a mapping from \\f$(r,\\phi) \\rightarrow (x,y) \\f$.\n *\n * The formula for the mapping is...\n * \\f{eqnarray*}\n *     x &=& r \\cos\\phi \\\\\n *     y &=& r \\sin\\phi\n * \\f}\n */\nclass PolarToCartesian {\n public:\n  static constexpr size_t dim = 2;\n  PolarToCartesian();\n  ~PolarToCartesian() = default;\n  PolarToCartesian(PolarToCartesian&&);\n  PolarToCartesian(const PolarToCartesian&);\n  PolarToCartesian& operator=(const PolarToCartesian&);\n  PolarToCartesian& operator=(PolarToCartesian&&);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 2> operator()(\n      const std::array<T, 2>& source_coords) const;\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  std::optional<std::array<double, 2>> inverse(\n      const std::array<double, 2>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame> jacobian(\n      const std::array<T, 2>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 2>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static constexpr bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n};\n\nbool operator==(const PolarToCartesian& lhs, const PolarToCartesian& rhs);\n\nbool operator!=(const PolarToCartesian& lhs, const PolarToCartesian& rhs);\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/ProductMaps.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class templates ProductOf2Maps and ProductOf3Maps.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n/// \\ingroup CoordinateMapsGroup\n/// \\brief Product of two codimension=0 CoordinateMaps.\n///\n/// \\tparam Map1 the map for the first coordinate(s)\n/// \\tparam Map2 the map for the second coordinate(s)\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps {\n public:\n  static constexpr size_t dim = Map1::dim + Map2::dim;\n  using map_list = tmpl::list<Map1, Map2>;\n  static_assert(dim == 2 or dim == 3,\n                \"Only 2D and 3D maps are supported by ProductOf2Maps\");\n\n  // Needed for Charm++ serialization\n  ProductOf2Maps() = default;\n\n  ProductOf2Maps(Map1 map1, Map2 map2);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, dim> operator()(\n      const std::array<T, dim>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, dim>> inverse(\n      const std::array<double, dim>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, dim>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, dim, Frame::NoFrame> jacobian(\n      const std::array<T, dim>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{Map1::supports_hessian and\n                                          Map2::supports_hessian};\n\n private:\n  friend bool operator==(const ProductOf2Maps& lhs, const ProductOf2Maps& rhs) {\n    return lhs.map1_ == rhs.map1_ and lhs.map2_ == rhs.map2_ and\n           lhs.is_identity_ == rhs.is_identity_;\n  }\n\n  Map1 map1_;\n  Map2 map2_;\n  bool is_identity_ = false;\n};\n\ntemplate <typename Map1, typename Map2>\nbool operator!=(const ProductOf2Maps<Map1, Map2>& lhs,\n                const ProductOf2Maps<Map1, Map2>& rhs);\n\n/// \\ingroup CoordinateMapsGroup\n/// \\brief Product of three one-dimensional CoordinateMaps.\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps {\n public:\n  static constexpr size_t dim = Map1::dim + Map2::dim + Map3::dim;\n  using map_list = tmpl::list<Map1, Map2, Map3>;\n  static_assert(dim == 3, \"Only 3D maps are implemented for ProductOf3Maps\");\n\n  // Needed for Charm++ serialization\n  ProductOf3Maps() = default;\n\n  ProductOf3Maps(Map1 map1, Map2 map2, Map3 map3);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, dim> operator()(\n      const std::array<T, dim>& source_coords) const;\n\n  std::optional<std::array<double, dim>> inverse(\n      const std::array<double, dim>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, dim>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, dim, Frame::NoFrame> jacobian(\n      const std::array<T, dim>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{Map1::supports_hessian and\n                                          Map2::supports_hessian and\n                                          Map3::supports_hessian};\n\n private:\n  friend bool operator==(const ProductOf3Maps& lhs, const ProductOf3Maps& rhs) {\n    return lhs.map1_ == rhs.map1_ and lhs.map2_ == rhs.map2_ and\n           lhs.map3_ == rhs.map3_ and lhs.is_identity_ == rhs.is_identity_;\n  }\n\n  Map1 map1_;\n  Map2 map2_;\n  Map3 map3_;\n  bool is_identity_ = false;\n};\n\ntemplate <typename Map1, typename Map2, typename Map3>\nbool operator!=(const ProductOf3Maps<Map1, Map2, Map3>& lhs,\n                const ProductOf3Maps<Map1, Map2, Map3>& rhs);\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/ProductMaps.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain {\nnamespace CoordinateMaps {\n\nnamespace product_detail {\ntemplate <typename T, size_t Size, typename Map1, typename Map2,\n          typename Function, size_t... Is, size_t... Js>\nstd::array<tt::remove_cvref_wrap_t<T>, Size> apply_call(\n    const std::array<T, Size>& coords, const Map1& map1, const Map2& map2,\n    const Function func, std::integer_sequence<size_t, Is...> /*meta*/,\n    std::integer_sequence<size_t, Js...> /*meta*/) {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  return {\n      {func(\n           std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Is)>{\n               {coords[Is]...}},\n           map1)[Is]...,\n       func(\n           std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Js)>{\n               {coords[Map1::dim + Js]...}},\n           map2)[Js]...}};\n}\n\ntemplate <size_t Size, typename Map1, typename Map2, typename Function,\n          size_t... Is, size_t... Js>\nstd::optional<std::array<double, Size>> apply_inverse(\n    const std::array<double, Size>& coords, const Map1& map1, const Map2& map2,\n    const Function func, std::integer_sequence<size_t, Is...> /*meta*/,\n    std::integer_sequence<size_t, Js...> /*meta*/) {\n  auto map1_func =\n      func(std::array<double, sizeof...(Is)>{{coords[Is]...}}, map1);\n  auto map2_func = func(\n      std::array<double, sizeof...(Js)>{{coords[Map1::dim + Js]...}}, map2);\n  if (map1_func.has_value() and map2_func.has_value()) {\n    return {{{map1_func.value()[Is]..., map2_func.value()[Js]...}}};\n  } else {\n    return std::nullopt;\n  }\n}\n\ntemplate <typename T, size_t Size, typename Map1, typename Map2,\n          typename Function, size_t... Is, size_t... Js>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Size, Frame::NoFrame> apply_jac(\n    const std::array<T, Size>& source_coords, const Map1& map1,\n    const Map2& map2, const Function func,\n    std::integer_sequence<size_t, Is...> /*meta*/,\n    std::integer_sequence<size_t, Js...> /*meta*/) {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  auto map1_jac = func(\n      std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Is)>{\n          {source_coords[Is]...}},\n      map1);\n  auto map2_jac = func(\n      std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Js)>{\n          {source_coords[Map1::dim + Js]...}},\n      map2);\n  tnsr::Ij<UnwrappedT, Size, Frame::NoFrame> jac{\n      make_with_value<UnwrappedT>(dereference_wrapper(source_coords[0]), 0.0)};\n  for (size_t i = 0; i < Map1::dim; ++i) {\n    for (size_t j = 0; j < Map1::dim; ++j) {\n      jac.get(i, j) = std::move(map1_jac.get(i, j));\n    }\n  }\n  for (size_t i = 0; i < Map2::dim; ++i) {\n    for (size_t j = 0; j < Map2::dim; ++j) {\n      jac.get(Map1::dim + i, Map1::dim + j) = std::move(map2_jac.get(i, j));\n    }\n  }\n  return jac;\n}\n}  // namespace product_detail\n\ntemplate <typename Map1, typename Map2>\nProductOf2Maps<Map1, Map2>::ProductOf2Maps(Map1 map1, Map2 map2)\n    : map1_(std::move(map1)),\n      map2_(std::move(map2)),\n      is_identity_(map1_.is_identity() and map2_.is_identity()) {}\n\ntemplate <typename Map1, typename Map2>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, ProductOf2Maps<Map1, Map2>::dim>\nProductOf2Maps<Map1, Map2>::operator()(\n    const std::array<T, dim>& source_coords) const {\n  return product_detail::apply_call(\n      source_coords, map1_, map2_,\n      [](const auto& point, const auto& map) { return map(point); },\n      std::make_index_sequence<Map1::dim>{},\n      std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\nstd::optional<std::array<double, ProductOf2Maps<Map1, Map2>::dim>>\nProductOf2Maps<Map1, Map2>::inverse(\n    const std::array<double, dim>& target_coords) const {\n  return product_detail::apply_inverse(\n      target_coords, map1_, map2_,\n      [](const auto& point, const auto& map) {\n        return map.inverse(point);\n      },\n      std::make_index_sequence<Map1::dim>{},\n      std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, ProductOf2Maps<Map1, Map2>::dim,\n         Frame::NoFrame>\nProductOf2Maps<Map1, Map2>::inv_jacobian(\n    const std::array<T, dim>& source_coords) const {\n  return product_detail::apply_jac(\n      source_coords, map1_, map2_,\n      [](const auto& point, const auto& map) {\n        return map.inv_jacobian(point);\n      },\n      std::make_index_sequence<Map1::dim>{},\n      std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, ProductOf2Maps<Map1, Map2>::dim,\n         Frame::NoFrame>\nProductOf2Maps<Map1, Map2>::jacobian(\n    const std::array<T, dim>& source_coords) const {\n  return product_detail::apply_jac(\n      source_coords, map1_, map2_,\n      [](const auto& point, const auto& map) {\n        return map.jacobian(point);\n      },\n      std::make_index_sequence<Map1::dim>{},\n      std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\nvoid ProductOf2Maps<Map1, Map2>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | map1_;\n    p | map2_;\n    p | is_identity_;\n  }\n}\n\ntemplate <typename Map1, typename Map2>\nbool operator!=(const ProductOf2Maps<Map1, Map2>& lhs,\n                const ProductOf2Maps<Map1, Map2>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\nProductOf3Maps<Map1, Map2, Map3>::ProductOf3Maps(Map1 map1, Map2 map2,\n                                                 Map3 map3)\n    : map1_(std::move(map1)),\n      map2_(std::move(map2)),\n      map3_(std::move(map3)),\n      is_identity_(map1_.is_identity() and map2_.is_identity() and\n                   map3_.is_identity()) {}\n\ntemplate <typename Map1, typename Map2, typename Map3>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, ProductOf3Maps<Map1, Map2, Map3>::dim>\nProductOf3Maps<Map1, Map2, Map3>::operator()(\n    const std::array<T, dim>& source_coords) const {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  return {{map1_(std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[0]}})[0],\n           map2_(std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[1]}})[0],\n           map3_(std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[2]}})[0]}};\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\nstd::optional<std::array<double, ProductOf3Maps<Map1, Map2, Map3>::dim>>\nProductOf3Maps<Map1, Map2, Map3>::inverse(\n    const std::array<double, dim>& target_coords) const {\n  auto c1 = map1_.inverse(std::array<double, 1>{{target_coords[0]}});\n  auto c2 = map2_.inverse(std::array<double, 1>{{target_coords[1]}});\n  auto c3 = map3_.inverse(std::array<double, 1>{{target_coords[2]}});\n  if (c1.has_value() and c2.has_value() and c3.has_value()) {\n    return {{{c1.value()[0], c2.value()[0], c3.value()[0]}}};\n  } else {\n    return std::nullopt;\n  }\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, ProductOf3Maps<Map1, Map2, Map3>::dim,\n         Frame::NoFrame>\nProductOf3Maps<Map1, Map2, Map3>::inv_jacobian(\n    const std::array<T, dim>& source_coords) const {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  tnsr::Ij<UnwrappedT, dim, Frame::NoFrame> inv_jacobian_matrix{\n      make_with_value<UnwrappedT>(dereference_wrapper(source_coords[0]), 0.0)};\n  get<0, 0>(inv_jacobian_matrix) = get<0, 0>(map1_.inv_jacobian(\n      std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[0]}}));\n  get<1, 1>(inv_jacobian_matrix) = get<0, 0>(map2_.inv_jacobian(\n      std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[1]}}));\n  get<2, 2>(inv_jacobian_matrix) = get<0, 0>(map3_.inv_jacobian(\n      std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[2]}}));\n  return inv_jacobian_matrix;\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, ProductOf3Maps<Map1, Map2, Map3>::dim,\n         Frame::NoFrame>\nProductOf3Maps<Map1, Map2, Map3>::jacobian(\n    const std::array<T, dim>& source_coords) const {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  tnsr::Ij<UnwrappedT, dim, Frame::NoFrame> jacobian_matrix{\n      make_with_value<UnwrappedT>(dereference_wrapper(source_coords[0]), 0.0)};\n  get<0, 0>(jacobian_matrix) = get<0, 0>(\n      map1_.jacobian(std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[0]}}));\n  get<1, 1>(jacobian_matrix) = get<0, 0>(\n      map2_.jacobian(std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[1]}}));\n  get<2, 2>(jacobian_matrix) = get<0, 0>(\n      map3_.jacobian(std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[2]}}));\n  return jacobian_matrix;\n}\ntemplate <typename Map1, typename Map2, typename Map3>\nvoid ProductOf3Maps<Map1, Map2, Map3>::pup(PUP::er& p) {\n  p | map1_;\n  p | map2_;\n  p | map3_;\n  p | is_identity_;\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\nbool operator!=(const ProductOf3Maps<Map1, Map2, Map3>& lhs,\n                const ProductOf3Maps<Map1, Map2, Map3>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Python/Composition.hpp\"\n#include \"Domain/CoordinateMaps/Python/CoordinateMap.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain {\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py::enum_<CoordinateMaps::Distribution>(m, \"Distribution\")\n      .value(\"Linear\", CoordinateMaps::Distribution::Linear)\n      .value(\"Equiangular\", CoordinateMaps::Distribution::Equiangular)\n      .value(\"Logarithmic\", CoordinateMaps::Distribution::Logarithmic)\n      .value(\"Inverse\", CoordinateMaps::Distribution::Inverse)\n      .value(\"Projective\", CoordinateMaps::Distribution::Projective);\n  py::class_<CoordinateMaps::DistributionAndSingularityPosition>(\n      m, \"DistributionAndSingularityPosition\")\n      .def(py::init<CoordinateMaps::Distribution, std::optional<double>>(),\n           py::arg(\"distribution\"),\n           py::arg(\"singularity_position\") = std::nullopt);\n  // Order is important: The base class `CoordinateMap` needs to have its\n  // bindings set up before the derived classes\n  py_bindings::bind_coordinate_map(m);\n  py_bindings::bind_composition(m);\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyCoordinateMaps\")\n\nspectre_python_add_module(\n  CoordinateMaps\n  LIBRARY_NAME ${LIBRARY}\n  MODULE_PATH \"Domain\"\n  SOURCES\n  Bindings.cpp\n  Composition.cpp\n  CoordinateMap.cpp\n  PYTHON_FILES\n  __init__.py\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Composition.hpp\n  CoordinateMap.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  CoordinateMaps\n  pybind11::module\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Python/Composition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Python/Composition.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/SnakeCase.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\n\ntemplate <typename Frames, size_t Dim>\nvoid bind_composition_impl(py::module& m) {  // NOLINT\n  using CompositionType = CoordinateMaps::Composition<Frames, Dim>;\n  std::string frames_string{};\n  tmpl::for_each<Frames>(\n      [&frames_string]<typename Frame>(const tmpl::type_<Frame> /*meta*/) {\n        frames_string += get_output(Frame{});\n      });\n  auto binding = py::class_<CompositionType, typename CompositionType::Base>(\n      m, (\"CompositionMap\" + frames_string + get_output(Dim) + \"D\").c_str());\n  tmpl::for_each<\n      tmpl::pop_back<Frames>>([&binding]<typename SourceFrame>(\n                                  const tmpl::type_<SourceFrame> /*meta*/) {\n    using TargetFrame =\n        tmpl::at<Frames,\n                 tmpl::size_t<tmpl::index_of<Frames, SourceFrame>::value + 1>>;\n    binding.def_property_readonly(\n        (camel_case_to_snake_case(get_output(SourceFrame{})) + \"_to_\" +\n         camel_case_to_snake_case(get_output(TargetFrame{})))\n            .c_str(),\n        &CompositionType::template get_component<SourceFrame, TargetFrame>,\n        py::return_value_policy::reference_internal);\n  });\n}\n}  // namespace\n\nvoid bind_composition(py::module& m) {  // NOLINT\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  bind_composition_impl<                                                       \\\n      tmpl::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Grid,      \\\n                 Frame::Distorted, Frame::Inertial>,                           \\\n      DIM(data)>(m);                                                           \\\n  bind_composition_impl<tmpl::list<Frame::ElementLogical, Frame::BlockLogical, \\\n                                   Frame::Grid, Frame::Inertial>,              \\\n                        DIM(data)>(m);                                         \\\n  bind_composition_impl<                                                       \\\n      tmpl::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Inertial>, \\\n      DIM(data)>(m);\n\n  GENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Python/Composition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_composition(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Python/CoordinateMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Python/CoordinateMap.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\nusing FuncOfTimeMap =\n    std::unordered_map<std::string,\n                       const domain::FunctionsOfTime::FunctionOfTime&>;\n\n// Transform functions-of-time map to unique_ptrs because pybind11\n// can't handle them easily as function arguments (it's hard to\n// transfer ownership of a Python object to C++)\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\ntransform_functions_of_time(\n    const std::optional<FuncOfTimeMap>& functions_of_time) {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time_ptrs{};\n  if (functions_of_time.has_value()) {\n    for (const auto& [name, fot] : *functions_of_time) {\n      functions_of_time_ptrs[name] = fot.get_clone();\n    }\n  }\n  return functions_of_time_ptrs;\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim>\nvoid bind_coordinate_map_impl(py::module& m) {  // NOLINT\n  using CoordMapType = CoordinateMapBase<SourceFrame, TargetFrame, Dim>;\n  py::class_<CoordMapType>(m,\n                           (\"CoordinateMap\" + get_output(SourceFrame{}) + \"To\" +\n                            get_output(TargetFrame{}) + get_output(Dim) + \"D\")\n                               .c_str())\n      .def(\"is_identity\", &CoordMapType::is_identity)\n      .def(\"inv_jacobian_is_time_dependent\",\n           &CoordMapType::inv_jacobian_is_time_dependent)\n      .def(\"jacobian_is_time_dependent\",\n           &CoordMapType::jacobian_is_time_dependent)\n      .def(\n          \"__call__\",\n          [](const CoordMapType& map,\n             const tnsr::I<DataVector, Dim, SourceFrame>& source_point,\n             const std::optional<double> time,\n             const std::optional<FuncOfTimeMap>& functions_of_time) {\n            return map(\n                source_point,\n                time.value_or(std::numeric_limits<double>::signaling_NaN()),\n                transform_functions_of_time(functions_of_time));\n          },\n          py::arg(\"source_point\"), py::arg(\"time\") = std::nullopt,\n          py::arg(\"functions_of_time\") = std::nullopt)\n      .def(\n          \"inverse\",\n          [](const CoordMapType& map,\n             const tnsr::I<double, Dim, TargetFrame>& source_point,\n             const std::optional<double> time,\n             const std::optional<FuncOfTimeMap>& functions_of_time) {\n            return map.inverse(\n                source_point,\n                time.value_or(std::numeric_limits<double>::signaling_NaN()),\n                transform_functions_of_time(functions_of_time));\n          },\n          py::arg(\"source_point\"), py::arg(\"time\") = std::nullopt,\n          py::arg(\"functions_of_time\") = std::nullopt)\n      .def(\n          \"inv_jacobian\",\n          [](const CoordMapType& map,\n             const tnsr::I<DataVector, Dim, SourceFrame>& source_point,\n             const std::optional<double> time,\n             const std::optional<FuncOfTimeMap>& functions_of_time) {\n            return map.inv_jacobian(\n                source_point,\n                time.value_or(std::numeric_limits<double>::signaling_NaN()),\n                transform_functions_of_time(functions_of_time));\n          },\n          py::arg(\"source_point\"), py::arg(\"time\") = std::nullopt,\n          py::arg(\"functions_of_time\") = std::nullopt)\n      .def(\n          \"jacobian\",\n          [](const CoordMapType& map,\n             const tnsr::I<DataVector, Dim, SourceFrame>& source_point,\n             const std::optional<double> time,\n             const std::optional<FuncOfTimeMap>& functions_of_time) {\n            return map.jacobian(\n                source_point,\n                time.value_or(std::numeric_limits<double>::signaling_NaN()),\n                transform_functions_of_time(functions_of_time));\n          },\n          py::arg(\"source_point\"), py::arg(\"time\") = std::nullopt,\n          py::arg(\"functions_of_time\") = std::nullopt);\n}\n}  // namespace\n\nvoid bind_coordinate_map(py::module& m) {  // NOLINT\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  bind_coordinate_map_impl<Frame::ElementLogical, Frame::BlockLogical,         \\\n                           DIM(data)>(m);                                      \\\n  bind_coordinate_map_impl<Frame::ElementLogical, Frame::Inertial, DIM(data)>( \\\n      m);                                                                      \\\n  bind_coordinate_map_impl<Frame::BlockLogical, Frame::Grid, DIM(data)>(m);    \\\n  bind_coordinate_map_impl<Frame::BlockLogical, Frame::Inertial, DIM(data)>(   \\\n      m);                                                                      \\\n  bind_coordinate_map_impl<Frame::Grid, Frame::Distorted, DIM(data)>(m);       \\\n  bind_coordinate_map_impl<Frame::Grid, Frame::Inertial, DIM(data)>(m);        \\\n  bind_coordinate_map_impl<Frame::Distorted, Frame::Inertial, DIM(data)>(m);\n\n  GENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Python/CoordinateMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_coordinate_map(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Rotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nRotation<2>::Rotation(const double rotation_angle)\n    : rotation_angle_(rotation_angle),\n      rotation_matrix_(std::numeric_limits<double>::signaling_NaN()),\n      is_identity_(rotation_angle_ == 0.0) {\n  const double cos_alpha = cos(rotation_angle_);\n  const double sin_alpha = sin(rotation_angle_);\n  get<0, 0>(rotation_matrix_) = cos_alpha;\n  get<0, 1>(rotation_matrix_) = -sin_alpha;\n  get<1, 0>(rotation_matrix_) = sin_alpha;\n  get<1, 1>(rotation_matrix_) = cos_alpha;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 2> Rotation<2>::operator()(\n    const std::array<T, 2>& source_coords) const {\n  return {{source_coords[0] * get<0, 0>(rotation_matrix_) +\n               source_coords[1] * get<0, 1>(rotation_matrix_),\n           source_coords[0] * get<1, 0>(rotation_matrix_) +\n               source_coords[1] * get<1, 1>(rotation_matrix_)}};\n}\n\nstd::optional<std::array<double, 2>> Rotation<2>::inverse(\n    const std::array<double, 2>& target_coords) const {\n  return {{{target_coords[0] * get<0, 0>(rotation_matrix_) +\n                target_coords[1] * get<1, 0>(rotation_matrix_),\n            target_coords[0] * get<0, 1>(rotation_matrix_) +\n                target_coords[1] * get<1, 1>(rotation_matrix_)}}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame> Rotation<2>::jacobian(\n    const std::array<T, 2>& source_coords) const {\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame> jacobian_matrix{\n      make_with_value<tt::remove_cvref_wrap_t<T>>(\n          dereference_wrapper(source_coords[0]), 0.0)};\n  get<0, 0>(jacobian_matrix) = get<0, 0>(rotation_matrix_);\n  get<1, 0>(jacobian_matrix) = get<1, 0>(rotation_matrix_);\n  get<0, 1>(jacobian_matrix) = get<0, 1>(rotation_matrix_);\n  get<1, 1>(jacobian_matrix) = get<1, 1>(rotation_matrix_);\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame>\nRotation<2>::inv_jacobian(const std::array<T, 2>& source_coords) const {\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame> inv_jacobian_matrix{\n      make_with_value<tt::remove_cvref_wrap_t<T>>(\n          dereference_wrapper(source_coords[0]), 0.0)};\n  get<0, 0>(inv_jacobian_matrix) = get<0, 0>(rotation_matrix_);\n  get<1, 0>(inv_jacobian_matrix) = get<0, 1>(rotation_matrix_);\n  get<0, 1>(inv_jacobian_matrix) = get<1, 0>(rotation_matrix_);\n  get<1, 1>(inv_jacobian_matrix) = get<1, 1>(rotation_matrix_);\n  return inv_jacobian_matrix;\n}\n\nvoid Rotation<2>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | rotation_angle_;\n    p | rotation_matrix_;\n    p | is_identity_;\n  }\n}\n\nbool operator==(const Rotation<2>& lhs, const Rotation<2>& rhs) {\n  return lhs.rotation_angle_ == rhs.rotation_angle_ and\n         lhs.is_identity_ == rhs.is_identity_;\n}\n\nbool operator!=(const Rotation<2>& lhs, const Rotation<2>& rhs) {\n  return not(lhs == rhs);\n}\n\nRotation<3>::Rotation(const double rotation_about_z,\n                      const double rotation_about_rotated_y,\n                      const double rotation_about_rotated_z)\n    : rotation_about_z_(rotation_about_z),\n      rotation_about_rotated_y_(rotation_about_rotated_y),\n      rotation_about_rotated_z_(rotation_about_rotated_z),\n      rotation_matrix_(std::numeric_limits<double>::signaling_NaN()),\n      is_identity_(rotation_about_z_ == 0.0 and\n                   rotation_about_rotated_y_ == 0.0 and\n                   rotation_about_rotated_z_ == 0.0) {\n  const double cos_alpha = cos(rotation_about_z_);\n  const double sin_alpha = sin(rotation_about_z_);\n  const double cos_beta = cos(rotation_about_rotated_y_);\n  const double sin_beta = sin(rotation_about_rotated_y_);\n  const double cos_gamma = cos(rotation_about_rotated_z_);\n  const double sin_gamma = sin(rotation_about_rotated_z_);\n  get<0, 0>(rotation_matrix_) =\n      cos_gamma * cos_beta * cos_alpha - sin_gamma * sin_alpha;\n  get<0, 1>(rotation_matrix_) =\n      -sin_gamma * cos_beta * cos_alpha - cos_gamma * sin_alpha;\n  get<0, 2>(rotation_matrix_) = sin_beta * cos_alpha;\n  get<1, 0>(rotation_matrix_) =\n      cos_gamma * cos_beta * sin_alpha + sin_gamma * cos_alpha;\n  get<1, 1>(rotation_matrix_) =\n      -sin_gamma * cos_beta * sin_alpha + cos_gamma * cos_alpha;\n  get<1, 2>(rotation_matrix_) = sin_beta * sin_alpha;\n  get<2, 0>(rotation_matrix_) = -cos_gamma * sin_beta;\n  get<2, 1>(rotation_matrix_) = sin_gamma * sin_beta;\n  get<2, 2>(rotation_matrix_) = cos_beta;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> Rotation<3>::operator()(\n    const std::array<T, 3>& source_coords) const {\n  return {{source_coords[0] * get<0, 0>(rotation_matrix_) +\n               source_coords[1] * get<0, 1>(rotation_matrix_) +\n               source_coords[2] * get<0, 2>(rotation_matrix_),\n           source_coords[0] * get<1, 0>(rotation_matrix_) +\n               source_coords[1] * get<1, 1>(rotation_matrix_) +\n               source_coords[2] * get<1, 2>(rotation_matrix_),\n           source_coords[0] * get<2, 0>(rotation_matrix_) +\n               source_coords[1] * get<2, 1>(rotation_matrix_) +\n               source_coords[2] * get<2, 2>(rotation_matrix_)}};\n}\n\nstd::optional<std::array<double, 3>> Rotation<3>::inverse(\n    const std::array<double, 3>& target_coords) const {\n  // Inverse rotation matrix is the same as the transpose.\n  return {{{target_coords[0] * get<0, 0>(rotation_matrix_) +\n                target_coords[1] * get<1, 0>(rotation_matrix_) +\n                target_coords[2] * get<2, 0>(rotation_matrix_),\n            target_coords[0] * get<0, 1>(rotation_matrix_) +\n                target_coords[1] * get<1, 1>(rotation_matrix_) +\n                target_coords[2] * get<2, 1>(rotation_matrix_),\n            target_coords[0] * get<0, 2>(rotation_matrix_) +\n                target_coords[1] * get<1, 2>(rotation_matrix_) +\n                target_coords[2] * get<2, 2>(rotation_matrix_)}}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> Rotation<3>::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian_matrix{\n      make_with_value<tt::remove_cvref_wrap_t<T>>(\n          dereference_wrapper(source_coords[0]), 0.0)};\n  get<0, 0>(jacobian_matrix) = get<0, 0>(rotation_matrix_);\n  get<1, 0>(jacobian_matrix) = get<1, 0>(rotation_matrix_);\n  get<0, 1>(jacobian_matrix) = get<0, 1>(rotation_matrix_);\n  get<1, 1>(jacobian_matrix) = get<1, 1>(rotation_matrix_);\n  get<2, 0>(jacobian_matrix) = get<2, 0>(rotation_matrix_);\n  get<2, 1>(jacobian_matrix) = get<2, 1>(rotation_matrix_);\n  get<0, 2>(jacobian_matrix) = get<0, 2>(rotation_matrix_);\n  get<1, 2>(jacobian_matrix) = get<1, 2>(rotation_matrix_);\n  get<2, 2>(jacobian_matrix) = get<2, 2>(rotation_matrix_);\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nRotation<3>::inv_jacobian(const std::array<T, 3>& source_coords) const {\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian_matrix{\n      make_with_value<tt::remove_cvref_wrap_t<T>>(\n          dereference_wrapper(source_coords[0]), 0.0)};\n  get<0, 0>(inv_jacobian_matrix) = get<0, 0>(rotation_matrix_);\n  get<1, 0>(inv_jacobian_matrix) = get<0, 1>(rotation_matrix_);\n  get<0, 1>(inv_jacobian_matrix) = get<1, 0>(rotation_matrix_);\n  get<1, 1>(inv_jacobian_matrix) = get<1, 1>(rotation_matrix_);\n  get<2, 0>(inv_jacobian_matrix) = get<0, 2>(rotation_matrix_);\n  get<2, 1>(inv_jacobian_matrix) = get<1, 2>(rotation_matrix_);\n  get<0, 2>(inv_jacobian_matrix) = get<2, 0>(rotation_matrix_);\n  get<1, 2>(inv_jacobian_matrix) = get<2, 1>(rotation_matrix_);\n  get<2, 2>(inv_jacobian_matrix) = get<2, 2>(rotation_matrix_);\n  return inv_jacobian_matrix;\n}\n\nvoid Rotation<3>::pup(PUP::er& p) {  // NOLINT\n  p | rotation_about_z_;\n  p | rotation_about_rotated_y_;\n  p | rotation_about_rotated_z_;\n  p | rotation_matrix_;\n  p | is_identity_;\n}\n\nbool operator==(const Rotation<3>& lhs, const Rotation<3>& rhs) {\n  return lhs.rotation_about_z_ == rhs.rotation_about_z_ and\n         lhs.rotation_about_rotated_y_ == rhs.rotation_about_rotated_y_ and\n         lhs.rotation_about_rotated_z_ == rhs.rotation_about_rotated_z_ and\n         lhs.is_identity_ == rhs.is_identity_;\n}\n\nbool operator!=(const Rotation<3>& lhs, const Rotation<3>& rhs) {\n  return not(lhs == rhs);\n}\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)> \\\n  Rotation<DIM(data)>::operator()(                                     \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  Rotation<DIM(data)>::jacobian(                                       \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  Rotation<DIM(data)>::inv_jacobian(                                   \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (2, 3),\n    (double, DataVector,\n     std::reference_wrapper<const double>,\n     std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3), MAP_AUTODIFF_TYPES)\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Rotation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class Rotation.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <size_t Dim>\nclass Rotation;\n/// \\endcond\n\n/*!\n * \\ingroup CoordinateMapsGroup\n * \\brief Spatial rotation in two dimensions.\n *\n * Let \\f$(R,\\Phi)\\f$ be the polar coordinates associated with\n * \\f$(\\xi,\\eta)\\f$.\n * Let \\f$(r,\\phi)\\f$ be the polar coordinates associated with \\f$(x,y)\\f$.\n * Applies the spatial rotation \\f$\\phi = \\Phi + \\alpha\\f$.\n *\n * The formula for the mapping is:\n *\\f{eqnarray*}\n  x &=& \\xi \\cos \\alpha - \\eta \\sin \\alpha \\\\\n  y &=& \\xi \\sin \\alpha + \\eta \\cos \\alpha\n  \\f}.\n */\ntemplate <>\nclass Rotation<2> {\n public:\n  static constexpr size_t dim = 2;\n\n  /// Constructor.\n  ///\n  /// \\param rotation_angle the angle \\f$\\alpha\\f$ (in radians).\n  explicit Rotation(double rotation_angle);\n  Rotation() = default;\n  ~Rotation() = default;\n  Rotation(const Rotation&) = default;\n  Rotation& operator=(const Rotation&) = default;\n  Rotation(Rotation&&) = default;  // NOLINT\n  Rotation& operator=(Rotation&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 2> operator()(\n      const std::array<T, 2>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 2>> inverse(\n      const std::array<double, 2>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame> jacobian(\n      const std::array<T, 2>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 2, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 2>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  friend bool operator==(const Rotation<2>& lhs, const Rotation<2>& rhs);\n\n  double rotation_angle_{std::numeric_limits<double>::signaling_NaN()};\n  tnsr::ij<double, 2, Frame::Grid> rotation_matrix_{\n      std::numeric_limits<double>::signaling_NaN()};\n  bool is_identity_{false};\n};\n\nbool operator!=(const Rotation<2>& lhs, const Rotation<2>& rhs);\n\n/*!\n * \\ingroup CoordinateMapsGroup\n * \\brief Spatial rotation in three dimensions using Euler angles\n *\n * Rotation angles should be specified in degrees.\n * First rotation \\f$\\alpha\\f$ is about z axis.\n * Second rotation \\f$\\beta\\f$ is about rotated y axis.\n * Third rotation \\f$\\gamma\\f$ is about rotated z axis.\n * These rotations are of the \\f$(\\xi,\\eta,\\zeta)\\f$ coordinate system with\n * respect\n * to the grid coordinates \\f$(x,y,z)\\f$.\n *\n * The formula for the mapping is:\n * \\f{eqnarray*}\n *   x &=& \\xi (\\cos\\gamma \\cos\\beta \\cos\\alpha - \\sin\\gamma \\sin\\alpha)\n * + \\eta (-\\sin\\gamma \\cos\\beta \\cos\\alpha - \\cos\\gamma \\sin\\alpha)\n * + \\zeta \\sin\\beta \\cos\\alpha \\\\\n * y &=& \\xi (\\cos\\gamma \\cos\\beta \\sin\\alpha + \\sin\\gamma \\cos\\alpha)\n * + \\eta (-\\sin\\gamma \\cos\\beta \\sin\\alpha + \\cos\\gamma \\cos\\alpha)\n * + \\zeta \\sin\\beta \\sin\\alpha \\\\\n *        z &=& -\\xi \\cos\\gamma \\sin\\beta + \\eta \\sin\\gamma \\sin\\beta\n *        +  \\zeta \\cos\\beta\n *  \\f}\n */\ntemplate <>\nclass Rotation<3> {\n public:\n  static constexpr size_t dim = 3;\n\n  /// Constructor.\n  ///\n  /// \\param rotation_about_z the angle \\f$\\alpha\\f$ (in radians).\n  /// \\param rotation_about_rotated_y the angle \\f$\\beta\\f$ (in radians).\n  /// \\param rotation_about_rotated_z the angle \\f$\\gamma\\f$ (in radians).\n  Rotation(double rotation_about_z, double rotation_about_rotated_y,\n           double rotation_about_rotated_z);\n  Rotation() = default;\n  ~Rotation() = default;\n  Rotation(const Rotation&) = default;\n  Rotation& operator=(const Rotation&) = default;\n  Rotation(Rotation&&) = default;  // NOLINT\n  Rotation& operator=(Rotation&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  friend bool operator==(const Rotation<3>& lhs, const Rotation<3>& rhs);\n\n  double rotation_about_z_{std::numeric_limits<double>::signaling_NaN()};\n  double rotation_about_rotated_y_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double rotation_about_rotated_z_{\n      std::numeric_limits<double>::signaling_NaN()};\n  tnsr::ij<double, 3, Frame::Grid> rotation_matrix_{\n      std::numeric_limits<double>::signaling_NaN()};\n  bool is_identity_{false};\n};\n\nbool operator!=(const Rotation<3>& lhs, const Rotation<3>& rhs);\n\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/SpecialMobius.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/SpecialMobius.hpp\"\n\n#include <cmath>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nSpecialMobius::SpecialMobius(const double mu)\n    : mu_(mu), is_identity_(mu_ == 0.0) {\n  // Note: Empirically we have found that the map is accurate\n  // to 12 decimal places for mu = 0.96.\n  ASSERT(abs(mu) < 0.96, \"The magnitude of mu must be less than 0.96.\");\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> SpecialMobius::mobius_distortion(\n    const std::array<T, 3>& coords, const double mu) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& x = coords[0];\n  const ReturnType& y = coords[1];\n  const ReturnType& z = coords[2];\n  const double mu_squared = square(mu);\n  const ReturnType r_squared = square(x) + square(y) + square(z);\n  const ReturnType lambda = 1.0 / (1.0 - 2.0 * mu * x + mu_squared * r_squared);\n  return std::array<ReturnType, 3>{\n      {lambda * ((1.0 + mu_squared) * x - mu * (1.0 + r_squared)),\n       (1.0 - mu_squared) * lambda * y, (1.0 - mu_squared) * lambda * z}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nSpecialMobius::mobius_distortion_jacobian(const std::array<T, 3>& coords,\n                                          const double mu) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& x = coords[0];\n  const ReturnType& y = coords[1];\n  const ReturnType& z = coords[2];\n  const double mu_squared = square(mu);\n  const ReturnType r_squared = square(x) + square(y) + square(z);\n  const ReturnType common_factor =\n      (mu_squared - 1.0) / square(1.0 - 2.0 * mu * x + mu_squared * r_squared);\n  auto jacobian_matrix =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(coords[0]), 0.0);\n\n  get<0, 0>(jacobian_matrix) =\n      -(mu_squared * (2.0 * square(x) - r_squared) - 2.0 * mu * x + 1.0) *\n      common_factor;\n  get<1, 0>(jacobian_matrix) = (2.0 * mu * y * (mu * x - 1.0)) * common_factor;\n  get<2, 0>(jacobian_matrix) = (2.0 * mu * z * (mu * x - 1.0)) * common_factor;\n\n  get<0, 1>(jacobian_matrix) = -1.0 * get<1, 0>(jacobian_matrix);\n  get<1, 1>(jacobian_matrix) =\n      -(mu_squared * (r_squared - 2.0 * square(y)) - 2.0 * mu * x + 1.0) *\n      common_factor;\n  get<2, 1>(jacobian_matrix) = 2.0 * mu_squared * y * z * common_factor;\n\n  get<0, 2>(jacobian_matrix) = -1.0 * get<2, 0>(jacobian_matrix);\n  get<1, 2>(jacobian_matrix) = get<2, 1>(jacobian_matrix);\n  get<2, 2>(jacobian_matrix) =\n      -(mu_squared * (r_squared - 2.0 * square(z)) - 2.0 * mu * x + 1.0) *\n      common_factor;\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> SpecialMobius::operator()(\n    const std::array<T, 3>& source_coords) const {\n  return mobius_distortion(source_coords, mu_);\n}\n\nstd::optional<std::array<double, 3>> SpecialMobius::inverse(\n    const std::array<double, 3>& target_coords) const {\n  // Invert only points inside or on the unit sphere.\n  const auto r_squared = magnitude(target_coords);\n  if (r_squared <= 1.0 or equal_within_roundoff(r_squared,1.0)) {\n    return mobius_distortion(target_coords, -mu_);\n  }\n  return std::nullopt;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> SpecialMobius::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  return mobius_distortion_jacobian(source_coords, mu_);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nSpecialMobius::inv_jacobian(const std::array<T, 3>& source_coords) const {\n  return mobius_distortion_jacobian((*this)(source_coords), -mu_);\n}\n\nvoid SpecialMobius::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | mu_;\n    p | is_identity_;\n  }\n}\n\nbool operator==(const SpecialMobius& lhs, const SpecialMobius& rhs) {\n  return lhs.mu_ == rhs.mu_ and lhs.is_identity_ == rhs.is_identity_;\n}\n\nbool operator!=(const SpecialMobius& lhs, const SpecialMobius& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  SpecialMobius::operator()(const std::array<DTYPE(data), 3>& source_coords)   \\\n      const;                                                                   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  SpecialMobius::jacobian(const std::array<DTYPE(data), 3>& source_coords)     \\\n      const;                                                                   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  SpecialMobius::inv_jacobian(const std::array<DTYPE(data), 3>& source_coords) \\\n      const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (double, DataVector,\n                  std::reference_wrapper<const double>,\n                  std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, MAP_AUTODIFF_TYPES)\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/SpecialMobius.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Redistributes gridpoints within the unit sphere.\n * \\image html SpecialMobius.png \"A sphere with a `mu` of 0.25.\"\n *\n * \\details A special case of the conformal Mobius transformation that\n * maps the unit ball to itself. This map depends on a single\n * parameter, `mu` \\f$ = \\mu\\f$, which is the x-coordinate of the preimage\n * of the origin under this map. This map has the fixed points \\f$x=1\\f$ and\n * \\f$x=-1\\f$. The map is singular for \\f$\\mu=1\\f$ but we have found that this\n * map is accurate up to 12 decimal places for values of \\f$\\mu\\f$ up to 0.96.\n *\n * We define the auxiliary variables\n * \\f[ r := \\sqrt{x^2 + y^2 +z^2}\\f]\n * and\n * \\f[ \\lambda := \\frac{1}{1 - 2 x \\mu + \\mu^2 r^2}\\f]\n *\n * The map corresponding to this transformation in cartesian coordinates\n * is then given by:\n *\n * \\f[\\vec{x}'(x,y,z) =\n * \\lambda\\begin{bmatrix}\n * x(1+\\mu^2) - \\mu(1+r^2)\\\\\n * y(1-\\mu^2)\\\\\n * z(1-\\mu^2)\\\\\n * \\end{bmatrix}\\f]\n *\n * The inverse map is the same as the forward map with \\f$\\mu\\f$\n * replaced by \\f$-\\mu\\f$.\n *\n * This map is intended to be used only inside the unit sphere.  A\n * point inside the unit sphere maps to another point inside the unit\n * sphere. The map can have undesirable behavior at certain points\n * outside the unit sphere: The map is singular at\n * \\f$(x,y,z) = (1/\\mu, 0, 0)\\f$ (which is outside the unit sphere\n * since \\f$|\\mu| < 1\\f$). Moreover, a point on the \\f$x\\f$-axis\n * arbitrarily close to the singularity maps to an arbitrarily large\n * value on the \\f$\\pm x\\f$-axis, where the sign depends on which side\n * of the singularity the point is on.\n *\n * A general Mobius transformation is a function on the complex plane, and\n * takes the form \\f$ f(z) = \\frac{az+b}{cz+d}\\f$, where\n * \\f$z, a, b, c, d \\in \\mathbb{C}\\f$, and \\f$ad-bc\\neq 0\\f$.\n *\n * The special case used in this map is the function\n * \\f$ f(z) = \\frac{z - \\mu}{1 - z\\mu}\\f$. This has the desired properties:\n * - The unit disk in the complex plane is mapped to itself.\n *\n * - The x-axis is mapped to itself.\n *\n * - \\f$f(\\mu) = 0\\f$.\n *\n * The three-dimensional version of this map is obtained by rotating the disk\n * in the plane about the x-axis.\n *\n * This map is useful for performing transformations along the x-axis\n * that preserve the unit disk. A concrete example of this is in the BBH\n * domain, where two BBHs with a center-of-mass at x=\\f$\\mu\\f$ can be shifted\n * such that the new center of mass is now located at x=0. Additionally,\n * the spherical shape of the outer wave-zone is preserved and, as a mobius\n * map, the spherical coordinate shapes of the black holes is also preserved.\n */\nclass SpecialMobius {\n public:\n  static constexpr size_t dim = 3;\n  explicit SpecialMobius(double mu);\n  SpecialMobius() = default;\n  ~SpecialMobius() = default;\n  SpecialMobius(SpecialMobius&&) = default;\n  SpecialMobius(const SpecialMobius&) = default;\n  SpecialMobius& operator=(const SpecialMobius&) = default;\n  SpecialMobius& operator=(SpecialMobius&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// Returns std::nullopt for target_coords outside the unit sphere.\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return is_identity_; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> mobius_distortion(\n      const std::array<T, 3>& coords, double mu) const;\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\n  mobius_distortion_jacobian(const std::array<T, 3>& coords, double mu) const;\n  friend bool operator==(const SpecialMobius& lhs, const SpecialMobius& rhs);\n\n  double mu_{std::numeric_limits<double>::signaling_NaN()};\n  bool is_identity_{false};\n};\nbool operator!=(const SpecialMobius& lhs, const SpecialMobius& rhs);\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/SphericalToCartesianPfaffian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain::CoordinateMaps {\nSphericalToCartesianPfaffian::SphericalToCartesianPfaffian() = default;\nSphericalToCartesianPfaffian::SphericalToCartesianPfaffian(\n    SphericalToCartesianPfaffian&&) = default;\nSphericalToCartesianPfaffian::SphericalToCartesianPfaffian(\n    const SphericalToCartesianPfaffian&) = default;\nSphericalToCartesianPfaffian& SphericalToCartesianPfaffian::operator=(\n    const SphericalToCartesianPfaffian&) = default;\nSphericalToCartesianPfaffian& SphericalToCartesianPfaffian::operator=(\n    SphericalToCartesianPfaffian&&) = default;\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3>\nSphericalToCartesianPfaffian::operator()(\n    const std::array<T, 3>& source_coords) const {\n  const auto& [r, theta, phi] = source_coords;\n  return {\n      {r * sin(theta) * cos(phi), r * sin(theta) * sin(phi), r * cos(theta)}};\n}\n\n// NOLINTNEXTLINE(readability-convert-member-functions-to-static)\nstd::optional<std::array<double, 3>> SphericalToCartesianPfaffian::inverse(\n    const std::array<double, 3>& target_coords) const {\n  const auto& [x, y, z] = target_coords;\n  if (UNLIKELY(y == 0.0 and x == 0.0)) {\n    if (UNLIKELY(z == 0.0)) {\n      return std::array{0.0, 0.5 * M_PI, 0.0};\n    } else {\n      return std::array{std::abs(z), z > 0.0 ? 0.0 : M_PI, 0.0};\n    }\n  } else {\n    const double r = std::hypot(x, y, z);\n    const double phi = atan2(y, x);\n    return std::array{r, acos(z / r), phi < 0.0 ? phi + 2.0 * M_PI : phi};\n  }\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nSphericalToCartesianPfaffian::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  const auto& [r, theta, phi] = source_coords;\n  using DataType = tt::remove_cvref_wrap_t<T>;\n  tnsr::Ij<DataType, 3, Frame::NoFrame> jacobian_matrix{\n      make_with_value<DataType>(dereference_wrapper(r), 0.0)};\n  // Pfaffian basis means phi components are 1 / sin_theta times those of a\n  // coord basis\n  const auto& cos_theta = get<2, 0>(jacobian_matrix) = cos(theta);\n  const auto& sin_theta = get<2, 1>(jacobian_matrix) = sin(theta);\n  const auto& cos_phi = get<1, 2>(jacobian_matrix) = cos(phi);\n  const auto& sin_phi = get<0, 2>(jacobian_matrix) = sin(phi);\n  get<0, 0>(jacobian_matrix) = sin_theta * cos_phi;\n  get<1, 0>(jacobian_matrix) = sin_theta * sin_phi;\n  get<0, 1>(jacobian_matrix) = r * cos_theta * cos_phi;\n  get<1, 1>(jacobian_matrix) = r * cos_theta * sin_phi;\n  get<2, 1>(jacobian_matrix) *= -r;\n  get<0, 2>(jacobian_matrix) *= -r;\n  get<1, 2>(jacobian_matrix) *= r;\n  // get<2, 2>(jacobian_matrix) is zero\n  return jacobian_matrix;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nSphericalToCartesianPfaffian::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  const auto& [r, theta, phi] = source_coords;\n  using DataType = tt::remove_cvref_wrap_t<T>;\n  tnsr::Ij<DataType, 3, Frame::NoFrame> inv_jacobian_matrix{\n      make_with_value<DataType>(dereference_wrapper(r), 0.0)};\n  // Pfaffian basis means phi components are sin_theta times those of a coord\n  // basis\n  const auto& cos_theta = get<0, 2>(inv_jacobian_matrix) = cos(theta);\n  const auto& sin_theta = get<1, 2>(inv_jacobian_matrix) = sin(theta);\n  const auto& cos_phi = get<2, 1>(inv_jacobian_matrix) = cos(phi);\n  const auto& sin_phi = get<2, 0>(inv_jacobian_matrix) = sin(phi);\n  const auto& one_over_r = get<2, 2>(inv_jacobian_matrix) = 1.0/r;\n  get<0, 0>(inv_jacobian_matrix) = sin_theta * cos_phi;\n  get<0, 1>(inv_jacobian_matrix) = sin_theta * sin_phi;\n  get<1, 0>(inv_jacobian_matrix) = cos_theta * cos_phi * one_over_r;\n  get<1, 1>(inv_jacobian_matrix) = cos_theta * sin_phi * one_over_r;\n  get<1, 2>(inv_jacobian_matrix) *= -one_over_r;\n  get<2, 0>(inv_jacobian_matrix) *= -one_over_r;\n  get<2, 1>(inv_jacobian_matrix) *= one_over_r;\n  get<2, 2>(inv_jacobian_matrix) *= 0.0;\n  return inv_jacobian_matrix;\n}\n\nvoid SphericalToCartesianPfaffian::pup(PUP::er& /*p*/) {}\n\nbool operator==(const SphericalToCartesianPfaffian& /*lhs*/,\n                const SphericalToCartesianPfaffian& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const SphericalToCartesianPfaffian& lhs,\n                const SphericalToCartesianPfaffian& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE_DTYPE(_, data)                                           \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  SphericalToCartesianPfaffian::operator()(                                  \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  SphericalToCartesianPfaffian::jacobian(                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  SphericalToCartesianPfaffian::inv_jacobian(                                \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_DTYPE,\n                        (double, DataVector,\n                         std::reference_wrapper<const double>,\n                         std::reference_wrapper<const DataVector>))\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Pfaffian transformation from spherical to Cartesian coordinates.\n *\n * \\note This map is designed to be used together with Spherepack! Spherepack\n * expects a Pfaffian transformation of the derivatives.\n *\n * \\details This is a Pfaffian mapping from \\f$(r,\\theta,\\phi) \\rightarrow\n * (x,y,z) \\f$.\n *\n * The formula for the mapping is...\n * \\f{eqnarray*}\n *     x &=& r \\sin\\theta \\cos\\phi \\\\\n *     y &=& r \\sin\\theta \\sin\\phi \\\\\n *     z &=& r \\cos\\theta\n * \\f}\n *\n * The Pfaffian basis vectors\n * \\f$ (e_{\\hat r}, e_{\\hat \\theta}, e_{\\hat \\phi})\\f$\n * are related to the coordinate basis vectors\n * \\f$ (e_r, e_{\\theta}, e_{\\phi})\\f$\n * by...\n * \\f{eqnarray*}\n *     e_{\\hat r}      &=& e_r \\\\\n *     e_{\\hat \\theta} &=& e_{\\theta} \\\\\n *     e_{\\hat \\phi}   &=& \\frac{1}{\\sin \\theta} e_{\\phi}\n * \\f}\n */\nclass SphericalToCartesianPfaffian {\n public:\n  static constexpr size_t dim = 3;\n  SphericalToCartesianPfaffian();\n  ~SphericalToCartesianPfaffian() = default;\n  SphericalToCartesianPfaffian(SphericalToCartesianPfaffian&&);\n  SphericalToCartesianPfaffian(const SphericalToCartesianPfaffian&);\n  SphericalToCartesianPfaffian& operator=(const SphericalToCartesianPfaffian&);\n  SphericalToCartesianPfaffian& operator=(SphericalToCartesianPfaffian&&);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static constexpr bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n};\n\nbool operator==(const SphericalToCartesianPfaffian& lhs,\n                const SphericalToCartesianPfaffian& rhs);\n\nbool operator!=(const SphericalToCartesianPfaffian& lhs,\n                const SphericalToCartesianPfaffian& rhs);\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\n/// \\cond\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim>\nclass CoordinateMapBase;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\n/// \\ingroup ComputationalDomainGroup\n/// \\brief %Tags for the coordinate maps.\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The coordinate map from source to target coordinates\ntemplate <size_t VolumeDim, typename SourceFrame, typename TargetFrame>\nstruct CoordinateMap : db::SimpleTag {\nstatic constexpr size_t dim = VolumeDim;\n  using target_frame = TargetFrame;\n  using source_frame = SourceFrame;\n\n  static std::string name() {\n    return \"CoordinateMap(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n  using type = std::unique_ptr<\n      domain::CoordinateMapBase<SourceFrame, TargetFrame, VolumeDim>>;\n};\n}  // namespace Tags\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CubicScale.cpp\n  Rotation.cpp\n  RotationMatrixHelpers.cpp\n  RotScaleTrans.cpp\n  Shape.cpp\n  Skew.cpp\n  SphericalCompression.cpp\n  Translation.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CubicScale.hpp\n  ProductMaps.hpp\n  ProductMaps.tpp\n  Rotation.hpp\n  RotationMatrixHelpers.hpp\n  RotScaleTrans.hpp\n  Shape.hpp\n  Skew.hpp\n  SphericalCompression.hpp\n  Translation.hpp\n  )\n\nadd_subdirectory(ShapeMapTransitionFunctions)\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/CubicScale.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace domain::CoordinateMaps::TimeDependent {\n\ntemplate <size_t Dim>\nCubicScale<Dim>::CubicScale(const double outer_boundary,\n                            std::string function_of_time_name_a,\n                            std::string function_of_time_name_b)\n    : f_of_t_a_(std::move(function_of_time_name_a)),\n      f_of_t_b_(std::move(function_of_time_name_b)),\n      f_of_t_names_({f_of_t_a_, f_of_t_b_}),\n      functions_of_time_equal_(f_of_t_a_ == f_of_t_b_) {\n  if (outer_boundary <= 0.0) {\n    ERROR(\"For invertability, we require outer_boundary to be positive, but is \"\n          << outer_boundary);\n  }\n  one_over_outer_boundary_ = 1.0 / outer_boundary;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> CubicScale<Dim>::operator()(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const double a_of_t = functions_of_time.at(f_of_t_a_)->func(time)[0][0];\n\n  if (functions_of_time_equal_) {\n    // optimization for linear radial scaling\n    std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(result, i) = a_of_t * gsl::at(source_coords, i);\n    }\n    return result;\n  }\n\n  const double b_of_t = functions_of_time.at(f_of_t_b_)->func(time)[0][0];\n\n  tt::remove_cvref_wrap_t<T> rho_squared =\n      square(dereference_wrapper(source_coords[0]));\n  for (size_t i = 1; i < Dim; ++i) {\n    rho_squared += square(dereference_wrapper(gsl::at(source_coords, i)));\n  }\n  // Reuse rho^2 allocation\n  rho_squared = a_of_t + (b_of_t - a_of_t) * square(one_over_outer_boundary_) *\n                             rho_squared;\n\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n  for (size_t i = 0; i < Dim - 1; ++i) {\n    gsl::at(result, i) = gsl::at(source_coords, i) * rho_squared;\n  }\n  rho_squared = source_coords[Dim - 1] * rho_squared;\n  result[Dim - 1] = std::move(rho_squared);\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::array<double, Dim>> CubicScale<Dim>::inverse(\n    const std::array<double, Dim>& target_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  if (functions_of_time_equal_) {\n    // optimization for linear radial scaling\n    const double one_over_a_of_t =\n        1.0 / functions_of_time.at(f_of_t_a_)->func(time)[0][0];\n\n    // Construct std::optional to have a default value of an empty array.\n    // Doing just result{} would construct a std::optional that doesn't hold a\n    // value and so *result would throw an exception.\n    std::optional<std::array<double, Dim>> result{std::array<double, Dim>{}};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(*result, i) = one_over_a_of_t * gsl::at(target_coords, i);\n    }\n    return result;\n  }\n\n  // the source coordinates \\xi^{\\hat{i}} are found by solving for the roots of\n  // (b-a)/R^2*\\rho^3 + a*\\rho - r = 0,\n  // where a and b are the FunctionsOfTime, R is the outer_boundary, and r is\n  // the mapped/target coordinates radius.\n  const double a_of_t = functions_of_time.at(f_of_t_a_)->func(time)[0][0];\n  const double b_of_t = functions_of_time.at(f_of_t_b_)->func(time)[0][0];\n\n  // these checks ensure that the function is monotonically increasing\n  // and that there is one real root in the domain of \\rho, [0,R]\n  if (a_of_t <= 0.0) {\n    ERROR(\"We require expansion_a > 0 for invertibility, however expansion_a = \"\n          << a_of_t << \".\");\n  }\n  if (b_of_t < 2.0 / 3.0 * a_of_t) {\n    ERROR(\"The map is invertible only if expansion_b >= expansion_a*2/3, \"\n          << \" but expansion_b = \" << b_of_t << \" and expansion_a = \" << a_of_t\n          << \".\");\n  }\n\n  // To invert the map we work in the dimensionless radius,\n  // r / R_{outer_boundary}.\n  const double target_dimensionless_radius =\n      magnitude(target_coords) * one_over_outer_boundary_;\n\n  if (UNLIKELY(target_dimensionless_radius == 0.0)) {\n    return {make_array<Dim>(0.0)};\n  }\n\n  double scale_factor = std::numeric_limits<double>::signaling_NaN();\n  // Check if x_bar is outside of the range of the map.\n  // We need a slight buffer because computing (r/R) is not equal to (r * (1/R))\n  // at roundoff and thus to make sure we include the boundary we need to\n  // support epsilon above b(t).\n  if (UNLIKELY(target_dimensionless_radius >\n               b_of_t * (1.0 + 2.0 * std::numeric_limits<double>::epsilon()))) {\n    return std::nullopt;\n  } else if (UNLIKELY(target_dimensionless_radius > b_of_t)) {\n    scale_factor = 1.0;\n  } else {\n    const double cubic_coef_a = (b_of_t - a_of_t);\n\n    // Solve the modified equation:\n    // q * ( (b-a) q^2 + a) - r / R = 0,\n    // where q = rho / R, and rho is the source radius\n    const auto cubic = [&cubic_coef_a, &a_of_t, &target_dimensionless_radius](\n                           const double source_dimensionless_radius) {\n      return source_dimensionless_radius *\n                 (cubic_coef_a * square(source_dimensionless_radius) + a_of_t) -\n             target_dimensionless_radius;\n    };\n\n    scale_factor = RootFinder::toms748(cubic, 0.0, 1.0, 1.0e-14, 1.0e-15);\n  }\n\n  return {scale_factor / target_dimensionless_radius * target_coords};\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> CubicScale<Dim>::frame_velocity(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const double dt_a_of_t =\n      functions_of_time.at(f_of_t_a_)->func_and_deriv(time)[1][0];\n\n  if (functions_of_time_equal_) {\n    // optimization for linear radial scaling\n    std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(result, i) = dt_a_of_t * gsl::at(source_coords, i);\n    }\n    return result;\n  }\n\n  const double dt_b_of_t =\n      functions_of_time.at(f_of_t_b_)->func_and_deriv(time)[1][0];\n\n  tt::remove_cvref_wrap_t<T> rho_squared =\n      square(dereference_wrapper(source_coords[0]));\n  for (size_t i = 1; i < Dim; ++i) {\n    rho_squared += square(dereference_wrapper(gsl::at(source_coords, i)));\n  }\n  // Reuse rho^2 allocation\n  rho_squared = dt_a_of_t + (dt_b_of_t - dt_a_of_t) *\n                                square(one_over_outer_boundary_) * rho_squared;\n\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n  for (size_t i = 0; i < Dim - 1; ++i) {\n    gsl::at(result, i) = gsl::at(source_coords, i) * rho_squared;\n  }\n  rho_squared = source_coords[Dim - 1] * rho_squared;\n  result[Dim - 1] = std::move(rho_squared);\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nCubicScale<Dim>::jacobian(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const double a_of_t = functions_of_time.at(f_of_t_a_)->func(time)[0][0];\n\n  if (functions_of_time_equal_) {\n    // optimization for linear radial scaling\n    auto jac{make_with_value<\n        tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>>(\n        dereference_wrapper(source_coords[0]), 0.0)};\n    for (size_t i = 0; i < Dim; ++i) {\n      jac.get(i, i) = a_of_t;\n    }\n    return jac;\n  }\n\n  const double b_of_t = functions_of_time.at(f_of_t_b_)->func(time)[0][0];\n\n  tt::remove_cvref_wrap_t<T> rho_squared =\n      square(dereference_wrapper(source_coords[0]));\n  for (size_t i = 1; i < Dim; ++i) {\n    rho_squared += square(dereference_wrapper(gsl::at(source_coords, i)));\n  }\n  const double rho_squared_coeff =\n      (b_of_t - a_of_t) * square(one_over_outer_boundary_);\n  // Reuse rho^2 allocation\n  rho_squared = a_of_t + rho_squared_coeff * rho_squared;\n\n  const double coeff =\n      2.0 * (b_of_t - a_of_t) * square(one_over_outer_boundary_);\n\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jac{};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      if (i == j) {\n        jac.get(i, j) = rho_squared + gsl::at(source_coords, i) *\n                                          gsl::at(source_coords, j) * coeff;\n      } else {\n        jac.get(i, j) =\n            gsl::at(source_coords, i) * gsl::at(source_coords, j) * coeff;\n      }\n    }\n  }\n\n  return jac;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nCubicScale<Dim>::inv_jacobian(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const double a_of_t = functions_of_time.at(f_of_t_a_)->func(time)[0][0];\n\n  if (functions_of_time_equal_) {\n    // optimization for linear radial scaling\n    auto inv_jac{make_with_value<\n        tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>>(\n        dereference_wrapper(source_coords[0]), 0.0)};\n    const double one_over_a = 1.0 / a_of_t;\n    for (size_t i = 0; i < Dim; ++i) {\n      inv_jac.get(i, i) = one_over_a;\n    }\n    return inv_jac;\n  }\n\n  const double b_of_t = functions_of_time.at(f_of_t_b_)->func(time)[0][0];\n\n  tt::remove_cvref_wrap_t<T> rho_squared =\n      square(dereference_wrapper(source_coords[0]));\n  for (size_t i = 1; i < Dim; ++i) {\n    rho_squared += square(dereference_wrapper(gsl::at(source_coords, i)));\n  }\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> inv_jac{};\n  get<0, 0>(inv_jac) =\n      1.0 / (a_of_t + (b_of_t - a_of_t) * square(one_over_outer_boundary_) *\n                          rho_squared);\n  for (size_t i = 1; i < Dim; ++i) {\n    inv_jac.get(i, i) = get<0, 0>(inv_jac);\n  }\n\n  // Factor out `double` computations to ensure minimal DataVector operations\n  const double denom_constant_a = a_of_t / square(one_over_outer_boundary_);\n  const double denom_constant_b = 3.0 * (b_of_t - a_of_t);\n  const double numerator_constant = -2.0 * (b_of_t - a_of_t);\n  if (Dim == 1) {\n    get<0, 0>(inv_jac) *=\n        (1.0 + numerator_constant /\n                   (denom_constant_a + denom_constant_b * rho_squared) *\n                   square(source_coords[0]));\n  } else {\n    // Reuse rho^2 allocation\n    rho_squared = numerator_constant /\n                  (denom_constant_a + denom_constant_b * rho_squared) *\n                  get<0, 0>(inv_jac);\n\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        if (i == j) {\n          inv_jac.get(i, j) += rho_squared * gsl::at(source_coords, i) *\n                               gsl::at(source_coords, j);\n        } else {\n          inv_jac.get(i, j) = rho_squared * gsl::at(source_coords, i) *\n                              gsl::at(source_coords, j);\n        }\n      }\n    }\n  }\n\n  return inv_jac;\n}\n\ntemplate <size_t Dim>\nvoid CubicScale<Dim>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | f_of_t_a_;\n    p | f_of_t_b_;\n    p | one_over_outer_boundary_;\n    p | functions_of_time_equal_;\n  }\n\n  // No need to pup this because it is uniquely determined by f_of_t_{a,b}_\n  if (p.isUnpacking()) {\n    f_of_t_names_ = std::unordered_set<std::string>{{f_of_t_a_, f_of_t_b_}};\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const CubicScale<Dim>& lhs, const CubicScale<Dim>& rhs) {\n  return lhs.f_of_t_a_ == rhs.f_of_t_a_ and lhs.f_of_t_b_ == rhs.f_of_t_b_ and\n         lhs.one_over_outer_boundary_ == rhs.one_over_outer_boundary_ and\n         lhs.functions_of_time_equal_ == rhs.functions_of_time_equal_;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                             \\\n  template class CubicScale<DIM(data)>;                  \\\n  template bool operator==(const CubicScale<DIM(data)>&, \\\n                           const CubicScale<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)> \\\n  CubicScale<DIM(data)>::operator()(                                   \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords,         \\\n      const double time,                                               \\\n      const std::unordered_map<                                        \\\n          std::string,                                                 \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&   \\\n          functions_of_time) const;                                    \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)> \\\n  CubicScale<DIM(data)>::frame_velocity(                               \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords,         \\\n      const double time,                                               \\\n      const std::unordered_map<                                        \\\n          std::string,                                                 \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&   \\\n          functions_of_time) const;                                    \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  CubicScale<DIM(data)>::jacobian(                                     \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords,         \\\n      const double time,                                               \\\n      const std::unordered_map<                                        \\\n          std::string,                                                 \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&   \\\n          functions_of_time) const;                                    \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  CubicScale<DIM(data)>::inv_jacobian(                                 \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords,         \\\n      const double time,                                               \\\n      const std::unordered_map<                                        \\\n          std::string,                                                 \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&   \\\n          functions_of_time) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3),\n                        (double, DataVector,\n                         std::reference_wrapper<const double>,\n                         std::reference_wrapper<const DataVector>))\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/CubicScale.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\n}  // namespace domain\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\nnamespace TimeDependent {\n/*!\n * \\ingroup CoordMapsTimeDependentGroup\n * \\brief Maps the radius as \\f$r(t) = a(t)\\rho + \\left(b(t) - a(t)\\right)\n * \\frac{\\rho^3} {R^2}\\f$ where \\f$\\rho\\f$ is the radius of the source\n * coordinates.\n *\n * The map scales the radius \\f$\\rho\\f$ in the source coordinates\n * \\f$\\xi^{\\hat{i}}\\f$ by a factor \\f$a(t)\\f$, while the coordinates near the\n * outer boundary \\f$R\\f$, are scaled by a factor \\f$b(t)\\f$. Here \\f$a(t)\\f$\n * and \\f$b(t)\\f$ are FunctionsOfTime. The target/mapped coordinates are denoted\n * by \\f$x^i\\f$.\n *\n * The mapped coordinates are given by:\n *\n * \\f{align}{\n * x^i = \\left[a + (b-a) \\frac{\\rho^2}{R^2}\\right] \\xi^{\\hat{i}}\n *       \\delta^i_{\\hat{i}},\n * \\f}\n *\n * where \\f$\\xi^{\\hat{i}}\\f$ are the source coordinates, \\f$a\\f$ and \\f$b\\f$ are\n * functions of time, \\f$\\rho\\f$ is the radius in the source coordinates, and\n * \\f$R\\f$ is the outer boundary.\n *\n * The inverse map is computed by solving the cubic equation:\n *\n * \\f{align}{\n * (b-a)\\frac{\\rho^3}{R^2} + a \\rho - r = 0,\n * \\f}\n *\n * which is done by defining \\f$q=\\rho/R\\f$, and solving\n *\n * \\f{align}{\n * q \\left[(b-a) q^2 + a\\right] - \\frac{r}{R} = 0.\n * \\f}\n *\n * The source coordinates are obtained using:\n *\n * \\f{align}{\n * \\xi^{\\hat{i}} = \\frac{qR}{r} x^i(t) \\delta^{\\hat{i}}_i\n * \\f}\n *\n * The Jacobian is given by:\n *\n * \\f{align}{\n * \\frac{\\partial x^i}{\\partial \\xi^{\\hat{i}}}=\n *    \\left[a + (b-a) \\frac{\\rho^2}{R^2}\\right] \\delta^i_{\\hat{i}}\n *    + \\frac{2 (b-a)}{R^2} \\xi^{\\hat{j}} \\delta^i_{\\hat{j}} \\xi^{\\hat{k}}\n *      \\delta_{\\hat{k}\\hat{i}}\n * \\f}\n *\n * The inverse Jacobian is given by:\n *\n * \\f{align}{\n * \\frac{\\partial \\xi^{\\hat{i}}}{\\partial x^i}=\n * \\frac{1}{\\left[a + (b-a)\\rho^2/R^2\\right]}\n *  \\left[\\delta^{\\hat{i}}_i -\n *        \\frac{2 (b-a)}{\\left[a R^2 + 3(b-a)\\rho^2\\right]}\n *        \\xi^{\\hat{i}}\\xi^{\\hat{j}}\\delta_{\\hat{j}i}\\right]\n * \\f}\n *\n * The mesh velocity \\f$v_g^i\\f$ is given by:\n *\n * \\f{align}{\n * v_g^i = \\left[\\frac{da}{dt} + \\left(\\frac{db}{dt}-\\frac{da}{dt}\\right)\n *         \\frac{\\rho^2}{R^2}\\right] \\xi^{\\hat{i}} \\delta^i_{\\hat{i}}.\n * \\f}\n */\ntemplate <size_t Dim>\nclass CubicScale {\n public:\n  static constexpr size_t dim = Dim;\n\n  explicit CubicScale(double outer_boundary,\n                      std::string function_of_time_name_a,\n                      std::string function_of_time_name_b);\n  CubicScale() = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> operator()(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  /// Returns std::nullopt if the point is outside the range of the map.\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, Dim>> inverse(\n      const std::array<double, Dim>& target_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> frame_velocity(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const CubicScale<LocalDim>& lhs,\n                         const CubicScale<LocalDim>& rhs);\n\n  std::string f_of_t_a_{};\n  std::string f_of_t_b_{};\n  std::unordered_set<std::string> f_of_t_names_;\n  double one_over_outer_boundary_{std::numeric_limits<double>::signaling_NaN()};\n  bool functions_of_time_equal_{false};\n};\n\ntemplate <size_t Dim>\nbool operator!=(const CubicScale<Dim>& lhs, const CubicScale<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace TimeDependent\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <limits>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependentHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\nnamespace TimeDependent {\n/// \\ingroup CoordMapsTimeDependentGroup\n/// \\brief Product of two codimension=0 CoordinateMaps, where one or both must\n/// be time-dependent.\n///\n/// \\tparam Map1 the map for the first coordinate(s)\n/// \\tparam Map2 the map for the second coordinate(s)\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps {\n public:\n  static constexpr size_t dim = Map1::dim + Map2::dim;\n  using map_list = tmpl::list<Map1, Map2>;\n  static_assert(dim == 2 or dim == 3,\n                \"Only 2D and 3D maps are supported by ProductOf2Maps\");\n  static_assert(\n      domain::is_map_time_dependent_v<Map1> or\n          domain::is_map_time_dependent_v<Map2>,\n      \"Either Map1 or Map2 must be time-dependent for time-dependent product \"\n      \"maps. A time-independent product map exists in domain::CoordinateMaps.\");\n\n  // Needed for Charm++ serialization\n  ProductOf2Maps() = default;\n\n  ProductOf2Maps(Map1 map1, Map2 map2);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, dim> operator()(\n      const std::array<T, dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, dim>> inverse(\n      const std::array<double, dim>& target_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, dim> frame_velocity(\n      const std::array<T, dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, dim, Frame::NoFrame> jacobian(\n      const std::array<T, dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const {\n    return map1_.is_identity() and map2_.is_identity();\n  }\n\n  static constexpr bool supports_hessian{Map1::supports_hessian and\n                                          Map2::supports_hessian};\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  friend bool operator==(const ProductOf2Maps& lhs, const ProductOf2Maps& rhs) {\n    return lhs.map1_ == rhs.map1_ and lhs.map2_ == rhs.map2_;\n  }\n\n  Map1 map1_;\n  Map2 map2_;\n  std::unordered_set<std::string> f_of_t_names_;\n};\n\ntemplate <typename Map1, typename Map2>\nbool operator!=(const ProductOf2Maps<Map1, Map2>& lhs,\n                const ProductOf2Maps<Map1, Map2>& rhs);\n\n/// \\ingroup CoordinateMapsGroup\n/// \\brief Product of three one-dimensional CoordinateMaps.\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps {\n public:\n  static constexpr size_t dim = Map1::dim + Map2::dim + Map3::dim;\n  using map_list = tmpl::list<Map1, Map2, Map3>;\n  static_assert(dim == 3, \"Only 3D maps are implemented for ProductOf3Maps\");\n  static_assert(\n      domain::is_map_time_dependent_v<Map1> or\n          domain::is_map_time_dependent_v<Map2> or\n          domain::is_map_time_dependent_v<Map3>,\n      \"Either Map1, Map2, or Map3 must be time-dependent for time-dependent \"\n      \"product maps. A time-independent product map exists in \"\n      \"domain::CoordinateMaps.\");\n\n  // Needed for Charm++ serialization\n  ProductOf3Maps() = default;\n\n  ProductOf3Maps(Map1 map1, Map2 map2, Map3 map3);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, dim> operator()(\n      const std::array<T, dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  std::optional<std::array<double, dim>> inverse(\n      const std::array<double, dim>& target_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, dim> frame_velocity(\n      const std::array<T, dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, dim, Frame::NoFrame> jacobian(\n      const std::array<T, dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const {\n    return map1_.is_identity() and map2_.is_identity() and map3_.is_identity();\n  }\n\n  static constexpr bool supports_hessian{Map1::supports_hessian and\n                                            Map2::supports_hessian and\n                                            Map3::supports_hessian};\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  friend bool operator==(const ProductOf3Maps& lhs, const ProductOf3Maps& rhs) {\n    return lhs.map1_ == rhs.map1_ and lhs.map2_ == rhs.map2_ and\n           lhs.map3_ == rhs.map3_;\n  }\n\n  Map1 map1_;\n  Map2 map2_;\n  Map3 map3_;\n  std::unordered_set<std::string> f_of_t_names_;\n};\n\ntemplate <typename Map1, typename Map2, typename Map3>\nbool operator!=(const ProductOf3Maps<Map1, Map2, Map3>& lhs,\n                const ProductOf3Maps<Map1, Map2, Map3>& rhs);\n}  // namespace TimeDependent\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <pup.h>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMapHelpers.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace domain {\nnamespace CoordinateMaps {\nnamespace TimeDependent {\nnamespace product_detail {\ntemplate <typename T, size_t Size, typename Map1, typename Map2, size_t... Is,\n          size_t... Js>\nstd::array<tt::remove_cvref_wrap_t<T>, Size> apply_map(\n    const std::array<T, Size>& coords, const Map1& map1, const Map2& map2,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    std::integer_sequence<size_t, Is...> /*meta*/,\n    std::integer_sequence<size_t, Js...> /*meta*/) {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  return {\n      {CoordinateMap_detail::apply_map(\n           map1,\n           std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Is)>{\n               {coords[Is]...}},\n           time, functions_of_time,\n           domain::is_map_time_dependent_t<Map1>{})[Is]...,\n       CoordinateMap_detail::apply_map(\n           map2,\n           std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Js)>{\n               {coords[Map1::dim + Js]...}},\n           time, functions_of_time,\n           domain::is_map_time_dependent_t<Map2>{})[Js]...}};\n}\n\ntemplate <size_t Size, typename Map1, typename Map2, size_t... Is, size_t... Js>\nstd::optional<std::array<double, Size>> apply_inverse(\n    const std::array<double, Size>& coords, const Map1& map1, const Map2& map2,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    std::integer_sequence<size_t, Is...> /*meta*/,\n    std::integer_sequence<size_t, Js...> /*meta*/) {\n  auto map1_func = CoordinateMap_detail::apply_inverse_map(\n      map1, std::array<double, sizeof...(Is)>{{coords[Is]...}}, time,\n      functions_of_time, domain::is_map_time_dependent_t<Map1>{});\n  auto map2_func = CoordinateMap_detail::apply_inverse_map(\n      map2, std::array<double, sizeof...(Js)>{{coords[Map1::dim + Js]...}},\n      time, functions_of_time, domain::is_map_time_dependent_t<Map2>{});\n  if (map1_func.has_value() and map2_func.has_value()) {\n    return {{{map1_func.value()[Is]..., map2_func.value()[Js]...}}};\n  } else {\n    return std::nullopt;\n  }\n}\n\ntemplate <typename T, size_t Size, typename Map1, typename Map2, size_t... Is,\n          size_t... Js>\nstd::array<tt::remove_cvref_wrap_t<T>, Size> apply_frame_velocity(\n    const std::array<T, Size>& coords, const Map1& map1, const Map2& map2,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    std::integer_sequence<size_t, Is...> /*meta*/,\n    std::integer_sequence<size_t, Js...> /*meta*/) {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  return {\n      {domain::CoordinateMap_detail::apply_frame_velocity(\n           map1,\n           std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Is)>{\n               {coords[Is]...}},\n           time, functions_of_time,\n           domain::is_map_time_dependent_t<Map1>{})[Is]...,\n       domain::CoordinateMap_detail::apply_frame_velocity(\n           map2,\n           std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Js)>{\n               {coords[Map1::dim + Js]...}},\n           time, functions_of_time,\n           domain::is_map_time_dependent_t<Map2>{})[Js]...}};\n}\n\ntemplate <typename T, size_t Size, typename Map1, typename Map2,\n          typename Function, size_t... Is, size_t... Js>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Size, Frame::NoFrame> apply_jac(\n    const std::array<T, Size>& source_coords, const Map1& map1,\n    const Map2& map2, const Function func,\n    std::integer_sequence<size_t, Is...> /*meta*/,\n    std::integer_sequence<size_t, Js...> /*meta*/) {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  auto map1_jac = func(\n      std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Is)>{\n          {source_coords[Is]...}},\n      map1);\n  auto map2_jac = func(\n      std::array<std::reference_wrapper<const UnwrappedT>, sizeof...(Js)>{\n          {source_coords[Map1::dim + Js]...}},\n      map2);\n  tnsr::Ij<UnwrappedT, Size, Frame::NoFrame> jac{\n      make_with_value<UnwrappedT>(dereference_wrapper(source_coords[0]), 0.0)};\n  for (size_t i = 0; i < Map1::dim; ++i) {\n    for (size_t j = 0; j < Map1::dim; ++j) {\n      jac.get(i, j) = std::move(map1_jac.get(i, j));\n    }\n  }\n  for (size_t i = 0; i < Map2::dim; ++i) {\n    for (size_t j = 0; j < Map2::dim; ++j) {\n      jac.get(Map1::dim + i, Map1::dim + j) = std::move(map2_jac.get(i, j));\n    }\n  }\n  return jac;\n}\n\nCREATE_IS_CALLABLE(function_of_time_names)\nCREATE_IS_CALLABLE_V(function_of_time_names)\n\ntemplate <typename... Maps>\nstd::unordered_set<std::string> initialize_names(const Maps&... maps) {\n  std::unordered_set<std::string> names{};\n\n  const auto add_name = [&names](const auto& map) {\n    using Map = std::decay_t<decltype(map)>;\n    if constexpr (is_function_of_time_names_callable_v<Map>) {\n      const auto& map_names = map.function_of_time_names();\n      names.insert(map_names.begin(), map_names.end());\n    }\n  };\n\n  EXPAND_PACK_LEFT_TO_RIGHT(add_name(maps));\n\n  return names;\n}\n}  // namespace product_detail\n\ntemplate <typename Map1, typename Map2>\nProductOf2Maps<Map1, Map2>::ProductOf2Maps(Map1 map1, Map2 map2)\n    : map1_(std::move(map1)),\n      map2_(std::move(map2)),\n      f_of_t_names_(product_detail::initialize_names(map1_, map2_)) {}\n\ntemplate <typename Map1, typename Map2>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, ProductOf2Maps<Map1, Map2>::dim>\nProductOf2Maps<Map1, Map2>::operator()(\n    const std::array<T, dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  return product_detail::apply_map(source_coords, map1_, map2_, time,\n                                   functions_of_time,\n                                   std::make_index_sequence<Map1::dim>{},\n                                   std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\nstd::optional<std::array<double, ProductOf2Maps<Map1, Map2>::dim>>\nProductOf2Maps<Map1, Map2>::inverse(\n    const std::array<double, dim>& target_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  return product_detail::apply_inverse(target_coords, map1_, map2_, time,\n                                       functions_of_time,\n                                       std::make_index_sequence<Map1::dim>{},\n                                       std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\ntemplate <typename T>\nauto ProductOf2Maps<Map1, Map2>::frame_velocity(\n    const std::array<T, dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const\n    -> std::array<tt::remove_cvref_wrap_t<T>, dim> {\n  return product_detail::apply_frame_velocity(\n      source_coords, map1_, map2_, time, functions_of_time,\n      std::make_index_sequence<Map1::dim>{},\n      std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, ProductOf2Maps<Map1, Map2>::dim,\n         Frame::NoFrame>\nProductOf2Maps<Map1, Map2>::inv_jacobian(\n    const std::array<T, dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  return product_detail::apply_jac(\n      source_coords, map1_, map2_,\n      [&time, &functions_of_time](const auto& point, const auto& map) {\n        return CoordinateMap_detail::apply_inverse_jacobian(\n            map, point, time, functions_of_time,\n            domain::is_jacobian_time_dependent_t<\n                std::decay_t<decltype(map)>,\n                std::reference_wrapper<const UnwrappedT>>{});\n      },\n      std::make_index_sequence<Map1::dim>{},\n      std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, ProductOf2Maps<Map1, Map2>::dim,\n         Frame::NoFrame>\nProductOf2Maps<Map1, Map2>::jacobian(\n    const std::array<T, dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  return product_detail::apply_jac(\n      source_coords, map1_, map2_,\n      [&time, &functions_of_time](const auto& point, const auto& map) {\n        return CoordinateMap_detail::apply_jacobian(\n            map, point, time, functions_of_time,\n            domain::is_jacobian_time_dependent_t<\n                std::decay_t<decltype(map)>,\n                std::reference_wrapper<const UnwrappedT>>{});\n      },\n      std::make_index_sequence<Map1::dim>{},\n      std::make_index_sequence<Map2::dim>{});\n}\n\ntemplate <typename Map1, typename Map2>\nvoid ProductOf2Maps<Map1, Map2>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | map1_;\n    p | map2_;\n  }\n\n  // No need to pup this because it is uniquely determined by the maps\n  if (p.isUnpacking()) {\n    f_of_t_names_ = product_detail::initialize_names(map1_, map2_);\n  }\n}\n\ntemplate <typename Map1, typename Map2>\nbool operator!=(const ProductOf2Maps<Map1, Map2>& lhs,\n                const ProductOf2Maps<Map1, Map2>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\nProductOf3Maps<Map1, Map2, Map3>::ProductOf3Maps(Map1 map1, Map2 map2,\n                                                 Map3 map3)\n    : map1_(std::move(map1)),\n      map2_(std::move(map2)),\n      map3_(std::move(map3)),\n      f_of_t_names_(product_detail::initialize_names(map1_, map2_, map3_)) {}\n\ntemplate <typename Map1, typename Map2, typename Map3>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, ProductOf3Maps<Map1, Map2, Map3>::dim>\nProductOf3Maps<Map1, Map2, Map3>::operator()(\n    const std::array<T, dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  return {\n      {CoordinateMap_detail::apply_map(\n           map1_,\n           std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[0]}},\n           time, functions_of_time, domain::is_map_time_dependent_t<Map1>{})[0],\n       CoordinateMap_detail::apply_map(\n           map2_,\n           std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[1]}},\n           time, functions_of_time, domain::is_map_time_dependent_t<Map2>{})[0],\n       CoordinateMap_detail::apply_map(\n           map3_,\n           std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[2]}},\n           time, functions_of_time,\n           domain::is_map_time_dependent_t<Map3>{})[0]}};\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\nstd::optional<std::array<double, ProductOf3Maps<Map1, Map2, Map3>::dim>>\nProductOf3Maps<Map1, Map2, Map3>::inverse(\n    const std::array<double, dim>& target_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const auto c1 = CoordinateMap_detail::apply_inverse_map(\n      map1_, std::array<double, 1>{{target_coords[0]}}, time, functions_of_time,\n      domain::is_map_time_dependent_t<Map1>{});\n  const auto c2 = CoordinateMap_detail::apply_inverse_map(\n      map2_, std::array<double, 1>{{target_coords[1]}}, time, functions_of_time,\n      domain::is_map_time_dependent_t<Map2>{});\n  const auto c3 = CoordinateMap_detail::apply_inverse_map(\n      map3_, std::array<double, 1>{{target_coords[2]}}, time, functions_of_time,\n      domain::is_map_time_dependent_t<Map3>{});\n  if (c1.has_value() and c2.has_value() and c3.has_value()) {\n    return {{{c1.value()[0], c2.value()[0], c3.value()[0]}}};\n  } else {\n    return std::nullopt;\n  }\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\ntemplate <typename T>\nauto ProductOf3Maps<Map1, Map2, Map3>::frame_velocity(\n    const std::array<T, dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const\n    -> std::array<tt::remove_cvref_wrap_t<T>, dim> {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  return {\n      {CoordinateMap_detail::apply_frame_velocity(\n           map1_,\n           std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[0]}},\n           time, functions_of_time, domain::is_map_time_dependent_t<Map1>{})[0],\n       CoordinateMap_detail::apply_frame_velocity(\n           map2_,\n           std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[1]}},\n           time, functions_of_time, domain::is_map_time_dependent_t<Map2>{})[0],\n       CoordinateMap_detail::apply_frame_velocity(\n           map3_,\n           std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n               {source_coords[2]}},\n           time, functions_of_time,\n           domain::is_map_time_dependent_t<Map3>{})[0]}};\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, ProductOf3Maps<Map1, Map2, Map3>::dim,\n         Frame::NoFrame>\nProductOf3Maps<Map1, Map2, Map3>::inv_jacobian(\n    const std::array<T, dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  tnsr::Ij<UnwrappedT, dim, Frame::NoFrame> inv_jacobian_matrix{\n      make_with_value<UnwrappedT>(dereference_wrapper(source_coords[0]), 0.0)};\n  get<0, 0>(inv_jacobian_matrix) =\n      get<0, 0>(CoordinateMap_detail::apply_inverse_jacobian(\n          map1_,\n          std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n              {source_coords[0]}},\n          time, functions_of_time,\n          domain::is_jacobian_time_dependent_t<\n              Map1, std::reference_wrapper<const UnwrappedT>>{}));\n  get<1, 1>(inv_jacobian_matrix) =\n      get<0, 0>(CoordinateMap_detail::apply_inverse_jacobian(\n          map2_,\n          std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n              {source_coords[1]}},\n          time, functions_of_time,\n          domain::is_jacobian_time_dependent_t<\n              Map2, std::reference_wrapper<const UnwrappedT>>{}));\n  get<2, 2>(inv_jacobian_matrix) =\n      get<0, 0>(CoordinateMap_detail::apply_inverse_jacobian(\n          map3_,\n          std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n              {source_coords[2]}},\n          time, functions_of_time,\n          domain::is_jacobian_time_dependent_t<\n              Map3, std::reference_wrapper<const UnwrappedT>>{}));\n  return inv_jacobian_matrix;\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, ProductOf3Maps<Map1, Map2, Map3>::dim,\n         Frame::NoFrame>\nProductOf3Maps<Map1, Map2, Map3>::jacobian(\n    const std::array<T, dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  using UnwrappedT = tt::remove_cvref_wrap_t<T>;\n  tnsr::Ij<UnwrappedT, dim, Frame::NoFrame> jacobian_matrix{\n      make_with_value<UnwrappedT>(dereference_wrapper(source_coords[0]), 0.0)};\n  get<0, 0>(jacobian_matrix) = get<0, 0>(CoordinateMap_detail::apply_jacobian(\n      map1_,\n      std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[0]}},\n      time, functions_of_time,\n      domain::is_jacobian_time_dependent_t<\n          Map1, std::reference_wrapper<const UnwrappedT>>{}));\n  get<1, 1>(jacobian_matrix) = get<0, 0>(CoordinateMap_detail::apply_jacobian(\n      map2_,\n      std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[1]}},\n      time, functions_of_time,\n      domain::is_jacobian_time_dependent_t<\n          Map2, std::reference_wrapper<const UnwrappedT>>{}\n\n      ));\n  get<2, 2>(jacobian_matrix) = get<0, 0>(CoordinateMap_detail::apply_jacobian(\n      map3_,\n      std::array<std::reference_wrapper<const UnwrappedT>, 1>{\n          {source_coords[2]}},\n      time, functions_of_time,\n      domain::is_jacobian_time_dependent_t<\n          Map3, std::reference_wrapper<const UnwrappedT>>{}));\n  return jacobian_matrix;\n}\ntemplate <typename Map1, typename Map2, typename Map3>\nvoid ProductOf3Maps<Map1, Map2, Map3>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | map1_;\n    p | map2_;\n    p | map3_;\n  }\n\n  // No need to pup this because it is uniquely determined by the maps\n  if (p.isUnpacking()) {\n    f_of_t_names_ = product_detail::initialize_names(map1_, map2_, map3_);\n  }\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\nbool operator!=(const ProductOf3Maps<Map1, Map2, Map3>& lhs,\n                const ProductOf3Maps<Map1, Map2, Map3>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace TimeDependent\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/RotScaleTrans.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/RotScaleTrans.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace domain::CoordinateMaps::TimeDependent {\n\ntemplate <size_t Dim>\nRotScaleTrans<Dim>::RotScaleTrans(\n    std::optional<std::pair<std::string, std::string>> scale_f_of_t_names,\n    std::optional<std::string> rot_f_of_t_name,\n    std::optional<std::string> trans_f_of_t_name, double inner_radius,\n    double outer_radius, BlockRegion region)\n    : inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      region_(region) {\n  if (scale_f_of_t_names.has_value()) {\n    scale_f_of_t_a_ = std::move(scale_f_of_t_names.value().first);\n    scale_f_of_t_b_ = std::move(scale_f_of_t_names.value().second);\n    f_of_t_names_.insert(scale_f_of_t_a_.value());\n    f_of_t_names_.insert(scale_f_of_t_b_.value());\n  }\n  if (rot_f_of_t_name.has_value()) {\n    rot_f_of_t_ = std::move(rot_f_of_t_name.value());\n    f_of_t_names_.insert(rot_f_of_t_.value());\n  }\n  if (trans_f_of_t_name.has_value()) {\n    trans_f_of_t_ = std::move(trans_f_of_t_name.value());\n    f_of_t_names_.insert(trans_f_of_t_.value());\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> RotScaleTrans<Dim>::operator()(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = gsl::at(source_coords, i);\n  }\n  const tt::remove_cvref_wrap_t<T> radius = magnitude(result);\n  // Rotation Map\n  if (rot_f_of_t_.has_value()) {\n    const Matrix rot_matrix = rotation_matrix<Dim>(\n        time, *(functions_of_time.at(rot_f_of_t_.value())));\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(result, i) = rot_matrix(i, 0) * source_coords[0];\n      for (size_t j = 1; j < Dim; j++) {\n        gsl::at(result, i) += rot_matrix(i, j) * gsl::at(source_coords, j);\n      }\n    }\n  }\n  // Expansion Map\n  if (scale_f_of_t_a_.has_value()) {\n    const double scale_a_of_t =\n        functions_of_time.at(scale_f_of_t_a_.value())->func(time)[0][0];\n    const double scale_b_of_t =\n        functions_of_time.at(scale_f_of_t_b_.value())->func(time)[0][0];\n    if (region_ == BlockRegion::Inner) {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(result, i) *= scale_a_of_t;\n      }\n    } else if (region_ == BlockRegion::Transition) {\n      for (size_t k = 0; k < get_size(radius); k++) {\n        // Optimization from SpEC to reduce roundoff.\n        // Closer to outer radius\n        if (1.0 - get_element(radius, k) / (inner_radius_ + outer_radius_) <\n            .5) {\n          // Expansion falloff factor w_E in the documentation of the form\n          // w_E = \\frac{R_{in}(R_{out} - r)(E_{a}(t) - E_{b}(t))}{r(R_{out} -\n          // R_{in})}\n          double radial_scaling_factor =\n              ((outer_radius_ - get_element(radius, k)) *\n               (scale_a_of_t - scale_b_of_t) * inner_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            get_element(gsl::at(result, i), k) *=\n                (scale_b_of_t + radial_scaling_factor);\n          }\n          // Closer to inner radius\n        } else {\n          // Expansion falloff factor w_E in the documentation of the form\n          // w_E = \\frac{R_{out}(R_{in} - r)(E_{a}(t) - E_{b}(t))}{r(R_{out} -\n          // R_{in})}\n          double radial_scaling_factor =\n              ((inner_radius_ - get_element(radius, k)) *\n               (scale_a_of_t - scale_b_of_t) * outer_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            get_element(gsl::at(result, i), k) *=\n                (scale_a_of_t + radial_scaling_factor);\n          }\n        }\n      }\n    } else {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(result, i) *= scale_b_of_t;\n      }\n    }\n  }\n  // Translation map\n  if (trans_f_of_t_.has_value()) {\n    const DataVector trans_func_of_time =\n        functions_of_time.at(trans_f_of_t_.value())->func(time)[0];\n    ASSERT(trans_func_of_time.size() == Dim,\n           \"The dimension of the function of time (\"\n               << trans_func_of_time.size()\n               << \") does not match the dimension of the map (\" << Dim << \").\");\n    if (region_ == BlockRegion::Inner) {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(result, i) += gsl::at(trans_func_of_time, i);\n      }\n    } else if (region_ == BlockRegion::Transition) {\n      for (size_t k = 0; k < get_size(radius); k++) {\n        // closer to outer radius\n        if (1.0 - get_element(radius, k) / (inner_radius_ + outer_radius_) <\n            .5) {\n          // Translation falloff factor w_T in the documentation of the\n          // form w_T = (R_{out} - r) / (R_{out} - R_{in})\n          const double radial_translation_factor =\n              (outer_radius_ - get_element(radius, k)) /\n              (outer_radius_ - inner_radius_);\n          for (size_t i = 0; i < Dim; i++) {\n            get_element(gsl::at(result, i), k) +=\n                gsl::at(trans_func_of_time, i) * radial_translation_factor;\n          }\n        } else {\n          // Translation falloff factor w_T in the documentation of the\n          // form w_T = (R_{in} - r) / (R_{out} - R_{in})\n          const double radial_translation_factor =\n              (inner_radius_ - get_element(radius, k)) /\n                  (outer_radius_ - inner_radius_) +\n              1.0;\n          for (size_t i = 0; i < Dim; i++) {\n            get_element(gsl::at(result, i), k) +=\n                gsl::at(trans_func_of_time, i) * radial_translation_factor;\n          }\n        }\n      }\n      ASSERT(max(magnitude(result)) <= outer_radius_ or\n                 equal_within_roundoff(max(magnitude(result)), outer_radius_),\n             \"Coordinates translated outside outer radius, this should \"\n             \"not happen: \"\n                 << max(magnitude(result))\n                 << \" outer radius: \" << outer_radius_);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::array<double, Dim>> RotScaleTrans<Dim>::inverse(\n    const std::array<double, Dim>& target_coords, double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  std::array<double, Dim> result{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = gsl::at(target_coords, i);\n  }\n  const double radius = magnitude(result);\n  // This variable is used when taking the inverse of rotation which needs the\n  // source coords after the inverse expansion and translation have been done.\n  std::array<double, Dim> pre_rotation_result = result;\n\n  // Inverse translation without expansion\n  if (trans_f_of_t_.has_value() and not scale_f_of_t_a_.has_value()) {\n    const DataVector trans_func_of_time =\n        functions_of_time.at(trans_f_of_t_.value())->func(time)[0];\n    ASSERT(trans_func_of_time.size() == Dim,\n           \"The dimension of the function of time (\"\n               << trans_func_of_time.size()\n               << \") does not match the dimension of the map (\" << Dim << \").\");\n      double non_translated_radius_squared = 0.;\n      for (size_t i = 0; i < Dim; i++) {\n        non_translated_radius_squared +=\n            square(gsl::at(target_coords, i) - gsl::at(trans_func_of_time, i));\n      }\n      if (radius >= outer_radius_) {\n        // no translation applied\n      } else if (non_translated_radius_squared <= square(inner_radius_)) {\n        for (size_t i = 0; i < Dim; i++) {\n          gsl::at(pre_rotation_result, i) -= gsl::at(trans_func_of_time, i);\n        }\n      } else {\n        // We need to solve a quadratic of the form w^2a + wb + c where\n        // a, b, and c change if you're closer to the inner radius. This\n        // is an optimization to reduce roundoff from SpEC.\n        double a = 0.;\n        double b = 0.;\n        double c = 0.;\n        double radial_translation_factor = 0.;\n        for (size_t i = 0; i < Dim; i++) {\n          a += square(gsl::at(trans_func_of_time, i));\n        }\n        a -= square(outer_radius_ - inner_radius_);\n        // When closer to the outer radius the quadratic has the form\n        // a = T(t)^2 - (R_{out} - R_{in})^2,\n        // b = 2(R_{out}(R_{out} - R_{in}) - T(t)\\vec{\\bar{\\xi}}), and\n        // c = \\vec{\\bar{\\xi}}^2 - R_{out}^2\n        if (1.0 - radius / (inner_radius_ + outer_radius_) < .5) {\n          c = square(radius) - square(outer_radius_);\n          for (size_t i = 0; i < Dim; i++) {\n            b -= 2.0 * gsl::at(trans_func_of_time, i) *\n                 gsl::at(target_coords, i);\n          }\n          b += 2.0 * outer_radius_ * (outer_radius_ - inner_radius_);\n          std::optional<std::array<double, 2>> roots = real_roots(a, b, c);\n          radial_translation_factor = root_helper(roots);\n          for (size_t i = 0; i < Dim; i++) {\n            gsl::at(pre_rotation_result, i) -=\n                gsl::at(trans_func_of_time, i) * radial_translation_factor;\n          }\n        }\n        // When closer to the inner radius, the quadratic has the form\n        // a = T(t)^2 - (R_{out} - R_{in})^2,\n        // b = -2 (T(t)(T(t) - \\vec{\\bar{\\xi}}) + R_{in}(R_{out} - R_{in}),\n        // c = (T(t) - \\vec{\\bar{\\xi}})^2 - R_{in}^2\n        else {\n          c = -square(inner_radius_);\n          for (size_t i = 0; i < Dim; i++) {\n            b -= 2.0 * gsl::at(trans_func_of_time, i) *\n                 (gsl::at(trans_func_of_time, i) - gsl::at(target_coords, i));\n            c += square(gsl::at(trans_func_of_time, i) -\n                        gsl::at(target_coords, i));\n          }\n          b -= 2.0 * inner_radius_ * (outer_radius_ - inner_radius_);\n          std::optional<std::array<double, 2>> roots = real_roots(a, b, c);\n          radial_translation_factor = root_helper(roots);\n          for (size_t i = 0; i < Dim; i++) {\n            gsl::at(pre_rotation_result, i) -=\n                gsl::at(trans_func_of_time, i) *\n                (1.0 - radial_translation_factor);\n          }\n        }\n      }\n  }\n  // Inverse expansion without translation\n  else if (scale_f_of_t_a_.has_value() and not trans_f_of_t_.has_value()) {\n    const double scale_a_of_t =\n        functions_of_time.at(scale_f_of_t_a_.value())->func(time)[0][0];\n    const double scale_b_of_t =\n        functions_of_time.at(scale_f_of_t_b_.value())->func(time)[0][0];\n    ASSERT(scale_a_of_t != 0.0 and scale_b_of_t != 0.0,\n           \"An expansion map \"\n           \"value was set to 0.0, this will cause an FPE. Expansion a: \"\n               << scale_a_of_t << \" expansion b: \" << scale_b_of_t);\n    if (radius >= scale_b_of_t * outer_radius_) {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(pre_rotation_result, i) /= scale_b_of_t;\n      }\n    } else if (radius <= scale_a_of_t * inner_radius_) {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(pre_rotation_result, i) /= scale_a_of_t;\n      }\n    } else {\n      // We need to solve a quadratic of the form w^2a + wb + c where\n      // a, b, and c change if you're closer to the inner radius. This\n      // is an optimization to reduce roundoff from SpEC.\n      double a =\n          square(scale_a_of_t * inner_radius_ - scale_b_of_t * outer_radius_);\n      double b = 0.;\n      double c = 0.;\n      double radial_scaling_factor = 0.;\n      double scaled_radius = 0.;\n      double root = 0.;\n      double radius_squared = square(radius);\n      // When closer to the inner radius the quadratic has the form\n      // a = (E_{a}(t)R_{in} - E_{b}(t)R_{out})^2,\n      // b = 2E{b}(t)R_{out}(E_{a}(t)R_{in} - E_{b}(t)R_{out}),\n      // c = E_{b}(t)^2 R_{out}^2 - \\bar{\\xi}^2\n      if (1.0 - radius / (inner_radius_ * scale_a_of_t +\n                          outer_radius_ * scale_b_of_t) <\n          .5) {\n        scaled_radius = scale_b_of_t * outer_radius_;\n        b = 2.0 * scaled_radius *\n            (scale_a_of_t * inner_radius_ - scaled_radius);\n        c = square(scale_b_of_t * outer_radius_) - radius_squared;\n        std::optional<std::array<double, 2>> roots = real_roots(a, b, c);\n        root = root_helper(roots);\n        radial_scaling_factor =\n            (root * inner_radius_ * scale_a_of_t +\n             (1.0 - root) * outer_radius_ * scale_b_of_t) /\n            (root * inner_radius_ + (1.0 - root) * outer_radius_);\n      }\n      // When closer to the inner radius the quadratic has the form\n      // a = (E_{a}(t)R_{in} - E_{b}(t)R_{out})^2,\n      // b = 2E{a}(t)R_{in}(E_{b}(t)R_{out} - E_{a}(t)R_{in}),\n      // c = E_{a}(t)^2 R_{in}^2 - \\bar{\\xi}^2\n      else {\n        scaled_radius = scale_b_of_t * outer_radius_;\n        b = 2.0 * scale_a_of_t * inner_radius_ *\n            (scaled_radius - scale_a_of_t * inner_radius_);\n        c = square(scale_a_of_t * inner_radius_) - radius_squared;\n        std::optional<std::array<double, 2>> roots = real_roots(a, b, c);\n        root = root_helper(roots);\n        radial_scaling_factor =\n            ((1.0 - root) * inner_radius_ * scale_a_of_t +\n             root * outer_radius_ * scale_b_of_t) /\n            ((1.0 - root) * inner_radius_ + root * outer_radius_);\n      }\n      pre_rotation_result /= radial_scaling_factor;\n    }\n  }\n\n  // Inverse expansion and translation\n  else if (trans_f_of_t_.has_value() and scale_f_of_t_a_.has_value()) {\n    const DataVector trans_func_of_time =\n        functions_of_time.at(trans_f_of_t_.value())->func(time)[0];\n    ASSERT(trans_func_of_time.size() == Dim,\n           \"The dimension of the function of time (\"\n               << trans_func_of_time.size()\n               << \") does not match the dimension of the map (\" << Dim << \").\");\n    const double scale_a_of_t =\n        functions_of_time.at(scale_f_of_t_a_.value())->func(time)[0][0];\n    const double scale_b_of_t =\n        functions_of_time.at(scale_f_of_t_b_.value())->func(time)[0][0];\n    ASSERT(scale_a_of_t != 0.0 and scale_b_of_t != 0.0,\n           \"An expansion map \"\n           \"value was set to 0.0, this will cause an FPE. Expansion a: \"\n               << scale_a_of_t << \" expansion b: \" << scale_b_of_t);\n      double non_translated_radius_squared = 0.;\n      for (size_t i = 0; i < Dim; i++) {\n        non_translated_radius_squared +=\n            square(gsl::at(target_coords, i) - gsl::at(trans_func_of_time, i));\n      }\n      if (radius >= scale_b_of_t * outer_radius_) {\n        pre_rotation_result /= scale_b_of_t;\n      } else if (non_translated_radius_squared <=\n                 square(scale_a_of_t * inner_radius_)) {\n        for (size_t i = 0; i < Dim; i++) {\n          gsl::at(pre_rotation_result, i) -= gsl::at(trans_func_of_time, i);\n        }\n        pre_rotation_result /= scale_a_of_t;\n      } else {\n        // We need to solve a quadratic of the form w^2a + wb + c where\n        // a, b, and c change if you're closer to the inner radius. This\n        // is an optimization to reduce roundoff from SpEC.\n        double a =\n            square(scale_a_of_t * inner_radius_ - scale_b_of_t * outer_radius_);\n        for (size_t i = 0; i < Dim; i++) {\n          a -= square(gsl::at(trans_func_of_time, i));\n        }\n        double b = 0.;\n        double c = 0.;\n        double radial_translation_factor = 0.;\n        double radial_scaling_factor = 0.;\n        // When closer to the outer radius, the quadratic has the form\n        // a = (E_{a}(t)R_{in} - E_{b}(t)R_{out})^2 - T(t)^2,\n        // b = 2(E_{b}(t)R_{out}(E_{a}(t)R_{in} - E_{b}(t)R_{out}) +\n        // T(t)\\vec{\\bar{\\xi}}), and\n        // c = E_{b}(t)^2 R_{out}^2 - \\vec{\\bar{\\xi}}^2\n        if (1.0 - radius / (scale_a_of_t * inner_radius_ +\n                            scale_b_of_t * outer_radius_) <\n            .5) {\n          b = 2.0 *\n              (scale_b_of_t * outer_radius_ *\n               (scale_a_of_t * inner_radius_ - scale_b_of_t * outer_radius_));\n          for (size_t i = 0; i < Dim; i++) {\n            b += 2.0 * gsl::at(trans_func_of_time, i) *\n                 gsl::at(target_coords, i);\n          }\n          c = square(scale_b_of_t * outer_radius_) - square(radius);\n          std::optional<std::array<double, 2>> roots = real_roots(a, b, c);\n          radial_translation_factor = root_helper(roots);\n          for (size_t i = 0; i < Dim; i++) {\n            gsl::at(pre_rotation_result, i) -=\n                gsl::at(trans_func_of_time, i) * radial_translation_factor;\n          }\n          radial_scaling_factor =\n              (radial_translation_factor * inner_radius_ * scale_a_of_t +\n               (1.0 - radial_translation_factor) * outer_radius_ *\n                   scale_b_of_t) /\n              (radial_translation_factor * inner_radius_ +\n               (1.0 - radial_translation_factor) * outer_radius_);\n        }\n        // When closer to the inner radius, the quadratic has the form\n        // a = (E_{a}(t)R_{in} - E_{b}(t)R_{out})^2 - T(t)^2,\n        // b = 2(E_{a}(t)R_{in}(E_{b}(t)R_{out} - E_{a}(t)R_{in}) +\n        // T(t)(T(t) - \\vec{\\bar{\\xi}})), and\n        // c = E_{a}(t)^2 R_{in}^2 - (\\vec{\\bar{\\xi}} - T(t))^2\n        else {\n          b = 2.0 *\n              (scale_a_of_t * inner_radius_ *\n               (scale_b_of_t * outer_radius_ - scale_a_of_t * inner_radius_));\n          c = square(scale_a_of_t * inner_radius_) -\n              non_translated_radius_squared;\n          for (size_t i = 0; i < Dim; i++) {\n            b += 2.0 * gsl::at(trans_func_of_time, i) *\n                 (gsl::at(trans_func_of_time, i) - gsl::at(target_coords, i));\n          }\n          std::optional<std::array<double, 2>> roots = real_roots(a, b, c);\n          radial_translation_factor = root_helper(roots);\n          for (size_t i = 0; i < Dim; i++) {\n            gsl::at(pre_rotation_result, i) -=\n                gsl::at(trans_func_of_time, i) *\n                (1.0 - radial_translation_factor);\n          }\n          radial_scaling_factor =\n              ((1.0 - radial_translation_factor) * inner_radius_ *\n                   scale_a_of_t +\n               radial_translation_factor * outer_radius_ * scale_b_of_t) /\n              ((1.0 - radial_translation_factor) * inner_radius_ +\n               radial_translation_factor * outer_radius_);\n        }\n        pre_rotation_result /= radial_scaling_factor;\n      }\n  }\n\n  // Inverse rotation is the transpose of the original rotation matrix.\n  if (rot_f_of_t_.has_value()) {\n    const Matrix rot_matrix = rotation_matrix<Dim>(\n        time, *(functions_of_time.at(rot_f_of_t_.value())));\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(result, i) = rot_matrix(0, i) * gsl::at(pre_rotation_result, 0);\n      for (size_t j = 1; j < Dim; j++) {\n        gsl::at(result, i) +=\n            rot_matrix(j, i) * gsl::at(pre_rotation_result, j);\n      }\n    }\n    return result;\n  } else {\n    return pre_rotation_result;\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> RotScaleTrans<Dim>::frame_velocity(\n    const std::array<T, Dim>& source_coords, double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> result =\n      make_with_value<std::array<tt::remove_cvref_wrap_t<T>, Dim>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n  tt::remove_cvref_wrap_t<T> radius =\n      square(dereference_wrapper(source_coords[0]));\n  for (size_t i = 1; i < Dim; ++i) {\n    radius += square(dereference_wrapper(gsl::at(source_coords, i)));\n  }\n  radius = sqrt(radius);\n  // Rotation map with no expansion\n  if (rot_f_of_t_.has_value() and not scale_f_of_t_a_.has_value()) {\n    const Matrix rot_matrix_deriv = rotation_matrix_deriv<Dim>(\n        time, *(functions_of_time.at(rot_f_of_t_.value())));\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t j = 0; j < Dim; j++) {\n        gsl::at(result, i) +=\n            rot_matrix_deriv(i, j) * gsl::at(source_coords, j);\n      }\n    }\n  }\n  // Expansion map with no rotation\n  else if (scale_f_of_t_a_.has_value() and not rot_f_of_t_.has_value()) {\n    const double dt_a_of_t = functions_of_time.at(scale_f_of_t_a_.value())\n                                 ->func_and_deriv(time)[1][0];\n    const double dt_b_of_t = functions_of_time.at(scale_f_of_t_b_.value())\n                                 ->func_and_deriv(time)[1][0];\n    if (region_ == BlockRegion::Inner) {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(result, i) +=\n            dereference_wrapper(gsl::at(source_coords, i)) * (dt_a_of_t);\n      }\n    } else if (region_ == BlockRegion::Transition) {\n      for (size_t k = 0; k < get_size(radius); k++) {\n        double deriv_radial_scaling_factor = 0;\n        // Optimization from SpEC to reduce roundoff.\n        // Closer to outer radius\n        if (1.0 - get_element(radius, k) / (inner_radius_ + outer_radius_) <\n            .5) {\n          deriv_radial_scaling_factor =\n              ((outer_radius_ - get_element(radius, k)) *\n               (dt_a_of_t - dt_b_of_t) * inner_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            get_element(gsl::at(result, i), k) +=\n                get_element(dereference_wrapper(gsl::at(source_coords, i)), k) *\n                (dt_b_of_t + deriv_radial_scaling_factor);\n          }\n          // Closer to inner radius\n        } else {\n          deriv_radial_scaling_factor =\n              ((inner_radius_ - get_element(radius, k)) *\n               (dt_a_of_t - dt_b_of_t) * outer_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            get_element(gsl::at(result, i), k) +=\n                get_element(dereference_wrapper(gsl::at(source_coords, i)), k) *\n                (dt_a_of_t + deriv_radial_scaling_factor);\n          }\n        }\n      }\n    } else {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(result, i) +=\n            dereference_wrapper(gsl::at(source_coords, i)) * (dt_b_of_t);\n      }\n    }\n  }\n  // Rotation and expansion map\n  else if (scale_f_of_t_a_.has_value() and rot_f_of_t_.has_value()) {\n    const Matrix rot_matrix = rotation_matrix<Dim>(\n        time, *(functions_of_time.at(rot_f_of_t_.value())));\n    const Matrix rot_matrix_deriv = rotation_matrix_deriv<Dim>(\n        time, *(functions_of_time.at(rot_f_of_t_.value())));\n    std::array<DataVector, 2> scale_a_func_and_deriv =\n        functions_of_time.at(scale_f_of_t_a_.value())->func_and_deriv(time);\n    std::array<DataVector, 2> scale_b_func_and_deriv =\n        functions_of_time.at(scale_f_of_t_b_.value())->func_and_deriv(time);\n    const double scale_a_of_t = scale_a_func_and_deriv[0][0];\n    const double scale_b_of_t = scale_b_func_and_deriv[0][0];\n    const double dt_a_of_t = scale_a_func_and_deriv[1][0];\n    const double dt_b_of_t = scale_b_func_and_deriv[1][0];\n    if (region_ == BlockRegion::Inner) {\n      for (size_t i = 0; i < Dim; i++) {\n        for (size_t j = 0; j < Dim; j++) {\n          gsl::at(result, i) += dereference_wrapper(gsl::at(source_coords, j)) *\n                                (scale_a_of_t * rot_matrix_deriv(i, j) +\n                                 dt_a_of_t * rot_matrix(i, j));\n        }\n      }\n    } else if (region_ == BlockRegion::Transition) {\n      for (size_t k = 0; k < get_size(radius); k++) {\n        double radial_scaling_factor = 0;\n        double deriv_radial_scaling_factor = 0;\n        // Optimization from SpEC to reduce roundoff.\n        // Closer to outer radius\n        if (1.0 - get_element(radius, k) / (inner_radius_ + outer_radius_) <\n            .5) {\n          radial_scaling_factor =\n              ((outer_radius_ - get_element(radius, k)) *\n               (scale_a_of_t - scale_b_of_t) * inner_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          deriv_radial_scaling_factor =\n              ((outer_radius_ - get_element(radius, k)) *\n               (dt_a_of_t - dt_b_of_t) * inner_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            for (size_t j = 0; j < Dim; j++) {\n              get_element(gsl::at(result, i), k) +=\n                  get_element(dereference_wrapper(gsl::at(source_coords, j)),\n                              k) *\n                  (rot_matrix_deriv(i, j) *\n                       (scale_b_of_t + radial_scaling_factor) +\n                   rot_matrix(i, j) *\n                       (dt_b_of_t + deriv_radial_scaling_factor));\n            }\n          }\n          // Closer to inner radius\n        } else {\n          radial_scaling_factor =\n              ((inner_radius_ - get_element(radius, k)) *\n               (scale_a_of_t - scale_b_of_t) * outer_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          deriv_radial_scaling_factor =\n              ((inner_radius_ - get_element(radius, k)) *\n               (dt_a_of_t - dt_b_of_t) * outer_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            for (size_t j = 0; j < Dim; j++) {\n              get_element(gsl::at(result, i), k) +=\n                  get_element(dereference_wrapper(gsl::at(source_coords, j)),\n                              k) *\n                  (rot_matrix_deriv(i, j) *\n                       (scale_a_of_t + radial_scaling_factor) +\n                   rot_matrix(i, j) *\n                       (dt_a_of_t + deriv_radial_scaling_factor));\n            }\n          }\n        }\n      }\n    } else {\n      for (size_t i = 0; i < Dim; i++) {\n        for (size_t j = 0; j < Dim; j++) {\n          gsl::at(result, i) += dereference_wrapper(gsl::at(source_coords, j)) *\n                                (scale_b_of_t * rot_matrix_deriv(i, j) +\n                                 dt_b_of_t * rot_matrix(i, j));\n        }\n      }\n    }\n  }\n  // Translation map\n  if (trans_f_of_t_.has_value()) {\n    const DataVector deriv_trans_func_of_time =\n        functions_of_time.at(trans_f_of_t_.value())->func_and_deriv(time)[1];\n    ASSERT(deriv_trans_func_of_time.size() == Dim,\n           \"The dimension of the function of time (\"\n               << deriv_trans_func_of_time.size()\n               << \") does not match the dimension of the map (\" << Dim << \").\");\n    if (region_ == BlockRegion::Inner) {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(result, i) += gsl::at(deriv_trans_func_of_time, i);\n      }\n    } else if (region_ == BlockRegion::Transition) {\n      for (size_t k = 0; k < get_size(radius); k++) {\n        // this is the linear falloff factor w in the documentation of the\n        // form w = (R_{out} - r) / (R_{out} - R_{in})\n        if (1.0 - get_element(radius, k) / (inner_radius_ + outer_radius_) <\n            .5) {\n          const double radial_translation_factor =\n              (outer_radius_ - get_element(radius, k)) /\n              (outer_radius_ - inner_radius_);\n          for (size_t i = 0; i < Dim; i++) {\n            get_element(gsl::at(result, i), k) +=\n                gsl::at(deriv_trans_func_of_time, i) *\n                radial_translation_factor;\n          }\n        } else {\n          const double radial_translation_factor =\n              (inner_radius_ - get_element(radius, k)) /\n                  (outer_radius_ - inner_radius_) +\n              1.0;\n          for (size_t i = 0; i < Dim; i++) {\n            get_element(gsl::at(result, i), k) +=\n                gsl::at(deriv_trans_func_of_time, i) *\n                radial_translation_factor;\n          }\n        }\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nRotScaleTrans<Dim>::jacobian(\n    const std::array<T, Dim>& source_coords, double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  auto result = make_with_value<\n      tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>>(\n      dereference_wrapper(source_coords[0]), 0.0);\n  // Making the identity in case rotation isn't specified.\n  for (size_t i = 0; i < Dim; i++) {\n    result.get(i, i) = 1;\n  }\n  const tt::remove_cvref_wrap_t<T> radius = magnitude(source_coords);\n  // Rotation map with no expansion\n  if (rot_f_of_t_.has_value() and not scale_f_of_t_a_.has_value()) {\n    const Matrix rot_matrix = rotation_matrix<Dim>(\n        time, *(functions_of_time.at(rot_f_of_t_.value())));\n\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t j = 0; j < Dim; j++) {\n        result.get(i, j) = rot_matrix(i, j);\n      }\n    }\n  }\n  // Expansion map with no rotation\n  else if (scale_f_of_t_a_.has_value() and not rot_f_of_t_.has_value()) {\n    const double scale_a_of_t =\n        functions_of_time.at(scale_f_of_t_a_.value())->func(time)[0][0];\n    const double scale_b_of_t =\n        functions_of_time.at(scale_f_of_t_b_.value())->func(time)[0][0];\n    if (region_ == BlockRegion::Inner) {\n      for (size_t i = 0; i < Dim; i++) {\n        result.get(i, i) = scale_a_of_t;\n      }\n    } else if (region_ == BlockRegion::Transition) {\n      for (size_t k = 0; k < get_size(radius); k++) {\n        const double alpha = inner_radius_ * outer_radius_ /\n                             (square((get_element(radius, k))) *\n                              (inner_radius_ - outer_radius_));\n        // Optimization from SpEC to reduce roundoff.\n        // Closer to outer radius\n        if (1.0 - get_element(radius, k) / (inner_radius_ + outer_radius_) <\n            .5) {\n          double radial_scaling_factor =\n              ((outer_radius_ - get_element(radius, k)) *\n               (scale_a_of_t - scale_b_of_t) * inner_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            for (size_t j = 0; j < Dim; j++) {\n              get_element(result.get(i, j), k) =\n                  alpha *\n                  get_element(dereference_wrapper(gsl::at(source_coords, i)),\n                              k) *\n                  (scale_a_of_t - scale_b_of_t) *\n                  get_element(dereference_wrapper(gsl::at(source_coords, j)),\n                              k) /\n                  get_element(radius, k);\n            }\n            get_element(result.get(i, i), k) +=\n                scale_b_of_t + radial_scaling_factor;\n          }\n          // Closer to inner radius\n        } else {\n          double radial_scaling_factor =\n              ((inner_radius_ - get_element(radius, k)) *\n               (scale_a_of_t - scale_b_of_t) * outer_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            for (size_t j = 0; j < Dim; j++) {\n              get_element(result.get(i, j), k) =\n                  alpha *\n                  get_element(dereference_wrapper(gsl::at(source_coords, i)),\n                              k) *\n                  (scale_a_of_t - scale_b_of_t) *\n                  get_element(dereference_wrapper(gsl::at(source_coords, j)),\n                              k) /\n                  get_element(radius, k);\n            }\n            get_element(result.get(i, i), k) +=\n                scale_a_of_t + radial_scaling_factor;\n          }\n        }\n      }\n    } else {\n      for (size_t i = 0; i < Dim; i++) {\n        result.get(i, i) = scale_b_of_t;\n      }\n    }\n  }\n  // Rotation and expansion map\n  else if (scale_f_of_t_a_.has_value() and rot_f_of_t_.has_value()) {\n    const Matrix rot_matrix = rotation_matrix<Dim>(\n        time, *(functions_of_time.at(rot_f_of_t_.value())));\n    const double scale_a_of_t =\n        functions_of_time.at(scale_f_of_t_a_.value())->func(time)[0][0];\n    const double scale_b_of_t =\n        functions_of_time.at(scale_f_of_t_b_.value())->func(time)[0][0];\n    if (region_ == BlockRegion::Inner) {\n      for (size_t i = 0; i < Dim; i++) {\n        for (size_t j = 0; j < Dim; j++) {\n          result.get(i, j) = scale_a_of_t * rot_matrix(i, j);\n        }\n      }\n    } else if (region_ == BlockRegion::Transition) {\n      for (size_t k = 0; k < get_size(radius); k++) {\n        const double alpha = inner_radius_ * outer_radius_ /\n                             (square((get_element(radius, k))) *\n                              (inner_radius_ - outer_radius_));\n        // Optimization from SpEC to reduce roundoff.\n        // Closer to outer radius\n        if (1.0 - get_element(radius, k) / (inner_radius_ + outer_radius_) <\n            .5) {\n          double radial_scaling_factor =\n              ((outer_radius_ - get_element(radius, k)) *\n               (scale_a_of_t - scale_b_of_t) * inner_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            double rotated_coords = 0;\n            for (size_t l = 0; l < Dim; l++) {\n              rotated_coords +=\n                  rot_matrix(i, l) *\n                  get_element(dereference_wrapper(gsl::at(source_coords, l)),\n                              k);\n            }\n            for (size_t j = 0; j < Dim; j++) {\n              get_element(result.get(i, j), k) =\n                  scale_b_of_t * rot_matrix(i, j) +\n                  alpha * rotated_coords * (scale_a_of_t - scale_b_of_t) *\n                      get_element(\n                          dereference_wrapper(gsl::at(source_coords, j)), k) /\n                      get_element(radius, k) +\n                  rot_matrix(i, j) * radial_scaling_factor;\n            }\n          }\n          // Closer to inner radius\n        } else {\n          double radial_scaling_factor =\n              ((inner_radius_ - get_element(radius, k)) *\n               (scale_a_of_t - scale_b_of_t) * outer_radius_) /\n              ((outer_radius_ - inner_radius_) * get_element(radius, k));\n          for (size_t i = 0; i < Dim; i++) {\n            double rotated_coords = 0;\n            for (size_t l = 0; l < Dim; l++) {\n              rotated_coords +=\n                  rot_matrix(i, l) *\n                  get_element(dereference_wrapper(gsl::at(source_coords, l)),\n                              k);\n            }\n            for (size_t j = 0; j < Dim; j++) {\n              get_element(result.get(i, j), k) =\n                  scale_a_of_t * rot_matrix(i, j) +\n                  alpha * rotated_coords * (scale_a_of_t - scale_b_of_t) *\n                      get_element(\n                          dereference_wrapper(gsl::at(source_coords, j)), k) /\n                      get_element(radius, k) +\n                  rot_matrix(i, j) * radial_scaling_factor;\n            }\n          }\n        }\n      }\n    } else {\n      for (size_t i = 0; i < Dim; i++) {\n        for (size_t j = 0; j < Dim; j++) {\n          result.get(i, j) = scale_b_of_t * rot_matrix(i, j);\n        }\n      }\n    }\n  }\n  // Translation map\n  if (trans_f_of_t_.has_value()) {\n      const DataVector trans_func_of_time =\n          functions_of_time.at(trans_f_of_t_.value())->func_and_deriv(time)[0];\n      for (size_t i = 0; i < Dim; i++) {\n        const double deriv_translation_factor =\n            (-gsl::at(trans_func_of_time, i) / (outer_radius_ - inner_radius_));\n        for (size_t j = 0; j < Dim; j++) {\n          for (size_t k = 0; k < get_size(radius); k++) {\n            // The jacobian is the identity for the translation map in regions\n            // not between the inner and outer radius.\n            if (region_ == BlockRegion::Transition) {\n              // using the derivative of the radial falloff factor as\n              // \\frac{dw}{dr} = \\frac{-1.0}{R_{out} - R{in}}\n              get_element(result.get(i, j), k) +=\n                  deriv_translation_factor *\n                  get_element(dereference_wrapper(gsl::at(source_coords, j)),\n                              k) /\n                  get_element(radius, k);\n            }\n          }\n        }\n      }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nRotScaleTrans<Dim>::inv_jacobian(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  return determinant_and_inverse(\n             jacobian(source_coords, time, functions_of_time))\n      .second;\n}\n\ntemplate <size_t Dim>\ndouble RotScaleTrans<Dim>::root_helper(\n    const std::optional<std::array<double, 2>> roots) const {\n  ASSERT(roots.has_value(), \"No roots found\");\n  if (roots.value()[0] >= 0 and roots.value()[0] <= 1.0) {\n    ASSERT(roots.value()[1] > 1.0 or roots.value()[1] < 0.0,\n           \"Singular map: two solutions between 0 and 1\");\n    return roots.value()[0];\n  } else if (roots.value()[1] >= 0 and roots.value()[1] <= 1.0) {\n    return roots.value()[1];\n  } else if (equal_within_roundoff(roots.value()[0], 1.0) or\n\n             equal_within_roundoff(roots.value()[1], 1.0)) {\n    return 1.0;\n  } else if (equal_within_roundoff(roots.value()[0], 0.0) or\n\n             equal_within_roundoff(roots.value()[1], 0.0)) {\n    return 0.0;\n  }\n  ERROR(\"Root helper couldn't find the correct root\");\n  return 0.0;\n}\n\ntemplate <size_t Dim>\nvoid RotScaleTrans<Dim>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | scale_f_of_t_a_;\n    p | scale_f_of_t_b_;\n    p | rot_f_of_t_;\n    p | trans_f_of_t_;\n    p | inner_radius_;\n    p | outer_radius_;\n    p | region_;\n  }\n  // No need to pup this because it is uniquely determined by f_of_t_names_\n  if (p.isUnpacking()) {\n    f_of_t_names_.clear();\n    if (rot_f_of_t_.has_value()) {\n      f_of_t_names_.insert(rot_f_of_t_.value());\n    }\n    if (scale_f_of_t_a_.has_value() and scale_f_of_t_b_.has_value()) {\n      f_of_t_names_.insert(scale_f_of_t_a_.value());\n      f_of_t_names_.insert(scale_f_of_t_b_.value());\n    }\n    if (trans_f_of_t_.has_value()) {\n      f_of_t_names_.insert(trans_f_of_t_.value());\n    }\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const RotScaleTrans<Dim>& lhs, const RotScaleTrans<Dim>& rhs) {\n  return lhs.scale_f_of_t_a_ == rhs.scale_f_of_t_a_ and\n         lhs.scale_f_of_t_b_ == rhs.scale_f_of_t_b_ and\n         lhs.rot_f_of_t_ == rhs.rot_f_of_t_ and\n         lhs.trans_f_of_t_ == rhs.trans_f_of_t_ and\n         lhs.inner_radius_ == rhs.inner_radius_ and\n         lhs.outer_radius_ == rhs.outer_radius_ and lhs.region_ == rhs.region_;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                \\\n  template class RotScaleTrans<DIM(data)>;                  \\\n  template bool operator==(const RotScaleTrans<DIM(data)>&, \\\n                           const RotScaleTrans<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\n\n#undef INSTANTIATE\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)>      \\\n  RotScaleTrans<DIM(data)>::operator()(                                     \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)>      \\\n  RotScaleTrans<DIM(data)>::frame_velocity(                                 \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),        \\\n                    Frame::NoFrame>                                         \\\n  RotScaleTrans<DIM(data)>::jacobian(                                       \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),        \\\n                    Frame::NoFrame>                                         \\\n  RotScaleTrans<DIM(data)>::inv_jacobian(                                   \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3),\n                        (double, DataVector,\n                         std::reference_wrapper<const double>,\n                         std::reference_wrapper<const DataVector>))\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/RotScaleTrans.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps::TimeDependent {\n/*!\n * \\ingroup CoordMapsTimeDependentGroup\n * \\brief RotScaleTrans map which applies a combination of rotation, expansion,\n * and translation based on which maps are supplied.\n *\n * \\details This map adds a rotation, expansion and translation based on what\n * types of maps are needed. Translation and expansion have piecewise functions\n * that map the coordinates $\\vec{\\xi}$ based on what region\n * $|\\vec{\\xi}|$ is in. Coordinates within the inner radius are translated by\n * the translation function of time $\\vec{T}(t)$ and expanded by the inner\n * expansion function of time $E_{a}(t)$. Coordinates in between the inner\n * radius and outer radius have a linear radial falloff applied to them.\n * Coordinates at or beyond the outer radius have no translation applied and are\n * expanded by the outer expansion function of time $E_{b}(t)$. This map assumes\n * that the center of the map is at (0., 0., 0.). There is an enum class to set\n * which region your block is in. Specifying RotScaleTrans::BlockRegion::Inner\n * treats coordinates as if they're inside the inner radius, setting\n * RotScaleTrans::BlockRegion::Transition treats the points as if they're\n * between the inner and outer radius, and setting\n * RotScaleTrans::BlockRegion::Outer treats points as if they're on or beyond\n * the outer boundary.\n *\n * \\note $E_{a}(t)$ and $E_{b}(t)$ are $a(t)$ and $b(t)$ from the CubicScale\n * documentation.\n *\n * ## Mapped Coordinates\n * The RotScaleTrans map takes the coordinates $\\vec{\\xi}$ to the target\n * coordinates $\\vec{\\bar{\\xi}}$ through\n * \\f{equation}{\n * \\vec{\\bar{\\xi}} = \\left\\{\\begin{array}{ll}E_{a}(t)R(t)\\vec{\\xi} + \\vec{T}(t),\n * & |\\vec{\\xi}| \\leq R_{in}, \\\\ (w_{E} + E_{a}(t))R(t)\\vec{\\xi} + (1 +\n * w_{T})\\vec{T}(t), & R_{in} < |\\vec{\\xi}| \\leq 0.5(R_{in} + R_{out}),\n * \\\\ (w_{E} + E_{b}(t))R(t)\\vec{\\xi} + w_{T}\\vec{T}(t), & 0.5(R_{in} + R_{out})\n * < |\\vec{\\xi}| < R_{out}, \\\\ E_{b}(t)R(t)\\vec{\\xi}, & |\\vec{\\xi}| \\geq R_{out}\n * \\end{array}\\right.\n * \\f}\n *\n * Where $R_{in}$ is the inner radius, $R_{out}$ is the outer radius, and\n * $w_{T}$ is the translation falloff factor and $w_{E}$ is the expansion\n * falloff factor found through\n * \\f{equation}{\n * w_{E} = \\left\\{\\begin{array}{ll}\\frac{R_{in}(R_{out} - |\\vec{\\xi}|)(E_{a}(t)\n * - E_{b}(t))}{|\\vec{\\xi}|(R_{out} - R_{in})}, & R_{in} < |\\vec{\\xi}| \\leq\n * 0.5(R_{in} + R_{out}), \\\\ \\frac{R_{out}(R_{in} - |\\vec{\\xi}|)(E_{a}(t)\n * - E_{b}(t))}{|\\vec{\\xi}|(R_{out} - R_{in})}, & 0.5(R_{in} + R_{out}) <\n * |\\vec{\\xi}| < R_{out} \\end{array}\\right.\n * \\f}\n *\n * and\n *\n * \\f{equation}{\n * w_{T} = \\left\\{\\begin{array}{ll}\\frac{R_{in} - |\\vec{\\xi}|}{R_{out} -\n * R_{in}}, & R_{in} < |\\vec{\\xi}| \\leq 0.5(R_{in} + R_{out}), \\\\ \\frac{R_{out}\n * - |\\vec{\\xi}|}{R_{out} - R_{in}}, & 0.5(R_{in} + R_{out}) < |\\vec{\\xi}| <\n * R_{out} \\end{array}\\right.\n * \\f}\n *\n * $w_{E}$ and $w_{T}$ are calculated differently based on if you're closer\n * to the inner radius or outer radius to reduce roundoff error.\n *\n * ## Inverse\n * The inverse function maps the coordinates $\\vec{\\bar{\\xi}}$ back to the\n * original coordinates $\\vec{\\xi}$ through different equations based on\n * which maps are supplied.\n *\n * If Rotation, Expansion, and Translation Maps are supplied then the\n * inverse is given by\n *\n * \\f{equation}{\n * \\label{eq:full_inverse}\n * \\vec{\\xi} = \\left\\{\\begin{array}{ll}R^{T}(t)(\\frac{(\\vec{\\bar{\\xi}} -\n * \\vec{T}(t))}{E_{a}(t)}), & |\\vec{\\bar{\\xi}}| \\leq R_{in}E_{a}(t),\n * \\\\ R^{T}(t)\\frac{\\vec{\\bar{\\xi}} - w_{T}\\vec{T}(t)}{w_{E}},\n * & R_{in}E_{a}(t) < |\\vec{\\bar{\\xi}}| \\leq 0.5(R_{in}E_{a}(t) +\n * R_{out}E_{b}(t)), \\\\ R^{T}(t)\\frac{\\vec{\\bar{\\xi}} - (1.0 -\n * w_{T})\\vec{T}(t)}{w_{E}}, & 0.5(R_{in}E_{a}(t) + R_{out}E_{b}(t)) <\n * |\\vec{\\bar{\\xi}}| < R_{out}E_{b}(t),\n * \\\\ R^{T}(t)\\frac{\\vec{\\bar{\\xi}}}{E_{b}(t)}, & |\\vec{\\bar{\\xi}}| \\geq\n * R_{out}E_{b}(t) \\end{array}\\right.\n * \\f}\n *\n * Where $w_{T}$ and $w_{E}$ are found through different quadratic solves.\n *\n * When closer to $R_{in}$ the quadratic has the form\n * \\f{equation}{\n * w_{T}^2((E_{a}(t)R_{in} - E_{b}(t)R_{out})^2 - T(t)^2) +\n * 2w_{T}(E_{a}(t)R_{in}(E_{b}(t)R_{out} - E_{a}(t)R_{in}) + \\vec{T}(t) \\cdot\n * (\\vec{T}(t) - \\vec{\\bar{\\xi}})) + \\vec{T}(t) \\cdot \\vec{\\bar{\\xi}})\n * + E_{a}(t)^2 R_{in}^2 - (\\vec{\\bar{\\xi}} - \\vec{T}(t))^2\n * \\f}\n *\n * where $w_{E} = \\frac{(1.0 - w_{T})R_{in}E_{a}(t) +\n * w_{T}R_{out}E_{b}(t)}{(1.0 - w_{T})R_{in} + w_{T}R_{out}}$\n *\n * When closer to $R_{out}$ the quadratic has the form\n * \\f{equation}{\n * w_{T}^2((E_{a}(t)R_{in} - E_{b}(t)R_{out})^2 - T(t)^2) +\n * 2w_{T}(E_{b}(t)R_{out}(E_{a}(t)R_{in} - E_{b}(t)R_{out}) +\n * \\vec{T}(t) \\cdot \\vec{\\bar{\\xi}})\n * + E_{b}(t)^2 R_{out}^2 - \\vec{\\bar{\\xi}}^2\n * \\f}\n *\n * where $w_{E} = \\frac{w_{T}R_{in}E_{a}(t) + (1.0 -\n * w_{T})R_{out}E_{b}(t)}{w_{T}R_{in} + (1.0 - w_{T})R_{out}}$\n *\n * If Rotation and Expansion are supplied then the inverse is given by\n *\n * \\f{equation}{\n * \\vec{\\xi} =\n * \\left\\{\\begin{array}{ll}R^{T}(t)(\\frac{\\vec{\\bar{\\xi}}}{E_{a}(t)}), &\n * |\\vec{\\bar{\\xi}}| \\leq R_{in}E_{a}(t),\n * \\\\ R^{T}(t)\\frac{\\vec{\\bar{\\xi}}}{w_{E}}, & R_{in}E_{a}(t) <\n * |\\vec{\\bar{\\xi}}| \\leq 0.5(R_{in}E_{a}(t) + R_{out}E_{b}(t)),\n * \\\\ R^{T}(t)\\frac{\\vec{\\bar{\\xi}}}{w_{E}}, & 0.5(R_{in}E_{a}(t)\n * + R_{out}E_{b}(t)) < |\\vec{\\bar{\\xi}}| < R_{out}E_{b}(t),\n * \\\\ R^{T}(t)\\frac{\\vec{\\bar{\\xi}}}{E_{b}(t)}, & |\\vec{\\bar{\\xi}}| \\geq\n * R_{out}E_{b}(t) \\end{array}\\right.\n * \\f}\n *\n * Where $w_{E}$ is found through different quadratic solves.\n *\n * When closer to $R_{in}$ the quadratic has the form\n * \\f{equation}{\n * w^2(E_{a}(t)R_{in} - E_{b}(t)R_{out})^2 +\n * 2wE_{a}(t)R_{in}(E_{b}(t)R_{out} - E_{a}(t)R_{in})\n * + (E_{a}(t) R_{in})^2 - \\bar{\\xi}^2\n * \\f}\n * with $w_{E} = \\frac{E_{a}(t)R_{in}(1.0 - w) + wE_{b}(t)R_{out}}{R_{in}(1.0 -\n * w) + wR_{out}}$\n *\n * When closer to $R_{out}$ the quadratic has the form\n * \\f{equation}{\n * w^2(E_{a}(t)R_{in} - E_{b}(t)R_{out})^2 +\n * 2wE_{b}(t)R_{out}(E_{a}(t)R_{in} - E_{b}(t)R_{out})\n * + (E_{b}(t) R_{out})^2 - \\bar{\\xi}^2\n * \\f}\n * with $w_{E} = \\frac{wE_{a}(t)R_{in} + E_{b}(t)R_{out}(1.0 - w)}{wR_{in} +\n * R_{out}(1.0 - w)}$\n *\n * If Rotation and Translation are supplied, then the inverse is given by\n *\n * \\f{equation}{\n * \\vec{\\xi} = \\left\\{\\begin{array}{ll}R^{T}(t)(\\vec{\\bar{\\xi}} -\n * \\vec{T}(t)), & |\\vec{\\bar{\\xi}}| \\leq R_{in},\n * \\\\ R^{T}(t)(\\vec{\\bar{\\xi}} - w_{T}\\vec{T}(t)), & R_{in} < |\\vec{\\bar{\\xi}}|\n * \\leq 0.5(R_{in} + R_{out}), \\\\ R^{T}(t)(\\vec{\\bar{\\xi}} - (1.0 -\n * w_{T})\\vec{T}(t)), & 0.5(R_{in} + R_{out}) < |\\vec{\\bar{\\xi}}| < R_{out},\n * \\\\ R^{T}(t)\\vec{\\bar{\\xi}}, & |\\vec{\\bar{\\xi}}| \\geq R_{out}\n * \\end{array}\\right.\n * \\f}\n *\n * Where $w_{T}$ is found through different quadratic solves.\n *\n * When closer to $R_{in}$ the quadratic has the form\n * \\f{equation}{\n * w_{T}^2(T(t)^2 - (R_{out} - R_{in})^2) - 2w_{T}(\\vec{T}(t) \\cdot\n * (\\vec{T}(t) - \\vec{\\bar{\\xi}}) + R_{in}(R_{out} - R_{in})\n * + (\\vec{T}(t) - \\vec{\\bar{\\xi}})^2 - R_{in}^2\n * \\f}\n *\n * When closer to $R_{out}$ the quadratic has the form\n * \\f{equation}{\n * w_{T}^2(T(t)^2 - (R_{out} - R_{in})^2) +\n * 2w_{T}(R_{out}(R_{out} - R_{in}) - \\vec{T}(t) \\cdot \\vec{\\bar{\\xi}})\n * + \\vec{\\bar{\\xi}}^2 - R_{out}^2\n * \\f}\n *\n * If Expansion and Translation are supplied, then the inverse is given by\n * Eq. $\\ref{eq:full_inverse}$, with no transpose of rotation applied.\n *\n * \\note For all the maps with rotation, the inverse of rotation is the\n * transpose of the original rotation. For maps with translation, the inverse\n * map also assumes that if $\\vec{\\bar{\\xi}} - \\vec{T}(t) \\leq R_{in}$ then the\n * translated point originally came from within the inner radius so it'll be\n * translated back without a quadratic solve.\n *\n * ## Frame Velocity\n * The Frame Velocity is found through different equations based on which maps\n * are supplied.\n *\n * If Rotation, Expansion, and Translation are supplied then the frame\n * velocity is found through\n *\n * \\f{equation}{\n * \\vec{v} = \\left\\{\\begin{array}{ll}(E_{a}(t)dR(t) +\n * dE_{a}(t)R(t))\\vec{\\xi} + d \\vec{T}(t), & |\\vec{\\xi}| \\leq R_{in},\n * \\\\ ((E_{a}(t) + w_{E})dR(t) + (dE_{a}(t) + dw_{E})R(t))\\vec{\\xi} + (1 +\n * w_{T})d \\vec{T}(t), & R_{in} < |\\vec{\\xi}| \\leq 0.5(R_{in} + R_{out}),\n * \\\\ ((E_{b}(t) + w_{E})dR(t) + (dE_{b}(t) + dw_{E})R(t))\\vec{\\xi} + w_{T}d\n * \\vec{T}(t), & 0.5(R_{in} + R_{out}) < |\\vec{\\xi}| < R_{out},\n * \\\\ (E_{b}(t)dR(t) + dE_{b}(t)R(t))\\vec{\\xi}, & |\\vec{\\xi}| \\geq R_{out}\n * \\end{array}\\right.\n * \\f}\n *\n * where $dw_{E}$ is the derivative of the $w_{E}$ given by\n *\n * \\f{equation}{\n * dw_{E} = \\left\\{\\begin{array}{ll}\\frac{R_{out}(R_{in} -\n * |\\vec{\\xi}|)(dE_{a}(t)\n * - dE_{b}(t))}{|\\vec{\\xi}|(R_{out} - R_{in})}, & R_{in} < |\\vec{\\xi}| \\leq\n * 0.5(R_{in} + R_{out}), \\\\ \\frac{R_{in}(R_{out} - |\\vec{\\xi}|)(dE_{a}(t) -\n * dE_{b}(t))}{|\\vec{\\xi}|(R_{out} - R_{in})}, & 0.5(R_{in} + R_{out}) <\n * |\\vec{\\xi}| < R_{out} \\end{array}\\right.\n * \\f}\n *\n * ## Jacobian\n * The jacobian is also found through different equations based on which maps\n * are supplied.\n *\n * If Rotation, Expansion and Translation maps are supplied then the\n * jacobian is found through\n *\n * \\f{equation}{\n * {J^{i}}_{j} = \\left\\{\\begin{array}{ll}E_{a}(t){R^{i}}_{j}(t), & |\\vec{\\xi}|\n * \\leq R_{in}, \\\\ {R^{i}}_{j}(t)E_{a}(t) + \\frac{\\alpha\n * {R^{i}}_{l}(t)\\vec{\\xi}^{l}\\vec{\\xi}_{j}(E_{A}(t) - E_{B}(t))}{|\\vec{\\xi}|} +\n * w_{E}{R^{i}}_{j}(t) + \\frac{dw_{T}T^{i}\\xi_{j}}{|\\vec{\\xi}|}, & R_{in} <\n * |\\vec{\\xi}| \\leq 0.5(R_{in} + R_{out}), \\\\ {R^{i}}_{j}(t)E_{b}(t) +\n * \\frac{\\alpha {R^{i}}_{l}(t)\\vec{\\xi}^{l}\\vec{\\xi}_{j}(E_{A}(t) -\n * E_{B}(t))}{|\\vec{\\xi}|} + w_{E}{R^{i}}_{j}(t) +\n * \\frac{dw_{T}T^{i}\\xi_{j}}{|\\vec{\\xi}|}, & 0.5(R_{in} + R_{out}) < |\\vec{\\xi}|\n * < R_{out}, \\\\ E_{b}(t){R^{i}}_{j}(t), & |\\vec{\\xi}| \\geq R_{out}\n * \\end{array}\\right.\n * \\f}\n *\n * where $\\alpha = \\frac{R_{in}R_{out}}{\\vec{\\xi}^2(R_{in} - R_{out})}$ and\n * $dw_{T} = \\frac{-1.0}{R_{out} - R_{in}}$\n *\n * \\note For the translation map, the map returns the identity for all regions\n * except between $R_{in}$ and $R_{out}$\n *\n * ## Inverse Jacobian\n * The inverse jacobian is computed numerically by inverting the jacobian.\n */\ntemplate <size_t Dim>\nclass RotScaleTrans {\n public:\n  enum class BlockRegion {\n    /// Within the inner radius\n    Inner,\n    /// Between inner and outer radius\n    Transition,\n    /// At or beyond outer boundary\n    Outer\n  };\n\n  static constexpr size_t dim = Dim;\n\n  explicit RotScaleTrans(\n      std::optional<std::pair<std::string, std::string>> scale_f_of_t_names,\n      std::optional<std::string> rot_f_of_t_name,\n      std::optional<std::string> trans_f_of_t_name, double inner_radius,\n      double outer_radius, BlockRegion region);\n\n  RotScaleTrans() = default;\n  ~RotScaleTrans() = default;\n  RotScaleTrans(const RotScaleTrans<Dim>& RotScaleTrans_Map) = default;\n  RotScaleTrans(RotScaleTrans&&) = default;\n  RotScaleTrans& operator=(RotScaleTrans&&) = default;\n  RotScaleTrans& operator=(const RotScaleTrans& RotScaleTrans_Map) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> operator()(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, Dim>> inverse(\n      const std::array<double, Dim>& target_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> frame_velocity(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  template <size_t LocalDim>\n  friend bool operator==(  // NOLINT(readability-redundant-declaration)\n      const RotScaleTrans<LocalDim>& lhs, const RotScaleTrans<LocalDim>& rhs);\n\n  // The root helper returns the correct root of the roots found during the\n  // quadratic solve in the inverse function.\n  double root_helper(std::optional<std::array<double, 2>> roots) const;\n\n  std::optional<std::string> scale_f_of_t_a_{};\n  std::optional<std::string> scale_f_of_t_b_{};\n  std::optional<std::string> rot_f_of_t_{};\n  std::optional<std::string> trans_f_of_t_{};\n  std::unordered_set<std::string> f_of_t_names_;\n  double inner_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double outer_radius_{std::numeric_limits<double>::signaling_NaN()};\n  BlockRegion region_ = BlockRegion::Inner;\n};\n\ntemplate <size_t Dim>\nbool operator!=(const RotScaleTrans<Dim>& lhs, const RotScaleTrans<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/Rotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n\n#include <cmath>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::CoordinateMaps::TimeDependent {\n\ntemplate <size_t Dim>\nRotation<Dim>::Rotation(std::string function_of_time_name)\n    : f_of_t_name_(std::move(function_of_time_name)),\n      f_of_t_names_({f_of_t_name_}) {}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Rotation<Dim>::operator()(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const Matrix rot_matrix =\n      rotation_matrix<Dim>(time, *(functions_of_time.at(f_of_t_name_)));\n\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = rot_matrix(i, 0) * source_coords[0];\n    for (size_t j = 1; j < Dim; j++) {\n      gsl::at(result, i) += rot_matrix(i, j) * gsl::at(source_coords, j);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::array<double, Dim>> Rotation<Dim>::inverse(\n    const std::array<double, Dim>& target_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const Matrix rot_matrix =\n      rotation_matrix<Dim>(time, *(functions_of_time.at(f_of_t_name_)));\n\n  // The inverse map uses the inverse rotation matrix, which is just the\n  // transpose of the rotation matrix\n  std::array<double, Dim> result{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = rot_matrix(0, i) * target_coords[0];\n    for (size_t j = 1; j < Dim; j++) {\n      gsl::at(result, i) += rot_matrix(j, i) * gsl::at(target_coords, j);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Rotation<Dim>::frame_velocity(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const Matrix rot_matrix_deriv =\n      rotation_matrix_deriv<Dim>(time, *(functions_of_time.at(f_of_t_name_)));\n\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = rot_matrix_deriv(i, 0) * source_coords[0];\n    for (size_t j = 1; j < Dim; j++) {\n      gsl::at(result, i) += rot_matrix_deriv(i, j) * gsl::at(source_coords, j);\n    }\n  }\n\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nRotation<Dim>::jacobian(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const Matrix rot_matrix =\n      rotation_matrix<Dim>(time, *(functions_of_time.at(f_of_t_name_)));\n\n  // Make tensor of zeros with correct type\n  auto jacobian_matrix = make_with_value<\n      tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>>(\n      dereference_wrapper(source_coords[0]), 0.0);\n\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = 0; j < Dim; j++) {\n      jacobian_matrix.get(i, j) = rot_matrix(i, j);\n    }\n  }\n\n  return jacobian_matrix;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nRotation<Dim>::inv_jacobian(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const Matrix rot_matrix =\n      rotation_matrix<Dim>(time, *(functions_of_time.at(f_of_t_name_)));\n\n  auto inv_jacobian_matrix = make_with_value<\n      tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>>(\n      dereference_wrapper(source_coords[0]), 0.0);\n\n  // The inverse jacobian is just the inverse rotation matrix, which is the\n  // transpose of the rotation matrix.\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = 0; j < Dim; j++) {\n      inv_jacobian_matrix.get(i, j) = rot_matrix(j, i);\n    }\n  }\n\n  return inv_jacobian_matrix;\n}\n\ntemplate <size_t Dim>\nvoid Rotation<Dim>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | f_of_t_name_;\n  }\n\n  // No need to pup this because it is uniquely determined by f_of_t_name_\n  if (p.isUnpacking()) {\n    f_of_t_names_.clear();\n    f_of_t_names_.insert(f_of_t_name_);\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const Rotation<Dim>& lhs, const Rotation<Dim>& rhs) {\n  return lhs.f_of_t_name_ == rhs.f_of_t_name_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Rotation<Dim>& lhs, const Rotation<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template class Rotation<DIM(data)>;                                      \\\n  template bool operator==                                                 \\\n      <DIM(data)>(const Rotation<DIM(data)>&, const Rotation<DIM(data)>&); \\\n  template bool operator!=                                                 \\\n      <DIM(data)>(const Rotation<DIM(data)>&, const Rotation<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\n\n#undef INSTANTIATE\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)>      \\\n  Rotation<DIM(data)>::operator()(                                          \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)>      \\\n  Rotation<DIM(data)>::frame_velocity(                                      \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords,              \\\n      const double time,                                                    \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),        \\\n                    Frame::NoFrame>                                         \\\n  Rotation<DIM(data)>::jacobian(                                            \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),        \\\n                    Frame::NoFrame>                                         \\\n  Rotation<DIM(data)>::inv_jacobian(                                        \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE, (2, 3),\n    (double, DataVector,\n     std::reference_wrapper<const double>,\n     std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3), MAP_AUTODIFF_TYPES)\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/Rotation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\n}  // namespace domain\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\nnamespace CoordinateMaps {\nnamespace TimeDependent {\n\n/*!\n * \\ingroup CoordMapsTimeDependentGroup\n * \\brief Time-dependent spatial rotation in two or three dimensions.\n *\n * ### General Transformation\n *\n * Let the source coordinates \\f$ \\vec{\\xi} \\f$ be mapped to coordinates \\f$\n * \\vec{x} \\f$ using the transformation\n *\n * \\f[ \\vec{x} = R(t)\\vec{\\xi}, \\f]\n *\n * where \\f$ R(t) \\f$ is a rotation matrix of proper dimensionality (defined\n * below) and \\f$ A\\vec{v} \\f$ is the standard matrix-vector multiplicaton. For\n * 2D rotation, \\f$ \\vec{\\xi} = \\left(\\xi, \\eta\\right) \\f$ and \\f$ \\vec{x} =\n * \\left(x, y\\right) \\f$ while for 3D rotations \\f$ \\vec{\\xi} = \\left(\\xi, \\eta,\n * \\zeta\\right) \\f$ and \\f$ \\vec{x} = \\left(x, y, z\\right) \\f$.\n *\n * The inverse transformation is\n *\n * \\f[ \\vec{\\xi} = R^T(t) \\vec{x} \\f]\n *\n * because the inverse of a rotation matrix is its transpose.\n *\n * The frame velocity \\f$ \\vec{v} = d\\vec{x}/dt \\f$ is\n *\n * \\f[ \\vec{v} = \\frac{d}{dt}\\big( R(t) \\big) \\vec{\\xi} \\f]\n *\n * where \\f$ d(R(t))/dt \\f$ is the time derivative of the rotation matrix.\n *\n * The components of the Jacobian \\f$ \\partial x^i/\\partial\\xi^j \\f$ are\n * trivially related to the components of the rotation matrix by\n *\n * \\f[ \\partial x^i/\\partial\\xi^j = R_{ij}, \\f]\n *\n * and similarly the components of the inverse Jacobian \\f$ \\partial\n * \\xi^i/\\partial x^j \\f$ are\n *\n * \\f[ \\partial \\xi^i/\\partial x^j = R^{-1}_{ij} = R^T_{ij} = R_{ji}. \\f]\n *\n * ### 2D Rotation Matrix\n *\n * The 2D rotaion matrix is defined in the usual way as\n *\n * \\f[\n * R(t) =\n *   \\begin{bmatrix}\n *   \\cos(\\theta(t)) & -\\sin(\\theta(t)) \\\\\n *   \\sin(\\theta(t)) &  \\cos(\\theta(t)) \\\\\n *   \\end{bmatrix}.\n * \\f]\n *\n * We associate the polar coordinates \\f$ \\left( \\mathrm{P}, \\Phi\\right) \\f$\n * with the unmapped coordinates \\f$ \\left(\\xi, \\eta\\right) \\f$ and the polar\n * coordinates \\f$ \\left(r,\\phi\\right) \\f$ with the mapped coordinates \\f$\n * \\left(x, y\\right) \\f$. We then have \\f$ \\phi = \\Phi + \\theta(t) \\f$.\n *\n * The derivative of the rotation matrix is then\n *\n * \\f[\n * R(t) =\n *   \\begin{bmatrix}\n *   -\\omega(t) \\sin(\\theta(t)) & -\\omega(t)\\cos(\\theta(t)) \\\\\n *    \\omega(t) \\cos(\\theta(t)) & -\\omega(t)\\sin(\\theta(t)) \\\\\n *   \\end{bmatrix}.\n * \\f]\n *\n * where \\f$ \\omega(t) = d\\theta(t)/dt \\f$.\n *\n * \\note This 2D rotation is assumed to be in the \\f$ xy \\f$-plane (about the\n * \\f$ z \\f$-axis).\n *\n * ### 3D Rotation Matrix\n *\n * For 3D rotations, we use quaternions to represent rotations about an\n * arbitrary axis. We define a unit quaternion as\n *\n * \\f[\n *   \\mathbf{q}\n *     = \\left(q_0, q_1, q_2, q_3\\right)\n *     = \\left(q_0, \\vec{q}\\right)\n *     = \\left(\\cos(\\frac{\\theta(t)}{2}),\n *             \\hat{n}\\sin(\\frac{\\theta(t)}{2})\\right)\n * \\f]\n *\n * where \\f$ \\hat{n} \\f$ is our arbitrary rotation axis and \\f$ \\theta(t) \\f$ is\n * the angle rotated about that axis. A rotation in 3D is then defined as\n *\n * \\f[ \\mathbf{x} = \\mathbf{q}\\mathbf{\\xi}\\mathbf{q}^* \\f]\n *\n * where \\f$ \\mathbf{q}^* = \\left(\\cos(\\theta(t)/2),\n * -\\hat{n}\\sin(\\theta(t)/2)\\right) \\f$ and we promote the vectors to\n * quaternions as \\f$ \\mathbf{x} = \\left(0, \\vec{x}\\right) \\f$ and \\f$\n * \\mathbf{\\xi} = \\left(0, \\vec{\\xi}\\right) \\f$. This will rotate the vector \\f$\n * \\vec{\\xi} \\f$ about \\f$ \\hat{n} \\f$ by an angle \\f$ \\theta(t) \\f$,\n * transforming it into \\f$ \\vec{x} \\f$.\n *\n * We can represent this rotation using quaternions as a rotation matrix of\n * the form\n *\n * \\f[\n * R(t) =\n *   \\begin{bmatrix}\n *   q_0^2 + q_1^2 - q_2^2 - q_3^2 & 2(q_1q_2 - q_0q_3) & 2(q_1q_3 + q_0q_2) \\\\\n *   2(q_1q_2 + q_0q_3) & q_0^2 + q_2^2 - q_1^2 - q_3^2 & 2(q_2q_3 - q_0q_1) \\\\\n *   2(q_1q_3 - q_0q_2) & 2(q_2q_3 + q_0q_1) & q_0^2 + q_3^2 - q_1^2 - q_2^2 \\\\\n *   \\end{bmatrix}.\n * \\f]\n *\n * The derivative of this rotation matrix can expressed in a similar form\n *\n * \\f[\n * R(t) =\n *   \\begin{bmatrix}\n *   2(q_0\\dot{q_0} + q_1\\dot{q_1} - q_2\\dot{q_2} - q_3\\dot{q_3})\n *     & 2(\\dot{q_1}q_2 + q_1\\dot{q_2} - \\dot{q_0}q_3 - q_0\\dot{q_3})\n *     & 2(\\dot{q_1}q_3 + q_1\\dot{q_3} + \\dot{q_0}q_2 + q_0\\dot{q_2}) \\\\\n *   2(\\dot{q_1}q_2 + q_1\\dot{q_2} + \\dot{q_0}q_3 + q_0\\dot{q_3})\n *     & 2(q_0\\dot{q_0} + q_2\\dot{q_2} - q_1\\dot{q_1} - q_3\\dot{q_3})\n *     & 2(\\dot{q_2}q_3 + q_2\\dot{q_3} - \\dot{q_0}q_1 - q_0\\dot{q_1}) \\\\\n *   2(\\dot{q_1}q_3 + q_1\\dot{q_3} - \\dot{q_0}q_2 - q_0\\dot{q_2})\n *     & 2(\\dot{q_2}q_3 + q_2\\dot{q_3} + \\dot{q_0}q_1 + q_0\\dot{q_1})\n *     & 2(q_0\\dot{q_0} + q_3\\dot{q_3} - q_1\\dot{q_1} - q_2\\dot{q_2}) \\\\\n *   \\end{bmatrix}.\n * \\f]\n *\n * \\note If you choose \\f$ \\hat{n} = (0, 0, 1) \\f$, this rotation will be\n * equivalent to the 2D rotation.\n */\ntemplate <size_t Dim>\nclass Rotation {\n public:\n  static_assert(Dim == 2 or Dim == 3,\n                \"Rotation map can only be constructed in 2 or 3 dimensions.\");\n  static constexpr size_t dim = Dim;\n\n  explicit Rotation(std::string function_of_time_name);\n  Rotation() = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> operator()(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, Dim>> inverse(\n      const std::array<double, Dim>& target_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> frame_velocity(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{true};\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Rotation<LocalDim>& lhs,\n                         const Rotation<LocalDim>& rhs);\n  std::string f_of_t_name_;\n  std::unordered_set<std::string> f_of_t_names_;\n};\n\ntemplate <size_t Dim>\nbool operator!=(const Rotation<Dim>& lhs, const Rotation<Dim>& rhs);\n\n}  // namespace TimeDependent\n}  // namespace CoordinateMaps\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid add_bilinear_term(const gsl::not_null<Matrix*> rot_matrix,\n                       const DataVector& q1, const DataVector& q2,\n                       const double coef = 1.0) {\n  (*rot_matrix)(0, 0) +=\n      coef * (q1[0] * q2[0] + q1[1] * q2[1] - q1[2] * q2[2] - q1[3] * q2[3]);\n  (*rot_matrix)(1, 1) +=\n      coef * (q1[0] * q2[0] + q1[2] * q2[2] - q1[1] * q2[1] - q1[3] * q2[3]);\n  (*rot_matrix)(2, 2) +=\n      coef * (q1[0] * q2[0] + q1[3] * q2[3] - q1[1] * q2[1] - q1[2] * q2[2]);\n\n  (*rot_matrix)(0, 1) += coef * (2.0 * (q1[1] * q2[2] - q1[0] * q2[3]));\n  (*rot_matrix)(0, 2) += coef * (2.0 * (q1[0] * q2[2] + q1[1] * q2[3]));\n  (*rot_matrix)(1, 0) += coef * (2.0 * (q1[1] * q2[2] + q1[0] * q2[3]));\n  (*rot_matrix)(1, 2) += coef * (2.0 * (q1[2] * q2[3] - q1[0] * q2[1]));\n  (*rot_matrix)(2, 0) += coef * (2.0 * (q1[1] * q2[3] - q1[0] * q2[2]));\n  (*rot_matrix)(2, 1) += coef * (2.0 * (q1[0] * q2[1] + q1[2] * q2[3]));\n}\n}  // namespace\n\ntemplate <size_t Dim>\nMatrix rotation_matrix(const double t,\n                       const domain::FunctionsOfTime::FunctionOfTime& fot) {\n  static_assert(\n      Dim == 2 or Dim == 3,\n      \"Rotation matrices can only be constructed in 2 or 3 dimensions.\");\n  Matrix rotation_matrix{Dim, Dim, 0.0};\n\n  if constexpr (Dim == 2) {\n    const double rotation_angle = fot.func(t)[0][0];\n    rotation_matrix(0, 0) = cos(rotation_angle);\n    rotation_matrix(0, 1) = -sin(rotation_angle);\n    rotation_matrix(1, 0) = sin(rotation_angle);\n    rotation_matrix(1, 1) = cos(rotation_angle);\n  } else {\n    const DataVector quat = fot.func(t)[0];\n    add_bilinear_term(make_not_null(&rotation_matrix), quat, quat);\n  }\n\n  return rotation_matrix;\n}\n\ntemplate <size_t Dim>\nMatrix rotation_matrix_deriv(\n    const double t, const domain::FunctionsOfTime::FunctionOfTime& fot) {\n  static_assert(\n      Dim == 2 or Dim == 3,\n      \"Rotation matrices can only be constructed in 2 or 3 dimensions.\");\n  Matrix rotation_matrix_deriv{Dim, Dim, 0.0};\n\n  if constexpr (Dim == 2) {\n    const std::array<DataVector, 2> angle_and_deriv = fot.func_and_deriv(t);\n    const double rotation_angle = angle_and_deriv[0][0];\n    const double rotation_angular_velocity = angle_and_deriv[1][0];\n    rotation_matrix_deriv(0, 0) =\n        -rotation_angular_velocity * sin(rotation_angle);\n    rotation_matrix_deriv(0, 1) =\n        -rotation_angular_velocity * cos(rotation_angle);\n    rotation_matrix_deriv(1, 0) =\n        rotation_angular_velocity * cos(rotation_angle);\n    rotation_matrix_deriv(1, 1) =\n        -rotation_angular_velocity * sin(rotation_angle);\n  } else {\n    const std::array<DataVector, 2> quat_and_deriv = fot.func_and_deriv(t);\n    add_bilinear_term(make_not_null(&rotation_matrix_deriv), quat_and_deriv[0],\n                      quat_and_deriv[1]);\n    add_bilinear_term(make_not_null(&rotation_matrix_deriv), quat_and_deriv[1],\n                      quat_and_deriv[0]);\n  }\n\n  return rotation_matrix_deriv;\n}\n\ntemplate Matrix rotation_matrix<2>(\n    const double, const domain::FunctionsOfTime::FunctionOfTime&);\ntemplate Matrix rotation_matrix<3>(\n    const double, const domain::FunctionsOfTime::FunctionOfTime&);\ntemplate Matrix rotation_matrix_deriv<2>(\n    const double, const domain::FunctionsOfTime::FunctionOfTime&);\ntemplate Matrix rotation_matrix_deriv<3>(\n    const double, const domain::FunctionsOfTime::FunctionOfTime&);\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines rotation matrices using quaternions.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Matrix.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\n}  // namespace domain\n/// \\endcond\n\ntemplate <size_t Dim>\nMatrix rotation_matrix(double t,\n                       const domain::FunctionsOfTime::FunctionOfTime& fot);\n\ntemplate <size_t Dim>\nMatrix rotation_matrix_deriv(\n    double t, const domain::FunctionsOfTime::FunctionOfTime& fot);\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/Shape.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <new>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <type_traits>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::CoordinateMaps::TimeDependent {\n\nusing ylm::Spherepack;\nusing ylm::SpherepackIterator;\n\nsize_t lmax_from_coefs(const DataVector& coefs) {\n  const size_t num_coefs = coefs.size();\n  const auto l_max =\n      static_cast<size_t>(sqrt(static_cast<double>(num_coefs) / 2) - 1);\n  ASSERT(square(l_max + 1) * 2 == num_coefs,\n         \"Number of shape coefficients (\"\n             << num_coefs << \") is not of valid size 2(l_max+1)(l_max+1).\");\n  ASSERT(l_max >= 2, \"l_max must be at least 2.\");\n  return l_max;\n}\n\nDataVector truncate_coefs(const DataVector& coefs, SpherepackIterator iterator,\n                          SpherepackIterator truncated_iterator) {\n  DataVector truncated_coefs(truncated_iterator.spherepack_array_size(), 0.0);\n  for (truncated_iterator.reset(); truncated_iterator; ++truncated_iterator) {\n    const size_t l = truncated_iterator.l();\n    const size_t m = truncated_iterator.m();\n    // If the requested truncation order exceeds the available coefficients,\n    // leave the entry at zero.\n    if (l > iterator.l_max() or m > iterator.m_max()) {\n      continue;\n    }\n    iterator.set(l, m, truncated_iterator.coefficient_array());\n    truncated_coefs[truncated_iterator()] = coefs[iterator()];\n  }\n  return truncated_coefs;\n}\n\ntemplate <typename T>\nstd::array<T, 2> cartesian_to_spherical(const std::array<T, 3>& cartesian) {\n  const auto& [x, y, z] = cartesian;\n  return {atan2(hypot(x, y), z), atan2(y, x)};\n}\ntemplate <typename T>\nvoid cartesian_to_spherical(gsl::not_null<std::array<T, 2>*> result,\n                            const std::array<T, 3>& cartesian) {\n  const auto& [x, y, z] = cartesian;\n  gsl::at(*result, 0) = atan2(hypot(x, y), z);\n  gsl::at(*result, 1) = atan2(y, x);\n}\n\ntemplate <typename T>\nvoid Shape::jacobian_helper(\n    gsl::not_null<tnsr::Ij<T, 3, Frame::NoFrame>*> result,\n    const ylm::Spherepack::InterpolationInfo<T>& interpolation_info,\n    const DataVector& extended_coefs, const std::array<T, 3>& centered_coords,\n    const T& radial_distortion, const T& transition_func,\n    const Spherepack& ylm) const {\n  const auto angular_gradient = ylm.gradient_from_coefs(extended_coefs);\n  tnsr::i<DataVector, 3, Frame::Inertial> cartesian_gradient(\n      ylm.physical_size());\n\n  std::array<DataVector, 2> collocation_theta_phis{};\n  collocation_theta_phis[0].set_data_ref(&get<2>(cartesian_gradient));\n  collocation_theta_phis[1].set_data_ref(&get<1>(cartesian_gradient));\n  collocation_theta_phis = ylm.theta_phi_points();\n\n  const auto& col_thetas = collocation_theta_phis[0];\n  const auto& col_phis = collocation_theta_phis[1];\n\n  // The Cartesian derivative is the Pfaffian derivative multiplied by the\n  // inverse Jacobian matrix. Some optimizations here may be possible by\n  // introducing temporaries for some of the sin/cos which are computed twice,\n  // if the compiler CSE doesn't take care of it.\n  get<0>(cartesian_gradient) =\n      (cos(col_thetas) * cos(col_phis) * get<0>(angular_gradient) -\n       sin(col_phis) * get<1>(angular_gradient));\n\n  get<1>(cartesian_gradient) =\n      (cos(col_thetas) * sin(col_phis) * get<0>(angular_gradient) +\n       cos(col_phis) * get<1>(angular_gradient));\n\n  get<2>(cartesian_gradient) = -sin(col_thetas) * get<0>(angular_gradient);\n\n  // re-use allocations. The specific buffers that are reused are important to\n  // avoid overwriting anything\n  std::array<T, 3> target_gradient{};\n  for (size_t i = 0; i < 3; i++) {\n    if constexpr (std::is_same_v<T, DataVector>) {\n      gsl::at(target_gradient, i)\n          .set_data_ref(make_not_null(&result->get(2, i)));\n    } else {\n      gsl::at(target_gradient, i) = result->get(2, i);\n    }\n\n    // interpolate the cartesian gradient to the thetas and phis of the\n    // `source_coords`\n    ylm.interpolate(make_not_null(&gsl::at(target_gradient, i)),\n                    cartesian_gradient.get(i).data(), interpolation_info);\n  }\n\n  // G / r\n  auto transition_func_over_radius =\n      transition_func_->operator()(centered_coords, {1});\n  auto transition_func_gradient_times_distortion =\n      transition_func_->gradient(centered_coords) * radial_distortion;\n\n  auto& target_gradient_times_spatial_part = target_gradient;\n  target_gradient_times_spatial_part *= transition_func_over_radius;\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      result->get(i, j) =\n          -gsl::at(centered_coords, i) *\n          (gsl::at(transition_func_gradient_times_distortion, j) +\n           gsl::at(target_gradient_times_spatial_part, j));\n    }\n\n    result->get(i, i) += 1.0 - radial_distortion * transition_func;\n  }\n}\n\nShape::Shape(\n    const std::array<double, 3>& center, const double truncation_limit,\n    std::unique_ptr<ShapeMapTransitionFunctions::ShapeMapTransitionFunction>\n        transition_func,\n    std::string shape_function_of_time_name,\n    std::optional<std::string> size_function_of_time_name)\n    : shape_f_of_t_name_(std::move(shape_function_of_time_name)),\n      size_f_of_t_name_(std::move(size_function_of_time_name)),\n      center_(center),\n      truncation_limit_(truncation_limit),\n      transition_func_(std::move(transition_func)) {\n  f_of_t_names_.insert(shape_f_of_t_name_);\n  if (size_f_of_t_name_.has_value()) {\n    f_of_t_names_.insert(size_f_of_t_name_.value());\n  }\n}\n\nShape& Shape::operator=(const Shape& rhs) {\n  if (*this != rhs) {\n    shape_f_of_t_name_ = rhs.shape_f_of_t_name_;\n    size_f_of_t_name_ = rhs.size_f_of_t_name_;\n    f_of_t_names_ = rhs.f_of_t_names_;\n    center_ = rhs.center_;\n    truncation_limit_ = rhs.truncation_limit_;\n    transition_func_ = rhs.transition_func_ != nullptr\n                           ? rhs.transition_func_->get_clone()\n                           : nullptr;\n    // we manually call the destructor and constructor here in case the cache\n    // needs to be resized. It is up to the user to guarantee that no thread is\n    // accessing the cache at this point.\n    spherepack_cache_.~SpherepackCache();\n    new (&spherepack_cache_) SpherepackCache{cache_capacity_};\n  }\n  return *this;\n}\n\nShape::Shape(const Shape& rhs)\n    : shape_f_of_t_name_(rhs.shape_f_of_t_name_),\n      size_f_of_t_name_(rhs.size_f_of_t_name_),\n      f_of_t_names_(rhs.f_of_t_names_),\n      center_(rhs.center_),\n      truncation_limit_(rhs.truncation_limit_),\n      transition_func_(rhs.transition_func_ != nullptr\n                           ? rhs.transition_func_->get_clone()\n                           : nullptr),\n      spherepack_cache_(cache_capacity_) {}\n\nShape::Shape(Shape&& rhs)\n    : shape_f_of_t_name_(std::move(rhs.shape_f_of_t_name_)),\n      size_f_of_t_name_(std::move(rhs.size_f_of_t_name_)),\n      f_of_t_names_(std::move(rhs.f_of_t_names_)),\n      center_(std::move(rhs.center_)),\n      truncation_limit_(rhs.truncation_limit_),\n      transition_func_(std::move(rhs.transition_func_)),\n      spherepack_cache_(cache_capacity_) {}\n\nShape& Shape::operator=(Shape&& rhs) {\n  if (this != &rhs) {\n    shape_f_of_t_name_ = std::move(rhs.shape_f_of_t_name_);\n    size_f_of_t_name_ = std::move(rhs.size_f_of_t_name_);\n    f_of_t_names_ = std::move(rhs.f_of_t_names_);\n    center_ = std::move(rhs.center_);\n    truncation_limit_ = rhs.truncation_limit_;\n    transition_func_ = std::move(rhs.transition_func_);\n    // we manually call the destructor and constructor here in case the cache\n    // needs to be resized. It is up to the user to guarantee that no thread is\n    // accessing the cache at this point.\n    spherepack_cache_.~SpherepackCache();\n    new (&spherepack_cache_) SpherepackCache{cache_capacity_};\n  }\n  return *this;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> Shape::operator()(\n    const std::array<T, 3>& source_coords, const double time,\n    const FunctionsOfTimeMap& functions_of_time) const {\n  const auto centered_coords = center_coordinates(source_coords);\n  auto theta_phis = cartesian_to_spherical(centered_coords);\n  const auto [coefs, coef_derivs, coef_dderivs] =\n      functions_of_time.at(shape_f_of_t_name_)->func_and_2_derivs(time);\n  const size_t l_max = lmax_from_coefs(coefs);\n  const SpherepackIterator full_iterator{l_max, l_max};\n  const size_t truncated_l_max =\n      find_truncated_l_max(coefs, coef_derivs, coef_dderivs, full_iterator);\n  const auto cached_item = get_spherepack_cache_entry(truncated_l_max);\n  const auto& [truncated_iterator, ylm] = *cached_item.value();\n  DataVector truncated_coefs =\n      truncate_coefs(coefs, full_iterator, truncated_iterator);\n  check_size(make_not_null(&truncated_coefs), functions_of_time, time, false);\n  // re-use allocation\n  const auto interpolation_info = ylm.set_up_interpolation_info(theta_phis);\n  auto& radial_distortion = get<0>(theta_phis);\n  // evaluate the spherical harmonic expansion at the angles of\n  // `source_coords`\n  ylm.interpolate_from_coefs(make_not_null(&radial_distortion), truncated_coefs,\n                             interpolation_info);\n\n  // this should be taken care of by the control system but is very hard to\n  // debug\n#ifdef SPECTRE_DEBUG\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType shift_radii =\n      radial_distortion *\n      transition_func_->operator()(centered_coords, std::nullopt);\n  if constexpr (std::is_same_v<ReturnType, double>) {\n    ASSERT(shift_radii < 1., \"Coordinates mapped through the center!\");\n  } else {\n    for (const auto& radius : shift_radii) {\n      ASSERT(radius < 1., \"Coordinates mapped through the center!\");\n    }\n  }\n#endif  // SPECTRE_DEBUG\n\n  return center_ +\n         centered_coords *\n             (1. - radial_distortion * transition_func_->operator()(\n                                           centered_coords, std::nullopt));\n}\n\nstd::optional<std::array<double, 3>> Shape::inverse(\n    const std::array<double, 3>& target_coords, const double time,\n    const FunctionsOfTimeMap& functions_of_time) const {\n  const std::array<double, 3> centered_coords =\n      center_coordinates(target_coords);\n  const std::array<double, 2> theta_phis =\n      cartesian_to_spherical(centered_coords);\n  const auto [coefs, coef_derivs, coef_dderivs] =\n      functions_of_time.at(shape_f_of_t_name_)->func_and_2_derivs(time);\n  const size_t l_max = lmax_from_coefs(coefs);\n  const SpherepackIterator full_iterator{l_max, l_max};\n  const size_t truncated_l_max =\n      find_truncated_l_max(coefs, coef_derivs, coef_dderivs, full_iterator);\n  double radial_distortion = 0.0;\n  // extra guard to minimize lifetime of cached_item which has a lock\n  {\n    const auto cached_item = get_spherepack_cache_entry(truncated_l_max);\n    const auto& [truncated_iterator, ylm] = *cached_item.value();\n    DataVector truncated_coefs =\n        truncate_coefs(coefs, full_iterator, truncated_iterator);\n    check_size(make_not_null(&truncated_coefs), functions_of_time, time, false);\n    radial_distortion = ylm.interpolate_from_coefs(truncated_coefs, theta_phis);\n  }\n  const std::optional<double> original_radius_over_radius =\n      transition_func_->original_radius_over_radius(centered_coords,\n                                                    radial_distortion);\n  if (not original_radius_over_radius.has_value()) {\n    return std::nullopt;\n  }\n  return center_ + centered_coords * original_radius_over_radius.value();\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> Shape::frame_velocity(\n    const std::array<T, 3>& source_coords, const double time,\n    const FunctionsOfTimeMap& functions_of_time) const {\n  const auto centered_coords = center_coordinates(source_coords);\n  auto theta_phis = cartesian_to_spherical(centered_coords);\n  const auto [coefs, coef_derivs, coef_dderivs] =\n      functions_of_time.at(shape_f_of_t_name_)->func_and_2_derivs(time);\n  const size_t l_max = lmax_from_coefs(coefs);\n  const SpherepackIterator full_iterator{l_max, l_max};\n  const size_t truncated_l_max =\n      find_truncated_l_max(coefs, coef_derivs, coef_dderivs, full_iterator);\n  const auto cached_item = get_spherepack_cache_entry(truncated_l_max);\n  const auto& [truncated_iterator, ylm] = *cached_item.value();\n  DataVector truncated_coef_derivs =\n      truncate_coefs(coef_derivs, full_iterator, truncated_iterator);\n  check_size(make_not_null(&truncated_coef_derivs), functions_of_time, time,\n             true);\n  const auto interpolation_info = ylm.set_up_interpolation_info(theta_phis);\n  // re-use allocation\n  auto& radii_velocities = get<0>(theta_phis);\n  ylm.interpolate_from_coefs(make_not_null(&radii_velocities),\n                             truncated_coef_derivs, interpolation_info);\n  return -centered_coords * radii_velocities *\n         transition_func_->operator()(centered_coords, std::nullopt);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> Shape::jacobian(\n    const std::array<T, 3>& source_coords, const double time,\n    const FunctionsOfTimeMap& functions_of_time) const {\n  const auto centered_coords = center_coordinates(source_coords);\n\n  // The distorted radii are calculated analogously to the call operator\n  auto theta_phis = cartesian_to_spherical(centered_coords);\n  const auto [coefs, coef_derivs, coef_dderivs] =\n      functions_of_time.at(shape_f_of_t_name_)->func_and_2_derivs(time);\n  const size_t l_max = lmax_from_coefs(coefs);\n  const SpherepackIterator full_iterator{l_max, l_max};\n  size_t truncated_l_max =\n      find_truncated_l_max(coefs, coef_derivs, coef_dderivs, full_iterator);\n  // we need an additional l_max to compute the gradient without aliasing error\n  truncated_l_max += 1;\n  const auto cached_item = get_spherepack_cache_entry(truncated_l_max);\n  const auto& [truncated_iterator, ylm] = *cached_item.value();\n\n  DataVector truncated_coefs =\n      truncate_coefs(coefs, full_iterator, truncated_iterator);\n  check_size(make_not_null(&truncated_coefs), functions_of_time, time, false);\n  const auto interpolation_info = ylm.set_up_interpolation_info(theta_phis);\n\n  // Re-use allocation\n  auto& radial_distortion = get<0>(theta_phis);\n  ylm.interpolate_from_coefs(make_not_null(&radial_distortion), truncated_coefs,\n                             interpolation_info);\n\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType transition_func =\n      transition_func_->operator()(centered_coords, std::nullopt);\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> result(\n      get_size(centered_coords[0]));\n\n  jacobian_helper(make_not_null(&result), interpolation_info, truncated_coefs,\n                  centered_coords, radial_distortion, transition_func, ylm);\n  return result;\n}\n\nvoid Shape::coords_frame_velocity_jacobian(\n    gsl::not_null<std::array<DataVector, 3>*> source_and_target_coords,\n    gsl::not_null<std::array<DataVector, 3>*> frame_vel,\n    gsl::not_null<tnsr::Ij<DataVector, 3, Frame::NoFrame>*> jac, double time,\n    const FunctionsOfTimeMap& functions_of_time) const {\n  const size_t size = get<0>(*source_and_target_coords).size();\n  ASSERT(size > 0,\n         \"The source coords have size 0 but the argument requires you to pass \"\n         \"in the coordinates.\");\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(*frame_vel, i).destructive_resize(size);\n    for (size_t j = 0; j < 3; ++j) {\n      jac->get(i, j).destructive_resize(size);\n    }\n  }\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempI<0, 3, Frame::Inertial>>>\n      temps(size);\n\n  std::array<DataVector, 3> centered_coords{};\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(centered_coords, i)\n        .set_data_ref(&get<::Tags::TempI<0, 3, Frame::Inertial>>(temps).get(i));\n  }\n  center_coordinates(make_not_null(&centered_coords),\n                     *source_and_target_coords);\n\n  std::array<DataVector, 2> theta_phis{};\n  theta_phis[0].set_data_ref(&get<0, 0>(*jac));\n  theta_phis[1].set_data_ref(&get<0, 1>(*jac));\n  cartesian_to_spherical(make_not_null(&theta_phis), centered_coords);\n\n  const auto [coefs, coef_derivs, coef_dderivs] =\n      functions_of_time.at(shape_f_of_t_name_)->func_and_2_derivs(time);\n  const size_t l_max = lmax_from_coefs(coefs);\n  const SpherepackIterator full_iterator{l_max, l_max};\n  size_t truncated_l_max =\n      find_truncated_l_max(coefs, coef_derivs, coef_dderivs, full_iterator);\n  // we need an additional l_max to compute the gradient without aliasing error\n  truncated_l_max += 1;\n  const auto cached_item = get_spherepack_cache_entry(truncated_l_max);\n  const auto& [truncated_iterator, ylm] = *cached_item.value();\n  DataVector truncated_coefs =\n      truncate_coefs(coefs, full_iterator, truncated_iterator);\n  DataVector truncated_coef_derivs =\n      truncate_coefs(coef_derivs, full_iterator, truncated_iterator);\n  check_size(make_not_null(&truncated_coefs), functions_of_time, time, false);\n  check_size(make_not_null(&truncated_coef_derivs), functions_of_time, time,\n             true);\n  const auto interpolation_info = ylm.set_up_interpolation_info(theta_phis);\n  auto& radial_distortion = get(get<::Tags::TempScalar<0>>(temps));\n  // evaluate the spherical harmonic expansion at the angles of\n  // `source_coords`\n  ylm.interpolate_from_coefs(make_not_null(&radial_distortion), truncated_coefs,\n                             interpolation_info);\n\n  auto& transition_func = get(get<::Tags::TempScalar<1>>(temps));\n  transition_func = transition_func_->operator()(centered_coords, std::nullopt);\n  *source_and_target_coords =\n      center_ + centered_coords * (1. - radial_distortion * transition_func);\n\n  auto& radii_velocities = get<0, 1>(*jac);\n  ylm.interpolate_from_coefs(make_not_null(&radii_velocities),\n                             truncated_coef_derivs, interpolation_info);\n  *frame_vel = -centered_coords * radii_velocities * transition_func;\n\n  jacobian_helper<DataVector>(jac, interpolation_info, truncated_coefs,\n                              centered_coords, radial_distortion,\n                              transition_func, ylm);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> Shape::inv_jacobian(\n    const std::array<T, 3>& source_coords, const double time,\n    const FunctionsOfTimeMap& functions_of_time) const {\n  return determinant_and_inverse(\n             jacobian(source_coords, time, functions_of_time))\n      .second;\n}\n\nsize_t Shape::find_truncated_l_max(const DataVector& coefs,\n                                   const DataVector& coef_derivs,\n                                   const DataVector& coef_dderivs,\n                                   SpherepackIterator iterator) const {\n  const size_t l_max = iterator.l_max();\n  // we require at least l=2 because l=0 is size and l=1 is translation\n  for (size_t l = l_max; l > 2; --l) {\n    for (int m = static_cast<int>(l); m >= -static_cast<int>(l); --m) {\n      iterator.set(l, m);\n      const size_t current_index = iterator();\n      if (abs(coefs[current_index]) > truncation_limit_ or\n          abs(coef_derivs[current_index]) > truncation_limit_ or\n          abs(coef_dderivs[current_index]) > truncation_limit_) {\n        return l;\n      }\n    }\n  }\n  return 2;\n}\n\nvoid Shape::check_size(const gsl::not_null<DataVector*>& coefs,\n                       const FunctionsOfTimeMap& functions_of_time,\n                       const double time, const bool use_deriv) const {\n  if (size_f_of_t_name_.has_value()) {\n    ASSERT((*coefs)[0] == 0.0,\n           \"When using a size function of time, the l=0 \"\n               << (use_deriv ? \"derivative\" : \"component\")\n               << \" of the shape \"\n                  \"function of time must be zero. Currently it is \"\n               << (*coefs)[0]);\n\n    double l0m0_spherical_harmonic_coef =\n        std::numeric_limits<double>::signaling_NaN();\n    if (use_deriv) {\n      l0m0_spherical_harmonic_coef =\n          functions_of_time.at(size_f_of_t_name_.value())\n              ->func_and_deriv(time)[1][0];\n    } else {\n      l0m0_spherical_harmonic_coef =\n          functions_of_time.at(size_f_of_t_name_.value())->func(time)[0][0];\n    }\n\n    // Size holds the *actual* \\lambda_00 spherical harmonic coefficient, but\n    // shape holds Spherepack coefficients so we must convert between the two.\n    // Need to multiply lambda_00 by sqrt(2/pi)\n    (*coefs)[0] = M_SQRT1_2 * M_2_SQRTPI * l0m0_spherical_harmonic_coef;\n  }\n}\n\nShape::CachedItem Shape::get_spherepack_cache_entry(const size_t l_max) const {\n  auto predicate =\n      [&l_max](\n          const std::unique_ptr<Shape::SpherepackEntry>& cached_entry) -> bool {\n    return cached_entry->first.l_max() == l_max;\n  };\n  // we expect few cache misses, so use find first to avoid locking the cache\n  auto cached_entry = spherepack_cache_.find(predicate);\n  if (cached_entry.has_value()) {\n    return cached_entry;\n  }\n  auto compute_new_entry = [&l_max]() {\n    return std::make_unique<Shape::SpherepackEntry>(\n        Shape::SpherepackEntry{{l_max, l_max}, {l_max, l_max}});\n  };\n  auto new_entry = spherepack_cache_.push(compute_new_entry, predicate);\n  ASSERT(new_entry.has_value(), \"FifoCache push failed to return a value.\");\n  return new_entry;\n}\n\nbool operator==(const Shape& lhs, const Shape& rhs) {\n  return lhs.shape_f_of_t_name_ == rhs.shape_f_of_t_name_ and\n         lhs.size_f_of_t_name_ == rhs.size_f_of_t_name_ and\n         lhs.center_ == rhs.center_ and\n         lhs.truncation_limit_ == rhs.truncation_limit_ and\n         (lhs.transition_func_ == nullptr) ==\n             (rhs.transition_func_ == nullptr) and\n         ((lhs.transition_func_ == nullptr and\n           rhs.transition_func_ == nullptr) or\n          *lhs.transition_func_ == *rhs.transition_func_);\n}\n\nbool operator!=(const Shape& lhs, const Shape& rhs) { return not(lhs == rhs); }\n\nvoid Shape::pup(PUP::er& p) {\n  size_t version = 1;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version == 0) {\n    size_t old_l_max = 0;\n    size_t old_m_max = 0;\n    p | old_l_max;\n    p | old_m_max;\n  }\n  p | center_;\n  p | shape_f_of_t_name_;\n  p | size_f_of_t_name_;\n  p | transition_func_;\n\n  if (version >= 1) {\n    p | truncation_limit_;\n  }\n\n  // No need to pup these because they are uniquely determined by other\n  // members\n  if (p.isUnpacking()) {\n    if (version == 0) {\n      truncation_limit_ = 0.;\n    }\n    f_of_t_names_.clear();\n    f_of_t_names_.insert(shape_f_of_t_name_);\n    if (size_f_of_t_name_.has_value()) {\n      f_of_t_names_.insert(size_f_of_t_name_.value());\n    }\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                                  \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                \\\n  Shape::operator()(const std::array<DTYPE(data), 3>& source_coords,          \\\n                    double time, const FunctionsOfTimeMap& functions_of_time) \\\n      const;                                                                  \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                \\\n  Shape::frame_velocity(const std::array<DTYPE(data), 3>& source_coords,      \\\n                        double time,                                          \\\n                        const FunctionsOfTimeMap& functions_of_time) const;   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>  \\\n  Shape::jacobian(const std::array<DTYPE(data), 3>& source_coords,            \\\n                  double time, const FunctionsOfTimeMap& functions_of_time)   \\\n      const;                                                                  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>  \\\n  Shape::inv_jacobian(const std::array<DTYPE(data), 3>& source_coords,        \\\n                      double time,                                            \\\n                      const FunctionsOfTimeMap& functions_of_time) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/Shape.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"Parallel/FifoCache.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps::TimeDependent {\n\nsize_t lmax_from_coefs(const DataVector& coefs);\n\nDataVector truncate_coefs(const DataVector& coefs,\n                          ylm::SpherepackIterator iterator,\n                          ylm::SpherepackIterator truncated_iterator);\n\n/*!\n * \\ingroup CoordMapsTimeDependentGroup\n * \\brief Distorts a distribution of points radially according to a spherical\n * harmonic expansion while preserving angles.\n *\n * \\details The shape map distorts the distance \\f$r\\f$ between a point and\n * the center while leaving the angles \\f$\\theta\\f$, \\f$\\phi\\f$ between them\n * preserved by applying a spherical harmonic expansion with time-dependent\n * coefficients \\f$\\lambda_{lm}(t)\\f$. There are two ways to specify the\n * time-dependent coefficients \\f$\\lambda_{lm}(t)\\f$:\n *\n * 1. A single FunctionOfTime which specifies all coefficients. This\n *    FunctionOfTime should have `ylm::Spherepack::spectral_size()` number of\n *    components. These are in Spherepack order and should be the Spherepack\n *    coefficients, *not* the spherical harmonic coefficients. See the note\n *    below. To use this, set the `size_function_of_time_name` argument of the\n *    constructor to `std::nullopt`.\n * 2. Two different FunctionOfTime%s. The first is similar to 1.) in that it\n *    should have the same number of components, be in Spherepack order, and be\n *    the Spherepack coefficients. The only difference is that the \\f$l = 0\\f$\n *    coefficient should be identically 0. The second FunctionOfTime should have\n *    a single component which will be the \\f$l = 0\\f$ coefficient. This\n *    component should be stored as the spherical harmonic coefficient and *not*\n *    a Spherepack coefficient. See the note below. To use this method, set the\n *    `size_function_of_time_name` argument of the constructor to the name of\n *    the FunctionOfTime that's in the cache. This method is useful if we have\n *    control systems because we have a separate control system controlling a\n *    separate function of time for the \\f$l = 0\\f$ coefficient than we do for\n *    the other coefficients.\n *\n * \\note The quantities stored in the \"shape\" FunctionOfTime (the\n * `shape_function_of_time_name` argument in the constructor that must always be\n * specified) are ***not*** the complex spherical-harmonic coefficients\n * \\f$\\lambda_{lm}(t)\\f$, but instead are the real-valued SPHEREPACK\n * coefficients \\f$a_{lm}(t)\\f$ and \\f$b_{lm}(t)\\f$ used by Spherepack. This\n * is the same for both methods of specifying FunctionOfTime%s above. The\n * relationship between these two sets of coefficients is\n * \\f{align}\n * a_{l0} & = \\sqrt{\\frac{2}{\\pi}}\\lambda_{l0}&\\qquad l\\geq 0,\\\\\n * a_{lm} & = (-1)^m\\sqrt{\\frac{2}{\\pi}} \\mathrm{Re}(\\lambda_{lm})\n * &\\qquad l\\geq 1, m\\geq 1, \\\\\n * b_{lm} & = (-1)^m\\sqrt{\\frac{2}{\\pi}} \\mathrm{Im}(\\lambda_{lm})\n * &\\qquad l\\geq 1, m\\geq 1.\n * \\f}\n * The \"shape\" FunctionOfTime stores coefficients only for non-negative \\f$m\\f$;\n * this is because the function we are expanding is real, so the\n * coefficients for \\f$m<0\\f$ can be obtained from \\f$m>0\\f$ coefficients by\n * complex conjugation.\n * If the `size_function_of_time_name` argument is given to the constructor,\n * then it is asserted that the \\f$l=0\\f$ coefficient of the \"shape\" function of\n * time is exactly 0. The \\f$l=0\\f$ coefficient is then controlled by the \"size\"\n * FunctionOfTime. Unlike the \"shape\" FunctionOfTime, the quantity in the\n * \"size\" FunctionOfTime ***is*** the \"complex\" spherical harmonic coefficient\n * \\f$\\lambda_{00}(t)\\f$, and not the SPHEREPACK coefficient \\f$a_{00}(t)\\f$\n * (\"complex\" is in quotes because all \\f$m=0\\f$ coefficients are always real.)\n * Here and below we write the equations in terms of \\f$\\lambda_{lm}(t)\\f$\n * instead of \\f$a_{lm}(t)\\f$ and \\f$b_{lm}(t)\\f$, regardless of which\n * FunctionOfTime representation we are using, because the resulting expressions\n * are much shorter.\n *\n * \\parblock\n *\n * \\note Also note that the FunctionOfTime coefficients $\\lambda_{lm}(t)$ are\n * stored as *negative* of the coefficients you'd retrieve from a\n * `Strahlkorper`. This is because you would typically represent the expansion\n * of a strahlkorper as $S(r) = +\\sum S_{lm} Y_{lm}$. However, in equation\n * $\\ref{eq:map_form_2}$ there is a minus sign on the $\\sum \\lambda_{lm}\n * Y_{lm}$, not a plus sign. Therefore, $\\lambda_{lm}(t)$ picks up an extra\n * factor of $-1$. This is purely a choice of convention.\n *\n * \\endparblock\n *\n * An additional domain-dependent transition function\n *\n * \\begin{equation}\n *     G(r,\\theta,\\phi) = \\frac{f(r,\\theta,\\phi)}{r}\n * \\end{equation}\n *\n * ensures that the distortion falls off correctly to zero at a certain boundary\n * (must be a block boundary). The dimensionless function \\f$f(r, \\theta,\n * \\phi)\\f$ is restricted such that\n *\n * \\f{equation}{\n * 0 \\leq f(r, \\theta, \\phi) \\leq 1\n * \\f}\n *\n * ### Mapped coordinates\n *\n * Given a point with cartesian coordinates \\f$\\xi^i\\f$, let the polar\n * coordinates \\f$(r, \\theta, \\phi)\\f$ with respect to a center \\f$x_c^i\\f$ be\n * defined in the usual way:\n *\n * \\f{align}{\n * \\xi^0 - x_c^0 &= r \\sin(\\theta) \\cos(\\phi)\\\\\n * \\xi^1 - x_c^1 &= r \\sin(\\theta) \\sin(\\phi)\\\\\n * \\xi^2 - x_c^2 &= r \\cos(\\theta)\n * \\f}\n *\n * The shape map maps the unmapped\n * coordinates \\f$\\xi^i\\f$ to coordinates \\f$x^i\\f$:\n *\n * \\f{equation}{\\label{eq:map_form_1}\n * x^i = \\xi^i - (\\xi^i - x_c^i) G(r,\\theta,\\phi) \\sum_{lm}\n * \\lambda_{lm}(t)Y_{lm}(\\theta, \\phi).\n * \\f}\n *\n * Or written another way\n *\n * \\f{equation}{\\label{eq:map_form_2}\n * x^i = x_c^i + (\\xi^i - x_c^i) \\left(1 - G(r,\\theta,\\phi)\n * \\sum_{lm} \\lambda_{lm}(t)Y_{lm}(\\theta, \\phi)\\right).\n * \\f}\n *\n * The form in Eq. \\f$\\ref{eq:map_form_2}\\f$ makes two things\n * clearer\n *\n * 1. This shape map is just a radial distortion about \\f$x_c^i\\f$\n * 2. The coefficients \\f$\\lambda_{lm}\\f$ have units of distance because\n *    \\f$\\sum\\lambda_{lm}(t)Y_{lm}(\\theta,\\phi) / r\\f$ must be dimensionless\n *    (because \\f$f\\f$ is dimensionless).\n *\n * ### Inverse map\n *\n * The inverse map is given by:\n * \\f{equation}{\n * \\xi^i = x_c^i + (x^i-x_c^i)*(r/\\tilde{r}),\n * \\f}\n * where \\f$\\tilde{r}\\f$ is the radius of $\\vec{x}$, calculated by the\n * transition map. In order to compute $r/\\tilde{r}$, the following equation\n * must be solved\n *\n * \\f{equation}{\n * \\frac{r}{\\tilde{r}} =\n * \\frac{1}{1-G(r,\\theta,\\phi)\\sum\\lambda_{lm}(t)Y_{lm}(\\theta,\\phi)}\n * \\f}\n *\n * For more details, see\n * \\link domain::CoordinateMaps::ShapeMapTransitionFunctions::ShapeMapTransitionFunction::original_radius_over_radius\n * ShapeMapTransitionFunction::original_radius_over_radius \\endlink.\n *\n * ### Frame velocity\n *\n * The frame velocity \\f$v^i\\ = dx^i / dt\\f$ is calculated trivially:\n * \\f{equation}{\n * v^i = - (\\xi^i - x_c^i) G(r, \\theta, \\phi) \\sum_{lm}\n * \\dot{\\lambda}_{lm}(t)Y_{lm}(\\theta, \\phi).\n * \\f}\n *\n * ### Jacobian\n *\n * The Jacobian is given by:\n * \\f{align}{\n * \\frac{\\partial x^i}{\\partial \\xi^j} = \\delta_j^i &\\left( 1 - G(r,\\theta,\\phi)\n * \\sum_{lm} \\lambda_{lm}(t)Y_{lm}(\\theta, \\phi)\\right) \\nonumber \\\\\n * &- (\\xi^i - x_c^i)\n * \\left[\\frac{\\partial G(r,\\theta,\\phi)}{\\partial\\xi^j} \\sum_{lm}\n * \\lambda_{lm}(t)Y_{lm}(\\theta, \\phi) + G(r, \\theta, \\phi)\n * \\sum_{lm} \\lambda_{lm}(t) \\frac{\\partial}{\\partial \\xi^j} Y_{lm}(\\theta,\n * \\phi) \\right].\n * \\f}\n *\n * where \\f$\\xi_j = \\xi^j\\f$. It should be noted that there is an additional\n * factor of $1/r$ hidden in the $\\partial/\\partial\\xi^j Y_{lm}(\\theta, \\phi)$\n * term, so the transition function $G(r,\\theta,\\phi)$ must have a functional\n * form to avoid division by zero if $r=0$.\n *\n * ### Inverse Jacobian\n *\n * The inverse Jacobian is computed by numerically inverting the Jacobian.\n *\n * For future optimization, the `interpolation_info` objects calculated in all\n * functions of this class could be cached. Since every element should evaluate\n * the same grid coordinates most time steps, this might greatly decrease\n * computation. Every element has their own clone of the shape map so the\n * caching could be done with member variables. Care must be taken that\n * `jacobian` currently calculates the `interpolation_info` with an order\n * higher.\n *\n * \\warning The Shape map uses a mutable spherepack cache that can be read and\n * mutated concurrently. It is up to the user to guarantee that\n * the cache is in a valid state while threads are calling the map methods.\n * Special care needs to be taken when using move and copy assignment.\n */\nclass Shape {\n public:\n  using FunctionsOfTimeMap = std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;\n\n  explicit Shape(\n      const std::array<double, 3>& center, double truncation_limit,\n      std::unique_ptr<ShapeMapTransitionFunctions::ShapeMapTransitionFunction>\n          transition_func,\n      std::string shape_function_of_time_name,\n      std::optional<std::string> size_function_of_time_name = std::nullopt);\n\n  Shape() = default;\n  ~Shape() = default;\n  Shape(const Shape& rhs);\n  Shape& operator=(const Shape& rhs);\n  Shape(Shape&& rhs);\n  Shape& operator=(Shape&& rhs);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> frame_velocity(\n      const std::array<T, 3>& source_coords, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  /*!\n   * \\brief An optimized call that computes the target coordinates, frame\n   * velocity and jacobian at once to avoid duplicate calculations.\n   *\n   * \\details The first argument `source_and_target_coords` should contain\n   * the source coordinates and will be overwritten in place with the target\n   * coordinates.\n   */\n  void coords_frame_velocity_jacobian(\n      gsl::not_null<std::array<DataVector, 3>*> source_and_target_coords,\n      gsl::not_null<std::array<DataVector, 3>*> frame_vel,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::NoFrame>*> jac, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n  static bool is_identity() { return false; }\n  static constexpr bool supports_hessian{false};\n  static constexpr size_t dim = 3;\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  std::string shape_f_of_t_name_;\n  std::optional<std::string> size_f_of_t_name_;\n  std::unordered_set<std::string> f_of_t_names_;\n  std::array<double, 3> center_{};\n  double truncation_limit_{0.};\n  std::unique_ptr<ShapeMapTransitionFunctions::ShapeMapTransitionFunction>\n      transition_func_;\n\n  using SpherepackEntry = std::pair<ylm::SpherepackIterator, ylm::Spherepack>;\n  using SpherepackCache = Parallel::FifoCache<std::unique_ptr<SpherepackEntry>>;\n  using CachedItem = SpherepackCache::Cached;\n  static constexpr size_t cache_capacity_ = 5;\n\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable SpherepackCache spherepack_cache_{cache_capacity_};\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> center_coordinates(\n      const std::array<T, 3>& coords) const {\n    return {coords[0] - center_[0], coords[1] - center_[1],\n            coords[2] - center_[2]};\n  }\n\n  template <typename T>\n  void center_coordinates(\n      gsl::not_null<std::array<tt::remove_cvref_wrap_t<T>, 3>*> result,\n      const std::array<T, 3>& coords) const {\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(*result, i) = gsl::at(coords, i) - gsl::at(center_, i);\n    }\n  }\n\n  template <typename T>\n  void jacobian_helper(\n      gsl::not_null<tnsr::Ij<T, 3, Frame::NoFrame>*> result,\n      const ylm::Spherepack::InterpolationInfo<T>& interpolation_info,\n      const DataVector& extended_coefs, const std::array<T, 3>& centered_coords,\n      const T& radial_distortion, const T& transition_func,\n      const ylm::Spherepack& ylm) const;\n\n  void check_size(const gsl::not_null<DataVector*>& coefs,\n                  const FunctionsOfTimeMap& functions_of_time, double time,\n                  bool use_deriv) const;\n\n  size_t find_truncated_l_max(const DataVector& coefs,\n                              const DataVector& coef_derivs,\n                              const DataVector& coef_dderivs,\n                              ylm::SpherepackIterator iterator) const;\n\n  CachedItem get_spherepack_cache_entry(size_t l_max) const;\n\n  friend bool operator==(const Shape& lhs, const Shape& rhs);\n};\nbool operator!=(const Shape& lhs, const Shape& rhs);\n\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  RegisterDerivedWithCharm.cpp\n  SphereTransition.cpp\n  Wedge.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  RegisterDerivedWithCharm.hpp\n  ShapeMapTransitionFunction.hpp\n  SphereTransition.hpp\n  Wedge.hpp\n)\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/RegisterDerivedWithCharm.hpp\"\n\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Wedge.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\nvoid register_derived_with_charm() {\n  register_classes_with_charm<SphereTransition, Wedge>();\n}\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\nvoid register_derived_with_charm();\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\n\n/*!\n * \\brief Abstract base class for the transition function $G(r,\\theta,\\phi)$\n * used by the domain::CoordinateMaps::TimeDependent::Shape map.\n *\n * \\details This base class defines the required methods of a transition\n * function that is defined as\n *\n * \\begin{equation}\n * G(r,\\theta,\\phi) = \\frac{f(r,\\theta,\\phi)}{r}\n * \\end{equation}\n *\n * where $0 <= f(r,\\theta,\\phi) <= 1$. Different domains may require the shape\n * map to fall off towards the boundary in different ways. This behavior is\n * controlled by the transition function. It is also needed to find the inverse\n * of the shape map. Since the shape map preserves angles, the problem of\n * finding its inverse reduces to the 1-dimensional problem of finding the\n * original radius from the mapped radius. The mapped radius $\\tilde{r}$ is\n * related to the original $r$ radius by:\n * \\begin{equation}\n * \\label{eq:shape_map_radius}\n * \\tilde{r} = r \\left(1 - G(r,\\theta,\\phi)\\sum_{lm}\n *     \\lambda_{lm}(t)Y_{lm}(\\theta,\\phi)\\right),\n * \\end{equation}\n * Depending on the format of the transition function, it should be possible to\n * analytically derive this map's inverse because it preserves angles and shifts\n * only the radius of each point. Otherwise the inverse has to be computed\n * numerically.\n *\n * The transition function must also be able to compute the gradient and the\n * value of the function divided by the radius. Care must be taken that this\n * does not divide by zero.\n *\n * All member functions with the exception of `original_radius_over_radius`\n * exist as overloads for types `double` and `DataVector` so that they work with\n * the templated shape map methods calling them. To avoid code duplication these\n * can be forwarded to templated implementation methods held by the derived\n * classes only.\n *\n * For an example, see SphereTransition.\n *\n * #### Design Decisions:\n *\n * It was decided to make the ShapeMapTransitionFunction an abstract base class\n * with overloads for types `double` and `DataVector` corresponding to the\n * template parameter `T` of the shape map's methods. The shape map holds a\n * `unique_ptr` to this abstract class using a common dynamic dispatch design\n * pattern. This approach avoids templating the shape map all together.\n *\n * An alternative approach would be to directly template the transition\n * functions onto the shape map so that no abstract base class is necessary.\n * These approaches can also be combined by making the transition function an\n * abstract base class but also templating it onto the shape map. In this way\n * the shape map does not need to hold a `unique_ptr` but can hold the\n * transition function directly as a member.\n */\nclass ShapeMapTransitionFunction : public PUP::able {\n public:\n  ShapeMapTransitionFunction() = default;\n\n  /// @{\n  /*!\n   * Evaluate the transition function $G(r,\\theta,\\phi)$ at the\n   * Cartesian coordinates `source_coords` and possibly divide by the radius\n   * $r$.\n   *\n   * \\details If \\p one_over_radius_power has a value, then divide $G$ by $r$ to\n   * that power. This is done here because the transition function knows how to\n   * handle points in various regions of the map.\n   */\n  virtual double operator()(\n      const std::array<double, 3>& source_coords,\n      const std::optional<size_t>& one_over_radius_power) const = 0;\n  virtual DataVector operator()(\n      const std::array<DataVector, 3>& source_coords,\n      const std::optional<size_t>& one_over_radius_power) const = 0;\n  /// @}\n\n  /*!\n   * \\brief The inverse of the transition function\n   *\n   * This method returns $r/\\tilde{r}$ given the mapped coordinates\n   * $\\tilde{x}^i$ (`target_coords`) and the spherical harmonic expansion\n   * $\\Sigma(t, \\theta, \\phi) = \\sum_{lm} \\lambda_{lm}(t)Y_{lm}(\\theta, \\phi)$\n   * (`radial_distortion`). See domain::CoordinateMaps::TimeDependent::Shape for\n   * details on how this quantity is used to compute the inverse of the Shape\n   * map.\n   *\n   * To derive the expression for this inverse, solve Eq.\n   * ($\\ref{eq:shape_map_radius}$) for $r$ after substituting\n   * $G(r,\\theta,\\phi)$.\n   *\n   * \\param target_coords The mapped Cartesian coordinates $\\tilde{x}^i$.\n   * \\param radial_distortion The spherical harmonic expansion\n   * $\\Sigma(t, \\theta, \\phi)$.\n   * \\return The quantity $r/\\tilde{r}$.\n   */\n  virtual std::optional<double> original_radius_over_radius(\n      const std::array<double, 3>& target_coords,\n      double radial_distortion) const = 0;\n\n  /*!\n   * Evaluate the gradient of the transition function with respect to the\n   * Cartesian coordinates x, y and z at the Cartesian coordinates\n   * `source_coords`.\n   */\n  /// @{\n  virtual std::array<double, 3> gradient(\n      const std::array<double, 3>& source_coords) const = 0;\n  virtual std::array<DataVector, 3> gradient(\n      const std::array<DataVector, 3>& source_coords) const = 0;\n  /// @}\n\n  virtual std::unique_ptr<ShapeMapTransitionFunction> get_clone() const = 0;\n\n  virtual bool operator==(const ShapeMapTransitionFunction& other) const = 0;\n  virtual bool operator!=(const ShapeMapTransitionFunction& other) const = 0;\n\n  WRAPPED_PUPable_abstract(ShapeMapTransitionFunction);\n  explicit ShapeMapTransitionFunction(CkMigrateMessage* m) : PUP::able(m) {}\n};\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <optional>\n#include <pup.h>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/Blaze/IntegerPow.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\n\nSphereTransition::SphereTransition(const double r_min, const double r_max,\n                                   const bool reverse, const bool interior)\n    : r_min_(r_min), r_max_(r_max), interior_(interior) {\n  if (r_min <= 0.) {\n    ERROR(\"The minimum radius must be greater than 0 but is \" << r_min);\n  }\n  inverse_cube_r_min_ = 1.0 / cube(r_min_);\n  if (r_max <= r_min) {\n    ERROR(\n        \"The maximum radius must be greater than the minimum radius but \"\n        \"r_max =  \"\n        << r_max << \", and r_min = \" << r_min);\n  }\n  if (interior and reverse) {\n    ERROR(\"Cannot be reverse while also in the interior.\");\n  }\n  a_ = -1.0 / (r_max - r_min);\n  b_ = -a_ * r_max;\n  if (reverse) {\n    a_ *= -1.0;\n    b_ = 1.0 - b_;\n  }\n}\n\ndouble SphereTransition::operator()(\n    const std::array<double, 3>& source_coords,\n    const std::optional<size_t>& one_over_radius_power) const {\n  return call_impl<double>(source_coords, one_over_radius_power);\n}\n\nDataVector SphereTransition::operator()(\n    const std::array<DataVector, 3>& source_coords,\n    const std::optional<size_t>& one_over_radius_power) const {\n  return call_impl<DataVector>(source_coords, one_over_radius_power);\n}\n\nstd::optional<double> SphereTransition::original_radius_over_radius(\n    const std::array<double, 3>& target_coords,\n    double radial_distortion) const {\n  const double mag = magnitude(target_coords);\n  // If we are at the center, the radius is the same\n  if (UNLIKELY(equal_within_roundoff(mag, 0.0))) {\n    return interior_ ? std::optional{1.0} : std::nullopt;\n  }\n\n  // a_ being positive is a sentinel for reversed.\n  // If we aren't reversed, check near or within r_min_.\n  if (a_ < 0.0) {\n    if (mag + radial_distortion < (1.0 - eps_) * r_min_) {\n      if (not interior_) {\n        return std::nullopt;\n      }\n\n      const double factor =\n          1.0 / (inverse_cube_r_min_ * square(mag) * radial_distortion);\n\n      // The following variable names are defined in Numerical Recipes (3rd\n      // edition) pg 228. We choose to keep them as they appear in NR for better\n      // comparison with the book.\n\n      // Numerical Recipes (3rd edition) pg 228, eq 5.6.10\n      const double R = 0.5 * factor;\n      const double Q = factor / 3.0;\n      const bool multiple_real_roots = square(R) < cube(Q);\n\n      if (multiple_real_roots) {\n        // Numerical Recipes (3rd edition) pg 228, eqs 5.6.11 - 5.6.12\n        const double theta = acos(R / sqrt(cube(Q)));\n        std::vector<double> roots{\n            -2.0 * sqrt(Q) * cos(theta / 3.0),\n            -2.0 * sqrt(Q) * cos((theta + 2.0 * M_PI) / 3.0),\n            -2.0 * sqrt(Q) * cos((theta - 2.0 * M_PI) / 3.0)};\n\n        // Radii are positive\n        std::erase_if(roots, [](const double root) { return root < 0.0; });\n\n        // Since the root of this is the original radius over target radius, it\n        // will be of order unity and not something super large, so we take the\n        // smallest of the positive roots. If this turns out to not be robust\n        // enough we can change this.\n        return *alg::min_element(roots);\n      } else {\n        // Numerical Recipes (3rd edition) pg 228, eqs 5.6.13 - 5.6.17\n        const double A = -sgn(R) * cbrt(abs(R) + sqrt(square(R) - cube(Q)));\n        const double B = A == 0.0 ? 0.0 : Q / A;\n\n        return A + B;\n      }\n    } else if (equal_within_roundoff(mag + radial_distortion, r_min_)) {\n      // IntMid: Since the point is on the boundary, we can't tell if this is\n      // the interior or not, but either map will work.\n      return std::optional{r_min_ / (r_min_ - radial_distortion)};\n    }\n  }\n\n  // Beyond the range of validity for both reversed and not reversed\n  if ((a_ < 0.0 and mag > (1.0 + eps_) * r_max_) or\n      (a_ > 0.0 and mag < (1.0 - eps_) * r_min_)) {\n    return std::nullopt;\n  }\n\n  // No distortion means our point is the same\n  if (equal_within_roundoff(radial_distortion, 0.0)) {\n    return std::optional{1.0};\n  }\n\n  // At the f=0 boundary.\n  if ((a_ < 0.0 and equal_within_roundoff(mag, r_max_)) or\n      (a_ > 0.0 and equal_within_roundoff(mag, r_min_))) {\n    return std::optional{1.0};\n  }\n\n  const double denom = 1. - radial_distortion * a_;\n  // prevent zero division\n  if (UNLIKELY(equal_within_roundoff(denom, 0.))) {\n    return std::nullopt;\n  }\n\n  const double original_radius = (mag + radial_distortion * b_) / denom;\n\n  // Check at or beyond f=1 boundary for reversed\n  if (a_ > 0.0) {\n    if (equal_within_roundoff(original_radius, r_max_)) {\n      return std::optional{1.0 + radial_distortion / mag};\n    } else if (original_radius > (1.0 + eps_) * r_max_) {\n      return std::nullopt;\n    }\n  }\n\n  // We are within r_min and r_max and not at a boundary\n  return std::optional{original_radius / mag};\n}\n\nstd::array<double, 3> SphereTransition::gradient(\n    const std::array<double, 3>& source_coords) const {\n  return gradient_impl<double>(source_coords);\n}\nstd::array<DataVector, 3> SphereTransition::gradient(\n    const std::array<DataVector, 3>& source_coords) const {\n  return gradient_impl<DataVector>(source_coords);\n}\n\ntemplate <typename T>\nT SphereTransition::call_impl(\n    const std::array<T, 3>& source_coords,\n    const std::optional<size_t>& one_over_radius_power) const {\n  const T mag = magnitude(source_coords);\n\n#ifdef SPECTRE_DEBUG\n  if (UNLIKELY(one_over_radius_power.value_or(0_st) >= 3)) {\n    for (size_t i = 0; i < get_size(source_coords[0]); i++) {\n      if (equal_within_roundoff(get_element(mag, i), 0.0)) {\n        const std::array<double, 3> point{get_element(source_coords[0], i),\n                                          get_element(source_coords[1], i),\n                                          get_element(source_coords[2], i)};\n        ERROR(\"Trying to divide by a point \"\n              << point << \" with radius zero in SphereTransition operator.\");\n      }\n    }\n  }\n#endif\n\n  if (interior_) {\n    return inverse_cube_r_min_ *\n           (one_over_radius_power.value_or(0_st) < 3\n                ? T{integer_pow(mag,\n                                static_cast<int>(\n                                    2 - one_over_radius_power.value_or(0_st)))}\n                : 1.0 /\n                      integer_pow(mag, static_cast<int>(\n                                           one_over_radius_power.value() - 2)));\n  }\n\n#ifdef SPECTRE_DEBUG\n  for (size_t i = 0; i < get_size(source_coords[0]); i++) {\n    const std::array<double, 3> point{get_element(source_coords[0], i),\n                                      get_element(source_coords[1], i),\n                                      get_element(source_coords[2], i)};\n    if (UNLIKELY(get_element(mag, i) < (1.0 - eps_) * r_min_)) {\n      ERROR(\"SphereTransition coord \" << point << \" with radius \"\n                                      << get_element(mag, i)\n                                      << \" is within r_min of \" << r_min_\n                                      << \", but the class was not constructed \"\n                                         \"for the interior of the sphere.\");\n    } else if (UNLIKELY(get_element(mag, i) > (1.0 + eps_) * r_max_)) {\n      ERROR(\"SphereTransition coord \"\n            << point << \" with radius \" << get_element(mag, i)\n            << \"is beyond r_max of \" << r_max_ << \".\");\n    }\n  }\n#endif\n\n  T result = (a_ * mag + b_);\n  // Avoid roundoff\n  result = blaze::clamp(result, 0.0, 1.0);\n\n  return result /\n         integer_pow(\n             mag, static_cast<int>(1 + one_over_radius_power.value_or(0_st)));\n}\n\ntemplate <typename T>\nstd::array<T, 3> SphereTransition::gradient_impl(\n    const std::array<T, 3>& source_coords) const {\n  // Short circuit for the interior\n  if (interior_) {\n    return 2.0 * inverse_cube_r_min_ * source_coords;\n  }\n\n#ifdef SPECTRE_DEBUG\n  const T mag = magnitude(source_coords);\n\n  for (size_t i = 0; i < get_size(source_coords[0]); i++) {\n    const std::array<double, 3> point{get_element(source_coords[0], i),\n                                      get_element(source_coords[1], i),\n                                      get_element(source_coords[2], i)};\n    if (UNLIKELY(equal_within_roundoff(get_element(mag, i), 0.0))) {\n      ERROR(\"Trying to divide by a point \"\n            << point << \" with radius zero in SphereTransition gradient.\");\n    }\n\n    if (UNLIKELY(get_element(mag, i) < (1.0 - eps_) * r_min_)) {\n      ERROR(\"SphereTransition gradient coord \"\n            << point << \" with radius \" << get_element(mag, i)\n            << \" is within r_min of \" << r_min_\n            << \", but the class was not constructed for the interior of the \"\n               \"sphere.\");\n    } else if (UNLIKELY(get_element(mag, i) > (1.0 + eps_) * r_max_)) {\n      ERROR(\"SphereTransition gradient coord \"\n            << point << \" with radius \" << get_element(mag, i)\n            << \"is beyond r_max of \" << r_max_ << \".\");\n    }\n  }\n#endif\n\n  // We can call the operator() and be sure it won't error because we did the\n  // checks here as well.\n  return source_coords *\n         (a_ / dot(source_coords, source_coords) - (*this)(source_coords, {2}));\n}\n\nbool SphereTransition::operator==(\n    const ShapeMapTransitionFunction& other) const {\n  if (dynamic_cast<const SphereTransition*>(&other) == nullptr) {\n    return false;\n  }\n  const auto& derived = dynamic_cast<const SphereTransition&>(other);\n  // no need to check `a_` or `b_` as they are uniquely determined by `r_min_`\n  // and `r_max_`.\n  return this->r_min_ == derived.r_min_ and this->r_max_ == derived.r_max_ and\n         this->interior_ == derived.interior_;\n}\n\nbool SphereTransition::operator!=(\n    const ShapeMapTransitionFunction& other) const {\n  return not(*this == other);\n}\n\nvoid SphereTransition::pup(PUP::er& p) {\n  ShapeMapTransitionFunction::pup(p);\n  size_t version = 1;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | r_min_;\n    p | r_max_;\n    p | a_;\n    p | b_;\n  }\n\n  if (p.isUnpacking()) {\n    inverse_cube_r_min_ = 1.0 / cube(r_min_);\n  }\n\n  if (version >= 1) {\n    p | interior_;\n  } else if (p.isUnpacking()) {\n    interior_ = false;\n  }\n}\n\nSphereTransition::SphereTransition(CkMigrateMessage* const msg)\n    : ShapeMapTransitionFunction(msg) {}\n\nPUP::able::PUP_ID SphereTransition::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\n\n/*!\n * \\ingroup CoordMapsTimeDependentGroup\n * \\brief A transition function that falls off as $G(r,\\theta,\\phi) =\n * \\frac{f(r)}{r} = \\frac{ar + b}{r}$.\n *\n * \\details The coefficients $a$ and $b$ are chosen so that the function $f(r) =\n * ar + b$ falls off linearly from 1 at \\p r_min to 0 at \\p r_max. The\n * coefficients are\n *\n * \\f{align}{\n * \\label{eq:transition_func}\n * a &= \\frac{-1}{r_{\\text{max}} - r_{\\text{min}}} \\\\\n * b &= \\frac{r_{\\text{max}}}{r_{\\text{max}} - r_{\\text{min}}} = -a\n * r_{\\text{max}}\n * \\f}\n *\n * If \\p reverse is set to `true`, then the function falls off from 0 at\n * \\p r_min to 1 at \\p r_max. To do this, the coefficients are modified as\n * $a \\rightarrow -a$ and $b \\rightarrow 1-b$.\n *\n * The function can be called within \\p r_min, but only if \\p interior is `true`\n * and \\p reverse is `false`. Within \\p r_min,\n *\n * \\begin{equation}\n * G(r,\\theta,\\phi) = \\frac{r^2}{r_{\\text{min}}^3}.\n * \\end{equation}\n *\n * which is chosen to match Eq. $\\ref{eq:transition_func}$ at $r_{\\text{min}}$\n * and go to 0 at $r=0$.\n *\n * This function cannot be called beyond \\p r_max.\n *\n * If \\p interior is `false` and if the `operator()` or `gradient()` is called\n * with a point within \\p r_min, an error will occur and\n * `original_radius_over_radius` will return `std::nullopt`.\n *\n * This is a special, simplified, case of the\n * `domain::CoordinateMaps::ShapeMapTransitionFunctions::Wedge` class where both\n * the inner and outer surface are spheres centered on the same center.\n */\nclass SphereTransition final : public ShapeMapTransitionFunction {\n public:\n  explicit SphereTransition() = default;\n  SphereTransition(double r_min, double r_max, bool reverse = false,\n                   bool interior = false);\n\n  double operator()(\n      const std::array<double, 3>& source_coords,\n      const std::optional<size_t>& one_over_radius_power) const override;\n  DataVector operator()(\n      const std::array<DataVector, 3>& source_coords,\n      const std::optional<size_t>& one_over_radius_power) const override;\n\n  std::optional<double> original_radius_over_radius(\n      const std::array<double, 3>& target_coords,\n      double radial_distortion) const override;\n\n  std::array<double, 3> gradient(\n      const std::array<double, 3>& source_coords) const override;\n  std::array<DataVector, 3> gradient(\n      const std::array<DataVector, 3>& source_coords) const override;\n\n  WRAPPED_PUPable_decl_template(SphereTransition);\n  explicit SphereTransition(CkMigrateMessage* msg);\n  void pup(PUP::er& p) override;\n\n  std::unique_ptr<ShapeMapTransitionFunction> get_clone() const override {\n    return std::make_unique<SphereTransition>(*this);\n  }\n\n  bool operator==(const ShapeMapTransitionFunction& other) const override;\n  bool operator!=(const ShapeMapTransitionFunction& other) const override;\n\n private:\n  template <typename T>\n  T call_impl(const std::array<T, 3>& source_coords,\n              const std::optional<size_t>& one_over_radius_power) const;\n\n  template <typename T>\n  std::array<T, 3> gradient_impl(const std::array<T, 3>& source_coords) const;\n\n  double r_min_{};\n  double inverse_cube_r_min_{};\n  double r_max_{};\n  double a_{};\n  double b_{};\n  bool interior_{};\n  static constexpr double eps_ = std::numeric_limits<double>::epsilon() * 100;\n};\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Wedge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Wedge.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/Blaze/IntegerPow.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\nWedge::Surface::Surface(const std::array<double, 3>& center_in,\n                        const double radius_in, const double sphericity_in)\n    : center(center_in),\n      radius(radius_in),\n      sphericity(sphericity_in),\n      half_cube_length(radius / sqrt(3.0)) {}\n\nvoid Wedge::Surface::pup(PUP::er& p) {\n  p | center;\n  p | radius;\n  p | sphericity;\n  p | half_cube_length;\n}\n\nsize_t Wedge::axis_index(const Axis axis) {\n  if (axis == Axis::Interior or axis == Axis::None) {\n    ERROR(\"Cannot get the index of the axis \" << axis);\n  }\n  return static_cast<size_t>(abs(static_cast<int>(axis)) - 1);\n}\n\ndouble Wedge::axis_sgn(const Axis axis) {\n  if (axis == Axis::Interior or axis == Axis::None) {\n    ERROR(\"Cannot get the sign of the axis \" << axis);\n  }\n  return static_cast<double>(sgn(static_cast<int>(axis)));\n}\n\nstd::ostream& operator<<(std::ostream& os, const Wedge::Axis axis) {\n  if (axis == Wedge::Axis::Interior or axis == Wedge::Axis::None) {\n    os << (axis == Wedge::Axis::Interior ? \"Interior\" : \"None\");\n    return os;\n  }\n\n  os << (Wedge::axis_sgn(axis) < 0.0 ? \"-\" : \"+\") << Wedge::axis_index(axis);\n  return os;\n}\n\nWedge::Wedge(const std::array<double, 3>& inner_center,\n             const double inner_radius, const double inner_sphericity,\n             const std::array<double, 3>& outer_center,\n             const double outer_radius, const double outer_sphericity,\n             const Axis axis, const bool reverse)\n    : inner_surface_(inner_center, inner_radius, inner_sphericity),\n      outer_surface_(outer_center, outer_radius, outer_sphericity),\n      projection_center_(inner_surface_.center - outer_surface_.center),\n      axis_(axis),\n      reverse_(reverse) {\n  if (projection_center_ != std::array{0.0, 0.0, 0.0} and\n      inner_surface_.sphericity != 1.0) {\n    ERROR(\n        \"The sphericity of the inner surface must be exactly 1.0 if the \"\n        \"centers of the inner and outer object are different.\\ninner center: \"\n        << inner_surface_.center\n        << \"\\nouter center: \" << outer_surface_.center);\n  }\n  for (size_t i = 0; i < inner_surface_.center.size(); i++) {\n    if (abs(gsl::at(projection_center_, i)) >=\n        outer_surface_.half_cube_length) {\n      ERROR(\"The inner surface center \"\n            << inner_surface_.center\n            << \" must be within the outer surface with center \"\n            << outer_surface_.center << \" and half cube length \"\n            << outer_surface_.half_cube_length);\n    }\n  }\n  if (axis_ == Axis::None) {\n    ERROR(\n        \"Axis for Wedge shape map transition function cannot be 'None'. Please \"\n        \"choose another.\");\n  }\n  if (axis_ == Axis::Interior and\n      (inner_surface_.sphericity != 1.0 or reverse)) {\n    ERROR(\n        \"When the axis is 'Interior', the inner surface must be a sphere \"\n        \"(sphericity 1.0) and reverse must be 'false'.\");\n  }\n}\n\ntemplate <typename T>\nstd::array<T, 3> Wedge::compute_inner_surface_vector(\n    const std::array<T, 3>& centered_coords, const T& centered_coords_magnitude,\n    const std::optional<Axis>& potential_axis) const {\n  // If the surface is a sphere, then the vector is trivially the radial vector\n  if (inner_surface_.sphericity == 1.0) {\n    return inner_surface_.radius * centered_coords / centered_coords_magnitude;\n  }\n\n  T non_spherical_contribution =\n      (1.0 - inner_surface_.sphericity) / sqrt(3.0) * centered_coords_magnitude;\n  // Do some debug checks so we don't divide by zero below if we are given an\n  // axis\n  if (potential_axis.has_value()) {\n    const size_t index = axis_index(potential_axis.value());\n#ifdef SPECTRE_DEBUG\n    for (size_t i = 0; i < get_size(centered_coords[0]); i++) {\n      if (get_element(gsl::at(centered_coords, index), i) == 0.0) {\n        ERROR(\n            \"The Wedge transition map was called with coordinates outside of \"\n            \"its wedge. The \"\n            << index << \"-coordinate shouldn't be 0 but it is. Coordinate: (\"\n            << get_element(centered_coords[0], i) << \",\"\n            << get_element(centered_coords[1], i) << \",\"\n            << get_element(centered_coords[2], i) << \")\");\n      }\n    }\n#endif\n    non_spherical_contribution /= abs(gsl::at(centered_coords, index));\n  } else {\n    non_spherical_contribution /=\n        blaze::max(abs(centered_coords[0]), abs(centered_coords[1]),\n                   abs(centered_coords[2]));\n  }\n\n  return inner_surface_.radius *\n         (non_spherical_contribution + inner_surface_.sphericity) *\n         centered_coords / centered_coords_magnitude;\n}\n\ntemplate <typename T>\nstd::array<T, 3> Wedge::compute_outer_surface_vector(\n    const std::array<T, 3>& centered_coords, const T& lambda) const {\n  return lambda * centered_coords;\n}\n\ntemplate <typename T>\nT Wedge::lambda_cube(const std::array<T, 3>& centered_coords,\n                     const std::optional<Axis>& potential_axis) const {\n  const auto lambda_axis = [&centered_coords, &potential_axis,\n                            this](const Axis axis) -> T {\n    const size_t axis_idx = axis_index(axis);\n    const double factor = axis_sgn(axis) * outer_surface_.half_cube_length -\n                          gsl::at(projection_center_, axis_idx);\n    T result{};\n    // If we were given an axis to this function, then we can be confident that\n    // all the coords of this axis aren't zero, so we don't have to check them\n    if (potential_axis.has_value()) {\n      result = factor / gsl::at(centered_coords, axis_idx);\n    } else {\n      // However, if we have to figure out the axis, it's possible some coords\n      // for this axis are zero but some aren't so we have to check them all\n      // individually to avoid dividing by zero. If a coordinate is zero, then\n      // that point can't exist in the wedge whose coordinate is zero. Since we\n      // take the min of all positive lambdas to find the correct one, we\n      // overwrite this lambda with -max so it'll never be chosen\n      result = make_with_value<T>(centered_coords[0], factor);\n      for (size_t i = 0; i < get_size(result); i++) {\n        if (get_element(gsl::at(centered_coords, axis_idx), i) == 0.0) {\n          get_element(result, i) = -std::numeric_limits<double>::max();\n        } else {\n          get_element(result, i) /=\n              get_element(gsl::at(centered_coords, axis_idx), i);\n        }\n      }\n    }\n\n    return result;\n  };\n\n  // If we specified an axis that we are on, then use that lambda. This saves\n  // some computation.\n  if (potential_axis.has_value()) {\n    return lambda_axis(potential_axis.value());\n  }\n\n  // Otherwise we need to loop over all of them\n  const std::array all_axes{Axis::PlusZ,  Axis::MinusZ, Axis::PlusY,\n                            Axis::MinusY, Axis::PlusX,  Axis::MinusX};\n  std::array<T, 6> candidate_lambdas{};\n\n  for (size_t i = 0; i < all_axes.size(); i++) {\n    gsl::at(candidate_lambdas, i) = lambda_axis(gsl::at(all_axes, i));\n  }\n\n  // We need to take the min of all the positive lambdas\n  const double fill_value = std::numeric_limits<double>::max();\n  T result = make_with_value<T>(candidate_lambdas[0], fill_value);\n  std::vector<Axis> chosen_axes(get_size(result), Axis::None);\n  for (size_t i = 0; i < get_size(result); i++) {\n    for (size_t j = 0; j < candidate_lambdas.size(); j++) {\n      if (get_element(gsl::at(candidate_lambdas, j), i) > 0.0 and\n          get_element(gsl::at(candidate_lambdas, j), i) <\n              get_element(result, i)) {\n        get_element(result, i) = get_element(gsl::at(candidate_lambdas, j), i);\n        chosen_axes[i] = gsl::at(all_axes, j);\n      }\n    }\n  }\n\n  using ::operator<<;\n  ASSERT(not alg::any_of(chosen_axes,\n                         [&](const Axis& a) { return a == Axis::None; }),\n         \"No candidate cube lambdas in the Wedge shape map transition function \"\n         \"are positive.\\nCandidate lambdas: \"\n             << candidate_lambdas\n             << \"\\nThis is an internal error. Please file an issue.\");\n\n  return result;\n}\n\ntemplate <typename T>\nT Wedge::lambda_sphere(const std::array<T, 3>& centered_coords,\n                       const T& centered_coords_magnitude) const {\n  // quadratic equation is\n  // a lambda^2 + b lambda + c = 0\n  //\n  // For this case\n  // a = |x^i-P^i|^2\n  // b = 2(x^i-P^i)(P^j-C^j)\\delta_{ij}\n  // c = |P^i-C^i|^2 - R^2\n\n  const T a = square(centered_coords_magnitude);\n  const T b = 2.0 * dot(centered_coords, projection_center_);\n  const T c =\n      make_with_value<T>(b, dot(projection_center_, projection_center_) -\n                                square(outer_surface_.radius));\n\n  CAPTURE_FOR_ERROR(a);\n  CAPTURE_FOR_ERROR(b);\n  CAPTURE_FOR_ERROR(c);\n\n  // The root that we are looking for is positive (since a negative root would\n  // be for the opposite side of the sphere). The epsilon is to handle slightly\n  // negative values.\n  return smallest_root_greater_than_value_within_roundoff(\n      a, b, c, -std::numeric_limits<double>::epsilon() * 100.0);\n}\n\ntemplate <typename T>\nT Wedge::compute_lambda(const std::array<T, 3>& centered_coords,\n                        const T& centered_coords_magnitude,\n                        const std::optional<Axis>& potential_axis) const {\n  T result{};\n  // Avoid extra computation if possible\n  if (outer_surface_.sphericity == 1.0) {\n    result = lambda_sphere(centered_coords, centered_coords_magnitude);\n  } else if (outer_surface_.sphericity == 0.0) {\n    result = lambda_cube(centered_coords, potential_axis);\n  } else {\n    result = (1.0 - outer_surface_.sphericity) *\n                 lambda_cube(centered_coords, potential_axis) +\n             outer_surface_.sphericity *\n                 lambda_sphere(centered_coords, centered_coords_magnitude);\n  }\n\n  return result;\n}\n\ntemplate <typename T>\nstd::array<T, 3> Wedge::lambda_cube_gradient(\n    const T& lambda_cube, const std::array<T, 3>& centered_coords) const {\n  const size_t axis_idx = axis_index(axis_);\n\n  // We don't have to worry about dividing by zero here because we only call the\n  // gradient when we know our axis and so the coord of this axis can't be zero.\n  std::array<T, 3> result =\n      make_array<3>(make_with_value<T>(centered_coords[0], 0.0));\n  gsl::at(result, axis_idx) = -lambda_cube / gsl::at(centered_coords, axis_idx);\n\n  return result;\n}\n\ntemplate <typename T>\nstd::array<T, 3> Wedge::lambda_sphere_gradient(\n    const T& lambda_sphere, const std::array<T, 3>& centered_coords,\n    const T& centered_coords_magnitude) const {\n  return -1.0 * lambda_sphere *\n         (lambda_sphere * centered_coords + projection_center_) /\n         (lambda_sphere * square(centered_coords_magnitude) +\n          dot(centered_coords, projection_center_));\n}\n\ntemplate <typename T>\nstd::array<T, 3> Wedge::compute_lambda_gradient(\n    const std::array<T, 3>& centered_coords,\n    const T& centered_coords_magnitude) const {\n  std::array<T, 3> result{};\n  // Avoid extra computation if possible\n  if (outer_surface_.sphericity == 1.0) {\n    result = lambda_sphere_gradient(\n        lambda_sphere(centered_coords, centered_coords_magnitude),\n        centered_coords, centered_coords_magnitude);\n  } else if (outer_surface_.sphericity == 0.0) {\n    result = lambda_cube_gradient(lambda_cube(centered_coords, {axis_}),\n                                  centered_coords);\n  } else {\n    result = (1.0 - outer_surface_.sphericity) *\n                 lambda_cube_gradient(lambda_cube(centered_coords, {axis_}),\n                                      centered_coords) +\n             outer_surface_.sphericity *\n                 lambda_sphere_gradient(\n                     lambda_sphere(centered_coords, centered_coords_magnitude),\n                     centered_coords, centered_coords_magnitude);\n  }\n\n  return result;\n}\n\ndouble Wedge::operator()(\n    const std::array<double, 3>& source_coords,\n    const std::optional<size_t>& one_over_radius_power) const {\n  return call_impl<double>(source_coords, one_over_radius_power);\n}\n\nDataVector Wedge::operator()(\n    const std::array<DataVector, 3>& source_coords,\n    const std::optional<size_t>& one_over_radius_power) const {\n  return call_impl<DataVector>(source_coords, one_over_radius_power);\n}\n\ntemplate <typename T>\nT Wedge::call_impl(const std::array<T, 3>& source_coords,\n                   const std::optional<size_t>& one_over_radius_power) const {\n  // The source coords are centered\n  const T centered_coords_magnitude = magnitude(source_coords);\n\n#ifdef SPECTRE_DEBUG\n  using ::operator<<;\n  if (UNLIKELY(one_over_radius_power.value_or(0_st) >= 3)) {\n    for (size_t i = 0; i < get_size(source_coords[0]); i++) {\n      if (UNLIKELY(equal_within_roundoff(\n              get_element(centered_coords_magnitude, i), 0.0))) {\n        const std::array<double, 3> point{get_element(source_coords[0], i),\n                                          get_element(source_coords[1], i),\n                                          get_element(source_coords[2], i)};\n        ERROR(\"Trying to divide by a point \"\n              << point << \" with radius zero in Wedge transition operator.\");\n      }\n    }\n  }\n#endif\n\n  // If the axis is the interior, short circuit\n  if (axis_ == Axis::Interior) {\n    return 1.0 / cube(inner_surface_.radius) *\n           (one_over_radius_power.value_or(0_st) < 3\n                ? T{integer_pow(centered_coords_magnitude,\n                                static_cast<int>(\n                                    2 - one_over_radius_power.value_or(0_st)))}\n                : 1.0 / integer_pow(centered_coords_magnitude,\n                                    static_cast<int>(\n                                        one_over_radius_power.value() - 2)));\n  }\n\n  // First check if we are at the inner center to avoid dividing by zero below\n#ifdef SPECTRE_DEBUG\n  for (size_t i = 0; i < get_size(source_coords[0]); i++) {\n    if (UNLIKELY(equal_within_roundoff(\n            get_element(centered_coords_magnitude, i), 0.0))) {\n      const std::array<double, 3> point{get_element(source_coords[0], i),\n                                        get_element(source_coords[1], i),\n                                        get_element(source_coords[2], i)};\n      ERROR(\n          \"The Wedge transition was called with a point that has zero \"\n          \"centered \"\n          \"radius, but the axis isn't the 'Interior'. Point is \"\n          << point << \", axis is \" << axis_);\n    }\n  }\n#endif\n\n  // Now we can safely compute lambda without worrying about dividing by zero\n  const T lambda =\n      compute_lambda(source_coords, centered_coords_magnitude, {axis_});\n\n  const T inner_distance =\n      inner_surface_.sphericity == 1.0\n          ? make_with_value<T>(centered_coords_magnitude, inner_surface_.radius)\n          : magnitude(compute_inner_surface_vector(\n                source_coords, centered_coords_magnitude, {axis_}));\n\n  const std::array<T, 3> outer_surface_vector =\n      compute_outer_surface_vector(source_coords, lambda);\n  const T outer_distance = magnitude(outer_surface_vector);\n\n  // Check if the point we were passed is within the transition region\n#ifdef SPECTRE_DEBUG\n  for (size_t i = 0; i < get_size(source_coords[0]); i++) {\n    const std::array<double, 3> point{get_element(source_coords[0], i),\n                                      get_element(source_coords[1], i),\n                                      get_element(source_coords[2], i)};\n    if (get_element(centered_coords_magnitude, i) <\n        (1.0 - eps_) * get_element(inner_distance, i)) {\n      ERROR(\"Wedge transition called with centered coordinate \"\n            << point << \" (with radius \"\n            << get_element(centered_coords_magnitude, i)\n            << \") which is inside the inner surface (\"\n            << get_element(inner_distance, i)\n            << \") while the axis was not 'Interior' (\" << axis_ << \")\");\n    } else if (get_element(centered_coords_magnitude, i) >\n               (1.0 + eps_) * get_element(outer_distance, i)) {\n      ERROR(\"Wedge transition called with centered coordinate \"\n            << point << \" (with radius \"\n            << get_element(centered_coords_magnitude, i)\n            << \") which is outside the outer surface (\"\n            << get_element(outer_distance, i) << \").\");\n    }\n  }\n#endif\n\n  T linear_transition_func = (outer_distance - centered_coords_magnitude) /\n                             (outer_distance - inner_distance);\n\n  if (reverse_) {\n    linear_transition_func = 1.0 - linear_transition_func;\n  }\n\n  // Accounts for roundoff\n  linear_transition_func = blaze::clamp(linear_transition_func, 0.0, 1.0);\n\n  return linear_transition_func /\n         integer_pow(\n             centered_coords_magnitude,\n             static_cast<int>(1 + one_over_radius_power.value_or(0_st)));\n}\n\nstd::optional<double> Wedge::original_radius_over_radius(\n    const std::array<double, 3>& target_coords,\n    double radial_distortion) const {\n  // The target coords are centered\n  const double centered_coords_magnitude = magnitude(target_coords);\n  CAPTURE_FOR_ERROR(target_coords);\n  CAPTURE_FOR_ERROR(radial_distortion);\n  CAPTURE_FOR_ERROR(centered_coords_magnitude);\n\n  // There are a number of different special cases that can (and should) be\n  // accounted for before we go to the general inverse formula. To denote all\n  // these special cases we introduce the following notation. For the 3 possible\n  // regions, we use Int (Interior), Ext (Exterior), and Mid\n  // (middle/transition). For the boundaries between these regions we'll combine\n  // them: IntMid (inner surface) and MidExt (outer surface). Then for reverse,\n  // we'll add 'R+' (e.g. R+Ext accounts for a point in the exterior region when\n  // the transition is reversed). The absence of 'R+' means this is the\n  // \"regular\" transition.\n\n  // Int: Protect against point that wouldn't have any radial distortion\n  if (equal_within_roundoff(centered_coords_magnitude, 0.0)) {\n    return (axis_ == Axis::Interior and not reverse_) ? std::optional{1.0}\n                                                      : std::nullopt;\n  }\n\n  const double inner_distance =\n      inner_surface_.sphericity == 1.0\n          ? inner_surface_.radius\n          : magnitude(compute_inner_surface_vector(\n                target_coords, centered_coords_magnitude, std::nullopt));\n\n  if (not reverse_) {\n    // Check if we are within the interior region or at the inner surface. The\n    // formula is simplified at the inner surface\n    if (centered_coords_magnitude + radial_distortion <\n        (1.0 - eps_) * inner_distance) {\n      // Int: Unless the axis_ is Interior, this block doesn't contain this\n      // point\n      if (axis_ != Axis::Interior) {\n        return std::nullopt;\n      }\n\n      // We want to solve\n      //\n      //   0 = -1 + r/r~ - (r/r~)^3 radial_distortion r~^2 / R_in^3\n      //\n      // for r/r~.  Solving in this form is badly behaved when\n      // radial_distortion is small, so instead we solve for the\n      // inverse:\n      //\n      //   0 = (r~/r)^3 - (r~/r)^2 + radial_distortion r~^2 / R_in^3\n      //\n      // See Numerical Recipes (3rd edition) pg 228 for the solution\n      // for a cubic.\n\n      const double intercept = square(centered_coords_magnitude) *\n                               radial_distortion / cube(inner_surface_.radius);\n\n      if (intercept > 4.0 / 27.0) {\n        ERROR(\"No positive root in original_radius_over_radius.\");\n      }\n\n      if (intercept > 0.0) {\n        // Three real roots: NR eqs 5.6.11 - 5.6.12\n\n        // The other roots can be found by shifting the argument of\n        // cos by multiples of 2 pi/3, but the one here is the\n        // solution continuous with the single-real-root case.\n        return 3.0 / (1.0 + 2.0 * cos(acos(1.0 - 13.5 * intercept) / 3.0));\n      } else {\n        // Single real root: NR eqs 5.6.13 - 5.6.17\n\n        // 3 * A in eq 5.6.15\n        const double threeA =\n            cbrt(1.0 - 13.5 * intercept +\n                 sqrt((182.25 * intercept - 27.0) * intercept));\n        return 3.0 * threeA / (1.0 + threeA * (1.0 + threeA));\n      }\n    } else if (equal_within_roundoff(\n                   centered_coords_magnitude + radial_distortion,\n                   inner_distance)) {\n      // IntMid: Since the point is on the boundary, we can't tell if this is\n      // the interior or not, but either map will work.\n      return std::optional{inner_distance /\n                           (inner_distance - radial_distortion)};\n    }\n  }\n\n  const double lambda =\n      compute_lambda(target_coords, centered_coords_magnitude, std::nullopt);\n\n  const double outer_distance =\n      magnitude(compute_outer_surface_vector(target_coords, lambda));\n\n  // Ext,R+Int: First we check the extremal cases where we are outside the\n  // transition region. If we are reversed and inside the inner surface, we\n  // return nullopt. If we aren't reversed and beyond the outer surface we\n  // return nullopt because we could still be in the interior region.\n  if ((not reverse_ and\n       centered_coords_magnitude > (1.0 + eps_) * outer_distance) or\n      (reverse_ and\n       centered_coords_magnitude < (1.0 - eps_) * inner_distance)) {\n    return std::nullopt;\n  }\n\n  // All remaining cases: If distorted radius is 0, this means the map is the\n  // identity so the radius and the original radius are equal. Also we don't\n  // want to divide by 0 below. We do this check after we check if the point is\n  // beyond the outer distance because if a point is outside the distorted\n  // frame, this function should return nullopt.\n  if (equal_within_roundoff(radial_distortion, 0.0)) {\n    return std::optional{1.0};\n  }\n\n  // MidExt,R+IntMid: If we are at the overall outer distance, then the\n  // transition function is 0 so the map is again the identity so the radius and\n  // original radius are equal. We can't check the overall inner distance\n  // because that has been distorted so we don't know where the mapped inner\n  // distance is. This logic is reversed if we are in reverse mode.\n  if ((not reverse_ and\n       equal_within_roundoff(centered_coords_magnitude, outer_distance)) or\n      (reverse_ and\n       equal_within_roundoff(centered_coords_magnitude, inner_distance))) {\n    return std::optional{1.0};\n  }\n\n  const double denom = outer_distance - inner_distance +\n                       (reverse_ ? -1.0 : 1.0) * radial_distortion;\n  // prevent zero division\n  if (equal_within_roundoff(denom, 0.)) {\n    return std::nullopt;\n  }\n\n  const double original_radius_over_radius =\n      (outer_distance - inner_distance +\n       (reverse_ ? -inner_distance : outer_distance) * radial_distortion /\n           centered_coords_magnitude) /\n      denom;\n  const double original_radius =\n      original_radius_over_radius * centered_coords_magnitude;\n\n  // R+MidExt,R+Ext: Extremal transition at outer boundary for reverse has a\n  // simplified formula.\n  if (reverse_) {\n    if (equal_within_roundoff(original_radius, outer_distance)) {\n      return {1.0 + radial_distortion / centered_coords_magnitude};\n    } else if (original_radius > (1.0 + eps_) * outer_distance) {\n      return std::nullopt;\n    }\n  }\n\n  // Mid,R+Mid\n  return {original_radius_over_radius};\n}\n\nstd::array<double, 3> Wedge::gradient(\n    const std::array<double, 3>& source_coords) const {\n  return gradient_impl<double>(source_coords);\n}\nstd::array<DataVector, 3> Wedge::gradient(\n    const std::array<DataVector, 3>& source_coords) const {\n  return gradient_impl<DataVector>(source_coords);\n}\n\ntemplate <typename T>\nstd::array<T, 3> Wedge::gradient_impl(\n    const std::array<T, 3>& source_coords) const {\n  // The source coords are centered\n  const T centered_coords_magnitude = magnitude(source_coords);\n\n  // Short circuit if we are in the interior\n  if (axis_ == Axis::Interior) {\n    return 2.0 / cube(inner_surface_.radius) * source_coords;\n  }\n\n// First check if we are at the inner center to avoid dividing by zero below\n#ifdef SPECTRE_DEBUG\n  for (size_t i = 0; i < get_size(centered_coords_magnitude); i++) {\n    if (UNLIKELY(equal_within_roundoff(\n            get_element(centered_coords_magnitude, i), 0.0))) {\n      ERROR(\n          \"The gradient of the wedge transition was called with a point that \"\n          \"has zero centered radius, but the axis isn't the 'Interior'. Point \"\n          \"is \"\n          << inner_surface_.center << \", axis is \" << axis_);\n    }\n  }\n#endif\n\n  const T one_over_centered_coords_magnitude = 1.0 / centered_coords_magnitude;\n\n  const T lambda =\n      compute_lambda(source_coords, centered_coords_magnitude, {axis_});\n\n  const T inner_distance =\n      inner_surface_.sphericity == 1.0\n          ? make_with_value<T>(lambda, inner_surface_.radius)\n          : magnitude(compute_inner_surface_vector(\n                source_coords, centered_coords_magnitude, {axis_}));\n\n  const std::array<T, 3> outer_surface_vector =\n      compute_outer_surface_vector(source_coords, lambda);\n  const T outer_distance = magnitude(outer_surface_vector);\n\n// Check if the point we were passed is within the transition region\n#ifdef SPECTRE_DEBUG\n  for (size_t i = 0; i < get_size(centered_coords_magnitude); i++) {\n    const std::array<double, 3> point{get_element(source_coords[0], i),\n                                      get_element(source_coords[1], i),\n                                      get_element(source_coords[2], i)};\n    if (get_element(centered_coords_magnitude, i) <\n        (1.0 - eps_) * get_element(inner_distance, i)) {\n      ERROR(\"Wedge transition called with centered coordinate \"\n            << point << \" (with radius \"\n            << get_element(centered_coords_magnitude, i)\n            << \") which is inside the inner surface (\"\n            << get_element(inner_distance, i)\n            << \") while the axis was not 'Interior' (\" << axis_ << \")\");\n    } else if (get_element(centered_coords_magnitude, i) >\n               (1.0 + eps_) * get_element(outer_distance, i)) {\n      ERROR(\"Wedge transition called with centered coordinate \"\n            << point << \" (with radius \"\n            << get_element(centered_coords_magnitude, i)\n            << \") which is outside the outer surface (\"\n            << get_element(outer_distance, i) << \").\");\n    }\n  }\n#endif\n\n  // This can only be called if the projection center is 0, otherwise this\n  // formula won't work. And if the projection center isn't 0, then we require\n  // that the sphericity of the inner surface is 1, so we ASSERT that here\n  // as well\n  const auto inner_surface_gradient = [&]() -> std::array<T, 3> {\n    using ::operator<<;\n    ASSERT((projection_center_ == std::array{0.0, 0.0, 0.0}),\n           \"Should not be calculating the inner surface gradient when the \"\n           \"projection center is non-zero. \"\n               << projection_center_);\n    ASSERT(inner_surface_.sphericity != 1.0,\n           \"Should not be calculating the inner surface gradient when its \"\n           \"sphericity is 1.0 because the gradient is exactly 0.\");\n\n    const size_t axis_idx = axis_index(axis_);\n    const size_t axis_plus_one = (axis_idx + 1) % 3;\n    const size_t axis_plus_two = (axis_idx + 2) % 3;\n\n    const T& axis_coord = gsl::at(source_coords, axis_idx);\n\n    std::array<T, 3> grad = make_array<3, T>(\n        inner_surface_.radius * (1.0 - inner_surface_.sphericity) / sqrt(3.0) *\n        one_over_centered_coords_magnitude / abs(axis_coord));\n\n    // Dividing by axis_coord here takes care of the sgn(axis_coord) that would\n    // have been necessary\n    gsl::at(grad, axis_idx) *=\n        -(square(centered_coords_magnitude) - square(axis_coord)) / axis_coord;\n    gsl::at(grad, axis_plus_one) *= gsl::at(source_coords, axis_plus_one);\n    gsl::at(grad, axis_plus_two) *= gsl::at(source_coords, axis_plus_two);\n\n    return grad;\n  };\n\n  // We always need to compute the outer gradient regardless of the projection\n  // center or the outer sphericity\n  const std::array<T, 3> outer_gradient =\n      lambda * source_coords * one_over_centered_coords_magnitude +\n      compute_lambda_gradient(source_coords, centered_coords_magnitude) *\n          centered_coords_magnitude;\n\n  const T one_over_distance_difference =\n      1.0 / (outer_distance - inner_distance);\n\n  // Avoid allocating an array of 0 if the inner surface is a sphere\n  if (inner_surface_.sphericity == 1.0) {\n    return (outer_gradient -\n            source_coords * one_over_centered_coords_magnitude -\n            (outer_distance - centered_coords_magnitude) * outer_gradient *\n                one_over_distance_difference) *\n               one_over_distance_difference *\n               one_over_centered_coords_magnitude * (reverse_ ? -1.0 : 1.0) -\n           source_coords * (*this)(source_coords, {2});\n  } else {\n    const std::array<T, 3> inner_gradient = inner_surface_gradient();\n\n    return (outer_gradient -\n            source_coords * one_over_centered_coords_magnitude -\n            (outer_distance - centered_coords_magnitude) *\n                (outer_gradient - inner_gradient) *\n                one_over_distance_difference) *\n               one_over_distance_difference *\n               one_over_centered_coords_magnitude * (reverse_ ? -1.0 : 1.0) -\n           source_coords * (*this)(source_coords, {2});\n  }\n}\n\nbool Wedge::operator==(const ShapeMapTransitionFunction& other) const {\n  if (dynamic_cast<const Wedge*>(&other) == nullptr) {\n    return false;\n  }\n  const auto surface_equal = [](const Surface& s1, const Surface& s2) {\n    return s1.center == s2.center and s1.radius == s2.radius and\n           s1.sphericity == s2.sphericity and\n           s1.half_cube_length == s2.half_cube_length;\n  };\n  const Wedge& other_ref = *dynamic_cast<const Wedge*>(&other);\n  return surface_equal(inner_surface_, other_ref.inner_surface_) and\n         surface_equal(outer_surface_, other_ref.outer_surface_) and\n         axis_ == other_ref.axis_ and reverse_ == other_ref.reverse_;\n}\n\nbool Wedge::operator!=(const ShapeMapTransitionFunction& other) const {\n  return not(*this == other);\n}\n\nnamespace {\n// struct to allow unpacking version 0 of this class\nstruct LegacySurface {\n  double radius{std::numeric_limits<double>::signaling_NaN()};\n  double sphericity{std::numeric_limits<double>::signaling_NaN()};\n\n  LegacySurface() = default;\n  LegacySurface(const double radius_in, const double sphericity_in)\n      : radius(radius_in), sphericity(sphericity_in) {}\n\n  void pup(PUP::er& p) {\n    p | radius;\n    p | sphericity;\n  }\n};\n}  // namespace\n\nvoid Wedge::pup(PUP::er& p) {\n  ShapeMapTransitionFunction::pup(p);\n  size_t version = 2;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 1) {\n    p | inner_surface_;\n    p | outer_surface_;\n    p | projection_center_;\n    p | axis_;\n    if (version >= 2) {\n      p | reverse_;\n    } else {\n      reverse_ = false;\n    }\n  } else if (p.isUnpacking()) {\n    LegacySurface inner_surface{};\n    LegacySurface outer_surface{};\n    size_t axis = 0;\n    p | inner_surface;\n    p | outer_surface;\n    p | axis;\n\n    // We don't know the centers of the objects so we just set them to 0\n    inner_surface_ = Surface{\n        {0.0, 0.0, 0.0},\n        inner_surface.radius,\n        inner_surface.sphericity,\n    };\n    outer_surface_ = Surface{\n        {0.0, 0.0, 0.0},\n        outer_surface.radius,\n        outer_surface.sphericity,\n    };\n    // Unfortunately we can't recover the sign for the axis so we just set it\n    // to positive. This is wrong though.\n    axis_ = static_cast<Axis>(axis + 1);\n    projection_center_ = std::array{0.0, 0.0, 0.0};\n  }\n}\n\nWedge::Wedge(CkMigrateMessage* const msg) : ShapeMapTransitionFunction(msg) {}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID Wedge::my_PUP_ID = 0;\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Wedge.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\n/*!\n * \\brief A transition function $G(r,\\theta,\\phi)$ with $f(r,\\theta,\\phi)$ that\n * falls off linearly from an inner surface of a wedge to an outer surface of a\n * wedge. Meant to be used in `domain::CoordinateMaps::Wedge` blocks.\n *\n * \\details The functional form of this transition is\n *\n * \\begin{equation}\n *     G(r,\\theta,\\phi) = \\frac{f(r,\\theta,\\phi)}{r}\n *     \\label{eq:transition_func}\n * \\end{equation}\n *\n * where\n *\n * \\begin{equation}\n *     f(r, \\theta, \\phi) = \\frac{D_{\\text{out}}(r, \\theta, \\phi) -\n *     r}{D_{\\text{out}}(r, \\theta, \\phi) - D_{\\text{in}}(r, \\theta, \\phi)},\n * \\label{eq:linear_transition_func}\n * \\end{equation}\n *\n * where, in general,\n *\n * \\begin{equation}\n * D(r, \\theta, \\phi) = R\\left(\\frac{1 -\n * s}{\\sqrt{3}\\max(|\\sin{\\theta}\\cos{\\phi}|,|\\sin{\\theta}\\sin{\\phi}|,\n * |\\cos{\\theta}|)} + s\\right).\n * \\label{eq:distance}\n * \\end{equation}\n *\n * Here, $s$ is the sphericity of the surface which goes from 0 (flat) to 1\n * (spherical), $R$ is the radius of the spherical surface, $\\text{out}$ is the\n * outer surface, and $\\text{in}$ is the inner surface. If the sphericity is 1,\n * then the surface is a sphere so $D = R$. If the sphericity is 0, then the\n * surface is a cube. This cube is circumscribed by a sphere of radius $R$. See\n * `domain::CoordinateMaps::Wedge` for more of an explanation of these boundary\n * surfaces and their sphericities.\n *\n * \\note If \\p reverse is true, then the functional form of the transition is\n * actually\n * \\begin{equation}\n * f(r, \\theta, \\phi) = 1 - \\frac{D_{\\text{out}}(r, \\theta, \\phi) -\n * r}{D_{\\text{out}}(r, \\theta, \\phi) - D_{\\text{in}}(r, \\theta, \\phi)} =\n * \\frac{r - D_{\\text{in}}(r, \\theta, \\phi)}\n * {D_{\\text{out}}(r, \\theta, \\phi) - D_{\\text{in}}(r, \\theta, \\phi)}.\n * \\label{eq:linear_transition_func_reverse}\n * \\end{equation}\n *\n * \\parblock\n *\n * \\note Because the shape map distorts only radii and does not affect angles,\n * $D$ is not a function of $r$ so we have that $D(r,\\theta,\\phi) =\n * D(\\theta, \\phi)$.\n *\n * \\endparblock\n *\n * The transition function is also defined within $D_{\\text{in}}(r, \\theta,\n * \\phi)$ if the axis is `Axis::Interior`. Within $D_{\\text{in}}(r, \\theta,\n * \\phi)$, the transition function is\n *\n * \\begin{equation}\n *     \\label{eq:interior_transition_func}\n *     G(r,\\theta,\\phi) = \\frac{r^2}{D_{\\text{in}}(r, \\theta, \\phi)^3} =\n *     \\frac{r^3}{R_0^3}\n * \\end{equation}\n *\n * and none of the vector formulation is necessary. This is chosen to match Eq.\n * $\\ref{eq:transition_func}$ at $D_{\\text{in}}(r, \\theta, \\phi)$ and go to 0 at\n * $r=0$. The second equality is explained by one of the following bullets.\n *\n * There are several assumptions made for this mapping:\n *\n * - The coordinates $r, \\theta, \\phi$ are assumed to be from the center of the\n *   inner surface, not the center of the computational domain.\n * - The wedges are concentric. (see the constructor) This is also enforced by\n *   the center of the inner surface being within the outer surface.\n * - If the centers of the inner and outer surface are different, then the inner\n *   sphericity must be 1 (i.e. $D_{\\text{in}} = R$).\n * - If the axis is `Axis::Interior`, then the inner sphericity must be 1 and\n *    \\p reverse must be false.\n * - The $\\max$ in the denominator of $\\ref{eq:distance}$ can be simplified a\n *   bit to $\\max(|x|, |y|, |z|)/r$. It was written the other way in\n *   $\\ref{eq:distance}$ to emphasize that $D$ has no radial dependence.\n *\n * ### Vector formulation\n *\n * \\image html WedgeTransition.jpg \"Useful vectors for Wedge transition\"\n *\n * It can be more convenient to work with vectors rather than just distances for\n * the following equations. Therefore, we define the projection center $\\vec P$\n * which points from the center of the outer surface to the center of the inner\n * surface. If the centers of the inner and outer surface are the same, then\n * $\\vec P = 0$. Then, we define $\\vec x$ as the vector from the center of the\n * outer surface to the coordinate we are interested in. We define $\\vec x_0$ as\n * the vector from the center of the outer surface to the point along ray $\\vec\n * x - \\vec P$ which intersects the inner surface. Similarly, $\\vec x_1$\n * intersects the outer surface along the same ray as $\\vec x - \\vec P$. Any\n * cube has a side length of $2L$.\n *\n * In this way, we can reformulate Eq. $\\ref{eq:linear_transition_func}$ as\n *\n * \\begin{equation}\\label{eq:linear_transition_func_vec}\n *     f(\\vec x) = \\frac{|\\vec x_1 - \\vec P| - r}{|\\vec x_1 - \\vec P| - |\\vec\n * x_0 - \\vec P|}\n * \\end{equation}\n *\n * where\n *\n * \\begin{equation}\n *     r  = |\\vec x - P|\n * \\end{equation}\n *\n * #### Computing vector to inner surface\n *\n * Similar to Eq. $\\ref{eq:distance}$ we can define $\\vec x_0$ as\n *\n * \\begin{equation}\\label{eq:x_0_vector}\n *     \\vec x_0 = R_0\\left( \\frac{r(1-s_0)}{\\sqrt{3}\\max{(x_i - P_i)}} +\n * s\\right) \\hat r + \\vec P\n * \\end{equation}\n *\n * with\n *\n * \\begin{equation}\n *     \\hat r = \\frac{\\vec x - \\vec P}{|\\vec x - \\vec P|}.\n * \\end{equation}\n *\n * and also\n *\n * \\begin{equation}\\label{eq:x_0_norm}\n *     |\\vec x_0 - \\vec P| = R_0\\left( \\frac{r(1-s_0)}{\\sqrt{3}\\max{(x_i -\n * P_i)}} + s_0\\right)\n * \\end{equation}\n *\n * ### Computing vector to outer surface\n *\n * Then, we define\n *\n * \\begin{equation}\\label{eq:x_1}\n *     \\vec x_1 = \\lambda (\\vec x - \\vec P) + \\vec P\n * \\end{equation}\n *\n * where $\\lambda$ is a scalar to be determined. We can see that this is valid\n * because $\\vec x - \\vec P$ is centered on $\\vec P$ and lies along the ray so\n * $\\lambda$ will scale this vector to be the correct length.\n *\n * Computing $\\lambda$ is a bit complicated because the outer surface can either\n * be a cube, a sphere, or something in between and the way to solve for\n * $\\lambda$ in the cube case is different than in the sphere case. Therefore we\n * define $\\lambda_c$ and $\\lambda_s$ as the scalar factors for the cube and\n * sphere case, respectively. This allows us to define $\\lambda$ as\n *\n * \\begin{equation}\\label{eq:lambda}\n *     \\lambda = (1 - s_1) \\lambda_c + s_1 \\lambda_s\n * \\end{equation}\n *\n * #### Computing cube lambda\n *\n * To compute $\\lambda_c$ for the cube case, we notice that (in the image)\n *\n * \\begin{align}\n *     L &= \\vec x_1 \\cdot \\hat z \\\\\n *     &= \\left(\\lambda_c^{+z}(\\vec x - \\vec P) + \\vec P\\right) \\cdot \\hat z\\\\\n *     &= \\lambda_c^{+z}(x_z - P_z) + P_z\n * \\end{align}\n *\n * If we generalize this to all six unit vectors and solve for $\\lambda_c^{\\pm\n * k}$ we get\n *\n * \\begin{equation}\\label{eq:lambda_c_pm}\n *     \\lambda_c^{\\pm k} = \\frac{\\pm L - \\vec P \\cdot \\hat x_k}{(\\vec x -\n * \\vec P) \\cdot \\hat x_k}\n * \\end{equation}\n *\n * for $k \\in \\{x, y, z\\}$. This quantity $\\lambda_c^{\\pm k}$ represents the\n * factor needed to scale the vector $\\vec x - \\vec P$ to intersect with the\n * infinite planes that contain the six faces of the cube.\n *\n * Some $\\lambda_c^{\\pm k}$ will be negative as they point to the opposite\n * planes. We can discard those. Then for the $\\lambda_c^{\\pm k}$ that are\n * positive, we want the one that is smallest in magnitude. This can be written\n * as\n *\n * \\begin{equation}\\label{eq:lambda_c}\n *     \\lambda_c = \\min_{\\substack{\\lambda_c^{\\pm k} > 0}} (\\lambda_c^{\\pm k})\n * \\end{equation}\n *\n * #### Computing sphere lambda\n *\n * To compute $\\lambda_s$ for the sphere case, again we notice that (in the\n * image)\n *\n * \\begin{align}\n *     R_1^2 &= |\\vec x_1|^2 \\\\\n *     &= |\\lambda_s (\\vec x - \\vec P) + \\vec P|^2 \\\\\n *     &= \\lambda_s^2 |\\vec x - \\vec P|^2 + 2 \\lambda_s (\\vec x - \\vec P) \\cdot\n * \\vec P + |\\vec P|^2\n * \\end{align}\n *\n * This is simply a quadratic equation for $\\lambda_s$:\n *\n * \\begin{equation}\\label{eq:lambda_s}\n *     0 = \\lambda_s^2 |\\vec x - \\vec P|^2 + 2 \\lambda_s (\\vec x - \\vec P) \\cdot\n * \\vec P + |\\vec P|^2 - R_1^2\n * \\end{equation}\n *\n * We explicitly don't write the typical form of the solution of a quadratic\n * equation because that form behaviors poorly numerically if the two terms in\n * the numerator are close to each other (bad numerical roundoff). Therefore, we\n * just resolve to compute the solution numerically, and say that we have\n * $\\lambda_s$.\n *\n * We have now solved for $\\vec x_1$.\n *\n * \\begin{equation}\n *     \\vec x_1 = \\left((1 - s_1) \\lambda_c + s_1 \\lambda_s \\right) (\\vec x -\n * \\vec P) + \\vec P\n * \\end{equation}\n *\n * given the solutions for $\\lambda_c$ in Eqn. $\\ref{eq:lambda_c}$ and\n * $\\lambda_s$ in Eqn. $\\ref{eq:lambda_s}$.\n *\n * ## Original radius divided by mapped radius\n *\n * Given an already mapped point $\\tilde{\\vec x}$ and the radial distortion\n * $\\Sigma(\\theta, \\phi) = \\sum_{\\ell,m}\\lambda_{\\ell m}Y_{\\ell m}(\\theta,\n * \\phi)$, we can figure out the ratio of the original radius $r$ to the mapped\n * $\\tilde{r} = |\\tilde{\\vec x}|$ by solving\n *\n * \\begin{equation}\n * \\frac{r}{\\tilde{r}} =\n * \\frac{1}{1-G(r)\\Sigma(\\theta,\\phi)}\n * \\end{equation}\n *\n * If the axis is `Axis::Interior`, this formula becomes (after simplification\n * and because the inner surface must be spherical)\n *\n * \\begin{equation}\n *    0 = \\left(\\frac{r}{\\tilde{r}}\\right)^3 - \\left(\\frac{r}{\\tilde{r}}\\right)\n *    \\frac{R_0^3}{\\tilde{r}^2\\Sigma(\\theta,\\phi)} +\n *    \\frac{R_0^3}{\\tilde{r}^2\\Sigma(\\theta,\\phi)}\n * \\end{equation}\n *\n * This is a special case of a cubic root, so we use the method outlined in\n * \\cite NumericalRecipes on pg 228.\n *\n * For other axes, after plugging in $f(r,\\theta,\\phi)/r$ and solving, we get\n *\n * \\begin{equation}\n * \\frac{r}{\\tilde{r}} = \\frac{1 + \\frac{|\\vec x_1 - \\vec P|\\Sigma(\\theta,\n * \\phi)}{\\tilde{r}(|\\vec x_1 - \\vec P| - |\\vec x_0 - \\vec P|)}}{1 +\n * \\frac{\\Sigma(\\theta, \\phi)}{|\\vec x_1 - \\vec P| - |\\vec x_0 - \\vec P|}}\n * \\label{eq:r_over_rtil}\n * \\end{equation}\n *\n * \\note Since $\\vec x_0 - \\vec P$ and $\\vec x_1 - \\vec P$ are not functions of\n * the radius, we can use $\\tilde{\\vec x}$ in Eq. $\\ref{eq:x_0_vector}$ instead\n * of $\\vec x$.\n *\n * \\parblock\n *\n * \\note If \\p reverse is true, then the value multiplying $\\Sigma$ in the\n * numerator is now $-|\\vec x_0 - \\vec P|$ and in the denominator $\\Sigma$ picks\n * up a minus sign factor.\n *\n * \\endparblock\n *\n * ## Gradient\n *\n * The cartesian gradient of the transition function is\n *\n * \\begin{equation}\n *     \\frac{\\partial G}{\\partial x_i} = \\frac{1}{r}\\frac{\\partial f}{\\partial\n *     \\xi^i} - \\frac{f \\xi_i}{r^3}\n * \\end{equation}\n *\n * where\n *\n * \\begin{equation}\n * \\frac{\\partial f}{\\partial x_i} = \\frac{\\frac{\\partial\n * |\\vec x_1 - \\vec P|}{\\partial x_i} - \\frac{x_i - P_i}{r}}{|\\vec x_1 - \\vec P|\n * - |\\vec x_0 - \\vec P|} - \\frac{\\left(|\\vec x_1 - \\vec P| -\n * r\\right)\\left(\\frac{\\partial |\\vec x_1 - \\vec P|}{\\partial x_i} -\n * \\frac{\\partial |\\vec x_0 - \\vec P|}{\\partial x_i} \\right)}{\\left(|\\vec x_1 -\n * \\vec P| - |\\vec x_0 - \\vec P|\\right)^2}.\n * \\end{equation}\n *\n * \\note If \\p reverse is true, the gradient picks up an overall factor of -1.0.\n *\n * If the axis is `Axis::Interior`, then the gradient is\n *\n * \\begin{equation}\n * \\frac{\\partial G}{\\partial x_i} = \\frac{2(x_i-P_i)}{R_0^3}\n * \\end{equation}\n *\n * For the other axes, we need to compute the gradients of $\\vec x_0$ and $\\vec\n * x_1$.\n *\n * ### Gradient of vector to inner surface\n *\n * If $\\vec P \\neq 0$, then we require the inner surface to be a sphere, which\n * means the gradient is trivially zero. Therefore the following is only for\n * when $\\vec P = 0$.\n *\n * The gradient of $|\\vec x_0 - \\vec P|$ depends on the result of the $\\max$ in\n * the denominator of $\\ref{eq:x_0_norm}$. To simplify the expression, let $i\n * \\in \\{0,1,2\\}$ correspond to the $\\max(x_i - P_i)$. Then we can write the\n * gradient of as\n *\n * \\begin{align}\n * \\frac{\\partial |\\vec x_0 - \\vec P|}{\\partial x_i} &= -\\text{sgn}(x_i -\n * P_i)\\frac{R(1-s)\\left(r^2 - (x_i - P_i)^2\\right)}\n * {r (x_i - P_i)^2 \\sqrt{3}} \\\\\n * \\frac{\\partial |\\vec x_0 - \\vec P|}{\\partial x_{i+1}} &=\n * \\frac{R(1-s)(x_{i+1} - P_{i+1})}{r |x_i - P_i| \\sqrt{3}} \\\\\n * \\frac{\\partial |\\vec x_0 - \\vec P|}{\\partial x_{i+2}} &=\n * \\frac{R(1-s)(x_{i+2} - P_{i+2})}{r |x_i - P_i| \\sqrt{3}}\n * \\end{align}\n *\n * where $i+1$ and $i+2$ are understood to be $\\mod 3$. Also since we don't\n * allow points at the center of the inner surfaces, we can be assured that for\n * whichever $i$ is max, that $x_i - P_i$ won't be zero.\n *\n * ### Gradient of vector to outer surface\n *\n * This gradient is much more complicated because of how we have to define\n * $\\lambda$. To start off with, we need to compute\n *\n * \\begin{align}\\label{eq:x_1_grad}\n *     \\frac{\\partial}{\\partial x_i}|\\vec x_1 - \\vec P| &=\n * \\frac{\\partial}{\\partial x_i} (\\lambda |\\vec x - \\vec P|) \\\\\n *     &= \\frac{\\lambda (x_i - P_i)}{|\\vec x - \\vec P|} + \\frac{\\partial\n * \\lambda}{\\partial x_i}|\\vec x - \\vec P|\n * \\end{align}\n *\n * Since we know $\\lambda$ and $\\vec x$, that just leaves\n *\n * \\begin{equation}\n *     \\frac{\\partial\\lambda}{\\partial x_i} = \\frac{\\partial\\lambda_c}{\\partial\n * x_i}(1-s_1) + s_1\\frac{\\partial\\lambda_s}{\\partial x_i}\n * \\end{equation}\n *\n * #### Gradient of cube lambda\n *\n * Taking the gradient of Eqn. $\\ref{eq:lambda_c_pm}$ we notice that $L$, $\\vec\n * P$, and $\\hat x_k$ are constant, so we only have to worry about $\\vec x$\n *\n * \\begin{equation}\\label{eq:tmp_lambda_c_grad}\n *     \\frac{\\partial}{\\partial x_i}\\lambda_c^{\\pm k} = -\\frac{(\\pm L -\n * \\vec P \\cdot \\hat x_k)(\\partial/\\partial x_i(\\vec x - \\vec P) \\cdot \\hat\n * x_k)}{((\\vec x - \\vec P) \\cdot \\hat x_k)^2}\n * \\end{equation}\n *\n * The gradient in the numerator is simple to take so we end up with\n *\n * \\begin{equation}\n *     \\frac{\\partial}{\\partial x_i}\\lambda_c^{\\pm k} = -\\frac{(\\pm L -\n * \\vec P \\cdot \\hat x_k)}{( x_k - P_k)^2} \\delta_{ik}\n * \\end{equation}\n *\n * #### Gradient of sphere lambda\n *\n * Taking the gradient of Eqn. $\\ref{eq:lambda_s}$ (and using the fact that\n * $\\vec P$ and $R$ are constant), we get\n *\n * \\begin{equation}\n *     0 = \\lambda_s \\frac{\\partial \\lambda_s}{\\partial x_i}|\\vec x - \\vec P|^2\n * + \\lambda_s^2 (\\vec x - \\vec P) \\cdot \\frac{\\partial}{\\partial x_i}(\\vec x -\n * \\vec P) + \\frac{\\partial\\lambda_s}{\\partial x_i}(\\vec x - \\vec P) \\cdot \\vec\n * P + \\lambda_s \\vec P \\cdot \\frac{\\partial}{\\partial x_i}(\\vec x - \\vec P)\n * \\end{equation}\n *\n * Taking the simple gradients and moving things around, we get\n *\n * \\begin{equation}\n *     \\frac{\\partial\\lambda_s}{\\partial x_i} = - \\frac{\\lambda_s^2 (x_i - P_i)\n * + \\lambda_s P_i}{\\lambda_s |\\vec x - \\vec P|^2 + (\\vec x - \\vec P) \\cdot \\vec\n * P}\n * \\end{equation}\n *\n * where $\\lambda_s$ can be computed from Eqn. $\\ref{eq:lambda_s}$.\n *\n * So now we can put it all together and we get\n *\n * \\begin{equation}\n *    \\frac{\\partial\\lambda}{\\partial x_i} =  -\\frac{(\\pm L - \\vec P \\cdot\n * \\hat x_k)}{( x_k - P_k)^2} \\delta_{ik}(1 - s_1)\n *     + s_1 \\frac{\\lambda_s^2 (x_i - P_i) + \\lambda_s P_i}{\\lambda_s |\\vec x -\n * \\vec P|^2 + (\\vec x - \\vec P) \\cdot \\vec P} \\end{equation}\n *\n * which can then be substituted into Eqn. $\\ref{eq:x_1_grad}$ to get the\n * gradient of $|\\vec x_1 - \\vec P|$.\n */\nclass Wedge final : public ShapeMapTransitionFunction {\n  struct Surface {\n    std::array<double, 3> center{};\n    double radius{};\n    double sphericity{};\n    double half_cube_length{};\n\n    Surface() = default;\n    Surface(const std::array<double, 3>& center_in, double radius_in,\n            double sphericity_in);\n\n    void pup(PUP::er& p);\n  };\n\n public:\n  /*!\n   * \\brief Class to represent the direction of the wedge relative to the outer\n   * center.\n   *\n   * \\details \\p Interior means within inner surface.\n   */\n  enum class Axis : int {\n    Interior = 4,\n    PlusZ = 3,\n    MinusZ = -3,\n    PlusY = 2,\n    MinusY = -2,\n    PlusX = 1,\n    MinusX = -1,\n    None = 0,\n  };\n\n  friend std::ostream& operator<<(std::ostream& os, Axis axis);\n\n private:\n  static size_t axis_index(Axis axis);\n  static double axis_sgn(Axis axis);\n\n public:\n  explicit Wedge() = default;\n\n  /*!\n   * \\brief Construct a Wedge transition for a wedge block in a given direction.\n   *\n   * \\details Many concentric wedges can be part of the same falloff from 1 at\n   * the inner boundary of the innermost wedge, to 0 at the outer boundary of\n   * the outermost wedge.\n   *\n   * \\note If \\p inner_center and \\p outer_center are different, then\n   * \\p inner_sphericity must be 1.0. if the \\p axis is `Axis::Interior`, then\n   * \\p inner_sphericity must be 1.0 and \\p reverse must be false.\n   *\n   * \\param inner_center Center of the inner surface\n   * \\param inner_radius Inner radius of innermost wedge\n   * \\param inner_sphericity Sphericity of innermost surface of innermost wedge\n   * \\param outer_center Center of the outer surface\n   * \\param outer_radius Outermost radius of outermost wedge\n   * \\param outer_sphericity Sphericity of outermost surface of outermost wedge\n   * \\param axis The direction that this wedge is in.\n   * \\param reverse If true, the function $f$ will be 0 at the inner\n   * boundary and 1 at the outer boundary (useful for deforming star surfaces).\n   * Otherwise, it will be 1 at the inner boundary and 0 at the outer boundary\n   * (useful for deforming black hole excision surfaces).\n   */\n  Wedge(const std::array<double, 3>& inner_center, double inner_radius,\n        double inner_sphericity, const std::array<double, 3>& outer_center,\n        double outer_radius, double outer_sphericity, Axis axis,\n        bool reverse = false);\n\n  double operator()(\n      const std::array<double, 3>& source_coords,\n      const std::optional<size_t>& one_over_radius_power) const override;\n  DataVector operator()(\n      const std::array<DataVector, 3>& source_coords,\n      const std::optional<size_t>& one_over_radius_power) const override;\n\n  std::optional<double> original_radius_over_radius(\n      const std::array<double, 3>& target_coords,\n      double radial_distortion) const override;\n\n  std::array<double, 3> gradient(\n      const std::array<double, 3>& source_coords) const override;\n  std::array<DataVector, 3> gradient(\n      const std::array<DataVector, 3>& source_coords) const override;\n\n  WRAPPED_PUPable_decl_template(Wedge);\n  explicit Wedge(CkMigrateMessage* msg);\n  void pup(PUP::er& p) override;\n\n  std::unique_ptr<ShapeMapTransitionFunction> get_clone() const override {\n    return std::make_unique<Wedge>(*this);\n  }\n\n  bool operator==(const ShapeMapTransitionFunction& other) const override;\n  bool operator!=(const ShapeMapTransitionFunction& other) const override;\n\n private:\n  template <typename T>\n  T call_impl(const std::array<T, 3>& source_coords,\n              const std::optional<size_t>& one_over_radius_power) const;\n\n  template <typename T>\n  std::array<T, 3> gradient_impl(const std::array<T, 3>& source_coords) const;\n\n  // This is x_0 - P in the docs\n  template <typename T>\n  std::array<T, 3> compute_inner_surface_vector(\n      const std::array<T, 3>& centered_coords,\n      const T& centered_coords_magnitude,\n      const std::optional<Axis>& potential_axis) const;\n\n  // This is x_1 - P in the docs\n  template <typename T>\n  std::array<T, 3> compute_outer_surface_vector(\n      const std::array<T, 3>& centered_coords, const T& lambda) const;\n\n  template <typename T>\n  T lambda_cube(const std::array<T, 3>& centered_coords,\n                const std::optional<Axis>& potential_axis) const;\n\n  template <typename T>\n  T lambda_sphere(const std::array<T, 3>& centered_coords,\n                  const T& centered_coords_magnitude) const;\n\n  template <typename T>\n  T compute_lambda(const std::array<T, 3>& centered_coords,\n                   const T& centered_coords_magnitude,\n                   const std::optional<Axis>& potential_axis) const;\n\n  template <typename T>\n  std::array<T, 3> lambda_cube_gradient(\n      const T& lambda_cube, const std::array<T, 3>& centered_coords) const;\n\n  template <typename T>\n  std::array<T, 3> lambda_sphere_gradient(\n      const T& lambda_sphere, const std::array<T, 3>& centered_coords,\n      const T& centered_coords_magnitude) const;\n\n  template <typename T>\n  std::array<T, 3> compute_lambda_gradient(\n      const std::array<T, 3>& centered_coords,\n      const T& centered_coords_magnitude) const;\n\n  template <typename T>\n  void check_distances(const T& inner_distance, const T& outer_distance,\n                       const T& centered_coords_magnitude,\n                       const std::array<T, 3>& source_coords,\n                       bool check_bounds) const;\n\n  Surface inner_surface_;\n  Surface outer_surface_;\n  std::array<double, 3> projection_center_{};\n  Axis axis_{};\n  bool reverse_{false};\n\n  static constexpr double eps_ = std::numeric_limits<double>::epsilon() * 100;\n};\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/Skew.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/Skew.hpp\"\n\n#include <array>\n#include <cmath>\n#include <limits>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/StlStreamDeclarations.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace domain::CoordinateMaps::TimeDependent {\n\nSkew::Skew(std::string function_of_time_name,\n           const std::array<double, 3>& center, const double outer_radius)\n    : f_of_t_name_(std::move(function_of_time_name)),\n      center_(center),\n      outer_radius_(outer_radius),\n      f_of_t_names_({f_of_t_name_}) {\n  if (outer_radius_ <= 0.0) {\n    ERROR(\"Skew map outer radius must be positive, but is \" << outer_radius_);\n  }\n  one_over_outer_radius_squared_ = 1.0 / square(outer_radius_);\n  if (magnitude(center_) >= outer_radius) {\n    ERROR(\"Center of Skew map \"\n          << center_ << \" with radius \" << magnitude(center_)\n          << \" must be within the outer radius \" << outer_radius_);\n  }\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> Skew::operator()(\n    const std::array<T, 3>& source_coords, const double time,\n    const domain::FunctionsOfTimeMap& functions_of_time) const {\n  return map_and_velocity_helper(source_coords, time, functions_of_time, false);\n}\n\nstd::optional<std::array<double, 3>> Skew::inverse(\n    const std::array<double, 3>& target_coords, const double time,\n    const domain::FunctionsOfTimeMap& functions_of_time) const {\n  const auto& function_of_time = functions_of_time.at(f_of_t_name_);\n  const DataVector func = function_of_time->func_and_deriv(time)[0];\n  ASSERT(func.size() == 2, \"Expected a function of time with size 2, not \"\n                               << func.size() << \" in the Skew map.\");\n\n  // Short circuit if there's no function of time\n  if (equal_within_roundoff(func[0], 0.0) and\n      equal_within_roundoff(func[1], 0.0)) {\n    return target_coords;\n  }\n\n  // Short circuit if we're at the outer radius\n  const double target_radius = magnitude(target_coords);\n  if (equal_within_roundoff(target_radius, outer_radius_)) {\n    return target_coords;\n  } else if (target_radius >= outer_radius_) {\n    return std::nullopt;\n  }\n\n  // Another short circuit. Target y & z are the same as source y & z\n  const double tan_sum =\n      -1.0 * (tan(func[0]) * (target_coords[1] - center_[1]) +\n              tan(func[1]) * (target_coords[2] - center_[2]));\n  if (equal_within_roundoff(tan_sum, 0.0)) {\n    return target_coords;\n  }\n\n  std::array<double, 3> temporary_source_coord = target_coords;\n\n  const auto root_func = [&](const double source_coord_x) -> double {\n    temporary_source_coord[0] = source_coord_x;\n    // Sometimes the temporary point is outside the outer radius, but we still\n    // need to continue with the root find, so allow evaluation of the width\n    // outside the outer radius without an error\n    const double width = get_width(temporary_source_coord, true);\n    return source_coord_x + width * tan_sum - target_coords[0];\n  };\n\n  // \\bar{x} -> x - w * (tan(f_y)*(y-y_C) + tan(f_z)*(z-z_C)) and 0<=w<=1, then\n  // x <= \\bar{x} <= x - (tan(f_y)*(y-y_C) + tan(f_z)*(z-z_C)), ==>\n  // 0 <= \\bar{x} - x <= -(tan(f_y)*(y-y_C) + tan(f_z)*(z-z_C)), ==>\n  // -\\bar{x} <= - x <= -\\bar{x} - (tan(f_y)*(y-y_C) + tan(f_z)*(z-z_C)), ==>\n  // \\bar{x} >= x >= \\bar{x} + (tan(f_y)*(y-y_C) + tan(f_z)*(z-z_C))\n  // This last line is our bracket for the root\n\n  // We give a small epsilon to give the root find a little buffer. It is highly\n  // unlikely there will be another root within these bounds\n  const double eps = std::numeric_limits<double>::epsilon() * 100.0;\n\n  const double x_0 = target_coords[0] - tan_sum - eps;\n  const double x_1 = target_coords[0] + eps;\n  const double lower_x = std::min(x_0, x_1);\n  const double upper_x = std::max(x_0, x_1);\n  const double lower_func = root_func(lower_x);\n  const double upper_func = root_func(upper_x);\n\n  // Since we adjusted the bounds by eps above, we have a slightly larger eps\n  // here to account for the arithmetic of the root func\n  if (equal_within_roundoff(lower_func, 0.0, 5 * eps)) {\n    temporary_source_coord[0] = lower_x;\n    return temporary_source_coord;\n  }\n\n  if (equal_within_roundoff(upper_func, 0.0, 5 * eps)) {\n    temporary_source_coord[0] = upper_x;\n    return temporary_source_coord;\n  }\n\n  if (lower_func * upper_func > 0.0) {\n    ERROR(\"No root in Skew map inverse!. Target coord = \"\n          << target_coords << \", lower_x = \" << lower_x\n          << \", lower_func = \" << lower_func << \", upper_x = \" << upper_x\n          << \", upper_func = \" << upper_func << \", tan_sum = \" << tan_sum);\n  }\n\n  temporary_source_coord[0] = RootFinder::toms748<true>(\n      root_func, lower_x, upper_x, lower_func, upper_func, eps, eps);\n\n  return temporary_source_coord;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> Skew::frame_velocity(\n    const std::array<T, 3>& source_coords, const double time,\n    const domain::FunctionsOfTimeMap& functions_of_time) const {\n  return map_and_velocity_helper(source_coords, time, functions_of_time, true);\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> Skew::jacobian(\n    const std::array<T, 3>& source_coords, const double time,\n    const domain::FunctionsOfTimeMap& functions_of_time) const {\n  using ResultT = tt::remove_cvref_wrap_t<T>;\n\n  check_for_singular_map(source_coords, time, functions_of_time);\n\n  const ResultT width = get_width(source_coords);\n  const std::array<ResultT, 3> width_deriv = get_width_deriv(source_coords);\n\n  const auto& function_of_time = functions_of_time.at(f_of_t_name_);\n  const DataVector func = function_of_time->func_and_deriv(time)[0];\n  ASSERT(func.size() == 2, \"Expected a function of time with size 2, not \"\n                               << func.size() << \" in the Skew map.\");\n\n  auto result = identity<3>(dereference_wrapper(source_coords[0]));\n\n  const DataVector tan_func = -1.0 * tan(func);\n  // Temporarily use component to avoid allocation\n  auto& tan_sum = get<0, 2>(result);\n  tan_sum = tan_func[0] * (source_coords[1] - center_[1]) +\n            tan_func[1] * (source_coords[2] - center_[2]);\n\n  get<0, 0>(result) += width_deriv[0] * tan_sum;\n  get<0, 1>(result) = width_deriv[1] * tan_sum + width * tan_func[0];\n  get<0, 2>(result) = width_deriv[2] * tan_sum + width * tan_func[1];\n\n  return result;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> Skew::inv_jacobian(\n    const std::array<T, 3>& source_coords, const double time,\n    const domain::FunctionsOfTimeMap& functions_of_time) const {\n  return determinant_and_inverse(\n             jacobian(source_coords, time, functions_of_time))\n      .second;\n}\n\ntemplate <typename T>\ntt::remove_cvref_wrap_t<T> Skew::get_width(\n    const std::array<T, 3>& source_coords, const bool ignore_error) const {\n  using ResultT = tt::remove_cvref_wrap_t<T>;\n  // Will be reused for result\n  ResultT lambda =\n      one_over_outer_radius_squared_ * dot(source_coords, source_coords);\n\n  ResultT& result = lambda;\n\n  // Go point by point because we have to check roundoff\n  for (size_t i = 0; i < get_size(result); i++) {\n    if (equal_within_roundoff(get_element(lambda, i), 0.0)) {\n      get_element(result, i) = 1.0;\n    } else if (equal_within_roundoff(get_element(lambda, i), 1.0)) {\n      get_element(result, i) = 0.0;\n    } else if (ignore_error or (get_element(lambda, i) > 0.0 and\n                                get_element(lambda, i) < 1.0)) {\n      get_element(result, i) = 0.5 * (1.0 + cos(M_PI * get_element(lambda, i)));\n    } else {\n      using ::operator<<;\n      const std::vector<double> bad_point{\n          get_element(dereference_wrapper(source_coords[0]), i),\n          get_element(dereference_wrapper(source_coords[1]), i),\n          get_element(dereference_wrapper(source_coords[2]), i)};\n      ERROR(\"Skew map: Source coordinate \"\n            << bad_point << \" not in valid region. Lambda is \"\n            << get_element(lambda, i) << \". Center is \" << center_\n            << \". Outer radius is \" << outer_radius_);\n    }\n  }\n\n  return result;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> Skew::get_width_deriv(\n    const std::array<T, 3>& source_coords) const {\n  using ResultT = tt::remove_cvref_wrap_t<T>;\n\n  const ResultT lambda =\n      one_over_outer_radius_squared_ * dot(source_coords, source_coords);\n\n  std::array<ResultT, 3> grad_width{};\n  for (size_t i = 0; i < 3; i++) {\n    gsl::at(grad_width, i) = gsl::at(source_coords, i);\n  }\n  // Factors of two cancel from grad lambda and pi/2\n  grad_width *= -one_over_outer_radius_squared_ * M_PI * sin(M_PI * lambda);\n\n  for (size_t i = 0; i < get_size(lambda); i++) {\n    // sin(lambda * M_PI) is zero at both lambda=0 and lambda=1\n    if (equal_within_roundoff(get_element(lambda, i), 0.0) or\n        equal_within_roundoff(get_element(lambda, i), 1.0)) {\n      for (size_t j = 0; j < 3; j++) {\n        get_element(gsl::at(grad_width, j), i) = 0.0;\n      }\n    } else if (get_element(lambda, i) < 0.0 or get_element(lambda, i) > 1.0) {\n      using ::operator<<;\n      const std::vector<double> bad_point{\n          get_element(dereference_wrapper(source_coords[0]), i),\n          get_element(dereference_wrapper(source_coords[1]), i),\n          get_element(dereference_wrapper(source_coords[2]), i)};\n      ERROR(\"Skew map: Source coordinate \"\n            << bad_point << \" not in valid region. Lambda is \"\n            << get_element(lambda, i) << \". Center is \" << center_\n            << \". Outer radius is \" << outer_radius_);\n    }\n  }\n\n  return grad_width;\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> Skew::map_and_velocity_helper(\n    const std::array<T, 3>& source_coords, const double time,\n    const FunctionsOfTimeMap& functions_of_time,\n    const bool return_velocity) const {\n  using ResultT = tt::remove_cvref_wrap_t<T>;\n\n  check_for_singular_map(source_coords, time, functions_of_time);\n\n  std::array<ResultT, 3> result{};\n  if (return_velocity) {\n    result = make_array<3>(\n        make_with_value<ResultT>(dereference_wrapper(source_coords[0]), 0.0));\n  } else {\n    for (size_t i = 0; i < 3; i++) {\n      gsl::at(result, i) = gsl::at(source_coords, i);\n    }\n  }\n\n  // Currently result is the source coords, but with the proper return type\n  const ResultT width = get_width(source_coords);\n\n  const auto& function_of_time = functions_of_time.at(f_of_t_name_);\n  const auto func_and_deriv = function_of_time->func_and_deriv(time);\n  ASSERT(func_and_deriv[0].size() == 2,\n         \"Expected a function of time with size 2, not \"\n             << func_and_deriv[0].size() << \" in the Skew map.\");\n\n  if (return_velocity) {\n    const auto& func = func_and_deriv[0];\n    const auto& deriv = func_and_deriv[1];\n\n    result[0] = -1.0 * width *\n                (deriv[0] * (1.0 + square(tan(func[0]))) *\n                     (source_coords[1] - center_[1]) +\n                 deriv[1] * (1.0 + square(tan(func[1]))) *\n                     (source_coords[2] - center_[2]));\n  } else {\n    result[0] -=\n        width * (tan(func_and_deriv[0][0]) * (source_coords[1] - center_[1]) +\n                 tan(func_and_deriv[0][1]) * (source_coords[2] - center_[2]));\n  }\n\n  return result;\n}\n\nvoid Skew::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | f_of_t_name_;\n    p | center_;\n    p | outer_radius_;\n  }\n\n  // No need to pup these because they are uniquely determined\n  if (p.isUnpacking()) {\n    one_over_outer_radius_squared_ = 1.0 / square(outer_radius_);\n    f_of_t_names_.clear();\n    f_of_t_names_.insert(f_of_t_name_);\n  }\n}\n\ntemplate <typename T>\nvoid Skew::check_for_singular_map(\n    const std::array<T, 3>& source_coords, const double time,\n    const FunctionsOfTimeMap& functions_of_time) const {\n  (void)source_coords;\n  (void)time;\n  (void)functions_of_time;\n\n#ifdef SPECTRE_DEBUG\n  using ResultT = tt::remove_cvref_wrap_t<T>;\n  const ResultT lambda =\n      one_over_outer_radius_squared_ * dot(source_coords, source_coords);\n\n  const auto& function_of_time = functions_of_time.at(f_of_t_name_);\n  const auto func = function_of_time->func_and_deriv(time)[0];\n  ASSERT(func.size() == 2, \"Expected a function of time with size 2, not \"\n                               << func.size() << \" in the Skew map.\");\n  const double tan_func0 = tan(func[0]);\n  const double tan_func1 = tan(func[1]);\n\n  CAPTURE_FOR_ERROR(tan_func0);\n  CAPTURE_FOR_ERROR(tan_func1);\n\n  const ResultT tan_sum =\n      -1.0 * (tan(func[0]) * (source_coords[1] - center_[1]) +\n              tan(func[1]) * (source_coords[2] - center_[2]));\n\n  std::array<double, 3> temporary_point{\n      get_element(dereference_wrapper(source_coords[0]), 0),\n      get_element(dereference_wrapper(source_coords[1]), 0),\n      get_element(dereference_wrapper(source_coords[2]), 0)};\n\n  // We can check if the map is singular by checking if the x-deriv of the map\n  // is negative. If it is, then it means the map is not one-to-one and our grid\n  // is being stretched over itself. In order to check this, we use a property\n  // of the analytic functions used in the map that the second x-deriv of the\n  // map will cross zero exactly once for +x and once for -x. We use the\n  // x-values of this zero-crossing to evaluate the first x-deriv and check if\n  // it is negative. This is not a global check as it only checks along the\n  // y=z=const line, but if there is an issue, some point within the domain\n  // should error.\n  for (size_t i = 0; i < get_size(lambda); i++) {\n    // No need to check this point if the map is the identity\n    if (equal_within_roundoff(get_element(tan_sum, i), 0.0)) {\n      continue;\n    }\n\n    temporary_point[0] = get_element(dereference_wrapper(source_coords[0]), i);\n    temporary_point[1] = get_element(dereference_wrapper(source_coords[1]), i);\n    temporary_point[2] = get_element(dereference_wrapper(source_coords[2]), i);\n\n    // Can't check if x=0 and we are at the outer boundary, because then the\n    // largest_x below is just zero and we won't be able to find roots.\n    if (equal_within_roundoff(temporary_point[0], 0.0) and\n        equal_within_roundoff(magnitude(temporary_point), outer_radius_)) {\n      continue;\n    }\n\n    CAPTURE_FOR_ERROR(get_element(tan_sum, i));\n\n    const auto first_deriv = [&](const double x) {\n      temporary_point[0] = x;\n      const double temporary_lambda = one_over_outer_radius_squared_ *\n                                      dot(temporary_point, temporary_point);\n      return 1.0 + M_PI * x * one_over_outer_radius_squared_ *\n                       get_element(tan_sum, i) * sin(M_PI * temporary_lambda);\n    };\n    const auto second_deriv = [&](const double x) -> double {\n      temporary_point[0] = x;\n      const double temporary_lambda = one_over_outer_radius_squared_ *\n                                      dot(temporary_point, temporary_point);\n      return M_PI * one_over_outer_radius_squared_ * get_element(tan_sum, i) *\n             (sin(M_PI * temporary_lambda) +\n              2.0 * M_PI * one_over_outer_radius_squared_ * square(x) *\n                  cos(M_PI * temporary_lambda));\n    };\n\n    const double eps = std::numeric_limits<double>::epsilon() * 100.0;\n    // The endpoint of the y=z=const line given that the outer boundary is a\n    // sphere.\n    const double largest_x =\n        sqrt(square(outer_radius_) - square(temporary_point[1]) -\n             square(temporary_point[2]));\n\n    CAPTURE_FOR_ERROR(temporary_point);\n    CAPTURE_FOR_ERROR(largest_x);\n\n    // Positive and negative zeros of the second deriv\n    const double negative_candidate = RootFinder::toms748<true>(\n        second_deriv, -largest_x, -eps, second_deriv(-largest_x),\n        second_deriv(-eps), eps, eps);\n    const double positive_candidate = RootFinder::toms748<true>(\n        second_deriv, eps, largest_x, second_deriv(eps),\n        second_deriv(largest_x), eps, eps);\n\n    CAPTURE_FOR_ERROR(negative_candidate);\n    CAPTURE_FOR_ERROR(positive_candidate);\n\n    if (first_deriv(negative_candidate) <= 0.0 or\n        first_deriv(positive_candidate) <= 0.0) {\n      using ::operator<<;\n      ERROR(\"Skew map is singular. Found for point \"\n            << temporary_point\n            << \" (y and z coords are the important ones). Important values \"\n               \"are:\\n Outer radius, R = \"\n            << outer_radius_ << \"\\n |x|^2/R^2 = \" << get_element(lambda, i)\n            << \"\\n tan(F_y)*y + tan(F_z)*z = \" << tan_sum\n            << \"\\n F_y = \" << func[0] << \"\\n F_z = \" << func[1]\n            << \"\\n tan(F_y) = \" << tan(func[0])\n            << \"\\n tan(F_z) = \" << tan(func[1]) << \"\\n\");\n    }\n  }\n#endif\n}\n\nbool operator==(const Skew& lhs, const Skew& rhs) {\n  return lhs.f_of_t_name_ == rhs.f_of_t_name_ and lhs.center_ == rhs.center_ and\n         lhs.outer_radius_ == rhs.outer_radius_;\n}\n\nbool operator!=(const Skew& lhs, const Skew& rhs) { return not(lhs == rhs); }\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  Skew::operator()(const std::array<DTYPE(data), 3>& source_coords,            \\\n                   double time,                                                \\\n                   const domain::FunctionsOfTimeMap& functions_of_time) const; \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  Skew::frame_velocity(                                                        \\\n      const std::array<DTYPE(data), 3>& source_coords, const double time,      \\\n      const domain::FunctionsOfTimeMap& functions_of_time) const;              \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  Skew::jacobian(const std::array<DTYPE(data), 3>& source_coords, double time, \\\n                 const domain::FunctionsOfTimeMap& functions_of_time) const;   \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame>   \\\n  Skew::inv_jacobian(                                                          \\\n      const std::array<DTYPE(data), 3>& source_coords, double time,            \\\n      const domain::FunctionsOfTimeMap& functions_of_time) const;              \\\n  template tt::remove_cvref_wrap_t<DTYPE(data)> Skew::get_width(               \\\n      const std::array<DTYPE(data), 3>& source_coords,                         \\\n      const bool ignore_error) const;                                          \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  Skew::get_width_deriv(const std::array<DTYPE(data), 3>& source_coords)       \\\n      const;                                                                   \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>                 \\\n  Skew::map_and_velocity_helper(                                               \\\n      const std::array<DTYPE(data), 3>& source_coords, double time,            \\\n      const domain::FunctionsOfTimeMap& functions_of_time,                     \\\n      bool return_velocity) const;                                             \\\n  template void Skew::check_for_singular_map(                                  \\\n      const std::array<DTYPE(data), 3>& source_coords, double time,            \\\n      const domain::FunctionsOfTimeMap& functions_of_time) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/Skew.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps::TimeDependent {\n/*!\n * \\ingroup CoordMapsTimeDependentGroup\n * \\brief %Time dependent coordinate map that keeps the $y$ and $z$ coordinates\n * unchanged, but will distort (or skew) the $x$ coordinate based on functions\n * of time and radial distance to the origin.\n *\n * \\details This coordinate map is only available in 3 dimensions and is\n * intended to be used in the BinaryCompactObject domain.\n *\n * ### Mapped Coordinates\n * The Skew coordinate map is given by the mapping\n *\n * \\begin{align}\n *   \\label{eq:map}\n *   \\bar{x} &= x - W(\\vec{x})\\left(\\tan(F_y(t))(y-y_C) +\n *       \\tan(F_z(t))(z-z_C)\\right) \\\\\n *   \\bar{y} &= y \\\\\n *   \\bar{z} &= z\n * \\end{align}\n *\n * where $\\vec{x}_C = (x_C, y_C, z_C)$ is the \\p center of the skew map (which\n * is different than the origin of the coordinate system), and $F_y(t)$ and\n * $F_z(t)$ are the angles within the $(x,y)$ plane between the undistorted\n * $x$-axis and the skewed $\\bar{y}$-axis at the origin, represented by\n * `domain::FunctionsOfTime::FunctionOfTime`s. The actual function of time\n * should have two components; the first corresponds to $y$ and the second\n * corresponds to $z$.\n *\n * $W(\\vec{x})$ is a spatial function that should be 1 at $\\vec{x}_C$ (i.e.\n * maximally skewed between the two objects) and fall off to 0 at the \\p\n * outer_radius, $R$. Typically the \\p outer_radius should be set to the\n * envelope radius for the `domain::creators::BinaryCompactObject`. The reason\n * that $W(\\vec{x})$ *should* be 1 at $\\vec{x}_C$, and doesn't *need* to be 1 at\n * $\\vec{x}_C$ is because having $W(\\vec{x})$ centered at the origin is much\n * better for the smoothness of higher derivatives of $W(\\vec{x})$ than if it\n * were centered at $\\vec{x}_C$. The important part is that the map is left\n * invariant along the $x=x_C$ line and that $W(\\vec{x}_C)\\approx 1$ for the\n * skew control system, both of which are satisfied if $W(\\vec{x})$ is centered\n * at the origin and $|\\vec{x}_C|\\ll R$.\n *\n * With that in mind, $W$ is chosen to be\n *\n * \\begin{equation}\n *   \\label{eq:W}\n *   W(\\vec{x}) = \\frac{1}{2}\\left(1 + \\cos(\\pi\\lambda(\\vec{x}))\\right).\n * \\end{equation}\n *\n * When the $\\cos$ term is $-1$, then $W = 0$, and when the $\\cos$ term is 1,\n * then $W = 1$. Therefore, the function $\\lambda(\\vec{x})$ must go from 0 at\n * the origin, to 1 at $R$. We choose $\\lambda(\\vec{x})$ to quadratically go\n * from 0 at the origin to 1 at $R$ with the form\n *\n * \\begin{equation}\n *   \\label{eq:lambda}\n *   \\lambda(\\vec{x}) = \\frac{|\\vec{x}|^2}{R^2}.\n * \\end{equation}\n *\n * A quadratic form was chosen for $\\lambda$ rather than higher powers of 2 to\n * avoid needing too much resolution to resolve a steep falloff in the function.\n *\n * \\note If the quantity $S = \\tan(F_y(t))(y-y_C) + \\tan(F_z(t))(z-z_C)$ becomes\n * too large, then the map becomes singular because multiple $x$ will be mapped\n * to the same $\\bar{x}$. This is due to the fact we are adding a linear term to\n * a cosine term with different weights. If $S$ is too large, the cosine term\n * will overpower the linear and the map will become singular.\n *\n * ### Inverse\n * To find the inverse, we need to solve a 1D root find for the $x$ component of\n * the coordinate. The inverse of the $\\bar{y}$ and $\\bar{z}$ coordinates are\n * trivial because there was no mapping. The equation we need to find the root\n * of is\n *\n * \\begin{equation}\n *   0 = x - W(\\vec{x})\\left(\\tan(F_y(t))(y-y_C) + \\tan(F_z(t))(z-z_C)\\right) -\n *   \\bar{x}\n * \\end{equation}\n *\n * We can bound the root by noticing that in $\\ref{eq:map}$, if we substitute\n * the extremal values of $W = 0$ and $W = 1$, we get bounds on $\\bar{x}$ which\n * we can turn into bounds on $x$:\n *\n * \\begin{align}\n *   x &<=& \\bar{x} &<=& x - (\\tan(F_y)(y-y_C) + \\tan(F_z)(z-z_C)) \\\\\n *   0 &<=& \\bar{x} - x &<=& -(\\tan(F_y)(y-y_C) + \\tan(F_z)(z-z_C)) \\\\\n *   -\\bar{x} &<=& - x &<=& -\\bar{x} - (\\tan(F_y)(y-y_C) + \\tan(F_z)(z-z_C)) \\\\\n *   \\bar{x} &>=& x &>=& \\bar{x} + (\\tan(F_y)(y-y_C) + \\tan(F_z)(z-z_C))\n * \\end{align}\n *\n * where on each line we just made simple arithmetic operations. We pad each\n * bound by $10^{-14}$ just to avoid roundoff issues. If either of the bounds is\n * within roundoff of zero, the map is the identity at that point and we forgo\n * the root find. The root that is found is the original $x$ coordinate.\n *\n * ### Frame Velocity\n * Taking the time derivative of $\\ref{eq:map}$, the frame velocity is\n *\n * \\begin{align}\n *   \\label{eq:frame_vel}\n *   \\dot{\\bar{x}} &= -W(\\vec{x})\\left(\\dot{F}_y(t)(1 + \\tan^2(F_y(t)))(y-y_C) +\n *     \\dot{F}_z(t)(1 + \\tan^2(F_z(t))(z-z_C))\\right) \\\\\n *   \\dot{\\bar{y}} &= 0 \\\\\n *   \\dot{\\bar{z}} &= 0\n * \\end{align}\n *\n * ### Jacobian and Inverse Jacobian\n * Considering the first terms in each equation of $\\ref{eq:map}$, part of the\n * jacobian will be the identity matrix. The rest will come from only the $x$\n * equation. Therefore we can express the jacobian as\n *\n * \\begin{equation}\n *   \\frac{\\partial\\bar{x}^i}{\\partial x^j} = \\delta^i_j + {W^i}_j\n * \\end{equation}\n *\n * where all components of ${W^i}_j$ are zero except the following\n *\n * \\begin{align}\n *   {W^0}_0 &= \\frac{\\partial(\\bar{x}-x)}{\\partial x} &= -\\frac{\\partial\n *     W(\\vec{x})}{\\partial x}&\\left(\\tan(F_y(t))(y-y_C) +\n *     \\tan(F_z(t))(z-z_C)\\right), \\\\\n *   {W^0}_1 &= \\frac{\\partial(\\bar{x}-x)}{\\partial y} &= -\\frac{\\partial\n *     W(\\vec{x})}{\\partial y}&\\left(\\tan(F_y(t))(y-y_C) +\n *     \\tan(F_z(t))(z-z_C)\\right) -\n *     W\\tan(F_y(t)), \\\\\n *   {W^0}_2 &= \\frac{\\partial(\\bar{x}-x)}{\\partial z} &= -\\frac{\\partial\n *     W(\\vec{x})}{\\partial z}&\\left(\\tan(F_y(t))(y-y_C) +\n *     \\tan(F_z(t))(z-z_C)\\right) - W\\tan(F_z(t)).\n * \\end{align}\n *\n * The gradient of $W(\\vec{x})$ (Eq. $\\ref{eq:W}$) is given by\n *\n * \\begin{equation}\n *   \\frac{\\partial W(\\vec{x})}{\\partial x^i} =\n *   -\\frac{\\pi}{2}\\frac{\\partial\\lambda(\\vec{x})}{\\partial\n *   x^i}\\sin(\\pi\\lambda(\\vec{x})).\n * \\end{equation}\n *\n * The gradient of $\\lambda(\\vec{x})$ (Eq. $\\ref{eq:lambda}$) is given by\n *\n * \\begin{equation}\n *   \\frac{\\partial \\lambda(\\vec{x})}{\\partial x^i} = \\frac{2x^i}{R^2}\n * \\end{equation}\n *\n * The inverse jacobian is computed by numerically inverting the jacobian.\n */\nclass Skew {\n public:\n  static constexpr size_t dim = 3;\n\n  Skew(std::string function_of_time_name, const std::array<double, 3>& center,\n       double outer_radius);\n  Skew() = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords, double time,\n      const domain::FunctionsOfTimeMap& functions_of_time) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords, double time,\n      const domain::FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> frame_velocity(\n      const std::array<T, 3>& source_coords, double time,\n      const domain::FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords, double time,\n      const domain::FunctionsOfTimeMap& functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords, double time,\n      const domain::FunctionsOfTimeMap& functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  template <typename T>\n  tt::remove_cvref_wrap_t<T> get_width(const std::array<T, 3>& source_coords,\n                                       bool ignore_error = false) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> get_width_deriv(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> map_and_velocity_helper(\n      const std::array<T, 3>& source_coords, double time,\n      const domain::FunctionsOfTimeMap& functions_of_time,\n      bool return_velocity) const;\n\n  template <typename T>\n  void check_for_singular_map(\n      const std::array<T, 3>& source_coords, double time,\n      const FunctionsOfTimeMap& functions_of_time) const;\n\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Skew& lhs, const Skew& rhs);\n  std::string f_of_t_name_;\n  std::array<double, 3> center_{};\n  double outer_radius_{};\n  double one_over_outer_radius_squared_{};\n  std::unordered_set<std::string> f_of_t_names_;\n};\n\nbool operator!=(const Skew& lhs, const Skew& rhs);\n\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/SphericalCompression.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/SphericalCompression.hpp\"\n\n#include <array>\n#include <cmath>\n#include <iomanip>\n#include <limits>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <type_traits>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <typename T>\nusing ResultType = tt::remove_cvref_wrap_t<T>;\n\n// Evaluate \\f$\\lambda_{00}(t) * Y_{00} = \\lambda_{00}(t) / sqrt{4\\pi}\\f$.\ndouble lambda00_y00(\n    const std::string& f_of_t_name, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  return functions_of_time.at(f_of_t_name)->func(time)[0][0] * 0.25 *\n         M_2_SQRTPI;\n}\n\n// Evaluate \\f$\\lambda_{00}^{\\prime}(t) / sqrt{4\\pi}\\f$.\ndouble dt_lambda00_y00(\n    const std::string& f_of_t_name, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  return functions_of_time.at(f_of_t_name)->func_and_deriv(time)[1][0] * 0.25 *\n         M_2_SQRTPI;\n}\n\n// Evaluate \\f$\\rho^i = \\xi^i - C^i\\f$ or \\f$r^i = x^i - C^i\\f$.\ntemplate <typename T>\nstd::array<ResultType<T>, 3> radial_position(\n    const std::array<T, 3>& coords, const std::array<double, 3>& center) {\n  std::array<ResultType<T>, 3> result{};\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(result, i) = gsl::at(coords, i) - gsl::at(center, i);\n  }\n  return result;\n}\n\n// Adds the correction (terms other than the source coordinates) to\n// compute either the mapped coordinate (if input is initially the source\n// coordinates and and lambda_y is lambda00'(t) /sqrt(4*M_PI)) or the frame\n// velocity (if input is initially zero and lambda_y is lambda00'(t)\n// /sqrt(4*M_PI)).\ntemplate <bool InteriorMap, typename T>\nvoid correct_mapped_coordinate_or_frame_velocity(\n    const gsl::not_null<ResultType<T>*> input, const T& source_radial_coord,\n    const T& source_radius, const double lambda_y, const double min_radius,\n    const double max_radius) {\n  if constexpr (InteriorMap) {\n    *input -= lambda_y * source_radial_coord / min_radius;\n  } else {\n    *input -= lambda_y * source_radial_coord *\n              (max_radius / source_radius - 1.0) / (max_radius - min_radius);\n  }\n}\n\ntemplate <bool InteriorMap, typename T>\nbool check_radius(const T& radius, const double min_radius,\n                  const double max_radius) {\n  auto radius_in_expected_range =\n      [](const T& source_radius, const double min_rad, const double max_rad) {\n        constexpr double eps = 100 * std::numeric_limits<double>::epsilon();\n        if constexpr (std::is_floating_point<ResultType<T>>::value) {\n          if constexpr (InteriorMap) {\n            return source_radius <= min_rad + eps;\n          } else {\n            return source_radius >= min_rad - eps and\n                   source_radius <= max_rad + eps;\n          }\n        } else {\n          if constexpr (InteriorMap) {\n            return max(source_radius) <= min_rad + eps;\n          } else {\n            return min(source_radius) >= min_rad - eps and\n                   max(source_radius) <= max_rad + eps;\n          }\n        }\n      };\n  return radius_in_expected_range(radius, min_radius, max_radius);\n}\n\ntemplate <bool InteriorMap, typename T>\nvoid check_source_radius(const T& radius, const double min_radius,\n                         const double max_radius) {\n  if (!check_radius<InteriorMap, T>(radius, min_radius, max_radius)) {\n    ERROR(\"Source radius \" << std::setprecision(20) << radius\n                           << \" not in expected range. min_radius == \"\n                           << min_radius << \", max_radius == \" << max_radius\n                           << \", InteriorMap == \" << InteriorMap << \"\\n\");\n  }\n}\n\ntemplate <bool InteriorMap, typename T>\nbool check_target_radius(const T& radius, const double min_radius,\n                         const double max_radius) {\n  return check_radius<InteriorMap, T>(radius, min_radius, max_radius);\n}\n}  // namespace\n\nnamespace domain::CoordinateMaps::TimeDependent {\ntemplate <bool InteriorMap>\nSphericalCompression<InteriorMap>::SphericalCompression(\n    std::string function_of_time_name, const double min_radius,\n    const double max_radius, const std::array<double, 3>& center)\n    : f_of_t_name_(std::move(function_of_time_name)),\n      f_of_t_names_({f_of_t_name_}),\n      min_radius_(min_radius),\n      max_radius_(max_radius),\n      center_(center) {\n  ASSERT(max_radius - min_radius > 1.0e-10,\n         \"max_radius must be greater than min_radius, but max_radius - \"\n         \"min_radius == \"\n             << max_radius - min_radius);\n}\n\ntemplate <bool InteriorMap>\ntemplate <typename T>\nstd::array<ResultType<T>, 3> SphericalCompression<InteriorMap>::operator()(\n    const std::array<T, 3>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const std::array<ResultType<T>, 3> source_rad_position{\n      radial_position(source_coords, center_)};\n  const ResultType<T> source_radius{magnitude(source_rad_position)};\n  check_source_radius<InteriorMap>(source_radius, min_radius_, max_radius_);\n  std::array<ResultType<T>, 3> result{};\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(result, i) = gsl::at(source_coords, i);\n    correct_mapped_coordinate_or_frame_velocity<InteriorMap>(\n        make_not_null(&gsl::at(result, i)), gsl::at(source_rad_position, i),\n        source_radius, lambda00_y00(f_of_t_name_, time, functions_of_time),\n        min_radius_, max_radius_);\n  }\n  return result;\n}\n\ntemplate <bool InteriorMap>\nstd::optional<std::array<double, 3>> SphericalCompression<InteriorMap>::inverse(\n    const std::array<double, 3>& target_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const double lambda_y{lambda00_y00(f_of_t_name_, time, functions_of_time)};\n  if (UNLIKELY(min_radius_ - max_radius_ > lambda_y or\n               lambda_y > min_radius_)) {\n    return {};  // map not invertible\n  } else {\n    const std::array<double, 3> target_radial_position{\n        radial_position(target_coords, center_)};\n    const double target_radius{magnitude(target_radial_position)};\n    if (!check_target_radius<InteriorMap>(target_radius, min_radius_ - lambda_y,\n                                          max_radius_)) {\n      return std::nullopt;\n    }\n\n    double scale{std::numeric_limits<double>::signaling_NaN()};\n    if constexpr (InteriorMap) {\n      scale = 1.0 / (1.0 - lambda_y / min_radius_);\n    } else {\n      scale =\n          (max_radius_ - min_radius_ + lambda_y * max_radius_ / target_radius) /\n          (max_radius_ - min_radius_ + lambda_y);\n    }\n    std::array<double, 3> result{target_radial_position};\n    result[0] = result[0] * scale + center_[0];\n    result[1] = result[1] * scale + center_[1];\n    result[2] = result[2] * scale + center_[2];\n    return {result};\n  }\n}\n\ntemplate <bool InteriorMap>\ntemplate <typename T>\nstd::array<ResultType<T>, 3> SphericalCompression<InteriorMap>::frame_velocity(\n    const std::array<T, 3>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const std::array<ResultType<T>, 3> source_rad_position{\n      radial_position(source_coords, center_)};\n  const ResultType<T> source_radius{magnitude(source_rad_position)};\n  check_source_radius<InteriorMap>(source_radius, min_radius_, max_radius_);\n  auto result =\n      make_with_value<std::array<ResultType<T>, 3>>(source_radius, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(result, i) = 0.0;\n    correct_mapped_coordinate_or_frame_velocity<InteriorMap>(\n        make_not_null(&gsl::at(result, i)), gsl::at(source_rad_position, i),\n        source_radius, dt_lambda00_y00(f_of_t_name_, time, functions_of_time),\n        min_radius_, max_radius_);\n  }\n  return result;\n}\n\ntemplate <bool InteriorMap>\ntemplate <typename T>\ntnsr::Ij<ResultType<T>, 3, Frame::NoFrame>\nSphericalCompression<InteriorMap>::jacobian(\n    const std::array<T, 3>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  const std::array<ResultType<T>, 3> source_rad_position{\n      radial_position(source_coords, center_)};\n  const ResultType<T> source_radius{magnitude(source_rad_position)};\n  const double lambda_y{lambda00_y00(f_of_t_name_, time, functions_of_time)};\n  check_source_radius<InteriorMap>(source_radius, min_radius_, max_radius_);\n  auto jacobian_matrix{\n      make_with_value<tnsr::Ij<ResultType<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]),\n          std::numeric_limits<double>::signaling_NaN())};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      if constexpr (not InteriorMap) {\n        jacobian_matrix.get(i, j) =\n            gsl::at(source_rad_position, i) * gsl::at(source_rad_position, j) *\n            lambda_y * (max_radius_) / (max_radius_ - min_radius_) /\n            cube(source_radius);\n        if (i == j) {\n          jacobian_matrix.get(i, j) +=\n              (1.0 - lambda_y * (max_radius_ / source_radius - 1.0) /\n                         (max_radius_ - min_radius_));\n        }\n      } else {\n        if (i == j) {\n          jacobian_matrix.get(i, j) = 1.0 - lambda_y / min_radius_;\n        } else {\n          jacobian_matrix.get(i, j) = 0.0;\n        }\n      }\n    }\n  }\n  return jacobian_matrix;\n}\n\ntemplate <bool InteriorMap>\ntemplate <typename T>\ntnsr::Ij<ResultType<T>, 3, Frame::NoFrame>\nSphericalCompression<InteriorMap>::inv_jacobian(\n    const std::array<T, 3>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  // Obtain the Jacobian and then compute its inverse numerically.\n  // There is a clear opportunity here for a potential future optimization:\n  // instead of taking the determinant and inverse numerically, it is\n  // likely possible to compute them analytically.\n  return determinant_and_inverse(\n             jacobian(source_coords, time, functions_of_time))\n      .second;\n}\n\ntemplate <bool InteriorMap>\nvoid SphericalCompression<InteriorMap>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | f_of_t_name_;\n    p | min_radius_;\n    p | max_radius_;\n    p | center_;\n  }\n\n  // No need to pup this because it is uniquely determined by f_of_t_name_\n  if (p.isUnpacking()) {\n    f_of_t_names_.clear();\n    f_of_t_names_.insert(f_of_t_name_);\n  }\n}\n\n#define INTERIOR_MAP(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  SphericalCompression<INTERIOR_MAP(data)>::operator()(                      \\\n      const std::array<DTYPE(data), 3>& source_coords, const double time,    \\\n      const std::unordered_map<                                              \\\n          std::string,                                                       \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&         \\\n          functions_of_time) const;                                          \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  SphericalCompression<INTERIOR_MAP(data)>::frame_velocity(                  \\\n      const std::array<DTYPE(data), 3>& source_coords, const double time,    \\\n      const std::unordered_map<                                              \\\n          std::string,                                                       \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&         \\\n          functions_of_time) const;                                          \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  SphericalCompression<INTERIOR_MAP(data)>::jacobian(                        \\\n      const std::array<DTYPE(data), 3>& source_coords, double time,          \\\n      const std::unordered_map<                                              \\\n          std::string,                                                       \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&         \\\n          functions_of_time) const;                                          \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  SphericalCompression<INTERIOR_MAP(data)>::inv_jacobian(                    \\\n      const std::array<DTYPE(data), 3>& source_coords, double time,          \\\n      const std::unordered_map<                                              \\\n          std::string,                                                       \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&         \\\n          functions_of_time) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (true, false),\n                        (double, DataVector,\n                         std::reference_wrapper<const double>,\n                         std::reference_wrapper<const DataVector>))\n#undef DTYPE\n#undef INSTANTIATE\n\n#define INSTANTIATE(_, data)                                               \\\n  template SphericalCompression<INTERIOR_MAP(data)>::SphericalCompression( \\\n      std::string function_of_time_name, const double min_radius,          \\\n      const double max_radius, const std::array<double, 3>& center);       \\\n  template std::optional<std::array<double, 3>>                            \\\n  SphericalCompression<INTERIOR_MAP(data)>::inverse(                       \\\n      const std::array<double, 3>& target_coords, const double time,       \\\n      const std::unordered_map<                                            \\\n          std::string,                                                     \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&       \\\n          functions_of_time) const;                                        \\\n  template void SphericalCompression<INTERIOR_MAP(data)>::pup(PUP::er& p);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (true, false))\n#undef INTERIOR_MAP\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/SphericalCompression.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps::TimeDependent {\n/*!\n * \\ingroup CoordMapsTimeDependentGroup\n * \\brief Time-dependent compression of a finite 3D spherical volume.\n *\n * \\details Let \\f$\\xi^i\\f$ be the unmapped coordinates, and let \\f$\\rho\\f$ be\n * the Euclidean radius corresponding to these coordinates with respect to\n * some center \\f$C^i\\f$. The transformation implemented by this map is\n * equivalent to the following transformation: at each point, the mapped\n * coordinates are the same as the unmapped coordinates, except in\n * a spherical region \\f$\\rho \\leq \\rho_{\\rm max}\\f$, where instead coordinates\n * are mapped using a compression that is spherically symmetric about the center\n * \\f$C^i\\f$. The amount of compression decreases linearly from a maximum at\n * \\f$\\rho = \\rho_{\\rm min}\\f$ to zero at \\f$\\rho = \\rho_{\\rm max}\\f$. A\n * scalar domain::FunctionsOfTime::FunctionOfTime \\f$\\lambda_{00}(t)\\f$ controls\n * the amount of compression.\n *\n * The mapped coordinates are a continuous function of the unmapped\n * coordinates, but the Jacobians are not continuous at \\f$\\rho_{\\rm min}\\f$\n * and \\f$\\rho_{\\rm max}\\f$. Therefore, \\f$\\rho_{\\rm min}\\f$ and \\f$\\rho_{\\rm\n * max}\\f$ should both be surfaces corresponding to block boundaries. Therefore,\n * this class implements the transformation described above as follows: the\n * if the template parameter `InteriorMap` is true, the map is the one\n * appropriate for \\f$\\rho < \\rho_{\\rm min}\\f$, while if `InteriorMap` is false,\n * the map is the one appropriate for \\f$\\rho_{\\rm min} \\leq \\rho \\leq \\rho_{\\rm\n * max}\\f$. To use this map, add it to the blocks where the transformation is\n * not the identity, using the appropriate template parameter, depending on\n * which region the block is in.\n *\n * \\note This map performs a only a spherical compression. A\n * generalization of this map that changes the region's shape as well as\n * its size, by including more terms than the spherically symmetric\n * term included here, can be found in the\n * domain::CoordinateMaps::TimeDependent::Shape map.\n *\n * \\note The quantity stored in the FunctionOfTime is really\n * the spherical-harmonic coefficient \\f$\\lambda_{00}(t)\\f$.  This is\n * different from the Shape map, which stores ylm::Spherepack coefficients\n * \\f$a_{lm}(t)\\f$ and \\f$b_{lm}(t)\\f$ instead of \\f$\\lambda_{lm}(t)\\f$.\n * See domain::CoordinateMaps::TimeDependent::Shape for more details.\n *\n * ### Mapped coordinates\n *\n * The mapped coordinates\n * \\f$x^i\\f$ are related to the unmapped coordinates \\f$\\xi^i\\f$\n * as follows:\n * \\f{align}{\n * x^i &= \\left\\{\\begin{array}{ll}\\xi^i - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho^i}{\\rho_{\\rm min}}, & \\rho < \\rho_{\\rm min}, \\\\\n * \\xi^i - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max} / \\rho - 1}{\\rho_{\\rm max} - \\rho_{\\rm min}} \\rho^i, &\n * \\rho_{\\rm min} \\leq \\rho \\leq \\rho_{\\rm max}, \\\\\n * \\xi^i, & \\rho_{\\rm max} < \\rho,\\end{array}\\right.\n * \\f}\n * where \\f$\\rho^i = \\xi^i - C^i\\f$ is the Euclidean radial position vector in\n * the unmapped coordinates with respect to the center \\f$C^i\\f$, \\f$\\rho =\n * \\sqrt{\\delta_{kl}\\left(\\xi^k - C^l\\right)\\left(\\xi^l - C^l\\right)}\\f$ is the\n * Euclidean magnitude of \\f$\\rho^i\\f$, and \\f$\\rho_j = \\delta_{ij} \\rho^i\\f$.\n *\n * ### Frame velocity\n *\n * The frame velocity \\f$v^i \\equiv dx^i/dt\\f$ is then\n * \\f{align}{\n * v^i &= \\left\\{\\begin{array}{ll} - \\frac{\\lambda_{00}^\\prime(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho^i}{\\rho_{\\rm min}}, & \\rho < \\rho_{\\rm min}, \\\\\n * - \\frac{\\lambda_{00}^\\prime(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max} / \\rho - 1}{\\rho_{\\rm max} - \\rho_{\\rm min}} \\rho^i,\n * & \\rho_{\\rm min} \\leq \\rho \\leq \\rho_{\\rm max}, \\\\\n * 0, & \\rho_{\\rm max} < \\rho,\\end{array}\\right.\n * \\f} where \\f$\\lambda_{00}^\\prime(t) \\equiv d\\lambda_{00}/dt\\f$.\n *\n * ### Jacobian\n *\n * Differentiating the equations for \\f$x^i\\f$ gives the Jacobian\n * \\f$\\partial x^i / \\partial \\xi^j\\f$. Using the result\n * \\f{align}{\n * \\frac{\\partial \\rho^i}{\\partial \\xi^j} &= \\frac{\\partial}{\\partial \\xi^j}\n * \\left(\\xi^i - C^i\\right) = \\frac{\\partial \\xi^i}{\\partial \\xi^j}\n * = \\delta^i_{j}\n * \\f}\n * and taking the derivatives yields\n * \\f{align}{\n * \\frac{\\partial x^i}{\\partial \\xi^j} &= \\left\\{\\begin{array}{ll}\n * \\delta^i_j \\left(1\n * - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}} \\frac{1}{\\rho_{\\rm min}}\\right),\n * & \\rho < \\rho_{\\rm min},\\\\\n * \\delta^i_j\n * \\left(1 - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max} / \\rho - 1}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\right)\n * - \\rho^i \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\partial}{\\partial \\xi^j}\\left(\n * \\frac{\\rho_{\\rm max} / \\rho - 1}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\right),\n * & \\rho_{\\rm min} \\leq \\rho < \\rho_{\\rm max},\\\\\n * \\delta^i_j, & \\rho_{\\rm max} < \\rho.\\end{array}\\right.\n * \\f}\n * Inserting\n * \\f{align}{\n * \\frac{\\partial}{\\partial \\xi^j}\\left(\n * \\frac{\\rho_{\\rm max} / \\rho - 1}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\right)\n * &= \\frac{\\rho_{\\rm max}}{\\rho_{\\rm max} - \\rho_{\\rm min}}\n * \\frac{\\partial}{\\partial \\xi^j}\\left(\\frac{1}{\\rho}\\right)\n * = - \\frac{\\rho_{\\rm max}}{\\rho_{\\rm max} - \\rho_{\\rm min}} \\frac{1}{\\rho^2}\n * \\frac{\\partial \\rho}{\\partial \\xi^j}\n * \\f}\n * and\n * \\f{align}{\n * \\frac{\\partial \\rho}{\\partial \\xi^j} &= \\frac{\\rho_j}{\\rho}.\n * \\f}\n * into the Jacobian yields\n * \\f{align}{\n * \\frac{\\partial x^i}{\\partial \\xi^j} &= \\left\\{\\begin{array}{ll}\n * \\delta^i_j \\left(1\n * - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}} \\frac{1}{\\rho_{\\rm min}}\\right),\n * & \\rho < \\rho_{\\rm min},\\\\\n * \\delta^i_j\n * \\left(1 - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max} / \\rho - 1}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\right)\n * + \\rho^i \\rho_j \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max}}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\frac{1}{\\rho^3},\n * & \\rho_{\\rm min} \\leq \\rho < \\rho_{\\rm max},\\\\\n * \\delta^i_j, & \\rho_{\\rm max} < \\rho.\\end{array}\\right.\n * \\f}\n *\n * ### Inverse Jacobian\n *\n * This map finds the inverse Jacobian by first finding the Jacobian and then\n * numerically inverting it.\n *\n * ### Inverse map\n *\n * For \\f$\\lambda_{00}(t)\\f$ that satisfy\n * \\f{align}{\n * \\rho_{\\rm min} - \\rho_{\\rm max} < \\lambda_{00}(t) / \\sqrt{4\\pi} <\n * \\rho_{\\rm min},\n * \\f}\n * the map will be invertible and nonsingular. For simplicity, here we\n * enforce this condition, even though perhaps the map might be generalized to\n * handle cases that are still invertible but violate this condition. This\n * avoids the need to specially handle the cases\n * \\f$\\lambda_{00}(t) / \\sqrt{4\\pi} = \\rho_{\\rm min} - \\rho_{\\rm max}\\f$\n * and \\f$\\lambda_{00}(t) / \\sqrt{4\\pi} = \\rho_{\\rm min}\\f$, both of which\n * yield a singular map, and it also avoids cases where the map behaves\n * in undesirable ways (such as a larger \\f$\\lambda_{00}(t)\\f$ leading to\n * an expansion and a coordinate inversion instead of a compression).\n *\n * After\n * requiring the above inequality to be satisfied, however, the inverse mapping\n * can be derived as follows. Let \\f$r^i \\equiv x^i - C^i\\f$. In terms of\n * \\f$r^i\\f$, the map is \\f{align}{ r^i &= \\left\\{\\begin{array}{ll}\\rho^i\n * \\left(1 - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{1}{\\rho_{\\rm min}}\\right), & \\rho < \\rho_{\\rm min}, \\\\\n * \\rho^i\\left(1 - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max} / \\rho - 1}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\right),\n * & \\rho_{\\rm min} \\leq \\rho \\leq \\rho_{\\rm max}, \\\\\n * \\rho^i, & \\rho_{\\rm max} < \\rho.\\end{array}\\right.\n * \\f}\n *\n * Taking the Euclidean magnitude of both sides and simplifying yields\n * \\f{align}{\n * \\frac{r}{\\rho} &= \\left\\{\\begin{array}{ll}\n * 1 - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{1}{\\rho_{\\rm min}}, & \\rho < \\rho_{\\rm min}, \\\\\n * 1 - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max}/\\rho - 1}{\\rho_{\\rm max} - \\rho_{\\rm min}},\n * & \\rho_{\\rm min} \\leq \\rho \\leq \\rho_{\\rm max}, \\\\\n * 1, & \\rho_{\\rm max} < \\rho,\\end{array}\\right.\n * \\f}\n * which implies\n * \\f{align}{\n * r^i = \\rho^i \\frac{r}{\\rho} \\Rightarrow \\rho^i = r^i \\frac{\\rho}{r}.\n * \\f}\n *\n * Inserting \\f$\\rho_{\\rm min}\\f$ or \\f$\\rho_{\\rm max}\\f$ then gives the\n * corresponding bounds in the mapped coordinates: \\f{align}{\n * r_{\\rm min} &= \\rho_{\\rm min} - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}},\\\\\n * r_{\\rm max} &= \\rho_{\\rm max}.\n * \\f}\n *\n * In the regime \\f$\\rho_{\\rm min} \\leq \\rho < \\rho_{\\rm max}\\f$, rearranging\n * yields a linear relationship between \\f$\\rho\\f$ and \\f$r\\f$, which\n * can then be solved for \\f$\\rho(r)\\f$:\n * \\f{align}{\n * r &= \\rho - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max} - \\rho}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\\\\n * \\Rightarrow r &= \\rho \\left(1 + \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{1}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\right)\n * - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{\\rho_{\\rm max}}{\\rho_{\\rm max} - \\rho_{\\rm min}}.\n * \\f}\n * Solving this linear equation for \\f$\\rho\\f$ yields\n * \\f{align}{\n * \\rho &= \\left(r+\\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\\frac{\\rho_{\\rm\n * max}}{\\rho_{\\rm max}-\\rho_{\\rm min}}\\right)\n * \\left(1 + \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{1}{\\rho_{\\rm max} - \\rho_{\\rm min}}\\right)^{-1}.\n * \\f}\n *\n * Inserting the expressions for \\f$\\rho\\f$ into the equation\n * \\f{align}{\n * \\rho^i = r^i \\frac{\\rho}{r}\n * \\f}\n * then gives\n * \\f{align}{\n * \\rho^i &= \\left\\{\\begin{array}{ll}\n * r^i\\left(1 - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{1}{\\rho_{\\rm min}}\\right)^{-1},\n * & r < \\rho_{\\rm min} - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}},\\\\\n * r^i\n * \\left(1+\\frac{1}{r}\\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\\frac{\\rho_{\\rm\n * max}}{\\rho_{\\rm max}-\\rho_{\\rm min}}\\right)\\left(1 +\n * \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}} \\frac{1}{\\rho_{\\rm max} - \\rho_{\\rm\n * min}}\\right)^{-1}, & \\rho_{\\rm min} - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\leq r\n * \\leq \\rho_{\\rm max},\\\\\n * r^i, & \\rho_{\\rm max} < r.\\end{array}\\right.\n * \\f}\n * Finally, inserting \\f$\\rho^i = \\xi^i - C^i\\f$ yields the inverse map:\n * \\f{align}{\n * \\xi^i &= \\left\\{\\begin{array}{ll}\n * r^i\\left(1 - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\n * \\frac{1}{\\rho_{\\rm min}}\\right)^{-1} + C^i,\n * & r < \\rho_{\\rm min} - \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}},\\\\\n * r^i\n * \\left(1+\\frac{1}{r}\\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}}\\frac{\\rho_{\\rm\n * max}}{\\rho_{\\rm max}-\\rho_{\\rm min}}\\right)\\left(1 +\n * \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}} \\frac{1}{\\rho_{\\rm max} - \\rho_{\\rm\n * min}}\\right)^{-1} + C^i, & \\rho_{\\rm min} -\n * \\frac{\\lambda_{00}(t)}{\\sqrt{4\\pi}} \\leq r\n * \\leq \\rho_{\\rm max},\\\\\n * r^i + C^i = x^i, & \\rho_{\\rm max} < r.\\end{array}\\right.\n * \\f}\n *\n */\ntemplate <bool InteriorMap>\nclass SphericalCompression {\n public:\n  static constexpr size_t dim = 3;\n\n  explicit SphericalCompression(std::string function_of_time_name,\n                                double min_radius, double max_radius,\n                                const std::array<double, 3>& center);\n  SphericalCompression() = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> frame_velocity(\n      const std::array<T, 3>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  friend bool operator==(const SphericalCompression& lhs,\n                         const SphericalCompression& rhs) {\n    return lhs.f_of_t_name_ == rhs.f_of_t_name_ and\n           lhs.min_radius_ == rhs.min_radius_ and\n           lhs.max_radius_ == rhs.max_radius_ and lhs.center_ == rhs.center_;\n  }\n  std::string f_of_t_name_;\n  std::unordered_set<std::string> f_of_t_names_;\n  double min_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double max_radius_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> center_;\n};\n\ntemplate <bool InteriorMap>\nbool operator!=(const SphericalCompression<InteriorMap>& lhs,\n                const SphericalCompression<InteriorMap>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/Translation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n\n#include <cmath>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::CoordinateMaps::TimeDependent {\n\ntemplate <size_t Dim>\nTranslation<Dim>::Translation(std::string function_of_time_name)\n    : f_of_t_name_(std::move(function_of_time_name)),\n      f_of_t_names_({f_of_t_name_}),\n      inner_radius_(std::nullopt),\n      outer_radius_(std::nullopt),\n      f_of_r_(nullptr),\n      center_(make_array<Dim, double>(0.0)) {}\n\ntemplate <size_t Dim>\nTranslation<Dim>::Translation(std::string function_of_time_name,\n                              double inner_radius, double outer_radius)\n    : f_of_t_name_(std::move(function_of_time_name)),\n      f_of_t_names_({f_of_t_name_}),\n      inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      f_of_r_(nullptr),\n      center_(make_array<Dim, double>(0.0)) {}\n\ntemplate <size_t Dim>\nTranslation<Dim>::Translation(\n    std::string function_of_time_name,\n    std::unique_ptr<MathFunction<1, Frame::Inertial>> radial_function,\n    std::array<double, Dim>& center)\n    : f_of_t_name_(std::move(function_of_time_name)),\n      f_of_t_names_({f_of_t_name_}),\n      inner_radius_(std::nullopt),\n      outer_radius_(std::nullopt),\n      f_of_r_(std::move(radial_function)),\n      center_(center) {}\n\ntemplate <size_t Dim>\nTranslation<Dim>::Translation(const Translation<Dim>& Translation_Map)\n    : f_of_t_name_(Translation_Map.f_of_t_name_),\n      f_of_t_names_(Translation_Map.f_of_t_names_),\n      inner_radius_(Translation_Map.inner_radius_),\n      outer_radius_(Translation_Map.outer_radius_),\n      center_(Translation_Map.center_) {\n  if (Translation_Map.f_of_r_ == nullptr) {\n    f_of_r_ = nullptr;\n  } else {\n    f_of_r_ = Translation_Map.f_of_r_->get_clone();\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Translation<Dim>::operator()(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  if (inner_radius_.has_value()) {\n    return piecewise_helper(source_coords, time, functions_of_time, 0);\n  } else {\n    return math_function_helper(source_coords, time, functions_of_time, 0);\n  }\n}\n\ntemplate <size_t Dim>\nstd::optional<std::array<double, Dim>> Translation<Dim>::inverse(\n    const std::array<double, Dim>& target_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  std::array<double, Dim> result{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = gsl::at(target_coords, i);\n  }\n  const DataVector function_of_time =\n      functions_of_time.at(f_of_t_name_)->func(time)[0];\n  ASSERT(function_of_time.size() == Dim,\n         \"The dimension of the function of time (\"\n             << function_of_time.size()\n             << \") does not match the dimension of the translation map (\" << Dim\n             << \").\");\n  // If an inner radius specified then take the inverse of the\n  // piecewise specific translation.\n  if (inner_radius_.has_value()) {\n    const double target_radius = magnitude(target_coords);\n    double non_translated_radius = 0.;\n    for (size_t i = 0; i < Dim; i++) {\n      non_translated_radius +=\n          square(gsl::at(target_coords, i) - function_of_time[i]);\n    }\n    non_translated_radius = sqrt(non_translated_radius);\n    // Assumes no translation if the target radius is greater than the\n    // outer radius.\n    if (target_radius >= outer_radius_.value()) {\n      return result;\n    } else if (non_translated_radius <= inner_radius_.value()) {\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(result, i) -= function_of_time[i];\n      }\n    } else {\n      // We need to solve a quadratic of the form w^2a + wb + c where\n      // a = T(t)^2 - (R_{out} - R_{in})^2,\n      // b = 2(R_{out}(R_{out} - R_{in}) - T(t)\\vec{\\bar{\\xi}}), and\n      // c = \\vec{\\bar{\\xi}}^2 - R_{out}^2\n      double a = 0.;\n      double b = 0.;\n      double c = 0.;\n      for (size_t i = 0; i < Dim; i++) {\n        a += square(function_of_time[i]);\n        b -= 2 * function_of_time[i] * gsl::at(target_coords, i);\n        c += square(gsl::at(target_coords, i));\n      }\n      a -= square(outer_radius_.value() - inner_radius_.value());\n      b += 2 * outer_radius_.value() *\n           (outer_radius_.value() - inner_radius_.value());\n      c -= square(outer_radius_.value());\n      const std::optional<std::array<double, 2>> roots = real_roots(a, b, c);\n      double radial_falloff_factor = 0.;\n      ASSERT(\n          roots.has_value() and roots.value()[0] >= 0 and roots.value()[1] >= 0,\n          \"One of the roots is negative, this should not happen.\");\n      if (roots.value()[0] <= 1.0) {\n        ASSERT(roots.value()[1] > 1.0,\n               \"Singular map: two solutions between 0 and 1\");\n        radial_falloff_factor = roots.value()[0];\n      } else if (roots.value()[1] <= 1.0) {\n        radial_falloff_factor = roots.value()[1];\n      } else if (roots.value()[0] <=\n                     std::numeric_limits<double>::epsilon() + 1.0 or\n                 roots.value()[1] <=\n                     std::numeric_limits<double>::epsilon() + 1.0) {\n        radial_falloff_factor = 1.0;\n      }\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(result, i) -=\n            gsl::at(function_of_time, i) * radial_falloff_factor;\n      }\n    }\n    return result;\n    // Otherwise take the inverse of the radial MathFunction translation.\n  } else {\n    double radial_function_value = 1.0;\n    // If a MathFunction is specified, do a root find.\n    if (f_of_r_ != nullptr) {\n      const double root =\n          root_finder(target_coords - center_, function_of_time);\n      radial_function_value = (*f_of_r_)(root);\n    }\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(result, i) -= function_of_time[i] * radial_function_value;\n    }\n    return result;\n  }\n}\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Translation<Dim>::frame_velocity(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  if (inner_radius_.has_value()) {\n    return piecewise_helper(source_coords, time, functions_of_time, 1);\n  } else {\n    return math_function_helper(source_coords, time, functions_of_time, 1);\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nTranslation<Dim>::jacobian(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  // If inner radius has a value, then calculate the jacobian for the piecewise\n  // translation.\n  if (inner_radius_.has_value()) {\n    const tt::remove_cvref_wrap_t<T> radius = magnitude(source_coords);\n    const DataVector function_of_time =\n        functions_of_time.at(f_of_t_name_)->func(time)[0];\n    auto result = make_with_value<\n        tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>>(\n        dereference_wrapper(source_coords[0]), 0.0);\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t j = 0; j < Dim; j++) {\n        for (size_t k = 0; k < get_size(radius); k++) {\n          // The jacobian is the identity for regions not between the inner and\n          // outer radius.\n          if (get_element(radius, k) > inner_radius_.value() and\n              get_element(radius, k) < outer_radius_.value()) {\n            // using the derivative of the radial falloff factor as\n            // \\frac{dw}{dr} = \\frac{-1.0}{R_{out} - R{in}}\n            get_element(result.get(i, j), k) =\n                (-1.0 / (outer_radius_.value() - inner_radius_.value())) *\n                gsl::at(function_of_time, i) *\n                get_element(dereference_wrapper(gsl::at(source_coords, j)), k) /\n                get_element(radius, k);\n          }\n        }\n      }\n      result.get(i, i) += 1.0;\n    }\n    return result;\n    // Otherwise calculate the jacobian for the MathFunction translation.\n  } else {\n    if (f_of_r_ == nullptr) {\n      return identity<Dim>(dereference_wrapper(source_coords[0]));\n    } else {\n      std::array<tt::remove_cvref_wrap_t<T>, Dim> distance_to_center{};\n      for (size_t i = 0; i < Dim; i++) {\n        gsl::at(distance_to_center, i) =\n            gsl::at(source_coords, i) - gsl::at(center_, i);\n      }\n      const tt::remove_cvref_wrap_t<T> radius = magnitude(distance_to_center);\n      const DataVector function_of_time =\n          functions_of_time.at(f_of_t_name_)->func(time)[0];\n\n      auto result = make_with_value<\n          tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n      for (size_t i = 0; i < Dim; i++) {\n        for (size_t j = 0; j < Dim; j++) {\n          for (size_t k = 0; k < get_size(radius); k++) {\n            if (get_element(radius, k) > 1.e-13) {\n              get_element(result.get(i, j), k) =\n                  (*f_of_r_).first_deriv(get_element(radius, k)) *\n                  gsl::at(function_of_time, i) *\n                  get_element(gsl::at(distance_to_center, j), k) /\n                  get_element(radius, k);\n            } else {\n              ASSERT(equal_within_roundoff(\n                         (*f_of_r_).first_deriv(get_element(radius, k)), 0.),\n                     \"Non-smooth map, MathFunction derivative should be 0.0 at \"\n                     \"map center\");\n            }\n          }\n        }\n        result.get(i, i) += 1.0;\n      }\n      return result;\n    }\n  }\n}\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nTranslation<Dim>::inv_jacobian(\n    const std::array<T, Dim>& source_coords, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  if (f_of_r_ == nullptr and not inner_radius_.has_value()) {\n    return identity<Dim>(dereference_wrapper(source_coords[0]));\n  } else {\n    return determinant_and_inverse(\n               jacobian(source_coords, time, functions_of_time))\n        .second;\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim>\nTranslation<Dim>::math_function_helper(\n    const std::array<T, Dim>& source_coords, double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const size_t function_or_deriv_index) const {\n  const DataVector func_or_deriv_of_time =\n      gsl::at(functions_of_time.at(f_of_t_name_)->func_and_deriv(time),\n              function_or_deriv_index);\n  ASSERT(func_or_deriv_of_time.size() == Dim,\n         \"The dimension of the function of time (\"\n             << func_or_deriv_of_time.size()\n             << \") does not match the dimension of the translation map (\" << Dim\n             << \").\");\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n  // sizing the result and getting the radial function value\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = gsl::at(source_coords, i);\n  }\n  auto radial_function_value = make_with_value<tt::remove_cvref_wrap_t<T>>(\n      dereference_wrapper(source_coords[0]), 1.0);\n  if (f_of_r_ != nullptr) {\n    radial_function_value = (*f_of_r_)(magnitude(result - center_));\n  }\n  if (function_or_deriv_index == 1) {\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(result, i) = 0.0;\n    }\n  }\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) +=\n        gsl::at(func_or_deriv_of_time, i) * radial_function_value;\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Translation<Dim>::piecewise_helper(\n    const std::array<T, Dim>& source_coords, double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const size_t function_or_deriv_index) const {\n  const DataVector func_or_deriv_of_time =\n      gsl::at(functions_of_time.at(f_of_t_name_)->func_and_deriv(time),\n              function_or_deriv_index);\n  ASSERT(func_or_deriv_of_time.size() == Dim,\n         \"The dimension of the function of time (\"\n             << func_or_deriv_of_time.size()\n             << \") does not match the dimension of the translation map (\" << Dim\n             << \").\");\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> result{};\n  // sizing the result and getting the radial function value\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = gsl::at(source_coords, i);\n  }\n  const tt::remove_cvref_wrap_t<T> radius = magnitude(result);\n  if (function_or_deriv_index == 1) {\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(result, i) = 0.0;\n    }\n  }\n  for (size_t k = 0; k < get_size(radius); k++) {\n    if (get_element(radius, k) <= inner_radius_.value()) {\n      for (size_t i = 0; i < Dim; i++) {\n        get_element(gsl::at(result, i), k) += gsl::at(func_or_deriv_of_time, i);\n      }\n      ASSERT(max(magnitude(result)) <= outer_radius_.value(),\n             \"Coordinates translated outside outer radius, This should not \"\n             \"happen\");\n    } else if (get_element(radius, k) > inner_radius_.value() and\n               get_element(radius, k) < outer_radius_.value()) {\n      // this is the linear falloff factor w in the documentation of the form\n      // w = (R_{out} - r) / (R_{out} - R_{in})\n      const double radial_falloff_factor =\n          (outer_radius_.value() - get_element(radius, k)) /\n          (outer_radius_.value() - inner_radius_.value());\n      for (size_t i = 0; i < Dim; i++) {\n        get_element(gsl::at(result, i), k) +=\n            gsl::at(func_or_deriv_of_time, i) * radial_falloff_factor;\n      }\n      ASSERT(max(magnitude(result)) <= outer_radius_.value(),\n             \"Coordinates translated outside outer radius, This should not \"\n             \"happen\");\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ndouble Translation<Dim>::root_finder(\n    const std::array<double, Dim>& distance_to_center,\n    const DataVector& function_of_time) const {\n  const double target_radius = magnitude(distance_to_center);\n  const double center_offset = norm(function_of_time);\n  // These bounds come from the SpEC implementation\n  double lower_bound_on_r = (target_radius - center_offset) * (1.0 - 1.e-9);\n  double upper_bound_on_r = (target_radius + center_offset) * (1.0 + 1.e-9);\n  if (lower_bound_on_r < 0.) {\n    lower_bound_on_r = 0.;\n  }\n  const double absolute_tol = std::numeric_limits<double>::epsilon() *\n                              std::max(target_radius, center_offset);\n  const double relative_tol = 2.0 * std::numeric_limits<double>::epsilon();\n  const auto wrapper = [this, &distance_to_center,\n                        &function_of_time](const double r_squared) {\n    // This form makes it clear that wrapper(0.0) <= 0.0, even with\n    // roundoff, so it is guaranteed that the lower end of the bracket\n    // is valid even when we have to correct for negative radii.\n    const double f_of_r = (*f_of_r_)(sqrt(r_squared));\n    double expression_to_zero = r_squared;\n    for (size_t i = 0; i < Dim; ++i) {\n      expression_to_zero -=\n          square(gsl::at(distance_to_center, i) - f_of_r * function_of_time[i]);\n    }\n    return expression_to_zero;\n  };\n  return sqrt(RootFinder::toms748(wrapper, square(lower_bound_on_r),\n                                  square(upper_bound_on_r), absolute_tol,\n                                  relative_tol));\n}\n\ntemplate <size_t Dim>\nvoid Translation<Dim>::pup(PUP::er& p) {\n  size_t version = 2;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | f_of_t_name_;\n  }\n  if (version >= 1) {\n    p | f_of_r_;\n    p | center_;\n  } else if (p.isUnpacking()) {\n    f_of_r_ = nullptr;\n    center_ = make_array<Dim, double>(0.0);\n  }\n  if (version >= 2) {\n    p | inner_radius_;\n    p | outer_radius_;\n  } else if (p.isUnpacking()) {\n    inner_radius_ = std::nullopt;\n    outer_radius_ = std::nullopt;\n  }\n\n  // No need to pup this because it is uniquely determined by f_of_t_name_\n  if (p.isUnpacking()) {\n    f_of_t_names_.clear();\n    f_of_t_names_.insert(f_of_t_name_);\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const Translation<Dim>& lhs, const Translation<Dim>& rhs) {\n  return lhs.f_of_t_name_ == rhs.f_of_t_name_ and\n         (lhs.f_of_r_ == nullptr) == (rhs.f_of_r_ == nullptr) and\n         ((lhs.f_of_r_ == nullptr and rhs.f_of_r_ == nullptr) or\n          *lhs.f_of_r_ == *rhs.f_of_r_) and\n         lhs.center_ == rhs.center_ and\n         lhs.inner_radius_ == rhs.inner_radius_ and\n         lhs.outer_radius_ == rhs.outer_radius_;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                              \\\n  template class Translation<DIM(data)>;                  \\\n  template bool operator==(const Translation<DIM(data)>&, \\\n                           const Translation<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)>      \\\n  Translation<DIM(data)>::operator()(                                       \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)>      \\\n  Translation<DIM(data)>::frame_velocity(                                   \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),        \\\n                    Frame::NoFrame>                                         \\\n  Translation<DIM(data)>::jacobian(                                         \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;                                         \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),        \\\n                    Frame::NoFrame>                                         \\\n  Translation<DIM(data)>::inv_jacobian(                                     \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords, double time, \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3),\n                        (double, DataVector,\n                         std::reference_wrapper<const double>,\n                         std::reference_wrapper<const DataVector>))\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependent/Translation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps::TimeDependent {\n/*!\n * \\ingroup CoordMapsTimeDependentGroup\n * \\brief Translation map defined by \\f$\\vec{x} = \\vec{\\xi}+F(r)\\vec{T}(t)\\f$\n * where $F(r)$ takes on different forms based on which constructor is used.\n *\n * \\details The map adds a translation to the coordinates $\\vec{\\xi}$ based on\n * what type of translation is needed. For the piecewise translation, a\n * translation $F(r)\\vec{T}(t)$ is added to $\\vec{\\xi}$ based on what region\n * $|\\vec{\\xi}|$ is in. For coordinates within the inner radius, $F(r) = 1$\n * causing a uniform translation. Coordinates in between the inner and outer\n * radius have a linear radial falloff applied to them. Coordinates beyond the\n * outer radius have no translation applied to them $F(r) = 0$. The piecewise\n * translation assumes that the center of your map is at (0., 0., 0.). For the\n * radial MathFunction translation, a radial translation \\f$F(r)\\vec{T}(t)\\f$ is\n * added to the coordinates \\f$\\vec{\\xi}\\f$, where \\f$\\vec{T}(t)\\f$ is a\n * FunctionOfTime and\\f$F(r)\\f$ is a 1D radial MathFunction. The radius of each\n * point is found by subtracting the center map argument from the coordinates\n * \\f$\\vec{\\xi}\\f$ or the target coordinates \\f$\\vec{\\bar{\\xi}}\\f$. The\n * Translation Map class is overloaded so that the user can choose between a\n * piecewise translation, radial translation or a uniform translation based on\n * their problem. If a radial dependence is not specified, this sets \\f$F(r) =\n * 1\\f$.\n *\n * ### Mapped Coordinates\n * The piecewise translation translates the coordinates $\\vec{\\xi}$\n * to the target coordinates $\\vec{\\bar{\\xi}}$ based on the region $\\vec{\\xi}$\n * is in.\n * \\f{equation}{\n * \\vec{\\bar{\\xi}} = \\left\\{\\begin{array}{ll}\\vec{\\xi} + \\vec{T}(t), &\n * |\\vec{\\xi}| \\leq R_{in}, \\\\ \\vec{\\xi} + wT(t), &  R_{in} < |\\vec{\\xi}| <\n * R_{out}, \\\\ \\vec{\\xi}, & |\\vec{\\xi}| \\geq R_{out} \\end{array}\\right.\n * \\f}\n *\n * Where $R_{in}$ is the inner radius, $R_{out}$ is the outer radius, and $w$ is\n * the radial falloff factor found through\n * \\f{equation}{\n * w = \\frac{R_{out} - |\\vec{\\xi}|}{R_{out} - R_{in}}\n * \\f}\n *\n * The radial MathFunction translation translates the coordinates\n * \\f$\\vec{\\xi}\\f$ to the target coordinates \\f{equation}{\\vec{\\bar{\\xi}} =\n * \\vec{\\xi} + F(r)\\vec{T}(t) \\f}\n *\n * If you only supply a FunctionOfTime to the constructor of this class, the\n * radial function will be set to 1.0 causing a uniform translation for your\n * coordinates. If a FunctionOfTime, MathFunction, and map center are passed in,\n * the radius will be found through\n * \\f{equation}{\n * r = |\\vec{\\xi} - \\vec{c}|\n * \\f}\n * where r is the radius and \\f$\\vec{c}\\f$ is the center argument.\n *\n * ### Inverse Translation\n * The piecewise inverse translates the coordinates\n * \\f$\\vec{\\bar{\\xi}}\\f$ to the original coordinates based on what region\n * $\\vec{\\bar{\\xi}}$ is in.\n * \\f{equation}{\n * \\vec{\\xi} = \\left\\{\\begin{array}{ll}\\vec{\\bar{\\xi}} -\n * \\vec{T}(t), & |\\vec{\\bar{\\xi}}| \\leq R_{in}, or, |\\vec{\\bar{\\xi}} - T(t)|\n * \\leq R_{in}, \\\\\n * \\vec{\\bar{\\xi}} - wT(t), &  R_{in} < |\\vec{\\bar{\\xi}}| < R_{out}, \\\\\n * \\vec{\\bar{\\xi}}, & |\\vec{\\bar{\\xi}}| \\geq R_{out}\\end{array}\\right.\n * \\f}\n * Where $w$ is the radial falloff factor found through a quadratic solve of the\n * form\n * \\f{equation}{\n * w^2(\\vec{T}(t)^2 - (R_{out} - R_{in})^2) - 2w(\\vec{T}(t)\\vec{\\bar{\\xi}} -\n * R_{out}(R_{out} - R_{in})) + \\vec{\\bar{\\xi}}^2 - R_{out}^2\n * \\f}\n * The inverse map also assumes that if $\\vec{\\bar{\\xi}}\n * - \\vec{T}(t) \\leq R_{in}$ then the translated point originally came from\n * within the inner radius so it'll be translated back without a quadratic\n * solve.\n *\n * The radial MathFunction inverse translates the coordinates\n * \\f$\\vec{\\bar{\\xi}}\\f$ to the original coordinates using\n * \\f{equation}{\n * \\vec{\\xi} = \\vec{\\bar{\\xi}} - F(r)\\vec{T}(t)\n * \\f}\n * where \\f$r^2\\f$ is found as the root of\n * \\f{equation}{\n *   r^2 = \\Big(\\vec{\\bar{\\xi}} - \\vec{c} - F(r) \\vec{T}(t)\\Big)^2.\n * \\f}\n *\n * ### Frame Velocity\n * For the piecewise translation, the frame velocity is found through\n * \\f{equation}{\n * \\vec{v} = \\left\\{\\begin{array}{ll}\\frac{\\vec{dT}(t)}{dt}, & |\\vec{\\xi}| \\leq\n * R_{in}, \\\\ w\\frac{\\vec{dT}(t)}{dt}, &  R_{in} < |\\vec{\\xi}| < R_{out}, \\\\ 0,\n * & |\\vec{\\xi}| \\geq R_{out} \\end{array}\\right.\n * \\f}\n *\n * For the radial MathFunction translation, the frame velocity is found through\n * \\f{equation}{\n * \\vec{v} = \\frac{\\vec{dT}(t)}{dt} F(r)\n * \\f}\n * where \\f$\\frac{\\vec{dT}(t)}{dt}\\f$ is the first derivative of the\n * FunctionOfTime.\n *\n * ### Jacobian\n * For the piecewise translation, the jacobian is computed based on what region\n * the coordinates $\\vec{\\xi}$ is in.\n * \\f{equation}{\n * {J^{i}}_{j} = \\frac{dw}{dr} T(t)^i \\frac{\\xi_j}{r}, R_{in} <\n * |\\vec{\\bar{\\xi}}| < R_{out}\n * \\f}\n * otherwise, it will return the identity matrix.\n *\n * For the radial MathFunction translation, the jacobian is computed through the\n * first derivative when the radius is bigger than 1.e-13:\n * \\f{equation}{\n * {J^{i}}_{j} = \\frac{dF(r)}{dr} T(t)^i \\frac{(\\xi_j - c_j)}{r}\n * \\f}\n * Where \\f$\\frac{dF(r)}{dr}\\f$ is the first derivative of the MathFunction,\n * \\f$\\vec{\\xi_j}\\f$ is the source coordinates, \\f$\\vec{c}\\f$ is the center of\n * your map, and r is the radius.\n *\n * At a radius smaller than 1e-13, we ASSERT that the radial MathFunction is\n * smooth $\\frac{dF(r)}{dr} \\approx 0$, so return the identity matrix.\n *\n *\n * ### Inverse Jacobian\n * The inverse jacobian is computed numerically by inverting the jacobian.\n */\ntemplate <size_t Dim>\nclass Translation {\n public:\n  static constexpr size_t dim = Dim;\n\n  Translation() = default;\n  explicit Translation(std::string function_of_time_name);\n\n  explicit Translation(std::string function_of_time_name, double inner_radius,\n                       double outer_radius);\n\n  explicit Translation(\n      std::string function_of_time_name,\n      std::unique_ptr<MathFunction<1, Frame::Inertial>> radial_function,\n      std::array<double, Dim>& center);\n\n  Translation(const Translation<Dim>& Translation_Map);\n\n  ~Translation() = default;\n  Translation(Translation&&) = default;\n  Translation& operator=(Translation&&) = default;\n  Translation& operator=(const Translation& Translation_Map);\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> operator()(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, Dim>> inverse(\n      const std::array<double, Dim>& target_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> frame_velocity(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n  const std::unordered_set<std::string>& function_of_time_names() const {\n    return f_of_t_names_;\n  }\n\n private:\n  template <size_t LocalDim>\n  friend bool operator==(  // NOLINT(readability-redundant-declaration)\n      const Translation<LocalDim>& lhs, const Translation<LocalDim>& rhs);\n\n  // These 2 helper functions compute the translated coordinates or frame\n  // velocity based on the option passed in, 0 for translated coordinates, and\n  // frame velocity for any other number.\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> math_function_helper(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      size_t function_or_deriv_index) const;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> piecewise_helper(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      size_t function_or_deriv_index) const;\n\n  double root_finder(const std::array<double, Dim>& distance_to_center,\n                     const DataVector& function_of_time) const;\n\n  std::string f_of_t_name_{};\n  std::unordered_set<std::string> f_of_t_names_;\n  std::optional<double> inner_radius_;\n  std::optional<double> outer_radius_;\n  std::unique_ptr<MathFunction<1, Frame::Inertial>> f_of_r_{};\n  std::array<double, Dim> center_{};\n};\n\ntemplate <size_t Dim>\ninline bool operator!=(const Translation<Dim>& lhs,\n                       const Translation<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace domain::CoordinateMaps::TimeDependent\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/TimeDependentHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n#include \"Utilities/TypeTraits/IsCallable.hpp\"\n\nnamespace domain {\n/// Check if the calls to the coordinate map and its inverse map are\n/// time-dependent\ntemplate <typename T>\nusing is_map_time_dependent_t = tt::is_callable_t<\n    T, std::array<std::decay_t<T>, std::decay_t<T>::dim>, double,\n    std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>>;\n\n/// Check if the calls to the coordinate map and its inverse map are\n/// time-dependent\ntemplate <typename T>\nconstexpr bool is_map_time_dependent_v = is_map_time_dependent_t<T>::value;\n\n/// @{\n/// Check if the calls to the coordinate map and its inverse map are\n/// time-dependent\ntemplate <typename T>\nconcept TimeDependentMap = is_map_time_dependent_v<T>;\ntemplate <typename T>\nconcept TimeIndependentMap = not is_map_time_dependent_v<T>;\n/// @}\n\nnamespace detail {\nCREATE_IS_CALLABLE(jacobian)\n}  // namespace detail\n\n/// Check if the calls to the Jacobian and inverse Jacobian of the coordinate\n/// map are time-dependent\ntemplate <typename Map, typename T>\nusing is_jacobian_time_dependent_t = detail::is_jacobian_callable_t<\n    Map, std::array<std::decay_t<T>, std::decay_t<Map>::dim>, double,\n    std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>>;\n\n/// Check if the calls to the Jacobian and inverse Jacobian of the coordinate\n/// map are time-dependent\ntemplate <typename Map, typename T>\nconstexpr bool is_jacobian_time_dependent_v =\n    is_jacobian_time_dependent_t<Map, T>::value;\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/UniformCylindricalEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/UniformCylindricalEndcap.hpp\"\n\n#include <boost/math/special_functions/sign.hpp>\n#include <cmath>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <sstream>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CylindricalEndcapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nnamespace {\ndouble this_function_is_zero_for_correct_rhobar(\n    const double rhobar, const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, double radius_one,\n    const double radius_two, const double theta_max_one,\n    const double theta_max_two, const std::array<double, 3>& target_coords) {\n  const double r_one_cos_theta_one = radius_one * cos(rhobar * theta_max_one);\n  const double r_two_cos_theta_two = radius_two * cos(rhobar * theta_max_two);\n  const double lambda =\n      (target_coords[2] - center_one[2] - r_one_cos_theta_one) /\n      (center_two[2] - center_one[2] + r_two_cos_theta_two -\n       r_one_cos_theta_one);\n  return square(target_coords[0] - center_one[0] -\n                lambda * (center_two[0] - center_one[0])) +\n         square(target_coords[1] - center_one[1] -\n                lambda * (center_two[1] - center_one[1])) -\n         square((1.0 - lambda) * radius_one * sin(rhobar * theta_max_one) +\n                lambda * radius_two * sin(rhobar * theta_max_two));\n}\n\n// min and max values of rhobar in the inverse function.\nstd::tuple<double, double> rhobar_min_max(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, const double radius_one,\n    const double radius_two, const double theta_max_one,\n    const double theta_max_two, const std::array<double, 3>& target_coords) {\n  // Choose the minimum value of rhobar so that lambda >=0, where\n  // lambda is the quantity inside function_to_zero.  Note that the\n  // denominator of lambda inside that function is always positive.\n  const double rhobar_min =\n      target_coords[2] - center_one[2] >= radius_one\n          ? 0.0\n          : acos((target_coords[2] - center_one[2]) / radius_one) /\n                theta_max_one;\n  // Likewise, choose the maximum value of rhobar so that lambda <=1.\n  // The second ternary is to account for roundoff so that the acos doesn't\n  // FPE; (other than roundoff, target_coords[2] - center_two[2] should be\n  // <= radius_two)\n  const double rhobar_max =\n      target_coords[2] - center_two[2] <= radius_two * cos(theta_max_two)\n          ? 1.0\n          : target_coords[2] - center_two[2] >= radius_two\n                ? 0.0\n                : acos((target_coords[2] - center_two[2]) / radius_two) /\n                      theta_max_two;\n  return {rhobar_min, rhobar_max};\n}\n\nbool is_uniform_cylindrical_endcap_invertible_for_target_on_sphere_one(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, const double radius_one,\n    const double radius_two, const double theta_max_one,\n    const double theta_max_two, const std::array<double, 3>& target_coords) {\n  const auto [rhobar_min, rhobar_max] =\n      rhobar_min_max(center_one, center_two, radius_one, radius_two,\n                     theta_max_one, theta_max_two, target_coords);\n  if (equal_within_roundoff(rhobar_min, rhobar_max)) {\n    // Special case where rhobar_min == rhobar_max to roundoff.  This\n    // case occurs when the target point (assumed to be on sphere_one)\n    // is at rhobar=1.  In this case there is nothing to test because\n    // there is only one possible value of rhobar, so there cannot be\n    // two roots for two different values of rhobar.\n    return true;\n  }\n  const size_t num_pts = 1000;\n  const double drhobar = (rhobar_max - rhobar_min) / (num_pts - 1.0);\n  // Because the target point is on sphere_one, the only root should\n  // be at rhobar = rhobar_min, where lambda=0.  The actual value of\n  // the function at rhobar_min may be either sign because of\n  // roundoff.  So ignore that point, and start at the next point.\n  // Keep track of the sign, and look for sign changes as we progress\n  // from point to point.  zero is a sentinel value.\n  double sign = 0.0;\n  for (size_t i = 1; i < num_pts; ++i) {\n    const double rhobar = rhobar_min + i * drhobar;\n    const double func = this_function_is_zero_for_correct_rhobar(\n        rhobar, center_one, center_two, radius_one, radius_two, theta_max_one,\n        theta_max_two, target_coords);\n    if (sign == 0.0) {\n      sign = func > 0.0 ? 1.0 : -1.0;\n    } else if (func * sign <= 0.0) {\n      // There is another root, so function is not invertible.\n      return false;\n    }\n  }\n  return true;  // No roots found.\n}\n}  // namespace\n\nbool is_uniform_cylindrical_endcap_invertible_on_sphere_one(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, double radius_one,\n    double radius_two, double theta_max_one, double theta_max_two) {\n  if (center_one[1] == center_two[1] and center_one[0] == center_two[0]) {\n    return true;\n  }\n  // Choose the worst direction, which is the direction of x-y-plane\n  // projection of the difference between center_one and center_two.\n  const double phi =\n      atan2(center_one[1] - center_two[1], center_one[0] - center_two[0]);\n  const size_t num_pts = 100;\n  for (size_t i = 0; i < num_pts; ++i) {\n    const double theta_one = theta_max_one * (i / (num_pts - 1.0));\n    if (not is_uniform_cylindrical_endcap_invertible_for_target_on_sphere_one(\n            center_one, center_two, radius_one, radius_two, theta_max_one,\n            theta_max_two,\n            {{center_one[0] + radius_one * sin(theta_one) * cos(phi),\n              center_one[1] + radius_one * sin(theta_one) * sin(phi),\n              center_one[2] + radius_one * cos(theta_one)}})) {\n      return false;\n    }\n  }\n  return true;\n}\n\nUniformCylindricalEndcap::UniformCylindricalEndcap(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, double radius_one,\n    double radius_two, double z_plane_one, double z_plane_two)\n    : center_one_(center_one),\n      center_two_(center_two),\n      radius_one_([&radius_one]() {\n        // The equal_within_roundoff below has an implicit scale of 1,\n        // so the ASSERT may trigger in the case where we really\n        // want an entire domain that is very small.\n        ASSERT(not equal_within_roundoff(radius_one, 0.0),\n               \"Cannot have zero radius_one; you specified radius_one=\"\n                   << radius_one);\n        ASSERT(radius_one > 0.0,\n               \"Cannot have negative radius_one; you specified radius_one=\"\n                   << radius_one);\n        return radius_one;\n      }()),\n      radius_two_([&radius_two]() {\n        // The equal_within_roundoff below has an implicit scale of 1,\n        // so the ASSERT may trigger in the case where we really\n        // want an entire domain that is very small.\n        ASSERT(not equal_within_roundoff(radius_two, 0.0),\n               \"Cannot have zero radius_two; you specified radius_two=\"\n                   << radius_two);\n        ASSERT(radius_two > 0.0,\n               \"Cannot have negative radius_two; you specified radius_two=\"\n                   << radius_two);\n        return radius_two;\n      }()),\n      z_plane_one_(z_plane_one),\n      z_plane_two_(z_plane_two),\n      theta_max_one_([&center_one, &radius_one, &z_plane_one]() {\n        const double cos_theta_max = (z_plane_one - center_one[2]) / radius_one;\n        ASSERT(abs(cos_theta_max) < 1.0,\n               \"Plane one must intersect sphere_one, and at more than one \"\n               \"point. You probably specified a bad value of z_plane_one, \"\n               \"radius_one, or the z component of center_one.\"\n               \"\\nz_plane_one=\"\n                   << z_plane_one << \"\\nradius_one=\" << radius_one\n                   << \"\\ncenter_one=\" << center_one);\n        return acos(cos_theta_max);\n      }()),\n      theta_max_two_([&center_two, &radius_two, &z_plane_two]() {\n        const double cos_theta_max = (z_plane_two - center_two[2]) / radius_two;\n        ASSERT(abs(cos_theta_max) < 1.0,\n               \"Plane two must intersect sphere_two, and at more than one \"\n               \"point. You probably specified a bad value of z_plane_two, \"\n               \"radius_two, or the z component of center_two.\"\n               \"\\nz_plane_two=\"\n                   << z_plane_two << \"\\nradius_two=\" << radius_two\n                   << \"\\ncenter_two=\" << center_two);\n        return acos(cos_theta_max);\n      }()) {\n  // The code below defines several variables that are used only in ASSERTs.\n  // We put that code in a #ifdef SPECTRE_DEBUG to avoid clang-tidy complaining\n  // about unused variables in release mode.\n#ifdef SPECTRE_DEBUG\n\n  // For some reason, codecov thinks that the following lambda never\n  // gets called, even though it is in all of the ASSERT messages below.\n  // LCOV_EXCL_START\n  const auto param_string = [this]() -> std::string {\n    std::ostringstream buffer;\n    buffer << \"\\nParameters to UniformCylindricalEndcap:\\nradius_one=\"\n           << radius_one_ << \"\\nradius_two=\" << radius_two_\n           << \"\\ncenter_one=\" << center_one_ << \"\\ncenter_two=\" << center_two_\n           << \"\\nz_plane_one=\" << z_plane_one_\n           << \"\\nz_plane_two=\" << z_plane_two_;\n    return buffer.str();\n  };\n  // LCOV_EXCL_STOP\n\n  // Assumptions made in the map.  Some of these can be relaxed,\n  // as long as the unit test is changed to test them.\n  ASSERT(z_plane_two >= z_plane_one + 0.04 * radius_two,\n         \"z_plane_two must be >= z_plane_one + 0.04 * radius_two, not \"\n             << z_plane_two << \" \" << z_plane_one << \" \"\n             << z_plane_one + 0.04 * radius_two << param_string());\n  ASSERT(theta_max_one_ < M_PI * 0.45,\n         \"z_plane_one is too close to the center of sphere_one: theta/pi = \"\n             << theta_max_one_ / M_PI << param_string());\n  ASSERT(theta_max_one_ > M_PI * 0.075,\n         \"z_plane_one is too far from the center of sphere_one: theta/pi = \"\n             << theta_max_one_ / M_PI << param_string());\n  ASSERT(theta_max_two_ < M_PI * 0.45,\n         \"z_plane_two is too close to the center of sphere_two: theta/pi = \"\n             << theta_max_two_ / M_PI << param_string());\n  ASSERT(theta_max_two_ > M_PI * 0.075,\n         \"z_plane_two is too far from the center of sphere_two: theta/pi = \"\n             << theta_max_two_ / M_PI << param_string());\n\n  ASSERT(is_uniform_cylindrical_endcap_invertible_on_sphere_one(\n             center_one_, center_two_, radius_one_, radius_two_, theta_max_one_,\n             theta_max_two_),\n         \"The map is not invertible at at least one point on sphere_one.\"\n         \" theta_max_one = \"\n             << theta_max_one_ << \" theta_max_two = \" << theta_max_two_\n             << param_string());\n\n  const double dist_spheres = sqrt(square(center_one[0] - center_two[0]) +\n                                   square(center_one[1] - center_two[1]) +\n                                   square(center_one[2] - center_two[2]));\n\n  ASSERT(dist_spheres + radius_one <= 0.98 * radius_two,\n         \"The map has been tested only for the case when \"\n         \"sphere_one is sufficiently contained inside sphere_two, without the \"\n         \"two spheres almost touching. Radius_one = \"\n             << radius_one << \", radius_two = \" << radius_two\n             << \", dist_spheres = \" << dist_spheres\n             << \", (dist_spheres+radius_one)/radius_two=\"\n             << (dist_spheres + radius_one) / radius_two << param_string());\n\n  const double horizontal_dist_spheres =\n      sqrt(square(center_one[0] - center_two[0]) +\n           square(center_one[1] - center_two[1]));\n\n  // max_horizontal_dist_between_circles can be either sign;\n  // therefore alpha can be in either the first or second quadrant.\n  const double max_horizontal_dist_between_circles =\n      horizontal_dist_spheres + radius_one_ * sin(theta_max_one_) -\n      radius_two_ * sin(theta_max_two_);\n\n  const double alpha =\n      atan2(z_plane_two - z_plane_one, max_horizontal_dist_between_circles);\n  ASSERT(alpha > 1.1 * theta_max_one_ and alpha > 1.1 * theta_max_two_,\n         \"Angle alpha is too small: alpha = \"\n             << alpha << \", theta_max_one = \" << theta_max_one_\n             << \", theta_max_two = \" << theta_max_two_\n             << \", max_horizontal_dist_between_circles = \"\n             << max_horizontal_dist_between_circles\n             << \", horizontal_dist_spheres = \" << horizontal_dist_spheres\n             << param_string());\n#endif\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> UniformCylindricalEndcap::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n  std::array<ReturnType, 3> target_coords{};\n  ReturnType& x = target_coords[0];\n  ReturnType& y = target_coords[1];\n  ReturnType& z = target_coords[2];\n\n  // Use y and z as temporary storage to avoid allocations,\n  // before setting them to their actual values.\n  z = sqrt(square(xbar) + square(ybar));  // rhobar in the dox\n  y = radius_one_ *\n          cylindrical_endcap_helpers::sin_ax_over_x(z, theta_max_one_) *\n          (1.0 - zbar) +\n      radius_two_ *\n          cylindrical_endcap_helpers::sin_ax_over_x(z, theta_max_two_) *\n          (1.0 + zbar);\n\n  x = 0.5 * (y * xbar + center_one_[0] * (1.0 - zbar) +\n             center_two_[0] * (1.0 + zbar));\n  y = 0.5 * (y * ybar + center_one_[1] * (1.0 - zbar) +\n             center_two_[1] * (1.0 + zbar));\n  z = 0.5 *\n      ((radius_one_ * cos(theta_max_one_ * z) + center_one_[2]) * (1.0 - zbar) +\n       (radius_two_ * cos(theta_max_two_ * z) + center_two_[2]) * (1.0 + zbar));\n  return target_coords;\n}\n\nstd::optional<std::array<double, 3>> UniformCylindricalEndcap::inverse(\n    const std::array<double, 3>& target_coords) const {\n  // First do some easy checks that target_coords is in the range\n  // of the map.  Need to accept points that are out of range by roundoff.\n\n  // check z >= z_plane_one_\n  if (target_coords[2] < z_plane_one_ and\n      not equal_within_roundoff(target_coords[2], z_plane_one_)) {\n    return {};\n  }\n\n  // check that point is in or on sphere_two\n  const double r_minus_center_two_squared =\n      square(target_coords[0] - center_two_[0]) +\n      square(target_coords[1] - center_two_[1]) +\n      square(target_coords[2] - center_two_[2]);\n  if (r_minus_center_two_squared > square(radius_two_) and\n      not equal_within_roundoff(sqrt(r_minus_center_two_squared),\n                                radius_two_)) {\n    // sqrt above because we don't want to change the scale of\n    // equal_within_roundoff too much.\n    return {};\n  }\n\n  // check that point is outside or on sphere_one_\n  const double r_minus_center_one_squared =\n      square(target_coords[0] - center_one_[0]) +\n      square(target_coords[1] - center_one_[1]) +\n      square(target_coords[2] - center_one_[2]);\n  if (r_minus_center_one_squared < square(radius_one_) and\n      not equal_within_roundoff(sqrt(r_minus_center_one_squared),\n                                radius_one_)) {\n    // sqrt above because we don't want to change the scale of\n    // equal_within_roundoff too much.\n    return {};\n  }\n\n  // Check if the point is inside the cone\n  const double lambda_tilde =\n      (target_coords[2] - z_plane_one_) / (z_plane_two_ - z_plane_one_);\n  const double rho_tilde =\n      sqrt(square(target_coords[0] - center_one_[0] -\n                  lambda_tilde * (center_two_[0] - center_one_[0])) +\n           square(target_coords[1] - center_one_[1] -\n                  lambda_tilde * (center_two_[1] - center_one_[1])));\n  const double circle_radius =\n      radius_one_ * sin(theta_max_one_) * (1.0 - lambda_tilde) +\n      radius_two_ * sin(theta_max_two_) * lambda_tilde;\n  if (rho_tilde > circle_radius and\n      not equal_within_roundoff(rho_tilde, circle_radius,\n                                std::numeric_limits<double>::epsilon() * 100.0,\n                                radius_two_)) {\n    return {};\n  }\n\n  // To find rhobar we will do numerical root finding.\n  // function_to_zero is the function that is zero when rhobar has the\n  // correct value.\n  const auto function_to_zero = [this, &target_coords](const double rhobar) {\n    const double r_one_cos_theta_one =\n        radius_one_ * cos(rhobar * theta_max_one_);\n    const double r_two_cos_theta_two =\n        radius_two_ * cos(rhobar * theta_max_two_);\n    const double lambda =\n        (target_coords[2] - center_one_[2] - r_one_cos_theta_one) /\n        (center_two_[2] - center_one_[2] + r_two_cos_theta_two -\n         r_one_cos_theta_one);\n    return square(target_coords[0] - center_one_[0] -\n                  lambda * (center_two_[0] - center_one_[0])) +\n           square(target_coords[1] - center_one_[1] -\n                  lambda * (center_two_[1] - center_one_[1])) +\n           -square((1.0 - lambda) * radius_one_ * sin(rhobar * theta_max_one_) +\n                   lambda * radius_two_ * sin(rhobar * theta_max_two_));\n  };\n\n  // To find rhobar we will do numerical root finding.\n  // function_and_deriv_to_zero returns a std::pair consisting of the\n  // function that is zero when rhobar has the correct value, and the\n  // derivative of that function with respect to rhobar.\n  const auto function_and_deriv_to_zero =\n      [this, &target_coords](const double rhobar) {\n        const double r_one_cos_theta_one =\n            radius_one_ * cos(rhobar * theta_max_one_);\n        const double r_two_cos_theta_two =\n            radius_two_ * cos(rhobar * theta_max_two_);\n        const double lambda =\n            (target_coords[2] - center_one_[2] - r_one_cos_theta_one) /\n            (center_two_[2] - center_one_[2] + r_two_cos_theta_two -\n             r_one_cos_theta_one);\n        const double func_to_zero =\n            square(target_coords[0] - center_one_[0] -\n                   lambda * (center_two_[0] - center_one_[0])) +\n            square(target_coords[1] - center_one_[1] -\n                   lambda * (center_two_[1] - center_one_[1])) -\n            square((1.0 - lambda) * radius_one_ * sin(rhobar * theta_max_one_) +\n                   lambda * radius_two_ * sin(rhobar * theta_max_two_));\n\n        // deriv of r_one_cos_theta_one and r_two_cos_theta_two with respect to\n        // rho.\n        const double d_r_one_cos_theta_one =\n            radius_one_ * sin(rhobar * theta_max_one_) * theta_max_one_;\n        const double d_r_two_cos_theta_two =\n            radius_two_ * sin(rhobar * theta_max_two_) * theta_max_two_;\n        const double dlambda_drho =\n            (d_r_one_cos_theta_one +\n             lambda * (d_r_two_cos_theta_two - d_r_one_cos_theta_one)) /\n            (center_two_[2] - center_one_[2] + r_two_cos_theta_two -\n             r_one_cos_theta_one);\n        const double deriv_of_func_to_zero =\n            -2.0 * dlambda_drho *\n                ((center_two_[0] - center_one_[0]) *\n                     (target_coords[0] - center_one_[0] -\n                      lambda * (center_two_[0] - center_one_[0])) +\n                 (center_two_[1] - center_one_[1]) *\n                     (target_coords[1] - center_one_[1] -\n                      lambda * (center_two_[1] - center_one_[1]))) -\n            2.0 *\n                ((1.0 - lambda) * radius_one_ * sin(rhobar * theta_max_one_) +\n                 lambda * radius_two_ * sin(rhobar * theta_max_two_)) *\n                ((radius_two_ * sin(rhobar * theta_max_two_) -\n                  radius_one_ * sin(rhobar * theta_max_one_)) *\n                     dlambda_drho +\n                 (1.0 - lambda) * r_one_cos_theta_one * theta_max_one_ +\n                 lambda * r_two_cos_theta_two * theta_max_two_);\n        return std::make_pair(func_to_zero, deriv_of_func_to_zero);\n      };\n\n  double rhobar_min{};\n  double rhobar_max{};\n  std::tie(rhobar_min, rhobar_max) =\n      rhobar_min_max(center_one_, center_two_, radius_one_, radius_two_,\n                     theta_max_one_, theta_max_two_, target_coords);\n\n  // If rhobar is zero, then the root finding doesn't converge\n  // well. This is because the function behaves like rhobar^2 for\n  // small values of rhobar, so both the function and its derivative\n  // are zero at rhobar==0.\n  //\n  // Note that rhobar==0 is not that uncommon, and will occur if there\n  // are grid points on the symmetry axis.\n  //\n  // Similarly, rhobar==1 occurs if there are grid points on the block\n  // boundary.\n  //\n  // So check for the special cases of rhobar==0 and rhobar==1\n  // before doing any root finding.\n  const auto this_is_small_if_rhobar_is_near_unity = [this, &target_coords]() {\n    const double denom = 1.0 / (center_two_[2] - center_one_[2] +\n                                radius_two_ * cos(theta_max_two_) -\n                                radius_one_ * cos(theta_max_one_));\n    const double lambda_if_rhobar_is_unity =\n        (target_coords[2] - center_one_[2] -\n         radius_one_ * cos(theta_max_one_)) *\n        denom;\n    // The function function_to_zero goes like\n    // first_term + second_term (1-rhobar) when 1-rhobar is small,\n    // where first_term goes to zero (faster than (1-rhobar)) when\n    // 1-rhobar is small.\n    // We don't have a good sense of scale for first_term, so we\n    // check the size of first_term/|second_term|.\n    const double temp =\n        radius_one_ * sin(theta_max_one_) +\n        lambda_if_rhobar_is_unity * (radius_two_ * sin(theta_max_two_) -\n                                     radius_one_ * sin(theta_max_one_));\n    const double first_term =\n        square(target_coords[0] - center_one_[0] -\n               lambda_if_rhobar_is_unity * (center_two_[0] - center_one_[0])) +\n        square(target_coords[1] - center_one_[1] -\n               lambda_if_rhobar_is_unity * (center_two_[1] - center_one_[1])) -\n        square(temp);\n    // delta_lambda is (lambda-lambda_if_rhobar_is_unity)/(1-rhobar)\n    // to first order in 1-rhobar.\n    const double delta_lambda =\n        denom * (lambda_if_rhobar_is_unity *\n                     (radius_two_ * theta_max_two_ * sin(theta_max_two_) -\n                      radius_one_ * theta_max_one_ * sin(theta_max_one_)) -\n                 radius_one_ * theta_max_one_ * sin(theta_max_one_));\n    const double second_term =\n        2.0 * delta_lambda *\n            (lambda_if_rhobar_is_unity *\n                 (square(center_two_[0] - center_one_[0]) +\n                  square(center_two_[1] - center_one_[1])) -\n             (target_coords[0] - center_one_[0]) *\n                 (center_two_[1] - center_one_[1]) -\n             (target_coords[1] - center_one_[1]) *\n                 (center_two_[0] - center_one_[0])) -\n        2.0 * temp *\n            (-(1.0 - lambda_if_rhobar_is_unity) * radius_one_ * theta_max_one_ *\n                 cos(theta_max_one_) -\n             lambda_if_rhobar_is_unity * radius_two_ * theta_max_two_ *\n                 cos(theta_max_two_) +\n             delta_lambda * (radius_two_ * sin(theta_max_two_) -\n                             radius_one_ * sin(theta_max_one_)));\n    return std::abs(first_term) / std::abs(second_term);\n  };\n\n  const auto this_is_small_if_rhobar_is_near_zero = [this, &target_coords]() {\n    const double denom =\n        1.0 / (center_two_[2] - center_one_[2] + radius_two_ - radius_one_);\n    const double lambda_if_rhobar_is_zero =\n        (target_coords[2] - center_one_[2] - radius_one_) * denom;\n    // The function we are trying to make zero goes like\n    // first_term + second_term rhobar^2, and first_term goes to zero\n    // (faster than rhobar^2) when rhobar is small.  We don't have a good\n    // sense of scale for first_term, so we check the size of\n    // first_term/second_term.\n    const double first_term =\n        square(target_coords[0] - center_one_[0] -\n               lambda_if_rhobar_is_zero * (center_two_[0] - center_one_[0])) +\n        square(target_coords[1] - center_one_[1] -\n               lambda_if_rhobar_is_zero * (center_two_[1] - center_one_[1]));\n    // delta_lambda is (lambda-lambda_if_rhobar_is_zero)/rhobar^2\n    // to first order in rhobar^2.\n    const double delta_lambda =\n        denom *\n        (square(theta_max_one_) * radius_one_ +\n         lambda_if_rhobar_is_zero * (square(theta_max_two_) * radius_two_ -\n                                     square(theta_max_one_) * radius_one_));\n    const double second_term =\n        2.0 * delta_lambda *\n            (lambda_if_rhobar_is_zero *\n                 (square(center_two_[0] - center_one_[0]) +\n                  square(center_two_[1] - center_one_[1])) -\n             (target_coords[0] - center_one_[0]) *\n                 (center_two_[1] - center_one_[1]) -\n             (target_coords[1] - center_one_[1]) *\n                 (center_two_[0] - center_one_[0])) -\n        square((1.0 - lambda_if_rhobar_is_zero) * radius_one_ * theta_max_one_ +\n               lambda_if_rhobar_is_zero * radius_two_ * theta_max_two_);\n    return std::abs(first_term) / std::abs(second_term);\n  };\n\n  if (rhobar_min < 1.e-6 and this_is_small_if_rhobar_is_near_zero() < 1.e-14) {\n    // Treat rhobar as zero, if it is zero to roundoff.\n    // We empirically choose roundoff here to be 1.e-14.  We don't\n    // really need the rhobar_min < 1.e-6 in the 'if' above, but we\n    // add it so that we can save the computational expense of calling\n    // this_is_small_if_rhobar_is_near_zero() when we already know\n    // that rhobar isn't close to zero. We choose the relatively large\n    // value of 1.e-6 to err on the side of caution.\n    const double lambda =\n        (target_coords[2] - center_one_[2] - radius_one_) /\n        (center_two_[2] - center_one_[2] + radius_two_ - radius_one_);\n    return {{0.0, 0.0, 2.0 * lambda - 1.0}};\n  } else if (1.0 - rhobar_max < 1.e-6 and\n             this_is_small_if_rhobar_is_near_unity() < 1.e-14) {\n    // Treat rhobar as unity, if it is unity to roundoff.\n    // We empirically choose roundoff here to be 1.e-14.  We don't\n    // really need the 1-rhobar_max < 1.e-6 in the 'if' above, but we\n    // add it so that we can save the computational expense of calling\n    // this_is_small_if_rhobar_is_near_unity() when we already know\n    // that rhobar isn't close to unity. We choose the relatively\n    // large value of 1.e-6 to err on the side of caution.\n    const double lambda =\n        (target_coords[2] - center_one_[2] -\n         radius_one_ * cos(theta_max_one_)) /\n        (center_two_[2] - center_one_[2] + radius_two_ * cos(theta_max_two_) -\n         radius_one_ * cos(theta_max_one_));\n    const double denom =\n        1.0 / ((1.0 - lambda) * radius_one_ * sin(theta_max_one_) +\n               lambda * radius_two_ * sin(theta_max_two_));\n    return {{(target_coords[0] - center_one_[0] -\n              lambda * (center_two_[0] - center_one_[0])) *\n                 denom,\n             (target_coords[1] - center_one_[1] -\n              lambda * (center_two_[1] - center_one_[1])) *\n                 denom,\n             2.0 * lambda - 1.0}};\n  }\n\n  // If we get here, then rhobar is not near unity or near zero.\n  // So let's find rhobar.\n  double rhobar = std::numeric_limits<double>::signaling_NaN();\n\n  // The root should always be bracketed.  However, if\n  // rhobar==rhobar_min or rhobar==rhobar_max, the function may not be\n  // exactly zero because of roundoff and then the root might not be\n  // bracketed.\n  double function_at_rhobar_min = function_to_zero(rhobar_min);\n  double function_at_rhobar_max = function_to_zero(rhobar_max);\n  if (function_at_rhobar_min * function_at_rhobar_max > 0.0) {\n    // Root not bracketed.\n    // If the bracketing failure is due to roundoff, then\n    // slightly adjust the bounds to increase the range. Otherwise, error.\n    //\n    // roundoff_ratio is the limiting ratio of the two function values\n    // for which we should attempt to adjust the bounds.  It is ok for\n    // roundoff_ratio to be much larger than actual roundoff, since it\n    // doesn't hurt to expand the interval and try again when\n    // otherwise we would just error.\n    constexpr double roundoff_ratio = 1.e-3;\n    if (abs(function_at_rhobar_min) / abs(function_at_rhobar_max) <\n        roundoff_ratio) {\n      // Slightly decrease rhobar_min.  How far do we decrease it?\n      // Figure that out by looking at the deriv of the function.\n      const double deriv_function_at_rhobar_min =\n          function_and_deriv_to_zero(rhobar_min).second;\n      const double trial_rhobar_min_increment =\n          -2.0 * function_at_rhobar_min / deriv_function_at_rhobar_min;\n      // new_rhobar_min = rhobar_min + trial_rhobar_min_increment would be\n      // a Newton-Raphson step except for the factor of 2 in\n      // trial_rhobar_min_increment. The factor of 2 is there to\n      // over-compensate so that the new rhobar_min brackets the\n      // root.\n      //\n      // But sometimes the factor of 2 is not enough when\n      // trial_rhobar_min_increment is roundoff-small so that the\n      // change in new_rhobar_min is zero or only in the last one or\n      // two bits.  If this is the case, then we set\n      // rhobar_min_increment to some small roundoff value with the\n      // same sign as trial_rhobar_min_increment.\n      // Note that rhobar is always between zero and one, so it is\n      // ok to use an absolute epsilon.\n      const double rhobar_min_increment =\n          abs(trial_rhobar_min_increment) >\n                  100.0 * std::numeric_limits<double>::epsilon()\n              ? trial_rhobar_min_increment\n              : std::copysign(100.0 * std::numeric_limits<double>::epsilon(),\n                              trial_rhobar_min_increment);\n      const double new_rhobar_min = rhobar_min + rhobar_min_increment;\n      const double function_at_new_rhobar_min =\n          function_to_zero(new_rhobar_min);\n      if (function_at_new_rhobar_min * function_at_rhobar_min > 0.0) {\n        ERROR(\n            \"Cannot find bracket after trying to adjust bracketing \"\n            \"rhobar_min due \"\n            \"to roundoff : \"\n            << rhobar_min << \" \" << function_at_rhobar_min << \" \" << rhobar_max\n            << \" \" << function_at_rhobar_max << \" \" << new_rhobar_min << \" \"\n            << function_at_new_rhobar_min << \" \" << deriv_function_at_rhobar_min\n            << \" \" << new_rhobar_min - rhobar_min << \"\\n\");\n      }\n      // Now the root is bracketed between rhobar_min and new_rhobar_min,\n      // so replace rhobar_max and rhobar_min and then fall through to the\n      // root finder.\n      rhobar_max = rhobar_min;\n      function_at_rhobar_max = function_at_rhobar_min;\n      rhobar_min = new_rhobar_min;\n      function_at_rhobar_min = function_at_new_rhobar_min;\n    } else if (abs(function_at_rhobar_max) / abs(function_at_rhobar_min) <\n               roundoff_ratio) {\n      // Slightly increase rhobar_max.  How far do we increase it?\n      // Figure that out by looking at the deriv of the function.\n      const double deriv_function_at_rhobar_max =\n          function_and_deriv_to_zero(rhobar_max).second;\n      const double trial_rhobar_max_increment =\n          -2.0 * function_at_rhobar_max / deriv_function_at_rhobar_max;\n      // new_rhobar_max = rhobar_max + trial_rhobar_max_increment would be\n      // a Newton-Raphson step except for the factor of 2 in\n      // trial_rhobar_max_increment. The factor of 2 is there to\n      // over-compensate so that the new rhobar_max brackets the\n      // root.\n      //\n      // But sometimes the factor of 2 is not enough when\n      // trial_rhobar_max_increment is roundoff-small so that the\n      // change in new_rhobar_max is zero or only in the last one or\n      // two bits.  If this is the case, then we set\n      // rhobar_max_increment to some small roundoff value with the\n      // same sign as trial_rhobar_max_increment.\n      // Note that rhobar is always between zero and one, so it is\n      // ok to use an absolute epsilon.\n      const double rhobar_max_increment =\n          abs(trial_rhobar_max_increment) >\n                  100.0 * std::numeric_limits<double>::epsilon()\n              ? trial_rhobar_max_increment\n              : boost::math::copysign(\n                    100.0 * std::numeric_limits<double>::epsilon(),\n                    trial_rhobar_max_increment);\n      const double new_rhobar_max = rhobar_max + rhobar_max_increment;\n      const double function_at_new_rhobar_max =\n          function_to_zero(new_rhobar_max);\n      if (function_at_new_rhobar_max * function_at_rhobar_max > 0.0) {\n        ERROR(\n            \"Cannot find bracket after trying to adjust bracketing \"\n            \"rhobar_max due \"\n            \"to roundoff : \"\n            << rhobar_max << \" \" << function_at_rhobar_max << \" \" << rhobar_max\n            << \" \" << function_at_rhobar_max << \" \" << new_rhobar_max << \" \"\n            << function_at_new_rhobar_max << \" \" << deriv_function_at_rhobar_max\n            << \" \" << new_rhobar_max - rhobar_max << \"\\n\");\n      }\n      // Now the root is bracketed between rhobar_max and new_rhobar_max,\n      // so replace rhobar_max and rhobar_min and then fall through to the\n      // root finder.\n      rhobar_min = rhobar_max;\n      function_at_rhobar_min = function_at_rhobar_max;\n      rhobar_max = new_rhobar_max;\n      function_at_rhobar_max = function_at_new_rhobar_max;\n    } else {\n      ERROR(\"Root is not bracketed: \"\n            << rhobar_min << \" \" << function_at_rhobar_min << \" \" << rhobar_max\n            << \" \" << function_at_rhobar_max);\n    }\n  }\n  // If we get here, root is bracketed. Use toms748, and use the\n  // function evaluations that we have already done.\n\n  // Rhobar is between zero and 1, so the scale is unity, so therefore\n  // abs and rel tolerance are equal.\n  constexpr double abs_tol = 1.e-15;\n  constexpr double rel_tol = 1.e-15;\n\n  try {\n    rhobar =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(function_to_zero, rhobar_min, rhobar_max,\n                            function_at_rhobar_min, function_at_rhobar_max,\n                            abs_tol, rel_tol);\n  } catch (std::exception&) {\n    ERROR(\"Cannot find root after bracketing: \"\n          << rhobar_min << \" \" << function_at_rhobar_min << \" \" << rhobar_max\n          << \" \" << function_at_rhobar_max);\n  }\n\n  // Now that we have rhobar, construct inverse.\n  const double r_one_cos_theta_one = radius_one_ * cos(rhobar * theta_max_one_);\n  const double r_two_cos_theta_two = radius_two_ * cos(rhobar * theta_max_two_);\n  const double lambda =\n      (target_coords[2] - center_one_[2] - r_one_cos_theta_one) /\n      (center_two_[2] - center_one_[2] + r_two_cos_theta_two -\n       r_one_cos_theta_one);\n  const double denom =\n      1.0 / ((1.0 - lambda) * radius_one_ * sin(rhobar * theta_max_one_) +\n             lambda * radius_two_ * sin(rhobar * theta_max_two_));\n  return {{rhobar *\n               (target_coords[0] - center_one_[0] -\n                lambda * (center_two_[0] - center_one_[0])) *\n               denom,\n           rhobar*(target_coords[1] - center_one_[1] -\n                   lambda * (center_two_[1] - center_one_[1])) *\n               denom,\n           2.0 * lambda - 1.0}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nUniformCylindricalEndcap::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n\n  auto jac =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  // Use jacobian components as temporary storage to avoid extra\n  // memory allocations.\n  get<2, 2>(jac) = sqrt(square(xbar) + square(ybar));\n  get<2, 0>(jac) = 0.5 * radius_one_ *\n                   cylindrical_endcap_helpers::sin_ax_over_x(get<2, 2>(jac),\n                                                             theta_max_one_) *\n                   (1.0 - zbar);\n  get<2, 1>(jac) = 0.5 * radius_two_ *\n                   cylindrical_endcap_helpers::sin_ax_over_x(get<2, 2>(jac),\n                                                             theta_max_two_) *\n                   (1.0 + zbar);\n  get<1, 1>(jac) = get<2, 0>(jac) + get<2, 1>(jac);\n  get<2, 1>(jac) =\n      theta_max_one_ * get<2, 0>(jac) + theta_max_two_ * get<2, 1>(jac);\n  get<1, 2>(jac) = 0.5 * radius_two_ *\n                       cylindrical_endcap_helpers::sin_ax_over_x(\n                           get<2, 2>(jac), theta_max_two_) -\n                   0.5 * radius_one_ *\n                       cylindrical_endcap_helpers::sin_ax_over_x(\n                           get<2, 2>(jac), theta_max_one_);\n  get<1, 0>(jac) = 0.5 * radius_one_ *\n                       cylindrical_endcap_helpers::one_over_x_d_sin_ax_over_x(\n                           get<2, 2>(jac), theta_max_one_) *\n                       (1.0 - zbar) +\n                   0.5 * radius_two_ *\n                       cylindrical_endcap_helpers::one_over_x_d_sin_ax_over_x(\n                           get<2, 2>(jac), theta_max_two_) *\n                       (1.0 + zbar);\n\n  // Now fill Jacobian values\n  get<0, 0>(jac) = square(xbar) * get<1, 0>(jac) + get<1, 1>(jac);\n  get<1, 1>(jac) = square(ybar) * get<1, 0>(jac) + get<1, 1>(jac);\n  get<0, 1>(jac) = xbar * ybar * get<1, 0>(jac);\n  get<1, 0>(jac) = get<0, 1>(jac);\n  get<2, 0>(jac) = -xbar * get<2, 1>(jac);\n  get<2, 1>(jac) = -ybar * get<2, 1>(jac);\n  get<0, 2>(jac) =\n      xbar * get<1, 2>(jac) + 0.5 * (center_two_[0] - center_one_[0]);\n  get<1, 2>(jac) =\n      ybar * get<1, 2>(jac) + 0.5 * (center_two_[1] - center_one_[1]);\n  get<2, 2>(jac) = 0.5 * (center_two_[2] - center_one_[2] +\n                          radius_two_ * cos(theta_max_two_ * get<2, 2>(jac)) -\n                          radius_one_ * cos(theta_max_one_ * get<2, 2>(jac)));\n\n  return jac;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nUniformCylindricalEndcap::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  return determinant_and_inverse(jacobian(source_coords)).second;\n}\n\nvoid UniformCylindricalEndcap::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | center_one_;\n    p | center_two_;\n    p | radius_one_;\n    p | radius_two_;\n    p | z_plane_one_;\n    p | z_plane_two_;\n    p | theta_max_one_;\n    p | theta_max_two_;\n  }\n}\n\nbool operator==(const UniformCylindricalEndcap& lhs,\n                const UniformCylindricalEndcap& rhs) {\n  // don't need to compare theta_max_one_ or theta_max_two_\n  // because they are uniquely determined from the other variables.\n  return lhs.center_one_ == rhs.center_one_ and\n         lhs.radius_one_ == rhs.radius_one_ and\n         lhs.z_plane_one_ == rhs.z_plane_one_ and\n         lhs.center_two_ == rhs.center_two_ and\n         lhs.radius_two_ == rhs.radius_two_ and\n         lhs.z_plane_two_ == rhs.z_plane_two_;\n}\n\nbool operator!=(const UniformCylindricalEndcap& lhs,\n                const UniformCylindricalEndcap& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  UniformCylindricalEndcap::operator()(                                      \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  UniformCylindricalEndcap::jacobian(                                        \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  UniformCylindricalEndcap::inv_jacobian(                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/UniformCylindricalEndcap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class UniformCylindricalEndcap.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from 3D unit right cylinder to a volume that connects\n *  portions of two spherical surfaces.\n *\n * \\image html UniformCylEndcap.svg \"A cylinder maps to the shaded region.\"\n *\n * \\details Consider two spheres with centers \\f$C_1\\f$ and \\f$C_2\\f$,\n * and radii \\f$R_1\\f$ and \\f$R_2\\f$. Let sphere 1 be intersected by a\n * plane normal to the \\f$z\\f$ axis and located at \\f$z = z_{\\mathrm{P}1}\\f$,\n * and let sphere 2 be intersected by a plane normal to the \\f$z\\f$ axis and\n * located at \\f$z = z_{\\mathrm{P}2}\\f$.\n *\n * UniformCylindricalEndcap maps a 3D unit right cylinder (with coordinates\n * \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that \\f$-1\\leq\\bar{z}\\leq 1\\f$\n * and \\f$\\bar{x}^2+\\bar{y}^2 \\leq 1\\f$) to the shaded area\n * in the figure above (with coordinates \\f$(x,y,z)\\f$).  The \"bottom\"\n * of the cylinder \\f$\\bar{z}=-1\\f$ is mapped to the portion of sphere\n * 1 that has \\f$z \\geq z_{\\mathrm{P}1}\\f$, and on this portion of the\n * sphere the angular coordinate \\f$\\theta_1 = \\acos((z-C_1^2)/R_1)\\f$\n * is uniform in \\f$\\bar{\\rho} = \\sqrt{\\bar{x}^2+\\bar{y}^2}\\f$ and the angular\n * coordinate \\f$\\phi_1 = \\atan((y-C_1^1)/(x-C_1^0))\\f$ is the same as\n * \\f$\\phi = \\atan(\\bar{y}/\\bar{x})\\f$.\n * Likewise, the \"top\" of the cylinder\n * \\f$\\bar{z}=+1\\f$ is mapped to the portion of sphere 2 that has\n * \\f$z \\geq z_{\\mathrm{P}2}\\f$, and on this portion of the\n * sphere the angular coordinate \\f$\\theta_2 = \\acos((z-C_2^2)/R_2)\\f$\n * is uniform in \\f$\\bar\\rho\\f$ and the angular\n * coordinate \\f$\\phi_2 = \\atan((y-C_2^1)/(x-C_2^0))\\f$ is the same as\n * \\f$\\phi\\f$.\n *\n * UniformCylindricalEndcap is different from CylindricalEndcap\n * because of the distribution of points on the spheres, and because\n * for UniformCylindricalEndcap the mapped portion of both Sphere 1\n * and Sphere 2 are bounded by planes of constant \\f$z\\f$, whereas for\n * CylindricalEndcap only one of the mapped portions is bounded by a\n * plane (except for specially chosen map parameters).  Note that\n * UniformCylindricalEndcap can be used to construct maps that connect\n * an arbitrary number of nested spheres; this is not possible for\n * CylindricalEndcap for more than 3 nested spheres because of this\n * asymmetry between CylindricalEndcap's two spherical surfaces.\n *\n * UniformCylindricalEndcap is intended to be composed with `Wedge<2>` maps to\n * construct a portion of a cylindrical domain for a binary system.\n *\n * UniformCylindricalEndcap can be used to construct a domain that is similar\n * to, but not identical to, the one described briefly in the Appendix of\n * \\cite Buchman:2012dw.\n * UniformCylindricalEndcap is used to construct the Blocks analogous to\n * those labeled 'CA wedge', 'EA wedge', 'CB wedge', 'EE wedge',\n * and 'EB wedge' in Figure 20 of that paper.\n *\n * UniformCylindricalEndcap provides the following functions:\n *\n * ## operator()\n *\n * `operator()` maps \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ to \\f$(x,y,z)\\f$\n * according to\n *\n * \\f{align}\n * x^0 &= C_1^0+\\lambda(C_2^0-C_1^0) +\n *        \\cos\\phi\\left(R_1\\sin\\theta_1 +\n *        \\lambda(R_2\\sin\\theta_2-R_1\\sin\\theta_1)\\right), \\label{eq:x0} \\\\\n * x^1 &= C_1^1+\\lambda(C_2^1-C_1^1) +\n *        \\sin\\phi\\left(R_1\\sin\\theta_1 +\n *        \\lambda(R_2\\sin\\theta_2-R_1\\sin\\theta_1)\\right), \\label{eq:x1} \\\\\n * x^2 &= C_1^2+\\lambda(C_2^2-C_1^2) +\n *        R_1\\cos\\theta_1 +\n *        \\lambda(R_2\\cos\\theta_2-R_1\\cos\\theta_1) \\label{eq:x2}.\n * \\f}\n *\n * Here\n * \\f{align}\n * \\lambda  &= \\frac{\\bar{z}+1}{2},\\label{eq:lambdafromzbar}\\\\\n * \\theta_1 &= \\bar{\\rho} \\theta_{1 \\mathrm{max}},\\label{eq:deftheta1}\\\\\n * \\theta_2 &= \\bar{\\rho} \\theta_{2 \\mathrm{max}},\\label{eq:deftheta2}\\\\\n * \\phi     &= \\atan(\\bar{y}/\\bar{x})\\label{eq:defphi},\n * \\f}\n * where \\f$\\theta_{1 \\mathrm{max}}\\f$ and \\f$\\theta_{2 \\mathrm{max}}\\f$\n * are defined by\n * \\f{align}\n *   \\cos(\\theta_{1\\mathrm{max}}) &= (z_{\\mathrm{P}1}-C_1^2)/R_1,\\\\\n *   \\cos(\\theta_{2\\mathrm{max}}) &= (z_{\\mathrm{P}2}-C_2^2)/R_2,\n * \\f}\n * and\n * \\f{align}\n * \\bar{\\rho} &= \\sqrt{\\bar{x}^2+\\bar{y}^2}/\\bar{R} \\label{eq:defrhobar},\n * \\f}\n * where \\f$\\bar{R}\\f$ is the radius of the cylinder in barred\n * coordinates, which is always unity.\n *\n * ## inverse\n *\n * Given \\f$(x,y,z)\\f$ we want to find \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$.\n * From Eq. (\\f$\\ref{eq:x2}\\f$) we can write \\f$\\lambda\\f$ as a function\n * of \\f$\\bar\\rho\\f$:\n *\n * \\f{align}\n * \\lambda &= \\frac{x^2 - C_1^2 - R_1\\cos\\theta_1}\n *                 {C_2^2-C_1^2 + R_2\\cos\\theta_2-R_1\\cos\\theta_1}\n *                 \\label{eq:lambda_from_rho}.\n * \\f}\n *\n * Then by eliminating \\f$\\phi\\f$ from Eqs. (\\f$\\ref{eq:x0}\\f$) and\n * (\\f$\\ref{eq:x1}\\f$) we find that \\f$\\bar{\\rho}\\f$ is the solution\n * of \\f$Q(\\bar{\\rho})=0\\f$, where\n *\n * \\f{align}\n * Q(\\bar{\\rho}) &= \\left(x^0-C_1^0-\\lambda(C_2^0-C_1^0)\\right)^2+\n *                  \\left(x^1-C_1^1-\\lambda(C_2^1-C_1^1)\\right)^2-\n *                  \\left((1-\\lambda)R_1\\sin\\theta_1 +\n *                  \\lambda R_2\\sin\\theta_2\\right)^2.\\label{eq:defQ}\n * \\f}\n * Here \\f$\\lambda\\f$, \\f$\\theta_1\\f$, and \\f$\\theta_2\\f$ are functions\n * of \\f$\\bar{\\rho}\\f$ through Eqs. (\\f$\\ref{eq:lambda_from_rho}\\f$),\n * (\\f$\\ref{eq:deftheta1}\\f$), and (\\f$\\ref{eq:deftheta2}\\f$).\n *\n * We solve \\f$Q(\\bar{\\rho})=0\\f$ numerically; it is a one-dimensional\n * root-finding problem.\n *\n * Once we have determined \\f$\\bar{\\rho}\\f$, we then obtain \\f$\\lambda\\f$\n * from Eq. (\\f$\\ref{eq:lambda_from_rho}\\f$), and we obtain \\f$\\phi\\f$ from\n *\n * \\f{align}\n *  \\tan\\phi &=\n *  \\frac{x^1-C_1^1-\\lambda(C_2^1-C_1^1)}{x^0-C_1^0-\\lambda(C_2^0-C_1^0)}.\n * \\f}\n *\n * Then \\f$\\bar{z}\\f$ is obtained from Eq. (\\f$\\ref{eq:lambdafromzbar}\\f$)\n * and \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$ are obtained from\n *\n * \\f{align}\n *   \\bar{x} &= \\bar{\\rho}\\bar{R}\\cos\\phi,\\\\\n *   \\bar{y} &= \\bar{\\rho}\\bar{R}\\sin\\phi.\n * \\f}\n *\n * ### Considerations when root-finding.\n *\n * We solve \\f$Q(\\bar{\\rho})=0\\f$ numerically for \\f$\\bar{\\rho}\\f$,\n * where \\f$Q(\\bar{\\rho})\\f$ is given by Eq. (\\f$\\ref{eq:defQ}\\f$).\n *\n * #### min/max values of \\f$\\bar{\\rho}\\f$:\n *\n * Note that the root we care about must have\n * \\f$0\\leq\\lambda\\leq 1\\f$; therefore from Eq. (\\f$\\ref{eq:lambda_from_rho}\\f$)\n * we have\n *\n * \\f{align}\n *   \\bar{\\rho}_{\\mathrm{min}} &=\n *      \\left\\{\\begin{array}{ll}\n *           0 & \\text{for } x^2-C_1^2 \\geq R_1, \\\\\n *           \\displaystyle \\frac{1}{\\theta_{1 \\mathrm{max}}}\n *           \\cos^{-1}\\left(\\frac{x^2-C_1^2}{R_1}\\right) & \\text{otherwise}\n *      \\end{array}\\right.\\label{eq:rhobarmin}\\\\\n *   \\bar{\\rho}_{\\mathrm{max}} &=\n *      \\left\\{\\begin{array}{ll}\n *           1 & \\text{for } x^2-C_2^2 \\leq R_2\\cos\\theta_{2 \\mathrm{max}}, \\\\\n *           \\displaystyle \\frac{1}{\\theta_{2 \\mathrm{max}}}\n *           \\cos^{-1}\\left(\\frac{x^2-C_2^2}{R_2}\\right) & \\text{otherwise}\n *      \\end{array}\\right.\\label{eq:rhobarmax}\n * \\f}\n *\n * so we look for a root only between \\f$\\bar{\\rho}_{\\mathrm{min}}\\f$\n * and \\f$\\bar{\\rho}_{\\mathrm{max}}\\f$.\n *\n * #### Roots within roundoff of endpoints:\n *\n * Sometimes a root is within roundoff of \\f$\\bar{\\rho}_{\\mathrm{min}}\\f$\n * or \\f$\\bar{\\rho}_{\\mathrm{max}}\\f$.  This tends to happen at points on the\n * boundary of the mapped region. In this case, the root might\n * not be bracketed by\n * \\f$[\\bar{\\rho}_{\\mathrm{min}},\\bar{\\rho}_{\\mathrm{max}}]\\f$ if the root\n * is slightly outside that interval.  If we find that\n * \\f$Q(\\bar{\\rho}_{\\mathrm{min}})\\f$ is near zero but has the wrong sign,\n * then we slightly expand the interval as follows:\n *\n * \\f{align}\n *    \\bar{\\rho}_{\\mathrm{min}} \\to \\bar{\\rho}_{\\mathrm{min}}\n *  - 2 \\frac{Q(\\bar{\\rho}_{\\mathrm{min}})}{Q'(\\bar{\\rho}_{\\mathrm{min}})},\n * \\f}\n *\n * where \\f$Q'(\\bar{\\rho}_{\\mathrm{min}})\\f$ is the derivative of the function\n * in Eq. (\\f$\\ref{eq:defQ}\\f$). Note that without the factor of 2, this is\n * a Newton-Raphson step; the factor of 2 is there to overcompensate so that\n * the new \\f$\\bar{\\rho}_{\\mathrm{min}}\\f$ brackets the root.\n *\n * Similarly, if it is found that \\f$Q(\\bar{\\rho}_{\\mathrm{max}})\\f$\n * is near zero but has the wrong sign so that the root is not\n * bracketed, then the same formula is used to expand the interval\n * near \\f$\\bar{\\rho} = \\bar{\\rho}_{\\mathrm{max}}\\f$ to bracket the\n * root.\n *\n * Note that by differentiating Eqs. (\\f$\\ref{eq:defQ}\\f$) and\n * (\\f$\\ref{eq:lambda_from_rho}\\f$), one obtains\n *\n * \\f{align}\n * Q'(\\bar{\\rho}) =& -2 \\frac{d\\lambda}{d\\bar{\\rho}}\\left[\n *      \\left(x^0-C_1^0-\\lambda(C_2^0-C_1^0)\\right)(C_2^0-C_1^0)+\n *      \\left(x^1-C_1^1-\\lambda(C_2^1-C_1^1)\\right)(C_2^1-C_1^1)\n *      \\right]\\nonumber \\\\\n *     &\n *    -2 \\left((1-\\lambda)R_1\\sin\\theta_1 +\n *                \\lambda R_2\\sin\\theta_2\\right)\n *    \\left[\n *    \\frac{d\\lambda}{d\\bar{\\rho}} (R_2\\sin\\theta_2-R_1\\sin\\theta_1)\n *    +(1-\\lambda)R_1\\theta_{1 \\mathrm{max}}\\cos\\theta_1\n *    +\\lambda R_2\\theta_{2 \\mathrm{max}}\\cos\\theta_2\n *    \\right], \\label{eq:defQderiv}\n * \\f}\n *\n * where\n * \\f{align}\n *  \\frac{d\\lambda}{d\\bar{\\rho}} &=\n *  \\frac{(1-\\lambda)R_1\\theta_{1 \\mathrm{max}}\\sin\\theta_1\n *          +\\lambda R_2\\theta_{2 \\mathrm{max}}\\sin\\theta_2}\n *       {C_2^2-C_1^2 + R_2\\cos\\theta_2-R_1\\cos\\theta_1}\n *  \\label{eq:dlambda_drhobar}.\n * \\f}\n *\n * #### Roots within roundoff of \\f$\\bar{\\rho}=0\\f$ or \\f$\\bar{\\rho}=1\\f$:\n *\n * For some points on the boundary of the mapped domain, the root will\n * be within roundoff of \\f$\\bar{\\rho}=0\\f$ or \\f$\\bar{\\rho}=1\\f$.\n * Here it does not always make sense to expand the range of the map\n * if the root fails (by roundoff) to be bracketed, as is done above.\n * Furthermore, when \\f$\\bar{\\rho}=0\\f$ is a root it turns\n * out that both \\f$Q(\\bar{\\rho})=0\\f$ and \\f$Q'(\\bar{\\rho})=0\\f$ for\n * \\f$\\bar{\\rho}=0\\f$, so some root-finders (e.g. Newton-Raphson) have\n * difficulty converging.  Therefore the cases where the root is\n * within roundoff of \\f$\\bar{\\rho}=0\\f$ or \\f$\\bar{\\rho}=1\\f$ are\n * treated separately.\n *\n * These cases are detected by comparing terms in the first-order\n * power series of \\f$Q(\\bar{\\rho})=0\\f$ when expanded about\n * \\f$\\bar{\\rho}=0\\f$ or \\f$\\bar{\\rho}=1\\f$.  When one of these cases is\n * recognized, the root is returned as either \\f$\\bar{\\rho}=0\\f$ or\n * \\f$\\bar{\\rho}=1\\f$.\n *\n * #### Quick rejection of points out of range of the map.\n *\n * It is expected that `inverse()` will often be passed points\n * \\f$(x,y,z)\\f$ that are out of the range of the map; in this case\n * `inverse()` returns a default-constructed\n * `std::optional<std::array<double, 3>>`. To avoid the difficulty and\n * expense of attempting to solve \\f$Q(\\bar{\\rho})=0\\f$ numerically\n * for such points (and then having this solution fail), it is useful\n * to quickly reject points \\f$(x,y,z)\\f$ that are outside the range\n * of the map.\n *\n * Any point in the range of the map must be inside or on\n * sphere 2, and it must be outside or on sphere 1, so the inverse map\n * can immediately return a default-constructed\n * `std::optional<std::array<double, 3>>` for a point that does not\n * satisfy these conditions.\n *\n * Likewise, the inverse map can immediately reject any point with\n * \\f$z < z_{\\mathrm{P}1}\\f$.\n *\n * Finally, consider the circle \\f$S_1\\f$ defining the intersection of sphere 1\n * and the plane \\f$z = z_{\\mathrm{P}1}\\f$; this circle has radius\n * \\f$r_1 = R_1 \\sin\\theta_{1\\mathrm{max}}\\f$.  Similarly, the circle\n * \\f$S_2\\f$ defining the intersection of sphere 2 and the plane \\f$z =\n * z_{\\mathrm{P}2}\\f$ has radius \\f$r_2 = R_2\n * \\sin\\theta_{2\\mathrm{max}}\\f$.  Now consider the cone that passes\n * through these two circles.  A point in the range of the map must be inside\n * or on this cone. The cone can be defined parametrically as\n *\n * \\f{align}\n * x_c &= C_1^0 + \\tilde{\\lambda}(C_2^0-C_1^0) +\n *      \\cos\\varphi (r_1 + \\tilde{\\lambda} (r_2 -r_1)),\\\\\n * y_c &= C_1^1 + \\tilde{\\lambda}(C_2^1-C_1^1),+\n *      \\sin\\varphi (r_1 + \\tilde{\\lambda} (r_2 -r_1)),\\\\\n * z_c &= C_1^2 + R_1 \\cos\\theta_{1\\mathrm{max}} +\n *        \\tilde{\\lambda}(C_2^2 + R_2 \\cos\\theta_{2\\mathrm{max}} -\n *        C_1^2 - R_1 \\cos\\theta_{1\\mathrm{max}}),\n * \\f}\n *\n * where \\f$(x_c,y_c,z_c)\\f$ is a point on the cone, and the two\n * parameters defining a point on the cone are the angle \\f$\\varphi\\f$\n * around the cone and the parameter \\f$\\tilde{\\lambda}\\f$, which is\n * defined to be zero on \\f$S_1\\f$ and unity on \\f$S_2\\f$.\n *\n * Given an arbitrary point \\f$(x, y, z)\\f$, we can determine whether\n * or not that point is inside the cone as follows.  First determine\n *\n * \\f{align}\n *  \\tilde{\\lambda} &= \\frac{z - C_1^2 - R_1 \\cos\\theta_{1\\mathrm{max}}}\n *   {C_2^2+ R_2 \\cos\\theta_{2\\mathrm{max}}-\n *   C_1^2- R_1 \\cos\\theta_{1\\mathrm{max}}}, \\\\\n *  \\tilde{x} &= x - C_1^0 - \\tilde{\\lambda} (C_2^0-C_1^0),\\\\\n *  \\tilde{y} &= y - C_1^1 - \\tilde{\\lambda} (C_2^1-C_1^1).\\\\\n * \\f}\n *\n * Then the condition for the point to be inside or on the cone is\n * \\f{align}\n * \\sqrt{\\tilde{x}^2+\\tilde{y}^2} \\le r_1 + (r_2-r_1)\\tilde{\\lambda}.\n * \\f}\n *\n * The inverse map can therefore reject any points that do\n * not satisfy this criterion.\n *\n * ## jacobian\n *\n * One can rewrite Eqs.(\\f$\\ref{eq:x0}\\f$) through (\\f$\\ref{eq:x2}\\f$) as\n *\n * \\f{align}\n * x^0 &= \\frac{1}{2}\\left((1-\\bar{z})C_1^0+ (1+\\bar{z})C_2^0\\right) +\n *        \\frac{\\bar{x}}{2}\\left(\n * (1-\\bar{z}) R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n * (1+\\bar{z}) R_2 S(\\bar{\\rho},\\theta_{2 \\mathrm{max}})\n *     \\right), \\label{eq:x0alt} \\\\\n * x^1 &= \\frac{1}{2}\\left((1-\\bar{z})C_1^1 + (1+\\bar{z})C_2^1\\right) +\n *        \\frac{\\bar{y}}{2}\\left(\n * (1-\\bar{z})R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n * (1+\\bar{z})R_2 S(\\bar{\\rho},\\theta_{2 \\mathrm{max}})\n *     \\right), \\label{eq:x1alt} \\\\\n * x^2 &= \\frac{1}{2}\\left((1-\\bar{z})C_1^2 + (1+\\bar{z})C_2^2\\right) +\n *        \\left(\n * (1-\\bar{z})R_1 \\cos\\theta_1 +\n * (1+\\bar{z})R_2 \\cos\\theta_2\n *     \\right), \\label{eq:x2alt} \\\\\n * \\f}\n *\n * where we have used Eq. (\\f$\\ref{eq:lambdafromzbar}\\f$) to eliminate\n * \\f$\\lambda\\f$ in favor of \\f$\\bar{z}\\f$, and where we have defined the\n * function\n *\n * \\f{align}\n *   S(\\bar{\\rho},a) = \\frac{\\sin(\\bar{\\rho} a)}{\\bar{\\rho}}. \\label{eq:Sdef}\n * \\f}\n *\n * Note that \\f$S(\\bar{\\rho},a)\\f$ is finite as \\f$\\bar{\\rho}\\f$\n * approaches zero, and in the code we must take care that everything\n * remains well-behaved in that limit.\n *\n * Then differentiating Eqs. (\\f$\\ref{eq:x0alt}\\f$) and (\\f$\\ref{eq:x1alt}\\f$)\n * with respect to \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$, taking into account the\n * dependence of \\f$\\bar{\\rho}\\f$ on \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$ from Eq.\n * (\\f$\\ref{eq:defrhobar}\\f$), we find:\n *\n * \\f{align}\n * \\frac{\\partial x^0}{\\partial \\bar{x}} &=\n * \\frac{1}{2}\\left(\n *       (1-\\bar{z}) R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *       (1+\\bar{z}) R_2 S(\\bar{\\rho},\\theta_{2 \\mathrm{max}})\n *       \\right) +\n * \\frac{\\bar{x}^2}{2\\bar{\\rho}} \\left[\n *           (1-\\bar{z}) R_1 S'(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *           (1+\\bar{z}) R_2 S'(\\bar{\\rho},\\theta_{2 \\mathrm{max}})\n *           \\right], \\\\\n * \\frac{\\partial x^1}{\\partial \\bar{y}} &=\n * \\frac{1}{2}\\left(\n *       (1-\\bar{z}) R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *       (1+\\bar{z}) R_2 S(\\bar{\\rho},\\theta_{2 \\mathrm{max}})\n *       \\right) +\n * \\frac{\\bar{y}^2}{2\\bar{\\rho}} \\left[\n *           (1-\\bar{z}) R_1 S'(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *           (1+\\bar{z}) R_2 S'(\\bar{\\rho},\\theta_{2 \\mathrm{max}})\n *           \\right], \\\\\n * \\frac{\\partial x^0}{\\partial \\bar{y}} &=\n * \\frac{\\bar{x}\\bar{y}}{2\\bar{\\rho}}\\left[\n *           (1-\\bar{z}) R_1 S'(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *           (1+\\bar{z}) R_2 S'(\\bar{\\rho},\\theta_{2 \\mathrm{max}})\n *           \\right], \\\\\n * \\frac{\\partial x^1}{\\partial \\bar{x}} &=\n * \\frac{\\partial x^0}{\\partial \\bar{y}},\n * \\f}\n *\n * where \\f$S'(\\bar{\\rho},a)\\f$ means the derivative of \\f$S(\\bar{\\rho},a)\\f$\n * with respect to \\f$\\bar\\rho\\f$.  Note that \\f$S'(\\bar{\\rho},a)/\\bar{\\rho}\\f$\n * approaches a constant value as \\f$\\bar{\\rho}\\f$ approaches zero.\n *\n * Differentiating Eq. (\\f$\\ref{eq:x2alt}\\f$) with respect to\n * \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$ we find\n *\n * \\f{align}\n * \\frac{\\partial x^2}{\\partial \\bar{x}} &=\n * - \\frac{\\bar{x}}{2}\\left[\n *        (1-\\bar{z}) R_1 \\theta_{1 \\mathrm{max}}\n *                S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *        (1+\\bar{z}) R_2 \\theta_{2 \\mathrm{max}}\n *                S(\\bar{\\rho},\\theta_{2 \\mathrm{max}})-\n *                    \\right],\\\\\n * \\frac{\\partial x^2}{\\partial \\bar{y}} &=\n * - \\frac{\\bar{y}}{2}\\left[\n *        (1-\\bar{z}) R_1 \\theta_{1 \\mathrm{max}}\n *                S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *        (1+\\bar{z}) R_2 \\theta_{2 \\mathrm{max}}\n *                S(\\bar{\\rho},\\theta_{2 \\mathrm{max}})-\n *                    \\right].\n * \\f}\n *\n * Differentiating Eqs. (\\f$\\ref{eq:x0alt}\\f$) through (\\f$\\ref{eq:x2alt}\\f$)\n * with respect to \\f$\\bar{z}\\f$ yields\n *\n * \\f{align}\n * \\frac{\\partial x^0}{\\partial \\bar{z}} &=\n * \\frac{1}{2}\\left[\n *  C_2^0-C_1^0 +\n *        \\bar{x}\\left(R_2 S(\\bar{\\rho},\\theta_{2 \\mathrm{max}}) -\n *        R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}})\\right)\n * \\right],\\\\\n * \\frac{\\partial x^1}{\\partial \\bar{z}} &=\n * \\frac{1}{2}\\left[\n *  C_2^1-C_1^1 +\n *        \\bar{y}\\left(R_2 S(\\bar{\\rho},\\theta_{2 \\mathrm{max}}) -\n *        R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}})\\right)\n * \\right],\\\\\n * \\frac{\\partial x^2}{\\partial \\bar{z}} &=\n * \\frac{1}{2}\\left(\n *  C_2^2-C_1^2 + R_2\\cos\\theta_2 - R_1\\cos\\theta_1\n * \\right).\n * \\f}\n *\n * ## inv_jacobian\n *\n * The inverse Jacobian is computed by numerically inverting the\n * Jacobian.\n *\n * ## Restrictions on map parameters\n *\n * We demand that Sphere 1 is fully contained inside Sphere 2, and\n * that the two spheres have at least some small separation between\n * them. In particular, we demand that\n * - \\f$0.98 R_2 >= R_1 + |C_1-C_2|\\f$\n *\n * where 0.98 is a safety factor. It is possible to construct a valid\n * map without this assumption, but the assumption simplifies the\n * code, and the expected use cases obey this restriction.\n *\n * We also demand that \\f$z_{\\mathrm{P}1} <= z_{\\mathrm{P}2} -0.04 R_2\\f$, and\n * that the z planes in the above figures lie above the centers of the\n * corresponding spheres and are not too close to the centers or edges of\n * those spheres; specificially, we demand that\n * - \\f$ 0.075\\pi < \\theta_{1 \\mathrm{max}} < 0.45\\pi\\f$\n * - \\f$ 0.075\\pi < \\theta_{2 \\mathrm{max}} < 0.45\\pi\\f$\n *\n * Here 0.075 and 0.45 are safety factors. These restrictions are not\n * strictly necessary but are made for simplicity and to ensure the\n * accuracy of the inverse map (the inverse map becomes less accurate if\n * the map parameters are extreme).\n *\n * Consider the line segment \\f$L\\f$ that connects a point on the\n * circle \\f$S_1\\f$ (the circle formed by the intersection of sphere 1\n * and the plane \\f$z=z_{\\mathrm{P}1}\\f$) with the center of the\n * circle \\f$S_1\\f$.  Consider another line segment \\f$L'\\f$ that\n * connects the same point on the circle \\f$S_1\\f$ with the\n * corresponding point on the circle \\f$S_2\\f$ (the circle formed by\n * the intersection of sphere 2 and the plane\n * \\f$z=z_{\\mathrm{P}2}\\f$). Now consider the angle between \\f$L\\f$\n * and \\f$L'\\f$, as measured from the interior of sphere 1, and Let\n * \\f$\\alpha\\f$ be the minimum value of this angle over the circle.\n * \\f$\\alpha\\f$ is shown in the figure above. If\n * \\f$\\alpha < \\theta_{1 \\mathrm{max}}\\f$, then the line segment \\f$L'\\f$\n * intersects the mapped portion of sphere 1 twice, so the map is\n * multiply valued.  Similarly, if \\f$\\alpha < \\theta_{2 \\mathrm{max}}\\f$,\n * then the line segment \\f$L'\\f$ intersects the mapped portion of\n * sphere 2 twice, and again the map is multiply valued.  Therefore we\n * demand that the map parameters are such that\n * - \\f$\\alpha > 1.1 \\theta_{1 \\mathrm{max}}\\f$\n * - \\f$\\alpha > 1.1 \\theta_{2 \\mathrm{max}}\\f$\n *\n * where 1.1 is a safety factor.\n *\n * The condition on \\f$\\alpha\\f$ is guaranteed to provide an\n * invertible map if \\f$C_1^0=C_2^0\\f$ and \\f$C_1^1=C_2^1\\f$.\n * However, for \\f$C_1^0 \\neq C_2^0\\f$ or \\f$C_1^1\\neq C_2^1\\f$, even\n * if the \\f$\\alpha\\f$ condition is satisfied, it is possible for two\n * lines of constant \\f$(\\bar{x},\\bar{y})\\f$ (each line has different\n * values of \\f$(\\bar{x},\\bar{y})\\f$) to pass through the same point\n * \\f$(x,y,z)\\f$ if those lines are not coplanar.  This condition is\n * difficult to check analytically, so we check it numerically.  We\n * have found empirically that if \\f$Q(\\bar{\\rho})\\f$ from\n * Eq. (\\f$\\ref{eq:defQ}\\f$) has only a single root between\n * \\f$\\bar{\\rho}_{\\mathrm{min}}\\f$ and \\f$\\bar{\\rho}_{\\mathrm{max}}\\f$\n * for all points \\f$(x,y,z)\\f$ on the surface of sphere 1 with\n * \\f$z\\geq z_{\\mathrm{P}1}\\f$ and with \\f$(x-C_1^0)/(y-C_1^1) =\n * (C_1^0-C_2^0)/(C_1^1-C_2^1)\\f$, then the map is single-valued\n * everywhere.  We cannot numerically check every point in this\n * one-parameter family of points, but we demand that this condition\n * is satisfied for a reasonably large number of points in this\n * family.  This check is not very expensive since it is done only\n * once, in the constructor.\n *\n */\nclass UniformCylindricalEndcap {\n public:\n  static constexpr size_t dim = 3;\n  UniformCylindricalEndcap(const std::array<double, 3>& center_one,\n                           const std::array<double, 3>& center_two,\n                           double radius_one, double radius_two,\n                           double z_plane_one, double z_plane_two);\n  UniformCylindricalEndcap() = default;\n  ~UniformCylindricalEndcap() = default;\n  UniformCylindricalEndcap(UniformCylindricalEndcap&&) = default;\n  UniformCylindricalEndcap(const UniformCylindricalEndcap&) = default;\n  UniformCylindricalEndcap& operator=(const UniformCylindricalEndcap&) =\n      default;\n  UniformCylindricalEndcap& operator=(UniformCylindricalEndcap&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // clang-tidy: google runtime references\n  void pup(PUP::er& p);  // NOLINT\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const UniformCylindricalEndcap& lhs,\n                         const UniformCylindricalEndcap& rhs);\n  std::array<double, 3> center_one_{};\n  std::array<double, 3> center_two_{};\n  double radius_one_{std::numeric_limits<double>::signaling_NaN()};\n  double radius_two_{std::numeric_limits<double>::signaling_NaN()};\n  double z_plane_one_{std::numeric_limits<double>::signaling_NaN()};\n  double z_plane_two_{std::numeric_limits<double>::signaling_NaN()};\n  double theta_max_one_{std::numeric_limits<double>::signaling_NaN()};\n  double theta_max_two_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const UniformCylindricalEndcap& lhs,\n                const UniformCylindricalEndcap& rhs);\n\n/// Given parameters for UniformCylindricalEndcap, returns whether\n/// the map is invertible for target points on sphere_one.\n///\n/// `is_uniform_cylindrical_endcap_invertible_on_sphere_one` is\n/// publicly visible because it is useful for unit tests that need to\n/// choose valid parameters to pass into the map.\nbool is_uniform_cylindrical_endcap_invertible_on_sphere_one(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, const double radius_one,\n    const double radius_two, const double theta_max_one,\n    const double theta_max_two);\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/UniformCylindricalFlatEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/UniformCylindricalFlatEndcap.hpp\"\n\n#include <boost/math/special_functions/sign.hpp>\n#include <cmath>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <sstream>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CylindricalEndcapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nnamespace {\n\n// The function that the inverse map needs to root-find to find rhobar.\ndouble this_function_is_zero_for_correct_rhobar(\n    const double rhobar, const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, double radius_one,\n    const double radius_two, const double theta_max_one,\n    const std::array<double, 3>& target_coords) {\n  const double r_one_cos_theta_one = radius_one * cos(rhobar * theta_max_one);\n  const double lambda =\n      (target_coords[2] - center_one[2] - r_one_cos_theta_one) /\n      (center_two[2] - center_one[2] - r_one_cos_theta_one);\n  return square(target_coords[0] - center_one[0] -\n                lambda * (center_two[0] - center_one[0])) +\n         square(target_coords[1] - center_one[1] -\n                lambda * (center_two[1] - center_one[1])) -\n         square((1.0 - lambda) * radius_one * sin(rhobar * theta_max_one) +\n                lambda * radius_two * rhobar);\n}\n\n// min and max values of rhobar in the inverse function.\nstd::tuple<double, double> rhobar_min_max(\n    const std::array<double, 3>& center_one, const double radius_one,\n    const double theta_max_one, const std::array<double, 3>& target_coords) {\n  // Choose the minimum value of rhobar so that lambda >=0, where\n  // lambda is the quantity inside function_to_zero.  Note that the\n  // denominator of lambda inside that function is always positive.\n  const double rhobar_min =\n      target_coords[2] - center_one[2] >= radius_one\n          ? 0.0\n          : acos((target_coords[2] - center_one[2]) / radius_one) /\n                theta_max_one;\n  return {rhobar_min, 1.0};\n}\n\n// Returns whether the map is invertible if the target is on sphere_one.\nbool is_invertible_for_target_on_sphere_one(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, const double radius_one,\n    const double radius_two, const double theta_max_one,\n    const std::array<double, 3>& target_coords) {\n  const auto [rhobar_min, rhobar_max] =\n      rhobar_min_max(center_one, radius_one, theta_max_one, target_coords);\n  if (equal_within_roundoff(rhobar_min, rhobar_max)) {\n    // Special case where rhobar_min == rhobar_max to roundoff.  This\n    // case occurs when the target point (assumed to be on sphere_one)\n    // is at rhobar=1.  In this case there is nothing to test because\n    // there is only one possible value of rhobar, so there cannot be\n    // two roots for two different values of rhobar.\n    return true;\n  }\n  const size_t num_pts = 1000;\n  const double drhobar = (rhobar_max - rhobar_min) / (num_pts - 1.0);\n  // Because the target point is on sphere_one, the only root should\n  // be at rhobar = rhobar_min, where lambda=0.  The actual value of\n  // the function at rhobar_min may be either sign because of\n  // roundoff.  So ignore that point, and start at the next point.\n  // Keep track of the sign, and look for sign changes as we progress\n  // from point to point.\n  std::optional<double> sign;\n  for (size_t i = 1; i < num_pts; ++i) {\n    const double rhobar = rhobar_min + i * drhobar;\n    const double func = this_function_is_zero_for_correct_rhobar(\n        rhobar, center_one, center_two, radius_one, radius_two, theta_max_one,\n        target_coords);\n    if (not sign.has_value()) {\n      sign = func > 0.0 ? 1.0 : -1.0;\n    } else if (func * sign.value() <= 0.0) {\n      // There is another root, so function is not invertible.\n      //\n      // Note that the code should rarely get here if the map\n      // parameters are in range, so it is difficult to add an error\n      // test because I don't know how to make it get here on purpose;\n      // therefore we turn off codecov.\n      return false; // LCOV_EXCL_LINE\n    }\n  }\n  return true;  // No roots found.\n}\n}  // namespace\n\nbool is_uniform_cylindrical_flat_endcap_invertible_on_sphere_one(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, double radius_one,\n    double radius_two, double theta_max_one) {\n  if (center_one[1] == center_two[1] and center_one[0] == center_two[0]) {\n    return true;\n  }\n  // Choose the worst direction, which is the direction of x-y-plane\n  // projection of the difference between center_one and center_two.\n  const double phi =\n      atan2(center_one[1] - center_two[1], center_one[0] - center_two[0]);\n  const size_t num_pts = 100;\n  for (size_t i = 0; i < num_pts; ++i) {\n    const double theta_one = theta_max_one * (i / (num_pts - 1.0));\n    if (not is_invertible_for_target_on_sphere_one(\n            center_one, center_two, radius_one, radius_two, theta_max_one,\n            {{center_one[0] + radius_one * sin(theta_one) * cos(phi),\n              center_one[1] + radius_one * sin(theta_one) * sin(phi),\n              center_one[2] + radius_one * cos(theta_one)}})) {\n      // Note that the code should rarely get here if the map\n      // parameters are in range, so it is difficult to add an error\n      // test because I don't know how to make it get here on purpose;\n      // therefore we turn off codecov.\n      return false; // LCOV_EXCL_LINE\n    }\n  }\n  return true;\n}\n\nUniformCylindricalFlatEndcap::UniformCylindricalFlatEndcap(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, double radius_one,\n    double radius_two, double z_plane_one)\n    : center_one_(center_one),\n      center_two_(center_two),\n      radius_one_([&radius_one]() {\n        // The equal_within_roundoff below has an implicit scale of 1,\n        // so the ASSERT may trigger in the case where we really\n        // want an entire domain that is very small.\n        ASSERT(not equal_within_roundoff(radius_one, 0.0),\n               \"Cannot have zero radius_one\");\n        ASSERT(radius_one > 0.0, \"Cannot have negative radius_one\");\n        return radius_one;\n        // codecov doesn't understand lambdas in constructors for some\n        // reason, and says the next line is not covered even though it is.\n      }()), // LCOV_EXCL_LINE\n      radius_two_([&radius_two]() {\n        // The equal_within_roundoff below has an implicit scale of 1,\n        // so the ASSERT may trigger in the case where we really\n        // want an entire domain that is very small.\n        ASSERT(not equal_within_roundoff(radius_two, 0.0),\n               \"Cannot have zero radius_two\");\n        ASSERT(radius_two > 0.0, \"Cannot have negative radius_two\");\n        return radius_two;\n        // codecov doesn't understand lambdas in constructors for some\n        // reason, and says the next line is not covered even though it is.\n      }()), // LCOV_EXCL_LINE\n      z_plane_one_(z_plane_one),\n      theta_max_one_([&center_one, &radius_one, &z_plane_one]() {\n        const double cos_theta_max = (z_plane_one - center_one[2]) / radius_one;\n        ASSERT(abs(cos_theta_max) < 1.0,\n               \"Plane one must intersect sphere_one, and at more than one \"\n               \"point. You probably specified a bad value of z_plane_one, \"\n               \"radius_one, or the z component of center_one. \"\n               \"Here z_plane_one=\"\n                   << z_plane_one << \", radius_one = \" << radius_one\n                   << \", center_one=\" << center_one\n                   << \", and cos_theta_max = \" << cos_theta_max);\n        return acos(cos_theta_max);\n      }()) {\n  // The code below defines several variables that are used only in ASSERTs.\n  // We put that code in a #ifdef SPECTRE_DEBUG to avoid clang-tidy complaining\n  // about unused variables in release mode.\n#ifdef SPECTRE_DEBUG\n\n  // For some reason, codecov thinks that the following lambda never\n  // gets called, even though it is in all of the ASSERT messages below.\n  // LCOV_EXCL_START\n  const auto param_string = [this]() -> std::string {\n    std::ostringstream buffer;\n    buffer << \"\\nParameters to UniformCylindricalFlatEndcap:\\nradius_one=\"\n           << radius_one_ << \"\\nradius_two=\" << radius_two_\n           << \"\\ncenter_one=\" << center_one_ << \"\\ncenter_two=\" << center_two_\n           << \"\\nz_plane_one=\" << z_plane_one_;\n    return buffer.str();\n  };\n  // LCOV_EXCL_STOP\n\n  // Assumptions made in the map.  The exact numbers for all of these\n  // can be changed, as long as the unit test is changed to test them.\n  ASSERT(center_two[2] >= center_one[2] + 1.05 * radius_one,\n         \"center_two[2] must be >= center_one[2] + 1.05 * radius_one, not \"\n             << center_two[2] << \" \" << center_one[2] + 1.05 * radius_one\n             << param_string());\n  ASSERT(center_two[2] <= center_one[2] + 5.0 * radius_one,\n         \"center_two[2] must be <= center_one[2] + 5.0 * radius_one, not \"\n             << center_two[2] << \" \" << center_one[2] + 5.0 * radius_one\n             << param_string());\n  ASSERT(radius_two >= 0.1 * radius_one * sin(theta_max_one_),\n         \"radius_two is too small: \" << radius_two << \" \"\n                                     << 0.1 * radius_one * sin(theta_max_one_)\n                                     << param_string());\n  ASSERT(theta_max_one_ < M_PI * 0.35,\n         \"z_plane_one is too close to the center of sphere_one: theta/pi = \"\n             << theta_max_one_ / M_PI << param_string());\n  ASSERT(theta_max_one_ > M_PI * 0.075,\n         \"z_plane_one is too far from the center of sphere_one: theta/pi = \"\n             << theta_max_one_ / M_PI << param_string());\n  ASSERT(\n      is_uniform_cylindrical_flat_endcap_invertible_on_sphere_one(\n          center_one_, center_two_, radius_one_, radius_two_, theta_max_one_),\n      \"The map is not invertible at at least one point on sphere_one.\"\n      \" center_one = \"\n          << center_one_ << \" center_two = \" << center_two_\n          << \" radius_one = \" << radius_one_ << \" radius_two = \" << radius_two_\n          << \" theta_max_one = \" << theta_max_one_ << param_string());\n\n  const double horizontal_dist_spheres =\n      sqrt(square(center_one[0] - center_two[0]) +\n           square(center_one[1] - center_two[1]));\n\n  ASSERT(horizontal_dist_spheres <= radius_one_ * sin(theta_max_one_),\n         \"Horizontal distance between centers is too large: \"\n             << horizontal_dist_spheres << \" \"\n             << radius_one_ * sin(theta_max_one_) << param_string());\n\n  // max_horizontal_dist_between_circles can be either sign;\n  // therefore alpha can be in either the first or second quadrant.\n  const double max_horizontal_dist_between_circles =\n      horizontal_dist_spheres + radius_one_ * sin(theta_max_one_) - radius_two_;\n\n  const double alpha =\n      atan2(center_two[2] - z_plane_one, max_horizontal_dist_between_circles);\n  ASSERT(alpha > 1.1 * theta_max_one_,\n         \"Angle alpha is too small: alpha = \"\n             << alpha << \", theta_max_one = \" << theta_max_one_\n             << \", max_horizontal_dist_between_circles = \"\n             << max_horizontal_dist_between_circles\n             << \", horizontal_dist_spheres = \" << horizontal_dist_spheres\n             << param_string());\n#endif\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3>\nUniformCylindricalFlatEndcap::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n  std::array<ReturnType, 3> target_coords{};\n  ReturnType& x = target_coords[0];\n  ReturnType& y = target_coords[1];\n  ReturnType& z = target_coords[2];\n\n  // Use y and z as temporary storage to avoid allocations,\n  // before setting them to their actual values.\n  z = sqrt(square(xbar) + square(ybar));  // rhobar in the dox\n  y = radius_one_ *\n          cylindrical_endcap_helpers::sin_ax_over_x(z, theta_max_one_) *\n          (1.0 - zbar) +\n      radius_two_ * (1.0 + zbar);\n\n  x = 0.5 * (y * xbar + center_one_[0] * (1.0 - zbar) +\n             center_two_[0] * (1.0 + zbar));\n  y = 0.5 * (y * ybar + center_one_[1] * (1.0 - zbar) +\n             center_two_[1] * (1.0 + zbar));\n  z = 0.5 *\n      ((radius_one_ * cos(theta_max_one_ * z) + center_one_[2]) * (1.0 - zbar) +\n       center_two_[2] * (1.0 + zbar));\n  return target_coords;\n}\n\nstd::optional<std::array<double, 3>> UniformCylindricalFlatEndcap::inverse(\n    const std::array<double, 3>& target_coords) const {\n  // First do some easy checks that target_coords is in the range\n  // of the map.  Need to accept points that are out of range by roundoff.\n\n  // check z >= z_plane_one_\n  if (target_coords[2] < z_plane_one_ and\n      not equal_within_roundoff(target_coords[2], z_plane_one_)) {\n    return std::nullopt;\n  }\n\n  // check that target point is at smaller z than circle_two\n  if (target_coords[2] > center_two_[2] and\n      not equal_within_roundoff(target_coords[2], center_two_[2])) {\n    return std::nullopt;\n  }\n\n  // check that point is outside or on sphere_one_\n  const double r_minus_center_one_squared =\n      square(target_coords[0] - center_one_[0]) +\n      square(target_coords[1] - center_one_[1]) +\n      square(target_coords[2] - center_one_[2]);\n  if (r_minus_center_one_squared < square(radius_one_) and\n      not equal_within_roundoff(sqrt(r_minus_center_one_squared),\n                                radius_one_)) {\n    // sqrt above because we don't want to change the scale of\n    // equal_within_roundoff too much.\n    return std::nullopt;\n  }\n\n  // Check if the point is inside the cone\n  const double lambda_tilde =\n      (target_coords[2] - z_plane_one_) / (center_two_[2] - z_plane_one_);\n  const double rho_tilde =\n      sqrt(square(target_coords[0] - center_one_[0] -\n                  lambda_tilde * (center_two_[0] - center_one_[0])) +\n           square(target_coords[1] - center_one_[1] -\n                  lambda_tilde * (center_two_[1] - center_one_[1])));\n  const double circle_radius =\n      radius_one_ * sin(theta_max_one_) * (1.0 - lambda_tilde) +\n      radius_two_ * lambda_tilde;\n  if (rho_tilde > circle_radius and\n      not equal_within_roundoff(rho_tilde, circle_radius,\n                                std::numeric_limits<double>::epsilon() * 100.0,\n                                radius_two_)) {\n    return std::nullopt;\n  }\n\n  // To find rhobar we will do numerical root finding.\n  // function_to_zero is the function that is zero when rhobar has the\n  // correct value.\n  const auto function_to_zero = [this, &target_coords](const double rhobar) {\n    return this_function_is_zero_for_correct_rhobar(\n        rhobar, center_one_, center_two_, radius_one_, radius_two_,\n        theta_max_one_, target_coords);\n  };\n\n  // Derivative of function_to_zero with respect to rhobar.\n  const auto deriv_function_to_zero = [this,\n                                       &target_coords](const double rhobar) {\n    const double r_one_cos_theta_one =\n        radius_one_ * cos(rhobar * theta_max_one_);\n    const double lambda =\n        (target_coords[2] - center_one_[2] - r_one_cos_theta_one) /\n        (center_two_[2] - center_one_[2] - r_one_cos_theta_one);\n    // deriv of r_one_cos_theta_one with respect to rho.\n    const double d_r_one_cos_theta_one =\n        radius_one_ * sin(rhobar * theta_max_one_) * theta_max_one_;\n    const double dlambda_drho =\n        d_r_one_cos_theta_one * (1.0 - lambda) /\n        (center_two_[2] - center_one_[2] - r_one_cos_theta_one);\n    return -2.0 * dlambda_drho *\n               ((center_two_[0] - center_one_[0]) *\n                    (target_coords[0] - center_one_[0] -\n                     lambda * (center_two_[0] - center_one_[0])) +\n                (center_two_[1] - center_one_[1]) *\n                    (target_coords[1] - center_one_[1] -\n                     lambda * (center_two_[1] - center_one_[1]))) -\n           2.0 *\n               ((1.0 - lambda) * radius_one_ * sin(rhobar * theta_max_one_) +\n                lambda * radius_two_ * rhobar) *\n               ((radius_two_ * rhobar -\n                 radius_one_ * sin(rhobar * theta_max_one_)) *\n                    dlambda_drho +\n                (1.0 - lambda) * r_one_cos_theta_one * theta_max_one_ +\n                lambda * radius_two_);\n  };\n\n  double rhobar_min{};\n  double rhobar_max{};\n  std::tie(rhobar_min, rhobar_max) =\n      rhobar_min_max(center_one_, radius_one_, theta_max_one_, target_coords);\n\n  // If rhobar is zero, then the root finding doesn't converge\n  // well. This is because the function behaves like rhobar^2 for\n  // small values of rhobar, so both the function and its derivative\n  // are zero at rhobar==0.\n  //\n  // Note that rhobar==0 is not that uncommon, and will occur if there\n  // are grid points on the symmetry axis.\n  //\n  // Similarly, rhobar==1 occurs if there are grid points on the block\n  // boundary.\n  //\n  // So check for the special cases of rhobar==0 and rhobar==1\n  // before doing any root finding.\n  const auto this_is_small_if_rhobar_is_near_unity = [this, &target_coords]() {\n    const double denom = 1.0 / (center_two_[2] - center_one_[2] -\n                                radius_one_ * cos(theta_max_one_));\n    const double lambda_if_rhobar_is_unity =\n        (target_coords[2] - center_one_[2] -\n         radius_one_ * cos(theta_max_one_)) *\n        denom;\n    // The function function_to_zero goes like\n    // first_term + second_term (1-rhobar) when 1-rhobar is small,\n    // where first_term goes to zero (faster than (1-rhobar)) when\n    // 1-rhobar is small.\n    // We don't have a good sense of scale for first_term, so we\n    // check the size of first_term/|second_term|.\n    const double temp = radius_one_ * sin(theta_max_one_) +\n                        lambda_if_rhobar_is_unity *\n                            (radius_two_ - radius_one_ * sin(theta_max_one_));\n    const double first_term =\n        square(target_coords[0] - center_one_[0] -\n               lambda_if_rhobar_is_unity * (center_two_[0] - center_one_[0])) +\n        square(target_coords[1] - center_one_[1] -\n               lambda_if_rhobar_is_unity * (center_two_[1] - center_one_[1])) -\n        square(temp);\n    // delta_lambda is (lambda-lambda_if_rhobar_is_unity)/(1-rhobar)\n    // to first order in 1-rhobar.\n    const double delta_lambda =\n        -radius_one_ * theta_max_one_ * sin(theta_max_one_) * square(denom) *\n        (center_two_[2] - target_coords[2]);\n    const double second_term =\n        2.0 * delta_lambda *\n            (lambda_if_rhobar_is_unity *\n                 (square(center_two_[0] - center_one_[0]) +\n                  square(center_two_[1] - center_one_[1])) -\n             (target_coords[0] - center_one_[0]) *\n                 (center_two_[1] - center_one_[1]) -\n             (target_coords[1] - center_one_[1]) *\n                 (center_two_[0] - center_one_[0])) -\n        2.0 * temp *\n            (-(1.0 - lambda_if_rhobar_is_unity) * radius_one_ * theta_max_one_ *\n                 cos(theta_max_one_) -\n             lambda_if_rhobar_is_unity * radius_two_ +\n             delta_lambda * (radius_two_ - radius_one_ * sin(theta_max_one_)));\n    return std::abs(first_term) / std::abs(second_term);\n  };\n\n  const auto this_is_small_if_rhobar_is_near_zero = [this, &target_coords]() {\n    const double denom = 1.0 / (center_two_[2] - center_one_[2] - radius_one_);\n    const double lambda_if_rhobar_is_zero =\n        (target_coords[2] - center_one_[2] - radius_one_) * denom;\n    // The function we are trying to make zero goes like\n    // first_term + second_term rhobar^2, and first_term goes to zero\n    // (faster than rhobar^2) when rhobar is small.  We don't have a good\n    // sense of scale for first_term, so we check the size of\n    // first_term/second_term.\n    const double first_term =\n        square(target_coords[0] - center_one_[0] -\n               lambda_if_rhobar_is_zero * (center_two_[0] - center_one_[0])) +\n        square(target_coords[1] - center_one_[1] -\n               lambda_if_rhobar_is_zero * (center_two_[1] - center_one_[1]));\n    // delta_lambda is (lambda-lambda_if_rhobar_is_zero)/rhobar^2\n    // to first order in rhobar^2.\n    const double delta_lambda = radius_one_ * square(theta_max_one_) *\n                                square(denom) *\n                                (center_two_[2] - target_coords[2]);\n    const double second_term =\n        2.0 * delta_lambda *\n            (lambda_if_rhobar_is_zero *\n                 (square(center_two_[0] - center_one_[0]) +\n                  square(center_two_[1] - center_one_[1])) -\n             (target_coords[0] - center_one_[0]) *\n                 (center_two_[1] - center_one_[1]) -\n             (target_coords[1] - center_one_[1]) *\n                 (center_two_[0] - center_one_[0])) -\n        square((1.0 - lambda_if_rhobar_is_zero) * radius_one_ * theta_max_one_ +\n               lambda_if_rhobar_is_zero * radius_two_);\n    return std::abs(first_term) / std::abs(second_term);\n  };\n\n  if (rhobar_min < 1.e-6 and this_is_small_if_rhobar_is_near_zero() < 1.e-14) {\n    // Treat rhobar as zero, if it is zero to roundoff.\n    // We empirically choose roundoff here to be 1.e-14.  We don't\n    // really need the rhobar_min < 1.e-6 in the 'if' above, but we\n    // add it so that we can save the computational expense of calling\n    // this_is_small_if_rhobar_is_near_zero() when we already know\n    // that rhobar isn't close to zero. We choose the relatively large\n    // value of 1.e-6 to err on the side of caution.\n    const double lambda =\n        (target_coords[2] - center_one_[2] - radius_one_) /\n        (center_two_[2] - center_one_[2] - radius_one_);\n    return {{0.0, 0.0, 2.0 * lambda - 1.0}};\n  } else if (1.0 - rhobar_max < 1.e-6 and\n             this_is_small_if_rhobar_is_near_unity() < 1.e-14) {\n    // Treat rhobar as unity, if it is unity to roundoff.\n    // We empirically choose roundoff here to be 1.e-14.  We don't\n    // really need the 1-rhobar_max < 1.e-6 in the 'if' above, but we\n    // add it so that we can save the computational expense of calling\n    // this_is_small_if_rhobar_is_near_unity() when we already know\n    // that rhobar isn't close to unity. We choose the relatively\n    // large value of 1.e-6 to err on the side of caution.\n    const double lambda =\n        (target_coords[2] - center_one_[2] -\n         radius_one_ * cos(theta_max_one_)) /\n        (center_two_[2] - center_one_[2] - radius_one_ * cos(theta_max_one_));\n    const double denom =\n        1.0 / ((1.0 - lambda) * radius_one_ * sin(theta_max_one_) +\n               lambda * radius_two_);\n    return {{(target_coords[0] - center_one_[0] -\n              lambda * (center_two_[0] - center_one_[0])) *\n                 denom,\n             (target_coords[1] - center_one_[1] -\n              lambda * (center_two_[1] - center_one_[1])) *\n                 denom,\n             2.0 * lambda - 1.0}};\n  }\n\n  // If we get here, then rhobar is not near unity or near zero.\n  // So let's find rhobar.\n  double rhobar = std::numeric_limits<double>::signaling_NaN();\n\n  // The root should always be bracketed.  However, if\n  // rhobar==rhobar_min or rhobar==rhobar_max, the function may not be\n  // exactly zero because of roundoff and then the root might not be\n  // bracketed.\n  double function_at_rhobar_min = function_to_zero(rhobar_min);\n  double function_at_rhobar_max = function_to_zero(rhobar_max);\n  if (function_at_rhobar_min * function_at_rhobar_max > 0.0) {\n    // Root not bracketed.\n    // If the bracketing failure is due to roundoff, then\n    // slightly adjust the bounds to increase the range. Otherwise, error.\n    //\n    // roundoff_ratio is the limiting ratio of the two function values\n    // for which we should attempt to adjust the bounds.  It is ok for\n    // roundoff_ratio to be much larger than actual roundoff, since it\n    // doesn't hurt to expand the interval and try again when\n    // otherwise we would just error.\n    constexpr double roundoff_ratio = 1.e-3;\n    if (abs(function_at_rhobar_min) / abs(function_at_rhobar_max) <\n        roundoff_ratio) {\n      // Slightly decrease rhobar_min.  How far do we decrease it?\n      // Figure that out by looking at the deriv of the function.\n      const double deriv_function_at_rhobar_min =\n          deriv_function_to_zero(rhobar_min);\n      const double trial_rhobar_min_increment =\n          -2.0 * function_at_rhobar_min / deriv_function_at_rhobar_min;\n      // new_rhobar_min = rhobar_min + trial_rhobar_min_increment would be\n      // a Newton-Raphson step except for the factor of 2 in\n      // trial_rhobar_min_increment. The factor of 2 is there to\n      // over-compensate so that the new rhobar_min brackets the\n      // root.\n      //\n      // But sometimes the factor of 2 is not enough when\n      // trial_rhobar_min_increment is roundoff-small so that the\n      // change in new_rhobar_min is zero or only in the last one or\n      // two bits.  If this is the case, then we set\n      // rhobar_min_increment to some small roundoff value with the\n      // same sign as trial_rhobar_min_increment.\n      // Note that rhobar is always between zero and one, so it is\n      // ok to use an absolute epsilon.\n      const double rhobar_min_increment =\n          abs(trial_rhobar_min_increment) >\n                  100.0 * std::numeric_limits<double>::epsilon()\n              ? trial_rhobar_min_increment\n              : boost::math::copysign(\n                    100.0 * std::numeric_limits<double>::epsilon(),\n                    trial_rhobar_min_increment);\n      const double new_rhobar_min = rhobar_min + rhobar_min_increment;\n      const double function_at_new_rhobar_min =\n          function_to_zero(new_rhobar_min);\n      if (function_at_new_rhobar_min * function_at_rhobar_min > 0.0) {\n        // This error should never happen except for a bug, so it\n        // is hard to make an error test for it.\n        // LCOV_EXCL_START\n        ERROR(\n            \"Cannot find bracket after trying to adjust bracketing \"\n            \"rhobar_min due \"\n            \"to roundoff : rhobar_min=\"\n            << rhobar_min << \" f(rhobar_min)=\" << function_at_rhobar_min\n            << \" rhobar_max=\" << rhobar_max << \" f(rhobar_max)=\"\n            << function_at_rhobar_max << \" new_rhobar_min=\" << new_rhobar_min\n            << \" f(new_rhobar_min)=\" << function_at_new_rhobar_min\n            << \" df(new_rhobar_min)=\" << deriv_function_at_rhobar_min\n            << \" new_rhobar_min-rhobar_min=\" << new_rhobar_min - rhobar_min\n            << \"\\n\");\n        // LCOV_EXCL_STOP\n      }\n      // Now the root is bracketed between rhobar_min and new_rhobar_min,\n      // so replace rhobar_max and rhobar_min and then fall through to the\n      // root finder.\n      rhobar_max = rhobar_min;\n      function_at_rhobar_max = function_at_rhobar_min;\n      rhobar_min = new_rhobar_min;\n      function_at_rhobar_min = function_at_new_rhobar_min;\n    } else {\n      // This error should never happen except for a bug, so it\n      // is hard to make an error test for it.\n      // LCOV_EXCL_START\n      ERROR(\"Root is not bracketed: rhobar_min=\"\n            << rhobar_min << \" f(rhobar_min)=\" << function_at_rhobar_min\n            << \" rhobar_max=\" << rhobar_max\n            << \" f(rhobar_max)=\" << function_at_rhobar_max);\n      // LCOV_EXCL_STOP\n    }\n  }\n  // If we get here, root is bracketed. Use toms748, and use the\n  // function evaluations that we have already done.\n\n  // Rhobar is between zero and 1, so the scale is unity, so therefore\n  // abs and rel tolerance are equal.\n  constexpr double abs_tol = 1.e-15;\n  constexpr double rel_tol = 1.e-15;\n\n  try {\n    rhobar =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(function_to_zero, rhobar_min, rhobar_max,\n                            function_at_rhobar_min, function_at_rhobar_max,\n                            abs_tol, rel_tol);\n    // This error should never happen except for a bug, so it\n    // is hard to make an error test for it.\n    // LCOV_EXCL_START\n  } catch (std::exception&) {\n    ERROR(\"Cannot find root after bracketing: rhobar_min=\"\n          << rhobar_min << \" f(rhobar_min)=\" << function_at_rhobar_min\n          << \" rhobar_max=\" << rhobar_max\n          << \" f(rhobar_max)=\" << function_at_rhobar_max);\n    // LCOV_EXCL_STOP\n  }\n\n  // Now that we have rhobar, construct inverse.\n  const double r_one_cos_theta_one = radius_one_ * cos(rhobar * theta_max_one_);\n  const double lambda =\n      (target_coords[2] - center_one_[2] - r_one_cos_theta_one) /\n      (center_two_[2] - center_one_[2] - r_one_cos_theta_one);\n  const double denom =\n      1.0 / ((1.0 - lambda) * radius_one_ * sin(rhobar * theta_max_one_) +\n             lambda * radius_two_ * rhobar);\n  return {{rhobar *\n               (target_coords[0] - center_one_[0] -\n                lambda * (center_two_[0] - center_one_[0])) *\n               denom,\n           rhobar *\n               (target_coords[1] - center_one_[1] -\n                lambda * (center_two_[1] - center_one_[1])) *\n               denom,\n           2.0 * lambda - 1.0}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nUniformCylindricalFlatEndcap::jacobian(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n\n  auto jac =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  // Use jacobian components as temporary storage to avoid extra\n  // memory allocations.\n  get<2, 2>(jac) = sqrt(square(xbar) + square(ybar));\n  get<2, 0>(jac) = 0.5 * radius_one_ *\n                   cylindrical_endcap_helpers::sin_ax_over_x(get<2, 2>(jac),\n                                                             theta_max_one_) *\n                   (1.0 - zbar);\n  get<1, 1>(jac) = get<2, 0>(jac) + 0.5 * radius_two_ * (1.0 + zbar);\n  get<2, 1>(jac) = theta_max_one_ * get<2, 0>(jac);\n  get<1, 2>(jac) =\n      0.5 *\n      (radius_two_ - radius_one_ * cylindrical_endcap_helpers::sin_ax_over_x(\n                                       get<2, 2>(jac), theta_max_one_));\n  get<1, 0>(jac) = 0.5 * radius_one_ *\n                   cylindrical_endcap_helpers::one_over_x_d_sin_ax_over_x(\n                       get<2, 2>(jac), theta_max_one_) *\n                   (1.0 - zbar);\n\n  // Now fill Jacobian values\n  get<0, 0>(jac) = square(xbar) * get<1, 0>(jac) + get<1, 1>(jac);\n  get<1, 1>(jac) = square(ybar) * get<1, 0>(jac) + get<1, 1>(jac);\n  get<0, 1>(jac) = xbar * ybar * get<1, 0>(jac);\n  get<1, 0>(jac) = get<0, 1>(jac);\n  get<2, 0>(jac) = -xbar * get<2, 1>(jac);\n  get<2, 1>(jac) = -ybar * get<2, 1>(jac);\n  get<0, 2>(jac) =\n      xbar * get<1, 2>(jac) + 0.5 * (center_two_[0] - center_one_[0]);\n  get<1, 2>(jac) =\n      ybar * get<1, 2>(jac) + 0.5 * (center_two_[1] - center_one_[1]);\n  get<2, 2>(jac) = 0.5 * (center_two_[2] - center_one_[2] -\n                          radius_one_ * cos(theta_max_one_ * get<2, 2>(jac)));\n\n  return jac;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nUniformCylindricalFlatEndcap::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  return determinant_and_inverse(jacobian(source_coords)).second;\n}\n\nvoid UniformCylindricalFlatEndcap::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | center_one_;\n    p | center_two_;\n    p | radius_one_;\n    p | radius_two_;\n    p | z_plane_one_;\n    p | theta_max_one_;\n  }\n}\n\nbool operator==(const UniformCylindricalFlatEndcap& lhs,\n                const UniformCylindricalFlatEndcap& rhs) {\n  // don't need to compare theta_max_one_\n  // because it is uniquely determined from the other variables.\n  return lhs.center_one_ == rhs.center_one_ and\n         lhs.radius_one_ == rhs.radius_one_ and\n         lhs.z_plane_one_ == rhs.z_plane_one_ and\n         lhs.center_two_ == rhs.center_two_ and\n         lhs.radius_two_ == rhs.radius_two_;\n}\n\nbool operator!=(const UniformCylindricalFlatEndcap& lhs,\n                const UniformCylindricalFlatEndcap& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  UniformCylindricalFlatEndcap::operator()(                                  \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  UniformCylindricalFlatEndcap::jacobian(                                    \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  UniformCylindricalFlatEndcap::inv_jacobian(                                \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/UniformCylindricalFlatEndcap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class UniformCylindricalFlatEndcap.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from 3D unit right cylinder to a 3D volume that connects\n *  a portion of a spherical surface with a disk.\n *\n * \\image html UniCylFlatEndcap.svg \"A cylinder maps to the shaded region.\"\n *\n * \\details Consider a sphere with center \\f$C_1\\f$ and radius \\f$R_1\\f$,\n * and a disk in the \\f$xy\\f$\n * plane with center \\f$C_2\\f$ and radius \\f$R_2\\f$.\n * Let sphere 1 be intersected by a plane normal to the \\f$z\\f$ axis\n * and located at \\f$z = z_{\\mathrm{P}1}\\f$,\n *\n * UniformCylindricalFlatEndcap maps a 3D unit right cylinder (with coordinates\n * \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that \\f$-1\\leq\\bar{z}\\leq 1\\f$\n * and \\f$\\bar{x}^2+\\bar{y}^2 \\leq 1\\f$) to the shaded area\n * in the figure above (with coordinates \\f$(x,y,z)\\f$).  The \"bottom\"\n * of the cylinder \\f$\\bar{z}=-1\\f$ is mapped to the portion of sphere\n * 1 that has \\f$z \\geq z_{\\mathrm{P}1}\\f$, and on this portion of the\n * sphere the angular coordinate \\f$\\theta_1 = \\acos((z-C_1^z)/R_1)\\f$\n * is uniform in \\f$\\bar{\\rho} = \\sqrt{\\bar{x}^2+\\bar{y}^2}\\f$ and the angular\n * coordinate \\f$\\phi_1 = \\atan((y-C_1^y)/(x-C_1^x))\\f$ is the same as\n * \\f$\\phi = \\atan(\\bar{y}/\\bar{x})\\f$.\n * The \"top\" of the cylinder\n * \\f$\\bar{z}=+1\\f$ is mapped to the disk,\n * and the radial polar coordinate coordinate \\f$\\sqrt{x^2+y^2}\\f$\n * on this disk is equal to \\f$R_2\\bar\\rho\\f$ and the angular\n * coordinate \\f$\\phi_2 = \\atan((y-C_2^y)/(x-C_2^x))\\f$ is the same as\n * \\f$\\phi\\f$.\n *\n * UniformCylindricalFlatEndcap is intended to be composed with\n * `Wedge<2>` maps to construct a portion of a cylindrical domain for\n * a binary system.\n *\n * UniformCylindricalFlatEndcap can be used to construct a domain that\n * is similar to, but not identical to, the one described briefly in\n * the Appendix of \\cite Buchman:2012dw.  UniformCylindricalFlatEndcap\n * is used to construct the Blocks analogous to those labeled 'MA\n * wedge' and 'MB wedge' in Figure 20 of that paper.\n *\n * UniformCylindricalFlatEndcap provides the following functions:\n *\n * ## operator()\n *\n * `operator()` maps \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ to \\f$(x,y,z)\\f$\n * according to\n *\n * \\f{align}\n * x &= C_1^x+\\lambda(C_2^x-C_1^x) +\n *        \\cos\\phi\\left(R_1\\sin\\theta_1 +\n *        \\lambda(R_2\\bar\\rho-R_1\\sin\\theta_1)\\right), \\label{eq:x0} \\\\\n * y &= C_1^y+\\lambda(C_2^y-C_1^y) +\n *        \\sin\\phi\\left(R_1\\sin\\theta_1 +\n *        \\lambda(R_2\\bar\\rho-R_1\\sin\\theta_1)\\right), \\label{eq:x1} \\\\\n * z &= C_1^z+\\lambda(C_2^z-C_1^z) +\n *        (1-\\lambda)R_1\\cos\\theta_1 \\label{eq:x2}.\n * \\f}\n *\n * Here\n * \\f{align}\n * \\lambda  &= \\frac{\\bar{z}+1}{2},\\label{eq:lambdafromzbar}\\\\\n * \\theta_1 &= \\bar{\\rho} \\theta_{1 \\mathrm{max}},\\label{eq:deftheta1}\\\\\n * \\phi     &= \\atan(\\bar{y}/\\bar{x})\\label{eq:defphi},\n * \\f}\n * where \\f$\\theta_{1 \\mathrm{max}}\\f$\n * is defined by\n * \\f{align}\n *   \\cos(\\theta_{1\\mathrm{max}}) &= (z_{\\mathrm{P}1}-C_1^z)/R_1,\\\\\n * \\f}\n * and\n * \\f{align}\n * \\bar{\\rho} &= \\sqrt{\\bar{x}^2+\\bar{y}^2}/\\bar{R} \\label{eq:defrhobar},\n * \\f}\n * where \\f$\\bar{R}\\f$ is the radius of the cylinder in barred\n * coordinates, which is always unity.\n *\n * ## inverse\n *\n * Given \\f$(x,y,z)\\f$ we want to find \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$.\n * From Eq. (\\f$\\ref{eq:x2}\\f$) we can write \\f$\\lambda\\f$ as a function\n * of \\f$\\bar\\rho\\f$:\n *\n * \\f{align}\n * \\lambda &= \\frac{z - C_1^z - R_1\\cos\\theta_1}\n *                 {C_2^z-C_1^z - R_1\\cos\\theta_1}\n *                 \\label{eq:lambda_from_rho}.\n * \\f}\n *\n * Then by eliminating \\f$\\phi\\f$ from Eqs. (\\f$\\ref{eq:x0}\\f$) and\n * (\\f$\\ref{eq:x1}\\f$) we find that \\f$\\bar{\\rho}\\f$ is the solution\n * of \\f$Q(\\bar{\\rho})=0\\f$, where\n *\n * \\f{align}\n * Q(\\bar{\\rho}) &= \\left(x-C_1^x-\\lambda(C_2^x-C_1^x)\\right)^2+\n *                  \\left(y-C_1^y-\\lambda(C_2^y-C_1^y)\\right)^2-\n *                  \\left((1-\\lambda)R_1\\sin\\theta_1 +\n *                  \\lambda \\bar\\rho R_2\\right)^2.\\label{eq:defQ}\n * \\f}\n * Here \\f$\\lambda\\f$ and \\f$\\theta_1\\f$, are functions\n * of \\f$\\bar{\\rho}\\f$ through Eqs. (\\f$\\ref{eq:lambda_from_rho}\\f$)\n * and (\\f$\\ref{eq:deftheta1}\\f$).\n *\n * We solve \\f$Q(\\bar{\\rho})=0\\f$ numerically; it is a one-dimensional\n * root-finding problem.\n *\n * Once we have determined \\f$\\bar{\\rho}\\f$, we then obtain \\f$\\lambda\\f$\n * from Eq. (\\f$\\ref{eq:lambda_from_rho}\\f$), and we obtain \\f$\\phi\\f$ from\n *\n * \\f{align}\n *  \\tan\\phi &=\n *  \\frac{y-C_1^y-\\lambda(C_2^y-C_1^y)}{x-C_1^x-\\lambda(C_2^x-C_1^x)}.\n * \\f}\n *\n * Then \\f$\\bar{z}\\f$ is obtained from Eq. (\\f$\\ref{eq:lambdafromzbar}\\f$)\n * and \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$ are obtained from\n *\n * \\f{align}\n *   \\bar{x} &= \\bar{\\rho}\\bar{R}\\cos\\phi,\\\\\n *   \\bar{y} &= \\bar{\\rho}\\bar{R}\\sin\\phi.\n * \\f}\n *\n * ### Considerations when root-finding.\n *\n * We solve \\f$Q(\\bar{\\rho})=0\\f$ numerically for \\f$\\bar{\\rho}\\f$,\n * where \\f$Q(\\bar{\\rho})\\f$ is given by Eq. (\\f$\\ref{eq:defQ}\\f$).\n *\n * #### min/max values of \\f$\\bar{\\rho}\\f$:\n *\n * Note that the root we care about must have\n * \\f$0\\leq\\lambda\\leq 1\\f$; therefore from Eq. (\\f$\\ref{eq:lambda_from_rho}\\f$)\n * we have\n *\n * \\f{align}\n *   \\bar{\\rho}_{\\mathrm{min}} &=\n *      \\left\\{\\begin{array}{ll}\n *           0 & \\text{for } z-C_1^z \\geq R_1, \\\\\n *           \\displaystyle \\frac{1}{\\theta_{1 \\mathrm{max}}}\n *           \\cos^{-1}\\left(\\frac{z-C_1^z}{R_1}\\right) & \\text{otherwise}\n *      \\end{array}\\right.\\label{eq:rhobarmin}\\\\\n *   \\bar{\\rho}_{\\mathrm{max}} &= 1. \\label{eq:rhobarmax}\n * \\f}\n *\n * so we look for a root only between \\f$\\bar{\\rho}_{\\mathrm{min}}\\f$\n * and \\f$\\bar{\\rho}_{\\mathrm{max}}\\f$.\n *\n * #### Roots within roundoff of endpoints:\n *\n * Sometimes a root is within roundoff of \\f$\\bar{\\rho}_{\\mathrm{min}}\\f$\n * This tends to happen at points on the\n * boundary of the mapped region. In this case, the root might\n * not be bracketed by\n * \\f$[\\bar{\\rho}_{\\mathrm{min}},\\bar{\\rho}_{\\mathrm{max}}]\\f$ if the root\n * is slightly outside that interval.  If we find that\n * \\f$Q(\\bar{\\rho}_{\\mathrm{min}})\\f$ is near zero but has the wrong sign,\n * then we slightly expand the interval as follows:\n *\n * \\f{align}\n *    \\bar{\\rho}_{\\mathrm{min}} \\to \\bar{\\rho}_{\\mathrm{min}}\n *  - 2 \\frac{Q(\\bar{\\rho}_{\\mathrm{min}})}{Q'(\\bar{\\rho}_{\\mathrm{min}})},\n * \\f}\n *\n * where \\f$Q'(\\bar{\\rho}_{\\mathrm{min}})\\f$ is the derivative of the function\n * in Eq. (\\f$\\ref{eq:defQ}\\f$). Note that without the factor of 2, this is\n * a Newton-Raphson step; the factor of 2 is there to overcompensate so that\n * the new \\f$\\bar{\\rho}_{\\mathrm{min}}\\f$ brackets the root.  Sometimes, if\n * the derivative is large enough so that the correction above amounts to\n * a value less than roundoff; in that case, we increase the correction to\n * a value larger than roundoff.\n *\n * Note that by differentiating Eqs. (\\f$\\ref{eq:defQ}\\f$) and\n * (\\f$\\ref{eq:lambda_from_rho}\\f$), one obtains\n *\n * \\f{align}\n * Q'(\\bar{\\rho}) =& -2 \\frac{d\\lambda}{d\\bar{\\rho}}\\left[\n *      \\left(x-C_1^x-\\lambda(C_2^x-C_1^x)\\right)(C_2^x-C_1^x)+\n *      \\left(y-C_1^y-\\lambda(C_2^y-C_1^y)\\right)(C_2^y-C_1^y)\n *      \\right]\\nonumber \\\\\n *     &\n *    -2 \\left((1-\\lambda)R_1\\sin\\theta_1 +\n *                \\lambda \\bar\\rho R_2\\right)\n *    \\left[\n *    \\frac{d\\lambda}{d\\bar{\\rho}} (R_2\\bar\\rho - R_1\\sin\\theta_1)\n *    +(1-\\lambda)R_1\\theta_{1 \\mathrm{max}}\\cos\\theta_1\n *    +\\lambda R_2\n *    \\right], \\label{eq:defQderiv}\n * \\f}\n *\n * where\n * \\f{align}\n *  \\frac{d\\lambda}{d\\bar{\\rho}} &=\n *  \\frac{(1-\\lambda)R_1\\theta_{1 \\mathrm{max}}\\sin\\theta_1}\n *       {C_2^z-C_1^z -R_1\\cos\\theta_1}\n *  \\label{eq:dlambda_drhobar}.\n * \\f}\n *\n * #### Roots within roundoff of \\f$\\bar{\\rho}=0\\f$ or \\f$\\bar{\\rho}=1\\f$:\n *\n * For some points on the boundary of the mapped domain, the root will\n * be within roundoff of \\f$\\bar{\\rho}=0\\f$ or \\f$\\bar{\\rho}=1\\f$.\n * Here it does not always make sense to expand the range of the map\n * if the root fails (by roundoff) to be bracketed, as is done above.\n * Furthermore, when \\f$\\bar{\\rho}=0\\f$ is a root it turns\n * out that both \\f$Q(\\bar{\\rho})=0\\f$ and \\f$Q'(\\bar{\\rho})=0\\f$ for\n * \\f$\\bar{\\rho}=0\\f$, so some root-finders (e.g. Newton-Raphson) have\n * difficulty converging.  Therefore the cases where the root is\n * within roundoff of \\f$\\bar{\\rho}=0\\f$ or \\f$\\bar{\\rho}=1\\f$ are\n * treated separately.\n *\n * These cases are detected by comparing terms in the first-order\n * power series of \\f$Q(\\bar{\\rho})=0\\f$ when expanded about\n * \\f$\\bar{\\rho}=0\\f$ or \\f$\\bar{\\rho}=1\\f$.  When one of these cases is\n * recognized, the root is returned as either \\f$\\bar{\\rho}=0\\f$ or\n * \\f$\\bar{\\rho}=1\\f$.\n *\n * ### Quick rejection of points out of range of the map.\n *\n * It is expected that `inverse()` will often be passed points\n * \\f$(x,y,z)\\f$ that are out of the range of the map; in this case\n * `inverse()` returns a `std::nullopt`. To avoid the difficulty and\n * expense of attempting to solve \\f$Q(\\bar{\\rho})=0\\f$ numerically\n * for such points (and then having this solution fail), it is useful\n * to quickly reject points \\f$(x,y,z)\\f$ that are outside the range\n * of the map.\n *\n * Any point in the range of the map must be below the disk\n * and it must be outside or on sphere 1, so the inverse map\n * can immediately return a `std::nullopt` for a point that does not\n * satisfy these conditions.\n *\n * Likewise, the inverse map can immediately reject any point with\n * \\f$z < z_{\\mathrm{P}1}\\f$.\n *\n * Finally, consider the circle \\f$S_1\\f$ defining the intersection of sphere 1\n * and the plane \\f$z = z_{\\mathrm{P}1}\\f$; this circle has radius\n * \\f$r_1 = R_1 \\sin\\theta_{1\\mathrm{max}}\\f$.\n * Now consider the cone that passes through both \\f$S_1\\f$ and\n * the circle \\f$S_2\\f$ bounding the upper disk.\n * A point in the range of the map must be inside\n * or on this cone. The cone can be defined parametrically as\n *\n * \\f{align}\n * x_c &= C_1^x + \\tilde{\\lambda}(C_2^x-C_1^x) +\n *      \\cos\\varphi (r_1 + \\tilde{\\lambda} (R_2 -r_1)),\\\\\n * y_c &= C_1^y + \\tilde{\\lambda}(C_2^y-C_1^y),+\n *      \\sin\\varphi (r_1 + \\tilde{\\lambda} (R_2 -r_1)),\\\\\n * z_c &= C_1^z + R_1 \\cos\\theta_{1\\mathrm{max}} +\n *        \\tilde{\\lambda}(C_2^z -\n *        C_1^z - R_1 \\cos\\theta_{1\\mathrm{max}}),\n * \\f}\n *\n * where \\f$(x_c,y_c,z_c)\\f$ is a point on the cone, and the two\n * parameters defining a point on the cone are the angle \\f$\\varphi\\f$\n * around the cone and the parameter \\f$\\tilde{\\lambda}\\f$, which is\n * defined to be zero on \\f$S_1\\f$ and unity on \\f$S_2\\f$.\n *\n * Given an arbitrary point \\f$(x, y, z)\\f$, we can determine whether\n * or not that point is inside the cone as follows.  First determine\n *\n * \\f{align}\n *  \\tilde{\\lambda} &= \\frac{z - C_1^z - R_1 \\cos\\theta_{1\\mathrm{max}}}\n *   {C_2^z -\n *   C_1^z- R_1 \\cos\\theta_{1\\mathrm{max}}}, \\\\\n *  \\tilde{x} &= x - C_1^x - \\tilde{\\lambda} (C_2^x-C_1^x),\\\\\n *  \\tilde{y} &= y - C_1^y - \\tilde{\\lambda} (C_2^y-C_1^y).\\\\\n * \\f}\n *\n * Then the condition for the point to be inside or on the cone is\n * \\f{align}\n * \\sqrt{\\tilde{x}^2+\\tilde{y}^2} \\le r_1 + (R_2-r_1)\\tilde{\\lambda}.\n * \\f}\n *\n * The inverse map can therefore reject any points that do\n * not satisfy this criterion.\n *\n * ## jacobian\n *\n * One can rewrite Eqs.(\\f$\\ref{eq:x0}\\f$) through (\\f$\\ref{eq:x2}\\f$) as\n *\n * \\f{align}\n * x &= \\frac{1}{2}\\left((1-\\bar{z})C_1^x+ (1+\\bar{z})C_2^x\\right) +\n *        \\frac{\\bar{x}}{2}\\left(\n * (1-\\bar{z}) R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n * (1+\\bar{z}) R_2\n *     \\right), \\label{eq:x0alt} \\\\\n * y &= \\frac{1}{2}\\left((1-\\bar{z})C_1^y + (1+\\bar{z})C_2^y\\right) +\n *        \\frac{\\bar{y}}{2}\\left(\n * (1-\\bar{z})R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n * (1+\\bar{z})R_2\n *     \\right), \\label{eq:x1alt} \\\\\n * z &= \\frac{1}{2}\\left((1-\\bar{z})C_1^z + (1+\\bar{z})C_2^z\\right) +\n * \\frac{1}{2} (1-\\bar{z})R_1 \\cos\\theta_1, \\label{eq:x2alt} \\\\\n * \\f}\n *\n * where we have used Eq. (\\f$\\ref{eq:lambdafromzbar}\\f$) to eliminate\n * \\f$\\lambda\\f$ in favor of \\f$\\bar{z}\\f$, and where we have defined the\n * function\n *\n * \\f{align}\n *   S(\\bar{\\rho},a) = \\frac{\\sin(\\bar{\\rho} a)}{\\bar{\\rho}}. \\label{eq:Sdef}\n * \\f}\n *\n * Note that \\f$S(\\bar{\\rho},a)\\f$ is finite as \\f$\\bar{\\rho}\\f$\n * approaches zero, and in the code we must take care that everything\n * remains well-behaved in that limit.\n *\n * Then differentiating Eqs. (\\f$\\ref{eq:x0alt}\\f$) and (\\f$\\ref{eq:x1alt}\\f$)\n * with respect to \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$, taking into account the\n * dependence of \\f$\\bar{\\rho}\\f$ on \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$ from Eq.\n * (\\f$\\ref{eq:defrhobar}\\f$), we find:\n *\n * \\f{align}\n * \\frac{\\partial x^0}{\\partial \\bar{x}} &=\n * \\frac{1}{2}\\left(\n *       (1-\\bar{z}) R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *       (1+\\bar{z}) R_2\n *       \\right) +\n * \\frac{\\bar{x}^2}{2\\bar{\\rho}}\n *           (1-\\bar{z}) R_1 S'(\\bar{\\rho},\\theta_{1 \\mathrm{max}}), \\\\\n * \\frac{\\partial x^1}{\\partial \\bar{y}} &=\n * \\frac{1}{2}\\left(\n *       (1-\\bar{z}) R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}) +\n *       (1+\\bar{z}) R_2\n *       \\right) +\n * \\frac{\\bar{y}^2}{2\\bar{\\rho}}\n *           (1-\\bar{z}) R_1 S'(\\bar{\\rho},\\theta_{1 \\mathrm{max}}), \\\\\n * \\frac{\\partial x^0}{\\partial \\bar{y}} &=\n * \\frac{\\bar{x}\\bar{y}}{2\\bar{\\rho}}\n *           (1-\\bar{z}) R_1 S'(\\bar{\\rho},\\theta_{1 \\mathrm{max}}), \\\\\n * \\frac{\\partial x^1}{\\partial \\bar{x}} &=\n * \\frac{\\partial x^0}{\\partial \\bar{y}},\n * \\f}\n *\n * where \\f$S'(\\bar{\\rho},a)\\f$ means the derivative of \\f$S(\\bar{\\rho},a)\\f$\n * with respect to \\f$\\bar\\rho\\f$.  Note that \\f$S'(\\bar{\\rho},a)/\\bar{\\rho}\\f$\n * approaches a constant value as \\f$\\bar{\\rho}\\f$ approaches zero.\n *\n * Differentiating Eq. (\\f$\\ref{eq:x2alt}\\f$) with respect to\n * \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$ we find\n *\n * \\f{align}\n * \\frac{\\partial z}{\\partial \\bar{x}} &=\n * - \\frac{\\bar{x}}{2}\n *        (1-\\bar{z}) R_1 \\theta_{1 \\mathrm{max}}\n *                S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}),\\\\\n * \\frac{\\partial z}{\\partial \\bar{y}} &=\n * - \\frac{\\bar{y}}{2}\n *        (1-\\bar{z}) R_1 \\theta_{1 \\mathrm{max}}\n *                S(\\bar{\\rho},\\theta_{1 \\mathrm{max}}).\n * \\f}\n *\n * Differentiating Eqs. (\\f$\\ref{eq:x0alt}\\f$) through (\\f$\\ref{eq:x2alt}\\f$)\n * with respect to \\f$\\bar{z}\\f$ yields\n *\n * \\f{align}\n * \\frac{\\partial x}{\\partial \\bar{z}} &=\n * \\frac{1}{2}\\left[\n *  C_2^x-C_1^x +\n *        \\bar{x}\\left(R_2 -\n *        R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}})\\right)\n * \\right],\\\\\n * \\frac{\\partial y}{\\partial \\bar{z}} &=\n * \\frac{1}{2}\\left[\n *  C_2^y-C_1^y +\n *        \\bar{y}\\left(R_2 -\n *        R_1 S(\\bar{\\rho},\\theta_{1 \\mathrm{max}})\\right)\n * \\right],\\\\\n * \\frac{\\partial z}{\\partial \\bar{z}} &=\n * \\frac{1}{2}\\left(\n *  C_2^z-C_1^z - R_1\\cos\\theta_1\n * \\right).\n * \\f}\n *\n * ## inv_jacobian\n *\n * The inverse Jacobian is computed by numerically inverting the\n * Jacobian.\n *\n * ## Restrictions on map parameters\n *\n * We demand that \\f$C^2_1 + 1.05 R_1 \\leq C^2_2 \\leq C^2_1 + 5 R_1\\f$.\n * It is possible to construct a valid\n * map without this assumption, but the assumption simplifies the\n * code, and the expected use cases obey this restriction.\n *\n * We also demand that the z plane in the above figure lies\n * above the center of the sphere and is not too close to the center\n * or edge of the sphere; specifically, we demand that\n * - \\f$ 0.075\\pi < \\theta_{1 \\mathrm{max}} < 0.35\\pi\\f$\n *\n * Here 0.075 and 0.35 are safety factors. These restrictions are not\n * strictly necessary but are made for simplicity and to ensure the\n * accuracy of the inverse map (the inverse map becomes less accurate if\n * the map parameters are extreme).\n *\n * Consider the line segment \\f$L\\f$ that connects a point on the\n * circle \\f$S_1\\f$ (the circle formed by the intersection of sphere 1\n * and the plane \\f$z=z_{\\mathrm{P}1}\\f$) with the center of the\n * circle \\f$S_1\\f$.  Consider another line segment \\f$L'\\f$ that\n * connects the same point on the circle \\f$S_1\\f$ with the\n * corresponding point on the circle \\f$S_2\\f$ (the circle bounding\n * the disk with center \\f$C_2\\f$ and radius \\f$R_2\\f$).\n * Now consider the angle between \\f$L\\f$\n * and \\f$L'\\f$, as measured from the interior of sphere 1, and Let\n * \\f$\\alpha\\f$ be the minimum value of this angle over the circle.\n * \\f$\\alpha\\f$ is shown in the figure above. If\n * \\f$\\alpha < \\theta_{1 \\mathrm{max}}\\f$, then the line segment \\f$L'\\f$\n * intersects the mapped portion of sphere 1 twice, so the map is\n * multi-valued. Therefore we demand that the map parameters are such that\n * - \\f$\\alpha > 1.1 \\theta_{1 \\mathrm{max}}\\f$\n *\n * where 1.1 is a safety factor.\n *\n * The condition on \\f$\\alpha\\f$ is guaranteed to provide an\n * invertible map if \\f$C_1^x=C_2^x\\f$ and \\f$C_1^y=C_2^y\\f$.\n * However, for \\f$C_1^x \\neq C_2^x\\f$ or \\f$C_1^y\\neq C_2^y\\f$, even\n * if the \\f$\\alpha\\f$ condition is satisfied, it is possible for two\n * lines of constant \\f$(\\bar{x},\\bar{y})\\f$ (each line has different\n * values of \\f$(\\bar{x},\\bar{y})\\f$) to pass through the same point\n * \\f$(x,y,z)\\f$ if those lines are not coplanar.  This condition is\n * difficult to check analytically, so we check it numerically.  We\n * have found empirically that if \\f$Q(\\bar{\\rho})\\f$ from\n * Eq. (\\f$\\ref{eq:defQ}\\f$) has only a single root between\n * \\f$\\bar{\\rho}_{\\mathrm{min}}\\f$ and \\f$\\bar{\\rho}_{\\mathrm{max}}\\f$\n * for all points \\f$(x,y,z)\\f$ on the surface of sphere 1 with\n * \\f$z\\geq z_{\\mathrm{P}1}\\f$ and with \\f$(x-C_1^x)/(y-C_1^y) =\n * (C_1^x-C_2^x)/(C_1^y-C_2^y)\\f$, then the map is single-valued\n * everywhere.  We cannot numerically check every point in this\n * one-parameter family of points, but we demand that this condition\n * is satisfied for a reasonably large number of points (currently 1000)\n * in this family.  This check is not very expensive since it is done only\n * once, in the constructor.\n *\n */\nclass UniformCylindricalFlatEndcap {\n public:\n  static constexpr size_t dim = 3;\n  UniformCylindricalFlatEndcap(const std::array<double, 3>& center_one,\n                               const std::array<double, 3>& center_two,\n                               double radius_one, double radius_two,\n                               double z_plane_one);\n  UniformCylindricalFlatEndcap() = default;\n  ~UniformCylindricalFlatEndcap() = default;\n  UniformCylindricalFlatEndcap(UniformCylindricalFlatEndcap&&) = default;\n  UniformCylindricalFlatEndcap(const UniformCylindricalFlatEndcap&) = default;\n  UniformCylindricalFlatEndcap& operator=(const UniformCylindricalFlatEndcap&) =\n      default;\n  UniformCylindricalFlatEndcap& operator=(UniformCylindricalFlatEndcap&&) =\n      default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // clang-tidy: google runtime references\n  void pup(PUP::er& p);  // NOLINT\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const UniformCylindricalFlatEndcap& lhs,\n                         const UniformCylindricalFlatEndcap& rhs);\n  std::array<double, 3> center_one_{};\n  std::array<double, 3> center_two_{};\n  double radius_one_{std::numeric_limits<double>::signaling_NaN()};\n  double radius_two_{std::numeric_limits<double>::signaling_NaN()};\n  double z_plane_one_{std::numeric_limits<double>::signaling_NaN()};\n  double theta_max_one_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const UniformCylindricalFlatEndcap& lhs,\n                const UniformCylindricalFlatEndcap& rhs);\n\n/// Given parameters for UniformCylindricalFlatEndcap, returns whether\n/// the map is invertible for target points on sphere_one.\n///\n/// `is_uniform_cylindrical_flat_endcap_invertible_on_sphere_one` is\n/// publicly visible because it is useful for unit tests that need to\n/// choose valid parameters to pass into the map.\nbool is_uniform_cylindrical_flat_endcap_invertible_on_sphere_one(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, const double radius_one,\n    const double radius_two, const double theta_max_one);\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/UniformCylindricalSide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/UniformCylindricalSide.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <sstream>\n#include <utility>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nnamespace {\n// FunctionEvalTempVariables exists to remove code duplication between\n// function_to_zero and deriv_function_to_zero.\nstruct FunctionEvalTempVariables {\n  FunctionEvalTempVariables(double lambda,\n                            const std::array<double, 3>& target_coords,\n                            const std::array<double, 3>& center_one,\n                            const std::array<double, 3>& center_two,\n                            double radius_one, double radius_two,\n                            double z_plane_minus_one, double z_plane_minus_two,\n                            double z_plane_plus_one, double z_plane_plus_two);\n  double one_plus_zbar_over_two;\n  double r_one_cos_theta_one;\n  double r_two_cos_theta_two;\n  double r_one_sin_theta_one;\n  double r_two_sin_theta_two;\n};\n\nFunctionEvalTempVariables::FunctionEvalTempVariables(\n    const double lambda, const std::array<double, 3>& target_coords,\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, const double radius_one,\n    const double radius_two, const double z_plane_minus_one,\n    const double z_plane_minus_two, const double z_plane_plus_one,\n    const double z_plane_plus_two)\n    : one_plus_zbar_over_two(\n          (target_coords[2] + lambda * (z_plane_minus_one - z_plane_minus_two) -\n           z_plane_minus_one) /\n          ((1.0 - lambda) * (z_plane_plus_one - z_plane_minus_one) +\n           lambda * (z_plane_plus_two - z_plane_minus_two))),\n      r_one_cos_theta_one(z_plane_minus_one - center_one[2] +\n                          (z_plane_plus_one - z_plane_minus_one) *\n                              one_plus_zbar_over_two),\n      r_two_cos_theta_two(z_plane_minus_two - center_two[2] +\n                          (z_plane_plus_two - z_plane_minus_two) *\n                              one_plus_zbar_over_two),\n      r_one_sin_theta_one(\n          sqrt(square(radius_one) - square(r_one_cos_theta_one))),\n      r_two_sin_theta_two(\n          sqrt(square(radius_two) - square(r_two_cos_theta_two))) {}\n}  // namespace\n\nUniformCylindricalSide::UniformCylindricalSide(\n    const std::array<double, 3>& center_one,\n    const std::array<double, 3>& center_two, double radius_one,\n    double radius_two, double z_plane_plus_one, double z_plane_minus_one,\n    double z_plane_plus_two, double z_plane_minus_two)\n    : center_one_(center_one),\n      center_two_(center_two),\n      radius_one_([&radius_one]() {\n        // The equal_within_roundoff below has an implicit scale of 1,\n        // so the ASSERT may trigger in the case where we really\n        // want an entire domain that is very small.\n        ASSERT(not equal_within_roundoff(radius_one, 0.0),\n               \"Cannot have zero radius_one\");\n        ASSERT(radius_one > 0.0, \"Cannot have negative radius_one\");\n        return radius_one;\n                  }()), // LCOV_EXCL_LINE\n      radius_two_([&radius_two]() {\n        // The equal_within_roundoff below has an implicit scale of 1,\n        // so the ASSERT may trigger in the case where we really\n        // want an entire domain that is very small.\n        ASSERT(not equal_within_roundoff(radius_two, 0.0),\n               \"Cannot have zero radius_two\");\n        ASSERT(radius_two > 0.0, \"Cannot have negative radius_two\");\n        return radius_two;\n      }()), // LCOV_EXCL_LINE\n      z_plane_plus_one_(z_plane_plus_one),\n      z_plane_minus_one_(z_plane_minus_one),\n      z_plane_plus_two_(z_plane_plus_two),\n      z_plane_minus_two_(z_plane_minus_two) {\n  // Assumptions made in the map.  Some of these can be relaxed,\n  // as long as the unit test is changed to test them.\n  // The ASSERTS here match the ones in UniformCylindricalEndcap.\n  ASSERT(radius_one >= 0.08 * radius_two,\n         \"Radius_one = \" << radius_one << \" must be >= 0.08 * radius_two =\"\n                         << 0.08 * radius_two);\n  ASSERT(z_plane_plus_two == z_plane_plus_one or\n             z_plane_plus_two >= z_plane_plus_one + 0.03 * radius_two,\n         \"z_plane_plus_two must be >= z_plane_plus_one \"\n         \"+ 0.03 * radius_two, or exactly equal to z_plane_plus_one, not \"\n             << z_plane_plus_two << \" \" << z_plane_plus_one << \" \"\n             << z_plane_plus_one + 0.03 * radius_two);\n  ASSERT(z_plane_minus_two == z_plane_minus_one or\n             z_plane_minus_two <= z_plane_minus_one - 0.03 * radius_two,\n         \"z_plane_minus_two must be >= z_plane_minus_one \"\n         \"- 0.03 * radius_two, or exactly equal to z_plane_minus_one, but \"\n         \"z_plane_minus_two = \"\n             << z_plane_minus_two\n             << \", z_plane_minus_one = \" << z_plane_minus_one\n             << \", and z_plane_minus_one - 0.03 * radius_two = \"\n             << z_plane_minus_one - 0.03 * radius_two);\n  ASSERT(z_plane_minus_two != z_plane_minus_one or\n             z_plane_plus_two != z_plane_plus_one,\n         \"Not tested if both the positive z-planes and the negative z-planes \"\n         \"are equal\");\n\n  // The code below defines several variables that are used only in ASSERTs.\n  // We put that code in a #ifdef SPECTRE_DEBUG to avoid clang-tidy complaining\n  // about unused variables in release mode.\n#ifdef SPECTRE_DEBUG\n  const double cos_theta_min_one =\n      (z_plane_plus_one - center_one[2]) / radius_one;\n  const double cos_theta_max_one =\n      (z_plane_minus_one - center_one[2]) / radius_one;\n  const double cos_theta_min_two =\n      (z_plane_plus_two - center_two[2]) / radius_two;\n  const double cos_theta_max_two =\n      (z_plane_minus_two - center_two[2]) / radius_two;\n\n  // For some reason, codecov thinks that the following lambda never\n  // gets called, even though it is used in all of the ASSERT messages\n  // below.\n  // LCOV_EXCL_START\n  const auto param_string = [this]() -> std::string {\n    std::ostringstream buffer;\n    buffer << \"\\nParameters to UniformCylindricalSide:\\nradius_one=\"\n           << radius_one_ << \"\\nradius_two=\" << radius_two_\n           << \"\\ncenter_one=\" << center_one_ << \"\\ncenter_two=\" << center_two_\n           << \"\\nz_plane_plus_one=\" << z_plane_plus_one_\n           << \"\\nz_plane_plus_two=\" << z_plane_plus_two_\n           << \"\\nz_plane_minus_one=\" << z_plane_minus_one_\n           << \"\\nz_plane_minus_two=\" << z_plane_minus_two_;\n    return buffer.str();\n  };\n  // LCOV_EXCL_STOP\n\n  const double cos_theta_min_one_lower_limit =\n      z_plane_plus_one_ == z_plane_plus_two_\n          ? cos(M_PI * 0.59)\n          : (z_plane_minus_one_ == z_plane_minus_two_ and\n                     cos_theta_max_one > cos(M_PI * 0.6)\n                 ? cos(M_PI * 0.3)\n                 : cos(M_PI * 0.4));\n\n  const double cos_theta_max_one_upper_limit =\n      z_plane_minus_one_ == z_plane_minus_two_\n          ? cos(M_PI * 0.41)\n          : (z_plane_plus_one_ == z_plane_plus_two_ and\n                     cos_theta_min_one < cos(M_PI * 0.4)\n                 ? cos(M_PI * 0.7)\n                 : cos(M_PI * 0.6));\n\n  const double cos_theta_min_two_upper_limit =\n      z_plane_plus_one_ == z_plane_plus_two_ and\n              cos_theta_min_one < cos(M_PI * 0.4)\n          ? cos(M_PI * 0.25)\n          : cos(M_PI * 0.15);\n\n  const double cos_theta_max_two_lower_limit =\n      z_plane_minus_one_ == z_plane_minus_two_ and\n              cos_theta_max_one > cos(M_PI * 0.6)\n          ? cos(M_PI * 0.75)\n          : cos(M_PI * 0.85);\n\n  ASSERT(cos_theta_min_one > cos_theta_min_one_lower_limit,\n         \"z_plane_plus_one is too close to the center of sphere_one: \"\n         \"cos_theta_min_one must be > \"\n             << cos_theta_min_one_lower_limit << \" but is \" << cos_theta_min_one\n             << \" instead.\" << param_string());\n  ASSERT(cos_theta_max_one < cos_theta_max_one_upper_limit,\n         \"z_plane_minus_one is too close to the center of sphere_one: \"\n         \"cos_theta_max_one must be < \"\n             << cos_theta_max_one_upper_limit << \" but is \" << cos_theta_max_one\n             << \" instead.\" << param_string());\n  ASSERT(cos_theta_min_one < cos(M_PI * 0.15),\n         \"z_plane_plus_one is too far from the center of sphere_one: \"\n         \"cos_theta_min_one must be < \"\n             << cos(M_PI * 0.15) << \" but is \" << cos_theta_min_one\n             << \" instead.\" << param_string());\n  ASSERT(cos_theta_max_one > cos(M_PI * 0.85),\n         \"z_plane_minus_one is too far from the center of sphere_one: \"\n         \"cos_theta_max_one must be > \"\n             << cos(M_PI * 0.85) << \" but is \" << cos_theta_max_one\n             << \" instead.\" << param_string());\n  ASSERT(cos_theta_min_two > (z_plane_plus_one_ == z_plane_plus_two_\n                                  ? cos(M_PI * 0.75)\n                                  : cos(M_PI * 0.4)),\n         \"z_plane_plus_two is too close to the south pole: theta/pi=\"\n             << acos(cos_theta_min_two) / M_PI << param_string());\n  ASSERT(cos_theta_max_two < (z_plane_minus_one_ == z_plane_minus_two_\n                                  ? cos(M_PI * 0.25)\n                                  : cos(M_PI * 0.6)),\n         \"z_plane_minus_two is too close to the north pole: theta/pi=\"\n             << acos(cos_theta_max_two) / M_PI << param_string());\n  ASSERT(cos_theta_min_two < cos_theta_min_two_upper_limit,\n         \"z_plane_plus_two is too close to the north pole: theta_min_two/pi=\"\n             << acos(cos_theta_min_two) / M_PI << \" but it should be >\"\n             << acos(cos_theta_min_two_upper_limit) / M_PI << param_string());\n  ASSERT(cos_theta_max_two > cos_theta_max_two_lower_limit,\n         \"z_plane_minus_two is too close to the south pole: theta_max_two/pi=\"\n             << acos(cos_theta_max_two) / M_PI << \" but it should be < \"\n             << acos(cos_theta_max_two_lower_limit) / M_PI << param_string());\n\n  const double dist_spheres = sqrt(square(center_one[0] - center_two[0]) +\n                                   square(center_one[1] - center_two[1]) +\n                                   square(center_one[2] - center_two[2]));\n\n  ASSERT(dist_spheres + radius_one <= 0.98 * radius_two,\n         \"The map has been tested only for the case when \"\n         \"sphere_one is sufficiently contained inside sphere_two, without the \"\n         \"two spheres almost touching. Radius_one = \"\n             << radius_one << \", radius_two = \" << radius_two\n             << \", dist_spheres = \" << dist_spheres\n             << \", (dist_spheres+radius_one)/radius_two=\"\n             << (dist_spheres + radius_one) / radius_two << param_string());\n\n  const double horizontal_dist_spheres =\n      sqrt(square(center_one[0] - center_two[0]) +\n           square(center_one[1] - center_two[1]));\n\n  if (z_plane_plus_two != z_plane_plus_one and\n      z_plane_minus_two != z_plane_minus_one) {\n    ASSERT(center_one[2] - radius_one <= center_two[2] + 0.2 * radius_two and\n               center_one[2] + radius_one >= center_two[2] - 0.2 * radius_two,\n           \"The map has been tested only for the case when \"\n           \"sphere_one is not a tiny thing at the top or bottom of sphere_two. \"\n           \" Radius_one = \"\n               << radius_one << \", radius_two = \" << radius_two\n               << \" center_one[2] = \" << center_one[2]\n               << \" center_two[2] = \" << center_two[2] << param_string());\n  }\n\n  if (z_plane_plus_two == z_plane_plus_one) {\n    ASSERT(z_plane_minus_two <= z_plane_plus_two - 0.18 * radius_two,\n           \"The map has been tested only if the sphere_two planes are far \"\n           \"enough apart. z_plane_plus_two=\"\n               << z_plane_plus_two << \" z_plane_minus_two=\" << z_plane_minus_two\n               << \" radius_two=\" << radius_two << param_string());\n  }\n\n  if (z_plane_minus_two == z_plane_minus_one) {\n    ASSERT(z_plane_plus_two >= z_plane_minus_two + 0.18 * radius_two,\n           \"The map has been tested only if the sphere_two planes are far \"\n           \"enough apart. z_plane_plus_two=\"\n               << z_plane_plus_two << \" z_plane_minus_two=\" << z_plane_minus_two\n               << \" radius_two=\" << radius_two << param_string());\n  }\n\n  ASSERT(horizontal_dist_spheres <=\n             (z_plane_plus_two == z_plane_plus_one or\n                      z_plane_minus_two == z_plane_minus_one\n                  ? 0.0\n                  : std::min(radius_one,\n                             std::max(0.0, 0.95 * radius_two - radius_one))),\n         \"The map has been tested only for the case when \"\n         \"sphere_one intersects the polar axis of sphere_two and is not \"\n         \"too far from the edge of sphere_two (or in the \"\n         \"case of equal plus or minus z_planes, when the two spheres have the \"\n         \"same x and y centers).\"\n         \" Radius_one = \"\n             << radius_one << \", radius_two = \" << radius_two\n             << \", dist_spheres = \" << dist_spheres\n             << \", horizontal_dist_spheres = \" << horizontal_dist_spheres\n             << param_string());\n\n  const double theta_min_one = acos(cos_theta_min_one);\n  const double theta_max_one = acos(cos_theta_max_one);\n  const double theta_min_two = acos(cos_theta_min_two);\n  const double theta_max_two = acos(cos_theta_max_two);\n\n  // max_horizontal_dist_between_circles can be either sign;\n  // therefore alpha can be in either the first or second quadrant.\n  const double max_horizontal_dist_between_circles_plus =\n      horizontal_dist_spheres + radius_one_ * sin(theta_min_one) -\n      radius_two_ * sin(theta_min_two);\n  const double max_horizontal_dist_between_circles_minus =\n      horizontal_dist_spheres + radius_one_ * sin(theta_max_one) -\n      radius_two_ * sin(theta_max_two);\n\n  const double alpha_plus = atan2(z_plane_plus_two - z_plane_plus_one,\n                                  max_horizontal_dist_between_circles_plus);\n  ASSERT(alpha_plus > 1.1 * theta_min_one and alpha_plus > 1.1 * theta_min_two,\n         \"Angle alpha_plus is too small: alpha_plus = \"\n             << alpha_plus << \", theta_min_one = \" << theta_min_one\n             << \", theta_min_two = \" << theta_min_two\n             << \", max_horizontal_dist_between_circles_plus = \"\n             << max_horizontal_dist_between_circles_plus\n             << \", horizontal_dist_spheres = \" << horizontal_dist_spheres\n             << param_string());\n\n  const double alpha_minus = atan2(z_plane_minus_one - z_plane_minus_two,\n                                   max_horizontal_dist_between_circles_minus);\n  ASSERT(alpha_minus > 1.1 * (M_PI - theta_max_one) and\n             alpha_minus > 1.1 * (M_PI - theta_max_two),\n         \"Angle alpha_minus is too small: alpha_minus = \"\n             << alpha_minus << \", theta_max_one = \" << theta_max_one\n             << \", theta_max_two = \" << theta_max_two\n             << \", max_horizontal_dist_between_circles_minus = \"\n             << max_horizontal_dist_between_circles_minus\n             << \", horizontal_dist_spheres = \" << horizontal_dist_spheres\n             << param_string());\n#endif\n}\n\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, 3> UniformCylindricalSide::operator()(\n    const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n  std::array<ReturnType, 3> target_coords{};\n  ReturnType& x = target_coords[0];\n  ReturnType& y = target_coords[1];\n  ReturnType& z = target_coords[2];\n\n  // Use y and z as temporary storage to avoid allocations,\n  // before setting them to their actual values.\n  z = sqrt(square(xbar) + square(ybar));  // z is rhobar = lambda+1 in the dox\n  // y is temporarily R1 sin(theta_1)(1-lambda) + R2 sin(theta_2) lambda\n  // Here R1 sin(theta_1) is calculated from R1 cos(theta_1) as\n  // expressed in the dox\n  y = sqrt(square(radius_two_) -\n           square(z_plane_minus_two_ - center_two_[2] +\n                  0.5 * (zbar + 1.0) *\n                      (z_plane_plus_two_ - z_plane_minus_two_))) *\n          (z - 1.0) +\n      sqrt(square(radius_one_) -\n           square(z_plane_minus_one_ - center_one_[2] +\n                  0.5 * (zbar + 1.0) *\n                      (z_plane_plus_one_ - z_plane_minus_one_))) *\n          (2.0 - z);\n  // Now compute actual x,y,z.\n  x = y * xbar / z + center_two_[0] * (z - 1.0) + center_one_[0] * (2.0 - z);\n  y = y * ybar / z + center_two_[1] * (z - 1.0) + center_one_[1] * (2.0 - z);\n  z = (z_plane_minus_two_ +\n       0.5 * (zbar + 1.0) * (z_plane_plus_two_ - z_plane_minus_two_)) *\n          (z - 1.0) +\n      (z_plane_minus_one_ +\n       0.5 * (zbar + 1.0) * (z_plane_plus_one_ - z_plane_minus_one_)) *\n          (2.0 - z);\n  return target_coords;\n}\n\nstd::optional<std::array<double, 3>> UniformCylindricalSide::inverse(\n    const std::array<double, 3>& target_coords) const {\n  // First do some easy checks that target_coords is in the range\n  // of the map.  Need to accept points that are out of range by roundoff.\n\n  // check z <= z_plane_plus_two_\n  if (target_coords[2] > z_plane_plus_two_ and\n      not equal_within_roundoff(target_coords[2], z_plane_plus_two_)) {\n    return std::nullopt;\n  }\n\n  // check z >= z_plane_minus_two_\n  if (target_coords[2] < z_plane_minus_two_ and\n      not equal_within_roundoff(target_coords[2], z_plane_minus_two_)) {\n    return std::nullopt;\n  }\n\n  // check that point is in or on sphere_two\n  const double r_minus_center_two =\n      sqrt(square(target_coords[0] - center_two_[0]) +\n           square(target_coords[1] - center_two_[1]) +\n           square(target_coords[2] - center_two_[2]));\n  if (r_minus_center_two > radius_two_ and\n      not equal_within_roundoff(r_minus_center_two, radius_two_)) {\n    return std::nullopt;\n  }\n\n  // check that point is outside or on sphere_one_\n  const double r_minus_center_one =\n      sqrt(square(target_coords[0] - center_one_[0]) +\n           square(target_coords[1] - center_one_[1]) +\n           square(target_coords[2] - center_one_[2]));\n  if (r_minus_center_one < radius_one_ and\n      not equal_within_roundoff(r_minus_center_one, radius_one_)) {\n    return std::nullopt;\n  }\n\n  // Check if the point is inside the cone\n  auto point_inside_cone =\n      [this, &target_coords](const double z_one, const double z_two) {\n        const double lambda_tilde =\n            (target_coords[2] - z_one) / (z_two - z_one);\n        const double rho_tilde =\n            sqrt(square(target_coords[0] - center_one_[0] -\n                        lambda_tilde * (center_two_[0] - center_one_[0])) +\n                 square(target_coords[1] - center_one_[1] -\n                        lambda_tilde * (center_two_[1] - center_one_[1])));\n        const double circle_radius =\n            sqrt(square(radius_one_) -\n                 square(z_one - center_one_[2])) *\n                (1.0 - lambda_tilde) +\n            sqrt(square(radius_two_) -\n                 square(z_two - center_two_[2])) *\n                lambda_tilde;\n        return rho_tilde < circle_radius and\n               not equal_within_roundoff(\n                   rho_tilde, circle_radius,\n                   std::numeric_limits<double>::epsilon() * 100.0, radius_two_);\n      };\n  if ((target_coords[2] >= z_plane_plus_one_ and\n       z_plane_plus_one_ != z_plane_plus_two_ and\n       point_inside_cone(z_plane_plus_one_, z_plane_plus_two_)) or\n      (target_coords[2] <= z_plane_minus_one_ and\n       z_plane_minus_one_ != z_plane_minus_two_ and\n       point_inside_cone(z_plane_minus_one_, z_plane_minus_two_))) {\n    return std::nullopt;\n  }\n\n  // To find lambda we will do numerical root finding.\n  // function_to_zero is the function that is zero when lambda has the\n  // correct value.\n  const auto function_to_zero = [this, &target_coords](const double lambda) {\n    const FunctionEvalTempVariables tmp(lambda, target_coords, center_one_,\n                                        center_two_, radius_one_, radius_two_,\n                                        z_plane_minus_one_, z_plane_minus_two_,\n                                        z_plane_plus_one_, z_plane_plus_two_);\n    return square(target_coords[0] - center_one_[0] -\n                  lambda * (center_two_[0] - center_one_[0])) +\n           square(target_coords[1] - center_one_[1] -\n                  lambda * (center_two_[1] - center_one_[1])) -\n           square((1.0 - lambda) * tmp.r_one_sin_theta_one +\n                  lambda * tmp.r_two_sin_theta_two);\n  };\n\n  // Derivative with respect to lambda of function_to_zero.\n  const auto deriv_function_to_zero = [this,\n                                       &target_coords](const double lambda) {\n    const FunctionEvalTempVariables tmp(lambda, target_coords, center_one_,\n                                        center_two_, radius_one_, radius_two_,\n                                        z_plane_minus_one_, z_plane_minus_two_,\n                                        z_plane_plus_one_, z_plane_plus_two_);\n    const double d_zbar_d_lambda_over_two =\n        ((1.0 - tmp.one_plus_zbar_over_two) *\n             (z_plane_minus_one_ - z_plane_minus_two_) -\n         tmp.one_plus_zbar_over_two * (z_plane_plus_two_ - z_plane_plus_one_)) /\n        ((1.0 - lambda) * (z_plane_plus_one_ - z_plane_minus_one_) +\n         lambda * (z_plane_plus_two_ - z_plane_minus_two_));\n    const double half_deriv_of_func_to_zero =\n        (center_one_[0] - center_two_[0]) *\n            (target_coords[0] - center_one_[0] -\n             lambda * (center_two_[0] - center_one_[0])) +\n        (center_one_[1] - center_two_[1]) *\n            (target_coords[1] - center_one_[1] -\n             lambda * (center_two_[1] - center_one_[1])) +\n        -(tmp.r_one_sin_theta_one * (1.0 - lambda) +\n          lambda * tmp.r_two_sin_theta_two) *\n            (tmp.r_two_sin_theta_two - tmp.r_one_sin_theta_one -\n             d_zbar_d_lambda_over_two *\n                 (lambda * tmp.r_two_cos_theta_two *\n                      (z_plane_plus_two_ - z_plane_minus_two_) /\n                      tmp.r_two_sin_theta_two +\n                  (1.0 - lambda) * tmp.r_one_cos_theta_one *\n                      (z_plane_plus_one_ - z_plane_minus_one_) /\n                      tmp.r_one_sin_theta_one));\n    return 2.0 * half_deriv_of_func_to_zero;\n  };\n\n  // If we get here, then lambda is not near unity or near zero.\n  // So let's find lambda.\n  double lambda = std::numeric_limits<double>::signaling_NaN();\n\n  // Choose the minimum value of lambda.  Max value is always one.\n  // These are not const because they can change with re-bracketing\n  // below.\n  double lambda_min =\n      std::max({0.0,\n                z_plane_plus_two_ == z_plane_plus_one_\n                    ? 0.0\n                    : (target_coords[2] - z_plane_plus_one_) /\n                          (z_plane_plus_two_ - z_plane_plus_one_),\n                z_plane_minus_two_ == z_plane_minus_one_\n                    ? 0.0\n                    : (z_plane_minus_one_ - target_coords[2]) /\n                          (z_plane_minus_one_ - z_plane_minus_two_)});\n  double lambda_max = 1.0;\n\n  // The root should always be bracketed.  However, if\n  // lambda==lambda_min or lambda==lambda_max, the function may not be\n  // exactly zero because of roundoff and then the root might not be\n  // bracketed.\n  double function_at_lambda_min = function_to_zero(lambda_min);\n  double function_at_lambda_max = function_to_zero(lambda_max);\n\n  if (1.0 - lambda_min <\n             std::numeric_limits<double>::epsilon() * 100.0) {\n    // lambda_min is within roundoff of lambda_max, meaning that the\n    // point must be within roundoff of a point on the outer sphere\n    // where there is only one possible value of lambda.  Here we\n    // treat this case as if lambda is exactly 1.0.\n    lambda = 1.0;\n  } else {\n    // Here we do a root find.\n    if (function_at_lambda_min * function_at_lambda_max > 0.0) {\n      // Root not bracketed.\n      // If the bracketing failure is due to roundoff, then\n      // slightly adjust the bounds to increase the range. Otherwise, error.\n      //\n      // roundoff_ratio is the limiting ratio of the two function values\n      // for which we should attempt to adjust the bounds.  It is ok for\n      // roundoff_ratio to be much larger than actual roundoff, since it\n      // doesn't hurt to expand the interval and try again when\n      // otherwise we would just error.\n      constexpr double roundoff_ratio = 1.e-3;\n      if (abs(function_at_lambda_min) / abs(function_at_lambda_max) <\n          roundoff_ratio) {\n        // Slightly decrease lambda_min.  How far do we decrease it?\n        // Figure that out by looking at the deriv of the function.\n        const double deriv_function_at_lambda_min =\n            deriv_function_to_zero(lambda_min);\n        const double trial_lambda_min_increment =\n            -2.0 * function_at_lambda_min / deriv_function_at_lambda_min;\n        // new_lambda_min = lambda_min + trial_lambda_min_increment would be\n        // a Newton-Raphson step except for the factor of 2 in\n        // trial_lambda_min_increment. The factor of 2 is there to\n        // over-compensate so that the new lambda_min brackets the\n        // root.\n        //\n        // But sometimes the factor of 2 is not enough when\n        // trial_lambda_min_increment is roundoff-small so that the\n        // change in new_lambda_min is zero or only in the last one or\n        // two bits.  If this is the case, then we set\n        // lambda_min_increment to some small roundoff value with the\n        // same sign as trial_lambda_min_increment.\n        // Note that lambda is always between zero and one, so it is\n        // ok to use an absolute epsilon.\n        const double lambda_min_increment =\n            abs(trial_lambda_min_increment) >\n                    1000.0 * std::numeric_limits<double>::epsilon()\n                ? trial_lambda_min_increment\n                : std::copysign(1000.0 * std::numeric_limits<double>::epsilon(),\n                                trial_lambda_min_increment);\n        const double new_lambda_min = lambda_min + lambda_min_increment;\n        const double function_at_new_lambda_min =\n            function_to_zero(new_lambda_min);\n        if (function_at_new_lambda_min * function_at_lambda_min > 0.0) {\n          // Excluding code coverage, because it is not clear how to make\n          // this error actually occur.\n          // LCOV_EXCL_START\n          ERROR(\n              \"Cannot find bracket after trying to adjust bracketing \"\n              \"lambda_min due to roundoff : lambda_min=\"\n              << lambda_min << \" f(lambda_min)=\" << function_at_lambda_min\n              << \" lambda_max=\" << lambda_max << \" f(lambda_max)=\"\n              << function_at_lambda_max << \" new_lambda_min=\" << new_lambda_min\n              << \" f(new_lambda_min)=\" << function_at_new_lambda_min\n              << \" df(new_lambda_min)=\" << deriv_function_at_lambda_min\n              << \" new_lambda_min-lambda_min=\" << new_lambda_min - lambda_min\n              << \"\\n\");\n          // LCOV_EXCL_STOP\n        }\n        // Now the root is bracketed between lambda_min and new_lambda_min,\n        // so replace lambda_max and lambda_min and then fall through to the\n        // root finder.\n        lambda_max = lambda_min;\n        function_at_lambda_max = function_at_lambda_min;\n        lambda_min = new_lambda_min;\n        function_at_lambda_min = function_at_new_lambda_min;\n      } else if (abs(function_at_lambda_max) / abs(function_at_lambda_min) <\n                 roundoff_ratio) {\n        // Slightly increase lambda_max.  How far do we increase it?\n        // Figure that out by looking at the deriv of the function.\n        const double deriv_function_at_lambda_max =\n            deriv_function_to_zero(lambda_max);\n        const double trial_lambda_max_increment =\n            -2.0 * function_at_lambda_max / deriv_function_at_lambda_max;\n        // new_lambda_max = lambda_max + trial_lambda_max_increment would be\n        // a Newton-Raphson step except for the factor of 2 in\n        // trial_lambda_max_increment. The factor of 2 is there to\n        // over-compensate so that the new lambda_max brackets the\n        // root.\n        //\n        // But sometimes the factor of 2 is not enough when\n        // trial_lambda_max_increment is roundoff-small so that the\n        // change in new_lambda_max is zero or only in the last one or\n        // two bits.  If this is the case, then we set\n        // lambda_max_increment to some small roundoff value with the\n        // same sign as trial_lambda_max_increment.\n        // Note that lambda is always between zero and one, so it is\n        // ok to use an absolute epsilon.\n        const double lambda_max_increment =\n            abs(trial_lambda_max_increment) >\n                    1000.0 * std::numeric_limits<double>::epsilon()\n                ? trial_lambda_max_increment\n                : std::copysign(1000.0 * std::numeric_limits<double>::epsilon(),\n                                trial_lambda_max_increment);\n        const double new_lambda_max = lambda_max + lambda_max_increment;\n        const double function_at_new_lambda_max =\n            function_to_zero(new_lambda_max);\n        if (function_at_new_lambda_max * function_at_lambda_max > 0.0) {\n          // Excluding code coverage, because it is not clear how to make\n          // this error actually occur.\n          // LCOV_EXCL_START\n          ERROR(\n              \"Cannot find bracket after trying to adjust bracketing \"\n              \"lambda_max due to roundoff : lambda_min=\"\n              << lambda_min << \" f(lambda_min)=\" << function_at_lambda_min\n              << \" lambda_max=\" << lambda_max << \" f(lambda_max)=\"\n              << function_at_lambda_max << \" new_lambda_max=\" << new_lambda_max\n              << \" f(new_lambda_max)=\" << function_at_new_lambda_max\n              << \" df(new_lambda_max)=\" << deriv_function_at_lambda_max\n              << \" new_lambda_max-lambda_max=\" << new_lambda_max - lambda_min\n              << \"\\n\");\n          // LCOV_EXCL_STOP\n        }\n        // Now the root is bracketed between lambda_max and new_lambda_max,\n        // so replace lambda_max and lambda_min and then fall through to the\n        // root finder.\n        lambda_min = lambda_max;\n        function_at_lambda_min = function_at_lambda_max;\n        lambda_max = new_lambda_max;\n        function_at_lambda_max = function_at_new_lambda_max;\n      } else {\n        // Excluding code coverage, because it is not clear how to make\n        // this error actually occur.\n        // LCOV_EXCL_START\n        ERROR(\n            \"Root is not bracketed: \"\n            \"lambda_min=\"\n            << lambda_min << \" f(lambda_min)=\" << function_at_lambda_min\n            << \" lambda_max=\" << lambda_max\n            << \" f(lambda_max)=\" << function_at_lambda_max);\n        // LCOV_EXCL_STOP\n      }\n    }\n    // If we get here, root is bracketed. Use toms748, and use the\n    // function evaluations that we have already done.\n\n    // Lambda is between zero and 1, so the scale is unity, so therefore\n    // abs and rel tolerance are equal.\n    constexpr double abs_tol = 1.e-15;\n    constexpr double rel_tol = 1.e-15;\n\n    try {\n      lambda =\n          // NOLINTNEXTLINE(clang-analyzer-core)\n          RootFinder::toms748(function_to_zero, lambda_min, lambda_max,\n                              function_at_lambda_min, function_at_lambda_max,\n                              abs_tol, rel_tol);\n    } catch (std::exception&) { // LCOV_EXCL_LINE\n      // This should never happen unless something is really wrong with\n      // the root finder, so excluding this from code coverage.\n      // LCOV_EXCL_START\n      ERROR(\"Cannot find root after bracketing: lambda_min=\"\n            << lambda_min << \" f(lambda_min)=\" << function_at_lambda_min\n            << \" lambda_max=\" << lambda_max\n            << \" f(lambda_max)=\" << function_at_lambda_max);\n      // LCOV_EXCL_STOP\n    }\n  }\n\n  // Now that we have lambda, construct inverse.\n  const double one_plus_zbar_over_two =\n      (target_coords[2] + lambda * (z_plane_minus_one_ - z_plane_minus_two_) -\n       z_plane_minus_one_) /\n      ((1.0 - lambda) * (z_plane_plus_one_ - z_plane_minus_one_) +\n       lambda * (z_plane_plus_two_ - z_plane_minus_two_));\n  const double rhobar = 1.0 + lambda;\n  const double r_one_cos_theta_one =\n      z_plane_minus_one_ - center_one_[2] +\n      (z_plane_plus_one_ - z_plane_minus_one_) * one_plus_zbar_over_two;\n  const double r_two_cos_theta_two =\n      z_plane_minus_two_ - center_two_[2] +\n      (z_plane_plus_two_ - z_plane_minus_two_) * one_plus_zbar_over_two;\n  const double r_one_sin_theta_one =\n      sqrt(square(radius_one_) - square(r_one_cos_theta_one));\n  const double r_two_sin_theta_two =\n      sqrt(square(radius_two_) - square(r_two_cos_theta_two));\n  const double denom = 1.0 / ((1.0 - lambda) * r_one_sin_theta_one +\n                              lambda * r_two_sin_theta_two);\n  return {{rhobar *\n               (target_coords[0] - center_one_[0] -\n                lambda * (center_two_[0] - center_one_[0])) *\n               denom,\n           rhobar *\n               (target_coords[1] - center_one_[1] -\n                lambda * (center_two_[1] - center_one_[1])) *\n               denom,\n           2.0 * one_plus_zbar_over_two - 1.0}};\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nUniformCylindricalSide::jacobian(const std::array<T, 3>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  const ReturnType& xbar = source_coords[0];\n  const ReturnType& ybar = source_coords[1];\n  const ReturnType& zbar = source_coords[2];\n\n  auto jac =\n      make_with_value<tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>>(\n          dereference_wrapper(source_coords[0]), 0.0);\n\n  // Use jacobian components as temporary storage to avoid extra\n  // memory allocations.\n  // jac(2,2)=rhobar\n  get<2, 2>(jac) = sqrt(square(xbar) + square(ybar));\n  // jac(2,1)=R1 sin(theta1)\n  get<2, 1>(jac) = sqrt(\n      square(radius_one_) -\n      square(z_plane_minus_one_ - center_one_[2] +\n             0.5 * (zbar + 1.0) * (z_plane_plus_one_ - z_plane_minus_one_)));\n  // jac(0,2)=cot theta1/rho\n  get<0, 2>(jac) =\n      (z_plane_minus_one_ - center_one_[2] +\n       0.5 * (zbar + 1.0) * (z_plane_plus_one_ - z_plane_minus_one_)) /\n      (get<2, 1>(jac) * get<2, 2>(jac));\n  // jac(2,1)=R1 sin(theta1)/rho^3\n  get<2, 1>(jac) /= cube(get<2, 2>(jac));\n  // jac(2,0)=R2 sin(theta2)\n  get<2, 0>(jac) = sqrt(\n      square(radius_two_) -\n      square(z_plane_minus_two_ - center_two_[2] +\n             0.5 * (zbar + 1.0) * (z_plane_plus_two_ - z_plane_minus_two_)));\n  // jac(1,2)=cot theta2/rho\n  get<1, 2>(jac) =\n      (z_plane_minus_two_ - center_two_[2] +\n       0.5 * (zbar + 1.0) * (z_plane_plus_two_ - z_plane_minus_two_)) /\n      (get<2, 0>(jac) * get<2, 2>(jac));\n  // jac(2,0)=R2 sin(theta2)/rho^3\n  get<2, 0>(jac) /= cube(get<2, 2>(jac));\n  // jac(1,1)=lambda rho^2\n  get<1, 1>(jac) = square(get<2, 2>(jac)) * (get<2, 2>(jac) - 1.0);\n\n  // Now fill Jacobian values\n  get<0, 0>(jac) =\n      square(ybar) * get<2, 1>(jac) +\n      (get<1, 1>(jac) + square(xbar)) * (get<2, 0>(jac) - get<2, 1>(jac)) +\n      xbar * (center_two_[0] - center_one_[0]) / get<2, 2>(jac);\n  get<1, 1>(jac) =\n      square(xbar) * get<2, 1>(jac) +\n      (get<1, 1>(jac) + square(ybar)) * (get<2, 0>(jac) - get<2, 1>(jac)) +\n      ybar * (center_two_[1] - center_one_[1]) / get<2, 2>(jac);\n  get<0, 1>(jac) = xbar * ybar * (get<2, 0>(jac) - 2.0 * get<2, 1>(jac)) +\n                   ybar * (center_two_[0] - center_one_[0]) / get<2, 2>(jac);\n  get<1, 0>(jac) = xbar * ybar * (get<2, 0>(jac) - 2.0 * get<2, 1>(jac)) +\n                   xbar * (center_two_[1] - center_one_[1]) / get<2, 2>(jac);\n  get<1, 2>(jac) =\n      0.5 * (get<0, 2>(jac) * (z_plane_plus_one_ - z_plane_minus_one_) *\n                 (get<2, 2>(jac) - 2.0) +\n             get<1, 2>(jac) * (z_plane_plus_two_ - z_plane_minus_two_) *\n                 (1.0 - get<2, 2>(jac)));\n  get<0, 2>(jac) = get<1, 2>(jac) * xbar;\n  get<1, 2>(jac) *= ybar;\n  get<2, 1>(jac) = (z_plane_minus_two_ - z_plane_minus_one_ +\n                    0.5 * (zbar + 1) *\n                        (z_plane_plus_two_ - z_plane_minus_two_ -\n                         z_plane_plus_one_ + z_plane_minus_one_)) /\n                   get<2, 2>(jac);\n  get<2, 0>(jac) = xbar * get<2, 1>(jac);\n  get<2, 1>(jac) *= ybar;\n  get<2, 2>(jac) =\n      0.5 * ((2.0 - get<2, 2>(jac)) * (z_plane_plus_one_ - z_plane_minus_one_) +\n             (get<2, 2>(jac) - 1.0) * (z_plane_plus_two_ - z_plane_minus_two_));\n  return jac;\n}\n\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame>\nUniformCylindricalSide::inv_jacobian(\n    const std::array<T, 3>& source_coords) const {\n  return determinant_and_inverse(jacobian(source_coords)).second;\n}\n\nvoid UniformCylindricalSide::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | center_one_;\n    p | center_two_;\n    p | radius_one_;\n    p | radius_two_;\n    p | z_plane_plus_one_;\n    p | z_plane_minus_one_;\n    p | z_plane_plus_two_;\n    p | z_plane_minus_two_;\n  }\n}\n\nbool operator==(const UniformCylindricalSide& lhs,\n                const UniformCylindricalSide& rhs) {\n  // don't need to compare theta_max_one_ or theta_max_two_\n  // because they are uniquely determined from the other variables.\n  return lhs.center_one_ == rhs.center_one_ and\n         lhs.radius_one_ == rhs.radius_one_ and\n         lhs.z_plane_plus_one_ == rhs.z_plane_plus_one_ and\n         lhs.z_plane_minus_one_ == rhs.z_plane_minus_one_ and\n         lhs.center_two_ == rhs.center_two_ and\n         lhs.radius_two_ == rhs.radius_two_ and\n         lhs.z_plane_plus_two_ == rhs.z_plane_plus_two_ and\n         lhs.z_plane_minus_two_ == rhs.z_plane_minus_two_;\n}\n\nbool operator!=(const UniformCylindricalSide& lhs,\n                const UniformCylindricalSide& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, 3>               \\\n  UniformCylindricalSide::operator()(                                        \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  UniformCylindricalSide::jacobian(                                          \\\n      const std::array<DTYPE(data), 3>& source_coords) const;                \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, 3, Frame::NoFrame> \\\n  UniformCylindricalSide::inv_jacobian(                                      \\\n      const std::array<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector,\n                                      std::reference_wrapper<const double>,\n                                      std::reference_wrapper<const DataVector>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/UniformCylindricalSide.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class UniformCylindricalSide.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from 3D unit right cylindrical shell to a volume that connects\n *  portions of two spherical surfaces.\n *\n * \\image html UniformCylSide.svg \"A hollow cylinder maps to the shaded region.\"\n *\n * \\details Consider two spheres with centers \\f$C_1\\f$ and \\f$C_2\\f$,\n * and radii \\f$R_1\\f$ and \\f$R_2\\f$. Sphere 1 is assumed to be contained\n * inside Sphere 2.\n * Let sphere 1 be intersected by two\n * planes normal to the \\f$z\\f$ axis and located at\n * \\f$z = z^{\\pm}_{\\mathrm{P}1}\\f$,\n * and let sphere 2 be intersected by two planes normal to the \\f$z\\f$ axis and\n * located at \\f$z = z^{\\pm}_{\\mathrm{P}2}\\f$.  Here we assume that\n * \\f$z^{-}_{\\mathrm{P}2} \\leq z^{-}_{\\mathrm{P}1}<\n * z^{+}_{\\mathrm{P}1} \\leq z^{+}_{\\mathrm{P}2}\\f$.\n *\n * UniformCylindricalSide maps a 3D unit right cylindrical shell (with\n * coordinates \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ such that\n * \\f$-1\\leq\\bar{z}\\leq 1\\f$ and \\f$1 \\leq \\bar{x}^2+\\bar{y}^2 \\leq 4\\f$, where\n * the values of 1 and 2 for the inner and outer cylindrical radii\n * are arbitrary choices but are required by UniformCylindricalSide)\n * to the shaded area in the figure above (with coordinates\n * \\f$(x,y,z)\\f$).  The \"inner surface\" of the cylindrical shell\n * \\f$\\bar{x}^2+\\bar{y}^2=1\\f$ is mapped to the portion of sphere 1\n * that has \\f$z^{-}_{\\mathrm{P}1} \\leq z \\leq z^{+}_{\\mathrm{P}1} \\f$,\n * and on this portion of the sphere the cosine of the polar angular coordinate\n * \\f$\\cos\\theta_1 =(z-C_1^z)/R_1\\f$ is uniform in \\f$\\bar{z}\\f$,\n * and the angular coordinate \\f$\\phi_1 = \\atan((y-C_1^y)/(x-C_1^x))\\f$\n * is the same as \\f$\\phi = \\atan(\\bar{y}/\\bar{x})\\f$.\n * Likewise, the \"outer surface\" of the cylindrical shell\n * \\f$\\bar{x}^2+\\bar{y}^2=4\\f$ is mapped to the portion of sphere 2\n * that has \\f$z^{-}_{\\mathrm{P}2} \\leq z \\leq z^{+}_{\\mathrm{P}2}\n * \\f$, and on this portion of the sphere the cosine of the azimuthal\n * angular coordinate\n * \\f$\\cos\\theta_2 = (z-C_2^z)/R_2\\f$ is uniform in \\f$\\bar{z}\\f$,\n * and the angular coordinate \\f$\\phi_2 =\n * \\atan((y-C_2^y)/(x-C_2^x))\\f$ is the same as \\f$\\phi\\f$.\n *\n * UniformCylindricalSide is different from CylindricalSide\n * because of the distribution of points on the spheres, and because\n * for UniformCylindricalSide the mapped portion of both Sphere 1\n * and Sphere 2 are bounded by planes of constant \\f$z\\f$, whereas for\n * CylindricalSide only one of the mapped portions is bounded by a\n * plane (except for specially chosen map parameters).  Note that\n * UniformCylindricalSide can be used to construct maps that connect\n * an arbitrary number of nested spheres; this is not possible for\n * CylindricalSide for more than 3 nested spheres because of this\n * asymmetry between CylindricalSide's two spherical surfaces.\n *\n * Note that the entire region between Sphere 1 and Sphere 2 can be covered\n * by a single cylindrical shell (mapped using UniformCylindricalSide) and\n * two cylinders (each mapped by UniformCylindricalEndcap).\n *\n * UniformCylindricalSide is intended to be composed with `Wedge<2>` maps to\n * construct a portion of a cylindrical domain for a binary system.\n *\n * UniformCylindricalSide can be used to construct a domain that is similar\n * to, but not identical to, the one described briefly in the Appendix of\n * \\cite Buchman:2012dw.\n * UniformCylindricalSide is used to construct the Blocks analogous to\n * those labeled 'CA cylinder', 'EA cylinder', 'CB cylinder', 'EE cylinder',\n * and 'EB cylinder' in Figure 20 of that paper.\n *\n * UniformCylindricalSide provides the following functions:\n *\n * ## operator()\n *\n * `operator()` maps \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$ to \\f$(x,y,z)\\f$\n * according to\n *\n * \\f{align}\n * x &= C_1^x+\\lambda(C_2^x-C_1^x) +\n *        \\cos\\phi\\left(R_1\\sin\\theta_1 +\n *        \\lambda(R_2\\sin\\theta_2-R_1\\sin\\theta_1)\\right), \\label{eq:x0} \\\\\n * y &= C_1^y+\\lambda(C_2^y-C_1^y) +\n *        \\sin\\phi\\left(R_1\\sin\\theta_1 +\n *        \\lambda(R_2\\sin\\theta_2-R_1\\sin\\theta_1)\\right), \\label{eq:x1} \\\\\n * z &= C_1^z+\\lambda(C_2^z-C_1^z) +\n *        R_1\\cos\\theta_1 +\n *        \\lambda(R_2\\cos\\theta_2-R_1\\cos\\theta_1) \\label{eq:x2}.\n * \\f}\n *\n * Here\n * \\f{align}\n * \\lambda  &= \\bar{\\rho}-1,\\label{eq:lambdafromrhobar}\\\\\n * \\cos\\theta_1 &= \\cos\\theta_{1 \\mathrm{max}} +\n *         \\left(\\cos\\theta_{1 \\mathrm{min}}-\\cos\\theta_{1 \\mathrm{max}}\\right)\n *             \\frac{\\bar{z}+1}{2}\\label{eq:deftheta1}\\\\\n * \\cos\\theta_2 &= \\cos\\theta_{2 \\mathrm{max}} +\n *             \\left(\\cos\\theta_{2 \\mathrm{min}}-\n *                   \\cos\\theta_{2 \\mathrm{max}}\\right)\n *             \\frac{\\bar{z}+1}{2}\\label{eq:deftheta2}\\\\\n * \\phi     &= \\atan(\\bar{y}/\\bar{x})\\label{eq:defphi},\n * \\f}\n * where \\f$\\theta_{1 \\mathrm{min}}\\f$, \\f$\\theta_{2 \\mathrm{min}}\\f$,\n * \\f$\\theta_{1 \\mathrm{max}}\\f$, and \\f$\\theta_{2 \\mathrm{max}}\\f$\n * are defined by\n * \\f{align}\n *   \\label{eq:deftheta1min}\n *   \\cos(\\theta_{1\\mathrm{min}}) &= (z^{+}_{\\mathrm{P}1}-C_1^z)/R_1,\\\\\n *   \\cos(\\theta_{1\\mathrm{max}}) &= (z^{-}_{\\mathrm{P}1}-C_1^z)/R_1,\\\\\n *   \\cos(\\theta_{2\\mathrm{min}}) &= (z^{+}_{\\mathrm{P}2}-C_2^z)/R_2,\\\\\n *   \\label{eq:deftheta2max}\n *   \\cos(\\theta_{2\\mathrm{max}}) &= (z^{-}_{\\mathrm{P}2}-C_2^z)/R_2,\n * \\f}\n * and\n * \\f{align}\n * \\bar{\\rho} &= \\sqrt{\\bar{x}^2+\\bar{y}^2}/\\bar{R} \\label{eq:defrhobar},\n * \\f}\n * where \\f$\\bar{R}\\f$ is the inner radius of the cylindrical shell in barred\n * coordinates, which is always unity.\n *\n * Note that \\f$\\theta_{1\\mathrm{min}}<\\theta_{1\\mathrm{max}}\\f$ but\n * \\f$\\cos\\theta_{1\\mathrm{min}}>\\cos\\theta_{1\\mathrm{max}}\\f$ (and same\n * for sphere 2).\n *\n * Also note that Eqs. (\\f$\\ref{eq:deftheta1}\\f$) and\n * (\\f$\\ref{eq:deftheta2}\\f$) can be simplified using Eqs.\n * (\\f$\\ref{eq:deftheta1min}\\f$-\\f$\\ref{eq:deftheta2max}\\f$):\n * \\f{align}\n * R_1\\cos\\theta_1 &= z^{-}_{\\mathrm{P}1}-C_1^z\n *        +(z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1})\n *             \\frac{\\bar{z}+1}{2}\\label{eq:deftheta1alt}\\\\\n * R_2\\cos\\theta_2 &= z^{-}_{\\mathrm{P}2}-C_2^z\n *        +(z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2})\n *             \\frac{\\bar{z}+1}{2}\\label{eq:deftheta2alt}\\\\\n * \\f}\n *\n * ## inverse\n *\n * Given \\f$(x,y,z)\\f$ we want to find \\f$(\\bar{x},\\bar{y},\\bar{z})\\f$.\n * From Eqs. (\\f$\\ref{eq:x2}\\f$), (\\f$\\ref{eq:deftheta1alt}\\f$), and\n * (\\f$\\ref{eq:deftheta2alt}\\f$) we can write \\f$\\bar{z}\\f$ as a function\n * of \\f$\\lambda\\f$:\n *\n * \\f{align}\n * \\frac{1+\\bar{z}}{2} &=\n *   \\frac{z +\n *     \\lambda (z^{-}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}2}) - z^{-}_{\\mathrm{P}1}}\n *   {(1-\\lambda)(z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1})\n *    + \\lambda(z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2})}\n *                 \\label{eq:zbar_from_lambda},\n * \\f}\n * Note that the denominator of\n * Eq. (\\f$\\ref{eq:zbar_from_lambda}\\f$) is always positive because\n * \\f$0\\leq\\lambda\\leq 1\\f$, \\f$z^{+}_{\\mathrm{P}1}>z^{-}_{\\mathrm{P}1}\\f$,\n * and \\f$z^{+}_{\\mathrm{P}2}>z^{-}_{\\mathrm{P}2}\\f$.\n *\n * By eliminating \\f$\\phi\\f$ from Eqs. (\\f$\\ref{eq:x0}\\f$) and\n * (\\f$\\ref{eq:x1}\\f$) we find that \\f$\\lambda\\f$ is the solution\n * of \\f$Q(\\lambda)=0\\f$, where\n *\n * \\f{align}\n * Q(\\lambda) &= \\left(x-C_1^x-\\lambda(C_2^x-C_1^x)\\right)^2+\n *               \\left(y-C_1^y-\\lambda(C_2^y-C_1^y)\\right)^2-\n *               \\left((1-\\lambda)R_1\\sin\\theta_1 +\n *               \\lambda R_2\\sin\\theta_2\\right)^2.\\label{eq:defQ}\n * \\f}\n * Here \\f$\\theta_1\\f$ and \\f$\\theta_2\\f$ are functions\n * of \\f$\\bar{z}\\f$ through Eqs. (\\f$\\ref{eq:deftheta1alt}\\f$) and\n * (\\f$\\ref{eq:deftheta2alt}\\f$), and \\f$\\bar{z}\\f$ is a function of\n * \\f$\\lambda\\f$ through Eq. (\\f$\\ref{eq:zbar_from_lambda}\\f$).\n *\n * We solve \\f$Q(\\lambda)=0\\f$ numerically; it is a one-dimensional\n * root-finding problem.\n *\n * Once we have determined \\f$\\lambda\\f$, we then obtain \\f$\\bar{z}\\f$\n * from Eq. (\\f$\\ref{eq:zbar_from_lambda}\\f$), and we obtain \\f$\\phi\\f$ from\n *\n * \\f{align}\n *  \\tan\\phi &=\n *  \\frac{y-C_1^y-\\lambda(C_2^y-C_1^y)}{x-C_1^x-\\lambda(C_2^x-C_1^x)}.\n * \\f}\n *\n * Then \\f$\\bar{\\rho}\\f$ is obtained from Eq. (\\f$\\ref{eq:lambdafromrhobar}\\f$)\n * and \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$ are obtained from\n *\n * \\f{align}\n *   \\bar{x} &= \\bar{\\rho}\\bar{R}\\cos\\phi,\\\\\n *   \\bar{y} &= \\bar{\\rho}\\bar{R}\\sin\\phi.\n * \\f}\n *\n * ### Considerations when root-finding.\n *\n * We solve \\f$Q(\\lambda)=0\\f$ numerically for \\f$\\lambda\\f$,\n * where \\f$Q(\\lambda)\\f$ is given by Eq. (\\f$\\ref{eq:defQ}\\f$).\n *\n * #### min/max values of \\f$\\lambda\\f$:\n *\n * Note that the root we care about must have \\f$-1\\leq\\bar{z}\\leq 1\\f$;\n * therefore from Eq. (\\f$\\ref{eq:zbar_from_lambda}\\f$) we have\n *\n * \\f{align}\n *   \\lambda_{\\mathrm{min}} &=\n *   \\mathrm{max}\\left\\{0,\n *              \\frac{z-z^{+}_{\\mathrm{P}1}}\n *                 {(z^{+}_{\\mathrm{P}2}-z^{+}_{\\mathrm{P}1})},\n *              \\frac{z^{-}_{\\mathrm{P}1}-z}\n *                 {(z^{-}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}2})}\n *      \\right\\}\\label{eq:lambdamin}\n * \\f}\n * In the case where \\f$z^{+}_{\\mathrm{P}2}=z^{+}_{\\mathrm{P}1}\\f$\n * we treat the middle term in Eq.(\\f$\\ref{eq:lambdamin}\\f$) as zero since\n * in that case \\f$z-z^{+}_{\\mathrm{P}1}\\f$ can never be positive for\n * \\f$x^2\\f$ in the range of the map, and for\n * \\f$z=z^{+}_{\\mathrm{P}2}=z^{+}_{\\mathrm{P}1}\\f$\n * it turns out that\n * (\\f$\\ref{eq:zbar_from_lambda}\\f$) places no restriction on\n * \\f$\\lambda_{\\mathrm{min}}\\f$.  For the same reason, if\n * \\f$z^{-}_{\\mathrm{P}2}=z^{-}_{\\mathrm{P}1}\\f$ we treat\n * the last term in Eq.(\\f$\\ref{eq:lambdamin}\\f$) as zero.\n *\n * We look for a root only between \\f$\\lambda_{\\mathrm{min}}\\f$\n * and \\f$\\lambda_{\\mathrm{max}}=1\\f$.\n *\n * ##### Roots within roundoff of min or max \\f$\\lambda\\f$\n *\n * Sometimes a root is within roundoff of \\f$\\lambda_{\\mathrm{min}}\\f$.\n * In this case, the root might not be bracketed by\n * \\f$[\\lambda_{\\mathrm{min}},\\lambda_{\\mathrm{max}}]\\f$ if the root\n * is slightly outside that interval by roundoff error.  If we find that\n * \\f$Q(\\lambda_{\\mathrm{min}})\\f$ is near zero but has the wrong sign,\n * then we slightly expand the interval as follows:\n *\n * \\f{align}\n *    \\lambda_{\\mathrm{min}} \\to \\lambda_{\\mathrm{min}}\n *  - 2 \\frac{Q(\\lambda_{\\mathrm{min}})}{Q'(\\lambda_{\\mathrm{min}})},\n * \\f}\n *\n * where \\f$Q'(\\lambda_{\\mathrm{min}})\\f$ is the derivative of the function\n * in Eq. (\\f$\\ref{eq:defQ}\\f$). Note that without the factor of 2, this is\n * a Newton-Raphson step; the factor of 2 is there to overcompensate so that\n * the new \\f$\\lambda_{\\mathrm{min}}\\f$ brackets the root.\n *\n * Note that by differentiating Eqs. (\\f$\\ref{eq:defQ}\\f$) and\n * (\\f$\\ref{eq:zbar_from_lambda}\\f$), one obtains\n *\n * \\f{align}\n * Q'(\\lambda) =& -2 \\left[\n *      \\left(x-C_1^x-\\lambda(C_2^x-C_1^x)\\right)(C_2^x-C_1^x)+\n *      \\left(y-C_1^y-\\lambda(C_2^y-C_1^y)\\right)(C_2^y-C_1^y)\n *      \\right]\\nonumber \\\\\n *     &\n *    -\\left[\n *    2(R_2\\sin\\theta_2-R_1\\sin\\theta_1)\n *    -(1-\\lambda)\\cot\\theta_1 (z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1})\n *     \\frac{d\\bar{z}}{d\\lambda} \\right. \\nonumber \\\\\n *     & \\left.\\qquad\n *    -\\lambda \\cot\\theta_2 (z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2})\n *     \\frac{d\\bar{z}}{d\\lambda}\n *    \\right]\n *    \\left((1-\\lambda)R_1\\sin\\theta_1 +\n *                \\lambda R_2\\sin\\theta_2\\right), \\label{eq:defQderiv}\n * \\f}\n *\n * where\n * \\f{align}\n *  \\frac{d\\bar{z}}{d\\lambda} &=\n *  \\frac{(1-\\bar{z})(z^{-}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}2})\n *       -(1+\\bar{z})(z^{+}_{\\mathrm{P}2}-z^{+}_{\\mathrm{P}1})}\n *        {(1-\\lambda)(z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1})\n *       + \\lambda(z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2})}\n *  \\label{eq:dzbar_dlambda}.\n * \\f}\n *\n * A root within roundoff of \\f$\\lambda_{\\mathrm{max}}\\f$ is treated\n * similarly.\n *\n * #### Special cases:\n *\n * For some points on the boundary of the mapped domain,\n * \\f$\\lambda_{\\mathrm{min}}\\f$ will be within roundoff of\n * \\f$\\lambda=1\\f$. We check explicitly for this case, and we\n * compute the root as exactly \\f$\\lambda=1\\f$.\n *\n * ### Quick rejection of points out of range of the map.\n *\n * It is expected that `inverse()` will often be passed points\n * \\f$(x,y,z)\\f$ that are out of the range of the map; in this case\n * `inverse()` returns a `std::nullopt`. To avoid the difficulty and\n * expense of attempting to solve \\f$Q(\\lambda)=0\\f$ numerically\n * for such points (and then having this solution fail), it is useful\n * to quickly reject points \\f$(x,y,z)\\f$ that are outside the range\n * of the map.\n *\n * Any point in the range of the map must be inside or on\n * sphere 2, and it must be outside or on sphere 1, so the inverse map\n * can immediately return a default-constructed\n * `std::optional<std::array<double, 3>>` for a point that does not\n * satisfy these conditions.\n *\n * Likewise, the inverse map can immediately reject any point with\n * \\f$z < z^{-}_{\\mathrm{P}2}\\f$ or \\f$z > z^{+}_{\\mathrm{P}2}\\f$.\n *\n * Finally, for \\f$z^{+}_{\\mathrm{P}2}\\neq z^{+}_{\\mathrm{P}1}\\f$,\n * consider the circle \\f$S^{+}_1\\f$\n * defining the intersection of sphere 1\n * and the plane \\f$z = z^{+}_{\\mathrm{P}1}\\f$; this circle has radius\n * \\f$r_1 = R_1 \\sin\\theta_{1\\mathrm{min}}\\f$.  Similarly, the circle\n * \\f$S^{+}_2\\f$ defining the intersection of sphere 2 and the plane \\f$z =\n * z^{+}_{\\mathrm{P}2}\\f$ has radius \\f$r_2 = R_2\n * \\sin\\theta_{2\\mathrm{min}}\\f$.  Now consider the cone that passes\n * through these two circles.  A point in the range of the map must be outside\n * (where \"outside\" means farther from the \\f$z\\f$ axis) or on this cone.\n * The cone can be defined parametrically as\n *\n * \\f{align}\n * x_c &= C_1^x + \\tilde{\\lambda}(C_2^x-C_1^x) +\n *      \\cos\\varphi (r_1 + \\tilde{\\lambda} (r_2 -r_1)),\\\\\n * y_c &= C_1^y + \\tilde{\\lambda}(C_2^y-C_1^y),+\n *      \\sin\\varphi (r_1 + \\tilde{\\lambda} (r_2 -r_1)),\\\\\n * z_c &= z^{+}_{\\mathrm{P}1} +\n *        \\tilde{\\lambda}(z^{+}_{\\mathrm{P}2}-z^{+}_{\\mathrm{P}1}),\n * \\f}\n *\n * where \\f$(x_c,y_c,z_c)\\f$ is a point on the cone, and the two\n * parameters defining a point on the cone are the angle \\f$\\varphi\\f$\n * around the cone and the parameter \\f$\\tilde{\\lambda}\\f$, which is\n * defined to be zero on \\f$S^{+}_1\\f$ and unity on \\f$S^{+}_2\\f$.\n *\n * Given an arbitrary point \\f$(x, y, z)\\f$, we can determine whether\n * or not that point is inside the cone as follows.  First determine\n *\n * \\f{align}\n *  \\tilde{\\lambda} &= \\frac{z - z^{+}_{\\mathrm{P}1}}\n *   {z^{+}_{\\mathrm{P}2}-z^{+}_{\\mathrm{P}1}}, \\\\\n *  \\tilde{x} &= x - C_1^x - \\tilde{\\lambda} (C_2^x-C_1^x),\\\\\n *  \\tilde{y} &= y - C_1^y - \\tilde{\\lambda} (C_2^y-C_1^y).\\\\\n * \\f}\n *\n * Then the condition for the point to be outside or on the cone is\n * \\f{align}\n * \\sqrt{\\tilde{x}^2+\\tilde{y}^2} \\ge r_1 + (r_2-r_1)\\tilde{\\lambda}.\n * \\f}\n *\n * The inverse map therefore rejects any points that do\n * not satisfy this criterion. The cone criterion makes sense only\n * for points with \\f$z\\geq z^{+}_{\\mathrm{P}1}\\f$.\n *\n * For \\f$z^{-}_{\\mathrm{P}2} \\neq z^{-}_{\\mathrm{P}1}\\f$,\n * a similar cone can be constructed for the southern hemisphere. That\n * cone passes through\n * the circle \\f$S^{-}_1\\f$\n * defining the intersection of sphere 1\n * and the plane \\f$z = z^{-}_{\\mathrm{P}1}\\f$ and the circle\n * \\f$S^{-}_2\\f$ defining the intersection of sphere 2 and the plane \\f$z =\n * z^{-}_{\\mathrm{P}2}\\f$.  The inverse map rejects any point that is inside\n * that cone as well, provided that the point has\n * \\f$z\\leq z^{-}_{\\mathrm{P}1}\\f$.  For points with\n * \\f$z > z^{-}_{\\mathrm{P}1}\\f$ checking the cone criterion\n * does not make sense.\n *\n * ## jacobian\n *\n * From Eqs. (\\f$\\ref{eq:deftheta1alt}\\f$) and (\\f$\\ref{eq:deftheta2alt}\\f$)\n * we see that \\f$\\theta_1\\f$ and \\f$\\theta_2\\f$ depend on \\f$\\bar{z}\\f$ and\n * are independent of \\f$\\bar{x}\\f$ and \\f$\\bar{y}\\f$, and that\n * \\f{align}\n * \\frac{\\partial (R_1\\cos\\theta_1)}{\\partial\\bar{z}}\n * &= \\frac{1}{2}(z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1}),\n *     \\label{eq:dcostheta1} \\\\\n * \\frac{\\partial (R_2\\cos\\theta_2)}{\\partial\\bar{z}}\n * &= \\frac{1}{2}(z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2}),\n *     \\label{eq:dcostheta2} \\\\\n * \\frac{\\partial (R_1\\sin\\theta_1)}{\\partial\\bar{z}}\n * &= -\\frac{1}{2}\\cot\\theta_1 (z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1}),\n *     \\label{eq:dsintheta1} \\\\\n * \\frac{\\partial (R_2\\sin\\theta_2)}{\\partial\\bar{z}}\n * &= -\\frac{1}{2}\\cot\\theta_2(z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2}),\n *     \\label{eq:dsintheta2}\n * \\f}\n *\n * Also, from Eqs. (\\f$\\ref{eq:defphi}\\f$) and (\\f$\\ref{eq:defrhobar}\\f$)\n * we have\n * \\f{align}\n * \\frac{\\partial\\cos\\phi}{\\partial\\bar{x}}\n *  &= \\frac{\\bar{y}^2}{\\bar{R}^3\\bar{\\rho}^3},\n *  \\label{eq:dcosphidxbar} \\\\\n * \\frac{\\partial\\cos\\phi}{\\partial\\bar{y}}\n *  &= -\\frac{\\bar{x}\\bar{y}}{\\bar{R}^3\\bar{\\rho}^3},\n *  \\label{eq:dcosphidybar} \\\\\n * \\frac{\\partial\\sin\\phi}{\\partial\\bar{x}}\n *  &= -\\frac{\\bar{x}\\bar{y}}{\\bar{R}^3\\bar{\\rho}^3},\n *  \\label{eq:dsinphidxbar} \\\\\n * \\frac{\\partial\\sin\\phi}{\\partial\\bar{y}}\n *  &= \\frac{\\bar{x}^2}{\\bar{R}^3\\bar{\\rho}^3},\n *  \\label{eq:dsinphidybar}\n * \\f}\n * and we know that \\f$\\phi\\f$ is independent of \\f$\\bar{z}\\f$.\n *\n * Finally, from Eqs. (\\f$\\ref{eq:defrhobar}\\f$) and\n * (\\f$\\ref{eq:lambdafromrhobar}\\f$) we have\n *\n * \\f{align}\n * \\frac{\\partial\\lambda}{\\partial\\bar{x}}\n *  &= \\frac{\\bar{x}}{\\bar{R}^2\\bar{\\rho}},\n *  \\label{eq:dlambdadxbar} \\\\\n * \\frac{\\partial\\lambda}{\\partial\\bar{y}}\n *  &= \\frac{\\bar{y}}{\\bar{R}^2\\bar{\\rho}},\n *  \\label{eq:dlambdadybar}\n * \\f}\n * with no dependence on \\f$\\bar{z}\\f$.\n *\n * Putting these results together yields\n * \\f{align}\n * \\frac{\\partial x^0}{\\partial \\bar{x}} &=\n * \\frac{\\bar{y}^2}{\\bar{\\rho}^3\\bar{R}^3}R_1\\sin\\theta_1 +\n *        (R_2\\sin\\theta_2-R_1\\sin\\theta_1)\n *        \\frac{\\lambda \\bar{R}^2\\bar\\rho^2+\\bar{x}^2}{\\bar\\rho^3\\bar{R}^3}\n *  + \\frac{\\bar{x}}{\\bar\\rho\\bar{R}^2}(C_2^x-C_1^x),\\\\\n * \\frac{\\partial x^0}{\\partial \\bar{y}} &=\n * \\frac{\\bar{x}\\bar{y}}{\\bar{\\rho}^3\\bar{R}^3}\n *        (R_2\\sin\\theta_2-2 R_1\\sin\\theta_1)\n *  + \\frac{\\bar{y}}{\\bar\\rho\\bar{R}^2}(C_2^x-C_1^x),\\\\\n * \\frac{\\partial x^0}{\\partial \\bar{z}} &=\n * -\\frac{1}{2}\\frac{\\bar{x}}{\\bar\\rho\\bar{R}}\\left[\n *   \\cot\\theta_1(1-\\lambda)(z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1})+\n *   \\cot\\theta_2\\lambda(z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2})\\right],\\\\\n * \\frac{\\partial x^1}{\\partial \\bar{x}} &=\n * \\frac{\\bar{x}\\bar{y}}{\\bar{\\rho}^3\\bar{R}^3}\n *        (R_2\\sin\\theta_2-2 R_1\\sin\\theta_1)\n *  + \\frac{\\bar{x}}{\\bar\\rho\\bar{R}^2}(C_2^y-C_1^y),\\\\\n * \\frac{\\partial x^1}{\\partial \\bar{y}} &=\n * \\frac{\\bar{x}^2}{\\bar{\\rho}^3\\bar{R}^3}R_1\\sin\\theta_1 +\n *        (R_2\\sin\\theta_2-R_1\\sin\\theta_1)\n *        \\frac{\\lambda \\bar{R}^2\\bar\\rho^2+\\bar{y}^2}{\\bar\\rho^3\\bar{R}^3}\n *  + \\frac{\\bar{y}}{\\bar\\rho\\bar{R}^2}(C_2^y-C_1^y),\\\\\n * \\frac{\\partial x^1}{\\partial \\bar{z}} &=\n * -\\frac{1}{2}\\frac{\\bar{y}}{\\bar\\rho\\bar{R}}\\left[\n *   \\cot\\theta_1(1-\\lambda)(z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1})+\n *   \\cot\\theta_2\\lambda(z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2})\\right],\\\\\n * \\frac{\\partial x^2}{\\partial \\bar{x}} &=\n *  \\frac{\\bar{x}}{\\bar\\rho\\bar{R}^2}\\left(\n *        C_2^z-C_1^z + R_2\\cos\\theta_2-R_1\\cos\\theta_1\\right),\\\\\n * \\frac{\\partial x^2}{\\partial \\bar{y}} &=\n *  \\frac{\\bar{y}}{\\bar\\rho\\bar{R}^2}\\left(\n *        C_2^z-C_1^z + R_2\\cos\\theta_2-R_1\\cos\\theta_1\\right),\\\\\n * \\frac{\\partial x^2}{\\partial \\bar{z}} &=\n * \\frac{1}{2}(1-\\lambda)(z^{+}_{\\mathrm{P}1}-z^{-}_{\\mathrm{P}1})+\n * \\frac{1}{2}\\lambda(z^{+}_{\\mathrm{P}2}-z^{-}_{\\mathrm{P}2}).\n * \\f}\n *\n * ## inv_jacobian\n *\n * The inverse Jacobian is computed by numerically inverting the\n * Jacobian.\n *\n * ## Restrictions on map parameters\n *\n * We demand that Sphere 1 is fully contained inside Sphere 2, and\n * that the two spheres have at least some small separation between\n * them. In particular, we demand that\n * \\f{align}\n *  0.98 R_2 &\\geq R_1 + |C_1-C_2|, \\label{eq:spherecontained}\n * \\f}\n * where 0.98 is a safety factor. It is possible to construct a valid\n * map without this assumption, but the assumption simplifies the\n * code, and the expected use cases obey this restriction.\n *\n * We also demand that \\f$R_1 \\geq 0.08 R_2\\f$.  Again, this assumption\n * is made for accuracy purposes and might be relaxed.\n *\n * ### Invertibility condition\n *\n * Consider the line segment \\f$L^+_1\\f$ that connects a point on the\n * circle \\f$S^+_1\\f$ (the circle formed by the intersection of sphere 1\n * and the plane \\f$z=z^+_{\\mathrm{P}1}\\f$) with the center of the\n * circle \\f$S^+_1\\f$.  Consider another line segment \\f$L^+_2\\f$ that\n * connects the same point on the circle \\f$S^+_1\\f$ with the\n * corresponding point on the circle \\f$S^+_2\\f$ (the circle formed by\n * the intersection of sphere 2 and the plane\n * \\f$z=z^+_{\\mathrm{P}2}\\f$). Now consider the angle between \\f$L^+_1\\f$\n * and \\f$L^+_2\\f$, as measured from the interior of sphere 1, and Let\n * \\f$\\alpha^+\\f$ be the minimum value of this angle over the circle.\n * \\f$\\alpha^+\\f$ is shown in the figure above. If\n * \\f$\\alpha^+ < \\theta_{1 \\mathrm{min}}\\f$, then the line segment \\f$L^+_2\\f$\n * twice intersects the unmapped portion of sphere 1 near the north pole,\n * so the map is ill-defined.\n * Similarly, if \\f$\\alpha^+ < \\theta_{2 \\mathrm{min}}\\f$,\n * then the line segment \\f$L^+_2\\f$ twice intersects the mapped portion of\n * sphere 2 near the north pole, and again the map is poorly defined.\n * Therefore we demand that the map parameters satisfy\n * - \\f$\\alpha^+ > 1.1 \\theta_{1 \\mathrm{min}}\\f$\n * - \\f$\\alpha^+ > 1.1 \\theta_{2 \\mathrm{min}}\\f$\n *\n * where 1.1 is a safety factor.\n *\n * Similarly, one can define an angle \\f$\\alpha^-\\f$ for the region\n * near the south pole, and we require similar restrictions on that angle.\n *\n * ### Restrictions on z-planes\n *\n * We also demand that either\n * \\f$z^+_{\\mathrm{P}1} = z^+_{\\mathrm{P}2}\\f$\n * or that \\f$z^+_{\\mathrm{P}1} <= z^+_{\\mathrm{P}2} -0.03 R_2\\f$.\n * Similarly, we demand that either \\f$z^-_{\\mathrm{P}1} = z^-_{\\mathrm{P}2}\\f$\n * or \\f$z^-_{\\mathrm{P}1} >= z^-_{\\mathrm{P}2} + 0.03 R_2\\f$.\n * These restrictions follow expected use cases and avoid extreme distortions.\n *\n * ### Restrictions for unequal z planes\n * For \\f$z^+_{\\mathrm{P}1} \\neq z^+_{\\mathrm{P}2}\\f$ and\n * \\f$z^-_{\\mathrm{P}1} \\neq z^-_{\\mathrm{P}2}\\f$, we assume the following\n * restrictions on other parameters:\n *\n * We prohibit a tiny Sphere 1 near the edge of Sphere 2 by demanding that\n * \\f{align}\n *  C^z_1 - R_1 &\\leq C^z_2 + R_2/5,\\\\\n *  C^z_1 + R_1 &\\geq C^z_2 - R_2/5.\n * \\f}\n * We also demand that the polar axis of Sphere 2 intersects Sphere 1\n * somewhere:\n * \\f{align}\n * \\sqrt{(C^x_1-C^x_2)^2 + (C^y_1-C^y_2)^2} &\\leq R_1.\n * \\f}\n * and we demand that Sphere 1 is not too close to the edge of Sphere 2\n * in the \\f$x\\f$ or \\f$y\\f$ directions:\n * \\f{align}\n * \\sqrt{(C^y_1-C^y_2)^2 + (C^y_1-C^y_2)^2} &\\leq \\mathrm{max}(0,0.95 R_2-R_1),\n * \\f}\n * where the max avoids problems when \\f$0.95 R_2-R_1\\f$ is negative\n * (which, if it occurs, means that the \\f$x\\f$ and \\f$y\\f$ centers of the\n * two spheres are equal).\n *\n * We require that the z planes in the above figures lie above/below\n * the centers of the corresponding spheres and are not too close to\n * the centers or edges of those spheres; specificially, we demand\n * that\n * \\f{align}\n *   \\label{eq:theta_1_min_res}\n *   0.15\\pi &< \\theta_{1 \\mathrm{min}} < 0.4\\pi \\\\\n *   \\label{eq:theta_1_max_res}\n *   0.6\\pi &< \\theta_{1 \\mathrm{max}} < 0.85\\pi \\\\\n *   \\label{eq:theta_2_min_res}\n *   0.15\\pi &< \\theta_{2 \\mathrm{min}} < 0.4\\pi \\\\\n *   \\label{eq:theta_2_max_res}\n *   0.6\\pi &< \\theta_{2 \\mathrm{max}} < 0.85\\pi .\n * \\f}\n *\n * Here the numerical values are safety factors.\n * These restrictions are not strictly necessary but are made for simplicity.\n * Increasing the range will make the maps less accurate because the domain\n * is more distorted. These parameters\n * can be changed provided the unit tests are changed to test the\n * appropriate parameter ranges.\n *\n * ### Restrictions for equal z planes\n *\n * If \\f$z^+_{\\mathrm{P}1} = z^+_{\\mathrm{P}2}\\f$ or\n * \\f$z^-_{\\mathrm{P}1} = z^-_{\\mathrm{P}2}\\f$ we demand that\n * \\f$C_1^x=C_2^x\\f$ and \\f$C_1^y=C_2^y\\f$, which simplifies the cases\n * we need to test and agrees with our expected use cases.\n * We also demand\n * \\f{align}\n *   z^+_{\\mathrm{P}2} &\\geq z^-_{\\mathrm{P}2} + 0.18 R_2\n * \\f}\n * This condition is necessary because for unequal z planes,\n * \\f$\\theta_{2 \\mathrm{min}}\\f$ and\n * \\f$\\theta_{2 \\mathrm{max}}\\f$ are no longer required\n * to be on opposite sides of the equator of sphere 2 (see the paragraph below).\n * Note that for unequal z planes \\f$\\theta_{1 \\mathrm{min}}\\f$ and\n * \\f$\\theta_{1 \\mathrm{max}}\\f$ are no longer required\n * to be on opposite sides of the equator of sphere 1, but the conditions\n * in the paragraph below guarantee that\n * \\f$z^+_{\\mathrm{P}1} \\geq z^-_{\\mathrm{P}1}\\f$.\n *\n * Unlike the case with unequal z planes, we no longer require that the\n * z planes in the above figures lie above/below\n * the centers of the corresponding spheres, but we still require that\n * the z planes are not too close to the edges of those spheres.\n * The restrictions are the same as\n * Eqs. (\\f$\\ref{eq:theta_1_min_res}\\f$--\\f$\\ref{eq:theta_2_max_res}\\f$)\n * except for the following changes:\n * If \\f$z^+_{\\mathrm{P}1} = z^+_{\\mathrm{P}2}\\f$,\n * then we replace Eq. (\\f$\\ref{eq:theta_1_min_res}\\f$) with\n * \\f{align}\n *   \\label{eq:equal_plus_theta_1_min_res}\n *   0.15\\pi &< \\theta_{1 \\mathrm{min}} < 0.59\\pi,\n * \\f}\n * and furthermore, if \\f$z^+_{\\mathrm{P}1} = z^+_{\\mathrm{P}2}\\f$ and\n * \\f$\\theta_{1 \\mathrm{min}} > 0.4\\pi\\f$ we replace\n * Eqs. (\\f$\\ref{eq:theta_1_max_res}\\f$--\\f$\\ref{eq:theta_2_min_res}\\f$)\n * with\n * \\f{align}\n *   \\label{eq:equal_plus_high_theta_1_max_res}\n *   0.7\\pi &< \\theta_{1 \\mathrm{max}} < 0.85\\pi \\\\\n *   \\label{eq:equal_plus_high_theta_2_min_res}\n *   0.25\\pi &< \\theta_{2 \\mathrm{min}} < 0.75\\pi,\n * \\f}\n * but if \\f$z^+_{\\mathrm{P}1} = z^+_{\\mathrm{P}2}\\f$ and\n * \\f$\\theta_{1 \\mathrm{min}} \\leq 0.4\\pi\\f$ we replace\n * Eq. (\\f$\\ref{eq:theta_2_min_res}\\f$)\n * with\n * \\f{align}\n *   \\label{eq:equal_plus_low_theta_2_min_res}\n *   0.15\\pi &< \\theta_{2 \\mathrm{min}} < 0.75\\pi.\n * \\f}\n *\n * Similarly, if \\f$z^-_{\\mathrm{P}1} = z^-_{\\mathrm{P}2}\\f$ we replace\n * (\\f$\\ref{eq:theta_1_max_res}\\f$) with\n * \\f{align}\n *   \\label{eq:equal_minus_theta_1_max_res}\n *   0.41\\pi &< \\theta_{1 \\mathrm{max}} < 0.85\\pi,\n * \\f}\n * and furthermore, if \\f$z^-_{\\mathrm{P}1} = z^-_{\\mathrm{P}2}\\f$ and\n * \\f$\\theta_{1 \\mathrm{max}} < 0.6\\pi\\f$ we replace\n * Eqs. (\\f$\\ref{eq:theta_1_min_res}\\f$) and (\\f$\\ref{eq:theta_2_max_res}\\f$)\n * with\n * \\f{align}\n *   \\label{eq:equal_minus_high_theta_1_min_res}\n *   0.15\\pi &< \\theta_{1 \\mathrm{min}} < 0.3\\pi \\\\\n *   \\label{eq:equal_minus_high_theta_2_max_res}\n *   0.25\\pi &< \\theta_{2 \\mathrm{max}} < 0.75\\pi,\n * \\f}\n * but if \\f$z^-_{\\mathrm{P}1} = z^-_{\\mathrm{P}2}\\f$ and\n * \\f$\\theta_{1 \\mathrm{max}} \\geq 0.6\\pi\\f$ we replace\n * Eq. (\\f$\\ref{eq:theta_2_max_res}\\f$)\n * with\n * \\f{align}\n *   \\label{eq:equal_minus_low_theta_2_max_res}\n *   0.25\\pi &< \\theta_{2 \\mathrm{max}} < 0.85\\pi .\n * \\f}\n */\nclass UniformCylindricalSide {\n public:\n  static constexpr size_t dim = 3;\n  UniformCylindricalSide(const std::array<double, 3>& center_one,\n                         const std::array<double, 3>& center_two,\n                         double radius_one, double radius_two,\n                         double z_plane_plus_one, double z_plane_minus_one,\n                         double z_plane_plus_two, double z_plane_minus_two);\n  UniformCylindricalSide() = default;\n  ~UniformCylindricalSide() = default;\n  UniformCylindricalSide(UniformCylindricalSide&&) = default;\n  UniformCylindricalSide(const UniformCylindricalSide&) = default;\n  UniformCylindricalSide& operator=(const UniformCylindricalSide&) = default;\n  UniformCylindricalSide& operator=(UniformCylindricalSide&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, 3> operator()(\n      const std::array<T, 3>& source_coords) const;\n\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, 3>> inverse(\n      const std::array<double, 3>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, 3, Frame::NoFrame> inv_jacobian(\n      const std::array<T, 3>& source_coords) const;\n\n  // clang-tidy: google runtime references\n  void pup(PUP::er& p);  // NOLINT\n\n  static bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{false};\n\n private:\n  friend bool operator==(const UniformCylindricalSide& lhs,\n                         const UniformCylindricalSide& rhs);\n  std::array<double, 3> center_one_{};\n  std::array<double, 3> center_two_{};\n  double radius_one_{std::numeric_limits<double>::signaling_NaN()};\n  double radius_two_{std::numeric_limits<double>::signaling_NaN()};\n  double z_plane_plus_one_{std::numeric_limits<double>::signaling_NaN()};\n  double z_plane_minus_one_{std::numeric_limits<double>::signaling_NaN()};\n  double z_plane_plus_two_{std::numeric_limits<double>::signaling_NaN()};\n  double z_plane_minus_two_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const UniformCylindricalSide& lhs,\n                const UniformCylindricalSide& rhs);\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Wedge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n\n#include <climits>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/AutodiffInstantiationTypes.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace domain::CoordinateMaps {\nnamespace {\ntemplate <size_t Dim>\nvoid run_shared_asserts(const double radius_inner,\n                        const std::optional<double> radius_outer,\n                        const double sphericity_inner,\n                        const double sphericity_outer,\n                        const Distribution radial_distribution,\n                        const OrientationMap<Dim>& orientation_of_wedge,\n                        const std::array<double, Dim>& focal_offset) {\n  const double sqrt_dim = sqrt(Dim);\n  const bool zero_offset = (focal_offset == make_array<Dim, double>(0.0));\n\n  ASSERT(radius_inner > 0.0,\n         \"The radius of the inner surface must be greater than zero.\");\n  if (radius_outer.has_value()) {\n    // radius_outer should have a value\n    ASSERT(radius_outer.value() > radius_inner,\n           \"The radius of the outer surface must be greater than the radius of \"\n           \"the inner surface.\");\n  }\n  ASSERT(radial_distribution == Distribution::Linear or\n             (sphericity_inner == 1.0 and sphericity_outer == 1.0),\n         \"Only the 'Linear' radial distribution is supported for non-spherical \"\n         \"wedges.\");\n  if (zero_offset) {\n    // radius_outer should have a value\n    ASSERT(radius_outer.value() *\n                   ((1.0 - sphericity_outer) / sqrt_dim + sphericity_outer) >\n               radius_inner *\n                   ((1.0 - sphericity_inner) / sqrt_dim + sphericity_inner),\n           \"The arguments passed into the constructor for Wedge result in an \"\n           \"object where the outer surface is pierced by the inner surface.\");\n  }\n  ASSERT(\n      get(determinant(discrete_rotation_jacobian(orientation_of_wedge))) > 0.0,\n      \"Wedge rotations must be done in such a manner that the sign of \"\n      \"the determinant of the discrete rotation is positive. This is to \"\n      \"preserve handedness of the coordinates.\");\n}\n}  // namespace\n\ntemplate <size_t Dim>\nWedge<Dim>::Wedge(const double radius_inner, const double radius_outer,\n                  const double sphericity_inner, const double sphericity_outer,\n                  OrientationMap<Dim> orientation_of_wedge,\n                  const bool with_equiangular_map,\n                  const WedgeHalves halves_to_use,\n                  const Distribution radial_distribution,\n                  const std::array<double, Dim - 1>& opening_angles,\n                  const bool with_adapted_equiangular_map)\n    : radius_inner_(radius_inner),\n      radius_outer_(radius_outer),\n      sphericity_inner_(sphericity_inner),\n      sphericity_outer_(sphericity_outer),\n      cube_half_length_(std::nullopt),\n      focal_offset_(make_array<Dim, double>(0.0)),\n      orientation_of_wedge_(std::move(orientation_of_wedge)),\n      with_equiangular_map_(with_equiangular_map),\n      halves_to_use_(halves_to_use),\n      radial_distribution_(radial_distribution),\n      opening_angles_(opening_angles) {\n  ASSERT(sphericity_inner >= 0.0 and sphericity_inner <= 1.0,\n         \"Sphericity of the inner surface must be between 0 and 1\");\n  ASSERT(sphericity_outer >= 0.0 and sphericity_outer <= 1.0,\n         \"Sphericity of the outer surface must be between 0 and 1\");\n  run_shared_asserts(radius_inner_, radius_outer_, sphericity_inner_,\n                     sphericity_outer_, radial_distribution_,\n                     orientation_of_wedge_, focal_offset_);\n  ASSERT(opening_angles_ != make_array<Dim - 1>(M_PI_2) ? with_equiangular_map\n                                                        : true,\n         \"If using opening angles other than pi/2, then the \"\n         \"equiangular map option must be turned on.\");\n\n  if (radial_distribution_ == Distribution::Linear) {\n    const double sqrt_dim = sqrt(double{Dim});\n    sphere_zero_ = 0.5 * (sphericity_outer_ * radius_outer +\n                          sphericity_inner * radius_inner);\n    sphere_rate_ = 0.5 * (sphericity_outer_ * radius_outer -\n                          sphericity_inner * radius_inner);\n    scaled_frustum_zero_ = 0.5 / sqrt_dim *\n                           ((1.0 - sphericity_outer_) * radius_outer +\n                            (1.0 - sphericity_inner) * radius_inner);\n    scaled_frustum_rate_ = 0.5 / sqrt_dim *\n                           ((1.0 - sphericity_outer_) * radius_outer -\n                            (1.0 - sphericity_inner) * radius_inner);\n  } else if (radial_distribution_ == Distribution::Logarithmic) {\n    scaled_frustum_zero_ = 0.0;\n    sphere_zero_ = 0.5 * (log(radius_outer * radius_inner));\n    scaled_frustum_rate_ = 0.0;\n    sphere_rate_ = 0.5 * (log(radius_outer / radius_inner));\n  } else if (radial_distribution_ == Distribution::Inverse) {\n    scaled_frustum_zero_ = 0.0;\n    // Most places where sphere_zero_ and sphere_rate_ would be used\n    // would cause precision issues for large wedges, so we don't\n    // define them.\n    // 0.5 * (radius_inner + radius_outer) / radius_inner / radius_outer;\n    sphere_zero_ = std::numeric_limits<double>::signaling_NaN();\n    scaled_frustum_rate_ = 0.0;\n    // 0.5 * (radius_inner - radius_outer) / radius_inner / radius_outer;\n    sphere_rate_ = std::numeric_limits<double>::signaling_NaN();\n  } else {\n    ERROR(\"Unsupported radial distribution: \" << radial_distribution_);\n  }\n  if (with_adapted_equiangular_map) {\n    opening_angles_distribution_ = opening_angles_;\n  } else {\n    opening_angles_distribution_ = make_array<Dim - 1>(M_PI_2);\n  }\n}\n\ntemplate <size_t Dim>\nWedge<Dim>::Wedge(\n    const double radius_inner, const std::optional<double> radius_outer,\n    const double cube_half_length, const std::array<double, Dim> focal_offset,\n    OrientationMap<Dim> orientation_of_wedge, const bool with_equiangular_map,\n    const WedgeHalves halves_to_use, const Distribution radial_distribution)\n    : radius_inner_(radius_inner),\n      radius_outer_((focal_offset == make_array<Dim>(0.0))\n                        ? radius_outer.value_or(sqrt(Dim) * cube_half_length)\n                        : radius_outer),\n      sphericity_inner_(1.0),\n      sphericity_outer_(radius_outer.has_value() ? 1.0 : 0.0),\n      cube_half_length_((focal_offset == make_array<Dim>(0.0))\n                            ? std::nullopt\n                            : std::optional<double>(cube_half_length)),\n      focal_offset_(focal_offset),\n      orientation_of_wedge_(std::move(orientation_of_wedge)),\n      with_equiangular_map_(with_equiangular_map),\n      halves_to_use_(halves_to_use),\n      radial_distribution_(radial_distribution),\n      opening_angles_((focal_offset == make_array<Dim>(0.0))\n                          ? std::optional<std::array<double, Dim - 1>>(\n                                make_array<Dim - 1>(M_PI_2))\n                          : std::nullopt),\n      opening_angles_distribution_(\n          (focal_offset == make_array<Dim>(0.0))\n              ? std::optional<std::array<double, Dim - 1>>(\n                    make_array<Dim - 1>(M_PI_2))\n              : std::nullopt) {\n  run_shared_asserts(radius_inner_, radius_outer_, sphericity_inner_,\n                     sphericity_outer_, radial_distribution_,\n                     orientation_of_wedge_, focal_offset_);\n\n  const bool zero_offset = (focal_offset_ == make_array<Dim, double>(0.0));\n  if (not zero_offset) {\n    // Do checks specific to an offset Wedge\n    ASSERT(sphericity_inner_ == 1.0,\n           \"Focal offsets are not supported for inner sphericity < 1.0\");\n    ASSERT(sphericity_outer_ == 0.0 or sphericity_outer_ == 1.0,  // NOLINT\n           \"Focal offsets are only supported for wedges with outer sphericity \"\n           \"of 1.0 or 0.0\");\n\n    // coord of focal_offset_ with largest magnitude\n    const double max_abs_focal_offset_coord = *alg::max_element(\n        focal_offset_,\n        [](const int& a, const int& b) { return abs(a) < abs(b); });\n\n    if (radius_outer_.has_value()) {\n      // note: this assert may be more restrictive than we need, can be revisted\n      // if needed\n      ASSERT(\n          max_abs_focal_offset_coord + radius_outer_.value() < cube_half_length,\n          \"For a spherical focally offset Wedge, the sum of the outer radius \"\n          \"and the coordinate of the focal offset with the largest magnitude \"\n          \"must be less than the cube half length. In other words, the \"\n          \"spherical surface at the given outer radius centered at the focal \"\n          \"offset must not pierce the cube of length 2 * cube_half_length_ \"\n          \"centered at the origin. See the Wedge class documentation for a \"\n          \"visual representation of this sphere and cube.\");\n\n    } else {\n      // if sphericity_outer_= 0.0, the outer surface of the Wedge is the parent\n      // surface\n      ASSERT(\n          max_abs_focal_offset_coord + radius_inner_ < cube_half_length,\n          \"For a cubical focally offset Wedge, the sum of the inner radius \"\n          \"and the coordinate of the focal offset with the largest magnitude \"\n          \"must be less than the cube half length. In other words, the \"\n          \"spherical surface at the given inner radius centered at the focal \"\n          \"offset must not pierce the cube of length 2 * cube_half_length_ \"\n          \"centered at the origin. See the Wedge class documentation for a \"\n          \"visual representation of this sphere and cube.\");\n    }\n  }\n\n  if (radial_distribution_ == Distribution::Linear) {\n    // since sphericity_inner_ == 0.0 and since `radius_outer` indicates whether\n    // the sphericity_outer_ is 1.0 or 0.0, the expressions for $F_0$, $F_1$,\n    // $S_0$ and $S_1$ simplify greatly\n    scaled_frustum_zero_ =\n        radius_outer.has_value() ? 0.0 : 0.5 * cube_half_length;\n    scaled_frustum_rate_ =\n        radius_outer.has_value() ? 0.0 : 0.5 * cube_half_length;\n    sphere_zero_ = 0.5 * (radius_outer.value_or(0.0) + radius_inner);\n    sphere_rate_ = 0.5 * (radius_outer.value_or(0.0) - radius_inner);\n  } else if (radial_distribution_ == Distribution::Logarithmic) {\n    scaled_frustum_zero_ = 0.0;\n    scaled_frustum_rate_ = 0.0;\n    if (radius_outer.has_value()) {\n      sphere_zero_ = 0.5 * (log(radius_outer.value() * radius_inner));\n      sphere_rate_ = 0.5 * (log(radius_outer.value() / radius_inner));\n    } else {\n      // if we reach here, radius_outer == std::nullopt, which means a flat\n      // outer surface, which is only supported for Linear radial distributions\n      ERROR(\n          \"Logarithmic radial distribution is only supported for spherical \"\n          \"wedges\");\n    }\n  } else if (radial_distribution_ == Distribution::Inverse) {\n    scaled_frustum_zero_ = 0.0;\n    // Most places where sphere_zero_ and sphere_rate_ would be used\n    // would cause precision issues for large wedges, so we don't\n    // define them.\n    // 0.5 * (radius_inner + radius_outer) / radius_inner / radius_outer;\n    sphere_zero_ = std::numeric_limits<double>::signaling_NaN();\n    scaled_frustum_rate_ = 0.0;\n    // 0.5 * (radius_inner - radius_outer) / radius_inner / radius_outer;\n    sphere_rate_ = std::numeric_limits<double>::signaling_NaN();\n  } else {\n    ERROR(\"Unsupported radial distribution: \" << radial_distribution_);\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <bool FuncIsXi, typename T>\ntt::remove_cvref_wrap_t<T> Wedge<Dim>::get_cap_angular_function(\n    const T& lowercase_xi_or_eta) const {\n  constexpr auto cap_index = static_cast<size_t>(not FuncIsXi);\n  if (opening_angles_.has_value() and\n      opening_angles_distribution_.has_value()) {\n    return with_equiangular_map_\n               ? tan(0.5 * opening_angles_.value()[cap_index]) *\n                     tan(0.5 * opening_angles_distribution_.value()[cap_index] *\n                         lowercase_xi_or_eta) /\n                     tan(0.5 * opening_angles_distribution_.value()[cap_index])\n               : lowercase_xi_or_eta;\n  } else {\n    return with_equiangular_map_ ? tan(M_PI_4 * lowercase_xi_or_eta)\n                                 : lowercase_xi_or_eta;\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <bool FuncIsXi, typename T>\ntt::remove_cvref_wrap_t<T> Wedge<Dim>::get_deriv_cap_angular_function(\n    const T& lowercase_xi_or_eta) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  constexpr auto cap_index = static_cast<size_t>(not FuncIsXi);\n  if (opening_angles_.has_value() and\n      opening_angles_distribution_.has_value()) {\n    return with_equiangular_map_\n               ? 0.5 * opening_angles_distribution_.value()[cap_index] *\n                     tan(0.5 * opening_angles_.value()[cap_index]) /\n                     tan(0.5 *\n                         opening_angles_distribution_.value()[cap_index]) /\n                     square(cos(\n                         0.5 * opening_angles_distribution_.value()[cap_index] *\n                         lowercase_xi_or_eta))\n               : make_with_value<ReturnType>(lowercase_xi_or_eta, 1.0);\n  } else {\n    return with_equiangular_map_\n               ? M_PI_4 / square(cos(M_PI_4 * lowercase_xi_or_eta))\n               : make_with_value<ReturnType>(lowercase_xi_or_eta, 1.0);\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Wedge<Dim>::get_rho_vec(\n    const std::array<double, Dim>& rotated_focus,\n    const std::array<tt::remove_cvref_wrap_t<T>, Dim - 1>& cap) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  ASSERT(cube_half_length_.has_value() !=\n             (rotated_focus == make_array<Dim, double>(0.0)),\n         \"The rotated focus should be zero for a centered Wedge and non-zero \"\n         \"for an offset Wedge.\");\n  const bool zero_offset = not cube_half_length_.has_value();\n\n  std::array<ReturnType, Dim> rho_vec{};\n  rho_vec[polar_coord] =\n      zero_offset\n          ? cap[0]\n          : (cap[0] - rotated_focus[polar_coord] / cube_half_length_.value());\n  rho_vec[radial_coord] =\n      zero_offset ? make_with_value<ReturnType>(cap[0], 1.0)\n                  : make_with_value<ReturnType>(cap[0], 1.0) -\n                        rotated_focus[radial_coord] / cube_half_length_.value();\n  if constexpr (Dim == 3) {\n    rho_vec[azimuth_coord] =\n        zero_offset\n            ? cap[1]\n            : cap[1] - rotated_focus[azimuth_coord] / cube_half_length_.value();\n  }\n\n  return rho_vec;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntt::remove_cvref_wrap_t<T> Wedge<Dim>::get_one_over_rho(\n    const std::array<double, Dim>& rotated_focus,\n    const std::array<tt::remove_cvref_wrap_t<T>, Dim - 1>& cap) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  ReturnType one_over_rho;\n\n  ASSERT(cube_half_length_.has_value() !=\n             (rotated_focus == make_array<Dim, double>(0.0)),\n         \"The rotated focus should be zero for a centered Wedge and non-zero \"\n         \"for an offset Wedge.\");\n  const bool zero_offset = not cube_half_length_.has_value();\n\n  if (zero_offset) {\n    one_over_rho = 1.0 + square(cap[0]);\n  } else {\n    one_over_rho =\n        square(1.0 - rotated_focus[radial_coord] / cube_half_length_.value()) +\n        square(cap[0] - rotated_focus[polar_coord] / cube_half_length_.value());\n  }\n  if constexpr (Dim == 3) {\n    if (zero_offset) {\n      one_over_rho += square(cap[1]);\n    } else {\n      one_over_rho += square(cap[1] - rotated_focus[azimuth_coord] /\n                                          cube_half_length_.value());\n    }\n  }\n  one_over_rho = 1.0 / sqrt(one_over_rho);\n\n  return one_over_rho;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntt::remove_cvref_wrap_t<T> Wedge<Dim>::get_s_factor(const T& zeta) const {\n  if (radial_distribution_ == Distribution::Linear) {\n    return (sphere_zero_ + sphere_rate_ * zeta);\n  } else if (radial_distribution_ == Distribution::Logarithmic) {\n    return exp(sphere_zero_ + sphere_rate_ * zeta);\n  } else if (radial_distribution_ == Distribution::Inverse) {\n    if (radius_outer_.has_value()) {\n      return 2.0 / ((1.0 + zeta) / radius_outer_.value() +\n                    (1.0 - zeta) / radius_inner_);\n    } else {\n      // if we reach here, radius_outer == std::nullopt, which means a flat\n      // outer surface, which is only supported for Linear radial distributions\n      ERROR(\n          \"Inverse radial distribution is only supported for spherical wedges\");\n    }\n  } else {\n    ERROR(\"Unsupported radial distribution: \" << radial_distribution_);\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntt::remove_cvref_wrap_t<T> Wedge<Dim>::get_s_factor_deriv(\n    const T& zeta, const T& s_factor) const {\n  if (radial_distribution_ == Distribution::Linear) {\n    return make_with_value<T>(zeta, sphere_rate_);\n  } else if (radial_distribution_ == Distribution::Logarithmic) {\n    if (radius_outer_.has_value()) {\n      return 0.5 * s_factor * log(radius_outer_.value() / radius_inner_);\n    } else {\n      // if we reach here, radius_outer == std::nullopt, which means a flat\n      // outer surface, which is only supported for Linear radial distributions\n      ERROR(\n          \"Logarithmic radial distribution is only supported for spherical \"\n          \"wedges\");\n    }\n  } else if (radial_distribution_ == Distribution::Inverse) {\n    if (radius_outer_.has_value()) {\n      return 2.0 *\n             ((radius_inner_ * square(radius_outer_.value())) -\n              square(radius_inner_) * radius_outer_.value()) /\n             square(radius_inner_ + radius_outer_.value() +\n                    zeta * (radius_inner_ - radius_outer_.value()));\n    } else {\n      // if we reach here, radius_outer == std::nullopt, which means a flat\n      // outer surface, which is only supported for Linear radial distributions\n      ERROR(\n          \"Inverse radial distribution is only supported for spherical wedges\");\n    }\n  } else {\n    ERROR(\"Unsupported radial distribution: \" << radial_distribution_);\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntt::remove_cvref_wrap_t<T> Wedge<Dim>::get_generalized_z(\n    const T& zeta, const T& one_over_rho, const T& s_factor) const {\n  if (radial_distribution_ == Distribution::Linear) {\n    return s_factor * one_over_rho +\n           (scaled_frustum_zero_ + scaled_frustum_rate_ * zeta);\n  } else if (radial_distribution_ == Distribution::Logarithmic or\n             radial_distribution_ == Distribution::Inverse) {\n    return s_factor * one_over_rho;\n  } else {\n    ERROR(\"Unsupported radial distribution: \" << radial_distribution_);\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntt::remove_cvref_wrap_t<T> Wedge<Dim>::get_generalized_z(\n    const T& zeta, const T& one_over_rho) const {\n  return get_generalized_z(zeta, one_over_rho, get_s_factor(zeta));\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Wedge<Dim>::get_d_generalized_z(\n    const T& zeta, const T& one_over_rho, const T& s_factor,\n    const std::array<tt::remove_cvref_wrap_t<T>, Dim - 1>& cap_deriv,\n    const std::array<tt::remove_cvref_wrap_t<T>, Dim>& rho_vec) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  const ReturnType one_over_rho_cubed = pow<3>(one_over_rho);\n  const ReturnType s_factor_over_rho_cubed = s_factor * one_over_rho_cubed;\n\n  std::array<ReturnType, Dim> d_generalized_z{};\n  // Polar angle\n  d_generalized_z[polar_coord] =\n      -s_factor_over_rho_cubed * cap_deriv[0] * rho_vec[polar_coord];\n  // Radial coordinate\n  if (radial_distribution_ == Distribution::Linear) {\n    // note: sphere_rate_ = s_factor_deriv for Linear\n    d_generalized_z[radial_coord] =\n        sphere_rate_ * one_over_rho + scaled_frustum_rate_;\n  } else if (radial_distribution_ == Distribution::Logarithmic or\n             radial_distribution_ == Distribution::Inverse) {\n    const ReturnType s_factor_deriv = get_s_factor_deriv(zeta, s_factor);\n    d_generalized_z[radial_coord] = s_factor_deriv * one_over_rho;\n  } else {\n    ERROR(\"Unsupported radial distribution: \" << radial_distribution_);\n  }\n  if constexpr (Dim == 3) {\n    // Azimuthal angle\n    d_generalized_z[azimuth_coord] =\n        -s_factor_over_rho_cubed * cap_deriv[1] * rho_vec[azimuth_coord];\n  }\n\n  return d_generalized_z;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, Dim> Wedge<Dim>::operator()(\n    const std::array<T, Dim>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  // Radial coordinate\n  const ReturnType& zeta = source_coords[radial_coord];\n\n  // Polar angle\n  ReturnType xi = source_coords[polar_coord];\n  if (halves_to_use_ == WedgeHalves::UpperOnly) {\n    xi += 1.0;\n    xi *= 0.5;\n  } else if (halves_to_use_ == WedgeHalves::LowerOnly) {\n    xi -= 1.0;\n    xi *= 0.5;\n  }\n\n  std::array<ReturnType, Dim - 1> cap{};\n  cap[0] = get_cap_angular_function<true>(xi);\n  if constexpr (Dim == 3) {\n    // Azimuthal angle\n    const ReturnType& eta = source_coords[azimuth_coord];\n    cap[1] = get_cap_angular_function<false>(eta);\n  }\n\n  const auto rotated_focus =\n      discrete_rotation(orientation_of_wedge_.inverse_map(), focal_offset_);\n  const ReturnType one_over_rho = get_one_over_rho<T>(rotated_focus, cap);\n  const ReturnType generalized_z = get_generalized_z(zeta, one_over_rho);\n\n  ASSERT(cube_half_length_.has_value() !=\n             (rotated_focus == make_array<Dim, double>(0.0)),\n         \"The rotated focus should be zero for a centered Wedge and non-zero \"\n         \"for an offset Wedge.\");\n  const bool zero_offset = not cube_half_length_.has_value();\n\n  std::array<ReturnType, Dim> physical_coords{};\n  physical_coords[radial_coord] =\n      zero_offset ? generalized_z\n                  : generalized_z * (1.0 - rotated_focus[radial_coord] /\n                                               cube_half_length_.value()) +\n                        rotated_focus[radial_coord];\n  if (zero_offset) {\n    physical_coords[polar_coord] = generalized_z * cap[0];\n  } else {\n    physical_coords[polar_coord] =\n        generalized_z *\n            (cap[0] - rotated_focus[polar_coord] / cube_half_length_.value()) +\n        rotated_focus[polar_coord];\n  }\n  if constexpr (Dim == 3) {\n    if (zero_offset) {\n      physical_coords[azimuth_coord] = generalized_z * cap[1];\n    } else {\n      physical_coords[azimuth_coord] =\n          generalized_z * (cap[1] - rotated_focus[azimuth_coord] /\n                                        cube_half_length_.value()) +\n          rotated_focus[azimuth_coord];\n    }\n  }\n  return discrete_rotation(orientation_of_wedge_, std::move(physical_coords));\n}\n\ntemplate <size_t Dim>\nstd::optional<std::array<double, Dim>> Wedge<Dim>::inverse(\n    const std::array<double, Dim>& target_coords) const {\n  const std::array<double, Dim> physical_coords =\n      discrete_rotation(orientation_of_wedge_.inverse_map(), target_coords);\n  const auto rotated_focus =\n      discrete_rotation(orientation_of_wedge_.inverse_map(), focal_offset_);\n\n  if (physical_coords[radial_coord] < rotated_focus[radial_coord] or\n      equal_within_roundoff(physical_coords[radial_coord],\n                            rotated_focus[radial_coord])) {\n    return std::nullopt;\n  }\n\n  ASSERT(cube_half_length_.has_value() !=\n             (rotated_focus == make_array<Dim, double>(0.0)),\n         \"The rotated focus should be zero for a centered Wedge and non-zero \"\n         \"for an offset Wedge.\");\n  const bool zero_offset = not cube_half_length_.has_value();\n\n  const double generalized_z =\n      zero_offset\n          ? physical_coords[radial_coord]\n          : (physical_coords[radial_coord] - rotated_focus[radial_coord]) /\n                (1.0 - rotated_focus[radial_coord] / cube_half_length_.value());\n  const double one_over_generalized_z = 1.0 / generalized_z;\n\n  std::array<double, Dim - 1> cap{};\n  cap[0] = zero_offset\n               ? physical_coords[polar_coord] * one_over_generalized_z\n               : (physical_coords[polar_coord] - rotated_focus[polar_coord]) *\n                         one_over_generalized_z +\n                     rotated_focus[polar_coord] / cube_half_length_.value();\n  if constexpr (Dim == 3) {\n    cap[1] =\n        zero_offset\n            ? physical_coords[azimuth_coord] * one_over_generalized_z\n            : (physical_coords[azimuth_coord] - rotated_focus[azimuth_coord]) *\n                      one_over_generalized_z +\n                  rotated_focus[azimuth_coord] / cube_half_length_.value();\n  }\n\n  // Radial coordinate\n  double zeta = std::numeric_limits<double>::signaling_NaN();\n  const double radius = magnitude(physical_coords - rotated_focus);\n  if (radial_distribution_ == Distribution::Linear) {\n    const double one_over_rho = generalized_z / radius;\n    const double zeta_coefficient =\n        (scaled_frustum_rate_ + sphere_rate_ * one_over_rho);\n    // If -sphere_rate_/scaled_frustum_rate_ > 1, then\n    // there exists a cone in x,y,z space given by the surface\n    // zeta_coefficient=0; the map is singular on this surface.\n    // We return nullopt if we are on or outside this cone.\n    // If scaled_frustum_rate_ > 0, then outside the cone\n    // corresponds to zeta_coefficient > 0, and if scaled_frustum_rate_\n    // < 0, then outside the cone corresponds to zeta_coefficient < 0.\n    // We test in two cases, and avoid division.\n    if ((scaled_frustum_rate_ > 0.0 and scaled_frustum_rate_ < -sphere_rate_ and\n         zeta_coefficient > 0.0) or\n        (scaled_frustum_rate_ < 0.0 and scaled_frustum_rate_ > -sphere_rate_ and\n         zeta_coefficient < 0.0) or\n        equal_within_roundoff(zeta_coefficient, 0.0)) {\n      return std::nullopt;\n    }\n    const auto z_zero = (scaled_frustum_zero_ + sphere_zero_ * one_over_rho);\n    zeta = (generalized_z - z_zero) / zeta_coefficient;\n  } else if (radial_distribution_ == Distribution::Logarithmic) {\n    zeta = (log(radius) - sphere_zero_) / sphere_rate_;\n  } else if (radial_distribution_ == Distribution::Inverse) {\n    double radius_outer_or_radius_bounding_cube =\n        std::numeric_limits<double>::signaling_NaN();\n    if (radius_outer_.has_value()) {\n      radius_outer_or_radius_bounding_cube = radius_outer_.value();\n    } else if (cube_half_length_.has_value()) {\n      radius_outer_or_radius_bounding_cube =\n          sqrt(Dim) * cube_half_length_.value();\n    } else {\n      ERROR(\n          \"This indicates an error in the logic of Wedge. A Wedge that has no \"\n          \"value for radius_outer_ should still have a value for \"\n          \"cube_half_length_, and vice versa.\");\n    }\n\n    zeta =\n        (radius_inner_ * (radius_outer_or_radius_bounding_cube / radius - 1.0) +\n         radius_outer_or_radius_bounding_cube *\n             (radius_inner_ / radius - 1.0)) /\n        (radius_inner_ - radius_outer_or_radius_bounding_cube);\n  } else {\n    ERROR(\"Unsupported radial distribution: \" << radial_distribution_);\n  }\n\n  // Polar angle\n  double xi = std::numeric_limits<double>::signaling_NaN();\n  if (opening_angles_.has_value() and\n      opening_angles_distribution_.has_value()) {\n    xi = with_equiangular_map_\n             ? 2.0 *\n                   atan(tan(0.5 * opening_angles_distribution_.value()[0]) /\n                        tan(0.5 * opening_angles_.value()[0]) * cap[0]) /\n                   opening_angles_distribution_.value()[0]\n             : cap[0];\n  } else {\n    xi = with_equiangular_map_ ? atan(1.0 * cap[0]) / M_PI_4 : cap[0];\n  }\n\n  if (halves_to_use_ == WedgeHalves::UpperOnly) {\n    xi *= 2.0;\n    xi -= 1.0;\n  } else if (halves_to_use_ == WedgeHalves::LowerOnly) {\n    xi *= 2.0;\n    xi += 1.0;\n  }\n\n  std::array<double, Dim> logical_coords{};\n  logical_coords[radial_coord] = zeta;\n  logical_coords[polar_coord] = xi;\n  if constexpr (Dim == 3) {\n    // Azimuthal angle\n    if (opening_angles_.has_value() and\n        opening_angles_distribution_.has_value()) {\n      logical_coords[azimuth_coord] =\n          with_equiangular_map_\n              ? 2.0 *\n                    atan(tan(0.5 * opening_angles_distribution_.value()[1]) /\n                         tan(0.5 * opening_angles_.value()[1]) * cap[1]) /\n                    opening_angles_distribution_.value()[1]\n              : cap[1];\n    } else {\n      logical_coords[azimuth_coord] =\n          with_equiangular_map_ ? atan(1.0 * cap[1]) / M_PI_4 : cap[1];\n    }\n  }\n  return logical_coords;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> Wedge<Dim>::jacobian(\n    const std::array<T, Dim>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  // Radial coordinate\n  const ReturnType& zeta = source_coords[radial_coord];\n\n  // Polar angle\n  ReturnType xi = source_coords[polar_coord];\n  if (halves_to_use_ == WedgeHalves::UpperOnly) {\n    xi += 1.0;\n    xi *= 0.5;\n  } else if (halves_to_use_ == WedgeHalves::LowerOnly) {\n    xi -= 1.0;\n    xi *= 0.5;\n  }\n\n  std::array<ReturnType, Dim - 1> cap{};\n  std::array<ReturnType, Dim - 1> cap_deriv{};\n  cap[0] = get_cap_angular_function<true>(xi);\n  cap_deriv[0] = get_deriv_cap_angular_function<true>(xi);\n  if constexpr (Dim == 3) {\n    // Azimuthal angle\n    const ReturnType& eta = source_coords[azimuth_coord];\n    cap[1] = get_cap_angular_function<false>(eta);\n    cap_deriv[1] = get_deriv_cap_angular_function<false>(eta);\n  }\n\n  const auto rotated_focus =\n      discrete_rotation(orientation_of_wedge_.inverse_map(), focal_offset_);\n  const std::array<ReturnType, Dim> rho_vec =\n      get_rho_vec<T>(rotated_focus, cap);\n  const ReturnType one_over_rho = 1.0 / magnitude(rho_vec);\n  const ReturnType s_factor = get_s_factor(zeta);\n  const ReturnType generalized_z =\n      get_generalized_z(zeta, one_over_rho, s_factor);\n  const std::array<ReturnType, Dim> d_generalized_z =\n      get_d_generalized_z(zeta, one_over_rho, s_factor, cap_deriv, rho_vec);\n\n  auto jacobian_matrix =\n      make_with_value<tnsr::Ij<ReturnType, Dim, Frame::NoFrame>>(xi, 0.0);\n\n  // Derivative by polar angle\n  std::array<ReturnType, Dim> dxyz_dxi{};\n  dxyz_dxi[radial_coord] = rho_vec[radial_coord] * d_generalized_z[polar_coord];\n  dxyz_dxi[polar_coord] = rho_vec[polar_coord] * d_generalized_z[polar_coord] +\n                          cap_deriv[0] * generalized_z;\n\n  if constexpr (Dim == 3) {\n    dxyz_dxi[azimuth_coord] =\n        rho_vec[azimuth_coord] * d_generalized_z[polar_coord];\n  }\n\n  // Implement Scalings:\n  if (halves_to_use_ != WedgeHalves::Both) {\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(dxyz_dxi, d) *= 0.5;\n    }\n  }\n\n  std::array<ReturnType, Dim> dX_dlogical =\n      discrete_rotation(orientation_of_wedge_, std::move(dxyz_dxi));\n  get<0, polar_coord>(jacobian_matrix) = dX_dlogical[0];\n  get<1, polar_coord>(jacobian_matrix) = dX_dlogical[1];\n  if constexpr (Dim == 3) {\n    get<2, polar_coord>(jacobian_matrix) = dX_dlogical[2];\n  }\n\n  // Derivative by azimuthal angle\n  if constexpr (Dim == 3) {\n    std::array<ReturnType, Dim> dxyz_deta{};\n    dxyz_deta[radial_coord] =\n        rho_vec[radial_coord] * d_generalized_z[azimuth_coord];\n\n    dxyz_deta[azimuth_coord] =\n        rho_vec[azimuth_coord] * d_generalized_z[azimuth_coord] +\n        cap_deriv[1] * generalized_z;\n\n    dxyz_deta[polar_coord] =\n        rho_vec[polar_coord] * d_generalized_z[azimuth_coord];\n\n    dX_dlogical =\n        discrete_rotation(orientation_of_wedge_, std::move(dxyz_deta));\n    get<0, azimuth_coord>(jacobian_matrix) = dX_dlogical[0];\n    get<1, azimuth_coord>(jacobian_matrix) = dX_dlogical[1];\n    get<2, azimuth_coord>(jacobian_matrix) = dX_dlogical[2];\n  }\n\n  // Derivative by radial coordinate\n  std::array<ReturnType, Dim> dxyz_dzeta{};\n  dxyz_dzeta[radial_coord] =\n      rho_vec[radial_coord] * d_generalized_z[radial_coord];\n  dxyz_dzeta[polar_coord] =\n      rho_vec[polar_coord] * d_generalized_z[radial_coord];\n\n  if constexpr (Dim == 3) {\n    dxyz_dzeta[azimuth_coord] =\n        rho_vec[azimuth_coord] * d_generalized_z[radial_coord];\n  }\n\n  dX_dlogical = discrete_rotation(orientation_of_wedge_, std::move(dxyz_dzeta));\n  get<0, radial_coord>(jacobian_matrix) = dX_dlogical[0];\n  get<1, radial_coord>(jacobian_matrix) = dX_dlogical[1];\n  if constexpr (Dim == 3) {\n    get<2, radial_coord>(jacobian_matrix) = dX_dlogical[2];\n  }\n\n  return jacobian_matrix;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame>\nWedge<Dim>::inv_jacobian(const std::array<T, Dim>& source_coords) const {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n\n  // Radial coordinate\n  const ReturnType& zeta = source_coords[radial_coord];\n\n  // Polar angle\n  ReturnType xi = source_coords[polar_coord];\n  if (halves_to_use_ == WedgeHalves::UpperOnly) {\n    xi += 1.0;\n    xi *= 0.5;\n  } else if (halves_to_use_ == WedgeHalves::LowerOnly) {\n    xi -= 1.0;\n    xi *= 0.5;\n  }\n\n\n  std::array<ReturnType, Dim - 1> cap{};\n  std::array<ReturnType, Dim - 1> cap_deriv{};\n  cap[0] = get_cap_angular_function<true>(xi);\n  cap_deriv[0] = get_deriv_cap_angular_function<true>(xi);\n  if constexpr (Dim == 3) {\n    // Azimuthal angle\n    const ReturnType& eta = source_coords[azimuth_coord];\n    cap[1] = get_cap_angular_function<false>(eta);\n    cap_deriv[1] = get_deriv_cap_angular_function<false>(eta);\n  }\n\n  const auto rotated_focus =\n      discrete_rotation(orientation_of_wedge_.inverse_map(), focal_offset_);\n  const std::array<ReturnType, Dim> rho_vec =\n      get_rho_vec<T>(rotated_focus, cap);\n  const ReturnType one_over_rho = 1.0 / magnitude(rho_vec);\n  const ReturnType s_factor = get_s_factor(zeta);\n  const ReturnType generalized_z =\n      get_generalized_z(zeta, one_over_rho, s_factor);\n  const ReturnType one_over_generalized_z = 1.0 / generalized_z;\n  const std::array<ReturnType, Dim> d_generalized_z =\n      get_d_generalized_z(zeta, one_over_rho, s_factor, cap_deriv, rho_vec);\n  const ReturnType one_over_d_generalized_z_dzeta =\n      1.0 / d_generalized_z[radial_coord];\n  const ReturnType one_over_rho_z = 1.0 / rho_vec[radial_coord];\n  const ReturnType scaled_z_frustum =\n      scaled_frustum_zero_ + scaled_frustum_rate_ * zeta;\n\n  auto inv_jacobian_matrix =\n      make_with_value<tnsr::Ij<ReturnType, Dim, Frame::NoFrame>>(xi, 0.0);\n\n  // Derivatives of polar angle\n  std::array<ReturnType, Dim> dxi_dxyz{};\n  dxi_dxyz[polar_coord] = 1.0 / (generalized_z * cap_deriv[0]);\n  // Implement Scalings:\n  if (halves_to_use_ != WedgeHalves::Both) {\n    dxi_dxyz[polar_coord] *= 2.0;\n  }\n\n  dxi_dxyz[radial_coord] =\n      -dxi_dxyz[polar_coord] * one_over_rho_z * rho_vec[polar_coord];\n\n  if constexpr (Dim == 3) {\n    dxi_dxyz[azimuth_coord] = make_with_value<ReturnType>(xi, 0.0);\n  }\n\n  std::array<ReturnType, Dim> dlogical_dX =\n      discrete_rotation(orientation_of_wedge_, std::move(dxi_dxyz));\n  get<polar_coord, 0>(inv_jacobian_matrix) = dlogical_dX[0];\n  get<polar_coord, 1>(inv_jacobian_matrix) = dlogical_dX[1];\n  if constexpr (Dim == 3) {\n    get<polar_coord, 2>(inv_jacobian_matrix) = dlogical_dX[2];\n  }\n\n  // Derivatives of radial coordinate\n\n  // a common term that appears in the Jacobian, see Wedge docs\n  const ReturnType T_factor =\n      s_factor * one_over_d_generalized_z_dzeta * pow<3>(one_over_rho);\n\n  std::array<ReturnType, Dim> dzeta_dxyz{};\n  dzeta_dxyz[polar_coord] =\n      T_factor * rho_vec[polar_coord] * one_over_generalized_z;\n  dzeta_dxyz[radial_coord] =\n      one_over_generalized_z *\n      (one_over_rho_z * scaled_z_frustum * one_over_d_generalized_z_dzeta +\n       T_factor * rho_vec[radial_coord]);\n  if constexpr (Dim == 3) {\n    dzeta_dxyz[azimuth_coord] =\n        T_factor * rho_vec[azimuth_coord] * one_over_generalized_z;\n  }\n\n  dlogical_dX = discrete_rotation(orientation_of_wedge_, std::move(dzeta_dxyz));\n  get<radial_coord, 0>(inv_jacobian_matrix) = dlogical_dX[0];\n  get<radial_coord, 1>(inv_jacobian_matrix) = dlogical_dX[1];\n  if constexpr (Dim == 3) {\n    get<radial_coord, 2>(inv_jacobian_matrix) = dlogical_dX[2];\n  }\n\n  if constexpr (Dim == 3) {\n    // Derivatives of azimuthal angle\n    std::array<ReturnType, Dim> deta_dxyz{};\n    deta_dxyz[polar_coord] = make_with_value<ReturnType>(xi, 0.0);\n    deta_dxyz[azimuth_coord] = 1.0 / (generalized_z * cap_deriv[1]);\n    deta_dxyz[radial_coord] =\n        -deta_dxyz[azimuth_coord] * one_over_rho_z * rho_vec[azimuth_coord];\n\n    dlogical_dX =\n        discrete_rotation(orientation_of_wedge_, std::move(deta_dxyz));\n    get<azimuth_coord, 0>(inv_jacobian_matrix) = dlogical_dX[0];\n    get<azimuth_coord, 1>(inv_jacobian_matrix) = dlogical_dX[1];\n    get<azimuth_coord, 2>(inv_jacobian_matrix) = dlogical_dX[2];\n  }\n\n  return inv_jacobian_matrix;\n}\n\ntemplate <size_t Dim>\nvoid Wedge<Dim>::pup(PUP::er& p) {\n  size_t version = 2;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (p.isUnpacking()) {\n    p | radius_inner_;\n\n    if (version < 2) {\n      double radius_outer = std::numeric_limits<double>::signaling_NaN();\n      p | radius_outer;\n      radius_outer_ = radius_outer;\n    } else {\n      p | radius_outer_;\n    }\n\n    p | sphericity_inner_;\n    p | sphericity_outer_;\n    p | orientation_of_wedge_;\n    p | with_equiangular_map_;\n    p | halves_to_use_;\n    p | radial_distribution_;\n    p | scaled_frustum_zero_;\n    p | sphere_zero_;\n    p | scaled_frustum_rate_;\n    p | sphere_rate_;\n\n    if (version == 0) {\n      double half_opening_angle = std::numeric_limits<double>::signaling_NaN();\n      p | half_opening_angle;\n      opening_angles_ =\n          make_array<Dim - 1>(std::numeric_limits<double>::signaling_NaN());\n      opening_angles_.value()[0] = 2. * half_opening_angle;\n      if constexpr (Dim == 3) {\n        opening_angles_.value()[1] = M_PI_2;\n      }\n      opening_angles_distribution_ = opening_angles_;\n    } else if (version == 1) {\n      std::array<double, Dim - 1> opening_angles =\n          make_array<Dim - 1>(std::numeric_limits<double>::signaling_NaN());\n      p | opening_angles;\n      opening_angles_ = opening_angles;\n\n      std::array<double, Dim - 1> opening_angles_distribution =\n          make_array<Dim - 1>(std::numeric_limits<double>::signaling_NaN());\n      p | opening_angles_distribution;\n      opening_angles_distribution_ = opening_angles_distribution;\n    } else {\n      p | opening_angles_;\n      p | opening_angles_distribution_;\n    }\n\n    if (version < 2) {\n      cube_half_length_ = std::nullopt;\n      focal_offset_ = make_array<Dim>(0.0);\n    } else {\n      p | cube_half_length_;\n      p | focal_offset_;\n    }\n    return;\n  }\n\n  p | radius_inner_;\n  p | radius_outer_;\n  p | sphericity_inner_;\n  p | sphericity_outer_;\n  p | orientation_of_wedge_;\n  p | with_equiangular_map_;\n  p | halves_to_use_;\n  p | radial_distribution_;\n  p | scaled_frustum_zero_;\n  p | sphere_zero_;\n  p | scaled_frustum_rate_;\n  p | sphere_rate_;\n  p | opening_angles_;\n  p | opening_angles_distribution_;\n  p | cube_half_length_;\n  p | focal_offset_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const Wedge<Dim>& lhs, const Wedge<Dim>& rhs) {\n  return lhs.radius_inner_ == rhs.radius_inner_ and\n         lhs.radius_outer_ == rhs.radius_outer_ and\n         lhs.cube_half_length_ == rhs.cube_half_length_ and\n         lhs.focal_offset_ == rhs.focal_offset_ and\n         lhs.orientation_of_wedge_ == rhs.orientation_of_wedge_ and\n         lhs.with_equiangular_map_ == rhs.with_equiangular_map_ and\n         lhs.halves_to_use_ == rhs.halves_to_use_ and\n         lhs.radial_distribution_ == rhs.radial_distribution_ and\n         lhs.sphericity_inner_ == rhs.sphericity_inner_ and\n         lhs.sphericity_outer_ == rhs.sphericity_outer_ and\n         lhs.scaled_frustum_zero_ == rhs.scaled_frustum_zero_ and\n         lhs.scaled_frustum_rate_ == rhs.scaled_frustum_rate_ and\n         lhs.opening_angles_ == rhs.opening_angles_ and\n         lhs.opening_angles_distribution_ == rhs.opening_angles_distribution_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Wedge<Dim>& lhs, const Wedge<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_DIM(_, data)                         \\\n  template class Wedge<DIM(data)>;                       \\\n  template bool operator==(const Wedge<DIM(data)>& lhs,  \\\n                           const Wedge<DIM(data)>& rhs); \\\n  template bool operator!=(const Wedge<DIM(data)>& lhs,  \\\n                           const Wedge<DIM(data)>& rhs);\n\n#define INSTANTIATE_DTYPE(_, data)                                     \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)> \\\n  Wedge<DIM(data)>::operator()(                                        \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  Wedge<DIM(data)>::jacobian(                                          \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;  \\\n  template tnsr::Ij<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data),   \\\n                    Frame::NoFrame>                                    \\\n  Wedge<DIM(data)>::inv_jacobian(                                      \\\n      const std::array<DTYPE(data), DIM(data)>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_DIM, (2, 3))\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_DTYPE, (2, 3),\n    (double, DataVector,\n     std::reference_wrapper<const double>,\n     std::reference_wrapper<const DataVector>))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_DTYPE, (2, 3), MAP_AUTODIFF_TYPES)\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE_DIM\n#undef INSTANTIATE_DTYPE\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordinateMaps/Wedge.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::CoordinateMaps {\n\nnamespace detail {\n// This mapping can be deleted once the 2D and 3D wedges are oriented the same\n// (see issue https://github.com/sxs-collaboration/spectre/issues/2988)\ntemplate <size_t Dim>\nstruct WedgeCoordOrientation;\ntemplate <>\nstruct WedgeCoordOrientation<2> {\n  static constexpr size_t radial_coord = 0;\n  static constexpr size_t polar_coord = 1;\n  static constexpr size_t azimuth_coord = 2;  // unused\n};\ntemplate <>\nstruct WedgeCoordOrientation<3> {\n  static constexpr size_t radial_coord = 2;\n  static constexpr size_t polar_coord = 0;\n  static constexpr size_t azimuth_coord = 1;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup CoordinateMapsGroup\n *\n * \\brief Map from a square or cube to a wedge.\n * \\image html Shell.png \"A shell can be constructed out of six wedges.\"\n *\n * \\details The mapping that goes from a reference cube (in 3D) or square (in\n * 2D) to a wedge centered on a coordinate axis covering a volume between an\n * inner surface and outer surface. Each surface can be given a curvature\n * between flat (a sphericity of 0) or spherical (a sphericity of 1).\n *\n * In 2D, the first logical coordinate corresponds to the radial coordinate,\n * and the second logical coordinate corresponds to the angular coordinate. In\n * 3D, the first two logical coordinates correspond to the two angular\n * coordinates, and the third to the radial coordinate. This difference\n * originates from separate implementations for the 2D and 3D map that were\n * merged. The 3D implementation can be changed to use the first logical\n * coordinate as the radial direction, but this requires propagating the change\n * through the rest of the domain code (see issue\n * https://github.com/sxs-collaboration/spectre/issues/2988).\n *\n * The following documentation is for the **centered** 3D map, as we will defer\n * the dicussion of `Wedge`s with a `focal_offset_` to a later section. The 2D\n * map is obtained by setting either of the two angular coordinates to zero\n * (and using \\f$\\xi\\f$ as the radial coordinate). Note that there is also a\n * normalization factor of $\\sqrt{3}$ that appears in multiple expressions in\n * the 3D case that becomes $\\sqrt{2}$ in the 2D case.\n *\n * The Wedge map is constructed by linearly interpolating between a bulged\n * face of radius `radius_inner_` to a bulged face of radius `radius_outer_`,\n * where the radius of each bulged face is defined to be the radius of the\n * sphere circumscribing the bulge.\n *\n * We make a choice here as to whether we wish to use the logical coordinates\n * parameterizing these surface as they are, in which case we have the\n * equidistant choice of coordinates, or whether to apply a tangent map to them\n * which leads us to the equiangular choice of coordinates. `Wedge`s have\n * variable `opening_angles_` which, for centered `Wedge`s, are the angular\n * sizes of the wedge in the $\\xi$ and $\\eta$ directions (for the 3D case) in\n * the target frame. By default, `Wedge`s have opening angles of $\\pi/2$, so we\n * will discuss that case here and defer both the discussion of generalized\n * opening angles and the interaction between opening angles and non-zero focal\n * offsets for later sections.\n *\n * For a Wedge with $\\xi$ and $\\eta$ opening angles of $\\pi/2$, the\n * equiangular coordinates in terms of the logical coordinates are:\n *\n * \\begin{align}\n *   \\textrm{equiangular xi} : \\Xi(\\xi) = \\textrm{tan}(\\xi\\pi/4)\n *   \\label{eq:equiangular_xi_pi_over_2}\n * \\end{align}\n *\n * \\begin{align}\n *   \\textrm{equiangular eta} :\n *       \\mathrm{H}(\\eta) =  \\textrm{tan}(\\eta\\pi/4)\n *       \\label{eq:equiangular_eta_pi_over_2}\n * \\end{align}\n *\n * With derivatives:\n *\n * \\begin{align}\n *   \\Xi'(\\xi) &= \\frac{\\pi}{4}(1+\\Xi^2) \\\\\n *   \\mathrm{H}'(\\eta) &= \\frac{\\pi}{4}(1+\\mathrm{H}^2)\n * \\end{align}\n *\n * The equidistant coordinates are:\n *\n * \\begin{align}\n *   \\textrm{equidistant xi}  : \\Xi = \\xi \\\\\n *   \\textrm{equidistant eta}  : \\mathrm{H} = \\eta\n * \\end{align}\n *\n * with derivatives:\n *\n * \\begin{align}\n *   \\Xi'(\\xi) &= 1 \\\\\n *   \\mathrm{H}'(\\eta) &= 1\n * \\end{align}\n *\n * We also define the variable \\f$\\rho\\f$, given by:\n *\n * \\begin{align}\n *   \\textrm{rho} : \\rho = \\sqrt{1+\\Xi^2+\\mathrm{H}^2}\n * \\end{align}\n *\n * ### The Spherical Face Map\n * The surface map for the spherical face of radius \\f$R\\f$ lying in the\n * \\f$+z\\f$ direction in either choice of coordinates is then given by:\n *\n * \\begin{align}\n *   \\vec{\\sigma}_{spherical}: \\vec{\\xi} \\rightarrow \\vec{x}(\\vec{\\xi})\n * \\end{align}\n *\n * Where\n *\n * \\begin{align}\n *   \\vec{x}(\\xi,\\eta) =\n *       \\begin{bmatrix}\n *         x(\\xi,\\eta) \\\\\n *         y(\\xi,\\eta) \\\\\n *         z(\\xi,\\eta) \\\\\n *       \\end{bmatrix}  =\n *           \\frac{R}{\\rho}\n *               \\begin{bmatrix}\n *                 \\Xi \\\\\n *                 \\mathrm{H} \\\\\n *                 1 \\\\\n *               \\end{bmatrix}\n * \\end{align}\n *\n * ### The Bulged Face Map\n * The bulged surface is itself constructed by linearly interpolating between\n * a cubical face and a spherical face. The surface map for the cubical face\n * of side length \\f$2L\\f$ lying in the \\f$+z\\f$ direction is given by:\n *\n * \\begin{align}\n *   \\vec{\\sigma}_{cubical}: \\vec{\\xi} \\rightarrow \\vec{x}(\\vec{\\xi})\n * \\end{align}\n *\n * Where\n *\n * \\begin{align}\n *   \\vec{x}(\\xi,\\eta) =\n *       \\begin{bmatrix}\n *         x(\\xi,\\eta) \\\\\n *         y(\\xi,\\eta) \\\\\n *         L \\\\\n *       \\end{bmatrix} =\n *           L\\begin{bmatrix}\n *              \\Xi \\\\\n *              \\mathrm{H} \\\\\n *              1 \\\\\n *            \\end{bmatrix}\n * \\end{align}\n *\n * To construct the bulged map we interpolate between this cubical face map\n * and a spherical face map of radius \\f$R\\f$, with the interpolation\n * parameter being \\f$s\\f$, called the *sphericity* and which ranges from\n * 0 to 1, with 0 corresponding to a flat surface and 1 corresponding to a\n * spherical surface. The surface map for the bulged face lying in the \\f$+z\\f$\n * direction is then given by:\n *\n * \\begin{align}\n *   \\vec{\\sigma}_{bulged}(\\xi,\\eta) =\n *       \\left\\{(1-s)L +\n *       \\frac{sR}{\\rho}\\right\\}\n *           \\begin{bmatrix}\n *             \\Xi \\\\\n *             \\mathrm{H} \\\\\n *             1 \\\\\n *           \\end{bmatrix}\n * \\end{align}\n *\n * We constrain $L$ by demanding that the spherical face circumscribe the cube.\n * With this condition, we have \\f$L = R/\\sqrt3\\f$.\n * \\note This differs from the choice in SpEC where it is demanded that the\n * surfaces touch at the cube face centers, which leads to \\f$L = R\\f$.\n *\n * ### The Full Volume Map\n * The final map for the wedge which lies along the \\f$+z\\f$ axis is obtained\n * by interpolating between the two surfaces with the interpolation parameter\n * being the logical coordinate \\f$\\zeta\\f$. For a wedge whose gridpoints are\n * **linearly** distributed in the radial direction (`radial_distribution_` is\n * \\ref domain::CoordinateMaps::Distribution\n * \"domain::CoordinateMaps::Distribution::Linear\"), this interpolation results\n * in the following map:\n *\n * \\begin{align}\n *   \\vec{x}(\\xi,\\eta,\\zeta) =\n *       \\frac{1}{2}\\left\\{\n *         (1-\\zeta)\\Big[\n *           (1-s_{inner})\\frac{R_{inner}}{\\sqrt 3} +\n *           s_{inner}\\frac{R_{inner}}{\\rho}\n *         \\Big] +\n *         (1+\\zeta)\\Big[\n *           (1-s_{outer})\\frac{R_{outer}}{\\sqrt 3} +\n *           s_{outer}\\frac{R_{outer}}{\\rho}\n *         \\Big]\n *       \\right\\}\n *           \\begin{bmatrix}\n *             \\Xi \\\\\n *             \\mathrm{H} \\\\\n *             1 \\\\\n *           \\end{bmatrix}\n * \\end{align}\n *\n * We will define the variables \\f$F(\\zeta)\\f$ and \\f$S(\\zeta)\\f$, the frustum\n * and sphere factors (in the linear case):\n *\n * \\begin{align}\n *   F(\\zeta) &= F_0 + F_1\\zeta \\label{eq:frustum_factor} \\\\\n *   S(\\zeta) &= S_0 + S_1\\zeta \\label{eq:sphere_factor}\n * \\end{align}\n *\n * Where\n *\n * \\begin{align}\n *   F_0 &=\n *       \\frac{1}{2} \\big\\{\n *         (1-s_{outer})R_{outer} + (1-s_{inner})R_{inner}\n *       \\big\\} \\label{eq:frustum_zero_linear} \\\\\n *   F_1 &= \\partial_{\\zeta}F\n *        = \\frac{1}{2} \\big\\{\n *            (1-s_{outer})R_{outer} - (1-s_{inner})R_{inner}\n *          \\big\\} \\label{eq:frustum_rate_linear} \\\\\n *   S_0 &=\n *       \\frac{1}{2} \\big\\{\n *         s_{outer}R_{outer} + s_{inner}R_{inner}\n *       \\big\\} \\label{eq:sphere_zero_linear} \\\\\n *   S_1 &= \\partial_{\\zeta}S\n *        = \\frac{1}{2} \\big\\{ s_{outer}R_{outer} - s_{inner}R_{inner}\\big\\}\n *        \\label{eq:sphere_rate_linear}\n * \\end{align}\n *\n * The map can then be rewritten as:\n *\n * \\begin{align}\n *   \\vec{x}(\\xi,\\eta,\\zeta) =\n *       \\left\\{\n *         \\frac{F(\\zeta)}{\\sqrt 3} + \\frac{S(\\zeta)}{\\rho}\n *       \\right\\}\n *           \\begin{bmatrix}\n *             \\Xi \\\\\n *             \\mathrm{H} \\\\\n *             1 \\\\\n *           \\end{bmatrix}\n * \\end{align}\n *\n * The inverse map is given by:\n *\n * \\begin{align}\n *   \\xi &= \\frac{x}{z} \\\\\n *   \\eta &= \\frac{y}{z} \\\\\n *   \\zeta &= \\frac{z - \\left(\\frac{F_0}{\\sqrt{3}} + \\frac{S_0}{\\rho}\\right)}\n *                 {\\left(\\frac{F_1}{\\sqrt{3}} + \\frac{S_1}{\\rho}\\right)}\n * \\end{align}\n *\n * We provide some common derivatives:\n *\n * \\f{align}\n *   \\partial_{\\xi}z &= \\frac{-S(\\zeta)\\Xi\\Xi'}{\\rho^3} \\\\\n *   \\partial_{\\eta}z &= \\frac{-S(\\zeta)\\mathrm{H}\\mathrm{H}'}{\\rho^3} \\\\\n *   \\partial_{\\zeta}z &= \\frac{F'}{\\sqrt 3} + \\frac{S'(\\zeta)}{\\rho}\n * \\f}\n *\n * The Jacobian then is:\n *\n * \\begin{align}\n *   J =\n *       \\begin{bmatrix}\n *         \\Xi'z + \\Xi\\partial_{\\xi}z &\n *             \\Xi\\partial_{\\eta}z &\n *             \\Xi\\partial_{\\zeta}z \\\\\n *         \\mathrm{H}\\partial_{\\xi}z &\n *             \\mathrm{H}'z + \\mathrm{H}\\partial_{\\eta}z &\n *             \\mathrm{H}\\partial_{\\zeta}z \\\\\n *         \\partial_{\\xi}z &\n *             \\partial_{\\eta}z &\n *             \\partial_{\\zeta}z \\\\\n *       \\end{bmatrix}\n *       \\label{eq:jacobian_centered_wedge}\n * \\end{align}\n *\n * A common factor that shows up in the inverse Jacobian is:\n *\n * \\begin{align}\n *   T:= \\frac{S(\\zeta)}{(\\partial_{\\zeta}z)\\rho^3}\n * \\end{align}\n *\n * The inverse Jacobian then is:\n * \\f{align}\n *   J^{-1} =\n *       \\frac{1}{z}\\begin{bmatrix}\n *         \\Xi'^{-1} & 0 & -\\Xi\\Xi'^{-1} \\\\\n *         0 & \\mathrm{H}'^{-1} & -\\mathrm{H}\\mathrm{H}'^{-1} \\\\\n *         T\\Xi & T\\mathrm{H} & T + F(\\partial_{\\zeta}z)^{-1}/\\sqrt 3 \\\\\n *       \\end{bmatrix}\n * \\f}\n *\n * ### Changing the radial distribution of the gridpoints\n * By default, Wedge linearly distributes its gridpoints in the radial\n * direction. An exponential distribution of gridpoints can be obtained by\n * linearly interpolating in the logarithm of the radius in order to obtain\n * a relatively higher resolution at smaller radii. Since this is a radial\n * rescaling of Wedge, this option is only supported for fully spherical\n * wedges with `sphericity_inner_` = `sphericity_outer_` = 1.\n *\n * The linear interpolation done for a logarithmic radial distribution\n * (`radial_distribution_` is \\ref domain::CoordinateMaps::Distribution\n * \"domain::CoordinateMaps::Distribution::Logarithmic\") is:\n *\n * \\begin{align}\n *   \\ln r = \\frac{1-\\zeta}{2}\\ln R_{inner} + \\frac{1+\\zeta}{2}\\ln R_{outer}\n * \\end{align}\n *\n * The map then is:\n *\n * \\begin{align}\n *   \\vec{x}(\\xi,\\eta,\\zeta) =\n *       \\frac{\\sqrt{R_{inner}^{1-\\zeta}R_{outer}^{1+\\zeta}}}{\\rho}\n *           \\begin{bmatrix}\n *             \\Xi \\\\\n *             \\mathrm{H} \\\\\n *             1 \\\\\n *           \\end{bmatrix}\n * \\end{align}\n *\n * We can rewrite this map to take on the same form as the map for the linear\n * radial distribution, where we set\n *\n * \\begin{align}\n *   F(\\zeta) &= 0 \\\\\n *   S(\\zeta) &= \\sqrt{R_{inner}^{1-\\zeta}R_{outer}^{1+\\zeta}} \\\\\n * \\end{align}\n *\n * Which gives us\n *\n * \\begin{align}\n *   \\vec{x}(\\xi,\\eta,\\zeta) =\n *       \\frac{S(\\zeta)}{\\rho}\n *           \\begin{bmatrix}\n *             \\Xi \\\\\n *             \\mathrm{H} \\\\\n *             1 \\\\\n *           \\end{bmatrix}\n * \\end{align}\n *\n * The Jacobian then is still Eq. ($\\ref{eq:jacobian_centered_wedge}$) but\n * where $F(\\zeta)$ and $S(\\zeta)$ are the quantities defined here for the\n * logarithmic distribution.\n *\n * Alternatively, an inverse radial distribution (`radial_distribution_` is\n * \\ref domain::CoordinateMaps::Distribution\n * \"domain::CoordinateMaps::Distribution::Inverse\") can be chosen where the\n * linear interpolation is:\n *\n * \\begin{align}\n *   \\frac{1}{r} =\n *       \\frac{R_\\mathrm{inner} + R_\\mathrm{outer}}\n *            {2 R_\\mathrm{inner}R_\\mathrm{outer}} +\n *       \\frac{R_\\mathrm{inner} - R_\\mathrm{outer}}\n *            {2R_\\mathrm{inner} R_\\mathrm{outer}} \\zeta\n * \\end{align}\n *\n * Which can be rewritten as:\n *\n * \\begin{align}\n *   \\frac{1}{r} = \\frac{1-\\zeta}{2R_{inner}} + \\frac{1+\\zeta}{2R_{outer}}\n * \\end{align}\n *\n * The map likewise takes the form:\n *\n * \\begin{align}\n *   \\vec{x}(\\xi,\\eta,\\zeta) =\n *       \\frac{S(\\zeta)}{\\rho}\n *           \\begin{bmatrix}\n *             \\Xi \\\\\n *             \\mathrm{H} \\\\\n *             1 \\\\\n *           \\end{bmatrix}\n * \\end{align}\n *\n * Where\n *\n * \\begin{align}\n *   F(\\zeta) &= 0 \\\\\n *   S(\\zeta) &=\n *       \\frac{2R_{inner}R_{outer}}\n *            {(1 + \\zeta)R_{inner} + (1 - \\zeta)R_{outer}}\n * \\end{align}\n *\n * Again, the Jacobian is still Eq. ($\\ref{eq:jacobian_centered_wedge}$) but\n * where $F(\\zeta)$ and $S(\\zeta)$ are the quantities defined here for the\n * inverse distribution.\n *\n * ### Changing the opening angles\n * Consider the following map on \\f$\\xi \\in [-1,1]\\f$, which maps this interval\n * onto a parameterized curve that extends one fourth of a circle.\n *\n * \\begin{align}\n *   \\vec{\\Gamma}(\\xi) =\n *       \\frac{R}{\\sqrt{1+\\xi^2}}\n *           \\begin{bmatrix}\n *             1 \\\\\n *             \\xi \\\\\n *           \\end{bmatrix}.\n *   \\label{eq:quarter_circle}\n * \\end{align}\n *\n * It is convenient to compute the polar coordinate $\\theta$ of the mapped\n * point as a function of $\\xi$:\n *\n * \\begin{align}\n *   \\theta(\\xi) = \\tan^{-1}\\left(\\frac{\\Gamma_y(\\xi)}{\\Gamma_x(\\xi)}\\right).\n *   \\label{eq:polar_coord}\n * \\end{align}\n *\n * The *opening angle* of the map is defined to be:\n *\n * \\begin{align}\n *   \\Delta \\theta = \\theta(1) - \\theta(-1),\n *   \\label{eq:define_opening_angle}\n * \\end{align}\n *\n * We can see that with $\\xi=\\pm 1$, we have $\\Gamma_x = R/\\sqrt{2}$ and\n * $\\Gamma_y=\\pm R/\\sqrt{2}$, giving us\n * $\\theta(1) = \\pi/4$ and $\\theta(-1) = -\\pi/4$. This wedge has an opening\n * angle $\\pi/2$ radians, as expected.\n *\n * On the other hand, the following map has an opening angle of $\\theta_O$:\n *\n * \\begin{align}\n *   \\vec{\\Gamma}(\\xi) =\n *       \\frac{R}{\\sqrt{1+\\tan^2{(\\theta_O/2)}\\xi^2}}\n *           \\begin{bmatrix}\n *           1 \\\\\n *           \\tan{(\\theta_O/2)}\\xi \\\\\n *           \\end{bmatrix}.\n * \\end{align}\n *\n * Let us also consider the generalized map\n *\n * \\begin{align}\n *   \\vec{\\Gamma}(\\xi) =\n *       \\frac{R}{\\sqrt{1+\\Xi^2}}\n *           \\begin{bmatrix}\n *             1 \\\\\n *             \\Xi \\\\\n *           \\end{bmatrix},\n * \\end{align}\n *\n * where $\\Xi(\\xi)$ is a function of $\\xi$. $\\theta(\\xi)$ can then be written as\n *\n * \\begin{align}\n *   \\theta(\\xi) = \\tan^{-1}(\\Xi).\n *   \\label{eq:theta}\n * \\end{align}\n *\n * For the map $\\Xi(\\xi) = \\tan(\\pi\\xi/4)$, Eq. ($\\ref{eq:theta}$) yields\n * $\\theta(\\xi) = \\pi\\xi/4$ and $\\Delta\\theta = \\pi/2$. Note that this choice of\n * $\\Xi(\\xi)$ is equivalent to a reparameterization of the previous map given in\n * Eq. ($\\ref{eq:quarter_circle}$). The reparameterization of the curve\n * $\\vec{\\Gamma}(\\xi)$ via the tangent map yields an empirically superior\n * gridpoint distribution in practice. That this reparameterization should have\n * this property can be motivated by an observation of the following:\n *\n * \\begin{align}\n *   \\frac{\\mathrm{d}\\tan^{-1}\\Xi}{\\mathrm{d}\\xi}\n *       = \\frac{1}{1+\\Xi^2}\\frac{\\mathrm{d}\\Xi}{\\mathrm{d}\\xi}\n *       = \\frac{\\pi}{4}.\n * \\end{align}\n *\n * In other words, this parameterization has the property that the logical\n * coordinate $\\xi$ subtends the angle $\\theta$ at a constant rate. In general,\n * we say that a curve $\\vec{\\Gamma}(\\xi)$ is parameterized *equiangularly* if\n *\n * \\begin{align}\n *   \\frac{\\mathrm{d}\\theta}{\\mathrm{d}\\xi} = \\text{const}.\n * \\end{align}\n *\n * As for the map\n *\n * \\begin{align}\n *   \\Xi(\\xi) =\n *       \\tan{(\\theta_O/2)}\\frac{\\tan{(\\theta_D \\xi/2)}}{\\tan{(\\theta_D/2)}},\n * \\end{align}\n *\n * this choice of $\\Xi(\\xi)$ results in a $\\vec{\\Gamma}(\\xi)$ with opening\n * angle $\\theta_O$, which is equiangularly distributed if\n * $\\theta_O = \\theta_D$. In the Wedge map, the argument\n * `with_adapted_equiangular_map` controls whether to set\n * $\\theta_O = \\theta_D$ (the `true` case) or to set $\\theta_D = \\pi/2$\n * (the `false` case). When working with a 3D Wedge, the opening angles for the\n * Wedge can be separately controlled for both the $\\xi$ and $\\eta$ directions,\n * but `with_adapted_equiangular_map` will apply to both directions.\n * Additionally in the 3D case, it is not possible to set\n * `with_equiangular_map_` to `true` for all of the six wedges of a sphere\n * unless every opening angle is $\\pi/2$. In the\n * \\ref ::domain::creators::BinaryCompactObject \"BinaryCompactObject\" domain,\n * the outer $+y$, $-y$, $+z$, and $-z$ `Wedge`s are allowed to have a\n * user-specified opening angle in the $\\xi$-direction, with a corresponding\n * $\\theta_D$ equal to this opening angle, while in the $\\eta$-direction the\n * opening angle is set to $\\pi/2$. The two end cap `Wedge`s in the $+x$ and\n * $-x$ directions have angular dimensions and gridpoint distributions\n * determined by the other four `Wedge`s, as the six `Wedge`s must conforming\n * have gridpoint distributions at the $\\xi = \\pm1$, $\\eta = \\pm 1$ boundaries.\n *\n * ### Wedge with a Focal Offset\n * \\image html FocalOffset.jpg \"Wedges without and with a focal offset\"\n *\n * In the case of the rectangular\n * \\ref ::domain::creators::BinaryCompactObject \"BinaryCompactObject\" domain,\n * it becomes desirable to offset the center of the spherical excision surface\n * relative to the center of the cubical surface surrounding it. To enable the\n * offsetting of the central excision, the Wedge map must be generalized\n * according to the *focal lifting* method, which we will now discuss.\n *\n * We consider the problem of creating parameterized volumes from parameterized\n * surfaces. Consider a parameterized surface $\\vec{\\sigma}_{parent}(\\xi,\\eta)$,\n * also referred to as the *parent surface*. We define *focal lifting* as the\n * projection of this parent surface into a three-dimensional parameterized\n * volume $\\vec{x}(\\xi,\\eta, \\zeta)$ with respect to some *focus* $\\vec{x}_0$\n * and *lifting scale factor* $\\Lambda(\\xi,\\eta,\\zeta)$. The resulting volume\n * is then said to be a *focally lifted* volume. These volume maps can be cast\n * into the following form:\n *\n * \\begin{align}\n *   \\vec{x} - \\vec{x}_0 = \\Lambda(\\vec{\\sigma}_{parent}-\\vec{x}_0),\n *   \\label{eq:focal_lifting}\n * \\end{align}\n *\n * which makes apparent how the mapped point $\\vec{x}(\\xi,\\eta,\\zeta)$ is\n * obtained. The parametric equations for the generalized 3D Wedge maps can all\n * be written in the above form, which we will refer to as\n * *focally lifted form*. In the case of the 3D Wedge map with no focal offset,\n * we have:\n *\n * \\begin{align}\n *   \\vec{x}_0 &= 0 \\\\\n *   \\Lambda &= \\left\\{\\frac{F(\\zeta)}{\\sqrt{3}} +\n *                     \\frac{S(\\zeta)}{\\rho} \\right\\} \\\\\n *   \\vec{\\sigma}_{parent} &= \\begin{bmatrix} \\Xi, \\mathrm{H}, 1 \\end{bmatrix}^T\n * \\end{align}\n *\n * The above map can be thought of as constructing a wedge from a biunit cube\n * centered at the origin. Points on the parent surface are scaled by a factor\n * of $\\Lambda(\\xi,\\eta,\\zeta)$ to obtain the corresponding point in the\n * volume. When generalizing the map to have a focus shifted from the origin\n * (obtained by setting `focal_offset_` to be non-zero), we scale the original\n * parent surface $\\vec{\\sigma}_{parent} = [\\Xi, \\mathrm{H},1]^T$ by a factor\n * $L$, and let the focus $\\vec{x_0}$ shift away from the origin. The\n * generalized wedge map is then given by:\n *\n * \\begin{align}\n *   \\vec{x} - \\vec{x}_0 =\n *       \\left\\{\\frac{F(\\zeta)}{L\\sqrt 3} +\n *       \\frac{S(\\zeta)}{L\\rho}\\right\\}\n *           \\begin{bmatrix}\n *             L\\Xi - x_0 \\\\\n *             L\\mathrm{H} - y_0 \\\\\n *             L-z_0 \\\\\n *           \\end{bmatrix}\n * \\end{align}\n *\n * where we are now defining $\\rho$ to be\n *\n * \\begin{align}\n *   \\rho = \\sqrt{(\\Xi - x_0/L)^2 + (\\mathrm{H} - y_0/L)^2 + (1 - z_0/L)^2}.\n *   \\label{eq:generalized_rho}\n * \\end{align}\n *\n * This map is often written as:\n *\n * \\begin{align}\n *   \\vec{x} - \\vec{x}_0 =\n *       \\left\\{\\frac{F(\\zeta)}{\\sqrt{3}} +\n *       \\frac{S(\\zeta)}{\\rho}\\right\\}(\\vec{\\sigma}_0 - \\vec{x}_0/L),\n *    \\label{eq:focally_lifted_map_with_s_and_f_factors}\n * \\end{align}\n *\n * where $\\vec{\\sigma}_0 = [\\Xi, \\mathrm{H},1]^T$, as the parent surface\n * $\\vec{\\sigma}_{parent}$ is now $L\\vec{\\sigma}_0$. We give the quantity in\n * braces the name $z_{\\Lambda} = L\\Lambda$, *generalized z*. With this\n * definition, we can rewrite\n * Eq. ($\\ref{eq:focally_lifted_map_with_s_and_f_factors}$) in the simpler form,\n *\n * \\begin{align}\n *   \\vec{x} - \\vec{x}_0 = z_{\\Lambda}(\\vec{\\sigma}_0 - \\vec{x}_0/L).\n *   \\label{eq:focally_lifted_map_with_generalized_z_coef}\n * \\end{align}\n *\n * \\note In the offset case, the frustum factor $F(\\zeta)$ and sphere factor\n * $S(\\zeta)$ (Eqs. ($\\ref{eq:frustum_factor}$) and ($\\ref{eq:sphere_factor}$))\n * for a linear radial distribution are no longer defined by the general $F_0$,\n * $F_1$, $S_0$, and $S_1$ given by Eqs.\n * ($\\ref{eq:frustum_zero_linear}$), ($\\ref{eq:frustum_rate_linear}$),\n * ($\\ref{eq:sphere_zero_linear}$), and ($\\ref{eq:sphere_rate_linear}$). In the\n * offset case, the inner surface must be spherical $(s_{inner} = 1)$ and the\n * outer surface can only be spherical or flat\n * $(s_{outer} = 0 \\textrm{ or } s_{outer} = 1)$. In the case where\n * $s_{outer} = 0$, $L/\\sqrt{3}$ is taken to be $R_{outer}$.\n *\n * The map can be inverted by first solving for \\f$z_{\\Lambda}\\f$ in terms of\n * the target coordinates. We make use of the fact that the parent surface\n * $\\vec{\\sigma}_{parent}$ has a constant normal vector $\\hat{n} = \\hat{z}$.\n *\n * \\begin{align}\n *   z_{\\Lambda} = \\frac{(\\vec{x} - \\vec{x}_0)\\cdot\\hat{n}}\n *                      {(\\vec{\\sigma}_0-\\vec{x}_0/L)\\cdot\\hat{n}}.\n * \\end{align}\n *\n * In other words, when $\\hat{n} = \\hat{z}$,\n *\n * \\begin{align}\n *   z_{\\Lambda} = \\left\\{\\frac{F(\\zeta)}{\\sqrt{3}} +\n *                 \\frac{S(\\zeta)}{\\rho}\\right\\}\n *               = \\frac{z - z_0}{1 - z_0/L}\n * \\end{align}\n *\n * Moving all the known quantities in\n * Eq. ($\\ref{eq:focally_lifted_map_with_generalized_z_coef}$) to the left hand\n * side results in the following expression that solves for the source\n * coordinates $\\xi$ and $\\eta$ in terms of the target coordinates:\n *\n * \\begin{align}\n *   \\frac{\\vec{x} - \\vec{x}_0}{z_{\\Lambda}} + \\frac{\\vec{x}_0}{L}\n *        = \\vec{\\sigma}_0(\\xi,\\eta)\n *        = \\begin{bmatrix}\n *            \\Xi \\\\\n *            \\mathrm{H} \\\\\n *            1 \\\\\n *          \\end{bmatrix},\n * \\end{align}\n *\n * Note that $|\\vec{\\sigma}_0 - \\vec{x}_0/L| = \\sqrt{(\\Xi - x_0/L)^2 +\n * (\\mathrm{H} - y_0/L)^2 + (1 - z_0/L)^2} = \\rho$, indicating that an\n * expression for $\\rho$ in terms of the target coordinates can be computed via\n * taking the magnitude of both sides of\n * Eq. ($\\ref{eq:focally_lifted_map_with_generalized_z_coef}$):\n *\n * \\begin{align}\n *   |\\vec{x} - \\vec{x}_0| = z_{\\Lambda}|\\vec{\\sigma}_0 - \\vec{x}_0/L|\n *                         = z_{\\Lambda}\\rho.\n * \\end{align}\n *\n * The quantity $\\rho$ is then given by:\n *\n * \\begin{align}\n *   \\rho = \\frac{|\\vec{x} - \\vec{x}_0|}{z_{\\Lambda}}.\n * \\end{align}\n *\n * With $\\rho$ computed, the radial source coordinate $\\zeta$ can be computed\n * from\n *\n * \\begin{align}\n *   z_{\\Lambda} = \\left\\{\\frac{F(\\zeta)}{\\sqrt{3}} +\n *                 \\frac{S(\\zeta)}{\\rho} \\right\\}\n *               = \\left\\{\\frac{F_0}{\\sqrt{3}} + \\frac{S_0}{\\rho} +\n *                 \\frac{F_1\\zeta}{\\sqrt{3}} + \\frac{S_1\\zeta}{\\rho}\\right\\},\n * \\end{align}\n *\n * which gives\n *\n * \\begin{align}\n *   \\zeta = \\frac{z_{\\Lambda} -\n *                 \\left(\\frac{F_0}{\\sqrt{3}} + \\frac{S_0}{\\rho}\\right)}\n *                {\\left(\\frac{F_1}{\\sqrt{3}} + \\frac{S_1}{\\rho}\\right)}.\n * \\end{align}\n *\n * To compute the Jacobian, it is useful to first note that $\\rho$\n * (Eq. ($\\ref{eq:generalized_rho}$)) is the magnitude of the vector\n *\n * \\begin{align}\n *   \\vec{\\rho} = \\vec{\\sigma}_0 - \\vec{x}_0/L\n *              = \\begin{bmatrix}\n *                  \\Xi - x_0/L \\\\\n *                  \\mathrm{H} - y_0/L \\\\\n *                  1 - z_0/L\n *                \\end{bmatrix}\n * \\end{align}\n *\n * and that we can express the target coordinates in\n * Eq. ($\\ref{eq:focally_lifted_map_with_generalized_z_coef}$) in terms of the\n * components of $\\vec{\\rho}$:\n *\n * \\begin{align}\n *   x &= z_{\\Lambda}\\rho_x + x_0 \\\\\n *   y &= z_{\\Lambda}\\rho_y + y_0 \\\\\n *   z &= z_{\\Lambda}\\rho_z + z_0\n * \\end{align}\n *\n * Some common terms used in the Jacobian are the derivatives of $z_{\\Lambda}$\n * with respect to the source coordinates:\n *\n * \\begin{align}\n *   \\partial_{\\xi}z_{\\Lambda} &=\n *       \\frac{-S(\\zeta)\\Xi'\\rho_x}{\\rho^3} \\\\\n *   \\partial_{\\eta}z_{\\Lambda} &=\n *       \\frac{-S(\\zeta)\\mathrm{H}'\\rho_y}{\\rho^3} \\\\\n *   \\partial_{\\zeta}z_{\\Lambda} &=\n *       \\frac{F'(\\zeta)}{\\sqrt{3}} + \\frac{S'(\\zeta)}{\\rho}\n * \\end{align}\n *\n * The Jacobian then is:\n *\n * \\begin{align}\n *   J =\n *       \\begin{bmatrix}\n *         \\Xi'z_{\\Lambda} + \\rho_x\\partial_{\\xi}z_{\\Lambda} &\n *             \\rho_x\\partial_{\\eta}z_{\\Lambda} &\n *             \\rho_x\\partial_{\\zeta}z_{\\Lambda} \\\\\n *         \\rho_y\\partial_{\\xi}z_{\\Lambda} &\n *             \\mathrm{H}'z_{\\Lambda} + \\rho_y\\partial_{\\eta}z_{\\Lambda} &\n *             \\rho_y\\partial_{\\zeta}z_{\\Lambda} \\\\\n *         \\rho_z\\partial_{\\xi}z_{\\Lambda} &\n *             \\rho_z\\partial_{\\eta}z_{\\Lambda} &\n *             \\rho_z\\partial_{\\zeta}z_{\\Lambda} \\\\\n *       \\end{bmatrix}\n * \\end{align}\n *\n * A common factor that shows up in this inverse Jacobian is:\n *\n * \\begin{align}\n *   T:= \\frac{S(\\zeta)}{(\\partial_{\\zeta}z_{\\Lambda})\\rho^3}\n * \\end{align}\n *\n * And the inverse Jacobian is then:\n *\n * \\begin{align}\n *   J^{-1} =\n *       \\frac{1}{z_{\\Lambda}}\\begin{bmatrix}\n *         \\Xi'^{-1} & 0 & -\\rho_x(\\Xi'\\rho_z)^{-1} \\\\\n *         0 & \\mathrm{H}'^{-1} & -\\rho_y(\\mathrm{H}'\\rho_z)^{-1} \\\\\n *         T\\rho_x & T\\rho_y &\n *             T\\rho_z + F(\\partial_{\\zeta}z_{\\Lambda}\\rho_z)^{-1}/\\sqrt{3}\n *       \\end{bmatrix}\n * \\end{align}\n *\n * ### Offsetting a Rotated Wedge\n * The default Wedge map is oriented in the $+z$ direction, so the\n * construction of a Wedge oriented along a different direction requires an\n * additional OrientationMap $R$ to be passed to `orientation_of_wedge`. When\n * offsetting a rotated Wedge, the coordinates passed as parameters to\n * `focal_offset` are in the coordinate frame in which the Wedge is rotated\n * (the target frame). However, the focal lifting procedure (shown in\n * Eq. ($\\ref{eq:focal_lifting}$)) is done in the default frame in which the\n * Wedge is facing the $+z$ direction, so the focal offset $\\vec{x}_0$ is first\n * hit by the inverse rotation $R^{-1}$ and then the rotated focus\n * $R^{-1}\\vec{x}_0$ is used internally as the focus for the $+z$ Wedge. When\n * the focal lifting calculation has completed, the rotation of the $+z$ Wedge\n * into the desired orientation by $R$ also rotates the focus into the desired\n * location. When performing the inverse operation, the focus is similarly\n * rotated into the default frame, where the inversion is performed.\n *\n * ### Interaction between opening angles and focal offsets\n * When a Wedge is created with a non-zero focal offset, the resulting shape\n * can take on a variety of possible angular sizes, depending on where the\n * focus is placed relative to the default centered location. The reader might\n * note that the angular size of a Wedge can also be controlled by passing an\n * argument to the `opening_angles` parameter in the Wedge constructor. While\n * both of these methods allow the angular size of a Wedge to be changed, the\n * user is prevented from employing both of them at the same time. In\n * particular, when the the offset is set to some non-zero value, the\n * `opening_angles_` member variable is set to $\\pi/2$. Note that the\n * `opening_angles_` member being set to $\\pi/2$ does not imply the\n * resulting Wedge will have an angular size of $\\pi/2$. On the contrary, the\n * Wedge will have the angular size that is determined by the application of\n * the focal lifting method on the parent surface, which is the upper $+z$ face\n * of a cube that is centered at the origin.\n *\n * Because `opening_angles_` is set to $\\pi/2$ when there is a non-zero focal\n * offset, when there is a non-zero focal offset and `with_equiangular_map_` is\n * `true`, $\\Xi$ is given by Eq. ($\\ref{eq:equiangular_xi_pi_over_2}$) and\n * $\\mathrm{H}$ by Eq. ($\\ref{eq:equiangular_eta_pi_over_2}$), just as it is\n * for the case of a centered Wedge with `opening_angles_` of $\\pi/2$.\n */\ntemplate <size_t Dim>\nclass Wedge {\n public:\n  static constexpr size_t dim = Dim;\n  enum class WedgeHalves {\n    /// Use the entire wedge\n    Both,\n    /// Use only the upper logical half\n    UpperOnly,\n    /// Use only the lower logical half\n    LowerOnly\n  };\n\n  /*!\n   * \\brief Constructs a centered wedge (one with no focal offset)\n   *\n   * \\param radius_inner Distance from the origin to one of the corners which\n   * lie on the inner surface.\n   * \\param radius_outer Distance from the origin to one of the corners which\n   * lie on the outer surface.\n   * \\param orientation_of_wedge The orientation of the desired wedge relative\n   * to the orientation of the default wedge which is a wedge that has its\n   * curved surfaces pierced by the upper-z axis. The logical $\\xi$ and $\\eta$\n   * coordinates point in the cartesian x and y directions, respectively.\n   * \\param sphericity_inner Value between 0 and 1 which determines\n   * whether the inner surface is flat (value of 0), spherical (value of 1) or\n   * somewhere in between.\n   * \\param sphericity_outer Value between 0 and 1 which determines\n   * whether the outer surface is flat (value of 0), spherical (value of 1) or\n   * somewhere in between.\n   * \\param with_equiangular_map Determines whether to apply a tangent function\n   * mapping to the logical coordinates (for `true`) or not (for `false`).\n   * \\param halves_to_use Determines whether to construct a full wedge or only\n   * half a wedge. If constructing only half a wedge, the resulting shape has a\n   * face normal to the x direction (assuming default OrientationMap). If\n   * constructing half a wedge, an intermediate affine map is applied to the\n   * logical xi coordinate such that the interval [-1,1] is mapped to the\n   * corresponding logical half of the wedge. For example, if `UpperOnly` is\n   * specified, [-1,1] is mapped to [0,1], and if `LowerOnly` is specified,\n   * [-1,1] is mapped to [-1,0]. The case of `Both` means a full wedge, with no\n   * intermediate map applied. In all cases, the logical points returned by the\n   * inverse map will lie in the range [-1,1] in each dimension. Half wedges are\n   * currently only useful in constructing domains for binary systems.\n   * \\param radial_distribution Determines how to distribute gridpoints along\n   * the radial direction. For wedges that are not exactly spherical, only\n   * `Distribution::Linear` is currently supported.\n   * \\param opening_angles Determines the angular size of the wedge. The default\n   * value is $\\pi/2$, which corresponds to a wedge size of $\\pi/2$. For this\n   * setting, four Wedges can be put together to cover $2\\pi$ in angle along a\n   * great circle. This option is meant to be used with the equiangular map\n   * option turned on.\n   * \\param with_adapted_equiangular_map Determines whether to adapt the\n   * point distribution in the wedge to match its physical angular size. When\n   * `true`, angular distances are proportional to logical distances. Note\n   * that it is not possible to use adapted maps in every Wedge of a Sphere\n   * unless each Wedge has the same size along both angular directions.\n   */\n  Wedge(double radius_inner, double radius_outer, double sphericity_inner,\n        double sphericity_outer, OrientationMap<Dim> orientation_of_wedge,\n        bool with_equiangular_map,\n        WedgeHalves halves_to_use = WedgeHalves::Both,\n        Distribution radial_distribution = Distribution::Linear,\n        const std::array<double, Dim - 1>& opening_angles =\n            make_array<Dim - 1>(M_PI_2),\n        bool with_adapted_equiangular_map = true);\n\n  /*!\n   * \\brief Constructs a wedge with a focal offset\n   *\n   * \\details Can construct an offset Wedge with a spherical inner surface and\n   * either a spherical or a flat outer surface. If `radius_outer` has a value,\n   * a spherical Wedge will be constructed, and if not, a flat one will be\n   * constructed.\n   *\n   * Note that because the focal offset is what determines the angular size of\n   * the Wedge, opening angles cannot be used with offset Wedges.\n   *\n   * In the event that `focal_offset` happens to be zero, the Wedge's member\n   * variables and behavior will be set up to be equivalent to that of a\n   * centered Wedge:\n   * - `cube_half_length` will be ignored\n   * - if `radius_outer` is `std::nullopt`, the outer radius of the Wedge will\n   * be set to $\\sqrt{\\mathrm{Dim}}L$, where $L$ is the `cube_half_length`\n   * - the opening angles ($\\theta_O$) and opening angles distribution\n   * ($\\theta_D$) used will be $\\pi/2$\n   *\n   * \\param radius_inner Distance from the origin to one of the corners which\n   * lie on the inner surface.\n   * \\param radius_outer If this has a value, it creates a spherical Wedge\n   * (inner and outer sphericity are 1) where this is the distance from the\n   * origin to one of the corners that lie on the inner surface. If this is\n   * `std::nullopt`, it creates a Wedge with a flat outer surface\n   * (inner sphericity is 1 and outer sphericity is 0). In the event that\n   * `radius_outer == std::nullopt` **and** `focal_offset` is zero,\n   * the outer radius will instead be set to $\\sqrt{\\mathrm{Dim}}L$, where $L$\n   * is the `cube_half_length`. The outer radius is given a value in this\n   * circumstance so that it can be handled as a centered Wedge (one with no\n   * offset).\n   * \\param orientation_of_wedge The orientation of the desired wedge relative\n   * to the orientation of the default wedge which is a wedge that has its\n   * curved surfaces pierced by the upper-z axis. The logical $\\xi$ and $\\eta$\n   * coordinates point in the cartesian x and y directions, respectively.\n   * \\param cube_half_length Half the length of the parent surface (see Wedge\n   * documentation for more details). If `focal_offset` is zero, this\n   * parameter has no effect and is ignored so that the Wedge can be handled\n   * as a centered Wedge (one with no offset).\n   * \\param focal_offset The target frame coordinates of the focus from which\n   * the Wedge is focally lifted.\n   * \\param with_equiangular_map Determines whether to apply a tangent function\n   * mapping to the logical coordinates (for `true`) or not (for `false`).\n   * \\param halves_to_use Determines whether to construct a full wedge or only\n   * half a wedge. If constructing only half a wedge, the resulting shape has a\n   * face normal to the x direction (assuming default OrientationMap). If\n   * constructing half a wedge, an intermediate affine map is applied to the\n   * logical xi coordinate such that the interval [-1,1] is mapped to the\n   * corresponding logical half of the wedge. For example, if `UpperOnly` is\n   * specified, [-1,1] is mapped to [0,1], and if `LowerOnly` is specified,\n   * [-1,1] is mapped to [-1,0]. The case of `Both` means a full wedge, with no\n   * intermediate map applied. In all cases, the logical points returned by the\n   * inverse map will lie in the range [-1,1] in each dimension. Half wedges are\n   * currently only useful in constructing domains for binary systems.\n   * \\param radial_distribution Determines how to distribute gridpoints along\n   * the radial direction. For wedges that are not exactly spherical, only\n   * `Distribution::Linear` is currently supported.\n   */\n  Wedge(double radius_inner, std::optional<double> radius_outer,\n        double cube_half_length, std::array<double, Dim> focal_offset,\n        OrientationMap<Dim> orientation_of_wedge, bool with_equiangular_map,\n        WedgeHalves halves_to_use = WedgeHalves::Both,\n        Distribution radial_distribution = Distribution::Linear);\n\n  Wedge() = default;\n  ~Wedge() = default;\n  Wedge(Wedge&&) = default;\n  Wedge(const Wedge&) = default;\n  Wedge& operator=(const Wedge&) = default;\n  Wedge& operator=(Wedge&&) = default;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> operator()(\n      const std::array<T, Dim>& source_coords) const;\n\n  /// For a \\f$+z\\f$-oriented `Wedge`, returns invalid if \\f$z<=0\\f$\n  /// or if \\f$(x,y,z)\\f$ is on or outside the cone defined\n  /// by \\f$(x^2/z^2 + y^2/z^2+1)^{1/2} = -S/F\\f$, where\n  /// \\f$S = \\frac{1}{2}(s_1 r_1 - s_0 r_0)\\f$ and\n  /// \\f$F = \\frac{1}{2\\sqrt{3}}((1-s_1) r_1 - (1-s_0) r_0)\\f$.\n  /// Here \\f$s_0,s_1\\f$ and \\f$r_0,r_1\\f$ are the specified sphericities\n  /// and radii of the inner and outer \\f$z\\f$ surfaces.  The map is singular on\n  /// the cone and on the xy plane.\n  /// The inverse function is only callable with doubles because the inverse\n  /// might fail if called for a point out of range, and it is unclear\n  /// what should happen if the inverse were to succeed for some points in a\n  /// DataVector but fail for other points.\n  std::optional<std::array<double, Dim>> inverse(\n      const std::array<double, Dim>& target_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jacobian(\n      const std::array<T, Dim>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> inv_jacobian(\n      const std::array<T, Dim>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  static constexpr bool is_identity() { return false; }\n\n  static constexpr bool supports_hessian{true};\n\n private:\n  // maps between 2D and 3D choices for coordinate axis orientations\n  static constexpr size_t radial_coord =\n      detail::WedgeCoordOrientation<Dim>::radial_coord;\n  static constexpr size_t polar_coord =\n      detail::WedgeCoordOrientation<Dim>::polar_coord;\n  static constexpr size_t azimuth_coord =\n      detail::WedgeCoordOrientation<Dim>::azimuth_coord;\n\n  /*!\n   * \\brief Factors out the calculation of \\f$\\Xi(\\xi)\\f$ and $\\mathrm{H}$\n   *\n   * \\details The **equidistant** parametrization\n   * (when `with_equiangular_map_ == false`) of the logical coordinates is\n   *\n   * \\f{align*}{\n   *   \\Xi(\\xi) = \\xi.\n   * \\f}\n   *\n   * The **equiangular** reparametrization\n   * (when `with_equiangular_map_ == true`) of the logical coordinates is\n   *\n   * \\f{align*}{\n   *   \\Xi(\\xi) =\n   *       \\tan{(\\theta_O/2)}\\frac{\\tan{(\\theta_D \\xi/2)}}{\\tan{(\\theta_D/2)}},\n   * \\f}\n   *\n   * where $\\theta_O$ (element of `opening_angles_`) and $\\theta_D$\n   * (element of `opening_angles_distribution_`) are described in the Wedge\n   * class documentation.\n   *\n   * When `focal_offset_` is nonzero, the **equiangular** reparametrization\n   * is instead\n   *\n   * \\f{align*}{\n   *   \\Xi(\\xi) = \\tan{(\\pi/4)}\\xi\n   * \\f}\n   *\n   * \\tparam FuncIsXi whether the logical cooridnate `lowercase_xi_or_eta` is\n   * $\\xi$ (polar coordinate) or $\\eta$ (azimuthal coordinate)\n   * \\param lowercase_xi_or_eta the logical coordinate $\\xi$ or $\\eta$ to map\n   */\n  template <bool FuncIsXi, typename T>\n  tt::remove_cvref_wrap_t<T> get_cap_angular_function(\n      const T& lowercase_xi_or_eta) const;\n\n  /*!\n   * \\brief Factors out the calculation of \\f$\\Xi'(\\xi)\\f$ and $\\mathrm{H}'$\n   *\n   * \\details Computes the derivatives of the quantities defined in\n   * `get_cap_angular_function()`.\n   *\n   * \\tparam FuncIsXi whether the logical cooridnate `lowercase_xi_or_eta` is\n   * $\\xi$ (polar coordinate) or $\\eta$ (azimuthal coordinate)\n   * \\param lowercase_xi_or_eta the logical coordinate $\\xi$ or $\\eta$ to map\n   */\n  template <bool FuncIsXi, typename T>\n  tt::remove_cvref_wrap_t<T> get_deriv_cap_angular_function(\n      const T& lowercase_xi_or_eta) const;\n\n  /*!\n   * \\brief Factors out the calculation of $\\vec{\\rho}$\n   *\n   * \\details Computes\n   * \\f{align*}{\n   *   \\vec{\\rho} = [\\Xi-x_0/L, \\mathrm{H}-y_0/L, 1-z_0/L]^T\n   * \\f}\n   *\n   * where \\f$\\Xi\\f$ and $\\mathrm{H}$ are the logical coordinate maps defined in\n   * `get_cap_angular_function()` and the Wedge class documentation,\n   * \\f$\\vec{x_0} = [x_0, y_0, z_0]^T\\f$ is the result of applying the inverse\n   * map of the `orientation_of_wedge_` on the `focal_offset_`, and $L$ is the\n   * `cube_half_length_`.\n   *\n   * \\param rotated_focus the result of applying the inverse map of the\n   * `orientation_of_wedge_` on the `focal_offset_`\n   * \\param cap the function(s) \\f$\\Xi\\f$ (and $\\mathrm{H}$ in 3D)\n   */\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> get_rho_vec(\n      const std::array<double, Dim>& rotated_focus,\n      const std::array<tt::remove_cvref_wrap_t<T>, Dim - 1>& cap) const;\n\n  /*!\n   * \\brief Factors out the calculation of $1/\\rho$\n   *\n   * \\details Computes $1/\\rho$ where\n   *\n   * \\f{align*}{\n   *   \\rho = \\sqrt{(\\Xi - x_0/L)^2 + (\\mathrm{H} - y_0/L)^2 + (1 - z_0/L)^2}.\n   * \\f}\n   *\n   * Here, \\f$\\Xi\\f$ and $\\mathrm{H}$ are the logical coordinate maps defined in\n   * `get_cap_angular_function()` and the Wedge class documentation,\n   * \\f$\\vec{x_0} = [x_0, y_0, z_0]^T\\f$ is the result of applying the inverse\n   * map of the `orientation_of_wedge_` on the `focal_offset_`, and $L$ is the\n   * `cube_half_length_`.\n   *\n   * \\param rotated_focus the result of applying the inverse map of the\n   * `orientation_of_wedge_` on the `focal_offset_`\n   * \\param cap the function(s) \\f$\\Xi\\f$ (and $\\mathrm{H}$ in 3D)\n   */\n  template <typename T>\n  tt::remove_cvref_wrap_t<T> get_one_over_rho(\n      const std::array<double, Dim>& rotated_focus,\n      const std::array<tt::remove_cvref_wrap_t<T>, Dim - 1>& cap) const;\n\n  /*!\n   * \\brief Factors out the calculation of $S(\\zeta)$ needed for the map and the\n   * Jacobian\n   *\n   * \\details The value of $S(\\zeta)$ is computed differently for different\n   * radial distributions.\n   *\n   * For a **linear** radial distribution:\n   *\n   * \\f{align*}{\n   *   S(\\zeta) = S_0 + S_1\\zeta\n   * \\f}\n   *\n   * where $S_0$ and $S_1$ are defined as\n   *\n   * \\f{align*}{\n   *   S_0 &=\n   *       \\frac{1}{2} \\big\\{\n   *         s_{outer}R_{outer} + s_{inner}R_{inner}\n   *       \\big\\} \\\\\n   *   S_1 &= \\partial_{\\zeta}S\n   *        = \\frac{1}{2} \\big\\{ s_{outer}R_{outer} - s_{inner}R_{inner}\\big\\}\n   * \\f}\n   *\n   * and are stored in `sphere_zero_` and `sphere_rate_`, respectively.\n   *\n   * For a **logarithmic** radial distribution:\n   *\n   * \\f{align*}{\n   *   S(\\zeta) = \\exp{(S_0 + S_1\\zeta)}\n   * \\f}\n   *\n   * where $S_0$ and $S_1$ are defined as\n   *\n   * \\f{align*}{\n   *   S_0 &= \\frac{1}{2} \\ln(R_{outer}R_{inner}) \\\\\n   *   S_1 &= \\frac{1}{2} \\ln(R_{outer}/R_{inner})\n   * \\f}\n   *\n   * With these definitions of $S_0$ and $S_1$, we can rewrite the expression\n   * for $S(\\zeta)$ as:\n   *\n   * \\f{align*}{\n   *   S(\\zeta) &= \\sqrt{R_{inner}^{1-\\zeta}R_{outer}^{1+\\zeta}}\n   * \\f}\n   *\n   * As with the linear distribution, $S_0$ and $S_1$ are stored in\n   * `sphere_zero_` and `sphere_rate_`, respectively.\n   *\n   * For an **inverse** radial distribution:\n   *\n   * \\f{align*}{\n   *   S(\\zeta) =\n   *       \\frac{2R_{inner}R_{outer}}\n   *            {(1 + \\zeta)R_{inner} + (1 - \\zeta)R_{outer}}\n   * \\f}\n   *\n   * In this case, `sphere_zero_` and `sphere_rate_` will simply be `NaN`.\n   *\n   * See Wedge for more details on these quantities.\n   *\n   * \\param zeta the radial source coordinate\n   */\n  template <typename T>\n  tt::remove_cvref_wrap_t<T> get_s_factor(const T& zeta) const;\n  /*!\n   * \\brief Factors out the calculation of $S'(\\zeta)$ needed for the Jacobian\n   *\n   * \\details The value of $S'(\\zeta)$ is computed differently for different\n   * radial distributions.\n   *\n   * For a **linear** radial distribution:\n   *\n   * \\f{align*}{\n   *   S'(\\zeta) =\n   *       \\frac{1}{2} \\big\\{ s_{outer}R_{outer} - s_{inner}R_{inner}\\big\\}\n   * \\f}\n   *\n   * For a **logarithmic** radial distribution:\n   *\n   * \\f{align*}{\n   *   S'(\\zeta) = \\frac{1}{2} S(\\zeta)\\ln(R_{outer}/R_{inner})\n   * \\f}\n   *\n   * where $S(\\zeta)$ is defined in `get_s_factor()`.\n   *\n   * For an **inverse** radial distribution:\n   *\n   * \\f{align*}{\n   *   S'(\\zeta) =\n   *       \\frac{2(R_{inner} R_{outer}^2 - R_{inner}^2 R_{outer})}\n   *            {(R_{inner} + R_{outer} + \\zeta(R_{inner} - R_{outer}))^2}\n   * \\f}\n   *\n   * See Wedge and `get_s_factor()` for more details on these quantities.\n   *\n   * \\param zeta the radial source coordinate\n   * \\param s_factor $S(\\zeta)$ (see `get_s_factor()`)\n   */\n  template <typename T>\n  tt::remove_cvref_wrap_t<T> get_s_factor_deriv(const T& zeta,\n                                                const T& s_factor) const;\n\n  /*!\n   * \\brief Factors out the calculation of $z_{\\Lambda}$ needed for the map and\n   * the Jacobian\n   *\n   * \\details The value of $z_{\\Lambda}$  is computed differently for different\n   * radial distributions.\n   *\n   * For a **linear** radial distribution:\n   *\n   * \\f{align*}{\n   *   z_{\\Lambda} = \\frac{F(\\zeta)}{\\sqrt 3} + \\frac{S(\\zeta)}{\\rho}\n   * \\f}\n   *\n   * For a **logarithmic** or **inverse** radial distribution:\n   *\n   * \\f{align*}{\n   *   z_{\\Lambda} = \\frac{S(\\zeta)}{\\rho}\n   * \\f}\n   *\n   * See Wedge and `get_s_factor()` for more details on these quantities.\n   *\n   * \\param zeta the radial source coordinate\n   * \\param one_over_rho one over $\\rho$ where\n   * $\\rho = |\\vec{\\sigma}_0 - \\vec{x}_0/L| = \\sqrt{(\\Xi - x_0/L)^2 +\n   * (\\mathrm{H} - y_0/L)^2 + (1 - z_0/L)^2}$ (see Wedge)\n   * \\param s_factor $S(\\zeta)$ (see `get_s_factor()`)\n   */\n  template <typename T>\n  tt::remove_cvref_wrap_t<T> get_generalized_z(const T& zeta,\n                                               const T& one_over_rho,\n                                               const T& s_factor) const;\n  template <typename T>\n  tt::remove_cvref_wrap_t<T> get_generalized_z(const T& zeta,\n                                               const T& one_over_rho) const;\n  /*!\n   * \\brief Factors out the calculation of $\\partial_i z_{\\Lambda}$ needed for\n   * the Jacobian\n   *\n   * \\details For **all** radial distributions:\n   *\n   * \\f{align*}{\n   *   \\partial_{\\xi} z_{\\Lambda} &=\n   *       \\frac{-S(\\zeta)\\Xi'\\rho_x}{\\rho^3} \\\\\n   *   \\partial_{\\eta} z_{\\Lambda} &=\n   *       \\frac{-S(\\zeta)\\mathrm{H}'\\rho_y}{\\rho^3} \\\\\n   *   \\partial_{\\zeta} z_{\\Lambda} &=\n   *       \\frac{F'(\\zeta)}{\\sqrt 3} + \\frac{S'(\\zeta)}{\\rho}\n   * \\f}\n   *\n   * However, $\\partial_{\\zeta} z_{\\Lambda}$ reduces to\n   *\n   * \\f{align*}{\n   *   \\partial_{\\zeta} z_{\\Lambda} &= \\frac{S'(\\zeta)}{\\rho}\n   * \\f}\n   *\n   * for **logarithmic** and **inverse** radial distributions because\n   * $F(\\zeta) = 0$.\n   *\n   * See Wedge and `get_s_factor()` for more details on these quantities.\n   *\n   * \\param zeta the radial source coordinate\n   * \\param one_over_rho one over $\\rho$ where\n   * $\\rho = |\\vec{\\sigma}_0 - \\vec{x}_0/L| = \\sqrt{(\\Xi - x_0/L)^2 +\n   * (\\mathrm{H} - y_0/L)^2 + (1 - z_0/L)^2}$ (see Wedge)\n   * \\param s_factor $S(\\zeta)$ (see `get_s_factor()`)\n   * \\param cap_deriv $\\Xi'$ and $\\mathrm{H}'$ (see Wedge)\n   * \\param rho_vec $\\vec{\\rho} = [\\Xi-x_0/L, \\mathrm{H}-y_0/L, 1-z_0/L]^T$\n   * (see Wedge)\n   */\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> get_d_generalized_z(\n      const T& zeta, const T& one_over_rho, const T& s_factor,\n      const std::array<tt::remove_cvref_wrap_t<T>, Dim - 1>& cap_deriv,\n      const std::array<tt::remove_cvref_wrap_t<T>, Dim>& rho_vec) const;\n\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Wedge<LocalDim>& lhs,\n                         const Wedge<LocalDim>& rhs);\n\n  /// Distance from the origin to one of the corners which lie on the inner\n  /// surface.\n  double radius_inner_{std::numeric_limits<double>::signaling_NaN()};\n  /// If this contains a value, it is the distance from the `focal_offset` to\n  /// one of the corners that lie on the outer surface. Set to `std::nullopt`\n  /// when `focal_offset` is nonzero and the outer surface is flat, because\n  /// there is no single outer radius like there is for a centered Wedge or a\n  /// spherical offset Wedge.\n  std::optional<double> radius_outer_ = std::nullopt;\n  /// Value between 0 and 1 which determines whether the inner surface is flat\n  /// (value of 0), spherical (value of 1) or somewhere in between. If\n  /// `focal_offset` is nonzero, `sphericity_inner` must be `1.0`.\n  double sphericity_inner_{std::numeric_limits<double>::signaling_NaN()};\n  /// Value between 0 and 1 which determines whether the outer surface is flat\n  /// (value of 0), spherical (value of 1) or somewhere in between. If\n  /// `focal_offset` is nonzero, `sphericity_outer` must be `0.0` or `1.0`.\n  double sphericity_outer_{std::numeric_limits<double>::signaling_NaN()};\n  /// Half the length of the parent surface (see Wedge documentation for more\n  /// details). This parameter has no effect and is set to `std::nullopt` when\n  /// `focal_offset` is zero.\n  std::optional<double> cube_half_length_ = std::nullopt;\n  /// The target frame coordinates of the focus from which the Wedge is focally\n  /// lifted.\n  std::array<double, Dim> focal_offset_{\n      make_array<Dim>(std::numeric_limits<double>::signaling_NaN())};\n  /// The orientation of the desired wedge relative to the orientation of the\n  /// default wedge which is a wedge that has its curved surfaces pierced by the\n  /// upper-z axis. The logical $\\xi$ and $\\eta$ coordinates point in the\n  /// cartesian x and y directions, respectively.\n  OrientationMap<Dim> orientation_of_wedge_ =\n      OrientationMap<Dim>::create_aligned();\n  /// Determines whether to apply a tangent function mapping to the logical\n  /// coordinates (for `true`) or not (for `false`).\n  bool with_equiangular_map_ = false;\n  /// Determines whether to construct a full wedge or only half a wedge (see\n  /// Wedge documentation for more details)\n  WedgeHalves halves_to_use_ = WedgeHalves::Both;\n  /// Determines how to distribute gridpoints along the radial direction. For\n  /// wedges that are not exactly spherical, only `Distribution::Linear` is\n  /// currently supported.\n  Distribution radial_distribution_ = Distribution::Linear;\n  /// $F_0 / \\sqrt{3}$ (see Wedge documentation)\n  double scaled_frustum_zero_{std::numeric_limits<double>::signaling_NaN()};\n  /// $S_0$ (see Wedge documentation)\n  double sphere_zero_{std::numeric_limits<double>::signaling_NaN()};\n  /// $F_1 / \\sqrt{3}$ (see Wedge documentation)\n  double scaled_frustum_rate_{std::numeric_limits<double>::signaling_NaN()};\n  /// $S_1$ (see Wedge documentation)\n  double sphere_rate_{std::numeric_limits<double>::signaling_NaN()};\n  /// $\\theta_O$ (see Wedge documentation). Set to `std::nullopt` when\n  /// `focal_offset_` is nonzero.\n  std::optional<std::array<double, Dim - 1>> opening_angles_ = std::nullopt;\n  /// $\\theta_D$ (see Wedge documentation). Set to `std::nullopt` when\n  /// `focal_offset_` is nonzero.\n  std::optional<std::array<double, Dim - 1>> opening_angles_distribution_ =\n      std::nullopt;\n};\n\ntemplate <size_t Dim>\nbool operator!=(const Wedge<Dim>& lhs, const Wedge<Dim>& rhs);\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "src/Domain/CoordsToDifferentFrame.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CoordsToDifferentFrame.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n// Transforms cartesian coordinates from one frame to another, by calling\n// block_logical_coordinates and calling the correct map functions.\ntemplate <typename SrcFrame, typename DestFrame>\nvoid coords_to_different_frame(\n    const gsl::not_null<tnsr::I<DataVector, 3, DestFrame>*>\n        dest_cartesian_coords,\n    const tnsr::I<DataVector, 3, SrcFrame>& src_cartesian_coords,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double time) {\n  // If ever additional cases besides Grid->Inertial, Grid->Distorted,\n  // and Inertial->Distorted are needed, add if constexprs below\n  static_assert(std::is_same_v<SrcFrame, ::Frame::Grid> or\n                    std::is_same_v<SrcFrame, ::Frame::Inertial>,\n                \"Source frame must currently be Grid frame or Inertial frame\");\n  const auto block_logical_coords = block_logical_coordinates(\n      domain, src_cartesian_coords, time, functions_of_time);\n\n  tnsr::I<double, 3, DestFrame> x_dest{};\n  tnsr::I<double, 3, SrcFrame> x_src{};\n  for (size_t s = 0; s < get<0>(src_cartesian_coords).size(); ++s) {\n    get<0>(x_src) = get<0>(src_cartesian_coords)[s];\n    get<1>(x_src) = get<1>(src_cartesian_coords)[s];\n    get<2>(x_src) = get<2>(src_cartesian_coords)[s];\n\n    // If this doesn't have a value, then the point isn't in the domain which is\n    // really bad.\n    if (UNLIKELY(not block_logical_coords[s].has_value())) {\n      ERROR(\"A point in the \" << SrcFrame{}\n                              << \" could not be mapped to a block: \" << x_src);\n    }\n\n    const auto& block_id_and_coords = block_logical_coords[s].value();\n    const auto& block = domain.blocks()[block_id_and_coords.id.get_index()];\n\n    if constexpr (std::is_same_v<DestFrame, ::Frame::Distorted> and\n                  std::is_same_v<SrcFrame, ::Frame::Grid>) {\n      if (not block.has_distorted_frame()) {\n        ERROR(\"Point lies outside of distorted-frame region\");\n      }\n      const auto& grid_to_distorted_map =\n          block.moving_mesh_grid_to_distorted_map();\n      x_dest = grid_to_distorted_map(x_src, time, functions_of_time);\n    } else if constexpr (std::is_same_v<DestFrame, ::Frame::Distorted> and\n                         std::is_same_v<SrcFrame, ::Frame::Inertial>) {\n      if (not block.has_distorted_frame()) {\n        ERROR(\"Point lies outside of distorted-frame region\");\n      }\n      const auto& distorted_to_inertial_map =\n          block.moving_mesh_distorted_to_inertial_map();\n      const auto& inv_point =\n          distorted_to_inertial_map.inverse(x_src, time, functions_of_time);\n      if (inv_point.has_value()) {\n        x_dest = *inv_point;\n      } else {\n        ERROR(\"Map from Frame::Distorted to Frame::Inertial is not invertible\");\n      }\n    } else if constexpr (std::is_same_v<DestFrame, ::Frame::Inertial> and\n                         std::is_same_v<SrcFrame, ::Frame::Grid>) {\n      const auto& grid_to_inertial_map =\n          block.moving_mesh_grid_to_inertial_map();\n      x_dest = grid_to_inertial_map(x_src, time, functions_of_time);\n    } else {\n      static_assert(std::is_same_v<DestFrame, ::Frame::Grid> and\n                        std::is_same_v<SrcFrame, ::Frame::Inertial>,\n                    \"Source frame -> destination frame must be Grid -> \"\n                    \"Distorted, Grid -> Inertial, Inertial -> Distorted, or \"\n                    \"Inertial -> Grid\");\n      const auto& grid_to_inertial_map =\n          block.moving_mesh_grid_to_inertial_map();\n      const auto inv_point =\n          grid_to_inertial_map.inverse(x_src, time, functions_of_time);\n      if (inv_point.has_value()) {\n        x_dest = inv_point.value();\n      } else {\n        ERROR(\"Map from Frame::Inertial to Frame::Grid is not invertible\");\n      }\n    }\n\n    get<0>(*dest_cartesian_coords)[s] = get<0>(x_dest);\n    get<1>(*dest_cartesian_coords)[s] = get<1>(x_dest);\n    get<2>(*dest_cartesian_coords)[s] = get<2>(x_dest);\n  }\n}\n\n#define SRCFRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DESTFRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_GRID(_, data)                                         \\\n  template void coords_to_different_frame(                                \\\n      const gsl::not_null<tnsr::I<DataVector, 3, DESTFRAME(data)>*>       \\\n          dest_cartesian_coords,                                          \\\n      const tnsr::I<DataVector, 3, SRCFRAME(data)>& src_cartesian_coords, \\\n      const Domain<3>& domain,                                            \\\n      const std::unordered_map<                                           \\\n          std::string,                                                    \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&      \\\n          functions_of_time,                                              \\\n      const double time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_GRID, (::Frame::Grid),\n                        (::Frame::Inertial, ::Frame::Distorted))\n\ntemplate void coords_to_different_frame(\n    gsl::not_null<tnsr::I<DataVector, 3, ::Frame::Distorted>*>\n        dest_cartesian_coords,\n    const tnsr::I<DataVector, 3, ::Frame::Inertial>& src_cartesian_coords,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    double time);\n\ntemplate void coords_to_different_frame(\n    gsl::not_null<tnsr::I<DataVector, 3, ::Frame::Grid>*> dest_cartesian_coords,\n    const tnsr::I<DataVector, 3, ::Frame::Inertial>& src_cartesian_coords,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    double time);\n\n#undef INSTANTIATE\n#undef INSTANTIATEGENERAL\n#undef INSTANTIATEALIGNED\n#undef DESTFRAME\n#undef SRCFRAME\n"
  },
  {
    "path": "src/Domain/CoordsToDifferentFrame.hpp",
    "content": "// Distributed under the MIT License.Add commentMore actions\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Domain;\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/*!\n *  Transforms cartesian coordinates from one frame to another, by calling\n *  block_logical_coordinates and calling the correct map functions. Current\n *  supported frame changes Grid->Inertial, Grid-Distorted, Inertial->Distorted,\n *  and Inertial->Grid\n */\ntemplate <typename SrcFrame, typename DestFrame>\nvoid coords_to_different_frame(\n    gsl::not_null<tnsr::I<DataVector, 3, DestFrame>*> dest_cartesian_coords,\n    const tnsr::I<DataVector, 3, SrcFrame>& src_cartesian_coords,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    double time);\n"
  },
  {
    "path": "src/Domain/CreateInitialElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/CreateInitialElement.hpp\"\n\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/NeighborIsConforming.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\nSegmentId boundary_segment_id(const size_t refinement_level, const Side side) {\n  if (side == Side::Lower) {\n    return {refinement_level, 0_st};\n  }\n  ASSERT(side == Side::Upper, \"Invalid side: \" << side);\n  return {refinement_level, two_to_the(refinement_level) - 1};\n}\n\nstd::vector<SegmentId> valid_transverse_ids_conforming(\n    const SegmentId& oriented_self_id, const size_t neighbor_refinement_level,\n    const size_t self_block_id, const size_t neighbor_block_id) {\n  const size_t self_refinement_level = oriented_self_id.refinement_level();\n  if (self_refinement_level == neighbor_refinement_level) {\n    return std::vector{oriented_self_id};\n  }\n  if (self_refinement_level == neighbor_refinement_level + 1) {\n    return std::vector{oriented_self_id.id_of_parent()};\n  }\n  if (self_refinement_level + 1 != neighbor_refinement_level) {\n    ERROR(\"Block \" << self_block_id << \" with refinement level \"\n                   << self_refinement_level << \" and neighbor block \"\n                   << neighbor_block_id << \" with refinement level \"\n                   << neighbor_refinement_level << \" differ by more than one.\");\n  }\n  return std::vector{oriented_self_id.id_of_child(Side::Lower),\n                     oriented_self_id.id_of_child(Side::Upper)};\n}\n\nstd::vector<SegmentId> valid_transverse_ids_nonconforming(\n    const size_t neighbor_refinement_level) {\n  std::vector<SegmentId> result(two_to_the(neighbor_refinement_level));\n  for (size_t i = 0; i < result.size(); ++i) {\n    result[i] = SegmentId(neighbor_refinement_level, i);\n  }\n  return result;\n}\n\nstd::unordered_set<ElementId<1>> neighbor_ids(\n    const std::array<std::vector<SegmentId>, 1>& valid_segment_ids,\n    const size_t neighbor_block_id, const size_t grid_index) {\n  ASSERT(valid_segment_ids[0].size() == 1,\n         \"Cannot have more than one neighbor in one dimension.\");\n  return std::unordered_set{ElementId<1>{\n      neighbor_block_id, std::array{valid_segment_ids[0][0]}, grid_index}};\n}\n\nstd::unordered_set<ElementId<2>> neighbor_ids(\n    const std::array<std::vector<SegmentId>, 2>& valid_segment_ids,\n    const size_t neighbor_block_id, const size_t grid_index) {\n  std::unordered_set<ElementId<2>> result;\n  for (const auto& xi_segment : valid_segment_ids[0]) {\n    for (const auto& eta_segment : valid_segment_ids[1]) {\n      result.emplace(neighbor_block_id, std::array{xi_segment, eta_segment},\n                     grid_index);\n    }\n  }\n  return result;\n}\n\nstd::unordered_set<ElementId<3>> neighbor_ids(\n    const std::array<std::vector<SegmentId>, 3>& valid_segment_ids,\n    const size_t neighbor_block_id, const size_t grid_index) {\n  std::unordered_set<ElementId<3>> result;\n  for (const auto& xi_segment : valid_segment_ids[0]) {\n    for (const auto& eta_segment : valid_segment_ids[1]) {\n      for (const auto& zeta_segment : valid_segment_ids[2]) {\n        result.emplace(neighbor_block_id,\n                       std::array{xi_segment, eta_segment, zeta_segment},\n                       grid_index);\n      }\n    }\n  }\n  return result;\n}\n}  // namespace\n\nnamespace domain {\ntemplate <size_t VolumeDim>\nElement<VolumeDim> create_initial_element(\n    const ElementId<VolumeDim>& element_id,\n    const std::vector<Block<VolumeDim>>& blocks,\n    const std::vector<std::array<size_t, VolumeDim>>&\n        initial_refinement_levels) {\n  const auto& block = blocks[element_id.block_id()];\n  const auto& neighbors_of_block = block.neighbors();\n  const auto segment_ids = element_id.segment_ids();\n\n  const auto compute_element_neighbors_in_other_block =\n      [&block, &blocks, &initial_refinement_levels, &neighbors_of_block,\n       &segment_ids, grid_index = element_id.grid_index()](\n          const Direction<VolumeDim>& direction) {\n        const auto& block_neighbors = neighbors_of_block.at(direction);\n        Neighbors<VolumeDim> element_neighbors{\n            std::unordered_set<ElementId<VolumeDim>>{},\n            block_neighbors.orientations(), block_neighbors.are_conforming()};\n\n        if (block_neighbors.size() == 1) {\n          const size_t neighbor_block_id = *(block_neighbors.begin());\n          const auto& orientation =\n              block_neighbors.orientation(neighbor_block_id);\n          const auto direction_from_neighbor =\n              orientation(direction).opposite();\n          std::array<std::vector<SegmentId>, VolumeDim> valid_segment_ids;\n          if (neighbor_is_conforming(block.topologies(),\n                                     blocks[neighbor_block_id].topologies(),\n                                     direction, orientation)) {\n            const auto oriented_segment_ids = orientation(segment_ids);\n            for (size_t d = 0; d < VolumeDim; ++d) {\n              const size_t level =\n                  gsl::at(initial_refinement_levels[neighbor_block_id], d);\n              if (d == direction_from_neighbor.dimension()) {\n                gsl::at(valid_segment_ids, d) = std::vector{\n                    boundary_segment_id(level, direction_from_neighbor.side())};\n              } else {\n                gsl::at(valid_segment_ids, d) = valid_transverse_ids_conforming(\n                    gsl::at(oriented_segment_ids, d), level, block.id(),\n                    neighbor_block_id);\n              }\n            }\n          } else {\n            for (size_t d = 0; d < VolumeDim; ++d) {\n              const size_t level =\n                  gsl::at(initial_refinement_levels[neighbor_block_id], d);\n              if (d == direction_from_neighbor.dimension()) {\n                gsl::at(valid_segment_ids, d) = std::vector{\n                    boundary_segment_id(level, direction_from_neighbor.side())};\n              } else {\n                gsl::at(valid_segment_ids, d) =\n                    valid_transverse_ids_nonconforming(level);\n              }\n            }\n          }\n          element_neighbors.add_ids(\n              neighbor_ids(valid_segment_ids, neighbor_block_id, grid_index));\n        } else {\n          for (const auto& neighbor_block_id : block_neighbors.ids()) {\n            const auto& orientation =\n                block_neighbors.orientation(neighbor_block_id);\n            const auto direction_from_neighbor =\n                orientation(direction).opposite();\n            std::array<std::vector<SegmentId>, VolumeDim> valid_segment_ids;\n            for (size_t d = 0; d < VolumeDim; ++d) {\n              const size_t level =\n                  gsl::at(initial_refinement_levels[neighbor_block_id], d);\n              if (d == direction_from_neighbor.dimension()) {\n                gsl::at(valid_segment_ids, d) = std::vector{\n                    boundary_segment_id(level, direction_from_neighbor.side())};\n              } else {\n                gsl::at(valid_segment_ids, d) =\n                    valid_transverse_ids_nonconforming(level);\n              }\n            }\n            element_neighbors.add_ids(\n                neighbor_ids(valid_segment_ids, neighbor_block_id, grid_index));\n          }\n        }\n\n        return std::make_pair(direction, std::move(element_neighbors));\n      };\n\n  const auto compute_element_neighbor_in_same_block = [&element_id,\n                                                       &segment_ids](\n                                                          const Direction<\n                                                              VolumeDim>&\n                                                              direction) {\n    auto segment_ids_of_neighbor = segment_ids;\n    auto& perpendicular_segment_id =\n        gsl::at(segment_ids_of_neighbor, direction.dimension());\n    const auto index = perpendicular_segment_id.index();\n    perpendicular_segment_id =\n        SegmentId(perpendicular_segment_id.refinement_level(),\n                  direction.side() == Side::Upper ? index + 1 : index - 1);\n    return std::make_pair(\n        direction, Neighbors<VolumeDim>(\n                       {ElementId<VolumeDim>{element_id.block_id(),\n                                             std::move(segment_ids_of_neighbor),\n                                             element_id.grid_index()}},\n                       OrientationMap<VolumeDim>::create_aligned()));\n  };\n\n  typename Element<VolumeDim>::Neighbors_t neighbors_of_element;\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const auto index = gsl::at(segment_ids, d).index();\n    // lower neighbor\n    const auto lower_direction = Direction<VolumeDim>{d, Side::Lower};\n    if (0 == index and 1 == neighbors_of_block.count(lower_direction)) {\n      neighbors_of_element.emplace(\n          compute_element_neighbors_in_other_block(lower_direction));\n    } else if (0 != index) {\n      neighbors_of_element.emplace(\n          compute_element_neighbor_in_same_block(lower_direction));\n    }\n    // upper neighbor\n    const auto upper_direction = Direction<VolumeDim>{d, Side::Upper};\n    if (index == two_to_the(gsl::at(segment_ids, d).refinement_level()) - 1 and\n        1 == neighbors_of_block.count(upper_direction)) {\n      neighbors_of_element.emplace(\n          compute_element_neighbors_in_other_block(upper_direction));\n    } else if (index !=\n               two_to_the(gsl::at(segment_ids, d).refinement_level()) - 1) {\n      neighbors_of_element.emplace(\n          compute_element_neighbor_in_same_block(upper_direction));\n    }\n  }\n  return Element<VolumeDim>(ElementId<VolumeDim>(element_id),\n                            std::move(neighbors_of_element),\n                            block.topologies());\n}\n}  // namespace domain\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template Element<DIM(data)> domain::create_initial_element<DIM(data)>( \\\n      const ElementId<DIM(data)>&, const std::vector<Block<DIM(data)>>&, \\\n      const std::vector<std::array<size_t, DIM(data)>>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Domain/CreateInitialElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Structure/Element.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Block;\ntemplate <size_t Dim>\nclass ElementId;\n/// \\endcond\n\nnamespace domain {\n/*!\n * \\ingroup InitializationGroup\n * \\brief Creates an initial element of a Block.\n *\n * \\details This function creates an element at the refinement level and\n * position specified by the `element_id` within the `blocks`. It assumes\n * that all elements in a given block have the same refinement level,\n * given in `initial_refinement_levels`.\n */\ntemplate <size_t VolumeDim>\nElement<VolumeDim> create_initial_element(\n    const ElementId<VolumeDim>& element_id,\n    const std::vector<Block<VolumeDim>>& blocks,\n    const std::vector<std::array<size_t, VolumeDim>>&\n        initial_refinement_levels);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/AlignedLattice.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/AlignedLattice.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain::creators {\n\nusing ::operator<<;\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& /*s*/,\n                         const RefinementRegion<Dim>& /*unused*/) {\n  ERROR(\n      \"RefinementRegion stream operator is only for option parsing and \"\n      \"should never be called.\");\n}\n\ntemplate <size_t Dim>\nAlignedLattice<Dim>::AlignedLattice(\n    std::array<std::vector<double>, Dim> block_bounds,\n    std::array<size_t, Dim> initial_refinement_levels,\n    std::array<size_t, Dim> initial_number_of_grid_points,\n    std::vector<RefinementRegion<Dim>> refined_refinement,\n    std::vector<RefinementRegion<Dim>> refined_grid_points,\n    std::vector<std::array<size_t, Dim>> blocks_to_exclude,\n    std::array<bool, Dim> is_periodic_in, const Options::Context& context)\n    : block_bounds_(std::move(block_bounds)),\n      is_periodic_in_(is_periodic_in),\n      initial_refinement_levels_(initial_refinement_levels),\n      initial_number_of_grid_points_(initial_number_of_grid_points),\n      refined_refinement_(std::move(refined_refinement)),\n      refined_grid_points_(std::move(refined_grid_points)),\n      blocks_to_exclude_(std::move(blocks_to_exclude)),\n      number_of_blocks_by_dim_{map_array(\n          block_bounds_,\n          [](const std::vector<double>& v) { return v.size() - 1; })} {\n  if (not blocks_to_exclude_.empty() and\n      alg::any_of(is_periodic_in_, [](const bool t) { return t; })) {\n    PARSE_ERROR(context,\n                \"Cannot exclude blocks as well as have periodic boundary \"\n                \"conditions!\");\n  }\n  for (const auto& refinement_region : refined_grid_points_) {\n    for (size_t i = 0; i < Dim; ++i) {\n      if (gsl::at(refinement_region.upper_corner_index, i) >=\n          gsl::at(block_bounds_, i).size()) {\n        PARSE_ERROR(context, \"Refinement region extends to \"\n                                 << refinement_region.upper_corner_index\n                                 << \", which is outside the domain\");\n      }\n    }\n  }\n  for (const auto& refinement_region : refined_refinement_) {\n    for (size_t i = 0; i < Dim; ++i) {\n      if (gsl::at(refinement_region.upper_corner_index, i) >=\n          gsl::at(block_bounds_, i).size()) {\n        PARSE_ERROR(context, \"Refinement region extends to \"\n                                 << refinement_region.upper_corner_index\n                                 << \", which is outside the domain\");\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nAlignedLattice<Dim>::AlignedLattice(\n    std::array<std::vector<double>, Dim> block_bounds,\n    std::array<size_t, Dim> initial_refinement_levels,\n    std::array<size_t, Dim> initial_number_of_grid_points,\n    std::vector<RefinementRegion<Dim>> refined_refinement,\n    std::vector<RefinementRegion<Dim>> refined_grid_points,\n    std::vector<std::array<size_t, Dim>> blocks_to_exclude,\n    std::array<\n        std::array<\n            std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>, 2>,\n        Dim>\n        boundary_conditions,\n    const Options::Context& context)\n    : AlignedLattice(\n          std::move(block_bounds), initial_refinement_levels,\n          initial_number_of_grid_points, std::move(refined_refinement),\n          std::move(refined_grid_points), std::move(blocks_to_exclude),\n          make_array<Dim>(false), context) {\n  // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)\n  boundary_conditions_ = std::move(boundary_conditions);\n  using domain::BoundaryConditions::is_none;\n  using domain::BoundaryConditions::is_periodic;\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto& [lower_bc, upper_bc] = gsl::at(boundary_conditions_, d);\n    ASSERT(lower_bc != nullptr and upper_bc != nullptr,\n           \"None of the boundary conditions can be nullptr.\");\n    if (is_none(lower_bc) or is_none(upper_bc)) {\n      PARSE_ERROR(\n          context,\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\");\n    }\n    if (is_periodic(lower_bc) != is_periodic(upper_bc)) {\n      PARSE_ERROR(context,\n                  \"Periodic boundary conditions must be applied for both \"\n                  \"upper and lower direction in a dimension.\");\n    }\n    if (is_periodic(lower_bc) and is_periodic(upper_bc)) {\n      gsl::at(is_periodic_in_, d) = true;\n    }\n  }\n  if (not blocks_to_exclude_.empty() and\n      alg::any_of(is_periodic_in_, [](const bool t) { return t; })) {\n    PARSE_ERROR(context,\n                \"Cannot exclude blocks as well as have periodic boundary \"\n                \"conditions!\");\n  }\n  for (const auto& refinement_region : refined_grid_points_) {\n    for (size_t i = 0; i < Dim; ++i) {\n      if (gsl::at(refinement_region.upper_corner_index, i) >=\n          gsl::at(block_bounds_, i).size()) {\n        PARSE_ERROR(context, \"Refinement region extends to \"\n                                 << refinement_region.upper_corner_index\n                                 << \", which is outside the domain\");\n      }\n    }\n  }\n  for (const auto& refinement_region : refined_refinement_) {\n    for (size_t i = 0; i < Dim; ++i) {\n      if (gsl::at(refinement_region.upper_corner_index, i) >=\n          gsl::at(block_bounds_, i).size()) {\n        PARSE_ERROR(context, \"Refinement region extends to \"\n                                 << refinement_region.upper_corner_index\n                                 << \", which is outside the domain\");\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nDomain<Dim> AlignedLattice<Dim>::create_domain() const {\n  return rectilinear_domain<Dim>(\n      number_of_blocks_by_dim_, block_bounds_,\n      {std::vector<Index<Dim>>(blocks_to_exclude_.begin(),\n                               blocks_to_exclude_.end())},\n      {}, is_periodic_in_, {}, false);\n}\n\ntemplate <size_t Dim>\nstd::vector<DirectionMap<\n    Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nAlignedLattice<Dim>::external_boundary_conditions() const {\n  if (boundary_conditions_[0][0] == nullptr) {\n#ifdef SPECTRE_DEBUG\n    for (size_t d = 0; d < Dim; ++d) {\n      ASSERT(gsl::at(boundary_conditions_, d)[0] == nullptr and\n                 gsl::at(boundary_conditions_, d)[1] == nullptr,\n             \"Boundary conditions must be set for all directions or none.\");\n    }\n#endif  // SPECTRE_DEBUG\n    return {};\n  }\n  // Set boundary conditions by using the computed domain's external\n  // boundaries\n  const auto domain = create_domain();\n  const auto& blocks = domain.blocks();\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{blocks.size()};\n  for (size_t i = 0; i < blocks.size(); ++i) {\n    for (const Direction<Dim>& external_direction :\n         blocks[i].external_boundaries()) {\n      boundary_conditions[i][external_direction] =\n          gsl::at(gsl::at(boundary_conditions_, external_direction.dimension()),\n                  external_direction.side() == Side::Lower ? 0 : 1)\n              ->get_clone();\n    }\n  }\n  return boundary_conditions;\n}\n\ntemplate <size_t Dim>\nstd::vector<std::string> AlignedLattice<Dim>::block_names() const {\n  return block_names_for_rectilinear_domains(\n      number_of_blocks_by_dim_,\n      std::vector<Index<Dim>>(blocks_to_exclude_.begin(),\n                              blocks_to_exclude_.end()));\n}\n\nnamespace {\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>> apply_refinement_regions(\n    const Index<Dim>& number_of_blocks_by_dim,\n    const std::vector<std::array<size_t, Dim>>& blocks_to_exclude,\n    const std::array<size_t, Dim>& default_refinement,\n    const std::vector<RefinementRegion<Dim>>& refinement_regions) {\n  std::vector<std::array<size_t, Dim>> result;\n  for (const auto& block_index : indices_for_rectilinear_domains(\n           number_of_blocks_by_dim,\n           std::vector<Index<Dim>>(blocks_to_exclude.begin(),\n                                   blocks_to_exclude.end()))) {\n    std::array<size_t, Dim> block_result = default_refinement;\n    for (const auto& refinement_region : refinement_regions) {\n      for (size_t d = 0; d < Dim; ++d) {\n        if (block_index[d] < gsl::at(refinement_region.lower_corner_index, d) or\n            block_index[d] >=\n                gsl::at(refinement_region.upper_corner_index, d)) {\n          goto next_region;\n        }\n      }\n      block_result = refinement_region.refinement;\n    next_region:;\n    }\n    result.push_back(block_result);\n  }\n  return result;\n}\n}  // namespace\n\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>> AlignedLattice<Dim>::initial_extents()\n    const {\n  return apply_refinement_regions(number_of_blocks_by_dim_, blocks_to_exclude_,\n                                  initial_number_of_grid_points_,\n                                  refined_grid_points_);\n}\n\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>>\nAlignedLattice<Dim>::initial_refinement_levels() const {\n  return apply_refinement_regions(number_of_blocks_by_dim_, blocks_to_exclude_,\n                                  initial_refinement_levels_,\n                                  refined_refinement_);\n}\n\ntemplate class AlignedLattice<1>;\ntemplate class AlignedLattice<2>;\ntemplate class AlignedLattice<3>;\ntemplate std::ostream& operator<<(std::ostream& /*s*/,\n                                  const RefinementRegion<1>& /*unused*/);\ntemplate std::ostream& operator<<(std::ostream& /*s*/,\n                                  const RefinementRegion<2>& /*unused*/);\ntemplate std::ostream& operator<<(std::ostream& /*s*/,\n                                  const RefinementRegion<3>& /*unused*/);\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/AlignedLattice.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iosfwd>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain {\nnamespace creators {\n\ntemplate <size_t Dim>\nstruct RefinementRegion {\n  std::array<size_t, Dim> lower_corner_index;\n  std::array<size_t, Dim> upper_corner_index;\n  std::array<size_t, Dim> refinement;\n\n  struct LowerCornerIndex {\n    using type = std::array<size_t, Dim>;\n    static constexpr Options::String help = {\"Lower bound of refined region.\"};\n  };\n\n  struct UpperCornerIndex {\n    using type = std::array<size_t, Dim>;\n    static constexpr Options::String help = {\"Upper bound of refined region.\"};\n  };\n\n  struct Refinement {\n    using type = std::array<size_t, Dim>;\n    static constexpr Options::String help = {\"Refinement inside region.\"};\n  };\n\n  static constexpr Options::String help = {\n      \"A region to be refined differently from the default for the lattice.\\n\"\n      \"The region is a box between the block boundaries indexed by the\\n\"\n      \"Lower- and UpperCornerIndex options.\"};\n  using options = tmpl::list<LowerCornerIndex, UpperCornerIndex, Refinement>;\n  RefinementRegion(const std::array<size_t, Dim>& lower_corner_index_in,\n                   const std::array<size_t, Dim>& upper_corner_index_in,\n                   const std::array<size_t, Dim>& refinement_in)\n      : lower_corner_index(lower_corner_index_in),\n        upper_corner_index(upper_corner_index_in),\n        refinement(refinement_in) {}\n  RefinementRegion() = default;\n};\n\n/// \\cond\n// This is needed to print the default value for the RefinedGridPoints\n// option.  Since the default value is an empty vector, this function\n// is never actually called.\ntemplate <size_t Dim>\n[[noreturn]] std::ostream& operator<<(std::ostream& /*s*/,\n                                      const RefinementRegion<Dim>& /*unused*/);\n/// \\endcond\n\n/// \\brief Create a Domain consisting of multiple aligned Blocks arrayed in a\n/// lattice.\n///\n/// This is useful for setting up problems with piecewise smooth initial data,\n/// problems that specify different boundary conditions on distinct parts of\n/// the boundary, or problems that need different length scales initially.\n///\n/// \\note Adaptive mesh refinement can never join Block%s, so use the fewest\n/// number of Block%s that your problem needs.  More initial Element%s can be\n/// created by specifying a larger `InitialRefinement`.\ntemplate <size_t Dim>\nclass AlignedLattice : public DomainCreator<Dim> {\n public:\n  using maps_list = tmpl::list<\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::Affine>,\n      domain::CoordinateMap<\n          Frame::BlockLogical, Frame::Inertial,\n          CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                         CoordinateMaps::Affine>>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::ProductOf3Maps<\n                                CoordinateMaps::Affine, CoordinateMaps::Affine,\n                                CoordinateMaps::Affine>>>;\n\n  struct BlockBounds {\n    using type = std::array<std::vector<double>, Dim>;\n    static constexpr Options::String help = {\n        \"Coordinates of block boundaries in each dimension.\"};\n  };\n\n  struct IsPeriodicIn {\n    using type = std::array<bool, Dim>;\n    static constexpr Options::String help = {\n        \"Whether the domain is periodic in each dimension.\"};\n  };\n\n  struct InitialLevels {\n    using type = std::array<size_t, Dim>;\n    static constexpr Options::String help = {\n        \"Initial refinement level in each dimension.\"};\n  };\n\n  struct InitialGridPoints {\n    using type = std::array<size_t, Dim>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in each dimension.\"};\n  };\n\n  struct RefinedLevels {\n    using type = std::vector<RefinementRegion<Dim>>;\n    static constexpr Options::String help = {\n        \"h-refined regions.  Later entries take priority.\"};\n  };\n\n  struct RefinedGridPoints {\n    using type = std::vector<RefinementRegion<Dim>>;\n    static constexpr Options::String help = {\n        \"p-refined regions.  Later entries take priority.\"};\n  };\n\n  struct BlocksToExclude {\n    using type = std::vector<std::array<size_t, Dim>>;\n    static constexpr Options::String help = {\n        \"List of Block indices to exclude, if any.\"};\n  };\n\n  template <typename Metavariables>\n  using options = tmpl::list<\n      BlockBounds, InitialLevels, InitialGridPoints, RefinedLevels,\n      RefinedGridPoints, BlocksToExclude,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          typename Rectilinear<Dim>::template BoundaryConditions<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>,\n          IsPeriodicIn>>;\n\n  static constexpr Options::String help = {\n      \"AlignedLattice creates a regular lattice of blocks whose corners are\\n\"\n      \"given by tensor products of the specified BlockBounds. Each Block in\\n\"\n      \"the lattice is identified by a Dim-tuple of zero-based indices\\n\"\n      \"Supplying a list of these tuples to BlocksToExclude will result in\\n\"\n      \"the domain having the corresponding Blocks excluded. See the Domain\\n\"\n      \"Creation tutorial in the documentation for more information on Block\\n\"\n      \"numberings in rectilinear domains. Note that if any Blocks are\\n\"\n      \"excluded, setting the option IsPeriodicIn to `true` in any dimension\\n\"\n      \"will trigger an error, as periodic boundary\\n\"\n      \"conditions for this domain with holes is not supported.\"};\n\n  AlignedLattice(std::array<std::vector<double>, Dim> block_bounds,\n                 std::array<size_t, Dim> initial_refinement_levels,\n                 std::array<size_t, Dim> initial_number_of_grid_points,\n                 std::vector<RefinementRegion<Dim>> refined_refinement,\n                 std::vector<RefinementRegion<Dim>> refined_grid_points,\n                 std::vector<std::array<size_t, Dim>> blocks_to_exclude,\n                 std::array<bool, Dim> is_periodic_in = make_array<Dim>(false),\n                 const Options::Context& context = {});\n\n  AlignedLattice(\n      std::array<std::vector<double>, Dim> block_bounds,\n      std::array<size_t, Dim> initial_refinement_levels,\n      std::array<size_t, Dim> initial_number_of_grid_points,\n      std::vector<RefinementRegion<Dim>> refined_refinement,\n      std::vector<RefinementRegion<Dim>> refined_grid_points,\n      std::vector<std::array<size_t, Dim>> blocks_to_exclude,\n      std::array<std::array<std::unique_ptr<\n                                domain::BoundaryConditions::BoundaryCondition>,\n                            2>,\n                 Dim>\n          boundary_conditions,\n      const Options::Context& context = {});\n\n  template <typename BoundaryConditionsBase>\n  AlignedLattice(\n      std::array<std::vector<double>, Dim> block_bounds,\n      std::array<size_t, Dim> initial_refinement_levels,\n      std::array<size_t, Dim> initial_number_of_grid_points,\n      std::vector<RefinementRegion<Dim>> refined_refinement,\n      std::vector<RefinementRegion<Dim>> refined_grid_points,\n      std::vector<std::array<size_t, Dim>> blocks_to_exclude,\n      std::array<std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                              typename Rectilinear<Dim>::\n                                  template LowerUpperBoundaryCondition<\n                                      BoundaryConditionsBase>>,\n                 Dim>\n          boundary_conditions,\n      const Options::Context& context = {})\n      : AlignedLattice(std::move(block_bounds), initial_refinement_levels,\n                       initial_number_of_grid_points, refined_refinement,\n                       refined_grid_points, blocks_to_exclude,\n                       Rectilinear<Dim>::transform_boundary_conditions(\n                           std::move(boundary_conditions)),\n                       context) {}\n\n  AlignedLattice() = default;\n  AlignedLattice(const AlignedLattice&) = delete;\n  AlignedLattice(AlignedLattice&&) = default;\n  AlignedLattice& operator=(const AlignedLattice&) = delete;\n  AlignedLattice& operator=(AlignedLattice&&) = default;\n  ~AlignedLattice() override = default;\n\n  Domain<Dim> create_domain() const override;\n\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::string> block_names() const override;\n\n  std::vector<std::array<size_t, Dim>> initial_extents() const override;\n\n  std::vector<std::array<size_t, Dim>> initial_refinement_levels()\n      const override;\n\n private:\n  std::array<std::vector<double>, Dim> block_bounds_{\n      make_array<Dim, std::vector<double>>({})};\n  std::array<bool, Dim> is_periodic_in_{make_array<Dim>(false)};\n  std::array<size_t, Dim> initial_refinement_levels_{\n      make_array<Dim>(std::numeric_limits<size_t>::max())};\n  std::array<size_t, Dim> initial_number_of_grid_points_{\n      make_array<Dim>(std::numeric_limits<size_t>::max())};\n  std::vector<RefinementRegion<Dim>> refined_refinement_{};\n  std::vector<RefinementRegion<Dim>> refined_grid_points_{};\n  std::vector<std::array<size_t, Dim>> blocks_to_exclude_{};\n  Index<Dim> number_of_blocks_by_dim_{};\n  std::array<\n      std::array<std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>,\n                 2>,\n      Dim>\n      boundary_conditions_{};\n};\n}  // namespace creators\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/AngularCylinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/AngularCylinder.hpp\"\n\n#include <algorithm>\n#include <memory>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/PolarToCartesian.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace Frame {\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n\nnamespace domain::creators {\nAngularCylinder::AngularCylinder(\n    typename OuterRadius::type outer_radius,\n    typename LowerZBound::type lower_z_bound,\n    typename UpperZBound::type upper_z_bound,\n    typename RadialPartitioning::type radial_partitioning,\n    typename PartitioningInZ::type partitioning_in_z,\n    typename InitialCylinderThetaGridPoints::type\n        initial_cylinder_theta_grid_points,\n    typename InitialCylinderZGridPoints::type initial_cylinder_z_grid_points,\n    typename InitialHollowCylinderGridPoints::type\n        initial_hollow_cylinder_grid_points,\n    typename DistributionInZ::type distribution_in_z,\n    typename InitialRefinementInZ::type initial_refinement_in_z,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dependence,\n    bool is_periodic_in_z, const Options::Context& context)\n    : outer_radius_(outer_radius),\n      lower_z_bound_(lower_z_bound),\n      upper_z_bound_(upper_z_bound),\n      is_periodic_in_z_(is_periodic_in_z),\n      radial_partitioning_(std::move(radial_partitioning)),\n      partitioning_in_z_(std::move(partitioning_in_z)),\n      initial_cylinder_theta_grid_points_(initial_cylinder_theta_grid_points),\n      initial_cylinder_z_grid_points_(initial_cylinder_z_grid_points),\n      distribution_in_z_(std::move(distribution_in_z)),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<3>>();\n  }\n\n  if (outer_radius_ <= 0.0) {\n    PARSE_ERROR(context,\n                \"OuterRadius must be positive, but is: \" << outer_radius_);\n  }\n  if (lower_z_bound_ >= upper_z_bound_) {\n    PARSE_ERROR(context, \"LowerZBound must be less than UpperZBound, but lower=\"\n                             << lower_z_bound_\n                             << \" and upper=\" << upper_z_bound_);\n  }\n\n  if (not radial_partitioning_.empty()) {\n    if (not std::is_sorted(radial_partitioning_.begin(),\n                           radial_partitioning_.end())) {\n      PARSE_ERROR(context,\n                  \"You must specify radial partitioning in ascending order.\");\n    }\n    if (radial_partitioning_.front() <= 0) {\n      PARSE_ERROR(context, \"Radial partitions must be positive, got \"\n                               << radial_partitioning_.front());\n    }\n    if (radial_partitioning_.back() >= outer_radius_) {\n      PARSE_ERROR(context,\n                  \"Last radial partition must be smaller than the outer \"\n                  \"radius, but is \"\n                      << radial_partitioning_.back() << \" with outer radius \"\n                      << outer_radius_);\n    }\n    const auto duplicate = std::adjacent_find(radial_partitioning_.begin(),\n                                              radial_partitioning_.end());\n    if (duplicate != radial_partitioning_.end()) {\n      PARSE_ERROR(context, \"Radial partitioning contains duplicate element: \"\n                               << *duplicate);\n    }\n  }\n\n  if (not partitioning_in_z_.empty()) {\n    if (not std::is_sorted(partitioning_in_z_.begin(),\n                           partitioning_in_z_.end())) {\n      PARSE_ERROR(context, \"Specify partitioning in z in ascending order.\");\n    }\n    if (partitioning_in_z_.front() <= lower_z_bound_) {\n      PARSE_ERROR(context,\n                  \"First z partition must be larger than LowerZBound, but is: \"\n                      << partitioning_in_z_.front());\n    }\n    if (partitioning_in_z_.back() >= upper_z_bound_) {\n      PARSE_ERROR(context,\n                  \"Last z partition must be smaller than UpperZBound, but is: \"\n                      << partitioning_in_z_.back());\n    }\n  }\n\n  const size_t num_radial_blocks = 1 + radial_partitioning_.size();\n  const size_t num_layers = 1 + partitioning_in_z_.size();\n\n  if (distribution_in_z_.size() != num_layers) {\n    PARSE_ERROR(context,\n                \"Specify a 'DistributionInZ' for every layer. You specified \"\n                    << distribution_in_z_.size()\n                    << \" items, but the domain has \" << num_layers\n                    << \" layers.\");\n  }\n  if (distribution_in_z_.front() !=\n      domain::CoordinateMaps::Distribution::Linear) {\n    PARSE_ERROR(context,\n                \"The 'DistributionInZ' must be 'Linear' for the lowermost \"\n                \"layer because a 'Logarithmic' distribution places its \"\n                \"singularity at 'LowerZBound'. Add entries to \"\n                \"'PartitioningInZ' to add layers for which you can select \"\n                \"different distributions along z.\");\n  }\n\n  if (initial_cylinder_theta_grid_points_ % 2 != 1) {\n    PARSE_ERROR(context,\n                \"The number of angular grid points must be odd (this helps \"\n                \"with numerical stability), for center cylinder got \"\n                    << initial_cylinder_theta_grid_points_);\n  }\n\n  num_blocks_ = num_radial_blocks * num_layers;\n\n  // Build block names and groups.\n  // Block ordering: for each layer (outer loop), for each radial block (inner):\n  //   block_id = layer * num_radial_blocks + radial_block\n  block_names_.reserve(num_blocks_);\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    const std::string layer_prefix =\n        num_layers > 1 ? \"Layer\" + std::to_string(layer) : \"\";\n    const std::string center_name = layer_prefix + \"CenterDisk\";\n    block_names_.emplace_back(center_name);\n    if (num_layers > 1) {\n      block_groups_[layer_prefix].insert(center_name);\n    }\n    for (size_t shell = 0; shell < radial_partitioning_.size(); ++shell) {\n      const std::string shell_name =\n          layer_prefix + \"Shell\" + std::to_string(shell);\n      block_names_.emplace_back(shell_name);\n      if (num_layers > 1) {\n        block_groups_[layer_prefix].insert(shell_name);\n      }\n      block_groups_[\"Shells\"].insert(shell_name);\n    }\n  }\n  // InnerDisk holds the center disk blocks across layers\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    block_groups_[\"InnerDisks\"].insert(block_names_[layer * num_radial_blocks]);\n  }\n\n  // Expand hollow cylinder grid points\n  const size_t num_shells = radial_partitioning_.size();\n  if (std::holds_alternative<std::array<size_t, 3>>(\n          initial_hollow_cylinder_grid_points)) {\n    const auto input_value =\n        std::get<std::array<size_t, 3>>(initial_hollow_cylinder_grid_points);\n    if (input_value[1] % 2 != 1) {\n      PARSE_ERROR(context,\n                  \"The number of angular grid points must be odd (this helps \"\n                  \"with numerical stability), for shell(s) got \"\n                      << input_value[1]);\n    }\n    initial_hollow_cylinder_grid_points_ =\n        std::vector<std::array<size_t, 3>>(num_shells, input_value);\n  } else {\n    initial_hollow_cylinder_grid_points_ =\n        std::get<std::vector<std::array<size_t, 3>>>(\n            initial_hollow_cylinder_grid_points);\n    if (initial_hollow_cylinder_grid_points_.size() != num_shells) {\n      PARSE_ERROR(context,\n                  \"InitialHollowCylinderGridPoints must have one entry per \"\n                  \"shell (RadialPartitioning size=\"\n                      << num_shells << \"), but has size \"\n                      << initial_hollow_cylinder_grid_points_.size() << \".\");\n    }\n    for (size_t i = 0; i < initial_hollow_cylinder_grid_points_.size(); ++i) {\n      if (initial_hollow_cylinder_grid_points_[i][1] % 2 != 1) {\n        PARSE_ERROR(context,\n                    \"The number of angular grid points must be odd (this helps \"\n                    \"with numerical stability), for shell \"\n                        << i << \" got \"\n                        << initial_hollow_cylinder_grid_points_[i][1]);\n      }\n    }\n  }\n\n  // Expand initial refinement in z\n  if (std::holds_alternative<size_t>(initial_refinement_in_z)) {\n    const size_t single_refinement = std::get<size_t>(initial_refinement_in_z);\n    initial_refinement_in_z_ =\n        std::vector<size_t>(num_layers, single_refinement);\n  } else {\n    initial_refinement_in_z_ =\n        std::get<std::vector<size_t>>(initial_refinement_in_z);\n    if (initial_refinement_in_z_.size() != num_layers) {\n      PARSE_ERROR(context,\n                  \"InitialRefinementInZ must have one entry per layer \"\n                  \"(PartitioningInZ size + 1 = \"\n                      << num_layers << \"), but has size \"\n                      << initial_refinement_in_z_.size() << \".\");\n    }\n  }\n}\n\nAngularCylinder::AngularCylinder(\n    typename OuterRadius::type outer_radius,\n    typename LowerZBound::type lower_z_bound,\n    typename UpperZBound::type upper_z_bound,\n    typename RadialPartitioning::type radial_partitioning,\n    typename PartitioningInZ::type partitioning_in_z,\n    typename InitialCylinderThetaGridPoints::type\n        initial_cylinder_theta_grid_points,\n    typename InitialCylinderZGridPoints::type initial_cylinder_z_grid_points,\n    typename InitialHollowCylinderGridPoints::type\n        initial_hollow_cylinder_grid_points,\n    typename DistributionInZ::type distribution_in_z,\n    typename InitialRefinementInZ::type initial_refinement_in_z,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dependence,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        lower_z_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        upper_z_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        mantle_boundary_condition,\n    const Options::Context& context)\n    : AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound,\n          std::move(radial_partitioning), std::move(partitioning_in_z),\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          std::move(initial_hollow_cylinder_grid_points),\n          std::move(distribution_in_z), std::move(initial_refinement_in_z),\n          std::move(time_dependence),\n          false,  // is_periodic_in_z\n          context) {\n  // NOLINTNEXTLINE\n  lower_z_boundary_condition_ = std::move(lower_z_boundary_condition);\n  // NOLINTNEXTLINE\n  upper_z_boundary_condition_ = std::move(upper_z_boundary_condition);\n  // NOLINTNEXTLINE\n  mantle_boundary_condition_ = std::move(mantle_boundary_condition);\n\n  // Validate boundary conditions\n  using domain::BoundaryConditions::is_none;\n  using domain::BoundaryConditions::is_periodic;\n  if (lower_z_boundary_condition_ != nullptr) {\n    if (is_none(lower_z_boundary_condition_)) {\n      PARSE_ERROR(context,\n                  \"None boundary condition is not supported for LowerZ. \"\n                  \"Use an outflow-type boundary condition instead.\");\n    }\n    if (is_periodic(lower_z_boundary_condition_) xor\n        is_periodic(upper_z_boundary_condition_)) {\n      PARSE_ERROR(context,\n                  \"Either both lower and upper z-boundary conditions must \"\n                  \"be periodic, or neither.\");\n    }\n    if (is_periodic(lower_z_boundary_condition_) and\n        is_periodic(upper_z_boundary_condition_)) {\n      is_periodic_in_z_ = true;\n      lower_z_boundary_condition_ = nullptr;\n      upper_z_boundary_condition_ = nullptr;\n    }\n  }\n  if (upper_z_boundary_condition_ != nullptr and\n      is_none(upper_z_boundary_condition_)) {\n    PARSE_ERROR(context,\n                \"None boundary condition is not supported for UpperZ. \"\n                \"Use an outflow-type boundary condition instead.\");\n  }\n  if (mantle_boundary_condition_ != nullptr) {\n    if (is_none(mantle_boundary_condition_)) {\n      PARSE_ERROR(context,\n                  \"None boundary condition is not supported for Mantle. \"\n                  \"Use an outflow-type boundary condition instead.\");\n    }\n    if (is_periodic(mantle_boundary_condition_)) {\n      PARSE_ERROR(context,\n                  \"A cylinder can't have periodic boundary conditions in \"\n                  \"the radial direction.\");\n    }\n  } else {\n    if (lower_z_boundary_condition_ != nullptr) {\n      PARSE_ERROR(context,\n                  \"Mantle boundary condition is not set, but lower is. This \"\n                  \"is probably a mistake\");\n    }\n    if (upper_z_boundary_condition_ != nullptr) {\n      PARSE_ERROR(context,\n                  \"Mantle boundary condition is not set, but upper z is. This \"\n                  \"is probably a mistake\");\n    }\n  }\n}\n\nDomain<3> AngularCylinder::create_domain() const {\n  using Affine = CoordinateMaps::Affine;\n  using Identity1D = CoordinateMaps::Identity<1>;\n  using Interval = CoordinateMaps::Interval;\n  const auto aligned = OrientationMap<3>::create_aligned();\n\n  const size_t num_radial_blocks = 1 + radial_partitioning_.size();\n  const size_t num_layers = 1 + partitioning_in_z_.size();\n\n  // Build z-layer bounds\n  std::vector<double> z_bounds;\n  z_bounds.reserve(num_layers + 1);\n  z_bounds.push_back(lower_z_bound_);\n  for (const double z : partitioning_in_z_) {\n    z_bounds.push_back(z);\n  }\n  z_bounds.push_back(upper_z_bound_);\n\n  std::vector<Block<3>> blocks;\n  blocks.reserve(num_blocks_);\n\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    const double z_lower = z_bounds[layer];\n    const double z_upper = z_bounds[layer + 1];\n\n    for (size_t radial = 0; radial < num_radial_blocks; ++radial) {\n      const size_t block_id = layer * num_radial_blocks + radial;\n      const double inner_r =\n          radial == 0 ? 0.0 : radial_partitioning_[radial - 1];\n      const double outer_r = radial == num_radial_blocks - 1\n                                 ? outer_radius_\n                                 : radial_partitioning_[radial];\n\n      // Map: (xi, eta, zeta) in [-1,1]^3\n      //   xi -> r in [inner_r, outer_r]  (Affine)\n      //   eta -> phi in [0, 2pi)         (Identity<1>, passes through)\n      //   zeta -> z in [z_lower, z_upper] (Interval)\n      // Then PolarToCartesian x Identity<1> maps (r, phi, z) -> (x, y, z)\n      auto coord_map =\n          make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n              CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Interval>{\n                  Affine{-1.0, 1.0, inner_r, outer_r}, Identity1D{},\n                  Interval{-1.0, 1.0, z_lower, z_upper,\n                           distribution_in_z_[layer]}},\n              CoordinateMaps::ProductOf2Maps<CoordinateMaps::PolarToCartesian,\n                                             Identity1D>{\n                  CoordinateMaps::PolarToCartesian{}, Identity1D{}});\n\n      DirectionMap<3, BlockNeighbors<3>> neighbors{};\n\n      // Radial neighbors (xi direction)\n      if (radial > 0) {\n        neighbors.emplace(\n            Direction<3>::lower_xi(),\n            BlockNeighbors<3>(layer * num_radial_blocks + radial - 1, aligned));\n      }\n      if (radial < num_radial_blocks - 1) {\n        neighbors.emplace(\n            Direction<3>::upper_xi(),\n            BlockNeighbors<3>(layer * num_radial_blocks + radial + 1, aligned));\n      }\n\n      // z neighbors (zeta direction)\n      if (layer > 0) {\n        neighbors.emplace(\n            Direction<3>::lower_zeta(),\n            BlockNeighbors<3>((layer - 1) * num_radial_blocks + radial,\n                              aligned));\n      } else if (is_periodic_in_z_) {\n        // Connect bottom of layer 0 to top of last layer\n        neighbors.emplace(\n            Direction<3>::lower_zeta(),\n            BlockNeighbors<3>((num_layers - 1) * num_radial_blocks + radial,\n                              aligned));\n      }\n      if (layer < num_layers - 1) {\n        neighbors.emplace(\n            Direction<3>::upper_zeta(),\n            BlockNeighbors<3>((layer + 1) * num_radial_blocks + radial,\n                              aligned));\n      } else if (is_periodic_in_z_) {\n        // Connect top of last layer to bottom of layer 0\n        neighbors.emplace(Direction<3>::upper_zeta(),\n                          BlockNeighbors<3>(radial, aligned));\n      }\n\n      const auto& topology = radial == 0\n                                 ? domain::topologies::full_cylinder\n                                 : domain::topologies::cylindrical_shell;\n\n      blocks.emplace_back(std::move(coord_map), block_id, std::move(neighbors),\n                          block_names_.at(block_id), topology);\n    }\n  }\n\n  Domain<3> domain(std::move(blocks), {}, block_groups_);\n\n  if (not time_dependence_->is_none()) {\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n        block_maps_grid_to_inertial =\n            time_dependence_->block_maps_grid_to_inertial(num_blocks_);\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n        block_maps_grid_to_distorted =\n            time_dependence_->block_maps_grid_to_distorted(num_blocks_);\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n        block_maps_distorted_to_inertial =\n            time_dependence_->block_maps_distorted_to_inertial(num_blocks_);\n    for (size_t block_id = 0; block_id < num_blocks_; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n  return domain;\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nAngularCylinder::external_boundary_conditions() const {\n  if (mantle_boundary_condition_ == nullptr) {\n    return {};\n  }\n  const size_t num_radial_blocks = 1 + radial_partitioning_.size();\n  const size_t num_layers = 1 + partitioning_in_z_.size();\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{num_blocks_};\n\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    for (size_t radial = 0; radial < num_radial_blocks; ++radial) {\n      const size_t block_id = layer * num_radial_blocks + radial;\n\n      // Lower z boundary (lowermost layer only, non-periodic)\n      if (not is_periodic_in_z_ and layer == 0 and\n          lower_z_boundary_condition_ != nullptr) {\n        boundary_conditions[block_id][Direction<3>::lower_zeta()] =\n            lower_z_boundary_condition_->get_clone();\n      }\n\n      // Upper z boundary (uppermost layer only, non-periodic)\n      if (not is_periodic_in_z_ and layer == num_layers - 1 and\n          upper_z_boundary_condition_ != nullptr) {\n        boundary_conditions[block_id][Direction<3>::upper_zeta()] =\n            upper_z_boundary_condition_->get_clone();\n      }\n\n      // Radial (mantle) boundary on the outermost radial block\n      if (radial == num_radial_blocks - 1) {\n        boundary_conditions[block_id][Direction<3>::upper_xi()] =\n            mantle_boundary_condition_->get_clone();\n      }\n    }\n  }\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 3>> AngularCylinder::initial_extents() const {\n  // Throughout the code, we require n_phi be odd for numerical stability\n  // We also require the angular modal space of the angular dimension to be <=\n  // the angular modal space of the radial dimension. Here we set them to be\n  // equal\n  // \\Phi angular modal max M = N_\\Phi / 2 (integer division)\n  // r angular modal max M = 2 * N_r - 2\n  const size_t num_layers = 1 + partitioning_in_z_.size();\n\n  // The center disk block: n_r derived from theta grid points (Zernike),\n  // theta grid points, z grid points\n  const size_t theta_M = initial_cylinder_theta_grid_points_ / 2;\n  const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n\n  std::vector<std::array<size_t, 3>> extents;\n  extents.reserve(num_blocks_);\n\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    // Center disk block\n    extents.emplace_back(\n        std::array<size_t, 3>{disk_n_r, initial_cylinder_theta_grid_points_,\n                              initial_cylinder_z_grid_points_});\n    // Shell blocks\n    for (size_t shell = 0; shell < radial_partitioning_.size(); ++shell) {\n      const auto& shell_pts = initial_hollow_cylinder_grid_points_[shell];\n      extents.emplace_back(\n          std::array<size_t, 3>{shell_pts[0], shell_pts[1], shell_pts[2]});\n    }\n  }\n  return extents;\n}\n\nstd::vector<std::array<size_t, 3>> AngularCylinder::initial_refinement_levels()\n    const {\n  // ZernikeB2 and Fourier should never be refined.\n  const size_t num_radial_blocks = 1 + radial_partitioning_.size();\n  const size_t num_layers = 1 + partitioning_in_z_.size();\n\n  std::vector<std::array<size_t, 3>> refinement_levels;\n  refinement_levels.reserve(num_blocks_);\n\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    for (size_t radial = 0; radial < num_radial_blocks; ++radial) {\n      // Must use 0 for radial and theta refinement, but using the\n      // specified z refinement for each layer\n      refinement_levels.push_back({0, 0, initial_refinement_in_z_[layer]});\n    }\n  }\n  return refinement_levels;\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/AngularCylinder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\ntemplate <size_t Dim>\nclass Identity;\nclass Interval;\nclass PolarToCartesian;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators {\n/*!\n * \\brief Create a 3D filled cylinder domain with radial partitioning using a\n * B2/I1 filled cylinder at the center and Fourier hollow cylinders surrounding\n * it.\n */\nclass AngularCylinder : public DomainCreator<3> {\n public:\n  using maps_list = tmpl::list<domain::CoordinateMap<\n      Frame::BlockLogical, Frame::Inertial,\n      domain::CoordinateMaps::ProductOf3Maps<\n          domain::CoordinateMaps::Affine, domain::CoordinateMaps::Identity<1>,\n          domain::CoordinateMaps::Interval>,\n      domain::CoordinateMaps::ProductOf2Maps<\n          domain::CoordinateMaps::PolarToCartesian,\n          domain::CoordinateMaps::Identity<1>>>>;\n\n  /*!\n   * \\brief Radius of the cylinder's outer edge\n   */\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Radius of the cylinder.\"};\n  };\n\n  /*!\n   * \\brief Lower z-coordinate of the cylinder's base\n   */\n  struct LowerZBound {\n    using type = double;\n    static constexpr Options::String help = {\n        \"z-coordinate of the base of the cylinder.\"};\n  };\n\n  /*!\n   * \\brief Upper z-coordinate of the cylinder's top\n   */\n  struct UpperZBound {\n    using type = double;\n    static constexpr Options::String help = {\n        \"z-coordinate of the top of the cylinder.\"};\n  };\n\n  /*!\n   * \\brief Whether the cylinder is periodic in the z direction\n   */\n  struct IsPeriodicInZ {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"True if periodic in the cylindrical z direction.\"};\n  };\n\n  /*!\n   * \\brief Radial coordinates of the boundaries splitting elements\n   */\n  struct RadialPartitioning {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"Radial coordinates of the boundaries splitting the inner cylinder and \"\n        \"potential hollow cylinder shells\"};\n  };\n\n  /*!\n   * \\brief Z-coordinates of the boundaries splitting the domain into layers\n   */\n  struct PartitioningInZ {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"z-coordinates of the boundaries splitting the domain into layers \"\n        \"between LowerZBound and UpperZBound.\"};\n  };\n\n  /*!\n   * \\brief Initial number of \\f$\\theta\\f$ gridpoints for filled cylinder. The\n   * number for \\f$r\\f$ is accordingly set to match spectral space sizes. This\n   * is enforced to be odd for numerical stability.\n   */\n  struct InitialCylinderThetaGridPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in theta (r is accordingly set). It \"\n        \"must be an odd number for stability.\"};\n  };\n\n  /*!\n   * \\brief Initial number of z gridpoints for the cylinder\n   */\n  struct InitialCylinderZGridPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in z\"};\n  };\n\n  /*!\n   * \\brief Initial number of \\f$[r, \\theta, z]\\f$ gridpoints for hollow\n   * cylinders. Can be one triplet which is applied to all shells, or each can\n   * be specified. The theta component must be odd for numerical stability.\n   */\n  struct InitialHollowCylinderGridPoints {\n    using type =\n        std::variant<std::array<size_t, 3>, std::vector<std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in [r, theta, z] for each hollow \"\n        \"cylinder. If one triplet is given, it will be applied to all blocks, \"\n        \"otherwise each hollow cylinder can be specified. The theta component \"\n        \"must be odd for better numerical stability.\"};\n  };\n\n  /*!\n   * \\brief Grid point distribution along the z-axis for each layer\n   */\n  struct DistributionInZ {\n    using type = std::vector<domain::CoordinateMaps::Distribution>;\n    static constexpr Options::String help = {\n        \"Select the distribution of grid points along the z-axis in each \"\n        \"layer. The lowermost layer must have a 'Linear' distribution, because \"\n        \"both a 'Logarithmic' and 'Inverse' distribution places its \"\n        \"singularity at 'LowerZBound'. The 'PartitioningInZ' determines the \"\n        \"number of layers.\"};\n    static size_t lower_bound_on_size() { return 1; }\n  };\n\n  /*!\n   * \\brief Initial refinement levels in the z direction\n   */\n  struct InitialRefinementInZ {\n    using type = std::variant<size_t, std::vector<size_t>>;\n    static constexpr Options::String help = {\n        \"Initial refinement level in the z-direction. Can be either a single \"\n        \"value applied to all layers, or a vector with one value per layer. \"\n        \"The number of layers is determined by 'PartitioningInZ'.\"};\n  };\n\n  /*!\n   * \\brief Time dependence of the domain\n   */\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain. Specify `None` for no \"\n        \"time dependent maps.\"};\n  };\n\n  /*!\n   * \\brief Boundary conditions group\n   */\n  struct BoundaryConditions {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions\";\n  };\n\n  /*!\n   * \\brief Boundary condition on the lower base\n   */\n  template <typename BoundaryConditionsBase>\n  struct LowerZBoundaryCondition {\n    using group = BoundaryConditions;\n    static std::string name() { return \"LowerZ\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to be imposed on the lower base of the \"\n        \"cylinder, i.e. at the `LowerZBound` in the z-direction.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  /*!\n   * \\brief Boundary condition on the upper base\n   */\n  template <typename BoundaryConditionsBase>\n  struct UpperZBoundaryCondition {\n    using group = BoundaryConditions;\n    static std::string name() { return \"UpperZ\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to be imposed on the upper base of the \"\n        \"cylinder, i.e. at the `UpperZBound` in the z-direction.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  /*!\n   * \\brief Boundary condition on the radial boundary\n   */\n  template <typename BoundaryConditionsBase>\n  struct MantleBoundaryCondition {\n    using group = BoundaryConditions;\n    static std::string name() { return \"Mantle\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to be imposed on the mantle of the \"\n        \"cylinder, i.e. at the `OuterRadius` in the radial direction.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  using basic_options =\n      tmpl::list<OuterRadius, LowerZBound, UpperZBound, RadialPartitioning,\n                 PartitioningInZ, InitialCylinderThetaGridPoints,\n                 InitialCylinderZGridPoints, InitialHollowCylinderGridPoints,\n                 DistributionInZ, InitialRefinementInZ, TimeDependence>;\n\n  template <typename Metavariables>\n  using options = tmpl::append<\n      basic_options,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          tmpl::list<\n              LowerZBoundaryCondition<\n                  domain::BoundaryConditions::get_boundary_conditions_base<\n                      typename Metavariables::system>>,\n              UpperZBoundaryCondition<\n                  domain::BoundaryConditions::get_boundary_conditions_base<\n                      typename Metavariables::system>>,\n              MantleBoundaryCondition<\n                  domain::BoundaryConditions::get_boundary_conditions_base<\n                      typename Metavariables::system>>>,\n          tmpl::list<IsPeriodicInZ>>>;\n\n  static constexpr Options::String help{\n      \"Creates a cylinder using a Zernike basis radially, Fourier in the \"\n      \"angular direction, and I1 in the z direction\"};\n\n  AngularCylinder(\n      typename OuterRadius::type outer_radius,\n      typename LowerZBound::type lower_z_bound,\n      typename UpperZBound::type upper_z_bound,\n      typename RadialPartitioning::type radial_partitioning,\n      typename PartitioningInZ::type partitioning_in_z,\n      typename InitialCylinderThetaGridPoints::type\n          initial_cylinder_theta_grid_points,\n      typename InitialCylinderZGridPoints::type initial_cylinder_z_grid_points,\n      typename InitialHollowCylinderGridPoints::type\n          initial_hollow_cylinder_grid_points,\n      typename DistributionInZ::type distribution_in_z,\n      typename InitialRefinementInZ::type initial_refinement_in_z,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n          time_dependence = nullptr,\n      bool is_periodic_in_z = false,\n      const Options::Context& context = {});\n\n  AngularCylinder(\n      typename OuterRadius::type outer_radius,\n      typename LowerZBound::type lower_z_bound,\n      typename UpperZBound::type upper_z_bound,\n      typename RadialPartitioning::type radial_partitioning,\n      typename PartitioningInZ::type partitioning_in_z,\n      typename InitialCylinderThetaGridPoints::type\n          initial_cylinder_theta_grid_points,\n      typename InitialCylinderZGridPoints::type initial_cylinder_z_grid_points,\n      typename InitialHollowCylinderGridPoints::type\n          initial_hollow_cylinder_grid_points,\n      typename DistributionInZ::type distribution_in_z,\n      typename InitialRefinementInZ::type initial_refinement_in_z,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n          time_dependence = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          lower_z_boundary_condition = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          upper_z_boundary_condition = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          mantle_boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  AngularCylinder() = default;\n  AngularCylinder(const AngularCylinder&) = delete;\n  AngularCylinder(AngularCylinder&&) = default;\n  AngularCylinder& operator=(const AngularCylinder&) = delete;\n  AngularCylinder& operator=(AngularCylinder&&) = default;\n  ~AngularCylinder() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return block_groups_;\n  }\n\n private:\n  typename OuterRadius::type outer_radius_{};\n  typename LowerZBound::type lower_z_bound_{};\n  typename UpperZBound::type upper_z_bound_{};\n  typename IsPeriodicInZ::type is_periodic_in_z_{};\n  typename RadialPartitioning::type radial_partitioning_{};\n  typename PartitioningInZ::type partitioning_in_z_{};\n  typename InitialCylinderThetaGridPoints::type\n      initial_cylinder_theta_grid_points_{};\n  typename InitialCylinderZGridPoints::type initial_cylinder_z_grid_points_{};\n  std::vector<std::array<size_t, 3>> initial_hollow_cylinder_grid_points_{};\n  DistributionInZ::type distribution_in_z_{};\n  std::vector<size_t> initial_refinement_in_z_{};\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n      time_dependence_ = nullptr;\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      lower_z_boundary_condition_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      upper_z_boundary_condition_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      mantle_boundary_condition_{};\n  std::vector<std::string> block_names_;\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_;\n  size_t num_blocks_{};\n};\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/AngularDisk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/AngularDisk.hpp\"\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/PolarToCartesian.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace Frame {\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n\nnamespace domain::creators {\nAngularDisk::AngularDisk(\n    typename OuterRadius::type outer_radius,\n    typename RadialPartitioning::type radial_partitioning,\n    typename InitialDiskThetaGridPoints::type initial_disk_grid_points,\n    typename InitialAnnulusGridPoints::type initial_annulus_grid_points,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n        time_dependence,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        boundary_condition,\n    const Options::Context& context)\n    : outer_radius_(std::move(outer_radius)),\n      radial_partitioning_(std::move(radial_partitioning)),\n      initial_disk_grid_points_(std::move(initial_disk_grid_points)),\n      time_dependence_(std::move(time_dependence)),\n      boundary_condition_(std::move(boundary_condition)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<2>>();\n  }\n  using domain::BoundaryConditions::is_none;\n  if (is_none(boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (boundary_condition_ != nullptr and is_periodic(boundary_condition_)) {\n    PARSE_ERROR(context, \"Cannot have periodic boundary conditions on a disk.\");\n  }\n\n  if (outer_radius_ <= 0) {\n    PARSE_ERROR(context,\n                \"Must have a positive outer radius, got \" << outer_radius);\n  }\n  if (not radial_partitioning_.empty()) {\n    if (not std::is_sorted(radial_partitioning_.begin(),\n                           radial_partitioning_.end())) {\n      PARSE_ERROR(context, \"Specify radial partitioning in ascending order\");\n    }\n    if (radial_partitioning_.front() <= 0) {\n      PARSE_ERROR(context, \"Radial partitions must be positive, got \"\n                               << radial_partitioning_.front());\n    }\n    if (radial_partitioning_.back() >= outer_radius_) {\n      PARSE_ERROR(context,\n                  \"Last radial partition must be smaller than the outer \"\n                  \"radius, but is \"\n                      << radial_partitioning_.back() << \" with outer radius \"\n                      << outer_radius_);\n    }\n    const auto duplicate = std::adjacent_find(radial_partitioning_.begin(),\n                                              radial_partitioning_.end());\n    if (duplicate != radial_partitioning_.end()) {\n      PARSE_ERROR(context, \"Radial partitioning contains duplicate element: \"\n                               << *duplicate);\n    }\n  }\n  if (initial_disk_grid_points_ % 2 != 1) {\n    PARSE_ERROR(context,\n                \"The number of angular grid points must be odd (this helps \"\n                \"with numerical stability), for inner disk got \"\n                    << initial_disk_grid_points);\n  }\n  num_blocks_ = 1 + radial_partitioning_.size();\n\n  if (std::holds_alternative<std::array<size_t, 2>>(\n          initial_annulus_grid_points)) {\n    const auto input_value =\n        std::get<std::array<size_t, 2>>(initial_annulus_grid_points);\n    if (input_value[1] % 2 != 1) {\n      PARSE_ERROR(context,\n                  \"The number of angular grid points must be odd (this helps \"\n                  \"with numerical stability), for shell(s) got \"\n                      << input_value[1]);\n    }\n    initial_annulus_grid_points_ =\n        std::vector<std::array<size_t, 2>>(num_blocks_ - 1, input_value);\n  } else {\n    initial_annulus_grid_points_ = std::get<std::vector<std::array<size_t, 2>>>(\n        initial_annulus_grid_points);\n    if (initial_annulus_grid_points_.size() != num_blocks_ - 1) {\n      PARSE_ERROR(context,\n                  \"InitialAnnulusGridPoints must be one larger than \"\n                  \"RadialPartitioning (size=\"\n                      << radial_partitioning_.size() << \"), but has size \"\n                      << initial_annulus_grid_points_.size() << \".\");\n    }\n    for (size_t i = 0; i < initial_annulus_grid_points_.size(); ++i) {\n      if (initial_annulus_grid_points_[i][1] % 2 != 1) {\n        PARSE_ERROR(context,\n                    \"The number of angular grid points must be odd (this helps \"\n                    \"with numerical stability), for shell \"\n                        << i << \" got \" << initial_annulus_grid_points_[i][1]);\n      }\n    }\n  }\n\n  block_names_.reserve(num_blocks_);\n  block_names_.emplace_back(\"InnerDisk\");\n  block_groups_[\"InnerDisk\"];\n  block_groups_[\"InnerDisk\"].insert(\"InnerDisk\");\n  if (num_blocks_ > 1) {\n    block_groups_[\"Shells\"];\n    for (size_t i = 1; i < num_blocks_; ++i) {\n      const std::string shell = \"Shell\" + std::to_string(i - 1);\n      block_names_.emplace_back(shell);\n      block_groups_[\"Shells\"].insert(shell);\n    }\n  }\n}\n\nDomain<2> AngularDisk::create_domain() const {\n  using Identity = CoordinateMaps::Identity<1>;\n  using Affine = CoordinateMaps::Affine;\n  const auto aligned = OrientationMap<2>::create_aligned();\n\n  std::vector<Block<2>> blocks;\n  blocks.reserve(num_blocks_);\n\n  for (size_t i = 0; i < num_blocks_; ++i) {\n    const double inner_radius = i == 0 ? 0.0 : radial_partitioning_[i - 1];\n    const double outer_radius =\n        i == num_blocks_ - 1 ? outer_radius_ : radial_partitioning_[i];\n    auto coord_map =\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            CoordinateMaps::ProductOf2Maps<Affine, Identity>{\n                Affine{-1.0, 1.0, inner_radius, outer_radius}, Identity{}},\n            CoordinateMaps::PolarToCartesian{});\n    DirectionMap<2, BlockNeighbors<2>> neighbors{};\n    if (num_blocks_ > 1) {\n      if (i == 0) {\n        neighbors.emplace(std::pair(Direction<2>::upper_xi(),\n                                    BlockNeighbors<2>(i + 1, aligned)));\n      } else {\n        neighbors.emplace(std::pair(Direction<2>::lower_xi(),\n                                    BlockNeighbors<2>(i - 1, aligned)));\n        if (i != num_blocks_ - 1) {\n          neighbors.emplace(std::pair(Direction<2>::upper_xi(),\n                                      BlockNeighbors<2>(i + 1, aligned)));\n        }\n      }\n    }\n\n    blocks.emplace_back(\n        std::move(coord_map), i, std::move(neighbors), block_names_.at(i),\n        i == 0 ? domain::topologies::disk : domain::topologies::annulus);\n  }\n\n  Domain<2> domain(std::move(blocks), {}, block_groups_);\n\n  if (not time_dependence_->is_none()) {\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 2>>>\n        block_maps_grid_to_inertial =\n            time_dependence_->block_maps_grid_to_inertial(num_blocks_);\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 2>>>\n        block_maps_grid_to_distorted =\n            time_dependence_->block_maps_grid_to_distorted(num_blocks_);\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 2>>>\n        block_maps_distorted_to_inertial =\n            time_dependence_->block_maps_distorted_to_inertial(num_blocks_);\n    for (size_t block_id = 0; block_id < num_blocks_; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n  return domain;\n}\n\nstd::vector<DirectionMap<\n    2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nAngularDisk::external_boundary_conditions() const {\n  if (boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{num_blocks_};\n  boundary_conditions[num_blocks_ - 1][Direction<2>::upper_xi()] =\n      boundary_condition_->get_clone();\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 2>> AngularDisk::initial_extents() const {\n  // Throughout the code, we require n_phi be odd for numerical stability\n  // We also require the angular modal space of the angular dimension to be <=\n  // the angular modal space of the radial dimension. Here we set them to be\n  // equal\n  // \\Phi angular modal max M = N_\\Phi / 2 (integer division)\n  // r angular modal max M = 2 * N_r - 2\n  const size_t disk_M = initial_disk_grid_points_ / 2;\n  const size_t disk_n_r = disk_M / 2 + 1 + disk_M % 2;\n\n  std::vector<std::array<size_t, 2>> extents;\n  extents.reserve(num_blocks_);\n  extents.emplace_back(\n      std::array<size_t, 2>{disk_n_r, initial_disk_grid_points_});\n  for (const auto& elem : initial_annulus_grid_points_) {\n    extents.emplace_back(elem);\n  }\n  return extents;\n}\n\nstd::vector<std::array<size_t, 2>> AngularDisk::initial_refinement_levels()\n    const {\n  // ZernikeB2 and Fourier should never be refined. The shell I1 refinement is\n  // not implemented\n  return {num_blocks_, {0, 0}};\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/AngularDisk.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\ntemplate <size_t Dim>\nclass Identity;\nclass PolarToCartesian;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators {\n/*!\n * \\brief Create a 2D filled disk domain with radial partitioning using a B2\n * filled disk at the center and Fourier hollow disks surrounding it.\n */\nclass AngularDisk : public DomainCreator<2> {\n public:\n  using maps_list = tmpl::list<domain::CoordinateMap<\n      Frame::BlockLogical, Frame::Inertial,\n      domain::CoordinateMaps::ProductOf2Maps<\n          domain::CoordinateMaps::Affine, domain::CoordinateMaps::Identity<1>>,\n      domain::CoordinateMaps::PolarToCartesian>>;\n\n  /*!\n   * \\brief Radius of the disk's outer edge\n   */\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Radius of the Disk.\"};\n  };\n\n  /*!\n   * \\brief Radial coordinates of the boundaries splitting elements\n   */\n  struct RadialPartitioning {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"Radial coordinates of the boundaries splitting the inner disk and \"\n        \"potential annulus shells\"};\n  };\n\n  /*!\n   * \\brief Initial number of \\f$\\theta\\f$ gridpoints for filled disk. The\n   * number for \\f$r\\f$ is accordingly set to match spectral space sizes. This\n   * is enforced to be odd for numerical stability.\n   */\n  struct InitialDiskThetaGridPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in theta (r is accordingly set). This \"\n        \"value must be odd for better numerical stability.\"};\n  };\n\n  /*!\n   * \\brief Initial number of \\f$[r, \\theta]\\f$ gridpoints. Can be one pair\n   * which is applied to all shells, or each can be specified.\n   */\n  struct InitialAnnulusGridPoints {\n    using type =\n        std::variant<std::array<size_t, 2>, std::vector<std::array<size_t, 2>>>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in [r, theta] for each annulus. If one \"\n        \"pair is given, it will be applied to all blocks, otherwise each \"\n        \"annulus can be specified.\"};\n  };\n\n  /*!\n   * \\brief Boundary condition to impose on outer side\n   */\n  template <typename BoundaryConditionsBase>\n  struct BoundaryCondition {\n    static std::string name() { return \"BoundaryCondition\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to impose on outer side.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  /*!\n   * \\brief Time dependence of the domain\n   */\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain. Specify `None` for no \"\n        \"time dependent maps.\"};\n  };\n\n  using basic_options =\n      tmpl::list<OuterRadius, RadialPartitioning, InitialDiskThetaGridPoints,\n                 InitialAnnulusGridPoints, TimeDependence>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          BoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"Creates a disk using a Zernike basis radially and Fourier in the \"\n      \"angular direction\"};\n\n  AngularDisk(\n      typename OuterRadius::type outer_radius,\n      typename RadialPartitioning::type radial_partitioning,\n      typename InitialDiskThetaGridPoints::type initial_disk_grid_points,\n      typename InitialAnnulusGridPoints::type initial_annulus_grid_points,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n          time_dependence = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  AngularDisk() = default;\n  AngularDisk(const AngularDisk&) = delete;\n  AngularDisk(AngularDisk&&) = default;\n  AngularDisk& operator=(const AngularDisk&) = delete;\n  AngularDisk& operator=(AngularDisk&&) = default;\n  ~AngularDisk() override = default;\n\n  Domain<2> create_domain() const override;\n\n  std::vector<DirectionMap<\n      2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 2>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 2>> initial_refinement_levels() const override;\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return block_groups_;\n  }\n\n private:\n  typename OuterRadius::type outer_radius_{};\n  typename RadialPartitioning::type radial_partitioning_{};\n  typename InitialDiskThetaGridPoints::type initial_disk_grid_points_{};\n  std::vector<std::array<size_t, 2>> initial_annulus_grid_points_{};\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n      time_dependence_ = nullptr;\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      boundary_condition_;\n  std::vector<std::string> block_names_;\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_;\n  size_t num_blocks_{};\n};\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/BinaryCompactObject.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ExpandOverBlocks.hpp\"\n#include \"Domain/Creators/ShellDistribution.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Frame {\nstruct BlockLogical;\n}  // namespace Frame\n\nnamespace domain::creators {\nnamespace bco {\nstd::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\ncreate_grid_anchors(const std::array<double, 3>& center_a,\n                    const std::array<double, 3>& center_b) {\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>> result{};\n  result[\"Center\" + get_output(ObjectLabel::A)] =\n      tnsr::I<double, 3, Frame::Grid>{center_a};\n  result[\"Center\" + get_output(ObjectLabel::B)] =\n      tnsr::I<double, 3, Frame::Grid>{center_b};\n  result[\"Center\"] =\n      tnsr::I<double, 3, Frame::Grid>{0.5 * (center_a + center_b)};\n\n  return result;\n}\n}  // namespace bco\n\ntemplate <bool UseWorldtube>\nbool BinaryCompactObject<UseWorldtube>::Object::is_excised() const {\n  return inner_boundary_condition.has_value();\n}\n\ntemplate <bool UseWorldtube>\nBinaryCompactObject<UseWorldtube>::BinaryCompactObject(\n    typename ObjectA::type object_A, typename ObjectB::type object_B,\n    std::array<double, 2> center_of_mass_offset, const double envelope_radius,\n    const double outer_radius, const double cube_scale,\n    const typename InitialRefinement::type& initial_refinement,\n    const typename InitialGridPoints::type& initial_number_of_grid_points,\n    const bool use_equiangular_map,\n    const CoordinateMaps::Distribution radial_distribution_envelope,\n    const std::vector<double>& radial_partitioning_outer_shell,\n    const typename RadialDistributionOuterShell::type&\n        radial_distribution_outer_shell,\n    const double opening_angle_in_degrees,\n    std::optional<bco::TimeDependentMapOptions<false>> time_dependent_options,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        outer_boundary_condition,\n    const Options::Context& context)\n    : object_A_(std::move(object_A)),\n      object_B_(std::move(object_B)),\n      center_of_mass_offset_(center_of_mass_offset),\n      envelope_radius_(envelope_radius),\n      outer_radius_(outer_radius),\n      use_equiangular_map_(use_equiangular_map),\n      radial_distribution_envelope_(radial_distribution_envelope),\n      radial_partitioning_outer_shell_(radial_partitioning_outer_shell),\n      outer_boundary_condition_(std::move(outer_boundary_condition)),\n      x_coord_a_(\n          std::visit([](const auto& arg) { return arg.x_coord; }, object_A_)),\n      x_coord_b_(\n          std::visit([](const auto& arg) { return arg.x_coord; }, object_B_)),\n      is_excised_a_(std::visit([](const auto& arg) { return arg.is_excised(); },\n                               object_A_)),\n      is_excised_b_(std::visit([](const auto& arg) { return arg.is_excised(); },\n                               object_B_)),\n      use_single_block_a_(\n          std::holds_alternative<CartesianCubeAtXCoord>(object_A_)),\n      use_single_block_b_(\n          std::holds_alternative<CartesianCubeAtXCoord>(object_B_)),\n      time_dependent_options_(std::move(time_dependent_options)),\n      opening_angle_(M_PI * opening_angle_in_degrees / 180.0) {\n  // Determination of parameters for domain construction:\n  const double tan_half_opening_angle = tan(0.5 * opening_angle_);\n  translation_ = 0.5 * (x_coord_a_ + x_coord_b_);\n  length_inner_cube_ = cube_scale * (x_coord_a_ - x_coord_b_);\n  if (length_inner_cube_ < (x_coord_a_ - x_coord_b_)) {\n    PARSE_ERROR(\n        context,\n        \"The cube length should be greater than or equal to the initial \"\n        \"separation between the two objects.\");\n  }\n  length_outer_cube_ =\n      2.0 * envelope_radius_ / sqrt(2.0 + square(tan_half_opening_angle));\n\n  // We chose to handle potential roundoff differences here by using equal\n  // within roundoff instead of exact equality because the wedge map expects\n  // exact equality when checking for a zero offset.\n  if (equal_within_roundoff(cube_scale, 1.0)) {\n    offset_x_coord_a_ = 0.0;\n    offset_x_coord_b_ = 0.0;\n  } else {\n    offset_x_coord_a_ =\n        x_coord_a_ - (x_coord_a_ + x_coord_b_ + length_inner_cube_) * 0.5;\n    offset_x_coord_b_ =\n        x_coord_b_ - (x_coord_a_ + x_coord_b_ - length_inner_cube_) * 0.5;\n  }\n\n  set_shell_distribution(make_not_null(&number_of_outer_shells_),\n                         make_not_null(&radial_distribution_outer_shell_),\n                         radial_partitioning_outer_shell_,\n                         radial_distribution_outer_shell, envelope_radius_,\n                         outer_radius_, \"envelope\", \"outer\", context);\n\n  // Calculate number of blocks\n  // Object cubes and shells have 6 blocks each, for a total for 24 blocks.\n  // The envelope and each outer shell have another 10 blocks each.\n  number_of_blocks_ = 34 + 10 * number_of_outer_shells_;\n  // For each object whose interior is not excised, add 1 block\n  if ((not use_single_block_a_) and (not is_excised_a_)) {\n    number_of_blocks_++;\n  }\n  if ((not use_single_block_b_) and (not is_excised_b_)) {\n    number_of_blocks_++;\n  }\n\n  // For each of the object replaced by a single block, remove (12-1)=11\n  if (use_single_block_a_) {\n    number_of_blocks_ -= 11;\n  }\n  if (use_single_block_b_) {\n    number_of_blocks_ -= 11;\n  }\n\n  if (x_coord_a_ <= 0.0) {\n    PARSE_ERROR(\n        context,\n        \"The x-coordinate of ObjectA's center is expected to be positive.\");\n  }\n  if (x_coord_b_ >= 0.0) {\n    PARSE_ERROR(\n        context,\n        \"The x-coordinate of ObjectB's center is expected to be negative.\");\n  }\n  if (envelope_radius_ <= length_inner_cube_ * sqrt(3.0)) {\n    const double suggested_value = 2.0 * length_inner_cube_ * sqrt(3.0);\n    PARSE_ERROR(\n        context,\n        \"The radius for the enveloping cube is too small! The Frustums will be \"\n        \"malformed. A recommended radius is:\\n\"\n            << suggested_value);\n  }\n  // The following options are irrelevant if the inner regions are covered\n  // with simple blocks, so we only check them if object_A_ uses the first\n  // variant type.\n  if (not use_single_block_a_) {\n    const auto& object_a = std::get<Object>(object_A_);\n    if (object_a.outer_radius < object_a.inner_radius) {\n      PARSE_ERROR(context,\n                  \"ObjectA's inner radius must be less than its outer radius.\");\n    }\n    // The length of the cube surrounding each object is length_inner_cube_\n    // / 2.0, this checks to make sure that the shell is not touching the cube.\n    if (object_a.outer_radius >=\n        (length_inner_cube_ / 2.0) + offset_x_coord_a_) {\n      PARSE_ERROR(\n          context,\n          \"ObjectA's outer radius is too large for the given separation, \"\n          \" try using \"\n              << length_inner_cube_ / (2.5 * cube_scale));\n    }\n    if (object_a.use_logarithmic_map and not object_a.is_excised()) {\n      PARSE_ERROR(\n          context,\n          \"Using a logarithmically spaced radial grid in the part \"\n          \"of Layer 1 enveloping Object A requires excising the interior of \"\n          \"Object A\");\n    }\n    if (not object_a.is_excised() and\n        not equal_within_roundoff(cube_scale, 1.0)) {\n      ERROR(\n          \"A filled object cannot be offset within its cube. Set the CubeScale \"\n          \"to 1.\");\n    }\n    if (object_a.is_excised() and\n        ((*object_a.inner_boundary_condition == nullptr) !=\n         (outer_boundary_condition_ == nullptr))) {\n      PARSE_ERROR(\n          context,\n          \"Must specify either both inner and outer boundary conditions \"\n          \"or neither.\");\n    }\n  }\n  if (not use_single_block_b_) {\n    const auto& object_b = std::get<Object>(object_B_);\n    if (object_b.outer_radius < object_b.inner_radius) {\n      PARSE_ERROR(context,\n                  \"ObjectB's inner radius must be less than its outer radius.\");\n    }\n    // The length of the cube surrounding each object is length_inner_cube_\n    // / 2.0, this checks to make sure that the shell is not touching the cube.\n    if (object_b.outer_radius >=\n        (length_inner_cube_ / 2.0) - offset_x_coord_b_) {\n      PARSE_ERROR(\n          context,\n          \"ObjectB's outer radius is too large for the given separation, \"\n          \" try using \"\n              << length_inner_cube_ / (2.5 * cube_scale));\n    }\n    if (object_b.use_logarithmic_map and not object_b.is_excised()) {\n      PARSE_ERROR(\n          context,\n          \"Using a logarithmically spaced radial grid in the part \"\n          \"of Layer 1 enveloping Object B requires excising the interior of \"\n          \"Object B\");\n    }\n    if (not object_b.is_excised() and\n        not equal_within_roundoff(cube_scale, 1.0)) {\n      ERROR(\n          \"A filled object cannot be offset within its cube. Set the CubeScale \"\n          \"to 1.\");\n    }\n    if (object_b.is_excised() and\n        ((*object_b.inner_boundary_condition == nullptr) !=\n         (outer_boundary_condition_ == nullptr))) {\n      PARSE_ERROR(\n          context,\n          \"Must specify either both inner and outer boundary conditions \"\n          \"or neither.\");\n    }\n  }\n  const bool filled_excision_a = not(use_single_block_a_ or is_excised_a_);\n  const bool filled_excision_b = not(use_single_block_b_ or is_excised_b_);\n  if ((filled_excision_a or filled_excision_b) and\n      not equal_within_roundoff(offset_x_coord_a_, 0.0)) {\n    PARSE_ERROR(context,\n                \"Setting CubeScale > 1.0 is not supported for domains with \"\n                \"ExciseInterior = False. Consider using \"\n                \"CartesianCubeAtX for the Object without an excised interior.\");\n  }\n\n  if (envelope_radius_ >= outer_radius_) {\n    PARSE_ERROR(context,\n                \"The outer radius must be larger than the envelope radius.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(outer_boundary_condition_) or\n      (is_excised_a_ and\n       is_periodic(*(std::get<Object>(object_A_).inner_boundary_condition))) or\n      (is_excised_b_ and\n       is_periodic(*(std::get<Object>(object_B_).inner_boundary_condition)))) {\n    PARSE_ERROR(\n        context,\n        \"Cannot have periodic boundary conditions with a binary domain\");\n  }\n\n  // Create grid anchors\n  grid_anchors_ =\n      bco::create_grid_anchors(std::array{x_coord_a_, center_of_mass_offset_[0],\n                                          center_of_mass_offset_[1]},\n                               std::array{x_coord_b_, center_of_mass_offset_[0],\n                                          center_of_mass_offset_[1]});\n\n  // Create block names and groups\n  static std::array<std::string, 6> wedge_directions{\n      \"UpperZ\", \"LowerZ\", \"UpperY\", \"LowerY\", \"UpperX\", \"LowerX\"};\n  const auto add_object_region = [this](const std::string& object_name,\n                                        const std::string& region_name) {\n    for (const std::string& wedge_direction : wedge_directions) {\n      const std::string block_name =\n          // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n          object_name + region_name + wedge_direction;\n      block_names_.push_back(block_name);\n      block_groups_[object_name + region_name].insert(block_name);\n    }\n  };\n  const auto add_object_interior = [this](const std::string& object_name) {\n    const std::string block_name = object_name + \"Interior\";\n    block_names_.push_back(block_name);\n  };\n  const auto add_outer_region = [this](const std::string& region_name) {\n    for (const std::string& wedge_direction : wedge_directions) {\n      for (const std::string& leftright : {\"Left\"s, \"Right\"s}) {\n        if ((wedge_direction == \"UpperX\" and leftright == \"Left\") or\n            (wedge_direction == \"LowerX\" and leftright == \"Right\")) {\n          // The outer regions are divided in half perpendicular to the\n          // x-axis at x=0. Therefore, the left side only has a block in\n          // negative x-direction, and the right side only has one in\n          // positive x-direction.\n          continue;\n        }\n        // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n        const std::string block_name =\n            region_name + wedge_direction +\n            (wedge_direction == \"UpperX\" or wedge_direction == \"LowerX\"\n                 ? \"\"\n                 : leftright);\n        block_names_.push_back(block_name);\n        block_groups_[region_name].insert(block_name);\n      }\n    }\n  };\n  // Finding the first block of outer shell\n  first_outer_shell_block_ = 0;\n  if (use_single_block_a_) {\n    block_names_.emplace_back(\"ObjectA\");\n    first_outer_shell_block_ += 1;\n  } else {\n    add_object_region(\"ObjectA\", \"Shell\");  // 6 blocks\n    add_object_region(\"ObjectA\", \"Cube\");   // 6 blocks\n    first_outer_shell_block_ += 12;\n  }\n  if (use_single_block_b_) {\n    block_names_.emplace_back(\"ObjectB\");\n    first_outer_shell_block_ += 1;\n  } else {\n    add_object_region(\"ObjectB\", \"Shell\");  // 6 blocks\n    add_object_region(\"ObjectB\", \"Cube\");   // 6 blocks\n    first_outer_shell_block_ += 12;\n  }\n  add_outer_region(\"Envelope\");  // 10 blocks\n  first_outer_shell_block_ += 10;\n  for (size_t shell = 0; shell < number_of_outer_shells_; shell++) {\n    add_outer_region(\"OuterShell\" + std::to_string(shell));  // 10 blocks\n  }\n\n  if ((not use_single_block_a_) and (not is_excised_a_)) {\n    add_object_interior(\"ObjectA\");  // 1 block\n  }\n  if ((not use_single_block_b_) and (not is_excised_b_)) {\n    add_object_interior(\"ObjectB\");  // 1 block\n  }\n\n  ASSERT(block_names_.size() == number_of_blocks_,\n         \"Number of block names (\" << block_names_.size()\n                                   << \") doesn't match number of blocks (\"\n                                   << number_of_blocks_ << \").\");\n\n  // Expand initial refinement and number of grid points over all blocks\n  const ExpandOverBlocks<std::array<size_t, 3>> expand_over_blocks{\n      block_names_, block_groups_};\n\n  try {\n    initial_refinement_ = std::visit(expand_over_blocks, initial_refinement);\n  } catch (const std::exception& error) {\n    PARSE_ERROR(context, \"Invalid 'InitialRefinement': \" << error.what());\n  }\n  try {\n    initial_number_of_grid_points_ =\n        std::visit(expand_over_blocks, initial_number_of_grid_points);\n  } catch (const std::exception& error) {\n    PARSE_ERROR(context, \"Invalid 'InitialGridPoints': \" << error.what());\n  }\n\n  // Build time-dependent maps\n  std::optional<std::array<double, 3>> radii_A{};\n  std::optional<std::array<double, 3>> radii_B{};\n\n  if (not use_single_block_a_) {\n    radii_A = std::array{std::get<Object>(object_A_).inner_radius,\n                         std::get<Object>(object_A_).outer_radius,\n                         sqrt(3.0) * 0.5 * length_inner_cube_};\n  }\n  if (not use_single_block_b_) {\n    radii_B = std::array{std::get<Object>(object_B_).inner_radius,\n                         std::get<Object>(object_B_).outer_radius,\n                         sqrt(3.0) * 0.5 * length_inner_cube_};\n  }\n\n  if (time_dependent_options_.has_value()) {\n    // The reason we don't just always use half the inner cube length is to\n    // avoid potential roundoff issues if there is no offset\n    const std::optional<std::array<double, 3>> cube_A_center =\n        length_inner_cube_ == x_coord_a_ - x_coord_b_\n            ? std::optional<std::array<double, 3>>{}\n            : std::array{translation_ + 0.5 * length_inner_cube_,\n                         center_of_mass_offset_[0], center_of_mass_offset_[1]};\n    const std::optional<std::array<double, 3>> cube_B_center =\n        length_inner_cube_ == x_coord_a_ - x_coord_b_\n            ? std::optional<std::array<double, 3>>{}\n            : std::array{translation_ - 0.5 * length_inner_cube_,\n                         center_of_mass_offset_[0], center_of_mass_offset_[1]};\n    time_dependent_options_->build_maps(\n        std::array{std::array{x_coord_a_, center_of_mass_offset_[0],\n                              center_of_mass_offset_[1]},\n                   std::array{x_coord_b_, center_of_mass_offset_[0],\n                              center_of_mass_offset_[1]}},\n        cube_A_center, cube_B_center,\n        std::array{translation_, center_of_mass_offset[0],\n                   center_of_mass_offset[1]},\n        radii_A, radii_B, not is_excised_a_, not is_excised_b_,\n        envelope_radius_, outer_radius_);\n  }\n}\n\ntemplate <bool UseWorldtube>\nDomain<3> BinaryCompactObject<UseWorldtube>::create_domain() const {\n  const double inner_sphericity_A = is_excised_a_ ? 1.0 : 0.0;\n  const double inner_sphericity_B = is_excised_b_ ? 1.0 : 0.0;\n\n  using Maps = std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>;\n\n  const std::vector<domain::CoordinateMaps::Distribution>\n      object_A_radial_distribution{\n          ((not use_single_block_a_) and\n           std::get<Object>(object_A_).use_logarithmic_map)\n              ? domain::CoordinateMaps::Distribution::Logarithmic\n              : domain::CoordinateMaps::Distribution::Linear};\n\n  const std::vector<domain::CoordinateMaps::Distribution>\n      object_B_radial_distribution{\n          ((not use_single_block_b_) and\n           std::get<Object>(object_B_).use_logarithmic_map)\n              ? domain::CoordinateMaps::Distribution::Logarithmic\n              : domain::CoordinateMaps::Distribution::Linear};\n\n  Maps maps{};\n\n  // ObjectA/B is on the right/left, respectively.\n  const Affine3D translation_A{\n      Affine{-1.0, 1.0, -1.0 + x_coord_a_ - offset_x_coord_a_,\n             1.0 + x_coord_a_ - offset_x_coord_a_},\n      Affine{-1.0, 1.0, -1.0 + center_of_mass_offset_[0],\n             1.0 + center_of_mass_offset_[0]},\n      Affine{-1.0, 1.0, -1.0 + center_of_mass_offset_[1],\n             1.0 + center_of_mass_offset_[1]}};\n  const Affine3D translation_B{\n      Affine{-1.0, 1.0, -1.0 + x_coord_b_ - offset_x_coord_b_,\n             1.0 + x_coord_b_ - offset_x_coord_b_},\n      Affine{-1.0, 1.0, -1.0 + center_of_mass_offset_[0],\n             1.0 + center_of_mass_offset_[0]},\n      Affine{-1.0, 1.0, -1.0 + center_of_mass_offset_[1],\n             1.0 + center_of_mass_offset_[1]}};\n\n  // Two blocks covering the compact objects and their immediate neighborhood\n  if (use_single_block_a_) {\n    maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(Affine3D{\n            Affine(-1.0, 1.0,\n                   -0.5 * length_inner_cube_ + x_coord_a_ - offset_x_coord_a_,\n                   0.5 * length_inner_cube_ + x_coord_a_ - offset_x_coord_a_),\n            Affine(-1.0, 1.0,\n                   -0.5 * length_inner_cube_ + center_of_mass_offset_[0],\n                   0.5 * length_inner_cube_ + center_of_mass_offset_[0]),\n            Affine(-1.0, 1.0,\n                   -0.5 * length_inner_cube_ + center_of_mass_offset_[1],\n                   0.5 * length_inner_cube_ + center_of_mass_offset_[1])}));\n  } else {\n    // --- Blocks enclosing each object (12 blocks per object) ---\n    //\n    // Each object is surrounded by 6 inner wedges that make a sphere, and\n    // another 6 outer wedges that transition to a cube.\n    const auto& object_a = std::get<Object>(object_A_);\n    const auto& offset_a_optional =\n        offset_x_coord_a_ == 0\n            ? std::nullopt\n            : std::make_optional(std::make_pair(\n                  length_inner_cube_ * 0.5,\n                  std::array<double, 3>{{offset_x_coord_a_, 0.0, 0.0}}));\n    Maps maps_center_A =\n        domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                Frame::Inertial, 3>(\n            sph_wedge_coordinate_maps(\n                object_a.inner_radius, object_a.outer_radius,\n                inner_sphericity_A, 1.0, use_equiangular_map_,\n                offset_a_optional, false, {}, object_A_radial_distribution),\n            translation_A);\n    Maps maps_cube_A =\n        domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                Frame::Inertial, 3>(\n            sph_wedge_coordinate_maps(\n                object_a.outer_radius, sqrt(3.0) * 0.5 * length_inner_cube_,\n                1.0, 0.0, use_equiangular_map_, offset_a_optional),\n            translation_A);\n    std::move(maps_center_A.begin(), maps_center_A.end(),\n              std::back_inserter(maps));\n    std::move(maps_cube_A.begin(), maps_cube_A.end(), std::back_inserter(maps));\n  }\n  if (use_single_block_b_) {\n    maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(Affine3D{\n            Affine(-1.0, 1.0,\n                   -0.5 * length_inner_cube_ + x_coord_b_ - offset_x_coord_b_,\n                   0.5 * length_inner_cube_ + x_coord_b_ - offset_x_coord_b_),\n            Affine(-1.0, 1.0,\n                   -0.5 * length_inner_cube_ + center_of_mass_offset_[0],\n                   0.5 * length_inner_cube_ + center_of_mass_offset_[0]),\n            Affine(-1.0, 1.0,\n                   -0.5 * length_inner_cube_ + center_of_mass_offset_[1],\n                   0.5 * length_inner_cube_ + center_of_mass_offset_[1])}));\n  } else {\n    // --- Blocks enclosing each object (12 blocks per object) ---\n    //\n    // Each object is surrounded by 6 inner wedges that make a sphere, and\n    // another 6 outer wedges that transition to a cube.\n    const auto& object_b = std::get<Object>(object_B_);\n    const auto& offset_b_optional =\n        offset_x_coord_b_ == 0\n            ? std::nullopt\n            : std::make_optional(std::make_pair(\n                  length_inner_cube_ * 0.5,\n                  std::array<double, 3>{{offset_x_coord_b_, 0.0, 0.0}}));\n    Maps maps_center_B =\n        domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                Frame::Inertial, 3>(\n            sph_wedge_coordinate_maps(\n                object_b.inner_radius, object_b.outer_radius,\n                inner_sphericity_B, 1.0, use_equiangular_map_,\n                offset_b_optional, false, {}, object_B_radial_distribution),\n            translation_B);\n    Maps maps_cube_B =\n        domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                Frame::Inertial, 3>(\n            sph_wedge_coordinate_maps(\n                object_b.outer_radius, sqrt(3.0) * 0.5 * length_inner_cube_,\n                1.0, 0.0, use_equiangular_map_, offset_b_optional),\n            translation_B);\n    std::move(maps_center_B.begin(), maps_center_B.end(),\n              std::back_inserter(maps));\n    std::move(maps_cube_B.begin(), maps_cube_B.end(), std::back_inserter(maps));\n  }\n\n  // --- Frustums enclosing both objects (10 blocks) ---\n  //\n  // The two abutting cubes are enclosed by a layer of bulged frustums that form\n  // a sphere enveloping both objects. While the two objects can be offset from\n  // the origin to account for their center of mass, the enveloping frustums are\n  // centered at the origin.\n  Maps maps_frustums = domain::make_vector_coordinate_map_base<\n      Frame::BlockLogical, Frame::Inertial, 3>(frustum_coordinate_maps(\n      length_inner_cube_, length_outer_cube_, use_equiangular_map_,\n      use_equiangular_map_ and not use_single_block_a_ and\n          not use_single_block_b_,\n      {{-translation_, -center_of_mass_offset_[0], -center_of_mass_offset_[1]}},\n      radial_distribution_envelope_,\n      radial_distribution_envelope_ ==\n              domain::CoordinateMaps::Distribution::Projective\n          ? std::optional<double>(length_inner_cube_ / length_outer_cube_)\n          : std::optional<double>(-(length_outer_cube_ + length_inner_cube_) /\n                                  (length_outer_cube_ - length_inner_cube_)),\n      1.0, opening_angle_));\n  std::move(maps_frustums.begin(), maps_frustums.end(),\n            std::back_inserter(maps));\n\n  // --- Outer spherical shell (10*num_outer_shells blocks) ---\n  Maps maps_outer_shell = domain::make_vector_coordinate_map_base<\n      Frame::BlockLogical, Frame::Inertial, 3>(sph_wedge_coordinate_maps(\n      envelope_radius_, outer_radius_, 1.0, 1.0, use_equiangular_map_,\n      std::nullopt, true, radial_partitioning_outer_shell_,\n      radial_distribution_outer_shell_, ShellWedges::All, opening_angle_));\n  std::move(maps_outer_shell.begin(), maps_outer_shell.end(),\n            std::back_inserter(maps));\n\n  // --- (Optional) object centers (0 to 2 blocks) ---\n  //\n  // Each object can optionally be filled with a cube-shaped block, in which\n  // case the enclosing wedges configured above transition from the cube to a\n  // sphere.\n  std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};\n  if (not use_single_block_a_) {\n    if (not is_excised_a_) {\n      const double scaled_r_inner_A =\n          std::get<Object>(object_A_).inner_radius / sqrt(3.0);\n      if (use_equiangular_map_) {\n        maps.emplace_back(\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                Equiangular3D{\n                    Equiangular(-1.0, 1.0,\n                                -1.0 * scaled_r_inner_A + offset_x_coord_a_,\n                                scaled_r_inner_A + offset_x_coord_a_),\n                    Equiangular(-1.0, 1.0, -1.0 * scaled_r_inner_A,\n                                scaled_r_inner_A),\n                    Equiangular(-1.0, 1.0, -1.0 * scaled_r_inner_A,\n                                scaled_r_inner_A)},\n                translation_A));\n      } else {\n        maps.emplace_back(make_coordinate_map_base<Frame::BlockLogical,\n                                                   Frame::Inertial>(\n            Affine3D{\n                Affine(-1.0, 1.0, -1.0 * scaled_r_inner_A + offset_x_coord_a_,\n                       scaled_r_inner_A + offset_x_coord_a_),\n                Affine(-1.0, 1.0, -1.0 * scaled_r_inner_A, scaled_r_inner_A),\n                Affine(-1.0, 1.0, -1.0 * scaled_r_inner_A, scaled_r_inner_A)},\n            translation_A));\n      }\n    }\n    // Excision spheres\n    // - Block 0 through 5 enclose object A, and 12 through 17 enclose object B.\n    // - The 3D wedge map is oriented such that the lower-zeta logical direction\n    //   points radially inward.\n    else {\n      excision_spheres.emplace(\n          \"ExcisionSphereA\",\n          ExcisionSphere<3>{std::get<Object>(object_A_).inner_radius,\n                            tnsr::I<double, 3, Frame::Grid>{\n                                {x_coord_a_, center_of_mass_offset_[0],\n                                 center_of_mass_offset_[1]}},\n                            {{0, Direction<3>::lower_zeta()},\n                             {1, Direction<3>::lower_zeta()},\n                             {2, Direction<3>::lower_zeta()},\n                             {3, Direction<3>::lower_zeta()},\n                             {4, Direction<3>::lower_zeta()},\n                             {5, Direction<3>::lower_zeta()}}});\n    }\n  }\n  if (not use_single_block_b_) {\n    if (not is_excised_b_) {\n      const double scaled_r_inner_B =\n          std::get<Object>(object_B_).inner_radius / sqrt(3.0);\n      if (use_equiangular_map_) {\n        maps.emplace_back(\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                Equiangular3D{\n                    Equiangular(-1.0, 1.0,\n                                -1.0 * scaled_r_inner_B + offset_x_coord_b_,\n                                scaled_r_inner_B + offset_x_coord_b_),\n                    Equiangular(-1.0, 1.0, -1.0 * scaled_r_inner_B,\n                                scaled_r_inner_B),\n                    Equiangular(-1.0, 1.0, -1.0 * scaled_r_inner_B,\n                                scaled_r_inner_B)},\n                translation_B));\n      } else {\n        maps.emplace_back(make_coordinate_map_base<Frame::BlockLogical,\n                                                   Frame::Inertial>(\n            Affine3D{\n                Affine(-1.0, 1.0, -1.0 * scaled_r_inner_B + offset_x_coord_b_,\n                       scaled_r_inner_B + offset_x_coord_b_),\n                Affine(-1.0, 1.0, -1.0 * scaled_r_inner_B, scaled_r_inner_B),\n                Affine(-1.0, 1.0, -1.0 * scaled_r_inner_B, scaled_r_inner_B)},\n            translation_B));\n      }\n    }\n    // Excision spheres\n    // - Block 0 through 5 enclose object A, and 12 through 17 enclose object B.\n    // - The 3D wedge map is oriented such that the lower-zeta logical direction\n    //   points radially inward.\n    else {\n      excision_spheres.emplace(\n          \"ExcisionSphereB\",\n          ExcisionSphere<3>{std::get<Object>(object_B_).inner_radius,\n                            tnsr::I<double, 3, Frame::Grid>{\n                                {x_coord_b_, center_of_mass_offset_[0],\n                                 center_of_mass_offset_[1]}},\n                            {{12, Direction<3>::lower_zeta()},\n                             {13, Direction<3>::lower_zeta()},\n                             {14, Direction<3>::lower_zeta()},\n                             {15, Direction<3>::lower_zeta()},\n                             {16, Direction<3>::lower_zeta()},\n                             {17, Direction<3>::lower_zeta()}}});\n    }\n  }\n\n  // Have corners determined automatically\n  Domain<3> domain{std::move(maps), std::move(excision_spheres), block_names_,\n                   block_groups_};\n\n  // Inject the hard-coded time-dependence\n  if (time_dependent_options_.has_value()) {\n    // Default initialize everything to nullptr so that we only need to set the\n    // appropriate block maps for the specific frames\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n        grid_to_inertial_block_maps{number_of_blocks_};\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n        grid_to_distorted_block_maps{number_of_blocks_};\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n        distorted_to_inertial_block_maps{number_of_blocks_};\n\n    // Some maps (e.g. expansion, rotation) are applied to all blocks,\n    // while other maps (e.g. size) are only applied to some blocks. Also, some\n    // maps are applied from the Grid to the Inertial frame, while others are\n    // applied to/from the Distorted frame. So there are several different\n    // distinct combinations of time-dependent maps that will be applied. This\n    // should largely be taken care of by the TimeDependentOptions.\n\n    // Single blocks are added after the outer shell if an object's interior is\n    // not excised, so to find the final outer shell block, subtract the single\n    // blocks that may have been added.\n    size_t final_block_outer_shell = number_of_blocks_ - 1;\n    if ((not use_single_block_a_) and (not is_excised_a_)) {\n      --final_block_outer_shell;\n    }\n    if ((not use_single_block_b_) and (not is_excised_b_)) {\n      --final_block_outer_shell;\n    }\n\n    // All blocks except possibly the first 6 or 12 blocks of each object get\n    // the same map from the Grid to the Inertial frame, so initialize the final\n    // block with the \"base\" map (here a composition of an expansion and a\n    // rotation). When covering the inner regions with cubes, all blocks will\n    // use the same time-dependent map instead.\n    grid_to_inertial_block_maps[final_block_outer_shell] =\n        time_dependent_options_\n            ->grid_to_inertial_map<domain::ObjectLabel::None>(std::nullopt,\n                                                              false);\n    if (final_block_outer_shell < number_of_blocks_ - 1) {\n      grid_to_inertial_block_maps[number_of_blocks_ - 1] =\n          time_dependent_options_\n              ->grid_to_inertial_map<domain::ObjectLabel::None>(std::nullopt,\n                                                                true);\n    }\n    size_t final_block_envelope = first_outer_shell_block_ - 1;\n\n    grid_to_inertial_block_maps[final_block_envelope] =\n        time_dependent_options_\n            ->grid_to_inertial_map<domain::ObjectLabel::None>(std::nullopt,\n                                                              true);\n    // Inside the excision sphere we add a special grid to inertial map that can\n    // be evaluated at the center. This allows the center of the\n    // excisions/horizons to be mapped properly to the inertial frame.\n    if (is_excised_a_ and\n        grid_to_inertial_block_maps[number_of_blocks_ - 1] != nullptr) {\n      domain.inject_time_dependent_map_for_excision_sphere(\n          \"ExcisionSphereA\",\n          time_dependent_options_->grid_to_inertial_map<domain::ObjectLabel::A>(\n              {0}, true, true));\n    }\n    if (is_excised_b_ and\n        grid_to_inertial_block_maps[number_of_blocks_ - 1] != nullptr) {\n      domain.inject_time_dependent_map_for_excision_sphere(\n          \"ExcisionSphereB\",\n          time_dependent_options_->grid_to_inertial_map<domain::ObjectLabel::B>(\n              {0}, true, true));\n    }\n\n    const size_t first_block_object_B = use_single_block_a_ ? 1 : 12;\n\n    // We loop over all blocks. If we are using a single block for either A or\n    // B, then we only need a grid to inertial map; there is no distorted frame.\n    // If we don't have a single block around A or B, then for 12 blocks around\n    // each object we determine if there is a central cube. If there is, then\n    // there is no distorted frame (this is signaled by\n    // block_for_distorted_frame being a nullopt). If the region is excised,\n    // then it is up to the time dependent options to know if a distorted frame\n    // exists in that block. Therefore we just pass the relative block number to\n    // the time_dependent_options_ functions. The remaining blocks only get the\n    // grid to inertial map.\n    for (size_t block = 0; block < number_of_blocks_ - 1; ++block) {\n      if ((not use_single_block_a_) and block < first_block_object_B) {\n        const size_t block_for_distorted_frame = block;\n        grid_to_inertial_block_maps[block] =\n            time_dependent_options_\n                ->grid_to_inertial_map<domain::ObjectLabel::A>(\n                    block_for_distorted_frame, true);\n        grid_to_distorted_block_maps[block] =\n            time_dependent_options_\n                ->grid_to_distorted_map<domain::ObjectLabel::A>(\n                    block_for_distorted_frame);\n        distorted_to_inertial_block_maps[block] =\n            time_dependent_options_\n                ->distorted_to_inertial_map<domain::ObjectLabel::A>(\n                    block_for_distorted_frame, true);\n      } else if ((not use_single_block_b_) and block >= first_block_object_B and\n                 block < first_block_object_B + 12) {\n        const size_t block_for_distorted_frame = block - first_block_object_B;\n        grid_to_inertial_block_maps[block] =\n            time_dependent_options_\n                ->grid_to_inertial_map<domain::ObjectLabel::B>(\n                    block_for_distorted_frame, true);\n        grid_to_distorted_block_maps[block] =\n            time_dependent_options_\n                ->grid_to_distorted_map<domain::ObjectLabel::B>(\n                    block_for_distorted_frame);\n        distorted_to_inertial_block_maps[block] =\n            time_dependent_options_\n                ->distorted_to_inertial_map<domain::ObjectLabel::B>(\n                    block_for_distorted_frame, true);\n        // check if block is less than outer shell block for rigid\n        // expansion/translation, but no distorted map\n      } else if (block < first_outer_shell_block_ and\n                 grid_to_inertial_block_maps[number_of_blocks_ - 1] !=\n                     nullptr) {\n        grid_to_inertial_block_maps[block] =\n            grid_to_inertial_block_maps[final_block_envelope]->get_clone();\n      } else if (block > final_block_outer_shell and\n                 grid_to_inertial_block_maps[number_of_blocks_ - 1] !=\n                     nullptr) {\n        // the inner cube blocks are after outershell and we want to copy the\n        // corresponding object blocks.\n        if ((not use_single_block_a_) and (not is_excised_a_)) {\n          grid_to_inertial_block_maps[block] =\n              grid_to_inertial_block_maps[0]->get_clone();\n        }\n        if ((not use_single_block_b_) and (not is_excised_b_)) {\n          grid_to_inertial_block_maps[block] =\n              grid_to_inertial_block_maps[first_block_object_B]->get_clone();\n        }\n      } else if (grid_to_inertial_block_maps[number_of_blocks_ - 1] !=\n                 nullptr) {\n        // No distorted frame\n        grid_to_inertial_block_maps[block] =\n            grid_to_inertial_block_maps[final_block_outer_shell]->get_clone();\n      }\n    }\n    // Finally, inject the time dependent maps into the corresponding blocks\n    for (size_t block = 0; block < number_of_blocks_; ++block) {\n      if (grid_to_inertial_block_maps[block] == nullptr) {\n        continue;\n      }\n      domain.inject_time_dependent_map_for_block(\n          block, std::move(grid_to_inertial_block_maps[block]),\n          std::move(grid_to_distorted_block_maps[block]),\n          std::move(distorted_to_inertial_block_maps[block]));\n    }\n  }\n  return domain;\n}\n\ntemplate <bool UseWorldtube>\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nBinaryCompactObject<UseWorldtube>::external_boundary_conditions() const {\n  if (outer_boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{number_of_blocks_};\n  // Excision surfaces\n  for (size_t i = 0; i < 6; ++i) {\n    // Block 0 - 5 wrap excision surface A\n    if (is_excised_a_) {\n      boundary_conditions[i][Direction<3>::lower_zeta()] =\n          (*(std::get<Object>(object_A_).inner_boundary_condition))\n              ->get_clone();\n    }\n    // Blocks 12 - 17 or 1 - 6 wrap excision surface B\n    const size_t first_block_object_B = use_single_block_a_ ? 1 : 12;\n    if (is_excised_b_) {\n      boundary_conditions[i +\n                          first_block_object_B][Direction<3>::lower_zeta()] =\n          (*(std::get<Object>(object_B_).inner_boundary_condition))\n              ->get_clone();\n    }\n  }\n  // Outer boundary\n  const size_t offset_outer_blocks =\n      ((use_single_block_a_ and use_single_block_b_)\n           ? 12\n           : ((use_single_block_a_ or use_single_block_b_) ? 23 : 34)) +\n      10 * (number_of_outer_shells_ - 1);\n  for (size_t i = 0; i < 10; ++i) {\n    boundary_conditions[i + offset_outer_blocks][Direction<3>::upper_zeta()] =\n        outer_boundary_condition_->get_clone();\n  }\n  return boundary_conditions;\n}\n\ntemplate <bool UseWorldtube>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nBinaryCompactObject<UseWorldtube>::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  return time_dependent_options_.has_value()\n             ? time_dependent_options_->create_functions_of_time<UseWorldtube>(\n                   initial_expiration_times)\n             : std::unordered_map<\n                   std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{};\n}\n\ntemplate class BinaryCompactObject<true>;\ntemplate class BinaryCompactObject<false>;\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/BinaryCompactObject.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\nclass Equiangular;\ntemplate <size_t VolumeDim>\nclass Identity;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\ntemplate <size_t Dim>\nclass Wedge;\ntemplate <size_t VolumeDim>\nclass DiscreteRotation;\nclass Frustum;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\n}  // namespace domain\n\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators {\nnamespace bco {\n/*!\n * \\brief Create a set of centers of objects for the binary domains.\n *\n * \\details Will add the following centers to the set:\n *\n * - Center: The origin\n * - CenterA: Center of object A\n * - CenterB: Center of object B\n *\n * \\return Object required by the DomainCreator%s\n */\nstd::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\ncreate_grid_anchors(const std::array<double, 3>& center_a,\n                    const std::array<double, 3>& center_b);\n}  // namespace bco\n\n/*!\n * \\ingroup ComputationalDomainGroup\n *\n * \\brief A general domain for two compact objects.\n *\n * \\image html binary_compact_object_domain.png \"A BHNS domain.\"\n *\n * Creates a 3D Domain that represents a binary compact object solution. The\n * Domain consists of 4 or 5 nested layers of blocks; these layers are, working\n * from the interior toward the exterior:\n *\n * - **Object A/B interior**: (optional) The block at the center of each\n *   compact object, if not excised. If present, this block is a cube. If\n *   excised, the hole left by its absence is spherical.\n * - **Object A/B shell**: The 6 blocks that resolve each individual compact\n *   object. This layer has a spherical outer boundary - if the corresponding\n *   interior block exists, then the layer is a cube-to-sphere transition; if\n *   the interior block is excised, then the layer is a spherical shell.\n * - **Object A/B cube**: The 6 blocks that surround each object with a cube.\n *   Around each compact object, this layer transitions from a sphere to a cube.\n * - **Envelope**: The 10 blocks that transition from the two inner cubes to a\n *   sphere centered at the origin.\n * - **Outer shell**: The 10 blocks that form an outer shell centered at the\n *   origin, consisting of 2 endcap Wedges on the +x and -x axes, and 8 half\n *   Wedges along the yz plane. This layer is spherical, so a logarithmic map\n *   can optionally be used in this layer. This allows the domain to extend to\n *   large radial distances from the compact objects. This layer can be\n *   h-refined radially, creating a layer of multiple concentric spherical\n *   shells.\n *\n * \\par Notes:\n * - Object A is located to the right of the origin (along the positive x-axis)\n *   and Object B is located to the left of the origin in the Grid frame.\n * - This domain offers some grid anchors. See\n *   `domain::creators::bco::create_grid_anchors` for which ones are offered.\n * - \"Cutting plane\" refers to the plane along which the domain divides into two\n *   hemispheres. The cutting plane always intersects the x-axis at the origin.\n * - The x-coordinate locations of the two objects should be chosen such that\n *   the center of mass is located at x=0 at the initial time (typically t=0).\n * - The cubes are first constructed at the origin. Then, they are translated\n *   left/right by their Object's x-coordinate and offset depending on the cube\n *   length.\n * - The CubeScale option describes how to scale the length of the cube\n *   surrounding object A/B. It must be greater than or equal to 1.0 with 1.0\n *   meaning the side length of the cube is the initial physical separation\n *   between the two objects. If CubeScale is greater than 1.0, the centers of\n *   the two objects will be offset relative to the centers of the cubes.\n * - Alternatively, one can replace the inner shell and cube blocks of each\n *   object with a single cartesian cube. This is less efficient, but allows\n *   testing of methods only coded on cartesian grids.\n *\n * \\par Time dependence:\n * The following time-dependent maps are applied:\n *\n * - A piecewise `Expansion`, a `Rotation` and a piecewise `Translation` is\n * applied to all blocks from the Grid to the Inertial frame. However, if there\n * is a shape map in the block (defined below), then the expansion, rotation,\n * and translation maps go from the Distorted to the Inertial frame.\n * - If an object is excised, then the corresponding shell has a\n *   `Shape` map. The shape map goes from the Grid to the Distorted frame.\n *\n * All time dependent maps are optional to specify. To include a map, specify\n * its options. Otherwise specify `None` for that map. You can also turn off\n * time dependent maps all together by specifying `None` for the\n * `TimeDependentMaps` option. See\n * `domain::creators::bco::TimeDependentMapOptions`. This class must pass a\n * template parameter of `false` to\n * `domain::creators::bco::TimeDependentMapOptions`.\n *\n * The `UseWorldtube` template parameter is set to false by default. When set to\n * true, some of the functions of time will be `IntegratedFunctionOfTime` used\n * to control the orbit of the worldtube.\n */\ntemplate <bool UseWorldtube = false>\nclass BinaryCompactObject : public DomainCreator<3> {\n private:\n  // Time-independent maps\n  using Affine = CoordinateMaps::Affine;\n  using Affine3D = CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  using Identity2D = CoordinateMaps::Identity<2>;\n  // The Translation type is no longer needed, but it is kept here for backwards\n  // compatibility with old domains.\n  using Translation = CoordinateMaps::ProductOf2Maps<Affine, Identity2D>;\n  using Equiangular = CoordinateMaps::Equiangular;\n  using Equiangular3D =\n      CoordinateMaps::ProductOf3Maps<Equiangular, Equiangular, Equiangular>;\n\n public:\n  using maps_list = tmpl::flatten<tmpl::list<\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine3D>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            Equiangular3D>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine3D,\n                            Translation>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::DiscreteRotation<3>, Affine3D>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            Equiangular3D>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Equiangular3D,\n                            Translation>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::Frustum>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::Wedge<3>>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::Wedge<3>, Translation>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine3D,\n                            Affine3D>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Equiangular3D,\n                            Affine3D>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::Wedge<3>, Affine3D>,\n      bco::TimeDependentMapOptions<false>::maps_list>>;\n\n  /// Options for an excision region in the domain\n  struct Excision {\n    static constexpr Options::String help = {\n        \"Excise the interior of the object, leaving a spherical hole in its \"\n        \"absence.\"};\n    template <typename BoundaryConditionsBase>\n    struct BoundaryCondition {\n      static std::string name() { return \"ExciseWithBoundaryCondition\"; }\n      using type = std::unique_ptr<BoundaryConditionsBase>;\n      static constexpr Options::String help = {\n          \"The boundary condition to impose on the excision surface.\"};\n    };\n    template <typename Metavariables>\n    using options = tmpl::list<BoundaryCondition<\n        domain::BoundaryConditions::get_boundary_conditions_base<\n            typename Metavariables::system>>>;\n    Excision() = default;\n    // NOLINTNEXTLINE(google-explicit-constructor)\n    Excision(std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n                 boundary_condition_in)\n        : boundary_condition(std::move(boundary_condition_in)) {}\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        boundary_condition;\n  };\n\n  /// Options for one of the two objects in the binary domain\n  struct Object {\n    static constexpr Options::String help = {\n        \"Options for an object in a binary domain.\"};\n    struct InnerRadius {\n      using type = double;\n      static constexpr Options::String help = {\n          \"Inner coordinate radius of Layer 1.\"};\n      static double lower_bound() { return 0.; }\n    };\n    struct OuterRadius {\n      using type = double;\n      static constexpr Options::String help = {\n          \"Outer coordinate radius of Layer 1\"};\n      static double lower_bound() { return 0.; }\n    };\n    struct XCoord {\n      using type = double;\n      static constexpr Options::String help = {\"x-coordinate of center.\"};\n    };\n    struct Interior {\n      using type = Options::Auto<Excision>;\n      static constexpr Options::String help = {\n          \"Specify 'ExciseWithBoundaryCondition' and a boundary condition to \"\n          \"excise Layer 0, leaving a spherical hole in its absence, or set to \"\n          \"'Auto' to fill the interior.\"};\n    };\n    struct ExciseInterior {\n      using type = bool;\n      static constexpr Options::String help = {\n          \"Excise Layer 0, leaving a spherical hole in its absence.\"};\n    };\n    struct UseLogarithmicMap {\n      using type = bool;\n      static constexpr Options::String help = {\n          \"Use a logarithmically spaced radial grid in the part of Layer 1 \"\n          \"enveloping the object (requires the interior is excised)\"};\n    };\n    template <typename Metavariables>\n    using options = tmpl::list<\n        InnerRadius, OuterRadius, XCoord,\n        tmpl::conditional_t<\n            domain::BoundaryConditions::has_boundary_conditions_base_v<\n                typename Metavariables::system>,\n            Interior, ExciseInterior>,\n        UseLogarithmicMap>;\n    Object() = default;\n    Object(double local_inner_radius, double local_outer_radius,\n           double local_x_coord, std::optional<Excision> interior,\n           bool local_use_logarithmic_map)\n        : inner_radius(local_inner_radius),\n          outer_radius(local_outer_radius),\n          x_coord(local_x_coord),\n          inner_boundary_condition(\n              interior.has_value()\n                  ? std::make_optional(std::move(interior->boundary_condition))\n                  : std::nullopt),\n          use_logarithmic_map(local_use_logarithmic_map) {}\n    Object(double local_inner_radius, double local_outer_radius,\n           double local_x_coord, bool local_excise_interior,\n           bool local_use_logarithmic_map)\n        : inner_radius(local_inner_radius),\n          outer_radius(local_outer_radius),\n          x_coord(local_x_coord),\n          inner_boundary_condition(\n              local_excise_interior\n                  ? std::optional<std::unique_ptr<\n                        domain::BoundaryConditions::BoundaryCondition>>{nullptr}\n                  : std::nullopt),\n          use_logarithmic_map(local_use_logarithmic_map) {}\n\n    /// Whether or not the object should be excised from the domain, leaving a\n    /// spherical hole. When this is true, `inner_boundary_condition` is\n    /// guaranteed to hold a value (though it might be a `nullptr` if we are not\n    /// working with boundary conditions).\n    bool is_excised() const;\n\n    double inner_radius{};\n    double outer_radius{};\n    double x_coord{};\n    std::optional<\n        std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>\n        inner_boundary_condition;\n    bool use_logarithmic_map{};\n  };\n\n  // Simpler version of an object: a single cube centered on (xCoord,0,0)\n  struct CartesianCubeAtXCoord {\n    static constexpr Options::String help = {\n        \"Options to set a single cube at a location on the x-axis\"};\n    struct XCoord {\n      static std::string name() { return \"CartesianCubeAtXCoord\"; }\n      using type = double;\n      static constexpr Options::String help = {\"x-coordinate of center.\"};\n    };\n    using options = tmpl::list<XCoord>;\n    CartesianCubeAtXCoord() = default;\n    // NOLINTNEXTLINE(google-explicit-constructor)\n    CartesianCubeAtXCoord(const double x_coord_in) : x_coord(x_coord_in) {}\n    bool is_excised() const { return false; }\n    double x_coord;\n  };\n\n  struct ObjectA {\n    using type = std::variant<Object, CartesianCubeAtXCoord>;\n    static constexpr Options::String help = {\n        \"Options for the object to the right of the origin (along the positive \"\n        \"x-axis).\"};\n  };\n\n  struct ObjectB {\n    using type = std::variant<Object, CartesianCubeAtXCoord>;\n    static constexpr Options::String help = {\n        \"Options for the object to the left of the origin (along the negative \"\n        \"x-axis).\"};\n  };\n\n  struct CenterOfMassOffset {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\n        \"Offset in the y and z axes applied to both object A and B in order to \"\n        \"control the center of mass. This moves the location of the two objects\"\n        \" in the grid frame but keeps the Envelope and OuterShell centered on \"\n        \"the origin in the grid frame.\"};\n  };\n\n  struct Envelope {\n    static constexpr Options::String help = {\n        \"Options for the sphere enveloping the two objects.\"};\n  };\n\n  struct EnvelopeRadius {\n    using group = Envelope;\n    static std::string name() { return \"Radius\"; }\n    using type = double;\n    static constexpr Options::String help = {\n        \"Radius of the sphere enveloping the two objects.\"};\n  };\n\n  struct OuterShell {\n    static constexpr Options::String help = {\n        \"Options for the outer spherical shell.\"};\n  };\n\n  struct OuterRadius {\n    using group = OuterShell;\n    static std::string name() { return \"Radius\"; }\n    using type = double;\n    static constexpr Options::String help = {\"Radius of the entire domain.\"};\n  };\n\n  struct RadialPartitioningOuterShell {\n    static std::string name() { return \"RadialPartitioning\"; }\n    using group = OuterShell;\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"Radial coordinates of the boundaries splitting the outer spherical \"\n        \"shells between the envelope radius and OuterRadius. They must be \"\n        \"given in ascending order. This should be used if boundaries need to \"\n        \"be set at specific radii. If the number but not the specific \"\n        \"locations of the boundaries are important, use InitialRefinement \"\n        \"instead.\"};\n  };\n\n  struct RadialDistributionOuterShell {\n    static std::string name() { return \"RadialDistribution\"; }\n    using group = OuterShell;\n    using type =\n        std::variant<domain::CoordinateMaps::Distribution,\n                     std::vector<domain::CoordinateMaps::Distribution>>;\n    static constexpr Options::String help = {\n        \"Select the radial distribution of grid points in each outer spherical \"\n        \"shell. There must be N+1 radial distributions specified for N radial \"\n        \"partitions. You can also specify just a single radial distribution \"\n        \"(not in a vector) which will use the same distribution for all \"\n        \"partitions.\"};\n  };\n\n  struct OpeningAngle {\n    using group = OuterShell;\n    static std::string name() { return \"OpeningAngle\"; }\n    using type = double;\n    static constexpr Options::String help = {\n        \"The combined opening angle of the two half wedges of the outer shell\"\n        \" in degrees. A value of 120.0 partitions the x-y and x-z slices of the\"\n        \" outer shell into six Blocks of equal angular size.\"};\n  };\n\n  struct CubeScale {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Specify the desired cube scale that must be greater than or equal to \"\n        \"1.0. The initial separation is multiplied by this cube scale to \"\n        \"produce larger cubes around each object which is desirable when \"\n        \"closer to merger.\"};\n    static double lower_bound() { return 1.0; }\n  };\n\n  struct InitialRefinement {\n    using type =\n        std::variant<size_t, std::array<size_t, 3>,\n                     std::vector<std::array<size_t, 3>>,\n                     std::unordered_map<std::string, std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial refinement level in each block of the domain. See main help \"\n        \"text for details.\"};\n  };\n\n  struct InitialGridPoints {\n    using type =\n        std::variant<size_t, std::array<size_t, 3>,\n                     std::vector<std::array<size_t, 3>>,\n                     std::unordered_map<std::string, std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in the elements of each block of the \"\n        \"domain. See main help text for details.\"};\n  };\n\n  struct UseEquiangularMap {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Distribute grid points equiangularly.\"};\n    static bool suggested_value() { return true; }\n  };\n\n  struct RadialDistributionEnvelope {\n    using group = Envelope;\n    static std::string name() { return \"RadialDistribution\"; }\n    using type = CoordinateMaps::Distribution;\n    static constexpr Options::String help = {\n        \"The distribution of radial grid points in the envelope, the layer \"\n        \"made of ten bulged Frustums.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct OuterBoundaryCondition {\n    using group = OuterShell;\n    static std::string name() { return \"BoundaryCondition\"; }\n    static constexpr Options::String help =\n        \"Options for the outer boundary conditions.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  // This is for optional time dependent maps\n  struct TimeDependentMaps {\n    using type = Options::Auto<bco::TimeDependentMapOptions<false>,\n                               Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        bco::TimeDependentMapOptions<false>::help;\n  };\n\n  template <typename Metavariables>\n  using options = tmpl::append<\n      tmpl::list<ObjectA, ObjectB, CenterOfMassOffset, EnvelopeRadius,\n                 OuterRadius, CubeScale, InitialRefinement, InitialGridPoints,\n                 UseEquiangularMap, RadialDistributionEnvelope,\n                 RadialPartitioningOuterShell, RadialDistributionOuterShell,\n                 OpeningAngle, TimeDependentMaps>,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          tmpl::list<OuterBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n          tmpl::list<>>>;\n\n  static constexpr Options::String help{\n      \"A general domain for two compact objects. Each object is represented by \"\n      \"a cube along the x-axis. Object A is located on the right and Object B \"\n      \"is located on the left. Their locations should be chosen such that \"\n      \"their center of mass is located at the origin.\"\n      \"The interior of each object can have a spherical excision to \"\n      \"represent a black hole.\"\n      \"\\n\"\n      \"The two objects are enveloped by a sphere centered at the origin, \"\n      \"and by an outer shell that can transition to large outer radii.\"\n      \"\\n\"\n      \"Both the InitialRefinement and the InitialGridPoints can be one of \"\n      \"the following:\\n\"\n      \"  - A single number: Uniform refinement in all blocks and \"\n      \"dimensions\\n\"\n      \"  - Three numbers: Refinement in [polar, azimuthal, radial] direction \"\n      \"in all blocks\\n\"\n      \"  - A map from block names or groups to three numbers: Per-block \"\n      \"refinement in [polar, azimuthal, radial] direction\\n\"\n      \"  - A list, with [polar, azimuthal, radial] refinement for each block\\n\"\n      \"\\n\"\n      \"The domain can rotate around the \"\n      \"z-axis and expand/compress radially. The two objects can each have a \"\n      \"spherical distortion (shape map).\"};\n\n  BinaryCompactObject(\n      typename ObjectA::type object_A, typename ObjectB::type object_B,\n      std::array<double, 2> center_of_mass_offset, double envelope_radius,\n      double outer_radius, double cube_scale,\n      const typename InitialRefinement::type& initial_refinement,\n      const typename InitialGridPoints::type& initial_number_of_grid_points,\n      bool use_equiangular_map = true,\n      CoordinateMaps::Distribution radial_distribution_envelope =\n          CoordinateMaps::Distribution::Projective,\n      const std::vector<double>& radial_partitioning_outer_shell = {},\n      const typename RadialDistributionOuterShell::type&\n          radial_distribution_outer_shell =\n              CoordinateMaps::Distribution::Linear,\n      double opening_angle_in_degrees = 90.0,\n      std::optional<bco::TimeDependentMapOptions<false>>\n          time_dependent_options = std::nullopt,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          outer_boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  BinaryCompactObject() = default;\n  BinaryCompactObject(const BinaryCompactObject&) = delete;\n  BinaryCompactObject(BinaryCompactObject&&) = default;\n  BinaryCompactObject& operator=(const BinaryCompactObject&) = delete;\n  BinaryCompactObject& operator=(BinaryCompactObject&&) = default;\n  ~BinaryCompactObject() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n  grid_anchors() const override {\n    return grid_anchors_;\n  }\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override {\n    return initial_number_of_grid_points_;\n  }\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels()\n      const override {\n    return initial_refinement_;\n  }\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return block_groups_;\n  }\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  typename ObjectA::type object_A_{};\n  typename ObjectB::type object_B_{};\n  std::array<double, 2> center_of_mass_offset_{};\n  double envelope_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double outer_radius_ = std::numeric_limits<double>::signaling_NaN();\n  std::vector<std::array<size_t, 3>> initial_refinement_;\n  std::vector<std::array<size_t, 3>> initial_number_of_grid_points_;\n  bool use_equiangular_map_ = true;\n  CoordinateMaps::Distribution radial_distribution_envelope_ =\n      CoordinateMaps::Distribution::Projective;\n  std::vector<double> radial_partitioning_outer_shell_;\n  std::vector<CoordinateMaps::Distribution> radial_distribution_outer_shell_ = {\n      CoordinateMaps::Distribution::Linear};\n  double translation_{};\n  double length_inner_cube_{};\n  double length_outer_cube_{};\n  size_t number_of_outer_shells_{};\n  size_t number_of_blocks_{};\n  size_t first_outer_shell_block_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      outer_boundary_condition_;\n  std::vector<std::string> block_names_;\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_;\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n      grid_anchors_;\n  double offset_x_coord_a_{};\n  double offset_x_coord_b_{};\n\n  // Variables to handle std::variant on Object A and B\n  double x_coord_a_{};\n  double x_coord_b_{};\n  bool is_excised_a_ = false;\n  bool is_excised_b_ = false;\n  bool use_single_block_a_ = false;\n  bool use_single_block_b_ = false;\n  std::optional<bco::TimeDependentMapOptions<false>> time_dependent_options_;\n  double opening_angle_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DomainCreators)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AlignedLattice.cpp\n  AngularCylinder.cpp\n  AngularDisk.cpp\n  BinaryCompactObject.cpp\n  CartoonCylinder.cpp\n  CartoonSphere1D.cpp\n  CartoonSphere2D.cpp\n  Cylinder.cpp\n  CylindricalBinaryCompactObject.cpp\n  Disk.cpp\n  ExpandOverBlocks.cpp\n  FrustalCloak.cpp\n  NonconformingSphericalShells.cpp\n  Rectilinear.cpp\n  RegisterDerivedWithCharm.cpp\n  RotatedBricks.cpp\n  RotatedIntervals.cpp\n  RotatedRectangles.cpp\n  ShellDistribution.cpp\n  Sphere.cpp\n  SphericalShells.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AlignedLattice.hpp\n  AngularCylinder.hpp\n  AngularDisk.hpp\n  BinaryCompactObject.hpp\n  CartoonCylinder.hpp\n  CartoonSphere1D.hpp\n  CartoonSphere2D.hpp\n  Cylinder.hpp\n  CylindricalBinaryCompactObject.hpp\n  Disk.hpp\n  DomainCreator.hpp\n  ExpandOverBlocks.hpp\n  ExpandOverBlocks.tpp\n  Factory.hpp\n  Factory1D.hpp\n  Factory2D.hpp\n  Factory3D.hpp\n  FrustalCloak.hpp\n  NonconformingSphericalShells.hpp\n  OptionTags.hpp\n  Rectilinear.hpp\n  RegisterDerivedWithCharm.hpp\n  RotatedBricks.hpp\n  RotatedIntervals.hpp\n  RotatedRectangles.hpp\n  ShellDistribution.hpp\n  Sphere.hpp\n  SphericalShells.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DomainStructure\n  H5\n  PUBLIC\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainBoundaryConditions\n  DomainTimeDependence\n  GeneralRelativity\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  INTERFACE\n  ErrorHandling\n  Options\n  )\n\nadd_subdirectory(Python)\nadd_subdirectory(Tags)\nadd_subdirectory(TimeDependence)\nadd_subdirectory(TimeDependentOptions)\n"
  },
  {
    "path": "src/Domain/Creators/CartoonCylinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/CartoonCylinder.hpp\"\n\n#include <array>\n#include <memory>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ShellDistribution.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace Frame {\nstruct BlockLogical;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain::creators {\n\nCartoonCylinder::CartoonCylinder(\n    const std::array<double, 2> lower_bounds,\n    const std::array<double, 2> upper_bounds,\n    const std::array<size_t, 2> initial_refinement_levels,\n    const std::array<size_t, 2> initial_num_points,\n    const std::array<CoordinateMaps::Distribution, 2> distributions,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dependence,\n    std::array<\n        std::array<\n            std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>, 2>,\n        2>\n        boundary_conditions,\n    const Options::Context& context)\n    : lower_bounds_(lower_bounds),\n      upper_bounds_(upper_bounds),\n      initial_refinement_levels_(initial_refinement_levels),\n      initial_num_points_(initial_num_points),\n      distributions_(distributions),\n      boundary_conditions_(std::move(boundary_conditions)),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<3>>();\n  }\n  if (gsl::at(lower_bounds_, 0) < 0) {\n    PARSE_ERROR(context,\n                \"The lower bound for the x dimension must be >= 0, but got \"\n                    << gsl::at(lower_bounds_, 0) << \".\");\n  }\n  for (size_t d = 0; d < 2; ++d) {\n    if (gsl::at(lower_bounds_, d) >= gsl::at(upper_bounds_, d)) {\n      PARSE_ERROR(context,\n                  \"Lower bound (\"\n                      << gsl::at(lower_bounds_, d)\n                      << \") must be strictly smaller than upper bound (\"\n                      << gsl::at(upper_bounds_, d) << \") in dimension \" << d\n                      << \".\");\n    }\n  }\n  for (size_t d = 0; d < 2; ++d) {\n    const auto& [lower_bc, upper_bc] = gsl::at(boundary_conditions_, d);\n    if (lower_bc == nullptr and upper_bc == nullptr) {\n      PARSE_ERROR(context, \"None of the boundary conditions can be nullptr\");\n    }\n    using domain::BoundaryConditions::is_none;\n    if (is_none(lower_bc) or is_none(upper_bc)) {\n      PARSE_ERROR(\n          context,\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\");\n    }\n    using domain::BoundaryConditions::is_periodic;\n    if (d == 0 and (is_periodic(lower_bc) or is_periodic(upper_bc))) {\n      PARSE_ERROR(\n          context,\n          \"Cannot have periodic boundary conditions in the x dimension.\");\n    }\n    if (d == 1) {\n      if (is_periodic(lower_bc) != is_periodic(upper_bc)) {\n        PARSE_ERROR(context,\n                    \"Periodic boundary conditions must be applied for both \"\n                    \"upper and lower directions in the y dimension or none.\");\n      }\n      is_periodic_in_y_ = is_periodic(lower_bc);\n    }\n  }\n}\n\nDomain<3> CartoonCylinder::create_domain() const {\n  using Interval = CoordinateMaps::Interval;\n  using Identity1D = CoordinateMaps::Identity<1>;\n  using cartoon_cylinder_map =\n      CoordinateMaps::ProductOf3Maps<Interval, Interval, Identity1D>;\n  const Identity1D identity_map;\n\n  std::vector<Block<3>> blocks;\n  blocks.reserve(num_blocks_);\n\n  auto block_map = cartoon_cylinder_map{\n      {-1.0, 1.0, lower_bounds_[0], upper_bounds_[0], distributions_[0], 0.0},\n      {-1.0, 1.0, lower_bounds_[1], upper_bounds_[1], distributions_[1], 0.0},\n      identity_map};\n  auto stationary_map =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(block_map);\n\n  if (is_periodic_in_y_) {\n    std::vector<DirectionMap<3, BlockNeighbors<3>>> neighbors;\n    std::vector<PairOfFaces> identifications{};\n    identifications.push_back({{0, 1, 4, 5}, {2, 3, 6, 7}});\n\n    std::vector<std::array<size_t, two_to_the(3_st)>> block_corners{1};\n    std::iota(block_corners[0].begin(), block_corners[0].end(), 0_st);\n\n    set_internal_boundaries<3>(&neighbors, block_corners);\n    set_identified_boundaries<3>(identifications, block_corners, &neighbors);\n    blocks.emplace_back(std::move(stationary_map), 0, std::move(neighbors[0]),\n                        block_names_.at(0),\n                        domain::topologies::cartoon_cylinder);\n  } else {\n    const DirectionMap<3, BlockNeighbors<3>> neighbors;\n    blocks.emplace_back(std::move(stationary_map), 0, neighbors,\n                        block_names_.at(0),\n                        domain::topologies::cartoon_cylinder);\n  }\n\n  Domain<3> domain(std::move(blocks), {}, block_groups());\n\n  if (not time_dependence_->is_none()) {\n    domain.inject_time_dependent_map_for_block(\n        0, std::move(time_dependence_->block_maps_grid_to_inertial(1)[0]),\n        std::move(time_dependence_->block_maps_grid_to_distorted(1)[0]),\n        std::move(time_dependence_->block_maps_distorted_to_inertial(1)[0]));\n  }\n  return domain;\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nCartoonCylinder::external_boundary_conditions() const {\n  if (boundary_conditions_[0][0] == nullptr) {\n#ifdef SPECTRE_DEBUG\n    for (size_t d = 0; d < 2; ++d) {\n      ASSERT(gsl::at(boundary_conditions_, d)[0] == nullptr and\n                 gsl::at(boundary_conditions_, d)[1] == nullptr,\n             \"Boundary conditions must be set for all directions or none.\");\n    }\n#endif  // SPECTRE_DEBUG\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{1};\n  for (size_t d = 0; d < 2; ++d) {\n    if (not is_periodic_in_y_ or d != 1) {\n      const auto& [lower_bc, upper_bc] = gsl::at(boundary_conditions_, d);\n      boundary_conditions[0][Direction<3>{d, Side::Lower}] =\n          lower_bc->get_clone();\n      boundary_conditions[0][Direction<3>{d, Side::Upper}] =\n          upper_bc->get_clone();\n    }\n  }\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 3>> CartoonCylinder::initial_extents() const {\n  // cartoon bases always have extents set to 1\n  return {{initial_num_points_[0], initial_num_points_[1], 1}};\n}\n\nstd::vector<std::array<size_t, 3>> CartoonCylinder::initial_refinement_levels()\n    const {\n  // cartoon bases always have refinement set to 0\n  return {{initial_refinement_levels_[0], initial_refinement_levels_[1], 0_st}};\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nCartoonCylinder::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if (time_dependence_->is_none()) {\n    return {};\n  } else {\n    return time_dependence_->functions_of_time(initial_expiration_times);\n  }\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/CartoonCylinder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Interval;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators {\n/// Create a 3D Domain with its computational domain being the\\f$x-y\\f$\n/// plane. The third dimension uses a Cartoon basis with Killing vector along\n/// the \\f$\\phi\\f$ direction.\nclass CartoonCylinder : public DomainCreator<3> {\n public:\n  using maps_list = tmpl::list<domain::CoordinateMap<\n      Frame::BlockLogical, Frame::Inertial,\n      CoordinateMaps::ProductOf3Maps<CoordinateMaps::Interval,\n                                     CoordinateMaps::Interval,\n                                     CoordinateMaps::Identity<1>>>>;\n\n  static std::string name() { return \"CartoonCylinder\"; }\n\n  struct LowerBounds {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\n        \"Lower bound in the [x,y] dimensions.\"};\n  };\n\n  struct UpperBounds {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\n        \"Upper bound in the [x,y] dimensions.\"};\n  };\n\n  struct InitialRefinement {\n    using type = std::array<size_t, 2>;\n    static constexpr Options::String help = {\n        \"Initial refinement level for the [x,y] dimensions.\"};\n  };\n\n  struct InitialGridPoints {\n    using type = std::array<size_t, 2>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in the [x,y] dimensions.\"};\n  };\n\n  struct Distributions {\n    using type = std::array<CoordinateMaps::Distribution, 2>;\n    static constexpr Options::String help = {\n        \"Distribution of grid points in the [x,y] dimensions.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct LowerUpperBoundaryCondition {\n    static constexpr Options::String help =\n        \"Lower and upper Boundary Conditions\";\n    struct LowerBC {\n      using type = std::unique_ptr<BoundaryConditionsBase>;\n      static constexpr Options::String help = \"Lower Boundary Condition\";\n      static std::string name() { return \"Lower\"; };\n    };\n    struct UpperBC {\n      using type = std::unique_ptr<BoundaryConditionsBase>;\n      static constexpr Options::String help = \"Upper Boundary Condition\";\n      static std::string name() { return \"Upper\"; };\n    };\n    LowerUpperBoundaryCondition(typename LowerBC::type lower_bc,\n                                typename UpperBC::type upper_bc)\n        : lower(std::move(lower_bc)), upper(std::move(upper_bc)){};\n    LowerUpperBoundaryCondition() = default;\n    std::unique_ptr<BoundaryConditionsBase> lower;\n    std::unique_ptr<BoundaryConditionsBase> upper;\n    using options = tmpl::list<LowerBC, UpperBC>;\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct BoundaryConditions {\n    static constexpr Options::String help = {\n        \"The boundary conditions to be imposed in the x & y dimensions. \"\n        \"Either specify one B.C. to be imposed for both \"\n        \"lower and upper boundary or a pair 'Lower:' and 'Upper:'.\"};\n    using type = std::array<\n        std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                     LowerUpperBoundaryCondition<BoundaryConditionsBase>>,\n        2>;\n  };\n\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain. Specify `None` for no \"\n        \"time dependant maps.\"};\n  };\n\n  using basic_options =\n      tmpl::list<LowerBounds, UpperBounds, InitialRefinement, InitialGridPoints,\n                 Distributions, TimeDependence>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          BoundaryConditions<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"A cylinder domain that requires/enforces axial symmetry. The \"\n      \"computational domain is the x-y plane, with Cartoon partial derivatives \"\n      \"being used for the z direction.\"};\n\n  CartoonCylinder(\n      std::array<double, 2> lower_bounds, std::array<double, 2> upper_bounds,\n      std::array<size_t, 2> initial_refinement_levels,\n      std::array<size_t, 2> initial_num_points,\n      std::array<CoordinateMaps::Distribution, 2> distributions = {},\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n          time_dependence = nullptr,\n      std::array<std::array<std::unique_ptr<\n                                domain::BoundaryConditions::BoundaryCondition>,\n                            2>,\n                 2>\n          boundary_conditions = {},\n      const Options::Context& context = {});\n\n  template <typename BoundaryConditionsBase>\n  CartoonCylinder(\n      std::array<double, 2> lower_bounds, std::array<double, 2> upper_bounds,\n      std::array<size_t, 2> initial_refinement_levels,\n      std::array<size_t, 2> initial_num_points,\n      std::array<CoordinateMaps::Distribution, 2> distributions = {},\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n          time_dependence = nullptr,\n      std::array<\n          std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                       LowerUpperBoundaryCondition<BoundaryConditionsBase>>,\n          2>\n          boundary_conditions = {},\n      const Options::Context& context = {})\n      : CartoonCylinder(\n            lower_bounds, upper_bounds, initial_refinement_levels,\n            initial_num_points,\n            distributions, std::move(time_dependence),\n            transform_boundary_conditions(std::move(boundary_conditions)),\n            context) {}\n\n  CartoonCylinder() = default;\n  CartoonCylinder(const CartoonCylinder&) = delete;\n  CartoonCylinder(CartoonCylinder&&) = default;\n  CartoonCylinder& operator=(const CartoonCylinder&) = delete;\n  CartoonCylinder& operator=(CartoonCylinder&&) = default;\n  ~CartoonCylinder() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return {{name(), {name()}}};\n  }\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n  // Transforms from option-created boundary conditions to the type used in the\n  // constructor\n  template <typename BoundaryConditionsBase>\n  static auto transform_boundary_conditions(\n      std::array<\n          std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                       LowerUpperBoundaryCondition<BoundaryConditionsBase>>,\n          2>\n          boundary_conditions)\n      -> std::array<\n          std::array<\n              std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>,\n              2>,\n          2>;\n\n private:\n  std::array<double, 2> lower_bounds_{};\n  std::array<double, 2> upper_bounds_{};\n  std::array<size_t, 2> initial_refinement_levels_{};\n  std::array<size_t, 2> initial_num_points_{};\n  bool is_periodic_in_y_{};\n  std::array<CoordinateMaps::Distribution, 2> distributions_{};\n  std::array<\n      std::array<std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>,\n                 2>,\n      2>\n      boundary_conditions_{};\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n      time_dependence_;\n  size_t num_blocks_{};\n  inline static const std::vector<std::string> block_names_{name()};\n};\n\ntemplate <typename BoundaryConditionsBase>\nauto CartoonCylinder::transform_boundary_conditions(\n    std::array<\n        std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                     LowerUpperBoundaryCondition<BoundaryConditionsBase>>,\n        2>\n        boundary_conditions)\n    -> std::array<\n        std::array<\n            std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>, 2>,\n        2> {\n  std::array<\n      std::array<std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>,\n                 2>,\n      2>\n      result{};\n  for (size_t d = 0; d < 2; ++d) {\n    if (std::holds_alternative<std::unique_ptr<BoundaryConditionsBase>>(\n            boundary_conditions[d])) {\n      auto bc = std::move(std::get<std::unique_ptr<BoundaryConditionsBase>>(\n          boundary_conditions[d]));\n      gsl::at(gsl::at(result, d), 0) = bc->get_clone();\n      gsl::at(gsl::at(result, d), 1) = std::move(bc);\n    } else {\n      auto& bc = std::get<LowerUpperBoundaryCondition<BoundaryConditionsBase>>(\n          boundary_conditions[d]);\n      gsl::at(gsl::at(result, d), 0) = std::move(bc.lower);\n      gsl::at(gsl::at(result, d), 1) = std::move(bc.upper);\n    }\n  }\n  return result;\n}\n\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/CartoonSphere1D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/CartoonSphere1D.hpp\"\n\n#include <array>\n#include <memory>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ShellDistribution.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace Frame {\nstruct BlockLogical;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain::creators {\n\nCartoonSphere1D::CartoonSphere1D(\n    double inner_bound, double outer_bound,\n    typename InitialRadialRefinement::type&& initial_refinement_levels,\n    typename InitialNumberOfRadialGridPoints::type&& initial_num_points,\n    std::vector<double> radial_partitioning,\n    const typename RadialDistributions::type& radial_distributions,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dependence,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        inner_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        outer_boundary_condition,\n    const Options::Context& context)\n    : inner_bound_(inner_bound),\n      outer_bound_(outer_bound),\n      radial_partitioning_(std::move(radial_partitioning)),\n      inner_boundary_condition_(std::move(inner_boundary_condition)),\n      outer_boundary_condition_(std::move(outer_boundary_condition)),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<3>>();\n  }\n  if (inner_bound_ >= outer_bound_) {\n    PARSE_ERROR(context,\n                \"Inner radius must be smaller than outer radius, but inner \"\n                \"radius is \" +\n                    std::to_string(inner_bound_) + \" and outer radius is \" +\n                    std::to_string(outer_bound_) + \".\");\n  }\n  set_shell_distribution(make_not_null(&num_blocks_),\n                         make_not_null(&radial_distributions_),\n                         radial_partitioning_, radial_distributions,\n                         inner_bound_, outer_bound_, \"inner\", \"outer\", context);\n  const auto check_possible_list_instantiation =\n      [this, &context](std::variant<size_t, std::vector<size_t>> input_param,\n                       const std::string& name) {\n        if (std::holds_alternative<size_t>(input_param)) {\n          const auto input_value = std::get<size_t>(input_param);\n          return std::vector<size_t>(num_blocks_, input_value);\n        } else {\n          const auto& input_vec = std::get<std::vector<size_t>>(input_param);\n          if (input_vec.size() != num_blocks_) {\n            PARSE_ERROR(\n                context,\n                name << \" must be the same size as RadialDistributions (size=\"\n                     << radial_distributions_.size()\n                     << \") and one larger than RadialPartitioning (size=\"\n                     << radial_partitioning_.size() << \"), but has size \"\n                     << input_vec.size() << \".\");\n          }\n          return input_vec;\n        }\n      };\n  initial_refinement_levels_ = check_possible_list_instantiation(\n      std::move(initial_refinement_levels), \"InitialRadialRefinement\");\n  initial_num_points_ = check_possible_list_instantiation(\n      std::move(initial_num_points), \"InitialNumberOfRadialGridPoints\");\n\n  if ((inner_boundary_condition_ == nullptr) !=\n      (outer_boundary_condition_ == nullptr)) {\n    PARSE_ERROR(context,\n                \"Must specify either both inner and outer boundary conditions \"\n                \"or neither.\");\n  }\n  using domain::BoundaryConditions::is_none;\n  if (is_none(inner_boundary_condition_) or\n      is_none(outer_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(inner_boundary_condition_) or\n      is_periodic(outer_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"Cannot have periodic boundary conditions with CartoonSphere1D\");\n  }\n  for (size_t block = 0; block < num_blocks_; ++block) {\n    const std::string block_name = \"Block\" + std::to_string(block);\n    block_names_.emplace_back(block_name);\n    block_groups_[block_name].insert(block_name);\n  }\n}\n\nDomain<3> CartoonSphere1D::create_domain() const {\n  using Interval = CoordinateMaps::Interval;\n  using Identity1D = CoordinateMaps::Identity<1>;\n  using cartoon_sphere_map =\n      CoordinateMaps::ProductOf3Maps<Interval, Identity1D, Identity1D>;\n  const Identity1D identity_map;\n\n  std::vector<Block<3>> blocks;\n  blocks.reserve(num_blocks_);\n  const auto aligned = OrientationMap<3>::create_aligned();\n  for (size_t i = 0; i < num_blocks_; ++i) {\n    auto block_map = cartoon_sphere_map{\n        {-1.0, 1.0, i == 0 ? inner_bound_ : radial_partitioning_[i - 1],\n         i == num_blocks_ - 1 ? outer_bound_ : radial_partitioning_[i],\n         radial_distributions_[i], 0.0},\n        identity_map,\n        identity_map};\n    auto stationary_map =\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            block_map);\n    DirectionMap<3, BlockNeighbors<3>> neighbors;\n    if (i > 0) {\n      neighbors.emplace(std::pair{Direction<3>::lower_xi(),\n                                  BlockNeighbors<3>{i - 1, aligned}});\n    }\n    if (i < num_blocks_ - 1) {\n      neighbors.emplace(std::pair{Direction<3>::upper_xi(),\n                                  BlockNeighbors<3>{i + 1, aligned}});\n    }\n    blocks.emplace_back(std::move(stationary_map), i, std::move(neighbors),\n                        block_names_.at(i), domain::topologies::cartoon_sphere);\n  }\n\n  Domain<3> domain(std::move(blocks), {}, block_groups_);\n\n  if (not time_dependence_->is_none()) {\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n        block_maps_grid_to_inertial =\n            time_dependence_->block_maps_grid_to_inertial(num_blocks_);\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n        block_maps_grid_to_distorted =\n            time_dependence_->block_maps_grid_to_distorted(num_blocks_);\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n        block_maps_distorted_to_inertial =\n            time_dependence_->block_maps_distorted_to_inertial(num_blocks_);\n    for (size_t block_id = 0; block_id < num_blocks_; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n  return domain;\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nCartoonSphere1D::external_boundary_conditions() const {\n  if (outer_boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{num_blocks_};\n  boundary_conditions[0][Direction<3>::lower_xi()] =\n      inner_boundary_condition_->get_clone();\n  boundary_conditions[num_blocks_ - 1][Direction<3>::upper_xi()] =\n      outer_boundary_condition_->get_clone();\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 3>> CartoonSphere1D::initial_extents() const {\n  std::vector<std::array<size_t, 3>> output;\n  output.reserve(initial_num_points_.size());\n  for (const auto& val : initial_num_points_) {\n    // cartoon bases always have extents set to 1\n    output.push_back({val, 1_st, 1_st});\n  }\n  return output;\n}\n\nstd::vector<std::array<size_t, 3>> CartoonSphere1D::initial_refinement_levels()\n    const {\n  std::vector<std::array<size_t, 3>> output;\n  output.reserve(initial_refinement_levels_.size());\n  for (const auto& val : initial_refinement_levels_) {\n    // cartoon bases always have no refinement\n    output.push_back({val, 0_st, 0_st});\n  }\n  return output;\n}\n\nstd::vector<std::string> CartoonSphere1D::block_names() const {\n  return block_names_;\n}\n\nstd::unordered_map<std::string, std::unordered_set<std::string>>\nCartoonSphere1D::block_groups() const {\n  return block_groups_;\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nCartoonSphere1D::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if (time_dependence_->is_none()) {\n    return {};\n  } else {\n    return time_dependence_->functions_of_time(initial_expiration_times);\n  }\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/CartoonSphere1D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Interval;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators {\n/// Create a 3D Domain that is topologically a line. The 2nd and 3rd\n/// dimensions use Cartoon bases with Killing vectors along the \\f$\\theta\\f$ and\n/// \\f$\\phi\\f$ directions.\nclass CartoonSphere1D : public DomainCreator<3> {\n public:\n  using maps_list = tmpl::list<domain::CoordinateMap<\n      Frame::BlockLogical, Frame::Inertial,\n      CoordinateMaps::ProductOf3Maps<CoordinateMaps::Interval,\n                                     CoordinateMaps::Identity<1>,\n                                     CoordinateMaps::Identity<1>>>>;\n\n  static std::string name() { return \"CartoonSphere1D\"; }\n\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Inner radius of domain, which is a sphere if set to 0, otherwise a \"\n        \"spherical shell.\"};\n  };\n\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Outer radius of domain.\"};\n  };\n\n  struct InitialRadialRefinement {\n    using type = std::variant<size_t, std::vector<size_t>>;\n    static constexpr Options::String help = {\n        \"Initial refinement level for the radial direction. If one value is \"\n        \"given, it will be applied to all blocks, or every block can be \"\n        \"specified individually.\"};\n  };\n\n  struct InitialNumberOfRadialGridPoints {\n    using type = std::variant<size_t, std::vector<size_t>>;\n    static constexpr Options::String help = {\n        \"Initial number of radial grid points. If one input is given, it \"\n        \"will be applied to all blocks, or every block can be specified \"\n        \"individually.\"};\n  };\n\n  struct RadialPartitioning {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"Radial coordinates of the boundaries splitting the radial blocks, \"\n        \"strictly between InnerRadius and OuterRadius. They must be given in \"\n        \"ascending order.\"};\n  };\n\n  struct RadialDistributions {\n    using type = std::variant<CoordinateMaps::Distribution,\n                              std::vector<CoordinateMaps::Distribution>>;\n    static constexpr Options::String help = {\n        \"Distribution of grid points along the radial blocks. A single input \"\n        \"will be applied to all blocks, or every block can be specified \"\n        \"individually, in which case for N partitions, there must be N+1 \"\n        \"distributions.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct InnerBoundaryCondition {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions at the inner boundary.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct OuterBoundaryCondition {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions at the outer boundary.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain. Specify `None` for no \"\n        \"time dependant maps.\"};\n  };\n\n  using basic_options =\n      tmpl::list<InnerRadius, OuterRadius, InitialRadialRefinement,\n                 InitialNumberOfRadialGridPoints, RadialPartitioning,\n                 RadialDistributions, TimeDependence>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          InnerBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>,\n          OuterBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"A sphere domain that requires/enforces spherical symmetry, resulting in \"\n      \"a 1D computational domain (the radial axis). It uses Cartoon partial \"\n      \"derivatives for the angular directions not in the computational \"\n      \"domain.\"};\n\n  CartoonSphere1D(\n      double inner_bound, double outer_bound,\n      typename InitialRadialRefinement::type&& initial_refinement_levels,\n      typename InitialNumberOfRadialGridPoints::type&& initial_num_points,\n      std::vector<double> radial_partitioning = {},\n      const typename RadialDistributions::type& radial_distributions =\n          domain::CoordinateMaps::Distribution::Linear,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n          time_dependence = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          inner_boundary_condition = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          outer_boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  CartoonSphere1D() = default;\n  CartoonSphere1D(const CartoonSphere1D&) = delete;\n  CartoonSphere1D(CartoonSphere1D&&) = default;\n  CartoonSphere1D& operator=(const CartoonSphere1D&) = delete;\n  CartoonSphere1D& operator=(CartoonSphere1D&&) = default;\n  ~CartoonSphere1D() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n  // The block names are Block0, Block1, ..., starting with the innermost\n  // Block.\n  std::vector<std::string> block_names() const override;\n\n  // The block groups are Block0, Block1, ..., starting with the innermost\n  // Block.\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  double inner_bound_{};\n  double outer_bound_{};\n  std::vector<size_t> initial_refinement_levels_{};\n  std::vector<size_t> initial_num_points_{};\n  std::vector<double> radial_partitioning_{};\n  std::vector<CoordinateMaps::Distribution> radial_distributions_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      inner_boundary_condition_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      outer_boundary_condition_{};\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n      time_dependence_;\n  size_t num_blocks_{};\n  std::vector<std::string> block_names_{};\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_{};\n};\n\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/CartoonSphere2D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/CartoonSphere2D.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ShellDistribution.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace Frame {\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n\nnamespace domain::creators {\nCartoonSphere2D::CartoonSphere2D(\n    double inner_radius, double outer_radius,\n    typename InitialRefinement::type&& initial_refinement,\n    typename InitialGridPoints::type&& initial_number_of_grid_points,\n    std::vector<double> radial_partitioning, bool use_equiangular_map,\n    std::variant<Excision, InnerSquare> interior,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dependence,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        y_axis_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        outer_boundary_condition,\n    const Options::Context& context)\n    : inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      radial_partitioning_(std::move(radial_partitioning)),\n      use_equiangular_map_(use_equiangular_map),\n      interior_(std::move(interior)),\n      fill_interior_(std::holds_alternative<InnerSquare>(interior_)),\n      time_dependence_(std::move(time_dependence)),\n      y_axis_boundary_condition_(std::move(y_axis_boundary_condition)),\n      outer_boundary_condition_(std::move(outer_boundary_condition)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<3>>();\n  }\n  if (inner_radius_ >= outer_radius_) {\n    PARSE_ERROR(context,\n                \"Inner radius must be smaller than outer radius, but inner \"\n                \"radius is \" +\n                    std::to_string(inner_radius_) + \" and outer radius is \" +\n                    std::to_string(outer_radius_) + \".\");\n  }\n\n  std::vector<domain::CoordinateMaps::Distribution> dummy_vec;\n  const auto dummy_distribution = CoordinateMaps::Distribution::Linear;\n  // using this for checks of input values\n  set_shell_distribution(make_not_null(&num_shells_), make_not_null(&dummy_vec),\n                         radial_partitioning_, dummy_distribution,\n                         inner_radius_, outer_radius_, \"inner\", \"outer\",\n                         context);\n  // num_shells_ is the number of wedge shells, always > 0\n  num_blocks_ = 3 * num_shells_ + (fill_interior_ ? 1 : 0);\n\n  const auto check_possible_list_instantiation =\n      [this, &context](std::variant<std::array<size_t, 2>,\n                                    std::vector<std::array<size_t, 2>>>\n                           input_param,\n                       const std::string& name) {\n        if (std::holds_alternative<std::array<size_t, 2>>(input_param)) {\n          const auto input_value = std::get<std::array<size_t, 2>>(input_param);\n          return std::vector<std::array<size_t, 2>>(num_blocks_, input_value);\n        } else {\n          const auto& input_vec =\n              std::get<std::vector<std::array<size_t, 2>>>(input_param);\n          if (input_vec.size() != num_shells_) {\n            PARSE_ERROR(\n                context,\n                name << \" must be one larger than RadialPartitioning (size=\"\n                     << radial_partitioning_.size() << \"), but has size \"\n                     << input_vec.size() << \".\");\n          }\n          std::vector<std::array<size_t, 2>> extended;\n          extended.reserve(num_blocks_);\n          if (fill_interior_) {\n            extended.push_back(input_vec[0]);\n          }\n          for (size_t i = 0; i < num_shells_; ++i) {\n            for (size_t j = 0; j < 3; ++j) {\n              extended.push_back(input_vec[i]);\n            }\n          }\n          // reversing due to block_id scheme going from outer to inner layers\n          std::reverse(extended.begin(), extended.end());\n          return extended;\n        }\n      };\n  initial_refinement_ = check_possible_list_instantiation(\n      std::move(initial_refinement), \"InitialRefinement\");\n  initial_number_of_grid_points_ = check_possible_list_instantiation(\n      std::move(initial_number_of_grid_points), \"InitialGridPoints\");\n\n  if ((y_axis_boundary_condition_ == nullptr) !=\n      (outer_boundary_condition_ == nullptr)) {\n    PARSE_ERROR(context,\n                \"Must specify either both inner and outer boundary conditions \"\n                \"or neither.\");\n  }\n  using domain::BoundaryConditions::is_none;\n  if (is_none(y_axis_boundary_condition_) or\n      is_none(outer_boundary_condition_) or\n      (not fill_interior_ and\n       is_none(std::get<Excision>(interior_).boundary_condition))) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(y_axis_boundary_condition_) or\n      is_periodic(outer_boundary_condition_) or\n      (not fill_interior_ and\n       is_periodic(std::get<Excision>(interior_).boundary_condition))) {\n    PARSE_ERROR(context,\n                \"Cannot have periodic boundary conditions on a 2D sphere.\");\n  }\n\n  block_names_.reserve(num_blocks_);\n  // naming and numbering goes from outer-most shell, starting from bottom and\n  // going counterclockwise, going to inside neighboring shell. The \"center\"\n  // half-circle follows same numbering with the half-square being the last\n  // block\n  const std::array<std::string, 3> wedge_directions{\"_LowerY\", \"_UpperX\",\n                                                    \"_UpperY\"};\n  for (size_t i = 0; i < num_shells_; ++i) {\n    const std::string shell = \"Shell\" + std::to_string(i);\n    block_groups_[shell];\n    for (const std::string& direction : wedge_directions) {\n      const std::string name = shell + direction;\n      block_names_.emplace_back(name);\n      block_groups_[shell].insert(name);\n    }\n  }\n  if (fill_interior_) {\n    const std::string shell = \"Shell\" + std::to_string(num_shells_ - 1);\n    block_names_.emplace_back(shell + \"_HalfSquare\");\n    block_groups_[shell].insert(shell + \"_HalfSquare\");\n  }\n}\n\nDomain<3> CartoonSphere2D::create_domain() const {\n  using Affine = CoordinateMaps::Affine;\n  using Equiangular = CoordinateMaps::Equiangular;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using Equiangular2D =\n      CoordinateMaps::ProductOf2Maps<Equiangular, Equiangular>;\n  using Wedge2DMap = CoordinateMaps::Wedge<2>;\n  using Identity1D = CoordinateMaps::Identity<1>;\n  using Wedge3DPrism =\n      domain::CoordinateMaps::ProductOf2Maps<Wedge2DMap, Identity1D>;\n  using Rotation3D = CoordinateMaps::DiscreteRotation<3>;\n\n  std::vector<Block<3>> blocks;\n  blocks.reserve(num_blocks_);\n\n  const double inner_square_sphericity =\n      fill_interior_ ? std::get<InnerSquare>(interior_).sphericity\n                     : std::numeric_limits<double>::signaling_NaN();\n  if (fill_interior_ and inner_square_sphericity != 0.0) {\n    ERROR(\"CartoonSphere2D cannot have a non-zero inner sphericity, \"\n          << \"got \" << inner_square_sphericity << \".\");\n  }\n\n  const auto aligned = OrientationMap<3>::create_aligned();\n  // quarter turn\n  const OrientationMap<3> turn_ccw(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_eta(), Direction<3>::upper_xi(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> half_turn(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n       Direction<3>::upper_zeta()}});\n  // quarter turn\n  const OrientationMap<3> turn_cw(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n       Direction<3>::upper_zeta()}});\n\n  for (size_t i = 0; i < num_shells_; ++i) {\n    const bool on_inner = i == num_shells_ - 1;\n    const bool has_square = on_inner and fill_interior_;\n    const double inner_radius =\n        on_inner ? inner_radius_\n                 : radial_partitioning_[radial_partitioning_.size() - 1 - i];\n    const double outer_radius =\n        i == 0 ? outer_radius_\n               : radial_partitioning_[radial_partitioning_.size() - i];\n    const double inner_sphericity = has_square ? inner_square_sphericity : 1.0;\n    // this is a half-wedge, -y\n    auto coord_maps = make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                      Frame::Inertial, 3>(\n        std::vector<Rotation3D>{Rotation3D{turn_ccw}},\n        Wedge3DPrism{\n            Wedge2DMap{\n                inner_radius, outer_radius, inner_sphericity, 1.0,\n                OrientationMap<2>{std::array<Direction<2>, 2>{\n                    {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                use_equiangular_map_,\n                domain::CoordinateMaps::Wedge<2>::WedgeHalves::UpperOnly},\n            Identity1D{}});\n    // this is a full wedge, +x\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            Rotation3D{turn_cw},\n            Wedge3DPrism{\n                Wedge2DMap{\n                    inner_radius, outer_radius, inner_sphericity, 1.0,\n                    OrientationMap<2>{std::array<Direction<2>, 2>{\n                        {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                    use_equiangular_map_},\n                Identity1D{}}));\n    // this is a half-wedge, +y\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            Rotation3D{turn_cw},\n            Wedge3DPrism{\n                Wedge2DMap{\n                    inner_radius, outer_radius, inner_sphericity, 1.0,\n                    OrientationMap<2>{std::array<Direction<2>, 2>{\n                        {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                    use_equiangular_map_,\n                    domain::CoordinateMaps::Wedge<2>::WedgeHalves::LowerOnly},\n                Identity1D{}}));\n    if (has_square) {\n      if (use_equiangular_map_) {\n        coord_maps.emplace_back(\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                CoordinateMaps::ProductOf2Maps<Equiangular2D, Identity1D>{\n                    Equiangular2D{\n                        // note the non-standard logical coordinate bounds, such\n                        // that the eta-neighbor collocation points match the\n                        // half-wedges'\n                        Equiangular(-3.0, 1.0, -1.0 * inner_radius_ / sqrt(2.0),\n                                    inner_radius_ / sqrt(2.0)),\n                        Equiangular(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(2.0),\n                                    inner_radius_ / sqrt(2.0))},\n                    Identity1D{}}));\n      } else {\n        coord_maps.emplace_back(\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                CoordinateMaps::ProductOf2Maps<Affine2D, Identity1D>{\n                    Affine2D{Affine(-1.0, 1.0, 0.0, inner_radius_ / sqrt(2.0)),\n                             Affine(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(2.0),\n                                    inner_radius_ / sqrt(2.0))},\n                    Identity1D{}}));\n      }\n    }\n\n    std::vector<DirectionMap<3, BlockNeighbors<3>>> neighbors{\n        static_cast<size_t>(3 + (has_square ? 1 : 0))};\n\n    // between shell blocks\n    neighbors[0].emplace(std::pair(Direction<3>::upper_xi(),\n                                   BlockNeighbors<3>(3 * i + 1, half_turn)));\n\n    neighbors[1].emplace(std::pair(Direction<3>::upper_xi(),\n                                   BlockNeighbors<3>(3 * i + 0, half_turn)));\n    neighbors[1].emplace(std::pair(Direction<3>::lower_xi(),\n                                   BlockNeighbors<3>(3 * i + 2, aligned)));\n\n    neighbors[2].emplace(std::pair(Direction<3>::upper_xi(),\n                                   BlockNeighbors<3>(3 * i + 1, aligned)));\n\n    // to the +r direction (if i==0, has external boundary)\n    if (i != 0) {\n      neighbors[0].emplace(\n          std::pair(Direction<3>::lower_eta(),\n                    BlockNeighbors<3>((i - 1) * 3 + 0, aligned)));\n      neighbors[1].emplace(\n          std::pair(Direction<3>::upper_eta(),\n                    BlockNeighbors<3>((i - 1) * 3 + 1, aligned)));\n      neighbors[2].emplace(\n          std::pair(Direction<3>::upper_eta(),\n                    BlockNeighbors<3>((i - 1) * 3 + 2, aligned)));\n    }\n\n    // to the -r direction (if excise_center_, innermost has external boundary)\n    if (i < num_shells_ - 1) {\n      neighbors[0].emplace(\n          std::pair(Direction<3>::upper_eta(),\n                    BlockNeighbors<3>((i + 1) * 3 + 0, aligned)));\n      neighbors[1].emplace(\n          std::pair(Direction<3>::lower_eta(),\n                    BlockNeighbors<3>((i + 1) * 3 + 1, aligned)));\n      neighbors[2].emplace(\n          std::pair(Direction<3>::lower_eta(),\n                    BlockNeighbors<3>((i + 1) * 3 + 2, aligned)));\n    } else if (has_square) {\n      // on the center half-circle\n      neighbors[0].emplace(std::pair(Direction<3>::upper_eta(),\n                                     BlockNeighbors<3>(3 * i + 3, aligned)));\n\n      neighbors[1].emplace(std::pair(Direction<3>::lower_eta(),\n                                     BlockNeighbors<3>(3 * i + 3, turn_ccw)));\n\n      neighbors[2].emplace(std::pair(Direction<3>::lower_eta(),\n                                     BlockNeighbors<3>(3 * i + 3, aligned)));\n\n      neighbors[3].emplace(std::pair(Direction<3>::lower_eta(),\n                                     BlockNeighbors<3>(3 * i + 0, aligned)));\n      neighbors[3].emplace(std::pair(Direction<3>::upper_xi(),\n                                     BlockNeighbors<3>(3 * i + 1, turn_cw)));\n      neighbors[3].emplace(std::pair(Direction<3>::upper_eta(),\n                                     BlockNeighbors<3>(3 * i + 2, aligned)));\n    }\n\n    for (size_t j = 0; j < neighbors.size(); ++j) {\n      blocks.emplace_back(\n          std::move(coord_maps.at(j)), 3 * i + j, std::move(neighbors.at(j)),\n          block_names_.at(3 * i + j), domain::topologies::cartoon_cylinder);\n    }\n  }\n\n  Domain<3> domain(std::move(blocks), {}, block_groups_);\n\n  if (not time_dependence_->is_none()) {\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n        block_maps_grid_to_inertial =\n            time_dependence_->block_maps_grid_to_inertial(num_blocks_);\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n        block_maps_grid_to_distorted =\n            time_dependence_->block_maps_grid_to_distorted(num_blocks_);\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n        block_maps_distorted_to_inertial =\n            time_dependence_->block_maps_distorted_to_inertial(num_blocks_);\n    for (size_t block_id = 0; block_id < num_blocks_; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n  return domain;\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nCartoonSphere2D::external_boundary_conditions() const {\n  if (outer_boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{num_blocks_};\n\n  // outer rim, +r direction\n  boundary_conditions[0][Direction<3>::lower_eta()] =\n      outer_boundary_condition_->get_clone();\n  boundary_conditions[1][Direction<3>::upper_eta()] =\n      outer_boundary_condition_->get_clone();\n  boundary_conditions[2][Direction<3>::upper_eta()] =\n      outer_boundary_condition_->get_clone();\n\n  // x=0 axis\n  for (size_t i = 0; i < num_shells_; ++i) {\n    boundary_conditions[3 * i + 0][Direction<3>::lower_xi()] =\n        y_axis_boundary_condition_->get_clone();\n    boundary_conditions[3 * i + 2][Direction<3>::lower_xi()] =\n        y_axis_boundary_condition_->get_clone();\n  }\n  if (fill_interior_) {\n    boundary_conditions[num_blocks_ - 1][Direction<3>::lower_xi()] =\n        y_axis_boundary_condition_->get_clone();\n  } else {\n    const auto& excision_boundary_condition =\n        std::get<Excision>(interior_).boundary_condition;\n    boundary_conditions[(num_shells_ - 1) * 3 + 0][Direction<3>::upper_eta()] =\n        excision_boundary_condition->get_clone();\n    boundary_conditions[(num_shells_ - 1) * 3 + 1][Direction<3>::lower_eta()] =\n        excision_boundary_condition->get_clone();\n    boundary_conditions[(num_shells_ - 1) * 3 + 2][Direction<3>::lower_eta()] =\n        excision_boundary_condition->get_clone();\n  }\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 3>> CartoonSphere2D::initial_extents() const {\n  std::vector<std::array<size_t, 3>> extended;\n  extended.reserve(num_blocks_);\n\n  std::transform(initial_number_of_grid_points_.begin(),\n                 initial_number_of_grid_points_.end(),\n                 std::back_inserter(extended),\n                 [](const std::array<size_t, 2>& arr) -> std::array<size_t, 3> {\n                   // data is read as [r, theta] but coordinates are [theta, r]\n                   return {arr[1], arr[0], 1};\n                 });\n  if (fill_interior_) {\n    // dealing with half-square, want identical extents, matching to wedge theta\n    extended.back()[1] = extended.back()[0];\n  }\n  return extended;\n}\n\nstd::vector<std::array<size_t, 3>> CartoonSphere2D::initial_refinement_levels()\n    const {\n  std::vector<std::array<size_t, 3>> extended;\n  extended.reserve(num_blocks_);\n\n  size_t n = 0;\n  std::transform(\n      initial_refinement_.begin(), initial_refinement_.end(),\n      std::back_inserter(extended),\n      [&n](const std::array<size_t, 2>& arr) -> std::array<size_t, 3> {\n        const size_t shift = (n % 3 == 0 or n % 3 == 2) and arr[1] != 0 ? 1 : 0;\n        ++n;\n        // data is read as [r, theta] but coordinates are [theta, r]\n        return {arr[1] - shift, arr[0], 0};\n      });\n  if (fill_interior_) {\n    // dealing with half-square, want proportional refinement, matching to wedge\n    // theta\n    extended.back()[1] = initial_refinement_.back()[1];\n    const size_t shift = extended.back()[1] != 0 ? 1 : 0;\n    extended.back()[0] = extended.back()[1] - shift;\n  }\n  return extended;\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nCartoonSphere2D::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if (time_dependence_->is_none()) {\n    return {};\n  } else {\n    return time_dependence_->functions_of_time(initial_expiration_times);\n  }\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/CartoonSphere2D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\nclass Equiangular;\ntemplate <size_t Dim>\nclass Identity;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\ntemplate <size_t Dim>\nclass Wedge;\ntemplate <size_t Dim>\nclass DiscreteRotation;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators::detail{\n\n/// Options for filling the interior of the sphere with a cube\nstruct InnerSquare {\n  static constexpr Options::String help = {\n      \"Fill the interior of the 2D sphere with a half-square.\"};\n  struct Sphericity {\n    static std::string name() { return \"FillWithSphericity\"; }\n    using type = double;\n    static constexpr Options::String help = {\n        \"Sphericity of the inner half-square. Only a sphericity of \"\n        \"0.0 is currently implemented.\"};\n    static double lower_bound() { return 0.0; }\n    static double upper_bound() { return 0.0; }\n  };\n  using options = tmpl::list<Sphericity>;\n  InnerSquare() = default;\n  explicit InnerSquare(double sphericity_in) : sphericity(sphericity_in) {}\n  double sphericity = std::numeric_limits<double>::signaling_NaN();\n};\n} // namespace domain::creators::detail\n\nnamespace domain::creators {\n/// Create a 3D Domain with a half-disk computational domain employing axial\n/// symmetry. The third dimension uses a Cartoon basis with Killing vector\n/// along the $\\phi$ direction.\nclass CartoonSphere2D : public DomainCreator<3> {\n public:\n  using maps_list = tmpl::list<\n      domain::CoordinateMap<\n          Frame::BlockLogical, Frame::Inertial,\n          CoordinateMaps::ProductOf2Maps<\n              CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                             CoordinateMaps::Affine>,\n              CoordinateMaps::Identity<1>>>,\n      domain::CoordinateMap<\n          Frame::BlockLogical, Frame::Inertial,\n          CoordinateMaps::ProductOf2Maps<\n              CoordinateMaps::ProductOf2Maps<CoordinateMaps::Equiangular,\n                                             CoordinateMaps::Equiangular>,\n              CoordinateMaps::Identity<1>>>,\n      domain::CoordinateMap<\n          Frame::BlockLogical, Frame::Inertial,\n          CoordinateMaps::DiscreteRotation<3>,\n          CoordinateMaps::ProductOf2Maps<CoordinateMaps::Wedge<2>,\n                                         CoordinateMaps::Identity<1>>>>;\n\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Radius of the circle circumscribing the inner half-square, or the \"\n        \"radius of the inner boundary if ExciseCenter = true.\"};\n  };\n\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Outer radius of the half-disk.\"};\n  };\n\n  struct InitialRefinement {\n    using type =\n        std::variant<std::array<size_t, 2>, std::vector<std::array<size_t, 2>>>;\n    static constexpr Options::String help = {\n        \"Initial refinement level in [r, theta]. If one pair is given, it will \"\n        \"be applied to all blocks, otherwise each shell can be specified.\\n\"\n        \"Note: the half-wedges will have their theta values decremented by \"\n        \"one.\\n\"\n        \"Note: the inner square, if included, will have refinement set to \"\n        \"the theta value for the center half-circle for both dimensions \"\n        \"(decremented by one in the halved dimension).\"};\n  };\n\n  struct InitialGridPoints {\n    using type =\n        std::variant<std::array<size_t, 2>, std::vector<std::array<size_t, 2>>>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in [r,theta]. If one pair is given, it \"\n        \"will be applied to all blocks, otherwise each shell can be \"\n        \"specified, from innermost to outermost.\\n\"\n        \"Note: if included, the inner square will have both dimensions'\"\n        \"number of grid points set to the theta value of the surrounding \"\n        \"wedges.\"};\n  };\n\n  struct RadialPartitioning {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"Radial coordinates of the boundaries splitting the radial shells. \"\n        \"Leave emtpy for only one layer.\"};\n  };\n\n  struct UseEquiangularMap {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Use equiangular instead of equidistant coordinates in wedges.\"};\n  };\n\n  using Excision = detail::Excision;\n  using InnerSquare = detail::InnerSquare;\n\n  struct Interior {\n    using type = std::variant<Excision, InnerSquare>;\n    static constexpr Options::String help = {\n        \"Specify 'ExciseWithBoundaryCondition' and a boundary condition to \"\n        \"excise the interior of the sphere, leaving a spherical shell \"\n        \"(or just 'Excise' if boundary conditions are disabled). \"\n        \"Or specify 'FillWithSphericity' to fill the interior.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct YAxisBoundaryCondition {\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n    static constexpr Options::String help = {\n      \"The boundary condition to impose at the x=z=0 boundary.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct OuterBoundaryCondition {\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n    static constexpr Options::String help = {\n        \"The boundary condition to impose at the outer boundary of the \"\n        \"domain.\"};\n  };\n\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain. Specify `None` for no \"\n        \"time dependant maps.\"};\n  };\n\n  using basic_options =\n      tmpl::list<InnerRadius, OuterRadius, InitialRefinement, InitialGridPoints,\n                 RadialPartitioning, UseEquiangularMap, Interior,\n                 TimeDependence>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          YAxisBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>,\n          OuterBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"Creates a sphere that requires/enforces axial-symmetry.\\n\"\n      \"The computational domain is a 2D half-disk, consisting of an inner\\n\"\n      \"half-square with two half-wedges above and below and a full wedge to\\n\"\n      \"the right. This can be extended to have mulitple shells of wedges by\\n\"\n      \"specifying a radial partition. The inner half-square can be excised,\\n\"\n      \"in which case the inner sphericity is set to 1.\\n\"\n      \"Equiangular coordinates give better gridpoint spacings in the angular\\n\"\n      \"direction, while equidistant coordinates give better gridpoint\\n\"\n      \"spacings in the center half-square.\"};\n\n  CartoonSphere2D(\n      double inner_radius, double outer_radius,\n      typename InitialRefinement::type&& initial_refinement,\n      typename InitialGridPoints::type&& initial_number_of_grid_points,\n      std::vector<double> radial_partitioning, bool use_equiangular_map,\n      std::variant<Excision, InnerSquare> interior,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n          time_dependence = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          y_axis_boundary_condition = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          outer_boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  CartoonSphere2D() = default;\n  CartoonSphere2D(const CartoonSphere2D&) = delete;\n  CartoonSphere2D(CartoonSphere2D&&) = default;\n  CartoonSphere2D& operator=(const CartoonSphere2D&) = delete;\n  CartoonSphere2D& operator=(CartoonSphere2D&&) = default;\n  ~CartoonSphere2D() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return block_groups_;\n  }\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  double inner_radius_{};\n  double outer_radius_{};\n  std::vector<std::array<size_t, 2>> initial_refinement_{};\n  std::vector<std::array<size_t, 2>> initial_number_of_grid_points_{};\n  std::vector<double> radial_partitioning_{};\n  bool use_equiangular_map_{false};\n  std::variant<Excision, InnerSquare> interior_{};\n  bool fill_interior_{false};\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n      time_dependence_ = nullptr;\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      y_axis_boundary_condition_ = nullptr;\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      outer_boundary_condition_ = nullptr;\n  std::vector<std::string> block_names_;\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_;\n  size_t num_shells_{};\n  size_t num_blocks_{};\n};\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Cylinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Cylinder.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <memory>\n#include <unordered_map>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ExpandOverBlocks.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain::creators {\nCylinder::Cylinder(\n    const double inner_radius, const double outer_radius,\n    const double lower_z_bound, const double upper_z_bound,\n    const bool is_periodic_in_z,\n    const typename InitialRefinement::type& initial_refinement,\n    const typename InitialGridPoints::type& initial_number_of_grid_points,\n    const bool use_equiangular_map, std::vector<double> radial_partitioning,\n    std::vector<double> partitioning_in_z,\n    std::vector<domain::CoordinateMaps::Distribution> radial_distribution,\n    std::vector<domain::CoordinateMaps::Distribution> distribution_in_z,\n    const Options::Context& context)\n    : inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      lower_z_bound_(lower_z_bound),\n      upper_z_bound_(upper_z_bound),\n      is_periodic_in_z_(is_periodic_in_z),\n      use_equiangular_map_(use_equiangular_map),\n      radial_partitioning_(std::move(radial_partitioning)),\n      partitioning_in_z_(std::move(partitioning_in_z)),\n      radial_distribution_(std::move(radial_distribution)),\n      distribution_in_z_(std::move(distribution_in_z)) {\n  if (inner_radius_ > outer_radius_) {\n    PARSE_ERROR(context,\n                \"Inner radius must be smaller than outer radius, but inner \"\n                \"radius is \" +\n                    std::to_string(inner_radius_) + \" and outer radius is \" +\n                    std::to_string(outer_radius_) + \".\");\n  }\n  if (lower_z_bound_ > upper_z_bound_) {\n    PARSE_ERROR(context,\n                \"Lower z-bound must be smaller than upper z-bound, but lower \"\n                \"bound is \" +\n                    std::to_string(lower_z_bound_) + \" and upper bound is \" +\n                    std::to_string(upper_z_bound_) + \".\");\n  }\n  if (not std::is_sorted(radial_partitioning_.begin(),\n                         radial_partitioning_.end())) {\n    PARSE_ERROR(context,\n                \"Specify radial partitioning in ascending order. Specified \"\n                \"radial partitioning is: \" +\n                    get_output(radial_partitioning_));\n  }\n  if (not radial_partitioning_.empty()) {\n    if (radial_partitioning_.front() <= inner_radius_) {\n      PARSE_ERROR(\n          context,\n          \"First radial partition must be larger than inner radius, but is: \" +\n              std::to_string(inner_radius_));\n    }\n    if (radial_partitioning_.back() >= outer_radius_) {\n      PARSE_ERROR(\n          context,\n          \"Last radial partition must be smaller than outer radius, but is: \" +\n              std::to_string(outer_radius_));\n    }\n  }\n  if (not std::is_sorted(partitioning_in_z_.begin(),\n                         partitioning_in_z_.end())) {\n    PARSE_ERROR(context,\n                \"Specify partitioning in z in ascending order. Specified \"\n                \"partitioning is: \" +\n                    get_output(partitioning_in_z_));\n  }\n  if (not partitioning_in_z_.empty()) {\n    if (partitioning_in_z_.front() <= lower_z_bound_) {\n      PARSE_ERROR(\n          context,\n          \"First partition in z must be larger than lower z-bound, but is: \" +\n              std::to_string(lower_z_bound_));\n    }\n    if (partitioning_in_z_.back() >= upper_z_bound_) {\n      PARSE_ERROR(\n          context,\n          \"Last partition in z must be smaller than upper z-bound, but is: \" +\n              std::to_string(upper_z_bound_));\n    }\n  }\n  const size_t num_shells = 1 + radial_partitioning_.size();\n  const size_t num_layers = 1 + partitioning_in_z_.size();\n  if (radial_distribution_.size() != num_shells) {\n    PARSE_ERROR(\n        context,\n        \"Specify a 'RadialDistribution' for every cylindrical shell. You \"\n        \"specified \"\n            << radial_distribution_.size() << \" items, but the domain has \"\n            << num_shells << \" shells.\");\n  }\n  if (radial_distribution_.front() !=\n      domain::CoordinateMaps::Distribution::Linear) {\n    PARSE_ERROR(context,\n                \"The 'RadialDistribution' must be 'Linear' for the innermost \"\n                \"shell because it changes in circularity. Add entries to \"\n                \"'RadialPartitioning' to add outer shells for which you can \"\n                \"select different radial distributions.\");\n  }\n  if (distribution_in_z_.size() != num_layers) {\n    PARSE_ERROR(context,\n                \"Specify a 'DistributionInZ' for every layer. You specified \"\n                    << distribution_in_z_.size()\n                    << \" items, but the domain has \" << num_layers\n                    << \" layers.\");\n  }\n  if (distribution_in_z_.front() !=\n      domain::CoordinateMaps::Distribution::Linear) {\n    PARSE_ERROR(context,\n                \"The 'DistributionInZ' must be 'Linear' for the lowermost \"\n                \"layer because a 'Logarithmic' distribution places its \"\n                \"singularity at 'LowerZBound'. Add entries to \"\n                \"'PartitioningInZ' to add layers for which you can \"\n                \"select different distributions along z.\");\n  }\n\n  // Create block names and groups\n  static std::array<std::string, 4> direction_descriptions{\"East\", \"North\",\n                                                           \"West\", \"South\"};\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    std::string layer_prefix =\n        num_layers > 1 ? \"Layer\" + std::to_string(layer) : \"\";\n    const std::string inner_cube_name = layer_prefix + \"InnerCube\";\n    block_names_.push_back(inner_cube_name);\n    if (num_layers > 1) {\n      block_groups_[layer_prefix].insert(inner_cube_name);\n      block_groups_[\"InnerCubes\"].insert(inner_cube_name);\n    }\n    for (size_t shell = 0; shell < num_shells; ++shell) {\n      std::string shell_prefix =\n          num_shells > 1 ? \"Shell\" + std::to_string(shell) : \"\";\n      std::string group_name = layer_prefix + shell_prefix + \"Wedges\";\n      for (size_t direction = 0; direction < 4; ++direction) {\n        const std::string shell_name =\n            layer_prefix + shell_prefix +\n            gsl::at(direction_descriptions, direction);\n        block_names_.push_back(shell_name);\n        block_groups_[group_name].insert(shell_name);\n        if (num_layers > 1) {\n          block_groups_[layer_prefix].insert(shell_name);\n        }\n        if (num_shells > 1) {\n          block_groups_[shell_prefix].insert(shell_name);\n        }\n      }\n    }\n  }\n\n  // Expand initial refinement and number of grid points over all blocks\n  const ExpandOverBlocks<std::array<size_t, 3>> expand_over_blocks{\n      block_names_, block_groups_};\n  try {\n    initial_refinement_ = std::visit(expand_over_blocks, initial_refinement);\n  } catch (const std::exception& error) {\n    PARSE_ERROR(context, \"Invalid 'InitialRefinement': \" << error.what());\n  }\n  try {\n    initial_number_of_grid_points_ =\n        std::visit(expand_over_blocks, initial_number_of_grid_points);\n  } catch (const std::exception& error) {\n    PARSE_ERROR(context, \"Invalid 'InitialGridPoints': \" << error.what());\n  }\n\n  // Set refinement and number of grid points of the central cubes in x and y to\n  // the value in angular direction of the wedges and shells\n  for (size_t layer = 0; layer < num_layers; layer++) {\n    auto& central_cube_refinement =\n        initial_refinement_.at(layer * (1 + 4 * num_shells));\n    auto& central_cube_grid_points =\n        initial_number_of_grid_points_.at(layer * (1 + 4 * num_shells));\n    central_cube_refinement[0] = central_cube_refinement[1];\n    central_cube_grid_points[0] = central_cube_grid_points[1];\n  }\n}\n\nCylinder::Cylinder(\n    const double inner_radius, const double outer_radius,\n    const double lower_z_bound, const double upper_z_bound,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        lower_z_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        upper_z_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        mantle_boundary_condition,\n    const typename InitialRefinement::type& initial_refinement,\n    const typename InitialGridPoints::type& initial_number_of_grid_points,\n    const bool use_equiangular_map, std::vector<double> radial_partitioning,\n    std::vector<double> partitioning_in_z,\n    std::vector<domain::CoordinateMaps::Distribution> radial_distribution,\n    std::vector<domain::CoordinateMaps::Distribution> distribution_in_z,\n    const Options::Context& context)\n    : Cylinder(inner_radius, outer_radius, lower_z_bound, upper_z_bound, false,\n               initial_refinement, initial_number_of_grid_points,\n               use_equiangular_map, std::move(radial_partitioning),\n               std::move(partitioning_in_z), std::move(radial_distribution),\n               std::move(distribution_in_z), context) {\n  lower_z_boundary_condition_ = std::move(lower_z_boundary_condition);\n  upper_z_boundary_condition_ = std::move(upper_z_boundary_condition);\n  mantle_boundary_condition_ = std::move(mantle_boundary_condition);\n\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(lower_z_boundary_condition_) xor\n      is_periodic(upper_z_boundary_condition_)) {\n    PARSE_ERROR(context,\n                \"Either both lower and upper z-boundary condition must be \"\n                \"periodic, or neither.\");\n  }\n  if (is_periodic(lower_z_boundary_condition_) and\n      is_periodic(upper_z_boundary_condition_)) {\n    is_periodic_in_z_ = true;\n    lower_z_boundary_condition_ = nullptr;\n    upper_z_boundary_condition_ = nullptr;\n  }\n  if (is_periodic(mantle_boundary_condition_)) {\n    PARSE_ERROR(context,\n                \"A Cylinder can't have periodic boundary conditions in the \"\n                \"radial direction.\");\n  }\n  using domain::BoundaryConditions::is_none;\n  if (is_none(lower_z_boundary_condition_) or\n      is_none(upper_z_boundary_condition_) or\n      is_none(mantle_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  if (mantle_boundary_condition_ == nullptr or\n      (not is_periodic_in_z_ and (lower_z_boundary_condition_ == nullptr or\n                                  upper_z_boundary_condition_ == nullptr))) {\n    PARSE_ERROR(context,\n                \"z-boundary conditions must not be 'nullptr'. Use the other \"\n                \"constructor to specify 'is_periodic_in_z' instead of boundary \"\n                \"conditions.\");\n  }\n}\n\nDomain<3> Cylinder::create_domain() const {\n  const size_t number_of_shells = 1 + radial_partitioning_.size();\n  const size_t number_of_layers = 1 + partitioning_in_z_.size();\n  std::vector<PairOfFaces> pairs_of_faces{};\n  if (is_periodic_in_z_) {\n    // connect faces of end caps in the periodic z-direction\n    const size_t corners_per_layer = 4 * (number_of_shells + 1);\n    const size_t num_corners = number_of_layers * corners_per_layer;\n    PairOfFaces center{\n        {0, 1, 2, 3},\n        {num_corners + 0, num_corners + 1, num_corners + 2, num_corners + 3}};\n    pairs_of_faces.push_back(std::move(center));\n    for (size_t j = 0; j < number_of_shells; j++) {\n      PairOfFaces east{{1 + 4 * j, 5 + 4 * j, 3 + 4 * j, 7 + 4 * j},\n                       {num_corners + 4 * j + 1, num_corners + 4 * j + 5,\n                        num_corners + 4 * j + 3, num_corners + 4 * j + 7}};\n      PairOfFaces north{{3 + 4 * j, 7 + 4 * j, 2 + 4 * j, 6 + 4 * j},\n                        {num_corners + 4 * j + 3, num_corners + 4 * j + 7,\n                         num_corners + 4 * j + 2, num_corners + 4 * j + 6}};\n      PairOfFaces west{{2 + 4 * j, 6 + 4 * j, 0 + 4 * j, 4 + 4 * j},\n                       {num_corners + 4 * j + 2, num_corners + 4 * j + 6,\n                        num_corners + 4 * j + 0, num_corners + 4 * j + 4}};\n      PairOfFaces south{{0 + 4 * j, 4 + 4 * j, 1 + 4 * j, 5 + 4 * j},\n                        {num_corners + 4 * j + 0, num_corners + 4 * j + 4,\n                         num_corners + 4 * j + 1, num_corners + 4 * j + 5}};\n      pairs_of_faces.push_back(std::move(east));\n      pairs_of_faces.push_back(std::move(north));\n      pairs_of_faces.push_back(std::move(west));\n      pairs_of_faces.push_back(std::move(south));\n    }\n  }\n\n  return Domain<3>{\n      cyl_wedge_coordinate_maps<Frame::Inertial>(\n          inner_radius_, outer_radius_, lower_z_bound_, upper_z_bound_,\n          use_equiangular_map_, radial_partitioning_, partitioning_in_z_,\n          radial_distribution_, distribution_in_z_),\n      corners_for_cylindrical_layered_domains(number_of_shells,\n                                              number_of_layers),\n      pairs_of_faces,\n      {},\n      block_names_,\n      block_groups_};\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nCylinder::external_boundary_conditions() const {\n  if (mantle_boundary_condition_ == nullptr) {\n    return {};\n  }\n  const size_t num_shells = 1 + radial_partitioning_.size();\n  const size_t num_layers = 1 + partitioning_in_z_.size();\n  const size_t num_blocks = (1 + 4 * num_shells) * num_layers;\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{num_blocks};\n  // Boundary conditions in z\n  for (size_t block_id = 0; not is_periodic_in_z_ and block_id < num_blocks;\n       ++block_id) {\n    if (block_id < (1 + num_shells * 4)) {\n      boundary_conditions[block_id][Direction<3>::lower_zeta()] =\n          lower_z_boundary_condition_->get_clone();\n    }\n    if (block_id >= boundary_conditions.size() - (1 + num_shells * 4)) {\n      boundary_conditions[block_id][Direction<3>::upper_zeta()] =\n          upper_z_boundary_condition_->get_clone();\n    }\n  }\n  // Radial boundary conditions\n  for (size_t block_id = 1 + 4 * (num_shells - 1);\n       block_id < boundary_conditions.size(); ++block_id) {\n    // clang-tidy thinks we can get division by zero on the modulus operator.\n    // NOLINTNEXTLINE\n    if (block_id % (1 + 4 * num_shells) == 0) {\n      // skip the central cubes and the inner radial wedges\n      block_id += 4 * (num_shells - 1);\n      continue;\n    }\n    boundary_conditions[block_id][Direction<3>::upper_xi()] =\n        mantle_boundary_condition_->get_clone();\n  }\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 3>> Cylinder::initial_extents() const {\n  return initial_number_of_grid_points_;\n}\n\nstd::vector<std::array<size_t, 3>> Cylinder::initial_refinement_levels() const {\n  return initial_refinement_;\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Cylinder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n#include <variant>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Interval;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\ntemplate <size_t Dim>\nclass Wedge;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators {\n/// Create a 3D Domain in the shape of a cylinder where the cross-section\n/// is a square surrounded by four two-dimensional wedges (see `Wedge`).\n///\n/// The outer shell can be split into sub-shells and the cylinder can be split\n/// into disks along its height.\n/// The block numbering starts at the inner square and goes counter-clockwise,\n/// starting with the eastern wedge (+x-direction), through consecutive shells,\n/// then repeats this pattern for all layers bottom to top.\n///\n/// \\image html Cylinder.png \"The Cylinder Domain.\"\nclass Cylinder : public DomainCreator<3> {\n public:\n  using maps_list =\n      tmpl::list<domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf3Maps<CoordinateMaps::Interval,\n                                                    CoordinateMaps::Interval,\n                                                    CoordinateMaps::Interval>>,\n                 domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf2Maps<CoordinateMaps::Wedge<2>,\n                                                    CoordinateMaps::Interval>>>;\n\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Radius of the circle circumscribing the inner square.\"};\n    static double lower_bound() { return 0.; }\n  };\n\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Radius of the cylinder.\"};\n    static double lower_bound() { return 0.; }\n  };\n\n  struct LowerZBound {\n    using type = double;\n    static constexpr Options::String help = {\n        \"z-coordinate of the base of the cylinder.\"};\n  };\n\n  struct UpperZBound {\n    using type = double;\n    static constexpr Options::String help = {\n        \"z-coordinate of the top of the cylinder.\"};\n  };\n\n  struct IsPeriodicInZ {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"True if periodic in the cylindrical z direction.\"};\n  };\n\n  struct InitialRefinement {\n    using type =\n        std::variant<size_t, std::array<size_t, 3>,\n                     std::vector<std::array<size_t, 3>>,\n                     std::unordered_map<std::string, std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial refinement level. Specify one of: a single number, a list \"\n        \"representing [r, theta, z], or such a list for every block in the \"\n        \"domain. The central cube always uses the value for 'theta' in both \"\n        \"x- and y-direction.\"};\n  };\n\n  struct InitialGridPoints {\n    using type =\n        std::variant<size_t, std::array<size_t, 3>,\n                     std::vector<std::array<size_t, 3>>,\n                     std::unordered_map<std::string, std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points. Specify one of: a single number, a \"\n        \"list representing [r, theta, z], or such a list for every block in \"\n        \"the domain. The central cube always uses the value for 'theta' in \"\n        \"both x- and y-direction.\"};\n  };\n\n  struct UseEquiangularMap {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Use equiangular instead of equidistant coordinates.\"};\n  };\n\n  struct RadialPartitioning {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"Radial coordinates of the boundaries splitting the outer shell \"\n        \"between InnerRadius and OuterRadius.\"};\n  };\n\n  struct PartitioningInZ {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"z-coordinates of the boundaries splitting the domain into layers \"\n        \"between LowerZBound and UpperZBound.\"};\n  };\n\n  struct RadialDistribution {\n    using type = std::vector<domain::CoordinateMaps::Distribution>;\n    static constexpr Options::String help = {\n        \"Select the radial distribution of grid points in each cylindrical \"\n        \"shell. The innermost shell must have a 'Linear' distribution because \"\n        \"it changes in circularity. The 'RadialPartitioning' determines the \"\n        \"number of shells.\"};\n    static size_t lower_bound_on_size() { return 1; }\n  };\n\n  struct DistributionInZ {\n    using type = std::vector<domain::CoordinateMaps::Distribution>;\n    static constexpr Options::String help = {\n        \"Select the distribution of grid points along the z-axis in each \"\n        \"layer. The lowermost layer must have a 'Linear' distribution, because \"\n        \"both a 'Logarithmic' and 'Inverse' distribution places its \"\n        \"singularity at 'LowerZBound'. The 'PartitioningInZ' determines the \"\n        \"number of layers.\"};\n    static size_t lower_bound_on_size() { return 1; }\n  };\n\n  struct BoundaryConditions {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions\";\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct LowerZBoundaryCondition {\n    using group = BoundaryConditions;\n    static std::string name() { return \"LowerZ\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to be imposed on the lower base of the \"\n        \"cylinder, i.e. at the `LowerZBound` in the z-direction.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct UpperZBoundaryCondition {\n    using group = BoundaryConditions;\n    static std::string name() { return \"UpperZ\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to be imposed on the upper base of the \"\n        \"cylinder, i.e. at the `UpperZBound` in the z-direction.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct MantleBoundaryCondition {\n    using group = BoundaryConditions;\n    static std::string name() { return \"Mantle\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to be imposed on the mantle of the \"\n        \"cylinder, i.e. at the `OuterRadius` in the radial direction.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  template <typename Metavariables>\n  using options = tmpl::append<\n      tmpl::list<InnerRadius, OuterRadius, LowerZBound, UpperZBound>,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          tmpl::list<\n              LowerZBoundaryCondition<\n                  domain::BoundaryConditions::get_boundary_conditions_base<\n                      typename Metavariables::system>>,\n              UpperZBoundaryCondition<\n                  domain::BoundaryConditions::get_boundary_conditions_base<\n                      typename Metavariables::system>>,\n              MantleBoundaryCondition<\n                  domain::BoundaryConditions::get_boundary_conditions_base<\n                      typename Metavariables::system>>>,\n          tmpl::list<IsPeriodicInZ>>,\n      tmpl::list<InitialRefinement, InitialGridPoints, UseEquiangularMap,\n                 RadialPartitioning, PartitioningInZ, RadialDistribution,\n                 DistributionInZ>>;\n\n  static constexpr Options::String help{\n      \"Creates a right circular Cylinder with a square prism surrounded by \\n\"\n      \"wedges. \\n\"\n      \"The cylinder can be partitioned radially into multiple cylindrical \\n\"\n      \"shells as well as partitioned along the cylinder's height into \\n\"\n      \"multiple layers. Including this partitioning, the number of Blocks is \\n\"\n      \"given by (1 + 4*(1+n_s)) * (1+n_z), where n_s is the \\n\"\n      \"length of RadialPartitioning and n_z the length of \\n\"\n      \"HeightPartitioning. The block numbering starts at the inner square \\n\"\n      \"and goes counter-clockwise, starting with the eastern wedge \\n\"\n      \"(+x-direction) through consecutive shells, then repeats this pattern \\n\"\n      \"for all layers bottom to top. The wedges are named as follows: \\n\"\n      \"  +x-direction: East \\n\"\n      \"  +y-direction: North \\n\"\n      \"  -x-direction: West \\n\"\n      \"  -y-direction: South \\n\"\n      \"The circularity of the wedge changes from 0 to 1 within the first \\n\"\n      \"shell.\\n\"\n      \"Equiangular coordinates give better gridpoint spacings in the angular\\n\"\n      \"direction, while equidistant coordinates give better gridpoint\\n\"\n      \"spacings in the center block.\"};\n\n  Cylinder(\n      double inner_radius, double outer_radius, double lower_z_bound,\n      double upper_z_bound, bool is_periodic_in_z,\n      const typename InitialRefinement::type& initial_refinement,\n      const typename InitialGridPoints::type& initial_number_of_grid_points,\n      bool use_equiangular_map, std::vector<double> radial_partitioning = {},\n      std::vector<double> partitioning_in_z = {},\n      std::vector<domain::CoordinateMaps::Distribution> radial_distribution =\n          {domain::CoordinateMaps::Distribution::Linear},\n      std::vector<domain::CoordinateMaps::Distribution> distribution_in_z =\n          {domain::CoordinateMaps::Distribution::Linear},\n      const Options::Context& context = {});\n\n  Cylinder(\n      double inner_radius, double outer_radius, double lower_z_bound,\n      double upper_z_bound,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          lower_z_boundary_condition,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          upper_z_boundary_condition,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          mantle_boundary_condition,\n      const typename InitialRefinement::type& initial_refinement,\n      const typename InitialGridPoints::type& initial_number_of_grid_points,\n      bool use_equiangular_map, std::vector<double> radial_partitioning = {},\n      std::vector<double> partitioning_in_z = {},\n      std::vector<domain::CoordinateMaps::Distribution> radial_distribution =\n          {domain::CoordinateMaps::Distribution::Linear},\n      std::vector<domain::CoordinateMaps::Distribution> distribution_in_z =\n          {domain::CoordinateMaps::Distribution::Linear},\n      const Options::Context& context = {});\n\n  Cylinder() = default;\n  Cylinder(const Cylinder&) = delete;\n  Cylinder(Cylinder&&) = default;\n  Cylinder& operator=(const Cylinder&) = delete;\n  Cylinder& operator=(Cylinder&&) = default;\n  ~Cylinder() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return block_groups_;\n  }\n\n private:\n  double inner_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double outer_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double lower_z_bound_{std::numeric_limits<double>::signaling_NaN()};\n  double upper_z_bound_{std::numeric_limits<double>::signaling_NaN()};\n  bool is_periodic_in_z_{true};\n  std::vector<std::array<size_t, 3>> initial_refinement_{};\n  std::vector<std::array<size_t, 3>> initial_number_of_grid_points_{};\n  bool use_equiangular_map_{false};\n  std::vector<double> radial_partitioning_{};\n  std::vector<double> partitioning_in_z_{};\n  std::vector<domain::CoordinateMaps::Distribution> radial_distribution_{};\n  std::vector<domain::CoordinateMaps::Distribution> distribution_in_z_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      lower_z_boundary_condition_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      upper_z_boundary_condition_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      mantle_boundary_condition_{};\n  std::vector<std::string> block_names_{};\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_{};\n};\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/CylindricalBinaryCompactObject.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/CylindricalBinaryCompactObject.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/UniformCylindricalEndcap.hpp\"\n#include \"Domain/CoordinateMaps/UniformCylindricalFlatEndcap.hpp\"\n#include \"Domain/CoordinateMaps/UniformCylindricalSide.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/ExpandOverBlocks.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace {\nstd::array<double, 3> rotate_to_z_axis(const std::array<double, 3> input) {\n  return discrete_rotation(\n      OrientationMap<3>{std::array<Direction<3>, 3>{Direction<3>::lower_zeta(),\n                                                    Direction<3>::upper_eta(),\n                                                    Direction<3>::upper_xi()}},\n      input);\n}\nstd::array<double, 3> rotate_from_z_to_x_axis(\n    const std::array<double, 3> input) {\n  return discrete_rotation(\n      OrientationMap<3>{std::array<Direction<3>, 3>{Direction<3>::upper_zeta(),\n                                                    Direction<3>::upper_eta(),\n                                                    Direction<3>::lower_xi()}},\n      input);\n}\nstd::array<double, 3> flip_about_xy_plane(const std::array<double, 3> input) {\n  return std::array<double, 3>{input[0], input[1], -input[2]};\n}\n}  // namespace\n\nnamespace domain::creators {\nCylindricalBinaryCompactObject::CylindricalBinaryCompactObject(\n    std::array<double, 3> center_A, std::array<double, 3> center_B,\n    double radius_A, double radius_B, bool include_inner_sphere_A,\n    bool include_inner_sphere_B, bool include_outer_sphere, double outer_radius,\n    bool use_equiangular_map,\n    const typename InitialRefinement::type& initial_refinement,\n    const typename InitialGridPoints::type& initial_grid_points,\n    std::optional<bco::TimeDependentMapOptions<true>> time_dependent_options,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        inner_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        outer_boundary_condition,\n    const Options::Context& context)\n    : center_A_(rotate_to_z_axis(center_A)),\n      center_B_(rotate_to_z_axis(center_B)),\n      radius_A_(radius_A),\n      radius_B_(radius_B),\n      include_inner_sphere_A_(include_inner_sphere_A),\n      include_inner_sphere_B_(include_inner_sphere_B),\n      include_outer_sphere_(include_outer_sphere),\n      outer_radius_(outer_radius),\n      use_equiangular_map_(use_equiangular_map),\n      inner_boundary_condition_(std::move(inner_boundary_condition)),\n      outer_boundary_condition_(std::move(outer_boundary_condition)),\n      time_dependent_options_(std::move(time_dependent_options)) {\n  if (center_A_[2] <= 0.0) {\n    PARSE_ERROR(\n        context,\n        \"The x-coordinate of the input CenterA is expected to be positive\");\n  }\n  if (center_B_[2] >= 0.0) {\n    PARSE_ERROR(\n        context,\n        \"The x-coordinate of the input CenterB is expected to be negative\");\n  }\n  if (radius_A_ <= 0.0 or radius_B_ <= 0.0) {\n    PARSE_ERROR(context, \"RadiusA and RadiusB are expected to be positive\");\n  }\n  if (radius_A_ < radius_B_) {\n    PARSE_ERROR(context, \"RadiusA should not be smaller than RadiusB\");\n  }\n  if (std::abs(center_A_[2]) > std::abs(center_B_[2])) {\n    PARSE_ERROR(context,\n                \"We expect |x_A| <= |x_B|, for x the x-coordinate of either \"\n                \"CenterA or CenterB.  We should roughly have \"\n                \"RadiusA x_A + RadiusB x_B = 0 (i.e. for BBHs the \"\n                \"center of mass should be about at the origin).\");\n  }\n  // The value 3.0 * (center_A_[2] - center_B_[2]) is what is\n  // chosen in SpEC as the inner radius of the innermost outer sphere.\n  if (outer_radius_ < 3.0 * (center_A_[2] - center_B_[2])) {\n    PARSE_ERROR(context,\n                \"OuterRadius is too small. Please increase it \"\n                \"beyond \"\n                    << 3.0 * (center_A_[2] - center_B_[2]));\n  }\n\n  if ((outer_boundary_condition_ == nullptr) xor\n      (inner_boundary_condition_ == nullptr)) {\n    PARSE_ERROR(context,\n                \"Must specify either both inner and outer boundary conditions \"\n                \"or neither.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(inner_boundary_condition_) or\n      is_periodic(outer_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"Cannot have periodic boundary conditions with a binary domain\");\n  }\n\n  // The choices made below for the quantities xi, z_cutting_plane_,\n  // and xi_min_sphere_e are the ones made in SpEC, and in the\n  // Appendix of https://arxiv.org/abs/1206.3015.  Other choices could\n  // be made that would still result in a reasonable Domain. In\n  // particular, during a SpEC BBH evolution the excision boundaries\n  // can sometimes get too close to z_cutting_plane_, and the\n  // simulation must be halted and regridded with a different choice\n  // of z_cutting_plane_, so it may be possible to choose a different\n  // initial value of z_cutting_plane_ that reduces the number of such\n  // regrids or eliminates them.\n\n  // xi is the quantity in Eq. (A10) of\n  // https://arxiv.org/abs/1206.3015 that represents how close the\n  // cutting plane is to either center.  Unfortunately, there is a\n  // discrepancy between what xi means in the paper and what it is in\n  // the code.  I (Mark) think that this is a typo in the paper,\n  // because otherwise the domain doesn't make sense.  To fix this,\n  // either Eq. (A9) in the paper should have xi -> 1-xi, or Eq. (A10)\n  // should have x_A and x_B swapped.\n  // Here we will use the same definition of xi in Eq. (A10), but we\n  // will swap xi -> 1-xi in Eq. (A9).\n  // Therefore, xi = 0 means that the cutting plane passes through the center of\n  // object B, and xi = 1 means that the cutting plane passes through\n  // the center of object A.  Note that for |x_A| <= |x_B| (as assumed\n  // above), xi is always <= 1/2.\n  constexpr double xi_min = 0.25;\n  // Same as Eq. (A10)\n  const double xi =\n      std::max(xi_min, std::abs(center_A_[2]) /\n                           (std::abs(center_A_[2]) + std::abs(center_B_[2])));\n\n  // Compute cutting plane\n  // This is Eq. (A9) with xi -> 1-xi.\n  z_cutting_plane_ = cut_spheres_offset_factor_ *\n                     ((1.0 - xi) * center_B_[2] + xi * center_A_[2]);\n\n  // outer_radius_A is the outer radius of the inner sphere A, if it exists.\n  // If the inner sphere A does not exist, then outer_radius_A is the same\n  // as radius_A_.\n  // If the inner sphere does exist, the algorithm for computing\n  // outer_radius_A is the same as in SpEC when there is one inner shell.\n  outer_radius_A_ =\n      include_inner_sphere_A_\n          ? radius_A_ +\n                0.5 * (std::abs(z_cutting_plane_ - center_A_[2]) - radius_A_)\n          : radius_A_;\n\n  // outer_radius_B is the outer radius of the inner sphere B, if it exists.\n  // If the inner sphere B does not exist, then outer_radius_B is the same\n  // as radius_B_.\n  // If the inner sphere does exist, the algorithm for computing\n  // outer_radius_B is the same as in SpEC when there is one inner shell.\n  outer_radius_B_ =\n      include_inner_sphere_B_\n          ? radius_B_ +\n                0.5 * (std::abs(z_cutting_plane_ - center_B_[2]) - radius_B_)\n          : radius_B_;\n\n  number_of_blocks_ = 46;\n  if (include_inner_sphere_A) {\n    number_of_blocks_ += 14;\n  }\n  if (include_inner_sphere_B) {\n    number_of_blocks_ += 14;\n  }\n  if (include_outer_sphere) {\n    number_of_blocks_ += 18;\n  }\n\n  // Add SphereE blocks if necessary.  Note that\n  // https://arxiv.org/abs/1206.3015 has a mistake just above\n  // Eq. (A.11) and the same mistake above Eq. (A.20), where it lists\n  // the wrong mass ratio (for BBHs). The correct statement is that if\n  // xi <= 1/3, this means that the mass ratio (for BBH) is large (>=2)\n  // and we should add SphereE blocks.\n  constexpr double xi_min_sphere_e = 1.0 / 3.0;\n  if (xi <= xi_min_sphere_e) {\n    // The following ERROR will be removed in an upcoming PR that\n    // will support higher mass ratios.\n    ERROR(\n        \"We currently only support domains where objects A and B are \"\n        \"approximately the same size, and approximately the same distance from \"\n        \"the origin.  More technically, we support xi > \"\n        << xi_min_sphere_e << \", but the value of xi is \" << xi\n        << \". Support for more general domains will be added in the near \"\n           \"future\");\n  }\n\n  // Create grid anchors in x direction from unrotated input centers\n  grid_anchors_ = bco::create_grid_anchors(center_A, center_B);\n\n  // Create block names and groups\n  auto add_filled_cylinder_name = [this](const std::string& prefix,\n                                         const std::string& group_name) {\n    for (const std::string& where :\n         {\"Center\"s, \"East\"s, \"North\"s, \"West\"s, \"South\"s}) {\n      const std::string name =\n          std::string(prefix).append(\"FilledCylinder\").append(where);\n      block_names_.push_back(name);\n      block_groups_[group_name].insert(name);\n    }\n  };\n  auto add_cylinder_name = [this](const std::string& prefix,\n                                  const std::string& group_name) {\n    for (const std::string& where : {\"East\"s, \"North\"s, \"West\"s, \"South\"s}) {\n      const std::string name =\n          std::string(prefix).append(\"Cylinder\").append(where);\n      block_names_.push_back(name);\n      block_groups_[group_name].insert(name);\n    }\n  };\n\n  // CA Filled Cylinder\n  // 5 blocks: 0 thru 4\n  add_filled_cylinder_name(\"CA\", \"Outer\");\n\n  // CA Cylinder\n  // 4 blocks: 5 thru 8\n  add_cylinder_name(\"CA\", \"Outer\");\n\n  // EA Filled Cylinder\n  // 5 blocks: 9 thru 13\n  add_filled_cylinder_name(\"EA\", \"InnerA\");\n\n  // EA Cylinder\n  // 4 blocks: 14 thru 17\n  add_cylinder_name(\"EA\", \"InnerA\");\n\n  // EB Filled Cylinder\n  // 5 blocks: 18 thru 22\n  add_filled_cylinder_name(\"EB\", \"InnerB\");\n\n  // EB Cylinder\n  // 4 blocks: 23 thru 26\n  add_cylinder_name(\"EB\", \"InnerB\");\n\n  // MA Filled Cylinder\n  // 5 blocks: 27 thru 31\n  add_filled_cylinder_name(\"MA\", \"InnerA\");\n\n  // MB Filled Cylinder\n  // 5 blocks: 32 thru 36\n  add_filled_cylinder_name(\"MB\", \"InnerB\");\n\n  // CB Filled Cylinder\n  // 5 blocks: 37 thru 41\n  add_filled_cylinder_name(\"CB\", \"Outer\");\n\n  // CB Cylinder\n  // 4 blocks: 42 thru 45\n  add_cylinder_name(\"CB\", \"Outer\");\n\n  first_outer_shell_block = 46;\n\n  if (include_inner_sphere_A) {\n    // 5 blocks\n    add_filled_cylinder_name(\"InnerSphereEA\", \"InnerSphereA\");\n    // 5 blocks\n    add_filled_cylinder_name(\"InnerSphereMA\", \"InnerSphereA\");\n    // 4 blocks\n    add_cylinder_name(\"InnerSphereEA\", \"InnerSphereA\");\n    first_outer_shell_block += 14;\n  }\n  if (include_inner_sphere_B) {\n    // 5 blocks\n    add_filled_cylinder_name(\"InnerSphereEB\", \"InnerSphereB\");\n    // 5 blocks\n    add_filled_cylinder_name(\"InnerSphereMB\", \"InnerSphereB\");\n    // 4 blocks\n    add_cylinder_name(\"InnerSphereEB\", \"InnerSphereB\");\n    first_outer_shell_block += 14;\n  }\n  if (include_outer_sphere) {\n    // 5 blocks\n    add_filled_cylinder_name(\"OuterSphereCA\", \"OuterSphere\");\n    // 5 blocks\n    add_filled_cylinder_name(\"OuterSphereCB\", \"OuterSphere\");\n    // 4 blocks\n    add_cylinder_name(\"OuterSphereCA\", \"OuterSphere\");\n    // 4 blocks\n    add_cylinder_name(\"OuterSphereCB\", \"OuterSphere\");\n  }\n\n  // Expand initial refinement over all blocks\n  const ExpandOverBlocks<std::array<size_t, 3>> expand_over_blocks{\n      block_names_, block_groups_};\n  try {\n    initial_refinement_ = std::visit(expand_over_blocks, initial_refinement);\n  } catch (const std::exception& error) {\n    PARSE_ERROR(context, \"Invalid 'InitialRefinement': \" << error.what());\n  }\n  try {\n    initial_grid_points_ = std::visit(expand_over_blocks, initial_grid_points);\n  } catch (const std::exception& error) {\n    PARSE_ERROR(context, \"Invalid 'InitialGridPoints': \" << error.what());\n  }\n\n  // Now we must change the initial refinement and initial grid points\n  // for certain blocks, because the [r, theta, perp] directions do\n  // not always correspond to [xi, eta, zeta].  The values in\n  // initial_refinement_ must correspond to [xi, eta, zeta].\n  //\n  // In particular, for cylinders: [xi, eta, zeta] = [r, theta, perp]\n  // but for filled cylinders: [xi, eta, zeta] = [perp, theta, r].\n\n  auto swap_refinement_and_grid_points_xi_zeta = [this](const size_t block_id) {\n    size_t val = gsl::at(initial_refinement_[block_id], 0);\n    gsl::at(initial_refinement_[block_id], 0) =\n        gsl::at(initial_refinement_[block_id], 2);\n    gsl::at(initial_refinement_[block_id], 2) = val;\n    val = gsl::at(initial_grid_points_[block_id], 0);\n    gsl::at(initial_grid_points_[block_id], 0) =\n        gsl::at(initial_grid_points_[block_id], 2);\n    gsl::at(initial_grid_points_[block_id], 2) = val;\n  };\n\n  // CA Filled Cylinder\n  // 5 blocks: 0 thru 4\n  for (size_t block = 0; block < 5; ++block) {\n    swap_refinement_and_grid_points_xi_zeta(block);\n  }\n\n  // EA Filled Cylinder\n  // 5 blocks: 9 thru 13\n  for (size_t block = 9; block < 14; ++block) {\n    swap_refinement_and_grid_points_xi_zeta(block);\n  }\n\n  // EB Filled Cylinder\n  // 5 blocks: 18 thru 22\n  for (size_t block = 18; block < 23; ++block) {\n    swap_refinement_and_grid_points_xi_zeta(block);\n  }\n\n  // MA Filled Cylinder\n  // 5 blocks: 27 thru 31\n  // MB Filled Cylinder\n  // 5 blocks: 32 thru 36\n  // CB Filled Cylinder\n  // 5 blocks: 37 thru 41\n  for (size_t block = 27; block < 42; ++block) {\n    swap_refinement_and_grid_points_xi_zeta(block);\n  }\n\n  // Now do the filled cylinders for the inner and outer shells,\n  // if they are present.\n  size_t current_block = 46;\n  if (include_inner_sphere_A) {\n    for (size_t block = 0; block < 10; ++block) {\n      swap_refinement_and_grid_points_xi_zeta(current_block++);\n    }\n    current_block += 4;\n  }\n  if (include_inner_sphere_B) {\n    for (size_t block = 0; block < 10; ++block) {\n      swap_refinement_and_grid_points_xi_zeta(current_block++);\n    }\n    current_block += 4;\n  }\n  if (include_outer_sphere) {\n    for (size_t block = 0; block < 10; ++block) {\n      swap_refinement_and_grid_points_xi_zeta(current_block++);\n    }\n  }\n\n  // Build time-dependent maps\n  // The size map, which is applied from the grid to distorted frame, currently\n  // needs to start and stop at certain radii around each excision. If the inner\n  // spheres aren't included, the outer radii would have to be in the middle of\n  // a block. With the inner spheres, the outer radii can be at block\n  // boundaries. The outer sphere must be specified because the time-dependent\n  // maps use piecewise functions for `Expansion` and `Translation`. This means\n  // an inner common radius must be specified for the piecewise bounds.\n  if (time_dependent_options_.has_value() and\n      not(include_inner_sphere_A and include_inner_sphere_B and\n          include_outer_sphere)) {\n    PARSE_ERROR(context,\n                \"To use the CylindricalBBH domain with time-dependent maps, \"\n                \"you must include the inner spheres for both objects and \"\n                \"the outer sphere. \"\n                \"Currently, one or both objects is missing the inner spheres or\"\n                \" the outer sphere is missing.\");\n  }\n\n  if (time_dependent_options_.has_value()) {\n    const double inner_common_radius = 3.0 * (center_A_[2] - center_B_[2]);\n    const auto center_A_aligned = rotate_from_z_to_x_axis(center_A_);\n    const auto center_B_aligned = rotate_from_z_to_x_axis(center_B_);\n    time_dependent_options_->build_maps(\n        std::array{center_A_aligned, center_B_aligned}, std::nullopt,\n        std::nullopt,\n        std::array{z_cutting_plane_,\n                   0.5 * (center_A_aligned[1] + center_B_aligned[1]),\n                   0.5 * (center_A_aligned[2] + center_B_aligned[2])},\n        std::array{radius_A_, outer_radius_A_},\n        std::array{radius_B_, outer_radius_B_}, false, false,\n        inner_common_radius, outer_radius_);\n  }\n}\n\nDomain<3> CylindricalBinaryCompactObject::create_domain() const {\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>\n      coordinate_maps{};\n\n  const OrientationMap<3> rotate_to_x_axis{std::array<Direction<3>, 3>{\n      Direction<3>::upper_zeta(), Direction<3>::upper_eta(),\n      Direction<3>::lower_xi()}};\n\n  const OrientationMap<3> rotate_to_minus_x_axis{std::array<Direction<3>, 3>{\n      Direction<3>::lower_zeta(), Direction<3>::upper_eta(),\n      Direction<3>::upper_xi()}};\n\n  const std::array<double, 3> center_cutting_plane = {0.0, 0.0,\n                                                      z_cutting_plane_};\n\n  // The labels EA, EB, EE, etc are from Figure 20 of\n  // https://arxiv.org/abs/1206.3015\n  //\n  // center_EA and radius_EA are the center and outer-radius of the\n  // cylindered-sphere EA in Figure 20.\n  //\n  // center_EB and radius_EB are the center and outer-radius of the\n  // cylindered-sphere EB in Figure 20.\n  //\n  // radius_MB is eq. A16 or A23 in the paper (depending on whether\n  // the EE spheres exist), and is the radius of the circle where the EB\n  // sphere intersects the cutting plane.\n  const std::array<double, 3> center_EA = {\n      0.0, 0.0, cut_spheres_offset_factor_ * center_A_[2]};\n  const std::array<double, 3> center_EB = {\n      0.0, 0.0, center_B_[2] * cut_spheres_offset_factor_};\n  const double radius_MB =\n      std::abs(cut_spheres_offset_factor_ * center_B_[2] - z_cutting_plane_);\n  const double radius_EA =\n      sqrt(square(center_EA[2] - z_cutting_plane_) + square(radius_MB));\n  const double radius_EB =\n      sqrt(2.0) * std::abs(center_EB[2] - z_cutting_plane_);\n\n  // Construct vector<CoordMap>s that go from logical coordinates to\n  // various blocks making up a unit right cylinder.  These blocks are\n  // either the central square blocks, or the surrounding wedge\n  // blocks. The radii and bounds are what are expected by the\n  // UniformCylindricalEndcap maps, (except cylinder_inner_radius, which\n  // determines the internal block boundaries inside the cylinder, and\n  // which the UniformCylindricalEndcap maps don't care about).\n  const double cylinder_inner_radius = 0.5;\n  const double cylinder_outer_radius = 1.0;\n  const double cylinder_lower_bound_z = -1.0;\n  const double cylinder_upper_bound_z = 1.0;\n  const auto logical_to_cylinder_center_maps =\n      cyl_wedge_coord_map_center_blocks(\n          cylinder_inner_radius, cylinder_lower_bound_z, cylinder_upper_bound_z,\n          use_equiangular_map_);\n  const auto logical_to_cylinder_surrounding_maps =\n      cyl_wedge_coord_map_surrounding_blocks(\n          cylinder_inner_radius, cylinder_outer_radius, cylinder_lower_bound_z,\n          cylinder_upper_bound_z, use_equiangular_map_, 0.0);\n\n  // Lambda that takes a UniformCylindricalEndcap map and a\n  // DiscreteRotation map, composes it with the logical-to-cylinder\n  // maps, and adds it to the list of coordinate maps. Also adds\n  // boundary conditions if requested.\n  auto add_endcap_to_list_of_maps =\n      [&coordinate_maps, &logical_to_cylinder_center_maps,\n       &logical_to_cylinder_surrounding_maps](\n          const CoordinateMaps::UniformCylindricalEndcap& endcap_map,\n          const CoordinateMaps::DiscreteRotation<3>& rotation_map) {\n        auto new_logical_to_cylinder_center_maps =\n            domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                    Frame::Inertial, 3>(\n                logical_to_cylinder_center_maps, endcap_map, rotation_map);\n        coordinate_maps.insert(\n            coordinate_maps.end(),\n            std::make_move_iterator(\n                new_logical_to_cylinder_center_maps.begin()),\n            std::make_move_iterator(new_logical_to_cylinder_center_maps.end()));\n        auto new_logical_to_cylinder_surrounding_maps =\n            domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                    Frame::Inertial, 3>(\n                logical_to_cylinder_surrounding_maps, endcap_map, rotation_map);\n        coordinate_maps.insert(\n            coordinate_maps.end(),\n            std::make_move_iterator(\n                new_logical_to_cylinder_surrounding_maps.begin()),\n            std::make_move_iterator(\n                new_logical_to_cylinder_surrounding_maps.end()));\n      };\n\n  // Lambda that takes a UniformCylindricalFlatEndcap map and a\n  // DiscreteRotation map, composes it with the logical-to-cylinder\n  // maps, and adds it to the list of coordinate maps. Also adds\n  // boundary conditions if requested.\n  auto add_flat_endcap_to_list_of_maps =\n      [&coordinate_maps, &logical_to_cylinder_center_maps,\n       &logical_to_cylinder_surrounding_maps](\n          const CoordinateMaps::UniformCylindricalFlatEndcap& endcap_map,\n          const CoordinateMaps::DiscreteRotation<3>& rotation_map) {\n        auto new_logical_to_cylinder_center_maps =\n            domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                    Frame::Inertial, 3>(\n                logical_to_cylinder_center_maps, endcap_map, rotation_map);\n        coordinate_maps.insert(\n            coordinate_maps.end(),\n            std::make_move_iterator(\n                new_logical_to_cylinder_center_maps.begin()),\n            std::make_move_iterator(new_logical_to_cylinder_center_maps.end()));\n        auto new_logical_to_cylinder_surrounding_maps =\n            domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                    Frame::Inertial, 3>(\n                logical_to_cylinder_surrounding_maps, endcap_map, rotation_map);\n        coordinate_maps.insert(\n            coordinate_maps.end(),\n            std::make_move_iterator(\n                new_logical_to_cylinder_surrounding_maps.begin()),\n            std::make_move_iterator(\n                new_logical_to_cylinder_surrounding_maps.end()));\n      };\n\n  // Construct vector<CoordMap>s that go from logical coordinates to\n  // various blocks making up a right cylindrical shell of inner radius 1,\n  // outer radius 2, and z-extents from -1 to +1.  These blocks are\n  // either the central square blocks, or the surrounding wedge\n  // blocks. The radii and bounds are what are expected by the\n  // UniformCylindricalEndcap maps.\n  const double cylindrical_shell_inner_radius = 1.0;\n  const double cylindrical_shell_outer_radius = 2.0;\n  const double cylindrical_shell_lower_bound_z = -1.0;\n  const double cylindrical_shell_upper_bound_z = 1.0;\n  const auto logical_to_cylindrical_shell_maps =\n      cyl_wedge_coord_map_surrounding_blocks(\n          cylindrical_shell_inner_radius, cylindrical_shell_outer_radius,\n          cylindrical_shell_lower_bound_z, cylindrical_shell_upper_bound_z,\n          use_equiangular_map_, 1.0);\n\n  // Lambda that takes a UniformCylindricalSide map and a DiscreteRotation\n  // map, composes it with the logical-to-cylinder maps, and adds it\n  // to the list of coordinate maps.  Also adds boundary conditions if\n  // requested.\n  auto add_side_to_list_of_maps =\n      [&coordinate_maps, &logical_to_cylindrical_shell_maps](\n          const CoordinateMaps::UniformCylindricalSide& side_map,\n          const CoordinateMaps::DiscreteRotation<3>& rotation_map) {\n        auto new_logical_to_cylindrical_shell_maps =\n            domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                    Frame::Inertial, 3>(\n                logical_to_cylindrical_shell_maps, side_map, rotation_map);\n        coordinate_maps.insert(\n            coordinate_maps.end(),\n            std::make_move_iterator(\n                new_logical_to_cylindrical_shell_maps.begin()),\n            std::make_move_iterator(\n                new_logical_to_cylindrical_shell_maps.end()));\n      };\n\n  // Inner radius of the outer C shell, if it exists.\n  // If it doesn't exist, then it is the same as the outer_radius_.\n  const double inner_radius_C = include_outer_sphere_\n                                    ? 3.0 * (center_A_[2] - center_B_[2])\n                                    : outer_radius_;\n\n  // z_cut_CA_lower is the lower z_plane position for the CA endcap,\n  // defined by https://arxiv.org/abs/1206.3015 in the bulleted list\n  // after Eq. (A.19) EXCEPT that here we use a factor of 1.6 instead of 1.5\n  // to put the plane farther from center_A.\n  const double z_cut_CA_lower =\n      z_cutting_plane_ + 1.6 * (center_EA[2] - z_cutting_plane_);\n  // z_cut_CA_upper is the upper z_plane position for the CA endcap,\n  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the\n  // maps are different).  We choose this plane to make the maps\n  // less extreme.\n  const double z_cut_CA_upper =\n      std::max(0.5 * (z_cut_CA_lower + inner_radius_C), 0.7 * inner_radius_C);\n  // z_cut_EA_upper is the upper z_plane position for the EA endcap,\n  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the\n  // maps are different).  We choose this plane to make the maps\n  // less extreme.\n  const double z_cut_EA_upper = center_A_[2] + 0.7 * outer_radius_A_;\n  // z_cut_EA_lower is the lower z_plane position for the EA endcap,\n  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the\n  // maps are different).  We choose this plane to make the maps\n  // less extreme.\n  const double z_cut_EA_lower = center_A_[2] - 0.7 * outer_radius_A_;\n\n  // CA Filled Cylinder\n  // 5 blocks: 0 thru 4\n  add_endcap_to_list_of_maps(\n      CoordinateMaps::UniformCylindricalEndcap(center_EA, make_array<3>(0.0),\n                                               radius_EA, inner_radius_C,\n                                               z_cut_CA_lower, z_cut_CA_upper),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n\n  // CA Cylinder\n  // 4 blocks: 5 thru 8\n  add_side_to_list_of_maps(\n      CoordinateMaps::UniformCylindricalSide(\n          // codecov complains about the next line being untested.\n          // No idea why, since this entire function is called.\n          // LCOV_EXCL_START\n          center_EA, make_array<3>(0.0), radius_EA, inner_radius_C,\n          // LCOV_EXCL_STOP\n          z_cut_CA_lower, z_cutting_plane_, z_cut_CA_upper, z_cutting_plane_),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n\n  // EA Filled Cylinder\n  // 5 blocks: 9 thru 13\n  add_endcap_to_list_of_maps(\n      CoordinateMaps::UniformCylindricalEndcap(center_A_, center_EA,\n                                               outer_radius_A_, radius_EA,\n                                               z_cut_EA_upper, z_cut_CA_lower),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n\n  // EA Cylinder\n  // 4 blocks: 14 thru 17\n  add_side_to_list_of_maps(\n      // For some reason codecov complains about the next line.\n      CoordinateMaps::UniformCylindricalSide(  // LCOV_EXCL_LINE\n          center_A_, center_EA, outer_radius_A_, radius_EA, z_cut_EA_upper,\n          z_cut_EA_lower, z_cut_CA_lower, z_cutting_plane_),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n\n  // z_cut_CB_lower is the lower z_plane position for the CB endcap,\n  // defined by https://arxiv.org/abs/1206.3015 in the bulleted list\n  // after Eq. (A.19) EXCEPT that here we use a factor of 1.6 instead of 1.5\n  // to put the plane farther from center_B.\n  // Note here that 'lower' means 'farther from z=-infinity'\n  // because we are on the -z side of the cutting plane.\n  const double z_cut_CB_lower =\n      z_cutting_plane_ + 1.6 * (center_EB[2] - z_cutting_plane_);\n  // z_cut_CB_upper is the upper z_plane position for the CB endcap,\n  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the\n  // maps are different).  We choose this plane to make the maps\n  // less extreme. Note here that 'upper' means 'closer to z=-infinity'\n  // because we are on the -z side of the cutting plane.\n  const double z_cut_CB_upper =\n      std::min(0.5 * (z_cut_CB_lower - inner_radius_C), -0.7 * inner_radius_C);\n  // z_cut_EB_upper is the upper z_plane position for the EB endcap,\n  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the\n  // maps are different).  We choose this plane to make the maps\n  // less extreme.  Note here that 'upper' means 'closer to z=-infinity'\n  // because we are on the -z side of the cutting plane.\n  const double z_cut_EB_upper = center_B_[2] - 0.7 * outer_radius_B_;\n  // z_cut_EB_lower is the lower z_plane position for the EB endcap,\n  // which isn't defined in https://arxiv.org/abs/1206.3015 (because the\n  // maps are different).  We choose this plane to make the maps\n  // less extreme. Note here that 'lower' means 'farther from z=-infinity'\n  // because we are on the -z side of the cutting plane.\n  const double z_cut_EB_lower = center_B_[2] + 0.7 * outer_radius_B_;\n\n  // EB Filled Cylinder\n  // 5 blocks: 18 thru 22\n  add_endcap_to_list_of_maps(\n      CoordinateMaps::UniformCylindricalEndcap(\n          flip_about_xy_plane(center_B_), flip_about_xy_plane(center_EB),\n          outer_radius_B_, radius_EB, -z_cut_EB_upper, -z_cut_CB_lower),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n\n  // EB Cylinder\n  // 4 blocks: 23 thru 26\n  add_side_to_list_of_maps(\n      CoordinateMaps::UniformCylindricalSide(\n          flip_about_xy_plane(center_B_), flip_about_xy_plane(center_EB),\n          outer_radius_B_, radius_EB, -z_cut_EB_upper, -z_cut_EB_lower,\n          -z_cut_CB_lower, -z_cutting_plane_),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n\n  // MA Filled Cylinder\n  // 5 blocks: 27 thru 31\n  add_flat_endcap_to_list_of_maps(\n      CoordinateMaps::UniformCylindricalFlatEndcap(\n          flip_about_xy_plane(center_A_),\n          flip_about_xy_plane(center_cutting_plane), outer_radius_A_, radius_MB,\n          -z_cut_EA_lower),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n  // MB Filled Cylinder\n  // 5 blocks: 32 thru 36\n  add_flat_endcap_to_list_of_maps(\n      // For some reason codecov complains about the next line.\n      CoordinateMaps::UniformCylindricalFlatEndcap(  // LCOV_EXCL_LINE\n          center_B_, center_cutting_plane, outer_radius_B_, radius_MB,\n          z_cut_EB_lower),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n\n  // CB Filled Cylinder\n  // 5 blocks: 37 thru 41\n  add_endcap_to_list_of_maps(\n      CoordinateMaps::UniformCylindricalEndcap(\n          flip_about_xy_plane(center_EB), make_array<3>(0.0), radius_EB,\n          inner_radius_C, -z_cut_CB_lower, -z_cut_CB_upper),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n\n  // CB Cylinder\n  // 4 blocks: 42 thru 45\n  add_side_to_list_of_maps(\n      CoordinateMaps::UniformCylindricalSide(\n          flip_about_xy_plane(center_EB), make_array<3>(0.0), radius_EB,\n          inner_radius_C, -z_cut_CB_lower, -z_cutting_plane_, -z_cut_CB_upper,\n          -z_cutting_plane_),\n      CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n\n  if (include_inner_sphere_A_) {\n    const double z_cut_upper = center_A_[2] + 0.7 * radius_A_;\n    const double z_cut_lower = center_A_[2] - 0.7 * radius_A_;\n    // InnerSphereEA Filled Cylinder\n    // 5 blocks\n    add_endcap_to_list_of_maps(\n        // For some reason codecov complains about the next function.\n        // LCOV_EXCL_START\n        CoordinateMaps::UniformCylindricalEndcap(center_A_, center_A_,\n                                                 radius_A_, outer_radius_A_,\n                                                 z_cut_upper, z_cut_EA_upper),\n        // LCOV_EXCL_START\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n    // InnerSphereMA Filled Cylinder\n    // 5 blocks\n    add_endcap_to_list_of_maps(\n        CoordinateMaps::UniformCylindricalEndcap(\n            flip_about_xy_plane(center_A_), flip_about_xy_plane(center_A_),\n            radius_A_, outer_radius_A_, -z_cut_lower, -z_cut_EA_lower),\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n    // InnerSphereEA Cylinder\n    // 4 blocks\n    add_side_to_list_of_maps(\n        // For some reason codecov complains about the next line.\n        CoordinateMaps::UniformCylindricalSide(  // LCOV_EXCL_LINE\n            center_A_, center_A_, radius_A_, outer_radius_A_, z_cut_upper,\n            z_cut_lower, z_cut_EA_upper, z_cut_EA_lower),\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n  }\n  if (include_inner_sphere_B_) {\n    // Note here that 'upper' means 'closer to z=-infinity'\n    // because we are on the -z side of the cutting plane.\n    const double z_cut_upper = center_B_[2] - 0.7 * radius_B_;\n    const double z_cut_lower = center_B_[2] + 0.7 * radius_B_;\n    // InnerSphereEB Filled Cylinder\n    // 5 blocks\n    add_endcap_to_list_of_maps(\n        CoordinateMaps::UniformCylindricalEndcap(\n            flip_about_xy_plane(center_B_), flip_about_xy_plane(center_B_),\n            radius_B_, outer_radius_B_, -z_cut_upper, -z_cut_EB_upper),\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n    // InnerSphereMB Filled Cylinder\n    // 5 blocks\n    add_endcap_to_list_of_maps(\n        // For some reason codecov complains about the next function.\n        // LCOV_EXCL_START\n        CoordinateMaps::UniformCylindricalEndcap(center_B_, center_B_,\n                                                 radius_B_, outer_radius_B_,\n                                                 z_cut_lower, z_cut_EB_lower),\n        // LCOV_EXCL_STOP\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n    // InnerSphereEB Cylinder\n    // 4 blocks\n    add_side_to_list_of_maps(\n        CoordinateMaps::UniformCylindricalSide(\n            flip_about_xy_plane(center_B_), flip_about_xy_plane(center_B_),\n            radius_B_, outer_radius_B_, -z_cut_upper, -z_cut_lower,\n            -z_cut_EB_upper, -z_cut_EB_lower),\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n  }\n  if (include_outer_sphere_) {\n    const double z_cut_CA_outer = 0.7 * outer_radius_;\n    const double z_cut_CB_outer = -0.7 * outer_radius_;\n    // OuterCA Filled Cylinder\n    // 5 blocks\n    add_endcap_to_list_of_maps(\n        CoordinateMaps::UniformCylindricalEndcap(\n            make_array<3>(0.0), make_array<3>(0.0), inner_radius_C,\n            outer_radius_, z_cut_CA_upper, z_cut_CA_outer),\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n    // OuterCB Filled Cylinder\n    // 5 blocks\n    add_endcap_to_list_of_maps(\n        CoordinateMaps::UniformCylindricalEndcap(\n            make_array<3>(0.0), make_array<3>(0.0), inner_radius_C,\n            outer_radius_, -z_cut_CB_upper, -z_cut_CB_outer),\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n    // OuterCA Cylinder\n    // 4 blocks\n    add_side_to_list_of_maps(\n        CoordinateMaps::UniformCylindricalSide(\n            make_array<3>(0.0), make_array<3>(0.0), inner_radius_C,\n            outer_radius_, z_cut_CA_upper, z_cutting_plane_, z_cut_CA_outer,\n            z_cutting_plane_),\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_x_axis));\n    // OuterCB Cylinder\n    // 4 blocks\n    add_side_to_list_of_maps(\n        CoordinateMaps::UniformCylindricalSide(\n            make_array<3>(0.0), make_array<3>(0.0), inner_radius_C,\n            outer_radius_, -z_cut_CB_upper, -z_cutting_plane_, -z_cut_CB_outer,\n            -z_cutting_plane_),\n        CoordinateMaps::DiscreteRotation<3>(rotate_to_minus_x_axis));\n  }\n\n  // Excision spheres\n  std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};\n\n  std::unordered_map<size_t, Direction<3>> abutting_directions_A;\n  size_t first_inner_sphere_block = 46;\n  if (include_inner_sphere_A_) {\n    for (size_t i = 0; i < 10; ++i) {\n      // LCOV_EXCL_START\n      abutting_directions_A.emplace(first_inner_sphere_block + i,\n                                    Direction<3>::lower_zeta());\n      // LCOV_EXCL_STOP\n    }\n    for (size_t i = 0; i < 4; ++i) {\n      // LCOV_EXCL_START\n      abutting_directions_A.emplace(first_inner_sphere_block + 10 + i,\n                                    Direction<3>::lower_xi());\n      // LCOV_EXCL_STOP\n    }\n    // Block numbers of sphereB might depend on whether there is an inner\n    // sphereA layer, so increment here to get that right.\n    first_inner_sphere_block += 14;\n  } else {\n    for (size_t i = 0; i < 5; ++i) {\n      abutting_directions_A.emplace(9 + i, Direction<3>::lower_zeta());\n      abutting_directions_A.emplace(27 + i, Direction<3>::lower_zeta());\n    }\n    for (size_t i = 0; i < 4; ++i) {\n      abutting_directions_A.emplace(14 + i, Direction<3>::lower_xi());\n    }\n  }\n  excision_spheres.emplace(\n      \"ExcisionSphereA\",\n      ExcisionSphere<3>{\n          radius_A_,\n          tnsr::I<double, 3, Frame::Grid>(rotate_from_z_to_x_axis(center_A_)),\n          abutting_directions_A});\n\n  std::unordered_map<size_t, Direction<3>> abutting_directions_B;\n  if (include_inner_sphere_B_) {\n    for (size_t i = 0; i < 10; ++i) {\n      // LCOV_EXCL_START\n      abutting_directions_B.emplace(first_inner_sphere_block + i,\n                                    Direction<3>::lower_zeta());\n      // LCOV_EXCL_STOP\n    }\n    for (size_t i = 0; i < 4; ++i) {\n      // LCOV_EXCL_START\n      abutting_directions_B.emplace(first_inner_sphere_block + 10 + i,\n                                    Direction<3>::lower_xi());\n      // LCOV_EXCL_STOP\n    }\n  } else {\n    for (size_t i = 0; i < 5; ++i) {\n      abutting_directions_B.emplace(18 + i, Direction<3>::lower_zeta());\n      abutting_directions_B.emplace(32 + i, Direction<3>::lower_zeta());\n    }\n    for (size_t i = 0; i < 4; ++i) {\n      abutting_directions_B.emplace(23 + i, Direction<3>::lower_xi());\n    }\n  }\n  excision_spheres.emplace(\n      \"ExcisionSphereB\",\n      ExcisionSphere<3>{\n          radius_B_,\n          tnsr::I<double, 3, Frame::Grid>(rotate_from_z_to_x_axis(center_B_)),\n          abutting_directions_B});\n\n  Domain<3> domain{std::move(coordinate_maps), std::move(excision_spheres),\n                   block_names_, block_groups_};\n\n  if (time_dependent_options_.has_value()) {\n    ASSERT(include_inner_sphere_A_ and include_inner_sphere_B_,\n           \"When using time dependent maps for the CylindricalBBH domain, you \"\n           \"must include both inner spheres.\");\n    // Default initialize everything to nullptr so that we only need to set the\n    // appropriate block maps for the specific frames\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n        grid_to_inertial_block_maps{number_of_blocks_};\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n        grid_to_distorted_block_maps{number_of_blocks_};\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n        distorted_to_inertial_block_maps{number_of_blocks_};\n\n    // The 0th block always exists and will only need an rigid expansion +\n    // rotation + translation map from the grid to inertial frame. No maps to\n    // the distorted frame\n    grid_to_inertial_block_maps[0] =\n        time_dependent_options_\n            ->grid_to_inertial_map<domain::ObjectLabel::None>(false, true);\n\n    // The first block in the outer shell needs the transition expansion +\n    // rotation + translation map from the grid to inertial frame. No maps to\n    // the distorted frame\n    grid_to_inertial_block_maps[first_outer_shell_block] =\n        time_dependent_options_\n            ->grid_to_inertial_map<domain::ObjectLabel::None>(false, false);\n\n    // Inside the excision sphere we add the grid to inertial map from the\n    // outer shell. This allows the center of the excisions/horizons to be\n    // mapped properly to the inertial frame.\n    domain.inject_time_dependent_map_for_excision_sphere(\n        \"ExcisionSphereA\",\n        time_dependent_options_->grid_to_inertial_map<domain::ObjectLabel::A>(\n            true, true, true));\n    domain.inject_time_dependent_map_for_excision_sphere(\n        \"ExcisionSphereB\",\n        time_dependent_options_->grid_to_inertial_map<domain::ObjectLabel::B>(\n            true, true, true));\n\n    // Because we require that both objects have inner shells, object A\n    // corresponds to blocks 46-59 and object B corresponds to blocks 60-73.\n    // If we have extra outer shells, those will have the same maps as block\n    // 0, and will start at block 74. The `true` being passed to the functions\n    // specifies that the size map *should* be included in the distorted\n    // frame.\n    grid_to_inertial_block_maps[46] =\n        time_dependent_options_->grid_to_inertial_map<domain::ObjectLabel::A>(\n            true, true);\n    grid_to_distorted_block_maps[46] =\n        time_dependent_options_->grid_to_distorted_map<domain::ObjectLabel::A>(\n            true);\n    distorted_to_inertial_block_maps[46] =\n        time_dependent_options_\n            ->distorted_to_inertial_map<domain::ObjectLabel::A>(true, true);\n\n    grid_to_inertial_block_maps[60] =\n        time_dependent_options_->grid_to_inertial_map<domain::ObjectLabel::B>(\n            true, true);\n    grid_to_distorted_block_maps[60] =\n        time_dependent_options_->grid_to_distorted_map<domain::ObjectLabel::B>(\n            true);\n    distorted_to_inertial_block_maps[60] =\n        time_dependent_options_\n            ->distorted_to_inertial_map<domain::ObjectLabel::B>(true, true);\n\n    for (size_t block = 1; block < number_of_blocks_; ++block) {\n      if (block == 46 or block == 60 or block == first_outer_shell_block) {\n        continue;  // Already initialized\n      } else if (block > 46 and block < 60) {\n        grid_to_inertial_block_maps[block] =\n            grid_to_inertial_block_maps[46]->get_clone();\n        if (grid_to_distorted_block_maps[46] != nullptr) {\n          grid_to_distorted_block_maps[block] =\n              grid_to_distorted_block_maps[46]->get_clone();\n          distorted_to_inertial_block_maps[block] =\n              distorted_to_inertial_block_maps[46]->get_clone();\n        }\n      } else if (block > 60 and block < 74) {\n        grid_to_inertial_block_maps[block] =\n            grid_to_inertial_block_maps[60]->get_clone();\n        if (grid_to_distorted_block_maps[60] != nullptr) {\n          grid_to_distorted_block_maps[block] =\n              grid_to_distorted_block_maps[60]->get_clone();\n          distorted_to_inertial_block_maps[block] =\n              distorted_to_inertial_block_maps[60]->get_clone();\n        }\n      } else if (block > 74) {\n        grid_to_inertial_block_maps[block] =\n            grid_to_inertial_block_maps[first_outer_shell_block]->get_clone();\n      } else {\n        grid_to_inertial_block_maps[block] =\n            grid_to_inertial_block_maps[0]->get_clone();\n      }\n    }\n\n    for (size_t block = 0; block < number_of_blocks_; ++block) {\n      domain.inject_time_dependent_map_for_block(\n          block, std::move(grid_to_inertial_block_maps[block]),\n          std::move(grid_to_distorted_block_maps[block]),\n          std::move(distorted_to_inertial_block_maps[block]));\n    }\n  }\n\n  return domain;\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nCylindricalBinaryCompactObject::external_boundary_conditions() const {\n  if (outer_boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{number_of_blocks_};\n  for (size_t i = 0; i < 5; ++i) {\n    if (not include_outer_sphere_) {\n      // CA Filled Cylinder\n      boundary_conditions[i][Direction<3>::upper_zeta()] =\n          outer_boundary_condition_->get_clone();\n      // CB Filled Cylinder\n      boundary_conditions[i + 37][Direction<3>::upper_zeta()] =\n          outer_boundary_condition_->get_clone();\n    }\n    if (not include_inner_sphere_A_) {\n      // EA Filled Cylinder\n      boundary_conditions[i + 9][Direction<3>::lower_zeta()] =\n          inner_boundary_condition_->get_clone();\n      // MA Filled Cylinder\n      boundary_conditions[i + 27][Direction<3>::lower_zeta()] =\n          inner_boundary_condition_->get_clone();\n    }\n    if (not include_inner_sphere_B_) {\n      // EB Filled Cylinder\n      boundary_conditions[i + 18][Direction<3>::lower_zeta()] =\n          inner_boundary_condition_->get_clone();\n      // MB Filled Cylinder\n      boundary_conditions[i + 32][Direction<3>::lower_zeta()] =\n          inner_boundary_condition_->get_clone();\n    }\n  }\n  for (size_t i = 0; i < 4; ++i) {\n    if (not include_outer_sphere_) {\n      // CA Cylinder\n      boundary_conditions[i + 5][Direction<3>::upper_xi()] =\n          outer_boundary_condition_->get_clone();\n      // CB Cylinder\n      boundary_conditions[i + 42][Direction<3>::upper_xi()] =\n          outer_boundary_condition_->get_clone();\n    }\n    if (not include_inner_sphere_A_) {\n      // EA Cylinder\n      boundary_conditions[i + 14][Direction<3>::lower_xi()] =\n          inner_boundary_condition_->get_clone();\n    }\n    if (not include_inner_sphere_B_) {\n      // EB Cylinder\n      boundary_conditions[i + 23][Direction<3>::lower_xi()] =\n          inner_boundary_condition_->get_clone();\n    }\n  }\n\n  size_t last_block = 46;\n  if (include_inner_sphere_A_) {\n    for (size_t i = 0; i < 5; ++i) {\n      // InnerSphereEA Filled Cylinder\n      boundary_conditions[last_block + i][Direction<3>::lower_zeta()] =\n          inner_boundary_condition_->get_clone();\n      // InnerSphereMA Filled Cylinder\n      boundary_conditions[last_block + i + 5][Direction<3>::lower_zeta()] =\n          inner_boundary_condition_->get_clone();\n    }\n    for (size_t i = 0; i < 4; ++i) {\n      // InnerSphereEA Cylinder\n      boundary_conditions[last_block + i + 10][Direction<3>::lower_xi()] =\n          inner_boundary_condition_->get_clone();\n    }\n    last_block += 14;\n  }\n  if (include_inner_sphere_B_) {\n    for (size_t i = 0; i < 5; ++i) {\n      // InnerSphereEB Filled Cylinder\n      boundary_conditions[last_block + i][Direction<3>::lower_zeta()] =\n          inner_boundary_condition_->get_clone();\n      // InnerSphereMB Filled Cylinder\n      boundary_conditions[last_block + i + 5][Direction<3>::lower_zeta()] =\n          inner_boundary_condition_->get_clone();\n    }\n    for (size_t i = 0; i < 4; ++i) {\n      // InnerSphereEB Cylinder\n      boundary_conditions[last_block + i + 10][Direction<3>::lower_xi()] =\n          inner_boundary_condition_->get_clone();\n    }\n    last_block += 14;\n  }\n  if (include_outer_sphere_) {\n    for (size_t i = 0; i < 5; ++i) {\n      // OuterCA Filled Cylinder\n      boundary_conditions[last_block + i][Direction<3>::upper_zeta()] =\n          outer_boundary_condition_->get_clone();\n      // OuterCB Filled Cylinder\n      boundary_conditions[last_block + i + 5][Direction<3>::upper_zeta()] =\n          outer_boundary_condition_->get_clone();\n    }\n    for (size_t i = 0; i < 4; ++i) {\n      // OuterCA Cylinder\n      boundary_conditions[last_block + i + 10][Direction<3>::upper_xi()] =\n          outer_boundary_condition_->get_clone();\n      // OuterCB Cylinder\n      boundary_conditions[last_block + i + 14][Direction<3>::upper_xi()] =\n          outer_boundary_condition_->get_clone();\n    }\n  }\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 3>>\nCylindricalBinaryCompactObject::initial_extents() const {\n  return initial_grid_points_;\n}\n\nstd::vector<std::array<size_t, 3>>\nCylindricalBinaryCompactObject::initial_refinement_levels() const {\n  return initial_refinement_;\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nCylindricalBinaryCompactObject::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  return time_dependent_options_.has_value()\n             ? time_dependent_options_->create_functions_of_time(\n                   initial_expiration_times)\n             : std::unordered_map<\n                   std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{};\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/CylindricalBinaryCompactObject.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Interval;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\ntemplate <size_t VolumeDim>\nclass Wedge;\ntemplate <size_t VolumeDim>\nclass DiscreteRotation;\nclass UniformCylindricalEndcap;\nclass UniformCylindricalFlatEndcap;\nclass UniformCylindricalSide;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\n}  // namespace domain\n\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators {\n\n/*!\n * \\ingroup ComputationalDomainGroup\n *\n * \\brief A general domain for two compact objects based on cylinders.\n *\n * Creates a 3D Domain that represents a binary compact object\n * solution.  This domain is described briefly in the Appendix of\n * \\cite Buchman:2012dw, and is illustrated in Figure 20 of that\n * paper.\n *\n * In the code and options below, `ObjectA` and `ObjectB` refer to the\n * two compact objects. In the grid frame, `ObjectA` is located to the\n * right of (i.e. a more positive value of the x-coordinate than)\n * `ObjectB`.  The inner edge of the Blocks surrounding each of\n * `ObjectA` and `ObjectB` is spherical in grid coordinates; the\n * user must specify the center and radius of this surface for both\n * `ObjectA` and `ObjectB`, and the user must specify the outer boundary\n * radius.  The outer boundary is a sphere centered at the origin.\n *\n * This domain offers some grid anchors. See\n * `domain::creators::bco::create_grid_anchors` for which ones are offered.\n *\n * Note that Figure 20 of \\cite Buchman:2012dw illustrates additional\n * spherical shells inside the \"EA\" and \"EB\" blocks, and the caption\n * of Figure 20 indicates that there are additional spherical shells\n * outside the \"CA\" and \"CB\" blocks; `CylindricalBinaryCompactObject`\n * has these extra shells inside \"EA\" only if the option `IncludeInnerSphereA`\n * is true, it has the extra shells inside \"EB\" only if the option\n * `IncludeInnerSphereB` is true, and it has the extra shells outside\n * \"CA\" and \"CB\" only if `IncludeOuterSphere` is true.\n * If the shells are absent, then the \"EA\" and \"EB\"\n * blocks extend to the excision boundaries and the \"CA\" and \"CB\" blocks\n * extend to the outer boundary.\n *\n * The Blocks are named as follows:\n * - Each of CAFilledCylinder, EAFilledCylinder, EBFilledCylinder,\n *   MAFilledCylinder, MBFilledCylinder, and CBFilledCylinder consists\n *   of 5 blocks, named 'Center', 'East', 'North', 'West', and\n *   'South', so an example of a valid block name is\n *   'CAFilledCylinderCenter'.\n * - Each of CACylinder, EACylinder, EBCylinder, and CBCylinder\n *   consists of 4 blocks, named 'East', 'North', 'West', and 'South',\n *   so an example of a valid block name is 'CACylinderEast'.\n * - The Block group called \"Outer\" consists of all the CA and CB blocks. They\n *   all border the outer boundary if `IncludeOuterSphere` is false.\n * - If `IncludeOuterSphere` is true, then there are more blocks named\n *   OuterSphereCAFilledCylinder, OuterSphereCBFilledCylinder,\n *   OuterSphereCACylinder, and OuterSphereCBCylinder.\n *   These are in a Block group called \"OuterSphere\",\n *   and all of these border the outer boundary.\n * - The Block group called \"InnerA\" consists of all the EA, and MA\n *   blocks. They all border the inner boundary \"A\" if\n *   `IncludeInnerSphereA` is false.\n * - If `IncludeInnerSphereA` is true, then there are new blocks\n *   InnerSphereEAFilledCylinder, InnerSphereMAFilledCylinder, and\n *   InnerSphereEACylinder. These are in a Block group called \"InnerSphereA\",\n *   and all of these border the inner excision boundary \"A\".\n * - The Block group called \"InnerB\" consists of all the EB, and MB\n *   blocks. They all border the inner boundary \"B\" if\n *   `IncludeInnerSphereB` is false.\n * - If `IncludeInnerSphereB` is true, then there are new blocks\n *   InnerSphereEBFilledCylinder, InnerSphereMBFilledCylinder, and\n *   InnerSphereEBCylinder. These are in a Block group called \"InnerSphereB\",\n *   and all of these border the inner excision boundary \"B\".\n *\n * If \\f$c_A\\f$ and \\f$c_B\\f$ are the input parameters center_A and\n * center_B, \\f$r_A\\f$ and \\f$r_B\\f$ are the input parameters radius_A and\n * radius_B, and \\f$R\\f$ is the outer boundary radius, we demand the\n * following restrictions on parameters:\n * - \\f$c_A^0>0\\f$; this is a convention to simplify the code.\n * - \\f$c_B^0<0\\f$; this is a convention to simplify the code.\n * - \\f$|c_A^0|\\le|c_B^0|\\f$. We should roughly have \\f$r_A c_A^0 + r_B c_B^0\\f$\n *   close to zero; that is, for BBHs (where \\f$r_A\\f$ is roughly twice the\n *   mass of the heavier object A, and \\f$r_B\\f$ is roughly twice the mass\n *   of the lighter object B) the center of mass should be roughly\n *   at the origin.\n * - \\f$0 < r_B < r_A\\f$\n * - \\f$R \\ge 3(|c_A^0|-|c_B^0|)\\f$; otherwise the blocks will be too compressed\n *   near the outer boundary.\n *\n * All time dependent maps are optional to specify. To include a map, specify\n * its options. Otherwise specify `None` for that map. You can also turn off\n * time dependent maps all together by specifying `None` for the\n * `TimeDependentMaps` option. See\n * `domain::creators::bco::TimeDependentMapOptions`. This class must pass a\n * template parameter of `true` to\n * `domain::creators::bco::TimeDependentMapOptions`.\n */\nclass CylindricalBinaryCompactObject : public DomainCreator<3> {\n public:\n  using maps_list = tmpl::flatten<\n      tmpl::list<domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf3Maps<CoordinateMaps::Interval,\n                                                    CoordinateMaps::Interval,\n                                                    CoordinateMaps::Interval>,\n                     CoordinateMaps::UniformCylindricalEndcap,\n                     CoordinateMaps::DiscreteRotation<3>>,\n                 domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf2Maps<CoordinateMaps::Wedge<2>,\n                                                    CoordinateMaps::Interval>,\n                     CoordinateMaps::UniformCylindricalEndcap,\n                     CoordinateMaps::DiscreteRotation<3>>,\n                 domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf3Maps<CoordinateMaps::Interval,\n                                                    CoordinateMaps::Interval,\n                                                    CoordinateMaps::Interval>,\n                     CoordinateMaps::UniformCylindricalFlatEndcap,\n                     CoordinateMaps::DiscreteRotation<3>>,\n                 domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf2Maps<CoordinateMaps::Wedge<2>,\n                                                    CoordinateMaps::Interval>,\n                     CoordinateMaps::UniformCylindricalFlatEndcap,\n                     CoordinateMaps::DiscreteRotation<3>>,\n                 domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf2Maps<CoordinateMaps::Wedge<2>,\n                                                    CoordinateMaps::Interval>,\n                     CoordinateMaps::UniformCylindricalSide,\n                     CoordinateMaps::DiscreteRotation<3>>,\n                 bco::TimeDependentMapOptions<true>::maps_list>>;\n\n  struct CenterA {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Grid coordinates of center for Object A, which is at x>0.\"};\n  };\n  struct CenterB {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Grid coordinates of center for Object B, which is at x<0.\"};\n  };\n  struct RadiusA {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Grid-coordinate radius of grid boundary around Object A.\"};\n  };\n  struct RadiusB {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Grid-coordinate radius of grid boundary around Object B.\"};\n  };\n  struct IncludeInnerSphereA {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Add an extra spherical layer of Blocks around Object A.\"};\n  };\n  struct IncludeInnerSphereB {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Add an extra spherical layer of Blocks around Object B.\"};\n  };\n  struct IncludeOuterSphere {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Add an extra spherical layer of Blocks inside the outer boundary.\"};\n  };\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Grid-coordinate radius of outer boundary.\"};\n  };\n  struct UseEquiangularMap {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Distribute grid points equiangularly in 2d wedges.\"};\n    static bool suggested_value() { return false; }\n  };\n\n  struct InitialRefinement {\n    using type =\n        std::variant<size_t, std::array<size_t, 3>,\n                     std::vector<std::array<size_t, 3>>,\n                     std::unordered_map<std::string, std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial refinement level. Specify one of: a single number, a list \"\n        \"representing [r, theta, perp], or such a list for every block in the \"\n        \"domain. Here 'r' is the radial direction normal to the inner and \"\n        \"outer boundaries, 'theta' is the periodic direction, and 'perp' is \"\n        \"the third direction.\"};\n  };\n  struct InitialGridPoints {\n    using type =\n        std::variant<size_t, std::array<size_t, 3>,\n                     std::vector<std::array<size_t, 3>>,\n                     std::unordered_map<std::string, std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points. Specify one of: a single number, a \"\n        \"list representing [r, theta, perp], or such a list for every block in \"\n        \"the domain. Here 'r' is the radial direction normal to the inner and \"\n        \"outer boundaries, 'theta' is the periodic direction, and 'perp' is \"\n        \"the third direction.\"};\n  };\n\n  struct BoundaryConditions {\n    static constexpr Options::String help = \"The boundary conditions to apply.\";\n  };\n  template <typename BoundaryConditionsBase>\n  struct InnerBoundaryCondition {\n    static std::string name() { return \"InnerBoundary\"; }\n    static constexpr Options::String help =\n        \"Options for the inner boundary conditions.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n    using group = BoundaryConditions;\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct OuterBoundaryCondition {\n    static std::string name() { return \"OuterBoundary\"; }\n    static constexpr Options::String help =\n        \"Options for the outer boundary conditions.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n    using group = BoundaryConditions;\n  };\n\n  struct TimeDependentMaps {\n    using type = Options::Auto<bco::TimeDependentMapOptions<true>,\n                               Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        bco::TimeDependentMapOptions<true>::help;\n  };\n\n  template <typename Metavariables>\n  using options = tmpl::append<\n      tmpl::list<CenterA, CenterB, RadiusA, RadiusB, IncludeInnerSphereA,\n                 IncludeInnerSphereB, IncludeOuterSphere, OuterRadius,\n                 UseEquiangularMap, InitialRefinement, InitialGridPoints,\n                 TimeDependentMaps>,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          tmpl::list<\n              InnerBoundaryCondition<\n                  domain::BoundaryConditions::get_boundary_conditions_base<\n                      typename Metavariables::system>>,\n              OuterBoundaryCondition<\n                  domain::BoundaryConditions::get_boundary_conditions_base<\n                      typename Metavariables::system>>>,\n          tmpl::list<>>>;\n\n  static constexpr Options::String help{\n      \"The CylindricalBinaryCompactObject domain is a general domain for \"\n      \"two compact objects. The user must provide the (grid-frame) \"\n      \"centers and radii of the spherical inner edge of the grid surrounding \"\n      \"each of the two compact objects A and B.\"};\n\n  CylindricalBinaryCompactObject(\n      std::array<double, 3> center_A, std::array<double, 3> center_B,\n      double radius_A, double radius_B, bool include_inner_sphere_A,\n      bool include_inner_sphere_B, bool include_outer_sphere,\n      double outer_radius, bool use_equiangular_map,\n      const typename InitialRefinement::type& initial_refinement,\n      const typename InitialGridPoints::type& initial_grid_points,\n      std::optional<bco::TimeDependentMapOptions<true>> time_dependent_options =\n          std::nullopt,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          inner_boundary_condition = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          outer_boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  CylindricalBinaryCompactObject() = default;\n  CylindricalBinaryCompactObject(const CylindricalBinaryCompactObject&) =\n      delete;\n  CylindricalBinaryCompactObject(CylindricalBinaryCompactObject&&) = default;\n  CylindricalBinaryCompactObject& operator=(\n      const CylindricalBinaryCompactObject&) = delete;\n  CylindricalBinaryCompactObject& operator=(CylindricalBinaryCompactObject&&) =\n      default;\n  ~CylindricalBinaryCompactObject() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n  grid_anchors() const override {\n    return grid_anchors_;\n  }\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return block_groups_;\n  }\n\n private:\n  // Note that center_A_ and center_B_ are rotated with respect to the\n  // input centers (which are in the grid frame), so that we can\n  // construct the map in a frame where the centers are offset in the\n  // z direction.  At the end, there will be another rotation back to\n  // the grid frame (where the centers are offset in the x direction).\n  std::array<double, 3> center_A_{};\n  std::array<double, 3> center_B_{};\n  double radius_A_{};\n  double radius_B_{};\n  double outer_radius_A_{};\n  double outer_radius_B_{};\n  bool include_inner_sphere_A_{};\n  bool include_inner_sphere_B_{};\n  bool include_outer_sphere_{};\n  double outer_radius_{};\n  bool use_equiangular_map_{false};\n  typename std::vector<std::array<size_t, 3>> initial_refinement_{};\n  typename std::vector<std::array<size_t, 3>> initial_grid_points_{};\n  // cut_spheres_offset_factor_ is eta in Eq. (A.9) of\n  // https://arxiv.org/abs/1206.3015.  cut_spheres_offset_factor_\n  // could be set to unity to simplify the equations.  Here we fix it\n  // to the value 0.99 used in SpEC, so that we reproduce SpEC's\n  // domain decomposition.\n  double cut_spheres_offset_factor_{0.99};\n  // z_cutting_plane_ is x_C in Eq. (A.9) of\n  // https://arxiv.org/abs/1206.3015 (but rotated to the z-axis).\n  double z_cutting_plane_{};\n  size_t number_of_blocks_{};\n  size_t first_outer_shell_block{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      inner_boundary_condition_;\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      outer_boundary_condition_;\n  std::vector<std::string> block_names_{};\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_{};\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n      grid_anchors_{};\n  // FunctionsOfTime options\n  std::optional<bco::TimeDependentMapOptions<true>> time_dependent_options_{};\n};\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Disk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Disk.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Frame {\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n\nnamespace domain::creators {\nDisk::Disk(typename InnerRadius::type inner_radius,\n           typename OuterRadius::type outer_radius,\n           typename InitialRefinement::type initial_refinement,\n           typename InitialGridPoints::type initial_number_of_grid_points,\n           typename UseEquiangularMap::type use_equiangular_map,\n           std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n               boundary_condition,\n           const Options::Context& context)\n    // clang-tidy: trivially copyable\n    : inner_radius_(std::move(inner_radius)),         // NOLINT\n      outer_radius_(std::move(outer_radius)),         // NOLINT\n      initial_refinement_(                            // NOLINT\n          std::move(initial_refinement)),             // NOLINT\n      initial_number_of_grid_points_(                 // NOLINT\n          std::move(initial_number_of_grid_points)),  // NOLINT\n      use_equiangular_map_(use_equiangular_map),      // NOLINT\n      boundary_condition_(std::move(boundary_condition)) {\n  using domain::BoundaryConditions::is_none;\n  if (is_none(boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (boundary_condition_ != nullptr and is_periodic(boundary_condition_)) {\n    PARSE_ERROR(context, \"Cannot have periodic boundary conditions on a disk.\");\n  }\n\n  block_groups_[\"Shell0\"];\n  block_names_.reserve(5);\n  const std::array<std::string, 4> wedge_directions{\"UpperX\", \"UpperY\",\n                                                    \"LowerX\", \"LowerY\"};\n  for (const std::string& name : wedge_directions) {\n    block_names_.emplace_back(name);\n    block_groups_[\"Shell0\"].insert(name);\n  }\n  block_names_.emplace_back(\"CenterSquare\");\n  block_groups_[\"CenterSquare\"].insert(\"CenterSquare\");\n}\n\nDomain<2> Disk::create_domain() const {\n  using Wedge2DMap = CoordinateMaps::Wedge<2>;\n  using Affine = CoordinateMaps::Affine;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using Equiangular = CoordinateMaps::Equiangular;\n  using Equiangular2D =\n      CoordinateMaps::ProductOf2Maps<Equiangular, Equiangular>;\n\n  std::array<size_t, 4> block0_corners{{1, 5, 3, 7}};  //+x wedge\n  std::array<size_t, 4> block1_corners{{3, 7, 2, 6}};  //+y wedge\n  std::array<size_t, 4> block2_corners{{2, 6, 0, 4}};  //-x wedge\n  std::array<size_t, 4> block3_corners{{0, 4, 1, 5}};  //-y wedge\n  std::array<size_t, 4> block4_corners{{0, 1, 2, 3}};  // Center square\n\n  std::vector<std::array<size_t, 4>> corners{block0_corners, block1_corners,\n                                             block2_corners, block3_corners,\n                                             block4_corners};\n\n  auto coord_maps = make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                    Frame::Inertial>(\n      Wedge2DMap{inner_radius_, outer_radius_, 0.0, 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                 use_equiangular_map_},\n      Wedge2DMap{inner_radius_, outer_radius_, 0.0, 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                 use_equiangular_map_},\n      Wedge2DMap{inner_radius_, outer_radius_, 0.0, 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                 use_equiangular_map_},\n      Wedge2DMap{inner_radius_, outer_radius_, 0.0, 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                 use_equiangular_map_});\n\n  if (use_equiangular_map_) {\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            Equiangular2D{\n                Equiangular(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(2.0),\n                            inner_radius_ / sqrt(2.0)),\n                Equiangular(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(2.0),\n                            inner_radius_ / sqrt(2.0))}));\n  } else {\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            Affine2D{Affine(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(2.0),\n                            inner_radius_ / sqrt(2.0)),\n                     Affine(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(2.0),\n                            inner_radius_ / sqrt(2.0))}));\n  }\n\n  std::vector<DirectionMap<\n      2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions_all_blocks{};\n  if (boundary_condition_ != nullptr) {\n    for (size_t block_id = 0; block_id < 4; ++block_id) {\n      DirectionMap<\n          2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>\n          boundary_conditions{};\n      boundary_conditions[Direction<2>::upper_xi()] =\n          boundary_condition_->get_clone();\n      boundary_conditions_all_blocks.push_back(std::move(boundary_conditions));\n    }\n    boundary_conditions_all_blocks.emplace_back();\n  }\n\n  return Domain<2>{std::move(coord_maps), corners,      {}, {},\n                   block_names_,          block_groups_};\n}\n\nstd::vector<DirectionMap<\n    2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nDisk::external_boundary_conditions() const {\n  if (boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{5};\n  for (size_t i = 0; i < 4; ++i) {\n    boundary_conditions[i][Direction<2>::upper_xi()] =\n        boundary_condition_->get_clone();\n  }\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 2>> Disk::initial_extents() const {\n  return {\n      initial_number_of_grid_points_,\n      initial_number_of_grid_points_,\n      initial_number_of_grid_points_,\n      initial_number_of_grid_points_,\n      {{initial_number_of_grid_points_[1], initial_number_of_grid_points_[1]}}};\n}\n\nstd::vector<std::array<size_t, 2>> Disk::initial_refinement_levels() const {\n  return {5, make_array<2>(initial_refinement_)};\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Disk.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\nclass Equiangular;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <size_t Dim>\nclass Wedge;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain {\nnamespace creators {\n/// Create a 2D Domain in the shape of a disk from a square surrounded by four\n/// wedges.\nclass Disk : public DomainCreator<2> {\n public:\n  using maps_list =\n      tmpl::list<domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                                    CoordinateMaps::Affine>>,\n                 domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       CoordinateMaps::ProductOf2Maps<\n                                           CoordinateMaps::Equiangular,\n                                           CoordinateMaps::Equiangular>>,\n                 domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       CoordinateMaps::Wedge<2>>>;\n\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Radius of the circle circumscribing the inner square.\"};\n  };\n\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Radius of the Disk.\"};\n  };\n\n  struct InitialRefinement {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial refinement level in each dimension.\"};\n  };\n\n  struct InitialGridPoints {\n    using type = std::array<size_t, 2>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in [r,theta].\"};\n  };\n\n  struct UseEquiangularMap {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Use equiangular instead of equidistant coordinates.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct BoundaryCondition {\n    static std::string name() { return \"BoundaryCondition\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to impose on all sides.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  using basic_options = tmpl::list<InnerRadius, OuterRadius, InitialRefinement,\n                                   InitialGridPoints, UseEquiangularMap>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          BoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"Creates a 2D Disk with five Blocks.\\n\"\n      \"Only one refinement level for both dimensions is currently supported.\\n\"\n      \"The number of gridpoints in each dimension can be set independently.\\n\"\n      \"The number of gridpoints along the dimensions of the square is equal\\n\"\n      \"to the number of gridpoints along the angular dimension of the wedges.\\n\"\n      \"Equiangular coordinates give better gridpoint spacings in the angular\\n\"\n      \"direction, while equidistant coordinates give better gridpoint\\n\"\n      \"spacings in the center block.\"};\n\n  Disk(typename InnerRadius::type inner_radius,\n       typename OuterRadius::type outer_radius,\n       typename InitialRefinement::type initial_refinement,\n       typename InitialGridPoints::type initial_number_of_grid_points,\n       typename UseEquiangularMap::type use_equiangular_map,\n       std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n           boundary_condition = nullptr,\n       const Options::Context& context = {});\n\n  Disk() = default;\n  Disk(const Disk&) = delete;\n  Disk(Disk&&) = default;\n  Disk& operator=(const Disk&) = delete;\n  Disk& operator=(Disk&&) = default;\n  ~Disk() override = default;\n\n  Domain<2> create_domain() const override;\n\n  std::vector<DirectionMap<\n      2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 2>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 2>> initial_refinement_levels() const override;\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return block_groups_;\n  }\n\n private:\n  typename InnerRadius::type inner_radius_{};\n  typename OuterRadius::type outer_radius_{};\n  typename InitialRefinement::type initial_refinement_{};\n  typename InitialGridPoints::type initial_number_of_grid_points_{};\n  typename UseEquiangularMap::type use_equiangular_map_{false};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      boundary_condition_;\n  std::vector<std::string> block_names_;\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_;\n};\n}  // namespace creators\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/DomainCreator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class DomainCreator.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\n}  // namespace Frame\ntemplate <size_t>\nclass Domain;\nnamespace domain::BoundaryConditions {\nclass BoundaryCondition;\n}\n/// \\endcond\n\nnamespace domain {\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Defines classes that create Domains.\nnamespace creators {}\n}  // namespace domain\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Base class for creating Domains from an option string.\ntemplate <size_t VolumeDim>\nclass DomainCreator {\n public:\n  static constexpr size_t volume_dim = VolumeDim;\n\n  DomainCreator() = default;\n  DomainCreator(const DomainCreator<VolumeDim>&) = delete;\n  DomainCreator(DomainCreator<VolumeDim>&&) = default;\n  DomainCreator<VolumeDim>& operator=(const DomainCreator<VolumeDim>&) = delete;\n  DomainCreator<VolumeDim>& operator=(DomainCreator<VolumeDim>&&) = default;\n  virtual ~DomainCreator() = default;\n\n  virtual Domain<VolumeDim> create_domain() const = 0;\n\n  /// A set of named coordinates in the grid frame, like the center of the\n  /// domain or the positions of specific objects in a domain\n  virtual std::unordered_map<std::string,\n                             tnsr::I<double, VolumeDim, Frame::Grid>>\n  grid_anchors() const {\n    return {};\n  }\n\n  /// The set of external boundary condition for every block in the domain\n  virtual std::vector<DirectionMap<\n      VolumeDim,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const = 0;\n\n  /// A human-readable name for every block, or empty if the domain creator\n  /// doesn't support block names (yet).\n  virtual std::vector<std::string> block_names() const { return {}; }\n\n  /// Labels to refer to groups of blocks. The groups can overlap, and they\n  /// don't have to cover all blocks in the domain. The groups can be used to\n  /// refer to multiple blocks at once when specifying input-file options.\n  virtual std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const {\n    return {};\n  }\n\n  /// Obtain the initial grid extents of the Element%s in each block.\n  virtual std::vector<std::array<size_t, VolumeDim>> initial_extents()\n      const = 0;\n\n  /// Obtain the initial refinement levels of the blocks.\n  virtual std::vector<std::array<size_t, VolumeDim>> initial_refinement_levels()\n      const = 0;\n\n  /// Retrieve the functions of time used for moving meshes.\n  // LCOV_EXCL_START\n  virtual auto functions_of_time(const std::unordered_map<std::string, double>&\n                                     initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> {\n    (void)(initial_expiration_times);\n    return {};\n  }\n  // LCOV_EXCL_STOP\n};\n"
  },
  {
    "path": "src/Domain/Creators/ExpandOverBlocks.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/ExpandOverBlocks.tpp\"\n\n#include <array>\n#include <cstddef>\n\nnamespace domain {\n\ntemplate class ExpandOverBlocks<std::array<size_t, 1>>;\ntemplate class ExpandOverBlocks<std::array<size_t, 2>>;\ntemplate class ExpandOverBlocks<std::array<size_t, 3>>;\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/ExpandOverBlocks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain {\n\nnamespace ExpandOverBlocks_detail {\ntemplate <typename T, typename U, typename = std::void_t<>>\nstruct is_value_type {\n  static constexpr bool value = false;\n};\n\ntemplate <typename T, typename U>\nstruct is_value_type<T, U, std::void_t<typename T::value_type>> {\n  static constexpr bool value = std::is_same_v<U, typename T::value_type>;\n};\n}  // namespace ExpandOverBlocks_detail\n\n/*!\n * \\brief Produce a std::vector<T> over all blocks of the domain\n *\n * This class is useful to option-create values for e.g. the initial refinement\n * level or initial number of grid points for domain creators. It can be used\n * with `std::visit` and a `std::variant` with (a subset of) these types:\n *\n * - `T`: Repeat the given value over all blocks (homogeneous).\n * - `std::vector<T>`: Only check if the size matches the\n *   number of blocks, throwing a `std::length_error` if it doesn't.\n * - `std::unordered_map<std::string, T>`: Map block names, or\n *   names of block groups, to values. The map must cover all blocks once the\n *   groups are expanded. To use this option you must pass the list of block\n *   names and groups to the constructor.\n * - `T::value_type`: Repeat the given value over all blocks and dimensions\n *   (isotropic and homogeneous). Only works if `T` is a `std::array`. For\n *   example, if `T` is `std::array<size_t, 3>`, this will produce a\n *   `std::vector<std::array<size_t, 3>>` with the same value repeated\n *   `num_blocks x 3` times.\n *\n * If `T` is a `std::unique_ptr`, the class will clone the value for each block\n * using `T::get_clone()`.\n *\n * Note that the call-operators `throw` when they encounter errors, such as\n * mismatches in the number of blocks. The exceptions can be used to output\n * user-facing error messages in an option-parsing context.\n *\n * Here's an example for using this class:\n *\n * \\snippet Test_ExpandOverBlocks.cpp expand_over_blocks_example\n *\n * Here's an example using block names and groups:\n *\n * \\snippet Test_ExpandOverBlocks.cpp expand_over_blocks_named_example\n *\n * \\tparam T The type distributed over the domain\n */\ntemplate <typename T>\nstruct ExpandOverBlocks {\n  ExpandOverBlocks(size_t num_blocks);\n  ExpandOverBlocks(\n      std::vector<std::string> block_names,\n      std::unordered_map<std::string, std::unordered_set<std::string>>\n          block_groups = {});\n\n  /// Repeat over all blocks (homogeneous)\n  std::vector<T> operator()(const T& value) const;\n\n  /// Only check if the size matches the number of blocks, throwing a\n  /// `std::length_error` if it doesn't\n  std::vector<T> operator()(const std::vector<T>& value) const;\n\n  /// Map block names, or names of block groups, to values. The map must cover\n  /// all blocks once the groups are expanded. To use this option you must pass\n  /// the list of block names and groups to the constructor. Here's an example:\n  ///\n  /// \\snippet Test_ExpandOverBlocks.cpp expand_over_blocks_named_example\n  std::vector<T> operator()(\n      const std::unordered_map<std::string, T>& value) const;\n\n  /// Repeat over all blocks and dimensions (isotropic and homogeneous)\n  template <\n      typename U,\n      Requires<ExpandOverBlocks_detail::is_value_type<T, U>::value> = nullptr>\n  std::vector<T> operator()(const U& value) const {\n    return {num_blocks_, make_array<std::tuple_size_v<T>>(value)};\n  }\n\n private:\n  size_t num_blocks_;\n  std::vector<std::string> block_names_;\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_;\n};\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/ExpandOverBlocks.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/Creators/ExpandOverBlocks.hpp\"\n\n#include <boost/algorithm/string/join.hpp>\n#include <cstddef>\n#include <exception>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace domain {\n\ntemplate <typename T>\nExpandOverBlocks<T>::ExpandOverBlocks(size_t num_blocks)\n    : num_blocks_(num_blocks) {}\n\ntemplate <typename T>\nExpandOverBlocks<T>::ExpandOverBlocks(\n    std::vector<std::string> block_names,\n    std::unordered_map<std::string, std::unordered_set<std::string>>\n        block_groups)\n    : num_blocks_(block_names.size()),\n      block_names_(std::move(block_names)),\n      block_groups_(std::move(block_groups)) {}\n\ntemplate <typename T>\nstd::vector<T> ExpandOverBlocks<T>::operator()(const T& value) const {\n  if constexpr (tt::is_a_v<std::unique_ptr, T>) {\n    std::vector<T> expanded(num_blocks_);\n    for (size_t i = 0; i < num_blocks_; ++i) {\n      expanded[i] = value->get_clone();\n    }\n    return expanded;\n  } else {\n    return {num_blocks_, value};\n  }\n}\n\ntemplate <typename T>\nstd::vector<T> ExpandOverBlocks<T>::operator()(\n    const std::vector<T>& value) const {\n  if (value.size() != num_blocks_) {\n    throw std::length_error{\"You supplied \" + std::to_string(value.size()) +\n                            \" values, but the domain creator has \" +\n                            std::to_string(num_blocks_) + \" blocks.\"};\n  }\n  if constexpr (tt::is_a_v<std::unique_ptr, T>) {\n    return clone_unique_ptrs(value);\n  } else {\n    return value;\n  }\n}\n\ntemplate <typename T>\nstd::vector<T> ExpandOverBlocks<T>::operator()(\n    const std::unordered_map<std::string, T>& value) const {\n  ASSERT(num_blocks_ == block_names_.size(),\n         \"Construct 'ExpandOverBlocks' with block names to use the \"\n         \"map-over-block-names feature.\");\n  // Expand group names\n  auto value_per_block = [&value]() {\n    if constexpr (tt::is_a_v<std::unique_ptr, T>) {\n      return clone_unique_ptrs(value);\n    } else {\n      return value;\n    }\n  }();\n  for (const auto& [name, block_value] : value) {\n    const auto found_group = block_groups_.find(name);\n    if (found_group != block_groups_.end()) {\n      for (const auto& expanded_name : found_group->second) {\n        if (value_per_block.count(expanded_name) == 0) {\n          value_per_block[expanded_name] = [&local_block_value =\n                                                block_value]() {\n            if constexpr (tt::is_a_v<std::unique_ptr, T>) {\n              return local_block_value->get_clone();\n            } else {\n              return local_block_value;\n            }\n          }();\n        } else {\n          throw std::invalid_argument{\n              \"Duplicate block name '\" + expanded_name +\n              // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n              \"' (expanded from '\" + name + \"').\"};\n        }\n      }\n      value_per_block.erase(name);\n    }\n  }\n  if (value_per_block.size() != num_blocks_) {\n    throw std::length_error{\n        \"You supplied \" + std::to_string(value_per_block.size()) +\n        \" values, but the domain creator has \" + std::to_string(num_blocks_) +\n        \" blocks: \" + boost::algorithm::join(block_names_, \", \")};\n  }\n  std::vector<T> result{};\n  result.reserve(num_blocks_);\n  for (const auto& block_name : block_names_) {\n    const auto found_value = value_per_block.find(block_name);\n    if (found_value != value_per_block.end()) {\n      result.emplace_back(std::move(found_value->second));\n    } else {\n      throw std::out_of_range{\"Value for block '\" + block_name +\n                              \"' is missing. Did you misspell its name?\"};\n    }\n  }\n  return result;\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\nnamespace DomainCreators_detail {\ntemplate <size_t Dim>\nstruct domain_creators;\n}  // namespace DomainCreators_detail\n\ntemplate <size_t Dim>\nusing domain_creators =\n    typename DomainCreators_detail::domain_creators<Dim>::type;\n"
  },
  {
    "path": "src/Domain/Creators/Factory1D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/Factory.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RotatedIntervals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace DomainCreators_detail {\ntemplate <>\nstruct domain_creators<1> {\n  using type = tmpl::list<domain::creators::AlignedLattice<1>,\n                          domain::creators::Interval,\n                          domain::creators::RotatedIntervals>;\n};\n}  // namespace DomainCreators_detail\n"
  },
  {
    "path": "src/Domain/Creators/Factory2D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/AngularDisk.hpp\"\n#include \"Domain/Creators/Disk.hpp\"\n#include \"Domain/Creators/Factory.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RotatedRectangles.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace DomainCreators_detail {\ntemplate <>\nstruct domain_creators<2> {\n  using type = tmpl::list<domain::creators::AlignedLattice<2>,\n                          domain::creators::AngularDisk, domain::creators::Disk,\n                          domain::creators::Rectangle,\n                          domain::creators::RotatedRectangles>;\n};\n}  // namespace DomainCreators_detail\n"
  },
  {
    "path": "src/Domain/Creators/Factory3D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/AngularCylinder.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/CartoonCylinder.hpp\"\n#include \"Domain/Creators/CartoonSphere1D.hpp\"\n#include \"Domain/Creators/CartoonSphere2D.hpp\"\n#include \"Domain/Creators/Cylinder.hpp\"\n#include \"Domain/Creators/CylindricalBinaryCompactObject.hpp\"\n#include \"Domain/Creators/Factory.hpp\"\n#include \"Domain/Creators/FrustalCloak.hpp\"\n#include \"Domain/Creators/NonconformingSphericalShells.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RotatedBricks.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/SphericalShells.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace DomainCreators_detail {\ntemplate <>\nstruct domain_creators<3> {\n  using type =\n      tmpl::list<domain::creators::AlignedLattice<3>,\n                 domain::creators::AngularCylinder,\n                 domain::creators::BinaryCompactObject<false>,\n                 domain::creators::Brick, domain::creators::CartoonCylinder,\n                 domain::creators::CartoonSphere1D,\n                 domain::creators::CartoonSphere2D, domain::creators::Cylinder,\n                 domain::creators::CylindricalBinaryCompactObject,\n                 domain::creators::FrustalCloak,\n                 domain::creators::NonconformingSphericalShells,\n                 domain::creators::RotatedBricks, domain::creators::Sphere,\n                 domain::creators::SphericalShells>;\n};\n}  // namespace DomainCreators_detail\n"
  },
  {
    "path": "src/Domain/Creators/FrustalCloak.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/FrustalCloak.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Frame {\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\nnamespace domain {\ntemplate <typename, typename, size_t>\nclass CoordinateMapBase;\n}  // namespace domain\n\nnamespace domain::creators {\nFrustalCloak::FrustalCloak(\n    typename InitialRefinement::type initial_refinement_level,\n    typename InitialGridPoints::type initial_number_of_grid_points,\n    typename UseEquiangularMap::type use_equiangular_map,\n    typename ProjectionFactor::type projection_factor,\n    typename LengthInnerCube::type length_inner_cube,\n    typename LengthOuterCube::type length_outer_cube,\n    typename OriginPreimage::type origin_preimage,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        boundary_condition,\n    const Options::Context& context)\n    // clang-tidy: trivially copyable\n    : initial_refinement_level_(                      // NOLINT\n          std::move(initial_refinement_level)),       // NOLINT\n      initial_number_of_grid_points_(                 // NOLINT\n          std::move(initial_number_of_grid_points)),  // NOLINT\n      use_equiangular_map_(use_equiangular_map),      // NOLINT\n      projection_factor_(projection_factor),          // NOLINT\n      length_inner_cube_(length_inner_cube),          // NOLINT\n      length_outer_cube_(length_outer_cube),          // NOLINT\n      origin_preimage_(origin_preimage),              // NOLINT\n      boundary_condition_(std::move(boundary_condition)) {\n  using domain::BoundaryConditions::is_none;\n  if (is_none(boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (boundary_condition_ != nullptr and is_periodic(boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"Cannot have periodic boundary conditions with a frustal cloak\");\n  }\n}\n\nDomain<3> FrustalCloak::create_domain() const {\n  std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>\n      coord_maps = domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                           Frame::Inertial, 3>(\n          frustum_coordinate_maps(\n              length_inner_cube_, length_outer_cube_, use_equiangular_map_,\n              use_equiangular_map_, origin_preimage_,\n              domain::CoordinateMaps::Distribution::Projective,\n              projection_factor_));\n  return Domain<3>{std::move(coord_maps),\n                   corners_for_biradially_layered_domains(\n                       0, 1, false, false, {{1, 2, 3, 4, 5, 6, 7, 8}})};\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nFrustalCloak::external_boundary_conditions() const {\n  if (boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{10};\n  for (size_t i = 0; i < 10; ++i) {\n    boundary_conditions[i][Direction<3>::lower_zeta()] =\n        boundary_condition_->get_clone();\n    boundary_conditions[i][Direction<3>::upper_zeta()] =\n        boundary_condition_->get_clone();\n  }\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 3>> FrustalCloak::initial_extents() const {\n  return {\n      10,\n      {{initial_number_of_grid_points_[1], initial_number_of_grid_points_[1],\n        initial_number_of_grid_points_[0]}}};\n}\n\nstd::vector<std::array<size_t, 3>> FrustalCloak::initial_refinement_levels()\n    const {\n  return {10, make_array<3>(initial_refinement_level_)};\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/FrustalCloak.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class FrustalCloak.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Frustum;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain {\nnamespace creators {\n/*!\n * \\brief Create a 3D cubical domain with two equal-sized abutting excised cubes\n * in the center. This is done by combining ten frusta.\n *\n * \\image html FrustalCloak.png \"A slice through the frustal cloak.\"\n */\nclass FrustalCloak : public DomainCreator<3> {\n public:\n  using maps_list =\n      tmpl::list<domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       domain::CoordinateMaps::Frustum>>;\n\n  struct InitialRefinement {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial refinement level in each dimension.\"};\n  };\n\n  struct InitialGridPoints {\n    using type = std::array<size_t, 2>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in [r,angular].\"};\n  };\n\n  struct UseEquiangularMap {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Use equiangular instead of equidistant coordinates.\"};\n  };\n\n  struct ProjectionFactor {\n    using type = double;\n    static constexpr Options::String help = {\"Grid compression factor.\"};\n  };\n\n  struct LengthInnerCube {\n    using type = double;\n    static constexpr Options::String help = {\"Side length of each inner cube.\"};\n    static constexpr type lower_bound() { return 0.0; }\n  };\n\n  struct LengthOuterCube {\n    using type = double;\n    static constexpr Options::String help = {\"Side length of the outer cube.\"};\n    static constexpr type lower_bound() { return 0.0; }\n  };\n\n  struct OriginPreimage {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\"The origin preimage in [x,y,z].\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct BoundaryCondition {\n    static std::string name() { return \"BoundaryCondition\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to impose on all sides.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  using basic_options =\n      tmpl::list<InitialRefinement, InitialGridPoints, UseEquiangularMap,\n                 ProjectionFactor, LengthInnerCube, LengthOuterCube,\n                 OriginPreimage>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          BoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"Creates a cubical domain with two equal-sized abutting excised cubes\\n\"\n      \"in the center. This is done by combining ten frusta. The parameter\\n\"\n      \"`UseEquiangularMap` can be used to apply a tangent mapping to the xi\\n\"\n      \"and eta logical coordinates of each frustum, while the parameter\\n\"\n      \"`ProjectionFactor` can be used to apply a projective map to the zeta\\n\"\n      \"logical coordinate of each frustum. Increasing the\\n\"\n      \"`ProjectionFactor` value can give better gridpoint spacings in the\\n\"\n      \"z direction. The user also specifies values for `LengthInnerCube` and\\n\"\n      \"`LengthOuterCube`. This will create a cubical Domain of side\"\n      \"length `LengthOuterCube` with the center excised. The size of the\\n\"\n      \"excised region is determined by the value set for `LengthInnerCube`.\\n\"\n      \"`OriginPreimage` moves the blocks such that the origin preimage is\\n\"\n      \"mapped to the origin. Note that the abutting excised cubes share a\\n\"\n      \"face in the x-direction. This Domain is primarily for testing the\\n\"\n      \"frustal cloak in the BinaryCompactObject Domain.\"};\n\n  FrustalCloak(typename InitialRefinement::type initial_refinement_level,\n               typename InitialGridPoints::type initial_number_of_grid_points,\n               typename UseEquiangularMap::type use_equiangular_map = false,\n               typename ProjectionFactor::type projection_factor = 1.0,\n               typename LengthInnerCube::type length_inner_cube = 0.0,\n               typename LengthOuterCube::type length_outer_cube = 0.0,\n               typename OriginPreimage::type origin_preimage = {{0.0, 0.0,\n                                                                 0.0}},\n               std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n                   boundary_condition = nullptr,\n               const Options::Context& context = {});\n\n  FrustalCloak() = default;\n  FrustalCloak(const FrustalCloak&) = delete;\n  FrustalCloak(FrustalCloak&&) = default;\n  FrustalCloak& operator=(const FrustalCloak&) = delete;\n  FrustalCloak& operator=(FrustalCloak&&) = default;\n  ~FrustalCloak() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n private:\n  typename InitialRefinement::type initial_refinement_level_{};\n  typename InitialGridPoints::type initial_number_of_grid_points_{};\n  typename UseEquiangularMap::type use_equiangular_map_ = false;\n  typename ProjectionFactor::type projection_factor_{};\n  typename LengthInnerCube::type length_inner_cube_{0.0};\n  typename LengthOuterCube::type length_outer_cube_{0.0};\n  typename OriginPreimage::type origin_preimage_{{0.0, 0.0, 0.0}};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      boundary_condition_;\n};\n}  // namespace creators\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/NonconformingSphericalShells.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/NonconformingSphericalShells.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain::creators {\n\nNonconformingSphericalShells::NonconformingSphericalShells(\n    const double inner_radius, const double interface_radius,\n    const double outer_radius, const size_t initial_radial_refinement,\n    const size_t initial_angular_refinement,\n    const size_t initial_number_of_radial_grid_points,\n    const size_t initial_spherical_harmonic_l,\n    const size_t initial_number_of_angular_grid_points_of_wedges,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        inner_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        outer_boundary_condition,\n    const Options::Context& context)\n    : inner_radius_(inner_radius),\n      interface_radius_(interface_radius),\n      outer_radius_(outer_radius),\n      initial_radial_refinement_(initial_radial_refinement),\n      initial_angular_refinement_(initial_angular_refinement),\n      initial_number_of_radial_grid_points_(\n          initial_number_of_radial_grid_points),\n      initial_spherical_harmonic_l_(initial_spherical_harmonic_l),\n      initial_number_of_angular_grid_points_of_wedges_(\n          initial_number_of_angular_grid_points_of_wedges),\n      initial_refinement_levels_{\n          7,\n          {{initial_angular_refinement_, initial_angular_refinement_,\n            initial_radial_refinement_}}},\n      initial_number_of_grid_points_{\n          7,\n          {{initial_number_of_angular_grid_points_of_wedges_,\n            initial_number_of_angular_grid_points_of_wedges_,\n            initial_number_of_radial_grid_points_}}},\n      inner_boundary_condition_(std::move(inner_boundary_condition)),\n      outer_boundary_condition_(std::move(outer_boundary_condition)),\n      grid_anchors_{{{\"Center\", tnsr::I<double, 3, Frame::Grid>{\n                                    std::array{0.0, 0.0, 0.0}}}}} {\n  if (inner_radius_ > interface_radius_) {\n    PARSE_ERROR(context,\n                \"Inner radius must be smaller than interface radius, but inner \"\n                \"radius is \" +\n                    std::to_string(inner_radius_) +\n                    \" and interface radius is \" +\n                    std::to_string(interface_radius_) + \".\");\n  }\n\n  if (interface_radius_ > outer_radius_) {\n    PARSE_ERROR(\n        context,\n        \"Interface radius must be smaller than outer radius, but interface \"\n        \"radius is \" +\n            std::to_string(interface_radius_) + \" and outer radius is \" +\n            std::to_string(outer_radius_) + \".\");\n  }\n\n  // Validate boundary conditions\n  using domain::BoundaryConditions::is_none;\n  if (is_none(inner_boundary_condition_) or\n      is_none(outer_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(inner_boundary_condition_) or\n      is_periodic(outer_boundary_condition_)) {\n    PARSE_ERROR(context,\n                \"Cannot have periodic boundary conditions with \"\n                \"NonconformingSphericalShells\");\n  }\n  // Validate consistency of inner and outer boundary condition\n  if ((inner_boundary_condition_ == nullptr) !=\n      (outer_boundary_condition_ == nullptr)) {\n    PARSE_ERROR(context,\n                \"Must specify either both inner and outer boundary conditions \"\n                \"or neither.\");\n  }\n\n  // correct for outer spherical shell\n  initial_number_of_grid_points_[6] = {{initial_number_of_radial_grid_points_,\n                                        initial_spherical_harmonic_l_ + 1,\n                                        2 * initial_spherical_harmonic_l_ + 1}};\n  initial_refinement_levels_[6] = {{initial_radial_refinement_, 0_st, 0_st}};\n}\n\nDomain<3> NonconformingSphericalShells::create_domain() const {\n  std::vector<Block<3>> blocks;\n  blocks.reserve(7);\n  const std::vector<std::array<size_t, 8>> corners_of_wedges =\n      corners_for_radially_layered_domains(1, false);\n  std::vector<DirectionMap<3, BlockNeighbors<3>>> neighbors_of_wedges{};\n  set_internal_boundaries<3>(make_not_null(&neighbors_of_wedges),\n                             corners_of_wedges);\n  const OrientationMap<3> shell_to_wedge{\n      {{Direction<3>::upper_zeta(), Direction<3>::self(),\n        Direction<3>::self()}}};\n  DirectionMap<3, BlockNeighbors<3>> neighbors_of_shell{};\n  neighbors_of_shell.emplace(std::pair{Direction<3>::lower_xi(),\n                                       BlockNeighbors<3>{{0, 1, 2, 3, 4, 5},\n                                                         {{0, shell_to_wedge},\n                                                          {1, shell_to_wedge},\n                                                          {2, shell_to_wedge},\n                                                          {3, shell_to_wedge},\n                                                          {4, shell_to_wedge},\n                                                          {5, shell_to_wedge}},\n                                                         false}});\n  for (size_t i = 0; i < 6; ++i) {\n    neighbors_of_wedges[i].emplace(std::pair{\n        Direction<3>::upper_zeta(),\n        BlockNeighbors<3>{{6}, {{6, shell_to_wedge.inverse_map()}}, false}});\n  }\n\n  auto wedge_coord_maps =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 3>(\n          sph_wedge_coordinate_maps(inner_radius_, interface_radius_, 1.0, 1.0,\n                                    true));\n  auto sphere_map =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                         CoordinateMaps::Identity<2>>{\n              CoordinateMaps::Affine{-1.0, 1.0, interface_radius_,\n                                     outer_radius_},\n              CoordinateMaps::Identity<2>{}},\n          CoordinateMaps::SphericalToCartesianPfaffian{});\n  for (size_t i = 0; i < 6; ++i) {\n    blocks.emplace_back(\n        std::move(wedge_coord_maps[i]), i, std::move(neighbors_of_wedges[i]),\n        \"Wedge\" + std::to_string(i), domain::topologies::hypercube<3>);\n  }\n  blocks.emplace_back(std::move(sphere_map), 6_st,\n                      std::move(neighbors_of_shell), \"Shell\",\n                      domain::topologies::spherical_shell);\n  return Domain(std::move(blocks));\n}\n\nstd::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\nNonconformingSphericalShells::grid_anchors() const {\n  return grid_anchors_;\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nNonconformingSphericalShells::external_boundary_conditions() const {\n  if (outer_boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{7};\n  for (size_t i = 0; i < 6; ++i) {\n    boundary_conditions[i][Direction<3>::lower_zeta()] =\n        inner_boundary_condition_->get_clone();\n  }\n  boundary_conditions[6][Direction<3>::upper_xi()] =\n      outer_boundary_condition_->get_clone();\n  return boundary_conditions;\n}\n\nstd::vector<std::string> NonconformingSphericalShells::block_names() const {\n  std::vector<std::string> names{};\n  names.reserve(7);\n  for (size_t i = 0; i < 6; ++i) {\n    names.emplace_back(\"Wedge\" + std::to_string(i));\n  }\n  names.emplace_back(\"Shell\");\n  return names;\n}\n\nstd::vector<std::array<size_t, 3>>\nNonconformingSphericalShells::initial_extents() const {\n  return initial_number_of_grid_points_;\n}\n\nstd::vector<std::array<size_t, 3>>\nNonconformingSphericalShells::initial_refinement_levels() const {\n  return initial_refinement_levels_;\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/NonconformingSphericalShells.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Domain;\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\ntemplate <size_t Dim>\nclass Identity;\nclass Interval;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\nclass SphericalToCartesianPfaffian;\ntemplate <size_t Dim>\nclass Wedge;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators {\n/*!\n * \\brief A set of non-conforming concentric spherical shells\n *\n * \\details The inner spherical shells are decomposed into six wedges\n * surrounding an excised interior region.  The outer spherical shells will use\n * a spherical harmonic basis which cannot be used with subcell.\n *\n * This domain creator offers one grid anchor \"Center\" at the origin.\n *\n */\nclass NonconformingSphericalShells : public DomainCreator<3> {\n public:\n  using maps_list =\n      tmpl::list<domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     domain::CoordinateMaps::ProductOf2Maps<\n                         domain::CoordinateMaps::Affine,\n                         domain::CoordinateMaps::Identity<2>>,\n                     domain::CoordinateMaps::SphericalToCartesianPfaffian>,\n                 domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       CoordinateMaps::Wedge<3>>>;\n\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Inner radius of the inner wedges.\"};\n  };\n\n  struct InterfaceRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Radius of interface between the inner wedges and the outer spherical \"\n        \"shells.\"};\n  };\n\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Outer radius of the outer spherical shell.\"};\n  };\n\n  struct InitialRadialRefinement {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial radial refinement level for both the inner wedges and the \"\n        \"outer spherical shells.\"};\n  };\n\n  struct InitialAngularRefinementOfWedges {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial angular refinement levels of inner wedges.\"};\n  };\n\n  struct InitialNumberOfRadialGridPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial number of radial grid points for both the inner wedges and \"\n        \"the outer spherical shells.\"};\n  };\n\n  struct InitialSphericalHarmonicL {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial spherical harmonic resolution specified as the highest \"\n        \"spherical harmonic represented on the grid.\"};\n  };\n\n  struct InitialNumberOfAngularGridPointsOfWedges {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial angular refinement levels of inner wedges.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct InnerBoundaryCondition {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions at the inner radius.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct OuterBoundaryCondition {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions at the outer radius.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  using basic_options =\n      tmpl::list<InnerRadius, InterfaceRadius, OuterRadius,\n                 InitialRadialRefinement, InitialAngularRefinementOfWedges,\n                 InitialNumberOfRadialGridPoints, InitialSphericalHarmonicL,\n                 InitialNumberOfAngularGridPointsOfWedges>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          InnerBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>,\n          OuterBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"A set of concentric spherical shells centered at the origin.\"};\n\n  NonconformingSphericalShells(\n      double inner_radius, double interface_radius, double outer_radius,\n      size_t initial_radial_refinement,\n      size_t initial_angular_refinement,\n      size_t initial_number_of_radial_grid_points,\n      size_t initial_spherical_harmonic_l,\n      size_t initial_number_of_angular_grid_points_of_wedges,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          inner_boundary_condition = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          outer_boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  NonconformingSphericalShells() = default;\n  NonconformingSphericalShells(const NonconformingSphericalShells&) = delete;\n  NonconformingSphericalShells(NonconformingSphericalShells&&) = default;\n  NonconformingSphericalShells& operator=(const NonconformingSphericalShells&) =\n      delete;\n  NonconformingSphericalShells& operator=(NonconformingSphericalShells&&) =\n      default;\n  ~NonconformingSphericalShells() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n  grid_anchors() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::string> block_names() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n private:\n  double inner_radius_{};\n  double interface_radius_{};\n  double outer_radius_{};\n  size_t initial_radial_refinement_{};\n  size_t initial_angular_refinement_{};\n  size_t initial_number_of_radial_grid_points_{};\n  size_t initial_spherical_harmonic_l_{};\n  size_t initial_number_of_angular_grid_points_of_wedges_{};\n  std::vector<std::array<size_t, 3>> initial_refinement_levels_{};\n  std::vector<std::array<size_t, 3>> initial_number_of_grid_points_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      inner_boundary_condition_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      outer_boundary_condition_{};\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n      grid_anchors_{};\n};\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/OptionTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"Options/String.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass DomainCreator;\n/// \\endcond\n\nnamespace domain {\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The input file tag for the DomainCreator to use\ntemplate <size_t Dim>\nstruct DomainCreator {\n  using type = std::unique_ptr<::DomainCreator<Dim>>;\n  static constexpr Options::String help = {\"The domain to create initially\"};\n};\n}  // namespace OptionTags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/Python/BinaryCompactObject.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Python/BinaryCompactObject.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::py_bindings {\nvoid bind_binary_compact_object(py::module& m) {\n  py::class_<domain::creators::BinaryCompactObject<false>, DomainCreator<3>>(\n      m, \"BinaryCompactObject\")\n      .def(py::init(\n               [](const double inner_radius_object_a,\n                  const double outer_radius_object_a, const double x_coord_a,\n                  const bool excise_a, const bool use_logarithmic_map_a,\n                  const double inner_radius_object_b,\n                  const double outer_radius_object_b, const double x_coord_b,\n                  const bool excise_b, const bool use_logarithmic_map_b,\n                  const std::array<double, 2> center_of_mass_offset,\n                  const double envelope_radius, const double outer_radius,\n                  const double cube_scale, const size_t initial_refinement,\n                  const size_t initial_num_points,\n                  const bool use_equiangular_map,\n                  const std::vector<double>& radial_partitioning_outer_shell,\n                  const double opening_angle_in_degrees,\n                  std::optional<bco::TimeDependentMapOptions<false>>\n                      time_dependent_options) {\n                 return domain::creators::BinaryCompactObject<false>{\n                     domain::creators::BinaryCompactObject<false>::Object{\n                         inner_radius_object_a, outer_radius_object_a,\n                         x_coord_a, excise_a, use_logarithmic_map_a},\n                     domain::creators::BinaryCompactObject<false>::Object{\n                         inner_radius_object_b, outer_radius_object_b,\n                         x_coord_b, excise_b, use_logarithmic_map_b},\n                     center_of_mass_offset,\n                     envelope_radius,\n                     outer_radius,\n                     cube_scale,\n                     initial_refinement,\n                     initial_num_points,\n                     use_equiangular_map,\n                     domain::CoordinateMaps::Distribution::Logarithmic,\n                     radial_partitioning_outer_shell,\n                     domain::CoordinateMaps::Distribution::Linear,\n                     opening_angle_in_degrees,\n                     std::move(time_dependent_options)};\n               }),\n           py::arg(\"inner_radius_a\"), py::arg(\"outer_radius_a\"),\n           py::arg(\"x_coord_a\"), py::arg(\"excise_a\"),\n           py::arg(\"use_logarithmic_map_a\"), py::arg(\"inner_radius_b\"),\n           py::arg(\"outer_radius_b\"), py::arg(\"x_coord_b\"), py::arg(\"excise_b\"),\n           py::arg(\"use_logarithmic_map_b\"), py::arg(\"center_of_mass_offset\"),\n           py::arg(\"envelope_radius\"), py::arg(\"outer_radius\"),\n           py::arg(\"cube_scale\"), py::arg(\"initial_refinement\"),\n           py::arg(\"initial_number_of_grid_points\"),\n           py::arg(\"use_equiangular_map\"),\n           py::arg(\"radial_partitioning_outer_shell\") = std::vector<double>{},\n           py::arg(\"opening_angle_in_degrees\") = 120,\n           py::arg(\"time_dependent_options\"));\n}\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/BinaryCompactObject.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\n// For now this does not support an outer-boundary condition or context.\nvoid bind_binary_compact_object(pybind11::module& m);\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"Domain/Creators/Python/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/Python/Cylinder.hpp\"\n#include \"Domain/Creators/Python/DomainCreator.hpp\"\n#include \"Domain/Creators/Python/Rectilinear.hpp\"\n#include \"Domain/Creators/Python/Sphere.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators {\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.Domain\");\n  py::module_::import(\"spectre.Domain.CoordinateMaps\");\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  // Order is important: The base class `DomainCreator` needs to have its\n  // bindings set up before the derived classes\n  py_bindings::bind_domain_creator(m);\n  py_bindings::bind_rectilinear(m);\n  py_bindings::bind_cylinder(m);\n  py_bindings::bind_sphere(m);\n  py_bindings::bind_binary_compact_object(m);\n}\n\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyDomainCreators\")\n\nspectre_python_add_module(\n  Creators\n  LIBRARY_NAME ${LIBRARY}\n  MODULE_PATH \"Domain\"\n  SOURCES\n  BinaryCompactObject.cpp\n  Bindings.cpp\n  Cylinder.cpp\n  DomainCreator.cpp\n  Rectilinear.cpp\n  Sphere.cpp\n  PYTHON_FILES\n  __init__.py\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BinaryCompactObject.hpp\n  Cylinder.hpp\n  DomainCreator.hpp\n  Rectilinear.hpp\n  Sphere.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DomainCreators\n  pybind11::module\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyCoordinateMaps\n  PyDomain\n  )\n"
  },
  {
    "path": "src/Domain/Creators/Python/Cylinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Python/Cylinder.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"Domain/Creators/Cylinder.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::py_bindings {\nvoid bind_cylinder(py::module& m) {\n  py::class_<Cylinder, DomainCreator<3>>(m, \"Cylinder\")\n      .def(py::init<double, double, double, double, bool, size_t,\n                    std::array<size_t, 3>, bool>(),\n           py::arg(\"inner_radius\"), py::arg(\"outer_radius\"),\n           py::arg(\"lower_bound\"), py::arg(\"upper_bound\"),\n           py::arg(\"is_periodic_in_z\"), py::arg(\"initial_refinement\"),\n           py::arg(\"initial_number_of_grid_points\"),\n           py::arg(\"use_equiangular_map\"));\n}\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/Cylinder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_cylinder(pybind11::module& m);\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/DomainCreator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Python/DomainCreator.hpp\"\n\n#include <array>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_domain_creator_impl(py::module& m) {  // NOLINT\n  py::class_<DomainCreator<Dim>>(\n      m, (\"DomainCreator\" + get_output(Dim) + \"D\").c_str())\n      .def(\"create_domain\", &DomainCreator<Dim>::create_domain)\n      .def(\"block_names\", &DomainCreator<Dim>::block_names)\n      .def(\"block_groups\", &DomainCreator<Dim>::block_groups)\n      .def(\"initial_extents\", &DomainCreator<Dim>::initial_extents)\n      .def(\"initial_refinement_levels\",\n           &DomainCreator<Dim>::initial_refinement_levels)\n      .def(\"functions_of_time\", &DomainCreator<Dim>::functions_of_time,\n           py::arg(\"initial_expiration_times\") =\n               std::unordered_map<std::string, double>{});\n}\n}  // namespace\n\nvoid bind_domain_creator(py::module& m) {  // NOLINT\n  bind_domain_creator_impl<1>(m);\n  bind_domain_creator_impl<2>(m);\n  bind_domain_creator_impl<3>(m);\n}\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/DomainCreator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_domain_creator(pybind11::module& m);\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/Rectilinear.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Python/Rectilinear.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::py_bindings {\ntemplate <size_t Dim>\nvoid bind_rectilinear_impl(py::module& m) {\n  py::class_<Rectilinear<Dim>, DomainCreator<Dim>>(\n      m, Rectilinear<Dim>::name().c_str())\n      .def(py::init<\n               std::array<double, Dim>, std::array<double, Dim>,\n               std::array<size_t, Dim>, std::array<size_t, Dim>,\n               std::array<bool, Dim>,\n               std::array<CoordinateMaps::DistributionAndSingularityPosition,\n                          Dim>>(),\n           py::arg(\"lower_bounds\"), py::arg(\"upper_bounds\"),\n           py::arg(\"initial_refinement_levels\"), py::arg(\"initial_num_points\"),\n           py::arg(\"is_periodic\") = make_array<Dim>(false),\n           py::arg(\"distribution\") =\n               std::array<CoordinateMaps::DistributionAndSingularityPosition,\n                          Dim>{});\n}\n\nvoid bind_rectilinear(py::module& m) {\n  bind_rectilinear_impl<1>(m);\n  bind_rectilinear_impl<2>(m);\n  bind_rectilinear_impl<3>(m);\n}\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/Rectilinear.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_rectilinear(pybind11::module& m);\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/Sphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Python/Sphere.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::py_bindings {\nvoid bind_sphere(py::module& m) {\n  py::class_<Sphere, DomainCreator<3>>(m, \"Sphere\")\n      .def(py::init([](const double inner_radius, const double outer_radius,\n                       const size_t initial_refinement,\n                       const size_t initial_num_points,\n                       const bool use_equiangular_map,\n                       const std::optional<bool>& excise,\n                       const std::optional<double>& inner_cube_sphericity) {\n             if (excise.has_value() == inner_cube_sphericity.has_value() or\n                 (excise.has_value() and not excise.value())) {\n               throw std::runtime_error(\n                   \"Specify either 'inner_cube_sphericity' or 'excise=True'.\");\n             }\n             auto interior = [&inner_cube_sphericity]()\n                 -> std::variant<Sphere::Excision, Sphere::InnerCube> {\n               if (inner_cube_sphericity.has_value()) {\n                 return Sphere::InnerCube{inner_cube_sphericity.value()};\n               } else {\n                 return Sphere::Excision{};\n               }\n             }();\n             return Sphere{inner_radius,        outer_radius,\n                           std::move(interior), initial_refinement,\n                           initial_num_points,  use_equiangular_map};\n           }),\n           py::arg(\"inner_radius\"), py::arg(\"outer_radius\"),\n           py::arg(\"initial_refinement\"),\n           py::arg(\"initial_number_of_grid_points\"),\n           py::arg(\"use_equiangular_map\"), py::arg(\"excise\") = std::nullopt,\n           py::arg(\"inner_cube_sphericity\") = std::nullopt);\n}\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/Sphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_sphere(pybind11::module& m);\n}  // namespace domain::creators::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n\nDomainCreator = {1: DomainCreator1D, 2: DomainCreator2D, 3: DomainCreator3D}\n"
  },
  {
    "path": "src/Domain/Creators/Rectilinear.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Rectilinear.hpp\"\n\n#include <array>\n#include <memory>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace Frame {\nstruct BlockLogical;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain::creators {\n\ntemplate <size_t Dim>\nRectilinear<Dim>::Rectilinear(\n    std::array<double, Dim> lower_bounds, std::array<double, Dim> upper_bounds,\n    std::array<size_t, Dim> initial_refinement_levels,\n    std::array<size_t, Dim> initial_num_points,\n    std::array<bool, Dim> is_periodic,\n    std::array<CoordinateMaps::DistributionAndSingularityPosition, Dim>\n        distributions,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<Dim>>\n        time_dependence,\n    const Options::Context& context)\n    : lower_bounds_(lower_bounds),\n      upper_bounds_(upper_bounds),\n      distributions_(distributions),\n      is_periodic_(is_periodic),\n      initial_refinement_levels_(initial_refinement_levels),\n      initial_num_points_(initial_num_points),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<Dim>>();\n  }\n  for (size_t d = 0; d < Dim; ++d) {\n    if (gsl::at(lower_bounds_, d) >= gsl::at(upper_bounds_, d)) {\n      PARSE_ERROR(context,\n                  \"Lower bound (\"\n                      << gsl::at(lower_bounds_, d)\n                      << \") must be strictly smaller than upper bound (\"\n                      << gsl::at(upper_bounds_, d) << \") in dimension \" << d\n                      << \".\");\n    }\n    const auto singularity_pos =\n        gsl::at(distributions_, d).singularity_position;\n    if (singularity_pos.has_value() and\n        *singularity_pos >= gsl::at(lower_bounds_, d) and\n        *singularity_pos <= gsl::at(upper_bounds_, d)) {\n      PARSE_ERROR(context, \"The 'SingularityPosition' (\"\n                               << *singularity_pos\n                               << \") falls inside the domain [\"\n                               << gsl::at(lower_bounds_, d) << \", \"\n                               << gsl::at(upper_bounds_, d) << \"].\");\n    }\n  }\n}\n\ntemplate <size_t Dim>\nRectilinear<Dim>::Rectilinear(\n    std::array<double, Dim> lower_bounds, std::array<double, Dim> upper_bounds,\n    std::array<size_t, Dim> initial_refinement_levels,\n    std::array<size_t, Dim> initial_num_points,\n    std::array<\n        std::array<\n            std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>, 2>,\n        Dim>\n        boundary_conditions,\n    std::array<CoordinateMaps::DistributionAndSingularityPosition, Dim>\n        distributions,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<Dim>>\n        time_dependence,\n    const Options::Context& context)\n    : Rectilinear(lower_bounds, upper_bounds, initial_refinement_levels,\n                  initial_num_points, make_array<Dim>(false), distributions,\n                  std::move(time_dependence), context) {\n  boundary_conditions_ = std::move(boundary_conditions);\n  using domain::BoundaryConditions::is_none;\n  using domain::BoundaryConditions::is_periodic;\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto& [lower_bc, upper_bc] = gsl::at(boundary_conditions_, d);\n    ASSERT(lower_bc != nullptr and upper_bc != nullptr,\n           \"None of the boundary conditions can be nullptr.\");\n    if (is_none(lower_bc) or is_none(upper_bc)) {\n      PARSE_ERROR(\n          context,\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\");\n    }\n    if (is_periodic(lower_bc) != is_periodic(upper_bc)) {\n      PARSE_ERROR(context,\n                  \"Periodic boundary conditions must be applied for both \"\n                  \"upper and lower direction in a dimension.\");\n    }\n    if (is_periodic(lower_bc) and is_periodic(upper_bc)) {\n      gsl::at(is_periodic_, d) = true;\n    }\n  }\n}\n\ntemplate <size_t Dim>\nDomain<Dim> Rectilinear<Dim>::create_domain() const {\n  // Handle periodicity by identifying faces\n  std::vector<PairOfFaces> identifications{};\n  if constexpr (Dim == 1) {\n    if (is_periodic_[0]) {\n      identifications.push_back({{0}, {1}});\n    }\n  } else if constexpr (Dim == 2) {\n    if (is_periodic_[0]) {\n      identifications.push_back({{0, 2}, {1, 3}});\n    }\n    if (is_periodic_[1]) {\n      identifications.push_back({{0, 1}, {2, 3}});\n    }\n  } else {\n    if (is_periodic_[0]) {\n      identifications.push_back({{0, 4, 2, 6}, {1, 5, 3, 7}});\n    }\n    if (is_periodic_[1]) {\n      identifications.push_back({{0, 1, 4, 5}, {2, 3, 6, 7}});\n    }\n    if (is_periodic_[2]) {\n      identifications.push_back({{0, 1, 2, 3}, {4, 5, 6, 7}});\n    }\n  }\n\n  auto block_map = [this]() {\n    if constexpr (Dim == 1) {\n      return Interval{-1.,\n                      1.,\n                      lower_bounds_[0],\n                      upper_bounds_[0],\n                      distributions_[0].distribution,\n                      distributions_[0].singularity_position};\n    } else if constexpr (Dim == 2) {\n      return Interval2D{Interval{-1., 1., lower_bounds_[0], upper_bounds_[0],\n                                 distributions_[0].distribution,\n                                 distributions_[0].singularity_position},\n                        Interval{-1., 1., lower_bounds_[1], upper_bounds_[1],\n                                 distributions_[1].distribution,\n                                 distributions_[1].singularity_position}};\n    } else {\n      return Interval3D{Interval{-1., 1., lower_bounds_[0], upper_bounds_[0],\n                                 distributions_[0].distribution,\n                                 distributions_[0].singularity_position},\n                        Interval{-1., 1., lower_bounds_[1], upper_bounds_[1],\n                                 distributions_[1].distribution,\n                                 distributions_[1].singularity_position},\n                        Interval{-1., 1., lower_bounds_[2], upper_bounds_[2],\n                                 distributions_[2].distribution,\n                                 distributions_[2].singularity_position}};\n    }\n  }();\n\n  std::array<size_t, two_to_the(Dim)> block_corners{};\n  std::iota(block_corners.begin(), block_corners.end(), 0_st);\n\n  Domain<Dim> domain{\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          std::move(block_map)),\n      {std::move(block_corners)},\n      std::move(identifications),\n      {},\n      block_names(),\n      block_groups()};\n\n  if (not time_dependence_->is_none()) {\n    domain.inject_time_dependent_map_for_block(\n        0, std::move(time_dependence_->block_maps_grid_to_inertial(1)[0]),\n        std::move(time_dependence_->block_maps_grid_to_distorted(1)[0]),\n        std::move(time_dependence_->block_maps_distorted_to_inertial(1)[0]));\n  }\n  return domain;\n}\n\ntemplate <size_t Dim>\nstd::vector<DirectionMap<\n    Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nRectilinear<Dim>::external_boundary_conditions() const {\n  if (boundary_conditions_[0][0] == nullptr) {\n#ifdef SPECTRE_DEBUG\n    for (size_t d = 0; d < Dim; ++d) {\n      ASSERT(gsl::at(boundary_conditions_, d)[0] == nullptr and\n                 gsl::at(boundary_conditions_, d)[1] == nullptr,\n             \"Boundary conditions must be set for all directions or none.\");\n    }\n#endif  // SPECTRE_DEBUG\n    return {};\n  }\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{1};\n  for (size_t d = 0; d < Dim; ++d) {\n    if (not gsl::at(is_periodic_, d)) {\n      const auto& [lower_bc, upper_bc] = gsl::at(boundary_conditions_, d);\n      boundary_conditions[0][Direction<Dim>{d, Side::Lower}] =\n          lower_bc->get_clone();\n      boundary_conditions[0][Direction<Dim>{d, Side::Upper}] =\n          upper_bc->get_clone();\n    }\n  }\n  return boundary_conditions;\n}\n\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>> Rectilinear<Dim>::initial_extents() const {\n  return {initial_num_points_};\n}\n\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>>\nRectilinear<Dim>::initial_refinement_levels() const {\n  return {initial_refinement_levels_};\n}\n\ntemplate <size_t Dim>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nRectilinear<Dim>::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if (time_dependence_->is_none()) {\n    return {};\n  } else {\n    return time_dependence_->functions_of_time(initial_expiration_times);\n  }\n}\n\ntemplate class Rectilinear<1>;\ntemplate class Rectilinear<2>;\ntemplate class Rectilinear<3>;\n\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Rectilinear.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\nclass Interval;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators {\n\n/// Create a domain consisting of a single Block in `Dim` dimensions.\ntemplate <size_t Dim>\nclass Rectilinear : public DomainCreator<Dim> {\n private:\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3,\n                \"Rectilinear domain is only implemented in 1, 2, or 3 \"\n                \"dimensions.\");\n\n  using Interval = CoordinateMaps::Interval;\n  using Interval2D = CoordinateMaps::ProductOf2Maps<Interval, Interval>;\n  using Interval3D =\n      CoordinateMaps::ProductOf3Maps<Interval, Interval, Interval>;\n  using Affine = CoordinateMaps::Affine;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using Affine3D = CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\n public:\n  using maps_list = tmpl::list<\n      domain::CoordinateMap<\n          Frame::BlockLogical, Frame::Inertial,\n          tmpl::conditional_t<\n              Dim == 1, Interval,\n              tmpl::conditional_t<Dim == 2, Interval2D, Interval3D>>>,\n      // Previous version of the domain used the `Affine` map, so we\n      // need to keep it in this list for backwards compatibility.\n      domain::CoordinateMap<\n          Frame::BlockLogical, Frame::Inertial,\n          tmpl::conditional_t<\n              Dim == 1, Affine,\n              tmpl::conditional_t<Dim == 2, Affine2D, Affine3D>>>>;\n\n  static std::string name() {\n    if constexpr (Dim == 1) {\n      return \"Interval\";\n    } else if constexpr (Dim == 2) {\n      return \"Rectangle\";\n    } else {\n      return \"Brick\";\n    }\n  }\n\n  struct LowerBound {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\"Lower bound in each dimension.\"};\n  };\n\n  struct UpperBound {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\"Upper bound in each dimension.\"};\n  };\n\n  struct Distribution {\n    using type =\n        std::array<CoordinateMaps::DistributionAndSingularityPosition, Dim>;\n    static constexpr Options::String help = {\n        \"Distribution of grid points in each dimension\"};\n  };\n\n  struct IsPeriodicIn {\n    using type = std::array<bool, Dim>;\n    static constexpr Options::String help = {\"Periodicity in each dimension.\"};\n  };\n\n  struct InitialRefinement {\n    using type = std::array<size_t, Dim>;\n    static constexpr Options::String help = {\n        \"Initial refinement level in each dimension.\"};\n  };\n\n  struct InitialGridPoints {\n    using type = std::array<size_t, Dim>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in each dimension.\"};\n  };\n\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<Dim>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct LowerUpperBoundaryCondition {\n    static constexpr Options::String help =\n        \"Lower and upper Boundary Conditions\";\n    struct LowerBC {\n      using type = std::unique_ptr<BoundaryConditionsBase>;\n      static constexpr Options::String help = \"Lower Boundary Condition\";\n      static std::string name() { return \"Lower\"; };\n    };\n    struct UpperBC {\n      using type = std::unique_ptr<BoundaryConditionsBase>;\n      static constexpr Options::String help = \"Upper Boundary Condition\";\n      static std::string name() { return \"Upper\"; };\n    };\n    LowerUpperBoundaryCondition(typename LowerBC::type lower_bc,\n                                typename UpperBC::type upper_bc)\n        : lower(std::move(lower_bc)), upper(std::move(upper_bc)){};\n    LowerUpperBoundaryCondition() = default;\n    std::unique_ptr<BoundaryConditionsBase> lower;\n    std::unique_ptr<BoundaryConditionsBase> upper;\n    using options = tmpl::list<LowerBC, UpperBC>;\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct BoundaryConditions {\n    static constexpr Options::String help = {\n        \"The boundary conditions to be imposed in each dimension. \"\n        \"Either specify one B.C. to be imposed for both \"\n        \"lower and upper boundary or a pair 'Lower:' and 'Upper:'.\"};\n    using type = std::array<\n        std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                     LowerUpperBoundaryCondition<BoundaryConditionsBase>>,\n        Dim>;\n  };\n\n  template <typename Metavariables>\n  using options = tmpl::list<\n      LowerBound, UpperBound, InitialRefinement, InitialGridPoints,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          BoundaryConditions<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>,\n          IsPeriodicIn>,\n      Distribution, TimeDependence>;\n\n  static constexpr Options::String help{\"A rectilinear domain.\"};\n\n  Rectilinear(\n      std::array<double, Dim> lower_bounds,\n      std::array<double, Dim> upper_bounds,\n      std::array<size_t, Dim> initial_refinement_levels,\n      std::array<size_t, Dim> initial_num_points,\n      std::array<bool, Dim> is_periodic = make_array<Dim>(false),\n      std::array<CoordinateMaps::DistributionAndSingularityPosition, Dim>\n          distributions = {},\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<Dim>>\n          time_dependence = nullptr,\n      const Options::Context& context = {});\n\n  Rectilinear(\n      std::array<double, Dim> lower_bounds,\n      std::array<double, Dim> upper_bounds,\n      std::array<size_t, Dim> initial_refinement_levels,\n      std::array<size_t, Dim> initial_num_points,\n      std::array<std::array<std::unique_ptr<\n                                domain::BoundaryConditions::BoundaryCondition>,\n                            2>,\n                 Dim>\n          boundary_conditions,\n      std::array<CoordinateMaps::DistributionAndSingularityPosition, Dim>\n          distributions = {},\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<Dim>>\n          time_dependence = nullptr,\n      const Options::Context& context = {});\n\n  template <typename BoundaryConditionsBase>\n  Rectilinear(\n      std::array<double, Dim> lower_bounds,\n      std::array<double, Dim> upper_bounds,\n      std::array<size_t, Dim> initial_refinement_levels,\n      std::array<size_t, Dim> initial_num_points,\n      std::array<\n          std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                       LowerUpperBoundaryCondition<BoundaryConditionsBase>>,\n          Dim>\n          boundary_conditions,\n      std::array<CoordinateMaps::DistributionAndSingularityPosition, Dim>\n          distributions = {},\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<Dim>>\n          time_dependence = nullptr,\n      const Options::Context& context = {})\n      : Rectilinear(\n            lower_bounds, upper_bounds, initial_refinement_levels,\n            initial_num_points,\n            transform_boundary_conditions(std::move(boundary_conditions)),\n            distributions, std::move(time_dependence), context) {}\n\n  Rectilinear() = default;\n  Rectilinear(const Rectilinear&) = delete;\n  Rectilinear(Rectilinear&&) = default;\n  Rectilinear& operator=(const Rectilinear&) = delete;\n  Rectilinear& operator=(Rectilinear&&) = default;\n  ~Rectilinear() override = default;\n\n  Domain<Dim> create_domain() const override;\n\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, Dim>> initial_extents() const override;\n\n  std::vector<std::array<size_t, Dim>> initial_refinement_levels()\n      const override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return {{name(), {name()}}};\n  }\n\n  // Transforms from option-created boundary conditions to the type used in the\n  // constructor\n  template <typename BoundaryConditionsBase>\n  static auto transform_boundary_conditions(\n      std::array<\n          std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                       LowerUpperBoundaryCondition<BoundaryConditionsBase>>,\n          Dim>\n          boundary_conditions)\n      -> std::array<\n          std::array<\n              std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>,\n              2>,\n          Dim>;\n\n private:\n  std::array<double, Dim> lower_bounds_{};\n  std::array<double, Dim> upper_bounds_{};\n  std::array<CoordinateMaps::DistributionAndSingularityPosition, Dim>\n      distributions_{};\n  std::array<bool, Dim> is_periodic_{};\n  std::array<size_t, Dim> initial_refinement_levels_{};\n  std::array<size_t, Dim> initial_num_points_{};\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<Dim>>\n      time_dependence_;\n  std::array<\n      std::array<std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>,\n                 2>,\n      Dim>\n      boundary_conditions_{};\n  inline static const std::vector<std::string> block_names_{name()};\n};\n\ntemplate <size_t Dim>\ntemplate <typename BoundaryConditionsBase>\nauto Rectilinear<Dim>::transform_boundary_conditions(\n    std::array<\n        std::variant<std::unique_ptr<BoundaryConditionsBase>,\n                     LowerUpperBoundaryCondition<BoundaryConditionsBase>>,\n        Dim>\n        boundary_conditions)\n    -> std::array<\n        std::array<\n            std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>, 2>,\n        Dim> {\n  std::array<\n      std::array<std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>,\n                 2>,\n      Dim>\n      result{};\n  for (size_t d = 0; d < Dim; ++d) {\n    if (std::holds_alternative<std::unique_ptr<BoundaryConditionsBase>>(\n            boundary_conditions[d])) {\n      auto bc = std::move(std::get<std::unique_ptr<BoundaryConditionsBase>>(\n          boundary_conditions[d]));\n      result[d][0] = bc->get_clone();\n      result[d][1] = std::move(bc);\n    } else {\n      auto& bc = std::get<LowerUpperBoundaryCondition<BoundaryConditionsBase>>(\n          boundary_conditions[d]);\n      result[d][0] = std::move(bc.lower);\n      result[d][1] = std::move(bc.upper);\n    }\n  }\n  return result;\n}\n\nusing Interval = Rectilinear<1>;\nusing Rectangle = Rectilinear<2>;\nusing Brick = Rectilinear<3>;\n\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/BulgedCube.hpp\"\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/CylindricalEndcap.hpp\"\n#include \"Domain/CoordinateMaps/CylindricalFlatEndcap.hpp\"\n#include \"Domain/CoordinateMaps/CylindricalFlatSide.hpp\"\n#include \"Domain/CoordinateMaps/CylindricalSide.hpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/FlatOffsetWedge.hpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/PolarToCartesian.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/SphericalCompression.hpp\"\n#include \"Domain/CoordinateMaps/UniformCylindricalEndcap.hpp\"\n#include \"Domain/CoordinateMaps/UniformCylindricalFlatEndcap.hpp\"\n#include \"Domain/CoordinateMaps/UniformCylindricalSide.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/Factory.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators {\nnamespace {\ntemplate <typename Creator>\nstruct get_maps {\n  using type = typename Creator::maps_list;\n};\n\ntemplate <typename Map>\nstruct to_grid_map {\n  using type = void;\n};\n\ntemplate <typename... Maps>\nstruct to_grid_map<\n    CoordinateMap<Frame::BlockLogical, Frame::Inertial, Maps...>> {\n  using type = CoordinateMap<Frame::BlockLogical, Frame::Grid, Maps...>;\n};\n\ntemplate <size_t Dim>\nusing maps_from_creators = tmpl::remove_duplicates<\n    tmpl::flatten<tmpl::transform<domain_creators<Dim>, get_maps<tmpl::_1>>>>;\n\ntemplate <size_t Dim>\nusing composition_map = domain::CoordinateMaps::Composition<\n    tmpl::list<Frame::BlockLogical, Frame::Grid, Frame::Inertial>, Dim>;\n}  // namespace\n\nvoid register_derived_with_charm() {\n  using all_maps = tmpl::remove_duplicates<tmpl::append<\n      maps_from_creators<1>, maps_from_creators<2>, maps_from_creators<3>,\n      tmpl::list<composition_map<1>, composition_map<2>, composition_map<3>>>>;\n  using maps_to_grid =\n      tmpl::remove<tmpl::transform<all_maps, to_grid_map<tmpl::_1>>, void>;\n  using maps_to_register =\n      tmpl::remove_duplicates<tmpl::append<all_maps, maps_to_grid>>;\n  register_classes_with_charm(maps_to_register{});\n\n  domain::CoordinateMaps::ShapeMapTransitionFunctions::\n      register_derived_with_charm();\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace domain {\nnamespace creators {\nvoid register_derived_with_charm();\n}  // namespace creators\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/RotatedBricks.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/RotatedBricks.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace domain::creators {\nRotatedBricks::RotatedBricks(\n    const typename LowerBound::type lower_xyz,\n    const typename Midpoint::type midpoint_xyz,\n    const typename UpperBound::type upper_xyz,\n    const typename InitialRefinement::type initial_refinement_level_xyz,\n    const typename InitialGridPoints::type initial_number_of_grid_points_in_xyz,\n    const typename IsPeriodicIn::type is_periodic_in,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dependence)\n    // clang-tidy: trivially copyable\n    : lower_xyz_(std::move(lower_xyz)),                      // NOLINT\n      midpoint_xyz_(std::move(midpoint_xyz)),                // NOLINT\n      upper_xyz_(std::move(upper_xyz)),                      // NOLINT\n      is_periodic_in_(std::move(is_periodic_in)),            // NOLINT\n      initial_refinement_level_xyz_(                         // NOLINT\n          std::move(initial_refinement_level_xyz)),          // NOLINT\n      initial_number_of_grid_points_in_xyz_(                 // NOLINT\n          std::move(initial_number_of_grid_points_in_xyz)),  // NOLINT\n      boundary_condition_(nullptr),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<3>>();\n  }\n}\n\nRotatedBricks::RotatedBricks(\n    const typename LowerBound::type lower_xyz,\n    const typename Midpoint::type midpoint_xyz,\n    const typename UpperBound::type upper_xyz,\n    const typename InitialRefinement::type initial_refinement_level_xyz,\n    const typename InitialGridPoints::type initial_number_of_grid_points_in_xyz,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        boundary_condition,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dependence,\n    const Options::Context& context)\n    // clang-tidy: trivially copyable\n    : lower_xyz_(std::move(lower_xyz)),                      // NOLINT\n      midpoint_xyz_(std::move(midpoint_xyz)),                // NOLINT\n      upper_xyz_(std::move(upper_xyz)),                      // NOLINT\n      is_periodic_in_{{false, false, false}},                // NOLINT\n      initial_refinement_level_xyz_(                         // NOLINT\n          std::move(initial_refinement_level_xyz)),          // NOLINT\n      initial_number_of_grid_points_in_xyz_(                 // NOLINT\n          std::move(initial_number_of_grid_points_in_xyz)),  // NOLINT\n      boundary_condition_(std::move(boundary_condition)),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<3>>();\n  }\n  using domain::BoundaryConditions::is_none;\n  if (is_none(boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(boundary_condition_)) {\n    is_periodic_in_[0] = true;\n    is_periodic_in_[1] = true;\n    is_periodic_in_[2] = true;\n  }\n}\n\nDomain<3> RotatedBricks::create_domain() const {\n  Domain<3> domain = rectilinear_domain<3>(\n      Index<3>{2, 2, 2},\n      std::array<std::vector<double>, 3>{\n          {{lower_xyz_[0], midpoint_xyz_[0], upper_xyz_[0]},\n           {lower_xyz_[1], midpoint_xyz_[1], upper_xyz_[1]},\n           {lower_xyz_[2], midpoint_xyz_[2], upper_xyz_[2]}}},\n      {},\n      std::vector<OrientationMap<3>>{\n          OrientationMap<3>::create_aligned(),\n          OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_zeta(), Direction<3>::upper_eta(),\n               Direction<3>::lower_xi()}}},\n          OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n               Direction<3>::lower_eta()}}},\n          OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_zeta(), Direction<3>::lower_xi(),\n               Direction<3>::lower_eta()}}},\n          OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n               Direction<3>::upper_zeta()}}},\n          OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_eta(), Direction<3>::lower_zeta(),\n               Direction<3>::lower_xi()}}},\n          OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_zeta(), Direction<3>::lower_xi(),\n               Direction<3>::lower_eta()}}},\n          OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_xi(), Direction<3>::upper_eta(),\n               Direction<3>::upper_zeta()}}}},\n      is_periodic_in_);\n  if (not time_dependence_->is_none()) {\n    const size_t number_of_blocks = domain.blocks().size();\n    auto block_maps_grid_to_inertial =\n        time_dependence_->block_maps_grid_to_inertial(number_of_blocks);\n    auto block_maps_grid_to_distorted =\n        time_dependence_->block_maps_grid_to_distorted(number_of_blocks);\n    auto block_maps_distorted_to_inertial =\n        time_dependence_->block_maps_distorted_to_inertial(number_of_blocks);\n    for (size_t block_id = 0; block_id < number_of_blocks; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n  return domain;\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nRotatedBricks::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if (time_dependence_->is_none()) {\n    return {};\n  } else {\n    return time_dependence_->functions_of_time(initial_expiration_times);\n  }\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nRotatedBricks::external_boundary_conditions() const {\n  if (boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{8};\n  if (is_periodic_in_[0]) {\n    return boundary_conditions;\n  }\n  std::vector<std::unordered_set<Direction<3>>>\n      external_boundaries_in_each_block{\n          {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n           Direction<3>::lower_zeta()},\n          {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n           Direction<3>::lower_zeta()},\n          {Direction<3>::upper_xi(), Direction<3>::upper_eta(),\n           Direction<3>::upper_zeta()}};\n  for (size_t i = 0; i < 8; ++i) {\n    for (const Direction<3>& direction : external_boundaries_in_each_block[i]) {\n      boundary_conditions[i][direction] = boundary_condition_->get_clone();\n    }\n  }\n  return boundary_conditions;\n}\n\nstd::vector<std::string> RotatedBricks::block_names() const {\n  return block_names_for_rectilinear_domains(Index<3>{2, 2, 2});\n}\n\nstd::vector<std::array<size_t, 3>> RotatedBricks::initial_extents() const {\n  const size_t& x_0 = initial_number_of_grid_points_in_xyz_[0][0];\n  const size_t& x_1 = initial_number_of_grid_points_in_xyz_[0][1];\n  const size_t& y_0 = initial_number_of_grid_points_in_xyz_[1][0];\n  const size_t& y_1 = initial_number_of_grid_points_in_xyz_[1][1];\n  const size_t& z_0 = initial_number_of_grid_points_in_xyz_[2][0];\n  const size_t& z_1 = initial_number_of_grid_points_in_xyz_[2][1];\n  return {{{x_0, y_0, z_0}}, {{z_0, y_0, x_1}}, {{x_0, z_0, y_1}},\n          {{y_1, z_0, x_1}}, {{y_0, x_0, z_1}}, {{z_1, x_1, y_0}},\n          {{y_1, z_1, x_0}}, {{x_1, y_1, z_1}}};\n}\n\nstd::vector<std::array<size_t, 3>> RotatedBricks::initial_refinement_levels()\n    const {\n  const size_t& x_0 = initial_refinement_level_xyz_[0];\n  const size_t& y_0 = initial_refinement_level_xyz_[1];\n  const size_t& z_0 = initial_refinement_level_xyz_[2];\n  return {{{x_0, y_0, z_0}}, {{z_0, y_0, x_0}}, {{x_0, z_0, y_0}},\n          {{y_0, z_0, x_0}}, {{y_0, x_0, z_0}}, {{z_0, x_0, y_0}},\n          {{y_0, z_0, x_0}}, {{x_0, y_0, z_0}}};\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/RotatedBricks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\ntemplate <size_t VolumeDim>\nclass DiscreteRotation;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain {\nnamespace creators {\n/// Create a 3D Domain consisting of eight rotated Blocks.\n///\n/// \\image html eightcubes_rotated_exploded.png\n///\n/// The orientations of the blocks are described in two different ways:\n///\n///  - 1 - As orientations of blocks relative to the physical cartesian axes.\n///\n///  - 2 - As rotations relative to the physical cartesian axes written using\n///     Rubik's cube rotation notation, for the sake of shorter variable names.\n///\n/// For reference, this is the notation used:\n///\n///  - U - A clockwise 90 degree rotation about the +z axis (The \"up\" side)\n///\n///  - R - A clockwise 90 degree rotation about the +x axis (The \"right\" side)\n///\n///  - F - A clockwise 90 degree rotation about the -y axis (The \"front\" side)\n///\n/// For reference, D, L, and B (\"down\", \"left\", and \"back\") are the inverse\n/// rotation to the aforementioned ones, respectively.\n/// Note: Whereas Rubik's cube rotations rotate a layer of the 3x3 puzzle\n/// cube, we are adopting the notation to apply to rotations of the\n/// cube itself.\n///\n/// - The -x, -y, -z block has the aligned orientation, that is,\n///   xi is aligned with x, eta is aligned with y, and zeta with z.\n///   This has block ID 0.\n///\n/// - The +x, -y, -z block has the orientation (zeta, eta, -xi).\n///   It corresponds to the orientation obtained by the rotation F.\n///   This has block ID 1.\n///\n/// - The -x, +y, -z block has the orientation (xi, zeta, -eta).\n///   It corresponds to the orientation obtained by the rotation R.\n///   This has block ID 2.\n///\n/// - The +x, +y, -z block has the orientation (zeta, -xi, -eta).\n///   It corresponds to the orientation obtained by the rotation F\n///   followed by the rotation R.\n///   This has block ID 3.\n///\n/// - The -x, -y, +z block has the orientation (eta, -xi, zeta).\n///   It corresponds to the orientation obtained by the rotation U.\n///   This has block ID 4.\n///\n/// - The +x, -y, +z block has the orientation (eta, -zeta, -xi).\n///   It corresponds to the orientation obtained by the rotation F\n///   followed by the rotation U.\n///   This has block ID 5.\n///\n/// - The -x, +y, +z block has the orientation (zeta, -xi, -eta).\n///   It corresponds to the orientation obtained by the rotation R\n///   followed by the rotation U (equivalently, F followed by R).\n///   This has block ID 6.\n///\n/// - The +x, +y, +z block also has the aligned orientation\n///   (xi, eta, zeta), relative to the edifice. It is not aligned\n///   relative to its neighbors.\n///   This has block ID 7.\n///\n/// This DomainCreator is useful for testing code that deals with\n/// unaligned blocks.\nclass RotatedBricks : public DomainCreator<3> {\n public:\n  using maps_list = tmpl::list<\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::ProductOf3Maps<\n                                CoordinateMaps::Affine, CoordinateMaps::Affine,\n                                CoordinateMaps::Affine>>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                            CoordinateMaps::DiscreteRotation<3>,\n                            CoordinateMaps::ProductOf3Maps<\n                                CoordinateMaps::Affine, CoordinateMaps::Affine,\n                                CoordinateMaps::Affine>>>;\n\n  struct LowerBound {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Sequence [x,y,z] for lower bound in the target frame.\"};\n  };\n\n  struct Midpoint {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Sequence [x,y,z] for midpoint in the target frame.\"};\n  };\n\n  struct UpperBound {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Sequence [x,y,z] for upper bound in the target frame.\"};\n  };\n\n  struct IsPeriodicIn {\n    using type = std::array<bool, 3>;\n    static constexpr Options::String help = {\n        \"Sequence in [x,y,z], true if periodic.\"};\n  };\n\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain.\"};\n  };\n\n  struct InitialRefinement {\n    using type = std::array<size_t, 3>;\n    static constexpr Options::String help = {\n        \"Initial refinement level in [x, y, z].\"};\n  };\n\n  struct InitialGridPoints {\n    using type = std::array<std::array<size_t, 2>, 3>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in [[x], [y], [z]].\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct BoundaryCondition {\n    static std::string name() { return \"BoundaryCondition\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to impose on all sides.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  using common_options = tmpl::list<LowerBound, Midpoint, UpperBound,\n                                    InitialRefinement, InitialGridPoints>;\n  using options_periodic = tmpl::list<IsPeriodicIn>;\n\n  template <typename Metavariables>\n  using options = tmpl::append<\n      common_options,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          tmpl::list<BoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n          options_periodic>,\n      tmpl::list<TimeDependence>>;\n\n  static constexpr Options::String help = {\n      \"A DomainCreator useful for testing purposes.\\n\"\n      \"RotatedBricks uses eight rotated Blocks to create the rectangular\\n\"\n      \"prism [LowerX,UpperX] x [LowerY,UpperY] x [LowerZ,UpperZ]. The\\n\"\n      \"outermost index to InitialGridPoints is the dimension index, and\\n\"\n      \"the innermost index is the block index along that dimension.\"};\n\n  RotatedBricks(\n      typename LowerBound::type lower_xyz, typename Midpoint::type midpoint_xyz,\n      typename UpperBound::type upper_xyz,\n      typename InitialRefinement::type initial_refinement_level_xyz,\n      typename InitialGridPoints::type initial_number_of_grid_points_in_xyz,\n      typename IsPeriodicIn::type is_periodic_in,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n          time_dependence);\n\n  RotatedBricks(\n      typename LowerBound::type lower_xyz, typename Midpoint::type midpoint_xyz,\n      typename UpperBound::type upper_xyz,\n      typename InitialRefinement::type initial_refinement_level_xyz,\n      typename InitialGridPoints::type initial_number_of_grid_points_in_xyz,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          boundary_condition,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n          time_dependence,\n      const Options::Context& context = {});\n\n  RotatedBricks() = default;\n  RotatedBricks(const RotatedBricks&) = delete;\n  RotatedBricks(RotatedBricks&&) = default;\n  RotatedBricks& operator=(const RotatedBricks&) = delete;\n  RotatedBricks& operator=(RotatedBricks&&) = default;\n  ~RotatedBricks() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n  std::vector<std::string> block_names() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n private:\n  typename LowerBound::type lower_xyz_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  typename Midpoint::type midpoint_xyz_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  typename UpperBound::type upper_xyz_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  typename IsPeriodicIn::type is_periodic_in_{{false, false, false}};\n  typename InitialRefinement::type initial_refinement_level_xyz_{\n      {std::numeric_limits<size_t>::max()}};\n  typename InitialGridPoints::type initial_number_of_grid_points_in_xyz_{\n      {{{std::numeric_limits<size_t>::max()}}}};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      boundary_condition_;\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n      time_dependence_{nullptr};\n};\n}  // namespace creators\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/RotatedIntervals.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/RotatedIntervals.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace domain::creators {\nRotatedIntervals::RotatedIntervals(\n    const std::array<double, 1> lower_x, const std::array<double, 1> midpoint_x,\n    const std::array<double, 1> upper_x,\n    const std::array<size_t, 1> initial_refinement_level_x,\n    const std::array<std::array<size_t, 2>, 1>\n        initial_number_of_grid_points_in_x,\n    const std::array<bool, 1> is_periodic_in,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<1>>\n        time_dependence)\n    : lower_x_(lower_x),\n      midpoint_x_(midpoint_x),\n      upper_x_(upper_x),\n      is_periodic_in_(is_periodic_in),\n      initial_refinement_level_x_(initial_refinement_level_x),\n      initial_number_of_grid_points_in_x_(initial_number_of_grid_points_in_x),\n      lower_boundary_condition_(nullptr),\n      upper_boundary_condition_(nullptr),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<1>>();\n  }\n}\n\nRotatedIntervals::RotatedIntervals(\n    const std::array<double, 1> lower_x, const std::array<double, 1> midpoint_x,\n    const std::array<double, 1> upper_x,\n    const std::array<size_t, 1> initial_refinement_level_x,\n    const std::array<std::array<size_t, 2>, 1>\n        initial_number_of_grid_points_in_x,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        lower_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        upper_boundary_condition,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<1>>\n        time_dependence,\n    const Options::Context& context)\n    : lower_x_(lower_x),\n      midpoint_x_(midpoint_x),\n      upper_x_(upper_x),\n      is_periodic_in_{{false}},\n      initial_refinement_level_x_(initial_refinement_level_x),\n      initial_number_of_grid_points_in_x_(initial_number_of_grid_points_in_x),\n      lower_boundary_condition_(std::move(lower_boundary_condition)),\n      upper_boundary_condition_(std::move(upper_boundary_condition)),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<1>>();\n  }\n  if ((lower_boundary_condition_ == nullptr) !=\n      (upper_boundary_condition_ == nullptr)) {\n    PARSE_ERROR(\n        context,\n        \"Both upper and lower boundary conditions must be specified, or \"\n        \"neither.\");\n  }\n  using domain::BoundaryConditions::is_none;\n  if (is_none(lower_boundary_condition_) or\n      is_none(upper_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(lower_boundary_condition_) !=\n      is_periodic(upper_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"Both the upper and lower boundary condition must be set to periodic \"\n        \"if imposing periodic boundary conditions.\");\n  }\n  if (is_periodic(lower_boundary_condition_)) {\n    is_periodic_in_[0] = true;\n  }\n}\n\nDomain<1> RotatedIntervals::create_domain() const {\n  std::vector<DirectionMap<\n      1, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions_all_blocks{};\n  Domain<1> domain = rectilinear_domain<1>(\n      Index<1>{2}, {{{lower_x_[0], midpoint_x_[0], upper_x_[0]}}}, {},\n      {OrientationMap<1>::create_aligned(),\n       OrientationMap<1>{\n           std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}}}},\n      is_periodic_in_);\n  if (not time_dependence_->is_none()) {\n    const size_t number_of_blocks = domain.blocks().size();\n    auto block_maps_grid_to_inertial =\n        time_dependence_->block_maps_grid_to_inertial(number_of_blocks);\n    auto block_maps_grid_to_distorted =\n        time_dependence_->block_maps_grid_to_distorted(number_of_blocks);\n    auto block_maps_distorted_to_inertial =\n        time_dependence_->block_maps_distorted_to_inertial(number_of_blocks);\n    for (size_t block_id = 0; block_id < number_of_blocks; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n  return domain;\n}\n\nstd::vector<DirectionMap<\n    1, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nRotatedIntervals::external_boundary_conditions() const {\n  if (upper_boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      1, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{2};\n  if (is_periodic_in_[0]) {\n    return boundary_conditions;\n  }\n  boundary_conditions[0][Direction<1>::lower_xi()] =\n      lower_boundary_condition_->get_clone();\n  boundary_conditions[1][Direction<1>::lower_xi()] =\n      upper_boundary_condition_->get_clone();\n  return boundary_conditions;\n}\n\nstd::vector<std::string> RotatedIntervals::block_names() const {\n  return block_names_for_rectilinear_domains(Index<1>{2});\n}\n\nstd::vector<std::array<size_t, 1>> RotatedIntervals::initial_extents() const {\n  return {{{initial_number_of_grid_points_in_x_[0][0]}},\n          {{initial_number_of_grid_points_in_x_[0][1]}}};\n}\n\nstd::vector<std::array<size_t, 1>>\nRotatedIntervals ::initial_refinement_levels() const {\n  return {{{initial_refinement_level_x_[0]}},\n          {{initial_refinement_level_x_[0]}}};\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nRotatedIntervals::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if (time_dependence_->is_none()) {\n    return {};\n  } else {\n    return time_dependence_->functions_of_time(initial_expiration_times);\n  }\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/RotatedIntervals.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class RotatedIntervals.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\ntemplate <size_t VolumeDim>\nclass DiscreteRotation;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain {\nnamespace creators {\n/// Create a 1D Domain consisting of two rotated Blocks.\n/// The left block has its logical \\f$\\xi\\f$-axis aligned with the grid x-axis.\n/// The right block has its logical \\f$\\xi\\f$-axis opposite to the grid x-axis.\n/// This is useful for testing code that deals with unaligned blocks.\nclass RotatedIntervals : public DomainCreator<1> {\n public:\n  using maps_list = tmpl::list<domain::CoordinateMap<\n      Frame::BlockLogical, Frame::Inertial,\n      domain::CoordinateMaps::DiscreteRotation<1>, CoordinateMaps::Affine>>;\n\n  struct LowerBound {\n    using type = std::array<double, 1>;\n    static constexpr Options::String help = {\n        \"Sequence of [x], the lower bound in the target frame.\"};\n  };\n\n  struct Midpoint {\n    using type = std::array<double, 1>;\n    static constexpr Options::String help = {\n        \"Sequence of [x], the midpoint in the target frame.\"};\n  };\n\n  struct UpperBound {\n    using type = std::array<double, 1>;\n    static constexpr Options::String help = {\n        \"Sequence of [x], the upper bound in the target frame.\"};\n  };\n\n  struct IsPeriodicIn {\n    using type = std::array<bool, 1>;\n    static constexpr Options::String help = {\n        \"Sequence for [x], true if periodic.\"};\n  };\n  struct InitialRefinement {\n    using type = std::array<size_t, 1>;\n    static constexpr Options::String help = {\n        \"Initial refinement level in [x].\"};\n  };\n\n  struct InitialGridPoints {\n    using type = std::array<std::array<size_t, 2>, 1>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in [[x]].\"};\n  };\n\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<1>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain.\"};\n  };\n\n  struct BoundaryConditions {\n    static constexpr Options::String help = \"The boundary conditions to apply.\";\n  };\n  template <typename BoundaryConditionsBase>\n  struct UpperBoundaryCondition {\n    static std::string name() { return \"UpperBoundary\"; }\n    static constexpr Options::String help =\n        \"Options for the boundary condition applied at the upper boundary.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n    using group = BoundaryConditions;\n  };\n  template <typename BoundaryConditionsBase>\n  struct LowerBoundaryCondition {\n    static std::string name() { return \"LowerBoundary\"; }\n    static constexpr Options::String help =\n        \"Options for the boundary condition applied at the lower boundary.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n    using group = BoundaryConditions;\n  };\n\n  using common_options = tmpl::list<LowerBound, Midpoint, UpperBound,\n                                    InitialRefinement, InitialGridPoints>;\n  using options_periodic = tmpl::list<IsPeriodicIn>;\n  template <typename System>\n  using options_boundary_conditions = tmpl::list<\n      LowerBoundaryCondition<\n          domain::BoundaryConditions::get_boundary_conditions_base<System>>,\n      UpperBoundaryCondition<\n          domain::BoundaryConditions::get_boundary_conditions_base<System>>>;\n\n  template <typename Metavariables>\n  using options = tmpl::append<\n      common_options,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          options_boundary_conditions<typename Metavariables::system>,\n          options_periodic>,\n      tmpl::list<TimeDependence>>;\n\n  static constexpr Options::String help = {\n      \"A DomainCreator useful for testing purposes.\\n\"\n      \"RotatedIntervals creates the interval [LowerX,UpperX] from two\\n\"\n      \"rotated Blocks. The outermost index to InitialGridPoints is the\\n\"\n      \"dimension index (of which there is only one in the case of\\n\"\n      \"RotatedIntervals), and the innermost index is the block index\\n\"\n      \"along that dimension.\"};\n\n  RotatedIntervals(\n      std::array<double, 1> lower_x, std::array<double, 1> midpoint_x,\n      std::array<double, 1> upper_x,\n      std::array<size_t, 1> initial_refinement_level_x,\n      std::array<std::array<size_t, 2>, 1> initial_number_of_grid_points_in_x,\n      std::array<bool, 1> is_periodic_in,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<1>>\n          time_dependence);\n\n  RotatedIntervals(\n      std::array<double, 1> lower_x, std::array<double, 1> midpoint_x,\n      std::array<double, 1> upper_x,\n      std::array<size_t, 1> initial_refinement_level_x,\n      std::array<std::array<size_t, 2>, 1> initial_number_of_grid_points_in_x,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          lower_boundary_condition,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          upper_boundary_condition,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<1>>\n          time_dependence,\n      const Options::Context& context = {});\n\n  RotatedIntervals() = default;\n  RotatedIntervals(const RotatedIntervals&) = delete;\n  RotatedIntervals(RotatedIntervals&&) = default;\n  RotatedIntervals& operator=(const RotatedIntervals&) = delete;\n  RotatedIntervals& operator=(RotatedIntervals&&) = default;\n  ~RotatedIntervals() override = default;\n\n  Domain<1> create_domain() const override;\n\n  std::vector<DirectionMap<\n      1, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::string> block_names() const override;\n\n  std::vector<std::array<size_t, 1>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 1>> initial_refinement_levels() const override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  std::array<double, 1> lower_x_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 1> midpoint_x_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 1> upper_x_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  std::array<bool, 1> is_periodic_in_{{false}};\n  std::array<size_t, 1> initial_refinement_level_x_{\n      {std::numeric_limits<size_t>::max()}};\n  std::array<std::array<size_t, 2>, 1> initial_number_of_grid_points_in_x_{\n      {{{std::numeric_limits<size_t>::max()}}}};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      lower_boundary_condition_{nullptr};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      upper_boundary_condition_{nullptr};\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<1>>\n      time_dependence_{nullptr};\n};\n}  // namespace creators\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/RotatedRectangles.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/RotatedRectangles.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace domain::creators {\nRotatedRectangles::RotatedRectangles(\n    const typename LowerBound::type lower_xy,\n    const typename Midpoint::type midpoint_xy,\n    const typename UpperBound::type upper_xy,\n    const typename InitialRefinement::type initial_refinement_level_xy,\n    const typename InitialGridPoints::type initial_number_of_grid_points_in_xy,\n    const typename IsPeriodicIn::type is_periodic_in,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n        time_dependence)\n    // clang-tidy: trivially copyable\n    : lower_xy_(std::move(lower_xy)),                       // NOLINT\n      midpoint_xy_(std::move(midpoint_xy)),                 // NOLINT\n      upper_xy_(std::move(upper_xy)),                       // NOLINT\n      is_periodic_in_(std::move(is_periodic_in)),           // NOLINT\n      initial_refinement_level_xy_(                         // NOLINT\n          std::move(initial_refinement_level_xy)),          // NOLINT\n      initial_number_of_grid_points_in_xy_(                 // NOLINT\n          std::move(initial_number_of_grid_points_in_xy)),  // NOLINT\n      boundary_condition_(nullptr),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<2>>();\n  }\n}\n\nRotatedRectangles::RotatedRectangles(\n    const typename LowerBound::type lower_xy,\n    const typename Midpoint::type midpoint_xy,\n    const typename UpperBound::type upper_xy,\n    const typename InitialRefinement::type initial_refinement_level_xy,\n    const typename InitialGridPoints::type initial_number_of_grid_points_in_xy,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        boundary_condition,\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n        time_dependence,\n    const Options::Context& context)\n    : lower_xy_(lower_xy),\n      midpoint_xy_(midpoint_xy),\n      upper_xy_(upper_xy),\n      is_periodic_in_{{false, false}},\n      initial_refinement_level_xy_(initial_refinement_level_xy),\n      initial_number_of_grid_points_in_xy_(initial_number_of_grid_points_in_xy),\n      boundary_condition_(std::move(boundary_condition)),\n      time_dependence_(std::move(time_dependence)) {\n  if (time_dependence_ == nullptr) {\n    time_dependence_ =\n        std::make_unique<domain::creators::time_dependence::None<2>>();\n  }\n  using domain::BoundaryConditions::is_none;\n  if (is_none(boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(boundary_condition_)) {\n    is_periodic_in_[0] = true;\n    is_periodic_in_[1] = true;\n  }\n}\n\nDomain<2> RotatedRectangles::create_domain() const {\n  Domain<2> domain = rectilinear_domain<2>(\n      Index<2>{2, 2},\n      std::array<std::vector<double>, 2>{\n          {{lower_xy_[0], midpoint_xy_[0], upper_xy_[0]},\n           {lower_xy_[1], midpoint_xy_[1], upper_xy_[1]}}},\n      {},\n      std::vector<OrientationMap<2>>{\n          OrientationMap<2>::create_aligned(),\n          OrientationMap<2>{\n              std::array{Direction<2>::lower_xi(), Direction<2>::lower_eta()}},\n          OrientationMap<2>{\n              std::array{Direction<2>::lower_eta(), Direction<2>::upper_xi()}},\n          OrientationMap<2>{\n              std::array{Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n      is_periodic_in_);\n  if (not time_dependence_->is_none()) {\n    const size_t number_of_blocks = domain.blocks().size();\n    auto block_maps_grid_to_inertial =\n        time_dependence_->block_maps_grid_to_inertial(number_of_blocks);\n    auto block_maps_grid_to_distorted =\n        time_dependence_->block_maps_grid_to_distorted(number_of_blocks);\n    auto block_maps_distorted_to_inertial =\n        time_dependence_->block_maps_distorted_to_inertial(number_of_blocks);\n    for (size_t block_id = 0; block_id < number_of_blocks; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n  return domain;\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nRotatedRectangles::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if (time_dependence_->is_none()) {\n    return {};\n  } else {\n    return time_dependence_->functions_of_time(initial_expiration_times);\n  }\n}\n\nstd::vector<DirectionMap<\n    2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nRotatedRectangles::external_boundary_conditions() const {\n  if (boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{4};\n  if (is_periodic_in_[0]) {\n    return boundary_conditions;\n  }\n  boundary_conditions[0][Direction<2>::lower_xi()] =\n      boundary_condition_->get_clone();\n  boundary_conditions[0][Direction<2>::lower_eta()] =\n      boundary_condition_->get_clone();\n  boundary_conditions[1][Direction<2>::lower_xi()] =\n      boundary_condition_->get_clone();\n  boundary_conditions[1][Direction<2>::upper_eta()] =\n      boundary_condition_->get_clone();\n  boundary_conditions[2][Direction<2>::upper_xi()] =\n      boundary_condition_->get_clone();\n  boundary_conditions[2][Direction<2>::upper_eta()] =\n      boundary_condition_->get_clone();\n  boundary_conditions[3][Direction<2>::lower_xi()] =\n      boundary_condition_->get_clone();\n  boundary_conditions[3][Direction<2>::upper_eta()] =\n      boundary_condition_->get_clone();\n  return boundary_conditions;\n}\n\nstd::vector<std::string> RotatedRectangles::block_names() const {\n  return block_names_for_rectilinear_domains(Index<2>{2, 2});\n}\n\nstd::vector<std::array<size_t, 2>> RotatedRectangles::initial_extents() const {\n  const size_t& x_0 = initial_number_of_grid_points_in_xy_[0][0];\n  const size_t& x_1 = initial_number_of_grid_points_in_xy_[0][1];\n  const size_t& y_0 = initial_number_of_grid_points_in_xy_[1][0];\n  const size_t& y_1 = initial_number_of_grid_points_in_xy_[1][1];\n  return {{{x_0, y_0}}, {{x_1, y_0}}, {{y_1, x_0}}, {{y_1, x_1}}};\n}\n\nstd::vector<std::array<size_t, 2>>\nRotatedRectangles::initial_refinement_levels() const {\n  const size_t& x_0 = initial_refinement_level_xy_[0];\n  const size_t& y_0 = initial_refinement_level_xy_[1];\n  return {{{x_0, y_0}}, {{x_0, y_0}}, {{y_0, x_0}}, {{y_0, x_0}}};\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/RotatedRectangles.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\ntemplate <size_t VolumeDim>\nclass DiscreteRotation;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain {\nnamespace creators {\n/// Create a 2D Domain consisting of four rotated Blocks.\n/// - The lower left block has its logical \\f$\\xi\\f$-axis aligned with\n/// the grid x-axis.\n///\n/// - The lower right block is rotated a half-turn (180 degrees) relative to the\n/// lower left block.\n///\n/// - The upper left block is rotated a quarter-turn counterclockwise\n/// (+90 degrees) relative to the lower left block.\n//\n/// - The upper right block is rotated a quarter-turn clockwise\n/// (-90 degrees) relative to the lower left block.\n///\n/// This DomainCreator is useful for testing code that deals with\n/// unaligned blocks.\nclass RotatedRectangles : public DomainCreator<2> {\n public:\n  using maps_list =\n      tmpl::list<domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                                    CoordinateMaps::Affine>>,\n                 domain::CoordinateMap<\n                     Frame::BlockLogical, Frame::Inertial,\n                     CoordinateMaps::DiscreteRotation<2>,\n                     CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                                    CoordinateMaps::Affine>>>;\n\n  struct LowerBound {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\n        \"Sequence of [x,y] for lower bounds in the target frame.\"};\n  };\n\n  struct Midpoint {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\n        \"Sequence of [x,y] for midpoints in the target frame.\"};\n  };\n\n  struct UpperBound {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\n        \"Sequence of [x,y] for upper bounds in the target frame.\"};\n  };\n\n  struct IsPeriodicIn {\n    using type = std::array<bool, 2>;\n    static constexpr Options::String help = {\n        \"Sequence for [x], true if periodic.\"};\n  };\n\n  struct TimeDependence {\n    using type =\n        std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>;\n    static constexpr Options::String help = {\n        \"The time dependence of the moving mesh domain.\"};\n  };\n\n  struct InitialRefinement {\n    using type = std::array<size_t, 2>;\n    static constexpr Options::String help = {\n        \"Initial refinement level in [x, y].\"};\n  };\n\n  struct InitialGridPoints {\n    using type = std::array<std::array<size_t, 2>, 2>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points in [[x], [y]].\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct BoundaryCondition {\n    static std::string name() { return \"BoundaryCondition\"; }\n    static constexpr Options::String help =\n        \"The boundary condition to impose on all sides.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  using common_options = tmpl::list<LowerBound, Midpoint, UpperBound,\n                                    InitialRefinement, InitialGridPoints>;\n  using options_periodic = tmpl::list<IsPeriodicIn>;\n\n  template <typename Metavariables>\n  using options = tmpl::append<\n      common_options,\n      tmpl::conditional_t<\n          domain::BoundaryConditions::has_boundary_conditions_base_v<\n              typename Metavariables::system>,\n          tmpl::list<BoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n          options_periodic>,\n      tmpl::list<TimeDependence>>;\n\n  static constexpr Options::String help = {\n      \"A DomainCreator useful for testing purposes.\\n\"\n      \"RotatedRectangles uses four rotated Blocks to create the rectangle\\n\"\n      \"[LowerX,UpperX] x [LowerY,UpperY]. The outermost index to\\n\"\n      \"InitialGridPoints is the dimension index, and the innermost index is\\n\"\n      \"the block index along that dimension.\"};\n\n  RotatedRectangles(\n      typename LowerBound::type lower_xy, typename Midpoint::type midpoint_xy,\n      typename UpperBound::type upper_xy,\n      typename InitialRefinement::type initial_refinement_level_xy,\n      typename InitialGridPoints::type initial_number_of_grid_points_in_xy,\n      typename IsPeriodicIn::type is_periodic_in,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n          time_dependence);\n\n  RotatedRectangles(\n      typename LowerBound::type lower_xy, typename Midpoint::type midpoint_xy,\n      typename UpperBound::type upper_xy,\n      typename InitialRefinement::type initial_refinement_level_xy,\n      typename InitialGridPoints::type initial_number_of_grid_points_in_xy,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          boundary_condition,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n          time_dependence,\n      const Options::Context& context = {});\n\n  RotatedRectangles() = default;\n  RotatedRectangles(const RotatedRectangles&) = delete;\n  RotatedRectangles(RotatedRectangles&&) = default;\n  RotatedRectangles& operator=(const RotatedRectangles&) = delete;\n  RotatedRectangles& operator=(RotatedRectangles&&) = default;\n  ~RotatedRectangles() override = default;\n\n  Domain<2> create_domain() const override;\n\n  std::vector<DirectionMap<\n      2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n  std::vector<std::string> block_names() const override;\n\n  std::vector<std::array<size_t, 2>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 2>> initial_refinement_levels() const override;\n\n private:\n  typename LowerBound::type lower_xy_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  typename Midpoint::type midpoint_xy_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  typename UpperBound::type upper_xy_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  typename IsPeriodicIn::type is_periodic_in_{{false, false}};\n  typename InitialRefinement::type initial_refinement_level_xy_{\n      {std::numeric_limits<size_t>::max()}};\n  typename InitialGridPoints::type initial_number_of_grid_points_in_xy_{\n      {{{std::numeric_limits<size_t>::max()}}}};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      boundary_condition_;\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n      time_dependence_{nullptr};\n};\n}  // namespace creators\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/ShellDistribution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/ShellDistribution.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <string>\n#include <variant>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::creators {\nnamespace {\nstruct DistributionVisitor {\n  size_t num_shells;\n\n  std::vector<domain::CoordinateMaps::Distribution> operator()(\n      const domain::CoordinateMaps::Distribution distribution) const {\n    // NOLINTNEXTLINE(modernize-return-braced-init-list)\n    return std::vector<domain::CoordinateMaps::Distribution>(num_shells,\n                                                             distribution);\n  }\n\n  std::vector<domain::CoordinateMaps::Distribution> operator()(\n      const std::vector<domain::CoordinateMaps::Distribution>& distributions)\n      const {\n    return distributions;\n  }\n};\n}  // namespace\n\nvoid set_shell_distribution(\n    const gsl::not_null<size_t*> number_of_shells,\n    const gsl::not_null<std::vector<domain::CoordinateMaps::Distribution>*>\n        radial_distribution,\n    const std::vector<double>& radial_partitioning,\n    const std::variant<domain::CoordinateMaps::Distribution,\n                       std::vector<domain::CoordinateMaps::Distribution>>&\n        input_radial_distribution,\n    const double innermost_shell_radius, const double outermost_shell_radius,\n    const std::string& innermost_radius_name,\n    const std::string& outermost_radius_name, const Options::Context& context) {\n  if (not std::is_sorted(radial_partitioning.begin(),\n                         radial_partitioning.end())) {\n    PARSE_ERROR(context,\n                \"Specify radial partitioning in ascending order. Specified \"\n                \"radial partitioning is: \"\n                    << radial_partitioning);\n  }\n\n  if (not radial_partitioning.empty()) {\n    if (radial_partitioning.front() <= innermost_shell_radius) {\n      PARSE_ERROR(context, \"First radial partition must be larger than the \"\n                               << innermost_radius_name << \" radius, but is: \"\n                               << innermost_shell_radius);\n    }\n    if (radial_partitioning.back() >= outermost_shell_radius) {\n      PARSE_ERROR(context, \"Last radial partition must be smaller than the \"\n                               << outermost_radius_name << \" radius, but is: \"\n                               << outermost_shell_radius);\n    }\n    const auto duplicate = std::adjacent_find(radial_partitioning.begin(),\n                                              radial_partitioning.end());\n    if (duplicate != radial_partitioning.end()) {\n      PARSE_ERROR(context, \"Radial partitioning contains duplicate element: \"\n                               << *duplicate);\n    }\n  }\n\n  (*number_of_shells) = 1 + radial_partitioning.size();\n  (*radial_distribution) = std::visit(DistributionVisitor{*number_of_shells},\n                                      input_radial_distribution);\n  if (radial_distribution->size() != *number_of_shells) {\n    PARSE_ERROR(context,\n                \"Specify a 'RadialDistribution' for every spherical shell. You \"\n                \"specified \"\n                    << radial_distribution->size()\n                    << \" items, but the domain has \" << *number_of_shells\n                    << \" shells.\");\n  }\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/ShellDistribution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <variant>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain::creators {\n/*!\n * \\brief Given info about the radial distribution, set the number of shells and\n * the distribution for each shell.\n *\n * \\details This is common code that all domain creators can use.\n */\nvoid set_shell_distribution(\n    gsl::not_null<size_t*> number_of_shells,\n    gsl::not_null<std::vector<domain::CoordinateMaps::Distribution>*>\n        radial_distribution,\n    const std::vector<double>& radial_partitioning,\n    const std::variant<domain::CoordinateMaps::Distribution,\n                       std::vector<domain::CoordinateMaps::Distribution>>&\n        input_radial_distribution,\n    double innermost_shell_radius, double outermost_shell_radius,\n    const std::string& innermost_radius_name,\n    const std::string& outermost_radius_name,\n    const Options::Context& context = {});\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Sphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Sphere.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <unordered_map>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/BulgedCube.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ExpandOverBlocks.hpp\"\n#include \"Domain/Creators/ShellDistribution.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Frame {\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n\nnamespace domain::creators {\n\nnamespace detail {\nExcision::Excision(\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        local_boundary_condition)\n    : boundary_condition(std::move(local_boundary_condition)) {}\n}  // namespace detail\n\nSphere::Sphere(\n    double inner_radius, double outer_radius,\n    std::variant<Excision, InnerCube> interior,\n    const typename InitialRefinement::type& initial_refinement,\n    const typename InitialGridPoints::type& initial_number_of_grid_points,\n    bool use_equiangular_map,\n    std::optional<EquatorialCompressionOptions> equatorial_compression,\n    std::vector<double> radial_partitioning,\n    const typename RadialDistribution::type& radial_distribution,\n    ShellWedges which_wedges,\n    std::optional<TimeDepOptionType> time_dependent_options,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        outer_boundary_condition,\n    const Options::Context& context)\n    : inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      interior_(std::move(interior)),\n      fill_interior_(std::holds_alternative<InnerCube>(interior_)),\n      use_equiangular_map_(use_equiangular_map),\n      equatorial_compression_(equatorial_compression),\n      radial_partitioning_(std::move(radial_partitioning)),\n      which_wedges_(which_wedges),\n      time_dependent_options_(std::move(time_dependent_options)),\n      outer_boundary_condition_(std::move(outer_boundary_condition)) {\n  if (inner_radius_ > outer_radius_) {\n    PARSE_ERROR(context,\n                \"Inner radius must be smaller than outer radius, but inner \"\n                \"radius is \" +\n                    std::to_string(inner_radius_) + \" and outer radius is \" +\n                    std::to_string(outer_radius_) + \".\");\n  }\n  if (equatorial_compression_.has_value() and fill_interior_) {\n    PARSE_ERROR(context,\n                \"The equatorial compression map can only be used with a shell, \"\n                \"not with a filled sphere.\");\n  }\n  // Validate radial partitions\n  set_shell_distribution(\n      make_not_null(&num_shells_), make_not_null(&radial_distribution_),\n      radial_partitioning_, radial_distribution, inner_radius_, outer_radius_,\n      \"inner\", \"outer\", context);\n  if (fill_interior_ and radial_distribution_.front() !=\n                             domain::CoordinateMaps::Distribution::Linear) {\n    PARSE_ERROR(context,\n                \"The 'RadialDistribution' must be 'Linear' for the innermost \"\n                \"shell filled with a cube because it changes in sphericity. \"\n                \"Add entries to 'RadialPartitioning' to add outer shells for \"\n                \"which you can select different radial distributions.\");\n  }\n\n  // Create grid anchors\n  grid_anchors_[\"Center\"] =\n      tnsr::I<double, 3, Frame::Grid>{std::array{0.0, 0.0, 0.0}};\n\n  // Determine number of blocks\n  num_blocks_per_shell_ = which_wedges_ == ShellWedges::All             ? 6\n                          : which_wedges_ == ShellWedges::FourOnEquator ? 4\n                                                                        : 1;\n  num_blocks_ = num_blocks_per_shell_ * num_shells_ + (fill_interior_ ? 1 : 0);\n\n  // Create block names and groups\n  static std::array<std::string, 6> wedge_directions{\n      \"UpperZ\", \"LowerZ\", \"UpperY\", \"LowerY\", \"UpperX\", \"LowerX\"};\n  for (size_t shell = 0; shell < num_shells_; ++shell) {\n    std::string shell_prefix = \"Shell\" + std::to_string(shell);\n    for (size_t direction = which_wedge_index(which_wedges_); direction < 6;\n         ++direction) {\n      const std::string wedge_name =\n          shell_prefix + gsl::at(wedge_directions, direction);\n      block_names_.emplace_back(wedge_name);\n      block_groups_[shell_prefix].insert(wedge_name);\n      block_groups_[\"Wedges\"].insert(wedge_name);\n    }\n  }\n  if (fill_interior_) {\n    block_names_.emplace_back(\"InnerCube\");\n  }\n  ASSERT(block_names_.size() == num_blocks_,\n         \"Invalid number of block names. Should be \"\n             << num_blocks_ << \" but is \" << block_names_.size() << \".\");\n\n  // Expand initial refinement and number of grid points over all blocks\n  const ExpandOverBlocks<std::array<size_t, 3>> expand_over_blocks{\n      block_names_, block_groups_};\n  try {\n    initial_refinement_ = std::visit(expand_over_blocks, initial_refinement);\n  } catch (const std::exception& error) {\n    PARSE_ERROR(context, \"Invalid 'InitialRefinement': \" << error.what());\n  }\n  try {\n    initial_number_of_grid_points_ =\n        std::visit(expand_over_blocks, initial_number_of_grid_points);\n  } catch (const std::exception& error) {\n    PARSE_ERROR(context, \"Invalid 'InitialGridPoints': \" << error.what());\n  }\n\n  if (fill_interior_) {\n    // The central cube has no notion of a \"radial\" direction, so we set\n    // refinement and number of grid points of the central cube z direction to\n    // its y value, which corresponds to the azimuthal direction of the\n    // wedges. This keeps the boundaries conforming when the radial direction\n    // is chosen differently to the angular directions.\n    auto& central_cube_refinement = initial_refinement_.back();\n    auto& central_cube_grid_points = initial_number_of_grid_points_.back();\n    central_cube_refinement[2] = central_cube_refinement[1];\n    central_cube_grid_points[2] = central_cube_grid_points[1];\n  }\n\n  // Validate boundary conditions\n  using domain::BoundaryConditions::is_none;\n  if (is_none(outer_boundary_condition_) or\n      (not fill_interior_ and\n       is_none(std::get<Excision>(interior_).boundary_condition))) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(outer_boundary_condition_) or\n      (not fill_interior_ and\n       is_periodic(std::get<Excision>(interior_).boundary_condition))) {\n    PARSE_ERROR(context,\n                \"Cannot have periodic boundary conditions with a Sphere\");\n  }\n  // Validate consistency of inner and outer boundary condition\n  if (not fill_interior_) {\n    const auto& inner_boundary_condition =\n        std::get<Excision>(interior_).boundary_condition;\n    if ((inner_boundary_condition == nullptr) !=\n        (outer_boundary_condition_ == nullptr)) {\n      PARSE_ERROR(\n          context,\n          \"Must specify either both inner and outer boundary conditions \"\n          \"or neither.\");\n    }\n  }\n  if (outer_boundary_condition != nullptr and\n      which_wedges_ != ShellWedges::All) {\n    PARSE_ERROR(\n        context,\n        \"Can only apply boundary conditions when the outer boundary of the \"\n        \"domain is a full sphere. \"\n        \"Additional cases can be supported by adding them to the Sphere \"\n        \"domain creator.\");\n  }\n\n  if (time_dependent_options_.has_value()) {\n    use_hard_coded_maps_ =\n        std::holds_alternative<sphere::TimeDependentMapOptions>(\n            time_dependent_options_.value());\n    if (use_hard_coded_maps_) {\n      std::get<sphere::TimeDependentMapOptions>(time_dependent_options_.value())\n          .build_maps(std::array{0.0, 0.0, 0.0}, fill_interior_, inner_radius_,\n                      radial_partitioning_, outer_radius_);\n    }\n  }\n}\n\nDomain<3> Sphere::create_domain() const {\n  std::vector<std::array<size_t, 8>> corners =\n      corners_for_radially_layered_domains(num_shells_, fill_interior_,\n                                           {{1, 2, 3, 4, 5, 6, 7, 8}},\n                                           which_wedges_);\n  double aspect_ratio = 1.0;\n  size_t index_polar_axis = 2;\n  if (equatorial_compression_.has_value()) {\n    aspect_ratio = equatorial_compression_.value().aspect_ratio;\n    index_polar_axis = equatorial_compression_.value().index_polar_axis;\n  }\n  const domain::CoordinateMaps::EquatorialCompression compression{\n      aspect_ratio, index_polar_axis};\n\n  auto coord_maps = domain::make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                            Frame::Inertial, 3>(\n      sph_wedge_coordinate_maps(\n          inner_radius_, outer_radius_,\n          fill_interior_ ? std::get<InnerCube>(interior_).sphericity : 1.0, 1.0,\n          use_equiangular_map_, std::nullopt, false, radial_partitioning_,\n          radial_distribution_, which_wedges_),\n      compression);\n\n  std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};\n\n  if (fill_interior_) {\n    const double inner_cube_sphericity =\n        std::get<InnerCube>(interior_).sphericity;\n    if (inner_cube_sphericity == 0.0) {\n      if (use_equiangular_map_) {\n        coord_maps.emplace_back(\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                Equiangular3D{\n                    Equiangular(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(3.0),\n                                inner_radius_ / sqrt(3.0)),\n                    Equiangular(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(3.0),\n                                inner_radius_ / sqrt(3.0)),\n                    Equiangular(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(3.0),\n                                inner_radius_ / sqrt(3.0))}));\n      } else {\n        coord_maps.emplace_back(\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                Affine3D{Affine(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(3.0),\n                                inner_radius_ / sqrt(3.0)),\n                         Affine(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(3.0),\n                                inner_radius_ / sqrt(3.0)),\n                         Affine(-1.0, 1.0, -1.0 * inner_radius_ / sqrt(3.0),\n                                inner_radius_ / sqrt(3.0))}));\n      }\n    } else {\n      coord_maps.emplace_back(\n          make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n              BulgedCube{inner_radius_, inner_cube_sphericity,\n                         use_equiangular_map_}));\n    }\n  } else {\n    // Set up excision sphere only for ShellWedges::All\n    // - The first 6 blocks enclose the excised sphere, see\n    //   sph_wedge_coordinate_maps\n    // - The 3D wedge map is oriented such that the lower-zeta logical direction\n    //   points radially inward.\n    if (which_wedges_ == ShellWedges::All) {\n      excision_spheres.emplace(\n          \"ExcisionSphere\",\n          ExcisionSphere<3>{inner_radius_,\n                            tnsr::I<double, 3, Frame::Grid>{0.0},\n                            {{0, Direction<3>::lower_zeta()},\n                             {1, Direction<3>::lower_zeta()},\n                             {2, Direction<3>::lower_zeta()},\n                             {3, Direction<3>::lower_zeta()},\n                             {4, Direction<3>::lower_zeta()},\n                             {5, Direction<3>::lower_zeta()}}});\n    }\n  }\n\n  Domain<3> domain{std::move(coord_maps),       corners,      {},\n                   std::move(excision_spheres), block_names_, block_groups_};\n  ASSERT(domain.blocks().size() == num_blocks_,\n         \"Unexpected number of blocks. Expected \"\n             << num_blocks_ << \" but created \" << domain.blocks().size()\n             << \".\");\n\n  if (time_dependent_options_.has_value()) {\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n        block_maps_grid_to_inertial{num_blocks_};\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n        block_maps_grid_to_distorted{num_blocks_};\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n        block_maps_distorted_to_inertial{num_blocks_};\n\n    if (use_hard_coded_maps_) {\n      const auto& hard_coded_options =\n          std::get<sphere::TimeDependentMapOptions>(\n              time_dependent_options_.value());\n\n      const size_t first_block_outer_shell =\n          (num_shells_ - 1) * num_blocks_per_shell_;\n      for (size_t block_id = 0; block_id < num_blocks_; block_id++) {\n        const bool is_outer_shell =\n            block_id >= first_block_outer_shell and\n            block_id < (first_block_outer_shell + num_blocks_per_shell_);\n        const bool is_inner_cube =\n            fill_interior_ and (block_id == num_blocks_ - 1);\n        // Correct for 'which_wedges' option\n        const size_t shell = block_id / num_blocks_per_shell_;\n        const size_t block_number = shell * 6 +\n                                    which_wedge_index(which_wedges_) +\n                                    block_id % num_blocks_per_shell_;\n        block_maps_grid_to_distorted[block_id] =\n            hard_coded_options.grid_to_distorted_map(\n                block_number, is_inner_cube, num_blocks_per_shell_);\n        block_maps_distorted_to_inertial[block_id] =\n            hard_coded_options.distorted_to_inertial_map(\n                block_number, is_inner_cube, num_blocks_per_shell_);\n        block_maps_grid_to_inertial[block_id] =\n            hard_coded_options.grid_to_inertial_map(\n                block_number, is_outer_shell, is_inner_cube,\n                num_blocks_per_shell_);\n      }\n\n      // Inject time dependent map into the excision\n      if (not fill_interior_ and which_wedges_ == ShellWedges::All) {\n        domain.inject_time_dependent_map_for_excision_sphere(\n            \"ExcisionSphere\", hard_coded_options.grid_to_inertial_map(\n                                  0, false, true, num_blocks_per_shell_));\n      }\n    } else {\n      const auto& time_dependence = std::get<std::unique_ptr<\n          domain::creators::time_dependence::TimeDependence<3>>>(\n          time_dependent_options_.value());\n\n      block_maps_grid_to_inertial =\n          time_dependence->block_maps_grid_to_inertial(num_blocks_);\n      block_maps_grid_to_distorted =\n          time_dependence->block_maps_grid_to_distorted(num_blocks_);\n      block_maps_distorted_to_inertial =\n          time_dependence->block_maps_distorted_to_inertial(num_blocks_);\n    }\n\n    for (size_t block_id = 0; block_id < num_blocks_; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n\n  return domain;\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nSphere::external_boundary_conditions() const {\n  if (outer_boundary_condition_ == nullptr) {\n    return {};\n  }\n\n  // This assumes 6 wedges making up the shell. If you need to support other\n  // configurations the below code needs to be updated. This would require\n  // adding more boundary condition options to the domain creator.\n  if (which_wedges_ != ShellWedges::All) {\n    ERROR(\n        \"Boundary conditions for incomplete spherical shells are not currently \"\n        \"implemented. Add support to the Sphere domain creator.\");\n  }\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{num_blocks_};\n  // Outer boundary conditions\n  const size_t outer_blocks_offset = num_blocks_ - 6 - (fill_interior_ ? 1 : 0);\n  for (size_t i = 0; i < 6; ++i) {\n    boundary_conditions[i + outer_blocks_offset][Direction<3>::upper_zeta()] =\n        outer_boundary_condition_->get_clone();\n  }\n  // Inner boundary conditions\n  if (not fill_interior_) {\n    const auto& inner_boundary_condition =\n        std::get<Excision>(interior_).boundary_condition;\n    for (size_t i = 0; i < 6; ++i) {\n      boundary_conditions[i][Direction<3>::lower_zeta()] =\n          inner_boundary_condition->get_clone();\n    }\n  }\n  return boundary_conditions;\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nSphere::functions_of_time(const std::unordered_map<std::string, double>&\n                              initial_expiration_times) const {\n  if (time_dependent_options_.has_value()) {\n    if (use_hard_coded_maps_) {\n      return std::get<sphere::TimeDependentMapOptions>(\n                 time_dependent_options_.value())\n          .create_functions_of_time(initial_expiration_times);\n    } else {\n      return std::get<std::unique_ptr<\n          domain::creators::time_dependence::TimeDependence<3>>>(\n                 time_dependent_options_.value())\n          ->functions_of_time(initial_expiration_times);\n    }\n  } else {\n    return {};\n  }\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Sphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <variant>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace CoordinateMaps {\nclass Affine;\nclass BulgedCube;\nclass EquatorialCompression;\nclass Equiangular;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\ntemplate <size_t Dim>\nclass Wedge;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators::detail {\n\n/// Options for excising the interior of the sphere. This class parses as the\n/// `ExcisionFromOptions` subclass if boundary conditions are enabled, and as a\n/// plain string if boundary conditions are disabled.\nstruct Excision {\n  Excision() = default;\n  Excision(std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n               boundary_condition);\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      boundary_condition = nullptr;\n};\n\nstruct ExcisionFromOptions : Excision {\n  static constexpr Options::String help = {\n      \"Excise the interior of the sphere, leaving a spherical shell.\"};\n  template <typename BoundaryConditionsBase>\n  struct BoundaryCondition {\n    static std::string name() { return \"ExciseWithBoundaryCondition\"; }\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n    static constexpr Options::String help = {\n        \"The boundary condition to impose on the excision surface.\"};\n  };\n  template <typename Metavariables>\n  using options = tmpl::list<BoundaryCondition<\n      domain::BoundaryConditions::get_boundary_conditions_base<\n          typename Metavariables::system>>>;\n  using Excision::Excision;\n};\n\n/// Options for filling the interior of the sphere with a cube\nstruct InnerCube {\n  static constexpr Options::String help = {\n      \"Fill the interior of the sphere with a cube.\"};\n  struct Sphericity {\n    static std::string name() { return \"FillWithSphericity\"; }\n    using type = double;\n    static constexpr Options::String help = {\n        \"Sphericity of the inner cube. A sphericity of 0 uses a product \"\n        \"of 1D maps as the map in the center. A sphericity > 0 uses a \"\n        \"BulgedCube. A sphericity of exactly 1 is not allowed. See \"\n        \"BulgedCube docs for why.\"};\n    static double lower_bound() { return 0.0; }\n    static double upper_bound() { return 1.0; }\n  };\n  using options = tmpl::list<Sphericity>;\n  InnerCube() = default;\n  explicit InnerCube(double sphericity_in) : sphericity(sphericity_in) {}\n  double sphericity = std::numeric_limits<double>::signaling_NaN();\n};\n\n}  // namespace domain::creators::detail\n\ntemplate <>\nstruct Options::create_from_yaml<domain::creators::detail::Excision> {\n  template <typename Metavariables>\n  static domain::creators::detail::Excision create(\n      const Options::Option& options) {\n    if constexpr (domain::BoundaryConditions::has_boundary_conditions_base_v<\n                      typename Metavariables::system>) {\n      // Boundary conditions are enabled. Parse with a nested option.\n      return options.parse_as<domain::creators::detail::ExcisionFromOptions,\n                              Metavariables>();\n    } else {\n      // Boundary conditions are disabled. Parse as a plain string.\n      if (options.parse_as<std::string>() == \"Excise\") {\n        return domain::creators::detail::Excision{};\n      } else {\n        PARSE_ERROR(options.context(), \"Parse error\");\n      }\n    }\n  }\n};\n\nnamespace domain::creators {\n\n/*!\n * \\brief A 3D cubed sphere.\n *\n * Six wedges surround an interior region, which is either excised or filled in\n * with a seventh block. The interior region is a (possibly deformed) sphere\n * when excised, or a (possibly deformed) cube when filled in. Additional\n * spherical shells, each composed of six wedges, can be added with the\n * 'RadialPartitioning' option.\n *\n * \\image html WedgeOrientations.png \"The orientation of each wedge in a cubed\n * sphere.\"\n *\n * This domain creator offers one grid anchor \"Center\" at the origin.\n *\n * #### Inner cube sphericity\n * The inner cube is a BulgedCube except if the inner cube sphericity is\n * exactly 0. Then an Equiangular or Affine map is used (depending on if it's\n * equiangular or not) to avoid a root find in the BulgedCube map.\n *\n * #### Time dependent maps\n * There are two ways to add time dependent maps to the Sphere domain\n * creator. In the input file, these are specified under the\n * `TimeDependentMaps:` block.\n *\n * ##### TimeDependence\n * You can use a simple TimeDependence (e.g.\n * `domain::creators::time_dependence::UniformTranslation` or\n * `domain::creators::time_dependence::RotationAboutZAxis`) to add time\n * dependent maps. This method will add the same maps to all blocks in the\n * domain. This method can be used with an inner cube or with an excision\n * surface.\n *\n * ##### Hard-coded time dependent maps\n * The Sphere domain creator also has the option to use some hard coded time\n * dependent maps that may be useful in certain scenarios. This method adds the\n * maps in `domain::creators::sphere::TimeDependentMapOptions` to the domain.\n * Currently, the first (inner-most) shell has maps between `Frame::Grid`,\n * `Frame::Distorted`, and `Frame::Inertial` while all subsequent shells only\n * have maps between `Frame::Grid` and `Frame::Inertial`.\n *\n * \\note You can only use hard-coded time dependent maps if you have an excision\n * surface. You cannot have a inner cube.\n *\n * ##### None\n * To not have any time dependent maps, pass a `std::nullopt` to appropriate\n * argument in the constructor. In the input file, simple have\n * `TimeDependentMaps: None`.\n *\n */\nclass Sphere : public DomainCreator<3> {\n private:\n  using Affine = CoordinateMaps::Affine;\n  using Affine3D = CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  using Equiangular = CoordinateMaps::Equiangular;\n  using Equiangular3D =\n      CoordinateMaps::ProductOf3Maps<Equiangular, Equiangular, Equiangular>;\n  using BulgedCube = CoordinateMaps::BulgedCube;\n\n public:\n  using maps_list = tmpl::append<\n      tmpl::list<\n          // Inner cube\n          domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                BulgedCube>,\n          domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine3D>,\n          domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                Equiangular3D>,\n          // Wedges\n          domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                CoordinateMaps::Wedge<3>>,\n          domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                CoordinateMaps::Wedge<3>,\n                                CoordinateMaps::EquatorialCompression>>,\n      typename sphere::TimeDependentMapOptions::maps_list>;\n\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Radius circumscribing the inner cube or the excision.\"};\n  };\n\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Radius of the sphere.\"};\n  };\n\n  using Excision = detail::Excision;\n  using InnerCube = detail::InnerCube;\n\n  struct Interior {\n    using type = std::variant<Excision, InnerCube>;\n    static constexpr Options::String help = {\n        \"Specify 'ExciseWithBoundaryCondition' and a boundary condition to \"\n        \"excise the interior of the sphere, leaving a spherical shell \"\n        \"(or just 'Excise' if boundary conditions are disabled). \"\n        \"Or specify 'CubeWithSphericity' to fill the interior.\"};\n  };\n\n  struct InitialRefinement {\n    using type =\n        std::variant<size_t, std::array<size_t, 3>,\n                     std::vector<std::array<size_t, 3>>,\n                     std::unordered_map<std::string, std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial refinement level. Specify one of: a single number, a \"\n        \"list representing [phi, theta, r], or such a list for every block \"\n        \"in the domain. The central cube always uses the value for 'theta' \"\n        \"in both y- and z-direction.\"};\n  };\n\n  struct InitialGridPoints {\n    using type =\n        std::variant<size_t, std::array<size_t, 3>,\n                     std::vector<std::array<size_t, 3>>,\n                     std::unordered_map<std::string, std::array<size_t, 3>>>;\n    static constexpr Options::String help = {\n        \"Initial number of grid points. Specify one of: a single number, a \"\n        \"list representing [phi, theta, r], or such a list for every block \"\n        \"in the domain. The central cube always uses the value for 'theta' \"\n        \"in both y- and z-direction.\"};\n  };\n\n  struct UseEquiangularMap {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Use equiangular instead of equidistant coordinates. Equiangular \"\n        \"coordinates give better gridpoint spacings in the angular \"\n        \"directions, while equidistant coordinates give better gridpoint \"\n        \"spacings in the inner cube.\"};\n  };\n\n  /// Options for the EquatorialCompression map\n  struct EquatorialCompressionOptions {\n    static constexpr Options::String help = {\n        \"Options for the EquatorialCompression map.\"};\n    struct AspectRatio {\n      using type = double;\n      static constexpr Options::String help = {\n          \"An aspect ratio greater than 1 moves grid points toward the \"\n          \"equator, and an aspect ratio smaller than 1 moves grid points \"\n          \"toward the poles.\"};\n      static double lower_bound() { return 0.0; }\n    };\n    struct IndexPolarAxis {\n      using type = size_t;\n      static constexpr Options::String help = {\n          \"The index (0, 1, or 2) of the axis along which equatorial \"\n          \"compression is applied, where 0 is x, 1 is y, and 2 is z.\"};\n      static size_t upper_bound() { return 2; }\n    };\n    using options = tmpl::list<AspectRatio, IndexPolarAxis>;\n\n    double aspect_ratio;\n    size_t index_polar_axis;\n  };\n\n  struct EquatorialCompression {\n    using type =\n        Options::Auto<EquatorialCompressionOptions, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"Apply an equatorial compression map to focus resolution on the \"\n        \"equator or on the poles. The equatorial compression is an angular \"\n        \"redistribution of grid points and will preserve the spherical shape \"\n        \"of the inner and outer boundaries.\"};\n  };\n\n  struct RadialPartitioning {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"Radial coordinates of the boundaries splitting the spherical shell \"\n        \"between InnerRadius and OuterRadius. They must be given in ascending \"\n        \"order. This should be used if boundaries need to be set at specific \"\n        \"radii. If the number but not the specific locations of the boundaries \"\n        \"are important, use InitialRefinement instead.\"};\n  };\n\n  struct RadialDistribution {\n    using type =\n        std::variant<domain::CoordinateMaps::Distribution,\n                     std::vector<domain::CoordinateMaps::Distribution>>;\n    static constexpr Options::String help = {\n        \"Select the radial distribution of grid points in each spherical \"\n        \"shell. There must be N+1 radial distributions specified for N radial \"\n        \"partitions. If the interior of the sphere is filled with a cube, the \"\n        \"innermost shell must have a 'Linear' distribution because it changes \"\n        \"in sphericity. You can also specify just a single radial distribution \"\n        \"(not in a vector) which will use the same distribution for all \"\n        \"partitions.\"};\n  };\n\n  struct WhichWedges {\n    using type = ShellWedges;\n    static constexpr Options::String help = {\n        \"Which wedges to include in the shell.\"};\n    static constexpr type suggested_value() { return ShellWedges::All; }\n  };\n\n  using TimeDepOptionType = std::variant<\n      sphere::TimeDependentMapOptions,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>>;\n\n  struct TimeDependentMaps {\n    using type = Options::Auto<TimeDepOptionType, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"The options for time dependent maps. This can either be a \"\n        \"TimeDependence or hard coded time dependent options. Specify `None` \"\n        \"for no time dependent maps.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct OuterBoundaryCondition {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions at the outer radius.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  using basic_options =\n      tmpl::list<InnerRadius, OuterRadius, Interior, InitialRefinement,\n                 InitialGridPoints, UseEquiangularMap, EquatorialCompression,\n                 RadialPartitioning, RadialDistribution, WhichWedges,\n                 TimeDependentMaps>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          OuterBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"A 3D cubed sphere. Six wedges surround an interior region, which is \"\n      \"either excised or filled in with a seventh block. The interior region \"\n      \"is a (possibly deformed) sphere when excised, or a (possibly deformed) \"\n      \"cube when filled in. Additional spherical shells, each composed of six \"\n      \"wedges, can be added with the 'RadialPartitioning' option.\"};\n\n  Sphere(\n      double inner_radius, double outer_radius,\n      std::variant<Excision, InnerCube> interior,\n      const typename InitialRefinement::type& initial_refinement,\n      const typename InitialGridPoints::type& initial_number_of_grid_points,\n      bool use_equiangular_map,\n      std::optional<EquatorialCompressionOptions> equatorial_compression = {},\n      std::vector<double> radial_partitioning = {},\n      const typename RadialDistribution::type& radial_distribution =\n          domain::CoordinateMaps::Distribution::Linear,\n      ShellWedges which_wedges = ShellWedges::All,\n      std::optional<TimeDepOptionType> time_dependent_options = std::nullopt,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          outer_boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  Sphere() = default;\n  Sphere(const Sphere&) = delete;\n  Sphere(Sphere&&) = default;\n  Sphere& operator=(const Sphere&) = delete;\n  Sphere& operator=(Sphere&&) = default;\n  ~Sphere() override = default;\n\n  Domain<3> create_domain() const override;\n\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n  grid_anchors() const override {\n    return grid_anchors_;\n  }\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override {\n    return initial_number_of_grid_points_;\n  }\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels()\n      const override {\n    return initial_refinement_;\n  }\n\n  std::vector<std::string> block_names() const override { return block_names_; }\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return block_groups_;\n  }\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  double inner_radius_{};\n  double outer_radius_{};\n  std::variant<Excision, InnerCube> interior_{};\n  bool fill_interior_ = false;\n  std::vector<std::array<size_t, 3>> initial_refinement_{};\n  std::vector<std::array<size_t, 3>> initial_number_of_grid_points_{};\n  bool use_equiangular_map_ = false;\n  std::optional<EquatorialCompressionOptions> equatorial_compression_{};\n  std::vector<double> radial_partitioning_{};\n  std::vector<domain::CoordinateMaps::Distribution> radial_distribution_{};\n  ShellWedges which_wedges_ = ShellWedges::All;\n  std::optional<TimeDepOptionType> time_dependent_options_{};\n  bool use_hard_coded_maps_{false};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      outer_boundary_condition_;\n  size_t num_shells_{};\n  size_t num_blocks_;\n  size_t num_blocks_per_shell_;\n  std::vector<std::string> block_names_{};\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_{};\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n      grid_anchors_{};\n};\n\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/SphericalShells.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/SphericalShells.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <unordered_map>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ShellDistribution.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace domain::creators {\n\nSphericalShells::SphericalShells(\n    const double inner_radius, const double outer_radius,\n    const size_t initial_radial_refinement,\n    const size_t initial_number_of_radial_grid_points,\n    const size_t initial_spherical_harmonic_l,\n    std::vector<double> radial_partitioning,\n    const typename RadialDistribution::type& radial_distribution,\n    std::optional<TimeDepOptionType> time_dependent_options,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        inner_boundary_condition,\n    std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n        outer_boundary_condition,\n    const Options::Context& context)\n    : inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      initial_radial_refinement_(initial_radial_refinement),\n      initial_number_of_radial_grid_points_(\n          initial_number_of_radial_grid_points),\n      initial_spherical_harmonic_l_(initial_spherical_harmonic_l),\n      radial_partitioning_(std::move(radial_partitioning)),\n      time_dependent_options_(std::move(time_dependent_options)),\n      inner_boundary_condition_(std::move(inner_boundary_condition)),\n      outer_boundary_condition_(std::move(outer_boundary_condition)),\n      grid_anchors_{{{\"Center\", tnsr::I<double, 3, Frame::Grid>{\n                                    std::array{0.0, 0.0, 0.0}}}}} {\n  if (inner_radius_ > outer_radius_) {\n    PARSE_ERROR(context,\n                \"Inner radius must be smaller than outer radius, but inner \"\n                \"radius is \" +\n                    std::to_string(inner_radius_) + \" and outer radius is \" +\n                    std::to_string(outer_radius_) + \".\");\n  }\n  set_shell_distribution(\n      make_not_null(&num_blocks_), make_not_null(&radial_distribution_),\n      radial_partitioning_, radial_distribution, inner_radius_, outer_radius_,\n      \"inner\", \"outer\", context);\n  for (size_t shell = 0; shell < num_blocks_; ++shell) {\n    const std::string shell_name = \"Shell\" + std::to_string(shell);\n    block_names_.emplace_back(shell_name);\n    // This makes consistent block groups with those created by Sphere\n    block_groups_[shell_name].insert(shell_name);\n  }\n\n  // Validate boundary conditions\n  using domain::BoundaryConditions::is_none;\n  if (is_none(inner_boundary_condition_) or\n      is_none(outer_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"None boundary condition is not supported. If you would like an \"\n        \"outflow-type boundary condition, you must use that.\");\n  }\n  using domain::BoundaryConditions::is_periodic;\n  if (is_periodic(inner_boundary_condition_) or\n      is_periodic(outer_boundary_condition_)) {\n    PARSE_ERROR(\n        context,\n        \"Cannot have periodic boundary conditions with SphericalShells\");\n  }\n  // Validate consistency of inner and outer boundary condition\n  if ((inner_boundary_condition_ == nullptr) !=\n      (outer_boundary_condition_ == nullptr)) {\n    PARSE_ERROR(context,\n                \"Must specify either both inner and outer boundary conditions \"\n                \"or neither.\");\n  }\n\n  if (time_dependent_options_.has_value()) {\n    use_hard_coded_maps_ =\n        std::holds_alternative<sphere::TimeDependentMapOptions>(\n            time_dependent_options_.value());\n    if (use_hard_coded_maps_) {\n      std::get<sphere::TimeDependentMapOptions>(time_dependent_options_.value())\n          .build_maps(std::array{0.0, 0.0, 0.0}, false, inner_radius_,\n                      radial_partitioning_, outer_radius_);\n    }\n  }\n}\n\nDomain<3> SphericalShells::create_domain() const {\n  std::vector<Block<3>> blocks;\n  blocks.reserve(num_blocks_);\n  const auto aligned = OrientationMap<3>::create_aligned();\n  for (size_t i = 0; i < num_blocks_; ++i) {\n    CoordinateMaps::Interval radial_map{\n        -1.0,\n        1.0,\n        i == 0 ? inner_radius_ : radial_partitioning_[i - 1],\n        i == num_blocks_ - 1 ? outer_radius_ : radial_partitioning_[i],\n        radial_distribution_[i],\n        0.0};\n    auto stationary_map =\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            CoordinateMaps::ProductOf2Maps<CoordinateMaps::Interval,\n                                           CoordinateMaps::Identity<2>>{\n                std::move(radial_map), CoordinateMaps::Identity<2>{}},\n            CoordinateMaps::SphericalToCartesianPfaffian{});\n    DirectionMap<3, BlockNeighbors<3>> neighbors;\n    if (i > 0) {\n      neighbors.emplace(std::pair{Direction<3>::lower_xi(),\n                                  BlockNeighbors<3>{i - 1, aligned}});\n    }\n    if (i < num_blocks_ - 1) {\n      neighbors.emplace(std::pair{Direction<3>::upper_xi(),\n                                  BlockNeighbors<3>{i + 1, aligned}});\n    }\n    blocks.emplace_back(std::move(stationary_map), i, std::move(neighbors),\n                        block_names_.at(i),\n                        domain::topologies::spherical_shell);\n  }\n\n  std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};\n  excision_spheres.emplace(\n      \"ExcisionSphere\", ExcisionSphere<3>{inner_radius_,\n                                          tnsr::I<double, 3, Frame::Grid>{0.0},\n                                          {{0, Direction<3>::lower_xi()}}});\n\n  Domain<3> domain(std::move(blocks), std::move(excision_spheres),\n                   block_groups_);\n\n  if (time_dependent_options_.has_value()) {\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n        block_maps_grid_to_inertial{num_blocks_};\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n        block_maps_grid_to_distorted{num_blocks_};\n    std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n        block_maps_distorted_to_inertial{num_blocks_};\n\n    if (use_hard_coded_maps_) {\n      const auto& hard_coded_options =\n          std::get<sphere::TimeDependentMapOptions>(\n              time_dependent_options_.value());\n\n      for (size_t block_id = 0; block_id < num_blocks_; block_id++) {\n        const bool is_outer_shell = block_id == num_blocks_ - 1;\n        block_maps_grid_to_distorted[block_id] =\n            hard_coded_options.grid_to_distorted_map(block_id, false, 1);\n        block_maps_distorted_to_inertial[block_id] =\n            hard_coded_options.distorted_to_inertial_map(block_id, false, 1);\n        block_maps_grid_to_inertial[block_id] =\n            hard_coded_options.grid_to_inertial_map(block_id, is_outer_shell,\n                                                    false, 1);\n      }\n\n      domain.inject_time_dependent_map_for_excision_sphere(\n          \"ExcisionSphere\",\n          hard_coded_options.grid_to_inertial_map(0, false, true, 1));\n    } else {\n      const auto& time_dependence = std::get<std::unique_ptr<\n          domain::creators::time_dependence::TimeDependence<3>>>(\n          time_dependent_options_.value());\n\n      block_maps_grid_to_inertial =\n          time_dependence->block_maps_grid_to_inertial(num_blocks_);\n      block_maps_grid_to_distorted =\n          time_dependence->block_maps_grid_to_distorted(num_blocks_);\n      block_maps_distorted_to_inertial =\n          time_dependence->block_maps_distorted_to_inertial(num_blocks_);\n    }\n\n    for (size_t block_id = 0; block_id < num_blocks_; ++block_id) {\n      domain.inject_time_dependent_map_for_block(\n          block_id, std::move(block_maps_grid_to_inertial[block_id]),\n          std::move(block_maps_grid_to_distorted[block_id]),\n          std::move(block_maps_distorted_to_inertial[block_id]));\n    }\n  }\n\n  return domain;\n}\n\nstd::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\nSphericalShells::grid_anchors() const {\n  return grid_anchors_;\n}\n\nstd::vector<DirectionMap<\n    3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\nSphericalShells::external_boundary_conditions() const {\n  if (outer_boundary_condition_ == nullptr) {\n    return {};\n  }\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{num_blocks_};\n  boundary_conditions[0][Direction<3>::lower_xi()] =\n      inner_boundary_condition_->get_clone();\n  boundary_conditions[num_blocks_ - 1][Direction<3>::upper_xi()] =\n      outer_boundary_condition_->get_clone();\n  return boundary_conditions;\n}\n\nstd::vector<std::array<size_t, 3>> SphericalShells::initial_extents() const {\n  return std::vector{num_blocks_,\n                     std::array{initial_number_of_radial_grid_points_,\n                                initial_spherical_harmonic_l_ + 1,\n                                2 * initial_spherical_harmonic_l_ + 1}};\n}\n\nstd::vector<std::array<size_t, 3>> SphericalShells::initial_refinement_levels()\n    const {\n  return std::vector{num_blocks_,\n                     std::array{initial_radial_refinement_, 0_st, 0_st}};\n}\n\nstd::vector<std::string> SphericalShells::block_names() const {\n  return block_names_;\n}\n\nstd::unordered_map<std::string, std::unordered_set<std::string>>\nSphericalShells::block_groups() const {\n  return block_groups_;\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nSphericalShells::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if (time_dependent_options_.has_value()) {\n    if (use_hard_coded_maps_) {\n      return std::get<sphere::TimeDependentMapOptions>(\n                 time_dependent_options_.value())\n          .create_functions_of_time(initial_expiration_times);\n    } else {\n      return std::get<std::unique_ptr<\n          domain::creators::time_dependence::TimeDependence<3>>>(\n                 time_dependent_options_.value())\n          ->functions_of_time(initial_expiration_times);\n    }\n  } else {\n    return {};\n  }\n}\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/SphericalShells.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <variant>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Domain;\nnamespace domain {\nnamespace CoordinateMaps {\ntemplate <size_t Dim>\nclass Identity;\nclass Interval;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\nclass SphericalToCartesianPfaffian;\n}  // namespace CoordinateMaps\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators {\n/// \\brief A set of concentric spherical shells\n///\n/// \\note This domain will use a spherical harmonic basis in the angular\n/// directions.  It cannot be used with subcell.\n///\n/// \\see Sphere for a spherical domain compatible with subcell\n///\n/// This domain creator offers one grid anchor \"Center\" at the origin.\n///\n/// #### Time dependent maps\n/// There are two ways to add time dependent maps to the SphericalShells domain\n/// creator. In the input file, these are specified under the\n/// `TimeDependentMaps:` block.\n///\n/// ##### TimeDependence\n/// You can use a simple TimeDependence (e.g.\n/// `domain::creators::time_dependence::UniformTranslation` or\n/// `domain::creators::time_dependence::RotationAboutZAxis`) to add time\n/// dependent maps. This method will add the same maps to all blocks in the\n/// domain.\n///\n/// ##### Hard-coded time dependent maps\n/// The SphericalShells domain creator also has the option to use some hard\n/// coded time dependent maps that may be useful in certain scenarios. This\n/// method adds the maps in `domain::creators::sphere::TimeDependentMapOptions`\n/// to the domain. Currently, the first (inner-most) shell has maps between\n/// `Frame::Grid`, `Frame::Distorted`, and `Frame::Inertial` while all\n/// subsequent shells only have maps between `Frame::Grid` and\n/// `Frame::Inertial`.\n///\n/// ##### None\n/// To not have any time dependent maps, pass a `std::nullopt` as the\n/// appropriate argument in the constructor. In the input file, simply have\n/// `TimeDependentMaps: None`.\nclass SphericalShells : public DomainCreator<3> {\n public:\n  using maps_list =\n      tmpl::append<tmpl::list<domain::CoordinateMap<\n                       Frame::BlockLogical, Frame::Inertial,\n                       domain::CoordinateMaps::ProductOf2Maps<\n                           domain::CoordinateMaps::Interval,\n                           domain::CoordinateMaps::Identity<2>>,\n                       domain::CoordinateMaps::SphericalToCartesianPfaffian>>,\n                   typename sphere::TimeDependentMapOptions::maps_list>;\n\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Inner radius of the spherical shells.\"};\n  };\n\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Outer radius of the spherical shells.\"};\n  };\n\n  struct InitialRadialRefinement {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial radial refinement level.\"};\n  };\n\n  struct InitialNumberOfRadialGridPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Initial number of radial grid points.\"};\n  };\n\n  struct InitialSphericalHarmonicL {\n    using type = size_t;\n    static size_t lower_bound() { return 6; }\n    static constexpr Options::String help = {\n        \"Initial spherical harmonic resolution specified as the highest \"\n        \"spherical harmonic represented on the grid.  Minimum value is 6.\"};\n  };\n\n  struct RadialPartitioning {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\n        \"Radial coordinates of the boundaries splitting the spherical shell \"\n        \"between InnerRadius and OuterRadius. They must be given in ascending \"\n        \"order. This should be used if boundaries need to be set at specific \"\n        \"radii. If the number but not the specific locations of the boundaries \"\n        \"are important, use InitialRefinement instead.\"};\n  };\n\n  struct RadialDistribution {\n    using type =\n        std::variant<domain::CoordinateMaps::Distribution,\n                     std::vector<domain::CoordinateMaps::Distribution>>;\n    static constexpr Options::String help = {\n        \"Select the radial distribution of grid points in each spherical \"\n        \"shell. There must be N+1 radial distributions specified for N radial \"\n        \"partitions. You can also specify just a single radial distribution \"\n        \"(not in a vector) which will use the same distribution for all \"\n        \"partitions.\"};\n  };\n\n  using TimeDepOptionType = std::variant<\n      sphere::TimeDependentMapOptions,\n      std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>>;\n\n  struct TimeDependentMaps {\n    using type = Options::Auto<TimeDepOptionType, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"The options for time dependent maps. This can either be a \"\n        \"TimeDependence or hard coded time dependent options. Specify `None` \"\n        \"for no time dependent maps.\"};\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct InnerBoundaryCondition {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions at the inner radius.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  template <typename BoundaryConditionsBase>\n  struct OuterBoundaryCondition {\n    static constexpr Options::String help =\n        \"Options for the boundary conditions at the outer radius.\";\n    using type = std::unique_ptr<BoundaryConditionsBase>;\n  };\n\n  using basic_options =\n      tmpl::list<InnerRadius, OuterRadius, InitialRadialRefinement,\n                 InitialNumberOfRadialGridPoints, InitialSphericalHarmonicL,\n                 RadialPartitioning, RadialDistribution, TimeDependentMaps>;\n\n  template <typename Metavariables>\n  using options = tmpl::conditional_t<\n      domain::BoundaryConditions::has_boundary_conditions_base_v<\n          typename Metavariables::system>,\n      tmpl::push_back<\n          basic_options,\n          InnerBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>,\n          OuterBoundaryCondition<\n              domain::BoundaryConditions::get_boundary_conditions_base<\n                  typename Metavariables::system>>>,\n      basic_options>;\n\n  static constexpr Options::String help{\n      \"A set of concentric spherical shells centered at the origin.\"};\n\n  SphericalShells(\n      double inner_radius, double outer_radius,\n      size_t initial_radial_refinement,\n      size_t initial_number_of_radial_grid_points,\n      size_t initial_spherical_harmonic_l,\n      std::vector<double> radial_partitioning = {},\n      const typename RadialDistribution::type& radial_distribution =\n          domain::CoordinateMaps::Distribution::Linear,\n      std::optional<TimeDepOptionType> time_dependent_options = std::nullopt,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          inner_boundary_condition = nullptr,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n          outer_boundary_condition = nullptr,\n      const Options::Context& context = {});\n\n  SphericalShells() = default;\n  SphericalShells(const SphericalShells&) = delete;\n  SphericalShells(SphericalShells&&) = default;\n  SphericalShells& operator=(const SphericalShells&) = delete;\n  SphericalShells& operator=(SphericalShells&&) = default;\n  ~SphericalShells() override = default;\n\n  Domain<3> create_domain() const override;\n\n  /// A single grid anchor \"Center\" at the origin.\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n  grid_anchors() const override;\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override;\n\n  std::vector<std::array<size_t, 3>> initial_extents() const override;\n\n  std::vector<std::array<size_t, 3>> initial_refinement_levels() const override;\n\n  /// The block names are Shell0, Shell1, ..., starting with the innermost\n  /// Block.\n  std::vector<std::string> block_names() const override;\n\n  /// The block groups are Shell0, Shell1, ..., starting with the innermost\n  /// Block.\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  double inner_radius_{};\n  double outer_radius_{};\n  size_t initial_radial_refinement_{};\n  size_t initial_number_of_radial_grid_points_{};\n  size_t initial_spherical_harmonic_l_{};\n  std::vector<double> radial_partitioning_{};\n  std::vector<domain::CoordinateMaps::Distribution> radial_distribution_{};\n  std::optional<TimeDepOptionType> time_dependent_options_{};\n  bool use_hard_coded_maps_{false};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      inner_boundary_condition_{};\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      outer_boundary_condition_{};\n  size_t num_blocks_{};\n  std::vector<std::string> block_names_{};\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_{};\n  std::unordered_map<std::string, tnsr::I<double, 3, Frame::Grid>>\n      grid_anchors_{};\n};\n}  // namespace domain::creators\n"
  },
  {
    "path": "src/Domain/Creators/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Domain.cpp\n  InitialExtents.cpp\n  InitialRefinementLevels.cpp\n  ObjectCenter.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Domain.hpp\n  ExternalBoundaryConditions.hpp\n  FunctionsOfTime.hpp\n  InitialExtents.hpp\n  InitialRefinementLevels.hpp\n  ObjectCenter.hpp\n  )\n"
  },
  {
    "path": "src/Domain/Creators/Tags/Domain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Tags/Domain.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace domain::Tags {\ntemplate <size_t VolumeDim>\n::Domain<VolumeDim> Domain<VolumeDim>::create_from_options(\n    const std::unique_ptr<::DomainCreator<VolumeDim>>& domain_creator) {\n  return domain_creator->create_domain();\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template ::Domain<DIM(data)> Domain<DIM(data)>::create_from_options( \\\n      const std::unique_ptr<::DomainCreator<DIM(data)>>& domain_creator);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/Domain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The ::Domain.\ntemplate <size_t VolumeDim>\nstruct Domain : db::SimpleTag {\n  using type = ::Domain<VolumeDim>;\n  using option_tags = tmpl::list<domain::OptionTags::DomainCreator<VolumeDim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static ::Domain<VolumeDim> create_from_options(\n      const std::unique_ptr<::DomainCreator<VolumeDim>>& domain_creator);\n};\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/ExternalBoundaryConditions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n\nnamespace domain::Tags {\n\n/*!\n * The boundary conditions to be applied at external boundaries. Holds an entry\n * per block, and a boundary condition per external direction.\n */\ntemplate <size_t Dim>\nstruct ExternalBoundaryConditions : db::SimpleTag {\n  using type = std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>;\n  using option_tags = tmpl::list<domain::OptionTags::DomainCreator<Dim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator) {\n    return domain_creator->external_boundary_conditions();\n  }\n};\n\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/FunctionsOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nstruct FunctionOfTime;\n}\n/// \\endcond\n\nnamespace domain::Tags {\n/// \\brief The FunctionsOfTime initialized from a DomainCreator\nstruct FunctionsOfTimeInitialize : FunctionsOfTime {\n  using base = FunctionsOfTime;\n\n  static constexpr bool pass_metavariables = true;\n\n  static std::string name() { return \"FunctionsOfTime\"; }\n\n  template <typename Metavariables>\n  using option_tags =\n      tmpl::list<domain::OptionTags::DomainCreator<Metavariables::volume_dim>>;\n\n  template <typename Metavariables>\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator) {\n    return domain_creator->functions_of_time();\n  }\n};\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/InitialExtents.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>> InitialExtents<Dim>::create_from_options(\n    const std::unique_ptr<::DomainCreator<Dim>>& domain_creator) {\n  return domain_creator->initial_extents();\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                          \\\n  template std::vector<std::array<size_t, DIM(data)>> \\\n  InitialExtents<DIM(data)>::create_from_options(     \\\n      const std::unique_ptr<::DomainCreator<DIM(data)>>& domain_creator);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/InitialExtents.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The number of grid points per dimension for all elements in each block of\n/// the initial computational domain\ntemplate <size_t Dim>\nstruct InitialExtents : db::SimpleTag {\n  using type = std::vector<std::array<size_t, Dim>>;\n  using option_tags = tmpl::list<domain::OptionTags::DomainCreator<Dim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::vector<std::array<size_t, Dim>> create_from_options(\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator);\n};\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/InitialRefinementLevels.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace domain::Tags {\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>>\nInitialRefinementLevels<Dim>::create_from_options(\n    const std::unique_ptr<::DomainCreator<Dim>>& domain_creator) {\n  return domain_creator->initial_refinement_levels();\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                               \\\n  template std::vector<std::array<size_t, DIM(data)>>      \\\n  InitialRefinementLevels<DIM(data)>::create_from_options( \\\n      const std::unique_ptr<::DomainCreator<DIM(data)>>& domain_creator);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/InitialRefinementLevels.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The initial refinement level per dimension for all elements in each block of\n/// the initial computational domain\ntemplate <size_t Dim>\nstruct InitialRefinementLevels : db::SimpleTag {\n  using type = std::vector<std::array<size_t, Dim>>;\n  using option_tags = tmpl::list<domain::OptionTags::DomainCreator<Dim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::vector<std::array<size_t, Dim>> create_from_options(\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator);\n};\n\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/ObjectCenter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace domain::Tags {\ntemplate <ObjectLabel Label>\ntnsr::I<double, 3, Frame::Grid> ObjectCenter<Label>::create_from_options(\n    const std::unique_ptr<::DomainCreator<3>>& domain_creator) {\n  const auto grid_anchors = domain_creator->grid_anchors();\n  const std::string name = \"Center\"s + get_output(Label);\n  if (grid_anchors.count(name) != 1) {\n    ERROR(\n        \"'\" << name\n            << \"' is not in the domain creators grid anchors but is needed to \"\n               \"generate the ObjectCenter<\"\n            << Label << \">.\");\n  }\n\n  return grid_anchors.at(name);\n}\n\n#define OBJECT(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) template struct ObjectCenter<OBJECT(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (ObjectLabel::A, ObjectLabel::B, ObjectLabel::None))\n\n#undef INSTANTIATE\n#undef OBJECT\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/Tags/ObjectCenter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::Tags {\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\ingroup ComputationalDomainGroup\n * \\brief The grid frame center of the given object.\n *\n * \\note Requires that the domain creator has a grid anchor with the name:\n * \"Center + get_output(Label)\"\n */\ntemplate <ObjectLabel Label>\nstruct ObjectCenter : db::SimpleTag {\n  using type = tnsr::I<double, 3, Frame::Grid>;\n  static std::string name() { return \"ObjectCenter\" + get_output(Label); }\n\n  using option_tags = tmpl::list<domain::OptionTags::DomainCreator<3>>;\n  static constexpr bool pass_metavariables = false;\n\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<3>>& domain_creator);\n};\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DomainTimeDependence)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CubicScale.cpp\n  None.cpp\n  ScalingAndZRotation.cpp\n  RegisterDerivedWithCharm.cpp\n  RotationAboutZAxis.cpp\n  Shape.cpp\n  SphericalCompression.cpp\n  UniformTranslation.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CubicScale.hpp\n  GenerateCoordinateMap.hpp\n  None.hpp\n  RegisterDerivedWithCharm.hpp\n  RotationAboutZAxis.hpp\n  ScalingAndZRotation.hpp\n  Shape.hpp\n  SphericalCompression.hpp\n  TimeDependence.hpp\n  UniformTranslation.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  CoordinateMaps\n  DataStructures\n  DomainStructure\n  FunctionsOfTime\n  Options\n  Utilities\n  PRIVATE\n  GeneralRelativity\n  ErrorHandling\n  )\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/CubicScale.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependence/CubicScale.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/MapInstantiationMacros.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\nnamespace creators::time_dependence {\ntemplate <size_t MeshDim>\nCubicScale<MeshDim>::CubicScale(const double initial_time,\n                                const double outer_boundary,\n                                bool use_linear_scaling,\n                                const std::array<double, 2>& initial_expansion,\n                                const std::array<double, 2>& velocity,\n                                const std::array<double, 2>& acceleration)\n    : initial_time_(initial_time),\n      outer_boundary_(outer_boundary),\n      use_linear_scaling_(use_linear_scaling),\n      initial_expansion_(initial_expansion),\n      velocity_(velocity),\n      acceleration_(acceleration) {\n  // If we are using linear scaling, then the names must be the same\n  if (use_linear_scaling_) {\n    functions_of_time_names_[0] = \"CubicScale\";\n    functions_of_time_names_[1] = \"CubicScale\";\n  }\n}\n\ntemplate <size_t MeshDim>\nstd::unique_ptr<TimeDependence<MeshDim>> CubicScale<MeshDim>::get_clone()\n    const {\n  return std::make_unique<CubicScale>(initial_time_, outer_boundary_,\n                                      use_linear_scaling_, initial_expansion_,\n                                      velocity_, acceleration_);\n}\n\ntemplate <size_t MeshDim>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\nCubicScale<MeshDim>::block_maps_grid_to_inertial(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0, \"Must have at least one block to create.\");\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\n      result{number_of_blocks};\n  result[0] = std::make_unique<GridToInertialMap>(grid_to_inertial_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nCubicScale<MeshDim>::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // Functions of time don't expire by default\n  std::unordered_map<std::string, double> expiration_times{};\n  for (auto& cubic_name : functions_of_time_names_) {\n    expiration_times[cubic_name] = std::numeric_limits<double>::infinity();\n  }\n\n  // If we have control systems, overwrite these expiration times with the ones\n  // supplied by the control system\n  for (auto& [name, expr_time] : initial_expiration_times) {\n    if (expiration_times.count(name) == 1) {\n      expiration_times[name] = expr_time;\n    }\n  }\n\n  // If we are using linear scaling, the function of time names will be the same\n  // so the first assignment will be overwritten by the second assignmnet.\n  // This is expected.\n  // Use a 3rd deriv function of time so that it can be used with a control\n  // system.\n  for (size_t i = 0; i < functions_of_time_names_.size(); i++) {\n    result[gsl::at(functions_of_time_names_, i)] =\n        std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n            initial_time_,\n            std::array<DataVector, 4>{{{gsl::at(initial_expansion_, i)},\n                                       {gsl::at(velocity_, i)},\n                                       {gsl::at(acceleration_, i)},\n                                       {0.0}}},\n            expiration_times.at(gsl::at(functions_of_time_names_, i)));\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim>\nauto CubicScale<MeshDim>::grid_to_inertial_map() const -> GridToInertialMap {\n  return GridToInertialMap{CubicScaleMap{outer_boundary_,\n                                         functions_of_time_names_[0],\n                                         functions_of_time_names_[1]}};\n}\n\ntemplate <size_t Dim>\nbool operator==(const CubicScale<Dim>& lhs, const CubicScale<Dim>& rhs) {\n  return lhs.initial_time_ == rhs.initial_time_ and\n         lhs.outer_boundary_ == rhs.outer_boundary_ and\n         lhs.use_linear_scaling_ == rhs.use_linear_scaling_ and\n         lhs.initial_expansion_ == rhs.initial_expansion_ and\n         lhs.velocity_ == rhs.velocity_ and\n         lhs.acceleration_ == rhs.acceleration_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const CubicScale<Dim>& lhs, const CubicScale<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                               \\\n  template class CubicScale<GET_DIM(data)>;                                  \\\n  template bool operator==<GET_DIM(data)>(const CubicScale<GET_DIM(data)>&,  \\\n                                          const CubicScale<GET_DIM(data)>&); \\\n  template bool operator!=<GET_DIM(data)>(const CubicScale<GET_DIM(data)>&,  \\\n                                          const CubicScale<GET_DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef GET_DIM\n}  // namespace creators::time_dependence\n\nINSTANTIATE_MAPS_FUNCTIONS(((CoordinateMaps::TimeDependent::CubicScale<1>),\n                            (CoordinateMaps::TimeDependent::CubicScale<2>),\n                            (CoordinateMaps::TimeDependent::CubicScale<3>)),\n                           (Frame::Grid), (Frame::Inertial),\n                           (double, DataVector))\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/CubicScale.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators::time_dependence {\n/// \\brief A linear or cubic radial scaling time dependence.\n///\n/// Adds the `domain::CoordinateMaps::TimeDependent::CubicScale` map. A linear\n/// radial scaling can be used by specifying the `UseLinearScaling` bool.\ntemplate <size_t MeshDim>\nclass CubicScale final : public TimeDependence<MeshDim> {\n private:\n  using CubicScaleMap =\n      domain::CoordinateMaps::TimeDependent::CubicScale<MeshDim>;\n\n public:\n  using maps_list =\n      tmpl::list<CoordinateMap<Frame::Grid, Frame::Inertial, CubicScaleMap>>;\n\n  static constexpr size_t mesh_dim = MeshDim;\n\n  /// \\brief The initial time of the functions of time.\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the functions of time\"};\n  };\n  /// \\brief The outer boundary or pivot point of the\n  /// `domain::CoordinateMaps::TimeDependent::CubicScale` map\n  struct OuterBoundary {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Outer boundary or pivot point of the map\"};\n  };\n  /// \\brief The initial values of the expansion factors.\n  struct InitialExpansion {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\n        \"Expansion values at initial time.\"};\n  };\n  /// \\brief The velocity of the expansion factors.\n  struct Velocity {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\"The rate of expansion.\"};\n  };\n  /// \\brief The acceleration of the expansion factors.\n  struct Acceleration {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\"The acceleration of expansion.\"};\n  };\n  /// \\brief Whether to use linear scaling or cubic scaling.\n  struct UseLinearScaling {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether or not to turn on cubic scaling.\"};\n  };\n\n  using options = tmpl::list<InitialTime, OuterBoundary, UseLinearScaling,\n                             InitialExpansion, Velocity, Acceleration>;\n\n  static constexpr Options::String help = {\n      \"A spatial radial scaling either based on a cubic scaling or a simple\\n\"\n      \"linear scaling.\\n\"\n      \"\\n\"\n      \"If the two functions of time have the same name then the scaling is a\\n\"\n      \"linear radial scaling.\"};\n\n  using GridToInertialMap =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, CubicScaleMap>;\n\n  CubicScale() = default;\n  ~CubicScale() override = default;\n  CubicScale(const CubicScale&) = delete;\n  CubicScale(CubicScale&&) = default;\n  CubicScale& operator=(const CubicScale&) = delete;\n  CubicScale& operator=(CubicScale&&) = default;\n\n  CubicScale(double initial_time, double outer_boundary,\n             bool use_linear_scaling,\n             const std::array<double, 2>& initial_expansion,\n             const std::array<double, 2>& velocity,\n             const std::array<double, 2>& acceleration);\n\n  auto get_clone() const -> std::unique_ptr<TimeDependence<MeshDim>> override;\n\n  auto block_maps_grid_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Inertial, MeshDim>>> override;\n\n  auto block_maps_grid_to_distorted(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Distorted, MeshDim>>> override {\n    using ptr_type =\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, MeshDim>;\n    return std::vector<std::unique_ptr<ptr_type>>(number_of_blocks);\n  }\n\n  auto block_maps_distorted_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Distorted, Frame::Inertial, MeshDim>>> override {\n    using ptr_type =\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, MeshDim>;\n    return std::vector<std::unique_ptr<ptr_type>>(number_of_blocks);\n  }\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const CubicScale<LocalDim>& lhs,\n                         const CubicScale<LocalDim>& rhs);\n\n  GridToInertialMap grid_to_inertial_map() const;\n\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  double outer_boundary_{std::numeric_limits<double>::signaling_NaN()};\n  bool use_linear_scaling_{false};\n  // Unlike other TimeDependences, these names aren't inline static const\n  // because they can potentially be changed by the run-time option\n  // use_linear_scaling in the constructor\n  std::array<std::string, 2> functions_of_time_names_{\n      {\"CubicScaleA\", \"CubicScaleB\"}};\n  std::array<double, 2> initial_expansion_{};\n  std::array<double, 2> velocity_{};\n  std::array<double, 2> acceleration_{};\n};\n\ntemplate <size_t Dim>\nbool operator==(const CubicScale<Dim>& lhs, const CubicScale<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const CubicScale<Dim>& lhs, const CubicScale<Dim>& rhs);\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/GenerateCoordinateMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain::creators::time_dependence::detail {\ntemplate <typename SourceFrame, typename TargetFrame, typename MapsList>\nstruct generate_coordinate_map;\n\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nstruct generate_coordinate_map<SourceFrame, TargetFrame, tmpl::list<Maps...>> {\n  using type = domain::CoordinateMap<SourceFrame, TargetFrame, Maps...>;\n};\n\ntemplate <typename SourceFrame, typename TargetFrame, typename MapsList>\nusing generate_coordinate_map_t =\n    typename generate_coordinate_map<SourceFrame, TargetFrame, MapsList>::type;\n}  // namespace domain::creators::time_dependence::detail\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/None.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace domain::creators::time_dependence {\ntemplate <size_t MeshDim>\nstd::unique_ptr<TimeDependence<MeshDim>> None<MeshDim>::get_clone() const {\n  return std::make_unique<None>(*this);\n}\n\ntemplate <size_t MeshDim>\n[[noreturn]] std::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\nNone<MeshDim>::block_maps_grid_to_inertial(\n    const size_t /*number_of_blocks*/) const {\n  ERROR(\n      \"The 'block_maps_grid_to_inertial' function of the 'None' TimeDependence \"\n      \"should never be called because 'None' is only used as a place holder \"\n      \"class to mark that the mesh is time-independent.\");\n}\n\ntemplate <size_t MeshDim>\n[[noreturn]] std::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, MeshDim>>>\nNone<MeshDim>::block_maps_grid_to_distorted(\n    const size_t /*number_of_blocks*/) const {\n  ERROR(\n      \"The 'block_maps_grid_to_distorted' function of the 'None' \"\n      \"TimeDependence should never be called because 'None' is only used as a \"\n      \"place holder class to mark that the mesh is time-independent.\");\n}\n\ntemplate <size_t MeshDim>\n[[noreturn]] std::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, MeshDim>>>\nNone<MeshDim>::block_maps_distorted_to_inertial(\n    const size_t /*number_of_blocks*/) const {\n  ERROR(\n      \"The 'block_maps_distorted_to_inertial' function of the 'None' \"\n      \"TimeDependence should never be called because 'None' is only used as a \"\n      \"place holder class to mark that the mesh is time-independent.\");\n}\n\ntemplate <size_t MeshDim>\n[[noreturn]] std::unordered_map<\n    std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nNone<MeshDim>::functions_of_time(const std::unordered_map<std::string, double>&\n                                 /*initial_expiration_times*/) const {\n  ERROR(\n      \"The 'functions_of_time' function of the 'None' TimeDependence should \"\n      \"never be called because 'None' is only used as a place holder class to \"\n      \"mark that the mesh is time-independent.\");\n}\n\ntemplate <size_t Dim>\nbool operator==(const None<Dim>& /*lhs*/, const None<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const None<Dim>& lhs, const None<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template class None<GET_DIM(data)>;                                          \\\n  template bool operator==                                                     \\\n      <GET_DIM(data)>(const None<GET_DIM(data)>&, const None<GET_DIM(data)>&); \\\n  template bool operator!=                                                     \\\n      <GET_DIM(data)>(const None<GET_DIM(data)>&, const None<GET_DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/None.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators::time_dependence {\n/// \\brief Make the mesh time independent so that it isn't moving.\n///\n/// \\warning Calling the `block_maps` and `functions_of_time` functions causes\n/// an error because the `None` class should be detected separately and\n/// optimizations applied so that the coordinates, Jacobians, etc. are not\n/// recomputed.\ntemplate <size_t MeshDim>\nclass None final : public TimeDependence<MeshDim> {\n public:\n  using maps_list = tmpl::list<>;\n  using options = tmpl::list<>;\n\n  static constexpr Options::String help = {\n      \"No time dependence in the in grid.\"};\n\n  None() = default;\n  ~None() override = default;\n  None(const None&) = default;\n  None(None&&) = default;\n  None& operator=(const None&) = default;\n  None& operator=(None&&) = default;\n\n  auto get_clone() const -> std::unique_ptr<TimeDependence<MeshDim>> override;\n\n  [[noreturn]] auto block_maps_grid_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Inertial, MeshDim>>> override;\n\n  [[noreturn]] auto block_maps_grid_to_distorted(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Distorted, MeshDim>>> override;\n\n  [[noreturn]] auto block_maps_distorted_to_inertial(size_t number_of_blocks)\n      const -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Distorted, Frame::Inertial, MeshDim>>> override;\n\n  [[noreturn]] auto functions_of_time(\n      const std::unordered_map<std::string, double>& initial_expiration_times =\n          {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n};\n\ntemplate <size_t Dim>\nbool operator==(const None<Dim>& lhs, const None<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const None<Dim>& lhs, const None<Dim>& rhs);\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/TimeDependence/CubicScale.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/RotationAboutZAxis.hpp\"\n#include \"Domain/Creators/TimeDependence/ScalingAndZRotation.hpp\"\n#include \"Domain/Creators/TimeDependence/Shape.hpp\"\n#include \"Domain/Creators/TimeDependence/SphericalCompression.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependence {\nnamespace {\ntemplate <typename TimeDep>\nstruct get_maps {\n  using type = typename TimeDep::maps_list;\n};\n\ntemplate <size_t Dim>\nvoid register_maps_with_charm() {\n  using maps_to_register =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::push_back<\n          tmpl::transform<typename TimeDependence<Dim>::creatable_classes,\n                          get_maps<tmpl::_1>>,\n          domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                CoordinateMaps::Identity<Dim>>>>>;\n\n  register_classes_with_charm(maps_to_register{});\n}\n}  // namespace\n\nvoid register_derived_with_charm() {\n  register_maps_with_charm<1>();\n  register_maps_with_charm<2>();\n  register_maps_with_charm<3>();\n}\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace domain {\nnamespace creators {\nnamespace time_dependence {\nvoid register_derived_with_charm();\n}  // namespace time_dependence\n}  // namespace creators\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/RotationAboutZAxis.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependence/RotationAboutZAxis.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/MapInstantiationMacros.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\nnamespace creators::time_dependence {\n\ntemplate <size_t MeshDim>\nRotationAboutZAxis<MeshDim>::RotationAboutZAxis(\n    double initial_time, double initial_angle, double initial_angular_velocity,\n    double initial_angular_acceleration)\n    : initial_time_(initial_time),\n      initial_angle_(initial_angle),\n      initial_angular_velocity_(initial_angular_velocity),\n      initial_angular_acceleration_(initial_angular_acceleration) {}\n\ntemplate <size_t MeshDim>\nstd::unique_ptr<TimeDependence<MeshDim>>\nRotationAboutZAxis<MeshDim>::get_clone() const {\n  return std::make_unique<RotationAboutZAxis>(initial_time_, initial_angle_,\n                                              initial_angular_velocity_,\n                                              initial_angular_acceleration_);\n}\n\ntemplate <size_t MeshDim>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\nRotationAboutZAxis<MeshDim>::block_maps_grid_to_inertial(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0, \"Must have at least one block to create.\");\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\n      result{number_of_blocks};\n  result[0] = std::make_unique<GridToInertialMap>(grid_to_inertial_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nRotationAboutZAxis<MeshDim>::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // Functions of time don't expire by default\n  double expiration_time = std::numeric_limits<double>::infinity();\n\n  // If we have control systems, overwrite the expiration time with the one\n  // supplied by the control system\n  if (initial_expiration_times.count(function_of_time_name_) == 1) {\n    expiration_time = initial_expiration_times.at(function_of_time_name_);\n  }\n\n  // We use a third-order `PiecewisePolynomial` to ensure sufficiently\n  // smooth behavior of the function of time\n  result[function_of_time_name_] =\n      std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n          initial_time_,\n          std::array<DataVector, 4>{{{initial_angle_},\n                                     {initial_angular_velocity_},\n                                     {initial_angular_acceleration_},\n                                     {0.0}}},\n          expiration_time);\n  return result;\n}\n\ntemplate <>\nauto RotationAboutZAxis<2>::grid_to_inertial_map() const -> GridToInertialMap {\n  return GridToInertialMap{domain::CoordinateMaps::TimeDependent::Rotation<2>{\n      function_of_time_name_}};\n}\n\ntemplate <>\nauto RotationAboutZAxis<3>::grid_to_inertial_map() const -> GridToInertialMap {\n  using ProductMap = domain::CoordinateMaps::TimeDependent::ProductOf2Maps<\n      domain::CoordinateMaps::TimeDependent::Rotation<2>,\n      domain::CoordinateMaps::Identity<1>>;\n  return GridToInertialMap{\n      ProductMap{domain::CoordinateMaps::TimeDependent::Rotation<2>{\n                     function_of_time_name_},\n                 domain::CoordinateMaps::Identity<1>{}}};\n}\n\ntemplate <size_t Dim>\nbool operator==(const RotationAboutZAxis<Dim>& lhs,\n                const RotationAboutZAxis<Dim>& rhs) {\n  return lhs.initial_time_ == rhs.initial_time_ and\n         lhs.initial_angle_ == rhs.initial_angle_ and\n         lhs.initial_angular_velocity_ == rhs.initial_angular_velocity_ and\n         lhs.initial_angular_acceleration_ == rhs.initial_angular_acceleration_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const RotationAboutZAxis<Dim>& lhs,\n                const RotationAboutZAxis<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                   \\\n  template class RotationAboutZAxis<GET_DIM(data)>;              \\\n  template bool operator==                                       \\\n      <GET_DIM(data)>(const RotationAboutZAxis<GET_DIM(data)>&,  \\\n                      const RotationAboutZAxis<GET_DIM(data)>&); \\\n  template bool operator!=                                       \\\n      <GET_DIM(data)>(const RotationAboutZAxis<GET_DIM(data)>&,  \\\n                      const RotationAboutZAxis<GET_DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace creators::time_dependence\n\nusing Identity = CoordinateMaps::Identity<1>;\nusing Rotation2d = CoordinateMaps::TimeDependent::Rotation<2>;\nusing Rotation3d =\n    CoordinateMaps::TimeDependent::ProductOf2Maps<Rotation2d, Identity>;\n\ntemplate class CoordinateMaps::TimeDependent::ProductOf2Maps<Rotation2d,\n                                                             Identity>;\n\nINSTANTIATE_MAPS_FUNCTIONS(((Rotation2d), (Rotation3d)), (Frame::Grid),\n                           (Frame::Inertial), (double, DataVector))\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/RotationAboutZAxis.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/Creators/TimeDependence/GenerateCoordinateMap.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace domain::CoordinateMaps::TimeDependent {\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\n}  // namespace domain::CoordinateMaps::TimeDependent\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators::time_dependence {\n/*!\n * \\brief A spatially uniform rotation about the \\f$z\\f$ axis:\n * \\f{eqnarray*}\n * x &\\to& x \\cos \\alpha(t) - y \\sin \\alpha(t)\\text{,} \\\\\n * y &\\to& x \\sin \\alpha(t) + y \\cos \\alpha(t)\\text{,}\n * \\f}\n * where \\f$\\alpha(t)\\f$ is a `domain::FunctionsOfTime::FunctionOfTime`. For 3\n * spatial dimensions, \\f$z \\to z\\f$, and the rotation is implemented as a\n * product of the 2D rotation and an identity map. The rotation is undefined\n * (and therefore unimplemented here) for 1 spatial dimension.\n */\ntemplate <size_t MeshDim>\nclass RotationAboutZAxis final : public TimeDependence<MeshDim> {\n  static_assert(MeshDim > 1,\n                \"RotationAboutZAxis<MeshDim> undefined for MeshDim == 1\");\n\n private:\n  using Identity = domain::CoordinateMaps::Identity<1>;\n  using Rotation = domain::CoordinateMaps::TimeDependent::Rotation<2>;\n\n public:\n  using maps_list = tmpl::list<\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, Rotation>,\n      domain::CoordinateMap<\n          Frame::Grid, Frame::Inertial,\n          CoordinateMaps::TimeDependent::ProductOf2Maps<Rotation, Identity>>>;\n\n  static constexpr size_t mesh_dim = MeshDim;\n\n  /// \\brief The initial time of the function of time.\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the function of time\"};\n  };\n  struct InitialAngle {\n    using type = double;\n    static constexpr Options::String help = {\"The initial angle.\"};\n  };\n  /// \\brief The \\f$x\\f$-, \\f$y\\f$-, and \\f$z\\f$-velocity.\n  struct InitialAngularVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial angular velocity of the map.\"};\n  };\n  struct InitialAngularAcceleration {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial angular acceleration of the map.\"};\n  };\n\n  using GridToInertialMap = detail::generate_coordinate_map_t<\n      Frame::Grid, Frame::Inertial,\n      tmpl::list<tmpl::conditional_t<MeshDim == 2, Rotation,\n                                     domain::CoordinateMaps::TimeDependent::\n                                         ProductOf2Maps<Rotation, Identity>>>>;\n\n  using options = tmpl::list<InitialTime, InitialAngle, InitialAngularVelocity,\n                             InitialAngularAcceleration>;\n\n  static constexpr Options::String help = {\n      \"A spatially uniform rotation about the z axis initialized with a \"\n      \"constant angular velocity.\"};\n\n  RotationAboutZAxis() = default;\n  ~RotationAboutZAxis() override = default;\n  RotationAboutZAxis(const RotationAboutZAxis&) = delete;\n  RotationAboutZAxis(RotationAboutZAxis&&) = default;\n  RotationAboutZAxis& operator=(const RotationAboutZAxis&) = delete;\n  RotationAboutZAxis& operator=(RotationAboutZAxis&&) = default;\n\n  RotationAboutZAxis(double initial_time, double initial_angle,\n                     double initial_angular_velocity,\n                     double initial_angular_acceleration);\n\n  auto get_clone() const -> std::unique_ptr<TimeDependence<MeshDim>> override;\n\n  auto block_maps_grid_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Inertial, MeshDim>>> override;\n\n  auto block_maps_grid_to_distorted(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Distorted, MeshDim>>> override {\n    using ptr_type =\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, MeshDim>;\n    return std::vector<std::unique_ptr<ptr_type>>(number_of_blocks);\n  }\n\n  auto block_maps_distorted_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Distorted, Frame::Inertial, MeshDim>>> override {\n    using ptr_type =\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, MeshDim>;\n    return std::vector<std::unique_ptr<ptr_type>>(number_of_blocks);\n  }\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const RotationAboutZAxis<LocalDim>& lhs,\n                         const RotationAboutZAxis<LocalDim>& rhs);\n\n  GridToInertialMap grid_to_inertial_map() const;\n\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  double initial_angle_{std::numeric_limits<double>::signaling_NaN()};\n  double initial_angular_velocity_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double initial_angular_acceleration_{\n      std::numeric_limits<double>::signaling_NaN()};\n  inline static const std::string function_of_time_name_{\"Rotation\"};\n};\n\ntemplate <size_t Dim>\nbool operator==(const RotationAboutZAxis<Dim>& lhs,\n                const RotationAboutZAxis<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const RotationAboutZAxis<Dim>& lhs,\n                const RotationAboutZAxis<Dim>& rhs);\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/ScalingAndZRotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependence/ScalingAndZRotation.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/MapInstantiationMacros.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain::creators::time_dependence {\n\ntemplate <size_t MeshDim>\nScalingAndZRotation<MeshDim>::ScalingAndZRotation(\n    const double initial_time, const double angular_velocity,\n    const double outer_boundary, bool use_linear_scaling,\n    const std::array<double, 2>& initial_expansion,\n    const std::array<double, 2>& velocity,\n    const std::array<double, 2>& acceleration)\n    : initial_time_(initial_time),\n      angular_velocity_(angular_velocity),\n      outer_boundary_(outer_boundary),\n      use_linear_scaling_(use_linear_scaling),\n      initial_expansion_(initial_expansion),\n      velocity_(velocity),\n      acceleration_(acceleration) {\n  // If we are using linear scaling, then CubicScale names must be the same\n  if (use_linear_scaling_) {\n    functions_of_time_names_[0] = \"CubicScale\";\n    functions_of_time_names_[1] = \"CubicScale\";\n  }\n}\n\ntemplate <size_t MeshDim>\nstd::unique_ptr<TimeDependence<MeshDim>>\nScalingAndZRotation<MeshDim>::get_clone() const {\n  return std::make_unique<ScalingAndZRotation>(\n      initial_time_, angular_velocity_, outer_boundary_, use_linear_scaling_,\n      initial_expansion_, velocity_, acceleration_);\n}\n\ntemplate <size_t MeshDim>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\nScalingAndZRotation<MeshDim>::block_maps_grid_to_inertial(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0, \"Must have at least one block to create.\");\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\n      result{number_of_blocks};\n  result[0] = std::make_unique<GridToInertialMap>(grid_to_inertial_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, MeshDim>>>\nScalingAndZRotation<MeshDim>::block_maps_grid_to_distorted(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0, \"Must have at least one block to create.\");\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, MeshDim>>>\n      result{number_of_blocks};\n  result[0] = std::make_unique<GridToDistortedMap>(grid_to_distorted_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, MeshDim>>>\nScalingAndZRotation<MeshDim>::block_maps_distorted_to_inertial(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0, \"Must have at least one block to create.\");\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, MeshDim>>>\n      result{number_of_blocks};\n  result[0] =\n      std::make_unique<DistortedToInertialMap>(distorted_to_inertial_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nScalingAndZRotation<MeshDim>::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // Functions of time don't expire by default\n  std::unordered_map<std::string, double> expiration_times{};\n  for (auto& this_name : functions_of_time_names_) {\n    expiration_times[this_name] = std::numeric_limits<double>::infinity();\n  }\n\n  // If we have control systems, overwrite these expiration times with the ones\n  // supplied by the control system\n  for (auto& [name, expr_time] : initial_expiration_times) {\n    if (expiration_times.count(name) == 1) {\n      expiration_times[name] = expr_time;\n    }\n  }\n\n  result[functions_of_time_names_[2]] =\n      std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n          initial_time_,\n          std::array<DataVector, 4>{{{0.0}, {angular_velocity_}, {0.0}, {0.0}}},\n          expiration_times[functions_of_time_names_[2]]);\n  // If we are using linear scaling, the scaling function of time\n  // names will be the same so the first assignment will be\n  // overwritten by the second assignment.  This is expected.  Use a\n  // 3rd deriv function of time so that it can be used with a control\n  // system.\n  for (size_t i = 0; i < functions_of_time_names_.size() - 1; i++) {\n    result[gsl::at(functions_of_time_names_, i)] =\n        std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n            initial_time_,\n            std::array<DataVector, 4>{{{gsl::at(initial_expansion_, i)},\n                                       {gsl::at(velocity_, i)},\n                                       {gsl::at(acceleration_, i)},\n                                       {0.0}}},\n            expiration_times.at(gsl::at(functions_of_time_names_, i)));\n  }\n  return result;\n}\n\ntemplate <>\nauto ScalingAndZRotation<2>::grid_to_inertial_map() const -> GridToInertialMap {\n  return GridToInertialMap{\n      CubicScaleMap{outer_boundary_, functions_of_time_names_[0],\n                    functions_of_time_names_[1]},\n      domain::CoordinateMaps::TimeDependent::Rotation<2>{\n          functions_of_time_names_[2]}};\n}\n\ntemplate <>\nauto ScalingAndZRotation<3>::grid_to_inertial_map() const -> GridToInertialMap {\n  using ProductRotationMap =\n      domain::CoordinateMaps::TimeDependent::ProductOf2Maps<\n          domain::CoordinateMaps::TimeDependent::Rotation<2>,\n          domain::CoordinateMaps::Identity<1>>;\n  return GridToInertialMap{\n      CubicScaleMap{outer_boundary_, functions_of_time_names_[0],\n                    functions_of_time_names_[1]},\n      ProductRotationMap{domain::CoordinateMaps::TimeDependent::Rotation<2>{\n                             functions_of_time_names_[2]},\n                         domain::CoordinateMaps::Identity<1>{}}};\n}\n\ntemplate <size_t MeshDim>\nauto ScalingAndZRotation<MeshDim>::grid_to_distorted_map() const\n    -> GridToDistortedMap {\n  return GridToDistortedMap{CubicScaleMap{outer_boundary_,\n                                          functions_of_time_names_[0],\n                                          functions_of_time_names_[1]}};\n}\n\ntemplate <>\nauto ScalingAndZRotation<2>::distorted_to_inertial_map() const\n    -> DistortedToInertialMap {\n  return DistortedToInertialMap{\n      domain::CoordinateMaps::TimeDependent::Rotation<2>{\n          functions_of_time_names_[2]}};\n}\n\ntemplate <>\nauto ScalingAndZRotation<3>::distorted_to_inertial_map() const\n    -> DistortedToInertialMap {\n  using ProductRotationMap =\n      domain::CoordinateMaps::TimeDependent::ProductOf2Maps<\n          domain::CoordinateMaps::TimeDependent::Rotation<2>,\n          domain::CoordinateMaps::Identity<1>>;\n  return DistortedToInertialMap{\n      ProductRotationMap{domain::CoordinateMaps::TimeDependent::Rotation<2>{\n                             functions_of_time_names_[2]},\n                         domain::CoordinateMaps::Identity<1>{}}};\n}\n\ntemplate <size_t Dim>\nbool operator==(const ScalingAndZRotation<Dim>& lhs,\n                const ScalingAndZRotation<Dim>& rhs) {\n  return lhs.initial_time_ == rhs.initial_time_ and\n         lhs.angular_velocity_ == rhs.angular_velocity_ and\n         lhs.outer_boundary_ == rhs.outer_boundary_ and\n         lhs.use_linear_scaling_ == rhs.use_linear_scaling_ and\n         lhs.initial_expansion_ == rhs.initial_expansion_ and\n         lhs.velocity_ == rhs.velocity_ and\n         lhs.acceleration_ == rhs.acceleration_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const ScalingAndZRotation<Dim>& lhs,\n                const ScalingAndZRotation<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                    \\\n  template class ScalingAndZRotation<GET_DIM(data)>;              \\\n  template bool operator==                                        \\\n      <GET_DIM(data)>(const ScalingAndZRotation<GET_DIM(data)>&,  \\\n                      const ScalingAndZRotation<GET_DIM(data)>&); \\\n  template bool operator!=                                        \\\n      <GET_DIM(data)>(const ScalingAndZRotation<GET_DIM(data)>&,  \\\n                      const ScalingAndZRotation<GET_DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (2, 3))\n\n#undef INSTANTIATION\n#undef GET_DIM\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/ScalingAndZRotation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/Creators/TimeDependence/GenerateCoordinateMap.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators::time_dependence {\n/*!\n * \\brief Cubic scaling, followed by uniform rotation about the \\f$z\\f$ axis:\n * \\f{eqnarray*}\n * x &\\to& x \\cos \\alpha(t) - y \\sin \\alpha(t)\\text{,} \\\\\n * y &\\to& x \\sin \\alpha(t) + y \\cos \\alpha(t)\\text{,}\n * \\f}\n * where \\f$\\alpha(t)\\f$ is a `domain::FunctionsOfTime::FunctionOfTime`. For 3\n * spatial dimensions, \\f$z \\to z\\f$, and the rotation is implemented as a\n * product of the 2D rotation and an identity map. The rotation is undefined\n * (and therefore unimplemented here) for 1 spatial dimension.\n *\n * The expansion is done by the\n * `domain::CoordinateMaps::TimeDependent::CubicScale` map. A linear\n * radial scaling can be used by specifying the `UseLinearScaling` bool.\n *\n * For this map, the cubic scaling goes from the grid frame to the distorted\n * frame, and the rotation goes from the distorted frame to the inertial frame.\n * This was chosen as a way of testing composed maps in the distorted frame.\n */\ntemplate <size_t MeshDim>\nclass ScalingAndZRotation final : public TimeDependence<MeshDim> {\n  static_assert(\n      MeshDim > 1,\n      \"ScalingAndZRotation<MeshDim> undefined for MeshDim == 1\");\n\n private:\n  using Identity = domain::CoordinateMaps::Identity<1>;\n  using Rotation = domain::CoordinateMaps::TimeDependent::Rotation<2>;\n  using Rotation3D =\n      domain::CoordinateMaps::TimeDependent::ProductOf2Maps<Rotation, Identity>;\n  using CubicScaleMap =\n      domain::CoordinateMaps::TimeDependent::CubicScale<MeshDim>;\n\n public:\n  using maps_list = tmpl::flatten<tmpl::list<\n      domain::CoordinateMap<Frame::Grid, Frame::Distorted, CubicScaleMap>,\n      tmpl::conditional_t<\n          MeshDim == 2,\n          tmpl::list<domain::CoordinateMap<Frame::Distorted, Frame::Inertial,\n                                           Rotation>,\n                     domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                           CubicScaleMap, Rotation>>,\n          tmpl::list<domain::CoordinateMap<Frame::Distorted, Frame::Inertial,\n                                           Rotation3D>,\n                     domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                           CubicScaleMap, Rotation3D>>>>>;\n\n  static constexpr size_t mesh_dim = MeshDim;\n\n  /// \\brief The initial time of the function of time.\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the function of time\"};\n  };\n  /// \\brief The \\f$x\\f$-, \\f$y\\f$-, and \\f$z\\f$-velocity.\n  struct AngularVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The angular velocity of the map.\"};\n  };\n\n  /// \\brief The outer boundary or pivot point of the\n  /// `domain::CoordinateMaps::TimeDependent::CubicScale` map\n  struct OuterBoundary {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Outer boundary or pivot point of the map\"};\n  };\n  /// \\brief The initial values of the expansion factors.\n  struct InitialExpansion {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\n        \"Expansion values at initial time.\"};\n  };\n  /// \\brief The velocity of the expansion factors.\n  struct Velocity {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\"The rate of expansion.\"};\n  };\n  /// \\brief The acceleration of the expansion factors.\n  struct Acceleration {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\"The acceleration of expansion.\"};\n  };\n  /// \\brief Whether to use linear scaling or cubic scaling.\n  struct UseLinearScaling {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether or not to turn on cubic scaling.\"};\n  };\n\n  using GridToInertialMap = detail::generate_coordinate_map_t<\n      Frame::Grid, Frame::Inertial,\n      tmpl::list<CubicScaleMap,\n                 tmpl::conditional_t<MeshDim == 2, Rotation,\n                                     domain::CoordinateMaps::TimeDependent::\n                                         ProductOf2Maps<Rotation, Identity>>>>;\n  using GridToDistortedMap =\n      domain::CoordinateMap<Frame::Grid, Frame::Distorted, CubicScaleMap>;\n\n  using DistortedToInertialMap = detail::generate_coordinate_map_t<\n      Frame::Distorted, Frame::Inertial,\n      tmpl::list<tmpl::conditional_t<MeshDim == 2, Rotation,\n                                     domain::CoordinateMaps::TimeDependent::\n                                         ProductOf2Maps<Rotation, Identity>>>>;\n  using options =\n      tmpl::list<InitialTime, AngularVelocity, OuterBoundary, UseLinearScaling,\n                 InitialExpansion, Velocity, Acceleration>;\n\n  static constexpr Options::String help = {\n      \"A spatial radial scaling followed by a rotation about the z-axis.\\n\"\n      \"The spatial radial scaling is either based on a cubic scaling or a\\n\"\n      \"simple linear scaling. If the two expansion functions of time have\\n\"\n      \"the same name then the scaling is a linear radial scaling.\\n\"\n      \"The spatially uniform rotation about is initialized with a\\n\"\n      \"constant angular velocity.\"};\n\n  ScalingAndZRotation() = default;\n  ~ScalingAndZRotation() override = default;\n  ScalingAndZRotation(const ScalingAndZRotation&) = delete;\n  ScalingAndZRotation(ScalingAndZRotation&&) = default;\n  ScalingAndZRotation& operator=(const ScalingAndZRotation&) = delete;\n  ScalingAndZRotation& operator=(ScalingAndZRotation&&) = default;\n\n  ScalingAndZRotation(double initial_time, double angular_velocity,\n                      double outer_boundary, bool use_linear_scaling,\n                      const std::array<double, 2>& initial_expansion,\n                      const std::array<double, 2>& velocity,\n                      const std::array<double, 2>& acceleration);\n\n  auto get_clone() const -> std::unique_ptr<TimeDependence<MeshDim>> override;\n\n  auto block_maps_grid_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Inertial, MeshDim>>> override;\n\n  auto block_maps_grid_to_distorted(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Distorted, MeshDim>>> override;\n\n  auto block_maps_distorted_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Distorted, Frame::Inertial, MeshDim>>> override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const ScalingAndZRotation<LocalDim>& lhs,\n                         const ScalingAndZRotation<LocalDim>& rhs);\n\n  GridToInertialMap grid_to_inertial_map() const;\n  GridToDistortedMap grid_to_distorted_map() const;\n  DistortedToInertialMap distorted_to_inertial_map() const;\n\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  double angular_velocity_{std::numeric_limits<double>::signaling_NaN()};\n  double outer_boundary_{std::numeric_limits<double>::signaling_NaN()};\n  bool use_linear_scaling_{false};\n  std::array<double, 2> initial_expansion_{};\n  std::array<double, 2> velocity_{};\n  std::array<double, 2> acceleration_{};\n  // Unlike other TimeDependences, these names aren't inline static const\n  // because they can potentially be changed by the run-time option\n  // use_linear_scaling in the constructor\n  std::array<std::string, 3> functions_of_time_names_{\n      {\"CubicScaleA\", \"CubicScaleB\", \"Rotation\"}};\n};\n\ntemplate <size_t Dim>\nbool operator==(const ScalingAndZRotation<Dim>& lhs,\n                const ScalingAndZRotation<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const ScalingAndZRotation<Dim>& lhs,\n                const ScalingAndZRotation<Dim>& rhs);\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/Shape.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependence/Shape.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/MapInstantiationMacros.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain {\nnamespace creators::time_dependence {\nusing SphereTransition =\n    domain::CoordinateMaps::ShapeMapTransitionFunctions::SphereTransition;\n\ntemplate <domain::ObjectLabel Label>\nShape<Label>::Shape(const double initial_time, const size_t l_max,\n                    const double mass, const std::array<double, 3> spin,\n                    const std::array<double, 3> center,\n                    const double coefficient_truncation_limit,\n                    const double inner_radius, const double outer_radius,\n                    const Options::Context& context)\n    : initial_time_(initial_time),\n      l_max_(l_max),\n      mass_(mass),\n      spin_(spin),\n      center_(center),\n      coefficient_truncation_limit_(coefficient_truncation_limit),\n      inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      transition_func_(std::make_unique<SphereTransition>(\n          SphereTransition{inner_radius, outer_radius})) {\n  using ::operator<<;\n  if (mass <= 0.0) {\n    PARSE_ERROR(context,\n                \"Tried to create a Shape TimeDependence, but \"\n                \"the mass (\"\n                    << mass << \") must be strictly positive.\");\n  }\n  if (magnitude(spin) >= 1.0) {\n    PARSE_ERROR(context,\n                \"Tried to create a Shape TimeDependence, but \"\n                \"the magnitude of the spin (\"\n                    << spin << \") is greater than one.\");\n  }\n  if (coefficient_truncation_limit_ < 0.0) {\n    PARSE_ERROR(context,\n                \"CoefficientTruncationLimit must be non-negative, but is \"\n                    << coefficient_truncation_limit_ << \".\");\n  }\n  // There is no PARSE_ERROR for the `inner_radius` < `outer_radius` condition\n  // because the SphereTransition already checks for this condition.\n}\n\ntemplate <domain::ObjectLabel Label>\nstd::unique_ptr<TimeDependence<Shape<Label>::mesh_dim>>\nShape<Label>::get_clone() const {\n  return std::make_unique<Shape<Label>>(initial_time_, l_max_, mass_, spin_,\n                                        center_, coefficient_truncation_limit_,\n                                        inner_radius_, outer_radius_);\n}\n\ntemplate <domain::ObjectLabel Label>\nstd::vector<std::unique_ptr<domain::CoordinateMapBase<\n    Frame::Grid, Frame::Inertial, Shape<Label>::mesh_dim>>>\nShape<Label>::block_maps_grid_to_inertial(const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0,\n         \"Must have at least one block on which to create a map.\");\n\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n      result{number_of_blocks};\n  result[0] = std::make_unique<GridToInertialMap>(grid_to_inertial_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <domain::ObjectLabel Label>\nstd::vector<std::unique_ptr<domain::CoordinateMapBase<\n    Frame::Grid, Frame::Distorted, Shape<Label>::mesh_dim>>>\nShape<Label>::block_maps_grid_to_distorted(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0,\n         \"Must have at least one block on which to create a map.\");\n\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n      result{number_of_blocks};\n  result[0] = std::make_unique<GridToDistortedMap>(grid_to_distorted_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <domain::ObjectLabel Label>\nstd::vector<std::unique_ptr<domain::CoordinateMapBase<\n    Frame::Distorted, Frame::Inertial, Shape<Label>::mesh_dim>>>\nShape<Label>::block_maps_distorted_to_inertial(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0,\n         \"Must have at least one block on which to create a map.\");\n\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n      result{number_of_blocks};\n  result[0] =\n      std::make_unique<DistortedToInertialMap>(distorted_to_inertial_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <domain::ObjectLabel Label>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nShape<Label>::functions_of_time(const std::unordered_map<std::string, double>&\n                                    initial_expiration_times) const {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // Functions of time don't expire by default\n  double expiration_time = std::numeric_limits<double>::infinity();\n\n  // If we have control systems, overwrite the expiration time with the one\n  // supplied by the control system\n  if (initial_expiration_times.count(function_of_time_name_) == 1) {\n    expiration_time = initial_expiration_times.at(function_of_time_name_);\n  }\n\n  const ylm::Spherepack ylm{l_max_, l_max_};\n  // The lambda_lm coefs actually have dimensions same as radius.\n  const DataVector radial_distortion =\n      inner_radius_ -\n      get(gr::Solutions::kerr_schild_radius_from_boyer_lindquist(\n          inner_radius_, ylm.theta_phi_points(), mass_, spin_));\n  const auto radial_distortion_coefs = ylm.phys_to_spec(radial_distortion);\n  const DataVector zeros =\n      make_with_value<DataVector>(radial_distortion_coefs, 0.0);\n  result[function_of_time_name_] =\n      std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n          initial_time_,\n          std::array<DataVector, 4>{\n              {radial_distortion_coefs, zeros, zeros, zeros}},\n          expiration_time);\n  // This adds a size function of time that is the l=0 component of the radial\n  // distortion coefficients. We do this so that this time dependence can be\n  // used with the Shape control system. The control error for the shape control\n  // system requires there to be two functions of time in the cache: one for\n  // shape and one for size. This size function of time isn't controlling any\n  // maps so it never expires and is constant the entire time. If we want this\n  // time dependence to work with size control as well, we'll have to add in the\n  // size map as well.\n  const DataVector zeros_size{1, 0.0};\n  const std::string size_name = \"Size\"s + get_output(Label);\n  result[size_name] = std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n      initial_time_,\n      std::array<DataVector, 4>{\n          // Size holds the *actual* \\lambda_00 spherical harmonic coefficient,\n          // but shape holds Spherepack coefficients so we must convert between\n          // the two. Need to multiply lambda_00 by sqrt(pi/2)\n          {{sqrt(0.5 * M_PI) * radial_distortion_coefs[0]},\n           zeros_size,\n           zeros_size,\n           zeros_size}},\n      std::numeric_limits<double>::infinity());\n  return result;\n}\n\ntemplate <domain::ObjectLabel Label>\nauto Shape<Label>::grid_to_inertial_map() const -> GridToInertialMap {\n  return GridToInertialMap{ShapeMap{center_, coefficient_truncation_limit_,\n                                    transition_func_->get_clone(),\n                                    function_of_time_name_}};\n}\n\ntemplate <domain::ObjectLabel Label>\nauto Shape<Label>::grid_to_distorted_map() const -> GridToDistortedMap {\n  return GridToDistortedMap{ShapeMap{center_, coefficient_truncation_limit_,\n                                     transition_func_->get_clone(),\n                                     function_of_time_name_}};\n}\n\ntemplate <domain::ObjectLabel Label>\nauto Shape<Label>::distorted_to_inertial_map() -> DistortedToInertialMap {\n  return DistortedToInertialMap{Identity{}};\n}\n\ntemplate <domain::ObjectLabel Label>\nbool operator==(const Shape<Label>& lhs, const Shape<Label>& rhs) {\n  return lhs.initial_time_ == rhs.initial_time_ and lhs.l_max_ == rhs.l_max_ and\n         lhs.mass_ == rhs.mass_ and lhs.spin_ == rhs.spin_ and\n         lhs.center_ == rhs.center_ and\n         lhs.coefficient_truncation_limit_ ==\n             rhs.coefficient_truncation_limit_ and\n         lhs.inner_radius_ == rhs.inner_radius_ and\n         lhs.outer_radius_ == rhs.outer_radius_;\n}\n\ntemplate <domain::ObjectLabel Label>\nbool operator!=(const Shape<Label>& lhs, const Shape<Label>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define LABEL(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                                             \\\n  template class Shape<LABEL(data)>;                                       \\\n  template bool operator==                                                 \\\n      <LABEL(data)>(const Shape<LABEL(data)>&, const Shape<LABEL(data)>&); \\\n  template bool operator!=                                                 \\\n      <LABEL(data)>(const Shape<LABEL(data)>&, const Shape<LABEL(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (domain::ObjectLabel::A, domain::ObjectLabel::B,\n                         domain::ObjectLabel::None))\n\n#undef LABEL\n}  // namespace creators::time_dependence\n\nusing ShapeMap3d = CoordinateMaps::TimeDependent::Shape;\n\nINSTANTIATE_MAPS_FUNCTIONS(((ShapeMap3d)), (Frame::Grid),\n                           (Frame::Distorted, Frame::Inertial),\n                           (double, DataVector))\nINSTANTIATE_MAPS_FUNCTIONS(((ShapeMap3d)), (Frame::Distorted),\n                           (Frame::Inertial), (double, DataVector))\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/Shape.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/Creators/TimeDependence/GenerateCoordinateMap.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace domain::CoordinateMaps::TimeDependent {\nclass Shape;\n}  // namespace domain::CoordinateMaps::TimeDependent\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators::time_dependence {\n/*!\n * \\brief A Shape whose inner surface conforms to a surface of constant\n * Boyer-Lindquist radius, in Kerr-Schild coordinates as given by\n * domain::CoordinateMaps::TimeDependent::Shape.\n *\n * \\details This TimeDependence is suitable for use on a spherical shell,\n * where LMax is the number of l and m spherical harmonics to use in\n * approximating the Kerr horizon, of mass `Mass` and spin `Spin`. The value\n * of the Boyer-Lindquist radius to which the inner surface conforms is given\n * by the value of `inner_radius`. If the user wants the inner surface of the\n * Shape to conform to a Kerr horizon for a given mass and spin, `inner_radius`\n * should be the Boyer-Lindquist radius of the outer horizon.\n *\n * The actual shape map that is applied will go from the Grid to the Distorted\n * frame, and then an identity map will go from the Distorted to Inertial frame.\n * The reasoning behind this is because in basically all use cases (BBH), the\n * shape map will go to the Distorted frame only and other maps will go from the\n * Distorted frame to the Inertial frame.\n *\n * \\note The quantities stored in the FunctionOfTime are not the\n * complex spherical-harmonic coefficients \\f$\\lambda_{lm}(t)\\f$, but\n * instead are the real-valued SPHEREPACK coefficients\n * \\f$a_{lm}(t)\\f$ and \\f$b_{lm}(t)\\f$ used by Spherepack.  The\n * relationship between these two sets of coefficients is\n * \\f{align}\n * a_{l0} & = \\sqrt{\\frac{2}{\\pi}}\\lambda_{l0}&\\qquad l\\geq 0,\\\\\n * a_{lm} & = (-1)^m\\sqrt{\\frac{2}{\\pi}} \\mathrm{Re}(\\lambda_{lm})\n * &\\qquad l\\geq 1, m\\geq 1, \\\\\n * b_{lm} & = (-1)^m\\sqrt{\\frac{2}{\\pi}} \\mathrm{Im}(\\lambda_{lm})\n * &\\qquad l\\geq 1, m\\geq 1.\n * \\f}\n * See domain::CoordinateMaps::TimeDependent::Shape for more details.\n *\n * \\note To use this time dependence with the `control_system::system::Shape`\n * control system, you must choose the same \\tparam Label that the control\n * system is using.\n */\ntemplate <domain::ObjectLabel Label>\nclass Shape final : public TimeDependence<3> {\n private:\n  using ShapeMap = domain::CoordinateMaps::TimeDependent::Shape;\n  using Identity = domain::CoordinateMaps::Identity<3>;\n  using GridToInertialMap =\n      detail::generate_coordinate_map_t<Frame::Grid, Frame::Inertial,\n                                        tmpl::list<ShapeMap>>;\n  using GridToDistortedMap =\n      detail::generate_coordinate_map_t<Frame::Grid, Frame::Distorted,\n                                        tmpl::list<ShapeMap>>;\n  using DistortedToInertialMap =\n      detail::generate_coordinate_map_t<Frame::Distorted, Frame::Inertial,\n                                        tmpl::list<Identity>>;\n\n public:\n  using maps_list = tmpl::list<\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, ShapeMap>,\n      domain::CoordinateMap<Frame::Grid, Frame::Distorted, ShapeMap>,\n      domain::CoordinateMap<Frame::Distorted, Frame::Inertial, Identity>>;\n\n  static constexpr size_t mesh_dim = 3;\n\n  static std::string name() { return \"Shape\"s + get_output(Label); }\n\n  /// \\brief The initial time of the function of time.\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the function of time\"};\n  };\n  /// \\brief The max angular resolution `l` of the Shape.\n  struct LMax {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The max l value of the Ylms used by the Shape map.\"};\n  };\n  /// \\brief The mass of the Kerr black hole.\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\"The mass of the Kerr BH.\"};\n  };\n  /// \\brief The dimensionless spin of the Kerr black hole.\n  struct Spin {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The dim'less spin of the Kerr BH.\"};\n  };\n  /// \\brief Center for the Shape map\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\"Center for the Shape map.\"};\n  };\n  /// \\brief Coefficients below this absolute value will be truncated from the\n  /// Shape map.\n  struct CoefficientTruncationLimit {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Coefficients below this absolute value will be truncated from the \"\n        \"Shape map. Set to 0.0 to disable truncation.\"};\n    static constexpr type default_value = 0.0;\n  };\n  /// \\brief The inner radius of the Shape map, the radius at which\n  /// to begin applying the map.\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The inner radius of the Shape map.\"};\n  };\n  /// \\brief The outer radius of the Shape map, beyond which\n  /// it is no longer applied.\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The outer radius of the Shape map.\"};\n  };\n\n  using options =\n      tmpl::list<InitialTime, LMax, Mass, Spin, Center,\n                 CoefficientTruncationLimit, InnerRadius, OuterRadius>;\n\n  static constexpr Options::String help = {\n      \"Creates a Shape that conforms to a Kerr horizon of given mass and \"\n      \"spin.\"};\n\n  Shape() = default;\n  ~Shape() override = default;\n  Shape(const Shape&) = delete;\n  Shape(Shape&&) = default;\n  Shape& operator=(const Shape&) = delete;\n  Shape& operator=(Shape&&) = default;\n\n  Shape(double initial_time, size_t l_max, double mass,\n        std::array<double, 3> spin, std::array<double, 3> center,\n        double coefficient_truncation_limit, double inner_radius,\n        double outer_radius, const Options::Context& context = {});\n\n  auto get_clone() const -> std::unique_ptr<TimeDependence<mesh_dim>> override;\n\n  auto block_maps_grid_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Inertial, mesh_dim>>> override;\n\n  auto block_maps_grid_to_distorted(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Distorted, mesh_dim>>> override;\n\n  auto block_maps_distorted_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Distorted, Frame::Inertial, mesh_dim>>> override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  template <domain::ObjectLabel OtherLabel>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Shape<OtherLabel>& lhs,\n                         const Shape<OtherLabel>& rhs);\n\n  using TransitionFunction = domain::CoordinateMaps::\n      ShapeMapTransitionFunctions::ShapeMapTransitionFunction;\n\n  GridToInertialMap grid_to_inertial_map() const;\n  GridToDistortedMap grid_to_distorted_map() const;\n  static DistortedToInertialMap distorted_to_inertial_map();\n\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  size_t l_max_{2};\n  double mass_{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> spin_{\n      make_array<3>(std::numeric_limits<double>::signaling_NaN())};\n  std::array<double, 3> center_{\n      make_array<3>(std::numeric_limits<double>::signaling_NaN())};\n  inline static const std::string function_of_time_name_{\"Shape\" +\n                                                         get_output(Label)};\n  double coefficient_truncation_limit_{0.0};\n  double inner_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double outer_radius_{std::numeric_limits<double>::signaling_NaN()};\n  std::unique_ptr<TransitionFunction> transition_func_;\n};\n\ntemplate <domain::ObjectLabel Label>\nbool operator!=(const Shape<Label>& lhs, const Shape<Label>& rhs);\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/SphericalCompression.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependence/SphericalCompression.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/MapInstantiationMacros.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\nnamespace creators::time_dependence {\n\nSphericalCompression::SphericalCompression(\n    const double initial_time, const double min_radius, const double max_radius,\n    const std::array<double, 3> center, const double initial_value,\n    const double initial_velocity, const double initial_acceleration,\n    const Options::Context& context)\n    : initial_time_(initial_time),\n      min_radius_(min_radius),\n      max_radius_(max_radius),\n      center_(center),\n      initial_value_(initial_value),\n      initial_velocity_(initial_velocity),\n      initial_acceleration_(initial_acceleration) {\n  if (min_radius >= max_radius) {\n    PARSE_ERROR(context,\n                \"Tried to create a SphericalCompression TimeDependence, but \"\n                \"the minimum radius (\"\n                    << min_radius << \") is not less than the maximum radius (\"\n                    << max_radius << \")\");\n  }\n}\n\nstd::unique_ptr<TimeDependence<3>> SphericalCompression::get_clone() const {\n  return std::make_unique<SphericalCompression>(\n      initial_time_, min_radius_, max_radius_, center_, initial_value_,\n      initial_velocity_, initial_acceleration_);\n}\n\nstd::vector<\n    std::unique_ptr<domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\nSphericalCompression::block_maps_grid_to_inertial(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0,\n         \"Must have at least one block on which to create a map.\");\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n      result{number_of_blocks};\n  result[0] = std::make_unique<GridToInertialMap>(grid_to_inertial_map());\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nSphericalCompression::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // Functions of time don't expire by default\n  double expiration_time = std::numeric_limits<double>::infinity();\n\n  // If we have control systems, overwrite the expiration time with the one\n  // supplied by the control system\n  if (initial_expiration_times.count(function_of_time_name_) == 1) {\n    expiration_time = initial_expiration_times.at(function_of_time_name_);\n  }\n\n  result[function_of_time_name_] =\n      std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n          initial_time_,\n          std::array<DataVector, 4>{{{initial_value_},\n                                     {initial_velocity_},\n                                     {initial_acceleration_},\n                                     {0.0}}},\n          expiration_time);\n  return result;\n}\n\nauto SphericalCompression::grid_to_inertial_map() const -> GridToInertialMap {\n  return GridToInertialMap{SphericalCompressionMap{\n      function_of_time_name_, min_radius_, max_radius_, center_}};\n}\n\nbool operator==(const SphericalCompression& lhs,\n                const SphericalCompression& rhs) {\n  return lhs.initial_time_ == rhs.initial_time_ and\n         lhs.min_radius_ == rhs.min_radius_ and\n         lhs.max_radius_ == rhs.max_radius_ and lhs.center_ == rhs.center_ and\n         lhs.initial_value_ == rhs.initial_value_ and\n         lhs.initial_velocity_ == rhs.initial_velocity_ and\n         lhs.initial_acceleration_ == rhs.initial_acceleration_;\n}\n\nbool operator!=(const SphericalCompression& lhs,\n                const SphericalCompression& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace creators::time_dependence\n\nusing SphericalCompressionMap3d =\n    CoordinateMaps::TimeDependent::SphericalCompression<false>;\n\nINSTANTIATE_MAPS_FUNCTIONS(((SphericalCompressionMap3d)), (Frame::Grid),\n                           (Frame::Inertial), (double, DataVector))\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/SphericalCompression.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/SphericalCompression.hpp\"\n#include \"Domain/Creators/TimeDependence/GenerateCoordinateMap.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace domain::CoordinateMaps::TimeDependent {\ntemplate <bool InertiorMap>\nclass SphericalCompression;\n}  // namespace domain::CoordinateMaps::TimeDependent\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators::time_dependence {\n/*!\n * \\brief A spherical compression about some center, as given by\n * domain::CoordinateMaps::TimeDependent::SphericalCompression<false>.\n *\n * \\details This TimeDependence is suitable for use on a spherical shell,\n * where MinRadius and MaxRadius are the inner and outer radii of the shell,\n * respectively.\n *\n * \\note The quantity stored in the FunctionOfTime is really\n * the spherical-harmonic coefficient \\f$\\lambda_{00}(t)\\f$.  This is\n * different from the Shape map, which stores ylm::Spherepack coefficients\n * \\f$a_{lm}(t)\\f$ and \\f$b_{lm}(t)\\f$ instead of \\f$\\lambda_{lm}(t)\\f$.\n * See domain::CoordinateMaps::TimeDependent::Shape for more details.\n */\nclass SphericalCompression final : public TimeDependence<3> {\n private:\n  using SphericalCompressionMap =\n      domain::CoordinateMaps::TimeDependent::SphericalCompression<false>;\n\n public:\n  using maps_list =\n      tmpl::list<domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                       SphericalCompressionMap>>;\n\n  static constexpr size_t mesh_dim = 3;\n\n  /// \\brief The initial time of the function of time.\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the function of time\"};\n  };\n  /// \\brief Minimum radius for the SphericalCompression map\n  struct MinRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Min radius for SphericalCompression map.\"};\n  };\n  /// \\brief Maximum radius for the SphericalCompression map\n  struct MaxRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Max radius for SphericalCompression map.\"};\n  };\n  /// \\brief Center for the SphericalCompression map\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Center for the SphericalCompression map.\"};\n  };\n  /// \\brief Initial value for function of time for the spherical compression\n  struct InitialValue {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Spherical compression value at initial time.\"};\n  };\n  /// \\brief Initial radial velocity for the function of time for the spherical\n  /// compression\n  struct InitialVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Spherical compression initial radial velocity.\"};\n  };\n  /// \\brief Initial radial acceleration for the function of time for the\n  /// spherical compression\n  struct InitialAcceleration {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Spherical compression initial radial acceleration.\"};\n  };\n\n  using GridToInertialMap =\n        detail::generate_coordinate_map_t<Frame::Grid, Frame::Inertial,\n                                          tmpl::list<SphericalCompressionMap>>;\n\n  using options =\n      tmpl::list<InitialTime, MinRadius, MaxRadius, Center, InitialValue,\n                 InitialVelocity, InitialAcceleration>;\n\n  static constexpr Options::String help = {\"A spherical compression.\"};\n\n  SphericalCompression() = default;\n  ~SphericalCompression() override = default;\n  SphericalCompression(const SphericalCompression&) = delete;\n  SphericalCompression(SphericalCompression&&) = default;\n  SphericalCompression& operator=(const SphericalCompression&) = delete;\n  SphericalCompression& operator=(SphericalCompression&&) = default;\n\n  SphericalCompression(double initial_time, double min_radius,\n                       double max_radius, std::array<double, 3> center,\n                       double initial_value, double initial_velocity,\n                       double initial_acceleration,\n                       const Options::Context& context = {});\n\n  auto get_clone() const -> std::unique_ptr<TimeDependence<mesh_dim>> override;\n\n  auto block_maps_grid_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Inertial, mesh_dim>>> override;\n\n  auto block_maps_grid_to_distorted(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Distorted, mesh_dim>>> override {\n    using ptr_type =\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, mesh_dim>;\n    return std::vector<std::unique_ptr<ptr_type>>(number_of_blocks);\n  }\n\n  auto block_maps_distorted_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Distorted, Frame::Inertial, mesh_dim>>> override {\n    using ptr_type =\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, mesh_dim>;\n    return std::vector<std::unique_ptr<ptr_type>>(number_of_blocks);\n  }\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n private:\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const SphericalCompression& lhs,\n                         const SphericalCompression& rhs);\n\n  GridToInertialMap grid_to_inertial_map() const;\n\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  double min_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double max_radius_{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> center_{};\n  double initial_value_{std::numeric_limits<double>::signaling_NaN()};\n  double initial_velocity_{std::numeric_limits<double>::signaling_NaN()};\n  double initial_acceleration_{std::numeric_limits<double>::signaling_NaN()};\n  inline static const std::string function_of_time_name_{\"Size\"};\n};\n\nbool operator!=(const SphericalCompression& lhs,\n                const SphericalCompression& rhs);\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/TimeDependence.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\n}  // namespace domain\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain::creators::time_dependence {\ntemplate <size_t MeshDim>\nclass CubicScale;\ntemplate <size_t MeshDim>\nclass None;\ntemplate <size_t MeshDim>\nclass ScalingAndZRotation;\ntemplate <domain::ObjectLabel Label>\nclass Shape;\nclass SphericalCompression;\ntemplate <size_t MeshDim>\nclass RotationAboutZAxis;\ntemplate <size_t MeshDim, size_t Index = 0>\nclass UniformTranslation;\n}  // namespace domain::creators::time_dependence\n/// \\endcond\n\nnamespace domain::creators {\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Classes and functions for adding time dependence to a domain.\nnamespace time_dependence {\n/// \\brief The abstract base class off of which specific classes for adding\n/// time dependence into a domain creator must inherit off of.\n///\n/// The simplest examples of a `TimeDependence` are `None` and\n/// `UniformTranslation`. The `None` class is treated in a special manner to\n/// communicate to the code that the domain is time-independent. The\n/// `UniformTranslation` takes an extra template parameter `Index` so that its\n/// name is unique from other `UniformTranslation`s.\ntemplate <size_t MeshDim>\nstruct TimeDependence {\n private:\n  using creatable_classes_1d = tmpl::list<>;\n  using creatable_classes_2d =\n      tmpl::list<RotationAboutZAxis<2>, ScalingAndZRotation<2>>;\n  using creatable_classes_3d =\n      tmpl::list<Shape<domain::ObjectLabel::A>, Shape<domain::ObjectLabel::B>,\n                 Shape<domain::ObjectLabel::None>, SphericalCompression,\n                 RotationAboutZAxis<3>, ScalingAndZRotation<3>>;\n  using creatable_classes_any_dim =\n      tmpl::list<CubicScale<MeshDim>, None<MeshDim>,\n                 UniformTranslation<MeshDim>>;\n\n public:\n  using creatable_classes =\n      tmpl::append<creatable_classes_any_dim,\n                   tmpl::conditional_t<\n                       MeshDim == 1, creatable_classes_1d,\n                       tmpl::conditional_t<MeshDim == 2, creatable_classes_2d,\n                                           creatable_classes_3d>>>;\n\n  TimeDependence() = default;\n  virtual ~TimeDependence() = 0;\n  TimeDependence(const TimeDependence&) = default;\n  TimeDependence& operator=(const TimeDependence&) = default;\n  TimeDependence(TimeDependence&&) = default;\n  TimeDependence& operator=(TimeDependence&&) = default;\n\n  /// Returns a `std::unique_ptr` pointing to a copy of the `TimeDependence`.\n  virtual auto get_clone() const -> std::unique_ptr<TimeDependence> = 0;\n\n  /// Returns the coordinate maps from the `Frame::Grid` to the\n  /// `Frame::Inertial` frame for each block.\n  virtual auto block_maps_grid_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Inertial, MeshDim>>> = 0;\n\n  /// Returns the coordinate maps from the `Frame::Grid` to the\n  /// `Frame::Distorted` frame for each block.\n  /// Returns vector of nullptr if there is no distorted frame.\n  virtual auto block_maps_grid_to_distorted(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Distorted, MeshDim>>> = 0;\n\n  /// Returns the coordinate maps from the `Frame::Distorted` to the\n  /// `Frame::Inertial` frame for each block.\n  /// Returns vector of nullptr if is no distorted frame.\n  virtual auto block_maps_distorted_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Distorted, Frame::Inertial, MeshDim>>> = 0;\n\n  /// Returns the functions of time for the domain.\n  virtual auto functions_of_time(const std::unordered_map<std::string, double>&\n                                     initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> = 0;\n\n  /// Returns `true` if the instance is `None`, meaning no time dependence.\n  bool is_none() const {\n    return dynamic_cast<const None<MeshDim>*>(this) != nullptr;\n  }\n};\n\ntemplate <size_t MeshDim>\nTimeDependence<MeshDim>::~TimeDependence() = default;\n}  // namespace time_dependence\n}  // namespace domain::creators\n\n#include \"Domain/Creators/TimeDependence/CubicScale.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/RotationAboutZAxis.hpp\"\n#include \"Domain/Creators/TimeDependence/ScalingAndZRotation.hpp\"\n#include \"Domain/Creators/TimeDependence/Shape.hpp\"\n#include \"Domain/Creators/TimeDependence/SphericalCompression.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/UniformTranslation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/MapInstantiationMacros.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\nnamespace creators::time_dependence {\ntemplate <size_t MeshDim, size_t Index>\nUniformTranslation<MeshDim, Index>::UniformTranslation(\n    const double initial_time, const std::array<double, MeshDim>& velocity)\n    : initial_time_(initial_time), velocity_grid_to_distorted_(velocity) {}\n\ntemplate <size_t MeshDim, size_t Index>\nUniformTranslation<MeshDim, Index>::UniformTranslation(\n    const double initial_time,\n    const std::array<double, MeshDim>& velocity_grid_to_distorted,\n    const std::array<double, MeshDim>& velocity_distorted_to_inertial)\n    : initial_time_(initial_time),\n      velocity_grid_to_distorted_(velocity_grid_to_distorted),\n      velocity_distorted_to_inertial_(velocity_distorted_to_inertial),\n      distorted_and_inertial_frames_are_equal_(false) {}\n\ntemplate <size_t MeshDim, size_t Index>\nstd::unique_ptr<TimeDependence<MeshDim>>\nUniformTranslation<MeshDim, Index>::get_clone() const {\n  if(distorted_and_inertial_frames_are_equal_) {\n    return std::make_unique<UniformTranslation>(initial_time_,\n                                                velocity_grid_to_distorted_);\n  } else {\n    return std::make_unique<UniformTranslation>(\n        initial_time_, velocity_grid_to_distorted_,\n        velocity_distorted_to_inertial_);\n  }\n}\n\ntemplate <size_t MeshDim, size_t Index>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\nUniformTranslation<MeshDim, Index>::block_maps_grid_to_inertial(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0, \"Must have at least one block to create.\");\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, MeshDim>>>\n      result{number_of_blocks};\n  if (distorted_and_inertial_frames_are_equal_) {\n    result[0] = std::make_unique<GridToInertialMapSimple>(\n        grid_to_inertial_map_simple());\n  } else {\n    result[0] = std::make_unique<GridToInertialMapCombined>(\n        grid_to_inertial_map_combined());\n  }\n  for (size_t i = 1; i < number_of_blocks; ++i) {\n    result[i] = result[0]->get_clone();\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim, size_t Index>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, MeshDim>>>\nUniformTranslation<MeshDim, Index>::block_maps_grid_to_distorted(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0, \"Must have at least one block to create.\");\n  using ptr_type =\n      domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, MeshDim>;\n  std::vector<std::unique_ptr<ptr_type>> result{number_of_blocks};\n\n  if (not distorted_and_inertial_frames_are_equal_) {\n    result[0] = std::make_unique<GridToDistortedMap>(grid_to_distorted_map());\n    for (size_t i = 1; i < number_of_blocks; ++i) {\n      result[i] = result[0]->get_clone();\n    }\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim, size_t Index>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, MeshDim>>>\nUniformTranslation<MeshDim, Index>::block_maps_distorted_to_inertial(\n    const size_t number_of_blocks) const {\n  ASSERT(number_of_blocks > 0, \"Must have at least one block to create.\");\n  using ptr_type =\n      domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, MeshDim>;\n  std::vector<std::unique_ptr<ptr_type>> result{number_of_blocks};\n  if (not distorted_and_inertial_frames_are_equal_) {\n    result[0] =\n        std::make_unique<DistortedToInertialMap>(distorted_to_inertial_map());\n    for (size_t i = 1; i < number_of_blocks; ++i) {\n      result[i] = result[0]->get_clone();\n    }\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim, size_t Index>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nUniformTranslation<MeshDim, Index>::functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // We use a `PiecewisePolynomial` with 2 derivs since some transformations\n  // between different frames for moving meshes can require Hessians.\n  DataVector velocity_grid_to_distorted{MeshDim, 0.0};\n  for (size_t i = 0; i < MeshDim; i++) {\n    velocity_grid_to_distorted[i] = gsl::at(velocity_grid_to_distorted_, i);\n  }\n\n  // Functions of time don't expire by default\n  double expiration_time_grid_to_distorted =\n      std::numeric_limits<double>::infinity();\n\n  // If we have control systems, overwrite the expiration time with the one\n  // supplied by the control system\n  if (initial_expiration_times.count(\n          function_of_time_name_grid_to_distorted_) == 1) {\n    expiration_time_grid_to_distorted =\n        initial_expiration_times.at(function_of_time_name_grid_to_distorted_);\n  }\n\n  result[function_of_time_name_grid_to_distorted_] =\n      std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time_,\n          std::array<DataVector, 3>{\n              {{MeshDim, 0.0}, velocity_grid_to_distorted, {MeshDim, 0.0}}},\n          expiration_time_grid_to_distorted);\n\n  if (not distorted_and_inertial_frames_are_equal_) {\n    DataVector velocity_distorted_to_inertial{MeshDim, 0.0};\n    for (size_t i = 0; i < MeshDim; i++) {\n      velocity_distorted_to_inertial[i] =\n          gsl::at(velocity_distorted_to_inertial_, i);\n    }\n    double expiration_time_distorted_to_inertial =\n        std::numeric_limits<double>::infinity();\n    if (initial_expiration_times.count(\n            function_of_time_name_distorted_to_inertial_) == 1) {\n      expiration_time_distorted_to_inertial = initial_expiration_times.at(\n          function_of_time_name_distorted_to_inertial_);\n    }\n\n    result[function_of_time_name_distorted_to_inertial_] =\n        std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n            initial_time_,\n            std::array<DataVector, 3>{{{MeshDim, 0.0},\n                                       velocity_distorted_to_inertial,\n                                       {MeshDim, 0.0}}},\n            expiration_time_distorted_to_inertial);\n  }\n  return result;\n}\n\ntemplate <size_t MeshDim, size_t Index>\nauto UniformTranslation<MeshDim, Index>::grid_to_inertial_map_simple() const\n    -> GridToInertialMapSimple {\n  return GridToInertialMapSimple{\n      domain::CoordinateMaps::TimeDependent::Translation<MeshDim>{\n          function_of_time_name_grid_to_distorted_}};\n}\n\ntemplate <size_t MeshDim, size_t Index>\nauto UniformTranslation<MeshDim, Index>::grid_to_distorted_map() const\n    -> GridToDistortedMap {\n  return GridToDistortedMap{\n      domain::CoordinateMaps::TimeDependent::Translation<MeshDim>{\n          function_of_time_name_grid_to_distorted_}};\n}\n\ntemplate <size_t MeshDim, size_t Index>\nauto UniformTranslation<MeshDim, Index>::distorted_to_inertial_map() const\n    -> DistortedToInertialMap {\n  return DistortedToInertialMap{\n      domain::CoordinateMaps::TimeDependent::Translation<MeshDim>{\n          function_of_time_name_distorted_to_inertial_}};\n}\n\ntemplate <size_t MeshDim, size_t Index>\nauto UniformTranslation<MeshDim, Index>::grid_to_inertial_map_combined() const\n    -> GridToInertialMapCombined {\n  return GridToInertialMapCombined{\n      domain::CoordinateMaps::TimeDependent::Translation<MeshDim>{\n          function_of_time_name_grid_to_distorted_},\n      domain::CoordinateMaps::TimeDependent::Translation<MeshDim>{\n          function_of_time_name_distorted_to_inertial_}};\n}\n\ntemplate <size_t Dim, size_t Index>\nbool operator==(const UniformTranslation<Dim, Index>& lhs,\n                const UniformTranslation<Dim, Index>& rhs) {\n  // If distorted_and_inertial_frames_are_equal_, we short-circuit\n  // evaluating velocity_distorted_to_inertial_ below.\n  return lhs.initial_time_ == rhs.initial_time_ and\n         lhs.velocity_grid_to_distorted_ == rhs.velocity_grid_to_distorted_ and\n         lhs.distorted_and_inertial_frames_are_equal_ ==\n             rhs.distorted_and_inertial_frames_are_equal_ and\n         (lhs.distorted_and_inertial_frames_are_equal_ or\n          lhs.velocity_distorted_to_inertial_ ==\n              rhs.velocity_distorted_to_inertial_);\n}\n\ntemplate <size_t Dim, size_t Index>\nbool operator!=(const UniformTranslation<Dim, Index>& lhs,\n                const UniformTranslation<Dim, Index>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INDEX(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                   \\\n  template class UniformTranslation<GET_DIM(data), INDEX(data)>; \\\n  template bool operator==<GET_DIM(data), INDEX(data)>(          \\\n      const UniformTranslation<GET_DIM(data), INDEX(data)>&,     \\\n      const UniformTranslation<GET_DIM(data), INDEX(data)>&);    \\\n  template bool operator!=<GET_DIM(data), INDEX(data)>(          \\\n      const UniformTranslation<GET_DIM(data), INDEX(data)>&,     \\\n      const UniformTranslation<GET_DIM(data), INDEX(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (0, 1, 2))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace creators::time_dependence\n\ntemplate <size_t MeshDim>\nusing Translation = CoordinateMaps::TimeDependent::Translation<MeshDim>;\n\nINSTANTIATE_MAPS_FUNCTIONS(((Translation<1>), (Translation<2>),\n                            (Translation<3>)),\n                           (Frame::Grid), (Frame::Distorted, Frame::Inertial),\n                           (double, DataVector))\nINSTANTIATE_MAPS_FUNCTIONS(((Translation<1>), (Translation<2>),\n                            (Translation<3>)),\n                           (Frame::Distorted), (Frame::Inertial),\n                           (double, DataVector))\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependence/UniformTranslation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/TimeDependence/GenerateCoordinateMap.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace domain::CoordinateMaps::TimeDependent {\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\n}  // namespace domain::CoordinateMaps::TimeDependent\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nclass CoordinateMap;\n}  // namespace domain\n\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators::time_dependence {\n/*!\n * \\brief A uniform translation in the \\f$x-, y-\\f$ and \\f$z-\\f$direction.\n *\n * The coordinates are adjusted according to:\n *\n * \\f{align}{\n * x^i \\to x^i + f^i(t)\n * \\f}\n *\n * where \\f$f^i(t)\\f$ are the functions of time.\n *\n * \\p Index is used to distinguish multiple `UniformTranslation`s from each\n * other in CompositionUniformTranslation.\n *\n * See the documentation for the constructors below: one constructor\n * takes two velocities, which correspond to two translations: one from\n * Frame::Grid to Frame::Distorted, and the other from Frame::Distorted to\n * Frame::Inertial.\n */\ntemplate <size_t MeshDim, size_t Index>\nclass UniformTranslation final : public TimeDependence<MeshDim> {\n private:\n  using TranslationMap =\n      domain::CoordinateMaps::TimeDependent::Translation<MeshDim>;\n\n public:\n  using maps_list = tmpl::list<\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap>,\n      domain::CoordinateMap<Frame::Grid, Frame::Distorted, TranslationMap>,\n      domain::CoordinateMap<Frame::Distorted, Frame::Inertial, TranslationMap>,\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap,\n                            TranslationMap>>;\n\n  static constexpr size_t mesh_dim = MeshDim;\n\n  /// \\brief The initial time of the functions of time.\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the functions of time\"};\n  };\n  /// \\brief The \\f$x\\f$-, \\f$y\\f$-, and \\f$z\\f$-velocity.\n  struct Velocity {\n    using type = std::array<double, MeshDim>;\n    static constexpr Options::String help = {\"The velocity of the map.\"};\n  };\n\n  using GridToInertialMapSimple =\n      detail::generate_coordinate_map_t<Frame::Grid, Frame::Inertial,\n                                        tmpl::list<TranslationMap>>;\n\n  using GridToInertialMapCombined = detail::generate_coordinate_map_t<\n      Frame::Grid, Frame::Inertial, tmpl::list<TranslationMap, TranslationMap>>;\n\n  using GridToDistortedMap =\n      detail::generate_coordinate_map_t<Frame::Grid, Frame::Distorted,\n                                        tmpl::list<TranslationMap>>;\n\n  using DistortedToInertialMap =\n      detail::generate_coordinate_map_t<Frame::Distorted, Frame::Inertial,\n                                        tmpl::list<TranslationMap>>;\n\n  using options = tmpl::list<InitialTime, Velocity>;\n\n  static constexpr Options::String help = {\n      \"A spatially uniform translation initialized with a constant velocity.\"};\n\n  UniformTranslation() = default;\n  ~UniformTranslation() override = default;\n  UniformTranslation(const UniformTranslation&) = delete;\n  UniformTranslation(UniformTranslation&&) = default;\n  UniformTranslation& operator=(const UniformTranslation&) = delete;\n  UniformTranslation& operator=(UniformTranslation&&) = default;\n\n  /// If UniformTranslation is created using the constructor that\n  /// takes a single velocity, then there is no distorted frame (so\n  /// block_maps_grid_to_distorted() and\n  /// block_maps_distorted_to_inertial() contain nullptrs), and the\n  /// given velocity is the one that goes from Frame::Grid to\n  /// Frame::Inertial.\n  UniformTranslation(double initial_time,\n                     const std::array<double, MeshDim>& velocity);\n\n  /// If UniformTranslation is created using the constructor that\n  /// takes two velocities, then the first velocity is the one\n  /// describing a uniform translation that goes from Frame::Grid to\n  /// Frame::Distorted, and the second velocity is the one that\n  /// describes a uniform translation that goes from Frame::Distorted\n  /// to Frame::Inertial.  In this case there are also two\n  /// FunctionsOfTime, one for each of the two translation maps.\n  UniformTranslation(\n      double initial_time,\n      const std::array<double, MeshDim>& velocity_grid_to_distorted,\n      const std::array<double, MeshDim>& velocity_distorted_to_inertial);\n\n  auto get_clone() const -> std::unique_ptr<TimeDependence<MeshDim>> override;\n\n  auto block_maps_grid_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Inertial, MeshDim>>> override;\n\n  auto block_maps_grid_to_distorted(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Grid, Frame::Distorted, MeshDim>>> override;\n\n  auto block_maps_distorted_to_inertial(size_t number_of_blocks) const\n      -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::Distorted, Frame::Inertial, MeshDim>>> override;\n\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override;\n\n  static std::string name() {\n    return \"UniformTranslation\" + (Index == 0 ? \"\" : get_output(Index));\n  }\n\n private:\n  template <size_t LocalDim, size_t LocalIndex>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const UniformTranslation<LocalDim, LocalIndex>& lhs,\n                         const UniformTranslation<LocalDim, LocalIndex>& rhs);\n\n  GridToInertialMapSimple grid_to_inertial_map_simple() const;\n  GridToInertialMapCombined grid_to_inertial_map_combined() const;\n  GridToDistortedMap grid_to_distorted_map() const;\n  DistortedToInertialMap distorted_to_inertial_map() const;\n\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  // If distorted and inertial frames are equal, then\n  // velocity_grid_to_distorted_ is the grid to inertial velocity, and\n  // velocity_distorted_to_inertial_ is unused.\n  std::array<double, MeshDim> velocity_grid_to_distorted_{};\n  std::array<double, MeshDim> velocity_distorted_to_inertial_{};\n  bool distorted_and_inertial_frames_are_equal_{true};\n  inline static const std::string function_of_time_name_grid_to_distorted_{\n      \"Translation\" + (Index == 0 ? \"\" : get_output(Index))};\n  inline static const std::string function_of_time_name_distorted_to_inertial_{\n      \"TranslationDistortedToInertial\" + (Index == 0 ? \"\" : get_output(Index))};\n};\n\ntemplate <size_t Dim, size_t Index>\nbool operator==(const UniformTranslation<Dim, Index>& lhs,\n                const UniformTranslation<Dim, Index>& rhs);\n\ntemplate <size_t Dim, size_t Index>\nbool operator!=(const UniformTranslation<Dim, Index>& lhs,\n                const UniformTranslation<Dim, Index>& rhs);\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/BinaryCompactObject.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Wedge.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/GridCenters.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/SkewMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/IntegratedFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain::creators::bco {\ntemplate <bool IsCylindrical>\nTimeDependentMapOptions<IsCylindrical>::TimeDependentMapOptions(\n    double initial_time, ExpansionMapOptionType expansion_map_options,\n    RotationMapOptionType rotation_map_options,\n    TranslationMapOptionType translation_map_options,\n    SkewMapOptionType skew_map_options,\n    ShapeMapOptionType<domain::ObjectLabel::A> shape_options_A,\n    ShapeMapOptionType<domain::ObjectLabel::B> shape_options_B,\n    GridCentersOptionType grid_centers, const Options::Context& context)\n    : initial_time_(initial_time),\n      expansion_map_options_(std::move(expansion_map_options)),\n      rotation_map_options_(std::move(rotation_map_options)),\n      translation_map_options_(std::move(translation_map_options)),\n      skew_map_options_(std::move(skew_map_options)),\n      shape_options_A_(std::move(shape_options_A)),\n      shape_options_B_(std::move(shape_options_B)),\n      grid_centers_options_(std::move(grid_centers)) {\n  if (not(expansion_map_options_.has_value() or\n          rotation_map_options_.has_value() or\n          translation_map_options_.has_value() or\n          skew_map_options_.has_value() or shape_options_A_.has_value() or\n          shape_options_B_.has_value() or grid_centers_options_.has_value())) {\n    PARSE_ERROR(context,\n                \"Time dependent map options were specified, but all options \"\n                \"were 'None'. If you don't want time dependent maps, specify \"\n                \"'None' for the TimeDependentMapOptions. If you want time \"\n                \"dependent maps, specify options for at least one map.\");\n  }\n}\n\ntemplate <bool IsCylindrical>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nTimeDependentMapOptions<IsCylindrical>::create_worldtube_functions_of_time()\n    const {\n  if (translation_map_options_.has_value()) {\n    ERROR(\"Translation map is not implemented for worldtube evolutions.\");\n  }\n  if (skew_map_options_.has_value()) {\n    ERROR(\"Skew map is not implemented for worldtube evolutions.\");\n  }\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // The functions of time need to be valid only for the very first time step,\n  // after that they need to be updated by the worldtube singleton.\n  const double initial_expiration_time = initial_time_ + 1e-10;\n  if (not expansion_map_options_.has_value()) {\n    ERROR(\"Initial values for the expansion map need to be provided.\");\n  }\n\n  const auto& expansion_map_opts =\n      std::get<time_dependent_options::ExpansionMapOptions<false>>(\n          expansion_map_options_.value());\n  result[expansion_name] =\n      std::make_unique<FunctionsOfTime::IntegratedFunctionOfTime>(\n          initial_time_,\n          std::array<double, 2>{expansion_map_opts.initial_values[0][0],\n                                expansion_map_opts.initial_values[1][0]},\n          initial_expiration_time, false);\n  result[expansion_outer_boundary_name] =\n      std::make_unique<FunctionsOfTime::FixedSpeedCubic>(\n          1.0, initial_time_,\n          expansion_map_opts.asymptotic_velocity_outer_boundary.value(),\n          expansion_map_opts.decay_timescale_outer_boundary);\n\n  if (not rotation_map_options_.has_value()) {\n    ERROR(\n        \"Initial values for the rotation map need to be provided when using \"\n        \"the worldtube.\");\n  }\n  const auto& rotation_map_opts =\n      std::get<time_dependent_options::RotationMapOptions<false>>(\n          rotation_map_options_.value());\n\n  result[rotation_name] =\n      std::make_unique<FunctionsOfTime::IntegratedFunctionOfTime>(\n          initial_time_,\n          std::array<double, 2>{0., rotation_map_opts.angles[1][2]},\n          initial_expiration_time, true);\n\n  // Size and Shape FunctionOfTime for objects A and B. Only spherical excision\n  // spheres are supported currently.\n  if (not(shape_options_A_.has_value() and shape_options_B_.has_value())) {\n    ERROR(\n        \"Initial size for both excision spheres need to be provided when using \"\n        \"the worldtube.\");\n  }\n  const auto& shape_opts_A = std::get<time_dependent_options::ShapeMapOptions<\n      not IsCylindrical, domain::ObjectLabel::A>>(shape_options_A_.value());\n  const auto& shape_opts_B = std::get<time_dependent_options::ShapeMapOptions<\n      not IsCylindrical, domain::ObjectLabel::B>>(shape_options_B_.value());\n  for (size_t i = 0; i < shape_names.size(); i++) {\n    const auto make_initial_size_values = [](const auto& lambda_options) {\n      return std::array<double, 2>{\n          {gsl::at(lambda_options.initial_size_values.value(), 0),\n           gsl::at(lambda_options.initial_size_values.value(), 1)}};\n    };\n    const std::array<double, 2> initial_size_values =\n        i == 0 ? make_initial_size_values(shape_opts_A)\n               : make_initial_size_values(shape_opts_B);\n    const size_t initial_l_max = 2;\n    const DataVector shape_zeros{\n        ylm::Spherepack::spectral_size(initial_l_max, initial_l_max), 0.0};\n\n    result[gsl::at(shape_names, i)] =\n        std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n            initial_time_,\n            std::array<DataVector, 3>{shape_zeros, shape_zeros, shape_zeros},\n            std::numeric_limits<double>::infinity());\n    result[gsl::at(size_names, i)] =\n        std::make_unique<FunctionsOfTime::IntegratedFunctionOfTime>(\n            initial_time_, initial_size_values, initial_expiration_time, false);\n  }\n  return result;\n}\n\ntemplate <bool IsCylindrical>\ntemplate <bool UseWorldtube>\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nTimeDependentMapOptions<IsCylindrical>::create_functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  if constexpr (UseWorldtube) {\n    static_assert(not IsCylindrical,\n                  \"Cylindrical map not supported with worldtube\");\n    if (not initial_expiration_times.empty()) {\n      ERROR(\n          \"Initial expiration times were specified with worldtube functions of \"\n          \"time. This is not supported, as the worldtube singleton has to set \"\n          \"the expiration times each time step\");\n    }\n    return create_worldtube_functions_of_time();\n  }\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // Get existing function of time names that are used for the maps and assign\n  // their initial expiration time to infinity (i.e. not expiring)\n  std::unordered_map<std::string, double> expiration_times{\n      {expansion_name, std::numeric_limits<double>::infinity()},\n      {rotation_name, std::numeric_limits<double>::infinity()},\n      {translation_name, std::numeric_limits<double>::infinity()},\n      {skew_name, std::numeric_limits<double>::infinity()},\n      {grid_centers_name, std::numeric_limits<double>::infinity()},\n      {gsl::at(size_names, 0), std::numeric_limits<double>::infinity()},\n      {gsl::at(size_names, 1), std::numeric_limits<double>::infinity()},\n      {gsl::at(shape_names, 0), std::numeric_limits<double>::infinity()},\n      {gsl::at(shape_names, 1), std::numeric_limits<double>::infinity()}};\n\n  // If we have control systems, overwrite these expiration times with the ones\n  // supplied by the control system\n  for (const auto& [name, expr_time] : initial_expiration_times) {\n    expiration_times[name] = expr_time;\n  }\n\n  // ExpansionMap FunctionOfTime for the function a(t) and b(t) in the\n  // domain::CoordinateMaps::TimeDependent::RotScaleTrans map\n  if (expansion_map_options_.has_value()) {\n    auto expansion_functions_of_time = time_dependent_options::get_expansion(\n        expansion_map_options_.value(), initial_time_,\n        expiration_times.at(expansion_name));\n\n    result.merge(expansion_functions_of_time);\n  }\n\n  // RotationMap FunctionOfTime for the rotation angles about each\n  // axis.\n  if (rotation_map_options_.has_value()) {\n    result[rotation_name] = time_dependent_options::get_rotation(\n        rotation_map_options_.value(), initial_time_,\n        expiration_times.at(rotation_name));\n  }\n\n  // TranslationMap FunctionOfTime\n  if (translation_map_options_.has_value()) {\n    result[translation_name] = time_dependent_options::get_translation(\n        translation_map_options_.value(), initial_time_,\n        expiration_times.at(translation_name));\n  }\n\n  // SkewMap FunctionOfTime\n  if (skew_map_options_.has_value()) {\n    result[skew_name] = time_dependent_options::get_skew(\n        skew_map_options_.value(), initial_time_,\n        expiration_times.at(skew_name));\n  }\n\n  // Size and Shape FunctionOfTime for objects A and B\n  if (shape_options_A_.has_value()) {\n    if (not deformed_radii_[0].has_value()) {\n      ERROR(\n          \"A shape map was specified for object A, but no inner radius is \"\n          \"available. The object must be enclosed by a sphere.\");\n    }\n\n    auto shape_and_size = time_dependent_options::get_shape_and_size(\n        shape_options_A_.value(), initial_time_,\n        expiration_times.at(shape_names[0]), expiration_times.at(size_names[0]),\n        *deformed_radii_[0]);\n\n    result.merge(shape_and_size);\n  }\n  if (shape_options_B_.has_value()) {\n    if (not deformed_radii_[1].has_value()) {\n      ERROR(\n          \"A shape map was specified for object B, but no inner radius is \"\n          \"available. The object must be enclosed by a sphere.\");\n    }\n\n    auto shape_and_size = time_dependent_options::get_shape_and_size(\n        shape_options_B_.value(), initial_time_,\n        expiration_times.at(shape_names[1]), expiration_times.at(size_names[1]),\n        *deformed_radii_[1]);\n\n    result.merge(shape_and_size);\n  }\n\n  if (grid_centers_options_.has_value()) {\n    result[grid_centers_name] = time_dependent_options::get_grid_centers(\n        grid_centers_options_.value(), initial_time_,\n        expiration_times.at(grid_centers_name));\n  }\n\n  return result;\n}\n\ntemplate <bool IsCylindrical>\nvoid TimeDependentMapOptions<IsCylindrical>::build_maps(\n    const std::array<std::array<double, 3>, 2>& object_centers,\n    const std::optional<std::array<double, 3>>& cube_A_center,\n    const std::optional<std::array<double, 3>>& cube_B_center,\n    const std::array<double, 3>& center_of_mass,\n    const std::optional<std::array<double, IsCylindrical ? 2 : 3>>&\n        object_A_radii,\n    const std::optional<std::array<double, IsCylindrical ? 2 : 3>>&\n        object_B_radii,\n    const bool object_A_filled, const bool object_B_filled,\n    const double envelope_radius, const double domain_outer_radius) {\n  if (expansion_map_options_.has_value() or rotation_map_options_.has_value() or\n      translation_map_options_.has_value()) {\n    rot_scale_trans_map_ = std::make_pair(\n        RotScaleTrans{\n            expansion_map_options_.has_value()\n                ? std::make_pair(expansion_name, expansion_outer_boundary_name)\n                : std::optional<std::pair<std::string, std::string>>{},\n            rotation_map_options_.has_value() ? rotation_name\n                                              : std::optional<std::string>{},\n            translation_map_options_.has_value() ? translation_name\n                                                 : std::optional<std::string>{},\n            envelope_radius, domain_outer_radius,\n            domain::CoordinateMaps::TimeDependent::RotScaleTrans<\n                3>::BlockRegion::Inner},\n        RotScaleTrans{\n            expansion_map_options_.has_value()\n                ? std::make_pair(expansion_name, expansion_outer_boundary_name)\n                : std::optional<std::pair<std::string, std::string>>{},\n            rotation_map_options_.has_value() ? rotation_name\n                                              : std::optional<std::string>{},\n            translation_map_options_.has_value() ? translation_name\n                                                 : std::optional<std::string>{},\n            envelope_radius, domain_outer_radius,\n            domain::CoordinateMaps::TimeDependent::RotScaleTrans<\n                3>::BlockRegion::Transition});\n  }\n  if (skew_map_options_.has_value()) {\n    skew_map_ = Skew{skew_name, center_of_mass, envelope_radius};\n  }\n\n  for (size_t i = 0; i < 2; i++) {\n    const auto& radii_opt = i == 0 ? object_A_radii : object_B_radii;\n    const bool has_shape_map =\n        i == 0 ? shape_options_A_.has_value() : shape_options_B_.has_value();\n    if (not radii_opt.has_value()) {\n      // No radii were specified, so we skip building the shape map. This\n      // happens when the object is covered by a Cartesian cube.\n      if (has_shape_map) {\n        ERROR(\n            \"A shape map was specified for object \"\n            << (i == 0 ? \"A\" : \"B\")\n            << \", but no radii were provided. The object must be enclosed by a \"\n               \"sphere, not covered by a Cartesian cube.\");\n      }\n      continue;\n    }\n    if (not has_shape_map) {\n      // Radii were specified, but no shape map was requested. Skip building the\n      // shape map.\n      continue;\n    }\n    const auto& radii = radii_opt.value();\n    const bool filled = i == 0 ? object_A_filled : object_B_filled;\n    // Store the inner radii for creating functions of time\n    gsl::at(deformed_radii_, i) = filled ? radii[1] : radii[0];\n\n    std::unique_ptr<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                        ShapeMapTransitionFunction>\n        transition_func{};\n    const double coefficient_truncation_limit =\n        i == 0 ? time_dependent_options::\n                     coefficient_truncation_limit_from_shape_options(\n                         shape_options_A_.value())\n               : time_dependent_options::\n                     coefficient_truncation_limit_from_shape_options(\n                         shape_options_B_.value());\n\n    // Currently, we don't support different transition functions for the\n    // cylindrical domain\n    if constexpr (IsCylindrical) {\n      if (cube_A_center.has_value() or cube_B_center.has_value()) {\n        ERROR_NO_TRACE(\n            \"When using the CylindricalBinaryCompactObject domain creator, \"\n            \"the excision centers cannot be offset.\");\n      }\n      transition_func =\n          std::make_unique<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                               SphereTransition>(radii[0], radii[1]);\n\n      gsl::at(shape_maps_, i) =\n          Shape{gsl::at(object_centers, i), coefficient_truncation_limit,\n                std::move(transition_func), gsl::at(shape_names, i),\n                gsl::at(size_names, i)};\n\n      transition_func =\n          std::make_unique<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                               SphereTransition>(radii[0], radii[1], false,\n                                                 true);\n\n      // Last two are the interior maps\n      gsl::at(shape_maps_, shape_maps_.size() - 2 + i) =\n          Shape{gsl::at(object_centers, i), coefficient_truncation_limit,\n                std::move(transition_func), gsl::at(shape_names, i),\n                gsl::at(size_names, i)};\n    } else {\n      // These must match the order of orientations_for_sphere_wrappings() in\n      // DomainHelpers.hpp. The values must match that of Wedge::Axis\n      const std::array<int, 6> axes{3, -3, 2, -2, 1, -1};\n\n      const bool transition_ends_at_cube =\n          i == 0 ? time_dependent_options::\n                       transition_ends_at_cube_from_shape_options(\n                           shape_options_A_.value())\n                 : time_dependent_options::\n                       transition_ends_at_cube_from_shape_options(\n                           shape_options_B_.value());\n\n      // These centers must take in to account if we have an offset of the\n      // center of the object and where the transition ends. The inner center\n      // is always the center of the object. The outer center depends on if we\n      // have an offset and where the transition ends. If the transition ends\n      // at the cube, then if we have an offset we use the cube center, if not\n      // it's the same as the object center. If the transition ends at the\n      // sphere, then the center is the object center\n      const std::optional<std::array<double, 3>>& cube_center =\n          i == 0 ? cube_A_center : cube_B_center;\n      const std::array<double, 3>& inner_center = gsl::at(object_centers, i);\n      const std::array<double, 3>& outer_center =\n          transition_ends_at_cube\n              ? cube_center.value_or(gsl::at(object_centers, i))\n              : gsl::at(object_centers, i);\n\n      // These are the inner and outer radii between which the shape map falls\n      // off outside the object/excision. If the object is filled, then inside\n      // the object there will be an additional reverse transition function from\n      // the inner cube to the deformed outer surface of the sphere.\n      const double inner_radius = filled ? radii[1] : radii[0];\n      const double outer_radius = transition_ends_at_cube ? radii[2] : radii[1];\n      const double inner_sphericity = 1.0;\n      const double outer_sphericity = transition_ends_at_cube ? 0.0 : 1.0;\n\n      if (filled and not transition_ends_at_cube) {\n        ERROR_NO_TRACE(\n            \"If the object is filled, the transition must end at the cube.\");\n      }\n\n      using Wedge = domain::CoordinateMaps::ShapeMapTransitionFunctions::Wedge;\n      for (size_t j = 0; j < 12; j++) {\n        if (filled and j < 6) {\n          // Reverse the transition function so the shape map goes to zero at\n          // the inner cube\n          transition_func = std::make_unique<Wedge>(\n              inner_center, radii[0], 0.0, outer_center, radii[1], 1.0,\n              static_cast<Wedge::Axis>(gsl::at(axes, j)), true);\n        } else {\n          transition_func = std::make_unique<Wedge>(\n              inner_center, inner_radius, inner_sphericity, outer_center,\n              outer_radius, outer_sphericity,\n              static_cast<Wedge::Axis>(gsl::at(axes, j % 6)));\n        }\n\n        // The shape map should be given the center of the excision always,\n        // regardless of if it is offset or not\n        gsl::at(gsl::at(shape_maps_, i), j) =\n            Shape{inner_center, coefficient_truncation_limit,\n                  std::move(transition_func), gsl::at(shape_names, i),\n                  gsl::at(size_names, i)};\n      }\n\n      // Add the interior maps if we are excised (aka not filled)\n      if (not filled) {\n        transition_func = std::make_unique<Wedge>(\n            inner_center, inner_radius, inner_sphericity, outer_center,\n            outer_radius, outer_sphericity, Wedge::Axis::Interior);\n\n        gsl::at(gsl::at(shape_maps_, i), gsl::at(shape_maps_, i).size() - 1) =\n            Shape{inner_center, coefficient_truncation_limit,\n                  std::move(transition_func), gsl::at(shape_names, i),\n                  gsl::at(size_names, i)};\n      }\n    }\n  }\n}\n\ntemplate <bool IsCylindrical>\nbool TimeDependentMapOptions<IsCylindrical>::has_distorted_frame_options(\n    domain::ObjectLabel object) const {\n  ASSERT(object == domain::ObjectLabel::A or object == domain::ObjectLabel::B,\n         \"object label for TimeDependentMapOptions must be either A or B, not\"\n             << object);\n  return object == domain::ObjectLabel::A ? shape_options_A_.has_value()\n                                          : shape_options_B_.has_value();\n}\n\ntemplate <bool IsCylindrical>\ntemplate <domain::ObjectLabel Object>\ntypename TimeDependentMapOptions<IsCylindrical>::template MapType<\n    Frame::Distorted, Frame::Inertial>\nTimeDependentMapOptions<IsCylindrical>::distorted_to_inertial_map(\n    const IncludeDistortedMapType& include_distorted_map,\n    const bool use_rigid_map) const {\n  bool block_has_shape_map = false;\n\n  if constexpr (IsCylindrical) {\n    block_has_shape_map = include_distorted_map;\n  } else {\n    const bool transition_ends_at_cube =\n        Object == domain::ObjectLabel::A\n            ? (shape_options_A_.has_value()\n                   ? time_dependent_options::\n                         transition_ends_at_cube_from_shape_options(\n                             shape_options_A_.value())\n                   : false)\n            : (shape_options_B_.has_value()\n                   ? time_dependent_options::\n                         transition_ends_at_cube_from_shape_options(\n                             shape_options_B_.value())\n                   : false);\n    block_has_shape_map =\n        include_distorted_map.has_value() and\n        (transition_ends_at_cube or include_distorted_map.value() < 6);\n  }\n\n  const auto& rot_scale_trans = rot_scale_trans_map_.has_value()\n                                    ? use_rigid_map\n                                          ? rot_scale_trans_map_->first\n                                          : rot_scale_trans_map_->second\n                                    : RotScaleTrans{};\n\n  if (block_has_shape_map) {\n    // The skew map is only applied within the envelope, which is also where we\n    // apply the rigid RotScaleTrans map, so `use_rigid_map` is a sentinel\n    // for where we put the skew map (if we have one).\n    if (not use_rigid_map) {\n      ERROR(\n          \"'use_rigid_map' must be true when requesting the distorted to \"\n          \"inertial map with a shape map.\");\n    }\n    if (rot_scale_trans_map_.has_value() and skew_map_.has_value()) {\n      return std::make_unique<detail::di_map<Skew, RotScaleTrans>>(\n          skew_map_.value(), rot_scale_trans);\n    } else if (rot_scale_trans_map_.has_value()) {\n      return std::make_unique<detail::di_map<RotScaleTrans>>(rot_scale_trans);\n    } else if (skew_map_.has_value()) {\n      return std::make_unique<detail::di_map<Skew>>(skew_map_.value());\n    } else {\n      return std::make_unique<detail::di_map<Identity>>(Identity{});\n    }\n  } else {\n    return nullptr;\n  }\n}\n\ntemplate <bool IsCylindrical>\ntemplate <domain::ObjectLabel Object>\ntypename TimeDependentMapOptions<IsCylindrical>::template MapType<\n    Frame::Grid, Frame::Distorted>\nTimeDependentMapOptions<IsCylindrical>::grid_to_distorted_map(\n    const IncludeDistortedMapType& include_distorted_map) const {\n  bool block_has_shape_map = Object == domain::ObjectLabel::A\n                                 ? shape_options_A_.has_value()\n                                 : shape_options_B_.has_value();\n\n  if constexpr (IsCylindrical) {\n    block_has_shape_map = block_has_shape_map and include_distorted_map;\n  } else if (block_has_shape_map) {\n    const bool transition_ends_at_cube =\n        Object == domain::ObjectLabel::A\n            ? time_dependent_options::\n                  transition_ends_at_cube_from_shape_options(\n                      shape_options_A_.value())\n            : time_dependent_options::\n                  transition_ends_at_cube_from_shape_options(\n                      shape_options_B_.value());\n    block_has_shape_map =\n        block_has_shape_map and include_distorted_map.has_value() and\n        (transition_ends_at_cube or include_distorted_map.value() < 6);\n  }\n\n  if (block_has_shape_map) {\n    const size_t index = get_index(Object);\n    const std::optional<Shape>* shape{};\n    if constexpr (IsCylindrical) {\n      shape = &gsl::at(shape_maps_, index);\n    } else {\n      if (include_distorted_map.value() >= 12) {\n        ERROR(\n            \"Invalid 'include_distorted_map' argument. Max value allowed is \"\n            \"11, but it is \"\n            << include_distorted_map.value());\n      }\n      shape =\n          &gsl::at(gsl::at(shape_maps_, index), include_distorted_map.value());\n    }\n    ASSERT(shape->has_value(), \"Shape map was requested but not built.\");\n    return std::make_unique<detail::gd_map<Shape>>(shape->value());\n  } else {\n    return nullptr;\n  }\n}\n\ntemplate <bool IsCylindrical>\ntemplate <domain::ObjectLabel Object>\ntypename TimeDependentMapOptions<IsCylindrical>::template MapType<\n    Frame::Grid, Frame::Inertial>\nTimeDependentMapOptions<IsCylindrical>::grid_to_inertial_map(\n    const IncludeDistortedMapType& include_distorted_map,\n    const bool use_rigid_map, const bool return_excision_map) const {\n  bool block_has_shape_map = Object == domain::ObjectLabel::A\n                                 ? shape_options_A_.has_value()\n                                 : shape_options_B_.has_value();\n\n  if constexpr (IsCylindrical) {\n    block_has_shape_map = block_has_shape_map and include_distorted_map;\n  } else if (block_has_shape_map) {\n    if (return_excision_map and include_distorted_map.value_or(6) >= 6) {\n      ERROR(\n          \"If 'return_excision_map' is true, then 'include_distorted_map' must \"\n          \"be less than 6.\");\n    }\n\n    const bool transition_ends_at_cube =\n        Object == domain::ObjectLabel::A\n            ? time_dependent_options::\n                  transition_ends_at_cube_from_shape_options(\n                      shape_options_A_.value())\n            : time_dependent_options::\n                  transition_ends_at_cube_from_shape_options(\n                      shape_options_B_.value());\n    block_has_shape_map =\n        block_has_shape_map and include_distorted_map.has_value() and\n        (transition_ends_at_cube or include_distorted_map.value() < 6 or\n         return_excision_map);\n  }\n\n  const auto& rot_scale_trans = rot_scale_trans_map_.has_value()\n                                    ? use_rigid_map\n                                          ? rot_scale_trans_map_->first\n                                          : rot_scale_trans_map_->second\n                                    : RotScaleTrans{};\n\n  if (block_has_shape_map) {\n    const size_t index = get_index(Object);\n    const std::optional<Shape>* shape{};\n    if constexpr (IsCylindrical) {\n      shape = &gsl::at(shape_maps_, return_excision_map\n                                        ? shape_maps_.size() - 2 + index\n                                        : index);\n    } else {\n      if (include_distorted_map.value() >= 12) {\n        ERROR(\n            \"Invalid 'include_distorted_map' argument. Max value allowed is \"\n            \"11, but it is \"\n            << include_distorted_map.value());\n      }\n      shape =\n          &gsl::at(gsl::at(shape_maps_, index),\n                   return_excision_map ? gsl::at(shape_maps_, index).size() - 1\n                                       : include_distorted_map.value());\n    }\n    ASSERT(shape->has_value(), \"Shape map was requested but not built.\");\n    // The skew map is only applied within the envelope, which is also where we\n    // apply the rigid RotScaleTrans map, so `use_rigid_map` is a sentinel\n    // for where we put the skew map (if we have one).\n    if (not use_rigid_map) {\n      ERROR(\n          \"'use_rigid_map' must be true when requesting the grid to inertial \"\n          \"map with a shape map.\");\n    }\n    if (rot_scale_trans_map_.has_value() and skew_map_.has_value()) {\n      return std::make_unique<detail::gi_map<Shape, Skew, RotScaleTrans>>(\n          shape->value(), skew_map_.value(), rot_scale_trans);\n    } else if (rot_scale_trans_map_.has_value()) {\n      return std::make_unique<detail::gi_map<Shape, RotScaleTrans>>(\n          shape->value(), rot_scale_trans);\n    } else if (skew_map_.has_value()) {\n      return std::make_unique<detail::gi_map<Shape, Skew>>(shape->value(),\n                                                           skew_map_.value());\n    } else {\n      return std::make_unique<detail::gi_map<Shape>>(shape->value());\n    }\n  } else {\n    // The skew map is only applied within the envelope, which is also where we\n    // apply the rigid RotScaleTrans map, so use `use_rigid_map` as a sentinel\n    // for where we put the skew map (if we have one).\n    if (rot_scale_trans_map_.has_value() and\n        (use_rigid_map and skew_map_.has_value())) {\n      return std::make_unique<detail::gi_map<Skew, RotScaleTrans>>(\n          skew_map_.value(), rot_scale_trans);\n    } else if (rot_scale_trans_map_.has_value()) {\n      return std::make_unique<detail::gi_map<RotScaleTrans>>(rot_scale_trans);\n    } else if (use_rigid_map and skew_map_.has_value()) {\n      return std::make_unique<detail::gi_map<Skew>>(skew_map_.value());\n    } else {\n      return nullptr;\n    }\n  }\n}\n\ntemplate <bool IsCylindrical>\nsize_t TimeDependentMapOptions<IsCylindrical>::get_index(\n    const domain::ObjectLabel object) {\n  ASSERT(object == domain::ObjectLabel::A or object == domain::ObjectLabel::B,\n         \"object label for TimeDependentMapOptions must be either A or B, not\"\n             << object);\n  return object == domain::ObjectLabel::A ? 0_st : 1_st;\n}\n\ntemplate class TimeDependentMapOptions<true>;\ntemplate class TimeDependentMapOptions<false>;\n\n#define ISCYL(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define OBJECT(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template TimeDependentMapOptions<ISCYL(data)>::MapType<Frame::Distorted,   \\\n                                                         Frame::Inertial>    \\\n  TimeDependentMapOptions<ISCYL(data)>::distorted_to_inertial_map<OBJECT(    \\\n      data)>(                                                                \\\n      const TimeDependentMapOptions<ISCYL(data)>::IncludeDistortedMapType&,  \\\n      const bool) const;                                                     \\\n  template TimeDependentMapOptions<ISCYL(data)>::MapType<Frame::Grid,        \\\n                                                         Frame::Distorted>   \\\n  TimeDependentMapOptions<ISCYL(data)>::grid_to_distorted_map<OBJECT(data)>( \\\n      const TimeDependentMapOptions<ISCYL(data)>::IncludeDistortedMapType&)  \\\n      const;                                                                 \\\n  template TimeDependentMapOptions<ISCYL(data)>::MapType<Frame::Grid,        \\\n                                                         Frame::Inertial>    \\\n  TimeDependentMapOptions<ISCYL(data)>::grid_to_inertial_map<OBJECT(data)>(  \\\n      const TimeDependentMapOptions<ISCYL(data)>::IncludeDistortedMapType&,  \\\n      const bool, const bool) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (true, false),\n                        (domain::ObjectLabel::A, domain::ObjectLabel::B,\n                         domain::ObjectLabel::None))\n\n#undef OBJECT\n#undef ISCYL\n#undef INSTANTIATE\n\ntemplate std::unordered_map<\n    std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nTimeDependentMapOptions<false>::create_functions_of_time<true>(\n    const std::unordered_map<std::string, double>&) const;\ntemplate std::unordered_map<\n    std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nTimeDependentMapOptions<false>::create_functions_of_time<false>(\n    const std::unordered_map<std::string, double>&) const;\ntemplate std::unordered_map<\n    std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nTimeDependentMapOptions<true>::create_functions_of_time<false>(\n    const std::unordered_map<std::string, double>&) const;\n}  // namespace domain::creators::bco\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/RotScaleTrans.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Skew.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/GridCenters.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/SkewMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\n/// Namespace used to hold things used in both the BinaryCompactObject and\n/// CylindricalBinaryCompactObject domain creators.\nnamespace domain::creators::bco {\nnamespace detail {\n// Convenience type alias\ntemplate <typename... Maps>\nusing gi_map = domain::CoordinateMap<Frame::Grid, Frame::Inertial, Maps...>;\ntemplate <typename... Maps>\nusing gd_map = domain::CoordinateMap<Frame::Grid, Frame::Distorted, Maps...>;\ntemplate <typename... Maps>\nusing di_map =\n    domain::CoordinateMap<Frame::Distorted, Frame::Inertial, Maps...>;\n\ntemplate <typename List>\nstruct power_set {\n  using rest = typename power_set<tmpl::pop_front<List>>::type;\n  using type = tmpl::append<\n      rest, tmpl::transform<rest, tmpl::lazy::push_front<\n                                      tmpl::_1, tmpl::pin<tmpl::front<List>>>>>;\n};\n\ntemplate <>\nstruct power_set<tmpl::list<>> {\n  using type = tmpl::list<tmpl::list<>>;\n};\n\ntemplate <typename SourceFrame, typename TargetFrame, typename Maps>\nusing produce_all_maps_helper =\n    tmpl::wrap<tmpl::push_front<Maps, SourceFrame, TargetFrame>,\n               domain::CoordinateMap>;\n\n/*\n * This will produce all the possible combinations of maps we will need for the\n * maps_list. It does so using a power set. Say you have 3 maps, Map1, Map2,\n * Map3. The end result of this will be a list with the following map\n * combinations where these combinations may not appear in the same position in\n * the list, but the order of each combination is fixed:\n *  - Map1\n *  - Map2\n *  - Map3\n *  - Map1, Map2\n *  - Map1, Map3\n *  - Map2, Map3\n *  - Map1, Map2, Map3\n */\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nusing produce_all_maps = tmpl::transform<\n    tmpl::remove<typename power_set<tmpl::list<Maps...>>::type, tmpl::list<>>,\n    tmpl::bind<produce_all_maps_helper, tmpl::pin<SourceFrame>,\n               tmpl::pin<TargetFrame>, tmpl::_1>>;\n}  // namespace detail\n\n/*!\n * \\brief This holds all options related to the time dependent maps of the\n * binary compact object domains.\n *\n * \\details Since both domains will have the same (overall) time dependent maps,\n * their options are going to be the same as well. The options won't be exactly\n * the same though, so there is a \\p IsCylindrical template parameter to\n * distinguish.\n *\n * This class will create the FunctionsOfTime needed for the binary compact\n * object domains as well as the actual CoordinateMap%s themselves\n *\n * \\note This struct contains no information about what blocks the time\n * dependent maps will go in.\n */\ntemplate <bool IsCylindrical>\nstruct TimeDependentMapOptions {\n private:\n  template <typename SourceFrame, typename TargetFrame>\n  using MapType =\n      std::unique_ptr<domain::CoordinateMapBase<SourceFrame, TargetFrame, 3>>;\n  // Time-dependent maps\n  using Expansion = domain::CoordinateMaps::TimeDependent::CubicScale<3>;\n  using Rotation = domain::CoordinateMaps::TimeDependent::Rotation<3>;\n  using RotScaleTrans = domain::CoordinateMaps::TimeDependent::RotScaleTrans<3>;\n  using Skew = domain::CoordinateMaps::TimeDependent::Skew;\n  using Shape = domain::CoordinateMaps::TimeDependent::Shape;\n  using Identity = domain::CoordinateMaps::Identity<3>;\n\n public:\n  using maps_list = tmpl::append<\n      // We need this odd-one-out Identity map because all maps are optional to\n      // specify. It's possible to specify a shape map, but no expansion or\n      // rotation map. So we have a grid to distorted map, and need an identity\n      // distorted to inertial map. We don't need an identity grid to distorted\n      // map because if a user requests the grid to distorted frame map with a\n      // distorted frame, but didn't specify shape map options, an error occurs.\n      tmpl::list<detail::di_map<Identity>>,\n      detail::produce_all_maps<Frame::Grid, Frame::Inertial, Shape, Expansion,\n                               Rotation>,\n      detail::produce_all_maps<Frame::Grid, Frame::Distorted, Shape>,\n      detail::produce_all_maps<Frame::Distorted, Frame::Inertial, Expansion,\n                               Rotation>,\n      detail::produce_all_maps<Frame::Grid, Frame::Inertial, Shape,\n                               RotScaleTrans>,\n      detail::produce_all_maps<Frame::Grid, Frame::Inertial, Shape, Skew,\n                               RotScaleTrans>,\n      detail::produce_all_maps<Frame::Distorted, Frame::Inertial,\n                               RotScaleTrans>,\n      detail::produce_all_maps<Frame::Distorted, Frame::Inertial, Skew,\n                               RotScaleTrans>>;\n\n  /// \\brief The initial time of the functions of time.\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the functions of time\"};\n  };\n\n  /// \\brief Options for the expansion map.\n  /// The outer boundary radius of the map is always set to\n  /// the outer boundary of the Domain, so there is no option\n  /// here to set the outer boundary radius.\n  using ExpansionMapOptions =\n      domain::creators::time_dependent_options::ExpansionMapOptions<false>;\n  using ExpansionMapOptionType = typename ExpansionMapOptions::type::value_type;\n\n  /// \\brief Options for the rotation map\n  using RotationMapOptions =\n      domain::creators::time_dependent_options::RotationMapOptions<false>;\n  using RotationMapOptionType = typename RotationMapOptions::type::value_type;\n\n  /// \\brief Options for the Translation Map, the outer radius is always set to\n  /// the outer boundary of the Domain, so there's no option needed for outer\n  /// boundary.\n  using TranslationMapOptions =\n      domain::creators::time_dependent_options::TranslationMapOptions<3>;\n  using TranslationMapOptionType =\n      typename TranslationMapOptions::type::value_type;\n\n  /// \\brief Options for the Skew map\n  using SkewMapOptions =\n      domain::creators::time_dependent_options::SkewMapOptions;\n  using SkewMapOptionType = typename SkewMapOptions::type::value_type;\n\n  /// \\brief Options for the shape map\n  template <domain::ObjectLabel Object>\n  using ShapeMapOptions =\n      domain::creators::time_dependent_options::ShapeMapOptions<\n          not IsCylindrical, Object>;\n  template <domain::ObjectLabel Object>\n  using ShapeMapOptionType = typename ShapeMapOptions<Object>::type::value_type;\n\n  /// \\brief Options for tracking the grid centers.\n  ///\n  /// This is only used in binary neutron star simulations right now.\n  using GridCentersOptions = time_dependent_options::GridCentersOptions;\n  using GridCentersOptionType =\n      typename GridCentersOptions::type::value_type;\n\n  using options =\n      tmpl::list<InitialTime, ExpansionMapOptions, RotationMapOptions,\n                 TranslationMapOptions, SkewMapOptions,\n                 ShapeMapOptions<domain::ObjectLabel::A>,\n                 ShapeMapOptions<domain::ObjectLabel::B>, GridCentersOptions>;\n  static constexpr Options::String help{\n      \"The options for all time dependent maps in a binary compact object \"\n      \"domain. Specify 'None' to not use any time dependent maps.\"};\n\n  TimeDependentMapOptions() = default;\n\n  TimeDependentMapOptions(\n      double initial_time, ExpansionMapOptionType expansion_map_options,\n      RotationMapOptionType rotation_map_options,\n      TranslationMapOptionType translation_map_options,\n      SkewMapOptionType skew_map_options,\n      ShapeMapOptionType<domain::ObjectLabel::A> shape_options_A,\n      ShapeMapOptionType<domain::ObjectLabel::B> shape_options_B,\n      GridCentersOptionType grid_centers,\n      const Options::Context& context = {});\n\n  /*!\n   * \\brief Create the function of time map using the options that were\n   * provided to this class.\n   *\n   * Currently, this will add:\n   *\n   * - Skew: `PiecewisePolynomial<2>`\n   * - Expansion: `PiecewisePolynomial<2>`\n   * - ExpansionOuterBoundary: `FixedSpeedCubic`\n   * - Rotation: `QuaternionFunctionOfTime<3>`\n   * - Translation: `PiecewisePolynomial<3>`\n   * - SizeA/B: `PiecewisePolynomial<3>`\n   * - ShapeA/B: `PiecewisePolynomial<2>`\n   * - GridCenters: `PiecewisePolynomial<2>`\n   *\n   *  When `UseWorldtube` is set to true, they are\n   *\n   * - Skew: None\n   * - Expansion: `IntegratedFunctionOfTime`\n   * - ExpansionOuterBoundary: `FixedSpeedCubic`\n   * - Rotation: `IntegratedFunctionOfTime`\n   * - Translation: None\n   * - SizeA/B: IntegratedFunctionOfTime\n   * - ShapeA/B: `PiecewisePolynomial<2>`\n   */\n  template <bool UseWorldtube = false>\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n  create_functions_of_time(const std::unordered_map<std::string, double>&\n                               initial_expiration_times) const;\n\n  /*!\n   * \\brief Construct the actual maps that will be used.\n   *\n   * Currently, this constructs a:\n   *\n   * - Skew: `domain::CoordinateMaps::TimeDependent::Skew`\n   * - Rotation, Expansion, Translation: `RotScaleTrans<3>`\n   * - ShapeA/B: `Shape` (with size FunctionOfTime)\n   *\n   * If the radii for an object are `std::nullopt`, this means that a Shape map\n   * is not constructed for that object. An identity map will be used instead.\n   * This happens when the object is covered by a Cartesian cube, rather than a\n   * sphere.\n   * If \\p IsCylindrical is true, only pass two radii for the inner/outer radius\n   * of the object sphere. If it is false, pass three radii corresponding to the\n   * excision radius, the outer radius of the inner sphere, and the radius of\n   * the surrounding cube.\n   *\n   * If a shape map is requested and the object is excised, then the shape map\n   * will deform the inner excision surface of the object. This deformation\n   * either extends to the outer radius of the sphere, or further to the edge of\n   * the cube that surrounds the sphere, depending on the `TransitionEndsAtCube`\n   * option.\n   * If the object is filled, then the shape map will deform the outer radius of\n   * the sphere that covers the object and the `TransitionEndsAtCube` option\n   * must be `true`.\n   */\n  void build_maps(\n      const std::array<std::array<double, 3>, 2>& object_centers,\n      const std::optional<std::array<double, 3>>& cube_A_center,\n      const std::optional<std::array<double, 3>>& cube_B_center,\n      const std::array<double, 3>& center_of_mass,\n      const std::optional<std::array<double, IsCylindrical ? 2 : 3>>&\n          object_A_radii,\n      const std::optional<std::array<double, IsCylindrical ? 2 : 3>>&\n          object_B_radii,\n      bool object_A_filled, bool object_B_filled, double envelope_radius,\n      double domain_outer_radius);\n\n  /*!\n   * \\brief Check whether options were specified in the constructor for the\n   * shape map of this object\n   */\n  bool has_distorted_frame_options(domain::ObjectLabel object) const;\n\n  /*!\n   * \\brief Type to pass to `grid_to_distorted_map()` and\n   * `grid_to_inertial_map()`.\n   *\n   * \\details If \\p IsCylindrical is true, pass a `bool` for whether to include\n   * a shape map or not. If it's false, pass a `std::optional<size_t>`. If this\n   * has a value, then it includes a shape map. The `size_t` represents the\n   * relative block number around each object in the BinaryCompactObject domain.\n   * It should go from 0 to 11 for the 12 blocks surrounding each object.\n   */\n  using IncludeDistortedMapType =\n      tmpl::conditional_t<IsCylindrical, bool, std::optional<size_t>>;\n\n  /*!\n   * \\brief This will construct the map from `Frame::Distorted` to\n   * `Frame::Inertial`\n   *\n   * If we are including a shape map, then this will be a `RotScaleTrans` map.\n   * If we aren't, this returns a `nullptr`.\n   *\n   * \\see IncludeDistortedMapType\n   */\n  template <domain::ObjectLabel Object>\n  MapType<Frame::Distorted, Frame::Inertial> distorted_to_inertial_map(\n      const IncludeDistortedMapType& include_distorted_map,\n      bool use_rigid_map) const;\n\n  /*!\n   * \\brief This will construct the maps from the `Frame::Grid` to the\n   * `Frame::Distorted`.\n   *\n   * If we are including a shape map, then this will be a `Shape` map (with size\n   * FunctionOfTime) for the templated `Object`. If we aren't, then this returns\n   * a `nullptr`.\n   *\n   * \\see IncludeDistortedMapType\n   */\n  template <domain::ObjectLabel Object>\n  MapType<Frame::Grid, Frame::Distorted> grid_to_distorted_map(\n      const IncludeDistortedMapType& include_distorted_map) const;\n\n  /*!\n   * \\brief This will construct the entire map from the `Frame::Grid` to the\n   * `Frame::Inertial`.\n   *\n   * If we are including a shape map, then this map will have a composition of a\n   * `Shape` (with size FunctionOfTime) and `RotScaleTrans` map. If\n   * not, there will only be a `RotScaleTrans` map.\n   *\n   * If \\p return_excision_map is true, this will use a special shape map within\n   * the excision. Also if \\p return_excision_map is true, then\n   * \\p include_distorted_map must be either 'true' (if \\p IsCylindrical is\n   * true) or have a value less than 6, but it doesn't matter what value\n   * (because inside the excision isn't a specific block).\n   *\n   * \\see IncludeDistortedMapType\n   */\n  template <domain::ObjectLabel Object>\n  MapType<Frame::Grid, Frame::Inertial> grid_to_inertial_map(\n      const IncludeDistortedMapType& include_distorted_map, bool use_rigid_map,\n      bool return_excision_map = false) const;\n\n  // Names are public because they need to be used when constructing maps in the\n  // BCO domain creators themselves\n  inline static const std::string expansion_name{\"Expansion\"};\n  inline static const std::string expansion_outer_boundary_name{\n      \"ExpansionOuterBoundary\"};\n  inline static const std::string rotation_name{\"Rotation\"};\n  inline static const std::string translation_name{\"Translation\"};\n  inline static const std::string skew_name{\"Skew\"};\n  inline static const std::array<std::string, 2> size_names{{\"SizeA\", \"SizeB\"}};\n  inline static const std::array<std::string, 2> shape_names{\n      {\"ShapeA\", \"ShapeB\"}};\n  inline static const std::string grid_centers_name{\"GridCenters\"};\n\n private:\n  static size_t get_index(domain::ObjectLabel object);\n\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  ExpansionMapOptionType expansion_map_options_;\n  RotationMapOptionType rotation_map_options_;\n  TranslationMapOptionType translation_map_options_;\n  SkewMapOptionType skew_map_options_;\n  ShapeMapOptionType<domain::ObjectLabel::A> shape_options_A_{};\n  ShapeMapOptionType<domain::ObjectLabel::B> shape_options_B_{};\n  GridCentersOptionType grid_centers_options_{};\n  std::array<std::optional<double>, 2> deformed_radii_{};\n\n  // Maps\n  std::optional<Expansion> expansion_map_{};\n  std::optional<Rotation> rotation_map_{};\n  std::optional<std::pair<RotScaleTrans, RotScaleTrans>> rot_scale_trans_map_{};\n  std::optional<Skew> skew_map_{};\n  using ShapeMapType =\n      tmpl::conditional_t<IsCylindrical, std::array<std::optional<Shape>, 4>,\n                          std::array<std::array<std::optional<Shape>, 13>, 2>>;\n  ShapeMapType shape_maps_{};\n\n  // helper function that creates the functions of time used by the worldtube\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n  create_worldtube_functions_of_time() const;\n};\n}  // namespace domain::creators::bco\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BinaryCompactObject.cpp\n  ExpansionMap.cpp\n  FromVolumeFile.cpp\n  GridCenters.cpp\n  RotationMap.cpp\n  ShapeMap.cpp\n  SkewMap.cpp\n  Sphere.cpp\n  TranslationMap.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BinaryCompactObject.hpp\n  ExpansionMap.hpp\n  FromVolumeFile.hpp\n  GridCenters.hpp\n  RotationMap.hpp\n  ShapeMap.hpp\n  SkewMap.hpp\n  Sphere.hpp\n  TranslationMap.hpp\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/ExpansionMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n\n#include <array>\n#include <memory>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstant.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\ntemplate <bool AllowSettleFoTs>\nExpansionMapOptions<AllowSettleFoTs>::ExpansionMapOptions(\n    const std::array<double, 3>& initial_values_in,\n    double decay_timescale_outer_boundary_in,\n    const std::array<double, 3>& initial_values_outer_boundary_in,\n    double decay_timescale_in, const Options::Context& context)\n    : decay_timescale_outer_boundary(decay_timescale_outer_boundary_in),\n      decay_timescale(decay_timescale_in) {\n  if constexpr (AllowSettleFoTs) {\n    initial_values = std::array{DataVector{initial_values_in[0]},\n                                DataVector{initial_values_in[1]},\n                                DataVector{initial_values_in[2]}};\n    initial_values_outer_boundary =\n        std::array{DataVector{initial_values_outer_boundary_in[0]},\n                   DataVector{initial_values_outer_boundary_in[1]},\n                   DataVector{initial_values_outer_boundary_in[2]}};\n  } else {\n    PARSE_ERROR(context,\n                \"This class does not allow SettleToConst functions of time, \"\n                \"but the constructor for allowing SettleToConst functions of \"\n                \"time was used. Please use the other constructor.\");\n  }\n}\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__)\n\ntemplate <bool AllowSettleFoTs>\nExpansionMapOptions<AllowSettleFoTs>::ExpansionMapOptions(\n    const std::array<double, 3>& initial_values_in,\n    double decay_timescale_outer_boundary_in,\n    double asymptotic_velocity_outer_boundary_in,\n    const Options::Context& /*context*/)\n    : decay_timescale_outer_boundary(decay_timescale_outer_boundary_in),\n      asymptotic_velocity_outer_boundary(\n          asymptotic_velocity_outer_boundary_in) {\n  initial_values = std::array{DataVector{initial_values_in[0]},\n                              DataVector{initial_values_in[1]},\n                              DataVector{initial_values_in[2]}};\n  initial_values_outer_boundary =\n      std::array{DataVector{1.0}, DataVector{0.0}, DataVector{0.0}};\n}\n\ntemplate <bool AllowSettleFoTs>\nFunctionsOfTimeMap get_expansion(\n    const std::variant<ExpansionMapOptions<AllowSettleFoTs>, FromVolumeFile>&\n        expansion_map_options,\n    const double initial_time, const double expiration_time) {\n  const std::string name{\"Expansion\"};\n  const std::string name_outer_boundary{\"ExpansionOuterBoundary\"};\n  FunctionsOfTimeMap result{};\n\n  if (std::holds_alternative<FromVolumeFile>(expansion_map_options)) {\n    const auto& from_vol_file = std::get<FromVolumeFile>(expansion_map_options);\n    auto volume_fot = from_vol_file.retrieve_function_of_time(\n        {name, name_outer_boundary}, initial_time);\n\n    // Expansion must be either a PiecewisePolynomial or a SettleToConstant\n    const auto* pp_volume_fot =\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n            volume_fot.at(name).get());\n    const auto* settle_volume_fot =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstant*>(\n            volume_fot.at(name).get());\n\n    if (UNLIKELY(pp_volume_fot == nullptr and settle_volume_fot == nullptr)) {\n      ERROR_NO_TRACE(\n          \"Expansion function of time read from volume data is not a \"\n          \"PiecewisePolynomial<2> or a SettleToConstant. Cannot use it to \"\n          \"initialize the expansion map.\");\n    }\n\n    if (from_vol_file.replay()) {\n      result[name] = std::move(volume_fot.at(name));\n    } else {\n      result[name] =\n          volume_fot.at(name)->create_at_time(initial_time, expiration_time);\n    }\n\n    // Outer boundary must be either a FixedSpeedCubic or a SettleToConstant\n    const auto* outer_boundary_cubic_volume_fot =\n        dynamic_cast<domain::FunctionsOfTime::FixedSpeedCubic*>(\n            volume_fot.at(name_outer_boundary).get());\n    const auto* outer_boundary_settle_volume_fot =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstant*>(\n            volume_fot.at(name_outer_boundary).get());\n\n    if (UNLIKELY(outer_boundary_cubic_volume_fot == nullptr and\n                 outer_boundary_settle_volume_fot == nullptr)) {\n      ERROR_NO_TRACE(\n          \"ExpansionOuterBoundary function of time read from volume data is \"\n          \"not a FixedSpeedCubic or a SettleToConstant. Cannot use it to \"\n          \"initialize the expansion map.\");\n    }\n\n    ASSERT(outer_boundary_cubic_volume_fot != nullptr or AllowSettleFoTs,\n           \"ExpansionOuterBoundary function of time in the volume file is a \"\n           \"SettleToConstant, but SettleToConstant functions of time aren't \"\n           \"allowed.\");\n\n    if (from_vol_file.replay()) {\n      result[name] = std::move(volume_fot.at(name_outer_boundary));\n    } else {\n      result[name_outer_boundary] =\n          volume_fot.at(name_outer_boundary)->get_clone();\n    }\n  } else if (std::holds_alternative<ExpansionMapOptions<AllowSettleFoTs>>(\n                 expansion_map_options)) {\n    const auto& hard_coded_options =\n        std::get<ExpansionMapOptions<AllowSettleFoTs>>(expansion_map_options);\n\n    if (hard_coded_options.asymptotic_velocity_outer_boundary.has_value()) {\n      result[name] =\n          std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n              initial_time, hard_coded_options.initial_values, expiration_time);\n      result[name_outer_boundary] =\n          std::make_unique<domain::FunctionsOfTime::FixedSpeedCubic>(\n              hard_coded_options.initial_values_outer_boundary[0][0],\n              initial_time,\n              hard_coded_options.asymptotic_velocity_outer_boundary.value(),\n              hard_coded_options.decay_timescale_outer_boundary);\n    } else {\n      ASSERT(hard_coded_options.decay_timescale.has_value(),\n             \"To construct an ExpansionMap SettleToConstant function of time, \"\n             \"a decay timescale must be supplied.\");\n      result[name] =\n          std::make_unique<domain::FunctionsOfTime::SettleToConstant>(\n              hard_coded_options.initial_values, initial_time,\n              hard_coded_options.decay_timescale.value());\n      result[name_outer_boundary] =\n          std::make_unique<domain::FunctionsOfTime::SettleToConstant>(\n              hard_coded_options.initial_values_outer_boundary, initial_time,\n              hard_coded_options.decay_timescale_outer_boundary);\n    }\n  } else {\n    ERROR(\"Unknown ExpansionMap.\");\n  }\n\n  return result;\n}\n\n#define ALLOWSETTLE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                     \\\n  template struct ExpansionMapOptions<ALLOWSETTLE(data)>;        \\\n  template FunctionsOfTimeMap get_expansion(                     \\\n      const std::variant<ExpansionMapOptions<ALLOWSETTLE(data)>, \\\n                         FromVolumeFile>& expansion_map_options, \\\n      double initial_time, double expiration_time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (true, false))\n\n#undef INSTANTIATE\n#undef ALLOWSETTLE\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/ExpansionMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n/*!\n * \\brief Class that holds hard coded expansion map options from the input file.\n *\n * \\details This class can also be used as an option tag with the \\p type type\n * alias, `name()` function, and \\p help string.\n */\ntemplate <bool AllowSettleFoTs>\nstruct ExpansionMapOptions {\n  using type = Options::Auto<\n      std::variant<ExpansionMapOptions<AllowSettleFoTs>, FromVolumeFile>,\n      Options::AutoLabel::None>;\n  static std::string name() { return \"ExpansionMap\"; }\n  static constexpr Options::String help = {\n      \"Options for a time-dependent expansion of the coordinates. Specify \"\n      \"'None' to not use this map.\"};\n\n  struct InitialValues {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help =\n        \"Initial values for the expansion map, its velocity and acceleration.\";\n  };\n\n  struct InitialValuesOuterBoundary {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Initial values for the expansion map, its velocity and acceleration \"\n        \"at the outer boundary.\"};\n  };\n\n  struct DecayTimescaleOuterBoundary {\n    using type = double;\n    static constexpr Options::String help = {\n        \"A timescale for how fast the outer boundary expansion approaches its \"\n        \"asymptotic value.\"};\n  };\n\n  struct DecayTimescale {\n    using type = double;\n    static constexpr Options::String help = {\n        \"A timescale for how fast the expansion approaches its asymptotic \"\n        \"value with a SettleToConstant function of time.\"};\n  };\n\n  struct AsymptoticVelocityOuterBoundary {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The constant velocity of the outer boundary expansion.\"};\n  };\n\n  using common_options = tmpl::list<InitialValues, DecayTimescaleOuterBoundary>;\n  using settle_options =\n      tmpl::push_back<common_options, InitialValuesOuterBoundary,\n                      DecayTimescale>;\n  using non_settle_options =\n      tmpl::push_back<common_options, AsymptoticVelocityOuterBoundary>;\n\n  using options = tmpl::conditional_t<\n      AllowSettleFoTs,\n      tmpl::list<Options::Alternatives<settle_options, non_settle_options>>,\n      non_settle_options>;\n\n  ExpansionMapOptions() = default;\n  // Constructor for SettleToConstant functions of time\n  ExpansionMapOptions(\n      const std::array<double, 3>& initial_values_in,\n      double decay_timescale_outer_boundary_in,\n      const std::array<double, 3>& initial_values_outer_boundary_in,\n      double decay_timescale_in, const Options::Context& context = {});\n  // Constructor for non SettleToConstant functions of time\n  ExpansionMapOptions(const std::array<double, 3>& initial_values_in,\n                      double decay_timescale_outer_boundary_in,\n                      double asymptotic_velocity_outer_boundary_in,\n                      const Options::Context& context = {});\n\n  std::array<DataVector, 3> initial_values{};\n  std::array<DataVector, 3> initial_values_outer_boundary{};\n  double decay_timescale_outer_boundary{};\n  std::optional<double> decay_timescale;\n  std::optional<double> asymptotic_velocity_outer_boundary;\n};\n\n/*!\n * \\brief Helper functions that take the variant of the expansion map options,\n * and return the fully constructed expansion functions of time.\n *\n * \\details The function of time will have a new \\p initial_time and \\p\n * expiration_time, unless it is read from a file and is replaying.\n */\ntemplate <bool AllowSettleFoTs>\nFunctionsOfTimeMap get_expansion(\n    const std::variant<ExpansionMapOptions<AllowSettleFoTs>, FromVolumeFile>&\n        expansion_map_options,\n    double initial_time, double expiration_time);\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/FromVolumeFile.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n\n#include <array>\n#include <boost/math/quaternion.hpp>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionHelpers.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nFromVolumeFile::FromVolumeFile(std::string h5_filename,\n                               std::string subfile_name, const bool replay)\n    : h5_filename_(std::move(h5_filename)),\n      subfile_name_(std::move(subfile_name)),\n      replay_(replay) {}\n\nFunctionsOfTimeMap FromVolumeFile::retrieve_function_of_time(\n    const std::unordered_set<std::string>& function_of_time_names,\n    const double time) const {\n  const h5::H5File<h5::AccessType::ReadOnly> h5_file{h5_filename_};\n  const auto& vol_file = h5_file.get<h5::VolumeData>(subfile_name_);\n  const auto obs_id = vol_file.find_observation_id(time);\n  std::optional<std::vector<char>> serialized_functions_of_time =\n      vol_file.get_functions_of_time(obs_id);\n  if (not serialized_functions_of_time.has_value()) {\n    ERROR_NO_TRACE(\"There are no functions of time in the subfile \"\n                   << subfile_name_ << \" of the H5 file \" << h5_filename_\n                   << \". Choose a different subfile or H5 file.\");\n  }\n\n  const auto functions_of_time = deserialize<std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>>(\n      serialized_functions_of_time->data());\n\n  FunctionsOfTimeMap result{};\n  for (const std::string& function_of_time_name : function_of_time_names) {\n    if (not functions_of_time.contains(function_of_time_name)) {\n      ERROR_NO_TRACE(\"No function of time named \"\n                     << function_of_time_name << \" in the subfile \"\n                     << subfile_name_ << \" of the H5 file \" << h5_filename_);\n    }\n\n    const auto& function_of_time = functions_of_time.at(function_of_time_name);\n    const std::array<double, 2> time_bounds = function_of_time->time_bounds();\n\n    if (time < time_bounds[0] or time > time_bounds[1]) {\n      using ::operator<<;\n      ERROR_NO_TRACE(function_of_time_name\n                     << \": The requested time \" << time\n                     << \" is out of the range of the function of time \"\n                     << time_bounds << \" from the subfile \" << subfile_name_\n                     << \" of the H5 file \" << h5_filename_);\n    }\n\n    result[function_of_time_name] = function_of_time->get_clone();\n  }\n\n  return result;\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n/// @{\n/*!\n * \\brief Read in FunctionOfTime coefficients from an H5 file and volume\n * subfile.\n *\n * \\details The H5 file will only be accessed in the `retrieve_function_of_time`\n * function.\n */\nstruct FromVolumeFile {\n  struct H5Filename {\n    using type = std::string;\n    static constexpr Options::String help{\n        \"Name of H5 file to read functions of time from.\"};\n  };\n\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help{\n        \"Subfile that holds the volume data. Must be an h5::VolumeData \"\n        \"subfile.\"};\n  };\n\n  using options = tmpl::list<H5Filename, SubfileName>;\n  static constexpr Options::String help =\n      \"Read function of time coefficients from a volume subfile of an H5 file.\";\n\n  FromVolumeFile() = default;\n  // The replay option cannot be set from options\n  FromVolumeFile(std::string h5_filename, std::string subfile_name,\n                 bool replay = false);\n\n  /*!\n   * \\returns clones of all global functions of time\n   * in \\p function_of_time_names\n   *\n   * \\details If a value for \\p time is specified, will ensure that \\p time is\n   * within the `domain::FunctionsOfTime::FunctionOfTime::time_bounds()` of each\n   * function of time.\n   */\n  FunctionsOfTimeMap retrieve_function_of_time(\n      const std::unordered_set<std::string>& function_of_time_names,\n      double time) const;\n\n  /*!\n   * \\brief Whether the function of time from the volume file should just be\n   * replayed (read in and unmodified), or not.\n   */\n  bool replay() const { return replay_; }\n\n private:\n  std::string h5_filename_;\n  std::string subfile_name_;\n  bool replay_{false};\n};\n/// @}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/GridCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/GridCenters.hpp\"\n\n#include <array>\n#include <fstream>\n#include <iterator>\n#include <optional>\n#include <regex>\n#include <string>\n#include <variant>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nGridCentersOptions::GridCentersOptions() = default;\n\nGridCentersOptions::GridCentersOptions(\n    const std::string& spec_evolution_parameters_perl_file,\n    const std::optional<double> in_scale_inspiral_rate_by,\n    const Options::Context& context)\n    : initial_values{DataVector{6}, DataVector{6}, DataVector{6, 0.0}},\n      scale_inspiral_rate_by(in_scale_inspiral_rate_by) {\n  if (scale_inspiral_rate_by.has_value() and\n      scale_inspiral_rate_by.value() <= 0.0) {\n    PARSE_ERROR(context,\n                \"The inspiral rate must be scaled by a positive number but got \"\n                    << scale_inspiral_rate_by.value());\n  }\n\n  if (file_system::check_if_file_exists(spec_evolution_parameters_perl_file)) {\n    std::ifstream ifs{spec_evolution_parameters_perl_file};\n    const std::string spec_parameters(std::istreambuf_iterator<char>{ifs}, {});\n    ifs.close();\n\n    std::smatch match;\n\n    std::array<double, 3> center_a{};\n    std::array<double, 3> center_b{};\n\n    if (std::regex_search(spec_parameters, match,\n                          std::regex{\"@CenterNS1.*=.*\\\\((.*),(.*),(.*)\\\\);\"})) {\n      for (size_t i = 1; i < match.size(); ++i) {\n        const std::string m{match[i]};\n        gsl::at(center_b, i - 1) = std::strtod(m.c_str(), nullptr);\n      }\n    } else {\n      PARSE_ERROR(\n          context,\n          \"Failed to parse CenterNS1 (object B location) from SpEC file.\");\n    }\n\n    if (std::regex_search(spec_parameters, match,\n                          std::regex{\"@CenterNS2.*=.*\\\\((.*),(.*),(.*)\\\\);\"})) {\n      for (size_t i = 1; i < match.size(); ++i) {\n        const std::string m{match[i]};\n        gsl::at(center_a, i - 1) = std::strtod(m.c_str(), nullptr);\n      }\n    } else {\n      PARSE_ERROR(\n          context,\n          \"Failed to parse CenterNS2 (object A location) from SpEC file.\");\n    }\n\n    double adot0 = std::numeric_limits<double>::signaling_NaN();\n    if (std::regex_search(spec_parameters, match,\n                          std::regex{\"\\\\$ID_adot0.*=(.*);\"})) {\n      const std::string m{match[1]};\n      adot0 = std::strtod(m.c_str(), nullptr);\n    } else {\n      PARSE_ERROR(context, \"Failed to parse adot0 from SpEC file.\");\n    }\n    adot0 *= scale_inspiral_rate_by.value_or(1.0);\n\n    const std::array<double, 3> velocity_a{adot0, 0.0, 0.0};\n    const std::array<double, 3> velocity_b{-adot0, 0.0, 0.0};\n\n    for (size_t i = 0; i < 3; ++i) {\n      initial_values[0][i] = gsl::at(center_a, i);\n      initial_values[0][3 + i] = gsl::at(center_b, i);\n      initial_values[1][i] = gsl::at(velocity_a, i);\n      initial_values[1][3 + i] = gsl::at(velocity_b, i);\n    }\n  } else {\n    PARSE_ERROR(context, \"The SpEC EvolutionParameters.perl file \"\n                             << spec_evolution_parameters_perl_file\n                             << \" does not exist.\");\n  }\n}\n\nstd::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> get_grid_centers(\n    const GridCentersOptions& grid_centers_options,\n    const double initial_time, const double expiration_time) {\n  return std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n      initial_time, grid_centers_options.initial_values, expiration_time);\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/GridCenters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n/*!\n * \\brief Class that holds map options from the grid centers location.\n *\n * This is needed in BNS simulations where the stars are not at the centers of\n * the cubes.\n *\n * \\details This class can also be used as an option tag with the \\p type type\n * alias, `name()` function, and \\p help string.\n */\nstruct GridCentersOptions {\n  using type = Options::Auto<GridCentersOptions, Options::AutoLabel::None>;\n  static std::string name() { return \"GridCenters\"; }\n\n  struct SpecEvolutionParametersPerlFile {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"Path to the EvolutionParameters.perl file that SpEC outputs to parse \"\n        \"for the initial positions and velocities.\"};\n  };\n\n  struct ScaleInspiralRateBy {\n    using type = Options::Auto<double>;\n    static constexpr Options::String help = {\n        \"The inspiral rate from the initial data is not exact because of gauge \"\n        \"changes and in general that initial data is not perfect. The control \"\n        \"system can have an easier time if the inspiral rate is scaled in some \"\n        \"cases. You typically shouldn't need to scale by more than a factor of \"\n        \"2 larger or smaller.\"};\n  };\n\n  using options =\n      tmpl::list<SpecEvolutionParametersPerlFile, ScaleInspiralRateBy>;\n  static constexpr Options::String help = {\n      \"Sets the initial value of the GridCenters of the objects.\"};\n\n  GridCentersOptions();\n  explicit GridCentersOptions(\n      const std::string& spec_evolution_parameters_perl_file,\n      std::optional<double> in_scale_inspiral_rate_by,\n      const Options::Context& context = {});\n\n  std::array<DataVector, 3> initial_values{};\n  std::optional<double> scale_inspiral_rate_by{std::nullopt};\n};\n\n/*!\n * \\brief Helper function that creates the FunctionOfTime from the center\n * options, initial time, and expiration time.\n */\nstd::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> get_grid_centers(\n    const GridCentersOptions& grid_centers_options, double initial_time,\n    double expiration_time);\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/BinaryCompactObject.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/Python/BinaryCompactObject.hpp\"\n\n#include <array>\n#include <optional>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/GridCenters.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/SkewMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::time_dependent_options::py_bindings {\nvoid bind_binary_compact_object(py::module& m) {\n  py::class_<domain::creators::bco::TimeDependentMapOptions<false>>(\n      m, \"BinaryCompactObjectTimeDependentOptions\")\n      .def(py::init<\n               double,\n               std::optional<domain::creators::time_dependent_options::\n                                 ExpansionMapOptions<false>>,\n               std::optional<domain::creators::time_dependent_options::\n                                 RotationMapOptions<false>>,\n               std::optional<domain::creators::time_dependent_options::\n                                 TranslationMapOptions<3>>,\n               std::optional<\n                   domain::creators::time_dependent_options::SkewMapOptions>,\n               std::optional<domain::creators::time_dependent_options::\n                                 ShapeMapOptions<true, ObjectLabel::A>>,\n               std::optional<domain::creators::time_dependent_options::\n                                 ShapeMapOptions<true, ObjectLabel::B>>,\n               std::optional<domain::creators::time_dependent_options::\n                                 GridCentersOptions>>(),\n           py::arg(\"initial_time\"), py::arg(\"expansion_map_options\"),\n           py::arg(\"rotation_map_options\"), py::arg(\"translation_map_options\"),\n           py::arg(\"skew_map_options\"), py::arg(\"shape_options_A\"),\n           py::arg(\"shape_options_B\"), py::arg(\"grid_centers_options\"));\n}\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/BinaryCompactObject.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::time_dependent_options::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\n// For now this does not support SettleToConst maps.\nvoid bind_binary_compact_object(pybind11::module& m);\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"Domain/Creators/TimeDependentOptions/Python/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Python/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Python/GridCenters.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Python/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Python/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Python/SkewMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Python/TranslationMap.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n\nPYBIND11_MODULE(_Pybindings, m) {\n  py_bindings::bind_binary_compact_object(m);\n  py_bindings::bind_expansion_map(m);\n  py_bindings::bind_grid_centers(m);\n  py_bindings::bind_translation_map(m);\n  py_bindings::bind_skew_map(m);\n  py_bindings::bind_rotation_map(m);\n  py_bindings::bind_shape_map(m);\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyDomainTimeDependentOptions\")\n\nspectre_python_add_module(\n    TimeDependentOptions\n    LIBRARY_NAME ${LIBRARY}\n    MODULE_PATH \"Domain/Creators\"\n    SOURCES\n    BinaryCompactObject.cpp\n    Bindings.cpp\n    ExpansionMap.cpp\n    GridCenters.cpp\n    RotationMap.cpp\n    ShapeMap.cpp\n    SkewMap.cpp\n    TranslationMap.cpp\n    PYTHON_FILES\n    __init__.py\n)\n\nspectre_python_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n    BinaryCompactObject.hpp\n    ExpansionMap.hpp\n    GridCenters.hpp\n    RotationMap.hpp\n    ShapeMap.hpp\n    SkewMap.hpp\n    TranslationMap.hpp\n)\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DomainCreators\n  DomainTimeDependence\n  pybind11::module\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDomainCreators\n  )\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/ExpansionMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/Python/ExpansionMap.hpp\"\n\n#include <array>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::time_dependent_options::py_bindings {\nvoid bind_expansion_map(py::module& m) {\n  py::class_<time_dependent_options::ExpansionMapOptions<false>>(\n      m, \"ExpansionMapOptions\")\n      .def(py::init<std::array<double, 3>, double, double>(),\n           py::arg(\"initial_values_in\"),\n           py::arg(\"decay_timescale_outer_boundary_in\"),\n           py::arg(\"asymptotic_velocity_outer_boundary_in\"));\n}\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/ExpansionMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::time_dependent_options::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_expansion_map(pybind11::module& m);\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/GridCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/Python/GridCenters.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/GridCenters.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::time_dependent_options::py_bindings {\nvoid bind_grid_centers(py::module& m) {\n  py::class_<time_dependent_options::GridCentersOptions>(m,\n                                                         \"GridCentersOptions\")\n      .def(py::init<std::string, std::optional<double>>(),\n           py::arg(\"spec_evolution_parameters_perl_file\"),\n           py::arg(\"in_scale_inspiral_rate_by\"));\n}\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/GridCenters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::time_dependent_options::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_grid_centers(pybind11::module& m);\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/RotationMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/Python/RotationMap.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <vector>\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::time_dependent_options::py_bindings {\nvoid bind_rotation_map(py::module& m) {\n  py::class_<time_dependent_options::RotationMapOptions<false>>(\n      m, \"RotationMapOptions\")\n      .def(py::init<std::vector<std::array<double, 4>>, double>(),\n           py::arg(\"initial_values_in\"), py::arg(\"decay_timescale_in\"));\n}\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/RotationMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::time_dependent_options::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_rotation_map(pybind11::module& m);\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/ShapeMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/Python/ShapeMap.hpp\"\n\n#include <array>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::time_dependent_options::py_bindings {\nnamespace {\nvoid bind_kerrschild_from_boyer_lindquist_impl(py::module& m) {\n  py::class_<time_dependent_options::KerrSchildFromBoyerLindquist>(\n      m, \"KerrSchildFromBoyerLindquist\")\n      .def(py::init<double, std::array<double, 3>>(), py::arg(\"mass\"),\n           py::arg(\"spin\"));\n}\n\nvoid bind_ylms_from_file_impl(py::module& m) {\n  py::class_<time_dependent_options::YlmsFromFile>(m, \"YlmsFromFile\")\n      .def(py::init<std::string, std::vector<std::string>, double,\n                    std::optional<double>, bool, bool>(),\n           py::arg(\"h5_filename\"), py::arg(\"subfile_names\"),\n           py::arg(\"match_time\"), py::arg(\"match_time_epsilon\") = std::nullopt,\n           py::arg(\"set_l1_coefs_to_zero\"), py::arg(\"check_frame\"));\n}\n\nvoid bind_ylms_from_spec_impl(py::module& m) {\n  py::class_<time_dependent_options::YlmsFromSpEC>(m, \"YlmsFromSpEC\")\n      .def(py::init<std::string, double, std::optional<double>, bool>(),\n           py::arg(\"dat_filename\"), py::arg(\"match_time\"),\n           py::arg(\"match_time_epsilon\"), py::arg(\"set_l1_coefs_to_zero\"));\n}\n\ntemplate <domain::ObjectLabel Object>\nvoid bind_shape_map_impl(py::module& m) {\n  py::class_<time_dependent_options::ShapeMapOptions<true, Object>>(\n      m, (\"ShapeMapOptions\" + get_output(name(Object))).c_str())\n      .def(\n          py::init<size_t,\n                   std::optional<std::variant<\n                       domain::creators::time_dependent_options::\n                           KerrSchildFromBoyerLindquist,\n                       domain::creators::time_dependent_options::YlmsFromFile,\n                       domain::creators::time_dependent_options::YlmsFromSpEC>>,\n                   std::optional<std::array<double, 3>>, double, bool>(),\n          py::arg(\"l_max\"), py::arg(\"initial_values\"),\n          py::arg(\"initial_size_values\") = std::nullopt,\n          py::arg(\"coefficient_truncation_limit\") = 0.0,\n          py::arg(\"transition_ends_at_cube\") = true);\n}\n}  // namespace\n\nvoid bind_shape_map(py::module& m) {\n  py_bindings::bind_kerrschild_from_boyer_lindquist_impl(m);\n  py_bindings::bind_shape_map_impl<ObjectLabel::A>(m);\n  py_bindings::bind_shape_map_impl<ObjectLabel::B>(m);\n  py_bindings::bind_shape_map_impl<ObjectLabel::C>(m);\n  py_bindings::bind_shape_map_impl<ObjectLabel::None>(m);\n  py_bindings::bind_ylms_from_file_impl(m);\n  py_bindings::bind_ylms_from_spec_impl(m);\n}\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/ShapeMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\nnamespace domain::creators::time_dependent_options::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_shape_map(pybind11::module& m);\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/SkewMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/Python/SkewMap.hpp\"\n\n#include <array>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/SkewMap.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::time_dependent_options::py_bindings {\nvoid bind_skew_map(py::module& m) {\n  py::class_<time_dependent_options::SkewMapOptions>(m, \"SkewMapOptions\")\n      .def(py::init<std::array<double, 3>, std::array<double, 3>>(),\n           py::arg(\"initial_angles_y_in\"), py::arg(\"initial_angles_z_in\"));\n}\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/SkewMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::time_dependent_options::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_skew_map(pybind11::module& m);\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/TranslationMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/Python/TranslationMap.hpp\"\n\n#include <array>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::creators::time_dependent_options::py_bindings {\nvoid bind_translation_map(py::module& m) {\n  py::class_<time_dependent_options::TranslationMapOptions<3>>(\n      m, \"TranslationMapOptions\")\n      .def(py::init<std::array<std::array<double, 3>, 3>>(),\n           py::arg(\"initial_values_in\"));\n}\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/TranslationMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::creators::time_dependent_options::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_translation_map(pybind11::module& m);\n}  // namespace domain::creators::time_dependent_options::py_bindings\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n\nShapeMapOptions = {\n    \"A\": ShapeMapOptionsA,\n    \"B\": ShapeMapOptionsB,\n    \"C\": ShapeMapOptionsC,\n    \"None\": ShapeMapOptions,\n}\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/RotationMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain::creators::time_dependent_options {\ntemplate <bool AllowSettleFoTs>\nvoid RotationMapOptions<AllowSettleFoTs>::initialize_angles_and_quats() {\n  quaternions = make_array<3, DataVector>(DataVector{4, 0.0});\n  // Defautl to the identity quaternion\n  quaternions[0][0] = 1.0;\n  angles = make_array<4, DataVector>(DataVector{3, 0.0});\n}\n\ntemplate <bool AllowSettleFoTs>\nRotationMapOptions<AllowSettleFoTs>::RotationMapOptions(\n    const std::array<double, 3>& initial_angular_velocity,\n    const Options::Context& /*context*/) {\n  initialize_angles_and_quats();\n\n  angles[1] = DataVector{initial_angular_velocity};\n}\n\ntemplate <bool AllowSettleFoTs>\nRotationMapOptions<AllowSettleFoTs>::RotationMapOptions(\n    const std::vector<std::array<double, 4>>& initial_quaternions,\n    const double decay_timescale_in, const Options::Context& context)\n    : decay_timescale(decay_timescale_in) {\n  initialize_angles_and_quats();\n\n  if (initial_quaternions.empty() or initial_quaternions.size() > 3) {\n    PARSE_ERROR(\n        context,\n        \"Must specify at least the value of the quaternion, and optionally \"\n        \"up to 2 time derivatives.\");\n  }\n  for (size_t i = 0; i < initial_quaternions.size(); i++) {\n    gsl::at(quaternions, i) = DataVector{initial_quaternions[i]};\n  }\n}\n\ntemplate <bool AllowSettleFoTs>\nstd::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> get_rotation(\n    const std::variant<RotationMapOptions<AllowSettleFoTs>, FromVolumeFile>&\n        rotation_map_options,\n    const double initial_time, const double expiration_time) {\n  const std::string name = \"Rotation\";\n  std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> result{};\n\n  if (std::holds_alternative<FromVolumeFile>(rotation_map_options)) {\n    const auto& from_vol_file = std::get<FromVolumeFile>(rotation_map_options);\n    auto volume_fot =\n        from_vol_file.retrieve_function_of_time({name}, initial_time);\n\n    // It must be either a QuaternionFoT or a SettleToConstant\n    const auto* quat_volume_fot =\n        dynamic_cast<domain::FunctionsOfTime::QuaternionFunctionOfTime<3>*>(\n            volume_fot.at(name).get());\n    const auto* settle_volume_fot =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstantQuaternion*>(\n            volume_fot.at(name).get());\n\n    if (UNLIKELY(quat_volume_fot == nullptr and settle_volume_fot == nullptr)) {\n      ERROR_NO_TRACE(\n          \"Rotation function of time read from volume data is not a \"\n          \"QuaternionFunctionOfTime<3> or a SettleToConstantQuaternion. Cannot \"\n          \"use it to initialize the rotation map.\");\n    }\n\n    if (from_vol_file.replay()) {\n      result = std::move(volume_fot.at(name));\n    } else {\n      result =\n          volume_fot.at(name)->create_at_time(initial_time, expiration_time);\n    }\n  } else if (std::holds_alternative<RotationMapOptions<AllowSettleFoTs>>(\n                 rotation_map_options)) {\n    const auto& hard_coded_options =\n        std::get<RotationMapOptions<AllowSettleFoTs>>(rotation_map_options);\n\n    if (hard_coded_options.decay_timescale.has_value()) {\n      result =\n          std::make_unique<domain::FunctionsOfTime::SettleToConstantQuaternion>(\n              hard_coded_options.quaternions, initial_time,\n              hard_coded_options.decay_timescale.value());\n    } else {\n      result = std::make_unique<\n          domain::FunctionsOfTime::QuaternionFunctionOfTime<3>>(\n          initial_time, std::array{hard_coded_options.quaternions[0]},\n          hard_coded_options.angles, expiration_time);\n    }\n  } else {\n    ERROR(\"Unknown RotationMap.\");\n  }\n\n  return result;\n}\n\n#define ALLOWSETTLE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template struct RotationMapOptions<ALLOWSETTLE(data)>;                 \\\n  template std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>      \\\n  get_rotation(const std::variant<RotationMapOptions<ALLOWSETTLE(data)>, \\\n                                  FromVolumeFile>& rotation_map_options, \\\n               double initial_time, double expiration_time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (true, false))\n\n#undef INSTANTIATE\n#undef ALLOWSETTLE\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/RotationMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n/*!\n * \\brief Class that holds hard coded rotation map options from the input file.\n *\n * \\details This class can also be used as an option tag with the \\p type type\n * alias, `name()` function, and \\p help string.\n */\ntemplate <bool AllowSettleFoTs>\nstruct RotationMapOptions {\n  using type = Options::Auto<\n      std::variant<RotationMapOptions<AllowSettleFoTs>, FromVolumeFile>,\n      Options::AutoLabel::None>;\n  static std::string name() { return \"RotationMap\"; }\n  static constexpr Options::String help = {\n      \"Options for a time-dependent rotation of the coordinates. Specify \"\n      \"'None' to not use this map.\"};\n\n  struct InitialQuaternions {\n    using type = std::vector<std::array<double, 4>>;\n    static constexpr Options::String help = {\n        \"Initial values for the quaternion of the rotation map. You can \"\n        \"optionally specify its first two time derivatives. If time \"\n        \"derivatives aren't specified, zero will be used.\"};\n  };\n\n  struct InitialAngularVelocity {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\"The initial angular velocity.\"};\n  };\n\n  struct DecayTimescale {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The timescale for how fast the rotation approaches its asymptotic \"\n        \"value with a SettleToConstant function of time.\"};\n  };\n\n  using non_settle_options = tmpl::list<InitialAngularVelocity>;\n  using settle_options = tmpl::list<InitialQuaternions, DecayTimescale>;\n\n  using options = tmpl::conditional_t<\n      AllowSettleFoTs,\n      tmpl::list<Options::Alternatives<non_settle_options, settle_options>>,\n      non_settle_options>;\n\n  RotationMapOptions() = default;\n  // Constructor for non SettleToConstant functions of time\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  RotationMapOptions(const std::array<double, 3>& initial_angular_velocity,\n                     const Options::Context& context = {});\n  // Constructor for SettleToConst functions of time\n  RotationMapOptions(\n      const std::vector<std::array<double, 4>>& initial_quaternions,\n      double decay_timescale_in, const Options::Context& context = {});\n\n  std::array<DataVector, 3> quaternions{};\n  std::array<DataVector, 4> angles{};\n  std::optional<double> decay_timescale;\n\n private:\n  void initialize_angles_and_quats();\n};\n\n/*!\n * \\brief Helper function that takes the variant of the rotation map options,\n * and returns the fully constructed rotation function of time.\n *\n * \\details The function of time will have a new \\p initial_time and \\p\n * expiration_time, unless it is read from a file and is replaying.\n */\ntemplate <bool AllowSettleFoTs>\nstd::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> get_rotation(\n    const std::variant<RotationMapOptions<AllowSettleFoTs>, FromVolumeFile>&\n        rotation_map_options,\n    double initial_time, double expiration_time);\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/ShapeMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n\n#include <array>\n#include <cmath>\n#include <fstream>\n#include <istream>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"FromVolumeFile.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nKerrSchildFromBoyerLindquist::KerrSchildFromBoyerLindquist() = default;\nKerrSchildFromBoyerLindquist::KerrSchildFromBoyerLindquist(\n    const double mass_in, const std::array<double, 3> spin_in)\n    : mass(mass_in), spin(spin_in) {}\n\nYlmsFromFile::YlmsFromFile() = default;\nYlmsFromFile::YlmsFromFile(std::string h5_filename_in,\n                           std::vector<std::string> subfile_names_in,\n                           double match_time_in,\n                           std::optional<double> match_time_epsilon_in,\n                           bool set_l1_coefs_to_zero_in, bool check_frame_in)\n    : h5_filename(std::move(h5_filename_in)),\n      subfile_names(std::move(subfile_names_in)),\n      match_time(match_time_in),\n      match_time_epsilon(match_time_epsilon_in),\n      set_l1_coefs_to_zero(set_l1_coefs_to_zero_in),\n      check_frame(check_frame_in) {}\n\nYlmsFromSpEC::YlmsFromSpEC() = default;\nYlmsFromSpEC::YlmsFromSpEC(std::string dat_filename_in,\n                           const double match_time_in,\n                           const std::optional<double> match_time_epsilon_in,\n                           bool set_l1_coefs_to_zero_in)\n    : dat_filename(std::move(dat_filename_in)),\n      match_time(match_time_in),\n      match_time_epsilon(match_time_epsilon_in),\n      set_l1_coefs_to_zero(set_l1_coefs_to_zero_in) {}\n\ntemplate <ObjectLabel Object>\nFromVolumeFileShapeSize<Object>::FromVolumeFileShapeSize(\n    const std::optional<size_t>& l_max_in,\n    const double coefficient_truncation_limit_in,\n    const bool transition_ends_at_cube_in, std::string h5_filename,\n    std::string subfile_name, const Options::Context& context)\n    : FromVolumeFile(std::move(h5_filename), std::move(subfile_name)),\n      l_max(l_max_in),\n      transition_ends_at_cube(transition_ends_at_cube_in) {\n  if (this->replay() and l_max_in.has_value()) {\n    PARSE_ERROR(context,\n                \"Cannot supply LMax for shape map while also replaying.\");\n  }\n\n  if (l_max.has_value() and *l_max <= 1) {\n    PARSE_ERROR(context, \"Initial LMax must be 2 or greater but is \"\n                             << *l_max << \" instead.\");\n  }\n\n  if (coefficient_truncation_limit_in < 0.0) {\n    PARSE_ERROR(context,\n                \"CoefficientTruncationLimit must be non-negative, but is \"\n                    << coefficient_truncation_limit_in << \".\");\n  }\n  coefficient_truncation_limit = coefficient_truncation_limit_in;\n}\n\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\nShapeMapOptions<IncludeTransitionEndsAtCube, Object>::ShapeMapOptions(\n    const size_t l_max_in,\n    std::optional<\n        std::variant<KerrSchildFromBoyerLindquist, YlmsFromFile, YlmsFromSpEC>>\n        initial_values_in,\n    std::optional<std::array<double, 3>> initial_size_values_in,\n    const double coefficient_truncation_limit_in,\n    const Options::Context& context)\n    : ShapeMapOptions(l_max_in, std::move(initial_values_in),\n                      std::move(initial_size_values_in),\n                      coefficient_truncation_limit_in, false, context) {}\n\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\nShapeMapOptions<IncludeTransitionEndsAtCube, Object>::ShapeMapOptions(\n    const size_t l_max_in,\n    std::optional<\n        std::variant<KerrSchildFromBoyerLindquist, YlmsFromFile, YlmsFromSpEC>>\n        initial_values_in,\n    std::optional<std::array<double, 3>> initial_size_values_in,\n    const double coefficient_truncation_limit_in,\n    const bool transition_ends_at_cube_in, const Options::Context& context)\n    : l_max(l_max_in),\n      initial_values(std::move(initial_values_in)),\n      initial_size_values(initial_size_values_in),\n      coefficient_truncation_limit(coefficient_truncation_limit_in),\n      transition_ends_at_cube(transition_ends_at_cube_in) {\n  if (l_max <= 1) {\n    PARSE_ERROR(context, \"Initial LMax must be 2 or greater but is \"\n                             << l_max << \" instead.\");\n  }\n}\n\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\ndouble coefficient_truncation_limit_from_shape_options(\n    const std::variant<ShapeMapOptions<IncludeTransitionEndsAtCube, Object>,\n                       FromVolumeFileShapeSize<Object>>& shape_map_options) {\n  return std::visit(\n      [](auto variant) { return variant.coefficient_truncation_limit; },\n      shape_map_options);\n}\n\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\nbool transition_ends_at_cube_from_shape_options(\n    const std::variant<ShapeMapOptions<IncludeTransitionEndsAtCube, Object>,\n                       FromVolumeFileShapeSize<Object>>& shape_map_options) {\n  return std::visit(\n      [](auto variant) { return variant.transition_ends_at_cube; },\n      shape_map_options);\n}\n\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\nFunctionsOfTimeMap get_shape_and_size(\n    const std::variant<ShapeMapOptions<IncludeTransitionEndsAtCube, Object>,\n                       FromVolumeFileShapeSize<Object>>& shape_map_options,\n    const double initial_time, const double shape_expiration_time,\n    const double size_expiration_time, const double deformed_radius) {\n  const std::string shape_name = \"Shape\" + name(Object);\n  const std::string size_name = \"Size\" + name(Object);\n\n  FunctionsOfTimeMap result{};\n\n  if (std::holds_alternative<FromVolumeFileShapeSize<Object>>(\n          shape_map_options)) {\n    const auto& from_vol_file =\n        std::get<FromVolumeFileShapeSize<Object>>(shape_map_options);\n    auto volume_fots = from_vol_file.retrieve_function_of_time(\n        {shape_name, size_name}, initial_time);\n\n    const auto check_fot = [&]<size_t MaxDeriv>(const std::string& name) {\n      // It must be a PiecewisePolynomial\n      if (UNLIKELY(dynamic_cast<\n                       domain::FunctionsOfTime::PiecewisePolynomial<MaxDeriv>*>(\n                       volume_fots.at(name).get()) == nullptr)) {\n        ERROR_NO_TRACE(name << \" function of time read from volume data is not \"\n                               \"a PiecewisePolynomial<\"\n                            << MaxDeriv << \">. Cannot use it to initialize the \"\n                            << name << \" map.\");\n      }\n    };\n\n    check_fot.template operator()<2>(shape_name);\n    check_fot.template operator()<3>(size_name);\n\n    if (from_vol_file.replay()) {\n      result[shape_name] = std::move(volume_fots.at(shape_name));\n    } else {\n      // Not const in case we move it below\n      auto temporary_shape_fot =\n          volume_fots.at(shape_name)\n              ->create_at_time(initial_time, shape_expiration_time);\n      if (from_vol_file.l_max.has_value()) {\n        const size_t l_max = *from_vol_file.l_max;\n        const auto shape_funcs =\n            temporary_shape_fot->func_and_2_derivs(initial_time);\n\n        const size_t file_l_max =\n            -1 + sqrt(shape_funcs[0].size() / 2);  // NOLINT\n\n        // Prolong or restrict if necessary, otherwise just use the\n        // exact function of time from the volume file\n        if (file_l_max != l_max) {\n          std::array<DataVector, 3> new_shape_funcs{};\n          const ylm::Spherepack file_spherepack{file_l_max, file_l_max};\n          const ylm::Spherepack new_spherepack{l_max, l_max};\n          for (size_t i = 0; i < shape_funcs.size(); i++) {\n            gsl::at(new_shape_funcs, i) = file_spherepack.prolong_or_restrict(\n                gsl::at(shape_funcs, i), new_spherepack);\n          }\n\n          result[shape_name] =\n              std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n                  initial_time, std::move(new_shape_funcs),\n                  shape_expiration_time);\n        } else {\n          result[shape_name] = std::move(temporary_shape_fot);\n        }\n      } else {\n        result[shape_name] = std::move(temporary_shape_fot);\n      }\n    }\n\n    if (from_vol_file.replay()) {\n      result[size_name] = std::move(volume_fots.at(size_name));\n    } else {\n      result[size_name] = volume_fots.at(size_name)->create_at_time(\n          initial_time, size_expiration_time);\n    }\n\n    return result;\n  }\n\n  if (not std::holds_alternative<\n          ShapeMapOptions<IncludeTransitionEndsAtCube, Object>>(\n          shape_map_options)) {\n    ERROR(\"Unknown ShapeMap.\");\n  }\n\n  const auto& hard_coded_options =\n      std::get<ShapeMapOptions<IncludeTransitionEndsAtCube, Object>>(\n          shape_map_options);\n\n  const size_t l_max = hard_coded_options.l_max;\n  const DataVector shape_zeros{ylm::Spherepack::spectral_size(l_max, l_max),\n                               0.0};\n\n  std::array<DataVector, 3> shape_funcs =\n      make_array<3, DataVector>(shape_zeros);\n  std::array<DataVector, 4> size_funcs =\n      make_array<4, DataVector>(DataVector{1, 0.0});\n\n  if (hard_coded_options.initial_values.has_value()) {\n    if (std::holds_alternative<KerrSchildFromBoyerLindquist>(\n            hard_coded_options.initial_values.value())) {\n      const ylm::Spherepack ylm{hard_coded_options.l_max,\n                                hard_coded_options.l_max};\n      const auto& mass_and_spin = std::get<KerrSchildFromBoyerLindquist>(\n          hard_coded_options.initial_values.value());\n      const DataVector radial_distortion =\n          deformed_radius -\n          get(gr::Solutions::kerr_schild_radius_from_boyer_lindquist(\n              deformed_radius, ylm.theta_phi_points(), mass_and_spin.mass,\n              mass_and_spin.spin));\n      shape_funcs[0] = ylm.phys_to_spec(radial_distortion);\n      // Transform from SPHEREPACK to actual Ylm for size func\n      size_funcs[0][0] = shape_funcs[0][0] * sqrt(0.5 * M_PI);\n      // Set l=0 for shape map to 0 because size control will adjust l=0\n      shape_funcs[0][0] = 0.0;\n    } else if (std::holds_alternative<YlmsFromFile>(\n                   hard_coded_options.initial_values.value())) {\n      const auto& files =\n          std::get<YlmsFromFile>(hard_coded_options.initial_values.value());\n      const std::string& h5_filename = files.h5_filename;\n      const std::vector<std::string>& subfile_names = files.subfile_names;\n      const double match_time = files.match_time;\n      const double match_time_epsilon =\n          files.match_time_epsilon.value_or(1e-12);\n      const bool set_l1_coefs_to_zero = files.set_l1_coefs_to_zero;\n      ylm::SpherepackIterator iter{l_max, l_max};\n\n      for (size_t i = 0; i < subfile_names.size(); i++) {\n        // Frame doesn't matter here\n        const ylm::Strahlkorper<Frame::Distorted> file_strahlkorper =\n            ylm::read_surface_ylm_single_time<Frame::Distorted>(\n                h5_filename, gsl::at(subfile_names, i), match_time,\n                match_time_epsilon, files.check_frame);\n        const ylm::Strahlkorper<Frame::Distorted> this_strahlkorper{\n            hard_coded_options.l_max, 1.0, std::array{0.0, 0.0, 0.0}};\n\n        // The coefficients in the shape map are stored as the negative\n        // coefficients of the strahlkorper, so we need to multiply by -1 here.\n        gsl::at(shape_funcs, i) =\n            -1.0 * file_strahlkorper.ylm_spherepack().prolong_or_restrict(\n                       file_strahlkorper.coefficients(),\n                       this_strahlkorper.ylm_spherepack());\n        // Transform from SPHEREPACK to actual Ylm for size func\n        gsl::at(size_funcs, i)[0] =\n            gsl::at(shape_funcs, i)[0] * sqrt(0.5 * M_PI) +\n            // Account for the size of the original sphere, since the shape/size\n            // coefficients are deformations from the original sphere.\n            // The factor 2 sqrt(pi) is 1/Y_00.\n            deformed_radius * 2.0 * sqrt(M_PI);\n        // Set l=0 for shape map to 0 because size control will adjust l=0\n        gsl::at(shape_funcs, i)[0] = 0.0;\n        if (set_l1_coefs_to_zero) {\n          for (int m = -1; m <= 1; m++) {\n            gsl::at(shape_funcs, i)[iter.set(1_st, m)()] = 0.0;\n          }\n        }\n      }\n    } else if (std::holds_alternative<YlmsFromSpEC>(\n                   hard_coded_options.initial_values.value())) {\n      const auto& spec_option =\n          std::get<YlmsFromSpEC>(hard_coded_options.initial_values.value());\n      const std::string& dat_filename = spec_option.dat_filename;\n      const double match_time = spec_option.match_time;\n      const double match_time_epsilon =\n          spec_option.match_time_epsilon.value_or(1e-12);\n      const bool set_l1_coefs_to_zero = spec_option.set_l1_coefs_to_zero;\n\n      std::ifstream dat_file(dat_filename);\n      if (not dat_file.is_open()) {\n        ERROR(\"Unable to open SpEC dat file \" << dat_filename);\n      }\n      std::string line{};\n      size_t total_col = 0;\n      std::optional<size_t> file_l_max{};\n      std::array<double, 3> center{};\n      ModalVector coefficients{};\n      // This will be actually set below\n      ylm::SpherepackIterator file_iter{2, 2};\n\n      // We have to parse the dat file manually\n      while (std::getline(dat_file, line)) {\n        // Avoid comment lines. The SpEC file puts the legend in comments at the\n        // top of the file, so we count how many columns the dat file has based\n        // on the number of comment lines that are the legend (ends in ')')\n        if (line.starts_with(\"#\")) {\n          if (line.starts_with(\"# [\") and line.ends_with(\")\")) {\n            ++total_col;\n          }\n          continue;\n        }\n\n        std::stringstream ss(line);\n\n        double time = 0.0;\n        ss >> time;\n\n        // Set scale to current time plus 1 just in case time = 0\n        if (not equal_within_roundoff(time, match_time, match_time_epsilon,\n                                      time + 1.0)) {\n          continue;\n        }\n\n        if (file_l_max.has_value()) {\n          ERROR(\"Found more than one time in the SpEC dat file \"\n                << dat_filename << \" that is within a relative epsilon of \"\n                << match_time_epsilon << \" of the time requested \" << time);\n        }\n\n        // Casting to an integer floors a double, so we add 0.5 before we take\n        // the sqrt to avoid any rounding issues\n        const auto file_l_max_plus_one =\n            static_cast<size_t>(sqrt(static_cast<double>(total_col) + 0.5));\n        if (file_l_max_plus_one == 0) {\n          ERROR(\n              \"Invalid l_max from SpEC dat file. l_max + 1 was computed to be \"\n              \"0\");\n        }\n        file_l_max = file_l_max_plus_one - 1;\n\n        ss >> center[0];\n        ss >> center[1];\n        ss >> center[2];\n\n        coefficients.destructive_resize(ylm::Spherepack::spectral_size(\n            file_l_max.value(), file_l_max.value()));\n\n        file_iter =\n            ylm::SpherepackIterator{file_l_max.value(), file_l_max.value()};\n\n        for (int l = 0; l <= static_cast<int>(file_l_max.value()); l++) {\n          for (int m = -l; m <= l; m++) {\n            ss >> coefficients[file_iter.set(static_cast<size_t>(l), m)()];\n          }\n        }\n      }\n\n      if (not file_l_max.has_value()) {\n        ERROR_NO_TRACE(\"Unable to find requested time \"\n                       << time << \" within an epsilon of \" << match_time_epsilon\n                       << \" in SpEC dat file \" << dat_filename);\n      }\n\n      const ylm::Strahlkorper<Frame::Inertial> file_strahlkorper{\n          file_l_max.value(), file_l_max.value(), coefficients, center};\n      const ylm::Strahlkorper<Frame::Inertial> this_strahlkorper{\n          l_max, 1.0, std::array{0.0, 0.0, 0.0}};\n      ylm::SpherepackIterator iter{l_max, l_max};\n\n      shape_funcs[0] =\n          -1.0 * file_strahlkorper.ylm_spherepack().prolong_or_restrict(\n                     file_strahlkorper.coefficients(),\n                     this_strahlkorper.ylm_spherepack());\n      // Transform from SPHEREPACK to actual Ylm for size func\n      size_funcs[0][0] = shape_funcs[0][0] * sqrt(0.5 * M_PI);\n      // Set l=0 for shape map to 0 because size control will adjust l=0\n      shape_funcs[0][0] = 0.0;\n      if (set_l1_coefs_to_zero) {\n        for (int m = -1; m <= 1; m++) {\n          shape_funcs[0][iter.set(1_st, m)()] = 0.0;\n        }\n      }\n    }\n  }\n\n  // If any size options were specified, those override the values from the\n  // shape coefs\n  if (hard_coded_options.initial_size_values.has_value()) {\n    for (size_t i = 0; i < 3; i++) {\n      gsl::at(size_funcs, i)[0] =\n          gsl::at(hard_coded_options.initial_size_values.value(), i);\n    }\n  }\n\n  result[shape_name] =\n      std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time, std::move(shape_funcs), shape_expiration_time);\n  result[size_name] = std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n      initial_time, std::move(size_funcs), size_expiration_time);\n\n  return result;\n}\n\n#define OBJECT(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INCLUDETRANSITION(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template struct ShapeMapOptions<INCLUDETRANSITION(data), OBJECT(data)>; \\\n  template double coefficient_truncation_limit_from_shape_options(        \\\n      const std::variant<                                                 \\\n          ShapeMapOptions<INCLUDETRANSITION(data), OBJECT(data)>,         \\\n          FromVolumeFileShapeSize<OBJECT(data)>>& shape_map_options);     \\\n  template bool transition_ends_at_cube_from_shape_options(               \\\n      const std::variant<                                                 \\\n          ShapeMapOptions<INCLUDETRANSITION(data), OBJECT(data)>,         \\\n          FromVolumeFileShapeSize<OBJECT(data)>>& shape_map_options);     \\\n  template FunctionsOfTimeMap get_shape_and_size(                         \\\n      const std::variant<                                                 \\\n          ShapeMapOptions<INCLUDETRANSITION(data), OBJECT(data)>,         \\\n          FromVolumeFileShapeSize<OBJECT(data)>>& shape_map_options,      \\\n      double initial_time, double shape_expiration_time,                  \\\n      double size_expiration_time, double deformed_radius);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (domain::ObjectLabel::A, domain::ObjectLabel::B,\n                         domain::ObjectLabel::C, domain::ObjectLabel::None),\n                        (true, false))\n\n#undef INCLUDETRANSITION\n#undef INSTANTIATE\n\n#define INSTANTIATE(_, data) \\\n  template class FromVolumeFileShapeSize<OBJECT(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (domain::ObjectLabel::A, domain::ObjectLabel::B,\n                         domain::ObjectLabel::None))\n\n#undef OBJECT\n#undef INSTANTIATE\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/ShapeMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/ReadSurfaceYlm.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n/*!\n * \\brief Mass and spin necessary for calculating the \\f$ Y_{lm} \\f$\n * coefficients of a Kerr horizon of certain Boyer-Lindquist radius for the\n * shape map of the Sphere domain creator.\n */\nstruct KerrSchildFromBoyerLindquist {\n  /// \\brief The mass of the Kerr black hole.\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\"The mass of the Kerr BH.\"};\n  };\n  /// \\brief The dimensionless spin of the Kerr black hole.\n  struct Spin {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The dim'less spin of the Kerr BH.\"};\n  };\n\n  using options = tmpl::list<Mass, Spin>;\n\n  static constexpr Options::String help = {\n      \"Conform to an ellipsoid of constant Boyer-Lindquist radius in \"\n      \"Kerr-Schild coordinates. This Boyer-Lindquist radius is chosen as the \"\n      \"value of the 'InnerRadius'. To conform to the outer Kerr horizon, \"\n      \"choose an 'InnerRadius' of r_+ = M + sqrt(M^2-a^2).\"};\n\n  KerrSchildFromBoyerLindquist();\n  KerrSchildFromBoyerLindquist(double mass_in, std::array<double, 3> spin_in);\n\n  double mass{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> spin{std::numeric_limits<double>::signaling_NaN(),\n                             std::numeric_limits<double>::signaling_NaN(),\n                             std::numeric_limits<double>::signaling_NaN()};\n};\n\n/// Label for shape map options\nstruct Spherical {};\n\nstruct YlmsFromFile {\n  struct H5Filename {\n    using type = std::string;\n    static constexpr Options::String help =\n        \"Path to the data file containing the ylm coefficients and their \"\n        \"derivatives.\";\n  };\n\n  struct SubfileNames {\n    using type = std::vector<std::string>;\n    static constexpr Options::String help =\n        \"Subfile names for the different order derivatives of the ylm \"\n        \"coefficients. You must specify the subfile name for the ylm \"\n        \"coefficients themselves, and can optionally specify the subfile name \"\n        \"for the first and second time derivatives as well, in that order. If \"\n        \"you don't specify a derivative subfile, those coefficients will be \"\n        \"defaulted to zero.\";\n    static size_t lower_bound_on_size() { return 1; }\n    static size_t upper_bound_on_size() { return 3; }\n  };\n\n  struct MatchTime {\n    using type = double;\n    static constexpr Options::String help =\n        \"Time in the H5File to get the coefficients at. Will likely be the \"\n        \"same as the initial time\";\n  };\n\n  struct MatchTimeEpsilon {\n    using type = Options::Auto<double>;\n    static constexpr Options::String help =\n        \"Look for times in the H5File within this epsilon of the match time. \"\n        \"This is to avoid having to know the exact time to all digits. Default \"\n        \"is 1e-12.\";\n  };\n\n  struct SetL1CoefsToZero {\n    using type = bool;\n    static constexpr Options::String help =\n        \"Whether to set the L=1 coefs to zero or not. This may be desirable \"\n        \"because L=1 is degenerate with a translation of the BH.\";\n  };\n\n  struct CheckFrame {\n    using type = bool;\n    static constexpr Options::String help =\n        \"Whether to check if the frame of the Strahlkorper in the file matches \"\n        \"the Distorted frame.\";\n  };\n\n  using options = tmpl::list<H5Filename, SubfileNames, MatchTime,\n                             MatchTimeEpsilon, SetL1CoefsToZero, CheckFrame>;\n\n  static constexpr Options::String help = {\n      \"Read the Y_lm coefficients of a Strahlkorper from file and use those to \"\n      \"initialize the coefficients of a shape map.\"};\n  YlmsFromFile();\n  YlmsFromFile(std::string h5_filename_in,\n               std::vector<std::string> subfile_names_in, double match_time_in,\n               std::optional<double> match_time_epsilon_in,\n               bool set_l1_coefs_to_zero_in, bool check_frame_in = true);\n\n  std::string h5_filename;\n  std::vector<std::string> subfile_names;\n  double match_time{};\n  std::optional<double> match_time_epsilon;\n  bool set_l1_coefs_to_zero{};\n  bool check_frame{true};\n};\n\nstruct YlmsFromSpEC {\n  struct DatFilename {\n    using type = std::string;\n    static constexpr Options::String help =\n        \"Name of the SpEC dat file holding the coefficients. Note that this \"\n        \"isn't a Dat file within an H5 file. This must be an actual '.dat' \"\n        \"file on disk.\";\n  };\n\n  struct MatchTime {\n    using type = double;\n    static constexpr Options::String help =\n        \"Time in the H5File to get the coefficients at. Will likely be the \"\n        \"same as the initial time\";\n  };\n\n  struct MatchTimeEpsilon {\n    using type = Options::Auto<double>;\n    static constexpr Options::String help =\n        \"Look for times in the H5File within this epsilon of the match time. \"\n        \"This is to avoid having to know the exact time to all digits. Default \"\n        \"is 1e-12.\";\n  };\n\n  struct SetL1CoefsToZero {\n    using type = bool;\n    static constexpr Options::String help =\n        \"Whether to set the L=1 coefs to zero or not. This may be desirable \"\n        \"because L=1 is degenerate with a translation of the BH.\";\n  };\n\n  using options =\n      tmpl::list<DatFilename, MatchTime, MatchTimeEpsilon, SetL1CoefsToZero>;\n\n  static constexpr Options::String help = {\n      \"Read the Y_lm coefficients of a Strahlkorper from file and use those to \"\n      \"initialize the coefficients of a shape map.\"};\n  YlmsFromSpEC();\n  YlmsFromSpEC(std::string dat_filename_in, double match_time_in,\n               std::optional<double> match_time_epsilon_in,\n               bool set_l1_coefs_to_zero_in);\n\n  std::string dat_filename;\n  double match_time{};\n  std::optional<double> match_time_epsilon;\n  bool set_l1_coefs_to_zero{};\n};\n\nnamespace detail {\nstruct TransitionEndsAtCube {\n  using type = bool;\n  static constexpr Options::String help = {\n      \"If 'true', the shape map transition function will be 0 at the cubical \"\n      \"boundary around the object. If 'false' the transition function will \"\n      \"be 0 at the outer radius of the inner sphere around the object\"};\n};\n}  // namespace detail\n\n/*!\n * \\brief Specialized version of `FromVolumeFile` for the shape map\n *\n * \\details This is needed because the regular `FromVolumeFile` doesn't have\n * options for domain settings like `TransitionEndsAtCube` or `LMax`.\n */\ntemplate <ObjectLabel Object>\nstruct FromVolumeFileShapeSize : public FromVolumeFile {\n public:\n  struct CoefficientTruncationLimit {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Coefficients below this absolute value will be truncated from the \"\n        \"Shape map. Set to 0.0 to disable truncation.\"};\n    static constexpr type default_value = 0.0;\n  };\n  struct LMax {\n    using type = Options::Auto<size_t>;\n    static constexpr Options::String help = {\n        \"LMax used for the number of spherical harmonic coefficients of the \"\n        \"distortion map. If set to 'Auto', will use the LMax from the shape \"\n        \"function of time in the volume file.\"};\n  };\n  using options = tmpl::push_front<FromVolumeFile::options, LMax,\n                                   CoefficientTruncationLimit,\n                                   detail::TransitionEndsAtCube>;\n\n  FromVolumeFileShapeSize() = default;\n  FromVolumeFileShapeSize(const std::optional<size_t>& l_max_in,\n                          double coefficient_truncation_limit_in,\n                          bool transition_ends_at_cube_in,\n                          std::string h5_filename, std::string subfile_name,\n                          const Options::Context& context = {});\n\n  std::optional<size_t> l_max{};\n  double coefficient_truncation_limit{0.0};\n  bool transition_ends_at_cube{};\n\n private:\n  std::string h5_filename_;\n  std::string subfile_name_;\n};\n\n/*!\n * \\brief Class to be used as an option for initializing shape map coefficients.\n *\n * \\details This class can also be used as an option tag with the \\p type type\n * alias, `name()` function, and \\p help string.\n *\n * \\tparam IncludeTransitionEndsAtCube This is mainly added for the\n * `domain::creators::BinaryCompactObject` domain.\n * \\tparam Object Which object that this shape map represents. Use\n * `domain::ObjectLabel::None` if there is only a single object in your\n * simulation.\n */\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\nstruct ShapeMapOptions {\n  struct CoefficientTruncationLimit {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Coefficients below this absolute value will be truncated from the \"\n        \"Shape map. Set to 0.0 to disable truncation.\"};\n    static constexpr type default_value = 0.0;\n  };\n  using type = Options::Auto<\n      std::variant<ShapeMapOptions<IncludeTransitionEndsAtCube, Object>,\n                   FromVolumeFileShapeSize<Object>>,\n      Options::AutoLabel::None>;\n  static std::string name() { return \"ShapeMap\" + get_output(Object); }\n  static constexpr Options::String help = {\n      \"Options for a time-dependent distortion (shape) map about the \"\n      \"specified object. Specify 'None' to not use this map.\"};\n\n  struct LMax {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"LMax used for the number of spherical harmonic coefficients of the \"\n        \"distortion map.\"};\n  };\n\n  struct InitialValues {\n    using type = Options::Auto<\n        std::variant<KerrSchildFromBoyerLindquist, YlmsFromFile, YlmsFromSpEC>,\n        Spherical>;\n    static constexpr Options::String help = {\n        \"Initial Ylm coefficients for the shape map. Specify 'Spherical' for \"\n        \"all coefficients to be initialized to zero.\"};\n  };\n\n  struct SizeInitialValues {\n    using type = Options::Auto<std::array<double, 3>>;\n    static constexpr Options::String help = {\n        \"Initial value and two derivatives of the 00 coefficient. Specify \"\n        \"'Auto' to use the 00 coefficient specified in the 'InitialValues' \"\n        \"option. If you specify 'Auto', the deformed sphere will match the \"\n        \"'InitialValues' surface exactly, and the original radius will only \"\n        \"set the radius of the sphere in the grid frame (before deformation).\"};\n  };\n\n  using common_options = tmpl::list<LMax, InitialValues, SizeInitialValues,\n                                    CoefficientTruncationLimit>;\n\n  using options = tmpl::conditional_t<\n      IncludeTransitionEndsAtCube,\n      tmpl::push_back<common_options, detail::TransitionEndsAtCube>,\n      common_options>;\n  ShapeMapOptions() = default;\n  ShapeMapOptions(size_t l_max_in,\n                  std::optional<std::variant<KerrSchildFromBoyerLindquist,\n                                             YlmsFromFile, YlmsFromSpEC>>\n                      initial_values_in,\n                  std::optional<std::array<double, 3>> initial_size_values_in =\n                      std::nullopt,\n                  double coefficient_truncation_limit_in = 0.0,\n                  const Options::Context& context = {});\n  ShapeMapOptions(size_t l_max_in,\n                  std::optional<std::variant<KerrSchildFromBoyerLindquist,\n                                             YlmsFromFile, YlmsFromSpEC>>\n                      initial_values_in,\n                  std::optional<std::array<double, 3>> initial_size_values_in,\n                  double coefficient_truncation_limit_in,\n                  bool transition_ends_at_cube_in,\n                  const Options::Context& context = {});\n\n  size_t l_max{};\n  std::optional<\n      std::variant<KerrSchildFromBoyerLindquist, YlmsFromFile, YlmsFromSpEC>>\n      initial_values;\n  std::optional<std::array<double, 3>> initial_size_values;\n  double coefficient_truncation_limit{0.0};\n  bool transition_ends_at_cube{false};\n};\n\n/*!\n * \\brief Helper function to get the coefficient truncation limit from the\n * different variants that the shape map options could be.\n */\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\ndouble coefficient_truncation_limit_from_shape_options(\n    const std::variant<ShapeMapOptions<IncludeTransitionEndsAtCube, Object>,\n                       FromVolumeFileShapeSize<Object>>& shape_map_options);\n\n/*!\n * \\brief Helper function to get whether the shape map transition function ends\n * at the cube from the different variants that the shape map options could be.\n */\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\nbool transition_ends_at_cube_from_shape_options(\n    const std::variant<ShapeMapOptions<IncludeTransitionEndsAtCube, Object>,\n                       FromVolumeFileShapeSize<Object>>& shape_map_options);\n\n/*!\n * \\brief Helper function that takes the variant of the shape map options, and\n * returns the fully constructed shape and size functions of time.\n *\n * \\details Even if the functions of time are read from a file, they will have a\n * new \\p initial_time, \\p shape_expiration_time, and \\p size_expiration_time.\n * The \\p deformed_radius is only used for the non-volume file variants.\n */\ntemplate <bool IncludeTransitionEndsAtCube, domain::ObjectLabel Object>\nFunctionsOfTimeMap get_shape_and_size(\n    const std::variant<ShapeMapOptions<IncludeTransitionEndsAtCube, Object>,\n                       FromVolumeFileShapeSize<Object>>& shape_map_options,\n    double initial_time, double shape_expiration_time,\n    double size_expiration_time, double deformed_radius);\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/SkewMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/SkewMap.hpp\"\n\n#include <array>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nSkewMapOptions::SkewMapOptions(const std::array<double, 3>& initial_angles_y_in,\n                               const std::array<double, 3>& initial_angles_z_in)\n    : initial_angles_y(initial_angles_y_in),\n      initial_angles_z(initial_angles_z_in) {}\n\nstd::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> get_skew(\n    const std::variant<SkewMapOptions, FromVolumeFile>& skew_map_options,\n    const double initial_time, const double expiration_time) {\n  const std::string name{\"Skew\"};\n  std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> result{};\n\n  if (std::holds_alternative<FromVolumeFile>(skew_map_options)) {\n    const auto& from_vol_file = std::get<FromVolumeFile>(skew_map_options);\n    auto volume_fot =\n        from_vol_file.retrieve_function_of_time({name}, initial_time);\n\n    // It must be a PiecewisePolynomial\n    if (UNLIKELY(dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n                     volume_fot.at(name).get()) == nullptr)) {\n      ERROR_NO_TRACE(\n          \"Skew function of time read from volume data is not a \"\n          \"PiecewisePolynomial<2>. Cannot use it to initialize the skew map.\");\n    }\n\n    if (from_vol_file.replay()) {\n      result = std::move(volume_fot.at(name));\n    } else {\n      result =\n          volume_fot.at(name)->create_at_time(initial_time, expiration_time);\n    }\n  } else if (std::holds_alternative<SkewMapOptions>(skew_map_options)) {\n    const auto& hard_coded_options = std::get<SkewMapOptions>(skew_map_options);\n\n    result = std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n        initial_time,\n        std::array{\n            DataVector{hard_coded_options.initial_angles_y[0],\n                       hard_coded_options.initial_angles_z[0]},\n            DataVector{hard_coded_options.initial_angles_y[1],\n                       hard_coded_options.initial_angles_z[1]},\n            DataVector{hard_coded_options.initial_angles_y[2],\n                       hard_coded_options.initial_angles_z[2]},\n        },\n        expiration_time);\n  } else {\n    ERROR(\"Unknown SkewMap.\");\n  }\n\n  return result;\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/SkewMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n/*!\n * \\brief Class that holds hard coded skew map options from the input\n * file.\n *\n * \\details This class can also be used as an option tag with the \\p type type\n * alias, `name()` function, and \\p help string.\n */\nstruct SkewMapOptions {\n private:\n  struct Y {};\n  struct Z {};\n\n public:\n  using type = Options::Auto<std::variant<SkewMapOptions, FromVolumeFile>,\n                             Options::AutoLabel::None>;\n  static std::string name() { return \"SkewMap\"; }\n  static constexpr Options::String help = {\n      \"Options for a time-dependent skew of the x coordinate (y and z \"\n      \"coordinates are left unchanged). Specify 'None' to not use this map.\"};\n\n  template <typename Coord>\n  struct InitialValues {\n    using type = std::array<double, 3>;\n    static std::string name() {\n      return \"InitialValues\" + pretty_type::name<Coord>();\n    }\n    static constexpr Options::String help =\n        \"Initial value, and two time derivatives.\";\n  };\n\n  using options = tmpl::list<InitialValues<Y>, InitialValues<Z>>;\n\n  SkewMapOptions() = default;\n  SkewMapOptions(const std::array<double, 3>& initial_angles_y_in,\n                 const std::array<double, 3>& initial_angles_z_in);\n\n  std::array<double, 3> initial_angles_y{};\n  std::array<double, 3> initial_angles_z{};\n};\n\n/*!\n * \\brief Helper function that takes the variant of the skew map options,\n * and returns the fully constructed skew function of time.\n *\n * \\details The function of time will have a new \\p initial_time and \\p\n * expiration_time, unless it is read from a file and is replaying.\n */\nstd::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> get_skew(\n    const std::variant<SkewMapOptions, FromVolumeFile>& skew_map_options,\n    double initial_time, double expiration_time);\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Sphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n\n#include <array>\n#include <cmath>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Wedge.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstant.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace domain::creators::sphere {\n\nTimeDependentMapOptions::TimeDependentMapOptions(\n    const double initial_time, ShapeMapOptionType shape_map_options,\n    RotationMapOptionType rotation_map_options,\n    ExpansionMapOptionType expansion_map_options,\n    TranslationMapOptionType translation_map_options,\n    const bool transition_rot_scale_trans)\n    : initial_time_(initial_time),\n      shape_map_options_(std::move(shape_map_options)),\n      rotation_map_options_(std::move(rotation_map_options)),\n      expansion_map_options_(std::move(expansion_map_options)),\n      translation_map_options_(std::move(translation_map_options)),\n      transition_rot_scale_trans_(transition_rot_scale_trans) {}\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nTimeDependentMapOptions::create_functions_of_time(\n    const std::unordered_map<std::string, double>& initial_expiration_times)\n    const {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      result{};\n\n  // Get existing function of time names that are used for the maps and assign\n  // their initial expiration time to infinity (i.e. not expiring)\n  std::unordered_map<std::string, double> expiration_times{\n      {size_name, std::numeric_limits<double>::infinity()},\n      {shape_name, std::numeric_limits<double>::infinity()},\n      {rotation_name, std::numeric_limits<double>::infinity()},\n      {expansion_name, std::numeric_limits<double>::infinity()},\n      {translation_name, std::numeric_limits<double>::infinity()}};\n\n  // If we have control systems, overwrite these expiration times with the ones\n  // supplied by the control system\n  for (const auto& [name, expr_time] : initial_expiration_times) {\n    expiration_times[name] = expr_time;\n  }\n\n  if (shape_map_options_.has_value()) {\n    // ShapeMap and Size FunctionOfTime (used in ShapeMap)\n    auto shape_and_size = time_dependent_options::get_shape_and_size(\n        shape_map_options_.value(), initial_time_,\n        expiration_times.at(shape_name), expiration_times.at(size_name),\n        deformed_radius_);\n    result.merge(shape_and_size);\n  }\n\n  // ExpansionMap FunctionOfTime\n  if (expansion_map_options_.has_value()) {\n    auto expansion_functions_of_time = time_dependent_options::get_expansion(\n        expansion_map_options_.value(), initial_time_,\n        expiration_times.at(expansion_name));\n\n    result.merge(expansion_functions_of_time);\n  }\n\n  // RotationMap FunctionOfTime\n  if (rotation_map_options_.has_value()) {\n    result[rotation_name] = time_dependent_options::get_rotation(\n        rotation_map_options_.value(), initial_time_,\n        expiration_times.at(rotation_name));\n  }\n\n  // Translation FunctionOfTime\n  if (translation_map_options_.has_value()) {\n    result[translation_name] = time_dependent_options::get_translation(\n        translation_map_options_.value(), initial_time_,\n        expiration_times.at(translation_name));\n  }\n\n  return result;\n}\n\nvoid TimeDependentMapOptions::build_maps(\n    const std::array<double, 3>& center, const bool filled,\n    const double inner_radius, const std::vector<double>& radial_partitions,\n    const double outer_radius) {\n  filled_ = filled;\n  if (shape_map_options_.has_value()) {\n    const double coefficient_truncation_limit =\n        time_dependent_options::coefficient_truncation_limit_from_shape_options(\n            shape_map_options_.value());\n    std::unique_ptr<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                        ShapeMapTransitionFunction>\n        transition_func;\n    if (filled_) {\n      if (transition_rot_scale_trans_ and radial_partitions.size() < 2) {\n        ERROR(\n            \"Currently at least two radial partitions are required to \"\n            \"transition the RotScaleTrans map to zero in the outer shell \"\n            \"when a shape map is present and the interior is filled.\");\n      }\n      using WedgeTransition =\n          domain::CoordinateMaps::ShapeMapTransitionFunctions::Wedge;\n      // Shape map transitions from 0 to 1 from the inner cube to this surface\n      deformed_radius_ =\n          radial_partitions.empty() ? outer_radius : radial_partitions.front();\n      // Shape map transitions from 1 to 0 from the deformed surface to the next\n      // radial partition or to the outer radius\n      const bool has_shape_rolloff = not radial_partitions.empty();\n      const double shape_outer_radius =\n          radial_partitions.size() > 1 ? radial_partitions[1] : outer_radius;\n      // These must match the order of orientations_for_sphere_wrappings() in\n      // DomainHelpers.hpp. The values must match that of Wedge::Axis\n      const std::array<int, 6> axes{3, -3, 2, -2, 1, -1};\n      for (size_t j = 0; j < (has_shape_rolloff ? 12 : 6); ++j) {\n        if (j < 6) {\n          // Reverse the transition function so the shape map goes to zero at\n          // the inner cube\n          transition_func = std::make_unique<WedgeTransition>(\n              center, inner_radius, /* inner_sphericity */ 0.0, center,\n              deformed_radius_,\n              /* outer_sphericity */ 1.0,\n              static_cast<WedgeTransition::Axis>(gsl::at(axes, j)),\n              /* reverse */ true);\n        } else {\n          transition_func = std::make_unique<WedgeTransition>(\n              center, deformed_radius_, /* inner_sphericity */ 1.0, center,\n              shape_outer_radius,\n              /* outer_sphericity */ 1.0,\n              static_cast<WedgeTransition::Axis>(gsl::at(axes, j % 6)));\n        }\n        gsl::at(shape_maps_, j) =\n            ShapeMap{center, coefficient_truncation_limit,\n                     std::move(transition_func), shape_name, size_name};\n      }\n    } else {\n      // Shape map transitions from 1 to 0 from the inner radius to the first\n      // radial partition or to the outer radius\n      deformed_radius_ = inner_radius;\n      const double shape_outer_radius =\n          radial_partitions.empty() ? outer_radius : radial_partitions.front();\n      transition_func =\n          std::make_unique<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                               SphereTransition>(inner_radius,\n                                                 shape_outer_radius);\n      shape_maps_[0] =\n          ShapeMap{center, coefficient_truncation_limit,\n                   std::move(transition_func), shape_name, size_name};\n\n      // Interior map\n      transition_func =\n          std::make_unique<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                               SphereTransition>(\n              inner_radius, shape_outer_radius, false, true);\n      shape_maps_[1] =\n          ShapeMap{center, coefficient_truncation_limit,\n                   std::move(transition_func), shape_name, size_name};\n    }\n  }\n\n  const double outer_shell_inner_radius =\n      radial_partitions.empty() ? inner_radius : radial_partitions.back();\n  inner_rot_scale_trans_map_ = RotScaleTransMap{\n      expansion_map_options_.has_value()\n          ? std::optional<std::pair<\n                std::string, std::string>>{{expansion_name,\n                                            expansion_outer_boundary_name}}\n          : std::nullopt,\n      rotation_map_options_.has_value()\n          ? std::optional<std::string>{rotation_name}\n          : std::nullopt,\n      translation_map_options_.has_value()\n          ? std::optional<std::string>{translation_name}\n          : std::nullopt,\n      outer_shell_inner_radius,\n      outer_radius,\n      domain::CoordinateMaps::TimeDependent::RotScaleTrans<\n          3>::BlockRegion::Inner};\n  if (transition_rot_scale_trans_) {\n    if (radial_partitions.empty()) {\n      ERROR(\n          \"Currently at least one radial partition is required to transition \"\n          \"the RotScaleTrans map to zero in the outer shell.\");\n    }\n    transition_rot_scale_trans_map_ = RotScaleTransMap{\n        expansion_map_options_.has_value()\n            ? std::optional<std::pair<\n                  std::string, std::string>>{{expansion_name,\n                                              expansion_outer_boundary_name}}\n            : std::nullopt,\n        rotation_map_options_.has_value()\n            ? std::optional<std::string>{rotation_name}\n            : std::nullopt,\n        translation_map_options_.has_value()\n            ? std::optional<std::string>{translation_name}\n            : std::nullopt,\n        outer_shell_inner_radius,\n        outer_radius,\n        domain::CoordinateMaps::TimeDependent::RotScaleTrans<\n            3>::BlockRegion::Transition};\n  }\n}\n\n// If you edit any of the functions below, be sure to update the documentation\n// in the Sphere domain creator as well as this class' documentation.\nTimeDependentMapOptions::MapType<Frame::Distorted, Frame::Inertial>\nTimeDependentMapOptions::distorted_to_inertial_map(\n    const size_t block_number, const bool is_inner_cube,\n    const size_t num_blocks_per_shell) const {\n  const bool block_has_shape_map =\n      shape_map_options_.has_value() and\n      block_number <\n          (filled_ ? 2 * num_blocks_per_shell : num_blocks_per_shell) and\n      not is_inner_cube;\n  if (block_has_shape_map) {\n    return std::make_unique<DistortedToInertialComposition>(\n        inner_rot_scale_trans_map_);\n  } else {\n    return nullptr;\n  }\n}\n\nTimeDependentMapOptions::MapType<Frame::Grid, Frame::Distorted>\nTimeDependentMapOptions::grid_to_distorted_map(\n    const size_t block_number, const bool is_inner_cube,\n    const size_t num_blocks_per_shell) const {\n  const bool block_has_shape_map =\n      shape_map_options_.has_value() and\n      block_number <\n          (filled_ ? 2 * num_blocks_per_shell : num_blocks_per_shell) and\n      not is_inner_cube;\n  if (block_has_shape_map) {\n    // If the interior is not filled we use the SphereTransition function and\n    // build only one shape map at index 0 (see `build_maps` above). Otherwise,\n    // we use the Wedge transition function and build a shape map for each\n    // direction, so we have to use the block number here to get the correct\n    // shape map.\n    return std::make_unique<GridToDistortedComposition>(\n        gsl::at(shape_maps_, filled_ ? block_number : 0));\n  } else {\n    return nullptr;\n  }\n}\n\nTimeDependentMapOptions::MapType<Frame::Grid, Frame::Inertial>\nTimeDependentMapOptions::grid_to_inertial_map(\n    const size_t block_number, const bool is_outer_shell,\n    const bool is_central_region, const size_t num_blocks_per_shell) const {\n  const bool block_has_shape_map =\n      shape_map_options_.has_value() and\n      block_number <\n          (filled_ ? 2 * num_blocks_per_shell : num_blocks_per_shell) and\n      not(is_central_region and filled_);\n  if (block_has_shape_map) {\n    // If the interior is not filled we use the SphereTransition function and\n    // build only one shape map at index 0 (see `build_maps` above). Otherwise,\n    // we use the Wedge transition function and build a shape map for each\n    // direction, so we have to use the block number here to get the correct\n    // shape map.\n    return std::make_unique<GridToInertialComposition>(\n        gsl::at(shape_maps_,\n                filled_ ? block_number : (is_central_region ? 1 : 0)),\n        inner_rot_scale_trans_map_);\n  } else if (is_outer_shell and transition_rot_scale_trans_) {\n    return std::make_unique<GridToInertialSimple>(\n        transition_rot_scale_trans_map_);\n  } else {\n    return std::make_unique<GridToInertialSimple>(inner_rot_scale_trans_map_);\n  }\n}\n\nbool TimeDependentMapOptions::using_distorted_frame() const {\n  // We use shape map options and not the shape map just in case this is called\n  // before `build_maps` is called.\n  return shape_map_options_.has_value();\n}\n}  // namespace domain::creators::sphere\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/Sphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <variant>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/RotScaleTrans.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace domain::creators::sphere {\n/*!\n * \\brief This holds all options related to the time dependent maps of the\n * domain::creators::Sphere domain creator.\n */\nstruct TimeDependentMapOptions {\n private:\n  template <typename SourceFrame, typename TargetFrame>\n  using MapType =\n      std::unique_ptr<domain::CoordinateMapBase<SourceFrame, TargetFrame, 3>>;\n  using IdentityMap = domain::CoordinateMaps::Identity<3>;\n  // Time-dependent maps\n  using ShapeMap = domain::CoordinateMaps::TimeDependent::Shape;\n  using RotScaleTransMap =\n      domain::CoordinateMaps::TimeDependent::RotScaleTrans<3>;\n\n  template <typename SourceFrame, typename TargetFrame>\n  using IdentityForComposition =\n      domain::CoordinateMap<SourceFrame, TargetFrame, IdentityMap>;\n  using GridToDistortedComposition =\n      domain::CoordinateMap<Frame::Grid, Frame::Distorted, ShapeMap>;\n  using GridToInertialComposition =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, ShapeMap,\n                            RotScaleTransMap>;\n  using GridToInertialSimple =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, RotScaleTransMap>;\n  using DistortedToInertialComposition =\n      domain::CoordinateMap<Frame::Distorted, Frame::Inertial,\n                            RotScaleTransMap>;\n  using GridToInertialShapeMap =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, ShapeMap>;\n\n public:\n  using maps_list =\n      tmpl::list<IdentityForComposition<Frame::Grid, Frame::Inertial>,\n                 IdentityForComposition<Frame::Grid, Frame::Distorted>,\n                 IdentityForComposition<Frame::Distorted, Frame::Inertial>,\n                 GridToDistortedComposition, GridToInertialShapeMap,\n                 GridToInertialSimple, GridToInertialComposition,\n                 DistortedToInertialComposition>;\n\n  /// \\brief The initial time of the functions of time.\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the functions of time\"};\n  };\n\n  using ShapeMapOptions =\n      time_dependent_options::ShapeMapOptions<false, domain::ObjectLabel::None>;\n  using ShapeMapOptionType = typename ShapeMapOptions::type::value_type;\n\n  using RotationMapOptions = time_dependent_options::RotationMapOptions<true>;\n  using RotationMapOptionType = typename RotationMapOptions::type::value_type;\n\n  using ExpansionMapOptions = time_dependent_options::ExpansionMapOptions<true>;\n  using ExpansionMapOptionType = typename ExpansionMapOptions::type::value_type;\n\n  using TranslationMapOptions =\n      time_dependent_options::TranslationMapOptions<3>;\n  using TranslationMapOptionType =\n      typename TranslationMapOptions::type::value_type;\n\n  struct TransitionRotScaleTrans {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Transition rotation, expansion, and translation to zero in the outer \"\n        \"shell\"};\n  };\n\n  using options = tmpl::list<InitialTime, ShapeMapOptions, RotationMapOptions,\n                             ExpansionMapOptions, TranslationMapOptions,\n                             TransitionRotScaleTrans>;\n  static constexpr Options::String help{\n      \"The options for all the hard-coded time dependent maps in the \"\n      \"Sphere domain.\"};\n\n  TimeDependentMapOptions() = default;\n\n  TimeDependentMapOptions(double initial_time,\n                          ShapeMapOptionType shape_map_options,\n                          RotationMapOptionType rotation_map_options,\n                          ExpansionMapOptionType expansion_map_options,\n                          TranslationMapOptionType translation_map_options,\n                          bool transition_rot_scale_trans);\n\n  /*!\n   * \\brief Create the function of time map using the options that were\n   * provided to this class.\n   *\n   * Currently, this will add:\n   *\n   * - Size: `PiecewisePolynomial<3>`\n   * - Shape: `PiecewisePolynomial<2>`\n   * - Rotation: `SettleToConstantQuaternion`\n   * - Expansion: `SettleToConstant`\n   * - ExpansionOuterBoundary: `PiecewisePolynomial<2>`\n   * - Translation: `PiecewisePolynomial<2>`\n   */\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n  create_functions_of_time(const std::unordered_map<std::string, double>&\n                               initial_expiration_times) const;\n\n  /*!\n   * \\brief Construct the actual maps that will be used.\n   *\n   * Currently, this constructs a:\n   *\n   * - Shape: `Shape` (with a size function of time)\n   * - Rotation: `Rotation`\n   * - Expansion: `Expansion`\n   * - Expansion outside the transition region: `ExpansionOuterBoundary`\n   * - Translation: `Translation`\n   */\n  void build_maps(const std::array<double, 3>& center, bool is_filled,\n                  double inner_radius,\n                  const std::vector<double>& radial_partitions,\n                  double outer_radius);\n\n  /*!\n   * \\brief This will construct the map from `Frame::Distorted` to\n   * `Frame::Inertial`.\n   *\n   * For blocks with a shape map, this will be a RotScaleTrans map. For other\n   * blocks, this returns `nullptr`.\n   */\n  MapType<Frame::Distorted, Frame::Inertial> distorted_to_inertial_map(\n      size_t block_number, bool is_inner_cube,\n      size_t num_blocks_per_shell) const;\n\n  /*!\n   * \\brief This will construct the map from `Frame::Grid` to\n   * `Frame::Distorted`.\n   *\n   * For blocks with a shape map, this will return the `Shape` map (with a size\n   * function of time). For other blocks, this returns `nullptr`.\n   */\n  MapType<Frame::Grid, Frame::Distorted> grid_to_distorted_map(\n      size_t block_number, bool is_inner_cube,\n      size_t num_blocks_per_shell) const;\n\n  /*!\n   * \\brief This will construct the map from `Frame::Grid` to `Frame::Inertial`.\n   *\n   * For blocks with a shape map, this will return the `Shape` and\n   * `RotScaleTrans` composition. For other blocks, this returns just the\n   * `RotScaleTrans` map. In the outer shell, the `RotScaleTrans` map will\n   * transition to zero.\n   */\n  MapType<Frame::Grid, Frame::Inertial> grid_to_inertial_map(\n      size_t block_number, bool is_outer_shell, bool is_central_region,\n      size_t num_blocks_per_shell) const;\n\n  /*!\n   * \\brief Whether or not the distorted frame is being used. I.e. whether or\n   * not shape map options were specified.\n   */\n  bool using_distorted_frame() const;\n\n  inline static const std::string size_name{\"Size\"};\n  inline static const std::string shape_name{\"Shape\"};\n  inline static const std::string rotation_name{\"Rotation\"};\n  inline static const std::string expansion_name{\"Expansion\"};\n  inline static const std::string expansion_outer_boundary_name{\n      \"ExpansionOuterBoundary\"};\n  inline static const std::string translation_name{\"Translation\"};\n\n private:\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  bool filled_{false};\n  double deformed_radius_{std::numeric_limits<double>::signaling_NaN()};\n  std::array<ShapeMap, 12> shape_maps_{};\n  RotScaleTransMap inner_rot_scale_trans_map_{};\n  RotScaleTransMap transition_rot_scale_trans_map_{};\n\n  ShapeMapOptionType shape_map_options_;\n  RotationMapOptionType rotation_map_options_;\n  ExpansionMapOptionType expansion_map_options_;\n  TranslationMapOptionType translation_map_options_;\n  bool transition_rot_scale_trans_{false};\n};\n}  // namespace domain::creators::sphere\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/TranslationMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n\n#include <array>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace domain::creators::time_dependent_options {\ntemplate <size_t Dim>\nTranslationMapOptions<Dim>::TranslationMapOptions(\n    const std::array<std::array<double, Dim>, 3>& initial_values_in,\n    const Options::Context& context) {\n  if (initial_values_in.empty() or initial_values_in.size() > 3) {\n    PARSE_ERROR(\n        context,\n        \"Must specify at least the value of the translation, and optionally \"\n        \"up to 2 time derivatives.\");\n  }\n\n  for (size_t i = 0; i < initial_values_in.size(); i++) {\n    gsl::at(initial_values, i) = DataVector{gsl::at(initial_values_in, i)};\n  }\n}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> get_translation(\n    const std::variant<TranslationMapOptions<Dim>, FromVolumeFile>&\n        translation_map_options,\n    const double initial_time, const double expiration_time) {\n  const std::string name = \"Translation\";\n  std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> result{};\n\n  if (std::holds_alternative<FromVolumeFile>(translation_map_options)) {\n    const auto& from_vol_file =\n        std::get<FromVolumeFile>(translation_map_options);\n    auto volume_fot =\n        from_vol_file.retrieve_function_of_time({name}, initial_time);\n\n    // It must be a PiecewisePolynomial\n    if (UNLIKELY(dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n                     volume_fot.at(name).get()) == nullptr)) {\n      ERROR_NO_TRACE(\n          \"Translation function of time read from volume data is not a \"\n          \"PiecewisePolynomial<2>. Cannot use it to initialize the translation \"\n          \"map.\");\n    }\n\n    if (from_vol_file.replay()) {\n      result = std::move(volume_fot.at(name));\n    } else {\n      result =\n          volume_fot.at(name)->create_at_time(initial_time, expiration_time);\n    }\n  } else if (std::holds_alternative<TranslationMapOptions<Dim>>(\n                 translation_map_options)) {\n    const auto& hard_coded_options =\n        std::get<TranslationMapOptions<Dim>>(translation_map_options);\n\n    result = std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n        initial_time, hard_coded_options.initial_values, expiration_time);\n  } else {\n    ERROR(\"Unknown TranslationMap.\");\n  }\n\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template class TranslationMapOptions<DIM(data)>;                             \\\n  template std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>            \\\n  get_translation(const std::variant<TranslationMapOptions<DIM(data)>,         \\\n                                     FromVolumeFile>& translation_map_options, \\\n                  double initial_time, double expiration_time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Creators/TimeDependentOptions/TranslationMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n/*!\n * \\brief Class that holds hard coded translation map options from the input\n * file.\n *\n * \\details This class can also be used as an option tag with the \\p type type\n * alias, `name()` function, and \\p help string.\n */\ntemplate <size_t Dim>\nstruct TranslationMapOptions {\n  using type =\n      Options::Auto<std::variant<TranslationMapOptions<Dim>, FromVolumeFile>,\n                    Options::AutoLabel::None>;\n  static std::string name() { return \"TranslationMap\"; }\n  static constexpr Options::String help = {\n      \"Options for a time-dependent translation of the coordinates. Specify \"\n      \"'None' to not use this map.\"};\n\n  struct InitialValues {\n    using type = std::array<std::array<double, Dim>, 3>;\n    static constexpr Options::String help = {\n        \"Initial values for the translation map. You can optionally specify \"\n        \"its first two time derivatives. If time derivatives aren't specified, \"\n        \"zero will be used.\"};\n  };\n\n  using options = tmpl::list<InitialValues>;\n\n  TranslationMapOptions() = default;\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  TranslationMapOptions(\n      const std::array<std::array<double, Dim>, 3>& initial_values_in,\n      const Options::Context& context = {});\n\n  std::array<DataVector, 3> initial_values{};\n};\n\n/*!\n * \\brief Helper function that takes the variant of the translation map options,\n * and returns the fully constructed translation function of time.\n *\n * \\details The function of time will have a new \\p initial_time and \\p\n * expiration_time, unless it is read from a file and is replaying.\n */\ntemplate <size_t Dim>\nstd::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> get_translation(\n    const std::variant<TranslationMapOptions<Dim>, FromVolumeFile>&\n        translation_map_options,\n    double initial_time, double expiration_time);\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "src/Domain/Domain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Domain.hpp\"\n\n#include <ostream>\n#include <pup.h>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace Frame {\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n\ntemplate <size_t VolumeDim>\nDomain<VolumeDim>::Domain(\n    std::vector<Block<VolumeDim>> blocks,\n    std::unordered_map<std::string, ExcisionSphere<VolumeDim>> excision_spheres,\n    std::unordered_map<std::string, std::unordered_set<std::string>>\n        block_groups)\n    : blocks_(std::move(blocks)),\n      excision_spheres_(std::move(excision_spheres)),\n      block_groups_(std::move(block_groups)) {}\n\ntemplate <size_t VolumeDim>\nDomain<VolumeDim>::Domain(\n    std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, Frame::Inertial, VolumeDim>>>\n        maps,\n    std::unordered_map<std::string, ExcisionSphere<VolumeDim>> excision_spheres,\n    std::vector<std::string> block_names,\n    std::unordered_map<std::string, std::unordered_set<std::string>>\n        block_groups)\n    : excision_spheres_(std::move(excision_spheres)),\n      block_groups_(std::move(block_groups)) {\n  std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>\n      neighbors_of_all_blocks;\n  set_internal_boundaries<VolumeDim>(&neighbors_of_all_blocks, maps);\n  ASSERT(block_names.empty() or block_names.size() == maps.size(),\n         \"Expected \" << maps.size() << \" block names but got \"\n                     << block_names.size() << \".\");\n  for (size_t i = 0; i < maps.size(); i++) {\n    blocks_.emplace_back(std::move(maps[i]), i,\n                         std::move(neighbors_of_all_blocks[i]),\n                         block_names.empty() ? \"\" : block_names[i]);\n  }\n}\n\ntemplate <size_t VolumeDim>\nDomain<VolumeDim>::Domain(\n    std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, Frame::Inertial, VolumeDim>>>\n        maps,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks,\n    const std::vector<PairOfFaces>& identifications,\n    std::unordered_map<std::string, ExcisionSphere<VolumeDim>> excision_spheres,\n    std::vector<std::string> block_names,\n    std::unordered_map<std::string, std::unordered_set<std::string>>\n        block_groups)\n    : excision_spheres_(std::move(excision_spheres)),\n      block_groups_(std::move(block_groups)) {\n  ASSERT(\n      maps.size() == corners_of_all_blocks.size(),\n      \"Must pass same number of maps as block corner sets, but maps.size() == \"\n          << maps.size() << \" and corners_of_all_blocks.size() == \"\n          << corners_of_all_blocks.size());\n  std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>\n      neighbors_of_all_blocks;\n  set_internal_boundaries<VolumeDim>(&neighbors_of_all_blocks,\n                                     corners_of_all_blocks);\n  set_identified_boundaries<VolumeDim>(identifications, corners_of_all_blocks,\n                                       &neighbors_of_all_blocks);\n  ASSERT(block_names.empty() or block_names.size() == maps.size(),\n         \"Expected \" << maps.size() << \" block names but got \"\n                     << block_names.size() << \".\");\n  for (size_t i = 0; i < corners_of_all_blocks.size(); i++) {\n    blocks_.emplace_back(std::move(maps[i]), i,\n                         std::move(neighbors_of_all_blocks[i]),\n                         block_names.empty() ? \"\" : block_names[i]);\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid Domain<VolumeDim>::inject_time_dependent_map_for_block(\n    const size_t block_id,\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n        moving_mesh_grid_to_inertial_map,\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, VolumeDim>>\n        moving_mesh_grid_to_distorted_map,\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Distorted, Frame::Inertial, VolumeDim>>\n        moving_mesh_distorted_to_inertial_map) {\n  ASSERT(block_id < blocks_.size(),\n         \"The block id \" << block_id\n                         << \" larger than the total number of blocks, \"\n                         << blocks_.size());\n  blocks_[block_id].inject_time_dependent_map(\n      std::move(moving_mesh_grid_to_inertial_map),\n      std::move(moving_mesh_grid_to_distorted_map),\n      std::move(moving_mesh_distorted_to_inertial_map));\n}\n\ntemplate <size_t VolumeDim>\nvoid Domain<VolumeDim>::inject_time_dependent_map_for_excision_sphere(\n    const std::string& excision_sphere_name,\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n        moving_mesh_grid_to_inertial_map) {\n  ASSERT(excision_spheres_.count(excision_sphere_name) == 1,\n         \"Cannot inject time dependent maps into excision sphere '\"\n             << excision_sphere_name << \"' because it does not exist.\");\n\n  excision_spheres_.at(excision_sphere_name)\n      .inject_time_dependent_maps(std::move(moving_mesh_grid_to_inertial_map));\n}\n\ntemplate <size_t VolumeDim>\nbool Domain<VolumeDim>::is_time_dependent() const {\n  return alg::any_of(blocks_, [](const Block<VolumeDim>& block) {\n    return block.is_time_dependent();\n  });\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const Domain<VolumeDim>& lhs, const Domain<VolumeDim>& rhs) {\n  return lhs.blocks() == rhs.blocks() and\n         lhs.excision_spheres() == rhs.excision_spheres() and\n         lhs.block_groups() == rhs.block_groups();\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Domain<VolumeDim>& lhs, const Domain<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const Domain<VolumeDim>& d) {\n  const auto& blocks = d.blocks();\n  os << \"Domain with \" << blocks.size() << \" blocks:\\n\";\n  for (const auto& block : blocks) {\n    os << block << '\\n';\n  }\n  os << \"Excision spheres:\\n\" << d.excision_spheres() << '\\n';\n  return os;\n}\n\ntemplate <size_t VolumeDim>\nstd::vector<std::string> Domain<VolumeDim>::block_names() const {\n  std::vector<std::string> result{blocks_.size()};\n  alg::transform(blocks_, result.begin(),\n                 [](const Block<VolumeDim>& block) -> const std::string& {\n                   return block.name();\n                 });\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nvoid Domain<VolumeDim>::pup(PUP::er& p) {\n  size_t version = 1;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | blocks_;\n    p | excision_spheres_;\n  }\n  if (version >= 1) {\n    p | block_groups_;\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define INSTANTIATE(_, data)                              \\\n  template class Domain<DIM(data)>;                       \\\n  template bool operator==(const Domain<DIM(data)>& lhs,  \\\n                           const Domain<DIM(data)>& rhs); \\\n  template bool operator!=(const Domain<DIM(data)>& lhs,  \\\n                           const Domain<DIM(data)>& rhs); \\\n  template std::ostream& operator<<(std::ostream& os,     \\\n                                    const Domain<DIM(data)>& d);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n\n// Some compilers (clang 3.9.1) don't instantiate the default argument\n// to the second Domain constructor.\ntemplate class std::vector<PairOfFaces>;\n"
  },
  {
    "path": "src/Domain/Domain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class template Domain.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iosfwd>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace Frame {\nstruct BlockLogical;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\cond\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim>\nclass CoordinateMapBase;\n}  // namespace domain\n/// \\endcond\n\n/*!\n * \\brief Holds entities related to the computational domain.\n */\nnamespace domain {}\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief A wrapper around a vector of Blocks that represent the computational\n * domain.\n *\n * ### Serialization and versioning\n *\n * The domain will be serialized and written to output files so it can be used\n * for interpolations, as are `domain::FunctionOfTime` classes. To be able to\n * read in domains written by older versions of the code the `pup` function in\n * this class and all `pup` functions it invokes support lightweight versioning.\n * A `version` integer is written alongside the data when serializing, and read\n * back in when deserializing. Increment the version number every time you make\n * changes to the `pup` function. Retain support for unpacking data written by\n * previous versions whenever possible. When adding a new field to serialize,\n * you can simply pack/unpack the new field only for newer versions like this:\n *\n * ```cpp\n * void pup(PUP::er& p) {\n *   size_t version = 1;  // Incremented from 0 to 1\n *   p | version;\n *   if (version >= 0) {\n *     p | some_data_;\n *   }\n *   if (version >= 1) {\n *     p | added_data_;\n *   } else if (p.isUnpacking()) {\n *     // You may have to initialize added_data_ here if default-construction\n *     // isn't sufficient.\n *   }\n * }\n * ```\n *\n * When removing or changing a field, make sure to deserialize old data\n * consistent with how it was written:\n *\n * ```cpp\n * void pup(PUP::er& p) {\n *   size_t version = 2;  // Incremented from 1 to 2\n *   p | version;\n *   if (version < 2) {\n *     // The field some_data_ was changed in version 2\n *     OldDataType old_some_data_;\n *     p | old_some_data_;\n *     // Possibly use the old deserialized data to initialize the changed field\n *     some_data_ = old_some_data_ * 2.;\n *   } else {\n *     p | some_data_;\n *   }\n *   // ...\n * }\n * ```\n *\n * Make sure that all data types you serialize in the `pup` function also\n * support versioning. Also make sure to keep all factory-creatable classes\n * registered that were written by old versions of the code.\n */\ntemplate <size_t VolumeDim>\nclass Domain {\n public:\n  /// \\brief Create a Domain from its blocks\n  ///\n  /// \\param blocks each Block of the domain.\n  /// \\param excision_spheres Any ExcisionSphere%s in the domain.\n  /// \\param block_groups Labels to refer to groups of blocks. The groups can\n  ///        overlap, and they don't have to cover all blocks in the domain.\n  ///        The groups can be used to refer to multiple blocks at once.\n  explicit Domain(\n      std::vector<Block<VolumeDim>> blocks,\n      std::unordered_map<std::string, ExcisionSphere<VolumeDim>>\n          excision_spheres = {},\n      std::unordered_map<std::string, std::unordered_set<std::string>>\n          block_groups = {});\n\n  /*!\n   * \\brief Create a Domain using CoordinateMaps to encode the Orientations.\n   * This constructor does not support periodic boundary conditions.\n   *\n   * \\details A constructor that does not require the user to provide a corner\n   * numbering scheme. Constructs a global corner numbering for each pair\n   * of abutting Blocks from their maps alone. The numbering is used to\n   * set up the corresponding Orientation, and then is discarded; the\n   * next pair of blocks uses a new global corner numbering, and so on,\n   * until all pairs of abutting Blocks have had their Orientations\n   * determined. For more information on setting up domains, see the\n   * [domain creation tutorial](\\ref tutorial_domain_creation).\n   *\n   * \\param maps The BlockLogical -> Inertial coordinate map for each block.\n   * \\param excision_spheres Any ExcisionSphere%s in the domain.\n   * \\param block_names A human-readable name for every block, or empty if no\n   * block names have been chosen (yet).\n   * \\param block_groups Labels to refer to groups of blocks. The groups can\n   * overlap, and they don't have to cover all blocks in the domain. The groups\n   * can be used to refer to multiple blocks at once.\n   */\n  explicit Domain(\n      std::vector<std::unique_ptr<domain::CoordinateMapBase<\n          Frame::BlockLogical, Frame::Inertial, VolumeDim>>>\n          maps,\n      std::unordered_map<std::string, ExcisionSphere<VolumeDim>>\n          excision_spheres = {},\n      std::vector<std::string> block_names = {},\n      std::unordered_map<std::string, std::unordered_set<std::string>>\n          block_groups = {});\n\n  /*!\n   * \\brief Create a Domain using a corner numbering scheme to encode the\n   * Orientations\n   *\n   * \\see [domain creation tutorial](@ref tutorial_domain_creation)\n   *\n   * \\param maps The BlockLogical -> Inertial coordinate map for each block.\n   * \\param corners_of_all_blocks The corner numbering for each block's corners\n   * according to the global corner number scheme. The details of the corner\n   * numbering scheme are described in the\n   * [tutorial](@ref tutorial_orientations).\n   * \\param identifications Used to impose periodic boundary conditions on the\n   * domain. To identify faces, `identifications` should contain the PairOfFaces\n   * containing the corners of each pair of faces that you wish to identify with\n   * one another. The number of `identifications` must be even.\n   * \\param excision_spheres Any ExcisionSphere%s in the domain.\n   * \\param block_names A human-readable name for every block, or empty if no\n   * block names have been chosen (yet).\n   * \\param block_groups Labels to refer to groups of blocks. The groups can\n   * overlap, and they don't have to cover all blocks in the domain. The groups\n   * can be used to refer to multiple blocks at once.\n   */\n  Domain(std::vector<std::unique_ptr<domain::CoordinateMapBase<\n             Frame::BlockLogical, Frame::Inertial, VolumeDim>>>\n             maps,\n         const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n             corners_of_all_blocks,\n         const std::vector<PairOfFaces>& identifications = {},\n         std::unordered_map<std::string, ExcisionSphere<VolumeDim>>\n             excision_spheres = {},\n         std::vector<std::string> block_names = {},\n         std::unordered_map<std::string, std::unordered_set<std::string>>\n             block_groups = {});\n\n  Domain() = default;\n  ~Domain() = default;\n  Domain(const Domain&) = delete;\n  Domain(Domain&&) = default;\n  Domain<VolumeDim>& operator=(const Domain<VolumeDim>&) = delete;\n  Domain<VolumeDim>& operator=(Domain<VolumeDim>&&) = default;\n\n  void inject_time_dependent_map_for_block(\n      size_t block_id,\n      std::unique_ptr<\n          domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n          moving_mesh_grid_to_inertial_map,\n      std::unique_ptr<\n          domain::CoordinateMapBase<Frame::Grid, Frame::Distorted, VolumeDim>>\n          moving_mesh_grid_to_distorted_map = nullptr,\n      std::unique_ptr<domain::CoordinateMapBase<Frame::Distorted,\n                                                Frame::Inertial, VolumeDim>>\n          moving_mesh_distorted_to_inertial_map = nullptr);\n\n  void inject_time_dependent_map_for_excision_sphere(\n      const std::string& excision_sphere_name,\n      std::unique_ptr<\n          domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n          moving_mesh_grid_to_inertial_map);\n\n  const std::vector<Block<VolumeDim>>& blocks() const { return blocks_; }\n\n  bool is_time_dependent() const;\n\n  const std::unordered_map<std::string, ExcisionSphere<VolumeDim>>&\n  excision_spheres() const {\n    return excision_spheres_;\n  }\n\n  /// Labels to refer to groups of blocks. The groups can overlap, and they\n  /// don't have to cover all blocks in the domain. The groups can be used to\n  /// refer to multiple blocks at once.\n  const std::unordered_map<std::string, std::unordered_set<std::string>>&\n  block_groups() const {\n    return block_groups_;\n  }\n\n  /// The block names in the current domain.\n  std::vector<std::string> block_names() const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  std::vector<Block<VolumeDim>> blocks_{};\n  std::unordered_map<std::string, ExcisionSphere<VolumeDim>>\n      excision_spheres_{};\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups_{};\n};\n\ntemplate <size_t VolumeDim>\nbool operator==(const Domain<VolumeDim>& lhs, const Domain<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Domain<VolumeDim>& lhs, const Domain<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const Domain<VolumeDim>& d);\n"
  },
  {
    "path": "src/Domain/DomainHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/DomainHelpers.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <limits>\n#include <ostream>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/MapInstantiationMacros.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\n\n// returns the common corners in the global corner numbering scheme.\ntemplate <size_t VolumeDim>\nstd::vector<size_t> get_common_global_corners(\n    const std::array<size_t, two_to_the(VolumeDim)>& block_1_global_corners,\n    const std::array<size_t, two_to_the(VolumeDim)>& block_2_global_corners) {\n  std::vector<size_t> result;\n\n  for (size_t id_i : block_1_global_corners) {\n    if (alg::find(block_2_global_corners, id_i) !=\n        block_2_global_corners.end()) {\n      result.push_back(id_i);\n    }\n  }\n\n  return result;\n}\n\n// Given Directions in block1 and their corresponding Directions\n// in block2, this function first repackages them into arrays and\n// then makes a std::pair of the arrays.\ntemplate <size_t VolumeDim>\nstd::pair<std::array<Direction<VolumeDim>, VolumeDim>,\n          std::array<Direction<VolumeDim>, VolumeDim>>\ncreate_correspondence_between_blocks(\n    const Direction<VolumeDim>& direction_to_neighbor_in_self_frame,\n    const Direction<VolumeDim>& direction_to_self_in_neighbor_frame,\n    const std::vector<Direction<VolumeDim>>&\n        alignment_of_shared_face_in_self_frame,\n    const std::vector<Direction<VolumeDim>>&\n        alignment_of_shared_face_in_neighbor_frame) {\n  std::array<Direction<VolumeDim>, VolumeDim> result1;\n  result1[0] = direction_to_neighbor_in_self_frame;\n\n  for (size_t i = 1; i < VolumeDim; i++) {\n    gsl::at(result1, i) = alignment_of_shared_face_in_self_frame[i - 1];\n  }\n\n  std::array<Direction<VolumeDim>, VolumeDim> result2;\n  result2[0] = direction_to_self_in_neighbor_frame.opposite();\n\n  for (size_t i = 1; i < VolumeDim; i++) {\n    gsl::at(result2, i) = alignment_of_shared_face_in_neighbor_frame[i - 1];\n  }\n  return std::make_pair(result1, result2);\n}\n\n// requires that the face is an external boundary of the domain.\n// returns the id of the block with that face.\ntemplate <size_t VolumeDim>\nsize_t find_block_id_of_external_face(\n    const std::vector<size_t>& global_corners_external_face,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks) {\n  size_t number_of_blocks_found = 0;\n  size_t id_of_found_block = std::numeric_limits<size_t>::max();\n  for (size_t j = 0; j < corners_of_all_blocks.size(); j++) {\n    const auto& corners_set = corners_of_all_blocks[j];\n    auto is_in_corners_set = [&corners_set](const auto element) {\n      return alg::find(corners_set, element) != corners_set.end();\n    };\n    if (alg::all_of(global_corners_external_face, is_in_corners_set)) {\n      number_of_blocks_found++;\n      id_of_found_block = j;\n    }\n  }\n  ASSERT(number_of_blocks_found > 0, \"This face does not belong to a block.\");\n  ASSERT(number_of_blocks_found == 1, \"This face is not an external boundary.\");\n  return id_of_found_block;\n}\n\ntemplate <size_t VolumeDim>\nstd::array<size_t, two_to_the(VolumeDim - 1)> get_common_local_corners(\n    const std::array<size_t, two_to_the(VolumeDim)>& block_global_corners,\n    const std::vector<size_t>& block_global_common_corners) {\n  std::array<size_t, two_to_the(VolumeDim - 1)> result{{0}};\n  size_t i = 0;\n  for (const auto global_id : block_global_common_corners) {\n    ASSERT(alg::find(block_global_corners, global_id) !=\n               block_global_corners.end(),\n           \"Elements of block_global_common_corners must be in \"\n           \"block_global_corners.\");\n    gsl::at(result, i) =\n        static_cast<size_t>(alg::find(block_global_corners, global_id) -\n                            block_global_corners.begin());\n    i++;\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nstd::array<tnsr::I<double, VolumeDim, Frame::BlockLogical>,\n           two_to_the(VolumeDim - 1)>\nlogical_coords_of_corners(\n    const std::array<size_t, two_to_the(VolumeDim - 1)>& local_ids) {\n  std::array<tnsr::I<double, VolumeDim, Frame::BlockLogical>,\n             two_to_the(VolumeDim - 1)>\n      result{};\n  std::transform(local_ids.begin(), local_ids.end(), result.begin(),\n                 [](const size_t id) {\n                   tnsr::I<double, VolumeDim, Frame::BlockLogical> point{};\n                   for (size_t i = 0; i < VolumeDim; i++) {\n                     point[i] = 2.0 * get_nth_bit(id, i) - 1.0;\n                   };\n                   return point;\n                 });\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nDirection<VolumeDim> get_direction_normal_to_face(\n    const std::array<tnsr::I<double, VolumeDim, Frame::BlockLogical>,\n                     two_to_the(VolumeDim - 1)>& face_pts) {\n  const auto summed_point = alg::accumulate(\n      face_pts, tnsr::I<double, VolumeDim, Frame::BlockLogical>(0.0),\n      [](auto&& prior,\n         const tnsr::I<double, VolumeDim, Frame::BlockLogical>& point) {\n        for (size_t i = 0; i < prior.size(); i++) {\n          prior[i] += point[i];\n        }\n        return prior;\n      });\n  ASSERT(VolumeDim - 1 == static_cast<size_t>(std::count(\n                              summed_point.begin(), summed_point.end(), 0.0)),\n         \"The face_pts passed in do not correspond to a face.\");\n  const auto index = static_cast<size_t>(\n      alg::find_if(summed_point, [](const double x) { return x != 0; }) -\n      summed_point.begin());\n  return Direction<VolumeDim>(\n      index, summed_point[index] > 0 ? Side::Upper : Side::Lower);\n}\n\ntemplate <size_t VolumeDim>\nstd::vector<Direction<VolumeDim>> get_directions_along_face(\n    const std::array<size_t, two_to_the(VolumeDim - 1)>& face_local_corners) {\n  std::vector<Direction<VolumeDim>> result;\n\n  // Directions encoded as powers of 2. (xi:1, eta:2, zeta:4)\n  std::vector<int> dir_pow2;\n  for (size_t i = 0; i < VolumeDim - 1; i++) {\n    int direction_pow2 =\n        static_cast<int>(gsl::at(face_local_corners, two_to_the(i))) -\n        static_cast<int>(gsl::at(face_local_corners, 0));\n    ASSERT(\n        direction_pow2 ==\n            static_cast<int>(\n                gsl::at(face_local_corners, face_local_corners.size() - 1)) -\n                static_cast<int>(\n                    gsl::at(face_local_corners,\n                            face_local_corners.size() - 1 - two_to_the(i))),\n        \"The corners provided do not correspond to a possible face.\"\n        \"Help: As the direction of a face can be determined from any pair of \"\n        \"edges, all pairs of edges must agree on their directionality. \"\n        \"Providing '0,1,3,2' as a pair of edges (in the canonical corner \"\n        \"numbering scheme) will trigger this ASSERT as 1-0 != 2-3; they \"\n        \"point in opposite directions. Additionally, 3-0 != 2-1. The proper \"\n        \"numbering for this face is '0,1,2,3', or any other numbering that \"\n        \"results from a rigid manipulation of this face, for example \"\n        \"'1,3,0,2'.\");\n\n    ASSERT(abs(direction_pow2) == 1 or abs(direction_pow2) == 2 or\n               abs(direction_pow2) == 4,\n           \"The corners making up an edge always have a difference of either \"\n           \"1, 2, or 4, under the local numbering scheme, depending on whether \"\n           \"the edge is parallel to the xi, eta, or zeta axis, respectively. \"\n           \"This ASSERT is triggered when the two corners passed do not make \"\n           \"up an edge of the n-cube.\");\n    const size_t dimension = std::round(log2(abs(direction_pow2)));\n    const Side side = direction_pow2 > 0 ? Side::Upper : Side::Lower;\n    result.push_back(Direction<VolumeDim>(dimension, side));\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nDirection<VolumeDim> mapped(\n    size_t block_id1, size_t block_id2,\n    const Direction<VolumeDim>& dir_in_block1,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks) {\n  const auto correspondence = obtain_correspondence_between_blocks(\n      block_id1, block_id2, corners_of_all_blocks);\n  for (size_t i = 0; i < VolumeDim; i++) {\n    if (dir_in_block1.dimension() == (correspondence.first)[i].dimension()) {\n      return dir_in_block1.side() == (correspondence.first)[i].side()\n                 ? (correspondence.second)[i]\n                 : (correspondence.second)[i].opposite();\n    }\n  }\n  ERROR(\"Improper Direction was passed to function.\");\n}\n\ntemplate <size_t VolumeDim>\nstd::pair<std::array<Direction<VolumeDim>, VolumeDim>,\n          std::array<Direction<VolumeDim>, VolumeDim>>\nobtain_correspondence_between_blocks(\n    size_t block_id1, size_t block_id2,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks) {\n  const auto& self = corners_of_all_blocks[block_id1];\n  const auto& nhbr = corners_of_all_blocks[block_id2];\n  const auto common = get_common_global_corners<VolumeDim>(self, nhbr);\n  const auto self_common = get_common_local_corners<VolumeDim>(self, common);\n  const auto nhbr_common = get_common_local_corners<VolumeDim>(nhbr, common);\n  const auto self_dir = get_direction_normal_to_face<VolumeDim>(\n      logical_coords_of_corners<VolumeDim>(self_common));\n  const auto nhbr_dir = get_direction_normal_to_face<VolumeDim>(\n      logical_coords_of_corners<VolumeDim>(nhbr_common));\n  const auto self_align = get_directions_along_face<VolumeDim>(self_common);\n  const auto nhbr_align = get_directions_along_face<VolumeDim>(nhbr_common);\n\n  return create_correspondence_between_blocks(self_dir, nhbr_dir, self_align,\n                                              nhbr_align);\n}\n\ntemplate <size_t VolumeDim>\nstd::vector<std::array<size_t, two_to_the(VolumeDim)>> corners_from_two_maps(\n    const std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, Frame::Inertial, VolumeDim>>& map1,\n    const std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, Frame::Inertial, VolumeDim>>& map2) {\n  std::array<size_t, two_to_the(VolumeDim)> corners_for_block1{};\n  std::array<size_t, two_to_the(VolumeDim)> corners_for_block2{};\n  for (VolumeCornerIterator<VolumeDim> vci{}; vci; ++vci) {\n    gsl::at(corners_for_block1, vci.local_corner_number()) =\n        vci.local_corner_number();\n  }\n  // Fill corners_for_block2 based off which corners are shared with map1.\n  for (VolumeCornerIterator<VolumeDim> vci_map2{}; vci_map2; ++vci_map2) {\n    const auto mapped_coords2 =\n        (*map2)(tnsr::I<double, VolumeDim, Frame::BlockLogical>{\n            vci_map2.coords_of_corner()});\n\n    // Set num_unshared_corners to zero because we only need a local corner\n    // numbering scheme. This is because our algorithm treats each pair of\n    // blocks independently to create the orientation (if any) between them.\n    size_t num_unshared_corners = 0;\n    // Check each corner of map1 to see if it shares any corners with map2.\n    for (VolumeCornerIterator<VolumeDim> vci_map1{}; vci_map1; ++vci_map1) {\n      const auto mapped_coords1 =\n          (*map1)(tnsr::I<double, VolumeDim, Frame::BlockLogical>{\n              vci_map1.coords_of_corner()});\n      double max_separation = 0.0;\n      for (size_t j = 0; j < VolumeDim; j++) {\n        max_separation =\n            std::max(max_separation,\n                     std::abs(mapped_coords2.get(j) - mapped_coords1.get(j)));\n      }\n      // If true, then the mapped_coords lie on top of one another.\n      // This corner of map2 is assigned the same number as that of map1.\n      if (max_separation < 1.0e-6) {\n        // Note: Ideally the threshold would depend on and be proportional\n        // to the map size, but for simplicity we assume that maps have sizes\n        // that are of order unity. If you are using maps that have sizes\n        // that are much smaller, this threshold (1e-6) will create situations\n        // where adjacent corners are incorrectly determined. If you are using\n        // very small maps please watch out for this.\n        gsl::at(corners_for_block2, vci_map2.local_corner_number()) =\n            vci_map1.local_corner_number();\n        break;\n      } else {\n        // Otherwise, a new number is assigned to this corner.\n        gsl::at(corners_for_block2, vci_map2.local_corner_number()) =\n            two_to_the(VolumeDim) + num_unshared_corners;\n        num_unshared_corners++;\n      }\n    }\n  }\n  return {corners_for_block1, corners_for_block2};\n}\n\ntemplate <size_t VolumeDim>\nstd::pair<std::array<Direction<VolumeDim>, VolumeDim>,\n          std::array<Direction<VolumeDim>, VolumeDim>>\nobtain_correspondence_between_maps(\n    const size_t map_id1, const size_t map_id2,\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, Frame::Inertial, VolumeDim>>>& maps) {\n  return obtain_correspondence_between_blocks<VolumeDim>(\n      0, 1, corners_from_two_maps(maps[map_id1], maps[map_id2]));\n}\n\n// Sets periodic boundary conditions for rectilinear multi-cube domains.\n//\n// For each block with an external boundary in the +i^th direction, traverses\n// the block neighbors in the -i^th direction until finding a neighbor block\n// with an external boundary in the -i^th direction, and identifies these two\n// external boundaries with each other, if `dimension_is_periodic[i]` is\n// `true`. This is useful when setting the boundaries of a rectilinear domain.\n//\n// The argument passed to `orientations_of_all_blocks` is the relative\n// orientation of the edifice relative to each block. This is used when\n// constructing non-trivial domains for testing such as RotatedBricks.\ntemplate <size_t VolumeDim>\nvoid set_cartesian_periodic_boundaries(\n    const std::array<bool, VolumeDim>& dimension_is_periodic,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks,\n    const std::vector<OrientationMap<VolumeDim>>& orientations_of_all_blocks,\n    const gsl::not_null<\n        std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>*>\n        neighbors_of_all_blocks) {\n  if (orientations_of_all_blocks.size() != corners_of_all_blocks.size()) {\n    ERROR(\"Each block must have an OrientationMap relative to an edifice.\");\n  }\n  size_t block_id = 0;\n  for (auto& block : *neighbors_of_all_blocks) {\n    const auto& corners_of_block = corners_of_all_blocks[block_id];\n    const auto& orientation_of_block = orientations_of_all_blocks[block_id];\n    for (const auto& direction : Direction<VolumeDim>::all_directions()) {\n      // Loop over Directions to search which Directions were not set to\n      // neighbors, these Directions are external_boundaries, so we proceed\n      // in the opposite direction to find the block we wish to identify\n      // boundaries with.\n      if (not gsl::at(dimension_is_periodic, direction.dimension())) {\n        continue;  // Don't identify neighbors in dimensions not specified as\n                   // periodic.\n      }\n      if (block.find(orientation_of_block(direction)) == block.end()) {\n        // follow to neighbor in the opposite direction:\n        const auto local_direction_in_block = orientation_of_block(direction);\n        auto opposite_block_id = block_id;\n        auto opposite_block = block;\n        auto corners_of_opposite_block = corners_of_block;\n        // The local direction that points to the opposite block:\n        auto opposite_block_direction = local_direction_in_block.opposite();\n        for (size_t neighbor_counter = 0;\n             neighbor_counter < corners_of_all_blocks.size();\n             neighbor_counter++) {\n          if (opposite_block.find(opposite_block_direction) ==\n              opposite_block.end()) {\n            continue;\n          } else {\n            ASSERT(opposite_block[opposite_block_direction].size() == 1,\n                   \"Multiple block neighbors not supported by this function.\");\n            opposite_block_id =\n                *(opposite_block[opposite_block_direction].begin());\n            opposite_block = (*neighbors_of_all_blocks)[opposite_block_id];\n            opposite_block_direction =\n                orientations_of_all_blocks[opposite_block_id](direction)\n                    .opposite();\n            corners_of_opposite_block =\n                corners_of_all_blocks[opposite_block_id];\n          }\n          if (neighbor_counter == corners_of_all_blocks.size() - 1) {\n            // There is no domain that can be constructed using\n            // `rectilinear_domain`, that triggers this ERROR, so the ERROR is\n            // expected to be unreachable. We keep this check in the case that\n            // this assumption is wrong.\n            // LCOV_EXCL_START\n            ERROR(\n                \"A closed cycle of neighbors exists in this domain. Has \"\n                \"`orientations_of_all_blocks` been set correctly?\");\n            // LCOV_EXCL_STOP\n          }\n        }\n        //`opposite_block` is now the block on the opposite cartesian side\n        // of `block`, so we identify the faces of those blocks that were\n        // external boundaries as now being periodic. Note that `opposite_block`\n        // and `block`  may be the same block, if the domain only has an extent\n        // of one in that dimension.\n        std::vector<size_t> face_corners_of_block{};\n        std::vector<size_t> face_corners_of_opposite_block{};\n        for (FaceCornerIterator<VolumeDim> fci(local_direction_in_block); fci;\n             ++fci) {\n          face_corners_of_block.push_back(\n              gsl::at(corners_of_block, fci.volume_index()));\n        }\n        for (FaceCornerIterator<VolumeDim> fci(\n                 orientations_of_all_blocks[opposite_block_id](direction)\n                     .opposite());\n             fci; ++fci) {\n          face_corners_of_opposite_block.push_back(\n              gsl::at(corners_of_opposite_block, fci.volume_index()));\n        }\n        // The face corners in both blocks should be in ascending order\n        // for periodic boundary conditions. Note that this shortcut only\n        // works in the special case where the global corners are\n        // increasing in each cartesian direction of the domain.\n        std::sort(face_corners_of_block.begin(), face_corners_of_block.end());\n        std::sort(face_corners_of_opposite_block.begin(),\n                  face_corners_of_opposite_block.end());\n        // This modifies the BlockNeighbors of both the current block\n        // and the block it found to be its neighbor.\n        set_identified_boundaries(\n            std::vector<PairOfFaces>{PairOfFaces{\n                face_corners_of_block, face_corners_of_opposite_block}},\n            corners_of_all_blocks, neighbors_of_all_blocks);\n      }\n    }\n    block_id++;\n  }\n}\n}  // namespace\n\ntemplate <size_t VolumeDim>\nvoid set_internal_boundaries(\n    const gsl::not_null<\n        std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>*>\n        neighbors_of_all_blocks,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks) {\n  for (size_t block1_index = 0; block1_index < corners_of_all_blocks.size();\n       block1_index++) {\n    DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>> neighbor;\n    for (size_t block2_index = 0; block2_index < corners_of_all_blocks.size();\n         block2_index++) {\n      if (block1_index != block2_index and\n          get_common_global_corners<VolumeDim>(\n              corners_of_all_blocks[block1_index],\n              corners_of_all_blocks[block2_index])\n                  .size() == two_to_the(VolumeDim - 1)) {\n        const auto orientation_helper =\n            obtain_correspondence_between_blocks<VolumeDim>(\n                block1_index, block2_index, corners_of_all_blocks);\n        neighbor.emplace(std::move((orientation_helper.first)[0]),\n                         BlockNeighbors<VolumeDim>(\n                             block2_index, OrientationMap<VolumeDim>(\n                                               orientation_helper.first,\n                                               orientation_helper.second)));\n      }\n    }\n    neighbors_of_all_blocks->push_back(neighbor);\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid set_internal_boundaries(\n    const gsl::not_null<\n        std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>*>\n        neighbors_of_all_blocks,\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, Frame::Inertial, VolumeDim>>>& maps) {\n  for (size_t block1_index = 0; block1_index < maps.size(); block1_index++) {\n    DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>> neighbor;\n    for (size_t block2_index = 0; block2_index < maps.size(); block2_index++) {\n      const auto corners_for_blocks1_and_2 =\n          corners_from_two_maps(maps[block1_index], maps[block2_index]);\n      if (block1_index != block2_index and\n          get_common_global_corners<VolumeDim>(corners_for_blocks1_and_2[0],\n                                               corners_for_blocks1_and_2[1])\n                  .size() == two_to_the(VolumeDim - 1)) {\n        const auto orientation_helper =\n            obtain_correspondence_between_blocks<VolumeDim>(\n                0, 1, corners_for_blocks1_and_2);\n\n        neighbor.emplace(std::move((orientation_helper.first)[0]),\n                         BlockNeighbors<VolumeDim>(\n                             block2_index, OrientationMap<VolumeDim>(\n                                               orientation_helper.first,\n                                               orientation_helper.second)));\n      }\n    }\n    neighbors_of_all_blocks->push_back(neighbor);\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid set_identified_boundaries(\n    const std::vector<PairOfFaces>& identifications,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks,\n    const gsl::not_null<\n        std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>*>\n        neighbors_of_all_blocks) {\n  for (const auto& pair : identifications) {\n    const auto& face1 = pair.first;\n    const auto& face2 = pair.second;\n    ASSERT(face1.size() == face2.size(),\n           \"Each set must have the same number of corners.\");\n    size_t id1 = ::find_block_id_of_external_face<VolumeDim>(\n        face1, corners_of_all_blocks);\n    size_t id2 = ::find_block_id_of_external_face<VolumeDim>(\n        face2, corners_of_all_blocks);\n    const auto face1_canon =\n        get_common_local_corners<VolumeDim>(corners_of_all_blocks[id1], face1);\n    const auto face2_canon =\n        get_common_local_corners<VolumeDim>(corners_of_all_blocks[id2], face2);\n    const auto face1_normal_dir = get_direction_normal_to_face(\n        logical_coords_of_corners<VolumeDim>(face1_canon));\n    const auto face2_normal_dir = get_direction_normal_to_face(\n        logical_coords_of_corners<VolumeDim>(face2_canon));\n    const auto face1_align_dirs =\n        get_directions_along_face<VolumeDim>(face1_canon);\n    const auto face2_align_dirs =\n        get_directions_along_face<VolumeDim>(face2_canon);\n    const auto obtain_correspondence_between_blocks1 =\n        create_correspondence_between_blocks<VolumeDim>(\n            face1_normal_dir, face2_normal_dir, face1_align_dirs,\n            face2_align_dirs);\n    const auto obtain_correspondence_between_blocks2 =\n        create_correspondence_between_blocks<VolumeDim>(\n            face2_normal_dir, face1_normal_dir, face2_align_dirs,\n            face1_align_dirs);\n    const OrientationMap<VolumeDim> connect1(\n        obtain_correspondence_between_blocks1.first,\n        obtain_correspondence_between_blocks1.second);\n    const OrientationMap<VolumeDim> connect2(\n        obtain_correspondence_between_blocks2.first,\n        obtain_correspondence_between_blocks2.second);\n    (*neighbors_of_all_blocks)[id1].emplace(\n        face1_normal_dir, BlockNeighbors<VolumeDim>(id2, connect1));\n    (*neighbors_of_all_blocks)[id2].emplace(\n        face2_normal_dir, BlockNeighbors<VolumeDim>(id1, connect2));\n  }\n}\n\nstd::array<OrientationMap<3>, 6> orientations_for_sphere_wrappings() {\n  return {{\n      // Upper Z\n      OrientationMap<3>::create_aligned(),\n      // Lower Z\n      OrientationMap<3>{std::array<Direction<3>, 3>{\n          {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n           Direction<3>::lower_zeta()}}},\n      // Upper Y\n      OrientationMap<3>(std::array<Direction<3>, 3>{\n          {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n           Direction<3>::lower_eta()}}),\n      // Lower Y\n      OrientationMap<3>(std::array<Direction<3>, 3>{\n          {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n           Direction<3>::upper_eta()}}),\n      // Upper X\n      OrientationMap<3>(std::array<Direction<3>, 3>{\n          {Direction<3>::upper_zeta(), Direction<3>::upper_xi(),\n           Direction<3>::upper_eta()}}),\n      // Lower X\n      OrientationMap<3>(std::array<Direction<3>, 3>{\n          {Direction<3>::lower_zeta(), Direction<3>::lower_xi(),\n           Direction<3>::upper_eta()}}),\n  }};\n}\n\nsize_t which_wedge_index(const ShellWedges& which_wedges) {\n  switch (which_wedges) {\n    case ShellWedges::All:\n      return 0;\n    case ShellWedges::FourOnEquator:\n      return 2;\n    case ShellWedges::OneAlongMinusX:\n      return 5;\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Unknown ShellWedges type\");\n      // LCOV_EXCL_STOP\n  }\n}\n\nstd::vector<domain::CoordinateMaps::Wedge<3>> sph_wedge_coordinate_maps(\n    const double inner_radius, const double outer_radius,\n    const double inner_sphericity, const double outer_sphericity,\n    const bool use_equiangular_map,\n    const std::optional<std::pair<double, std::array<double, 3>>>&\n        offset_options,\n    const bool use_half_wedges, const std::vector<double>& radial_partitioning,\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution,\n    const ShellWedges which_wedges, const double opening_angle) {\n  if (use_half_wedges and which_wedges != ShellWedges::All) {\n    ERROR(\n        \"If we are using half wedges we must also be using ShellWedges::All.\");\n  }\n  if (radial_distribution.size() != 1 + radial_partitioning.size()) {\n    ERROR(\n        \"Specify a radial distribution for every spherical shell. You \"\n        \"specified \"\n        << radial_distribution.size() << \" items, but the domain has \"\n        << 1 + radial_partitioning.size() << \" shells.\");\n  }\n\n  const auto wedge_orientations = orientations_for_sphere_wrappings();\n\n  using Wedge3DMap = domain::CoordinateMaps::Wedge<3>;\n  using Halves = Wedge3DMap::WedgeHalves;\n  std::vector<Wedge3DMap> wedges_for_all_layers{};\n\n  const size_t number_of_layers = 1 + radial_partitioning.size();\n  double temp_inner_radius = inner_radius;\n  double temp_inner_sphericity = inner_sphericity;\n  if (offset_options.has_value()) {\n    for (size_t layer_i = 0; layer_i < number_of_layers; layer_i++) {\n      const auto& radial_distribution_this_layer =\n          radial_distribution.at(layer_i);\n      std::optional<double> optional_outer_radius{};\n      if (outer_sphericity != 0.0) {\n        optional_outer_radius = outer_radius;\n      } else {\n        optional_outer_radius = std::nullopt;\n      }\n      // Generate wedges/half-wedges a layer at a time.\n      std::vector<Wedge3DMap> wedges_for_this_layer{};\n      if (not use_half_wedges) {\n        for (size_t face_j = which_wedge_index(which_wedges); face_j < 6;\n             face_j++) {\n          wedges_for_this_layer.emplace_back(\n              temp_inner_radius, optional_outer_radius,\n              offset_options.value().first, offset_options.value().second,\n              gsl::at(wedge_orientations, face_j), use_equiangular_map,\n              Halves::Both, radial_distribution_this_layer);\n        }\n      } else {\n        for (size_t i = 0; i < 4; i++) {\n          wedges_for_this_layer.emplace_back(\n              temp_inner_radius, optional_outer_radius,\n              offset_options.value().first, offset_options.value().second,\n              gsl::at(wedge_orientations, i), use_equiangular_map,\n              Halves::LowerOnly, radial_distribution_this_layer);\n          wedges_for_this_layer.emplace_back(\n              temp_inner_radius, optional_outer_radius,\n              offset_options.value().first, offset_options.value().second,\n              gsl::at(wedge_orientations, i), use_equiangular_map,\n              Halves::UpperOnly, radial_distribution_this_layer);\n        }\n        wedges_for_this_layer.emplace_back(\n            temp_inner_radius, optional_outer_radius,\n            offset_options.value().first, offset_options.value().second,\n            gsl::at(wedge_orientations, 4), use_equiangular_map, Halves::Both,\n            radial_distribution_this_layer);\n        wedges_for_this_layer.emplace_back(\n            temp_inner_radius, optional_outer_radius,\n            offset_options.value().first, offset_options.value().second,\n            gsl::at(wedge_orientations, 5), use_equiangular_map, Halves::Both,\n            radial_distribution_this_layer);\n      }\n      for (const auto& wedge : wedges_for_this_layer) {\n        wedges_for_all_layers.push_back(wedge);\n      }\n    }\n  } else {\n    double temp_outer_radius{};\n    for (size_t layer_i = 0; layer_i < number_of_layers; layer_i++) {\n      const auto& radial_distribution_this_layer =\n          radial_distribution.at(layer_i);\n      if (layer_i != radial_partitioning.size()) {\n        temp_outer_radius = radial_partitioning.at(layer_i);\n      } else {\n        temp_outer_radius = outer_radius;\n      }\n      // Generate wedges/half-wedges a layer at a time.\n      std::vector<Wedge3DMap> wedges_for_this_layer{};\n      if (not use_half_wedges) {\n        for (size_t face_j = which_wedge_index(which_wedges); face_j < 6;\n             face_j++) {\n          wedges_for_this_layer.emplace_back(\n              temp_inner_radius, temp_outer_radius, temp_inner_sphericity,\n              outer_sphericity, gsl::at(wedge_orientations, face_j),\n              use_equiangular_map, Halves::Both, radial_distribution_this_layer,\n              std::array<double, 2>({{M_PI_2, M_PI_2}}));\n        }\n      } else {\n        for (size_t i = 0; i < 4; i++) {\n          wedges_for_this_layer.emplace_back(\n              temp_inner_radius, temp_outer_radius, temp_inner_sphericity,\n              outer_sphericity, gsl::at(wedge_orientations, i),\n              use_equiangular_map, Halves::LowerOnly,\n              radial_distribution_this_layer,\n              std::array<double, 2>({{opening_angle, M_PI_2}}));\n          wedges_for_this_layer.emplace_back(\n              temp_inner_radius, temp_outer_radius, temp_inner_sphericity,\n              outer_sphericity, gsl::at(wedge_orientations, i),\n              use_equiangular_map, Halves::UpperOnly,\n              radial_distribution_this_layer,\n              std::array<double, 2>({{opening_angle, M_PI_2}}));\n        }\n        const double endcap_opening_angle = M_PI - opening_angle;\n        const std::array<double, 2> endcap_opening_angles = {\n            {endcap_opening_angle, endcap_opening_angle}};\n        wedges_for_this_layer.emplace_back(\n            temp_inner_radius, temp_outer_radius, temp_inner_sphericity,\n            outer_sphericity, gsl::at(wedge_orientations, 4),\n            use_equiangular_map, Halves::Both, radial_distribution_this_layer,\n            endcap_opening_angles, false);\n        wedges_for_this_layer.emplace_back(\n            temp_inner_radius, temp_outer_radius, temp_inner_sphericity,\n            outer_sphericity, gsl::at(wedge_orientations, 5),\n            use_equiangular_map, Halves::Both, radial_distribution_this_layer,\n            endcap_opening_angles, false);\n      }\n      for (const auto& wedge : wedges_for_this_layer) {\n        wedges_for_all_layers.push_back(wedge);\n      }\n\n      if (layer_i != radial_partitioning.size()) {\n        temp_inner_radius = radial_partitioning.at(layer_i);\n        temp_inner_sphericity = outer_sphericity;\n      }\n    }\n  }\n  return wedges_for_all_layers;\n}\n\nstd::vector<domain::CoordinateMaps::Frustum> frustum_coordinate_maps(\n    const double length_inner_cube, const double length_outer_cube,\n    const bool equiangular_map_at_outer, const bool equiangular_map_at_inner,\n    const std::array<double, 3>& origin_preimage,\n    const domain::CoordinateMaps::Distribution radial_distribution,\n    const std::optional<double> distribution_value, const double sphericity,\n    const double opening_angle) {\n  if (sphericity != 0.0 and sphericity != 1.0) {\n    ERROR(\"Sphericity for Frusmtums must be either 0 or 1, not \" << sphericity);\n  }\n  const double sphericity_factor = sphericity == 0.0 ? 1.0 : std::sqrt(3.0);\n  if (length_inner_cube >= 0.5 * sphericity_factor * length_outer_cube) {\n    ERROR(\"The outer cube (\"\n          << length_outer_cube << \") is too small! The inner cubes (\"\n          << (length_inner_cube * sphericity_factor)\n          << \") will pierce the surface of the outer cube. The sphericity is \"\n          << sphericity);\n  }\n  if (not(abs(origin_preimage[0]) + length_inner_cube <\n              0.5 * sphericity_factor * length_outer_cube and\n          abs(origin_preimage[1]) + length_inner_cube <\n              0.5 * sphericity_factor * length_outer_cube and\n          abs(origin_preimage[2]) + 0.5 * length_inner_cube <\n              0.5 * sphericity_factor * length_outer_cube)) {\n    ERROR(\n        \"The current choice for `origin_preimage` results in the inner cubes \"\n        \"piercing the surface of the outer cube. The sphericity is \"\n        << sphericity);\n  }\n  const auto frustum_orientations = orientations_for_sphere_wrappings();\n  const double lower = 0.5 * length_inner_cube;\n  const double top = 0.5 * length_outer_cube;\n\n  using FrustumMap = domain::CoordinateMaps::Frustum;\n  // Binary compact object Domains use 10 Frustums, 4 around each Shell in the\n  // +z,-z,+y,-y directions, and 1 Frustum capping each end in +x, -x.\n  // The frustums on the left are given a -1.0 indicating a lower half\n  // equiangular map while the frustums on the right are given a 1.0 indicating\n  // an upper half equiangular map\n  // If we wish to stretch this enveloping cube of frustums along its x-axis,\n  // the four left frustums and four right frustums have their upper bases\n  // stretched, and the end caps have their upper bases shifted upwards.\n  const double stretch = tan(0.5 * opening_angle);\n  std::vector<FrustumMap> frustums{};\n  for (size_t i = 0; i < 4; i++) {\n    // frustums on the left\n    std::array<double, 3> displacement_from_origin = discrete_rotation(\n        gsl::at(frustum_orientations, i).inverse_map(), origin_preimage);\n    frustums.push_back(FrustumMap{\n        {{{{-2.0 * lower - displacement_from_origin[0],\n            -lower - displacement_from_origin[1]}},\n          {{-displacement_from_origin[0], lower - displacement_from_origin[1]}},\n          {{stretch * -top, -top}},\n          {{0.0, top}}}},\n        lower - displacement_from_origin[2],\n        top,\n        gsl::at(frustum_orientations, i),\n        equiangular_map_at_outer,\n        equiangular_map_at_inner,\n        radial_distribution,\n        distribution_value,\n        sphericity,\n        -1.0,\n        opening_angle});\n    // frustums on the right\n    frustums.push_back(FrustumMap{{{{{-displacement_from_origin[0],\n                                      -lower - displacement_from_origin[1]}},\n                                    {{2.0 * lower - displacement_from_origin[0],\n                                      lower - displacement_from_origin[1]}},\n                                    {{0.0, -top}},\n                                    {{stretch * top, top}}}},\n                                  lower - displacement_from_origin[2],\n                                  top,\n                                  gsl::at(frustum_orientations, i),\n                                  equiangular_map_at_outer,\n                                  equiangular_map_at_inner,\n                                  radial_distribution,\n                                  distribution_value,\n                                  sphericity,\n                                  1.0,\n                                  opening_angle});\n  }\n  // end cap frustum on the right\n  std::array<double, 3> displacement_from_origin =\n      discrete_rotation(frustum_orientations[4].inverse_map(), origin_preimage);\n  frustums.push_back(FrustumMap{{{{{-lower - displacement_from_origin[0],\n                                    -lower - displacement_from_origin[1]}},\n                                  {{lower - displacement_from_origin[0],\n                                    lower - displacement_from_origin[1]}},\n                                  {{-top, -top}},\n                                  {{top, top}}}},\n                                2.0 * lower - displacement_from_origin[2],\n                                stretch * top,\n                                frustum_orientations[4],\n                                equiangular_map_at_outer,\n                                equiangular_map_at_inner,\n                                radial_distribution,\n                                distribution_value,\n                                sphericity,\n                                0.0,\n                                M_PI_2});\n  // end cap frustum on the left\n  displacement_from_origin =\n      discrete_rotation(frustum_orientations[5].inverse_map(), origin_preimage);\n  frustums.push_back(FrustumMap{{{{{-lower - displacement_from_origin[0],\n                                    -lower - displacement_from_origin[1]}},\n                                  {{lower - displacement_from_origin[0],\n                                    lower - displacement_from_origin[1]}},\n                                  {{-top, -top}},\n                                  {{top, top}}}},\n                                2.0 * lower - displacement_from_origin[2],\n                                stretch * top,\n                                frustum_orientations[5],\n                                equiangular_map_at_outer,\n                                equiangular_map_at_inner,\n                                radial_distribution,\n                                distribution_value,\n                                sphericity,\n                                0.0,\n                                M_PI_2});\n  return frustums;\n}\n\nnamespace {\nstd::array<size_t, 8> concatenate_faces(const std::array<size_t, 4>& face1,\n                                        const std::array<size_t, 4>& face2) {\n  return {{face1[0], face1[1], face1[2], face1[3], face2[0], face2[1], face2[2],\n           face2[3]}};\n}\n\nstd::array<size_t, 4> next_face_in_radial_direction(\n    const std::array<size_t, 4>& face) {\n  return {{face[0] + 8, face[1] + 8, face[2] + 8, face[3] + 8}};\n}\n\nstd::array<std::array<size_t, 4>, 6> next_layer_in_radial_direction(\n    const std::array<std::array<size_t, 4>, 6>& layer) {\n  return {{next_face_in_radial_direction(layer[0]),\n           next_face_in_radial_direction(layer[1]),\n           next_face_in_radial_direction(layer[2]),\n           next_face_in_radial_direction(layer[3]),\n           next_face_in_radial_direction(layer[4]),\n           next_face_in_radial_direction(layer[5])}};\n}\n\nvoid identify_corners_with_each_other(\n    const gsl::not_null<std::vector<std::array<size_t, 8>>*> corners,\n    const size_t corner_1, const size_t corner_2) {\n  for (auto& block_corners : *corners) {\n    for (auto& corner : block_corners) {\n      if (corner == corner_2) {\n        corner = corner_1;\n      }\n    }\n  }\n}\n}  // namespace\n\nstd::vector<std::array<size_t, 8>> corners_for_radially_layered_domains(\n    const size_t number_of_layers, const bool include_central_block,\n    const std::array<size_t, 8>& central_block_corners,\n    ShellWedges which_wedges) {\n  const std::array<size_t, 8>& c = central_block_corners;\n  std::array<std::array<size_t, 4>, 6> faces_layer_zero{\n      {{{c[4], c[5], c[6], c[7]}},    // Upper z\n       {{c[2], c[3], c[0], c[1]}},    // Lower z\n       {{c[6], c[7], c[2], c[3]}},    // Upper y\n       {{c[0], c[1], c[4], c[5]}},    // Lower y\n       {{c[1], c[3], c[5], c[7]}},    // Upper x\n       {{c[2], c[0], c[6], c[4]}}}};  // Lower x\n  std::vector<std::array<std::array<size_t, 4>, 6>> faces_in_each_layer{\n      faces_layer_zero};\n  for (size_t layer_j = 0; layer_j < number_of_layers; layer_j++) {\n    faces_in_each_layer.push_back(\n        next_layer_in_radial_direction(faces_in_each_layer[layer_j]));\n  }\n  std::vector<std::array<size_t, 8>> corners{};\n  for (size_t layer_i = 0; layer_i < number_of_layers; layer_i++) {\n    for (size_t face_j = which_wedge_index(which_wedges); face_j < 6;\n         face_j++) {\n      corners.push_back(concatenate_faces(\n          gsl::at(gsl::at(faces_in_each_layer, layer_i), face_j),\n          gsl::at(gsl::at(faces_in_each_layer, layer_i + 1), face_j)));\n    }\n  }\n  if (include_central_block) {\n    corners.push_back(central_block_corners);\n  }\n  return corners;\n}\n\nstd::vector<std::array<size_t, 8>> corners_for_biradially_layered_domains(\n    const size_t number_of_radial_layers,\n    const size_t number_of_biradial_layers,\n    const bool include_central_block_lhs, const bool include_central_block_rhs,\n    const std::array<size_t, 8>& central_block_corners_lhs) {\n  const size_t total_number_of_layers =\n      number_of_radial_layers + number_of_biradial_layers;\n  const std::array<size_t, 8> central_block_corners_rhs = [&]() {\n    std::array<size_t, 8> local_central_block_corners_rhs{};\n    std::transform(\n        central_block_corners_lhs.begin(), central_block_corners_lhs.end(),\n        local_central_block_corners_rhs.begin(), [&](const size_t corner) {\n          return corner + 8 * (total_number_of_layers + 1);\n        });\n    return local_central_block_corners_rhs;\n  }();\n\n  const auto layers_lhs = corners_for_radially_layered_domains(\n      total_number_of_layers, include_central_block_lhs,\n      central_block_corners_lhs);\n  const auto layers_rhs = corners_for_radially_layered_domains(\n      total_number_of_layers, include_central_block_rhs,\n      central_block_corners_rhs);\n  std::vector<std::array<size_t, 8>> corners{};\n  // Fill six-block layers on the left-hand side first,\n  // then fill six-block layers on the right-hand side.\n  for (size_t layer_i = 0; layer_i < number_of_radial_layers; layer_i++) {\n    for (size_t face_j = 0; face_j < 6; face_j++) {\n      corners.push_back(layers_lhs[face_j + 6 * layer_i]);\n    }\n  }\n  for (size_t layer_i = 0; layer_i < number_of_radial_layers; layer_i++) {\n    for (size_t face_j = 0; face_j < 6; face_j++) {\n      corners.push_back(layers_rhs[face_j + 6 * layer_i]);\n    }\n  }\n  // Then fill ten-block layers which wrap both sides.\n  for (size_t layer_i = number_of_radial_layers;\n       layer_i < total_number_of_layers; layer_i++) {\n    for (size_t face_j = 0; face_j < 6; face_j++) {\n      // Face number 4 is the face in the +x direction,\n      // which is the block to be skipped on the left-hand side.\n      if (face_j != 4) {\n        corners.push_back(layers_lhs[face_j + 6 * layer_i]);\n      }\n      // Face number 5 is the face in the -x direction,\n      // which is the block to be skipped on the right-hand side.\n      if (face_j != 5) {\n        corners.push_back(layers_rhs[face_j + 6 * layer_i]);\n      }\n    }\n  }\n  if (include_central_block_lhs) {\n    corners.push_back(central_block_corners_lhs);\n  }\n  if (include_central_block_rhs) {\n    corners.push_back(central_block_corners_rhs);\n  }\n  // The first corner to identify is on the last full layer.\n  const size_t first_corner_to_identify = 2 + number_of_radial_layers * 8;\n  const size_t corner_to_identify_with =\n      first_corner_to_identify - 1 + (8 * (total_number_of_layers + 1));\n  for (size_t j = 0; j < number_of_biradial_layers + 1; j++) {\n    for (size_t i = 0; i < 4; i++) {\n      const size_t offset = 2 * i + 8 * j;\n      identify_corners_with_each_other(&corners,\n                                       first_corner_to_identify + offset,\n                                       corner_to_identify_with + offset);\n    }\n  }\n  return corners;\n}\n\nstd::vector<domain::CoordinateMaps::ProductOf3Maps<\n    domain::CoordinateMaps::Interval, domain::CoordinateMaps::Interval,\n    domain::CoordinateMaps::Interval>>\ncyl_wedge_coord_map_center_blocks(\n    const double inner_radius, const double lower_z_bound,\n    const double upper_z_bound, const bool use_equiangular_map,\n    const std::vector<double>& partitioning_in_z,\n    const std::vector<domain::CoordinateMaps::Distribution>& distribution_in_z,\n    const CylindricalDomainParityFlip parity_flip) {\n  using Interval = domain::CoordinateMaps::Interval;\n  using Interval3D =\n      domain::CoordinateMaps::ProductOf3Maps<Interval, Interval, Interval>;\n  std::vector<Interval3D> maps{};\n  const double lower_logical_zeta =\n      parity_flip == CylindricalDomainParityFlip::z_direction ? 1.0 : -1.0;\n  const double upper_logical_zeta =\n      parity_flip == CylindricalDomainParityFlip::z_direction ? -1.0 : 1.0;\n  double temp_lower_z_bound = lower_z_bound;\n  double temp_upper_z_bound{};\n  const auto angular_distribution =\n      use_equiangular_map ? domain::CoordinateMaps::Distribution::Equiangular\n                          : domain::CoordinateMaps::Distribution::Linear;\n  for (size_t layer = 0; layer < 1 + partitioning_in_z.size(); layer++) {\n    if (layer != partitioning_in_z.size()) {\n      temp_upper_z_bound = partitioning_in_z.at(layer);\n    } else {\n      temp_upper_z_bound = upper_z_bound;\n    }\n    maps.emplace_back(\n        Interval3D{Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                            inner_radius / sqrt(2.0), angular_distribution),\n                   Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                            inner_radius / sqrt(2.0), angular_distribution),\n                   Interval{lower_logical_zeta, upper_logical_zeta,\n                            temp_lower_z_bound, temp_upper_z_bound,\n                            distribution_in_z.at(layer), lower_z_bound}});\n\n    if (layer != partitioning_in_z.size()) {\n      temp_lower_z_bound = partitioning_in_z.at(layer);\n    }\n  }\n  return maps;\n}\n\nstd::vector<domain::CoordinateMaps::ProductOf2Maps<\n    domain::CoordinateMaps::Wedge<2>, domain::CoordinateMaps::Interval>>\ncyl_wedge_coord_map_surrounding_blocks(\n    const double inner_radius, const double outer_radius,\n    const double lower_z_bound, const double upper_z_bound,\n    const bool use_equiangular_map, const double inner_circularity,\n    const std::vector<double>& radial_partitioning,\n    const std::vector<double>& partitioning_in_z,\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution,\n    const std::vector<domain::CoordinateMaps::Distribution>& distribution_in_z,\n    const CylindricalDomainParityFlip parity_flip) {\n  using Interval = domain::CoordinateMaps::Interval;\n  using Wedge2D = domain::CoordinateMaps::Wedge<2>;\n  using Wedge3DPrism =\n      domain::CoordinateMaps::ProductOf2Maps<Wedge2D, Interval>;\n  ASSERT(radial_distribution.size() == 1 + radial_partitioning.size(),\n         \"Specify a radial distribution for every cylindrical shell. You \"\n         \"specified \"\n             << radial_distribution.size() << \" items, but the domain has \"\n             << 1 + radial_partitioning.size() << \" shells.\");\n  ASSERT(radial_distribution.front() ==\n             domain::CoordinateMaps::Distribution::Linear,\n         \"The innermost shell must have a 'Linear' radial distribution because \"\n         \"it changes in circularity.\");\n  std::vector<Wedge3DPrism> maps{};\n  double temp_inner_circularity{};\n  double temp_inner_radius{};\n  double temp_outer_radius{};\n  double temp_lower_z_bound = lower_z_bound;\n  double temp_upper_z_bound{};\n  const auto use_both_halves = Wedge2D::WedgeHalves::Both;\n  const std::array<OrientationMap<2>, 4> wedge_orientations = {\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},  //+x wedge\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},  //+y wedge\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},  //-x wedge\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}}  //-y wedge\n  };\n  for (size_t layer = 0; layer < 1 + partitioning_in_z.size(); layer++) {\n    temp_inner_radius = inner_radius;\n    if (layer != partitioning_in_z.size()) {\n      temp_upper_z_bound = partitioning_in_z.at(layer);\n    } else {\n      temp_upper_z_bound = upper_z_bound;\n    }\n    temp_inner_circularity = inner_circularity;\n    for (size_t shell = 0; shell < 1 + radial_partitioning.size(); shell++) {\n      if (shell != radial_partitioning.size()) {\n        temp_outer_radius = radial_partitioning.at(shell);\n      } else {\n        temp_outer_radius = outer_radius;\n      }\n      auto z_map = Interval{\n          parity_flip == CylindricalDomainParityFlip::z_direction ? 1.0 : -1.0,\n          parity_flip == CylindricalDomainParityFlip::z_direction ? -1.0 : 1.0,\n          temp_lower_z_bound,\n          temp_upper_z_bound,\n          distribution_in_z.at(layer),\n          lower_z_bound};\n      for (const auto& cardinal_direction : wedge_orientations) {\n        maps.emplace_back(Wedge3DPrism{\n            Wedge2D{temp_inner_radius, temp_outer_radius,\n                    temp_inner_circularity, 1.0, cardinal_direction,\n                    use_equiangular_map, use_both_halves,\n                    radial_distribution.at(shell)},\n            z_map});\n      }\n      temp_inner_circularity = 1.;\n      if (shell != radial_partitioning.size()) {\n        temp_inner_radius = radial_partitioning.at(shell);\n      }\n    }\n    if (layer != partitioning_in_z.size()) {\n      temp_lower_z_bound = partitioning_in_z.at(layer);\n    }\n  }\n  return maps;\n}\n\ntemplate <typename TargetFrame>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>>\ncyl_wedge_coordinate_maps(\n    const double inner_radius, const double outer_radius,\n    const double lower_z_bound, const double upper_z_bound,\n    const bool use_equiangular_map,\n    const std::vector<double>& radial_partitioning,\n    const std::vector<double>& partitioning_in_z,\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution,\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        distribution_in_z) {\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>>\n      cylinder_mapping;\n  const auto maps_surrounding = cyl_wedge_coord_map_surrounding_blocks(\n      inner_radius, outer_radius, lower_z_bound, upper_z_bound,\n      use_equiangular_map, 0.0, radial_partitioning, partitioning_in_z,\n      radial_distribution, distribution_in_z,\n      CylindricalDomainParityFlip::none);\n\n  // add_to_cylinder_mapping adds the maps for individual blocks in\n  // the correct order.  This order is a center block at each height\n  // partition and then the surrounding blocks at that height\n  // partition.\n  auto add_to_cylinder_mapping = [&cylinder_mapping, &partitioning_in_z,\n                                  &radial_partitioning,\n                                  &maps_surrounding](const auto& maps_center) {\n    size_t surrounding_block_index = 0;\n    for (size_t height = 0; height < 1 + partitioning_in_z.size(); ++height) {\n      cylinder_mapping.emplace_back(\n          domain::make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n              maps_center.at(height)));\n      for (size_t radius = 0; radius < 1 + radial_partitioning.size();\n           ++radius) {\n        // There are 4 surrounding blocks at each radius and height\n        for (size_t block_at_this_radius = 0; block_at_this_radius < 4;\n             ++block_at_this_radius, ++surrounding_block_index) {\n          cylinder_mapping.emplace_back(\n              domain::make_coordinate_map_base<Frame::BlockLogical,\n                                               TargetFrame>(\n                  maps_surrounding.at(surrounding_block_index)));\n        }\n      }\n    }\n  };\n\n  add_to_cylinder_mapping(cyl_wedge_coord_map_center_blocks(\n      inner_radius, lower_z_bound, upper_z_bound, use_equiangular_map,\n      partitioning_in_z, distribution_in_z, CylindricalDomainParityFlip::none));\n  return cylinder_mapping;\n}\n\nstd::vector<std::array<size_t, 8>> corners_for_cylindrical_layered_domains(\n    const size_t number_of_shells, const size_t number_of_discs) {\n  using BlockCorners = std::array<size_t, 8>;\n  std::vector<BlockCorners> corners;\n  const size_t n_L = 4 * (number_of_shells + 1);  // number of corners per layer\n  const BlockCorners center{{0, 1, 2, 3, n_L + 0, n_L + 1, n_L + 2, n_L + 3}};\n  const BlockCorners east{{1, 5, 3, 7, n_L + 1, n_L + 5, n_L + 3, n_L + 7}};\n  const BlockCorners north{{3, 7, 2, 6, n_L + 3, n_L + 7, n_L + 2, n_L + 6}};\n  const BlockCorners west{{2, 6, 0, 4, n_L + 2, n_L + 6, n_L + 0, n_L + 4}};\n  const BlockCorners south{{0, 4, 1, 5, n_L + 0, n_L + 4, n_L + 1, n_L + 5}};\n  for (size_t i = 0; i < number_of_discs; i++) {\n    corners.push_back(center + make_array<8>(i * n_L));\n    for (size_t j = 0; j < number_of_shells; j++) {\n      const auto offset = make_array<8>(i * n_L + 4 * j);\n      corners.push_back(east + offset);   //+x wedge\n      corners.push_back(north + offset);  //+y wedge\n      corners.push_back(west + offset);   //-x wedge\n      corners.push_back(south + offset);  //-y wedge\n    }\n  }\n  return corners;\n}\n\ntemplate <size_t VolumeDim>\nauto indices_for_rectilinear_domains(\n    const Index<VolumeDim>& domain_extents,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude)\n    -> std::vector<Index<VolumeDim>> {\n  std::vector<Index<VolumeDim>> block_indices;\n  for (IndexIterator<VolumeDim> ii(domain_extents); ii; ++ii) {\n    if (std::find(block_indices_to_exclude.begin(),\n                  block_indices_to_exclude.end(),\n                  *ii) == block_indices_to_exclude.end()) {\n      block_indices.push_back(*ii);\n    }\n  }\n  return block_indices;\n}\n\ntemplate <size_t VolumeDim>\nauto block_names_for_rectilinear_domains(\n    const Index<VolumeDim>& domain_extents,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude)\n    -> std::vector<std::string> {\n  std::vector<std::string> names{};\n  for (const auto& index : indices_for_rectilinear_domains(\n           domain_extents, block_indices_to_exclude)) {\n    names.emplace_back(MakeString{} << \"Block\" << index);\n  }\n  return names;\n}\n\ntemplate <size_t VolumeDim>\nstd::vector<std::array<size_t, two_to_the(VolumeDim)>>\ncorners_for_rectilinear_domains(\n    const Index<VolumeDim>& domain_extents,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude) {\n  std::vector<std::array<size_t, two_to_the(VolumeDim)>> corners{};\n\n  // The number of blocks is one less than\n  // the number of corners in each dimension:\n  std::array<size_t, VolumeDim> number_of_corners{};\n  for (size_t d = 0; d < VolumeDim; d++) {\n    gsl::at(number_of_corners, d) = domain_extents[d] + 1;\n  }\n\n  Index<VolumeDim> global_corner_extents{number_of_corners};\n  for (const auto& block_index : indices_for_rectilinear_domains(\n           domain_extents, block_indices_to_exclude)) {\n    std::array<size_t, two_to_the(VolumeDim)> corners_for_single_block{};\n    for (VolumeCornerIterator<VolumeDim> vci(block_index,\n                                             global_corner_extents);\n         vci; ++vci) {\n      gsl::at(corners_for_single_block, vci.local_corner_number()) =\n          vci.global_corner_number();\n    }\n    corners.push_back(corners_for_single_block);\n  }\n  return corners;\n}\n\ntemplate <typename TargetFrame, typename Map>\nstd::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 1>>\nproduct_of_1d_maps(\n    std::array<Map, 1>& maps,\n    const OrientationMap<1>& rotation = OrientationMap<1>::create_aligned()) {\n  if (rotation.is_aligned()) {\n    return domain::make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n        maps[0]);\n  }\n  return domain::make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n      domain::CoordinateMaps::DiscreteRotation<1>{rotation}, maps[0]);\n}\n\ntemplate <typename TargetFrame, typename Map>\nstd::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 2>>\nproduct_of_1d_maps(\n    std::array<Map, 2>& maps,\n    const OrientationMap<2>& rotation = OrientationMap<2>::create_aligned()) {\n  if (rotation.is_aligned()) {\n    return domain::make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n        domain::CoordinateMaps::ProductOf2Maps<Map, Map>(maps[0], maps[1]));\n  }\n  return domain::make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n      domain::CoordinateMaps::DiscreteRotation<2>{rotation},\n      domain::CoordinateMaps::ProductOf2Maps<Map, Map>(maps[0], maps[1]));\n}\n\ntemplate <typename TargetFrame, typename Map>\nstd::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>\nproduct_of_1d_maps(\n    std::array<Map, 3>& maps,\n    const OrientationMap<3>& rotation = OrientationMap<3>::create_aligned()) {\n  if (rotation.is_aligned()) {\n    return domain::make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n        domain::CoordinateMaps::ProductOf3Maps<Map, Map, Map>(maps[0], maps[1],\n                                                              maps[2]));\n  }\n  return domain::make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n      domain::CoordinateMaps::DiscreteRotation<3>{rotation},\n      domain::CoordinateMaps::ProductOf3Maps<Map, Map, Map>(maps[0], maps[1],\n                                                            maps[2]));\n}\n\ntemplate <typename TargetFrame, size_t VolumeDim>\nstd::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, VolumeDim>>>\nmaps_for_rectilinear_domains(\n    const Index<VolumeDim>& domain_extents,\n    const std::array<std::vector<double>, VolumeDim>& block_demarcations,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude,\n    const std::vector<OrientationMap<VolumeDim>>& orientations_of_all_blocks,\n    const bool use_equiangular_map) {\n  for (size_t d = 0; d < VolumeDim; d++) {\n    ASSERT(gsl::at(block_demarcations, d).size() == domain_extents[d] + 1,\n           \"If there are N blocks, there must be N+1 demarcations.\");\n  }\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, VolumeDim>>>\n      maps{};\n  size_t block_orientation_index = 0;\n  for (const auto& block_index : indices_for_rectilinear_domains(\n           domain_extents, block_indices_to_exclude)) {\n    std::array<double, VolumeDim> lower_bounds{};\n    std::array<double, VolumeDim> upper_bounds{};\n    for (size_t d = 0; d < VolumeDim; d++) {\n      gsl::at(lower_bounds, d) =\n          gsl::at(gsl::at(block_demarcations, d), block_index[d]);\n      gsl::at(upper_bounds, d) =\n          gsl::at(gsl::at(block_demarcations, d), block_index[d] + 1);\n      ASSERT(gsl::at(upper_bounds, d) > gsl::at(lower_bounds, d),\n             \"The block demarcations must be strictly increasing.\");\n    }\n    if (not use_equiangular_map) {\n      using Affine = domain::CoordinateMaps::Affine;\n      std::array<Affine, VolumeDim> affine_maps{};\n      for (size_t d = 0; d < VolumeDim; d++) {\n        gsl::at(affine_maps, d) = Affine{-1.0, 1.0, gsl::at(lower_bounds, d),\n                                         gsl::at(upper_bounds, d)};\n      }\n      if (not orientations_of_all_blocks.empty()) {\n        maps.push_back(product_of_1d_maps<TargetFrame>(\n            affine_maps, orientations_of_all_blocks[block_orientation_index]));\n      } else {\n        maps.push_back(product_of_1d_maps<TargetFrame>(affine_maps));\n      }\n    } else {\n      using Equiangular = domain::CoordinateMaps::Equiangular;\n      std::array<Equiangular, VolumeDim> equiangular_maps{};\n      for (size_t d = 0; d < VolumeDim; d++) {\n        gsl::at(equiangular_maps, d) = Equiangular{\n            -1.0, 1.0, gsl::at(lower_bounds, d), gsl::at(upper_bounds, d)};\n      }\n      if (not orientations_of_all_blocks.empty()) {\n        maps.push_back(product_of_1d_maps<TargetFrame>(\n            equiangular_maps,\n            orientations_of_all_blocks[block_orientation_index]));\n      } else {\n        maps.push_back(product_of_1d_maps<TargetFrame>(equiangular_maps));\n      }\n    }\n    block_orientation_index++;\n  }\n  return maps;\n}\n\ntemplate <size_t VolumeDim>\nstd::array<size_t, two_to_the(VolumeDim)> discrete_rotation(\n    const OrientationMap<VolumeDim>& orientation,\n    const std::array<size_t, two_to_the(VolumeDim)>& corners_of_aligned) {\n  // compute the mapped logical corners, as\n  // they are the indices into the global corners.\n  const std::array<size_t, two_to_the(VolumeDim)> mapped_logical_corners =\n      [&orientation]() {\n        std::array<size_t, two_to_the(VolumeDim)> result{};\n        for (VolumeCornerIterator<VolumeDim> vci{}; vci; ++vci) {\n          const std::array<Direction<VolumeDim>, VolumeDim>\n              directions_of_logical_corner = vci.directions_of_corner();\n          std::array<Direction<VolumeDim>, VolumeDim>\n              directions_of_mapped_corner{};\n          for (size_t i = 0; i < VolumeDim; i++) {\n            gsl::at(directions_of_mapped_corner, i) =\n                // The inverse_map is used here to match the sense of rotation\n                // used in OrientationMap's discrete_rotation.\n                orientation.inverse_map()(\n                    gsl::at(directions_of_logical_corner, i));\n          }\n          size_t mapped_corner = 0;\n          for (size_t i = 0; i < VolumeDim; i++) {\n            const auto& direction = gsl::at(directions_of_mapped_corner, i);\n            if (direction.side() == Side::Upper) {\n              mapped_corner += two_to_the(direction.dimension());\n            }\n          }\n          gsl::at(result, vci.local_corner_number()) = mapped_corner;\n        }\n        return result;\n      }();\n\n  std::array<size_t, two_to_the(VolumeDim)> result{};\n  for (size_t i = 0; i < two_to_the(VolumeDim); i++) {\n    gsl::at(result, i) =\n        gsl::at(corners_of_aligned, gsl::at(mapped_logical_corners, i));\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nDomain<VolumeDim> rectilinear_domain(\n    const Index<VolumeDim>& domain_extents,\n    const std::array<std::vector<double>, VolumeDim>& block_demarcations,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude,\n    const std::vector<OrientationMap<VolumeDim>>& orientations_of_all_blocks,\n    const std::array<bool, VolumeDim>& dimension_is_periodic,\n    const std::vector<PairOfFaces>& identifications,\n    const bool use_equiangular_map) {\n  std::vector<Block<VolumeDim>> blocks{};\n  auto corners_of_all_blocks =\n      corners_for_rectilinear_domains(domain_extents, block_indices_to_exclude);\n  auto rotations_of_all_blocks = orientations_of_all_blocks;\n  // If no OrientationMaps are provided - default to all aligned:\n  if (orientations_of_all_blocks.empty()) {\n    rotations_of_all_blocks = std::vector<OrientationMap<VolumeDim>>{\n        corners_of_all_blocks.size(),\n        OrientationMap<VolumeDim>::create_aligned()};\n  }\n  auto maps = maps_for_rectilinear_domains<Frame::Inertial>(\n      domain_extents, block_demarcations, block_indices_to_exclude,\n      rotations_of_all_blocks, use_equiangular_map);\n  for (size_t i = 0; i < corners_of_all_blocks.size(); i++) {\n    corners_of_all_blocks[i] =\n        discrete_rotation(rotations_of_all_blocks[i], corners_of_all_blocks[i]);\n  }\n  std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>\n      neighbors_of_all_blocks;\n  set_internal_boundaries<VolumeDim>(&neighbors_of_all_blocks,\n                                     corners_of_all_blocks);\n  set_identified_boundaries<VolumeDim>(identifications, corners_of_all_blocks,\n                                       &neighbors_of_all_blocks);\n  set_cartesian_periodic_boundaries<VolumeDim>(\n      dimension_is_periodic, corners_of_all_blocks, rotations_of_all_blocks,\n      &neighbors_of_all_blocks);\n  auto block_names = block_names_for_rectilinear_domains(\n      domain_extents, block_indices_to_exclude);\n  for (size_t i = 0; i < corners_of_all_blocks.size(); i++) {\n    blocks.emplace_back(std::move(maps[i]), i,\n                        std::move(neighbors_of_all_blocks[i]),\n                        std::move(block_names[i]));\n  }\n  return Domain<VolumeDim>(std::move(blocks));\n}\n\nstd::ostream& operator<<(std::ostream& os, const ShellWedges& which_wedges) {\n  switch (which_wedges) {\n    case ShellWedges::All:\n      return os << \"All\";\n    case ShellWedges::FourOnEquator:\n      return os << \"FourOnEquator\";\n    case ShellWedges::OneAlongMinusX:\n      return os << \"OneAlongMinusX\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Unknown ShellWedges type\");\n      // LCOV_EXCL_STOP\n  }\n}\n\ntemplate <>\nShellWedges Options::create_from_yaml<ShellWedges>::create<void>(\n    const Options::Option& options) {\n  const auto which_wedges = options.parse_as<std::string>();\n  if (which_wedges == \"All\") {\n    return ShellWedges::All;\n  } else if (which_wedges == \"FourOnEquator\") {\n    return ShellWedges::FourOnEquator;\n  } else if (which_wedges == \"OneAlongMinusX\") {\n    return ShellWedges::OneAlongMinusX;\n  }\n  PARSE_ERROR(options.context(),\n              \"WhichWedges must be 'All', 'FourOnEquator' or 'OneAlongMinusX'\");\n}\n\ntemplate std::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>\ncyl_wedge_coordinate_maps(\n    const double inner_radius, const double outer_radius,\n    const double lower_z_bound, const double upper_z_bound,\n    const bool use_equiangular_map,\n    const std::vector<double>& radial_partitioning,\n    const std::vector<double>& partitioning_in_z,\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution,\n    const std::vector<domain::CoordinateMaps::Distribution>& distribution_in_z);\ntemplate std::vector<std::unique_ptr<\n    domain::CoordinateMapBase<Frame::BlockLogical, Frame::Grid, 3>>>\ncyl_wedge_coordinate_maps(\n    const double inner_radius, const double outer_radius,\n    const double lower_z_bound, const double upper_z_bound,\n    const bool use_equiangular_map,\n    const std::vector<double>& radial_partitioning,\n    const std::vector<double>& partitioning_in_z,\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution,\n    const std::vector<domain::CoordinateMaps::Distribution>& distribution_in_z);\n// Explicit instantiations\nusing Affine2d =\n    domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::Affine,\n                                           domain::CoordinateMaps::Affine>;\nusing Affine3d =\n    domain::CoordinateMaps::ProductOf3Maps<domain::CoordinateMaps::Affine,\n                                           domain::CoordinateMaps::Affine,\n                                           domain::CoordinateMaps::Affine>;\nusing Equiangular3d =\n    domain::CoordinateMaps::ProductOf3Maps<domain::CoordinateMaps::Equiangular,\n                                           domain::CoordinateMaps::Equiangular,\n                                           domain::CoordinateMaps::Equiangular>;\nusing AffineIdentity =\n    domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::Affine,\n                                           domain::CoordinateMaps::Identity<2>>;\n\ntemplate class domain::CoordinateMaps::ProductOf2Maps<\n    domain::CoordinateMaps::Affine, domain::CoordinateMaps::Affine>;\ntemplate class domain::CoordinateMaps::ProductOf3Maps<\n    domain::CoordinateMaps::Affine, domain::CoordinateMaps::Affine,\n    domain::CoordinateMaps::Affine>;\ntemplate class domain::CoordinateMaps::ProductOf2Maps<\n    domain::CoordinateMaps::Equiangular, domain::CoordinateMaps::Equiangular>;\ntemplate class domain::CoordinateMaps::ProductOf3Maps<\n    domain::CoordinateMaps::Equiangular, domain::CoordinateMaps::Equiangular,\n    domain::CoordinateMaps::Equiangular>;\ntemplate class domain::CoordinateMaps::ProductOf2Maps<\n    domain::CoordinateMaps::Affine, domain::CoordinateMaps::Identity<2>>;\n\nINSTANTIATE_MAPS_FUNCTIONS(((Affine2d), (Affine3d), (Equiangular3d),\n                            (domain::CoordinateMaps::Wedge<3>,\n                             domain::CoordinateMaps::EquatorialCompression,\n                             AffineIdentity)),\n                           (Frame::BlockLogical, Frame::ElementLogical),\n                           (Frame::Grid, Frame::Inertial), (double, DataVector))\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template void set_internal_boundaries(                                    \\\n      const gsl::not_null<                                                  \\\n          std::vector<DirectionMap<DIM(data), BlockNeighbors<DIM(data)>>>*> \\\n          neighbors_of_all_blocks,                                          \\\n      const std::vector<std::array<size_t, two_to_the(DIM(data))>>&         \\\n          corners_of_all_blocks);                                           \\\n  template void set_internal_boundaries(                                    \\\n      const gsl::not_null<                                                  \\\n          std::vector<DirectionMap<DIM(data), BlockNeighbors<DIM(data)>>>*> \\\n          neighbors_of_all_blocks,                                          \\\n      const std::vector<std::unique_ptr<domain::CoordinateMapBase<          \\\n          Frame::BlockLogical, Frame::Inertial, DIM(data)>>>& maps);        \\\n  template void set_identified_boundaries(                                  \\\n      const std::vector<PairOfFaces>& identifications,                      \\\n      const std::vector<std::array<size_t, two_to_the(DIM(data))>>&         \\\n          corners_of_all_blocks,                                            \\\n      const gsl::not_null<                                                  \\\n          std::vector<DirectionMap<DIM(data), BlockNeighbors<DIM(data)>>>*> \\\n          neighbors_of_all_blocks);                                         \\\n  template auto indices_for_rectilinear_domains(                            \\\n      const Index<DIM(data)>& domain_extents,                               \\\n      const std::vector<Index<DIM(data)>>& block_indices_to_exclude)        \\\n      -> std::vector<Index<DIM(data)>>;                                     \\\n  template auto block_names_for_rectilinear_domains(                        \\\n      const Index<DIM(data)>& domain_extents,                               \\\n      const std::vector<Index<DIM(data)>>& block_indices_to_exclude)        \\\n      -> std::vector<std::string>;                                          \\\n  template std::vector<std::array<size_t, two_to_the(DIM(data))>>           \\\n  corners_for_rectilinear_domains(                                          \\\n      const Index<DIM(data)>& domain_extents,                               \\\n      const std::vector<Index<DIM(data)>>& block_indices_to_exclude);       \\\n  template std::vector<std::unique_ptr<domain::CoordinateMapBase<           \\\n      Frame::BlockLogical, Frame::Inertial, DIM(data)>>>                    \\\n  maps_for_rectilinear_domains(                                             \\\n      const Index<DIM(data)>& domain_extents,                               \\\n      const std::array<std::vector<double>, DIM(data)>& block_demarcations, \\\n      const std::vector<Index<DIM(data)>>& block_indices_to_exclude,        \\\n      const std::vector<OrientationMap<DIM(data)>>&                         \\\n          orientations_of_all_blocks,                                       \\\n      const bool use_equiangular_map);                                      \\\n  template std::array<size_t, two_to_the(DIM(data))> discrete_rotation(     \\\n      const OrientationMap<DIM(data)>& orientation,                         \\\n      const std::array<size_t, two_to_the(DIM(data))>& corners_of_aligned); \\\n  template Domain<DIM(data)> rectilinear_domain(                            \\\n      const Index<DIM(data)>& domain_extents,                               \\\n      const std::array<std::vector<double>, DIM(data)>& block_demarcations, \\\n      const std::vector<Index<DIM(data)>>& block_indices_to_exclude,        \\\n      const std::vector<OrientationMap<DIM(data)>>&                         \\\n          orientations_of_all_blocks,                                       \\\n      const std::array<bool, DIM(data)>& dimension_is_periodic,             \\\n      const std::vector<PairOfFaces>& identifications,                      \\\n      const bool use_equiangular_map);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1_st, 2_st, 3_st))\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Domain/DomainHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines DomainHelper functions\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iosfwd>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass BlockNeighbors;\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim>\nclass CoordinateMapBase;\n}  // namespace domain\ntemplate <size_t VolumeDim, typename T>\nclass DirectionMap;\ntemplate <size_t VolumeDim>\nclass Domain;\ntemplate <size_t VolumeDim>\nclass OrientationMap;\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\nnamespace domain::CoordinateMaps {\ntemplate <typename Map1, typename Map2>\nclass ProductOf2Maps;\ntemplate <typename Map1, typename Map2, typename Map3>\nclass ProductOf3Maps;\nclass Interval;\ntemplate <size_t Dim>\nclass Wedge;\nclass Frustum;\n}  // namespace domain::CoordinateMaps\n/// \\endcond\n\n/// \\ingroup ComputationalDomainGroup\n/// Each member in `PairOfFaces` holds the global corner ids of a block face.\n/// `PairOfFaces` is used in setting up periodic boundary conditions by\n/// identifying the two faces with each other.\n/// \\requires The pair of faces must belong to a single block.\nstruct PairOfFaces {\n  std::vector<size_t> first;\n  std::vector<size_t> second;\n};\n\n/// \\ingroup ComputationalDomainGroup\n/// Sets up the BlockNeighbors using the corner numbering scheme\n/// provided by the user to deduce the correct neighbors and\n/// orientations. Does not set up periodic boundary conditions.\ntemplate <size_t VolumeDim>\nvoid set_internal_boundaries(\n    gsl::not_null<\n        std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>*>\n        neighbors_of_all_blocks,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks);\n\n/// \\ingroup ComputationalDomainGroup\n/// Sets up the BlockNeighbors using the corner numbering scheme\n/// implied by the maps provided by the user to deduce the correct\n/// neighbors and orientations.\n/// \\warning Does not set up periodic boundary conditions.\ntemplate <size_t VolumeDim>\nvoid set_internal_boundaries(\n    gsl::not_null<\n        std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>*>\n        neighbors_of_all_blocks,\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, Frame::Inertial, VolumeDim>>>& maps);\n\n/// \\ingroup ComputationalDomainGroup\n/// Sets up additional BlockNeighbors corresponding to any\n/// identifications of faces provided by the user. Can be used\n/// for manually setting up periodic boundary conditions.\ntemplate <size_t VolumeDim>\nvoid set_identified_boundaries(\n    const std::vector<PairOfFaces>& identifications,\n    const std::vector<std::array<size_t, two_to_the(VolumeDim)>>&\n        corners_of_all_blocks,\n    gsl::not_null<\n        std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>*>\n        neighbors_of_all_blocks);\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The multi-indices that identify the individual Blocks in the lattice\ntemplate <size_t VolumeDim>\nauto indices_for_rectilinear_domains(\n    const Index<VolumeDim>& domain_extents,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude = {})\n    -> std::vector<Index<VolumeDim>>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The block names for the individual Blocks in the lattice\ntemplate <size_t VolumeDim>\nauto block_names_for_rectilinear_domains(\n    const Index<VolumeDim>& domain_extents,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude = {})\n    -> std::vector<std::string>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The corners for a rectilinear domain made of n-cubes.\n///\n/// The `domain_extents` argument holds the number of blocks to have\n/// in each dimension. The blocks all have aligned orientations by\n/// construction. The `block_indices_to_exclude` argument allows the user\n/// to selectively exclude blocks from the resulting domain. This allows\n/// for the creation of non-trivial shapes such as the net for a tesseract.\ntemplate <size_t VolumeDim>\nauto corners_for_rectilinear_domains(\n    const Index<VolumeDim>& domain_extents,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude = {})\n    -> std::vector<std::array<size_t, two_to_the(VolumeDim)>>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief An array of the orientations of the six blocks that make up a Sphere.\n///\n/// A Block or Blocks can be wrapped in an outer layer of Blocks surrounding\n/// the original Block(s). In the BBH Domain, this occurs several times, using\n/// both Wedges and Frustums. This standardizes the ordering of the orientations\n/// for both.\nstd::array<OrientationMap<3>, 6> orientations_for_sphere_wrappings();\n\n/// \\ingroup ComputationalDomainGroup\n/// The number of wedges to include in the Sphere domain.\nenum class ShellWedges {\n  /// Use the entire shell\n  All,\n  /// Use only the four equatorial wedges\n  FourOnEquator,\n  /// Use only the single wedge along -x\n  OneAlongMinusX\n};\n\n/// \\ingroup ComputationalDomainGroup\n/// The first index in the list \"UpperZ\", \"LowerZ\", \"UpperY\", \"LowerY\", \"UpperX\"\n/// \"LowerX\" that is included in `which_wedges`. It is 0 for `ShellWedges::All`,\n/// 2 for `ShellWedges::FourOnEquator`, and 5 for `ShellWedges::OneAlongMinusX`.\nsize_t which_wedge_index(const ShellWedges& which_wedges);\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * These are the CoordinateMaps of the Wedge<3>s used in the Sphere and\n * binary compact object DomainCreators. This function can also be used to\n * wrap the Sphere in a cube made of six Wedge<3>s.\n *\n * \\param inner_radius Radius of the inner boundary of the shell, or the\n * radius circumscribing the inner cube of a sphere.\n * \\param outer_radius Outer radius of the shell or sphere.\n * \\param inner_sphericity Specifies if the wedges form a spherical inner\n * boundary (1.0) or a cubical inner boundary (0.0).\n * \\param outer_sphericity Specifies if the wedges form a spherical outer\n * boundary (1.0) or a cubical outer boundary (0.0).\n * \\param offset_options A pair of values with the first being half the length\n * of the cube that would form the outer boundary and the second being the\n * offset to apply to the wedges.\n * \\param use_equiangular_map Toggles the equiangular map of the Wedge map.\n * \\param use_half_wedges When `true`, the wedges in the +z,-z,+y,-y directions\n * are cut in half along their xi-axes. The resulting ten CoordinateMaps are\n * used for the outermost Blocks of the BBH Domain.\n * \\param radial_partitioning Specifies the radial boundaries of sub-shells\n * between `inner_radius` and `outer_radius`. If the inner and outer\n * sphericities are different, the innermost shell does the transition.\n * \\param radial_distribution Select the radial distribution of grid points in\n * the spherical shells.\n * \\param which_wedges Select a subset of wedges.\n * \\param opening_angle sets the combined opening angle of the two half wedges\n * that open up along the y-z plane. The endcap wedges are then given an angle\n * of pi minus this opening angle. This parameter only has an effect if\n * `use_half_wedges` is set to `true`.\n */\nstd::vector<domain::CoordinateMaps::Wedge<3>> sph_wedge_coordinate_maps(\n    double inner_radius, double outer_radius, double inner_sphericity,\n    double outer_sphericity, bool use_equiangular_map,\n    const std::optional<std::pair<double, std::array<double, 3>>>&\n        offset_options = std::nullopt,\n    bool use_half_wedges = false,\n    const std::vector<double>& radial_partitioning = {},\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution = {domain::CoordinateMaps::Distribution::Linear},\n    ShellWedges which_wedges = ShellWedges::All, double opening_angle = M_PI_2);\n\n/// \\ingroup ComputationalDomainGroup\n/// These are the ten Frustums used in the DomainCreators for binary compact\n/// objects. The Frustums partition the volume defined by two bounding\n/// surfaces: The inner surface is the surface of the two joined inner cubes\n/// enveloping the two compact objects, while the outer is the surface of the\n/// outer cube.\n///\n/// When the sphericity is 0, the \\p length_inner_cube must be less than $1/2$\n/// \\p length_outer_cube while when the sphericity is 1 it must be less than\n/// $\\sqrt{3}/2$ \\p length_outer_cube.\n///\n/// \\param length_inner_cube The side length of the cubes enveloping the two\n/// shells.\n/// \\param length_outer_cube The side length of the outer cube.\n/// \\param equiangular_map_at_outer Whether to apply a tangent map in the\n/// angular directions at the outer boundary.\n/// \\param equiangular_map_at_inner Whether to apply a tangent map in the\n/// angular directions at the inner boundary.\n/// \\param origin_preimage The center of the two joined inner cubes is moved\n/// away from the origin and to this point, origin_preimage.\n/// \\param radial_distribution The gridpoint distribution in the radial\n/// direction, possibly dependent on the value passed to `distribution_value`.\n/// \\param distribution_value Used by `radial_distribution`. \\see Frustum for\n/// details.\n/// \\param sphericity Determines whether the outer surface is a cube\n/// (value of 0), a sphere (value of 1) or somewhere in between.\n/// \\param opening_angle determines the gridpoint distribution used\n/// in the Frustums such that they conform to the outer sphere of Wedges with\n/// the same value for `opening_angle`.\nstd::vector<domain::CoordinateMaps::Frustum> frustum_coordinate_maps(\n    double length_inner_cube, double length_outer_cube,\n    bool equiangular_map_at_outer, bool equiangular_map_at_inner,\n    const std::array<double, 3>& origin_preimage = {{0.0, 0.0, 0.0}},\n    domain::CoordinateMaps::Distribution radial_distribution =\n        domain::CoordinateMaps::Distribution::Linear,\n    std::optional<double> distribution_value = std::nullopt,\n    double sphericity = 0.0, double opening_angle = M_PI_2);\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The corners for a domain with radial layers.\n///\n/// Generates the corners for a Domain which is made of one or more layers\n/// of Blocks fully enveloping an interior volume, e.g. Sphere.\n///\n/// \\param number_of_layers specifies how many layers of Blocks to have\n/// in the final domain.\n/// \\param include_central_block set to `true` where the interior\n/// volume is filled with a central Block, and `false` where the\n/// interior volume is left empty.\n/// \\param central_block_corners are used as seed values to generate the corners\n/// for the surrounding Blocks.\n/// \\param which_wedges can be used to exclude a subset of the wedges.\nstd::vector<std::array<size_t, 8>> corners_for_radially_layered_domains(\n    size_t number_of_layers, bool include_central_block,\n    const std::array<size_t, 8>& central_block_corners = {{1, 2, 3, 4, 5, 6, 7,\n                                                           8}},\n    ShellWedges which_wedges = ShellWedges::All);\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The corners for a domain with biradial layers.\n///\n/// Generates the corners for a BBH-like Domain which is made of one or more\n/// layers of Blocks fully enveloping two interior volumes. The\n/// `number_of_radial_layers` gives the number of layers that fully envelop\n/// each interior volume with six Blocks each. The `number_of_biradial_layers`\n/// gives the number of layers that fully envelop both volumes at once, using\n/// ten Blocks per layer as opposed to six. The `central_block_corners_lhs`\n/// are used as seed values to generate the corners for the surrounding\n/// Blocks.\nstd::vector<std::array<size_t, 8>> corners_for_biradially_layered_domains(\n    size_t number_of_radial_layers, size_t number_of_biradial_layers,\n    bool include_central_block_lhs, bool include_central_block_rhs,\n    const std::array<size_t, 8>& central_block_corners_lhs = {\n        {1, 2, 3, 4, 5, 6, 7, 8}});\n\n/// \\ingroup ComputationalDomainGroup\n/// These are the CoordinateMaps used in the Cylinder DomainCreator.\n///\n/// The `radial_partitioning` specifies the radial boundaries of sub-shells\n/// between `inner_radius` and `outer_radius`, while `partitioning_in_z`\n/// specifies the z-boundaries, splitting the cylinder into stacked\n/// 3-dimensional disks. The circularity of the shell wedges changes from 0 to 1\n/// within the innermost sub-shell.\n///\n/// Set the `radial_distribution` to select the radial distribution of grid\n/// points in the cylindrical shells. The innermost shell must have\n/// `domain::CoordinateMaps::Distribution::Linear` because it changes the\n/// circularity. The distribution along the z-axis for each circular\n/// disc is specified through `distribution_in_z`.\ntemplate <typename TargetFrame>\nauto cyl_wedge_coordinate_maps(\n    double inner_radius, double outer_radius, double lower_z_bound,\n    double upper_z_bound, bool use_equiangular_map,\n    const std::vector<double>& radial_partitioning = {},\n    const std::vector<double>& partitioning_in_z = {},\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution = {domain::CoordinateMaps::Distribution::Linear},\n    const std::vector<domain::CoordinateMaps::Distribution>& distribution_in_z =\n        {domain::CoordinateMaps::Distribution::Linear})\n    -> std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>>;\n\nenum class CylindricalDomainParityFlip { none, z_direction };\n\n/// \\ingroup ComputationalDomainGroup\n/// Same as `cyl_wedge_coordinate_maps`, but only the center square blocks,\n///\n/// If `CylindricalDomainParityFlip::z_direction` is specified, then\n/// the returned maps describe a cylinder with `lower_z_bound`\n/// corresponding to logical coordinate `upper_zeta` and `upper_z_bound`\n/// corresponding to logical coordinate `lower_zeta`, and thus the\n/// resulting maps are left-handed.\n/// `CylindricalDomainParityFlip::z_direction` is therefore useful\n/// only when composing with another map that is also left-handed, so\n/// that the composed coordinate system is right-handed.\n///\n/// Returned as a vector of the coordinate maps so that they can\n/// be composed with other maps later.\nauto cyl_wedge_coord_map_center_blocks(\n    double inner_radius, double lower_z_bound, double upper_z_bound,\n    bool use_equiangular_map, const std::vector<double>& partitioning_in_z = {},\n    const std::vector<domain::CoordinateMaps::Distribution>& distribution_in_z =\n        {domain::CoordinateMaps::Distribution::Linear},\n    CylindricalDomainParityFlip parity_flip = CylindricalDomainParityFlip::none)\n    -> std::vector<domain::CoordinateMaps::ProductOf3Maps<\n        domain::CoordinateMaps::Interval, domain::CoordinateMaps::Interval,\n        domain::CoordinateMaps::Interval>>;\n\n/// \\ingroup ComputationalDomainGroup\n/// Same as cyl_wedge_coordinate_maps, but only the surrounding wedge blocks.\n///\n/// If `CylindricalDomainParityFlip::z_direction` is specified, then\n/// the returned maps describe a cylinder with `lower_z_bound`\n/// corresponding to logical coordinate `upper_zeta` and `upper_z_bound`\n/// corresponding to logical coordinate `lower_zeta`, and thus the\n/// resulting maps are left-handed.\n/// `CylindricalDomainParityFlip::z_direction` is therefore useful\n/// only when composing with another map that is also left-handed, so\n/// that the composed coordinate system is right-handed.\n///\n/// Returned as a vector of the coordinate maps so that they can\n/// be composed with other maps later.\nauto cyl_wedge_coord_map_surrounding_blocks(\n    double inner_radius, double outer_radius, double lower_z_bound,\n    double upper_z_bound, bool use_equiangular_map, double inner_circularity,\n    const std::vector<double>& radial_partitioning = {},\n    const std::vector<double>& partitioning_in_z = {},\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution = {domain::CoordinateMaps::Distribution::Linear},\n    const std::vector<domain::CoordinateMaps::Distribution>& distribution_in_z =\n        {domain::CoordinateMaps::Distribution::Linear},\n    CylindricalDomainParityFlip parity_flip = CylindricalDomainParityFlip::none)\n    -> std::vector<domain::CoordinateMaps::ProductOf2Maps<\n        domain::CoordinateMaps::Wedge<2>, domain::CoordinateMaps::Interval>>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The corners for a cylindrical domain split into discs with radial\n/// shells.\n///\n/// Generates the corners for a Domain which is made of one or more stacked\n/// discs consisting of layers of Blocks enveloping an interior square prism.\n/// The `number_of_shells` specifies how many of these layers of Blocks to have\n/// in each disc.\n///\n/// The `number_of_discs` specifies how many discs make up the domain.\n/// The very basic cylinder with one shell and one layer serves as a base\n/// to generate the corners for subsequent shells first and discs second.\nstd::vector<std::array<size_t, 8>> corners_for_cylindrical_layered_domains(\n    size_t number_of_shells, size_t number_of_discs);\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Permutes the corner numbers of an n-cube.\n///\n/// Returns the correct ordering of global corner numbers for a rotated block\n/// in an otherwise aligned edifice of blocks, given the OrientationMap a\n/// block aligned with the edifice has relative to this one, and given the\n/// corner numbering the rotated block would have if it were aligned.\n/// This is useful in creating domains for testing purposes, e.g.\n/// RotatedIntervals, RotatedRectangles, and RotatedBricks.\ntemplate <size_t VolumeDim>\nstd::array<size_t, two_to_the(VolumeDim)> discrete_rotation(\n    const OrientationMap<VolumeDim>& orientation,\n    const std::array<size_t, two_to_the(VolumeDim)>& corners_of_aligned);\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The CoordinateMaps for a rectilinear domain of n-cubes.\n///\n/// Allows for both Affine and Equiangular maps.\ntemplate <typename TargetFrame, size_t VolumeDim>\nauto maps_for_rectilinear_domains(\n    const Index<VolumeDim>& domain_extents,\n    const std::array<std::vector<double>, VolumeDim>& block_demarcations,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude = {},\n    const std::vector<OrientationMap<VolumeDim>>& orientations_of_all_blocks =\n        {},\n    bool use_equiangular_map = false)\n    -> std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, TargetFrame, VolumeDim>>>;\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Create a rectilinear Domain of multicubes.\n///\n/// \\details Useful for constructing domains for testing non-trivially\n/// connected rectilinear domains made up of cubes. We refer to a domain of\n/// this type as an edifice. The `domain_extents` provides the size (in the\n/// number of blocks) of the initial aligned edifice to construct. The\n/// `block_indices_to_exclude` parameter is used in refining the shape of\n/// the edifice from a cube to sometime more non-trivial, such as an L-shape\n/// or the net of a tesseract. The `block_demarcations` and\n/// `use_equiangular_map` parameters determine the CoordinateMaps to be used.\n/// `orientations_of_all_blocks` contains the OrientationMap of the edifice\n/// relative to each block.\n///\n/// The `identifications` parameter is used when identifying the faces of\n/// blocks in an edifice. This is used to identify the 1D boundaries in the 2D\n/// net for a 3D cube to construct a domain with topology S2. Note: If the user\n/// wishes to rotate the blocks as well as manually identify their faces, the\n/// user must provide the PairOfFaces corresponding to the rotated corners.\ntemplate <size_t VolumeDim>\nDomain<VolumeDim> rectilinear_domain(\n    const Index<VolumeDim>& domain_extents,\n    const std::array<std::vector<double>, VolumeDim>& block_demarcations,\n    const std::vector<Index<VolumeDim>>& block_indices_to_exclude = {},\n    const std::vector<OrientationMap<VolumeDim>>& orientations_of_all_blocks =\n        {},\n    const std::array<bool, VolumeDim>& dimension_is_periodic =\n        make_array<VolumeDim>(false),\n    const std::vector<PairOfFaces>& identifications = {},\n    bool use_equiangular_map = false);\n\n/// \\ingroup ComputationalDomainGroup\n/// Iterates over the corners of a VolumeDim-dimensional cube.\ntemplate <size_t VolumeDim>\nclass VolumeCornerIterator {\n public:\n  VolumeCornerIterator() { setup_from_local_corner_number(); }\n\n  explicit VolumeCornerIterator(size_t initial_local_corner_number)\n      : local_corner_number_(initial_local_corner_number) {\n    setup_from_local_corner_number();\n  }\n  VolumeCornerIterator(\n      // The block index is also global corner\n      // index of the lowest corner of the block.\n      Index<VolumeDim> block_index, Index<VolumeDim> global_corner_extents)\n      : global_corner_number_(\n            collapsed_index(block_index, global_corner_extents)),\n        global_corner_index_(block_index),\n        global_corner_extents_(global_corner_extents) {}\n\n  void operator++() {\n    ++local_corner_number_;\n    setup_from_local_corner_number();\n  }\n\n  explicit operator bool() const {\n    return local_corner_number_ < two_to_the(VolumeDim);\n  }\n\n  size_t local_corner_number() const { return local_corner_number_; }\n\n  size_t global_corner_number() const {\n    std::array<size_t, VolumeDim> new_indices{};\n    for (size_t i = 0; i < VolumeDim; i++) {\n      gsl::at(new_indices, i) =\n          global_corner_index_[i] +\n          (gsl::at(array_sides_, i) == Side::Upper ? 1 : 0);\n    }\n    const Index<VolumeDim> interior_multi_index(new_indices);\n    return collapsed_index(interior_multi_index, global_corner_extents_);\n  }\n\n  const std::array<Side, VolumeDim>& operator()() const { return array_sides_; }\n\n  const std::array<Side, VolumeDim>& operator*() const { return array_sides_; }\n\n  const std::array<double, VolumeDim>& coords_of_corner() const {\n    return coords_of_corner_;\n  }\n\n  const std::array<Direction<VolumeDim>, VolumeDim>& directions_of_corner()\n      const {\n    return array_directions_;\n  }\n\n  void setup_from_local_corner_number() {\n    for (size_t i = 0; i < VolumeDim; i++) {\n      gsl::at(coords_of_corner_, i) =\n          2.0 * get_nth_bit(local_corner_number_, i) - 1.0;\n      gsl::at(array_sides_, i) =\n          2 * get_nth_bit(local_corner_number_, i) - 1 == 1 ? Side::Upper\n                                                            : Side::Lower;\n      gsl::at(array_directions_, i) =\n          Direction<VolumeDim>(i, gsl::at(array_sides_, i));\n    }\n  }\n\n private:\n  size_t local_corner_number_ = 0;\n  size_t global_corner_number_{std::numeric_limits<size_t>::max()};\n  Index<VolumeDim> global_corner_index_{};\n  Index<VolumeDim> global_corner_extents_{};\n  std::array<Side, VolumeDim> array_sides_ = make_array<VolumeDim>(Side::Lower);\n  std::array<Direction<VolumeDim>, VolumeDim> array_directions_{};\n  std::array<double, VolumeDim> coords_of_corner_ = make_array<VolumeDim>(-1.0);\n};\n\n/// \\ingroup ComputationalDomainGroup\n/// Iterates over the 2^(VolumeDim-1) logical corners of the face of a\n/// VolumeDim-dimensional cube in the given direction.\ntemplate <size_t VolumeDim>\nclass FaceCornerIterator {\n public:\n  explicit FaceCornerIterator(Direction<VolumeDim> direction);\n\n  void operator++() {\n    face_index_++;\n    do {\n      index_++;\n    } while (get_nth_bit(index_, direction_.dimension()) ==\n             (direction_.side() == Side::Upper ? 0 : 1));\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      corner_[i] = 2 * static_cast<int>(get_nth_bit(index_, i)) - 1;\n    }\n  }\n\n  explicit operator bool() const {\n    return face_index_ < two_to_the(VolumeDim - 1);\n  }\n\n  tnsr::I<double, VolumeDim, Frame::BlockLogical> operator()() const {\n    return corner_;\n  }\n\n  tnsr::I<double, VolumeDim, Frame::BlockLogical> operator*() const {\n    return corner_;\n  }\n\n  // Returns the value used to construct the logical corner.\n  size_t volume_index() const { return index_; }\n\n  // Returns the number of times operator++ has been called.\n  size_t face_index() const { return face_index_; }\n\n private:\n  const Direction<VolumeDim> direction_;\n  size_t index_;\n  size_t face_index_ = 0;\n  tnsr::I<double, VolumeDim, Frame::BlockLogical> corner_;\n};\n\ntemplate <size_t VolumeDim>\nFaceCornerIterator<VolumeDim>::FaceCornerIterator(\n    Direction<VolumeDim> direction)\n    : direction_(std::move(direction)),\n      index_(direction_.side() == Side::Upper\n                 ? two_to_the(direction_.dimension())\n                 : 0) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    corner_[i] = 2 * static_cast<int>(get_nth_bit(index_, i)) - 1;\n  }\n}\n\nstd::ostream& operator<<(std::ostream& os, const ShellWedges& which_wedges);\n\ntemplate <>\nstruct Options::create_from_yaml<ShellWedges> {\n  template <typename Metavariables>\n  static ShellWedges create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nShellWedges Options::create_from_yaml<ShellWedges>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Domain/ElementDistribution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/ElementDistribution.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/MinimumGridSpacing.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Structure/ZCurve.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace domain {\nnamespace {\n// \\brief Get the cost of an `Element` computed as\n// `(number of grid points) / sqrt(minimum grid spacing in Frame::Grid)`\n//\n// \\details As grid points in an `Element` increase, we expect the\n// computational cost of an `Element` to scale proportionally (if the minimum\n// grid spacing is held constant). In addition, the minimum grid spacing\n// between two points in an `Element` informs the time step that we take, where\n// the smaller the minimum spacing, the smaller time step we must take, which\n// means we expect computational work to scale inversely with the minimum grid\n// spacing.\n//\n// The reason that we use the square root of the spacing as opposed to just the\n// spacing in the denominator of the cost is that it was found experimentally\n// that using the square root yielded faster BBH simulation runtimes when using\n// local time stepping.\ntemplate <size_t Dim>\ndouble get_num_points_and_grid_spacing_cost(\n    const ElementId<Dim>& element_id, const std::vector<Block<Dim>>& blocks,\n    const std::vector<std::array<size_t, Dim>>& initial_refinement_levels,\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const Spectral::Basis i1_basis, const Spectral::Quadrature i1_quadrature) {\n  const Element<Dim> element = ::domain::create_initial_element(\n      element_id, blocks, initial_refinement_levels);\n  const Mesh<Dim> mesh = ::domain::create_initial_mesh(initial_extents, element,\n                                                       i1_basis, i1_quadrature);\n  const ElementMap<Dim, Frame::Grid> element_map{element_id,\n                                                 blocks[element_id.block_id()]};\n  const tnsr::I<DataVector, Dim, Frame::ElementLogical> logical_coords =\n      logical_coordinates(mesh);\n  const tnsr::I<DataVector, Dim, Frame::Grid> grid_coords =\n      element_map(logical_coords);\n  const double min_grid_spacing =\n      minimum_grid_spacing(mesh.extents(), grid_coords);\n\n  return mesh.number_of_grid_points() / sqrt(min_grid_spacing);\n}\n}  //  namespace\n\nstd::ostream& operator<<(std::ostream& os, ElementWeight weight) {\n  switch (weight) {\n    case ElementWeight::Uniform:\n      return os << \"Uniform\";\n    case ElementWeight::NumGridPoints:\n      return os << \"NumGridPoints\";\n    case ElementWeight::NumGridPointsAndGridSpacing:\n      return os << \"NumGridPointsAndGridSpacing\";\n    default:\n      ERROR(\"Unknown ElementWeight type\");\n  }\n}\n\ntemplate <size_t Dim>\nstd::unordered_map<ElementId<Dim>, double> get_element_costs(\n    const std::vector<Block<Dim>>& blocks,\n    const std::vector<std::array<size_t, Dim>>& initial_refinement_levels,\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const ElementWeight element_weight,\n    const std::optional<Spectral::Basis>& i1_basis,\n    const std::optional<Spectral::Quadrature>& i1_quadrature) {\n  std::unordered_map<ElementId<Dim>, double> element_costs{};\n\n  for (size_t block_number = 0; block_number < blocks.size(); block_number++) {\n    const auto& block = blocks[block_number];\n    const auto initial_ref_levs = initial_refinement_levels[block_number];\n    const std::vector<ElementId<Dim>> element_ids =\n        initial_element_ids(block.id(), initial_ref_levs);\n    const size_t grid_points_per_element = alg::accumulate(\n        initial_extents[block_number], 1_st, std::multiplies<size_t>());\n\n    for (const auto& element_id : element_ids) {\n      if (element_weight == ElementWeight::Uniform) {\n        element_costs.insert({element_id, 1.0});\n      } else if (element_weight == ElementWeight::NumGridPoints) {\n        element_costs.insert({element_id, grid_points_per_element});\n      } else {\n        ASSERT(element_weight == ElementWeight::NumGridPointsAndGridSpacing,\n               \"Unknown element_weight\");\n        ASSERT(i1_basis.has_value() and i1_quadrature.has_value(),\n               \"Since element_weight is \"\n               \"ElementWeight::NumGridPointsAndGridSpacing, i1_basis and \"\n               \"i1_quadrature must have values\");\n\n        element_costs.insert(\n            {element_id,\n             get_num_points_and_grid_spacing_cost(\n                 element_id, blocks, initial_refinement_levels, initial_extents,\n                 i1_basis.value(), i1_quadrature.value())});\n      }\n    }\n  }\n\n  return element_costs;\n}\n\ntemplate <size_t Dim>\nBlockZCurveProcDistribution<Dim>::BlockZCurveProcDistribution(\n    const std::unordered_map<ElementId<Dim>, double>& element_costs,\n    const size_t number_of_procs_with_elements,\n    const std::vector<Block<Dim>>& blocks,\n    const std::vector<std::array<size_t, Dim>>& initial_refinement_levels,\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const std::unordered_set<size_t>& global_procs_to_ignore) {\n  const size_t num_blocks = blocks.size();\n\n  ASSERT(\n      number_of_procs_with_elements > 0,\n      \"Must have a non-zero number of processors to distribute elements to.\");\n  ASSERT(num_blocks > 0, \"Must have a non-zero number of blocks.\");\n  ASSERT(\n      initial_refinement_levels.size() == num_blocks,\n      \"`initial_refinement_levels` is not the same size as number of blocks\");\n  ASSERT(initial_extents.size() == num_blocks,\n         \"`initial_extents` is not the same size as number of blocks\");\n\n  size_t num_elements = 0;\n  std::vector<size_t> num_elements_by_block(num_blocks);\n  for (size_t i = 0; i < num_blocks; i++) {\n    const size_t num_elements_current_block = two_to_the(alg::accumulate(\n        initial_refinement_levels[i], 0_st, std::plus<size_t>()));\n    num_elements_by_block[i] = num_elements_current_block;\n    num_elements += num_elements_current_block;\n  }\n\n  ASSERT(element_costs.size() == num_elements,\n         \"`element_costs` is not the same size as the total number of elements \"\n         \"computed from `initial_refinement_levels`\");\n\n  block_element_distribution_ =\n      std::vector<std::vector<std::pair<size_t, size_t>>>(num_blocks);\n\n  std::vector<std::vector<ElementId<Dim>>> initial_element_ids_by_block(\n      num_blocks);\n  for (size_t i = 0; i < num_blocks; i++) {\n    initial_element_ids_by_block[i].reserve(num_elements_by_block[i]);\n    initial_element_ids_by_block[i] =\n        initial_element_ids(blocks[i].id(), initial_refinement_levels[i]);\n    alg::sort(initial_element_ids_by_block[i],\n              [](const ElementId<Dim>& lhs, const ElementId<Dim>& rhs) {\n                return z_curve_index(lhs) < z_curve_index(rhs);\n              });\n  }\n\n  double total_cost = 0.0;\n  for (const auto& element_id_and_cost : element_costs) {\n    total_cost += element_id_and_cost.second;\n  }\n\n  size_t current_block_num = 0;\n  size_t element_num_of_block = 0;\n  double cost_remaining = total_cost;\n  size_t number_of_ignored_procs_so_far = 0;\n  // distribute Elements to all but the final proc\n  for (size_t i = 0; i < number_of_procs_with_elements - 1; ++i) {\n    size_t global_proc_number = i + number_of_ignored_procs_so_far;\n    while (global_procs_to_ignore.find(global_proc_number) !=\n           global_procs_to_ignore.end()) {\n      ++number_of_ignored_procs_so_far;\n      ++global_proc_number;\n    }\n\n    // The target cost per proc is updated as we distribute to each proc since\n    // the total cost on a proc will nearly never be exactly the target average.\n    // If we don't adjust the target cost, then we risk either not using all\n    // procs (from overshooting the average too much on multiple procs) or\n    // piling up cost on the last proc (from undershooting the average on\n    // multiple procs). Updating the target cost per proc keeps the total cost\n    // spread somewhat evenly to each proc.\n    double target_cost_per_proc =\n        cost_remaining / static_cast<double>(number_of_procs_with_elements - i);\n    double cost_spent_on_proc = 0.0;\n    size_t total_elements_distributed_to_proc = 0;\n    bool add_more_elements_to_proc = true;\n    // while we haven't yet distributed all blocks and we still have cost\n    // allowed on the proc\n    while (add_more_elements_to_proc and (current_block_num < num_blocks)) {\n      const size_t num_elements_current_block =\n          num_elements_by_block[current_block_num];\n      size_t num_elements_distributed_to_proc = 0;\n      // while we still have elements left on the block to distribute and we\n      // still have cost allowed on the proc\n      while (add_more_elements_to_proc and\n             (element_num_of_block < num_elements_current_block)) {\n        const ElementId<Dim>& element_id =\n            initial_element_ids_by_block[current_block_num]\n                                        [element_num_of_block];\n        const double element_cost = element_costs.at(element_id);\n\n        if (total_elements_distributed_to_proc == 0) {\n          // if we haven't yet assigned any elements to this proc, assign the\n          // current element to the current proc to ensure it gets at least one\n          // element\n          cost_remaining -= element_cost;\n          cost_spent_on_proc = element_cost;\n          num_elements_distributed_to_proc = 1;\n          total_elements_distributed_to_proc = 1;\n          element_num_of_block++;\n        } else {\n          const double current_cost_diff =\n              abs(target_cost_per_proc - cost_spent_on_proc);\n          const double next_cost_diff =\n              abs(target_cost_per_proc - (cost_spent_on_proc + element_cost));\n\n          if (current_cost_diff <= next_cost_diff) {\n            // if the current proc cost is closer to the target cost than if we\n            // were to add one more element, then we're done adding elements to\n            // this proc and don't add the current one\n            add_more_elements_to_proc = false;\n          } else {\n            // otherwise, the current proc cost is farther from the target then\n            // if we were to add one more element, so we add the current element\n            // to the current proc\n            cost_spent_on_proc += element_cost;\n            cost_remaining -= element_cost;\n            num_elements_distributed_to_proc++;\n            total_elements_distributed_to_proc++;\n            element_num_of_block++;\n          }\n        }\n      }\n\n      // add a proc and its element allowance for the current block\n      block_element_distribution_.at(current_block_num)\n          .emplace_back(std::make_pair(global_proc_number,\n                                       num_elements_distributed_to_proc));\n      if (element_num_of_block >= num_elements_current_block) {\n        // if we're done assigning elements from the current block, move on to\n        // the elements in the next block\n        ++current_block_num;\n        element_num_of_block = 0;\n      }\n    }\n  }\n\n  // distribute all remaining Elements on the final proc\n\n  size_t global_proc_number =\n      number_of_procs_with_elements - 1 + number_of_ignored_procs_so_far;\n  while (global_procs_to_ignore.find(global_proc_number) !=\n         global_procs_to_ignore.end()) {\n    ++global_proc_number;\n  }\n\n  // distribute remaining Elements of Block we left off on\n  if (current_block_num < num_blocks) {\n    block_element_distribution_.at(current_block_num)\n        .emplace_back(std::make_pair(\n            global_proc_number,\n            num_elements_by_block[current_block_num] - element_num_of_block));\n  }\n\n  // distribute any Blocks that still remain after the Block we left off on\n  current_block_num++;\n  while (current_block_num < num_blocks) {\n    const size_t num_elements_current_block =\n        num_elements_by_block[current_block_num];\n    block_element_distribution_.at(current_block_num)\n        .emplace_back(\n            std::make_pair(global_proc_number, num_elements_current_block));\n    current_block_num++;\n  }\n}\n\ntemplate <size_t Dim>\nsize_t BlockZCurveProcDistribution<Dim>::get_proc_for_element(\n    const ElementId<Dim>& element_id) const {\n  const size_t element_order_index = z_curve_index(element_id);\n  size_t total_so_far = 0;\n  for (const std::pair<size_t, size_t>& element_info :\n       gsl::at(block_element_distribution_, element_id.block_id())) {\n    if (total_so_far <= element_order_index and\n        element_info.second + total_so_far > element_order_index) {\n      return element_info.first;\n    }\n    total_so_far += element_info.second;\n  }\n  ERROR(\n      \"Processor not successfully chosen. This indicates a flaw in the logic \"\n      \"of BlockZCurveProcDistribution.\");\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                               \\\n  template class BlockZCurveProcDistribution<GET_DIM(data)>;                 \\\n  double get_num_points_and_grid_spacing_cost(                               \\\n      const ElementId<GET_DIM(data)>& element_id,                            \\\n      const std::vector<Block<GET_DIM(data)>>& blocks,                       \\\n      const std::vector<std::array<size_t, GET_DIM(data)>>&                  \\\n          initial_refinement_levels,                                         \\\n      const std::vector<std::array<size_t, GET_DIM(data)>>& initial_extents, \\\n      Spectral::Basis i1_basis, Spectral::Quadrature i1_quadrature);         \\\n  template std::unordered_map<ElementId<GET_DIM(data)>, double>              \\\n  get_element_costs(                                                         \\\n      const std::vector<Block<GET_DIM(data)>>& blocks,                       \\\n      const std::vector<std::array<size_t, GET_DIM(data)>>&                  \\\n          initial_refinement_levels,                                         \\\n      const std::vector<std::array<size_t, GET_DIM(data)>>& initial_extents, \\\n      ElementWeight element_weight,                                          \\\n      const std::optional<Spectral::Basis>& i1_basis,                        \\\n      const std::optional<Spectral::Quadrature>& i1_quadrature);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/ElementDistribution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/TypeTraits/CreateGetStaticMemberVariableOrDefault.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Block;\n\ntemplate <size_t Dim>\nclass ElementId;\n\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace domain {\n/// The weighting scheme for assigning computational costs to `Element`s for\n/// distributing balanced compuational costs per processor (see\n/// `BlockZCurveProcDistribution`)\nenum class ElementWeight {\n  /// A weighting scheme where each `Element` is assigned the same computational\n  /// cost\n  Uniform,\n  /// A weighting scheme where each `Element`'s computational cost is equal to\n  /// the number of grid points in that `Element`\n  NumGridPoints,\n  /// A weighting scheme where each `Element`'s computational cost is weighted\n  /// by both the number of grid points and minimum spacing between grid points\n  /// in that `Element` (see `get_num_points_and_grid_spacing_cost()` for\n  /// details)\n  NumGridPointsAndGridSpacing\n};\n\nstd::ostream& operator<<(std::ostream& os, ElementWeight weight);\n\n/// \\brief Get the cost of each `Element` in a list of `Block`s where\n/// `element_weight` specifies which weight distribution scheme to use\n///\n/// \\details It is only necessary to pass in a value for `i1_basis` and\n/// `i1_quadrature` if the value for `element_weight` is\n/// `ElementWeight::NumGridPointsAndGridSpacing`. Otherwise, the argument isn't\n/// needed and will have no effect if it does have a value.\ntemplate <size_t Dim>\nstd::unordered_map<ElementId<Dim>, double> get_element_costs(\n    const std::vector<Block<Dim>>& blocks,\n    const std::vector<std::array<size_t, Dim>>& initial_refinement_levels,\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    ElementWeight element_weight,\n    const std::optional<Spectral::Basis>& i1_basis,\n    const std::optional<Spectral::Quadrature>& i1_quadrature);\n\n/*!\n * \\brief Distribution strategy for assigning elements to CPUs using a\n * Morton ('Z-order') space-filling curve to determine placement within each\n * block, where `Element`s are distributed across CPUs\n *\n * \\details The element distribution attempts to assign a balanced total\n * computational cost to each processor that is allowed to have `Element`s.\n * First, each `Block`'s `Element`s are ordered by their Z-curve index (see more\n * below). `Element`s are traversed in this order and assigned to CPUs in order,\n * moving onto the next CPU once the target cost per CPU is met. The target cost\n * per CPU is defined as the remaining cost to distribute divided by the\n * remaining number of CPUs to distribute to. This is an important distinction\n * from simply having one constant target cost per CPU defined as the total cost\n * divided by the total number of CPUs with elements. Since the total cost of\n * `Element`s on a processor will nearly never add up to be exactly the average\n * cost per CPU, this means that we would either have to decide to overshoot or\n * undershoot the average as we iterate over the CPUs and assign `Element`s. If\n * we overshoot the average on each processor, the final processor could have a\n * much lower cost than the rest of the processors and we run the risk of\n * overshooting so much that one or more of the requested processors don't get\n * assigned any `Element`s at all. If we undershoot the average on each\n * processor, the final processor could have a much higher cost than the others\n * due to remainder cost piling up. This algorithm avoids these risks by instead\n * adjusting the target cost per CPU as we finish assigning cost to previous\n * CPUs.\n *\n * Morton curves are a simple and easily-computed space-filling curve that\n * (unlike Hilbert curves) permit diagonal traversal. See, for instance,\n * \\cite Borrell2018 for a discussion of mesh partitioning using space-filling\n * curves.\n * A concrete example of the use of a Morton curve in 2d is given below.\n *\n * A sketch of a 2D block with 4x2 elements, with each element labeled according\n * to the order on the Morton curve:\n * ```\n *          x-->\n *          0   1   2   3\n *        ----------------\n *  y  0 |  0   2   4   6\n *  |    |  | / | / | / |\n *  v  1 |  1   3   5   7\n * ```\n * (forming a zig-zag path, that under some rotation/reflection has a 'Z'\n * shape).\n *\n * The Morton curve method is a quick way of getting acceptable spatial locality\n * -- usually, for approximately even distributions, it will ensure that\n * elements are assigned in large volume chunks, and the structure of the Morton\n * curve ensures that for a given processor and block, the elements will be\n * assigned in no more than two orthogonally connected clusters. In principle, a\n * Hilbert curve could potentially improve upon the gains obtained by this class\n * by guaranteeing that all elements within each block form a single\n * orthogonally connected cluster.\n *\n * The assignment of portions of blocks to processors may use partial blocks,\n * and/or multiple blocks to ensure an even distribution of elements to\n * processors.\n * We currently make no distinction between dividing elements between processors\n * within a node and dividing elements between processors across nodes. The\n * current technique aims to have a simple method of reducing communication\n * globally, though it would likely be more efficient to prioritize minimization\n * of inter-node communication, because communication across interconnects is\n * the primary cost of communication in charm++ runs.\n *\n * \\warning The use of the Morton curve to generate a well-clustered element\n * distribution currently assumes that the refinement is uniform over each\n * block, with no internal structure that would be generated by, for instance\n * AMR.\n * This distribution method will need alteration to perform well for blocks with\n * internal structure from h-refinement. Morton curves can be defined\n * recursively, so a generalization of the present method is possible for blocks\n * with internal refinement\n *\n * \\tparam Dim the number of spatial dimensions of the `Block`s\n */\ntemplate <size_t Dim>\nstruct BlockZCurveProcDistribution {\n  BlockZCurveProcDistribution() = default;\n\n  /// The `number_of_procs_with_elements` argument represents how many procs\n  /// will have elements. This is not necessarily equal to the total number of\n  /// procs because some global procs may be ignored by the sixth argument\n  /// `global_procs_to_ignore`.\n  BlockZCurveProcDistribution(\n      const std::unordered_map<ElementId<Dim>, double>& element_costs,\n      size_t number_of_procs_with_elements,\n      const std::vector<Block<Dim>>& blocks,\n      const std::vector<std::array<size_t, Dim>>& initial_refinement_levels,\n      const std::vector<std::array<size_t, Dim>>& initial_extents,\n      const std::unordered_set<size_t>& global_procs_to_ignore = {});\n\n  /// Gets the suggested processor number for a particular `ElementId`,\n  /// determined by the Morton curve weighted element assignment described in\n  /// detail in the parent class documentation.\n  size_t get_proc_for_element(const ElementId<Dim>& element_id) const;\n\n  const std::vector<std::vector<std::pair<size_t, size_t>>>&\n  block_element_distribution() const {\n    return block_element_distribution_;\n  }\n\n private:\n  // in this nested data structure:\n  // - The block id is the first index\n  // - There is an arbitrary number of CPUs per block, each with an element\n  //   allowance\n  // - Each element allowance is represented by a pair of proc number, number of\n  //   elements in the allowance\n  std::vector<std::vector<std::pair<size_t, size_t>>>\n      block_element_distribution_;\n};\n}  // namespace domain\n\nnamespace element_weight_detail {\nCREATE_GET_STATIC_MEMBER_VARIABLE_OR_DEFAULT(local_time_stepping)\n}  // namespace element_weight_detail\n\ntemplate <>\nstruct Options::create_from_yaml<domain::ElementWeight> {\n  template <typename Metavariables>\n  static domain::ElementWeight create(const Options::Option& options) {\n    const auto ordering = options.parse_as<std::string>();\n    if (ordering == \"Uniform\") {\n      return domain::ElementWeight::Uniform;\n    } else if (ordering == \"NumGridPoints\") {\n      return domain::ElementWeight::NumGridPoints;\n    } else if (ordering == \"NumGridPointsAndGridSpacing\") {\n      if constexpr (not element_weight_detail::\n                        get_local_time_stepping_or_default_v<Metavariables,\n                                                             false>) {\n        PARSE_ERROR(\n            options.context(),\n            \"When not using local time stepping, you cannot use \"\n            \"NumGridPointsAndGridSpacing for the element distribution. Please \"\n            \"choose another element distribution.\");\n      }\n      return domain::ElementWeight::NumGridPointsAndGridSpacing;\n    }\n    PARSE_ERROR(options.context(),\n                \"ElementWeight must be 'Uniform', 'NumGridPoints', or, \"\n                \"'NumGridPointsAndGridSpacing'\");\n  }\n};\n"
  },
  {
    "path": "src/Domain/ElementLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n\n#include <array>\n#include <map>\n#include <memory>\n#include <optional>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/IdPair.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Structure/BlockId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\ntemplate <size_t Dim>\nstd::optional<tnsr::I<double, Dim, Frame::ElementLogical>>\nelement_logical_coordinates(\n    const tnsr::I<double, Dim, Frame::BlockLogical>& x_block_logical,\n    const ElementId<Dim>& element_id) {\n  tnsr::I<double, Dim, Frame::ElementLogical> x_element_logical{};\n  for (size_t d = 0; d < Dim; ++d) {\n    // Check if the point is outside the element\n    if (element_id.segment_id(d).refinement_level() == 0) {\n      // Root segment in this dimension (map is identity)\n      x_element_logical.get(d) = x_block_logical.get(d);\n      continue;\n    }\n    const double up = element_id.segment_id(d).endpoint(Side::Upper);\n    const double lo = element_id.segment_id(d).endpoint(Side::Lower);\n    if (x_block_logical.get(d) < lo or x_block_logical.get(d) > up) {\n      return std::nullopt;\n    }\n    // Map to element logical coords\n    x_element_logical.get(d) =\n        (2.0 * x_block_logical.get(d) - up - lo) / (up - lo);\n  }\n  return x_element_logical;\n}\n\ntemplate <size_t Dim>\nstd::optional<\n    std::pair<ElementId<Dim>, tnsr::I<double, Dim, Frame::ElementLogical>>>\nelement_logical_coordinates(\n    const IdPair<domain::BlockId, tnsr::I<double, Dim, Frame::BlockLogical>>&\n        block_logical_coords,\n    const std::map<size_t, domain::ElementSearchTree<Dim>>& search_tree) {\n  const auto block_search_tree_it =\n      search_tree.find(block_logical_coords.id.get_index());\n  if (block_search_tree_it == search_tree.end()) {\n    return std::nullopt;\n  }\n  const auto& block_search_tree = block_search_tree_it->second;\n  const auto found_element_id =\n      block_search_tree.begin_covers(block_logical_coords.data);\n  if (found_element_id == block_search_tree.end_covers()) {\n    return std::nullopt;\n  }\n  const auto& element_id = *found_element_id;\n  return std::make_pair(element_id, element_logical_coordinates(\n                                        block_logical_coords.data, element_id)\n                                        .value());\n}\n\nnamespace {\n// The segments bounds are binary fractions (i.e. the numerator is an\n// integer and the denominator is a power of 2) so these floating point\n// comparisons should be safe from roundoff problems\n// Need to return true if on upper face of block\n// Otherwise return true if point is within the segment or on the lower bound\nbool segment_contains(const double x_block_logical,\n                      const double lower_bound_block_logical,\n                      const double upper_bound_block_logical) {\n  if (UNLIKELY(x_block_logical == upper_bound_block_logical)) {\n    return (upper_bound_block_logical == 1.0);\n  }\n  return (x_block_logical >= lower_bound_block_logical and\n          x_block_logical < upper_bound_block_logical);\n}\n}  // namespace\n\ntemplate <size_t Dim>\nstd::unordered_map<ElementId<Dim>, ElementLogicalCoordHolder<Dim>>\nelement_logical_coordinates(\n    const std::vector<ElementId<Dim>>& element_ids,\n    const std::vector<BlockLogicalCoords<Dim>>& block_coord_holders) {\n  // Temporarily put results here in data structures that allow\n  // push_back, because we don't know the sizes of the output\n  // DataVectors ahead of time.\n  std::vector<std::array<std::vector<double>, Dim>> x_element_logical(\n      element_ids.size());\n  std::vector<std::vector<size_t>> offsets(element_ids.size());\n\n  // Loop over points\n  for (size_t offset = 0; offset < block_coord_holders.size(); ++offset) {\n    // Skip points that are not in any block.\n    if (not block_coord_holders[offset].has_value()) {\n      continue;\n    }\n\n    const auto& block_id = block_coord_holders[offset].value().id;\n    const auto& x_block_logical = block_coord_holders[offset].value().data;\n    // Need to loop over elements, because the block doesn't know\n    // things like the refinement_level of each element.\n    for (size_t index = 0; index < element_ids.size(); ++index) {\n      const auto& element_id = element_ids[index];\n      if (element_id.block_id() == block_id.get_index()) {\n        // This element is in this block; now check if the point is in\n        // this element.\n        const auto x_elem =\n            element_logical_coordinates(x_block_logical, element_id);\n        if (not x_elem.has_value()) {\n          continue;\n        }\n        // Disambiguate points on shared element boundaries\n        bool is_contained = true;\n        for (size_t d = 0; d < Dim; ++d) {\n          if (element_id.segment_id(d).refinement_level() == 0) {\n            // Don't need to check bounds as segment is root segment of the\n            // block\n            continue;\n          }\n          const double up = element_id.segment_id(d).endpoint(Side::Upper);\n          const double lo = element_id.segment_id(d).endpoint(Side::Lower);\n          const double x_block_log = x_block_logical.get(d);\n          if (not segment_contains(x_block_log, lo, up)) {\n            is_contained = false;\n            break;\n          }\n        }\n        if (is_contained) {\n          for (size_t d = 0; d < Dim; ++d) {\n            gsl::at(x_element_logical[index], d).push_back(x_elem->get(d));\n          }\n          offsets[index].push_back(offset);\n          // Found a matching element, so we don't need to check other\n          // elements.\n          break;\n        }\n      }\n    }\n  }\n\n  // Now we know how many points are in each element, so we can\n  // put the intermediate results into the final data structure.\n  std::unordered_map<ElementId<Dim>, ElementLogicalCoordHolder<Dim>> result;\n  for (size_t index = 0; index < element_ids.size(); ++index) {\n    const size_t num_grid_pts = x_element_logical[index][0].size();\n    if (num_grid_pts > 0) {\n      tnsr::I<DataVector, Dim, Frame::ElementLogical> tmp(num_grid_pts);\n      std::vector<size_t> off(num_grid_pts);\n      for (size_t s = 0; s < num_grid_pts; ++s) {\n        for (size_t d = 0; d < Dim; ++d) {\n          tmp.get(d)[s] = gsl::at(x_element_logical[index], d)[s];\n        }\n        off[s] = offsets[index][s];\n      }\n      result.emplace(element_ids[index], ElementLogicalCoordHolder<Dim>{\n                                             std::move(tmp), std::move(off)});\n    }\n  }\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template std::optional<tnsr::I<double, DIM(data), Frame::ElementLogical>>   \\\n  element_logical_coordinates(                                                \\\n      const tnsr::I<double, DIM(data), Frame::BlockLogical>& x_block_logical, \\\n      const ElementId<DIM(data)>& element_id);                                \\\n  template std::optional<                                                     \\\n      std::pair<ElementId<DIM(data)>,                                         \\\n                tnsr::I<double, DIM(data), Frame::ElementLogical>>>           \\\n  element_logical_coordinates(                                                \\\n      const IdPair<domain::BlockId,                                           \\\n                   tnsr::I<double, DIM(data), Frame::BlockLogical>>&          \\\n          block_logical_coords,                                               \\\n      const std::map<size_t, domain::ElementSearchTree<DIM(data)>>&           \\\n          search_tree);                                                       \\\n  template std::unordered_map<ElementId<DIM(data)>,                           \\\n                              ElementLogicalCoordHolder<DIM(data)>>           \\\n  element_logical_coordinates(                                                \\\n      const std::vector<ElementId<DIM(data)>>& element_ids,                   \\\n      const std::vector<BlockLogicalCoords<DIM(data)>>& block_coord_holders);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Domain/ElementLogicalCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Structure/ElementSearchTree.hpp\"\n\n/// \\cond\nnamespace domain {\nclass BlockId;\n}  // namespace domain\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <typename IdType, typename DataType>\nclass IdPair;\n/// \\endcond\n\n/*!\n * \\brief Map block logical coordinates to element logical coordinates\n *\n * If the point is outside the element, returns std::nullopt. Otherwise, returns\n * the element logical coordinates of the point.\n *\n * Points on the element boundary are considered to be in the element and will\n * have element logical coordinates of -1 or 1. See the other function overload\n * for handling multiple points and disambiguating points on shared element\n * boundaries.\n */\ntemplate <size_t Dim>\nstd::optional<tnsr::I<double, Dim, Frame::ElementLogical>>\nelement_logical_coordinates(\n    const tnsr::I<double, Dim, Frame::BlockLogical>& x_block_logical,\n    const ElementId<Dim>& element_id);\n\n/*!\n * \\brief Map block logical coordinates to element logical coordinates using an\n * indexed set of elements\n *\n * The indexing is done by the `domain::index_element_ids` function, which\n * places element IDs into a search tree for fast lookup. If the point is on a\n * shared element boundary then no guarantee is made which of the abutting\n * element IDs is returned (the returned element ID is deterministic but depends\n * on the order in which the elements were inserted into the search tree). See\n * the other function overload for handling multiple points and disambiguating\n * points on shared element boundaries.\n *\n * \\param block_logical_coords the block logical coordinates of the point\n * \\param search_tree the result of `domain::index_element_ids`\n */\ntemplate <size_t Dim>\nstd::optional<\n    std::pair<ElementId<Dim>, tnsr::I<double, Dim, Frame::ElementLogical>>>\nelement_logical_coordinates(\n    const IdPair<domain::BlockId, tnsr::I<double, Dim, Frame::BlockLogical>>&\n        block_logical_coords,\n    const std::map<size_t, domain::ElementSearchTree<Dim>>& search_tree);\n\n/// \\ingroup ComputationalDomainGroup\n///\n/// Holds element logical coordinates of an arbitrary set of points on\n/// a single `Element`.  The arbitrary set of points is assumed to be\n/// a subset of a larger set of points spanning multiple `Element`s,\n/// and this class holds `offsets` that index into that larger set of\n/// points.\n///\n/// \\details `offsets.size()` is the same as the size of the `DataVector`\n/// inside `element_logical_coords`.\n///\n/// This is used during the process of interpolating volume quantities\n/// on the `Elements` (e.g. the spatial metric) onto an arbitrary set\n/// of points (e.g. the points on an apparent horizon or a\n/// wave-extraction surface) expressed in some frame.  Here is an\n/// outline of how this interpolation proceeds, and where\n/// `element_logical_coordinates` and `block_logical_coordinates` fit\n/// into the picture:\n///\n/// Assume some component (e.g. HorizonA) has a `Tensor<DataVector>`\n/// of target coordinate points in some coordinate frame.  The goal is\n/// to determine the `Element` and logical coordinates of each point,\n/// have each `Element` interpolate volume data onto the points\n/// contained inside that `Element`, and send the interpolated data\n/// back to the component.  The first step of this process is to\n/// determine the block_id and block_logical_coordinates of each\n/// point; this is done by the component (e.g. HorizonA), which calls\n/// the function `block_logical_coordinates` on its full set of target\n/// points.  The result of `block_logical_coordinates` is then\n/// communicated to the members of a NodeGroup component\n/// (e.g. HorizonManager).  Each node of the NodeGroup then calls\n/// `element_logical_coordinates`, which returns a map of `ElementId`\n/// to `ElementLogicalCoordHolder` for all the `Element`s on that node\n/// that contain one or more of the target points. The NodeGroup\n/// (which already has received the volume data from the `Elements` on\n/// that node), interpolates the volume data to the element logical\n/// coordinates for all of these `ElementId`s.  The `offsets` in the\n/// `ElementLogicalCoordHolder` are the indices into the `DataVectors`\n/// of the original target coordinates and will be used to assemble\n/// the interpolated data into `Tensor<DataVector>`s that have the\n/// same ordering as the original target coordinates. The NodeGroups\n/// perform a reduction to get the data back to the original\n/// component.\ntemplate <size_t Dim>\nstruct ElementLogicalCoordHolder {\n  tnsr::I<DataVector, Dim, Frame::ElementLogical> element_logical_coords;\n  std::vector<size_t> offsets;\n};\n\n/// \\ingroup ComputationalDomainGroup\n///\n/// Given a set of points in block logical coordinates and their\n/// `BlockIds`, as returned from the function\n/// `block_logical_coordinates`, determines which `Element`s in a list\n/// of `ElementId`s contains each point, and determines the element\n/// logical coordinates of each point.\n///\n/// \\details Returns a std::unordered_map from `ElementId`s to\n/// `ElementLogicalCoordHolder`s.\n/// It is expected that only a subset of the points will be found\n/// in the given `Element`s.\n/// Boundary points: If a point is on the boundary of an Element, it is\n/// considered contained in that Element only if it is on the lower bound\n/// of the Element, or if it is on the upper bound of the element and that\n/// upper bound coincides with the upper bound of the containing Block.\n/// This means that each boundary point is contained in one and only one\n/// Element.  We assume that the input block_coord_holders associates\n/// a point on a Block boundary with only a single Block, the one with\n/// the smaller BlockId, which is always the lower-bounding Block.\n///\n/// \\code\n///  <---    Block 0   ---> <---   Block 1   --->\n///  |          |          |          |          |\n/// P_0   E0   P_1   E1   P_2   E2   P_3   E3   P_4\n///  |          |          |          |          |\n///\n/// For example, the above 1D diagram shows four Elements labeled E0\n/// through E3, and five boundary points labeled P_0 through P_4 (where\n/// P_0 and P_4 are external boundaries).  There are two Blocks.  This\n/// algorithm assigns each boundary point to one and only one Element as\n/// follows:\n/// P_0 -> E0\n/// P_1 -> E1\n/// P_2 -> E1 (Note: block_coord_holders includes P_2 only in Block 0)\n/// P_3 -> E3\n/// P_4 -> E3\n/// \\endcode\ntemplate <size_t Dim>\nauto element_logical_coordinates(\n    const std::vector<ElementId<Dim>>& element_ids,\n    const std::vector<BlockLogicalCoords<Dim>>& block_coord_holders)\n    -> std::unordered_map<ElementId<Dim>, ElementLogicalCoordHolder<Dim>>;\n"
  },
  {
    "path": "src/Domain/ElementMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/ElementMap.hpp\"\n\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/OptimizerHacks.hpp\"\n\ntemplate <size_t Dim, typename TargetFrame>\nElementMap<Dim, TargetFrame>::ElementMap(\n    const ElementId<Dim>& element_id,\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, Dim>>\n        block_map)\n    : block_map_(std::move(block_map)),\n      map_slope_{[](const ElementId<Dim>& id) {\n        std::array<double, Dim> result{};\n        for (size_t d = 0; d < Dim; ++d) {\n          gsl::at(result, d) = 0.5 * (id.segment_id(d).endpoint(Side::Upper) -\n                                      id.segment_id(d).endpoint(Side::Lower));\n        }\n        return result;\n      }(element_id)},\n      map_offset_{[](const ElementId<Dim>& id) {\n        std::array<double, Dim> result{};\n        for (size_t d = 0; d < Dim; ++d) {\n          // The clang optimizer appears to generate code to execute\n          // this loop extra times and then throw away the results.\n          VARIABLE_CAUSES_CLANG_FPE(d);\n          gsl::at(result, d) = 0.5 * (id.segment_id(d).endpoint(Side::Upper) +\n                                      id.segment_id(d).endpoint(Side::Lower));\n        }\n        return result;\n      }(element_id)} {}\n\n// We could refactor the ElementMap class to use a `Composition` internally also\n// for the element-to-block-logical map, so the whole map is just one\n// composition.\ntemplate <size_t Dim, typename TargetFrame>\nElementMap<Dim, TargetFrame>::ElementMap(const ElementId<Dim>& element_id,\n                                         const Block<Dim>& block)\n    : ElementMap(\n          element_id,\n          [&block]() -> std::unique_ptr<domain::CoordinateMapBase<\n                         Frame::BlockLogical, TargetFrame, Dim>> {\n            if constexpr (std::is_same_v<TargetFrame, Frame::Inertial>) {\n              if (block.is_time_dependent()) {\n                using CompositionType = domain::CoordinateMaps::Composition<\n                    tmpl::list<Frame::BlockLogical, Frame::Grid,\n                               Frame::Inertial>,\n                    Dim>;\n                return std::make_unique<CompositionType>(\n                    block.moving_mesh_logical_to_grid_map().get_clone(),\n                    block.moving_mesh_grid_to_inertial_map().get_clone());\n              } else {\n                return block.stationary_map().get_clone();\n              }\n            } else if constexpr (std::is_same_v<TargetFrame, Frame::Grid>) {\n              if (block.is_time_dependent()) {\n                return block.moving_mesh_logical_to_grid_map().get_clone();\n              } else {\n                return block.stationary_map().get_to_grid_frame();\n              }\n            }\n          }()) {\n  ASSERT(element_id.block_id() == block.id(),\n         \"Element \" << element_id << \" is not in block \" << block.id() << \".\");\n}\n\ntemplate <size_t Dim, typename TargetFrame>\nvoid ElementMap<Dim, TargetFrame>::pup(PUP::er& p) {\n  p | block_map_;\n  p | map_slope_;\n  p | map_offset_;\n}\n\n// For dual frame evolutions the ElementMap only goes to the grid frame\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data) \\\n  template class ElementMap<GET_DIM(data), GET_FRAME(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3),\n                        (Frame::Inertial, Frame::Grid))\n\n#undef GET_DIM\n#undef GET_FRAME\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/ElementMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief The CoordinateMap for the Element from the Logical frame to the\n * `TargetFrame`\n *\n * An ElementMap takes a CoordinateMap for a Block and an ElementId as input,\n * and then \"prepends\" the correct affine map to the CoordinateMap so that the\n * map corresponds to the coordinate map for the Element rather than the Block.\n * This allows DomainCreators to only specify the maps for the Blocks without\n * worrying about how the domain may be decomposed beyond that.\n */\ntemplate <size_t Dim, typename TargetFrame>\nclass ElementMap {\n public:\n  static constexpr size_t dim = Dim;\n  using source_frame = Frame::ElementLogical;\n  using target_frame = TargetFrame;\n\n  /// \\cond HIDDEN_SYMBOLS\n  ElementMap() = default;\n  /// \\endcond\n\n  ElementMap(const ElementId<Dim>& element_id,\n             std::unique_ptr<domain::CoordinateMapBase<Frame::BlockLogical,\n                                                       TargetFrame, Dim>>\n                 block_map);\n\n  /// Construct from an `element_id` within the `block`. The (affine)\n  /// ElementLogical to BlockLogical map is determined by the `element_id`. The\n  /// BlockLogical to TargetFrame map is determined by the `block`:\n  /// - If the block is time-independent: the `block.stationary_map()` is used.\n  /// - If the block is time-dependent: The `block.moving_mesh_*_map()` maps\n  ///   are used. Which maps are used depends on the TargetFrame.\n  ElementMap(const ElementId<Dim>& element_id, const Block<Dim>& block);\n\n  const domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, Dim>&\n  block_map() const {\n    return *block_map_;\n  }\n\n  template <typename T>\n  tnsr::I<T, Dim, TargetFrame> operator()(\n      const tnsr::I<T, Dim, Frame::ElementLogical>& source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const domain::FunctionsOfTimeMap& functions_of_time = {}) const {\n    auto block_source_point =\n        apply_affine_transformation_to_point(source_point);\n    return block_map_->operator()(std::move(block_source_point), time,\n                                  functions_of_time);\n  }\n\n  template <typename T>\n  tnsr::I<T, Dim, Frame::ElementLogical> inverse(\n      tnsr::I<T, Dim, TargetFrame> target_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const domain::FunctionsOfTimeMap& functions_of_time = {}) const {\n    auto block_source_point{\n        block_map_->inverse(std::move(target_point), time, functions_of_time)\n            .value()};\n    // Apply the affine map to the points\n    tnsr::I<T, Dim, Frame::ElementLogical> source_point;\n    for (size_t d = 0; d < Dim; ++d) {\n      const double inv_jac = 1.0 / gsl::at(map_slope_, d);\n      const double temp = gsl::at(map_offset_, d) * inv_jac;\n      source_point.get(d) = inv_jac * block_source_point.get(d) - temp;\n    }\n    return source_point;\n  }\n\n  template <typename T>\n  InverseJacobian<T, Dim, Frame::ElementLogical, TargetFrame> inv_jacobian(\n      const tnsr::I<T, Dim, Frame::ElementLogical>& source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const domain::FunctionsOfTimeMap& functions_of_time = {}) const {\n    auto block_source_point =\n        apply_affine_transformation_to_point(source_point);\n    auto block_inv_jac = block_map_->inv_jacobian(std::move(block_source_point),\n                                                  time, functions_of_time);\n    InverseJacobian<T, Dim, Frame::ElementLogical, TargetFrame> inv_jac;\n    for (size_t d = 0; d < Dim; ++d) {\n      const double inv_jac_in_dir = 1.0 / gsl::at(map_slope_, d);\n      for (size_t i = 0; i < Dim; ++i) {\n        inv_jac.get(d, i) = inv_jac_in_dir * block_inv_jac.get(d, i);\n      }\n    }\n    return inv_jac;\n  }\n\n  template <typename T>\n  Jacobian<T, Dim, Frame::ElementLogical, TargetFrame> jacobian(\n      const tnsr::I<T, Dim, Frame::ElementLogical>& source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const domain::FunctionsOfTimeMap& functions_of_time = {}) const {\n    auto block_source_point =\n        apply_affine_transformation_to_point(source_point);\n    auto block_jac = block_map_->jacobian(std::move(block_source_point), time,\n                                          functions_of_time);\n    Jacobian<T, Dim, Frame::ElementLogical, TargetFrame> jac;\n    for (size_t d = 0; d < Dim; ++d) {\n      for (size_t i = 0; i < Dim; ++i) {\n        jac.get(i, d) = block_jac.get(i, d) * gsl::at(map_slope_, d);\n      }\n    }\n    return jac;\n  }\n\n#ifdef SPECTRE_AUTODIFF\n  /*!\n   * \\brief The inverse Hessian of the element map.\n   *\n   * Let \\f$ \\xi^i \\f$ be the element logical coordinates, \\f$ x^i \\f$ be the\n   * block logical coordinates, and \\f$ y^i \\f$ be the inertial coordinates.\n   * The element map maps \\f$ \\xi^i \\rightarrow x^i \\rightarrow y^i \\f$. Thus,\n   * the inverse Hessian is\n   * \\f[\n   * \\frac{\\partial^2\\xi^i}{\\partial y^j \\partial y^k} =\n   * \\frac{\\partial}{\\partial y^j}\\frac{\\partial x^l}{\\partial y^k}\n   * \\frac{\\partial \\xi^i}{\\partial x^l}} =\n   * \\frac{\\partial^2 x^l}{\\partial y^j \\partial y^k}\n   * \\frac{\\partial \\xi^i}{\\partial x^l}\n   * \\f]\n   * where we have used the fact\n   * \\f[\n   * \\frac{\\partial^2 \\xi^i}{\\partial x^l \\partial x^n} = 0.\n   * \\f]\n   */\n  template <typename T>\n  InverseHessian<T, Dim, Frame::ElementLogical, TargetFrame> inv_hessian(\n      const tnsr::I<T, Dim, Frame::ElementLogical>& source_point,\n      const double time = std::numeric_limits<double>::signaling_NaN(),\n      const domain::FunctionsOfTimeMap& functions_of_time = {}) const {\n    auto block_source_point =\n        apply_affine_transformation_to_point(source_point);\n    auto block_inv_jac =\n        block_map_->inv_jacobian(block_source_point, time, functions_of_time);\n    auto block_inv_hes = block_map_->inv_hessian(\n        std::move(block_source_point), block_inv_jac, time, functions_of_time);\n    InverseHessian<T, Dim, Frame::ElementLogical, TargetFrame> inv_hes;\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        for (size_t k = j; k < Dim; ++k) {\n          inv_hes.get(i, j, k) =\n              block_inv_hes.get(i, j, k) / gsl::at(map_slope_, i);\n        }\n      }\n    }\n    return inv_hes;\n  }\n#endif  // SPECTRE_AUTODIFF\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <typename T>\n  tnsr::I<T, Dim, Frame::BlockLogical> apply_affine_transformation_to_point(\n      const tnsr::I<T, Dim, Frame::ElementLogical>& source_point) const {\n    tnsr::I<T, Dim, Frame::BlockLogical> block_source_point;\n    for (size_t d = 0; d < Dim; ++d) {\n      block_source_point.get(d) = source_point.get(d) * gsl::at(map_slope_, d) +\n                                  gsl::at(map_offset_, d);\n    }\n    return block_source_point;\n  }\n\n  std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, Dim>>\n      block_map_{nullptr};\n  // map_slope_[i] = 0.5 * (segment_ids[i].endpoint(Side::Upper) -\n  //                        segment_ids[i].endpoint(Side::Lower))\n  // Note: the map_slope_ is the jacobian_\n  std::array<double, Dim> map_slope_{\n      make_array<Dim>(std::numeric_limits<double>::signaling_NaN())};\n  // map_offset_[i] = 0.5 * (segment_ids[i].endpoint(Side::Upper) +\n  //                         segment_ids[i].endpoint(Side::Lower))\n  std::array<double, Dim> map_offset_{\n      make_array<Dim>(std::numeric_limits<double>::signaling_NaN())};\n};\n\ntemplate <size_t Dim, typename TargetFrame>\nElementMap(ElementId<Dim> element_id,\n           std::unique_ptr<\n               domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, Dim>>\n               block_map) -> ElementMap<Dim, TargetFrame>;\n"
  },
  {
    "path": "src/Domain/ElementToBlockLogicalMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Structure/Side.hpp\"\n\nnamespace domain {\n\nusing AffineMap = CoordinateMaps::Affine;\n\ntemplate <>\nstd::unique_ptr<\n    CoordinateMapBase<Frame::ElementLogical, Frame::BlockLogical, 1>>\nelement_to_block_logical_map(const ElementId<1>& element_id) {\n  return std::make_unique<\n      CoordinateMap<Frame::ElementLogical, Frame::BlockLogical, AffineMap>>(\n      AffineMap{-1., 1., element_id.segment_id(0).endpoint(Side::Lower),\n                element_id.segment_id(0).endpoint(Side::Upper)});\n}\ntemplate <>\nstd::unique_ptr<\n    CoordinateMapBase<Frame::ElementLogical, Frame::BlockLogical, 2>>\nelement_to_block_logical_map(const ElementId<2>& element_id) {\n  using AffineMap2D = CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n  return std::make_unique<\n      CoordinateMap<Frame::ElementLogical, Frame::BlockLogical, AffineMap2D>>(\n      AffineMap2D{{-1., 1., element_id.segment_id(0).endpoint(Side::Lower),\n                   element_id.segment_id(0).endpoint(Side::Upper)},\n                  {-1., 1., element_id.segment_id(1).endpoint(Side::Lower),\n                   element_id.segment_id(1).endpoint(Side::Upper)}});\n}\n\ntemplate <>\nstd::unique_ptr<\n    CoordinateMapBase<Frame::ElementLogical, Frame::BlockLogical, 3>>\nelement_to_block_logical_map(const ElementId<3>& element_id) {\n  using AffineMap3D =\n      CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n  return std::make_unique<\n      CoordinateMap<Frame::ElementLogical, Frame::BlockLogical, AffineMap3D>>(\n      AffineMap3D{{-1., 1., element_id.segment_id(0).endpoint(Side::Lower),\n                   element_id.segment_id(0).endpoint(Side::Upper)},\n                  {-1., 1., element_id.segment_id(1).endpoint(Side::Lower),\n                   element_id.segment_id(1).endpoint(Side::Upper)},\n                  {-1., 1., element_id.segment_id(2).endpoint(Side::Lower),\n                   element_id.segment_id(2).endpoint(Side::Upper)}});\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/ElementToBlockLogicalMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n\nnamespace domain {\n/*!\n * \\brief Constructs the affine map from ElementLogical to BlockLogical\n * coordinates\n *\n * An element is the result of repeatedly splitting a block in half along any of\n * its logical axes. This map transforms from ElementLogical coordinates\n * [-1, 1]^dim to the subset of BlockLogical coordinates [-1, 1]^dim that cover\n * the element. For instance, the two elements at refinement level 1 in 1D\n * cover [-1, 0] and [0, 1] in BlockLogical coordinates, respectively.\n */\ntemplate <size_t Dim>\nstd::unique_ptr<\n    CoordinateMapBase<Frame::ElementLogical, Frame::BlockLogical, Dim>>\nelement_to_block_logical_map(const ElementId<Dim>& element_id);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/ExcisionSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/ExcisionSphere.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <unordered_map>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\ntemplate <size_t VolumeDim>\nExcisionSphere<VolumeDim>::ExcisionSphere(\n    const double radius, const tnsr::I<double, VolumeDim, Frame::Grid> center,\n    std::unordered_map<size_t, Direction<VolumeDim>> abutting_directions)\n    : radius_(radius),\n      center_(center),\n      abutting_directions_(std::move(abutting_directions)) {\n  ASSERT(radius_ > 0.0,\n         \"The ExcisionSphere must have a radius greater than zero.\");\n}\n\ntemplate <size_t VolumeDim>\nExcisionSphere<VolumeDim>::ExcisionSphere(\n    const ExcisionSphere<VolumeDim>& rhs) {\n  *this = rhs;\n}\n\ntemplate <size_t VolumeDim>\nExcisionSphere<VolumeDim>& ExcisionSphere<VolumeDim>::operator=(\n    const ExcisionSphere<VolumeDim>& rhs) {\n  radius_ = rhs.radius_;\n  center_ = rhs.center_;\n  abutting_directions_ = rhs.abutting_directions_;\n  if (rhs.grid_to_inertial_map_ == nullptr) {\n    grid_to_inertial_map_ = nullptr;\n  } else {\n    grid_to_inertial_map_ = rhs.grid_to_inertial_map_->get_clone();\n  }\n  return *this;\n}\n\ntemplate <size_t VolumeDim>\nvoid ExcisionSphere<VolumeDim>::inject_time_dependent_maps(\n    std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n        moving_mesh_grid_to_inertial_map) {\n  grid_to_inertial_map_ = std::move(moving_mesh_grid_to_inertial_map);\n}\n\ntemplate <size_t VolumeDim>\nconst domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\nExcisionSphere<VolumeDim>::moving_mesh_grid_to_inertial_map() const {\n  ASSERT(\n      grid_to_inertial_map_ != nullptr,\n      \"Trying to access grid to inertial map from ExcisionSphere, but it has \"\n      \"not been set.\");\n\n  return *grid_to_inertial_map_;\n}\n\ntemplate <size_t VolumeDim>\nstd::optional<Direction<VolumeDim>>\nExcisionSphere<VolumeDim>::abutting_direction(\n    const ElementId<VolumeDim>& element_id) const {\n  const size_t block_id = element_id.block_id();\n  if (abutting_directions_.count(block_id)) {\n    const auto& direction = abutting_directions_.at(block_id);\n    const auto& segment_id = element_id.segment_id(direction.dimension());\n    const size_t abutting_index =\n        direction.side() == Side::Lower\n            ? 0\n            : two_to_the(segment_id.refinement_level()) - 1;\n    if (segment_id.index() == abutting_index) {\n      return direction;\n    }\n  }\n  return std::nullopt;\n}\n\ntemplate <size_t VolumeDim>\nvoid ExcisionSphere<VolumeDim>::pup(PUP::er& p) {\n  size_t version = 1;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | radius_;\n    p | center_;\n    p | abutting_directions_;\n  }\n  if (version >= 1) {\n    p | grid_to_inertial_map_;\n  } else if (p.isUnpacking()) {\n    grid_to_inertial_map_ = nullptr;\n  }\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const ExcisionSphere<VolumeDim>& lhs,\n                const ExcisionSphere<VolumeDim>& rhs) {\n  // First check time dep maps. If both are time dep, safe to dereference\n  // unique_ptr\n  if (lhs.is_time_dependent() != rhs.is_time_dependent()) {\n    return false;\n  }\n  bool equal = lhs.radius_ == rhs.radius_ and lhs.center_ == rhs.center_ and\n               lhs.abutting_directions_ == rhs.abutting_directions_;\n  if (lhs.is_time_dependent() and rhs.is_time_dependent()) {\n    equal = equal and *lhs.grid_to_inertial_map_ == *rhs.grid_to_inertial_map_;\n  }\n\n  return equal;\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const ExcisionSphere<VolumeDim>& lhs,\n                const ExcisionSphere<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const ExcisionSphere<VolumeDim>& sphere) {\n  os << \"ExcisionSphere:\\n\";\n  os << \"  Radius: \" << sphere.radius() << \"\\n\";\n  os << \"  Center: \" << sphere.center() << \"\\n\";\n  os << \"  Abutting directions: \" << sphere.abutting_directions() << \"\\n\";\n  return os;\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                    \\\n  template class ExcisionSphere<GET_DIM(data)>;                   \\\n  template bool operator==(const ExcisionSphere<GET_DIM(data)>&,  \\\n                           const ExcisionSphere<GET_DIM(data)>&); \\\n  template bool operator!=(const ExcisionSphere<GET_DIM(data)>&,  \\\n                           const ExcisionSphere<GET_DIM(data)>&); \\\n  template std::ostream& operator<<(std::ostream&,                \\\n                                    const ExcisionSphere<GET_DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/ExcisionSphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class ExcisionSphere.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup ComputationalDomainGroup\n/// The excision sphere information of a computational domain.\n/// The excision sphere is assumed to be a coordinate sphere in the\n/// grid frame.\n///\n/// \\tparam VolumeDim the volume dimension.\ntemplate <size_t VolumeDim>\nclass ExcisionSphere {\n public:\n  /// Constructor\n  ///\n  /// \\param radius the radius of the excision sphere in the\n  /// computational domain.\n  /// \\param center the coordinate center of the excision sphere\n  /// in the computational domain.\n  /// \\param abutting_directions the set of blocks that touch the excision\n  /// sphere, along with the direction in which they touch it.\n  ExcisionSphere(\n      double radius, tnsr::I<double, VolumeDim, Frame::Grid> center,\n      std::unordered_map<size_t, Direction<VolumeDim>> abutting_directions);\n\n  /// Default constructor needed for Charm++ serialization.\n  ExcisionSphere() = default;\n  ~ExcisionSphere() = default;\n  ExcisionSphere(const ExcisionSphere<VolumeDim>& /*rhs*/);\n  ExcisionSphere(ExcisionSphere<VolumeDim>&& /*rhs*/) = default;\n  ExcisionSphere<VolumeDim>& operator=(\n      const ExcisionSphere<VolumeDim>& /*rhs*/);\n  ExcisionSphere<VolumeDim>& operator=(ExcisionSphere<VolumeDim>&& /*rhs*/) =\n      default;\n\n  /// \\brief Add time dependent coordinate maps to the ExcisionSphere\n  ///\n  /// \\note There is no actual mesh that is inside an excision sphere, but this\n  /// region moves along with the domain just the same. Meaning that we\n  /// should be able to take a point inside the excision sphere and map it to\n  /// the Inertial frame.\n  void inject_time_dependent_maps(\n      std::unique_ptr<\n          domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n          moving_mesh_grid_to_inertial_map);\n\n  /// \\brief The map going from the last time independent frame to the frame in\n  /// which the equations are solved. Only used when `is_time_dependent()` is\n  /// true.\n  const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n  moving_mesh_grid_to_inertial_map() const;\n\n  /// \\brief Return whether or not time dependent maps have been injected\n  bool is_time_dependent() const { return grid_to_inertial_map_ != nullptr; }\n\n  /// The radius of the ExcisionSphere.\n  double radius() const { return radius_; }\n\n  /// The coordinate center of the ExcisionSphere.\n  const tnsr::I<double, VolumeDim, Frame::Grid>& center() const {\n    return center_;\n  }\n\n  /// The set of blocks that touch the excision sphere, along with the direction\n  /// in which they touch it\n  const std::unordered_map<size_t, Direction<VolumeDim>>& abutting_directions()\n      const {\n    return abutting_directions_;\n  }\n  /// Checks whether an element abuts the excision sphere. If it does, returns\n  /// the corresponding direction. Else, `nullopt` is returned.\n  std::optional<Direction<VolumeDim>> abutting_direction(\n      const ElementId<VolumeDim>& element_id) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <size_t LocalVolumeDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const ExcisionSphere<LocalVolumeDim>& lhs,\n                         const ExcisionSphere<LocalVolumeDim>& rhs);\n\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  tnsr::I<double, VolumeDim, Frame::Grid> center_{\n      std::numeric_limits<double>::signaling_NaN()};\n  std::unordered_map<size_t, Direction<VolumeDim>> abutting_directions_{};\n  std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>\n      grid_to_inertial_map_{};\n};\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const ExcisionSphere<VolumeDim>& excision_sphere);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const ExcisionSphere<VolumeDim>& lhs,\n                const ExcisionSphere<VolumeDim>& rhs);\n"
  },
  {
    "path": "src/Domain/FaceNormal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FaceNormal.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid unnormalized_face_normal(\n    const gsl::not_null<tnsr::i<DataVector, VolumeDim, TargetFrame>*> result,\n    const Mesh<VolumeDim - 1>& /*interface_mesh*/,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inv_jacobian_on_interface,\n    const Direction<VolumeDim>& direction) {\n  const auto sliced_away_dim = direction.dimension();\n  const double sign = direction.sign();\n\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    result->get(d) = sign * inv_jacobian_on_interface.get(sliced_away_dim, d);\n  }\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\ntnsr::i<DataVector, VolumeDim, TargetFrame> unnormalized_face_normal(\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inv_jacobian_on_interface,\n    const Direction<VolumeDim>& direction) {\n  tnsr::i<DataVector, VolumeDim, TargetFrame> result{};\n  unnormalized_face_normal(make_not_null(&result), interface_mesh,\n                           inv_jacobian_on_interface, direction);\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid unnormalized_face_normal(\n    const gsl::not_null<tnsr::i<DataVector, VolumeDim, TargetFrame>*> result,\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const ElementMap<VolumeDim, TargetFrame>& map,\n    const Direction<VolumeDim>& direction) {\n  unnormalized_face_normal(result, interface_mesh,\n                           map.inv_jacobian(interface_logical_coordinates(\n                               interface_mesh, direction)),\n                           direction);\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\ntnsr::i<DataVector, VolumeDim, TargetFrame> unnormalized_face_normal(\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const ElementMap<VolumeDim, TargetFrame>& map,\n    const Direction<VolumeDim>& direction) {\n  tnsr::i<DataVector, VolumeDim, TargetFrame> result{};\n  unnormalized_face_normal(make_not_null(&result), interface_mesh,\n                           map.inv_jacobian(interface_logical_coordinates(\n                               interface_mesh, direction)),\n                           direction);\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid unnormalized_face_normal(\n    const gsl::not_null<tnsr::i<DataVector, VolumeDim, TargetFrame>*> result,\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const domain::CoordinateMapBase<Frame::ElementLogical, TargetFrame,\n                                    VolumeDim>& map,\n    const Direction<VolumeDim>& direction) {\n  unnormalized_face_normal(result, interface_mesh,\n                           map.inv_jacobian(interface_logical_coordinates(\n                               interface_mesh, direction)),\n                           direction);\n}\n\ntemplate <size_t VolumeDim, typename TargetFrame>\ntnsr::i<DataVector, VolumeDim, TargetFrame> unnormalized_face_normal(\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const domain::CoordinateMapBase<Frame::ElementLogical, TargetFrame,\n                                    VolumeDim>& map,\n    const Direction<VolumeDim>& direction) {\n  tnsr::i<DataVector, VolumeDim, TargetFrame> result{};\n  unnormalized_face_normal(make_not_null(&result), interface_mesh,\n                           map.inv_jacobian(interface_logical_coordinates(\n                               interface_mesh, direction)),\n                           direction);\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nvoid unnormalized_face_normal(\n    const gsl::not_null<tnsr::i<DataVector, VolumeDim, Frame::Inertial>*>\n        result,\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const ElementMap<VolumeDim, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n        grid_to_inertial_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const Direction<VolumeDim>& direction) {\n  auto logical_to_grid_inv_jac = logical_to_grid_map.inv_jacobian(\n      interface_logical_coordinates(interface_mesh, direction));\n  ::InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                    Frame::Inertial>\n      logical_to_inertial_inv_jac{};\n\n  if (grid_to_inertial_map.is_identity()) {\n    for (size_t i = 0; i < logical_to_inertial_inv_jac.size(); ++i) {\n      logical_to_inertial_inv_jac[i] = std::move(logical_to_grid_inv_jac[i]);\n    }\n  } else {\n    const auto grid_to_inertial_inv_jac = grid_to_inertial_map.inv_jacobian(\n        logical_to_grid_map(\n            interface_logical_coordinates(interface_mesh, direction)),\n        time, functions_of_time);\n\n    for (size_t logical_i = 0; logical_i < VolumeDim; ++logical_i) {\n      for (size_t inertial_i = 0; inertial_i < VolumeDim; ++inertial_i) {\n        logical_to_inertial_inv_jac.get(logical_i, inertial_i) =\n            logical_to_grid_inv_jac.get(logical_i, 0) *\n            grid_to_inertial_inv_jac.get(0, inertial_i);\n        for (size_t grid_i = 1; grid_i < VolumeDim; ++grid_i) {\n          logical_to_inertial_inv_jac.get(logical_i, inertial_i) +=\n              logical_to_grid_inv_jac.get(logical_i, grid_i) *\n              grid_to_inertial_inv_jac.get(grid_i, inertial_i);\n        }\n      }\n    }\n  }\n\n  unnormalized_face_normal(result, interface_mesh, logical_to_inertial_inv_jac,\n                           direction);\n}\n\ntemplate <size_t VolumeDim>\ntnsr::i<DataVector, VolumeDim, Frame::Inertial> unnormalized_face_normal(\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const ElementMap<VolumeDim, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n        grid_to_inertial_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const Direction<VolumeDim>& direction) {\n  tnsr::i<DataVector, VolumeDim, Frame::Inertial> result{};\n  unnormalized_face_normal(make_not_null(&result), interface_mesh,\n                           logical_to_grid_map, grid_to_inertial_map, time,\n                           functions_of_time, direction);\n  return result;\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(_, data)                                                \\\n  template tnsr::i<DataVector, GET_DIM(data), GET_FRAME(data)>                \\\n  unnormalized_face_normal(                                                   \\\n      const Mesh<GET_DIM(data) - 1>&,                                         \\\n      const InverseJacobian<DataVector, GET_DIM(data), Frame::ElementLogical, \\\n                            GET_FRAME(data)>&,                                \\\n      const Direction<GET_DIM(data)>&);                                       \\\n  template void unnormalized_face_normal(                                     \\\n      const gsl::not_null<                                                    \\\n          tnsr::i<DataVector, GET_DIM(data), GET_FRAME(data)>*>               \\\n          result,                                                             \\\n      const Mesh<GET_DIM(data) - 1>&,                                         \\\n      const ElementMap<GET_DIM(data), GET_FRAME(data)>&,                      \\\n      const Direction<GET_DIM(data)>&);                                       \\\n  template tnsr::i<DataVector, GET_DIM(data), GET_FRAME(data)>                \\\n  unnormalized_face_normal(const Mesh<GET_DIM(data) - 1>&,                    \\\n                           const ElementMap<GET_DIM(data), GET_FRAME(data)>&, \\\n                           const Direction<GET_DIM(data)>&);                  \\\n  template void unnormalized_face_normal(                                     \\\n      const gsl::not_null<                                                    \\\n          tnsr::i<DataVector, GET_DIM(data), GET_FRAME(data)>*>               \\\n          result,                                                             \\\n      const Mesh<GET_DIM(data) - 1>&,                                         \\\n      const domain::CoordinateMapBase<Frame::ElementLogical, GET_FRAME(data), \\\n                                      GET_DIM(data)>&,                        \\\n      const Direction<GET_DIM(data)>&);                                       \\\n  template tnsr::i<DataVector, GET_DIM(data), GET_FRAME(data)>                \\\n  unnormalized_face_normal(                                                   \\\n      const Mesh<GET_DIM(data) - 1>&,                                         \\\n      const domain::CoordinateMapBase<Frame::ElementLogical, GET_FRAME(data), \\\n                                      GET_DIM(data)>&,                        \\\n      const Direction<GET_DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3),\n                        (Frame::Inertial, Frame::Grid))\n\n#undef INSTANTIATION\n\n#define INSTANTIATION(_, data)                                                \\\n  template void unnormalized_face_normal(                                     \\\n      const gsl::not_null<                                                    \\\n          tnsr::i<DataVector, GET_DIM(data), Frame::Inertial>*>               \\\n          result,                                                             \\\n      const Mesh<GET_DIM(data) - 1>& interface_mesh,                          \\\n      const ElementMap<GET_DIM(data), Frame::Grid>& logical_to_grid_map,      \\\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial,           \\\n                                      GET_DIM(data)>& grid_to_inertial_map,   \\\n      const double time,                                                      \\\n      const std::unordered_map<                                               \\\n          std::string,                                                        \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&          \\\n          functions_of_time,                                                  \\\n      const Direction<GET_DIM(data)>& direction);                             \\\n  template void unnormalized_face_normal(                                     \\\n      const gsl::not_null<                                                    \\\n          tnsr::i<DataVector, GET_DIM(data), Frame::Inertial>*>               \\\n          result,                                                             \\\n      const Mesh<GET_DIM(data) - 1>& interface_mesh,                          \\\n      const InverseJacobian<DataVector, GET_DIM(data), Frame::ElementLogical, \\\n                            Frame::Inertial>& inv_jacobian_on_interface,      \\\n      const Direction<GET_DIM(data)>& direction);                             \\\n  template tnsr::i<DataVector, GET_DIM(data), Frame::Inertial>                \\\n  unnormalized_face_normal(                                                   \\\n      const Mesh<GET_DIM(data) - 1>& interface_mesh,                          \\\n      const ElementMap<GET_DIM(data), Frame::Grid>& logical_to_grid_map,      \\\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial,           \\\n                                      GET_DIM(data)>& grid_to_inertial_map,   \\\n      const double time,                                                      \\\n      const std::unordered_map<                                               \\\n          std::string,                                                        \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&          \\\n          functions_of_time,                                                  \\\n      const Direction<GET_DIM(data)>& direction);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef GET_FRAME\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/FaceNormal.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares function unnormalized_face_normal\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <functional>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/InterfaceComputeTags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain {\ntemplate <typename, typename, size_t>\nclass CoordinateMapBase;\n}  // namespace domain\nclass DataVector;\ntemplate <size_t>\nclass Direction;\ntemplate <size_t Dim, typename Frame>\nclass ElementMap;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\n/// @{\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Compute the outward grid normal on a face of an Element\n *\n * \\details\n * Computes the grid-frame normal by taking the logical-frame unit\n * one-form in the given Direction and mapping it to the grid frame\n * with the given map, or the given inverse Jacobian.\n *\n * \\example\n * \\snippet Test_FaceNormal.cpp face_normal_example\n */\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid unnormalized_face_normal(\n    const gsl::not_null<tnsr::i<DataVector, VolumeDim, TargetFrame>*> result,\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inv_jacobian_on_interface,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\ntnsr::i<DataVector, VolumeDim, TargetFrame> unnormalized_face_normal(\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const InverseJacobian<DataVector, VolumeDim, Frame::ElementLogical,\n                          TargetFrame>& inv_jacobian_on_interface,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid unnormalized_face_normal(\n    gsl::not_null<tnsr::i<DataVector, VolumeDim, TargetFrame>*> result,\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const ElementMap<VolumeDim, TargetFrame>& map,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\ntnsr::i<DataVector, VolumeDim, TargetFrame> unnormalized_face_normal(\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const ElementMap<VolumeDim, TargetFrame>& map,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\nvoid unnormalized_face_normal(\n    gsl::not_null<tnsr::i<DataVector, VolumeDim, TargetFrame>*> result,\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const domain::CoordinateMapBase<Frame::ElementLogical, TargetFrame,\n                                    VolumeDim>& map,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim, typename TargetFrame>\ntnsr::i<DataVector, VolumeDim, TargetFrame> unnormalized_face_normal(\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const domain::CoordinateMapBase<Frame::ElementLogical, TargetFrame,\n                                    VolumeDim>& map,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim>\nvoid unnormalized_face_normal(\n    gsl::not_null<tnsr::i<DataVector, VolumeDim, Frame::Inertial>*> result,\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const ElementMap<VolumeDim, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n        grid_to_inertial_map,\n    double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const Direction<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim>\ntnsr::i<DataVector, VolumeDim, Frame::Inertial> unnormalized_face_normal(\n    const Mesh<VolumeDim - 1>& interface_mesh,\n    const ElementMap<VolumeDim, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n        grid_to_inertial_map,\n    double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const Direction<VolumeDim>& direction);\n/// @}\n\nnamespace domain {\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The unnormalized face normal one form\ntemplate <size_t VolumeDim, typename Frame = ::Frame::Inertial>\nstruct UnnormalizedFaceNormal : db::SimpleTag {\n  using type = tnsr::i<DataVector, VolumeDim, Frame>;\n};\n\ntemplate <size_t VolumeDim, typename Frame = ::Frame::Inertial>\nstruct UnnormalizedFaceNormalCompute\n    : db::ComputeTag, UnnormalizedFaceNormal<VolumeDim, Frame> {\n  using base = UnnormalizedFaceNormal<VolumeDim, Frame>;\n  using return_type = typename base::type;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<return_type*>, const ::Mesh<VolumeDim - 1>&,\n      const ::ElementMap<VolumeDim, Frame>&, const ::Direction<VolumeDim>&)>(\n      &unnormalized_face_normal);\n  using argument_tags =\n      tmpl::list<Mesh<VolumeDim - 1>, ElementMap<VolumeDim, Frame>,\n                 Direction<VolumeDim>>;\n  using volume_tags = tmpl::list<ElementMap<VolumeDim, Frame>>;\n};\n\ntemplate <size_t VolumeDim>\nstruct UnnormalizedFaceNormalMovingMeshCompute\n    : db::ComputeTag,\n      UnnormalizedFaceNormal<VolumeDim, Frame::Inertial> {\n  using base = UnnormalizedFaceNormal<VolumeDim, Frame::Inertial>;\n  using return_type = typename base::type;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<return_type*>, const ::Mesh<VolumeDim - 1>&,\n      const ::ElementMap<VolumeDim, Frame::Grid>&,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&,\n      double,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&,\n      const ::Direction<VolumeDim>&)>(&unnormalized_face_normal);\n  using argument_tags =\n      tmpl::list<Mesh<VolumeDim - 1>, ElementMap<VolumeDim, Frame::Grid>,\n                 CoordinateMaps::Tags::CoordinateMap<VolumeDim, Frame::Grid,\n                                                     Frame::Inertial>,\n                 ::Tags::Time, Tags::FunctionsOfTime, Direction<VolumeDim>>;\n  using volume_tags =\n      tmpl::list<ElementMap<VolumeDim, Frame::Grid>,\n                 CoordinateMaps::Tags::CoordinateMap<VolumeDim, Frame::Grid,\n                                                     Frame::Inertial>,\n                 ::Tags::Time, Tags::FunctionsOfTime>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// Specialisation of UnnormalizedFaceNormal for the external boundaries which\n/// inverts the normals. Since ExternalBoundariesDirections are meant to\n/// represent ghost elements, the normals should correspond to the normals in\n/// said element, which are inverted with respect to the current element.\ntemplate <size_t VolumeDim, typename Frame>\nstruct InterfaceCompute<Tags::BoundaryDirectionsExterior<VolumeDim>,\n                        UnnormalizedFaceNormalCompute<VolumeDim, Frame>>\n    : db::ComputeTag,\n      Tags::Interface<Tags::BoundaryDirectionsExterior<VolumeDim>,\n                      Tags::UnnormalizedFaceNormal<VolumeDim, Frame>> {\n  using base = Tags::Interface<Tags::BoundaryDirectionsExterior<VolumeDim>,\n                               Tags::UnnormalizedFaceNormal<VolumeDim, Frame>>;\n  using dirs = BoundaryDirectionsExterior<VolumeDim>;\n\n  static std::string name() {\n    return \"BoundaryDirectionsExterior<UnnormalizedFaceNormal>\";\n  }\n  using return_type = std::unordered_map<::Direction<VolumeDim>,\n                                         tnsr::i<DataVector, VolumeDim, Frame>>;\n  static void function(const gsl::not_null<return_type*> normals,\n                       const std::unordered_map<::Direction<VolumeDim>,\n                                                ::Mesh<VolumeDim - 1>>& meshes,\n                       const ::ElementMap<VolumeDim, Frame>& map) {\n    for (const auto& direction_and_mesh : meshes) {\n      const auto& direction = direction_and_mesh.first;\n      const auto& mesh = direction_and_mesh.second;\n      auto internal_face_normal =\n          unnormalized_face_normal(mesh, map, direction);\n      std::transform(internal_face_normal.begin(), internal_face_normal.end(),\n                     internal_face_normal.begin(), std::negate<>());\n      (*normals)[direction] = std::move(internal_face_normal);\n    }\n  }\n\n  using argument_tags = tmpl::list<Tags::Interface<dirs, Mesh<VolumeDim - 1>>,\n                                   Tags::ElementMap<VolumeDim, Frame>>;\n};\n\ntemplate <size_t VolumeDim>\nstruct InterfaceCompute<Tags::BoundaryDirectionsExterior<VolumeDim>,\n                        UnnormalizedFaceNormalMovingMeshCompute<VolumeDim>>\n    : db::ComputeTag,\n      Tags::Interface<\n          Tags::BoundaryDirectionsExterior<VolumeDim>,\n          Tags::UnnormalizedFaceNormal<VolumeDim, Frame::Inertial>> {\n  using base =\n      Tags::Interface<Tags::BoundaryDirectionsExterior<VolumeDim>,\n                      Tags::UnnormalizedFaceNormal<VolumeDim, Frame::Inertial>>;\n  using dirs = BoundaryDirectionsExterior<VolumeDim>;\n\n  static std::string name() {\n    return \"BoundaryDirectionsExterior<UnnormalizedFaceNormal>\";\n  }\n  using return_type =\n      std::unordered_map<::Direction<VolumeDim>,\n                         tnsr::i<DataVector, VolumeDim, Frame::Inertial>>;\n  static void function(\n      const gsl::not_null<return_type*> normals,\n      const std::unordered_map<::Direction<VolumeDim>, ::Mesh<VolumeDim - 1>>&\n          meshes,\n      const ::ElementMap<VolumeDim, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n          grid_to_inertial_map,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    for (const auto& direction_and_mesh : meshes) {\n      const auto& direction = direction_and_mesh.first;\n      const auto& mesh = direction_and_mesh.second;\n      auto internal_face_normal = unnormalized_face_normal(\n          mesh, logical_to_grid_map, grid_to_inertial_map, time,\n          functions_of_time, direction);\n      std::transform(internal_face_normal.begin(), internal_face_normal.end(),\n                     internal_face_normal.begin(), std::negate<>());\n      (*normals)[direction] = std::move(internal_face_normal);\n    }\n  }\n\n  using argument_tags =\n      tmpl::list<Tags::Interface<dirs, Mesh<VolumeDim - 1>>,\n                 Tags::ElementMap<VolumeDim, Frame::Grid>,\n                 CoordinateMaps::Tags::CoordinateMap<VolumeDim, Frame::Grid,\n                                                     Frame::Inertial>,\n                 ::Tags::Time, Tags::FunctionsOfTime>;\n};\n\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/FlatLogicalMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FlatLogicalMetric.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace domain {\n\ntemplate <size_t Dim>\nvoid flat_logical_metric(\n    const gsl::not_null<tnsr::ii<DataVector, Dim, Frame::ElementLogical>*>\n        result,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian) {\n  set_number_of_grid_points(result, jacobian);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      result->get(i, j) = 0.;\n      for (size_t k = 0; k < Dim; ++k) {\n        result->get(i, j) += jacobian.get(k, i) * jacobian.get(k, j);\n      }\n    }\n  }\n}\n\nnamespace Tags {\n\ntemplate <size_t Dim>\nvoid FlatLogicalMetricCompute<Dim>::function(\n    const gsl::not_null<tnsr::ii<DataVector, Dim, Frame::ElementLogical>*>\n        result,\n    const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian) {\n  // We have the inverse Jacobian available, so just do a numerical inverse to\n  // compute the Jacobian. That's easier than computing the Jacobian from\n  // time-dependent maps here, though if the Jacobian is available then it's\n  // of course better to use it here directly.\n  const auto jacobian = determinant_and_inverse(inv_jacobian).second;\n  flat_logical_metric(result, jacobian);\n}\n}  // namespace Tags\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                                     \\\n  template void flat_logical_metric(                               \\\n      const gsl::not_null<                                         \\\n          tnsr::ii<DataVector, DIM(data), Frame::ElementLogical>*> \\\n          result,                                                  \\\n      const Jacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                     Frame::Inertial>& jacobian);                  \\\n  template class Tags::FlatLogicalMetricCompute<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/FlatLogicalMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\n\n/*!\n * \\brief The \"flat logical metric\" $\\sum_k \\frac{\\partial x^k}{\\partial\n * \\xi^\\hat{i}} \\frac{\\partial x^k}{\\partial \\xi^\\hat{j}}$, which is the flat\n * spatial metric in element-logical coordinates.\n *\n * We define the \"flat logical metric\" to be the matrix of inner products of the\n * $\\hat{i}$-th logical coordinate basis vector $\\partial_{\\xi^\\hat{i}}$ with\n * the $\\hat{j}$-th logical coordinate basis vector $\\partial_{\\xi^\\hat{j}}$,\n * where the inner product is taken assuming a flat spatial metric. When\n * expressed in the (\"inertial\") $x$-coordinate system, each basis vector is a\n * column of the Jacobian.\n */\ntemplate <size_t Dim>\nvoid flat_logical_metric(\n    const gsl::not_null<tnsr::ii<DataVector, Dim, Frame::ElementLogical>*>\n        result,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian);\n\nnamespace Tags {\n/// The flat metric in element-logical coordinates\n///\n/// \\see domain::flat_logical_metric\ntemplate <size_t Dim>\nstruct FlatLogicalMetric : db::SimpleTag {\n  using type = tnsr::ii<DataVector, Dim, Frame::ElementLogical>;\n};\n\n/// Compute the flat metric in element-logical coordinates from the inverse\n/// Jacobian.\n///\n/// \\see domain::flat_logical_metric\ntemplate <size_t Dim>\nstruct FlatLogicalMetricCompute : FlatLogicalMetric<Dim>, db::ComputeTag {\n  using base = FlatLogicalMetric<Dim>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>>;\n  static void function(\n      const gsl::not_null<tnsr::ii<DataVector, Dim, Frame::ElementLogical>*>\n          result,\n      const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                              Frame::Inertial>& inv_jacobian);\n};\n\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY FunctionsOfTime)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FixedSpeedCubic.cpp\n  IntegratedFunctionOfTime.cpp\n  OutputTimeBounds.cpp\n  PiecewisePolynomial.cpp\n  QuaternionFunctionOfTime.cpp\n  QuaternionHelpers.cpp\n  RegisterDerivedWithCharm.cpp\n  SettleToConstant.cpp\n  SettleToConstantQuaternion.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FixedSpeedCubic.hpp\n  FunctionOfTime.hpp\n  IntegratedFunctionOfTime.hpp\n  OptionTags.hpp\n  OutputTimeBounds.hpp\n  PiecewisePolynomial.hpp\n  QuaternionFunctionOfTime.hpp\n  QuaternionHelpers.hpp\n  RegisterDerivedWithCharm.hpp\n  SettleToConstant.hpp\n  SettleToConstantQuaternion.hpp\n  Tags.hpp\n  ThreadsafeList.hpp\n  ThreadsafeList.tpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Options\n  Serialization\n  Utilities\n  PRIVATE\n  ErrorHandling\n  )\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/FixedSpeedCubic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <ostream>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain::FunctionsOfTime {\nFixedSpeedCubic::FixedSpeedCubic(const double initial_function_value,\n                                 const double initial_time,\n                                 const double velocity,\n                                 const double decay_timescale)\n    : initial_function_value_(initial_function_value),\n      initial_time_(initial_time),\n      velocity_(velocity),\n      squared_decay_timescale_(square(decay_timescale)) {}\n\nstd::unique_ptr<FunctionOfTime> FixedSpeedCubic::get_clone() const {\n  return std::make_unique<FixedSpeedCubic>(*this);\n}\n\nstd::array<DataVector, 1> FixedSpeedCubic::func(const double t) const {\n  return func_and_derivs<0>(t);\n}\n\nstd::array<DataVector, 2> FixedSpeedCubic::func_and_deriv(\n    const double t) const {\n  return func_and_derivs<1>(t);\n}\n\nstd::array<DataVector, 3> FixedSpeedCubic::func_and_2_derivs(\n    const double t) const {\n  return func_and_derivs<2>(t);\n}\n\nstd::array<double, 2> FixedSpeedCubic::time_bounds() const {\n  return {{initial_time_, std::numeric_limits<double>::infinity()}};\n}\n\ndouble FixedSpeedCubic::expiration_after(const double /*time*/) const {\n  return std::numeric_limits<double>::infinity();\n}\n\nvoid FixedSpeedCubic::truncate_at_time(const double /*time*/) {}\n\ndouble FixedSpeedCubic::velocity() const { return velocity_; }\n\ndouble FixedSpeedCubic::decay_timescale() const {\n  return sqrt(squared_decay_timescale_);\n}\n\ntemplate <size_t MaxDerivReturned>\nstd::array<DataVector, MaxDerivReturned + 1> FixedSpeedCubic::func_and_derivs(\n    const double t) const {\n  static_assert(MaxDerivReturned < 3, \"The maximum available derivative is 2.\");\n\n  // initialize result for the number of derivs requested\n  std::array<DataVector, MaxDerivReturned + 1> result =\n      make_array<MaxDerivReturned + 1>(DataVector(1, 0.0));\n\n  const double dt = t - initial_time_;\n  const double denom = squared_decay_timescale_ + square(dt);\n  ASSERT(fabs(denom) > 0.0,\n         \"FixedSpeedCubic denominator should not be zero; if evaluating at \"\n         \"t == t0, then do not set the decay timescale tau to 0.\");\n  const double one_over_denom = 1.0 / (squared_decay_timescale_ + square(dt));\n\n  gsl::at(result, 0)[0] =\n      initial_function_value_ + velocity_ * cube(dt) * one_over_denom;\n  if (MaxDerivReturned > 0) {\n    gsl::at(result, 1)[0] = (3.0 * squared_decay_timescale_ + square(dt)) *\n                            square(dt) * velocity_ * square(one_over_denom);\n    if (MaxDerivReturned > 1) {\n      gsl::at(result, 2)[0] = 2.0 * squared_decay_timescale_ *\n                              (3.0 * squared_decay_timescale_ - square(dt)) *\n                              dt * velocity_ * cube(one_over_denom);\n    }\n  }\n\n  return result;\n}\n\nvoid FixedSpeedCubic::pup(PUP::er& p) {\n  FunctionOfTime::pup(p);\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | initial_function_value_;\n    p | initial_time_;\n    p | velocity_;\n    p | squared_decay_timescale_;\n  }\n}\n\nbool operator==(const FixedSpeedCubic& lhs, const FixedSpeedCubic& rhs) {\n  return lhs.initial_function_value_ == rhs.initial_function_value_ and\n         lhs.initial_time_ == rhs.initial_time_ and\n         lhs.velocity_ == rhs.velocity_ and\n         lhs.squared_decay_timescale_ == rhs.squared_decay_timescale_;\n}\n\nstd::ostream& operator<<(\n    std::ostream& os, const FixedSpeedCubic& fixed_speed_cubic) {\n  os << \"FixedSpeedCubic(t=\" << fixed_speed_cubic.initial_time_\n     << \", f=\" << fixed_speed_cubic.initial_function_value_\n     << \", v=\" << fixed_speed_cubic.velocity_\n     << \", tau^2=\" << fixed_speed_cubic.squared_decay_timescale_ << \")\";\n  return os;\n}\n\nbool operator!=(const FixedSpeedCubic& lhs, const FixedSpeedCubic& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID FixedSpeedCubic::my_PUP_ID = 0;  // NOLINT\n\n#define DERIV(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                       \\\n  template std::array<DataVector, DERIV(data) + 1> \\\n  FixedSpeedCubic::func_and_derivs<DERIV(data)>(const double) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2))\n\n#undef DERIV\n#undef INSTANTIATE\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/FixedSpeedCubic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace domain {\nnamespace FunctionsOfTime {\n/*!\n * \\ingroup ControlSystemGroup\n * \\brief Sets \\f$f(t)\\f$ and derivatives using cubic rational functions,\n * such that the first derivative approaches a constant and the second\n * derivative approaches zero.\n *\n * The resultant function of time is\n *\n * \\f{align*}{\n *   f(t) &= f_0 + \\frac{v(t-t_0)^3}{\\tau^2+(t-t_0)^2},\n * \\f}\n *\n * where \\f$f_0\\f$ is the value of the function \\f$f\\f$ at the initial time\n * \\f$t_0\\f$, and \\f$v\\f$ is the velocity that \\f$f^\\prime(t)\\f$ approaches on a\n * timescale of \\f$\\tau\\f$.\n */\nclass FixedSpeedCubic : public FunctionOfTime {\n public:\n  FixedSpeedCubic() = default;\n  FixedSpeedCubic(double initial_function_value, double initial_time,\n                  double velocity, double decay_timescale);\n\n  ~FixedSpeedCubic() override = default;\n  FixedSpeedCubic(FixedSpeedCubic&&) = default;\n  FixedSpeedCubic& operator=(FixedSpeedCubic&&) = default;\n  FixedSpeedCubic(const FixedSpeedCubic&) = default;\n  FixedSpeedCubic& operator=(const FixedSpeedCubic&) = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  WRAPPED_PUPable_decl_template(FixedSpeedCubic);\n\n  explicit FixedSpeedCubic(CkMigrateMessage* /*unused*/) {}\n\n  auto get_clone() const -> std::unique_ptr<FunctionOfTime> override;\n\n  /// Returns the function at an arbitrary time `t`.\n  std::array<DataVector, 1> func(double t) const override;\n  /// Returns the function and its first derivative at an arbitrary time `t`.\n  std::array<DataVector, 2> func_and_deriv(double t) const override;\n  /// Returns the function and the first two derivatives at an arbitrary time\n  /// `t`.\n  std::array<DataVector, 3> func_and_2_derivs(double t) const override;\n\n  /// Returns the domain of validity of the function.\n  std::array<double, 2> time_bounds() const override;\n\n  double expiration_after(double time) const override;\n\n  void truncate_at_time(double time) override;\n\n  /// Returns the velocity that the function approaches\n  double velocity() const;\n\n  /// Returns the timescale at which the function approaches a constant\n  /// velocity.\n  double decay_timescale() const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const FixedSpeedCubic& lhs,\n                         const FixedSpeedCubic& rhs);\n\n  friend std::ostream& operator<<(  // NOLINT(readability-redundant-declaration\n      std::ostream& os, const FixedSpeedCubic& fixed_speed_cubic);\n\n  template <size_t MaxDerivReturned = 2>\n  std::array<DataVector, MaxDerivReturned + 1> func_and_derivs(double t) const;\n\n  double initial_function_value_{std::numeric_limits<double>::signaling_NaN()};\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  double velocity_{std::numeric_limits<double>::signaling_NaN()};\n  double squared_decay_timescale_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const FixedSpeedCubic& lhs, const FixedSpeedCubic& rhs);\n}  // namespace FunctionsOfTime\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/FunctionOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <memory>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace domain {\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Contains functions of time to support the dual frame system.\nnamespace FunctionsOfTime {\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Base class for FunctionsOfTime\n///\n/// A FunctionOfTime is a function that will return the same value for\n/// a time `t`, regardless of when that function is called during a run\n/// (provided that the time `t` is in the domain of validity of the function).\n/// All FunctionsOfTime have members\n///   - `func`, that returns a `std::array<DataVector, 1>`\n///   - `func_and_deriv`, that returns a `std::array<DataVector, 2>`\n///   - `func_and_2_derivs`, that returns a `std::array<DataVector, 3>`\n///\n/// The DataVectors that are returned can be of any size: e.g. a scalar\n/// FunctionOfTime will have DataVectors with one component and a 3-vector\n/// FunctionOfTime will have DataVectors with three components.\n///\n/// The domain of validity of the function is given by the `time_bounds` member\n/// function.\n///\n/// The function and all of its derivatives are left-continuous, that\n/// is, when evaluated at a time when the function was updated, they\n/// return the values just before the update, ignoring the updated\n/// value.\nclass FunctionOfTime : public PUP::able {\n public:\n  FunctionOfTime() = default;\n  FunctionOfTime(FunctionOfTime&&) = default;\n  FunctionOfTime& operator=(FunctionOfTime&&) = default;\n  FunctionOfTime(const FunctionOfTime&) = default;\n  FunctionOfTime& operator=(const FunctionOfTime&) = default;\n  ~FunctionOfTime() override = default;\n\n  virtual auto get_clone() const -> std::unique_ptr<FunctionOfTime> = 0;\n\n  /// Create a FunctionOfTime at time \\p t as if one had created a new\n  /// FunctionOfTime with \\p t as its initial time, except the initial values of\n  /// new FunctionOfTime are the exact values of the old FunctionOfTime at time\n  /// \\p t, and the new expriation is \\p expiration_time.\n  ///\n  /// \\details This defaults to just `get_clone()` since some functions can't be\n  /// updated/don't expire.\n  virtual std::unique_ptr<FunctionOfTime> create_at_time(\n      const double /*t*/, const double /*expiration_time*/) const {\n    return get_clone();\n  }\n\n  /// Returns the domain of validity of the function.\n  /// For FunctionsOfTime that allow a small amount of time extrapolation,\n  /// `time_bounds` tells you the bounds including the allowed extrapolation\n  /// interval.\n  virtual std::array<double, 2> time_bounds() const = 0;\n\n  /// \\brief The first expiration time after \\p time.\n  ///\n  /// \\details For non-updatable functions, this returns infinity. For\n  /// updatable functions, the first expiration time after \\p time is\n  /// found by determining the update immediately before \\p time. The\n  /// expiration time of this update is what is returned. If \\p time\n  /// happens to be an update itself, then the expiration of that\n  /// update is returned.\n  virtual double expiration_after(double time) const = 0;\n\n  /// Updates the maximum derivative of the FunctionOfTime at a given time while\n  /// also resetting the expiration. By default, a FunctionOfTime cannot be\n  /// updated.\n  virtual void update(double /*time_of_update*/,\n                      DataVector /*updated_max_deriv*/,\n                      double /*next_expiration_time*/) {\n    ERROR(\"Cannot update this FunctionOfTime.\");\n  }\n\n  /// Discards data only needed before the passed time, to reduce\n  /// memory use.\n  ///\n  /// It should be safe to call this method concurrently with update\n  /// and access of any data not before \\p time.\n  virtual void truncate_at_time(double time) = 0;\n\n  /// The DataVector can be of any size\n  virtual std::array<DataVector, 1> func(double t) const = 0;\n  /// The DataVector can be of any size\n  virtual std::array<DataVector, 2> func_and_deriv(double t) const = 0;\n  /// The DataVector can be of any size\n  virtual std::array<DataVector, 3> func_and_2_derivs(double t) const = 0;\n\n  /// \\brief All derivatives a function of time has to offer (because it can be\n  /// more than 2)\n  ///\n  /// \\details Defaults to the return values from `func_and_2_derivs` since some\n  /// functions of time only go up to 2 derivatives by design\n  virtual std::vector<DataVector> func_and_all_derivs(double t) const {\n    std::array<DataVector, 3> tmp_func_and_2_derivs = func_and_2_derivs(t);\n\n    return std::vector{std::move(tmp_func_and_2_derivs[0]),\n                       std::move(tmp_func_and_2_derivs[1]),\n                       std::move(tmp_func_and_2_derivs[2])};\n  }\n\n  WRAPPED_PUPable_abstract(FunctionOfTime);  // NOLINT\n};\n}  // namespace FunctionsOfTime\n\nusing FunctionsOfTimeMap = std::unordered_map<\n    std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/IntegratedFunctionOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/IntegratedFunctionOfTime.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <iterator>\n#include <memory>\n#include <ostream>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/ThreadsafeList.hpp\"\n#include \"Domain/FunctionsOfTime/ThreadsafeList.tpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain::FunctionsOfTime {\n\nIntegratedFunctionOfTime::IntegratedFunctionOfTime() = default;\nIntegratedFunctionOfTime::IntegratedFunctionOfTime(IntegratedFunctionOfTime&&) =\n    default;\nIntegratedFunctionOfTime::IntegratedFunctionOfTime(\n    const IntegratedFunctionOfTime&) = default;\nauto IntegratedFunctionOfTime::operator=(IntegratedFunctionOfTime&&)\n    -> IntegratedFunctionOfTime& = default;\nauto IntegratedFunctionOfTime::operator=(const IntegratedFunctionOfTime&)\n    -> IntegratedFunctionOfTime& = default;\nIntegratedFunctionOfTime::~IntegratedFunctionOfTime() = default;\nIntegratedFunctionOfTime::IntegratedFunctionOfTime(\n    CkMigrateMessage* /*unused*/) {}\n\nIntegratedFunctionOfTime::IntegratedFunctionOfTime(\n    const double t, std::array<double, 2> initial_func_and_derivs,\n    const double expiration_time, const bool rotation)\n    : deriv_info_at_update_times_(t), rotation_(rotation) {\n  deriv_info_at_update_times_.insert(t, initial_func_and_derivs,\n                                     expiration_time);\n}\n\nstd::array<DataVector, 1> IntegratedFunctionOfTime::func(double t) const {\n  return func_and_derivs<0>(t);\n}\nstd::array<DataVector, 2> IntegratedFunctionOfTime::func_and_deriv(\n    double t) const {\n  return func_and_derivs<1>(t);\n}\n[[noreturn]] std::array<DataVector, 3>\nIntegratedFunctionOfTime::func_and_2_derivs(double /*t*/) const {\n  ERROR(\"Can only return first derivative.\");\n}\n\nstd::unique_ptr<FunctionOfTime> IntegratedFunctionOfTime::get_clone() const {\n  return std::make_unique<IntegratedFunctionOfTime>(*this);\n}\n\nstd::unique_ptr<FunctionOfTime> IntegratedFunctionOfTime::create_at_time(\n    const double t, const double expiration_time) const {\n  const auto initial_func_and_deriv = deriv_info_at_update_times_(t);\n  return std::make_unique<IntegratedFunctionOfTime>(\n      t, initial_func_and_deriv.data, expiration_time, rotation_);\n}\n\ntemplate <size_t MaxDerivReturned>\nstd::array<DataVector, MaxDerivReturned + 1>\nIntegratedFunctionOfTime::func_and_derivs(const double t) const {\n  static_assert(MaxDerivReturned <= 1, \"can only return 1 derivative\");\n\n  const auto deriv_info_at_t = deriv_info_at_update_times_(t);\n  std::array<DataVector, MaxDerivReturned + 1> result{};\n\n  if (rotation_) {\n    result.at(0) = DataVector{cos(deriv_info_at_t.data[0] / 2.), 0., 0.,\n                              sin(deriv_info_at_t.data[0] / 2.)};\n    if constexpr (MaxDerivReturned == 1) {\n      result.at(1) = DataVector{\n          -sin(deriv_info_at_t.data[0] / 2.) * deriv_info_at_t.data[1] / 2., 0.,\n          0., cos(deriv_info_at_t.data[0] / 2.) * deriv_info_at_t.data[1] / 2.};\n    }\n  } else {\n    for (size_t i = 0; i <= MaxDerivReturned; ++i) {\n      result.at(i) = DataVector{gsl::at(deriv_info_at_t.data, i)};\n    }\n  }\n  return result;\n}\n\nvoid IntegratedFunctionOfTime::update(const double time_of_update,\n                                      DataVector updated_value_and_derivative,\n                                      const double next_expiration_time) {\n  if (time_of_update < deriv_info_at_update_times_.expiration_time()) {\n    ERROR(\"Attempted to update from time \"\n          << time_of_update << \" to time\" << next_expiration_time\n          << \" but expiration time is \"\n          << deriv_info_at_update_times_.expiration_time());\n  }\n\n  ASSERT(updated_value_and_derivative.size() == 2,\n         \"The size of the DataVector should be 2: value and derivative\");\n  if (time_of_update != deriv_info_at_update_times_.expiration_time()) {\n    update_backlog_[time_of_update] = std::make_pair(\n        std::move(updated_value_and_derivative), next_expiration_time);\n    return;\n  }\n  const std::array<double, 2> func{updated_value_and_derivative[0],\n                                   updated_value_and_derivative[1]};\n  deriv_info_at_update_times_.insert(time_of_update, func,\n                                     next_expiration_time);\n  while (not update_backlog_.empty() and\n         update_backlog_.begin()->first == time_bounds()[1]) {\n    const double current_exp_time = time_bounds()[1];\n    const auto& item = update_backlog_[current_exp_time];\n    const std::array<double, 2> updated_func{item.first[0], item.first[1]};\n    deriv_info_at_update_times_.insert(current_exp_time, updated_func,\n                                       item.second);\n    update_backlog_.erase(current_exp_time);\n  }\n  deriv_info_at_update_times_.truncate_to_length(100);\n}\n\nstd::array<double, 2> IntegratedFunctionOfTime::time_bounds() const {\n  return {{deriv_info_at_update_times_.initial_time(),\n           deriv_info_at_update_times_.expiration_time()}};\n}\n\ndouble IntegratedFunctionOfTime::expiration_after(const double time) const {\n  return deriv_info_at_update_times_.expiration_after(time);\n}\n\nvoid IntegratedFunctionOfTime::truncate_at_time(const double time) {\n  deriv_info_at_update_times_.truncate_at_time(time);\n}\n\nvoid IntegratedFunctionOfTime::pup(PUP::er& p) {\n  FunctionOfTime::pup(p);\n  size_t version = 1;\n  p | version;\n  if (version >= 0) {\n    p | deriv_info_at_update_times_;\n    p | rotation_;\n    // Serializing the backlog is not threadsafe, and so was removed\n    // in version 1.  It should never have any entries during\n    // checkpointing, and should be safe to ignore during IO.\n    if (version == 0) {\n      p | update_backlog_;\n    }\n  }\n}\n\nbool operator==(const IntegratedFunctionOfTime& lhs,\n                const IntegratedFunctionOfTime& rhs) {\n  return lhs.deriv_info_at_update_times_ == rhs.deriv_info_at_update_times_ and\n         lhs.rotation_ == rhs.rotation_ and\n         lhs.update_backlog_ == rhs.update_backlog_;\n}\n\nbool operator!=(const IntegratedFunctionOfTime& lhs,\n                const IntegratedFunctionOfTime& rhs) {\n  return not(lhs == rhs);\n}\nPUP::able::PUP_ID IntegratedFunctionOfTime::my_PUP_ID = 0;  // NOLINT\n\n#define DIMRETURNED(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::array<DataVector, DIMRETURNED(data) + 1>                     \\\n  IntegratedFunctionOfTime::func_and_derivs<DIMRETURNED(data)>(const double) \\\n      const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1))\n\n#undef DIMRETURNED\n#undef INSTANTIATE\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/IntegratedFunctionOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <map>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/ThreadsafeList.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::FunctionsOfTime {\n/*!\n * \\brief A function that is integrated manually.\n * \\details This function only works with global time steppers that have\n * strictly positively increasing substeps. When evaluated at this global time\n * step, it returns the function and first derivative at that time step. It\n * therefore needs to be updated every time step with its current values.\n */\nclass IntegratedFunctionOfTime : public FunctionOfTime {\n public:\n  IntegratedFunctionOfTime();\n  IntegratedFunctionOfTime(IntegratedFunctionOfTime&&);\n  IntegratedFunctionOfTime(const IntegratedFunctionOfTime&);\n  IntegratedFunctionOfTime& operator=(IntegratedFunctionOfTime&&);\n  IntegratedFunctionOfTime& operator=(const IntegratedFunctionOfTime&);\n  ~IntegratedFunctionOfTime() override;\n  /*!\n   * \\brief Constructs the function using the initial time, initial values,\n   * derivative and expiration time. If `rotation` is true, the function will be\n   * converted to a quaternion before it is output as used by the `Rotation`\n   * map.\n   */\n  IntegratedFunctionOfTime(double t,\n                           std::array<double, 2> initial_func_and_derivs,\n                           double expiration_time, bool rotation);\n\n  // clang-tidy: google-runtime-references\n  // clang-tidy: cppcoreguidelines-owning-memory,-warnings-as-errors\n  WRAPPED_PUPable_decl_template(IntegratedFunctionOfTime);  // NOLINT\n\n  explicit IntegratedFunctionOfTime(CkMigrateMessage* /*unused*/);\n\n  auto get_clone() const -> std::unique_ptr<FunctionOfTime> override;\n\n  std::unique_ptr<FunctionOfTime> create_at_time(\n      double t, double expiration_time) const override;\n\n  std::array<DataVector, 1> func(double t) const override;\n  std::array<DataVector, 2> func_and_deriv(double t) const override;\n  [[noreturn]] std::array<DataVector, 3> func_and_2_derivs(\n      double /*t*/) const override;\n\n  /*!\n   * \\brief Updates the function to the next global time step. The\n   * `updated_value_and_derivative` argument needs to be a DataVector of size 2,\n   * with the zeroth element holding the function's value and the first element\n   * holding its derivative. If `rotation_` is set to true, this corresponds to\n   * the angle and the angular velocity for a rotation about the z-axis.\n   */\n  void update(double time_of_update, DataVector updated_value_and_derivative,\n              double next_expiration_time) override;\n\n  double expiration_after(double time) const override;\n\n  std::array<double, 2> time_bounds() const override;\n\n  void truncate_at_time(double time) override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(  // NOLINT(readability-redundant-declaration)\n      const IntegratedFunctionOfTime& lhs, const IntegratedFunctionOfTime& rhs);\n\n  template <size_t MaxDerivReturned>\n  std::array<DataVector, MaxDerivReturned + 1> func_and_derivs(double t) const;\n  FunctionOfTimeHelpers::ThreadsafeList<std::array<double, 2>>\n      deriv_info_at_update_times_;\n  std::map<double, std::pair<DataVector, double>> update_backlog_{};\n  bool rotation_ = false;\n};\n\nbool operator!=(const IntegratedFunctionOfTime& lhs,\n                const IntegratedFunctionOfTime& rhs);\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/OptionTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <unordered_map>\n\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace domain::FunctionsOfTime::OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Groups options for reading in FunctionOfTime data from SpEC\n */\nstruct CubicFunctionOfTimeOverride {\n  static constexpr Options::String help{\n      \"Options for importing FunctionOfTimes from SpEC\"};\n};\n\n/*!\n * \\brief Path to an H5 file containing SpEC FunctionOfTime data\n */\nstruct FunctionOfTimeFile {\n  using type = Options::Auto<std::string, Options::AutoLabel::None>;\n  static constexpr Options::String help{\n      \"Path to an H5 file containing SpEC FunctionOfTime data\"};\n  using group = CubicFunctionOfTimeOverride;\n};\n\n/*!\n * \\brief Pairs of strings mapping SpEC FunctionOfTime names to SpECTRE names\n */\nstruct FunctionOfTimeNameMap {\n  using type = std::map<std::string, std::string>;\n  static constexpr Options::String help{\n      \"String pairs mapping spec names to spectre names\"};\n  using group = CubicFunctionOfTimeOverride;\n};\n}  // namespace domain::FunctionsOfTime::OptionTags\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/OutputTimeBounds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/OutputTimeBounds.hpp\"\n\n#include <iomanip>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::FunctionsOfTime {\nstd::string output_time_bounds(\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  std::stringstream ss{};\n  ss << std::scientific << std::setprecision(16);\n  ss << \"FunctionsOfTime time bounds:\\n\";\n  for (const auto& [name, function_of_time] : functions_of_time) {\n    ss << \" \" << name << \": \" << function_of_time->time_bounds() << \"\\n\";\n  }\n\n  return ss.str();\n}\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/OutputTimeBounds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n\nnamespace domain::FunctionsOfTime {\n/*!\n * \\brief Put time bounds for all functions of time in a nicely formatted string\n *\n * All time bounds are printed in the following format:\n *\n * \\code\n * FunctionsOfTime time bounds:\n *  Name1: (0.0000000000000000e+00,1.0000000000000000e+00)\n *  Name2: (3.0000000000000000e+00,4.0000000000000000e+00)\n *  ...\n * \\endcode\n *\n */\nstd::string output_time_bounds(\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time);\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/PiecewisePolynomial.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <deque>\n#include <iterator>\n#include <limits>\n#include <map>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/ThreadsafeList.tpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::FunctionsOfTime {\ntemplate <size_t MaxDeriv>\nPiecewisePolynomial<MaxDeriv>::PiecewisePolynomial() = default;\n\ntemplate <size_t MaxDeriv>\nPiecewisePolynomial<MaxDeriv>::PiecewisePolynomial(PiecewisePolynomial&&) =\n    default;\n\ntemplate <size_t MaxDeriv>\nPiecewisePolynomial<MaxDeriv>::PiecewisePolynomial(const PiecewisePolynomial&) =\n    default;\n\ntemplate <size_t MaxDeriv>\nauto PiecewisePolynomial<MaxDeriv>::operator=(PiecewisePolynomial&&)\n    -> PiecewisePolynomial& = default;\n\ntemplate <size_t MaxDeriv>\nauto PiecewisePolynomial<MaxDeriv>::operator=(const PiecewisePolynomial&)\n    -> PiecewisePolynomial& = default;\n\ntemplate <size_t MaxDeriv>\nPiecewisePolynomial<MaxDeriv>::~PiecewisePolynomial() = default;\n\ntemplate <size_t MaxDeriv>\nPiecewisePolynomial<MaxDeriv>::PiecewisePolynomial(\n    const double t,\n    std::array<DataVector, MaxDeriv + 1> initial_func_and_derivs,\n    const double expiration_time)\n    : deriv_info_at_update_times_(t) {\n  store_entry(t, std::move(initial_func_and_derivs), expiration_time);\n}\n\ntemplate <size_t MaxDeriv>\nPiecewisePolynomial<MaxDeriv>::PiecewisePolynomial(\n    CkMigrateMessage* /*unused*/) {}\n\ntemplate <size_t MaxDeriv>\nstd::unique_ptr<FunctionOfTime> PiecewisePolynomial<MaxDeriv>::get_clone()\n    const {\n  return std::make_unique<PiecewisePolynomial>(*this);\n}\n\ntemplate <size_t MaxDeriv>\nstd::unique_ptr<FunctionOfTime> PiecewisePolynomial<MaxDeriv>::create_at_time(\n    const double t, const double expiration_time) const {\n  return std::make_unique<PiecewisePolynomial>(t, func_and_derivs(t),\n                                               expiration_time);\n}\n\ntemplate <size_t MaxDeriv>\ntemplate <size_t MaxDerivReturned>\nstd::array<DataVector, MaxDerivReturned + 1>\nPiecewisePolynomial<MaxDeriv>::func_and_derivs(const double t) const {\n  const auto deriv_info_at_t = deriv_info_at_update_times_(t);\n  const double dt = t - deriv_info_at_t.update;\n  const auto& coefs = deriv_info_at_t.data;\n\n  // initialize result for the number of derivs requested\n  std::array<DataVector, MaxDerivReturned + 1> result =\n      make_array<MaxDerivReturned + 1>(DataVector(coefs.back().size(), 0.0));\n\n  // evaluate the polynomial using ddpoly (Numerical Recipes sec 5.1)\n  result[0] = coefs[MaxDeriv];\n  for (size_t j = MaxDeriv; j-- > 0;) {\n    const size_t min_deriv = std::min(MaxDerivReturned, MaxDeriv - j);\n    for (size_t k = min_deriv; k > 0; k--) {\n      gsl::at(result, k) = gsl::at(result, k) * dt + gsl::at(result, k - 1);\n    }\n    result[0] = result[0] * dt + gsl::at(coefs, j);\n  }\n  // after the first derivative, factorial constants come in\n  double fact = 1.0;\n  for (size_t j = 2; j < MaxDerivReturned + 1; j++) {\n    fact *= j;\n    gsl::at(result, j) *= fact;\n  }\n\n  return result;\n}\n\ntemplate <size_t MaxDeriv>\nstd::vector<DataVector> PiecewisePolynomial<MaxDeriv>::func_and_all_derivs(\n    double t) const {\n  auto tmp_func_and_max_deriv = func_and_derivs(t);\n  std::vector<DataVector> result{};\n  result.resize(tmp_func_and_max_deriv.size());\n  for (size_t i = 0; i < tmp_func_and_max_deriv.size(); i++) {\n    result[i] = std::move(gsl::at(tmp_func_and_max_deriv, i));\n  }\n\n  return result;\n}\n\ntemplate <size_t MaxDeriv>\nvoid PiecewisePolynomial<MaxDeriv>::store_entry(\n    const double time_of_update,\n    std::array<DataVector, MaxDeriv + 1> func_and_derivs,\n    const double next_expiration_time) {\n  // Convert derivs to coefficients for polynomial evaluation\n  // The coefficient of x^N is the Nth deriv rescaled by 1/factorial(N)\n  double fact = 1.0;\n  for (size_t j = 2; j <= MaxDeriv; j++) {\n    fact *= static_cast<double>(j);\n    gsl::at(func_and_derivs, j) /= fact;\n  }\n  deriv_info_at_update_times_.insert(time_of_update, std::move(func_and_derivs),\n                                     next_expiration_time);\n}\n\ntemplate <size_t MaxDeriv>\nvoid PiecewisePolynomial<MaxDeriv>::update(const double time_of_update,\n                                           DataVector updated_max_deriv,\n                                           const double next_expiration_time) {\n  // This check is just to give a better error message.  The same\n  // condition will be checked internally by some of the operations\n  // below.  If this method is improperly called in parallel, the\n  // value of deriv_info_at_update_times_.expiration_time() can change\n  // during this function, but it is obtained atomically so at worst\n  // you get a nonsense error message.\n  if (time_of_update < deriv_info_at_update_times_.expiration_time()) {\n    ERROR(\"Attempted to update at time \"\n          << time_of_update << \" which is earlier than the expiration time \"\n          << deriv_info_at_update_times_.expiration_time());\n  }\n\n  // It is possible for updates to come out of order because of the\n  // asynchronous nature of our algorithm. Therefore, if we receive an update\n  // at a time later than the expiration time, we store that update and will\n  // apply it later.\n  if (time_of_update != deriv_info_at_update_times_.expiration_time()) {\n    update_backlog_[time_of_update] =\n        std::make_pair(std::move(updated_max_deriv), next_expiration_time);\n\n    return;\n  }\n\n  // NOLINTNEXTLINE(performance-unnecessary-value-param) false positive\n  const auto update_func = [this](const double time, DataVector updated_deriv,\n                                  const double expiration_time) {\n    auto func = func_and_derivs(time);\n\n    if (updated_deriv.size() != func.back().size()) {\n      ERROR(\"the number of components trying to be updated (\"\n            << updated_deriv.size()\n            << \") does not match the number of components (\"\n            << func.back().size() << \") in the PiecewisePolynomial.\");\n    }\n\n    func[MaxDeriv] = std::move(updated_deriv);\n    store_entry(time, std::move(func), expiration_time);\n  };\n\n  update_func(time_of_update, std::move(updated_max_deriv),\n              next_expiration_time);\n\n  // Go through the backlog and see if we have anything to update\n  while (not update_backlog_.empty() and\n         update_backlog_.begin()->first == time_bounds()[1]) {\n    auto entry = update_backlog_.begin();\n    const double new_update_time = entry->first;\n    DataVector& new_updated_deriv = entry->second.first;\n    const double new_expiration_time = entry->second.second;\n    update_func(new_update_time, std::move(new_updated_deriv),\n                new_expiration_time);\n    update_backlog_.erase(entry);\n  }\n}\n\ntemplate <size_t MaxDeriv>\nstd::array<double, 2> PiecewisePolynomial<MaxDeriv>::time_bounds() const {\n  return {{deriv_info_at_update_times_.initial_time(),\n           deriv_info_at_update_times_.expiration_time()}};\n}\n\ntemplate <size_t MaxDeriv>\ndouble PiecewisePolynomial<MaxDeriv>::expiration_after(\n    const double time) const {\n  return deriv_info_at_update_times_.expiration_after(time);\n}\n\ntemplate <size_t MaxDeriv>\nvoid PiecewisePolynomial<MaxDeriv>::truncate_at_time(const double time) {\n  deriv_info_at_update_times_.truncate_at_time(time);\n}\n\ntemplate <size_t MaxDeriv>\nvoid PiecewisePolynomial<MaxDeriv>::pup(PUP::er& p) {\n  FunctionOfTime::pup(p);\n  size_t version = 5;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n\n  if (version < 3) {\n    unpack_old_version(p, version);\n    return;\n  }\n\n  p | deriv_info_at_update_times_;\n\n  // Serializing the backlog is not threadsafe, and so was removed in\n  // version 5.  It should never have any entries during\n  // checkpointing, and should be safe to ignore during IO.\n  if (version == 4) {\n    p | update_backlog_;\n  }\n}\n\nnamespace {\ntemplate <size_t MaxDerivPlusOne>\nstruct LegacyStoredInfo {\n  LegacyStoredInfo() = default;\n  LegacyStoredInfo(double in_time,\n                   std::array<DataVector, 1> in_stored_quantities)\n      : time(in_time), stored_quantities(in_stored_quantities) {}\n\n  double time{std::numeric_limits<double>::signaling_NaN()};\n  std::array<DataVector, MaxDerivPlusOne> stored_quantities;\n\n  void pup(PUP::er& p) {\n    p | time;\n    p | stored_quantities;\n  }\n};\n}  // namespace\n\ntemplate <size_t MaxDeriv>\nvoid PiecewisePolynomial<MaxDeriv>::unpack_old_version(PUP::er& p,\n                                                       const size_t version) {\n  ASSERT(p.isUnpacking(), \"Can't serialize old version\");\n\n  std::vector<LegacyStoredInfo<MaxDeriv + 1>> info_vector{};\n  double expiration_time{};\n\n  // For versions 0 and 1, we stored the data first, then expiration time,\n  // then possibly the size\n  if (version <= 1) {\n    if (version == 0) {\n      // Version 0 had a std::vector\n      p | info_vector;\n    } else {\n      // Version 1 had a std::deque\n      std::deque<LegacyStoredInfo<MaxDeriv + 1>> pupped_info{};\n      p | pupped_info;\n      info_vector.assign(std::move_iterator(pupped_info.begin()),\n                         std::move_iterator(pupped_info.end()));\n    }\n\n    // Same for v0 and v1\n    p | expiration_time;\n\n    if (version == 1) {\n      uint64_t deriv_info_size{};\n      p | deriv_info_size;\n    }\n  } else if (version >= 2) {\n    // However, for v2+, we store expiration time, size, then data for\n    // thread-safety reasons\n    p | expiration_time;\n    size_t size = 0;\n    p | size;\n    info_vector.resize(size);\n    for (auto& deriv_info : info_vector) {\n      p | deriv_info;\n    }\n  }\n\n  deriv_info_at_update_times_ =\n      decltype(deriv_info_at_update_times_)(info_vector.front().time);\n  for (size_t i = 0; i < info_vector.size() - 1; ++i) {\n    deriv_info_at_update_times_.insert(\n        info_vector[i].time, std::move(info_vector[i].stored_quantities),\n        info_vector[i + 1].time);\n  }\n  deriv_info_at_update_times_.insert(\n      info_vector.back().time, std::move(info_vector.back().stored_quantities),\n      expiration_time);\n}\n\ntemplate <size_t MaxDeriv>\nbool operator==(const PiecewisePolynomial<MaxDeriv>& lhs,\n                const PiecewisePolynomial<MaxDeriv>& rhs) {\n  return lhs.deriv_info_at_update_times_ == rhs.deriv_info_at_update_times_;\n}\n\ntemplate <size_t MaxDeriv>\nbool operator!=(const PiecewisePolynomial<MaxDeriv>& lhs,\n                const PiecewisePolynomial<MaxDeriv>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t MaxDeriv>\nstd::ostream& operator<<(\n    std::ostream& os,\n    const PiecewisePolynomial<MaxDeriv>& piecewise_polynomial) {\n  const auto updates_begin =\n      piecewise_polynomial.deriv_info_at_update_times_.begin();\n  const auto updates_end =\n      piecewise_polynomial.deriv_info_at_update_times_.end();\n  if (updates_begin == updates_end) {\n    using ::operator<<;\n    os << \"backlog=\" << piecewise_polynomial.update_backlog_;\n    return os;\n  }\n  // We want to write the entries in order, but the iterator goes the\n  // other way.\n  std::vector iters{updates_begin};\n  {\n    for (;;) {\n      auto next = std::next(iters.back());\n      if (next == updates_end) {\n        break;\n      }\n      iters.push_back(std::move(next));\n    }\n  }\n  std::reverse(iters.begin(), iters.end());\n\n  for (size_t entry = 0; entry < iters.size(); ++entry) {\n    os << \"t=\" << iters[entry]->update << \": \";\n    for (size_t i = 0; i < MaxDeriv; ++i) {\n      os << gsl::at(iters[entry]->data, i) << \" \";\n    }\n    os << iters[entry]->data[MaxDeriv] << \"\\n\";\n  }\n  using ::operator<<;\n  os << \"backlog=\" << piecewise_polynomial.update_backlog_;\n  return os;\n}\n\n// do explicit instantiation of MaxDeriv = {0,1,2,3,4}\n// along with all combinations of MaxDerivReturned = {0,...,MaxDeriv}\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIMRETURNED(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template class PiecewisePolynomial<DIM(data)>;                              \\\n  template bool operator==<DIM(data)>(const PiecewisePolynomial<DIM(data)>&,  \\\n                                      const PiecewisePolynomial<DIM(data)>&); \\\n  template bool operator!=<DIM(data)>(const PiecewisePolynomial<DIM(data)>&,  \\\n                                      const PiecewisePolynomial<DIM(data)>&); \\\n  template std::ostream& operator<<(                                          \\\n      std::ostream& os,                                                       \\\n      const PiecewisePolynomial<DIM(data)>& piecewise_polynomial);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2, 3, 4))\n\n#undef INSTANTIATE\n\n#define INSTANTIATE(_, data)                                          \\\n  template std::array<DataVector, DIMRETURNED(data) + 1>              \\\n  PiecewisePolynomial<DIM(data)>::func_and_derivs<DIMRETURNED(data)>( \\\n      const double) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2, 3, 4), (0, 1, 2))\n\n#undef DIM\n#undef DIMRETURNED\n#undef INSTANTIATE\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/PiecewisePolynomial.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <map>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/ThreadsafeList.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace domain {\nnamespace FunctionsOfTime {\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief A function that has a piecewise-constant `MaxDeriv`th derivative.\n *\n * \\note This class conforms to the requirements of the\n * `Parallel::GlobalCache` for objects held by mutable global cache tags.\n */\ntemplate <size_t MaxDeriv>\nclass PiecewisePolynomial : public FunctionOfTime {\n public:\n  PiecewisePolynomial();\n  PiecewisePolynomial(PiecewisePolynomial&&);\n  PiecewisePolynomial(const PiecewisePolynomial&);\n  PiecewisePolynomial& operator=(PiecewisePolynomial&&);\n  PiecewisePolynomial& operator=(const PiecewisePolynomial&);\n  ~PiecewisePolynomial() override;\n\n  PiecewisePolynomial(\n      double t, std::array<DataVector, MaxDeriv + 1> initial_func_and_derivs,\n      double expiration_time);\n\n  explicit PiecewisePolynomial(CkMigrateMessage* /*unused*/);\n\n  auto get_clone() const -> std::unique_ptr<FunctionOfTime> override;\n\n  std::unique_ptr<FunctionOfTime> create_at_time(\n      double t, double expiration_time) const override;\n\n  // clang-tidy: google-runtime-references\n  // clang-tidy: cppcoreguidelines-owning-memory,-warnings-as-errors\n  WRAPPED_PUPable_decl_template(PiecewisePolynomial<MaxDeriv>);  // NOLINT\n\n  /// Returns the function at an arbitrary time `t`.  If `MaxDeriv` is\n  /// 0 and `update` has been called for time `t`, the updated value\n  /// is ignored.\n  std::array<DataVector, 1> func(double t) const override {\n    return func_and_derivs<0>(t);\n  }\n  /// Returns the function and its first derivative at an arbitrary time `t`.\n  /// If `MaxDeriv` is 1 and `update` has been called for time `t`, the updated\n  /// value is ignored.\n  std::array<DataVector, 2> func_and_deriv(double t) const override {\n    return func_and_derivs<1>(t);\n  }\n  /// Returns the function and the first two derivatives at an arbitrary time\n  /// `t`.  If `MaxDeriv` is 2 and `update` has been called for time `t`, the\n  /// updated value is ignored.\n  std::array<DataVector, 3> func_and_2_derivs(double t) const override {\n    return func_and_derivs<2>(t);\n  }\n\n  /// Return the function and all derivs up to and including the `MaxDeriv` at\n  /// an arbitrary time `t`.\n  std::vector<DataVector> func_and_all_derivs(double t) const override;\n\n  /// Returns the function and `MaxDerivReturned` derivatives at\n  /// an arbitrary time `t`.\n  /// The function has multiple components.\n  template <size_t MaxDerivReturned = MaxDeriv>\n  std::array<DataVector, MaxDerivReturned + 1> func_and_derivs(double t) const;\n\n  /// Updates the `MaxDeriv`th derivative of the function at the given time.\n  /// `updated_max_deriv` is a vector of the `MaxDeriv`ths for each component.\n  /// `next_expiration_time` is the next expiration time.\n  ///\n  /// The \\p time_of_update must be the same as the old expiration\n  /// time.  It is passed as a check that the calling code is\n  /// computing the other arguments with the correct value.\n  void update(double time_of_update, DataVector updated_max_deriv,\n              double next_expiration_time) override;\n\n  /// Returns the domain of validity of the function,\n  /// including the extrapolation region.\n  std::array<double, 2> time_bounds() const override;\n\n  double expiration_after(double time) const override;\n\n  void truncate_at_time(double time) override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  template <size_t LocalMaxDeriv>\n  friend bool operator==(  // NOLINT(readability-redundant-declaration)\n      const PiecewisePolynomial<LocalMaxDeriv>& lhs,\n      const PiecewisePolynomial<LocalMaxDeriv>& rhs);\n\n  template <size_t LocalMaxDeriv>\n  friend std::ostream& operator<<(  // NOLINT(readability-redundant-declaration\n      std::ostream& os,\n      const PiecewisePolynomial<LocalMaxDeriv>& piecewise_polynomial);\n\n  void unpack_old_version(PUP::er& p, size_t version);\n\n  void store_entry(double time_of_update,\n                   std::array<DataVector, MaxDeriv + 1> func_and_derivs,\n                   double next_expiration_time);\n\n  FunctionOfTimeHelpers::ThreadsafeList<std::array<DataVector, MaxDeriv + 1>>\n      deriv_info_at_update_times_;\n  std::map<double, std::pair<DataVector, double>> update_backlog_{};\n};\n\ntemplate <size_t MaxDeriv>\nbool operator!=(const PiecewisePolynomial<MaxDeriv>& lhs,\n                const PiecewisePolynomial<MaxDeriv>& rhs);\n\n/// \\cond\ntemplate <size_t MaxDeriv>\nPUP::able::PUP_ID PiecewisePolynomial<MaxDeriv>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace FunctionsOfTime\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/QuaternionFunctionOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n\n// BoostMultiArray is used internally in odeint, so BoostMultiArray MUST be\n// included before odeint\n#include \"DataStructures/BoostMultiArray.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <boost/math/quaternion.hpp>\n#include <boost/numeric/odeint.hpp>\n#include <cstddef>\n#include <cstdint>\n#include <deque>\n#include <iterator>\n#include <limits>\n#include <map>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/ThreadsafeList.tpp\"\n#include \"NumericalAlgorithms/OdeIntegration/OdeIntegration.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/PupBoost.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain::FunctionsOfTime {\ntemplate <size_t MaxDeriv>\nQuaternionFunctionOfTime<MaxDeriv>::QuaternionFunctionOfTime() = default;\n\ntemplate <size_t MaxDeriv>\nQuaternionFunctionOfTime<MaxDeriv>::QuaternionFunctionOfTime(\n    QuaternionFunctionOfTime&&) = default;\n\ntemplate <size_t MaxDeriv>\nQuaternionFunctionOfTime<MaxDeriv>::QuaternionFunctionOfTime(\n    const QuaternionFunctionOfTime&) = default;\n\ntemplate <size_t MaxDeriv>\nauto QuaternionFunctionOfTime<MaxDeriv>::operator=(QuaternionFunctionOfTime&&)\n    -> QuaternionFunctionOfTime& = default;\n\ntemplate <size_t MaxDeriv>\nauto QuaternionFunctionOfTime<MaxDeriv>::operator=(\n    const QuaternionFunctionOfTime&) -> QuaternionFunctionOfTime& = default;\n\ntemplate <size_t MaxDeriv>\nQuaternionFunctionOfTime<MaxDeriv>::~QuaternionFunctionOfTime() = default;\n\ntemplate <size_t MaxDeriv>\nQuaternionFunctionOfTime<MaxDeriv>::QuaternionFunctionOfTime(\n    const double t, const std::array<DataVector, 1>& initial_quat_func,\n    std::array<DataVector, MaxDeriv + 1> initial_angle_func,\n    const double expiration_time)\n    : stored_quaternions_and_times_(t),\n      angle_f_of_t_(t, std::move(initial_angle_func), expiration_time) {\n  stored_quaternions_and_times_.insert(\n      t, datavector_to_quaternion(initial_quat_func[0]), expiration_time);\n}\n\ntemplate <size_t MaxDeriv>\nQuaternionFunctionOfTime<MaxDeriv>::QuaternionFunctionOfTime(\n    CkMigrateMessage* /*unused*/) {}\n\ntemplate <size_t MaxDeriv>\nstd::unique_ptr<FunctionOfTime> QuaternionFunctionOfTime<MaxDeriv>::get_clone()\n    const {\n  return std::make_unique<QuaternionFunctionOfTime>(*this);\n}\n\ntemplate <size_t MaxDeriv>\nstd::unique_ptr<FunctionOfTime>\nQuaternionFunctionOfTime<MaxDeriv>::create_at_time(\n    const double t, const double expiration_time) const {\n  return std::make_unique<QuaternionFunctionOfTime>(\n      t, func(t), angle_f_of_t_.func_and_derivs(t), expiration_time);\n}\n\ntemplate <size_t MaxDeriv>\nstd::array<double, 2> QuaternionFunctionOfTime<MaxDeriv>::time_bounds() const {\n  return std::array{stored_quaternions_and_times_.initial_time(),\n                    stored_quaternions_and_times_.expiration_time()};\n}\n\ntemplate <size_t MaxDeriv>\ndouble QuaternionFunctionOfTime<MaxDeriv>::expiration_after(\n    const double time) const {\n  return stored_quaternions_and_times_.expiration_after(time);\n}\n\ntemplate <size_t MaxDeriv>\nvoid QuaternionFunctionOfTime<MaxDeriv>::pup(PUP::er& p) {\n  FunctionOfTime::pup(p);\n  size_t version = 6;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n\n  if (version < 4) {\n    unpack_old_version(p, version);\n    return;\n  }\n\n  p | stored_quaternions_and_times_;\n  p | angle_f_of_t_;\n\n  // Serializing the backlog is not threadsafe, and so was removed in\n  // version 6.  It should never have any entries during\n  // checkpointing, and should be safe to ignore during IO.\n  if (version == 5) {\n    p | update_backlog_;\n  }\n}\n\nnamespace {\nstruct LegacyStoredInfo {\n  LegacyStoredInfo() = default;\n  LegacyStoredInfo(double in_time,\n                   std::array<DataVector, 1> in_stored_quantities)\n      : time(in_time), stored_quantities(in_stored_quantities) {}\n\n  double time{std::numeric_limits<double>::signaling_NaN()};\n  std::array<DataVector, 1> stored_quantities;\n\n  void pup(PUP::er& p) {\n    p | time;\n    p | stored_quantities;\n  }\n};\n}  // namespace\n\ntemplate <size_t MaxDeriv>\nvoid QuaternionFunctionOfTime<MaxDeriv>::unpack_old_version(\n    PUP::er& p, const size_t version) {\n  ASSERT(p.isUnpacking(), \"Can't serialize old version\");\n\n  std::vector<LegacyStoredInfo> quaternions{};\n  double expiration_time{};\n\n  // For versions 0 and 1, we stored the data first, then angle fot, then\n  // possibly the size\n  if (version <= 1) {\n    if (version == 0) {\n      // Version 0 had a std::vector\n      p | quaternions;\n    } else {\n      // Version 1 had a std::deque\n      std::deque<LegacyStoredInfo> pupped_quaternions{};\n      p | pupped_quaternions;\n      quaternions.assign(std::move_iterator(pupped_quaternions.begin()),\n                         std::move_iterator(pupped_quaternions.end()));\n    }\n\n    // Same for v0 and v1\n    p | angle_f_of_t_;\n    expiration_time = angle_f_of_t_.time_bounds()[1];\n\n    if (version == 1) {\n      uint64_t stored_quaternion_size{};\n      p | stored_quaternion_size;\n    }\n  } else if (version >= 2) {\n    // However, for v2+, we store angle fot, expiration time, size, then data\n    // for thread-safety reasons\n    p | angle_f_of_t_;\n\n    // For v3+ we pup our own expiration time, while for 2 we have to get it\n    // from the angle_f_of_t_.\n    if (version >= 3) {\n      p | expiration_time;\n    } else {\n      expiration_time = angle_f_of_t_.time_bounds()[1];\n    }\n\n    size_t size = 0;\n    p | size;\n    quaternions.resize(size);\n    for (auto& stored_quaternion : quaternions) {\n      p | stored_quaternion;\n    }\n  }\n\n  stored_quaternions_and_times_ =\n      decltype(stored_quaternions_and_times_)(quaternions.front().time);\n  for (size_t i = 0; i < quaternions.size() - 1; ++i) {\n    stored_quaternions_and_times_.insert(\n        quaternions[i].time,\n        datavector_to_quaternion(quaternions[i].stored_quantities[0]),\n        quaternions[i + 1].time);\n  }\n  stored_quaternions_and_times_.insert(\n      quaternions.back().time,\n      datavector_to_quaternion(quaternions.back().stored_quantities[0]),\n      expiration_time);\n}\n\ntemplate <size_t MaxDeriv>\nvoid QuaternionFunctionOfTime<MaxDeriv>::update(\n    const double time_of_update, DataVector updated_max_deriv,\n    const double next_expiration_time) {\n  angle_f_of_t_.update(time_of_update, std::move(updated_max_deriv),\n                       next_expiration_time);\n\n  // If the stored PP didn't update, then don't compute the new quaternion, just\n  // store the times.\n  const double angle_expiration_time = angle_f_of_t_.time_bounds()[1];\n  if (angle_expiration_time < next_expiration_time) {\n    update_backlog_[time_of_update] = next_expiration_time;\n    return;\n  }\n\n  // We add this time to the backlog even though it doesn't need to be added\n  // just to make the loop easier. It'll be removed\n  update_backlog_[time_of_update] = next_expiration_time;\n\n  while (not update_backlog_.empty() and\n         update_backlog_.begin()->second <= angle_expiration_time) {\n    auto entry = update_backlog_.begin();\n    const double stored_time_of_update = entry->first;\n    const double stored_expiration_time = entry->second;\n\n    const auto old_interval =\n        stored_quaternions_and_times_(stored_time_of_update);\n    boost::math::quaternion<double> quaternion_to_integrate = old_interval.data;\n\n    solve_quaternion_ode(make_not_null(&quaternion_to_integrate),\n                         old_interval.update, old_interval.expiration);\n\n    normalize_quaternion(make_not_null(&quaternion_to_integrate));\n\n    stored_quaternions_and_times_.insert(\n        stored_time_of_update, quaternion_to_integrate, stored_expiration_time);\n\n    update_backlog_.erase(entry);\n  }\n}\n\ntemplate <size_t MaxDeriv>\nvoid QuaternionFunctionOfTime<MaxDeriv>::truncate_at_time(const double time) {\n  stored_quaternions_and_times_.truncate_at_time(time);\n  angle_f_of_t_.truncate_at_time(time);\n}\n\ntemplate <size_t MaxDeriv>\nvoid QuaternionFunctionOfTime<MaxDeriv>::solve_quaternion_ode(\n    const gsl::not_null<boost::math::quaternion<double>*>\n        quaternion_to_integrate,\n    const double t0, const double t) const {\n  // Boost is stupid and assumes the times it's integrating between are order\n  // unity. So if t or t0 is > 10, then you can't take t-t0 to machine precision\n  // which causes roundoff error within boost. It was found experimentally that\n  // an infinite loop *within boost* is possible if such a condition is met.\n  // Thus, we rescale the times and the RHS by this factor to ensure the times\n  // are of order unity to avoid this infinite loop. The resulting quaternion is\n  // left unchanged.\n  const double factor = std::max(1.0, std::max(t0, t));\n\n  // lambda that stores the internals of the ode\n  const auto quaternion_ode_system =\n      [this, &factor](const boost::math::quaternion<double>& state,\n                      boost::math::quaternion<double>& dt_state,\n                      const double time) {\n        // multiply time and rhs by factor for reasons explained above\n        const boost::math::quaternion<double> omega = datavector_to_quaternion(\n            angle_f_of_t_.func_and_deriv(time * factor)[1]);\n        dt_state = factor * 0.5 * state * omega;\n      };\n\n  // Dense stepper\n  auto dense_stepper = boost::numeric::odeint::make_dense_output(\n      1.0e-12, 1.0e-14,\n      boost::numeric::odeint::runge_kutta_dopri5<\n          boost::math::quaternion<double>, double,\n          boost::math::quaternion<double>, double,\n          boost::numeric::odeint::vector_space_algebra>{});\n\n  // Integrate from t0 / factor to t / factor (see explanation above), storing\n  // result in quaternion_to_integrate\n  boost::numeric::odeint::integrate_adaptive(\n      dense_stepper, quaternion_ode_system, *quaternion_to_integrate,\n      t0 / factor, t / factor, 1e-4);\n}\n\ntemplate <size_t MaxDeriv>\nboost::math::quaternion<double> QuaternionFunctionOfTime<MaxDeriv>::setup_func(\n    const double t) const {\n  // Get quaternion and time at closest time before t\n  const auto stored_info_at_t0 = stored_quaternions_and_times_(t);\n  boost::math::quaternion<double> quat_to_integrate = stored_info_at_t0.data;\n\n  // Solve the ode and store the result in quat_to_integrate\n  solve_quaternion_ode(make_not_null(&quat_to_integrate),\n                       stored_info_at_t0.update, t);\n\n  // Make unit quaternion\n  normalize_quaternion(make_not_null(&quat_to_integrate));\n\n  return quat_to_integrate;\n}\n\ntemplate <size_t MaxDeriv>\nstd::array<DataVector, 1> QuaternionFunctionOfTime<MaxDeriv>::quat_func(\n    const double t) const {\n  return std::array<DataVector, 1>{quaternion_to_datavector(setup_func(t))};\n}\n\ntemplate <size_t MaxDeriv>\nstd::array<DataVector, 2>\nQuaternionFunctionOfTime<MaxDeriv>::quat_func_and_deriv(const double t) const {\n  boost::math::quaternion<double> quat = setup_func(t);\n\n  // Get angle and however many derivatives we need\n  std::array<DataVector, 2> angle_and_deriv = angle_f_of_t_.func_and_deriv(t);\n\n  boost::math::quaternion<double> omega =\n      datavector_to_quaternion(angle_and_deriv[1]);\n\n  boost::math::quaternion<double> dtquat = 0.5 * quat * omega;\n\n  return std::array<DataVector, 2>{quaternion_to_datavector(quat),\n                                   quaternion_to_datavector(dtquat)};\n}\n\ntemplate <size_t MaxDeriv>\nstd::array<DataVector, 3>\nQuaternionFunctionOfTime<MaxDeriv>::quat_func_and_2_derivs(\n    const double t) const {\n  boost::math::quaternion<double> quat = setup_func(t);\n\n  // Get angle and however many derivatives we need\n  std::array<DataVector, 3> angle_and_2_derivs =\n      angle_f_of_t_.func_and_2_derivs(t);\n\n  boost::math::quaternion<double> omega =\n      datavector_to_quaternion(angle_and_2_derivs[1]);\n  boost::math::quaternion<double> dtomega =\n      datavector_to_quaternion(angle_and_2_derivs[2]);\n\n  boost::math::quaternion<double> dtquat = 0.5 * quat * omega;\n  boost::math::quaternion<double> dt2quat =\n      0.5 * (dtquat * omega + quat * dtomega);\n\n  return std::array<DataVector, 3>{quaternion_to_datavector(quat),\n                                   quaternion_to_datavector(dtquat),\n                                   quaternion_to_datavector(dt2quat)};\n}\n\ntemplate <size_t MaxDeriv>\nstd::array<DataVector, 4>\nQuaternionFunctionOfTime<MaxDeriv>::quat_func_and_3_derivs(\n    const double t) const {\n  boost::math::quaternion<double> quat = setup_func(t);\n\n  // Get angle and however many derivatives we need\n  std::vector<DataVector> angle_and_all_derivs =\n      angle_f_of_t_.func_and_all_derivs(t);\n\n  if (angle_and_all_derivs.size() < 4) {\n    ERROR(\n        \"Need more angle derivs to compute the third derivative of the \"\n        \"quaternion. Currently only have \"\n        << MaxDeriv);\n  }\n\n  boost::math::quaternion<double> omega =\n      datavector_to_quaternion(angle_and_all_derivs[1]);\n  boost::math::quaternion<double> dtomega =\n      datavector_to_quaternion(angle_and_all_derivs[2]);\n\n  boost::math::quaternion<double> dt2omega =\n      datavector_to_quaternion(angle_and_all_derivs[3]);\n\n  boost::math::quaternion<double> dtquat = 0.5 * quat * omega;\n  boost::math::quaternion<double> dt2quat =\n      0.5 * (dtquat * omega + quat * dtomega);\n  boost::math::quaternion<double> dt3quat =\n      0.5 * (dt2quat * omega + 2.0 * dtquat * dtomega + quat * dt2omega);\n\n  return std::array<DataVector, 4>{\n      quaternion_to_datavector(quat), quaternion_to_datavector(dtquat),\n      quaternion_to_datavector(dt2quat), quaternion_to_datavector(dt3quat)};\n}\n\ntemplate <size_t MaxDeriv>\nbool operator==(const QuaternionFunctionOfTime<MaxDeriv>& lhs,\n                const QuaternionFunctionOfTime<MaxDeriv>& rhs) {\n  return lhs.stored_quaternions_and_times_ ==\n             rhs.stored_quaternions_and_times_ and\n         lhs.angle_f_of_t_ == rhs.angle_f_of_t_;\n}\n\ntemplate <size_t MaxDeriv>\nbool operator!=(const QuaternionFunctionOfTime<MaxDeriv>& lhs,\n                const QuaternionFunctionOfTime<MaxDeriv>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t MaxDeriv>\nstd::ostream& operator<<(\n    std::ostream& os,\n    const QuaternionFunctionOfTime<MaxDeriv>& quaternion_f_of_t) {\n  const auto quaternions_begin =\n      quaternion_f_of_t.stored_quaternions_and_times_.begin();\n  const auto quaternions_end =\n      quaternion_f_of_t.stored_quaternions_and_times_.end();\n  // We want to write the entries in order, but the iterator goes the\n  // other way.\n  std::vector<std::decay_t<decltype(quaternions_begin)>> iters{};\n  if (quaternions_begin != quaternions_end) {\n    iters.push_back(quaternions_begin);\n    for (;;) {\n      auto next = std::next(iters.back());\n      if (next == quaternions_end) {\n        break;\n      }\n      iters.push_back(std::move(next));\n    }\n  }\n  std::reverse(iters.begin(), iters.end());\n\n  os << \"Quaternion:\\n\";\n  for (const auto& entry : iters) {\n    os << \"t=\" << entry->update << \": \" << entry->data << \"\\n\";\n  }\n  using ::operator<<;\n  os << \"backlog=\" << quaternion_f_of_t.update_backlog_ << \"\\n\";\n  os << \"Angle:\\n\";\n  os << quaternion_f_of_t.angle_f_of_t_;\n  return os;\n}\n\n// do explicit instantiation of MaxDeriv = {2,3}\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                   \\\n  template class QuaternionFunctionOfTime<DIM(data)>;          \\\n  template bool operator==                                     \\\n      <DIM(data)>(const QuaternionFunctionOfTime<DIM(data)>&,  \\\n                  const QuaternionFunctionOfTime<DIM(data)>&); \\\n  template bool operator!=                                     \\\n      <DIM(data)>(const QuaternionFunctionOfTime<DIM(data)>&,  \\\n                  const QuaternionFunctionOfTime<DIM(data)>&); \\\n  template std::ostream& operator<<(                           \\\n      std::ostream& os, const QuaternionFunctionOfTime<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/math/quaternion.hpp>\n#include <cstddef>\n#include <map>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/ThreadsafeList.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace domain::FunctionsOfTime {\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief A FunctionOfTime that stores quaternions for the rotation map\n *\n * \\details This FunctionOfTime stores quaternions that will be used in the\n * time-dependent rotation map as well as the orbital angular velocity that\n * will be controlled by the rotation control sytem. To get the quaternion, an\n * ODE is solved of the form \\f$ \\dot{q} = \\frac{1}{2} q \\times \\omega \\f$\n * where \\f$ \\omega \\f$ is the orbital angular velocity which is stored\n * internally as the derivative of an angle `PiecewisePolynomial`, and\n * \\f$ \\times \\f$ here is quaternion multiplication.\n *\n * Different from a `PiecewisePolynomial`, only the quaternion\n * itself is stored, not any of the derivatives because the derivatives must be\n * calculated from the solved ODE at every function call. Because\n * derivatives of the quaternion are not stored, the template parameter\n * `MaxDeriv` refers to both the max derivative of the stored angle\n * PiecewisePolynomial and the max derivative returned by the\n * QuaternionFunctionOfTime. The `update` function is then just a wrapper\n * around the internal `PiecewisePolynomial::update` function with the addition\n * that it then updates the stored quaternions as well.\n *\n * \\note The initial rotation angles passed to the angle PiecewisePolynomial\n * don't matter as we never actually use the angles themselves. We only use\n * their derivatives (angular velocity) to determine map parameters. In theory\n * we could determine each initial angle from the input axis-angle\n * representation, but we don't need to.\n *\n * The angle PiecewisePolynomial is accessible through the `angle_func`,\n * `angle_func_and_deriv`, and `angle_func_and_2_derivs` functions which\n * correspond to the function calls of a normal PiecewisePolynomial except\n * without the `angle_` prefix.\n *\n * It is encouraged to use `quat_func` and `angle_func` when you want the\n * specific values of the functions to avoid ambiguity in what you are\n * calling. However, the original three `func` functions inherited from the\n * FunctionOfTime base class are necessary because the maps use the generic\n * `func` functions, thus they return the quaternion and its derivatives (which\n * are needed for the map). This is all to keep the symmetry of naming\n * `angle_func` and `quat_func` so that function calls won't be ambiguous.\n *\n * \\note This class conforms to the requirements of the\n * `Parallel::GlobalCache` for objects held by mutable global cache tags.\n */\ntemplate <size_t MaxDeriv>\nclass QuaternionFunctionOfTime : public FunctionOfTime {\n public:\n  QuaternionFunctionOfTime();\n  QuaternionFunctionOfTime(QuaternionFunctionOfTime&&);\n  QuaternionFunctionOfTime(const QuaternionFunctionOfTime&);\n  QuaternionFunctionOfTime& operator=(QuaternionFunctionOfTime&&);\n  QuaternionFunctionOfTime& operator=(const QuaternionFunctionOfTime&);\n  ~QuaternionFunctionOfTime() override;\n\n  QuaternionFunctionOfTime(\n      double t, const std::array<DataVector, 1>& initial_quat_func,\n      std::array<DataVector, MaxDeriv + 1> initial_angle_func,\n      double expiration_time);\n\n  // LCOV_EXCL_START\n  explicit QuaternionFunctionOfTime(CkMigrateMessage* /*unused*/);\n  // LCOV_EXCL_STOP\n\n  auto get_clone() const -> std::unique_ptr<FunctionOfTime> override;\n\n  std::unique_ptr<FunctionOfTime> create_at_time(\n      double t, double expiration_time) const override;\n\n  // clang-tidy: google-runtime-references\n  // clang-tidy: cppcoreguidelines-owning-memory,-warnings-as-errors\n  WRAPPED_PUPable_decl_template(QuaternionFunctionOfTime<MaxDeriv>);  // NOLINT\n\n  /// Returns domain of validity for the function of time\n  std::array<double, 2> time_bounds() const override;\n\n  double expiration_after(double time) const override;\n\n  /// Updates the `MaxDeriv`th derivative of the angle piecewisepolynomial at\n  /// the given time, then updates the stored quaternions.\n  ///\n  /// `updated_max_deriv` is a datavector of the `MaxDeriv`s for each component.\n  /// `next_expiration_time` is the next expiration time.\n  ///\n  /// The \\p time_of_update must be the same as the old expiration\n  /// time.  It is passed as a check that the calling code is\n  /// computing the other arguments with the correct value.\n  void update(double time_of_update, DataVector updated_max_deriv,\n              double next_expiration_time) override;\n\n  void truncate_at_time(double time) override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// Returns the quaternion at an arbitrary time `t`.\n  std::array<DataVector, 1> func(const double t) const override {\n    return quat_func(t);\n  }\n\n  /// Returns the quaternion and its first derivative at an arbitrary time `t`.\n  std::array<DataVector, 2> func_and_deriv(const double t) const override {\n    return quat_func_and_deriv(t);\n  }\n\n  /// Returns the quaternion and the first two derivatives at an arbitrary\n  /// time `t`.\n  std::array<DataVector, 3> func_and_2_derivs(const double t) const override {\n    return quat_func_and_2_derivs(t);\n  }\n\n  /// Returns the quaternion at an arbitrary time `t`.\n  std::array<DataVector, 1> quat_func(double t) const;\n\n  /// Returns the quaternion and its first derivative at an arbitrary time `t`.\n  std::array<DataVector, 2> quat_func_and_deriv(double t) const;\n\n  /// Returns the quaternion and the first two derivatives at an arbitrary\n  /// time `t`.\n  std::array<DataVector, 3> quat_func_and_2_derivs(double t) const;\n\n  /// Returns the quaternion and the first three derivatives at an arbitrary\n  /// time `t`.\n  std::array<DataVector, 4> quat_func_and_3_derivs(double t) const;\n\n  /// Returns stored angle at an arbitrary time `t`.\n  std::array<DataVector, 1> angle_func(const double t) const {\n    return angle_f_of_t_.func(t);\n  }\n\n  /// Returns stored angle and its first derivative (omega) at an arbitrary time\n  /// `t`.\n  std::array<DataVector, 2> angle_func_and_deriv(const double t) const {\n    return angle_f_of_t_.func_and_deriv(t);\n  }\n\n  /// Returns stored angle and the first two derivatives at an arbitrary\n  /// time `t`.\n  std::array<DataVector, 3> angle_func_and_2_derivs(const double t) const {\n    return angle_f_of_t_.func_and_2_derivs(t);\n  }\n\n  std::vector<DataVector> angle_func_and_all_derivs(const double t) const {\n    return angle_f_of_t_.func_and_all_derivs(t);\n  }\n\n private:\n  template <size_t LocalMaxDeriv>\n  friend bool operator==(  // NOLINT(readability-redundant-declaration)\n      const QuaternionFunctionOfTime<LocalMaxDeriv>& lhs,\n      const QuaternionFunctionOfTime<LocalMaxDeriv>& rhs);\n\n  template <size_t LocalMaxDeriv>\n  friend std::ostream& operator<<(  // NOLINT(readability-redundant-declaration)\n      std::ostream& os,\n      const QuaternionFunctionOfTime<LocalMaxDeriv>& quaternion_f_of_t);\n\n  FunctionOfTimeHelpers::ThreadsafeList<boost::math::quaternion<double>>\n      stored_quaternions_and_times_{};\n  domain::FunctionsOfTime::PiecewisePolynomial<MaxDeriv> angle_f_of_t_{};\n  std::map<double, double> update_backlog_{};\n\n  void unpack_old_version(PUP::er& p, size_t version);\n\n  /// Integrates the ODE \\f$ \\dot{q} = \\frac{1}{2} q \\times \\omega \\f$ from time\n  /// `t0` to time `t`. On input, `quaternion_to_integrate` is the initial\n  /// quaternion at time `t0` and on output, it stores the result at time `t`\n  void solve_quaternion_ode(\n      gsl::not_null<boost::math::quaternion<double>*> quaternion_to_integrate,\n      double t0, double t) const;\n\n  /// Does common operations to all the `func` functions such as updating stored\n  /// info, solving the ODE, and returning the normalized quaternion as a boost\n  /// quaternion for easy calculations\n  boost::math::quaternion<double> setup_func(double t) const;\n};\n\ntemplate <size_t MaxDeriv>\nbool operator!=(const QuaternionFunctionOfTime<MaxDeriv>& lhs,\n                const QuaternionFunctionOfTime<MaxDeriv>& rhs);\n\n/// \\cond\ntemplate <size_t MaxDeriv>\nPUP::able::PUP_ID QuaternionFunctionOfTime<MaxDeriv>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/QuaternionHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/QuaternionHelpers.hpp\"\n\n#include <boost/math/quaternion.hpp>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nDataVector quaternion_to_datavector(\n    const boost::math::quaternion<double>& input) {\n  return DataVector{input.R_component_1(), input.R_component_2(),\n                    input.R_component_3(), input.R_component_4()};\n}\n\nboost::math::quaternion<double> datavector_to_quaternion(\n    const DataVector& input) {\n  ASSERT(input.size() == 3 or input.size() == 4,\n         \"To form a quaternion, a DataVector can either have 3 or 4 components \"\n         \"only. This DataVector has \"\n             << input.size() << \" components.\");\n  if (input.size() == 3) {\n    return boost::math::quaternion<double>(0.0, input[0], input[1], input[2]);\n  } else {\n    return boost::math::quaternion<double>(input[0], input[1], input[2],\n                                           input[3]);\n  }\n}\n\n// Normalize a `boost::math::quaternion`\nvoid normalize_quaternion(\n    const gsl::not_null<boost::math::quaternion<double>*> input) {\n  *input /= abs(*input);\n}\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/QuaternionHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines helper functions for converting between `DataVector`s and boost\n/// quaternions.\n\n#pragma once\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n\n#include <boost/math/quaternion.hpp>\n#include <boost/numeric/odeint.hpp>\n\n#include \"DataStructures/DataVector.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// Convert a `boost::math::quaternion` to a `DataVector`\nDataVector quaternion_to_datavector(\n    const boost::math::quaternion<double>& input);\n\n/// \\brief Convert a `DataVector` to a `boost::math::quaternion`\n///\n/// \\details To convert to a quaternion, a `DataVector` must have either 3 or 4\n/// components. If it has 3 components, the quaternion will be constructed with\n/// 0 scalar part while the vector part is the `DataVector`. If the `DataVector`\n/// has 4 components, the quaternion is just the `DataVector` itself.\nboost::math::quaternion<double> datavector_to_quaternion(\n    const DataVector& input);\n\n/// Normalize a `boost::math::quaternion`\nvoid normalize_quaternion(\n    gsl::not_null<boost::math::quaternion<double>*> input);\n\n// Necessary for odeint to be able to integrate boost quaternions\nnamespace boost::numeric::odeint {\ntemplate <>\nstruct vector_space_norm_inf<boost::math::quaternion<double>> {\n  using result_type = double;\n  result_type operator()(const boost::math::quaternion<double>& q) const {\n    return sup(q);\n  }\n};\n}  // namespace boost::numeric::odeint\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/IntegratedFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstant.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::FunctionsOfTime {\nvoid register_derived_with_charm() {\n  register_classes_with_charm<FunctionsOfTime::FixedSpeedCubic,\n                              FunctionsOfTime::IntegratedFunctionOfTime,\n                              FunctionsOfTime::PiecewisePolynomial<0>,\n                              FunctionsOfTime::PiecewisePolynomial<1>,\n                              FunctionsOfTime::PiecewisePolynomial<2>,\n                              FunctionsOfTime::PiecewisePolynomial<3>,\n                              FunctionsOfTime::PiecewisePolynomial<4>,\n                              FunctionsOfTime::QuaternionFunctionOfTime<2>,\n                              FunctionsOfTime::QuaternionFunctionOfTime<3>,\n                              FunctionsOfTime::SettleToConstant,\n                              FunctionsOfTime::SettleToConstantQuaternion>();\n}\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace domain {\nnamespace FunctionsOfTime {\nvoid register_derived_with_charm();\n}  // namespace FunctionsOfTime\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/SettleToConstant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/SettleToConstant.hpp\"\n\n#include <cmath>\n#include <memory>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain::FunctionsOfTime {\nSettleToConstant::SettleToConstant(\n    const std::array<DataVector, 3>& initial_func_and_derivs,\n    const double match_time, const double decay_time)\n    : match_time_(match_time), inv_decay_time_(1.0 / decay_time) {\n  // F = f(t0)\n  // the constants are then computed from F and its derivs:\n  // C = -dtF-dt2F*decay_time;\n  // B = (C-dtF)*decay_time;\n  // A = F-B;\n  coef_c_ =\n      -initial_func_and_derivs[1] - initial_func_and_derivs[2] * decay_time;\n  coef_b_ = (coef_c_ - initial_func_and_derivs[1]) * decay_time;\n  coef_a_ = initial_func_and_derivs[0] - coef_b_;\n}\n\nstd::unique_ptr<FunctionOfTime> SettleToConstant::get_clone() const {\n  return std::make_unique<SettleToConstant>(*this);\n}\n\nstd::array<DataVector, 1> SettleToConstant::func(const double t) const {\n  return func_and_derivs<0>(t);\n}\n\nstd::array<DataVector, 2> SettleToConstant::func_and_deriv(\n    const double t) const {\n  return func_and_derivs<1>(t);\n}\n\nstd::array<DataVector, 3> SettleToConstant::func_and_2_derivs(\n    const double t) const {\n  return func_and_derivs<2>(t);\n}\n\nstd::array<double, 2> SettleToConstant::time_bounds() const {\n  return {{match_time_, std::numeric_limits<double>::infinity()}};\n}\n\ndouble SettleToConstant::expiration_after(const double /*time*/) const {\n  return std::numeric_limits<double>::infinity();\n}\n\nvoid SettleToConstant::truncate_at_time(const double /*time*/) {}\n\ntemplate <size_t MaxDerivReturned>\nstd::array<DataVector, MaxDerivReturned + 1> SettleToConstant::func_and_derivs(\n    const double t) const {\n  static_assert(MaxDerivReturned < 3, \"The maximum available derivative is 2.\");\n\n  // initialize result for the number of derivs requested\n  std::array<DataVector, MaxDerivReturned + 1> result =\n      make_array<MaxDerivReturned + 1>(DataVector(coef_a_.size(), 0.0));\n\n  const double dt = t - match_time_;\n  const double ex = exp(-dt * inv_decay_time_);\n\n  gsl::at(result, 0) = coef_a_ + (coef_b_ + coef_c_ * dt) * ex;\n  if (MaxDerivReturned > 0) {\n    gsl::at(result, 1) = ex * (coef_c_ * (1.0 - dt * inv_decay_time_) -\n                               coef_b_ * inv_decay_time_);\n    if (MaxDerivReturned > 1) {\n      gsl::at(result, 2) =\n          ex * inv_decay_time_ *\n          (coef_c_ * (inv_decay_time_ * dt - 2.0) + inv_decay_time_ * coef_b_);\n    }\n  }\n\n  return result;\n}\n\nvoid SettleToConstant::pup(PUP::er& p) {\n  FunctionOfTime::pup(p);\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | coef_a_;\n    p | coef_b_;\n    p | coef_c_;\n    p | match_time_;\n    p | inv_decay_time_;\n  }\n}\n\nbool operator==(const SettleToConstant& lhs, const SettleToConstant& rhs) {\n  return lhs.coef_a_ == rhs.coef_a_ and lhs.coef_b_ == rhs.coef_b_ and\n         lhs.coef_c_ == rhs.coef_c_ and lhs.match_time_ == rhs.match_time_ and\n         lhs.inv_decay_time_ == rhs.inv_decay_time_;\n}\n\nbool operator!=(const SettleToConstant& lhs, const SettleToConstant& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID SettleToConstant::my_PUP_ID = 0;  // NOLINT\n\n#define DERIV(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                       \\\n  template std::array<DataVector, DERIV(data) + 1> \\\n  SettleToConstant::func_and_derivs<DERIV(data)>(const double) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2))\n\n#undef DERIV\n#undef INSTANTIATE\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/SettleToConstant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace domain {\nnamespace FunctionsOfTime {\n/// \\ingroup ControlSystemGroup\n/// \\brief Given an initial function of time, transitions the map to a\n/// constant-in-time value.\n///\n/// Given an initial function \\f$f(t)\\f$ and its first two derivatives\n/// at the matching time \\f$t_0\\f$, the constant coefficients \\f$A,B,C\\f$\n/// are computed such that the resulting function of time\n/// \\f$g(t)\\f$ satisfies \\f$g(t=t_0)=f(t=t_0)\\f$ and\n/// approaches a constant value for \\f$t > t_0\\f$ on a timescale\n/// of \\f$\\tau\\f$. The resultant\n/// function is \\f[ g(t) = A + (B+C(t-t_0)) e^{-(t-t_0)/\\tau} \\f]\n/// where \\f$\\tau\\f$=`decay_time` and \\f$t_0\\f$=`match_time`.\nclass SettleToConstant : public FunctionOfTime {\n public:\n  SettleToConstant() = default;\n  SettleToConstant(const std::array<DataVector, 3>& initial_func_and_derivs,\n                   double match_time, double decay_time);\n\n  ~SettleToConstant() override = default;\n  SettleToConstant(SettleToConstant&&) = default;\n  SettleToConstant& operator=(SettleToConstant&&) = default;\n  SettleToConstant(const SettleToConstant&) = default;\n  SettleToConstant& operator=(const SettleToConstant&) = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  WRAPPED_PUPable_decl_template(SettleToConstant);\n\n  explicit SettleToConstant(CkMigrateMessage* /*unused*/) {}\n\n  auto get_clone() const -> std::unique_ptr<FunctionOfTime> override;\n\n  /// Returns the function at an arbitrary time `t`.\n  std::array<DataVector, 1> func(double t) const override;\n  /// Returns the function and its first derivative at an arbitrary time `t`.\n  std::array<DataVector, 2> func_and_deriv(double t) const override;\n  /// Returns the function and the first two derivatives at an arbitrary time\n  /// `t`.\n  std::array<DataVector, 3> func_and_2_derivs(double t) const override;\n\n  /// Returns the domain of validity of the function.\n  std::array<double, 2> time_bounds() const override;\n\n  double expiration_after(double time) const override;\n\n  void truncate_at_time(double time) override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const SettleToConstant& lhs,\n                         const SettleToConstant& rhs);\n\n  template <size_t MaxDerivReturned = 2>\n  std::array<DataVector, MaxDerivReturned + 1> func_and_derivs(double t) const;\n\n  DataVector coef_a_, coef_b_, coef_c_;\n  double match_time_{std::numeric_limits<double>::signaling_NaN()};\n  double inv_decay_time_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const SettleToConstant& lhs, const SettleToConstant& rhs);\n}  // namespace FunctionsOfTime\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/SettleToConstantQuaternion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <memory>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\ntemplate <size_t MaxDerivReturned>\nstd::array<double, MaxDerivReturned + 1> quat_norm_and_derivs(\n    const std::array<DataVector, MaxDerivReturned + 1>&\n        unnormalized_func_and_derivs) {\n  std::array<double, MaxDerivReturned + 1> result =\n      make_array<MaxDerivReturned + 1>(0.0);\n  gsl::at(result, 0) = norm(gsl::at(unnormalized_func_and_derivs, 0));\n  if constexpr (MaxDerivReturned > 0) {\n    gsl::at(result, 1) = sum(gsl::at(unnormalized_func_and_derivs, 0) *\n                             gsl::at(unnormalized_func_and_derivs, 1)) /\n                         gsl::at(result, 0);\n    if constexpr (MaxDerivReturned > 1) {\n      gsl::at(result, 2) =\n          sum(square(gsl::at(unnormalized_func_and_derivs, 1)) +\n              gsl::at(unnormalized_func_and_derivs, 0) *\n                  gsl::at(unnormalized_func_and_derivs, 2)) /\n          gsl::at(result, 0);\n    }\n  }\n  return result;\n}\n}  // namespace\n\nnamespace domain::FunctionsOfTime {\nSettleToConstantQuaternion::SettleToConstantQuaternion(\n    const std::array<DataVector, 3>& initial_func_and_derivs,\n    const double match_time, const double decay_time)\n    : unnormalized_function_of_time_(initial_func_and_derivs, match_time,\n                                     decay_time),\n      match_time_(match_time),\n      decay_time_(decay_time) {\n  if (initial_func_and_derivs[0].size() != 4) {\n    ERROR(\n        \"SettleToConstantQuaternion requires the initial function and its time \"\n        \"derivatives to be quaternions stored as DataVectors of size 4, not \"\n        \"size \"\n        << initial_func_and_derivs[0].size());\n  }\n  if (not equal_within_roundoff(\n          gsl::at(quat_norm_and_derivs<2>(initial_func_and_derivs), 0), 1.0)) {\n    ERROR(\n        \"SettleToConstantQuaternion requires that the initial quaternion \"\n        \"should be a quaternion with a norm of 1.0, not \"\n        << gsl::at(quat_norm_and_derivs<2>(initial_func_and_derivs), 0));\n  }\n}\n\nstd::unique_ptr<FunctionOfTime> SettleToConstantQuaternion::get_clone() const {\n  return std::make_unique<SettleToConstantQuaternion>(*this);\n}\n\nstd::array<DataVector, 1> SettleToConstantQuaternion::func(\n    const double t) const {\n  return func_and_derivs<0>(t);\n}\n\nstd::array<DataVector, 2> SettleToConstantQuaternion::func_and_deriv(\n    const double t) const {\n  return func_and_derivs<1>(t);\n}\n\nstd::array<DataVector, 3> SettleToConstantQuaternion::func_and_2_derivs(\n    const double t) const {\n  return func_and_derivs<2>(t);\n}\n\nstd::array<double, 2> SettleToConstantQuaternion::time_bounds() const {\n  return {{match_time_, std::numeric_limits<double>::infinity()}};\n}\n\ndouble SettleToConstantQuaternion::expiration_after(\n    const double /*time*/) const {\n  return std::numeric_limits<double>::infinity();\n}\n\nvoid SettleToConstantQuaternion::truncate_at_time(const double /*time*/) {}\n\ntemplate <size_t MaxDerivReturned>\nstd::array<DataVector, MaxDerivReturned + 1>\nSettleToConstantQuaternion::func_and_derivs(const double t) const {\n  static_assert(MaxDerivReturned < 3, \"The maximum available derivative is 2.\");\n\n  std::array<DataVector, MaxDerivReturned + 1> result{};\n  if constexpr (MaxDerivReturned == 0) {\n    result = unnormalized_function_of_time_.func(t);\n  } else if constexpr (MaxDerivReturned == 1) {\n    result = unnormalized_function_of_time_.func_and_deriv(t);\n  } else if constexpr (MaxDerivReturned == 2) {\n    result = unnormalized_function_of_time_.func_and_2_derivs(t);\n  }\n  std::array<double, MaxDerivReturned + 1> norm_and_derivs =\n      quat_norm_and_derivs<MaxDerivReturned>(result);\n  result /= gsl::at(norm_and_derivs, 0);\n  if constexpr (MaxDerivReturned > 0) {\n    gsl::at(result, 1) -= gsl::at(result, 0) * gsl::at(norm_and_derivs, 1) /\n                          gsl::at(norm_and_derivs, 0);\n    if constexpr (MaxDerivReturned > 1) {\n      gsl::at(result, 2) -= gsl::at(result, 1) * 2.0 *\n                            gsl::at(norm_and_derivs, 1) /\n                            gsl::at(norm_and_derivs, 0);\n      gsl::at(result, 2) -= gsl::at(result, 0) * gsl::at(norm_and_derivs, 2) /\n                            gsl::at(norm_and_derivs, 0);\n    }\n  }\n\n  return result;\n}\n\nvoid SettleToConstantQuaternion::pup(PUP::er& p) {\n  FunctionOfTime::pup(p);\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  if (version >= 0) {\n    p | unnormalized_function_of_time_;\n    p | match_time_;\n    p | decay_time_;\n  }\n}\n\nbool operator==(const SettleToConstantQuaternion& lhs,\n                const SettleToConstantQuaternion& rhs) {\n  return lhs.unnormalized_function_of_time_ ==\n             rhs.unnormalized_function_of_time_ and\n         lhs.match_time_ == rhs.match_time_ and\n         lhs.decay_time_ == rhs.decay_time_;\n}\n\nbool operator!=(const SettleToConstantQuaternion& lhs,\n                const SettleToConstantQuaternion& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID SettleToConstantQuaternion::my_PUP_ID = 0;  // NOLINT\n\n#define DERIV(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template std::array<DataVector, DERIV(data) + 1>                       \\\n  SettleToConstantQuaternion::func_and_derivs<DERIV(data)>(const double) \\\n      const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2))\n\n#undef DERIV\n#undef INSTANTIATE\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstant.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace domain::FunctionsOfTime {\n/// \\ingroup ControlSystemGroup\n/// \\brief Given an initial function of time that is a unit quaternion,\n/// transitions to a constant-in-time unit quaternion.\n///\n/// Given an initial unit quaternion \\f$\\mathbf{f}(t)\\f$ and its first two\n/// derivatives at the matching time \\f$t_0\\f$, return a function of time\n/// \\f$\\mathbf{g}(t)\\f$ satisfies \\f$\\mathbf{g}(t=t_0)=\\mathbf{f}f(t=t_0)\\f$ and\n/// approaches a constant value for \\f$t > t_0\\f$ on a timescale of \\f$\\tau\\f$.\n/// This is done by internally holding a `SettleToConstant` function of time\n/// initialized from \\f$\\mathbf{f}(t)\\f$ and its first two derivatives at the\n/// matching time, but then ensuring that \\f$\\mathbf{g}(t)\\f$ remains\n/// a unit quaternion.\nclass SettleToConstantQuaternion : public FunctionOfTime {\n public:\n  SettleToConstantQuaternion() = default;\n  SettleToConstantQuaternion(\n      const std::array<DataVector, 3>& initial_func_and_derivs,\n      double match_time, double decay_time);\n\n  ~SettleToConstantQuaternion() override = default;\n  SettleToConstantQuaternion(SettleToConstantQuaternion&&) = default;\n  SettleToConstantQuaternion& operator=(SettleToConstantQuaternion&&) = default;\n  SettleToConstantQuaternion(const SettleToConstantQuaternion&) = default;\n  SettleToConstantQuaternion& operator=(const SettleToConstantQuaternion&) =\n      default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  WRAPPED_PUPable_decl_template(SettleToConstantQuaternion);\n\n  explicit SettleToConstantQuaternion(CkMigrateMessage* /*unused*/) {}\n\n  auto get_clone() const -> std::unique_ptr<FunctionOfTime> override;\n\n  /// Returns the function at an arbitrary time `t`.\n  std::array<DataVector, 1> func(double t) const override;\n  /// Returns the function and its first derivative at an arbitrary time `t`.\n  std::array<DataVector, 2> func_and_deriv(double t) const override;\n  /// Returns the function and the first two derivatives at an arbitrary time\n  /// `t`.\n  std::array<DataVector, 3> func_and_2_derivs(double t) const override;\n\n  /// Returns the domain of validity of the function.\n  std::array<double, 2> time_bounds() const override;\n\n  double expiration_after(double time) const override;\n\n  void truncate_at_time(double time) override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const SettleToConstantQuaternion& lhs,\n                         const SettleToConstantQuaternion& rhs);\n\n  template <size_t MaxDerivReturned = 2>\n  std::array<DataVector, MaxDerivReturned + 1> func_and_derivs(double t) const;\n\n  SettleToConstant unnormalized_function_of_time_;\n  double match_time_{std::numeric_limits<double>::signaling_NaN()};\n  double decay_time_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator!=(const SettleToConstantQuaternion& lhs,\n                const SettleToConstantQuaternion& rhs);\n}  // namespace domain::FunctionsOfTime\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\n/// \\cond\nnamespace domain::FunctionsOfTime {\nstruct FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n/// \\endcond\n\nnamespace domain::Tags {\n/// Tag to retrieve the FunctionsOfTime from the GlobalCache.\nstruct FunctionsOfTime : db::SimpleTag {\n  using type = std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;\n};\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/ThreadsafeList.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <atomic>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <mutex>\n#include <optional>\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain::FunctionsOfTime::FunctionOfTimeHelpers {\nnamespace ThreadsafeList_detail {\ntemplate <typename T>\nstruct Interval;\n}  // namespace ThreadsafeList_detail\n\n/// A list of time intervals that allows safe access to existing\n/// elements even during modification.\n///\n/// Concurrent modification is not supported.  All non-modifying\n/// operations except for serialization can be safely performed in\n/// parallel with each other and with `insert` and will return a\n/// consistent state.\ntemplate <typename T>\nclass ThreadsafeList {\n private:\n  using Interval = ThreadsafeList_detail::Interval<T>;\n\n public:\n  /// No operations are valid on a default-initialized object except\n  /// for assignment and deserialization.\n  ThreadsafeList();\n  ThreadsafeList(ThreadsafeList&& other);\n  ThreadsafeList(const ThreadsafeList& other);\n  ThreadsafeList& operator=(ThreadsafeList&& other);\n  ThreadsafeList& operator=(const ThreadsafeList& other);\n  ~ThreadsafeList();\n\n  explicit ThreadsafeList(double initial_time);\n\n  /// Insert data valid over the interval from \\p update_time to \\p\n  /// expiration_time.\n  ///\n  /// The \\p update_time must be the same as the old expiration time.\n  /// It is passed as a check that the calling code is computing the\n  /// other arguments with the correct value.\n  void insert(double update_time, T data, double expiration_time);\n\n  struct IntervalInfo {\n    double update;\n    const T& data;\n    double expiration;\n\n    friend bool operator==(const IntervalInfo& a, const IntervalInfo& b) {\n      return a.update == b.update and a.data == b.data and\n             a.expiration == b.expiration;\n    }\n    friend bool operator!=(const IntervalInfo& a, const IntervalInfo& b) {\n      return not(a == b);\n    }\n  };\n\n  /// Obtain the start time, data, and expiration time for the time\n  /// interval containing \\p time.\n  ///\n  /// If \\p time is at an interval boundary, the earlier interval is\n  /// returned, except for the initial time, which returns the first\n  /// interval.\n  IntervalInfo operator()(double time) const;\n\n  double initial_time() const;\n\n  /// The expiration time of the last data interval.\n  double expiration_time() const;\n\n  /// The first interval boundary after \\p time.  If \\p time is an\n  /// interval boundary, the one after it is returned.\n  double expiration_after(double time) const;\n\n  /// Remove the oldest data in the list, leaving at least \\p length\n  /// entries.\n  ///\n  /// If the list is truncated to zero length, the caller must ensure\n  /// that no concurrent access or modification is performed.  The\n  /// list is guaranteed to be empty afterwards.\n  ///\n  /// Otherwise, it is the caller's responsibility to ensure that,\n  /// during this call, no reader attempts to look up what will become\n  /// the new oldest interval.  Storing references to that interval's\n  /// data is safe, and lookups of that interval are safe if they are\n  /// sequenced after this call.  Concurrent insertions are safe, and\n  /// concurrent access to newer intervals is safe.  Concurrent calls\n  /// to truncate functions and serialization are protected by\n  /// mutexes, but other concurrent calls are lock-free.\n  ///\n  /// If the list is initially shorter than \\p length, it is left\n  /// unchanged.  Otherwise, if concurrent insertions occur, the\n  /// amount of removed data is approximate, but will not leave the\n  /// list shorter than the requested value.  In the absence of\n  /// concurrent modifications, the list will be left with exactly the\n  /// requested length.\n  void truncate_to_length(size_t length);\n\n  /// Remove the oldest data in the list, retaining data needed to\n  /// access at least as far back as \\p time.\n  ///\n  /// If \\p time is before `initial_time()`, the list is not modified.\n  /// If \\p time is after `expiration_time()`, the amount of removed\n  /// data is unspecified.\n  ///\n  /// The amount of removed data is approximate.  There may be\n  /// slightly more data remaining after a call than requested.\n  ///\n  /// It is safe to insert and access data concurrently with\n  /// truncation, as long as the accessed times are not before the\n  /// truncation time.  Concurrent calls to truncate functions and\n  /// serialization are protected by mutexes, but other concurrent\n  /// calls are lock-free.\n  void truncate_at_time(double time);\n\n  /// Empty the list.  Equivalent to `truncate_to_length(0)`.  See\n  /// that method for details.\n  void clear();\n\n  void pup(PUP::er& p);\n\n  class iterator {\n   public:\n    using iterator_category = std::input_iterator_tag;\n    using value_type = IntervalInfo;\n    using reference = value_type;\n    using pointer = std::optional<value_type>;\n    using difference_type = std::ptrdiff_t;\n\n    iterator() = default;\n    iterator& operator++();\n    iterator operator++(int);\n    reference operator*() const;\n    pointer operator->() const;\n\n    friend bool operator==(const iterator& a, const iterator& b) {\n      return a.interval_ == b.interval_;\n    }\n    friend bool operator!=(const iterator& a, const iterator& b) {\n      return not(a == b);\n    }\n\n   private:\n    friend class ThreadsafeList;\n\n    iterator(const ThreadsafeList* parent, const Interval* interval);\n\n    const ThreadsafeList* parent_ = nullptr;\n    const Interval* interval_ = nullptr;\n  };\n\n  /// Iterate over all the intervals in the list, in reverse order\n  /// (i.e., most recent first).\n  /// @{\n  iterator begin() const;\n  iterator end() const;\n  /// @}\n\n private:\n  // interval_after_boundary controls behavior at boundary points.\n  // True -> later interval, false -> earlier.  Initial time always\n  // gives the first interval.\n  const Interval& find_interval(double time,\n                                bool interval_after_boundary) const;\n\n  alignas(64) std::atomic<double> initial_time_ =\n      std::numeric_limits<double>::signaling_NaN();\n  // Pad memory to avoid false-sharing when accessing initial_time_\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  char unused_padding_initial_time_[64 - (sizeof(initial_time_) % 64)] = {};\n  std::unique_ptr<Interval> interval_list_{};\n  alignas(64) std::atomic<Interval*> most_recent_interval_{};\n  // Pad memory to avoid false-sharing when accessing most_recent_interval_\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  char\n      unused_padding_most_recent_interval_[64 - (sizeof(most_recent_interval_) %\n                                                 64)] = {};\n  // Mutex protecting methods that access the tail of the list, i.e.,\n  // truncation and serialization.  Other methods are assumed to be\n  // called with parameters that will not interact with truncation.\n  std::mutex truncation_mutex_{};\n};\n\ntemplate <typename T>\nbool operator==(const ThreadsafeList<T>& a, const ThreadsafeList<T>& b);\n\ntemplate <typename T>\nbool operator!=(const ThreadsafeList<T>& a, const ThreadsafeList<T>& b);\n}  // namespace domain::FunctionsOfTime::FunctionOfTimeHelpers\n"
  },
  {
    "path": "src/Domain/FunctionsOfTime/ThreadsafeList.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/FunctionsOfTime/ThreadsafeList.hpp\"\n\n#include <atomic>\n#include <memory>\n#include <mutex>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace domain::FunctionsOfTime::FunctionOfTimeHelpers {\nnamespace ThreadsafeList_detail {\ntemplate <typename T>\nstruct Interval {\n  Interval() = default;\n  Interval(double in_expiration, T in_data,\n           std::unique_ptr<Interval> in_previous)\n      : expiration(in_expiration),\n        data(std::move(in_data)),\n        previous(std::move(in_previous)) {}\n  double expiration{};\n  T data{};\n  std::unique_ptr<Interval> previous{};\n\n  void pup(PUP::er& p);\n};\n\ntemplate <typename T>\nvoid Interval<T>::pup(PUP::er& p) {\n  p | expiration;\n  p | data;\n  p | previous;\n}\n}  // namespace ThreadsafeList_detail\n\ntemplate <typename T>\nThreadsafeList<T>::ThreadsafeList() = default;\n\ntemplate <typename T>\nThreadsafeList<T>::ThreadsafeList(ThreadsafeList&& other) {\n  *this = std::move(other);\n}\n\ntemplate <typename T>\nThreadsafeList<T>::ThreadsafeList(const ThreadsafeList& other) {\n  *this = other;\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::operator=(ThreadsafeList&& other) -> ThreadsafeList& {\n  if (this == &other) {\n    return *this;\n  }\n  initial_time_.store(other.initial_time_.load(std::memory_order_acquire),\n                      std::memory_order_release);\n  interval_list_ = std::move(other.interval_list_);\n  most_recent_interval_.store(interval_list_.get(), std::memory_order_release);\n  other.most_recent_interval_.store(nullptr, std::memory_order_release);\n  return *this;\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::operator=(const ThreadsafeList& other)\n    -> ThreadsafeList& {\n  if (this == &other) {\n    return *this;\n  }\n  initial_time_.store(other.initial_time_.load(std::memory_order_acquire),\n                      std::memory_order_release);\n\n  std::unique_ptr<Interval>* previous_pointer = &interval_list_;\n  for (auto&& entry : other) {\n    // make_unique doesn't work on aggregates until C++20\n    previous_pointer->reset(\n        new Interval{entry.expiration, entry.data, nullptr});\n    previous_pointer = &(*previous_pointer)->previous;\n  }\n\n  most_recent_interval_.store(interval_list_.get(), std::memory_order_release);\n  return *this;\n}\n\ntemplate <typename T>\nThreadsafeList<T>::~ThreadsafeList() = default;\n\ntemplate <typename T>\nThreadsafeList<T>::ThreadsafeList(const double initial_time)\n    : initial_time_(initial_time), most_recent_interval_(nullptr) {}\n\ntemplate <typename T>\nvoid ThreadsafeList<T>::insert(const double update_time, T data,\n                               const double expiration_time) {\n  auto* old_interval = most_recent_interval_.load(std::memory_order_acquire);\n  const double old_expiration =\n      old_interval != nullptr ? old_interval->expiration\n                              : initial_time_.load(std::memory_order_acquire);\n  if (old_expiration != update_time) {\n    ERROR(\"Tried to insert at time \"\n          << update_time << \", which is not the old expiration time \"\n          << old_expiration);\n  }\n  if (expiration_time <= update_time) {\n    ERROR(\"Expiration time \" << expiration_time << \" is not after update time \"\n                             << update_time);\n  }\n  // make_unique doesn't work on aggregates until C++20\n  std::unique_ptr<Interval> new_interval(new Interval{\n      expiration_time, std::move(data), std::move(interval_list_)});\n  auto* const new_interval_p = new_interval.get();\n  interval_list_ = std::move(new_interval);\n  if (not most_recent_interval_.compare_exchange_strong(\n          old_interval, new_interval_p, std::memory_order_acq_rel)) {\n    ERROR(\"Attempt at concurrent modification detected.\");\n  }\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::operator()(const double time) const -> IntervalInfo {\n  const auto& interval = find_interval(time, false);\n  if (interval.previous != nullptr) {\n    return {interval.previous->expiration, interval.data, interval.expiration};\n  }\n\n  const double initial_time = initial_time_.load(std::memory_order_acquire);\n  if (time < initial_time and not equal_within_roundoff(time, initial_time)) {\n    ERROR(\"Requested time \" << time << \" precedes earliest time \"\n                            << initial_time);\n  }\n  return {initial_time, interval.data, interval.expiration};\n}\n\ntemplate <typename T>\ndouble ThreadsafeList<T>::initial_time() const {\n  return initial_time_.load(std::memory_order_acquire);\n}\n\ntemplate <typename T>\ndouble ThreadsafeList<T>::expiration_time() const {\n  auto* interval = most_recent_interval_.load(std::memory_order_acquire);\n  return interval != nullptr ? interval->expiration\n                             : initial_time_.load(std::memory_order_acquire);\n}\n\ntemplate <typename T>\ndouble ThreadsafeList<T>::expiration_after(const double time) const {\n  return find_interval(time, true).expiration;\n}\n\ntemplate <typename T>\nvoid ThreadsafeList<T>::truncate_to_length(const size_t length) {\n  if (length == 0) {\n    clear();\n    return;\n  }\n\n  const std::lock_guard lock(truncation_mutex_);\n  auto* last_interval = most_recent_interval_.load(std::memory_order_acquire);\n  if (last_interval == nullptr or last_interval->previous == nullptr) {\n    return;\n  }\n  for (size_t i = 1; i < length; ++i) {\n    last_interval = last_interval->previous.get();\n    if (last_interval->previous == nullptr) {\n      return;\n    }\n  }\n\n  initial_time_.store(last_interval->previous->expiration);\n  last_interval->previous.reset();\n}\n\ntemplate <typename T>\nvoid ThreadsafeList<T>::truncate_at_time(const double time) {\n  const std::lock_guard lock(truncation_mutex_);\n  auto* last_interval = most_recent_interval_.load(std::memory_order_acquire);\n  // For simplicity, never empty the list.  We don't guarantee exactness.\n  if (last_interval == nullptr or last_interval->previous == nullptr) {\n    return;\n  }\n  for (;;) {\n    last_interval = last_interval->previous.get();\n    if (last_interval->previous == nullptr) {\n      return;\n    }\n    // Keep one extra interval by checking the current interval's\n    // expiration time rather than it's update time, so that the\n    // expiration time of the previous interval can be accessed.\n    // Removing it and setting the initial_time would violate the\n    // thread-safety guarantees.\n    if (last_interval->expiration < time) {\n      break;\n    }\n  }\n\n  initial_time_.store(last_interval->previous->expiration);\n  last_interval->previous.reset();\n}\n\ntemplate <typename T>\nvoid ThreadsafeList<T>::clear() {\n  // This method has no thread-safety guarantees, so we don't have to\n  // be careful with modification sequencing.\n  if (interval_list_ == nullptr) {\n    return;\n  }\n  initial_time_.store(interval_list_->expiration);\n  most_recent_interval_.store(nullptr, std::memory_order_release);\n  interval_list_.reset();\n}\n\ntemplate <typename T>\nvoid ThreadsafeList<T>::pup(PUP::er& p) {\n  size_t version = 0;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n\n  if (version != 0) {\n    ERROR(\"Unrecognized version \" << version);\n  }\n\n  const std::lock_guard lock(truncation_mutex_);\n  p | initial_time_;\n  if (p.isUnpacking()) {\n    bool empty{};\n    p | empty;\n    interval_list_.reset();\n    if (not empty) {\n      interval_list_ = std::make_unique<Interval>();\n      p | *interval_list_;\n    }\n    most_recent_interval_.store(interval_list_.get(),\n                                std::memory_order_release);\n  } else {\n    const Interval* const threadsafe_interval_list =\n        most_recent_interval_.load(std::memory_order_acquire);\n    bool empty = threadsafe_interval_list == nullptr;\n    p | empty;\n    if (not empty) {\n      p | const_cast<Interval&>(*threadsafe_interval_list);\n    }\n  }\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::iterator::operator++() -> iterator& {\n  interval_ = interval_->previous.get();\n  return *this;\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::iterator::operator++(int) -> iterator {\n  auto result = *this;\n  ++*this;\n  return result;\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::iterator::operator*() const -> reference {\n  return {interval_->previous != nullptr ? interval_->previous->expiration\n                                         : parent_->initial_time(),\n          interval_->data, interval_->expiration};\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::iterator::operator->() const -> pointer {\n  return {**this};\n}\n\ntemplate <typename T>\nThreadsafeList<T>::iterator::iterator(const ThreadsafeList* parent,\n                                      const Interval* const interval)\n    : parent_(parent), interval_(interval) {}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::begin() const -> iterator {\n  return {this, most_recent_interval_.load(std::memory_order_acquire)};\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::end() const -> iterator {\n  return {};\n}\n\ntemplate <typename T>\nauto ThreadsafeList<T>::find_interval(const double time,\n                                      const bool interval_after_boundary) const\n    -> const Interval& {\n  auto* interval = most_recent_interval_.load(std::memory_order_acquire);\n  if (interval == nullptr) {\n    ERROR(\"Attempt to access an empty function of time.\");\n  }\n  if (time > interval->expiration or\n      (interval_after_boundary and time == interval->expiration and\n       interval->expiration < std::numeric_limits<double>::infinity())) {\n    ERROR(\"Attempt to evaluate at time \"\n          << time << \", which is after the expiration time \"\n          << interval->expiration);\n  }\n  // Loop over the intervals until we find the one containing `time`,\n  // possibly at the endpoint determined by `interval_after_boundary`.\n  for (;;) {\n    auto* const previous_interval = interval->previous.get();\n    if (previous_interval == nullptr or time > previous_interval->expiration or\n        (interval_after_boundary and time == previous_interval->expiration)) {\n      return *interval;\n    }\n    interval = previous_interval;\n  }\n}\n\ntemplate <typename T>\nbool operator==(const ThreadsafeList<T>& a, const ThreadsafeList<T>& b) {\n  if (a.initial_time() != b.initial_time()) {\n    return false;\n  }\n  auto a_iter = a.begin();\n  auto b_iter = b.begin();\n  while (a_iter != a.end() and b_iter != b.end()) {\n    if (*a_iter++ != *b_iter++) {\n      return false;\n    }\n  }\n  return a_iter == a.end() and b_iter == b.end();\n}\n\ntemplate <typename T>\nbool operator!=(const ThreadsafeList<T>& a, const ThreadsafeList<T>& b) {\n  return not(a == b);\n}\n}  // namespace domain::FunctionsOfTime::FunctionOfTimeHelpers\n"
  },
  {
    "path": "src/Domain/InterfaceComputeTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/SubitemTag.hpp\"\n#include \"DataStructures/DataBox/Subitems.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/InterfaceHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace domain {\nnamespace Tags {\nstruct FunctionsOfTime;\n}  // namespace Tags\n\nnamespace Tags {\n\nnamespace Interface_detail {\n\ntemplate <typename ComputeTag, typename VolumeTags, typename... ArgumentTags,\n          size_t Dim, typename... ArgTypes>\nconstexpr void evaluate_compute_item(\n    const gsl::not_null<typename ComputeTag::type*> result,\n    const ::Direction<Dim>& direction, tmpl::list<ArgumentTags...> /*meta*/,\n    const ArgTypes&... args) {\n  ComputeTag::function(\n      result,\n      InterfaceHelpers_detail::unmap_interface_args<\n          tmpl::list_contains_v<VolumeTags, ArgumentTags>>::apply(direction,\n                                                                  args)...);\n}\n}  // namespace Interface_detail\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Compute tag for representing items computed on a set of interfaces.\n/// Can be retrieved using `Tags::Interface<DirectionsTag, Tag>`\n///\n/// The computed object will be a map from a ::Direction to the item type of\n/// `Tag` (i.e. `Tag::type`), with the set of directions being those produced by\n/// `DirectionsTag`. `Tag::function` will be applied separately to the data on\n/// each interface. If some of the compute item's inputs (i.e.\n/// `Tag::argument_tags`) should be taken from the volume rather than the\n/// interface, they should be specified in the typelist `Tag::volume_tags`.\n///\n/// \\tparam DirectionsTag the simple tag labeling the set of Directions\n/// \\tparam Tag the compute tag to apply on each interface\ntemplate <typename DirectionsTag, typename Tag>\nstruct InterfaceCompute : Interface<DirectionsTag, typename Tag::base>,\n                          db::ComputeTag {\n  static_assert(db::is_simple_tag_v<DirectionsTag>);\n  static_assert(db::is_compute_tag_v<Tag>,\n                \"Cannot use a non compute item as an interface compute item.\");\n  using base = Interface<DirectionsTag, typename Tag::base>;\n  static constexpr size_t volume_dim = DirectionsTag::volume_dim;\n  using return_type =\n      std::unordered_map<::Direction<volume_dim>, typename Tag::type>;\n\n  using forwarded_argument_tags =\n      InterfaceHelpers_detail::get_interface_argument_tags<Tag, DirectionsTag>;\n  using argument_tags =\n      tmpl::push_front<forwarded_argument_tags, DirectionsTag>;\n\n  using volume_tags = get_volume_tags<Tag>;\n  static_assert(\n      tmpl::size<tmpl::list_difference<volume_tags, argument_tags>>::value == 0,\n      \"volume_tags contains tags not in argument_tags\");\n\n  template <typename... ArgTypes>\n  static constexpr void function(\n      const gsl::not_null<return_type*> result,\n      const std::unordered_set<::Direction<volume_dim>>& directions,\n      const ArgTypes&... args) {\n    for (const auto& direction : directions) {\n      Interface_detail::evaluate_compute_item<Tag, volume_tags>(\n          make_not_null(&(*result)[direction]), direction,\n          forwarded_argument_tags{}, args...);\n    }\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Compute tag for representing a compute item that slices data from\n/// the volume to a set of interfaces.\n///\n/// The computed object will be a map from a ::Direction to the item type of\n/// `Tag` (i.e. `Tag::type`), with the set of directions being those produced by\n/// `DirectionsTag`. `Tag::type` must be a `Tensor` or a `Variables`.\n/// Retrievable from the DataBox using `Tags::Interface<DirectionsTag, Tag>`\n///\n/// \\requires `Tag` correspond to a `Tensor` or a `Variables`\n///\n/// \\tparam DirectionsTag the simple tag labeling the set of Directions\n/// \\tparam Tag the simple tag for the volume data to be sliced\ntemplate <typename DirectionsTag, typename Tag>\nstruct Slice : Interface<DirectionsTag, Tag>, db::ComputeTag {\n  static_assert(db::is_simple_tag_v<DirectionsTag>);\n  static_assert(db::is_simple_tag_v<Tag>);\n  using base = Interface<DirectionsTag, Tag>;\n  static constexpr size_t volume_dim = DirectionsTag::volume_dim;\n\n  using return_type =\n      std::unordered_map<::Direction<volume_dim>, typename Tag::type>;\n\n  static constexpr void function(\n      const gsl::not_null<return_type*> sliced_vars,\n      const ::Mesh<volume_dim>& mesh,\n      const std::unordered_set<::Direction<volume_dim>>& directions,\n      const typename Tag::type& variables) {\n    for (const auto& direction : directions) {\n      data_on_slice(make_not_null(&((*sliced_vars)[direction])), variables,\n                    mesh.extents(), direction.dimension(),\n                    index_to_slice_at(mesh.extents(), direction));\n    }\n  }\n\n  using argument_tags = tmpl::list<Mesh<volume_dim>, DirectionsTag, Tag>;\n  using volume_tags = tmpl::list<Mesh<volume_dim>, Tag>;\n};\n\n/// \\cond\ntemplate <typename DirectionsTag, size_t VolumeDim>\nstruct InterfaceCompute<DirectionsTag, Direction<VolumeDim>>\n    : db::ComputeTag, Interface<DirectionsTag, Direction<VolumeDim>> {\n  static_assert(db::is_simple_tag_v<DirectionsTag>);\n  using base = Interface<DirectionsTag, Direction<VolumeDim>>;\n  using return_type =\n      std::unordered_map<::Direction<VolumeDim>, ::Direction<VolumeDim>>;\n  using argument_tags = tmpl::list<DirectionsTag>;\n  static constexpr auto function(\n      const gsl::not_null<\n          std::unordered_map<::Direction<VolumeDim>, ::Direction<VolumeDim>>*>\n          result,\n      const std::unordered_set<::Direction<VolumeDim>>& directions) {\n    for (const auto& d : directions) {\n      result->insert_or_assign(d, d);\n    }\n  }\n};\n/// \\endcond\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// Computes the `VolumeDim-1` dimensional mesh on an interface from the volume\n/// mesh. `Tags::InterfaceCompute<Dirs, InterfaceMesh<VolumeDim>>` is\n/// retrievable as `Tags::Interface<Dirs, Mesh<VolumeDim>>` from the DataBox.\ntemplate <size_t VolumeDim>\nstruct InterfaceMesh : db::ComputeTag, Tags::Mesh<VolumeDim - 1> {\n  using base = Tags::Mesh<VolumeDim - 1>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<Direction<VolumeDim>, Mesh<VolumeDim>>;\n  static constexpr auto function(const gsl::not_null<return_type*> mesh,\n                                 const ::Direction<VolumeDim>& direction,\n                                 const ::Mesh<VolumeDim>& volume_mesh) {\n    *mesh = volume_mesh.slice_away(direction.dimension());\n  }\n  using volume_tags = tmpl::list<Mesh<VolumeDim>>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// Computes the coordinates in the frame `Frame` on the faces defined by\n/// `Direction`. Intended to be prefixed by a `Tags::InterfaceCompute` to\n/// define the directions on which to compute the coordinates.\ntemplate <size_t VolumeDim, bool MovingMesh = false>\nstruct BoundaryCoordinates : db::ComputeTag,\n                             Tags::Coordinates<VolumeDim, Frame::Inertial> {\n  using base = Tags::Coordinates<VolumeDim, Frame::Inertial>;\n  using return_type = typename base::type;\n  static void function(const gsl::not_null<return_type*> boundary_coords,\n                       const ::Direction<VolumeDim>& direction,\n                       const ::Mesh<VolumeDim - 1>& interface_mesh,\n                       const ::ElementMap<VolumeDim, Frame::Inertial>& map) {\n    *boundary_coords =\n        map(interface_logical_coordinates(interface_mesh, direction));\n  }\n\n  static void function(\n      const gsl::not_null<return_type*> boundary_coords,\n      const ::Direction<VolumeDim>& direction,\n      const ::Mesh<VolumeDim - 1>& interface_mesh,\n      const ::ElementMap<VolumeDim, ::Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n          grid_to_inertial_map,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    *boundary_coords =\n        grid_to_inertial_map(logical_to_grid_map(interface_logical_coordinates(\n                                 interface_mesh, direction)),\n                             time, functions_of_time);\n  }\n\n  using argument_tags = tmpl::conditional_t<\n      MovingMesh,\n      tmpl::list<Direction<VolumeDim>, Mesh<VolumeDim - 1>,\n                 Tags::ElementMap<VolumeDim, Frame::Grid>,\n                 CoordinateMaps::Tags::CoordinateMap<VolumeDim, Frame::Grid,\n                                                     Frame::Inertial>,\n                 ::Tags::Time, domain::Tags::FunctionsOfTime>,\n      tmpl::list<Direction<VolumeDim>, Mesh<VolumeDim - 1>,\n                 ElementMap<VolumeDim, Frame::Inertial>>>;\n  using volume_tags = tmpl::conditional_t<\n      MovingMesh,\n      tmpl::list<Tags::ElementMap<VolumeDim, Frame::Grid>,\n                 CoordinateMaps::Tags::CoordinateMap<VolumeDim, Frame::Grid,\n                                                     Frame::Inertial>,\n                 ::Tags::Time, domain::Tags::FunctionsOfTime>,\n      tmpl::list<ElementMap<VolumeDim, Frame::Inertial>>>;\n};\n\n}  // namespace Tags\n}  // namespace domain\n\nnamespace db {\ntemplate <typename DirectionsTag, typename VariablesTag>\nstruct Subitems<domain::Tags::InterfaceCompute<DirectionsTag, VariablesTag>,\n                Requires<tt::is_a_v<Variables, typename VariablesTag::type>>>\n    : detail::InterfaceSubitemsImpl<DirectionsTag,\n                                    typename VariablesTag::base> {};\n\ntemplate <typename DirectionsTag, typename VariablesTag>\nstruct Subitems<domain::Tags::Slice<DirectionsTag, VariablesTag>,\n                Requires<tt::is_a_v<Variables, typename VariablesTag::type>>>\n    : detail::InterfaceSubitemsImpl<DirectionsTag, VariablesTag> {};\n}  // namespace db\n\nnamespace Tags {\n/// \\brief specialization of a subitem tag for an interface compute tag of a\n/// Variables\n///\n/// This is a compute tag for an unordered map from ::Direction (with the set of\n/// directions being those produced by `DirectionsTag`) to a Tensor (whose\n/// type comes from `TensorTag`) that is contained in a Variables (labeled by\n/// `VariablesTag`) for a ::domain::Tags::InterfaceCompute<DirectionsTag,\n/// VariablesTag>>.\ntemplate <typename DirectionsTag, typename TensorTag, typename VariablesTag>\nstruct Subitem<::domain::Tags::Interface<DirectionsTag, TensorTag>,\n               ::domain::Tags::InterfaceCompute<DirectionsTag, VariablesTag>>\n    : db::ComputeTag, ::domain::Tags::Interface<DirectionsTag, TensorTag> {\n  using base = ::domain::Tags::Interface<DirectionsTag, TensorTag>;\n  using return_type = typename base::type;\n  using parent_tag =\n      ::domain::Tags::InterfaceCompute<DirectionsTag, VariablesTag>;\n  static void function(const gsl::not_null<return_type*> subitems,\n                       const typename parent_tag::type& parent_value) {\n    ::db::Subitems<parent_tag>::template create_compute_item<base>(\n        subitems, parent_value);\n  }\n  using argument_tags = tmpl::list<typename parent_tag::base>;\n};\n\n/// \\brief specialization of a subitem tag for an interface compute tag of a\n/// sliced Variables\n///\n/// This is a compute tag for an unordered map from ::Direction (with the set of\n/// directions being those produced by `DirectionsTag`) to a Tensor (whose\n/// type comes from `TensorTag`) that is contained in a Variables (labeled by\n/// `VariablesTag`) for a domain::Tags::Slice<DirectionsTag, VariablesTag>>.\ntemplate <typename DirectionsTag, typename TensorTag, typename VariablesTag>\nstruct Subitem<::domain::Tags::Interface<DirectionsTag, TensorTag>,\n               ::domain::Tags::Slice<DirectionsTag, VariablesTag>>\n    : db::ComputeTag, ::domain::Tags::Interface<DirectionsTag, TensorTag> {\n  using base = ::domain::Tags::Interface<DirectionsTag, TensorTag>;\n  using return_type = typename base::type;\n  using parent_tag = ::domain::Tags::Slice<DirectionsTag, VariablesTag>;\n  static void function(const gsl::not_null<return_type*> subitems,\n                       const typename parent_tag::type& parent_value) {\n    ::db::Subitems<parent_tag>::template create_compute_item<base>(\n        subitems, parent_value);\n  }\n  using argument_tags = tmpl::list<typename parent_tag::base>;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Domain/InterfaceHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace InterfaceHelpers_detail {\n\ntemplate <typename T, typename = std::void_t<>>\nstruct get_volume_tags_impl {\n  using type = tmpl::list<>;\n};\ntemplate <typename T>\nstruct get_volume_tags_impl<T, std::void_t<typename T::volume_tags>> {\n  using type = typename T::volume_tags;\n};\n\n}  // namespace InterfaceHelpers_detail\n\n/// Retrieve `T::volume_tags`, defaulting to an empty list\ntemplate <typename T>\nusing get_volume_tags =\n    tmpl::type_from<InterfaceHelpers_detail::get_volume_tags_impl<T>>;\n\ntemplate <typename Tag, typename DirectionsTag, typename VolumeTags>\nstruct make_interface_tag {\n  using type = tmpl::conditional_t<tmpl::list_contains_v<VolumeTags, Tag>, Tag,\n                                   domain::Tags::Interface<DirectionsTag, Tag>>;\n};\n\ntemplate <typename TagsList, typename DirectionsTag,\n          typename VolumeTags = tmpl::list<>>\nusing make_interface_tags =\n    tmpl::transform<TagsList,\n                    make_interface_tag<tmpl::_1, tmpl::pin<DirectionsTag>,\n                                       tmpl::pin<VolumeTags>>>;\n\nnamespace InterfaceHelpers_detail {\n\n// Retrieve the `argument_tags` from the `InterfaceInvokable` and wrap them in\n// `::Tags::Interface` if they are not listed in\n// `InterfaceInvokable::volume_tags`.\ntemplate <typename InterfaceInvokable, typename DirectionsTag>\nusing get_interface_argument_tags = tmpl::transform<\n    typename InterfaceInvokable::argument_tags,\n    make_interface_tag<tmpl::_1, tmpl::pin<DirectionsTag>,\n                       tmpl::pin<get_volume_tags<InterfaceInvokable>>>>;\n\n/// Pull the direction's entry from interface arguments, passing volume\n/// arguments through unchanged.\ntemplate <bool IsVolumeTag>\nstruct unmap_interface_args;\n\ntemplate <>\nstruct unmap_interface_args<true> {\n  template <typename T>\n  using f = T;\n\n  template <size_t VolumeDim, typename T>\n  static constexpr const T& apply(const ::Direction<VolumeDim>& /*direction*/,\n                                  const T& arg) {\n    return arg;\n  }\n};\n\ntemplate <>\nstruct unmap_interface_args<false> {\n  template <typename T>\n  using f = typename T::mapped_type;\n\n  template <size_t VolumeDim, typename T>\n  static constexpr decltype(auto) apply(const ::Direction<VolumeDim>& direction,\n                                        const T& arg) {\n    return arg.at(direction);\n  }\n};\n\nCREATE_IS_CALLABLE(apply)\nCREATE_IS_CALLABLE_V(apply)\n\ntemplate <bool HasStaticApply, typename InterfaceReturnType,\n          typename DirectionsTag, typename VolumeTagsList,\n          typename... ArgumentTags>\nstruct DispatchInterfaceInvokable;\n\ntemplate <typename InterfaceReturnType, typename DirectionsTag,\n          typename VolumeTagsList, typename... ArgumentTags>\nstruct DispatchInterfaceInvokable<true, InterfaceReturnType, DirectionsTag,\n                                  VolumeTagsList, ArgumentTags...> {\n  template <typename InterfaceInvokable, typename DbTagsList,\n            typename... ExtraArgs>\n  static constexpr auto apply(InterfaceInvokable&& /*interface_invokable*/,\n                              const db::DataBox<DbTagsList>& box,\n                              ExtraArgs&&... extra_args) {\n    DirectionMap<DirectionsTag::volume_dim, InterfaceReturnType> result{};\n    for (const auto& direction : get<DirectionsTag>(box)) {\n      auto interface_value = InterfaceInvokable::apply(\n          unmap_interface_args<\n              tmpl::list_contains_v<VolumeTagsList, ArgumentTags>>::\n              apply(direction,\n                    get<tmpl::type_from<make_interface_tag<\n                        ArgumentTags, DirectionsTag, VolumeTagsList>>>(box))...,\n          extra_args...);\n      result.insert({direction, std::move(interface_value)});\n    }\n    return result;\n  }\n};\n\ntemplate <typename DirectionsTag, typename VolumeTagsList,\n          typename... ArgumentTags>\nstruct DispatchInterfaceInvokable<true, void, DirectionsTag, VolumeTagsList,\n                                  ArgumentTags...> {\n  template <typename InterfaceInvokable, typename DbTagsList,\n            typename... ExtraArgs>\n  static constexpr void apply(InterfaceInvokable&& /*interface_invokable*/,\n                              const db::DataBox<DbTagsList>& box,\n                              ExtraArgs&&... extra_args) {\n    for (const auto& direction : get<DirectionsTag>(box)) {\n      InterfaceInvokable::apply(\n          unmap_interface_args<\n              tmpl::list_contains_v<VolumeTagsList, ArgumentTags>>::\n              apply(direction,\n                    get<tmpl::type_from<make_interface_tag<\n                        ArgumentTags, DirectionsTag, VolumeTagsList>>>(box))...,\n          extra_args...);\n    }\n  }\n};\n\ntemplate <typename InterfaceReturnType, typename DirectionsTag,\n          typename VolumeTagsList, typename... ArgumentTags>\nstruct DispatchInterfaceInvokable<false, InterfaceReturnType, DirectionsTag,\n                                  VolumeTagsList, ArgumentTags...> {\n  template <typename InterfaceInvokable, typename DbTagsList,\n            typename... ExtraArgs>\n  static constexpr auto apply(InterfaceInvokable&& interface_invokable,\n                              const db::DataBox<DbTagsList>& box,\n                              ExtraArgs&&... extra_args) {\n    DirectionMap<DirectionsTag::volume_dim, InterfaceReturnType> result{};\n    for (const auto& direction : get<DirectionsTag>(box)) {\n      auto interface_value = interface_invokable(\n          unmap_interface_args<\n              tmpl::list_contains_v<VolumeTagsList, ArgumentTags>>::\n              apply(direction,\n                    get<tmpl::type_from<make_interface_tag<\n                        ArgumentTags, DirectionsTag, VolumeTagsList>>>(box))...,\n          extra_args...);\n      result.insert({direction, std::move(interface_value)});\n    }\n    return result;\n  }\n};\n\ntemplate <typename DirectionsTag, typename VolumeTagsList,\n          typename... ArgumentTags>\nstruct DispatchInterfaceInvokable<false, void, DirectionsTag, VolumeTagsList,\n                                  ArgumentTags...> {\n  template <typename InterfaceInvokable, typename DbTagsList,\n            typename... ExtraArgs>\n  static constexpr void apply(InterfaceInvokable&& interface_invokable,\n                              const db::DataBox<DbTagsList>& box,\n                              ExtraArgs&&... extra_args) {\n    for (const auto& direction : get<DirectionsTag>(box)) {\n      interface_invokable(\n          unmap_interface_args<\n              tmpl::list_contains_v<VolumeTagsList, ArgumentTags>>::\n              apply(direction,\n                    get<tmpl::type_from<make_interface_tag<\n                        ArgumentTags, DirectionsTag, VolumeTagsList>>>(box))...,\n          extra_args...);\n    }\n  }\n};\n\ntemplate <typename DirectionsTag, typename ArgumentTags, typename VolumeTags>\nstruct InterfaceApplyImpl;\n\ntemplate <typename DirectionsTag, typename VolumeTagsList,\n          typename... ArgumentTags>\nstruct InterfaceApplyImpl<DirectionsTag, tmpl::list<ArgumentTags...>,\n                          VolumeTagsList> {\n  static constexpr size_t volume_dim = DirectionsTag::volume_dim;\n\n  template <\n      typename InterfaceInvokable, typename DbTagsList, typename... ExtraArgs,\n      Requires<is_apply_callable_v<\n          InterfaceInvokable, db::const_item_type<ArgumentTags, DbTagsList>...,\n          ExtraArgs...>> = nullptr>\n  static constexpr auto apply(InterfaceInvokable&& interface_invokable,\n                              const db::DataBox<DbTagsList>& box,\n                              ExtraArgs&&... extra_args) {\n    using interface_return_type =\n        std::decay_t<decltype(InterfaceInvokable::apply(\n            std::declval<db::const_item_type<ArgumentTags, DbTagsList>>()...,\n            std::declval<ExtraArgs&&>()...))>;\n    return DispatchInterfaceInvokable<true, interface_return_type,\n                                      DirectionsTag, VolumeTagsList,\n                                      ArgumentTags...>::\n        apply(std::forward<InterfaceInvokable>(interface_invokable), box,\n              std::forward<ExtraArgs>(extra_args)...);\n  }\n\n  template <\n      typename InterfaceInvokable, typename DbTagsList, typename... ExtraArgs,\n      Requires<not is_apply_callable_v<\n          InterfaceInvokable, db::const_item_type<ArgumentTags, DbTagsList>...,\n          ExtraArgs...>> = nullptr>\n  static constexpr auto apply(InterfaceInvokable&& interface_invokable,\n                              const db::DataBox<DbTagsList>& box,\n                              ExtraArgs&&... extra_args) {\n    using interface_return_type = std::decay_t<decltype(interface_invokable(\n        std::declval<db::const_item_type<ArgumentTags, DbTagsList>>()...,\n        std::declval<ExtraArgs&&>()...))>;\n    return DispatchInterfaceInvokable<false, interface_return_type,\n                                      DirectionsTag, VolumeTagsList,\n                                      ArgumentTags...>::\n        apply(std::forward<InterfaceInvokable>(interface_invokable), box,\n              std::forward<ExtraArgs>(extra_args)...);\n  }\n};\n\n}  // namespace InterfaceHelpers_detail\n\n/// @{\n/*!\n * \\brief Apply an invokable to the `box` on all interfaces given by the\n * `DirectionsTag`.\n *\n * This function has an overload that takes an `interface_invokable` as its\n * first argument, and one that takes an `InterfaceInvokable` class as its first\n * template parameter instead. For the latter, the `InterfaceInvokable` class\n * must have a static `apply` function. The `interface_invokable` or static\n * `apply` function, respectively, is expected to be invokable with the types\n * held by the `ArgumentTags`, followed by the `extra_args`. The `ArgumentTags`\n * will be prefixed as `::Tags::Interface<DirectionsTag, ArgumentTag>` and thus\n * taken from the interface, except for those specified in the `VolumeTags`.\n *\n * This function returns a `DirectionMap` that holds the value returned by\n * the `interface_invokable` in every direction of the `DirectionsTag`.\n *\n * Here is an example how to use this function:\n *\n * \\snippet Test_InterfaceHelpers.cpp interface_apply_example\n *\n * Here is another example how to use this function with a stateless invokable\n * class:\n *\n * \\snippet Test_InterfaceHelpers.cpp interface_apply_example_stateless\n *\n * This is the class that defines the invokable in the example above:\n *\n * \\snippet Test_InterfaceHelpers.cpp interface_invokable_example\n */\ntemplate <typename DirectionsTag, typename ArgumentTags, typename VolumeTags,\n          typename InterfaceInvokable, typename DbTagsList,\n          typename... ExtraArgs>\nSPECTRE_ALWAYS_INLINE constexpr auto interface_apply(\n    InterfaceInvokable&& interface_invokable,\n    const db::DataBox<DbTagsList>& box, ExtraArgs&&... extra_args) {\n  return InterfaceHelpers_detail::\n      InterfaceApplyImpl<DirectionsTag, ArgumentTags, VolumeTags>::apply(\n          std::forward<InterfaceInvokable>(interface_invokable), box,\n          std::forward<ExtraArgs>(extra_args)...);\n}\n\n// The `box` argument to this function overload is not constrained to\n// `db::DataBox` to work around an issue with GCC <= 8.\n//\n// Details on the issue:\n//\n// GCC <= 8 tries to instantiate the `db::DataBox<DbTagsList>` template even\n// when this function overload is _not_ SFINAE-selected. In that case the\n// template parameter substitution for `DbTagsList` may contain base tags that\n// causes errors with the `db::DataBox` template instantiation.\ntemplate <typename DirectionsTag, typename InterfaceInvokable,\n          typename DataBoxType, typename... ExtraArgs,\n          // Needed to disambiguate the overloads\n          typename ArgumentTags = typename InterfaceInvokable::argument_tags>\nSPECTRE_ALWAYS_INLINE constexpr auto interface_apply(\n    const DataBoxType& box, ExtraArgs&&... extra_args) {\n  return interface_apply<DirectionsTag, ArgumentTags,\n                         get_volume_tags<InterfaceInvokable>>(\n      InterfaceInvokable{}, box, std::forward<ExtraArgs>(extra_args)...);\n}\n/// @}\n"
  },
  {
    "path": "src/Domain/InterfaceLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\ntemplate <size_t VolumeDim>\ntnsr::I<DataVector, VolumeDim, Frame::ElementLogical>\ninterface_logical_coordinates(const Mesh<VolumeDim - 1>& mesh,\n                              const Direction<VolumeDim>& direction) {\n  const auto num_grid_points = mesh.number_of_grid_points();\n  const size_t sliced_away_dim = direction.dimension();\n  tnsr::I<DataVector, VolumeDim, Frame::ElementLogical> logical_x(\n      num_grid_points);\n\n  auto xi = logical_coordinates(mesh);\n  for (size_t d = 0; d < sliced_away_dim; ++d) {\n    logical_x.get(d) = std::move(xi.get(d));\n  }\n  logical_x[sliced_away_dim] =\n      (Side::Lower == direction.side() ? DataVector(num_grid_points, -1.0)\n                                       : DataVector(num_grid_points, 1.0));\n  for (size_t d = sliced_away_dim; d < VolumeDim - 1; ++d) {\n    logical_x.get(d + 1) = std::move(xi.get(d));\n  }\n\n  return logical_x;\n}\n\n// We need this specialisation since Mesh<Dim>::slice_through() is available\n// only for Dim > 0\ntemplate <>\ntnsr::I<DataVector, 1, Frame::ElementLogical> interface_logical_coordinates<1>(\n    const Mesh<0>& mesh, const Direction<1>& direction) {\n  return tnsr::I<DataVector, 1, Frame::ElementLogical>{\n      mesh.number_of_grid_points(), direction.sign()};\n}\n\n// Explicit instantiations\ntemplate tnsr::I<DataVector, 2, Frame::ElementLogical>\ninterface_logical_coordinates(const Mesh<1>&, const Direction<2>&);\ntemplate tnsr::I<DataVector, 3, Frame::ElementLogical>\ninterface_logical_coordinates(const Mesh<2>&, const Direction<3>&);\n"
  },
  {
    "path": "src/Domain/InterfaceLogicalCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// Defines functions interface_logical_coordinates\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\n/// \\endcond\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Compute the logical coordinates on a face of an Element.\n *\n * \\returns element logical-frame vector holding coordinates\n *\n * \\example\n * \\snippet Test_InterfaceLogicalCoordinates.cpp interface_logical_coordinates_example\n */\ntemplate <size_t VolumeDim>\ntnsr::I<DataVector, VolumeDim, Frame::ElementLogical>\ninterface_logical_coordinates(const Mesh<VolumeDim - 1>& mesh,\n                              const Direction<VolumeDim>& direction);\n"
  },
  {
    "path": "src/Domain/JacobianDiagnostic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/JacobianDiagnostic.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain {\ntemplate <size_t Dim, typename Fr>\nvoid jacobian_diagnostic(\n    const gsl::not_null<\n        tnsr::i<DataVector, Dim, typename Frame::ElementLogical>*>\n        jacobian_diag,\n    const Jacobian<DataVector, Dim, typename Frame::ElementLogical, Fr>&\n        analytic_jacobian,\n    const TensorMetafunctions::prepend_spatial_index<\n        tnsr::I<DataVector, Dim, Fr>, Dim, UpLo::Lo,\n        typename Frame::ElementLogical>& numeric_jacobian_transpose) {\n  const size_t number_of_points = analytic_jacobian.begin()->size();\n\n  // i_hat = logical frame index\n  // i = mapped frame index\n  Scalar<DataVector> jacobian_component_sum{number_of_points};\n  for (size_t i_hat = 0; i_hat < Dim; ++i_hat) {\n    jacobian_diag->get(i_hat) = -abs(analytic_jacobian.get(0, i_hat));\n    for (size_t i = 1; i < Dim; ++i) {\n      jacobian_diag->get(i_hat) -= abs(analytic_jacobian.get(i, i_hat));\n    }\n    get(jacobian_component_sum) = abs(numeric_jacobian_transpose.get(i_hat, 0));\n    for (size_t i = 1; i < Dim; ++i) {\n      get(jacobian_component_sum) +=\n          abs(numeric_jacobian_transpose.get(i_hat, i));\n    }\n    jacobian_diag->get(i_hat) /= get(jacobian_component_sum);\n    jacobian_diag->get(i_hat) += 1.0;\n  }\n}\n\ntemplate <size_t Dim, typename Fr>\nvoid jacobian_diagnostic(\n    const gsl::not_null<\n        tnsr::i<DataVector, Dim, typename Frame::ElementLogical>*>\n        jacobian_diag,\n    const ::Jacobian<DataVector, Dim, Frame::ElementLogical, Fr>&\n        analytic_jacobian,\n    const tnsr::I<DataVector, Dim, Fr>& mapped_coords,\n    const ::Mesh<Dim>& mesh) {\n  // Note: Jacobian has the source frame index second, but\n  // logical_partial_derivative prepends the logical (source frame) index.\n  // So this is actually the transpose of the numerical jacobian.\n  const auto numerical_jacobian_transpose =\n      logical_partial_derivative(mapped_coords, mesh);\n\n  jacobian_diagnostic(jacobian_diag, analytic_jacobian,\n                      numerical_jacobian_transpose);\n}\n\ntemplate <size_t Dim, typename Fr>\ntnsr::i<DataVector, Dim, typename Frame::ElementLogical> jacobian_diagnostic(\n    const ::Jacobian<DataVector, Dim, Frame::ElementLogical, Fr>&\n        analytic_jacobian,\n    const tnsr::I<DataVector, Dim, Fr>& mapped_coords,\n    const ::Mesh<Dim>& mesh) {\n  tnsr::i<DataVector, Dim, typename Frame::ElementLogical> jacobian_diag{};\n  jacobian_diagnostic(make_not_null(&jacobian_diag), analytic_jacobian,\n                      mapped_coords, mesh);\n  return jacobian_diag;\n}\n}  // namespace domain\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template void domain::jacobian_diagnostic(                                \\\n      const gsl::not_null<                                                  \\\n          tnsr::i<DataVector, DIM(data), typename Frame::ElementLogical>*>  \\\n          jacobian_diag,                                                    \\\n      const Jacobian<DataVector, DIM(data), Frame::ElementLogical,          \\\n                     FRAME(data)>& analytic_jacobian,                       \\\n      const TensorMetafunctions::prepend_spatial_index<                     \\\n          tnsr::I<DataVector, DIM(data), FRAME(data)>, DIM(data), UpLo::Lo, \\\n          typename Frame::ElementLogical>& numeric_jacobian_transpose);     \\\n  template void domain::jacobian_diagnostic(                                \\\n      const gsl::not_null<                                                  \\\n          tnsr::i<DataVector, DIM(data), typename Frame::ElementLogical>*>  \\\n          jacobian_diag,                                                    \\\n      const ::Jacobian<DataVector, DIM(data), Frame::ElementLogical,        \\\n                       FRAME(data)>& analytic_jacobian,                     \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& mapped_coords,     \\\n      const ::Mesh<DIM(data)>& mesh);                                       \\\n  template tnsr::i<DataVector, DIM(data), typename Frame::ElementLogical>   \\\n  domain::jacobian_diagnostic(                                              \\\n      const ::Jacobian<DataVector, DIM(data), Frame::ElementLogical,        \\\n                       FRAME(data)>& analytic_jacobian,                     \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& mapped_coords,     \\\n      const ::Mesh<DIM(data)>& mesh);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Domain/JacobianDiagnostic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace domain {\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief A diagnostic comparing the analytic and numerical Jacobians for a\n * map.\n *\n * Specifically, returns\n * \\f[\n * C_{\\hat{i}} = 1 -\n * \\frac{\\sum_i |\\partial_{\\hat{i}} x^i|}{\\sum_i |D_{\\hat{i}} x^i|}\n * \\f], where \\f$x^{\\hat{i}}\\f$ are the logical\n * coordinates, \\f$x^i\\f$ are the coordinates in the target frame,\n * \\f$\\partial_{\\hat{i}}x^i\\f$ is the analytic Jacobian, and \\f$D_{\\hat{i}}\n * x^i\\f$ is the numerical Jacobian.\n *\n * \\note This function accepts the transpose of the numeric Jacobian as a\n * parameter, since the numeric Jacobian will typically be computed via\n * logical_partial_derivative(), which prepends the logical (source frame)\n * derivative index. Tensors of type Jacobian, in contrast, have the derivative\n * index second.\n */\ntemplate <size_t Dim, typename Fr>\nvoid jacobian_diagnostic(\n    const gsl::not_null<\n        tnsr::i<DataVector, Dim, typename Frame::ElementLogical>*>\n        jacobian_diag,\n    const Jacobian<DataVector, Dim, typename Frame::ElementLogical, Fr>&\n        analytic_jacobian,\n    const TensorMetafunctions::prepend_spatial_index<\n        tnsr::I<DataVector, Dim, Fr>, Dim, UpLo::Lo,\n        typename Frame::ElementLogical>& numeric_jacobian_transpose);\n\n/// @{\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief A diagnostic comparing the analytic and numerical Jacobians for a\n * map.\n *\n * Specifically, returns\n * \\f[\n * C_{\\hat{i}} = 1 -\n * \\frac{\\sum_i |\\partial_{\\hat{i}} x^i|}{\\sum_i |D_{\\hat{i}} x^i|}\n * \\f],\n * where \\f$x^{\\hat{i}}\\f$ are the logical coordinates, \\f$x^i\\f$ are the\n * coordinates in the target frame, \\f$\\partial_{\\hat{i}}x^i\\f$ is the analytic\n * Jacobian, and \\f$D_{\\hat{i}} x^i\\f$ is the numerical Jacobian.\n *\n * \\note This function accepts the analytic jacobian, mapped coordinates, and\n * mesh as a parameter. The numeric jacobian is computed\n * internally by differentiating the mapped coordinates with respect to the\n * logical coordinates.\n */\ntemplate <size_t Dim, typename Fr>\nvoid jacobian_diagnostic(\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::ElementLogical>*>\n        jacobian_diag,\n    const ::Jacobian<DataVector, Dim, Frame::ElementLogical, Fr>&\n        analytic_jacobian,\n    const tnsr::I<DataVector, Dim, Fr>& mapped_coords, const ::Mesh<Dim>& mesh);\n\ntemplate <size_t Dim, typename Fr>\ntnsr::i<DataVector, Dim, Frame::ElementLogical> jacobian_diagnostic(\n    const ::Jacobian<DataVector, Dim, Frame::ElementLogical, Fr>&\n        analytic_jacobian,\n    const tnsr::I<DataVector, Dim, Fr>& mapped_coords, const ::Mesh<Dim>& mesh);\n/// @}\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief A diagnostic comparing the analytic and numerical Jacobians for a\n/// map. See `domain::jacobian_diagnostic` for details.\ntemplate <size_t Dim>\nstruct JacobianDiagnostic : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, typename Frame::ElementLogical>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Computes the Jacobian diagnostic, which compares the analytic\n/// Jacobian (provided by some coordinate map) to a numerical Jacobian computed\n/// using numerical partial derivatives. The coordinates must be in the target\n/// frame of the map. See `domain::jacobian_diagnostic` for details of the\n/// calculation.\ntemplate <size_t Dim, typename TargetFrame>\nstruct JacobianDiagnosticCompute : JacobianDiagnostic<Dim>, db::ComputeTag {\n  using base = JacobianDiagnostic<Dim>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      ::domain::Tags::Jacobian<Dim, Frame::ElementLogical, TargetFrame>,\n      ::domain::Tags::Coordinates<Dim, TargetFrame>, Mesh<Dim>>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<\n          tnsr::i<DataVector, Dim, typename Frame::ElementLogical>*>,\n      const ::Jacobian<DataVector, Dim, Frame::ElementLogical, TargetFrame>&,\n      const tnsr::I<DataVector, Dim, TargetFrame>&, const ::Mesh<Dim>&)>(\n      &::domain::jacobian_diagnostic<Dim, TargetFrame>);\n};\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/MinimumGridSpacing.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/MinimumGridSpacing.hpp\"\n\n// For std::min\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename Frame>\nstd::array<double, Dim> nth_point(const tnsr::I<DataVector, Dim, Frame>& tensor,\n                                  const size_t index) {\n  std::array<double, Dim> result{};\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(result, d) = tensor.get(d)[index];\n  }\n  return result;\n}\n}  // namespace\n\ntemplate <size_t Dim, typename Frame>\ndouble minimum_grid_spacing(const Index<Dim>& extents,\n                            const tnsr::I<DataVector, Dim, Frame>& coords) {\n  double minimum_spacing = std::numeric_limits<double>::max();\n  for (IndexIterator<Dim> point(extents); point; ++point) {\n    const auto point_coords = nth_point(coords, point.collapsed_index());\n    // We assume that the coordinates are not too distorted in that\n    // the closest point to a given point cannot be off by more than\n    // one index in any dimension.\n    for (IndexIterator<Dim> offset(Index<Dim>(3)); offset; ++offset) {\n      if (*offset == Index<Dim>(1)) {\n        // All the remaining directions are the opposite of ones we\n        // have already checked.  They will be checked from the other\n        // point.\n        break;\n      }\n      // On initialization this is one-indexed.\n      Index<Dim> other_point(point->indices() + offset->indices());\n      for (size_t d = 0; d < Dim; ++d) {\n        if (other_point[d] == 0 or other_point[d] == extents[d] + 1) {\n          goto next_offset;\n        }\n        --other_point[d];\n      }\n      minimum_spacing = std::min(\n          minimum_spacing,\n          magnitude(nth_point(coords, collapsed_index(other_point, extents)) -\n                    point_coords));\n\n    next_offset:;\n    }\n  }\n\n  return minimum_spacing;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)            \\\n  template double minimum_grid_spacing( \\\n      const Index<DIM(data)>& extents,  \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Domain/MinimumGridSpacing.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Index;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\n/// \\ingroup ComputationalDomainGroup\n/// Finds the minimum coordinate distance between grid points.\ntemplate <size_t Dim, typename Frame>\ndouble minimum_grid_spacing(const Index<Dim>& extents,\n                            const tnsr::I<DataVector, Dim, Frame>& coords);\n\nnamespace domain {\nnamespace Tags {\n/// @{\n/// \\ingroup ComputationalDomainGroup\n/// \\ingroup DataBoxTagsGroup\n/// The minimum coordinate distance between grid points.\ntemplate <size_t Dim, typename Frame>\nstruct MinimumGridSpacing : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <size_t Dim, typename Frame>\nstruct MinimumGridSpacingCompute : MinimumGridSpacing<Dim, Frame>,\n                                   db::ComputeTag {\n  using base = MinimumGridSpacing<Dim, Frame>;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> result,\n                       const ::Mesh<Dim>& mesh,\n                       const tnsr::I<DataVector, Dim, Frame>& coordinates) {\n    *result = minimum_grid_spacing(mesh.extents(), coordinates);\n  }\n  using argument_tags = tmpl::list<Mesh<Dim>, Coordinates<Dim, Frame>>;\n};\n/// @}\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"Domain/Python/Block.hpp\"\n#include \"Domain/Python/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Python/Direction.hpp\"\n#include \"Domain/Python/Domain.hpp\"\n#include \"Domain/Python/ElementId.hpp\"\n#include \"Domain/Python/ElementLogicalCoordinates.hpp\"\n#include \"Domain/Python/ElementMap.hpp\"\n#include \"Domain/Python/FunctionsOfTime.hpp\"\n#include \"Domain/Python/JacobianDiagnostic.hpp\"\n#include \"Domain/Python/RadiallyCompressedCoordinates.hpp\"\n#include \"Domain/Python/SegmentId.hpp\"\n#include \"Domain/Python/StrahlkorperTransformations.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain {\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py::module_::import(\"spectre.Domain.CoordinateMaps\");\n  py_bindings::bind_direction(m);\n  py_bindings::bind_block(m);\n  py_bindings::bind_block_logical_coordinates(m);\n  py_bindings::bind_domain(m);\n  py_bindings::bind_element_id(m);\n  py_bindings::bind_element_logical_coordinates(m);\n  py_bindings::bind_element_map(m);\n  py_bindings::bind_functions_of_time(m);\n  py_bindings::bind_jacobian_diagnostic(m);\n  py_bindings::bind_radially_compressed_coordinates(m);\n  py_bindings::bind_segment_id(m);\n  py_bindings::bind_strahlkorper_transformations(m);\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Python/Block.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/Block.hpp\"\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_block_impl(py::module& m) {  // NOLINT\n  py::class_<Block<Dim>>(m, (\"Block\" + get_output(Dim) + \"D\").c_str())\n      .def(\"is_time_dependent\", &Block<Dim>::is_time_dependent)\n      .def(\"has_distorted_frame\", &Block<Dim>::has_distorted_frame)\n      .def_property_readonly(\"id\", &Block<Dim>::id)\n      .def_property_readonly(\"name\", &Block<Dim>::name)\n      .def_property_readonly(\n          \"neighbors\",\n          [](const Block<Dim>& block) {\n            std::unordered_map<Direction<Dim>, size_t> neighbors{};\n            for (const auto& [direction, neighbor] : block.neighbors()) {\n              ASSERT(\n                  neighbor.size() == 1,\n                  \"Multiple block neighbors not supported by this function.\");\n              neighbors[direction] = *neighbor.begin();\n            }\n            return neighbors;\n          })\n      .def_property_readonly(\"stationary_map\", &Block<Dim>::stationary_map)\n      .def_property_readonly(\"moving_mesh_logical_to_grid_map\",\n                             &Block<Dim>::moving_mesh_logical_to_grid_map)\n      .def_property_readonly(\"moving_mesh_grid_to_inertial_map\",\n                             &Block<Dim>::moving_mesh_grid_to_inertial_map)\n      .def_property_readonly(\"moving_mesh_grid_to_distorted_map\",\n                             &Block<Dim>::moving_mesh_grid_to_distorted_map)\n      .def_property_readonly(\n          \"moving_mesh_distorted_to_inertial_map\",\n          &Block<Dim>::moving_mesh_distorted_to_inertial_map);\n}\n}  // namespace\n\nvoid bind_block(py::module& m) {  // NOLINT\n  bind_block_impl<1>(m);\n  bind_block_impl<2>(m);\n  bind_block_impl<3>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/Block.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_block(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/BlockLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/BlockLogicalCoordinates.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IdPair.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/BlockId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_block_logical_coordinates_impl(py::module& m) {  // NOLINT\n  using BlockIdAndLogicalCoord =\n      IdPair<domain::BlockId, tnsr::I<double, Dim, Frame::BlockLogical>>;\n  py::class_<BlockIdAndLogicalCoord>(\n      m, (\"BlockIdAndLogicalCoord\" + get_output(Dim) + \"D\").c_str())\n      .def_readonly(\"id\", &BlockIdAndLogicalCoord::id)\n      .def_readonly(\"data\", &BlockIdAndLogicalCoord::data);\n  m.def(\n      \"block_logical_coordinates\",\n      [](const Domain<Dim>& domain,\n         const tnsr::I<DataVector, Dim>& inertial_coords,\n         const std::optional<double>& time = std::nullopt,\n         const std::optional<std::unordered_map<\n             std::string, const domain::FunctionsOfTime::FunctionOfTime&>>&\n             functions_of_time = std::nullopt) {\n        // Transform functions-of-time map to unique_ptrs because pybind11 can't\n        // handle unique_ptrs easily as function arguments (it's hard to\n        // transfer ownership of a Python object to C++)\n        std::unordered_map<\n            std::string,\n            std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n            functions_of_time_ptrs{};\n        if (functions_of_time.has_value()) {\n          for (const auto& [name, fot] : *functions_of_time) {\n            functions_of_time_ptrs[name] = fot.get_clone();\n          }\n        }\n        return block_logical_coordinates(\n            domain, inertial_coords,\n            time.value_or(std::numeric_limits<double>::signaling_NaN()),\n            functions_of_time_ptrs);\n      },\n      py::arg(\"domain\"), py::arg(\"inertial_coords\"), py::arg(\"time\"),\n      py::arg(\"functions_of_time\"));\n}\n}  // namespace\n\nvoid bind_block_logical_coordinates(py::module& m) {  // NOLINT\n  py::class_<BlockId>(m, \"BlockId\").def(\"get_index\", &BlockId::get_index);\n  bind_block_logical_coordinates_impl<1>(m);\n  bind_block_logical_coordinates_impl<2>(m);\n  bind_block_logical_coordinates_impl<3>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/BlockLogicalCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_block_logical_coordinates(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyDomain\")\n\nspectre_python_add_module(\n  Domain\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  Block.cpp\n  BlockLogicalCoordinates.cpp\n  Direction.cpp\n  Domain.cpp\n  ElementId.cpp\n  ElementLogicalCoordinates.cpp\n  ElementMap.cpp\n  FunctionsOfTime.cpp\n  JacobianDiagnostic.cpp\n  RadiallyCompressedCoordinates.cpp\n  SegmentId.cpp\n  StrahlkorperTransformations.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Block.hpp\n  BlockLogicalCoordinates.hpp\n  Direction.hpp\n  Domain.hpp\n  ElementId.hpp\n  ElementLogicalCoordinates.hpp\n  ElementMap.hpp\n  FunctionsOfTime.hpp\n  JacobianDiagnostic.hpp\n  RadiallyCompressedCoordinates.hpp\n  SegmentId.hpp\n  StrahlkorperTransformations.hpp\n)\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  FunctionsOfTime\n  pybind11::module\n  Utilities\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyCoordinateMaps\n  PyDataStructures\n  PySphericalHarmonics\n  PyTensor\n  )\n"
  },
  {
    "path": "src/Domain/Python/Direction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/Direction.hpp\"\n\n#include <cstddef>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_direction_impl(py::module& m) {  // NOLINT\n  auto binding =\n      py::class_<Direction<Dim>>(m,\n                                 (\"Direction\" + get_output(Dim) + \"D\").c_str())\n          .def(py::init<size_t, Side>(), py::arg(\"dimension\"), py::arg(\"side\"))\n          .def_property_readonly(\"dimension\", &Direction<Dim>::dimension)\n          .def_property_readonly(\"side\", &Direction<Dim>::side)\n          .def_property_readonly(\"sign\", &Direction<Dim>::sign)\n          .def(\"opposite\", &Direction<Dim>::opposite)\n          .def(\"__repr__\",\n               [](const Direction<Dim>& direction) {\n                 return get_output(direction);\n               })\n          // NOLINTNEXTLINE(misc-redundant-expression)\n          .def(py::self == py::self)\n          // NOLINTNEXTLINE(misc-redundant-expression)\n          .def(py::self != py::self)\n          // NOLINTNEXTLINE(misc-redundant-expression)\n          .def(py::self < py::self)\n          .def(hash(py::self));\n  if constexpr (Dim >= 1) {\n    binding.def_static(\"lower_xi\", &Direction<Dim>::lower_xi)\n        .def_static(\"upper_xi\", &Direction<Dim>::upper_xi);\n  }\n  if constexpr (Dim >= 2) {\n    binding.def_static(\"lower_eta\", &Direction<Dim>::lower_eta)\n        .def_static(\"upper_eta\", &Direction<Dim>::upper_eta);\n  }\n  if constexpr (Dim >= 3) {\n    binding.def_static(\"lower_zeta\", &Direction<Dim>::lower_zeta)\n        .def_static(\"upper_zeta\", &Direction<Dim>::upper_zeta);\n  }\n}\n}  // namespace\n\nvoid bind_direction(py::module& m) {  // NOLINT\n  py::enum_<Side>(m, \"Side\")\n      .value(\"Lower\", Side::Lower)\n      .value(\"Upper\", Side::Upper);\n  bind_direction_impl<1>(m);\n  bind_direction_impl<2>(m);\n  bind_direction_impl<3>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/Direction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_direction(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/Domain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/Domain.hpp\"\n\n#include <cstddef>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_domain_impl(py::module& m) {  // NOLINT\n  py::class_<Domain<Dim>>(m, (\"Domain\" + get_output(Dim) + \"D\").c_str())\n      .def_property_readonly_static(\n          \"dim\", [](const py::object& /*self */) { return Dim; })\n      .def(\"is_time_dependent\", &Domain<Dim>::is_time_dependent)\n      .def_property_readonly(\"blocks\", &Domain<Dim>::blocks)\n      .def_property_readonly(\"block_groups\", &Domain<Dim>::block_groups)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self);\n  m.def(\"serialize_domain\", &serialize<Domain<Dim>>);\n  m.def((\"deserialize_domain_\" + get_output(Dim) + \"d\").c_str(),\n        [](const std::vector<char>& serialized_domain) {\n          return deserialize<Domain<Dim>>(serialized_domain.data());\n        },\n        py::arg(\"serialized_domain\"));\n}\n}  // namespace\n\nvoid bind_domain(py::module& m) {  // NOLINT\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  bind_domain_impl<1>(m);\n  bind_domain_impl<2>(m);\n  bind_domain_impl<3>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/Domain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_domain(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/ElementId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/ElementId.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_element_id_impl(py::module& m) {  // NOLINT\n  // These bindings don't cover the full public interface yet. More bindings\n  // can be added as needed.\n  py::class_<ElementId<Dim>>(m, (\"ElementId\" + get_output(Dim) + \"D\").c_str())\n      .def(py::init<size_t>(), py::arg(\"block_id\"))\n      .def(py::init<size_t, std::array<SegmentId, Dim>>(), py::arg(\"block_id\"),\n           py::arg(\"segment_ids\"))\n      .def(py::init<const std::string&>(), py::arg(\"grid_name\"))\n      .def_property(\"block_id\", &ElementId<Dim>::block_id, nullptr)\n      .def_property(\"grid_index\", &ElementId<Dim>::grid_index, nullptr)\n      .def_property(\"refinement_levels\", &ElementId<Dim>::refinement_levels,\n                    nullptr)\n      .def_property(\"segment_ids\", &ElementId<Dim>::segment_ids, nullptr)\n      .def_static(\"external_boundary_id\", &ElementId<Dim>::external_boundary_id)\n      .def(\"id_of_child\", &ElementId<Dim>::id_of_child, py::arg(\"dim\"),\n           py::arg(\"side\"))\n      .def(\"id_of_parent\", &ElementId<Dim>::id_of_parent, py::arg(\"dim\"))\n      .def(\"to_short_id\", &ElementId<Dim>::to_short_id)\n      .def(\"__repr__\",\n           [](const ElementId<Dim>& element_id) {\n             return get_output(element_id);\n           })\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self < py::self)\n      .def(hash(py::self));\n}\n}  // namespace\n\nvoid bind_element_id(py::module& m) {\n  bind_element_id_impl<1>(m);\n  bind_element_id_impl<2>(m);\n  bind_element_id_impl<3>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/ElementId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_element_id(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/ElementLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/ElementLogicalCoordinates.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IdPair.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/BlockId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_element_logical_coordinates_impl(py::module& m) {  // NOLINT\n  m.def(\"element_logical_coordinates\",\n        py::overload_cast<const tnsr::I<double, Dim, Frame::BlockLogical>&,\n                          const ElementId<Dim>&>(\n            &element_logical_coordinates<Dim>),\n        py::arg(\"x_block_logical\"), py::arg(\"element_id\"));\n  py::class_<ElementLogicalCoordHolder<Dim>>(\n      m, (\"ElementLogicalCoordHolder\" + get_output(Dim) + \"D\").c_str())\n      .def_readonly(\"element_logical_coords\",\n                    &ElementLogicalCoordHolder<Dim>::element_logical_coords)\n      .def_readonly(\"offsets\", &ElementLogicalCoordHolder<Dim>::offsets);\n  m.def(\"element_logical_coordinates\",\n        py::overload_cast<const std::vector<ElementId<Dim>>&,\n                          const std::vector<BlockLogicalCoords<Dim>>&>(\n            &element_logical_coordinates<Dim>),\n        py::arg(\"element_ids\"), py::arg(\"block_coord_holders\"));\n}\n}  // namespace\n\nvoid bind_element_logical_coordinates(py::module& m) {  // NOLINT\n  bind_element_logical_coordinates_impl<1>(m);\n  bind_element_logical_coordinates_impl<2>(m);\n  bind_element_logical_coordinates_impl<3>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/ElementLogicalCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_element_logical_coordinates(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/ElementMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/ElementMap.hpp\"\n\n#include <array>\n#include <memory>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid bind_element_map_impl(py::module& m) {  // NOLINT\n  // We construct a `Composition` coordinate map here instead of the\n  // `ElementMap` class so we can construct a full time-dependent ElementLogical\n  // -> Inertial map. We could actually replace the `ElementMap` class\n  // throughout the code with a `Composition`, since `ElementMap` is just a\n  // composition in disguise. Note that in evolutions we want to keep the\n  // (static) ElementLogical -> Grid part of the composition separate from the\n  // (time-dependent) Grid -> Inertial part to avoid reevaluating the static\n  // part at every time step. This probably isn't important in Python, but could\n  // be done as well.\n  //\n  // We return the type-erased base class `CoordinateMapBase` here, but could\n  // also bind and return the `Composition` class if we need to in the future\n  // (e.g. if we want access to parts of the composition in Python).\n  m.def(\n      \"ElementMap\",\n      [](const ElementId<Dim>& element_id, const Domain<Dim>& domain)\n          -> std::unique_ptr<\n              CoordinateMapBase<Frame::ElementLogical, Frame::Inertial, Dim>> {\n        const auto& block = domain.blocks()[element_id.block_id()];\n        // ElementLogical -> BlockLogical\n        auto element_to_block_logical_map =\n            domain::element_to_block_logical_map(element_id);\n        // BlockLogical -> Inertial\n        if (block.is_time_dependent()) {\n          if (block.has_distorted_frame()) {\n            using CompositionType = CoordinateMaps::Composition<\n                tmpl::list<Frame::ElementLogical, Frame::BlockLogical,\n                           Frame::Grid, Frame::Distorted, Frame::Inertial>,\n                Dim>;\n            return std::make_unique<CompositionType>(\n                std::move(element_to_block_logical_map),\n                block.moving_mesh_logical_to_grid_map().get_clone(),\n                block.moving_mesh_grid_to_distorted_map().get_clone(),\n                block.moving_mesh_distorted_to_inertial_map().get_clone());\n          } else {\n            using CompositionType = CoordinateMaps::Composition<\n                tmpl::list<Frame::ElementLogical, Frame::BlockLogical,\n                           Frame::Grid, Frame::Inertial>,\n                Dim>;\n            return std::make_unique<CompositionType>(\n                std::move(element_to_block_logical_map),\n                block.moving_mesh_logical_to_grid_map().get_clone(),\n                block.moving_mesh_grid_to_inertial_map().get_clone());\n          }\n        } else {\n          using CompositionType = CoordinateMaps::Composition<\n              tmpl::list<Frame::ElementLogical, Frame::BlockLogical,\n                         Frame::Inertial>,\n              Dim>;\n          return std::make_unique<CompositionType>(\n              std::move(element_to_block_logical_map),\n              block.stationary_map().get_clone());\n        }\n      },\n      py::arg(\"element_id\"), py::arg(\"domain\"));\n}\n}  // namespace\n\nvoid bind_element_map(py::module& m) {  // NOLINT\n  bind_element_map_impl<1>(m);\n  bind_element_map_impl<2>(m);\n  bind_element_map_impl<3>(m);\n}\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/ElementMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_element_map(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/FunctionsOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/FunctionsOfTime.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nvoid bind_functions_of_time(py::module& m) {  // NOLINT\n  domain::FunctionsOfTime::register_derived_with_charm();\n  py::class_<FunctionsOfTime::FunctionOfTime>(m, \"FunctionOfTime\")\n      .def(\"time_bounds\", &FunctionsOfTime::FunctionOfTime::time_bounds)\n      .def(\"func\", &FunctionsOfTime::FunctionOfTime::func, py::arg(\"t\"))\n      .def(\"func_and_deriv\", &FunctionsOfTime::FunctionOfTime::func_and_deriv,\n           py::arg(\"t\"))\n      .def(\"func_and_2_derivs\",\n           &FunctionsOfTime::FunctionOfTime::func_and_2_derivs, py::arg(\"t\"));\n  const auto bind_piecewise_polynomial = [&]<size_t Order>() {\n    const std::string class_name =\n        \"PiecewisePolynomial\" + std::to_string(Order);\n    py::class_<FunctionsOfTime::PiecewisePolynomial<Order>,\n               FunctionsOfTime::FunctionOfTime>(m, class_name.c_str())\n        .def(py::init<double, std::array<DataVector, Order + 1>, double>(),\n             py::arg(\"time\"), py::arg(\"initial_func_and_derivs\"),\n             py::arg(\"expiration_time\"));\n  };\n  bind_piecewise_polynomial.template operator()<2>();\n  bind_piecewise_polynomial.template operator()<3>();\n  py::class_<FunctionsOfTime::QuaternionFunctionOfTime<3>,\n             FunctionsOfTime::FunctionOfTime>(m, \"QuaternionFunctionOfTime\")\n      .def(py::init<double, std::array<DataVector, 1>,\n                    std::array<DataVector, 4>, double>(),\n           py::arg(\"time\"), py::arg(\"initial_quat_func\"),\n           py::arg(\"initial_angle_func\"), py::arg(\"expiration_time\"))\n      .def(\"quat_func\", &FunctionsOfTime::FunctionOfTime::func, py::arg(\"t\"))\n      .def(\"quat_func_and_deriv\",\n           &FunctionsOfTime::FunctionOfTime::func_and_deriv, py::arg(\"t\"))\n      .def(\"quat_func_and_2_derivs\",\n           &FunctionsOfTime::FunctionOfTime::func_and_2_derivs, py::arg(\"t\"));\n  m.def(\n      \"serialize_functions_of_time\",\n      [](const std::unordered_map<\n          std::string, const domain::FunctionsOfTime::FunctionOfTime&>&\n             functions_of_time) {\n        std::unordered_map<\n            std::string,\n            std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n            unique_functions_of_time{};\n        for (const auto& key_value : functions_of_time) {\n          unique_functions_of_time[key_value.first] =\n              functions_of_time.at(key_value.first).get_clone();\n        }\n        return serialize<std::unordered_map<\n            std::string,\n            std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>>(\n            unique_functions_of_time);\n      },\n      py::arg(\"functions_of_time\"));\n  m.def(\n      \"deserialize_functions_of_time\",\n      [](const std::vector<char>& serialized_functions_of_time) {\n        return deserialize<std::unordered_map<\n            std::string,\n            std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>>(\n            serialized_functions_of_time.data());\n      },\n      py::arg(\"serialized_functions_of_time\"));\n}\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/FunctionsOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_functions_of_time(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/JacobianDiagnostic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/JacobianDiagnostic.hpp\"\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/JacobianDiagnostic.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\ntemplate <size_t Dim, typename TargetFrame>\nvoid bind_jacobian_diagnostic_impl(py::module& m) {  // NOLINT\n  m.def(\n      \"jacobian_diagnostic\",\n      static_cast<tnsr::i<DataVector, Dim, typename Frame::ElementLogical> (*)(\n          const ::Jacobian<DataVector, Dim, Frame::ElementLogical,\n                           TargetFrame>&,\n          const tnsr::I<DataVector, Dim, TargetFrame>&, const ::Mesh<Dim>&)>(\n          &::domain::jacobian_diagnostic<Dim, TargetFrame>),\n      py::arg(\"jacobian\"),\n      py::arg(std::is_same_v<TargetFrame, Frame::Inertial> ? \"inertial_coords\"\n                                                           : \"grid_coords\"),\n      py::arg(\"mesh\"));\n}\n}  // namespace\n\nvoid bind_jacobian_diagnostic(py::module& m) {  // NOLINT\n  bind_jacobian_diagnostic_impl<1, Frame::Grid>(m);\n  bind_jacobian_diagnostic_impl<2, Frame::Grid>(m);\n  bind_jacobian_diagnostic_impl<3, Frame::Grid>(m);\n  bind_jacobian_diagnostic_impl<1, Frame::Inertial>(m);\n  bind_jacobian_diagnostic_impl<2, Frame::Inertial>(m);\n  bind_jacobian_diagnostic_impl<3, Frame::Inertial>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/JacobianDiagnostic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_jacobian_diagnostic(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/RadiallyCompressedCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/RadiallyCompressedCoordinates.hpp\"\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/RadiallyCompressedCoordinates.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nnamespace {\ntemplate <size_t Dim, typename CoordsFrame>\nvoid bind_radially_compressed_coordinates_impl(py::module& m) {  // NOLINT\n  m.def(\"radially_compressed_coordinates\",\n        static_cast<tnsr::I<DataVector, Dim, CoordsFrame> (*)(\n            const tnsr::I<DataVector, Dim, CoordsFrame>&, double, double,\n            CoordinateMaps::Distribution)>(\n            &radially_compressed_coordinates<DataVector, Dim, CoordsFrame>),\n        py::arg(std::is_same_v<CoordsFrame, Frame::Inertial> ? \"inertial_coords\"\n                                                             : \"grid_coords\"),\n        py::arg(\"inner_radius\"), py::arg(\"outer_radius\"),\n        py::arg(\"compression\"));\n}\n}  // namespace\n\nvoid bind_radially_compressed_coordinates(py::module& m) {  // NOLINT\n  bind_radially_compressed_coordinates_impl<1, Frame::Inertial>(m);\n  bind_radially_compressed_coordinates_impl<2, Frame::Inertial>(m);\n  bind_radially_compressed_coordinates_impl<3, Frame::Inertial>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/RadiallyCompressedCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_radially_compressed_coordinates(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/SegmentId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/SegmentId.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\n\nvoid bind_segment_id(py::module& m) {\n  // These bindings don't cover the full public interface yet. More bindings\n  // can be added as needed.\n  py::class_<SegmentId>(m, \"SegmentId\")\n      .def(py::init<size_t, size_t>(), py::arg(\"refinement_level\"),\n           py::arg(\"index\"))\n      .def_property(\"refinement_level\", &SegmentId::refinement_level, nullptr)\n      .def_property(\"index\", &SegmentId::index, nullptr)\n      .def_property(\n          \"endpoints\",\n          [](const SegmentId& segment_id) {\n            return std::array<double, 2>{{segment_id.endpoint(Side::Lower),\n                                          segment_id.endpoint(Side::Upper)}};\n          },\n          nullptr)\n      .def(\"__repr__\",\n           [](const SegmentId& segment_id) { return get_output(segment_id); })\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/SegmentId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_segment_id(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/StrahlkorperTransformations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Python/StrahlkorperTransformations.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/StrahlkorperTransformations.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n\nnamespace py = pybind11;\n\nnamespace domain::py_bindings {\nnamespace {\n\nusing PyFunctionsOfTimeMap =\n    std::unordered_map<std::string,\n                       const domain::FunctionsOfTime::FunctionOfTime&>;\n\n// Transform functions-of-time map to unique_ptrs because pybind11\n// can't handle them easily as function arguments (it's hard to\n// transfer ownership of a Python object to C++)\ndomain::FunctionsOfTimeMap transform_functions_of_time(\n    const std::optional<PyFunctionsOfTimeMap>& functions_of_time) {\n  domain::FunctionsOfTimeMap functions_of_time_ptrs{};\n  if (functions_of_time.has_value()) {\n    for (const auto& [name, fot] : *functions_of_time) {\n      functions_of_time_ptrs[name] = fot.get_clone();\n    }\n  }\n  return functions_of_time_ptrs;\n}\n\ntemplate <typename SrcFrame, typename DestFrame>\nvoid bind_strahlkorper_transformations_impl(py::module& m) {  // NOLINT\n  m.def(\n      \"strahlkorper_in_inertial_frame\",\n      [](const ylm::Strahlkorper<SrcFrame>& strahlkorper,\n         const Domain<3>& domain,\n         const std::optional<PyFunctionsOfTimeMap>& functions_of_time,\n         const std::optional<double>& time) {\n        ylm::Strahlkorper<DestFrame> result{};\n        strahlkorper_in_different_frame<SrcFrame, DestFrame>(\n            make_not_null(&result), strahlkorper, domain,\n            transform_functions_of_time(functions_of_time),\n            time.value_or(std::numeric_limits<double>::signaling_NaN()));\n        return result;\n      },\n      py::arg(\"strahlkorper\"), py::arg(\"domain\"),\n      py::arg(\"functions_of_time\") = std::nullopt,\n      py::arg(\"time\") = std::nullopt);\n  m.def(\n      \"strahlkorper_in_inertial_frame_aligned\",\n      [](const ylm::Strahlkorper<SrcFrame>& strahlkorper,\n         const Domain<3>& domain,\n         const std::optional<PyFunctionsOfTimeMap>& functions_of_time,\n         const std::optional<double>& time) {\n        ylm::Strahlkorper<DestFrame> result{};\n        strahlkorper_in_different_frame_aligned<SrcFrame, DestFrame>(\n            make_not_null(&result), strahlkorper, domain,\n            transform_functions_of_time(functions_of_time),\n            time.value_or(std::numeric_limits<double>::signaling_NaN()));\n        return result;\n      },\n      py::arg(\"strahlkorper\"), py::arg(\"domain\"),\n      py::arg(\"functions_of_time\") = std::nullopt,\n      py::arg(\"time\") = std::nullopt);\n}\n}  // namespace\n\nvoid bind_strahlkorper_transformations(py::module& m) {  // NOLINT\n  // Only instantiating for Grid->Inertial because the Py functions are\n  // currently named like that\n  bind_strahlkorper_transformations_impl<Frame::Grid, Frame::Inertial>(m);\n}\n\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/StrahlkorperTransformations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace domain::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_strahlkorper_transformations(pybind11::module& m);\n}  // namespace domain::py_bindings\n"
  },
  {
    "path": "src/Domain/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n\nBlock = {1: Block1D, 2: Block2D, 3: Block3D}\nDirection = {1: Direction1D, 2: Direction2D, 3: Direction3D}\nDomain = {1: Domain1D, 2: Domain2D, 3: Domain3D}\nElementId = {1: ElementId1D, 2: ElementId2D, 3: ElementId3D}\n\ndeserialize_domain = {\n    1: deserialize_domain_1d,\n    2: deserialize_domain_2d,\n    3: deserialize_domain_3d,\n}\n"
  },
  {
    "path": "src/Domain/RadiallyCompressedCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/RadiallyCompressedCoordinates.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace domain {\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\nvoid radially_compressed_coordinates(\n    const gsl::not_null<tnsr::I<DataType, Dim, CoordsFrame>*> result,\n    const tnsr::I<DataType, Dim, CoordsFrame>& coordinates,\n    const double inner_radius, const double outer_radius,\n    const CoordinateMaps::Distribution compression) {\n  const DataType radius = get(magnitude(coordinates));\n  // Return early if all radii are within the inner radius\n  if (max(radius) <= inner_radius + 1.e-14) {\n    *result = coordinates;\n    return;\n  }\n  // The compressed outer radius is chosen so it increases with both the inner\n  // and the outer radius but exponentials are tamed\n  const double compressed_outer_radius = inner_radius * log10(outer_radius);\n  // We use the inverse of the Interval map, which is also used to distribute\n  // grid points radially\n  CoordinateMaps::Interval interval{inner_radius, compressed_outer_radius,\n                                    inner_radius, outer_radius,\n                                    compression,  0.};\n  DataType compressed_radius = radius;\n  for (size_t i = 0; i < get_size(radius); ++i) {\n    if (get_element(radius, i) > inner_radius + 1.e-14) {\n      get_element(compressed_radius, i) =\n          interval.inverse({{get_element(radius, i)}}).value()[0];\n    }\n  }\n  *result = coordinates;\n  for (size_t d = 0; d < Dim; ++d) {\n    result->get(d) *= compressed_radius / radius;\n  }\n}\n\nvoid RadiallyCompressedCoordinatesOptions::pup(PUP::er& p) {\n  p | inner_radius;\n  p | outer_radius;\n  p | compression;\n}\n\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\ntnsr::I<DataType, Dim, CoordsFrame> radially_compressed_coordinates(\n    const tnsr::I<DataType, Dim, CoordsFrame>& coordinates,\n    const double inner_radius, const double outer_radius,\n    const CoordinateMaps::Distribution compression) {\n  tnsr::I<DataType, Dim, CoordsFrame> result{};\n  radially_compressed_coordinates(make_not_null(&result), coordinates,\n                                  inner_radius, outer_radius, compression);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void radially_compressed_coordinates(                           \\\n      gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*> result, \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& coordinates,     \\\n      double inner_radius, double outer_radius,                            \\\n      CoordinateMaps::Distribution compression);                           \\\n  template tnsr::I<DTYPE(data), DIM(data), FRAME(data)>                    \\\n  radially_compressed_coordinates(                                         \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& coordinates,     \\\n      double inner_radius, double outer_radius,                            \\\n      CoordinateMaps::Distribution compression);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector), (1, 2, 3),\n                        (Frame::Inertial))\n\n#undef DTYPE\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/RadiallyCompressedCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace domain {\n\n/// @{\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Coordinates suitable for visualizing large radii by compressing them\n * logarithmically or inversely.\n *\n * Rescales the coordinates $\\boldsymbol{x}$ as\n *\n * \\begin{equation}\n * \\hat{\\boldsymbol{x}} = \\frac{\\hat{r}}{r} \\boldsymbol{x}\n * \\text{,}\n * \\end{equation}\n *\n * for $r > r_0$, where $r=\\sqrt{x^2+y^2+z^2}$ is the Euclidean coordinate\n * radius and $r_0$ is the `inner_radius`.\n * The coordinates are compressed from $r \\in [r_0, r_1]$ to $\\hat{r} \\in [r_0,\n * \\hat{r}_1]$, where the `outer_radius` $r_1$ can be incomprehensibly large\n * like $10^9$ and the compressed outer radius $\\hat{r}_1$ is reasonably small\n * so it can be visualized well. We choose\n *\n * \\begin{equation}\n * \\hat{r}_1 = r_0 \\log_{10}(r_1)\n * \\end{equation}\n *\n * so the compressed outer radius is a multiple of the inner radius and\n * increases with the outer radius as well, but exponentials are tamed.\n *\n * The radial compression map $\\hat{r}(r)$ is just the inverse of the\n * `domain::CoordinateMaps::Interval` map, which is also used to distribute grid\n * points radially. Therefore, radial grid points will be distributed linearly\n * in the radially compressed coordinates if you use the same `compression`\n * distribution that you used to distribute radial grid points in the\n * `CoordsFrame`.\n *\n * \\see domain::CoordinateMaps::Interval\n */\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\nvoid radially_compressed_coordinates(\n    gsl::not_null<tnsr::I<DataType, Dim, CoordsFrame>*> result,\n    const tnsr::I<DataType, Dim, CoordsFrame>& coordinates, double inner_radius,\n    double outer_radius, CoordinateMaps::Distribution compression);\ntemplate <typename DataType, size_t Dim, typename CoordsFrame>\ntnsr::I<DataType, Dim, CoordsFrame> radially_compressed_coordinates(\n    const tnsr::I<DataType, Dim, CoordsFrame>& coordinates, double inner_radius,\n    double outer_radius, CoordinateMaps::Distribution compression);\n/// @}\n\n/// Options for radially compressed coordinates\n///\n/// \\see radially_compressed_coordinates\nstruct RadiallyCompressedCoordinatesOptions {\n  static constexpr Options::String help =\n      \"Define radially compressed coordinates for visualizing large outer \"\n      \"radii.\";\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help =\n        \"Radially compressed coordinates begin at this radius, and coincide \"\n        \"with the original coordinates for smaller radii.\";\n  };\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help =\n        \"Outer radius of the domain which will be compressed down to a \"\n        \"comprehensible radius, namely to r_inner * log10(r_outer).\";\n  };\n  struct Compression {\n    using type = CoordinateMaps::Distribution;\n    static constexpr Options::String help =\n        \"Compression mode: 'Logarithmic' or 'Inverse'. If you use the same \"\n        \"mode that you used to distribute radial grid points then the \"\n        \"grid points will be distributed linearly in the radially compressed \"\n        \"coordinates.\";\n  };\n  using options = tmpl::list<InnerRadius, OuterRadius, Compression>;\n  void pup(PUP::er& p);\n  double inner_radius;\n  double outer_radius;\n  CoordinateMaps::Distribution compression;\n};\n\nnamespace OptionTags {\n\nstruct RadiallyCompressedCoordinates {\n  using type = Options::Auto<domain::RadiallyCompressedCoordinatesOptions,\n                             Options::AutoLabel::None>;\n  static constexpr Options::String help =\n      \"Define radially compressed coordinates for visualizing large outer \"\n      \"radii.\";\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n\n/// Options for radially compressed coordinates, or `std::nullopt` if none\n/// were specified in the input file\n///\n/// \\see radially_compressed_coordinates\nstruct RadiallyCompressedCoordinatesOptions : db::SimpleTag {\n  using type = std::optional<domain::RadiallyCompressedCoordinatesOptions>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::RadiallyCompressedCoordinates>;\n  static type create_from_options(type value) { return value; };\n};\n\n/// @{\n/*!\n * \\brief Coordinates suitable for visualizing large radii by compressing them\n * logarithmically or inversely.\n *\n * The coordinate map reduces to the identity if no options for radially\n * compressed coordinates were specified in the input file.\n *\n * \\see radially_compressed_coordinates\n */\ntemplate <size_t Dim, typename CoordsFrame>\nstruct RadiallyCompressedCoordinates : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, CoordsFrame>;\n};\n\ntemplate <size_t Dim, typename CoordsFrame>\nstruct RadiallyCompressedCoordinatesCompute\n    : RadiallyCompressedCoordinates<Dim, CoordsFrame>,\n      db::ComputeTag {\n  using base = RadiallyCompressedCoordinates<Dim, CoordsFrame>;\n  using return_type = tnsr::I<DataVector, Dim, CoordsFrame>;\n  using argument_tags = tmpl::list<Tags::Coordinates<Dim, CoordsFrame>,\n                                   Tags::RadiallyCompressedCoordinatesOptions>;\n  static void function(\n      const gsl::not_null<tnsr::I<DataVector, Dim, CoordsFrame>*> result,\n      const tnsr::I<DataVector, Dim, CoordsFrame>& coords,\n      const std::optional<domain::RadiallyCompressedCoordinatesOptions>&\n          options) {\n    if (options.has_value()) {\n      radially_compressed_coordinates(result, coords, options->inner_radius,\n                                      options->outer_radius,\n                                      options->compression);\n    } else {\n      *result = coords;\n    }\n  }\n};\n/// @}\n\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/SizeOfElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/SizeOfElement.hpp\"\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\n// The face-center coordinates for the unit cube's face in direction `dir`, so,\n// * in 1D, returns (-1), (1)\n// * in 2D, returns (-1, 0), (1, 0), (0, -1), (0, 1)\n// * in 3D, returns (-1, 0, 0), ..., (0, 0, 1)\ntemplate <size_t VolumeDim>\ninline tnsr::I<double, VolumeDim, Frame::ElementLogical> logical_face_center(\n    const Direction<VolumeDim>& dir) {\n  tnsr::I<double, VolumeDim, Frame::ElementLogical> result{{{0.0}}};\n  result.get(dir.dimension()) = (dir.side() == Side::Lower ? -1.0 : 1.0);\n  return result;\n}\n\ntemplate <size_t VolumeDim>\ninline double distance_between_face_centers(\n    const tnsr::I<double, VolumeDim, Frame::Inertial>& lower_center,\n    const tnsr::I<double, VolumeDim, Frame::Inertial>& upper_center) {\n  auto center_to_center = make_array<VolumeDim>(0.0);\n  alg::transform(upper_center, lower_center, center_to_center.begin(),\n                 std::minus<>{});\n  return magnitude(center_to_center);\n}\n}  // namespace\n\ntemplate <size_t VolumeDim>\nstd::array<double, VolumeDim> size_of_element(\n    const ElementMap<VolumeDim, Frame::Inertial>& logical_to_inertial_map) {\n  auto result = make_array<VolumeDim>(0.0);\n  for (size_t logical_index = 0; logical_index < VolumeDim; ++logical_index) {\n    const auto inertial_face_center =\n        [&logical_index, &logical_to_inertial_map](const Side& side) {\n          const Direction<VolumeDim> dir(logical_index, side);\n          return logical_to_inertial_map(logical_face_center(dir));\n        };\n    const auto lower_center = inertial_face_center(Side::Lower);\n    const auto upper_center = inertial_face_center(Side::Upper);\n    result.at(logical_index) =\n        distance_between_face_centers(lower_center, upper_center);\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nstd::array<double, VolumeDim> size_of_element(\n    const ElementMap<VolumeDim, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n        grid_to_inertial_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  auto result = make_array<VolumeDim>(0.0);\n  for (size_t logical_index = 0; logical_index < VolumeDim; ++logical_index) {\n    const auto inertial_face_center = [&functions_of_time,\n                                       &grid_to_inertial_map, &logical_index,\n                                       &logical_to_grid_map,\n                                       time](const Side& side) {\n      const Direction<VolumeDim> dir(logical_index, side);\n      return grid_to_inertial_map(logical_to_grid_map(logical_face_center(dir)),\n                                  time, functions_of_time);\n    };\n    const auto lower_center = inertial_face_center(Side::Lower);\n    const auto upper_center = inertial_face_center(Side::Upper);\n    result.at(logical_index) =\n        distance_between_face_centers(lower_center, upper_center);\n  }\n  return result;\n}\n\n// Explicit instantiations\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template std::array<double, GET_DIM(data)> size_of_element(               \\\n      const ElementMap<GET_DIM(data), Frame::Inertial>&                     \\\n          logical_to_inertial_map);                                         \\\n  template std::array<double, GET_DIM(data)> size_of_element(               \\\n      const ElementMap<GET_DIM(data), Frame::Grid>& logical_to_grid_map,    \\\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial,         \\\n                                      GET_DIM(data)>& grid_to_inertial_map, \\\n      const double time,                                                    \\\n      const std::unordered_map<                                             \\\n          std::string,                                                      \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&        \\\n          functions_of_time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/SizeOfElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim>\nclass CoordinateMapBase;\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\n}  // namespace domain\ntemplate <size_t VolumeDim, typename Frame>\nclass ElementMap;\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct ElementMap;\n}  // namespace Tags\n}  // namespace domain\n/// \\endcond\n\n/// @{\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Compute the inertial-coordinate size of an element along each of its\n * logical directions.\n *\n * For each logical direction, compute the distance (in inertial coordinates)\n * between the element's lower and upper faces in that logical direction.\n * The distance is measured between centers of the faces, with the centers\n * defined in the logical coordinates.\n * Note that for curved elements, this is an approximate measurement of size.\n *\n * \\details\n * Because this quantity is defined in terms of specific coordinates, it is\n * not well represented by a `Tensor`, so we use a `std::array`.\n */\ntemplate <size_t VolumeDim>\nstd::array<double, VolumeDim> size_of_element(\n    const ElementMap<VolumeDim, Frame::Inertial>& logical_to_inertial_map);\n\ntemplate <size_t VolumeDim>\nstd::array<double, VolumeDim> size_of_element(\n    const ElementMap<VolumeDim, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n        grid_to_inertial_map,\n    double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time);\n/// @}\n\nnamespace domain {\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The inertial-coordinate size of an element along each of its logical\n/// directions.\ntemplate <size_t VolumeDim>\nstruct SizeOfElement : db::SimpleTag {\n  using type = std::array<double, VolumeDim>;\n};\n\ntemplate <size_t VolumeDim>\nstruct SizeOfElementCompute : db::ComputeTag, SizeOfElement<VolumeDim> {\n  using base = SizeOfElement<VolumeDim>;\n  using argument_tags =\n      tmpl::list<Tags::ElementMap<VolumeDim, Frame::Grid>,\n                 CoordinateMaps::Tags::CoordinateMap<VolumeDim, Frame::Grid,\n                                                     Frame::Inertial>,\n                 ::Tags::Time, domain::Tags::FunctionsOfTime>;\n  using return_type = typename base::type;\n\n  static constexpr void function(\n      gsl::not_null<std::array<double, VolumeDim>*> result,\n      const ::ElementMap<VolumeDim, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>&\n          grid_to_inertial_map,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    *result = size_of_element(logical_to_grid_map, grid_to_inertial_map, time,\n                              functions_of_time);\n  }\n};\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/StrahlkorperTransformations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/StrahlkorperTransformations.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <stdexcept>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordsToDifferentFrame.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"NumericalAlgorithms/RootFinding/RootBracketing.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\ntemplate <typename SrcFrame, typename DestFrame>\nvoid strahlkorper_in_different_frame(\n    const gsl::not_null<ylm::Strahlkorper<DestFrame>*> dest_strahlkorper,\n    const ylm::Strahlkorper<SrcFrame>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double time) {\n  // Temporary storage; reduce the number of allocations.\n  Variables<\n      tmpl::list<::Tags::Tempi<0, 2, ::Frame::Spherical<SrcFrame>>,\n                 ::Tags::Tempi<1, 3, SrcFrame>, ::Tags::TempI<2, 3, SrcFrame>,\n                 ::Tags::TempI<3, 3, DestFrame>, ::Tags::TempScalar<4>,\n                 ::Tags::TempScalar<5>, ::Tags::TempScalar<6>,\n                 ::Tags::TempScalar<7>, ::Tags::TempScalar<8>>>\n      temp_buffer(src_strahlkorper.ylm_spherepack().physical_size());\n  auto& src_theta_phi =\n      get<::Tags::Tempi<0, 2, ::Frame::Spherical<SrcFrame>>>(temp_buffer);\n  auto& r_hat = get<::Tags::Tempi<1, 3, SrcFrame>>(temp_buffer);\n  auto& src_cartesian_coords = get<Tags::TempI<2, 3, SrcFrame>>(temp_buffer);\n  auto& dest_cartesian_coords = get<Tags::TempI<3, 3, DestFrame>>(temp_buffer);\n  auto& src_radius = get<::Tags::TempScalar<4>>(temp_buffer);\n  auto& bracket_r_min = get<::Tags::TempScalar<5>>(temp_buffer);\n  auto& bracket_r_max = get<::Tags::TempScalar<6>>(temp_buffer);\n  auto& f_bracket_r_min = get<::Tags::TempScalar<7>>(temp_buffer);\n  auto& f_bracket_r_max = get<::Tags::TempScalar<8>>(temp_buffer);\n\n  ylm::theta_phi(make_not_null(&src_theta_phi), src_strahlkorper);\n  // r_hat doesn't depend on the actual surface (that is, it is\n  // identical for the src and dest surfaces), so we use\n  // src_strahlkorper to compute it because it has a sensible max Ylm l.\n  ylm::rhat(make_not_null(&r_hat), src_theta_phi);\n  ylm::radius(make_not_null(&src_radius), src_strahlkorper);\n  ylm::cartesian_coords(make_not_null(&src_cartesian_coords), src_strahlkorper,\n                        src_radius, r_hat);\n\n  coords_to_different_frame<SrcFrame, DestFrame>(\n      make_not_null(&dest_cartesian_coords), src_cartesian_coords, domain,\n      functions_of_time, time);\n\n  // To find the expansion center of the destination surface, take a\n  // simple average of the Cartesian coordinates of the surface in the\n  // destination frame.  An average should be good enough, since this\n  // is only the expansion center (not the physical center), so its\n  // only requirement is that it is centered enough so that the\n  // surface is star-shaped.  If we want to re-center the strahlkorper later, we\n  // can call `ylm::change_expansion_center_of_strahlkorper_to_physical`.\n  const auto center_dest = [&dest_cartesian_coords]() {\n    std::array<double, 3> center{};\n    for (size_t d = 0; d < 3; ++d) {\n      gsl::at(center, d) =\n          std::accumulate(dest_cartesian_coords.get(d).begin(),\n                          dest_cartesian_coords.get(d).end(), 0.0) /\n          dest_cartesian_coords.get(d).size();\n    }\n    return center;\n  }();\n\n  const auto center_src = src_strahlkorper.expansion_center();\n\n  // Find the coordinate radius of the destination surface at each of\n  // the angular collocation points of the destination surface. To do\n  // so, for each index 's' (corresponding to an angular collocation\n  // point), find the root 'r_dest' that zeroes this lambda function.\n  //\n  // This version of the function returns a std::optional<double>\n  // because it might fail if r_dest is out of range of the map. Below\n  // there is another version of the function that returns a double.\n  const auto radius_function_for_bracketing =\n      [&r_hat, &center_src, &center_dest, &src_strahlkorper, &domain,\n       &functions_of_time,\n       &time](const double r_dest, const size_t s) -> std::optional<double> {\n    // Get destination Cartesian coordinates of the point.\n    const tnsr::I<DataVector, 3, DestFrame> x_dest{\n        {DataVector{r_dest * get<0>(r_hat)[s] + center_dest[0]},\n         DataVector{r_dest * get<1>(r_hat)[s] + center_dest[1]},\n         DataVector{r_dest * get<2>(r_hat)[s] + center_dest[2]}}};\n\n    const auto block_logical_coords =\n        block_logical_coordinates(domain, x_dest, time, functions_of_time);\n\n    if (not block_logical_coords[0].has_value()) {\n      // If we get here, the inverse has failed for every block, so the point is\n      // outside the domain (e.g. inside an excision boundary or outside the\n      // outer boundary), so we return an nullopt.\n      return std::nullopt;\n    }\n\n    const auto& block_id_and_coords = block_logical_coords[0].value();\n    const auto& block = domain.blocks()[block_id_and_coords.id.get_index()];\n\n    // Update for new instantiations\n    static_assert((std::is_same_v<SrcFrame, ::Frame::Grid> and\n                   std::is_same_v<DestFrame, ::Frame::Inertial>) or\n                      (std::is_same_v<SrcFrame, ::Frame::Inertial> and\n                       std::is_same_v<DestFrame, ::Frame::Distorted>) or\n                      (std::is_same_v<SrcFrame, ::Frame::Inertial> and\n                       std::is_same_v<DestFrame, ::Frame::Grid>),\n                  \"Invalid frame choices for strahlkorper_in_different_frame\");\n    tnsr::I<double, 3, SrcFrame> x_src{};\n    if constexpr (std::is_same_v<SrcFrame, ::Frame::Grid>) {\n      x_src = block.moving_mesh_logical_to_grid_map()(block_id_and_coords.data);\n    } else {\n      x_src = block.moving_mesh_grid_to_inertial_map()(\n          block.moving_mesh_logical_to_grid_map()(block_id_and_coords.data),\n          time, functions_of_time);\n    }\n    const double r_src =\n        std::hypot(get<0>(x_src) - center_src[0], get<1>(x_src) - center_src[1],\n                   get<2>(x_src) - center_src[2]);\n    const double theta_src = atan2(std::hypot(get<0>(x_src) - center_src[0],\n                                              get<1>(x_src) - center_src[1]),\n                                   get<2>(x_src) - center_src[2]);\n    const double phi_src =\n        atan2(get<1>(x_src) - center_src[1], get<0>(x_src) - center_src[0]);\n\n    // Evaluate the radius of the surface at (theta_src,phi_src).\n    const double r_surf_src = src_strahlkorper.radius(theta_src, phi_src);\n\n    // If r_surf_src = r_src, then r_dest is on the surface.\n    return r_surf_src - r_src;\n  };\n\n  // This version of radius_function returns a double, not a\n  // std::optional<double>, and will be used once the root is\n  // bracketed.\n  const auto radius_function = [&radius_function_for_bracketing](\n                                   const double r_dest, const size_t s) {\n    return radius_function_for_bracketing(r_dest, s).value();\n  };\n\n  // We try to roughly bracket the root between r_min and r_max.\n  double r_min{};\n  double r_max{};\n  std::tie(r_min, r_max) = [&dest_cartesian_coords, &center_dest]() {\n    const DataVector dest_radius =\n        sqrt(square(get<0>(dest_cartesian_coords) - center_dest[0]) +\n             square(get<1>(dest_cartesian_coords) - center_dest[1]) +\n             square(get<2>(dest_cartesian_coords) - center_dest[2]));\n    const auto minmax =\n        std::minmax_element(dest_radius.begin(), dest_radius.end());\n    return std::make_pair(*(minmax.first), *(minmax.second));\n  }();\n\n  // But r_min and r_max are only approximate, and there may be grid points\n  // with radii outside that range. So we pad by 10% to be safe.\n  const double padding = 0.10;\n  get(bracket_r_min) = r_min * (1.0 - padding);\n  get(bracket_r_max) = r_max * (1.0 + padding);\n  try {\n    RootFinder::bracket_possibly_undefined_function_in_interval(\n        make_not_null(&get(bracket_r_min)), make_not_null(&get(bracket_r_max)),\n        make_not_null(&get(f_bracket_r_min)),\n        make_not_null(&get(f_bracket_r_max)), radius_function_for_bracketing);\n  } catch (std::runtime_error& e) {\n    ERROR(\n        \"StrahlkorperInDifferentFrame: Trying to find radius of Strahlkorper \"\n        \"by root finding, but cannot bracket the root between \"\n        << r_min * (1.0 - padding) << \" and \" << r_max * (1.0 + padding)\n        << \". Maybe the padding interval needs to be increased?  Or maybe the \"\n           \"Strahlkorper is extremely close to the excision boundary? Internal \"\n           \"error message is \"\n        << e.what());\n  }\n  // Find the radius at each angular point by root finding.\n  const auto radius_at_each_angle = RootFinder::toms748<false>(\n      radius_function, get(bracket_r_min), get(bracket_r_max),\n      get(f_bracket_r_min), get(f_bracket_r_max),\n      std::numeric_limits<double>::epsilon() * (r_min + r_max),\n      2.0 * std::numeric_limits<double>::epsilon());\n\n  // Reset the radius and center of the destination strahlkorper.\n  // Keep the same l_max() and m_max() as the source strahlkorper.\n  *dest_strahlkorper = ylm::Strahlkorper<DestFrame>(\n      src_strahlkorper.l_max(), src_strahlkorper.m_max(), radius_at_each_angle,\n      center_dest);\n}\n\ntemplate <typename SrcFrame, typename DestFrame>\nvoid strahlkorper_in_different_frame_aligned(\n    const gsl::not_null<ylm::Strahlkorper<DestFrame>*> dest_strahlkorper,\n    const ylm::Strahlkorper<SrcFrame>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double time) {\n  // Temporary storage; reduce the number of allocations.\n  Variables<\n      tmpl::list<::Tags::Tempi<0, 2, ::Frame::Spherical<SrcFrame>>,\n                 ::Tags::Tempi<1, 3, SrcFrame>, ::Tags::TempI<2, 3, SrcFrame>,\n                 ::Tags::TempI<3, 3, DestFrame>, ::Tags::TempScalar<4>>>\n      temp_buffer(src_strahlkorper.ylm_spherepack().physical_size());\n  auto& src_theta_phi =\n      get<::Tags::Tempi<0, 2, ::Frame::Spherical<SrcFrame>>>(temp_buffer);\n  auto& r_hat = get<::Tags::Tempi<1, 3, SrcFrame>>(temp_buffer);\n  auto& src_cartesian_coords = get<Tags::TempI<2, 3, SrcFrame>>(temp_buffer);\n  auto& dest_cartesian_coords = get<Tags::TempI<3, 3, DestFrame>>(temp_buffer);\n  auto& radius = get<::Tags::TempScalar<4>>(temp_buffer);\n\n  ylm::theta_phi(make_not_null(&src_theta_phi), src_strahlkorper);\n  // r_hat doesn't depend on the actual surface (that is, it is\n  // identical for the src and dest surfaces), so we use\n  // src_strahlkorper to compute it because it has a sensible max Ylm l.\n  ylm::rhat(make_not_null(&r_hat), src_theta_phi);\n  ylm::radius(make_not_null(&radius), src_strahlkorper);\n  ylm::cartesian_coords(make_not_null(&src_cartesian_coords), src_strahlkorper,\n                        radius, r_hat);\n\n  coords_to_different_frame<SrcFrame, DestFrame>(\n      make_not_null(&dest_cartesian_coords), src_cartesian_coords, domain,\n      functions_of_time, time);\n\n  // Fill radius with the radius in the destination frame.\n  const auto center = src_strahlkorper.expansion_center();\n  for (size_t s = 0; s < src_strahlkorper.ylm_spherepack().physical_size();\n       ++s) {\n    get(radius)[s] = std::hypot(get<0>(dest_cartesian_coords)[s] - center[0],\n                                get<1>(dest_cartesian_coords)[s] - center[1],\n                                get<2>(dest_cartesian_coords)[s] - center[2]);\n  }\n  // Because center and angles are preserved by the map, we can easily\n  // construct the destination Strahlkorper.\n  *dest_strahlkorper = ylm::Strahlkorper<DestFrame>(\n      src_strahlkorper.l_max(), src_strahlkorper.m_max(), get(radius), center);\n}\n\ntemplate <typename SrcFrame, typename DestFrame>\nvoid strahlkorper_coords_in_different_frame(\n    const gsl::not_null<tnsr::I<DataVector, 3, DestFrame>*>\n        dest_cartesian_coords,\n    const ylm::Strahlkorper<SrcFrame>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double time) {\n  static_assert(std::is_same_v<DestFrame, ::Frame::Inertial>,\n                \"Destination frame must currently be Inertial frame\");\n  set_number_of_grid_points(dest_cartesian_coords,\n                            src_strahlkorper.ylm_spherepack().physical_size());\n\n  // Temporary storage; reduce the number of allocations.\n  Variables<tmpl::list<::Tags::Tempi<0, 2, ::Frame::Spherical<SrcFrame>>,\n                       ::Tags::Tempi<1, 3, SrcFrame>,\n                       ::Tags::TempI<2, 3, SrcFrame>, ::Tags::TempScalar<3>>>\n      temp_buffer(src_strahlkorper.ylm_spherepack().physical_size());\n  auto& src_theta_phi =\n      get<::Tags::Tempi<0, 2, ::Frame::Spherical<SrcFrame>>>(temp_buffer);\n  auto& r_hat = get<::Tags::Tempi<1, 3, SrcFrame>>(temp_buffer);\n  auto& src_cartesian_coords = get<Tags::TempI<2, 3, SrcFrame>>(temp_buffer);\n  auto& src_radius = get<::Tags::TempScalar<3>>(temp_buffer);\n\n  ylm::Tags::ThetaPhiCompute<SrcFrame>::function(make_not_null(&src_theta_phi),\n                                                 src_strahlkorper);\n  ylm::Tags::RhatCompute<SrcFrame>::function(make_not_null(&r_hat),\n                                             src_theta_phi);\n  ylm::Tags::RadiusCompute<SrcFrame>::function(make_not_null(&src_radius),\n                                               src_strahlkorper);\n  ylm::Tags::CartesianCoordsCompute<SrcFrame>::function(\n      make_not_null(&src_cartesian_coords), src_strahlkorper, src_radius,\n      r_hat);\n\n  // We now wish to map src_cartesian_coords to the destination frame.\n  // Each Block will have a different map, so the mapping must be done\n  // Block by Block.  Since each point in general has a different\n  // block (given by block_logical_coordinates), this means we must\n  // map point by point.\n  const auto block_logical_coords = block_logical_coordinates(\n      domain, src_cartesian_coords, time, functions_of_time);\n  for (size_t s = 0; s < block_logical_coords.size(); ++s) {\n    const tnsr::I<double, 3, SrcFrame> x_src{{get<0>(src_cartesian_coords)[s],\n                                              get<1>(src_cartesian_coords)[s],\n                                              get<2>(src_cartesian_coords)[s]}};\n    if (UNLIKELY(not block_logical_coords[s].has_value())) {\n      ERROR(\"A point on the Strahlkorper in the \"\n            << SrcFrame{} << \" could not be mapped to a block: \" << x_src);\n    }\n    const auto& block =\n        domain.blocks()[block_logical_coords[s].value().id.get_index()];\n    if (block.is_time_dependent()) {\n      if constexpr (std::is_same_v<SrcFrame, ::Frame::Grid>) {\n        const auto x_dest = block.moving_mesh_grid_to_inertial_map()(\n            x_src, time, functions_of_time);\n        get<0>(*dest_cartesian_coords)[s] = get<0>(x_dest);\n        get<1>(*dest_cartesian_coords)[s] = get<1>(x_dest);\n        get<2>(*dest_cartesian_coords)[s] = get<2>(x_dest);\n      } else {\n        static_assert(std::is_same_v<SrcFrame, ::Frame::Distorted>,\n                      \"Src frame must be Distorted if it is not Grid\");\n        const auto x_dest = block.moving_mesh_distorted_to_inertial_map()(\n            x_src, time, functions_of_time);\n        get<0>(*dest_cartesian_coords)[s] = get<0>(x_dest);\n        get<1>(*dest_cartesian_coords)[s] = get<1>(x_dest);\n        get<2>(*dest_cartesian_coords)[s] = get<2>(x_dest);\n      }\n    } else {\n      // If we get here, then the frames are actually the same, but\n      // they have different frame tags.  So we just copy.\n      get<0>(*dest_cartesian_coords)[s] = get<0>(x_src);\n      get<1>(*dest_cartesian_coords)[s] = get<1>(x_src);\n      get<2>(*dest_cartesian_coords)[s] = get<2>(x_src);\n    }\n  }\n}\n\n#define SRCFRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DESTFRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATEGENERAL(_, data)                                  \\\n  template void strahlkorper_in_different_frame(                     \\\n      const gsl::not_null<ylm::Strahlkorper<DESTFRAME(data)>*>       \\\n          dest_strahlkorper,                                         \\\n      const ylm::Strahlkorper<SRCFRAME(data)>& src_strahlkorper,     \\\n      const Domain<3>& domain,                                       \\\n      const std::unordered_map<                                      \\\n          std::string,                                               \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>& \\\n          functions_of_time,                                         \\\n      const double time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATEGENERAL, (::Frame::Grid),\n                        (::Frame::Inertial))\n\n// Generate a specific instantiation: inertial -> distorted\n// (needed, e.g., for initializing a binary-black-hole ringdown).\n// Don't just add to GENERATE_INSTANTIATIONS above, to avoid also generating\n// unwanted instantiations, such as inertial -> inertial\ntemplate void strahlkorper_in_different_frame(\n    const gsl::not_null<ylm::Strahlkorper<::Frame::Distorted>*>\n        dest_strahlkorper,\n    const ylm::Strahlkorper<::Frame::Inertial>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double time);\ntemplate void strahlkorper_in_different_frame(\n    const gsl::not_null<ylm::Strahlkorper<::Frame::Grid>*> dest_strahlkorper,\n    const ylm::Strahlkorper<::Frame::Inertial>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double time);\n\ntemplate void strahlkorper_in_different_frame_aligned(\n    const gsl::not_null<ylm::Strahlkorper<::Frame::Grid>*> dest_strahlkorper,\n    const ylm::Strahlkorper<::Frame::Inertial>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double time);\n\n#define INSTANTIATEALIGNED(_, data)                                  \\\n  template void strahlkorper_in_different_frame_aligned(             \\\n      const gsl::not_null<ylm::Strahlkorper<DESTFRAME(data)>*>       \\\n          dest_strahlkorper,                                         \\\n      const ylm::Strahlkorper<SRCFRAME(data)>& src_strahlkorper,     \\\n      const Domain<3>& domain,                                       \\\n      const std::unordered_map<                                      \\\n          std::string,                                               \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>& \\\n          functions_of_time,                                         \\\n      const double time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATEALIGNED, (::Frame::Grid),\n                        (::Frame::Distorted, ::Frame::Inertial))\n\n#define INSTANTIATE(_, data)                                         \\\n  template void strahlkorper_coords_in_different_frame(              \\\n      const gsl::not_null<tnsr::I<DataVector, 3, DESTFRAME(data)>*>  \\\n          dest_cartesian_coords,                                     \\\n      const ylm::Strahlkorper<SRCFRAME(data)>& src_strahlkorper,     \\\n      const Domain<3>& domain,                                       \\\n      const std::unordered_map<                                      \\\n          std::string,                                               \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>& \\\n          functions_of_time,                                         \\\n      const double time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (::Frame::Grid, ::Frame::Distorted),\n                        (::Frame::Inertial))\n\n#undef INSTANTIATE\n#undef INSTANTIATEGENERAL\n#undef INSTANTIATEALIGNED\n#undef DESTFRAME\n#undef SRCFRAME\n"
  },
  {
    "path": "src/Domain/StrahlkorperTransformations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\ntemplate <size_t Dim>\nclass Domain;\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// \\brief Transforms a Strahlkorper from SrcFrame to DestFrame.\n///\n/// The destination Strahlkorper has the same l_max() and m_max() as the\n/// source Strahlkorper.\n///\n/// \\note Because the Blocks inside the Domain allow access to\n/// maps only between a selected subset of frames, we cannot use\n/// strahlkorper_in_different_frame to map between arbitrary frames;\n/// allowing strahlkorper_in_different_frame to work on more frames\n/// requires adding member functions to Block.\n///\n/// \\note strahlkorper_in_different_frame is currently instantiated\n/// only for i) SrcFrame=Grid and DestFrame=Inertial and ii) SrcFrame=Inertial\n/// and DestFrame=Distorted (necessary for initializing a binary-black-hole\n/// ringdown).  In particular, we intentionally do not instantiate\n/// strahlkorper_in_different_frame for SrcFrame=Grid and DestFrame=Distorted,\n/// because our Grid->Distorted maps preserve centers and angles, so for\n/// Grid->Distorted one should use\n/// strahlkorper_in_different_frame_aligned because it is more\n/// efficient.\ntemplate <typename SrcFrame, typename DestFrame>\nvoid strahlkorper_in_different_frame(\n    gsl::not_null<ylm::Strahlkorper<DestFrame>*> dest_strahlkorper,\n    const ylm::Strahlkorper<SrcFrame>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    double time);\n\n/// \\brief Transforms a Strahlkorper from SrcFrame to DestFrame, for easy maps.\n///\n/// This is a simplified version of strahlkorper_in_different_frame\n/// with the following two assumptions: First, the map leaves the\n/// center of the Strahlkorper invariant. Second, for every point on\n/// the Strahlkorper, the map leaves the angles about the center\n/// (i.e. the quantity\n/// \\f$(x^i-c^i)/\\sqrt{(x^0-c^0)^2+(x^1-c^1)^2+(x^2-c^2)^2}\\f$)\n/// invariant. If those assumptions are not satisfied, then\n/// strahlkorper_in_different_frame_aligned will give the wrong answer\n/// and you must use strahlkorper_in_different_frame.  Those\n/// assumptions are satisfied for the shape and size maps but not for\n/// general maps.\n///\n/// \\note strahlkorper_in_different_frame_aligned is instantiated only\n/// for SrcFrame=Grid and DestFrame=Distorted or Inertial. This\n/// doesn't mean that strahlkorper_in_different_frame_aligned is\n/// always correct for Grid->Inertial; the correctness depends on\n/// whether the Grid->Inertial map satisfies the above assumptions,\n/// which it might for simple cases (like single BHs) but it won't for\n/// more complicated cases (like BBHs).  To add different frames to\n/// strahlkorper_in_different_frame_aligned, one must add `if\n/// constexpr`s to call the appropriate member functions of Block.\ntemplate <typename SrcFrame, typename DestFrame>\nvoid strahlkorper_in_different_frame_aligned(\n    gsl::not_null<ylm::Strahlkorper<DestFrame>*> dest_strahlkorper,\n    const ylm::Strahlkorper<SrcFrame>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    double time);\n\n/// \\brief Maps (cartesian) collocation points of a Strahlkorper to\n/// a different frame.\n///\n/// If SrcFrame is Frame::Distorted, strahlkorper_coords_in_different_frame\n/// will fail if src_strahlkorper intersects a Block that lacks a distorted\n/// frame.  Note that if such a Strahlkorper intersects\n/// such a Block, we have worse problems anyway (e.g. jacobians will usually\n/// be discontinuous at the boundaries of such a Block, and attempting to\n/// find such a Strahlkorper with an apparent-horizon finder will probably\n/// fail).\n///\n/// Note that because the Blocks inside the Domain allow access to\n/// maps only between a selected subset of frames, we cannot use\n/// strahlkorper_in_different_frame to map between arbitrary frames;\n/// allowing strahlkorper_coords_in_different_frame to work on more frames\n/// requires adding member functions to Block.\ntemplate <typename SrcFrame, typename DestFrame>\nvoid strahlkorper_coords_in_different_frame(\n    gsl::not_null<tnsr::I<DataVector, 3, DestFrame>*> dest_cartesian_coords,\n    const ylm::Strahlkorper<SrcFrame>& src_strahlkorper,\n    const Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    double time);\n"
  },
  {
    "path": "src/Domain/Structure/BlockGroups.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/BlockGroups.hpp\"\n\n#include <exception>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain {\n\nbool block_is_in_group(\n    const std::string& block_name, const std::string& block_or_group_name,\n    const std::unordered_map<std::string, std::unordered_set<std::string>>&\n        block_groups) {\n  if (block_name == block_or_group_name) {\n    return true;\n  }\n  const auto block_group_it = block_groups.find(block_or_group_name);\n  return block_group_it != block_groups.end() and\n         block_group_it->second.count(block_name) == 1;\n}\n\nstd::unordered_set<std::string> expand_block_groups_to_block_names(\n    const std::vector<std::string>& block_or_group_names,\n    const std::vector<std::string>& all_block_names,\n    const std::unordered_map<std::string, std::unordered_set<std::string>>&\n        block_groups) {\n  // Use an unordered_set to elide duplicates\n  std::unordered_set<std::string> expanded_block_names;\n  for (const auto& block_or_group_name : block_or_group_names) {\n    if (const auto block_group_it = block_groups.find(block_or_group_name);\n        block_group_it != block_groups.end()) {\n      expanded_block_names.insert(block_group_it->second.begin(),\n                                  block_group_it->second.end());\n    } else if (const auto block_name_it =\n                   std::find(all_block_names.begin(), all_block_names.end(),\n                             block_or_group_name);\n               block_name_it != all_block_names.end()) {\n      expanded_block_names.insert(*block_name_it);\n    } else {\n      using ::operator<<;\n      ERROR_AS(\n          \"The block or group '\"\n              << block_or_group_name\n              << \"' is not one of the block names or groups of the domain. \"\n                 \"The known block groups are \"\n              << keys_of(block_groups) << \" and the known block names are \"\n              << all_block_names,\n          std::invalid_argument);\n    }\n  }\n  return expanded_block_names;\n}\n\nstd::vector<size_t> block_ids_from_names(\n    const std::vector<std::string>& block_or_group_names,\n    const std::vector<std::string>& all_block_names,\n    const std::unordered_map<std::string, std::unordered_set<std::string>>&\n        block_groups) {\n  const auto block_names = expand_block_groups_to_block_names(\n      block_or_group_names, all_block_names, block_groups);\n  std::vector<size_t> result{};\n  result.reserve(block_names.size());\n  // Get the block ID of each block name\n  for (const auto& block_name : block_names) {\n    result.push_back(static_cast<size_t>(\n        std::distance(all_block_names.begin(),\n                      std::find(all_block_names.begin(), all_block_names.end(),\n                                block_name))));\n  }\n  // Sort the block IDs just so they're easier to deal with.\n  alg::sort(result);\n  return result;\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/BlockGroups.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Utilities to work with block names and groups\n\n#pragma once\n\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\nnamespace domain {\n\n/*!\n * \\brief Check if a block is in the given group\n *\n * \\param block_name The name of the block to check\n * \\param block_or_group_name The group name we're testing. Returns true if\n * the block is in this group. This can also be the name of the block itself.\n * \\param block_groups All block groups.\n */\nbool block_is_in_group(\n    const std::string& block_name, const std::string& block_or_group_name,\n    const std::unordered_map<std::string, std::unordered_set<std::string>>&\n        block_groups);\n\n/*!\n * \\brief Expand a list of block or group names into a list of block names\n *\n * Throws a `std::invalid_argument` exception if any of the input names are not\n * in \\p all_block_names or \\p block_groups.\n *\n * \\param block_or_group_names Block or group names to expand\n * \\param all_block_names All block names in the domain\n * \\param block_groups Block groups used to expand the names\n * \\return std::unordered_set<std::string> List of block names that appear in\n * \\p all_block_names. If one of the input names was a group, then all block\n * names from that group are included. Overlaps between groups are allowed.\n */\nstd::unordered_set<std::string> expand_block_groups_to_block_names(\n    const std::vector<std::string>& block_or_group_names,\n    const std::vector<std::string>& all_block_names,\n    const std::unordered_map<std::string, std::unordered_set<std::string>>&\n        block_groups);\n\n/*!\n * \\brief Sorted block IDs corresponding to the given block or group names.\n *\n * Throws a `std::invalid_argument` exception if any of the input names are not\n * in \\p all_block_names or \\p block_groups.\n *\n * \\param block_or_group_names Block names to get the IDs of. Any block groups\n * in this list are expanded.\n * \\param all_block_names All block names in the domain.\n * \\param block_groups Block groups used to expand the names.\n * \\return std::vector<size_t> Sorted list of block IDs.\n */\nstd::vector<size_t> block_ids_from_names(\n    const std::vector<std::string>& block_or_group_names,\n    const std::vector<std::string>& all_block_names,\n    const std::unordered_map<std::string, std::unordered_set<std::string>>&\n        block_groups);\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/BlockId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace domain {\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Index a block of the computational domain.\n */\nclass BlockId {\n public:\n  BlockId() = default;\n  explicit BlockId(size_t id) : id_(id) {}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | id_; }\n\n  size_t get_index() const { return id_; }\n\n  BlockId& operator++() {\n    ++id_;\n    return *this;\n  }\n\n  BlockId& operator--() {\n    --id_;\n    return *this;\n  }\n\n  BlockId operator++(int) {\n    BlockId temp = *this;\n    id_++;\n    return temp;\n  }\n\n  BlockId operator--(int) {\n    BlockId temp = *this;\n    id_--;\n    return temp;\n  }\n\n private:\n  size_t id_{0};\n};\n\ninline bool operator==(const BlockId& lhs, const BlockId& rhs) {\n  return lhs.get_index() == rhs.get_index();\n}\n\ninline bool operator!=(const BlockId& lhs, const BlockId& rhs) {\n  return not(lhs == rhs);\n}\n\ninline std::ostream& operator<<(std::ostream& os, const BlockId& block_id) {\n  return os << '[' << block_id.get_index() << ']';\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/BlockNeighbors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n\ntemplate class BlockNeighbors<1>;\ntemplate class BlockNeighbors<2>;\ntemplate class BlockNeighbors<3>;\n"
  },
  {
    "path": "src/Domain/Structure/BlockNeighbors.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/Structure/Neighbors.hpp\"\n\n/// \\ingroup ComputationalDomainGroup\n/// Information about the neighbors of a host Block in a particular direction.\n///\n/// \\tparam VolumeDim the volume dimension.\ntemplate <size_t VolumeDim>\nclass BlockNeighbors : public Neighbors<VolumeDim, size_t> {\n public:\n  using Neighbors<VolumeDim, size_t>::Neighbors;\n};\n"
  },
  {
    "path": "src/Domain/Structure/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DomainStructure)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BlockGroups.cpp\n  BlockNeighbors.cpp\n  ChildSize.cpp\n  CreateInitialMesh.cpp\n  Direction.cpp\n  DirectionalId.cpp\n  Element.cpp\n  ElementId.cpp\n  ElementSearchTree.cpp\n  FaceType.cpp\n  HasBoundary.cpp\n  Hypercube.cpp\n  InitialElementIds.cpp\n  NeighborIsConforming.cpp\n  Neighbors.cpp\n  ObjectLabel.cpp\n  OrientationMap.cpp\n  OrientationMapHelpers.cpp\n  SegmentId.cpp\n  Side.cpp\n  Topology.cpp\n  ZCurve.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BlockGroups.hpp\n  BlockId.hpp\n  BlockNeighbors.hpp\n  ChildSize.hpp\n  CreateInitialMesh.hpp\n  Direction.hpp\n  DirectionalId.hpp\n  DirectionalIdMap.hpp\n  DirectionMap.hpp\n  Element.hpp\n  ElementId.hpp\n  ElementSearchTree.hpp\n  FaceType.hpp\n  HasBoundary.hpp\n  Hypercube.hpp\n  IndexToSliceAt.hpp\n  InitialElementIds.hpp\n  MaxNumberOfNeighbors.hpp\n  NeighborIsConforming.hpp\n  Neighbors.hpp\n  ObjectLabel.hpp\n  OrientationMap.hpp\n  OrientationMapHelpers.hpp\n  SegmentId.hpp\n  Side.hpp\n  Topology.hpp\n  TrimMap.hpp\n  ZCurve.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Autodiff\n  Boost::boost\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "src/Domain/Structure/ChildSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/ChildSize.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <ostream>\n#include <string>\n\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\n\nSpectral::SegmentSize child_size(const SegmentId& child_segment_id,\n                                 const SegmentId& parent_segment_id) {\n  if (child_segment_id == parent_segment_id) {\n    return Spectral::SegmentSize::Full;\n  } else {\n    ASSERT(child_segment_id.id_of_parent() == parent_segment_id,\n           \"Segment id '\" << parent_segment_id << \"' is not the parent of '\"\n                          << child_segment_id << \"'.\");\n    return parent_segment_id.id_of_child(Side::Lower) == child_segment_id\n               ? Spectral::SegmentSize::LowerHalf\n               : Spectral::SegmentSize::UpperHalf;\n  }\n}\n\ntemplate <size_t Dim>\nstd::array<Spectral::SegmentSize, Dim> child_size(\n    const std::array<SegmentId, Dim>& child_segment_ids,\n    const std::array<SegmentId, Dim>& parent_segment_ids) {\n  std::array<Spectral::SegmentSize, Dim> result{};\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(result, d) = child_size(gsl::at(child_segment_ids, d),\n                                    gsl::at(parent_segment_ids, d));\n  }\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                        \\\n  template std::array<Spectral::SegmentSize, DIM(data)> child_size( \\\n      const std::array<SegmentId, DIM(data)>& child_segment_ids,    \\\n      const std::array<SegmentId, DIM(data)>& parent_segment_ids);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/ChildSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n\nnamespace domain {\n/// @{\n/*!\n * \\brief Size of a child segment relative to its parent\n *\n * Determines which part of the `parent_segment_id` is covered by the\n * `child_segment_id`: The full segment, its lower half or its upper half.\n */\nSpectral::SegmentSize child_size(const SegmentId& child_segment_id,\n                                 const SegmentId& parent_segment_id);\n\ntemplate <size_t Dim>\nstd::array<Spectral::SegmentSize, Dim> child_size(\n    const std::array<SegmentId, Dim>& child_segment_ids,\n    const std::array<SegmentId, Dim>& parent_segment_ids);\n/// @}\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/CreateInitialMesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstd::array<Spectral::Basis, Dim> make_basis(\n    const std::array<domain::Topology, Dim>& topologies,\n    const Spectral::Basis i1_basis) {\n  const auto topology_to_basis = [&i1_basis](const domain::Topology topology) {\n    switch (topology) {\n      case (domain::Topology::I1): {\n        ASSERT(i1_basis == Spectral::Basis::Legendre or\n                   i1_basis == Spectral::Basis::Chebyshev,\n               \"Invalid I1 Basis: \" << i1_basis);\n        return i1_basis;\n      }\n      case (domain::Topology::S1):\n        return Spectral::Basis::Fourier;\n      case (domain::Topology::S2Colatitude):\n        [[fallthrough]];\n      case (domain::Topology::S2Longitude):\n        return Spectral::Basis::SphericalHarmonic;\n      case (domain::Topology::B2Radial):\n        [[fallthrough]];\n      case (domain::Topology::B2Angular):\n        return Spectral::Basis::ZernikeB2;\n      // NOLINTNEXTLINE(bugprone-branch-clone)\n      case (domain::Topology::B3Radial):\n        [[fallthrough]];\n      case (domain::Topology::B3Colatitude):\n        [[fallthrough]];\n      case (domain::Topology::B3Longitude):\n        return Spectral::Basis::ZernikeB3;\n      case (domain::Topology::CartoonSphere):\n        [[fallthrough]];\n      case (domain::Topology::CartoonCylinder):\n        return Spectral::Basis::Cartoon;\n      default:\n        ERROR(\"Invalid topology\");\n    }\n  };\n  return map_array(topologies, topology_to_basis);\n}\n\ntemplate <size_t Dim>\nstd::array<Spectral::Quadrature, Dim> make_quadrature(\n    const std::array<domain::Topology, Dim>& topologies,\n    const Spectral::Quadrature i1_quadrature) {\n  const auto topology_to_quadrature =\n      [&i1_quadrature](const domain::Topology topology) {\n        switch (topology) {\n          case (domain::Topology::I1): {\n            ASSERT(i1_quadrature == Spectral::Quadrature::GaussLobatto or\n                       i1_quadrature == Spectral::Quadrature::Gauss,\n                   \"Invalid I1 Quadrature: \" << i1_quadrature);\n            return i1_quadrature;\n          }\n          // NOLINTNEXTLINE(bugprone-branch-clone)\n          case (domain::Topology::S1):\n            [[fallthrough]];\n          case (domain::Topology::S2Longitude):\n            [[fallthrough]];\n          case (domain::Topology::B3Longitude):\n            [[fallthrough]];\n          case (domain::Topology::B2Angular):\n            return Spectral::Quadrature::Equiangular;\n          case (domain::Topology::S2Colatitude):\n            [[fallthrough]];\n          case (domain::Topology::B3Colatitude):\n            return Spectral::Quadrature::Gauss;\n          case (domain::Topology::B2Radial):\n            [[fallthrough]];\n          case (domain::Topology::B3Radial):\n            return Spectral::Quadrature::GaussRadauUpper;\n          case (domain::Topology::CartoonSphere):\n            return Spectral::Quadrature::SphericalSymmetry;\n          case (domain::Topology::CartoonCylinder):\n            return Spectral::Quadrature::AxialSymmetry;\n          default:\n            ERROR(\"Invalid topology\");\n        }\n      };\n  return map_array(topologies, topology_to_quadrature);\n}\n\ntemplate <size_t Dim>\nbool is_radially_refined_b2(const std::array<domain::Topology, Dim>& topologies,\n                            const ElementId<Dim>& element_id) {\n  const auto it = alg::find(topologies, domain::Topology::B2Radial);\n  if (it == topologies.end()) {\n    return false;\n  }\n  return gsl::at(element_id.refinement_levels(),\n                 std::distance(topologies.begin(), it)) != 0;\n}\n}  // namespace\n\nnamespace domain {\ntemplate <size_t Dim>\nMesh<Dim> create_initial_mesh(\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const Element<Dim>& element, const Spectral::Basis i1_basis,\n    const Spectral::Quadrature i1_quadrature) {\n  return {initial_extents[element.id().block_id()],\n          make_basis(element.topologies(), i1_basis),\n          make_quadrature(element.topologies(), i1_quadrature)};\n}\n\ntemplate <size_t Dim>\nMesh<Dim> create_initial_mesh(\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const Block<Dim>& block, [[maybe_unused]] const ElementId<Dim>& element_id,\n    const Spectral::Basis i1_basis, const Spectral::Quadrature i1_quadrature) {\n  ASSERT(not is_radially_refined_b2(block.topologies(), element_id),\n         \"Splitting Topology::B2Radial is not yet supported\");\n  return {initial_extents[block.id()], make_basis(block.topologies(), i1_basis),\n          make_quadrature(block.topologies(), i1_quadrature)};\n}\n}  // namespace domain\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template Mesh<DIM(data)> domain::create_initial_mesh<DIM(data)>(           \\\n      const std::vector<std::array<size_t, DIM(data)>>& initial_extents,     \\\n      const Element<DIM(data)>& element, const Spectral::Basis i1_basis,     \\\n      const Spectral::Quadrature i1_quadrature);                             \\\n  template Mesh<DIM(data)> domain::create_initial_mesh<DIM(data)>(           \\\n      const std::vector<std::array<size_t, DIM(data)>>& initial_extents,     \\\n      const Block<DIM(data)>& block, const ElementId<DIM(data)>& element_id, \\\n      const Spectral::Basis i1_basis, const Spectral::Quadrature _quadrature);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Domain/Structure/CreateInitialMesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n/// \\cond\ntemplate <size_t Dim>\nstruct Block;\ntemplate <size_t Dim>\nstruct Element;\ntemplate <size_t Dim>\nstruct ElementId;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace domain {\n/// \\ingroup InitializationGroup\n/// \\brief Construct the initial Mesh of an Element.\n///\n/// \\param initial_extents initial extents for Elements in each Block of the\n///        Domain\n/// \\param element Element\n/// \\param i1_basis the Spectral::Basis used for dimensions with Topology::I1\n/// \\param i1_quadrature the Spectral::Quadrature for dimensions with\n///        Topology::I1\ntemplate <size_t Dim>\nMesh<Dim> create_initial_mesh(\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const Element<Dim>& element, Spectral::Basis i1_basis,\n    Spectral::Quadrature i1_quadrature);\n\n/// \\ingroup InitializationGroup\n/// \\brief Construct the initial Mesh of an Element from its Block and\n/// ElementId.\n///\n/// \\param initial_extents initial extents for Elements in each Block of the\n///        Domain\n/// \\param block the Block of the Element\n/// \\param element_id the ElementId of the Element\n/// \\param i1_basis the Spectral::Basis used for dimensions with Topology::I1\n/// \\param i1_quadrature the Spectral::Quadrature for dimensions with\n///        Topology::I1\ntemplate <size_t Dim>\nMesh<Dim> create_initial_mesh(\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const Block<Dim>& block, const ElementId<Dim>& element_id,\n    Spectral::Basis i1_basis, Spectral::Quadrature i1_quadrature);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/Direction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/Direction.hpp\"\n\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t VolumeDim>\nDirection<VolumeDim> Direction<VolumeDim>::self() {\n  return Direction<VolumeDim>{Axis::Xi, Side::Self};\n}\n\ntemplate <size_t VolumeDim>\nDirection<VolumeDim>::Direction(Axis axis, Side side)\n    : bit_field_(static_cast<uint8_t>(axis) bitor static_cast<uint8_t>(side)) {\n  ASSERT(bit_field_ < 0b10000, \"Direction is too large.\");\n}\n\ntemplate <size_t VolumeDim>\nvoid Direction<VolumeDim>::pup(PUP::er& p) {\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  const uint8_t current_version = 1 << 6;\n  if (p.isUnpacking()) {\n    uint8_t data = 0;\n    p | data;\n    const uint8_t version_read = (0b11000000 bitand data) >> 6;\n    if (LIKELY(version_read > 0)) {\n      bit_field_ = data bitand 0b00001111;\n    } else {\n      // Read next 3 bytes.\n      p | data;\n      p | data;\n      p | data;\n      // Read \"bottom\" of version\n      uint32_t old_version{0};\n      p | old_version;\n      if (UNLIKELY(old_version > 0)) {\n        ERROR(\n            \"Incompatible version format for Direction. Expected to receive \"\n            \"version 0 but got \"\n            << old_version);\n      }\n      int read_axis{0};\n      p | read_axis;\n      if (UNLIKELY(read_axis < 0)) {\n        ERROR(\"Expected a non-negative axis value but read \" << read_axis);\n      }\n      int read_side{0};\n      p | read_side;\n      if (UNLIKELY(read_side < 0)) {\n        ERROR(\"Expected a non-negative side value but read \" << read_side);\n      }\n      // add 1 to account for the added Uninitialized\n      const auto side = static_cast<uint8_t>(read_side + 1)\n                        << detail::side_shift;\n      bit_field_ = 0;\n      bit_field_ = static_cast<uint8_t>(read_axis) bitor side;\n    }\n  } else {\n    uint8_t data = current_version bitor bit_field_;\n    p | data;\n  }\n}\n\ntemplate <size_t VolumeDim>\nDirection<VolumeDim>::Direction(const size_t dimension, const Side side)\n    : Direction(static_cast<Axis>(static_cast<uint8_t>(dimension)), side) {\n  ASSERT(dimension < VolumeDim,\n         \"dim = \" << dimension << \", but must be less than \" << VolumeDim);\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const Direction<VolumeDim>& direction) {\n  switch (direction.side()) {\n    case Side::Uninitialized:\n      os << \"Uninitialized\";\n      return os;\n    case Side::Self:\n      os << \"Self\";\n      return os;\n    case Side::Lower:\n      os << '-';\n      break;\n    case Side::Upper:\n      os << '+';\n      break;\n    default:  // LCOV_EXCL_LINE\n      ERROR(\n          \"A direction with an unknown Side was passed to the stream \"\n          \"operator.\");\n      // LCOV_EXCL_STOP\n  }\n  os << direction.dimension();\n  return os;\n}\n\ntemplate <size_t VolumeDim>\nbool operator<(const Direction<VolumeDim>& lhs,\n               const Direction<VolumeDim>& rhs) {\n  if (lhs.axis() != rhs.axis()) {\n    return lhs.axis() < rhs.axis();\n  }\n  return lhs.side() < rhs.side();\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                        \\\n  template std::ostream& operator<<(std::ostream&,                    \\\n                                    const Direction<GET_DIM(data)>&); \\\n  template bool operator<(const Direction<GET_DIM(data)>& lhs,        \\\n                          const Direction<GET_DIM(data)>& rhs);       \\\n  template void Direction<GET_DIM(data)>::pup(PUP::er&);              \\\n  template Direction<GET_DIM(data)>::Direction(Axis, Side);           \\\n  template Direction<GET_DIM(data)>::Direction(size_t, Side);         \\\n  template Direction<GET_DIM(data)> Direction<GET_DIM(data)>::self();\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/Structure/Direction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class template Direction.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <iosfwd>\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\n/// \\ingroup ComputationalDomainGroup\n/// A particular Side along a particular coordinate Axis.\ntemplate <size_t VolumeDim>\nclass Direction {\n public:\n  static constexpr const size_t volume_dim = VolumeDim;\n  static constexpr uint8_t axis_mask = 0b0011;\n  static constexpr uint8_t side_mask = 0b1100;\n  static constexpr uint8_t all_mask = 0b1111;\n  static constexpr uint8_t number_of_bits = 4;\n\n  /// The logical-coordinate names of each dimension\n  enum class Axis : uint8_t;\n\n  /// Construct by specifying an Axis and a Side.\n  Direction(Axis axis, Side side);\n\n  /// Construct by specifying a dimension and a Side.\n  Direction(size_t dimension, Side side);\n\n  /// Default constructor for Charm++ serialization.\n  Direction();\n\n  /// Get a Direction representing \"self\" or \"no direction\"\n  static Direction<VolumeDim> self();\n\n  /// The dimension of the Direction\n  size_t dimension() const {\n    ASSERT(bit_field_ != 0, \"Cannot use an uninitialized Direction\");\n    return static_cast<uint8_t>(axis());\n  }\n\n  /// The Axis of the Direction\n  Axis axis() const {\n    ASSERT(bit_field_ != 0, \"Cannot use an uninitialized Direction\");\n    return static_cast<Axis>(bit_field_ bitand axis_mask);\n  }\n\n  /// The side of the Direction\n  Side side() const { return static_cast<Side>(bit_field_ bitand side_mask); }\n\n  /// The sign for the normal to the Side.\n  ///\n  /// This is `+1.0` if `side() == Side::Upper` and `-1.0` if\n  /// `side() == Side::Lower`, otherwise an `ASSERT` is triggered.\n  double sign() const {\n    ASSERT(Side::Lower == side() or Side::Upper == side(),\n           \"sign() is only defined for Side::Lower and Side::Upper, not \"\n               << side());\n    return (Side::Lower == side() ? -1.0 : 1.0);\n  }\n\n  /// The opposite Direction.\n  Direction<VolumeDim> opposite() const;\n\n  // An array of all logical Directions for a given dimensionality.\n  static const std::array<Direction<VolumeDim>, 2 * VolumeDim>&\n  all_directions();\n\n  /// @{\n  /// Helper functions for creating specific Directions.\n  /// These are labeled by the logical-coordinate names (Xi,Eta,Zeta).\n  // Note: these are functions because they contain static_assert.\n  static Direction<VolumeDim> lower_xi();\n  static Direction<VolumeDim> upper_xi();\n  static Direction<VolumeDim> lower_eta();\n  static Direction<VolumeDim> upper_eta();\n  static Direction<VolumeDim> lower_zeta();\n  static Direction<VolumeDim> upper_zeta();\n  /// @}\n\n  uint8_t bits() const { return bit_field_; }\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <size_t LocalVolumeDim>\n  friend bool operator==(const Direction<LocalVolumeDim>& lhs,\n                         const Direction<LocalVolumeDim>& rhs);\n  template <size_t LocalVolumeDim>\n  friend size_t hash_value(const Direction<LocalVolumeDim>& d);\n\n  uint8_t bit_field_{0};\n};\n\n/// Output operator for a Direction.\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const Direction<VolumeDim>& direction);\n\n//##############################################################################\n// INLINE DEFINITIONS\n//##############################################################################\n\n/// \\cond\n// clang-tidy: redundant declaration false positive. Needs to be here because of\n// the Axis enum, otherwise won't compile.\ntemplate <size_t VolumeDim>\nDirection<VolumeDim>::Direction() = default;  // NOLINT\n\n// Needed in order to address warning; ignorning -Wpedantic is needed.\n// Bug 61491\n// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61491\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpedantic\"\ntemplate <>\nenum class Direction<1>::Axis : uint8_t{Xi = 0};\n\ntemplate <>\nenum class Direction<2>::Axis : uint8_t{Xi = 0, Eta = 1};\n\ntemplate <>\nenum class Direction<3>::Axis : uint8_t{Xi = 0, Eta = 1, Zeta = 2};\n#pragma GCC diagnostic pop\n\ntemplate <size_t VolumeDim>\ninline Direction<VolumeDim> Direction<VolumeDim>::lower_xi() {\n  return Direction(Direction<VolumeDim>::Axis::Xi, Side::Lower);\n}\n\ntemplate <size_t VolumeDim>\ninline Direction<VolumeDim> Direction<VolumeDim>::upper_xi() {\n  return Direction(Direction<VolumeDim>::Axis::Xi, Side::Upper);\n}\n\ntemplate <size_t VolumeDim>\ninline Direction<VolumeDim> Direction<VolumeDim>::lower_eta() {\n  static_assert(VolumeDim == 2 or VolumeDim == 3, \"VolumeDim must be 2 or 3.\");\n  return Direction(Direction<VolumeDim>::Axis::Eta, Side::Lower);\n}\n\ntemplate <size_t VolumeDim>\ninline Direction<VolumeDim> Direction<VolumeDim>::upper_eta() {\n  static_assert(VolumeDim == 2 or VolumeDim == 3, \"VolumeDim must be 2 or 3.\");\n  return Direction(Direction<VolumeDim>::Axis::Eta, Side::Upper);\n}\n\ntemplate <size_t VolumeDim>\ninline Direction<VolumeDim> Direction<VolumeDim>::lower_zeta() {\n  static_assert(VolumeDim == 3, \"VolumeDim must be 3.\");\n  return Direction(Direction<VolumeDim>::Axis::Zeta, Side::Lower);\n}\n\ntemplate <size_t VolumeDim>\ninline Direction<VolumeDim> Direction<VolumeDim>::upper_zeta() {\n  static_assert(VolumeDim == 3, \"VolumeDim must be 3.\");\n  return Direction(Direction<VolumeDim>::Axis::Zeta, Side::Upper);\n}\n\ntemplate <size_t VolumeDim>\ninline Direction<VolumeDim> Direction<VolumeDim>::opposite() const {\n  ASSERT(bit_field_ != 0, \"Cannot use an uninitialized Direction\");\n  return Direction<VolumeDim>(axis(), ::opposite(side()));\n}\n\ntemplate <>\ninline const std::array<Direction<1>, 2>& Direction<1>::all_directions() {\n  const static auto directions = std::array<Direction<1>, 2>{\n      {Direction<1>::lower_xi(), Direction<1>::upper_xi()}};\n  return directions;\n}\n\ntemplate <>\ninline const std::array<Direction<2>, 4>& Direction<2>::all_directions() {\n  const static auto directions = std::array<Direction<2>, 4>{\n      {Direction<2>::lower_xi(), Direction<2>::upper_xi(),\n       Direction<2>::lower_eta(), Direction<2>::upper_eta()}};\n  return directions;\n}\n\ntemplate <>\ninline const std::array<Direction<3>, 6>& Direction<3>::all_directions() {\n  const static auto directions = std::array<Direction<3>, 6>{\n      {Direction<3>::lower_xi(), Direction<3>::upper_xi(),\n       Direction<3>::lower_eta(), Direction<3>::upper_eta(),\n       Direction<3>::lower_zeta(), Direction<3>::upper_zeta()}};\n  return directions;\n}\n/// \\endcond\n\ntemplate <size_t VolumeDim>\nbool operator==(const Direction<VolumeDim>& lhs,\n                const Direction<VolumeDim>& rhs) {\n  return lhs.bit_field_ == rhs.bit_field_;\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Direction<VolumeDim>& lhs,\n                const Direction<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n/// Define an ordering of directions first by axis (xi, eta, zeta), then by side\n/// (lower, upper). There's no particular reason for this choice of ordering.\ntemplate <size_t VolumeDim>\nbool operator<(const Direction<VolumeDim>& lhs,\n               const Direction<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nbool operator>(const Direction<VolumeDim>& lhs,\n               const Direction<VolumeDim>& rhs) {\n  return rhs < lhs;\n}\ntemplate <size_t VolumeDim>\nbool operator<=(const Direction<VolumeDim>& lhs,\n                const Direction<VolumeDim>& rhs) {\n  return !(lhs > rhs);\n}\ntemplate <size_t VolumeDim>\nbool operator>=(const Direction<VolumeDim>& lhs,\n                const Direction<VolumeDim>& rhs) {\n  return !(lhs < rhs);\n}\n\ntemplate <size_t VolumeDim>\nsize_t hash_value(const Direction<VolumeDim>& d) {\n  return d.bit_field_;\n}\n\nnamespace std {\ntemplate <size_t VolumeDim>\nstruct hash<Direction<VolumeDim>> {\n  size_t operator()(const Direction<VolumeDim>& d) const {\n    return hash_value(d);\n  }\n};\n}  // namespace std\n\n/// \\ingroup ComputationalDomainGroup\n/// Provides a perfect hash if the size of the hash table is `2 * Dim`. To take\n/// advantage of this, use the `FixedHashMap` class.\ntemplate <size_t Dim>\nstruct DirectionHash {\n  template <size_t MaxSize>\n  static constexpr bool is_perfect = MaxSize == 2 * Dim;\n\n  size_t operator()(const Direction<Dim>& t) {\n    return 2 * t.dimension() + (t.side() == Side::Upper ? 1 : 0);\n  }\n};\n"
  },
  {
    "path": "src/Domain/Structure/DirectionMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"DataStructures/FixedHashMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n\n/// \\ingroup DataStructuresGroup\n/// \\ingroup ComputationalDomainGroup\n/// An optimized map with Direction keys\ntemplate <size_t Dim, typename T>\nclass DirectionMap\n    : public FixedHashMap<2 * Dim, Direction<Dim>, T, DirectionHash<Dim>> {\n public:\n  using base = FixedHashMap<2 * Dim, Direction<Dim>, T, DirectionHash<Dim>>;\n  using base::base;\n};\n\nnamespace PUP {\ntemplate <size_t Dim, typename T>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid pup(PUP::er& p, DirectionMap<Dim, T>& t) {\n  pup(p, static_cast<typename DirectionMap<Dim, T>::base&>(t));\n}\n\ntemplate <size_t Dim, typename T>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid operator|(PUP::er& p, DirectionMap<Dim, T>& t) {\n  p | static_cast<typename DirectionMap<Dim, T>::base&>(t);\n}\n}  // namespace PUP\n"
  },
  {
    "path": "src/Domain/Structure/DirectionalId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/DirectionalId.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <ostream>\n#include <pup.h>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nstatic_assert(sizeof(DirectionalId<1>) == sizeof(ElementId<1>));\nstatic_assert(sizeof(DirectionalId<2>) == sizeof(ElementId<2>));\nstatic_assert(sizeof(DirectionalId<3>) == sizeof(ElementId<3>));\n\ntemplate <size_t VolumeDim>\nsize_t hash_value(const DirectionalId<VolumeDim>& id) {\n  return hash_value(id.id()) bitor static_cast<uint64_t>(id.direction().bits())\n                                       << ElementId<VolumeDim>::direction_shift;\n}\n\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nnamespace std {\ntemplate <size_t VolumeDim>\nsize_t hash<DirectionalId<VolumeDim>>::operator()(\n    const DirectionalId<VolumeDim>& id) const {\n  return boost::hash<DirectionalId<VolumeDim>>{}(id);\n}\n}  // namespace std\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const DirectionalId<VolumeDim>& direction_id) {\n  os << \"(\" << direction_id.direction() << \", \" << direction_id.id() << \")\";\n  return os;\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const DirectionalId<VolumeDim>& lhs,\n                const DirectionalId<VolumeDim>& rhs) {\n  return lhs.direction() == rhs.direction() and lhs.id() == rhs.id();\n}\n\ntemplate <size_t VolumeDim>\nbool operator<(const DirectionalId<VolumeDim>& lhs,\n               const DirectionalId<VolumeDim>& rhs) {\n  if (lhs.direction() != rhs.direction()) {\n    return lhs.direction() < rhs.direction();\n  }\n  return lhs.id() < rhs.id();\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                            \\\n  template std::ostream& operator<<(std::ostream&,                        \\\n                                    const DirectionalId<GET_DIM(data)>&); \\\n  template bool operator==(const DirectionalId<GET_DIM(data)>& lhs,       \\\n                           const DirectionalId<GET_DIM(data)>& rhs);      \\\n  template bool operator<(const DirectionalId<GET_DIM(data)>& lhs,        \\\n                          const DirectionalId<GET_DIM(data)>& rhs);       \\\n  template size_t hash_value(const DirectionalId<GET_DIM(data)>&);        \\\n  namespace std { /* NOLINT */                                            \\\n  template struct hash<DirectionalId<GET_DIM(data)>>;                     \\\n  }\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/Structure/DirectionalId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\brief The ElementId of an Element in a given Direction.\n///\n/// \\details Used as the key in a DirectionalIdMap\ntemplate <size_t VolumeDim>\nstruct DirectionalId : private ElementId<VolumeDim> {\n  DirectionalId() = default;\n\n  DirectionalId(const Direction<VolumeDim>& direction,\n                const ElementId<VolumeDim>& element_id)\n      : ElementId<VolumeDim>(direction, element_id) {}\n\n  ElementId<VolumeDim> id() const { return this->without_direction(); }\n\n  Direction<VolumeDim> direction() const {\n    return ElementId<VolumeDim>::direction();\n  }\n  void pup(PUP::er& p) { p | static_cast<ElementId<VolumeDim>&>(*this); }\n};\n\ntemplate <size_t VolumeDim>\nsize_t hash_value(const DirectionalId<VolumeDim>& id);\n\nnamespace std {\ntemplate <size_t VolumeDim>\nstruct hash<DirectionalId<VolumeDim>> {\n  size_t operator()(const DirectionalId<VolumeDim>& id) const;\n};\n}  // namespace std\n\ntemplate <size_t VolumeDim>\nbool operator==(const DirectionalId<VolumeDim>& lhs,\n                const DirectionalId<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const DirectionalId<VolumeDim>& lhs,\n                const DirectionalId<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t VolumeDim>\nbool operator<(const DirectionalId<VolumeDim>& lhs,\n               const DirectionalId<VolumeDim>& rhs);\n\n/// Output operator for a DirectionalId.\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const DirectionalId<VolumeDim>& direction);\n\ntemplate <size_t VolumeDim>\nDirectionalId(Direction<VolumeDim> direction, ElementId<VolumeDim> id)\n    -> DirectionalId<VolumeDim>;\n"
  },
  {
    "path": "src/Domain/Structure/DirectionalIdMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/functional/hash.hpp>\n#include <pup.h>\n\n#include \"DataStructures/FixedHashMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/MaxNumberOfNeighbors.hpp\"\n\n/// An optimized map with DirectionalId keys\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap : public FixedHashMap<maximum_number_of_neighbors(Dim),\n                                             DirectionalId<Dim>, T> {\n public:\n  using base =\n      FixedHashMap<maximum_number_of_neighbors(Dim), DirectionalId<Dim>, T>;\n  using base::base;\n};\n\nnamespace PUP {\ntemplate <size_t Dim, typename T>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid pup(PUP::er& p, DirectionalIdMap<Dim, T>& t) {\n  pup(p, static_cast<typename DirectionalIdMap<Dim, T>::base&>(t));\n}\n\ntemplate <size_t Dim, typename T>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid operator|(PUP::er& p, DirectionalIdMap<Dim, T>& t) {\n  p | static_cast<typename DirectionalIdMap<Dim, T>::base&>(t);\n}\n}  // namespace PUP\n"
  },
  {
    "path": "src/Domain/Structure/Element.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/Element.hpp\"\n\n#include <array>\n#include <ostream>\n#include <pup.h>\n\n#include \"Domain/Structure/HasBoundary.hpp\"\n#include \"Domain/Structure/MaxNumberOfNeighbors.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\ntemplate <size_t VolumeDim>\nElement<VolumeDim>::Element(ElementId<VolumeDim> id, Neighbors_t neighbors,\n                            std::array<domain::Topology, VolumeDim> topologies)\n    : id_(std::move(id)),\n      neighbors_(std::move(neighbors)),\n      number_of_neighbors_([this]() {\n        size_t number_of_neighbors = 0;\n        for (const auto& p : neighbors_) {\n          number_of_neighbors += p.second.size();\n        }\n        return number_of_neighbors;\n      }()),\n      topologies_(std::move(topologies)) {\n  for (const auto& direction : Direction<VolumeDim>::all_directions()) {\n    const bool has_boundary_in_this_direction = has_boundary(\n        gsl::at(topologies_, direction.dimension()), direction.side());\n    const auto& it_to_neighbors_in_this_direction = neighbors_.find(direction);\n    if (it_to_neighbors_in_this_direction == neighbors_.end()) {\n      if (has_boundary_in_this_direction) {\n        external_boundaries_.emplace(direction);\n        face_types_.emplace(direction, domain::FaceType::External);\n      } else {\n        face_types_.emplace(direction, domain::FaceType::Topological);\n      }\n    } else {\n      const Neighbors<VolumeDim>& neighbors_in_this_direction =\n          it_to_neighbors_in_this_direction->second;\n      ASSERT(not neighbors_in_this_direction.ids().empty(),\n             \"Do not specify an empty set of neighbors\");\n      ASSERT(has_boundary_in_this_direction,\n             \"Cannot specify a neighbor in a direction with no boundary.  \"\n             \"Topologies: \"\n                 << topologies_ << \"; Direction: \" << direction);\n      internal_boundaries_.emplace(direction);\n      if (neighbors_in_this_direction.are_conforming()) {\n        const auto& orientation =\n            neighbors_in_this_direction.orientations().begin()->second;\n        if (orientation.is_aligned()) {\n          face_types_.emplace(direction, domain::FaceType::ConformingAligned);\n        } else {\n          face_types_.emplace(direction, domain::FaceType::ConformingUnaligned);\n        }\n      } else {\n        if (neighbors_in_this_direction.size() == 1) {\n          face_types_.emplace(direction, domain::FaceType::SingleNonconforming);\n        } else {\n          face_types_.emplace(direction,\n                              domain::FaceType::MultipleNonconforming);\n        }\n      }\n    }\n  }\n  // Assuming a maximum 2-to-1 refinement between neighboring elements:\n  ASSERT(number_of_neighbors_ <= maximum_number_of_neighbors(VolumeDim) or\n             alg::any_of(face_types_,\n                         [](const auto& key_value) {\n                           return key_value.second ==\n                                  domain::FaceType::MultipleNonconforming;\n                         }),\n         \"Can't have \" << number_of_neighbors_ << \" neighbors in \" << VolumeDim\n                       << \" dimensions\");\n}\n\ntemplate <size_t VolumeDim>\nvoid Element<VolumeDim>::pup(PUP::er& p) {\n  p | id_;\n  p | neighbors_;\n  p | number_of_neighbors_;\n  p | external_boundaries_;\n  p | internal_boundaries_;\n  p | topologies_;\n  p | face_types_;\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const Element<VolumeDim>& lhs, const Element<VolumeDim>& rhs) {\n  return lhs.id() == rhs.id() and lhs.neighbors() == rhs.neighbors() and\n         lhs.number_of_neighbors() == rhs.number_of_neighbors() and\n         lhs.external_boundaries() == rhs.external_boundaries() and\n         lhs.internal_boundaries() == rhs.internal_boundaries() and\n         lhs.topologies() == rhs.topologies() and\n         lhs.face_types() == rhs.face_types();\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Element<VolumeDim>& lhs, const Element<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const Element<VolumeDim>& element) {\n  os << \"Element \" << element.id() << \":\\n\";\n  os << \"  Topology: \" << element.topologies() << '\\n';\n  os << \"  Neighbors: \" << element.neighbors() << \"\\n\";\n  os << \"  External boundaries: \" << element.external_boundaries() << \"\\n\";\n  os << \"  Face types: \" << element.face_types() << \"\\n\";\n  return os;\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                             \\\n  template class Element<GET_DIM(data)>;                   \\\n  template bool operator==(const Element<GET_DIM(data)>&,  \\\n                           const Element<GET_DIM(data)>&); \\\n  template bool operator!=(const Element<GET_DIM(data)>&,  \\\n                           const Element<GET_DIM(data)>&); \\\n  template std::ostream& operator<<(std::ostream&,         \\\n                                    const Element<GET_DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/Structure/Element.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class Element.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iosfwd>\n#include <unordered_set>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/FaceType.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup ComputationalDomainGroup\n/// A spectral element with knowledge of its neighbors.\n///\n/// \\tparam VolumeDim the volume dimension.\ntemplate <size_t VolumeDim>\nclass Element {\n public:\n  using Neighbors_t = DirectionMap<VolumeDim, Neighbors<VolumeDim>>;\n\n  /// Constructor\n  ///\n  /// \\param id a unique identifier for the Element.\n  /// \\param neighbors info about the Elements that share an interface\n  /// with this Element.\n  /// \\param topologies domain::Topology in each dimension (default value is\n  /// domain::Topology::I1)\n  Element(ElementId<VolumeDim> id, Neighbors_t neighbors,\n          std::array<domain::Topology, VolumeDim> topologies =\n              make_array<VolumeDim>(domain::Topology::I1));\n\n  /// Default needed for serialization\n  Element() = default;\n\n  ~Element() = default;\n  Element(const Element<VolumeDim>& /*rhs*/) = default;\n  Element(Element<VolumeDim>&& /*rhs*/) = default;\n  Element<VolumeDim>& operator=(const Element<VolumeDim>& /*rhs*/) = default;\n  Element<VolumeDim>& operator=(Element<VolumeDim>&& /*rhs*/) = default;\n\n  /// The directions of the faces of the Element that are external boundaries.\n  const std::unordered_set<Direction<VolumeDim>>& external_boundaries() const {\n    return external_boundaries_;\n  }\n\n  /// The directions of the faces of the Element that are internal boundaries.\n  const std::unordered_set<Direction<VolumeDim>>& internal_boundaries() const {\n    return internal_boundaries_;\n  }\n\n  /// A unique ID for the Element.\n  const ElementId<VolumeDim>& id() const { return id_; }\n\n  /// Information about the neighboring Elements.\n  const Neighbors_t& neighbors() const { return neighbors_; }\n\n  /// The number of neighbors this element has\n  size_t number_of_neighbors() const { return number_of_neighbors_; }\n\n  /// The topology in each dimensio of this Element\n  const std::array<domain::Topology, VolumeDim>& topologies() const {\n    return topologies_;\n  }\n\n  /// The FaceType in each direction\n  const DirectionMap<VolumeDim, domain::FaceType>& face_types() const {\n    return face_types_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  ElementId<VolumeDim> id_{};\n  Neighbors_t neighbors_{};\n  size_t number_of_neighbors_{};\n  std::unordered_set<Direction<VolumeDim>> external_boundaries_{};\n  std::unordered_set<Direction<VolumeDim>> internal_boundaries_{};\n  std::array<domain::Topology, VolumeDim> topologies_;\n  DirectionMap<VolumeDim, domain::FaceType> face_types_{};\n};\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const Element<VolumeDim>& element);\n\ntemplate <size_t VolumeDim>\nbool operator==(const Element<VolumeDim>& lhs, const Element<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Element<VolumeDim>& lhs, const Element<VolumeDim>& rhs);\n"
  },
  {
    "path": "src/Domain/Structure/ElementId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/ElementId.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <optional>\n#include <ostream>\n#include <regex>\n#include <string>\n#include <type_traits>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/StdHelpers/Bit.hpp\"\n\n// The `static_assert`s verify that `ElementId` satisfies the constraints\n// imposed by Charm++ to make `ElementId` able to act as an index into Charm++'s\n// arrays. These constraints are:\n// - `ElementId` must satisfy `std::is_standard_layout` and `std::is_trivial`\n// - `ElementId` is the size of two `int`s`\nstatic_assert(std::is_standard_layout_v<ElementId<1>> and\n              std::is_trivial_v<ElementId<1>>);\nstatic_assert(std::is_standard_layout_v<ElementId<2>> and\n              std::is_trivial_v<ElementId<2>>);\nstatic_assert(std::is_standard_layout_v<ElementId<3>> and\n              std::is_trivial_v<ElementId<3>>);\n\nstatic_assert(sizeof(ElementId<1>) == 2 * sizeof(int),\n              \"Wrong size for ElementId<1>\");\nstatic_assert(sizeof(ElementId<2>) == 2 * sizeof(int),\n              \"Wrong size for ElementId<2>\");\nstatic_assert(sizeof(ElementId<3>) == 2 * sizeof(int),\n              \"Wrong size for ElementId<3>\");\n\n// IMPLEMENTATION DETAILS:\n// We use a compact representation of a SegmentId as a uint16_t.\n// This allows 16 refinement levels from the coarsest level 0 thorugh 15\n// Labeling the 16 bits of the uint16_t from right (0) to left (15), the\n// position of the bit of the leading 1 (also known as the most significant bit)\n// determines the refinement level of the SegmentId, while masking that bit\n// (i.e. setting it to zero) of the uint16_t will determine the index of the\n// SegmentId.\n// We reserve 0 as representing an undefined SegmentId\n// Thus using the string representation of a SegmentId as LlIi with l and i\n// as the refinement level and index respectively we have:\n// 0000000000000000 undefined\n// 0000000000000001 L0I0\n// 0000000000000010 L1I0\n// 0000000000000011 L1I1\n// 0000000000000100 L2I0\n// ...\n// 1111111111111111 L15I32767\nnamespace {\nuint16_t make_compact_segment_id(const SegmentId& segment_id) {\n  return (uint16_t{1} << segment_id.refinement_level()) +\n         static_cast<uint16_t>(segment_id.index());\n}\n\nsize_t get_refinement_level(const uint16_t compact_segment_id) {\n  ASSERT(compact_segment_id > 0, \"Undefined compact segment id\");\n  return 15_st - static_cast<size_t>(std::countl_zero(compact_segment_id));\n}\n\nsize_t get_index(const uint16_t compact_segment_id) {\n  ASSERT(compact_segment_id > 0, \"Undefined compact segment id\");\n  return static_cast<size_t>(compact_segment_id -\n                             std::bit_floor(compact_segment_id));\n}\n\nSegmentId make_segment_id(const uint16_t compact_segment_id) {\n  ASSERT(compact_segment_id > 0, \"Undefined compact segment id\");\n  return SegmentId{get_refinement_level(compact_segment_id),\n                   get_index(compact_segment_id)};\n}\n\nbool is_on_lower_block_boundary(const uint16_t compact_segment_id) {\n  ASSERT(compact_segment_id > 0, \"Undefined compact segment id\");\n  // true if index is zero, which means only a single bit is a one\n  return std::has_single_bit(compact_segment_id);\n}\n\nbool is_on_upper_block_boundary(const uint16_t compact_segment_id) {\n  ASSERT(compact_segment_id > 0, \"Undefined compact segment id\");\n  // true if all bits after the leading one are also one\n  return 16 == (std::countl_zero(compact_segment_id) +\n                std::countr_one(compact_segment_id));\n}\n}  // namespace\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim>::ElementId(const uint8_t block_id,\n                                const uint8_t grid_index,\n                                const uint8_t direction,\n                                const uint16_t compact_segment_id_xi,\n                                const uint16_t compact_segment_id_eta,\n                                const uint16_t compact_segment_id_zeta)\n    : block_id_(block_id),\n      grid_index_(grid_index),\n      direction_(direction),\n      compact_segment_id_xi_(compact_segment_id_xi),\n      compact_segment_id_eta_(compact_segment_id_eta),\n      compact_segment_id_zeta_(compact_segment_id_zeta) {}\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim>::ElementId(const size_t block_id, const size_t grid_index)\n    : block_id_(static_cast<uint8_t>(block_id)),\n      grid_index_(static_cast<uint8_t>(grid_index)),\n      direction_(Direction<VolumeDim>::self().bits()),\n      compact_segment_id_xi_(uint16_t{1}),\n      compact_segment_id_eta_(VolumeDim > 1 ? uint16_t{1} : uint16_t{0}),\n      compact_segment_id_zeta_(VolumeDim > 2 ? uint16_t{1} : uint16_t{0}) {\n  ASSERT(block_id < two_to_the(block_id_bits),\n         \"Block id out of bounds: \" << block_id << \"\\nMaximum value is: \"\n                                    << two_to_the(block_id_bits) - 1);\n  ASSERT(grid_index < two_to_the(grid_index_bits),\n         \"Grid index out of bounds: \" << grid_index << \"\\nMaximum value is: \"\n                                      << two_to_the(grid_index_bits) - 1);\n}\n\ntemplate <>\nElementId<1>::ElementId(const size_t block_id,\n                        const std::array<SegmentId, 1>& segment_ids,\n                        const size_t grid_index)\n    : block_id_(static_cast<uint8_t>(block_id)),\n      grid_index_(static_cast<uint8_t>(grid_index)),\n      direction_(Direction<3>::self().bits()),\n      compact_segment_id_xi_(make_compact_segment_id(segment_ids[0])),\n      compact_segment_id_eta_(uint16_t{0}),\n      compact_segment_id_zeta_(uint16_t{0}) {\n  ASSERT(block_id < two_to_the(block_id_bits),\n         \"Block id out of bounds: \" << block_id << \"\\nMaximum value is: \"\n                                    << two_to_the(block_id_bits) - 1);\n  ASSERT(grid_index < two_to_the(grid_index_bits),\n         \"Grid index out of bounds: \" << grid_index << \"\\nMaximum value is: \"\n                                      << two_to_the(grid_index_bits) - 1);\n}\n\ntemplate <>\nElementId<2>::ElementId(const size_t block_id,\n                        const std::array<SegmentId, 2>& segment_ids,\n                        const size_t grid_index)\n    : block_id_(static_cast<uint8_t>(block_id)),\n      grid_index_(static_cast<uint8_t>(grid_index)),\n      direction_(Direction<3>::self().bits()),\n      compact_segment_id_xi_(make_compact_segment_id(segment_ids[0])),\n      compact_segment_id_eta_(make_compact_segment_id(segment_ids[1])),\n      compact_segment_id_zeta_(uint16_t{0}) {\n  ASSERT(block_id < two_to_the(block_id_bits),\n         \"Block id out of bounds: \" << block_id << \"\\nMaximum value is: \"\n                                    << two_to_the(block_id_bits) - 1);\n  ASSERT(grid_index < two_to_the(grid_index_bits),\n         \"Grid index out of bounds: \" << grid_index << \"\\nMaximum value is: \"\n                                      << two_to_the(grid_index_bits) - 1);\n}\n\ntemplate <>\nElementId<3>::ElementId(const size_t block_id,\n                        const std::array<SegmentId, 3>& segment_ids,\n                        const size_t grid_index)\n    : block_id_(static_cast<uint8_t>(block_id)),\n      grid_index_(static_cast<uint8_t>(grid_index)),\n      direction_(Direction<3>::self().bits()),\n      compact_segment_id_xi_(make_compact_segment_id(segment_ids[0])),\n      compact_segment_id_eta_(make_compact_segment_id(segment_ids[1])),\n      compact_segment_id_zeta_(make_compact_segment_id(segment_ids[2])) {\n  ASSERT(block_id < two_to_the(block_id_bits),\n         \"Block id out of bounds: \" << block_id << \"\\nMaximum value is: \"\n                                    << two_to_the(block_id_bits) - 1);\n  ASSERT(grid_index < two_to_the(grid_index_bits),\n         \"Grid index out of bounds: \" << grid_index << \"\\nMaximum value is: \"\n                                      << two_to_the(grid_index_bits) - 1);\n}\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim>::ElementId(const Direction<VolumeDim>& direction,\n                                const ElementId<VolumeDim>& element_id)\n    : block_id_(element_id.block_id_),\n      grid_index_(element_id.grid_index_),\n      direction_(direction.bits()),\n      compact_segment_id_xi_(element_id.compact_segment_id_xi_),\n      compact_segment_id_eta_(element_id.compact_segment_id_eta_),\n      compact_segment_id_zeta_(element_id.compact_segment_id_zeta_) {}\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim>::ElementId(const std::string& grid_name)\n    : direction_{Direction<VolumeDim>::self().bits()} {\n  std::string pattern_str = \"\\\\[B([0-9]+),\\\\(\";\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    pattern_str.append(\"L([0-9]+)I([0-9]+)\");\n    if (d < VolumeDim - 1) {\n      pattern_str.append(\",\");\n    }\n  }\n  pattern_str.append(\"\\\\)(,G([0-9]+))?\\\\]\");\n  const std::regex pattern(pattern_str);\n  std::smatch match;\n  std::regex_match(grid_name, match, pattern);\n  if (match.empty()) {\n    throw std::invalid_argument{\"Invalid grid name '\" + grid_name +\n                                \"' does not match the pattern '\" + pattern_str +\n                                \"'.\"};\n  }\n  const auto to_size_t = [](const std::ssub_match& s) {\n    return static_cast<size_t>(std::stoi(s.str()));\n  };\n  block_id_ = to_size_t(match[1]);\n  const auto make_compact_segment_id = [](const size_t refinement_level,\n                                          const size_t index,\n                                          const std::string& name) {\n    ASSERT(refinement_level <= max_refinement_level,\n           \"Refinement level '\"\n               << refinement_level << \"' out of bounds for element ID '\" << name\n               << \"'. Maximum value is: \" << max_refinement_level);\n    ASSERT(index < two_to_the(refinement_level),\n           \"Index '\" << index << \"' out of bounds for element ID '\" << name\n                     << \"'. Maximum value is: \"\n                     << two_to_the(refinement_level) - 1);\n    return (uint16_t{1} << refinement_level) + static_cast<uint16_t>(index);\n  };\n\n  compact_segment_id_xi_ = make_compact_segment_id(\n      to_size_t(match[2]), to_size_t(match[3]), grid_name);\n  if constexpr (VolumeDim > 1) {\n    compact_segment_id_eta_ = make_compact_segment_id(\n        to_size_t(match[4]), to_size_t(match[5]), grid_name);\n  } else {\n    compact_segment_id_eta_ = 0;\n  }\n  if constexpr (VolumeDim > 2) {\n    compact_segment_id_zeta_ = make_compact_segment_id(\n        to_size_t(match[6]), to_size_t(match[7]), grid_name);\n  } else {\n    compact_segment_id_zeta_ = 0;\n  }\n  if (match[1                // Full matched string\n            + 1              // Block ID\n            + 2 * VolumeDim  // Segment IDs\n            + 1              // Optional grid_index subexpression\n  ]\n          .matched) {\n    grid_index_ = to_size_t(match[2 * VolumeDim + 3]);\n  } else {\n    grid_index_ = 0;\n  }\n  ASSERT(block_id_ < two_to_the(block_id_bits),\n         \"Block id out of bounds: \" << block_id_ << \"\\nMaximum value is: \"\n                                    << two_to_the(block_id_bits) - 1);\n  ASSERT(grid_index_ < two_to_the(grid_index_bits),\n         \"Grid index out of bounds: \" << grid_index_ << \"\\nMaximum value is: \"\n                                      << two_to_the(grid_index_bits) - 1);\n}\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim> ElementId<VolumeDim>::id_of_child(const size_t dim,\n                                                       const Side side) const {\n  ASSERT(dim < VolumeDim,\n         \"Dimension must be smaller than \" << VolumeDim << \", but is: \" << dim);\n  ElementId<VolumeDim> result = this->without_direction();\n  switch (dim) {\n    case 0:\n      ASSERT(get_refinement_level(result.compact_segment_id_xi_) !=\n                 max_refinement_level,\n             \"Cannot get child of element on max refinement level\");\n      result.compact_segment_id_xi_ = result.compact_segment_id_xi_ << 1;\n      if (side == Side::Upper) {\n        ++result.compact_segment_id_xi_;\n      }\n      return result;\n    case 1:\n      ASSERT(get_refinement_level(result.compact_segment_id_eta_) !=\n                 max_refinement_level,\n             \"Cannot get child of element on max refinement level\");\n      result.compact_segment_id_eta_ = result.compact_segment_id_eta_ << 1;\n      if (side == Side::Upper) {\n        ++result.compact_segment_id_eta_;\n      }\n      return result;\n    case 2:\n      ASSERT(get_refinement_level(result.compact_segment_id_zeta_) !=\n                 max_refinement_level,\n             \"Cannot get child of element on max refinement level\");\n      result.compact_segment_id_zeta_ = result.compact_segment_id_zeta_ << 1;\n      if (side == Side::Upper) {\n        ++result.compact_segment_id_zeta_;\n      }\n      return result;\n    default:\n      ERROR(\"Invalid dimension: \" << dim);\n  }\n}\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim> ElementId<VolumeDim>::id_of_parent(\n    const size_t dim) const {\n  ASSERT(dim < VolumeDim,\n         \"Dimension must be smaller than \" << VolumeDim << \", but is: \" << dim);\n  ElementId<VolumeDim> result = this->without_direction();\n  switch (dim) {\n    case 0:\n      ASSERT(get_refinement_level(result.compact_segment_id_xi_) != 0,\n             \"Cannot get parent of element on refinement level 0\");\n      result.compact_segment_id_xi_ = result.compact_segment_id_xi_ >> 1;\n      return result;\n    case 1:\n      ASSERT(get_refinement_level(result.compact_segment_id_xi_) != 0,\n             \"Cannot get parent of element on refinement level 0\");\n      result.compact_segment_id_eta_ = result.compact_segment_id_eta_ >> 1;\n      return result;\n    case 2:\n      ASSERT(get_refinement_level(result.compact_segment_id_xi_) != 0,\n             \"Cannot get parent of element on refinement level 0\");\n      result.compact_segment_id_zeta_ = result.compact_segment_id_zeta_ >> 1;\n      return result;\n    default:\n      ERROR(\"Invalid dimension: \" << dim);\n  }\n}\n\ntemplate <size_t VolumeDim>\nDirection<VolumeDim> ElementId<VolumeDim>::direction() const {\n  return Direction<VolumeDim>{static_cast<typename Direction<VolumeDim>::Axis>(\n                                  direction_ bitand 0b0011),\n                              static_cast<Side>(direction_ bitand 0b1100)};\n}\n\ntemplate <size_t VolumeDim>\nstd::array<size_t, VolumeDim> ElementId<VolumeDim>::refinement_levels() const {\n  if constexpr (VolumeDim == 1) {\n    return {{get_refinement_level(compact_segment_id_xi_)}};\n  } else if constexpr (VolumeDim == 2) {\n    return {{get_refinement_level(compact_segment_id_xi_),\n             get_refinement_level(compact_segment_id_eta_)}};\n  } else if constexpr (VolumeDim == 3) {\n    return {{get_refinement_level(compact_segment_id_xi_),\n             get_refinement_level(compact_segment_id_eta_),\n             get_refinement_level(compact_segment_id_zeta_)}};\n  }\n}\n\ntemplate <size_t VolumeDim>\nstd::array<SegmentId, VolumeDim> ElementId<VolumeDim>::segment_ids() const {\n  if constexpr (VolumeDim == 1) {\n    return {{make_segment_id(compact_segment_id_xi_)}};\n  } else if constexpr (VolumeDim == 2) {\n    return {{make_segment_id(compact_segment_id_xi_),\n             make_segment_id(compact_segment_id_eta_)}};\n  } else if constexpr (VolumeDim == 3) {\n    return {{make_segment_id(compact_segment_id_xi_),\n             make_segment_id(compact_segment_id_eta_),\n             make_segment_id(compact_segment_id_zeta_)}};\n  }\n}\n\ntemplate <size_t VolumeDim>\nSegmentId ElementId<VolumeDim>::segment_id(const size_t dim) const {\n  ASSERT(dim < VolumeDim,\n         \"Dimension must be smaller than \" << VolumeDim << \", but is: \" << dim);\n  switch (dim) {\n    case 0:\n      return make_segment_id(compact_segment_id_xi_);\n    case 1:\n      return make_segment_id(compact_segment_id_eta_);\n    case 2:\n      return make_segment_id(compact_segment_id_zeta_);\n    default:\n      ERROR(\"Invalid dimension: \" << dim);\n  }\n}\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim> ElementId<VolumeDim>::external_boundary_id() {\n  // In order to distinguish this from an uninitialized ElementId, we use the\n  // maximum possible value that can be stored in `block_id_bits`\n  static_assert(ElementId::block_id_bits == 8);\n  return ElementId{255, 0, 0, 0, 0, 0};\n}\n\ntemplate <size_t VolumeDim>\nElementId<VolumeDim> ElementId<VolumeDim>::without_direction() const {\n  ElementId result = *this;\n  result.direction_ = Direction<VolumeDim>::self().bits();\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nsize_t ElementId<VolumeDim>::to_short_id() const {\n  // The bottom 16 bits are: block_id (8), grid_index (4), direction (4).\n  // Shift right by 16 to return only the 48-bit segment ID portion.\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  return (*reinterpret_cast<const uint64_t*>(this)) >> 16;\n}\n\ntemplate <size_t VolumeDim>\nsize_t ElementId<VolumeDim>::number_of_block_boundaries() const {\n  return (is_on_lower_block_boundary(compact_segment_id_xi_) ? 1_st : 0_st) +\n         (is_on_upper_block_boundary(compact_segment_id_xi_) ? 1_st : 0_st) +\n         (VolumeDim > 1\n              ? (is_on_lower_block_boundary(compact_segment_id_eta_) ? 1_st\n                                                                     : 0_st) +\n                    (is_on_upper_block_boundary(compact_segment_id_eta_) ? 1_st\n                                                                         : 0_st)\n              : 0_st) +\n         (VolumeDim > 2\n              ? (is_on_lower_block_boundary(compact_segment_id_zeta_) ? 1_st\n                                                                      : 0_st) +\n                    (is_on_upper_block_boundary(compact_segment_id_zeta_)\n                         ? 1_st\n                         : 0_st)\n              : 0_st);\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const ElementId<VolumeDim>& id) {\n  os << \"[B\" << id.block_id() << ',' << id.segment_ids();\n  if (id.grid_index() > 0) {\n    os << \",G\" << id.grid_index();\n  }\n  os << ']';\n  return os;\n}\n\ntemplate <size_t VolumeDim>\nbool operator<(const ElementId<VolumeDim>& lhs,\n               const ElementId<VolumeDim>& rhs) {\n  if (lhs.grid_index_ != rhs.grid_index_) {\n    return lhs.grid_index_ < rhs.grid_index_;\n  }\n  if (lhs.block_id_ != rhs.block_id_) {\n    return lhs.block_id_ < rhs.block_id_;\n  }\n  if (lhs.compact_segment_id_xi_ != rhs.compact_segment_id_xi_) {\n    return lhs.compact_segment_id_xi_ < rhs.compact_segment_id_xi_;\n  }\n  if constexpr (VolumeDim > 1) {\n    if (lhs.compact_segment_id_eta_ != rhs.compact_segment_id_eta_) {\n      return lhs.compact_segment_id_eta_ < rhs.compact_segment_id_eta_;\n    }\n  }\n  if constexpr (VolumeDim > 2) {\n    if (lhs.compact_segment_id_zeta_ != rhs.compact_segment_id_zeta_) {\n      return lhs.compact_segment_id_zeta_ < rhs.compact_segment_id_zeta_;\n    }\n  }\n  return false;\n}\n\ntemplate <size_t VolumeDim>\nbool overlapping(const ElementId<VolumeDim>& a, const ElementId<VolumeDim>& b) {\n  if (a == b) {\n    return true;\n  }\n  if (a.block_id() != b.block_id()) {\n    return false;\n  }\n  const auto segments_a = a.segment_ids();\n  const auto segments_b = b.segment_ids();\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    if (not overlapping(gsl::at(segments_a, d), gsl::at(segments_b, d))) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <size_t Dim>\nbool is_zeroth_element(const ElementId<Dim>& id,\n                       const std::optional<size_t>& grid_index) {\n  if (id.block_id_ != 0) {\n    return false;\n  }\n  if (not is_on_lower_block_boundary(id.compact_segment_id_xi_)) {\n    return false;\n  }\n  if (Dim > 1 and not is_on_lower_block_boundary(id.compact_segment_id_eta_)) {\n    return false;\n  }\n  if (Dim > 2 and not is_on_lower_block_boundary(id.compact_segment_id_zeta_)) {\n    return false;\n  }\n  if (grid_index.has_value()) {\n    return id.grid_index_ == grid_index.value();\n  }\n  return true;\n}\n\ntemplate <size_t Dim>\nbool is_zeroth_element(const ElementId<Dim>& id) {\n  return is_zeroth_element(id, std::nullopt);\n}\n\n// LCOV_EXCL_START\ntemplate <size_t VolumeDim>\nsize_t hash_value(const ElementId<VolumeDim>& id) {\n  // ElementId is used as an opaque array of bytes by Charm, so we\n  // treat it that way as well. However, since only the lowest 64 bits are\n  // used, the hash is trivial: just extract the lowest 64 bits.\n  // clang-tidy: do not use reinterpret_cast\n  return (*reinterpret_cast<const uint64_t*>(&id)) bitand  // NOLINT\n         (compl ElementId<VolumeDim>::direction_mask);\n}\n\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nnamespace std {\ntemplate <size_t VolumeDim>\nsize_t hash<ElementId<VolumeDim>>::operator()(\n    const ElementId<VolumeDim>& id) const {\n  return hash_value(id);\n}\n}  // namespace std\n// LCOV_EXCL_STOP\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template class ElementId<GET_DIM(data)>;                                  \\\n  template std::ostream& operator<<(std::ostream&,                          \\\n                                    const ElementId<GET_DIM(data)>&);       \\\n  template bool operator<(const ElementId<GET_DIM(data)>& lhs,              \\\n                          const ElementId<GET_DIM(data)>& rhs);             \\\n  template bool overlapping(const ElementId<GET_DIM(data)>& a,              \\\n                            const ElementId<GET_DIM(data)>& b);             \\\n  template bool is_zeroth_element(const ElementId<GET_DIM(data)>& id,       \\\n                                  const std::optional<size_t>& grid_index); \\\n  template bool is_zeroth_element(const ElementId<GET_DIM(data)>& id);      \\\n  template size_t hash_value(const ElementId<GET_DIM(data)>&);              \\\n  namespace std { /* NOLINT */                                              \\\n  template struct hash<ElementId<GET_DIM(data)>>;                           \\\n  }\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/Structure/ElementId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class ElementId.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <iosfwd>\n#include <optional>\n#include <string>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief An ElementId uniquely labels an Element.\n *\n * It is constructed from the BlockId of the Block to which the Element belongs\n * and the VolumeDim SegmentIds that label the segments of the Block that the\n * Element covers. An optional `grid_index` identifies elements with the same\n * BlockId and SegmentIds across multiple grids.\n *\n * \\details\n * The `ElementId` serves as an index that is compatible with Charm++ and\n * therefore must adhere to the restrictions imposed by Charm++. These are:\n * - `ElementId` must satisfy `std::is_pod`\n * - `ElementId` must not be larger than the size of three `int`s, i.e.\n *   `sizeof(ElementId) <= 3 * sizeof(int)`\n */\ntemplate <size_t VolumeDim>\nclass alignas(int[2]) ElementId {  // NOLINT(modernize-avoid-c-arrays)\n public:\n  // We restrict the ElementId size to 64 bits for easy hashing into\n  // size_t. This still allows us to have over 9 quadrillion elements, which is\n  // probably enough. 2^45 * 2^8 9 quadrillion elements per grid index, with\n  // up to 16 grid indices.\n  //\n  // Note: C++ populates bits from right to left in order of the\n  // variables. This gives us the direction_mask we use below.\n  static constexpr size_t block_id_bits = 8;\n  static constexpr size_t grid_index_bits = 4;\n  static constexpr size_t direction_bits = 4;\n  /// The maximum allowed refinement level\n  static constexpr size_t max_refinement_level = 15;\n  static constexpr uint64_t direction_shift =\n      static_cast<uint64_t>(block_id_bits + grid_index_bits);\n  static constexpr uint64_t direction_mask = static_cast<uint64_t>(0b1111)\n                                             << direction_shift;\n  static_assert(block_id_bits + 3 * (1 + max_refinement_level) +\n                        grid_index_bits + direction_bits ==\n                    static_cast<size_t>(2 * 8) * sizeof(int),\n                \"Bit representation requires padding or is too large\");\n\n  static constexpr size_t volume_dim = VolumeDim;\n\n  /// Default constructor needed for Charm++ serialization.\n  ElementId() = default;\n  ElementId(const ElementId&) = default;\n  ElementId& operator=(const ElementId&) = default;\n  ElementId(ElementId&&) = default;\n  ElementId& operator=(ElementId&&) = default;\n  ~ElementId() = default;\n\n  /// Create the ElementId of the root Element of a Block.\n  explicit ElementId(size_t block_id, size_t grid_index = 0);\n\n  /// Create an arbitrary ElementId.\n  ElementId(size_t block_id,\n            const std::array<SegmentId, VolumeDim>& segment_ids,\n            size_t grid_index = 0);\n\n  /// Create an ElementId from its string representation (see `operator<<`).\n  explicit ElementId(const std::string& grid_name);\n\n  ElementId<VolumeDim> id_of_child(size_t dim, Side side) const;\n\n  ElementId<VolumeDim> id_of_parent(size_t dim) const;\n\n  size_t block_id() const { return block_id_; }\n\n  size_t grid_index() const { return grid_index_; }\n\n  std::array<size_t, VolumeDim> refinement_levels() const;\n\n  std::array<SegmentId, VolumeDim> segment_ids() const;\n\n  SegmentId segment_id(size_t dim) const;\n\n  /// Returns an ElementId meant for identifying data on external boundaries,\n  /// which does not correspond to the Id of an actual element.\n  static ElementId<VolumeDim> external_boundary_id();\n\n  /*!\n   * \\brief Returns a compact ID containing only the segment ID bits,\n   * with block_id, grid_index, and direction bits stripped.\n   *\n   * This is useful for identifying elements in visualization tools\n   * like ParaView.\n   */\n  size_t to_short_id() const;\n\n  /// Returns the number of block boundaries the element has.\n  size_t number_of_block_boundaries() const;\n\n protected:\n  /// Create an `ElementId` in a specified direction.\n  ElementId(const Direction<VolumeDim>& direction,\n            const ElementId<VolumeDim>& element_id);\n\n  Direction<VolumeDim> direction() const;\n\n  ElementId without_direction() const;\n\n private:\n  template <size_t Dim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const ElementId<Dim>& lhs, const ElementId<Dim>& rhs);\n\n  template <size_t Dim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator<(const ElementId<Dim>& lhs, const ElementId<Dim>& rhs);\n\n  template <size_t Dim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool is_zeroth_element(const ElementId<Dim>& id,\n                                const std::optional<size_t>& grid_index);\n\n  ElementId(uint8_t block_id, uint8_t grid_index, uint8_t direction,\n            uint16_t compact_segment_id_xi, uint16_t compact_segment_id_eta,\n            uint16_t compact_segment_id_zeta);\n\n  uint8_t block_id_ : block_id_bits;\n  uint8_t grid_index_ : grid_index_bits;\n  uint8_t direction_ : direction_bits;  // end first 16 bits\n  // each of the following is 16 bits in length\n  uint16_t compact_segment_id_xi_ : max_refinement_level + 1;\n  uint16_t compact_segment_id_eta_ : max_refinement_level + 1;\n  uint16_t compact_segment_id_zeta_ : max_refinement_level + 1;\n};\n\n/// \\cond\n// clang-format off\n// macro that generate the pup operator for SegmentId\nPUPbytes(ElementId<1>)      // NOLINT\nPUPbytes(ElementId<2>)  // NOLINT\nPUPbytes(ElementId<3>)  // NOLINT\n/// \\endcond\n\n/// Output operator for ElementId.\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os, const ElementId<VolumeDim>& id);\n// clang-format on\n\n/// Equivalence operator for ElementId.\ntemplate <size_t VolumeDim>\nbool operator==(const ElementId<VolumeDim>& lhs,\n                const ElementId<VolumeDim>& rhs);\n\n/// Inequivalence operator for ElementId.\ntemplate <size_t VolumeDim>\nbool operator!=(const ElementId<VolumeDim>& lhs,\n                const ElementId<VolumeDim>& rhs);\n\n/// Define an ordering of element IDs first by grid index, then by block ID,\n/// then by segment ID in each dimension in turn. In each dimension, segment IDs\n/// are ordered first by refinement level (which will typically be the same when\n/// comparing two element IDs), and second by index. There's no particular\n/// reason for this choice of ordering. For applications such as distributing\n/// elements among cores, orderings such as defined by\n/// `domain::BlockZCurveProcDistribution` may be more appropriate.\ntemplate <size_t VolumeDim>\nbool operator<(const ElementId<VolumeDim>& lhs,\n               const ElementId<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nbool operator>(const ElementId<VolumeDim>& lhs,\n               const ElementId<VolumeDim>& rhs) {\n  return rhs < lhs;\n}\ntemplate <size_t VolumeDim>\nbool operator<=(const ElementId<VolumeDim>& lhs,\n                const ElementId<VolumeDim>& rhs) {\n  return !(lhs > rhs);\n}\ntemplate <size_t VolumeDim>\nbool operator>=(const ElementId<VolumeDim>& lhs,\n                const ElementId<VolumeDim>& rhs) {\n  return !(lhs < rhs);\n}\n\n/// \\ingroup ComputationalDomainGroup\n/// Check if two elements overlap, i.e., they are in the same block\n/// and all their segments overlap.\ntemplate <size_t VolumeDim>\nbool overlapping(const ElementId<VolumeDim>& a, const ElementId<VolumeDim>& b);\n\n/// @{\n/// \\brief Returns a bool if the element is the zeroth element in the domain.\n///\n/// \\details An element is considered to be the zeroth element if its ElementId\n/// `id` has\n/// 1. id.block_id() == 0\n/// 2. All id.segment_ids() have SegmentId.index() == 0\n/// 3. If the argument `grid_index` is specified, id.grid_index() == grid_index.\n///\n/// This means that the only element in a domain that this function will return\n/// `true` for is the element in the lower corner of Block0 of that domain. The\n/// `grid_index` will determine which domain is used for the comparison. During\n/// evolutions, only one domain will be active at a time so it doesn't make\n/// sense to compare the `grid_index`. However, during an elliptic solve\n/// when there are multiple grids, this `grid_index` is useful for specifying\n/// only one element over all domains.\n///\n/// This function is useful if you need a unique element in the domain because\n/// only one element in the whole domain can be the zeroth element.\n///\n/// \\parblock\n/// \\warning If you have multiple grids and you don't specify the `grid_index`\n/// argument, this function will return `true` for one element in every grid\n/// and thus can't be used to determine a unique element in a simulation; only a\n/// unique element in each grid.\n/// \\endparblock\n/// \\parblock\n/// \\warning If the domain is re-gridded, a different ElementId may represent\n/// the zeroth element.\n/// \\endparblock\ntemplate <size_t Dim>\nbool is_zeroth_element(const ElementId<Dim>& id,\n                       const std::optional<size_t>& grid_index);\n\n// This overload is added (instead of adding a default value for grid_index)\n// in order to avoid adding DomainStructures as a dependency of Parallel\n// by using a forward declaration in Parallel/DistributedObject.hpp\ntemplate <size_t Dim>\nbool is_zeroth_element(const ElementId<Dim>& id);\n/// @}\n// ######################################################################\n// INLINE DEFINITIONS\n// ######################################################################\n\ntemplate <size_t VolumeDim>\nsize_t hash_value(const ElementId<VolumeDim>& id);\n\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nnamespace std {\ntemplate <size_t VolumeDim>\nstruct hash<ElementId<VolumeDim>> {\n  size_t operator()(const ElementId<VolumeDim>& id) const;\n};\n}  // namespace std\n\ntemplate <size_t VolumeDim>\ninline bool operator==(const ElementId<VolumeDim>& lhs,\n                       const ElementId<VolumeDim>& rhs) {\n  // Note: Direction is intentionally skipped.\n  return lhs.block_id_ == rhs.block_id_ and\n         lhs.grid_index_ == rhs.grid_index_ and\n         lhs.compact_segment_id_xi_ == rhs.compact_segment_id_xi_ and\n         lhs.compact_segment_id_eta_ == rhs.compact_segment_id_eta_ and\n         lhs.compact_segment_id_zeta_ == rhs.compact_segment_id_zeta_;\n}\n\ntemplate <size_t VolumeDim>\ninline bool operator!=(const ElementId<VolumeDim>& lhs,\n                       const ElementId<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n"
  },
  {
    "path": "src/Domain/Structure/ElementSearchTree.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/ElementSearchTree.hpp\"\n\n#include <array>\n#include <boost/geometry.hpp>\n#include <boost/geometry/index/rtree.hpp>\n#include <cstddef>\n#include <map>\n#include <memory>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace boost::geometry::traits {\n// Make Tensor compatible with Boost.Geometry\n// This is needed to search for a block-logical coordinate given as a Tensor\n// in the `ElementSearchTree`.\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct tag<tnsr::I<DataType, Dim, Frame>> {\n  using type = boost::geometry::point_tag;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct coordinate_type<tnsr::I<DataType, Dim, Frame>> {\n  using type = DataType;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct coordinate_system<tnsr::I<DataType, Dim, Frame>> {\n  using type = boost::geometry::cs::cartesian;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct dimension<tnsr::I<DataType, Dim, Frame>>\n    : std::integral_constant<size_t, Dim> {};\ntemplate <typename DataType, size_t Dim, typename Frame, size_t GetDimension>\nstruct access<tnsr::I<DataType, Dim, Frame>, GetDimension> {\n  static constexpr DataType get(const tnsr::I<DataType, Dim, Frame>& point) {\n    return ::get<GetDimension>(point);\n  }\n  static void set(tnsr::I<DataType, Dim, Frame>& point, const DataType& value) {\n    ::get<GetDimension>(point) = value;\n  }\n};\n// Make ElementId compatible with Boost.Geometry\n// Each ElementId defines a bounding box in block-logical coordinates, which is\n// used to search for elements in the `ElementSearchTree`.\ntemplate <size_t Dim>\nstruct tag<ElementId<Dim>> {\n  using type = boost::geometry::box_tag;\n};\ntemplate <size_t Dim>\nstruct coordinate_type<ElementId<Dim>> {\n  using type = double;\n};\ntemplate <size_t Dim>\nstruct coordinate_system<ElementId<Dim>> {\n  using type = boost::geometry::cs::cartesian;\n};\ntemplate <size_t Dim>\nstruct dimension<ElementId<Dim>> : std::integral_constant<size_t, Dim> {};\ntemplate <size_t Dim>\nstruct point_type<ElementId<Dim>> {\n  using type = tnsr::I<double, Dim, ::Frame::BlockLogical>;\n};\ntemplate <size_t Dim, size_t Index, size_t GetDimension>\nstruct indexed_access<ElementId<Dim>, Index, GetDimension> {\n  static constexpr double get(const ElementId<Dim>& element_id) {\n    if constexpr (Index == 0) {\n      return element_id.segment_id(GetDimension).endpoint(Side::Lower);\n    } else {\n      return element_id.segment_id(GetDimension).endpoint(Side::Upper);\n    }\n  }\n};\n}  // namespace boost::geometry::traits\n\nnamespace domain {\nnamespace {\ntemplate <size_t Dim>\nusing SearchTreeGeometryImpl =\n    boost::geometry::index::rtree<ElementId<Dim>,\n                                  boost::geometry::index::quadratic<16>>;\n\ntemplate <size_t Dim>\nusing GeometryIterator =\n    typename SearchTreeGeometryImpl<Dim>::const_query_iterator;\n\ntemplate <size_t Dim, size_t N>\nGeometryIterator<Dim>& underlying_iterator(std::array<char, N>& data) {\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  return *reinterpret_cast<GeometryIterator<Dim>*>(data.data());\n}\n\ntemplate <size_t Dim, size_t N>\nconst GeometryIterator<Dim>& underlying_iterator(\n    const std::array<char, N>& data) {\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  return *reinterpret_cast<const GeometryIterator<Dim>*>(data.data());\n}\n}  // namespace\n\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim>::ElementSearchTreeIterator() : data_{} {\n  static_assert(decltype(data_){}.size() >= sizeof(GeometryIterator<Dim>));\n  new (data_.data()) GeometryIterator<Dim>{};\n}\n\n// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) - false positive\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim>::ElementSearchTreeIterator(\n    ElementSearchTreeIterator&& other)\n    : ElementSearchTreeIterator() {\n  underlying_iterator<Dim>(data_) =\n      std::move(underlying_iterator<Dim>(other.data_));\n}\n\n// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) - false positive\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim>::ElementSearchTreeIterator(\n    const ElementSearchTreeIterator& other)\n    : ElementSearchTreeIterator() {\n  underlying_iterator<Dim>(data_) = underlying_iterator<Dim>(other.data_);\n}\n\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim>& ElementSearchTreeIterator<Dim>::operator=(\n    ElementSearchTreeIterator&& other) {\n  underlying_iterator<Dim>(data_) =\n      std::move(underlying_iterator<Dim>(other.data_));\n  return *this;\n}\n\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim>& ElementSearchTreeIterator<Dim>::operator=(\n    const ElementSearchTreeIterator& other) {\n  underlying_iterator<Dim>(data_) = underlying_iterator<Dim>(other.data_);\n  return *this;\n}\n\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim>::~ElementSearchTreeIterator() {\n  // This stupid-looking way of writing the destructor works around an\n  // nvcc bug.\n  []<typename T>(T& iter) { iter.~T(); }(underlying_iterator<Dim>(data_));\n}\n\ntemplate <size_t Dim>\nauto ElementSearchTreeIterator<Dim>::operator*() const -> reference {\n  return *underlying_iterator<Dim>(data_);\n}\n\ntemplate <size_t Dim>\nauto ElementSearchTreeIterator<Dim>::operator->() const -> pointer {\n  return underlying_iterator<Dim>(data_).operator->();\n}\n\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim>& ElementSearchTreeIterator<Dim>::operator++() {\n  ++underlying_iterator<Dim>(data_);\n  return *this;\n}\n\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim> ElementSearchTreeIterator<Dim>::operator++(int) {\n  auto result = *this;\n  ++*this;\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nElementSearchTreeIterator<Dim> ElementSearchTreeIterator<Dim>::from_impl(\n    T impl) {\n  static_assert(std::is_same_v<T, GeometryIterator<Dim>>);\n  ElementSearchTreeIterator<Dim> result{};\n  underlying_iterator<Dim>(result.data_) = std::move(impl);\n  return result;\n}\n\ntemplate <size_t Dim2>\nbool operator==(const ElementSearchTreeIterator<Dim2>& a,\n                const ElementSearchTreeIterator<Dim2>& b) {\n  return underlying_iterator<Dim2>(a.data_) ==\n         underlying_iterator<Dim2>(b.data_);\n}\n\ntemplate <size_t Dim>\nElementSearchTree<Dim>::ElementSearchTree() : impl_(std::make_unique<Impl>()) {}\n\ntemplate <size_t Dim>\nElementSearchTree<Dim>::ElementSearchTree(ElementSearchTree<Dim>&&) = default;\n\ntemplate <size_t Dim>\nElementSearchTree<Dim>& ElementSearchTree<Dim>::operator=(\n    ElementSearchTree<Dim>&&) = default;\n\ntemplate <size_t Dim>\nElementSearchTree<Dim>::~ElementSearchTree() = default;\n\ntemplate <size_t Dim>\nsize_t ElementSearchTree<Dim>::size() const {\n  return impl_->size();\n}\n\ntemplate <size_t Dim>\nbool ElementSearchTree<Dim>::empty() const {\n  return impl_->empty();\n}\n\ntemplate <size_t Dim>\nvoid ElementSearchTree<Dim>::clear() {\n  return impl_->clear();\n}\n\ntemplate <size_t Dim>\nvoid ElementSearchTree<Dim>::insert(const ElementId<Dim>& id) {\n  impl_->insert(id);\n}\n\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim> ElementSearchTree<Dim>::begin_covers(\n    const tnsr::I<double, Dim, Frame::BlockLogical>& coords) const {\n  return ElementSearchTreeIterator<Dim>::from_impl(\n      impl_->qbegin(boost::geometry::index::covers(coords)));\n}\n\ntemplate <size_t Dim>\nElementSearchTreeIterator<Dim> ElementSearchTree<Dim>::end_covers() const {\n  return ElementSearchTreeIterator<Dim>::from_impl(impl_->qend());\n}\n\ntemplate <size_t Dim>\nstruct ElementSearchTree<Dim>::Impl : SearchTreeGeometryImpl<Dim> {};\n\ntemplate <size_t Dim>\nstd::map<size_t, ElementSearchTree<Dim>> index_element_ids(\n    const std::vector<ElementId<Dim>>& element_ids) {\n  std::map<size_t, ElementSearchTree<Dim>> trees{};\n  for (const auto& element_id : element_ids) {\n    trees[element_id.block_id()].insert(element_id);\n  }\n  return trees;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template class ElementSearchTreeIterator<DIM(data)>;                       \\\n  template class ElementSearchTree<DIM(data)>;                               \\\n  template std::map<size_t, ElementSearchTree<DIM(data)>> index_element_ids( \\\n      const std::vector<ElementId<DIM(data)>>& element_ids);                 \\\n  template bool operator==(const ElementSearchTreeIterator<DIM(data)>& a,    \\\n                           const ElementSearchTreeIterator<DIM(data)>& b);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/ElementSearchTree.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <map>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\nnamespace domain {\n/// \\cond\ntemplate <size_t Dim>\nclass ElementSearchTree;\n/// \\endcond\n\ntemplate <size_t Dim>\nclass ElementSearchTreeIterator {\n public:\n  using iterator_category = std::forward_iterator_tag;\n  using value_type = ElementId<Dim>;\n  using difference_type = std::ptrdiff_t;\n  using pointer = const ElementId<Dim>*;\n  using reference = const ElementId<Dim>&;\n\n  ElementSearchTreeIterator();\n  ElementSearchTreeIterator(ElementSearchTreeIterator&&);\n  ElementSearchTreeIterator(const ElementSearchTreeIterator&);\n  ElementSearchTreeIterator& operator=(ElementSearchTreeIterator&&);\n  ElementSearchTreeIterator& operator=(const ElementSearchTreeIterator&);\n  ~ElementSearchTreeIterator();\n\n  reference operator*() const;\n  pointer operator->() const;\n\n  ElementSearchTreeIterator& operator++();\n  ElementSearchTreeIterator operator++(int);\n\n private:\n  friend class ElementSearchTree<Dim>;\n\n  template <typename T>\n  static ElementSearchTreeIterator from_impl(T impl);\n\n  template <size_t Dim2>\n  friend bool operator==(const ElementSearchTreeIterator<Dim2>& a,\n                         const ElementSearchTreeIterator<Dim2>& b);\n\n  // Use an array and manual construction rather than a simpler pimpl\n  // since iterators are constructed a lot.\n  std::array<char, 8> data_;\n};\n\n/*!\n * \\brief Search tree for efficiently looking up elements by their bounding\n * boxes in block-logical coordinates.\n *\n * The search tree is constructed from a list of `ElementId`s, which define\n * bounding boxes in block-logical coordinates. All `ElementId`s must be in the\n * same block. Then, the search tree can be used to efficiently search for the\n * element that contains a given block-logical coordinate (see e.g.\n * `element_logical_coordinates`).\n *\n * Example usage:\n * \\snippet Test_ElementSearchTree.cpp element_search_tree_example\n *\n * Use `domain::index_element_ids()` to create one search tree for each block in\n * the domain given the full list of `ElementId`s.\n *\n * \\details The search tree is a `boost::geometry::rtree` with a choice of\n * quadratic splitting algorithm and a maximum of 16 elements per node. These\n * choices work well, but haven't been extensively tuned.\n */\ntemplate <size_t Dim>\nclass ElementSearchTree {\n public:\n  ElementSearchTree();\n  ElementSearchTree(ElementSearchTree&&);\n  ElementSearchTree& operator=(ElementSearchTree&&);\n  ElementSearchTree(const ElementSearchTree&) = delete;\n  ElementSearchTree& operator=(const ElementSearchTree&) = delete;\n  ~ElementSearchTree();\n\n  /// Construct a search tree containing the ids in `[begin, end)`.\n  template <typename Iter>\n  ElementSearchTree(Iter begin, const Iter end) : ElementSearchTree() {\n    insert(begin, end);\n  }\n\n  size_t size() const;\n  bool empty() const;\n  void clear();\n\n  void insert(const ElementId<Dim>& id);\n\n  /// Insert the ids in `[begin, end)`.\n  template <typename Iter>\n  void insert(Iter begin, const Iter end) {\n    while (begin != end) {\n      insert(*begin);\n      ++begin;\n    }\n  }\n\n  ElementSearchTreeIterator<Dim> begin_covers(\n      const tnsr::I<double, Dim, Frame::BlockLogical>& coords) const;\n\n  ElementSearchTreeIterator<Dim> end_covers() const;\n\n private:\n  // Use a pimpl to keep all the boost::geometry stuff in a cpp file,\n  // because the boost headers are quite expensive to include.\n  struct Impl;\n  std::unique_ptr<Impl> impl_;\n};\n\n/*!\n * \\brief Sorts element IDs into one `ElementSearchTree` per block for efficient\n * searching.\n *\n * Returns a map of search trees indexed by block ID. Each search tree contains\n * all the `ElementId`s for that block.\n *\n * \\see ElementSearchTree\n */\ntemplate <size_t Dim>\nstd::map<size_t, ElementSearchTree<Dim>> index_element_ids(\n    const std::vector<ElementId<Dim>>& element_ids);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/FaceType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/FaceType.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace domain {\nstd::ostream& operator<<(std::ostream& os, const FaceType face_type) {\n  switch (face_type) {\n    case FaceType::Uninitialized:\n      return os << \"Uninitialized\";\n    case FaceType::External:\n      return os << \"External\";\n    case FaceType::Topological:\n      return os << \"Topological\";\n    case FaceType::ConformingAligned:\n      return os << \"ConformingAligned\";\n    case FaceType::ConformingUnaligned:\n      return os << \"ConformingUnaligned\";\n    case FaceType::SingleNonconforming:\n      return os << \"SingleNonconforming\";\n    case FaceType::MultipleNonconforming:\n      return os << \"MultipleNonconforming\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"An unknown value of FaceType was passed to the stream operator.\");\n      // LCOV_EXCL_STOP\n  }\n}\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/FaceType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <iosfwd>\n\nnamespace domain {\n\n/// \\brief  The type of face for a Block or Element\n///\n/// \\details For a face with neighboring Blocks (or Elements) the FaceType is\n/// determined by whether the block logical coordinates of the  neighboring\n/// Blocks (or Elements) have conforming block logical coordinates on their\n/// interface. Block logical coordinates are considered to be conforming if\n/// they are identical (i.e. an aligned OrientationMap) or related by a\n/// discrete rotation (i.e. a valid non-aligned OrientationMap).\n///\n/// \\note FaceType::Topological is for the faces of an Element or Block with a\n/// Topology other than I1 (e.g. the angular directions of an S2 topology). The\n/// face is not on the external boundary, and has no neighboring Block or\n/// Element.\nenum class FaceType : uint8_t {\n  /// Used to denote an uninitialized value\n  Uninitialized = 0,\n  /// Used to denote a face on an external boundary\n  External,\n  /// Used to denote a face that is not on the external boundary, and that has\n  /// no neighboring Block or Element (e.g. the angular directions of an S2\n  /// topology).\n  Topological,\n  /// Used to denote a face shared with one or more conforming, aligned\n  /// neighbors\n  ConformingAligned,\n  /// Used to denote a face shared with one or more conforming, unaligned\n  /// neighbors\n  ConformingUnaligned,\n  /// Used to denote a face shared with a single non-conforming neighbor\n  SingleNonconforming,\n  /// Used to denote a face shared with multiple non-conforming neighbors\n  MultipleNonconforming,\n};\n\n/// Output operator for a FaceType.\nstd::ostream& operator<<(std::ostream& os, FaceType face_type);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/HasBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/HasBoundary.hpp\"\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n\nnamespace domain {\nbool has_boundary(const domain::Topology topology, const Side side) {\n  return topology == Topology::I1 or\n         (side == Side::Upper and\n          (topology == Topology::B2Radial or topology == Topology::B3Radial));\n}\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/HasBoundary.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n\n/// \\cond\nnamespace domain {\nenum class Topology : uint8_t;\n}  // namespace domain\nenum class Side : uint8_t;\n/// \\endcond\n\nnamespace domain {\n/// \\brief Whether or not a Topology has a boundary on a given Side\n///\n/// \\note the boundary can either be an internal (i.e. an interface between\n/// neighboring Elements or Blocks) or external boundary\nbool has_boundary(domain::Topology topology, Side side);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/Hypercube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/Hypercube.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <bitset>\n#include <cstddef>\n#include <ostream>\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nHypercubeElement<ElementDim, HypercubeDim>::HypercubeElement(\n    std::array<size_t, ElementDim> dimensions_in_parent,\n    std::array<Side, HypercubeDim - ElementDim> index)\n    : dimensions_in_parent_{std::move(dimensions_in_parent)},\n      index_{std::move(index)} {\n  ASSERT(\n      not std::any_of(dimensions_in_parent_.begin(),\n                      dimensions_in_parent_.end(),\n                      [](const size_t d) { return d >= HypercubeDim; }),\n      \"Found dimension that exceeds the hypercube dimension in construction of \"\n          << HypercubeDim << \"D element: \" << dimensions_in_parent_);\n  if constexpr (ElementDim > 1) {\n    std::sort(dimensions_in_parent_.begin(), dimensions_in_parent_.end());\n    ASSERT(\n        [this]() {\n          for (size_t d = 1; d < ElementDim; ++d) {\n            if (gsl::at(dimensions_in_parent_, d) ==\n                gsl::at(dimensions_in_parent_, d - 1)) {\n              return false;\n            }\n          }\n          return true;\n        }(),\n        \"Found repeated dimension in construction of hypercube element: \"\n            << dimensions_in_parent_ << \" (sorted)\");\n  }\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\ntemplate <size_t LocalElementDim, Requires<LocalElementDim == 0>>\nHypercubeElement<ElementDim, HypercubeDim>::HypercubeElement(\n    std::array<Side, HypercubeDim> index)\n    : index_{std::move(index)} {}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\ntemplate <size_t LocalElementDim, Requires<LocalElementDim == 1>>\nHypercubeElement<ElementDim, HypercubeDim>::HypercubeElement(\n    size_t dim_in_parent, std::array<Side, HypercubeDim - 1> index)\n    : dimensions_in_parent_{dim_in_parent}, index_{std::move(index)} {\n  ASSERT(dim_in_parent < HypercubeDim,\n         \"Dimension \" << dim_in_parent << \" exceeds hypercube dimension in \"\n                      << HypercubeDim << \"D\");\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nconst std::array<size_t, ElementDim>&\nHypercubeElement<ElementDim, HypercubeDim>::dimensions_in_parent() const {\n  return dimensions_in_parent_;\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\ntemplate <size_t LocalElementDim, Requires<LocalElementDim == 1>>\nsize_t HypercubeElement<ElementDim, HypercubeDim>::dimension_in_parent() const {\n  return dimensions_in_parent_[0];\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nconst std::array<Side, HypercubeDim - ElementDim>&\nHypercubeElement<ElementDim, HypercubeDim>::index() const {\n  return index_;\n}\n\n// GCC warns that some instantiations (ElementDim == HypercubeDim) never return\n// because the gsl::at(index_, d) always throws if the size of index_ is 0. The\n// general template cannot be marked [[noreturn]] since other instantiations\n// do return.\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif\ntemplate <size_t ElementDim, size_t HypercubeDim>\nconst Side&\nHypercubeElement<ElementDim, HypercubeDim>::side_in_parent_dimension(\n    size_t d) const {\n  ASSERT(not std::any_of(\n             dimensions_in_parent_.begin(), dimensions_in_parent_.end(),\n             [&d](const size_t dim_in_parent) { return dim_in_parent == d; }),\n         \"The parent dimension \"\n             << d << \" is aligned with the hypercube element '\" << *this\n             << \"', so the element is not located at a particular side in this \"\n                \"dimension.\");\n  for (size_t dim_in_element = ElementDim; dim_in_element > 0;\n       --dim_in_element) {\n    if (gsl::at(dimensions_in_parent_, dim_in_element - 1) <= d) {\n      --d;\n    }\n  }\n  return gsl::at(index_, d);\n}\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nstd::ostream& operator<<(\n    std::ostream& os,\n    const HypercubeElement<ElementDim, HypercubeDim>& element) {\n  if constexpr (ElementDim == 0) {\n    os << \"Vertex\";\n  } else if constexpr (ElementDim == 1) {\n    os << \"Edge\";\n  } else if constexpr (ElementDim == 2) {\n    os << \"Face\";\n  } else if constexpr (ElementDim == 3) {\n    os << \"Cell\";\n  } else {\n    os << ElementDim << \"-face\";\n  }\n  return os << HypercubeDim << \"D[\" << element.dimensions_in_parent() << \",\"\n            << element.index() << \"]\";\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>::HypercubeElementsIterator()\n    : index_{0} {\n  if constexpr (ElementDim > 0) {\n    for (size_t d = 0; d < ElementDim; ++d) {\n      gsl::at(dimensions_in_parent_, d) = d;\n    }\n  }\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>::begin() {\n  return {};\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>::end() {\n  HypercubeElementsIterator end_iterator{};\n  if constexpr (ElementDim > 0) {\n    for (size_t d = 0; d < ElementDim; ++d) {\n      gsl::at(end_iterator.dimensions_in_parent_, d) = num_indices + d + 1;\n    }\n  } else {\n    end_iterator.index_ = two_to_the(num_indices);\n  }\n  return end_iterator;\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\ntemplate <size_t LocalElementDim, Requires<(LocalElementDim > 0)>>\nvoid HypercubeElementsIterator<\n    ElementDim, HypercubeDim>::increment_dimension_in_parent(const size_t d) {\n  ++gsl::at(dimensions_in_parent_, d);\n  if (gsl::at(dimensions_in_parent_, d) == num_indices + d + 1 and d > 0) {\n    increment_dimension_in_parent(d - 1);\n    gsl::at(dimensions_in_parent_, d) =\n        gsl::at(dimensions_in_parent_, d - 1) + 1;\n  }\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>&\nHypercubeElementsIterator<ElementDim, HypercubeDim>::operator++() {\n  ++index_;\n  if constexpr (ElementDim > 0) {\n    if (index_ == two_to_the(num_indices)) {\n      index_ = 0;\n      increment_dimension_in_parent(ElementDim - 1);\n    }\n  }\n  return *this;\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>::operator++(int) {\n  const auto ret = *this;\n  operator++();\n  return ret;\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nHypercubeElement<ElementDim, HypercubeDim>\nHypercubeElementsIterator<ElementDim, HypercubeDim>::operator*() const {\n  const std::bitset<num_indices> index_bits{index_};\n  std::array<Side, num_indices> sides{};\n  for (size_t d = 0; d < num_indices; ++d) {\n    gsl::at(sides, d) = index_bits[d] ? Side::Upper : Side::Lower;\n  }\n  return HypercubeElement<ElementDim, HypercubeDim>{dimensions_in_parent_,\n                                                    std::move(sides)};\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nbool operator==(\n    const HypercubeElementsIterator<ElementDim, HypercubeDim>& lhs,\n    const HypercubeElementsIterator<ElementDim, HypercubeDim>& rhs) {\n  return lhs.dimensions_in_parent_ == rhs.dimensions_in_parent_ and\n         lhs.index_ == rhs.index_;\n}\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nbool operator!=(\n    const HypercubeElementsIterator<ElementDim, HypercubeDim>& lhs,\n    const HypercubeElementsIterator<ElementDim, HypercubeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(ELEMENT_DIM, HYPERCUBE_DIM)                          \\\n  template struct HypercubeElement<ELEMENT_DIM, HYPERCUBE_DIM>;          \\\n  template std::ostream& operator<<(                                     \\\n      std::ostream& os,                                                  \\\n      const HypercubeElement<ELEMENT_DIM, HYPERCUBE_DIM>& element);      \\\n  template struct HypercubeElementsIterator<ELEMENT_DIM, HYPERCUBE_DIM>; \\\n  template bool operator==(                                              \\\n      const HypercubeElementsIterator<ELEMENT_DIM, HYPERCUBE_DIM>& lhs,  \\\n      const HypercubeElementsIterator<ELEMENT_DIM, HYPERCUBE_DIM>& rhs); \\\n  template bool operator!=(                                              \\\n      const HypercubeElementsIterator<ELEMENT_DIM, HYPERCUBE_DIM>& lhs,  \\\n      const HypercubeElementsIterator<ELEMENT_DIM, HYPERCUBE_DIM>& rhs);\n#define INSTANTIATE_VERTEX(r, data)                          \\\n  INSTANTIATE(0, DIM(data))                                  \\\n  template HypercubeElement<0, DIM(data)>::HypercubeElement( \\\n      std::array<Side, DIM(data)>);\n#define INSTANTIATE_EDGE(r, data)                            \\\n  INSTANTIATE(1, DIM(data))                                  \\\n  template HypercubeElement<1, DIM(data)>::HypercubeElement( \\\n      size_t, std::array<Side, DIM(data) - 1>);              \\\n  template size_t HypercubeElement<1, DIM(data)>::dimension_in_parent() const;\n#define INSTANTIATE_FACE(r, data) INSTANTIATE(2, DIM(data))\n#define INSTANTIATE_CELL(r, data) INSTANTIATE(3, DIM(data))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VERTEX, (0, 1, 2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATE_EDGE, (1, 2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATE_FACE, (2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATE_CELL, (3))\n\n#undef DIM\n#undef INSTANTIATE\n#undef INSTANTIATE_VERTEX\n#undef INSTANTIATE_EDGE\n#undef INSTANTIATE_FACE\n#undef INSTANTIATE_CELL\n"
  },
  {
    "path": "src/Domain/Structure/Hypercube.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <ostream>\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\n/*!\n * \\brief An element of dimension `ElementDim` on the boundary of a hypercube of\n * dimension `HypercubeDim`\n *\n * A hypercube of dimension \\f$n\\f$ (`HypercubeDim`) is composed of\n * \\f$2^{n-k}\\binom{n}{k}\\f$ elements of dimension \\f$k \\leq n\\f$\n * (`ElementDim`). For example, a 3D cube has 8 vertices (\\f$k=0\\f$), 12 edges\n * (\\f$k=1\\f$), 6 faces (\\f$k=2\\f$) and 1 cell (\\f$k=3\\f$). Each element is\n * identified by the \\f$k\\f$ dimensions it shares with the parent hypercube\n * and \\f$n - k\\f$ indices that specify whether it is located on the lower or\n * upper side of the parent hypercube's remaining dimensions.\n */\ntemplate <size_t ElementDim, size_t HypercubeDim>\nstruct HypercubeElement {\n  static_assert(ElementDim <= HypercubeDim);\n\n  HypercubeElement(std::array<size_t, ElementDim> dimensions_in_parent,\n                   std::array<Side, HypercubeDim - ElementDim> index);\n\n  template <size_t NumIndices = HypercubeDim - ElementDim,\n            Requires<NumIndices == 0> = nullptr>\n  HypercubeElement() {\n    for (size_t d = 0; d < ElementDim; ++d) {\n      gsl::at(dimensions_in_parent_, d) = d;\n    }\n  }\n\n  template <size_t LocalElementDim = ElementDim,\n            Requires<LocalElementDim == 0> = nullptr>\n  explicit HypercubeElement(std::array<Side, HypercubeDim> index);\n\n  template <typename... Indices, size_t LocalElementDim = ElementDim,\n            size_t LocalHypercubeDim = HypercubeDim,\n            Requires<(LocalElementDim == 0 and LocalHypercubeDim > 0 and\n                      sizeof...(Indices) == LocalHypercubeDim)> = nullptr>\n  explicit HypercubeElement(Indices... indices)\n      : index_{{static_cast<Side>(indices)...}} {}\n\n  template <size_t LocalElementDim = ElementDim,\n            Requires<LocalElementDim == 1> = nullptr>\n  HypercubeElement(size_t dim_in_parent,\n                   std::array<Side, HypercubeDim - 1> index);\n\n  /// @{\n  /// The parent hypercube's dimensions that this element shares\n  const std::array<size_t, ElementDim>& dimensions_in_parent() const;\n\n  template <size_t LocalElementDim = ElementDim,\n            Requires<LocalElementDim == 1> = nullptr>\n  size_t dimension_in_parent() const;\n  /// @}\n\n  /// @{\n  /// Whether this element is located on the lower or upper side in those\n  /// dimensions that it does not share with its parent hypercube\n  const std::array<Side, HypercubeDim - ElementDim>& index() const;\n\n  const Side& side_in_parent_dimension(size_t d) const;\n\n  template <size_t NumIndices = HypercubeDim - ElementDim,\n            Requires<NumIndices == 1> = nullptr>\n  const Side& side() const {\n    return index_[0];\n  }\n  /// @}\n\n  bool operator==(const HypercubeElement& rhs) const {\n    return dimensions_in_parent_ == rhs.dimensions_in_parent_ and\n           index_ == rhs.index_;\n  }\n\n  bool operator!=(const HypercubeElement& rhs) const {\n    return not(*this == rhs);\n  }\n\n private:\n  std::array<size_t, ElementDim> dimensions_in_parent_{};\n  std::array<Side, HypercubeDim - ElementDim> index_{};\n};\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nstd::ostream& operator<<(\n    std::ostream& os,\n    const HypercubeElement<ElementDim, HypercubeDim>& element);\n\n/// A vertex in a `Dim`-dimensional hypercube\ntemplate <size_t Dim>\nusing Vertex = HypercubeElement<0, Dim>;\n\n/// An edge in a `Dim`-dimensional hypercube\ntemplate <size_t Dim>\nusing Edge = HypercubeElement<1, Dim>;\n\n/// A face in a `Dim`-dimensional hypercube\ntemplate <size_t Dim>\nusing Face = HypercubeElement<2, Dim>;\n\n/// A cell in a `Dim`-dimensional hypercube\ntemplate <size_t Dim>\nusing Cell = HypercubeElement<3, Dim>;\n\n/*!\n * \\brief Iterator over all `ElementDim`-dimensional elements on the boundary of\n * a `HypercubeDim`-dimensional hypercube.\n *\n * \\see `HypercubeElement`\n */\ntemplate <size_t ElementDim, size_t HypercubeDim>\nstruct HypercubeElementsIterator {\n  static_assert(\n      ElementDim <= HypercubeDim,\n      \"Hypercube element dimension must not exceed hypercube dimension.\");\n\n public:\n  static constexpr size_t num_indices = HypercubeDim - ElementDim;\n\n  /// The number of `ElementDim`-dimensional elements on the boundary of a\n  /// `HypercubeDim`-dimensional hypercube.\n  static constexpr size_t size() {\n    return two_to_the(num_indices) * factorial(HypercubeDim) /\n           factorial(ElementDim) / factorial(num_indices);\n  }\n\n  HypercubeElementsIterator();\n\n  static HypercubeElementsIterator begin();\n\n  static HypercubeElementsIterator end();\n\n  HypercubeElementsIterator& operator++();\n\n  HypercubeElementsIterator operator++(int);\n\n  HypercubeElement<ElementDim, HypercubeDim> operator*() const;\n\n private:\n  template <size_t LocalElementDim = ElementDim,\n            Requires<(LocalElementDim > 0)> = nullptr>\n  void increment_dimension_in_parent(size_t d);\n\n  template <size_t LocalElementDim, size_t LocalHypercubeDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(\n      const HypercubeElementsIterator<LocalElementDim, LocalHypercubeDim>& lhs,\n      const HypercubeElementsIterator<LocalElementDim, LocalHypercubeDim>& rhs);\n\n  std::array<size_t, ElementDim> dimensions_in_parent_{};\n  size_t index_ = std::numeric_limits<size_t>::max();\n};\n\ntemplate <size_t ElementDim, size_t HypercubeDim>\nbool operator!=(const HypercubeElementsIterator<ElementDim, HypercubeDim>& lhs,\n                const HypercubeElementsIterator<ElementDim, HypercubeDim>& rhs);\n\n/// Iterate over all vertices in a `Dim`-dimensional hypercube\ntemplate <size_t Dim>\nusing VertexIterator = HypercubeElementsIterator<0, Dim>;\n\n/// Iterate over all edges in a `Dim`-dimensional hypercube\ntemplate <size_t Dim>\nusing EdgeIterator = HypercubeElementsIterator<1, Dim>;\n\n/// Iterate over all faces in a `Dim`-dimensional hypercube\ntemplate <size_t Dim>\nusing FaceIterator = HypercubeElementsIterator<2, Dim>;\n\n/// Iterate over all cells in a `Dim`-dimensional hypercube\ntemplate <size_t Dim>\nusing CellIterator = HypercubeElementsIterator<3, Dim>;\n"
  },
  {
    "path": "src/Domain/Structure/IndexToSliceAt.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Finds the index in the perpendicular dimension of an element boundary\n///\n/// Optionally provide an `offset` to find an index offset from the element\n/// boundary.\ntemplate <size_t Dim>\nsize_t index_to_slice_at(const Index<Dim>& extents,\n                         const Direction<Dim>& direction,\n                         const size_t offset = 0) {\n  return direction.side() == Side::Lower\n             ? offset\n             : extents[direction.dimension()] - 1 - offset;\n}\n"
  },
  {
    "path": "src/Domain/Structure/InitialElementIds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/InitialElementIds.hpp\"\n\n#include <iterator>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\ntemplate <>\nstd::vector<ElementId<1>> initial_element_ids<1>(\n    const size_t block_id, const std::array<size_t, 1> initial_ref_levs,\n    const size_t grid_index) {\n  std::vector<ElementId<1>> ids;\n  ids.reserve(two_to_the(initial_ref_levs[0]));\n  for (size_t x_i = 0; x_i < two_to_the(initial_ref_levs[0]); ++x_i) {\n    SegmentId x_segment_id(initial_ref_levs[0], x_i);\n    ids.emplace_back(block_id, make_array<1>(x_segment_id), grid_index);\n  }\n  return ids;\n}\n\ntemplate <>\nstd::vector<ElementId<2>> initial_element_ids<2>(\n    const size_t block_id, const std::array<size_t, 2> initial_ref_levs,\n    const size_t grid_index) {\n  std::vector<ElementId<2>> ids;\n  ids.reserve(two_to_the(initial_ref_levs[0]) *\n              two_to_the(initial_ref_levs[1]));\n  for (size_t x_i = 0; x_i < two_to_the(initial_ref_levs[0]); ++x_i) {\n    SegmentId x_segment_id(initial_ref_levs[0], x_i);\n    for (size_t y_i = 0; y_i < two_to_the(initial_ref_levs[1]); ++y_i) {\n      SegmentId y_segment_id(initial_ref_levs[1], y_i);\n      ids.emplace_back(block_id, make_array(x_segment_id, y_segment_id),\n                       grid_index);\n    }\n  }\n  return ids;\n}\n\ntemplate <>\nstd::vector<ElementId<3>> initial_element_ids<3>(\n    const size_t block_id, const std::array<size_t, 3> initial_ref_levs,\n    const size_t grid_index) {\n  std::vector<ElementId<3>> ids;\n  ids.reserve(two_to_the(initial_ref_levs[0]) *\n              two_to_the(initial_ref_levs[1]) *\n              two_to_the(initial_ref_levs[2]));\n  for (size_t x_i = 0; x_i < two_to_the(initial_ref_levs[0]); ++x_i) {\n    SegmentId x_segment_id(initial_ref_levs[0], x_i);\n    for (size_t y_i = 0; y_i < two_to_the(initial_ref_levs[1]); ++y_i) {\n      SegmentId y_segment_id(initial_ref_levs[1], y_i);\n      for (size_t z_i = 0; z_i < two_to_the(initial_ref_levs[2]); ++z_i) {\n        SegmentId z_segment_id(initial_ref_levs[2], z_i);\n        ids.emplace_back(block_id,\n                         make_array(x_segment_id, y_segment_id, z_segment_id),\n                         grid_index);\n      }\n    }\n  }\n  return ids;\n}\n\ntemplate <size_t VolumeDim>\nstd::vector<ElementId<VolumeDim>> initial_element_ids(\n    const std::vector<std::array<size_t, VolumeDim>>& initial_refinement_levels,\n    const size_t grid_index) {\n  std::vector<ElementId<VolumeDim>> element_ids;\n  for (size_t block_id = 0; block_id < initial_refinement_levels.size();\n       ++block_id) {\n    auto ids_for_block = initial_element_ids(\n        block_id, initial_refinement_levels[block_id], grid_index);\n    element_ids.reserve(element_ids.size() + ids_for_block.size());\n    std::move(ids_for_block.begin(), ids_for_block.end(),\n              std::back_inserter(element_ids));\n  }\n  return element_ids;\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                              \\\n  template std::vector<ElementId<GET_DIM(data)>>            \\\n  initial_element_ids<GET_DIM(data)>(                       \\\n      const std::vector<std::array<size_t, GET_DIM(data)>>& \\\n          initial_refinement_levels,                        \\\n      size_t grid_index);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/Structure/InitialElementIds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Create the `ElementId`s of the a single Block\ntemplate <size_t VolumeDim>\nstd::vector<ElementId<VolumeDim>> initial_element_ids(\n    size_t block_id, std::array<size_t, VolumeDim> initial_ref_levs,\n    size_t grid_index = 0);\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Create the `ElementId`s of the initial computational domain.\ntemplate <size_t VolumeDim>\nstd::vector<ElementId<VolumeDim>> initial_element_ids(\n    const std::vector<std::array<size_t, VolumeDim>>& initial_refinement_levels,\n    size_t grid_index = 0);\n"
  },
  {
    "path": "src/Domain/Structure/MaxNumberOfNeighbors.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n\n/// \\ingroup ComputationalDomainGroup\n/// Returns the maximum number of neighbors an element can have in `dim`\n/// dimensions.\n///\n/// \\note Assumes a maximum 2-to-1 refinement between two adjacent Elements.\nconstexpr size_t maximum_number_of_neighbors(const size_t dim) {\n  switch (dim) {\n    case 1:\n      return 2;\n    case 2:\n      return 8;\n    case 3:\n      return 24;\n    default:\n      // need to throw because we cannot ERROR in constexpr\n      throw \"Invalid dim specified\";\n  };\n}\n\n/// \\ingroup ComputationalDomainGroup\n/// Returns the maximum number of neighbors in each direction an element can\n/// have in `dim` dimensions.\n///\n/// \\note Assumes a maximum 2-to-1 refinement between two adjacent Elements.\nconstexpr size_t maximum_number_of_neighbors_per_direction(const size_t dim) {\n  switch (dim) {\n    case 0:\n      return 0;\n    default:\n      return two_to_the(dim - 1);\n  };\n}\n"
  },
  {
    "path": "src/Domain/Structure/NeighborIsConforming.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/NeighborIsConforming.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/HasBoundary.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nstd::array<domain::Topology, VolumeDim - 1> boundary_topologies(\n    const std::array<domain::Topology, VolumeDim>& topologies,\n    const Direction<VolumeDim>& direction) {\n  const size_t dim = direction.dimension();\n  ASSERT(domain::has_boundary(gsl::at(topologies, dim), direction.side()),\n         \"There is no boundary for topologies \"\n             << topologies << \" in the direction \" << direction);\n  auto result = all_but_specified_element_of(topologies, dim);\n  if (gsl::at(topologies, dim) == domain::Topology::B2Radial) {\n    for (size_t d = 0; d < VolumeDim - 1; ++d) {\n      if (gsl::at(result, d) == domain::Topology::B2Angular) {\n        gsl::at(result, d) = domain::Topology::S1;\n      }\n    }\n  }\n  if (gsl::at(topologies, dim) == domain::Topology::B3Radial) {\n    for (size_t d = 0; d < VolumeDim - 1; ++d) {\n      if (gsl::at(result, d) == domain::Topology::B3Colatitude) {\n        gsl::at(result, d) = domain::Topology::S2Colatitude;\n      }\n      if (gsl::at(result, d) == domain::Topology::B3Longitude) {\n        gsl::at(result, d) = domain::Topology::S2Longitude;\n      }\n    }\n  }\n  return result;\n}\n}  // namespace\n\nnamespace domain {\ntemplate <size_t VolumeDim>\nbool neighbor_is_conforming(\n    const std::array<Topology, VolumeDim>& self_topologies,\n    const std::array<Topology, VolumeDim>& neighbor_topologies,\n    const Direction<VolumeDim>& direction_to_neighbor,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor) {\n  if constexpr (VolumeDim > 1) {\n    const auto self_boundary_topologies =\n        boundary_topologies(self_topologies, direction_to_neighbor);\n    if (orientation_of_neighbor.is_aligned()) {\n      const auto neighbor_boundary_topologies = boundary_topologies(\n          neighbor_topologies, direction_to_neighbor.opposite());\n      return self_boundary_topologies == neighbor_boundary_topologies;\n    } else {\n      for (size_t d = 0; d < VolumeDim; ++d) {\n        if (d == direction_to_neighbor.dimension()) {\n          continue;\n        }\n        if (orientation_of_neighbor(Direction<VolumeDim>{d, Side::Upper}) ==\n            Direction<VolumeDim>::self()) {\n          return false;\n        }\n      }\n      const auto neighbor_boundary_topologies = boundary_topologies(\n          orientation_of_neighbor.permute_from_neighbor(neighbor_topologies),\n          direction_to_neighbor);\n      return self_boundary_topologies == neighbor_boundary_topologies;\n    }\n  }\n  return true;\n}\n\ntemplate bool neighbor_is_conforming(\n    const std::array<Topology, 1>& self_topologies,\n    const std::array<Topology, 1>& neighbor_topologies,\n    const Direction<1>& direction_to_neighbor,\n    const OrientationMap<1>& orientation_of_neighbor);\ntemplate bool neighbor_is_conforming(\n    const std::array<Topology, 2>& self_topologies,\n    const std::array<Topology, 2>& neighbor_topologies,\n    const Direction<2>& direction_to_neighbor,\n    const OrientationMap<2>& orientation_of_neighbor);\ntemplate bool neighbor_is_conforming(\n    const std::array<Topology, 3>& self_topologies,\n    const std::array<Topology, 3>& neighbor_topologies,\n    const Direction<3>& direction_to_neighbor,\n    const OrientationMap<3>& orientation_of_neighbor);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/NeighborIsConforming.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Direction;\ntemplate <size_t VolumeDim>\nclass OrientationMap;\nnamespace domain {\nenum class Topology : uint8_t;\n}  // namespace domain\n/// \\endcond\n\nnamespace domain {\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Returns whether or not neighboring Blocks (or Elements) have\n/// conforming block logical coordinates on their interface\n///\n/// \\details Block logical coordinates are considered to be conforming if they\n/// are identical or related by a discrete rotation (i.e. a valid non-aligned\n/// OrientationMap).  It is a requirement that neighboring Blocks be conforming\n/// if their oriented topologies are the same in the interface dimensions.\n///\n/// \\note If neighboring Elements are conforming, they can exchange boundary\n/// data via either copy (if they have the same h- and p-refinement) or\n/// projection (if they don't), taking into account the discrete rotation if\n/// necessary. If the neighbors are not conforming, boundary data will need to\n/// be interpolated.\ntemplate <size_t VolumeDim>\nbool neighbor_is_conforming(\n    const std::array<Topology, VolumeDim>& self_topologies,\n    const std::array<Topology, VolumeDim>& neighbor_topologies,\n    const Direction<VolumeDim>& direction_to_neighbor,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/Neighbors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/Neighbors.hpp\"\n\n#include <algorithm>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nbool ids_and_orientations_are_consistent(\n    const std::unordered_set<size_t>& block_ids,\n    const std::unordered_map<size_t, OrientationMap<VolumeDim>>& orientations) {\n  return alg::none_of(block_ids, [&orientations](size_t block_id) {\n    return orientations.count(block_id) == 0;\n  });\n}\n\ntemplate <size_t VolumeDim>\nbool ids_and_orientations_are_consistent(\n    const std::unordered_set<ElementId<VolumeDim>>& element_ids,\n    const std::unordered_map<size_t, OrientationMap<VolumeDim>>& orientations) {\n  return alg::none_of(element_ids,\n                      [&orientations](const ElementId<VolumeDim>& element_id) {\n                        return orientations.count(element_id.block_id()) == 0;\n                      });\n}\n}  // namespace\n\ntemplate <size_t VolumeDim, typename IdType>\nNeighbors<VolumeDim, IdType>::Neighbors(\n    std::unordered_set<IdType> ids,\n    std::unordered_map<size_t, OrientationMap<VolumeDim>> orientations,\n    const bool are_conforming)\n    : ids_(std::move(ids)),\n      orientations_(std::move(orientations)),\n      are_conforming_(are_conforming) {\n  ASSERT(alg::none_of(orientations_,\n                      [](const auto& orientation) {\n                        return orientation.second ==\n                               OrientationMap<VolumeDim>{};\n                      }),\n         \"Cannot use a default-constructed OrientationMap in Neighbors.\");\n  ASSERT(orientations_.size() == 1 or not are_conforming_,\n         \"Conforming neighbors must all be in the same block, but multiple \"\n         \"orientations were specified \"\n             << orientations_);\n  ASSERT(ids_and_orientations_are_consistent(ids_, orientations_),\n         \"Not all Ids \" << ids_ << \" have orientations \" << orientations_);\n}\n\ntemplate <size_t VolumeDim, typename IdType>\nNeighbors<VolumeDim, IdType>::Neighbors(std::unordered_set<IdType> ids,\n                                        OrientationMap<VolumeDim> orientation)\n    : ids_(std::move(ids)) {\n  ASSERT(orientation != OrientationMap<VolumeDim>{},\n         \"Cannot use a default-constructed OrientationMap in Neighbors.\");\n  if constexpr (std::is_same_v<size_t, IdType>) {\n    orientations_.emplace(*ids_.begin(), std::move(orientation));\n  } else {\n    orientations_.emplace(ids_.begin()->block_id(), std::move(orientation));\n  }\n  ASSERT(ids_and_orientations_are_consistent(ids_, orientations_),\n         \"Conforming neighbors must all be in the same block, but the \"\n         \"following Ids were specified: \"\n             << ids_);\n}\n\ntemplate <size_t VolumeDim, typename IdType>\nNeighbors<VolumeDim, IdType>::Neighbors(const IdType id,\n                                        OrientationMap<VolumeDim> orientation)\n    : Neighbors(std::unordered_set{std::move(id)}, std::move(orientation)) {}\n\ntemplate <size_t VolumeDim, typename IdType>\nconst OrientationMap<VolumeDim>& Neighbors<VolumeDim, IdType>::orientation(\n    const IdType& id) const {\n  if constexpr (std::is_same_v<size_t, IdType>) {\n    return orientations_.at(id);\n  } else {\n    return orientations_.at(id.block_id());\n  }\n}\n\ntemplate <size_t VolumeDim, typename IdType>\nvoid Neighbors<VolumeDim, IdType>::set_ids_to(\n    const std::unordered_set<IdType> new_ids) {\n  ids_ = std::move(new_ids);\n  ASSERT(ids_and_orientations_are_consistent(ids_, orientations_),\n         \"Some of the Ids \" << ids_\n                            << \"passed to set_ids_to are from different blocks \"\n                               \"than the current orientations \"\n                            << orientations_);\n}\n\ntemplate <size_t VolumeDim, typename IdType>\nvoid Neighbors<VolumeDim, IdType>::add_ids(\n    std::unordered_set<IdType> additional_ids) {\n  ids_.merge(additional_ids);\n  ASSERT(ids_and_orientations_are_consistent(ids_, orientations_),\n         \"Some of the added Ids \"\n             << additional_ids\n             << \" are from different blocks than the current orientations \"\n             << orientations_);\n}\n\ntemplate <size_t VolumeDim, typename IdType>\nstd::ostream& operator<<(std::ostream& os,\n                         const Neighbors<VolumeDim, IdType>& neighbors) {\n  os << \"Ids = \" << neighbors.ids()\n     << \"; orientations = \" << neighbors.orientations()\n     << \"; conforming = \" << std::boolalpha << neighbors.are_conforming();\n  return os;\n}\n\ntemplate <size_t VolumeDim, typename IdType>\nbool operator==(const Neighbors<VolumeDim, IdType>& lhs,\n                const Neighbors<VolumeDim, IdType>& rhs) {\n  return (lhs.ids() == rhs.ids() and\n          lhs.orientations() == rhs.orientations() and\n          lhs.are_conforming() == rhs.are_conforming());\n}\n\ntemplate <size_t VolumeDim, typename IdType>\nbool operator!=(const Neighbors<VolumeDim, IdType>& lhs,\n                const Neighbors<VolumeDim, IdType>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t VolumeDim, typename IdType>\nvoid Neighbors<VolumeDim, IdType>::pup(PUP::er& p) {\n  if constexpr (std::is_same_v<IdType, size_t>) {\n    size_t version = 2;\n    p | version;\n    if (version == 0) {\n      // Deserialize old BlockNeighbor class\n      size_t id = 0;\n      p | id;\n      ids_.clear();\n      ids_.emplace(id);\n      OrientationMap<VolumeDim> orientation;\n      p | orientation;\n      orientations_.clear();\n      orientations_.emplace(id, orientation);\n      are_conforming_ = true;\n    } else if (version == 1) {\n      p | ids_;\n      OrientationMap<VolumeDim> orientation;\n      p | orientation;\n      orientations_.clear();\n      orientations_.emplace(*(ids_.begin()), orientation);\n      are_conforming_ = true;\n    } else {\n      ASSERT(version == 2, \"Unknonwn version \" << version);\n      p | ids_;\n      p | orientations_;\n      p | are_conforming_;\n    }\n  } else {\n    p | ids_;\n    p | orientations_;\n    p | are_conforming_;\n  }\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template class Neighbors<GET_DIM(data), size_t>;                          \\\n  template std::ostream& operator<<(                                        \\\n      std::ostream& os, const Neighbors<GET_DIM(data), size_t>& neighbors); \\\n  template bool operator==(const Neighbors<GET_DIM(data), size_t>& lhs,     \\\n                           const Neighbors<GET_DIM(data), size_t>& rhs);    \\\n  template bool operator!=(const Neighbors<GET_DIM(data), size_t>& lhs,     \\\n                           const Neighbors<GET_DIM(data), size_t>& rhs);    \\\n  template class Neighbors<GET_DIM(data)>;                                  \\\n  template std::ostream& operator<<(                                        \\\n      std::ostream& os, const Neighbors<GET_DIM(data)>& neighbors);         \\\n  template bool operator==(const Neighbors<GET_DIM(data)>& lhs,             \\\n                           const Neighbors<GET_DIM(data)>& rhs);            \\\n  template bool operator!=(const Neighbors<GET_DIM(data)>& lhs,             \\\n                           const Neighbors<GET_DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Domain/Structure/Neighbors.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <type_traits>\n#include <unordered_set>\n\n#include \"Domain/Structure/OrientationMap.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\n/// \\ingroup ComputationalDomainGroup\n/// Information about the neighbors of a host Element or Block in a particular\n/// direction.\n///\n/// \\tparam VolumeDim the volume dimension.\n/// \\tparam IdType the type of the Id of the neighbor (either ElementId or\n/// size_t for a Block)\ntemplate <size_t VolumeDim, typename IdType = ElementId<VolumeDim>>\nclass Neighbors {\n  static_assert(std::is_same_v<IdType, size_t> or\n                std::is_same_v<IdType, ElementId<VolumeDim>>);\n\n public:\n  /// Construct with the ids, the orientation of the neighbors relative to the\n  /// host, and whether the neighbors are conforming.\n  ///\n  /// \\param ids the ids of the neighbors.\n  /// \\param orientations An OrientationMap (which takes objects in the logical\n  /// coordinate frame of the host and maps them to the logical coordinate frame\n  /// of the neighbor) for each neighboring Block (Elements within a Block share\n  /// the same orientation).  The key of the unordered map is the Block ID.\n  /// \\param are_conforming whether or not the block logical coordinates of the\n  /// neighbors conform to those of the host (see\n  /// domain::neighbor_is_conforming)\n  Neighbors(std::unordered_set<IdType> ids,\n            std::unordered_map<size_t, OrientationMap<VolumeDim>> orientations,\n            bool are_conforming);\n\n  /// Construct with the ids and orientation of the neighbors relative to the\n  /// host, assuming the neighbors are conforming.\n  ///\n  /// \\param ids the ids of the neighbors.\n  /// \\param orientation This OrientationMap takes objects in the logical\n  /// coordinate frame of the host and maps them to the logical coordinate frame\n  /// of the neighbor.\n  Neighbors(std::unordered_set<IdType> ids,\n            OrientationMap<VolumeDim> orientation);\n\n  /// Construct with the id and orientation of a single neighbor relative to the\n  /// host, assuming the neighbor is conforming.\n  ///\n  /// \\param id the id of the neighbors.\n  /// \\param orientation This OrientationMap takes objects in the logical\n  /// coordinate frame of the host Element and maps them to the logical\n  /// coordinate frame of the neighbor Element.\n  Neighbors(IdType id, OrientationMap<VolumeDim> orientation);\n\n  /// Default constructor for Charm++ serialization.\n  Neighbors() = default;\n  ~Neighbors() = default;\n  Neighbors(const Neighbors& neighbor) = default;\n  Neighbors(Neighbors&&) = default;\n  Neighbors& operator=(const Neighbors& rhs) = default;\n  Neighbors& operator=(Neighbors&&) = default;\n\n  const std::unordered_set<IdType>& ids() const { return ids_; }\n\n  /// The orientations of the neighbors for each neighboring Block.\n  ///\n  /// \\note All Elements within a Block share the same orientation.\n  const std::unordered_map<size_t, OrientationMap<VolumeDim>>& orientations()\n      const {\n    return orientations_;\n  }\n\n  /// Whether or not the block logical coordinates of the neighbors conform to\n  /// those of the host (see domain::neighbor_is_conforming)\n  bool are_conforming() const { return are_conforming_; }\n\n  /// The orientation of a particular neighbor.\n  const OrientationMap<VolumeDim>& orientation(const IdType& id) const;\n\n  /// Reset the ids of the neighbors.\n  ///\n  /// \\note This should only be called to reset Element Neighbors after\n  /// h-refinement\n  void set_ids_to(std::unordered_set<IdType> new_ids);\n\n  /// Add ids of neighbors.\n  ///\n  /// \\note Adding an existing neighbor is allowed.\n  /// \\note The additional ids must be from Blocks with the existing\n  /// orientations.\n  void add_ids(std::unordered_set<IdType> additional_ids);\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// The number of neighbors\n  size_t size() const { return ids_.size(); }\n\n  typename std::unordered_set<IdType>::iterator begin() { return ids_.begin(); }\n\n  typename std::unordered_set<IdType>::iterator end() { return ids_.end(); }\n\n  typename std::unordered_set<IdType>::const_iterator begin() const {\n    return ids_.begin();\n  }\n\n  typename std::unordered_set<IdType>::const_iterator end() const {\n    return ids_.end();\n  }\n\n  typename std::unordered_set<IdType>::const_iterator cbegin() const {\n    return ids_.begin();\n  }\n\n  typename std::unordered_set<IdType>::const_iterator cend() const {\n    return ids_.end();\n  }\n\n private:\n  std::unordered_set<IdType> ids_{};\n  std::unordered_map<size_t, OrientationMap<VolumeDim>> orientations_{};\n  bool are_conforming_{true};\n};\n\n/// Output operator for Neighbors.\ntemplate <size_t VolumeDim, typename IdType>\nstd::ostream& operator<<(std::ostream& os,\n                         const Neighbors<VolumeDim, IdType>& neighbors);\n\ntemplate <size_t VolumeDim, typename IdType>\nbool operator==(const Neighbors<VolumeDim, IdType>& lhs,\n                const Neighbors<VolumeDim, IdType>& rhs);\n\ntemplate <size_t VolumeDim, typename IdType>\nbool operator!=(const Neighbors<VolumeDim, IdType>& lhs,\n                const Neighbors<VolumeDim, IdType>& rhs);\n"
  },
  {
    "path": "src/Domain/Structure/ObjectLabel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/ObjectLabel.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace domain {\nstd::string name(const ObjectLabel x) {\n  if (x == ObjectLabel::A) {\n    return \"A\"s;\n  } else if (x == ObjectLabel::B) {\n    return \"B\"s;\n  } else if (x == ObjectLabel::C) {\n    return \"C\"s;\n  } else if (x == ObjectLabel::None) {\n    return \"\"s;\n  } else {\n    ERROR(\"Unknown object label!\");\n  }\n}\n\nstd::ostream& operator<<(std::ostream& s, const ObjectLabel x) {\n  return s << name(x);\n}\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/ObjectLabel.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n#include <string>\n\nnamespace domain {\n/// Labels for the objects in a binary system.\nenum class ObjectLabel {\n  /// The object along the positive x-axis in the grid frame\n  A,\n  /// The object along the negative x-axis in the grid frame\n  B,\n  /// A third object, typically centered at the origin.\n  C,\n  /// This object has no label\n  None\n};\n\nstd::string name(const ObjectLabel x);\n\nstd::ostream& operator<<(std::ostream& s, const ObjectLabel x);\n\n/// \\brief Similar to a `tmpl::list` but for `ObjectLabel`s.\ntemplate <ObjectLabel... Objects>\nstruct object_list {};\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/OrientationMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/OrientationMap.hpp\"\n\n#include <functional>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#ifdef SPECTRE_AUTODIFF\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#endif\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nstd::set<size_t> set_of_dimensions(\n    const std::array<Direction<VolumeDim>, VolumeDim>& directions) {\n  std::set<size_t> set_of_dims;\n  for (size_t j = 0; j < VolumeDim; j++) {\n    if (gsl::at(directions, j) != Direction<VolumeDim>::self()) {\n      set_of_dims.insert(gsl::at(directions, j).dimension());\n    }\n  }\n  return set_of_dims;\n}\n}  // namespace\n\ntemplate <size_t VolumeDim>\nOrientationMap<VolumeDim>::OrientationMap() = default;\n\ntemplate <size_t VolumeDim>\nOrientationMap<VolumeDim> OrientationMap<VolumeDim>::create_aligned() {\n  OrientationMap<VolumeDim> result{};\n  for (size_t j = 0; j < VolumeDim; j++) {\n    result.set_direction(j, Direction<VolumeDim>(j, Side::Upper));\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nOrientationMap<VolumeDim>::OrientationMap(\n    std::array<Direction<VolumeDim>, VolumeDim> mapped_directions) {\n  for (size_t j = 0; j < VolumeDim; j++) {\n    set_direction(j, gsl::at(mapped_directions, j));\n  }\n  for (size_t j = 0; j < VolumeDim; j++) {\n    if (const auto dir = get_direction(j);\n        dir.dimension() != j or dir.side() != Side::Upper) {\n      set_aligned(false);\n    }\n  }\n  ASSERT(static_cast<size_t>(\n             alg::count(mapped_directions, Direction<VolumeDim>::self())) +\n                 set_of_dimensions().size() ==\n             VolumeDim,\n         \"This OrientationMap fails to map Directions one-to-one.\\n\"\n         \"Mapped directions = \"\n             << mapped_directions);\n}\n\ntemplate <size_t VolumeDim>\nOrientationMap<VolumeDim>::OrientationMap(\n    const std::array<Direction<VolumeDim>, VolumeDim>& directions_in_host,\n    const std::array<Direction<VolumeDim>, VolumeDim>& directions_in_neighbor) {\n  for (size_t j = 0; j < VolumeDim; j++) {\n    set_direction(gsl::at(directions_in_host, j).dimension(),\n                  gsl::at(directions_in_host, j).side() == Side::Upper\n                      ? gsl::at(directions_in_neighbor, j)\n                      : gsl::at(directions_in_neighbor, j).opposite());\n    if (gsl::at(directions_in_host, j) != gsl::at(directions_in_neighbor, j)) {\n      set_aligned(false);\n    }\n  }\n  ASSERT(::set_of_dimensions(directions_in_host).size() == VolumeDim,\n         \"This OrientationMap fails to map Directions one-to-one.\"\n         \"directions_in_host = \"\n             << directions_in_host);\n  ASSERT(::set_of_dimensions(directions_in_neighbor).size() +\n                 static_cast<size_t>(alg::count(\n                     directions_in_neighbor, Direction<VolumeDim>::self())) ==\n             VolumeDim,\n         \"This OrientationMap fails to map Directions one-to-one.\"\n         \"directions_in_neighbor = \"\n             << directions_in_neighbor);\n}\n\ntemplate <size_t VolumeDim>\nstd::array<SegmentId, VolumeDim> OrientationMap<VolumeDim>::operator()(\n    const std::array<SegmentId, VolumeDim>& segmentIds) const {\n  ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15),\n         \"Cannot use a default-constructed OrientationMap\");\n  std::array<SegmentId, VolumeDim> result = segmentIds;\n  for (size_t d = 0; d < VolumeDim; d++) {\n    const auto neighbor_direction = get_direction(d);\n    ASSERT(neighbor_direction.side() != Side::Self,\n           \"Cannot re-orient all SegmentIds for this Orientation\");\n    gsl::at(result, neighbor_direction.dimension()) =\n        get_direction(d).side() == Side::Upper\n            ? gsl::at(segmentIds, d)\n            : gsl::at(segmentIds, d).id_if_flipped();\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nMesh<VolumeDim> OrientationMap<VolumeDim>::operator()(\n    const Mesh<VolumeDim>& mesh) const {\n  ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15),\n         \"Cannot use a default-constructed OrientationMap\");\n  return Mesh<VolumeDim>(this->permute_to_neighbor(mesh.extents().indices()),\n                         this->permute_to_neighbor(mesh.basis()),\n                         this->permute_to_neighbor(mesh.quadrature()));\n}\n\ntemplate <size_t VolumeDim>\nOrientationMap<VolumeDim> OrientationMap<VolumeDim>::inverse_map() const {\n  ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15),\n         \"Cannot use a default-constructed OrientationMap\");\n  auto result = make_array<VolumeDim>(Direction<VolumeDim>::self());\n  for (size_t i = 0; i < VolumeDim; i++) {\n    if (get_direction(i).side() == Side::Self) {\n      continue;\n    }\n    gsl::at(result, get_direction(i).dimension()) =\n        Direction<VolumeDim>(i, get_direction(i).side());\n  }\n  return OrientationMap<VolumeDim>{result};\n}\n\ntemplate <size_t VolumeDim>\nvoid OrientationMap<VolumeDim>::pup(PUP::er& p) {\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  const uint16_t version = 1;\n  if (p.isUnpacking()) {\n    p | bit_field_;\n    if (LIKELY(bit_field_ != 0)) {\n      // Zero out the version bits.\n      bit_field_ = bit_field_ bitand 0b1000111111111111;\n    } else {\n      // We deserialize version 0\n      p | bit_field_;\n      uint32_t read_version = 0;\n      p | read_version;\n      if (read_version != 0) {\n        ERROR(\"Expected to deserialize version 0 of OrientationMap but got \"\n              << read_version);\n      }\n      bit_field_ = 0;\n      std::array<Direction<VolumeDim>, VolumeDim> mapped_directions{};\n      bool is_aligned = false;\n      p | mapped_directions;\n      for (size_t i = 0; i < VolumeDim; ++i) {\n        set_direction(i, gsl::at(mapped_directions, i));\n      }\n      p | is_aligned;\n      if (is_aligned) {\n        bit_field_ = bit_field_ bitor aligned_mask;\n      }\n    }\n  } else {\n    uint16_t bit_field = bit_field_ bitor (version << 12);\n    p | bit_field;\n  }\n}\n\ntemplate <size_t VolumeDim>\nDirection<VolumeDim> OrientationMap<VolumeDim>::get_direction(\n    const size_t dim) const {\n  ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15),\n         \"Cannot use a default-constructed OrientationMap\");\n  const uint16_t direction_mask =\n      Direction<VolumeDim>::all_mask\n      << (dim * Direction<VolumeDim>::number_of_bits);\n  const uint16_t direction_bits = (bit_field_ bitand direction_mask) >>\n                                  (dim * Direction<VolumeDim>::number_of_bits);\n  return Direction<VolumeDim>{\n      static_cast<typename Direction<VolumeDim>::Axis>(\n          direction_bits bitand Direction<VolumeDim>::axis_mask),\n      static_cast<Side>(direction_bits bitand Direction<VolumeDim>::side_mask)};\n}\n\ntemplate <size_t VolumeDim>\nvoid OrientationMap<VolumeDim>::set_direction(\n    const size_t dim, const Direction<VolumeDim>& direction) {\n  const uint16_t base_mask = Direction<VolumeDim>::all_mask;\n  const uint16_t zero_direction_mask =\n      ~(base_mask << (Direction<VolumeDim>::number_of_bits * dim));\n  bit_field_ = (bit_field_ bitand zero_direction_mask) bitor\n               (static_cast<uint16_t>(direction.bits())\n                << (Direction<VolumeDim>::number_of_bits * dim));\n}\n\ntemplate <size_t VolumeDim>\nvoid OrientationMap<VolumeDim>::set_aligned(bool is_aligned) {\n  bit_field_ = static_cast<uint16_t>(bit_field_ bitand ~aligned_mask) bitor\n               static_cast<uint16_t>(\n                   static_cast<uint16_t>(is_aligned ? 0b1 : 0b0) << 15);\n}\n\ntemplate <size_t VolumeDim>\nstd::set<size_t> OrientationMap<VolumeDim>::set_of_dimensions() const {\n  std::set<size_t> set_of_dims;\n  for (size_t j = 0; j < VolumeDim; j++) {\n    if (get_direction(j) != Direction<VolumeDim>::self()) {\n      set_of_dims.insert(get_direction(j).dimension());\n    }\n  }\n  return set_of_dims;\n}\n\ntemplate <>\nstd::ostream& operator<<(std::ostream& os,\n                         const OrientationMap<1>& orientation) {\n  os << \"(\" << orientation(Direction<1>::upper_xi()) << \")\";\n  return os;\n}\n\ntemplate <>\nstd::ostream& operator<<(std::ostream& os,\n                         const OrientationMap<2>& orientation) {\n  os << \"(\" << orientation(Direction<2>::upper_xi()) << \", \"\n     << orientation(Direction<2>::upper_eta()) << \")\";\n  return os;\n}\n\ntemplate <>\nstd::ostream& operator<<(std::ostream& os,\n                         const OrientationMap<3>& orientation) {\n  os << \"(\" << orientation(Direction<3>::upper_xi()) << \", \"\n     << orientation(Direction<3>::upper_eta()) << \", \"\n     << orientation(Direction<3>::upper_zeta()) << \")\";\n  return os;\n}\n\ntemplate <size_t VolumeDim, typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, VolumeDim> discrete_rotation(\n    const OrientationMap<VolumeDim>& rotation,\n    std::array<T, VolumeDim> source_coords) {\n  using ReturnType = tt::remove_cvref_wrap_t<T>;\n  std::array<ReturnType, VolumeDim> new_coords{};\n  for (size_t i = 0; i < VolumeDim; i++) {\n    const auto new_direction = rotation(Direction<VolumeDim>(i, Side::Upper));\n    ASSERT(new_direction.side() != Side::Self,\n           \"Cannot define discrete rotation for this OrientationMap\");\n    gsl::at(new_coords, i) =\n        std::move(gsl::at(source_coords, new_direction.dimension()));\n    if (new_direction.side() != Side::Upper) {\n      gsl::at(new_coords, i) *= -1.0;\n    }\n  }\n  return new_coords;\n}\n\ntemplate <size_t VolumeDim>\ntnsr::Ij<double, VolumeDim, Frame::NoFrame> discrete_rotation_jacobian(\n    const OrientationMap<VolumeDim>& orientation) {\n  tnsr::Ij<double, VolumeDim, Frame::NoFrame> jacobian_matrix{0.0};\n  for (size_t d = 0; d < VolumeDim; d++) {\n    const auto new_direction =\n        orientation(Direction<VolumeDim>(d, Side::Upper));\n    ASSERT(new_direction.side() != Side::Self,\n           \"Cannot define discrete rotation for this OrientationMap\");\n    jacobian_matrix.get(d, orientation(d)) =\n        new_direction.side() == Side::Upper ? 1.0 : -1.0;\n  }\n  return jacobian_matrix;\n}\n\ntemplate <size_t VolumeDim>\ntnsr::Ij<double, VolumeDim, Frame::NoFrame> discrete_rotation_inverse_jacobian(\n    const OrientationMap<VolumeDim>& orientation) {\n  tnsr::Ij<double, VolumeDim, Frame::NoFrame> inverse_jacobian_matrix{0.0};\n  for (size_t d = 0; d < VolumeDim; d++) {\n    const auto new_direction =\n        orientation(Direction<VolumeDim>(d, Side::Upper));\n    ASSERT(new_direction.side() != Side::Self,\n           \"Cannot define discrete rotation for this OrientationMap\");\n    inverse_jacobian_matrix.get(orientation(d), d) =\n        new_direction.side() == Side::Upper ? 1.0 : -1.0;\n  }\n  return inverse_jacobian_matrix;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template class OrientationMap<DIM(data)>;                                 \\\n  template tnsr::Ij<double, DIM(data), Frame::NoFrame>                      \\\n  discrete_rotation_jacobian(const OrientationMap<DIM(data)>& orientation); \\\n  template tnsr::Ij<double, DIM(data), Frame::NoFrame>                      \\\n  discrete_rotation_inverse_jacobian(                                       \\\n      const OrientationMap<DIM(data)>& orientation);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                         \\\n  template std::array<tt::remove_cvref_wrap_t<DTYPE(data)>, DIM(data)> \\\n  discrete_rotation<DIM(data), DTYPE(data)>(                           \\\n      const OrientationMap<DIM(data)>& rotation,                       \\\n      std::array<DTYPE(data), DIM(data)> source_coords);\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION, (1, 2, 3),\n    (double, DataVector, const double, const DataVector,\n     std::reference_wrapper<double>, std::reference_wrapper<DataVector>,\n     std::reference_wrapper<const double>,\n     std::reference_wrapper<const DataVector>,\n     std::reference_wrapper<double> const,\n     std::reference_wrapper<DataVector> const,\n     std::reference_wrapper<const double> const,\n     std::reference_wrapper<const DataVector> const))\n\n#ifdef SPECTRE_AUTODIFF\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION, (1, 2, 3),\n    (autodiff::SecondOrderDual, autodiff::SecondOrderDualNum,\n     const autodiff::SecondOrderDual, const autodiff::SecondOrderDualNum,\n     std::reference_wrapper<autodiff::SecondOrderDual>,\n     std::reference_wrapper<autodiff::SecondOrderDualNum>,\n     std::reference_wrapper<const autodiff::SecondOrderDual>,\n     std::reference_wrapper<const autodiff::SecondOrderDualNum>,\n     std::reference_wrapper<autodiff::SecondOrderDual> const,\n     std::reference_wrapper<autodiff::SecondOrderDualNum> const,\n     std::reference_wrapper<const autodiff::SecondOrderDual> const,\n     std::reference_wrapper<const autodiff::SecondOrderDualNum> const))\n#endif  // SPECTRE_AUTODIFF\n\n#undef INSTANTIATION\n#undef DTYPE\n#undef DIM\n"
  },
  {
    "path": "src/Domain/Structure/OrientationMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iosfwd>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief A mapping of the logical coordinate axes of a host to the logical\n * coordinate axes of a neighbor of the host.\n *\n * Given a `size_t dimension`, a `Direction`, a `SegmentId`, or a `Mesh` of the\n * host, an `OrientationMap` will give the corresponding value in the neighbor.\n *\n * \\tparam VolumeDim the dimension of the blocks.\n *\n * See the [tutorial](@ref tutorial_orientations) for information on how\n * OrientationMaps are used and constructed.\n *\n * \\note If there is no discrete rotation between logical coordinates (e.g. the\n * angular coordinates of a spherical shell abutting a wedge of a cubed sphere)\n * specify Direction<VolumeDim>::self() as the mapped direction\n *\n */\ntemplate <size_t VolumeDim>\nclass OrientationMap {\n public:\n  static constexpr uint16_t aligned_mask = 0b1000000000000000;\n  static constexpr uint16_t version_mask = 0b0111000000000000;\n\n  /// \\brief Creates an OrientationMap in an uninitialized state.\n  ///\n  /// This can be helpful for debugging code. If you would like the identity\n  /// map, please use `create_aligned()`.\n  OrientationMap();\n  /// Mapped directions relative to the positive (`Side::Upper`) direction in\n  /// each logical direction.\n  explicit OrientationMap(\n      std::array<Direction<VolumeDim>, VolumeDim> mapped_directions);\n  OrientationMap(\n      const std::array<Direction<VolumeDim>, VolumeDim>& directions_in_host,\n      const std::array<Direction<VolumeDim>, VolumeDim>&\n          directions_in_neighbor);\n  ~OrientationMap() = default;\n  OrientationMap(const OrientationMap&) = default;\n  OrientationMap& operator=(const OrientationMap&) = default;\n  OrientationMap(OrientationMap&& /*rhs*/) = default;\n  OrientationMap& operator=(OrientationMap&& /*rhs*/) = default;\n\n  /// Creates an OrientationMap that is the identity map on directions.\n  /// `is_aligned()` is `true` in this case.\n  static OrientationMap<VolumeDim> create_aligned();\n\n  /// True when mapped(Direction) == Direction\n  bool is_aligned() const {\n    ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15),\n           \"Cannot use a default-constructed OrientationMap\");\n    return (bit_field_ bitand aligned_mask) == aligned_mask;\n  }\n\n  /// The corresponding dimension in the neighbor.\n  size_t operator()(const size_t dim) const {\n    ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15),\n           \"Cannot use a default-constructed OrientationMap\");\n    const auto neighbor_direction = get_direction(dim);\n    ASSERT(neighbor_direction.side() != Side::Self,\n           \"There is no corresponding dimension\");\n    return neighbor_direction.dimension();\n  }\n\n  /// The corresponding direction in the neighbor.\n  Direction<VolumeDim> operator()(const Direction<VolumeDim>& direction) const {\n    ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15),\n           \"Cannot use a default-constructed OrientationMap\");\n    return direction.side() == Side::Upper\n               ? get_direction(direction.dimension())\n               : get_direction(direction.dimension()).opposite();\n  }\n\n  /// The corresponding SegmentIds in the neighbor.\n  std::array<SegmentId, VolumeDim> operator()(\n      const std::array<SegmentId, VolumeDim>& segmentIds) const;\n\n  /// The corresponding Mesh in the neighbor\n  Mesh<VolumeDim> operator()(const Mesh<VolumeDim>& mesh) const;\n\n  /// An array whose elements are permuted such that\n  /// `result[this->operator()(d)] = array_to_permute[d]`.\n  ///\n  /// \\note the permutation depends only on how the dimension is mapped\n  /// and ignores the side of the mapped direction.\n  template <typename T>\n  std::array<T, VolumeDim> permute_to_neighbor(\n      const std::array<T, VolumeDim>& array_to_permute) const;\n\n  /// An array whose elements are permuted such that\n  /// `result[d] = array_in_neighbor[this->operator()(d)]`\n  ///\n  /// \\note the permutation depends only on how the dimension is mapped\n  /// and ignores the side of the mapped direction.\n  template <typename T>\n  std::array<T, VolumeDim> permute_from_neighbor(\n      const std::array<T, VolumeDim>& array_in_neighbor) const;\n\n  /// The corresponding Orientation of the host in the frame of the neighbor.\n  OrientationMap<VolumeDim> inverse_map() const;\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  friend bool operator==(const OrientationMap& lhs, const OrientationMap& rhs) {\n    return lhs.bit_field_ == rhs.bit_field_;\n  }\n\n  Direction<VolumeDim> get_direction(size_t dim) const;\n  void set_direction(size_t dim, const Direction<VolumeDim>& direction);\n  void set_aligned(bool is_aligned);\n  std::set<size_t> set_of_dimensions() const;\n\n  uint16_t bit_field_{0b1 << 15};\n};\n\n/// Output operator for OrientationMap.\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const OrientationMap<VolumeDim>& orientation);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const OrientationMap<VolumeDim>& lhs,\n                const OrientationMap<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t VolumeDim>\ntemplate <typename T>\nstd::array<T, VolumeDim> OrientationMap<VolumeDim>::permute_to_neighbor(\n    const std::array<T, VolumeDim>& array_to_permute) const {\n  std::array<T, VolumeDim> array_in_neighbor = array_to_permute;\n  if (is_aligned() or VolumeDim <= 1) {\n    return array_in_neighbor;\n  }\n  for (size_t i = 0; i < VolumeDim; i++) {\n    gsl::at(array_in_neighbor, this->operator()(i)) =\n        gsl::at(array_to_permute, i);\n  }\n  return array_in_neighbor;\n}\n\ntemplate <size_t VolumeDim>\ntemplate <typename T>\nstd::array<T, VolumeDim> OrientationMap<VolumeDim>::permute_from_neighbor(\n    const std::array<T, VolumeDim>& array_in_neighbor) const {\n  std::array<T, VolumeDim> result = array_in_neighbor;\n  if (not is_aligned() and VolumeDim > 1) {\n    for (size_t i = 0; i < VolumeDim; i++) {\n      gsl::at(result, i) = gsl::at(array_in_neighbor, this->operator()(i));\n    }\n  }\n  return result;\n}\n\n/// \\ingroup ComputationalDomainGroup\n/// `OrientationMap`s define an active rotation of the logical axes that bring\n/// the axes of a host block into alignment with the logical axes of the\n/// neighbor block. `discrete_rotation` applies this active rotation on the\n/// coordinates as opposed to the axes.\n/// For a two-dimensional example, consider a host block and a neighbor block,\n/// where the OrientationMap between them is \\f$\\{-\\eta,+\\xi\\}\\f$. A quarter-\n/// turn counterclockwise of the host block's logical axes would bring them into\n/// alignment with those of the neighbor. That is, after this active rotation,\n/// the blocks would be Aligned. Now consider a point A with coordinates\n/// (+1.0,-0.5). An active quarter-turn rotation counter-clockwise about the\n/// origin, keeping the axes fixed, brings point A into the coordinates\n/// (+0.5,+1.0). This is how `discrete_rotation` interprets the\n/// `OrientationMap` passed to it.\ntemplate <size_t VolumeDim, typename T>\nstd::array<tt::remove_cvref_wrap_t<T>, VolumeDim> discrete_rotation(\n    const OrientationMap<VolumeDim>& rotation,\n    std::array<T, VolumeDim> source_coords);\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Computes the Jacobian of the transformation that is computed by\n * `discrete_rotation()`\n *\n * \\note This always returns a `double` because the Jacobian is spatially\n * constant.\n */\ntemplate <size_t VolumeDim>\ntnsr::Ij<double, VolumeDim, Frame::NoFrame> discrete_rotation_jacobian(\n    const OrientationMap<VolumeDim>& orientation);\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Computes the inverse Jacobian of the transformation that is computed\n * by `discrete_rotation()`\n */\ntemplate <size_t VolumeDim>\ntnsr::Ij<double, VolumeDim, Frame::NoFrame> discrete_rotation_inverse_jacobian(\n    const OrientationMap<VolumeDim>& orientation);\n"
  },
  {
    "path": "src/Domain/Structure/OrientationMapHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <numeric>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\n\n// 1D data can be aligned or anti-aligned\nstd::vector<size_t> compute_offset_permutation(\n    const Index<1>& extents, const bool neighbor_axis_is_aligned) {\n  std::vector<size_t> oriented_offsets(extents.product());\n  std::iota(oriented_offsets.begin(), oriented_offsets.end(), 0);\n  if (not neighbor_axis_is_aligned) {\n    std::reverse(oriented_offsets.begin(), oriented_offsets.end());\n  }\n  return oriented_offsets;\n}\n\n// 2D data can have 8 different data-storage orders relative to the neighbor.\n// These are determined by whether the new data-storage order varies fastest by\n// the lowest dim or the highest dim, and by whether each axis is aligned or\n// anti-aligned.\nstd::vector<size_t> compute_offset_permutation(\n    const Index<2>& extents, const bool neighbor_first_axis_is_aligned,\n    const bool neighbor_second_axis_is_aligned,\n    const bool neighbor_axes_are_transposed) {\n  std::vector<size_t> oriented_offsets(extents.product());\n  // Reduce the number of cases to explicitly write out by 4, by encoding the\n  // (anti-)alignment of each axis as numerical factors (\"offset\" and \"step\")\n  // that then contribute in identically-structured loops.\n  // But doing this requires mixing positive and negative factors, so we cast\n  // from size_t to int, do the work, then cast from int back to size_t.\n  const auto num_pts_1 = static_cast<int>(extents[0]);\n  const auto num_pts_2 = static_cast<int>(extents[1]);\n  const int i1_offset = neighbor_first_axis_is_aligned ? 0 : num_pts_1 - 1;\n  const int i1_step = neighbor_first_axis_is_aligned ? 1 : -1;\n  const int i2_offset = neighbor_second_axis_is_aligned ? 0 : num_pts_2 - 1;\n  const int i2_step = neighbor_second_axis_is_aligned ? 1 : -1;\n  if (neighbor_axes_are_transposed) {\n    for (int i2 = 0; i2 < num_pts_2; ++i2) {\n      for (int i1 = 0; i1 < num_pts_1; ++i1) {\n        // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n        oriented_offsets[static_cast<size_t>(i1 + num_pts_1 * i2)] =\n            // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n            static_cast<size_t>((i2_offset + i2_step * i2) +\n                                num_pts_2 * (i1_offset + i1_step * i1));\n      }\n    }\n  } else {\n    for (int i2 = 0; i2 < num_pts_2; ++i2) {\n      for (int i1 = 0; i1 < num_pts_1; ++i1) {\n        // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n        oriented_offsets[static_cast<size_t>(i1 + num_pts_1 * i2)] =\n            // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n            static_cast<size_t>((i1_offset + i1_step * i1) +\n                                num_pts_1 * (i2_offset + i2_step * i2));\n      }\n    }\n  }\n  return oriented_offsets;\n}\n\n// 3D data can have 48 (!) different data-storage orders relative to the\n// neighbor. A factor of 6 arises from the different ways in which the three\n// dimensions can be ordered from fastest to slowest varying. The remaining\n// factor of 8 arises from having two possible directions (aligned or\n// anti-aligned) for each of the three axes.\nstd::vector<size_t> compute_offset_permutation(\n    const Index<3>& extents, const bool neighbor_first_axis_is_aligned,\n    const bool neighbor_second_axis_is_aligned,\n    const bool neighbor_third_axis_is_aligned,\n    const std::array<size_t, 3>& neighbor_axis_permutation) {\n  std::vector<size_t> oriented_offsets(extents.product());\n  // Reduce the number of cases to explicitly write out by 8, by encoding the\n  // (anti-)alignment of each axis as numerical factors (\"offset\" and \"step\")\n  // that then contribute in identically-structured loops.\n  // But doing this requires mixing positive and negative factors, so we cast\n  // from size_t to int, do the work, then cast from int back to size_t.\n  const auto num_pts_1 = static_cast<int>(extents[0]);\n  const auto num_pts_2 = static_cast<int>(extents[1]);\n  const auto num_pts_3 = static_cast<int>(extents[2]);\n  const int i1_offset = neighbor_first_axis_is_aligned ? 0 : num_pts_1 - 1;\n  const int i1_step = neighbor_first_axis_is_aligned ? 1 : -1;\n  const int i2_offset = neighbor_second_axis_is_aligned ? 0 : num_pts_2 - 1;\n  const int i2_step = neighbor_second_axis_is_aligned ? 1 : -1;\n  const int i3_offset = neighbor_third_axis_is_aligned ? 0 : num_pts_3 - 1;\n  const int i3_step = neighbor_third_axis_is_aligned ? 1 : -1;\n  // The three cyclic permutations of the dimensions 0, 1, 2\n  // Note that these do not necessarily lead to right-handed coordinate\n  // systems, because the final \"handedness\" also depends on whether\n  // each axis is aligned or anti-aligned.\n  if (neighbor_axis_permutation == make_array(0_st, 1_st, 2_st)) {\n    for (int i3 = 0; i3 < num_pts_3; ++i3) {\n      for (int i2 = 0; i2 < num_pts_2; ++i2) {\n        for (int i1 = 0; i1 < num_pts_1; ++i1) {\n          // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n          oriented_offsets[static_cast<size_t>(i1 + num_pts_1 * i2 +\n                                               num_pts_1 * num_pts_2 * i3)] =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              static_cast<size_t>((i1_offset + i1_step * i1) +\n                                  num_pts_1 * (i2_offset + i2_step * i2) +\n                                  num_pts_1 * num_pts_2 *\n                                      (i3_offset + i3_step * i3));\n        }\n      }\n    }\n  } else if (neighbor_axis_permutation == make_array(1_st, 2_st, 0_st)) {\n    for (int i3 = 0; i3 < num_pts_3; ++i3) {\n      for (int i2 = 0; i2 < num_pts_2; ++i2) {\n        for (int i1 = 0; i1 < num_pts_1; ++i1) {\n          // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n          oriented_offsets[static_cast<size_t>(i1 + num_pts_1 * i2 +\n                                               num_pts_1 * num_pts_2 * i3)] =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              static_cast<size_t>((i3_offset + i3_step * i3) +\n                                  num_pts_3 * (i1_offset + i1_step * i1) +\n                                  num_pts_3 * num_pts_1 *\n                                      (i2_offset + i2_step * i2));\n        }\n      }\n    }\n  } else if (neighbor_axis_permutation == make_array(2_st, 0_st, 1_st)) {\n    for (int i3 = 0; i3 < num_pts_3; ++i3) {\n      for (int i2 = 0; i2 < num_pts_2; ++i2) {\n        for (int i1 = 0; i1 < num_pts_1; ++i1) {\n          // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n          oriented_offsets[static_cast<size_t>(i1 + num_pts_1 * i2 +\n                                               num_pts_1 * num_pts_2 * i3)] =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              static_cast<size_t>((i2_offset + i2_step * i2) +\n                                  num_pts_2 * (i3_offset + i3_step * i3) +\n                                  num_pts_2 * num_pts_3 *\n                                      (i1_offset + i1_step * i1));\n        }\n      }\n    }\n  }\n  // The three acyclic permutations\n  else if (neighbor_axis_permutation == make_array(0_st, 2_st, 1_st)) {\n    for (int i3 = 0; i3 < num_pts_3; ++i3) {\n      for (int i2 = 0; i2 < num_pts_2; ++i2) {\n        for (int i1 = 0; i1 < num_pts_1; ++i1) {\n          // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n          oriented_offsets[static_cast<size_t>(i1 + num_pts_1 * i2 +\n                                               num_pts_1 * num_pts_2 * i3)] =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              static_cast<size_t>((i1_offset + i1_step * i1) +\n                                  num_pts_1 * (i3_offset + i3_step * i3) +\n                                  num_pts_1 * num_pts_3 *\n                                      (i2_offset + i2_step * i2));\n        }\n      }\n    }\n  } else if (neighbor_axis_permutation == make_array(2_st, 1_st, 0_st)) {\n    for (int i3 = 0; i3 < num_pts_3; ++i3) {\n      for (int i2 = 0; i2 < num_pts_2; ++i2) {\n        for (int i1 = 0; i1 < num_pts_1; ++i1) {\n          // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n          oriented_offsets[static_cast<size_t>(i1 + num_pts_1 * i2 +\n                                               num_pts_1 * num_pts_2 * i3)] =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              static_cast<size_t>((i3_offset + i3_step * i3) +\n                                  num_pts_3 * (i2_offset + i2_step * i2) +\n                                  num_pts_3 * num_pts_2 *\n                                      (i1_offset + i1_step * i1));\n        }\n      }\n    }\n  } else {  // make_array(1_st, 0_st, 2_st)\n    for (int i3 = 0; i3 < num_pts_3; ++i3) {\n      for (int i2 = 0; i2 < num_pts_2; ++i2) {\n        for (int i1 = 0; i1 < num_pts_1; ++i1) {\n          // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n          oriented_offsets[static_cast<size_t>(i1 + num_pts_1 * i2 +\n                                               num_pts_1 * num_pts_2 * i3)] =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              static_cast<size_t>((i2_offset + i2_step * i2) +\n                                  num_pts_2 * (i1_offset + i1_step * i1) +\n                                  num_pts_2 * num_pts_1 *\n                                      (i3_offset + i3_step * i3));\n        }\n      }\n    }\n  }\n  return oriented_offsets;\n}\n\nstd::vector<size_t> oriented_offset(\n    const Index<1>& extents, const OrientationMap<1>& orientation_of_neighbor) {\n  const Direction<1> neighbor_axis =\n      orientation_of_neighbor(Direction<1>::upper_xi());\n  const bool is_aligned = (neighbor_axis.side() == Side::Upper);\n  return compute_offset_permutation(extents, is_aligned);\n}\n\nstd::vector<size_t> oriented_offset(\n    const Index<2>& extents, const OrientationMap<2>& orientation_of_neighbor) {\n  const Direction<2> neighbor_first_axis =\n      orientation_of_neighbor(Direction<2>::upper_xi());\n  const Direction<2> neighbor_second_axis =\n      orientation_of_neighbor(Direction<2>::upper_eta());\n  const bool axes_are_transposed =\n      (neighbor_first_axis.dimension() > neighbor_second_axis.dimension());\n  const bool neighbor_first_axis_is_aligned =\n      (Side::Upper == neighbor_first_axis.side());\n  const bool neighbor_second_axis_is_aligned =\n      (Side::Upper == neighbor_second_axis.side());\n\n  return compute_offset_permutation(extents, neighbor_first_axis_is_aligned,\n                                    neighbor_second_axis_is_aligned,\n                                    axes_are_transposed);\n}\n\nstd::vector<size_t> oriented_offset(\n    const Index<3>& extents, const OrientationMap<3>& orientation_of_neighbor) {\n  const Direction<3> neighbor_first_axis =\n      orientation_of_neighbor(Direction<3>::upper_xi());\n  const Direction<3> neighbor_second_axis =\n      orientation_of_neighbor(Direction<3>::upper_eta());\n  const Direction<3> neighbor_third_axis =\n      orientation_of_neighbor(Direction<3>::upper_zeta());\n\n  const bool neighbor_first_axis_is_aligned =\n      (Side::Upper == neighbor_first_axis.side());\n  const bool neighbor_second_axis_is_aligned =\n      (Side::Upper == neighbor_second_axis.side());\n  const bool neighbor_third_axis_is_aligned =\n      (Side::Upper == neighbor_third_axis.side());\n\n  const auto neighbor_axis_permutation = make_array(\n      neighbor_first_axis.dimension(), neighbor_second_axis.dimension(),\n      neighbor_third_axis.dimension());\n\n  return compute_offset_permutation(\n      extents, neighbor_first_axis_is_aligned, neighbor_second_axis_is_aligned,\n      neighbor_third_axis_is_aligned, neighbor_axis_permutation);\n}\n\nstd::vector<size_t> oriented_offset_on_slice(\n    const Index<0>& /*slice_extents*/, const size_t /*sliced_dim*/,\n    const OrientationMap<1>& /*orientation_of_neighbor*/) {\n  // There is only one point on a slice of a 1D mesh\n  return {0};\n}\n\nstd::vector<size_t> oriented_offset_on_slice(\n    const Index<1>& slice_extents, const size_t sliced_dim,\n    const OrientationMap<2>& orientation_of_neighbor) {\n  const Direction<2> my_slice_axis =\n      (0 == sliced_dim ? Direction<2>::upper_eta() : Direction<2>::upper_xi());\n  const Direction<2> neighbor_slice_axis =\n      orientation_of_neighbor(my_slice_axis);\n  const bool is_aligned = (neighbor_slice_axis.side() == Side::Upper);\n  return compute_offset_permutation(slice_extents, is_aligned);\n}\n\nstd::vector<size_t> oriented_offset_on_slice(\n    const Index<2>& slice_extents, const size_t sliced_dim,\n    const OrientationMap<3>& orientation_of_neighbor) {\n  const std::array<size_t, 2> dims_of_slice =\n      (0 == sliced_dim ? make_array(1_st, 2_st)\n                       : (1 == sliced_dim) ? make_array(0_st, 2_st)\n                                           : make_array(0_st, 1_st));\n  const bool neighbor_axes_are_transposed =\n      (orientation_of_neighbor(dims_of_slice[0]) >\n       orientation_of_neighbor(dims_of_slice[1]));\n  const Direction<3> neighbor_first_axis =\n      orientation_of_neighbor(Direction<3>(dims_of_slice[0], Side::Upper));\n  const Direction<3> neighbor_second_axis =\n      orientation_of_neighbor(Direction<3>(dims_of_slice[1], Side::Upper));\n  const bool neighbor_first_axis_is_aligned =\n      (Side::Upper == neighbor_first_axis.side());\n  const bool neighbor_second_axis_is_aligned =\n      (Side::Upper == neighbor_second_axis.side());\n\n  return compute_offset_permutation(\n      slice_extents, neighbor_first_axis_is_aligned,\n      neighbor_second_axis_is_aligned, neighbor_axes_are_transposed);\n}\n\ntemplate <typename T>\nvoid orient_each_component(\n    const gsl::not_null<gsl::span<T>*> oriented_variables,\n    const gsl::span<const T>& variables, const size_t num_pts,\n    const std::vector<size_t>& oriented_offset) {\n  const size_t num_components = variables.size() / num_pts;\n  ASSERT(oriented_variables->size() == variables.size(),\n         \"The number of oriented variables, \"\n             << oriented_variables->size() / num_pts\n             << \", must be equal to the number of variables, \"\n             << variables.size() / num_pts);\n  for (size_t component_index = 0; component_index < num_components;\n       ++component_index) {\n    const size_t offset = component_index * num_pts;\n    for (size_t s = 0; s < num_pts; ++s) {\n      gsl::at((*oriented_variables), offset + oriented_offset[s]) =\n          gsl::at(variables, offset + s);\n    }\n  }\n}\n}  // namespace\n\ntemplate <size_t VolumeDim>\nMesh<VolumeDim - 1> orient_mesh_on_slice(\n    const Mesh<VolumeDim - 1>& mesh_on_slice, const size_t sliced_dim,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor) {\n  if constexpr (VolumeDim < 3) {\n    return mesh_on_slice;\n  } else {\n    if (orientation_of_neighbor.is_aligned()) {\n      return mesh_on_slice;\n    }\n    const size_t first_dim_of_slice = sliced_dim == 0 ? 1 : 0;\n    const size_t second_dim_of_slice = sliced_dim == 2 ? 1 : 2;\n    if (orientation_of_neighbor(first_dim_of_slice) >\n        orientation_of_neighbor(second_dim_of_slice)) {\n      return Mesh<2>{\n          {{mesh_on_slice.extents(1), mesh_on_slice.extents(0)}},\n          {{mesh_on_slice.basis(1), mesh_on_slice.basis(0)}},\n          {{mesh_on_slice.quadrature(1), mesh_on_slice.quadrature(0)}}};\n    } else {\n      return mesh_on_slice;\n    }\n  }\n}\n\ntemplate <typename VectorType, size_t VolumeDim>\nvoid orient_variables(\n    const gsl::not_null<VectorType*> result, const VectorType& variables,\n    const Index<VolumeDim>& extents,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor) {\n  ASSERT(result->size() == variables.size(),\n         \"Result should have size \" << variables.size() << \" but has size \"\n                                    << result->size());\n  const size_t number_of_grid_points = extents.product();\n  ASSERT(variables.size() % number_of_grid_points == 0,\n         \"The size of the variables must be divisible by the number of grid \"\n         \"points. Number of grid points: \"\n             << number_of_grid_points << \" size: \" << variables.size());\n  // Skip work (aside from a copy) if neighbor is aligned\n  if (orientation_of_neighbor.is_aligned()) {\n    (*result) = variables;\n    return;\n  }\n\n  const auto oriented_extents =\n      oriented_offset(extents, orientation_of_neighbor);\n  auto oriented_vars_view = gsl::make_span(result->data(), result->size());\n  orient_each_component(make_not_null(&oriented_vars_view),\n                        gsl::make_span(variables.data(), variables.size()),\n                        number_of_grid_points, oriented_extents);\n}\n\ntemplate <typename VectorType, size_t VolumeDim>\nvoid orient_variables_on_slice(\n    const gsl::not_null<VectorType*> result,\n    const VectorType& variables_on_slice,\n    const Index<VolumeDim - 1>& slice_extents, const size_t sliced_dim,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor) {\n  ASSERT(result->size() == variables_on_slice.size(),\n         \"Result should have size \" << variables_on_slice.size()\n                                    << \" but has size \" << result->size());\n  const size_t number_of_grid_points = slice_extents.product();\n  ASSERT(variables_on_slice.size() % number_of_grid_points == 0,\n         \"The size of the variables must be divisible by the number of grid \"\n         \"points. Number of grid points: \"\n             << number_of_grid_points\n             << \" size: \" << variables_on_slice.size());\n  // Skip work (aside from a copy) if neighbor slice is aligned\n  if (orientation_of_neighbor.is_aligned()) {\n    (*result) = variables_on_slice;\n    return;\n  }\n\n  const auto oriented_offset = oriented_offset_on_slice(\n      slice_extents, sliced_dim, orientation_of_neighbor);\n\n  auto oriented_vars_view = gsl::make_span(result->data(), result->size());\n  orient_each_component(\n      make_not_null(&oriented_vars_view),\n      gsl::make_span(variables_on_slice.data(), variables_on_slice.size()),\n      number_of_grid_points, oriented_offset);\n}\n\ntemplate <typename VectorType, size_t VolumeDim>\nVectorType orient_variables(\n    const VectorType& variables, const Index<VolumeDim>& extents,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor) {\n  VectorType oriented_variables(variables.size());\n  orient_variables(make_not_null(&oriented_variables), variables, extents,\n                   orientation_of_neighbor);\n  return oriented_variables;\n}\n\ntemplate <typename VectorType, size_t VolumeDim>\nVectorType orient_variables_on_slice(\n    const VectorType& variables_on_slice,\n    const Index<VolumeDim - 1>& slice_extents, const size_t sliced_dim,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor) {\n  VectorType oriented_variables(variables_on_slice.size());\n  orient_variables_on_slice(make_not_null(&oriented_variables),\n                            variables_on_slice, slice_extents, sliced_dim,\n                            orientation_of_neighbor);\n  return oriented_variables;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\ntemplate Mesh<0> orient_mesh_on_slice(\n    const Mesh<0>& mesh_on_slice, const size_t sliced_dim,\n    const OrientationMap<1>& orientation_of_neighbor);\ntemplate Mesh<1> orient_mesh_on_slice(\n    const Mesh<1>& mesh_on_slice, const size_t sliced_dim,\n    const OrientationMap<2>& orientation_of_neighbor);\ntemplate Mesh<2> orient_mesh_on_slice(\n    const Mesh<2>& mesh_on_slice, const size_t sliced_dim,\n    const OrientationMap<3>& orientation_of_neighbor);\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void orient_variables(                                              \\\n      const gsl::not_null<DTYPE(data)*> result, const DTYPE(data) & variables, \\\n      const Index<DIM(data)>& extents,                                         \\\n      const OrientationMap<DIM(data)>& orientation_of_neighbor);               \\\n  template void orient_variables_on_slice(                                     \\\n      const gsl::not_null<DTYPE(data)*> result,                                \\\n      const DTYPE(data) & variables_on_slice,                                  \\\n      const Index<DIM(data) - 1>& slice_extents, size_t sliced_dim,            \\\n      const OrientationMap<DIM(data)>& orientation_of_neighbor);               \\\n  template DTYPE(data) orient_variables(                                       \\\n      const DTYPE(data) & variables, const Index<DIM(data)>& extents,          \\\n      const OrientationMap<DIM(data)>& orientation_of_neighbor);               \\\n  template DTYPE(data) orient_variables_on_slice(                              \\\n      const DTYPE(data) & variables, const Index<DIM(data) - 1>& extents,      \\\n      size_t sliced_dim,                                                       \\\n      const OrientationMap<DIM(data)>& orientation_of_neighbor);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (DataVector, ComplexDataVector, ModalVector,\n                         std::vector<double>,\n                         std::vector<std::complex<double>>),\n                        (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Domain/Structure/OrientationMapHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Mesh;\ntemplate <size_t VolumeDim>\nclass OrientationMap;\ntemplate <size_t>\nclass Index;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Orient a sliced Mesh to the logical frame of  a neighbor element with\n/// the given orientation.\ntemplate <size_t VolumeDim>\nMesh<VolumeDim - 1> orient_mesh_on_slice(\n    const Mesh<VolumeDim - 1>& mesh_on_slice, size_t sliced_dim,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor);\n\n/// @{\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Orient a `DataVector`, `ComplexDataVector`, `std::vector<double>`, or\n/// `std::vector<std::complex<double>>` to the data-storage order of a neighbor\n/// element with the given orientation.\n///\n/// The vector may represent more than one tensor component over the grid\n/// represented by `extents`.\n///\n/// \\warning The result is *not* resized and assumes to be of the correct size\n/// (`variables.size()`).\n///\n/// In most cases the `Variables` version of `orient_variables` should be\n/// called. However, in some cases the tags and thus the type of the data being\n/// sent is determined at runtime. In these cases the `std::vector` version of\n/// `orient_variables` is useful. A concrete example of this is when hybridizing\n/// DG with finite difference methods, where sometimes the data sent is both the\n/// variables for reconstruction and the fluxes for either the DG or finite\n/// difference scheme, while at other points only one of these three is sent.\ntemplate <typename VectorType, size_t VolumeDim>\nvoid orient_variables(gsl::not_null<VectorType*> result,\n                      const VectorType& variables,\n                      const Index<VolumeDim>& extents,\n                      const OrientationMap<VolumeDim>& orientation_of_neighbor);\n\ntemplate <typename VectorType, size_t VolumeDim>\nVectorType orient_variables(\n    const VectorType& variables, const Index<VolumeDim>& extents,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor);\n\ntemplate <typename VectorType, size_t VolumeDim>\nvoid orient_variables_on_slice(\n    gsl::not_null<VectorType*> result, const VectorType& variables_on_slice,\n    const Index<VolumeDim - 1>& slice_extents, size_t sliced_dim,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor);\n\ntemplate <typename VectorType, size_t VolumeDim>\nVectorType orient_variables_on_slice(\n    const VectorType& variables_on_slice,\n    const Index<VolumeDim - 1>& slice_extents, size_t sliced_dim,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor);\n/// @}\n\n/// @{\n/// \\ingroup ComputationalDomainGroup\n/// Orient variables to the data-storage order of a neighbor element with\n/// the given orientation.\ntemplate <size_t VolumeDim, typename TagsList>\nVariables<TagsList> orient_variables(\n    const Variables<TagsList>& variables, const Index<VolumeDim>& extents,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor) {\n  // Skip work (aside from a copy) if neighbor is aligned\n  if (orientation_of_neighbor.is_aligned()) {\n    return variables;\n  }\n\n  const size_t number_of_grid_points = extents.product();\n  ASSERT(variables.number_of_grid_points() == number_of_grid_points,\n         \"Inconsistent `variables` and `extents`:\\n\"\n         \"  variables.number_of_grid_points() = \"\n             << variables.number_of_grid_points()\n             << \"\\n\"\n                \"  extents.product() = \"\n             << extents.product());\n  Variables<TagsList> oriented_variables(number_of_grid_points);\n  using VectorType = typename Variables<TagsList>::vector_type;\n  using ValueType = typename Variables<TagsList>::value_type;\n  VectorType result(oriented_variables.data(), oriented_variables.size());\n  orient_variables(\n      make_not_null(&result),\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n      VectorType(const_cast<ValueType*>(variables.data()), variables.size()),\n      extents, orientation_of_neighbor);\n  return oriented_variables;\n}\n\ntemplate <size_t VolumeDim, typename TagsList>\nVariables<TagsList> orient_variables_on_slice(\n    const Variables<TagsList>& variables_on_slice,\n    const Index<VolumeDim - 1>& slice_extents, const size_t sliced_dim,\n    const OrientationMap<VolumeDim>& orientation_of_neighbor) {\n  // Skip work (aside from a copy) if neighbor slice is aligned\n  if (orientation_of_neighbor.is_aligned()) {\n    return variables_on_slice;\n  }\n\n  const size_t number_of_grid_points = slice_extents.product();\n  ASSERT(variables_on_slice.number_of_grid_points() == number_of_grid_points,\n         \"Inconsistent `variables_on_slice` and `slice_extents`:\\n\"\n         \"  variables_on_slice.number_of_grid_points() = \"\n             << variables_on_slice.number_of_grid_points()\n             << \"\\n\"\n                \"  slice_extents.product() = \"\n             << slice_extents.product());\n\n  Variables<TagsList> oriented_variables(number_of_grid_points);\n  using VectorType = typename Variables<TagsList>::vector_type;\n  using ValueType = typename Variables<TagsList>::value_type;\n  VectorType result(oriented_variables.data(), oriented_variables.size());\n  orient_variables_on_slice(\n      make_not_null(&result),\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n      VectorType(const_cast<ValueType*>(variables_on_slice.data()),\n                 variables_on_slice.size()),\n      slice_extents, sliced_dim, orientation_of_neighbor);\n  return oriented_variables;\n}\n/// @}\n"
  },
  {
    "path": "src/Domain/Structure/SegmentId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/SegmentId.hpp\"\n\n#if BOOST_VERSION >= 106700\n#include <boost/container_hash/hash.hpp>\n#else\n#include <boost/functional/hash.hpp>\n#endif\n#include <cstddef>\n#include <functional>\n#include <ostream>\n#include <pup.h>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nSegmentId::SegmentId(const size_t refinement_level, const size_t index)\n    : refinement_level_(refinement_level), index_(index) {\n  ASSERT(index < two_to_the(refinement_level),\n         \"index = \" << index << \", refinement_level = \" << refinement_level);\n}\n\nvoid SegmentId::pup(PUP::er& p) {\n  p | refinement_level_;\n  p | index_;\n}\n\nstd::ostream& operator<<(std::ostream& os, const SegmentId& id) {\n  os << 'L' << id.refinement_level() << 'I' << id.index();\n  return os;\n}\n\nbool overlapping(const SegmentId& a, const SegmentId& b) {\n  const size_t a_denom = two_to_the(a.refinement_level());\n  const size_t b_denom = two_to_the(b.refinement_level());\n  return a.index() * b_denom < (b.index() + 1) * a_denom and\n         b.index() * a_denom < (a.index() + 1) * b_denom;\n}\n\n// LCOV_EXCL_START\nsize_t hash_value(const SegmentId& segment_id) {\n  size_t hash = 0;\n  boost::hash_combine(hash, segment_id.refinement_level());\n  boost::hash_combine(hash, segment_id.index());\n  return hash;\n}\n// LCOV_EXCL_STOP\n\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nnamespace std {\nsize_t hash<SegmentId>::operator()(const SegmentId& segment_id) const {\n  return hash_value(segment_id);\n}\n}  // namespace std\n"
  },
  {
    "path": "src/Domain/Structure/SegmentId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class SegmentId.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <pup.h>\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief A SegmentId labels a segment of the interval \\f$[-1,1]\\f$ and is used\n * to identify the bounds of an Element in a Block in each dimension.\n *\n * In \\f$d\\f$ dimensions, \\f$d\\f$ SegmentId%s are used to identify an Element.\n * In each dimension, a segment spans the subinterval \\f$[-1 + 2 \\frac{i}{N}, -1\n * + 2 \\frac{i+1}{N}]\\f$ of the logical coordinates of a Block, where \\f$i \\f$=\n * `index` and \\f$N = 2^L\\f$ where \\f$L\\f$ = `refinement_level`.\n *\n *  \\image html SegmentId.svg  \"SegmentIds\"\n *\n * In the figure, The `index` of segments increase from the `lower side` to the\n * `upper side` in each dimension of a Block, while the `refinement level`\n * increases as the segments are subdivided.  For example, let the segment\n * labeled `self` be on `refinement level` \\f$L\\f$, with `index` \\f$i\\f$.  Its\n * `parent` segment is on `refinement level` \\f$L-1\\f$ with  `index`\n * \\f$\\frac{i-1}{2}\\f$. The `children` of `self` are on `refinement level`\n * \\f$L+1\\f$, and have `index` \\f$2i\\f$ and \\f$2i+1\\f$ for the lower and upper\n * child respectively.  Also labeled on the figure are the `sibling` and\n * `abutting nibling` (child of sibling) of `self`. These relationships between\n * segments are important for h-refinement, since in each dimension an Element\n * can be flagged to split into its two `children` segments, or join with its\n * `sibling` segment to form its `parent` segment.  As refinement levels of\n * neighboring elements are kept within one, in the direction of its `sibling`,\n * a segment can only abut its `sibling` or `abutting nibling`, while on the\n * opposite side, it can abut a segment on its level, the next-lower, or the\n * next-higher level.\n */\nclass SegmentId {\n public:\n  /// Default constructor needed for Charm++ serialization.\n  SegmentId() = default;\n  SegmentId(const SegmentId& segment_id) = default;\n  SegmentId(SegmentId&& segment_id) = default;\n  ~SegmentId() = default;\n  SegmentId& operator=(const SegmentId& segment_id) = default;\n  SegmentId& operator=(SegmentId&& segment_id) = default;\n\n  SegmentId(size_t refinement_level, size_t index);\n\n  constexpr size_t refinement_level() const { return refinement_level_; }\n\n  constexpr size_t index() const { return index_; }\n\n  SegmentId id_of_parent() const;\n\n  SegmentId id_of_child(Side side) const;\n\n  /// The other child of the parent of this segment\n  SegmentId id_of_sibling() const;\n\n  /// The child of the sibling of this segment that shares an endpoint with it\n  SegmentId id_of_abutting_nibling() const;\n\n  /// The side on which this segment shares an endpoint with its sibling\n  Side side_of_sibling() const;\n\n  /// The id this segment would have if the coordinate axis were flipped.\n  SegmentId id_if_flipped() const;\n\n  /// The block logical coordinate of the endpoint of the segment on the given\n  /// Side.\n  double endpoint(Side side) const;\n\n  /// The block logical coordinate of the midpoint of the segment\n  double midpoint() const {\n    return -1.0 + (1.0 + 2.0 * index_) / two_to_the(refinement_level_);\n  }\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p);\n\n private:\n  size_t refinement_level_;\n  size_t index_;\n};\n\n/// Output operator for SegmentId.\nstd::ostream& operator<<(std::ostream& os, const SegmentId& id);\n\n/// Equivalence operator for SegmentId.\nbool operator==(const SegmentId& lhs, const SegmentId& rhs);\n\n/// Inequivalence operator for SegmentId.\nbool operator!=(const SegmentId& lhs, const SegmentId& rhs);\n\n/// \\ingroup ComputationalDomainGroup\n/// Check if two segments overlap.\nbool overlapping(const SegmentId& a, const SegmentId& b);\n\n// #############################################################################\n//  INLINE DEFINITIONS\n// #############################################################################\n\ninline SegmentId SegmentId::id_of_parent() const {\n  ASSERT(0 != refinement_level_,\n         \"Cannot call id_of_parent() on root refinement level!\");\n  // The parent has half as many segments as the child.\n  return {refinement_level() - 1, index() / 2};\n}\n\ninline SegmentId SegmentId::id_of_child(Side side) const {\n  // We cannot ASSERT on the maximum level because it's only known at runtime\n  // and only known elsewhere in the code, not by SegmentId. I.e. SegmentId is\n  // too low-level to know about this.\n  // The child has twice as many segments as the parent, so for a particular\n  // parent segment, there is both an upper and lower child segment.\n  if (Side::Lower == side) {\n    return {refinement_level() + 1, index() * 2};\n  }\n  return {refinement_level() + 1, 1 + index() * 2};\n}\n\ninline SegmentId SegmentId::id_of_sibling() const {\n  ASSERT(0 != refinement_level(),\n         \"The segment on the root refinement level has no sibling\");\n  return {refinement_level(), (0 == index() % 2 ? index() + 1 : index() - 1)};\n}\n\ninline SegmentId SegmentId::id_of_abutting_nibling() const {\n  ASSERT(0 != refinement_level(),\n         \"The segment on the root refinement level has no abutting nibling\");\n  return {refinement_level() + 1,\n          (0 == index() % 2 ? 2 * index() + 2 : 2 * index() - 1)};\n}\n\ninline Side SegmentId::side_of_sibling() const {\n  ASSERT(0 != refinement_level(),\n         \"The segment on the root refinement level has no sibling\");\n  return 0 == index() % 2 ? Side::Upper : Side::Lower;\n}\n\ninline SegmentId SegmentId::id_if_flipped() const {\n  return {refinement_level(), two_to_the(refinement_level()) - 1 - index()};\n}\n\ninline double SegmentId::endpoint(Side side) const {\n  if (Side::Lower == side) {\n    return -1.0 + (2.0 * index()) / two_to_the(refinement_level());\n  }\n  return -1.0 + (2.0 * index() + 2.0) / two_to_the(refinement_level());\n}\n\n// These are defined so that a SegmentId can be used as part of a key of an\n// unordered_set or unordered_map.\n\n// hash_value is called by boost::hash and related functions.\nsize_t hash_value(const SegmentId& s);\n\nnamespace std {\ntemplate <>\nstruct hash<SegmentId> {\n  size_t operator()(const SegmentId& segment_id) const;\n};\n}  // namespace std\n\ninline bool operator==(const SegmentId& lhs, const SegmentId& rhs) {\n  return (lhs.refinement_level() == rhs.refinement_level() and\n          lhs.index() == rhs.index());\n}\n\ninline bool operator!=(const SegmentId& lhs, const SegmentId& rhs) {\n  return not(lhs == rhs);\n}\n"
  },
  {
    "path": "src/Domain/Structure/Side.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/Side.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nSide opposite(const Side side) {\n  switch (side) {\n    case (Side::Uninitialized):\n      ERROR(\"Cannot get the opposite of Side::Uninitialized\");\n    case (Side::Lower):\n      return Side::Upper;\n    case (Side::Upper):\n      return Side::Lower;\n    case (Side::Self):\n      return Side::Self;\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"An unknown Side was passed to Side::opposite()\");\n      // LCOV_EXCL_STOP\n  }\n}\n\nstd::ostream& operator<<(std::ostream& os, const Side& side) {\n  switch (side) {\n    case Side::Uninitialized:\n      return os << \"Uninitialized\";\n    case Side::Lower:\n      return os << \"Lower\";\n    case Side::Upper:\n      return os << \"Upper\";\n    case Side::Self:\n      return os << \"Self\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\n          \"A Side that was neither Upper nor Lower was passed to the stream \"\n          \"operator.\");\n      // LCOV_EXCL_STOP\n  }\n}\n"
  },
  {
    "path": "src/Domain/Structure/Side.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines enum class Side.\n\n#pragma once\n\n#include <cstdint>\n#include <iosfwd>\n\nnamespace detail {\nconstexpr uint8_t side_shift = 2;\n}  // namespace detail\n\n/// \\ingroup ComputationalDomainGroup\n/// A label for the side of a manifold.\n///\n/// Lower and Upper are with respect to the logical coordinate whose axis is\n/// normal to the side, i.e. beyond the Upper (Lower) side, the logical\n/// coordinate is increasing (decreasing).\n///\n/// `Self` is used to mark when an `ElementId` does not have a direction.\n///\n/// Currently `Direction` assumes this enum uses no more than 2 bits.\nenum class Side : uint8_t {\n  Uninitialized = 0 << detail::side_shift,\n  Lower = 1 << detail::side_shift,\n  Upper = 2 << detail::side_shift,\n  Self = 3 << detail::side_shift\n};\n\n/// The opposite side (Side::Self is its own opposite)\nSide opposite(Side side);\n\n/// Output operator for a Side.\nstd::ostream& operator<<(std::ostream& os, const Side& side);\n"
  },
  {
    "path": "src/Domain/Structure/Topology.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/Topology.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace domain {\nstd::ostream& operator<<(std::ostream& os, const Topology topology) {\n  switch (topology) {\n    case Topology::Uninitialized:\n      return os << \"Uninitialized\";\n    case Topology::I1:\n      return os << \"I1\";\n    case Topology::S1:\n      return os << \"S1\";\n    case Topology::S2Colatitude:\n      return os << \"S2Colatitude\";\n    case Topology::S2Longitude:\n      return os << \"S2Longitude\";\n    case Topology::B2Radial:\n      return os << \"B2Radial\";\n    case Topology::B2Angular:\n      return os << \"B2Angular\";\n    case Topology::B3Radial:\n      return os << \"B3Radial\";\n    case Topology::B3Colatitude:\n      return os << \"B3Colatitude\";\n    case Topology::B3Longitude:\n      return os << \"B3Longitude\";\n    case Topology::CartoonSphere:\n      return os << \"CartoonSphere\";\n    case Topology::CartoonCylinder:\n      return os << \"CartoonCylinder\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"An unknown value of Topology was passed to the stream operator.\");\n      // LCOV_EXCL_STOP\n  }\n}\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/Topology.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <iosfwd>\n\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain {\n\n/// \\brief  The topology of a Block or Element in a particular dimension\n///\n/// \\details The Topology is used to determine the geometry of the Block or\n/// Element, which can be used to determine:\n/// - Whether there is an interface (with a neighbor or external boundary) in a\n/// given direction\n/// - The block (element) logical coordinate bounds\n/// - The appropriate Basis and Quadrature for a Mesh on an Element\n/// - Whether or not h-refinement is allowed in the given dimension\n/// - Whether or not the hybrid DG-Subcell scheme can be used\n///\n/// \\note Choose I1 to represent an open interval \\f$[-1, 1]\\f$\n///\n/// \\note Choose S1 to represent a periodic interval \\f$[0, 2 \\pi)\\f$\n///\n/// \\note In consecutive dimensions, choose S2Colatitude and S2Longitude to\n/// represent the surface of a sphere\n///\n/// \\note In consecutive dimensions, choose B2Radial and B2Angular to represent\n/// a disk (including the center) or cross-section of a cylinder\n///\n/// \\note In consecutive dimensions, choose B3Radial, B3Colatitude and\n/// B3Longitude to represent a sphere\n///\n/// \\note Currently h-refinement can only be done in dimensions with\n/// Topology::I1\n///\n/// \\note Currently the hybrid DG-Subcell scheme can be used only in Elements\n/// for which all dimensions have Topology::I1\nenum class Topology : uint8_t {\n  Uninitialized = 0,\n  I1 = 1,\n  S1 = 2,\n  S2Colatitude = 3,\n  S2Longitude = 4,\n  B2Radial = 5,\n  B2Angular = 6,\n  B3Radial = 7,\n  B3Colatitude = 8,\n  B3Longitude = 9,\n  CartoonSphere = 10,\n  CartoonCylinder = 11\n};\n\n/// Output operator for a Topology.\nstd::ostream& operator<<(std::ostream& os, Topology topology);\n\nnamespace topologies {\ntemplate <size_t VolumeDim>\nstatic constexpr auto hypercube = make_array<VolumeDim>(Topology::I1);\n\nstatic constexpr auto annulus = std::array{Topology::I1, Topology::S1};\n\nstatic constexpr auto disk =\n    std::array{Topology::B2Radial, Topology::B2Angular};\n\nstatic constexpr auto spherical_shell =\n    std::array{Topology::I1, Topology::S2Colatitude, Topology::S2Longitude};\n\nstatic constexpr auto cylindrical_shell =\n    std::array{Topology::I1, Topology::S1, Topology::I1};\n\nstatic constexpr auto full_cylinder =\n    std::array{Topology::B2Radial, Topology::B2Angular, Topology::I1};\n\nstatic constexpr auto full_sphere = std::array{\n    Topology::B3Radial, Topology::B3Colatitude, Topology::B3Longitude};\n\nstatic constexpr auto cartoon_sphere =\n    std::array{Topology::I1, Topology::CartoonSphere, Topology::CartoonSphere};\n\nstatic constexpr auto cartoon_cylinder =\n    std::array{Topology::I1, Topology::I1, Topology::CartoonCylinder};\n}  // namespace topologies\n\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/TrimMap.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\n/*!\n * \\brief Remove entries in `map_to_trim` that aren't face neighbors of the\n * `element`\n */\ntemplate <size_t Dim, typename T>\nvoid remove_nonexistent_neighbors(\n    const gsl::not_null<DirectionalIdMap<Dim, T>*> map_to_trim,\n    const Element<Dim>& element) {\n  std::array<DirectionalId<Dim>, maximum_number_of_neighbors(Dim)>\n      ids_to_remove{};\n  size_t ids_index = 0;\n  for (const auto& [neighbor_id, mesh] : *map_to_trim) {\n    const auto& neighbors = element.neighbors();\n    if (const auto neighbors_it = neighbors.find(neighbor_id.direction());\n        neighbors_it != neighbors.end()) {\n      if (const auto neighbor_it =\n              neighbors_it->second.ids().find(neighbor_id.id());\n          neighbor_it == neighbors_it->second.ids().end()) {\n        gsl::at(ids_to_remove, ids_index) = neighbor_id;\n        ++ids_index;\n      }\n    } else {\n      gsl::at(ids_to_remove, ids_index) = neighbor_id;\n      ++ids_index;\n    }\n  }\n  for (size_t i = 0; i < ids_index; ++i) {\n    map_to_trim->erase(gsl::at(ids_to_remove, i));\n  }\n}\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/ZCurve.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/Structure/ZCurve.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\n\ntemplate <size_t Dim>\nsize_t z_curve_index(const ElementId<Dim>& element_id) {\n  // for the bit manipulation of the element index, we need to interleave the\n  // indices in each dimension in order according to how many bits are in the\n  // index representation. This variable stores the refinement level and\n  // dimension index in ascending order of refinement level, representing a\n  // permutation of the dimensions\n  // pair<refinement level, dim index> in order of ascending refinement\n  std::array<std::pair<size_t, size_t>, Dim>\n      dimension_by_highest_refinement_level;\n  for (size_t i = 0; i < Dim; ++i) {\n    dimension_by_highest_refinement_level.at(i) =\n        std::make_pair(element_id.segment_id(i).refinement_level(), i);\n  }\n  alg::sort(dimension_by_highest_refinement_level);\n\n  size_t element_order_index = 0;\n\n  // 'gap' the lowest refinement direction bits as:\n  // ... x1 x0 -> ... x1 0 0 x0,\n  // then bitwise or in 'gap'ed and shifted next-lowest refinement direction\n  // bits as:\n  // ... y2 y1 y0 -> ... y2 0 y1 x1 0 y0 x0\n  // then bitwise or in 'gap'ed and shifted highest-refinement direction bits\n  // as:\n  // ... z3 z2 z1 z0 -> z3 z2 y2 z1 y1 x1 z0 y0 x0\n  // note that we must skip refinement-level 0 dimensions as though they are\n  // not present\n  size_t leading_gap = 0;\n  for (size_t i = 0; i < Dim; ++i) {\n    const size_t id_to_gap_and_shift =\n        element_id\n            .segment_id(\n                gsl::at(dimension_by_highest_refinement_level, i).second)\n            .index();\n    size_t total_gap = leading_gap;\n    if (gsl::at(dimension_by_highest_refinement_level, i).first > 0) {\n      ++leading_gap;\n    }\n    for (size_t bit_index = 0;\n         bit_index < gsl::at(dimension_by_highest_refinement_level, i).first;\n         ++bit_index) {\n      // This operation will not overflow for our present use of `ElementId`s.\n      // This technique densely assigns an ElementID a unique size_t identifier\n      // determining the Morton curve order, and `ElementId` supports refinement\n      // levels such that a global index within a block will fit in a 64-bit\n      // unsigned integer.\n      element_order_index |=\n          ((id_to_gap_and_shift & two_to_the(bit_index)) << total_gap);\n      for (size_t j = 0; j < Dim; ++j) {\n        if (i != j and\n            bit_index + 1 <\n                gsl::at(dimension_by_highest_refinement_level, j).first) {\n          ++total_gap;\n        }\n      }\n    }\n  }\n  return element_order_index;\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template size_t z_curve_index(const ElementId<GET_DIM(data)>& element_id);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Structure/ZCurve.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\ntemplate <size_t Dim>\nclass ElementId;\n\nnamespace domain {\n/// \\brief Computes the Z-curve index of a given `ElementId`\n///\n/// \\details The Z-curve index is computed by interleaving the bits of the\n/// `ElementId`'s `Segment` indices. Here is a sketch of a 2D block with 4x2\n/// elements, with bit indices and the resulting z-curve:\n///\n/// \\code\n///        x-->\n///        00  01  10  11\n/// y  0 |  0   2   4   6\n/// |    |\n/// v  1 |  1   3   5   7\n/// \\endcode\n///\n/// \\param element_id the `ElementId` for which to compute the Z-curve index\ntemplate <size_t Dim>\nsize_t z_curve_index(const ElementId<Dim>& element_id);\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ElementDistribution.hpp\n  FaceNormal.hpp\n  Faces.hpp\n  NeighborMesh.hpp\n  SurfaceJacobian.hpp\n  )\n"
  },
  {
    "path": "src/Domain/Tags/ElementDistribution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementDistribution.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel::OptionTags {\nstruct Parallelization;\n}  // namespace Parallel::OptionTags\n/// \\endcond\n\nnamespace domain {\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup ComputationalDomainGroup\nstruct ElementDistribution {\n  struct RoundRobin {};\n  using type = Options::Auto<ElementWeight, RoundRobin>;\n  static constexpr Options::String help = {\n      \"Weighting pattern to use for ZCurve element distribution. Specify \"\n      \"RoundRobin to just place each element on the next core.\"};\n  using group = Parallel::OptionTags::Parallelization;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// Tag that holds method for how to distribute the elements on the given\n/// resources.\n///\n/// \\note When not using local time stepping (LTS), a user cannot choose the\n/// NumGridPointsAndGridSpacing element distribution because grid spacing does\n/// not affect the computational cost at all. Therefore, if a user does choose\n/// NumGridPointsAndGridSpacing when not using LTS, an error will occur.\nstruct ElementDistribution : db::SimpleTag {\n  using type = std::optional<ElementWeight>;\n  using option_tags = tmpl::list<OptionTags::ElementDistribution>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& element_distribution) {\n    return element_distribution;\n  }\n};\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/Tags/FaceNormal.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Tags.hpp\"\n\nnamespace domain::Tags {\n\n/// The _normalized_ face normal\ntemplate <size_t Dim, typename LocalFrame = Frame::Inertial>\nusing FaceNormal =\n    ::Tags::Normalized<Tags::UnnormalizedFaceNormal<Dim, LocalFrame>>;\n\n/// The normalized face normal vector, i.e. the `FaceNormal` raised with the\n/// spatial metric.\ntemplate <size_t Dim, typename LocalFrame = Frame::Inertial>\nstruct FaceNormalVector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, LocalFrame>;\n};\n\n/// The magnitude of the _unnormalized_ face normal, see\n/// `::unnormalized_face_normal`\ntemplate <size_t Dim, typename LocalFrame = Frame::Inertial>\nusing UnnormalizedFaceNormalMagnitude =\n    ::Tags::Magnitude<Tags::UnnormalizedFaceNormal<Dim, LocalFrame>>;\n\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Tags/Faces.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/SubitemTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain {\nnamespace Tags {\n\n/// The `Tag` on element faces\ntemplate <size_t Dim, typename Tag>\nstruct Faces : db::PrefixTag, db::SimpleTag {\n  static constexpr size_t volume_dim = Dim;\n  using tag = Tag;\n  using type = DirectionMap<Dim, typename Tag::type>;\n};\n\n}  // namespace Tags\n\n/// Wrap `Tag` in `domain::Tags::Faces`, unless `Tag` is in the `VolumeTags`\n/// list\ntemplate <typename Dim, typename Tag, typename VolumeTags = tmpl::list<>>\nstruct make_faces_tag {\n  using type = tmpl::conditional_t<tmpl::list_contains_v<VolumeTags, Tag>, Tag,\n                                   domain::Tags::Faces<Dim::value, Tag>>;\n};\n\n/// Wrap all tags in `TagsList` in `domain::Tags::Faces`, except those in the\n/// `VolumeTags` list\ntemplate <size_t Dim, typename TagsList, typename VolumeTags = tmpl::list<>>\nusing make_faces_tags =\n    tmpl::transform<TagsList, make_faces_tag<tmpl::pin<tmpl::size_t<Dim>>,\n                                             tmpl::_1, tmpl::pin<VolumeTags>>>;\n\n}  // namespace domain\n\nnamespace db {\ntemplate <typename FacesTag>\nstruct Subitems<\n    FacesTag,\n    Requires<std::is_base_of_v<domain::Tags::Faces<FacesTag::volume_dim,\n                                                   typename FacesTag::tag>,\n                               FacesTag> and\n             tt::is_a_v<Variables, typename FacesTag::tag::type>>> {\n  static constexpr size_t Dim = FacesTag::volume_dim;\n  using VariablesTag = typename FacesTag::tag;\n  using VectorType = typename VariablesTag::type::vector_type;\n\n  template <typename LocalTag>\n  using faces_tag = domain::Tags::Faces<Dim, LocalTag>;\n\n  using tag = faces_tag<VariablesTag>;\n  using type =\n      db::wrap_tags_in<faces_tag, typename VariablesTag::type::tags_list>;\n\n  template <typename Subtag>\n  static void create_item(\n      const gsl::not_null<typename tag::type*> parent_value,\n      const gsl::not_null<typename Subtag::type*> sub_value) {\n    sub_value->clear();\n    for (auto& [direction, all_parent_vars] : *parent_value) {\n      auto& parent_var = get<typename Subtag::tag>(all_parent_vars);\n      auto& sub_var = (*sub_value)[direction];\n      for (auto vars_it = parent_var.begin(), sub_var_it = sub_var.begin();\n           vars_it != parent_var.end(); ++vars_it, ++sub_var_it) {\n        sub_var_it->set_data_ref(&*vars_it);\n      }\n    }\n  }\n\n  // The `return_type` can be anything for Subitems because the DataBox figures\n  // out the correct return type, we just use the `return_type` type alias to\n  // signal to the DataBox we want mutating behavior.\n  using return_type = NoSuchType;\n\n  template <typename Subtag>\n  static void create_compute_item(\n      const gsl::not_null<typename Subtag::type*> sub_value,\n      const typename tag::type& parent_value) {\n    for (const auto& [direction, all_parent_vars] : parent_value) {\n      const auto& parent_var = get<typename Subtag::tag>(all_parent_vars);\n      auto& sub_var = (*sub_value)[direction];\n      auto sub_var_it = sub_var.begin();\n      for (auto vars_it = parent_var.begin(); vars_it != parent_var.end();\n           ++vars_it, ++sub_var_it) {\n        // clang-tidy: do not use const_cast\n        // The DataBox will only give out a const reference to the\n        // result of a compute item.  Here, that is a reference to a\n        // const map to Tensors of DataVectors.  There is no (publicly\n        // visible) indirection there, so having the map const will\n        // allow only allow const access to the contained DataVectors,\n        // so no modification through the pointer cast here is\n        // possible.\n        sub_var_it->set_data_ref(const_cast<VectorType*>(&*vars_it));  // NOLINT\n      }\n    }\n  }\n};\n}  // namespace db\n\nnamespace Tags {\n/// \\brief Specialization of a subitem tag for a compute tag that inherits off\n/// `domain::Tags::Faces`.\n///\n/// This tag holds a map from faces to _one_ of the subitems, typically one\n/// tensor in a Variables. The `FacesSubitemTag` represents the particular\n/// tensor on faces, and the `FacesComputeTag` represents the full Variables on\n/// faces.\ntemplate <typename FacesSubitemTag, typename FacesComputeTag>\nstruct Subitem<FacesSubitemTag, FacesComputeTag,\n               Requires<std::is_base_of_v<\n                   domain::Tags::Faces<FacesComputeTag::volume_dim,\n                                       typename FacesComputeTag::tag>,\n                   FacesComputeTag>>> : db::ComputeTag,\n                                        FacesSubitemTag {\n  using base = FacesSubitemTag;\n  using return_type = typename base::type;\n  using parent_tag = FacesComputeTag;\n  using argument_tags = tmpl::list<typename parent_tag::base>;\n  static void function(const gsl::not_null<return_type*> subitems,\n                       const typename parent_tag::type& parent_value) {\n    ::db::Subitems<parent_tag>::template create_compute_item<base>(\n        subitems, parent_value);\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Domain/Tags/NeighborMesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\n/// \\cond\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace domain::Tags {\n/*!\n * \\brief Holds the mesh of each neighboring element, oriented to the\n * host's logical frame.\n *\n * This knowledge can be used to determine the geometry of mortars between\n * elements. It is kept up to date by AMR.\n *\n * For DG-FD hybrid methods this is necessary to determine what numerical method\n * the neighbor is using. This knowledge can be used for optimizing code.\n */\ntemplate <size_t Dim>\nstruct NeighborMesh : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, ::Mesh<Dim>>;\n};\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Tags/SurfaceJacobian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace domain::Tags {\n\n/*!\n * \\brief The determinant of the induced Jacobian on a surface\n *\n * The surface Jacobian determinant on a surface \\f$\\Sigma\\f$ with constant\n * logical coordinate \\f$\\xi^i\\f$ is:\n *\n * \\f{equation}\n * J^\\Sigma = J \\sqrt{\\gamma^{jk} (J^{-1})^i_j (J^{-1})^i_k}\n * \\f}\n *\n * where \\f$J^i_j = \\partial x^i / \\xi^j\\f$ is the volume Jacobian with\n * determinant \\f$J\\f$ and inverse \\f$(J^{-1})^i_j = \\partial \\xi^i / \\partial\n * x^j\\f$. Note that the square root in the expression above is the magnitude of\n * the unnormalized face normal, where \\f$\\gamma^{jk}\\f$ is the inverse spatial\n * metric.\n */\ntemplate <typename SourceFrame, typename TargetFrame>\nstruct DetSurfaceJacobian : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines tags related to domain quantities\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/Subitems.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Domain;\ntemplate <size_t VolumeDim, typename Frame>\nclass ElementMap;\ntemplate <size_t VolumeDim>\nclass Mesh;\n/// \\endcond\n\nnamespace domain {\n/// \\ingroup ComputationalDomainGroup\n/// \\brief %Tags for the domain.\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The ::Element associated with the DataBox\ntemplate <size_t VolumeDim>\nstruct Element : db::SimpleTag {\n  using type = ::Element<VolumeDim>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The computational grid of the Element in the DataBox\n/// \\details The corresponding interface tag uses Mesh::slice_through to compute\n/// the mesh on the face of the element.\ntemplate <size_t VolumeDim>\nstruct Mesh : db::SimpleTag {\n  using type = ::Mesh<VolumeDim>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The coordinate map from the ElementLogical frame to the TargetFrame\ntemplate <size_t VolumeDim, typename TargetFrame = Frame::Inertial>\nstruct ElementMap : db::SimpleTag {\n  static constexpr size_t dim = VolumeDim;\n  using target_frame = TargetFrame;\n  using source_frame = Frame::ElementLogical;\n\n  static std::string name() {\n    return \"ElementMap(\" + get_output(TargetFrame{}) + \")\";\n  }\n  using type = ::ElementMap<VolumeDim, TargetFrame>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The coordinates in a given frame.\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates : db::SimpleTag {\n  static std::string name() { return get_output(Frame{}) + \"Coordinates\"; }\n  using type = tnsr::I<DataVector, Dim, Frame>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The coordinates in the target frame of `MapTag`. The `SourceCoordsTag`'s\n/// frame must be the source frame of `MapTag`\ntemplate <class MapTag, class SourceCoordsTag,\n          template <size_t, class> class CoordinatesTag = Coordinates>\nstruct MappedCoordinates\n    : CoordinatesTag<MapTag::dim, typename MapTag::target_frame>,\n      db::ComputeTag {\n  using base = CoordinatesTag<MapTag::dim, typename MapTag::target_frame>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<MapTag, SourceCoordsTag>;\n  static constexpr auto function(\n      const gsl::not_null<return_type*> target_coords,\n      const typename MapTag::type& element_map,\n      const tnsr::I<DataVector, MapTag::dim, typename MapTag::source_frame>&\n          source_coords) {\n    *target_coords = element_map(source_coords);\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The inverse Jacobian from the source frame to the target frame.\n///\n/// Specifically, \\f$\\partial x^{\\bar{i}} / \\partial x^i\\f$, where \\f$\\bar{i}\\f$\n/// denotes the source frame and \\f$i\\f$ denotes the target frame.\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct InverseJacobian : db::SimpleTag {\n  static std::string name() {\n    return \"InverseJacobian(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n  using type = ::InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// Computes the inverse Jacobian of the map held by `MapTag` at the coordinates\n/// held by `SourceCoordsTag`. The coordinates must be in the source frame of\n/// the map.\ntemplate <typename MapTag, typename SourceCoordsTag>\nstruct InverseJacobianCompute\n    : InverseJacobian<MapTag::dim, typename MapTag::source_frame,\n                      typename MapTag::target_frame>,\n      db::ComputeTag {\n  using base = InverseJacobian<MapTag::dim, typename MapTag::source_frame,\n                               typename MapTag::target_frame>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<MapTag, SourceCoordsTag>;\n  static constexpr auto function(\n      const gsl::not_null<return_type*> inv_jacobian,\n      const typename MapTag::type& element_map,\n      const tnsr::I<DataVector, MapTag::dim, typename MapTag::source_frame>&\n          source_coords) {\n    *inv_jacobian = element_map.inv_jacobian(source_coords);\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The Jacobian from the source frame to the target frame.\n///\n/// Specifically, \\f$\\partial x^{i} / \\partial \\xi^{\\bar{i}}\\f$, where\n/// \\f$\\xi^\\bar{i}\\f$ denotes the source frame and \\f$x^i\\f$ denotes the target\n/// frame.\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct Jacobian : db::SimpleTag {\n  static std::string name() {\n    return \"Jacobian(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n  using type = ::Jacobian<DataVector, Dim, SourceFrame, TargetFrame>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// Computes the Jacobian of the map from the `InverseJacobian<Dim, SourceFrame,\n/// TargetFrame>` tag.\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct JacobianCompute : Jacobian<Dim, SourceFrame, TargetFrame>,\n                         db::ComputeTag {\n  using base = Jacobian<Dim, SourceFrame, TargetFrame>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<InverseJacobian<Dim, SourceFrame, TargetFrame>>;\n  static constexpr auto function(\n      const gsl::not_null<return_type*> jacobian,\n      const ::InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&\n          inv_jac) {\n    *jacobian = determinant_and_inverse(inv_jac).second;\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The determinant of the inverse Jacobian from the source frame to the\n/// target frame.\ntemplate <typename SourceFrame, typename TargetFrame>\nstruct DetInvJacobian : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"DetInvJacobian(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// Computes the determinant of the inverse Jacobian.\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct DetInvJacobianCompute : db::ComputeTag,\n                               DetInvJacobian<SourceFrame, TargetFrame> {\n  using base = DetInvJacobian<SourceFrame, TargetFrame>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<InverseJacobian<Dim, SourceFrame, TargetFrame>>;\n  static void function(const gsl::not_null<return_type*> det_inv_jac,\n                       const ::InverseJacobian<DataVector, Dim, SourceFrame,\n                                               TargetFrame>& inv_jac) {\n    determinant(det_inv_jac, inv_jac);\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The determinant of the Jacobian from the source frame to the target\n/// frame.\ntemplate <typename SourceFrame, typename TargetFrame>\nstruct DetJacobian : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"DetJacobian(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief The inverse Jacobian times the determinant of the Jacobian.\n///\n/// This quantity is divergence-free analytically. See\n/// `::dg::metric_identity_det_jac_times_inv_jac` for more information.\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct DetTimesInvJacobian : db::SimpleTag {\n  using type = ::InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>;\n  static std::string name() {\n    return \"DetTimesInvJacobian(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n};\n\n/// @{\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The set of directions to neighboring Elements\ntemplate <size_t VolumeDim>\nstruct InternalDirections : db::SimpleTag {\n  static constexpr size_t volume_dim = VolumeDim;\n  using type = std::unordered_set<::Direction<VolumeDim>>;\n};\n\ntemplate <size_t VolumeDim>\nstruct InternalDirectionsCompute : InternalDirections<VolumeDim>,\n                                   db::ComputeTag {\n  static constexpr size_t volume_dim = VolumeDim;\n  using base = InternalDirections<VolumeDim>;\n  using return_type = std::unordered_set<::Direction<VolumeDim>>;\n  using argument_tags = tmpl::list<Element<VolumeDim>>;\n  static void function(const gsl::not_null<return_type*> directions,\n                       const ::Element<VolumeDim>& element) {\n    for (const auto& direction_neighbors : element.neighbors()) {\n      directions->insert(direction_neighbors.first);\n    }\n  }\n};\n/// @}\n\n/// @{\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The set of directions which correspond to external boundaries.\n/// Used for representing data on the interior side of the external boundary\n/// faces.\ntemplate <size_t VolumeDim>\nstruct BoundaryDirectionsInterior : db::SimpleTag {\n  static constexpr size_t volume_dim = VolumeDim;\n  using type = std::unordered_set<::Direction<VolumeDim>>;\n};\n\ntemplate <size_t VolumeDim>\nstruct BoundaryDirectionsInteriorCompute\n    : BoundaryDirectionsInterior<VolumeDim>,\n      db::ComputeTag {\n  static constexpr size_t volume_dim = VolumeDim;\n  using base = BoundaryDirectionsInterior<VolumeDim>;\n  using return_type = std::unordered_set<::Direction<VolumeDim>>;\n  using argument_tags = tmpl::list<Element<VolumeDim>>;\n  static void function(const gsl::not_null<return_type*> directions,\n                       const ::Element<VolumeDim>& element) {\n    *directions = element.external_boundaries();\n  }\n};\n/// @}\n\n/// @{\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The set of directions which correspond to external boundaries. To be used\n/// to represent data which exists on the exterior side of the external boundary\n/// faces.\ntemplate <size_t VolumeDim>\nstruct BoundaryDirectionsExterior : db::SimpleTag {\n  static constexpr size_t volume_dim = VolumeDim;\n  using type = std::unordered_set<::Direction<VolumeDim>>;\n};\n\ntemplate <size_t VolumeDim>\nstruct BoundaryDirectionsExteriorCompute\n    : BoundaryDirectionsExterior<VolumeDim>,\n      db::ComputeTag {\n  static constexpr size_t volume_dim = VolumeDim;\n  using base = BoundaryDirectionsExterior<VolumeDim>;\n  using return_type = std::unordered_set<::Direction<VolumeDim>>;\n  using argument_tags = tmpl::list<Element<VolumeDim>>;\n  static constexpr auto function(const gsl::not_null<return_type*> directions,\n                                 const ::Element<VolumeDim>& element) {\n    *directions = element.external_boundaries();\n  }\n};\n/// @}\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// \\brief Tag which is either a SimpleTag for quantities on an\n/// interface, base tag to a compute item which acts on tags on an interface, or\n/// base tag to a compute item which slices a tag from the volume to an\n/// interface.\n///\n/// The contained object will be a map from ::Direction to the item type of\n/// `Tag`, with the set of directions being those produced by `DirectionsTag`.\n///\n/// If a SimpleTag is desired on the interface, then this tag can be added to\n/// the DataBox directly. If a ComputeTag which acts on Tags on the interface is\n/// desired, then the tag should be added using `InterfaceCompute`. If a\n/// ComputeTag which slices a TensorTag or a VariablesTag in the volume to an\n/// Interface is desired, then it should be added using `Slice`. In all cases,\n/// the tag can then be retrieved using `Tags::Interface<DirectionsTag, Tag>`.\n///\n/// \\tparam DirectionsTag the item of directions\n/// \\tparam Tag the tag labeling the item\n///\n/// \\see InterfaceCompute, Slice\ntemplate <typename DirectionsTag, typename Tag>\nstruct Interface : db::SimpleTag {\n  static_assert(db::is_simple_tag_v<DirectionsTag>);\n  static_assert(db::is_simple_tag_v<Tag>);\n  static std::string name() {\n    return \"Interface<\" + db::tag_name<DirectionsTag>() + \", \" +\n           db::tag_name<Tag>() + \">\";\n  };\n  using tag = Tag;\n  using type = std::unordered_map<::Direction<DirectionsTag::volume_dim>,\n                                  typename Tag::type>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// ::Direction to an interface\ntemplate <size_t VolumeDim>\nstruct Direction : db::SimpleTag {\n  using type = ::Direction<VolumeDim>;\n};\n\n}  // namespace Tags\n}  // namespace domain\n\nnamespace db {\nnamespace detail {\ntemplate <typename DirectionsTag, typename VariablesTag>\nstruct InterfaceSubitemsImpl {\n  using type = tmpl::transform<\n      typename VariablesTag::type::tags_list,\n      tmpl::bind<domain::Tags::Interface, tmpl::pin<DirectionsTag>, tmpl::_1>>;\n\n  using tag = domain::Tags::Interface<DirectionsTag, VariablesTag>;\n\n  template <typename Subtag>\n  static void create_item(\n      const gsl::not_null<typename tag::type*> parent_value,\n      const gsl::not_null<typename Subtag::type*> sub_value) {\n    sub_value->clear();\n    for (auto& direction_vars : *parent_value) {\n      const auto& direction = direction_vars.first;\n      auto& parent_vars = get<typename Subtag::tag>(direction_vars.second);\n      auto& sub_var = (*sub_value)[direction];\n      for (auto vars_it = parent_vars.begin(), sub_var_it = sub_var.begin();\n           vars_it != parent_vars.end(); ++vars_it, ++sub_var_it) {\n        sub_var_it->set_data_ref(&*vars_it);\n      }\n    }\n  }\n\n  // The `return_type` can be anything for Subitems because the DataBox figures\n  // out the correct return type, we just use the `return_type` type alias to\n  // signal to the DataBox we want mutating behavior.\n  using return_type = NoSuchType;\n\n  template <typename Subtag>\n  static void create_compute_item(\n      const gsl::not_null<typename Subtag::type*> sub_value,\n      const typename tag::type& parent_value) {\n    for (const auto& direction_vars : parent_value) {\n      const auto& direction = direction_vars.first;\n      const auto& parent_vars =\n          get<typename Subtag::tag>(direction_vars.second);\n      auto& sub_var = (*sub_value)[direction];\n      auto sub_var_it = sub_var.begin();\n      for (auto vars_it = parent_vars.begin();\n           vars_it != parent_vars.end(); ++vars_it, ++sub_var_it) {\n        // clang-tidy: do not use const_cast\n        // The DataBox will only give out a const reference to the\n        // result of a compute item.  Here, that is a reference to a\n        // const map to Tensors of DataVectors.  There is no (publicly\n        // visible) indirection there, so having the map const will\n        // allow only allow const access to the contained DataVectors,\n        // so no modification through the pointer cast here is\n        // possible.\n        sub_var_it->set_data_ref(const_cast<DataVector*>(&*vars_it));  // NOLINT\n      }\n    }\n  }\n};\n}  // namespace detail\n\ntemplate <typename DirectionsTag, typename VariablesTag>\nstruct Subitems<domain::Tags::Interface<DirectionsTag, VariablesTag>,\n                Requires<tt::is_a_v<Variables, typename VariablesTag::type>>>\n    : detail::InterfaceSubitemsImpl<DirectionsTag, VariablesTag> {};\n\n}  // namespace db\n"
  },
  {
    "path": "src/Domain/TagsCharacteristicSpeeds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"  // For Tags::Normalized\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/InterfaceHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain {\nnamespace Tags {\n/// Compute the characteristic speeds on the moving mesh given the\n/// characteristic speeds if the mesh were stationary.\n///\n/// \\note Assumes that `typename CharSpeedsComputeTag::return_type` is a\n/// `std::array<DataVector, NumberOfCharSpeeds>`\ntemplate <typename CharSpeedsComputeTag, size_t Dim>\nstruct CharSpeedCompute : CharSpeedsComputeTag::base, db::ComputeTag {\n  using base = typename CharSpeedsComputeTag::base;\n  using return_type = typename CharSpeedsComputeTag::return_type;\n\n  template <typename... Ts, typename T, size_t NumberOfCharSpeeds>\n  static void function(\n      const gsl::not_null<std::array<T, NumberOfCharSpeeds>*> result,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          grid_velocity,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_covector,\n      const Ts&... ts) {\n    // Note that while the CharSpeedsComputeTag almost certainly also needs the\n    // unit normal covector for computing the original characteristic speeds, we\n    // don't know which of the `ts` it is, and thus we need the unit normal\n    // covector to be passed explicitly.\n    CharSpeedsComputeTag::function(result, ts...);\n    if (grid_velocity.has_value()) {\n      const Scalar<DataVector> normal_dot_velocity =\n          dot_product(*grid_velocity, unit_normal_covector);\n      for (size_t i = 0; i < result->size(); ++i) {\n        gsl::at(*result, i) -= get(normal_dot_velocity);\n      }\n    }\n  }\n\n  using argument_tags =\n      tmpl::push_front<typename CharSpeedsComputeTag::argument_tags,\n                       MeshVelocity<Dim, Frame::Inertial>,\n                       ::Tags::Normalized<UnnormalizedFaceNormal<Dim>>>;\n  using volume_tags = get_volume_tags<CharSpeedsComputeTag>;\n};\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Domain/TagsTimeDependent.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Domain/TagsTimeDependent.hpp\"\n\n#include <memory>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain::Tags {\ntemplate <size_t Dim>\nvoid InertialFromGridCoordinatesCompute<Dim>::function(\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        target_coords,\n    const tnsr::I<DataVector, Dim, Frame::Grid>& source_coords,\n    const std::optional<std::tuple<\n        tnsr::I<DataVector, Dim, Frame::Inertial>,\n        ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n        ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n        tnsr::I<DataVector, Dim, Frame::Inertial>>>&\n        grid_to_inertial_quantities) {\n  if (not grid_to_inertial_quantities.has_value()) {\n    // We use a const_cast to point the data into the existing allocation\n    // inside `source_coords` to avoid copying. This is safe because the output\n    // of a compute tag is immutable.\n    for (size_t i = 0; i < Dim; ++i) {\n      target_coords->get(i).set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          &const_cast<DataVector&>(source_coords.get(i)));\n    }\n  } else {\n    // We use a const_cast to point the data into the existing allocation\n    // inside `grid_to_inertial_quantities` to avoid copying. This is\n    // effectively unpacking the tuple. This is safe because the output\n    // of a compute tag is immutable.\n    for (size_t i = 0; i < Dim; ++i) {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n      target_coords->get(i).set_data_ref(&const_cast<DataVector&>(\n          std::get<0>(*grid_to_inertial_quantities).get(i)));\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid GridToInertialInverseJacobian<Dim>::function(\n    const gsl::not_null<\n        ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>*>\n        inv_jac_grid_to_inertial,\n    const std::optional<std::tuple<\n        tnsr::I<DataVector, Dim, Frame::Inertial>,\n        ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n        ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n        tnsr::I<DataVector, Dim, Frame::Inertial>>>&\n        grid_to_inertial_quantities) {\n  if (not grid_to_inertial_quantities.has_value()) {\n    ERROR(\n        \"Should not request Grid to Inertial jacobian for a non-moving mesh \"\n        \"because it is the identity.\");\n  } else {\n    const auto& inv_jac_grid_to_inertial_tuple =\n        std::get<1>(*grid_to_inertial_quantities);\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        inv_jac_grid_to_inertial->get(i, j).set_data_ref(\n            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n            &const_cast<DataVector&>(inv_jac_grid_to_inertial_tuple.get(i, j)));\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid ElementToInertialInverseJacobian<Dim>::function(\n    const gsl::not_null<::InverseJacobian<\n        DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>\n        inv_jac_logical_to_inertial,\n    const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Grid>& inv_jac_logical_to_grid,\n    const std::optional<std::tuple<\n        tnsr::I<DataVector, Dim, Frame::Inertial>,\n        ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n        ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n        tnsr::I<DataVector, Dim, Frame::Inertial>>>&\n        grid_to_inertial_quantities) {\n  if (not grid_to_inertial_quantities.has_value()) {\n    // We use a const_cast to point the data into the existing allocation\n    // inside `inv_jac_logical_to_grid` to avoid copying. This is safe because\n    // the output of a compute tag is immutable.\n    for (size_t i = 0; i < inv_jac_logical_to_inertial->size(); ++i) {\n      inv_jac_logical_to_inertial->operator[](i).set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          &const_cast<DataVector&>(inv_jac_logical_to_grid[i]));\n    }\n  } else {\n    const auto& inv_jac_grid_to_inertial =\n        std::get<1>(*grid_to_inertial_quantities);\n    for (size_t logical_i = 0; logical_i < Dim; ++logical_i) {\n      for (size_t inertial_i = 0; inertial_i < Dim; ++inertial_i) {\n        inv_jac_logical_to_inertial->get(logical_i, inertial_i) =\n            inv_jac_logical_to_grid.get(logical_i, 0) *\n            inv_jac_grid_to_inertial.get(0, inertial_i);\n        for (size_t grid_i = 1; grid_i < Dim; ++grid_i) {\n          inv_jac_logical_to_inertial->get(logical_i, inertial_i) +=\n              inv_jac_logical_to_grid.get(logical_i, grid_i) *\n              inv_jac_grid_to_inertial.get(grid_i, inertial_i);\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid InertialMeshVelocityCompute<Dim>::function(\n    const gsl::not_null<return_type*> mesh_velocity,\n    const std::optional<std::tuple<\n        tnsr::I<DataVector, Dim, Frame::Inertial>,\n        ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n        ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n        tnsr::I<DataVector, Dim, Frame::Inertial>>>&\n        grid_to_inertial_quantities) {\n  if (not grid_to_inertial_quantities.has_value()) {\n    *mesh_velocity = std::nullopt;\n  } else {\n    if (not *mesh_velocity) {\n      *mesh_velocity = typename return_type::value_type{};\n    }\n    // We use a const_cast to point the data into the existing allocation\n    // inside `grid_to_inertial_quantities` to avoid copying. This is\n    // effectively unpacking the tuple. This is safe because the output\n    // of a compute tag is immutable.\n    for (size_t i = 0; i < Dim; ++i) {\n      mesh_velocity->value().operator[](i).set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          &const_cast<DataVector&>(\n              std::get<3>(*grid_to_inertial_quantities).get(i)));\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                     \\\n  template struct InertialFromGridCoordinatesCompute<DIM(data)>; \\\n  template struct ElementToInertialInverseJacobian<DIM(data)>;   \\\n  template struct InertialMeshVelocityCompute<DIM(data)>;        \\\n  template struct GridToInertialInverseJacobian<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace domain::Tags\n"
  },
  {
    "path": "src/Domain/TagsTimeDependent.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"  // For Tags::Normalized\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace domain {\n/// \\ingroup ComputationalDomainGroup\n/// \\brief %Tags for the domain.\nnamespace Tags {\n/// The Inertial coordinates, the inverse Jacobian from the Grid to the Inertial\n/// frame, the Jacobian from the Grid to the Inertial frame, and the Inertial\n/// mesh velocity.\n///\n/// The type is a `std::optional`, which, when is not valid, signals that the\n/// mesh is not moving. Thus,\n/// `coordinates_velocity_and_jacobian.has_value()` can be used to check\n/// if the mesh is moving.\ntemplate <size_t Dim>\nstruct CoordinatesMeshVelocityAndJacobians : db::SimpleTag {\n  using type = std::optional<std::tuple<\n      tnsr::I<DataVector, Dim, Frame::Inertial>,\n      ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n      ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n      tnsr::I<DataVector, Dim, Frame::Inertial>>>;\n};\n\n/// Computes the Inertial coordinates, the inverse Jacobian from the Grid to the\n/// Inertial frame, the Jacobian from the Grid to the Inertial frame, and the\n/// Inertial mesh velocity.\ntemplate <typename MapTagGridToInertial>\nstruct CoordinatesMeshVelocityAndJacobiansCompute\n    : CoordinatesMeshVelocityAndJacobians<MapTagGridToInertial::dim>,\n      db::ComputeTag {\n  static constexpr size_t dim = MapTagGridToInertial::dim;\n  using base = CoordinatesMeshVelocityAndJacobians<dim>;\n\n  using return_type = typename base::type;\n\n  static void function(\n      const gsl::not_null<return_type*> result,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, dim>&\n          grid_to_inertial_map,\n      const tnsr::I<DataVector, dim, Frame::Grid>& source_coords,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    // Use identity to signal time-independent\n    if (not grid_to_inertial_map.is_identity()) {\n      *result = grid_to_inertial_map.coords_frame_velocity_jacobians(\n          source_coords, time, functions_of_time);\n    } else {\n      *result = std::nullopt;\n    }\n  }\n\n  using argument_tags =\n      tmpl::list<MapTagGridToInertial, Tags::Coordinates<dim, Frame::Grid>,\n                 ::Tags::Time, Tags::FunctionsOfTime>;\n};\n\n/// Computes the Inertial coordinates from\n/// `CoordinatesVelocityAndJacobians`\ntemplate <size_t Dim>\nstruct InertialFromGridCoordinatesCompute\n    : Tags::Coordinates<Dim, Frame::Inertial>,\n      db::ComputeTag {\n  using base = Tags::Coordinates<Dim, Frame::Inertial>;\n  using return_type = typename base::type;\n\n  static void function(\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> target_coords,\n      const tnsr::I<DataVector, Dim, Frame::Grid>& source_coords,\n      const std::optional<std::tuple<\n          tnsr::I<DataVector, Dim, Frame::Inertial>,\n          ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n          ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n          tnsr::I<DataVector, Dim, Frame::Inertial>>>&\n          grid_to_inertial_quantities);\n\n  using argument_tags = tmpl::list<Tags::Coordinates<Dim, Frame::Grid>,\n                                   CoordinatesMeshVelocityAndJacobians<Dim>>;\n};\n\n/// Computes the Grid to Inertial inverse Jacobian from\n/// `CoordinatesVelocityAndJacobians`. If the mesh is not moving, requesting\n/// this tag will throw an error because the Jacobian is just the identity.\ntemplate <size_t Dim>\nstruct GridToInertialInverseJacobian\n    : Tags::InverseJacobian<Dim, Frame::Grid, Frame::Inertial>,\n      db::ComputeTag {\n  using base = Tags::InverseJacobian<Dim, Frame::Grid, Frame::Inertial>;\n  using return_type = typename base::type;\n\n  static void function(\n      gsl::not_null<\n          ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>*>\n          inv_jac_grid_to_inertial,\n      const std::optional<std::tuple<\n          tnsr::I<DataVector, Dim, Frame::Inertial>,\n          ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n          ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n          tnsr::I<DataVector, Dim, Frame::Inertial>>>&\n          grid_to_inertial_quantities);\n\n  using argument_tags = tmpl::list<CoordinatesMeshVelocityAndJacobians<Dim>>;\n};\n\n/// Computes the Logical to Inertial inverse Jacobian from\n/// `CoordinatesVelocityAndJacobians`\ntemplate <size_t Dim>\nstruct ElementToInertialInverseJacobian\n    : Tags::InverseJacobian<Dim, Frame::ElementLogical, Frame::Inertial>,\n      db::ComputeTag {\n  using base =\n      Tags::InverseJacobian<Dim, Frame::ElementLogical, Frame::Inertial>;\n  using return_type = typename base::type;\n\n  static void function(\n      gsl::not_null<::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                      Frame::Inertial>*>\n          inv_jac_logical_to_inertial,\n      const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                              Frame::Grid>& inv_jac_logical_to_grid,\n      const std::optional<std::tuple<\n          tnsr::I<DataVector, Dim, Frame::Inertial>,\n          ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n          ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n          tnsr::I<DataVector, Dim, Frame::Inertial>>>&\n          grid_to_inertial_quantities);\n\n  using argument_tags =\n      tmpl::list<Tags::InverseJacobian<Dim, Frame::ElementLogical, Frame::Grid>,\n                 CoordinatesMeshVelocityAndJacobians<Dim>>;\n};\n\n/// The mesh velocity\n///\n/// The type is a `std::optional`, which when it is not set indicates that the\n/// mesh is not moving.\ntemplate <size_t Dim, typename Frame = ::Frame::Inertial>\nstruct MeshVelocity : db::SimpleTag {\n  using type = std::optional<tnsr::I<DataVector, Dim, Frame>>;\n};\n\n/// Computes the Inertial mesh velocity from `CoordinatesVelocityAndJacobians`\n///\n/// The type is a `std::optional`, which when it is not set indicates that the\n/// mesh is not moving.\ntemplate <size_t Dim>\nstruct InertialMeshVelocityCompute : MeshVelocity<Dim, Frame::Inertial>,\n                                     db::ComputeTag {\n  using base = MeshVelocity<Dim, Frame::Inertial>;\n  using return_type = typename base::type;\n\n  static void function(\n      gsl::not_null<return_type*> mesh_velocity,\n      const std::optional<std::tuple<\n          tnsr::I<DataVector, Dim, Frame::Inertial>,\n          ::InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n          ::Jacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>,\n          tnsr::I<DataVector, Dim, Frame::Inertial>>>&\n          grid_to_inertial_quantities);\n\n  using argument_tags = tmpl::list<CoordinatesMeshVelocityAndJacobians<Dim>>;\n};\n\n/// The divergence of the mesh velocity\nstruct DivMeshVelocity : db::SimpleTag {\n  using type = std::optional<Scalar<DataVector>>;\n  static std::string name() { return \"div(MeshVelocity)\"; }\n};\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/Elliptic/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InitializeAnalyticSolution.hpp\n  InitializeBackgroundFields.hpp\n  InitializeFields.hpp\n  InitializeFixedSources.hpp\n  RunEventsAndTriggers.hpp\n  )\n"
  },
  {
    "path": "src/Elliptic/Actions/InitializeAnalyticSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace elliptic::Actions {\n\n/// @{\n/*!\n * \\brief Place the analytic solution of the system fields in the DataBox.\n *\n * The `::Tags::AnalyticSolutions` tag retrieved from the DataBox will hold\n * a `std::optional`. The analytic solution is only evaluated and stored in the\n * DataBox if the `BackgroundTag` holds a type that inherits from the\n * `AnalyticSolutionType`.\n *\n * Uses:\n * - DataBox:\n *   - `AnalyticSolutionTag` or `BackgroundTag`\n *   - `Tags::Coordinates<Dim, Frame::Inertial>`\n *\n * DataBox:\n * - Adds:\n *   - `::Tags::AnalyticSolutions<AnalyticSolutionFields>`\n */\ntemplate <size_t Dim, typename BackgroundTag, typename AnalyticSolutionFields,\n          typename AnalyticSolutionType>\nstruct InitializeOptionalAnalyticSolution\n    : tt::ConformsTo<::amr::protocols::Projector> {\n private:\n  using analytic_fields_tag = ::Tags::AnalyticSolutions<AnalyticSolutionFields>;\n\n public:  // Iterable action\n  using simple_tags = tmpl::list<analytic_fields_tag>;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<BackgroundTag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeOptionalAnalyticSolution>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // DataBox mutator, amr::protocols::Projector\n  using return_tags = tmpl::list<analytic_fields_tag>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Mesh<Dim>,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>, BackgroundTag,\n                 Parallel::Tags::Metavariables>;\n\n  template <typename Background, typename Metavariables, typename... AmrData>\n  static void apply(const gsl::not_null<typename analytic_fields_tag::type*>\n                        analytic_solution_fields,\n                    const Mesh<Dim>& mesh,\n                    const tnsr::I<DataVector, Dim> inertial_coords,\n                    const Background& background, const Metavariables& /*meta*/,\n                    const AmrData&... amr_data) {\n    if constexpr (sizeof...(AmrData) == 1) {\n      if constexpr (std::is_same_v<AmrData...,\n                                   std::pair<Mesh<Dim>, Element<Dim>>>) {\n        if (((mesh == amr_data.first) and ...)) {\n          // This element hasn't changed during AMR. Nothing to do.\n          return;\n        }\n      }\n    }\n\n    const auto analytic_solution =\n        dynamic_cast<const AnalyticSolutionType*>(&background);\n    if (analytic_solution != nullptr) {\n      using factory_classes = typename std::decay_t<\n          Metavariables>::factory_creation::factory_classes;\n      *analytic_solution_fields = call_with_dynamic_type<\n          Variables<AnalyticSolutionFields>,\n          tmpl::at<factory_classes, AnalyticSolutionType>>(\n          analytic_solution, [&inertial_coords](const auto* const derived) {\n            return variables_from_tagged_tuple(\n                derived->variables(inertial_coords, AnalyticSolutionFields{}));\n          });\n    } else {\n      *analytic_solution_fields = std::nullopt;\n    }\n  }\n};\n/// @}\n}  // namespace elliptic::Actions\n"
  },
  {
    "path": "src/Elliptic/Actions/InitializeBackgroundFields.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace elliptic::Actions {\n\n/*!\n * \\brief Initialize the variable-independent background fields for an elliptic\n * solve.\n *\n * Examples for background fields would be a background metric, associated\n * curvature quantities, or matter sources such as a mass-density in the XCTS\n * equations.\n *\n * This action retrieves the `System::background_fields` from the\n * `BackgroundTag`. It invokes the `variables` function with the inertial\n * coordinates, the element's `Mesh` and the element's inverse Jacobian. These\n * arguments allow computing numeric derivatives, if necessary.\n *\n * Uses:\n * - System:\n *   - `background_fields`\n * - DataBox:\n *   - `BackgroundTag`\n *   - `domain::Tags::Coordinates<Dim, Frame::Inertial>`\n *\n * DataBox:\n * - Adds:\n *   - `::Tags::Variables<background_fields>`\n */\ntemplate <typename System, typename BackgroundTag>\nstruct InitializeBackgroundFields {\n  static_assert(\n      not std::is_same_v<typename System::background_fields, tmpl::list<>>,\n      \"The system has no background fields. Don't add the \"\n      \"'InitializeBackgroundFields' action to the action list.\");\n\n private:\n  using background_fields_tag =\n      ::Tags::Variables<typename System::background_fields>;\n\n public:\n  using simple_tags = tmpl::list<background_fields_tag>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& background = db::get<BackgroundTag>(box);\n    const auto& inertial_coords =\n        get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box);\n    const auto& mesh = get<domain::Tags::Mesh<Dim>>(box);\n    const auto& inv_jacobian =\n        get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                          Frame::Inertial>>(box);\n    auto background_fields = variables_from_tagged_tuple(\n        background.variables(inertial_coords, mesh, inv_jacobian,\n                             typename background_fields_tag::tags_list{}));\n    ::Initialization::mutate_assign<simple_tags>(make_not_null(&box),\n                                                 std::move(background_fields));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace elliptic::Actions\n"
  },
  {
    "path": "src/Elliptic/Actions/InitializeFields.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Utilities/GetAnalyticData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::Actions {\n\n/*!\n * \\brief Initialize the dynamic fields of the elliptic system, i.e. those we\n * solve for.\n *\n * Uses:\n * - System:\n *   - `primal_fields`\n * - DataBox:\n *   - `InitialGuessTag`\n *   - `Tags::Coordinates<Dim, Frame::Inertial>`\n *\n * DataBox:\n * - Adds:\n *   - `primal_fields`\n */\ntemplate <typename System, typename InitialGuessTag>\nstruct InitializeFields {\n private:\n  using fields_tag = ::Tags::Variables<typename System::primal_fields>;\n\n public:\n  using simple_tags = tmpl::list<fields_tag>;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<InitialGuessTag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& inertial_coords =\n        get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box);\n    const auto& initial_guess = db::get<InitialGuessTag>(box);\n    auto initial_fields =\n        elliptic::util::get_analytic_data<typename fields_tag::tags_list>(\n            initial_guess, box, inertial_coords);\n    ::Initialization::mutate_assign<simple_tags>(make_not_null(&box),\n                                                 std::move(initial_fields));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace elliptic::Actions\n"
  },
  {
    "path": "src/Elliptic/Actions/InitializeFixedSources.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace elliptic::Actions {\n\n/*!\n * \\brief Initialize the \"fixed sources\" of the elliptic equations, i.e. their\n * variable-independent source term \\f$f(x)\\f$\n *\n * This action initializes \\f$f(x)\\f$ in an elliptic system of PDEs \\f$-div(F) +\n * S = f(x)\\f$.\n *\n * Uses:\n * - System:\n *   - `primal_fields`\n * - DataBox:\n *   - `BackgroundTag`\n *   - `Tags::Coordinates<Dim, Frame::Inertial>`\n *\n * DataBox:\n * - Adds:\n *   - `db::wrap_tags_in<::Tags::FixedSource, primal_fields>`\n */\ntemplate <typename System, typename BackgroundTag,\n          size_t Dim = System::volume_dim>\nstruct InitializeFixedSources : tt::ConformsTo<::amr::protocols::Projector> {\n private:\n  using fixed_sources_tag = ::Tags::Variables<\n      db::wrap_tags_in<::Tags::FixedSource, typename System::primal_fields>>;\n\n public:  // Iterable action\n  using const_global_cache_tags =\n      tmpl::list<elliptic::dg::Tags::Massive, BackgroundTag>;\n  using simple_tags = tmpl::list<fixed_sources_tag>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeFixedSources>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // DataBox mutator, amr::protocols::Projector\n  using return_tags = tmpl::list<fixed_sources_tag>;\n  using argument_tags = tmpl::list<\n      domain::Tags::Coordinates<Dim, Frame::Inertial>, BackgroundTag,\n      elliptic::dg::Tags::Massive, domain::Tags::Mesh<Dim>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      Parallel::Tags::Metavariables>;\n\n  template <typename Background, typename Metavariables, typename... AmrData>\n  static void apply(\n      const gsl::not_null<typename fixed_sources_tag::type*> fixed_sources,\n      const tnsr::I<DataVector, Dim>& inertial_coords,\n      const Background& background, const bool massive, const Mesh<Dim>& mesh,\n      const Scalar<DataVector>& det_inv_jacobian, const Metavariables& /*meta*/,\n      const AmrData&... /*amr_data*/) {\n    // Retrieve the fixed-sources of the elliptic system from the background,\n    // which (along with the boundary conditions) define the problem we want to\n    // solve.\n    using factory_classes =\n        typename std::decay_t<Metavariables>::factory_creation::factory_classes;\n    *fixed_sources =\n        call_with_dynamic_type<Variables<typename fixed_sources_tag::tags_list>,\n                               tmpl::at<factory_classes, Background>>(\n            &background, [&inertial_coords](const auto* const derived) {\n              return variables_from_tagged_tuple(derived->variables(\n                  inertial_coords, typename fixed_sources_tag::tags_list{}));\n            });\n\n    // Apply DG mass matrix to the fixed sources if the DG operator is massive\n    if (massive) {\n      *fixed_sources /= get(det_inv_jacobian);\n      ::dg::apply_mass_matrix(fixed_sources, mesh);\n    }\n  }\n};\n\n}  // namespace elliptic::Actions\n"
  },
  {
    "path": "src/Elliptic/Actions/RunEventsAndTriggers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::Actions {\n/// \\ingroup ActionsGroup\n/// \\ingroup EventsAndTriggersGroup\n/// \\brief Run the events and triggers\n///\n/// Uses:\n/// - GlobalCache: the EventsAndTriggers tag, as required by\n///   events and triggers\n/// - DataBox: as required by events and triggers\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename ObservationId>\nstruct RunEventsAndTriggers {\n  using const_global_cache_tags =\n      tmpl::list<Tags::EventsAndTriggers<Triggers::WhenToCheck::AtIterations>>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const component) {\n    Parallel::get<Tags::EventsAndTriggers<Triggers::WhenToCheck::AtIterations>>(\n        cache)\n        .run_events(make_not_null(&box), cache, array_index, component,\n                    {db::tag_name<ObservationId>(),\n                     static_cast<double>(db::get<ObservationId>(box))});\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace elliptic::Actions\n"
  },
  {
    "path": "src/Elliptic/Amr/Actions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Actions to control the elliptic AMR algorithm\nnamespace elliptic::amr::Actions {\n\n/*!\n * \\brief Initializes items for the elliptic AMR algorithm projector\n *\n * When projecting these items during h-AMR, they are copied from the parent or\n * children to retain the AMR state.\n */\nstruct Initialize : tt::ConformsTo<::amr::protocols::Projector> {\n private:\n  using iteration_id_tag =\n      Convergence::Tags::IterationId<::amr::OptionTags::AmrGroup>;\n  using num_iterations_tag =\n      Convergence::Tags::Iterations<::amr::OptionTags::AmrGroup>;\n  using has_converged_tag =\n      Convergence::Tags::HasConverged<::amr::OptionTags::AmrGroup>;\n\n public:  // Initialization mutator\n  using simple_tags = tmpl::list<iteration_id_tag, has_converged_tag>;\n  using compute_tags = tmpl::list<>;\n  using simple_tags_from_options = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<num_iterations_tag>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using return_tags = simple_tags;\n  using argument_tags = tmpl::list<num_iterations_tag>;\n\n  static void apply(\n      const gsl::not_null<size_t*> amr_iteration_id,\n      const gsl::not_null<Convergence::HasConverged*> amr_has_converged,\n      const size_t num_iterations) {\n    *amr_iteration_id = 0;\n    *amr_has_converged = Convergence::HasConverged{num_iterations, 0};\n  }\n\n public:  // amr::protocols::Projector\n  // p-refinement\n  template <size_t Dim>\n  static void apply(\n      const gsl::not_null<size_t*> /*amr_iteration_id*/,\n      const gsl::not_null<Convergence::HasConverged*> /*amr_has_converged*/,\n      const size_t /*num_iterations*/,\n      const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {}\n\n  // h-refinement\n  template <typename... ParentTags>\n  static void apply(\n      const gsl::not_null<size_t*> amr_iteration_id,\n      const gsl::not_null<Convergence::HasConverged*> amr_has_converged,\n      const size_t /*num_iterations*/,\n      const tuples::TaggedTuple<ParentTags...>& parent_items) {\n    *amr_iteration_id = get<iteration_id_tag>(parent_items);\n    *amr_has_converged = get<has_converged_tag>(parent_items);\n  }\n\n  // h-coarsening\n  template <size_t Dim, typename... ChildTags>\n  static void apply(\n      const gsl::not_null<size_t*> amr_iteration_id,\n      const gsl::not_null<Convergence::HasConverged*> amr_has_converged,\n      const size_t /*num_iterations*/,\n      const std::unordered_map<\n          ElementId<Dim>, tuples::TaggedTuple<ChildTags...>>& children_items) {\n    *amr_iteration_id = get<iteration_id_tag>(children_items.begin()->second);\n    *amr_has_converged = get<has_converged_tag>(children_items.begin()->second);\n  }\n};\n\n/// Increment the AMR iteration ID and determine convergence\nstruct IncrementIterationId {\n private:\n  using iteration_id_tag =\n      Convergence::Tags::IterationId<::amr::OptionTags::AmrGroup>;\n  using num_iterations_tag =\n      Convergence::Tags::Iterations<::amr::OptionTags::AmrGroup>;\n  using has_converged_tag =\n      Convergence::Tags::HasConverged<::amr::OptionTags::AmrGroup>;\n\n public:\n  template <typename DataBox, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      DataBox& box, const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t num_iterations = get<num_iterations_tag>(box);\n    // Increment AMR iteration id and determine convergence\n    db::mutate<iteration_id_tag, has_converged_tag>(\n        [&num_iterations](\n            const gsl::not_null<size_t*> iteration_id,\n            const gsl::not_null<Convergence::HasConverged*> has_converged) {\n          ++(*iteration_id);\n          *has_converged =\n              Convergence::HasConverged{num_iterations, *iteration_id};\n        },\n        make_not_null(&box));\n    // Do some logging\n    if (db::get<logging::Tags::Verbosity<::amr::OptionTags::AmrGroup>>(box) >=\n        ::Verbosity::Debug) {\n      Parallel::printf(\"%s AMR iteration %zu / %zu\\n\", element_id,\n                       db::get<iteration_id_tag>(box), num_iterations);\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// Stop the algorithm if it has converged\nstruct StopAmr {\n private:\n  using has_converged_tag =\n      Convergence::Tags::HasConverged<::amr::OptionTags::AmrGroup>;\n\n public:\n  template <typename DataBox, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      DataBox& box, const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {db::get<has_converged_tag>(box)\n                ? Parallel::AlgorithmExecution::Pause\n                : Parallel::AlgorithmExecution::Continue,\n            std::nullopt};\n  }\n};\n\n}  // namespace elliptic::amr::Actions\n"
  },
  {
    "path": "src/Elliptic/Amr/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Actions.hpp\n  )\n"
  },
  {
    "path": "src/Elliptic/BoundaryConditions/AnalyticSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <ostream>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/BoundaryConditions/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::BoundaryConditions {\n\n/// \\cond\ntemplate <typename System, size_t Dim = System::volume_dim,\n          typename FieldTags = typename System::primal_fields,\n          typename FluxTags = typename System::primal_fluxes>\nstruct AnalyticSolution;\n/// \\endcond\n\n/*!\n * \\brief Impose the analytic solution on the boundary.\n *\n * The user can select to impose the analytic solution as Dirichlet or\n * Neumann boundary conditions for each field separately.  Dirichlet\n * boundary conditions are imposed on the fields and Neumann boundary\n * conditions are imposed on the fluxes.\n */\ntemplate <typename System, size_t Dim, typename... FieldTags,\n          typename... FluxTags>\nclass AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,\n                       tmpl::list<FluxTags...>>\n    : public BoundaryCondition<Dim> {\n private:\n  using Base = BoundaryCondition<Dim>;\n\n public:\n  struct Solution {\n    using type = std::unique_ptr<elliptic::analytic_data::AnalyticSolution>;\n    static constexpr Options::String help = {\n        \"The analytic solution to impose on the boundary\"};\n  };\n\n  using options =\n      tmpl::list<Solution,\n                 elliptic::OptionTags::BoundaryConditionType<FieldTags>...>;\n  static constexpr Options::String help =\n      \"Boundary conditions from the analytic solution\";\n\n  AnalyticSolution() = default;\n  AnalyticSolution(const AnalyticSolution& rhs) : Base(rhs) { *this = rhs; }\n  AnalyticSolution& operator=(const AnalyticSolution& rhs) {\n    if (rhs.solution_ != nullptr) {\n      solution_ = rhs.solution_->get_clone();\n    } else {\n      solution_ = nullptr;\n    }\n    boundary_condition_types_ = rhs.boundary_condition_types_;\n    return *this;\n  }\n  AnalyticSolution(AnalyticSolution&&) = default;\n  AnalyticSolution& operator=(AnalyticSolution&&) = default;\n  ~AnalyticSolution() = default;\n\n  /// \\cond\n  explicit AnalyticSolution(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(AnalyticSolution);\n  /// \\endcond\n\n  /// Select which `elliptic::BoundaryConditionType` to apply for each field\n  explicit AnalyticSolution(\n      std::unique_ptr<elliptic::analytic_data::AnalyticSolution> solution,\n      // This pack expansion repeats the type `elliptic::BoundaryConditionType`\n      // for each system field\n      const typename elliptic::OptionTags::BoundaryConditionType<\n          FieldTags>::type... boundary_condition_types)\n      : solution_(std::move(solution)),\n        boundary_condition_types_{boundary_condition_types...} {}\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<AnalyticSolution>(*this);\n  }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    std::vector<elliptic::BoundaryConditionType> result{};\n    const auto collect = [&result](\n                             const auto tag_v,\n                             const elliptic::BoundaryConditionType bc_type) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      for (size_t i = 0; i < tag::type::size(); ++i) {\n        result.push_back(bc_type);\n      }\n    };\n    EXPAND_PACK_LEFT_TO_RIGHT(collect(\n        FieldTags{}, get<elliptic::Tags::BoundaryConditionType<FieldTags>>(\n                         boundary_condition_types_)));\n    return result;\n  }\n\n  using argument_tags =\n      tmpl::list<Parallel::Tags::Metavariables,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<\n                     Dim, Frame::Inertial>>>;\n  using volume_tags = tmpl::list<Parallel::Tags::Metavariables>;\n\n  template <typename Metavariables>\n  void apply(const gsl::not_null<typename FieldTags::type*>... fields,\n             const gsl::not_null<typename FieldTags::type*>... n_dot_fluxes,\n             const TensorMetafunctions::prepend_spatial_index<\n                 typename FieldTags::type, Dim, UpLo::Lo,\n                 Frame::Inertial>&... /*deriv_fields*/,\n             const Metavariables& /*meta*/,\n             const tnsr::I<DataVector, Dim>& face_inertial_coords,\n             const tnsr::i<DataVector, Dim>& face_normal) const {\n    // Retrieve variables for both Dirichlet and Neumann conditions, then decide\n    // which to impose. We could also retrieve either the field for the flux for\n    // each field individually based on the selection, but that would incur the\n    // overhead of calling into the analytic solution multiple times and\n    // possibly computing temporary quantities multiple times. This performance\n    // consideration is probably irrelevant because the boundary conditions are\n    // only evaluated once at the beginning of the solve.\n    using analytic_tags = tmpl::list<FieldTags..., FluxTags...>;\n    using factory_classes =\n        typename Metavariables::factory_creation::factory_classes;\n    const auto solution_vars = call_with_dynamic_type<\n        tuples::tagged_tuple_from_typelist<analytic_tags>,\n        tmpl::at<factory_classes, elliptic::analytic_data::AnalyticSolution>>(\n        solution_.get(), [&face_inertial_coords](const auto* const derived) {\n          return derived->variables(face_inertial_coords, analytic_tags{});\n        });\n    const auto impose_boundary_condition = [this, &solution_vars, &face_normal](\n                                               auto field_tag_v,\n                                               auto flux_tag_v,\n                                               const auto field,\n                                               const auto n_dot_flux) {\n      using field_tag = decltype(field_tag_v);\n      using flux_tag = decltype(flux_tag_v);\n      switch (get<elliptic::Tags::BoundaryConditionType<field_tag>>(\n          boundary_condition_types_)) {\n        case elliptic::BoundaryConditionType::Dirichlet:\n          *field = get<field_tag>(solution_vars);\n          break;\n        case elliptic::BoundaryConditionType::Neumann:\n          normal_dot_flux(n_dot_flux, face_normal,\n                          get<flux_tag>(solution_vars));\n          break;\n        default:\n          ERROR(\"Unsupported boundary condition type: \"\n                << get<elliptic::Tags::BoundaryConditionType<field_tag>>(\n                       boundary_condition_types_));\n      }\n    };\n    EXPAND_PACK_LEFT_TO_RIGHT(impose_boundary_condition(FieldTags{}, FluxTags{},\n                                                        fields, n_dot_fluxes));\n  }\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      const gsl::not_null<typename FieldTags::type*>... fields,\n      const gsl::not_null<typename FieldTags::type*>... n_dot_fluxes,\n      const TensorMetafunctions::prepend_spatial_index<\n          typename FieldTags::type, Dim, UpLo::Lo,\n          Frame::Inertial>&... /*deriv_fields*/) const {\n    const auto impose_boundary_condition = [this](auto field_tag_v,\n                                                  const auto field,\n                                                  const auto n_dot_flux) {\n      using field_tag = decltype(field_tag_v);\n      switch (get<elliptic::Tags::BoundaryConditionType<field_tag>>(\n          boundary_condition_types_)) {\n        case elliptic::BoundaryConditionType::Dirichlet:\n          for (auto& field_component : *field) {\n            field_component = 0.;\n          }\n          break;\n        case elliptic::BoundaryConditionType::Neumann:\n          for (auto& n_dot_flux_component : *n_dot_flux) {\n            n_dot_flux_component = 0.;\n          }\n          break;\n        default:\n          ERROR(\"Unsupported boundary condition type: \"\n                << get<elliptic::Tags::BoundaryConditionType<field_tag>>(\n                       boundary_condition_types_));\n      }\n    };\n    EXPAND_PACK_LEFT_TO_RIGHT(\n        impose_boundary_condition(FieldTags{}, fields, n_dot_fluxes));\n  }\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override;\n\n private:\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> solution_{nullptr};\n  tuples::TaggedTuple<elliptic::Tags::BoundaryConditionType<FieldTags>...>\n      boundary_condition_types_{};\n};\n\ntemplate <typename System, size_t Dim, typename... FieldTags,\n          typename... FluxTags>\nvoid AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,\n                      tmpl::list<FluxTags...>>::pup(PUP::er& p) {\n  Base::pup(p);\n  p | solution_;\n  p | boundary_condition_types_;\n}\n\n/// \\cond\ntemplate <typename System, size_t Dim, typename... FieldTags,\n          typename... FluxTags>\nPUP::able::PUP_ID AnalyticSolution<System, Dim, tmpl::list<FieldTags...>,\n                                   tmpl::list<FluxTags...>>::my_PUP_ID =\n    0;  // NOLINT\n/// \\endcond\n\n}  // namespace elliptic::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Utilities/ApplyAt.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic {\n\nnamespace detail {\n// Return the `BoundaryConditionClasses`, or get the list of derived classes\n// from `Metavariables::factory_creation` if `BoundaryConditionClasses` is\n// empty.\ntemplate <typename Base, typename BoundaryConditionClasses, typename DbTagsList>\nstruct GetBoundaryConditionClasses {\n  using type = BoundaryConditionClasses;\n};\ntemplate <typename Base, typename DbTagsList>\nstruct GetBoundaryConditionClasses<Base, tmpl::list<>, DbTagsList> {\n  using type = tmpl::at<\n      typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n          std::declval<db::DataBox<DbTagsList>>()))>::factory_creation::\n          factory_classes,\n      Base>;\n};\n}  // namespace detail\n\n/*!\n * \\brief Apply the `boundary_condition` to the `fields_and_fluxes` with\n * arguments from interface tags in the DataBox.\n *\n * This functions assumes the arguments for the `boundary_condition` are stored\n * in the DataBox in tags `domain::Tags::Faces<Dim, Tag>`.\n * This may turn out not to be the most efficient setup, so code that\n * uses the boundary conditions doesn't have to use this function but can\n * procure the arguments differently. For example, future optimizations may\n * involve storing a subset of arguments that don't change during an elliptic\n * solve in direction-maps in the DataBox, and slicing other arguments to the\n * interface every time the boundary conditions are applied.\n *\n * The `ArgsTransform` template parameter can be used to transform the set of\n * argument tags for the boundary conditions further. It must be compatible with\n * `tmpl::transform`. For example, it may wrap the tags in another prefix. Set\n * it to `void` (default) to apply no transformation.\n *\n * The `BoundaryConditionClasses` can be used to list a set of classes derived\n * from `elliptic::BoundaryConditions::BoundaryCondition` that are iterated to\n * determine the concrete type of `boundary_condition`. It can be `tmpl::list<>`\n * (default) to use the classes listed in `Metavariables::factory_creation`\n * instead.\n */\ntemplate <bool Linearized, typename ArgsTransform = void,\n          typename BoundaryConditionClasses = tmpl::list<>, size_t Dim,\n          typename DbTagsList, typename MapKeys, typename... FieldsAndFluxes>\nvoid apply_boundary_condition(\n    const elliptic::BoundaryConditions::BoundaryCondition<Dim>&\n        boundary_condition,\n    const db::DataBox<DbTagsList>& box, const MapKeys& map_keys_to_direction,\n    FieldsAndFluxes&&... fields_and_fluxes) {\n  using boundary_condition_classes =\n      typename detail::GetBoundaryConditionClasses<\n          elliptic::BoundaryConditions::BoundaryCondition<Dim>,\n          BoundaryConditionClasses, DbTagsList>::type;\n  call_with_dynamic_type<void, boundary_condition_classes>(\n      &boundary_condition,\n      []<typename... LocalFieldsAndFluxes>(\n          const auto* const derived, const MapKeys& local_map_keys_to_direction,\n          const db::DataBox<DbTagsList>& local_box,\n          const LocalFieldsAndFluxes&... local_fields_and_fluxes) {\n        using Derived = std::decay_t<std::remove_pointer_t<decltype(derived)>>;\n        using volume_tags =\n            tmpl::conditional_t<Linearized,\n                                typename Derived::volume_tags_linearized,\n                                typename Derived::volume_tags>;\n        using argument_tags = domain::make_faces_tags<\n            Dim,\n            tmpl::conditional_t<Linearized,\n                                typename Derived::argument_tags_linearized,\n                                typename Derived::argument_tags>,\n            volume_tags>;\n        using argument_tags_transformed =\n            tmpl::conditional_t<std::is_same_v<ArgsTransform, void>,\n                                argument_tags,\n                                tmpl::transform<argument_tags, ArgsTransform>>;\n        using volume_tags_transformed =\n            tmpl::conditional_t<std::is_same_v<ArgsTransform, void>,\n                                volume_tags,\n                                tmpl::transform<volume_tags, ArgsTransform>>;\n        elliptic::util::apply_at<argument_tags_transformed,\n                                 volume_tags_transformed>(\n            [&](const auto&... args) {\n              if constexpr (Linearized) {\n                derived->apply_linearized(\n                    local_fields_and_fluxes...,\n                    args...);\n              } else {\n                derived->apply(local_fields_and_fluxes..., args...);\n              }\n            },\n            local_box, local_map_keys_to_direction);\n      },\n      map_keys_to_direction, box,\n      std::forward<FieldsAndFluxes>(fields_and_fluxes)...);\n}\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic {\n/// Boundary conditions for elliptic systems\nnamespace BoundaryConditions {\n\n/*!\n * \\brief Base class for boundary conditions for elliptic systems\n *\n * Boundary conditions for elliptic systems derive from this abstract base\n * class. This allows boundary conditions to be factory-created from input-file\n * options. Specific systems may implement further abstract base classes that\n * derive from this class and add additional requirements.\n *\n * Each derived class represents one kind of boundary conditions. For example,\n * one derived class might implement homogeneous (zero) Dirichlet or Neumann\n * boundary conditions, another might implement Dirichlet fields procured from\n * an analytic solution, and yet another might set the boundary fields as a\n * function of the dynamic variables on the domain boundary (e.g. Robin-type\n * boundary conditions).\n *\n * Note that almost all boundary conditions are actually nonlinear because even\n * those that depend only linearly on the dynamic fields typically contribute\n * non-zero field values. For example, a standard Dirichlet boundary condition\n * \\f$u(x=0)=u_0\\f$ is nonlinear for any \\f$u_0\\neq 0\\f$. Boundary conditions\n * for linear systems may have exactly this nonlinearity (a constant non-zero\n * contribution) but must depend at most linearly on the dynamic fields.\n * Boundary conditions for nonlinear systems may have any nonlinearity. Either\n * must implement their linearization as a separate function (see below). For\n * linear systems the nonlinear (constant) contribution is typically added to\n * the fixed-source part of the discretized equations and the linearized\n * boundary conditions are being employed throughout the solve so the\n * discretized operator remains linear. For nonlinear systems we typically solve\n * the linearized equations repeatedly for a correction quantity, so we apply\n * the linearized boundary conditions when solving for the correction quantity\n * and apply the nonlinear boundary conditions when dealing with the nonlinear\n * fields that are being corrected (see e.g.\n * `NonlinearSolver::newton_raphson::NewtonRaphson`).\n *\n * Derived classes are expected to implement the following compile-time\n * interface:\n *\n * - They are option-creatable.\n * - They have type aliases `argument_tags`, `volume_tags`,\n *   `argument_tags_linearized` and `volume_tags_linearized`. Those aliases list\n *   the tags required for computing nonlinear and linearized boundary\n *   conditions, respectively. The tags are always taken to represent quantities\n *   on the _interior_ side of the domain boundary, i.e. whenever normal vectors\n *   are involved they point _out_ of the computational domain. The\n *   `volume_tags` list the subset of the `argument_tags` that are _not_\n *   evaluated on the boundary but taken from the element directly.\n * - They have `apply` and `apply_linearized` member functions that take these\n *   arguments (in this order):\n *\n *   1. The dynamic fields as not-null pointers.\n *   2. The normal-dot-fluxes corresponding to the dynamic fields as not-null\n *      pointers. These have the same types as the dynamic fields.\n *   3. The field derivatives as const-refs.\n *   4. The types held by the argument tags.\n *\n *   For example, boundary conditions for a first-order Poisson system might\n *   have an `apply` function signature that looks like this:\n *\n *   \\snippet Elliptic/BoundaryConditions/Test_BoundaryCondition.cpp example_poisson_fields\n *\n * The fields and normal-dot-fluxes passed to the `apply` and `apply_linearized`\n * functions hold data which the implementations of the functions can use, and\n * also serve as return variables. Modifying the fields means applying\n * Dirichlet-type boundary conditions and modifying the normal-dot-fluxes means\n * applying Neumann-type boundary conditions. Just like the arguments evaluated\n * on the boundary, the normal-dot-fluxes involve normal vectors that point\n * _out_ of the computation domain. Note that linearized boundary conditions, as\n * well as nonlinear boundary conditions for linear systems, may only depend\n * linearly on the field data, since these are the fields the linearization is\n * performed for.\n */\ntemplate <size_t Dim>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n private:\n  using Base = domain::BoundaryConditions::BoundaryCondition;\n\n public:\n  static constexpr size_t volume_dim = Dim;\n\n  BoundaryCondition() = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  ~BoundaryCondition() override = default;\n\n  /// \\cond\n  explicit BoundaryCondition(CkMigrateMessage* m) : Base(m) {}\n  WRAPPED_PUPable_abstract(BoundaryCondition);  // NOLINT\n  /// \\endcond\n\n  // The type of boundary condition (Dirichlet or Neumann) for every tensor\n  // component. For example, if the derived class imposes Neumann-type\n  // conditions on a Scalar and Dirichlet-type conditions on a 2D vector, then\n  // it should return `{Dirichlet, Neumann, Neumann}`.\n  virtual std::vector<elliptic::BoundaryConditionType>\n  boundary_condition_types() const = 0;\n};\n\n}  // namespace BoundaryConditions\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/BoundaryConditions/BoundaryConditionType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace elliptic {\nstd::ostream& operator<<(\n    std::ostream& os,\n    const elliptic::BoundaryConditionType boundary_condition_type) {\n  switch (boundary_condition_type) {\n    case elliptic::BoundaryConditionType::Dirichlet:\n      return os << \"Dirichlet\";\n    case elliptic::BoundaryConditionType::Neumann:\n      return os << \"Neumann\";\n    default:\n      ERROR(\"Missing case for operator<<(elliptic::BoundaryConditionType).\");\n  }\n}\n}  // namespace elliptic\n\ntemplate <>\nelliptic::BoundaryConditionType\nOptions::create_from_yaml<elliptic::BoundaryConditionType>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (\"Dirichlet\" == type_read) {\n    return elliptic::BoundaryConditionType::Dirichlet;\n  } else if (\"Neumann\" == type_read) {\n    return elliptic::BoundaryConditionType::Neumann;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read\n                  << \"\\\" to elliptic::BoundaryConditionType. Must be \"\n                     \"either 'Dirichlet' or 'Neumann'.\");\n}\n"
  },
  {
    "path": "src/Elliptic/BoundaryConditions/BoundaryConditionType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace elliptic {\n\n/// Identify types of boundary conditions for elliptic equations\nenum class BoundaryConditionType {\n  /// Dirichlet boundary conditions like \\f$u(x_0)=u_0\\f$\n  Dirichlet,\n  /// Neumann boundary conditions like \\f$n^i\\partial_i u(x_0)=v_0\\f$, where\n  /// \\f$\\boldsymbol{n}\\f$ is the normal to the domain boundary\n  Neumann\n};\n\nstd::ostream& operator<<(std::ostream& os,\n                         BoundaryConditionType boundary_condition_type);\n\n}  // namespace elliptic\n\n/// \\cond\ntemplate <>\nstruct Options::create_from_yaml<elliptic::BoundaryConditionType> {\n  template <typename Metavariables>\n  static elliptic::BoundaryConditionType create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nelliptic::BoundaryConditionType\nOptions::create_from_yaml<elliptic::BoundaryConditionType>::create<void>(\n    const Options::Option& options);\n/// \\endcond\n"
  },
  {
    "path": "src/Elliptic/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryConditionType.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticSolution.hpp\n  ApplyBoundaryCondition.hpp\n  BoundaryCondition.hpp\n  BoundaryConditionType.hpp\n  Tags.hpp\n  )\n"
  },
  {
    "path": "src/Elliptic/BoundaryConditions/Tags/BoundaryFields.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::Tags {\n\n/// The `FieldsTag` on external boundaries\ntemplate <size_t Dim, typename FieldsTag>\nstruct BoundaryFieldsCompute : db::ComputeTag,\n                               domain::Tags::Faces<Dim, FieldsTag> {\n  using base = domain::Tags::Faces<Dim, FieldsTag>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<FieldsTag, domain::Tags::Mesh<Dim>,\n                                   domain::Tags::Element<Dim>>;\n  static void function(const gsl::not_null<return_type*> vars_on_face,\n                       const typename FieldsTag::type& vars,\n                       const Mesh<Dim>& mesh, const Element<Dim>& element) {\n    ASSERT(mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto,\n           \"Slicing fields to the boundary currently supports only \"\n           \"Gauss-Lobatto grids. Add support to \"\n           \"'elliptic::Tags::BoundaryFieldsCompute'.\");\n    for (const auto& direction : element.external_boundaries()) {\n      data_on_slice(make_not_null(&((*vars_on_face)[direction])), vars,\n                    mesh.extents(), direction.dimension(),\n                    index_to_slice_at(mesh.extents(), direction));\n    }\n  }\n};\n\n/// The `::Tags::NormalDotFlux<FieldsTag>` on external boundaries\ntemplate <size_t Dim, typename FieldsTag, typename FluxesTag>\nstruct BoundaryFluxesCompute\n    : db::ComputeTag,\n      domain::Tags::Faces<\n          Dim, db::add_tag_prefix<::Tags::NormalDotFlux, FieldsTag>> {\n  using base =\n      domain::Tags::Faces<Dim,\n                          db::add_tag_prefix<::Tags::NormalDotFlux, FieldsTag>>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<FluxesTag,\n                 domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>,\n                 domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>>;\n  static void function(\n      const gsl::not_null<return_type*> normal_dot_fluxes,\n      const typename FluxesTag::type& fluxes,\n      const DirectionMap<Dim, tnsr::i<DataVector, Dim>>& face_normals,\n      const Mesh<Dim>& mesh, const Element<Dim>& element) {\n    ASSERT(mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto,\n           \"Slicing fluxes to the boundary currently supports only \"\n           \"Gauss-Lobatto grids. Add support to \"\n           \"'elliptic::Tags::BoundaryFluxesCompute'.\");\n    for (const auto& direction : element.external_boundaries()) {\n      const auto fluxes_on_face =\n          data_on_slice(fluxes, mesh.extents(), direction.dimension(),\n                        index_to_slice_at(mesh.extents(), direction));\n      normal_dot_flux(make_not_null(&((*normal_dot_fluxes)[direction])),\n                      face_normals.at(direction), fluxes_on_face);\n    }\n  }\n};\n\n}  // namespace elliptic::Tags\n"
  },
  {
    "path": "src/Elliptic/BoundaryConditions/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace elliptic {\nnamespace OptionTags {\n\ntemplate <typename Tag>\nstruct BoundaryConditionType {\n  static std::string name() { return db::tag_name<Tag>(); }\n  using type = elliptic::BoundaryConditionType;\n  static constexpr Options::String help =\n      \"Type of boundary conditions to impose on this variable\";\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n\n/// The `elliptic::BoundaryConditionType` to impose on the variable represented\n/// by `Tag`, e.g. Dirichlet or Neumann boundary conditions\ntemplate <typename Tag>\nstruct BoundaryConditionType : db::PrefixTag, db::SimpleTag {\n  using type = elliptic::BoundaryConditionType;\n  using tag = Tag;\n};\n\n/// The `elliptic::BoundaryConditionType` to impose on the variables represented\n/// by `Tags`, e.g. Dirichlet or Neumann boundary conditions\ntemplate <typename Tags>\nstruct BoundaryConditionTypes : db::SimpleTag {\n  using type = tuples::tagged_tuple_from_typelist<\n      db::wrap_tags_in<elliptic::Tags::BoundaryConditionType, Tags>>;\n};\n\n}  // namespace Tags\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Elliptic)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ErrorHandling\n  Options\n  INTERFACE\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  EventsAndTriggers\n  LinearOperators\n  ParallelAmr\n  Printf\n  Serialization\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Amr)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(DiscontinuousGalerkin)\nadd_subdirectory(Executables)\nadd_subdirectory(Protocols)\nadd_subdirectory(Python)\nadd_subdirectory(SubdomainPreconditioners)\nadd_subdirectory(Systems)\nadd_subdirectory(Triggers)\nadd_subdirectory(Utilities)\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/Actions/ApplyOperator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Domain/Tags/SurfaceJacobian.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgOperator.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Initialization.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Systems/GetFluxesComputer.hpp\"\n#include \"Elliptic/Systems/GetModifyBoundaryData.hpp\"\n#include \"Elliptic/Systems/GetSourcesComputer.hpp\"\n#include \"Elliptic/Utilities/ApplyAt.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Actions related to elliptic discontinuous Galerkin schemes\nnamespace elliptic::dg::Actions {\n// The individual actions in this namespace are not exposed publicly because\n// they don't work on their own. Instead, the public interface (defined below)\n// exposes them in action lists.\nnamespace detail {\n\n// This is a global ID that identifies the DG operator application. It\n// increments with each application of the operator.\nstruct DgOperatorLabel {};\nusing TemporalIdTag = Convergence::Tags::IterationId<DgOperatorLabel>;\n\n// This tag is used to communicate mortar data across neighbors\ntemplate <size_t Dim, typename TemporalIdTag, typename PrimalFields,\n          typename PrimalFluxes>\nstruct MortarDataInboxTag\n    : public Parallel::InboxInserters::Map<\n          MortarDataInboxTag<Dim, TemporalIdTag, PrimalFields, PrimalFluxes>> {\n  using temporal_id = typename TemporalIdTag::type;\n  using type =\n      std::map<temporal_id,\n               DirectionalIdMap<Dim, elliptic::dg::BoundaryData<PrimalFields,\n                                                                PrimalFluxes>>>;\n};\n\n// Initializes all quantities the DG operator needs on internal and external\n// faces, as well as the mortars between neighboring elements. Also initializes\n// the variable-independent background fields in the PDEs.\ntemplate <typename System, typename BackgroundTag>\nstruct InitializeFacesMortarsAndBackground {\n private:\n  static constexpr size_t Dim = System::volume_dim;\n  static constexpr bool has_background_fields =\n      not std::is_same_v<typename System::background_fields, tmpl::list<>>;\n  static_assert(\n      not(has_background_fields and std::is_same_v<BackgroundTag, void>),\n      \"The system has background fields that need initialization. Supply a \"\n      \"'BackgroundTag' to 'elliptic::dg::Actions::initialize_operator'.\");\n\n  using InitializeFacesAndMortars = elliptic::dg::InitializeFacesAndMortars<\n      Dim, typename System::inv_metric_tag, BackgroundTag>;\n  using InitializeBackground = elliptic::dg::InitializeBackground<\n      Dim, typename System::background_fields, BackgroundTag>;\n\n public:\n  using simple_tags = tmpl::append<\n      typename InitializeFacesAndMortars::return_tags,\n      tmpl::conditional_t<has_background_fields,\n                          typename InitializeBackground::return_tags,\n                          tmpl::list<>>>;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::conditional_t<has_background_fields, tmpl::list<BackgroundTag>,\n                          tmpl::list<>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Initialize faces and mortars\n    db::mutate_apply<InitializeFacesAndMortars>(make_not_null(&box));\n    if constexpr (has_background_fields) {\n      // Initialize background fields\n      db::mutate_apply<InitializeBackground>(make_not_null(&box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Compute auxiliary variables and fluxes from the primal variables, prepare the\n// local side of all mortars and send the local mortar data to neighbors. Also\n// handle boundary conditions by preparing the exterior (\"ghost\") side of\n// external mortars.\ntemplate <typename System, bool Linearized, typename TemporalIdTag,\n          typename PrimalFieldsTag, typename PrimalFluxesTag,\n          typename OperatorAppliedToFieldsTag, typename PrimalMortarFieldsTag,\n          typename PrimalMortarFluxesTag,\n          typename FluxesArgsTags =\n              elliptic::get_fluxes_argument_tags<System, Linearized>,\n          typename SourcesArgsTags =\n              elliptic::get_sources_argument_tags<System, Linearized>>\nstruct PrepareAndSendMortarData;\n\ntemplate <typename System, bool Linearized, typename TemporalIdTag,\n          typename PrimalFieldsTag, typename PrimalFluxesTag,\n          typename OperatorAppliedToFieldsTag, typename PrimalMortarFieldsTag,\n          typename PrimalMortarFluxesTag, typename... FluxesArgsTags,\n          typename... SourcesArgsTags>\nstruct PrepareAndSendMortarData<\n    System, Linearized, TemporalIdTag, PrimalFieldsTag, PrimalFluxesTag,\n    OperatorAppliedToFieldsTag, PrimalMortarFieldsTag, PrimalMortarFluxesTag,\n    tmpl::list<FluxesArgsTags...>, tmpl::list<SourcesArgsTags...>> {\n private:\n  static constexpr size_t Dim = System::volume_dim;\n  using all_mortar_data_tag = ::Tags::Mortars<\n      elliptic::dg::Tags::MortarData<typename TemporalIdTag::type,\n                                     typename PrimalMortarFieldsTag::tags_list,\n                                     typename PrimalMortarFluxesTag::tags_list>,\n      Dim>;\n  using mortar_data_inbox_tag =\n      MortarDataInboxTag<Dim, TemporalIdTag,\n                         typename PrimalMortarFieldsTag::tags_list,\n                         typename PrimalMortarFluxesTag::tags_list>;\n  using BoundaryConditionsBase = typename System::boundary_conditions_base;\n  using DerivFieldsTag = db::add_tag_prefix<::Tags::deriv, PrimalFieldsTag,\n                                            tmpl::size_t<Dim>, Frame::Inertial>;\n\n public:\n  // Request these tags be added to the DataBox. We\n  // don't actually need to initialize them, because the\n  // `PrimalFieldsTag` will be set by other actions before applying the operator\n  // and the remaining tags hold output of the operator.\n  using simple_tags = tmpl::list<TemporalIdTag, PrimalFieldsTag, DerivFieldsTag,\n                                 PrimalFluxesTag, OperatorAppliedToFieldsTag,\n                                 all_mortar_data_tag>;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::ExternalBoundaryConditions<Dim>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& temporal_id = db::get<TemporalIdTag>(box);\n    const auto& element = db::get<domain::Tags::Element<Dim>>(box);\n\n    const auto& mortar_meshes =\n        db::get<::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>>(box);\n    const auto& boundary_conditions =\n        db::get<domain::Tags::ExternalBoundaryConditions<Dim>>(box).at(\n            element_id.block_id());\n    const auto apply_boundary_condition = [&box, &boundary_conditions,\n                                           &element_id](\n                                              const Direction<Dim>& direction,\n                                              auto&&... fields_and_fluxes) {\n      ASSERT(boundary_conditions.contains(direction),\n             \"No boundary condition is available in block \"\n                 << element_id.block_id() << \" in direction \" << direction\n                 << \". Make sure you are setting up boundary conditions when \"\n                    \"creating the domain.\");\n      ASSERT(dynamic_cast<const BoundaryConditionsBase*>(\n                 boundary_conditions.at(direction).get()) != nullptr,\n             \"The boundary condition in block \"\n                 << element_id.block_id() << \" in direction \" << direction\n                 << \" has an unexpected type. Make sure it derives off the \"\n                    \"'boundary_conditions_base' class set in the system.\");\n      const auto& boundary_condition =\n          dynamic_cast<const BoundaryConditionsBase&>(\n              *boundary_conditions.at(direction));\n      elliptic::apply_boundary_condition<Linearized>(\n          boundary_condition, box, direction,\n          std::forward<decltype(fields_and_fluxes)>(fields_and_fluxes)...);\n    };\n\n    // Can't `db::get` the arguments for the boundary conditions within\n    // `db::mutate`, so we extract the data to mutate and move it back in when\n    // we're done.\n    // Possible optimization: also keep memory for the mortar data around in the\n    // DataBox. Currently the mortar data is extracted and erased by\n    // `apply_operator` anyway, so we can just create a new map here to avoid\n    // dealing with AMR resizing the mortars. When we keep the memory around,\n    // its size has to be adjusted when the mesh changes during AMR.\n    typename PrimalFluxesTag::type primal_fluxes;\n    typename DerivFieldsTag::type deriv_fields;\n    typename all_mortar_data_tag::type all_mortar_data{};\n    db::mutate<PrimalFluxesTag, DerivFieldsTag>(\n        [&primal_fluxes, &deriv_fields](const auto local_primal_fluxes,\n                                        const auto local_deriv_fields) {\n          primal_fluxes = std::move(*local_primal_fluxes);\n          deriv_fields = std::move(*local_deriv_fields);\n        },\n        make_not_null(&box));\n\n    elliptic::dg::prepare_mortar_data<System, Linearized>(\n        make_not_null(&deriv_fields), make_not_null(&primal_fluxes),\n        make_not_null(&all_mortar_data), db::get<PrimalFieldsTag>(box), element,\n        db::get<domain::Tags::Mesh<Dim>>(box),\n        db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                              Frame::Inertial>>(box),\n        db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>>(box),\n        mortar_meshes,\n        db::get<::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>>(box),\n        temporal_id, apply_boundary_condition,\n        std::forward_as_tuple(db::get<FluxesArgsTags>(box)...));\n\n    // Move the mutated data back into the DataBox\n    db::mutate<PrimalFluxesTag, DerivFieldsTag, all_mortar_data_tag>(\n        [&primal_fluxes, &deriv_fields, &all_mortar_data](\n            const auto local_primal_fluxes, const auto local_deriv_fields,\n            const auto local_all_mortar_data) {\n          *local_primal_fluxes = std::move(primal_fluxes);\n          *local_deriv_fields = std::move(deriv_fields);\n          *local_all_mortar_data = std::move(all_mortar_data);\n        },\n        make_not_null(&box));\n\n    // Send mortar data to neighbors\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      const size_t dimension = direction.dimension();\n      for (const auto& neighbor_id : neighbors) {\n        const auto& orientation = neighbors.orientation(neighbor_id);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        const ::dg::MortarId<Dim> mortar_id{direction, neighbor_id};\n        // Make a copy of the local boundary data on the mortar to send to the\n        // neighbor\n        auto remote_boundary_data_on_mortar =\n            get<all_mortar_data_tag>(box).at(mortar_id).local_data(temporal_id);\n        // Reorient the data to the neighbor orientation if necessary\n        if (not orientation.is_aligned()) {\n          remote_boundary_data_on_mortar.orient_on_slice(\n              mortar_meshes.at(mortar_id).extents(), dimension, orientation);\n        }\n        // Send remote data to neighbor\n        Parallel::receive_data<mortar_data_inbox_tag>(\n            receiver_proxy[neighbor_id], temporal_id,\n            std::make_pair(\n                ::dg::MortarId<Dim>{direction_from_neighbor, element.id()},\n                std::move(remote_boundary_data_on_mortar)));\n      }  // loop over neighbors in direction\n    }  // loop over directions\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Wait until all mortar data from neighbors is available. Then add boundary\n// corrections to the primal fluxes, compute their derivatives (i.e. the second\n// derivatives of the primal variables) and add boundary corrections to the\n// result.\ntemplate <typename System, bool Linearized, typename TemporalIdTag,\n          typename PrimalFieldsTag, typename PrimalFluxesTag,\n          typename OperatorAppliedToFieldsTag, typename PrimalMortarFieldsTag,\n          typename PrimalMortarFluxesTag,\n          typename FluxesArgsTags =\n              elliptic::get_fluxes_argument_tags<System, Linearized>,\n          typename SourcesArgsTags =\n              elliptic::get_sources_argument_tags<System, Linearized>>\nstruct ReceiveMortarDataAndApplyOperator;\n\ntemplate <typename System, bool Linearized, typename TemporalIdTag,\n          typename PrimalFieldsTag, typename PrimalFluxesTag,\n          typename OperatorAppliedToFieldsTag, typename PrimalMortarFieldsTag,\n          typename PrimalMortarFluxesTag, typename... FluxesArgsTags,\n          typename... SourcesArgsTags>\nstruct ReceiveMortarDataAndApplyOperator<\n    System, Linearized, TemporalIdTag, PrimalFieldsTag, PrimalFluxesTag,\n    OperatorAppliedToFieldsTag, PrimalMortarFieldsTag, PrimalMortarFluxesTag,\n    tmpl::list<FluxesArgsTags...>, tmpl::list<SourcesArgsTags...>> {\n private:\n  static constexpr size_t Dim = System::volume_dim;\n  using all_mortar_data_tag = ::Tags::Mortars<\n      elliptic::dg::Tags::MortarData<typename TemporalIdTag::type,\n                                     typename PrimalMortarFieldsTag::tags_list,\n                                     typename PrimalMortarFluxesTag::tags_list>,\n      Dim>;\n  using mortar_data_inbox_tag =\n      MortarDataInboxTag<Dim, TemporalIdTag,\n                         typename PrimalMortarFieldsTag::tags_list,\n                         typename PrimalMortarFluxesTag::tags_list>;\n  using DerivFieldsTag = db::add_tag_prefix<::Tags::deriv, PrimalFieldsTag,\n                                            tmpl::size_t<Dim>, Frame::Inertial>;\n public:\n  using const_global_cache_tags =\n      tmpl::list<elliptic::dg::Tags::PenaltyParameter,\n                 elliptic::dg::Tags::Massive, elliptic::dg::Tags::Formulation>;\n  using inbox_tags = tmpl::list<mortar_data_inbox_tag>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& temporal_id = get<TemporalIdTag>(box);\n    const auto& element = get<domain::Tags::Element<Dim>>(box);\n\n    if (not::dg::has_received_from_all_mortars<mortar_data_inbox_tag>(\n            temporal_id, element, inboxes)) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Move received \"remote\" mortar data into the DataBox\n    if (LIKELY(element.number_of_neighbors() > 0)) {\n      auto received_mortar_data =\n          std::move(tuples::get<mortar_data_inbox_tag>(inboxes)\n                        .extract(temporal_id)\n                        .mapped());\n      db::mutate<all_mortar_data_tag>(\n          [&received_mortar_data, &temporal_id](const auto all_mortar_data) {\n            for (auto& [mortar_id, mortar_data] : received_mortar_data) {\n              all_mortar_data->at(mortar_id).remote_insert(\n                  temporal_id, std::move(mortar_data));\n            }\n          },\n          make_not_null(&box));\n    }\n\n    // Used to retrieve items out of the DataBox to forward to functions\n    const auto get_items = [](const auto&... args) {\n      return std::forward_as_tuple(args...);\n    };\n\n    // Apply DG operator\n    using fluxes_args_tags =\n        typename elliptic::get_fluxes_argument_tags<System, Linearized>;\n    using fluxes_args_volume_tags =\n        typename elliptic::get_fluxes_volume_tags<System, Linearized>;\n    DirectionMap<Dim, std::tuple<decltype(db::get<FluxesArgsTags>(box))...>>\n        fluxes_args_on_faces{};\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      fluxes_args_on_faces.emplace(\n          direction, elliptic::util::apply_at<\n                         domain::make_faces_tags<Dim, fluxes_args_tags,\n                                                 fluxes_args_volume_tags>,\n                         fluxes_args_volume_tags>(get_items, box, direction));\n    }\n    db::mutate<OperatorAppliedToFieldsTag, all_mortar_data_tag>(\n        [](const auto&... args) {\n          elliptic::dg::apply_operator<System, Linearized>(args...);\n        },\n        make_not_null(&box), db::get<PrimalFieldsTag>(box),\n        db::get<DerivFieldsTag>(box), db::get<PrimalFluxesTag>(box), element,\n        db::get<domain::Tags::Mesh<Dim>>(box),\n        db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                              Frame::Inertial>>(box),\n        db::get<domain::Tags::DetInvJacobian<Frame::ElementLogical,\n                                             Frame::Inertial>>(box),\n        db::get<\n            domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>>(\n            box),\n        db::get<domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,\n                                                  Frame::Inertial>>(box),\n        db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>>(box),\n        db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormalVector<Dim>>>(\n            box),\n        db::get<domain::Tags::Faces<\n            Dim, domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>>>(box),\n        db::get<domain::Tags::Faces<\n            Dim, domain::Tags::DetSurfaceJacobian<Frame::ElementLogical,\n                                                  Frame::Inertial>>>(box),\n        db::get<domain::Tags::Faces<\n            Dim, domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,\n                                                   Frame::Inertial>>>(box),\n        db::get<::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>>(box),\n        db::get<::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>>(box),\n        db::get<::Tags::Mortars<domain::Tags::DetSurfaceJacobian<\n                                    Frame::ElementLogical, Frame::Inertial>,\n                                Dim>>(box),\n        db::get<::Tags::Mortars<elliptic::dg::Tags::PenaltyFactor, Dim>>(box),\n        db::get<elliptic::dg::Tags::Massive>(box),\n        db::get<elliptic::dg::Tags::Formulation>(box), temporal_id,\n        fluxes_args_on_faces,\n        std::forward_as_tuple(db::get<SourcesArgsTags>(box)...));\n\n    // Increment temporal ID\n    db::mutate<TemporalIdTag>(\n        [](const gsl::not_null<size_t*> stored_temporal_id) {\n          ++(*stored_temporal_id);\n        },\n        make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace detail\n\n/*!\n * \\brief Initialize geometric and background quantities for the elliptic DG\n * operator\n *\n * The geometric and background quantities are initialized together because the\n * geometry depends on the background metric through the normalization of face\n * normals. Other examples for background fields are curvature quantities\n * associated with the background metric, or matter sources such as a\n * mass-density in the XCTS equations. All `System::background_fields` are\n * retrieved from the `BackgroundTag` together, to enable re-using cached\n * temporary quantities in the computations. The `variables` function is invoked\n * on the `BackgroundTag` with the inertial coordinates, the element's `Mesh`\n * and the element's inverse Jacobian. These arguments allow computing numeric\n * derivatives, if necessary. The `BackgroundTag` can be set to `void` (default)\n * if the `System` has no background fields.\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::InitialExtents<Dim>`\n *   - `BackgroundTag`\n * - Adds:\n *   - `::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>`\n *   - `::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>`\n *   - `::Tags::Variables<background_fields>`\n * - Adds on internal and external faces:\n *   - `domain::Tags::Coordinates<Dim, Frame::Inertial>`\n *   - `::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim>>`\n *   - `::Tags::Magnitude<domain::Tags::UnnormalizedFaceNormal<Dim>>`\n *   - `::Tags::Variables<background_fields>`\n *\n * \\see elliptic::dg::Actions::apply_operator\n */\ntemplate <typename System, typename BackgroundTag = void>\nusing initialize_operator = tmpl::list<\n    detail::InitializeFacesMortarsAndBackground<System, BackgroundTag>>;\n\n/*!\n * \\brief AMR projectors for the tags added by `initialize_operator`\n */\ntemplate <typename System, typename BackgroundTag = void>\nusing amr_projectors = tmpl::append<\n    tmpl::list<elliptic::dg::InitializeFacesAndMortars<\n        System::volume_dim, typename System::inv_metric_tag, BackgroundTag>>,\n    tmpl::conditional_t<\n        std::is_same_v<typename System::background_fields, tmpl::list<>>,\n        tmpl::list<>,\n        tmpl::list<elliptic::dg::InitializeBackground<\n            System::volume_dim, typename System::background_fields,\n            BackgroundTag>>>>;\n\n/*!\n * \\brief Apply the DG operator to the `PrimalFieldsTag` and write the result to\n * the `OperatorAppliedToFieldsTag`\n *\n * Add the `apply_actions` list to the action list of a parallel component to\n * compute the elliptic DG operator or its linearization. The operator involves\n * a communication between nearest-neighbor elements. See `elliptic::dg` for\n * details on the elliptic DG operator. Make sure to add\n * `elliptic::dg::Actions::initialize_operator` to the initialization phase of\n * your parallel component so the required DataBox tags are set up before\n * applying the operator.\n *\n * The result of the computation is written to the `OperatorAppliedToFieldsTag`.\n * Additionally, the primal fluxes are written to the `PrimalFluxesTag` as an\n * intermediate result. The auxiliary fields and fluxes are discarded to avoid\n * inflating the memory usage.\n *\n * You can specify the `PrimalMortarFieldsTag` and the `PrimalMortarFluxesTag`\n * to re-use mortar-data memory buffers from other operator applications, for\n * example when applying the nonlinear and linearized operator. They default to\n * the `PrimalFieldsTag` and the `PrimalFluxesTag`, meaning memory buffers\n * corresponding to these tags are set up in the DataBox.\n *\n * \\par AMR\n * Also add the `amr_projectors` to the list of AMR projectors to support AMR.\n */\ntemplate <typename System, bool Linearized, typename PrimalFieldsTag,\n          typename PrimalFluxesTag, typename OperatorAppliedToFieldsTag,\n          typename PrimalMortarFieldsTag = PrimalFieldsTag,\n          typename PrimalMortarFluxesTag = PrimalFluxesTag>\nstruct DgOperator {\n  using system = System;\n  using temporal_id_tag = detail::TemporalIdTag;\n\n private:\n  static constexpr size_t Dim = System::volume_dim;\n  using DerivVarsTag = db::add_tag_prefix<::Tags::deriv, PrimalFieldsTag,\n                                          tmpl::size_t<Dim>, Frame::Inertial>;\n public:\n  using apply_actions =\n      tmpl::list<detail::PrepareAndSendMortarData<\n                     System, Linearized, temporal_id_tag, PrimalFieldsTag,\n                     PrimalFluxesTag, OperatorAppliedToFieldsTag,\n                     PrimalMortarFieldsTag, PrimalMortarFluxesTag>,\n                 detail::ReceiveMortarDataAndApplyOperator<\n                     System, Linearized, temporal_id_tag, PrimalFieldsTag,\n                     PrimalFluxesTag, OperatorAppliedToFieldsTag,\n                     PrimalMortarFieldsTag, PrimalMortarFluxesTag>>;\n  using amr_projectors = tmpl::list<\n      ::amr::projectors::DefaultInitialize<\n          DerivVarsTag, PrimalFluxesTag, OperatorAppliedToFieldsTag,\n          ::Tags::Mortars<elliptic::dg::Tags::MortarData<\n                              typename temporal_id_tag::type,\n                              typename PrimalMortarFieldsTag::tags_list,\n                              typename PrimalMortarFluxesTag::tags_list>,\n                          Dim>>,\n      ::amr::projectors::CopyFromCreatorOrLeaveAsIs<temporal_id_tag>>;\n};\n\n/*!\n * \\brief For linear systems, impose inhomogeneous boundary conditions as\n * contributions to the fixed sources (i.e. the RHS of the equations).\n *\n * \\see elliptic::dg::impose_inhomogeneous_boundary_conditions_on_source\n */\ntemplate <typename System, typename FixedSourcesTag,\n          typename FluxesArgsTags =\n              elliptic::get_fluxes_argument_tags<System, false>,\n          typename SourcesArgsTags =\n              elliptic::get_sources_argument_tags<System, false>,\n          typename ModifyBoundaryDataArgsTags =\n              elliptic::get_modify_boundary_data_args_tags<System>>\nstruct ImposeInhomogeneousBoundaryConditionsOnSource;\n\n/// \\cond\ntemplate <typename System, typename FixedSourcesTag, typename... FluxesArgsTags,\n          typename... SourcesArgsTags, typename... ModifyBoundaryDataArgsTags>\nstruct ImposeInhomogeneousBoundaryConditionsOnSource<\n    System, FixedSourcesTag, tmpl::list<FluxesArgsTags...>,\n    tmpl::list<SourcesArgsTags...>, tmpl::list<ModifyBoundaryDataArgsTags...>>\n    : tt::ConformsTo<::amr::protocols::Projector> {\n private:\n  static constexpr size_t Dim = System::volume_dim;\n  using BoundaryConditionsBase = typename System::boundary_conditions_base;\n\n public:  // DataBox mutator, amr::protocols::Projector\n  using const_global_cache_tags =\n      tmpl::list<elliptic::dg::Tags::PenaltyParameter,\n                 elliptic::dg::Tags::Massive, elliptic::dg::Tags::Formulation,\n                 domain::Tags::ExternalBoundaryConditions<Dim>>;\n\n  using return_tags = tmpl::list<::Tags::DataBox>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... AmrData>\n  static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box_ptr,\n                    const AmrData&... /*amr_data*/) {\n    // Just to get the same semantics as in actions\n    auto& box = *box_ptr;\n    const auto& element_id = db::get<domain::Tags::Element<Dim>>(box).id();\n    // Used to retrieve items out of the DataBox to forward to functions\n    const auto get_items = [](const auto&... args) {\n      return std::forward_as_tuple(args...);\n    };\n    const auto& boundary_conditions =\n        db::get<domain::Tags::ExternalBoundaryConditions<Dim>>(box).at(\n            element_id.block_id());\n    const auto apply_boundary_condition = [&box, &boundary_conditions,\n                                           &element_id](\n                                              const Direction<Dim>& direction,\n                                              auto&&... fields_and_fluxes) {\n      ASSERT(boundary_conditions.contains(direction),\n             \"No boundary condition is available in block \"\n                 << element_id.block_id() << \" in direction \" << direction\n                 << \". Make sure you are setting up boundary conditions when \"\n                    \"creating the domain.\");\n      ASSERT(dynamic_cast<const BoundaryConditionsBase*>(\n                 boundary_conditions.at(direction).get()) != nullptr,\n             \"The boundary condition in block \"\n                 << element_id.block_id() << \" in direction \" << direction\n                 << \" has an unexpected type. Make sure it derives off the \"\n                    \"'boundary_conditions_base' class set in the system.\");\n      const auto& boundary_condition =\n          dynamic_cast<const BoundaryConditionsBase&>(\n              *boundary_conditions.at(direction));\n      elliptic::apply_boundary_condition<false>(\n          boundary_condition, box, direction,\n          std::forward<decltype(fields_and_fluxes)>(fields_and_fluxes)...);\n    };\n\n    // Can't `db::get` the arguments for the boundary conditions within\n    // `db::mutate`, so we extract the data to mutate and move it back in when\n    // we're done.\n    typename FixedSourcesTag::type fixed_sources;\n    db::mutate<FixedSourcesTag>(\n        [&fixed_sources](const auto local_fixed_sources) {\n          fixed_sources = std::move(*local_fixed_sources);\n        },\n        make_not_null(&box));\n\n    using fluxes_args_tags =\n        typename elliptic::get_fluxes_argument_tags<System, false>;\n    using fluxes_args_volume_tags =\n        elliptic::get_fluxes_volume_tags<System, false>;\n    DirectionMap<Dim, std::tuple<decltype(db::get<FluxesArgsTags>(box))...>>\n        fluxes_args_on_faces{};\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      fluxes_args_on_faces.emplace(\n          direction, elliptic::util::apply_at<\n                         domain::make_faces_tags<Dim, fluxes_args_tags,\n                                                 fluxes_args_volume_tags>,\n                         fluxes_args_volume_tags>(get_items, box, direction));\n    }\n\n    elliptic::dg::impose_inhomogeneous_boundary_conditions_on_source<System>(\n        make_not_null(&fixed_sources), db::get<domain::Tags::Element<Dim>>(box),\n        db::get<domain::Tags::Mesh<Dim>>(box),\n        db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                              Frame::Inertial>>(box),\n        db::get<domain::Tags::DetInvJacobian<Frame::ElementLogical,\n                                             Frame::Inertial>>(box),\n        db::get<\n            domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>>(\n            box),\n        db::get<domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,\n                                                  Frame::Inertial>>(box),\n        db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>>(box),\n        db::get<domain::Tags::Faces<Dim, domain::Tags::FaceNormalVector<Dim>>>(\n            box),\n        db::get<domain::Tags::Faces<\n            Dim, domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>>>(box),\n        db::get<domain::Tags::Faces<\n            Dim, domain::Tags::DetSurfaceJacobian<Frame::ElementLogical,\n                                                  Frame::Inertial>>>(box),\n        db::get<domain::Tags::Faces<\n            Dim, domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,\n                                                   Frame::Inertial>>>(box),\n        db::get<::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>>(box),\n        db::get<::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>>(box),\n        db::get<::Tags::Mortars<domain::Tags::DetSurfaceJacobian<\n                                    Frame::ElementLogical, Frame::Inertial>,\n                                Dim>>(box),\n        db::get<::Tags::Mortars<elliptic::dg::Tags::PenaltyFactor, Dim>>(box),\n        db::get<elliptic::dg::Tags::Massive>(box),\n        db::get<elliptic::dg::Tags::Formulation>(box), apply_boundary_condition,\n        std::forward_as_tuple(db::get<FluxesArgsTags>(box)...),\n        std::forward_as_tuple(db::get<SourcesArgsTags>(box)...),\n        fluxes_args_on_faces,\n        std::forward_as_tuple(db::get<ModifyBoundaryDataArgsTags>(box)...));\n\n    // Move the mutated data back into the DataBox\n    db::mutate<FixedSourcesTag>(\n        [&fixed_sources](const auto local_fixed_sources) {\n          *local_fixed_sources = std::move(fixed_sources);\n        },\n        make_not_null(&box));\n  }\n};\n/// \\endcond\n\n}  // namespace elliptic::dg::Actions\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApplyOperator.hpp\n  InitializeDomain.hpp\n  )\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Initialization.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\nnamespace tuples {\ntemplate <typename... Tags>\nstruct TaggedTuple;\n}  // namespace tuples\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\nnamespace elliptic::dg::Actions {\n/*!\n * \\ingroup InitializationGroup\n * \\brief Initialize items related to the basic structure of the element\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::Domain<Dim, Frame::Inertial>`\n *   - `domain::Tags::InitialExtents<Dim>`\n *   - `domain::Tags::InitialRefinementLevels<Dim>`\n *   - `domain::Tags::FunctionsOfTime` (Parameters for maps that distort the\n *     domain. These are always evaluated at t=0.)\n * - Adds:\n *   - `domain::Tags::Mesh<Dim>`\n *   - `domain::Tags::Element<Dim>`\n *   - `domain::Tags::ElementMap<Dim, Frame::Inertial>`\n *   - `domain::Tags::Coordinates<Dim, Frame::ElementLogical>`\n *   - `domain::Tags::Coordinates<Dim, Frame::Inertial>`\n *   - `domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n *      Frame::Inertial>`\n *   - `domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>`\n */\ntemplate <size_t Dim>\nstruct InitializeDomain {\n private:\n  using InitializeGeometry = elliptic::dg::InitializeGeometry<Dim>;\n\n public:\n  using simple_tags_from_options =\n      tmpl::list<domain::Tags::InitialExtents<Dim>,\n                 domain::Tags::InitialRefinementLevels<Dim>>;\n  using simple_tags = typename InitializeGeometry::return_tags;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 elliptic::dg::Tags::Quadrature>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeGeometry>(make_not_null(&box), element_id);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace elliptic::dg::Actions\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EllipticDg)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Initialization.cpp\n  Penalty.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  DgElementArray.hpp\n  DgOperator.hpp\n  Initialization.hpp\n  Penalty.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainStructure\n  ErrorHandling\n  FunctionsOfTime\n  LinearOperators\n  ParallelAmr\n  Printf\n  Spectral\n  Utilities\n  INTERFACE\n  Boost::boost\n  DomainCreators\n  Elliptic\n  EllipticSystems\n  Initialization\n  LinearOperators\n  Options\n  Parallel\n  Serialization\n  SystemUtilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(SubdomainOperator)\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/DgElementArray.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementDistribution.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/ElementDistribution.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/DomainDiagnosticInfo.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Protocols/ArrayElementsAllocator.hpp\"\n#include \"Parallel/Tags/Parallelization.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic {\n\n/*!\n * \\brief A `Parallel::protocols::ArrayElementsAllocator` that creates array\n * elements to cover the initial computational domain\n *\n * An element is created for every element ID in every block, determined by the\n * `initial_element_ids` function and the option-created `domain::Tags::Domain`\n * and `domain::Tags::InitialRefinementLevels`. The elements are distributed\n * on processors using the `domain::BlockZCurveProcDistribution`. In both cases,\n * an unordered set of `size_t`s can be passed to the `allocate_array` function\n * which represents physical processors to avoid placing elements on. `Element`s\n * are distributed to processors according to their computational costs\n * determined by the number of grid points.\n */\ntemplate <size_t Dim>\nstruct DefaultElementsAllocator\n    : tt::ConformsTo<Parallel::protocols::ArrayElementsAllocator> {\n  template <typename ParallelComponent>\n  using array_allocation_tags = tmpl::list<>;\n\n  template <typename ParallelComponent, typename Metavariables,\n            typename... InitializationTags>\n  static void apply(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::TaggedTuple<InitializationTags...>& initialization_items,\n      const tuples::tagged_tuple_from_typelist<\n          typename ParallelComponent::array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& element_array =\n        Parallel::get_parallel_component<ParallelComponent>(local_cache);\n    const auto& initial_extents =\n        get<domain::Tags::InitialExtents<Dim>>(initialization_items);\n    const auto basis = Spectral::Basis::Legendre;\n    const auto& quadrature =\n        Parallel::get<elliptic::dg::Tags::Quadrature>(local_cache);\n\n    const auto& domain = Parallel::get<domain::Tags::Domain<Dim>>(local_cache);\n    const auto& initial_refinement_levels =\n        get<domain::Tags::InitialRefinementLevels<Dim>>(initialization_items);\n\n    const size_t number_of_procs =\n        Parallel::number_of_procs<size_t>(local_cache);\n    const size_t number_of_nodes =\n        Parallel::number_of_nodes<size_t>(local_cache);\n    const size_t num_of_procs_to_use = number_of_procs - procs_to_ignore.size();\n\n    const auto& blocks = domain.blocks();\n\n    const std::optional<domain::ElementWeight>& element_weight =\n        get<domain::Tags::ElementDistribution>(local_cache);\n\n    domain::BlockZCurveProcDistribution<Dim> element_distribution{};\n    if (element_weight.has_value()) {\n      const std::unordered_map<ElementId<Dim>, double> element_costs =\n          domain::get_element_costs(blocks, initial_refinement_levels,\n                                    initial_extents, element_weight.value(),\n                                    basis, quadrature);\n      element_distribution = domain::BlockZCurveProcDistribution<Dim>{\n          element_costs,   num_of_procs_to_use,\n          blocks,          initial_refinement_levels,\n          initial_extents, procs_to_ignore};\n    }\n\n    // Will be used to print domain diagnostic info\n    std::vector<size_t> elements_per_core(number_of_procs, 0_st);\n    std::vector<size_t> elements_per_node(number_of_nodes, 0_st);\n    std::vector<size_t> grid_points_per_core(number_of_procs, 0_st);\n    std::vector<size_t> grid_points_per_node(number_of_nodes, 0_st);\n\n    size_t which_proc = 0;\n    for (const auto& block : blocks) {\n      const size_t grid_points_per_element = alg::accumulate(\n          initial_extents[block.id()], 1_st, std::multiplies<size_t>());\n\n      const std::vector<ElementId<Dim>> element_ids = initial_element_ids(\n          block.id(), initial_refinement_levels[block.id()]);\n\n      // Distributed with weighted space filling curve\n      if (element_weight.has_value()) {\n        for (const auto& element_id : element_ids) {\n          const size_t target_proc =\n              element_distribution.get_proc_for_element(element_id);\n          element_array(element_id)\n              .insert(global_cache, initialization_items, target_proc);\n\n          const size_t target_node =\n              Parallel::node_of<size_t>(target_proc, local_cache);\n          ++elements_per_core[target_proc];\n          ++elements_per_node[target_node];\n          grid_points_per_core[target_proc] += grid_points_per_element;\n          grid_points_per_node[target_node] += grid_points_per_element;\n        }\n      } else {\n        // Distributed with round-robin\n        for (size_t i = 0; i < element_ids.size(); ++i) {\n          while (procs_to_ignore.find(which_proc) != procs_to_ignore.end()) {\n            which_proc = which_proc + 1 == number_of_procs ? 0 : which_proc + 1;\n          }\n\n          element_array(ElementId<Dim>(element_ids[i]))\n              .insert(global_cache, initialization_items, which_proc);\n\n          const size_t target_node =\n              Parallel::node_of<size_t>(which_proc, local_cache);\n          ++elements_per_core[which_proc];\n          ++elements_per_node[target_node];\n          grid_points_per_core[which_proc] += grid_points_per_element;\n          grid_points_per_node[target_node] += grid_points_per_element;\n\n          which_proc = which_proc + 1 == number_of_procs ? 0 : which_proc + 1;\n        }\n      }\n    }\n    element_array.doneInserting();\n\n    Parallel::printf(\"\\n%s\\n\", domain::diagnostic_info(\n                                   domain.blocks().size(), local_cache,\n                                   elements_per_core, elements_per_node,\n                                   grid_points_per_core, grid_points_per_node));\n  }\n};\n\n/*!\n * \\brief The parallel component responsible for managing the DG elements that\n * compose the computational domain\n *\n * This parallel component will perform the actions specified by the\n * `PhaseDepActionList`.\n *\n * \\note This parallel component is nearly identical to\n * `Evolution/DiscontinuousGalerkin/DgElementArray.hpp` right now, but will\n * likely diverge in the future, for instance to support a multigrid domain.\n *\n */\ntemplate <typename Metavariables, typename PhaseDepActionList,\n          typename ElementsAllocator =\n              DefaultElementsAllocator<Metavariables::volume_dim>>\nstruct DgElementArray {\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  static_assert(\n      tt::assert_conforms_to_v<ElementsAllocator,\n                               Parallel::protocols::ArrayElementsAllocator>);\n\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = PhaseDepActionList;\n  using array_index = ElementId<volume_dim>;\n\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<volume_dim>,\n                                             domain::Tags::ElementDistribution>;\n\n  using array_allocation_tags =\n      typename ElementsAllocator::template array_allocation_tags<\n          DgElementArray>;\n\n  using simple_tags_from_options =\n      tmpl::append<Parallel::get_simple_tags_from_options<\n                       Parallel::get_initialization_actions_list<\n                           phase_dependent_action_list>>,\n                   array_allocation_tags>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_items,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n          array_allocation_items = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    ElementsAllocator::template apply<DgElementArray>(\n        global_cache, initialization_items, array_allocation_items,\n        procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<DgElementArray>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/DgOperator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/Variables/FrameTransform.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/GetFluxesComputer.hpp\"\n#include \"Elliptic/Systems/GetSourcesComputer.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/SimpleBoundaryData.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/SimpleMortarData.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/WeakDivergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\brief Functionality related to discontinuous Galerkin discretizations of\n * elliptic equations\n *\n * The following is a brief overview of the elliptic DG schemes that are\n * implemented here. The scheme is described in detail in \\cite Fischer2021voj.\n *\n * The DG schemes apply to any elliptic PDE that can be formulated in\n * first-order flux-form, as detailed by\n * `elliptic::protocols::FirstOrderSystem`.\n * The DG discretization of equations in this first-order form amounts to\n * projecting the equations on the set of basis functions that we also use to\n * represent the fields on the computational grid. The currently implemented DG\n * operator uses Lagrange interpolating polynomials w.r.t. Legendre-Gauss or\n * Legendre-Gauss-Lobatto collocation points as basis functions. Skipping all\n * further details here, the discretization results in a linear equation\n * \\f$A(u)=b\\f$ over all grid points and primal variables. Solving the elliptic\n * equations amounts to numerically inverting the DG operator \\f$A\\f$, typically\n * without ever constructing the full matrix but by employing an iterative\n * linear solver that repeatedly applies the DG operator to \"test data\". Note\n * that the DG operator applies directly to the primal variables. Auxiliary\n * variables are only computed temporarily and don't inflate the size of the\n * operator. This means the DG operator essentially computes second derivatives\n * of the primal variables, modified by the fluxes and sources of the system\n * as well as by DG boundary corrections that couple grid points across element\n * boundaries.\n *\n * \\par Boundary corrections:\n * In this implementation we employ the \"internal penalty\" DG scheme that\n * couples grid points across nearest-neighbor elements through the fluxes:\n *\n * \\f{align}\n * \\label{eq:internal_penalty_auxiliary}\n * u^* &= \\frac{1}{2} \\left(u^\\mathrm{int} + u^\\mathrm{ext}\\right) \\\\\n * \\label{eq:internal_penalty_primal}\n * (n_i F^i)^* &= \\frac{1}{2} n_i \\left(\n * F^i_\\mathrm{int} + F^i_\\mathrm{ext} \\right)\n * - \\sigma n_i F^i(n_j (u^\\mathrm{int} - u^\\mathrm{ext}))\n * \\f}\n *\n * Note that \\f$n_i\\f$ denotes the face normal on the \"interior\" side of the\n * element under consideration. We assume \\f$n^\\mathrm{ext}_i=-n_i\\f$ in the\n * implementation, i.e. face normals don't depend on the dynamic variables\n * (which may be discontinuous on element faces). This is the case for the\n * problems we are expecting to solve, because those will be on fixed background\n * metrics (e.g. a conformal metric for the XCTS system). Numerically, the face\n * normals on either side of a mortar may nonetheless be different because the\n * two faces adjacent to the mortar may resolve them at different resolutions.\n *\n * Also note that the numerical fluxes intentionally don't depend on the\n * auxiliary field values \\f$v\\f$. This property allows us to communicate data\n * for both the primal and auxiliary boundary corrections together, instead of\n * communicating them in two steps. If we were to resort to a two-step\n * communication we could replace the derivatives in \\f$(n_i F^i)^*\\f$ with\n * \\f$v\\f$, which would result in a generalized \"stabilized central flux\" that\n * is slightly less sparse than the internal penalty flux (see e.g.\n * \\cite HesthavenWarburton, section 7.2). We could also choose to ignore the\n * fluxes in the penalty term, but preliminary tests suggest that this may hurt\n * convergence.\n *\n * For a Poisson system (see `Poisson::FirstOrderSystem`) this numerical flux\n * reduces to the standard internal penalty flux (see e.g.\n * \\cite HesthavenWarburton, section 7.2, or \\cite Arnold2002):\n *\n * \\f{align}\n * u^* &= \\frac{1}{2} \\left(u^\\mathrm{int} + u^\\mathrm{ext}\\right) \\\\\n * (n_i F^i)^* &= n_i v_i^* = \\frac{1}{2} n_i \\left(\n * \\partial_i u^\\mathrm{int} + \\partial_i u^\\mathrm{ext}\\right)\n * - \\sigma \\left(u^\\mathrm{int} - u^\\mathrm{ext}\\right)\n * \\f}\n *\n * where a sum over repeated indices is assumed, since the equation is\n * formulated on a Euclidean geometry.\n *\n * The penalty factor \\f$\\sigma\\f$ is responsible for removing zero eigenmodes\n * and impacts the conditioning of the linear operator to be solved. See\n * `elliptic::dg::penalty` for details. For the element size that goes into\n * computing the penalty we choose\n * \\f$h=\\frac{J_\\mathrm{volume}}{J_\\mathrm{face}}\\f$, i.e. the ratio of Jacobi\n * determinants from logical to inertial coordinates in the element volume and\n * on the element face, both evaluated on the face (see \\cite Vincent2019qpd).\n * Since both \\f$N_\\mathrm{points}\\f$ and \\f$h\\f$ can be different on either\n * side of the element boundary we take the maximum of \\f$N_\\mathrm{points}\\f$\n * and the pointwise minimum of \\f$h\\f$ across the element boundary as is done\n * in \\cite Vincent2019qpd. Note that we use the number of points\n * \\f$N_\\mathrm{points}\\f$ where \\cite Vincent2019qpd uses the polynomial degree\n * \\f$N_\\mathrm{points} - 1\\f$ because we found unstable configurations on\n * curved meshes when using the polynomial degree. Optimizing the penalty on\n * curved meshes is subject to further investigation.\n *\n * \\par Discontinuous fluxes:\n * The DG operator also supports systems with potentially discontinuous fluxes,\n * such as elasticity with layered materials. The way to handle the\n * discontinuous fluxes in the DG scheme is described in \\cite Vu2023thn.\n * Essentially, we evaluate the penalty term in\n * Eq. $\\ref{eq:internal_penalty_primal}$ on both sides of an element boundary\n * and take the average. The other terms in the numerical flux remain unchanged.\n */\nnamespace elliptic::dg {\n\n/// Data that is projected to mortars and communicated across element\n/// boundaries\ntemplate <typename PrimalFields, typename PrimalFluxes>\nusing BoundaryData = ::dg::SimpleBoundaryData<\n    tmpl::append<PrimalFields,\n                 db::wrap_tags_in<::Tags::NormalDotFlux, PrimalFields>>,\n    tmpl::list<>>;\n\n/// Boundary data on both sides of a mortar.\n///\n/// \\note This is a struct (as opposed to a type alias) so it can be used to\n/// deduce the template parameters\ntemplate <typename TemporalId, typename PrimalFields, typename PrimalFluxes>\nstruct MortarData\n    : ::dg::SimpleMortarData<TemporalId,\n                             BoundaryData<PrimalFields, PrimalFluxes>,\n                             BoundaryData<PrimalFields, PrimalFluxes>> {};\n\nnamespace Tags {\n/// Holds `elliptic::dg::MortarData`, i.e. boundary data on both sides of a\n/// mortar\ntemplate <typename TemporalId, typename PrimalFields, typename PrimalFluxes>\nstruct MortarData : db::SimpleTag {\n  using type = elliptic::dg::MortarData<TemporalId, PrimalFields, PrimalFluxes>;\n};\n}  // namespace Tags\n\nnamespace detail {\n\n// Mass-conservative restriction: R = M^{-1}_face P^T M_mortar\n//\n// Note that projecting the mortar data times the Jacobian using\n// `Spectral::projection_matrix_child_to_parent(operand_is_massive=false)` is\n// equivalent to this implementation on Gauss grids. However, on Gauss-Lobatto\n// grids we usually diagonally approximate the mass matrix (\"mass lumping\") but\n// `projection_matrix_child_to_parent(operand_is_massive=false)` uses the full\n// mass matrix. Therefore, the two implementations differ slightly on\n// Gauss-Lobatto grids. Furthermore, note that\n// `projection_matrix_child_to_parent(operand_is_massive=false)` already\n// includes the factors of two that account for the mortar size, so they must be\n// omitted from the mortar Jacobian when using that approach.\ntemplate <typename TagsList, size_t FaceDim>\nVariables<TagsList> mass_conservative_restriction(\n    Variables<TagsList> mortar_vars,\n    [[maybe_unused]] const Mesh<FaceDim>& mortar_mesh,\n    [[maybe_unused]] const ::dg::MortarSize<FaceDim>& mortar_size,\n    [[maybe_unused]] const Scalar<DataVector>& mortar_jacobian,\n    [[maybe_unused]] const Mesh<FaceDim> face_mesh,\n    [[maybe_unused]] const Scalar<DataVector>& face_jacobian) {\n  if constexpr (FaceDim == 0) {\n    return mortar_vars;\n  } else {\n    const auto projection_matrices =\n        Spectral::projection_matrix_child_to_parent(mortar_mesh, face_mesh,\n                                                    mortar_size, true);\n    mortar_vars *= get(mortar_jacobian);\n    ::dg::apply_mass_matrix(make_not_null(&mortar_vars), mortar_mesh);\n    auto face_vars =\n        apply_matrices(projection_matrices, mortar_vars, mortar_mesh.extents());\n    face_vars /= get(face_jacobian);\n    ::dg::apply_inverse_mass_matrix(make_not_null(&face_vars), face_mesh);\n    return face_vars;\n  }\n}\n\ntemplate <typename System, bool Linearized,\n          typename PrimalFields = typename System::primal_fields,\n          typename PrimalFluxes = typename System::primal_fluxes>\nstruct DgOperatorImpl;\n\ntemplate <typename System, bool Linearized, typename... PrimalFields,\n          typename... PrimalFluxes>\nstruct DgOperatorImpl<System, Linearized, tmpl::list<PrimalFields...>,\n                      tmpl::list<PrimalFluxes...>> {\n  static_assert(\n      tt::assert_conforms_to_v<System, elliptic::protocols::FirstOrderSystem>);\n\n  static constexpr size_t Dim = System::volume_dim;\n  using FluxesComputer = elliptic::get_fluxes_computer<System, Linearized>;\n  using SourcesComputer = elliptic::get_sources_computer<System, Linearized>;\n\n  struct AllDirections {\n    bool operator()(const Direction<Dim>& /*unused*/) const { return true; }\n  };\n\n  struct NoDataIsZero {\n    bool operator()(const ElementId<Dim>& /*unused*/) const { return false; }\n  };\n\n  static constexpr auto full_mortar_size =\n      make_array<Dim - 1>(Spectral::SegmentSize::Full);\n\n  template <bool AllDataIsZero, typename... DerivVars, typename... PrimalVars,\n            typename... PrimalFluxesVars, typename... PrimalMortarVars,\n            typename... PrimalMortarFluxes, typename TemporalId,\n            typename ApplyBoundaryCondition, typename... FluxesArgs,\n            typename DataIsZero = NoDataIsZero,\n            typename DirectionsPredicate = AllDirections>\n  static void prepare_mortar_data(\n      const gsl::not_null<Variables<tmpl::list<DerivVars...>>*> deriv_vars,\n      const gsl::not_null<Variables<tmpl::list<PrimalFluxesVars...>>*>\n          primal_fluxes,\n      const gsl::not_null<::dg::MortarMap<\n          Dim, MortarData<TemporalId, tmpl::list<PrimalMortarVars...>,\n                          tmpl::list<PrimalMortarFluxes...>>>*>\n          all_mortar_data,\n      const Variables<tmpl::list<PrimalVars...>>& primal_vars,\n      const Element<Dim>& element, const Mesh<Dim>& mesh,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const DirectionMap<Dim, tnsr::i<DataVector, Dim>>& face_normals,\n      const ::dg::MortarMap<Dim, Mesh<Dim - 1>>& all_mortar_meshes,\n      const ::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>& all_mortar_sizes,\n      const TemporalId& temporal_id,\n      const ApplyBoundaryCondition& apply_boundary_condition,\n      const std::tuple<FluxesArgs...>& fluxes_args,\n      const DataIsZero& data_is_zero = NoDataIsZero{},\n      const DirectionsPredicate& directions_predicate = AllDirections{}) {\n    static_assert(\n        sizeof...(PrimalVars) == sizeof...(PrimalFields) and\n            sizeof...(PrimalFluxesVars) == sizeof...(PrimalFluxes),\n        \"The number of variables must match the number of system fields.\");\n    static_assert(\n        (std::is_same_v<typename PrimalVars::type,\n                        typename PrimalFields::type> and\n         ...) and\n            (std::is_same_v<typename PrimalFluxesVars::type,\n                            typename PrimalFluxes::type> and\n             ...),\n        \"The variables must have the same tensor types as the system fields.\");\n#ifdef SPECTRE_DEBUG\n    for (size_t d = 0; d < Dim; ++d) {\n      ASSERT(mesh.basis(d) == Spectral::Basis::Legendre and\n                 (mesh.quadrature(d) == Spectral::Quadrature::GaussLobatto or\n                  mesh.quadrature(d) == Spectral::Quadrature::Gauss),\n             \"The elliptic DG operator is currently only implemented for \"\n             \"Legendre-Gauss(-Lobatto) grids. Found basis '\"\n                 << mesh.basis(d) << \"' and quadrature '\" << mesh.quadrature(d)\n                 << \"' in dimension \" << d << \".\");\n    }\n#endif  // SPECTRE_DEBUG\n    const auto& element_id = element.id();\n    const bool local_data_is_zero = data_is_zero(element_id);\n    ASSERT(Linearized or not local_data_is_zero,\n           \"Only a linear operator can take advantage of the knowledge that \"\n           \"the operand is zero. Don't return 'true' in 'data_is_zero' unless \"\n           \"you also set 'Linearized' to 'true'.\");\n    const size_t num_points = mesh.number_of_grid_points();\n\n    // This function and the one below allocate various Variables to compute\n    // intermediate quantities. It could be a performance optimization to reduce\n    // the number of these allocations and/or move some of the memory buffers\n    // into the DataBox to keep them around permanently. The latter should be\n    // informed by profiling.\n\n    // Compute partial derivatives grad(u) of the system variables, and from\n    // those the fluxes F^i(grad(u)) in the volume. We will take the divergence\n    // of the fluxes in the `apply_operator` function below to compute the full\n    // elliptic equation -div(F) + S = f(x).\n    if (AllDataIsZero or local_data_is_zero) {\n      deriv_vars->initialize(num_points, 0.);\n      primal_fluxes->initialize(num_points, 0.);\n    } else {\n      // Compute partial derivatives of the variables\n      partial_derivatives(deriv_vars, primal_vars, mesh, inv_jacobian);\n      // Compute the fluxes\n      primal_fluxes->initialize(num_points);\n      std::apply(\n          [&primal_fluxes, &primal_vars, &deriv_vars,\n           &element_id](const auto&... expanded_fluxes_args) {\n            if constexpr (FluxesComputer::is_discontinuous) {\n              FluxesComputer::apply(\n                  make_not_null(&get<PrimalFluxesVars>(*primal_fluxes))...,\n                  expanded_fluxes_args..., element_id,\n                  get<PrimalVars>(primal_vars)...,\n                  get<DerivVars>(*deriv_vars)...);\n            } else {\n              (void)element_id;\n              FluxesComputer::apply(\n                  make_not_null(&get<PrimalFluxesVars>(*primal_fluxes))...,\n                  expanded_fluxes_args..., get<PrimalVars>(primal_vars)...,\n                  get<DerivVars>(*deriv_vars)...);\n            }\n          },\n          fluxes_args);\n    }\n\n    // Populate the mortar data on this element's side of the boundary so it's\n    // ready to be sent to neighbors.\n    for (const auto& direction : [&element]() -> const auto& {\n           if constexpr (AllDataIsZero) {\n             // Skipping internal boundaries for all-zero data because they\n             // won't contribute boundary corrections anyway (data on both sides\n             // of the boundary is the same). For all-zero data we are\n             // interested in external boundaries, to extract inhomogeneous\n             // boundary conditions from a non-linear operator.\n             return element.external_boundaries();\n           } else {\n             (void)element;\n             return Direction<Dim>::all_directions();\n           };\n         }()) {\n      if (not directions_predicate(direction)) {\n        continue;\n      }\n      const bool is_internal = element.neighbors().contains(direction);\n      // Skip directions altogether when both this element and all neighbors in\n      // the direction have zero data. These boundaries won't contribute\n      // corrections, because the data is the same on both sides. External\n      // boundaries also count as zero data here, because they are linearized\n      // (see assert above).\n      if (local_data_is_zero and\n          (not is_internal or\n           alg::all_of(element.neighbors().at(direction), data_is_zero))) {\n        continue;\n      }\n      const auto face_mesh = mesh.slice_away(direction.dimension());\n      const size_t face_num_points = face_mesh.number_of_grid_points();\n      const auto& face_normal = face_normals.at(direction);\n      Variables<tmpl::list<PrimalFluxesVars...>> primal_fluxes_on_face{};\n      BoundaryData<tmpl::list<PrimalMortarVars...>,\n                   tmpl::list<PrimalMortarFluxes...>>\n          boundary_data{};\n      if (AllDataIsZero or local_data_is_zero) {\n        if (is_internal) {\n          // We manufacture zero boundary data directly on the mortars below.\n          // Nothing to do here.\n        } else {\n          boundary_data.field_data.initialize(face_num_points, 0.);\n        }\n      } else {\n        boundary_data.field_data.initialize(face_num_points);\n        primal_fluxes_on_face.initialize(face_num_points);\n        // Project fields to faces\n        // Note: need to convert tags of `Variables` because\n        // `project_contiguous_data_to_boundary` requires that the face and\n        // volume tags are subsets.\n        Variables<\n            tmpl::list<PrimalVars..., ::Tags::NormalDotFlux<PrimalVars>...>>\n            boundary_data_ref{};\n        boundary_data_ref.set_data_ref(boundary_data.field_data.data(),\n                                       boundary_data.field_data.size());\n        ::dg::project_contiguous_data_to_boundary(\n            make_not_null(&boundary_data_ref), primal_vars, mesh, direction);\n        // Compute n_i F^i on faces\n        ::dg::project_contiguous_data_to_boundary(\n            make_not_null(&primal_fluxes_on_face), *primal_fluxes, mesh,\n            direction);\n        EXPAND_PACK_LEFT_TO_RIGHT(normal_dot_flux(\n            make_not_null(&get<::Tags::NormalDotFlux<PrimalMortarVars>>(\n                boundary_data.field_data)),\n            face_normal, get<PrimalFluxesVars>(primal_fluxes_on_face)));\n      }\n\n      if (is_internal) {\n        if constexpr (not AllDataIsZero) {\n          // Project boundary data on internal faces to mortars\n          for (const auto& neighbor_id : element.neighbors().at(direction)) {\n            if (local_data_is_zero and data_is_zero(neighbor_id)) {\n              continue;\n            }\n            const ::dg::MortarId<Dim> mortar_id{direction, neighbor_id};\n            const auto& mortar_mesh = all_mortar_meshes.at(mortar_id);\n            const auto& mortar_size = all_mortar_sizes.at(mortar_id);\n            if (local_data_is_zero) {\n              // No need to project anything. We just manufacture zero boundary\n              // data on the mortar.\n              BoundaryData<tmpl::list<PrimalMortarVars...>,\n                           tmpl::list<PrimalMortarFluxes...>>\n                  zero_boundary_data{};\n              zero_boundary_data.field_data.initialize(\n                  mortar_mesh.number_of_grid_points(), 0.);\n              (*all_mortar_data)[mortar_id].local_insert(\n                  temporal_id, std::move(zero_boundary_data));\n              continue;\n            }\n            // When no projection is necessary we can safely move the boundary\n            // data from the face as there is only a single neighbor in this\n            // direction\n            auto projected_boundary_data =\n                Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)\n                    // NOLINTNEXTLINE\n                    ? boundary_data.project_to_mortar(face_mesh, mortar_mesh,\n                                                      mortar_size)\n                    : std::move(boundary_data);  // NOLINT\n            (*all_mortar_data)[mortar_id].local_insert(\n                temporal_id, std::move(projected_boundary_data));\n          }\n        }\n      } else {\n        // No need to do projections on external boundaries\n        const ::dg::MortarId<Dim> mortar_id{\n            direction, ElementId<Dim>::external_boundary_id()};\n        (*all_mortar_data)[mortar_id].local_insert(temporal_id, boundary_data);\n\n        // -------------------------\n        // Apply boundary conditions\n        // -------------------------\n        //\n        // To apply boundary conditions we fill the boundary data with\n        // \"exterior\" or \"ghost\" data and set it as remote mortar data, so\n        // external boundaries behave just like internal boundaries when\n        // applying boundary corrections.\n        //\n        // The `apply_boundary_conditions` invocable is expected to impose\n        // boundary conditions by modifying the fields and fluxes that are\n        // passed by reference. Dirichlet-type boundary conditions are imposed\n        // by modifying the fields, and Neumann-type boundary conditions are\n        // imposed by modifying the interior n.F. Note that all data passed to\n        // the boundary conditions is taken from the \"interior\" side of the\n        // boundary, i.e. with a normal vector that points _out_ of the\n        // computational domain.\n        Variables<tmpl::list<DerivVars...>> deriv_vars_on_boundary{};\n        if (AllDataIsZero or local_data_is_zero) {\n          deriv_vars_on_boundary.initialize(face_num_points, 0.);\n        } else {\n          deriv_vars_on_boundary.initialize(face_num_points);\n          ::dg::project_contiguous_data_to_boundary(\n              make_not_null(&deriv_vars_on_boundary), *deriv_vars, mesh,\n              direction);\n        }\n        apply_boundary_condition(\n            direction,\n            make_not_null(&get<PrimalMortarVars>(boundary_data.field_data))...,\n            make_not_null(&get<::Tags::NormalDotFlux<PrimalMortarVars>>(\n                boundary_data.field_data))...,\n            get<DerivVars>(deriv_vars_on_boundary)...);\n\n        // Invert the sign of the fluxes to account for the inverted normal on\n        // exterior faces. Also multiply by 2 and add the interior fluxes to\n        // impose the boundary conditions on the _average_ instead of just\n        // setting the fields on the exterior:\n        //   (Dirichlet)  u_D = avg(u) = 1/2 (u_int + u_ext)\n        //                => u_ext = 2 u_D - u_int\n        //   (Neumann)    (n.F)_N = avg(n.F) = 1/2 [(n.F)_int - (n.F)_ext]\n        //                => (n.F)_ext = -2 (n.F)_N + (n.F)_int]\n        const auto impose_on_average = [](const auto exterior_field,\n                                          const auto& interior_field) {\n          for (size_t i = 0; i < interior_field.size(); ++i) {\n            (*exterior_field)[i] *= 2.;\n            (*exterior_field)[i] -= interior_field[i];\n          }\n        };\n        EXPAND_PACK_LEFT_TO_RIGHT(impose_on_average(\n            make_not_null(&get<PrimalMortarVars>(boundary_data.field_data)),\n            get<PrimalMortarVars>(all_mortar_data->at(mortar_id)\n                                      .local_data(temporal_id)\n                                      .field_data)));\n        const auto invert_sign_and_impose_on_average =\n            [](const auto exterior_n_dot_flux,\n               const auto& interior_n_dot_flux) {\n              for (size_t i = 0; i < interior_n_dot_flux.size(); ++i) {\n                (*exterior_n_dot_flux)[i] *= -2.;\n                (*exterior_n_dot_flux)[i] += interior_n_dot_flux[i];\n              }\n            };\n        EXPAND_PACK_LEFT_TO_RIGHT(invert_sign_and_impose_on_average(\n            make_not_null(&get<::Tags::NormalDotFlux<PrimalMortarVars>>(\n                boundary_data.field_data)),\n            get<::Tags::NormalDotFlux<PrimalMortarVars>>(\n                all_mortar_data->at(mortar_id)\n                    .local_data(temporal_id)\n                    .field_data)));\n\n        // Store the exterior boundary data on the mortar\n        all_mortar_data->at(mortar_id).remote_insert(temporal_id,\n                                                     std::move(boundary_data));\n      }  // if (is_internal)\n    }  // loop directions\n  }\n\n  // --- This is essentially a break to communicate the mortar data ---\n\n  template <typename... OperatorTags, typename... PrimalVars,\n            typename... DerivVars, typename... PrimalFluxesVars,\n            typename... PrimalMortarVars, typename... PrimalMortarFluxes,\n            typename TemporalId, typename... FluxesArgs,\n            typename... SourcesArgs, typename DataIsZero = NoDataIsZero,\n            typename DirectionsPredicate = AllDirections>\n  static void apply_operator(\n      const gsl::not_null<Variables<tmpl::list<OperatorTags...>>*>\n          operator_applied_to_vars,\n      const gsl::not_null<::dg::MortarMap<\n          Dim, MortarData<TemporalId, tmpl::list<PrimalMortarVars...>,\n                          tmpl::list<PrimalMortarFluxes...>>>*>\n          all_mortar_data,\n      const Variables<tmpl::list<PrimalVars...>>& primal_vars,\n      const Variables<tmpl::list<DerivVars...>>& deriv_vars,\n      // Taking the primal fluxes computed in the `prepare_mortar_data` function\n      // by const-ref here because other code might use them and so we don't\n      // want to modify them by adding boundary corrections. E.g. linearized\n      // sources use the nonlinear fields and fluxes as background fields.\n      const Variables<tmpl::list<PrimalFluxesVars...>>& primal_fluxes,\n      const Element<Dim>& element, const Mesh<Dim>& mesh,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const Scalar<DataVector>& det_inv_jacobian,\n      const Scalar<DataVector>& det_jacobian,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& det_times_inv_jacobian,\n      const DirectionMap<Dim, tnsr::i<DataVector, Dim>>& face_normals,\n      const DirectionMap<Dim, tnsr::I<DataVector, Dim>>& face_normal_vectors,\n      const DirectionMap<Dim, Scalar<DataVector>>& face_normal_magnitudes,\n      const DirectionMap<Dim, Scalar<DataVector>>& face_jacobians,\n      const DirectionMap<Dim,\n                         InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                         Frame::Inertial>>&\n          face_jacobian_times_inv_jacobians,\n      const ::dg::MortarMap<Dim, Mesh<Dim - 1>>& all_mortar_meshes,\n      const ::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>& all_mortar_sizes,\n      const ::dg::MortarMap<Dim, Scalar<DataVector>>& mortar_jacobians,\n      const ::dg::MortarMap<Dim, Scalar<DataVector>>& penalty_factors,\n      const bool massive, const ::dg::Formulation formulation,\n      const TemporalId& /*temporal_id*/,\n      const DirectionMap<Dim, std::tuple<FluxesArgs...>>& fluxes_args_on_faces,\n      const std::tuple<SourcesArgs...>& sources_args,\n      const DataIsZero& data_is_zero = NoDataIsZero{},\n      const DirectionsPredicate& directions_predicate = AllDirections{}) {\n    static_assert(\n        sizeof...(PrimalVars) == sizeof...(PrimalFields) and\n            sizeof...(PrimalFluxesVars) == sizeof...(PrimalFluxes) and\n            sizeof...(PrimalMortarVars) == sizeof...(PrimalFields) and\n            sizeof...(PrimalMortarFluxes) == sizeof...(PrimalFluxes) and\n            sizeof...(OperatorTags) == sizeof...(PrimalFields),\n        \"The number of variables must match the number of system fields.\");\n    static_assert(\n        (std::is_same_v<typename PrimalVars::type,\n                        typename PrimalFields::type> and\n         ...) and\n            (std::is_same_v<typename PrimalFluxesVars::type,\n                            typename PrimalFluxes::type> and\n             ...) and\n            (std::is_same_v<typename PrimalMortarVars::type,\n                            typename PrimalFields::type> and\n             ...) and\n            (std::is_same_v<typename PrimalMortarFluxes::type,\n                            typename PrimalFluxes::type> and\n             ...) and\n            (std::is_same_v<typename OperatorTags::type,\n                            typename PrimalFields::type> and\n             ...),\n        \"The variables must have the same tensor types as the system fields.\");\n#ifdef SPECTRE_DEBUG\n    for (size_t d = 0; d < Dim; ++d) {\n      ASSERT(mesh.basis(d) == Spectral::Basis::Legendre and\n                 (mesh.quadrature(d) == Spectral::Quadrature::GaussLobatto or\n                  mesh.quadrature(d) == Spectral::Quadrature::Gauss),\n             \"The elliptic DG operator is currently only implemented for \"\n             \"Legendre-Gauss(-Lobatto) grids. Found basis '\"\n                 << mesh.basis(d) << \"' and quadrature '\" << mesh.quadrature(d)\n                 << \"' in dimension \" << d << \".\");\n    }\n#endif  // SPECTRE_DEBUG\n    const auto& element_id = element.id();\n    const bool local_data_is_zero = data_is_zero(element_id);\n    ASSERT(Linearized or not local_data_is_zero,\n           \"Only a linear operator can take advantage of the knowledge that \"\n           \"the operand is zero. Don't return 'true' in 'data_is_zero' unless \"\n           \"you also set 'Linearized' to 'true'.\");\n    const size_t num_points = mesh.number_of_grid_points();\n\n    // This function and the one above allocate various Variables to compute\n    // intermediate quantities. It could be a performance optimization to reduce\n    // the number of these allocations and/or move some of the memory buffers\n    // into the DataBox to keep them around permanently. The latter should be\n    // informed by profiling.\n\n    // Compute volume terms: -div(F) + S\n    if (local_data_is_zero) {\n      operator_applied_to_vars->initialize(num_points, 0.);\n    } else {\n      // \"Massive\" operators retain the factors from the volume integral:\n      //   \\int_volume div(F) \\phi_p = w_p det(J)_p div(F)_p\n      // Here, `w` are the quadrature weights (the diagonal logical mass matrix\n      // with mass-lumping) and det(J) is the Jacobian determinant. The\n      // quantities are evaluated at the grid point `p`.\n      if (formulation == ::dg::Formulation::StrongInertial) {\n        // Compute strong divergence:\n        //   div(F) = (J^\\hat{i}_i)_p \\sum_q (D_\\hat{i})_pq (F^i)_q.\n        divergence(operator_applied_to_vars, primal_fluxes, mesh,\n                   massive ? det_times_inv_jacobian : inv_jacobian);\n        // This is the sign flip that makes the operator _minus_ the Laplacian\n        // for a Poisson system\n        *operator_applied_to_vars *= -1.;\n      } else if (formulation == ::dg::Formulation::StrongLogical) {\n        // Strong divergence but with the Jacobian moved into the divergence:\n        //   div(F) = 1/J_p \\sum_q (D_\\hat{i})_pq J_q (J^\\hat{i}_i)_q (F^i)_q.\n        const auto logical_fluxes = transform::first_index_to_different_frame(\n            primal_fluxes, det_times_inv_jacobian);\n        logical_divergence(operator_applied_to_vars, logical_fluxes, mesh);\n        if (massive) {\n          *operator_applied_to_vars *= -1.;\n        } else {\n          *operator_applied_to_vars *= -1. * get(det_inv_jacobian);\n        }\n      } else if (formulation == ::dg::Formulation::WeakInertial) {\n        // Compute weak divergence:\n        //   F^i \\partial_i \\phi = 1/w_p \\sum_q\n        //     (D^T_\\hat{i})_pq (w det(J) J^\\hat{i}_i F^i)_q\n        weak_divergence(operator_applied_to_vars, primal_fluxes, mesh,\n                        det_times_inv_jacobian);\n        if (not massive) {\n          *operator_applied_to_vars *= get(det_inv_jacobian);\n        }\n      } else {\n        ERROR(\"Unsupported DG formulation: \"\n              << formulation\n              << \"\\nSupported formulations are: StrongInertial, WeakInertial, \"\n                 \"StrongLogical.\");\n      }\n      if constexpr (not std::is_same_v<SourcesComputer, void>) {\n        Variables<tmpl::list<OperatorTags...>> sources{num_points, 0.};\n        std::apply(\n            [&sources, &primal_vars, &deriv_vars,\n             &primal_fluxes](const auto&... expanded_sources_args) {\n              SourcesComputer::apply(\n                  make_not_null(&get<OperatorTags>(sources))...,\n                  expanded_sources_args..., get<PrimalVars>(primal_vars)...,\n                  get<DerivVars>(deriv_vars)...,\n                  get<PrimalFluxesVars>(primal_fluxes)...);\n            },\n            sources_args);\n        if (massive) {\n          sources *= get(det_jacobian);\n        }\n        *operator_applied_to_vars += sources;\n      }\n    }\n    if (massive) {\n      ::dg::apply_mass_matrix(operator_applied_to_vars, mesh);\n    }\n\n    // Add boundary corrections\n    // Keeping track if any corrections were applied here, for an optimization\n    // below\n    bool has_any_boundary_corrections = false;\n    Variables<tmpl::list<transform::Tags::TransformedFirstIndex<\n        PrimalFluxesVars, Frame::ElementLogical>...>>\n        lifted_logical_aux_boundary_corrections{num_points, 0.};\n    for (auto& [mortar_id, mortar_data] : *all_mortar_data) {\n      const auto& direction = mortar_id.direction();\n      const auto& neighbor_id = mortar_id.id();\n      const bool is_internal =\n          (neighbor_id != ElementId<Dim>::external_boundary_id());\n      if (not directions_predicate(direction)) {\n        continue;\n      }\n      // When the data on both sides of the mortar is zero then we don't need to\n      // handle this mortar at all.\n      if (local_data_is_zero and\n          (not is_internal or data_is_zero(neighbor_id))) {\n        continue;\n      }\n      has_any_boundary_corrections = true;\n\n      const auto face_mesh = mesh.slice_away(direction.dimension());\n      auto [local_data, remote_data] = mortar_data.extract();\n      const size_t face_num_points = face_mesh.number_of_grid_points();\n      const auto& face_normal = face_normals.at(direction);\n      const auto& face_normal_vector = face_normal_vectors.at(direction);\n      const auto& fluxes_args_on_face = fluxes_args_on_faces.at(direction);\n      const auto& face_normal_magnitude = face_normal_magnitudes.at(direction);\n      const auto& face_jacobian = face_jacobians.at(direction);\n      const auto& face_jacobian_times_inv_jacobian =\n          face_jacobian_times_inv_jacobians.at(direction);\n      const auto& mortar_mesh =\n          is_internal ? all_mortar_meshes.at(mortar_id) : face_mesh;\n      const auto& mortar_size =\n          is_internal ? all_mortar_sizes.at(mortar_id) : full_mortar_size;\n\n      // This is the strong auxiliary boundary correction:\n      //   G^i = F^i(n_j (avg(u) - u))\n      // where\n      //   avg(u) - u = -0.5 * (u_int - u_ext)\n      auto avg_vars_on_mortar = Variables<tmpl::list<PrimalMortarVars...>>(\n          local_data.field_data\n              .template extract_subset<tmpl::list<PrimalMortarVars...>>());\n      const auto add_remote_contribution = [](auto& lhs, const auto& rhs) {\n        for (size_t i = 0; i < lhs.size(); ++i) {\n          lhs[i] -= rhs[i];\n        }\n      };\n      EXPAND_PACK_LEFT_TO_RIGHT(add_remote_contribution(\n          get<PrimalMortarVars>(avg_vars_on_mortar),\n          get<PrimalMortarVars>(remote_data.field_data)));\n      avg_vars_on_mortar *= -0.5;\n\n      // Project from the mortar back down to the face if needed\n      const auto avg_vars_on_face =\n          Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)\n              ? mass_conservative_restriction(\n                    std::move(avg_vars_on_mortar), mortar_mesh, mortar_size,\n                    mortar_jacobians.at(mortar_id), face_mesh, face_jacobian)\n              : std::move(avg_vars_on_mortar);\n\n      // Apply fluxes to get G^i\n      Variables<tmpl::list<PrimalFluxesVars...>> auxiliary_boundary_corrections{\n          face_num_points};\n      std::apply(\n          [&auxiliary_boundary_corrections, &face_normal, &face_normal_vector,\n           &avg_vars_on_face,\n           &element_id](const auto&... expanded_fluxes_args_on_face) {\n            if constexpr (FluxesComputer::is_discontinuous) {\n              FluxesComputer::apply(make_not_null(&get<PrimalFluxesVars>(\n                                        auxiliary_boundary_corrections))...,\n                                    expanded_fluxes_args_on_face..., element_id,\n                                    face_normal, face_normal_vector,\n                                    get<PrimalMortarVars>(avg_vars_on_face)...);\n            } else {\n              (void)element_id;\n              FluxesComputer::apply(make_not_null(&get<PrimalFluxesVars>(\n                                        auxiliary_boundary_corrections))...,\n                                    expanded_fluxes_args_on_face...,\n                                    face_normal, face_normal_vector,\n                                    get<PrimalMortarVars>(avg_vars_on_face)...);\n            }\n          },\n          fluxes_args_on_face);\n\n      // Lifting for the auxiliary boundary correction:\n      //   \\int_face G^i \\partial_i \\phi\n      // We first transform the flux index to the logical frame, apply the\n      // quadrature weights and the Jacobian for the face integral, then take\n      // the logical weak divergence in the volume after lifting (below the loop\n      // over faces).\n      auto logical_aux_boundary_corrections =\n          transform::first_index_to_different_frame(\n              auxiliary_boundary_corrections, face_jacobian_times_inv_jacobian);\n      ::dg::apply_mass_matrix(make_not_null(&logical_aux_boundary_corrections),\n                              face_mesh);\n      if (mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto) {\n        add_slice_to_data(\n            make_not_null(&lifted_logical_aux_boundary_corrections),\n            logical_aux_boundary_corrections, mesh.extents(),\n            direction.dimension(),\n            index_to_slice_at(mesh.extents(), direction));\n      } else {\n        ::dg::lift_boundary_terms_gauss_points(\n            make_not_null(&lifted_logical_aux_boundary_corrections),\n            logical_aux_boundary_corrections, mesh, direction);\n      }\n\n      // This is the strong primal boundary correction:\n      //   -n.H = -avg(n.F) + n.F + penalty * n.F(n_j jump(u))\n      // Note that the \"internal penalty\" numerical flux\n      // (as opposed to the LLF flux) uses the raw field derivatives without\n      // boundary corrections in the average, which is why we can communicate\n      // the data so early together with the auxiliary boundary data. In this\n      // case the penalty needs to include a factor N_points^2 / h (see the\n      // `penalty` function).\n      const auto& penalty_factor = penalty_factors.at(mortar_id);\n      // Compute jump on mortar:\n      //   penalty * jump(u) = penalty * (u_int - u_ext)\n      const auto add_remote_jump_contribution =\n          [&penalty_factor](auto& lhs, const auto& rhs) {\n            for (size_t i = 0; i < lhs.size(); ++i) {\n              lhs[i] -= rhs[i];\n              lhs[i] *= get(penalty_factor);\n            }\n          };\n      EXPAND_PACK_LEFT_TO_RIGHT(add_remote_jump_contribution(\n          get<PrimalMortarVars>(local_data.field_data),\n          get<PrimalMortarVars>(remote_data.field_data)));\n      // Compute average on mortar:\n      //   (strong)  -avg(n.F) + n.F = 0.5 * (n.F)_int + 0.5 * (n.F)_ext\n      //   (weak)    -avg(n.F) = -0.5 * (n.F)_int + 0.5 * (n.F)_ext\n      const auto add_avg_contribution = [](auto& lhs, const auto& rhs,\n                                           const double factor) {\n        for (size_t i = 0; i < lhs.size(); ++i) {\n          lhs[i] *= factor;\n          lhs[i] += 0.5 * rhs[i];\n        }\n      };\n      const bool is_strong_formulation =\n          formulation == ::dg::Formulation::StrongInertial or\n          formulation == ::dg::Formulation::StrongLogical;\n      EXPAND_PACK_LEFT_TO_RIGHT(add_avg_contribution(\n          get<::Tags::NormalDotFlux<PrimalMortarVars>>(local_data.field_data),\n          get<::Tags::NormalDotFlux<PrimalMortarVars>>(remote_data.field_data),\n          is_strong_formulation ? 0.5 : -0.5));\n\n      // Project from the mortar back down to the face if needed, lift and add\n      // to operator. See auxiliary boundary corrections above for details.\n      auto primal_boundary_corrections_on_face =\n          Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)\n              ? mass_conservative_restriction(\n                    std::move(local_data.field_data), mortar_mesh, mortar_size,\n                    mortar_jacobians.at(mortar_id), face_mesh, face_jacobian)\n              : std::move(local_data.field_data);\n\n      // Compute fluxes for jump term: n.F(n_j jump(u))\n      // If the fluxes are trivial (just the spatial metric), we can skip this\n      // step because the face normal is normalized.\n      if constexpr (not FluxesComputer::is_trivial) {\n        // We reuse the memory buffer from above for the result.\n        std::apply(\n            [&auxiliary_boundary_corrections, &face_normal, &face_normal_vector,\n             &primal_boundary_corrections_on_face,\n             &element_id](const auto&... expanded_fluxes_args_on_face) {\n              if constexpr (FluxesComputer::is_discontinuous) {\n                FluxesComputer::apply(\n                    make_not_null(&get<PrimalFluxesVars>(\n                        auxiliary_boundary_corrections))...,\n                    expanded_fluxes_args_on_face..., element_id, face_normal,\n                    face_normal_vector,\n                    get<PrimalMortarVars>(\n                        primal_boundary_corrections_on_face)...);\n              } else {\n                (void)element_id;\n                FluxesComputer::apply(\n                    make_not_null(&get<PrimalFluxesVars>(\n                        auxiliary_boundary_corrections))...,\n                    expanded_fluxes_args_on_face..., face_normal,\n                    face_normal_vector,\n                    get<PrimalMortarVars>(\n                        primal_boundary_corrections_on_face)...);\n              }\n            },\n            fluxes_args_on_face);\n        if constexpr (FluxesComputer::is_discontinuous) {\n          if (is_internal) {\n            // For penalty term with discontinuous fluxes: evaluate the fluxes\n            // on the other side of the boundary as well and take average\n            Variables<tmpl::list<PrimalFluxesVars...>> fluxes_other_side{\n                face_num_points};\n            std::apply(\n                [&fluxes_other_side, &face_normal, &face_normal_vector,\n                 &primal_boundary_corrections_on_face,\n                 &local_neighbor_id =\n                     neighbor_id](const auto&... expanded_fluxes_args_on_face) {\n                  FluxesComputer::apply(\n                      make_not_null(\n                          &get<PrimalFluxesVars>(fluxes_other_side))...,\n                      expanded_fluxes_args_on_face..., local_neighbor_id,\n                      face_normal, face_normal_vector,\n                      get<PrimalMortarVars>(\n                          primal_boundary_corrections_on_face)...);\n                },\n                fluxes_args_on_face);\n            auxiliary_boundary_corrections += fluxes_other_side;\n            auxiliary_boundary_corrections *= 0.5;\n          }\n        }\n        EXPAND_PACK_LEFT_TO_RIGHT(normal_dot_flux(\n            make_not_null(\n                &get<PrimalMortarVars>(primal_boundary_corrections_on_face)),\n            face_normal,\n            get<PrimalFluxesVars>(auxiliary_boundary_corrections)));\n      }\n\n      // Add penalty term to average term\n      Variables<tmpl::list<PrimalMortarVars...>> primal_boundary_corrections{};\n      // First half of the memory allocated above is filled with the penalty\n      // term, so just use that memory here.\n      primal_boundary_corrections.set_data_ref(\n          primal_boundary_corrections_on_face.data(), avg_vars_on_face.size());\n      // Second half of the memory is filled with the average term. Add that to\n      // the penalty term.\n      const auto add_avg_term = [](auto& lhs, const auto& rhs) {\n        for (size_t i = 0; i < lhs.size(); ++i) {\n          lhs[i] += rhs[i];\n        }\n      };\n      EXPAND_PACK_LEFT_TO_RIGHT(\n          add_avg_term(get<PrimalMortarVars>(primal_boundary_corrections),\n                       get<::Tags::NormalDotFlux<PrimalMortarVars>>(\n                           primal_boundary_corrections_on_face)));\n\n      // Lifting for the primal boundary correction:\n      //   \\int_face n.H \\phi\n      if (massive) {\n        // We apply the quadrature weights and Jacobian for the face integral,\n        // then lift to the volume\n        primal_boundary_corrections *= get(face_jacobian);\n        ::dg::apply_mass_matrix(make_not_null(&primal_boundary_corrections),\n                                face_mesh);\n        if (mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto) {\n          add_slice_to_data(operator_applied_to_vars,\n                            primal_boundary_corrections, mesh.extents(),\n                            direction.dimension(),\n                            index_to_slice_at(mesh.extents(), direction));\n        } else {\n          ::dg::lift_boundary_terms_gauss_points(operator_applied_to_vars,\n                                                 primal_boundary_corrections,\n                                                 mesh, direction);\n        }\n      } else {\n        // Apply an extra inverse mass matrix to the boundary corrections (with\n        // mass lumping, so it's diagonal).\n        // For Gauss-Lobatto grids this divides out the quadrature weights and\n        // Jacobian on the face, leaving only factors perpendicular to the face.\n        // Those are handled by `dg::lift_flux` (with an extra minus sign since\n        // the function was written for evolution systems).\n        // For Gauss grids the quadrature weights and Jacobians are handled\n        // by `::dg::lift_boundary_terms_gauss_points` (which was also written\n        // for evolution systems, hence the extra minus sign).\n        primal_boundary_corrections *= -1.;\n        if (mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto) {\n          ::dg::lift_flux(make_not_null(&primal_boundary_corrections),\n                          mesh.extents(direction.dimension()),\n                          face_normal_magnitude);\n          add_slice_to_data(operator_applied_to_vars,\n                            primal_boundary_corrections, mesh.extents(),\n                            direction.dimension(),\n                            index_to_slice_at(mesh.extents(), direction));\n        } else {\n          // We already have the `face_jacobian = det(J) * magnitude(n)` here,\n          // so just pass a constant 1 for `magnitude(n)`. This could be\n          // optimized to avoid allocating the vector of ones.\n          ::dg::lift_boundary_terms_gauss_points(\n              operator_applied_to_vars, det_inv_jacobian, mesh, direction,\n              primal_boundary_corrections,\n              Scalar<DataVector>{face_mesh.number_of_grid_points(), 1.},\n              face_jacobian);\n        }\n      }\n    }  // loop over all mortars\n\n    if (not has_any_boundary_corrections) {\n      // No need to handle auxiliary boundary corrections; return early\n      return;\n    }\n\n    // Apply weak divergence to lifted auxiliary boundary corrections and add to\n    // operator\n    if (massive) {\n      logical_weak_divergence(operator_applied_to_vars,\n                              lifted_logical_aux_boundary_corrections, mesh,\n                              true);\n    } else {\n      // Possible optimization: eliminate this allocation by building the\n      // inverse mass matrix into `logical_weak_divergence`\n      Variables<tmpl::list<OperatorTags...>> massless_aux_boundary_corrections{\n          num_points};\n      logical_weak_divergence(make_not_null(&massless_aux_boundary_corrections),\n                              lifted_logical_aux_boundary_corrections, mesh);\n      massless_aux_boundary_corrections *= get(det_inv_jacobian);\n      ::dg::apply_inverse_mass_matrix(\n          make_not_null(&massless_aux_boundary_corrections), mesh);\n      *operator_applied_to_vars += massless_aux_boundary_corrections;\n    }\n  }\n\n  template <typename... FixedSourcesTags, typename ApplyBoundaryCondition,\n            typename... FluxesArgs, typename... SourcesArgs,\n            typename... ModifyBoundaryDataArgs>\n  static void impose_inhomogeneous_boundary_conditions_on_source(\n      const gsl::not_null<Variables<tmpl::list<FixedSourcesTags...>>*>\n          fixed_sources,\n      const Element<Dim>& element, const Mesh<Dim>& mesh,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const Scalar<DataVector>& det_inv_jacobian,\n      const Scalar<DataVector>& det_jacobian,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& det_times_inv_jacobian,\n      const DirectionMap<Dim, tnsr::i<DataVector, Dim>>& face_normals,\n      const DirectionMap<Dim, tnsr::I<DataVector, Dim>>& face_normal_vectors,\n      const DirectionMap<Dim, Scalar<DataVector>>& face_normal_magnitudes,\n      const DirectionMap<Dim, Scalar<DataVector>>& face_jacobians,\n      const DirectionMap<Dim,\n                         InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                         Frame::Inertial>>&\n          face_jacobian_times_inv_jacobians,\n      const ::dg::MortarMap<Dim, Mesh<Dim - 1>>& all_mortar_meshes,\n      const ::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>& all_mortar_sizes,\n      const ::dg::MortarMap<Dim, Scalar<DataVector>>& mortar_jacobians,\n      const ::dg::MortarMap<Dim, Scalar<DataVector>>& penalty_factors,\n      const bool massive, const ::dg::Formulation formulation,\n      const ApplyBoundaryCondition& apply_boundary_condition,\n      const std::tuple<FluxesArgs...>& fluxes_args,\n      const std::tuple<SourcesArgs...>& sources_args,\n      const DirectionMap<Dim, std::tuple<FluxesArgs...>>& fluxes_args_on_faces,\n      const std::tuple<ModifyBoundaryDataArgs...>& modify_boundary_data_args)\n      // This function adds nothing to the fixed sources if the operator is\n      // linearized, so it shouldn't be used in that case\n    requires(not Linearized)\n  {\n    // We just feed zero variables through the nonlinear operator to extract the\n    // constant contribution at external boundaries. Since the variables are\n    // zero the operator simplifies quite a lot. The simplification is probably\n    // not very important for performance because this function will only be\n    // called when solving a linear elliptic system and only once during\n    // initialization, but we specialize the operator for zero data nonetheless\n    // just so we can ignore internal boundaries. Only when the system modifies\n    // boundary data do we need to handle internal boundaries (see below),\n    // otherwise we can skip them.\n    const size_t num_points = mesh.number_of_grid_points();\n    const Variables<tmpl::list<PrimalFields...>> zero_primal_vars{num_points,\n                                                                  0.};\n    Variables<tmpl::list<PrimalFluxes...>> primal_fluxes_buffer{num_points};\n    Variables<tmpl::list<\n        ::Tags::deriv<PrimalFields, tmpl::size_t<Dim>, Frame::Inertial>...>>\n        zero_deriv_vars{num_points, 0.};\n    Variables<tmpl::list<FixedSourcesTags...>> operator_applied_to_zero_vars{\n        num_points};\n    // Set up data on mortars\n    ::dg::MortarMap<Dim, MortarData<size_t, tmpl::list<PrimalFields...>,\n                                    tmpl::list<PrimalFluxes...>>>\n        all_mortar_data{};\n    constexpr size_t temporal_id = std::numeric_limits<size_t>::max();\n    // Apply the operator to the zero variables, skipping internal boundaries\n    prepare_mortar_data<true>(\n        make_not_null(&zero_deriv_vars), make_not_null(&primal_fluxes_buffer),\n        make_not_null(&all_mortar_data), zero_primal_vars, element, mesh,\n        inv_jacobian, face_normals, all_mortar_meshes, all_mortar_sizes,\n        temporal_id, apply_boundary_condition, fluxes_args);\n    // Modify internal boundary data if needed, e.g. to transform from one\n    // variable to another when crossing element boundaries. This is a nonlinear\n    // operation in the sense that feeding zero through the operator is nonzero,\n    // so we evaluate it here and add the contribution to the fixed sources,\n    // just like inhomogeneous external boundary conditions.\n    if constexpr (not std::is_same_v<typename System::modify_boundary_data,\n                                     void>) {\n      for (const auto& [direction, neighbors] : element.neighbors()) {\n        for (const auto& neighbor_id : neighbors) {\n          const ::dg::MortarId<Dim> mortar_id{direction, neighbor_id};\n          ASSERT(not all_mortar_data.contains(mortar_id),\n                 \"Mortar data with ID \" << mortar_id << \" already exists.\");\n          auto& mortar_data = all_mortar_data[mortar_id];\n          // Manufacture zero mortar data, store as local data, then treat as\n          // received from neighbor and apply modifications\n          BoundaryData<tmpl::list<PrimalFields...>, tmpl::list<PrimalFluxes...>>\n              remote_boundary_data_on_mortar{};\n          remote_boundary_data_on_mortar.field_data.initialize(\n              all_mortar_meshes.at(mortar_id).number_of_grid_points(), 0.);\n          mortar_data.local_insert(temporal_id, remote_boundary_data_on_mortar);\n          // Modify \"received\" mortar data\n          std::apply(\n              [&remote_boundary_data_on_mortar,\n               &mortar_id](const auto&... args) {\n                System::modify_boundary_data::apply(\n                    make_not_null(&get<PrimalFields>(\n                        remote_boundary_data_on_mortar.field_data))...,\n                    make_not_null(&get<::Tags::NormalDotFlux<PrimalFields>>(\n                        remote_boundary_data_on_mortar.field_data))...,\n                    mortar_id, args...);\n              },\n              modify_boundary_data_args);\n          // Insert as remote mortar data\n          mortar_data.remote_insert(temporal_id,\n                                    std::move(remote_boundary_data_on_mortar));\n        }\n      }\n    }\n    apply_operator(\n        make_not_null(&operator_applied_to_zero_vars),\n        make_not_null(&all_mortar_data), zero_primal_vars, zero_deriv_vars,\n        primal_fluxes_buffer, element, mesh, inv_jacobian, det_inv_jacobian,\n        det_jacobian, det_times_inv_jacobian, face_normals, face_normal_vectors,\n        face_normal_magnitudes, face_jacobians,\n        face_jacobian_times_inv_jacobians, all_mortar_meshes, all_mortar_sizes,\n        mortar_jacobians, penalty_factors, massive, formulation, temporal_id,\n        fluxes_args_on_faces, sources_args);\n    // Impose the nonlinear (constant) boundary contribution as fixed sources on\n    // the RHS of the equations\n    *fixed_sources -= operator_applied_to_zero_vars;\n  }\n};\n\n}  // namespace detail\n\n/*!\n * \\brief Prepare data on mortars so they can be communicated to neighbors\n *\n * Call this function on all elements and communicate the mortar data, then call\n * `elliptic::dg::apply_operator`.\n */\ntemplate <typename System, bool Linearized, typename... Args>\nvoid prepare_mortar_data(Args&&... args) {\n  detail::DgOperatorImpl<System, Linearized>::template prepare_mortar_data<\n      false>(std::forward<Args>(args)...);\n}\n\n/*!\n * \\brief Apply the elliptic DG operator\n *\n * This function applies the elliptic DG operator on an element, assuming all\n * data on mortars is already available. Use the\n * `elliptic::dg::prepare_mortar_data` function to prepare mortar data on\n * neighboring elements, then communicate the data and insert them on the\n * \"remote\" side of the mortars before calling this function.\n */\ntemplate <typename System, bool Linearized, typename... Args>\nvoid apply_operator(Args&&... args) {\n  detail::DgOperatorImpl<System, Linearized>::apply_operator(\n      std::forward<Args>(args)...);\n}\n\n/*!\n * \\brief For linear systems, impose inhomogeneous boundary conditions as\n * contributions to the fixed sources (i.e. the RHS of the equations).\n *\n * This function exists because the DG operator must typically be linear, but\n * even for linear elliptic equations we typically apply boundary conditions\n * with a constant, and therefore nonlinear, contribution. Standard examples are\n * inhomogeneous (i.e. non-zero) Dirichlet or Neumann boundary conditions. This\n * nonlinear contribution can be added to the fixed sources, leaving only the\n * linearized boundary conditions in the DG operator. For standard constant\n * Dirichlet or Neumann boundary conditions the linearization is of course just\n * zero.\n *\n * This function essentially feeds zero variables through the nonlinear operator\n * and subtracts the result from the fixed sources: `b -= A(x=0)`.\n */\ntemplate <typename System, typename... Args>\nvoid impose_inhomogeneous_boundary_conditions_on_source(Args&&... args) {\n  detail::DgOperatorImpl<System, false>::\n      impose_inhomogeneous_boundary_conditions_on_source(\n          std::forward<Args>(args)...);\n}\n\n}  // namespace elliptic::dg\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/Initialization.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/DiscontinuousGalerkin/Initialization.hpp\"\n\n#include <array>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace elliptic::dg {\n\nnamespace detail{\ntemplate <size_t Dim>\nvoid initialize_coords_and_jacobians(\n    gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>\n        logical_coords,\n    gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> inertial_coords,\n    gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>*>\n        inv_jacobian,\n    gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,\n    gsl::not_null<Scalar<DataVector>*> det_jacobian,\n    gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>*>\n        det_times_inv_jacobian,\n        const Mesh<Dim>& mesh,\n    const ElementMap<Dim, Frame::Inertial>& element_map,\n    const domain::FunctionsOfTimeMap& functions_of_time) {\n  // Coordinates\n  *logical_coords = logical_coordinates(mesh);\n  *inertial_coords =\n      element_map(*logical_coords, 0., functions_of_time);\n  // Jacobian\n  // Note: we can try to use `::dg::metric_identity_jacobian_quantities` here.\n  // When I tried (NV, Dec 2023) the DG residuals diverged on a sphere domain\n  // with a large outer boundary (1e9). This was possibly because no\n  // metric-identity Jacobians were used on faces, though I also tried slicing\n  // the metric-identity Jacobian to the faces and that didn't help.\n  *inv_jacobian =\n      element_map.inv_jacobian(*logical_coords, 0., functions_of_time);\n  *det_inv_jacobian = determinant(*inv_jacobian);\n  get(*det_jacobian) = 1. / get(*det_inv_jacobian);\n  *det_times_inv_jacobian = *inv_jacobian;\n  for (auto& component : *det_times_inv_jacobian) {\n    component *= get(*det_jacobian);\n  }\n    }\n} // namespace detail\n\ntemplate <size_t Dim>\nvoid InitializeGeometry<Dim>::apply(\n    const gsl::not_null<Mesh<Dim>*> mesh,\n    const gsl::not_null<Element<Dim>*> element,\n    const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*> neighbor_meshes,\n    const gsl::not_null<ElementMap<Dim, Frame::Inertial>*> element_map,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>\n        logical_coords,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        inertial_coords,\n    const gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                        Frame::Inertial>*>\n        inv_jacobian,\n    const gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,\n    const gsl::not_null<Scalar<DataVector>*> det_jacobian,\n    const gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                        Frame::Inertial>*>\n        det_times_inv_jacobian,\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const std::vector<std::array<size_t, Dim>>& initial_refinement,\n    const Domain<Dim>& domain,\n    const domain::FunctionsOfTimeMap& functions_of_time,\n    const Spectral::Quadrature i1_quadrature,\n    const ElementId<Dim>& element_id) {\n  // Element\n  *element = domain::create_initial_element(element_id, domain.blocks(),\n                                            initial_refinement);\n  // Mesh\n  const Spectral::Basis i1_basis{Spectral::Basis::Legendre};\n  ASSERT(i1_quadrature == Spectral::Quadrature::GaussLobatto or\n             i1_quadrature == Spectral::Quadrature::Gauss,\n         \"The elliptic DG scheme supports Gauss and Gauss-Lobatto \"\n         \"grids, but the chosen quadrature is: \"\n             << i1_quadrature);\n  *mesh = domain::create_initial_mesh(initial_extents, *element, i1_basis,\n                                      i1_quadrature);\n  // Neighbor meshes\n  for (const auto& [direction, neighbors] : element->neighbors()) {\n    for (const auto& neighbor_id : neighbors) {\n      const auto& neighbor_block = domain.blocks()[neighbor_id.block_id()];\n      const auto& orientation = neighbors.orientation(neighbor_id);\n      neighbor_meshes->emplace(\n          DirectionalId<Dim>{direction, neighbor_id},\n          orientation.inverse_map()(domain::create_initial_mesh(\n              initial_extents, neighbor_block, neighbor_id, i1_basis,\n              i1_quadrature)));\n    }\n  }\n  // Element map\n  const auto& block = domain.blocks()[element_id.block_id()];\n  *element_map = ElementMap<Dim, Frame::Inertial>{element_id, block};\n  // Coordinates and Jacobians\n  detail::initialize_coords_and_jacobians(\n      logical_coords, inertial_coords, inv_jacobian, det_inv_jacobian,\n      det_jacobian, det_times_inv_jacobian, *mesh, *element_map,\n      functions_of_time);\n}\n\nnamespace detail {\ntemplate <size_t Dim>\nvoid deriv_unnormalized_face_normals_impl(\n    const gsl::not_null<DirectionMap<Dim, tnsr::ij<DataVector, Dim>>*>\n        deriv_unnormalized_face_normals,\n    const Mesh<Dim>& mesh, const Element<Dim>& element,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian) {\n  if (element.external_boundaries().empty()) {\n    return;\n  }\n  // If the accuracy of this derivative is insufficient we could also compute\n  // it on a higher-order grid and then project it down.\n  // On Gauss grids we could compute the derivative on a Gauss-Lobatto grid and\n  // slice it.\n  const auto deriv_inv_jac =\n      partial_derivative(inv_jacobian, mesh, inv_jacobian);\n  for (const auto& direction : element.external_boundaries()) {\n    const auto deriv_inv_jac_on_face =\n        ::dg::project_tensor_to_boundary(deriv_inv_jac, mesh, direction);\n    auto& deriv_unnormalized_face_normal =\n        (*deriv_unnormalized_face_normals)[direction];\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        deriv_unnormalized_face_normal.get(i, j) =\n            direction.sign() *\n            deriv_inv_jac_on_face.get(i, direction.dimension(), j);\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim, Frame::ElementLogical> mortar_logical_coordinates(\n    const Mesh<Dim - 1>& mortar_mesh,\n    const ::dg::MortarSize<Dim - 1>& mortar_size,\n    const Direction<Dim>& direction) {\n  auto mortar_logical_coords =\n      interface_logical_coordinates(mortar_mesh, direction);\n  size_t d_m = 0;\n  for (size_t d = 0; d < Dim; ++d) {\n    if (d == direction.dimension()) {\n      continue;\n    }\n    if (mortar_size.at(d_m) == Spectral::SegmentSize::LowerHalf) {\n      mortar_logical_coords.get(d) -= 1.;\n      mortar_logical_coords.get(d) *= 0.5;\n    } else if (mortar_size.at(d_m) == Spectral::SegmentSize::UpperHalf) {\n      mortar_logical_coords.get(d) += 1.;\n      mortar_logical_coords.get(d) *= 0.5;\n    }\n    ++d_m;\n  }\n  return mortar_logical_coords;\n}\n\ntemplate <size_t Dim>\nvoid mortar_jacobian(\n    const gsl::not_null<Scalar<DataVector>*> mortar_jacobian,\n    const gsl::not_null<Scalar<DataVector>*> perpendicular_element_size,\n    const Mesh<Dim - 1>& mortar_mesh,\n    const ::dg::MortarSize<Dim - 1>& mortar_size,\n    const Direction<Dim>& direction,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n        mortar_logical_coords,\n    const std::optional<tnsr::II<DataVector, Dim>>& inv_metric_on_mortar,\n    const ElementMap<Dim, Frame::Inertial>& element_map,\n    const domain::FunctionsOfTimeMap& functions_of_time) {\n  determinant(mortar_jacobian, element_map.jacobian(mortar_logical_coords, 0.,\n                                                    functions_of_time));\n  // These factors of two account for the mortar size\n  for (const auto& mortar_size_i : mortar_size) {\n    if (mortar_size_i != Spectral::SegmentSize::Full) {\n      get(*mortar_jacobian) *= 0.5;\n    }\n  }\n  const auto inv_jacobian_on_mortar =\n      element_map.inv_jacobian(mortar_logical_coords, 0., functions_of_time);\n  const auto unnormalized_mortar_normal =\n      unnormalized_face_normal(mortar_mesh, inv_jacobian_on_mortar, direction);\n  Scalar<DataVector> mortar_normal_magnitude{};\n  if (inv_metric_on_mortar.has_value()) {\n    magnitude(make_not_null(&mortar_normal_magnitude),\n              unnormalized_mortar_normal, inv_metric_on_mortar.value());\n  } else {\n    magnitude(make_not_null(&mortar_normal_magnitude),\n              unnormalized_mortar_normal);\n  }\n  get(*mortar_jacobian) *= get(mortar_normal_magnitude);\n  get(*perpendicular_element_size) = 2. / get(mortar_normal_magnitude);\n}\n}  // namespace detail\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template class InitializeGeometry<DIM(data)>;                                \\\n  template void detail::deriv_unnormalized_face_normals_impl(                  \\\n      gsl::not_null<DirectionMap<DIM(data), tnsr::ij<DataVector, DIM(data)>>*> \\\n          deriv_unnormalized_face_normals,                                     \\\n      const Mesh<DIM(data)>& mesh, const Element<DIM(data)>& element,          \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            Frame::Inertial>& inv_jacobian);                   \\\n  template tnsr::I<DataVector, DIM(data), Frame::ElementLogical>               \\\n  detail::mortar_logical_coordinates(                                          \\\n      const Mesh<DIM(data) - 1>& mortar_mesh,                                  \\\n      const ::dg::MortarSize<DIM(data) - 1>& mortar_size,                      \\\n      const Direction<DIM(data)>& direction);                                  \\\n  template void detail::mortar_jacobian(                                       \\\n      gsl::not_null<Scalar<DataVector>*> mortar_jacobian,                      \\\n      gsl::not_null<Scalar<DataVector>*> perpendicular_element_size,           \\\n      const Mesh<DIM(data) - 1>& mortar_mesh,                                  \\\n      const ::dg::MortarSize<DIM(data) - 1>& mortar_size,                      \\\n      const Direction<DIM(data)>& direction,                                   \\\n      const tnsr::I<DataVector, DIM(data), Frame::ElementLogical>&             \\\n          mortar_logical_coords,                                               \\\n      const std::optional<tnsr::II<DataVector, DIM(data)>>&                    \\\n          inv_metric_on_mortar,                                                \\\n      const ElementMap<DIM(data), Frame::Inertial>& element_map,               \\\n      const domain::FunctionsOfTimeMap& functions_of_time);                    \\\n  template void detail::initialize_coords_and_jacobians(                       \\\n      gsl::not_null<tnsr::I<DataVector, DIM(data), Frame::ElementLogical>*>    \\\n          logical_coords,                                                      \\\n      gsl::not_null<tnsr::I<DataVector, DIM(data), Frame::Inertial>*>          \\\n          inertial_coords,                                                     \\\n      gsl::not_null<InverseJacobian<DataVector, DIM(data),                     \\\n                                    Frame::ElementLogical, Frame::Inertial>*>  \\\n          inv_jacobian,                                                        \\\n      gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,                     \\\n      gsl::not_null<Scalar<DataVector>*> det_jacobian,                         \\\n      gsl::not_null<InverseJacobian<DataVector, DIM(data),                     \\\n                                    Frame::ElementLogical, Frame::Inertial>*>  \\\n          det_times_inv_jacobian,                                              \\\n      const Mesh<DIM(data)>& mesh,                                             \\\n      const ElementMap<DIM(data), Frame::Inertial>& element_map,               \\\n      const domain::FunctionsOfTimeMap& functions_of_time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace elliptic::dg\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/Initialization.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Domain/Tags/SurfaceJacobian.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Penalty.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::dg {\n\nnamespace detail {\ntemplate <size_t Dim>\nvoid initialize_coords_and_jacobians(\n    gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>\n        logical_coords,\n    gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> inertial_coords,\n    gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>*>\n        inv_jacobian,\n    gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,\n    gsl::not_null<Scalar<DataVector>*> det_jacobian,\n    gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>*>\n        det_times_inv_jacobian,\n    const Mesh<Dim>& mesh, const ElementMap<Dim, Frame::Inertial>& element_map,\n    const domain::FunctionsOfTimeMap& functions_of_time);\n}  // namespace detail\n\n/*!\n * \\brief Initialize the background-independent geometry for the elliptic DG\n * operator\n *\n * ## Geometric aliasing\n *\n * The geometric quantities such as Jacobians are evaluated on the DG grid.\n * Since we know them analytically, we could also evaluate them on a\n * higher-order grid or with a stronger quadrature (Gauss instead of\n * Gauss-Lobatto) to combat geometric aliasing. See discussions in\n * \\cite Vincent2019qpd and \\cite Fischer2021voj .\n */\ntemplate <size_t Dim>\nstruct InitializeGeometry {\n  using return_tags = tmpl::list<\n      domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n      domain::Tags::NeighborMesh<Dim>, domain::Tags::ElementMap<Dim>,\n      domain::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      domain::Tags::Coordinates<Dim, Frame::Inertial>,\n      domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,\n                                        Frame::Inertial>>;\n  using argument_tags =\n      tmpl::list<domain::Tags::InitialExtents<Dim>,\n                 domain::Tags::InitialRefinementLevels<Dim>,\n                 domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime,\n                 elliptic::dg::Tags::Quadrature>;\n  using volume_tags = argument_tags;\n  static void apply(\n      gsl::not_null<Mesh<Dim>*> mesh, gsl::not_null<Element<Dim>*> element,\n      gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*> neighbor_meshes,\n      gsl::not_null<ElementMap<Dim, Frame::Inertial>*> element_map,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>\n          logical_coords,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> inertial_coords,\n      gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                    Frame::Inertial>*>\n          inv_jacobian,\n      gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,\n      gsl::not_null<Scalar<DataVector>*> det_jacobian,\n      gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                    Frame::Inertial>*>\n          det_times_inv_jacobian,\n      const std::vector<std::array<size_t, Dim>>& initial_extents,\n      const std::vector<std::array<size_t, Dim>>& initial_refinement,\n      const Domain<Dim>& domain,\n      const domain::FunctionsOfTimeMap& functions_of_time,\n      Spectral::Quadrature i1_quadrature, const ElementId<Dim>& element_id);\n};\n\ntemplate <size_t Dim>\nstruct ProjectGeometry : tt::ConformsTo<::amr::protocols::Projector> {\n  using return_tags = tmpl::list<\n      domain::Tags::ElementMap<Dim>,\n      domain::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      domain::Tags::Coordinates<Dim, Frame::Inertial>,\n      domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,\n                                        Frame::Inertial>>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                 domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime>;\n  using volume_tags =\n      tmpl::list<domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime>;\n\n  // p-refinement\n  static void apply(\n      const gsl::not_null<ElementMap<Dim, Frame::Inertial>*> element_map,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>\n          logical_coords,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          inertial_coords,\n      const gsl::not_null<InverseJacobian<\n          DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>\n          inv_jacobian,\n      const gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,\n      const gsl::not_null<Scalar<DataVector>*> det_jacobian,\n      const gsl::not_null<InverseJacobian<\n          DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>\n          det_times_inv_jacobian,\n      const Mesh<Dim>& mesh, const Element<Dim>& /*element*/,\n      const Domain<Dim>& /*domain*/,\n      const domain::FunctionsOfTimeMap& functions_of_time,\n      const std::pair<Mesh<Dim>, Element<Dim>>& old_mesh_and_element) {\n    if (mesh == old_mesh_and_element.first) {\n      // Neighbors may have changed, but this element hasn't. Nothing to do.\n      return;\n    }\n    // The element map doesn't change with p-refinement\n    detail::initialize_coords_and_jacobians(\n        logical_coords, inertial_coords, inv_jacobian, det_inv_jacobian,\n        det_jacobian, det_times_inv_jacobian, mesh, *element_map,\n        functions_of_time);\n  }\n\n  // h-refinement\n  template <typename... ParentOrChildrenItemsType>\n  static void apply(\n      const gsl::not_null<ElementMap<Dim, Frame::Inertial>*> element_map,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>\n          logical_coords,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          inertial_coords,\n      const gsl::not_null<InverseJacobian<\n          DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>\n          inv_jacobian,\n      const gsl::not_null<Scalar<DataVector>*> det_inv_jacobian,\n      const gsl::not_null<Scalar<DataVector>*> det_jacobian,\n      const gsl::not_null<InverseJacobian<\n          DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>\n          det_times_inv_jacobian,\n      const Mesh<Dim>& mesh, const Element<Dim>& element,\n      const Domain<Dim>& domain,\n      const domain::FunctionsOfTimeMap& functions_of_time,\n      const ParentOrChildrenItemsType&... /*parent_or_children_items*/) {\n    const auto& element_id = element.id();\n    const auto& block = domain.blocks()[element_id.block_id()];\n    *element_map = ElementMap<Dim, Frame::Inertial>{element_id, block};\n    detail::initialize_coords_and_jacobians(\n        logical_coords, inertial_coords, inv_jacobian, det_inv_jacobian,\n        det_jacobian, det_times_inv_jacobian, mesh, *element_map,\n        functions_of_time);\n  }\n};\n\nnamespace detail {\n// Compute derivative of the Jacobian numerically\ntemplate <size_t Dim>\nvoid deriv_unnormalized_face_normals_impl(\n    gsl::not_null<DirectionMap<Dim, tnsr::ij<DataVector, Dim>>*>\n        deriv_unnormalized_face_normals,\n    const Mesh<Dim>& mesh, const Element<Dim>& element,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian);\n\n// Get element-logical coordinates of the mortar collocation points\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim, Frame::ElementLogical> mortar_logical_coordinates(\n    const Mesh<Dim - 1>& mortar_mesh,\n    const ::dg::MortarSize<Dim - 1>& mortar_size,\n    const Direction<Dim>& direction);\n\ntemplate <size_t Dim>\nvoid mortar_jacobian(\n    gsl::not_null<Scalar<DataVector>*> mortar_jacobian,\n    gsl::not_null<Scalar<DataVector>*> perpendicular_element_size,\n    const Mesh<Dim - 1>& mortar_mesh,\n    const ::dg::MortarSize<Dim - 1>& mortar_size,\n    const Direction<Dim>& direction,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n        mortar_logical_coords,\n    const std::optional<tnsr::II<DataVector, Dim>>& inv_metric_on_mortar,\n    const ElementMap<Dim, Frame::Inertial>& element_map,\n    const domain::FunctionsOfTimeMap& functions_of_time);\n}  // namespace detail\n\n/// Initialize the geometry on faces and mortars for the elliptic DG operator\n///\n/// To normalize face normals this function needs the inverse background metric.\n/// Pass the tag representing the inverse background metric to the\n/// `InvMetricTag` template parameter, and the tag representing the analytic\n/// background from which it can be retrieved to the `BackgroundTag` template\n/// parameter. Set `InvMetricTag` and `BackgroundTag` to `void` to normalize\n/// face normals with the Euclidean magnitude.\n///\n/// Mortar Jacobians are added only on nonconforming internal element\n/// boundaries, i.e., when `Spectral::needs_projection()` is true.\n///\n/// The `::Tags::deriv<domain::Tags::UnnormalizedFaceNormal<Dim>>` is only added\n/// on external boundaries, for use by boundary conditions.\ntemplate <size_t Dim, typename InvMetricTag, typename BackgroundTag>\nstruct InitializeFacesAndMortars : tt::ConformsTo<::amr::protocols::Projector> {\n  using return_tags = tmpl::append<\n      domain::make_faces_tags<\n          Dim,\n          tmpl::list<domain::Tags::Direction<Dim>,\n                     domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                     domain::Tags::FaceNormal<Dim>,\n                     domain::Tags::FaceNormalVector<Dim>,\n                     domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>,\n                     domain::Tags::DetSurfaceJacobian<Frame::ElementLogical,\n                                                      Frame::Inertial>,\n                     // This is the volume inverse Jacobian on the face grid\n                     // points, multiplied by the determinant of the _face_\n                     // Jacobian (the tag above)\n                     domain::Tags::DetTimesInvJacobian<\n                         Dim, Frame::ElementLogical, Frame::Inertial>,\n                     // Possible optimization: The derivative of the face normal\n                     // could be omitted for some systems, but its memory usage\n                     // is probably insignificant since it's only added on\n                     // external boundaries.\n                     ::Tags::deriv<domain::Tags::UnnormalizedFaceNormal<Dim>,\n                                   tmpl::size_t<Dim>, Frame::Inertial>>>,\n      tmpl::list<\n          ::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>,\n          ::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>,\n          ::Tags::Mortars<domain::Tags::Coordinates<Dim, Frame::Inertial>, Dim>,\n          ::Tags::Mortars<domain::Tags::DetSurfaceJacobian<\n                              Frame::ElementLogical, Frame::Inertial>,\n                          Dim>,\n          ::Tags::Mortars<elliptic::dg::Tags::PenaltyFactor, Dim>>>;\n  using argument_tags = tmpl::append<\n      tmpl::list<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                 domain::Tags::NeighborMesh<Dim>, domain::Tags::ElementMap<Dim>,\n                 domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>,\n                 domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime,\n                 elliptic::dg::Tags::PenaltyParameter>,\n      tmpl::conditional_t<\n          std::is_same_v<BackgroundTag, void>, tmpl::list<>,\n          tmpl::list<BackgroundTag, Parallel::Tags::Metavariables>>>;\n  using volume_tags = tmpl::append<\n      tmpl::list<domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTime,\n                 elliptic::dg::Tags::PenaltyParameter>,\n      tmpl::conditional_t<\n          std::is_same_v<BackgroundTag, void>, tmpl::list<>,\n          tmpl::list<BackgroundTag, Parallel::Tags::Metavariables>>>;\n  template <typename... AmrData>\n  static void apply(\n      const gsl::not_null<DirectionMap<Dim, Direction<Dim>>*> face_directions,\n      const gsl::not_null<DirectionMap<Dim, tnsr::I<DataVector, Dim>>*>\n          faces_inertial_coords,\n      const gsl::not_null<DirectionMap<Dim, tnsr::i<DataVector, Dim>>*>\n          face_normals,\n      const gsl::not_null<DirectionMap<Dim, tnsr::I<DataVector, Dim>>*>\n          face_normal_vectors,\n      const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>\n          face_normal_magnitudes,\n      const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>\n          face_jacobians,\n      const gsl::not_null<DirectionMap<\n          Dim, InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                               Frame::Inertial>>*>\n          face_jacobian_times_inv_jacobian,\n      const gsl::not_null<DirectionMap<Dim, tnsr::ij<DataVector, Dim>>*>\n          deriv_unnormalized_face_normals,\n      const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_meshes,\n      const gsl::not_null<::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>*>\n          mortar_sizes,\n      const gsl::not_null<::dg::MortarMap<Dim, tnsr::I<DataVector, Dim>>*>\n          all_mortar_inertial_coords,\n      const gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>\n          mortar_jacobians,\n      const gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>\n          penalty_factors,\n      const Mesh<Dim>& mesh, const Element<Dim>& element,\n      const DirectionalIdMap<Dim, Mesh<Dim>>& neighbor_meshes,\n      const ElementMap<Dim, Frame::Inertial>& element_map,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const Domain<Dim>& domain,\n      const domain::FunctionsOfTimeMap& functions_of_time,\n      const double penalty_parameter, const AmrData&... amr_data) {\n    apply(face_directions, faces_inertial_coords, face_normals,\n          face_normal_vectors, face_normal_magnitudes, face_jacobians,\n          face_jacobian_times_inv_jacobian, deriv_unnormalized_face_normals,\n          mortar_meshes, mortar_sizes, all_mortar_inertial_coords,\n          mortar_jacobians, penalty_factors, mesh, element, neighbor_meshes,\n          element_map, inv_jacobian, domain, functions_of_time,\n          penalty_parameter, nullptr, nullptr, amr_data...);\n  }\n  template <typename Background, typename Metavariables, typename... AmrData>\n  static void apply(\n      const gsl::not_null<DirectionMap<Dim, Direction<Dim>>*> face_directions,\n      const gsl::not_null<DirectionMap<Dim, tnsr::I<DataVector, Dim>>*>\n          faces_inertial_coords,\n      const gsl::not_null<DirectionMap<Dim, tnsr::i<DataVector, Dim>>*>\n          face_normals,\n      const gsl::not_null<DirectionMap<Dim, tnsr::I<DataVector, Dim>>*>\n          face_normal_vectors,\n      const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>\n          face_normal_magnitudes,\n      const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>\n          face_jacobians,\n      const gsl::not_null<DirectionMap<\n          Dim, InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                               Frame::Inertial>>*>\n          face_jacobian_times_inv_jacobian,\n      const gsl::not_null<DirectionMap<Dim, tnsr::ij<DataVector, Dim>>*>\n          deriv_unnormalized_face_normals,\n      const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_meshes,\n      const gsl::not_null<::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>*>\n          mortar_sizes,\n      const gsl::not_null<::dg::MortarMap<Dim, tnsr::I<DataVector, Dim>>*>\n          all_mortar_inertial_coords,\n      const gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>\n          mortar_jacobians,\n      const gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>\n          penalty_factors,\n      const Mesh<Dim>& mesh, const Element<Dim>& element,\n      const DirectionalIdMap<Dim, Mesh<Dim>>& neighbor_meshes,\n      const ElementMap<Dim, Frame::Inertial>& element_map,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const Domain<Dim>& domain,\n      const domain::FunctionsOfTimeMap& functions_of_time,\n      const double penalty_parameter, const Background& background,\n      const Metavariables& /*meta*/, const AmrData&... /*amr_data*/) {\n    static_assert(std::is_same_v<InvMetricTag, void> or\n                      not(std::is_same_v<Background, std::nullptr_t>),\n                  \"Supply an analytic background from which the 'InvMetricTag' \"\n                  \"can be retrieved\");\n    [[maybe_unused]] const auto get_inv_metric =\n        [&background]([[maybe_unused]] const tnsr::I<DataVector, Dim>&\n                          local_inertial_coords)\n        -> std::optional<tnsr::II<DataVector, Dim>> {\n      if constexpr (not std::is_same_v<InvMetricTag, void>) {\n        using factory_classes = typename std::decay_t<\n            Metavariables>::factory_creation::factory_classes;\n        return call_with_dynamic_type<tnsr::II<DataVector, Dim>,\n                                      tmpl::at<factory_classes, Background>>(\n            &background, [&local_inertial_coords](const auto* const derived) {\n              return get<InvMetricTag>(derived->variables(\n                  local_inertial_coords, tmpl::list<InvMetricTag>{}));\n            });\n      } else {\n        (void)background;\n        return std::nullopt;\n      }\n    };\n    ASSERT(\n        alg::all_of(mesh.quadrature(),\n                    [&mesh](const auto t) { return t == mesh.quadrature(0); }),\n        \"This function is implemented assuming the quadrature is isotropic\");\n    // Faces\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      const auto face_mesh = mesh.slice_away(direction.dimension());\n      (*face_directions)[direction] = direction;\n      // Possible optimization: Not all systems need the coordinates on internal\n      // faces.\n      const auto face_logical_coords =\n          interface_logical_coordinates(face_mesh, direction);\n      auto& face_inertial_coords = (*faces_inertial_coords)[direction];\n      face_inertial_coords =\n          element_map(face_logical_coords, 0., functions_of_time);\n      auto& face_normal = (*face_normals)[direction];\n      auto& face_normal_vector = (*face_normal_vectors)[direction];\n      auto& face_normal_magnitude = (*face_normal_magnitudes)[direction];\n      // Buffer the inv Jacobian on the face here, then multiply by the face\n      // Jacobian below\n      auto& inv_jacobian_on_face =\n          (*face_jacobian_times_inv_jacobian)[direction];\n      inv_jacobian_on_face =\n          element_map.inv_jacobian(face_logical_coords, 0., functions_of_time);\n      unnormalized_face_normal(make_not_null(&face_normal), face_mesh,\n                               inv_jacobian_on_face, direction);\n      if constexpr (std::is_same_v<InvMetricTag, void>) {\n        magnitude(make_not_null(&face_normal_magnitude), face_normal);\n        for (size_t d = 0; d < Dim; ++d) {\n          face_normal.get(d) /= get(face_normal_magnitude);\n          face_normal_vector.get(d) = face_normal.get(d);\n        }\n      } else {\n        const auto inv_metric_on_face = *get_inv_metric(face_inertial_coords);\n        magnitude(make_not_null(&face_normal_magnitude), face_normal,\n                  inv_metric_on_face);\n        for (size_t d = 0; d < Dim; ++d) {\n          face_normal.get(d) /= get(face_normal_magnitude);\n        }\n        raise_or_lower_index(make_not_null(&face_normal_vector), face_normal,\n                             inv_metric_on_face);\n      }\n      auto& face_jacobian = (*face_jacobians)[direction];\n      get(face_jacobian) =\n          get(face_normal_magnitude) / get(determinant(inv_jacobian_on_face));\n      for (auto& component : inv_jacobian_on_face) {\n        component *= get(face_jacobian);\n      }\n    }\n    // Compute the Jacobian derivative numerically, because our coordinate maps\n    // currently don't provide it analytically.\n    detail::deriv_unnormalized_face_normals_impl(\n        deriv_unnormalized_face_normals, mesh, element, inv_jacobian);\n    // Mortars (internal directions)\n    mortar_meshes->clear();\n    mortar_sizes->clear();\n    all_mortar_inertial_coords->clear();\n    mortar_jacobians->clear();\n    penalty_factors->clear();\n    const auto& element_id = element.id();\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      const auto face_mesh = mesh.slice_away(direction.dimension());\n      for (const auto& neighbor_id : neighbors) {\n        const auto& orientation = neighbors.orientation(neighbor_id);\n        const ::dg::MortarId<Dim> mortar_id{direction, neighbor_id};\n        const auto& neighbor_mesh = neighbor_meshes.at(mortar_id);\n        mortar_meshes->emplace(\n            mortar_id,\n            ::dg::mortar_mesh(face_mesh,\n                              neighbor_mesh.slice_away(direction.dimension())));\n        mortar_sizes->emplace(\n            mortar_id, ::dg::mortar_size(element_id, neighbor_id,\n                                         direction.dimension(), orientation));\n        // Mortar Jacobian\n        const auto& mortar_mesh = mortar_meshes->at(mortar_id);\n        const auto& mortar_size = mortar_sizes->at(mortar_id);\n        const auto mortar_logical_coords = detail::mortar_logical_coordinates(\n            mortar_mesh, mortar_size, direction);\n        const auto& mortar_inertial_coords =\n            all_mortar_inertial_coords\n                ->emplace(mortar_id, element_map(mortar_logical_coords, 0.,\n                                                 functions_of_time))\n                .first->second;\n        Scalar<DataVector> perpendicular_element_size{};\n        if (Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)) {\n          auto& mortar_jacobian = (*mortar_jacobians)[mortar_id];\n          detail::mortar_jacobian(make_not_null(&mortar_jacobian),\n                                  make_not_null(&perpendicular_element_size),\n                                  mortar_mesh, mortar_size, direction,\n                                  mortar_logical_coords,\n                                  get_inv_metric(mortar_inertial_coords),\n                                  element_map, functions_of_time);\n        } else {\n          // Mortar is identical to face, and we have computed the face normal\n          // magnitude already above\n          get(perpendicular_element_size) =\n              2. / get(face_normal_magnitudes->at(direction));\n        }\n        // Penalty factor\n        // The penalty factor (like all quantities on mortars) must agree when\n        // calculated on both sides of the mortar. So we switch perspective to\n        // the neighbor here.\n        const auto direction_in_neighbor = orientation(direction).opposite();\n        const auto reoriented_mortar_mesh = orient_mesh_on_slice(\n            mortar_mesh, direction.dimension(), orientation);\n        const auto mortar_size_in_neighbor = ::dg::mortar_size(\n            neighbor_id, element_id, direction_in_neighbor.dimension(),\n            orientation.inverse_map());\n        const auto mortar_logical_coords_in_neighbor =\n            detail::mortar_logical_coordinates(reoriented_mortar_mesh,\n                                               mortar_size_in_neighbor,\n                                               direction_in_neighbor);\n        const ElementMap<Dim, Frame::Inertial> neighbor_element_map{\n            neighbor_id, domain.blocks()[neighbor_id.block_id()]};\n        const auto reoriented_mortar_inertial_coords = neighbor_element_map(\n            mortar_logical_coords_in_neighbor, 0., functions_of_time);\n        Scalar<DataVector> buffer{};\n        Scalar<DataVector> reoriented_neighbor_element_size{};\n        detail::mortar_jacobian(\n            make_not_null(&buffer),\n            make_not_null(&reoriented_neighbor_element_size),\n            reoriented_mortar_mesh, mortar_size_in_neighbor,\n            direction_in_neighbor, mortar_logical_coords_in_neighbor,\n            get_inv_metric(reoriented_mortar_inertial_coords),\n            neighbor_element_map, functions_of_time);\n        // Orient the result back to the perspective of this element\n        const auto neighbor_element_size = orient_variables_on_slice(\n            get(reoriented_neighbor_element_size),\n            reoriented_mortar_mesh.extents(), direction_in_neighbor.dimension(),\n            orientation.inverse_map());\n        penalty_factors->emplace(\n            mortar_id,\n            elliptic::dg::penalty(\n                blaze::min(get(perpendicular_element_size),\n                           neighbor_element_size),\n                std::max(mesh.extents(direction.dimension()),\n                         neighbor_mesh.extents(direction.dimension())),\n                penalty_parameter));\n      }  // neighbors\n    }    // internal directions\n    // Mortars (external directions)\n    for (const auto& direction : element.external_boundaries()) {\n      const auto face_mesh = mesh.slice_away(direction.dimension());\n      const auto mortar_id =\n          DirectionalId<Dim>{direction, ElementId<Dim>::external_boundary_id()};\n      mortar_meshes->emplace(mortar_id, face_mesh);\n      mortar_sizes->emplace(mortar_id,\n                            make_array<Dim - 1>(Spectral::SegmentSize::Full));\n      penalty_factors->emplace(\n          mortar_id,\n          elliptic::dg::penalty(2. / get(face_normal_magnitudes->at(direction)),\n                                mesh.extents(direction.dimension()),\n                                penalty_parameter));\n    }  // external directions\n  }\n};\n\n/// Initialize background quantities for the elliptic DG operator, possibly\n/// including the metric necessary for normalizing face normals\ntemplate <size_t Dim, typename BackgroundFields, typename BackgroundTag>\nstruct InitializeBackground : tt::ConformsTo<::amr::protocols::Projector> {\n  using return_tags =\n      tmpl::list<::Tags::Variables<BackgroundFields>,\n                 domain::Tags::Faces<Dim, ::Tags::Variables<BackgroundFields>>>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 domain::Tags::Mesh<Dim>,\n                 domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>,\n                 BackgroundTag, Parallel::Tags::Metavariables>;\n\n  template <typename BackgroundBase, typename Metavariables,\n            typename... AmrData>\n  static void apply(\n      const gsl::not_null<Variables<BackgroundFields>*> background_fields,\n      const gsl::not_null<DirectionMap<Dim, Variables<BackgroundFields>>*>\n          face_background_fields,\n      const tnsr::I<DataVector, Dim>& inertial_coords, const Mesh<Dim>& mesh,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const BackgroundBase& background, const Metavariables& /*meta*/,\n      const AmrData&... amr_data) {\n    if constexpr (sizeof...(AmrData) == 1) {\n      if constexpr (std::is_same_v<AmrData...,\n                                   std::pair<Mesh<Dim>, Element<Dim>>>) {\n        if (((mesh == amr_data.first) and ...)) {\n          // This element hasn't changed during AMR. Nothing to do.\n          return;\n        }\n      }\n    }\n\n    // Background fields in the volume\n    using factory_classes =\n        typename std::decay_t<Metavariables>::factory_creation::factory_classes;\n    *background_fields =\n        call_with_dynamic_type<Variables<BackgroundFields>,\n                               tmpl::at<factory_classes, BackgroundBase>>(\n            &background, [&inertial_coords, &mesh,\n                          &inv_jacobian](const auto* const derived) {\n              return variables_from_tagged_tuple(derived->variables(\n                  inertial_coords, mesh, inv_jacobian, BackgroundFields{}));\n            });\n    // Background fields on faces\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      // Possible optimization: Only the background fields in the\n      // System::fluxes_computer::argument_tags are needed on internal faces.\n      // On Gauss grids we could evaluate the background directly on the faces\n      // instead of projecting the data. However, we need to take derivatives of\n      // the background fields, so we could evaluate them on a Gauss-Lobatto\n      // grid in the volume. We could even evaluate the background fields on a\n      // higher-order grid and project down to get more accurate derivatives if\n      // needed.\n      (*face_background_fields)[direction].initialize(\n          mesh.slice_away(direction.dimension()).number_of_grid_points());\n      ::dg::project_contiguous_data_to_boundary(\n          make_not_null(&(*face_background_fields)[direction]),\n          *background_fields, mesh, direction);\n    }\n  }\n};\n\n}  // namespace elliptic::dg\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/Penalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/DiscontinuousGalerkin/Penalty.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace elliptic::dg {\n\nDataVector penalty(const DataVector& element_size, const size_t num_points,\n                   const double penalty_parameter) {\n  return penalty_parameter * square(num_points) / element_size;\n}\n\n}  // namespace elliptic::dg\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/Penalty.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n\nnamespace elliptic::dg {\n\n/*!\n * \\brief The penalty factor in internal penalty fluxes\n *\n * The penalty factor is computed as\n *\n * \\f{equation}\n * \\sigma = C \\frac{N_\\text{points}^2}{h}\n * \\f}\n *\n * where \\f$N_\\text{points} = 1 + N_p\\f$ is the number of points (or one plus\n * the polynomial degree) and \\f$h\\f$ is a measure of the element size. Both\n * quantities are taken perpendicular to the face of the DG element that the\n * penalty is being computed on. \\f$C\\f$ is the \"penalty parameter\". For details\n * see section 7.2 in \\cite HesthavenWarburton.\n */\nDataVector penalty(const DataVector& element_size, size_t num_points,\n                   double penalty_parameter);\n\n}  // namespace elliptic::dg\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/SubdomainOperator/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EllipticDgSubdomainOperator)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  InitializeSubdomain.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InitializeSubdomain.hpp\n  SubdomainOperator.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DomainStructure\n  DiscontinuousGalerkin\n  FunctionsOfTime\n  ParallelSchwarz\n  Spectral\n  Utilities\n  INTERFACE\n  Boost::boost\n  Domain\n  Elliptic\n  EllipticDg\n  ErrorHandling\n)\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/SubdomainOperator/InitializeSubdomain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/InitializeSubdomain.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace elliptic::dg::subdomain_operator::Actions::detail {\n\ntemplate <size_t Dim>\nvoid InitializeOverlapGeometry<Dim>::operator()(\n    const gsl::not_null<size_t*> extruding_extent,\n    const gsl::not_null<::dg::MortarMap<\n        Dim, Scalar<DataVector>>*> /*neighbor_face_normal_magnitudes*/,\n    const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*>\n        neighbor_mortar_meshes,\n    const gsl::not_null<::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>*>\n        neighbor_mortar_sizes,\n    const Element<Dim>& element, const Mesh<Dim>& mesh,\n    const DirectionalIdMap<Dim, Mesh<Dim>>& neighbor_meshes,\n    const ElementId<Dim>& element_id, const Direction<Dim>& overlap_direction,\n    const std::optional<size_t> max_overlap) const {\n  // Extruding extent\n  *extruding_extent = LinearSolver::Schwarz::overlap_extent(\n      mesh.extents(overlap_direction.dimension()), max_overlap);\n  // Geometry on the remote side of the mortar. These are only needed on mortars\n  // to neighbors that are not part of the subdomain, so conditionally skipping\n  // this setup is a possible optimization. The computational cost and memory\n  // usage is probably irrelevant though.\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    for (const auto& neighbor_id : neighbors) {\n      const auto& orientation = neighbors.orientation(neighbor_id);\n      const auto direction_from_neighbor = orientation(direction.opposite());\n      const auto reoriented_face_mesh =\n          orientation(mesh).slice_away(direction_from_neighbor.dimension());\n      const ::dg::MortarId<Dim> mortar_id{direction, neighbor_id};\n      const auto& neighbor_mesh = neighbor_meshes.at(mortar_id);\n      const auto neighbor_face_mesh =\n          orientation(neighbor_mesh)\n              .slice_away(direction_from_neighbor.dimension());\n      neighbor_mortar_meshes->emplace(\n          mortar_id,\n          ::dg::mortar_mesh(reoriented_face_mesh, neighbor_face_mesh));\n      neighbor_mortar_sizes->emplace(\n          mortar_id, ::dg::mortar_size(neighbor_id, element_id,\n                                       direction_from_neighbor.dimension(),\n                                       orientation.inverse_map()));\n    }  // neighbors\n  }  // internal directions\n}\n\ntemplate class InitializeOverlapGeometry<1>;\ntemplate class InitializeOverlapGeometry<2>;\ntemplate class InitializeOverlapGeometry<3>;\n\n}  // namespace elliptic::dg::subdomain_operator::Actions::detail\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/SubdomainOperator/InitializeSubdomain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Initialization.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/Tags.hpp\"\n#include \"Elliptic/Systems/GetFluxesComputer.hpp\"\n#include \"Elliptic/Utilities/ApplyAt.hpp\"\n#include \"Elliptic/Utilities/GetAnalyticData.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\nnamespace tuples {\ntemplate <typename... Tags>\nstruct TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\n/// Actions related to the DG subdomain operator\nnamespace elliptic::dg::subdomain_operator::Actions {\n\nnamespace detail {\n// Initialize the geometry of a neighbor into which an overlap extends\ntemplate <size_t Dim>\nstruct InitializeOverlapGeometry {\n  using return_tags =\n      tmpl::list<elliptic::dg::subdomain_operator::Tags::ExtrudingExtent,\n                 elliptic::dg::subdomain_operator::Tags::NeighborMortars<\n                     domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>, Dim>,\n                 elliptic::dg::subdomain_operator::Tags::NeighborMortars<\n                     domain::Tags::Mesh<Dim - 1>, Dim>,\n                 elliptic::dg::subdomain_operator::Tags::NeighborMortars<\n                     ::Tags::MortarSize<Dim - 1>, Dim>>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                 domain::Tags::NeighborMesh<Dim>>;\n  void operator()(\n      gsl::not_null<size_t*> extruding_extent,\n      gsl::not_null<::dg::MortarMap<Dim, Scalar<DataVector>>*>\n          neighbor_face_normal_magnitudes,\n      gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*>\n          neighbor_mortar_meshes,\n      gsl::not_null<::dg::MortarMap<Dim, ::dg::MortarSize<Dim - 1>>*>\n          neighbor_mortar_sizes,\n      const Element<Dim>& element, const Mesh<Dim>& mesh,\n      const DirectionalIdMap<Dim, Mesh<Dim>>& neighbor_meshes,\n      const ElementId<Dim>& element_id, const Direction<Dim>& overlap_direction,\n      std::optional<size_t> max_overlap) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief Initialize the geometry for the DG subdomain operator\n *\n * Initializes tags that define the geometry of overlap regions with neighboring\n * elements. The data needs to be updated if the geometry of neighboring\n * elements changes.\n *\n * Note that the geometry depends on the system and on the choice of background\n * through the normalization of face normals, which involves a metric.\n *\n * The `FromInitialDomain` template parameter controls how the\n * next-to-nearest-neighbor information is acquired:\n * - If `FromInitialDomain = true`, uses the initial parameters from the input\n *   file. This can be used at the beginning of a simulation or if the domain\n *   never changes (no AMR).\n * - If `FromInitialDomain = false`, uses the mesh, element, and neighbor meshes\n *   of each neighbor. This typically requires communication to exchange this\n *   data between neighbors each time the domain changes, e.g. with AMR.\n *\n * DataBox:\n * - Uses:\n *   - `BackgroundTag`\n *   - `domain::Tags::Element<Dim>`\n *   - If `FromInitialDomain = true`:\n *     - `domain::Tags::InitialExtents<Dim>`\n *     - `domain::Tags::InitialRefinementLevels<Dim>`\n *     - `elliptic::dg::Tags::Quadrature`\n *   - If `FromInitialDomain = false`:\n *     - `Overlaps<domain::Tags::Mesh<Dim>>`\n *     - `Overlaps<domain::Tags::Element<Dim>>`\n *     - `Overlaps<domain::Tags::NeighborMesh<Dim>>`\n *   - `domain::Tags::Domain<Dim>`\n *   - `domain::Tags::FunctionsOfTime`\n *   - `LinearSolver::Schwarz::Tags::MaxOverlap<OptionsGroup>`\n * - Adds: Many tags prefixed with `LinearSolver::Schwarz::Tags::Overlaps`. See\n *   `elliptic::dg::Actions::InitializeDomain` and\n *   `elliptic::dg::Actions::initialize_operator` for a complete list.\n */\ntemplate <typename System, typename BackgroundTag, typename OptionsGroup,\n          bool FromInitialDomain = true>\nstruct InitializeSubdomain {\n private:\n  static constexpr size_t Dim = System::volume_dim;\n  static constexpr bool is_curved =\n      not std::is_same_v<typename System::inv_metric_tag, void>;\n  static constexpr bool has_background_fields =\n      not std::is_same_v<typename System::background_fields, tmpl::list<>>;\n\n  using InitializeGeometry =\n      tmpl::conditional_t<FromInitialDomain,\n                          elliptic::dg::InitializeGeometry<Dim>,\n                          elliptic::dg::ProjectGeometry<Dim>>;\n  using InitializeOverlapGeometry = detail::InitializeOverlapGeometry<Dim>;\n  using InitializeFacesAndMortars = elliptic::dg::InitializeFacesAndMortars<\n      Dim, typename System::inv_metric_tag, BackgroundTag>;\n\n  template <typename Tag>\n  using overlaps_tag =\n      LinearSolver::Schwarz::Tags::Overlaps<Tag, Dim, OptionsGroup>;\n  // Only slice those background fields to internal boundaries that are\n  // necessary for the DG operator, i.e. the background fields in the\n  // System::fluxes_computer::argument_tags\n  using fluxes_non_background_args =\n      tmpl::list_difference<elliptic::get_fluxes_argument_tags<System, true>,\n                            typename System::background_fields>;\n  using background_fields_internal =\n      tmpl::list_difference<elliptic::get_fluxes_argument_tags<System, true>,\n                            fluxes_non_background_args>;\n  // Slice all background fields to external boundaries for use in boundary\n  // conditions\n  using background_fields_external = typename System::background_fields;\n\n public:\n  using simple_tags_from_options =\n      tmpl::list<domain::Tags::InitialExtents<Dim>,\n                 domain::Tags::InitialRefinementLevels<Dim>>;\n  using const_global_cache_tags =\n      tmpl::list<LinearSolver::Schwarz::Tags::MaxOverlap<OptionsGroup>>;\n  using simple_tags = db::wrap_tags_in<\n      overlaps_tag,\n      tmpl::append<\n          typename InitializeGeometry::return_tags,\n          typename InitializeFacesAndMortars::return_tags,\n          typename InitializeOverlapGeometry::return_tags,\n          tmpl::conditional_t<\n              has_background_fields,\n              tmpl::list<::Tags::Variables<typename System::background_fields>>,\n              tmpl::list<>>,\n          domain::make_faces_tags<Dim, typename System::background_fields>>>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DataBox, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      DataBox& box, const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& element = db::get<domain::Tags::Element<Dim>>(box);\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      for (const auto& neighbor_id : neighbors) {\n        const auto& orientation = neighbors.orientation(neighbor_id);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        const LinearSolver::Schwarz::OverlapId<Dim> overlap_id{direction,\n                                                               neighbor_id};\n        // Initialize background-agnostic geometry on overlaps\n        elliptic::util::mutate_apply_at<\n            db::wrap_tags_in<overlaps_tag,\n                             typename InitializeGeometry::return_tags>,\n            tmpl::append<\n                db::wrap_tags_in<overlaps_tag,\n                                 tmpl::list_difference<\n                                     typename InitializeGeometry::argument_tags,\n                                     typename InitializeGeometry::volume_tags>>,\n                typename InitializeGeometry::volume_tags>,\n            typename InitializeGeometry::volume_tags>(\n            InitializeGeometry{}, make_not_null(&box), overlap_id, neighbor_id);\n        // Initialize subdomain-specific tags on overlaps\n        elliptic::util::mutate_apply_at<\n            db::wrap_tags_in<overlaps_tag,\n                             typename InitializeOverlapGeometry::return_tags>,\n            db::wrap_tags_in<overlaps_tag,\n                             typename InitializeOverlapGeometry::argument_tags>,\n            tmpl::list<>>(\n            InitializeOverlapGeometry{}, make_not_null(&box), overlap_id,\n            neighbor_id, direction_from_neighbor,\n            db::get<LinearSolver::Schwarz::Tags::MaxOverlap<OptionsGroup>>(\n                box));\n        // Initialize faces and mortars on overlaps\n        elliptic::util::mutate_apply_at<\n            db::wrap_tags_in<overlaps_tag,\n                             typename InitializeFacesAndMortars::return_tags>,\n            tmpl::append<\n                db::wrap_tags_in<\n                    overlaps_tag,\n                    tmpl::list_difference<\n                        typename InitializeFacesAndMortars::argument_tags,\n                        typename InitializeFacesAndMortars::volume_tags>>,\n                typename InitializeFacesAndMortars::volume_tags>,\n            typename InitializeFacesAndMortars::volume_tags>(\n            InitializeFacesAndMortars{}, make_not_null(&box), overlap_id);\n        if constexpr (has_background_fields) {\n          // Background fields\n          initialize_background_fields(make_not_null(&box), overlap_id);\n        }\n        // Faces on the other side of the overlapped element's mortars\n        initialize_remote_faces(make_not_null(&box), overlap_id);\n      }  // neighbors in direction\n    }  // directions\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n private:\n  template <typename DbTagsList>\n  static void initialize_background_fields(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const LinearSolver::Schwarz::OverlapId<Dim>& overlap_id) {\n    const auto& background = db::get<BackgroundTag>(*box);\n    DirectionMap<Dim, Variables<typename System::background_fields>>\n        face_background_fields{};\n    elliptic::util::mutate_apply_at<\n        tmpl::list<overlaps_tag<\n            ::Tags::Variables<typename System::background_fields>>>,\n        db::wrap_tags_in<\n            overlaps_tag,\n            tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                       domain::Tags::Mesh<Dim>,\n                       domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                     Frame::Inertial>>>,\n        tmpl::list<>>(\n        [&background, &face_background_fields, &box](\n            const gsl::not_null<Variables<typename System::background_fields>*>\n                background_fields,\n            const tnsr::I<DataVector, Dim>& inertial_coords,\n            const Mesh<Dim>& mesh,\n            const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>& inv_jacobian) {\n          *background_fields = elliptic::util::get_analytic_data<\n              typename System::background_fields>(\n              background, *box, inertial_coords, mesh, inv_jacobian);\n          for (const auto& direction : Direction<Dim>::all_directions()) {\n            // Slice the background fields to the face instead of evaluating\n            // them on the face coords to avoid re-computing them, and because\n            // this is also what the DG operator currently does. The result is\n            // the same on Gauss-Lobatto grids, but may need adjusting when\n            // adding support for Gauss grids.\n            face_background_fields[direction].initialize(\n                mesh.slice_away(direction.dimension()).number_of_grid_points());\n            ::dg::project_contiguous_data_to_boundary(\n                make_not_null(&face_background_fields[direction]),\n                *background_fields, mesh, direction);\n          }\n        },\n        box, overlap_id);\n    // Move face background fields into DataBox\n    const auto mutate_assign_face_background_field =\n        [&box, &overlap_id, &face_background_fields](\n            auto tag_v, const Direction<Dim>& direction) {\n          using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n          db::mutate<overlaps_tag<domain::Tags::Faces<Dim, tag>>>(\n              [&face_background_fields, &overlap_id,\n               &direction](const auto stored_value) {\n                (*stored_value)[overlap_id][direction] =\n                    get<tag>(face_background_fields.at(direction));\n              },\n              box);\n        };\n    const auto& element =\n        db::get<overlaps_tag<domain::Tags::Element<Dim>>>(*box).at(overlap_id);\n    for (const auto& direction : element.internal_boundaries()) {\n      tmpl::for_each<background_fields_internal>(\n          [&mutate_assign_face_background_field, &direction](auto tag_v) {\n            mutate_assign_face_background_field(tag_v, direction);\n          });\n    }\n    for (const auto& direction : element.external_boundaries()) {\n      tmpl::for_each<background_fields_external>(\n          [&mutate_assign_face_background_field, &direction](auto tag_v) {\n            mutate_assign_face_background_field(tag_v, direction);\n          });\n    }\n  }\n\n  // Faces on the other side of the overlapped element's mortars\n  template <typename DbTagsList>\n  static void initialize_remote_faces(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const LinearSolver::Schwarz::OverlapId<Dim>& overlap_id) {\n    const auto& element =\n        db::get<overlaps_tag<domain::Tags::Element<Dim>>>(*box).at(overlap_id);\n    const auto& domain = db::get<domain::Tags::Domain<Dim>>(*box);\n    const auto& functions_of_time =\n        db::get<domain::Tags::FunctionsOfTime>(*box);\n    const auto& neighbor_meshes =\n        db::get<overlaps_tag<domain::Tags::NeighborMesh<Dim>>>(*box).at(\n            overlap_id);\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      for (const auto& neighbor_id : neighbors) {\n        const auto& orientation = neighbors.orientation(neighbor_id);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        const ::dg::MortarId<Dim> mortar_id{direction, neighbor_id};\n        const auto neighbor_face_mesh =\n            orientation(neighbor_meshes.at(mortar_id))\n                .slice_away(direction_from_neighbor.dimension());\n        const auto neighbor_face_logical_coords = interface_logical_coordinates(\n            neighbor_face_mesh, direction_from_neighbor);\n        const auto& neighbor_block = domain.blocks()[neighbor_id.block_id()];\n        const ElementMap<Dim, Frame::Inertial> neighbor_element_map{\n            neighbor_id, neighbor_block};\n        const auto neighbor_face_normal = unnormalized_face_normal(\n            neighbor_face_mesh,\n            neighbor_element_map.inv_jacobian(neighbor_face_logical_coords, 0.,\n                                              functions_of_time),\n            direction_from_neighbor);\n        using neighbor_face_normal_magnitudes_tag = overlaps_tag<\n            elliptic::dg::subdomain_operator::Tags::NeighborMortars<\n                domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>, Dim>>;\n        if constexpr (is_curved) {\n          const auto& background = db::get<BackgroundTag>(*box);\n          const auto neighbor_face_inertial_coords = neighbor_element_map(\n              neighbor_face_logical_coords, 0., functions_of_time);\n          const auto inv_metric_on_face = get<typename System::inv_metric_tag>(\n              elliptic::util::get_analytic_data<\n                  tmpl::list<typename System::inv_metric_tag>>(\n                  background, *box, neighbor_face_inertial_coords));\n          elliptic::util::mutate_apply_at<\n              tmpl::list<neighbor_face_normal_magnitudes_tag>, tmpl::list<>,\n              tmpl::list<>>(\n              [&neighbor_face_normal,\n               &inv_metric_on_face](const auto neighbor_face_normal_magnitude) {\n                magnitude(neighbor_face_normal_magnitude, neighbor_face_normal,\n                          inv_metric_on_face);\n              },\n              box, std::make_tuple(overlap_id, mortar_id));\n        } else {\n          elliptic::util::mutate_apply_at<\n              tmpl::list<neighbor_face_normal_magnitudes_tag>, tmpl::list<>,\n              tmpl::list<>>(\n              [&neighbor_face_normal](\n                  const auto neighbor_face_normal_magnitude) {\n                magnitude(neighbor_face_normal_magnitude, neighbor_face_normal);\n              },\n              box, std::make_tuple(overlap_id, mortar_id));\n        }\n      }  // neighbors\n    }  // internal directions\n  }\n};\n\n}  // namespace elliptic::dg::subdomain_operator::Actions\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/SubdomainOperator/SubdomainOperator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Domain/Tags/SurfaceJacobian.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgOperator.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/Tags.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Systems/GetFluxesComputer.hpp\"\n#include \"Elliptic/Systems/GetSourcesComputer.hpp\"\n#include \"Elliptic/Utilities/ApplyAt.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/SubdomainOperator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Items related to the restriction of the DG operator to an element-centered\n/// subdomain\nnamespace elliptic::dg::subdomain_operator {\n\nnamespace detail {\n// Wrap the `Tag` in `LinearSolver::Schwarz::Tags::Overlaps`, except if it is\n// included in `TakeFromCenterTags`.\ntemplate <typename Tag, typename Dim, typename OptionsGroup,\n          typename TakeFromCenterTags>\nstruct make_overlap_tag_impl {\n  using type = tmpl::conditional_t<\n      tmpl::list_contains_v<TakeFromCenterTags, Tag>, Tag,\n      LinearSolver::Schwarz::Tags::Overlaps<Tag, Dim::value, OptionsGroup>>;\n};\n\n// Wrap the `Tag` in `Tags::NeighborMortars`\ntemplate <typename Tag, typename Dim>\nstruct make_neighbor_mortars_tag_impl {\n  using type = Tags::NeighborMortars<Tag, Dim::value>;\n};\n}  // namespace detail\n\n/*!\n * \\brief The elliptic DG operator on an element-centered subdomain\n *\n * This operator is a restriction of the full (linearized) DG-operator to an\n * element-centered subdomain with a few points overlap into neighboring\n * elements. It is a `LinearSolver::Schwarz::SubdomainOperator` to be used with\n * the Schwarz linear solver when it solves the elliptic DG operator.\n *\n * This operator requires the following tags are available on overlap regions\n * with neighboring elements:\n *\n * - Geometric quantities provided by\n *   `elliptic::dg::subdomain_operator::InitializeSubdomain`.\n * - All `System::fluxes_computer::argument_tags` and\n *   `System::sources_computer::argument_tags` (or\n *   `System::fluxes_computer_linearized::argument_tags` and\n *   `System::sources_computer_linearized::argument_tags` for nonlinear\n *   systems), except those listed in `ArgsTagsFromCenter`. The latter will be\n *   taken from the central element's DataBox, so they don't need to be made\n *   available on overlaps.\n * - The `System::fluxes_computer::argument_tags` (or\n *   `System::fluxes_computer_linearized::argument_tags`) on internal and\n *   external interfaces, except those listed in\n *   `System::fluxes_computer::volume_tags` (or\n *   `System::fluxes_computer_linearized::volume_tags`).\n *\n * Some of these tags may require communication between elements. For example,\n * nonlinear system fields are constant background fields for the linearized DG\n * operator, but are updated in every nonlinear solver iteration. Therefore, the\n * updated nonlinear fields must be communicated across overlaps between\n * nonlinear solver iterations. To perform the communication you can use\n * `LinearSolver::Schwarz::Actions::SendOverlapFields` and\n * `LinearSolver::Schwarz::Actions::ReceiveOverlapFields`, setting\n * `RestrictToOverlap` to `false`. See\n * `LinearSolver::Schwarz::SubdomainOperator` for details.\n *\n * \\par Overriding boundary conditions\n * Sometimes the subdomain operator should not use the boundary conditions that\n * have been selected when setting up the domain. For example, when the\n * subdomain operator is cached between non-linear solver iterations but the\n * boundary conditions depend on the non-linear fields, the preconditioning can\n * become ineffective (see\n * `LinearSolver::Schwarz::Actions::ResetSubdomainSolver`). Another example is\n * `elliptic::subdomain_preconditioners::MinusLaplacian`, where an auxiliary\n * Poisson system is used for preconditioning that doesn't have boundary\n * conditions set up in the domain. In these cases, the boundary conditions used\n * for the subdomain operator can be overridden with the optional\n * `override_boundary_conditions` argument to the `operator()`. If the\n * overriding boundary conditions are different from those listed in\n * `Metavariables::factory_creation`, you can supply the list of\n * boundary-condition classes to the `BoundaryConditionClasses` template\n * parameter. Note that the subdomain operator always applies the _linearized_\n * boundary conditions.\n *\n * \\warning The subdomain operator hasn't been tested with periodic boundary\n * conditions so far.\n */\ntemplate <typename System, typename OptionsGroup,\n          typename BoundaryConditionClasses = tmpl::list<>>\nstruct SubdomainOperator\n    : LinearSolver::Schwarz::SubdomainOperator<System::volume_dim> {\n public:\n  using system = System;\n  using options_group = OptionsGroup;\n\n private:\n  static constexpr size_t Dim = System::volume_dim;\n\n  // Operator applications happen sequentially so we don't have to keep track of\n  // the temporal id\n  static constexpr size_t temporal_id = 0;\n\n  // The subdomain operator always applies the linearized DG operator\n  static constexpr bool linearized = true;\n\n  using BoundaryConditionsBase = typename System::boundary_conditions_base;\n\n  // These are the arguments that we need to retrieve from the DataBox and pass\n  // to the functions in `elliptic::dg`, both on the central element and on\n  // neighbors\n  using prepare_args_tags =\n      tmpl::list<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                 domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>,\n                 domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>,\n                 ::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>,\n                 ::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>>;\n  using apply_args_tags = tmpl::list<\n      domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n      domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,\n                                        Frame::Inertial>,\n      domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>,\n      domain::Tags::Faces<Dim, domain::Tags::FaceNormalVector<Dim>>,\n      domain::Tags::Faces<Dim,\n                          domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>>,\n      domain::Tags::Faces<Dim, domain::Tags::DetSurfaceJacobian<\n                                   Frame::ElementLogical, Frame::Inertial>>,\n      domain::Tags::Faces<\n          Dim, domain::Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical,\n                                                 Frame::Inertial>>,\n      ::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>,\n      ::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>,\n      ::Tags::Mortars<domain::Tags::DetSurfaceJacobian<Frame::ElementLogical,\n                                                       Frame::Inertial>,\n                      Dim>,\n      ::Tags::Mortars<elliptic::dg::Tags::PenaltyFactor, Dim>,\n      elliptic::dg::Tags::Massive, elliptic::dg::Tags::Formulation>;\n  using fluxes_args_tags =\n      elliptic::get_fluxes_argument_tags<System, linearized>;\n  using sources_args_tags =\n      elliptic::get_sources_argument_tags<System, linearized>;\n\n  // We need the fluxes args also on interfaces (internal and external). The\n  // volume tags are the subset that don't have to be taken from interfaces.\n  using fluxes_args_volume_tags =\n      elliptic::get_fluxes_volume_tags<System, linearized>;\n\n  // These tags can be taken directly from the central element's DataBox, even\n  // when evaluating neighbors\n  using args_tags_from_center = tmpl::remove_duplicates<tmpl::push_back<\n      elliptic::get_fluxes_const_global_cache_tags<System, linearized>,\n      elliptic::get_sources_const_global_cache_tags<System, linearized>,\n      elliptic::dg::Tags::Massive, elliptic::dg::Tags::Formulation>>;\n\n  // Data on neighbors is stored in the central element's DataBox in\n  // `LinearSolver::Schwarz::Tags::Overlaps` maps, so we wrap the argument tags\n  // with this prefix\n  using make_overlap_tag =\n      detail::make_overlap_tag_impl<tmpl::_1, tmpl::pin<tmpl::size_t<Dim>>,\n                                    tmpl::pin<OptionsGroup>,\n                                    tmpl::pin<args_tags_from_center>>;\n  using prepare_args_tags_overlap =\n      tmpl::transform<prepare_args_tags, make_overlap_tag>;\n  using apply_args_tags_overlap =\n      tmpl::transform<apply_args_tags, make_overlap_tag>;\n  using fluxes_args_tags_overlap =\n      tmpl::transform<fluxes_args_tags, make_overlap_tag>;\n  using sources_args_tags_overlap =\n      tmpl::transform<sources_args_tags, make_overlap_tag>;\n  using fluxes_args_tags_overlap_faces = tmpl::transform<\n      domain::make_faces_tags<Dim, fluxes_args_tags, fluxes_args_volume_tags>,\n      make_overlap_tag>;\n\n  // We also need some data on the remote side of all neighbors' mortars. Such\n  // data is stored in the central element's DataBox in `Tags::NeighborMortars`\n  // maps\n  using make_neighbor_mortars_tag =\n      detail::make_neighbor_mortars_tag_impl<tmpl::_1,\n                                             tmpl::pin<tmpl::size_t<Dim>>>;\n\n public:\n  /// \\warning This function is not thread-safe because it accesses mutable\n  /// memory buffers.\n  template <typename ResultTags, typename OperandTags, typename DbTagsList>\n  void operator()(\n      const gsl::not_null<\n          LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, ResultTags>*>\n          result,\n      const LinearSolver::Schwarz::ElementCenteredSubdomainData<\n          Dim, OperandTags>& operand,\n      const db::DataBox<DbTagsList>& box,\n      const std::unordered_map<std::pair<size_t, Direction<Dim>>,\n                               const BoundaryConditionsBase&,\n                               boost::hash<std::pair<size_t, Direction<Dim>>>>&\n          override_boundary_conditions = {}) const {\n    result->destructive_resize(operand);\n    central_mortar_data_.clear();\n    neighbors_mortar_data_.clear();\n\n    // Used to retrieve items out of the DataBox to forward to functions. This\n    // replaces a long series of db::get calls.\n    const auto get_items = [](const auto&... args) {\n      return std::forward_as_tuple(args...);\n    };\n\n    // Retrieve data out of the DataBox\n    using tags_to_retrieve = tmpl::flatten<tmpl::list<\n        domain::Tags::ExternalBoundaryConditions<Dim>,\n        domain::Tags::Element<Dim>,\n        ::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>,\n        // Data on overlaps with neighbors\n        tmpl::transform<\n            tmpl::flatten<tmpl::list<\n                Tags::ExtrudingExtent, domain::Tags::Element<Dim>,\n                domain::Tags::Mesh<Dim>,\n                domain::Tags::Faces<\n                    Dim, domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>>,\n                ::Tags::Mortars<domain::Tags::Mesh<Dim - 1>, Dim>,\n                ::Tags::Mortars<::Tags::MortarSize<Dim - 1>, Dim>,\n                // Data on the remote side of the neighbor's mortars\n                tmpl::transform<\n                    tmpl::list<\n                        domain::Tags::UnnormalizedFaceNormalMagnitude<Dim>,\n                        domain::Tags::Mesh<Dim - 1>,\n                        ::Tags::MortarSize<Dim - 1>>,\n                    make_neighbor_mortars_tag>>>,\n            make_overlap_tag>>>;\n    const auto& [external_boundary_conditions, central_element,\n                 central_mortar_meshes, all_overlap_extents,\n                 all_neighbor_elements, all_neighbor_meshes,\n                 all_neighbor_face_normal_magnitudes,\n                 all_neighbor_mortar_meshes, all_neighbor_mortar_sizes,\n                 all_neighbors_neighbor_face_normal_magnitudes,\n                 all_neighbors_neighbor_mortar_meshes,\n                 all_neighbors_neighbor_mortar_sizes] =\n        db::apply<tags_to_retrieve>(get_items, box);\n    const auto fluxes_args = db::apply<fluxes_args_tags>(get_items, box);\n    const auto sources_args = db::apply<sources_args_tags>(get_items, box);\n    using FluxesArgs = std::decay_t<decltype(fluxes_args)>;\n    DirectionMap<Dim, FluxesArgs> fluxes_args_on_faces{};\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      fluxes_args_on_faces.emplace(\n          direction, elliptic::util::apply_at<\n                         domain::make_faces_tags<Dim, fluxes_args_tags,\n                                                 fluxes_args_volume_tags>,\n                         fluxes_args_volume_tags>(get_items, box, direction));\n    }\n\n    // Setup boundary conditions\n    const auto apply_boundary_condition =\n        [&box, &all_boundary_conditions = external_boundary_conditions,\n         &override_boundary_conditions](const ElementId<Dim>& local_element_id,\n                                        const Direction<Dim>& local_direction,\n                                        auto is_overlap, const auto& map_keys,\n                                        auto&&... fields_and_fluxes) {\n          constexpr bool is_overlap_v =\n              std::decay_t<decltype(is_overlap)>::value;\n          // Get boundary conditions from domain, or use overridden boundary\n          // conditions\n          const auto& boundary_condition = [&all_boundary_conditions,\n                                            &local_element_id, &local_direction,\n                                            &override_boundary_conditions]()\n              -> const BoundaryConditionsBase& {\n            if (not override_boundary_conditions.empty()) {\n              const auto found_overridden_boundary_conditions =\n                  override_boundary_conditions.find(\n                      {local_element_id.block_id(), local_direction});\n              ASSERT(found_overridden_boundary_conditions !=\n                         override_boundary_conditions.end(),\n                     \"Overriding boundary conditions in subdomain operator, \"\n                     \"but none is available for block \"\n                         << local_element_id.block_id() << \" in direction \"\n                         << local_direction\n                         << \". Make sure you have considered this external \"\n                            \"boundary of the subdomain. If this is \"\n                            \"intentional, add support to \"\n                            \"elliptic::dg::SubdomainOperator.\");\n              return found_overridden_boundary_conditions->second;\n            }\n            const auto& boundary_conditions =\n                all_boundary_conditions.at(local_element_id.block_id());\n            ASSERT(boundary_conditions.contains(local_direction),\n                   \"No boundary condition is available in block \"\n                       << local_element_id.block_id() << \" in direction \"\n                       << local_direction\n                       << \". Make sure you are setting up boundary conditions \"\n                          \"when creating the domain.\");\n            ASSERT(\n                dynamic_cast<const BoundaryConditionsBase*>(\n                    boundary_conditions.at(local_direction).get()) != nullptr,\n                \"The boundary condition in block \"\n                    << local_element_id.block_id() << \" in direction \"\n                    << local_direction\n                    << \" has an unexpected type. Make sure it derives off the \"\n                       \"'boundary_conditions_base' class set in the system.\");\n            return dynamic_cast<const BoundaryConditionsBase&>(\n                *boundary_conditions.at(local_direction));\n          }();\n          elliptic::apply_boundary_condition<\n              linearized,\n              tmpl::conditional_t<is_overlap_v, make_overlap_tag, void>,\n              BoundaryConditionClasses>(\n              boundary_condition, box, map_keys,\n              std::forward<decltype(fields_and_fluxes)>(fields_and_fluxes)...);\n        };\n\n    // Check if the subdomain data is sparse, i.e. if some elements have zero\n    // data. If they are, the operator is a lot cheaper to apply due to its\n    // linearity.\n    std::unordered_set<ElementId<Dim>> elements_in_subdomain{\n        central_element.id()};\n    std::unordered_set<ElementId<Dim>> elements_with_zero_data{};\n    if (equal_within_roundoff(operand.element_data, 0.)) {\n      elements_with_zero_data.insert(central_element.id());\n    }\n    for (const auto& [overlap_id, overlap_data] : operand.overlap_data) {\n      elements_in_subdomain.insert(overlap_id.id());\n      if (equal_within_roundoff(overlap_data, 0.)) {\n        elements_with_zero_data.insert(overlap_id.id());\n      }\n    }\n    const auto is_in_subdomain =\n        [&elements_in_subdomain](const ElementId<Dim>& element_id) {\n          return elements_in_subdomain.find(element_id) !=\n                 elements_in_subdomain.end();\n        };\n    const auto data_is_zero = [&elements_with_zero_data, &is_in_subdomain](\n                                  const ElementId<Dim>& element_id) {\n      return elements_with_zero_data.find(element_id) !=\n                 elements_with_zero_data.end() or\n             // Data outside the subdomain is zero by definition\n             not is_in_subdomain(element_id);\n    };\n    const bool central_data_is_zero = data_is_zero(central_element.id());\n\n    // The subdomain operator essentially does two sweeps over all elements in\n    // the subdomain: In the first sweep it prepares the mortar data and stores\n    // them on both sides of all mortars, and in the second sweep it consumes\n    // the mortar data to apply the operator. This implementation is relatively\n    // simple because it can re-use the implementation for the parallel DG\n    // operator. However, it is also possible to apply the subdomain operator in\n    // a single sweep over all elements, incrementally building up the mortar\n    // data and applying boundary corrections immediately to both adjacent\n    // elements once the data is available. That approach is possibly a\n    // performance optimization but requires re-implementing a lot of logic for\n    // the DG operator here. It should be considered once the subdomain operator\n    // has been identified as the performance bottleneck. An alternative to\n    // optimizing the subdomain operator performance is to precondition the\n    // subdomain solve with a _much_ simpler subdomain operator, such as a\n    // finite-difference Laplacian, so fewer applications of the more expensive\n    // DG subdomain operator are necessary.\n\n    // 1. Prepare mortar data on all elements in the subdomain and store them on\n    //    mortars, reorienting if needed\n    //\n    // Prepare central element\n    const auto apply_boundary_condition_center =\n        [&apply_boundary_condition, &local_central_element = central_element](\n            const Direction<Dim>& local_direction,\n            auto&&... fields_and_fluxes) {\n          apply_boundary_condition(\n              local_central_element.id(), local_direction, std::false_type{},\n              local_direction,\n              std::forward<decltype(fields_and_fluxes)>(fields_and_fluxes)...);\n        };\n    db::apply<prepare_args_tags>(\n        [this, &operand](const auto&... args) {\n          elliptic::dg::prepare_mortar_data<System, linearized>(\n              make_not_null(&central_deriv_vars_),\n              make_not_null(&central_primal_fluxes_),\n              make_not_null(&central_mortar_data_), operand.element_data,\n              args...);\n        },\n        box, temporal_id, apply_boundary_condition_center, fluxes_args,\n        data_is_zero);\n    // Prepare neighbors\n    for (const auto& [direction, neighbors] : central_element.neighbors()) {\n      for (const auto& neighbor_id : neighbors) {\n        const auto& orientation = neighbors.orientation(neighbor_id);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        const LinearSolver::Schwarz::OverlapId<Dim> overlap_id{direction,\n                                                               neighbor_id};\n        const auto& overlap_extent = all_overlap_extents.at(overlap_id);\n        const auto& neighbor = all_neighbor_elements.at(overlap_id);\n        const auto& neighbor_mesh = all_neighbor_meshes.at(overlap_id);\n        const auto& mortar_id = overlap_id;\n        const auto& mortar_mesh = central_mortar_meshes.at(mortar_id);\n        const bool neighbor_data_is_zero = data_is_zero(neighbor_id);\n\n        // Intercept empty overlaps. In the unlikely case that overlaps have\n        // zero extent, meaning no point of the neighbor is part of the\n        // subdomain (which is fairly useless, except for testing), the\n        // subdomain is identical to the central element and no communication\n        // with neighbors is necessary. We can just handle the mortar between\n        // central element and neighbor and continue.\n        if (UNLIKELY(overlap_extent == 0)) {\n          elliptic::dg::BoundaryData<typename System::primal_fields,\n                                     typename System::primal_fluxes>\n              remote_boundary_data{};\n          remote_boundary_data.field_data.initialize(\n              mortar_mesh.number_of_grid_points(), 0.);\n          central_mortar_data_.at(mortar_id).remote_insert(\n              temporal_id, std::move(remote_boundary_data));\n          continue;\n        }\n\n        // Copy the central element's mortar data to the neighbor\n        if (not(central_data_is_zero and neighbor_data_is_zero)) {\n          auto oriented_mortar_data =\n              central_mortar_data_.at(mortar_id).local_data(temporal_id);\n          if (not orientation.is_aligned()) {\n            oriented_mortar_data.orient_on_slice(\n                mortar_mesh.extents(), direction.dimension(), orientation);\n          }\n          neighbors_mortar_data_[overlap_id][::dg::MortarId<Dim>{\n                                                 direction_from_neighbor,\n                                                 central_element.id()}]\n              .remote_insert(temporal_id, std::move(oriented_mortar_data));\n        }\n\n        // Now we switch perspective to the neighbor. First, we extend the\n        // overlap data to the full neighbor mesh by padding it with zeros. This\n        // is necessary because spectral operators such as derivatives require\n        // data on the full mesh.\n        // Possible performance optimization: this copy can be avoided if the\n        // overlap covers the full neighbor, so no padding with zeros is needed.\n        if (not neighbor_data_is_zero) {\n          LinearSolver::Schwarz::extended_overlap_data(\n              make_not_null(&extended_operand_vars_[overlap_id]),\n              operand.overlap_data.at(overlap_id), neighbor_mesh.extents(),\n              overlap_extent, direction_from_neighbor);\n        }\n\n        const auto apply_boundary_condition_neighbor =\n            [&apply_boundary_condition, &local_neighbor_id = neighbor_id,\n             &overlap_id](const Direction<Dim>& local_direction,\n                          auto&&... fields_and_fluxes) {\n              apply_boundary_condition(\n                  local_neighbor_id, local_direction, std::true_type{},\n                  std::forward_as_tuple(overlap_id, local_direction),\n                  std::forward<decltype(fields_and_fluxes)>(\n                      fields_and_fluxes)...);\n            };\n\n        const auto fluxes_args_on_overlap =\n            elliptic::util::apply_at<fluxes_args_tags_overlap,\n                                     args_tags_from_center>(get_items, box,\n                                                            overlap_id);\n\n        elliptic::util::apply_at<prepare_args_tags_overlap,\n                                 args_tags_from_center>(\n            [this, &overlap_id](const auto&... args) {\n              elliptic::dg::prepare_mortar_data<System, linearized>(\n                  make_not_null(&neighbors_deriv_vars_[overlap_id]),\n                  make_not_null(&neighbors_primal_fluxes_[overlap_id]),\n                  make_not_null(&neighbors_mortar_data_[overlap_id]),\n                  extended_operand_vars_[overlap_id], args...);\n            },\n            box, overlap_id, temporal_id, apply_boundary_condition_neighbor,\n            fluxes_args_on_overlap, data_is_zero);\n\n        // Copy this neighbor's mortar data to the other side of the mortars. On\n        // the other side we either have the central element, or another element\n        // that may or may not be part of the subdomain.\n        const auto& neighbor_mortar_meshes =\n            all_neighbor_mortar_meshes.at(overlap_id);\n        for (const auto& neighbor_mortar_id_and_data :\n             neighbors_mortar_data_.at(overlap_id)) {\n          // No structured bindings because capturing these in lambdas doesn't\n          // work until C++20\n          const auto& neighbor_mortar_id = neighbor_mortar_id_and_data.first;\n          const auto& neighbor_mortar_data = neighbor_mortar_id_and_data.second;\n          const auto& neighbor_direction = neighbor_mortar_id.direction();\n          const auto& neighbors_neighbor_id = neighbor_mortar_id.id();\n          // No need to do anything on external boundaries\n          if (neighbors_neighbor_id == ElementId<Dim>::external_boundary_id()) {\n            continue;\n          }\n          const auto& neighbor_orientation =\n              neighbor.neighbors()\n                  .at(neighbor_direction)\n                  .orientation(neighbors_neighbor_id);\n          const auto neighbors_neighbor_direction =\n              neighbor_orientation(neighbor_direction.opposite());\n          const ::dg::MortarId<Dim> mortar_id_from_neighbors_neighbor{\n              neighbors_neighbor_direction, neighbor_id};\n          const auto send_mortar_data =\n              [&neighbor_orientation, &neighbor_mortar_meshes,\n               &neighbor_mortar_data, &neighbor_mortar_id, &neighbor_direction,\n               &neighbor_data_is_zero,\n               &data_is_zero](auto& remote_mortar_data,\n                              const ElementId<Dim>& remote_element_id) {\n                if (neighbor_data_is_zero and data_is_zero(remote_element_id)) {\n                  return;\n                }\n                const auto& neighbor_mortar_mesh =\n                    neighbor_mortar_meshes.at(neighbor_mortar_id);\n                auto oriented_neighbor_mortar_data =\n                    neighbor_mortar_data.local_data(temporal_id);\n                if (not neighbor_orientation.is_aligned()) {\n                  oriented_neighbor_mortar_data.orient_on_slice(\n                      neighbor_mortar_mesh.extents(),\n                      neighbor_direction.dimension(), neighbor_orientation);\n                }\n                remote_mortar_data.remote_insert(\n                    temporal_id, std::move(oriented_neighbor_mortar_data));\n              };\n          if (neighbors_neighbor_id == central_element.id() and\n              mortar_id_from_neighbors_neighbor == mortar_id) {\n            send_mortar_data(central_mortar_data_.at(mortar_id),\n                             central_element.id());\n            continue;\n          }\n          // Determine whether the neighbor's neighbor overlaps with the\n          // subdomain and find its overlap ID if it does.\n          const auto neighbors_neighbor_overlap_id =\n              [&local_all_neighbor_mortar_meshes = all_neighbor_mortar_meshes,\n               &neighbors_neighbor_id, &mortar_id_from_neighbors_neighbor,\n               &is_in_subdomain]()\n              -> std::optional<LinearSolver::Schwarz::OverlapId<Dim>> {\n            if (not is_in_subdomain(neighbors_neighbor_id)) {\n              return std::nullopt;\n            }\n            for (const auto& [local_overlap_id, local_mortar_meshes] :\n                 local_all_neighbor_mortar_meshes) {\n              if (local_overlap_id.id() != neighbors_neighbor_id) {\n                continue;\n              }\n              for (const auto& local_mortar_id_and_mesh : local_mortar_meshes) {\n                if (local_mortar_id_and_mesh.first ==\n                    mortar_id_from_neighbors_neighbor) {\n                  return local_overlap_id;\n                }\n              }\n            }\n            ERROR(\"The neighbor's neighbor \"\n                  << neighbors_neighbor_id\n                  << \" is part of the subdomain, but we didn't find its \"\n                     \"overlap ID. This is a bug, so please file an issue.\");\n          }();\n          if (neighbors_neighbor_overlap_id.has_value()) {\n            // The neighbor's neighbor is part of the subdomain so we copy the\n            // mortar data over. Once the loop is complete we will also have\n            // received mortar data back. At that point, both neighbors have a\n            // copy of each other's mortar data, which is the subject of the\n            // possible optimizations mentioned above. Note that the data may\n            // differ by orientations.\n            send_mortar_data(\n                neighbors_mortar_data_[*neighbors_neighbor_overlap_id]\n                                      [mortar_id_from_neighbors_neighbor],\n                neighbors_neighbor_overlap_id->id());\n          } else if (not neighbor_data_is_zero) {\n            // The neighbor's neighbor does not overlap with the subdomain, so\n            // we don't copy mortar data and also don't expect to receive any.\n            // Instead, we assume the data on it is zero and manufacture\n            // appropriate remote boundary data.\n            const auto& neighbors_neighbor_mortar_mesh =\n                all_neighbors_neighbor_mortar_meshes.at(overlap_id)\n                    .at(neighbor_mortar_id);\n            elliptic::dg::BoundaryData<typename System::primal_fields,\n                                       typename System::primal_fluxes>\n                zero_mortar_data{};\n            zero_mortar_data.field_data.initialize(\n                neighbors_neighbor_mortar_mesh.number_of_grid_points(), 0.);\n            neighbors_mortar_data_.at(overlap_id)\n                .at(neighbor_mortar_id)\n                .remote_insert(temporal_id, std::move(zero_mortar_data));\n          }\n        }  // loop over neighbor's mortars\n      }  // loop over neighbors\n    }  // loop over directions\n\n    // 2. Apply the operator on all elements in the subdomain\n    //\n    // Apply on central element\n    db::apply<apply_args_tags>(\n        [this, &result, &operand](const auto&... args) {\n          elliptic::dg::apply_operator<System, linearized>(\n              make_not_null(&result->element_data),\n              make_not_null(&central_mortar_data_), operand.element_data,\n              central_deriv_vars_, central_primal_fluxes_, args...);\n        },\n        box, temporal_id, fluxes_args_on_faces, sources_args, data_is_zero);\n    // Apply on neighbors\n    for (const auto& [direction, neighbors] : central_element.neighbors()) {\n      for (const auto& neighbor_id : neighbors) {\n        const auto& orientation = neighbors.orientation(neighbor_id);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        const LinearSolver::Schwarz::OverlapId<Dim> overlap_id{direction,\n                                                               neighbor_id};\n        const auto& overlap_extent = all_overlap_extents.at(overlap_id);\n        const auto& neighbor_mesh = all_neighbor_meshes.at(overlap_id);\n\n        if (UNLIKELY(overlap_extent == 0)) {\n          continue;\n        }\n\n        DirectionMap<Dim, FluxesArgs> fluxes_args_on_overlap_faces{};\n        for (const auto& neighbor_direction :\n             Direction<Dim>::all_directions()) {\n          fluxes_args_on_overlap_faces.emplace(\n              neighbor_direction,\n              elliptic::util::apply_at<fluxes_args_tags_overlap_faces,\n                                       args_tags_from_center>(\n                  get_items, box,\n                  std::forward_as_tuple(overlap_id, neighbor_direction)));\n        }\n\n        elliptic::util::apply_at<apply_args_tags_overlap,\n                                 args_tags_from_center>(\n            [this, &overlap_id](const auto&... args) {\n              elliptic::dg::apply_operator<System, linearized>(\n                  make_not_null(&extended_results_[overlap_id]),\n                  make_not_null(&neighbors_mortar_data_.at(overlap_id)),\n                  extended_operand_vars_.at(overlap_id),\n                  neighbors_deriv_vars_.at(overlap_id),\n                  neighbors_primal_fluxes_.at(overlap_id), args...);\n            },\n            box, overlap_id, temporal_id, fluxes_args_on_overlap_faces,\n            elliptic::util::apply_at<sources_args_tags_overlap,\n                                     args_tags_from_center>(get_items, box,\n                                                            overlap_id),\n            data_is_zero);\n\n        // Restrict the extended operator data back to the subdomain, assuming\n        // we can discard any data outside the overlaps. WARNING: This\n        // assumption may break with changes to the DG operator that affect its\n        // sparsity. For example, multiplying the DG operator with the _full_\n        // inverse mass-matrix (\"massless\" scheme with no \"mass-lumping\"\n        // approximation) means that lifted boundary corrections bleed into the\n        // volume.\n        // Possible performance optimization: this copy can be avoided if the\n        // overlap covers the full neighbor, so no restriction is needed.\n        if (UNLIKELY(\n                result->overlap_data[overlap_id].number_of_grid_points() !=\n                operand.overlap_data.at(overlap_id).number_of_grid_points())) {\n          result->overlap_data[overlap_id].initialize(\n              operand.overlap_data.at(overlap_id).number_of_grid_points());\n        }\n        LinearSolver::Schwarz::data_on_overlap(\n            make_not_null(&result->overlap_data[overlap_id]),\n            extended_results_.at(overlap_id), neighbor_mesh.extents(),\n            overlap_extent, direction_from_neighbor);\n      }  // loop over neighbors\n    }  // loop over directions\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n private:\n  // Memory buffers for repeated operator applications\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Variables<\n      db::wrap_tags_in<::Tags::deriv, typename System::primal_fields,\n                       tmpl::size_t<Dim>, Frame::Inertial>>\n      central_deriv_vars_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Variables<typename System::primal_fluxes> central_primal_fluxes_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable LinearSolver::Schwarz::OverlapMap<\n      Dim,\n      Variables<db::wrap_tags_in<::Tags::deriv, typename System::primal_fields,\n                                 tmpl::size_t<Dim>, Frame::Inertial>>>\n      neighbors_deriv_vars_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable LinearSolver::Schwarz::OverlapMap<\n      Dim, Variables<typename System::primal_fluxes>>\n      neighbors_primal_fluxes_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable LinearSolver::Schwarz::OverlapMap<\n      Dim, Variables<typename System::primal_fields>>\n      extended_operand_vars_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable LinearSolver::Schwarz::OverlapMap<\n      Dim, Variables<typename System::primal_fields>>\n      extended_results_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable ::dg::MortarMap<\n      Dim, elliptic::dg::MortarData<size_t, typename System::primal_fields,\n                                    typename System::primal_fluxes>>\n      central_mortar_data_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable LinearSolver::Schwarz::OverlapMap<\n      Dim, ::dg::MortarMap<Dim, elliptic::dg::MortarData<\n                                    size_t, typename System::primal_fields,\n                                    typename System::primal_fluxes>>>\n      neighbors_mortar_data_{};\n};\n\n}  // namespace elliptic::dg::subdomain_operator\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/SubdomainOperator/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n\nnamespace elliptic::dg::subdomain_operator::Tags {\n\n/// The number of points an element-centered subdomain extends into the\n/// neighbor, i.e. the \"extruding\" overlap extents. This tag is used in\n/// conjunction with `LinearSolver::Schwarz::Tags::Overlaps` to describe the\n/// extruding extent into each neighbor.\nstruct ExtrudingExtent : db::SimpleTag {\n  using type = size_t;\n};\n\n/// Data on the neighbor's side of a mortar. Used to store data for elements\n/// that do not overlap with the element-centered subdomain, but play a role\n/// in the DG operator nonetheless.\ntemplate <typename Tag, size_t VolumeDim>\nstruct NeighborMortars : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = DirectionalIdMap<VolumeDim, typename Tag::type>;\n};\n\n}  // namespace elliptic::dg::subdomain_operator::Tags\n"
  },
  {
    "path": "src/Elliptic/DiscontinuousGalerkin/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::dg {\n/// Option tags related to elliptic discontinuous Galerkin schemes\nnamespace OptionTags {\n\nstruct Discretization {\n  static constexpr Options::String help =\n      \"Options for the discretization of the elliptic equations\";\n};\n\nstruct DiscontinuousGalerkin {\n  using group = Discretization;\n  static constexpr Options::String help =\n      \"Options for the discontinuous Galerkin discretization\";\n};\n\nstruct PenaltyParameter {\n  using type = double;\n  static constexpr Options::String help =\n      \"The prefactor to the penalty term of the numerical flux. Values closer \"\n      \"to one lead to better-conditioned problems, but on curved meshes the \"\n      \"penalty parameter may need to be increased to keep the problem \"\n      \"well-defined.\";\n  using group = DiscontinuousGalerkin;\n};\n\nstruct Massive {\n  using type = bool;\n  static constexpr Options::String help =\n      \"Whether or not to multiply the DG operator with the mass matrix. \"\n      \"Massive DG operators can be easier to solve because they are symmetric, \"\n      \"or at least closer to symmetry\";\n  using group = DiscontinuousGalerkin;\n};\n\nstruct Quadrature {\n  using type = Spectral::Quadrature;\n  static constexpr Options::String help =\n      \"The quadrature method used, which determines the distribution of points \"\n      \"on the grid. Gauss-Lobatto points have a point at the boundary and \"\n      \"Gauss points do not. Other quadratures are not currently supported.\";\n  using group = DiscontinuousGalerkin;\n};\n\nstruct Formulation {\n  using type = ::dg::Formulation;\n  static constexpr Options::String help =\n      \"The DG formulation to use (strong or weak).\";\n  using group = DiscontinuousGalerkin;\n};\n\n}  // namespace OptionTags\n\n/// DataBox tags related to elliptic discontinuous Galerkin schemes\nnamespace Tags {\n\n/*!\n * \\brief The prefactor to the penalty term of the numerical flux\n *\n * \\see elliptic::dg::penalty\n */\nstruct PenaltyParameter : db::SimpleTag {\n  using type = double;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::PenaltyParameter>;\n  static double create_from_options(const double value) { return value; }\n};\n\n/*!\n * \\brief The penalty factor in internal penalty fluxes\n *\n * \\see elliptic::dg::penalty\n */\nstruct PenaltyFactor : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// Whether or not to multiply the DG operator with the mass matrix. Massive DG\n/// operators can be easier to solve because they are symmetric, or at least\n/// closer to symmetry.\nstruct Massive : db::SimpleTag {\n  using type = bool;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::Massive>;\n  static bool create_from_options(const bool value) { return value; }\n};\n\n/// The quadrature method used for the elliptic DG discretization\nstruct Quadrature : db::SimpleTag {\n  using type = Spectral::Quadrature;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::Quadrature>;\n  static type create_from_options(const type value) {\n    if (not(value == Spectral::Quadrature::Gauss or\n            value == Spectral::Quadrature::GaussLobatto)) {\n      ERROR_NO_TRACE(\n          \"Choose Gauss or Gauss-Lobatto quadrature for elliptic DG \"\n          \"discretizations.\");\n    }\n    return value;\n  }\n};\n\n/// The DG formulation to use (strong or weak)\nstruct Formulation : db::SimpleTag {\n  using type = ::dg::Formulation;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::Formulation>;\n  static type create_from_options(const type value) { return value; }\n};\n\n}  // namespace Tags\n}  // namespace elliptic::dg\n"
  },
  {
    "path": "src/Elliptic/Executables/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Elasticity)\nadd_subdirectory(Poisson)\nadd_subdirectory(Punctures)\nadd_subdirectory(SelfForce)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "src/Elliptic/Executables/Elasticity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  AmrCriteria\n  Charmxx::main\n  ConstitutiveRelations\n  Convergence\n  CoordinateMaps\n  DiscontinuousGalerkin\n  DomainCreators\n  Elasticity\n  ElasticityActions\n  ElasticityBoundaryConditions\n  ElasticityPointwiseFunctions\n  ElasticitySolutions\n  Elliptic\n  EllipticDg\n  EllipticDgSubdomainOperator\n  EllipticSubdomainPreconditioners\n  Events\n  EventsAndTriggers\n  FunctionsOfTime\n  Informer\n  InitialDataUtilities\n  LinearOperators\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelLinearSolver\n  ParallelMultigrid\n  ParallelSchwarz\n  PhaseControl\n  Utilities\n  )\n\nfunction(add_elasticity_executable DIM)\n  set(EXECUTABLE \"SolveElasticity${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    SolveElasticity.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction(add_elasticity_executable)\n\nadd_elasticity_executable(2)\nadd_elasticity_executable(3)\n"
  },
  {
    "path": "src/Elliptic/Executables/Elasticity/SolveElasticity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Executables/Elasticity/SolveElasticity.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/RegisterDerived.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = Metavariables<DIM>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_derived_classes_with_charm<\n           metavariables::solver::schwarz_smoother::subdomain_solver>,\n       &elliptic::subdomain_preconditioners::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>,\n       &::amr::register_callbacks<metavariables,\n                                  typename metavariables::amr::element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Elliptic/Executables/Elasticity/SolveElasticity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/RunEventsAndTriggers.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Elliptic/Executables/Solver.hpp\"\n#include \"Elliptic/Systems/Elasticity/Actions/InitializeConstitutiveRelation.hpp\"\n#include \"Elliptic/Systems/Elasticity/BoundaryConditions/Factory.hpp\"\n#include \"Elliptic/Systems/Elasticity/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Elliptic/Triggers/Factory.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/Factory.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Factory.hpp\"\n#include \"PointwiseFunctions/Elasticity/PotentialEnergy.hpp\"\n#include \"PointwiseFunctions/Elasticity/Strain.hpp\"\n#include \"PointwiseFunctions/Elasticity/Stress.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/NumericData.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr Options::String help{\n      \"Find the solution to a linear elasticity problem.\"};\n\n  static constexpr size_t volume_dim = Dim;\n  using system = Elasticity::FirstOrderSystem<Dim>;\n  using solver = elliptic::Solver<Metavariables, Dim, system>;\n\n  using analytic_solution_fields = typename system::primal_fields;\n  using error_compute = ::Tags::ErrorsCompute<analytic_solution_fields>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_solution_fields>;\n  using observe_fields = tmpl::append<\n      analytic_solution_fields, error_tags, typename solver::observe_fields,\n      tmpl::list<Elasticity::Tags::StrainCompute<volume_dim>,\n                 Elasticity::Tags::StressCompute<volume_dim>,\n                 Elasticity::Tags::PotentialEnergyDensityCompute<volume_dim>,\n                 domain::Tags::Coordinates<volume_dim, Frame::Inertial>>>;\n  using observer_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 ::Events::Tags::ObserverDetInvJacobianCompute<\n                     Frame::ElementLogical, Frame::Inertial>,\n                 error_compute>;\n\n  // Collect all items to store in the cache.\n  using const_global_cache_tags = tmpl::list<>;\n\n  // Just a name in the input file\n  struct ObserveNormsPerLayer {};\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<elliptic::BoundaryConditions::BoundaryCondition<volume_dim>,\n                   Elasticity::BoundaryConditions::standard_boundary_conditions<\n                       system>>,\n        tmpl::pair<elliptic::analytic_data::Background,\n                   tmpl::push_back<Elasticity::Solutions::\n                                       all_analytic_solutions<volume_dim>,\n                                   elliptic::analytic_data::NumericData>>,\n        tmpl::pair<elliptic::analytic_data::InitialGuess,\n                   tmpl::push_back<Elasticity::Solutions::\n                                       all_analytic_solutions<volume_dim>,\n                                   elliptic::analytic_data::NumericData>>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution,\n                   Elasticity::Solutions::all_analytic_solutions<volume_dim>>,\n        tmpl::pair<\n            Elasticity::ConstitutiveRelations::ConstitutiveRelation<volume_dim>,\n            Elasticity::ConstitutiveRelations::standard_constitutive_relations<\n                volume_dim>>,\n        tmpl::pair<::amr::Criterion,\n                   ::amr::Criteria::standard_criteria<\n                       volume_dim,\n                       tmpl::list<Elasticity::Tags::Displacement<volume_dim>>>>,\n        tmpl::pair<\n            Event,\n            tmpl::flatten<tmpl::list<\n                Events::Completion,\n                dg::Events::field_observations<volume_dim, observe_fields,\n                                               observer_compute_tags,\n                                               ::amr::Tags::IsFinestGrid>,\n                // Observation per material layer\n                ::Events::ObserveNorms<observe_fields, observer_compute_tags,\n                                       Elasticity::Tags::MaterialLayerName,\n                                       ObserveNormsPerLayer>>>>,\n        tmpl::pair<Trigger, elliptic::Triggers::all_triggers<\n                                ::amr::OptionTags::AmrGroup>>,\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<\n                // Phases for AMR\n                PhaseControl::VisitAndReturn<\n                    Parallel::Phase::EvaluateAmrCriteria>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::AdjustDomain>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::UpdateSections>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::CheckDomain>>>>;\n  };\n\n  // Collect all reduction tags for observers\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>,\n          solver>>>;\n\n  using initialization_actions =\n      tmpl::push_back<typename solver::initialization_actions,\n                      Elasticity::Actions::InitializeConstitutiveRelation<Dim>,\n                      Parallel::Actions::TerminatePhase>;\n\n  using register_actions =\n      tmpl::push_back<typename solver::register_actions,\n                      observers::Actions::RegisterEventsWithObservers>;\n\n  using solve_actions = typename solver::template solve_actions<tmpl::list<>>;\n\n  using dg_element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialization_actions>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Register,\n                     tmpl::push_back<register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Restart,\n                     tmpl::push_back<register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<Parallel::Phase::Solve, solve_actions>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::CheckDomain,\n                     tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                Parallel::Actions::TerminatePhase>>>,\n      LinearSolver::multigrid::ElementsAllocator<\n          volume_dim, typename solver::multigrid::options_group>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = tmpl::push_back<\n        typename solver::amr_projectors,\n        Elasticity::Actions::InitializeConstitutiveRelation<Dim>>;\n    static constexpr bool keep_coarse_grids = true;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, register_actions>>;\n  };\n\n  // Specify all parallel components that will execute actions at some point.\n  using component_list = tmpl::flatten<\n      tmpl::list<dg_element_array, typename solver::component_list,\n                 observers::Observer<Metavariables>,\n                 observers::ObserverWriter<Metavariables>>>;\n\n  static constexpr std::array<Parallel::Phase, 6> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::UpdateSections, Parallel::Phase::CheckDomain,\n       Parallel::Phase::Solve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n/// \\endcond\n"
  },
  {
    "path": "src/Elliptic/Executables/Poisson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Charmxx::main\n  AmrCriteria\n  Convergence\n  CoordinateMaps\n  DiscontinuousGalerkin\n  DomainCreators\n  Elliptic\n  EllipticDg\n  EllipticDgSubdomainOperator\n  EllipticSubdomainPreconditioners\n  Events\n  EventsAndTriggers\n  FunctionsOfTime\n  Informer\n  InitialDataUtilities\n  LinearOperators\n  MathFunctions\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelLinearSolver\n  ParallelMultigrid\n  ParallelSchwarz\n  PhaseControl\n  Poisson\n  PoissonBoundaryConditions\n  PoissonSolutions\n  Utilities\n  )\n\nfunction(add_poisson_executable DIM)\n  set(EXECUTABLE \"SolvePoisson${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    SolvePoisson.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction(add_poisson_executable)\n\nadd_poisson_executable(1)\nadd_poisson_executable(2)\nadd_poisson_executable(3)\n"
  },
  {
    "path": "src/Elliptic/Executables/Poisson/SolvePoisson.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Executables/Poisson/SolvePoisson.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/RegisterDerived.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = Metavariables<DIM>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_derived_classes_with_charm<\n           metavariables::solver::schwarz_smoother::subdomain_solver>,\n       &elliptic::subdomain_preconditioners::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>,\n       &::amr::register_callbacks<metavariables,\n                                  typename metavariables::amr::element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Elliptic/Executables/Poisson/SolvePoisson.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/RadiallyCompressedCoordinates.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/RunEventsAndTriggers.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Elliptic/Executables/Solver.hpp\"\n#include \"Elliptic/Systems/Poisson/BoundaryConditions/Factory.hpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Triggers/Factory.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Factory.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/NumericData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr Options::String help{\n      \"Find the solution to a Poisson problem.\"};\n\n  static constexpr size_t volume_dim = Dim;\n  using system =\n      Poisson::FirstOrderSystem<Dim, Poisson::Geometry::FlatCartesian>;\n  using solver = elliptic::Solver<Metavariables, Dim, system>;\n\n  using analytic_solution_fields = typename system::primal_fields;\n  using error_compute = ::Tags::ErrorsCompute<analytic_solution_fields>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_solution_fields>;\n  using observe_fields = tmpl::append<\n      analytic_solution_fields, error_tags, typename solver::observe_fields,\n      tmpl::list<domain::Tags::Coordinates<volume_dim, Frame::Inertial>,\n                 ::Events::Tags::ObserverDetInvJacobianCompute<\n                     Frame::ElementLogical, Frame::Inertial>,\n                 domain::Tags::RadiallyCompressedCoordinatesCompute<\n                     volume_dim, Frame::Inertial>>>;\n  using observer_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 error_compute>;\n\n  // Collect all items to store in the cache.\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::RadiallyCompressedCoordinatesOptions>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<elliptic::analytic_data::Background,\n                   tmpl::push_back<\n                       Poisson::Solutions::all_analytic_solutions<volume_dim>,\n                       elliptic::analytic_data::NumericData>>,\n        tmpl::pair<elliptic::analytic_data::InitialGuess,\n                   tmpl::push_back<\n                       Poisson::Solutions::all_analytic_solutions<volume_dim>,\n                       elliptic::analytic_data::NumericData>>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution,\n                   Poisson::Solutions::all_analytic_solutions<volume_dim>>,\n        tmpl::pair<\n            ::MathFunction<volume_dim, Frame::Inertial>,\n            MathFunctions::all_math_functions<volume_dim, Frame::Inertial>>,\n        tmpl::pair<\n            elliptic::BoundaryConditions::BoundaryCondition<volume_dim>,\n            Poisson::BoundaryConditions::standard_boundary_conditions<system>>,\n        tmpl::pair<\n            ::amr::Criterion,\n            ::amr::Criteria::standard_criteria<\n                volume_dim, tmpl::list<Poisson::Tags::Field<DataVector>>>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, observer_compute_tags,\n                           ::amr::Tags::IsFinestGrid>>>>,\n        tmpl::pair<Trigger, elliptic::Triggers::all_triggers<\n                                ::amr::OptionTags::AmrGroup>>,\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<\n                // Phases for AMR\n                PhaseControl::VisitAndReturn<\n                    Parallel::Phase::EvaluateAmrCriteria>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::AdjustDomain>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::UpdateSections>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::CheckDomain>>>>;\n  };\n\n  // Collect all reduction tags for observers\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>,\n          solver>>>;\n\n  using initialization_actions =\n      tmpl::push_back<typename solver::initialization_actions,\n                      Parallel::Actions::TerminatePhase>;\n\n  using register_actions =\n      tmpl::push_back<typename solver::register_actions,\n                      observers::Actions::RegisterEventsWithObservers>;\n\n  using solve_actions = typename solver::template solve_actions<tmpl::list<>>;\n\n  using dg_element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialization_actions>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Register,\n                     tmpl::push_back<register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Restart,\n                     tmpl::push_back<register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<Parallel::Phase::Solve, solve_actions>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::CheckDomain,\n                     tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                Parallel::Actions::TerminatePhase>>>,\n      LinearSolver::multigrid::ElementsAllocator<\n          volume_dim, typename solver::multigrid::options_group>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = typename solver::amr_projectors;\n    static constexpr bool keep_coarse_grids = true;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, register_actions>>;\n  };\n\n  // Specify all parallel components that will execute actions at some point.\n  using component_list = tmpl::flatten<\n      tmpl::list<dg_element_array, typename solver::component_list,\n                 observers::Observer<Metavariables>,\n                 observers::ObserverWriter<Metavariables>>>;\n\n  static constexpr std::array<Parallel::Phase, 6> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::UpdateSections, Parallel::Phase::CheckDomain,\n       Parallel::Phase::Solve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n/// \\endcond\n"
  },
  {
    "path": "src/Elliptic/Executables/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE SolvePunctures)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  SolvePunctures.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  AmrCriteria\n  Charmxx::main\n  Convergence\n  CoordinateMaps\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainCreators\n  Elliptic\n  EllipticDg\n  EllipticDgSubdomainOperator\n  EllipticSubdomainPreconditioners\n  ErrorHandling\n  Events\n  EventsAndTriggers\n  FunctionsOfTime\n  Informer\n  InitialDataUtilities\n  Initialization\n  LinearOperators\n  MathFunctions\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelLinearSolver\n  ParallelMultigrid\n  ParallelNonlinearSolver\n  ParallelSchwarz\n  PhaseControl\n  Punctures\n  PuncturesAmrCriteria\n  PuncturesAnalyticData\n  PuncturesBoundaryConditions\n  PuncturesPointwiseFunctions\n  PuncturesSolutions\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Executables/Punctures/SolvePunctures.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Executables/Punctures/SolvePunctures.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/RegisterDerived.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_derived_classes_with_charm<\n           Metavariables::solver::schwarz_smoother::subdomain_solver>,\n       &elliptic::subdomain_preconditioners::register_derived_with_charm,\n       &register_factory_classes_with_charm<Metavariables>,\n       &::amr::register_callbacks<Metavariables,\n                                  typename Metavariables::amr::element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Elliptic/Executables/Punctures/SolvePunctures.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/RadiallyCompressedCoordinates.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/RunEventsAndTriggers.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Elliptic/Executables/Solver.hpp\"\n#include \"Elliptic/Systems/Punctures/AmrCriteria/RefineAtPunctures.hpp\"\n#include \"Elliptic/Systems/Punctures/BoundaryConditions/Flatness.hpp\"\n#include \"Elliptic/Systems/Punctures/FirstOrderSystem.hpp\"\n#include \"Elliptic/Triggers/Factory.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Punctures/MultiplePunctures.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Punctures/Flatness.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/NumericData.hpp\"\n#include \"PointwiseFunctions/Punctures/AdmIntegrals.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nstruct Metavariables {\n  static constexpr Options::String help{\"Solve for puncture initial data\"};\n\n  static constexpr size_t volume_dim = 3;\n  using system = Punctures::FirstOrderSystem;\n  using solver = elliptic::Solver<Metavariables, volume_dim, system>;\n\n  using observe_integral_fields =\n      tmpl::list<Punctures::Tags::AdmMassIntegrandCompute>;\n  using observe_fields = tmpl::append<\n      typename system::primal_fields, typename system::background_fields,\n      typename solver::observe_fields, observe_integral_fields,\n      tmpl::list<domain::Tags::Coordinates<volume_dim, Frame::Inertial>,\n                 domain::Tags::RadiallyCompressedCoordinatesCompute<\n                     volume_dim, Frame::Inertial>>>;\n  using observer_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 ::Events::Tags::ObserverDetInvJacobianCompute<\n                     Frame::ElementLogical, Frame::Inertial>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<elliptic::analytic_data::Background,\n                   tmpl::list<Punctures::AnalyticData::MultiplePunctures,\n                              elliptic::analytic_data::NumericData>>,\n        tmpl::pair<elliptic::analytic_data::InitialGuess,\n                   tmpl::list<Punctures::Solutions::Flatness,\n                              elliptic::analytic_data::NumericData>>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution, tmpl::list<>>,\n        tmpl::pair<elliptic::BoundaryConditions::BoundaryCondition<volume_dim>,\n                   tmpl::list<Punctures::BoundaryConditions::Flatness>>,\n        tmpl::pair<\n            ::amr::Criterion,\n            tmpl::push_back<::amr::Criteria::standard_criteria<\n                                volume_dim, tmpl::list<Punctures::Tags::Field>>,\n                            Punctures::AmrCriteria::RefineAtPunctures>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, observer_compute_tags,\n                           ::amr::Tags::IsFinestGrid>>>>,\n        tmpl::pair<Trigger, elliptic::Triggers::all_triggers<\n                                ::amr::OptionTags::AmrGroup>>,\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<\n                PhaseControl::VisitAndReturn<\n                    Parallel::Phase::EvaluateAmrCriteria>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::AdjustDomain>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::UpdateSections>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::CheckDomain>>>>;\n  };\n\n  // Additional items to store in the global cache\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::RadiallyCompressedCoordinatesOptions>;\n\n  // Collect all reduction tags for observers\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::push_back<\n          tmpl::at<factory_creation::factory_classes, Event>, solver>>;\n\n  using initialization_actions =\n      tmpl::push_back<typename solver::initialization_actions,\n                      Parallel::Actions::TerminatePhase>;\n\n  using register_actions =\n      tmpl::push_back<typename solver::register_actions,\n                      observers::Actions::RegisterEventsWithObservers>;\n\n  using solve_actions = typename solver::template solve_actions<tmpl::list<>>;\n\n  using dg_element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialization_actions>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Register,\n                     tmpl::push_back<register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Restart,\n                     tmpl::push_back<register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<Parallel::Phase::Solve, solve_actions>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::CheckDomain,\n                     tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                Parallel::Actions::TerminatePhase>>>,\n      LinearSolver::multigrid::ElementsAllocator<\n          volume_dim, typename solver::multigrid::options_group>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = typename solver::amr_projectors;\n    static constexpr bool keep_coarse_grids = true;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, register_actions>>;\n  };\n\n  // Specify all parallel components that will execute actions at some point.\n  using component_list = tmpl::flatten<\n      tmpl::list<dg_element_array, typename solver::component_list,\n                 observers::Observer<Metavariables>,\n                 observers::ObserverWriter<Metavariables>>>;\n\n  static constexpr std::array<Parallel::Phase, 6> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::UpdateSections, Parallel::Phase::CheckDomain,\n       Parallel::Phase::Solve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n/// \\endcond\n"
  },
  {
    "path": "src/Elliptic/Executables/SelfForce/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(Scalar)\n"
  },
  {
    "path": "src/Elliptic/Executables/SelfForce/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE \"SolveGrSelfForce\")\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  SolveGrSelfForce.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  AmrCriteria\n  Charmxx::main\n  Convergence\n  CoordinateMaps\n  DiscontinuousGalerkin\n  DomainCreators\n  Elliptic\n  EllipticDg\n  EllipticDgSubdomainOperator\n  EllipticSubdomainPreconditioners\n  Events\n  EventsAndTriggers\n  FunctionsOfTime\n  Informer\n  LinearOperators\n  MathFunctions\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelLinearSolver\n  ParallelMultigrid\n  ParallelNonlinearSolver\n  ParallelSchwarz\n  PhaseControl\n  GrSelfForce\n  GrSelfForceAnalyticData\n  GrSelfForceBoundaryConditions\n  )\n"
  },
  {
    "path": "src/Elliptic/Executables/SelfForce/GeneralRelativity/SolveGrSelfForce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Executables/SelfForce/GeneralRelativity/SolveGrSelfForce.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/RegisterDerived.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_derived_classes_with_charm<\n           Metavariables::solver::schwarz_smoother::subdomain_solver>,\n       &elliptic::subdomain_preconditioners::register_derived_with_charm,\n       &register_factory_classes_with_charm<Metavariables>,\n       &::amr::register_callbacks<Metavariables,\n                                  typename Metavariables::amr::element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Elliptic/Executables/SelfForce/GeneralRelativity/SolveGrSelfForce.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/RadiallyCompressedCoordinates.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/RunEventsAndTriggers.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Elliptic/Executables/Solver.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Actions/InitializeEffectiveSource.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Angular.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Tags.hpp\"\n#include \"Elliptic/Triggers/Factory.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nstruct Metavariables {\n  static constexpr Options::String help{\n      \"Find the self force for a point mass by solving a 2D elliptic \"\n      \"problem.\"};\n\n  using system = GrSelfForce::FirstOrderSystem;\n  static constexpr size_t volume_dim = system::volume_dim;\n  using solver = elliptic::Solver<Metavariables, volume_dim, system>;\n\n  using observe_fields = tmpl::append<\n      typename system::primal_fields, typename system::background_fields,\n      tmpl::list<GrSelfForce::Tags::SingularField,\n                 GrSelfForce::Tags::BoyerLindquistRadius>,\n      typename solver::observe_fields,\n      tmpl::list<domain::Tags::Coordinates<volume_dim, Frame::Inertial>>>;\n  using observer_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 ::Events::Tags::ObserverDetInvJacobianCompute<\n                     Frame::ElementLogical, Frame::Inertial>>;\n\n  // Collect all items to store in the cache.\n  using const_global_cache_tags = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>,\n                   tmpl::list<domain::creators::AlignedLattice<2>>>,\n        tmpl::pair<elliptic::analytic_data::Background,\n                   tmpl::list<GrSelfForce::AnalyticData::CircularOrbit>>,\n        tmpl::pair<elliptic::analytic_data::InitialGuess,\n                   tmpl::list<GrSelfForce::AnalyticData::CircularOrbit>>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution, tmpl::list<>>,\n        tmpl::pair<elliptic::BoundaryConditions::BoundaryCondition<volume_dim>,\n                   tmpl::list<GrSelfForce::BoundaryConditions::Angular,\n                              GrSelfForce::BoundaryConditions::Sommerfeld>>,\n        tmpl::pair<::amr::Criterion,\n                   ::amr::Criteria::standard_criteria<\n                       volume_dim, typename system::primal_fields>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, observer_compute_tags,\n                           amr::Tags::IsFinestGrid>>>>,\n        tmpl::pair<Trigger, elliptic::Triggers::all_triggers<\n                                ::amr::OptionTags::AmrGroup>>,\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<\n                // Phases for AMR\n                PhaseControl::VisitAndReturn<\n                    Parallel::Phase::EvaluateAmrCriteria>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::AdjustDomain>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::UpdateSections>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::CheckDomain>>>>;\n  };\n\n  // Collect all reduction tags for observers\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::push_back<\n          tmpl::at<typename factory_creation::factory_classes, Event>, solver>>;\n\n  using initialization_actions =\n      tmpl::push_back<typename solver::initialization_actions,\n                      Parallel::Actions::TerminatePhase>;\n\n  using register_actions =\n      tmpl::push_back<typename solver::register_actions,\n                      observers::Actions::RegisterEventsWithObservers>;\n\n  using solve_actions = typename solver::template solve_actions<tmpl::list<>>;\n\n  using dg_element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<\n          Parallel::PhaseActions<\n              Parallel::Phase::Initialization,\n              tmpl::replace<initialization_actions,\n                            elliptic::Actions::InitializeFixedSources<\n                                system, typename solver::background_tag>,\n                            GrSelfForce::Actions::InitializeEffectiveSource<\n                                system, typename solver::background_tag>>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Register,\n              tmpl::push_back<register_actions,\n                              Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Restart,\n              tmpl::push_back<register_actions,\n                              Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Solve, solve_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::CheckDomain,\n              tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                         Parallel::Actions::TerminatePhase>>>,\n      LinearSolver::multigrid::ElementsAllocator<\n          volume_dim, typename solver::multigrid::options_group>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors =\n        tmpl::replace<typename solver::amr_projectors,\n                      elliptic::Actions::InitializeFixedSources<\n                          system, typename solver::background_tag>,\n                      GrSelfForce::Actions::InitializeEffectiveSource<\n                          system, typename solver::background_tag>>;\n    static constexpr bool keep_coarse_grids = true;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, register_actions>>;\n  };\n\n  // Specify all parallel components that will execute actions at some point.\n  using component_list = tmpl::flatten<\n      tmpl::list<dg_element_array, typename solver::component_list,\n                 observers::Observer<Metavariables>,\n                 observers::ObserverWriter<Metavariables>>>;\n\n  static constexpr std::array<Parallel::Phase, 6> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::UpdateSections, Parallel::Phase::CheckDomain,\n       Parallel::Phase::Solve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n/// \\endcond\n"
  },
  {
    "path": "src/Elliptic/Executables/SelfForce/Scalar/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE \"SolveScalarSelfForce\")\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  SolveScalarSelfForce.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  AmrCriteria\n  Charmxx::main\n  Convergence\n  CoordinateMaps\n  DiscontinuousGalerkin\n  DomainCreators\n  Elliptic\n  EllipticDg\n  EllipticDgSubdomainOperator\n  EllipticSubdomainPreconditioners\n  Events\n  EventsAndTriggers\n  FunctionsOfTime\n  Informer\n  LinearOperators\n  MathFunctions\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelLinearSolver\n  ParallelMultigrid\n  ParallelNonlinearSolver\n  ParallelSchwarz\n  PhaseControl\n  ScalarSelfForce\n  ScalarSelfForceActions\n  ScalarSelfForceAmrCriteria\n  ScalarSelfForceAnalyticData\n  ScalarSelfForceBoundaryConditions\n  ScalarSelfForceEvents\n  )\n"
  },
  {
    "path": "src/Elliptic/Executables/SelfForce/Scalar/SolveScalarSelfForce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Executables/SelfForce/Scalar/SolveScalarSelfForce.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/RegisterDerived.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_derived_classes_with_charm<\n           Metavariables::solver::schwarz_smoother::subdomain_solver>,\n       &elliptic::subdomain_preconditioners::register_derived_with_charm,\n       &register_factory_classes_with_charm<Metavariables>,\n       &::amr::register_callbacks<Metavariables,\n                                  typename Metavariables::amr::element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Elliptic/Executables/SelfForce/Scalar/SolveScalarSelfForce.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/RadiallyCompressedCoordinates.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/RunEventsAndTriggers.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Elliptic/Executables/Solver.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Actions/InitializeEffectiveSource.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AmrCriteria/RefineAtPuncture.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/None.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Events/ObserveSelfForce.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"Elliptic/Triggers/Factory.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nstruct Metavariables {\n  static constexpr Options::String help{\n      \"Find the self-force for a scalar charge by solving a 2D elliptic \"\n      \"problem.\"};\n\n  static constexpr size_t volume_dim = 2;\n  using system = ScalarSelfForce::FirstOrderSystem;\n  using solver = elliptic::Solver<Metavariables, volume_dim, system>;\n\n  using observe_fields = tmpl::append<\n      typename system::primal_fields, typename system::background_fields,\n      tmpl::list<ScalarSelfForce::Tags::SingularField,\n                 ScalarSelfForce::Tags::BoyerLindquistRadius>,\n      typename solver::observe_fields,\n      tmpl::list<domain::Tags::Coordinates<volume_dim, Frame::Inertial>>>;\n  using observer_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 ::Events::Tags::ObserverDetInvJacobianCompute<\n                     Frame::ElementLogical, Frame::Inertial>>;\n\n  // Collect all items to store in the cache.\n  using const_global_cache_tags = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>,\n                   tmpl::list<domain::creators::AlignedLattice<2>>>,\n        tmpl::pair<elliptic::analytic_data::Background,\n                   tmpl::list<ScalarSelfForce::AnalyticData::CircularOrbit>>,\n        tmpl::pair<elliptic::analytic_data::InitialGuess,\n                   tmpl::list<ScalarSelfForce::AnalyticData::CircularOrbit>>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution, tmpl::list<>>,\n        tmpl::pair<elliptic::BoundaryConditions::BoundaryCondition<volume_dim>,\n                   tmpl::list<ScalarSelfForce::BoundaryConditions::None,\n                              ScalarSelfForce::BoundaryConditions::Sommerfeld>>,\n        tmpl::pair<\n            ::amr::Criterion,\n            tmpl::push_back<::amr::Criteria::standard_criteria<\n                                volume_dim, typename system::primal_fields>,\n                            ScalarSelfForce::AmrCriteria::RefineAtPuncture>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       ScalarSelfForce::Events::ObserveSelfForce<\n                           amr::Tags::IsFinestGrid>,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, observer_compute_tags,\n                           amr::Tags::IsFinestGrid>>>>,\n        tmpl::pair<Trigger, elliptic::Triggers::all_triggers<\n                                ::amr::OptionTags::AmrGroup>>,\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<\n                // Phases for AMR\n                PhaseControl::VisitAndReturn<\n                    Parallel::Phase::EvaluateAmrCriteria>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::AdjustDomain>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::UpdateSections>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::CheckDomain>>>>;\n  };\n\n  // Collect all reduction tags for observers\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::push_back<\n          tmpl::at<typename factory_creation::factory_classes, Event>, solver>>;\n\n  using initialization_actions =\n      tmpl::push_back<typename solver::initialization_actions,\n                      Parallel::Actions::TerminatePhase>;\n\n  using register_actions =\n      tmpl::push_back<typename solver::register_actions,\n                      observers::Actions::RegisterEventsWithObservers>;\n\n  using solve_actions = typename solver::template solve_actions<tmpl::list<>>;\n\n  using dg_element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<\n          Parallel::PhaseActions<\n              Parallel::Phase::Initialization,\n              tmpl::replace<initialization_actions,\n                            elliptic::Actions::InitializeFixedSources<\n                                system, typename solver::background_tag>,\n                            ScalarSelfForce::Actions::InitializeEffectiveSource<\n                                system, typename solver::background_tag>>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Register,\n              tmpl::push_back<register_actions,\n                              Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Restart,\n              tmpl::push_back<register_actions,\n                              Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Solve, solve_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::CheckDomain,\n              tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                         Parallel::Actions::TerminatePhase>>>,\n      LinearSolver::multigrid::ElementsAllocator<\n          volume_dim, typename solver::multigrid::options_group>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors =\n        tmpl::replace<typename solver::amr_projectors,\n                      elliptic::Actions::InitializeFixedSources<\n                          system, typename solver::background_tag>,\n                      ScalarSelfForce::Actions::InitializeEffectiveSource<\n                          system, typename solver::background_tag>>;\n    static constexpr bool keep_coarse_grids = true;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, register_actions>>;\n  };\n\n  // Specify all parallel components that will execute actions at some point.\n  using component_list = tmpl::flatten<\n      tmpl::list<dg_element_array, typename solver::component_list,\n                 observers::Observer<Metavariables>,\n                 observers::ObserverWriter<Metavariables>>>;\n\n  static constexpr std::array<Parallel::Phase, 6> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::UpdateSections, Parallel::Phase::CheckDomain,\n       Parallel::Phase::Solve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n/// \\endcond\n"
  },
  {
    "path": "src/Elliptic/Executables/Solver.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/Actions/InitializeAnalyticSolution.hpp\"\n#include \"Elliptic/Actions/InitializeFields.hpp\"\n#include \"Elliptic/Actions/InitializeFixedSources.hpp\"\n#include \"Elliptic/Actions/RunEventsAndTriggers.hpp\"\n#include \"Elliptic/Amr/Actions.hpp\"\n#include \"Elliptic/BoundaryConditions/Tags/BoundaryFields.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/ApplyOperator.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/InitializeSubdomain.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/SubdomainOperator.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/MinusLaplacian.hpp\"\n#include \"Elliptic/Systems/GetSourcesComputer.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/RandomizeVariables.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/ElementsRegistration.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Variables.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Actions/BuildMatrix.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Actions/MakeIdentityIfSkipped.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Actions/RestrictFields.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Multigrid.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Actions/CommunicateOverlapFields.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Actions/ResetSubdomainSolver.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Schwarz.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/NewtonRaphson.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Items related to composing nonlinear elliptic solver executables\nnamespace elliptic {\n\n/// Option tags for nonlinear elliptic solver executables\nnamespace OptionTags {\n\nstruct NonlinearSolverGroup {\n  static std::string name() { return \"NonlinearSolver\"; }\n  static constexpr Options::String help = \"The iterative nonlinear solver\";\n};\nstruct NewtonRaphsonGroup {\n  static std::string name() { return \"NewtonRaphson\"; }\n  static constexpr Options::String help =\n      \"Options for the Newton-Raphson nonlinear solver\";\n  using group = NonlinearSolverGroup;\n};\nstruct LinearSolverGroup {\n  static std::string name() { return \"LinearSolver\"; }\n  static constexpr Options::String help =\n      \"The iterative Krylov-subspace linear solver\";\n};\nstruct GmresGroup {\n  static std::string name() { return \"Gmres\"; }\n  static constexpr Options::String help = \"Options for the GMRES linear solver\";\n  using group = LinearSolverGroup;\n};\nstruct SchwarzSmootherGroup {\n  static std::string name() { return \"SchwarzSmoother\"; }\n  static constexpr Options::String help = \"Options for the Schwarz smoother\";\n  using group = LinearSolverGroup;\n};\nstruct MultigridGroup {\n  static std::string name() { return \"Multigrid\"; }\n  static constexpr Options::String help = \"Options for the multigrid\";\n  using group = LinearSolverGroup;\n};\n\n}  // namespace OptionTags\n\n/*!\n * \\brief A complete nonlinear elliptic solver stack. Use to compose an\n * executable.\n *\n * The elliptic solver stack is described in detail in \\cite Vu2021coj.\n *\n * Uses `Metavariables` to instantiate parallel components.\n */\ntemplate <typename Metavariables, size_t Dim, typename System>\nstruct Solver {\n  static constexpr size_t volume_dim = Dim;\n  using system = System;\n  static_assert(\n      tt::assert_conforms_to_v<system, elliptic::protocols::FirstOrderSystem>);\n  static constexpr bool is_linear =\n      std::is_same_v<elliptic::get_sources_computer<system, true>,\n                     typename system::sources_computer>;\n\n  using background_tag =\n      elliptic::Tags::Background<elliptic::analytic_data::Background>;\n  using initial_guess_tag =\n      elliptic::Tags::InitialGuess<elliptic::analytic_data::InitialGuess>;\n\n  /// These are the fields we solve for\n  using fields_tag = ::Tags::Variables<typename system::primal_fields>;\n  /// These are the fluxes corresponding to the fields, i.e. essentially their\n  /// first derivatives. These are background fields for the linearized sources.\n  using fluxes_tag = ::Tags::Variables<typename system::primal_fluxes>;\n  /// These are the fixed sources, i.e. the RHS of the equations\n  using fixed_sources_tag = db::add_tag_prefix<::Tags::FixedSource, fields_tag>;\n  using operator_applied_to_fields_tag = tmpl::conditional_t<\n      is_linear,\n      // This is the linear operator applied to the fields. We only use it to\n      // apply the operator to the initial guess, so an optimization would be to\n      // re-use the `operator_applied_to_vars_tag` below. This optimization\n      // needs a few minor changes to the parallel linear solver algorithm.\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>,\n      db::add_tag_prefix<NonlinearSolver::Tags::OperatorAppliedTo, fields_tag>>;\n\n  // For AMR iterations\n  using amr_iteration_id =\n      Convergence::Tags::IterationId<::amr::OptionTags::AmrGroup>;\n\n  /// The nonlinear solver algorithm\n  using nonlinear_solver = NonlinearSolver::newton_raphson::NewtonRaphson<\n      Metavariables, fields_tag, OptionTags::NewtonRaphsonGroup,\n      fixed_sources_tag, ::amr::Tags::IsFinestGrid>;\n  using nonlinear_solver_iteration_id =\n      Convergence::Tags::IterationId<typename nonlinear_solver::options_group>;\n\n  /// The linear solver algorithm. We use GMRES since the operator is not\n  /// necessarily symmetric. Using CG here would be an optimization for\n  /// symmetric problems.\n  using linear_solver = LinearSolver::gmres::Gmres<\n      Metavariables,\n      tmpl::conditional_t<is_linear, fields_tag,\n                          typename nonlinear_solver::linear_solver_fields_tag>,\n      OptionTags::GmresGroup, true,\n      tmpl::conditional_t<is_linear, fixed_sources_tag,\n                          typename nonlinear_solver::linear_solver_source_tag>,\n      ::amr::Tags::IsFinestGrid>;\n  using linear_solver_iteration_id =\n      Convergence::Tags::IterationId<typename linear_solver::options_group>;\n\n  /// Precondition each linear solver iteration with a multigrid V-cycle\n  using multigrid = LinearSolver::multigrid::Multigrid<\n      Metavariables, volume_dim, typename linear_solver::operand_tag,\n      OptionTags::MultigridGroup, elliptic::dg::Tags::Massive,\n      typename linear_solver::preconditioner_source_tag>;\n\n  /// Smooth each multigrid level with a number of Schwarz smoothing steps\n  using subdomain_operator =\n      elliptic::dg::subdomain_operator::SubdomainOperator<\n          system, OptionTags::SchwarzSmootherGroup>;\n  using subdomain_preconditioners = tmpl::list<\n      elliptic::subdomain_preconditioners::Registrars::MinusLaplacian<\n          volume_dim, OptionTags::SchwarzSmootherGroup>>;\n  using schwarz_smoother = LinearSolver::Schwarz::Schwarz<\n      typename multigrid::smooth_fields_tag, OptionTags::SchwarzSmootherGroup,\n      subdomain_operator, subdomain_preconditioners,\n      typename multigrid::smooth_source_tag, ::amr::Tags::GridIndex>;\n\n  /// For the GMRES linear solver we need to apply the DG operator to its\n  /// internal \"operand\" in every iteration of the algorithm.\n  using vars_tag = typename linear_solver::operand_tag;\n  using operator_applied_to_vars_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, vars_tag>;\n  /// The correction fluxes can be stored in an arbitrary tag. We don't need to\n  /// access them anywhere, they're just a memory buffer for the linearized\n  /// operator.\n  using fluxes_vars_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Correction, fluxes_tag>;\n\n  /// Fields that may be observed to monitor the state of the solver\n  using observe_fields = tmpl::conditional_t<\n      is_linear,\n      tmpl::append<\n          typename fixed_sources_tag::tags_list,\n          typename db::add_tag_prefix<LinearSolver::Tags::Operand,\n                                      fields_tag>::tags_list,\n          typename db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo,\n                                      fields_tag>::tags_list>,\n      tmpl::append<\n          typename fixed_sources_tag::tags_list,\n          typename nonlinear_solver::linear_solver_fields_tag::tags_list,\n          typename nonlinear_solver::linear_solver_source_tag::tags_list>>;\n\n  /// Collect all reduction tags for observers\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::conditional_t<is_linear, tmpl::list<>, nonlinear_solver>,\n          linear_solver, multigrid, schwarz_smoother>>>;\n\n  template <bool Linearized>\n  using dg_operator = elliptic::dg::Actions::DgOperator<\n      system, Linearized, tmpl::conditional_t<Linearized, vars_tag, fields_tag>,\n      tmpl::conditional_t<Linearized, fluxes_vars_tag, fluxes_tag>,\n      tmpl::conditional_t<Linearized, operator_applied_to_vars_tag,\n                          operator_applied_to_fields_tag>>;\n\n  using build_matrix = LinearSolver::Actions::BuildMatrix<\n      typename multigrid::smooth_fields_tag,\n      typename multigrid::smooth_source_tag, vars_tag,\n      operator_applied_to_vars_tag,\n      domain::Tags::Coordinates<volume_dim, Frame::Inertial>,\n      ::amr::Tags::GridIndex>;\n\n  using build_matrix_actions = typename build_matrix::template actions<\n      typename dg_operator<true>::apply_actions>;\n\n  // For labeling the yaml option for RandomizeVariables\n  struct RandomizeInitialGuess {};\n\n  using init_analytic_solution_action =\n      elliptic::Actions::InitializeOptionalAnalyticSolution<\n          volume_dim, background_tag,\n          tmpl::append<typename system::primal_fields,\n                       typename system::primal_fluxes>,\n          elliptic::analytic_data::AnalyticSolution>;\n\n  using initialization_actions = tmpl::list<\n      elliptic::dg::Actions::InitializeDomain<volume_dim>,\n      tmpl::conditional_t<is_linear, tmpl::list<>,\n                          typename nonlinear_solver::initialize_element>,\n      typename linear_solver::initialize_element,\n      typename multigrid::initialize_element,\n      typename schwarz_smoother::initialize_element,\n      Initialization::Actions::InitializeItems<\n          ::amr::Initialization::Initialize<volume_dim, Metavariables>,\n          elliptic::amr::Actions::Initialize>,\n      elliptic::Actions::InitializeFields<system, initial_guess_tag>,\n      ::Actions::RandomizeVariables<fields_tag, RandomizeInitialGuess>,\n      init_analytic_solution_action,\n      elliptic::dg::Actions::initialize_operator<system, background_tag>,\n      // Actions below may use faces and normals\n      elliptic::Actions::InitializeFixedSources<system, background_tag>,\n      tmpl::conditional_t<is_linear,\n                          tmpl::list<::Actions::MutateApply<\n                              elliptic::dg::Actions::\n                                  ImposeInhomogeneousBoundaryConditionsOnSource<\n                                      system, fixed_sources_tag>>>,\n                          tmpl::list<>>,\n      tmpl::conditional_t<\n          is_linear, tmpl::list<>,\n          ::Initialization::Actions::AddComputeTags<tmpl::list<\n              // For linearized boundary conditions\n              elliptic::Tags::BoundaryFieldsCompute<volume_dim, fields_tag>,\n              elliptic::Tags::BoundaryFluxesCompute<volume_dim, fields_tag,\n                                                    fluxes_tag>>>>>;\n\n  using register_actions = tmpl::flatten<tmpl::list<\n      ::amr::Actions::RegisterElement,\n      tmpl::conditional_t<is_linear, tmpl::list<>,\n                          typename nonlinear_solver::register_element>,\n      typename multigrid::register_element,\n      typename schwarz_smoother::register_element,\n      typename build_matrix::register_actions>>;\n\n  template <typename Label>\n  using smooth_actions = typename schwarz_smoother::template solve<\n      typename dg_operator<true>::apply_actions, Label>;\n\n  // These tags are communicated on subdomain overlaps to initialize the\n  // subdomain geometry. AMR updates these tags, so we have to communicate them\n  // after each AMR step.\n  using subdomain_init_tags =\n      tmpl::list<domain::Tags::Mesh<volume_dim>,\n                 domain::Tags::Element<volume_dim>,\n                 domain::Tags::NeighborMesh<volume_dim>>;\n\n  /// This data needs to be communicated on subdomain overlap regions\n  using communicated_overlap_tags = tmpl::flatten<tmpl::list<\n      // For linearized sources\n      fields_tag, fluxes_tag,\n      // For linearized boundary conditions\n      domain::make_faces_tags<volume_dim, typename system::primal_fields>,\n      domain::make_faces_tags<\n          volume_dim, db::wrap_tags_in<::Tags::NormalDotFlux,\n                                       typename system::primal_fields>>>>;\n\n  using init_subdomain_action =\n      elliptic::dg::subdomain_operator::Actions::InitializeSubdomain<\n          system, background_tag, typename schwarz_smoother::options_group,\n          false>;\n\n  template <typename StepActions>\n  using linear_solve_actions = typename linear_solver::template solve<\n      tmpl::list<\n          // Multigrid preconditioning\n          typename multigrid::template solve<\n              typename dg_operator<true>::apply_actions,\n              // Schwarz smoothing on each multigrid level\n              smooth_actions<LinearSolver::multigrid::VcycleDownLabel>,\n              smooth_actions<LinearSolver::multigrid::VcycleUpLabel>,\n              build_matrix_actions>,\n          // Support disabling the preconditioner\n          ::LinearSolver::Actions::make_identity_if_skipped<\n              multigrid, typename dg_operator<true>::apply_actions>>,\n      StepActions>;\n\n  template <typename StepActions>\n  using nonlinear_solve_actions = typename nonlinear_solver::template solve<\n      typename dg_operator<false>::apply_actions,\n      tmpl::list<\n          // Transfer data down the multigrid hierachy\n          LinearSolver::multigrid::Actions::ReceiveFieldsFromFinerGrid<\n              volume_dim, tmpl::list<fields_tag, fluxes_tag>,\n              typename multigrid::options_group>,\n          LinearSolver::multigrid::Actions::SendFieldsToCoarserGrid<\n              tmpl::list<fields_tag, fluxes_tag>,\n              typename multigrid::options_group, void>,\n          // Communicate data on subdomain overlap regions\n          LinearSolver::Schwarz::Actions::SendOverlapFields<\n              communicated_overlap_tags,\n              typename schwarz_smoother::options_group, false,\n              nonlinear_solver_iteration_id>,\n          LinearSolver::Schwarz::Actions::ReceiveOverlapFields<\n              volume_dim, communicated_overlap_tags,\n              typename schwarz_smoother::options_group, false,\n              nonlinear_solver_iteration_id>,\n          // Reset Schwarz subdomain solver\n          LinearSolver::Schwarz::Actions::ResetSubdomainSolver<\n              typename schwarz_smoother::subdomain_solver,\n              typename schwarz_smoother::options_group>,\n          // Reset explicitly built matrix\n          typename build_matrix::reset_actions,\n          // Linear solve for correction\n          linear_solve_actions<tmpl::list<>>>,\n      StepActions>;\n\n  template <typename StepActions>\n  using solve_actions = tmpl::flatten<tmpl::list<\n      // Observe initial state\n      elliptic::Actions::RunEventsAndTriggers<amr_iteration_id>,\n      // Stop AMR iterations if complete\n      elliptic::amr::Actions::StopAmr,\n      // Run AMR or other phase changes if requested\n      PhaseControl::Actions::ExecutePhaseChange, StepActions,\n      // Increment AMR iteration ID\n      elliptic::amr::Actions::IncrementIterationId,\n      // Communicate subdomain geometry and initialize subdomain to account for\n      // domain changes\n      LinearSolver::Schwarz::Actions::SendOverlapFields<\n          subdomain_init_tags, typename schwarz_smoother::options_group, false,\n          amr_iteration_id>,\n      LinearSolver::Schwarz::Actions::ReceiveOverlapFields<\n          volume_dim, subdomain_init_tags,\n          typename schwarz_smoother::options_group, false, amr_iteration_id>,\n      init_subdomain_action,\n      // Linear or nonlinear solve\n      tmpl::conditional_t<is_linear,\n                          // Linear solve\n                          tmpl::list<\n                              // Apply the DG operator to the initial guess\n                              typename elliptic::dg::Actions::DgOperator<\n                                  system, true, fields_tag, fluxes_vars_tag,\n                                  operator_applied_to_fields_tag, vars_tag,\n                                  fluxes_vars_tag>::apply_actions,\n                              // Krylov solve\n                              linear_solve_actions<tmpl::list<>>>,\n                          // Nonlinear solve\n                          nonlinear_solve_actions<tmpl::list<>>>>>;\n\n  template <typename Tag>\n  using overlaps_tag =\n      LinearSolver::Schwarz::Tags::Overlaps<Tag, volume_dim,\n                                            OptionTags::SchwarzSmootherGroup>;\n\n  using amr_projectors = tmpl::flatten<tmpl::list<\n      elliptic::dg::ProjectGeometry<volume_dim>,\n      tmpl::conditional_t<is_linear, tmpl::list<>,\n                          typename nonlinear_solver::amr_projectors>,\n      typename linear_solver::amr_projectors,\n      typename multigrid::amr_projectors,\n      typename schwarz_smoother::amr_projectors,\n      typename build_matrix::amr_projectors,\n      ::amr::projectors::DefaultInitialize<tmpl::append<\n          tmpl::list<\n              domain::Tags::InitialExtents<volume_dim>,\n              domain::Tags::InitialRefinementLevels<volume_dim>,\n              db::add_tag_prefix<::Tags::deriv, fields_tag,\n                                 tmpl::size_t<volume_dim>, Frame::Inertial>>,\n          // Tags communicated on subdomain overlaps. No need to project\n          // these during AMR because they will be communicated.\n          db::wrap_tags_in<\n              overlaps_tag,\n              tmpl::append<subdomain_init_tags,\n                           tmpl::conditional_t<is_linear, tmpl::list<>,\n                                               communicated_overlap_tags>>>,\n          // Tags initialized on subdomains. No need to project these during\n          // AMR because they will get re-initialized after communication.\n          typename init_subdomain_action::simple_tags>>,\n      ::amr::projectors::ProjectVariables<volume_dim, fields_tag>,\n      init_analytic_solution_action,\n      elliptic::dg::Actions::amr_projectors<system, background_tag>,\n      typename dg_operator<true>::amr_projectors,\n      tmpl::conditional_t<is_linear, tmpl::list<>,\n                          typename dg_operator<false>::amr_projectors>,\n      // Actions below may use faces and normals\n      elliptic::Actions::InitializeFixedSources<system, background_tag>,\n      tmpl::conditional_t<\n          is_linear,\n          tmpl::list<elliptic::dg::Actions::\n                         ImposeInhomogeneousBoundaryConditionsOnSource<\n                             system, fixed_sources_tag>>,\n          tmpl::list<>>,\n      elliptic::amr::Actions::Initialize>>;\n\n  using component_list = tmpl::flatten<\n      tmpl::list<tmpl::conditional_t<is_linear, tmpl::list<>,\n                                     typename nonlinear_solver::component_list>,\n                 typename linear_solver::component_list,\n                 typename multigrid::component_list,\n                 typename schwarz_smoother::component_list,\n                 typename build_matrix::template component_list<Metavariables>,\n                 ::amr::Component<Metavariables>>>;\n};\n\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/Executables/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE SolveXcts)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  SolveXcts.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  AmrCriteria\n  Charmxx::main\n  Convergence\n  CoordinateMaps\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainCreators\n  Elliptic\n  EllipticDg\n  EllipticDgSubdomainOperator\n  EllipticSubdomainPreconditioners\n  ErrorHandling\n  Events\n  EventsAndTriggers\n  FunctionsOfTime\n  Hydro\n  Informer\n  InitialDataUtilities\n  Initialization\n  LinearOperators\n  MathFunctions\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelLinearSolver\n  ParallelMultigrid\n  ParallelNonlinearSolver\n  ParallelSchwarz\n  PhaseControl\n  Utilities\n  Xcts\n  XctsAnalyticData\n  XctsBoundaryConditions\n  XctsEvents\n  XctsSolutions\n  )\n"
  },
  {
    "path": "src/Elliptic/Executables/Xcts/SolveXcts.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Executables/Xcts/SolveXcts.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/RegisterDerived.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_derived_classes_with_charm<\n           Metavariables::solver::schwarz_smoother::subdomain_solver>,\n       &elliptic::subdomain_preconditioners::register_derived_with_charm,\n       &EquationsOfState::register_derived_with_charm,\n       &register_factory_classes_with_charm<Metavariables>,\n       &::amr::register_callbacks<Metavariables,\n                                  typename Metavariables::amr::element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Elliptic/Executables/Xcts/SolveXcts.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/RadiallyCompressedCoordinates.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/RunEventsAndTriggers.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Elliptic/Executables/Solver.hpp\"\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/Factory.hpp\"\n#include \"Elliptic/Systems/Xcts/Events/ObserveAdmIntegrals.hpp\"\n#include \"Elliptic/Systems/Xcts/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Xcts/HydroQuantities.hpp\"\n#include \"Elliptic/Triggers/Factory.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/Binary.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/LowerSpatialFourVelocity.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/NumericData.hpp\"\n#include \"PointwiseFunctions/Xcts/SpacetimeQuantities.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  static constexpr int conformal_matter_scale = 0;\n  using system =\n      Xcts::FirstOrderSystem<Xcts::Equations::HamiltonianLapseAndShift,\n                             Xcts::Geometry::Curved, conformal_matter_scale>;\n  using solver = elliptic::Solver<Metavariables, volume_dim, system>;\n\n  static constexpr Options::String help{\n      \"Find the solution to an XCTS problem.\"};\n\n  using analytic_solution_fields = tmpl::append<typename system::primal_fields>;\n  using spacetime_quantities_compute =\n      Xcts::Tags::SpacetimeQuantitiesCompute<tmpl::list<\n          Xcts::Tags::ConformalFactor<DataVector>,\n          Xcts::Tags::LapseTimesConformalFactor<DataVector>,\n          ::Tags::deriv<Xcts::Tags::ConformalFactor<DataVector>,\n                        tmpl::size_t<3>, Frame::Inertial>,\n          gr::Tags::HamiltonianConstraint<DataVector>,\n          gr::Tags::MomentumConstraint<DataVector, 3>,\n          gr::Tags::SpatialMetric<DataVector, 3>,\n          gr::Tags::InverseSpatialMetric<DataVector, 3>,\n          ::Tags::deriv<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                        tmpl::size_t<3>, Frame::Inertial>,\n          gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>,\n          gr::Tags::SpatialRicci<DataVector, 3>, gr::Tags::Lapse<DataVector>,\n          ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        Frame::Inertial>,\n          gr::Tags::Shift<DataVector, 3>,\n          ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>,\n          gr::Tags::ExtrinsicCurvature<DataVector, 3>>>;\n  using hydro_quantities_compute = Xcts::Tags::HydroQuantitiesCompute<\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpecificEnthalpy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>>>;\n  using error_compute = ::Tags::ErrorsCompute<analytic_solution_fields>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_solution_fields>;\n  using observe_fields = tmpl::append<\n      analytic_solution_fields, typename system::background_fields,\n      typename spacetime_quantities_compute::tags_list,\n      typename hydro_quantities_compute::tags_list, error_tags,\n      typename solver::observe_fields,\n      tmpl::list<domain::Tags::Coordinates<volume_dim, Frame::Inertial>,\n                 domain::Tags::RadiallyCompressedCoordinatesCompute<\n                     volume_dim, Frame::Inertial>,\n                 ::Tags::NonEuclideanMagnitude<\n                     Xcts::Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n                     gr::Tags::SpatialMetric<DataVector, 3>>,\n                 hydro::Tags::LowerSpatialFourVelocityCompute>>;\n  using observer_compute_tags = tmpl::list<\n      ::Events::Tags::ObserverMeshCompute<volume_dim>,\n      ::Events::Tags::ObserverDetInvJacobianCompute<Frame::ElementLogical,\n                                                    Frame::Inertial>,\n      spacetime_quantities_compute, hydro_quantities_compute, error_compute>;\n\n  // Collect all items to store in the cache.\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::RadiallyCompressedCoordinatesOptions>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using analytic_solutions_and_data = tmpl::push_back<\n        Xcts::Solutions::all_analytic_solutions,\n        Xcts::AnalyticData::Binary<elliptic::analytic_data::AnalyticSolution,\n                                   Xcts::Solutions::all_analytic_solutions>,\n        elliptic::analytic_data::NumericData>;\n\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<elliptic::analytic_data::Background,\n                   analytic_solutions_and_data>,\n        tmpl::pair<elliptic::analytic_data::InitialGuess,\n                   analytic_solutions_and_data>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution,\n                   Xcts::Solutions::all_analytic_solutions>,\n        tmpl::pair<\n            elliptic::BoundaryConditions::BoundaryCondition<volume_dim>,\n            Xcts::BoundaryConditions::standard_boundary_conditions<system>>,\n        tmpl::pair<::amr::Criterion,\n                   ::amr::Criteria::standard_criteria<\n                       volume_dim, typename system::primal_fields>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       Events::ObserveAdmIntegrals<::amr::Tags::IsFinestGrid>,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, observer_compute_tags,\n                           ::amr::Tags::IsFinestGrid>>>>,\n        tmpl::pair<Trigger, elliptic::Triggers::all_triggers<\n                                ::amr::OptionTags::AmrGroup>>,\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<\n                PhaseControl::VisitAndReturn<\n                    Parallel::Phase::EvaluateAmrCriteria>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::AdjustDomain>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::UpdateSections>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::CheckDomain>>>,\n        tmpl::pair<\n            grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField,\n            grmhd::AnalyticData::InitialMagneticFields::\n                initial_magnetic_fields>>;\n  };\n\n  // Collect all reduction tags for observers\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<factory_creation::factory_classes, Event>, solver>>>;\n\n  using initialization_actions =\n      tmpl::push_back<typename solver::initialization_actions,\n                      Parallel::Actions::TerminatePhase>;\n\n  using register_actions =\n      tmpl::push_back<typename solver::register_actions,\n                      observers::Actions::RegisterEventsWithObservers>;\n\n  using solve_actions = typename solver::template solve_actions<tmpl::list<>>;\n\n  using dg_element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialization_actions>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Register,\n                     tmpl::push_back<register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Restart,\n                     tmpl::push_back<register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<Parallel::Phase::Solve, solve_actions>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::CheckDomain,\n                     tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                Parallel::Actions::TerminatePhase>>>,\n      LinearSolver::multigrid::ElementsAllocator<\n          volume_dim, typename solver::multigrid::options_group>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = typename solver::amr_projectors;\n    static constexpr bool keep_coarse_grids = true;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, register_actions>>;\n  };\n\n  // Specify all parallel components that will execute actions at some point.\n  using component_list = tmpl::flatten<\n      tmpl::list<dg_element_array, typename solver::component_list,\n                 observers::Observer<Metavariables>,\n                 observers::ObserverWriter<Metavariables>>>;\n\n  static constexpr std::array<Parallel::Phase, 6> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::UpdateSections, Parallel::Phase::CheckDomain,\n       Parallel::Phase::Solve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n/// \\endcond\n"
  },
  {
    "path": "src/Elliptic/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FirstOrderSystem.hpp\n  )\n"
  },
  {
    "path": "src/Elliptic/Protocols/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\ref protocols related to elliptic systems\nnamespace elliptic::protocols {\n\nnamespace FirstOrderSystem_detail {\ntemplate <size_t Dim, typename PrimalFields, typename PrimalFluxes>\nstruct test_fields_and_fluxes;\ntemplate <size_t Dim, typename... PrimalFields, typename... PrimalFluxes>\nstruct test_fields_and_fluxes<Dim, tmpl::list<PrimalFields...>,\n                              tmpl::list<PrimalFluxes...>> : std::true_type {\n  static_assert(sizeof...(PrimalFields) == sizeof...(PrimalFluxes),\n                \"The system must have the same number of fields and fluxes.\");\n  static_assert(\n      ((tmpl::size<typename PrimalFluxes::type::index_list>::value ==\n        tmpl::size<typename PrimalFields::type::index_list>::value + 1) and\n       ...) and\n          (std::is_same_v<tmpl::front<typename PrimalFluxes::type::index_list>,\n                          SpatialIndex<Dim, UpLo::Up, Frame::Inertial>> and\n           ...),\n      \"Primal fluxes and primal fields must correspond to each \"\n      \"other. In particular, each primal flux must have one \"\n      \"index more than its corresponding primal field and an upper-spatial \"\n      \"first index.\");\n};\n}  // namespace FirstOrderSystem_detail\n\n/*!\n * \\brief A system of elliptic equations in first-order \"flux\" formulation\n *\n * Classes conforming to this protocol represent a set of elliptic partial\n * differential equations in first-order \"flux\" formulation:\n *\n * \\f{equation}\n * -\\partial_i F^i_\\alpha + S_\\alpha = f_\\alpha(x)\n * \\f}\n *\n * in terms of fluxes \\f$F_\\alpha^i\\f$, sources \\f$S_\\alpha\\f$ and fixed-sources\n * \\f$f_\\alpha(x)\\f$ \\cite Fischer2021voj.\n * It resembles closely formulations of hyperbolic\n * conservation laws but allows the fluxes \\f$F_\\alpha^i\\f$ to be higher-rank\n * tensor fields. The fluxes and sources are functionals of the system\n * variables \\f$u_\\alpha(x)\\f$ and their derivatives. The fixed-sources\n * \\f$f_\\alpha(x)\\f$ are independent of the system variables. See the\n * `Poisson::FirstOrderSystem` and the `Elasticity::FirstOrderSystem` for\n * examples.\n *\n * Note that this formulation has been simplified since \\cite Fischer2021voj :\n * We assume that the fluxes are linear in the fields and their derivatives and\n * removed the notion of \"auxiliary variables\" from the formulation altogether.\n * In the language of \\cite Fischer2021voj we always just choose the partial\n * derivatives of the fields as auxiliary variables.\n *\n * Conforming classes must have these static member variables:\n *\n * - `size_t volume_dim`: The number of spatial dimensions.\n *\n * Conforming classes must have these type aliases:\n *\n * - `primal_fields`: A list of tags representing the primal fields. These are\n *   the fields we solve for, e.g. \\f$u\\f$ for a Poisson equation.\n *   (we may rename this to just \"fields\" since we removed the notion of\n *   \"auxiliary fields\")\n *\n * - `primal_fluxes`: A list of tags representing the primal fluxes\n *   \\f$F_\\alpha^i\\f$. These are typically some linear combination of the\n *   derivatives of the system fields with raised indices, e.g. \\f$v^i = g^{ij}\n *   \\partial_j u\\f$ for a curved-space Poisson equation on a background metric\n *   \\f$g_{ij}\\f$. They must have an upper-spatial first index, because their\n *   divergence defines the elliptic equation.\n *\n * - `background_fields`: A list of tags representing the variable-independent\n *   background fields in the equations. Examples are a background metric,\n *   associated fixed geometry quantities such as Christoffel symbols or the\n *   Ricci scalar, or any other fixed field that determines the problem to be\n *   solved such as matter sources in the Einstein constraint equations.\n *\n * - `inv_metric_tag`: The tag that defines the background geometry, i.e. the\n *   the geometry that the elliptic equations are formulated on. This is the\n *   metric responsible for normalizing one-forms, such as face normals.\n *\n * - `fluxes_computer`: A class that defines the fluxes \\f$F_\\alpha^i\\f$. Must\n *   have an `argument_tags` type alias and an `apply` function that takes these\n *   arguments in this order:\n *\n *   1. The `primal_fluxes` as not-null pointer\n *   2. The `argument_tags`\n *   3. If `is_discontinuous` is `true` (see below):\n *      const ElementId<Dim>& element_id\n *   4. The `primal_fields`\n *   5. The partial derivatives of the `primal_fields`\n *\n *   The function can assume the output buffers are already correctly sized,\n *   but no guarantee is made on the values that the buffers hold at input.\n *\n *   The `fluxes_computer` must also have an `apply` function overload that is\n *   evaluated on faces of DG elements. It computes the same fluxes\n *   \\f$F_\\alpha^i\\f$, but with the field derivatives replaced by the the face\n *   normal times the fields, and with the non-principal (non-derivative) terms\n *   set to zero. Having this separate function is an optimization to take\n *   advantage of the face normal remaining constant throughout the solve, so it\n *   can be \"baked in\" to the flux. The function takes these arguments in this\n *   order:\n *\n *   1. The `primal_fluxes` as not-null pointer\n *   2. The `argument_tags`\n *   3. If `is_discontinuous` is `true` (see below):\n *      const ElementId<Dim>& element_id\n *   4. The `const tnsr::i<DataVector, Dim>& face_normal` ($n_i$)\n *   5. The `const tnsr::I<DataVector, Dim>& face_normal_vector` ($n^i$)\n *   6. The `primal_fields`\n *\n *   The `fluxes_computer` class must also have the following additional\n *   type aliases and static member variables:\n *\n *   - `volume_tags`: the subset of `argument_tags` that will be retrieved\n *     directly from the DataBox, instead of retrieving it from the face of an\n *     element, when fluxes are applied on a face.\n *   - `const_global_cache_tags`: the subset of `argument_tags` that can be\n *     retrieved from _any_ element's DataBox, because they are stored in the\n *     global cache.\n *   - `bool is_trivial`: a boolean indicating whether the fluxes are simply\n *     the spatial metric, as is the case for the Poisson equation. Some\n *     computations can be skipped in this case.\n *   - `bool is_discontinuous`: a boolean indicating whether the fluxes are\n *     potentially discontinuous across element boundaries. This is `true` for\n *     systems where the equations on both sides of the boundary are different,\n *     e.g. elasticity with different materials on either side of the boundary.\n *     An additional `element_id` argument is passed to the `apply` functions in\n *     this case, which identifies on which side of an element boundary the\n *     fluxes are being evaluated.\n *\n * - `sources_computer`: A class that defines the sources \\f$S_\\alpha\\f$. Must\n *   have an `argument_tags` type alias and an `apply` function that adds the\n *   sources to the equations. It takes these arguments in this order:\n *\n *   1. The types of the `primal_fields` as not-null pointer. These are the\n *      primal equations.\n *   2. The `argument_tags`\n *   3. The `primal_fields`\n *   4. The partial derivatives of the `primal_fields`\n *   5. The `primal_fluxes`\n *\n *   The function is expected to _add_ the sources \\f$S_\\alpha\\f$ to the\n *   output buffers. It must also have the following alias:\n *\n *   - `const_global_cache_tags`: the subset of `argument_tags` that can be\n *     retrieved from _any_ element's DataBox, because they are stored in the\n *     global cache.\n *\n *   The `sources_computer` may also be `void`, in which case \\f$S_\\alpha=0\\f$.\n *\n * - `boundary_conditions_base`: A base class representing the supported\n *   boundary conditions. Boundary conditions can be factory-created from this\n *   base class. Currently this should be a specialization of\n *   `elliptic::BoundaryConditions::BoundaryCondition`.\n *\n * - `modify_boundary_data` (optional, can be `void`): A class that can modify\n *   boundary data received from a neighboring element, e.g. to transform from\n *   one variable to another across element boundaries. This can be used to\n *   solve for different variables in some regions of the domain to absorb\n *   singularities. For example, when solving $-\\Delta u = f$ we could define\n *   $u=u_R + u_P$ in some region of the domain with a known (often singular)\n *   field $u_P$ and solve only for the regular field $u_R$ in this region. So,\n *   when receiving boundary data from outside this region, we subtract $u_P$,\n *   and when receiving boundary data from inside this region, we add $u_P$. We\n *   also add $\\Delta u_P$ to the volume fixed sources $f$ inside the\n *   regularized region.\n *\n *   The `modify_boundary_data` must have an `argument_tags` type alias and an\n *   `apply` function that takes these arguments in this order:\n *\n *   1. The primal fields and the normal-dot-fluxes on the mortar as not-null\n *      pointer. These hold the received data from the neighbor and can be\n *      modified. Note: the normal-dot-fluxes have been computed with the\n *      neighbor's normal, so from the perspective of the receiving element they\n *      are $-n_i F^i$ where $n_i$ is the face normal of the receiving element.\n *   2. The `const DirectionalId<Dim>& mortar_id` identifying the mortar.\n *   3. The `argument_tags`.\n *\n *   Currently, modification made by this function must not depend on the\n *   variables, meaning that the modification can only be adding or subtracting\n *   a precomputed field. This is a simplification so the linearized operator is\n *   not modified at all and can be relaxed if needed (then we also need\n *   `modify_boundary_data_linearized`).\n */\nstruct FirstOrderSystem {\n  template <typename ConformingType>\n  struct test {\n    static constexpr size_t volume_dim = ConformingType::volume_dim;\n\n    using primal_fields = typename ConformingType::primal_fields;\n    using primal_fluxes = typename ConformingType::primal_fluxes;\n    static_assert(FirstOrderSystem_detail::test_fields_and_fluxes<\n                  volume_dim, primal_fields, primal_fluxes>::value);\n\n    using background_fields = typename ConformingType::background_fields;\n    static_assert(tt::is_a_v<tmpl::list, background_fields>);\n    using inv_metric_tag = typename ConformingType::inv_metric_tag;\n    static_assert(std::is_same_v<inv_metric_tag, void> or\n                  tmpl::list_contains_v<background_fields, inv_metric_tag>);\n\n    using fluxes_computer = typename ConformingType::fluxes_computer;\n    using sources_computer = typename ConformingType::sources_computer;\n    static_assert(\n        tt::is_a_v<tmpl::list, typename fluxes_computer::argument_tags>);\n\n    using boundary_conditions_base =\n        typename ConformingType::boundary_conditions_base;\n    static_assert(\n        std::is_base_of_v<domain::BoundaryConditions::BoundaryCondition,\n                          boundary_conditions_base>);\n    using modify_boundary_data = typename ConformingType::modify_boundary_data;\n  };\n};\n\n}  // namespace elliptic::protocols\n"
  },
  {
    "path": "src/Elliptic/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  Elliptic\n  PYTHON_FILES\n  __init__.py\n  ReadH5.py\n)\n"
  },
  {
    "path": "src/Elliptic/Python/ReadH5.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom typing import Sequence, Union\n\nimport numpy as np\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5.IterElements import iter_elements\n\n\ndef read_matrix(volfiles: Union[spectre_h5.H5Vol, Sequence[spectre_h5.H5Vol]]):\n    \"\"\"Read matrix representation written by elliptic solver.\n\n    Arguments:\n      volfiles: Open spectre H5 volume files that contain volume data written\n        by the 'BuildMatrix' phase of an elliptic solve.\n\n    Returns: The matrix as a dense numpy array. If memory becomes a concern, we\n      can change this to a sparse matrix.\n    \"\"\"\n    if isinstance(volfiles, spectre_h5.H5Vol):\n        volfiles = [volfiles]\n    # Each observation is a column of the matrix\n    obs_ids = volfiles[0].list_observation_ids()\n    size = len(obs_ids)\n    # Determine dtype (complex or float)\n    all_tensor_components = volfiles[0].list_tensor_components(obs_ids[0])\n    if \"Variable_0\" in all_tensor_components:\n        dtype = float\n    elif \"Re(Variable_0)\" in all_tensor_components:\n        dtype = complex\n    else:\n        raise ValueError(\n            \"Expected 'Variable_0' or 'Re(Variable_0)' in tensor components.\"\n        )\n    # Collect all variables to be able to order them\n    num_vars = len(\n        list(\n            component\n            for component in all_tensor_components\n            if \"Variable_\" in component\n        )\n    )\n    if dtype is complex:\n        num_vars //= 2\n        variables = sum(\n            (\n                [f\"Re(Variable_{i})\", f\"Im(Variable_{i})\"]\n                for i in range(num_vars)\n            ),\n            [],\n        )\n    else:\n        variables = [f\"Variable_{i}\" for i in range(num_vars)]\n    # Collect all element IDs to be able to order them\n    num_points_per_element = {\n        element.id: element.mesh.number_of_grid_points()\n        for element in iter_elements(volfiles, obs_ids=obs_ids[0])\n    }\n    element_ids = sorted(num_points_per_element.keys())\n    slice_start_per_element = {}\n    slice_start = 0\n    for element_id in element_ids:\n        slice_start_per_element[element_id] = slice_start\n        slice_start += num_points_per_element[element_id] * num_vars\n    del num_points_per_element\n    # Fill matrix\n    matrix = np.zeros((size, size), dtype=dtype)\n    for element, tensor_data in iter_elements(\n        volfiles, obs_ids=None, tensor_components=variables\n    ):\n        if dtype is complex:\n            tensor_data = np.array(\n                [\n                    tensor_data[2 * i] + 1j * tensor_data[2 * i + 1]\n                    for i in range(num_vars)\n                ]\n            )\n        # Column of the matrix is the observation ID\n        col = int(element.time)\n        # Slice of the row is determined by ordering of elements\n        slice_length = np.prod(tensor_data.shape)\n        slice_start = slice_start_per_element[element.id]\n        matrix[\n            slice_start : slice_start + slice_length,\n            col,\n        ] = np.concatenate(tensor_data)\n    return matrix\n"
  },
  {
    "path": "src/Elliptic/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "src/Elliptic/SubdomainPreconditioners/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EllipticSubdomainPreconditioners)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  RegisterDerived.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  MinusLaplacian.hpp\n  RegisterDerived.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  LinearSolver\n  Parallel\n  ParallelSchwarz\n  Poisson\n  Serialization\n  Utilities\n  INTERFACE\n  Convergence\n  DataStructures\n  Domain\n  DomainStructure\n  Elliptic\n  EllipticDgSubdomainOperator\n  ErrorHandling\n  Logging\n  Options\n  PoissonBoundaryConditions\n  )\n"
  },
  {
    "path": "src/Elliptic/SubdomainPreconditioners/MinusLaplacian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <map>\n#include <memory>\n#include <optional>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/SubdomainOperator.hpp\"\n#include \"Elliptic/Systems/Poisson/BoundaryConditions/Robin.hpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/ExplicitInverse.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/Gmres.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/LinearSolver.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Linear solvers that approximately invert the\n/// `elliptic::dg::subdomain_operator::SubdomainOperator` to make the Schwarz\n/// subdomain solver converge faster.\n/// \\see LinearSolver::Schwarz::Schwarz\nnamespace elliptic::subdomain_preconditioners {\n\n/// \\cond\ntemplate <size_t Dim, typename OptionsGroup, typename Solver,\n          typename LinearSolverRegistrars>\nstruct MinusLaplacian;\n/// \\endcond\n\nnamespace Registrars {\ntemplate <size_t Dim, typename OptionsGroup,\n          typename Solver = LinearSolver::Serial::LinearSolver<tmpl::list<\n              ::LinearSolver::Serial::Registrars::Gmres<\n                  ::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n                      Dim, tmpl::list<Poisson::Tags::Field<DataVector>>>>,\n              ::LinearSolver::Serial::Registrars::ExplicitInverse<double>>>>\nstruct MinusLaplacian {\n  template <typename LinearSolverRegistrars>\n  using f = subdomain_preconditioners::MinusLaplacian<Dim, OptionsGroup, Solver,\n                                                      LinearSolverRegistrars>;\n};\n}  // namespace Registrars\n\n/*!\n * \\brief Approximate the subdomain operator with a flat-space Laplacian\n * for every tensor component separately.\n *\n * This linear solver applies the `Solver` to every tensor component in\n * turn, approximating the subdomain operator with a flat-space Laplacian.\n * This can be a lot cheaper than solving the\n * full subdomain operator and can provide effective preconditioning for an\n * iterative subdomain solver. The approximation is better the closer the\n * original PDEs are to a set of decoupled flat-space Poisson equations.\n *\n * \\par Boundary conditions\n * At external boundaries we impose homogeneous Dirichlet or Neumann boundary\n * conditions on the Poisson sub-problems. For tensor components and element\n * faces where the original boundary conditions are of Dirichlet-type we choose\n * homogeneous Dirichlet boundary conditions, and for those where the original\n * boundary conditions are of Neumann-type we choose homogeneous Neumann\n * boundary conditions. This means we may end up with more than one distinct\n * Poisson operator on subdomains with external boundaries, one per unique\n * combination of element face and boundary-condition type among the tensor\n * components.\n *\n * \\par Complex variables\n * This preconditioner can also be applied to complex variables, in which case\n * the Laplacian just applies to the real and imaginary part separately.\n *\n * \\tparam Dim Spatial dimension\n * \\tparam OptionsGroup The options group identifying the\n * `LinearSolver::Schwarz::Schwarz` solver that defines the subdomain geometry.\n * \\tparam Solver Any class that provides a `solve` and a `reset` function,\n * but typically a `LinearSolver::Serial::LinearSolver`. The solver will be\n * factory-created from input-file options.\n */\ntemplate <size_t Dim, typename OptionsGroup,\n          typename Solver = LinearSolver::Serial::LinearSolver<tmpl::list<\n              ::LinearSolver::Serial::Registrars::Gmres<\n                  ::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n                      Dim, tmpl::list<Poisson::Tags::Field<DataVector>>>>,\n              ::LinearSolver::Serial::Registrars::ExplicitInverse<double>>>,\n          typename LinearSolverRegistrars =\n              tmpl::list<Registrars::MinusLaplacian<Dim, OptionsGroup, Solver>>>\nclass MinusLaplacian\n    : public LinearSolver::Serial::LinearSolver<LinearSolverRegistrars> {\n private:\n  using Base = LinearSolver::Serial::LinearSolver<LinearSolverRegistrars>;\n  using StoredSolverType = tmpl::conditional_t<std::is_abstract_v<Solver>,\n                                               std::unique_ptr<Solver>, Solver>;\n  // Identifies an external block boundary, for boundary conditions\n  using BoundaryId = std::pair<size_t, Direction<Dim>>;\n\n public:\n  static constexpr size_t volume_dim = Dim;\n  using options_group = OptionsGroup;\n  using poisson_system =\n      Poisson::FirstOrderSystem<Dim, Poisson::Geometry::FlatCartesian>;\n  using BoundaryConditionsBase =\n      typename poisson_system::boundary_conditions_base;\n  using SubdomainOperator = elliptic::dg::subdomain_operator::SubdomainOperator<\n      poisson_system, OptionsGroup,\n      tmpl::list<Poisson::BoundaryConditions::Robin<Dim>>>;\n  using SubdomainData = ::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n      Dim, tmpl::list<Poisson::Tags::Field<DataVector>>>;\n  // Associates Dirichlet or Neumann conditions to every external block\n  // boundary. For every configuration of this type we'll need a separate\n  // solver, which may cache the Poisson operator matrix that imposes the\n  // corresponding boundary conditions. This type is used as key in other maps,\n  // so it must be hashable. `std::unordered_map` isn't supported by\n  // `boost::hash`, but `std::map` is.\n  using BoundaryConditionsSignature =\n      std::map<BoundaryId, elliptic::BoundaryConditionType>;\n  using solver_type = Solver;\n\n  struct SolverOptionTag {\n    static std::string name() { return \"Solver\"; }\n    using type = StoredSolverType;\n    static constexpr Options::String help =\n        \"The linear solver used to invert the Laplace operator. The solver is \"\n        \"shared between tensor components with the same type of boundary \"\n        \"conditions (Dirichlet-type or Neumann-type).\";\n  };\n\n  struct BoundaryConditions {\n    using type = Options::Auto<elliptic::BoundaryConditionType>;\n    static constexpr Options::String help =\n        \"The boundary conditions imposed by the Laplace operator. Specify \"\n        \"'Auto' to choose between homogeneous Dirichlet or Neumann boundary \"\n        \"conditions automatically, based on the configuration of the the full \"\n        \"operator.\";\n  };\n\n  using options = tmpl::list<SolverOptionTag, BoundaryConditions>;\n  static constexpr Options::String help =\n      \"Approximate the linear operator with a Laplace operator \"\n      \"for every tensor component separately.\";\n\n  MinusLaplacian() = default;\n  MinusLaplacian(MinusLaplacian&& /*rhs*/) = default;\n  MinusLaplacian& operator=(MinusLaplacian&& /*rhs*/) = default;\n  ~MinusLaplacian() = default;\n  MinusLaplacian(const MinusLaplacian& rhs)\n      : Base(rhs),\n        solver_(rhs.clone_solver()),\n        boundary_condition_type_(rhs.boundary_condition_type_) {}\n  MinusLaplacian& operator=(const MinusLaplacian& rhs) {\n    Base::operator=(rhs);\n    solver_ = rhs.clone_solver();\n    boundary_condition_type_ = rhs.boundary_condition_type_;\n    return *this;\n  }\n\n  /// \\cond\n  explicit MinusLaplacian(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MinusLaplacian);  // NOLINT\n  /// \\endcond\n\n  MinusLaplacian(\n      StoredSolverType solver,\n      std::optional<elliptic::BoundaryConditionType> boundary_condition_type)\n      : solver_(std::move(solver)),\n        boundary_condition_type_(boundary_condition_type) {}\n\n  const Solver& solver() const {\n    if constexpr (std::is_abstract_v<Solver>) {\n      return *solver_;\n    } else {\n      return solver_;\n    }\n  }\n\n  /// The cached solvers for each unique boundary-condition signature. These are\n  /// only exposed to allow verifying their consistency.\n  const auto& cached_solvers() const { return solvers_; }\n\n  /// Solve the equation \\f$Ax=b\\f$ by approximating \\f$A\\f$ with a Laplace\n  /// operator for every tensor component in \\f$x\\f$.\n  template <typename LinearOperator, typename VarsType, typename SourceType,\n            typename... OperatorArgs>\n  Convergence::HasConverged solve(\n      gsl::not_null<VarsType*> solution, LinearOperator&& linear_operator,\n      const SourceType& source,\n      const std::tuple<OperatorArgs...>& operator_args) const;\n\n  void reset() override {\n    mutable_solver().reset();\n    // These buffers depend on the operator being solved, so they are reset when\n    // the operator changes. The other buffers only hold workspace memory so\n    // they don't need to be reset.\n    bc_signatures_ = std::nullopt;\n    solvers_.clear();\n    boundary_conditions_.clear();\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Base::pup(p);\n    // Serialize object state\n    p | solver_;\n    p | boundary_condition_type_;\n    // Serialize caches that are possibly expensive to re-create\n    p | bc_signatures_;\n    p | solvers_;\n    // Other properties are memory buffers that don't need to be serialized.\n  }\n\n  std::unique_ptr<Base> get_clone() const override {\n    return std::make_unique<MinusLaplacian>(*this);\n  }\n\n private:\n  // For each tensor component, get the set of boundary conditions that should\n  // be imposed by the Laplacian operator on that component, based on the domain\n  // configuration. These are cached for successive solves of the same operator.\n  // An empty list means the subdomain has no external boundaries.\n  template <typename DbTagsList>\n  const std::vector<BoundaryConditionsSignature>&\n  get_cached_boundary_conditions_signatures(\n      const db::DataBox<DbTagsList>& box) const {\n    if (bc_signatures_.has_value()) {\n      // Use cached value\n      return *bc_signatures_;\n    }\n    bc_signatures_ = std::vector<BoundaryConditionsSignature>{};\n    const auto& all_boundary_conditions =\n        db::get<domain::Tags::ExternalBoundaryConditions<Dim>>(box);\n    const auto collect_bc_signatures = [&bc_signatures = *bc_signatures_,\n                                        &override_boundary_condition_type =\n                                            boundary_condition_type_,\n                                        &all_boundary_conditions](\n                                           const BoundaryId& boundary_id) {\n      if (not bc_signatures.empty() and\n          bc_signatures[0].find(boundary_id) != bc_signatures[0].end()) {\n        // We have already handled this block boundary in an earlier\n        // iteration of the loop. This can happend when the domain is\n        // h-refined, or when multiple elements in a subdomain face the\n        // block boundary.\n        return;\n      }\n      // Get the type of boundary condition (Dirichlet or Neumann) for\n      // each tensor component from the domain configuration\n      const auto& [block_id, direction] = boundary_id;\n      const auto original_boundary_condition = dynamic_cast<\n          const elliptic::BoundaryConditions::BoundaryCondition<Dim>*>(\n          all_boundary_conditions.at(block_id).at(direction).get());\n      ASSERT(original_boundary_condition != nullptr,\n             \"The boundary condition in block \"\n                 << block_id << \", direction \" << direction\n                 << \" is not of the expected type \"\n                    \"'elliptic::BoundaryConditions::BoundaryCondition<\"\n                 << Dim << \">'.\");\n      const std::vector<elliptic::BoundaryConditionType> bc_types =\n          original_boundary_condition->boundary_condition_types();\n      const size_t num_components = bc_types.size();\n      if (bc_signatures.empty()) {\n        bc_signatures.resize(num_components);\n      } else {\n        ASSERT(bc_signatures.size() == num_components,\n               \"Unexpected number of boundary-condition types. The \"\n               \"boundary condition in block \"\n                   << block_id << \", direction \" << direction << \" returned \"\n                   << num_components << \" items, but another returned \"\n                   << bc_signatures.size()\n                   << \" (one for each tensor component).\");\n      }\n      for (size_t component = 0; component < num_components; ++component) {\n        bc_signatures[component].emplace(\n            boundary_id,\n            override_boundary_condition_type.value_or(bc_types.at(component)));\n      }\n    };\n    // Collect boundary-condition signatures from all elements in the subdomain\n    const auto& element = db::get<domain::Tags::Element<Dim>>(box);\n    for (const auto& direction : element.external_boundaries()) {\n      collect_bc_signatures({element.id().block_id(), direction});\n    }\n    const auto& overlap_elements =\n        db::get<LinearSolver::Schwarz::Tags::Overlaps<\n            domain::Tags::Element<Dim>, Dim, OptionsGroup>>(box);\n    for (const auto& [overlap_id, overlap_element] : overlap_elements) {\n      (void)overlap_id;\n      for (const auto& direction : overlap_element.external_boundaries()) {\n        collect_bc_signatures({overlap_element.id().block_id(), direction});\n      }\n    }\n    return *bc_signatures_;\n  }\n\n  // For a tensor component with the given boundary-condition configuration, get\n  // the solver and the set of boundary conditions that will be passed to the\n  // Poisson subdomain operator. The solver is cached for successive solves of\n  // the same operator. Caching is very important for performance, since the\n  // solver may build up an explicit matrix representation that is expensive to\n  // re-create.\n  std::pair<const Solver&,\n            const std::unordered_map<BoundaryId, const BoundaryConditionsBase&,\n                                     boost::hash<BoundaryId>>&>\n  get_cached_solver_and_boundary_conditions(\n      const std::vector<BoundaryConditionsSignature>& bc_signatures,\n      const size_t component) const {\n    if (bc_signatures.empty()) {\n      // The subdomain has no external boundaries. We use the original solver.\n      return {solver(), boundary_conditions_[{}]};\n    }\n    // Get the cached solver corresponding to this component's\n    // boundary-condition configuration\n    const auto& bc_signature = bc_signatures.at(component);\n    if (solvers_.find(bc_signature) == solvers_.end()) {\n      solvers_.emplace(bc_signature, clone_solver());\n    }\n    const Solver& cached_solver = [this, &bc_signature]() -> const Solver& {\n      if constexpr (std::is_abstract_v<Solver>) {\n        return *solvers_.at(bc_signature);\n      } else {\n        return solvers_.at(bc_signature);\n      }\n    }();\n    // Get the cached set of boundary conditions for this component\n    if (boundary_conditions_.find(bc_signature) == boundary_conditions_.end()) {\n      auto& bc = boundary_conditions_[bc_signature];\n      for (const auto& [boundary_id, bc_type] : bc_signature) {\n        bc.emplace(boundary_id,\n                   bc_type == elliptic::BoundaryConditionType::Dirichlet\n                       ? dirichlet_bc\n                       : neumann_bc);\n      }\n    }\n    return {cached_solver, boundary_conditions_[bc_signature]};\n  }\n\n  Solver& mutable_solver() {\n    if constexpr (std::is_abstract_v<Solver>) {\n      return *solver_;\n    } else {\n      return solver_;\n    }\n  }\n\n  StoredSolverType clone_solver() const {\n    if constexpr (std::is_abstract_v<Solver>) {\n      return solver_->get_clone();\n    } else {\n      return solver_;\n    }\n  }\n\n  // The option-constructed solver for the separate Poisson problems. It is\n  // cloned for each unique boundary-condition configuration of the tensor\n  // components.\n  StoredSolverType solver_{};\n  std::optional<elliptic::BoundaryConditionType> boundary_condition_type_;\n\n  // These are caches to keep track of the unique boundary-condition\n  // configurations. Each configuration has its own solver, because the solver\n  // may cache the operator to speed up repeated solves.\n  // - The boundary-condition configuration for each tensor component, or an\n  //   empty list if the subdomain has no external boundaries, or `std::nullopt`\n  //   to signal a clean cache.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::optional<std::vector<BoundaryConditionsSignature>>\n      bc_signatures_{};\n  // - A clone of the `solver_` for each unique boundary-condition configuration\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::unordered_map<BoundaryConditionsSignature, StoredSolverType,\n                             boost::hash<BoundaryConditionsSignature>>\n      solvers_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::unordered_map<\n      BoundaryConditionsSignature,\n      std::unordered_map<BoundaryId, const BoundaryConditionsBase&,\n                         boost::hash<BoundaryId>>,\n      boost::hash<BoundaryConditionsSignature>>\n      boundary_conditions_{};\n\n  // These are memory buffers that are kept around for repeated solves. Possible\n  // optimization: Free this memory at appropriate times, e.g. when the element\n  // has completed a full subdomain solve and goes to the background. In some\n  // cases the `subdomain_operator_` is never even used again in subsequent\n  // subdomain solves because it is cached as a matrix (see\n  // LinearSolver::Serial::ExplicitInverse), so we don't need the memory anymore\n  // at all.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable SubdomainOperator subdomain_operator_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable SubdomainData source_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable SubdomainData initial_guess_in_solution_out_{};\n\n  // These boundary condition instances can be re-used for all tensor components\n  // Note that the _linearized_ boundary conditions are applied by the subdomain\n  // operator, so the constant (third argument) is irrelevant.\n  const Poisson::BoundaryConditions::Robin<Dim> dirichlet_bc{1., 0., 0.};\n  const Poisson::BoundaryConditions::Robin<Dim> neumann_bc{0., 1., 0.};\n};\n\nnamespace detail {\ntemplate <size_t Dim, typename LhsTagsList, typename RhsTagsList>\nvoid assign_component(\n    const gsl::not_null<::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n        Dim, LhsTagsList>*>\n        lhs,\n    const ::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n        Dim, RhsTagsList>& rhs,\n    const size_t lhs_component, const size_t rhs_component,\n    // Use real or imaginary part of the lhs or rhs complex value. This is used\n    // to apply the Laplacian to the real or imaginary part separately.\n    const bool lhs_imag = false, const bool rhs_imag = false) {\n  using LhsValueType =\n      typename ::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n          Dim, LhsTagsList>::value_type;\n  // Possible optimization: Once we have non-owning Variables we can use a view\n  // into the rhs here instead of copying.\n  const size_t num_points_element = rhs.element_data.number_of_grid_points();\n  for (size_t i = 0; i < num_points_element; ++i) {\n    auto& lhs_i =\n        lhs->element_data.data()[lhs_component * num_points_element + i];\n    const auto& rhs_i =\n        rhs.element_data.data()[rhs_component * num_points_element + i];\n    const double rhs_i_fundamental = rhs_imag ? imag(rhs_i) : real(rhs_i);\n    if constexpr (std::is_same_v<LhsValueType, std::complex<double>>) {\n      if (lhs_imag) {\n        lhs_i.imag(rhs_i_fundamental);\n      } else {\n        lhs_i.real(rhs_i_fundamental);\n      }\n    } else {\n      lhs_i = rhs_i_fundamental;\n    }\n  }\n  for (const auto& [overlap_id, rhs_data] : rhs.overlap_data) {\n    const size_t num_points_overlap = rhs_data.number_of_grid_points();\n    // The random-access operation is relatively slow because it computes a\n    // hash, so it's important for performance to avoid repeating it in every\n    // iteration of the loop below.\n    auto& lhs_vars = lhs->overlap_data[overlap_id];\n    for (size_t i = 0; i < num_points_overlap; ++i) {\n      auto& lhs_i = lhs_vars.data()[lhs_component * num_points_overlap + i];\n      const auto& rhs_i =\n          rhs_data.data()[rhs_component * num_points_overlap + i];\n      const double rhs_i_fundamental = rhs_imag ? imag(rhs_i) : real(rhs_i);\n      if constexpr (std::is_same_v<LhsValueType, std::complex<double>>) {\n        if (lhs_imag) {\n          lhs_i.imag(rhs_i_fundamental);\n        } else {\n          lhs_i.real(rhs_i_fundamental);\n        }\n      } else {\n        lhs_i = rhs_i_fundamental;\n      }\n    }\n  }\n}\n}  // namespace detail\n\ntemplate <size_t Dim, typename OptionsGroup, typename Solver,\n          typename LinearSolverRegistrars>\ntemplate <typename LinearOperator, typename VarsType, typename SourceType,\n          typename... OperatorArgs>\nConvergence::HasConverged\nMinusLaplacian<Dim, OptionsGroup, Solver, LinearSolverRegistrars>::solve(\n    const gsl::not_null<VarsType*> initial_guess_in_solution_out,\n    LinearOperator&& /*linear_operator*/, const SourceType& source,\n    const std::tuple<OperatorArgs...>& operator_args) const {\n  const auto& box = get<0>(operator_args);\n  // Solve each component of the source variables in turn, assuming the operator\n  // is a Laplacian. For each component we select either homogeneous Dirichlet\n  // or Neumann boundary conditions, based on the type of boundary conditions\n  // imposed by the full operator.\n  static constexpr size_t num_components =\n      VarsType::ElementData::number_of_independent_components;\n  source_.destructive_resize(source);\n  initial_guess_in_solution_out_.destructive_resize(source);\n  const std::vector<BoundaryConditionsSignature>& bc_signatures =\n      get_cached_boundary_conditions_signatures(box);\n  ASSERT(bc_signatures.empty() or bc_signatures.size() == num_components,\n         \"Expected \" << num_components\n                     << \" boundary-condition signatures (one per tensor \"\n                        \"component), but got \"\n                     << bc_signatures.size() << \".\");\n  // Possible optimization: elide when num_components is 1\n  using ValueType = typename VarsType::value_type;\n  constexpr auto complex_components = []() {\n    if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n      return std::array<bool, 2>{{false, true}};\n    } else {\n      return std::array<bool, 1>{{false}};\n    }\n  }();\n  for (size_t component = 0; component < num_components; ++component) {\n    const auto& [solver, boundary_conditions] =\n        get_cached_solver_and_boundary_conditions(bc_signatures, component);\n    // Solve real and imaginary parts separately, if applicable\n    for (const bool imag_component : complex_components) {\n      detail::assign_component(make_not_null(&source_), source, 0, component,\n                               false, imag_component);\n      detail::assign_component(make_not_null(&initial_guess_in_solution_out_),\n                               *initial_guess_in_solution_out, 0, component,\n                               false, imag_component);\n      solver.solve(make_not_null(&initial_guess_in_solution_out_),\n                   subdomain_operator_, source_,\n                   std::forward_as_tuple(box, boundary_conditions));\n      detail::assign_component(initial_guess_in_solution_out,\n                               initial_guess_in_solution_out_, component, 0,\n                               imag_component, false);\n    }\n  }\n  return {0, 0};\n}\n\n/// \\cond\ntemplate <size_t Dim, typename OptionsGroup, typename Solver,\n          typename LinearSolverRegistrars>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID MinusLaplacian<Dim, OptionsGroup, Solver,\n                                 LinearSolverRegistrars>::my_PUP_ID = 0;\n/// \\endcond\n\n}  // namespace elliptic::subdomain_preconditioners\n"
  },
  {
    "path": "src/Elliptic/SubdomainPreconditioners/RegisterDerived.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/SubdomainPreconditioners/RegisterDerived.hpp\"\n\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/ExplicitInverse.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/Gmres.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/LinearSolver.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid register_derived_with_charm_impl() {\n  register_derived_classes_with_charm<\n      LinearSolver::Serial::LinearSolver<tmpl::list<\n          ::LinearSolver::Serial::Registrars::Gmres<\n              ::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n                  Dim, tmpl::list<Poisson::Tags::Field<DataVector>>>>,\n          ::LinearSolver::Serial::Registrars::ExplicitInverse<double>>>>();\n}\n}  // namespace\n\nnamespace elliptic::subdomain_preconditioners {\nvoid register_derived_with_charm() {\n  register_derived_with_charm_impl<1>();\n  register_derived_with_charm_impl<2>();\n  register_derived_with_charm_impl<3>();\n}\n}  // namespace elliptic::subdomain_preconditioners\n"
  },
  {
    "path": "src/Elliptic/SubdomainPreconditioners/RegisterDerived.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace elliptic::subdomain_preconditioners {\nvoid register_derived_with_charm();\n}  // namespace elliptic::subdomain_preconditioners\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/BnsInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Documents the `IrrotationalBns` namespace\n\n#pragma once\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving for irrotational Binary Neutron Star initial\n * data.\n *\n * We solve the equations for the relativistic quasi-equilibrium configuration\n * associated with an object in a quasi-circular orbit around a binary\n * companion.  We assume the compact object is nonspinning, which allows the\n * solution to be written in terms of a \"velocity potential\", which is a\n * function \\f$\\Phi\\f$ such that \\f$D_i \\Phi = \\rho h u_i\\f$, with \\f$ \\rho,\n * h\\f$ the rest mass density and specific enthalpy respectively, and \\f$u_i \\f$\n * the spatial part of the four velocity\n *\n */\nnamespace BnsInitialData {}\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY IrrotationalBnsBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  StarSurface.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  StarSurface.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Elliptic\n  Options\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/Systems/BnsInitialData/BoundaryConditions/StarSurface.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\brief Boundary conditions for binary neutron star initial data.\nnamespace BnsInitialData::BoundaryConditions {\n\ntemplate <typename System>\nusing standard_boundary_conditions =\n    tmpl::list<elliptic::BoundaryConditions::AnalyticSolution<System>,\n               StarSurface>;\n}  // namespace BnsInitialData::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/BoundaryConditions/StarSurface.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/BnsInitialData/BoundaryConditions/StarSurface.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\nnamespace BnsInitialData::BoundaryConditions {\n\nStarSurface::StarSurface(const Options::Context& /*context*/) {}\n\nvoid StarSurface::apply(\n    gsl::not_null<Scalar<DataVector>*> /*velocity_potential*/,\n    gsl::not_null<Scalar<DataVector>*> n_dot_flux_for_potential,\n    const tnsr::i<DataVector, 3>& /*deriv_velocity_potential*/,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3>& rotational_shift,\n    const double euler_enthalpy_constant,\n    const tnsr::i<DataVector, 3>& normal) {\n  tenex::evaluate<>(n_dot_flux_for_potential,\n                    euler_enthalpy_constant / square(lapse()) *\n                        rotational_shift(ti::I) * normal(ti::i));\n}\n// The term on the RHS is constant w.r.t. the variables, so the linearized form\n// of the RHS is zero.\nvoid StarSurface::apply_linearized(\n    gsl::not_null<Scalar<DataVector>*> velocity_potential_correction,\n    gsl::not_null<Scalar<DataVector>*> n_dot_flux_for_potential_correction,\n    const tnsr::i<DataVector, 3>& /*deriv_velocity_potential*/) {\n  set_number_of_grid_points(n_dot_flux_for_potential_correction,\n                            *velocity_potential_correction);\n  get(*n_dot_flux_for_potential_correction) = 0.0;\n}\n\nvoid StarSurface::pup(PUP::er& /*p*/) {}\n\nbool operator==(const StarSurface& /*lhs*/, const StarSurface& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const StarSurface& lhs, const StarSurface& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID StarSurface::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace BnsInitialData::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/BoundaryConditions/StarSurface.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems//BnsInitialData/Tags.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace BnsInitialData::BoundaryConditions {\n\n/*!\n * Impose StarSurface boundary conditions:\n * \\f[\n *   n_i F^i = \\frac{C}{\\alpha^2} B^i n_i.\n * \\f]\n * The boundary condition results from requiring the conservation\n * equations be regular at the surface of the neutron star.  See\n * \\cite BaumgarteShapiro 15.79.\n */\nclass StarSurface : public elliptic::BoundaryConditions::BoundaryCondition<3> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<3>;\n\n public:\n  static constexpr Options::String help =\n      \"StarSurface boundary conditions  n_i F^i = C/square(alpha) B^i n_i .\";\n\n  using options = tmpl::list<>;\n\n  StarSurface() = default;\n  StarSurface(const StarSurface&) = default;\n  StarSurface& operator=(const StarSurface&) = default;\n  StarSurface(StarSurface&&) = default;\n  StarSurface& operator=(StarSurface&&) = default;\n  ~StarSurface() override = default;\n\n  /// \\cond\n  explicit StarSurface(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(StarSurface);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<StarSurface>(*this);\n  }\n\n  explicit StarSurface(const Options::Context& context);\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {elliptic::BoundaryConditionType::Neumann};\n  }\n\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 BnsInitialData::Tags::RotationalShift<DataVector>,\n                 BnsInitialData::Tags::EulerEnthalpyConstant,\n                 domain::Tags::FaceNormal<3>>;\n  using volume_tags = tmpl::list<BnsInitialData::Tags::EulerEnthalpyConstant>;\n\n  static void apply(gsl::not_null<Scalar<DataVector>*> velocity_potential,\n                    gsl::not_null<Scalar<DataVector>*> n_dot_flux_for_potential,\n                    const tnsr::i<DataVector, 3>& deriv_velocity_potential,\n                    const Scalar<DataVector>& lapse,\n                    const tnsr::I<DataVector, 3>& rotational_shift,\n                    double euler_enthalpy_constant,\n                    const tnsr::i<DataVector, 3>& normal);\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  static void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> velocity_potential_correction,\n      gsl::not_null<Scalar<DataVector>*> n_dot_flux_for_potential_correction,\n      const tnsr::i<DataVector, 3>& deriv_velocity_potential);\n\n  void pup(PUP::er& p) override;\n};\n\nbool operator==(const StarSurface& lhs, const StarSurface& rhs);\n\nbool operator!=(const StarSurface& lhs, const StarSurface& rhs);\n\n}  // namespace BnsInitialData::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY BnsInitialData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Equations.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Equations.hpp\n  FirstOrderSystem.hpp\n  BnsInitialData.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  GeneralRelativity\n  Hydro\n  Utilities\n  INTERFACE\n  LinearOperators\n  )\n\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/BnsInitialData/Equations.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Poisson/Equations.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace BnsInitialData {\n\nvoid potential_fluxes(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_potential,\n    const tnsr::II<DataVector, 3>& rotational_shift_stress,\n    const tnsr::II<DataVector, 3>& inverse_spatial_metric,\n    const tnsr::i<DataVector, 3>& velocity_potential_gradient) {\n  ::tenex::evaluate<ti::I>(flux_for_potential,\n                           (inverse_spatial_metric(ti::I, ti::J) -\n                            rotational_shift_stress(ti::I, ti::J)) *\n                               velocity_potential_gradient(ti::j));\n}\n\nvoid fluxes_on_face(\n    gsl::not_null<tnsr::I<DataVector, 3>*> face_flux_for_potential,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::I<DataVector, 3>& face_normal_vector,\n    const tnsr::II<DataVector, 3>& rotational_shift_stress,\n    const Scalar<DataVector>& velocity_potential) {\n  // potential optimization : precompute the product of the `face_normal`\n  // and the `rotational_shift_stress`\n  ::tenex::evaluate<ti::I>(\n      face_flux_for_potential,\n      velocity_potential() *\n          (face_normal_vector(ti::I) -\n           rotational_shift_stress(ti::I, ti::J) * face_normal(ti::j)));\n}\n\nvoid add_potential_sources(\n    const gsl::not_null<Scalar<DataVector>*> source_for_potential,\n    const tnsr::i<DataVector, 3>&\n        log_deriv_lapse_times_density_over_specific_enthalpy,\n    const tnsr::i<DataVector, 3>& christoffel_contracted,\n    const tnsr::I<DataVector, 3>& flux_for_potential) {\n  ::tenex::update(\n      source_for_potential,\n      (*source_for_potential)() -\n          flux_for_potential(ti::I) *\n              (log_deriv_lapse_times_density_over_specific_enthalpy(ti::i) +\n               christoffel_contracted(ti::i)));\n}\n\nvoid Fluxes::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_potential,\n    const tnsr::II<DataVector, 3>& inverse_spatial_metric,\n    const tnsr::II<DataVector, 3>& rotational_shift_stress,\n    const Scalar<DataVector>& /*velocity_potential*/,\n    const tnsr::i<DataVector, 3>& velocity_potential_gradient) {\n  potential_fluxes(flux_for_potential, rotational_shift_stress,\n                   inverse_spatial_metric, velocity_potential_gradient);\n}\n\nvoid Fluxes::apply(const gsl::not_null<tnsr::I<DataVector, 3>*> flux_on_face,\n                   const tnsr::II<DataVector, 3>& /*inverse_spatial_metric*/,\n                   const tnsr::II<DataVector, 3>& rotational_shift_stress,\n                   const tnsr::i<DataVector, 3>& face_normal,\n                   const tnsr::I<DataVector, 3>& face_normal_vector,\n                   const Scalar<DataVector>& velocity_potential) {\n  fluxes_on_face(flux_on_face, face_normal, face_normal_vector,\n                 rotational_shift_stress, velocity_potential);\n}\n\nvoid Sources::apply(\n    const gsl::not_null<Scalar<DataVector>*> equation_for_potential,\n    const tnsr::i<DataVector, 3>&\n        log_deriv_lapse_times_density_over_specific_enthalpy,\n    const tnsr::i<DataVector, 3>& christoffel_contracted,\n    const Scalar<DataVector>& /*velocity_potential*/,\n    const tnsr::i<DataVector, 3>& /*gradient_for_potential*/,\n    const tnsr::I<DataVector, 3>& flux_for_potential) {\n  add_potential_sources(equation_for_potential,\n                        log_deriv_lapse_times_density_over_specific_enthalpy,\n                        christoffel_contracted, flux_for_potential);\n}\n\n}  // namespace BnsInitialData\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/BnsInitialData/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace BnsInitialData {\nstruct Fluxes;\nstruct Sources;\n}  // namespace BnsInitialData\n/// \\endcond\n\nnamespace BnsInitialData {\n\n/*!\n * \\brief Compute the fluxes\n * \\f[ F^i = \\gamma^{ij} n_j \\Phi  - n_j \\Phi\n * \\frac{B^iB^j}{\\alpha^2}  \\f] where \\f$ n_j\\f$ is the\n * `face_normal`.\n *\n * The `face_normal_vector` is \\f$ \\gamma^{ij} n_j\\f$.\n */\nvoid fluxes_on_face(\n    gsl::not_null<tnsr::I<DataVector, 3>*> face_flux_for_potential,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::I<DataVector, 3>& face_normal_vector,\n    const tnsr::II<DataVector, 3>& rotational_shift_stress,\n    const Scalar<DataVector>& velocity_potential);\n\n/*!\n * \\brief Compute the generic fluxes \\f$ F^i = D^i \\Phi -\n * \\frac{B^jD_j\\Phi}{\\alpha^2} B^i \\f$ for the Irrotational BNS equation for the\n * velocity potential.\n */\nvoid potential_fluxes(\n    gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_potential,\n    const tnsr::II<DataVector, 3>& rotational_shift_stress,\n    const tnsr::II<DataVector, 3>& inverse_spatial_metric,\n    const tnsr::i<DataVector, 3>& velocity_potential_gradient);\n\n/*!\n * \\brief Add the sources \\f$S=-\\Gamma^i_{ij}F^j - \\D_j \\left(\\ln\\alpha \\rho /\n * h\\right)  F^j\\f$ for the curved-space Irrotational BNS equation on a spatial\n * metric \\f$\\gamma_{ij}\\f$.\n *\n * These sources arise from the non-principal part of the Laplacian on a\n * non-Euclidean background.\n */\nvoid add_potential_sources(\n    gsl::not_null<Scalar<DataVector>*> source_for_potential,\n    const tnsr::i<DataVector, 3>&\n        log_deriv_lapse_times_density_over_specific_enthalpy,\n    const tnsr::i<DataVector, 3>& christoffel_contracted,\n    const tnsr::I<DataVector, 3>& flux_for_potential);\n\n/*!\n * \\brief Compute the fluxes \\f$F^i\\f$ for the curved-space Irrotatational BNS\n * equations on a spatial metric \\f$\\gamma_{ij}\\f$.\n *\n * \\see IrrotationalBns::FirstOrderSystem\n */\nstruct Fluxes {\n  using argument_tags =\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 Tags::RotationalShiftStress<DataVector>>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_potential,\n                    const tnsr::II<DataVector, 3>& inverse_spatial_metric,\n                    const tnsr::II<DataVector, 3>& rotational_shift_stress,\n                    const Scalar<DataVector>& velocity_potential,\n                    const tnsr::i<DataVector, 3>& velocity_potential_gradient);\n  static void apply(gsl::not_null<tnsr::I<DataVector, 3>*> flux_on_face,\n                    const tnsr::II<DataVector, 3>& inverse_spatial_metric,\n                    const tnsr::II<DataVector, 3>& rotational_shift_stress,\n                    const tnsr::i<DataVector, 3>& face_normal,\n                    const tnsr::I<DataVector, 3>& face_normal_vector,\n                    const Scalar<DataVector>& velocity_potential);\n};\n\n/*!\n * \\brief Add the sources \\f$S_A\\f$ for the curved-space Irrotatioanl BNS\n * equation on a spatial metric \\f$\\gamma_{ij}\\f$.\n *\n * \\see IrrotationalBns::FirstOrderSystem\n */\nstruct Sources {\n  using argument_tags = tmpl::list<\n      Tags::DerivLogLapseTimesDensityOverSpecificEnthalpy<DataVector>,\n      gr::Tags::SpatialChristoffelSecondKindContracted<DataVector, 3>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(gsl::not_null<Scalar<DataVector>*> equation_for_potential,\n                    const tnsr::i<DataVector, 3>&\n                        log_deriv_lapse_times_density_over_specific_enthalpy,\n                    const tnsr::i<DataVector, 3>& christoffel_contracted,\n                    const Scalar<DataVector>& velocity_potential,\n                    const tnsr::i<DataVector, 3>& /*gradient_for_potential*/,\n                    const tnsr::I<DataVector, 3>& flux_for_potential);\n};\n\n}  // namespace BnsInitialData\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/FirstOrderSystem.hpp",
    "content": "\n// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/BnsInitialData/Equations.hpp\"\n#include \"Elliptic/Systems/BnsInitialData/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace BnsInitialData {\n\n/*!\n * \\brief The Irrotational Bns equations From Baumgarte and Shapiro Chapter 15\n *  formulated as a set of coupled first-order PDEs.\n *\n * \\details This system formulates the Irrotational Bns Hydrostatic Equilibrium\n * equations for the velocity potential \\f$\\Phi\\f$. For a background matter\n * distribution (given by the specific enthalpy h) and a background metric\n * \\f$\\gamma_{ij}\\f$. The velocity potential is defined by \\f$D_i \\Phi = h\n * u_i\\f$ with \\f$u_i\\f$ (the spatial part of) the four velocity and where\n * \\f$\\Gamma^i_{jk}=\\frac{1}{2}\\gamma^{il}\\left(\\partial_j\\gamma_{kl}\n * +\\partial_k\\gamma_{jl}-\\partial_l\\gamma_{jk}\\right)\\f$ are the Christoffel\n * symbols of the second kind of the background (spatial) metric\n * \\f$\\gamma_{ij}\\f$. The\n * background metric \\f$\\gamma_{ij}\\f$ and the Christoffel symbols derived from\n * it are assumed to be independent of the variables \\f$\\Phi\\f$ and \\f$u_i\\f$,\n * i.e.\n * constant throughout an iterative elliptic solve.  Additionally a background\n * lapse (\\f$\\alpha\\f$) and\n * shift (\\f$\\beta\\f$) must be provided.  Finally, a \"rotational killing\n * vector\" \\f$k^i\\f$ (with magnitude\n * proportional to the angular velocity of the orbital motion) is provided.  The\n * rotational shift is defined as \\f$B^i = \\beta^i + k^i\\f$ which is\n * heuristically the background\n * motion of the spacetime.\n *\n * The system can be formulated in terms of these fluxes and sources (see\n * `elliptic::protocols::FirstOrderSystem`):\n *\n *\n * \\f{align*}\n * -\\partial_i F^i + S = f\n * \\f}\n *\n * \\f{align*}\n * F^i &=  D^i \\phi - \\frac{B^j D_j \\phi}{\\alpha^2}B^i  \\\\\n * S &= -F^iD_i \\left( \\ln \\frac{\\alpha \\rho}{h}\\right) -\\Gamma^i_{ij}F^j \\\\\n * f &= -D_i \\left(\\frac{C B^i}{\\alpha^2}\\right) -\n * \\frac{C}{\\alpha^2}B^iD_i\\left(\n * \\ln \\frac{\\alpha \\rho}{h}\\right)\\\\\n * \\f}\n */\nstruct FirstOrderSystem\n    : tt::ConformsTo<elliptic::protocols::FirstOrderSystem> {\n private:\n  using velocity_potential = Tags::VelocityPotential<DataVector>;\n\n public:\n  static constexpr size_t volume_dim = 3;\n\n  using primal_fields = tmpl::list<velocity_potential>;\n\n  // We just use the standard `Flux` prefix because the fluxes don't have\n  // symmetries and we don't need to give them a particular meaning.\n  using primal_fluxes = tmpl::list<\n      ::Tags::Flux<velocity_potential, tmpl::size_t<3>, Frame::Inertial>>;\n\n  using background_fields = tmpl::list<\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SpatialChristoffelSecondKindContracted<DataVector, 3>,\n      gr::Tags::Lapse<DataVector>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                    tmpl::integral_constant<size_t, 3>, Frame::Inertial>,\n      gr::Tags::Shift<DataVector, 3>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, 3>,\n                    tmpl::integral_constant<size_t, 3>, Frame::Inertial>,\n      Tags::RotationalShift<DataVector>,\n      Tags::DerivLogLapseTimesDensityOverSpecificEnthalpy<DataVector>,\n      Tags::RotationalShiftStress<DataVector>>;\n  using inv_metric_tag = gr::Tags::InverseSpatialMetric<DataVector, 3>;\n\n  using fluxes_computer = Fluxes;\n  using sources_computer = Sources;\n\n  using boundary_conditions_base =\n      elliptic::BoundaryConditions::BoundaryCondition<3>;\n  using modify_boundary_data = void;\n};\n}  // namespace BnsInitialData\n"
  },
  {
    "path": "src/Elliptic/Systems/BnsInitialData/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving for irrotational bns initial data\n */\nnamespace BnsInitialData::Tags {\n\n/*!\n * \\brief The shift plus a spatial vector \\f$ k^i\\f$\n * \\f$B^i = \\beta^i + k^i\\f$\n */\ntemplate <typename DataType>\nstruct RotationalShift : db::SimpleTag {\n  using type = tnsr::I<DataType, 3>;\n};\n/*!\n * \\brief The stress-energy corresponding to the rotation shift\n *\n *\n * \\f[\\Sigma^{ij} = \\frac{B^iB^j}{\\alpha^2}\\f]\n */\ntemplate <typename DataType>\nstruct RotationalShiftStress : db::SimpleTag {\n  using type = tnsr::II<DataType, 3>;\n};\n/*!\n * \\brief  The derivative  \\f$D_i \\ln (\\alpha \\rho/h)\\f$\n */\ntemplate <typename DataType>\nstruct DerivLogLapseTimesDensityOverSpecificEnthalpy : db::SimpleTag {\n  using type = tnsr::i<DataType, 3>;\n};\n\n/*!\n * \\brief The velocity potential for the fluid flow \\f$\\Phi\\f$, i.e. the\n * curl-free part of the fluid is given by \\f$\\nabla_a \\Phi = h u_a\\f$\n */\ntemplate <typename DataType>\nstruct VelocityPotential : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType>\nstruct SpatialRotationalKillingVector : db::SimpleTag {\n  using type = tnsr::I<DataType, 3>;\n};\n\ntemplate <typename DataType>\nstruct DerivSpatialRotationalKillingVector : db::SimpleTag {\n  using type = tnsr::iJ<DataType, 3>;\n};\n\nstruct EulerEnthalpyConstant : db::SimpleTag {\n  using type = double;\n};\n\n}  // namespace BnsInitialData::Tags\n"
  },
  {
    "path": "src/Elliptic/Systems/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EllipticSystems)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GetFluxesComputer.hpp\n  GetModifyBoundaryData.hpp\n  GetSourcesComputer.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Utilities\n  )\n\nadd_subdirectory(Elasticity)\nadd_subdirectory(BnsInitialData)\nadd_subdirectory(Poisson)\nadd_subdirectory(Punctures)\nadd_subdirectory(ScalarGaussBonnet)\nadd_subdirectory(SelfForce)\nadd_subdirectory(Xcts)\n\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ElasticityActions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  InitializeConstitutiveRelation.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InitializeConstitutiveRelation.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ConstitutiveRelations\n  DomainCreators\n  INTERFACE\n  DataStructures\n  Domain\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/Actions/InitializeConstitutiveRelation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Instantiations of domain::ExpandOverBlocks for Elasticity\n\n#include <cstddef>\n#include <memory>\n\n#include \"Domain/Creators/ExpandOverBlocks.tpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n\ntemplate <size_t Dim>\nusing ConstRelPtr = std::unique_ptr<\n    Elasticity::ConstitutiveRelations::ConstitutiveRelation<Dim>>;\n\ntemplate class domain::ExpandOverBlocks<ConstRelPtr<2>>;\ntemplate class domain::ExpandOverBlocks<ConstRelPtr<3>>;\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/Actions/InitializeConstitutiveRelation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <exception>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ExpandOverBlocks.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Structure/BlockGroups.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename... Tags>\nstruct TaggedTuple;\n}  // namespace tuples\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Elasticity {\nnamespace Tags {\n\n/// A constitutive relation in every block of the domain\ntemplate <size_t Dim>\nstruct ConstitutiveRelationPerBlock : db::SimpleTag {\n  using ConstRelPtr =\n      std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>;\n  using type = std::vector<ConstRelPtr>;\n\n  using option_tags = tmpl::list<domain::OptionTags::DomainCreator<Dim>,\n                                 OptionTags::ConstitutiveRelationPerBlock<Dim>>;\n  static constexpr bool pass_metavariables = false;\n\n  static type create_from_options(\n      const std::unique_ptr<DomainCreator<Dim>>& domain_creator,\n      const std::variant<ConstRelPtr, std::vector<ConstRelPtr>,\n                         std::unordered_map<std::string, ConstRelPtr>>&\n          constitutive_relation_per_block) {\n    const auto block_names = domain_creator->block_names();\n    const auto block_groups = domain_creator->block_groups();\n    const domain::ExpandOverBlocks<ConstRelPtr> expand_over_blocks{\n        block_names, block_groups};\n    try {\n      return std::visit(expand_over_blocks, constitutive_relation_per_block);\n    } catch (const std::exception& error) {\n      ERROR_NO_TRACE(\"Invalid 'Material':\\n\" << error.what());\n    }\n  }\n};\n\n/// References the constitutive relation for the element's block, which is\n/// stored in the global cache\ntemplate <size_t Dim>\nstruct ConstitutiveRelationReference : ConstitutiveRelation<Dim>,\n                                       db::ReferenceTag {\n  using base = ConstitutiveRelation<Dim>;\n  using argument_tags =\n      tmpl::list<ConstitutiveRelationPerBlock<Dim>, domain::Tags::Element<Dim>>;\n  static const ConstitutiveRelations::ConstitutiveRelation<Dim>& get(\n      const std::vector<\n          std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>>&\n          constitutive_relation_per_block,\n      const Element<Dim>& element) {\n    return *constitutive_relation_per_block.at(element.id().block_id());\n  }\n};\n\n/// Stores the names of the block groups that split the domain into layers with\n/// different material properties. Useful to observe quantities in each layer.\ntemplate <size_t Dim>\nstruct MaterialBlockGroups : db::SimpleTag {\n  using type = std::unordered_set<std::string>;\n\n  using option_tags = tmpl::list<OptionTags::ConstitutiveRelationPerBlock<Dim>>;\n  static constexpr bool pass_metavariables = false;\n  using ConstRelPtr =\n      std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>;\n\n  static type create_from_options(\n      const std::variant<ConstRelPtr, std::vector<ConstRelPtr>,\n                         std::unordered_map<std::string, ConstRelPtr>>&\n          constitutive_relation_per_block) {\n    if (std::holds_alternative<std::unordered_map<std::string, ConstRelPtr>>(\n            constitutive_relation_per_block)) {\n      const auto& map = std::get<std::unordered_map<std::string, ConstRelPtr>>(\n          constitutive_relation_per_block);\n      std::unordered_set<std::string> block_groups;\n      for (const auto& [block_name, _] : map) {\n        block_groups.insert(block_name);\n      }\n      return block_groups;\n    } else {\n      return {};\n    }\n  }\n};\n\n/// The name of the material layer (name of a block group with some material)\nstruct MaterialLayerName : db::SimpleTag {\n  using type = std::optional<std::string>;\n};\n\ntemplate <size_t Dim>\nstruct MaterialLayerObservationKeyCompute\n    : db::ComputeTag,\n      observers::Tags::ObservationKey<MaterialLayerName> {\n  using base = observers::Tags::ObservationKey<MaterialLayerName>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<MaterialLayerName, amr::Tags::ChildIds<Dim>>;\n  static void function(\n      const gsl::not_null<std::optional<std::string>*> observation_key,\n      const std::optional<std::string>& material_layer_name,\n      const std::unordered_set<ElementId<Dim>>& child_ids) {\n    const bool is_finest_grid = child_ids.empty();\n    // Set the corresponding observation key, but only on the finest multigrid\n    // level. This could be done better by supporting intersections of array\n    // sections in observation events or something like that.\n    if (is_finest_grid) {\n      *observation_key = material_layer_name;\n    } else {\n      *observation_key = std::nullopt;\n    }\n  }\n};\n\n}  // namespace Tags\n\n/// Actions related to solving Elasticity systems\nnamespace Actions {\n\n/*!\n * \\brief Initialize the constitutive relation describing properties of the\n * elastic material\n *\n * Every block in the domain can have a different constitutive relation,\n * allowing for composite materials. All constitutive relations are stored in\n * the global cache indexed by block, and elements reference their block's\n * constitutive relation in the DataBox. This means an element can retrieve the\n * local constitutive relation from the DataBox simply by requesting\n * `Elasticity::Tags::ConstitutiveRelation<Dim>`.\n */\ntemplate <size_t Dim>\nstruct InitializeConstitutiveRelation\n    : tt::ConformsTo<amr::protocols::Projector> {\n public:  // Iterable action\n  using const_global_cache_tags =\n      tmpl::list<Tags::ConstitutiveRelationPerBlock<Dim>,\n                 Tags::MaterialBlockGroups<Dim>>;\n  using simple_tags = tmpl::list<Tags::MaterialLayerName>;\n  using compute_tags =\n      tmpl::list<Tags::ConstitutiveRelationReference<Dim>,\n                 Tags::MaterialLayerObservationKeyCompute<Dim>>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeConstitutiveRelation>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // amr::protocols::Projector\n  using return_tags = simple_tags;\n  using argument_tags =\n      tmpl::list<Tags::MaterialBlockGroups<Dim>, domain::Tags::Element<Dim>,\n                 domain::Tags::Domain<Dim>>;\n\n  template <typename... AmrData>\n  static void apply(\n      const gsl::not_null<std::optional<std::string>*> material_layer_name,\n      const std::unordered_set<std::string>& material_block_groups,\n      const Element<Dim>& element, const Domain<Dim>& domain,\n      const AmrData&... /*unused*/) {\n    const auto& block = domain.blocks()[element.id().block_id()];\n    // Check if this element is in a material layer\n    *material_layer_name = [&material_block_groups, &domain,\n                            &block]() -> std::optional<std::string> {\n      for (const auto& name : material_block_groups) {\n        if (domain::block_is_in_group(block.name(), name,\n                                      domain.block_groups())) {\n          return name;\n        }\n      }\n      return std::nullopt;\n    }();\n  }\n};\n\n}  // namespace Actions\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ElasticityBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  LaserBeam.cpp\n  Zero.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  LaserBeam.hpp\n  Zero.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Options\n  Serialization\n  Utilities\n  INTERFACE\n  Domain\n  Elliptic\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Elasticity/BoundaryConditions/LaserBeam.hpp\"\n#include \"Elliptic/Systems/Elasticity/BoundaryConditions/Zero.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity::BoundaryConditions {\n\ntemplate <typename System>\nusing standard_boundary_conditions = tmpl::append<\n    tmpl::list<\n        elliptic::BoundaryConditions::AnalyticSolution<System>,\n        Zero<System::volume_dim, elliptic::BoundaryConditionType::Dirichlet>,\n        Zero<System::volume_dim, elliptic::BoundaryConditionType::Neumann>>,\n    tmpl::conditional_t<System::volume_dim == 3, tmpl::list<LaserBeam>,\n                        tmpl::list<>>>;\n\n}\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/BoundaryConditions/LaserBeam.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Elasticity/BoundaryConditions/LaserBeam.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Elasticity::BoundaryConditions {\n\nvoid LaserBeam::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> /*displacement*/,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_minus_stress,\n    const tnsr::iJ<DataVector, 3>& /*deriv_displacement*/,\n    const tnsr::I<DataVector, 3>& x,\n    const tnsr::i<DataVector, 3>& face_normal) const {\n  const auto n_dot_x = get<0>(face_normal) * get<0>(x) +\n                       get<1>(face_normal) * get<1>(x) +\n                       get<2>(face_normal) * get<2>(x);\n  const auto r_sq = square(get<0>(x)) + square(get<1>(x)) + square(get<2>(x)) -\n                    square(n_dot_x);\n  const DataVector beam_profile =\n      exp(-r_sq / square(beam_width_)) / (M_PI * square(beam_width_));\n  get<0>(*n_dot_minus_stress) = -beam_profile * get<0>(face_normal);\n  get<1>(*n_dot_minus_stress) = -beam_profile * get<1>(face_normal);\n  get<2>(*n_dot_minus_stress) = -beam_profile * get<2>(face_normal);\n}\n\nvoid LaserBeam::apply_linearized(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> /*displacement*/,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_minus_stress,\n    const tnsr::iJ<DataVector, 3>& /*deriv_displacement*/) {\n  get<0>(*n_dot_minus_stress) = 0.;\n  get<1>(*n_dot_minus_stress) = 0.;\n  get<2>(*n_dot_minus_stress) = 0.;\n}\n\nbool operator==(const LaserBeam& lhs, const LaserBeam& rhs) {\n  return lhs.beam_width() == rhs.beam_width();\n}\n\nbool operator!=(const LaserBeam& lhs, const LaserBeam& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID LaserBeam::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace Elasticity::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/BoundaryConditions/LaserBeam.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Elasticity::BoundaryConditions {\n\n/*!\n * \\brief A laser beam with Gaussian profile normally incident to the surface\n *\n * This boundary condition represents a laser beam with Gaussian profile that\n * exerts pressure normal to the surface of a reflecting material. The pressure\n * we are considering here is\n *\n * \\f{align}\n * n_i T^{ij} = -n^j \\frac{e^{-\\frac{r^2}{r_0^2}}}{\\pi r_0^2}\n * \\f}\n *\n * where \\f$n_i\\f$ is the unit normal pointing _out_ of the surface, \\f$r\\f$ is\n * the coordinate distance from the origin in the plane perpendicular to\n * \\f$n_i\\f$ and \\f$r_0\\f$ is the \"beam width\" parameter. The pressure profile\n * and the angle of incidence can be generalized in future work. Note that we\n * follow the convention of \\cite Lovelace2007tn, \\cite Lovelace2017xyf, and\n * \\cite Vu2023thn in defining the beam width, and other publications may\n * include a factor of \\f$\\sqrt{2}\\f$ in its definition.\n *\n * This boundary condition is used to simulate thermal noise induced in a mirror\n * by the laser, as detailed for instance in \\cite Lovelace2007tn,\n * \\cite Lovelace2017xyf, and \\cite Vu2023thn.\n * See also `Elasticity::Solutions::HalfSpaceMirror` for\n * an analytic solution that involves this boundary condition.\n */\nclass LaserBeam : public elliptic::BoundaryConditions::BoundaryCondition<3> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<3>;\n\n public:\n  struct BeamWidth {\n    using type = double;\n    static constexpr Options::String help =\n        \"The width r_0 of the Gaussian beam profile, such that FWHM = 2 * \"\n        \"sqrt(ln 2) * r_0\";\n    static type lower_bound() { return 0.0; }\n  };\n\n  static constexpr Options::String help =\n      \"A laser beam with Gaussian profile normally incident to the surface.\";\n  using options = tmpl::list<BeamWidth>;\n\n  LaserBeam() = default;\n  LaserBeam(const LaserBeam&) = default;\n  LaserBeam& operator=(const LaserBeam&) = default;\n  LaserBeam(LaserBeam&&) = default;\n  LaserBeam& operator=(LaserBeam&&) = default;\n  ~LaserBeam() = default;\n\n  /// \\cond\n  explicit LaserBeam(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(LaserBeam);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<LaserBeam>(*this);\n  }\n\n  LaserBeam(double beam_width) : beam_width_(beam_width) {}\n\n  double beam_width() const { return beam_width_; }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {3, elliptic::BoundaryConditionType::Neumann};\n  }\n\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>,\n                 ::Tags::Normalized<\n                     domain::Tags::UnnormalizedFaceNormal<3, Frame::Inertial>>>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(gsl::not_null<tnsr::I<DataVector, 3>*> displacement,\n             gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_minus_stress,\n             const tnsr::iJ<DataVector, 3>& deriv_displacement,\n             const tnsr::I<DataVector, 3>& x,\n             const tnsr::i<DataVector, 3>& face_normal) const;\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  static void apply_linearized(\n      gsl::not_null<tnsr::I<DataVector, 3>*> displacement,\n      gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_minus_stress,\n      const tnsr::iJ<DataVector, 3>& deriv_displacement);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override { p | beam_width_; }\n\n private:\n  double beam_width_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator==(const LaserBeam& lhs, const LaserBeam& rhs);\n\nbool operator!=(const LaserBeam& lhs, const LaserBeam& rhs);\n\n}  // namespace Elasticity::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/BoundaryConditions/Zero.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Elasticity/BoundaryConditions/Zero.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Elasticity::BoundaryConditions {\n\ntemplate <size_t Dim, elliptic::BoundaryConditionType BoundaryConditionType>\nstd::string Zero<Dim, BoundaryConditionType>::name() {\n  if constexpr (BoundaryConditionType ==\n                elliptic::BoundaryConditionType::Dirichlet) {\n    return \"Fixed\";\n  } else {\n    return \"Free\";\n  }\n}\n\ntemplate <size_t Dim, elliptic::BoundaryConditionType BoundaryConditionType>\nvoid Zero<Dim, BoundaryConditionType>::apply(\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> displacement,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> n_dot_minus_stress,\n    const tnsr::iJ<DataVector, Dim>& /*deriv_displacement*/) {\n  if constexpr (BoundaryConditionType ==\n                elliptic::BoundaryConditionType::Dirichlet) {\n    (void)n_dot_minus_stress;\n    for (size_t d = 0; d < Dim; ++d) {\n      displacement->get(d) = 0.;\n    }\n  } else {\n    (void)displacement;\n    for (size_t d = 0; d < Dim; ++d) {\n      n_dot_minus_stress->get(d) = 0.;\n    }\n  }\n}\n\ntemplate <size_t Dim, elliptic::BoundaryConditionType BoundaryConditionType>\nvoid Zero<Dim, BoundaryConditionType>::apply_linearized(\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> displacement,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> n_dot_minus_stress,\n    const tnsr::iJ<DataVector, Dim>& deriv_displacement) {\n  apply(displacement, n_dot_minus_stress, deriv_displacement);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define BCTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data) template class Zero<DIM(data), BCTYPE(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3),\n                        (elliptic::BoundaryConditionType::Dirichlet,\n                         elliptic::BoundaryConditionType::Neumann))\n\n#undef DIM\n#undef BCTYPE\n#undef INSTANTIATE\n\n}  // namespace Elasticity::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/BoundaryConditions/Zero.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Elasticity::BoundaryConditions {\nnamespace detail {\n\ntemplate <elliptic::BoundaryConditionType BoundaryConditionType>\nstruct ZeroHelpString;\ntemplate <>\nstruct ZeroHelpString<elliptic::BoundaryConditionType::Dirichlet> {\n  static constexpr Options::String help =\n      \"Zero Dirichlet boundary conditions imposed on the displacement vector, \"\n      \"i.e. the elastic material is held fixed at this boundary.\";\n};\ntemplate <>\nstruct ZeroHelpString<elliptic::BoundaryConditionType::Neumann> {\n  static constexpr Options::String help =\n      \"Zero Neumann boundary conditions imposed on the stress tensor \"\n      \"perpendicular to the surface, i.e. the elastic material is free to \"\n      \"deform at this boundary.\";\n};\n\n}  // namespace detail\n\n/// Impose zero Dirichlet (\"fixed\") or Neumann (\"free\") boundary conditions.\ntemplate <size_t Dim, elliptic::BoundaryConditionType BoundaryConditionType>\nclass Zero : public elliptic::BoundaryConditions::BoundaryCondition<Dim> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<Dim>;\n\n  static_assert(BoundaryConditionType ==\n                        elliptic::BoundaryConditionType::Dirichlet or\n                    BoundaryConditionType ==\n                        elliptic::BoundaryConditionType::Neumann,\n                \"Unexpected boundary condition type. Supported are Dirichlet \"\n                \"and Neumann.\");\n\n public:\n  static std::string name();\n  using options = tmpl::list<>;\n  static constexpr Options::String help =\n      detail::ZeroHelpString<BoundaryConditionType>::help;\n\n  Zero() = default;\n  Zero(const Zero&) = default;\n  Zero& operator=(const Zero&) = default;\n  Zero(Zero&&) = default;\n  Zero& operator=(Zero&&) = default;\n  ~Zero() = default;\n\n  /// \\cond\n  explicit Zero(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Zero);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<Zero>(*this);\n  }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {Dim, BoundaryConditionType};\n  }\n\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n\n  static void apply(gsl::not_null<tnsr::I<DataVector, Dim>*> displacement,\n                    gsl::not_null<tnsr::I<DataVector, Dim>*> n_dot_minus_stress,\n                    const tnsr::iJ<DataVector, Dim>& deriv_displacement);\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  static void apply_linearized(\n      gsl::not_null<tnsr::I<DataVector, Dim>*> displacement,\n      gsl::not_null<tnsr::I<DataVector, Dim>*> n_dot_minus_stress,\n      const tnsr::iJ<DataVector, Dim>& deriv_displacement);\n};\n\ntemplate <size_t Dim, elliptic::BoundaryConditionType BoundaryConditionType>\nbool operator==(const Zero<Dim, BoundaryConditionType>& /*lhs*/,\n                const Zero<Dim, BoundaryConditionType>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim, elliptic::BoundaryConditionType BoundaryConditionType>\nbool operator!=(const Zero<Dim, BoundaryConditionType>& lhs,\n                const Zero<Dim, BoundaryConditionType>& rhs) {\n  return not(lhs == rhs);\n}\n\n/// \\cond\ntemplate <size_t Dim, elliptic::BoundaryConditionType BoundaryConditionType>\nPUP::able::PUP_ID Zero<Dim, BoundaryConditionType>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace Elasticity::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Elasticity)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Equations.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Equations.hpp\n  FirstOrderSystem.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ConstitutiveRelations\n  DataStructures\n  Domain\n  Utilities\n  INTERFACE\n  LinearOperators\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Elasticity/Equations.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity {\n\ntemplate <size_t Dim>\nvoid primal_fluxes(const gsl::not_null<tnsr::II<DataVector, Dim>*> minus_stress,\n                   const tnsr::iJ<DataVector, Dim>& deriv_displacement,\n                   const ConstitutiveRelations::ConstitutiveRelation<Dim>&\n                       constitutive_relation,\n                   const tnsr::I<DataVector, Dim>& coordinates) {\n  tnsr::ii<DataVector, Dim> strain{deriv_displacement.begin()->size()};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      strain.get(i, j) =\n          0.5 * (deriv_displacement.get(i, j) + deriv_displacement.get(j, i));\n    }\n  }\n  constitutive_relation.stress(minus_stress, strain, coordinates);\n  for (auto& component : *minus_stress) {\n    component *= -1.;\n  }\n}\n\ntemplate <size_t Dim>\nvoid add_curved_sources(\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> source_for_displacement,\n    const tnsr::Ijj<DataVector, Dim>& christoffel_second_kind,\n    const tnsr::i<DataVector, Dim>& christoffel_contracted,\n    const tnsr::II<DataVector, Dim>& stress) {\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      source_for_displacement->get(j) -=\n          christoffel_contracted.get(i) * stress.get(i, j);\n      for (size_t k = 0; k < Dim; ++k) {\n        source_for_displacement->get(j) -=\n            christoffel_second_kind.get(j, i, k) * stress.get(i, k);\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid Fluxes<Dim>::apply(\n    const gsl::not_null<tnsr::II<DataVector, Dim>*> minus_stress,\n    const std::vector<\n        std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>>&\n        constitutive_relation_per_block,\n    const tnsr::I<DataVector, Dim>& coordinates,\n    const ElementId<Dim>& element_id,\n    const tnsr::I<DataVector, Dim>& /*displacement*/,\n    const tnsr::iJ<DataVector, Dim>& deriv_displacement) {\n  primal_fluxes(minus_stress, deriv_displacement,\n                *constitutive_relation_per_block.at(element_id.block_id()),\n                coordinates);\n}\n\ntemplate <size_t Dim>\nvoid Fluxes<Dim>::apply(\n    const gsl::not_null<tnsr::II<DataVector, Dim>*> minus_stress,\n    const std::vector<\n        std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>>&\n        constitutive_relation_per_block,\n    const tnsr::I<DataVector, Dim>& coordinates,\n    const ElementId<Dim>& element_id,\n    const tnsr::i<DataVector, Dim>& face_normal,\n    const tnsr::I<DataVector, Dim>& /*face_normal_vector*/,\n    const tnsr::I<DataVector, Dim>& displacement) {\n  tnsr::ii<DataVector, Dim> strain{displacement.begin()->size()};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      strain.get(i, j) = 0.5 * (face_normal.get(i) * displacement.get(j) +\n                                face_normal.get(j) * displacement.get(i));\n    }\n  }\n  const auto& constitutive_relation =\n      *constitutive_relation_per_block.at(element_id.block_id());\n  constitutive_relation.stress(minus_stress, strain, coordinates);\n  for (auto& component : *minus_stress) {\n    component *= -1.;\n  }\n}\n\n}  // namespace Elasticity\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template void Elasticity::primal_fluxes<DIM(data)>(                    \\\n      gsl::not_null<tnsr::II<DataVector, DIM(data)>*>,                   \\\n      const tnsr::iJ<DataVector, DIM(data)>&,                            \\\n      const Elasticity::ConstitutiveRelations::ConstitutiveRelation<DIM( \\\n          data)>&,                                                       \\\n      const tnsr::I<DataVector, DIM(data)>&);                            \\\n  template void Elasticity::add_curved_sources<DIM(data)>(               \\\n      gsl::not_null<tnsr::I<DataVector, DIM(data)>*>,                    \\\n      const tnsr::Ijj<DataVector, DIM(data)>&,                           \\\n      const tnsr::i<DataVector, DIM(data)>&,                             \\\n      const tnsr::II<DataVector, DIM(data)>&);                           \\\n  template class Elasticity::Fluxes<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Elasticity {\nnamespace ConstitutiveRelations {\ntemplate <size_t Dim>\nclass ConstitutiveRelation;\n}  // namespace ConstitutiveRelations\nnamespace Tags {\ntemplate <size_t Dim>\nstruct ConstitutiveRelationPerBlock;\n}  // namespace Tags\n}  // namespace Elasticity\n/// \\endcond\n\nnamespace Elasticity {\n\n/*!\n * \\brief Compute the fluxes \\f$F^{ij}=Y^{ijkl}(x) S_{kl}(x)=-T^{ij}\\f$ for\n * the Elasticity equation.\n */\ntemplate <size_t Dim>\nvoid primal_fluxes(gsl::not_null<tnsr::II<DataVector, Dim>*> minus_stress,\n                   const tnsr::iJ<DataVector, Dim>& deriv_displacement,\n                   const ConstitutiveRelations::ConstitutiveRelation<Dim>&\n                       constitutive_relation,\n                   const tnsr::I<DataVector, Dim>& coordinates);\n\n/*!\n * \\brief Add the contribution \\f$-\\Gamma^i_{ik}T^{kj} - \\Gamma^j_{ik}T^{ik}\\f$\n * to the displacement source for the curved-space elasticity equations on a\n * metric \\f$\\gamma_{ij}\\f$.\n *\n * These sources arise from the non-principal part of the divergence on a\n * curved background.\n */\ntemplate <size_t Dim>\nvoid add_curved_sources(\n    gsl::not_null<tnsr::I<DataVector, Dim>*> source_for_displacement,\n    const tnsr::Ijj<DataVector, Dim>& christoffel_second_kind,\n    const tnsr::i<DataVector, Dim>& christoffel_contracted,\n    const tnsr::II<DataVector, Dim>& stress);\n\n/*!\n * \\brief Compute the fluxes \\f$F^i_A\\f$ for the Elasticity equation on a flat\n * metric in Cartesian coordinates.\n *\n * \\see Elasticity::FirstOrderSystem\n */\ntemplate <size_t Dim>\nstruct Fluxes {\n  using argument_tags =\n      tmpl::list<Tags::ConstitutiveRelationPerBlock<Dim>,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n  using volume_tags = tmpl::list<Tags::ConstitutiveRelationPerBlock<Dim>>;\n  using const_global_cache_tags = volume_tags;\n  static constexpr bool is_trivial = false;\n  static constexpr bool is_discontinuous = true;\n  static void apply(\n      gsl::not_null<tnsr::II<DataVector, Dim>*> minus_stress,\n      const std::vector<\n          std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>>&\n          constitutive_relation_per_block,\n      const tnsr::I<DataVector, Dim>& coordinates,\n      const ElementId<Dim>& element_id,\n      const tnsr::I<DataVector, Dim>& displacement,\n      const tnsr::iJ<DataVector, Dim>& deriv_displacement);\n  static void apply(\n      gsl::not_null<tnsr::II<DataVector, Dim>*> minus_stress,\n      const std::vector<\n          std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>>&\n          constitutive_relation_per_block,\n      const tnsr::I<DataVector, Dim>& coordinates,\n      const ElementId<Dim>& element_id,\n      const tnsr::i<DataVector, Dim>& face_normal,\n      const tnsr::I<DataVector, Dim>& face_normal_vector,\n      const tnsr::I<DataVector, Dim>& displacement);\n};\n\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Elasticity/Equations.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity {\n\n/*!\n * \\brief The linear elasticity equation formulated as a set of coupled\n * first-order PDEs.\n *\n * This system formulates the elasticity equation (see `Elasticity`):\n *\n * \\f{align*}\n * \\nabla_i T^{ij} = f_\\mathrm{ext}^j \\\\\n * T^{ij} = -Y^{ijkl} \\nabla_{(k} \\xi_{l)}\n * \\f}\n *\n * The system can be formulated in terms of these fluxes and sources (see\n * `elliptic::protocols::FirstOrderSystem`):\n *\n * \\f{align*}\n * F^{ij} &= -T^{ij} = Y^{ijkl} \\nabla_{(k} \\xi_{l)} \\\\\n * S^j &= 0 \\\\\n * f^j &= f_\\mathrm{ext}^j \\text{.}\n * \\f}\n */\ntemplate <size_t Dim>\nstruct FirstOrderSystem\n    : tt::ConformsTo<elliptic::protocols::FirstOrderSystem> {\n  static constexpr size_t volume_dim = Dim;\n\n  using primal_fields = tmpl::list<Tags::Displacement<Dim>>;\n  using primal_fluxes = tmpl::list<Tags::MinusStress<Dim>>;\n\n  using background_fields = tmpl::list<>;\n  using inv_metric_tag = void;\n\n  using fluxes_computer = Fluxes<Dim>;\n  using sources_computer = void;\n\n  using boundary_conditions_base =\n      elliptic::BoundaryConditions::BoundaryCondition<Dim>;\n  using modify_boundary_data = void;\n};\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/Elliptic/Systems/Elasticity/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving elasticity problems\n *\n * \\details In elasticity problems we solve for the displacement vector\n * field \\f$\\boldsymbol{\\xi}\\f$ in an elastic material that responds to external\n * forces, stresses or deformations. In this static approximation the\n * equations of motion reduce to the elliptic equations\n *\n * \\f[\n * \\nabla_i T^{ij} = f_\\mathrm{ext}^j\n * \\f]\n *\n * that describes a state of equilibrium between the stresses \\f$T^{ij}\\f$\n * within the material and the external body forces\n * \\f$\\boldsymbol{f}_\\mathrm{ext}\\f$ (Eqns. 11.13 and 11.14 in\n * \\cite ThorneBlandford2017 with the counteracting internal forces\n * \\f$\\boldsymbol{f} = -\\boldsymbol{f}_\\mathrm{ext}\\f$). For small\n * deformations (see e.g. \\cite ThorneBlandford2017, Section 11.3.2 for a\n * discussion) the stress is related to the strain\n * \\f$S_{ij}=\\nabla_{(i}\\xi_{j)}\\f$ by a linear constitutive relation\n * \\f$T^{ij}=-Y^{ijkl}S_{kl}\\f$ (Eq. 11.17 in \\cite ThorneBlandford2017) that\n * describes the elastic properties of the material (see\n * `Elasticity::ConstitutiveRelations::ConstitutiveRelation`).\n */\nnamespace Elasticity {\nnamespace Tags {\n\n/*!\n * \\brief The material displacement field \\f$\\boldsymbol{u}(x)\\f$\n */\ntemplate <size_t Dim>\nstruct Displacement : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n\n/*!\n * \\brief The symmetric strain \\f$S_{ij}=\\nabla_{(i}u_{j)}\\f$, describing the\n * deformation of the elastic material.\n */\ntemplate <size_t Dim>\nstruct Strain : db::SimpleTag {\n  using type = tnsr::ii<DataVector, Dim>;\n};\n\n/*!\n * \\brief The symmetric stress, i.e. $T^{ij}$, describing pressure within the\n * elastic material.\n */\ntemplate <size_t Dim>\nstruct Stress : db::SimpleTag {\n  using type = tnsr::II<DataVector, Dim>;\n};\n\n/*!\n * \\brief Minus the `Stress`, i.e. $-T^{ij}$. This tag can be used for the flux\n * in a first-order formulation of the elasticity system.\n */\ntemplate <size_t Dim>\nstruct MinusStress : db::SimpleTag {\n  using type = tnsr::II<DataVector, Dim>;\n};\n\n/*!\n * \\brief The energy density \\f$U=-\\frac{1}{2}S_{ij}T^{ij}\\f$ stored in the\n * deformation of the elastic material.\n */\ntemplate <size_t Dim>\nstruct PotentialEnergyDensity : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n}  // namespace Tags\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/Elliptic/Systems/GetFluxesComputer.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic {\nnamespace detail {\ntemplate <typename System, typename = std::void_t<>>\nstruct fluxes_computer_linearized {\n  using type = typename System::fluxes_computer;\n};\ntemplate <typename System>\nstruct fluxes_computer_linearized<\n    System, std::void_t<typename System::fluxes_computer_linearized>> {\n  using type = typename System::fluxes_computer_linearized;\n};\n}  // namespace detail\n\n/// The `System::fluxes_computer` or the `System::fluxes_computer_linearized`,\n/// depending on the `Linearized` parameter. If the system has no\n/// `fluxes_computer_linearized` alias it is assumed that the linear flux is\n/// functionally identical to the non-linear flux, so the\n/// `System::fluxes_computer` is returned either way.\ntemplate <typename System, bool Linearized>\nusing get_fluxes_computer = tmpl::conditional_t<\n    Linearized, typename detail::fluxes_computer_linearized<System>::type,\n    typename System::fluxes_computer>;\n\n/// The `argument_tags` of either the `System::fluxes_computer` or the\n/// `System::fluxes_computer_linearized`, depending on the `Linearized`\n/// parameter.\ntemplate <typename System, bool Linearized>\nusing get_fluxes_argument_tags =\n    typename get_fluxes_computer<System, Linearized>::argument_tags;\n\n/// The `volume_tags` of either the `System::fluxes_computer` or the\n/// `System::fluxes_computer_linearized`, depending on the `Linearized`\n/// parameter.\ntemplate <typename System, bool Linearized>\nusing get_fluxes_volume_tags =\n    typename get_fluxes_computer<System, Linearized>::volume_tags;\n\n/// The `const_global_cache_tags` of either the `System::fluxes_computer` or the\n/// `System::fluxes_computer_linearized`, depending on the `Linearized`\n/// parameter.\ntemplate <typename System, bool Linearized>\nusing get_fluxes_const_global_cache_tags =\n    typename get_fluxes_computer<System, Linearized>::const_global_cache_tags;\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/Systems/GetModifyBoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic {\nnamespace detail {\nstruct NoModifyBoundaryData {\n  using argument_tags = tmpl::list<>;\n};\n}  // namespace detail\n\n/// The `argument_tags` of the `System::modify_boundary_data`, or an empty list\n/// if `System::modify_boundary_data` is `void`.\ntemplate <typename System>\nusing get_modify_boundary_data_args_tags = typename tmpl::conditional_t<\n    std::is_same_v<typename System::modify_boundary_data, void>,\n    detail::NoModifyBoundaryData,\n    typename System::modify_boundary_data>::argument_tags;\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/Systems/GetSourcesComputer.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic {\nnamespace detail {\ntemplate <typename System, typename = std::void_t<>>\nstruct sources_computer_linearized {\n  using type = typename System::sources_computer;\n};\ntemplate <typename System>\nstruct sources_computer_linearized<\n    System, std::void_t<typename System::sources_computer_linearized>> {\n  using type = typename System::sources_computer_linearized;\n};\nstruct NoSourcesComputer {\n  using argument_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n};\n}  // namespace detail\n\n/// The `System::sources_computer` or the `System::sources_computer_linearized`,\n/// depending on the `Linearized` parameter. If the system has no\n/// `sources_computer_linearized` alias it is assumed to be linear, so the\n/// `System::sources_computer` is returned either way.\ntemplate <typename System, bool Linearized>\nusing get_sources_computer = tmpl::conditional_t<\n    Linearized, typename detail::sources_computer_linearized<System>::type,\n    typename System::sources_computer>;\n\n/// The `argument_tags` of either the `System::sources_computer` or the\n/// `System::sources_computer_linearized`, depending on the `Linearized`\n/// parameter, or an empty list if the sources computer is `void`.\ntemplate <typename System, bool Linearized>\nusing get_sources_argument_tags = typename tmpl::conditional_t<\n    std::is_same_v<get_sources_computer<System, Linearized>, void>,\n    detail::NoSourcesComputer,\n    get_sources_computer<System, Linearized>>::argument_tags;\n\n/// The `const_global_cache_tags` of either the `System::sources_computer` or\n/// the `System::sources_computer_linearized`, depending on the `Linearized`\n/// parameter, or an empty list if the sources computer is `void`.\ntemplate <typename System, bool Linearized>\nusing get_sources_const_global_cache_tags = typename tmpl::conditional_t<\n    std::is_same_v<get_sources_computer<System, Linearized>, void>,\n    detail::NoSourcesComputer,\n    get_sources_computer<System, Linearized>>::const_global_cache_tags;\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY PoissonBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Robin.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Robin.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Elliptic\n  Options\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/Systems/Poisson/BoundaryConditions/Robin.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::BoundaryConditions {\n\ntemplate <typename System>\nusing standard_boundary_conditions =\n    tmpl::list<elliptic::BoundaryConditions::AnalyticSolution<System>,\n               Robin<System::volume_dim>>;\n\n}\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/BoundaryConditions/Robin.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Poisson/BoundaryConditions/Robin.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Poisson::BoundaryConditions {\n\ntemplate <size_t Dim>\nRobin<Dim>::Robin(const double dirichlet_weight, const double neumann_weight,\n                  const double constant, const Options::Context& context)\n    : dirichlet_weight_(dirichlet_weight),\n      neumann_weight_(neumann_weight),\n      constant_(constant) {\n  if (dirichlet_weight == 0. and neumann_weight == 0.) {\n    PARSE_ERROR(\n        context,\n        \"Either the dirichlet_weight or the neumann_weight must be non-zero \"\n        \"(or both).\");\n  }\n}\n\ntemplate <size_t Dim>\nvoid Robin<Dim>::apply(\n    const gsl::not_null<Scalar<DataVector>*> field,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient,\n    const tnsr::i<DataVector, Dim>& /*deriv_field*/) const {\n  if (neumann_weight_ == 0.) {\n    ASSERT(\n        not equal_within_roundoff(dirichlet_weight_, 0.),\n        \"The dirichlet_weight is close to zero. Set it to a non-zero value to \"\n        \"avoid divisions by small numbers.\");\n    get(*field) = constant_ / dirichlet_weight_;\n  } else {\n    ASSERT(not equal_within_roundoff(neumann_weight_, 0.),\n           \"The neumann_weight is close to zero. Set it to a non-zero value to \"\n           \"avoid divisions by small numbers.\");\n    get(*n_dot_field_gradient) =\n        (constant_ - dirichlet_weight_ * get(*field)) / neumann_weight_;\n  }\n}\n\ntemplate <size_t Dim>\nvoid Robin<Dim>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> field_correction,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient_correction,\n    const tnsr::i<DataVector, Dim>& /*deriv_field_correction*/) const {\n  if (neumann_weight_ == 0.) {\n    get(*field_correction) = 0.;\n  } else {\n    ASSERT(not equal_within_roundoff(neumann_weight_, 0.),\n           \"The neumann_weight is close to zero. Set it to a non-zero value to \"\n           \"avoid divisions by small numbers.\");\n    get(*n_dot_field_gradient_correction) =\n        -dirichlet_weight_ / neumann_weight_ * get(*field_correction);\n  }\n}\n\ntemplate <size_t Dim>\nvoid Robin<Dim>::pup(PUP::er& p) {\n  p | dirichlet_weight_;\n  p | neumann_weight_;\n  p | constant_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const Robin<Dim>& lhs, const Robin<Dim>& rhs) {\n  return lhs.dirichlet_weight() == rhs.dirichlet_weight() and\n         lhs.neumann_weight() == rhs.neumann_weight() and\n         lhs.constant() == rhs.constant();\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Robin<Dim>& lhs, const Robin<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID Robin<Dim>::my_PUP_ID = 0;  // NOLINT\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template class Robin<DIM(data)>;                                            \\\n  template bool operator==(const Robin<DIM(data)>&, const Robin<DIM(data)>&); \\\n  template bool operator!=(const Robin<DIM(data)>&, const Robin<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace Poisson::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/BoundaryConditions/Robin.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Poisson::BoundaryConditions {\n\n/// Impose Robin boundary conditions \\f$a u + b n_i \\nabla^i u = c\\f$. The\n/// boundary condition is imposed as Neumann-type (i.e. on \\f$n_i \\nabla^i u\\f$)\n/// if \\f$|b| > 0\\f$ and as Dirichlet-type (i.e. on \\f$u\\f$) if \\f$b = 0\\f$.\ntemplate <size_t Dim>\nclass Robin : public elliptic::BoundaryConditions::BoundaryCondition<Dim> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<Dim>;\n\n public:\n  static constexpr Options::String help =\n      \"Robin boundary conditions a * u + b * n_i grad(u)^i = c. The boundary \"\n      \"condition is imposed as Neumann-type (i.e. on n_i grad(u)^i) if abs(b) \"\n      \"> 0 and as Dirichlet-type (i.e. on u) if b = 0.\";\n\n  struct DirichletWeight {\n    using type = double;\n    static constexpr Options::String help = \"The parameter 'a'\";\n  };\n\n  struct NeumannWeight {\n    using type = double;\n    static constexpr Options::String help = \"The parameter 'b'\";\n  };\n\n  struct Constant {\n    using type = double;\n    static constexpr Options::String help = \"The parameter 'c'\";\n  };\n\n  using options = tmpl::list<DirichletWeight, NeumannWeight, Constant>;\n\n  Robin() = default;\n  Robin(const Robin&) = default;\n  Robin& operator=(const Robin&) = default;\n  Robin(Robin&&) = default;\n  Robin& operator=(Robin&&) = default;\n  ~Robin() = default;\n\n  /// \\cond\n  explicit Robin(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Robin);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<Robin>(*this);\n  }\n\n  Robin(double dirichlet_weight, double neumann_weight, double constant,\n        const Options::Context& context = {});\n\n  double dirichlet_weight() const { return dirichlet_weight_; }\n  double neumann_weight() const { return neumann_weight_; }\n  double constant() const { return constant_; }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {1, neumann_weight_ == 0.\n                   ? elliptic::BoundaryConditionType::Dirichlet\n                   : elliptic::BoundaryConditionType::Neumann};\n  }\n\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(gsl::not_null<Scalar<DataVector>*> field,\n             gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient,\n             const tnsr::i<DataVector, Dim>& deriv_field) const;\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> field_correction,\n      gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient_correction,\n      const tnsr::i<DataVector, Dim>& deriv_field_correction) const;\n\n  void pup(PUP::er& p) override;\n\n private:\n  double dirichlet_weight_ = std::numeric_limits<double>::signaling_NaN();\n  double neumann_weight_ = std::numeric_limits<double>::signaling_NaN();\n  double constant_ = std::numeric_limits<double>::signaling_NaN();\n};\n\ntemplate <size_t Dim>\nbool operator==(const Robin<Dim>& lhs, const Robin<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const Robin<Dim>& lhs, const Robin<Dim>& rhs);\n\n}  // namespace Poisson::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Poisson)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Equations.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Equations.hpp\n  FirstOrderSystem.hpp\n  Geometry.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Utilities\n  INTERFACE\n  GeneralRelativity\n  LinearOperators\n  )\n\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Poisson/Equations.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Poisson {\n\ntemplate <typename DataType, size_t Dim>\nvoid flat_cartesian_fluxes(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const tnsr::i<DataType, Dim>& field_gradient) {\n  for (size_t d = 0; d < Dim; d++) {\n    flux_for_field->get(d) = field_gradient.get(d);\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid curved_fluxes(const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                   const tnsr::II<DataVector, Dim>& inv_spatial_metric,\n                   const tnsr::i<DataType, Dim>& field_gradient) {\n  raise_or_lower_index(flux_for_field, field_gradient, inv_spatial_metric);\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid fluxes_on_face(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                    const tnsr::I<DataVector, Dim>& face_normal_vector,\n                    const Scalar<DataType>& field) {\n  std::copy(face_normal_vector.begin(), face_normal_vector.end(),\n            flux_for_field->begin());\n  for (size_t d = 0; d < Dim; d++) {\n    flux_for_field->get(d) *= get(field);\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid add_curved_sources(const gsl::not_null<Scalar<DataType>*> source_for_field,\n                        const tnsr::i<DataVector, Dim>& christoffel_contracted,\n                        const tnsr::I<DataType, Dim>& flux_for_field) {\n  get(*source_for_field) -=\n      get(dot_product(christoffel_contracted, flux_for_field));\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid Fluxes<Dim, Geometry::FlatCartesian, DataType>::apply(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const Scalar<DataType>& /*field*/,\n    const tnsr::i<DataType, Dim>& field_gradient) {\n  flat_cartesian_fluxes(flux_for_field, field_gradient);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid Fluxes<Dim, Geometry::FlatCartesian, DataType>::apply(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const tnsr::i<DataVector, Dim>& /*face_normal*/,\n    const tnsr::I<DataVector, Dim>& face_normal_vector,\n    const Scalar<DataType>& field) {\n  fluxes_on_face(flux_for_field, face_normal_vector, field);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid Fluxes<Dim, Geometry::Curved, DataType>::apply(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const tnsr::II<DataVector, Dim>& inv_spatial_metric,\n    const Scalar<DataType>& /*field*/,\n    const tnsr::i<DataType, Dim>& field_gradient) {\n  curved_fluxes(flux_for_field, inv_spatial_metric, field_gradient);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid Fluxes<Dim, Geometry::Curved, DataType>::apply(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const tnsr::II<DataVector, Dim>& /*inv_spatial_metric*/,\n    const tnsr::i<DataVector, Dim>& /*face_normal*/,\n    const tnsr::I<DataVector, Dim>& face_normal_vector,\n    const Scalar<DataType>& field) {\n  fluxes_on_face(flux_for_field, face_normal_vector, field);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid Sources<Dim, Geometry::Curved, DataType>::apply(\n    const gsl::not_null<Scalar<DataType>*> equation_for_field,\n    const tnsr::i<DataVector, Dim>& christoffel_contracted,\n    const Scalar<DataType>& /*field*/,\n    const tnsr::i<DataType, Dim>& /*field_gradient*/,\n    const tnsr::I<DataType, Dim>& field_flux) {\n  add_curved_sources(equation_for_field, christoffel_contracted, field_flux);\n}\n\n}  // namespace Poisson\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void Poisson::flat_cartesian_fluxes(                               \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data)>*>,                  \\\n      const tnsr::i<DTYPE(data), DIM(data)>&);                                \\\n  template void Poisson::curved_fluxes(                                       \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data)>*>,                  \\\n      const tnsr::II<DataVector, DIM(data)>&,                                 \\\n      const tnsr::i<DTYPE(data), DIM(data)>&);                                \\\n  template void Poisson::fluxes_on_face(                                      \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data)>*>,                  \\\n      const tnsr::I<DataVector, DIM(data)>&, const Scalar<DTYPE(data)>&);     \\\n  template void Poisson::add_curved_sources(                                  \\\n      const gsl::not_null<Scalar<DTYPE(data)>*>,                              \\\n      const tnsr::i<DataVector, DIM(data)>&,                                  \\\n      const tnsr::I<DTYPE(data), DIM(data)>&);                                \\\n  template class Poisson::Fluxes<DIM(data), Poisson::Geometry::FlatCartesian, \\\n                                 DTYPE(data)>;                                \\\n  template class Poisson::Fluxes<DIM(data), Poisson::Geometry::Curved,        \\\n                                 DTYPE(data)>;                                \\\n  template class Poisson::Sources<DIM(data), Poisson::Geometry::Curved,       \\\n                                  DTYPE(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, ComplexDataVector), (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Poisson {\ntemplate <size_t Dim, Geometry BackgroundGeometry,\n          typename DataType = DataVector>\nstruct Fluxes;\ntemplate <size_t Dim, Geometry BackgroundGeometry,\n          typename DataType = DataVector>\nstruct Sources;\n}  // namespace Poisson\n/// \\endcond\n\nnamespace Poisson {\n\n/*!\n * \\brief Compute the fluxes \\f$F^i=\\partial_i u(x)\\f$ for the Poisson\n * equation on a flat spatial metric in Cartesian coordinates.\n */\ntemplate <typename DataType, size_t Dim>\nvoid flat_cartesian_fluxes(\n    gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const tnsr::i<DataType, Dim>& field_gradient);\n\n/*!\n * \\brief Compute the fluxes \\f$F^i=\\gamma^{ij}\\partial_j u(x)\\f$\n * for the curved-space Poisson equation on a spatial metric \\f$\\gamma_{ij}\\f$.\n */\ntemplate <typename DataType, size_t Dim>\nvoid curved_fluxes(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                   const tnsr::II<DataVector, Dim>& inv_spatial_metric,\n                   const tnsr::i<DataType, Dim>& field_gradient);\n\n/*!\n * \\brief Compute the fluxes $F^i=\\gamma^{ij} n_j u$ where $n_j$ is the\n * `face_normal`.\n *\n * The `face_normal_vector` is $\\gamma^{ij} n_j$.\n */\ntemplate <typename DataType, size_t Dim>\nvoid fluxes_on_face(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                    const tnsr::I<DataVector, Dim>& face_normal_vector,\n                    const Scalar<DataType>& field);\n\n/*!\n * \\brief Add the sources \\f$S=-\\Gamma^i_{ij}v^j\\f$\n * for the curved-space Poisson equation on a spatial metric \\f$\\gamma_{ij}\\f$.\n *\n * These sources arise from the non-principal part of the Laplacian on a\n * non-Euclidean background.\n */\ntemplate <typename DataType, size_t Dim>\nvoid add_curved_sources(gsl::not_null<Scalar<DataType>*> source_for_field,\n                        const tnsr::i<DataVector, Dim>& christoffel_contracted,\n                        const tnsr::I<DataType, Dim>& flux_for_field);\n\n/*!\n * \\brief Compute the fluxes \\f$F^i\\f$ for the Poisson equation on a flat\n * metric in Cartesian coordinates.\n *\n * \\see Poisson::FirstOrderSystem\n */\ntemplate <size_t Dim, typename DataType>\nstruct Fluxes<Dim, Geometry::FlatCartesian, DataType> {\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = true;\n  static constexpr bool is_discontinuous = false;\n  static void apply(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                    const Scalar<DataType>& field,\n                    const tnsr::i<DataType, Dim>& field_gradient);\n  static void apply(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                    const tnsr::i<DataVector, Dim>& face_normal,\n                    const tnsr::I<DataVector, Dim>& face_normal_vector,\n                    const Scalar<DataType>& field);\n};\n\n/*!\n * \\brief Compute the fluxes \\f$F^i\\f$ for the curved-space Poisson equation\n * on a spatial metric \\f$\\gamma_{ij}\\f$.\n *\n * \\see Poisson::FirstOrderSystem\n */\ntemplate <size_t Dim, typename DataType>\nstruct Fluxes<Dim, Geometry::Curved, DataType> {\n  using argument_tags =\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, Dim>>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = true;\n  static constexpr bool is_discontinuous = false;\n  static void apply(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                    const tnsr::II<DataVector, Dim>& inv_spatial_metric,\n                    const Scalar<DataType>& field,\n                    const tnsr::i<DataType, Dim>& field_gradient);\n  static void apply(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                    const tnsr::II<DataVector, Dim>& inv_spatial_metric,\n                    const tnsr::i<DataVector, Dim>& face_normal,\n                    const tnsr::I<DataVector, Dim>& face_normal_vector,\n                    const Scalar<DataType>& field);\n};\n\n/*!\n * \\brief Add the sources \\f$S\\f$ for the curved-space Poisson equation\n * on a spatial metric \\f$\\gamma_{ij}\\f$.\n *\n * \\see Poisson::FirstOrderSystem\n */\ntemplate <size_t Dim, typename DataType>\nstruct Sources<Dim, Geometry::Curved, DataType> {\n  using argument_tags = tmpl::list<\n      gr::Tags::SpatialChristoffelSecondKindContracted<DataVector, Dim>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(gsl::not_null<Scalar<DataType>*> equation_for_field,\n                    const tnsr::i<DataVector, Dim>& christoffel_contracted,\n                    const Scalar<DataType>& field,\n                    const tnsr::i<DataType, Dim>& /*field_gradient*/,\n                    const tnsr::I<DataType, Dim>& field_flux);\n};\n\n}  // namespace Poisson\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class Poisson::FirstOrderSystem\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Equations.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson {\n\n/*!\n * \\brief The Poisson equation formulated as a set of coupled first-order PDEs.\n *\n * \\details This system formulates the Poisson equation \\f$-\\Delta_\\gamma u(x) =\n * f(x)\\f$ on a background metric \\f$\\gamma_{ij}\\f$ as the set of coupled\n * first-order PDEs\n *\n * \\f{align*}\n * -\\partial_i v^i - \\Gamma^i_{ij} v^j = f(x) \\\\\n * v^i = \\gamma^{ij} \\partial_j u(x)\n * \\f}\n *\n * where \\f$\\Gamma^i_{jk}=\\frac{1}{2}\\gamma^{il}\\left(\\partial_j\\gamma_{kl}\n * +\\partial_k\\gamma_{jl}-\\partial_l\\gamma_{jk}\\right)\\f$ are the Christoffel\n * symbols of the second kind of the background metric \\f$\\gamma_{ij}\\f$. The\n * background metric \\f$\\gamma_{ij}\\f$ and the Christoffel symbols derived from\n * it are assumed to be independent of the variables \\f$u\\f$, i.e.\n * constant throughout an iterative elliptic solve.\n *\n * The system can be formulated in terms of these fluxes and sources (see\n * `elliptic::protocols::FirstOrderSystem`):\n *\n * \\f{align*}\n * F^i &= v^i = \\gamma^{ij} \\partial_j u \\\\\n * S &= -\\Gamma^i_{ij} v^j \\\\\n * f &= f(x) \\text{.}\n * \\f}\n *\n * The fluxes and sources simplify significantly when the background metric is\n * flat and we employ Cartesian coordinates so \\f$\\gamma_{ij} = \\delta_{ij}\\f$\n * and \\f$\\Gamma^i_{jk} = 0\\f$. Set the template parameter `BackgroundGeometry`\n * to `Poisson::Geometry::FlatCartesian` to specialize the system for this case.\n * Set it to `Poisson::Geometry::Curved` for the general case.\n *\n * ## Complex Poisson equation\n *\n * This system can also be used to solve the complex Poisson equation where\n * $u(x)$ and $f(x)$ are complex-valued, by setting the `DataType` template\n * parameter to `ComplexDataVector`. Note that the real and imaginary sectors of\n * the equations decouple, so they are essentially two independent Poisson\n * equations. This is useful for testing the elliptic solver with complex-valued\n * fields, and also as building blocks for other Poisson-like systems of\n * equations that have additional complex-valued source terms.\n */\ntemplate <size_t Dim, Geometry BackgroundGeometry,\n          typename DataType = DataVector>\nstruct FirstOrderSystem\n    : tt::ConformsTo<elliptic::protocols::FirstOrderSystem> {\n  static constexpr size_t volume_dim = Dim;\n\n  using primal_fields = tmpl::list<Tags::Field<DataType>>;\n  // We just use the standard `Flux` prefix because the fluxes don't have\n  // symmetries and we don't need to give them a particular meaning.\n  using primal_fluxes = tmpl::list<\n      ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>>;\n\n  using background_fields = tmpl::conditional_t<\n      BackgroundGeometry == Geometry::FlatCartesian, tmpl::list<>,\n      tmpl::list<\n          gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n          gr::Tags::SpatialChristoffelSecondKindContracted<DataVector, Dim>>>;\n  using inv_metric_tag =\n      tmpl::conditional_t<BackgroundGeometry == Geometry::FlatCartesian, void,\n                          gr::Tags::InverseSpatialMetric<DataVector, Dim>>;\n\n  using fluxes_computer = Fluxes<Dim, BackgroundGeometry, DataType>;\n  using sources_computer =\n      tmpl::conditional_t<BackgroundGeometry == Geometry::FlatCartesian, void,\n                          Sources<Dim, BackgroundGeometry, DataType>>;\n\n  using boundary_conditions_base =\n      elliptic::BoundaryConditions::BoundaryCondition<Dim>;\n  using modify_boundary_data = void;\n};\n}  // namespace Poisson\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/Geometry.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Poisson {\n/// \\brief Types of background geometries for the Poisson equation\nenum class Geometry {\n  /// Euclidean (flat) manifold with Cartesian coordinates, i.e. the metric has\n  /// components \\f$\\gamma_{ij} = \\delta_{ij}\\f$ in these coordinates and thus\n  /// all Christoffel symbols vanish: \\f$\\Gamma^i_{jk}=0\\f$.\n  FlatCartesian,\n  /// The manifold is either curved or employs curved coordinates, so\n  /// non-vanishing Christoffel symbols must be taken into account.\n  Curved\n};\n}  // namespace Poisson\n"
  },
  {
    "path": "src/Elliptic/Systems/Poisson/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines DataBox tags for the Poisson system\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving a Poisson equation \\f$-\\Delta u(x)=f(x)\\f$.\n */\nnamespace Poisson {\nnamespace Tags {\n\n/*!\n * \\brief The scalar field \\f$u(x)\\f$ to solve for\n */\ntemplate <typename DataType>\nstruct Field : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n}  // namespace Tags\n}  // namespace Poisson\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/AmrCriteria/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY PuncturesAmrCriteria)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  RefineAtPunctures.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  RefineAtPunctures.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Amr\n  AmrCriteria\n  DataStructures\n  DomainStructure\n  Elliptic\n  InitialDataUtilities\n  Options\n  Parallel\n  Serialization\n  Utilities\n  PRIVATE\n  Domain\n  PuncturesAnalyticData\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/AmrCriteria/RefineAtPunctures.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Punctures/AmrCriteria/RefineAtPunctures.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Punctures/MultiplePunctures.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Punctures::AmrCriteria {\n\nstd::array<amr::Flag, 3> RefineAtPunctures::impl(\n    const elliptic::analytic_data::Background& background,\n    const Domain<3>& domain, const ElementId<3>& element_id) {\n  // Casting down to MultiplePunctures because that's the only background class\n  // we have\n  const auto& punctures =\n      dynamic_cast<const Punctures::AnalyticData::MultiplePunctures&>(\n          background)\n          .punctures();\n  // Split (h-refine) the element if it contains a puncture\n  const auto& block = domain.blocks()[element_id.block_id()];\n  for (const auto& puncture : punctures) {\n    // Check if the puncture is in the block\n    const auto block_logical_coords = block_logical_coordinates_single_point(\n        tnsr::I<double, 3>{puncture.position}, block);\n    if (not block_logical_coords.has_value()) {\n      continue;\n    }\n    // Check if the puncture is in the element\n    if (element_logical_coordinates(*block_logical_coords, element_id)) {\n      return make_array<3>(amr::Flag::Split);\n    }\n  }\n  return make_array<3>(amr::Flag::DoNothing);\n}\n\nPUP::able::PUP_ID RefineAtPunctures::my_PUP_ID = 0;  // NOLINT\n}  // namespace Punctures::AmrCriteria\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/AmrCriteria/RefineAtPunctures.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures::AmrCriteria {\n\n/*!\n * \\brief h-refine (split) elements containing a puncture\n *\n * This refinement scheme is expected to yield exponential convergence, despite\n * the presence of the C^2 punctures.\n */\nclass RefineAtPunctures : public amr::Criterion {\n public:\n  using options = tmpl::list<>;\n\n  static constexpr Options::String help = {\n      \"h-refine (split) elements containing a puncture.\"};\n\n  RefineAtPunctures() = default;\n\n  /// \\cond\n  explicit RefineAtPunctures(CkMigrateMessage* msg) : Criterion(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RefineAtPunctures);  // NOLINT\n  /// \\endcond\n\n  amr::Criteria::Type type() override { return amr::Criteria::Type::h; }\n\n  std::string observation_name() override { return \"RefineAtPunctures\"; }\n\n  using argument_tags = tmpl::list<\n      elliptic::Tags::Background<elliptic::analytic_data::Background>,\n      domain::Tags::Domain<3>>;\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  template <typename Metavariables>\n  std::array<amr::Flag, 3> operator()(\n      const elliptic::analytic_data::Background& background,\n      const Domain<3>& domain, Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<3>& element_id) const {\n    return impl(background, domain, element_id);\n  }\n\n private:\n  static std::array<amr::Flag, 3> impl(\n      const elliptic::analytic_data::Background& background,\n      const Domain<3>& domain, const ElementId<3>& element_id);\n};\n\n}  // namespace Punctures::AmrCriteria\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY PuncturesBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Flatness.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Flatness.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Elliptic\n  Options\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Elliptic/Systems/Punctures/BoundaryConditions/Flatness.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures::BoundaryConditions {\n\nusing standard_boundary_conditions = tmpl::list<Flatness>;\n\n}\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/BoundaryConditions/Flatness.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Punctures/BoundaryConditions/Flatness.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Punctures::BoundaryConditions {\n\nvoid Flatness::apply(\n    const gsl::not_null<Scalar<DataVector>*> field,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient,\n    const tnsr::i<DataVector, 3>& /*field_gradient*/,\n    const tnsr::I<DataVector, 3>& x) {\n  get(*n_dot_field_gradient) = -get(*field) / get(magnitude(x));\n}\n\nvoid Flatness::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> field_correction,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient_correction,\n    const tnsr::i<DataVector, 3>& /*field_gradient*/,\n    const tnsr::I<DataVector, 3>& x) {\n  get(*n_dot_field_gradient_correction) =\n      -get(*field_correction) / get(magnitude(x));\n}\n\nbool operator==(const Flatness& /*lhs*/, const Flatness& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const Flatness& lhs, const Flatness& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID Flatness::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace Punctures::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/BoundaryConditions/Flatness.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Punctures::BoundaryConditions {\n\n/// Impose asymptotic flatness boundary conditions $\\partial_r(ru)=0$\nclass Flatness : public elliptic::BoundaryConditions::BoundaryCondition<3> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<3>;\n\n public:\n  static constexpr Options::String help = \"Asymptotic flatness d_r(ru)=0\";\n  using options = tmpl::list<>;\n\n  Flatness() = default;\n  Flatness(const Flatness&) = default;\n  Flatness& operator=(const Flatness&) = default;\n  Flatness(Flatness&&) = default;\n  Flatness& operator=(Flatness&&) = default;\n  ~Flatness() = default;\n\n  /// \\cond\n  explicit Flatness(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Flatness);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<Flatness>(*this);\n  }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {elliptic::BoundaryConditionType::Neumann};\n  }\n\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>>;\n  using volume_tags = tmpl::list<>;\n\n  static void apply(gsl::not_null<Scalar<DataVector>*> field,\n                    gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient,\n                    const tnsr::i<DataVector, 3>& field_gradient,\n                    const tnsr::I<DataVector, 3>& x);\n\n  using argument_tags_linearized =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  static void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> field_correction,\n      gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient_correction,\n      const tnsr::i<DataVector, 3>& field_gradient,\n      const tnsr::I<DataVector, 3>& x);\n};\n\nbool operator==(const Flatness& lhs, const Flatness& rhs);\nbool operator!=(const Flatness& lhs, const Flatness& rhs);\n\n}  // namespace Punctures::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Punctures)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Sources.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FirstOrderSystem.hpp\n  Punctures.hpp\n  Sources.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Utilities\n  INTERFACE\n  Poisson\n  )\n\nadd_subdirectory(AmrCriteria)\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Equations.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Elliptic/Systems/Punctures/Sources.hpp\"\n#include \"Elliptic/Systems/Punctures/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures {\n\n/*!\n * \\brief The puncture equation, formulated as a set of coupled first-order\n * partial differential equations\n *\n * See \\ref Punctures for details on the puncture equation. Since it is just a\n * flat-space Poisson equation with nonlinear sources, we can reuse the\n * Euclidean Poisson fluxes.\n */\nstruct FirstOrderSystem\n    : tt::ConformsTo<elliptic::protocols::FirstOrderSystem> {\n  static constexpr size_t volume_dim = 3;\n\n  using primal_fields = tmpl::list<Tags::Field>;\n  using primal_fluxes =\n      tmpl::list<::Tags::Flux<Tags::Field, tmpl::size_t<3>, Frame::Inertial>>;\n\n  using background_fields = tmpl::list<Tags::Alpha, Tags::Beta>;\n  using inv_metric_tag = void;\n\n  using fluxes_computer = Poisson::Fluxes<3, Poisson::Geometry::FlatCartesian>;\n  using sources_computer = Sources;\n  using sources_computer_linearized = LinearizedSources;\n\n  using boundary_conditions_base =\n      elliptic::BoundaryConditions::BoundaryCondition<3>;\n  using modify_boundary_data = void;\n};\n\n}  // namespace Punctures\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/Punctures.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Documents the `Punctures` namespace\n\n#pragma once\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving the puncture equation\n *\n * The puncture equation\n *\n * \\begin{equation}\\label{eq:puncture_eqn}\n * -\\nabla^2 u = \\beta \\left(\\alpha \\left(1 + u\\right) + 1\\right)^{-7}\n * \\end{equation}\n *\n * is a nonlinear Poisson-type elliptic PDE for the \"puncture field\" $u$. See\n * Eq. (12.52) and surrounding discussion in \\cite BaumgarteShapiro, or\n * \\cite BrandtBruegmann1997 for an introduction. To arrive at the puncture\n * equation we assume conformal flatness and maximal slicing in vacuum so the\n * Einstein momentum constraint becomes homogeneous:\n *\n * \\begin{equation}\\label{eq:mom_constraint}\n * \\nabla_j \\bar{A}^{ij} = 0\n * \\end{equation}\n *\n * Here, $\\nabla$ is the flat-space covariant derivate. $\\bar{A}^{ij}$ is the\n * conformal traceless extrinsic curvature that composes the extrinsic curvature\n * as\n *\n * \\begin{equation}\n * K_{ij} = \\psi^{-2} \\bar{A}_{ij} + \\frac{1}{3} \\gamma_{ij} K\n * \\end{equation}\n *\n * (where $K=0$ under maximal slicing). $\\psi$ is the conformal factor that\n * composes the spatial metric as\n *\n * \\begin{equation}\n * \\gamma_{ij} = \\psi^4 \\bar{\\gamma}_{ij}\n * \\end{equation}\n *\n * (where $\\bar{\\gamma}_{ij} = \\delta{ij}$ under conformal flatness and in\n * Cartesian coordinates).\n *\n * The momentum constraint ($\\ref{eq:mom_constraint}$) is solved analytically by\n * the Bowen-York extrinsic curvature\n *\n * \\begin{equation}\n * \\bar{A}^{ij} = \\frac{3}{2} \\frac{1}{r_C^2} \\left(\n * 2 P^{(i} n^{j)} - (\\delta^{ij} - n^i n^j) P^k n^k\n * + \\frac{4}{r_C} n^{(i} \\epsilon^{j)kl} S^k n^l\\right)\n * \\end{equation}\n *\n * representing a black hole with linear momentum $\\mathbf{P}$ and angular\n * momentum $\\mathbf{S}$ at position $\\mathbf{C}$. The quantity\n * $r_C=||\\mathbf{x}-\\mathbf{C}||$ is the Euclidean coordinate distance to the\n * black hole, and $\\mathbf{n}=(\\mathbf{x}-\\mathbf{C})/r_C$ is the radial unit\n * normal to the black hole. Since the momentum constraint is linear, any\n * superposition of $\\bar{A}^{ij}$ is also a solution to the momentum\n * constraint, allowing to represent multiple black holes.\n *\n * Only the Einstein Hamiltonian constraint remains to be solved numerically for\n * the conformal factor:\n *\n * \\begin{equation}\n * \\nabla^2 \\psi = \\frac{1}{8} \\psi^{-7} \\bar{A}_{ij} \\bar{A}^{ij}\n * \\end{equation}\n *\n * It reduces to the puncture equation ($\\ref{eq:puncture_eqn}$) when we\n * decompose the conformal factor as:\n *\n * \\begin{equation}\n * \\psi = 1 + \\frac{1}{\\alpha} + u\n * \\end{equation}\n *\n * where we define\n *\n * \\begin{equation}\n * \\frac{1}{\\alpha} = \\sum_I \\frac{M_I}{2 r_I}\n * \\end{equation}\n *\n * and\n *\n * \\begin{equation}\n * \\beta = \\frac{1}{8} \\alpha^7 \\bar{A}_{ij} \\bar{A}^{ij}.\n * \\end{equation}\n *\n * Here, $M_I$ is the \"puncture mass\" (or \"bare mass\") parameter for the $I$th\n * black hole at position $\\mathbf{C}_I$, and $\\bar{A}_{ij}$ is the\n * superposition of the Bowen-York extrinsic curvature of the black holes with\n * the parameters defined above. Note that the definition of $\\frac{1}{\\alpha}$\n * in Eq. (12.51) in \\cite BaumgarteShapiro is missing factors of $\\frac{1}{2}$,\n * but their Eq. (3.23) includes them, as does Eq. (8) in\n * \\cite BrandtBruegmann1997 (though the latter includes the unit offset in\n * the definition of $u$).\n */\nnamespace Punctures {}\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/Sources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Punctures/Sources.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Punctures {\n\nvoid add_sources(const gsl::not_null<Scalar<DataVector>*> puncture_equation,\n                 const Scalar<DataVector>& alpha,\n                 const Scalar<DataVector>& beta,\n                 const Scalar<DataVector>& field) {\n  get(*puncture_equation) -=\n      get(beta) / pow<7>(get(alpha) * (get(field) + 1.) + 1.);\n}\n\nvoid add_linearized_sources(\n    const gsl::not_null<Scalar<DataVector>*> linearized_puncture_equation,\n    const Scalar<DataVector>& alpha, const Scalar<DataVector>& beta,\n    const Scalar<DataVector>& field,\n    const Scalar<DataVector>& field_correction) {\n  get(*linearized_puncture_equation) +=\n      7. * get(alpha) * get(beta) /\n      pow<8>(get(alpha) * (get(field) + 1.) + 1.) * get(field_correction);\n}\n\nvoid Sources::apply(const gsl::not_null<Scalar<DataVector>*> puncture_equation,\n                    const Scalar<DataVector>& alpha,\n                    const Scalar<DataVector>& beta,\n                    const Scalar<DataVector>& field,\n                    const tnsr::i<DataVector, 3>& /*field_gradient*/,\n                    const tnsr::I<DataVector, 3>& /*field_flux*/) {\n  add_sources(puncture_equation, alpha, beta, field);\n}\n\nvoid LinearizedSources::apply(\n    const gsl::not_null<Scalar<DataVector>*> linearized_puncture_equation,\n    const Scalar<DataVector>& alpha, const Scalar<DataVector>& beta,\n    const Scalar<DataVector>& field, const Scalar<DataVector>& field_correction,\n    const tnsr::i<DataVector, 3>& /*field_gradient_correction*/,\n    const tnsr::I<DataVector, 3>& /*field_flux_correction*/) {\n  add_linearized_sources(linearized_puncture_equation, alpha, beta, field,\n                         field_correction);\n}\n\n}  // namespace Punctures\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/Sources.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Punctures/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Punctures {\n\n/*!\n * \\brief Add the nonlinear sources for the puncture equation.\n *\n * Adds $-\\beta \\left(\\alpha \\left(1 + u\\right) + 1\\right)^{-7}$.\n *\n * \\see Punctures\n */\nvoid add_sources(gsl::not_null<Scalar<DataVector>*> puncture_equation,\n                 const Scalar<DataVector>& alpha,\n                 const Scalar<DataVector>& beta,\n                 const Scalar<DataVector>& field);\n\n/*!\n * \\brief Add the linearized sources for the puncture equation.\n *\n * Adds $-\\frac{d}{du}(\\beta \\left(\\alpha \\left(1 + u\\right) + 1\\right)^{-7})$.\n *\n * \\see Punctures\n */\nvoid add_linearized_sources(\n    gsl::not_null<Scalar<DataVector>*> linearized_puncture_equation,\n    const Scalar<DataVector>& alpha, const Scalar<DataVector>& beta,\n    const Scalar<DataVector>& field,\n    const Scalar<DataVector>& field_correction);\n\n/// The sources \\f$S\\f$ for the first-order formulation of the puncture equation\n///\n/// \\see elliptic::protocols::FirstOrderSystem\nstruct Sources {\n  using argument_tags = tmpl::list<Tags::Alpha, Tags::Beta>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(gsl::not_null<Scalar<DataVector>*> puncture_equation,\n                    const Scalar<DataVector>& alpha,\n                    const Scalar<DataVector>& beta,\n                    const Scalar<DataVector>& field,\n                    const tnsr::i<DataVector, 3>& /*field_gradient*/,\n                    const tnsr::I<DataVector, 3>& field_flux);\n};\n\n/// The linearization of the sources \\f$S\\f$ for the first-order formulation of\n/// the puncture equation\n///\n/// \\see elliptic::protocols::FirstOrderSystem\nstruct LinearizedSources {\n  using argument_tags = tmpl::list<Tags::Alpha, Tags::Beta, Tags::Field>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> linearized_puncture_equation,\n      const Scalar<DataVector>& alpha, const Scalar<DataVector>& beta,\n      const Scalar<DataVector>& field,\n      const Scalar<DataVector>& field_correction,\n      const tnsr::i<DataVector, 3>& /*field_gradient_correction*/,\n      const tnsr::I<DataVector, 3>& field_flux_correction);\n};\n\n}  // namespace Punctures\n"
  },
  {
    "path": "src/Elliptic/Systems/Punctures/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Punctures {\n/// Tags related to the puncture equation\nnamespace Tags {\n\n/*!\n * \\brief The puncture field $u(x)$ to solve for\n *\n * \\see Punctures\n */\nstruct Field : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The source field $\\alpha(x)$\n *\n * \\see Punctures\n */\nstruct Alpha : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The source field $\\beta(x)$\n *\n * \\see Punctures\n */\nstruct Beta : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The traceless conformal extrinsic curvature $\\bar{A}_{ij}$\n *\n * \\see Punctures\n */\nstruct TracelessConformalExtrinsicCurvature : db::SimpleTag {\n  using type = tnsr::II<DataVector, 3>;\n};\n\n}  // namespace Tags\n}  // namespace Punctures\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  DoNothing.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Elliptic\n  Options\n  Parallel\n  PoissonBoundaryConditions\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/BoundaryConditions/DoNothing.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace sgb::BoundaryConditions {\n\n/// Do not apply a boundary condition, used exclusively for singular boundary\n/// value problems.\n\nclass DoNothing : public elliptic::BoundaryConditions::BoundaryCondition<3> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<3>;\n\n public:\n  static constexpr Options::String help =\n      \"Do not apply a boundary condition, used exclusively for singular \"\n      \"boundary value problems.\";\n\n  using options = tmpl::list<>;\n\n  DoNothing() = default;\n  DoNothing(const DoNothing&) = default;\n  DoNothing& operator=(const DoNothing&) = default;\n  DoNothing(DoNothing&&) = default;\n  DoNothing& operator=(DoNothing&&) = default;\n  ~DoNothing() override = default;\n\n  /// \\cond\n  explicit DoNothing(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(DoNothing);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<DoNothing>(*this);\n  }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {1, elliptic::BoundaryConditionType::Dirichlet};\n  }\n\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(gsl::not_null<Scalar<DataVector>*> /*field*/,\n             gsl::not_null<Scalar<DataVector>*> /*n_dot_field_gradient*/,\n             const tnsr::i<DataVector, 3>& /*deriv_field*/) const {};\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> /*field_correction*/,\n      gsl::not_null<Scalar<DataVector>*>\n      /*n_dot_field_gradient_correction*/,\n      const tnsr::i<DataVector, 3>& /*deriv_field_correction*/) const {};\n};\n\ninline bool operator==(const DoNothing& /*lhs*/, const DoNothing& /*rhs*/) {\n  return true;\n}\n\ninline bool operator!=(const DoNothing& /*lhs*/, const DoNothing& /*rhs*/) {\n  return false;\n}\n\n/// \\cond\nPUP::able::PUP_ID DoNothing::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace sgb::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Elliptic/Systems/Poisson/BoundaryConditions/Robin.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/BoundaryConditions/DoNothing.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace sgb::BoundaryConditions {\n\nusing standard_boundary_conditions =\n    tmpl::list<::Poisson::BoundaryConditions::Robin<3>, DoNothing>;\n\n}  // namespace sgb::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarGaussBonnet)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Equations.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Equations.hpp\n  FirstOrderSystem.hpp\n  ScalarGaussBonnet.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  ErrorHandling\n  GeneralRelativity\n  Utilities\n  Xcts\n  INTERFACE\n  LinearOperators\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/ScalarGaussBonnet/Equations.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace sgb {\n\nvoid curved_fluxes(const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_field,\n                   const tnsr::II<DataVector, 3>& inv_conformal_metric,\n                   const tnsr::I<DataVector, 3>& shift,\n                   const Scalar<DataVector>& lapse,\n                   const Scalar<DataVector>& conformal_factor,\n                   const tnsr::i<DataVector, 3>& field_gradient) {\n  raise_or_lower_index(flux_for_field, field_gradient, inv_conformal_metric);\n  const DataVector conformal_factor_power = pow<4>(get(conformal_factor));\n  const DataVector shift_term =\n      get(dot_product(shift, field_gradient)) / square(get(lapse));\n\n  for (size_t i = 0; i < 3; i++) {\n    flux_for_field->get(i) /= conformal_factor_power;\n    flux_for_field->get(i) -= shift.get(i) * shift_term;\n  }\n}\n\nvoid face_fluxes(gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_field,\n                 const tnsr::II<DataVector, 3>& inv_conformal_metric,\n                 const tnsr::I<DataVector, 3>& shift,\n                 const Scalar<DataVector>& lapse,\n                 const Scalar<DataVector>& conformal_factor,\n                 const tnsr::i<DataVector, 3>& face_normal,\n                 const Scalar<DataVector>& field) {\n  raise_or_lower_index(flux_for_field, face_normal, inv_conformal_metric);\n  const DataVector conformal_factor_power = pow<4>(get(conformal_factor));\n  // This term is constant unless the domain changes (i.e. through AMR), and\n  // so in a given non-linear elliptic solve could be precomputed once at the\n  // start as a possible performance optimizaton.\n  const DataVector shift_term =\n      get(dot_product(shift, face_normal)) / square(get(lapse));\n\n  for (size_t i = 0; i < 3; i++) {\n    flux_for_field->get(i) /= conformal_factor_power;\n    flux_for_field->get(i) -= shift.get(i) * shift_term;\n    flux_for_field->get(i) *= get(field);\n  }\n}\n\nvoid add_curved_sources(\n    const gsl::not_null<Scalar<DataVector>*> source_for_field,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::I<DataVector, 3>& flux_for_field,\n    const tnsr::i<DataVector, 3>& deriv_lapse, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3>& conformal_factor_deriv) {\n  get(*source_for_field) -=\n      get(dot_product(deriv_lapse, flux_for_field)) / get(lapse);\n  get(*source_for_field) -=\n      get(dot_product(conformal_christoffel_contracted, flux_for_field));\n  get(*source_for_field) -=\n      6. * get(dot_product(conformal_factor_deriv, flux_for_field)) /\n      get(conformal_factor);\n}\n\nvoid add_GB_terms(gsl::not_null<Scalar<DataVector>*> scalar_tensor_equation,\n                  const double eps2, const double eps4,\n                  const Scalar<DataVector>& weyl_electric,\n                  const Scalar<DataVector>& weyl_magnetic,\n                  const Scalar<DataVector>& field) {\n  get(*scalar_tensor_equation) -=\n      2. * (weyl_electric.get() - weyl_magnetic.get()) *\n      (eps2 * field.get() + eps4 * cube(get(field)));\n}\n\nvoid add_linearized_GB_terms(\n    gsl::not_null<Scalar<DataVector>*> linearized_scalar_tensor_equation,\n    const double eps2, const double eps4,\n    const Scalar<DataVector>& weyl_electric,\n    const Scalar<DataVector>& weyl_magnetic, const Scalar<DataVector>& field,\n    const Scalar<DataVector>& field_correction) {\n  get(*linearized_scalar_tensor_equation) -=\n      2. * (weyl_electric.get() - weyl_magnetic.get()) *\n      (eps2 * field_correction.get() +\n       3. * eps4 * square(get(field)) * field_correction.get());\n}\n\nvoid Fluxes::apply(const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_field,\n                   const tnsr::II<DataVector, 3>& inv_conformal_metric,\n                   const tnsr::I<DataVector, 3>& shift,\n                   const Scalar<DataVector>& lapse,\n                   const Scalar<DataVector>& conformal_factor,\n                   const Scalar<DataVector>& /*field*/,\n                   const tnsr::i<DataVector, 3>& field_gradient) {\n  curved_fluxes(flux_for_field, inv_conformal_metric, shift, lapse,\n                conformal_factor, field_gradient);\n}\n\nvoid Fluxes::apply(gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_field,\n                   const tnsr::II<DataVector, 3>& inv_conformal_metric,\n                   const tnsr::I<DataVector, 3>& shift,\n                   const Scalar<DataVector>& lapse,\n                   const Scalar<DataVector>& conformal_factor,\n                   const tnsr::i<DataVector, 3>& face_normal,\n                   const tnsr::I<DataVector, 3>& /*face_normal_vector*/,\n                   const Scalar<DataVector>& field) {\n  face_fluxes(flux_for_field, inv_conformal_metric, shift, lapse,\n              conformal_factor, face_normal, field);\n}\n\nvoid Sources::apply(\n    gsl::not_null<Scalar<DataVector>*> equation_for_field,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::i<DataVector, 3>& deriv_lapse, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3>& conformal_factor_deriv, const double& eps2,\n    const double& eps4, const Scalar<DataVector>& weyl_electric,\n    const Scalar<DataVector>& weyl_magnetic, const Scalar<DataVector>& field,\n    const tnsr::i<DataVector, 3>& /*field_gradient*/,\n    const tnsr::I<DataVector, 3>& field_flux) {\n  add_curved_sources(equation_for_field, conformal_christoffel_contracted,\n                     field_flux, deriv_lapse, lapse, conformal_factor,\n                     conformal_factor_deriv);\n  add_GB_terms(equation_for_field, eps2, eps4, weyl_electric, weyl_magnetic,\n               field);\n}\n\nvoid LinearizedSources::apply(\n    gsl::not_null<Scalar<DataVector>*> linearized_equation_for_field,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::i<DataVector, 3>& deriv_lapse, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3>& conformal_factor_deriv,\n    const Scalar<DataVector>& field, const double& eps2, const double& eps4,\n    const Scalar<DataVector>& weyl_electric,\n    const Scalar<DataVector>& weyl_magnetic,\n    const Scalar<DataVector>& field_correction,\n    const tnsr::i<DataVector, 3>& /*field_gradient_correction*/,\n    const tnsr::I<DataVector, 3>& field_flux_correction) {\n  add_curved_sources(linearized_equation_for_field,\n                     conformal_christoffel_contracted, field_flux_correction,\n                     deriv_lapse, lapse, conformal_factor,\n                     conformal_factor_deriv);\n  add_linearized_GB_terms(linearized_equation_for_field, eps2, eps4,\n                          weyl_electric, weyl_magnetic, field,\n                          field_correction);\n}\n\n}  // namespace sgb\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/Tags.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace sgb {\nstruct Fluxes;\nstruct Sources;\n}  // namespace sgb\n/// \\endcond\n\nnamespace sgb {\n\n/*!\n * \\brief Compute the fluxes $F^i=\\left(\\psi^{-4} \\tilde{\\gamma}^{ij}\n * -\\alpha^{2} \\beta^i \\beta^j \\right) \\partial_j \\Psi(x)$ for the scalar\n * equation in sGB gravity on a conformal metric $\\tilde{\\gamma}_{ij}$.\n */\nvoid curved_fluxes(gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_field,\n                   const tnsr::II<DataVector, 3>& inv_conformal_metric,\n                   const tnsr::I<DataVector, 3>& shift,\n                   const Scalar<DataVector>& lapse,\n                   const Scalar<DataVector>& conformal_factor,\n                   const tnsr::i<DataVector, 3>& field_gradient);\n\n/*!\n * \\brief Compute the fluxes $F^i=\\left(\\psi^{-4} \\tilde{\\gamma}^{ij}\n * -\\alpha^{2} \\beta^i \\beta^j \\right) \\partial_j \\Psi(x)$ for the scalar\n * equation in sGB gravity on a conformal metric $\\tilde{\\gamma}_{ij}$ on a face\n * normal.\n */\nvoid face_fluxes(gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_field,\n                 const tnsr::II<DataVector, 3>& inv_conformal_metric,\n                 const tnsr::I<DataVector, 3>& shift,\n                 const Scalar<DataVector>& lapse,\n                 const Scalar<DataVector>& conformal_factor,\n                 const tnsr::i<DataVector, 3>& face_normal,\n                 const Scalar<DataVector>& field);\n\n/*!\n * \\brief Adds the source terms arising from the $\\Box \\Psi$ term in the\n * equation of motion for the scalar field: $S=-\\tilde{\\Gamma}^i_{ij}F^j-F^j\n * \\alpha^{-1} \\partial_j \\alpha - 6F^j \\psi^{-1} \\partial_j \\psi$.*/\nvoid add_curved_sources(gsl::not_null<Scalar<DataVector>*> source_for_field,\n                        const tnsr::i<DataVector, 3>& christoffel_contracted,\n                        const tnsr::I<DataVector, 3>& flux_for_field,\n                        const tnsr::i<DataVector, 3>& deriv_lapse,\n                        const Scalar<DataVector>& lapse,\n                        const Scalar<DataVector>& conformal_factor,\n                        const tnsr::i<DataVector, 3>& conformal_factor_deriv);\n\n/*!\n * \\brief Add the sGB coupling term $\\mathcal{R} f'(\\Psi)=2(E-B)(\\epsilon_2 \\Psi\n * + \\epsilon_4 \\Psi^3)$.\n */\nvoid add_GB_terms(gsl::not_null<Scalar<DataVector>*> scalar_tensor_equation,\n                  double eps2, double eps4,\n                  const Scalar<DataVector>& weyl_electric,\n                  const Scalar<DataVector>& weyl_magnetic,\n                  const Scalar<DataVector>& field);\n\n/*!\n * \\brief Add sources arising from linearising the sGB coupling term.\n */\nvoid add_linearized_GB_terms(\n    gsl::not_null<Scalar<DataVector>*> linearized_scalar_tensor_equation,\n    double eps2, double eps4, const Scalar<DataVector>& weyl_electric,\n    const Scalar<DataVector>& weyl_magnetic, const Scalar<DataVector>& field,\n    const Scalar<DataVector>& field_correction);\n\n/*!\n * \\brief Compute the fluxes \\f$F^i\\f$ for the scalar equation in sGB gravity on\n * a spatial metric \\f$\\gamma_{ij}\\f$.\n */\nstruct Fluxes {\n  using argument_tags = tmpl::list<\n      Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n      sgb::Tags::RolledOffShift, gr::Tags::Lapse<DataVector>,\n      Xcts::Tags::ConformalFactor<DataVector>>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = false;\n  static constexpr bool is_discontinuous = false;\n  static void apply(gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_field,\n                    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n                    const tnsr::I<DataVector, 3>& shift,\n                    const Scalar<DataVector>& lapse,\n                    const Scalar<DataVector>& conformal_factor,\n                    const Scalar<DataVector>& field,\n                    const tnsr::i<DataVector, 3>& field_gradient);\n  static void apply(gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_field,\n                    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n                    const tnsr::I<DataVector, 3>& shift,\n                    const Scalar<DataVector>& lapse,\n                    const Scalar<DataVector>& conformal_factor,\n                    const tnsr::i<DataVector, 3>& face_normal,\n                    const tnsr::I<DataVector, 3>& face_normal_vector,\n                    const Scalar<DataVector>& field);\n};\n\n/*!\n * \\brief Add the sources \\f$S_A\\f$ for the scalar equation in sGB gravity on a\n * spatial metric \\f$\\gamma_{ij}\\f$.\n */\nstruct Sources {\n  using argument_tags = tmpl::list<\n      Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                 Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      gr::Tags::Lapse<DataVector>, Xcts::Tags::ConformalFactor<DataVector>,\n      ::Tags::deriv<Xcts::Tags::ConformalFactor<DataVector>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      Tags::Epsilon2, Tags::Epsilon4, gr::Tags::WeylElectricScalar<DataVector>,\n      gr::Tags::WeylMagneticScalar<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> equation_for_field,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const tnsr::i<DataVector, 3>& deriv_lapse,\n      const Scalar<DataVector>& lapse,\n      const Scalar<DataVector>& conformal_factor,\n      const tnsr::i<DataVector, 3>& conformal_factor_deriv, const double& eps2,\n      const double& eps4, const Scalar<DataVector>& weyl_electric,\n      const Scalar<DataVector>& weyl_magnetic, const Scalar<DataVector>& field,\n      const tnsr::i<DataVector, 3>& /*field_gradient*/,\n      const tnsr::I<DataVector, 3>& field_flux);\n};\n\n/*!\n * \\brief Add the linearised sources \\f$S_A\\f$ for the scalar equation in sGB\n * gravity on a spatial metric \\f$\\gamma_{ij}\\f$.\n */\nstruct LinearizedSources {\n  using argument_tags =\n      tmpl::list<Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                            Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 gr::Tags::Lapse<DataVector>,\n                 Xcts::Tags::ConformalFactor<DataVector>,\n                 ::Tags::deriv<Xcts::Tags::ConformalFactor<DataVector>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 ::CurvedScalarWave::Tags::Psi, Tags::Epsilon2, Tags::Epsilon4,\n                 gr::Tags::WeylElectricScalar<DataVector>,\n                 gr::Tags::WeylMagneticScalar<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> linearized_equation_for_field,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const tnsr::i<DataVector, 3>& deriv_lapse,\n      const Scalar<DataVector>& lapse,\n      const Scalar<DataVector>& conformal_factor,\n      const tnsr::i<DataVector, 3>& conformal_factor_deriv,\n      const Scalar<DataVector>& field, const double& eps2, const double& eps4,\n      const Scalar<DataVector>& weyl_electric,\n      const Scalar<DataVector>& weyl_magnetic,\n      const Scalar<DataVector>& field_correction,\n      const tnsr::i<DataVector, 3>& /*field_gradient_correction*/,\n      const tnsr::I<DataVector, 3>& field_flux_correction);\n};\n\n}  // namespace sgb\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/Equations.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace sgb {\n\n/*!\n * \\brief The scalar equation in sGB theories of gravity in the decoupled limit,\n * assuming quasi-stationarity. See \\ref sgb for details on the\n * explicit equation.\n */\nstruct FirstOrderSystem\n    : tt::ConformsTo<elliptic::protocols::FirstOrderSystem> {\n private:\n  using field = ::sgb::Tags::Psi;\n\n public:\n  static constexpr size_t volume_dim = 3;\n\n  using primal_fields = tmpl::list<field>;\n  using primal_fluxes =\n      tmpl::list<::Tags::Flux<field, tmpl::size_t<3>, Frame::Inertial>>;\n  // Note that there are many more background fields required for the elliptic\n  // solve, however these are numerically imported and so are not listed here.\n  using background_fields = tmpl::list<\n      Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n      Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                 Frame::Inertial>>;\n  using inv_metric_tag =\n      Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>;\n\n  using fluxes_computer = Fluxes;\n  using sources_computer = Sources;\n  using sources_computer_linearized = LinearizedSources;\n\n  using boundary_conditions_base =\n      elliptic::BoundaryConditions::BoundaryCondition<3>;\n  using modify_boundary_data = void;\n};\n\n}  // namespace sgb\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/ScalarGaussBonnet.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Documents the `sgb` namespace\n\n#pragma once\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving the sGB scalar equation\n *\n * The quasi-stationary scalar equation in sGB gravity\n * \\begin{equation}\\label{eq:sGB}\n * -\\partial_i \\left[ \\left( \\gamma^{ij} - \\alpha^{-2} \\beta^i \\beta^j \\right)\n * \\partial_j \\Psi \\right] + \\partial_j \\Psi \\left( \\gamma^{ij} - \\alpha^{-2}\n * \\beta^i \\beta^j \\right) \\left( \\Gamma_i + \\partial_i \\ln \\alpha \\right) =\n * \\ell^2 f' \\left( \\Psi \\right) \\mathcal{G}\n * \\end{equation}\n *\n * is a nonlinear Poisson-type elliptic PDE for the scalar field $\\Psi$. To\n * obtain this equation, one begins by considering the action:\n *\n * \\begin{equation}\n * S\\left[g_{ab}, \\Psi \\right] \\equiv \\int \\, d^4 x \\sqrt{-g}\n * \\Big[ \\dfrac{R}{2 \\kappa } - \\dfrac{1}{2}  \\nabla_{a} \\Psi \\nabla^{a} \\Psi +\n * \\ell^2 f(\\Psi) \\, \\mathcal{G} \\Big],\n * \\end{equation}\n *\n * where $\\mathcal{G} \\equiv R_{abcd}R^{abcd} - 4 R_{ab}R^{ab} + R^2$. Varying\n * the action with respect to $\\Psi$, one obtains the wave-like equation\n *\n * \\begin{equation}\n * \\Box \\Psi = - \\ell^2 f'(\\Psi) \\mathcal{G}\n * \\end{equation}\n *\n * In the spirit of quasi-stationarity, we set $\\partial_t \\Psi = \\partial_t^2\n * \\Psi = \\partial_t \\alpha = \\partial_t \\beta^{i} = 0$, where $\\alpha$ and\n * $\\beta^i$ are the lapse and shift respectively. This yields ($\\ref{eq:sGB}$),\n * with $\\gamma^{ij}$ being the spatial metric, and $\\Gamma_i$ its associated\n * contracted christoffel symbol of the second kind.\n *\n * Currently, we have implemented the coupling function\n *\n * \\begin{equation}\n * \\ell^2 f(\\Psi) = \\epsilon_2 \\frac{\\Psi^2}{8} + \\epsilon_4 \\frac{\\Psi^4}{16}\n * \\end{equation}\n *\n * Note that the principal part of the master equation will generically turn\n * singular at black hole horizon's for stationary initial data. Typically, this\n * requires a boundary condition ensuring regularity of the solution. However,\n * as detailed in \\cite Nee2024bur, this is already enforced by the chosen\n * spectral decomposition, and so instead one should impose the DoNothing\n * boundary condition for excision surfaces within black hole apparent horizons.\n *\n * As is currently implemented, one must provide numeric data corresponding to\n * the full metric $g_{ab}$. All of this can be generated using SolveXcts,\n * with a glob for the volume files being specified in the\n * SolveScalarGaussBonnet input file.\n *\n * Specifically, one must ensure the following is in the provided volume file:\n *\n * - the conformal factor $\\psi$,\n * - the spatial metric $\\gamma_{ij}$,\n * - the lapse $\\alpha$,\n * - the shift $\\beta^i$,\n * - the shift excess $\\beta_{exc}^i$,\n * - the extrinsic curvature $K^{ij}$, and\n * - the inverse conformal metric $\\bar{\\gamma}^{ij}$.\n *\n * One must also ensure that the Background specified in the input file is the\n * same one that was used in SolveXcts. This is checked using\n * $\\bar{\\gamma}^{ij}$.\n */\nnamespace sgb {}\n"
  },
  {
    "path": "src/Elliptic/Systems/ScalarGaussBonnet/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines DataBox tags for the ScalarGaussBonnet system.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace sgb {\n/// Tags related to solving the scalar equation in sGB gravity.\nnamespace OptionTags {\n\nstruct OptionGroup {\n  static std::string name() { return \"ScalarGaussBonnet\"; }\n  static constexpr Options::String help =\n      \"Options for elliptic solve in sGB gravity.\";\n};\n\nstruct Epsilon2 {\n  static std::string name() { return \"Epsilon2\"; }\n  using type = double;\n  using group = OptionGroup;\n  static constexpr Options::String help{\n      \"Epsilon2 (quadratic term) for the coupling function.\"};\n};\n\nstruct Epsilon4 {\n  static std::string name() { return \"Epsilon4\"; }\n  using type = double;\n  using group = OptionGroup;\n  static constexpr Options::String help{\n      \"Epsilon4 (quartic term) for the coupling function.\"};\n};\n\nstruct RolloffLocation {\n  static std::string name() { return \"RolloffLocation\"; }\n  using type = double;\n  using group = OptionGroup;\n  static constexpr Options::String help{\n      \"Location of center of tanh function for rolling off the shift.\"};\n};\n\nstruct RolloffRate {\n  static std::string name() { return \"RolloffRate\"; }\n  using type = double;\n  using group = OptionGroup;\n  static constexpr Options::String help{\n      \"Rate of roll off for rolling off the shift.\"};\n};\n\n}  // namespace OptionTags\nnamespace Tags {\n\n/*!\n * \\brief Epsilon2 (quadratic term) for the coupling function.\n */\nstruct Epsilon2 : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::Epsilon2>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double epsilon) { return epsilon; }\n};\n\n/*!\n * \\brief Epsilon4 (quartic term) for the coupling function.\n */\nstruct Epsilon4 : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::Epsilon4>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double epsilon) { return epsilon; }\n};\n\n/*!\n * \\brief Location of center of tanh function for rolling off the shift.\n */\nstruct RolloffLocation : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::RolloffLocation>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double rolloff_location) {\n    return rolloff_location;\n  }\n};\n\n/*!\n * \\brief Rate of roll off for rolling off the shift.\n */\nstruct RolloffRate : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::RolloffRate>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double rolloff_rate) {\n    return rolloff_rate;\n  }\n};\n\n/*!\n * \\brief The scalar field $\\Psi$.\n */\nstruct Psi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Rolled-off shift (i.e. the shift used in computing the fluxes).\n */\nstruct RolledOffShift : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n};\n\n/*!\n * \\brief Pi computed using the rolled-off shift.\n */\nstruct PiWithRolledOffShift : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace Tags\n}  // namespace sgb\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(Scalar)\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GrSelfForceActions)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InitializeEffectiveSource.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  Elliptic\n  Parallel\n  ParallelAmr\n  GrSelfForce\n  GrSelfForceAnalyticData\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/Actions/InitializeEffectiveSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Equations.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace GrSelfForce::Actions {\n\n/*!\n * \\brief Initialize the effective source and singular field for the\n * gravitational self-force problem in a regularized region around the puncture.\n * Replaces 'elliptic::Actions::InitializeFixedSources' in the standard elliptic\n * initialization.\n *\n * \\details The regularized region is chosen as the full block containing the\n * puncture. In this region, the `Tags::FieldIsRegularized` is set to true and\n * the effective source and the singular field are set by the\n * GrSelfForce::AnalyticData::CircularOrbit class. Outside this region, the\n * fixed source and singular field are set to zero.\n */\ntemplate <typename System, typename BackgroundTag>\nstruct InitializeEffectiveSource : tt::ConformsTo<::amr::protocols::Projector> {\n private:\n  static constexpr size_t Dim = System::volume_dim;\n  using fixed_sources_tag = ::Tags::Variables<\n      db::wrap_tags_in<::Tags::FixedSource, typename System::primal_fields>>;\n  using singular_vars_tag = ::Tags::Variables<tmpl::list<\n      Tags::SingularField,\n      ::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>, Frame::Inertial>>>;\n  using singular_vars_on_mortars_tag =\n      ::Tags::Variables<tmpl::list<Tags::SingularField,\n                                   ::Tags::NormalDotFlux<Tags::SingularField>>>;\n  using analytic_tags_list = tmpl::push_back<\n      typename fixed_sources_tag::tags_list, Tags::SingularField,\n      ::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>, Frame::Inertial>,\n      Tags::BoyerLindquistRadius>;\n\n public:  // Iterable action\n  using const_global_cache_tags =\n      tmpl::list<elliptic::dg::Tags::Massive, BackgroundTag>;\n  using simple_tags =\n      tmpl::list<fixed_sources_tag, singular_vars_tag,\n                 ::Tags::Mortars<singular_vars_on_mortars_tag, Dim>,\n                 Tags::BoyerLindquistRadius, Tags::FieldIsRegularized,\n                 ::Tags::Mortars<Tags::FieldIsRegularized, Dim>>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeEffectiveSource>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // DataBox mutator, amr::protocols::Projector\n  using return_tags = simple_tags;\n  using argument_tags = tmpl::list<\n      domain::Tags::Coordinates<Dim, Frame::Inertial>,\n      domain::Tags::Domain<Dim>, domain::Tags::Element<Dim>,\n      ::Tags::Mortars<domain::Tags::Coordinates<Dim, Frame::Inertial>, Dim>,\n      BackgroundTag, elliptic::dg::Tags::Massive, domain::Tags::Mesh<Dim>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      Parallel::Tags::Metavariables>;\n\n  template <typename Background, typename Metavariables, typename... AmrData>\n  static void apply(\n      const gsl::not_null<typename fixed_sources_tag::type*> fixed_sources,\n      const gsl::not_null<typename singular_vars_tag::type*> singular_vars,\n      const gsl::not_null<\n          DirectionalIdMap<Dim, typename singular_vars_on_mortars_tag::type>*>\n          singular_vars_on_mortars,\n      const gsl::not_null<Scalar<DataVector>*> bl_radius,\n      const gsl::not_null<bool*> field_is_regularized,\n      const gsl::not_null<DirectionalIdMap<Dim, bool>*>\n          neighbors_field_is_regularized,\n      const tnsr::I<DataVector, Dim>& inertial_coords,\n      const Domain<Dim>& domain, const Element<Dim>& element,\n      const DirectionalIdMap<Dim, tnsr::I<DataVector, Dim>>&\n          all_mortar_inertial_coords,\n      const Background& background, const bool massive, const Mesh<Dim>& mesh,\n      const Scalar<DataVector>& det_inv_jacobian, const Metavariables& /*meta*/,\n      const AmrData&... /*amr_data*/) {\n    const auto& circular_orbit =\n        dynamic_cast<const GrSelfForce::AnalyticData::CircularOrbit&>(\n            background);\n\n    // Determine the regularized region\n    const tnsr::I<double, Dim> puncture_pos =\n        circular_orbit.puncture_position();\n    const auto puncture_in_element =\n        [&puncture_pos, &domain](const ElementId<Dim>& element_id) -> bool {\n      // Regularize full block that contains the puncture\n      const auto& block = domain.blocks()[element_id.block_id()];\n      const auto block_logical_coords =\n          block_logical_coordinates_single_point(puncture_pos, block);\n      return block_logical_coords.has_value();\n    };\n    *field_is_regularized = puncture_in_element(element.id());\n    neighbors_field_is_regularized->clear();\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      for (const auto& neighbor_id : neighbors) {\n        const dg::MortarId<Dim> mortar_id{direction, neighbor_id};\n        neighbors_field_is_regularized->emplace(\n            mortar_id, puncture_in_element(neighbor_id));\n      }\n    }\n\n    // Set the effective source if solving for the regular field\n    const size_t num_points = mesh.number_of_grid_points();\n    if (*field_is_regularized) {\n      const auto vars =\n          circular_orbit.variables(inertial_coords, analytic_tags_list{});\n      fixed_sources->initialize(num_points);\n      singular_vars->initialize(num_points);\n      get<::Tags::FixedSource<Tags::MMode>>(*fixed_sources) =\n          get<::Tags::FixedSource<Tags::MMode>>(vars);\n      get<Tags::SingularField>(*singular_vars) = get<Tags::SingularField>(vars);\n      get<::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>,\n                        Frame::Inertial>>(*singular_vars) =\n          get<::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>,\n                            Frame::Inertial>>(vars);\n      *bl_radius = get<Tags::BoyerLindquistRadius>(vars);\n      // Apply DG mass matrix to the fixed sources if the DG operator is massive\n      if (massive) {\n        *fixed_sources /= get(det_inv_jacobian);\n        ::dg::apply_mass_matrix(fixed_sources, mesh);\n      }\n    } else {\n      *fixed_sources =\n          Variables<typename fixed_sources_tag::tags_list>{num_points, 0.};\n      *singular_vars =\n          Variables<typename singular_vars_tag::tags_list>{num_points, 0.};\n      *bl_radius = Scalar<DataVector>{num_points, 0.};\n    }\n\n    // Set the singular field and flux on mortars, which is needed to transform\n    // between regularized and full fields\n    singular_vars_on_mortars->clear();\n    for (const auto& [mortar_id, mortar_inertial_coords] :\n         all_mortar_inertial_coords) {\n      if (*field_is_regularized ==\n          neighbors_field_is_regularized->at(mortar_id)) {\n        // Both elements solve for the same field. No transformation needed.\n        continue;\n      }\n      const auto vars_on_mortar = circular_orbit.variables(\n          mortar_inertial_coords, analytic_tags_list{});\n      const auto background_on_mortar = circular_orbit.variables(\n          mortar_inertial_coords,\n          tmpl::list<Tags::Alpha, Tags::Beta, Tags::GammaRstar,\n                     Tags::GammaTheta>{});\n      auto& singular_vars_on_mortar = (*singular_vars_on_mortars)[mortar_id];\n      singular_vars_on_mortar.initialize(\n          mortar_inertial_coords.begin()->size());\n      get<Tags::SingularField>(singular_vars_on_mortar) =\n          get<Tags::SingularField>(vars_on_mortar);\n      const auto& deriv_singular_field_on_mortar =\n          get<::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>,\n                            Frame::Inertial>>(vars_on_mortar);\n      const auto& alpha_on_mortar = get<Tags::Alpha>(background_on_mortar);\n      FluxTensorType singular_field_flux_on_mortar{};\n      GrSelfForce::fluxes(make_not_null(&singular_field_flux_on_mortar),\n                          alpha_on_mortar, deriv_singular_field_on_mortar);\n      // Assuming mortar normal is just (1, 0) or (0, 1). This is true for the\n      // rectangular domains used in the self-force problem (AlignedLattice in\n      // r, theta coordinates).\n      tnsr::i<DataVector, Dim> mortar_normal{\n          mortar_inertial_coords.begin()->size(), 0.};\n      mortar_normal.get(mortar_id.direction().dimension()) =\n          mortar_id.direction().sign();\n      normal_dot_flux(\n          make_not_null(&get<::Tags::NormalDotFlux<Tags::SingularField>>(\n              singular_vars_on_mortar)),\n          mortar_normal, singular_field_flux_on_mortar);\n    }\n  }\n};\n\n}  // namespace GrSelfForce::Actions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GrSelfForceAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CircularOrbit.cpp\n  CircularOrbitCoeffs.cpp\n  CircularOrbitConvertEffsource.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CircularOrbit.hpp\n  CircularOrbitCoeffs.hpp\n  CircularOrbitConvertEffsource.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  GeneralRelativity\n  GravitationalEffectiveSource\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbit.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbit.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <effsource_gr.hpp>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbitCoeffs.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbitConvertEffsource.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TortoiseCoordinates.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace GrSelfForce::AnalyticData {\n\nCircularOrbit::CircularOrbit(const double black_hole_mass,\n                             const double black_hole_spin,\n                             const double orbital_radius,\n                             const int m_mode_number)\n    : black_hole_mass_(black_hole_mass),\n      black_hole_spin_(black_hole_spin),\n      orbital_radius_(orbital_radius),\n      m_mode_number_(m_mode_number) {}\n\nCircularOrbit::CircularOrbit(CkMigrateMessage* m)\n    : elliptic::analytic_data::Background(m),\n      elliptic::analytic_data::InitialGuess(m) {}\n\ntnsr::I<double, 2> CircularOrbit::puncture_position() const {\n  const double M = black_hole_mass_;\n  const double r_plus = M * (1. + sqrt(1. - square(black_hole_spin_)));\n  const double r_0 = orbital_radius_;\n  const double r_star = gr::tortoise_radius_from_boyer_lindquist_minus_r_plus(\n      r_0 - r_plus, M, black_hole_spin_);\n  return tnsr::I<double, 2>{{{r_star, M_PI_2}}};\n}\n\n// Background\ntuples::TaggedTuple<Tags::Alpha, Tags::Beta, Tags::GammaRstar, Tags::GammaTheta>\nCircularOrbit::variables(const tnsr::I<DataVector, 2>& x,\n                         tmpl::list<Tags::Alpha, Tags::Beta, Tags::GammaRstar,\n                                    Tags::GammaTheta> /*meta*/) const {\n  const double a = black_hole_spin_ * black_hole_mass_;\n  const double M = black_hole_mass_;\n  const double r_plus = M * (1. + sqrt(1. - square(black_hole_spin_)));\n  const double r_minus = M * (1. - sqrt(1. - square(black_hole_spin_)));\n  const double r_0 = orbital_radius_;\n  const double omega = 1. / (a + sqrt(cube(r_0) / M));\n  const auto& r_star = get<0>(x);\n  const auto& theta = get<1>(x);\n  const DataVector cos_theta = cos(theta);\n  const DataVector r_minus_r_plus =\n      gr::boyer_lindquist_radius_minus_r_plus_from_tortoise(r_star, M,\n                                                            black_hole_spin_);\n  const DataVector r = r_minus_r_plus + r_plus;\n  const DataVector delta = r_minus_r_plus * (r - r_minus);\n  const DataVector r_sq_plus_a_sq = square(r) + square(a);\n  const DataVector r_sq_plus_a_sq_sq = square(r_sq_plus_a_sq);\n  const DataVector sin_theta_squared = 1. - square(cos_theta);\n  const DataVector sigma_squared =\n      r_sq_plus_a_sq_sq - square(a) * delta * sin_theta_squared;\n  tuples::TaggedTuple<Tags::Alpha, Tags::Beta, Tags::GammaRstar,\n                      Tags::GammaTheta>\n      result{};\n  auto& alpha = get<Tags::Alpha>(result);\n  auto& beta = get<Tags::Beta>(result);\n  auto& gamma_rstar = get<Tags::GammaRstar>(result);\n  auto& gamma_theta = get<Tags::GammaTheta>(result);\n  const size_t num_points = r.size();\n  get(alpha) = delta / r_sq_plus_a_sq_sq;\n  for (size_t i = 0; i < beta.size(); ++i) {\n    beta[i] = ComplexDataVector{num_points, 0.};\n    gamma_rstar[i] = ComplexDataVector{num_points, 0.};\n    gamma_theta[i] = ComplexDataVector{num_points, 0.};\n  }\n  const ComplexDataVector temp1 =\n      1. / r * std::complex<double>(0., 2. * a * m_mode_number_);\n  // tt, tr, ttheta, tphi, rr, rtheta, rphi, theta theta, theta phi, phi phi\n  std::array<std::array<double, 10>, 10> Areal{};\n  std::array<std::array<double, 10>, 10> Aimag{};\n  std::array<std::array<double, 10>, 10> Breal{};\n  std::array<std::array<double, 10>, 10> Bimag{};\n  std::array<std::array<double, 10>, 10> Creal{};\n  std::array<std::array<double, 10>, 10> Cimag{};\n  for (size_t i = 0; i < r.size(); i++) {\n    detail::getAreal(m_mode_number_, a, m_mode_number_ * omega, r[i], theta[i],\n                     Areal);\n    detail::getAimag(m_mode_number_, a, m_mode_number_ * omega, r[i], theta[i],\n                     Aimag);\n    detail::getBreal(m_mode_number_, a, m_mode_number_ * omega, r[i], theta[i],\n                     Breal);\n    detail::getBimag(m_mode_number_, a, m_mode_number_ * omega, r[i], theta[i],\n                     Bimag);\n    detail::getCreal(m_mode_number_, a, m_mode_number_ * omega, r[i], theta[i],\n                     Creal);\n    detail::getCimag(m_mode_number_, a, m_mode_number_ * omega, r[i], theta[i],\n                     Cimag);\n    // NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index)\n    for (size_t a1 = 0; a1 < 4; ++a1) {\n      for (size_t b = 0; b <= a1; ++b) {\n        const size_t matrix_i =\n            tnsr::aa<ComplexDataVector, 3>::get_storage_index(\n                std::array<size_t, 2>{{a1, b}});\n        for (size_t c = 0; c < 4; ++c) {\n          for (size_t d = 0; d <= c; ++d) {\n            const size_t matrix_j =\n                tnsr::aa<ComplexDataVector, 3>::get_storage_index(\n                    std::array<size_t, 2>{{c, d}});\n            gamma_rstar.get(a1, b, c, d)[i] =\n                Areal[matrix_i][matrix_j] +\n                std::complex<double>(0., 1.) * Aimag[matrix_i][matrix_j];\n            gamma_theta.get(a1, b, c, d)[i] =\n                Breal[matrix_i][matrix_j] +\n                std::complex<double>(0., 1.) * Bimag[matrix_i][matrix_j];\n            beta.get(a1, b, c, d)[i] =\n                Creal[matrix_i][matrix_j] +\n                std::complex<double>(0., 1.) * Cimag[matrix_i][matrix_j];\n          }\n        }\n      }\n    }\n    // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index)\n  }\n  for (size_t i = 0; i < beta.size(); ++i) {\n    beta[i] *= -1.;\n    gamma_rstar[i] *= -1.;\n    gamma_theta[i] *= -1. / get(alpha);\n  }\n  return result;\n}\n\n// Initial guess\ntuples::TaggedTuple<Tags::MMode> CircularOrbit::variables(\n    const tnsr::I<DataVector, 2>& x, tmpl::list<Tags::MMode> /*meta*/) {\n  tuples::TaggedTuple<Tags::MMode> result{};\n  auto& field = get<Tags::MMode>(result);\n  for (size_t i = 0; i < field.size(); ++i) {\n    field[i] = ComplexDataVector{get<0>(x).size(), 0.};\n  }\n  return result;\n}\n\n// Fixed sources\ntuples::TaggedTuple<\n    ::Tags::FixedSource<Tags::MMode>, Tags::SingularField,\n    ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>,\n    Tags::BoyerLindquistRadius>\nCircularOrbit::variables(\n    const tnsr::I<DataVector, 2>& x,\n    tmpl::list<\n        ::Tags::FixedSource<Tags::MMode>, Tags::SingularField,\n        ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>,\n        Tags::BoyerLindquistRadius> /*meta*/) const {\n  const double a = black_hole_spin_ * black_hole_mass_;\n  const double M = black_hole_mass_;\n  const double r_0 = orbital_radius_;\n  const double r_plus = M * (1. + sqrt(1. - square(black_hole_spin_)));\n  const double r_minus = M * (1. - sqrt(1. - square(black_hole_spin_)));\n  {\n    // Initialize effsource\n    effsource_init(M, a);\n    coordinate xp{};\n    xp.t = 0;\n    xp.r = r_0;\n    xp.theta = M_PI_2;\n    xp.phi = 0;\n    // Circular equatorial orbit, as given in the EffectiveSource example\n    const double e = ((r_0 - 2.0 * M) * sqrt(M * r_0) + a * M) /\n                     (sqrt(M * r_0) * sqrt(r_0 * r_0 - 3.0 * M * r_0 +\n                                           2.0 * a * sqrt(M * r_0)));\n    const double l = (M * (a * a + r_0 * r_0 - 2.0 * a * sqrt(M * r_0))) /\n                     (sqrt(M * r_0) * sqrt(r_0 * r_0 - 3.0 * M * r_0 +\n                                           2.0 * a * sqrt(M * r_0)));\n    effsource_set_particle(&xp, e, l, 0.);\n  }\n  const auto& r_star = get<0>(x);\n  const auto& theta = get<1>(x);\n  const DataVector r_minus_r_plus =\n      gr::boyer_lindquist_radius_minus_r_plus_from_tortoise(r_star, M,\n                                                            black_hole_spin_);\n  const DataVector r = r_minus_r_plus + r_plus;\n  const DataVector delta = r_minus_r_plus * (r - r_minus);\n  const DataVector r_sq_plus_a_sq = square(r) + square(a);\n  const DataVector r_sq_plus_a_sq_sq = square(r_sq_plus_a_sq);\n  const DataVector delta_phi = m_mode_number_ * a / (r_plus - r_minus) *\n                               log((r - r_plus) / (r - r_minus));\n  const ComplexDataVector rotation =\n      cos(delta_phi) - std::complex<double>(0., 1.) * sin(delta_phi);\n  tuples::TaggedTuple<\n      ::Tags::FixedSource<Tags::MMode>, Tags::SingularField,\n      ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>,\n      Tags::BoyerLindquistRadius>\n      result{};\n  get(get<Tags::BoyerLindquistRadius>(result)) = r;\n  const size_t num_points = get<0>(x).size();\n  tnsr::aa<ComplexDataVector, 3>& effective_source =\n      get<::Tags::FixedSource<Tags::MMode>>(result);\n  tnsr::aa<ComplexDataVector, 3>& singular_field =\n      get<Tags::SingularField>(result);\n  for (size_t i = 0; i < singular_field.size(); i++) {\n    effective_source[i].destructive_resize(num_points);\n    singular_field[i].destructive_resize(num_points);\n  }\n  auto& deriv_singular_field =\n      get<::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>>(\n          result);\n  for (size_t i = 0; i < deriv_singular_field.size(); i++) {\n    deriv_singular_field[i].destructive_resize(num_points);\n  }\n  {\n    // Call into effsource\n    coordinate x_i{};\n    std::array<double, 10> hS_re{};\n    std::array<double, 10> hS_im{};\n    std::array<double, 10> hS_conv_re{};\n    std::array<double, 10> hS_conv_im{};\n    std::array<double, 10> dhS_dr_re{};\n    std::array<double, 10> dhS_dr_im{};\n    std::array<double, 10> dhS_dth_re{};\n    std::array<double, 10> dhS_dth_im{};\n    std::array<double, 10> dhS_dph_re{};\n    std::array<double, 10> dhS_dph_im{};\n    std::array<double, 10> dhS_dt_re{};\n    std::array<double, 10> dhS_dt_im{};\n    std::array<double, 10> dhS_drstar_re_conv{};\n    std::array<double, 10> dhS_drstar_im_conv{};\n    std::array<double, 10> dhS_dth_re_conv{};\n    std::array<double, 10> dhS_dth_im_conv{};\n    std::array<double, 10> src_re{};\n    std::array<double, 10> src_im{};\n    std::array<double, 10> src_conv_re{};\n    std::array<double, 10> src_conv_im{};\n    for (size_t i = 0; i < get<0>(x).size(); ++i) {\n      x_i.t = 0;\n      x_i.r = r[i];\n      x_i.theta = theta[i];\n      x_i.phi = 0;\n      effsource_calc_m(m_mode_number_, &x_i, hS_re.data(), hS_im.data(),\n                       dhS_dr_re.data(), dhS_dr_im.data(), dhS_dth_re.data(),\n                       dhS_dth_im.data(), dhS_dph_re.data(), dhS_dph_im.data(),\n                       dhS_dt_re.data(), dhS_dt_im.data(), src_re.data(),\n                       src_im.data());\n      detail::convert_effsource_psi(m_mode_number_, a, r[i], theta[i], hS_re,\n                                    hS_im, hS_conv_re, hS_conv_im);\n      detail::convert_effsource_dpsidtheta(m_mode_number_, a, r[i], theta[i],\n                                           hS_re, hS_im, dhS_dth_re, dhS_dth_im,\n                                           dhS_dth_re_conv, dhS_dth_im_conv);\n      detail::convert_effsource_dpsidrstar(\n          m_mode_number_, a, r[i], theta[i], hS_re, hS_im, dhS_dr_re, dhS_dr_im,\n          dhS_drstar_re_conv, dhS_drstar_im_conv);\n      detail::convert_effsource_Seff(m_mode_number_, a, r[i], theta[i], src_re,\n                                     src_im, src_conv_re, src_conv_im);\n      // NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index)\n      for (size_t a1 = 0; a1 < 4; ++a1) {\n        for (size_t b = 0; b <= a1; ++b) {\n          const size_t comp = tnsr::aa<ComplexDataVector, 3>::get_storage_index(\n              std::array<size_t, 2>{{a1, b}});\n          effective_source.get(a1, b)[i] =\n              -src_conv_re[comp] -\n              std::complex<double>(0., 1.) * src_conv_im[comp];\n          singular_field.get(a1, b)[i] =\n              hS_conv_re[comp] +\n              std::complex<double>(0., 1.) * hS_conv_im[comp];\n          deriv_singular_field.get(0, a1, b)[i] =\n              dhS_drstar_re_conv[comp] +\n              std::complex<double>(0., 1.) * dhS_drstar_im_conv[comp];\n          deriv_singular_field.get(1, a1, b)[i] =\n              dhS_dth_re_conv[comp] +\n              std::complex<double>(0., 1.) * dhS_dth_im_conv[comp];\n        }\n      }\n      // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index)\n    }\n  }\n  return result;\n}\n\nvoid CircularOrbit::pup(PUP::er& p) {\n  elliptic::analytic_data::Background::pup(p);\n  elliptic::analytic_data::InitialGuess::pup(p);\n  p | black_hole_mass_;\n  p | black_hole_spin_;\n  p | orbital_radius_;\n  p | m_mode_number_;\n}\n\nbool operator==(const CircularOrbit& lhs, const CircularOrbit& rhs) {\n  return lhs.black_hole_mass_ == rhs.black_hole_mass_ and\n         lhs.black_hole_spin_ == rhs.black_hole_spin_ and\n         lhs.orbital_radius_ == rhs.orbital_radius_ and\n         lhs.m_mode_number_ == rhs.m_mode_number_;\n}\n\nbool operator!=(const CircularOrbit& lhs, const CircularOrbit& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID CircularOrbit::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace GrSelfForce::AnalyticData\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbit.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::AnalyticData {\n\n/*!\n * \\brief Gravitational self-force of a point mass on a circular equatorial\n * orbit in Kerr.\n *\n * This class defines the gravitational self-force equations for a circular\n * orbit by setting the coefficients $\\alpha$, $\\beta$, and $\\gamma$ (see\n * `GrSelfForce::FirstOrderSystem`). It also sets the effective source\n * $S_m^\\mathrm{eff}$ and the singular field $\\Psi_m^P$ in the regularized\n * region. The coefficients are computed using Mathematica-generated functions\n * (see CircularOrbitCoeffs.hpp) and the effective source is computed using the\n * GravitationalEffectiveSource code by Wardell et. al.\n * (https://github.com/barrywardell/GravitationalEffectiveSource) and then\n * transformed to our form of the equations with more Mathematica-generated\n * functions (see CircularOrbitConvertEffsource.hpp). The derivation of these\n * equations will be presented in a future publication. A very strong test of\n * the validity of these equations is evaluating them on the singular field and\n * the corresponding effective source provided by the external\n * GravitationalEffectiveSource code (see Test_CircularOrbit.cpp).\n */\nclass CircularOrbit : public elliptic::analytic_data::Background,\n                      public elliptic::analytic_data::InitialGuess {\n public:\n  struct BlackHoleMass {\n    static constexpr Options::String help =\n        \"Kerr mass parameter 'M' of the black hole\";\n    using type = double;\n  };\n  struct BlackHoleSpin {\n    static constexpr Options::String help =\n        \"Kerr dimensionless spin parameter 'chi' of the black hole\";\n    using type = double;\n  };\n  struct OrbitalRadius {\n    static constexpr Options::String help =\n        \"Radius 'r_0' of the circular orbit\";\n    using type = double;\n  };\n  struct MModeNumber {\n    static constexpr Options::String help =\n        \"Mode number 'm' of the m-mode decomposition\";\n    using type = int;\n  };\n  using options =\n      tmpl::list<BlackHoleMass, BlackHoleSpin, OrbitalRadius, MModeNumber>;\n  static constexpr Options::String help =\n      \"Quasicircular orbit of a point mass in Kerr spacetime\";\n\n  CircularOrbit() = default;\n  CircularOrbit(const CircularOrbit&) = default;\n  CircularOrbit& operator=(const CircularOrbit&) = default;\n  CircularOrbit(CircularOrbit&&) = default;\n  CircularOrbit& operator=(CircularOrbit&&) = default;\n  ~CircularOrbit() override = default;\n\n  CircularOrbit(double black_hole_mass, double black_hole_spin,\n                double orbital_radius, int m_mode_number);\n\n  explicit CircularOrbit(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CircularOrbit);\n\n  tnsr::I<double, 2> puncture_position() const;\n  double black_hole_mass() const { return black_hole_mass_; }\n  double black_hole_spin() const { return black_hole_spin_; }\n  double orbital_radius() const { return orbital_radius_; }\n  int m_mode_number() const { return m_mode_number_; }\n\n  using background_tags =\n      tmpl::list<Tags::Alpha, Tags::Beta, Tags::GammaRstar, Tags::GammaTheta>;\n  using source_tags = tmpl::list<\n      ::Tags::FixedSource<Tags::MMode>, Tags::SingularField,\n      ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>,\n      Tags::BoyerLindquistRadius>;\n\n  // Background\n  tuples::tagged_tuple_from_typelist<background_tags> variables(\n      const tnsr::I<DataVector, 2>& x, background_tags /*meta*/) const;\n\n  // Initial guess\n  static tuples::TaggedTuple<Tags::MMode> variables(\n      const tnsr::I<DataVector, 2>& x, tmpl::list<Tags::MMode> /*meta*/);\n\n  // Fixed sources\n  tuples::tagged_tuple_from_typelist<source_tags> variables(\n      const tnsr::I<DataVector, 2>& x, source_tags /*meta*/) const;\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 2>& x, const Mesh<2>& /*mesh*/,\n      const InverseJacobian<DataVector, 2, Frame::ElementLogical,\n                            Frame::Inertial>& /*inv_jacobian*/,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables(x, tmpl::list<RequestedTags...>{});\n  }\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const CircularOrbit& lhs, const CircularOrbit& rhs);\n\n  double black_hole_mass_{std::numeric_limits<double>::signaling_NaN()};\n  double black_hole_spin_{std::numeric_limits<double>::signaling_NaN()};\n  double orbital_radius_{std::numeric_limits<double>::signaling_NaN()};\n  int m_mode_number_{};\n};\n\nbool operator!=(const CircularOrbit& lhs, const CircularOrbit& rhs);\n\n}  // namespace GrSelfForce::AnalyticData\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbitCoeffs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbitCoeffs.hpp\"\n\n#include <cmath>\n\n// Disable clang-tidy for this generated code. Also disable clang-format because\n// the added whitespace would significantly increase the file size.\n// NOLINTBEGIN\n// clang-format off\nnamespace GrSelfForce::detail {\n\nvoid getAreal(const int /*m*/, const double a, const double /*om*/, const double r, const double th,\n              std::array<std::array<double, 10>, 10>& Areal) {\n/* om = m*Omega */\nstd::fill(Areal[0].data(), Areal[0].data() + 100, 0.0);\nAreal[0][0]=(4*a*a)/pow(a*a+r*r,2)-(2*a*a*a*a)/(r*pow(a*a+r*r,2))-(2*a*a*r)/pow(a*a+r*r,2)-(32*r*r)/pow(a*a+2*r*r+a*a*cos(2*th),2)+8/(a*a+2*r*r+a*a*cos(2*th));\nAreal[0][3]=(8*a*a*a*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*r*r*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*r*cos(2*th)*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nAreal[1][1]=(-2*a*a)/pow(a*a+r*r,2)+(2*a*a*a*a)/(r*pow(a*a+r*r,2))+(4*a*a*r)/pow(a*a+r*r,2)-(6*r*r)/pow(a*a+r*r,2)+(2*r*r*r)/pow(a*a+r*r,2)-(16*r*r)/pow(a*a+2*r*r+a*a*cos(2*th),2)+(4*a*a)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))+(12*r*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*r*r*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[1][2]=(4*a*a*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*sin(2*th))/(r*(a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*r*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[1][6]=(4*a*a*a*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*r*r*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*r*cos(2*th)*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nAreal[2][1]=(2*a*a*r*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[2][2]=(2*a*a*r)/pow(a*a+r*r,2)-(4*r*r)/pow(a*a+r*r,2)+(2*r*r*r)/pow(a*a+r*r,2)-(16*r*r)/pow(a*a+2*r*r+a*a*cos(2*th),2)+(4*a*a)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))+(12*r*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*r*r*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[2][8]=(4*a*a*a*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*r*r*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*r*cos(2*th)*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nAreal[3][0]=(2*a*a*a*r*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))+(6*a*r*r*r*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))-(2*a*a*a*a*a*pow(cos(th),2)*sin(th))/(r*(a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))+(2*a*a*a*r*pow(cos(th),2)*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2));\nAreal[3][3] = (2 * a * a) / pow(a * a + r * r, 2) - (2 * r * r) / pow(a * a + r * r, 2);\nAreal[3][9]=(4*a*a*a*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*r*r*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*r*cos(2*th)*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nAreal[4][4]=(-8*a*a)/pow(a*a+r*r,2)+(6*a*a*a*a)/(r*pow(a*a+r*r,2))+(10*a*a*r)/pow(a*a+r*r,2)-(12*r*r)/pow(a*a+r*r,2)+(4*r*r*r)/pow(a*a+r*r,2)-(8*a*a*a*a*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*a*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*r*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[4][5]=(8*a*a*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*a*a*sin(2*th))/(r*(a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*r*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[5][4]=(2*a*a*r*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[5][5]=(-6*a*a)/pow(a*a+r*r,2)+(4*a*a*a*a)/(r*pow(a*a+r*r,2))+(8*a*a*r)/pow(a*a+r*r,2)-(10*r*r)/pow(a*a+r*r,2)+(4*r*r*r)/pow(a*a+r*r,2)-(8*a*a*a*a*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*a*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*r*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[5][7]=(4*a*a*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*sin(2*th))/(r*(a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*r*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[6][1]=(-4*a*a*a*a*a*sin(th))/(r*(a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*a*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*r*r*r*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*cos(2*th)*sin(th))/(r*(a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*r*cos(2*th)*sin(th))/((a*a+r*r)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nAreal[6][6]=(-4*a*a)/pow(a*a+r*r,2)+(4*a*a*a*a)/(r*pow(a*a+r*r,2))+(6*a*a*r)/pow(a*a+r*r,2)-(8*r*r)/pow(a*a+r*r,2)+(2*r*r*r)/pow(a*a+r*r,2)+(16*r*r)/pow(a*a+2*r*r+a*a*cos(2*th),2)-(4*a*a)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))+(4*r*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*r*r*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[6][8]=(2*a*a*sin(2*th))/((a*a+r*r)*(r*r+a*a*pow(cos(th),2)))-(a*a*a*a*sin(2*th))/(r*(a*a+r*r)*(r*r+a*a*pow(cos(th),2)))-(a*a*r*sin(2*th))/((a*a+r*r)*(r*r+a*a*pow(cos(th),2)));\nAreal[7][5]=(4*a*a*r*sin(2*th))/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[7][7]=(-4*a*a*a*a)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*a)/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*a*cos(2*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(6*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[8][2]=-((a*a*a*a*a*sin(th))/(r*(a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2)))+(3*a*a*a*r*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))+(6*a*r*r*r*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))-(a*a*a*a*a*cos(2*th)*sin(th))/(r*(a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))+(a*a*a*r*cos(2*th)*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2));\nAreal[8][6]=(a*a*r*sin(2*th))/((a*a+r*r)*(r*r+a*a*pow(cos(th),2)));\nAreal[8][8]=(-2*a*a)/pow(a*a+r*r,2)+(2*a*a*a*a)/(r*pow(a*a+r*r,2))+(4*a*a*r)/pow(a*a+r*r,2)-(6*r*r)/pow(a*a+r*r,2)+(2*r*r*r)/pow(a*a+r*r,2)+(16*r*r)/pow(a*a+2*r*r+a*a*cos(2*th),2)-(4*a*a)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))+(4*r*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)))-(4*r*r*r)/((a*a+r*r)*(a*a+2*r*r+a*a*cos(2*th)));\nAreal[9][3]=(4*a*a*a*r*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))+(12*a*r*r*r*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))-(4*a*a*a*a*a*pow(cos(th),2)*sin(th))/(r*(a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2))+(4*a*a*a*r*pow(cos(th),2)*sin(th))/((a*a+r*r)*pow(r*r+a*a*pow(cos(th),2),2));\nAreal[9][9]=(2*a*a*a*a)/(r*pow(a*a+r*r,2))+(2*a*a*r)/pow(a*a+r*r,2)-(4*r*r)/pow(a*a+r*r,2)+(32*r*r)/pow(a*a+2*r*r+a*a*cos(2*th),2)-8/(a*a+2*r*r+a*a*cos(2*th));\n}\n\nvoid getAimag(const int m, const double a, const double /*om*/, const double r, const double th,\n              std::array<std::array<double, 10>, 10>& Aimag) {\n/* om = m*Omega */\nstd::fill(Aimag[0].data(), Aimag[0].data() + 100, 0.0);\nAimag[0][0]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\nAimag[1][1]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\nAimag[2][2]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\nAimag[3][3]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\nAimag[4][4]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\nAimag[5][5]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\nAimag[6][6]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\nAimag[7][7]=(2*a*a*a*a*a*m)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(6*a*a*a*m*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*m*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nAimag[8][8]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\nAimag[9][9]=(2*a*a*a*m)/pow(a*a+r*r,2)+(2*a*m*r*r)/pow(a*a+r*r,2);\n}\n\nvoid getBreal(const int /*m*/, const double a, const double /*om*/, const double r, const double th,\n              std::array<std::array<double, 10>, 10>& Breal) {\n/* om = m*Omega */\nstd::fill(Breal[0].data(), Breal[0].data() + 100, 0.0);\nBreal[0][0]=(-6*a*a*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(44*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(14*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(6*a*a*a*a*a*a*a*a*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(44*a*a*a*a*a*a*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*a*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(14*a*a*a*a*a*a*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*a*a*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*r*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*a*a*a*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nBreal[0][3]=(32*a*a*a*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nBreal[1][1]=-((a*a*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2)))+(10*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*a*a*sin(4*th))/(2.*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*r*r*sin(4*th))/(2.*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nBreal[1][2]=(-4*a*a*a*a)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nBreal[1][6]=(16*a*a*a*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nBreal[2][1]=(2*a*a*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(4*r*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))+(2*r*r*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)));\nBreal[2][2]=(-2*a*a*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(20*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*a*a*a*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(20*a*a*a*a*a*a*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*a*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*r*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*r*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*r*r*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*r*r*r*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*a*a*pow(cos(th),2)*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*a*r*pow(cos(th),2)*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*r*r*pow(cos(th),2)*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nBreal[2][8]=(16*a*a*a*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nBreal[3][0]=(-4*a*a*a*a*a*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))+(8*a*a*a*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))-(4*a*a*a*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2));\nBreal[3][3]=-((a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))))+(2*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th)))-(r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th)));\nBreal[3][9]=(16*a*a*a*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nBreal[4][4]=(-5*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(10*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(7*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(4*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(2*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(3*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(6*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(3*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)));\nBreal[4][5]=(-8*a*a*a*a)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(32*a*a*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(32*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*a*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(32*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nBreal[5][4]=(4*a*a*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nBreal[5][5]=(-5*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(10*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(7*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(4*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(2*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(3*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(6*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(3*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)));\nBreal[5][7]=(-4*a*a*a*a)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nBreal[6][1]=(-4*a*a*a*a*a*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))+(8*a*a*a*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))-(4*a*a*a*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2));\nBreal[6][6]=(-5*a*a*a*a*a*a*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(13*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(21*a*a*a*a*r*r*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(2*a*a*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(2*a*a*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*a*a*cos(4*th)*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*r*r*cos(4*th)*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2));\nBreal[6][8]=(-2*a*a*a*a)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))+(8*a*a*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(8*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(4*a*a*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))+(8*r*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(2*r*r*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)));\nBreal[7][5]=(4*a*a*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(8*r*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))+(4*r*r*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)));\nBreal[7][7]=(-5*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(10*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(7*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(4*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(2*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(3*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(6*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(3*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)));\nBreal[8][2]=(-4*a*a*a*a*a*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))+(8*a*a*a*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))-(4*a*a*a*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2));\nBreal[8][6]=(2*a*a*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(4*r*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))+(2*r*r*r*r)/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)));\nBreal[8][8]=(-5*a*a*a*a*a*a*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(13*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(21*a*a*a*a*r*r*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(2*a*a*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(2*a*a*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*a*a*cos(4*th)*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*r*r*cos(4*th)*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2));\nBreal[9][3]=(-8*a*a*a*a*a*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))+(16*a*a*a*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))-(8*a*a*a*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2));\nBreal[9][9]=(-3*a*a*a*a*a*a*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(19*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(11*a*a*a*a*r*r*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(2*a*a*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(6*a*a*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*a*a*cos(4*th)*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*r*r*cos(4*th)*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2));\n}\n\nvoid getBimag(const int /*m*/, const double /*a*/, const double /*om*/, const double /*r*/, const double /*th*/,\n              std::array<std::array<double, 10>, 10>& Bimag) {\n/* om = m*Omega */\nstd::fill(Bimag[0].data(), Bimag[0].data() + 100, 0.0);\n}\n\nvoid getCreal(const int m, const double a, const double om, const double r, const double th,\n              std::array<std::array<double, 10>, 10>& Creal) {\n/* om = m*Omega */\nstd::fill(Creal[0].data(), Creal[0].data() + 100, 0.0);\nCreal[0][0]=4/pow(a*a+r*r,2)+(2*a*a)/pow(a*a+r*r,2)+(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(6*a*a)/(r*pow(a*a+r*r,2))+(2*a*a*a*a)/(r*r*pow(a*a+r*r,2))-(2*r)/pow(a*a+r*r,2)-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))+(24*a*a*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*a)/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(160*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(112*a*a*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(160*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(160*a*a*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(96*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*a*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(80*a*a*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(112*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(56*a*a*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(48*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(96*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(56*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*a*a*a*a*a*a*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(160*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(56*a*a*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(16*a*a*a*a*a*a*a*a*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),3))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*a*a*a*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(16*a*a*a*a*a*a*r*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*r*r*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[0][3]=(32*a*a*a*a*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(352*a*a*a*a*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1280*a*a*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(352*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1280*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*a*a*a*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(576*a*a*a*a*a*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1280*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(384*a*a*a*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(576*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1280*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(384*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*a*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(224*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(224*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*a*r*r*r*r*cos(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*r*r*r*r*r*cos(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*a*r*r*r*r*r*r*cos(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*r*r*r*r*r*r*r*cos(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*r*r*r*r*r*r*r*r*cos(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*r*r*r*r*r*r*r*r*r*cos(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*r*r*r*r*r*r*r*r*r*r*cos(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),3)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*a*a*r*r*r*pow(cos(th),3)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),3)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*a*a*r*r*r*r*r*pow(cos(th),3)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),3)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*r*r*r*r*r*r*r*pow(cos(th),3)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),3)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*a*a*a*r*r*r*r*cos(th)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*a*r*r*r*r*r*cos(th)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(384*a*a*a*a*a*r*r*r*r*r*r*cos(th)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*r*r*r*r*r*r*r*cos(th)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*r*r*r*r*r*r*r*r*cos(th)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),3)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*a*a*a*r*r*r*pow(cos(th),3)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(384*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),3)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*a*a*r*r*r*r*r*pow(cos(th),3)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),3)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*a*r*r*r*r*cos(th)*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*r*r*r*r*r*cos(th)*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*r*r*r*r*r*r*cos(th)*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),3)*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*a*a*r*r*r*pow(cos(th),3)*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),3)*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCreal[0][4]=(-24*a*a*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(216*a*a*a*a*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*r*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*r*r*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(216*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(224*a*a*r*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(224*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(8*a*a*a*a*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*r*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(8*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCreal[0][5]=(-48*a*a*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(96*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(240*a*a*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(576*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(288*a*a*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(48*a*a*a*a*a*a*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(96*a*a*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(48*a*a*a*a*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCreal[0][7]=(-144*a*a*a*a*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(608*a*a*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(640*a*a*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(112*a*a*a*a*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*r*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*r*r*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(144*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(608*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(640*a*a*a*a*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(112*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(640*a*a*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(640*a*a*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(160*a*a*r*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(512*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(640*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(640*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(160*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*r*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCreal[0][9]=(16*a*a*a*a*a*a*a*a*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*a*a*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(48*a*a*a*a*a*a*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*r*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*r*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(48*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*a*a*r*r*r*r*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*a*a*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(960*a*a*a*a*a*a*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(3072*a*a*a*a*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(416*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(3072*a*a*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2432*a*a*a*a*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(2560*a*a*r*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(448*a*a*a*a*r*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(2048*r*r*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*r*r*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(3072*r*r*r*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*r*r*r*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1536*r*r*r*r*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*r*r*r*r*r*r*r*r*r*r*r*r*r*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(960*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(3072*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(416*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(3072*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2432*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(2560*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(448*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(2048*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(3072*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1536*a*a*r*r*r*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*r*r*r*r*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*a*a*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2112*a*a*a*a*a*a*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4608*a*a*a*a*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(928*a*a*a*a*a*a*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(3072*a*a*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(3840*a*a*a*a*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(3584*a*a*r*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(768*a*a*a*a*r*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1280*a*a*r*r*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*r*r*r*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2112*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4608*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(928*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(3072*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(3840*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(3584*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(768*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1280*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1344*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1536*a*a*a*a*r*r*r*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(608*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1408*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*r*r*r*r*r*r*r*r*r*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1344*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1536*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(608*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1408*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),3)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(2*th),3)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(2*th),3)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(cos(2*th),3)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),3)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),3)*pow(sin(th),4))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*r*r*r*r*r*r*pow(sin(th),2)*pow(sin(2*th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*r*r*r*r*r*r*r*pow(sin(th),2)*pow(sin(2*th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*r*r*r*r*r*r*r*r*pow(sin(th),2)*pow(sin(2*th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(sin(th),2)*pow(sin(2*th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2)*pow(sin(2*th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2)*pow(sin(2*th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCreal[1][1]=(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))-(8*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*a*a)/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(80*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(136*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(140*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(52*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(112*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(48*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*a*a*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(80*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(112*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(184*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(160*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(40*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(112*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(24*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[1][2]=(-16*a*a*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(6*a*a*a*a*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*a*a*sin(2*th))/(r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(72*a*a*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(136*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(28*a*a*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(208*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(192*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(54*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(192*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*a*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(40*a*a*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*a*a*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*cos(4*th)*sin(2*th))/(r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(24*a*a*a*a*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*r*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[1][6]=(4*a*a*a*a*a*a*a*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(192*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(128*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(128*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(128*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(6*a*a*a*a*a*a*a*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(12*a*a*a*a*a*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(38*a*a*a*a*a*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*r*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*r*r*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*a*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*r*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*r*r*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[1][8]=(-32*a*a*a*a*a*a*a*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*a*a*a*a*a*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(96*a*a*a*a*a*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(256*a*a*a*r*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(128*a*a*a*r*r*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*a*a*a*cos(th)*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*a*a*a*a*a*r*cos(th)*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*a*r*r*cos(th)*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[2][1]=(5*a*a*a*a*a*a*a*a*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(13*a*a*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(21*a*a*a*a*a*a*r*r*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(56*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(160*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*a*a*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*r*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(6*a*a*a*a*a*a*r*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(12*a*a*a*a*r*r*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*r*r*r*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*a*a*sin(6*th))/(2.*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(a*a*a*a*a*a*r*sin(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*r*r*sin(6*th))/(2.*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[2][2]=(2*a*a)/pow(a*a+r*r,2)+(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(4*r)/pow(a*a+r*r,2)-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(2*r*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))-(48*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(96*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(20*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(80*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(96*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(24*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[2][6]=(-16*a*a*a*a*a*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(144*a*a*a*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(320*a*r*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(160*a*r*r*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*r*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*r*r*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[2][8]=(2*a*a*a*a*a*a*a*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(110*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(224*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(128*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(128*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(256*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(256*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(256*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(128*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(3*a*a*a*a*a*a*a*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(6*a*a*a*a*a*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(45*a*a*a*a*a*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(96*a*a*a*r*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(48*a*a*a*r*r*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*a*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*r*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*r*r*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[3][0]=(8*a*a*a*a*a*a*a*a*a*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*a*a*a*a*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*a*a*a*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(40*a*a*a*a*a*a*a*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(160*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(52*a*a*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(232*a*a*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(200*a*a*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(192*a*a*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(240*a*a*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(96*a*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(16*a*a*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(40*a*a*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(12*a*a*a*a*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(120*a*a*a*a*a*a*a*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(92*a*a*a*a*a*a*a*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(432*a*a*a*a*a*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(208*a*a*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(320*a*a*a*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(112*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(12*a*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(88*a*a*a*a*a*a*a*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(160*a*a*a*a*a*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(44*a*a*a*a*a*a*a*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(168*a*a*a*a*a*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(40*a*a*a*a*a*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*pow(cos(2*th),3)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),3)*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*a*a*a*a*a*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCreal[3][3]=(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))-(8*a*a*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(24*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(320*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(104*a*a*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(96*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(112*a*a*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(72*a*a*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(152*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(232*a*a*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(352*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(128*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(192*a*a*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(160*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(72*a*a*a*a*a*a*a*a*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(168*a*a*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(320*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(152*a*a*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(192*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(80*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(40*a*a*a*a*a*a*r*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*r*r*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[3][4]=(24*a*a*a*a*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(312*a*a*a*a*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(320*a*a*a*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*r*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(24*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(312*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(320*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(320*a*a*a*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(320*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(8*a*a*a*a*a*r*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(8*a*a*a*a*a*r*r*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(8*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(8*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCreal[3][5]=(48*a*a*a*a*a*a*a*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(96*a*a*a*a*a*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(368*a*a*a*a*a*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(832*a*a*a*r*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(736*a*a*a*r*r*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(640*a*r*r*r*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(320*a*r*r*r*r*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*a*a*a*a*a*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*a*a*a*r*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(192*a*a*a*a*a*r*r*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(320*a*a*a*r*r*r*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(160*a*a*a*r*r*r*r*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*a*a*r*r*cos(th)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*a*a*a*r*r*r*cos(th)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*r*r*r*r*cos(th)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCreal[3][7]=(-48*a*a*a*a*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(224*a*a*a*a*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(592*a*a*a*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(768*a*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(320*a*r*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(48*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(224*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(592*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(768*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(320*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*a*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*r*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*r*r*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(176*a*a*a*a*a*a*a*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*a*a*r*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(320*a*a*a*r*r*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(352*a*a*a*a*a*r*r*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*r*r*r*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(176*a*a*a*r*r*r*r*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(176*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(320*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(352*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(512*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(176*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCreal[3][9]=(-8*a*a*a*r*r*sin(th))/(pow(a*a+r*r,4)*(-1+cos(2*th)))+(16*a*r*r*r*sin(th))/(pow(a*a+r*r,4)*(-1+cos(2*th)))-(8*a*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*(-1+cos(2*th)))+(4*a*a*a*a*a*a*a*a*a*a*a*a*a*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(12*a*a*a*a*a*a*a*a*a*a*a*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(24*a*a*a*a*a*a*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(136*a*a*a*a*a*a*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(320*a*a*a*a*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(44*a*a*a*a*a*a*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(380*a*a*a*a*a*a*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(512*a*a*a*a*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(288*a*a*a*a*a*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*a*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*a*a*a*a*a*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(144*a*a*a*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*a*r*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(160*a*a*a*r*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(192*a*r*r*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*r*r*r*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(12*a*a*a*a*a*a*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(28*a*a*a*a*a*a*a*a*a*a*a*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*a*a*a*a*a*a*a*a*a*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(264*a*a*a*a*a*a*a*a*a*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(320*a*a*a*a*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(116*a*a*a*a*a*a*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(556*a*a*a*a*a*a*a*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(640*a*a*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(48*a*a*a*a*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(320*a*a*a*a*a*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(320*a*a*a*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(80*a*a*a*a*a*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(48*a*a*a*r*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*a*r*r*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(12*a*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(20*a*a*a*a*a*a*a*a*a*a*a*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(56*a*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(152*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(84*a*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(212*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(24*a*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*a*a*a*a*a*a*a*a*a*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(16*a*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(12*a*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCreal[4][0]=(-39*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*a*a)/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(58*a*a*a*a*a*a)/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(62*a*a*a*a*a*a)/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(27*a*a*a*a*a*a*a*a)/(r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(3*a*a*a*a*a*a*a*a)/(r*r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(48*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(19*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(56*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(28*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(116*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(76*a*a*a*a*a*a*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(48*a*a*a*a*a*a*cos(2*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*a*a*a*a*a*a*cos(2*th))/(r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*a*a*cos(2*th))/(r*r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(80*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(88*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(28*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(11*a*a*a*a*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*a*cos(4*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(6*a*a*a*a*a*a*cos(4*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(14*a*a*a*a*a*a*cos(4*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(3*a*a*a*a*a*a*a*a*cos(4*th))/(r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*a*a*a*a*cos(4*th))/(r*r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(3*a*a*a*a*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCreal[4][3]=(96*a*a*a*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(92*a*a*a*a*a*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(204*a*a*a*a*a*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(76*a*a*a*a*a*a*a*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*a*sin(th))/(r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*a*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(192*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(72*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(272*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(96*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(96*a*a*a*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(136*a*a*a*a*a*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(200*a*a*a*a*a*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(80*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(144*a*a*a*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(40*a*a*a*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCreal[4][4]=14/pow(a*a+r*r,2)+(4*a*a)/pow(a*a+r*r,2)+(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(18*a*a)/(r*pow(a*a+r*r,2))+(6*a*a*a*a)/(r*r*pow(a*a+r*r,2))-(6*r)/pow(a*a+r*r,2)-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))-(20*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(80*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(80*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(68*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(200*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(128*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(96*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(20*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(80*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(80*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(28*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(56*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[4][5]=(-24*a*a*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(12*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(28*a*a*a*a*sin(2*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*a*a*a*a*sin(2*th))/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(20*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(32*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(32*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(32*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(8*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)));\nCreal[4][7]=(-44*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(10*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*a*a)/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*a*a*a*a)/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(36*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(80*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(25*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(96*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(100*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(128*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(56*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(48*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*a*a*a*a*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(48*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(2*a*a*a*a*a*a*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*cos(4*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*a*a*a*a*cos(4*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCreal[4][9]=(-96*a*a*a*a*a*a*a*a*a*a*a*a*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(288*a*a*a*a*a*a*a*a*a*a*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(192*a*a*a*a*a*a*a*a*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(576*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(1440*a*a*a*a*a*a*a*a*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(768*a*a*a*a*a*a*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(1344*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2592*a*a*a*a*a*a*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(960*a*a*a*a*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(1536*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2016*a*a*a*a*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(384*a*a*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(864*a*a*a*a*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(576*a*a*r*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(192*a*a*r*r*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*a*a*a*a*a*a*a*a*a*a*a*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(288*a*a*a*a*a*a*a*a*a*a*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(192*a*a*a*a*a*a*a*a*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(384*a*a*a*a*a*a*a*a*a*a*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(864*a*a*a*a*a*a*a*a*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(384*a*a*a*a*a*a*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(576*a*a*a*a*a*a*a*a*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(864*a*a*a*a*a*a*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(192*a*a*a*a*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(384*a*a*a*a*a*a*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(288*a*a*a*a*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*a*a*a*r*r*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*a*a*a*a*a*a*a*a*a*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(36*a*a*a*a*a*a*a*a*a*a*a*a*pow(sin(th),2))/(r*pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*a*a*a*a*a*a*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(184*a*a*a*a*a*a*a*a*a*a*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(368*a*a*a*a*a*a*a*a*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(96*a*a*a*a*a*a*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(344*a*a*a*a*a*a*a*a*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(416*a*a*a*a*a*a*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(48*a*a*a*a*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(288*a*a*a*a*a*a*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(112*a*a*a*a*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(96*a*a*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(100*a*a*a*a*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(96*a*a*a*a*a*a*a*a*a*a*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(36*a*a*a*a*a*a*a*a*a*a*a*a*cos(2*th)*pow(sin(th),2))/(r*pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*a*a*a*a*a*a*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(112*a*a*a*a*a*a*a*a*a*a*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(176*a*a*a*a*a*a*a*a*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(120*a*a*a*a*a*a*a*a*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*a*a*a*a*a*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(48*a*a*a*a*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*a*a*a*a*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*r*r*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(34*a*a*a*a*a*a*a*a*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(10*a*a*a*a*a*a*a*a*a*a*pow(sin(th),2))/(r*pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*a*a*a*a*a*a*pow(sin(th),2))/(r*r*pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(24*a*a*a*a*a*a*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(30*a*a*a*a*a*a*a*a*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(58*a*a*a*a*a*a*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(24*a*a*a*a*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(10*a*a*a*a*a*a*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(34*a*a*a*a*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(48*a*a*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(30*a*a*a*a*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(60*a*a*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(20*a*a*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(30*a*a*a*a*a*a*a*a*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(10*a*a*a*a*a*a*a*a*a*a*cos(2*th)*pow(sin(th),2))/(r*pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*a*a*a*a*a*a*cos(2*th)*pow(sin(th),2))/(r*r*pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(24*a*a*a*a*a*a*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(10*a*a*a*a*a*a*a*a*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*a*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(24*a*a*a*a*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(10*a*a*a*a*a*a*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))+(30*a*a*a*a*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(10*a*a*a*a*r*r*r*r*r*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,5)*(a*a+2*r*r+a*a*cos(2*th)))-(48*a*a*a*a*a*a*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(4*a*a*a*a*a*a*a*a*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(12*a*a*a*a*a*a*a*a*pow(sin(th),4))/(r*pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(48*a*a*a*a*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(20*a*a*a*a*a*a*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(80*a*a*a*a*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(20*a*a*a*a*a*a*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(48*a*a*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(52*a*a*a*a*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(96*a*a*r*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(36*a*a*a*a*r*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(96*r*r*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(116*a*a*r*r*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(128*r*r*r*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(28*a*a*r*r*r*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(56*r*r*r*r*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(8*r*r*r*r*r*r*r*r*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(48*a*a*a*a*a*a*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(4*a*a*a*a*a*a*a*a*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(12*a*a*a*a*a*a*a*a*cos(2*th)*pow(sin(th),4))/(r*pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(48*a*a*a*a*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(4*a*a*a*a*a*a*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(16*a*a*a*a*r*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(12*a*a*a*a*a*a*r*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(48*a*a*r*r*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(44*a*a*a*a*r*r*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(64*a*a*r*r*r*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(12*a*a*a*a*r*r*r*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))-(28*a*a*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2))+(4*a*a*r*r*r*r*r*r*cos(2*th)*pow(sin(th),4))/(pow(a*a+r*r,5)*pow(-1+cos(2*th),2));\nCreal[5][0]=(-64*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*a*a*a*a*sin(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*a*a*sin(2*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(6*a*a*a*a*a*a*a*a*sin(2*th))/(r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(66*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(36*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*a*a*a*cos(2*th)*sin(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*a*a*cos(2*th)*sin(2*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(6*a*a*a*a*a*a*a*a*cos(2*th)*sin(2*th))/(r*r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(6*a*a*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCreal[5][3]=(64*a*a*a*a*a*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*a*a*a*sin(th)*sin(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(128*a*a*a*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(72*a*a*a*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*a*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*a*sin(th)*sin(2*th))/(r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*a*a*a*sin(th)*sin(2*th))/(r*r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(64*a*a*a*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(80*a*a*a*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(96*a*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(48*a*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*sin(th)*sin(4*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*a*sin(th)*sin(4*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCreal[5][4]=(6*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(10*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCreal[5][5]=8/pow(a*a+r*r,2)+(6*a*a)/pow(a*a+r*r,2)+(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(8*a*a)/(r*pow(a*a+r*r,2))+(2*a*a*a*a)/(r*r*pow(a*a+r*r,2))-(12*r)/pow(a*a+r*r,2)-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(4*r*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))-(16*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(176*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(128*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(80*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(128*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[5][7]=(-8*a*a*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*a*a*sin(2*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*a*a*sin(2*th))/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(16*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(16*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(4*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)));\nCreal[5][9]=(-16*a*a*a*a*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(10*a*a*a*a*a*a*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(6*a*a*a*a*a*a*pow(sin(th),2)*sin(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*a*r*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*a*a*r*r*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*a*a*a*a*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*a*a*pow(sin(th),2)*sin(2*th))/(r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(128*a*a*a*a*r*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(192*a*a*r*r*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*a*a*a*a*r*r*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(144*a*a*r*r*r*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*r*r*r*r*pow(sin(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*a*a*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(128*a*a*a*a*r*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(128*a*a*r*r*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(96*a*a*a*a*r*r*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(256*a*a*r*r*r*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(128*r*r*r*r*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(96*a*a*r*r*r*r*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(128*r*r*r*r*r*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*r*r*r*r*r*r*pow(sin(th),4)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),3)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(a*a*a*a*a*a*pow(sin(th),2)*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(a*a*a*a*a*a*pow(sin(th),2)*sin(4*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCreal[6][1]=(-68*a*a*a*a*a*a*a*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*a*a*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(6*a*a*a*a*a*a*a*a*a*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(124*a*a*a*a*a*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(62*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(112*a*a*a*a*a*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(104*a*a*a*a*a*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(96*a*a*a*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(48*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*a*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*a*cos(4*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*a*a*a*a*cos(4*th)*sin(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(12*a*a*a*a*a*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(6*a*a*a*a*a*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[6][2]=(-64*a*a*a*a*a*a*a*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(40*a*a*a*a*a*a*a*cos(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(20*a*a*a*a*a*a*a*a*a*cos(th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(168*a*a*a*a*a*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(308*a*a*a*a*a*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(448*a*a*a*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(320*a*a*a*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(192*a*r*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*r*r*r*r*r*r*cos(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*a*a*a*a*a*a*a*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*a*a*a*cos(th)*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*a*a*a*cos(th)*cos(2*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*a*a*a*a*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(112*a*a*a*a*a*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(128*a*a*a*r*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*a*a*a*r*r*r*r*cos(th)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*a*cos(th)*cos(4*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*a*a*cos(th)*cos(4*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*r*cos(th)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*r*r*cos(th)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[6][6]=(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(4*a*a)/(r*pow(a*a+r*r,2))+(2*a*a*a*a)/(r*r*pow(a*a+r*r,2))+(4*r)/pow(a*a+r*r,2)-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)-(2*r*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))+(16*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a)/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(48*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(112*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(44*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(80*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(128*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(84*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(256*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(192*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(128*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*a*a*a*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(56*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(56*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*a*a*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(48*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(12*a*a*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[6][8]=(2*a*a*a*a*sin(2*th))/(r*pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(a*a*a*a*a*a*sin(2*th))/(r*r*pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(2*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))+(a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))+(4*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(-1+cos(2*th)))-(16*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(-1+cos(2*th)))+(16*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(-1+cos(2*th)))+(8*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(-1+cos(2*th)))-(16*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(-1+cos(2*th)))+(4*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(-1+cos(2*th)))-(48*a*a*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(96*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(96*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(96*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(48*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*a*a*a*a*sin(2*th))/(r*pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2))*(a*a+2*r*r+a*a*cos(2*th)));\nCreal[7][0]=(-36*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(28*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(24*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(36*a*a*a*a*a*a*a*a*pow(cos(th),2))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(28*a*a*a*a*a*a*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(24*a*a*a*a*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(40*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*a*a*pow(cos(th),2)*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*a*a*a*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(40*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*a*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*a*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*a*a*a*pow(cos(th),2)*cos(4*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*a*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[7][3]=(-120*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(128*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(128*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(96*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(120*a*a*a*a*a*a*a*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(128*a*a*a*a*a*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(128*a*a*a*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(96*a*a*a*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(112*a*a*a*a*a*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(128*a*a*a*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(112*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(112*a*a*a*a*a*a*a*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(128*a*a*a*a*a*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(112*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*a*pow(cos(th),2)*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[7][4]=(3*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))+(2*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))-(6*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))+(2*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))+(3*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2))+(a*a*a*a*r*r*cos(th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),2));\nCreal[7][5]=(8*a*a*a*a*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*a*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*r*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCreal[7][7]=4/pow(a*a+r*r,2)+(4*a*a)/pow(a*a+r*r,2)+(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(2*a*a)/(r*pow(a*a+r*r,2))-(10*r)/pow(a*a+r*r,2)-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(4*r*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))-(4*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(20*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(64*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(48*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(16*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(8*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[7][9]=(-3*a*a)/pow(a*a+r*r,2)+(18*r)/pow(a*a+r*r,2)-(2*r*r)/pow(a*a+r*r,2)-(a*a*cos(2*th))/pow(a*a+r*r,2)-(36*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(44*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(40*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(36*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(68*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(16*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[8][1]=(16*a*a*a*a*a*a*a*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*a*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(144*a*a*a*a*a*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(320*a*a*a*r*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(160*a*a*a*r*r*r*r*cos(th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*a*cos(th)*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*a*r*cos(th)*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*r*r*cos(th)*cos(2*th)*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[8][2]=(32*a*a*a*a*a*a*a*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*a*a*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(80*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(136*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(192*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*a*a*a*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*a*a*a*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(56*a*a*a*a*a*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(48*a*a*a*r*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(24*a*a*a*r*r*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[8][6]=(2*a*a*r*r*sin(2*th))/(pow(a*a+r*r,3)*(-1+cos(2*th)))-(4*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*(-1+cos(2*th)))+(2*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*(-1+cos(2*th)))+(4*a*a*a*a*a*a*a*a*a*a*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(22*a*a*a*a*a*a*a*a*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(100*a*a*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(144*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(42*a*a*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(200*a*a*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(160*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(96*a*a*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*a*a*a*a*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(28*a*a*a*a*a*a*a*a*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(28*a*a*a*a*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*r*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(6*a*a*a*a*a*a*a*a*r*r*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*r*r*r*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*r*r*r*r*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCreal[8][8]=(4*a*a)/pow(a*a+r*r,2)+(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))-(8*r)/pow(a*a+r*r,2)-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(4*r*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))+(8*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*a)/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(40*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(72*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(60*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(52*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(48*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(88*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(64*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(40*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(64*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*a*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(56*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(20*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(32*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(16*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\nCreal[9][0]=(-24*a*a*a*a*a*a*a*a*a*a*a*a*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(48*a*a*a*a*a*a*a*a*a*a*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*a*a*a*a*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(152*a*a*a*a*a*a*a*a*a*a*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*a*a*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(416*a*a*a*a*a*a*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(344*a*a*a*a*a*a*a*a*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(208*a*a*a*a*a*a*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(296*a*a*a*a*a*a*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(288*a*a*a*a*r*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*r*r*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*r*r*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*r*r*r*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(160*a*a*r*r*r*r*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*r*r*r*r*r*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*r*r*r*r*r*r*r*r*r*r*r*r*r)/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(48*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),2))/(r*(a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(152*a*a*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(416*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(344*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(208*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(296*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(288*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(160*a*a*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*r*r*r*r*r*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*r*r*r*r*r*r*r*r*r*r*r*pow(cos(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*a*a*a*a*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*a*a*a*a*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(288*a*a*a*a*a*a*a*a*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(376*a*a*a*a*a*a*a*a*a*a*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*a*a*a*a*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(416*a*a*a*a*a*a*a*a*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(160*a*a*a*a*a*a*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(728*a*a*a*a*a*a*a*a*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(624*a*a*a*a*a*a*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*r*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(648*a*a*a*a*a*a*r*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*r*r*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*r*r*r*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*r*r*r*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*r*r*r*r*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*r*r*r*r*r*r*r*r*r*r*r*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),2)*cos(2*th))/(r*(a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(288*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(376*a*a*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(416*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(160*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(728*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(624*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(128*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(648*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(32*a*a*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*a*a*a*a*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(240*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*a*a*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(296*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(768*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(456*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*r*r*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(816*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*a*a*r*r*r*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(312*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(288*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(80*a*a*a*a*r*r*r*r*r*r*r*r*r*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(240*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),2)*pow(cos(2*th),2))/(r*(a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(296*a*a*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(768*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(456*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(816*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(312*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(288*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(80*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*a*a*a*a*a*a*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(144*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*a*a*r*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*r*r*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(288*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(144*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*r*r*r*r*r*r*r*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(144*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),2)*pow(cos(2*th),3))/(r*(a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(288*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(144*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(cos(2*th),3))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2*a*a*a*a*a*a*a*a*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(4*a*a*a*a*a*a*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(14*a*a*a*a*a*a*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(24*a*a*a*a*r*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(30*a*a*a*a*r*r*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(36*a*a*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(18*a*a*r*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(2*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(4*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(2*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(8*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(34*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(60*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(30*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(2*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),4)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(4*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),4)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(14*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),4)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(24*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),4)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(2*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),4)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(28*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),4)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(14*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),4)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(2*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),6)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(4*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),6)*pow(sin(th),2))/(r*(a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(2*a*a*a*a*a*a*a*a*a*a*a*a*a*a*pow(cos(th),6)*pow(sin(th),2))/(r*r*(a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(8*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),6)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(2*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),6)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(4*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),6)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(2*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),6)*pow(sin(th),2))/((a*a+(-2+r)*r)*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(8*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*pow(sin(th),4))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(16*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*pow(sin(th),4))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(8*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*pow(sin(th),4))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(8*a*a*a*a*a*a*a*a*a*a*pow(cos(th),4)*pow(sin(th),4))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))-(16*a*a*a*a*a*a*a*a*r*pow(cos(th),4)*pow(sin(th),4))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5))+(8*a*a*a*a*a*a*a*a*r*r*pow(cos(th),4)*pow(sin(th),4))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),5));\nCreal[9][3]=(8*a*a*a*a*a*a*a*a*a*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*a*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(104*a*a*a*a*a*a*a*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(320*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*a*a*a*a*a*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(128*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(112*a*a*a*a*a*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(480*a*a*a*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(288*a*a*a*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(512*a*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(192*a*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(24*a*a*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*a*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(264*a*a*a*a*a*a*a*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(288*a*a*a*a*a*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(192*a*a*a*a*a*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*a*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(224*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(24*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(152*a*a*a*a*a*a*a*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(320*a*a*a*a*a*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(96*a*a*a*a*a*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(80*a*a*a*a*a*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*a*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*pow(cos(2*th),3)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCreal[9][4]=(-4*a*a*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(152*a*a*a*a*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(80*a*a*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(20*a*a*a*a*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(72*a*a*a*a*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*r*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*r*r*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*r*r*r*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(152*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(80*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(20*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(72*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(288*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*r*r*r*r*r*r*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(2*a*a*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(100*a*a*a*a*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(30*a*a*a*a*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*r*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*r*r*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(2*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(100*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(30*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(192*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(96*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(96*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(4*a*a*a*a*a*a*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(56*a*a*a*a*a*a*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(80*a*a*a*a*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(12*a*a*a*a*a*a*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*r*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(24*a*a*a*a*r*r*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(4*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(56*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(80*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(12*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(24*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2*a*a*a*a*a*a*r*r*r*r*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*r*r*r*r*r*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2*a*a*a*a*a*a*r*r*r*r*r*r*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(2*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCreal[9][5]=(40*a*a*a*a*a*a*a*a*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(80*a*a*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(40*a*a*a*a*a*a*a*a*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(440*a*a*a*a*a*a*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1120*a*a*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(184*a*a*a*a*a*a*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(912*a*a*a*a*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1280*a*a*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(336*a*a*a*a*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*r*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*r*r*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*r*r*r*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*r*r*r*r*r*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(40*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(80*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(40*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(440*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1120*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(184*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(912*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1280*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(336*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(256*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(320*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(256*a*a*r*r*r*r*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*r*r*r*r*r*r*r*r*r*r*pow(cos(th),2)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*a*a*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(8*a*a*a*a*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(60*a*a*a*a*a*a*a*a*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(692*a*a*a*a*a*a*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1152*a*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(252*a*a*a*a*a*a*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1600*a*a*a*a*r*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1280*a*a*r*r*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(384*a*a*a*a*r*r*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1024*a*a*r*r*r*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*r*r*r*r*r*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(8*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(60*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(692*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1152*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(252*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1600*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(1280*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(384*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(1024*a*a*a*a*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(192*a*a*a*a*r*r*r*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(40*a*a*a*a*a*a*a*a*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(80*a*a*a*a*a*a*r*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*a*a*r*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(8*a*a*a*a*a*a*r*r*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*r*r*r*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*r*r*r*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(80*a*a*a*a*r*r*r*r*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(48*a*a*a*a*r*r*r*r*r*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(40*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(80*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(24*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(8*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(72*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(80*a*a*a*a*a*a*r*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(48*a*a*a*a*a*a*r*r*r*r*r*r*pow(cos(th),2)*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(4*a*a*a*a*a*a*a*a*r*r*r*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(8*a*a*a*a*a*a*r*r*r*r*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*a*a*r*r*r*r*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(12*a*a*a*a*a*a*r*r*r*r*r*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*r*r*r*r*r*r*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(4*a*a*a*a*a*a*a*a*a*a*r*pow(cos(th),2)*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(8*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*a*a*a*a*r*r*pow(cos(th),2)*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))+(12*a*a*a*a*a*a*a*a*r*r*r*pow(cos(th),2)*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5))-(4*a*a*a*a*a*a*a*a*r*r*r*r*pow(cos(th),2)*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCreal[9][7]=(-70*a*a*a*a*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(100*a*a*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(120*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(320*a*a*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(80*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(496*a*a*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(574*a*a*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(828*a*a*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(320*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(500*a*a*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(560*a*a*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(208*a*a*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(128*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*r*r*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(47*a*a*a*a*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(92*a*a*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(190*a*a*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(268*a*a*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(287*a*a*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(256*a*a*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(192*a*a*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(80*a*a*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(48*a*a*r*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(10*a*a*a*a*a*a*a*a*a*a*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(60*a*a*a*a*a*a*a*a*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(120*a*a*a*a*a*a*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*a*a*a*a*a*a*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(80*a*a*a*a*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(128*a*a*a*a*a*a*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*a*a*a*a*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(34*a*a*a*a*a*a*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(68*a*a*a*a*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(12*a*a*a*a*r*r*r*r*r*r*cos(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(a*a*a*a*a*a*a*a*a*a*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*r*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*a*a*a*a*r*r*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(2*a*a*a*a*a*a*a*a*r*r*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*r*r*r*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(a*a*a*a*a*a*r*r*r*r*cos(6*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(128*a*a*a*a*a*a*a*a*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(256*a*a*a*a*a*a*a*a*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(640*a*a*a*a*a*a*a*a*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(1024*a*a*a*a*a*a*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(1280*a*a*a*a*a*a*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(1536*a*a*a*a*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(1280*a*a*a*a*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(1024*a*a*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(640*a*a*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(256*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(128*r*r*r*r*r*r*r*r*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCreal[9][9]=-2/pow(a*a+r*r,2)-(2*a*a)/pow(a*a+r*r,2)+(a*a*a*a*om*om)/(2.*pow(a*a+r*r,2))+(2*a*a)/(r*pow(a*a+r*r,2))+(2*r)/pow(a*a+r*r,2)-(4*a*m*om*r)/pow(a*a+r*r,2)+(a*a*om*om*r)/pow(a*a+r*r,2)+(3*a*a*om*om*r*r)/(2.*pow(a*a+r*r,2))+(om*om*r*r*r*r)/pow(a*a+r*r,2)+(a*a*a*a*om*om*cos(2*th))/(2.*pow(a*a+r*r,2))-(a*a*om*om*r*cos(2*th))/pow(a*a+r*r,2)+(a*a*om*om*r*r*cos(2*th))/(2.*pow(a*a+r*r,2))+(16*a*a*a*a*a*a*a*a)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*a)/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(40*a*a*a*a*a*a*a*a*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(72*a*a*a*a*a*a*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(160*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*a*a*a*a*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(120*a*a*a*a*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*a*a*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(40*a*a*a*a*a*a*a*a*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*a*cos(2*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(104*a*a*a*a*a*a*a*a*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(152*a*a*a*a*a*a*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(176*a*a*a*a*a*a*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(208*a*a*a*a*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(160*a*a*a*a*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*a*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*r*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),2))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(88*a*a*a*a*a*a*a*a*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(120*a*a*a*a*a*a*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(160*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(128*a*a*a*a*a*a*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(120*a*a*a*a*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*a*a*a*r*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*a*pow(cos(2*th),3))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(40*a*a*a*a*a*a*r*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*a*r*r*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*a*a*m*m*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(16*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))+(8*m*m*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(8*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2))-(4*m*m*r*r*pow(sin(th),2))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2));\n}\n\nvoid getCimag(const int m, const double a, const double om, const double r, const double th,\n              std::array<std::array<double, 10>, 10>& Cimag) {\n/* om = m*Omega */\nstd::fill(Cimag[0].data(), Cimag[0].data() + 100, 0.0);\nCimag[0][0]=(4*a*m)/pow(a*a+r*r,2)-(2*a*a*a*m)/(r*pow(a*a+r*r,2))-(2*a*m*r)/pow(a*a+r*r,2)+(8*a*a*a*a*a*a*a*a*a*m)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(24*a*a*a*a*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(96*a*a*a*m*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(64*a*m*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(24*a*a*a*a*a*a*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(56*a*a*a*a*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*a*m*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(24*a*a*a*a*a*a*a*a*a*m*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(40*a*a*a*a*a*a*a*m*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(16*a*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*a*m*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*m*r*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCimag[0][1]=(-8*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*om*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*om*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*om*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*om*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*om*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[0][2]=(32*a*a*a*m*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*om*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*m*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*om*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*m*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*om*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*om*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*om*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*om*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[0][3]=(16*a*a*a*a*a*a*a*a*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*m*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*m*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*a*a*a*a*m*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*a*a*m*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*m*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(128*a*a*a*a*m*r*r*r*r*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*a*a*m*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*m*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*a*a*a*a*m*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(64*a*a*a*a*a*a*a*a*m*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))-(64*a*a*a*a*a*a*m*r*r*r*r*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*m*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(48*a*a*a*a*a*a*a*a*a*a*m*r*pow(cos(th),2)*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(32*a*a*a*a*a*a*a*a*m*r*r*r*pow(cos(th),2)*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*a*a*m*r*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5))+(16*a*a*a*a*a*a*a*a*a*a*m*r*pow(cos(th),2)*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),5));\nCimag[1][0]=(6*a*a*a*m)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*a*m)/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*a*om)/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*m*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*om*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(12*a*m*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(6*a*a*om*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*om*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*a*om*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*a*m*cos(2*th))/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*a*om*cos(2*th))/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*om*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*om*r*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[1][1]=(-2*a*m)/pow(a*a+r*r,2)+(2*a*a*a*m)/(r*pow(a*a+r*r,2))+(4*a*a*a*a*a*a*a*m)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(20*a*a*a*a*a*m*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*m*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*m*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*m*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*m*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(24*a*a*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*m*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*m*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*m*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*a*m*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*m*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[1][2]=(-2*a*a*a*a*a*a*a*m*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*a*a*a*m*sin(2*th))/(r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(5*a*a*a*a*a*a*a*m*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*m*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*m*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*m*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*m*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(a*a*a*a*a*a*a*m*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*a*a*a*m*cos(2*th)*sin(2*th))/(2.*r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*a*m*r*cos(2*th)*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*m*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*m*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*m*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*a*m*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(a*a*a*a*a*a*a*a*a*m*cos(4*th)*sin(2*th))/(r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(5*a*a*a*a*a*a*a*m*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*m*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*m*r*r*r*cos(4*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*a*m*cos(6*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(a*a*a*a*a*a*a*a*a*m*cos(6*th)*sin(2*th))/(2.*r*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3))-(a*a*a*a*a*a*a*m*r*cos(6*th)*sin(2*th))/(2.*pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[1][3]=(5*a*a*a*a*m*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(a*a*a*a*m*sin(th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(a*a*a*a*a*om*sin(th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(12*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(5*a*a*a*om*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*m*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*om*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(12*a*om*r*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*a*a*m*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(32*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(32*m*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*m*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(32*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(8*m*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(a*a*a*a*m*sin(3*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(a*a*a*a*m*sin(3*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(a*a*a*a*a*om*sin(3*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(a*a*a*om*r*sin(3*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[1][4]=(-4*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*om*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*om*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*om*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*om*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*om*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[1][5]=(16*a*a*a*m*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*a*a*om*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*m*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*om*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*m*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*om*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*om*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*om*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*om*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[1][6]=(2*a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(3*a*a*a*a*a*a*m*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*m*r*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[2][0]=(-4*a*a*a*m*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*a*om*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*om*r*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[2][1]=(5*a*a*a*a*a*a*a*m*r*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*m*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*m*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*a*m*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*m*r*r*r*sin(4*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*a*m*r*sin(6*th))/(2.*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[2][2]=(4*a*a*a*a*a*a*a*m)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(20*a*a*a*a*a*m*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*m*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*m*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*m*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*m*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(24*a*a*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*m*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*m*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*m*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*a*m*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*m*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[2][3]=(16*a*a*a*a*m*r*r*cos(th)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*om*r*r*r*cos(th)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*a*a*m*pow(cos(th),3)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*a*om*r*pow(cos(th),3)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*m*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*m*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*m*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*m*r*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*m*r*r*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*m*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*m*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*a*a*m*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*a*a*m*r*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*m*r*r*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[2][5]=(-4*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*om*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*om*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*om*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*om*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*om*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[2][7]=(16*a*a*a*m*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*a*a*om*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*m*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*om*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*m*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*om*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*om*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*om*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*om*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[2][8]=(2*a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(3*a*a*a*a*a*a*m*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*m*r*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[3][0]=(-4*a*a*a*a*a*a*a*a*a*a*m*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(56*a*a*a*a*a*a*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(144*a*a*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(96*a*a*m*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(12*a*a*a*a*a*a*a*a*a*a*m*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*a*a*a*a*a*a*m*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(96*a*a*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(112*a*a*a*a*m*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(12*a*a*a*a*a*a*a*a*a*a*m*pow(cos(2*th),2)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*m*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(40*a*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(4*a*a*a*a*a*a*a*a*a*a*m*pow(cos(2*th),3)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*m*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCimag[3][1]=-0.25*(a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*a*om*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(3*a*a*a*a*a*a*m*r*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(5*a*a*a*a*m*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(3*a*a*a*a*a*om*r*r*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(7*a*a*a*a*m*r*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(10*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(11*a*a*a*om*r*r*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*m*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(8*m*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*om*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(4*m*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*a*om*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(7*a*a*a*a*a*a*m*r*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(4*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*om*r*r*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*a*a*m*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(6*a*a*m*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(2*a*a*a*om*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*m*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(6*a*om*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*om*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(2*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(3*a*a*a*om*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*m*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*a*om*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*m*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*om*r*r*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*m*r*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*om*r*r*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*m*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*a*om*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*om*r*r*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)));\nCimag[3][2]=(3*a*a*a*a*a*a*a*a*m*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*m*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*a*a*a*a*om*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*m*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(17*a*a*a*a*a*a*m*r*r*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*om*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*m*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(9*a*a*a*a*a*om*r*r*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(19*a*a*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(6*a*a*a*om*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*m*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*om*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(10*a*a*m*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*m*r*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*m*r*r*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(7*a*a*a*a*a*a*a*a*m*cos(2*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(7*a*a*a*a*a*a*m*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*om*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(31*a*a*a*a*a*a*m*r*r*cos(2*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*om*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*a*a*m*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(9*a*a*a*a*a*om*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(8*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(12*a*a*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*a*om*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*m*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*om*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(6*a*a*m*r*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*a*a*m*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*a*a*om*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*a*om*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(6*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*om*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*m*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*a*om*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*a*m*r*r*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*om*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*m*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*a*a*om*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*m*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*om*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*om*r*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*m*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*a*om*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*m*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*om*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*om*r*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2));\nCimag[3][3]=(2*a*m)/pow(a*a+r*r,2)-(2*a*m*r)/pow(a*a+r*r,2);\nCimag[3][6]=(-4*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*om*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*om*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*om*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*om*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*om*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[3][8]=(16*a*a*a*m*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*a*a*om*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*m*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*om*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*m*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*om*r*r*r*r*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*om*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*om*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*om*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[3][9]=(4*a*a*a*a*a*a*a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(16*a*a*a*a*a*a*a*a*a*a*m*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(56*a*a*a*a*a*a*m*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(80*a*a*a*a*m*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(32*a*a*m*r*r*r*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(12*a*a*a*a*a*a*a*a*a*a*a*a*m*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(40*a*a*a*a*a*a*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(28*a*a*a*a*a*a*a*a*m*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*a*m*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*m*r*r*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(12*a*a*a*a*a*a*a*a*a*a*a*a*m*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*a*a*a*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(28*a*a*a*a*a*a*a*a*m*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*m*r*r*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*a*a*a*a*m*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*a*a*m*r*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(4*a*a*a*a*a*a*a*a*m*r*r*r*r*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,4)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCimag[4][1]=(12*a*a*a*m)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*a*a*a*m)/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*a*a*a*om)/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(32*a*m*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*om*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(24*a*m*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(12*a*a*om*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*om*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*a*a*om*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*a*a*a*m*cos(2*th))/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*a*a*a*om*cos(2*th))/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*a*om*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*om*r*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[4][4]=(-8*a*m)/pow(a*a+r*r,2)+(6*a*a*a*m)/(r*pow(a*a+r*r,2))+(2*a*m*r)/pow(a*a+r*r,2)-(8*a*a*a*m*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*m*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*m*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[4][5]=(8*a*a*a*m*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*a*a*a*m*sin(2*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*a*a*a*m*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[4][6]=(10*a*a*a*a*m*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*m*sin(th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*om*sin(th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(24*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(10*a*a*a*om*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*m*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(32*a*om*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(24*a*om*r*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*a*a*m*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(64*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(64*m*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(32*a*a*m*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(64*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(16*m*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*m*sin(3*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*m*sin(3*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*om*sin(3*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*om*r*sin(3*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[5][1]=(-4*a*a*a*m*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*a*om*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*om*r*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[5][2]=(6*a*a*a*m)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*a*m)/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*a*om)/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*m*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*om*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(12*a*m*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(6*a*a*om*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(4*om*r*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*a*om*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*a*m*cos(2*th))/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*a*a*a*a*om*cos(2*th))/(r*r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*om*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(2*a*a*om*r*r*cos(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[5][4]=(2*a*a*a*m*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[5][5]=(-6*a*m)/pow(a*a+r*r,2)+(4*a*a*a*m)/(r*pow(a*a+r*r,2))+(2*a*m*r)/pow(a*a+r*r,2)-(8*a*a*a*m*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*m*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*m*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[5][6]=(8*a*a*a*a*m*cos(th)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(16*a*a*a*om*r*cos(th)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*m*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*m*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*a*a*m*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*m*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*a*m*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[5][7]=(4*a*a*a*m*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*a*a*m*sin(2*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(2*a*a*a*m*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[5][8]=(5*a*a*a*a*m*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(a*a*a*a*m*sin(th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(a*a*a*a*a*om*sin(th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(12*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(5*a*a*a*om*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*m*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*om*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(12*a*om*r*r*r*sin(th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*a*a*m*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(32*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(32*m*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*a*m*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))-(32*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(8*m*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*(-1+cos(2*th))*(a*a+2*r*r+a*a*cos(2*th)))+(a*a*a*a*m*sin(3*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(a*a*a*a*m*sin(3*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(a*a*a*a*a*om*sin(3*th))/(r*pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(a*a*a*om*r*sin(3*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[6][1]=(-6*a*a*a*a*a*a*a*a*m*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(6*a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*a*a*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*a*m*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*a*m*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(2*a*a*a*a*a*a*a*a*m*cos(4*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*m*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[6][3]=(-4*a*a*a*a*a*m)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*om)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*m*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*om*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*om*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*om*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*a*a*a*m*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*a*m*pow(cos(th),2))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*a*a*om*pow(cos(th),2))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*m*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*om*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(24*a*a*a*m*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*a*om*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(8*a*a*om*r*r*r*r*pow(cos(th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*om*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*om*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*om*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*om*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*m*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(8*a*a*a*a*a*a*om*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*a*m*pow(cos(th),2)*cos(2*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*a*a*om*pow(cos(th),2)*cos(2*th))/(r*r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*om*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*om*r*r*pow(cos(th),2)*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[6][4]=-0.25*(a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*a*om*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(3*a*a*a*a*a*a*m*r*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(5*a*a*a*a*m*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(3*a*a*a*a*a*om*r*r*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(7*a*a*a*a*m*r*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(10*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(11*a*a*a*om*r*r*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*m*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(8*m*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*om*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(4*m*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*a*om*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(7*a*a*a*a*a*a*m*r*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(4*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*om*r*r*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*a*a*m*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(6*a*a*m*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(2*a*a*a*om*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*m*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(6*a*om*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*om*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(2*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(3*a*a*a*om*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*m*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*a*om*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*m*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*om*r*r*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*m*r*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*om*r*r*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*m*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*a*om*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*om*r*r*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)));\nCimag[6][5]=(3*a*a*a*a*a*a*a*a*m*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*m*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*a*a*a*a*om*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*m*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(17*a*a*a*a*a*a*m*r*r*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*om*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*m*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(9*a*a*a*a*a*om*r*r*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(19*a*a*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(6*a*a*a*om*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*m*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*om*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(10*a*a*m*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*m*r*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*m*r*r*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(7*a*a*a*a*a*a*a*a*m*cos(2*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(7*a*a*a*a*a*a*m*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*om*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(31*a*a*a*a*a*a*m*r*r*cos(2*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*om*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*a*a*m*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(9*a*a*a*a*a*om*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(8*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(12*a*a*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*a*om*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*m*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*om*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(6*a*a*m*r*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*a*a*m*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*a*a*om*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*a*om*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(6*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*om*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*m*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*a*om*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*a*m*r*r*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*om*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*m*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*a*a*om*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*m*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*om*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*om*r*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*m*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*a*om*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*m*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*om*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*om*r*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2));\nCimag[6][6]=(-4*a*m)/pow(a*a+r*r,2)+(4*a*a*a*m)/(r*pow(a*a+r*r,2))-(4*a*a*a*a*a*a*a*m)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(20*a*a*a*a*a*m*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*m*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*m*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*m*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*m*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*m*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*m*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*m*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*m*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[6][8]=(2*a*a*a*m*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(a*a*a*a*a*m*sin(2*th))/(r*pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)))-(a*a*a*m*r*sin(2*th))/(pow(a*a+r*r,2)*(r*r+a*a*pow(cos(th),2)));\nCimag[6][9]=(-4*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*om*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*a*a*m*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(24*a*a*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*om*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*m*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*om*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(24*a*om*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(12*a*a*a*a*a*a*m*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*m*pow(cos(th),2)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*a*om*pow(cos(th),2)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(24*a*a*a*a*m*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(12*a*a*a*a*a*om*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*m*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*a*om*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(24*a*a*a*om*r*r*r*pow(cos(th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*m*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*om*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*m*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*om*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*m*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*a*m*pow(cos(th),2)*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(4*a*a*a*a*a*a*a*om*pow(cos(th),2)*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(4*a*a*a*a*a*om*r*pow(cos(th),2)*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*m*r*r*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(128*a*a*m*r*r*r*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(128*m*r*r*r*r*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*a*m*r*r*r*r*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(128*m*r*r*r*r*r*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*m*r*r*r*r*r*r*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*a*a*m*pow(cos(th),2)*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(128*a*a*a*a*m*r*pow(cos(th),2)*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(128*a*a*m*r*r*pow(cos(th),2)*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*a*a*a*m*r*r*pow(cos(th),2)*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(128*a*a*m*r*r*r*pow(cos(th),2)*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*m*r*r*r*r*pow(cos(th),2)*pow(sin(th),3))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[7][2]=(-8*a*a*a*m*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*a*a*om*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(8*a*a*om*r*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[7][5]=(4*a*a*a*m*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[7][7]=(-4*a*m)/pow(a*a+r*r,2)+(2*a*a*a*m)/(r*pow(a*a+r*r,2))+(2*a*m*r)/pow(a*a+r*r,2)-(8*a*a*a*m*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(16*a*m*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))-(8*a*m*r*r*r)/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[7][8]=(32*a*a*a*a*m*r*r*cos(th)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*a*a*om*r*r*r*cos(th)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*a*a*a*a*m*pow(cos(th),3)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(64*a*a*a*a*a*om*r*pow(cos(th),3)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(64*m*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*m*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*m*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(64*a*a*m*r*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*m*r*r*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*m*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(64*a*a*m*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*a*a*m*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(64*a*a*a*a*m*r*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*m*r*r*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[8][2]=(-2*a*a*a*a*a*a*a*a*m*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*a*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(3*a*a*a*a*a*a*a*a*m*sin(3*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(3*a*a*a*a*a*a*m*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*m*r*r*r*sin(3*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(a*a*a*a*a*a*a*a*m*sin(5*th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(a*a*a*a*a*a*m*r*sin(5*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[8][3]=(-4*a*a*a*m*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*a*a*om*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)))+(4*a*a*om*r*r*sin(2*th))/(pow(a*a+r*r,2)*(a*a+2*r*r+a*a*cos(2*th)));\nCimag[8][5]=-0.25*(a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*a*om*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(3*a*a*a*a*a*a*m*r*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(5*a*a*a*a*m*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(3*a*a*a*a*a*om*r*r*r*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(7*a*a*a*a*m*r*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(10*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(11*a*a*a*om*r*r*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*m*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(8*m*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*om*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(4*m*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*a*om*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(7*a*a*a*a*a*a*m*r*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(4*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*om*r*r*r*cos(2*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*a*a*m*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(6*a*a*m*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(2*a*a*a*om*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(6*a*a*m*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(6*a*om*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*om*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(2*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(3*a*a*a*om*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*m*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*a*om*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*m*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*om*r*r*r*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*m*r*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*om*r*r*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*m*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*a*om*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*om*r*r*r*cos(2*th)*cos(4*th)*sin(th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)));\nCimag[8][6]=(2*a*a*a*a*a*a*a*a*a*m*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(10*a*a*a*a*a*a*a*m*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(16*a*a*a*a*a*m*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*m*r*r*r*r*r*r*r*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*a*a*a*a*m*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(12*a*a*a*a*a*a*a*m*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*m*r*r*r*r*r*cos(2*th)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*a*a*a*m*r*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(2*a*a*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(2*th))/(pow(a*a+r*r,3)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[8][7]=(3*a*a*a*a*a*a*a*a*m*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*m*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*a*a*a*a*om*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*m*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(17*a*a*a*a*a*a*m*r*r*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*om*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*m*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(9*a*a*a*a*a*om*r*r*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(19*a*a*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(6*a*a*a*om*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*m*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*om*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(10*a*a*m*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*m*r*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*m*r*r*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(7*a*a*a*a*a*a*a*a*m*cos(2*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(7*a*a*a*a*a*a*m*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*om*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(31*a*a*a*a*a*a*m*r*r*cos(2*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*om*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*a*a*m*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(9*a*a*a*a*a*om*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(8*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(12*a*a*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*a*om*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*m*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*om*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(6*a*a*m*r*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*a*a*m*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*a*a*om*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(4*a*a*a*a*a*om*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(6*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*om*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*m*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*a*om*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*a*m*r*r*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*om*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*m*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*a*a*om*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*m*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*om*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*om*r*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*m*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*a*om*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*m*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(4.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*om*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*om*r*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2));\nCimag[8][8]=(-2*a*m)/pow(a*a+r*r,2)+(2*a*a*a*m)/(r*pow(a*a+r*r,2))-(4*a*a*a*a*a*a*a*m)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(20*a*a*a*a*a*m*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*a*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(32*a*a*a*m*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(48*a*m*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*m*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(8*a*a*a*a*a*a*a*m*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(8*a*a*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(24*a*a*a*a*a*m*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(32*a*a*a*m*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(16*a*a*a*m*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*a*a*m*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))+(4*a*a*a*a*a*m*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3))-(4*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),3));\nCimag[8][9]=(16*a*a*a*a*m*r*r*cos(th)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*om*r*r*r*cos(th)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))+(16*a*a*a*a*a*a*m*pow(cos(th),3)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(32*a*a*a*a*a*om*r*pow(cos(th),3)*pow(sin(th),4))/(pow(a*a+r*r,2)*(-1+cos(2*th))*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*m*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*m*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*m*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*m*r*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*m*r*r*r*r*pow(cos(th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*m*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*m*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*a*a*m*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))+(32*a*a*a*a*m*r*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2))-(16*a*a*a*a*m*r*r*pow(cos(th),2)*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(-1+cos(2*th),2)*pow(a*a+2*r*r+a*a*cos(2*th),2));\nCimag[9][3]=(-8*a*a*a*a*a*a*a*a*a*a*m*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(112*a*a*a*a*a*a*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(288*a*a*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(192*a*a*m*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*a*m*cos(2*th)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*m*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(192*a*a*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(224*a*a*a*a*m*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*a*m*pow(cos(2*th),2)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*m*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(80*a*a*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*a*m*pow(cos(2*th),3)*sin(th))/(r*pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(8*a*a*a*a*a*a*a*a*m*r*pow(cos(2*th),3)*sin(th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\nCimag[9][6]=-0.5*(a*a*a*a*a*a*m*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*a*om*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(3*a*a*a*a*a*a*m*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(5*a*a*a*a*m*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(3*a*a*a*a*a*om*r*r*r*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(7*a*a*a*a*m*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(20*a*a*m*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(11*a*a*a*om*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(12*a*a*m*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(16*m*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(12*a*om*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(8*m*r*r*r*r*r*r*r*r*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*cos(2*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*a*om*r*cos(2*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(7*a*a*a*a*a*a*m*r*r*cos(2*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(8*a*a*a*a*m*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*om*r*r*r*cos(2*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(12*a*a*a*a*m*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(12*a*a*m*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(4*a*a*a*om*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(12*a*a*m*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(12*a*om*r*r*r*r*r*r*r*cos(2*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(2*a*a*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(2*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(2*a*a*a*a*a*om*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(4*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(6*a*a*a*om*r*r*r*r*r*pow(cos(2*th),2)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*m*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*a*om*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*m*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*om*r*r*r*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*m*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*om*r*r*r*r*r*cos(4*th)*sin(th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*a*m*r*cos(2*th)*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*a*om*r*cos(2*th)*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))-(a*a*a*a*a*a*m*r*r*cos(2*th)*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)))+(a*a*a*a*a*om*r*r*r*cos(2*th)*cos(4*th)*sin(th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*(-1+cos(2*th)));\nCimag[9][8]=(3*a*a*a*a*a*a*a*a*m*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*m*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*a*a*a*a*om*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*a*a*m*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(17*a*a*a*a*a*a*m*r*r*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(6*a*a*a*a*a*om*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(2*a*a*a*a*m*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(9*a*a*a*a*a*om*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(19*a*a*a*a*m*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(12*a*a*a*om*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*m*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(6*a*a*a*om*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(20*a*a*m*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*m*r*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(8*m*r*r*r*r*r*r*r*r*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(7*a*a*a*a*a*a*a*a*m*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(7*a*a*a*a*a*a*m*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*om*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(31*a*a*a*a*a*a*m*r*r*cos(2*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(2*a*a*a*a*a*om*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(32*a*a*a*a*m*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(9*a*a*a*a*a*om*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(16*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(24*a*a*a*a*m*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(16*a*a*a*om*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(32*a*a*m*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(8*a*a*a*om*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(12*a*a*m*r*r*r*r*r*r*cos(2*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*a*a*a*m*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*a*a*a*a*m*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*a*a*a*a*om*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(8*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(6*a*a*a*a*a*a*m*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(8*a*a*a*a*a*om*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(12*a*a*a*a*m*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*a*a*om*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*m*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*a*om*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(3*a*a*a*a*a*a*m*r*r*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*om*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(2*a*a*a*a*m*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(3*a*a*a*a*a*om*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*m*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(4*a*a*a*om*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(2*a*a*a*om*r*r*r*r*r*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*a*a*m*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*m*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*a*a*om*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(a*a*a*a*a*a*m*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(2.*pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))+(2*a*a*a*a*a*om*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2))-(a*a*a*a*a*om*r*r*r*cos(2*th)*cos(4*th)*sin(th)*sin(2*th))/(pow(a*a+r*r,2)*pow(r*r+a*a*pow(cos(th),2),3)*pow(-1+cos(2*th),2));\nCimag[9][9]=(2*a*a*a*m)/(r*pow(a*a+r*r,2))-(2*a*m*r)/pow(a*a+r*r,2)-(8*a*a*a*a*a*a*a*a*a*m)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*m*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(16*a*a*a*a*a*m*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(96*a*a*a*m*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(64*a*m*r*r*r*r*r*r*r*r)/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*m*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(56*a*a*a*a*a*a*a*m*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))+(32*a*a*a*m*r*r*r*r*r*r*cos(2*th))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(24*a*a*a*a*a*a*a*a*a*m*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(40*a*a*a*a*a*a*a*m*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(16*a*a*a*a*a*m*r*r*r*r*pow(cos(2*th),2))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*a*a*m*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4))-(8*a*a*a*a*a*a*a*m*r*r*pow(cos(2*th),3))/(pow(a*a+r*r,2)*pow(a*a+2*r*r+a*a*cos(2*th),4));\n}\n\n}  // namespace GrSelfForce::detail\n// clang-format on\n// NOLINTEND\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbitCoeffs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n/// \\file\n/// These functions are generated by Mathematica. They compute the terms in the\n/// general relativistic self-force equations for a circular orbit in Kerr\n/// spacetime. See CircularOrbit.hpp for details.\n\nnamespace GrSelfForce::detail {\n\nvoid getAreal(int m, double a, double om, double r, double th,\n              std::array<std::array<double, 10>, 10>& Areal);\n\nvoid getAimag(int m, double a, double om, double r, double th,\n              std::array<std::array<double, 10>, 10>& Aimag);\n\nvoid getBreal(int m, double a, double om, double r, double th,\n              std::array<std::array<double, 10>, 10>& Breal);\n\nvoid getBimag(int m, double a, double om, double r, double th,\n              std::array<std::array<double, 10>, 10>& Bimag);\n\nvoid getCreal(int m, double a, double om, double r, double th,\n              std::array<std::array<double, 10>, 10>& Creal);\n\nvoid getCimag(int m, double a, double om, double r, double th,\n              std::array<std::array<double, 10>, 10>& Cimag);\n\n}  // namespace GrSelfForce::detail\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbitConvertEffsource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbitConvertEffsource.hpp\"\n\n#include <cmath>\n\n// NOLINTBEGIN\nnamespace GrSelfForce::detail {\n\nvoid convert_effsource_psi(const int m, const double a, const double r,\n                           const double th, std::array<double, 10>& real_orig,\n                           std::array<double, 10>& imag_orig,\n                           std::array<double, 10>& real_conv,\n                           std::array<double, 10>& imag_conv) {\n  const double factor = r / (2 * M_PI);\n  const double rplus = 1 + sqrt(1 - a * a);\n  const double rminus = 1 - sqrt(1 - a * a);\n  const double mdphi =\n      m * a / (rplus - rminus) * log((r - rplus) / (r - rminus));\n  const double cosmdphi = cos(mdphi);\n  const double sinmdphi = sin(mdphi);\n  const double sinth = sin(th);\n  real_conv[0] = factor * (real_orig[0] * cosmdphi + imag_orig[0] * sinmdphi);\n  imag_conv[0] = factor * (imag_orig[0] * cosmdphi - real_orig[0] * sinmdphi);\n  real_conv[1] = factor * (real_orig[1] * cosmdphi + imag_orig[1] * sinmdphi) *\n                 (r * r - 2 * r + a * a) / (r * r);\n  imag_conv[1] = factor * (imag_orig[1] * cosmdphi - real_orig[1] * sinmdphi) *\n                 (r * r - 2 * r + a * a) / (r * r);\n  real_conv[2] =\n      factor * (real_orig[2] * cosmdphi + imag_orig[2] * sinmdphi) / r;\n  imag_conv[2] =\n      factor * (imag_orig[2] * cosmdphi - real_orig[2] * sinmdphi) / r;\n  real_conv[3] = factor * (real_orig[3] * cosmdphi + imag_orig[3] * sinmdphi) /\n                 (r * sinth);\n  imag_conv[3] = factor * (imag_orig[3] * cosmdphi - real_orig[3] * sinmdphi) /\n                 (r * sinth);\n  real_conv[4] = factor * (real_orig[4] * cosmdphi + imag_orig[4] * sinmdphi) *\n                 (r * r - 2 * r + a * a) * (r * r - 2 * r + a * a) /\n                 (r * r * r * r);\n  imag_conv[4] = factor * (imag_orig[4] * cosmdphi - real_orig[4] * sinmdphi) *\n                 (r * r - 2 * r + a * a) * (r * r - 2 * r + a * a) /\n                 (r * r * r * r);\n  real_conv[5] = factor * (real_orig[5] * cosmdphi + imag_orig[5] * sinmdphi) *\n                 (r * r - 2 * r + a * a) / (r * r * r);\n  imag_conv[5] = factor * (imag_orig[5] * cosmdphi - real_orig[5] * sinmdphi) *\n                 (r * r - 2 * r + a * a) / (r * r * r);\n  real_conv[6] = factor * (real_orig[6] * cosmdphi + imag_orig[6] * sinmdphi) *\n                 (r * r - 2 * r + a * a) / (r * r * r * sinth);\n  imag_conv[6] = factor * (imag_orig[6] * cosmdphi - real_orig[6] * sinmdphi) *\n                 (r * r - 2 * r + a * a) / (r * r * r * sinth);\n  real_conv[7] =\n      factor * (real_orig[7] * cosmdphi + imag_orig[7] * sinmdphi) / (r * r);\n  imag_conv[7] =\n      factor * (imag_orig[7] * cosmdphi - real_orig[7] * sinmdphi) / (r * r);\n  real_conv[8] = factor * (real_orig[8] * cosmdphi + imag_orig[8] * sinmdphi) /\n                 (r * r * sinth);\n  imag_conv[8] = factor * (imag_orig[8] * cosmdphi - real_orig[8] * sinmdphi) /\n                 (r * r * sinth);\n  real_conv[9] = factor * (real_orig[9] * cosmdphi + imag_orig[9] * sinmdphi) /\n                 (r * r * sinth * sinth);\n  imag_conv[9] = factor * (imag_orig[9] * cosmdphi - real_orig[9] * sinmdphi) /\n                 (r * r * sinth * sinth);\n}\n\nvoid convert_effsource_Seff(const int m, const double a, const double r,\n                            const double th, std::array<double, 10>& real_orig,\n                            std::array<double, 10>& imag_orig,\n                            std::array<double, 10>& real_conv,\n                            std::array<double, 10>& imag_conv) {\n  const double factor = 1.0 / (2 * M_PI);\n  const double rplus = 1 + sqrt(1 - a * a);\n  const double rminus = 1 - sqrt(1 - a * a);\n  const double mdphi =\n      m * a / (rplus - rminus) * log((r - rplus) / (r - rminus));\n  const double cosmdphi = cos(mdphi);\n  const double sinmdphi = sin(mdphi);\n  const double sinth = sin(th);\n  const double costh = cos(th);\n  const double cos_sq_th = costh * costh;\n  const double cos_2th = cos(2 * th);\n  real_conv[0] = factor * (real_orig[0] * cosmdphi + imag_orig[0] * sinmdphi) *\n                 (-((r * (a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    ((a * a + r * r) * (a * a + r * r))));\n  imag_conv[0] = factor * (imag_orig[0] * cosmdphi - real_orig[0] * sinmdphi) *\n                 (-((r * (a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    ((a * a + r * r) * (a * a + r * r))));\n  real_conv[1] = factor * (real_orig[1] * cosmdphi + imag_orig[1] * sinmdphi) *\n                 (-0.5 *\n                  ((a * a + (-2 + r) * r) * (a * a + (-2 + r) * r) *\n                   (a * a + 2 * r * r + a * a * cos_2th)) /\n                  (r * (a * a + r * r) * (a * a + r * r)));\n  imag_conv[1] = factor * (imag_orig[1] * cosmdphi - real_orig[1] * sinmdphi) *\n                 (-0.5 *\n                  ((a * a + (-2 + r) * r) * (a * a + (-2 + r) * r) *\n                   (a * a + 2 * r * r + a * a * cos_2th)) /\n                  (r * (a * a + r * r) * (a * a + r * r)));\n  real_conv[2] = factor * (real_orig[2] * cosmdphi + imag_orig[2] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    ((a * a + r * r) * (a * a + r * r))));\n  imag_conv[2] = factor * (imag_orig[2] * cosmdphi - real_orig[2] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    ((a * a + r * r) * (a * a + r * r))));\n  real_conv[3] = factor * (real_orig[3] * cosmdphi + imag_orig[3] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    (sinth * (a * a + r * r) * (a * a + r * r))));\n  imag_conv[3] = factor * (imag_orig[3] * cosmdphi - real_orig[3] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    (sinth * (a * a + r * r) * (a * a + r * r))));\n  real_conv[4] =\n      factor * (real_orig[4] * cosmdphi + imag_orig[4] * sinmdphi) *\n      (-0.5 *\n       ((a * a + (-2 + r) * r) * (a * a + (-2 + r) * r) *\n        (a * a + (-2 + r) * r) * (a * a + 2 * r * r + a * a * cos_2th)) /\n       (r * r * r * (a * a + r * r) * (a * a + r * r)));\n  imag_conv[4] =\n      factor * (imag_orig[4] * cosmdphi - real_orig[4] * sinmdphi) *\n      (-0.5 *\n       ((a * a + (-2 + r) * r) * (a * a + (-2 + r) * r) *\n        (a * a + (-2 + r) * r) * (a * a + 2 * r * r + a * a * cos_2th)) /\n       (r * r * r * (a * a + r * r) * (a * a + r * r)));\n  real_conv[5] = factor * (real_orig[5] * cosmdphi + imag_orig[5] * sinmdphi) *\n                 (-0.5 *\n                  ((a * a + (-2 + r) * r) * (a * a + (-2 + r) * r) *\n                   (a * a + 2 * r * r + a * a * cos_2th)) /\n                  (r * r * (a * a + r * r) * (a * a + r * r)));\n  imag_conv[5] = factor * (imag_orig[5] * cosmdphi - real_orig[5] * sinmdphi) *\n                 (-0.5 *\n                  ((a * a + (-2 + r) * r) * (a * a + (-2 + r) * r) *\n                   (a * a + 2 * r * r + a * a * cos_2th)) /\n                  (r * r * (a * a + r * r) * (a * a + r * r)));\n  real_conv[6] = factor * (real_orig[6] * cosmdphi + imag_orig[6] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (a * a + (-2 + r) * r) *\n                     (r * r + a * a * cos_sq_th)) /\n                    (sinth * r * r * (a * a + r * r) * (a * a + r * r))));\n  imag_conv[6] = factor * (imag_orig[6] * cosmdphi - real_orig[6] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (a * a + (-2 + r) * r) *\n                     (r * r + a * a * cos_sq_th)) /\n                    (sinth * r * r * (a * a + r * r) * (a * a + r * r))));\n  real_conv[7] = factor * (real_orig[7] * cosmdphi + imag_orig[7] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    (r * (a * a + r * r) * (a * a + r * r))));\n  imag_conv[7] = factor * (imag_orig[7] * cosmdphi - real_orig[7] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    (r * (a * a + r * r) * (a * a + r * r))));\n  real_conv[8] = factor * (real_orig[8] * cosmdphi + imag_orig[8] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    (sinth * r * (a * a + r * r) * (a * a + r * r))));\n  imag_conv[8] = factor * (imag_orig[8] * cosmdphi - real_orig[8] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    (sinth * r * (a * a + r * r) * (a * a + r * r))));\n  real_conv[9] = factor * (real_orig[9] * cosmdphi + imag_orig[9] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    (sinth * sinth * r * (a * a + r * r) * (a * a + r * r))));\n  imag_conv[9] = factor * (imag_orig[9] * cosmdphi - real_orig[9] * sinmdphi) *\n                 (-(((a * a + (-2 + r) * r) * (r * r + a * a * cos_sq_th)) /\n                    (sinth * sinth * r * (a * a + r * r) * (a * a + r * r))));\n}\n\nvoid convert_effsource_dpsidtheta(const int m, const double a, const double r,\n                                  const double th,\n                                  std::array<double, 10>& real_orig,\n                                  std::array<double, 10>& imag_orig,\n                                  std::array<double, 10>& real_orig_dth,\n                                  std::array<double, 10>& imag_orig_dth,\n                                  std::array<double, 10>& real_conv_dth,\n                                  std::array<double, 10>& imag_conv_dth) {\n  const double factor = r / (2 * M_PI);\n  const double rplus = 1 + sqrt(1 - a * a);\n  const double rminus = 1 - sqrt(1 - a * a);\n  const double mdphi =\n      m * a / (rplus - rminus) * log((r - rplus) / (r - rminus));\n  const double cosmdphi = cos(mdphi);\n  const double sinmdphi = sin(mdphi);\n  const double sinth = sin(th);\n  const double costh = cos(th);\n  real_conv_dth[0] =\n      factor * (real_orig_dth[0] * cosmdphi + imag_orig_dth[0] * sinmdphi);\n  imag_conv_dth[0] =\n      factor * (imag_orig_dth[0] * cosmdphi - real_orig_dth[0] * sinmdphi);\n  real_conv_dth[1] =\n      factor * (real_orig_dth[1] * cosmdphi + imag_orig_dth[1] * sinmdphi) *\n      (r * r - 2 * r + a * a) / (r * r);\n  imag_conv_dth[1] =\n      factor * (imag_orig_dth[1] * cosmdphi - real_orig_dth[1] * sinmdphi) *\n      (r * r - 2 * r + a * a) / (r * r);\n  real_conv_dth[2] =\n      factor * (real_orig_dth[2] * cosmdphi + imag_orig_dth[2] * sinmdphi) / r;\n  imag_conv_dth[2] =\n      factor * (imag_orig_dth[2] * cosmdphi - real_orig_dth[2] * sinmdphi) / r;\n  real_conv_dth[3] =\n      factor * (real_orig_dth[3] * cosmdphi + imag_orig_dth[3] * sinmdphi) /\n          (r * sinth) -\n      factor * (real_orig[3] * cosmdphi + imag_orig[3] * sinmdphi) * costh /\n          (r * sinth * sinth);\n  imag_conv_dth[3] =\n      factor * (imag_orig_dth[3] * cosmdphi - real_orig_dth[3] * sinmdphi) /\n          (r * sinth) -\n      factor * (imag_orig[3] * cosmdphi - real_orig[3] * sinmdphi) * costh /\n          (r * sinth * sinth);\n  real_conv_dth[4] =\n      factor * (real_orig_dth[4] * cosmdphi + imag_orig_dth[4] * sinmdphi) *\n      (r * r - 2 * r + a * a) * (r * r - 2 * r + a * a) / (r * r * r * r);\n  imag_conv_dth[4] =\n      factor * (imag_orig_dth[4] * cosmdphi - real_orig_dth[4] * sinmdphi) *\n      (r * r - 2 * r + a * a) * (r * r - 2 * r + a * a) / (r * r * r * r);\n  real_conv_dth[5] =\n      factor * (real_orig_dth[5] * cosmdphi + imag_orig_dth[5] * sinmdphi) *\n      (r * r - 2 * r + a * a) / (r * r * r);\n  imag_conv_dth[5] =\n      factor * (imag_orig_dth[5] * cosmdphi - real_orig_dth[5] * sinmdphi) *\n      (r * r - 2 * r + a * a) / (r * r * r);\n  real_conv_dth[6] =\n      factor * (real_orig_dth[6] * cosmdphi + imag_orig_dth[6] * sinmdphi) *\n          (r * r - 2 * r + a * a) / (r * r * r * sinth) -\n      factor * (real_orig[6] * cosmdphi + imag_orig[6] * sinmdphi) *\n          (r * r - 2 * r + a * a) * costh / (r * r * r * sinth * sinth);\n  imag_conv_dth[6] =\n      factor * (imag_orig_dth[6] * cosmdphi - real_orig_dth[6] * sinmdphi) *\n          (r * r - 2 * r + a * a) / (r * r * r * sinth) -\n      factor * (imag_orig[6] * cosmdphi - real_orig[6] * sinmdphi) *\n          (r * r - 2 * r + a * a) * costh / (r * r * r * sinth * sinth);\n  real_conv_dth[7] =\n      factor * (real_orig_dth[7] * cosmdphi + imag_orig_dth[7] * sinmdphi) /\n      (r * r);\n  imag_conv_dth[7] =\n      factor * (imag_orig_dth[7] * cosmdphi - real_orig_dth[7] * sinmdphi) /\n      (r * r);\n  real_conv_dth[8] =\n      factor * (real_orig_dth[8] * cosmdphi + imag_orig_dth[8] * sinmdphi) /\n          (r * r * sinth) -\n      factor * (real_orig[8] * cosmdphi + imag_orig[8] * sinmdphi) * costh /\n          (r * r * sinth * sinth);\n  imag_conv_dth[8] =\n      factor * (imag_orig_dth[8] * cosmdphi - real_orig_dth[8] * sinmdphi) /\n          (r * r * sinth) -\n      factor * (imag_orig[8] * cosmdphi - real_orig[8] * sinmdphi) * costh /\n          (r * r * sinth * sinth);\n  real_conv_dth[9] =\n      factor * (real_orig_dth[9] * cosmdphi + imag_orig_dth[9] * sinmdphi) /\n          (r * r * sinth * sinth) -\n      factor * (real_orig[9] * cosmdphi + imag_orig[9] * sinmdphi) * 2 * costh /\n          (r * r * sinth * sinth * sinth);\n  imag_conv_dth[9] =\n      factor * (imag_orig_dth[9] * cosmdphi - real_orig_dth[9] * sinmdphi) /\n          (r * r * sinth * sinth) -\n      factor * (imag_orig[9] * cosmdphi - real_orig[9] * sinmdphi) * 2 * costh /\n          (r * r * sinth * sinth * sinth);\n}\n\nvoid convert_effsource_dpsidrstar(const int m, const double a, const double r,\n                                  const double th,\n                                  std::array<double, 10>& real_orig,\n                                  std::array<double, 10>& imag_orig,\n                                  std::array<double, 10>& real_orig_dr,\n                                  std::array<double, 10>& imag_orig_dr,\n                                  std::array<double, 10>& real_conv_drs,\n                                  std::array<double, 10>& imag_conv_drs) {\n  const double factor =\n      (r * r - 2 * r + a * a) * r / ((r * r + a * a) * 2 * M_PI);\n  const double rplus = 1 + sqrt(1 - a * a);\n  const double rminus = 1 - sqrt(1 - a * a);\n  const double mdphi =\n      m * a / (rplus - rminus) * log((r - rplus) / (r - rminus));\n  const double mdphi_dr = m * a / (r * r - 2 * r + a * a);\n  const double cosmdphi = cos(mdphi);\n  const double sinmdphi = sin(mdphi);\n  const double sinth = sin(th);\n  real_conv_drs[0] =\n      factor * (real_orig_dr[0] * cosmdphi + imag_orig_dr[0] * sinmdphi) +\n      factor * (real_orig[0] * cosmdphi + imag_orig[0] * sinmdphi) / r +\n      factor * (imag_orig[0] * cosmdphi - real_orig[0] * sinmdphi) * (mdphi_dr);\n  imag_conv_drs[0] =\n      factor * (imag_orig_dr[0] * cosmdphi - real_orig_dr[0] * sinmdphi) +\n      factor * (imag_orig[0] * cosmdphi - real_orig[0] * sinmdphi) / r +\n      factor * (-real_orig[0] * cosmdphi - imag_orig[0] * sinmdphi) *\n          (mdphi_dr);\n  real_conv_drs[1] =\n      factor * (real_orig_dr[1] * cosmdphi + imag_orig_dr[1] * sinmdphi) *\n          (r * r - 2 * r + a * a) / (r * r) +\n      factor * (real_orig[1] * cosmdphi + imag_orig[1] * sinmdphi) *\n          (r * r - a * a) / (r * r * r) +\n      factor * (imag_orig[1] * cosmdphi - real_orig[1] * sinmdphi) *\n          (mdphi_dr * (r * r - 2 * r + a * a) / (r * r));\n  imag_conv_drs[1] =\n      factor * (imag_orig_dr[1] * cosmdphi - real_orig_dr[1] * sinmdphi) *\n          (r * r - 2 * r + a * a) / (r * r) +\n      factor * (imag_orig[1] * cosmdphi - real_orig[1] * sinmdphi) *\n          (r * r - a * a) / (r * r * r) +\n      factor * (-real_orig[1] * cosmdphi - imag_orig[1] * sinmdphi) *\n          (mdphi_dr * (r * r - 2 * r + a * a) / (r * r));\n  real_conv_drs[2] =\n      factor * (real_orig_dr[2] * cosmdphi + imag_orig_dr[2] * sinmdphi) / r +\n      factor * (imag_orig[2] * cosmdphi - real_orig[2] * sinmdphi) *\n          (mdphi_dr / r);\n  imag_conv_drs[2] =\n      factor * (imag_orig_dr[2] * cosmdphi - real_orig_dr[2] * sinmdphi) / r +\n      factor * (-real_orig[2] * cosmdphi - imag_orig[2] * sinmdphi) *\n          (mdphi_dr / r);\n  real_conv_drs[3] =\n      factor * (real_orig_dr[3] * cosmdphi + imag_orig_dr[3] * sinmdphi) /\n          (r * sinth) +\n      factor * (imag_orig[3] * cosmdphi - real_orig[3] * sinmdphi) *\n          (mdphi_dr / (r * sinth));\n  imag_conv_drs[3] =\n      factor * (imag_orig_dr[3] * cosmdphi - real_orig_dr[3] * sinmdphi) /\n          (r * sinth) +\n      factor * (-real_orig[3] * cosmdphi - imag_orig[3] * sinmdphi) *\n          (mdphi_dr / (r * sinth));\n  real_conv_drs[4] =\n      factor * (real_orig_dr[4] * cosmdphi + imag_orig_dr[4] * sinmdphi) *\n          (r * r - 2 * r + a * a) * (r * r - 2 * r + a * a) / (r * r * r * r) +\n      factor * (real_orig[4] * cosmdphi + imag_orig[4] * sinmdphi) *\n          (r * r - 2 * r + a * a) * (r * (2 + r) - 3 * a * a) /\n          (r * r * r * r * r) +\n      factor * (imag_orig[4] * cosmdphi - real_orig[4] * sinmdphi) *\n          (mdphi_dr * (r * r - 2 * r + a * a) * (r * r - 2 * r + a * a) /\n           (r * r * r * r));\n  imag_conv_drs[4] =\n      factor * (imag_orig_dr[4] * cosmdphi - real_orig_dr[4] * sinmdphi) *\n          (r * r - 2 * r + a * a) * (r * r - 2 * r + a * a) / (r * r * r * r) +\n      factor * (imag_orig[4] * cosmdphi - real_orig[4] * sinmdphi) *\n          (r * r - 2 * r + a * a) * (r * (2 + r) - 3 * a * a) /\n          (r * r * r * r * r) +\n      factor * (-real_orig[4] * cosmdphi - imag_orig[4] * sinmdphi) *\n          (mdphi_dr * (r * r - 2 * r + a * a) * (r * r - 2 * r + a * a) /\n           (r * r * r * r));\n  real_conv_drs[5] =\n      factor * (real_orig_dr[5] * cosmdphi + imag_orig_dr[5] * sinmdphi) *\n          (r * r - 2 * r + a * a) / (r * r * r) +\n      factor * (real_orig[5] * cosmdphi + imag_orig[5] * sinmdphi) * 2 *\n          (r - a * a) / (r * r * r * r) +\n      factor * (imag_orig[5] * cosmdphi - real_orig[5] * sinmdphi) *\n          (mdphi_dr * (r * r - 2 * r + a * a) / (r * r * r));\n  imag_conv_drs[5] =\n      factor * (imag_orig_dr[5] * cosmdphi - real_orig_dr[5] * sinmdphi) *\n          (r * r - 2 * r + a * a) / (r * r * r) +\n      factor * (imag_orig[5] * cosmdphi - real_orig[5] * sinmdphi) * 2 *\n          (r - a * a) / (r * r * r * r) +\n      factor * (-real_orig[5] * cosmdphi - imag_orig[5] * sinmdphi) *\n          (mdphi_dr * (r * r - 2 * r + a * a) / (r * r * r));\n  real_conv_drs[6] =\n      factor * (real_orig_dr[6] * cosmdphi + imag_orig_dr[6] * sinmdphi) *\n          (r * r - 2 * r + a * a) / (r * r * r * sinth) +\n      factor * (real_orig[6] * cosmdphi + imag_orig[6] * sinmdphi) * 2 *\n          (r - a * a) / (sinth * r * r * r * r) +\n      factor * (imag_orig[6] * cosmdphi - real_orig[6] * sinmdphi) *\n          (mdphi_dr * (r * r - 2 * r + a * a) / (sinth * r * r * r));\n  imag_conv_drs[6] =\n      factor * (imag_orig_dr[6] * cosmdphi - real_orig_dr[6] * sinmdphi) *\n          (r * r - 2 * r + a * a) / (r * r * r * sinth) +\n      factor * (imag_orig[6] * cosmdphi - real_orig[6] * sinmdphi) * 2 *\n          (r - a * a) / (sinth * r * r * r * r) +\n      factor * (-real_orig[6] * cosmdphi - imag_orig[6] * sinmdphi) *\n          (mdphi_dr * (r * r - 2 * r + a * a) / (sinth * r * r * r));\n  real_conv_drs[7] =\n      factor * (real_orig_dr[7] * cosmdphi + imag_orig_dr[7] * sinmdphi) /\n          (r * r) +\n      factor * (real_orig[7] * cosmdphi + imag_orig[7] * sinmdphi) /\n          (-r * r * r) +\n      factor * (imag_orig[7] * cosmdphi - real_orig[7] * sinmdphi) *\n          (mdphi_dr / (r * r));\n  imag_conv_drs[7] =\n      factor * (imag_orig_dr[7] * cosmdphi - real_orig_dr[7] * sinmdphi) /\n          (r * r) +\n      factor * (imag_orig[7] * cosmdphi - real_orig[7] * sinmdphi) /\n          (-r * r * r) +\n      factor * (-real_orig[7] * cosmdphi - imag_orig[7] * sinmdphi) *\n          (mdphi_dr / (r * r));\n  real_conv_drs[8] =\n      factor * (real_orig_dr[8] * cosmdphi + imag_orig_dr[8] * sinmdphi) /\n          (r * r * sinth) +\n      factor * (real_orig[8] * cosmdphi + imag_orig[8] * sinmdphi) /\n          (-r * r * r * sinth) +\n      factor * (imag_orig[8] * cosmdphi - real_orig[8] * sinmdphi) *\n          (mdphi_dr / (r * r * sinth));\n  imag_conv_drs[8] =\n      factor * (imag_orig_dr[8] * cosmdphi - real_orig_dr[8] * sinmdphi) /\n          (r * r * sinth) +\n      factor * (imag_orig[8] * cosmdphi - real_orig[8] * sinmdphi) /\n          (-r * r * r * sinth) +\n      factor * (-real_orig[8] * cosmdphi - imag_orig[8] * sinmdphi) *\n          (mdphi_dr / (r * r * sinth));\n  real_conv_drs[9] =\n      factor * (real_orig_dr[9] * cosmdphi + imag_orig_dr[9] * sinmdphi) /\n          (r * r * sinth * sinth) +\n      factor * (real_orig[9] * cosmdphi + imag_orig[9] * sinmdphi) /\n          (-r * r * r * sinth * sinth) +\n      factor * (imag_orig[9] * cosmdphi - real_orig[9] * sinmdphi) *\n          (mdphi_dr / (r * r * sinth * sinth));\n  imag_conv_drs[9] =\n      factor * (imag_orig_dr[9] * cosmdphi - real_orig_dr[9] * sinmdphi) /\n          (r * r * sinth * sinth) +\n      factor * (imag_orig[9] * cosmdphi - real_orig[9] * sinmdphi) /\n          (-r * r * r * sinth * sinth) +\n      factor * (-real_orig[9] * cosmdphi - imag_orig[9] * sinmdphi) *\n          (mdphi_dr / (r * r * sinth * sinth));\n}\n\n}  // namespace GrSelfForce::detail\n// NOLINTEND\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbitConvertEffsource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n/// \\file\n/// These functions are generated by Mathematica. They transform results from\n/// the GrEffectiveSource code into our form of the equations. See\n/// CircularOrbit.hpp for details.\n\nnamespace GrSelfForce::detail {\n\nvoid convert_effsource_psi(int m, double a, double r, double th,\n                           std::array<double, 10>& real_orig,\n                           std::array<double, 10>& imag_orig,\n                           std::array<double, 10>& real_conv,\n                           std::array<double, 10>& imag_conv);\n\nvoid convert_effsource_Seff(int m, double a, double r, double th,\n                            std::array<double, 10>& real_orig,\n                            std::array<double, 10>& imag_orig,\n                            std::array<double, 10>& real_conv,\n                            std::array<double, 10>& imag_conv);\n\nvoid convert_effsource_dpsidtheta(int m, double a, double r, double th,\n                                  std::array<double, 10>& real_orig,\n                                  std::array<double, 10>& imag_orig,\n                                  std::array<double, 10>& real_orig_dth,\n                                  std::array<double, 10>& imag_orig_dth,\n                                  std::array<double, 10>& real_conv_dth,\n                                  std::array<double, 10>& imag_conv_dth);\n\nvoid convert_effsource_dpsidrstar(int m, double a, double r, double th,\n                                  std::array<double, 10>& real_orig,\n                                  std::array<double, 10>& imag_orig,\n                                  std::array<double, 10>& real_orig_dr,\n                                  std::array<double, 10>& imag_orig_dr,\n                                  std::array<double, 10>& real_conv_drs,\n                                  std::array<double, 10>& imag_conv_drs);\n\n}  // namespace GrSelfForce::detail\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Angular.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Angular.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\nAngular::Angular(const int m_mode_number) : m_mode_number_(m_mode_number) {}\n\nAngular::Angular(CkMigrateMessage* m) : Base(m) {}\n\nvoid Angular::apply(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field,\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> n_dot_field_gradient,\n    const GradTensorType& /*deriv_field*/) const {\n  // Ordering of numbered variables in comments below:\n  // tt, tr, ttheta, tphi, rr, rtheta, rphi, theta theta, theta phi, phi phi\n  if (m_mode_number_ == 0) {\n    ERROR(\"Not yet implemented for m=0.\");\n  } else if (m_mode_number_ == 1) {\n    // Dirichlet for components 0, 1, 4, 7, 8, 9\n    // Neumann for components 2, 3, 5, 6\n    get<0, 0>(*field) = 0.;\n    get<0, 1>(*field) = 0.;\n    get<0, 2>(*n_dot_field_gradient) = 0.;\n    get<0, 3>(*n_dot_field_gradient) = 0.;\n    get<1, 1>(*field) = 0.;\n    get<1, 2>(*n_dot_field_gradient) = 0.;\n    get<1, 3>(*n_dot_field_gradient) = 0.;\n    get<2, 2>(*field) = 0.;\n    get<2, 3>(*field) = 0.;\n    get<3, 3>(*field) = 0.;\n  } else if (m_mode_number_ == 2) {\n    // Dirichlet for components 0-6\n    // Neumann for components 7-9\n    get<0, 0>(*field) = 0.;\n    get<0, 1>(*field) = 0.;\n    get<0, 2>(*field) = 0.;\n    get<0, 3>(*field) = 0.;\n    get<1, 1>(*field) = 0.;\n    get<1, 2>(*field) = 0.;\n    get<1, 3>(*field) = 0.;\n    get<2, 2>(*n_dot_field_gradient) = 0.;\n    get<2, 3>(*n_dot_field_gradient) = 0.;\n    get<3, 3>(*n_dot_field_gradient) = 0.;\n  } else {\n    // All Dirichlet\n    for (size_t i = 0; i < field->size(); ++i) {\n      (*field)[i] = 0.;\n    }\n  }\n}\n\nvoid Angular::apply_linearized(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field_correction,\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*>\n        n_dot_field_correction_gradient,\n    const GradTensorType& deriv_field_correction) const {\n  apply(field_correction, n_dot_field_correction_gradient,\n        deriv_field_correction);\n}\n\nvoid Angular::pup(PUP::er& p) {\n  Base::pup(p);\n  p | m_mode_number_;\n}\n\nbool operator==(const Angular& lhs, const Angular& rhs) {\n  return lhs.m_mode_number_ == rhs.m_mode_number_;\n}\n\nbool operator!=(const Angular& lhs, const Angular& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID Angular::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Angular.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\n/*!\n * \\brief Angular boundary conditions for the m-mode metric perturbations.\n *\n * The angular boundary conditions are determined by enforcing regularity of\n * the metric perturbation at the poles $\\theta=0,\\pi$:\n * \\begin{align}\n *   &\\Psi_{m=1}^{0,1,4,7,8,9} = 0 \\\\\n *   &\\partial_\\theta \\Psi_{m=1}^{2,3,5,6} = 0 \\\\\n *   &\\Psi_{m=2}^{0,1,2,3,4,5,6} = 0 \\\\\n *   &\\partial_\\theta \\Psi_{m=2}^{7,8,9} = 0 \\\\\n *   &\\Psi_{m>2} = 0 \\\\\n * \\end{align}\n * The ordering of components in $\\Psi$ here is:\n * $\\{tt,tr,t\\theta,t\\phi,rr,r\\theta,r\\phi,\\theta\\theta,\\theta\\phi,\\phi\\phi\\}$.\n */\nclass Angular : public elliptic::BoundaryConditions::BoundaryCondition<2> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<2>;\n  using GradTensorType =\n      TensorMetafunctions::prepend_spatial_index<tnsr::aa<ComplexDataVector, 3>,\n                                                 2, UpLo::Lo, Frame::Inertial>;\n\n public:\n  struct MModeNumber {\n    static constexpr Options::String help =\n        \"Mode number 'm' of the metric perturbation\";\n    using type = int;\n  };\n\n  static constexpr Options::String help = \"Angular boundary condition\";\n  using options = tmpl::list<MModeNumber>;\n\n  Angular() = default;\n  Angular(const Angular&) = default;\n  Angular& operator=(const Angular&) = default;\n  Angular(Angular&&) = default;\n  Angular& operator=(Angular&&) = default;\n  ~Angular() override = default;\n\n  explicit Angular(int m_mode_number);\n\n  int m_mode_number() const { return m_mode_number_; }\n\n  /// \\cond\n  explicit Angular(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Angular);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<Angular>(*this);\n  }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    // For m > 0 some tensor components are Neumann-type and others Dirichlet.\n    // The boundary condition type is currently only used for the\n    // `MinusLaplacian` subdomain preconditioner, so its effectiveness will be\n    // lower unless we extend this function to return a type for each tensor\n    // component.\n    return {m_mode_number_ == 0 ? elliptic::BoundaryConditionType::Neumann\n                                : elliptic::BoundaryConditionType::Dirichlet};\n  }\n\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field,\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> n_dot_field_gradient,\n      const GradTensorType& deriv_field) const;\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field_correction,\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*>\n          n_dot_field_correction_gradient,\n      const GradTensorType& deriv_field_correction) const;\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const Angular& lhs, const Angular& rhs);\n\n  int m_mode_number_{};\n};\n\nbool operator!=(const Angular& lhs, const Angular& rhs);\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GrSelfForceBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Angular.cpp\n  None.cpp\n  Sommerfeld.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Angular.hpp\n  None.hpp\n  Sommerfeld.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Elliptic\n  Options\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Angular.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/None.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\n/// The list of standard boundary conditions for the GR self-force system.\nusing standard_boundary_conditions = tmpl::list<Angular, Sommerfeld, None>;\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/None.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/None.hpp\"\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\nNone::None(CkMigrateMessage* m) : Base(m) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition> None::get_clone()\n    const {\n  return std::make_unique<None>(*this);\n}\n\nvoid None::apply(const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> /*field*/,\n                 const gsl::not_null<\n                     tnsr::aa<ComplexDataVector, 3>*> /*n_dot_field_gradient*/,\n                 const GradTensorType& /*deriv_field*/) const {\n  // Nothing to do\n}\n\nvoid None::apply_linearized(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> /*field_correction*/,\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*>\n    /*n_dot_field_gradient_correction*/,\n    const GradTensorType& /*deriv_field_correction*/) const {\n  // Nothing to do\n}\n\nbool operator==(const None& /*lhs*/, const None& /*rhs*/) { return true; }\n\nbool operator!=(const None& lhs, const None& rhs) { return not(lhs == rhs); }\n\nPUP::able::PUP_ID None::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/None.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\n/*!\n * \\brief Applies no boundary condition at all. Used to impose nothing but\n * regularity at the horizon in horizon-penetrating coordinates.\n */\nclass None : public elliptic::BoundaryConditions::BoundaryCondition<2> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<2>;\n  using GradTensorType =\n      TensorMetafunctions::prepend_spatial_index<tnsr::aa<ComplexDataVector, 3>,\n                                                 2, UpLo::Lo, Frame::Inertial>;\n\n public:\n  static constexpr Options::String help =\n      \"Applies no boundary condition at all. Used to impose nothing but \"\n      \"regularity at the horizon in horizon-penetrating coordinates.\";\n  using options = tmpl::list<>;\n\n  None() = default;\n  None(const None&) = default;\n  None& operator=(const None&) = default;\n  None(None&&) = default;\n  None& operator=(None&&) = default;\n  ~None() override = default;\n\n  /// \\cond\n  explicit None(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(None);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override;\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {elliptic::BoundaryConditionType::Neumann};\n  }\n\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field,\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> n_dot_field_gradient,\n      const GradTensorType& deriv_field) const;\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field_correction,\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*>\n          n_dot_field_gradient_correction,\n      const GradTensorType& deriv_field_correction) const;\n\n private:\n  friend bool operator==(const None& lhs, const None& rhs);\n};\n\nbool operator!=(const None& lhs, const None& rhs);\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Sommerfeld.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Sommerfeld.hpp\"\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\nSommerfeld::Sommerfeld(const double black_hole_mass,\n                       const double black_hole_spin,\n                       const double orbital_radius, const int m_mode_number)\n    : black_hole_mass_(black_hole_mass),\n      black_hole_spin_(black_hole_spin),\n      orbital_radius_(orbital_radius),\n      m_mode_number_(m_mode_number) {}\n\nSommerfeld::Sommerfeld(CkMigrateMessage* m) : Base(m) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nSommerfeld::get_clone() const {\n  return std::make_unique<Sommerfeld>(*this);\n}\n\nvoid Sommerfeld::apply(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field,\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> n_dot_field_gradient,\n    const GradTensorType& /*deriv_field*/) const {\n  const double a = black_hole_spin_ * black_hole_mass_;\n  const double M = black_hole_mass_;\n  const double r_0 = orbital_radius_;\n  const double omega = 1. / (a + sqrt(cube(r_0) / M));\n  for (size_t i = 0; i < field->size(); ++i) {\n    (*n_dot_field_gradient)[i] =\n        std::complex<double>(0.0, m_mode_number_ * omega) * (*field)[i];\n  }\n}\n\nvoid Sommerfeld::apply_linearized(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field_correction,\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*>\n        n_dot_field_correction_gradient,\n    const GradTensorType& deriv_field_correction) const {\n  apply(field_correction, n_dot_field_correction_gradient,\n        deriv_field_correction);\n}\n\nvoid Sommerfeld::pup(PUP::er& p) {\n  p | black_hole_mass_;\n  p | black_hole_spin_;\n  p | orbital_radius_;\n  p | m_mode_number_;\n}\n\nbool operator==(const Sommerfeld& lhs, const Sommerfeld& rhs) {\n  return lhs.black_hole_mass_ == rhs.black_hole_mass_ and\n         lhs.black_hole_spin_ == rhs.black_hole_spin_ and\n         lhs.orbital_radius_ == rhs.orbital_radius_ and\n         lhs.m_mode_number_ == rhs.m_mode_number_;\n}\n\nbool operator!=(const Sommerfeld& lhs, const Sommerfeld& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID Sommerfeld::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Sommerfeld.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\n/*!\n * \\brief Radial Sommerfeld boundary conditions for the m-mode metric\n * perturbations.\n *\n * These radial boundary conditions enforce an outgoing direction of radiation\n * propagation. They apply both near the Kerr horizon (inner radial\n * boundary) and at large distance (outer radial boundary):\n *\n * \\begin{equation}\n * n_i F^i = i m \\Omega \\Psi_m\n * \\end{equation}\n *\n * These boundary conditions currently assume a circular equatorial orbit.\n */\nclass Sommerfeld : public elliptic::BoundaryConditions::BoundaryCondition<2> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<2>;\n  using GradTensorType =\n      TensorMetafunctions::prepend_spatial_index<tnsr::aa<ComplexDataVector, 3>,\n                                                 2, UpLo::Lo, Frame::Inertial>;\n\n public:\n  struct BlackHoleMass {\n    static constexpr Options::String help =\n        \"Kerr mass parameter 'M' of the black hole\";\n    using type = double;\n  };\n  struct BlackHoleSpin {\n    static constexpr Options::String help =\n        \"Kerr dimensionless spin parameter 'chi' of the black hole\";\n    using type = double;\n  };\n  struct OrbitalRadius {\n    static constexpr Options::String help =\n        \"Radius 'r_0' of the circular orbit\";\n    using type = double;\n  };\n  struct MModeNumber {\n    static constexpr Options::String help =\n        \"Mode number 'm' of the scalar field\";\n    using type = int;\n  };\n\n  static constexpr Options::String help =\n      \"Radial Sommerfeld boundary condition\";\n  using options =\n      tmpl::list<BlackHoleMass, BlackHoleSpin, OrbitalRadius, MModeNumber>;\n\n  Sommerfeld() = default;\n  Sommerfeld(const Sommerfeld&) = default;\n  Sommerfeld& operator=(const Sommerfeld&) = default;\n  Sommerfeld(Sommerfeld&&) = default;\n  Sommerfeld& operator=(Sommerfeld&&) = default;\n  ~Sommerfeld() override = default;\n\n  explicit Sommerfeld(double black_hole_mass, double black_hole_spin,\n                      double orbital_radius, int m_mode_number);\n\n  double black_hole_mass() const { return black_hole_mass_; }\n  double black_hole_spin() const { return black_hole_spin_; }\n  double orbital_radius() const { return orbital_radius_; }\n  int m_mode_number() const { return m_mode_number_; }\n\n  /// \\cond\n  explicit Sommerfeld(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Sommerfeld);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override;\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {elliptic::BoundaryConditionType::Neumann};\n  }\n\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field,\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> n_dot_field_gradient,\n      const GradTensorType& deriv_field) const;\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field_correction,\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*>\n          n_dot_field_correction_gradient,\n      const GradTensorType& deriv_field_correction) const;\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const Sommerfeld& lhs, const Sommerfeld& rhs);\n\n  double black_hole_mass_{std::numeric_limits<double>::signaling_NaN()};\n  double black_hole_spin_{std::numeric_limits<double>::signaling_NaN()};\n  double orbital_radius_{std::numeric_limits<double>::signaling_NaN()};\n  int m_mode_number_{};\n};\n\nbool operator!=(const Sommerfeld& lhs, const Sommerfeld& rhs);\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GrSelfForce)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Equations.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Equations.hpp\n  FirstOrderSystem.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DomainStructure\n  ErrorHandling\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(AnalyticData)\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Equations.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\nnamespace GrSelfForce {\n\nvoid fluxes(const gsl::not_null<FluxTensorType*> flux,\n            const Scalar<ComplexDataVector>& alpha,\n            const GradTensorType& field_gradient) {\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b <= a; ++b) {\n      flux->get(0, a, b) = field_gradient.get(0, a, b);\n      flux->get(1, a, b) = get(alpha) * field_gradient.get(1, a, b);\n    }\n  }\n}\n\nvoid fluxes_on_face(const gsl::not_null<FluxTensorType*> flux,\n                    const Scalar<ComplexDataVector>& alpha,\n                    const tnsr::I<DataVector, 2>& face_normal_vector,\n                    const tnsr::aa<ComplexDataVector, 3>& field) {\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b <= a; ++b) {\n      flux->get(0, a, b) = get<0>(face_normal_vector) * field.get(a, b);\n      flux->get(1, a, b) =\n          get(alpha) * get<1>(face_normal_vector) * field.get(a, b);\n    }\n  }\n}\n\nvoid add_sources(const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> source,\n                 const tnsr::aaBB<ComplexDataVector, 3>& beta,\n                 const tnsr::aaBB<ComplexDataVector, 3>& gamma_rstar,\n                 const tnsr::aaBB<ComplexDataVector, 3>& gamma_theta,\n                 const tnsr::aa<ComplexDataVector, 3>& field,\n                 const FluxTensorType& flux) {\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b <= a; ++b) {\n      for (size_t c = 0; c < 4; ++c) {\n        for (size_t d = 0; d <= c; ++d) {\n          source->get(a, b) += beta.get(a, b, c, d) * field.get(c, d) +\n                               gamma_rstar.get(a, b, c, d) * flux.get(0, c, d) +\n                               gamma_theta.get(a, b, c, d) * flux.get(1, c, d);\n        }\n      }\n    }\n  }\n}\n\nvoid Fluxes::apply(const gsl::not_null<FluxTensorType*> flux,\n                   const Scalar<ComplexDataVector>& alpha,\n                   const tnsr::aa<ComplexDataVector, 3>& /*field*/,\n                   const GradTensorType& field_gradient) {\n  fluxes(flux, alpha, field_gradient);\n}\n\nvoid Fluxes::apply(const gsl::not_null<FluxTensorType*> flux,\n                   const Scalar<ComplexDataVector>& alpha,\n                   const tnsr::i<DataVector, 2>& /*face_normal*/,\n                   const tnsr::I<DataVector, 2>& face_normal_vector,\n                   const tnsr::aa<ComplexDataVector, 3>& field) {\n  fluxes_on_face(flux, alpha, face_normal_vector, field);\n}\n\nvoid Sources::apply(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> scalar_equation,\n    const tnsr::aaBB<ComplexDataVector, 3>& beta,\n    const tnsr::aaBB<ComplexDataVector, 3>& gamma_rstar,\n    const tnsr::aaBB<ComplexDataVector, 3>& gamma_theta,\n    const tnsr::aa<ComplexDataVector, 3>& field,\n    const GradTensorType& /*field_gradient*/, const FluxTensorType& flux) {\n  add_sources(scalar_equation, beta, gamma_rstar, gamma_theta, field, flux);\n}\n\nvoid ModifyBoundaryData::apply(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field,\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> n_dot_flux,\n    const DirectionalId<Dim>& mortar_id, const bool field_is_regularized,\n    const DirectionalIdMap<Dim, bool>& neighbors_field_is_regularized,\n    const DirectionalIdMap<Dim, typename singular_vars_on_mortars_tag::type>&\n        singular_vars_on_mortars) {\n  if (field_is_regularized == neighbors_field_is_regularized.at(mortar_id)) {\n    // Both elements solve for the same field. Nothing to do.\n    return;\n  }\n  // Subtract the singular field on the regularized side, and add it on the\n  // other side\n  const double sign = field_is_regularized ? -1. : 1.;\n  const auto& singular_field =\n      get<Tags::SingularField>(singular_vars_on_mortars.at(mortar_id));\n  const auto& singular_field_n_dot_flux =\n      get<::Tags::NormalDotFlux<Tags::SingularField>>(\n          singular_vars_on_mortars.at(mortar_id));\n  for (size_t i = 0; i < singular_field.size(); ++i) {\n    (*field)[i] += sign * singular_field[i];\n    (*n_dot_flux)[i] -= sign * singular_field_n_dot_flux[i];\n  }\n}\n\n}  // namespace GrSelfForce\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce {\n\n/// @{\n/// We're working with 4D tensors to represent the 10 independent components\n/// we're solving for, but we only take 2D spatial derivatives, so we define\n/// these mixed-dimension tensors for gradients and fluxes.\nusing GradTensorType =\n    TensorMetafunctions::prepend_spatial_index<tnsr::aa<ComplexDataVector, 3>,\n                                               2, UpLo::Lo, Frame::Inertial>;\nusing FluxTensorType =\n    TensorMetafunctions::prepend_spatial_index<tnsr::aa<ComplexDataVector, 3>,\n                                               2, UpLo::Up, Frame::Inertial>;\n/// @}\n\n/*!\n * \\brief The first-order flux $F^i=\\{\\partial_{r_\\star}, \\alpha\n * \\partial_\\theta\\}\\Psi_m$.\n */\nvoid fluxes(gsl::not_null<FluxTensorType*> flux,\n            const Scalar<ComplexDataVector>& alpha,\n            const GradTensorType& field_gradient);\n\n/*!\n * \\brief The first-order flux on an element face\n * $F^i=\\{n_{r_\\star}, \\alpha n_\\theta\\}\\Psi_m$.\n */\nvoid fluxes_on_face(gsl::not_null<FluxTensorType*> flux,\n                    const Scalar<ComplexDataVector>& alpha,\n                    const tnsr::I<DataVector, 2>& face_normal_vector,\n                    const tnsr::aa<ComplexDataVector, 3>& field);\n\n/*!\n * \\brief The source term $\\beta_{ab}^{cd} (\\Psi_m)_{cd} + \\gamma_{iab}^{cd}\n * F^i_{cd}$.\n */\nvoid add_sources(gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> source,\n                 const tnsr::aaBB<ComplexDataVector, 3>& beta,\n                 const tnsr::aaBB<ComplexDataVector, 3>& gamma_rstar,\n                 const tnsr::aaBB<ComplexDataVector, 3>& gamma_theta,\n                 const tnsr::aa<ComplexDataVector, 3>& field,\n                 const FluxTensorType& flux);\n\n/// Fluxes $F^i$ for the gravitational self-force system.\n/// \\see GrSelfForce::FirstOrderSystem\nstruct Fluxes {\n  using argument_tags = tmpl::list<Tags::Alpha>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = false;\n  static constexpr bool is_discontinuous = false;\n  static void apply(gsl::not_null<FluxTensorType*> flux,\n                    const Scalar<ComplexDataVector>& alpha,\n                    const tnsr::aa<ComplexDataVector, 3>& /*field*/,\n                    const GradTensorType& field_gradient);\n  static void apply(gsl::not_null<FluxTensorType*> flux,\n                    const Scalar<ComplexDataVector>& alpha,\n                    const tnsr::i<DataVector, 2>& /*face_normal*/,\n                    const tnsr::I<DataVector, 2>& face_normal_vector,\n                    const tnsr::aa<ComplexDataVector, 3>& field);\n};\n\n/// Source terms for the gravitational self-force system.\n/// \\see GrSelfForce::FirstOrderSystem\nstruct Sources {\n  using argument_tags =\n      tmpl::list<Tags::Beta, Tags::GammaRstar, Tags::GammaTheta>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> scalar_equation,\n      const tnsr::aaBB<ComplexDataVector, 3>& beta,\n      const tnsr::aaBB<ComplexDataVector, 3>& gamma_rstar,\n      const tnsr::aaBB<ComplexDataVector, 3>& gamma_theta,\n      const tnsr::aa<ComplexDataVector, 3>& field,\n      const GradTensorType& /*field_gradient*/, const FluxTensorType& flux);\n};\n\n/*!\n * \\brief Adds or subtracts the singular field to/from the received data on\n * element boundaries.\n *\n * In the regularized region we solve for the regularized field\n * \\begin{equation}\n *   \\Psi_m^R = \\Psi_m - \\Psi_m^P\n *   \\text{,}\n * \\end{equation}\n * so we subtract the singular field on the regularized side (where\n * `field_is_regularized` is true) and add it on the other side of the boundary\n * (where `field_is_regularized` is false). We do the same for the received\n * normal dot flux $n_i F^i$, but with an extra minus sign because this quantity\n * is defined with the face normal from the perspective of the sending element\n * (see `elliptic::protocols::FirstOrderSystem`).\n */\nstruct ModifyBoundaryData {\n private:\n  static constexpr size_t Dim = 2;\n  using singular_vars_on_mortars_tag =\n      ::Tags::Variables<tmpl::list<Tags::SingularField,\n                                   ::Tags::NormalDotFlux<Tags::SingularField>>>;\n\n public:\n  using argument_tags =\n      tmpl::list<Tags::FieldIsRegularized,\n                 ::Tags::Mortars<Tags::FieldIsRegularized, Dim>,\n                 ::Tags::Mortars<singular_vars_on_mortars_tag, Dim>>;\n  static void apply(\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> field,\n      gsl::not_null<tnsr::aa<ComplexDataVector, 3>*> n_dot_flux,\n      const DirectionalId<Dim>& mortar_id, bool field_is_regularized,\n      const DirectionalIdMap<Dim, bool>& neighbors_field_is_regularized,\n      const DirectionalIdMap<Dim, typename singular_vars_on_mortars_tag::type>&\n          singular_vars_on_mortars);\n};\n\n}  // namespace GrSelfForce\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Equations.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce {\n\n/*!\n * \\brief Gravitational self-force of a small gravitating body in a Kerr\n * background.\n *\n * Extension of the `ScalarSelfForce::FirstOrderSystem` to the gravitational\n * case. We solve a 2D elliptic equation for the 10 independent components of\n * the symmetric m-mode field $(\\Psi_m)_{ab}$. The two dimensions are a\n * radial and angular coordinate, specifically the tortoise radius $r_\\star$ and\n * polar angle $\\theta$. We parametrize the 2D elliptic equations as:\n * \\begin{equation}\n * -\\Delta_m (\\Psi_m)_{ab} = -\\partial_i F^i_{ab} + \\beta_{ab}^{cd}\n *   (\\Psi_m)_{cd} + \\gamma_{iab}^{cd} F^i_{cd} = 0\n * \\end{equation}\n * with the flux\n * \\begin{equation}\n * F^i_{ab} = \\{\\partial_{r_\\star}, \\alpha \\partial_\\theta\\} (\\Psi_m)_{ab}\n * \\text{,}\n * \\end{equation}\n * where $\\alpha$, $\\beta_{ab}^{cd}$, and $\\gamma_{iab}^{cd}$ are coefficients\n * that define the elliptic equations. The particular coefficients for a\n * circular equatorial orbit in Kerr are implemented in\n * `GrSelfForce::AnalyticData::CircularOrbit`.\n *\n * See `ScalarSelfForce::FirstOrderSystem` for a description of the\n * regularization and modified boundary data.\n *\n * Once the 2D elliptic equations are solved for a given m-mode, the\n * contribution to the self-force can be extracted from the gradient of\n * $(\\Psi_m)_{ab}$ at the location of the small body. These self-force\n * contributions are then summed over all m-modes up to some cutoff. The\n * resulting self-force can be used to drive a quasi-adiabatic inspiral to\n * generate waveforms. For details and more references see \\cite Osburn:2022bby\n * for now (more references will be added when they are published).\n */\nstruct FirstOrderSystem\n    : tt::ConformsTo<elliptic::protocols::FirstOrderSystem> {\n  static constexpr size_t volume_dim = 2;\n\n  using primal_fields = tmpl::list<Tags::MMode>;\n  using primal_fluxes =\n      tmpl::list<::Tags::Flux<Tags::MMode, tmpl::size_t<2>, Frame::Inertial>>;\n\n  using background_fields =\n      tmpl::list<Tags::Alpha, Tags::Beta, Tags::GammaRstar, Tags::GammaTheta>;\n  using inv_metric_tag = void;\n\n  using fluxes_computer = Fluxes;\n  using sources_computer = Sources;\n  using modify_boundary_data = ModifyBoundaryData;\n\n  using boundary_conditions_base =\n      elliptic::BoundaryConditions::BoundaryCondition<2>;\n};\n\n}  // namespace GrSelfForce\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/GeneralRelativity/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\nclass DataVector;\n/// \\endcond\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving the gravitational self-force of a gravitating\n * body in a Kerr background.\n *\n * \\see GrSelfForce::FirstOrderSystem\n */\nnamespace GrSelfForce {}\n\n/// Tags for the GrSelfForce system.\nnamespace GrSelfForce::Tags {\n\n/*!\n * \\brief The complex m-mode field $(\\Psi_m)_{ab}$.\n *\n * Defined by the m-mode decomposition of the metric perturbation $h_{ab}$:\n * \\begin{equation}\n * \\bar{h}_{ab}(t,r,\\theta,\\phi) = \\sum_{m=-\\infty}^{\\infty}\n *   \\bar{h}^m_{ab}(r,\\theta) e^{im(\\phi - \\Omega t)}\n * \\end{equation}\n * and further decomposition with convenient prefactors:\n * \\begin{align*}\n * &(\\Psi_m)_{tt} = r \\bar{h}^m_{tt} \\\\\n * &(\\Psi_m)_{tr} = \\frac{\\Delta}{r} \\bar{h}^m_{tr} \\\\\n * &(\\Psi_m)_{t\\theta} = \\bar{h}^m_{t\\theta} \\\\\n * &(\\Psi_m)_{t\\phi} = \\frac{1}{\\sin\\theta} \\bar{h}^m_{t\\phi} \\\\\n * &(\\Psi_m)_{rr} = \\frac{\\Delta^2}{r^3} \\bar{h}^m_{rr} \\\\\n * &(\\Psi_m)_{r\\theta} = \\frac{\\Delta}{r^2} \\bar{h}^m_{r\\theta} \\\\\n * &(\\Psi_m)_{r\\phi} = \\frac{\\Delta}{r^2\\sin\\theta} \\bar{h}^m_{r\\phi} \\\\\n * &(\\Psi_m)_{\\theta\\theta} = \\frac{1}{r} \\bar{h}^m_{\\theta\\theta} \\\\\n * &(\\Psi_m)_{\\theta\\phi} = \\frac{1}{r\\sin\\theta} \\bar{h}^m_{\\theta\\phi} \\\\\n * &(\\Psi_m)_{\\phi\\phi} = \\frac{1}{r\\sin^2\\theta} \\bar{h}^m_{\\phi\\phi} \\\\\n * \\end{align*}\n */\nstruct MMode : db::SimpleTag {\n  using type = tnsr::aa<ComplexDataVector, 3>;\n};\n\n/*!\n * \\brief The factor multiplying the angular derivative in the principal part of\n * the equations.\n *\n * This is the factor $\\alpha$ that defines the principal part of the equations\n * and allows to write it in first-order flux form (see\n * `GrSelfForce::FirstOrderSystem`). This factor is set by the analytic data\n * class (see `ScalarSelfForce::AnalyticData::CircularOrbit`).\n */\nstruct Alpha : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n};\n\n/*!\n * \\brief The factors $\\beta_{ab}^{cd}$ multiplying the non-derivative terms in\n * the equations.\n */\nstruct Beta : db::SimpleTag {\n  using type = tnsr::aaBB<ComplexDataVector, 3>;\n};\n\n/*!\n * \\brief The factors $\\gamma_{{r_\\star}ab}^{cd}$ multiplying the\n * $r_\\star$-derivative terms in the equations.\n */\nstruct GammaRstar : db::SimpleTag {\n  using type = tnsr::aaBB<ComplexDataVector, 3>;\n};\n\n/*!\n * \\brief The factors $\\gamma_{\\theta ab}^{cd}$ multiplying the\n * $\\theta$-derivative terms in the equations.\n */\nstruct GammaTheta : db::SimpleTag {\n  using type = tnsr::aaBB<ComplexDataVector, 3>;\n};\n\n/*!\n * \\brief A flag indicating that we are solving for the regularized field in\n * this element.\n *\n * In elements at and around the point mass we use the effective source\n * approach to split the m-mode field into a singular and a regular field\n * [Eq. (3.6) in \\cite Osburn:2022bby ]:\n * \\begin{equation}\n * \\Psi_m = \\Psi_m^\\mathcal{P} + \\Psi_m^\\mathcal{R}\n * \\text{.}\n * \\end{equation}\n * In these elements, we solve for $\\Psi_m^\\mathcal{R}$ rather than $\\Psi_m$.\n */\nstruct FieldIsRegularized : db::SimpleTag {\n  using type = bool;\n};\n\n/*!\n * \\brief The singular field $\\Psi_m^\\mathcal{P}$.\n *\n * Only defined where `FieldIsRegularized` is true.\n */\nstruct SingularField : db::SimpleTag {\n  using type = tnsr::aa<ComplexDataVector, 3>;\n};\n\n/*!\n * \\brief The Boyer-Lindquist radius $r$.\n *\n * Computed numerically from the tortoise radial coordinate $r_\\star$\n * [see Eq. (2.12) in \\cite Osburn:2022bby ].\n */\nstruct BoyerLindquistRadius : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n}  // namespace GrSelfForce::Tags\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarSelfForceActions)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InitializeEffectiveSource.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  Elliptic\n  Parallel\n  ParallelAmr\n  ScalarSelfForce\n  ScalarSelfForceAnalyticData\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/Actions/InitializeEffectiveSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Equations.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace ScalarSelfForce::Actions {\n\n/*!\n * \\brief Initialize the effective source and singular field for the scalar\n * self-force problem in a regularized region around the puncture. Replaces\n * 'elliptic::Actions::InitializeFixedSources' in the standard elliptic\n * initialization.\n *\n * \\details The regularized region is chosen as the full block containing the\n * puncture. In this region, the `Tags::FieldIsRegularized` is set to true and\n * the effective source and the singular field are set by the\n * ScalarSelfForce::AnalyticData::CircularOrbit class. Outside this region, the\n * fixed source and singular field are set to zero.\n */\ntemplate <typename System, typename BackgroundTag>\nstruct InitializeEffectiveSource : tt::ConformsTo<::amr::protocols::Projector> {\n private:\n  static constexpr size_t Dim = System::volume_dim;\n  using fixed_sources_tag = ::Tags::Variables<\n      db::wrap_tags_in<::Tags::FixedSource, typename System::primal_fields>>;\n  using singular_vars_tag = ::Tags::Variables<tmpl::list<\n      Tags::SingularField,\n      ::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>, Frame::Inertial>>>;\n  using singular_vars_on_mortars_tag =\n      ::Tags::Variables<tmpl::list<Tags::SingularField,\n                                   ::Tags::NormalDotFlux<Tags::SingularField>>>;\n  using analytic_tags_list = tmpl::push_back<\n      typename fixed_sources_tag::tags_list, Tags::SingularField,\n      ::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>, Frame::Inertial>,\n      Tags::BoyerLindquistRadius>;\n\n public:  // Iterable action\n  using const_global_cache_tags =\n      tmpl::list<elliptic::dg::Tags::Massive, BackgroundTag>;\n  using simple_tags =\n      tmpl::list<fixed_sources_tag, singular_vars_tag,\n                 ::Tags::Mortars<singular_vars_on_mortars_tag, Dim>,\n                 Tags::BoyerLindquistRadius, Tags::FieldIsRegularized,\n                 ::Tags::Mortars<Tags::FieldIsRegularized, Dim>>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeEffectiveSource>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // DataBox mutator, amr::protocols::Projector\n  using return_tags = simple_tags;\n  using argument_tags = tmpl::list<\n      domain::Tags::Coordinates<Dim, Frame::Inertial>,\n      domain::Tags::Domain<Dim>, domain::Tags::Element<Dim>,\n      ::Tags::Mortars<domain::Tags::Coordinates<Dim, Frame::Inertial>, Dim>,\n      BackgroundTag, elliptic::dg::Tags::Massive, domain::Tags::Mesh<Dim>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      Parallel::Tags::Metavariables>;\n\n  template <typename Background, typename Metavariables, typename... AmrData>\n  static void apply(\n      const gsl::not_null<typename fixed_sources_tag::type*> fixed_sources,\n      const gsl::not_null<typename singular_vars_tag::type*> singular_vars,\n      const gsl::not_null<\n          DirectionalIdMap<Dim, typename singular_vars_on_mortars_tag::type>*>\n          singular_vars_on_mortars,\n      const gsl::not_null<Scalar<DataVector>*> bl_radius,\n      const gsl::not_null<bool*> field_is_regularized,\n      const gsl::not_null<DirectionalIdMap<Dim, bool>*>\n          neighbors_field_is_regularized,\n      const tnsr::I<DataVector, Dim>& inertial_coords,\n      const Domain<Dim>& domain, const Element<Dim>& element,\n      const DirectionalIdMap<Dim, tnsr::I<DataVector, Dim>>&\n          all_mortar_inertial_coords,\n      const Background& background, const bool massive, const Mesh<Dim>& mesh,\n      const Scalar<DataVector>& det_inv_jacobian, const Metavariables& /*meta*/,\n      const AmrData&... /*amr_data*/) {\n    const auto& circular_orbit =\n        dynamic_cast<const ScalarSelfForce::AnalyticData::CircularOrbit&>(\n            background);\n\n    // Determine the regularized region\n    const tnsr::I<double, Dim> puncture_pos =\n        circular_orbit.puncture_position();\n    const auto puncture_in_element =\n        [&puncture_pos, &domain](const ElementId<Dim>& element_id) -> bool {\n      // Regularize full block that contains the puncture\n      const auto& block = domain.blocks()[element_id.block_id()];\n      const auto block_logical_coords =\n          block_logical_coordinates_single_point(puncture_pos, block);\n      return block_logical_coords.has_value();\n    };\n    *field_is_regularized = puncture_in_element(element.id());\n    neighbors_field_is_regularized->clear();\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      for (const auto& neighbor_id : neighbors) {\n        const dg::MortarId<Dim> mortar_id{direction, neighbor_id};\n        neighbors_field_is_regularized->emplace(\n            mortar_id, puncture_in_element(neighbor_id));\n      }\n    }\n\n    // Set the effective source if solving for the regular field\n    const size_t num_points = mesh.number_of_grid_points();\n    if (*field_is_regularized) {\n      const auto vars =\n          circular_orbit.variables(inertial_coords, analytic_tags_list{});\n      fixed_sources->initialize(num_points);\n      singular_vars->initialize(num_points);\n      get<::Tags::FixedSource<Tags::MMode>>(*fixed_sources) =\n          get<::Tags::FixedSource<Tags::MMode>>(vars);\n      get<Tags::SingularField>(*singular_vars) = get<Tags::SingularField>(vars);\n      get<::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>,\n                        Frame::Inertial>>(*singular_vars) =\n          get<::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>,\n                            Frame::Inertial>>(vars);\n      *bl_radius = get<Tags::BoyerLindquistRadius>(vars);\n      // Apply DG mass matrix to the fixed sources if the DG operator is massive\n      if (massive) {\n        *fixed_sources /= get(det_inv_jacobian);\n        ::dg::apply_mass_matrix(fixed_sources, mesh);\n      }\n    } else {\n      *fixed_sources =\n          Variables<typename fixed_sources_tag::tags_list>{num_points, 0.};\n      *singular_vars =\n          Variables<typename singular_vars_tag::tags_list>{num_points, 0.};\n      *bl_radius = Scalar<DataVector>{num_points, 0.};\n    }\n\n    // Set the singular field and flux on mortars, which is needed to transform\n    // between regularized and full fields\n    singular_vars_on_mortars->clear();\n    for (const auto& [mortar_id, mortar_inertial_coords] :\n         all_mortar_inertial_coords) {\n      if (*field_is_regularized ==\n          neighbors_field_is_regularized->at(mortar_id)) {\n        // Both elements solve for the same field. No transformation needed.\n        continue;\n      }\n      const auto vars_on_mortar = circular_orbit.variables(\n          mortar_inertial_coords, analytic_tags_list{});\n      const auto background_on_mortar = circular_orbit.variables(\n          mortar_inertial_coords,\n          tmpl::list<Tags::Alpha, Tags::Beta, Tags::Gamma>{});\n      auto& singular_vars_on_mortar = (*singular_vars_on_mortars)[mortar_id];\n      singular_vars_on_mortar.initialize(\n          mortar_inertial_coords.begin()->size());\n      get<Tags::SingularField>(singular_vars_on_mortar) =\n          get<Tags::SingularField>(vars_on_mortar);\n      const auto& deriv_singular_field_on_mortar =\n          get<::Tags::deriv<Tags::SingularField, tmpl::size_t<Dim>,\n                            Frame::Inertial>>(vars_on_mortar);\n      const auto& alpha_on_mortar = get<Tags::Alpha>(background_on_mortar);\n      tnsr::I<ComplexDataVector, Dim> singular_field_flux_on_mortar{};\n      ScalarSelfForce::fluxes(make_not_null(&singular_field_flux_on_mortar),\n                              alpha_on_mortar, deriv_singular_field_on_mortar);\n      // Assuming mortar normal is just (1, 0) or (0, 1). This is true for the\n      // rectangular domains used in the self-force problem (AlignedLattice in\n      // r, theta coordinates).\n      tnsr::i<DataVector, Dim> mortar_normal{\n          mortar_inertial_coords.begin()->size(), 0.};\n      mortar_normal.get(mortar_id.direction().dimension()) =\n          mortar_id.direction().sign();\n      normal_dot_flux(\n          make_not_null(&get<::Tags::NormalDotFlux<Tags::SingularField>>(\n              singular_vars_on_mortar)),\n          mortar_normal, singular_field_flux_on_mortar);\n    }\n  }\n};\n\n}  // namespace ScalarSelfForce::Actions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/AmrCriteria/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarSelfForceAmrCriteria)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  RefineAtPuncture.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  RefineAtPuncture.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Amr\n  AmrCriteria\n  DataStructures\n  DomainStructure\n  Elliptic\n  InitialDataUtilities\n  Options\n  Parallel\n  Serialization\n  Utilities\n  PRIVATE\n  Domain\n  ScalarSelfForceAnalyticData\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/AmrCriteria/RefineAtPuncture.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/Scalar/AmrCriteria/RefineAtPuncture.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace ScalarSelfForce::AmrCriteria {\n\nstd::array<amr::Flag, 2> RefineAtPuncture::impl(\n    const elliptic::analytic_data::Background& background,\n    const Domain<2>& domain, const ElementId<2>& element_id) {\n  const auto circular_orbit_ptr =\n      dynamic_cast<const ScalarSelfForce::AnalyticData::CircularOrbit*>(\n          &background);\n  if (circular_orbit_ptr == nullptr) {\n    ERROR(\n        \"RefineAtPuncture only works with 'CircularOrbit'. \"\n        \"See ScalarSelfForce::AmrCriteria::RefineAtPuncture for details.\");\n  }\n  const auto puncture_position = circular_orbit_ptr->puncture_position();\n  // Split (h-refine) the element if it contains the puncture\n  const auto& block = domain.blocks()[element_id.block_id()];\n  // Check if the puncture is in the block\n  const auto block_logical_coords =\n      block_logical_coordinates_single_point(puncture_position, block);\n  if (not block_logical_coords.has_value()) {\n    return make_array<2>(amr::Flag::DoNothing);\n  }\n  if (not element_logical_coordinates(*block_logical_coords, element_id)) {\n    return make_array<2>(amr::Flag::DoNothing);\n  }\n  return make_array<2>(amr::Flag::Split);\n}\n\nPUP::able::PUP_ID RefineAtPuncture::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace ScalarSelfForce::AmrCriteria\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/AmrCriteria/RefineAtPuncture.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::AmrCriteria {\n\n/*!\n * \\brief h-refine (split) elements containing a puncture\n *\n * This refinement scheme is expected to yield exponential convergence, despite\n * the presence of the non-smooth punctures.\n *\n * Currently only works with ScalarSelfForce::AnalyticData::CircularOrbit.\n * For more generic orbits, the refinement needs to happen not only at a single\n * point but along the trajectory of the puncture.\n */\nclass RefineAtPuncture : public amr::Criterion {\n public:\n  using options = tmpl::list<>;\n\n  static constexpr Options::String help = {\n      \"h-refine (split) elements containing a puncture.\"};\n\n  RefineAtPuncture() = default;\n\n  /// \\cond\n  explicit RefineAtPuncture(CkMigrateMessage* msg) : Criterion(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RefineAtPuncture);  // NOLINT\n  /// \\endcond\n\n  amr::Criteria::Type type() override { return amr::Criteria::Type::h; }\n\n  std::string observation_name() override { return \"RefineAtPuncture\"; }\n\n  using argument_tags = tmpl::list<\n      elliptic::Tags::Background<elliptic::analytic_data::Background>,\n      domain::Tags::Domain<2>>;\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  template <typename Metavariables>\n  std::array<amr::Flag, 2> operator()(\n      const elliptic::analytic_data::Background& background,\n      const Domain<2>& domain, Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<2>& element_id) const {\n    return impl(background, domain, element_id);\n  }\n\n private:\n  static std::array<amr::Flag, 2> impl(\n      const elliptic::analytic_data::Background& background,\n      const Domain<2>& domain, const ElementId<2>& element_id);\n};\n\n}  // namespace ScalarSelfForce::AmrCriteria\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/AnalyticData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarSelfForceAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CircularOrbit.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CircularOrbit.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  EffectiveSource\n  ErrorHandling\n  GeneralRelativity\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <effsource.hpp>\n#include <gsl/gsl_errno.h>\n#include <utility>\n\n#include \"DataStructures/Blaze/IntegerPow.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TortoiseCoordinates.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace ScalarSelfForce::AnalyticData {\n\nnamespace {\nstd::pair<DataVector, DataVector> boost_function_and_deriv(\n    const DataVector& r_star, const std::array<double, 4>& transition_points) {\n  return {\n      smoothstep<1>(transition_points[0], transition_points[1], r_star) +\n          smoothstep<1>(transition_points[2], transition_points[3], r_star) -\n          1.0,\n      smoothstep_deriv<1>(transition_points[0], transition_points[1], r_star) +\n          smoothstep_deriv<1>(transition_points[2], transition_points[3],\n                              r_star)};\n}\n}  // namespace\n\nCircularOrbit::CircularOrbit(const double black_hole_mass,\n                             const double black_hole_spin,\n                             const double orbital_radius,\n                             const int m_mode_number,\n                             const std::optional<std::array<double, 4>>\n                                 hyperboloidal_slicing_transitions,\n                             const bool impose_equatorial_symmetry)\n    : black_hole_mass_(black_hole_mass),\n      black_hole_spin_(black_hole_spin),\n      orbital_radius_(orbital_radius),\n      m_mode_number_(m_mode_number),\n      hyperboloidal_slicing_transitions_(hyperboloidal_slicing_transitions),\n      impose_equatorial_symmetry_(impose_equatorial_symmetry) {}\n\nCircularOrbit::CircularOrbit(CkMigrateMessage* m)\n    : elliptic::analytic_data::Background(m),\n      elliptic::analytic_data::InitialGuess(m) {}\n\ntnsr::I<double, 2> CircularOrbit::puncture_position() const {\n  const double M = black_hole_mass_;\n  const double r_plus = M * (1. + sqrt(1. - square(black_hole_spin_)));\n  const double r_0 = orbital_radius_;\n  const double r_star = gr::tortoise_radius_from_boyer_lindquist_minus_r_plus(\n      r_0 - r_plus, M, black_hole_spin_);\n  return tnsr::I<double, 2>{{{r_star, 0.}}};\n}\n\n// Background\ntuples::TaggedTuple<Tags::Alpha, Tags::Beta, Tags::Gamma>\nCircularOrbit::variables(\n    const tnsr::I<DataVector, 2>& x,\n    tmpl::list<Tags::Alpha, Tags::Beta, Tags::Gamma> /*meta*/) const {\n  const double a = black_hole_spin_ * black_hole_mass_;\n  const double M = black_hole_mass_;\n  const double r_plus = M * (1. + sqrt(1. - square(black_hole_spin_)));\n  const double r_minus = M * (1. - sqrt(1. - square(black_hole_spin_)));\n  const double r_0 = orbital_radius_;\n  const double omega = 1. / (a + sqrt(cube(r_0) / M));\n  const auto& r_star = get<0>(x);\n  const auto& cos_theta_or_sq = get<1>(x);\n  DataVector cos_theta_sq;\n  if (impose_equatorial_symmetry_) {\n    // NOLINTNEXTLINE\n    cos_theta_sq.set_data_ref(const_cast<DataVector*>(&cos_theta_or_sq));\n  } else {\n    cos_theta_sq = square(cos_theta_or_sq);\n  }\n  const DataVector r_minus_r_plus =\n      gr::boyer_lindquist_radius_minus_r_plus_from_tortoise(r_star, M,\n                                                            black_hole_spin_);\n  const DataVector r = r_minus_r_plus + r_plus;\n  const DataVector delta = r_minus_r_plus * (r - r_minus);\n  const DataVector r_sq_plus_a_sq = square(r) + square(a);\n  const DataVector r_sq_plus_a_sq_sq = square(r_sq_plus_a_sq);\n  const DataVector sin_theta_squared = 1. - cos_theta_sq;\n  const DataVector sigma_squared =\n      r_sq_plus_a_sq_sq - square(a) * delta * sin_theta_squared;\n  tuples::TaggedTuple<Tags::Alpha, Tags::Beta, Tags::Gamma> result{};\n  auto& alpha = get<Tags::Alpha>(result);\n  auto& beta = get<Tags::Beta>(result);\n  auto& gamma = get<Tags::Gamma>(result);\n  get(alpha) = delta / r_sq_plus_a_sq_sq;\n  const ComplexDataVector temp1 =\n      1. / r * std::complex<double>(0., 2. * a * m_mode_number_);\n  get(beta) = (-square(m_mode_number_ * omega) * sigma_squared +\n               4. * a * square(m_mode_number_) * omega * M * r +\n               delta * (m_mode_number_ * (m_mode_number_ + 1) +\n                        2. * M / r * (1. - square(a) / M / r) + temp1)) /\n              r_sq_plus_a_sq_sq;\n  get<0>(gamma) =\n      -1. / r_sq_plus_a_sq * std::complex<double>(0., 2. * a * m_mode_number_) +\n      2. * square(a) * get(alpha) / r;\n  get<1>(gamma) = 2. * m_mode_number_ * cos_theta_or_sq * get(alpha);\n  if (impose_equatorial_symmetry_) {\n    get<1>(gamma) += sin_theta_squared * get(alpha);\n    get<1>(gamma) *= 2.0;\n  }\n  get(alpha) *= sin_theta_squared;\n  if (impose_equatorial_symmetry_) {\n    get(alpha) *= 4. * cos_theta_sq;\n  }\n  // Hyperboloidal slicing\n  if (hyperboloidal_slicing_transitions_.has_value()) {\n    const auto [H, dH] = boost_function_and_deriv(\n        r_star, hyperboloidal_slicing_transitions_.value());\n    const double k = m_mode_number_ * omega;\n    get(beta) += std::complex<double>(0., -k) * dH + square(k) * square(H) +\n                 std::complex<double>(0., k) * get<0>(gamma) * H;\n    get<0>(gamma) += std::complex<double>(0., -2. * k) * H;\n  }\n  return result;\n}\n\n// Initial guess\ntuples::TaggedTuple<Tags::MMode> CircularOrbit::variables(\n    const tnsr::I<DataVector, 2>& x, tmpl::list<Tags::MMode> /*meta*/) {\n  tuples::TaggedTuple<Tags::MMode> result{};\n  auto& field = get<Tags::MMode>(result);\n  get(field) = ComplexDataVector{get<0>(x).size(), 0.};\n  return result;\n}\n\n// Fixed sources\ntuples::TaggedTuple<\n    ::Tags::FixedSource<Tags::MMode>, Tags::SingularField,\n    ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>,\n    Tags::BoyerLindquistRadius>\nCircularOrbit::variables(\n    const tnsr::I<DataVector, 2>& x,\n    tmpl::list<\n        ::Tags::FixedSource<Tags::MMode>, Tags::SingularField,\n        ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>,\n        Tags::BoyerLindquistRadius> /*meta*/) const {\n  const double a = black_hole_spin_ * black_hole_mass_;\n  const double M = black_hole_mass_;\n  const double r_0 = orbital_radius_;\n  const double r_plus = M * (1. + sqrt(1. - square(black_hole_spin_)));\n  const double r_minus = M * (1. - sqrt(1. - square(black_hole_spin_)));\n  {\n    // Initialize effsource\n    effsource_init(M, a);\n    coordinate xp{};\n    xp.t = 0;\n    xp.r = r_0;\n    xp.theta = M_PI_2;\n    xp.phi = 0;\n    // Circular equatorial orbit, as given in the EffectiveSource example\n    const double e = ((r_0 - 2.0 * M) * sqrt(M * r_0) + a * M) /\n                     (sqrt(M * r_0) * sqrt(r_0 * r_0 - 3.0 * M * r_0 +\n                                           2.0 * a * sqrt(M * r_0)));\n    const double l = (M * (a * a + r_0 * r_0 - 2.0 * a * sqrt(M * r_0))) /\n                     (sqrt(M * r_0) * sqrt(r_0 * r_0 - 3.0 * M * r_0 +\n                                           2.0 * a * sqrt(M * r_0)));\n    effsource_set_particle(&xp, e, l, 0.);\n  }\n  const auto& r_star = get<0>(x);\n  if (hyperboloidal_slicing_transitions_.has_value() and\n      (min(r_star) < (*hyperboloidal_slicing_transitions_)[1] or\n       max(r_star) > (*hyperboloidal_slicing_transitions_)[2])) {\n    ERROR(\n        \"The effective source is only valid where no hyperboloidal slicing is \"\n        \"applied, which is in the r_* range [\"\n        << (*hyperboloidal_slicing_transitions_)[1] << \", \"\n        << (*hyperboloidal_slicing_transitions_)[2]\n        << \"], but was requested in the range [\" << min(r_star) << \", \"\n        << max(r_star) << \"]\");\n  }\n  const auto& cos_theta_or_sq = get<1>(x);\n  DataVector cos_theta;\n  DataVector cos_theta_sq;\n  if (impose_equatorial_symmetry_) {\n    // NOLINTNEXTLINE\n    cos_theta_sq.set_data_ref(const_cast<DataVector*>(&cos_theta_or_sq));\n    cos_theta = sqrt(cos_theta_or_sq);\n  } else {\n    // NOLINTNEXTLINE\n    cos_theta.set_data_ref(const_cast<DataVector*>(&cos_theta_or_sq));\n    cos_theta_sq = square(cos_theta_or_sq);\n  }\n  const DataVector r_minus_r_plus =\n      gr::boyer_lindquist_radius_minus_r_plus_from_tortoise(r_star, M,\n                                                            black_hole_spin_);\n  const DataVector r = r_minus_r_plus + r_plus;\n  const DataVector delta = r_minus_r_plus * (r - r_minus);\n  const DataVector r_sq_plus_a_sq = square(r) + square(a);\n  const DataVector r_sq_plus_a_sq_sq = square(r_sq_plus_a_sq);\n  const DataVector sin_theta_sq = 1. - cos_theta_sq;\n  const DataVector sin_theta = sqrt(sin_theta_sq);\n  const DataVector sin_theta_pow_m = integer_pow(sin_theta, m_mode_number_);\n  const DataVector delta_phi = m_mode_number_ * a / (r_plus - r_minus) *\n                               log((r - r_plus) / (r - r_minus));\n  const ComplexDataVector rotation =\n      cos(delta_phi) - std::complex<double>(0., 1.) * sin(delta_phi);\n  tuples::TaggedTuple<\n      ::Tags::FixedSource<Tags::MMode>, Tags::SingularField,\n      ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>,\n      Tags::BoyerLindquistRadius>\n      result{};\n  get(get<Tags::BoyerLindquistRadius>(result)) = r;\n  const size_t num_points = get<0>(x).size();\n  Scalar<ComplexDataVector>& effective_source =\n      get<::Tags::FixedSource<Tags::MMode>>(result);\n  get(effective_source).destructive_resize(num_points);\n  Scalar<ComplexDataVector>& singular_field = get<Tags::SingularField>(result);\n  get(singular_field).destructive_resize(num_points);\n  tnsr::i<ComplexDataVector, 2>& deriv_singular_field =\n      get<::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>>(\n          result);\n  get<0>(deriv_singular_field).destructive_resize(num_points);\n  get<1>(deriv_singular_field).destructive_resize(num_points);\n  {\n    // Call into effsource\n    coordinate x_i{};\n    std::array<double, 2> PhiS{};\n    std::array<double, 8> dPhiS_dx{};\n    std::array<double, 20> d2PhiS_dx2{};\n    std::array<double, 2> src{};\n    for (size_t i = 0; i < num_points; ++i) {\n      x_i.t = 0;\n      x_i.r = r[i];\n      x_i.theta = acos(cos_theta[i]);\n      x_i.phi = 0;\n      effsource_calc_m(m_mode_number_, &x_i, PhiS.data(), dPhiS_dx.data(),\n                       d2PhiS_dx2.data(), src.data());\n      get(effective_source)[i] = src[0] + std::complex<double>(0., 1.) * src[1];\n      get(singular_field)[i] = PhiS[0] + std::complex<double>(0., 1.) * PhiS[1];\n      get<0>(deriv_singular_field)[i] =\n          dPhiS_dx[2] + std::complex<double>(0., 1.) * dPhiS_dx[3];\n      get<1>(deriv_singular_field)[i] =\n          dPhiS_dx[4] + std::complex<double>(0., 1.) * dPhiS_dx[5];\n    }\n  }\n  // Rotate the source by delta_phi and multiply by r / 2 pi\n  get(effective_source) *= rotation * 0.5 * r / M_PI;\n  // Factor Delta * (r^2 + a^2 cos^2(theta)) / Sigma^2\n  // Factor Sigma^2 / (r^2 + a^2)^2 from first-order formulation\n  // Factor 1/sin(theta)^m from change of variables\n  get(effective_source) *= delta * (square(r) + square(a) * cos_theta_sq) /\n                           r_sq_plus_a_sq_sq / sin_theta_pow_m;\n  get(singular_field) *= rotation * 0.5 * r / M_PI / sin_theta_pow_m;\n  get<0>(deriv_singular_field) *= rotation * 0.5 * r / M_PI / sin_theta_pow_m;\n  get<0>(deriv_singular_field) +=\n      get(singular_field) / r - std::complex<double>(0., a * m_mode_number_) /\n                                    delta * get(singular_field);\n  get<0>(deriv_singular_field) *= delta / r_sq_plus_a_sq;\n  get<1>(deriv_singular_field) *= rotation * 0.5 * r / M_PI / sin_theta_pow_m;\n  get<1>(deriv_singular_field) /= -sin_theta;\n  if (impose_equatorial_symmetry_) {\n    get<1>(deriv_singular_field) /= 2. * cos_theta;\n  }\n  {\n    ComplexDataVector add_term =\n        m_mode_number_ * get(singular_field) / sin_theta_sq;\n    if (impose_equatorial_symmetry_) {\n      add_term *= 0.5;\n    } else {\n      add_term *= cos_theta;\n    }\n    get<1>(deriv_singular_field) += add_term;\n  }\n  return result;\n}\n\nvoid CircularOrbit::pup(PUP::er& p) {\n  elliptic::analytic_data::Background::pup(p);\n  elliptic::analytic_data::InitialGuess::pup(p);\n  p | black_hole_mass_;\n  p | black_hole_spin_;\n  p | orbital_radius_;\n  p | m_mode_number_;\n  p | hyperboloidal_slicing_transitions_;\n  p | impose_equatorial_symmetry_;\n}\n\nbool operator==(const CircularOrbit& lhs, const CircularOrbit& rhs) {\n  return lhs.black_hole_mass_ == rhs.black_hole_mass_ and\n         lhs.black_hole_spin_ == rhs.black_hole_spin_ and\n         lhs.orbital_radius_ == rhs.orbital_radius_ and\n         lhs.m_mode_number_ == rhs.m_mode_number_ and\n         lhs.hyperboloidal_slicing_transitions_ ==\n             rhs.hyperboloidal_slicing_transitions_ and\n         lhs.impose_equatorial_symmetry_ == rhs.impose_equatorial_symmetry_;\n}\n\nbool operator!=(const CircularOrbit& lhs, const CircularOrbit& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID CircularOrbit::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace ScalarSelfForce::AnalyticData\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::AnalyticData {\n\n/*!\n * \\brief Scalar self force for a scalar charge on a circular equatorial orbit.\n *\n * This class implements Eq. (2.9) in \\cite Osburn:2022bby . It does so by\n * defining the background fields $\\alpha$, $\\beta$, and $\\gamma_i$ in the\n * general form of the equations\n * \\begin{equation}\n * -\\partial_i F^i + \\beta \\Psi_m + \\gamma_i \\partial_i \\Psi_m  = S_m\n * \\text{.}\n * \\end{equation}\n * with the flux\n * \\begin{equation}\n * F^i = \\{\\partial_{r_\\star}, \\alpha \\partial_{\\cos\\theta}\\} \\Psi_m\n * \\text{.}\n * \\end{equation}\n * We make the following changes compared to \\cite Osburn:2022bby :\n *\n * - Multiply by the factor $\\Sigma^2 / (r^2 + a^2)^2$ so that we can easily\n *   write the equations in first-order flux form.\n * - Use $\\cos\\theta$ as angular coordinate instead of $\\theta$. This avoids\n *   the $\\cot\\theta$ term by rewriting the angular derivatives as:\n *   \\f[\n *   \\partial_\\theta^2+\\cot\\theta\\partial_\\theta =\n *   \\partial_{\\cos\\theta}\\sin^2\\theta\\partial_{\\cos\\theta}\n *   \\f]\n * - Decompose $\\Psi_m = \\sin(\\theta)^m u_m(r_\\star, \\theta)$. This avoids the\n *   $m^2/\\sin^2\\theta$ term by factoring out the singular behavior at the\n *   poles. The equations transform as:\n *   \\f[\n *   -\\partial_{\\cos\\theta}\\sin^2\\theta\\partial_{\\cos\\theta} \\Psi_m +\n *   \\frac{m^2}{\\sin^2\\theta}\\Psi_m = \\sin(\\theta)^m \\left( m(m+1)\n *   + 2m \\cos\\theta \\partial_{\\cos\\theta}\n *   - \\partial_{\\cos\\theta}\\sin^2\\theta\\partial_{\\cos\\theta} \\right) u_m\n *   \\f]\n *   We divide by $\\sin(\\theta)^m$ to get the equations for $u_m$.\n *\n * Written this way, the equations are regular at the poles and converge\n * exponentially. We also don't have to apply angular boundary conditions\n * because regularity at the poles is automatically enforced by the\n * $\\sin^2\\theta$ factor in the flux.\n *\n * The resulting factors in the equation are:\n * \\begin{align}\n * &\\alpha = \\frac{\\Delta}{(r^2 + a^2)^2} \\sin^2\\theta \\\\\n * &\\beta = \\left(-m^2\\Omega^2 \\Sigma^2 + 4a m^2 \\Omega M r + \\Delta \\left[\n *   m (m + 1) + \\frac{2M}{r}(1-\\frac{a^2}{Mr}) + \\frac{2iam}{r}\n *   \\right]\\right) \\frac{1}{(r^2 + a^2)^2} \\\\\n * &\\gamma_{r_\\star} = -\\frac{2iam}{r^2+a^2} + \\frac{2a^2}{r}\n *   \\frac{\\alpha}{\\sin^2\\theta} \\\\\n * &\\gamma_{\\cos\\theta} = 2 m \\cos(\\theta) \\frac{\\Delta}{(r^2 + a^2)^2}\n * \\end{align}\n *\n * This class also provides the effective source $S_m^\\mathrm{eff} = \\Delta_m\n * \\Psi_m^P$ and the singular field $\\Psi_m^P$ in the regularized region (see\n * `ScalarSelfForce::FirstOrderSystem` and Sec. III in \\cite Osburn:2022bby ).\n * The effective source is computed using the scalar EffectiveSource code by\n * Wardell et. al. (https://github.com/barrywardell/EffectiveSource and\n * \\cite Wardell:2011gb ). It is then transformed to correspond to the m-mode\n * decomposition used in \\cite Osburn:2022bby Eq. (2.8) as:\n * \\begin{align}\n *   &\\Psi_m^P = \\frac{r}{2 \\pi} e^{-i m \\Delta\\phi} \\Phi_m^\\mathrm{Wardell} \\\\\n *   &S_m^\\mathrm{eff} = \\frac{r}{2 \\pi} e^{-i m \\Delta\\phi}\n *     \\frac{\\Delta\\,(r^2 + a^2\\cos^2\\theta)}{(r^2 + a^2)^2}\n *     S_m^\\mathrm{Wardell}\n *   \\text{,}\n * \\end{align}\n * where $\\Delta\\phi = \\frac{a}{r_+ - r_-} \\ln(\\frac{r-r_+}{r-r_-})$ (Eq. (2.7)\n * in \\cite Osburn:2022bby ).\n * We also divide by $\\sin(\\theta)^m$ to account for the change of variable from\n * $\\Psi_m$ to $u_m$ described above.\n *\n * \\par Impose equatorial symmetry\n * Since this work is restricted to equatorial orbits, we can enforce equatorial\n * symmetry by reformulating the equations in terms of the coordinate\n * $z^2 = \\cos^2\\theta$ instead of $z = \\cos\\theta$. This transform the factors\n * of first-order flux formulation as:\n * \\begin{equation}\n * F^{\\cos^2\\theta}  = 4 \\cos^2\\theta F^{\\cos\\theta}\n * \\text{.}\n * \\end{equation}\n * \\begin{equation}\n * \\gamma_{\\cos^2\\theta} = 2 \\cos\\theta \\gamma_{\\cos\\theta} + 2 \\alpha\n * \\text{.}\n * \\end{equation}\n *\n * \\par Hyperboloidal slicing\n * Transforming to a hyperboloidal time coordinate $s = t - h(r_*)$ can simplify\n * the solution a lot because the slice will only intersect a finite number of\n * wave fronts. In frequency domain this transformation means that partial\n * derivatives transform as:\n * \\begin{align}\n *   &\\partial_{r_*} \\rightarrow \\partial_{r_*} + i m\\Omega H(r_*) \\\\\n *   &\\partial_{r_*}^2 \\rightarrow \\partial_{r_*}^2 +\n *     2 i m\\Omega H(r_*) \\partial_{r_*} + i m\\Omega H'(r_*)\n *     -m^2\\Omega^2 H(r_*)^2\n * \\end{align}\n * where $H(r_*) = h'(r_*)$ is the boost function that asymptotes to\n * $H(r_* \\rightarrow \\pm \\infty) = \\pm 1$.\n * This maps to the following additional terms in $\\beta$ and $\\gamma_{r_*}$:\n * \\begin{align}\n *   &\\beta \\rightarrow \\beta + i m\\Omega \\gamma_{r_*} H(r_*)\n *     - i m\\Omega H'(r_*) + m^2\\Omega^2 H(r_*)^2 \\\\\n *   &\\gamma_{r_*} \\rightarrow \\gamma_{r_*} - 2 i m\\Omega H(r_*)\n * \\end{align}\n * For the boost function we choose here a simple sigmoid so that it is exactly\n * zero in a finite region around the puncture where we apply the effective\n * source, and transitions to -1 and 1 outside of this region. We choose\n * the $C^1$ continuous `smoothstep<1>` for this. Reasonable transition points\n * are from the boundaries of the effective source region to about $20M$\n * away from it, though this hasn't been tested very much yet. We may also want\n * to try jumping from $H=0$ to $H=\\pm 1$ discontinuously at the boundary of the\n * effective source region, which is supported by the DG scheme (see\n * `fluxes_computer::is_discontinuous` in\n * `elliptic::protocols::FirstOrderSystem`), so (1) we don't have to resolve the\n * transition, and (2) we can more easily support the 2nd order self-force\n * source.\n */\nclass CircularOrbit : public elliptic::analytic_data::Background,\n                      public elliptic::analytic_data::InitialGuess {\n public:\n  struct BlackHoleMass {\n    static constexpr Options::String help =\n        \"Kerr mass parameter 'M' of the black hole\";\n    using type = double;\n  };\n  struct BlackHoleSpin {\n    static constexpr Options::String help =\n        \"Kerr dimensionless spin parameter 'chi' of the black hole\";\n    using type = double;\n  };\n  struct OrbitalRadius {\n    static constexpr Options::String help =\n        \"Radius 'r_0' of the circular orbit\";\n    using type = double;\n  };\n  struct MModeNumber {\n    static constexpr Options::String help =\n        \"Mode number 'm' of the scalar field\";\n    using type = int;\n  };\n  struct HyperboloidalSlicingTransitions {\n    static constexpr Options::String help =\n        \"Enable hyperboloidal slicing by specifying the transition points for \"\n        \"the boost function. The boost function transitions from -1 to zero \"\n        \"between the first two points and from zero to 1 between the last \"\n        \"two points. The effective source can only be evaluated where the \"\n        \"boost function is zero, so the regularized region must be between \"\n        \"the second and third points.\";\n    using type = Options::Auto<std::array<double, 4>, Options::AutoLabel::None>;\n  };\n  struct ImposeEquatorialSymmetry {\n    static constexpr Options::String help =\n        \"Impose symmetry across the equatorial plane by using cos(theta)^2 \"\n        \"as the angular coordinate instead of cos(theta). This means the \"\n        \"domain should span [0, 1] instead of [-1, 1].\";\n    using type = bool;\n  };\n  using options =\n      tmpl::list<BlackHoleMass, BlackHoleSpin, OrbitalRadius, MModeNumber,\n                 HyperboloidalSlicingTransitions, ImposeEquatorialSymmetry>;\n  static constexpr Options::String help =\n      \"Quasicircular orbit of a scalar point charge in Kerr spacetime\";\n\n  CircularOrbit() = default;\n  CircularOrbit(const CircularOrbit&) = default;\n  CircularOrbit& operator=(const CircularOrbit&) = default;\n  CircularOrbit(CircularOrbit&&) = default;\n  CircularOrbit& operator=(CircularOrbit&&) = default;\n  ~CircularOrbit() override = default;\n\n  CircularOrbit(\n      double black_hole_mass, double black_hole_spin, double orbital_radius,\n      int m_mode_number,\n      std::optional<std::array<double, 4>> hyperboloidal_slicing_transitions,\n      bool impose_equatorial_symmetry);\n\n  explicit CircularOrbit(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CircularOrbit);\n\n  tnsr::I<double, 2> puncture_position() const;\n  double black_hole_mass() const { return black_hole_mass_; }\n  double black_hole_spin() const { return black_hole_spin_; }\n  double orbital_radius() const { return orbital_radius_; }\n  int m_mode_number() const { return m_mode_number_; }\n  std::optional<std::array<double, 4>> hyperboloidal_slicing_transitions()\n      const {\n    return hyperboloidal_slicing_transitions_;\n  }\n  bool impose_equatorial_symmetry() const {\n    return impose_equatorial_symmetry_;\n  }\n\n  using background_tags = tmpl::list<Tags::Alpha, Tags::Beta, Tags::Gamma>;\n  using source_tags = tmpl::list<\n      ::Tags::FixedSource<Tags::MMode>, Tags::SingularField,\n      ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>,\n      Tags::BoyerLindquistRadius>;\n\n  // Background\n  tuples::tagged_tuple_from_typelist<background_tags> variables(\n      const tnsr::I<DataVector, 2>& x, background_tags /*meta*/) const;\n\n  // Initial guess\n  static tuples::TaggedTuple<Tags::MMode> variables(\n      const tnsr::I<DataVector, 2>& x, tmpl::list<Tags::MMode> /*meta*/);\n\n  // Fixed sources\n  tuples::tagged_tuple_from_typelist<source_tags> variables(\n      const tnsr::I<DataVector, 2>& x, source_tags /*meta*/) const;\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 2>& x, const Mesh<2>& /*mesh*/,\n      const InverseJacobian<DataVector, 2, Frame::ElementLogical,\n                            Frame::Inertial>& /*inv_jacobian*/,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables(x, tmpl::list<RequestedTags...>{});\n  }\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const CircularOrbit& lhs, const CircularOrbit& rhs);\n\n  double black_hole_mass_{std::numeric_limits<double>::signaling_NaN()};\n  double black_hole_spin_{std::numeric_limits<double>::signaling_NaN()};\n  double orbital_radius_{std::numeric_limits<double>::signaling_NaN()};\n  int m_mode_number_{};\n  std::optional<std::array<double, 4>> hyperboloidal_slicing_transitions_{};\n  bool impose_equatorial_symmetry_{false};\n};\n\nbool operator!=(const CircularOrbit& lhs, const CircularOrbit& rhs);\n\n}  // namespace ScalarSelfForce::AnalyticData\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarSelfForceBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  None.cpp\n  Sommerfeld.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  None.hpp\n  Sommerfeld.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Elliptic\n  Options\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/None.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Boundary conditions for the scalar self-force system.\nnamespace ScalarSelfForce::BoundaryConditions {\n\n/// List of all boundary conditions for the scalar self-force system.\nusing standard_boundary_conditions = tmpl::list<Sommerfeld, None>;\n\n}  // namespace ScalarSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/None.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/None.hpp\"\n\nnamespace ScalarSelfForce::BoundaryConditions {\n\nNone::None(CkMigrateMessage* m) : Base(m) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition> None::get_clone()\n    const {\n  return std::make_unique<None>(*this);\n}\n\nvoid None::apply(\n    const gsl::not_null<Scalar<ComplexDataVector>*> /*field*/,\n    const gsl::not_null<Scalar<ComplexDataVector>*> /*n_dot_field_gradient*/,\n    const tnsr::i<ComplexDataVector, 2>& /*deriv_field*/) const {\n  // Nothing to do\n}\n\nvoid None::apply_linearized(\n    const gsl::not_null<Scalar<ComplexDataVector>*> /*field_correction*/,\n    const gsl::not_null<Scalar<ComplexDataVector>*>\n    /*n_dot_field_gradient_correction*/,\n    const tnsr::i<ComplexDataVector, 2>& /*deriv_field_correction*/) const {\n  // Nothing to do\n}\n\nbool operator==(const None& /*lhs*/, const None& /*rhs*/) { return true; }\n\nbool operator!=(const None& lhs, const None& rhs) { return not(lhs == rhs); }\n\nPUP::able::PUP_ID None::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace ScalarSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/None.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::BoundaryConditions {\n\n/*!\n * \\brief Applies no boundary condition at all. Used to impose nothing but\n * regularity at the horizon in horizon-penetrating coordinates or at\n * angular boundaries.\n */\nclass None : public elliptic::BoundaryConditions::BoundaryCondition<2> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<2>;\n\n public:\n  static constexpr Options::String help =\n      \"Applies no boundary condition at all. Used to impose nothing but \"\n      \"regularity at the horizon in horizon-penetrating coordinates.\";\n  using options = tmpl::list<>;\n\n  None() = default;\n  None(const None&) = default;\n  None& operator=(const None&) = default;\n  None(None&&) = default;\n  None& operator=(None&&) = default;\n  ~None() override = default;\n\n  /// \\cond\n  explicit None(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(None);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override;\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {elliptic::BoundaryConditionType::Neumann};\n  }\n\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(gsl::not_null<Scalar<ComplexDataVector>*> field,\n             gsl::not_null<Scalar<ComplexDataVector>*> n_dot_field_gradient,\n             const tnsr::i<ComplexDataVector, 2>& deriv_field) const;\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<ComplexDataVector>*> field_correction,\n      gsl::not_null<Scalar<ComplexDataVector>*> n_dot_field_gradient_correction,\n      const tnsr::i<ComplexDataVector, 2>& deriv_field_correction) const;\n\n private:\n  friend bool operator==(const None& lhs, const None& rhs);\n};\n\nbool operator!=(const None& lhs, const None& rhs);\n\n}  // namespace ScalarSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Sommerfeld.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Sommerfeld.hpp\"\n\n#include <complex>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarSelfForce::BoundaryConditions {\n\nSommerfeld::Sommerfeld(const double black_hole_mass,\n                       const double black_hole_spin,\n                       const double orbital_radius, const int m_mode_number,\n                       const bool hyperboloidal_slicing, const int order)\n    : black_hole_mass_(black_hole_mass),\n      black_hole_spin_(black_hole_spin),\n      orbital_radius_(orbital_radius),\n      m_mode_number_(m_mode_number),\n      hyperboloidal_slicing_(hyperboloidal_slicing),\n      order_(order) {}\n\nSommerfeld::Sommerfeld(CkMigrateMessage* m) : Base(m) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nSommerfeld::get_clone() const {\n  return std::make_unique<Sommerfeld>(*this);\n}\n\nvoid Sommerfeld::apply(\n    const gsl::not_null<Scalar<ComplexDataVector>*> field,\n    const gsl::not_null<Scalar<ComplexDataVector>*> n_dot_field_gradient,\n    const tnsr::i<ComplexDataVector, 2>& /*deriv_field*/,\n    const Scalar<ComplexDataVector>& beta,\n    const tnsr::i<ComplexDataVector, 2>& gamma) const {\n  if (hyperboloidal_slicing_) {\n    if (order_ == 1) {\n      get(*n_dot_field_gradient) = 0.;\n    } else if (order_ == 2) {\n      get(*n_dot_field_gradient) = -get(beta) / get<0>(gamma) * get(*field);\n    } else {\n      ERROR(\"Order \" << order_\n                     << \" not implemented for Sommerfeld boundary condition \"\n                        \"with hyperboloidal slicing.\");\n    }\n    return;\n  }\n  const double a = black_hole_spin_ * black_hole_mass_;\n  const double M = black_hole_mass_;\n  const double r_0 = orbital_radius_;\n  const double omega = 1. / (a + sqrt(cube(r_0) / M));\n  const double k = m_mode_number_ * omega;\n  if (order_ == 1) {\n    get(*n_dot_field_gradient) = std::complex<double>(0.0, k) * get(*field);\n  } else if (order_ == 2) {\n    get(*n_dot_field_gradient) =\n        (square(k) - get(beta)) /\n        (get<0>(gamma) - std::complex<double>(0.0, 2. * k)) * get(*field);\n  } else {\n    ERROR(\"Order \" << order_\n                   << \" not implemented for Sommerfeld boundary condition.\");\n  }\n}\n\nvoid Sommerfeld::apply_linearized(\n    const gsl::not_null<Scalar<ComplexDataVector>*> field_correction,\n    const gsl::not_null<Scalar<ComplexDataVector>*>\n        n_dot_field_gradient_correction,\n    const tnsr::i<ComplexDataVector, 2>& deriv_field_correction,\n    const Scalar<ComplexDataVector>& beta,\n    const tnsr::i<ComplexDataVector, 2>& gamma) const {\n  apply(field_correction, n_dot_field_gradient_correction,\n        deriv_field_correction, beta, gamma);\n}\n\nvoid Sommerfeld::pup(PUP::er& p) {\n  Base::pup(p);\n  p | black_hole_mass_;\n  p | black_hole_spin_;\n  p | orbital_radius_;\n  p | m_mode_number_;\n  p | hyperboloidal_slicing_;\n  p | order_;\n}\n\nbool operator==(const Sommerfeld& lhs, const Sommerfeld& rhs) {\n  return lhs.black_hole_mass_ == rhs.black_hole_mass_ and\n         lhs.black_hole_spin_ == rhs.black_hole_spin_ and\n         lhs.orbital_radius_ == rhs.orbital_radius_ and\n         lhs.m_mode_number_ == rhs.m_mode_number_ and\n         lhs.hyperboloidal_slicing_ == rhs.hyperboloidal_slicing_ and\n         lhs.order_ == rhs.order_;\n}\n\nbool operator!=(const Sommerfeld& lhs, const Sommerfeld& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID Sommerfeld::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace ScalarSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Sommerfeld.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::BoundaryConditions {\n\n/*!\n * \\brief Radial Sommerfeld boundary conditions for the m-mode field.\n *\n * The radial boundary conditions are given in Eq. (4.10-4.11) in\n * \\cite Osburn:2022bby . They apply both near the Kerr horizon (inner radial\n * boundary) and at large distance (outer radial boundary):\n *\n * \\begin{equation}\n * n_i F^i = i m \\Omega \\Psi_m\n * \\end{equation}\n *\n * These boundary conditions currently assume a circular equatorial orbit.\n */\nclass Sommerfeld : public elliptic::BoundaryConditions::BoundaryCondition<2> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<2>;\n\n public:\n  struct BlackHoleMass {\n    static constexpr Options::String help =\n        \"Kerr mass parameter 'M' of the black hole\";\n    using type = double;\n  };\n  struct BlackHoleSpin {\n    static constexpr Options::String help =\n        \"Kerr dimensionless spin parameter 'chi' of the black hole\";\n    using type = double;\n  };\n  struct OrbitalRadius {\n    static constexpr Options::String help =\n        \"Radius 'r_0' of the circular orbit\";\n    using type = double;\n  };\n  struct MModeNumber {\n    static constexpr Options::String help =\n        \"Mode number 'm' of the scalar field\";\n    using type = int;\n  };\n  struct HyperboloidalSlicing {\n    static constexpr Options::String help =\n        \"Whether hyperboloidal slicing is applied. If true, a simple Neumann \"\n        \"boundary condition is applied.\";\n    using type = bool;\n  };\n  struct Order {\n    static constexpr Options::String help =\n        \"Order of the boundary condition. First order (Order=1) implements \"\n        \"just the leading 'i m Omega' term. Second order (Order=2) includes \"\n        \"the next-to-leading '1/r' term as well (Robin-type).\";\n    using type = int;\n  };\n\n  static constexpr Options::String help =\n      \"Radial Sommerfeld boundary condition\";\n  using options = tmpl::list<BlackHoleMass, BlackHoleSpin, OrbitalRadius,\n                             MModeNumber, HyperboloidalSlicing, Order>;\n\n  Sommerfeld() = default;\n  Sommerfeld(const Sommerfeld&) = default;\n  Sommerfeld& operator=(const Sommerfeld&) = default;\n  Sommerfeld(Sommerfeld&&) = default;\n  Sommerfeld& operator=(Sommerfeld&&) = default;\n  ~Sommerfeld() override = default;\n\n  explicit Sommerfeld(double black_hole_mass, double black_hole_spin,\n                      double orbital_radius, int m_mode_number,\n                      bool hyperboloidal_slicing, int order);\n\n  double black_hole_mass() const { return black_hole_mass_; }\n  double black_hole_spin() const { return black_hole_spin_; }\n  double orbital_radius() const { return orbital_radius_; }\n  int m_mode_number() const { return m_mode_number_; }\n  bool hyperboloidal_slicing() const { return hyperboloidal_slicing_; }\n  int order() const { return order_; }\n\n  /// \\cond\n  explicit Sommerfeld(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Sommerfeld);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override;\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {elliptic::BoundaryConditionType::Neumann};\n  }\n\n  using argument_tags =\n      tmpl::list<Tags::Beta, Tags::Gamma>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(gsl::not_null<Scalar<ComplexDataVector>*> field,\n             gsl::not_null<Scalar<ComplexDataVector>*> n_dot_field_gradient,\n             const tnsr::i<ComplexDataVector, 2>& deriv_field,\n             const Scalar<ComplexDataVector>& beta,\n             const tnsr::i<ComplexDataVector, 2>& gamma) const;\n\n  using argument_tags_linearized =\n      tmpl::list<Tags::Beta, Tags::Gamma>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<ComplexDataVector>*> field_correction,\n      gsl::not_null<Scalar<ComplexDataVector>*> n_dot_field_gradient_correction,\n      const tnsr::i<ComplexDataVector, 2>& deriv_field_correction,\n      const Scalar<ComplexDataVector>& beta,\n      const tnsr::i<ComplexDataVector, 2>& gamma) const;\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const Sommerfeld& lhs, const Sommerfeld& rhs);\n\n  double black_hole_mass_{std::numeric_limits<double>::signaling_NaN()};\n  double black_hole_spin_{std::numeric_limits<double>::signaling_NaN()};\n  double orbital_radius_{std::numeric_limits<double>::signaling_NaN()};\n  int m_mode_number_{};\n  bool hyperboloidal_slicing_{};\n  int order_{};\n};\n\nbool operator!=(const Sommerfeld& lhs, const Sommerfeld& rhs);\n\n}  // namespace ScalarSelfForce::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarSelfForce)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Equations.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Equations.hpp\n  FirstOrderSystem.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DomainStructure\n  ErrorHandling\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(AmrCriteria)\nadd_subdirectory(AnalyticData)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(Events)\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/Scalar/Equations.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n\nnamespace ScalarSelfForce {\n\nvoid fluxes(const gsl::not_null<tnsr::I<ComplexDataVector, 2>*> flux,\n            const Scalar<ComplexDataVector>& alpha,\n            const tnsr::i<ComplexDataVector, 2>& field_gradient) {\n  get<0>(*flux) = get<0>(field_gradient);\n  get<1>(*flux) = get(alpha) * get<1>(field_gradient);\n}\n\nvoid fluxes_on_face(const gsl::not_null<tnsr::I<ComplexDataVector, 2>*> flux,\n                    const Scalar<ComplexDataVector>& alpha,\n                    const tnsr::I<DataVector, 2>& face_normal_vector,\n                    const Scalar<ComplexDataVector>& field) {\n  get<0>(*flux) = get<0>(face_normal_vector) * get(field);\n  get<1>(*flux) = get(alpha) * get<1>(face_normal_vector) * get(field);\n}\n\nvoid add_sources(const gsl::not_null<Scalar<ComplexDataVector>*> source,\n                 const Scalar<ComplexDataVector>& beta,\n                 const tnsr::i<ComplexDataVector, 2>& gamma,\n                 const Scalar<ComplexDataVector>& field,\n                 const tnsr::i<ComplexDataVector, 2>& field_gradient) {\n  get(*source) += get(beta) * get(field) +\n                  get<0>(gamma) * get<0>(field_gradient) +\n                  get<1>(gamma) * get<1>(field_gradient);\n}\n\nvoid Fluxes::apply(const gsl::not_null<tnsr::I<ComplexDataVector, 2>*> flux,\n                   const Scalar<ComplexDataVector>& alpha,\n                   const Scalar<ComplexDataVector>& /*field*/,\n                   const tnsr::i<ComplexDataVector, 2>& field_gradient) {\n  fluxes(flux, alpha, field_gradient);\n}\n\nvoid Fluxes::apply(const gsl::not_null<tnsr::I<ComplexDataVector, 2>*> flux,\n                   const Scalar<ComplexDataVector>& alpha,\n                   const tnsr::i<DataVector, 2>& /*face_normal*/,\n                   const tnsr::I<DataVector, 2>& face_normal_vector,\n                   const Scalar<ComplexDataVector>& field) {\n  fluxes_on_face(flux, alpha, face_normal_vector, field);\n}\n\nvoid Sources::apply(\n    const gsl::not_null<Scalar<ComplexDataVector>*> scalar_equation,\n    const Scalar<ComplexDataVector>& beta,\n    const tnsr::i<ComplexDataVector, 2>& gamma,\n    const Scalar<ComplexDataVector>& field,\n    const tnsr::i<ComplexDataVector, 2>& field_gradient,\n    const tnsr::I<ComplexDataVector, 2>& /*flux*/) {\n  add_sources(scalar_equation, beta, gamma, field, field_gradient);\n}\n\nvoid ModifyBoundaryData::apply(\n    const gsl::not_null<Scalar<ComplexDataVector>*> field,\n    const gsl::not_null<Scalar<ComplexDataVector>*> n_dot_flux,\n    const DirectionalId<Dim>& mortar_id, const bool field_is_regularized,\n    const DirectionalIdMap<Dim, bool>& neighbors_field_is_regularized,\n    const DirectionalIdMap<Dim, typename singular_vars_on_mortars_tag::type>&\n        singular_vars_on_mortars) {\n  if (field_is_regularized == neighbors_field_is_regularized.at(mortar_id)) {\n    // Both elements solve for the same field. Nothing to do.\n    return;\n  }\n  // Subtract the singular field on the regularized side, and add it on the\n  // other side\n  const double sign = field_is_regularized ? -1. : 1.;\n  const auto& singular_field =\n      get<Tags::SingularField>(singular_vars_on_mortars.at(mortar_id));\n  const auto& singular_field_n_dot_flux =\n      get<::Tags::NormalDotFlux<Tags::SingularField>>(\n          singular_vars_on_mortars.at(mortar_id));\n  get(*field) += sign * get(singular_field);\n  get(*n_dot_flux) -= sign * get(singular_field_n_dot_flux);\n}\n\n}  // namespace ScalarSelfForce\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce {\n\n/*!\n * \\brief The first-order flux\n * $F^i=\\{\\partial_{r_\\star}, \\alpha \\partial_{\\cos\\theta}\\}\\Psi_m$.\n */\nvoid fluxes(gsl::not_null<tnsr::I<ComplexDataVector, 2>*> flux,\n            const Scalar<ComplexDataVector>& alpha,\n            const tnsr::i<ComplexDataVector, 2>& field_gradient);\n\n/*!\n * \\brief The first-order flux on an element face\n * $F^i=\\{n_{r_\\star}, \\alpha n_{\\cos\\theta}\\}\\Psi_m$.\n */\nvoid fluxes_on_face(gsl::not_null<tnsr::I<ComplexDataVector, 2>*> flux,\n                    const Scalar<ComplexDataVector>& alpha,\n                    const tnsr::I<DataVector, 2>& face_normal_vector,\n                    const Scalar<ComplexDataVector>& field);\n\n/*!\n * \\brief The source term $\\beta \\Psi_m + \\gamma_i \\partial_i \\Psi_m$.\n */\nvoid add_sources(gsl::not_null<Scalar<ComplexDataVector>*> source,\n                 const Scalar<ComplexDataVector>& beta,\n                 const tnsr::i<ComplexDataVector, 2>& gamma,\n                 const Scalar<ComplexDataVector>& field,\n                 const tnsr::i<ComplexDataVector, 2>& field_gradient);\n\n/// Fluxes $F^i$ for the scalar self-force system.\n/// \\see ScalarSelfForce::FirstOrderSystem\nstruct Fluxes {\n  using argument_tags = tmpl::list<Tags::Alpha>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = false;\n  static constexpr bool is_discontinuous = false;\n  static void apply(gsl::not_null<tnsr::I<ComplexDataVector, 2>*> flux,\n                    const Scalar<ComplexDataVector>& alpha,\n                    const Scalar<ComplexDataVector>& /*field*/,\n                    const tnsr::i<ComplexDataVector, 2>& field_gradient);\n  static void apply(gsl::not_null<tnsr::I<ComplexDataVector, 2>*> flux,\n                    const Scalar<ComplexDataVector>& alpha,\n                    const tnsr::i<DataVector, 2>& /*face_normal*/,\n                    const tnsr::I<DataVector, 2>& face_normal_vector,\n                    const Scalar<ComplexDataVector>& field);\n};\n\n/// Source terms for the scalar self-force system.\n/// \\see ScalarSelfForce::FirstOrderSystem\nstruct Sources {\n  using argument_tags = tmpl::list<Tags::Beta, Tags::Gamma>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(gsl::not_null<Scalar<ComplexDataVector>*> scalar_equation,\n                    const Scalar<ComplexDataVector>& beta,\n                    const tnsr::i<ComplexDataVector, 2>& gamma,\n                    const Scalar<ComplexDataVector>& field,\n                    const tnsr::i<ComplexDataVector, 2>& field_gradient,\n                    const tnsr::I<ComplexDataVector, 2>& flux);\n};\n\n/*!\n * \\brief Adds or subtracts the singular field to/from the received data on\n * element boundaries.\n *\n * In the regularized region we solve for the regularized field\n * \\begin{equation}\n *   \\Psi_m^R = \\Psi_m - \\Psi_m^P\n *   \\text{,}\n * \\end{equation}\n * so we subtract the singular field on the regularized side and add it on the\n * other side of the boundary. We do the same for the received normal dot flux\n * $n_i F^i$, but with an extra minus sign because this quantity is defined with\n * the face normal from the perspective of the sending element (see\n * `elliptic::protocols::FirstOrderSystem`).\n */\nstruct ModifyBoundaryData {\n private:\n  static constexpr size_t Dim = 2;\n  using singular_vars_on_mortars_tag =\n      ::Tags::Variables<tmpl::list<Tags::SingularField,\n                                   ::Tags::NormalDotFlux<Tags::SingularField>>>;\n\n public:\n  using argument_tags =\n      tmpl::list<Tags::FieldIsRegularized,\n                 ::Tags::Mortars<Tags::FieldIsRegularized, Dim>,\n                 ::Tags::Mortars<singular_vars_on_mortars_tag, Dim>>;\n  static void apply(\n      gsl::not_null<Scalar<ComplexDataVector>*> field,\n      gsl::not_null<Scalar<ComplexDataVector>*> n_dot_flux,\n      const DirectionalId<Dim>& mortar_id, bool field_is_regularized,\n      const DirectionalIdMap<Dim, bool>& neighbors_field_is_regularized,\n      const DirectionalIdMap<Dim, typename singular_vars_on_mortars_tag::type>&\n          singular_vars_on_mortars);\n};\n\n}  // namespace ScalarSelfForce\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarSelfForceEvents)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ObserveSelfForce.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ObserveSelfForce.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Interpolation\n  PUBLIC\n  DataStructures\n  Domain\n  Elliptic\n  Options\n  ScalarSelfForceAnalyticData\n  Serialization\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/Events/ObserveSelfForce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/SelfForce/Scalar/Events/ObserveSelfForce.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace ScalarSelfForce::Events::detail {\n\nstd::optional<tnsr::i<std::complex<double>, 2>> extract_self_force(\n    const Domain<2>& domain, const ElementId<2>& element_id,\n    const AnalyticData::CircularOrbit& circular_orbit,\n    const Scalar<ComplexDataVector>& field, const Mesh<2>& mesh,\n    const InverseJacobian<DataVector, 2, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian) {\n  // Get element-logical coords of puncture\n  const auto puncture_position = circular_orbit.puncture_position();\n  const auto& block = domain.blocks()[element_id.block_id()];\n  const auto block_logical_coords =\n      block_logical_coordinates_single_point(puncture_position, block);\n  if (not block_logical_coords.has_value()) {\n    return std::nullopt;\n  }\n  const auto puncture_logical_coords =\n      element_logical_coordinates(block_logical_coords.value(), element_id);\n  if (not puncture_logical_coords.has_value()) {\n    return std::nullopt;\n  }\n  // Interpolate field and its derivatives to puncture location\n  const auto deriv_field = partial_derivative(field, mesh, inv_jacobian);\n  const intrp::Irregular<2> interpolator(mesh, puncture_logical_coords.value());\n  ComplexDataVector intrp_result{1_st};\n  Scalar<std::complex<double>> field_at_puncture{};\n  interpolator.interpolate(make_not_null(&intrp_result), get(field));\n  get(field_at_puncture) = intrp_result[0];\n  tnsr::i<std::complex<double>, 2> deriv_field_at_puncture{};\n  interpolator.interpolate(make_not_null(&intrp_result), get<0>(deriv_field));\n  get<0>(deriv_field_at_puncture) = intrp_result[0];\n  // Assuming equatorial symmetry, so theta derivative must be zero\n  // (violated only by truncation error)\n  get<1>(deriv_field_at_puncture) = 0.;\n  // Calculate self-force in r and theta coordinates\n  tnsr::i<std::complex<double>, 2> self_force = deriv_field_at_puncture;\n  const double r0 = circular_orbit.orbital_radius();\n  const double M = circular_orbit.black_hole_mass();\n  const double spin = circular_orbit.black_hole_spin();\n  const double a = M * spin;\n  const int m_mode = circular_orbit.m_mode_number();\n  const double r_plus = M * (1. + sqrt(1. - square(spin)));\n  const double r_minus = M * (1. - sqrt(1. - square(spin)));\n  const double alpha = 1. - 2. * M * r0 / (square(r0) + square(a));\n  get<0>(self_force) /= r0 * alpha;\n  get<0>(self_force) -= get(field_at_puncture) / square(r0);\n  if (m_mode > 0) {\n    const double delta_phi =\n        m_mode * a / (r_plus - r_minus) * log((r0 - r_plus) / (r0 - r_minus));\n    const double delta_phi_dr = m_mode * a / (r0 - r_minus) / (r0 - r_plus);\n    get<0>(self_force) +=\n        std::complex<double>{0., delta_phi_dr / r0} * get(field_at_puncture);\n    const std::complex<double> rotation =\n        cos(delta_phi) + std::complex<double>(0., 1.) * sin(delta_phi);\n    get<0>(self_force) *= 2. * rotation;\n  }\n  return self_force;\n}\n\n}  // namespace ScalarSelfForce::Events::detail\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/Events/ObserveSelfForce.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/OptionalHelpers.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::Events {\n\nnamespace detail {\nstd::optional<tnsr::i<std::complex<double>, 2>> extract_self_force(\n    const Domain<2>& domain, const ElementId<2>& element_id,\n    const AnalyticData::CircularOrbit& circular_orbit,\n    const Scalar<ComplexDataVector>& field, const Mesh<2>& mesh,\n    const InverseJacobian<DataVector, 2, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian);\n}  // namespace detail\n\n/*!\n * \\brief Observe the self-force at the position of the scalar charge.\n *\n * \\details This event interpolates the scalar m-mode field and its derivatives\n * to the position of the scalar charge (puncture) and computes the m-mode\n * contribution to the self-force acting on the charge. The self-force is then\n * written to a reduction file. If multiple elements contain the puncture, their\n * contributions are averaged (they should only differ by truncation error).\n *\n * The self-force is computed in the Boyer-Lindquist `r` and `theta`\n * coordinates (for scalar charge $q=1$) following Eq. (3.10) in\n * \\cite Osburn:2022bby :\n * \\begin{align}\n *   F_r^m &= \\partial_r (\\Psi_m^R / r) = \\frac{1}{r \\alpha}\n *     \\partial_{r_*} \\Psi_m^R - \\frac{1}{r^2} \\Psi_m^R \\\\\n *   F_\\theta^m &= \\partial_\\theta (\\Psi_m^R / r) = -\\frac{1}{r}\n *     \\partial_{\\cos\\theta} \\Psi_m^R\n * \\end{align}\n * with $\\alpha = 1 - 2 M r / (r^2 + a^2)$. For modes $m > 0$ the self-force\n * includes an additional factor of 2 and a complex rotation by\n * $m \\Delta\\phi = \\frac{m a}{r_+ - r_-}\\ln\\frac{r - r_+}{r - r_-}$.\n *\n * These contributions can be summed over all m-modes (in postprocessing) to\n * form the total self-force $F_\\mu^\\mathrm{self} = \\sum_m F_\\mu^m$, where the\n * sum is truncated at some maximum m-mode number.\n */\ntemplate <typename ArraySectionIdTag = void>\nclass ObserveSelfForce : public Event {\n public:\n  using ReductionData = Parallel::ReductionData<\n      // Observation value\n      Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n      // Number of grid points\n      Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n      // Num contributing elements\n      Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n      // Re(SelfForce_r)\n      Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,\n                               std::index_sequence<2>>,\n      // Im(SelfForce_r)\n      Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,\n                               std::index_sequence<2>>,\n      // Re(SelfForce_theta)\n      Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,\n                               std::index_sequence<2>>,\n      // Im(SelfForce_theta)\n      Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,\n                               std::index_sequence<2>>>;\n\n  static constexpr Options::String help =\n      \"Observe the self-force at the position of the scalar charge.\";\n  using options = tmpl::list<>;\n\n  ObserveSelfForce() = default;\n\n  explicit ObserveSelfForce(CkMigrateMessage* msg) : Event(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveSelfForce);  // NOLINT\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using observed_reduction_data_tags =\n      observers::make_reduction_data_tags<tmpl::list<ReductionData>>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::ObservationBox>;\n\n  using BackgroundTag =\n      elliptic::Tags::Background<elliptic::analytic_data::Background>;\n\n  template <typename ComputeTagsList, typename DataBoxType,\n            typename Metavariables, typename ParallelComponent>\n  void operator()(const ObservationBox<ComputeTagsList, DataBoxType>& box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<2>& element_id,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    // Skip observation on elements that are not part of the section\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (not section_observation_key.has_value()) {\n      return;\n    }\n    // Extract self-force\n    const auto& background = get<BackgroundTag>(box);\n    const auto& circular_orbit =\n        dynamic_cast<const AnalyticData::CircularOrbit&>(background);\n    const auto& mesh = get<domain::Tags::Mesh<2>>(box);\n    const size_t num_points = mesh.number_of_grid_points();\n    const auto self_force = detail::extract_self_force(\n        get<domain::Tags::Domain<2>>(box), element_id, circular_orbit,\n        get<Tags::MMode>(box), mesh,\n        get<domain::Tags::InverseJacobian<2, Frame::ElementLogical,\n                                          Frame::Inertial>>(box));\n    // Send data to reduction observer\n    auto& local_observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            tmpl::conditional_t<Parallel::is_nodegroup_v<ParallelComponent>,\n                                observers::ObserverWriter<Metavariables>,\n                                observers::Observer<Metavariables>>>(cache));\n    const std::string subfile_name = \"SelfForce\";\n    observers::ObservationId observation_id{observation_value.value,\n                                            subfile_name + \".dat\"};\n    Parallel::ArrayComponentId array_component_id{\n        std::add_pointer_t<ParallelComponent>{nullptr},\n        Parallel::ArrayIndex<ElementId<2>>(element_id)};\n    ReductionData reduction_data =\n        self_force.has_value()\n            ? ReductionData{observation_value.value,\n                            num_points,\n                            1_st,\n                            get<0>(*self_force).real(),\n                            get<0>(*self_force).imag(),\n                            get<1>(*self_force).real(),\n                            get<1>(*self_force).imag()}\n            : ReductionData{\n                  observation_value.value, num_points, 0_st, 0., 0., 0., 0.};\n    std::vector<std::string> legend{\n        \"IterationId\",        \"NumGridPoints\",   \"NumContributingElements\",\n        \"Re(SelfForce_r)\",    \"Im(SelfForce_r)\", \"Re(SelfForce_theta)\",\n        \"Im(SelfForce_theta)\"};\n    if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {\n      Parallel::threaded_action<\n          observers::ThreadedActions::CollectReductionDataOnNode>(\n          local_observer, std::move(observation_id),\n          std::move(array_component_id), subfile_name + \".dat\",\n          std::move(legend), std::move(reduction_data));\n    } else {\n      Parallel::simple_action<observers::Actions::ContributeReductionData>(\n          local_observer, std::move(observation_id),\n          std::move(array_component_id), subfile_name + \".dat\",\n          std::move(legend), std::move(reduction_data));\n    }\n  }\n\n  using observation_registration_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList>\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration(\n      const db::DataBox<DbTagsList>& box) const {\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (not section_observation_key.has_value()) {\n      return std::nullopt;\n    }\n    return {{observers::TypeOfObservation::Reduction,\n             observers::ObservationKey(\n                 \"SelfForce\" + section_observation_key.value() + \".dat\")}};\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n};\n\n/// \\cond\n// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)\ntemplate <typename ArraySectionIdTag>\nPUP::able::PUP_ID ObserveSelfForce<ArraySectionIdTag>::my_PUP_ID = 0;\n// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)\n/// \\endcond\n}  // namespace ScalarSelfForce::Events\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Equations.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce {\n\n/*!\n * \\brief Self-force of a scalar charge on a Kerr background.\n *\n * In this formulation we solve a 2D elliptic equation for the m-mode field\n * $\\Psi_m$, following \\cite Osburn:2022bby . The two dimensions are a radial\n * and angular coordinate, specifically the tortoise radius $r_\\star$ and\n * $\\cos\\theta$ (note that \\cite Osburn:2022bby uses $\\theta$).\n *\n * We write the equations in generic first-order flux form\n * \\begin{equation}\n * -\\Delta_m \\Psi_m = -\\partial_i F^i + \\beta \\Psi_m + \\gamma_i \\partial_i\n * \\Psi_m = 0\n * \\end{equation}\n * with the flux\n * \\begin{equation}\n * F^i = \\{\\partial_{r_\\star}, \\alpha \\partial_{\\cos\\theta}\\} \\Psi_m\n * \\text{,}\n * \\end{equation}\n * where $\\alpha$, $\\beta$, and $\\gamma_i$ are coefficients that define the\n * elliptic equations. The particular coefficients that match Eq. (2.9) of\n * \\cite Osburn:2022bby for a circular equatorial orbit in Kerr are implemented\n * in `ScalarSelfForce::AnalyticData::CircularOrbit`.\n *\n * \\par Regularization and modified boundary data\n * As described in \\cite Osburn:2022bby Sec. III, the field $\\Psi_m$ is singular\n * at the location of the scalar point charge (\"puncture\"). Therefore, in a\n * region near the puncture we split the field into a regular and a singular\n * part,\n * \\begin{equation}\n *   \\Psi_m = \\Psi_m^R + \\Psi_m^P\n *   \\text{,}\n * \\end{equation}\n * where $\\Psi_m^P$ is the singular part that we can (approximately) compute\n * analytically and $\\Psi_m^R$ is the regular part that we solve for. Therefore,\n * we need to transform variables between the full field $\\Psi_m$ and the\n * regularized field $\\Psi_m^R$ when data moves across the boundary of the\n * regularized region. We do this at element boundaries using the\n * `modify_boundary_data` mechanism detailed in\n * `elliptic::protocols::FirstOrderSystem` by just adding or subtracting the\n * singular field to/from the received data (see also\n * `ScalarSelfForce::ModifyBoundaryData`). In this regularized region the\n * equations transform to\n * \\begin{equation}\n * -\\Delta_m \\Psi_m^R = \\Delta_m \\Psi_m^P = S_m^\\mathrm{eff}\n * \\text{,}\n * \\end{equation}\n * so they gain an effective source $S_m^\\mathrm{eff}$ from the singular field\n * $\\Psi_m^P$. The singular field and the effective source for a circular\n * equatorial orbit in Kerr is implemented in\n * `ScalarSelfForce::AnalyticData::CircularOrbit`.\n */\nstruct FirstOrderSystem\n    : tt::ConformsTo<elliptic::protocols::FirstOrderSystem> {\n  static constexpr size_t volume_dim = 2;\n\n  using primal_fields = tmpl::list<Tags::MMode>;\n  using primal_fluxes =\n      tmpl::list<::Tags::Flux<Tags::MMode, tmpl::size_t<2>, Frame::Inertial>>;\n\n  using background_fields = tmpl::list<Tags::Alpha, Tags::Beta, Tags::Gamma>;\n  using inv_metric_tag = void;\n\n  using fluxes_computer = Fluxes;\n  using sources_computer = Sources;\n  using modify_boundary_data = ModifyBoundaryData;\n\n  using boundary_conditions_base =\n      elliptic::BoundaryConditions::BoundaryCondition<2>;\n};\n\n}  // namespace ScalarSelfForce\n"
  },
  {
    "path": "src/Elliptic/Systems/SelfForce/Scalar/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Structure/BlockGroups.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\nclass DataVector;\n/// \\endcond\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving the self force of a scalar charge on a Kerr\n * background.\n *\n * \\see ScalarSelfForce::FirstOrderSystem\n */\nnamespace ScalarSelfForce {\n\nnamespace OptionTags {\n\nstruct OptionGroup {\n  static std::string name() { return \"ScalarSelfForce\"; }\n  static constexpr Options::String help =\n      \"Options for the scalar self-force system\";\n};\n\nstruct NullSlicingBlocks {\n  using group = OptionGroup;\n  using type = std::vector<std::string>;\n  static constexpr Options::String help =\n      \"The blocks in which to use null slicing (vtu-slicing).\";\n};\n\n}  // namespace OptionTags\n\n/// Tags for the ScalarSelfForce system.\nnamespace Tags {\n\n/*!\n * \\brief The complex m-mode field $\\Psi_m$.\n *\n * Defined by the m-mode decomposition of the complex scalar field $\\Phi$\n * [Eq. (2.6) in \\cite Osburn:2022bby ]:\n *\n * \\begin{equation}\n * \\Phi(t,r,\\theta,\\phi) = \\frac{1}{r} \\sum_{m=-\\infty}^{\\infty}\n *   \\Psi_m(r,\\theta) e^{im\\Delta\\phi(r)} e^{im(\\phi - \\Omega t)}\n * \\end{equation}\n *\n * where $\\Delta\\phi(r) = \\frac{a}{r_\\plus - r_\\minus}\n * \\ln(\\frac{r-r_\\plus}{r-r_\\minus})$.\n */\nstruct MMode : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n};\n\n/*!\n * \\brief The factor multiplying the angular derivative in the principal part of\n * the equations.\n *\n * This is the factor $\\alpha$ that defines the principal part of the equations\n * and allows to write it in first-order flux form given by\n * \\begin{equation}\n * -\\partial_i F^i + \\beta \\Psi_m + \\gamma_i \\partial_i \\Psi_m = S_m\n * \\end{equation}\n * with the flux\n * \\begin{equation}\n * F^i = \\{\\partial_{r_\\star}, \\alpha \\partial_{\\cos\\theta}\\} \\Psi_m\n * \\text{.}\n * \\end{equation}\n * This factor is set by the analytic data class (see\n * `ScalarSelfForce::AnalyticData::CircularOrbit`).\n */\nstruct Alpha : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n};\n\n/*!\n * \\brief The factor multiplying the non-derivative terms in the equations.\n *\n * This is the factor $\\beta$ in the general form of the equations\n * \\begin{equation}\n * -\\partial_i F^i + \\beta \\Psi_m + \\gamma_i \\partial_i \\Psi_m = S_m\n * \\text{.}\n * \\end{equation}\n * This factor is set by the analytic data class (see\n * `ScalarSelfForce::AnalyticData::CircularOrbit`).\n */\nstruct Beta : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n};\n\n/*!\n * \\brief The factor multiplying the first-derivative terms in the equations.\n *\n * This is the factor $\\gamma_i$ in the general form of the equations\n * \\begin{equation}\n * -\\partial_i F^i + \\beta \\Psi_m + \\gamma_i \\partial_i \\Psi_m = S_m\n * \\text{.}\n * \\end{equation}\n * This factor is set by the analytic data class (see\n * `ScalarSelfForce::AnalyticData::CircularOrbit`).\n */\nstruct Gamma : db::SimpleTag {\n  using type = tnsr::i<ComplexDataVector, 2>;\n};\n\n/*!\n * \\brief A flag indicating that we are solving for the regularized field in\n * this element.\n *\n * In elements at and around the scalar point charge we use the effective source\n * approach to split the m-mode field into a singular and a regular field\n * [Eq. (3.6) in \\cite Osburn:2022bby ]:\n * \\begin{equation}\n * \\Psi_m = \\Psi_m^\\mathcal{P} + \\Psi_m^\\mathcal{R}\n * \\text{.}\n * \\end{equation}\n * In these elements, we solve for $\\Psi_m^\\mathcal{R}$ rather than $\\Psi_m$.\n */\nstruct FieldIsRegularized : db::SimpleTag {\n  using type = bool;\n};\n\n/*!\n * \\brief The singular field $\\Psi_m^\\mathcal{P}$.\n *\n * Only defined where `FieldIsRegularized` is true.\n */\nstruct SingularField : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n};\n\n/*!\n * \\brief The Boyer-Lindquist radius $r$.\n *\n * Computed numerically from the tortoise radial coordinate $r_\\star$\n * [see Eq. (2.12) in \\cite Osburn:2022bby ].\n */\nstruct BoyerLindquistRadius : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Blocks in which we use null slicing (vtu-slicing).\n */\nstruct NullSlicingBlocks : db::SimpleTag {\n  using type = std::vector<size_t>;\n  using option_tags = tmpl::list<OptionTags::NullSlicingBlocks,\n                                 domain::OptionTags::DomainCreator<2>>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const std::vector<std::string>& null_slicing_blocks,\n      const std::unique_ptr<DomainCreator<2>>& domain_creator) {\n    return domain::block_ids_from_names(null_slicing_blocks,\n                                        domain_creator->block_names(),\n                                        domain_creator->block_groups());\n  }\n};\n\n/*!\n * \\brief The hyperboloidal boost function $H(r_*)$.\n */\nstruct BoostFunction : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n};\n\n/*!\n * \\brief The radial derivative of the boost function, $H'(r_*)$.\n */\nstruct BoostFunctionDeriv : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n};\n\n}  // namespace Tags\n}  // namespace ScalarSelfForce\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/BoundaryConditions/ApparentHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/ApparentHorizon.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/Binary.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace Xcts::BoundaryConditions {\n\ntemplate <Xcts::Geometry ConformalGeometry>\nApparentHorizon<ConformalGeometry>::ApparentHorizon(\n    std::array<double, 3> center, std::array<double, 3> rotation,\n    std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>\n        solution_for_lapse,\n    std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>\n        solution_for_negative_expansion,\n    const Options::Context& /*context*/)\n    : center_(center),\n      rotation_(rotation),\n      // NOLINTNEXTLINE(performance-move-const-arg)\n      solution_for_lapse_(std::move(solution_for_lapse)),\n      solution_for_negative_expansion_(\n          // NOLINTNEXTLINE(performance-move-const-arg)\n          std::move(solution_for_negative_expansion)) {}\n\nnamespace {\n// This sets the output buffer to: \\bar{m}^{ij} \\bar{\\nabla}_i n_j\nvoid normal_gradient_term_flat_cartesian(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n    const Scalar<DataVector>& face_normal_magnitude) {\n  // Write directly into the output buffer\n  DataVector& projected_normal_gradient = get(*n_dot_conformal_factor_gradient);\n  projected_normal_gradient = (1. - square(get<0>(face_normal))) *\n                                  get<0, 0>(deriv_unnormalized_face_normal) -\n                              get<0>(face_normal) * get<1>(face_normal) *\n                                  get<0, 1>(deriv_unnormalized_face_normal) -\n                              get<0>(face_normal) * get<2>(face_normal) *\n                                  get<0, 2>(deriv_unnormalized_face_normal);\n  for (size_t i = 1; i < 3; ++i) {\n    projected_normal_gradient += deriv_unnormalized_face_normal.get(i, i);\n    for (size_t j = 0; j < 3; ++j) {\n      projected_normal_gradient -= face_normal.get(i) * face_normal.get(j) *\n                                   deriv_unnormalized_face_normal.get(i, j);\n    }\n  }\n  projected_normal_gradient /= get(face_normal_magnitude);\n}\n\n// This sets the output buffer to: \\bar{m}^{ij} \\bar{\\nabla}_i n_j\nvoid normal_gradient_term_curved(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<DataVector*> temp_buffer,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::I<DataVector, 3>& face_normal_raised,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n    const Scalar<DataVector>& face_normal_magnitude,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) {\n  // Write directly into the output buffer\n  DataVector& projected_normal_gradient = get(*n_dot_conformal_factor_gradient);\n  DataVector& projection = *temp_buffer;\n  // Possible performance optimization: unroll the first iteration of the loop\n  // to avoid zeroing the buffer. It's very verbose to do that though.\n  *projected_normal_gradient = 0.;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      projection = inv_conformal_metric.get(i, j) -\n                   face_normal_raised.get(i) * face_normal_raised.get(j);\n      projected_normal_gradient += projection *\n                                   deriv_unnormalized_face_normal.get(i, j) /\n                                   get(face_normal_magnitude);\n      for (size_t k = 0; k < 3; ++k) {\n        projected_normal_gradient -=\n            projection * face_normal.get(k) *\n            conformal_christoffel_second_kind.get(k, i, j);\n      }\n    }\n  }\n}\n\n// Compute the expansion Theta = m^ij (D_i s_j - K_ij) and the shift-correction\n// epsilon = shift_orthogonal - lapse of an analytic solution\nvoid negative_expansion_quantities(\n    const gsl::not_null<Scalar<DataVector>*> expansion,\n    const gsl::not_null<Scalar<DataVector>*> beta_orthogonal_correction,\n    const std::unique_ptr<elliptic::analytic_data::InitialGuess>& solution,\n    const tnsr::I<DataVector, 3>& x,\n    const tnsr::i<DataVector, 3>& conformal_face_normal,\n    const Scalar<DataVector>& unnormalized_conformal_face_normal_magnitude,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal) {\n  using analytic_tags =\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>>;\n  const auto solution_vars =\n      call_with_dynamic_type<tuples::tagged_tuple_from_typelist<analytic_tags>,\n                             Xcts::Solutions::all_analytic_solutions>(\n          solution.get(), [&x](const auto* const local_solution) {\n            return local_solution->variables(x, analytic_tags{});\n          });\n  const auto& inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(solution_vars);\n  const auto& deriv_spatial_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(solution_vars);\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(solution_vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(solution_vars);\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(solution_vars);\n  // The passed-in face normal is normalized with the full conformal metric\n  // (typically a superposition of isolated metrics). Therefore, we need to\n  // normalized the face normal again with the isolated Kerr metric. Possible\n  // optimization if this turns out to have a significant computational cost:\n  // We could just re-use the face normal and the projected normal gradient\n  // from the full conformal metric, since it's probably sufficiently close to\n  // the isolated Kerr metric. However, the difference between the two metric\n  // means that the expansion is not exactly zero when the excision surface\n  // coincides with the isolated Kerr horizon, which users would probably\n  // expect.\n  TempBuffer<tmpl::list<::Tags::Tempi<0, 3>, ::Tags::TempScalar<1>,\n                        ::Tags::TempI<2, 3>, ::Tags::TempIjj<3, 3>>>\n      buffer{x.begin()->size()};\n  auto& face_normal = get<::Tags::Tempi<0, 3>>(buffer);\n  auto& face_normal_magnitude = get<::Tags::TempScalar<1>>(buffer);\n  auto& face_normal_raised = get<::Tags::TempI<2, 3>>(buffer);\n  auto& spatial_christoffel_second_kind = get<::Tags::TempIjj<3, 3>>(buffer);\n  magnitude(make_not_null(&face_normal_magnitude), conformal_face_normal,\n            inv_spatial_metric);\n  face_normal = conformal_face_normal;\n  for (size_t d = 0; d < 3; ++d) {\n    face_normal.get(d) /= get(face_normal_magnitude);\n  }\n  get(face_normal_magnitude) *=\n      get(unnormalized_conformal_face_normal_magnitude);\n  raise_or_lower_index(make_not_null(&face_normal_raised), face_normal,\n                       inv_spatial_metric);\n  gr::christoffel_second_kind(make_not_null(&spatial_christoffel_second_kind),\n                              deriv_spatial_metric, inv_spatial_metric);\n  // Compute Theta = m^ij (D_i s_j - K_ij)\n  // Use second output buffer as temporary memory\n  DataVector& projection = get(*beta_orthogonal_correction);\n  get(*expansion) = 0.;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      projection = inv_spatial_metric.get(i, j) -\n                   face_normal_raised.get(i) * face_normal_raised.get(j);\n      // This first part is the same as `normal_gradient_term_curved` above\n      get(*expansion) += projection * deriv_unnormalized_face_normal.get(i, j) /\n                         get(face_normal_magnitude);\n      for (size_t k = 0; k < 3; ++k) {\n        get(*expansion) -= projection * face_normal.get(k) *\n                           spatial_christoffel_second_kind.get(k, i, j);\n      }\n      // Additional extrinsic-curvature term\n      get(*expansion) += projection * extrinsic_curvature.get(i, j);\n    }\n  }\n  get(*expansion) *= -1.;\n  // Compute epsilon = shift_orthogonal - lapse\n  normal_dot_flux(beta_orthogonal_correction, face_normal, shift);\n  get(*beta_orthogonal_correction) *= -1.;\n  get(*beta_orthogonal_correction) -= get(lapse);\n}\n\ntemplate <Xcts::Geometry ConformalGeometry>\nvoid apparent_horizon_impl(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        n_dot_longitudinal_shift_excess,\n    const std::array<double, 3>& center, const std::array<double, 3>& rotation,\n    const std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>&\n        solution_for_lapse,\n    const std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>&\n        solution_for_negative_expansion,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n    const Scalar<DataVector>& face_normal_magnitude,\n    const tnsr::I<DataVector, 3>& x_offcenter,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::I<DataVector, 3>& shift_background,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n    [[maybe_unused]] const std::optional<\n        std::reference_wrapper<const tnsr::II<DataVector, 3>>>\n        inv_conformal_metric,\n    [[maybe_unused]] const std::optional<\n        std::reference_wrapper<const tnsr::Ijj<DataVector, 3>>>\n        conformal_christoffel_second_kind) {\n  // Allocate some temporary memory\n  TempBuffer<tmpl::list<::Tags::TempI<0, 3>, ::Tags::TempScalar<1>,\n                        ::Tags::TempI<2, 3>, ::Tags::TempScalar<3>>>\n      buffer{face_normal.begin()->size()};\n  // Center the coordinates\n  tnsr::I<DataVector, 3>& x = get<::Tags::TempI<2, 3>>(buffer);\n  x = x_offcenter;\n  get<0>(x) -= center[0];\n  get<1>(x) -= center[1];\n  get<2>(x) -= center[2];\n  // Note that the face normal points _out_ of the computational domain, i.e.\n  // _into_ the excised region. It is opposite the conformal unit normal to the\n  // horizon surface: \\bar{s}_i = -n_i.\n  tnsr::I<DataVector, 3>& face_normal_raised = get<::Tags::TempI<0, 3>>(buffer);\n  if constexpr (ConformalGeometry == Xcts::Geometry::FlatCartesian) {\n    get<0>(face_normal_raised) = get<0>(face_normal);\n    get<1>(face_normal_raised) = get<1>(face_normal);\n    get<2>(face_normal_raised) = get<2>(face_normal);\n  } else {\n    raise_or_lower_index(make_not_null(&face_normal_raised), face_normal,\n                         inv_conformal_metric->get());\n  }\n\n  // Compute quantities for negative-expansion boundary conditions. We collect\n  // all calls into the analytic solution in one place so it doesn't have to\n  // compute intermediate quantities multiple times. Possible optimization:\n  // Store the result in the DataBox and use it in repeated applications of the\n  // boundary conditions.\n  Scalar<DataVector>& expansion_of_solution =\n      get<::Tags::TempScalar<3>>(buffer);\n  Scalar<DataVector>& beta_orthogonal = get<::Tags::TempScalar<1>>(buffer);\n  if (solution_for_negative_expansion.has_value()) {\n    negative_expansion_quantities(\n        make_not_null(&expansion_of_solution), make_not_null(&beta_orthogonal),\n        *solution_for_negative_expansion, x, face_normal, face_normal_magnitude,\n        deriv_unnormalized_face_normal);\n    ASSERT(max(get(expansion_of_solution)) <\n               100.0 * std::numeric_limits<double>::epsilon(),\n           \"Expansion of solution is not negative everywhere.\");\n  }\n\n  // Shift\n  {\n    if (solution_for_negative_expansion.has_value()) {\n      get(beta_orthogonal) /= square(get(*conformal_factor_minus_one) + 1.);\n    } else {\n      get(beta_orthogonal) = 0.;\n    }\n    get(beta_orthogonal) +=\n        (get(*lapse_times_conformal_factor_minus_one) + 1.) /\n        cube(get(*conformal_factor_minus_one) + 1.);\n    for (size_t i = 0; i < 3; ++i) {\n      shift_excess->get(i) = -get(beta_orthogonal) * face_normal_raised.get(i) -\n                             shift_background.get(i);\n    }\n  }\n  // At this point we're done with `beta_orthogonal`, so we can re-purpose the\n  // memory buffer.\n  for (LeviCivitaIterator<3> it; it; ++it) {\n    shift_excess->get(it[0]) +=\n        it.sign() * gsl::at(rotation, it[1]) * x.get(it[2]);\n  }\n\n  // Conformal factor\n  if constexpr (ConformalGeometry == Xcts::Geometry::FlatCartesian) {\n    normal_gradient_term_flat_cartesian(\n        n_dot_conformal_factor_gradient, face_normal,\n        deriv_unnormalized_face_normal, face_normal_magnitude);\n  } else {\n    normal_gradient_term_curved(\n        n_dot_conformal_factor_gradient,\n        make_not_null(&get(get<::Tags::TempScalar<1>>(buffer))), face_normal,\n        face_normal_raised, deriv_unnormalized_face_normal,\n        face_normal_magnitude, *inv_conformal_metric,\n        *conformal_christoffel_second_kind);\n  }\n  // At this point we're done with `face_normal_raised`, so we can re-purpose\n  // the memory buffer.\n  get(*n_dot_conformal_factor_gradient) *=\n      -0.25 * (get(*conformal_factor_minus_one) + 1.);\n  if (solution_for_negative_expansion.has_value()) {\n    get(*n_dot_conformal_factor_gradient) -=\n        0.25 * cube(get(*conformal_factor_minus_one) + 1.) *\n        get(expansion_of_solution);\n  }\n  {\n    tnsr::I<DataVector, 3>& n_dot_longitudinal_shift =\n        get<::Tags::TempI<0, 3>>(buffer);\n    normal_dot_flux(make_not_null(&n_dot_longitudinal_shift), face_normal,\n                    longitudinal_shift_background);\n    for (size_t i = 0; i < 3; ++i) {\n      n_dot_longitudinal_shift.get(i) +=\n          n_dot_longitudinal_shift_excess->get(i);\n    }\n    Scalar<DataVector>& nn_dot_longitudinal_shift =\n        get<::Tags::TempScalar<1>>(buffer);\n    normal_dot_flux(make_not_null(&nn_dot_longitudinal_shift), face_normal,\n                    n_dot_longitudinal_shift);\n    get(*n_dot_conformal_factor_gradient) +=\n        -get(extrinsic_curvature_trace) *\n            cube(get(*conformal_factor_minus_one) + 1.) / 6. +\n        pow<4>(get(*conformal_factor_minus_one) + 1.) / 8. /\n            (get(*lapse_times_conformal_factor_minus_one) + 1.) *\n            get(nn_dot_longitudinal_shift);\n  }\n\n  // Lapse\n  if (solution_for_lapse.has_value()) {\n    using Binary =\n        Xcts::AnalyticData::Binary<elliptic::analytic_data::AnalyticSolution,\n                                   Xcts::Solutions::all_analytic_solutions>;\n    // Evaluate isolated solutions at coordinates centered on the excision, but\n    // evaluate the superposed binary data at the original coordinates\n    const bool evaluate_centered =\n        dynamic_cast<const Binary*>(solution_for_lapse.value().get()) ==\n        nullptr;\n    *lapse_times_conformal_factor_minus_one = call_with_dynamic_type<\n        Scalar<DataVector>,\n        tmpl::push_back<Xcts::Solutions::all_analytic_solutions, Binary>>(\n        solution_for_lapse.value().get(),\n        [&evaluate_centered, &x,\n         &x_offcenter](const auto* const local_solution) {\n          return get<Xcts::Tags::LapseTimesConformalFactorMinusOne<DataVector>>(\n              local_solution->variables(\n                  evaluate_centered ? x : x_offcenter,\n                  tmpl::list<Xcts::Tags::LapseTimesConformalFactorMinusOne<\n                      DataVector>>{}));\n        });\n  } else {\n    get(*n_dot_lapse_times_conformal_factor_gradient) = 0.;\n  }\n}\n\ntemplate <Xcts::Geometry ConformalGeometry>\nvoid linearized_apparent_horizon_impl(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_conformal_factor_gradient_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        n_dot_longitudinal_shift_correction,\n    const std::array<double, 3>& center,\n    const std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>&\n        solution_for_lapse,\n    const std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>&\n        solution_for_negative_expansion,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n    const Scalar<DataVector>& face_normal_magnitude,\n    const tnsr::I<DataVector, 3>& x_offcenter,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess,\n    [[maybe_unused]] const std::optional<\n        std::reference_wrapper<const tnsr::II<DataVector, 3>>>\n        inv_conformal_metric,\n    [[maybe_unused]] const std::optional<\n        std::reference_wrapper<const tnsr::Ijj<DataVector, 3>>>\n        conformal_christoffel_second_kind) {\n  // Allocate some temporary memory\n  TempBuffer<tmpl::list<::Tags::TempI<0, 3>, ::Tags::TempScalar<1>,\n                        ::Tags::TempScalar<2>>>\n      buffer{face_normal.begin()->size()};\n\n  // Negative-expansion quantities\n  Scalar<DataVector>& expansion_of_solution =\n      get<::Tags::TempScalar<2>>(buffer);\n  Scalar<DataVector>& beta_orthogonal_correction =\n      get<::Tags::TempScalar<1>>(buffer);\n  if (solution_for_negative_expansion.has_value()) {\n    // Center the coordinates\n    tnsr::I<DataVector, 3>& x = get<::Tags::TempI<0, 3>>(buffer);\n    x = x_offcenter;\n    get<0>(x) -= center[0];\n    get<1>(x) -= center[1];\n    get<2>(x) -= center[2];\n    negative_expansion_quantities(make_not_null(&expansion_of_solution),\n                                  make_not_null(&beta_orthogonal_correction),\n                                  *solution_for_negative_expansion, x,\n                                  face_normal, face_normal_magnitude,\n                                  deriv_unnormalized_face_normal);\n  }\n\n  tnsr::I<DataVector, 3>& face_normal_raised = get<::Tags::TempI<0, 3>>(buffer);\n  if constexpr (ConformalGeometry == Xcts::Geometry::FlatCartesian) {\n    get<0>(face_normal_raised) = get<0>(face_normal);\n    get<1>(face_normal_raised) = get<1>(face_normal);\n    get<2>(face_normal_raised) = get<2>(face_normal);\n  } else {\n    raise_or_lower_index(make_not_null(&face_normal_raised), face_normal,\n                         inv_conformal_metric->get());\n  }\n\n  // Shift\n  {\n    if (solution_for_negative_expansion.has_value()) {\n      get(beta_orthogonal_correction) *=\n          -2. * get(*conformal_factor_correction) /\n          cube(get(conformal_factor_minus_one) + 1.);\n    } else {\n      get(beta_orthogonal_correction) = 0.;\n    }\n    get(beta_orthogonal_correction) +=\n        get(*lapse_times_conformal_factor_correction) /\n            cube(get(conformal_factor_minus_one) + 1.) -\n        3. * (get(lapse_times_conformal_factor_minus_one) + 1.) /\n            pow<4>(get(conformal_factor_minus_one) + 1.) *\n            get(*conformal_factor_correction);\n    for (size_t i = 0; i < 3; ++i) {\n      shift_correction->get(i) =\n          -get(beta_orthogonal_correction) * face_normal_raised.get(i);\n    }\n  }\n  // At this point we're done with `beta_orthogonal_correction`, so we can\n  // re-purpose the memory buffer.\n\n  // Conformal factor\n  if constexpr (ConformalGeometry == Xcts::Geometry::FlatCartesian) {\n    normal_gradient_term_flat_cartesian(\n        n_dot_conformal_factor_gradient_correction, face_normal,\n        deriv_unnormalized_face_normal, face_normal_magnitude);\n  } else {\n    normal_gradient_term_curved(\n        n_dot_conformal_factor_gradient_correction,\n        make_not_null(&get(get<::Tags::TempScalar<1>>(buffer))), face_normal,\n        face_normal_raised, deriv_unnormalized_face_normal,\n        face_normal_magnitude, *inv_conformal_metric,\n        *conformal_christoffel_second_kind);\n  }\n  // At this point we're done with `face_normal_raised`, so we can re-purpose\n  // the memory buffer.\n  get(*n_dot_conformal_factor_gradient_correction) *=\n      -0.25 * get(*conformal_factor_correction);\n  if (solution_for_negative_expansion.has_value()) {\n    get(*n_dot_conformal_factor_gradient_correction) -=\n        0.75 * square(get(conformal_factor_minus_one) + 1.) *\n        get(expansion_of_solution) * get(*conformal_factor_correction);\n  }\n  {\n    tnsr::I<DataVector, 3>& n_dot_longitudinal_shift =\n        get<::Tags::TempI<0, 3>>(buffer);\n    normal_dot_flux(make_not_null(&n_dot_longitudinal_shift), face_normal,\n                    longitudinal_shift_background);\n    for (size_t i = 0; i < 3; ++i) {\n      n_dot_longitudinal_shift.get(i) += n_dot_longitudinal_shift_excess.get(i);\n    }\n    Scalar<DataVector>& nn_dot_longitudinal_shift =\n        get<::Tags::TempScalar<1>>(buffer);\n    normal_dot_flux(make_not_null(&nn_dot_longitudinal_shift), face_normal,\n                    n_dot_longitudinal_shift);\n    get(*n_dot_conformal_factor_gradient_correction) +=\n        -0.5 * get(extrinsic_curvature_trace) *\n            square(get(conformal_factor_minus_one) + 1.) *\n            get(*conformal_factor_correction) +\n        0.5 * pow<3>(get(conformal_factor_minus_one) + 1.) /\n            (get(lapse_times_conformal_factor_minus_one) + 1.) *\n            get(nn_dot_longitudinal_shift) * get(*conformal_factor_correction) -\n        0.125 * pow<4>(get(conformal_factor_minus_one) + 1.) /\n            square(get(lapse_times_conformal_factor_minus_one) + 1.) *\n            get(nn_dot_longitudinal_shift) *\n            get(*lapse_times_conformal_factor_correction);\n  }\n  {\n    Scalar<DataVector>& nn_dot_longitudinal_shift_correction =\n        get<::Tags::TempScalar<1>>(buffer);\n    normal_dot_flux(make_not_null(&nn_dot_longitudinal_shift_correction),\n                    face_normal, *n_dot_longitudinal_shift_correction);\n    get(*n_dot_conformal_factor_gradient_correction) +=\n        0.125 * pow<4>(get(conformal_factor_minus_one) + 1.) /\n        (get(lapse_times_conformal_factor_minus_one) + 1.) *\n        get(nn_dot_longitudinal_shift_correction);\n  }\n\n  // Lapse\n  if (solution_for_lapse.has_value()) {\n    get(*lapse_times_conformal_factor_correction) = 0.;\n  } else {\n    get(*n_dot_lapse_times_conformal_factor_gradient_correction) = 0.;\n  }\n}\n}  // namespace\n\ntemplate <Xcts::Geometry ConformalGeometry>\nvoid ApparentHorizon<ConformalGeometry>::apply(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        n_dot_longitudinal_shift_excess,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n    const tnsr::i<DataVector, 3>& /*deriv_lapse_times_conformal_factor*/,\n    const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess*/,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n    const Scalar<DataVector>& face_normal_magnitude,\n    const tnsr::I<DataVector, 3>& x,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::I<DataVector, 3>& shift_background,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background) const {\n  apparent_horizon_impl<ConformalGeometry>(\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      shift_excess, n_dot_conformal_factor_gradient,\n      n_dot_lapse_times_conformal_factor_gradient,\n      n_dot_longitudinal_shift_excess, center_, rotation_, solution_for_lapse_,\n      solution_for_negative_expansion_, face_normal,\n      deriv_unnormalized_face_normal, face_normal_magnitude, x,\n      extrinsic_curvature_trace, shift_background,\n      longitudinal_shift_background, std::nullopt, std::nullopt);\n}\n\ntemplate <Xcts::Geometry ConformalGeometry>\nvoid ApparentHorizon<ConformalGeometry>::apply(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        n_dot_longitudinal_shift_excess,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n    const tnsr::i<DataVector, 3>& /*deriv_lapse_times_conformal_factor*/,\n    const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess*/,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n    const Scalar<DataVector>& face_normal_magnitude,\n    const tnsr::I<DataVector, 3>& x,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::I<DataVector, 3>& shift_background,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) const {\n  apparent_horizon_impl<ConformalGeometry>(\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      shift_excess, n_dot_conformal_factor_gradient,\n      n_dot_lapse_times_conformal_factor_gradient,\n      n_dot_longitudinal_shift_excess, center_, rotation_, solution_for_lapse_,\n      solution_for_negative_expansion_, face_normal,\n      deriv_unnormalized_face_normal, face_normal_magnitude, x,\n      extrinsic_curvature_trace, shift_background,\n      longitudinal_shift_background, inv_conformal_metric,\n      conformal_christoffel_second_kind);\n}\n\ntemplate <Xcts::Geometry ConformalGeometry>\nvoid ApparentHorizon<ConformalGeometry>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_conformal_factor_gradient_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        n_dot_longitudinal_shift_excess_correction,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n    const tnsr::i<DataVector,\n                  3>& /*deriv_lapse_times_conformal_factor_correction*/,\n    const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess_correction*/,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n    const Scalar<DataVector>& face_normal_magnitude,\n    const tnsr::I<DataVector, 3>& x,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess) const {\n  linearized_apparent_horizon_impl<ConformalGeometry>(\n      conformal_factor_correction, lapse_times_conformal_factor_correction,\n      shift_excess_correction, n_dot_conformal_factor_gradient_correction,\n      n_dot_lapse_times_conformal_factor_gradient_correction,\n      n_dot_longitudinal_shift_excess_correction, center_, solution_for_lapse_,\n      solution_for_negative_expansion_, face_normal,\n      deriv_unnormalized_face_normal, face_normal_magnitude, x,\n      extrinsic_curvature_trace, longitudinal_shift_background,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      n_dot_longitudinal_shift_excess, std::nullopt, std::nullopt);\n}\n\ntemplate <Xcts::Geometry ConformalGeometry>\nvoid ApparentHorizon<ConformalGeometry>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_conformal_factor_gradient_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        n_dot_longitudinal_shift_excess_correction,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n    const tnsr::i<DataVector,\n                  3>& /*deriv_lapse_times_conformal_factor_correction*/,\n    const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess_correction*/,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n    const Scalar<DataVector>& face_normal_magnitude,\n    const tnsr::I<DataVector, 3>& x,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) const {\n  linearized_apparent_horizon_impl<ConformalGeometry>(\n      conformal_factor_correction, lapse_times_conformal_factor_correction,\n      shift_excess_correction, n_dot_conformal_factor_gradient_correction,\n      n_dot_lapse_times_conformal_factor_gradient_correction,\n      n_dot_longitudinal_shift_excess_correction, center_, solution_for_lapse_,\n      solution_for_negative_expansion_, face_normal,\n      deriv_unnormalized_face_normal, face_normal_magnitude, x,\n      extrinsic_curvature_trace, longitudinal_shift_background,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      n_dot_longitudinal_shift_excess, inv_conformal_metric,\n      conformal_christoffel_second_kind);\n}\n\ntemplate <Xcts::Geometry ConformalGeometry>\nvoid ApparentHorizon<ConformalGeometry>::pup(PUP::er& p) {\n  p | center_;\n  p | rotation_;\n  p | solution_for_lapse_;\n  p | solution_for_negative_expansion_;\n}\n\ntemplate <Xcts::Geometry ConformalGeometry>\nPUP::able::PUP_ID ApparentHorizon<ConformalGeometry>::my_PUP_ID = 0;  // NOLINT\n\ntemplate class ApparentHorizon<Xcts::Geometry::FlatCartesian>;\ntemplate class ApparentHorizon<Xcts::Geometry::Curved>;\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/BoundaryConditions/ApparentHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Xcts::BoundaryConditions {\n\n/*!\n * \\brief Impose the surface is a quasi-equilibrium apparent horizon.\n *\n * These boundary conditions on the conformal factor \\f$\\psi\\f$, the lapse\n * \\f$\\alpha\\f$ and the shift \\f$\\beta^i\\f$ impose the surface is an apparent\n * horizon, i.e. that the expansion on the surface vanishes: \\f$\\Theta=0\\f$.\n * Specifically, we impose:\n *\n * \\f{align}\n * \\label{eq:ah_psi}\n * \\bar{s}^k\\bar{D}_k\\psi &= -\\frac{\\psi^3}{8\\alpha}\\bar{s}_i\\bar{s}_j\\left(\n * (\\bar{L}\\beta)^{ij} - \\bar{u}^{ij}\\right)\n * - \\frac{\\psi}{4}\\bar{m}^{ij}\\bar{\\nabla}_i\\bar{s}_j + \\frac{1}{6}K\\psi^3\n * \\\\\n * \\label{eq:ah_beta}\n * \\beta_\\mathrm{excess}^i &= \\frac{\\alpha}{\\psi^2}\\bar{s}^i\n * + \\epsilon_{ijk}\\Omega^j x^k - \\beta_\\mathrm{background}^i\n * \\f}\n *\n * following section 7.2 of \\cite Pfeiffer2005zm, section 12.3.2 of\n * \\cite BaumgarteShapiro or section II.B.1 of \\cite Varma2018sqd. In these\n * equations \\f$\\bar{s}_i\\f$ is the conformal surface normal to the apparent\n * horizon, \\f$\\bar{m}^{ij}=\\bar{\\gamma}^{ij}-\\bar{s}^i\\bar{s}^j\\f$ is the\n * induced conformal surface metric (denoted \\f$\\tilde{h}^{ij}\\f$ in\n * \\cite Pfeiffer2005zm and \\cite Varma2018sqd) and \\f$\\bar{D}\\f$ is the\n * covariant derivative w.r.t. to the conformal metric \\f$\\bar{\\gamma}_{ij}\\f$.\n * Note that e.g. in \\cite Varma2018sqd Eq. (16) appears the surface-normal\n * \\f$s^i\\f$, not the _conformal_ surface normal \\f$\\bar{s}^i = \\psi^2 s^i\\f$.\n * To incur a spin on the apparent horizon we can freely choose the rotational\n * parameters \\f$\\boldsymbol{\\Omega}\\f$. Note that for a Kerr solution with\n * dimensionless spin \\f$\\boldsymbol{\\chi}\\f$ the rotational parameters at the\n * outer horizon are \\f$\\boldsymbol{\\Omega} =\n * -\\frac{\\boldsymbol{\\chi}}{2r_+}\\f$, where \\f$r_+ / M = 1 + \\sqrt{1 -\n * \\chi^2}\\f$ (see e.g. Eq. (8) in \\cite Ossokine2015yla). \\f$\\epsilon_{ijk}\\f$\n * is the flat-space Levi-Civita symbol.\n *\n * Note that the quasi-equilibrium conditions don't restrict the boundary\n * condition for the lapse. The choice for the lapse boundary condition is made\n * by the `Lapse` input-file options. Currently implemented choices are:\n * - A zero von-Neumann boundary condition:\n *   \\f$\\bar{s}^k\\bar{D}_k(\\alpha\\psi) = 0\\f$\n * - A Dirichlet boundary condition imposing the lapse of an analytic solution.\n *\n * \\par Negative-expansion boundary conditions:\n * This class also supports negative-expansion boundary conditions following\n * section II.B.2 of \\cite Varma2018sqd by taking a Kerr solution as additional\n * parameter and computing its expansion at the excision surface. Choosing an\n * excision surface _within_ the apparent horizon of the Kerr solution will\n * result in a negative expansion that is added to the boundary condition for\n * the conformal factor. Therefore, the excision surface will lie within an\n * apparent horizon. Specifically, we add the quantity\n * \\f$\\frac{\\psi^3}{4}\\Theta_\\mathrm{Kerr}\\f$ to (\\f$\\ref{eq:ah_psi}\\f$), where\n * \\f$\\Theta_\\mathrm{Kerr}\\f$ is the expansion of the specified Kerr solution at\n * the excision surface, and we add the quantity \\f$\\epsilon = s_i\n * \\beta_\\mathrm{Kerr}^i - \\alpha_\\mathrm{Kerr}\\f$ to the orthogonal part\n * \\f$s_i\\beta_\\mathrm{excess}^i\\f$ of (\\f$\\ref{eq:ah_beta}\\f$).\n *\n * \\note When negative-expansion boundary conditions are selected, the Kerr\n * solution gets evaluated at every boundary-condition application. This may\n * turn out to incur a significant computational cost, in which case a future\n * optimization might be to pre-compute the negative-expansion quantities at\n * initialization time and store them in the DataBox.\n */\ntemplate <Xcts::Geometry ConformalGeometry>\nclass ApparentHorizon\n    : public elliptic::BoundaryConditions::BoundaryCondition<3> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<3>;\n\n public:\n  static constexpr Options::String help =\n      \"Impose the boundary is a quasi-equilibrium apparent horizon.\";\n\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help =\n        \"The center of the excision surface representing the apparent-horizon \"\n        \"surface\";\n  };\n  struct Rotation {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help =\n        \"The rotational parameters 'Omega' on the surface, which parametrize \"\n        \"the spin of the black hole. The rotational parameters enter the \"\n        \"Dirichlet boundary conditions for the shift in a term \"\n        \"'Omega x (r - Center)', where 'r' are the coordinates on the surface.\";\n  };\n  struct Lapse {\n    using type =\n        Options::Auto<std::unique_ptr<elliptic::analytic_data::InitialGuess>>;\n    static constexpr Options::String help =\n        \"Specify an analytic solution or a superposed binary to impose a \"\n        \"Dirichlet condition on the lapse. The analytic solution will be \"\n        \"evaluated at coordinates centered at the apparent horizon. \"\n        \"Alternatively, set this option to 'None' \"\n        \"to impose a zero von-Neumann boundary condition on the lapse. Note \"\n        \"that the latter will not result in the standard Kerr-Schild slicing \"\n        \"for a single black hole.\";\n  };\n  struct NegativeExpansion {\n    using type =\n        Options::Auto<std::unique_ptr<elliptic::analytic_data::InitialGuess>,\n                      Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        \"Specify an analytic solution to impose its expansion at the excision \"\n        \"surface. The analytic solution will be evaluated at coordinates \"\n        \"centered at the apparent horizon. \"\n        \"If the excision surface lies within the solution's \"\n        \"apparent horizon, the imposed expansion will be negative and thus the \"\n        \"excision surface will lie within an apparent horizon. Alternatively, \"\n        \"set this option to 'None' to impose the expansion is zero at the \"\n        \"excision surface, meaning the excision surface _is_ an apparent \"\n        \"horizon.\";\n  };\n\n  using options = tmpl::list<Center, Rotation, Lapse, NegativeExpansion>;\n\n  ApparentHorizon() = default;\n  ApparentHorizon(const ApparentHorizon&) = delete;\n  ApparentHorizon& operator=(const ApparentHorizon&) = delete;\n  ApparentHorizon(ApparentHorizon&&) = default;\n  ApparentHorizon& operator=(ApparentHorizon&&) = default;\n  ~ApparentHorizon() = default;\n\n  /// \\cond\n  explicit ApparentHorizon(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ApparentHorizon);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<ApparentHorizon>(\n        center_, rotation_,\n        solution_for_lapse_.has_value()\n            ? std::make_optional(\n                  serialize_and_deserialize<\n                      std::unique_ptr<elliptic::analytic_data::InitialGuess>>(\n                      solution_for_lapse_.value()))\n            : std::nullopt,\n        solution_for_negative_expansion_.has_value()\n            ? std::make_optional(\n                  serialize_and_deserialize<\n                      std::unique_ptr<elliptic::analytic_data::InitialGuess>>(\n                      solution_for_negative_expansion_.value()))\n            : std::nullopt);\n  }\n\n  ApparentHorizon(\n      std::array<double, 3> center, std::array<double, 3> rotation,\n      std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>\n          solution_for_lapse,\n      std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>\n          solution_for_negative_expansion,\n      const Options::Context& context = {});\n\n  const std::array<double, 3>& center() const { return center_; }\n  const std::array<double, 3>& rotation() const { return rotation_; }\n  const std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>&\n  solution_for_lapse() const {\n    return solution_for_lapse_;\n  }\n  const std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>&\n  solution_for_negative_expansion() const {\n    return solution_for_negative_expansion_;\n  }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {// Conformal factor\n            elliptic::BoundaryConditionType::Neumann,\n            // Lapse times conformal factor\n            this->solution_for_lapse_.has_value()\n                ? elliptic::BoundaryConditionType::Dirichlet\n                : elliptic::BoundaryConditionType::Neumann,\n            // Shift\n            elliptic::BoundaryConditionType::Dirichlet,\n            elliptic::BoundaryConditionType::Dirichlet,\n            elliptic::BoundaryConditionType::Dirichlet};\n  }\n\n  using argument_tags = tmpl::flatten<tmpl::list<\n      domain::Tags::FaceNormal<3>,\n      ::Tags::deriv<domain::Tags::UnnormalizedFaceNormal<3>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      domain::Tags::UnnormalizedFaceNormalMagnitude<3>,\n      domain::Tags::Coordinates<3, Frame::Inertial>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n      Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,\n                                                              Frame::Inertial>,\n      tmpl::conditional_t<ConformalGeometry == Xcts::Geometry::Curved,\n                          tmpl::list<Tags::InverseConformalMetric<\n                                         DataVector, 3, Frame::Inertial>,\n                                     Tags::ConformalChristoffelSecondKind<\n                                         DataVector, 3, Frame::Inertial>>,\n                          tmpl::list<>>>>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n      gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient,\n      gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_longitudinal_shift_excess,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n      const tnsr::i<DataVector, 3>& deriv_lapse_times_conformal_factor,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n      const Scalar<DataVector>& face_normal_magnitude,\n      const tnsr::I<DataVector, 3>& x,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const tnsr::I<DataVector, 3>& shift_background,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_background) const;\n\n  void apply(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n      gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient,\n      gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_longitudinal_shift_excess,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n      const tnsr::i<DataVector, 3>& deriv_lapse_times_conformal_factor,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n      const Scalar<DataVector>& face_normal_magnitude,\n      const tnsr::I<DataVector, 3>& x,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const tnsr::I<DataVector, 3>& shift_background,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) const;\n\n  using argument_tags_linearized = tmpl::flatten<tmpl::list<\n      ::Tags::Normalized<\n          domain::Tags::UnnormalizedFaceNormal<3, Frame::Inertial>>,\n      ::Tags::deriv<domain::Tags::UnnormalizedFaceNormal<3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial>,\n      ::Tags::Magnitude<\n          domain::Tags::UnnormalizedFaceNormal<3, Frame::Inertial>>,\n      domain::Tags::Coordinates<3, Frame::Inertial>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,\n                                                              Frame::Inertial>,\n      Tags::ConformalFactorMinusOne<DataVector>,\n      Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n      ::Tags::NormalDotFlux<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>,\n      tmpl::conditional_t<ConformalGeometry == Xcts::Geometry::Curved,\n                          tmpl::list<Tags::InverseConformalMetric<\n                                         DataVector, 3, Frame::Inertial>,\n                                     Tags::ConformalChristoffelSecondKind<\n                                         DataVector, 3, Frame::Inertial>>,\n                          tmpl::list<>>>>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          lapse_times_conformal_factor_correction,\n      gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_conformal_factor_gradient_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient_correction,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          n_dot_longitudinal_shift_excess_correction,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n      const tnsr::i<DataVector, 3>&\n          deriv_lapse_times_conformal_factor_correction,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess_correction,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n      const Scalar<DataVector>& face_normal_magnitude,\n      const tnsr::I<DataVector, 3>& x,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess) const;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          lapse_times_conformal_factor_correction,\n      gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_conformal_factor_gradient_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient_correction,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          n_dot_longitudinal_shift_excess_correction,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n      const tnsr::i<DataVector, 3>&\n          deriv_lapse_times_conformal_factor_correction,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess_correction,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::ij<DataVector, 3>& deriv_unnormalized_face_normal,\n      const Scalar<DataVector>& face_normal_magnitude,\n      const tnsr::I<DataVector, 3>& x,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::array<double, 3> center_ =\n      make_array<3>(std::numeric_limits<double>::signaling_NaN());\n  std::array<double, 3> rotation_ =\n      make_array<3>(std::numeric_limits<double>::signaling_NaN());\n  std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>\n      solution_for_lapse_{};\n  std::optional<std::unique_ptr<elliptic::analytic_data::InitialGuess>>\n      solution_for_negative_expansion_{};\n};\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY XctsBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ApparentHorizon.cpp\n  Flatness.cpp\n  Robin.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApparentHorizon.hpp\n  Factory.hpp\n  Flatness.hpp\n  Robin.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  Elliptic\n  ErrorHandling\n  XctsSolutions\n  Options\n  Serialization\n  Utilities\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/ApparentHorizon.hpp\"\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/Flatness.hpp\"\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/Robin.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::BoundaryConditions {\ntemplate <typename System>\nusing standard_boundary_conditions =\n    tmpl::list<elliptic::BoundaryConditions::AnalyticSolution<System>,\n               Flatness<System::enabled_equations>,\n               Robin<System::enabled_equations>,\n               ApparentHorizon<System::conformal_geometry>>;\n}\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/BoundaryConditions/Flatness.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/Flatness.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts::BoundaryConditions {\n\ntemplate <>\nvoid Flatness<Xcts::Equations::Hamiltonian>::apply(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_conformal_factor_gradient*/,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/) {\n  get(*conformal_factor_minus_one) = 0.;\n}\n\ntemplate <>\nvoid Flatness<Xcts::Equations::HamiltonianAndLapse>::apply(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_conformal_factor_gradient*/,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_lapse_times_conformal_factor_gradient*/,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n    const tnsr::i<DataVector, 3>& /*deriv_lapse_times_conformal_factor*/) {\n  get(*conformal_factor_minus_one) = 0.;\n  get(*lapse_times_conformal_factor_minus_one) = 0.;\n}\n\ntemplate <>\nvoid Flatness<Xcts::Equations::HamiltonianLapseAndShift>::apply(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_conformal_factor_gradient*/,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_lapse_times_conformal_factor_gradient*/,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n    /*n_dot_longitudinal_shift_excess*/,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n    const tnsr::i<DataVector, 3>& /*deriv_lapse_times_conformal_factor*/,\n    const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess*/) {\n  get(*conformal_factor_minus_one) = 0.;\n  get(*lapse_times_conformal_factor_minus_one) = 0.;\n  std::fill(shift_excess->begin(), shift_excess->end(), 0.);\n}\n\ntemplate <>\nvoid Flatness<Xcts::Equations::Hamiltonian>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_conformal_factor_gradient_correction*/,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/) {\n  get(*conformal_factor_correction) = 0.;\n}\n\ntemplate <>\nvoid Flatness<Xcts::Equations::HamiltonianAndLapse>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_conformal_factor_gradient_correction*/,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_lapse_times_conformal_factor_gradient_correction*/,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n    const tnsr::i<DataVector,\n                  3>& /*deriv_lapse_times_conformal_factor_correction*/) {\n  get(*conformal_factor_correction) = 0.;\n  get(*lapse_times_conformal_factor_correction) = 0.;\n}\n\ntemplate <>\nvoid Flatness<Xcts::Equations::HamiltonianLapseAndShift>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_conformal_factor_gradient_correction*/,\n    const gsl::not_null<Scalar<DataVector>*>\n    /*n_dot_lapse_times_conformal_factor_gradient_correction*/,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n    /*n_dot_longitudinal_shift_excess_correction*/,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n    const tnsr::i<DataVector,\n                  3>& /*deriv_lapse_times_conformal_factor_correction*/,\n    const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess_correction*/) {\n  get(*conformal_factor_correction) = 0.;\n  get(*lapse_times_conformal_factor_correction) = 0.;\n  std::fill(shift_excess_correction->begin(), shift_excess_correction->end(),\n            0.);\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nbool operator==(const Flatness<EnabledEquations>& /*lhs*/,\n                const Flatness<EnabledEquations>& /*rhs*/) {\n  return true;\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nbool operator!=(const Flatness<EnabledEquations>& lhs,\n                const Flatness<EnabledEquations>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nPUP::able::PUP_ID Flatness<EnabledEquations>::my_PUP_ID = 0;  // NOLINT\n\n#define EQNS(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                             \\\n  template class Flatness<EQNS(data)>;                   \\\n  template bool operator==(const Flatness<EQNS(data)>&,  \\\n                           const Flatness<EQNS(data)>&); \\\n  template bool operator!=(const Flatness<EQNS(data)>&,  \\\n                           const Flatness<EQNS(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Xcts::Equations::Hamiltonian,\n                         Xcts::Equations::HamiltonianAndLapse,\n                         Xcts::Equations::HamiltonianLapseAndShift))\n#undef INSTANTIATE\n#undef EQNS\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/BoundaryConditions/Flatness.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Xcts/FluxesAndSources.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Xcts::BoundaryConditions {\n\n/*!\n * \\brief Impose flat spacetime at this boundary\n *\n * Impose \\f$\\psi=1\\f$, \\f$\\alpha\\psi=1\\f$, \\f$\\beta_\\mathrm{excess}^i=0\\f$ on\n * this boundary, where \\f$\\psi\\f$ is the conformal factor, \\f$\\alpha\\f$ is the\n * lapse and \\f$\\beta_\\mathrm{excess}^i=\\beta^i-\\beta_\\mathrm{background}^i\\f$\n * is the shift excess (see `Xcts::Tags::ShiftExcess` for details on the split\n * of the shift in background and excess). Note that this choice only truly\n * represents flatness if the conformal background metric is flat.\n *\n * \\tparam EnabledEquations The subset of XCTS equations that are being solved\n */\ntemplate <Xcts::Equations EnabledEquations>\nclass Flatness : public elliptic::BoundaryConditions::BoundaryCondition<3> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<3>;\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help =\n      \"Impose flat spacetime at this boundary.\";\n\n  Flatness() = default;\n  Flatness(const Flatness&) = default;\n  Flatness& operator=(const Flatness&) = default;\n  Flatness(Flatness&&) = default;\n  Flatness& operator=(Flatness&&) = default;\n  ~Flatness() = default;\n\n  /// \\cond\n  explicit Flatness(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Flatness);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<Flatness>(*this);\n  }\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {[]() {\n              if constexpr (EnabledEquations == Xcts::Equations::Hamiltonian) {\n                return 1;\n              } else if constexpr (EnabledEquations ==\n                                   Xcts::Equations::HamiltonianAndLapse) {\n                return 2;\n              } else {\n                return 5;\n              }\n            }(),\n            elliptic::BoundaryConditionType::Dirichlet};\n  }\n\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor);\n\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n      const tnsr::i<DataVector, 3>& deriv_lapse_times_conformal_factor);\n\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n      gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient,\n      gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_longitudinal_shift_excess,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n      const tnsr::i<DataVector, 3>& deriv_lapse_times_conformal_factor,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess);\n\n  using argument_tags_linearized = tmpl::list<>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  static void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_conformal_factor_gradient_correction,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction);\n\n  static void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          lapse_times_conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_conformal_factor_gradient_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient_correction,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n      const tnsr::i<DataVector, 3>&\n          deriv_lapse_times_conformal_factor_correction);\n\n  static void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          lapse_times_conformal_factor_correction,\n      gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_conformal_factor_gradient_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient_correction,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          n_dot_longitudinal_shift_excess_correction,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n      const tnsr::i<DataVector, 3>&\n          deriv_lapse_times_conformal_factor_correction,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess_correction);\n};\n\ntemplate <Xcts::Equations EnabledEquations>\nbool operator==(const Flatness<EnabledEquations>& lhs,\n                const Flatness<EnabledEquations>& rhs);\n\ntemplate <Xcts::Equations EnabledEquations>\nbool operator!=(const Flatness<EnabledEquations>& lhs,\n                const Flatness<EnabledEquations>& rhs);\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/BoundaryConditions/Robin.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/Robin.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts::BoundaryConditions {\n\nnamespace {\nvoid robin_boundary_condition_scalar(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_gradient,\n    const Scalar<DataVector>& scalar, const Scalar<DataVector>& r) {\n  get(*n_dot_gradient) = -get(scalar) / get(r);\n}\n\nvoid robin_boundary_condition_shift(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_longitudinal_shift,\n    tnsr::I<DataVector, 3> shift, const tnsr::iJ<DataVector, 3>& deriv_shift,\n    const Scalar<DataVector>& r, const tnsr::i<DataVector, 3>& face_normal) {\n  for (size_t i = 0; i < 3; ++i) {\n    shift.get(i) /= get(r);\n    for (size_t j = 0; j < 3; ++j) {\n      shift.get(i) += face_normal.get(j) * deriv_shift.get(j, i);\n    }\n  }\n  const auto n_dot_shift = dot_product(face_normal, shift);\n  for (size_t i = 0; i < 3; ++i) {\n    n_dot_longitudinal_shift->get(i) -=\n        shift.get(i) + face_normal.get(i) * get(n_dot_shift) / 3.;\n  }\n}\n}  // namespace\n\ntemplate <Xcts::Equations EnabledEquations>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nRobin<EnabledEquations>::get_clone() const {\n  return std::make_unique<Robin>(*this);\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nstd::vector<elliptic::BoundaryConditionType>\nRobin<EnabledEquations>::boundary_condition_types() const {\n  if constexpr (EnabledEquations == Xcts::Equations::Hamiltonian) {\n    return {1, elliptic::BoundaryConditionType::Neumann};\n  } else if constexpr (EnabledEquations ==\n                       Xcts::Equations::HamiltonianAndLapse) {\n    return {2, elliptic::BoundaryConditionType::Neumann};\n  } else {\n    return {5, elliptic::BoundaryConditionType::Neumann};\n  }\n}\n\ntemplate <>\nvoid Robin<Xcts::Equations::Hamiltonian>::apply(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n    const tnsr::I<DataVector, 3>& x,\n    const tnsr::i<DataVector, 3>& /*face_normal*/) const {\n  robin_boundary_condition_scalar(n_dot_conformal_factor_gradient,\n                                  *conformal_factor_minus_one, magnitude(x));\n}\n\ntemplate <>\nvoid Robin<Xcts::Equations::HamiltonianAndLapse>::apply(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n    const tnsr::i<DataVector, 3>& /*deriv_lapse_times_conformal_factor*/,\n    const tnsr::I<DataVector, 3>& x,\n    const tnsr::i<DataVector, 3>& /*face_normal*/) const {\n  const auto r = magnitude(x);\n  robin_boundary_condition_scalar(n_dot_conformal_factor_gradient,\n                                  *conformal_factor_minus_one, r);\n  robin_boundary_condition_scalar(n_dot_lapse_times_conformal_factor_gradient,\n                                  *lapse_times_conformal_factor_minus_one, r);\n}\n\ntemplate <>\nvoid Robin<Xcts::Equations::HamiltonianLapseAndShift>::apply(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        n_dot_longitudinal_shift_excess,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n    const tnsr::i<DataVector, 3>& /*deriv_lapse_times_conformal_factor*/,\n    const tnsr::iJ<DataVector, 3>& deriv_shift_excess,\n    const tnsr::I<DataVector, 3>& x,\n    const tnsr::i<DataVector, 3>& face_normal) const {\n  const auto r = magnitude(x);\n  robin_boundary_condition_scalar(n_dot_conformal_factor_gradient,\n                                  *conformal_factor_minus_one, r);\n  robin_boundary_condition_scalar(n_dot_lapse_times_conformal_factor_gradient,\n                                  *lapse_times_conformal_factor_minus_one, r);\n  robin_boundary_condition_shift(n_dot_longitudinal_shift_excess, *shift_excess,\n                                 deriv_shift_excess, r, face_normal);\n}\n\ntemplate <>\nvoid Robin<Xcts::Equations::Hamiltonian>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_conformal_factor_gradient_correction,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n    const tnsr::I<DataVector, 3>& x,\n    const tnsr::i<DataVector, 3>& /*face_normal*/) const {\n  robin_boundary_condition_scalar(n_dot_conformal_factor_gradient_correction,\n                                  *conformal_factor_correction, magnitude(x));\n}\n\ntemplate <>\nvoid Robin<Xcts::Equations::HamiltonianAndLapse>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_conformal_factor_gradient_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient_correction,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n    const tnsr::i<DataVector,\n                  3>& /*deriv_lapse_times_conformal_factor_correction*/,\n    const tnsr::I<DataVector, 3>& x,\n    const tnsr::i<DataVector, 3>& /*face_normal*/) const {\n  const auto r = magnitude(x);\n  robin_boundary_condition_scalar(n_dot_conformal_factor_gradient_correction,\n                                  *conformal_factor_correction, r);\n  robin_boundary_condition_scalar(\n      n_dot_lapse_times_conformal_factor_gradient_correction,\n      *lapse_times_conformal_factor_correction, r);\n}\n\ntemplate <>\nvoid Robin<Xcts::Equations::HamiltonianLapseAndShift>::apply_linearized(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_conformal_factor_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_conformal_factor_gradient_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        n_dot_longitudinal_shift_excess_correction,\n    const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n    const tnsr::i<DataVector,\n                  3>& /*deriv_lapse_times_conformal_factor_correction*/,\n    const tnsr::iJ<DataVector, 3>& deriv_shift_excess_correction,\n    const tnsr::I<DataVector, 3>& x,\n    const tnsr::i<DataVector, 3>& face_normal) const {\n  const auto r = magnitude(x);\n  robin_boundary_condition_scalar(n_dot_conformal_factor_gradient_correction,\n                                  *conformal_factor_correction, r);\n  robin_boundary_condition_scalar(\n      n_dot_lapse_times_conformal_factor_gradient_correction,\n      *lapse_times_conformal_factor_correction, r);\n  robin_boundary_condition_shift(n_dot_longitudinal_shift_excess_correction,\n                                 *shift_excess_correction,\n                                 deriv_shift_excess_correction, r, face_normal);\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nbool operator==(const Robin<EnabledEquations>& /*lhs*/,\n                const Robin<EnabledEquations>& /*rhs*/) {\n  return true;\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nbool operator!=(const Robin<EnabledEquations>& lhs,\n                const Robin<EnabledEquations>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nPUP::able::PUP_ID Robin<EnabledEquations>::my_PUP_ID = 0;  // NOLINT\n\n#define EQNS(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                          \\\n  template class Robin<EQNS(data)>;                   \\\n  template bool operator==(const Robin<EQNS(data)>&,  \\\n                           const Robin<EQNS(data)>&); \\\n  template bool operator!=(const Robin<EQNS(data)>&, const Robin<EQNS(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Xcts::Equations::Hamiltonian,\n                         Xcts::Equations::HamiltonianAndLapse,\n                         Xcts::Equations::HamiltonianLapseAndShift))\n#undef INSTANTIATE\n#undef EQNS\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/BoundaryConditions/Robin.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Xcts/FluxesAndSources.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Xcts::BoundaryConditions {\n\n/*!\n * \\brief Impose Robin boundary conditions at the outer boundary\n *\n * Impose $\\partial_r(r\\psi)=0$, $\\partial_r(\\alpha\\psi)=0$, and\n * $\\partial_r(r\\beta_\\mathrm{excess}^j)=0$ on the boundary. These Robin\n * boundary conditions incur an error of order $1/R^2$, where $R$ is the outer\n * radius of the domain. This allows to place the outer boundary much closer\n * to the center than for Dirichlet boundary conditions\n * (Xcts::BoundaryConditions::Flatness), which incur an error of order $1/R$.\n *\n * The Robin boundary conditions are imposed as Neumann-type as follows:\n *\n * \\begin{align*}\n * (n^i \\partial_i \\psi)_N &= -\\frac{\\psi}{r}, \\\\\n * (n^i \\partial_i (\\alpha\\psi))_N &= -\\frac{\\alpha\\psi}{r}, \\\\\n * (n^i (L\\beta)^{ij})_N = n^i (L\\beta)^{ij} - \\left[\n *   \\frac{\\beta_\\mathrm{excess}^j}{r}\n *   + \\frac{1}{3} n^j \\frac{n^i \\beta_\\mathrm{excess}^i}{r}\n *   + n^i \\partial_i \\beta_\\mathrm{excess}^j\n *   + \\frac{1}{3} n^j n^i n_k \\partial_i \\beta_\\mathrm{excess}^k\n * \\right]\n * \\end{align*}\n *\n * Here, the condition on the longitudinal shift is derived by imposing the\n * Robin boundary condition\n * $n^i\\partial_i\\beta_\\mathrm{excess}^j=-\\beta_\\mathrm{excess}^j/r$ only on the\n * normal component of the shift gradient. To do this we can use the projection\n * operator $P_{ij}=\\delta_{ij}-n_i n_j$ to set the shift gradient to\n * $\\partial_i\\beta_\\mathrm{excess}^j=P_{ik}\\partial_k\\beta_\\mathrm{excess}^j\n * -n_i \\beta_\\mathrm{excess}^j/r$ and then apply the longitudinal operator.\n *\n * \\tparam EnabledEquations The subset of XCTS equations that are being solved\n */\ntemplate <Xcts::Equations EnabledEquations>\nclass Robin : public elliptic::BoundaryConditions::BoundaryCondition<3> {\n private:\n  using Base = elliptic::BoundaryConditions::BoundaryCondition<3>;\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help =\n      \"Impose Robin boundary conditions at the outer boundary. \"\n      \"They incur an error of order 1/R^2, where R is the outer radius.\";\n\n  Robin() = default;\n  Robin(const Robin&) = default;\n  Robin& operator=(const Robin&) = default;\n  Robin(Robin&&) = default;\n  Robin& operator=(Robin&&) = default;\n  ~Robin() override = default;\n\n  /// \\cond\n  explicit Robin(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Robin);\n  /// \\endcond\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override;\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override;\n\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>,\n                 domain::Tags::FaceNormal<3, Frame::Inertial>>;\n  using volume_tags = tmpl::list<>;\n\n  void apply(gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n             gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n             const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n             const tnsr::I<DataVector, 3>& x,\n             const tnsr::i<DataVector, 3>& face_normal) const;\n\n  void apply(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n      const tnsr::i<DataVector, 3>& deriv_lapse_times_conformal_factor,\n      const tnsr::I<DataVector, 3>& x,\n      const tnsr::i<DataVector, 3>& face_normal) const;\n\n  void apply(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_minus_one,\n      gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n      gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient,\n      gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_longitudinal_shift_excess,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n      const tnsr::i<DataVector, 3>& deriv_lapse_times_conformal_factor,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess,\n      const tnsr::I<DataVector, 3>& x,\n      const tnsr::i<DataVector, 3>& face_normal) const;\n\n  using argument_tags_linearized =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>,\n                 domain::Tags::FaceNormal<3, Frame::Inertial>>;\n  using volume_tags_linearized = tmpl::list<>;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_conformal_factor_gradient_correction,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n      const tnsr::I<DataVector, 3>& x,\n      const tnsr::i<DataVector, 3>& face_normal) const;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          lapse_times_conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_conformal_factor_gradient_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient_correction,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n      const tnsr::i<DataVector, 3>&\n          deriv_lapse_times_conformal_factor_correction,\n      const tnsr::I<DataVector, 3>& x,\n      const tnsr::i<DataVector, 3>& face_normal) const;\n\n  void apply_linearized(\n      gsl::not_null<Scalar<DataVector>*> conformal_factor_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          lapse_times_conformal_factor_correction,\n      gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_conformal_factor_gradient_correction,\n      gsl::not_null<Scalar<DataVector>*>\n          n_dot_lapse_times_conformal_factor_gradient_correction,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          n_dot_longitudinal_shift_excess_correction,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n      const tnsr::i<DataVector, 3>&\n          deriv_lapse_times_conformal_factor_correction,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess_correction,\n      const tnsr::I<DataVector, 3>& x,\n      const tnsr::i<DataVector, 3>& face_normal) const;\n};\n\ntemplate <Xcts::Equations LocalEnabledEquations>\nbool operator==(const Robin<LocalEnabledEquations>& lhs,\n                const Robin<LocalEnabledEquations>& rhs);\n\ntemplate <Xcts::Equations EnabledEquations>\nbool operator!=(const Robin<EnabledEquations>& lhs,\n                const Robin<EnabledEquations>& rhs);\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Xcts)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Equations.cpp\n  FluxesAndSources.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Equations.hpp\n  FirstOrderSystem.hpp\n  FluxesAndSources.hpp\n  Geometry.hpp\n  HydroQuantities.hpp\n  Tags.hpp\n  Xcts.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  GeneralRelativity\n  Hydro\n  Utilities\n  PRIVATE\n  Elasticity\n  ErrorHandling\n  LinearOperators\n  Poisson\n  Utilities\n  XctsPointwiseFunctions\n  )\n\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(Events)\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Xcts/Equations.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts {\nnamespace detail {\nvoid fully_contract_flat_cartesian(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const tnsr::II<DataVector, 3>& tensor1,\n    const tnsr::II<DataVector, 3>& tensor2) {\n  get(*result) = tensor1.multiplicity(0_st) * tensor1[0] * tensor2[0];\n  for (size_t i = 1; i < tensor1.size(); ++i) {\n    get(*result) += tensor1.multiplicity(i) * tensor1[i] * tensor2[i];\n  }\n}\nvoid fully_contract(const gsl::not_null<Scalar<DataVector>*> result,\n                    const gsl::not_null<DataVector*> buffer1,\n                    const gsl::not_null<DataVector*> buffer2,\n                    const tnsr::II<DataVector, 3>& tensor1,\n                    const tnsr::II<DataVector, 3>& tensor2,\n                    const tnsr::ii<DataVector, 3>& metric) {\n  get(*result) = 0.;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      *buffer1 = metric.get(i, 0) * tensor1.get(0, j);\n      *buffer2 = metric.get(j, 0) * tensor2.get(i, 0);\n      for (size_t k = 1; k < 3; ++k) {\n        *buffer1 += metric.get(i, k) * tensor1.get(k, j);\n        *buffer2 += metric.get(j, k) * tensor2.get(i, k);\n      }\n      get(*result) += *buffer1 * *buffer2;\n    }\n  }\n}\n}  // namespace detail\n\ntemplate <int ConformalMatterScale>\nvoid add_hamiltonian_sources(\n    const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    const Scalar<DataVector>& conformal_energy_density,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const Scalar<DataVector>& conformal_factor_minus_one) {\n  // See BaumgarteShapiro, Eq. (3.110)\n  get(*hamiltonian_constraint) +=\n      (square(get(extrinsic_curvature_trace)) / 12. -\n       2. * M_PI * get(conformal_energy_density) /\n           pow<ConformalMatterScale>(get(conformal_factor_minus_one) + 1.)) *\n      pow<5>(get(conformal_factor_minus_one) + 1.);\n}\n\ntemplate <int ConformalMatterScale>\nvoid add_linearized_hamiltonian_sources(\n    const gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    const Scalar<DataVector>& conformal_energy_density,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& conformal_factor_correction) {\n  get(*linearized_hamiltonian_constraint) +=\n      ((5. / 12.) * square(get(extrinsic_curvature_trace)) -\n       2. * (5. - ConformalMatterScale) * M_PI * get(conformal_energy_density) /\n           pow<ConformalMatterScale>(get(conformal_factor_minus_one) + 1.)) *\n      pow<4>(get(conformal_factor_minus_one) + 1.) *\n      get(conformal_factor_correction);\n}\n\nvoid add_distortion_hamiltonian_sources(\n    const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const Scalar<DataVector>& conformal_factor_minus_one) {\n  // See BaumgarteShapiro, Eq. (3.110) and (3.112). Note: 0.03125 = 1 / 32\n  get(*hamiltonian_constraint) -=\n      0.03125 * pow<5>(get(conformal_factor_minus_one) + 1.) *\n      get(longitudinal_shift_minus_dt_conformal_metric_over_lapse_square);\n}\n\nvoid add_linearized_distortion_hamiltonian_sources(\n    const gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& conformal_factor_correction) {\n  // Note: 0.15625 = 5 / 32\n  get(*linearized_hamiltonian_constraint) -=\n      0.15625 * pow<4>(get(conformal_factor_minus_one) + 1.) *\n      get(longitudinal_shift_minus_dt_conformal_metric_over_lapse_square) *\n      get(conformal_factor_correction);\n}\n\nvoid add_curved_hamiltonian_or_lapse_sources(\n    const gsl::not_null<Scalar<DataVector>*> hamiltonian_or_lapse_equation,\n    const Scalar<DataVector>& conformal_ricci_scalar,\n    const Scalar<DataVector>& field, const double add_constant) {\n  // See BaumgarteShapiro, Eq. (3.110) and (3.111). Note: 0.125 = 1 / 8\n  get(*hamiltonian_or_lapse_equation) +=\n      0.125 * get(conformal_ricci_scalar) * (get(field) + add_constant);\n}\n\ntemplate <int ConformalMatterScale>\nvoid add_lapse_sources(\n    const gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const Scalar<DataVector>& conformal_energy_density,\n    const Scalar<DataVector>& conformal_stress_trace,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n    const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one) {\n  // See BaumgarteShapiro, Eq. (3.111)\n  get(*lapse_equation) +=\n      (get(lapse_times_conformal_factor_minus_one) + 1.) *\n          pow<4>(get(conformal_factor_minus_one) + 1.) *\n          ((5. / 12.) * square(get(extrinsic_curvature_trace)) +\n           2. * M_PI *\n               (get(conformal_energy_density) +\n                2. * get(conformal_stress_trace)) /\n               pow<ConformalMatterScale>(get(conformal_factor_minus_one) +\n                                         1.)) +\n      pow<5>(get(conformal_factor_minus_one) + 1.) *\n          (get(shift_dot_deriv_extrinsic_curvature_trace) -\n           get(dt_extrinsic_curvature_trace));\n}\n\ntemplate <int ConformalMatterScale>\nvoid add_linearized_lapse_sources(\n    const gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n    const Scalar<DataVector>& conformal_energy_density,\n    const Scalar<DataVector>& conformal_stress_trace,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n    const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction) {\n  get(*linearized_lapse_equation) +=\n      pow<3>(get(conformal_factor_minus_one) + 1.) *\n          (((get(conformal_factor_minus_one) + 1.) *\n                get(lapse_times_conformal_factor_correction) +\n            4. * (get(lapse_times_conformal_factor_minus_one) + 1.) *\n                get(conformal_factor_correction)) *\n               5. / 12. * square(get(extrinsic_curvature_trace)) +\n           ((get(conformal_factor_minus_one) + 1.) *\n                get(lapse_times_conformal_factor_correction) +\n            (4. - ConformalMatterScale) *\n                (get(lapse_times_conformal_factor_minus_one) + 1.) *\n                get(conformal_factor_correction)) *\n               2. * M_PI *\n               (get(conformal_energy_density) +\n                2 * get(conformal_stress_trace)) /\n               pow<ConformalMatterScale>(get(conformal_factor_minus_one) +\n                                         1.)) +\n      5. * pow<4>(get(conformal_factor_minus_one) + 1.) *\n          get(conformal_factor_correction) *\n          (get(shift_dot_deriv_extrinsic_curvature_trace) -\n           get(dt_extrinsic_curvature_trace));\n}\n\nvoid add_distortion_hamiltonian_and_lapse_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_square,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one) {\n  // See BaumgarteShapiro, Eq. (3.110--3.112). Note: 0.03125 = 1 / 32 and\n  // 0.21875 = 7 / 32\n  get(*hamiltonian_constraint) -=\n      0.03125 * pow<7>(get(conformal_factor_minus_one) + 1.) /\n      square(get(lapse_times_conformal_factor_minus_one) + 1.) *\n      get(longitudinal_shift_minus_dt_conformal_metric_square);\n  get(*lapse_equation) +=\n      0.21875 * pow<6>(get(conformal_factor_minus_one) + 1.) /\n      (get(lapse_times_conformal_factor_minus_one) + 1.) *\n      get(longitudinal_shift_minus_dt_conformal_metric_square);\n}\n\nvoid add_linearized_distortion_hamiltonian_and_lapse_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_square,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction) {\n  // Note: 0.03125 = 1 / 32 and 0.21875 = 7 / 32\n  get(*hamiltonian_constraint) -=\n      0.03125 * pow<6>(get(conformal_factor_minus_one) + 1.) *\n      (7. * get(conformal_factor_correction) -\n       2. * (get(conformal_factor_minus_one) + 1.) /\n           (get(lapse_times_conformal_factor_minus_one) + 1.) *\n           get(lapse_times_conformal_factor_correction)) /\n      square(get(lapse_times_conformal_factor_minus_one) + 1.) *\n      get(longitudinal_shift_minus_dt_conformal_metric_square);\n  get(*lapse_equation) +=\n      0.21875 * pow<5>(get(conformal_factor_minus_one) + 1.) *\n      (6. * get(conformal_factor_correction) -\n       (get(conformal_factor_minus_one) + 1.) /\n           (get(lapse_times_conformal_factor_minus_one) + 1.) *\n           get(lapse_times_conformal_factor_correction)) /\n      (get(lapse_times_conformal_factor_minus_one) + 1.) *\n      get(longitudinal_shift_minus_dt_conformal_metric_square);\n}\n\ntemplate <int ConformalMatterScale, Geometry ConformalGeometry>\nvoid add_momentum_sources_impl(\n    const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    const gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const std::optional<std::reference_wrapper<const tnsr::ii<DataVector, 3>>>\n        conformal_metric,\n    const std::optional<std::reference_wrapper<const tnsr::II<DataVector, 3>>>\n        inv_conformal_metric,\n    const tnsr::I<DataVector, 3>& minus_div_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>&\n        longitudinal_shift_minus_dt_conformal_metric) {\n  if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n    ASSERT(not conformal_metric.has_value() and\n               not inv_conformal_metric.has_value(),\n           \"You don't need to pass a conformal metric to this function when it \"\n           \"is specialized for a flat conformal geometry in Cartesian \"\n           \"coordinates.\");\n  } else {\n    ASSERT(conformal_metric.has_value() and inv_conformal_metric.has_value(),\n           \"You must pass a conformal metric to this function when it is \"\n           \"specialized for a curved conformal geometry.\");\n  }\n  const size_t num_points = get(conformal_factor_minus_one).size();\n  TempBuffer<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempI<1, 3>,\n                        ::Tags::Tempi<2, 3>>>\n      buffer{num_points};\n  {\n    auto& longitudinal_shift_square = get<::Tags::TempScalar<0>>(buffer);\n    if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n      detail::fully_contract_flat_cartesian(\n          make_not_null(&longitudinal_shift_square),\n          longitudinal_shift_minus_dt_conformal_metric,\n          longitudinal_shift_minus_dt_conformal_metric);\n    } else {\n      auto& buffer1 = get<0>(get<::Tags::TempI<1, 3>>(buffer));\n      auto& buffer2 = get<1>(get<::Tags::TempI<1, 3>>(buffer));\n      detail::fully_contract(make_not_null(&longitudinal_shift_square),\n                             make_not_null(&buffer1), make_not_null(&buffer2),\n                             longitudinal_shift_minus_dt_conformal_metric,\n                             longitudinal_shift_minus_dt_conformal_metric,\n                             conformal_metric->get());\n    }\n    // Add shift terms to Hamiltonian and lapse equations, see BaumgarteShapiro,\n    // Eq. (3.110--3.112). Note: 0.03125 = 1 / 32 and 0.21875 = 7 / 32\n    get(*hamiltonian_constraint) -=\n        0.03125 * pow<7>(get(conformal_factor_minus_one) + 1.) /\n        square(get(lapse_times_conformal_factor_minus_one) + 1.) *\n        get(longitudinal_shift_square);\n    get(*lapse_equation) += 0.21875 *\n                            pow<6>(get(conformal_factor_minus_one) + 1.) /\n                            (get(lapse_times_conformal_factor_minus_one) + 1.) *\n                            get(longitudinal_shift_square);\n  }\n  // Add sources to momentum constraint, see BaumgarteShapiro, Eq. (3.109)\n  {\n    // Extrinsic curvature term\n    auto& extrinsic_curvature_trace_gradient_term =\n        get<::Tags::TempI<1, 3>>(buffer);\n    if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n      get<0>(extrinsic_curvature_trace_gradient_term) =\n          get<0>(extrinsic_curvature_trace_gradient);\n      get<1>(extrinsic_curvature_trace_gradient_term) =\n          get<1>(extrinsic_curvature_trace_gradient);\n      get<2>(extrinsic_curvature_trace_gradient_term) =\n          get<2>(extrinsic_curvature_trace_gradient);\n    } else {\n      raise_or_lower_index(\n          make_not_null(&extrinsic_curvature_trace_gradient_term),\n          extrinsic_curvature_trace_gradient, inv_conformal_metric->get());\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      extrinsic_curvature_trace_gradient_term.get(i) *=\n          4. / 3. * (get(lapse_times_conformal_factor_minus_one) + 1.) /\n          (get(conformal_factor_minus_one) + 1.);\n      momentum_constraint->get(i) +=\n          extrinsic_curvature_trace_gradient_term.get(i);\n    }\n  }\n  {\n    // Lapse deriv term, to be contracted with longitudinal shift\n    auto& lapse_deriv_term = get<::Tags::TempI<1, 3>>(buffer);\n    for (size_t i = 0; i < 3; ++i) {\n      lapse_deriv_term.get(i) =\n          (lapse_times_conformal_factor_flux.get(i) /\n               (get(lapse_times_conformal_factor_minus_one) + 1.) -\n           7. * conformal_factor_flux.get(i) /\n               (get(conformal_factor_minus_one) + 1.));\n    }\n    auto& lapse_deriv_term_lo = get<::Tags::Tempi<2, 3>>(buffer);\n    if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n      get<0>(lapse_deriv_term_lo) = get<0>(lapse_deriv_term);\n      get<1>(lapse_deriv_term_lo) = get<1>(lapse_deriv_term);\n      get<2>(lapse_deriv_term_lo) = get<2>(lapse_deriv_term);\n    } else {\n      raise_or_lower_index(make_not_null(&lapse_deriv_term_lo),\n                           lapse_deriv_term, conformal_metric->get());\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      // Add momentum density term\n      momentum_constraint->get(i) +=\n          16. * M_PI * (get(lapse_times_conformal_factor_minus_one) + 1.) *\n          pow<3 - ConformalMatterScale>(get(conformal_factor_minus_one) + 1.) *\n          conformal_momentum_density.get(i);\n      // Add longitudinal shift term\n      for (size_t j = 0; j < 3; ++j) {\n        momentum_constraint->get(i) +=\n            longitudinal_shift_minus_dt_conformal_metric.get(i, j) *\n            lapse_deriv_term_lo.get(j);\n      }\n      momentum_constraint->get(i) -= minus_div_dt_conformal_metric.get(i);\n    }\n  }\n}\n\ntemplate <int ConformalMatterScale>\nvoid add_curved_momentum_sources(\n    const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    const gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::I<DataVector, 3>& minus_div_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>&\n        longitudinal_shift_minus_dt_conformal_metric) {\n  add_momentum_sources_impl<ConformalMatterScale, Geometry::Curved>(\n      hamiltonian_constraint, lapse_equation, momentum_constraint,\n      conformal_momentum_density, extrinsic_curvature_trace_gradient,\n      conformal_metric, inv_conformal_metric, minus_div_dt_conformal_metric,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_flux, lapse_times_conformal_factor_flux,\n      longitudinal_shift_minus_dt_conformal_metric);\n}\n\ntemplate <int ConformalMatterScale>\nvoid add_flat_cartesian_momentum_sources(\n    const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    const gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const tnsr::I<DataVector, 3>& minus_div_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>&\n        longitudinal_shift_minus_dt_conformal_metric) {\n  add_momentum_sources_impl<ConformalMatterScale, Geometry::FlatCartesian>(\n      hamiltonian_constraint, lapse_equation, momentum_constraint,\n      conformal_momentum_density, extrinsic_curvature_trace_gradient,\n      std::nullopt, std::nullopt, minus_div_dt_conformal_metric,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_flux, lapse_times_conformal_factor_flux,\n      longitudinal_shift_minus_dt_conformal_metric);\n}\n\ntemplate <int ConformalMatterScale, Geometry ConformalGeometry>\nvoid add_linearized_momentum_sources_impl(\n    const gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    const gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const std::optional<std::reference_wrapper<const tnsr::ii<DataVector, 3>>>\n        conformal_metric,\n    const std::optional<std::reference_wrapper<const tnsr::II<DataVector, 3>>>\n        inv_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n    const tnsr::I<DataVector, 3>& shift_correction,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux_correction,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_correction) {\n  if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n    ASSERT(not conformal_metric.has_value() and\n               not inv_conformal_metric.has_value(),\n           \"You don't need to pass a conformal metric to this function when it \"\n           \"is specialized for a flat conformal geometry in Cartesian \"\n           \"coordinates.\");\n  } else {\n    ASSERT(conformal_metric.has_value() and inv_conformal_metric.has_value(),\n           \"You must pass a conformal metric to this function when it is \"\n           \"specialized for a curved conformal geometry.\");\n  }\n  const size_t num_points = get(conformal_factor_minus_one).size();\n  TempBuffer<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempI<1, 3>,\n                        ::Tags::Tempi<2, 3>>>\n      buffer{num_points};\n  {\n    // Add shift terms to linearized Hamiltonian and lapse equations\n    auto& longitudinal_shift_square = get<::Tags::TempScalar<0>>(buffer);\n    if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n      detail::fully_contract_flat_cartesian(\n          make_not_null(&longitudinal_shift_square),\n          longitudinal_shift_minus_dt_conformal_metric,\n          longitudinal_shift_minus_dt_conformal_metric);\n    } else {\n      auto& buffer1 = get<0>(get<::Tags::TempI<1, 3>>(buffer));\n      auto& buffer2 = get<1>(get<::Tags::TempI<1, 3>>(buffer));\n      detail::fully_contract(make_not_null(&longitudinal_shift_square),\n                             make_not_null(&buffer1), make_not_null(&buffer2),\n                             longitudinal_shift_minus_dt_conformal_metric,\n                             longitudinal_shift_minus_dt_conformal_metric,\n                             conformal_metric->get());\n    }\n    // Note: 0.21875 = 7 / 32, 0.0625 = 1 / 16 and 1.3125 = 21 / 16\n    get(*linearized_hamiltonian_constraint) +=\n        -0.21875 * pow<6>(get(conformal_factor_minus_one) + 1.) /\n            square(get(lapse_times_conformal_factor_minus_one) + 1.) *\n            get(longitudinal_shift_square) * get(conformal_factor_correction) +\n        0.0625 * pow<7>(get(conformal_factor_minus_one) + 1.) /\n            pow<3>(get(lapse_times_conformal_factor_minus_one) + 1.) *\n            get(longitudinal_shift_square) *\n            get(lapse_times_conformal_factor_correction);\n    get(*linearized_lapse_equation) +=\n        1.3125 * pow<5>(get(conformal_factor_minus_one) + 1.) /\n            (get(lapse_times_conformal_factor_minus_one) + 1.) *\n            get(longitudinal_shift_square) * get(conformal_factor_correction) -\n        0.21875 * pow<6>(get(conformal_factor_minus_one) + 1.) /\n            square(get(lapse_times_conformal_factor_minus_one) + 1.) *\n            get(longitudinal_shift_square) *\n            get(lapse_times_conformal_factor_correction);\n  }\n  {\n    // Add shift-correction terms to linearized Hamiltonian and lapse equations,\n    // re-using the buffer used for `longitudinal_shift_square` above\n    auto& longitudinal_shift_dot_correction =\n        get<::Tags::TempScalar<0>>(buffer);\n    if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n      detail::fully_contract_flat_cartesian(\n          make_not_null(&longitudinal_shift_dot_correction),\n          longitudinal_shift_minus_dt_conformal_metric,\n          longitudinal_shift_correction);\n    } else {\n      auto& buffer1 = get<0>(get<::Tags::TempI<1, 3>>(buffer));\n      auto& buffer2 = get<1>(get<::Tags::TempI<1, 3>>(buffer));\n      detail::fully_contract(make_not_null(&longitudinal_shift_dot_correction),\n                             make_not_null(&buffer1), make_not_null(&buffer2),\n                             longitudinal_shift_minus_dt_conformal_metric,\n                             longitudinal_shift_correction,\n                             conformal_metric->get());\n    }\n    // Note: 0.0625 = 1 / 16 and 0.4375 = 7 / 16\n    get(*linearized_hamiltonian_constraint) -=\n        0.0625 * pow<7>(get(conformal_factor_minus_one) + 1.) /\n        square(get(lapse_times_conformal_factor_minus_one) + 1.) *\n        get(longitudinal_shift_dot_correction);\n    get(*linearized_lapse_equation) +=\n        0.4375 * pow<6>(get(conformal_factor_minus_one) + 1.) /\n        (get(lapse_times_conformal_factor_minus_one) + 1.) *\n        get(longitudinal_shift_dot_correction);\n  }\n  {\n    // Add remaining linearization w.r.t. shift of the shift.grad(K) term to the\n    // lapse equation. The linearization w.r.t. conformal factor and lapse is\n    // already added in `linearized_lapse_sources`.\n    auto& shift_correction_dot_extrinsic_curvature_trace_gradient =\n        get<::Tags::TempScalar<0>>(buffer);\n    dot_product(\n        make_not_null(&shift_correction_dot_extrinsic_curvature_trace_gradient),\n        shift_correction, extrinsic_curvature_trace_gradient);\n    get(*linearized_lapse_equation) +=\n        pow<5>(get(conformal_factor_minus_one) + 1.) *\n        get(shift_correction_dot_extrinsic_curvature_trace_gradient);\n  }\n  // Add sources to linearized momentum constraint\n  {\n    auto& extrinsic_curvature_trace_gradient_term =\n        get<::Tags::TempI<1, 3>>(buffer);\n    // Extrinsic curvature term\n    if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n      get<0>(extrinsic_curvature_trace_gradient_term) =\n          get<0>(extrinsic_curvature_trace_gradient);\n      get<1>(extrinsic_curvature_trace_gradient_term) =\n          get<1>(extrinsic_curvature_trace_gradient);\n      get<2>(extrinsic_curvature_trace_gradient_term) =\n          get<2>(extrinsic_curvature_trace_gradient);\n    } else {\n      raise_or_lower_index(\n          make_not_null(&extrinsic_curvature_trace_gradient_term),\n          extrinsic_curvature_trace_gradient, inv_conformal_metric->get());\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      extrinsic_curvature_trace_gradient_term.get(i) *=\n          4. / 3. *\n          (get(lapse_times_conformal_factor_correction) /\n               (get(conformal_factor_minus_one) + 1.) -\n           (get(lapse_times_conformal_factor_minus_one) + 1.) /\n               square(get(conformal_factor_minus_one) + 1.) *\n               get(conformal_factor_correction));\n      linearized_momentum_constraint->get(i) +=\n          extrinsic_curvature_trace_gradient_term.get(i);\n    }\n  }\n  {\n    // Compute lapse deriv term to be contracted with longitudinal\n    // shift-correction\n    auto& lapse_deriv_term = get<::Tags::TempI<1, 3>>(buffer);\n    for (size_t i = 0; i < 3; ++i) {\n      lapse_deriv_term.get(i) =\n          (lapse_times_conformal_factor_flux.get(i) /\n               (get(lapse_times_conformal_factor_minus_one) + 1.) -\n           7. * conformal_factor_flux.get(i) /\n               (get(conformal_factor_minus_one) + 1.));\n    }\n    auto& lapse_deriv_term_lo = get<::Tags::Tempi<2, 3>>(buffer);\n    if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n      get<0>(lapse_deriv_term_lo) = get<0>(lapse_deriv_term);\n      get<1>(lapse_deriv_term_lo) = get<1>(lapse_deriv_term);\n      get<2>(lapse_deriv_term_lo) = get<2>(lapse_deriv_term);\n    } else {\n      raise_or_lower_index(make_not_null(&lapse_deriv_term_lo),\n                           lapse_deriv_term, conformal_metric->get());\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      // Add momentum density term\n      linearized_momentum_constraint->get(i) +=\n          16. * M_PI *\n          (pow<3 - ConformalMatterScale>(get(conformal_factor_minus_one) + 1.) *\n               get(lapse_times_conformal_factor_correction) +\n           (3. - ConformalMatterScale) *\n               pow<2 - ConformalMatterScale>(get(conformal_factor_minus_one) +\n                                             1.) *\n               (get(lapse_times_conformal_factor_minus_one) + 1.) *\n               get(conformal_factor_correction)) *\n          conformal_momentum_density.get(i);\n      // Add longitudinal shift-correction term\n      for (size_t j = 0; j < 3; ++j) {\n        linearized_momentum_constraint->get(i) +=\n            longitudinal_shift_correction.get(i, j) *\n            lapse_deriv_term_lo.get(j);\n      }\n    }\n  }\n  {\n    // Compute lapse deriv correction term to be contracted with longitudinal\n    // shift\n    auto& lapse_deriv_correction_term = get<::Tags::TempI<1, 3>>(buffer);\n    for (size_t i = 0; i < 3; ++i) {\n      lapse_deriv_correction_term.get(i) =\n          (lapse_times_conformal_factor_flux_correction.get(i) /\n               (get(lapse_times_conformal_factor_minus_one) + 1.) -\n           lapse_times_conformal_factor_flux.get(i) /\n               square(get(lapse_times_conformal_factor_minus_one) + 1.) *\n               get(lapse_times_conformal_factor_correction) -\n           7. * conformal_factor_flux_correction.get(i) /\n               (get(conformal_factor_minus_one) + 1.) +\n           7. * conformal_factor_flux.get(i) /\n               square(get(conformal_factor_minus_one) + 1.) *\n               get(conformal_factor_correction));\n    }\n    auto& lapse_deriv_correction_term_lo = get<::Tags::Tempi<2, 3>>(buffer);\n    if constexpr (ConformalGeometry == Geometry::FlatCartesian) {\n      get<0>(lapse_deriv_correction_term_lo) =\n          get<0>(lapse_deriv_correction_term);\n      get<1>(lapse_deriv_correction_term_lo) =\n          get<1>(lapse_deriv_correction_term);\n      get<2>(lapse_deriv_correction_term_lo) =\n          get<2>(lapse_deriv_correction_term);\n    } else {\n      raise_or_lower_index(make_not_null(&lapse_deriv_correction_term_lo),\n                           lapse_deriv_correction_term,\n                           conformal_metric->get());\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      // Add longitudinal shift term\n      for (size_t j = 0; j < 3; ++j) {\n        linearized_momentum_constraint->get(i) +=\n            longitudinal_shift_minus_dt_conformal_metric.get(i, j) *\n            lapse_deriv_correction_term_lo.get(j);\n      }\n    }\n  }\n}\n\ntemplate <int ConformalMatterScale>\nvoid add_curved_linearized_momentum_sources(\n    gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n    gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n    const tnsr::I<DataVector, 3>& shift_correction,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux_correction,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_correction) {\n  add_linearized_momentum_sources_impl<ConformalMatterScale, Geometry::Curved>(\n      linearized_hamiltonian_constraint, linearized_lapse_equation,\n      linearized_momentum_constraint, conformal_momentum_density,\n      extrinsic_curvature_trace_gradient, conformal_metric,\n      inv_conformal_metric, conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one, conformal_factor_flux,\n      lapse_times_conformal_factor_flux,\n      longitudinal_shift_minus_dt_conformal_metric, conformal_factor_correction,\n      lapse_times_conformal_factor_correction, shift_correction,\n      conformal_factor_flux_correction,\n      lapse_times_conformal_factor_flux_correction,\n      longitudinal_shift_correction);\n}\n\ntemplate <int ConformalMatterScale>\nvoid add_flat_cartesian_linearized_momentum_sources(\n    gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n    gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n    const tnsr::I<DataVector, 3>& shift_correction,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux_correction,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_correction) {\n  add_linearized_momentum_sources_impl<ConformalMatterScale,\n                                       Geometry::FlatCartesian>(\n      linearized_hamiltonian_constraint, linearized_lapse_equation,\n      linearized_momentum_constraint, conformal_momentum_density,\n      extrinsic_curvature_trace_gradient, std::nullopt, std::nullopt,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_flux, lapse_times_conformal_factor_flux,\n      longitudinal_shift_minus_dt_conformal_metric, conformal_factor_correction,\n      lapse_times_conformal_factor_correction, shift_correction,\n      conformal_factor_flux_correction,\n      lapse_times_conformal_factor_flux_correction,\n      longitudinal_shift_correction);\n}\n\n// Instantiate all function templates\n\n#define CONF_MATTER_SCALE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                \\\n  template void add_hamiltonian_sources<CONF_MATTER_SCALE(data)>(             \\\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,              \\\n      const Scalar<DataVector>& conformal_energy_density,                     \\\n      const Scalar<DataVector>& extrinsic_curvature_trace,                    \\\n      const Scalar<DataVector>& conformal_factor_minus_one);                  \\\n  template void add_linearized_hamiltonian_sources<CONF_MATTER_SCALE(data)>(  \\\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,   \\\n      const Scalar<DataVector>& conformal_energy_density,                     \\\n      const Scalar<DataVector>& extrinsic_curvature_trace,                    \\\n      const Scalar<DataVector>& conformal_factor_minus_one,                   \\\n      const Scalar<DataVector>& conformal_factor_correction);                 \\\n  template void add_lapse_sources<CONF_MATTER_SCALE(data)>(                   \\\n      gsl::not_null<Scalar<DataVector>*> lapse_equation,                      \\\n      const Scalar<DataVector>& conformal_energy_density,                     \\\n      const Scalar<DataVector>& conformal_stress_trace,                       \\\n      const Scalar<DataVector>& extrinsic_curvature_trace,                    \\\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,                 \\\n      const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,    \\\n      const Scalar<DataVector>& conformal_factor_minus_one,                   \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one);      \\\n  template void add_linearized_lapse_sources<CONF_MATTER_SCALE(data)>(        \\\n      gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,           \\\n      const Scalar<DataVector>& conformal_energy_density,                     \\\n      const Scalar<DataVector>& conformal_stress_trace,                       \\\n      const Scalar<DataVector>& extrinsic_curvature_trace,                    \\\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,                 \\\n      const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,    \\\n      const Scalar<DataVector>& conformal_factor_minus_one,                   \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,       \\\n      const Scalar<DataVector>& conformal_factor_correction,                  \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_correction);     \\\n  template void add_curved_momentum_sources<CONF_MATTER_SCALE(data)>(         \\\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,              \\\n      gsl::not_null<Scalar<DataVector>*> lapse_equation,                      \\\n      gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,             \\\n      const tnsr::I<DataVector, 3>& conformal_momentum_density,               \\\n      const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,       \\\n      const tnsr::ii<DataVector, 3>& conformal_metric,                        \\\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,                    \\\n      const tnsr::I<DataVector, 3>& minus_div_dt_conformal_metric,            \\\n      const Scalar<DataVector>& conformal_factor_minus_one,                   \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,       \\\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,                    \\\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,        \\\n      const tnsr::II<DataVector, 3>&                                          \\\n          longitudinal_shift_minus_dt_conformal_metric);                      \\\n  template void add_flat_cartesian_momentum_sources<CONF_MATTER_SCALE(data)>( \\\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,              \\\n      gsl::not_null<Scalar<DataVector>*> lapse_equation,                      \\\n      gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,             \\\n      const tnsr::I<DataVector, 3>& conformal_momentum_density,               \\\n      const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,       \\\n      const tnsr::I<DataVector, 3>& minus_div_dt_conformal_metric,            \\\n      const Scalar<DataVector>& conformal_factor_minus_one,                   \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,       \\\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,                    \\\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,        \\\n      const tnsr::II<DataVector, 3>&                                          \\\n          longitudinal_shift_minus_dt_conformal_metric);                      \\\n  template void                                                               \\\n  add_curved_linearized_momentum_sources<CONF_MATTER_SCALE(data)>(            \\\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,   \\\n      gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,           \\\n      gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,  \\\n      const tnsr::I<DataVector, 3>& conformal_momentum_density,               \\\n      const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,       \\\n      const tnsr::ii<DataVector, 3>& conformal_metric,                        \\\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,                    \\\n      const Scalar<DataVector>& conformal_factor_minus_one,                   \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,       \\\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,                    \\\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,        \\\n      const tnsr::II<DataVector, 3>&                                          \\\n          longitudinal_shift_minus_dt_conformal_metric,                       \\\n      const Scalar<DataVector>& conformal_factor_correction,                  \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_correction,      \\\n      const tnsr::I<DataVector, 3>& shift_correction,                         \\\n      const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,         \\\n      const tnsr::I<DataVector, 3>&                                           \\\n          lapse_times_conformal_factor_flux_correction,                       \\\n      const tnsr::II<DataVector, 3>& longitudinal_shift_correction);          \\\n  template void                                                               \\\n  add_flat_cartesian_linearized_momentum_sources<CONF_MATTER_SCALE(data)>(    \\\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,   \\\n      gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,           \\\n      gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,  \\\n      const tnsr::I<DataVector, 3>& conformal_momentum_density,               \\\n      const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,       \\\n      const Scalar<DataVector>& conformal_factor_minus_one,                   \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,       \\\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,                    \\\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,        \\\n      const tnsr::II<DataVector, 3>&                                          \\\n          longitudinal_shift_minus_dt_conformal_metric,                       \\\n      const Scalar<DataVector>& conformal_factor_correction,                  \\\n      const Scalar<DataVector>& lapse_times_conformal_factor_correction,      \\\n      const tnsr::I<DataVector, 3>& shift_correction,                         \\\n      const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,         \\\n      const tnsr::I<DataVector, 3>&                                           \\\n          lapse_times_conformal_factor_flux_correction,                       \\\n      const tnsr::II<DataVector, 3>& longitudinal_shift_correction);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (0, 6, 8))\n\n#undef CONF_MATTER_SCALE\n#undef INSTANTIATION\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Xcts {\nnamespace detail {\n// Tensor-contraction helper functions that should be replaced by tensor\n// expressions once those work\nvoid fully_contract_flat_cartesian(gsl::not_null<Scalar<DataVector>*> result,\n                                   const tnsr::II<DataVector, 3>& tensor1,\n                                   const tnsr::II<DataVector, 3>& tensor2);\nvoid fully_contract(gsl::not_null<Scalar<DataVector>*> result,\n                    gsl::not_null<DataVector*> buffer1,\n                    gsl::not_null<DataVector*> buffer2,\n                    const tnsr::II<DataVector, 3>& tensor1,\n                    const tnsr::II<DataVector, 3>& tensor2,\n                    const tnsr::ii<DataVector, 3>& metric);\n}  // namespace detail\n\n/*!\n * \\brief Add the nonlinear source to the Hamiltonian constraint on a flat\n * conformal background in Cartesian coordinates and with\n * \\f$\\bar{u}_{ij}=0=\\beta^i\\f$.\n *\n * Adds \\f$\\frac{1}{12}\\psi^5 K^2 - 2\\pi\\psi^{5-n}\\bar{\\rho}\\f$ where \\f$n\\f$ is\n * the `ConformalMatterScale` and \\f$\\bar{\\rho}=\\psi^n\\rho\\f$ is the\n * conformally-scaled energy density. Additional sources can be added with\n * `add_distortion_hamiltonian_sources` and\n * `add_curved_hamiltonian_or_lapse_sources`.\n *\n * \\see Xcts\n */\ntemplate <int ConformalMatterScale>\nvoid add_hamiltonian_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    const Scalar<DataVector>& conformal_energy_density,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const Scalar<DataVector>& conformal_factor_minus_one);\n\n/// The linearization of `add_hamiltonian_sources`\ntemplate <int ConformalMatterScale>\nvoid add_linearized_hamiltonian_sources(\n    gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    const Scalar<DataVector>& conformal_energy_density,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& conformal_factor_correction);\n\n/*!\n * \\brief Add the \"distortion\" source term to the Hamiltonian constraint.\n *\n * Adds \\f$-\\frac{1}{8}\\psi^{-7}\\bar{A}^{ij}\\bar{A}_{ij}\\f$.\n *\n * \\see Xcts\n * \\see Xcts::Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare\n */\nvoid add_distortion_hamiltonian_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const Scalar<DataVector>& conformal_factor_minus_one);\n\n/*!\n * \\brief The linearization of `add_distortion_hamiltonian_sources`\n *\n * Note that this linearization is only w.r.t. \\f$\\psi\\f$.\n */\nvoid add_linearized_distortion_hamiltonian_sources(\n    gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& conformal_factor_correction);\n\n/*!\n * \\brief Add the contributions from a curved background geometry to the\n * Hamiltonian constraint or lapse equation\n *\n * Adds \\f$\\frac{1}{8}\\psi\\bar{R}\\f$. This term appears both in the Hamiltonian\n * constraint and the lapse equation (where in the latter \\f$\\psi\\f$ is replaced\n * by \\f$\\alpha\\psi\\f$).\n *\n * This term is linear.\n *\n * \\see Xcts\n */\nvoid add_curved_hamiltonian_or_lapse_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_or_lapse_equation,\n    const Scalar<DataVector>& conformal_ricci_scalar,\n    const Scalar<DataVector>& field, double add_constant = 0.);\n\n/*!\n * \\brief Add the nonlinear source to the lapse equation on a flat conformal\n * background in Cartesian coordinates and with \\f$\\bar{u}_{ij}=0=\\beta^i\\f$.\n *\n * Adds \\f$(\\alpha\\psi)\\left(\\frac{5}{12}\\psi^4 K^2 + 2\\pi\\psi^{4-n}\n * \\left(\\bar{\\rho} + 2\\bar{S}\\right)\\right) + \\psi^5\n * \\left(\\beta^i\\partial_i K - \\partial_t K\\right)\\f$ where \\f$n\\f$ is the\n * `ConformalMatterScale` and matter quantities are conformally-scaled.\n * Additional sources can be added with\n * `add_distortion_hamiltonian_and_lapse_sources` and\n * `add_curved_hamiltonian_or_lapse_sources`.\n *\n * \\see Xcts\n */\ntemplate <int ConformalMatterScale>\nvoid add_lapse_sources(\n    gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const Scalar<DataVector>& conformal_energy_density,\n    const Scalar<DataVector>& conformal_stress_trace,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n    const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one);\n\n/*!\n * \\brief The linearization of `add_lapse_sources`\n *\n * Note that this linearization is only w.r.t. \\f$\\psi\\f$ and \\f$\\alpha\\psi\\f$.\n * The linearization w.r.t. \\f$\\beta^i\\f$ is added in\n * `add_curved_linearized_momentum_sources` or\n * `add_flat_cartesian_linearized_momentum_sources`.\n */\ntemplate <int ConformalMatterScale>\nvoid add_linearized_lapse_sources(\n    gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n    const Scalar<DataVector>& conformal_energy_density,\n    const Scalar<DataVector>& conformal_stress_trace,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n    const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction);\n\n/*!\n * \\brief Add the \"distortion\" source term to the Hamiltonian constraint and the\n * lapse equation.\n *\n * Adds \\f$-\\frac{1}{8}\\psi^{-7}\\bar{A}^{ij}\\bar{A}_{ij}\\f$ to the Hamiltonian\n * constraint and \\f$\\frac{7}{8}\\alpha\\psi^{-7}\\bar{A}^{ij}\\bar{A}_{ij}\\f$ to\n * the lapse equation.\n *\n * \\see Xcts\n * \\see Xcts::Tags::LongitudinalShiftMinusDtConformalMetricSquare\n */\nvoid add_distortion_hamiltonian_and_lapse_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_square,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one);\n\n/*!\n * \\brief The linearization of `add_distortion_hamiltonian_and_lapse_sources`\n *\n * Note that this linearization is only w.r.t. \\f$\\psi\\f$ and \\f$\\alpha\\psi\\f$.\n */\nvoid add_linearized_distortion_hamiltonian_and_lapse_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_square,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction);\n\n/*!\n * \\brief Add the nonlinear source to the momentum constraint and add the\n * \"distortion\" source term to the Hamiltonian constraint and lapse equation.\n *\n * Adds \\f$\\left((\\bar{L}\\beta)^{ij} - \\bar{u}^{ij}\\right)\n * \\left(\\frac{\\partial_j (\\alpha\\psi)}{\\alpha\\psi}\n * - 7 \\frac{\\partial_j \\psi}{\\psi}\\right)\n * + \\partial_j\\bar{u}^{ij}\n * + \\frac{4}{3}\\frac{\\alpha\\psi}{\\psi}\\bar{\\gamma}^{ij}\\partial_j K\n * + 16\\pi\\left(\\alpha\\psi\\right)\\psi^{3-n} \\bar{S}^i\\f$ to the momentum\n * constraint, where \\f$n\\f$ is the `ConformalMatterScale` and\n * \\f$\\bar{S}^i=\\psi^n S^i\\f$ is the conformally-scaled momentum density.\n *\n * Note that the \\f$\\partial_j\\bar{u}^{ij}\\f$ term is not the full covariant\n * divergence, but only the partial-derivatives part of it. The curved\n * contribution to this term can be added together with the curved contribution\n * to the flux divergence of the dynamic shift variable with the\n * `Elasticity::add_curved_sources` function.\n *\n * Also adds \\f$-\\frac{1}{8}\\psi^{-7}\\bar{A}^{ij}\\bar{A}_{ij}\\f$ to the\n * Hamiltonian constraint and\n * \\f$\\frac{7}{8}\\alpha\\psi^{-7}\\bar{A}^{ij}\\bar{A}_{ij}\\f$ to the lapse\n * equation.\n *\n * \\see Xcts\n */\n/// @{\ntemplate <int ConformalMatterScale>\nvoid add_curved_momentum_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::I<DataVector, 3>& minus_div_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>&\n        longitudinal_shift_minus_dt_conformal_metric);\n\ntemplate <int ConformalMatterScale>\nvoid add_flat_cartesian_momentum_sources(\n    gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> lapse_equation,\n    gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const tnsr::I<DataVector, 3>& minus_div_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>&\n        longitudinal_shift_minus_dt_conformal_metric);\n/// @}\n\n/// The linearization of `add_curved_momentum_sources`\ntemplate <int ConformalMatterScale>\nvoid add_curved_linearized_momentum_sources(\n    gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n    gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n    const tnsr::I<DataVector, 3>& shift_correction,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux_correction,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_correction);\n\n/// The linearization of `add_flat_cartesian_momentum_sources`\ntemplate <int ConformalMatterScale>\nvoid add_flat_cartesian_linearized_momentum_sources(\n    gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n    gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n    gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,\n    const tnsr::I<DataVector, 3>& conformal_momentum_density,\n    const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n    const tnsr::I<DataVector, 3>& shift_correction,\n    const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n    const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux_correction,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_correction);\n}  // namespace Xcts\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY XctsEvents)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ObserveAdmIntegrals.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ObserveAdmIntegrals.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  Elliptic\n  Xcts\n  Options\n  Serialization\n  Utilities\n  XctsPointwiseFunctions\n  )\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/Events/ObserveAdmIntegrals.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Xcts/Events/ObserveAdmIntegrals.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Xcts/AdmMass.hpp\"\n#include \"PointwiseFunctions/Xcts/AdmMomentum.hpp\"\n#include \"PointwiseFunctions/Xcts/CenterOfMass.hpp\"\n\nnamespace Events {\n\nvoid local_adm_integrals(\n    gsl::not_null<Scalar<double>*> adm_mass,\n    gsl::not_null<tnsr::I<double, 3>*> adm_linear_momentum,\n    gsl::not_null<Scalar<double>*> adm_angular_momentum_z,\n    gsl::not_null<tnsr::I<double, 3>*> center_of_mass,\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const tnsr::II<DataVector, 3>& inv_spatial_metric,\n    const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian,\n    const Mesh<3>& mesh, const Element<3>& element,\n    const DirectionMap<3, tnsr::i<DataVector, 3>>& conformal_face_normals) {\n  // Initialize integrals to 0\n  adm_mass->get() = 0;\n  adm_angular_momentum_z->get() = 0;\n  for (int I = 0; I < 3; I++) {\n    adm_linear_momentum->get(I) = 0;\n    center_of_mass->get(I) = 0;\n  }\n\n  // Compute quantities not in the data box\n  tnsr::II<DataVector, 3> inv_extrinsic_curvature;\n  tenex::evaluate<ti::I, ti::J>(make_not_null(&inv_extrinsic_curvature),\n                                inv_spatial_metric(ti::I, ti::K) *\n                                    inv_spatial_metric(ti::J, ti::L) *\n                                    extrinsic_curvature(ti::k, ti::l));\n\n  // Get integrands\n  const auto linear_momentum_surface_integrand =\n      Xcts::adm_linear_momentum_surface_integrand(\n          conformal_factor, inv_spatial_metric, inv_extrinsic_curvature,\n          trace_extrinsic_curvature);\n  const auto linear_momentum_volume_integrand =\n      Xcts::adm_linear_momentum_volume_integrand(\n          linear_momentum_surface_integrand, conformal_factor,\n          deriv_conformal_factor, conformal_metric, inv_conformal_metric,\n          conformal_christoffel_second_kind, conformal_christoffel_contracted);\n  const auto angular_momentum_z_volume_integrand =\n      Xcts::adm_angular_momentum_z_volume_integrand(\n          linear_momentum_volume_integrand, inertial_coords);\n\n  // Evaluate volume integrals.\n  const auto det_jacobian =\n      Scalar<DataVector>(1. / get(determinant(inv_jacobian)));\n  adm_angular_momentum_z->get() += definite_integral(\n        get(angular_momentum_z_volume_integrand) * get(det_jacobian), mesh);\n  for (int I = 0; I < 3; I++) {\n    adm_linear_momentum->get(I) += definite_integral(\n        linear_momentum_volume_integrand.get(I) * get(det_jacobian), mesh);\n  }\n\n  // Loop over external boundaries\n  for (const auto boundary_direction : element.external_boundaries()) {\n    // Skip non-zeta boundaries\n    if (boundary_direction.dimension() != 2) {\n        continue;\n    }\n\n    // Project fields to the boundary\n    const auto face_conformal_factor_minus_one = dg::project_tensor_to_boundary(\n        conformal_factor_minus_one, mesh, boundary_direction);\n    const auto face_deriv_conformal_factor = dg::project_tensor_to_boundary(\n        deriv_conformal_factor, mesh, boundary_direction);\n    const auto face_conformal_metric = dg::project_tensor_to_boundary(\n        conformal_metric, mesh, boundary_direction);\n    const auto face_inv_conformal_metric = dg::project_tensor_to_boundary(\n        inv_conformal_metric, mesh, boundary_direction);\n    const auto face_conformal_christoffel_second_kind =\n        dg::project_tensor_to_boundary(conformal_christoffel_second_kind,\n                                        mesh, boundary_direction);\n    const auto face_conformal_christoffel_contracted =\n        dg::project_tensor_to_boundary(conformal_christoffel_contracted, mesh,\n                                        boundary_direction);\n    const auto face_spatial_metric = dg::project_tensor_to_boundary(\n        spatial_metric, mesh, boundary_direction);\n    const auto face_inv_spatial_metric = dg::project_tensor_to_boundary(\n        inv_spatial_metric, mesh, boundary_direction);\n    const auto face_extrinsic_curvature = dg::project_tensor_to_boundary(\n        extrinsic_curvature, mesh, boundary_direction);\n    const auto face_trace_extrinsic_curvature =\n        dg::project_tensor_to_boundary(trace_extrinsic_curvature, mesh,\n                                        boundary_direction);\n    const auto face_inertial_coords = dg::project_tensor_to_boundary(\n        inertial_coords, mesh, boundary_direction);\n    // This projection could be avoided by using\n    // domain::Tags::DetSurfaceJacobian from the DataBox, which is computed\n    // directly on the face (not projected). That would be better on Gauss\n    // meshes that have no grid point at the boundary. The DetSurfaceJacobian\n    // could then be multiplied by the (conformal) metric determinant to form\n    // the area element. Note that the DetSurfaceJacobian is computed using\n    // the conformal metric.\n    const auto face_inv_jacobian = dg::project_tensor_to_boundary(\n        inv_jacobian, mesh, boundary_direction);\n\n    // Get interface mesh and normal\n    const auto& face_mesh = mesh.slice_away(boundary_direction.dimension());\n    const auto& conformal_face_normal =\n        conformal_face_normals.at(boundary_direction);\n    const auto face_normal_magnitude = magnitude(conformal_face_normal);\n    const auto flat_face_normal = tenex::evaluate<ti::i>(\n        conformal_face_normal(ti::i) / face_normal_magnitude());\n\n    // Compute curved and flat area elements\n    const auto face_sqrt_det_conformal_metric =\n        Scalar<DataVector>(sqrt(get(determinant(face_conformal_metric))));\n    const auto conformal_area_element = area_element(\n        face_inv_jacobian, boundary_direction, face_inv_conformal_metric,\n        face_sqrt_det_conformal_metric);\n    const auto flat_area_element =\n        euclidean_area_element(face_inv_jacobian, boundary_direction);\n\n    // Interfaces at the inner boundary\n    if (boundary_direction == Direction<3>::lower_zeta()) {\n      // Compute surface integrands\n      const auto face_linear_momentum_surface_integrand =\n          dg::project_tensor_to_boundary(linear_momentum_surface_integrand,\n                                         mesh, boundary_direction);\n      const auto angular_momentum_z_surface_integrand =\n          Xcts::adm_angular_momentum_z_surface_integrand(\n              face_linear_momentum_surface_integrand, face_inertial_coords);\n\n      // Contract surface integrands with face normal\n      const auto contracted_linear_momentum_integrand = tenex::evaluate<ti::I>(\n          -face_linear_momentum_surface_integrand(ti::I, ti::J) *\n          flat_face_normal(ti::j));\n      const auto contracted_angular_momentum_z_integrand =\n          tenex::evaluate(-angular_momentum_z_surface_integrand(ti::I) *\n                          flat_face_normal(ti::i));\n\n      // Take integrals\n      adm_angular_momentum_z->get() += definite_integral(\n          get(contracted_angular_momentum_z_integrand) * get(flat_area_element),\n          face_mesh);\n      for (int I = 0; I < 3; I++) {\n        adm_linear_momentum->get(I) +=\n            definite_integral(contracted_linear_momentum_integrand.get(I) *\n                                  get(flat_area_element),\n                              face_mesh);\n      }\n    }\n\n    // Interfaces at the outer boundary\n    if (boundary_direction == Direction<3>::upper_zeta()) {\n      // Compute surface integrands\n      const auto mass_surface_integrand = Xcts::adm_mass_surface_integrand(\n          face_deriv_conformal_factor, face_inv_conformal_metric,\n          face_conformal_christoffel_second_kind,\n          face_conformal_christoffel_contracted);\n      const auto center_of_mass_surface_integrand =\n          Xcts::center_of_mass_surface_integrand(\n              face_conformal_factor_minus_one, face_inertial_coords);\n\n      // Contract surface integrands with face normal\n      const auto contracted_mass_integrand = tenex::evaluate(\n          mass_surface_integrand(ti::I) * conformal_face_normal(ti::i));\n\n      // Take integrals\n      adm_mass->get() += definite_integral(\n          get(contracted_mass_integrand) * get(conformal_area_element),\n          face_mesh);\n      for (int I = 0; I < 3; I++) {\n        center_of_mass->get(I) += definite_integral(\n            center_of_mass_surface_integrand.get(I) * get(flat_area_element),\n            face_mesh);\n      }\n    }\n  }\n}\n\n}  // namespace Events\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/Events/ObserveAdmIntegrals.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace Events {\n\n/// @{\n/*!\n * \\brief Computes the ADM integrals locally (within one element).\n *\n * To get the total ADM integrals, the results need to be summed over in a\n * reduction.\n */\nvoid local_adm_integrals(\n    gsl::not_null<Scalar<double>*> adm_mass,\n    gsl::not_null<tnsr::I<double, 3>*> adm_linear_momentum,\n    gsl::not_null<Scalar<double>*> adm_angular_momentum_z,\n    gsl::not_null<tnsr::I<double, 3>*> center_of_mass,\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const tnsr::II<DataVector, 3>& inv_spatial_metric,\n    const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian,\n    const Mesh<3>& mesh, const Element<3>& element,\n    const DirectionMap<3, tnsr::i<DataVector, 3>>& conformal_face_normals);\n/// @}\n\n/// @{\n/*!\n * \\brief Observe ADM integrals after the XCTS solve.\n *\n * The surface integrals are taken over the outer boundary, which is defined as\n * the domain boundary in the upper logical zeta direction.\n *\n * Writes reduction quantities:\n * - Number of points in the domain\n * - ADM mass\n * - ADM linear momentum\n * - ADM angular momentum (z-component)\n * - Center of mass\n */\ntemplate <typename ArraySectionIdTag = void>\nclass ObserveAdmIntegrals : public Event {\n private:\n  using ReductionData = Parallel::ReductionData<\n      // Number of points\n      Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n      // ADM Mass\n      Parallel::ReductionDatum<double, funcl::Plus<>>,\n      // ADM Linear Momentum (x-component)\n      Parallel::ReductionDatum<double, funcl::Plus<>>,\n      // ADM Linear Momentum (y-component)\n      Parallel::ReductionDatum<double, funcl::Plus<>>,\n      // ADM Linear Momentum (z-component)\n      Parallel::ReductionDatum<double, funcl::Plus<>>,\n      // ADM Angular Momentum (z-component)\n      Parallel::ReductionDatum<double, funcl::Plus<>>,\n      // Center of Mass (x-component)\n      Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,\n                               std::index_sequence<1>>,\n      // Center of Mass (y-component)\n      Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,\n                               std::index_sequence<1>>,\n      // Center of Mass (z-component)\n      Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,\n                               std::index_sequence<1>>>;\n\n public:\n  /// \\cond\n  explicit ObserveAdmIntegrals(CkMigrateMessage* msg) : Event(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveAdmIntegrals);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help =\n      \"Observe ADM integrals after the XCTS solve.\\n\"\n      \"\\n\"\n      \"Writes reduction quantities:\\n\"\n      \"- Number of points in the domain\\n\"\n      \"- ADM mass\\n\"\n      \"- ADM linear momentum\\n\"\n      \"- ADM angular momentum (z-component)\\n\"\n      \"- Center of mass\";\n\n  ObserveAdmIntegrals() = default;\n\n  using observed_reduction_data_tags =\n      observers::make_reduction_data_tags<tmpl::list<ReductionData>>;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n\n  using argument_tags = tmpl::list<\n      Xcts::Tags::ConformalFactor<DataVector>,\n      Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n      ::Tags::deriv<Xcts::Tags::ConformalFactor<DataVector>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n      Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n      Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                 Frame::Inertial>,\n      Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                 Frame::Inertial>,\n      gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>,\n      gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame::Inertial>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      domain::Tags::Coordinates<3, Frame::Inertial>,\n      domain::Tags::InverseJacobian<3, Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::Mesh<3>, domain::Tags::Element<3>,\n      domain::Tags::Faces<3, domain::Tags::FaceNormal<3>>,\n      ::Tags::ObservationBox>;\n\n  template <typename DataBoxType, typename ComputeTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(\n      const Scalar<DataVector>& conformal_factor,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n      const tnsr::ii<DataVector, 3>& conformal_metric,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const tnsr::ii<DataVector, 3>& spatial_metric,\n      const tnsr::II<DataVector, 3>& inv_spatial_metric,\n      const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n      const Scalar<DataVector>& trace_extrinsic_curvature,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const Mesh<3>& mesh, const Element<3>& element,\n      const DirectionMap<3, tnsr::i<DataVector, 3>>& conformal_face_normals,\n      const ObservationBox<DataBoxType, ComputeTagsList>& box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ParallelComponent* const /*meta*/,\n      const ObservationValue& observation_value) const {\n    // Skip observation on elements that are not part of a section\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (not section_observation_key.has_value()) {\n      return;\n    }\n    const std::string subfile_path = subfile_path_ + *section_observation_key;\n\n    Scalar<double> adm_mass;\n    tnsr::I<double, 3> adm_linear_momentum;\n    Scalar<double> adm_angular_momentum_z;\n    tnsr::I<double, 3> center_of_mass;\n    local_adm_integrals(\n        make_not_null(&adm_mass), make_not_null(&adm_linear_momentum),\n        make_not_null(&adm_angular_momentum_z), make_not_null(&center_of_mass),\n        conformal_factor, conformal_factor_minus_one, deriv_conformal_factor,\n        conformal_metric, inv_conformal_metric,\n        conformal_christoffel_second_kind, conformal_christoffel_contracted,\n        spatial_metric, inv_spatial_metric, extrinsic_curvature,\n        trace_extrinsic_curvature, inertial_coords, inv_jacobian, mesh, element,\n        conformal_face_normals);\n\n    // Save components of linear momentum as reduction data\n    ReductionData reduction_data{\n        mesh.number_of_grid_points(), get(adm_mass),\n        get<0>(adm_linear_momentum),  get<1>(adm_linear_momentum),\n        get<2>(adm_linear_momentum),  get(adm_angular_momentum_z),\n        get<0>(center_of_mass),       get<1>(center_of_mass),\n        get<2>(center_of_mass)};\n    std::vector<std::string> legend{\n        \"NumberOfPoints\",      \"AdmMass\",\n        \"AdmLinearMomentum_x\", \"AdmLinearMomentum_y\",\n        \"AdmLinearMomentum_z\", \"AdmAngularMomentum_z\",\n        \"CenterOfMass_x\",      \"CenterOfMass_y\",\n        \"CenterOfMass_z\"};\n\n    // Get information required for reduction\n    auto& local_observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            tmpl::conditional_t<Parallel::is_nodegroup_v<ParallelComponent>,\n                                observers::ObserverWriter<Metavariables>,\n                                observers::Observer<Metavariables>>>(cache));\n    observers::ObservationId observation_id{observation_value.value,\n                                            subfile_path + \".dat\"};\n    Parallel::ArrayComponentId array_component_id{\n        std::add_pointer_t<ParallelComponent>{nullptr},\n        Parallel::ArrayIndex<ElementId<3>>(array_index)};\n\n    // Send reduction action\n    if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {\n      Parallel::threaded_action<\n          observers::ThreadedActions::CollectReductionDataOnNode>(\n          local_observer, std::move(observation_id),\n          std::move(array_component_id), subfile_path, std::move(legend),\n          std::move(reduction_data));\n    } else {\n      Parallel::simple_action<observers::Actions::ContributeReductionData>(\n          local_observer, std::move(observation_id),\n          std::move(array_component_id), subfile_path, std::move(legend),\n          std::move(reduction_data));\n    }\n  }\n\n  using observation_registration_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList>\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration(\n      const db::DataBox<DbTagsList>& box) const {\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (not section_observation_key.has_value()) {\n      return std::nullopt;\n    }\n    return {{observers::TypeOfObservation::Reduction,\n             observers::ObservationKey(\n                 subfile_path_ + section_observation_key.value() + \".dat\")}};\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | subfile_path_;\n  }\n\n private:\n  std::string subfile_path_ = \"/AdmIntegrals\";\n};\n/// @}\n\n/// \\cond\ntemplate <typename ArraySectionIdTag>\nPUP::able::PUP_ID ObserveAdmIntegrals<ArraySectionIdTag>::my_PUP_ID =\n    0;  // NOLINT\n/// \\endcond\n\n}  // namespace Events\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Xcts/FluxesAndSources.hpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts {\n\n/*!\n * \\brief The Extended Conformal Thin Sandwich (XCTS) decomposition of the\n * Einstein constraint equations, formulated as a set of coupled first-order\n * partial differential equations\n *\n * See \\ref Xcts for details on the XCTS equations.\n * The system can be formulated in terms of these fluxes and sources (see\n * `elliptic::protocols::FirstOrderSystem`):\n *\n * \\f{align}\n * F^i_\\psi ={} &\\bar{\\gamma}^{ij} \\partial_j \\psi \\\\\n * S_\\psi ={} &-\\bar{\\Gamma}^i_{ij} F^j_\\psi\n * + \\frac{1}{8}\\psi\\bar{R} + \\frac{1}{12}\\psi^5 K^2\n * - \\frac{1}{8}\\psi^{-7}\\bar{A}^2 - 2\\pi\\psi^5\\rho\n * \\f}\n *\n * for the Hamiltonian constraint,\n *\n * \\f{align}\n * F^i_{\\alpha\\psi} ={} &\\bar{\\gamma}^{ij} \\partial_j \\alpha\\psi \\\\\n * S_{\\alpha\\psi} ={} &-\\bar{\\Gamma}^i_{ij} F^j_{\\alpha\\psi}\n * + \\alpha\\psi \\left(\\frac{7}{8}\\psi^{-8} \\bar{A}^2\n * + \\frac{5}{12} \\psi^4 K^2 + \\frac{1}{8}\\bar{R}\n * + 2\\pi\\psi^4\\left(\\rho + 2S\\right) \\right) \\\\\n * &- \\psi^5\\partial_t K + \\psi^5\\left(\\beta^i\\bar{D}_i K\n * + \\beta_\\mathrm{background}^i\\bar{D}_i K\\right)\n * \\f}\n *\n * for the lapse equation, and\n *\n * \\f{align}\n * F^{ij}_\\beta ={} &\\left(\\bar{L}\\beta\\right)^{ij} = \\bar{\\nabla}^i \\beta^j\n * + \\bar{\\nabla}^j \\beta^i\n * - \\frac{2}{3} \\bar{\\gamma}^{ij} \\bar{\\nabla}_k \\beta^k \\\\\n * S^i_\\beta ={} &-\\bar{\\Gamma}^j_{jk} F^{ik}_\\beta\n * - \\bar{\\Gamma}^i_{jk} F^{jk}_\\beta\n * + \\left(F^{ij}_\\beta\n * + \\left(\\bar{L}\\beta_\\mathrm{background}\\right)^{ij} - \\bar{u}^{ij}\\right)\n * \\bar{\\gamma}_{jk} \\left(\\frac{F^k_{\\alpha\\psi}}{\\alpha\\psi}\n * - 7 \\frac{F^k_\\psi}{\\psi}\\right) \\\\\n * &- \\bar{D}_j\\left(\\left(\\bar{L}\\beta_\\mathrm{background}\\right)^{ij}\n * - \\bar{u}^{ij}\\right)\n * + \\frac{4}{3}\\frac{\\alpha\\psi}{\\psi}\\bar{D}^i K\n * + 16\\pi\\left(\\alpha\\psi\\right)\\psi^3 S^i\n * \\f}\n *\n * for the momentum constraint, with\n *\n * \\f{align}\n * \\bar{A}^{ij} ={} &\\frac{\\psi^7}{2\\alpha\\psi}\\left(\n * \\left(\\bar{L}\\beta\\right)^{ij} +\n * \\left(\\bar{L}\\beta_\\mathrm{background}\\right)^{ij} - \\bar{u}^{ij} \\right)\n * \\f}\n *\n * and all \\f$f_\\alpha=0\\f$.\n *\n * Note that the symbol \\f$\\beta\\f$ in the equations above means\n * \\f$\\beta_\\mathrm{excess}\\f$. The full shift is \\f$\\beta_\\mathrm{excess} +\n * \\beta_\\mathrm{background}\\f$. See `Xcts::Tags::ShiftBackground` and\n * `Xcts::Tags::ShiftExcess` for details on this split. Also note that the\n * background shift is degenerate with \\f$\\bar{u}\\f$ so we treat the quantity\n * \\f$\\left(\\bar{L}\\beta_\\mathrm{background}\\right)^{ij} - \\bar{u}^{ij}\\f$ as a\n * single background field (see\n * `Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric`). The\n * covariant divergence of this quantity w.r.t. the conformal metric is also a\n * background field.\n *\n * \\par Solving a subset of equations:\n * This system allows you to select a subset of `Xcts::Equations` so you don't\n * have to solve for all variables if some are analytically known. Specify the\n * set of enabled equations as the first template parameter. The set of required\n * background fields depends on your choice of equations.\n *\n * \\par Conformal background geometry:\n * The equations simplify significantly if the conformal metric is flat\n * (\"conformal flatness\") and in Cartesian coordinates. In this case you can\n * specify `Xcts::Geometry::FlatCartesian` as the second template parameter so\n * computations are optimized for a flat background geometry and you don't have\n * to supply geometric background fields. Else, specify\n * `Xcts::Geometry::Curved`.\n *\n * \\par Conformal matter scale:\n * The matter source terms in the XCTS equations have the known defect that they\n * can spoil uniqueness of the solutions. See e.g. \\cite Baumgarte2006ug for a\n * detailed study. To cure this defect one can conformally re-scale the matter\n * source terms as \\f$\\bar{\\rho}=\\psi^n\\rho\\f$, \\f$\\bar{S}=\\psi^n S\\f$ and\n * \\f$\\bar{S^i}=\\psi^n S^i\\f$ and treat the re-scaled fields as\n * freely-specifyable background data for the XCTS equations. You can select the\n * `ConformalMatterScale` \\f$n\\f$ as the third template parameter. Common\n * choices are \\f$n=0\\f$ for vacuum systems where the matter sources are\n * irrelevant, \\f$n=6\\f$ as suggested in \\cite Foucart2008qt or \\f$n=8\\f$ as\n * suggested in \\cite Baumgarte2006ug.\n */\ntemplate <Equations EnabledEquations, Geometry ConformalGeometry,\n          int ConformalMatterScale>\nstruct FirstOrderSystem\n    : tt::ConformsTo<elliptic::protocols::FirstOrderSystem> {\n  static constexpr Equations enabled_equations = EnabledEquations;\n  static constexpr Geometry conformal_geometry = ConformalGeometry;\n  static constexpr int conformal_matter_scale = ConformalMatterScale;\n  static constexpr size_t volume_dim = 3;\n\n  using primal_fields = tmpl::flatten<tmpl::list<\n      Tags::ConformalFactorMinusOne<DataVector>,\n      tmpl::conditional_t<\n          EnabledEquations == Equations::HamiltonianAndLapse or\n              EnabledEquations == Equations::HamiltonianLapseAndShift,\n          Tags::LapseTimesConformalFactorMinusOne<DataVector>, tmpl::list<>>,\n      tmpl::conditional_t<\n          EnabledEquations == Equations::HamiltonianLapseAndShift,\n          Tags::ShiftExcess<DataVector, 3, Frame::Inertial>, tmpl::list<>>>>;\n\n  // As fluxes we use the gradients with raised indices for the Hamiltonian and\n  // lapse equation, and the longitudinal shift excess for the momentum\n  // constraint. The gradient fluxes don't have symmetries and no particular\n  // meaning so we use the standard `Flux` tags, but for the symmetric\n  // longitudinal shift we use the corresponding symmetric tag.\n  using primal_fluxes = tmpl::flatten<tmpl::list<\n      ::Tags::Flux<Tags::ConformalFactorMinusOne<DataVector>, tmpl::size_t<3>,\n                   Frame::Inertial>,\n      tmpl::conditional_t<\n          EnabledEquations == Equations::HamiltonianAndLapse or\n              EnabledEquations == Equations::HamiltonianLapseAndShift,\n          ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n                       tmpl::size_t<3>, Frame::Inertial>,\n          tmpl::list<>>,\n      tmpl::conditional_t<\n          EnabledEquations == Equations::HamiltonianLapseAndShift,\n          Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>,\n          tmpl::list<>>>>;\n\n  using background_fields = tmpl::flatten<tmpl::list<\n      // Quantities for Hamiltonian constraint\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>,\n                          ConformalMatterScale>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      tmpl::conditional_t<\n          ConformalGeometry == Geometry::Curved,\n          tmpl::list<\n              Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n              Tags::ConformalRicciTensor<DataVector, 3, Frame::Inertial>,\n              Tags::ConformalRicciScalar<DataVector>,\n              Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                   Frame::Inertial>,\n              ::Tags::deriv<\n                  Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>>,\n          tmpl::list<>>,\n      tmpl::conditional_t<\n          EnabledEquations ==\n              Equations::Hamiltonian,\n          Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<\n              DataVector>,\n          tmpl::list<>>,\n      // Additional quantities for lapse equation\n      tmpl::conditional_t<\n          EnabledEquations == Equations::HamiltonianAndLapse or\n              EnabledEquations ==\n                  Equations::HamiltonianLapseAndShift,\n          tmpl::list<gr::Tags::Conformal<gr::Tags::StressTrace<DataVector>,\n                                         ConformalMatterScale>,\n                     ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataVector>>>,\n          tmpl::list<>>,\n      tmpl::conditional_t<\n          EnabledEquations ==\n              Equations::HamiltonianAndLapse,\n          tmpl::list<\n              Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataVector>,\n              Tags::ShiftDotDerivExtrinsicCurvatureTrace<DataVector>>,\n          tmpl::list<>>,\n      // Additional quantities for momentum constraint\n      tmpl::conditional_t<\n          EnabledEquations ==\n              Equations::HamiltonianLapseAndShift,\n          tmpl::list<\n              gr::Tags::Conformal<gr::Tags::MomentumDensity<DataVector, 3>,\n                                  ConformalMatterScale>,\n              ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                            tmpl::size_t<3>, Frame::Inertial>,\n              Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n              // The deriv(ShiftBackground) is only needed for some observables,\n              // not to evaluate the equations\n              ::Tags::deriv<\n                  Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n              Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n                  DataVector, 3, Frame::Inertial>,\n              // Note that this is the plain divergence, i.e. with no\n              // Christoffel symbol terms added\n              ::Tags::div<\n                  Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n                      DataVector, 3, Frame::Inertial>>,\n              tmpl::conditional_t<\n                  ConformalGeometry == Geometry::Curved,\n                  tmpl::list<\n                      Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n                      Tags::ConformalChristoffelFirstKind<DataVector, 3,\n                                                          Frame::Inertial>,\n                      Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                           Frame::Inertial>>,\n                  tmpl::list<>>>,\n          tmpl::list<>>>>;\n  using inv_metric_tag = tmpl::conditional_t<\n      ConformalGeometry == Geometry::FlatCartesian, void,\n      Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>;\n\n  using fluxes_computer = Fluxes<EnabledEquations, ConformalGeometry>;\n  using sources_computer =\n      Sources<EnabledEquations, ConformalGeometry, ConformalMatterScale>;\n  using sources_computer_linearized =\n      LinearizedSources<EnabledEquations, ConformalGeometry,\n                        ConformalMatterScale>;\n\n  using boundary_conditions_base =\n      elliptic::BoundaryConditions::BoundaryCondition<3>;\n  using modify_boundary_data = void;\n};\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/FluxesAndSources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Elliptic/Systems/Xcts/FluxesAndSources.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Elasticity/Equations.hpp\"\n#include \"Elliptic/Systems/Poisson/Equations.hpp\"\n#include \"Elliptic/Systems/Xcts/Equations.hpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"PointwiseFunctions/Xcts/LongitudinalOperator.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n\nnamespace Xcts {\n\nvoid Fluxes<Equations::Hamiltonian, Geometry::FlatCartesian>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const Scalar<DataVector>& /*conformal_factor_minus_one*/,\n    const tnsr::i<DataVector, 3>& conformal_factor_gradient) {\n  Poisson::flat_cartesian_fluxes(flux_for_conformal_factor,\n                                 conformal_factor_gradient);\n}\n\nvoid Fluxes<Equations::Hamiltonian, Geometry::FlatCartesian>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const tnsr::i<DataVector, 3>& /*face_normal*/,\n    const tnsr::I<DataVector, 3>& face_normal_vector,\n    const Scalar<DataVector>& conformal_factor_minus_one) {\n  Poisson::fluxes_on_face(flux_for_conformal_factor, face_normal_vector,\n                          conformal_factor_minus_one);\n}\n\nvoid Fluxes<Equations::Hamiltonian, Geometry::Curved>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const Scalar<DataVector>& /*conformal_factor*/,\n    const tnsr::i<DataVector, 3>& conformal_factor_gradient) {\n  Poisson::curved_fluxes(flux_for_conformal_factor, inv_conformal_metric,\n                         conformal_factor_gradient);\n}\n\nvoid Fluxes<Equations::Hamiltonian, Geometry::Curved>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const tnsr::II<DataVector, 3>& /*inv_conformal_metric*/,\n    const tnsr::i<DataVector, 3>& /*face_normal*/,\n    const tnsr::I<DataVector, 3>& face_normal_vector,\n    const Scalar<DataVector>& conformal_factor_minus_one) {\n  Poisson::fluxes_on_face(flux_for_conformal_factor, face_normal_vector,\n                          conformal_factor_minus_one);\n}\n\nvoid Fluxes<Equations::HamiltonianAndLapse, Geometry::FlatCartesian>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        flux_for_lapse_times_conformal_factor,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& /*lapse_times_conformal_factor_minus_one*/,\n    const tnsr::i<DataVector, 3>& conformal_factor_gradient,\n    const tnsr::i<DataVector, 3>& lapse_times_conformal_factor_gradient) {\n  Fluxes<Equations::Hamiltonian, Geometry::FlatCartesian>::apply(\n      flux_for_conformal_factor, conformal_factor_minus_one,\n      conformal_factor_gradient);\n  Poisson::flat_cartesian_fluxes(flux_for_lapse_times_conformal_factor,\n                                 lapse_times_conformal_factor_gradient);\n}\n\nvoid Fluxes<Equations::HamiltonianAndLapse, Geometry::FlatCartesian>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        flux_for_lapse_times_conformal_factor,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::I<DataVector, 3>& face_normal_vector,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one) {\n  Fluxes<Equations::Hamiltonian, Geometry::FlatCartesian>::apply(\n      flux_for_conformal_factor, face_normal, face_normal_vector,\n      conformal_factor_minus_one);\n  Poisson::fluxes_on_face(flux_for_lapse_times_conformal_factor,\n                          face_normal_vector,\n                          lapse_times_conformal_factor_minus_one);\n}\n\nvoid Fluxes<Equations::HamiltonianAndLapse, Geometry::Curved>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        flux_for_lapse_times_conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& /*lapse_times_conformal_factor_minus_one*/,\n    const tnsr::i<DataVector, 3>& conformal_factor_gradient,\n    const tnsr::i<DataVector, 3>& lapse_times_conformal_factor_gradient) {\n  Fluxes<Equations::Hamiltonian, Geometry::Curved>::apply(\n      flux_for_conformal_factor, inv_conformal_metric,\n      conformal_factor_minus_one, conformal_factor_gradient);\n  Poisson::curved_fluxes(flux_for_lapse_times_conformal_factor,\n                         inv_conformal_metric,\n                         lapse_times_conformal_factor_gradient);\n}\n\nvoid Fluxes<Equations::HamiltonianAndLapse, Geometry::Curved>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        flux_for_lapse_times_conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::I<DataVector, 3>& face_normal_vector,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one) {\n  Fluxes<Equations::Hamiltonian, Geometry::Curved>::apply(\n      flux_for_conformal_factor, inv_conformal_metric, face_normal,\n      face_normal_vector, conformal_factor_minus_one);\n  Poisson::fluxes_on_face(flux_for_lapse_times_conformal_factor,\n                          face_normal_vector,\n                          lapse_times_conformal_factor_minus_one);\n}\n\nvoid Fluxes<Equations::HamiltonianLapseAndShift, Geometry::FlatCartesian>::\n    apply(\n        const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n        const gsl::not_null<tnsr::I<DataVector, 3>*>\n            flux_for_lapse_times_conformal_factor,\n        const gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n        const Scalar<DataVector>& conformal_factor_minus_one,\n        const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n        const tnsr::I<DataVector, 3>& /*shift_excess*/,\n        const tnsr::i<DataVector, 3>& conformal_factor_gradient,\n        const tnsr::i<DataVector, 3>& lapse_times_conformal_factor_gradient,\n        const tnsr::iJ<DataVector, 3>& deriv_shift_excess) {\n  Fluxes<Equations::HamiltonianAndLapse, Geometry::FlatCartesian>::apply(\n      flux_for_conformal_factor, flux_for_lapse_times_conformal_factor,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_gradient, lapse_times_conformal_factor_gradient);\n  Xcts::longitudinal_operator_flat_cartesian(longitudinal_shift_excess,\n                                             deriv_shift_excess);\n}\n\nvoid Fluxes<Equations::HamiltonianLapseAndShift, Geometry::FlatCartesian>::\n    apply(\n        const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n        const gsl::not_null<tnsr::I<DataVector, 3>*>\n            flux_for_lapse_times_conformal_factor,\n        const gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n        const tnsr::i<DataVector, 3>& face_normal,\n        const tnsr::I<DataVector, 3>& face_normal_vector,\n        const Scalar<DataVector>& conformal_factor_minus_one,\n        const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n        const tnsr::I<DataVector, 3>& shift_excess) {\n  Fluxes<Equations::HamiltonianAndLapse, Geometry::FlatCartesian>::apply(\n      flux_for_conformal_factor, flux_for_lapse_times_conformal_factor,\n      face_normal, face_normal_vector, conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < i; ++j) {\n      longitudinal_shift_excess->get(i, j) =\n          face_normal_vector.get(i) * shift_excess.get(j) +\n          face_normal_vector.get(j) * shift_excess.get(i);\n    }\n    longitudinal_shift_excess->get(i, i) =\n        2. * face_normal_vector.get(i) * shift_excess.get(i);\n    for (size_t k = 0; k < 3; ++k) {\n      longitudinal_shift_excess->get(i, i) -=\n          2. / 3. * face_normal.get(k) * shift_excess.get(k);\n    }\n  }\n}\n\nvoid Fluxes<Equations::HamiltonianLapseAndShift, Geometry::Curved>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        flux_for_lapse_times_conformal_factor,\n    const gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n    const tnsr::ii<DataVector, 3>& /*conformal_metric*/,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& shift_excess,\n    const tnsr::i<DataVector, 3>& conformal_factor_gradient,\n    const tnsr::i<DataVector, 3>& lapse_times_conformal_factor_gradient,\n    const tnsr::iJ<DataVector, 3>& deriv_shift_excess) {\n  Fluxes<Equations::HamiltonianAndLapse, Geometry::Curved>::apply(\n      flux_for_conformal_factor, flux_for_lapse_times_conformal_factor,\n      inv_conformal_metric, conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one, conformal_factor_gradient,\n      lapse_times_conformal_factor_gradient);\n  Xcts::longitudinal_operator(longitudinal_shift_excess, shift_excess,\n                              deriv_shift_excess, inv_conformal_metric,\n                              conformal_christoffel_second_kind);\n}\n\nvoid Fluxes<Equations::HamiltonianLapseAndShift, Geometry::Curved>::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3>*>\n        flux_for_lapse_times_conformal_factor,\n    const gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n    const tnsr::ii<DataVector, 3>& /*conformal_metric*/,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& /*conformal_christoffel_second_kind*/,\n    const tnsr::i<DataVector, 3>& face_normal,\n    const tnsr::I<DataVector, 3>& face_normal_vector,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& shift_excess) {\n  Fluxes<Equations::HamiltonianAndLapse, Geometry::Curved>::apply(\n      flux_for_conformal_factor, flux_for_lapse_times_conformal_factor,\n      inv_conformal_metric, face_normal, face_normal_vector,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one);\n  // Buffer n_k \\beta^k in the (2, 2) component of the result, which is filled\n  // last in the loop below\n  auto& n_dot_shift = get<2, 2>(*longitudinal_shift_excess);\n  n_dot_shift = get<0>(face_normal) * get<0>(shift_excess) +\n                get<1>(face_normal) * get<1>(shift_excess) +\n                get<2>(face_normal) * get<2>(shift_excess);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      longitudinal_shift_excess->get(i, j) =\n          face_normal_vector.get(i) * shift_excess.get(j) +\n          face_normal_vector.get(j) * shift_excess.get(i) -\n          2. / 3. * inv_conformal_metric.get(i, j) * n_dot_shift;\n    }\n  }\n}\n\ntemplate <int ConformalMatterScale>\nvoid Sources<Equations::Hamiltonian, Geometry::FlatCartesian,\n             ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>&\n              longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n          const tnsr::I<DataVector, 3>& /*conformal_factor_flux*/) {\n  add_hamiltonian_sources<ConformalMatterScale>(\n      hamiltonian_constraint, conformal_energy_density,\n      extrinsic_curvature_trace, conformal_factor_minus_one);\n  add_distortion_hamiltonian_sources(\n      hamiltonian_constraint,\n      longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n      conformal_factor_minus_one);\n}\n\ntemplate <int ConformalMatterScale>\nvoid Sources<Equations::Hamiltonian, Geometry::Curved, ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>&\n              longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n          const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n          const Scalar<DataVector>& conformal_ricci_scalar,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux) {\n  Sources<Equations::Hamiltonian, Geometry::FlatCartesian,\n          ConformalMatterScale>::\n      apply(hamiltonian_constraint, conformal_energy_density,\n            extrinsic_curvature_trace,\n            longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n            conformal_factor_minus_one, deriv_conformal_factor,\n            conformal_factor_flux);\n  add_curved_hamiltonian_or_lapse_sources(hamiltonian_constraint,\n                                          conformal_ricci_scalar,\n                                          conformal_factor_minus_one, 1.);\n  Poisson::add_curved_sources(hamiltonian_constraint,\n                              conformal_christoffel_contracted,\n                              conformal_factor_flux);\n}\n\ntemplate <int ConformalMatterScale>\nvoid Sources<Equations::HamiltonianAndLapse, Geometry::FlatCartesian,\n             ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n          const gsl::not_null<Scalar<DataVector>*> lapse_equation,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& conformal_stress_trace,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n          const Scalar<DataVector>&\n              longitudinal_shift_minus_dt_conformal_metric_square,\n          const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n          const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n          const tnsr::i<DataVector, 3>&\n          /*deriv_lapse_times_conformal_factor*/,\n          const tnsr::I<DataVector, 3>& /*conformal_factor_flux*/,\n          const tnsr::I<DataVector, 3>&\n          /*lapse_times_conformal_factor_flux*/) {\n  add_hamiltonian_sources<ConformalMatterScale>(\n      hamiltonian_constraint, conformal_energy_density,\n      extrinsic_curvature_trace, conformal_factor_minus_one);\n  add_lapse_sources<ConformalMatterScale>(\n      lapse_equation, conformal_energy_density, conformal_stress_trace,\n      extrinsic_curvature_trace, dt_extrinsic_curvature_trace,\n      shift_dot_deriv_extrinsic_curvature_trace, conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one);\n  add_distortion_hamiltonian_and_lapse_sources(\n      hamiltonian_constraint, lapse_equation,\n      longitudinal_shift_minus_dt_conformal_metric_square,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one);\n}\n\ntemplate <int ConformalMatterScale>\nvoid Sources<Equations::HamiltonianAndLapse, Geometry::Curved,\n             ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n          const gsl::not_null<Scalar<DataVector>*> lapse_equation,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& conformal_stress_trace,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n          const Scalar<DataVector>&\n              longitudinal_shift_minus_dt_conformal_metric_square,\n          const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n          const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n          const Scalar<DataVector>& conformal_ricci_scalar,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n          const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n          const tnsr::i<DataVector, 3>& deriv_lapse_times,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux,\n          const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux) {\n  Sources<Equations::HamiltonianAndLapse, Geometry::FlatCartesian,\n          ConformalMatterScale>::\n      apply(hamiltonian_constraint, lapse_equation, conformal_energy_density,\n            conformal_stress_trace, extrinsic_curvature_trace,\n            dt_extrinsic_curvature_trace,\n            longitudinal_shift_minus_dt_conformal_metric_square,\n            shift_dot_deriv_extrinsic_curvature_trace,\n            conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n            deriv_conformal_factor,deriv_lapse_times,\n            conformal_factor_flux, lapse_times_conformal_factor_flux);\n  add_curved_hamiltonian_or_lapse_sources(hamiltonian_constraint,\n                                          conformal_ricci_scalar,\n                                          conformal_factor_minus_one, 1.);\n  Poisson::add_curved_sources(hamiltonian_constraint,\n                              conformal_christoffel_contracted,\n                              conformal_factor_flux);\n  add_curved_hamiltonian_or_lapse_sources(\n      lapse_equation, conformal_ricci_scalar,\n      lapse_times_conformal_factor_minus_one, 1.);\n  Poisson::add_curved_sources(lapse_equation, conformal_christoffel_contracted,\n                              lapse_times_conformal_factor_flux);\n}\n\ntemplate <int ConformalMatterScale>\nvoid Sources<Equations::HamiltonianLapseAndShift, Geometry::FlatCartesian,\n             ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n          const gsl::not_null<Scalar<DataVector>*> lapse_equation,\n          const gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& conformal_stress_trace,\n          const tnsr::I<DataVector, 3>& conformal_momentum_density,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n          const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n          const tnsr::I<DataVector, 3>& shift_background,\n          const tnsr::II<DataVector, 3>&\n              longitudinal_shift_background_minus_dt_conformal_metric,\n          const tnsr::I<DataVector, 3>&\n              div_longitudinal_shift_background_minus_dt_conformal_metric,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n          const tnsr::I<DataVector, 3>& shift_excess,\n          const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n          const tnsr::i<DataVector, 3>&\n          /*deriv_lapse_times_conformal_factor*/,\n          const tnsr::iJ<DataVector, 3>& /*deriv_shift*/,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux,\n          const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n          const tnsr::II<DataVector, 3>& longitudinal_shift_excess) {\n  auto shift = shift_background;\n  for (size_t i = 0; i < 3; ++i) {\n    shift.get(i) += shift_excess.get(i);\n  }\n  auto longitudinal_shift_minus_dt_conformal_metric =\n      longitudinal_shift_background_minus_dt_conformal_metric;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      longitudinal_shift_minus_dt_conformal_metric.get(i, j) +=\n          longitudinal_shift_excess.get(i, j);\n    }\n  }\n  add_hamiltonian_sources<ConformalMatterScale>(\n      hamiltonian_constraint, conformal_energy_density,\n      extrinsic_curvature_trace, conformal_factor_minus_one);\n  add_lapse_sources<ConformalMatterScale>(\n      lapse_equation, conformal_energy_density, conformal_stress_trace,\n      extrinsic_curvature_trace, dt_extrinsic_curvature_trace,\n      dot_product(shift, extrinsic_curvature_trace_gradient),\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one);\n  add_flat_cartesian_momentum_sources<ConformalMatterScale>(\n      hamiltonian_constraint, lapse_equation, momentum_constraint,\n      conformal_momentum_density, extrinsic_curvature_trace_gradient,\n      div_longitudinal_shift_background_minus_dt_conformal_metric,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_flux, lapse_times_conformal_factor_flux,\n      longitudinal_shift_minus_dt_conformal_metric);\n}\n\ntemplate <int ConformalMatterScale>\nvoid Sources<Equations::HamiltonianLapseAndShift, Geometry::Curved,\n             ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n          const gsl::not_null<Scalar<DataVector>*> lapse_equation,\n          const gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& conformal_stress_trace,\n          const tnsr::I<DataVector, 3>& conformal_momentum_density,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n          const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n          const tnsr::I<DataVector, 3>& shift_background,\n          const tnsr::II<DataVector, 3>&\n              longitudinal_shift_background_minus_dt_conformal_metric,\n          const tnsr::I<DataVector, 3>&\n              div_longitudinal_shift_background_minus_dt_conformal_metric,\n          const tnsr::ii<DataVector, 3>& conformal_metric,\n          const tnsr::II<DataVector, 3>& inv_conformal_metric,\n          const tnsr::ijj<DataVector, 3>& /*conformal_christoffel_first_kind*/,\n          const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n          const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n          const Scalar<DataVector>& conformal_ricci_scalar,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n          const tnsr::I<DataVector, 3>& shift_excess,\n          const tnsr::i<DataVector, 3>& /*deriv_conformal_factor*/,\n          const tnsr::i<DataVector, 3>&\n          /*deriv_lapse_times_conformal_factor*/,\n          const tnsr::iJ<DataVector, 3>& /*deriv_shift*/,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux,\n          const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n          const tnsr::II<DataVector, 3>& longitudinal_shift_excess) {\n  auto shift = shift_background;\n  for (size_t i = 0; i < 3; ++i) {\n    shift.get(i) += shift_excess.get(i);\n  }\n  auto longitudinal_shift_minus_dt_conformal_metric =\n      longitudinal_shift_background_minus_dt_conformal_metric;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      longitudinal_shift_minus_dt_conformal_metric.get(i, j) +=\n          longitudinal_shift_excess.get(i, j);\n    }\n  }\n  add_hamiltonian_sources<ConformalMatterScale>(\n      hamiltonian_constraint, conformal_energy_density,\n      extrinsic_curvature_trace, conformal_factor_minus_one);\n  add_curved_hamiltonian_or_lapse_sources(hamiltonian_constraint,\n                                          conformal_ricci_scalar,\n                                          conformal_factor_minus_one, 1.);\n  Poisson::add_curved_sources(hamiltonian_constraint,\n                              conformal_christoffel_contracted,\n                              conformal_factor_flux);\n  add_lapse_sources<ConformalMatterScale>(\n      lapse_equation, conformal_energy_density, conformal_stress_trace,\n      extrinsic_curvature_trace, dt_extrinsic_curvature_trace,\n      dot_product(shift, extrinsic_curvature_trace_gradient),\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one);\n  add_curved_hamiltonian_or_lapse_sources(\n      lapse_equation, conformal_ricci_scalar,\n      lapse_times_conformal_factor_minus_one, 1.);\n  Poisson::add_curved_sources(lapse_equation, conformal_christoffel_contracted,\n                              lapse_times_conformal_factor_flux);\n  add_curved_momentum_sources<ConformalMatterScale>(\n      hamiltonian_constraint, lapse_equation, momentum_constraint,\n      conformal_momentum_density, extrinsic_curvature_trace_gradient,\n      conformal_metric, inv_conformal_metric,\n      div_longitudinal_shift_background_minus_dt_conformal_metric,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_flux, lapse_times_conformal_factor_flux,\n      longitudinal_shift_minus_dt_conformal_metric);\n  Elasticity::add_curved_sources(momentum_constraint,\n                                 conformal_christoffel_second_kind,\n                                 conformal_christoffel_contracted,\n                                 longitudinal_shift_minus_dt_conformal_metric);\n}\n\ntemplate <int ConformalMatterScale>\nvoid LinearizedSources<Equations::Hamiltonian, Geometry::FlatCartesian,\n                       ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*>\n              linearized_hamiltonian_constraint,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>&\n              longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& conformal_factor_correction,\n          const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n          const tnsr::I<DataVector, 3>&\n          /*conformal_factor_flux_correction*/) {\n  add_linearized_hamiltonian_sources<ConformalMatterScale>(\n      linearized_hamiltonian_constraint, conformal_energy_density,\n      extrinsic_curvature_trace, conformal_factor_minus_one,\n      conformal_factor_correction);\n  add_linearized_distortion_hamiltonian_sources(\n      linearized_hamiltonian_constraint,\n      longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n      conformal_factor_minus_one, conformal_factor_correction);\n}\n\ntemplate <int ConformalMatterScale>\nvoid LinearizedSources<Equations::Hamiltonian, Geometry::Curved,\n                       ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*>\n              linearized_hamiltonian_constraint,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>&\n              longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n          const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n          const Scalar<DataVector>& conformal_ricci_scalar,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& conformal_factor_correction,\n          const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux_correction) {\n  LinearizedSources<Equations::Hamiltonian, Geometry::FlatCartesian,\n                    ConformalMatterScale>::\n      apply(linearized_hamiltonian_constraint, conformal_energy_density,\n            extrinsic_curvature_trace,\n            longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n            conformal_factor_minus_one, conformal_factor_correction,\n            deriv_conformal_factor_correction,\n            conformal_factor_flux_correction);\n  add_curved_hamiltonian_or_lapse_sources(linearized_hamiltonian_constraint,\n                                          conformal_ricci_scalar,\n                                          conformal_factor_correction);\n  Poisson::add_curved_sources(linearized_hamiltonian_constraint,\n                              conformal_christoffel_contracted,\n                              conformal_factor_flux_correction);\n}\n\ntemplate <int ConformalMatterScale>\nvoid LinearizedSources<Equations::HamiltonianAndLapse, Geometry::FlatCartesian,\n                       ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*>\n              linearized_hamiltonian_constraint,\n          const gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& conformal_stress_trace,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n          const Scalar<DataVector>&\n              longitudinal_shift_minus_dt_conformal_metric_square,\n          const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n          const Scalar<DataVector>& conformal_factor_correction,\n          const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n          const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n          const tnsr::i<DataVector, 3>&\n          /*deriv_lapse_times_conformal_factor_correction*/,\n          const tnsr::I<DataVector, 3>& /*conformal_factor_flux_correction*/,\n          const tnsr::I<DataVector, 3>&\n          /*lapse_times_conformal_factor_flux_correction*/) {\n  add_linearized_hamiltonian_sources<ConformalMatterScale>(\n      linearized_hamiltonian_constraint, conformal_energy_density,\n      extrinsic_curvature_trace, conformal_factor_minus_one,\n      conformal_factor_correction);\n  add_linearized_lapse_sources<ConformalMatterScale>(\n      linearized_lapse_equation, conformal_energy_density,\n      conformal_stress_trace, extrinsic_curvature_trace,\n      dt_extrinsic_curvature_trace, shift_dot_deriv_extrinsic_curvature_trace,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_correction, lapse_times_conformal_factor_correction);\n  add_linearized_distortion_hamiltonian_and_lapse_sources(\n      linearized_hamiltonian_constraint, linearized_lapse_equation,\n      longitudinal_shift_minus_dt_conformal_metric_square,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_correction, lapse_times_conformal_factor_correction);\n}\n\ntemplate <int ConformalMatterScale>\nvoid LinearizedSources<Equations::HamiltonianAndLapse, Geometry::Curved,\n                       ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*>\n              linearized_hamiltonian_constraint,\n          const gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& conformal_stress_trace,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n          const Scalar<DataVector>&\n              longitudinal_shift_minus_dt_conformal_metric_square,\n          const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n          const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n          const Scalar<DataVector>& conformal_ricci_scalar,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n          const Scalar<DataVector>& conformal_factor_correction,\n          const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n          const tnsr::i<DataVector, 3>& deriv_conformal_factor_correction,\n          const tnsr::i<DataVector, 3>&\n          deriv_lapse_times_conformal_factor_correction,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n          const tnsr::I<DataVector, 3>&\n              lapse_times_conformal_factor_flux_correction) {\n  LinearizedSources<Equations::HamiltonianAndLapse, Geometry::FlatCartesian,\n                    ConformalMatterScale>::\n      apply(linearized_hamiltonian_constraint, linearized_lapse_equation,\n            conformal_energy_density, conformal_stress_trace,\n            extrinsic_curvature_trace, dt_extrinsic_curvature_trace,\n            longitudinal_shift_minus_dt_conformal_metric_square,\n            shift_dot_deriv_extrinsic_curvature_trace,\n            conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n            conformal_factor_correction,\n            lapse_times_conformal_factor_correction,\n            deriv_conformal_factor_correction,\n            deriv_lapse_times_conformal_factor_correction,\n            conformal_factor_flux_correction,\n            lapse_times_conformal_factor_flux_correction);\n  add_curved_hamiltonian_or_lapse_sources(linearized_hamiltonian_constraint,\n                                          conformal_ricci_scalar,\n                                          conformal_factor_correction);\n  Poisson::add_curved_sources(linearized_hamiltonian_constraint,\n                              conformal_christoffel_contracted,\n                              conformal_factor_flux_correction);\n  add_curved_hamiltonian_or_lapse_sources(\n      linearized_lapse_equation, conformal_ricci_scalar,\n      lapse_times_conformal_factor_correction);\n  Poisson::add_curved_sources(linearized_lapse_equation,\n                              conformal_christoffel_contracted,\n                              lapse_times_conformal_factor_flux_correction);\n}\n\ntemplate <int ConformalMatterScale>\nvoid LinearizedSources<Equations::HamiltonianLapseAndShift,\n                       Geometry::FlatCartesian, ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*>\n              linearized_hamiltonian_constraint,\n          const gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n          const gsl::not_null<tnsr::I<DataVector, 3>*>\n              linearized_momentum_constraint,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& conformal_stress_trace,\n          const tnsr::I<DataVector, 3>& conformal_momentum_density,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n          const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n          const tnsr::I<DataVector, 3>& shift_background,\n          const tnsr::II<DataVector, 3>&\n              longitudinal_shift_background_minus_dt_conformal_metric,\n          const tnsr::I<DataVector, 3>&\n          /*div_longitudinal_shift_background_minus_dt_conformal_metric*/,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n          const tnsr::I<DataVector, 3>& shift_excess,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux,\n          const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n          const tnsr::II<DataVector, 3>& longitudinal_shift_excess,\n          const Scalar<DataVector>& conformal_factor_correction,\n          const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n          const tnsr::I<DataVector, 3>& shift_excess_correction,\n          const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n          const tnsr::i<DataVector, 3>&\n          /*deriv_lapse_times_conformal_factor_correction*/,\n          const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess_correction*/,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n          const tnsr::I<DataVector, 3>&\n              lapse_times_conformal_factor_flux_correction,\n          const tnsr::II<DataVector, 3>& longitudinal_shift_excess_correction) {\n  auto shift = shift_background;\n  for (size_t i = 0; i < 3; ++i) {\n    shift.get(i) += shift_excess.get(i);\n  }\n  auto longitudinal_shift_minus_dt_conformal_metric =\n      longitudinal_shift_background_minus_dt_conformal_metric;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      longitudinal_shift_minus_dt_conformal_metric.get(i, j) +=\n          longitudinal_shift_excess.get(i, j);\n    }\n  }\n  add_linearized_hamiltonian_sources<ConformalMatterScale>(\n      linearized_hamiltonian_constraint, conformal_energy_density,\n      extrinsic_curvature_trace, conformal_factor_minus_one,\n      conformal_factor_correction);\n  add_linearized_lapse_sources<ConformalMatterScale>(\n      linearized_lapse_equation, conformal_energy_density,\n      conformal_stress_trace, extrinsic_curvature_trace,\n      dt_extrinsic_curvature_trace,\n      dot_product(shift, extrinsic_curvature_trace_gradient),\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_correction, lapse_times_conformal_factor_correction);\n  add_flat_cartesian_linearized_momentum_sources<ConformalMatterScale>(\n      linearized_hamiltonian_constraint, linearized_lapse_equation,\n      linearized_momentum_constraint, conformal_momentum_density,\n      extrinsic_curvature_trace_gradient, conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one, conformal_factor_flux,\n      lapse_times_conformal_factor_flux,\n      longitudinal_shift_minus_dt_conformal_metric, conformal_factor_correction,\n      lapse_times_conformal_factor_correction, shift_excess_correction,\n      conformal_factor_flux_correction,\n      lapse_times_conformal_factor_flux_correction,\n      longitudinal_shift_excess_correction);\n}\n\ntemplate <int ConformalMatterScale>\nvoid LinearizedSources<Equations::HamiltonianLapseAndShift, Geometry::Curved,\n                       ConformalMatterScale>::\n    apply(const gsl::not_null<Scalar<DataVector>*>\n              linearized_hamiltonian_constraint,\n          const gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n          const gsl::not_null<tnsr::I<DataVector, 3>*>\n              linearized_momentum_constraint,\n          const Scalar<DataVector>& conformal_energy_density,\n          const Scalar<DataVector>& conformal_stress_trace,\n          const tnsr::I<DataVector, 3>& conformal_momentum_density,\n          const Scalar<DataVector>& extrinsic_curvature_trace,\n          const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n          const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n          const tnsr::I<DataVector, 3>& shift_background,\n          const tnsr::II<DataVector, 3>&\n              longitudinal_shift_background_minus_dt_conformal_metric,\n          const tnsr::I<DataVector, 3>&\n          /*div_longitudinal_shift_background_minus_dt_conformal_metric*/,\n          const tnsr::ii<DataVector, 3>& conformal_metric,\n          const tnsr::II<DataVector, 3>& inv_conformal_metric,\n          const tnsr::ijj<DataVector, 3>& /*conformal_christoffel_first_kind*/,\n          const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n          const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n          const Scalar<DataVector>& conformal_ricci_scalar,\n          const Scalar<DataVector>& conformal_factor_minus_one,\n          const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n          const tnsr::I<DataVector, 3>& shift_excess,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux,\n          const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n          const tnsr::II<DataVector, 3>& longitudinal_shift_excess,\n          const Scalar<DataVector>& conformal_factor_correction,\n          const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n          const tnsr::I<DataVector, 3>& shift_excess_correction,\n          const tnsr::i<DataVector, 3>& /*deriv_conformal_factor_correction*/,\n          const tnsr::i<DataVector, 3>&\n          /*deriv_lapse_times_conformal_factor_correction*/,\n          const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess_correction*/,\n          const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n          const tnsr::I<DataVector, 3>&\n              lapse_times_conformal_factor_flux_correction,\n          const tnsr::II<DataVector, 3>& longitudinal_shift_excess_correction) {\n  auto shift = shift_background;\n  for (size_t i = 0; i < 3; ++i) {\n    shift.get(i) += shift_excess.get(i);\n  }\n  auto longitudinal_shift_minus_dt_conformal_metric =\n      longitudinal_shift_background_minus_dt_conformal_metric;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      longitudinal_shift_minus_dt_conformal_metric.get(i, j) +=\n          longitudinal_shift_excess.get(i, j);\n    }\n  }\n  add_linearized_hamiltonian_sources<ConformalMatterScale>(\n      linearized_hamiltonian_constraint, conformal_energy_density,\n      extrinsic_curvature_trace, conformal_factor_minus_one,\n      conformal_factor_correction);\n  add_curved_hamiltonian_or_lapse_sources(linearized_hamiltonian_constraint,\n                                          conformal_ricci_scalar,\n                                          conformal_factor_correction);\n  Poisson::add_curved_sources(linearized_hamiltonian_constraint,\n                              conformal_christoffel_contracted,\n                              conformal_factor_flux_correction);\n  add_linearized_lapse_sources<ConformalMatterScale>(\n      linearized_lapse_equation, conformal_energy_density,\n      conformal_stress_trace, extrinsic_curvature_trace,\n      dt_extrinsic_curvature_trace,\n      dot_product(shift, extrinsic_curvature_trace_gradient),\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      conformal_factor_correction, lapse_times_conformal_factor_correction);\n  add_curved_hamiltonian_or_lapse_sources(\n      linearized_lapse_equation, conformal_ricci_scalar,\n      lapse_times_conformal_factor_correction);\n  Poisson::add_curved_sources(linearized_lapse_equation,\n                              conformal_christoffel_contracted,\n                              lapse_times_conformal_factor_flux_correction);\n  add_curved_linearized_momentum_sources<ConformalMatterScale>(\n      linearized_hamiltonian_constraint, linearized_lapse_equation,\n      linearized_momentum_constraint, conformal_momentum_density,\n      extrinsic_curvature_trace_gradient, conformal_metric,\n      inv_conformal_metric, conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one, conformal_factor_flux,\n      lapse_times_conformal_factor_flux,\n      longitudinal_shift_minus_dt_conformal_metric, conformal_factor_correction,\n      lapse_times_conformal_factor_correction, shift_excess_correction,\n      conformal_factor_flux_correction,\n      lapse_times_conformal_factor_flux_correction,\n      longitudinal_shift_excess_correction);\n  Elasticity::add_curved_sources(\n      linearized_momentum_constraint, conformal_christoffel_second_kind,\n      conformal_christoffel_contracted, longitudinal_shift_excess_correction);\n}\n\n#define EQNS(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GEOM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define CONF_MATTER_SCALE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATION(r, data)                                             \\\n  template class Sources<EQNS(data), GEOM(data), CONF_MATTER_SCALE(data)>; \\\n  template class LinearizedSources<EQNS(data), GEOM(data),                 \\\n                                   CONF_MATTER_SCALE(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (Equations::Hamiltonian, Equations::HamiltonianAndLapse,\n                         Equations::HamiltonianLapseAndShift),\n                        (Geometry::FlatCartesian, Geometry::Curved), (0, 6, 8))\n\n#undef EQNS\n#undef GEOM\n#undef CONF_MATTER_SCALE\n#undef INSTANTIATION\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/FluxesAndSources.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Xcts {\n\n/// Indicates a subset of the XCTS equations\nenum class Equations {\n  /// Only the Hamiltonian constraint, solved for \\f$\\psi\\f$\n  Hamiltonian,\n  /// Both the Hamiltonian constraint and the lapse equation, solved for\n  /// \\f$\\psi\\f$ and \\f$\\alpha\\psi\\f$\n  HamiltonianAndLapse,\n  /// The full XCTS equations, solved for \\f$\\psi\\f$, \\f$\\alpha\\psi\\f$ and\n  /// \\f$\\beta_\\mathrm{excess}\\f$\n  HamiltonianLapseAndShift\n};\n\n/// The fluxes \\f$F^i\\f$ for the first-order formulation of the XCTS equations.\n///\n/// \\see Xcts::FirstOrderSystem\ntemplate <Equations EnabledEquations, Geometry ConformalGeometry>\nstruct Fluxes;\n\n/// \\cond\ntemplate <>\nstruct Fluxes<Equations::Hamiltonian, Geometry::FlatCartesian> {\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = true;\n  static constexpr bool is_discontinuous = false;\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& conformal_factor_gradient);\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::I<DataVector, 3>& face_normal_vector,\n      const Scalar<DataVector>& conformal_factor_minus_one);\n};\n\ntemplate <>\nstruct Fluxes<Equations::Hamiltonian, Geometry::Curved> {\n  using argument_tags =\n      tmpl::list<Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = true;\n  static constexpr bool is_discontinuous = false;\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& conformal_factor_gradient);\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::I<DataVector, 3>& face_normal_vector,\n      const Scalar<DataVector>& conformal_factor_minus_one);\n};\n\ntemplate <>\nstruct Fluxes<Equations::HamiltonianAndLapse, Geometry::FlatCartesian> {\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = true;\n  static constexpr bool is_discontinuous = false;\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          flux_for_lapse_times_conformal_factor,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& conformal_factor_gradient,\n      const tnsr::i<DataVector, 3>& lapse_times_conformal_factor_gradient);\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          flux_for_lapse_times_conformal_factor,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::I<DataVector, 3>& face_normal_vector,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one);\n};\n\ntemplate <>\nstruct Fluxes<Equations::HamiltonianAndLapse, Geometry::Curved> {\n  using argument_tags =\n      tmpl::list<Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = true;\n  static constexpr bool is_discontinuous = false;\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          flux_for_lapse_times_conformal_factor,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& conformal_factor_gradient,\n      const tnsr::i<DataVector, 3>& lapse_times_conformal_factor_gradient);\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          flux_for_lapse_times_conformal_factor,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::I<DataVector, 3>& face_normal_vector,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one);\n};\n\ntemplate <>\nstruct Fluxes<Equations::HamiltonianLapseAndShift, Geometry::FlatCartesian> {\n  using argument_tags = tmpl::list<>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = false;\n  static constexpr bool is_discontinuous = false;\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          flux_for_lapse_times_conformal_factor,\n      gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& shift_excess,\n      const tnsr::i<DataVector, 3>& conformal_factor_gradient,\n      const tnsr::i<DataVector, 3>& lapse_times_conformal_factor_gradient,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess);\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          flux_for_lapse_times_conformal_factor,\n      gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::I<DataVector, 3>& face_normal_vector,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& shift_excess);\n};\n\ntemplate <>\nstruct Fluxes<Equations::HamiltonianLapseAndShift, Geometry::Curved> {\n  using argument_tags = tmpl::list<\n      Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n      Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n      Tags::ConformalChristoffelSecondKind<DataVector, 3, Frame::Inertial>>;\n  using volume_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr bool is_trivial = false;\n  static constexpr bool is_discontinuous = false;\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          flux_for_lapse_times_conformal_factor,\n      gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n      const tnsr::ii<DataVector, 3>& /*conformal_metric*/,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::Ijj<DataVector, 3>& christoffel_second_kind,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& shift_excess,\n      const tnsr::i<DataVector, 3>& conformal_factor_gradient,\n      const tnsr::i<DataVector, 3>& lapse_times_conformal_factor_gradient,\n      const tnsr::iJ<DataVector, 3>& deriv_shift_excess);\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3>*> flux_for_conformal_factor,\n      gsl::not_null<tnsr::I<DataVector, 3>*>\n          flux_for_lapse_times_conformal_factor,\n      gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n      const tnsr::ii<DataVector, 3>& /*conformal_metric*/,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::Ijj<DataVector, 3>& christoffel_second_kind,\n      const tnsr::i<DataVector, 3>& face_normal,\n      const tnsr::I<DataVector, 3>& face_normal_vector,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& shift_excess);\n};\n/// \\endcond\n\n/// The sources \\f$S\\f$ for the first-order formulation of the XCTS equations.\n///\n/// \\see Xcts::FirstOrderSystem\ntemplate <Equations EnabledEquations, Geometry ConformalGeometry,\n          int ConformalMatterScale>\nstruct Sources;\n\n/// \\cond\ntemplate <int ConformalMatterScale>\nstruct Sources<Equations::Hamiltonian, Geometry::FlatCartesian,\n               ConformalMatterScale> {\n  using argument_tags = tmpl::list<\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>,\n                          ConformalMatterScale>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<DataVector>>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>&\n          longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux);\n};\n\ntemplate <int ConformalMatterScale>\nstruct Sources<Equations::Hamiltonian, Geometry::Curved, ConformalMatterScale> {\n  using argument_tags = tmpl::push_back<\n      typename Sources<Equations::Hamiltonian, Geometry::FlatCartesian,\n                       ConformalMatterScale>::argument_tags,\n      Tags::ConformalChristoffelContracted<DataVector, 3, Frame::Inertial>,\n      Tags::ConformalRicciScalar<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>&\n          longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const Scalar<DataVector>& conformal_ricci_scalar,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux);\n};\n\ntemplate <int ConformalMatterScale>\nstruct Sources<Equations::HamiltonianAndLapse, Geometry::FlatCartesian,\n               ConformalMatterScale> {\n  using argument_tags = tmpl::list<\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>,\n                          ConformalMatterScale>,\n      gr::Tags::Conformal<gr::Tags::StressTrace<DataVector>,\n                          ConformalMatterScale>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataVector>>,\n      Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataVector>,\n      Tags::ShiftDotDerivExtrinsicCurvatureTrace<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n      gsl::not_null<Scalar<DataVector>*> lapse_equation,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& conformal_stress_trace,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n      const Scalar<DataVector>&\n          longitudinal_shift_minus_dt_conformal_metric_square,\n      const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient*/,\n      const tnsr::i<DataVector, 3>&\n      /*lapse_times_conformal_factor_gradient*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux);\n};\n\ntemplate <int ConformalMatterScale>\nstruct Sources<Equations::HamiltonianAndLapse, Geometry::Curved,\n               ConformalMatterScale> {\n  using argument_tags = tmpl::push_back<\n      typename Sources<Equations::HamiltonianAndLapse, Geometry::FlatCartesian,\n                       ConformalMatterScale>::argument_tags,\n      Tags::ConformalChristoffelContracted<DataVector, 3, Frame::Inertial>,\n      Tags::ConformalRicciScalar<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n      gsl::not_null<Scalar<DataVector>*> lapse_equation,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& conformal_stress_trace,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n      const Scalar<DataVector>&\n          longitudinal_shift_minus_dt_conformal_metric_square,\n      const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const Scalar<DataVector>& conformal_ricci_scalar,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient*/,\n      const tnsr::i<DataVector, 3>&\n      /*lapse_times_conformal_factor_gradient*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux);\n};\n\ntemplate <int ConformalMatterScale>\nstruct Sources<Equations::HamiltonianLapseAndShift, Geometry::FlatCartesian,\n               ConformalMatterScale> {\n  using argument_tags = tmpl::list<\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>,\n                          ConformalMatterScale>,\n      gr::Tags::Conformal<gr::Tags::StressTrace<DataVector>,\n                          ConformalMatterScale>,\n      gr::Tags::Conformal<gr::Tags::MomentumDensity<DataVector, 3>,\n                          ConformalMatterScale>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataVector>>,\n      ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                    tmpl::size_t<3>, Frame::Inertial>,\n      Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n      Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,\n                                                              Frame::Inertial>,\n      ::Tags::div<Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n          DataVector, 3, Frame::Inertial>>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n      gsl::not_null<Scalar<DataVector>*> lapse_equation,\n      gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& conformal_stress_trace,\n      const tnsr::I<DataVector, 3>& conformal_momentum_density,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n      const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n      const tnsr::I<DataVector, 3>& shift_background,\n      const tnsr::II<DataVector, 3>&\n          longitudinal_shift_background_minus_dt_conformal_metric,\n      const tnsr::I<DataVector, 3>&\n          div_longitudinal_shift_background_minus_dt_conformal_metric,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& shift_excess,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient*/,\n      const tnsr::i<DataVector, 3>&\n      /*lapse_times_conformal_factor_gradient*/,\n      const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_excess);\n};\n\ntemplate <int ConformalMatterScale>\nstruct Sources<Equations::HamiltonianLapseAndShift, Geometry::Curved,\n               ConformalMatterScale> {\n  using argument_tags = tmpl::push_back<\n      typename Sources<Equations::HamiltonianLapseAndShift,\n                       Geometry::FlatCartesian,\n                       ConformalMatterScale>::argument_tags,\n      Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n      Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n      Tags::ConformalChristoffelFirstKind<DataVector, 3, Frame::Inertial>,\n      Tags::ConformalChristoffelSecondKind<DataVector, 3, Frame::Inertial>,\n      Tags::ConformalChristoffelContracted<DataVector, 3, Frame::Inertial>,\n      Tags::ConformalRicciScalar<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n      gsl::not_null<Scalar<DataVector>*> lapse_equation,\n      gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& conformal_stress_trace,\n      const tnsr::I<DataVector, 3>& conformal_momentum_density,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n      const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n      const tnsr::I<DataVector, 3>& shift_background,\n      const tnsr::II<DataVector, 3>&\n          longitudinal_shift_background_minus_dt_conformal_metric,\n      const tnsr::I<DataVector, 3>&\n          div_longitudinal_shift_background_minus_dt_conformal_metric,\n      const tnsr::ii<DataVector, 3>& conformal_metric,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::ijj<DataVector, 3>& /*conformal_christoffel_first_kind*/,\n      const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const Scalar<DataVector>& conformal_ricci_scalar,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& shift_excess,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient*/,\n      const tnsr::i<DataVector, 3>&\n      /*lapse_times_conformal_factor_gradient*/,\n      const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_excess);\n};\n/// \\endcond\n\n/// The linearization of the sources \\f$S\\f$ for the first-order formulation of\n/// the XCTS equations.\n///\n/// \\see Xcts::FirstOrderSystem\ntemplate <Equations EnabledEquations, Geometry ConformalGeometry,\n          int ConformalMatterScale>\nstruct LinearizedSources;\n\n/// \\cond\ntemplate <int ConformalMatterScale>\nstruct LinearizedSources<Equations::Hamiltonian, Geometry::FlatCartesian,\n                         ConformalMatterScale> {\n  using argument_tags = tmpl::push_back<\n      typename Sources<Equations::Hamiltonian, Geometry::FlatCartesian,\n                       ConformalMatterScale>::argument_tags,\n      Tags::ConformalFactorMinusOne<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>&\n          longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& conformal_factor_correction,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient_correction*/,\n      const tnsr::I<DataVector, 3>&\n      /*conformal_factor_flux_correction*/);\n};\n\ntemplate <int ConformalMatterScale>\nstruct LinearizedSources<Equations::Hamiltonian, Geometry::Curved,\n                         ConformalMatterScale> {\n  using argument_tags =\n      tmpl::push_back<typename Sources<Equations::Hamiltonian, Geometry::Curved,\n                                       ConformalMatterScale>::argument_tags,\n                      Tags::ConformalFactorMinusOne<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>&\n          longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const Scalar<DataVector>& conformal_ricci_scalar,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& conformal_factor_correction,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient_correction*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux_correction);\n};\n\ntemplate <int ConformalMatterScale>\nstruct LinearizedSources<Equations::HamiltonianAndLapse,\n                         Geometry::FlatCartesian, ConformalMatterScale> {\n  using argument_tags = tmpl::push_back<\n      typename Sources<Equations::HamiltonianAndLapse, Geometry::FlatCartesian,\n                       ConformalMatterScale>::argument_tags,\n      Tags::ConformalFactorMinusOne<DataVector>,\n      Tags::LapseTimesConformalFactorMinusOne<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n      gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& conformal_stress_trace,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n      const Scalar<DataVector>&\n          longitudinal_shift_minus_dt_conformal_metric_square,\n      const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const Scalar<DataVector>& conformal_factor_correction,\n      const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient_correction*/,\n      const tnsr::i<DataVector, 3>&\n      /*lapse_times_conformal_factor_gradient_correction*/,\n      const tnsr::I<DataVector, 3>& /*conformal_factor_flux_correction*/,\n      const tnsr::I<DataVector, 3>&\n          lapse_times_conformal_factor_flux_correction);\n};\n\ntemplate <int ConformalMatterScale>\nstruct LinearizedSources<Equations::HamiltonianAndLapse, Geometry::Curved,\n                         ConformalMatterScale> {\n  using argument_tags = tmpl::push_back<\n      typename Sources<Equations::HamiltonianAndLapse, Geometry::Curved,\n                       ConformalMatterScale>::argument_tags,\n      Tags::ConformalFactorMinusOne<DataVector>,\n      Tags::LapseTimesConformalFactorMinusOne<DataVector>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n      gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& conformal_stress_trace,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n      const Scalar<DataVector>&\n          longitudinal_shift_minus_dt_conformal_metric_square,\n      const Scalar<DataVector>& shift_dot_deriv_extrinsic_curvature_trace,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const Scalar<DataVector>& conformal_ricci_scalar,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const Scalar<DataVector>& conformal_factor_correction,\n      const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient_correction*/,\n      const tnsr::i<DataVector, 3>&\n      /*lapse_times_conformal_factor_gradient_correction*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n      const tnsr::I<DataVector, 3>&\n          lapse_times_conformal_factor_flux_correction);\n};\n\ntemplate <int ConformalMatterScale>\nstruct LinearizedSources<Equations::HamiltonianLapseAndShift,\n                         Geometry::FlatCartesian, ConformalMatterScale> {\n  using argument_tags = tmpl::push_back<\n      typename Sources<Equations::HamiltonianLapseAndShift,\n                       Geometry::FlatCartesian,\n                       ConformalMatterScale>::argument_tags,\n      Tags::ConformalFactorMinusOne<DataVector>,\n      Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n      Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n      ::Tags::Flux<Tags::ConformalFactorMinusOne<DataVector>, tmpl::size_t<3>,\n                   Frame::Inertial>,\n      ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n                   tmpl::size_t<3>, Frame::Inertial>,\n      Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n      gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n      gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& conformal_stress_trace,\n      const tnsr::I<DataVector, 3>& conformal_momentum_density,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n      const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n      const tnsr::I<DataVector, 3>& shift_background,\n      const tnsr::II<DataVector, 3>&\n          longitudinal_shift_background_minus_dt_conformal_metric,\n      const tnsr::I<DataVector, 3>&\n          div_longitudinal_shift_background_minus_dt_conformal_metric,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& shift_excess,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_excess,\n      const Scalar<DataVector>& conformal_factor_correction,\n      const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n      const tnsr::I<DataVector, 3>& shift_excess_correction,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient_correction*/,\n      const tnsr::i<DataVector, 3>&\n      /*lapse_times_conformal_factor_gradient_correction*/,\n      const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess_correction*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n      const tnsr::I<DataVector, 3>&\n          lapse_times_conformal_factor_flux_correction,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_excess_correction);\n};\n\ntemplate <int ConformalMatterScale>\nstruct LinearizedSources<Equations::HamiltonianLapseAndShift, Geometry::Curved,\n                         ConformalMatterScale> {\n  using argument_tags = tmpl::push_back<\n      typename Sources<Equations::HamiltonianLapseAndShift, Geometry::Curved,\n                       ConformalMatterScale>::argument_tags,\n      Tags::ConformalFactorMinusOne<DataVector>,\n      Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n      Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n      ::Tags::Flux<Tags::ConformalFactorMinusOne<DataVector>, tmpl::size_t<3>,\n                   Frame::Inertial>,\n      ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n                   tmpl::size_t<3>, Frame::Inertial>,\n      Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>>;\n  using const_global_cache_tags = tmpl::list<>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> linearized_hamiltonian_constraint,\n      gsl::not_null<Scalar<DataVector>*> linearized_lapse_equation,\n      gsl::not_null<tnsr::I<DataVector, 3>*> linearized_momentum_constraint,\n      const Scalar<DataVector>& conformal_energy_density,\n      const Scalar<DataVector>& conformal_stress_trace,\n      const tnsr::I<DataVector, 3>& conformal_momentum_density,\n      const Scalar<DataVector>& extrinsic_curvature_trace,\n      const Scalar<DataVector>& dt_extrinsic_curvature_trace,\n      const tnsr::i<DataVector, 3>& extrinsic_curvature_trace_gradient,\n      const tnsr::I<DataVector, 3>& shift_background,\n      const tnsr::II<DataVector, 3>&\n          longitudinal_shift_background_minus_dt_conformal_metric,\n      const tnsr::I<DataVector, 3>&\n      /*div_longitudinal_shift_background_minus_dt_conformal_metric*/,\n      const tnsr::ii<DataVector, 3>& conformal_metric,\n      const tnsr::II<DataVector, 3>& inv_conformal_metric,\n      const tnsr::ijj<DataVector, 3>& /*conformal_christoffel_first_kind*/,\n      const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n      const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n      const Scalar<DataVector>& conformal_ricci_scalar,\n      const Scalar<DataVector>& conformal_factor_minus_one,\n      const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n      const tnsr::I<DataVector, 3>& shift_excess,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux,\n      const tnsr::I<DataVector, 3>& lapse_times_conformal_factor_flux,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_excess,\n      const Scalar<DataVector>& conformal_factor_correction,\n      const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n      const tnsr::I<DataVector, 3>& shift_excess_correction,\n      const tnsr::i<DataVector, 3>& /*conformal_factor_gradient_correction*/,\n      const tnsr::i<DataVector, 3>&\n      /*lapse_times_conformal_factor_gradient_correction*/,\n      const tnsr::iJ<DataVector, 3>& /*deriv_shift_excess_correction*/,\n      const tnsr::I<DataVector, 3>& conformal_factor_flux_correction,\n      const tnsr::I<DataVector, 3>&\n          lapse_times_conformal_factor_flux_correction,\n      const tnsr::II<DataVector, 3>& longitudinal_shift_excess_correction);\n};\n/// \\endcond\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/Geometry.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Xcts {\n/// \\brief Types of conformal geometries for the XCTS equations\nenum class Geometry {\n  /// Euclidean (flat) manifold with Cartesian coordinates, i.e. the conformal\n  /// metric has components \\f$\\bar{\\gamma}_{ij} = \\delta_{ij}\\f$ in these\n  /// coordinates and thus all Christoffel symbols vanish:\n  /// \\f$\\bar{\\Gamma}^i_{jk}=0\\f$.\n  FlatCartesian,\n  /// The conformal geometry is either curved or employs curved coordinates, so\n  /// non-vanishing Christoffel symbols must be taken into account.\n  Curved\n};\n}  // namespace Xcts\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/HydroQuantities.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::Tags {\n\n/*!\n * \\brief MHD quantities retrieved from the background solution/data\n *\n * Retrieve the `HydroTags` from the background solution/data so they can be\n * written out to disk. These quantities don't take part in a pure XCTS solve,\n * (which only solves for the gravity sector given the matter profile) except\n * for their contributions to the matter source terms in the XCTS equations.\n * However, the matter source terms are computed and stored separately, so these\n * hydro quantities are only used for observations.\n */\ntemplate <typename HydroTags>\nstruct HydroQuantitiesCompute : ::Tags::Variables<HydroTags>, db::ComputeTag {\n  using base = ::Tags::Variables<HydroTags>;\n  using argument_tags = tmpl::list<\n      domain::Tags::Coordinates<3, Frame::Inertial>,\n      elliptic::Tags::Background<elliptic::analytic_data::Background>,\n      Parallel::Tags::Metavariables>;\n  template <typename Metavariables>\n  static void function(const gsl::not_null<Variables<HydroTags>*> result,\n                       const tnsr::I<DataVector, 3>& inertial_coords,\n                       const elliptic::analytic_data::Background& background,\n                       const Metavariables& /*meta*/) {\n    using background_classes =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 elliptic::analytic_data::Background>;\n    *result = call_with_dynamic_type<Variables<HydroTags>, background_classes>(\n        &background, [&inertial_coords](const auto* const derived) {\n          return variables_from_tagged_tuple(\n              derived->variables(inertial_coords, HydroTags{}));\n        });\n  }\n};\n\n}  // namespace Xcts::Tags\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n\nnamespace Xcts {\n/// Tags related to the XCTS equations\nnamespace Tags {\n\n/*!\n * \\brief The conformal factor \\f$\\psi(x)\\f$ that rescales the spatial metric\n * \\f$\\gamma_{ij}=\\psi^4\\bar{\\gamma}_{ij}\\f$.\n */\ntemplate <typename DataType>\nstruct ConformalFactor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The conformal factor minus one \\f$\\psi(x) - 1\\f$. Useful as dynamic\n * variable in formulations of the XCTS equations because it approaches zero at\n * spatial infinity rather than one, hence derivatives may be more accurate.\n */\ntemplate <typename DataType>\nstruct ConformalFactorMinusOne : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The conformally scaled spatial metric\n * \\f$\\bar{\\gamma}_{ij}=\\psi^{-4}\\gamma_{ij}\\f$, where \\f$\\psi\\f$ is the\n * `Xcts::Tags::ConformalFactor` and \\f$\\gamma_{ij}\\f$ is the\n * `gr::Tags::SpatialMetric`\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nusing ConformalMetric =\n    gr::Tags::Conformal<gr::Tags::SpatialMetric<DataType, Dim, Frame>, -4>;\n\n/*!\n * \\brief The conformally scaled inverse spatial metric\n * \\f$\\bar{\\gamma}^{ij}=\\psi^{4}\\gamma^{ij}\\f$, where \\f$\\psi\\f$ is the\n * `Xcts::Tags::ConformalFactor` and \\f$\\gamma^{ij}\\f$ is the\n * `gr::Tags::InverseSpatialMetric`\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nusing InverseConformalMetric =\n    gr::Tags::Conformal<gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>,\n                        4>;\n\n/*!\n * \\brief The product of lapse \\f$\\alpha(x)\\f$ and conformal factor\n * \\f$\\psi(x)\\f$\n *\n * This quantity is commonly used in formulations of the XCTS equations.\n */\ntemplate <typename DataType>\nstruct LapseTimesConformalFactor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The lapse times the conformal factor minus one \\f$\\alpha \\psi - 1\\f$.\n *\n * \\see `Xcts::Tags::ConformalFactorMinusOne`\n */\ntemplate <typename DataType>\nstruct LapseTimesConformalFactorMinusOne : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The constant part \\f$\\beta^i_\\mathrm{background}\\f$ of the shift\n * \\f$\\beta^i=\\beta^i_\\mathrm{background} + \\beta^i_\\mathrm{excess}\\f$\n *\n * \\see `Xcts::Tags::ShiftExcess`\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ShiftBackground : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The dynamic part \\f$\\beta^i_\\mathrm{excess}\\f$ of the shift\n * \\f$\\beta^i=\\beta^i_\\mathrm{background} + \\beta^i_\\mathrm{excess}\\f$\n *\n * We commonly split off the part of the shift that diverges at large coordinate\n * distances (the \"background\" shift \\f$\\beta^i_\\mathrm{background}\\f$) and\n * solve only for the remainder (the \"excess\" shift\n * \\f$\\beta^i_\\mathrm{excess}\\f$). For example, the background shift might be a\n * uniform rotation \\f$\\beta^i_\\mathrm{background}=(-\\Omega y, \\Omega x, 0)\\f$\n * with angular velocity \\f$\\Omega\\f$ around the z-axis, given here in Cartesian\n * coordinates.\n *\n * \\see `Xcts::Tags::Background`\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ShiftExcess : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The symmetric \"strain\" of the shift vector\n * \\f$B_{ij} = \\bar{D}_{(i}\\bar{\\gamma}_{j)k}\\beta^k =\n * \\left(\\partial_{(i}\\bar{\\gamma}_{j)k} - \\bar{\\Gamma}_{kij}\\right)\\beta^k\\f$\n *\n * This quantity is used in our formulations of the XCTS equations.\n *\n * Note that the shift is not a conformal quantity, so its index is generally\n * raised and lowered with the spatial metric, not with the conformal metric.\n * However, to compute this \"strain\" we use the conformal metric as defined\n * above. The conformal longitudinal shift in terms of this quantity is then:\n *\n * \\f{equation}\n * (\\bar{L}\\beta)^{ij} = 2\\left(\\bar{\\gamma}^{ik}\\bar{\\gamma}^{jl}\n * - \\frac{1}{3}\\bar{\\gamma}^{ij}\\bar{\\gamma}^{kl}\\right) B_{kl}\n * \\f}\n *\n * Note that the conformal longitudinal shift is (minus) the \"stress\" quantity\n * of a linear elasticity system in which the shift takes the role of the\n * displacement vector and the definition of its \"strain\" remains the same. This\n * auxiliary elasticity system is formulated on an isotropic constitutive\n * relation based on the conformal metric with vanishing bulk modulus \\f$K=0\\f$\n * (not to be confused with the extrinsic curvature trace \\f$K\\f$ in this\n * context) and unit shear modulus \\f$\\mu=1\\f$. See the\n * `Elasticity::FirstOrderSystem` and the\n * `Elasticity::ConstitutiveRelations::IsotropicHomogeneous` for details.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ShiftStrain : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The conformal longitudinal operator applied to the shift excess\n * \\f$(\\bar{L}\\beta_\\mathrm{excess})^{ij}\\f$\n *\n * This quantity can be used as the \"flux\" for the momentum constraint in\n * formulations of the XCTS equations, because the principal part of the\n * momentum constraint is essentially the divergence of this quantity.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct LongitudinalShiftExcess : db::SimpleTag {\n  using type = tnsr::II<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The conformal longitudinal operator applied to the background shift\n * vector minus the time derivative of the conformal metric\n * \\f$(\\bar{L}\\beta_\\mathrm{background})^{ij} - \\bar{u}^{ij}\\f$\n *\n * This quantity appears in formulation of the XCTS equations (see `Xcts`) and\n * serves to specify their free data \\f$\\bar{u}_{ij}\\f$. It is combined with the\n * longitudinal background shift because the two quantities are degenerate.\n *\n * \\note As usual for conformal quantities, the indices here are raised with the\n * conformal metric: \\f$\\bar{u}^{ij} = \\bar{\\gamma}^{ik}\\bar{\\gamma}^{jl}\n * \\partial_t\\bar{\\gamma}_{kl}\\f$.\n *\n * \\see `Xcts::Tags::ShiftBackground`\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct LongitudinalShiftBackgroundMinusDtConformalMetric : db::SimpleTag {\n  using type = tnsr::II<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The conformal longitudinal operator applied to the shift vector minus\n * the time derivative of the conformal metric, squared:\n * \\f$\\left((\\bar{L}\\beta)^{ij} - \\bar{u}^{ij}\\right)\n * \\left((\\bar{L}\\beta)_{ij} - \\bar{u}_{ij}\\right)\\f$\n */\ntemplate <typename DataType>\nstruct LongitudinalShiftMinusDtConformalMetricSquare : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The conformal longitudinal operator applied to the shift vector minus\n * the time derivative of the conformal metric, squared and divided by the\n * square of the lapse:\n * \\f$\\frac{1}{\\alpha^2}\\left((\\bar{L}\\beta)^{ij} - \\bar{u}^{ij}\\right)\n * \\left((\\bar{L}\\beta)_{ij} - \\bar{u}_{ij}\\right)\\f$\n */\ntemplate <typename DataType>\nstruct LongitudinalShiftMinusDtConformalMetricOverLapseSquare : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The shift vector contracted with the gradient of the trace of the\n * extrinsic curvature: \\f$\\beta^i\\partial_i K\\f$\n *\n * The shift vector in this quantity is the full shift\n * \\f$\\beta^i=\\beta^i_\\mathrm{background}+\\beta^i_\\mathrm{excess}\\f$ (see\n * `Xcts::Tags::ShiftExcess` for details on this split).\n */\ntemplate <typename DataType>\nstruct ShiftDotDerivExtrinsicCurvatureTrace : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The Christoffel symbols of the first kind related to the conformal\n * metric \\f$\\bar{\\gamma}_{ij}\\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ConformalChristoffelFirstKind : db::SimpleTag {\n  using type = tnsr::ijj<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The Christoffel symbols of the second kind related to the conformal\n * metric \\f$\\bar{\\gamma}_{ij}\\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ConformalChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::Ijj<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The Christoffel symbols of the second kind (related to the conformal\n * metric \\f$\\bar{\\gamma}_{ij}\\f$) contracted in their first two indices:\n * \\f$\\bar{\\Gamma}_k = \\bar{\\Gamma}^{i}_{ik}\\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ConformalChristoffelContracted : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The Ricci tensor related to the conformal metric\n * \\f$\\bar{\\gamma}_{ij}\\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ConformalRicciTensor : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The Ricci scalar related to the conformal metric\n * \\f$\\bar{\\gamma}_{ij}\\f$\n */\ntemplate <typename DataType>\nstruct ConformalRicciScalar : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n}  // namespace Tags\n}  // namespace Xcts\n"
  },
  {
    "path": "src/Elliptic/Systems/Xcts/Xcts.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Documents the `Xcts` namespace\n\n#pragma once\n\n/*!\n * \\ingroup EllipticSystemsGroup\n * \\brief Items related to solving the Extended Conformal Thin Sandwich (XCTS)\n * decomposition of the Einstein constraint equations\n *\n * The XCTS equations\n *\n * \\f{align}\n * \\bar{D}^2 \\psi - \\frac{1}{8}\\psi\\bar{R} - \\frac{1}{12}\\psi^5 K^2 +\n * \\frac{1}{8}\\psi^{-7}\\bar{A}_{ij}\\bar{A}^{ij} &= -2\\pi\\psi^5\\rho\n * \\\\\n * \\bar{D}_i(\\bar{L}\\beta)^{ij} - (\\bar{L}\\beta)^{ij}\\bar{D}_i\n * \\ln(\\bar{\\alpha}) &= \\bar{\\alpha}\\bar{D}_i\\left(\\bar{\\alpha}^{-1}\\bar{u}^{ij}\n * \\right) + \\frac{4}{3}\\bar{\\alpha}\\psi^6\\bar{D}^j K + 16\\pi\\bar{\\alpha}\n * \\psi^{10}S^j\n * \\\\\n * \\bar{D}^2\\left(\\alpha\\psi\\right) &=\n * \\alpha\\psi\\left(\\frac{7}{8}\\psi^{-8}\\bar{A}_{ij}\\bar{A}^{ij}\n * + \\frac{5}{12}\\psi^4 K^2 + \\frac{1}{8}\\bar{R}\n * + 2\\pi\\psi^4\\left(\\rho + 2S\\right)\\right)\n * - \\psi^5\\partial_t K + \\psi^5\\beta^i\\bar{D}_i K\n * \\\\\n * \\text{with} \\quad \\bar{A} &= \\frac{1}{2\\bar{\\alpha}}\n * \\left((\\bar{L}\\beta)^{ij} - \\bar{u}^{ij}\\right) \\\\\n * \\quad \\text{and} \\quad \\bar{\\alpha} &= \\alpha \\psi^{-6}\n * \\f}\n *\n * are a set of nonlinear elliptic equations that the spacetime metric in\n * general relativity must satisfy at all times. For an introduction see e.g.\n * \\cite BaumgarteShapiro, in particular Box 3.3 which is largely mirrored here.\n * We solve the XCTS equations for the conformal factor \\f$\\psi\\f$, the product\n * of lapse times conformal factor \\f$\\alpha\\psi\\f$ and the shift vector\n * \\f$\\beta^j\\f$. The remaining quantities in the equations, i.e. the conformal\n * metric \\f$\\bar{\\gamma}_{ij}\\f$, the trace of the extrinsic curvature \\f$K\\f$,\n * their respective time derivatives \\f$\\bar{u}_{ij}\\f$ and \\f$\\partial_t K\\f$,\n * the energy density \\f$\\rho\\f$, the stress-energy trace \\f$S\\f$ and the\n * momentum density \\f$S^i\\f$, are freely specifyable fields that define the\n * physical scenario at hand. Of particular importance is the conformal metric,\n * which defines the background geometry, the covariant derivative\n * \\f$\\bar{D}\\f$, the Ricci scalar \\f$\\bar{R}\\f$ and the longitudinal operator\n *\n * \\f{equation}\n * \\left(\\bar{L}\\beta\\right)^{ij} = \\bar{D}^i\\beta^j + \\bar{D}^j\\beta^i\n * - \\frac{2}{3}\\bar{\\gamma}^{ij}\\bar{D}_k\\beta^k\n * \\text{.}\n * \\f}\n *\n * Note that the XCTS equations are essentially two Poisson equations and one\n * Elasticity equation with nonlinear sources on a curved geometry. In this\n * analogy, the longitudinal operator plays the role of the elastic constitutive\n * relation that connects the symmetric \"shift strain\"\n * \\f$\\bar{D}_{(i}\\beta_{j)}\\f$ with the \"stress\" \\f$(\\bar{L}\\beta)^{ij}\\f$ of\n * which we take the divergence in the momentum constraint. This particular\n * constitutive relation is equivalent to an isotropic and homogeneous material\n * with bulk modulus \\f$K=0\\f$ (not to be confused with the extrinsic curvature\n * trace \\f$K\\f$ in this context) and shear modulus \\f$\\mu=1\\f$ (see\n * `Elasticity::ConstitutiveRelations::IsotropicHomogeneous`).\n *\n * Once the XCTS equations are solved we can construct the spatial metric and\n * extrinsic curvature as\n *\n * \\f{align}\n * \\gamma_{ij} &= \\psi^4\\bar{\\gamma}_{ij} \\\\\n * K_{ij} &= \\psi^{-2}\\bar{A}_{ij} + \\frac{1}{3}\\gamma_{ij} K\n * \\f}\n *\n * from which we can compose the full spacetime metric.\n */\nnamespace Xcts {}\n"
  },
  {
    "path": "src/Elliptic/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\n/// Functionality related to solving elliptic partial differential equations\nnamespace elliptic {\n\nnamespace OptionTags {\n\ntemplate <typename BackgroundType>\nstruct Background {\n  static constexpr Options::String help =\n      \"The variable-independent part of the equations that define the problem \"\n      \"to solve (along with the boundary conditions).\";\n  using type = std::unique_ptr<BackgroundType>;\n};\n\ntemplate <typename InitialGuessType>\nstruct InitialGuess {\n  static constexpr Options::String help =\n      \"The initial guess for the elliptic solve. The solve converges faster if \"\n      \"the initial guess is close to the solution and may not converge at all \"\n      \"if the initial guess is too far away.\";\n  using type = std::unique_ptr<InitialGuessType>;\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n\n/// The variable-independent part of the elliptic equations, e.g. the\n/// fixed-sources \\f$f(x)\\f$ in a Poisson equation \\f$-\\Delta u=f(x)\\f$, the\n/// matter-density in a TOV-solve or the conformal metric in an XCTS solve.\ntemplate <typename BackgroundType>\nstruct Background : db::SimpleTag {\n  using type = std::unique_ptr<BackgroundType>;\n  using option_tags = tmpl::list<OptionTags::Background<BackgroundType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) {\n    return serialize_and_deserialize<type>(value);\n  }\n};\n\n/// The initial guess for the elliptic solve.\ntemplate <typename InitialGuessType>\nstruct InitialGuess : db::SimpleTag {\n  using type = std::unique_ptr<InitialGuessType>;\n  using option_tags = tmpl::list<OptionTags::InitialGuess<InitialGuessType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) {\n    return serialize_and_deserialize<type>(value);\n  }\n};\n\n}  // namespace Tags\n}  // namespace elliptic\n"
  },
  {
    "path": "src/Elliptic/Triggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  EveryNIterations.hpp\n  Factory.hpp\n  HasConverged.hpp\n  )\n"
  },
  {
    "path": "src/Elliptic/Triggers/EveryNIterations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <pup.h>\n\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// Trigger every N iterations of the solver identifid by the `Label`, after a\n/// given offset.\ntemplate <typename Label>\nclass EveryNIterations : public Trigger {\n public:\n  /// \\cond\n  EveryNIterations() = default;\n  explicit EveryNIterations(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(EveryNIterations);  // NOLINT\n  /// \\endcond\n\n  struct N {\n    using type = uint64_t;\n    static constexpr Options::String help{\"How frequently to trigger.\"};\n    static type lower_bound() { return 1; }\n  };\n  struct Offset {\n    using type = uint64_t;\n    static constexpr Options::String help{\"First iteration to trigger on.\"};\n  };\n\n  using options = tmpl::list<N, Offset>;\n  static constexpr Options::String help{\n      \"Trigger every N iterations after a given offset.\"};\n\n  EveryNIterations(const uint64_t interval, const uint64_t offset)\n      : interval_(interval), offset_(offset) {}\n\n  using argument_tags = tmpl::list<Convergence::Tags::IterationId<Label>>;\n\n  bool operator()(const size_t iteration_id) const {\n    const auto step_number = static_cast<uint64_t>(iteration_id);\n    return step_number >= offset_ and (step_number - offset_) % interval_ == 0;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | interval_;\n    p | offset_;\n  }\n\n private:\n  uint64_t interval_{0};\n  uint64_t offset_{0};\n};\n\n/// \\cond\ntemplate <typename Label>\nPUP::able::PUP_ID EveryNIterations<Label>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace elliptic::Triggers\n"
  },
  {
    "path": "src/Elliptic/Triggers/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Elliptic/Triggers/EveryNIterations.hpp\"\n#include \"Elliptic/Triggers/HasConverged.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Triggers for elliptic executables\nnamespace elliptic::Triggers {\ntemplate <typename Label>\nusing all_triggers =\n    tmpl::push_front<::Triggers::logical_triggers, EveryNIterations<Label>,\n                     HasConverged<Label>>;\n}  // namespace elliptic::Triggers\n"
  },
  {
    "path": "src/Elliptic/Triggers/HasConverged.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// Trigger when the solver identified by the `Label` has converged.\ntemplate <typename Label>\nclass HasConverged : public Trigger {\n public:\n  /// \\cond\n  HasConverged() = default;\n  explicit HasConverged(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(HasConverged);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Trigger when the solver has converged.\"};\n\n  using argument_tags = tmpl::list<Convergence::Tags::HasConverged<Label>>;\n\n  bool operator()(const Convergence::HasConverged& has_converged) const {\n    return static_cast<bool>(has_converged);\n  }\n};\n\n/// \\cond\ntemplate <typename Label>\nPUP::able::PUP_ID HasConverged<Label>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace elliptic::Triggers\n"
  },
  {
    "path": "src/Elliptic/Utilities/ApplyAt.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Utilities to retrieve values from maps in tagged containers\n\n#pragma once\n\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TupleSlice.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n#include \"Utilities/TypeTraits/IsMaplike.hpp\"\n\nnamespace elliptic::util {\nnamespace detail {\nCREATE_IS_CALLABLE(apply)\nCREATE_IS_CALLABLE_V(apply)\n\ntemplate <typename F, typename Args>\nstruct ApplyImpl;\n\ntemplate <typename F, typename... Args>\nstruct ApplyImpl<F, std::tuple<Args...>> {\n  // Dispatch to static apply function or call operator, whichever is available\n  static constexpr decltype(auto) apply(F&& f, Args&&... args) {\n    if constexpr (is_apply_callable_v<F, Args...>) {\n      return F::apply(std::forward<Args>(args)...);\n    } else {\n      return std::forward<F>(f)(std::forward<Args>(args)...);\n    }\n  }\n};\n\ntemplate <bool PassThrough, typename... MapKeys>\nstruct unmap_arg;\n\ntemplate <typename... MapKeys>\nstruct unmap_arg<true, MapKeys...> {\n  template <typename T>\n  static constexpr const T& apply(const T& arg,\n                                  const std::tuple<MapKeys...>& /*map_keys*/) {\n    return arg;\n  }\n\n  template <typename T>\n  static constexpr gsl::not_null<T*> apply(\n      const gsl::not_null<T*> arg, const std::tuple<MapKeys...>& /*map_keys*/) {\n    return arg;\n  }\n};\n\ntemplate <typename FirstMapKey, typename... MapKeys>\nstruct unmap_arg<false, FirstMapKey, MapKeys...> {\n  template <typename T>\n  static constexpr decltype(auto) apply(\n      const T& arg, const std::tuple<FirstMapKey, MapKeys...>& map_keys) {\n    return unmap_arg<((sizeof...(MapKeys) == 0) or\n                      (not tt::is_maplike_v<typename T::mapped_type>)),\n                     MapKeys...>::apply(arg.at(std::get<0>(map_keys)),\n                                        tuple_tail<sizeof...(MapKeys)>(\n                                            map_keys));\n  }\n\n  template <typename T>\n  static constexpr decltype(auto) apply(\n      const gsl::not_null<T*> arg,\n      const std::tuple<FirstMapKey, MapKeys...>& map_keys) {\n    return unmap_arg<((sizeof...(MapKeys) == 0) or\n                      (not tt::is_maplike_v<typename T::mapped_type>)),\n                     MapKeys...>::apply(make_not_null(&arg->\n                                                       operator[](std::get<0>(\n                                                           map_keys))),\n                                        tuple_tail<sizeof...(MapKeys)>(\n                                            map_keys));\n  }\n};\n\ntemplate <typename... ArgumentTags, typename PassthroughArgumentTags,\n          typename F, typename TaggedContainer, typename... MapKeys,\n          typename... Args>\nSPECTRE_ALWAYS_INLINE decltype(auto) apply_at(\n    F&& f, const TaggedContainer& box, const std::tuple<MapKeys...>& map_keys,\n    tmpl::list<ArgumentTags...> /*meta*/, PassthroughArgumentTags /*meta*/,\n    Args&&... args) {\n  using ::db::apply;\n  using ::tuples::apply;\n  return apply<tmpl::list<ArgumentTags...>>(\n      [&f, &map_keys, &args...](const auto&... args_items) {\n        (void)map_keys;\n        return std::forward<F>(f)(\n            unmap_arg<\n                tmpl::list_contains_v<PassthroughArgumentTags, ArgumentTags>,\n                MapKeys...>::apply(args_items, map_keys)...,\n            std::forward<Args>(args)...);\n      },\n      box);\n}\n\n}  // namespace detail\n\n/// @{\n/*!\n * \\brief Apply the invokable `f` with arguments from maps in the DataBox\n *\n * Retrieves the `ArgumentTags` from the DataBox, evaluates them at the\n * `map_key(s)` (by calling their `at` member function for every map key in\n * turn) and calls the invokable `f` with the unmapped arguments. The tags in\n * `PassthroughArgumentTags` are passed directly to `f` without unmapping them.\n *\n * For example, a DataBox may have these tags of which two are maps:\n *\n * \\snippet Test_ApplyAt.cpp apply_at_tags\n *\n * You can use `apply_at` to evaluate a function at a particular key for these\n * maps:\n *\n * \\snippet Test_ApplyAt.cpp apply_at_example\n *\n * \\see db::apply\n */\ntemplate <typename ArgumentTags, typename PassthroughArgumentTags,\n          typename... MapKeys, typename F, typename TaggedContainer,\n          typename... Args>\nSPECTRE_ALWAYS_INLINE decltype(auto) apply_at(\n    F&& f, const TaggedContainer& box, const std::tuple<MapKeys...>& map_keys,\n    Args&&... args) {\n  return detail::apply_at(std::forward<F>(f), box, map_keys, ArgumentTags{},\n                          PassthroughArgumentTags{},\n                          std::forward<Args>(args)...);\n}\n\ntemplate <typename ArgumentTags, typename PassthroughArgumentTags,\n          typename MapKey, typename F, typename TaggedContainer,\n          typename... Args>\nSPECTRE_ALWAYS_INLINE decltype(auto) apply_at(F&& f, const TaggedContainer& box,\n                                              const MapKey& map_key,\n                                              Args&&... args) {\n  return detail::apply_at(\n      std::forward<F>(f), box, std::forward_as_tuple(map_key), ArgumentTags{},\n      PassthroughArgumentTags{}, std::forward<Args>(args)...);\n}\n/// @}\n\nnamespace detail {\n\ntemplate <typename... ReturnTags, typename... ArgumentTags,\n          typename PassthroughTags, typename F, typename TaggedContainer,\n          typename... MapKeys, typename... Args>\nSPECTRE_ALWAYS_INLINE void mutate_apply_at(\n    F&& f, const gsl::not_null<TaggedContainer*> box,\n    const std::tuple<MapKeys...>& map_keys, tmpl::list<ReturnTags...> /*meta*/,\n    tmpl::list<ArgumentTags...> /*meta*/, PassthroughTags /*meta*/,\n    Args&&... args) {\n  using ::db::apply;\n  using ::db::mutate_apply;\n  using ::tuples::apply;\n  const auto args_items = apply<tmpl::list<ArgumentTags...>>(\n      [](const auto&... expanded_args_items) {\n        return std::forward_as_tuple(expanded_args_items...);\n      },\n      *box);\n  using argument_tags = tmpl::list<ArgumentTags...>;\n  mutate_apply<tmpl::list<ReturnTags...>, tmpl::list<>>(\n      [&f, &map_keys, &args_items, &args...](const auto... mutated_items) {\n        (void)map_keys;\n        (void)args_items;\n        auto all_args = std::tuple_cat(\n            std::make_tuple(\n                unmap_arg<tmpl::list_contains_v<PassthroughTags, ReturnTags>,\n                          MapKeys...>::apply(mutated_items, map_keys)...),\n            std::forward_as_tuple(\n                unmap_arg<tmpl::list_contains_v<PassthroughTags, ArgumentTags>,\n                          MapKeys...>::\n                    apply(std::get<tmpl::index_of<argument_tags,\n                                                  ArgumentTags>::value>(\n                              args_items),\n                          map_keys)...,\n                args...));\n        std::apply(\n            [&f](auto&&... expanded_args) {\n              ApplyImpl<F, std::decay_t<decltype(all_args)>>::apply(\n                  std::forward<F>(f), std::move(expanded_args)...);\n            },\n            std::move(all_args));\n      },\n      box);\n}\n\n}  // namespace detail\n\n/// @{\n/*!\n * \\brief Apply the invokable `f` to mutate items in maps in the DataBox\n *\n * Retrieves the `MutateTags` and `ArgumentTags` from the DataBox, evaluates\n * them at the `map_key(s)` (by calling their `at` member function for every map\n * key in turn) and calls the invokable `f` with the unmapped arguments. The\n * tags in `PassthroughTags` are passed directly to `f` without unmapping them.\n *\n * For example, a DataBox may have these tags of which two are maps:\n *\n * \\snippet Test_ApplyAt.cpp apply_at_tags\n *\n * You can use `mutate_apply_at` to mutate items at a particular key for these\n * maps:\n *\n * \\snippet Test_ApplyAt.cpp mutate_apply_at_example\n *\n * \\see db::mutate_apply\n */\ntemplate <typename MutateTags, typename ArgumentTags, typename PassthroughTags,\n          typename F, typename TaggedContainer, typename... MapKeys,\n          typename... Args>\nSPECTRE_ALWAYS_INLINE void mutate_apply_at(\n    F&& f, const gsl::not_null<TaggedContainer*> box,\n    const std::tuple<MapKeys...>& map_keys, Args&&... args) {\n  detail::mutate_apply_at(std::forward<F>(f), box, map_keys, MutateTags{},\n                          ArgumentTags{}, PassthroughTags{},\n                          std::forward<Args>(args)...);\n}\n\ntemplate <typename MutateTags, typename ArgumentTags, typename PassthroughTags,\n          typename F, typename TaggedContainer, typename MapKey,\n          typename... Args>\nSPECTRE_ALWAYS_INLINE void mutate_apply_at(\n    F&& f, const gsl::not_null<TaggedContainer*> box, const MapKey& map_key,\n    Args&&... args) {\n  detail::mutate_apply_at(\n      std::forward<F>(f), box, std::forward_as_tuple(map_key), MutateTags{},\n      ArgumentTags{}, PassthroughTags{}, std::forward<Args>(args)...);\n}\n/// @}\n\n}  // namespace elliptic::util\n"
  },
  {
    "path": "src/Elliptic/Utilities/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApplyAt.hpp\n  GetAnalyticData.hpp\n  )\n"
  },
  {
    "path": "src/Elliptic/Utilities/GetAnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::util {\n/// Get data for the tensors in `TagsList` from the `analytic_data`. The\n/// `analytic_data` can be any subclass of `Base` that is listed in\n/// `Metavariables::factory_creation`.\ntemplate <typename TagsList, typename Base, typename DbTagsList,\n          typename... Args>\nVariables<TagsList> get_analytic_data(const Base& analytic_data,\n                                      const db::DataBox<DbTagsList>& box,\n                                      const Args&... args) {\n  using factory_classes =\n      typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n          box))>::factory_creation::factory_classes;\n  return call_with_dynamic_type<Variables<TagsList>,\n                                tmpl::at<factory_classes, Base>>(\n      &analytic_data, [&args...](const auto* const derived) {\n        return variables_from_tagged_tuple(\n            derived->variables(args..., TagsList{}));\n      });\n}\n}  // namespace elliptic::util\n"
  },
  {
    "path": "src/Evolution/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  RunEventsAndDenseTriggers.hpp\n  RunEventsAndTriggers.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Actions/RunEventsAndDenseTriggers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::Actions {\n/// \\ingroup ActionsGroup\n/// \\ingroup EventsAndTriggersGroup\n/// \\brief Run the events and dense triggers\n///\n/// If dense output is required, each `postprocessor` in the \\p\n/// Postprocessors list will be called as\n/// `postprocessor::is_ready(make_not_null(&box),\n/// make_not_null(&inboxes), cache, array_index, component)`.  If it\n/// returns false, the algorithm will be stopped to wait for more\n/// data.  After performing dense output, each of the \\p\n/// Postprocessors will be passed to `db::mutate_apply` on the\n/// DataBox.  The wrapper struct `AlwaysReadyPostprocessor` is\n/// provided for convenience to provide an `is_ready` function when a\n/// pure mutate-apply is desired.\n///\n/// At the end of the action, the values of the time, evolved\n/// variables, and anything appearing in the `return_tags` of the \\p\n/// Postprocessors will be restored to their initial values.\n///\n/// Uses:\n/// - DataBox: EventsAndDenseTriggers and as required by events,\n///   triggers, and postprocessors\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: as performed by the postprocessor `is_ready` functions\ntemplate <typename Postprocessors>\nstruct RunEventsAndDenseTriggers {\n private:\n  static_assert(tt::is_a_v<tmpl::list, Postprocessors>);\n\n  // RAII object to restore the time and variables changed by dense\n  // output.\n  template <typename DbTags, typename Tags>\n  class StateRestorer {\n    template <typename Tag, bool IsVariables>\n    struct expand_variables_impl {\n      using type = typename Tag::tags_list;\n    };\n\n    template <typename Tag>\n    struct expand_variables_impl<Tag, false> {\n      using type = tmpl::list<Tag>;\n    };\n\n    template <typename Tag>\n    struct expand_variables\n        : expand_variables_impl<Tag,\n                                tt::is_a_v<Variables, typename Tag::type>> {};\n\n    using expanded_tags = tmpl::remove_duplicates<\n        tmpl::join<tmpl::transform<Tags, expand_variables<tmpl::_1>>>>;\n    using tensors_and_non_tensors = tmpl::partition<\n        expanded_tags,\n        tmpl::bind<\n            tmpl::apply,\n            tmpl::if_<tt::is_a<Tensor, tmpl::bind<tmpl::type_from, tmpl::_1>>,\n                      tmpl::defer<tmpl::parent<std::is_same<\n                          tmpl::bind<tmpl::type_from,\n                                     tmpl::bind<tmpl::type_from, tmpl::_1>>,\n                          DataVector>>>,\n                      std::false_type>>>;\n    using tensor_tags = tmpl::front<tensors_and_non_tensors>;\n    using non_tensor_tags = tmpl::back<tensors_and_non_tensors>;\n\n   public:\n    StateRestorer(const gsl::not_null<db::DataBox<DbTags>*> box) : box_(box) {}\n\n    void save() {\n      // Only store the value the first time, because after that we\n      // are seeing the value after the previous change instead of the\n      // original.\n      if (not non_tensors_.has_value()) {\n        if constexpr (not std::is_same_v<tensor_tags, tmpl::list<>>) {\n          tensors_.initialize(\n              db::get<tmpl::front<tensor_tags>>(*box_).begin()->size());\n          tmpl::for_each<tensor_tags>([this](auto tag_v) {\n            using tag = tmpl::type_from<decltype(tag_v)>;\n            get<tag>(tensors_) = db::get<tag>(*box_);\n          });\n        }\n        tmpl::as_pack<non_tensor_tags>([this](auto... tags_v) {\n          non_tensors_.emplace(\n              db::get<tmpl::type_from<decltype(tags_v)>>(*box_)...);\n        });\n      }\n    }\n\n    // WARNING: Manually calling this if there are non_tensor_tags\n    // will cause use-after-moves.\n    void restore() {\n      if (non_tensors_.has_value()) {\n        tmpl::for_each<tensor_tags>([this](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          db::mutate<tag>(\n              [this](const gsl::not_null<typename tag::type*> value) {\n                *value = get<tag>(tensors_);\n              },\n              box_);\n        });\n        tmpl::for_each<non_tensor_tags>([this](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          db::mutate<tag>(\n              [this](const gsl::not_null<typename tag::type*> value) {\n                *value = std::move(tuples::get<tag>(*non_tensors_));\n              },\n              box_);\n        });\n      }\n    }\n\n    ~StateRestorer() { restore(); }\n\n   private:\n    gsl::not_null<db::DataBox<DbTags>*> box_ = nullptr;\n    // Store all tensors in a single allocation.\n    Variables<tensor_tags> tensors_{};\n    std::optional<tuples::tagged_tuple_from_typelist<non_tensor_tags>>\n        non_tensors_;\n  };\n\n  template <typename T>\n  struct get_return_tags {\n    using type = typename T::return_tags;\n  };\n\n public:\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const component) {\n    using system = typename Metavariables::system;\n    using variables_tag = typename system::variables_tag;\n\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    if (time_step_id.slab_number() < 0) {\n      // Skip dense output during self-start\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    auto& events_and_dense_triggers =\n        db::get_mutable_reference<::Tags::EventsAndDenseTriggers>(\n            make_not_null(&box));\n\n    const auto step_end =\n        time_step_id.step_time() + db::get<::Tags::TimeStep>(box);\n    const evolution_less_equal<double> before_equal{\n        time_step_id.time_runs_forward()};\n\n    using postprocessor_return_tags =\n        tmpl::join<tmpl::transform<Postprocessors, get_return_tags<tmpl::_1>>>;\n    // The evolved variables will be restored anyway, so no reason to\n    // copy them twice.\n    using postprocessor_restore_tags =\n        tmpl::list_difference<postprocessor_return_tags,\n                              typename variables_tag::tags_list>;\n\n    StateRestorer<DbTags, tmpl::list<::Tags::Time>> time_restorer(\n        make_not_null(&box));\n    StateRestorer<DbTags, tmpl::list<variables_tag>> variables_restorer(\n        make_not_null(&box));\n    StateRestorer<DbTags, postprocessor_restore_tags> postprocessor_restorer(\n        make_not_null(&box));\n\n    for (;;) {\n      const double next_trigger = events_and_dense_triggers.next_trigger(box);\n      if (before_equal(step_end.value(), next_trigger)) {\n        return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n      }\n\n      // Avoid invalidating compute items unless necessary.\n      if (db::get<::Tags::Time>(box) != next_trigger) {\n        time_restorer.save();\n        db::mutate<::Tags::Time>(\n            [&next_trigger](const gsl::not_null<double*> time) {\n              *time = next_trigger;\n            },\n            make_not_null(&box));\n      }\n\n      const auto triggered = events_and_dense_triggers.is_ready(\n          make_not_null(&box), cache, array_index, component);\n      using TriggeringState = std::decay_t<decltype(triggered)>;\n      switch (triggered) {\n        case TriggeringState::NotReady:\n          return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n        case TriggeringState::NeedsEvolvedVariables: {\n          using history_tag = ::Tags::HistoryEvolvedVariables<variables_tag>;\n          bool dense_output_succeeded = false;\n          variables_restorer.save();\n          db::mutate<variables_tag>(\n              [&dense_output_succeeded, &next_trigger](\n                  gsl::not_null<typename variables_tag::type*> vars,\n                  const TimeStepper& stepper,\n                  const typename history_tag::type& history) {\n                *vars = *history.step_start(next_trigger).value;\n                dense_output_succeeded =\n                    stepper.dense_update_u(vars, history, next_trigger);\n              },\n              make_not_null(&box),\n              db::get<::Tags::TimeStepper<TimeStepper>>(box),\n              db::get<history_tag>(box));\n          if (not dense_output_succeeded) {\n            // Need to take another time step\n            return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n          }\n\n          bool ready = true;\n          tmpl::for_each<Postprocessors>([&](auto postprocessor_v) {\n            using postprocessor = tmpl::type_from<decltype(postprocessor_v)>;\n            if (ready) {\n              if (not postprocessor::is_ready(make_not_null(&box),\n                                              make_not_null(&inboxes), cache,\n                                              array_index, component)) {\n                ready = false;\n              }\n            }\n          });\n          if (not ready) {\n            return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n          }\n\n          postprocessor_restorer.save();\n          tmpl::for_each<Postprocessors>([&box](auto postprocessor_v) {\n            using postprocessor = tmpl::type_from<decltype(postprocessor_v)>;\n            db::mutate_apply<postprocessor>(make_not_null(&box));\n          });\n        }\n          [[fallthrough]];\n        default:\n          break;\n      }\n\n      events_and_dense_triggers.run_events(box, cache, array_index, component);\n      if (not events_and_dense_triggers.reschedule(make_not_null(&box), cache,\n                                                   array_index, component)) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n    }\n  }\n};\n\nstruct InitializeRunEventsAndDenseTriggers {\n  using simple_tags_from_options = tmpl::list<::Tags::EventsAndDenseTriggers>;\n  using simple_tags = tmpl::list<::Tags::PreviousTriggerTime>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*component*/) {\n    ::Initialization::mutate_assign<simple_tags>(make_not_null(&box),\n                                               std::nullopt);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\brief Initialize/update items related to events and dense triggers after an\n/// AMR change\n///\n/// Mutates:\n///   - ::Tags::EventsAndDenseTriggers\n///   - Tags::PreviousTriggerTime\n///\n/// For p-refinement:\n///   - Leaves both items unchanged\nstruct ProjectRunEventsAndDenseTriggers\n    : tt::ConformsTo<amr::protocols::Projector> {\n  using return_tags =\n      tmpl::list<::Tags::EventsAndDenseTriggers, ::Tags::PreviousTriggerTime>;\n  using argument_tags = tmpl::list<>;\n\n  template <size_t Dim>\n  static void apply(\n      const gsl::not_null<\n          EventsAndDenseTriggers*> /*events_and_dense_triggers*/,\n      const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,\n      const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {\n    // do not need to update anything\n  }\n\n  template <typename... Tags>\n  static void apply(\n      const gsl::not_null<EventsAndDenseTriggers*> events_and_dense_triggers,\n      const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,\n      const tuples::TaggedTuple<Tags...>& parent_items) {\n    *events_and_dense_triggers =\n        serialize_and_deserialize<EventsAndDenseTriggers>(\n            get<::Tags::EventsAndDenseTriggers>(parent_items));\n  }\n\n  template <size_t Dim, typename... Tags>\n  static void apply(\n      const gsl::not_null<EventsAndDenseTriggers*> events_and_dense_triggers,\n      const gsl::not_null<std::optional<double>*> /*previous_trigger_time*/,\n      const std::unordered_map<ElementId<Dim>, tuples::TaggedTuple<Tags...>>&\n          children_items) {\n    // Serialization of equivalent Events and DenseTriggers is not\n    // guaranteed to produce the same byte stream when there are\n    // things like unordered containers involved, so we can't compare\n    // with other children.\n    *events_and_dense_triggers =\n        serialize_and_deserialize<EventsAndDenseTriggers>(\n            get<::Tags::EventsAndDenseTriggers>(\n                children_items.begin()->second));\n  }\n};\n}  // namespace evolution::Actions\n\n/// A wrapper adding an always-true `is_ready` function for a\n/// `RunEventsAndDenseTriggers` postprocessor.  This allows structs\n/// designed as mutate_apply arguments to be used without\n/// modification.\ntemplate <typename T>\nstruct AlwaysReadyPostprocessor : T {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static bool is_ready(\n      const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,\n      const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*component*/) {\n    return true;\n  }\n};\n"
  },
  {
    "path": "src/Evolution/Actions/RunEventsAndTriggers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Time/SelfStart.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Triggers/OnSubsteps.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::Actions {\nnamespace detail {\ntemplate <typename DbTags, typename Metavariables, typename ArrayIndex,\n          typename ParallelComponent, typename EventsAndTriggers_t>\nvoid run_events_and_triggers(db::DataBox<DbTags>& box,\n                             Parallel::GlobalCache<Metavariables>& cache,\n                             const ArrayIndex& array_index,\n                             const ParallelComponent* const component,\n                             const EventsAndTriggers_t& events_and_triggers,\n                             const TimeStepId& time_step_id) {\n  if (time_step_id.substep() == 0) {\n    events_and_triggers.run_events(\n        make_not_null(&box), cache, array_index, component,\n        {db::tag_name<::Tags::Time>(), db::get<::Tags::Time>(box)});\n  } else {\n    const double substep_offset = 1.0e6;\n    const double observation_value =\n        time_step_id.step_time().value() +\n        substep_offset * static_cast<double>(time_step_id.substep());\n    events_and_triggers.run_events(\n        make_not_null(&box), cache, array_index, component,\n        {db::tag_name<::Tags::Time>(), observation_value},\n        [&box](const Trigger& trigger) {\n          const auto* substep_trigger =\n              dynamic_cast<const ::Triggers::OnSubsteps*>(&trigger);\n          return substep_trigger != nullptr and\n                 substep_trigger->is_triggered(box);\n        });\n  }\n}\n}  // namespace detail\n\n/// \\ingroup ActionsGroup\n/// \\ingroup EventsAndTriggersGroup\n/// \\brief Run the events and triggers\n///\n/// Triggers will only be checked on the first step of each slab to\n/// ensure that a consistent set of events is run across all elements.\n///\n/// Uses:\n/// - GlobalCache: the EventsAndTriggers tag, as required by\n///   events and triggers\n/// - DataBox: as required by events and triggers\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <Triggers::WhenToCheck WhenToCheck>\nstruct RunEventsAndTriggers {\n  using const_global_cache_tags =\n      tmpl::list<::Tags::EventsAndTriggers<WhenToCheck>>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const component) {\n    const auto time_step_id = db::get<::Tags::TimeStepId>(box);\n    if (SelfStart::is_self_starting(time_step_id)) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    if constexpr (WhenToCheck != Triggers::WhenToCheck::AtSteps) {\n      if (not time_step_id.step_time().is_at_slab_boundary()) {\n        return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n      }\n    }\n\n    const auto& events_and_triggers =\n        Parallel::get<::Tags::EventsAndTriggers<WhenToCheck>>(cache);\n    detail::run_events_and_triggers(box, cache, array_index, component,\n                                    events_and_triggers, time_step_id);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace evolution::Actions\n"
  },
  {
    "path": "src/Evolution/Ader/Ader.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\ingroup AderGroup\n * \\brief Namespace containing functions and classes for ADER\n */\nnamespace ader {\n/*!\n * \\brief Namespace containing functions and classes for ADER-DG\n */\nnamespace dg {}\n\n/*!\n * \\brief Namespace containing functions and classes for ADER-CG\n */\nnamespace cg {}\n\n/*!\n * \\brief Namespace containing functions and classes for ADER-CERK\n */\nnamespace cerk {}\n}  // namespace ader\n"
  },
  {
    "path": "src/Evolution/Ader/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Ader)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Matrices.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Ader.hpp\n  Matrices.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  ErrorHandling\n  Spectral\n  )\n"
  },
  {
    "path": "src/Evolution/Ader/Matrices.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Ader/Matrices.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace ader::dg {\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType,\n          typename SpectralQuantityGenerator>\nconst auto& precomputed_spectral_quantity(const size_t num_points) {\n  constexpr size_t max_num_points =\n      Spectral::maximum_number_of_points<BasisType>;\n  constexpr size_t min_num_points =\n      Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n  ASSERT(num_points >= min_num_points,\n         \"Tried to work with less than the minimum number of collocation \"\n         \"points for this quadrature.\");\n  ASSERT(num_points <= max_num_points,\n         \"Exceeded maximum number of collocation points.\");\n  // We compute the quantity for all possible `num_point`s the first time this\n  // function is called and keep the data around for the lifetime of the\n  // program. The computation is handled by the call operator of the\n  // `SpectralQuantityType` instance.\n  static const auto precomputed_data =\n      make_static_cache<CacheRange<min_num_points, max_num_points + 1>>(\n          SpectralQuantityGenerator{});\n  return precomputed_data(num_points);\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nstruct PredictorInverseTemporalMatrix;\n\ntemplate <Spectral::Basis BasisType>\nstruct PredictorInverseTemporalMatrix<BasisType,\n                                      Spectral::Quadrature::GaussLobatto> {\n  Matrix operator()(const size_t num_points) const {\n    const DataVector& collocation_pts = Spectral::collocation_points<\n        BasisType, Spectral::Quadrature::GaussLobatto>(num_points);\n    Matrix differences(num_points, num_points);\n    for (size_t i = 0; i < num_points; ++i) {\n      for (size_t j = 0; j < num_points; ++j) {\n        differences(i, j) = collocation_pts[i] - collocation_pts[j];\n      }\n    }\n\n    // computes d (ell_j(x))/dx at x = x_k\n    const auto lagrange_deriv = [num_points, &differences](const size_t j,\n                                                           const size_t k) {\n      double result = 0.0;\n      for (size_t i = 0; i < num_points; ++i) {\n        if (i == j) {\n          continue;\n        }\n        double temp = 1.0 / differences(j, i);\n        for (size_t m = 0; m < num_points; ++m) {\n          if (m == i or m == j) {\n            continue;\n          }\n          temp *= differences(k, m) / differences(j, m);\n        }\n        result += temp;\n      }\n      return result;\n    };\n\n    const DataVector& weights = Spectral::quadrature_weights<\n        BasisType, Spectral::Quadrature::GaussLobatto>(num_points);\n    Matrix result(num_points, num_points);\n    Matrix mass(num_points, num_points, 0.0);\n    for (size_t i = 0; i < num_points; ++i) {\n      mass(i, i) = weights[i];\n      for (size_t j = 0; j < num_points; ++j) {\n        result(i, j) = -weights[j] * lagrange_deriv(i, j);\n      }\n    }\n    // The 1.0 here assumes Gauss-Lobatto points since it hard codes ell_i(1)\n    result(num_points - 1, num_points - 1) += 1.0;\n    return inv(result) * mass;\n  }\n};\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nconst Matrix& predictor_inverse_temporal_matrix(const size_t num_points) {\n  return precomputed_spectral_quantity<\n      BasisType, QuadratureType,\n      PredictorInverseTemporalMatrix<BasisType, QuadratureType>>(num_points);\n}\n\ntemplate const Matrix& predictor_inverse_temporal_matrix<\n    Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>(\n    const size_t num_points);\n}  // namespace ader::dg\n"
  },
  {
    "path": "src/Evolution/Ader/Matrices.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n\n/// \\cond\nclass Matrix;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace ader::dg {\n/*!\n * \\brief Computes the matrix applied to the spacetime predictor volume\n * contributions to give the next initial guess in the iteration.\n *\n * Specifically, the returned matrix is:\n *\n * \\f{align}{\n * S_{a_0 b_0}=\\left(\\ell_{a_0}(1)\\ell_{c_0}(1) - w_{c_0}\n * \\left(\\partial_\\tau\\ell_{a_0}(\\tau)\\right)\\rvert_{c_0}\n * \\right)^{-1}M_{c_{0}b_{0}}\n * \\f}\n *\n * where \\f$M_{a_0 b_0}\\f$ is the temporal mass matrix.\n */\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nconst Matrix& predictor_inverse_temporal_matrix(size_t num_points);\n}  // namespace ader::dg\n"
  },
  {
    "path": "src/Evolution/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EvolutionBoundaryConditions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Type.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Type.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ErrorHandling\n  )\n"
  },
  {
    "path": "src/Evolution/BoundaryConditions/Type.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace evolution::BoundaryConditions {\nstd::ostream& operator<<(std::ostream& os, const Type boundary_condition_type) {\n  switch (boundary_condition_type) {\n    case Type::Ghost:\n      return os << \"Ghost\";\n    case Type::TimeDerivative:\n      return os << \"TimeDerivative\";\n    case Type::GhostAndTimeDerivative:\n      return os << \"GhostAndTimeDerivative\";\n    case Type::DemandOutgoingCharSpeeds:\n      return os << \"DemandOutgoingCharSpeeds\";\n    default:\n      ERROR(\n          \"Unknown enumeration value. Should be one of Ghost, TimeDerivative, \"\n          \"GhostAndTimeDerivative, or DemandOutgoingCharSpeeds.\");\n  }\n}\n}  // namespace evolution::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/BoundaryConditions/Type.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\nnamespace evolution::BoundaryConditions {\n/*!\n * \\brief The type of boundary condition.\n *\n * There are generally two categories or types of boundary conditions. Those\n * that:\n *\n * 1. set up additional cells or elements (called ghost cells or ghost elements)\n *    outside the computational domain and then apply boundary corrections the\n *    same way that is done in the the interior. For example, in a discontinuous\n *    Galerkin scheme these would impose boundary conditions through a numerical\n *    flux boundary correction term.\n *\n * 2. change the time derivatives at the external boundary\n */\nenum class Type {\n  /// Impose boundary conditions by setting values on ghost cells/elements.\n  ///\n  /// The ghost cell values can come either from internal data or from an\n  /// analytic prescription\n  Ghost,\n  /// Impose boundary conditions on the time derivative of the evolved\n  /// variables.\n  ///\n  /// These are imposed after all internal and external boundaries using a ghost\n  /// cell prescription have been handled.\n  TimeDerivative,\n  /// Impose ghost boundary conditions on some of the evolved variables and time\n  /// derivative boundary conditions on others.\n  GhostAndTimeDerivative,\n  /// Require all characteristics to be outgoing on the boundary.\n  ///\n  /// Typically this boundary condition should only check that all\n  /// characteristic speeds are out of the domain.\n  DemandOutgoingCharSpeeds\n};\n\nstd::ostream& operator<<(std::ostream& os, Type boundary_condition_type);\n}  // namespace evolution::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/BoundaryCorrection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace evolution {\n/*!\n * \\brief The base class used to create boundary corrections from input files\n * and store them in the global cache.\n */\nclass BoundaryCorrection : public PUP::able {\n public:\n  BoundaryCorrection() = default;\n  BoundaryCorrection(const BoundaryCorrection&) = default;\n  BoundaryCorrection& operator=(const BoundaryCorrection&) = default;\n  BoundaryCorrection(BoundaryCorrection&&) = default;\n  BoundaryCorrection& operator=(BoundaryCorrection&&) = default;\n  ~BoundaryCorrection() override = default;\n\n  explicit BoundaryCorrection(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  /// \\cond\n  WRAPPED_PUPable_abstract(BoundaryCorrection);  // NOLINT\n  /// \\endcond\n\n  virtual std::unique_ptr<BoundaryCorrection> get_clone() const = 0;\n};\n}  // namespace evolution\n"
  },
  {
    "path": "src/Evolution/BoundaryCorrectionTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"NumericalAlgorithms/SpatialDiscretization/OptionTags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution {\nnamespace OptionTags {\n/// The boundary correction used for coupling the local PDE system solution to\n/// solutions from neighboring elements or applying boundary conditions.\n///\n/// In the finite volume/difference and discontinuous Galerkin literature this\n/// is often referred to as the \"numerical flux\". We avoid that nomenclature\n/// because in the discontinuous Galerkin and finite volume case it is not the\n/// flux that is modified, but the integrand of the boundary integral.\nstruct BoundaryCorrection {\n  using type = std::unique_ptr<evolution::BoundaryCorrection>;\n  using group = SpatialDiscretization::OptionTags::SpatialDiscretizationGroup;\n  static constexpr Options::String help = \"The boundary correction to use.\";\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// The boundary correction used for coupling together neighboring cells or\n/// applying boundary conditions.\n///\n/// In the finite volume/difference and discontinuous Galerkin literature this\n/// is ofter referred to as the \"numerical flux\". We avoid that nomenclature\n/// because in the discontinuous Galerkin and finite volume case it is not the\n/// flux that is modified, but the integrand of the boundary integral.\nstruct BoundaryCorrection : db::SimpleTag {\n  using type = std::unique_ptr<evolution::BoundaryCorrection>;\n\n  using option_tags = tmpl::list<OptionTags::BoundaryCorrection>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& boundary_correction) {\n    return boundary_correction->get_clone();\n  }\n};\n}  // namespace Tags\n}  // namespace evolution\n"
  },
  {
    "path": "src/Evolution/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Evolution)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  TagsDomain.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCorrection.hpp\n  BoundaryCorrectionTags.hpp\n  ComputeTags.hpp\n  Evolution.hpp\n  NumericInitialData.hpp\n  PassVariables.hpp\n  Protocols.hpp\n  Tags.hpp\n  TagsDomain.hpp\n  TypeTraits.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainStructure\n  ErrorHandling\n  InitialDataUtilities\n  Options\n  Printf\n  Serialization\n  Spectral\n  Time\n  Utilities\n  PRIVATE\n  LinearOperators\n  INTERFACE\n  Observer\n  Parallel\n  ParallelArrayCollection\n  SystemUtilities\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_GlobalCache\n  )\n\nadd_subdirectory(Python)\n\nadd_subdirectory(Actions)\nadd_subdirectory(Ader)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(Conservative)\nadd_subdirectory(Deadlock)\nadd_subdirectory(DgSubcell)\nadd_subdirectory(DiscontinuousGalerkin)\nadd_subdirectory(Executables)\nadd_subdirectory(Imex)\nadd_subdirectory(Initialization)\nadd_subdirectory(Particles)\nadd_subdirectory(Ringdown)\nadd_subdirectory(Systems)\nadd_subdirectory(Tags)\nadd_subdirectory(Triggers)\nadd_subdirectory(VariableFixing)\n"
  },
  {
    "path": "src/Evolution/ComputeTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::Tags {\n/*!\n * \\brief Computes the analytic solution and adds `::Tags::Analytic` of the\n * `std::optional<Tensor>`s to the DataBox.\n *\n * \\note If `InitialDataList` is not an empty `tmpl::list`, then\n * `evolution::initial_data::Tags::InitialData` is retrieved and downcast to the\n * initial data for computing the errors.\n */\ntemplate <size_t Dim, typename AnalyticFieldsTagList, bool UsingDgSubcell,\n          typename InitialDataList = tmpl::list<>>\nstruct AnalyticSolutionsCompute\n    : ::Tags::AnalyticSolutions<AnalyticFieldsTagList>,\n      db::ComputeTag {\n  using field_tags = AnalyticFieldsTagList;\n  using base = ::Tags::AnalyticSolutions<AnalyticFieldsTagList>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData,\n                 tmpl::conditional_t<\n                     UsingDgSubcell,\n                     Events::Tags::ObserverCoordinates<Dim, Frame::Inertial>,\n                     domain::Tags::Coordinates<Dim, Frame::Inertial>>,\n                 ::Tags::Time>;\n\n  static void function(\n      const gsl::not_null<return_type*> analytic_solution,\n      const evolution::initial_data::InitialData& initial_data,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n      const double time) {\n    call_with_dynamic_type<void, InitialDataList>(\n        &initial_data, [&analytic_solution, &inertial_coords,\n                        time](const auto* const data_or_solution) {\n          function(analytic_solution, *data_or_solution, inertial_coords, time);\n        });\n  }\n\n private:\n  template <typename AnalyticSolution>\n  static void function(\n      const gsl::not_null<return_type*> analytic_solution,\n      const AnalyticSolution& analytic_solution_computer,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n      const double time) {\n    if constexpr (::is_analytic_solution_v<AnalyticSolution>) {\n      *analytic_solution =\n          variables_from_tagged_tuple(analytic_solution_computer.variables(\n              inertial_coords, time, AnalyticFieldsTagList{}));\n    } else {\n      (void)analytic_solution_computer;\n      (void)inertial_coords;\n      (void)time;\n      *analytic_solution = std::nullopt;\n    }\n  }\n};\n}  // namespace evolution::Tags\n"
  },
  {
    "path": "src/Evolution/Conservative/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  UpdateConservatives.hpp\n  UpdatePrimitives.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Conservative/UpdateConservatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\n/// \\endcond\n\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// \\brief Compute the conservative variables from the primitive variables\n///\n/// Uses:\n/// - DataBox: Items in system::conservative_from_primitive::argument_tags\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: Metavariables::system::conservative_from_primitive::return_tags\nstruct UpdateConservatives {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent,\n            Requires<tmpl::size<DbTagsList>::value != 0> = nullptr>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<\n        typename Metavariables::system::conservative_from_primitive>(\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n"
  },
  {
    "path": "src/Evolution/Conservative/UpdatePrimitives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\n/// \\endcond\n\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// \\brief Compute the primitive variables from the conservative variables\n///\n/// \\note `Metavariables` must specify an\n/// `ordered_list_of_primitive_recovery_schemes`.\n///\n/// Uses:\n/// - DataBox: Items in system::primitive_from_conservative::argument_tags\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: Metavariables::system::primitive_from_conservative::return_tags\nstruct UpdatePrimitives {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent,\n            Requires<tmpl::size<DbTagsList>::value != 0> = nullptr>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    using PrimFromCon =\n        typename Metavariables::system::template primitive_from_conservative<\n            typename Metavariables::ordered_list_of_primitive_recovery_schemes>;\n    db::mutate_apply<PrimFromCon>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n"
  },
  {
    "path": "src/Evolution/Deadlock/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Deadlock)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  PrintDgElementArray.hpp\n  PrintFunctionsOfTime.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  DiscontinuousGalerkin\n  FunctionsOfTime\n  Parallel\n  Printf\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "src/Evolution/Deadlock/PrintDgElementArray.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iomanip>\n#include <sstream>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/OutputInbox.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n\n/*!\n * \\brief Namespace for actions related to debugging deadlocks in communication.\n *\n * \\details These actions will typically be run in the\n * `run_deadlock_analysis_simple_actions` function in the metavariables (if it\n * exists).\n */\nnamespace deadlock {\n/*!\n * \\brief Simple action that will print a variety of information on evolution DG\n * elements\n *\n * \\details This will print the contents of the following inbox or DataBox tags:\n *\n * - `evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<3>`\n * - `evolution::dg::Tags::MortarNextTemporalId<3>`\n * - `evolution::dg::Tags::MortarDataHistory` (for LTS only)\n * - `evolution::dg::Tags::MortarData<3>` (for GTS only)\n *\n * Inbox tags are printed using the `Parallel::output_inbox` function. The\n * DataBox tags are printed with nice indenting for easy readability in the\n * stdout file.\n *\n * This can be generalized in the future to other dimensions if needed.\n */\nstruct PrintElementInfo {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index,\n                    const std::string& file_name) {\n    if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {\n      auto& element =\n          Parallel::local_synchronous_action<\n              Parallel::Actions::GetItemFromDistributedOject<\n                  typename ParallelComponent::element_collection_tag>>(\n              Parallel::get_parallel_component<ParallelComponent>(cache))\n              ->at(array_index);\n      apply<ParallelComponent>(box, cache, array_index, &element, file_name);\n    } else {\n      apply<ParallelComponent>(\n          box, cache, array_index,\n          Parallel::local(Parallel::get_parallel_component<ParallelComponent>(\n              cache)[array_index]), file_name);\n    }\n  }\n\n private:\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex, typename T>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& array_index, T* local_object_ptr,\n                    const std::string& file_name) {\n    auto& local_object = *local_object_ptr;\n\n    const bool terminated = local_object.get_terminate();\n\n    std::stringstream ss{};\n    ss << std::scientific << std::setprecision(16);\n    ss << \"Element \" << array_index\n       << (terminated ? \" terminated\" : \" did NOT terminate\") << \" at time \"\n       << db::get<::Tags::Time>(box) << \".\";\n\n    // Only print stuff if this element didn't terminate properly\n    if (not terminated) {\n      const std::string& next_action =\n          local_object.deadlock_analysis_next_iterable_action();\n      ss << \" Next action: \" << next_action << \"\\n\";\n\n      // Start with time step and next time\n      const auto& step = db::get<::Tags::TimeStep>(box);\n      // The time step only prints a slab (beginning/end) and a fraction so we\n      // also print the approx numerical value of the step for easier reading\n      ss << \" Time step: \" << step << \":\" << step.value() << \"\\n\";\n      ss << \" Next time: \"\n         << db::get<::Tags::Next<::Tags::TimeStepId>>(box).substep_time()\n         << \"\\n\";\n\n      ss << \" Inboxes:\\n\";\n\n      const auto& inboxes = local_object.get_inboxes();\n\n      const std::string mortar_inbox = Parallel::output_inbox<\n          evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n              3, Parallel::is_dg_element_collection_v<ParallelComponent>>>(\n          inboxes, 2_st);\n      ss << mortar_inbox;\n\n      ss << \" Mortars:\\n\";\n\n      const auto& mortar_next_temporal_id =\n          db::get<evolution::dg::Tags::MortarNextTemporalId<3>>(box);\n\n      ss << \"  MortarNextTemporalId\\n\";\n      for (const auto& [key, next_id] : mortar_next_temporal_id) {\n        ss << \"    Key: \" << key << \", next time: \" << next_id.substep_time()\n           << \"\\n\";\n      }\n\n      if constexpr (Metavariables::local_time_stepping) {\n        const auto& mortar_data_history =\n            db::get<evolution::dg::Tags::MortarDataHistory<3>>(box);\n        ss << \"  MortarDataHistory:\\n\";\n\n        for (const auto& [key, history] : mortar_data_history) {\n          ss << \"   Key: \" << key << \", history:\\n\";\n          history.template print<false>(ss, 4_st);\n        }\n      }\n    } else {\n      ss << \"\\n\";\n    }\n\n    Parallel::fprintf(file_name, \"%s\", ss.str());\n  }\n};\n}  // namespace deadlock\n"
  },
  {
    "path": "src/Evolution/Deadlock/PrintFunctionsOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/FunctionsOfTime/OutputTimeBounds.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n\nnamespace control_system::Tags {\nstruct MeasurementTimescales;\n}  // namespace control_system::Tags\n\nnamespace deadlock {\n/*!\n * \\brief Simple action that will print the `domain::Tags::FunctionsOfTime` and\n * `control_system::Tags::MeasurementTimescales` (if it exists) time bounds for\n * each node of a simulation.\n */\nstruct PrintFunctionsOfTime {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const std::string& file_name) {\n    const auto& functions_of_time =\n        Parallel::get<::domain::Tags::FunctionsOfTime>(cache);\n    const std::string time_bounds =\n        ::domain::FunctionsOfTime::output_time_bounds(functions_of_time);\n\n    if constexpr (Parallel::is_in_global_cache<\n                      Metavariables,\n                      control_system::Tags::MeasurementTimescales>) {\n      const auto& measurement_timescales =\n          Parallel::get<control_system::Tags::MeasurementTimescales>(cache);\n      const std::string measurement_time_bounds =\n          ::domain::FunctionsOfTime::output_time_bounds(measurement_timescales);\n\n      Parallel::fprintf(\n          file_name,\n          \"Node %zu\\nFunctionsOfTime:\\n%s\\n\\nMeasurementTimescales:\\n%s\\n\",\n          Parallel::my_node<size_t>(cache), time_bounds,\n          measurement_time_bounds);\n    } else {\n      Parallel::fprintf(file_name, \"Node %zu\\nFunctionsOfTime:%s\\n\",\n                        Parallel::my_node<size_t>(cache), time_bounds);\n    }\n  }\n};\n}  // namespace deadlock\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/Actions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace evolution::dg::subcell {\n/// \\ingroup DgSubcellGroup\n/// \\brief %Actions for the DG-subcell hybrid solver.\nnamespace Actions {}\nnamespace fd {\n/// \\ingroup DgSubcellGroup\n/// \\brief %Actions specific to using a finite-difference subcell method.\nnamespace Actions {}\n}  // namespace fd\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DgSubcell)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Actions.hpp\n  Initialize.hpp\n  Labels.hpp\n  ReconstructionCommunication.hpp\n  SelectNumericalMethod.hpp\n  TakeTimeStep.hpp\n  TciAndRollback.hpp\n  TciAndSwitchToDg.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostZoneInverseJacobian.hpp\"\n#include \"Evolution/DgSubcell/Tags/InitialTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/MeshForGhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ReconstructionOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/StepsSinceTciCall.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciCallsSinceRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciGridHistory.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::dg::subcell::Actions {\n/*!\n * \\brief Initialize the subcell grid, including the size of the evolved\n * `Variables` and, if present, primitive `Variables`.\n *\n * By default sets the element to `subcell::ActiveGrid::Subcell` unless it\n * is not allowed to use subcell either because it is at an external boundary\n * or because it or one of its neighbors has been marked as DG-only.\n *\n * GlobalCache:\n * - Uses:\n *   - `subcell::Tags::SubcellOptions`\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::Mesh<Dim>`\n *   - `domain::Tags::Element<Dim>`\n *   - `System::variables_tag`\n * - Adds:\n *   - `subcell::Tags::Mesh<Dim>`\n *   - `subcell::Tags::MeshForGhostData<Dim>`\n *   - `subcell::Tags::ActiveGrid`\n *   - `subcell::Tags::DidRollback`\n *   - `subcell::Tags::TciGridHistory`\n *   - `subcell::Tags::TciCallsSinceRollback`\n *   - `subcell::Tags::GhostDataForReconstruction<Dim>`\n *   - `subcell::Tags::GhostZoneInverseJacobian<Dim>`\n *   - `subcell::Tags::TciDecision`\n *   - `subcell::Tags::DataForRdmpTci`\n *   - `subcell::fd::Tags::InverseJacobianLogicalToGrid<Dim>`\n *   - `subcell::fd::Tags::DetInverseJacobianLogicalToGrid`\n *   - `subcell::Tags::LogicalCoordinates<Dim>`\n *   - `subcell::Tags::ReconstructionOrder<Dim>` (set as `std::nullopt`)\n *   - `subcell::Tags::Coordinates<Dim, Frame::Grid>` (as compute tag)\n *   - `subcell::Tags::Coordinates<Dim, Frame::Inertial>` (as compute tag)\n * - Removes: nothing\n * - Modifies:\n *   - `System::variables_tag` and `System::primitive_variables_tag` if the cell\n *     is troubled\n *   - `Tags::dt<System::variables_tag>` if the cell is troubled\n */\ntemplate <size_t Dim, typename System, bool UseNumericInitialData>\nstruct SetSubcellGrid {\n  using const_global_cache_tags = tmpl::list<Tags::SubcellOptions<Dim>>;\n\n  using simple_tags = tmpl::list<\n      Tags::ActiveGrid, Tags::DidRollback, Tags::TciGridHistory,\n      Tags::TciCallsSinceRollback, Tags::StepsSinceTciCall,\n      evolution::dg::subcell::Tags::MeshForGhostData<Dim>,\n      Tags::GhostDataForReconstruction<Dim>,\n      Tags::GhostZoneInverseJacobian<Dim>, Tags::TciDecision,\n      Tags::NeighborTciDecisions<Dim>, Tags::DataForRdmpTci,\n      subcell::Tags::CellCenteredFlux<typename System::flux_variables, Dim>,\n      subcell::Tags::ReconstructionOrder<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromDgToNeighborFd<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>,\n      typename System::variables_tag,\n      evolution::dg::subcell::Tags::ExtensionDirections<Dim>>;\n  using compute_tags =\n      tmpl::list<Tags::MeshCompute<Dim>, Tags::LogicalCoordinatesCompute<Dim>,\n                 ::domain::Tags::MappedCoordinates<\n                     ::domain::Tags::ElementMap<Dim, Frame::Grid>,\n                     subcell::Tags::Coordinates<Dim, Frame::ElementLogical>,\n                     subcell::Tags::Coordinates>,\n                 Tags::InertialCoordinatesCompute<\n                     ::domain::CoordinateMaps::Tags::CoordinateMap<\n                         Dim, Frame::Grid, Frame::Inertial>>,\n                 fd::Tags::InverseJacobianLogicalToGridCompute<\n                     ::domain::Tags::ElementMap<Dim, Frame::Grid>, Dim>,\n                 fd::Tags::DetInverseJacobianLogicalToGridCompute<Dim>,\n                 fd::Tags::InverseJacobianLogicalToInertialCompute<\n                     ::domain::CoordinateMaps::Tags::CoordinateMap<\n                         Dim, Frame::Grid, Frame::Inertial>,\n                     Dim>,\n                 fd::Tags::DetInverseJacobianLogicalToInertialCompute<\n                     ::domain::CoordinateMaps::Tags::CoordinateMap<\n                         Dim, Frame::Grid, Frame::Inertial>,\n                     Dim>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      [[maybe_unused]] const tuples::TaggedTuple<InboxTags...>& inboxes,\n      [[maybe_unused]] const Parallel::GlobalCache<Metavariables>& cache,\n      [[maybe_unused]] const ArrayIndex& array_index, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const SubcellOptions& subcell_options =\n        db::get<Tags::SubcellOptions<Dim>>(box);\n    const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);\n    const Mesh<Dim>& subcell_mesh = db::get<subcell::Tags::Mesh<Dim>>(box);\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n\n    for (size_t d = 0; d < Dim; ++d) {\n      if (subcell_options.persson_num_highest_modes() >= dg_mesh.extents(d)) {\n        ERROR(\"Number of the highest modes to be monitored by the Persson TCI (\"\n              << subcell_options.persson_num_highest_modes()\n              << \") must be smaller than the extent of the DG mesh (\"\n              << dg_mesh.extents(d) << \").\");\n      }\n    }\n\n    // Loop over block neighbors and if neighbor id is inside of\n    // subcell_options.only_dg_block_ids(), then bordering DG-only block\n    const bool bordering_dg_block = alg::any_of(\n        element.neighbors(),\n        [&subcell_options](const auto& direction_and_neighbor) {\n          const size_t first_block_id =\n              direction_and_neighbor.second.ids().begin()->block_id();\n          return alg::found(subcell_options.only_dg_block_ids(),\n                            first_block_id);\n        });\n\n    const bool subcell_allowed_in_element =\n        not alg::found(subcell_options.only_dg_block_ids(),\n                       element.id().block_id()) and\n        not bordering_dg_block;\n    const bool cell_is_not_on_external_boundary =\n        db::get<::domain::Tags::Element<Dim>>(box)\n            .external_boundaries()\n            .empty();\n\n    constexpr bool subcell_enabled_at_external_boundary =\n        Metavariables::SubcellOptions::subcell_enabled_at_external_boundary;\n\n    db::mutate<Tags::NeighborTciDecisions<Dim>>(\n        [&element](const auto neighbor_decisions_ptr) {\n          neighbor_decisions_ptr->clear();\n          for (const auto& [direction, neighbors_in_direction] :\n               element.neighbors()) {\n            for (const auto& neighbor : neighbors_in_direction.ids()) {\n              neighbor_decisions_ptr->insert(\n                  std::pair{DirectionalId<Dim>{direction, neighbor}, 0});\n            }\n          }\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<\n        tmpl::list<Tags::ActiveGrid, Tags::DidRollback,\n                   typename System::variables_tag, subcell::Tags::TciDecision,\n                   subcell::Tags::TciCallsSinceRollback,\n                   subcell::Tags::StepsSinceTciCall>,\n        tmpl::list<>>(\n        [&cell_is_not_on_external_boundary, &dg_mesh,\n         subcell_allowed_in_element, &subcell_mesh](\n            const gsl::not_null<ActiveGrid*> active_grid_ptr,\n            const gsl::not_null<bool*> did_rollback_ptr,\n            const auto active_vars_ptr,\n            const gsl::not_null<int*> tci_decision_ptr,\n            const gsl::not_null<size_t*> tci_calls_since_rollback_ptr,\n            const gsl::not_null<size_t*> steps_since_tci_call_ptr) {\n          // We don't consider setting the initial grid to subcell as rolling\n          // back. Since no time step is undone, we just continue on the\n          // subcells as a normal solve.\n          *did_rollback_ptr = false;\n\n          if ((cell_is_not_on_external_boundary or\n               subcell_enabled_at_external_boundary) and\n              subcell_allowed_in_element) {\n            *active_grid_ptr = ActiveGrid::Subcell;\n            active_vars_ptr->initialize(subcell_mesh.number_of_grid_points());\n          } else {\n            *active_grid_ptr = ActiveGrid::Dg;\n            active_vars_ptr->initialize(dg_mesh.number_of_grid_points());\n          }\n\n          *tci_decision_ptr = 0;\n          *tci_calls_since_rollback_ptr = 0;\n          *steps_since_tci_call_ptr = 0;\n        },\n        make_not_null(&box));\n    if constexpr (System::has_primitive_and_conservative_vars) {\n      db::mutate<typename System::primitive_variables_tag>(\n          [&dg_mesh, &subcell_mesh](const auto prim_vars_ptr,\n                                    const auto active_grid) {\n            if (active_grid == ActiveGrid::Dg) {\n              prim_vars_ptr->initialize(dg_mesh.number_of_grid_points());\n            } else {\n              prim_vars_ptr->initialize(subcell_mesh.number_of_grid_points());\n            }\n          },\n          make_not_null(&box), db::get<Tags::ActiveGrid>(box));\n    }\n    if constexpr (not UseNumericInitialData) {\n      if (db::get<Tags::ActiveGrid>(box) ==\n          evolution::dg::subcell::ActiveGrid::Dg) {\n        evolution::Initialization::Actions::SetVariables<\n            ::domain::Tags::Coordinates<Dim, Frame::ElementLogical>>::\n            apply(box, inboxes, cache, array_index, ActionList{},\n                  std::add_pointer_t<ParallelComponent>{nullptr});\n      } else {\n        evolution::Initialization::Actions::\n            SetVariables<Tags::Coordinates<Dim, Frame::ElementLogical>>::apply(\n                box, inboxes, cache, array_index, ActionList{},\n                std::add_pointer_t<ParallelComponent>{nullptr});\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Sets the RDMP data from the initial data and sends it to neighboring\n * elements.\n *\n * GlobalCache:\n * - Uses:\n *   - `ParallelComponent` proxy\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::Element<Dim>`\n *   - `subcell::Tags::DataForRdmpTci`\n *   - `subcell::Tags::InitialTciData`\n *   - whatever `SetInitialRdmpData` uses\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - whatever `SetInitialRdmpData` mutates\n */\ntemplate <size_t Dim, typename SetInitialRdmpData>\nstruct SetAndCommunicateInitialRdmpData {\n  using inbox_tags =\n      tmpl::list<evolution::dg::subcell::Tags::InitialTciData<Dim>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Get the RDMP data on this element and then initialize it.\n    db::mutate_apply<SetInitialRdmpData>(make_not_null(&box));\n\n    // Send RDMP data to neighbors\n    const auto& element = db::get<domain::Tags::Element<Dim>>(box);\n    const auto& rdmp_data =\n        db::get<evolution::dg::subcell::Tags::DataForRdmpTci>(box);\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      for (const auto& neighbor : neighbors) {\n        const auto& orientation = neighbors.orientation(neighbor);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        evolution::dg::subcell::InitialTciData data{{}, rdmp_data};\n        // We use temporal ID 0 for sending RDMP data\n        const int temporal_id = 0;\n        Parallel::receive_data<\n            evolution::dg::subcell::Tags::InitialTciData<Dim>>(\n            receiver_proxy[neighbor], temporal_id,\n            std::make_pair(\n                DirectionalId<Dim>{direction_from_neighbor, element.id()},\n                std::move(data)));\n      }\n    }\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Apply the TCI on the FD grid to the initial data and send the TCI\n * decision to neighboring elements.\n *\n * GlobalCache:\n * - Uses:\n *   - `ParallelComponent` proxy\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::Element<Dim>`\n *   - `subcell::Tags::DataForRdmpTci`\n *   - `subcell::Tags::InitialTciData`\n *   - `subcell::Tags::SubcellOptions`\n *   - `subcell::Tags::ActiveGrid`\n *   - whatever `TciOnFdGridMutator` uses\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `subcell::Tags::DataForRdmpTci`\n *   - `subcell::Tags::TciDecision`\n */\ntemplate <size_t Dim, typename System, typename TciOnFdGridMutator>\nstruct ComputeAndSendTciOnInitialGrid {\n  using inbox_tags =\n      tmpl::list<evolution::dg::subcell::Tags::InitialTciData<Dim>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n\n    // Check if we have received all RDMP data.\n    if (LIKELY(element.number_of_neighbors() != 0)) {\n      auto& inbox =\n          tuples::get<evolution::dg::subcell::Tags::InitialTciData<Dim>>(\n              inboxes);\n      const auto& received = inbox.find(0);\n      if (received == inbox.end() or\n          received->second.size() != element.number_of_neighbors()) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n\n      db::mutate<evolution::dg::subcell::Tags::DataForRdmpTci>(\n          [&element, &received](const auto rdmp_tci_data_ptr) {\n            (void)element;\n            const size_t number_of_rdmp_vars =\n                rdmp_tci_data_ptr->max_variables_values.size();\n            ASSERT(rdmp_tci_data_ptr->max_variables_values.size() ==\n                       number_of_rdmp_vars,\n                   \"The number of local max vars is \"\n                       << number_of_rdmp_vars\n                       << \" while the number of local min vars is \"\n                       << rdmp_tci_data_ptr->max_variables_values.size()\n                       << \" the local element ID is \" << element.id());\n            for (const auto& [direction_and_neighbor_element_id,\n                              neighbor_initial_tci_data] : received->second) {\n              ASSERT(neighbor_initial_tci_data.initial_rdmp_data.has_value(),\n                     \"Neighbor in direction \"\n                         << direction_and_neighbor_element_id.direction()\n                         << \" with element ID \"\n                         << direction_and_neighbor_element_id.id() << \" of \"\n                         << element.id()\n                         << \" didn't send initial TCI data correctly\");\n              ASSERT(\n                  neighbor_initial_tci_data.initial_rdmp_data.value()\n                          .max_variables_values.size() == number_of_rdmp_vars,\n                  \"The number of local RDMP vars is \"\n                      << number_of_rdmp_vars\n                      << \" while the number of remote max vars is \"\n                      << neighbor_initial_tci_data.initial_rdmp_data.value()\n                             .max_variables_values.size()\n                      << \" the local element ID is \" << element.id()\n                      << \" and the remote id is \"\n                      << direction_and_neighbor_element_id.id());\n              ASSERT(\n                  neighbor_initial_tci_data.initial_rdmp_data.value()\n                          .min_variables_values.size() == number_of_rdmp_vars,\n                  \"The number of local RDMP vars is \"\n                      << number_of_rdmp_vars\n                      << \" while the number of remote min vars is \"\n                      << neighbor_initial_tci_data.initial_rdmp_data.value()\n                             .min_variables_values.size()\n                      << \" the local element ID is \" << element.id()\n                      << \" and the remote id is \"\n                      << direction_and_neighbor_element_id.id());\n              for (size_t var_index = 0; var_index < number_of_rdmp_vars;\n                   ++var_index) {\n                rdmp_tci_data_ptr->max_variables_values[var_index] =\n                    std::max(rdmp_tci_data_ptr->max_variables_values[var_index],\n                             neighbor_initial_tci_data.initial_rdmp_data.value()\n                                 .max_variables_values[var_index]);\n                rdmp_tci_data_ptr->min_variables_values[var_index] =\n                    std::min(rdmp_tci_data_ptr->min_variables_values[var_index],\n                             neighbor_initial_tci_data.initial_rdmp_data.value()\n                                 .min_variables_values[var_index]);\n              }\n            }\n          },\n          make_not_null(&box));\n      inbox.erase(received);\n    }\n\n    const auto send_tci_decision = [&cache, &element](const int tci_decision) {\n      if (UNLIKELY(element.number_of_neighbors() == 0)) {\n        return;\n      }\n      auto& receiver_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      for (const auto& [direction, neighbors] : element.neighbors()) {\n        for (const auto& neighbor : neighbors) {\n          const auto& orientation = neighbors.orientation(neighbor);\n          const auto direction_from_neighbor =\n              orientation(direction.opposite());\n          evolution::dg::subcell::InitialTciData data{tci_decision, {}};\n          // We use temporal ID 1 for ending the TCI decision.\n          const int temporal_id = 1;\n          Parallel::receive_data<\n              evolution::dg::subcell::Tags::InitialTciData<Dim>>(\n              receiver_proxy[neighbor], temporal_id,\n              std::make_pair(\n                  DirectionalId<Dim>{direction_from_neighbor, element.id()},\n                  std::move(data)));\n        }\n      }\n    };\n\n    const SubcellOptions& subcell_options =\n        db::get<Tags::SubcellOptions<Dim>>(box);\n\n    if (subcell_options.always_use_subcells() or\n        get<Tags::ActiveGrid>(box) == ActiveGrid::Dg) {\n      db::mutate<Tags::TciDecision>(\n          [](const gsl::not_null<int*> tci_decision_ptr) {\n            *tci_decision_ptr = 0;\n          },\n          make_not_null(&box));\n      send_tci_decision(0);\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    // Now run the TCI to see if we could switch back to DG.\n    const std::tuple<int, evolution::dg::subcell::RdmpTciData> tci_result =\n        db::mutate_apply<TciOnFdGridMutator>(\n            make_not_null(&box), subcell_options.persson_exponent() + 1.0,\n            false);\n\n    db::mutate<Tags::TciDecision>(\n        [&tci_result](const gsl::not_null<int*> tci_decision_ptr) {\n          *tci_decision_ptr = std::get<0>(tci_result);\n        },\n        make_not_null(&box));\n    send_tci_decision(std::get<0>(tci_result));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Using the local and neighboring TCI decisions, switches the element to\n * DG if the DG solution was determined to be admissible.\n *\n * GlobalCache:\n * - Uses:\n *   - `ParallelComponent` proxy\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::Element<Dim>`\n *   - `subcell::Tags::DataForRdmpTci`\n *   - `subcell::Tags::InitialTciData`\n *   - `subcell::Tags::SubcellOptions`\n *   - `subcell::Tags::ActiveGrid`\n *   - whatever `TciOnFdGridMutator` uses\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `subcell::Tags::NeighborTciDecisions`\n *   - `System::variables_tag`\n *   - `Tags::HistoryEvolvedVariables<System::variables_tag>`\n *   - `subcell::Tags::GhostDataForReconstruction`\n *   - `subcell::Tags::TciGridHistory`\n *   - `subcell::Tags::CellCenteredFlux`\n */\ntemplate <size_t Dim, typename System>\nstruct SetInitialGridFromTciData {\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n    if (LIKELY(element.number_of_neighbors() != 0)) {\n      auto& inbox =\n          tuples::get<evolution::dg::subcell::Tags::InitialTciData<Dim>>(\n              inboxes);\n      const auto& received = inbox.find(1);\n      // Check if we have received all TCI decisions.\n      if (received == inbox.end() or\n          received->second.size() != element.number_of_neighbors()) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n\n      db::mutate<evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n          [&element, &received](const auto neighbor_tci_decisions_ptr) {\n            for (const auto& [directional_element_id,\n                              neighbor_initial_tci_data] : received->second) {\n              (void)element;\n              ASSERT(neighbor_initial_tci_data.tci_status.has_value(),\n                     \"Neighbor in direction \"\n                         << directional_element_id.direction()\n                         << \" with element ID \" << directional_element_id.id()\n                         << \" of \" << element.id()\n                         << \" didn't send initial TCI decision correctly\");\n              neighbor_tci_decisions_ptr->at(directional_element_id) =\n                  neighbor_initial_tci_data.tci_status.value();\n            }\n          },\n          make_not_null(&box));\n      inbox.erase(received);\n    }\n\n    if (get<Tags::ActiveGrid>(box) == ActiveGrid::Dg) {\n      // In this case we are allowed to only do DG in this element. No need to\n      // even do any checks.\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const SubcellOptions& subcell_options =\n        db::get<Tags::SubcellOptions<Dim>>(box);\n\n    bool cell_is_troubled =\n        subcell_options.always_use_subcells() or\n        (subcell_options.use_halo() and [&box]() -> bool {\n          for (const auto& [_, neighbor_decision] :\n               db::get<evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n                   box)) {\n            if (neighbor_decision != 0) {\n              return true;\n            }\n          }\n          return false;\n        }()) or\n        (db::get<Tags::TciDecision>(box) != 0);\n\n    if (not cell_is_troubled) {\n      using variables_tag = typename System::variables_tag;\n      using flux_variables = typename System::flux_variables;\n\n      const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);\n      const Mesh<Dim>& subcell_mesh = db::get<subcell::Tags::Mesh<Dim>>(box);\n      db::mutate<\n          variables_tag, ::Tags::HistoryEvolvedVariables<variables_tag>,\n          Tags::ActiveGrid, subcell::Tags::GhostDataForReconstruction<Dim>,\n          evolution::dg::subcell::Tags::TciGridHistory,\n          evolution::dg::subcell::Tags::CellCenteredFlux<flux_variables, Dim>>(\n          [&dg_mesh, &subcell_mesh, &subcell_options](\n              const auto active_vars_ptr, const auto active_history_ptr,\n              const gsl::not_null<ActiveGrid*> active_grid_ptr,\n              const auto subcell_ghost_data_ptr,\n              const gsl::not_null<\n                  std::deque<evolution::dg::subcell::ActiveGrid>*>\n                  tci_grid_history_ptr,\n              const auto subcell_cell_centered_fluxes) {\n            // Note: strictly speaking, to be conservative this should\n            // reconstruct uJ instead of u.\n            *active_vars_ptr = fd::reconstruct(\n                *active_vars_ptr, dg_mesh, subcell_mesh.extents(),\n                subcell_options.reconstruction_method());\n\n            // Reconstruct the DG solution for each time in the time stepper\n            // history\n            active_history_ptr->map_entries(\n                [&dg_mesh, &subcell_mesh, &subcell_options](const auto entry) {\n                  *entry =\n                      fd::reconstruct(*entry, dg_mesh, subcell_mesh.extents(),\n                                      subcell_options.reconstruction_method());\n                });\n            *active_grid_ptr = ActiveGrid::Dg;\n\n            // Clear the neighbor data needed for subcell reconstruction since\n            // we have now completed the time step.\n            subcell_ghost_data_ptr->clear();\n\n            // Clear the TCI grid history since we don't need to use it when on\n            // the DG grid.\n            tci_grid_history_ptr->clear();\n\n            // Clear the allocation for the cell-centered fluxes.\n            *subcell_cell_centered_fluxes = std::nullopt;\n          },\n          make_not_null(&box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace evolution::dg::subcell::Actions\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/Labels.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace evolution::dg::subcell::Actions {\n/// Labels used to navigate the action list when using a DG-subcell scheme\nnamespace Labels {\n/// Label marking the start of the unlimited DG solver\nstruct BeginDg {};\n/// Label marking the end of the `step_actions`, i.e. the end of both the\n/// unlimited DG solver and the subcell solver.\nstruct EndOfSolvers {};\n/// Label marking the start of the subcell solver\nstruct BeginSubcell {};\n/// Label marking the part of the subcell solver that the unlimited DG solver\n/// jumps to after rolling back the unlimited DG step because it was\n/// inadmissible.\nstruct BeginSubcellAfterDgRollback {};\n}  // namespace Labels\n}  // namespace evolution::dg::subcell::Actions\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/ReconstructionCommunication.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <map>\n#include <optional>\n#include <tuple>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Structure/TrimMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/CombineVolumeGhostData.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/NeighborRdmpAndVolumeData.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/MeshForGhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\nnamespace evolution::dg::Tags {\ntemplate <size_t Dim>\nstruct MortarInfo;\n}  // namespace evolution::dg::Tags\n/// \\endcond\n\nnamespace evolution::dg::subcell::Actions {\n/*!\n * \\brief Sets the local data from the relaxed discrete maximum principle\n * troubled-cell indicator and sends ghost zone data to neighboring elements.\n *\n * The action proceeds as follows:\n *\n * 1. Determine in which directions we have neighbors\n * 2. Slice the variables provided by GhostDataMutator to send to our neighbors\n *    for ghost zones\n * 3. Send the ghost zone data, appending the max/min for the TCI at the end of\n *    the `DataVector` we are sending.\n *\n * \\warning This assumes the RDMP TCI data in the DataBox has been set, it does\n * not calculate it automatically. The reason is this way we can only calculate\n * the RDMP data when it's needed since computing it can be pretty expensive.\n *\n * Some notes:\n * - In the future we will need to send the cell-centered fluxes to do\n *   high-order FD without additional reconstruction being necessary.\n *\n * GlobalCache:\n * - Uses:\n *   - `ParallelComponent` proxy\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::Mesh<Dim>`\n *   - `subcell::Tags::Mesh<Dim>`\n *   - `domain::Tags::Element<Dim>`\n *   - `Tags::TimeStepId`\n *   - `Tags::Next<Tags::TimeStepId>`\n *   - `subcell::Tags::ActiveGrid`\n *   - `System::variables_tag`\n *   - `subcell::Tags::DataForRdmpTci`\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `subcell::Tags::GhostDataForReconstruction<Dim>`\n */\ntemplate <size_t Dim, typename GhostDataMutator, bool UseNodegroupDgElements>\nstruct SendDataForReconstruction {\n  using inbox_tags =\n      tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n          Dim, UseNodegroupDgElements>>;\n\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(UseNodegroupDgElements ==\n                      Parallel::is_dg_element_collection_v<ParallelComponent>,\n                  \"The action SendDataForReconstruction is told by the \"\n                  \"template parameter UseNodegroupDgElements that it is being \"\n                  \"used with a DgElementCollection, but the ParallelComponent \"\n                  \"is not a DgElementCollection. You need to change the \"\n                  \"template parameter on the SendDataForReconstruction action \"\n                  \"in your action list.\");\n\n    ASSERT(db::get<Tags::ActiveGrid>(box) == ActiveGrid::Subcell,\n           \"The SendDataForReconstruction action can only be called when \"\n           \"Subcell is the active scheme.\");\n\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n    ASSERT(alg::all_of(db::get<evolution::dg::Tags::MortarInfo<Dim>>(box),\n                       [](const auto& mortar) {\n                         return mortar.second.time_stepping_policy() ==\n                                TimeSteppingPolicy::EqualRate;\n                       }),\n           \"Cannot send subcell data from \"\n               << element.id() << \" across an LTS mortar: \"\n               << db::get<evolution::dg::Tags::MortarInfo<Dim>>(box));\n\n    using flux_variables = typename Metavariables::system::flux_variables;\n\n    db::mutate<Tags::GhostDataForReconstruction<Dim>>(\n        [](const auto ghost_data_ptr) {\n          // Clear the previous neighbor data and add current local data\n          ghost_data_ptr->clear();\n        },\n        make_not_null(&box));\n\n    const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);\n    const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);\n    const size_t ghost_zone_size =\n        Metavariables::SubcellOptions::ghost_zone_size(box);\n\n    // Optimization note: could save a copy+allocation if we moved\n    // all_sliced_data when possible before sending.\n    //\n    // Note: RDMP size doesn't help here since we need to slice data after\n    // anyway, so no way to save an allocation through that.\n    const auto& cell_centered_flux =\n        db::get<Tags::CellCenteredFlux<flux_variables, Dim>>(box);\n    DataVector volume_data_to_slice = db::mutate_apply(\n        GhostDataMutator{}, make_not_null(&box),\n        cell_centered_flux.has_value() ? cell_centered_flux.value().size()\n                                       : 0_st);\n    if (cell_centered_flux.has_value()) {\n      std::copy(\n          cell_centered_flux.value().data(),\n          std::next(\n              cell_centered_flux.value().data(),\n              static_cast<std::ptrdiff_t>(cell_centered_flux.value().size())),\n          std::next(\n              volume_data_to_slice.data(),\n              static_cast<std::ptrdiff_t>(volume_data_to_slice.size() -\n                                          cell_centered_flux.value().size())));\n    }\n\n    // When using enable_extension_directions, we send the ghost data\n    // for problematic directions separately with the new action\n    // ReceiveAndSendDataForReconstruction, so we only slice the data\n    // for non-problematic directions here. (see the documentation of\n    // ReceiveAndSendDataForReconstruction for what \"problematic\" means)\n    const auto& extension_directions =\n        db::get<evolution::dg::subcell::Tags::ExtensionDirections<Dim>>(box);\n    std::unordered_set<Direction<Dim>> directions_to_work;\n    for (const auto& internal_direction : element.internal_boundaries()) {\n      if (extension_directions.contains(internal_direction)) {\n        continue;\n      } else {\n        directions_to_work.insert(internal_direction);\n      }\n    }\n\n    const DirectionMap<Dim, DataVector> all_sliced_data = slice_data(\n        volume_data_to_slice, subcell_mesh.extents(), ghost_zone_size,\n        directions_to_work, 0,\n        db::get<\n            evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>(\n            box));\n\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    const RdmpTciData& rdmp_tci_data = db::get<Tags::DataForRdmpTci>(box);\n    const TimeStepId& time_step_id = db::get<::Tags::TimeStepId>(box);\n    const TimeStepId& next_time_step_id =\n        db::get<::Tags::Next<::Tags::TimeStepId>>(box);\n\n    const int tci_decision =\n        db::get<evolution::dg::subcell::Tags::TciDecision>(box);\n    using history_tags = ::Tags::get_all_history_tags<DbTags>;\n    static_assert(tmpl::size<history_tags>::value == 1);\n    const auto& integration_order =\n        db::get<tmpl::front<history_tags>>(box).integration_order();\n    // Compute and send actual variables\n    for (const auto& [direction, neighbors_in_direction] :\n         element.neighbors()) {\n      // Only need to send data for directions that are not\n      // problematic directions (keys of extension_directions).\n      if (not extension_directions.contains(direction)) {\n        ASSERT(neighbors_in_direction.size() == 1,\n               \"AMR is not yet supported when using DG-subcell. Note that this \"\n               \"condition could be relaxed to support AMR only where the \"\n               \"evolution is using DG without any changes to subcell.\");\n\n        for (const ElementId<Dim>& neighbor : neighbors_in_direction) {\n          const auto& orientation =\n              neighbors_in_direction.orientation(neighbor);\n          const auto direction_from_neighbor =\n              orientation(direction.opposite());\n          const size_t rdmp_size = rdmp_tci_data.max_variables_values.size() +\n                                   rdmp_tci_data.min_variables_values.size();\n          const auto& sliced_data_in_direction = all_sliced_data.at(direction);\n\n          // Allocate with subcell data and rdmp data\n          DataVector subcell_data_to_send{sliced_data_in_direction.size() +\n                                          rdmp_size};\n          // Note: Currently we interpolate our solution to our neighbor FD grid\n          // even when grid points align but are oriented differently. There's a\n          // possible optimization for the rare (almost never?) edge case where\n          // two blocks have the same ghost zone coordinates but have different\n          // orientations (e.g. RotatedBricks). Since this shouldn't ever happen\n          // outside of tests, we currently don't bother with it. If we wanted\n          // to, here's the code:\n          //\n          // if (not orientation.is_aligned()) {\n          //   std::array<size_t, Dim> slice_extents{};\n          //   for (size_t d = 0; d < Dim; ++d) {\n          //     gsl::at(slice_extents, d) = subcell_mesh.extents(d);\n          //   }\n          //   gsl::at(slice_extents, direction.dimension()) = ghost_zone_size;\n          //   // Need a view so we only get the subcell data and not the rdmp\n          //   // data\n          //   DataVector subcell_data_to_send_view{\n          //       subcell_data_to_send.data(),\n          //       subcell_data_to_send.size() - rdmp_size};\n          //   orient_variables(make_not_null(&subcell_data_to_send_view),\n          //                  sliced_data_in_direction,\n          //                  Index<Dim>{slice_extents}, orientation);\n          // } else { std::copy(...); }\n          //\n\n          // Copy over data since it's already oriented from interpolation\n          std::copy(sliced_data_in_direction.begin(),\n                    sliced_data_in_direction.end(),\n                    subcell_data_to_send.begin());\n          // Copy rdmp data to end of subcell_data_to_send\n          std::copy(rdmp_tci_data.max_variables_values.cbegin(),\n                    rdmp_tci_data.max_variables_values.cend(),\n                    std::prev(subcell_data_to_send.end(),\n                              static_cast<int>(rdmp_size)));\n          std::copy(rdmp_tci_data.min_variables_values.cbegin(),\n                    rdmp_tci_data.min_variables_values.cend(),\n                    std::prev(subcell_data_to_send.end(),\n                              static_cast<int>(\n                                  rdmp_tci_data.min_variables_values.size())));\n\n          evolution::dg::BoundaryData<Dim> data{\n              dg_mesh,      subcell_mesh,\n              std::nullopt, std::move(subcell_data_to_send),\n              std::nullopt, next_time_step_id,\n              tci_decision, integration_order};\n\n          Parallel::receive_data<\n              evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                  Dim,\n                  Parallel::is_dg_element_collection_v<ParallelComponent>>>(\n              receiver_proxy[neighbor], time_step_id,\n              std::pair{\n                  DirectionalId<Dim>{direction_from_neighbor, element.id()},\n                  std::move(data)});\n        }\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Handles ghost data at block boundaries to ensure interpolation\n * is (almost) always used instead of extrapolation.\n *\n * Since the coordinate maps are only continuous and not smooth at block\n * boundaries, the logical coordinate axes, and therefore grid point axes,\n * do not necessarily align. An example is given in the image below, which is\n * a snapshot of 2d circular domain built from one central square surrounded\n * by four deformed wedges. We zoom in on the upper right corner of the\n * cube for illustration purposes.\n *\n * \\image html curved_mesh_illustration.png width=600px\n *\n * Blue circles denote the cell-centered FD points in the two elements whose\n * ghost points are being exchanged, bright red diamonds denote the ghost\n * points needed for reconstruction in the element on the right, and dark red\n * squares denote the cell-centered FD points in a neighboring element not\n * directly participating in the exchange. The dashed blue and dash-dotted\n * red lines show lines of constant logical coordinates in the left and\n * right elements, respectively. Notice that they intersect on the boundary,\n * but do not align.\n *\n * In this example, three lowest ghost points cannot be directly\n * interpolated from the element on the left. In such a case,\n * we flag the direction (from the perspective of the element on the left,\n * in this example, the direction the green arrow points to) as\n * problematic and the ghost points may be filled either\n * by extrapolation or interpolation. Extrapolation can lead to an unphysical\n * state like negative densities, so interpolation is generally preferred.\n * To enable interpolation, set `EnableExtensionDirections` to true in\n * `SubcellOptions` part of the input file. This enables the use of\n * ghost data from the neighboring element in the extension direction\n * (in this example, the direction the grey arrow points to) to fill the\n * ghost points.\n *\n * \\warning This option is only available in the case where we are only\n * using FD scheme for the evolution, i.e. we are using true for\n * `AlwaysUseSubcell` in the `SubcellOptions` part of the input file.\n * Currently, the following cases are not supported:\n * 1. There are multiple neighbors in a direction\n * 2. There are multiple extension directions required for a\n *    single problematic direction.\n * 3. The extension direction is itself a problematic direction, which can\n *    result in a deadlock.\n *\n * When disabled, this action does nothing.\n *\n * When enabled, the action:\n * 1. Receives subcell ghost data from neighbors for all non-problematic\n *    directions.\n * 2. For “problematic directions” it extends the element’s volume data\n *    using ghost data from another neighbor (in the \"extension direction\").\n *    This extension ensures that the ghost data can be filled using\n *    interpolation.\n * 3. Interpolates to the requested ghost points, and then sends the completed\n *    ghost data to the original neighbor.\n */\ntemplate <size_t Dim, typename GhostDataMutator, bool UseNodegroupDgElements>\nstruct ReceiveAndSendDataForReconstruction {\n  using inbox_tags =\n      tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n          Dim, UseNodegroupDgElements>>;\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (not db::get<evolution::dg::subcell::Tags::SubcellOptions<Dim>>(box)\n                .enable_extension_directions()) {\n      // We are not using extension directions, so just continue without doing\n      // any work.\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const auto& extension_directions =\n        db::get<evolution::dg::subcell::Tags::ExtensionDirections<Dim>>(box);\n    if (extension_directions.empty()) {\n      // For this element, we have no extension directions, so just\n      // continue without doing any work.\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n    // Need to subtract number of problematic directions.\n    const auto number_of_expected_messages =\n        element.neighbors().size() - extension_directions.size();\n\n    if (UNLIKELY(number_of_expected_messages == 0)) {\n      // We have no neighbors, so just continue without doing any work.\n      // Technically, this could also happen if all of the neighbors are in\n      // problematic directions, but this case is unlikely to ever happen.\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    std::unordered_set<DirectionalId<Dim>> expected_keys;\n    for (const auto& internal_direction : element.internal_boundaries()) {\n      if (not extension_directions.contains(internal_direction)) {\n        // Note here, we assume that we have only one neighbor per\n        // direction, so we can just take the first one.\n        ASSERT(element.neighbors().at(internal_direction).ids().size() == 1,\n               \"Assumption one neighbor per direction failed. \"\n               \"direction: \"\n                   << internal_direction << \", neighbors.size() = \"\n                   << element.neighbors().at(internal_direction).ids().size()\n                   << \", element: \" << element.id());\n        expected_keys.emplace(\n            internal_direction,\n            *element.neighbors().at(internal_direction).ids().begin());\n      }\n    }\n\n    const auto& interpolants = db::get<\n        evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>(\n        box);\n\n    const auto& current_time_step_id = db::get<::Tags::TimeStepId>(box);\n    auto& inbox =\n        tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n            Metavariables::volume_dim,\n            Parallel::is_dg_element_collection_v<ParallelComponent>>>(inboxes);\n    inbox.collect_messages();\n    const auto received = inbox.messages.find(current_time_step_id);\n    // Check we have at least some data from correct time, and then check\n    // we have received all data\n    if (received == inbox.messages.end()) {\n      inbox.set_missing_messages(expected_keys.size());\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    if (const auto found = static_cast<size_t>(\n            alg::count_if(received->second,\n                          [&expected_keys](const auto& message) {\n                            return expected_keys.contains(message.first);\n                          }));\n        found < expected_keys.size()) {\n      inbox.set_missing_messages(expected_keys.size() - found);\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    const size_t ghost_zone_size =\n        Metavariables::SubcellOptions::ghost_zone_size(box);\n    const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);\n    const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);\n    const Index<Dim>& subcell_extents = subcell_mesh.extents();\n\n    const auto& received_data = received->second;\n    ASSERT(received_data.size() >= number_of_expected_messages,\n           \"received_data size: \" << received_data.size()\n                                  << \" less than expected number of messages: \"\n                                  << number_of_expected_messages << \" !\");\n\n    using flux_variables = typename Metavariables::system::flux_variables;\n    const auto& cell_centered_flux =\n        db::get<Tags::CellCenteredFlux<flux_variables, Dim>>(box);\n    DataVector volume_data_to_slice = db::mutate_apply(\n        GhostDataMutator{}, make_not_null(&box),\n        cell_centered_flux.has_value() ? cell_centered_flux.value().size()\n                                       : 0_st);\n    if (cell_centered_flux.has_value()) {\n      std::copy(\n          cell_centered_flux.value().data(),\n          std::next(\n              cell_centered_flux.value().data(),\n              static_cast<std::ptrdiff_t>(cell_centered_flux.value().size())),\n          std::next(\n              volume_data_to_slice.data(),\n              static_cast<std::ptrdiff_t>(volume_data_to_slice.size() -\n                                          cell_centered_flux.value().size())));\n    }\n\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    const RdmpTciData& rdmp_tci_data = db::get<Tags::DataForRdmpTci>(box);\n    const TimeStepId& time_step_id = db::get<::Tags::TimeStepId>(box);\n    const TimeStepId& next_time_step_id =\n        db::get<::Tags::Next<::Tags::TimeStepId>>(box);\n\n    const int tci_decision =\n        db::get<evolution::dg::subcell::Tags::TciDecision>(box);\n    using history_tags = ::Tags::get_all_history_tags<DbTags>;\n    static_assert(tmpl::size<history_tags>::value == 1);\n    const auto& integration_order =\n        db::get<tmpl::front<history_tags>>(box).integration_order();\n\n    const size_t number_of_points = subcell_mesh.extents().product();\n    // Number of independent components per grid point.\n    const size_t number_of_components =\n        volume_data_to_slice.size() / number_of_points;\n\n    // For each problematic direction (a direction for which the element\n    // cannot directly provide ghost data):\n    // 1. Receive ghost data from our neighbor in the direction specified by\n    // extension_direction.direction_to_extend.\n    // 2. Use this data to extend our own volume data, then interpolate to the\n    // required ghost points.\n    // 3. Send the final ghost data to the neighbor at problematic_direction.\n    for (const auto& [problematic_direction, extension_direction] :\n         extension_directions) {\n      // Direction to extend the volume data.\n      const Direction<Dim> direction_to_extend =\n          extension_direction.direction_to_extend;\n\n      // Check that direction_to_extend is not a problematic direction.\n      ASSERT(problematic_direction != direction_to_extend,\n             \"The direction to extend must not be a problematic direction. \"\n             \"problematic_direction: \"\n                 << problematic_direction\n                 << \", direction_to_extend: \" << direction_to_extend);\n\n      // Only one neighbor per direction.\n      ASSERT(element.neighbors().at(direction_to_extend).ids().size() == 1,\n             \"Assumption one neighbor per direction failed. \"\n             \"direction_to_extend: \"\n                 << direction_to_extend << \", neighbors.size() = \"\n                 << element.neighbors().at(direction_to_extend).ids().size()\n                 << \", element: \" << element.id());\n\n      // Only internal boundary for the extension.\n      ASSERT(element.internal_boundaries().contains(direction_to_extend),\n             \"Direction to extend not an internal boundary! \"\n             \"dir_to_extend = \"\n                 << direction_to_extend << \", element.internal_boundaries() = \"\n                 << element.internal_boundaries()\n                 << \", element = \" << element.id());\n\n      // Again, we are assuming that we have only one neighbor per direction,\n      // and we are just taking the first one.\n      const ElementId<Dim> relevant_neighbor_id =\n          *((element.neighbors()).at(direction_to_extend).ids().begin());\n\n      const auto received_data_for_direction_it =\n          alg::find_if(received_data, [&](const auto& entry) {\n            return entry.first == DirectionalId<Dim>{direction_to_extend,\n                                                     relevant_neighbor_id};\n          });\n      // Received data must have entry for direction to extend.\n      ASSERT(received_data_for_direction_it != received_data.end(),\n             \"Received data missing entry for direction to extend.\"\n                 << \" direction_to_extend = \" << direction_to_extend\n                 << \", relevant_neighbor_id = \" << relevant_neighbor_id\n                 << \", problematic_direction = \" << problematic_direction\n                 << \", element = \" << element.id());\n      const auto& received_data_for_direction =\n          received_data_for_direction_it->second;\n\n      // Received data must have received ghost data for this extension\n      // direction.\n      ASSERT(received_data_for_direction.ghost_cell_data.has_value(),\n             \"Ghost data missing for this extension direction.\"\n                 << \" direction_to_extend = \" << direction_to_extend\n                 << \", relevant_neighbor_id = \" << relevant_neighbor_id\n                 << \", problematic_direction = \" << problematic_direction\n                 << \", element = \" << element.id());\n\n      const ElementId<Dim> problematic_neighbor_id =\n          *((element.neighbors()).at(problematic_direction).ids().begin());\n\n      const auto& orientation = (element.neighbors())\n                                    .at(problematic_direction)\n                                    .orientation(problematic_neighbor_id);\n      const auto direction_from_neighbor =\n          orientation(problematic_direction.opposite());\n\n      const size_t rdmp_size = rdmp_tci_data.max_variables_values.size() +\n                               rdmp_tci_data.min_variables_values.size();\n\n      const DataVector& full_ghost_cell_data =\n          received_data_for_direction.ghost_cell_data.value();\n      const size_t relevant_ghost_data_size =\n          full_ghost_cell_data.size() - rdmp_size;\n\n      const DataVector relevant_ghost_data;\n      make_const_view(make_not_null(&relevant_ghost_data), full_ghost_cell_data,\n                      0, relevant_ghost_data_size);\n\n      const auto& interpolant =\n          (interpolants.at(DirectionalId<Dim>{problematic_direction,\n                                              problematic_neighbor_id}))\n              .value();\n      const DataVector combined_data = combine_volume_ghost_data(\n          volume_data_to_slice, relevant_ghost_data, subcell_extents,\n          ghost_zone_size, direction_to_extend);\n      const size_t result_size =\n          ghost_zone_size * subcell_mesh.extents()\n                                .slice_away(problematic_direction.dimension())\n                                .product();\n      const size_t span_size = result_size * number_of_components;\n\n      DataVector subcell_data_to_send{span_size + rdmp_size};\n\n      auto result_span = gsl::make_span(subcell_data_to_send.data(), span_size);\n      interpolant.interpolate(\n          make_not_null(&result_span),\n          gsl::make_span(combined_data.data(), combined_data.size()));\n\n      // Copy rdmp data to end of subcell_data_to_send\n      std::copy(\n          rdmp_tci_data.max_variables_values.cbegin(),\n          rdmp_tci_data.max_variables_values.cend(),\n          std::prev(subcell_data_to_send.end(), static_cast<int>(rdmp_size)));\n      std::copy(rdmp_tci_data.min_variables_values.cbegin(),\n                rdmp_tci_data.min_variables_values.cend(),\n                std::prev(subcell_data_to_send.end(),\n                          static_cast<int>(\n                              rdmp_tci_data.min_variables_values.size())));\n      evolution::dg::BoundaryData<Dim> data{\n          dg_mesh,      subcell_mesh,\n          std::nullopt, std::move(subcell_data_to_send),\n          std::nullopt, next_time_step_id,\n          tci_decision, integration_order};\n      Parallel::receive_data<\n          evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n              Dim, Parallel::is_dg_element_collection_v<ParallelComponent>>>(\n          receiver_proxy[problematic_neighbor_id], time_step_id,\n          std::pair{DirectionalId<Dim>{direction_from_neighbor, element.id()},\n                    std::move(data)});\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n/*!\n * \\brief Receive the subcell data from our neighbor, and accumulate the data\n * from the relaxed discrete maximum principle troubled-cell indicator.\n *\n * Note:\n * - Since we only care about the min/max over all neighbors and ourself at the\n *   past time, we accumulate all data immediately into the `RdmpTciData`.\n * - If the neighbor is using DG and therefore sends boundary correction data\n *   then that is added into the `evolution::dg::Tags::MortarData` tag\n * - The next `TimeStepId` is recorded, but we do not yet support local time\n *   stepping.\n * - This action will never care about what variables are sent for\n *   reconstruction. It is only responsible for receiving the data and storing\n *   it in the `NeighborData`.\n *\n * GlobalCache:\n * -Uses: nothing\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::Element<Dim>`\n *   - `Tags::TimeStepId`\n *   - `domain::Tags::Mesh<Dim>`\n *   - `subcell::Tags::Mesh<Dim>`\n *   - `domain::Tags::Element<Dim>`\n *   - `Tags::Next<Tags::TimeStepId>`\n *   - `subcell::Tags::ActiveGrid`\n *   - `System::variables_tag`\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `subcell::Tags::GhostDataForReconstruction<Dim>`\n *   - `subcell::Tags::DataForRdmpTci`\n *   - `evolution::dg::Tags::MortarData`\n *   - `evolution::dg::Tags::MortarNextTemporalId`\n */\ntemplate <size_t Dim>\nstruct ReceiveDataForReconstruction {\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n    const auto number_of_expected_messages = element.neighbors().size();\n    if (UNLIKELY(number_of_expected_messages == 0)) {\n      // We have no neighbors, so just continue without doing any work\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    using ::operator<<;\n    const auto& current_time_step_id = db::get<::Tags::TimeStepId>(box);\n    auto& inbox =\n        tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n            Metavariables::volume_dim,\n            Parallel::is_dg_element_collection_v<ParallelComponent>>>(inboxes);\n    inbox.collect_messages();\n    const auto received = inbox.messages.find(current_time_step_id);\n    // Check we have at least some data from correct time, and then check that\n    // we have received all data\n    if (received == inbox.messages.end()) {\n      inbox.set_missing_messages(number_of_expected_messages);\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    if (received->second.size() != number_of_expected_messages) {\n      inbox.set_missing_messages(number_of_expected_messages -\n                                 received->second.size());\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Now that we have received all the data, copy it over as needed.\n    auto received_data = std::move(received->second);\n    inbox.messages.erase(received);\n\n    const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);\n    const auto& mortar_meshes = get<evolution::dg::Tags::MortarMesh<Dim>>(box);\n\n    db::mutate<Tags::GhostDataForReconstruction<Dim>, Tags::DataForRdmpTci,\n               evolution::dg::Tags::MortarData<Dim>,\n               evolution::dg::Tags::MortarNextTemporalId<Dim>,\n               domain::Tags::NeighborMesh<Dim>,\n               evolution::dg::subcell::Tags::MeshForGhostData<Dim>,\n               evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n        [&element,\n         ghost_zone_size = Metavariables::SubcellOptions::ghost_zone_size(box),\n         &received_data, &subcell_mesh, &mortar_meshes](\n            const gsl::not_null<DirectionalIdMap<Dim, GhostData>*>\n                ghost_data_ptr,\n            const gsl::not_null<RdmpTciData*> rdmp_tci_data_ptr,\n            const gsl::not_null<\n                DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n                mortar_data,\n            const gsl::not_null<DirectionalIdMap<Dim, TimeStepId>*>\n                mortar_next_time_step_id,\n            const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*>\n                neighbor_mesh,\n            const gsl::not_null<DirectionalIdMap<Dim, Mesh<Dim>>*>\n                mesh_for_ghost_data,\n            const auto neighbor_tci_decisions,\n            const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n                neighbor_dg_to_fd_interpolants) {\n          // Remove neighbor meshes for neighbors that don't exist anymore\n          domain::remove_nonexistent_neighbors(neighbor_mesh, element);\n          domain::remove_nonexistent_neighbors(mesh_for_ghost_data, element);\n\n          // Get the next time step id, and also the fluxes data if the neighbor\n          // is doing DG.\n          for (auto& received_mortar_data : received_data) {\n            const auto& mortar_id = received_mortar_data.first;\n            try {\n              mortar_next_time_step_id->at(mortar_id) =\n                  received_mortar_data.second.validity_range;\n            } catch (std::exception& e) {\n              ERROR(\"Failed retrieving the MortarId: (\"\n                    << mortar_id.direction() << ',' << mortar_id.id()\n                    << \") from the mortar_next_time_step_id. Got exception: \"\n                    << e.what());\n            }\n            if (received_mortar_data.second.boundary_correction_data\n                    .has_value()) {\n              mortar_data->at(mortar_id).neighbor().face_mesh =\n                  received_mortar_data.second.volume_mesh.slice_away(\n                      mortar_id.direction().dimension());\n              mortar_data->at(mortar_id).neighbor().mortar_mesh =\n                  mortar_meshes.at(mortar_id);\n              mortar_data->at(mortar_id).neighbor().mortar_data = std::move(\n                  *received_mortar_data.second.boundary_correction_data);\n            }\n            // Set new neighbor mesh\n            neighbor_mesh->insert_or_assign(\n                mortar_id, received_mortar_data.second.volume_mesh);\n            mesh_for_ghost_data->insert_or_assign(\n                mortar_id, received_mortar_data.second\n                               .volume_mesh_ghost_cell_data.value());\n          }\n\n          ASSERT(ghost_data_ptr->empty(),\n                 \"Should have no elements in the neighbor data when \"\n                 \"receiving neighbor data\");\n          const size_t number_of_rdmp_vars =\n              rdmp_tci_data_ptr->max_variables_values.size();\n          ASSERT(rdmp_tci_data_ptr->min_variables_values.size() ==\n                     number_of_rdmp_vars,\n                 \"The number of RDMP variables for which we have a maximum \"\n                 \"and minimum should be the same, but we have \"\n                     << number_of_rdmp_vars << \" for the max and \"\n                     << rdmp_tci_data_ptr->min_variables_values.size()\n                     << \" for the min.\");\n\n          for (const auto& [directional_element_id, boundary_data] :\n               received_data) {\n            ASSERT(ghost_data_ptr->count(directional_element_id) == 0,\n                   \"Found neighbor already inserted in direction \"\n                       << directional_element_id.direction()\n                       << \" with ElementId \" << directional_element_id.id());\n            ASSERT(boundary_data.ghost_cell_data.has_value(),\n                   \"Received subcell data message that does not contain any \"\n                   \"actual subcell data for reconstruction.\");\n            // Collect the max/min of u(t^n) for the RDMP as we receive data.\n            // This reduces the memory footprint.\n\n            evolution::dg::subcell::insert_neighbor_rdmp_and_volume_data(\n                rdmp_tci_data_ptr, ghost_data_ptr,\n                *boundary_data.ghost_cell_data, number_of_rdmp_vars,\n                directional_element_id,\n                mesh_for_ghost_data->at(directional_element_id), element,\n                subcell_mesh, ghost_zone_size, neighbor_dg_to_fd_interpolants);\n            ASSERT(neighbor_tci_decisions->contains(directional_element_id),\n                   \"The NeighorTciDecisions should contain the neighbor (\"\n                       << directional_element_id.direction() << \", \"\n                       << directional_element_id.id() << \") but doesn't\");\n            neighbor_tci_decisions->at(directional_element_id) =\n                boundary_data.tci_status;\n          }\n        },\n        make_not_null(&box),\n        db::get<\n            evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>>(\n            box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace evolution::dg::subcell::Actions\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/SelectNumericalMethod.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/DgSubcell/Actions/Labels.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace evolution::dg::subcell::Actions {\n/*!\n * \\brief Goes to `Labels::BeginDg` or `Labels::BeginSubcell` depending on\n * whether the active grid is `Dg` or `Subcell`.\n *\n * GlobalCache: nothing\n *\n * DataBox:\n * - Uses:\n *   - `subcell::Tags::ActiveGrid`\n */\nstruct SelectNumericalMethod {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Note: we jump to the `Label+1` because the label actions don't do\n    // anything anyway\n    if (db::get<Tags::ActiveGrid>(box) == subcell::ActiveGrid::Dg) {\n      const size_t dg_index =\n          tmpl::index_of<ActionList, ::Actions::Label<Labels::BeginDg>>::value +\n          1;\n      return {Parallel::AlgorithmExecution::Continue, dg_index};\n    } else if (db::get<Tags::ActiveGrid>(box) == subcell::ActiveGrid::Subcell) {\n      const size_t subcell_index =\n          tmpl::index_of<ActionList,\n                         ::Actions::Label<Labels::BeginSubcell>>::value +\n          1;\n      return {Parallel::AlgorithmExecution::Continue, subcell_index};\n    }\n    ERROR(\n        \"Only know DG and subcell active grids for selecting the numerical \"\n        \"method.\");\n  }\n};\n}  // namespace evolution::dg::subcell::Actions\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/TakeTimeStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace evolution::dg::subcell::fd::Actions {\n/*!\n * \\brief Take a finite-difference time step on the subcell grid.\n *\n * The template parameter `TimeDerivative` must have a `static apply` function\n * that takes the `DataBox` by `gsl::not_null` as the first argument, the\n * cell-centered inverse Jacobian from the logical to the grid frame as the\n * second argument, and its determinant as the third argument.\n *\n * GlobalCache: nothing\n *\n * DataBox:\n * - Uses:\n *   - `subcell::fd::Tags::InverseJacobianLogicalToGrid<Dim>`\n *   - `subcell::fd::Tags::DetInverseJacobianLogicalToGrid`\n *   - `domain::Tags::ElementMap<Dim, Frame::Grid>`\n *   - `domain::CoordinateMaps::Tags::CoordinateMap<Dim, Grid, Inertial>`\n *   - `subcell::Tags::Coordinates<Dim, Frame::ElementLogical>`\n *   - Anything that `Metavariables::SubcellOptions::TimeDerivative` uses\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - Anything that `Metavariables::SubcellOptions::TimeDerivative` modifies\n */\ntemplate <typename TimeDerivative>\nstruct TakeTimeStep {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent, size_t Dim = Metavariables::volume_dim>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    TimeDerivative::apply(make_not_null(&box));\n\n    db::mutate<evolution::dg::Tags::MortarData<Dim>>(\n        [](const auto mortar_data_ptr) {\n          for (auto& data : *mortar_data_ptr) {\n            data.second = evolution::dg::MortarDataHolder<Dim>{};\n          }\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace evolution::dg::subcell::fd::Actions\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/TciAndRollback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <deque>\n#include <iterator>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Actions/Labels.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/NeighborRdmpAndVolumeData.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/MeshForGhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/History.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::dg::subcell::Actions {\n/*!\n * \\brief Run the troubled-cell indicator on the candidate solution and perform\n * the time step rollback if needed.\n *\n * Interior cells are marked as troubled if\n * `subcell_options.always_use_subcells()` is `true`, or if either the RDMP\n * troubled-cell indicator (TCI) or the TciMutator reports the cell is\n * troubled. Exterior cells are marked as troubled only if\n * `Metavariables::SubcellOptions::subcell_enabled_at_external_boundary` is\n * `true`.\n *\n * The troubled-cell indicator (TCI) given by the mutator `TciMutator` can\n * mutate tags in the DataBox, but should do so cautiously. The main reason that\n * this is a mutator is because primitive variables, such as the pressure, are\n * used to check if the solution is physical. In the relativistic case, even\n * just whether or not the primitive variables can be recovered is used as a\n * condition. Note that the evolved variables are projected to the subcells\n * _after_ the TCI is called and marks the cell as troubled.\n *\n * After rollback, the subcell scheme must project the DG boundary corrections\n * \\f$G\\f$ to the subcells for the scheme to be conservative. The subcell\n * actions know if a rollback was done because the local mortar data would\n * already be computed.\n */\ntemplate <typename TciMutator>\nstruct TciAndRollback {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent, size_t Dim = Metavariables::volume_dim>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        tmpl::count_if<\n            ActionList,\n            std::is_same<tmpl::_1, tmpl::pin<TciAndRollback>>>::value == 1,\n        \"Must have the TciAndRollback action exactly once in the action list \"\n        \"of a phase.\");\n    static_assert(\n        tmpl::count_if<\n            ActionList,\n            std::is_same<tmpl::_1,\n                         tmpl::pin<::Actions::Label<\n                             evolution::dg::subcell::Actions::Labels::\n                                 BeginSubcellAfterDgRollback>>>>::value == 1,\n        \"Must have the BeginSubcellAfterDgRollback label exactly once in the \"\n        \"action list of a phase.\");\n\n    using variables_tag = typename Metavariables::system::variables_tag;\n\n    const ActiveGrid active_grid = db::get<Tags::ActiveGrid>(box);\n    ASSERT(active_grid == ActiveGrid::Dg,\n           \"Must be using DG when calling TciAndRollback action.\");\n\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n    const bool cell_has_external_boundary =\n        not element.external_boundaries().empty();\n\n    constexpr bool subcell_enabled_at_external_boundary =\n        Metavariables::SubcellOptions::subcell_enabled_at_external_boundary;\n    const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);\n    const Mesh<Dim>& subcell_mesh = db::get<Tags::Mesh<Dim>>(box);\n\n    const SubcellOptions& subcell_options =\n        db::get<Tags::SubcellOptions<Dim>>(box);\n    bool cell_is_troubled =\n        subcell_options.always_use_subcells() or\n        (subcell_options.use_halo() and [&box]() -> bool {\n          for (const auto& [_, neighbor_decision] :\n               db::get<evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n                   box)) {\n            if (neighbor_decision != 0) {\n              return true;\n            }\n          }\n          return false;\n        }());\n\n    // Loop over block neighbors and if neighbor id is inside of\n    // subcell_options.only_dg_block_ids(), then bordering DG-only block\n    const bool bordering_dg_block = alg::any_of(\n        element.neighbors(),\n        [&subcell_options](const auto& direction_and_neighbor) {\n          const size_t first_block_id =\n              direction_and_neighbor.second.ids().begin()->block_id();\n          return alg::found(subcell_options.only_dg_block_ids(),\n                            first_block_id);\n        });\n\n    // Subcell is allowed in the element if 2 conditions are met:\n    // (i)  The current element block id is not marked as DG only\n    // (ii) The current element is not bordering a DG only block.\n    const bool subcell_allowed_in_element =\n        not alg::found(subcell_options.only_dg_block_ids(),\n                       element.id().block_id()) and\n        not bordering_dg_block;\n\n    // The reason we pass in the persson_exponent explicitly instead of\n    // leaving it to the user is because the value of the exponent that\n    // should be used to decide if it is safe to switch back to DG should be\n    // `persson_exponent+1` to prevent the code from rapidly switching back\n    // and forth between DG and subcell. Rather than trying to enforce this\n    // by documentation, the switching back to DG TCI gets passed in the\n    // exponent it should use, and to keep the interface between the TCIs\n    // consistent, we also pass the exponent in separately here.\n    std::tuple<int, RdmpTciData> tci_result = db::mutate_apply<TciMutator>(\n        make_not_null(&box), subcell_options.persson_exponent(),\n        not subcell_allowed_in_element);\n\n    const int tci_decision = std::get<0>(tci_result);\n    db::mutate<Tags::TciDecision>(\n        [&tci_decision](const gsl::not_null<int*> tci_decision_ptr) {\n          *tci_decision_ptr = tci_decision;\n        },\n        make_not_null(&box));\n\n    cell_is_troubled |= (tci_decision != 0);\n\n    // If either:\n    //\n    // 1. we are not allowed to do subcell in this block\n    // 2. the element is at an outer boundary _and_ we aren't allow to go to\n    //    subcell at an outer boundary.\n    // 3. the cell is not troubled\n    //\n    // then we can remove the current neighbor data and update the RDMP TCI\n    // data.\n    if (not subcell_allowed_in_element or\n        (cell_has_external_boundary and\n         not subcell_enabled_at_external_boundary) or\n        not cell_is_troubled) {\n      db::mutate<subcell::Tags::GhostDataForReconstruction<Dim>,\n                 subcell::Tags::DataForRdmpTci>(\n          [&tci_result](const auto neighbor_data_ptr,\n                        const gsl::not_null<RdmpTciData*> rdmp_tci_data_ptr) {\n            neighbor_data_ptr->clear();\n            *rdmp_tci_data_ptr = std::move(std::get<1>(std::move(tci_result)));\n          },\n          make_not_null(&box));\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    db::mutate<variables_tag, ::Tags::HistoryEvolvedVariables<variables_tag>,\n               Tags::ActiveGrid, Tags::DidRollback,\n               subcell::Tags::GhostDataForReconstruction<Dim>>(\n        [&dg_mesh, &element, &subcell_mesh](\n            const auto active_vars_ptr, const auto active_history_ptr,\n            const gsl::not_null<ActiveGrid*> active_grid_ptr,\n            const gsl::not_null<bool*> did_rollback_ptr,\n            const gsl::not_null<DirectionalIdMap<Dim, GhostData>*>\n                ghost_data_ptr,\n            const DirectionalIdMap<Dim, Mesh<Dim>>& meshes_for_ghost_data,\n            const size_t ghost_zone_size,\n            const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n                neighbor_dg_to_fd_interpolants) {\n          ASSERT(active_history_ptr->size() > 0,\n                 \"We cannot have an empty history when unwinding, that's just \"\n                 \"nutty. Did you call the action too early in the action \"\n                 \"list?\");\n          // Rollback u^{n+1}* to u^n (undoing the candidate solution).\n          //\n          // Note: strictly speaking, to be conservative this should project\n          // uJ instead of u.\n          *active_vars_ptr = fd::project(active_history_ptr->latest_value(),\n                                         dg_mesh, subcell_mesh.extents());\n\n          // Project the time stepper history to the subcells, excluding the\n          // most recent inadmissible history.\n          active_history_ptr->undo_latest();\n          active_history_ptr->map_entries(\n              [&dg_mesh, &subcell_mesh](const auto entry) {\n                *entry = fd::project(*entry, dg_mesh, subcell_mesh.extents());\n              });\n          *active_grid_ptr = ActiveGrid::Subcell;\n          *did_rollback_ptr = true;\n          // Project the neighbor data we were sent for reconstruction since\n          // the neighbor might have sent DG volume data instead of ghost data\n          // in order to elide projections when they aren't necessary.\n          for (const auto& [directional_element_id, mesh_for_ghost_data] :\n               meshes_for_ghost_data) {\n            evolution::dg::subcell::insert_or_update_neighbor_volume_data<\n                false>(ghost_data_ptr,\n                       ghost_data_ptr->at(directional_element_id)\n                           .neighbor_ghost_data_for_reconstruction(),\n                       0, directional_element_id, mesh_for_ghost_data, element,\n                       subcell_mesh, ghost_zone_size,\n                       neighbor_dg_to_fd_interpolants);\n          }\n\n          // Note: We do _not_ project the boundary history here because\n          // that needs to be done at the lifting stage of the subcell\n          // method, since we need to lift G+D instead of the ingredients\n          // that go into G+D, which is what we would be projecting here.\n        },\n        make_not_null(&box),\n        db::get<evolution::dg::subcell::Tags::MeshForGhostData<Dim>>(box),\n        Metavariables::SubcellOptions::ghost_zone_size(box),\n        db::get<\n            evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>>(\n            box));\n\n    if (UNLIKELY(db::get<::Tags::TimeStepId>(box).slab_number() < 0)) {\n      // If we are doing self start, then we need to project the initial\n      // guess to the subcells as well.\n      //\n      // Warning: this unfortunately needs to be kept in sync with the\n      //          self-start procedure.\n      //\n      // Note: if we switch to the subcells then we might have an\n      // inconsistent\n      //       state between the primitive and conservative variables on the\n      //       subcells. The most correct thing is to re-compute the\n      //       primitive variables on the subcells, since projecting the\n      //       conservative variables is conservative.\n      if constexpr (Metavariables::system::\n                        has_primitive_and_conservative_vars) {\n        db::mutate<\n            SelfStart::Tags::InitialValue<variables_tag>,\n            SelfStart::Tags::InitialValue<\n                typename Metavariables::system::primitive_variables_tag>>(\n            [&dg_mesh, &subcell_mesh](const auto initial_vars_ptr,\n                                      const auto initial_prim_vars_ptr) {\n              // Note: for strict conservation, we need to project uJ\n              // instead of just u.\n              std::get<0>(*initial_vars_ptr) =\n                  fd::project(std::get<0>(*initial_vars_ptr), dg_mesh,\n                              subcell_mesh.extents());\n              std::get<0>(*initial_prim_vars_ptr) =\n                  fd::project(std::get<0>(*initial_prim_vars_ptr), dg_mesh,\n                              subcell_mesh.extents());\n            },\n            make_not_null(&box));\n      } else {\n        db::mutate<SelfStart::Tags::InitialValue<variables_tag>>(\n            [&dg_mesh, &subcell_mesh](const auto initial_vars_ptr) {\n              // Note: for strict conservation, we need to project uJ\n              // instead of just u.\n              std::get<0>(*initial_vars_ptr) =\n                  fd::project(std::get<0>(*initial_vars_ptr), dg_mesh,\n                              subcell_mesh.extents());\n            },\n            make_not_null(&box));\n      }\n    }\n\n    return {Parallel::AlgorithmExecution::Continue,\n            tmpl::index_of<\n                ActionList,\n                ::Actions::Label<evolution::dg::subcell::Actions::Labels::\n                                     BeginSubcellAfterDgRollback>>::value +\n                1};\n  }\n};\n}  // namespace evolution::dg::subcell::Actions\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Actions/TciAndSwitchToDg.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <deque>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/StepsSinceTciCall.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciCallsSinceRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciGridHistory.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nclass TimeStepper;\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace evolution::dg::subcell::Actions {\n/*!\n * \\brief Run the troubled-cell indicator on the subcell solution to see if it\n * is safe to switch back to DG.\n *\n * In terms of the DG-subcell/FD hybrid solver, this action is run after the FD\n * step has calculated the solution at \\f$t^{n+1}\\f$. At this point we check if\n * the FD solution at the new time \\f$t^{n+1}\\f$ is representable on the DG\n * grid.\n *\n * The algorithm proceeds as follows:\n * 1. If we are using a substep time integrator and are not at the end of a\n *    step, or we are in the self-starting stage of a multistep method, or the\n *    `subcell_options.always_use_subcells() == true`, then we do not run any\n *    TCI or try to go back to DG. We need to avoid reconstructing (in the sense\n *    of the inverse of projecting the DG solution to the subcells) the time\n *    stepper history if there are shocks present in the history, and for\n *    substep methods this is most easily handled by only switching back at the\n *    end of a full time step. During the self-start phase of the multistep time\n *    integrators we integrate over the same region of time at increasingly\n *    higher order, which means if we were on subcell \"previously\" (since we use\n *    a forward-in-time self-start method the time history is actually in the\n *    future of the current step) then we will very likely need to again switch\n *    to subcell.\n * 2. Reconstruct the subcell solution to the DG grid.\n * 3. Run the relaxed discrete maximum principle (RDMP) troubled-cell indicator\n *    (TCI), checking both the subcell solution at \\f$t^{n+1}\\f$ and the\n *    reconstructed DG solution at \\f$t^{n+1}\\f$ to make sure they are\n *    admissible.\n * 4. If the RDMP TCI marked the DG solution as admissible, run the\n *    user-specified mutator TCI `TciMutator`.\n * 5. If the cell is not troubled, and the time integrator type is substep or\n *    the TCI history indicates the entire history for the multistep method is\n *    free of discontinuities, then we can switch back to DG. Switching back to\n *    DG requires reconstructing dg variables, reconstructing the time stepper\n *    history, marking the active grid as `ActiveGrid::Dg`, and clearing the\n *    subcell neighbor data.\n * 6. If we are not using a substep method, then record the TCI decision in the\n *    `subcell::Tags::TciGridHistory`.\n *\n * \\note Unlike `Actions::TciAndRollback`, this action does _not_ jump back to\n * `Labels::BeginDg`. This is because users may add actions after a time step\n * has been completed. In that sense, it may be more proper to actually check\n * the TCI and switch back to DG at the start of the step rather than the end.\n *\n * \\note This action always sets `subcell::Tags::DidRollback` to `false` at the\n * very beginning since this action is called after an FD step has completed.\n *\n * \\note If `subcell::Tags::DidRollback=True` i.e. the grid has been switched\n * from DG to FD in the current time step by preceding actions, this action is\n * skipped except setting `DidRollback` to `false`. Stated differently, if an\n * element switched from DG to FD it needs to remain at least one time step on\n * the FD grid.\n *\n * GlobalCache:\n * - Uses:\n *   - `subcell::Tags::SubcellOptions`\n *\n * DataBox:\n * - Uses:\n *   - `domain::Tags::Mesh<Dim>`\n *   - `subcell::Tags::Mesh<Dim>`\n *   - `Tags::TimeStepId`\n *   - `subcell::Tags::ActiveGrid`\n *   - `subcell::Tags::DataForRdmpTci`\n *   - `subcell::Tags::TciGridHistory`\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `System::variables_tag` if the cell is not troubled\n *   - `Tags::HistoryEvolvedVariables` if the cell is not troubled\n *   - `subcell::Tags::ActiveGrid` if the cell is not troubled\n *   - `subcell::Tags::DidRollback` sets to `false`\n *   - `subcell::Tags::TciDecision` is set to an integer value according to the\n *     return of TciMutator.\n *   - `subcell::Tags::GhostDataForReconstruction<Dim>`\n *     if the cell is not troubled\n *   - `subcell::Tags::TciGridHistory` if the time stepper is a multistep method\n */\ntemplate <typename TciMutator>\nstruct TciAndSwitchToDg {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent, size_t Dim = Metavariables::volume_dim>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        tmpl::count_if<\n            ActionList,\n            std::is_same<tmpl::_1, tmpl::pin<TciAndSwitchToDg>>>::value == 1,\n        \"Must have the TciAndSwitchToDg action exactly once in the action list \"\n        \"of a phase.\");\n\n    using variables_tag = typename Metavariables::system::variables_tag;\n    using flux_variables = typename Metavariables::system::flux_variables;\n\n    ASSERT(db::get<Tags::ActiveGrid>(box) == ActiveGrid::Subcell,\n           \"Must be using subcells when calling TciAndSwitchToDg action.\");\n    const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(box);\n    const Mesh<Dim>& subcell_mesh = db::get<subcell::Tags::Mesh<Dim>>(box);\n    const SubcellOptions& subcell_options =\n        db::get<Tags::SubcellOptions<Dim>>(box);\n    const TimeStepId& time_step_id = db::get<::Tags::TimeStepId>(box);\n\n    // This should never be run if we are prohibited from using subcell on this\n    // element.\n    ASSERT(not alg::found(\n               subcell_options.only_dg_block_ids(),\n               db::get<::domain::Tags::Element<Dim>>(box).id().block_id()),\n           \"Should never use subcell on element \"\n               << db::get<domain::Tags::Element<Dim>>(box).id());\n\n    // The first condition is that for substep time integrators we only allow\n    // switching back to DG on step boundaries. This is the easiest way to\n    // avoid having a shock in the time stepper history, since there is no\n    // history at step boundaries.\n    //\n    // The second condition is that if we are in the self-start procedure of\n    // the time stepper, and we don't want to switch from subcell back to DG\n    // during self-start since we integrate over the same temporal region at\n    // increasingly higher order.\n    //\n    // The third condition is that the user has requested we always do\n    // subcell, so effectively a finite difference/volume code.\n    const bool only_need_rdmp_data =\n        db::get<subcell::Tags::DidRollback>(box) or\n        (time_step_id.substep() != 0 or time_step_id.slab_number() < 0)\n\n        or db::get<evolution::dg::subcell::Tags::StepsSinceTciCall>(box) + 1 <\n               subcell_options.number_of_steps_between_tci_calls();\n    if (UNLIKELY(db::get<subcell::Tags::DidRollback>(box))) {\n      db::mutate<subcell::Tags::DidRollback>(\n          [](const gsl::not_null<bool*> did_rollback) {\n            *did_rollback = false;\n          },\n          make_not_null(&box));\n    }\n\n    if (subcell_options.always_use_subcells()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    std::tuple<int, evolution::dg::subcell::RdmpTciData> tci_result =\n        db::mutate_apply<TciMutator>(make_not_null(&box),\n                                     subcell_options.persson_exponent() + 1.0,\n                                     only_need_rdmp_data);\n\n    db::mutate<evolution::dg::subcell::Tags::DataForRdmpTci,\n               evolution::dg::subcell::Tags::TciCallsSinceRollback,\n               evolution::dg::subcell::Tags::StepsSinceTciCall>(\n        [only_need_rdmp_data, &subcell_options, &tci_result](\n            const auto rdmp_data_ptr,\n            const gsl::not_null<size_t*> tci_calls_since_rollback_ptr,\n            const gsl::not_null<size_t*> steps_since_tci_call_ptr) {\n          *rdmp_data_ptr = std::move(std::get<1>(std::move(tci_result)));\n          (*tci_calls_since_rollback_ptr) += (only_need_rdmp_data ? 0 : 1);\n          if ((*steps_since_tci_call_ptr) + 1 <\n              subcell_options.number_of_steps_between_tci_calls()) {\n            (*steps_since_tci_call_ptr) += 1;\n          } else {\n            (*steps_since_tci_call_ptr) = 0;\n          }\n        },\n        make_not_null(&box));\n\n    // Use <= for TciCallsSinceRollback since we've already incremented the\n    // count in a mutate call above. This means that if this is the first TCI\n    // call after a rollback, TciCallsSinceRollback will be 1, not 0. We also\n    // require that `subcell_options.min_tci_calls_after_rollback() >= 1`, so\n    // 1 TCI call means only one step was taken after a rollback.\n    if (only_need_rdmp_data or\n        db::get<evolution::dg::subcell::Tags::TciCallsSinceRollback>(box) <=\n            subcell_options.min_tci_calls_after_rollback()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const int tci_decision = std::get<0>(tci_result);\n    const bool cell_is_troubled =\n        tci_decision != 0 or (subcell_options.use_halo() and [&box]() -> bool {\n          for (const auto& [_, neighbor_decision] :\n               db::get<evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n                   box)) {\n            if (neighbor_decision != 0) {\n              return true;\n            }\n          }\n          return false;\n        }());\n\n    db::mutate<Tags::TciDecision>(\n        [&tci_decision](const gsl::not_null<int*> tci_decision_ptr) {\n          *tci_decision_ptr = tci_decision;\n        },\n        make_not_null(&box));\n\n    // If the cell is not troubled, then we _might_ be able to switch back to\n    // DG. This depends on the type of time stepper we are using:\n    // - ADER: Not yet implemented, but here the TCI history is irrelevant\n    //         because it is a one-step scheme, so we can always switch.\n    // - Multistep: the TCI must have marked the entire evolved variable history\n    //              as free of shocks. In practice for LMMs this means the TCI\n    //              history is as long as the evolved variables history and that\n    //              the entire TCI history is `ActiveGrid::Dg`.\n    // - Substep: the easiest is to restrict switching back to DG to step\n    //            boundaries where there is no history.\n    const auto& time_stepper = db::get<::Tags::TimeStepper<TimeStepper>>(box);\n    const bool is_substep_method = time_stepper.number_of_substeps() != 1;\n    const bool is_multistep_method = time_stepper.number_of_past_steps() != 0;\n    ASSERT(time_stepper.number_of_substeps() != 0,\n           \"Don't know how to handle a time stepper with zero substeps. This \"\n           \"might be totally fine, but the case should be thought about.\");\n    ASSERT(subcell_options.min_clear_tci_before_dg() > 0,\n           \"The value of 'subcell_options.min_clear_tci_before_dg()' must be \"\n           \"at least 1\");\n    if (const auto& tci_history = db::get<subcell::Tags::TciGridHistory>(box);\n        not cell_is_troubled and\n\n        (((is_substep_method and\n           tci_history.size() >= subcell_options.min_clear_tci_before_dg() - 1)\n\n          or\n\n          (is_multistep_method and\n           tci_history.size() ==\n               std::max(subcell_options.min_clear_tci_before_dg(),\n                        // Use `number_of_past_steps()` instead of\n                        // `number_of_past_steps()+number_of_substeps()`\n                        // since we only perform this check if we are on\n                        // substep 0 (i.e. starting a new step).\n                        time_stepper.number_of_past_steps() + 1)))\n\n         and alg::all_of(tci_history,\n                         [](const ActiveGrid tci_grid_decision) {\n                           return tci_grid_decision == ActiveGrid::Dg;\n                         })\n\n             )) {\n      db::mutate<\n          variables_tag, ::Tags::HistoryEvolvedVariables<variables_tag>,\n          Tags::ActiveGrid, subcell::Tags::GhostDataForReconstruction<Dim>,\n          evolution::dg::subcell::Tags::TciGridHistory,\n          evolution::dg::subcell::Tags::TciCallsSinceRollback,\n          evolution::dg::subcell::Tags::CellCenteredFlux<flux_variables, Dim>>(\n          [&dg_mesh, &subcell_mesh, &subcell_options](\n              const auto active_vars_ptr, const auto active_history_ptr,\n              const gsl::not_null<ActiveGrid*> active_grid_ptr,\n              const auto subcell_ghost_data_ptr,\n              const gsl::not_null<\n                  std::deque<evolution::dg::subcell::ActiveGrid>*>\n                  tci_grid_history_ptr,\n              const gsl::not_null<size_t*> tci_calls_since_rollback_ptr,\n              const auto subcell_cell_centered_fluxes) {\n            // Note: strictly speaking, to be conservative this should\n            // reconstruct uJ instead of u.\n            *active_vars_ptr = fd::reconstruct(\n                *active_vars_ptr, dg_mesh, subcell_mesh.extents(),\n                subcell_options.reconstruction_method());\n\n            // Reconstruct the DG solution for each time in the time stepper\n            // history\n            active_history_ptr->map_entries(\n                [&dg_mesh, &subcell_mesh, &subcell_options](const auto entry) {\n                  *entry =\n                      fd::reconstruct(*entry, dg_mesh, subcell_mesh.extents(),\n                                      subcell_options.reconstruction_method());\n                });\n            *active_grid_ptr = ActiveGrid::Dg;\n\n            // Clear the neighbor data needed for subcell reconstruction since\n            // we have now completed the time step.\n            subcell_ghost_data_ptr->clear();\n\n            // Clear the TCI grid history since we don't need to use it when on\n            // the DG grid.\n            tci_grid_history_ptr->clear();\n\n            // Reset tci_calls_since_rollback\n            *tci_calls_since_rollback_ptr = 0;\n\n            // Clear the allocation for the cell-centered fluxes.\n            *subcell_cell_centered_fluxes = std::nullopt;\n          },\n          make_not_null(&box));\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    // We record the TCI history for a couple reasons.\n    //\n    // For multistep methods we track the TCI decision because we need the\n    // discontinuity to clear the entire history before we can switch back to\n    // DG.\n    //\n    // For substep methods tracking the TCI history allows us to stay on FD a\n    // bit longer to allow the solution to smoothen a bit more before going\n    // back to DG.\n    db::mutate<evolution::dg::subcell::Tags::TciGridHistory>(\n        [cell_is_troubled, &subcell_options, &time_stepper](\n            const gsl::not_null<std::deque<evolution::dg::subcell::ActiveGrid>*>\n                tci_grid_history) {\n          tci_grid_history->push_front(cell_is_troubled ? ActiveGrid::Subcell\n                                                        : ActiveGrid::Dg);\n          if (tci_grid_history->size() >\n              std::max(subcell_options.min_clear_tci_before_dg() - 1,\n                       // Use `number_of_past_steps()` instead of\n                       // `number_of_past_steps()+number_of_substeps()`\n                       // since we only perform this check if we are on\n                       // substep 0 (i.e. starting a new step).\n                       time_stepper.number_of_past_steps() + 1)) {\n            tci_grid_history->pop_back();\n          }\n          ASSERT(tci_grid_history->size() <=\n                     std::max(subcell_options.min_clear_tci_before_dg() - 1,\n                              time_stepper.number_of_past_steps() + 1),\n                 \"The TCI history is not being correctly cleared. This is an \"\n                 \"internal bug. Please file an issue.\\n\"\n                 \"tci_grid_history->size(): \"\n                     << tci_grid_history->size() << \"\\nExpected: \"\n                     << std::max(subcell_options.min_clear_tci_before_dg() - 1,\n                                 time_stepper.number_of_past_steps() + 1));\n        },\n        make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace evolution::dg::subcell::Actions\n"
  },
  {
    "path": "src/Evolution/DgSubcell/ActiveGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n\n#include <ostream>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace evolution::dg::subcell {\nstd::ostream& operator<<(std::ostream& os, ActiveGrid active_grid) {\n  switch (active_grid) {\n    case ActiveGrid::Dg:\n      return os << \"Dg\";\n    case ActiveGrid::Subcell:\n      return os << \"Subcell\";\n    default:\n      ERROR(\"ActiveGrid must be either 'Dg' or 'Subcell' but has value \"\n            << static_cast<int>(active_grid));\n  }\n}\n}  // namespace evolution::dg::subcell\n\ntemplate <>\nevolution::dg::subcell::ActiveGrid\nOptions::create_from_yaml<evolution::dg::subcell::ActiveGrid>::create<void>(\n    const Options::Option& options) {\n  const auto active_grid = options.parse_as<std::string>();\n  if (active_grid == \"Dg\") {\n    return evolution::dg::subcell::ActiveGrid::Dg;\n  } else if (active_grid == \"Subcell\") {\n    return evolution::dg::subcell::ActiveGrid::Subcell;\n  }\n  PARSE_ERROR(options.context(), \"ActiveGrid must be 'Dg' or 'Subcell'.\");\n}\n"
  },
  {
    "path": "src/Evolution/DgSubcell/ActiveGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n/// \\cond\nnamespace Options {\nstruct Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/// \\ingroup DgSubcellGroup\n/// The grid that is currently being used for the DG-subcell evolution.\nenum class ActiveGrid { Dg, Subcell };\n\nstd::ostream& operator<<(std::ostream& os, ActiveGrid active_grid);\n}  // namespace evolution::dg::subcell\n\ntemplate <>\nstruct Options::create_from_yaml<evolution::dg::subcell::ActiveGrid> {\n  template <typename Metavariables>\n  static evolution::dg::subcell::ActiveGrid create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nevolution::dg::subcell::ActiveGrid\nOptions::create_from_yaml<evolution::dg::subcell::ActiveGrid>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Evolution/DgSubcell/BackgroundGrVars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\n\n/*!\n * \\brief Allocate or assign background general relativity quantities on\n * cell-centered and face-centered FD grid points, for evolution systems run on\n * a curved spacetime without solving Einstein equations (e.g. ValenciaDivclean,\n * ForceFree),\n *\n * \\warning This mutator assumes that the GR analytic data or solution\n * specifying background spacetime metric is time-independent.\n *\n */\ntemplate <typename System, typename Metavariables, bool ComputeOnlyOnRollback>\nstruct BackgroundGrVars : tt::ConformsTo<db::protocols::Mutator> {\n  static constexpr size_t volume_dim = System::volume_dim;\n\n  using gr_vars_tag = typename System::spacetime_variables_tag;\n  using inactive_gr_vars_tag =\n      evolution::dg::subcell::Tags::Inactive<gr_vars_tag>;\n  using subcell_faces_gr_tag = evolution::dg::subcell::Tags::OnSubcellFaces<\n      typename System::flux_spacetime_variables_tag, volume_dim>;\n\n  using GrVars = typename gr_vars_tag::type;\n  using InactiveGrVars = typename inactive_gr_vars_tag::type;\n  using SubcellFaceGrVars = typename subcell_faces_gr_tag::type;\n\n  using argument_tags = tmpl::list<\n      ::Tags::Time, domain::Tags::FunctionsOfTime,\n      domain::Tags::Domain<volume_dim>, domain::Tags::Element<volume_dim>,\n      domain::Tags::ElementMap<volume_dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<volume_dim, Frame::Grid,\n                                                  Frame::Inertial>,\n      evolution::dg::subcell::Tags::Mesh<volume_dim>,\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n      subcell::Tags::DidRollback, evolution::initial_data::Tags::InitialData>;\n\n  using return_tags =\n      tmpl::list<gr_vars_tag, inactive_gr_vars_tag, subcell_faces_gr_tag>;\n\n  template <typename T>\n  static void apply(\n      const gsl::not_null<GrVars*> active_gr_vars,\n      const gsl::not_null<InactiveGrVars*> inactive_gr_vars,\n      const gsl::not_null<SubcellFaceGrVars*> subcell_face_gr_vars,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const Domain<volume_dim>& domain, const Element<volume_dim>& element,\n      const ElementMap<volume_dim, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, volume_dim>&\n          grid_to_inertial_map,\n      const Mesh<volume_dim>& subcell_mesh,\n      const tnsr::I<DataVector, volume_dim, Frame::Inertial>&\n          subcell_inertial_coords,\n      const bool did_rollback, const T& solution_or_data) {\n    const size_t num_subcell_pts = subcell_mesh.number_of_grid_points();\n\n    if (gsl::at(*subcell_face_gr_vars, 0).number_of_grid_points() != 0) {\n      // Evolution phase\n\n      // Check if the mesh is actually moving i.e. block coordinate map is\n      // time-dependent. If not, we can skip the evaluation of GR variables\n      // since they may stay with their values assigned at the initialization\n      // phase.\n      const auto& element_id = element.id();\n      const size_t block_id = element_id.block_id();\n      const Block<volume_dim>& block = domain.blocks()[block_id];\n\n      if (block.is_time_dependent()) {\n        if (did_rollback or not ComputeOnlyOnRollback) {\n          if (did_rollback) {\n            // Right after rollback, subcell GR vars are stored in the\n            // `inactive` one.\n            ASSERT(inactive_gr_vars->number_of_grid_points() == num_subcell_pts,\n                   \"The size of subcell GR variables (\"\n                       << inactive_gr_vars->number_of_grid_points()\n                       << \") is not equal to the number of FD grid points (\"\n                       << subcell_mesh.number_of_grid_points() << \").\");\n\n            cell_centered_impl(inactive_gr_vars, time, subcell_inertial_coords,\n                               solution_or_data);\n\n          } else {\n            // In this case the element didn't rollback but started from FD.\n            // Therefore subcell GR vars are in the `active` one.\n            ASSERT(active_gr_vars->number_of_grid_points() == num_subcell_pts,\n                   \"The size of subcell GR variables (\"\n                       << active_gr_vars->number_of_grid_points()\n                       << \") is not equal to the number of FD grid points (\"\n                       << subcell_mesh.number_of_grid_points() << \").\");\n\n            cell_centered_impl(active_gr_vars, time, subcell_inertial_coords,\n                               solution_or_data);\n          }\n          if constexpr (not std::is_same_v<\n                        typename SubcellFaceGrVars::value_type::tags_list,\n                        tmpl::list<>>){\n            face_centered_impl(subcell_face_gr_vars, time, functions_of_time,\n                               logical_to_grid_map, grid_to_inertial_map,\n                               subcell_mesh, solution_or_data);\n          }\n        }\n      }\n\n    } else {\n      // Initialization phase\n      (*inactive_gr_vars).initialize(num_subcell_pts);\n\n      ASSERT(Mesh<volume_dim>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                              subcell_mesh.quadrature(0)) == subcell_mesh,\n             \"The subcell mesh must have isotropic basis, quadrature. and \"\n             \"extents but got \"\n                 << subcell_mesh);\n      if constexpr (not std::is_same_v<\n                    typename SubcellFaceGrVars::value_type::tags_list,\n                    tmpl::list<>>){\n        const size_t num_face_centered_mesh_grid_pts =\n            (subcell_mesh.extents(0) + 1) * subcell_mesh.extents(1) *\n            subcell_mesh.extents(2);\n        for (size_t d = 0; d < volume_dim; ++d) {\n          gsl::at(*subcell_face_gr_vars, d)\n              .initialize(num_face_centered_mesh_grid_pts);\n        }\n        face_centered_impl(subcell_face_gr_vars, time, functions_of_time,\n                           logical_to_grid_map, grid_to_inertial_map,\n                           subcell_mesh, solution_or_data);\n      }\n\n      cell_centered_impl(inactive_gr_vars, time, subcell_inertial_coords,\n                         solution_or_data);\n    }\n  }\n\n private:\n  template <typename Vars, typename T>\n  static void cell_centered_impl(\n      const gsl::not_null<Vars*> background_gr_vars, const double time,\n      const tnsr::I<DataVector, volume_dim, Frame::Inertial>& inertial_coords,\n      const T& solution_or_data) {\n    GrVars temp{background_gr_vars->data(), background_gr_vars->size()};\n\n    using derived_classes =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 evolution::initial_data::InitialData>;\n    call_with_dynamic_type<void, derived_classes>(\n        &solution_or_data, [&temp, &inertial_coords,\n                            &time](const auto* const solution_or_data_ptr) {\n          temp.assign_subset(evolution::Initialization::initial_data(\n              *solution_or_data_ptr, inertial_coords, time,\n              typename GrVars::tags_list{}));\n        });\n  }\n\n  template <typename T>\n  static void face_centered_impl(\n      const gsl::not_null<SubcellFaceGrVars*> face_centered_gr_vars,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const ElementMap<volume_dim, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, volume_dim>&\n          grid_to_inertial_map,\n      const Mesh<volume_dim>& subcell_mesh, const T& solution_or_data) {\n    ASSERT(Mesh<volume_dim>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                            subcell_mesh.quadrature(0)) == subcell_mesh,\n           \"The subcell mesh must have isotropic basis, quadrature. and \"\n           \"extents but got \"\n               << subcell_mesh);\n\n    for (size_t dim = 0; dim < volume_dim; ++dim) {\n      const auto basis = make_array<volume_dim>(subcell_mesh.basis(0));\n      auto quadrature = make_array<volume_dim>(subcell_mesh.quadrature(0));\n      auto extents = make_array<volume_dim>(subcell_mesh.extents(0));\n\n      gsl::at(extents, dim) = subcell_mesh.extents(0) + 1;\n      gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n\n      const Mesh<volume_dim> face_centered_mesh{extents, basis, quadrature};\n      const auto face_centered_logical_coords =\n          logical_coordinates(face_centered_mesh);\n      const auto face_centered_inertial_coords = grid_to_inertial_map(\n          logical_to_grid_map(face_centered_logical_coords), time,\n          functions_of_time);\n\n      using derived_classes =\n          tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                   evolution::initial_data::InitialData>;\n      call_with_dynamic_type<void, derived_classes>(\n          &solution_or_data,\n          [&face_centered_gr_vars, &face_centered_inertial_coords, &dim,\n           &time](const auto* const solution_or_data_ptr) {\n            gsl::at(*face_centered_gr_vars, dim)\n                .assign_subset(evolution::Initialization::initial_data(\n                    *solution_or_data_ptr, face_centered_inertial_coords, time,\n                    typename SubcellFaceGrVars::value_type::tags_list{}));\n          });\n    }\n  }\n};\n\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DgSubcell)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ActiveGrid.hpp\n  BackgroundGrVars.hpp\n  CartesianFluxDivergence.hpp\n  CellCenteredFlux.hpp\n  CombineVolumeGhostData.hpp\n  ComputeBoundaryTerms.hpp\n  CorrectPackagedData.hpp\n  DgSubcell.hpp\n  DisableLts.hpp\n  GetActiveTag.hpp\n  GetTciDecision.hpp\n  GhostData.hpp\n  GhostZoneInverseJacobian.hpp\n  GhostZoneLogicalCoordinates.hpp\n  InitialTciData.hpp\n  Matrices.hpp\n  Mesh.hpp\n  NeighborRdmpAndVolumeData.hpp\n  NeighborReconstructedFaceSolution.hpp\n  NeighborReconstructedFaceSolution.tpp\n  NeighborTciDecision.hpp\n  PerssonTci.hpp\n  PrepareNeighborData.hpp\n  Projection.hpp\n  RdmpTci.hpp\n  RdmpTciData.hpp\n  ReceiveSubcellDataForDg.hpp\n  Reconstruction.hpp\n  ReconstructionMethod.hpp\n  ReconstructionOrder.hpp\n  SetInterpolators.hpp\n  SliceData.hpp\n  SliceTensor.hpp\n  SliceVariable.hpp\n  SubcellOptions.hpp\n  TwoMeshRdmpTci.hpp\n  )\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ActiveGrid.cpp\n  CartesianFluxDivergence.cpp\n  CombineVolumeGhostData.cpp\n  DisableLts.cpp\n  GhostData.cpp\n  GhostZoneLogicalCoordinates.cpp\n  InitialTciData.cpp\n  Matrices.cpp\n  Mesh.cpp\n  NeighborRdmpAndVolumeData.cpp\n  NeighborTciDecision.cpp\n  PerssonTci.cpp\n  Projection.cpp\n  RdmpTci.cpp\n  RdmpTciData.cpp\n  ReceiveSubcellDataForDg.cpp\n  Reconstruction.cpp\n  ReconstructionMethod.cpp\n  SliceData.cpp\n  SubcellOptions.cpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  DomainCreators\n  Interpolation\n  Parallel\n  Spectral\n\n  INTERFACE\n  Boost::boost\n  Events\n  FunctionsOfTime\n  Time\n\n  PRIVATE\n  BLAS::BLAS\n  ErrorHandling\n  Serialization\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Tags)\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/Evolution/DgSubcell/CartesianFluxDivergence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid validate_input_sizes(const gsl::not_null<DataVector*> dt_var,\n                          const DataVector& det_inv_jacobian,\n                          const DataVector& boundary_correction,\n                          size_t num_volume_points, size_t num_face_points) {\n  ASSERT(dt_var->size() == num_volume_points,\n         \"dt_var size \" << dt_var->size()\n                        << \" does not match expected volume points \"\n                        << num_volume_points);\n  ASSERT(det_inv_jacobian.size() == num_volume_points,\n         \"det_inv_jacobian size \" << det_inv_jacobian.size()\n                                  << \" does not match expected volume points \"\n                                  << num_volume_points);\n  ASSERT(boundary_correction.size() == num_face_points,\n         \"boundary_correction size \" << boundary_correction.size()\n                                     << \" does not match expected face points \"\n                                     << num_face_points);\n}\n}  // namespace\n\nnamespace evolution::dg::subcell {\nvoid add_cartesian_flux_divergence(const gsl::not_null<DataVector*> dt_var,\n                                   const double one_over_delta,\n                                   const DataVector& det_inv_jacobian,\n                                   const DataVector& boundary_correction,\n                                   const Index<1>& subcell_extents,\n                                   const size_t dimension) {\n  (void)dimension;\n  ASSERT(dimension == 0, \"dimension must be 0 but is \" << dimension);\n\n  const size_t num_volume_points = subcell_extents.product();\n  const size_t num_face_points = subcell_extents[0] + 1;\n  validate_input_sizes(dt_var, det_inv_jacobian, boundary_correction,\n                       num_volume_points, num_face_points);\n\n  for (size_t i = 0; i < subcell_extents[0]; ++i) {\n    (*dt_var)[i] += one_over_delta * det_inv_jacobian[i] *\n                    (boundary_correction[i + 1] - boundary_correction[i]);\n  }\n}\n\nvoid add_cartesian_flux_divergence(const gsl::not_null<DataVector*> dt_var,\n                                   const double one_over_delta,\n                                   const DataVector& det_inv_jacobian,\n                                   const DataVector& boundary_correction,\n                                   const Index<2>& subcell_extents,\n                                   const size_t dimension) {\n  ASSERT(dimension == 0 or dimension == 1,\n         \"dimension must be 0 or 1 but is \" << dimension);\n  Index<2> subcell_face_extents = subcell_extents;\n  ++subcell_face_extents[dimension];\n\n  const size_t num_volume_points = subcell_extents.product();\n  const size_t num_face_points = subcell_face_extents.product();\n  validate_input_sizes(dt_var, det_inv_jacobian, boundary_correction,\n                       num_volume_points, num_face_points);\n\n  for (size_t j = 0; j < subcell_extents[1]; ++j) {\n    for (size_t i = 0; i < subcell_extents[0]; ++i) {\n      Index<2> index(i, j);\n      const size_t volume_index = collapsed_index(index, subcell_extents);\n      const size_t boundary_correction_lower_index =\n          collapsed_index(index, subcell_face_extents);\n      ++index[dimension];\n      const size_t boundary_correction_upper_index =\n          collapsed_index(index, subcell_face_extents);\n      (*dt_var)[volume_index] +=\n          one_over_delta * det_inv_jacobian[volume_index] *\n          (boundary_correction[boundary_correction_upper_index] -\n           boundary_correction[boundary_correction_lower_index]);\n    }\n  }\n}\n\nvoid add_cartesian_flux_divergence(const gsl::not_null<DataVector*> dt_var,\n                                   const double one_over_delta,\n                                   const DataVector& det_inv_jacobian,\n                                   const DataVector& boundary_correction,\n                                   const Index<3>& subcell_extents,\n                                   const size_t dimension) {\n  ASSERT(dimension == 0 or dimension == 1 or dimension == 2,\n         \"dimension must be 0, 1, or 2 but is \" << dimension);\n  Index<3> subcell_face_extents = subcell_extents;\n  ++subcell_face_extents[dimension];\n\n  const size_t num_volume_points = subcell_extents.product();\n  const size_t num_face_points = subcell_face_extents.product();\n  validate_input_sizes(dt_var, det_inv_jacobian, boundary_correction,\n                       num_volume_points, num_face_points);\n\n  for (size_t k = 0; k < subcell_extents[2]; ++k) {\n    for (size_t j = 0; j < subcell_extents[1]; ++j) {\n      for (size_t i = 0; i < subcell_extents[0]; ++i) {\n        Index<3> index(i, j, k);\n        const size_t volume_index = collapsed_index(index, subcell_extents);\n        const size_t boundary_correction_lower_index =\n            collapsed_index(index, subcell_face_extents);\n        ++index[dimension];\n        const size_t boundary_correction_upper_index =\n            collapsed_index(index, subcell_face_extents);\n        (*dt_var)[volume_index] +=\n            one_over_delta * det_inv_jacobian[volume_index] *\n            (boundary_correction[boundary_correction_upper_index] -\n             boundary_correction[boundary_correction_lower_index]);\n      }\n    }\n  }\n}\n\nvoid add_cartoon_cartesian_flux_divergence(\n    const gsl::not_null<DataVector*> dt_var, const double one_over_delta,\n    const DataVector& det_inv_jacobian, const DataVector& boundary_correction,\n    const Index<3>& subcell_extents, const size_t dimension,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n        grid_to_inertial_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  // Validate that this is only used with cartoon bases\n  ASSERT(subcell_extents[2] == 1,\n         \"Expecting extent = 1 in third dimension, but got extents = \"\n             << subcell_extents);\n  ASSERT(subcell_extents[0] != 1,\n         \"Expecting extent != 1 in first dimension, but got extents = \"\n             << subcell_extents);\n\n  Index<3> subcell_face_extents = subcell_extents;\n  ++subcell_face_extents[dimension];\n\n  const size_t num_volume_points = subcell_extents.product();\n  const size_t num_face_points = subcell_face_extents.product();\n  validate_input_sizes(dt_var, det_inv_jacobian, boundary_correction,\n                       num_volume_points, num_face_points);\n  ASSERT(get<0>(inertial_coords).size() == num_volume_points,\n         \"inertial_coords size \" << get<0>(inertial_coords).size()\n                                 << \" does not match expected volume points \"\n                                 << num_volume_points);\n\n  const bool spherical = subcell_extents[1] == 1;\n\n  if (spherical) {\n    // 2nd and 3rd dimension handled by cartoon\n    ASSERT(dimension == 0,\n           \"Using cartoon derivatives with spherical symmetry, expecting \"\n           \"dimension = 0, got dimension = \"\n               << dimension);\n  } else {\n    // 3rd dimension handled by cartoon\n    ASSERT(dimension == 0 or dimension == 1,\n           \"Using cartoon derivatives with axial symmetry, expecting \"\n           \"dimension = 0 or 1, got dimension = \"\n               << dimension);\n  }\n\n  if (UNLIKELY(\n          equal_within_roundoff(0.0, get<0>(inertial_coords)[0],\n                                std::numeric_limits<double>::epsilon() * 100.0,\n                                max(get<0>(inertial_coords))))) {\n    ERROR(\n        \"Element contains x=0 for subcell; this should not happen for cartoon \"\n        \"FD grids\");\n  }\n\n  Mesh<3> face_centered_mesh;\n  if (spherical) {\n    face_centered_mesh = Mesh<3>{\n        {subcell_extents[0] + 1, subcell_extents[1], subcell_extents[2]},\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::Cartoon,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::FaceCentered,\n         Spectral::Quadrature::SphericalSymmetry,\n         Spectral::Quadrature::SphericalSymmetry}};\n  } else {\n    face_centered_mesh = Mesh<3>{\n        {subcell_extents[0] + (dimension == 0 ? 1 : 0),\n         subcell_extents[1] + (dimension == 1 ? 1 : 0), subcell_extents[2]},\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::FiniteDifference,\n         Spectral::Basis::Cartoon},\n        {dimension == 0 ? Spectral::Quadrature::FaceCentered\n                        : Spectral::Quadrature::CellCentered,\n         dimension == 1 ? Spectral::Quadrature::FaceCentered\n                        : Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::AxialSymmetry}};\n  }\n\n  const auto face_centered_logical_coords =\n      logical_coordinates(face_centered_mesh);\n  const auto face_centered_inertial_coords =\n      grid_to_inertial_map(logical_to_grid_map(face_centered_logical_coords),\n                           time, functions_of_time);\n\n  for (size_t k = 0; k < subcell_extents[2]; ++k) {\n    for (size_t j = 0; j < subcell_extents[1]; ++j) {\n      for (size_t i = 0; i < subcell_extents[0]; ++i) {\n        Index<3> index(i, j, k);\n        const size_t volume_index = collapsed_index(index, subcell_extents);\n        const size_t boundary_correction_lower_index =\n            collapsed_index(index, subcell_face_extents);\n        ++index[dimension];\n        const size_t boundary_correction_upper_index =\n            collapsed_index(index, subcell_face_extents);\n\n        double lower_face_weight{};\n        double upper_face_weight{};\n        if (spherical) {\n          lower_face_weight = square(get<0>(face_centered_inertial_coords)\n                                         [boundary_correction_lower_index]) /\n                              square(get<0>(inertial_coords)[volume_index]);\n\n          upper_face_weight = square(get<0>(face_centered_inertial_coords)\n                                         [boundary_correction_upper_index]) /\n                              square(get<0>(inertial_coords)[volume_index]);\n        } else {\n          lower_face_weight = get<0>(face_centered_inertial_coords)\n                                  [boundary_correction_lower_index] /\n                              get<0>(inertial_coords)[volume_index];\n          upper_face_weight = get<0>(face_centered_inertial_coords)\n                                  [boundary_correction_upper_index] /\n                              get<0>(inertial_coords)[volume_index];\n        }\n        (*dt_var)[volume_index] +=\n            one_over_delta * det_inv_jacobian[volume_index] *\n            (upper_face_weight *\n                 boundary_correction[boundary_correction_upper_index] -\n             lower_face_weight *\n                 boundary_correction[boundary_correction_lower_index]);\n      }\n    }\n  }\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/CartesianFluxDivergence.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Index;\ntemplate <size_t Dim, typename Frame>\nclass ElementMap;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace Frame {\nstruct Inertial;\nstruct Grid;\n}  // namespace Frame\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim>\nclass CoordinateMapBase;\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\n}  // namespace domain\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/// @{\n/*!\n * \\brief Compute and add the 2nd-order flux divergence on a Cartesian mesh to\n * the cell-centered time derivatives.\n */\nvoid add_cartesian_flux_divergence(gsl::not_null<DataVector*> dt_var,\n                                   double one_over_delta,\n                                   const DataVector& det_inv_jacobian,\n                                   const DataVector& boundary_correction,\n                                   const Index<1>& subcell_extents,\n                                   size_t dimension);\n\nvoid add_cartesian_flux_divergence(gsl::not_null<DataVector*> dt_var,\n                                   double one_over_delta,\n                                   const DataVector& det_inv_jacobian,\n                                   const DataVector& boundary_correction,\n                                   const Index<2>& subcell_extents,\n                                   size_t dimension);\n\nvoid add_cartesian_flux_divergence(gsl::not_null<DataVector*> dt_var,\n                                   double one_over_delta,\n                                   const DataVector& det_inv_jacobian,\n                                   const DataVector& boundary_correction,\n                                   const Index<3>& subcell_extents,\n                                   size_t dimension);\n/// @}\n\n/*!\n * \\brief Compute and add the 2nd-order flux divergence on a Cartesian mesh to\n * the cell-centered time derivatives when some of the bases are Cartoon.\n *\n * \\details Symmetries of your spacetime, used in the cartoon method, allow\n * the replacement of derivatives perpendicular to the computational domain by\n * scaled components of that same tensor. Specifically, for a spherically\n * symmetric system, this allows\n * \\f[\n * \\partial_t u + \\partial_x F^x + \\partial_y F^y + \\partial_z F^z \\Rightarrow\n * \\partial_t u + \\frac{1}{x^2} \\partial_x \\left( x^2 F^x \\right),\n * \\f]\n * and for an axially symmetric system,\n * \\f[\n * \\partial_t u + \\partial_x F^x + \\partial_y F^y + \\partial_z F^z \\Rightarrow\n * \\partial_t u + \\frac{1}{x} \\partial_x \\left( x F^x \\right) + \\partial_y F^y.\n * \\f]\n *\n * \\note This function will assume your basis is Cartoon by `subcell_extents ==\n * 1` in that dimension (only allowed in the second or third dimension).\n */\nvoid add_cartoon_cartesian_flux_divergence(\n    gsl::not_null<DataVector*> dt_var, double one_over_delta,\n    const DataVector& det_inv_jacobian, const DataVector& boundary_correction,\n    const Index<3>& subcell_extents, size_t dimension,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n    const ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n        grid_to_inertial_map,\n    double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/CellCenteredFlux.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell::fd {\n/*!\n * \\brief Mutator that wraps the system's `FluxMutator` to correctly set the\n * cell-centered fluxes on the subcell grid.\n *\n * Currently we only use high-order FD if the FD order was specified in the\n * input file. We will need to extend this to support adaptive-order in the\n * future. In that case we need to check if the FD reconstruction reports back\n * the order to use.\n */\ntemplate <typename System, typename FluxMutator, size_t Dim,\n          bool ComputeOnlyOnRollback, typename Fr = Frame::Inertial>\nstruct CellCenteredFlux {\n  using flux_variables = typename System::flux_variables;\n  using variables = typename System::variables_tag::tags_list;\n\n  using return_tags =\n      tmpl::list<subcell::Tags::CellCenteredFlux<flux_variables, Dim>>;\n  using argument_tags = tmpl::push_front<\n      typename FluxMutator::argument_tags, subcell::Tags::SubcellOptions<Dim>,\n      subcell::Tags::Mesh<Dim>, domain::Tags::Mesh<Dim>,\n      domain::Tags::MeshVelocity<Dim, Frame::Inertial>,\n      ::Tags::Variables<variables>, subcell::Tags::DidRollback>;\n\n  template <typename... FluxTags, typename... Args>\n  static void apply(\n      const gsl::not_null<std::optional<Variables<tmpl::list<FluxTags...>>>*>\n          cell_centered_fluxes,\n      const subcell::SubcellOptions& subcell_options,\n      const Mesh<Dim>& subcell_mesh, const Mesh<Dim>& dg_mesh,\n      const std::optional<tnsr::I<DataVector, Dim>>& dg_mesh_velocity,\n      const ::Variables<variables>& cell_centered_flux_vars,\n      const bool did_rollback, Args&&... args) {\n    if (did_rollback or not ComputeOnlyOnRollback) {\n      if (subcell_options.finite_difference_derivative_order() !=\n          ::fd::DerivativeOrder::Two) {\n        if (not cell_centered_fluxes->has_value()) {\n          (*cell_centered_fluxes) =\n              Variables<db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                         tmpl::size_t<Dim>, Fr>>{\n                  subcell_mesh.number_of_grid_points()};\n        }\n        FluxMutator::apply(\n            make_not_null(&get<FluxTags>((*cell_centered_fluxes).value()))...,\n            std::forward<Args>(args)...);\n        if (dg_mesh_velocity.has_value()) {\n          for (size_t i = 0; i < Dim; i++) {\n            //\n            // Project mesh velocity on face mesh. We only need the component\n            // orthogonal to the face.\n            const DataVector& cell_centered_mesh_velocity =\n                evolution::dg::subcell::fd::project(\n                    dg_mesh_velocity.value().get(i), dg_mesh,\n                    subcell_mesh.extents());\n\n            tmpl::for_each<flux_variables>(\n                [&cell_centered_flux_vars, &cell_centered_mesh_velocity,\n                 &cell_centered_fluxes, &i](auto tag_v) {\n                  using tag = tmpl::type_from<decltype(tag_v)>;\n                  using flux_tag =\n                      ::Tags::Flux<tag, tmpl::size_t<Dim>, Frame::Inertial>;\n                  using FluxTensor = typename flux_tag::type;\n                  const auto& var = get<tag>(cell_centered_flux_vars);\n                  auto& flux = get<flux_tag>(cell_centered_fluxes->value());\n                  for (size_t storage_index = 0; storage_index < var.size();\n                       ++storage_index) {\n                    const auto tensor_index =\n                        var.get_tensor_index(storage_index);\n                    const auto flux_storage_index =\n                        FluxTensor::get_storage_index(prepend(tensor_index, i));\n                    flux[flux_storage_index] -=\n                        cell_centered_mesh_velocity * var[storage_index];\n                  }\n                });\n          }\n        }\n      }\n    }\n  }\n};\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/CombineVolumeGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/CombineVolumeGhostData.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace {\ntemplate <size_t Dim>\nvoid fill_combined_data(const gsl::not_null<DataVector*> extended_data,\n                        const DataVector& volume_data,\n                        const DataVector& ghost_data, const size_t ext_offset,\n                        const size_t vol_offset, const size_t ghost_offset,\n                        const Index<Dim>& ext_extents,\n                        const Index<Dim>& vol_extents,\n                        const Index<Dim>& ghost_extents,\n                        const Direction<Dim>& direction_to_extend) {\n  const bool is_lower_side = direction_to_extend.side() == Side::Lower;\n  const size_t extended_dim = direction_to_extend.dimension();\n\n  const size_t num_vol_pts = vol_extents.product();\n  const size_t num_ghost_pts = ghost_extents.product();\n\n  for (size_t i_vol = 0; i_vol < num_vol_pts; ++i_vol) {\n    Index<Dim> multi_idx = expanded_index(i_vol, vol_extents);\n    if (is_lower_side) {\n      multi_idx[extended_dim] += ghost_extents[extended_dim];\n    }\n    const size_t combined_idx = collapsed_index(multi_idx, ext_extents);\n    (*extended_data)[combined_idx + ext_offset] =\n        volume_data[i_vol + vol_offset];\n  }\n\n  for (size_t i_ghost = 0; i_ghost < num_ghost_pts; ++i_ghost) {\n    Index<Dim> multi_idx = expanded_index(i_ghost, ghost_extents);\n    if (!is_lower_side) {\n      multi_idx[extended_dim] += vol_extents[extended_dim];\n    }\n    const size_t combined_idx = collapsed_index(multi_idx, ext_extents);\n    (*extended_data)[combined_idx + ext_offset] =\n        ghost_data[i_ghost + ghost_offset];\n  }\n}\n\n}  // namespace\ntemplate <size_t Dim>\nDataVector combine_volume_ghost_data(\n    const DataVector& volume_data, const DataVector& ghost_data,\n    const Index<Dim>& subcell_extents, const size_t ghost_zone_size,\n    const Direction<Dim>& direction_to_extend) {\n  const size_t num_vol_pts = subcell_extents.product();\n  const size_t num_components = volume_data.size() / num_vol_pts;\n  const size_t extended_dim = direction_to_extend.dimension();\n\n  Index<Dim> ghost_extents = subcell_extents;\n  ghost_extents[extended_dim] = ghost_zone_size;\n\n  Index<Dim> ext_extents = subcell_extents;\n  ext_extents[extended_dim] += ghost_zone_size;\n\n  const size_t num_ghost_pts = ghost_extents.product();\n  const size_t num_ext_pts = ext_extents.product();\n\n  ASSERT(volume_data.size() % num_vol_pts == 0,\n         \"volume_data size (\" << volume_data.size()\n                              << \") not divisible by number of volume points (\"\n                              << num_vol_pts << \").\");\n\n  ASSERT(ghost_data.size() == num_components * num_ghost_pts,\n         \"Expected ghost_data size to be \" << num_components * num_ghost_pts\n                                           << \" but got \" << ghost_data.size()\n                                           << \".\");\n\n  DataVector combined_data{num_ext_pts * num_components};\n\n  for (size_t comp = 0; comp < num_components; ++comp) {\n    // component offsets for volume, ghost, and extended data.\n    const size_t vol_offset = comp * num_vol_pts;\n    const size_t ghost_offset = comp * num_ghost_pts;\n    const size_t ext_offset = comp * num_ext_pts;\n\n    fill_combined_data(&combined_data, volume_data, ghost_data, ext_offset,\n                       vol_offset, ghost_offset, ext_extents, subcell_extents,\n                       ghost_extents, direction_to_extend);\n  }\n\n  return combined_data;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template DataVector combine_volume_ghost_data(                     \\\n      const DataVector&, const DataVector&, const Index<DIM(data)>&, \\\n      const size_t, const Direction<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/CombineVolumeGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\n/*!\n * \\brief Combine the volume subcell variables and the ghost variables\n * contained as DataVector into a single combined DataVector.\n */\n\nnamespace evolution::dg::subcell {\ntemplate <size_t Dim>\nDataVector combine_volume_ghost_data(const DataVector& volume_data,\n                                     const DataVector& ghost_data,\n                                     const Index<Dim>& subcell_extents,\n                                     size_t ghost_zone_size,\n                                     const Direction<Dim>& direction_to_extend);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/ComputeBoundaryTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\ntemplate <typename... CorrectionTags, typename BoundaryCorrection,\n          typename... PackageFieldTags, typename... BoundaryTermsVolumeTags>\nvoid compute_boundary_terms(\n    const gsl::not_null<Variables<tmpl::list<CorrectionTags...>>*>\n        boundary_corrections_on_face,\n    const BoundaryCorrection& boundary_correction,\n    const Variables<tmpl::list<PackageFieldTags...>>& upper_packaged_data,\n    const Variables<tmpl::list<PackageFieldTags...>>& lower_packaged_data,\n    const db::Access& box, tmpl::list<BoundaryTermsVolumeTags...> /*meta*/) {\n  ASSERT(\n      upper_packaged_data.number_of_grid_points() ==\n          lower_packaged_data.number_of_grid_points(),\n      \"The number of grid points must be the same for the upper packaged data (\"\n          << upper_packaged_data.number_of_grid_points()\n          << \") and the lower packaged data (\"\n          << lower_packaged_data.number_of_grid_points() << ')');\n  ASSERT(upper_packaged_data.number_of_grid_points() ==\n             boundary_corrections_on_face->number_of_grid_points(),\n         \"The number of grid points must be the same for the packaged data (\"\n             << upper_packaged_data.number_of_grid_points()\n             << \") and the boundary corrections on the faces (\"\n             << boundary_corrections_on_face->number_of_grid_points() << ')');\n  boundary_correction.dg_boundary_terms(\n      make_not_null(&get<CorrectionTags>(*boundary_corrections_on_face))...,\n      get<PackageFieldTags>(upper_packaged_data)...,\n      get<PackageFieldTags>(lower_packaged_data)...,\n      // FD schemes are basically weak form FV scheme\n      ::dg::Formulation::WeakInertial,\n      db::get<BoundaryTermsVolumeTags>(box)...);\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/CorrectPackagedData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Project the DG package data to the subcells. Data received from a\n * neighboring element doing DG is always projected, while the data we sent to\n * our neighbors before doing a rollback from DG to subcell is only projected if\n * `OverwriteInternalMortarData` is `true`.\n *\n * In order for the hybrid DG-FD/FV scheme to be conservative between elements\n * using DG and elements using subcell, the boundary terms must be the same on\n * both elements. In practice this means the boundary corrections \\f$G+D\\f$ must\n * be computed on the same grid. Consider the element doing subcell which\n * receives data from an element doing DG. In this case the DG element's\n * ingredients going into \\f$G+D\\f$ are projected to the subcells and then\n * \\f$G+D\\f$ are computed on the subcells. Similarly, for strict conservation\n * the element doing DG must first project the data it sent to the neighbor to\n * the subcells, then compute \\f$G+D\\f$ on the subcells, and finally reconstrct\n * \\f$G+D\\f$ back to the DG grid before lifting \\f$G+D\\f$ to the volume.\n *\n * This function updates the `packaged_data` (ingredients into \\f$G+D\\f$)\n * received by an element doing subcell by projecting the neighbor's DG data\n * onto the subcells. Note that this is only half of what is required for strict\n * conservation, the DG element must also compute \\f$G+D\\f$ on the subcells.\n * Note that we currently do not perform the other half of the correction\n * needed to be strictly conservative.\n *\n * If we are retaking a time step after the DG step failed then maintaining\n * conservation requires additional care. If `OverwriteInternalMortarData` is\n * `true` then the local (the element switching from DG to subcell) ingredients\n * into \\f$G+D\\f$ are projected and overwrite the data computed from\n * the FD reconstruction to the interface. However, even this is insufficient to\n * guarantee conservation. To guarantee conservation (which we do not currently\n * do) the correction \\f$G+D\\f$ must be computed on the DG grid and then\n * projected to the subcells.\n *\n * Note that our practical experience shows that since the DG-subcell hybrid\n * scheme switches to the subcell solver _before_ the local solution contains\n * discontinuities, strict conservation is not necessary between DG and FD/FV\n * regions. This was also observed with a block-adaptive finite difference AMR\n * code \\cite CHEN2016604\n *\n * The variable `variables_to_offset_in_dg_grid` is used in cases like the\n * combined generalized harmonic and GRMHD system where the DG grid uses\n * boundary corrections for both GH and GRMHD, but the subcell grid only does\n * GRMHD.\n */\ntemplate <bool OverwriteInternalMortarData, size_t Dim,\n          typename DgPackageFieldTags>\nvoid correct_package_data(\n    const gsl::not_null<Variables<DgPackageFieldTags>*> lower_packaged_data,\n    const gsl::not_null<Variables<DgPackageFieldTags>*> upper_packaged_data,\n    const size_t logical_dimension_to_operate_in, const Element<Dim>& element,\n    const Mesh<Dim>& subcell_volume_mesh,\n    const DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>>&\n        mortar_data,\n    const size_t variables_to_offset_in_dg_grid) {\n  const Direction<Dim> upper_direction{logical_dimension_to_operate_in,\n                                       Side::Upper};\n  const Direction<Dim> lower_direction{logical_dimension_to_operate_in,\n                                       Side::Lower};\n  const bool has_upper_neighbor = element.neighbors().contains(upper_direction);\n  const bool has_lower_neighbor = element.neighbors().contains(lower_direction);\n  const DirectionalId<Dim> upper_mortar_id =\n      has_upper_neighbor\n          ? DirectionalId<Dim>{upper_direction,\n                               *element.neighbors().at(upper_direction).begin()}\n          : DirectionalId<Dim>{};\n  const DirectionalId<Dim> lower_mortar_id =\n      has_lower_neighbor\n          ? DirectionalId<Dim>{lower_direction,\n                               *element.neighbors().at(lower_direction).begin()}\n          : DirectionalId<Dim>{};\n\n  Index<Dim> subcell_extents_with_faces = subcell_volume_mesh.extents();\n  ++subcell_extents_with_faces[logical_dimension_to_operate_in];\n  const Mesh<Dim - 1>& subcell_face_mesh =\n      subcell_volume_mesh.slice_away(logical_dimension_to_operate_in);\n\n  const auto project_dg_data_to_subcells =\n      [logical_dimension_to_operate_in, &subcell_extents_with_faces,\n       &subcell_face_mesh, variables_to_offset_in_dg_grid](\n          const gsl::not_null<Variables<DgPackageFieldTags>*>\n              subcell_packaged_data,\n          const size_t subcell_index, const Mesh<Dim - 1>& neighbor_face_mesh,\n          const DataVector& neighbor_data) {\n        const size_t dg_variables_offset_size =\n            variables_to_offset_in_dg_grid *\n            neighbor_face_mesh.number_of_grid_points();\n        const double* slice_data =\n            // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n            neighbor_data.data() + dg_variables_offset_size;\n        // Warning: projected_data can't be inside the `if constexpr` since that\n        // would lead to a dangling pointer.\n        DataVector projected_data{};\n        if constexpr (Dim > 1) {\n          projected_data = DataVector{\n              Variables<DgPackageFieldTags>::number_of_independent_components *\n              subcell_face_mesh.number_of_grid_points()};\n          evolution::dg::subcell::fd::detail::project_impl(\n              gsl::make_span(projected_data.data(), projected_data.size()),\n              gsl::make_span(\n                  slice_data,\n                  Variables<\n                      DgPackageFieldTags>::number_of_independent_components *\n                      neighbor_face_mesh.number_of_grid_points()),\n              neighbor_face_mesh, subcell_face_mesh.extents());\n          slice_data = projected_data.data();\n        } else {\n          (void)subcell_face_mesh;\n          (void)projected_data;\n        }\n        const size_t volume_grid_points = subcell_extents_with_faces.product();\n        const size_t slice_grid_points =\n            subcell_extents_with_faces\n                .slice_away(logical_dimension_to_operate_in)\n                .product();\n        double* const volume_data = subcell_packaged_data->data();\n        for (SliceIterator si(subcell_extents_with_faces,\n                              logical_dimension_to_operate_in, subcell_index);\n             si; ++si) {\n          for (size_t i = 0;\n               i <\n               Variables<DgPackageFieldTags>::number_of_independent_components;\n               ++i) {\n            // clang-tidy: do not use pointer arithmetic\n            volume_data[si.volume_offset() +\n                        i * volume_grid_points] =  // NOLINT\n                slice_data[si.slice_offset() +\n                           i * slice_grid_points];  // NOLINT\n          }\n        }\n      };\n\n  // Project DG data to the subcells\n  if (auto neighbor_mortar_data_it = mortar_data.find(upper_mortar_id);\n      has_upper_neighbor and neighbor_mortar_data_it != mortar_data.end()) {\n    if (neighbor_mortar_data_it->second.neighbor().mortar_data.has_value()) {\n      project_dg_data_to_subcells(\n          upper_packaged_data,\n          subcell_extents_with_faces[logical_dimension_to_operate_in] - 1,\n          neighbor_mortar_data_it->second.neighbor().face_mesh.value(),\n          neighbor_mortar_data_it->second.neighbor().mortar_data.value());\n    }\n    if constexpr (OverwriteInternalMortarData) {\n      if (neighbor_mortar_data_it->second.local().mortar_data.has_value()) {\n        project_dg_data_to_subcells(\n            lower_packaged_data,\n            subcell_extents_with_faces[logical_dimension_to_operate_in] - 1,\n            neighbor_mortar_data_it->second.local().face_mesh.value(),\n            neighbor_mortar_data_it->second.local().mortar_data.value());\n      }\n    }\n  }\n  if (auto neighbor_mortar_data_it = mortar_data.find(lower_mortar_id);\n      has_lower_neighbor and neighbor_mortar_data_it != mortar_data.end()) {\n    if (neighbor_mortar_data_it->second.neighbor().mortar_data.has_value()) {\n      project_dg_data_to_subcells(\n          lower_packaged_data, 0,\n          neighbor_mortar_data_it->second.neighbor().face_mesh.value(),\n          neighbor_mortar_data_it->second.neighbor().mortar_data.value());\n    }\n    if constexpr (OverwriteInternalMortarData) {\n      if (neighbor_mortar_data_it->second.local().mortar_data.has_value()) {\n        project_dg_data_to_subcells(\n            upper_packaged_data, 0,\n            neighbor_mortar_data_it->second.local().face_mesh.value(),\n            neighbor_mortar_data_it->second.local().mortar_data.value());\n      }\n    }\n  }\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/DgSubcell.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace evolution::dg {\n/*!\n * \\ingroup DgSubcellGroup\n * \\brief Implementation of a generic finite volume/conservative finite\n * difference subcell limiter\n *\n * Our implementation of a finite volume (FV) or finite difference (FD) subcell\n * limiter (SCL) follows \\cite Dumbser2014a. Other implementations of a subcell\n * limiter exist, e.g. \\cite Sonntag2014 \\cite Casoni2012 \\cite Hou2007. Our\n * implementation and that of \\cite Dumbser2014a are a generalization of the\n * Multidimensional Optimal Order Detection (MOOD) algorithm \\cite CLAIN20114028\n * \\cite DIOT201243 \\cite Diot2013 \\cite Loubere2014.\n */\nnamespace subcell {\n/*!\n * \\ingroup DgSubcellGroup\n * \\brief Code specific to a finite volume subcell limiter\n */\nnamespace fv {}\n/*!\n * \\ingroup DgSubcellGroup\n * \\brief Code specific to a conservative finite difference subcell limiter\n */\nnamespace fd {}\n}  // namespace subcell\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DgSubcell/DisableLts.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/DisableLts.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\ntemplate <size_t Dim>\nvoid DisableLts<Dim>::apply(\n    const gsl::not_null<std::optional<size_t>*> fixed_lts_ratio,\n    const gsl::not_null<\n        DirectionalIdMap<Dim, ::evolution::dg::MortarInfo<Dim>>*>\n        mortar_infos,\n    const Element<Dim>& element, const SubcellOptions& subcell_options) {\n  if (not alg::found(subcell_options.only_dg_block_ids(),\n                     element.id().block_id())) {\n    fixed_lts_ratio->emplace(subcell_options.lts_steps_per_slab());\n    for (auto& [mortar_id, info] : *mortar_infos) {\n      if (not alg::found(subcell_options.only_dg_block_ids(),\n                         mortar_id.id().block_id())) {\n        info.time_stepping_policy() = TimeSteppingPolicy::EqualRate;\n      }\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) template struct DisableLts<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/DisableLts.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Time/Tags/FixedLtsRatio.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\ntemplate <size_t VolumeDim>\nclass Element;\nnamespace domain::Tags {\ntemplate <size_t VolumeDim>\nstruct Element;\n}  // namespace domain::Tags\nnamespace evolution::dg {\ntemplate <size_t VolumeDim>\nclass MortarInfo;\n}  // namespace evolution::dg\nnamespace evolution::dg::Tags {\ntemplate <size_t Dim>\nstruct MortarInfo;\n}  // namespace evolution::dg::Tags\nnamespace evolution::dg::subcell {\nclass SubcellOptions;\n}  // namespace evolution::dg::subcell\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Initialization mutator that disables local time-stepping in\n * subcell regions for a mixed subcell-LTS evolution.\n *\n * Sets `Tags::FixedLtsRatio` for all elements not specified in\n * `OnlyDgBlocksAndGroups`, and sets all boundaries between such\n * elements to `TimeSteppingPolicy::EqualRate`.\n */\ntemplate <size_t Dim>\nstruct DisableLts {\n  using const_global_cache_tags = tmpl::list<Tags::SubcellOptions<Dim>>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using simple_tags_from_options = tmpl::list<>;\n  using simple_tags = tmpl::list<::Tags::FixedLtsRatio>;\n  using compute_tags = tmpl::list<>;\n\n  using return_tags =\n      tmpl::list<::Tags::FixedLtsRatio, evolution::dg::Tags::MortarInfo<Dim>>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Element<Dim>, Tags::SubcellOptions<Dim>>;\n\n  static void apply(\n      gsl::not_null<std::optional<size_t>*> fixed_lts_ratio,\n      gsl::not_null<DirectionalIdMap<Dim, ::evolution::dg::MortarInfo<Dim>>*>\n          mortar_infos,\n      const Element<Dim>& element, const SubcellOptions& subcell_options);\n};\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/GetActiveTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n\nnamespace evolution::dg::subcell {\n/// \\brief Retrieve a tag from the active grid.\ntemplate <typename DgTag, typename SubcellTag, typename DbTagsList>\nconst typename DgTag::type& get_active_tag(const db::DataBox<DbTagsList>& box) {\n  static_assert(\n      std::is_same_v<typename DgTag::type, typename SubcellTag::type>);\n  if (db::get<Tags::ActiveGrid>(box) == ActiveGrid::Subcell) {\n    return db::get<SubcellTag>(box);\n  }\n  return db::get<DgTag>(box);\n}\n\n/// \\brief Retrieve a tag from the inactive grid.\ntemplate <typename DgTag, typename SubcellTag, typename DbTagsList>\nconst typename DgTag::type& get_inactive_tag(\n    const db::DataBox<DbTagsList>& box) {\n  static_assert(\n      std::is_same_v<typename DgTag::type, typename SubcellTag::type>);\n  if (db::get<Tags::ActiveGrid>(box) == ActiveGrid::Subcell) {\n    return db::get<DgTag>(box);\n  }\n  return db::get<SubcellTag>(box);\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/GetTciDecision.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n\nnamespace evolution::dg::subcell {\n/// Returns the value of evolution::dg::subcell::Tags::TciDecision.\ntemplate <typename DbTagsList>\nint get_tci_decision(const db::DataBox<DbTagsList>& box) {\n  return db::get<Tags::TciDecision>(box);\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace evolution::dg::subcell {\nGhostData::GhostData(const size_t number_of_buffers)\n    : number_of_buffers_(number_of_buffers) {\n  if (number_of_buffers_ == 0) {\n    ERROR(\"The GhostData class must be constructed with at least one buffer.\");\n  }\n\n  local_ghost_data_.resize(number_of_buffers_);\n  neighbor_ghost_data_for_reconstruction_.resize(number_of_buffers_);\n  buffer_index_ = 0;\n}\n\nvoid GhostData::next_buffer() {\n  buffer_index_ =\n      buffer_index_ + 1 == number_of_buffers_ ? 0 : buffer_index_ + 1;\n}\n\nsize_t GhostData::current_buffer_index() const { return buffer_index_; }\n\nsize_t GhostData::total_number_of_buffers() const { return number_of_buffers_; }\n\nDataVector& GhostData::local_ghost_data() {\n  return local_ghost_data_[buffer_index_];\n}\n\nconst DataVector& GhostData::local_ghost_data() const {\n  return local_ghost_data_[buffer_index_];\n}\n\nDataVector& GhostData::neighbor_ghost_data_for_reconstruction() {\n  return neighbor_ghost_data_for_reconstruction_[buffer_index_];\n}\n\nconst DataVector& GhostData::neighbor_ghost_data_for_reconstruction() const {\n  return neighbor_ghost_data_for_reconstruction_[buffer_index_];\n}\n\nvoid GhostData::pup(PUP::er& p) {\n  p | number_of_buffers_;\n  p | buffer_index_;\n  p | local_ghost_data_;\n  // Once Charm++ messages are implemented, this will most likely contain\n  // non-owning DataVectors which we will have to account for\n  p | neighbor_ghost_data_for_reconstruction_;\n}\n\nbool operator==(const GhostData& lhs, const GhostData& rhs) {\n  return lhs.number_of_buffers_ == rhs.number_of_buffers_ and\n         lhs.buffer_index_ == rhs.buffer_index_ and\n         lhs.local_ghost_data_ == rhs.local_ghost_data_ and\n         lhs.neighbor_ghost_data_for_reconstruction_ ==\n             rhs.neighbor_ghost_data_for_reconstruction_;\n}\n\nbool operator!=(const GhostData& lhs, const GhostData& rhs) {\n  return not(lhs == rhs);\n}\n\nstd::ostream& operator<<(std::ostream& os, const GhostData& ghost_data) {\n  using ::operator<<;\n  os << \"LocalGhostData: \" << ghost_data.local_ghost_data() << \"\\n\";\n  os << \"NeighborGhostDataForReconstruction: \"\n     << ghost_data.neighbor_ghost_data_for_reconstruction() << \"\\n\";\n  return os;\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/GhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n\nnamespace evolution::dg::subcell {\n/*!\n * Ghost data used on the subcell grid for reconstruction\n *\n * This class holds both the local ghost data on the local subcell mesh for a\n * given direction, as well as the neighbor's ghost data (on the neighbor's\n * mesh) in that same direction. This class is similar to\n * `evolution::dg::MortarDataHolder` in the sense that it holds both local and\n * neighbor data in a direction. However, it differs because the local ghost\n * data is not used in our own calculation when reconstructing the solution at\n * the face between the elements. This is because we already have our own data\n * on our own FD grid. Only the neighbor ghost data is used to reconstruct the\n * solution on the face.\n *\n * With Charm++ messages, storing the local ghost data is necessary because it\n * must live somewhere so we can send a pointer to our neighbor.\n */\nclass GhostData {\n public:\n  GhostData(size_t number_of_buffers = 1);\n\n  /// Move to the next internal mortar buffer\n  void next_buffer();\n\n  /// Return the current internal buffer index\n  size_t current_buffer_index() const;\n\n  /// Return the total number of buffers that this GhostData was constructed\n  /// with\n  size_t total_number_of_buffers() const;\n\n  /// @{\n  /// The local ghost data for in the current buffer\n  ///\n  /// The non-const reference function can be used to edit the data in-place\n  DataVector& local_ghost_data();\n\n  const DataVector& local_ghost_data() const;\n  /// @}\n\n  /// @{\n  /// The ghost data from our neighbor which is meant to be used in\n  /// reconstruction for in the current buffer.\n  ///\n  /// The non-const reference function can be used to edit the data in-place\n  DataVector& neighbor_ghost_data_for_reconstruction();\n\n  const DataVector& neighbor_ghost_data_for_reconstruction() const;\n  /// @}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  // NOLINTNEXTLINE\n  friend bool operator==(const GhostData& lhs, const GhostData& rhs);\n\n  size_t number_of_buffers_{1};\n  size_t buffer_index_{0};\n  std::vector<DataVector> local_ghost_data_{};\n  std::vector<DataVector> neighbor_ghost_data_for_reconstruction_{};\n};\n\nbool operator!=(const GhostData& lhs, const GhostData& rhs);\n\nstd::ostream& operator<<(std::ostream& os, const GhostData& ghost_data);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/GhostZoneInverseJacobian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostZoneInverseJacobian.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\n\n/// \\brief Mutator that stores the grid coordinates and inverse Jacobians of the\n/// ghost zone.\n///\n/// \\details Mutator that stores the grid coordinates and inverse Jacobians of\n/// the ghost zone. This is run in the initialization phase since this\n/// information is time-independent. The full Jacobian to inertial coordinates\n/// may be applied at each time step.\n\ntemplate <size_t Dim, typename ReconstructorTag>\nstruct GhostZoneInverseJacobian {\n  /// Tags for constant items added to the GlobalCache.  These items are\n  /// initialized from input file options.\n  using const_global_cache_tags = tmpl::list<>;\n\n  /// Tags for mutable items added to the GlobalCache.  These items are\n  /// initialized from input file options.\n  using mutable_global_cache_tags = tmpl::list<>;\n\n  /// Tags for simple DataBox items that are initialized from input file options\n  using simple_tags_from_options = tmpl::list<>;\n\n  /// Tags for simple DataBox items that are default initialized.\n  using default_initialized_simple_tags = tmpl::list<>;\n\n  /// Tags for items fetched by the DataBox and passed to the apply function\n  using argument_tags =\n      tmpl::list<Tags::Mesh<Dim>, ::domain::Tags::ElementMap<Dim, Frame::Grid>,\n                 ReconstructorTag>;\n\n  /// Tags for items in the DataBox that are mutated by the apply function\n  using return_tags = tmpl::list<Tags::GhostZoneInverseJacobian<Dim>>;\n\n  /// Tags for mutable DataBox items that are either default initialized or\n  /// initialized by the apply function\n  using simple_tags = return_tags;\n\n  /// Tags for immutable DataBox items (compute items or reference items) added\n  /// to the DataBox.\n  using compute_tags = tmpl::list<>;\n\n  /// Given the items fetched from a DataBox by the argument_tags, mutate\n  /// the items in the DataBox corresponding to return_tags\n  template <typename ReconstructorType>\n  static void apply(\n      const gsl::not_null<DirectionMap<\n          Dim, Variables<tmpl::list<\n                   evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Grid>,\n                   evolution::dg::subcell::fd::Tags::\n                       InverseJacobianLogicalToGrid<Dim>>>>*>\n          ghost_zone_inverse_jacobian,\n      const Mesh<Dim>& subcell_mesh,\n      const ElementMap<Dim, Frame::Grid>& element_map,\n      const ReconstructorType& reconstructor) {\n    using neighbor_tags = tmpl::list<\n        evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Grid>,\n        evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGrid<Dim>>;\n\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      const auto logical_coords = fd::ghost_zone_logical_coordinates(\n          subcell_mesh, reconstructor.ghost_zone_size(), direction);\n      const auto inv_jacobian = element_map.inv_jacobian(logical_coords);\n      const auto grid_coords = element_map(logical_coords);\n\n      Variables<neighbor_tags> ghost_coords_and_inv_jacobian{\n          logical_coords.get(0).size()};\n      get<evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Grid>>(\n          ghost_coords_and_inv_jacobian) = grid_coords;\n      get<evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGrid<Dim>>(\n          ghost_coords_and_inv_jacobian) = inv_jacobian;\n\n      ghost_zone_inverse_jacobian->insert_or_assign(\n          direction, ghost_coords_and_inv_jacobian);\n    }\n  }\n};\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/GhostZoneLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace evolution::dg::subcell::fd {\n\ntemplate <size_t Dim>\nvoid ghost_zone_logical_coordinates(\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>\n        ghost_logical_coords,\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size,\n    const Direction<Dim>& direction) {\n  const size_t dim_direction = direction.dimension();\n  const auto subcell_extents = subcell_mesh.extents();\n  const size_t subcell_extent_to_direction = subcell_extents[dim_direction];\n\n  // Check if the `ghost_zone_size` has a valid value.\n  ASSERT(ghost_zone_size <= subcell_extent_to_direction,\n         \" Ghost zone size (\"\n             << ghost_zone_size << \") is larger than the volume extent (\"\n             << subcell_extent_to_direction << \") to the direction\");\n\n  set_number_of_grid_points(\n      ghost_logical_coords,\n      ghost_zone_size *\n          subcell_mesh.extents().slice_away(dim_direction).product());\n\n  Index<Dim> ghost_zone_extents{subcell_extents};\n  ghost_zone_extents[dim_direction] = ghost_zone_size;\n\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto& collocation_points_in_this_dim =\n        Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                     Spectral::Quadrature::CellCentered>(\n            subcell_extents[d]);\n\n    if (d == dim_direction) {\n      const size_t index_offset =\n          (direction.side() == Side::Upper)\n              ? subcell_extent_to_direction - ghost_zone_size\n              : 0;\n\n      const double delta_x{collocation_points_in_this_dim[1] -\n                           collocation_points_in_this_dim[0]};\n\n      for (IndexIterator<Dim> index(ghost_zone_extents); index; ++index) {\n        ghost_logical_coords->get(d)[index.collapsed_index()] =\n            collocation_points_in_this_dim[index()[d] + index_offset] +\n            direction.sign() * delta_x * ghost_zone_size;\n      }\n    } else {\n      for (IndexIterator<Dim> index(ghost_zone_extents); index; ++index) {\n        ghost_logical_coords->get(d)[index.collapsed_index()] =\n            collocation_points_in_this_dim[index()[d]];\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim, Frame::ElementLogical> ghost_zone_logical_coordinates(\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size,\n    const Direction<Dim>& direction) {\n  tnsr::I<DataVector, Dim, Frame::ElementLogical> logical_coords(\n      subcell_mesh.extents().product());\n  ghost_zone_logical_coordinates(make_not_null(&logical_coords), subcell_mesh,\n                                 ghost_zone_size, direction);\n  return logical_coords;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                           \\\n  template void ghost_zone_logical_coordinates(                          \\\n      const gsl::not_null<                                               \\\n          tnsr::I<DataVector, DIM(data), Frame::ElementLogical>*>        \\\n          ghost_logical_coords,                                          \\\n      const Mesh<DIM(data)>& subcell_mesh, const size_t ghost_zone_size, \\\n      const Direction<DIM(data)>& direction);                            \\\n  template tnsr::I<DataVector, DIM(data), Frame::ElementLogical>         \\\n  ghost_zone_logical_coordinates(const Mesh<DIM(data)>& subcell_mesh,    \\\n                                 const size_t ghost_zone_size,           \\\n                                 const Direction<DIM(data)>& direction);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATION\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <size_t Dim>\nclass Direction;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace evolution::dg::subcell::fd {\n/// @{\n/*!\n * \\brief Computes the logical coordinates of ghost grid points for a given\n * direction and ghost zone size.\n *\n * Let `d` be the axis dimension of the `direction`. The returned coordinate has\n * extents that is same as the volume mesh extents but `[d]`-th value\n * replaced by the ghost zone size.\n *\n * For instance if the (volume) subcell mesh has extents \\f$(6,6,6)\\f$, ghost\n * zone size is 2, and the `direction` is along Xi axis, the resulting\n * coordinates computed by this function has extents \\f$(2,6,6)\\f$.\n *\n */\ntemplate <size_t Dim>\nvoid ghost_zone_logical_coordinates(\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::ElementLogical>*>\n        ghost_logical_coords,\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size,\n    const Direction<Dim>& direction);\n\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim, Frame::ElementLogical> ghost_zone_logical_coordinates(\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size,\n    const Direction<Dim>& direction);\n/// @}\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/InitialTciData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/InitialTciData.hpp\"\n\n#include <pup.h>\n\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace evolution::dg::subcell {\nvoid InitialTciData::pup(PUP::er& p) {\n  p | tci_status;\n  p | initial_rdmp_data;\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/InitialTciData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Used to communicate the RDMP and TCI status/decision during\n * initialization.\n */\nstruct InitialTciData {\n  std::optional<int> tci_status{};\n  std::optional<evolution::dg::subcell::RdmpTciData> initial_rdmp_data{};\n\n  void pup(PUP::er& p);\n};\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Matrices.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Matrices.hpp\"\n\n#include <array>\n#include <ostream>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace evolution::dg::subcell::fd {\nconst Matrix& projection_matrix(\n    const Mesh<1>& dg_mesh, const size_t subcell_extents,\n    const Spectral::Quadrature& subcell_quadrature) {\n  ASSERT(dg_mesh.basis(0) == Spectral::Basis::Legendre,\n         \"FD Subcell projection only supports Legendre basis right now but got \"\n         \"basis \"\n             << dg_mesh.basis(0));\n  ASSERT(subcell_quadrature == Spectral::Quadrature::FaceCentered or\n             subcell_quadrature == Spectral::Quadrature::CellCentered,\n         \"subcell_quadrature option in projection_matrix should be \"\n         \"FaceCentered or CellCentered\");\n  static const auto cache = make_static_cache<\n      CacheRange<\n          Spectral::minimum_number_of_points<\n              Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>,\n          Spectral::maximum_number_of_points<Spectral::Basis::Legendre> + 1>,\n      CacheRange<Spectral::minimum_number_of_points<\n                     Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered>,\n                 Spectral::maximum_number_of_points<\n                     Spectral::Basis::FiniteDifference> +\n                     1>,\n      CacheEnumeration<Spectral::Quadrature, Spectral::Quadrature::Gauss,\n                       Spectral::Quadrature::GaussLobatto>,\n      CacheEnumeration<Spectral::Quadrature, Spectral::Quadrature::CellCentered,\n                       Spectral::Quadrature::FaceCentered>>(\n      [](const size_t local_num_dg_points, const size_t local_num_fd_points,\n         const Spectral::Quadrature dg_quadrature,\n         const Spectral::Quadrature local_subcell_quadrature) {\n        return Spectral::interpolation_matrix(\n            Mesh<1>{local_num_dg_points, Spectral::Basis::Legendre,\n                    dg_quadrature},\n            Spectral::collocation_points(\n                Mesh<1>{local_num_fd_points, Spectral::Basis::FiniteDifference,\n                        local_subcell_quadrature}));\n      });\n  return cache(dg_mesh.extents(0), subcell_extents, dg_mesh.quadrature(0),\n               subcell_quadrature);\n}\n\nnamespace {\ntemplate <Spectral::Quadrature QuadratureType, size_t NumDgGridPoints1d,\n          size_t Dim>\nMatrix projection_matrix_all_dimensions(const Index<Dim>& subcell_extents) {\n  // We currently require all dimensions to have the same number of grid\n  // points, but this is checked in the calling function.\n  const Index<Dim> dg_extents{NumDgGridPoints1d};\n  const size_t num_subcells = subcell_extents.product();\n  const size_t num_pts = dg_extents.product();\n\n  const size_t num_rows = num_subcells;\n  const size_t num_columns = num_pts;\n  Matrix proj_matrix(num_rows, num_columns);\n\n  std::array<Matrix, Dim> interpolation_matrices{};\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(interpolation_matrices, d) =\n        Spectral::interpolation_matrix<Spectral::Basis::Legendre,\n                                       QuadratureType>(\n            dg_extents[d],\n            Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                         Spectral::Quadrature::CellCentered>(\n                subcell_extents[d]));\n  }\n\n  for (IndexIterator<Dim> subcell_it(subcell_extents); subcell_it;\n       ++subcell_it) {\n    for (IndexIterator<Dim> basis_it(dg_extents); basis_it; ++basis_it) {\n      proj_matrix(subcell_it.collapsed_index(), basis_it.collapsed_index()) =\n          interpolation_matrices[0](subcell_it()[0], basis_it()[0]);\n      for (size_t d = 1; d < Dim; ++d) {\n        proj_matrix(subcell_it.collapsed_index(), basis_it.collapsed_index()) *=\n            gsl::at(interpolation_matrices, d)(subcell_it()[d], basis_it()[d]);\n      }\n    }\n  }\n\n  return proj_matrix;\n}\n\ndouble get_sixth_order_integration_coefficient(const size_t num_pts,\n                                               const size_t index) {\n  if (num_pts == 3) {\n    return gsl::at(std::array<double, 3>{{9. / 8., 3. / 4., 9. / 8.}}, index);\n  } else if (num_pts == 4) {\n    return gsl::at(\n        std::array<double, 4>{{13. / 12., 11. / 12., 11. / 12., 13. / 12.}},\n        index);\n  } else if (num_pts == 5) {\n    return gsl::at(\n        std::array<double, 5>{{1375. / 1152., 125. / 288., 335. / 192.,\n                               125. / 288., 1375. / 1152.}},\n        index);\n  } else if (num_pts == 6) {\n    return gsl::at(\n        std::array<double, 6>{{741. / 640., 417. / 640., 381. / 320.,\n                               381. / 320., 417. / 640., 741. / 640.}},\n        index);\n  } else if (num_pts == 7) {\n    return gsl::at(\n        std::array<double, 7>{{741. / 640., 3547. / 5760., 8111. / 5760.,\n                               611. / 960., 8111. / 5760., 3547. / 5760.,\n                               741. / 640.}},\n        index);\n  } else if (num_pts == 8) {\n    return gsl::at(\n        std::array<double, 8>{{1663. / 1440., 227. / 360., 323. / 240.,\n                               139. / 160., 139. / 160., 323. / 240.,\n                               227. / 360., 1663. / 1440.}},\n        index);\n  } else if (num_pts == 9) {\n    return gsl::at(\n        std::array<double, 9>{{1663. / 1440., 227. / 360., 1547. / 1152.,\n                               245. / 288., 3001. / 2880., 245. / 288.,\n                               1547. / 1152., 227. / 360., 1663. / 1440.}},\n        index);\n  } else if (num_pts == 10) {\n    return gsl::at(\n        std::array<double, 10>{{1375. / 1152., 125. / 288., 335. / 192.,\n                                125. / 288., 1375. / 1152., 1375. / 1152.,\n                                125. / 288., 335. / 192., 125. / 288.,\n                                1375. / 1152.}},\n        index);\n  } else if (num_pts == 11) {\n    return gsl::at(\n        std::array<double, 11>{{1375. / 1152., 125. / 288., 335. / 192.,\n                                2483. / 5760., 7183. / 5760., 863. / 960.,\n                                7183. / 5760., 2483. / 5760., 335. / 192.,\n                                125. / 288., 1375. / 1152.}},\n        index);\n  } else if (num_pts == 12) {\n    return gsl::at(\n        std::array<double, 12>{{1375. / 1152., 125. / 288., 335. / 192.,\n                                2483. / 5760., 3583. / 2880., 2743. / 2880.,\n                                2743. / 2880., 3583. / 2880., 2483. / 5760.,\n                                335. / 192., 125. / 288., 1375. / 1152.}},\n        index);\n  } else if (num_pts == 13) {\n    return gsl::at(\n        std::array<double, 13>{{1375. / 1152., 125. / 288., 335. / 192.,\n                                2483. / 5760., 3583. / 2880., 1823. / 1920.,\n                                2897. / 2880., 1823. / 1920., 3583. / 2880.,\n                                2483. / 5760., 335. / 192., 125. / 288.,\n                                1375. / 1152.}},\n        index);\n  } else if (num_pts == 14) {\n    return gsl::at(\n        std::array<double, 14>{{1375. / 1152., 125. / 288., 335. / 192.,\n                                2483. / 5760., 3583. / 2880., 1823. / 1920.,\n                                5777. / 5760., 5777. / 5760., 1823. / 1920.,\n                                3583. / 2880., 2483. / 5760., 335. / 192.,\n                                125. / 288., 1375. / 1152.}},\n        index);\n  } else if (num_pts >= 15) {\n    return gsl::at(\n        std::array<double, 8>{{1375. / 1152., 125. / 288., 335. / 192.,\n                               2483. / 5760., 3583. / 2880., 1823. / 1920.,\n                               5777. / 5760., 1.}},\n\n        index <= 7             ? index\n        : index >= num_pts - 8 ? num_pts - 1 - index\n                               : 7);\n  }\n  ERROR(\"Cannot get coefficients for a mesh with only '\"\n        << num_pts << \"' points. We need at least 5 points.\");\n}\n\n// Function to get the 1d integration coefficient from the 1, 2, or 3d spatial\n// location. The 1d coefficient is a product of the 3 coefficients in each\n// direction.\ntemplate <size_t Dim>\ndouble integration_weight(const Index<Dim>& extents, const Index<Dim>& index) {\n  double result = get_sixth_order_integration_coefficient(extents[0], index[0]);\n  for (size_t d = 1; d < Dim; ++d) {\n    result *= get_sixth_order_integration_coefficient(extents[d], index[d]);\n  }\n  return result;\n}\n\ntemplate <Spectral::Quadrature QuadratureType, size_t NumDgGridPoints1d,\n          size_t Dim>\nMatrix reconstruction_matrix_cache_impl_helper(\n    const Index<Dim>& subcell_extents) {\n  // We currently require all dimensions to have the same number of grid\n  // points.\n  const Index<Dim> dg_extents{NumDgGridPoints1d};\n  const Matrix& proj_matrix =\n      projection_matrix_all_dimensions<QuadratureType, NumDgGridPoints1d>(\n          subcell_extents);\n  const size_t num_pts = dg_extents.product();\n  const size_t num_subcells = subcell_extents.product();\n\n  const size_t reconstruction_rows_and_cols = num_pts + 1;\n  Matrix lhs_recons_matrix(reconstruction_rows_and_cols,\n                           reconstruction_rows_and_cols, 0.0);\n  // We use rhs_recons_matrix here as a temp buffer, we will fill it later.\n  Matrix rhs_recons_matrix(num_pts + 1, num_subcells);\n  dgemm_<true>('T', 'N', proj_matrix.columns(), proj_matrix.columns(),\n               proj_matrix.rows(), 2.0, proj_matrix.data(),\n               proj_matrix.spacing(), proj_matrix.data(), proj_matrix.spacing(),\n               0.0, rhs_recons_matrix.data(), rhs_recons_matrix.spacing());\n\n  for (size_t l = 0; l < num_pts; ++l) {\n    for (size_t j = 0; j < num_pts; ++j) {\n      lhs_recons_matrix(l, j) = rhs_recons_matrix(j, l);\n    }\n  }\n\n  std::array<const DataVector*, Dim> weights{};\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(weights, d) =\n        &Spectral::quadrature_weights<Spectral::Basis::Legendre,\n                                      QuadratureType>(dg_extents[d]);\n  }\n  for (IndexIterator<Dim> dg_it(dg_extents); dg_it; ++dg_it) {\n    lhs_recons_matrix(dg_it.collapsed_index(), num_pts) =\n        -(*weights[0])[dg_it()[0]];\n    lhs_recons_matrix(num_pts, dg_it.collapsed_index()) =\n        (*weights[0])[dg_it()[0]];\n    for (size_t i = 1; i < Dim; ++i) {\n      lhs_recons_matrix(dg_it.collapsed_index(), num_pts) *=\n          (*gsl::at(weights, i))[dg_it()[i]];\n      lhs_recons_matrix(num_pts, dg_it.collapsed_index()) *=\n          (*gsl::at(weights, i))[dg_it()[i]];\n    }\n  }\n\n  for (size_t k = 0; k < num_subcells; ++k) {\n    for (size_t l = 0; l < num_pts; ++l) {\n      rhs_recons_matrix(l, k) = 2.0 * proj_matrix(k, l);\n    }\n  }\n\n  double deltas = 2.0 / subcell_extents[0];\n  for (size_t d = 1; d < Dim; ++d) {\n    deltas *= 2.0 / subcell_extents[d];\n  }\n  for (size_t i = 0; i < num_subcells; ++i) {\n    rhs_recons_matrix(num_pts, i) = deltas;\n  }\n  for (IndexIterator<Dim> it(subcell_extents); it; ++it) {\n    rhs_recons_matrix(num_pts, it.collapsed_index()) *=\n        integration_weight(subcell_extents, *it);\n  }\n\n  const Matrix inv_lhs_recons_matrix = inv(lhs_recons_matrix);\n  Matrix full_recons_matrix(inv_lhs_recons_matrix.rows(),\n                            rhs_recons_matrix.columns());\n  // Do matrix multipy with dgemm_ directly because that seems to be faster\n  // than Blaze.\n  dgemm_<true>('N', 'N', inv_lhs_recons_matrix.rows(),\n               rhs_recons_matrix.columns(), inv_lhs_recons_matrix.columns(),\n               1.0, inv_lhs_recons_matrix.data(),\n               inv_lhs_recons_matrix.spacing(), rhs_recons_matrix.data(),\n               rhs_recons_matrix.spacing(), 0.0, full_recons_matrix.data(),\n               full_recons_matrix.spacing());\n  // exclude bottom row for Lagrange multiplier.\n  Matrix reduced_recons_matrix(num_pts, num_subcells);\n  for (size_t i = 0; i < num_pts; ++i) {\n    for (size_t j = 0; j < num_subcells; ++j) {\n      reduced_recons_matrix(i, j) = full_recons_matrix(i, j);\n    }\n  }\n\n  return reduced_recons_matrix;\n}\n\ntemplate <Spectral::Quadrature QuadratureType, size_t NumDgGridPoints,\n          size_t Dim>\nconst Matrix& reconstruction_matrix_cache_impl(\n    const Index<Dim>& subcell_extents) {\n  static const Matrix result =\n      reconstruction_matrix_cache_impl_helper<QuadratureType, NumDgGridPoints>(\n          subcell_extents);\n  return result;\n}\n\ntemplate <Spectral::Quadrature QuadratureType, size_t... Is, size_t Dim>\nconst Matrix& reconstruction_matrix_impl(\n    const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents,\n    std::index_sequence<Is...> /*num_dg_grid_points*/) {\n  static const std::array<const Matrix& (*)(const Index<Dim>&), sizeof...(Is)>\n      cache{{&reconstruction_matrix_cache_impl<QuadratureType, Is, Dim>...}};\n  return gsl::at(cache, dg_mesh.extents(0))(subcell_extents);\n}\n}  // namespace\n\ntemplate <size_t Dim>\nconst Matrix& reconstruction_matrix(const Mesh<Dim>& dg_mesh,\n                                    const Index<Dim>& subcell_extents) {\n  ASSERT(dg_mesh.basis(0) == Spectral::Basis::Legendre,\n         \"FD Subcell reconstruction only supports Legendre basis right now.\");\n  ASSERT(dg_mesh == Mesh<Dim>(dg_mesh.extents(0), dg_mesh.basis(0),\n                              dg_mesh.quadrature(0)),\n         \"The mesh must be uniform but is \" << dg_mesh);\n  ASSERT(subcell_extents == Index<Dim>(subcell_extents[0]),\n         \"The subcell mesh must be uniform but is \" << subcell_extents);\n  switch (dg_mesh.quadrature(0)) {\n    case Spectral::Quadrature::GaussLobatto:\n      return reconstruction_matrix_impl<Spectral::Quadrature::GaussLobatto>(\n          dg_mesh, subcell_extents,\n          std::make_index_sequence<\n              Spectral::maximum_number_of_points<Spectral::Basis::Legendre> +\n              1>{});\n    case Spectral::Quadrature::Gauss:\n      return reconstruction_matrix_impl<Spectral::Quadrature::Gauss>(\n          dg_mesh, subcell_extents,\n          std::make_index_sequence<\n              Spectral::maximum_number_of_points<Spectral::Basis::Legendre> +\n              1>{});\n    default:\n      ERROR(\"Unsupported quadrature type in FD subcell reconstruction matrix\");\n  };\n}\n\nconst Matrix& projection_matrix(const Mesh<1>& dg_mesh,\n                                const size_t subcell_extents,\n                                const size_t ghost_zone_size, const Side side) {\n  static constexpr size_t max_ghost_zone_size = 5;\n  ASSERT(dg_mesh.basis(0) == Spectral::Basis::Legendre,\n         \"FD Subcell projection only supports Legendre basis right now but got \"\n         \"basis \"\n             << dg_mesh.basis(0));\n  ASSERT(ghost_zone_size <= max_ghost_zone_size and ghost_zone_size >= 2,\n         \"ghost_zone_size must be in [2, \" << max_ghost_zone_size\n                                           << \" ] but got \" << ghost_zone_size);\n  static const auto cache =\n      make_static_cache<\n          CacheRange<\n              Spectral::minimum_number_of_points<\n                  Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto>,\n              Spectral::maximum_number_of_points<Spectral::Basis::Legendre> +\n                  1>,\n          CacheRange<Spectral::minimum_number_of_points<\n                         Spectral::Basis::FiniteDifference,\n                         Spectral::Quadrature::CellCentered>,\n                     Spectral::maximum_number_of_points<\n                         Spectral::Basis::FiniteDifference> +\n                         1>,\n          CacheRange<2_st, max_ghost_zone_size + 1>,\n          CacheEnumeration<Spectral::Quadrature, Spectral::Quadrature::Gauss,\n                           Spectral::Quadrature::GaussLobatto>,\n          CacheEnumeration<Side, Side::Lower, Side::Upper>>(\n          [](const size_t local_num_dg_points, const size_t local_num_fd_points,\n             const size_t local_ghost_zone_size,\n             const Spectral::Quadrature dg_quadrature, const Side local_side) {\n            const DataVector& fd_points = Spectral::collocation_points<\n                Spectral::Basis::FiniteDifference,\n                Spectral::Quadrature::CellCentered>(local_num_fd_points);\n            DataVector target_points(local_ghost_zone_size);\n            for (size_t i = 0; i < local_ghost_zone_size; ++i) {\n              target_points[i] = fd_points[local_side == Side::Lower\n                                               ? i\n                                               : (local_num_fd_points -\n                                                  local_ghost_zone_size + i)];\n            }\n            return Spectral::interpolation_matrix(\n                Mesh<1>{local_num_dg_points, Spectral::Basis::Legendre,\n                        dg_quadrature},\n                target_points);\n          });\n  return cache(dg_mesh.extents(0), subcell_extents, ghost_zone_size,\n               dg_mesh.quadrature(0), side);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                             \\\n  template const Matrix& reconstruction_matrix(const Mesh<GET_DIM(data)>&, \\\n                                               const Index<GET_DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Matrices.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Index;\nclass Matrix;\ntemplate <size_t Dim>\nclass Mesh;\nenum class Side : uint8_t;\nnamespace Spectral {\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace evolution::dg::subcell::fd {\n/*!\n * \\ingroup DgSubcellGroup\n * \\brief Computes the projection matrix in 1 dimension going from a DG\n * mesh to a conservative finite difference subcell mesh.\n */\nconst Matrix& projection_matrix(const Mesh<1>& dg_mesh, size_t subcell_extents,\n                                const Spectral::Quadrature& subcell_quadrature);\n\n/*!\n * \\ingroup DgSubcellGroup\n * \\brief Computes the matrix needed for reconstructing the DG solution from\n * the subcell solution.\n *\n * Reconstructing the DG solution from the FD solution is a bit more\n * involved than projecting the DG solution to the FD subcells. Denoting the\n * projection operator by \\f$\\mathcal{P}\\f$ and the reconstruction operator by\n * \\f$\\mathcal{R}\\f$, we desire the property\n *\n * \\f{align*}{\n *   \\mathcal{R}(\\mathcal{P}(u_{\\breve{\\imath}}\n *   J_{\\breve{\\imath}}))=u_{\\breve{\\imath}} J_{\\breve{\\imath}},\n * \\f}\n *\n * where \\f$\\breve{\\imath}\\f$ denotes a grid point on the DG grid, \\f$u\\f$ is\n * the solution on the DG grid, and \\f$J\\f$ is the determinant of the Jacobian\n * on the DG grid. We also require that the integral of the conserved variables\n * over the subcells is equal to the integral over the DG element. That is,\n *\n * \\f{align*}{\n *   \\int_{\\Omega}u \\,d^3x =\\int_{\\Omega} \\underline{u} \\,d^3x \\Longrightarrow\n *   \\int_{\\Omega}u J \\,d^3\\xi=\\int_{\\Omega} \\underline{u} J \\,d^3\\xi,\n * \\f}\n *\n * where \\f$\\underline{u}\\f$ is the solution on the subcells. Because the number\n * of subcell points is larger than the number of DG points, we need to solve a\n * constrained linear least squares problem to reconstruct the DG solution from\n * the subcells.\n *\n * The final reconstruction matrix is given by\n *\n * \\f{align*}{\n *   R_{\\breve{\\jmath}\\underline{i}}\n *   &=\\left\\{(2 \\mathcal{P}\\otimes\\mathcal{P})^{-1}2\\mathcal{P} - (2\n *   \\mathcal{P}\\otimes\\mathcal{P})^{-1}\\vec{w}\\left[\\mathbf{w}(2\n *   \\mathcal{P}\\otimes\\mathcal{P})^{-1}\\vec{w}\\right]^{-1}\\mathbf{w}(2\n *   \\mathcal{P}\\otimes\\mathcal{P})^{-1}2\\mathcal{P}\n *   + (2 \\mathcal{P}\\otimes\\mathcal{P})^{-1}\\vec{w}\\left[\\mathbf{w}(2\n *   \\mathcal{P}\\otimes\\mathcal{P})^{-1}\\vec{w}\\right]^{-1}\\vec{\\underline{w}}\n *   \\right\\}_{\\breve{\\jmath}\\underline{i}},\n * \\f}\n *\n * where \\f$\\vec{w}\\f$ is the vector of integration weights on the DG element,\n * \\f$\\mathbf{w}=w_{\\breve{l}}\\delta_{\\breve{l}\\breve{\\jmath}}\\f$, and\n * \\f$\\vec{\\underline{w}}\\f$ is the vector of integration weights over the\n * subcells. The integration weights \\f$\\vec{\\underline{w}}\\f$ on the subcells\n * are those for 6th-order integration on a uniform mesh.\n */\ntemplate <size_t Dim>\nconst Matrix& reconstruction_matrix(const Mesh<Dim>& dg_mesh,\n                                    const Index<Dim>& subcell_extents);\n\n/*!\n * \\ingroup DgSubcellGroup\n * \\brief Computes the projection matrix in 1 dimension going from a DG\n * mesh to a conservative finite difference subcell mesh for only the ghost\n * zones.\n *\n * This is used when a neighbor sends DG volume data and we need to switch to\n * FD. In this case we need to project the DG volume data onto the ghost zone\n * cells.\n *\n * \\note Currently assumes a max ghost zone size of `5` and a minimum ghost zone\n * size of 2.\n */\nconst Matrix& projection_matrix(const Mesh<1>& dg_mesh, size_t subcell_extents,\n                                size_t ghost_zone_size, Side side);\n}  // namespace evolution::dg::subcellfd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Mesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace evolution::dg::subcell::fd {\ntemplate <size_t Dim>\nMesh<Dim> mesh(const Mesh<Dim>& dg_mesh) {\n  ASSERT(dg_mesh.basis() == make_array<Dim>(Spectral::Basis::Legendre) or\n             dg_mesh.basis() == make_array<Dim>(Spectral::Basis::Chebyshev),\n         \"The DG basis for computing the subcell mesh must be Legendre or \"\n         \"Chebyshev but got DG mesh\"\n             << dg_mesh);\n  ASSERT(dg_mesh.quadrature() == make_array<Dim>(Spectral::Quadrature::Gauss) or\n             dg_mesh.quadrature() ==\n                 make_array<Dim>(Spectral::Quadrature::GaussLobatto),\n         \"The DG quadrature for computing the subcell mesh must be Gauss or \"\n         \"GaussLobatto but got DG mesh\"\n             << dg_mesh);\n  std::array<size_t, Dim> extents{};\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(extents, d) = 2 * dg_mesh.extents(d) - 1;\n  }\n  return Mesh<Dim>{extents, Spectral::Basis::FiniteDifference,\n                   Spectral::Quadrature::CellCentered};\n}\n\ntemplate <size_t Dim>\nMesh<Dim> dg_mesh(const Mesh<Dim>& subcell_mesh, const Spectral::Basis basis,\n                  const Spectral::Quadrature quadrature) {\n  ASSERT(subcell_mesh.basis() ==\n             make_array<Dim>(Spectral::Basis::FiniteDifference),\n         \"The basis for computing the DG mesh must be FiniteDifference but got \"\n             << subcell_mesh);\n  ASSERT(\n      subcell_mesh.quadrature() ==\n          make_array<Dim>(Spectral::Quadrature::CellCentered),\n      \"The quadrature for computing the DG mesh must be CellCentered but got \"\n          << subcell_mesh);\n  ASSERT(\n      basis == Spectral::Basis::Legendre or basis == Spectral::Basis::Chebyshev,\n      \"The DG basis must be Legendre or Chebyshev but got \" << basis);\n  ASSERT(quadrature == Spectral::Quadrature::Gauss or\n             quadrature == Spectral::Quadrature::GaussLobatto,\n         \"The DG quadrature for computing the DG mesh must be Gauss or \"\n         \"GaussLobatto but \"\n             << quadrature);\n  std::array<size_t, Dim> extents{};\n  for (size_t d = 0; d < Dim; ++d) {\n    ASSERT((subcell_mesh.extents(d) + 1) % 2 == 0,\n           \"Subcell mesh must have odd extents \" << subcell_mesh);\n    gsl::at(extents, d) = (subcell_mesh.extents(d) + 1) / 2;\n  }\n  return Mesh<Dim>{extents, basis, quadrature};\n}\n\ntemplate Mesh<1> mesh(const Mesh<1>& dg_mesh);\ntemplate Mesh<2> mesh(const Mesh<2>& dg_mesh);\ntemplate Mesh<3> mesh(const Mesh<3>& dg_mesh);\ntemplate Mesh<1> dg_mesh(const Mesh<1>& subcell_mesh,\n                         const Spectral::Basis basis,\n                         const Spectral::Quadrature quadrature);\ntemplate Mesh<2> dg_mesh(const Mesh<2>& subcell_mesh,\n                         const Spectral::Basis basis,\n                         const Spectral::Quadrature quadrature);\ntemplate Mesh<3> dg_mesh(const Mesh<3>& subcell_mesh,\n                         const Spectral::Basis basis,\n                         const Spectral::Quadrature quadrature);\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Mesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell::fd {\n/*!\n * \\brief Computes the cell-centered finite-difference mesh from the DG mesh,\n * using \\f$2N-1\\f$ grid points per dimension, where \\f$N\\f$ is the degree of\n * the DG basis.\n */\ntemplate <size_t Dim>\nMesh<Dim> mesh(const Mesh<Dim>& dg_mesh);\n\n/*!\n * \\brief Computes the DG mesh from the cell-centered finite-difference mesh.\n */\ntemplate <size_t Dim>\nMesh<Dim> dg_mesh(const Mesh<Dim>& subcell_mesh, Spectral::Basis basis,\n                  Spectral::Quadrature quadrature);\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/NeighborRdmpAndVolumeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/NeighborRdmpAndVolumeData.hpp\"\n\n#include <algorithm>\n#include <functional>\n#include <iterator>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Matrices.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace evolution::dg::subcell {\ntemplate <bool InsertIntoMap, size_t Dim>\nvoid insert_or_update_neighbor_volume_data(\n    const gsl::not_null<DirectionalIdMap<Dim, GhostData>*> ghost_data_ptr,\n    const DataVector& neighbor_subcell_data,\n    const size_t number_of_rdmp_vars_in_buffer,\n    const DirectionalId<Dim>& directional_element_id,\n    const Mesh<Dim>& neighbor_mesh, const Element<Dim>& element,\n    const Mesh<Dim>& subcell_mesh, const size_t number_of_ghost_zones,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        neighbor_dg_to_fd_interpolants) {\n  ASSERT(neighbor_mesh == Mesh<Dim>(neighbor_mesh.extents(0),\n                                    neighbor_mesh.basis(0),\n                                    neighbor_mesh.quadrature(0)),\n         \"The neighbor mesh must be uniform but is \" << neighbor_mesh);\n  ASSERT(neighbor_subcell_data.size() != 0,\n         \"neighbor_subcell_data must be non-empty\");\n  const size_t end_of_volume_data =\n      neighbor_subcell_data.size() - 2 * number_of_rdmp_vars_in_buffer;\n\n  if constexpr (InsertIntoMap) {\n    (*ghost_data_ptr)[directional_element_id] = GhostData{1};\n  }\n\n  DataVector& ghost_data = (*ghost_data_ptr)[directional_element_id]\n                               .neighbor_ghost_data_for_reconstruction();\n  DataVector computed_ghost_data{};\n  if (neighbor_mesh.basis(0) == Spectral::Basis::FiniteDifference) {\n    ASSERT(neighbor_mesh == subcell_mesh,\n           \"Neighbor mesh (\"\n               << neighbor_mesh << \") and my mesh (\" << subcell_mesh\n               << \") must be the same if we are both doing subcell.\");\n    if (not InsertIntoMap and\n        neighbor_subcell_data.data() == ghost_data.data()) {\n      // Short-circuit if we are already doing FD and we would be\n      // self-assigning, so elide copy and move.\n      return;\n    }\n    // Copy over the ghost cell data for subcell reconstruction. In this case\n    // the neighbor would have reoriented/interpolated the data for us.\n    computed_ghost_data.destructive_resize(end_of_volume_data);\n    std::copy(\n        neighbor_subcell_data.begin(),\n        std::prev(neighbor_subcell_data.end(),\n                  2 * static_cast<typename std::iterator_traits<\n                          typename DataVector::iterator>::difference_type>(\n                          number_of_rdmp_vars_in_buffer)),\n        computed_ghost_data.begin());\n  } else {\n    ASSERT(evolution::dg::subcell::fd::mesh(neighbor_mesh) == subcell_mesh,\n           \"Neighbor subcell mesh computed from the neighbor DG mesh (\"\n               << evolution::dg::subcell::fd::mesh(neighbor_mesh)\n               << \") and my mesh (\" << subcell_mesh << \") must be the same.\");\n    const Direction<Dim>& direction = directional_element_id.direction();\n    const size_t total_number_of_ghost_zones =\n        number_of_ghost_zones *\n        subcell_mesh.extents().slice_away(direction.dimension()).product();\n    ASSERT(\n        end_of_volume_data % neighbor_mesh.number_of_grid_points() == 0,\n        \"The number of DG volume grid points times the number of variables \"\n        \"sent for reconstruction (\"\n            << end_of_volume_data\n            << \") must be a multiple of the number of DG volume grid points: \"\n            << neighbor_mesh.number_of_grid_points() << \" number of RDMP vars \"\n            << number_of_rdmp_vars_in_buffer << \"\\nThis element: \" << element\n            << \"\\nDirection: \" << direction);\n    const size_t number_of_vars =\n        end_of_volume_data / neighbor_mesh.number_of_grid_points();\n\n    computed_ghost_data.destructive_resize(total_number_of_ghost_zones *\n                                           number_of_vars);\n    if (const auto iter =\n            neighbor_dg_to_fd_interpolants.find(directional_element_id);\n        iter != neighbor_dg_to_fd_interpolants.end() and\n        iter->second.has_value()) {\n      // Our neighbor is in a different Block, so we assume a different map\n      // and need to interpolate to our neighbors' ghost zone coordinates.\n      gsl::span<double> result{computed_ghost_data.data(),\n                               computed_ghost_data.size()};\n      iter->second.value().interpolate(\n          make_not_null(&result),\n          gsl::span<const double>{neighbor_subcell_data.data(),\n                                  end_of_volume_data});\n    } else {\n      // If our neighbor is in our block we can do simple dim-by-dim\n      // interpolation.\n      const auto project_to_ghost_data =\n          [&direction, &computed_ghost_data, &number_of_ghost_zones,\n           &subcell_mesh](const Mesh<Dim>& neighbor_mesh_for_projection,\n                          const DataVector& neighbor_data_for_projection) {\n            // Project to ghosts\n            Matrix empty{};\n            auto ghost_projection_mat = make_array<Dim>(std::cref(empty));\n            for (size_t i = 0; i < Dim; ++i) {\n              if (i == direction.dimension()) {\n                gsl::at(ghost_projection_mat, i) =\n                    std::cref(evolution::dg::subcell::fd::projection_matrix(\n                        neighbor_mesh_for_projection.slice_through(i),\n                        subcell_mesh.extents(i), number_of_ghost_zones,\n                        direction.opposite().side()));\n              } else {\n                gsl::at(ghost_projection_mat, i) =\n                    std::cref(evolution::dg::subcell::fd::projection_matrix(\n                        neighbor_mesh_for_projection.slice_through(i),\n                        subcell_mesh.extents(i),\n                        Spectral::Quadrature::CellCentered));\n              }\n            }\n            apply_matrices(make_not_null(&computed_ghost_data),\n                           ghost_projection_mat, neighbor_data_for_projection,\n                           neighbor_mesh_for_projection.extents());\n          };\n      const DataVector neighbor_data_without_rdmp_vars{\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          const_cast<double*>(neighbor_subcell_data.data()),\n          end_of_volume_data};\n      project_to_ghost_data(neighbor_mesh, neighbor_data_without_rdmp_vars);\n    }\n  }\n\n  ghost_data = std::move(computed_ghost_data);\n}\n\ntemplate <size_t Dim>\nvoid insert_neighbor_rdmp_and_volume_data(\n    const gsl::not_null<RdmpTciData*> rdmp_tci_data_ptr,\n    const gsl::not_null<DirectionalIdMap<Dim, GhostData>*> ghost_data_ptr,\n    const DataVector& received_neighbor_subcell_data,\n    const size_t number_of_rdmp_vars,\n    const DirectionalId<Dim>& directional_element_id,\n    const Mesh<Dim>& neighbor_mesh, const Element<Dim>& element,\n    const Mesh<Dim>& subcell_mesh, const size_t number_of_ghost_zones,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        neighbor_dg_to_fd_interpolants) {\n  ASSERT(received_neighbor_subcell_data.size() != 0,\n         \"received_neighbor_subcell_data must be non-empty\");\n  // Note: since we determine the starting point of the RDMP vars\n  // from how many RDMP vars there are, we don't need to account for\n  // the mesh the neighbor sent (DG or FD)\n  const size_t max_offset =\n      received_neighbor_subcell_data.size() - 2 * number_of_rdmp_vars;\n  const size_t min_offset =\n      received_neighbor_subcell_data.size() - number_of_rdmp_vars;\n  for (size_t var_index = 0; var_index < number_of_rdmp_vars; ++var_index) {\n    rdmp_tci_data_ptr->max_variables_values[var_index] =\n        std::max(rdmp_tci_data_ptr->max_variables_values[var_index],\n                 received_neighbor_subcell_data[max_offset + var_index]);\n    rdmp_tci_data_ptr->min_variables_values[var_index] =\n        std::min(rdmp_tci_data_ptr->min_variables_values[var_index],\n                 received_neighbor_subcell_data[min_offset + var_index]);\n  }\n  // Note: it would be good to assert that the neighbor is at the same\n  // refinement level as us, but such a function does not yet exist.\n\n  insert_or_update_neighbor_volume_data<true>(\n      ghost_data_ptr, received_neighbor_subcell_data, number_of_rdmp_vars,\n      directional_element_id, neighbor_mesh, element, subcell_mesh,\n      number_of_ghost_zones, neighbor_dg_to_fd_interpolants);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                               \\\n  template void insert_neighbor_rdmp_and_volume_data(                        \\\n      gsl::not_null<RdmpTciData*> rdmp_tci_data_ptr,                         \\\n      gsl::not_null<DirectionalIdMap<GET_DIM(data), GhostData>*>             \\\n          ghost_data_ptr,                                                    \\\n      const DataVector& neighbor_subcell_data, size_t number_of_rdmp_vars,   \\\n      const DirectionalId<GET_DIM(data)>& directional_element_id,            \\\n      const Mesh<GET_DIM(data)>& neighbor_mesh,                              \\\n      const Element<GET_DIM(data)>& element,                                 \\\n      const Mesh<GET_DIM(data)>& subcell_mesh, size_t number_of_ghost_zones, \\\n      const DirectionalIdMap<                                                \\\n          GET_DIM(data), std::optional<intrp::Irregular<GET_DIM(data)>>>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define GET_INSERT(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                               \\\n  template void insert_or_update_neighbor_volume_data<GET_INSERT(data)>(     \\\n      gsl::not_null<DirectionalIdMap<GET_DIM(data), GhostData>*>             \\\n          ghost_data_ptr,                                                    \\\n      const DataVector& received_neighbor_subcell_data,                      \\\n      size_t number_of_rdmp_vars_in_buffer,                                  \\\n      const DirectionalId<GET_DIM(data)>& directional_element_id,            \\\n      const Mesh<GET_DIM(data)>& neighbor_mesh,                              \\\n      const Element<GET_DIM(data)>& element,                                 \\\n      const Mesh<GET_DIM(data)>& subcell_mesh, size_t number_of_ghost_zones, \\\n      const DirectionalIdMap<                                                \\\n          GET_DIM(data), std::optional<intrp::Irregular<GET_DIM(data)>>>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (true, false))\n\n#undef INSTANTIATION\n#undef GET_DIM\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/NeighborRdmpAndVolumeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Check whether `neighbor_subcell_data` is FD or DG, and either insert\n * or copy into `ghost_data_ptr` the FD data (projecting if\n * `neighbor_subcell_data` is DG data).\n *\n * This is intended to be used during a rollback from DG to make sure neighbor\n * data is projected to the FD grid.\n */\ntemplate <bool InsertIntoMap, size_t Dim>\nvoid insert_or_update_neighbor_volume_data(\n    gsl::not_null<DirectionalIdMap<Dim, GhostData>*> ghost_data_ptr,\n    const DataVector& neighbor_subcell_data,\n    const size_t number_of_rdmp_vars_in_buffer,\n    const DirectionalId<Dim>& directional_element_id,\n    const Mesh<Dim>& neighbor_mesh, const Element<Dim>& element,\n    const Mesh<Dim>& subcell_mesh, size_t number_of_ghost_zones,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        Neighbor_dg_to_fd_interpolants);\n\n/*!\n * \\brief Check whether the neighbor sent is DG volume or FD ghost data, and\n * orient project DG volume data if necessary.\n *\n * This is intended to be used by the `ReceiveDataForReconstruction` action.\n */\ntemplate <size_t Dim>\nvoid insert_neighbor_rdmp_and_volume_data(\n    gsl::not_null<RdmpTciData*> rdmp_tci_data_ptr,\n    gsl::not_null<DirectionalIdMap<Dim, GhostData>*> ghost_data_ptr,\n    const DataVector& received_neighbor_subcell_data,\n    size_t number_of_rdmp_vars,\n    const DirectionalId<Dim>& directional_element_id,\n    const Mesh<Dim>& neighbor_mesh, const Element<Dim>& element,\n    const Mesh<Dim>& subcell_mesh, size_t number_of_ghost_zones,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        neighbor_dg_to_fd_interpolants);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nnamespace db {\nclass Access;\n}  // namespace db\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Invoked in directions where the neighbor is doing subcell, this\n * function computes the neighbor data on the mortar via reconstruction on\n * nearest neighbor subcells.\n *\n * A list of all the directions that are doing subcell is created and then\n * passed to the mutator\n * `Metavariables::SubcellOptions::DgComputeSubcellNeighborPackagedData::apply`,\n * which must return a\n *\n * \\code\n *  DirectionalIdMap<volume_dim, DataVector>\n * \\endcode\n *\n * which holds the reconstructed `dg_packaged_data` on the face (stored in the\n * `DataVector`) for the boundary correction. A\n * `std::vector<DirectionalId<volume_dim>>`\n * holding the list of mortars that need to be reconstructed to is passed in as\n * the last argument to\n * `Metavariables::SubcellOptions::DgComputeSubcellNeighborPackagedData::apply`.\n */\ntemplate <size_t VolumeDim, typename DgComputeSubcellNeighborPackagedData>\nvoid neighbor_reconstructed_face_solution(gsl::not_null<db::Access*> box);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/NeighborReconstructedFaceSolution.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp\"\n\n#include <cstddef>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\ntemplate <size_t VolumeDim, typename DgComputeSubcellNeighborPackagedData>\nvoid neighbor_reconstructed_face_solution(\n    const gsl::not_null<db::Access*> box) {\n  std::vector<DirectionalId<VolumeDim>> mortars_to_reconstruct_to{};\n  {\n    const auto& mortar_infos =\n        db::get<evolution::dg::Tags::MortarInfo<VolumeDim>>(*box);\n    const auto& mortar_data =\n        db::get<evolution::dg::Tags::MortarData<VolumeDim>>(*box);\n    for (const auto& [mortar_id, data] : mortar_data) {\n      if (mortar_infos.at(mortar_id).time_stepping_policy() ==\n              TimeSteppingPolicy::EqualRate and\n          not data.neighbor().mortar_data.has_value()) {\n        mortars_to_reconstruct_to.push_back(mortar_id);\n      }\n    }\n  }\n  DirectionalIdMap<VolumeDim, DataVector> neighbor_reconstructed_evolved_vars =\n      DgComputeSubcellNeighborPackagedData::apply(*box,\n                                                  mortars_to_reconstruct_to);\n  ASSERT(neighbor_reconstructed_evolved_vars.size() ==\n             mortars_to_reconstruct_to.size(),\n         \"Should have reconstructed \"\n             << mortars_to_reconstruct_to.size() << \" sides but reconstructed \"\n             << neighbor_reconstructed_evolved_vars.size() << \" sides.\");\n  const Mesh<VolumeDim>& dg_mesh = db::get<domain::Tags::Mesh<VolumeDim>>(*box);\n  db::mutate<evolution::dg::Tags::MortarData<VolumeDim>>(\n      [&](const gsl::not_null<DirectionalIdMap<\n              VolumeDim, evolution::dg::MortarDataHolder<VolumeDim>>*>\n              mortar_data) {\n        for (const auto& mortar_id : mortars_to_reconstruct_to) {\n          ASSERT(neighbor_reconstructed_evolved_vars.find(mortar_id) !=\n                     neighbor_reconstructed_evolved_vars.end(),\n                 \"Could not find mortar id \" << mortar_id\n                                             << \" in reconstructed data map.\");\n          auto& neighbor_data = mortar_data->at(mortar_id).neighbor();\n          neighbor_data.mortar_data =\n              std::move(neighbor_reconstructed_evolved_vars.at(mortar_id));\n          neighbor_data.mortar_mesh =\n              dg_mesh.slice_away(mortar_id.direction().dimension());\n        }\n      },\n      box);\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/NeighborTciDecision.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/NeighborTciDecision.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\ntemplate <size_t Dim>\nvoid neighbor_tci_decision(\n    const gsl::not_null<db::Access*> box,\n    const DirectionalId<Dim>& directional_element_id,\n    const evolution::dg::BoundaryData<Dim>& neighbor_data) {\n  db::mutate<subcell::Tags::NeighborTciDecisions<Dim>>(\n      [&](const auto neighbor_tci_decisions_ptr) {\n        ASSERT(neighbor_tci_decisions_ptr->contains(directional_element_id),\n               \"The NeighborTciDecisions tag does not contain the neighbor \"\n                   << directional_element_id);\n        neighbor_tci_decisions_ptr->at(directional_element_id) =\n            neighbor_data.tci_status;\n      },\n      box);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                \\\n  template void neighbor_tci_decision(                        \\\n      gsl::not_null<db::Access*> box,                         \\\n      const DirectionalId<DIM(data)>& directional_element_id, \\\n      const evolution::dg::BoundaryData<DIM(data)>& neighbor_data);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/NeighborTciDecision.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass DirectionalId;\nnamespace db {\nclass Access;\n}  // namespace db\nnamespace evolution::dg {\ntemplate <size_t Dim>\nstruct BoundaryData;\n}  // namespace evolution::dg\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Copies the neighbors' TCI decisions into\n * `subcell::Tags::NeighborTciDecisions<Dim>`\n */\ntemplate <size_t Dim>\nvoid neighbor_tci_decision(\n    gsl::not_null<db::Access*> box,\n    const DirectionalId<Dim>& directional_element_id,\n    const evolution::dg::BoundaryData<Dim>& neighbor_data);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/PerssonTci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Filtering.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell::detail {\ntemplate <size_t Dim>\nbool persson_tci_impl(const gsl::not_null<DataVector*> filtered_component,\n                      const DataVector& component, const Mesh<Dim>& dg_mesh,\n                      const double alpha, const size_t num_highest_modes) {\n  ASSERT(component.size() == dg_mesh.number_of_grid_points(),\n         \"The tensor components being checked must have the same number of \"\n         \"grid points as the DG mesh. The tensor has \"\n             << component.size() << \" grid points while the DG mesh has \"\n             << dg_mesh.number_of_grid_points() << \" grid points.\");\n\n  const Matrix identity{};\n  for (size_t d = 0; d < Dim; ++d) {\n    auto matrices = make_array<Dim>(std::cref(identity));\n    gsl::at(matrices, d) = Spectral::filtering::zero_lowest_modes(\n        dg_mesh.slice_through(d), dg_mesh.extents(d) - num_highest_modes);\n    apply_matrices(filtered_component, matrices, component, dg_mesh.extents());\n\n    //\n    // Note by Yoonsoo Kim, Oct 2021 :\n    //\n    // The original implementation was\n    //\n    // ```\n    // if (l2Norm(*filtered_component) <= zero_cutoff * l2Norm(component)) {\n    //   continue;\n    // }\n    // if (std::log10(l2Norm(*filtered_component) / l2Norm(component)) >\n    //     -alpha * std::log10(static_cast<double>(dg_mesh.extents(d) - 1))) {\n    //   return true;\n    // }\n    // ```\n    //\n    // This form requires the extra use of `zero_cutoff` to prevent log function\n    // acting on zero or a very small number, and also can be problematic if the\n    // l2Norm of component is computed to be zero (if U < ~10^{-160}). The\n    // evaluation of the TCI criteria is switched into a mathematically\n    // equivalent but less error-prone form using power instead of log.\n    //\n    // Below is the benchmark result for a 3D DG element with 5 grid points per\n    // dimension:\n    // -------------------------------------------------------------------------\n    //                                                 Non-troubled    Troubled\n    //                                                   element        element\n    // -------------------------------------------------------------------------\n    // 1. previous implementation using log10            1262 ns         224 ns\n    // 2. new implementation with pow<double,double>     1091 ns         197 ns\n    // 3. using templated pow<int,int>                   1015 ns         174 ns\n    // -------------------------------------------------------------------------\n    //\n    // Case 3 requires the Persson alpha exponent to be a compile time\n    // (hard-coded) integer, which may be too restrictive. Given that switching\n    // to pow<double,double> already provides a certain amount of speed-up, it\n    // would be sufficient to stick to the case 2 for now.\n    //\n\n    const double component_norm{l2Norm(component)};\n\n    if (pow(dg_mesh.extents(d) - num_highest_modes, alpha) *\n            l2Norm(*filtered_component) >\n        component_norm) {\n      return true;\n    }\n  }\n  return false;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                     \\\n  template bool persson_tci_impl(                                  \\\n      gsl::not_null<DataVector*> filtered_component,               \\\n      const DataVector& component, const Mesh<DIM(data)>& dg_mesh, \\\n      double alpha, size_t num_highest_modes);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg::subcell::detail\n"
  },
  {
    "path": "src/Evolution/DgSubcell/PerssonTci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace detail {\ntemplate <size_t Dim>\nbool persson_tci_impl(gsl::not_null<DataVector*> filtered_component,\n                      const DataVector& component, const Mesh<Dim>& dg_mesh,\n                      double alpha, size_t num_highest_modes);\n}  // namespace detail\n\n/*!\n * \\brief Troubled cell indicator using the idea of spectral falloff by\n *    \\cite Persson2006sub\n *\n * Consider a discontinuity sensing quantity \\f$U\\f$, which is typically a\n * scalar but could be a tensor of any rank. Let \\f$U\\f$ have the 1d spectral\n * decomposition (generalization to higher-dimensional tensor product bases is\n * done dimension-by-dimension):\n *\n * \\f{align*}{\n *   U(x)=\\sum_{i=0}^{N}c_i P_i(x),\n * \\f}\n *\n * where \\f$P_i(x)\\f$ are the basis functions, in our case the Legendre\n * polynomials, and \\f$c_i\\f$ are the spectral coefficients. We then define a\n * filtered solution \\f$\\hat{U}\\f$ as\n *\n * \\f{align*}{\n *   \\hat{U}(x)=\\sum_{i=N+1-M}^{N} c_i P_i(x).\n * \\f}\n *\n * where $M$ is the number of highest modes to include in the filtered solution.\n *\n * Note that when an exponential filter is being used to deal with aliasing,\n * lower modes can be included in \\f$\\hat{U}\\f$. The main goal of \\f$\\hat{U}\\f$\n * is to measure how much power is in the highest modes, which are the modes\n * responsible for Gibbs phenomena.\n *\n * A cell is troubled if\n *\n * \\f{align*}{\n *    \\frac{(\\hat{U}, \\hat{U})}{(U, U)} > (N + 1 - M)^{-\\alpha}\n * \\f}\n *\n * where \\f$(\\cdot,\\cdot)\\f$ is an inner product, which we take to be the\n * Euclidean \\f$L_2\\f$ norm (i.e. we do not divide by the number of grid points\n * since that cancels out anyway) computed as\n *\n * \\f{align*}{\n *    (U, U) \\equiv \\sqrt{\\sum_{i=0}^N U_i^2}\n * \\f}\n *\n * where $U_i$ are nodal values of the quantity $U$ at grid points.\n *\n * Typically, \\f$\\alpha=4.0\\f$ and $M=1$ is a good choice.\n *\n */\ntemplate <size_t Dim, typename SymmList, typename IndexList>\nbool persson_tci(const Tensor<DataVector, SymmList, IndexList>& tensor,\n                 const Mesh<Dim>& dg_mesh, const double alpha,\n                 const size_t num_highest_modes) {\n  DataVector filtered_component(dg_mesh.number_of_grid_points());\n  for (size_t component_index = 0; component_index < tensor.size();\n       ++component_index) {\n    if (detail::persson_tci_impl(make_not_null(&filtered_component),\n                                 tensor[component_index], dg_mesh, alpha,\n                                 num_highest_modes)) {\n      return true;\n    }\n  }\n  return false;\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/PrepareNeighborData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Add local data for our and our neighbor's relaxed discrete maximum\n * principle troubled-cell indicator, and for reconstruction.\n *\n * The local maximum and minimum of the evolved variables is added to\n * `Tags::DataForRdmpTci` for the RDMP TCI. Then the\n * data needed by neighbor elements to do reconstruction on the FD grid is sent.\n * The data to be sent is computed in the mutator\n * `Metavariables::SubcellOptions::GhostVariables`, which returns a\n * `Variables` of the tensors to send to the neighbors. The main reason for\n * having the mutator `GhostVariables` is to allow sending primitive or\n * characteristic variables for reconstruction.\n *\n * \\note If all neighbors are using DG then we send our DG volume data _without_\n * orienting it. This elides the expense of projection and slicing. If any\n * neighbors are doing FD, we project and slice to all neighbors. A future\n * optimization would be to measure the cost of slicing data, and figure out how\n * many neighbors need to be doing FD before it's worth projecting and slicing\n * vs. just projecting to the ghost cells. Another optimization is to always\n * send DG volume data to neighbors that we think are doing DG, so that we can\n * elide any slicing or projection cost in that direction.\n */\ntemplate <typename Metavariables, typename DbTagsList, size_t Dim>\nvoid prepare_neighbor_data(\n    const gsl::not_null<DirectionMap<Dim, DataVector>*>\n        all_neighbor_data_for_reconstruction,\n    const gsl::not_null<std::optional<Mesh<Dim>>*> ghost_data_mesh,\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    [[maybe_unused]] const Variables<db::wrap_tags_in<\n        ::Tags::Flux, typename Metavariables::system::flux_variables,\n        tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes) {\n  const Mesh<Dim>& dg_mesh = db::get<::domain::Tags::Mesh<Dim>>(*box);\n  const Mesh<Dim>& subcell_mesh =\n      db::get<::evolution::dg::subcell::Tags::Mesh<Dim>>(*box);\n  const auto& element = db::get<::domain::Tags::Element<Dim>>(*box);\n\n  const std::unordered_set<Direction<Dim>>& directions_to_slice =\n      element.internal_boundaries();\n\n  const auto& neighbor_meshes = db::get<domain::Tags::NeighborMesh<Dim>>(*box);\n  const subcell::RdmpTciData& rdmp_data =\n      db::get<subcell::Tags::DataForRdmpTci>(*box);\n  const ::fd::DerivativeOrder fd_derivative_order =\n      get<evolution::dg::subcell::Tags::SubcellOptions<Dim>>(*box)\n          .finite_difference_derivative_order();\n  const size_t rdmp_size = rdmp_data.max_variables_values.size() +\n                           rdmp_data.min_variables_values.size();\n  const size_t extra_size_for_ghost_data =\n      (fd_derivative_order == ::fd::DerivativeOrder::Two\n           ? 0\n           : volume_fluxes.size());\n\n  if (DataVector ghost_variables =\n          [&box, &extra_size_for_ghost_data, &rdmp_size, &volume_fluxes]() {\n            if (extra_size_for_ghost_data == 0) {\n              return db::mutate_apply(\n                  typename Metavariables::SubcellOptions::GhostVariables{}, box,\n                  rdmp_size);\n            } else {\n              DataVector ghost_vars = db::mutate_apply(\n                  typename Metavariables::SubcellOptions::GhostVariables{}, box,\n                  rdmp_size + extra_size_for_ghost_data);\n              std::copy(\n                  volume_fluxes.data(),\n                  std::next(volume_fluxes.data(),\n                            static_cast<std::ptrdiff_t>(volume_fluxes.size())),\n                  std::prev(ghost_vars.end(),\n                            static_cast<std::ptrdiff_t>(\n                                rdmp_size + extra_size_for_ghost_data)));\n              return ghost_vars;\n            }\n          }();\n      alg::all_of(neighbor_meshes,\n                  [](const auto& directional_element_id_and_mesh) {\n                    ASSERT(directional_element_id_and_mesh.second.basis(0) !=\n                               Spectral::Basis::Chebyshev,\n                           \"Don't yet support Chebyshev basis with DG-FD\");\n                    return directional_element_id_and_mesh.second.basis(0) ==\n                           Spectral::Basis::Legendre;\n                  })) {\n    *ghost_data_mesh = dg_mesh;\n    const size_t total_to_slice = directions_to_slice.size();\n    size_t slice_count = 0;\n    for (const auto& direction : directions_to_slice) {\n      ++slice_count;\n      // Move instead of copy on the last iteration. Elides a memory\n      // allocation and copy.\n      [[maybe_unused]] const auto insert_result =\n          UNLIKELY(slice_count == total_to_slice)\n              ? all_neighbor_data_for_reconstruction->insert_or_assign(\n                    direction, std::move(ghost_variables))\n              : all_neighbor_data_for_reconstruction->insert_or_assign(\n                    direction, ghost_variables);\n      ASSERT(all_neighbor_data_for_reconstruction->size() == total_to_slice or\n                 insert_result.second,\n             \"Failed to insert the neighbor data in direction \" << direction);\n    }\n  } else {\n    *ghost_data_mesh = subcell_mesh;\n    const size_t ghost_zone_size =\n        Metavariables::SubcellOptions::ghost_zone_size(*box);\n\n    const DataVector data_to_project{};\n    make_const_view(make_not_null(&data_to_project), ghost_variables, 0,\n                    ghost_variables.size() - rdmp_size);\n    const DataVector projected_data = evolution::dg::subcell::fd::project(\n        data_to_project, dg_mesh, subcell_mesh.extents());\n    typename evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<\n        Dim>::type directions_not_to_slice{};\n    for (const auto& [directional_element_id, _] :\n         db::get<evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<\n             Dim>>(*box)) {\n      directions_not_to_slice[directional_element_id] = std::nullopt;\n    }\n    *all_neighbor_data_for_reconstruction = subcell::slice_data(\n        projected_data, db::get<subcell::Tags::Mesh<Dim>>(*box).extents(),\n        ghost_zone_size, directions_to_slice, rdmp_size,\n        directions_not_to_slice);\n    for (const auto& [directional_element_id, interpolator] :\n         db::get<evolution::dg::subcell::Tags::InterpolatorsFromDgToNeighborFd<\n             Dim>>(*box)) {\n      // NOTE: no orienting is needed, that's done by the interpolation.\n      ASSERT(interpolator.has_value(),\n             \"All interpolators must have values. The std::optional is used \"\n             \"for some local optimizations.\");\n      DataVector& data_in_direction = all_neighbor_data_for_reconstruction->at(\n          directional_element_id.direction());\n      gsl::span result_in_direction{data_in_direction.data(),\n                                    data_in_direction.size() - rdmp_size};\n      interpolator->interpolate(\n          make_not_null(&result_in_direction),\n          {data_to_project.data(), data_to_project.size()});\n    }\n  }\n\n  // Note: The RDMP TCI data must be filled by the TCIs before getting to this\n  // call. That means once in the initial data and then in both the DG and FD\n  // TCIs.\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    // Add the RDMP TCI data to what we will be sending.\n    // Note that this is added _after_ the reconstruction data has been\n    // re-oriented (in the case where we have neighbors doing FD).\n    DataVector& neighbor_data =\n        all_neighbor_data_for_reconstruction->at(direction);\n    std::copy(rdmp_data.max_variables_values.begin(),\n              rdmp_data.max_variables_values.end(),\n              std::prev(neighbor_data.end(), static_cast<int>(rdmp_size)));\n    std::copy(\n        rdmp_data.min_variables_values.begin(),\n        rdmp_data.min_variables_values.end(),\n        std::prev(neighbor_data.end(),\n                  static_cast<int>(rdmp_data.min_variables_values.size())));\n  }\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Projection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Projection.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Evolution/DgSubcell/Matrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell::fd {\nnamespace detail {\ntemplate <size_t Dim>\nvoid project_impl(gsl::span<double> subcell_u,\n                  const gsl::span<const double> dg_u, const Mesh<Dim>& dg_mesh,\n                  const Index<Dim>& subcell_extents) {\n  const Matrix empty{};\n  auto projection_mat = make_array<Dim>(std::cref(empty));\n  for (size_t d = 0; d < Dim; d++) {\n    gsl::at(projection_mat, d) = std::cref(\n        projection_matrix(dg_mesh.slice_through(d), subcell_extents[d],\n                          Spectral::Quadrature::CellCentered));\n  }\n  DataVector result{subcell_u.data(), subcell_u.size()};\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n  const DataVector u{const_cast<double*>(dg_u.data()), dg_u.size()};\n  apply_matrices(make_not_null(&result), projection_mat, u, dg_mesh.extents());\n}\n\n// Project to a face of a Cartesian grid. We reconstruct on a FaceCentered basis\n// in the dimension of the desired face, and on a CellCentered basis in other\n// dimensions.\ntemplate <size_t Dim>\nvoid project_to_faces_impl(gsl::span<double> subcell_u,\n                           const gsl::span<const double> dg_u,\n                           const Mesh<Dim>& dg_mesh,\n                           const Index<Dim>& subcell_extents,\n                           const size_t& face_direction) {\n  const Matrix empty{};\n  auto projection_mat = make_array<Dim>(std::cref(empty));\n  for (size_t d = 0; d < Dim; d++) {\n    if (d == face_direction) {\n      gsl::at(projection_mat, d) = std::cref(\n          projection_matrix(dg_mesh.slice_through(d), subcell_extents[d],\n                            Spectral::Quadrature::FaceCentered));\n    } else {\n      gsl::at(projection_mat, d) = std::cref(\n          projection_matrix(dg_mesh.slice_through(d), subcell_extents[d],\n                            Spectral::Quadrature::CellCentered));\n    }\n  }\n  DataVector result{subcell_u.data(), subcell_u.size()};\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n  const DataVector u{const_cast<double*>(dg_u.data()), dg_u.size()};\n  apply_matrices(make_not_null(&result), projection_mat, u, dg_mesh.extents());\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nvoid project(const gsl::not_null<DataVector*> subcell_u, const DataVector& dg_u,\n             const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents) {\n  ASSERT(dg_u.size() % dg_mesh.number_of_grid_points() == 0,\n         \"The vector dg_u must have size that is a multiple of the number of \"\n         \"grid points \"\n             << dg_mesh.number_of_grid_points() << \" but got \" << dg_u.size());\n  subcell_u->destructive_resize(subcell_extents.product() * dg_u.size() /\n                                dg_mesh.number_of_grid_points());\n  detail::project_impl(gsl::span<double>{subcell_u->data(), subcell_u->size()},\n                       gsl::span<const double>{dg_u.data(), dg_u.size()},\n                       dg_mesh, subcell_extents);\n}\n\ntemplate <size_t Dim>\nDataVector project(const DataVector& dg_u, const Mesh<Dim>& dg_mesh,\n                   const Index<Dim>& subcell_extents) {\n  ASSERT(dg_u.size() % dg_mesh.number_of_grid_points() == 0,\n         \"The vector dg_u must have size that is a multiple of the number of \"\n         \"grid points \"\n             << dg_mesh.number_of_grid_points() << \" but got \" << dg_u.size());\n  DataVector subcell_u{};\n  project(&subcell_u, dg_u, dg_mesh, subcell_extents);\n  return subcell_u;\n}\n\ntemplate <size_t Dim>\nvoid project_to_faces(const gsl::not_null<DataVector*> subcell_u,\n                      const DataVector& dg_u, const Mesh<Dim>& dg_mesh,\n                      const Index<Dim>& subcell_extents,\n                      const size_t& face_direction) {\n  ASSERT(dg_u.size() % dg_mesh.number_of_grid_points() == 0,\n         \"The vector dg_u must have size that is a multiple of the number of \"\n         \"grid points \"\n             << dg_mesh.number_of_grid_points() << \" but got \" << dg_u.size());\n  subcell_u->destructive_resize(subcell_extents.product() * dg_u.size() /\n                                dg_mesh.number_of_grid_points());\n  detail::project_to_faces_impl(\n      gsl::span<double>{subcell_u->data(), subcell_u->size()},\n      gsl::span<const double>{dg_u.data(), dg_u.size()}, dg_mesh,\n      subcell_extents, face_direction);\n}\n\ntemplate <size_t Dim>\nDataVector project_to_faces(const DataVector& dg_u, const Mesh<Dim>& dg_mesh,\n                            const Index<Dim>& subcell_extents,\n                            const size_t& face_direction) {\n  ASSERT(dg_u.size() % dg_mesh.number_of_grid_points() == 0,\n         \"The vector dg_u must have size that is a multiple of the number of \"\n         \"grid points \"\n             << dg_mesh.number_of_grid_points() << \" but got \" << dg_u.size());\n  DataVector subcell_u{};\n  project_to_faces(&subcell_u, dg_u, dg_mesh, subcell_extents, face_direction);\n  return subcell_u;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template DataVector project(const DataVector&, const Mesh<DIM(data)>&,       \\\n                              const Index<DIM(data)>&);                        \\\n  template void project(gsl::not_null<DataVector*>, const DataVector&,         \\\n                        const Mesh<DIM(data)>&, const Index<DIM(data)>&);      \\\n  template void detail::project_impl(gsl::span<double> subcell_u,              \\\n                                     const gsl::span<const double> dg_u,       \\\n                                     const Mesh<DIM(data)>& dg_mesh,           \\\n                                     const Index<DIM(data)>& subcell_extents); \\\n  template DataVector project_to_faces(                                        \\\n      const DataVector&, const Mesh<DIM(data)>&, const Index<DIM(data)>&,      \\\n      const size_t&);                                                          \\\n  template void project_to_faces(gsl::not_null<DataVector*>,                   \\\n                                 const DataVector&, const Mesh<DIM(data)>&,    \\\n                                 const Index<DIM(data)>&, const size_t&);      \\\n  template void detail::project_to_faces_impl(                                 \\\n      gsl::span<double> subcell_u, const gsl::span<const double> dg_u,         \\\n      const Mesh<DIM(data)>& dg_mesh, const Index<DIM(data)>& subcell_extents, \\\n      const size_t& face_direction);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Projection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Index;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell::fd {\nnamespace detail {\ntemplate <size_t Dim>\nvoid project_impl(gsl::span<double> subcell_u, gsl::span<const double> dg_u,\n                  const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents);\ntemplate <size_t Dim>\nvoid project_to_faces_impl(gsl::span<double> subcell_u,\n                           gsl::span<const double> dg_u,\n                           const Mesh<Dim>& dg_mesh,\n                           const Index<Dim>& subcell_extents,\n                           const size_t& face_direction);\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup DgSubcellGroup\n * \\brief Project the variable `dg_u` onto the subcell grid with extents\n * `subcell_extents`.\n *\n * \\note In the return-by-`gsl::not_null` with `Variables` interface, the\n * `SubcellTagList` and the `DgtagList` must be the same when all tag prefixes\n * are removed.\n */\ntemplate <size_t Dim>\nDataVector project(const DataVector& dg_u, const Mesh<Dim>& dg_mesh,\n                   const Index<Dim>& subcell_extents);\n\ntemplate <size_t Dim>\nvoid project(gsl::not_null<DataVector*> subcell_u, const DataVector& dg_u,\n             const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents);\n\ntemplate <typename SubcellTagList, typename DgTagList, size_t Dim>\nvoid project(const gsl::not_null<Variables<SubcellTagList>*> subcell_u,\n             const Variables<DgTagList>& dg_u, const Mesh<Dim>& dg_mesh,\n             const Index<Dim>& subcell_extents) {\n  static_assert(\n      std::is_same_v<\n          tmpl::transform<SubcellTagList,\n                          tmpl::bind<db::remove_all_prefixes, tmpl::_1>>,\n          tmpl::transform<DgTagList,\n                          tmpl::bind<db::remove_all_prefixes, tmpl::_1>>>,\n      \"DG and subcell tag lists must be the same once prefix tags \"\n      \"are removed.\");\n  ASSERT(dg_u.number_of_grid_points() == dg_mesh.number_of_grid_points(),\n         \"dg_u has incorrect size \" << dg_u.number_of_grid_points()\n                                    << \" since the mesh is size \"\n                                    << dg_mesh.number_of_grid_points());\n  if (UNLIKELY(subcell_u->number_of_grid_points() !=\n               subcell_extents.product())) {\n    subcell_u->initialize(subcell_extents.product());\n  }\n  detail::project_impl(gsl::span<double>{subcell_u->data(), subcell_u->size()},\n                       gsl::span<const double>{dg_u.data(), dg_u.size()},\n                       dg_mesh, subcell_extents);\n}\n\ntemplate <typename TagList, size_t Dim>\nVariables<TagList> project(const Variables<TagList>& dg_u,\n                           const Mesh<Dim>& dg_mesh,\n                           const Index<Dim>& subcell_extents) {\n  Variables<TagList> subcell_u(subcell_extents.product());\n  project(make_not_null(&subcell_u), dg_u, dg_mesh, subcell_extents);\n  return subcell_u;\n}\n\ntemplate <size_t Dim>\nDataVector project_to_faces(const DataVector& dg_u, const Mesh<Dim>& dg_mesh,\n                            const Index<Dim>& subcell_extents,\n                            const size_t& face_direction);\n\ntemplate <size_t Dim>\nvoid project_to_faces(gsl::not_null<DataVector*> subcell_u,\n                      const DataVector& dg_u, const Mesh<Dim>& dg_mesh,\n                      const Index<Dim>& subcell_extents,\n                      const size_t& face_direction);\n\ntemplate <typename SubcellTagList, typename DgTagList, size_t Dim>\nvoid project_to_faces(const gsl::not_null<Variables<SubcellTagList>*> subcell_u,\n                      const Variables<DgTagList>& dg_u,\n                      const Mesh<Dim>& dg_mesh,\n                      const Index<Dim>& subcell_extents,\n                      const size_t& face_direction) {\n  static_assert(\n      std::is_same_v<\n          tmpl::transform<SubcellTagList,\n                          tmpl::bind<db::remove_all_prefixes, tmpl::_1>>,\n          tmpl::transform<DgTagList,\n                          tmpl::bind<db::remove_all_prefixes, tmpl::_1>>>,\n      \"DG and subcell tag lists must be the same once prefix tags \"\n      \"are removed.\");\n  ASSERT(dg_u.number_of_grid_points() == dg_mesh.number_of_grid_points(),\n         \"dg_u has incorrect size \" << dg_u.number_of_grid_points()\n                                    << \" since the mesh is size \"\n                                    << dg_mesh.number_of_grid_points());\n  if (UNLIKELY(subcell_u->number_of_grid_points() !=\n               subcell_extents.product())) {\n    subcell_u->initialize(subcell_extents.product());\n  }\n  detail::project_to_faces_impl(\n      gsl::span<double>{subcell_u->data(), subcell_u->size()},\n      gsl::span<const double>{dg_u.data(), dg_u.size()}, dg_mesh,\n      subcell_extents, face_direction);\n}\n\ntemplate <typename TagList, size_t Dim>\nVariables<TagList> project_to_faces(const Variables<TagList>& dg_u,\n                                    const Mesh<Dim>& dg_mesh,\n                                    const Index<Dim>& subcell_extents,\n                                    const size_t& face_direction) {\n  Variables<TagList> subcell_u(subcell_extents.product());\n  project_to_faces(make_not_null(&subcell_u), dg_u, dg_mesh, subcell_extents,\n                   face_direction);\n  return subcell_u;\n}\n/// @}\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace evolution::dg::subcell {\nnamespace {\ntemplate <size_t Dim>\nvoid impl(py::module& m) {  // NOLINT\n  m.def(\"mesh\", py::overload_cast<const Mesh<Dim>&>(&fd::mesh<Dim>),\n        py::arg(\"mesh\"));\n  m.def(\n      \"project\",\n      +[](const DataVector& dg_u, const Mesh<Dim>& dg_mesh,\n          const Index<Dim>& subcell_extents) {\n        return fd::project(dg_u, dg_mesh, subcell_extents);\n      },\n      py::arg(\"dg_u\"), py::arg(\"dg_mesh\"), py::arg(\"subcell_extents\"));\n  m.def(\n      \"reconstruct\",\n      +[](const DataVector& subcell_u_times_projected_det_jac,\n          const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents,\n          const fd::ReconstructionMethod reconstruction_method) {\n        return fd::reconstruct(subcell_u_times_projected_det_jac, dg_mesh,\n                               subcell_extents, reconstruction_method);\n      },\n      py::arg(\"subcell_u_times_projected_det_jac\"), py::arg(\"dg_mesh\"),\n      py::arg(\"subcell_extents\"), py::arg(\"reconstruction_method\"));\n  const auto bind_tensor =\n      [&m]<typename TensorType>(const TensorType& /*meta*/) {\n        m.def(\"persson_tci\",\n              &persson_tci<Dim, typename TensorType::symmetry,\n                           typename TensorType::index_list>,\n              py::arg(\"tensor\"), py::arg(\"dg_mesh\"), py::arg(\"alpha\"),\n              py::arg(\"num_highest_modes\"));\n      };\n  bind_tensor(Scalar<DataVector>{});\n  bind_tensor(tnsr::i<DataVector, Dim>{});\n  bind_tensor(tnsr::I<DataVector, Dim>{});\n}\n}  // namespace\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py::module_::import(\"spectre.Domain.CoordinateMaps\");\n  py::enum_<fd::ReconstructionMethod>(m, \"FdReconstructionMethod\")\n      .value(\"DimByDim\", fd::ReconstructionMethod::DimByDim)\n      .value(\"AllDimsAtOnce\", fd::ReconstructionMethod::AllDimsAtOnce);\n  impl<1>(m);\n  impl<2>(m);\n  impl<3>(m);\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyDgSubcell\")\n\nspectre_python_add_module(\n  DgSubcell\n  MODULE_PATH \"Evolution\"\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\n# spectre_python_headers(\n#   ${LIBRARY}\n#   INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n#   HEADERS\n# )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DgSubcell\n  pybind11::module\n  Spectral\n  Utilities\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyCoordinateMaps\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/Evolution/DgSubcell/RdmpTci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace evolution::dg::subcell {\nint rdmp_tci(const DataVector& max_of_current_variables,\n             const DataVector& min_of_current_variables,\n             const DataVector& max_of_past_variables,\n             const DataVector& min_of_past_variables, const double rdmp_delta0,\n             const double rdmp_epsilon) {\n  const size_t number_of_vars = max_of_current_variables.size();\n  ASSERT(min_of_current_variables.size() == number_of_vars and\n             max_of_past_variables.size() == number_of_vars and\n             min_of_past_variables.size() == number_of_vars,\n         \"The max and min of the current and past variables must all have the \"\n         \"same size.\");\n  for (size_t i = 0; i < number_of_vars; ++i) {\n    using std::max;\n    const double delta = max(\n        rdmp_delta0,\n        rdmp_epsilon * (max_of_past_variables[i] - min_of_past_variables[i]));\n\n    if (max_of_current_variables[i] > max_of_past_variables[i] + delta or\n        min_of_current_variables[i] < min_of_past_variables[i] - delta) {\n      return static_cast<int>(i + 1);\n    }\n  }\n  return 0;\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/RdmpTci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Troubled cell indicator using a relaxed discrete maximum principle,\n * comparing the candidate solution with the past solution in the element and\n * its neighbors.\n *\n * Let the candidate solution be denoted by \\f$u^\\star_{\\alpha}(t^{n+1})\\f$.\n * Then the RDMP requires that\n *\n * \\f{align*}{\n *   \\min_{\\forall\\mathcal{N}}\\left(u_{\\alpha}(t^n)\\right)\n *   - \\delta_\\alpha\n *   \\le\n *   u^\\star_{\\alpha}(t^{n+1})\n *   \\le\n *   \\max_{\\forall\\mathcal{N}}\n *   \\left(u_{\\alpha}(t^n)\\right) + \\delta_\\alpha\n * \\f}\n *\n * where \\f$\\mathcal{N}\\f$ are either the Neumann or Voronoi neighbors and the\n * element itself,  and \\f$\\delta_\\alpha\\f$ is a parameter defined below that\n * relaxes the discrete maximum principle (DMP). When computing\n * \\f$\\max(u_\\alpha)\\f$ and \\f$\\min(u_\\alpha)\\f$ over a DG element that is not\n * using subcells we first project the DG solution to the subcells and then\n * compute the maximum and minimum over *both* the DG grid and the subcell grid.\n * However, when a DG element is using subcells we compute the maximum and\n * minimum of \\f$u_\\alpha(t^n)\\f$ over the subcells only. Note that the maximum\n * and minimum values of \\f$u^\\star_\\alpha\\f$ are always computed over both the\n * DG and the subcell grids, even when using the RDMP to check if the\n * reconstructed DG solution would be admissible.\n *\n * The parameter \\f$\\delta_\\alpha\\f$ is given by:\n *\n * \\f{align*}{\n *   \\delta_\\alpha =\n *   \\max\\left(\\delta_{0},\\epsilon\n *   \\left(\\max_{\\forall\\mathcal{N}}\\left(u_{\\alpha}(t^n)\\right)\n *   - \\min_{\\forall\\mathcal{N}}\\left(u_{\\alpha}(t^n)\\right)\\right)\n *   \\right),\n * \\f}\n *\n * where we typically take \\f$\\delta_{0}=10^{-4}\\f$ and \\f$\\epsilon=10^{-3}\\f$.\n *\n * If all checks are passed and cell is not troubled, returns an integer `0`.\n * Otherwise returns 1-based index of the tag in the input Variables that fails\n * the check. For instance, if we have following two Variables objects as\n * candidate solutions on active and inactive grids\n *\n *  - `Variables<tmpl::list<DgVar1, DgVar2, DgVar3>>`\n *  - `Variables<tmpl::list<SubVar1, SubVar2, SubVar3>>`\n *\n * and TCI flags the second pair `DgVar2` and `SubVar2` not satisfying two-mesh\n * RDMP criteria, returned value is `2` since the second pair of tags failed the\n * check.\n *\n * \\note Once a single pair of tags fails to satisfy the check, checks for the\n * remaining part of the input variables are skipped. In the example above, for\n * instance if the second pair (`DgVar2`,`SubVar2`) is flagged as troubled, the\n * third pair (`DgVar3`,`SubVar3`) is ignored and not checked.\n *\n */\ntemplate <typename... EvolvedVarsTags>\nint rdmp_tci(const Variables<tmpl::list<EvolvedVarsTags...>>&\n                 active_grid_candidate_evolved_vars,\n             const Variables<tmpl::list<Tags::Inactive<EvolvedVarsTags>...>>&\n                 inactive_grid_candidate_evolved_vars,\n             const DataVector& max_of_past_variables,\n             const DataVector& min_of_past_variables, const double rdmp_delta0,\n             const double rdmp_epsilon) {\n  bool cell_is_troubled = false;\n  int rdmp_tci_status = 0;\n  size_t component_index = 0;\n\n  tmpl::for_each<tmpl::list<EvolvedVarsTags...>>(\n      [&active_grid_candidate_evolved_vars, &cell_is_troubled, &component_index,\n       &inactive_grid_candidate_evolved_vars, &max_of_past_variables,\n       &min_of_past_variables, &rdmp_tci_status, rdmp_delta0,\n       rdmp_epsilon](auto tag_v) {\n        if (cell_is_troubled) {\n          return;\n        }\n        using std::max;\n        using std::min;\n\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        using inactive_tag = Tags::Inactive<tag>;\n        const auto& active_var = get<tag>(active_grid_candidate_evolved_vars);\n        const auto& inactive_var =\n            get<inactive_tag>(inactive_grid_candidate_evolved_vars);\n        const size_t number_of_components = active_var.size();\n        ASSERT(number_of_components == inactive_var.size(),\n               \"The active and inactive vars must have the same type of tensor \"\n               \"and therefore the same number of components.\");\n\n        for (size_t tensor_storage_index = 0;\n             tensor_storage_index < number_of_components;\n             ++tensor_storage_index) {\n          ASSERT(not cell_is_troubled,\n                 \"If a cell has already been marked as troubled during the \"\n                 \"RDMP TCI, we should not be continuing to check other \"\n                 \"variables.\");\n          const double max_active = max(active_var[tensor_storage_index]);\n          const double min_active = min(active_var[tensor_storage_index]);\n          const double max_inactive = max(inactive_var[tensor_storage_index]);\n          const double min_inactive = min(inactive_var[tensor_storage_index]);\n          const double delta =\n              max(rdmp_delta0,\n                  rdmp_epsilon * (max_of_past_variables[component_index] -\n                                  min_of_past_variables[component_index]));\n          cell_is_troubled =\n              max(max_active, max_inactive) >\n                  max_of_past_variables[component_index] + delta or\n              min(min_active, min_inactive) <\n                  min_of_past_variables[component_index] - delta;\n          if (cell_is_troubled) {\n            rdmp_tci_status = static_cast<int>(component_index + 1);\n            return;\n          }\n          ++component_index;\n        }\n      });\n  return rdmp_tci_status;\n}\n\n/*!\n * \\brief get the max and min of each component of the active and inactive\n * variables. If `include_inactive_grid` is `false` then only the max over the\n * `active_grid_evolved_vars` for each component is returned.\n */\ntemplate <typename... EvolvedVarsTags>\nstd::pair<DataVector, DataVector> rdmp_max_min(\n    const Variables<tmpl::list<EvolvedVarsTags...>>& active_grid_evolved_vars,\n    const Variables<tmpl::list<Tags::Inactive<EvolvedVarsTags>...>>&\n        inactive_grid_evolved_vars,\n    const bool include_inactive_grid) {\n  DataVector max_of_vars{\n      active_grid_evolved_vars.number_of_independent_components,\n      std::numeric_limits<double>::min()};\n  DataVector min_of_vars{\n      active_grid_evolved_vars.number_of_independent_components,\n      std::numeric_limits<double>::max()};\n  size_t component_index = 0;\n  tmpl::for_each<tmpl::list<EvolvedVarsTags...>>(\n      [&active_grid_evolved_vars, &component_index, &inactive_grid_evolved_vars,\n       &include_inactive_grid, &max_of_vars, &min_of_vars](auto tag_v) {\n        using std::max;\n        using std::min;\n\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const auto& active_var = get<tag>(active_grid_evolved_vars);\n        const size_t number_of_components_in_tensor = active_var.size();\n        for (size_t tensor_storage_index = 0;\n             tensor_storage_index < number_of_components_in_tensor;\n             ++tensor_storage_index) {\n          ASSERT(component_index < max_of_vars.size() and\n                     component_index < min_of_vars.size(),\n                 \"The component index into the variables is out of bounds.\");\n          max_of_vars[component_index] = max(active_var[tensor_storage_index]);\n          min_of_vars[component_index] = min(active_var[tensor_storage_index]);\n          if (include_inactive_grid) {\n            using inactive_tag = Tags::Inactive<tag>;\n            const auto& inactive_var =\n                get<inactive_tag>(inactive_grid_evolved_vars);\n            max_of_vars[component_index] =\n                max(max_of_vars[component_index],\n                    max(inactive_var[tensor_storage_index]));\n            min_of_vars[component_index] =\n                min(min_of_vars[component_index],\n                    min(inactive_var[tensor_storage_index]));\n          }\n          ++component_index;\n        }\n      });\n  return {std::move(max_of_vars), std::move(min_of_vars)};\n}\n\n/*!\n * \\brief Check if the current variables satisfy the RDMP. Returns an integer\n * `0` if cell is not troubled and an integer `i+1` if the `[i]`-th element of\n * the input vector is responsible for failing the RDMP.\n *\n * Let the candidate solution be denoted by \\f$u^\\star_{\\alpha}(t^{n+1})\\f$.\n * Then the RDMP requires that\n *\n * \\f{align*}{\n *   \\min_{\\forall\\mathcal{N}}\\left(u_{\\alpha}(t^n)\\right)\n *   - \\delta_\\alpha\n *   \\le\n *   u^\\star_{\\alpha}(t^{n+1})\n *   \\le\n *   \\max_{\\forall\\mathcal{N}}\n *   \\left(u_{\\alpha}(t^n)\\right) + \\delta_\\alpha\n * \\f}\n *\n * where \\f$\\mathcal{N}\\f$ are either the Neumann or Voronoi neighbors and the\n * element itself,  and \\f$\\delta_\\alpha\\f$ is a parameter defined below that\n * relaxes the discrete maximum principle (DMP). When computing\n * \\f$\\max(u_\\alpha)\\f$ and \\f$\\min(u_\\alpha)\\f$ over a DG element that is not\n * using subcells we first project the DG solution to the subcells and then\n * compute the maximum and minimum over *both* the DG grid and the subcell grid.\n * However, when a DG element is using subcells we compute the maximum and\n * minimum of \\f$u_\\alpha(t^n)\\f$ over the subcells only. Note that the maximum\n * and minimum values of \\f$u^\\star_\\alpha\\f$ are always computed over both the\n * DG and the subcell grids, even when using the RDMP to check if the\n * reconstructed DG solution would be admissible.\n *\n * The parameter \\f$\\delta_\\alpha\\f$ is given by:\n *\n * \\f{align*}{\n *   \\delta_\\alpha =\n *   \\max\\left(\\delta_{0},\\epsilon\n *   \\left(\\max_{\\forall\\mathcal{N}}\\left(u_{\\alpha}(t^n)\\right)\n *   - \\min_{\\forall\\mathcal{N}}\\left(u_{\\alpha}(t^n)\\right)\\right)\n *   \\right),\n * \\f}\n *\n * where we typically take \\f$\\delta_{0}=10^{-4}\\f$ and \\f$\\epsilon=10^{-3}\\f$.\n *\n * If all checks are passed and cell is not troubled, returns an integer `0`.\n * Otherwise returns an 1-based index of the element in the input\n * `DataVector` that fails the check.\n *\n * e.g. Suppose we have three variables to check RDMP so that\n * `max_of_current_variables.size() == 3`. If RDMP TCI flags\n * `max_of_current_variables[1]`, `min_of_current_variables[1]`, .. (and so on)\n * as troubled, returned integer value is `2`.\n *\n * Once cell is marked as troubled, checks for the remaining part of the input\n * `std::vector`s are skipped. In the example above, for instance if `[1]`-th\n * component of inputs is flagged as troubled, checking the remaining index\n * `[2]` is skipped.\n *\n */\nint rdmp_tci(const DataVector& max_of_current_variables,\n             const DataVector& min_of_current_variables,\n             const DataVector& max_of_past_variables,\n             const DataVector& min_of_past_variables, double rdmp_delta0,\n             double rdmp_epsilon);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/RdmpTciData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n\nnamespace evolution::dg::subcell {\nvoid pup(PUP::er& p, RdmpTciData& rdmp_tci_data) {  // NOLINT\n  p | rdmp_tci_data.max_variables_values;\n  p | rdmp_tci_data.min_variables_values;\n}\n\nvoid operator|(PUP::er& p, RdmpTciData& rdmp_tci_data) {  // NOLINT\n  pup(p, rdmp_tci_data);\n}\n\nbool operator==(const RdmpTciData& lhs, const RdmpTciData& rhs) {\n  return lhs.max_variables_values == rhs.max_variables_values and\n         lhs.min_variables_values == rhs.min_variables_values;\n}\n\nbool operator!=(const RdmpTciData& lhs, const RdmpTciData& rhs) {\n  return not(lhs == rhs);\n}\n\nstd::ostream& operator<<(std::ostream& os, const RdmpTciData& t) {\n  using ::operator<<;\n  return os << t.max_variables_values << '\\n' << t.min_variables_values;\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/RdmpTciData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/// Holds data needed for the relaxed discrete maximum principle\n/// troubled-cell indicator.\nstruct RdmpTciData {\n  DataVector max_variables_values{};\n  DataVector min_variables_values{};\n};\n\nvoid pup(PUP::er& p, RdmpTciData& rdmp_tci_data);  // NOLINT\n\nvoid operator|(PUP::er& p, RdmpTciData& rdmp_tci_data);  // NOLINT\n\nbool operator==(const RdmpTciData& lhs, const RdmpTciData& rhs);\n\nbool operator!=(const RdmpTciData& lhs, const RdmpTciData& rhs);\n\nstd::ostream& operator<<(std::ostream& os, const RdmpTciData& t);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/ReceiveSubcellDataForDg.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/ReceiveSubcellDataForDg.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <utility>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/MeshForGhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\ntemplate <size_t VolumeDim>\nvoid receive_subcell_data_for_dg(\n    const gsl::not_null<db::Access*> box,\n    const DirectionalId<VolumeDim>& mortar_id,\n    const evolution::dg::BoundaryData<VolumeDim>& received_mortar_data) {\n  db::mutate<subcell::Tags::MeshForGhostData<VolumeDim>,\n             subcell::Tags::GhostDataForReconstruction<VolumeDim>,\n             subcell::Tags::DataForRdmpTci>(\n      [&](const auto mesh_for_ghost_data_ptr, const auto subcell_ghost_data_ptr,\n          const auto rdmp_tci_data_ptr) {\n        const size_t number_of_evolved_vars =\n            rdmp_tci_data_ptr->max_variables_values.size();\n        ASSERT(received_mortar_data.ghost_cell_data.has_value(),\n               \"The subcell mortar data was not sent with mortar id \"\n                   << mortar_id);\n        mesh_for_ghost_data_ptr->insert_or_assign(\n            mortar_id,\n            received_mortar_data.volume_mesh_ghost_cell_data.value());\n        const DataVector& neighbor_ghost_and_subcell_data =\n            received_mortar_data.ghost_cell_data.value();\n        // Compute min and max over neighbors\n        const size_t offset_for_min =\n            neighbor_ghost_and_subcell_data.size() - number_of_evolved_vars;\n        const size_t offset_for_max = offset_for_min - number_of_evolved_vars;\n        for (size_t var_index = 0; var_index < number_of_evolved_vars;\n             ++var_index) {\n          rdmp_tci_data_ptr->max_variables_values[var_index] = std::max(\n              rdmp_tci_data_ptr->max_variables_values[var_index],\n              neighbor_ghost_and_subcell_data[offset_for_max + var_index]);\n          rdmp_tci_data_ptr->min_variables_values[var_index] = std::min(\n              rdmp_tci_data_ptr->min_variables_values[var_index],\n              neighbor_ghost_and_subcell_data[offset_for_min + var_index]);\n        }\n\n        ASSERT(subcell_ghost_data_ptr->find(mortar_id) ==\n                   subcell_ghost_data_ptr->end(),\n               \"The subcell neighbor data is already inserted. Direction: \"\n                   << mortar_id.direction()\n                   << \" with ElementId: \" << mortar_id.id());\n\n        (*subcell_ghost_data_ptr)[mortar_id] = GhostData{1};\n        GhostData& all_ghost_data = subcell_ghost_data_ptr->at(mortar_id);\n        DataVector& neighbor_data =\n            all_ghost_data.neighbor_ghost_data_for_reconstruction();\n        neighbor_data.destructive_resize(\n            neighbor_ghost_and_subcell_data.size() -\n            2 * number_of_evolved_vars);\n\n        // Copy over the neighbor data for reconstruction. We need this\n        // since we might be doing a step unwind and the DG algorithm deletes\n        // the inbox data after lifting the fluxes to the volume.\n        // The std::prev avoids copying over the data for the RDMP TCI, which\n        // is both the maximum and minimum of each evolved variable, so\n        // `2*number_of_evolved_vars` components.\n        std::copy(\n            neighbor_ghost_and_subcell_data.begin(),\n            std::prev(neighbor_ghost_and_subcell_data.end(),\n                      2 * static_cast<std::ptrdiff_t>(number_of_evolved_vars)),\n            neighbor_data.begin());\n      },\n      box);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                     \\\n  template void receive_subcell_data_for_dg(     \\\n      const gsl::not_null<db::Access*> box,      \\\n      const DirectionalId<DIM(data)>& mortar_id, \\\n      const evolution::dg::BoundaryData<DIM(data)>& received_mortar_data);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/ReceiveSubcellDataForDg.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass DirectionalId;\nnamespace db {\nclass Access;\n}  // namespace db\nnamespace evolution::dg {\ntemplate <size_t Dim>\nstruct BoundaryData;\n}  // namespace evolution::dg\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Invoked in directions where the neighbor is doing subcell, this\n * function copies received subcell data into the DataBox.\n *\n * The mesh and data needed for reconstruction are copied over into\n * `subcell::Tags::MeshForGhostData` and\n * `subcell::Tags::GhostDataForReconstruction`.\n * Additionally, the max/min of the evolved variables from neighboring elements\n * that is used for the relaxed discrete maximum principle troubled-cell\n * indicator is combined with the data from the local element and stored in\n * `subcell::Tags::DataForRdmpTci`. We handle the RDMP\n * data now because it is sent in the same buffer as the data for\n * reconstruction.\n */\ntemplate <size_t VolumeDim>\nvoid receive_subcell_data_for_dg(\n    gsl::not_null<db::Access*> box, const DirectionalId<VolumeDim>& mortar_id,\n    const evolution::dg::BoundaryData<VolumeDim>& received_mortar_data);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Reconstruction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Evolution/DgSubcell/Matrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace evolution::dg::subcell::fd {\nnamespace detail {\ntemplate <size_t Dim>\nvoid reconstruct_impl(\n    gsl::span<double> dg_u,\n    const gsl::span<const double> subcell_u_times_projected_det_jac,\n    const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents,\n    const ReconstructionMethod reconstruction_method) {\n  const size_t number_of_components =\n      dg_u.size() / dg_mesh.number_of_grid_points();\n  if (reconstruction_method == ReconstructionMethod::AllDimsAtOnce) {\n    const Matrix& recons_matrix =\n        reconstruction_matrix(dg_mesh, subcell_extents);\n    dgemm_<true>(\n        'N', 'N', recons_matrix.rows(), number_of_components,\n        recons_matrix.columns(), 1.0, recons_matrix.data(),\n        recons_matrix.spacing(), subcell_u_times_projected_det_jac.data(),\n        recons_matrix.columns(), 0.0, dg_u.data(), recons_matrix.rows());\n  } else {\n    ASSERT(reconstruction_method == ReconstructionMethod::DimByDim,\n           \"reconstruction_method must be either DimByDim or AllDimsAtOnce\");\n    // We multiply the last dim first because projection is done with the last\n    // dim last. We do this because it ensures the R(P)==I up to machine\n    // precision.\n    const Matrix empty{};\n    auto recons_matrices = make_array<Dim>(std::cref(empty));\n    for (size_t d = 0; d < Dim; d++) {\n      gsl::at(recons_matrices, d) = std::cref(reconstruction_matrix(\n          dg_mesh.slice_through(d), Index<1>{subcell_extents[d]}));\n    }\n    DataVector result{dg_u.data(), dg_u.size()};\n    const DataVector u{\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        const_cast<double*>(subcell_u_times_projected_det_jac.data()),\n        subcell_u_times_projected_det_jac.size()};\n    apply_matrices(make_not_null(&result), recons_matrices, u, subcell_extents);\n  }\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nvoid reconstruct(const gsl::not_null<DataVector*> dg_u,\n                 const DataVector& subcell_u_times_projected_det_jac,\n                 const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents,\n                 const ReconstructionMethod reconstruction_method) {\n  ASSERT(subcell_u_times_projected_det_jac.size() == subcell_extents.product(),\n         \"Incorrect subcell size of u: \"\n             << subcell_u_times_projected_det_jac.size() << \" but should be \"\n             << subcell_extents.product());\n  dg_u->destructive_resize(dg_mesh.number_of_grid_points());\n  detail::reconstruct_impl(\n      gsl::span<double>{dg_u->data(), dg_u->size()},\n      gsl::span<const double>{subcell_u_times_projected_det_jac.data(),\n                              subcell_u_times_projected_det_jac.size()},\n      dg_mesh, subcell_extents, reconstruction_method);\n}\n\ntemplate <size_t Dim>\nDataVector reconstruct(const DataVector& subcell_u_times_projected_det_jac,\n                       const Mesh<Dim>& dg_mesh,\n                       const Index<Dim>& subcell_extents,\n                       const ReconstructionMethod reconstruction_method) {\n  DataVector dg_u{dg_mesh.number_of_grid_points()};\n  reconstruct(&dg_u, subcell_u_times_projected_det_jac, dg_mesh,\n              subcell_extents, reconstruction_method);\n  return dg_u;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template DataVector reconstruct(const DataVector&, const Mesh<DIM(data)>&,   \\\n                                  const Index<DIM(data)>&,                     \\\n                                  ReconstructionMethod);                       \\\n  template void reconstruct(gsl::not_null<DataVector*>, const DataVector&,     \\\n                            const Mesh<DIM(data)>&, const Index<DIM(data)>&,   \\\n                            ReconstructionMethod);                             \\\n  template void detail::reconstruct_impl(                                      \\\n      gsl::span<double> dg_u, const gsl::span<const double>,                   \\\n      const Mesh<DIM(data)>& dg_mesh, const Index<DIM(data)>& subcell_extents, \\\n      ReconstructionMethod);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Reconstruction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Index;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell::fd {\nnamespace detail {\ntemplate <size_t Dim>\nvoid reconstruct_impl(gsl::span<double> dg_u,\n                      gsl::span<const double> subcell_u_times_projected_det_jac,\n                      const Mesh<Dim>& dg_mesh,\n                      const Index<Dim>& subcell_extents,\n                      ReconstructionMethod reconstruction_method);\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup DgSubcellGroup\n * \\brief reconstruct the variable `subcell_u_times_projected_det_jac` onto the\n * DG grid `dg_mesh`.\n *\n * In general we wish that the reconstruction operator is the pseudo-inverse of\n * the projection operator. On curved meshes this means we either need to\n * compute a (time-dependent) reconstruction and projection matrix on each DG\n * element, or we expand the determinant of the Jacobian on the basis, accepting\n * the aliasing errors from that. We accept the aliasing errors in favor of the\n * significantly reduced computational overhead. This means that the projection\n * and reconstruction operators are only inverses of each other if both operate\n * on \\f$u J\\f$ where \\f$u\\f$ is the variable being projected and \\f$J\\f$ is the\n * determinant of the Jacobian. That is, the matrices are guaranteed to satisfy\n * \\f$\\mathcal{R}(\\mathcal{P}(u J))=u J\\f$. If the mesh is regular Cartesian,\n * then this isn't an issue. Furthermore, if we reconstruct\n * \\f$uJ/\\mathcal{P}(J)\\f$ we again recover the exact DG solution. Doing the\n * latter has the advantage that, in general, we are ideally projecting to the\n * subcells much more often than reconstructing from them (a statement that we\n * would rather use DG more than the subcells).\n */\ntemplate <size_t Dim>\nDataVector reconstruct(const DataVector& subcell_u_times_projected_det_jac,\n                       const Mesh<Dim>& dg_mesh,\n                       const Index<Dim>& subcell_extents,\n                       ReconstructionMethod reconstruction_method);\n\ntemplate <size_t Dim>\nvoid reconstruct(gsl::not_null<DataVector*> dg_u,\n                 const DataVector& subcell_u_times_projected_det_jac,\n                 const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents,\n                 ReconstructionMethod reconstruction_method);\n\ntemplate <typename SubcellTagList, typename DgTagList, size_t Dim>\nvoid reconstruct(const gsl::not_null<Variables<DgTagList>*> dg_u,\n                 const Variables<SubcellTagList>& subcell_u,\n                 const Mesh<Dim>& dg_mesh, const Index<Dim>& subcell_extents,\n                 const ReconstructionMethod reconstruction_method) {\n  ASSERT(subcell_u.number_of_grid_points() == subcell_extents.product(),\n         \"Incorrect subcell size of u: \" << subcell_u.number_of_grid_points()\n                                         << \" but should be \"\n                                         << subcell_extents.product());\n  if (UNLIKELY(dg_u->number_of_grid_points() !=\n               dg_mesh.number_of_grid_points())) {\n    dg_u->initialize(dg_mesh.number_of_grid_points(), 0.0);\n  }\n  detail::reconstruct_impl(\n      gsl::span<double>{dg_u->data(), dg_u->size()},\n      gsl::span<const double>{subcell_u.data(), subcell_u.size()}, dg_mesh,\n      subcell_extents, reconstruction_method);\n}\n\ntemplate <typename TagList, size_t Dim>\nVariables<TagList> reconstruct(\n    const Variables<TagList>& subcell_u, const Mesh<Dim>& dg_mesh,\n    const Index<Dim>& subcell_extents,\n    const ReconstructionMethod reconstruction_method) {\n  Variables<TagList> dg_u(dg_mesh.number_of_grid_points());\n  reconstruct(make_not_null(&dg_u), subcell_u, dg_mesh, subcell_extents,\n              reconstruction_method);\n  return dg_u;\n}\n/// @}\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "src/Evolution/DgSubcell/ReconstructionMethod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace evolution::dg::subcell::fd {\nstd::ostream& operator<<(std::ostream& os, ReconstructionMethod recons_method) {\n  switch (recons_method) {\n    case ReconstructionMethod::DimByDim:\n      return os << \"DimByDim\";\n    case ReconstructionMethod::AllDimsAtOnce:\n      return os << \"AllDimsAtOnce\";\n    default:\n      ERROR(\"Unknown reconstruction method, must be DimByDim or AllDimsAtOnce\");\n  }\n}\n}  // namespace evolution::dg::subcell::fd\n\ntemplate <>\nevolution::dg::subcell::fd::ReconstructionMethod\nOptions::create_from_yaml<evolution::dg::subcell::fd::ReconstructionMethod>::\n    create<void>(const Options::Option& options) {\n  const auto recons_method = options.parse_as<std::string>();\n  if (recons_method == get_output(type::DimByDim)) {\n    return type::DimByDim;\n  } else if (recons_method == get_output(type::AllDimsAtOnce)) {\n    return type::AllDimsAtOnce;\n  } else {\n    PARSE_ERROR(options.context(),\n                \"ReconstructionMethod must be '\"\n                    << get_output(type::DimByDim) << \"', or '\"\n                    << get_output(type::AllDimsAtOnce) << \"'\");\n  }\n}\n"
  },
  {
    "path": "src/Evolution/DgSubcell/ReconstructionMethod.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n#include \"Options/Options.hpp\"\n\nnamespace evolution::dg::subcell::fd {\n/*!\n * \\brief The reconstruction method to use\n */\nenum class ReconstructionMethod {\n  /// Dimension-by-dimension reconstruction assuming a tensor-product basis\n  DimByDim,\n  /// Reconstruct all dimensions at once.\n  AllDimsAtOnce\n};\n\nstd::ostream& operator<<(std::ostream& os, ReconstructionMethod recons_method);\n}  // namespace evolution::dg::subcell::fd\n\n/// \\cond\ntemplate <>\nstruct Options::create_from_yaml<\n    evolution::dg::subcell::fd::ReconstructionMethod> {\n  using type = evolution::dg::subcell::fd::ReconstructionMethod;\n  template <typename Metavariables>\n  static type create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nevolution::dg::subcell::fd::ReconstructionMethod\nOptions::create_from_yaml<evolution::dg::subcell::fd::ReconstructionMethod>::\n    create<void>(const Options::Option& options);\n/// \\endcond\n"
  },
  {
    "path": "src/Evolution/DgSubcell/ReconstructionOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ReconstructionOrder.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace evolution::dg::subcell {\n/// Copies the `reconstruction_order` into the DataBox tag\n/// `evolution::dg::subcell::Tags::ReconstructionOrder` if\n/// `reconstruction_order` has a value.\ntemplate <size_t Dim, typename DbTagsList>\nvoid store_reconstruction_order_in_databox(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    const std::optional<std::array<gsl::span<std::uint8_t>, Dim>>&\n        reconstruction_order) {\n  if (reconstruction_order.has_value()) {\n    db::mutate<evolution::dg::subcell::Tags::ReconstructionOrder<Dim>>(\n        [&reconstruction_order](const auto recons_order_ptr,\n                                const ::Mesh<Dim>& subcell_mesh) {\n          if (UNLIKELY(not recons_order_ptr->has_value())) {\n            (*recons_order_ptr) = typename std::decay_t<\n                decltype(*recons_order_ptr)>::value_type{};\n          }\n          set_number_of_grid_points(\n              make_not_null(&(recons_order_ptr->value())),\n              subcell_mesh.number_of_grid_points());\n          for (size_t d = Dim - 1; d < Dim; --d) {\n            // The data is always in d-varies fastest ordering\n            //\n            // We always copy into the `d=0` buffer, then do transposes later.\n            for (size_t j = 0;\n                 j < subcell_mesh.slice_away(d).number_of_grid_points(); ++j) {\n              for (size_t i = 0; i < subcell_mesh.extents(d); ++i) {\n                get<0>(recons_order_ptr\n                           ->value())[j * subcell_mesh.extents(d) + i] =\n                    gsl::at(reconstruction_order.value(),\n                            d)[j * (subcell_mesh.extents(d) + 2) + i + 1];\n              }\n            }\n            if (d == 1) {\n              // Order is (y, z, x) -> (x, y, z)\n              transpose(make_not_null(&recons_order_ptr->value().get(d)),\n                        get<0>(recons_order_ptr->value()),\n                        subcell_mesh.extents(1) *\n                            (Dim > 2 ? subcell_mesh.extents(2) : 1),\n                        subcell_mesh.number_of_grid_points() /\n                            (subcell_mesh.extents(1) *\n                             (Dim > 2 ? subcell_mesh.extents(2) : 1)));\n            } else if (d == 2) {\n              // Order is (z, x, y) -> (x, y, z)\n              transpose(make_not_null(&recons_order_ptr->value().get(d)),\n                        get<0>(recons_order_ptr->value()),\n                        subcell_mesh.extents(2),\n                        subcell_mesh.number_of_grid_points() /\n                            subcell_mesh.extents(2));\n            }\n            // }\n          }\n        },\n        box, db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(*box));\n  }\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/SetInterpolators.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/ExtractPoint.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/BlockId.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Sets the `intrp::IrregularInterpolant`s for interpolating to ghost\n * zone data at block boundaries.\n *\n * The DG to FD interpolants are at full order of the DG grid. The FD to FD\n * interpolant is piecewise linear with no support for neighboring\n * elements. We will want to use high-order slope-limited FD interpolation in\n * the future, but that requires neighbor communication. A slightly simpler\n * approach would be to use high-order Lagrange interpolation, which still\n * requires neighbor communication but does not require any additional changes\n * to the reconstruction routines to work on non-uniform grids. This is what\n * the Multipatch-MHD code does, relying on the slope limiting from the ghost\n * zones to remove oscillations. I (Nils Deppe) am not sure I love that, but\n * it's worth a try since it should be pretty easy to do.\n *\n * \\warning Currently assumes that neighboring DG/FD elements are on the same\n * refinement level and have the same DG mesh and subcell mesh.\n */\ntemplate <size_t Dim, typename ReconstructorTag>\nstruct SetInterpolators {\n  using return_tags = tmpl::list<\n      evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromDgToNeighborFd<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>,\n      evolution::dg::subcell::Tags::ExtensionDirections<Dim>>;\n  using argument_tags =\n      tmpl::list<::domain::Tags::Element<Dim>, ::domain::Tags::Domain<Dim>,\n                 domain::Tags::Mesh<Dim>, domain::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>,\n                 ::domain::Tags::ElementMap<Dim, Frame::Grid>, ReconstructorTag,\n                 evolution::dg::subcell::Tags::SubcellOptions<Dim>>;\n\n  template <typename ReconstructorType>\n  static void apply(\n      const gsl::not_null<\n          DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>*>\n          interpolators_fd_to_neighbor_fd_ptr,\n      const gsl::not_null<\n          DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>*>\n          interpolators_dg_to_neighbor_fd_ptr,\n      const gsl::not_null<\n          DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>*>\n          interpolators_neighbor_dg_to_fd_ptr,\n      const gsl::not_null<\n          DirectionMap<Dim, interpolators_detail::ExtensionDirection<Dim>>*>\n          extension_direction_ptr,\n      const Element<Dim>& element, const Domain<Dim>& domain,\n      const Mesh<Dim>& my_dg_mesh,\n      // Needs to be updated to support non-uniform h/p-refinement\n      const Mesh<Dim>& neighbor_dg_mesh, const Mesh<Dim>& my_fd_mesh,\n      // Needs to be updated to support non-uniform h/p-refinement\n      const Mesh<Dim>& neighbor_fd_mesh,\n      const ElementMap<Dim, Frame::Grid>& element_map,\n      const ReconstructorType& reconstructor,\n      const evolution::dg::subcell::SubcellOptions& subcell_options) {\n    if (alg::found(subcell_options.only_dg_block_ids(),\n                   element.id().block_id())) {\n      return;\n    }\n    const bool enable_extension_directions =\n        subcell_options.enable_extension_directions();\n    if (enable_extension_directions) {\n      *extension_direction_ptr = {};\n    }  // Initialize the extension directions to empty.\n\n    const size_t number_of_ghost_zones = reconstructor.ghost_zone_size();\n    const size_t my_block_id = element.id().block_id();\n    for (const auto& direction_neighbors_in_direction : element.neighbors()) {\n      const auto& direction = direction_neighbors_in_direction.first;\n      const auto& neighbors_in_direction =\n          direction_neighbors_in_direction.second;\n      for (const ElementId<Dim>& neighbor_id : neighbors_in_direction) {\n        const auto& orientation =\n            neighbors_in_direction.orientation(neighbor_id);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        const size_t neighbor_block_id = neighbor_id.block_id();\n        if (neighbor_block_id == my_block_id) {\n          continue;\n        }\n        const auto& neighbor_block = domain.blocks()[neighbor_block_id];\n        // InterpolatorsFromFdToNeighborFd &\n        // InterpolatorsFromDgToNeighborFd\n        // 1. Compute the grid coordinates of my neighbor's ghost zones.\n        // 2. Compute the element logical coordinates of my neighbor's\n        //    ghost zones.\n        // 3. Create interpolators\n\n        if (not is_isotropic(neighbor_fd_mesh)) {\n          ERROR(\"We assume an isotropic mesh but got \"\n                << neighbor_fd_mesh << \" ElementID is \" << element.id());\n        }\n\n        const auto get_logical_coords = [&element, &neighbor_id, &direction](\n                                            const auto& map,\n                                            const auto& grid_coords) {\n          tnsr::I<DataVector, Dim, Frame::ElementLogical> logical_coords{\n              get<0>(grid_coords).size()};\n          for (size_t i = 0; i < get<0>(grid_coords).size(); ++i) {\n            try {\n              tnsr::I<double, Dim, Frame::ElementLogical> logical_coord =\n                  map.inverse(extract_point(grid_coords, i));\n              for (size_t d = 0; d < Dim; ++d) {\n                logical_coords.get(d)[i] = logical_coord.get(d);\n              }\n            } catch (const std::bad_optional_access& e) {\n              ERROR(\n                  \"Failed to get logical coordinates for neighbor's \"\n                  \"ghost zone grid coordinates. This could be because the \"\n                  \"ghost zones are not in the nearest neighbor but instead in \"\n                  \"the next-to-nearest neighbor. The code assumes all ghost \"\n                  \"zones, even on curved meshes, are in the nearest neighbors. \"\n                  \"The current element is \"\n                  << element.id() << \" and the neighbor id is \" << neighbor_id\n                  << \" in direction \" << direction\n                  << \" The neighbor grid coordinates are \\n\"\n                  << extract_point(grid_coords, i) << \"\\n\");\n            }\n          }\n          return logical_coords;\n        };\n\n        tnsr::I<DataVector, Dim, Frame::Grid> neighbor_grid_ghost_zone_coords{};\n        // Get the neighbor's ghost zone coordinates in the grid\n        // frame.\n        if (const tnsr::I<DataVector, Dim, Frame::ElementLogical>\n                neighbor_logical_ghost_zone_coords =\n                    evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n                        neighbor_fd_mesh, number_of_ghost_zones,\n                        direction_from_neighbor);\n            neighbor_block.is_time_dependent()) {\n          const ElementMap neighbor_element_map(\n              neighbor_id,\n              neighbor_block.moving_mesh_logical_to_grid_map().get_clone());\n          neighbor_grid_ghost_zone_coords =\n              neighbor_element_map(neighbor_logical_ghost_zone_coords);\n        } else {\n          const ElementMap neighbor_element_map(\n              neighbor_id, neighbor_block.stationary_map().get_clone());\n          const tnsr::I<DataVector, Dim, Frame::Inertial>\n              neighbor_inertial_ghost_zone_coords =\n                  neighbor_element_map(neighbor_logical_ghost_zone_coords);\n          for (size_t i = 0; i < Dim; ++i) {\n            neighbor_grid_ghost_zone_coords[i] =\n                neighbor_inertial_ghost_zone_coords[i];\n          }\n        }\n        // Map the ghost zone grid coordinates back to our logical\n        // coordinates.\n        const tnsr::I<DataVector, Dim, Frame::ElementLogical>\n            neighbor_logical_ghost_zone_coords = get_logical_coords(\n                element_map, neighbor_grid_ghost_zone_coords);\n\n        // We need to check if the neighbor's ghost zone coordinates\n        // are in the same element as the current element. If not, we\n        // need to extend the mesh in the direction of the ghost zone\n        // coordinates.\n        // Note, we only need to do this check if we enable extending\n        // the mesh to avoid extrapolation. If not, we simply\n        // allow the extrapolation to happen.\n\n        // 'needs_extension' is set to true if the neighbor's ghost zone\n        // coordinates are outside the current element's mesh and if\n        // we enable extending the mesh.\n        bool needs_extension = false;\n        std::optional<Direction<Dim>> direction_to_extend;\n        if (enable_extension_directions) {\n          for (size_t d = 0; d < Dim; ++d) {\n            // small epsilon of 1e-10 used to ensure we are not unncessarily\n            // flagging as problematic\n            const double ext = 1. - (1. / my_fd_mesh.extents(d)) + 1.e-10;\n            const auto& coords = neighbor_logical_ghost_zone_coords.get(d);\n\n            for (size_t i = 0; i < coords.size(); ++i) {\n              if (std::abs(coords[i]) > ext) {\n                needs_extension = true;\n                Direction<Dim> new_direction = Direction<Dim>{\n                    d, coords[i] > 0 ? Side::Upper : Side::Lower};\n\n                if (!direction_to_extend.has_value()) {\n                  direction_to_extend = new_direction;\n                } else if (direction_to_extend.value() != new_direction) {\n                  ERROR(\"Multiple directions to extend: existing = \"\n                        << direction_to_extend.value()\n                        << \", new = \" << new_direction);\n                }\n                break;  // no reason to check remaining coords.\n              }\n            }\n          }\n        }\n\n        if (needs_extension) {\n          if (!direction_to_extend.has_value()) {\n            ERROR(\n                \"Should have direction to extend if flagged \"\n                \"as problematic!\");\n          }\n          const auto& external_boundaries = element.external_boundaries();\n          if (external_boundaries.find(direction_to_extend.value()) !=\n              external_boundaries.end()) {\n            ERROR(\n                \"Direction to extend is toward the \"\n                \"faces of the Element that are external boundaries.\");\n          }\n\n          auto new_basis = make_array<Dim>(my_fd_mesh.basis(0));\n          auto new_extents = make_array<Dim>(my_fd_mesh.extents(0));\n          auto new_quads = make_array<Dim>(my_fd_mesh.quadrature(0));\n\n          const size_t problematic_dim =\n              direction_to_extend.value().dimension();\n\n          // note we are extending our current volume by including its own ghost\n          // points in the problematic direction (direction to extend)\n          // which means the logical coordinates of the ghost (to be sent)\n          // must be transformed to accommodate the extended mesh in\n          // direction to extend.\n\n          const double rescale_factor =\n              static_cast<double>(my_fd_mesh.extents(problematic_dim)) /\n              (my_fd_mesh.extents(problematic_dim) + number_of_ghost_zones);\n          double translation =\n              static_cast<double>(number_of_ghost_zones) /\n              (my_fd_mesh.extents(problematic_dim) + number_of_ghost_zones);\n          // translation above is based on extending to Upper Side.\n          if (direction_to_extend.value().side() == Side::Lower) {\n            translation *= -1.;\n          }\n          auto new_neighbor_logical_ghost_zone_coords =\n              neighbor_logical_ghost_zone_coords;\n          for (size_t i = 0;\n               i < new_neighbor_logical_ghost_zone_coords[0].size(); ++i) {\n            new_neighbor_logical_ghost_zone_coords.get(problematic_dim)[i] *=\n                rescale_factor;\n            new_neighbor_logical_ghost_zone_coords.get(problematic_dim)[i] -=\n                translation;\n          }\n\n          for (size_t d = 0; d < Dim; ++d) {\n            gsl::at(new_basis, d) = my_fd_mesh.basis(d);\n            gsl::at(new_quads, d) = my_fd_mesh.quadrature(d);\n            if (d == problematic_dim) {\n              gsl::at(new_extents, d) =\n                  my_fd_mesh.extents(d) + number_of_ghost_zones;\n            } else {\n              gsl::at(new_extents, d) = my_fd_mesh.extents(d);\n            }\n          }\n          const Mesh<Dim> new_mesh{new_extents, new_basis, new_quads};\n          (*interpolators_fd_to_neighbor_fd_ptr)[DirectionalId<Dim>{\n              direction, neighbor_id}] = intrp::Irregular<Dim>{\n              new_mesh, new_neighbor_logical_ghost_zone_coords,\n              subcell_options.get_fd_to_fd_interp_order()};\n          (*extension_direction_ptr)[direction] =\n              interpolators_detail::ExtensionDirection<Dim>{\n                  direction_to_extend.value()};\n        } else {\n          (*interpolators_fd_to_neighbor_fd_ptr)[DirectionalId<Dim>{\n              direction, neighbor_id}] = intrp::Irregular<Dim>{\n              my_fd_mesh, neighbor_logical_ghost_zone_coords,\n              subcell_options.get_fd_to_fd_interp_order()};\n        }\n        // Set up interpolators for our local element to our neighbor's\n        // ghost zones.\n        (*interpolators_dg_to_neighbor_fd_ptr)[DirectionalId<Dim>{\n            direction, neighbor_id}] = intrp::Irregular<Dim>{\n            my_dg_mesh, neighbor_logical_ghost_zone_coords};\n\n        // InterpolatorsFromNeighborDgToFd: the interpolation from our\n        // neighbor's DG grid to our FD ghost zones.\n        //\n        // 1. Compute the grid coordinates of my ghost zones.\n        // 2. Compute neighbor's element logical coordinates of my ghost\n        //    zones\n        // 3. Create interpolator for InterpolatorsFromNeighborDgToFd\n        const tnsr::I<DataVector, Dim, Frame::ElementLogical>\n            my_logical_coords = logical_coordinates(my_fd_mesh);\n        tnsr::I<DataVector, Dim, Frame::ElementLogical>\n            my_logical_ghost_zone_coords =\n                evolution::dg::subcell::slice_tensor_for_subcell(\n                    my_logical_coords, neighbor_fd_mesh.extents(),\n                    number_of_ghost_zones, direction,\n                    // We want to _set_ the interpolators, so just do a simple\n                    // slice.\n                    {});\n        const double delta_xi =\n            get<0>(my_logical_coords)[1] - get<0>(my_logical_coords)[0];\n        // The sign accounts for whether we are shift along the\n        // positive or negative axis.\n        const double coordinate_shift =\n            direction.sign() * delta_xi * number_of_ghost_zones;\n        my_logical_ghost_zone_coords.get(direction.dimension()) +=\n            coordinate_shift;\n        const tnsr::I<DataVector, Dim, Frame::Grid> my_grid_ghost_zone_coords =\n            element_map(my_logical_ghost_zone_coords);\n        if (neighbor_block.is_time_dependent()) {\n          const ElementMap neighbor_element_map(\n              neighbor_id,\n              neighbor_block.moving_mesh_logical_to_grid_map().get_clone());\n          (*interpolators_neighbor_dg_to_fd_ptr)[DirectionalId<Dim>{\n              direction, neighbor_id}] = intrp::Irregular<Dim>{\n              neighbor_dg_mesh, get_logical_coords(neighbor_element_map,\n                                                   my_grid_ghost_zone_coords)};\n        } else {\n          const ElementMap neighbor_element_map(\n              neighbor_id, neighbor_block.stationary_map().get_clone());\n          const tnsr::I<DataVector, Dim, Frame::Inertial>\n              view_my_grid_ghost_zone_coords{};\n          for (size_t i = 0; i < Dim; ++i) {\n            make_const_view(make_not_null(&view_my_grid_ghost_zone_coords[i]),\n                            my_grid_ghost_zone_coords[i], 0,\n                            my_grid_ghost_zone_coords[i].size());\n          }\n          (*interpolators_neighbor_dg_to_fd_ptr)[DirectionalId<Dim>{\n              direction, neighbor_id}] = intrp::Irregular<Dim>{\n              neighbor_dg_mesh,\n              get_logical_coords(neighbor_element_map,\n                                 view_my_grid_ghost_zone_coords)};\n        }\n      }\n    }\n  }\n};\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/SliceData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace evolution::dg::subcell::detail {\nnamespace {\ntemplate <size_t Dim>\nvoid copy_data(gsl::not_null<DataVector*> sliced_subcell_vars,\n               const gsl::span<const double>& volume_subcell_vars,\n               size_t component_offset, size_t component_offset_volume,\n               const std::array<size_t, Dim>& lower_bounds,\n               const std::array<size_t, Dim>& upper_bounds,\n               const Index<Dim>& volume_extents);\n\ntemplate <>\nvoid copy_data(const gsl::not_null<DataVector*> sliced_subcell_vars,\n               const gsl::span<const double>& volume_subcell_vars,\n               const size_t component_offset,\n               const size_t component_offset_volume,\n               const std::array<size_t, 1>& lower_bounds,\n               const std::array<size_t, 1>& upper_bounds,\n               const Index<1>& /*volume_extents*/) {\n  for (size_t i = lower_bounds[0], index = 0; i < upper_bounds[0];\n       ++i, ++index) {\n    (*sliced_subcell_vars)[component_offset + index] =\n        volume_subcell_vars[component_offset_volume + i];\n  }\n}\n\ntemplate <>\nvoid copy_data(const gsl::not_null<DataVector*> sliced_subcell_vars,\n               const gsl::span<const double>& volume_subcell_vars,\n               const size_t component_offset,\n               const size_t component_offset_volume,\n               const std::array<size_t, 2>& lower_bounds,\n               const std::array<size_t, 2>& upper_bounds,\n               const Index<2>& volume_extents) {\n  for (size_t j = lower_bounds[1], index = 0; j < upper_bounds[1]; ++j) {\n    for (size_t i = lower_bounds[0]; i < upper_bounds[0]; ++i, ++index) {\n      (*sliced_subcell_vars)[component_offset + index] =\n          volume_subcell_vars[component_offset_volume + i +\n                              j * volume_extents[0]];\n    }\n  }\n}\n\ntemplate <>\nvoid copy_data(const gsl::not_null<DataVector*> sliced_subcell_vars,\n               const gsl::span<const double>& volume_subcell_vars,\n               const size_t component_offset,\n               const size_t component_offset_volume,\n               const std::array<size_t, 3>& lower_bounds,\n               const std::array<size_t, 3>& upper_bounds,\n               const Index<3>& volume_extents) {\n  for (size_t k = lower_bounds[2], index = 0; k < upper_bounds[2]; ++k) {\n    for (size_t j = lower_bounds[1]; j < upper_bounds[1]; ++j) {\n      for (size_t i = lower_bounds[0]; i < upper_bounds[0]; ++i, ++index) {\n        (*sliced_subcell_vars)[component_offset + index] =\n            volume_subcell_vars[component_offset_volume + i +\n                                volume_extents[0] *\n                                    (j + k * volume_extents[1])];\n      }\n    }\n  }\n}\n}  // namespace\n\ntemplate <size_t Dim>\nDirectionMap<Dim, DataVector> slice_data_impl(\n    const gsl::span<const double>& volume_subcell_vars,\n    const Index<Dim>& subcell_extents, const size_t number_of_ghost_points,\n    const std::unordered_set<Direction<Dim>>& directions_to_slice,\n    const size_t additional_buffer,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants) {\n  const size_t num_pts = subcell_extents.product();\n  const size_t number_of_components = volume_subcell_vars.size() / num_pts;\n  std::array<size_t, Dim> result_grid_points{};\n  for (size_t d = 0; d < Dim; ++d) {\n    const size_t num_sliced_pts =\n        number_of_ghost_points * subcell_extents.slice_away(d).product();\n\n    ASSERT(num_sliced_pts <= num_pts,\n           \"Number of ghost points (\" << number_of_ghost_points\n                                      << \") is larger than the subcell mesh \"\n                                         \"extent to the slicing direction (\"\n                                      << subcell_extents.indices().at(d)\n                                      << \") \");\n\n    gsl::at(result_grid_points, d) = num_sliced_pts;\n  }\n  DirectionMap<Dim, DataVector> result{};\n  for (const auto& direction : directions_to_slice) {\n    result[direction] =\n        DataVector{gsl::at(result_grid_points, direction.dimension()) *\n                       number_of_components +\n                   additional_buffer};\n  }\n\n  if (fd_to_neighbor_fd_interpolants.empty()) {\n    for (size_t component_index = 0; component_index < number_of_components;\n         ++component_index) {\n      const size_t component_offset_volume = component_index * num_pts;\n      for (auto& [direction, sliced_data] : result) {\n        const size_t component_offset_result =\n            gsl::at(result_grid_points, direction.dimension()) *\n            component_index;\n        std::array<size_t, Dim> lower_bounds =\n            make_array<Dim>(static_cast<size_t>(0));\n        std::array<size_t, Dim> upper_bounds = subcell_extents.indices();\n        if (direction.side() == Side::Lower) {\n          gsl::at(upper_bounds, direction.dimension()) = number_of_ghost_points;\n        } else {\n          gsl::at(lower_bounds, direction.dimension()) =\n              gsl::at(upper_bounds, direction.dimension()) -\n              number_of_ghost_points;\n        }\n        // No need to worry about sliced_data including the additional buffer\n        // because the instantiations of copy_data above never use the\n        // sliced_data.size(). All indexing is done by the lower/upper bounds\n        // arguments\n        copy_data(&sliced_data, volume_subcell_vars, component_offset_result,\n                  component_offset_volume, lower_bounds, upper_bounds,\n                  subcell_extents);\n      }\n    }\n  } else {\n    // We add directions to `interpolated` to mark that we are interpolating\n    // in this particular direction. The value of the bool is `true` _if_ we\n    // did FD interpolation and `false` if DG interpolation should be\n    // done. This allows passing in a DirectionalIdMap of just the neighbors\n    // that need interpolation but with `std::nullopt` in order to not spend\n    // resources slicing when the data would be overwritten by DG\n    // interpolation (cheaper and more accurate).\n    DirectionMap<Dim, bool> interpolated{};\n    for (const auto& [directional_element_id, interpolant] :\n         fd_to_neighbor_fd_interpolants) {\n      if (LIKELY(not interpolant.has_value())) {\n        // Just to keep track.\n        interpolated[directional_element_id.direction()] = false;\n        continue;\n      }\n      // fd_to_fd_neighbor_fd_interpolants contain interpolants\n      // for all direction where interpolation is necessary, and we only\n      // want to slice data for the directions that are in\n      // `directions_to_slice`.\n      if (!directions_to_slice.contains(directional_element_id.direction())) {\n        continue;\n      }\n      interpolated[directional_element_id.direction()] = true;\n      auto result_span =\n          gsl::make_span(result.at(directional_element_id.direction()).data(),\n                         result.at(directional_element_id.direction()).size() -\n                             additional_buffer);\n      interpolant.value().interpolate(make_not_null(&result_span),\n                                      volume_subcell_vars);\n    }\n    // Now copy data for neighbors that are in the same block.\n    for (size_t component_index = 0; component_index < number_of_components;\n         ++component_index) {\n      const size_t component_offset_volume = component_index * num_pts;\n      for (auto& [direction, sliced_data] : result) {\n        if (UNLIKELY(interpolated.contains(direction))) {\n          continue;\n        }\n        const size_t component_offset_result =\n            gsl::at(result_grid_points, direction.dimension()) *\n            component_index;\n        std::array<size_t, Dim> lower_bounds =\n            make_array<Dim>(static_cast<size_t>(0));\n        std::array<size_t, Dim> upper_bounds = subcell_extents.indices();\n        if (direction.side() == Side::Lower) {\n          gsl::at(upper_bounds, direction.dimension()) = number_of_ghost_points;\n        } else {\n          gsl::at(lower_bounds, direction.dimension()) =\n              gsl::at(upper_bounds, direction.dimension()) -\n              number_of_ghost_points;\n        }\n        // No need to worry about sliced_data including the additional buffer\n        // because the instantiations of copy_data above never use the\n        // sliced_data.size(). All indexing is done by the lower/upper bounds\n        // arguments\n        copy_data(&sliced_data, volume_subcell_vars, component_offset_result,\n                  component_offset_volume, lower_bounds, upper_bounds,\n                  subcell_extents);\n      }\n    }\n  }\n  return result;\n}\n\ntemplate DirectionMap<1, DataVector> slice_data_impl(\n    const gsl::span<const double>&, const Index<1>&, const size_t,\n    const std::unordered_set<Direction<1>>&, size_t,\n    const DirectionalIdMap<1, std::optional<intrp::Irregular<1>>>&);\ntemplate DirectionMap<2, DataVector> slice_data_impl(\n    const gsl::span<const double>&, const Index<2>&, const size_t,\n    const std::unordered_set<Direction<2>>&, size_t,\n    const DirectionalIdMap<2, std::optional<intrp::Irregular<2>>>&);\ntemplate DirectionMap<3, DataVector> slice_data_impl(\n    const gsl::span<const double>&, const Index<3>&, const size_t,\n    const std::unordered_set<Direction<3>>&, size_t,\n    const DirectionalIdMap<3, std::optional<intrp::Irregular<3>>>&);\n}  // namespace evolution::dg::subcell::detail\n"
  },
  {
    "path": "src/Evolution/DgSubcell/SliceData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace evolution::dg::subcell {\nnamespace detail {\ntemplate <size_t Dim>\nDirectionMap<Dim, DataVector> slice_data_impl(\n    const gsl::span<const double>& volume_subcell_vars,\n    const Index<Dim>& subcell_extents, size_t number_of_ghost_points,\n    const std::unordered_set<Direction<Dim>>& directions_to_slice,\n    size_t additional_buffer,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants);\n}  // namespace detail\n\n/// @{\n/*!\n * \\brief Slice the subcell variables needed for neighbors to perform\n * reconstruction.\n *\n * Note that we slice to a grid that is against the boundary of the element but\n * is several ghost points deep. This is in contrast to the slicing used in the\n * DG method which is to the boundary of the element only.\n *\n * The `number_of_ghost_points` will depend on the number of neighboring points\n * the reconstruction method needs that is used on the subcell. The\n * `directions_to_slice` determines in which directions data is sliced.\n * Generally this will be the directions in which the element has neighbors.\n *\n * The data always has the same ordering as the volume data (tags have the same\n * ordering, grid points are x-varies-fastest).\n *\n * The `additional_buffer` argument is used to add extra padding to the result\n * storage to be used for example for sending the RDMP TCI data. This eliminates\n * expensive data copying.\n */\ntemplate <size_t Dim>\nDirectionMap<Dim, DataVector> slice_data(\n    const DataVector& volume_subcell_vars, const Index<Dim>& subcell_extents,\n    const size_t number_of_ghost_points,\n    const std::unordered_set<Direction<Dim>>& directions_to_slice,\n    const size_t additional_buffer,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants) {\n  return detail::slice_data_impl(\n      gsl::make_span(volume_subcell_vars.data(), volume_subcell_vars.size()),\n      subcell_extents, number_of_ghost_points, directions_to_slice,\n      additional_buffer, fd_to_neighbor_fd_interpolants);\n}\n\ntemplate <size_t Dim, typename TagList>\nDirectionMap<Dim, DataVector> slice_data(\n    const Variables<TagList>& volume_subcell_vars,\n    const Index<Dim>& subcell_extents, const size_t number_of_ghost_points,\n    const std::unordered_set<Direction<Dim>>& directions_to_slice,\n    const size_t additional_buffer,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants) {\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n  const DataVector view{const_cast<double*>(volume_subcell_vars.data()),\n                        volume_subcell_vars.size()};\n  return slice_data(view, subcell_extents, number_of_ghost_points,\n                    directions_to_slice, additional_buffer,\n                    fd_to_neighbor_fd_interpolants);\n}\n/// @}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/SliceTensor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\n// template specialization for handling scalar (rank 0)\ntemplate <size_t Dim, typename VectorType>\nvoid slice_tensor_for_subcell(\n    const gsl::not_null<Tensor<VectorType, Symmetry<>, index_list<>>*>\n        sliced_scalar,\n    const Tensor<VectorType, Symmetry<>, index_list<>>& volume_scalar,\n    const Index<Dim>& subcell_extents, size_t number_of_ghost_points,\n    const Direction<Dim>& direction,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants) {\n  std::unordered_set directions_to_slice{direction};\n\n  auto& scalar_dv = get(volume_scalar);\n  auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n      gsl::make_span(scalar_dv.data(), scalar_dv.size()), subcell_extents,\n      number_of_ghost_points, directions_to_slice, 0,\n      fd_to_neighbor_fd_interpolants)[direction];\n\n  std::copy(sliced_data.begin(), sliced_data.end(), get(*sliced_scalar).data());\n}\n\n/// @{\n/*!\n * \\brief Slice a single volume tensor for a given direction and slicing depth\n * (number of ghost points).\n *\n * Note that the second to last argument has the type `Direction<Dim>`, not a\n * DirectionMap (cf. `evolution::dg::subcell::slice_data`)\n *\n */\ntemplate <size_t Dim, typename VectorType, typename... Structure>\nvoid slice_tensor_for_subcell(\n    const gsl::not_null<Tensor<VectorType, Structure...>*> sliced_tensor,\n    const Tensor<VectorType, Structure...>& volume_tensor,\n    const Index<Dim>& subcell_extents, size_t number_of_ghost_points,\n    const Direction<Dim>& direction,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants) {\n  std::unordered_set directions_to_slice{direction};\n\n  for (size_t i = 0; i < volume_tensor.size(); i++) {\n    auto& ti = volume_tensor[i];\n\n    auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(ti.data(), ti.size()), subcell_extents,\n        number_of_ghost_points, directions_to_slice, 0,\n        fd_to_neighbor_fd_interpolants)[direction];\n\n    std::copy(sliced_data.begin(), sliced_data.end(),\n              (*sliced_tensor)[i].data());\n  }\n}\n\ntemplate <size_t Dim, typename VectorType, typename... Structure>\nTensor<VectorType, Structure...> slice_tensor_for_subcell(\n    const Tensor<VectorType, Structure...>& volume_tensor,\n    const Index<Dim>& subcell_extents, size_t number_of_ghost_points,\n    const Direction<Dim>& direction,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants) {\n  Tensor<VectorType, Structure...> sliced_tensor(\n      subcell_extents.slice_away(direction.dimension()).product() *\n      number_of_ghost_points);\n  slice_tensor_for_subcell(make_not_null(&sliced_tensor), volume_tensor,\n                           subcell_extents, number_of_ghost_points, direction,\n                           fd_to_neighbor_fd_interpolants);\n  return sliced_tensor;\n}\n/// @}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/SliceVariable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\n/// @{\n/*!\n * \\brief Slice a Variables object on subcell mesh for a given direction and\n * slicing depth (number of ghost points).\n */\ntemplate <size_t Dim, typename TagList>\nvoid slice_variable(\n    const gsl::not_null<Variables<TagList>*>& sliced_subcell_vars,\n    const Variables<TagList>& volume_subcell_vars,\n    const Index<Dim>& subcell_extents, const size_t ghost_zone_size,\n    const Direction<Dim>& direction,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants) {\n  // check the size of sliced_subcell_vars (output)\n  const size_t num_sliced_pts =\n      subcell_extents.slice_away(direction.dimension()).product() *\n      ghost_zone_size;\n  if (sliced_subcell_vars->size() != num_sliced_pts) {\n    sliced_subcell_vars->initialize(num_sliced_pts);\n  }\n\n  // Slice volume variables. Note that the return type of the\n  // `slice_data_impl()` function is DataVector.\n  std::unordered_set directions_to_slice{direction};\n\n  const DataVector sliced_data{detail::slice_data_impl(\n      gsl::make_span(volume_subcell_vars.data(), volume_subcell_vars.size()),\n      subcell_extents, ghost_zone_size, directions_to_slice, 0,\n      fd_to_neighbor_fd_interpolants)[direction]};\n\n  // copy the returned DataVector data into sliced variables\n  std::copy(sliced_data.begin(), sliced_data.end(),\n            sliced_subcell_vars->data());\n}\n\ntemplate <size_t Dim, typename TagList>\nVariables<TagList> slice_variable(\n    const Variables<TagList>& volume_subcell_vars,\n    const Index<Dim>& subcell_extents, const size_t ghost_zone_size,\n    const Direction<Dim>& direction,\n    const DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>&\n        fd_to_neighbor_fd_interpolants) {\n  Variables<TagList> sliced_subcell_vars{\n      subcell_extents.slice_away(direction.dimension()).product() *\n      ghost_zone_size};\n  slice_variable(make_not_null(&sliced_subcell_vars), volume_subcell_vars,\n                 subcell_extents, ghost_zone_size, direction,\n                 fd_to_neighbor_fd_interpolants);\n  return sliced_subcell_vars;\n}\n/// @}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/SubcellOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n\n#include <bit>\n#include <cstddef>\n#include <initializer_list>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Structure/BlockGroups.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace evolution::dg::subcell {\nSubcellOptions::SubcellOptions(\n    double persson_exponent, size_t persson_num_highest_modes,\n    double rdmp_delta0, double rdmp_epsilon, bool always_use_subcells,\n    bool enable_extension_directions, fd::ReconstructionMethod recons_method,\n    bool use_halo,\n    std::optional<std::vector<std::string>> only_dg_block_and_group_names,\n    ::fd::DerivativeOrder finite_difference_derivative_order,\n    const size_t number_of_steps_between_tci_calls,\n    const size_t min_tci_calls_after_rollback,\n    const size_t min_clear_tci_before_dg, const size_t fd_to_fd_interp_order,\n    const std::optional<size_t>& lts_steps_per_slab,\n    const Options::Context& context)\n    : persson_exponent_(persson_exponent),\n      persson_num_highest_modes_(persson_num_highest_modes),\n      rdmp_delta0_(rdmp_delta0),\n      rdmp_epsilon_(rdmp_epsilon),\n      always_use_subcells_(always_use_subcells),\n      enable_extension_directions_(enable_extension_directions),\n      reconstruction_method_(recons_method),\n      use_halo_(use_halo),\n      only_dg_block_and_group_names_(std::move(only_dg_block_and_group_names)),\n      finite_difference_derivative_order_(finite_difference_derivative_order),\n      number_of_steps_between_tci_calls_(number_of_steps_between_tci_calls),\n      min_tci_calls_after_rollback_(min_tci_calls_after_rollback),\n      min_clear_tci_before_dg_(min_clear_tci_before_dg),\n      fd_to_fd_interp_order_(fd_to_fd_interp_order),\n      lts_steps_per_slab_(lts_steps_per_slab) {\n  if (not only_dg_block_and_group_names_.has_value()) {\n    only_dg_block_ids_ = std::vector<size_t>{};\n  }\n  ASSERT(number_of_steps_between_tci_calls_ > 0,\n         \"number_of_steps_between_tci_calls_ must be greater than zero.\");\n  ASSERT(min_tci_calls_after_rollback_ > 0,\n         \"min_tci_calls_after_rollback_ must be greater than zero.\");\n  ASSERT(min_clear_tci_before_dg_ > 0,\n         \"min_clear_tci_before_dg_ must be greater than zero.\");\n  if (!always_use_subcells_ && enable_extension_directions_) {\n    PARSE_ERROR(Options::Context{},\n                \"The extension directions are only enabled when \"\n                \"always_use_subcells_ is true.\");\n  }\n  if (lts_steps_per_slab_.has_value() and\n      std::popcount(*lts_steps_per_slab_) != 1) {\n    PARSE_ERROR(context, \"LtsStepsPerSlab must be a power of 2\");\n  }\n}\n\ntemplate <size_t Dim>\nSubcellOptions::SubcellOptions(\n    const SubcellOptions& subcell_options_with_block_names,\n    const DomainCreator<Dim>& domain_creator) {\n  *this = subcell_options_with_block_names;\n  only_dg_block_ids_ = domain::block_ids_from_names(\n      subcell_options_with_block_names.only_dg_block_and_group_names_.value_or(\n          std::vector<std::string>{}),\n      domain_creator.block_names(), domain_creator.block_groups());\n}\n\nvoid SubcellOptions::pup(PUP::er& p) {\n  p | persson_exponent_;\n  p | persson_num_highest_modes_;\n  p | rdmp_delta0_;\n  p | rdmp_epsilon_;\n  p | always_use_subcells_;\n  p | enable_extension_directions_;\n  p | reconstruction_method_;\n  p | use_halo_;\n  p | only_dg_block_and_group_names_;\n  p | only_dg_block_ids_;\n  p | fd_to_fd_interp_order_;\n  p | finite_difference_derivative_order_;\n  p | number_of_steps_between_tci_calls_;\n  p | min_tci_calls_after_rollback_;\n  p | min_clear_tci_before_dg_;\n  p | lts_steps_per_slab_;\n}\n\nbool operator==(const SubcellOptions& lhs, const SubcellOptions& rhs) {\n  return lhs.persson_exponent() == rhs.persson_exponent() and\n         lhs.persson_num_highest_modes() == rhs.persson_num_highest_modes() and\n         lhs.rdmp_delta0() == rhs.rdmp_delta0() and\n         lhs.rdmp_epsilon() == rhs.rdmp_epsilon() and\n         lhs.always_use_subcells() == rhs.always_use_subcells() and\n         lhs.enable_extension_directions() ==\n             rhs.enable_extension_directions() and\n         lhs.reconstruction_method() == rhs.reconstruction_method() and\n         lhs.use_halo() == rhs.use_halo() and\n         lhs.only_dg_block_and_group_names_ ==\n             rhs.only_dg_block_and_group_names_ and\n         lhs.only_dg_block_ids_ == rhs.only_dg_block_ids_ and\n         lhs.finite_difference_derivative_order_ ==\n             rhs.finite_difference_derivative_order_ and\n         lhs.number_of_steps_between_tci_calls_ ==\n             rhs.number_of_steps_between_tci_calls_ and\n         lhs.min_tci_calls_after_rollback_ ==\n             rhs.min_tci_calls_after_rollback_ and\n         lhs.min_clear_tci_before_dg_ == rhs.min_clear_tci_before_dg_ and\n         lhs.fd_to_fd_interp_order_ == rhs.fd_to_fd_interp_order_ and\n         lhs.lts_steps_per_slab_ == rhs.lts_steps_per_slab_;\n}\n\nbool operator!=(const SubcellOptions& lhs, const SubcellOptions& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                \\\n  template SubcellOptions::SubcellOptions(                    \\\n      const SubcellOptions& subcell_options_with_block_names, \\\n      const DomainCreator<DIM(data)>& domain_creator);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/SubcellOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass DomainCreator;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Holds the system-agnostic subcell parameters, such as numbers\n * controlling when to switch between DG and subcell.\n */\nclass SubcellOptions {\n public:\n  /// Parameters related to the troubled cell indicator (TCI) that determines\n  /// when to switch between DG and FD.\n  struct TroubledCellIndicator {\n    static constexpr Options::String help =\n        \"Parameters related to the troubled cell indicator (TCI) that \"\n        \"determines when to switch between DG and FD.\";\n  };\n\n  struct PerssonTci {\n    static constexpr Options::String help =\n        \"Parameters related to the Persson TCI\";\n    using group = TroubledCellIndicator;\n  };\n  /// The exponent \\f$\\alpha\\f$ passed to the Persson troubled-cell indicator\n  struct PerssonExponent {\n    static std::string name() { return \"Exponent\"; }\n    static constexpr Options::String help{\n        \"The exponent at which the error should decrease with (N+1-M)\"};\n    using type = double;\n    static constexpr type lower_bound() { return 1.0; }\n    static constexpr type upper_bound() { return 10.0; }\n    using group = PerssonTci;\n  };\n  /// The number of highest modes the Persson troubled-cell indicator monitors\n  struct PerssonNumHighestModes {\n    static std::string name() { return \"NumHighestModes\"; }\n    static constexpr Options::String help{\n        \"The number of highest modes M the Persson TCI monitors.\"};\n    using type = size_t;\n    static constexpr type lower_bound() { return 1_st; }\n    static constexpr type upper_bound() { return 10_st; }\n    using group = PerssonTci;\n  };\n\n  struct RdmpTci {\n    static constexpr Options::String help =\n        \"Parameters related to the relaxed discrete maximum principle TCI\";\n    using group = TroubledCellIndicator;\n  };\n  /// The \\f$\\delta_0\\f$ parameter in the relaxed discrete maximum principle\n  /// troubled-cell indicator\n  struct RdmpDelta0 {\n    static std::string name() { return \"Delta0\"; }\n    static constexpr Options::String help{\"Absolute jump tolerance parameter.\"};\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    using group = RdmpTci;\n  };\n  /// The \\f$\\epsilon\\f$ parameter in the relaxed discrete maximum principle\n  /// troubled-cell indicator\n  struct RdmpEpsilon {\n    static std::string name() { return \"Epsilon\"; }\n    static constexpr Options::String help{\n        \"The jump-dependent relaxation constant.\"};\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n    using group = RdmpTci;\n  };\n  /// If true, then we always use the subcell method, not DG.\n  struct AlwaysUseSubcells {\n    static constexpr Options::String help{\n        \"If true, then always use the subcell method (e.g. finite-difference) \"\n        \"instead of DG.\"};\n    using type = bool;\n    using group = TroubledCellIndicator;\n  };\n  /// If true, then we allow extension directions to be used for\n  /// computing the ghost points in the FD subcell method to avoid\n  /// extrapolation.\n  struct EnableExtensionDirections {\n    static constexpr Options::String help{\n        \"If true, then we allow extension directions to be used for \"\n        \"computing the ghost points in the FD subcell method to avoid \"\n        \"extrapolation.\"};\n    using type = bool;\n    using group = TroubledCellIndicator;\n  };\n  /// Method to use for reconstructing the DG solution from the subcell\n  /// solution.\n  struct SubcellToDgReconstructionMethod {\n    static constexpr Options::String help{\n        \"Method to use for reconstructing the DG solution from the subcell \"\n        \"solution.\"};\n    using type = fd::ReconstructionMethod;\n  };\n  /// \\brief Use a width-one halo of FD elements around any troubled element.\n  ///\n  /// This provides a buffer of FD subcells so that as a discontinuity moves\n  /// from one element to another we do not get any Gibbs phenomenon. In the\n  /// case where we evolve the spacetime metric (e.g. GH+GRMHD) a halo region\n  /// provides a buffer in case the stellar surface is near an element\n  /// boundary. Since the GH variables are interpolated using high-order\n  /// unlimited reconstruction, they can run into issues with Gibbs phenomenon.\n  struct UseHalo {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Use a width-one halo of FD elements around any troubled element.\"\n        \"\\n\"\n        \"This provides a buffer of FD subcells so that as a discontinuity \"\n        \"moves from one element to another we do not get any Gibbs \"\n        \"phenomenon.\"};\n    using group = TroubledCellIndicator;\n  };\n\n  /// \\brief A list of block names on which to never do subcell.\n  ///\n  /// Set to `None` to allow subcell in all blocks.\n  struct OnlyDgBlocksAndGroups {\n    using type =\n        Options::Auto<std::vector<std::string>, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"A list of block and group names on which to never do subcell.\\n\"\n        \"Set to 'None' to not restrict where FD can be used.\"};\n    using group = TroubledCellIndicator;\n  };\n\n  /// \\brief The order of the FD derivative used.\n  ///\n  /// Must be one of 2, 4, 6, 8, or 10. If `Auto` then the derivative order is\n  /// determined for you, typically the next-lowest even order compared with\n  /// the reconstruction scheme order. E.g. for a 5th-order reconstruction we\n  /// would use 4th order derivatives.\n  struct FiniteDifferenceDerivativeOrder {\n    using type = ::fd::DerivativeOrder;\n    static constexpr Options::String help = {\n        \"The finite difference derivative order to use. If computed from the \"\n        \"reconstruction, then the reconstruction method must support returning \"\n        \"its reconstruction order.\"};\n  };\n\n  struct FdToDgTci {\n    static constexpr Options::String help =\n        \"Options related to how quickly we switch from FD to DG.\";\n    using group = TroubledCellIndicator;\n  };\n  /// The number of time steps taken between calls to the TCI to check if we\n  /// can go back to the DG grid. A value of `1` means every time step, while\n  /// `2` means every other time step.\n  struct NumberOfStepsBetweenTciCalls {\n    static constexpr Options::String help{\n        \"The number of time steps taken between calls to the TCI to check if \"\n        \"we can go back to the DG grid. A value of `1` means every time step, \"\n        \"while `2` means every other time step.\"};\n    using type = size_t;\n    static constexpr type lower_bound() { return 1; }\n    using group = FdToDgTci;\n  };\n  /// The number of time steps/TCI calls after a switch from DG to FD before we\n  /// allow switching back to DG.\n  struct MinTciCallsAfterRollback {\n    static constexpr Options::String help{\n        \"The number of time steps/TCI calls after a switch from DG to FD \"\n        \"before we allow switching back to DG.\"};\n    using type = size_t;\n    static constexpr type lower_bound() { return 1; }\n    using group = FdToDgTci;\n  };\n  /// The number of time steps/TCI calls that the TCI needs to have decided\n  /// switching to DG is fine before we actually do the switch.\n  struct MinimumClearTcis {\n    static constexpr Options::String help{\n        \"The number of time steps/TCI calls that the TCI needs to have decided \"\n        \"switching to DG is fine before we actually do the switch.\"};\n    using type = size_t;\n    static constexpr type lower_bound() { return 1; }\n    using group = FdToDgTci;\n  };\n\n  /// \\brief FD to FD interpolation order.\n  ///\n  struct FdInterpolationOrder {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The interpolation order to use when interpolating from FD block to FD \"\n        \"block.\"};\n    static constexpr type lower_bound() { return 1; }\n    static constexpr type upper_bound() { return 3; }\n  };\n\n  /// \\brief Subcell steps per slab in regions allowed to do subcell\n  /// in LTS mode.  Must be a power of 2.\n  struct LtsStepsPerSlab {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Steps per slab in regions allowed to do subcell.  Must be a power of \"\n        \"2.\"};\n    static constexpr type lower_bound() { return 1; }\n    static constexpr type upper_bound() { return 1_st << 31; }\n  };\n\n  using gts_options =\n      tmpl::list<PerssonExponent, PerssonNumHighestModes, RdmpDelta0,\n                 RdmpEpsilon, AlwaysUseSubcells, EnableExtensionDirections,\n                 SubcellToDgReconstructionMethod, UseHalo,\n                 OnlyDgBlocksAndGroups, FiniteDifferenceDerivativeOrder,\n                 NumberOfStepsBetweenTciCalls, MinTciCallsAfterRollback,\n                 MinimumClearTcis, FdInterpolationOrder>;\n\n  template <typename Metavars>\n  using options =\n      tmpl::conditional_t<Metavars::local_time_stepping,\n                          tmpl::push_back<gts_options, LtsStepsPerSlab>,\n                          gts_options>;\n\n  static constexpr Options::String help{\n      \"System-agnostic options for the DG-subcell method.\"};\n\n  SubcellOptions() = default;\n  SubcellOptions(\n      double persson_exponent, size_t persson_num_highest_modes,\n      double rdmp_delta0, double rdmp_epsilon, bool always_use_subcells,\n      bool enable_extension_directions, fd::ReconstructionMethod recons_method,\n      bool use_halo,\n      std::optional<std::vector<std::string>> only_dg_block_and_group_names,\n      ::fd::DerivativeOrder finite_difference_derivative_order,\n      size_t number_of_steps_between_tci_calls,\n      size_t min_tci_calls_after_rollback, size_t min_clear_tci_before_dg,\n      size_t fd_to_fd_interp_order = 1_st,\n      const std::optional<size_t>& lts_steps_per_slab = std::nullopt,\n      const Options::Context& context = {});\n\n  /// \\brief Given an existing SubcellOptions that was created from block and\n  /// group names, create one that stores block IDs.\n  ///\n  /// The `DomainCreator` is used to convert block and group names into IDs\n  /// and also to check that all listed block names and groups are in the\n  /// domain.\n  ///\n  /// \\note This is a workaround since our option parser does not allow us to\n  /// retrieve options specified somewhere completely different in the input\n  /// file.\n  template <size_t Dim>\n  SubcellOptions(const SubcellOptions& subcell_options_with_block_names,\n                 const DomainCreator<Dim>& domain_creator);\n\n  void pup(PUP::er& p);\n\n  double persson_exponent() const { return persson_exponent_; }\n\n  size_t persson_num_highest_modes() const {\n    return persson_num_highest_modes_;\n  }\n\n  double rdmp_delta0() const { return rdmp_delta0_; }\n\n  double rdmp_epsilon() const { return rdmp_epsilon_; }\n\n  bool always_use_subcells() const { return always_use_subcells_; }\n\n  bool enable_extension_directions() const {\n    return enable_extension_directions_;\n  }\n\n  fd::ReconstructionMethod reconstruction_method() const {\n    return reconstruction_method_;\n  }\n\n  bool use_halo() const { return use_halo_; }\n\n  const std::vector<size_t>& only_dg_block_ids() const {\n    ASSERT(only_dg_block_ids_.has_value(),\n           \"The block IDs on which we are only allowed to do DG have not been \"\n           \"set.\");\n    return only_dg_block_ids_.value();\n  }\n\n  size_t get_fd_to_fd_interp_order() const { return fd_to_fd_interp_order_; }\n\n  ::fd::DerivativeOrder finite_difference_derivative_order() const {\n    return finite_difference_derivative_order_;\n  }\n\n  /// The number of time steps between when we check the TCI to see if we can\n  /// switch from FD back to DG.\n  size_t number_of_steps_between_tci_calls() const {\n    return number_of_steps_between_tci_calls_;\n  }\n\n  /// The number of time steps after a rollback before we check the TCI to see\n  /// if we can switch from FD back to DG.\n  size_t min_tci_calls_after_rollback() const {\n    return min_tci_calls_after_rollback_;\n  }\n\n  /// The number of times the TCI must have flagged that we can switch back to\n  /// DG before doing so.\n  ///\n  /// Note that if we only check the TCI every\n  /// `number_of_steps_between_tci_calls()` then it takes\n  /// `number_of_steps_between_tci_calls * min_clear_tci_before_dg` time steps\n  /// before switching from FD back to DG.\n  ///\n  /// `0 means\n  size_t min_clear_tci_before_dg() const { return min_clear_tci_before_dg_; }\n\n  /// The number of steps per slab in regions allowed to do subcell.\n  /// Only valid in LTS-subcell evolutions.\n  size_t lts_steps_per_slab() const {\n    ASSERT(lts_steps_per_slab_.has_value(),\n           \"Attempted to access lts_steps_per_slab in GTS evolution.\");\n    return lts_steps_per_slab_.value();\n  }\n\n private:\n  friend bool operator==(const SubcellOptions& lhs, const SubcellOptions& rhs);\n\n  double persson_exponent_ = std::numeric_limits<double>::signaling_NaN();\n  size_t persson_num_highest_modes_ =\n      std::numeric_limits<size_t>::signaling_NaN();\n  double rdmp_delta0_ = std::numeric_limits<double>::signaling_NaN();\n  double rdmp_epsilon_ = std::numeric_limits<double>::signaling_NaN();\n  bool always_use_subcells_ = false;\n  bool enable_extension_directions_ = false;\n  fd::ReconstructionMethod reconstruction_method_ =\n      fd::ReconstructionMethod::AllDimsAtOnce;\n  bool use_halo_{false};\n  std::optional<std::vector<std::string>> only_dg_block_and_group_names_{};\n  std::optional<std::vector<size_t>> only_dg_block_ids_{};\n  ::fd::DerivativeOrder finite_difference_derivative_order_{};\n  size_t number_of_steps_between_tci_calls_{1};\n  size_t min_tci_calls_after_rollback_{1};\n  size_t min_clear_tci_before_dg_{0};\n  size_t fd_to_fd_interp_order_{};\n  std::optional<size_t> lts_steps_per_slab_{};\n};\n\nbool operator!=(const SubcellOptions& lhs, const SubcellOptions& rhs);\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/ActiveGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"NumericalAlgorithms/SpatialDiscretization/OptionTags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace OptionTags {\nstruct ActiveGrid {\n  using type = evolution::dg::subcell::ActiveGrid;\n  static constexpr Options::String help = {\n      \"The type of the active grid. Either 'Dg' or 'Subcell'.\"};\n  using group = SpatialDiscretization::OptionTags::SpatialDiscretizationGroup;\n};\n}  // namespace OptionTags\nnamespace Tags {\n\n/// The grid currently used for the DG-subcell evolution on the element.\nstruct ActiveGrid : db::SimpleTag {\n  using type = evolution::dg::subcell::ActiveGrid;\n  using option_tags =\n      tmpl::list<evolution::dg::subcell::OptionTags::ActiveGrid>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type option) { return option; }\n};\n}  // namespace Tags\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DgSubcell)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ActiveGrid.hpp\n  CellCenteredFlux.hpp\n  Coordinates.hpp\n  DataForRdmpTci.hpp\n  DidRollback.hpp\n  GhostDataForReconstruction.hpp\n  GhostZoneInverseJacobian.hpp\n  Inactive.hpp\n  InitialTciData.hpp\n  Interpolators.hpp\n  Jacobians.hpp\n  Mesh.hpp\n  MeshForGhostData.hpp\n  MethodOrder.hpp\n  ObserverCoordinates.hpp\n  ObserverMesh.hpp\n  ObserverMeshVelocity.hpp\n  OnSubcellFaces.hpp\n  OnSubcells.hpp\n  ReconstructionOrder.hpp\n  StepsSinceTciCall.hpp\n  SubcellOptions.hpp\n  SubcellSolver.hpp\n  Tags.hpp\n  TciCallsSinceRollback.hpp\n  TciGridHistory.hpp\n  TciStatus.hpp\n  )\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Mesh.cpp\n  MethodOrder.cpp\n  ObserverMesh.cpp\n  ObserverMeshVelocity.cpp\n  TciStatus.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/CellCenteredFlux.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/// \\brief Holds the cell-centered fluxes on the subcell mesh.\n///\n/// These are only needed when using high-order FD methods and when we are\n/// actively doing FD. If either of these conditions isn't met, the value is\n/// `std::nullopt`.\n///\n/// The cell-centered fluxes are stored in the DataBox so they can be computed\n/// and sent to neighbor elements without having to be recomputed after\n/// receiving neighbor data. This means we maintain a single-send algorithm\n/// despite going to high-order, with no additional FLOP overhead. We do,\n/// however, have the memory overhead of the cell-centered fluxes.\n///\n/// \\note The `TagsList` is a list of the variables, not the fluxes. They are\n/// wrapped in the `::Tags::Flux` prefix.\ntemplate <typename TagsList, size_t Dim, typename Fr = Frame::Inertial>\nstruct CellCenteredFlux : db::SimpleTag {\n  using type = std::optional<Variables<\n      db::wrap_tags_in<::Tags::Flux, TagsList, tmpl::size_t<Dim>, Fr>>>;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/Coordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// The coordinates in a given frame.\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates : db::SimpleTag {\n  static std::string name() { return get_output(Frame{}) + \"Coordinates\"; }\n  using type = tnsr::I<DataVector, Dim, Frame>;\n};\n\n/// The element logical coordinates on the subcell grid\ntemplate <size_t VolumeDim>\nstruct LogicalCoordinatesCompute\n    : Coordinates<VolumeDim, Frame::ElementLogical>,\n      db::ComputeTag {\n  using base = Coordinates<VolumeDim, Frame::ElementLogical>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<Mesh<VolumeDim>>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<return_type*>, const ::Mesh<VolumeDim>&)>(\n      &logical_coordinates<VolumeDim>);\n};\n\n/// The inertial coordinates on the subcell grid\ntemplate <typename MapTagGridToInertial>\nstruct InertialCoordinatesCompute\n    : Coordinates<MapTagGridToInertial::dim, Frame::Inertial>,\n      db::ComputeTag {\n  static constexpr size_t dim = MapTagGridToInertial::dim;\n  using base = Coordinates<dim, Frame::Inertial>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<MapTagGridToInertial, Tags::Coordinates<dim, Frame::Grid>,\n                 ::Tags::Time, ::domain::Tags::FunctionsOfTime>;\n  static void function(\n      const gsl::not_null<return_type*> inertial_coords,\n      const ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, dim>&\n          grid_to_inertial_map,\n      const tnsr::I<DataVector, dim, Frame::Grid>& grid_coords,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    *inertial_coords =\n        grid_to_inertial_map(grid_coords, time, functions_of_time);\n  }\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/DataForRdmpTci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/// The data for the RDMP troubled-cell indicator.\nstruct DataForRdmpTci : db::SimpleTag {\n  using type = RdmpTciData;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/DidRollback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/// \\brief Tag indicating whether we are retrying a step after a rollback of a\n/// failed DG step\n///\n/// Set to `true` by the DG scheme when the predicted step failed and a rollback\n/// is performed. The subcell solver checks the tag, and uses the DG boundary\n/// data if a rollback occurred in order to maintain conservation. The subcell\n/// solver then sets `DidRollback` to `false`.\nstruct DidRollback : db::SimpleTag {\n  using type = bool;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/// The ghost data used for reconstructing the solution on the interfaces\n/// between elements\n///\n/// The `DirectionalIdMap` stores a `evolution::dg::subcell::GhostData` which\n/// stores both local and received neighbor ghost data. Even though subcell\n/// doesn't use it's local ghost data during reconstruction, we will need to\n/// store it when using Charm++ messages.\ntemplate <size_t Dim>\nstruct GhostDataForReconstruction : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, GhostData>;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/GhostZoneInverseJacobian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/// Inverse Jacobian data stored in the ghost zone.\n///\n/// The `DirectionalIdMap` stores the grid coordinates and logical to grid\n/// inverse Jacobians in the ghost zone to avoid recomputing these data when\n/// full Jacobians are needed. We store the grid coordinates to be able to\n/// compute the grid to inertial Jacobian at each time step.\ntemplate <size_t Dim>\nstruct GhostZoneInverseJacobian : db::SimpleTag {\n  using type = DirectionMap<\n      Dim, Variables<tmpl::list<Coordinates<Dim, Frame::Grid>,\n                                evolution::dg::subcell::fd::Tags::\n                                    InverseJacobianLogicalToGrid<Dim>>>>;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/Inactive.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n\n/// \\cond\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// Mark a tag as holding data for the inactive grid.\n///\n/// As an example, if the evolution in the element is currently being done on\n/// the DG grid then the subcell evolved variables would be in\n/// `Inactive<evolved_vars>`.\ntemplate <typename Tag>\nstruct Inactive : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = typename tag::type;\n};\n\n/// \\cond\n// This tag currently exposes its constituents as subitems. Possible\n// compile-time optimization: Remove this template specialization and use\n// `Inactive<VariablesTag<tmpl::list<Tag1, Tag2>>>` instead of the individual\n// `Inactive<Tag1>`.\ntemplate <typename TagList>\nstruct Inactive<::Tags::Variables<TagList>>\n    : ::Tags::Variables<db::wrap_tags_in<Inactive, TagList>> {\n  using tag = ::Tags::Variables<TagList>;\n  using type = Variables<db::wrap_tags_in<Inactive, TagList>>;\n};\n/// \\endcond\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/InitialTciData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iomanip>\n#include <map>\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/InitialTciData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/*!\n * \\brief Inbox tag for communicating the RDMP and TCI status/decision during\n * initialization.\n */\ntemplate <size_t Dim>\nstruct InitialTciData {\n  using temporal_id = int;\n  using type =\n      std::map<temporal_id,\n               DirectionalIdMap<Dim, evolution::dg::subcell::InitialTciData>>;\n\n  template <typename ReceiveDataType>\n  static bool insert_into_inbox(const gsl::not_null<type*> inbox,\n                                const temporal_id& time_step_id,\n                                ReceiveDataType&& data) {\n    auto& current_inbox = (*inbox)[time_step_id];\n\n    const auto& direction_and_element_id = data.first;\n\n    ASSERT(current_inbox.find(direction_and_element_id) == current_inbox.end(),\n           \"Received data from direction \"\n               << direction_and_element_id.direction() << \" and element ID \"\n               << direction_and_element_id.id() << \" more than once\");\n    current_inbox.emplace(std::forward<ReceiveDataType>(data));\n    return true;\n  }\n\n  static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n\n    ss << std::scientific << std::setprecision(16);\n    ss << pad << \"InitialTciDataInbox:\\n\";\n    for (const auto& [action_number, hash_map] : inbox) {\n      ss << pad << \" Action number: \" << action_number << \"\\n\";\n      for (const auto& [key, initial_tci_data] : hash_map) {\n        using ::operator<<;\n        ss << pad << \"  Key: \" << key\n           << \", TCI: \" << initial_tci_data.tci_status << \"\\n\";\n      }\n    }\n\n    return ss.str();\n  }\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/Interpolators.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace intrp {\ntemplate <size_t Dim>\nclass Irregular;\n}  // namespace intrp\n/// \\endcond\n\nnamespace interpolators_detail {\ntemplate <size_t Dim>\nstruct ExtensionDirection {\n  static constexpr size_t dim = Dim;\n\n  // Stores the direction along which a neighbor's ghost data lies\n  // outside of the local element's domain and require extension.\n  // This is used to determine the direction in which to extend the\n  // local element's domain to include the neighbor's ghost data.\n  Direction<Dim> direction_to_extend;\n\n  ExtensionDirection() = default;\n  explicit ExtensionDirection(Direction<Dim> direction_to_extend_v)\n      : direction_to_extend(direction_to_extend_v) {\n    ASSERT(\n        direction_to_extend.dimension() < dim,\n        \"Invalid direction: dimension must be less than the volume dimension.\");\n  }\n\n  // Serialization for Charm++\n  void pup(PUP::er& p) { p | direction_to_extend; };\n};\n}  // namespace interpolators_detail\n\nnamespace evolution::dg::subcell::Tags {\n/*!\n * \\brief An `intrp::Irregular` from our FD grid to our neighbors' FD grid.\n *\n * Values are only set if the neighboring elements' logical coordinate axes\n * are not aligned with ours. I.e., on block boundaries.\n */\ntemplate <size_t Dim>\nstruct InterpolatorsFromFdToNeighborFd : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n};\n\n/*!\n * \\brief An `intrp::Irregular` from our DG grid to our neighbors' FD grid.\n *\n * Values are only set if the neighboring elements' logical coordinate axes\n * are not aligned with ours. I.e., on block boundaries.\n */\ntemplate <size_t Dim>\nstruct InterpolatorsFromDgToNeighborFd : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n};\n\n/*!\n * \\brief An `intrp::Irregular` from our neighbors' DG grid to our FD grid.\n *\n * Values are only set if the neighboring elements' logical coordinate axes\n * are not aligned with ours. I.e., on block boundaries.\n */\ntemplate <size_t Dim>\nstruct InterpolatorsFromNeighborDgToFd : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n};\n\n/*!\n * \\brief Records directions in which neighbor ghost zones fall outside\n * the local element's domain and require extension.\n *\n * This tag is used on block boundaries where logical coordinate axes of\n * neighboring elements are not aligned, and ghost data cannot be\n * directly exchanged.\n */\ntemplate <size_t Dim>\nstruct ExtensionDirections : db::SimpleTag {\n  using type = DirectionMap<Dim, interpolators_detail::ExtensionDirection<Dim>>;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/Jacobians.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::dg::subcell::fd::Tags {\n/// \\brief The inverse Jacobian from the element logical frame to the grid frame\n/// at the cell centers.\n///\n/// Specifically, \\f$\\partial x^{\\bar{i}} / \\partial x^i\\f$, where \\f$\\bar{i}\\f$\n/// denotes the element logical frame and \\f$i\\f$ denotes the grid frame.\ntemplate <size_t Dim>\nstruct InverseJacobianLogicalToGrid : db::SimpleTag {\n  static std::string name() { return \"InverseJacobian(Logical,Grid)\"; }\n  using type =\n      ::InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Grid>;\n};\n\n/// \\brief The determinant of the inverse Jacobian from the element logical\n/// frame to the grid frame at the cell centers.\nstruct DetInverseJacobianLogicalToGrid : db::SimpleTag {\n  static std::string name() { return \"Det(InverseJacobian(Logical,Grid))\"; }\n  using type = Scalar<DataVector>;\n};\n\n/// \\brief The determinant of the inverse Jacobian from the element logical\n/// frame to the inertial frame at the cell centers.\nstruct DetInverseJacobianLogicalToInertial : db::SimpleTag {\n  static std::string name() { return \"Det(InverseJacobian(Logical,Inertial))\"; }\n  using type = Scalar<DataVector>;\n};\n\n/// \\brief The inverse Jacobian from the element logical frame to the inertial\n/// frame at the cell centers.\n///\n/// Specifically, \\f$\\partial x^{\\bar{i}} / \\partial x^i\\f$, where \\f$\\bar{i}\\f$\n/// denotes the element logical frame and \\f$i\\f$ denotes the inertial frame.\ntemplate <size_t Dim>\nstruct InverseJacobianLogicalToInertial : db::SimpleTag {\n  static std::string name() { return \"InverseJacobian(Logical,Inertial)\"; }\n  using type = ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                 Frame::Inertial>;\n};\n\n/// \\brief The inverse Hessian from the element logical frame to the grid frame\n/// at the cell centers.\n///\n/// Specifically, \\f$\\partial^2 x^{\\bar{i}} / {\\partial x^i \\partial x^j}\\f$,\n/// where \\f$\\bar{i}\\f$ denotes the element logical frame and \\f$i\\f$ denotes\n/// the grid frame.\ntemplate <size_t Dim>\nstruct InverseHessianLogicalToGrid : db::SimpleTag {\n  static std::string name() { return \"InverseHessian(Logical,Grid)\"; }\n  using type =\n      ::InverseHessian<DataVector, Dim, Frame::ElementLogical, Frame::Grid>;\n};\n\n/// \\brief The inverse Hessian from the element logical frame to the inertial\n/// frame at the cell centers.\n///\n/// Specifically, \\f$\\partial^2 x^{\\bar{i}} / {\\partial x^i \\partial x^j}\\f$,\n/// where \\f$\\bar{i}\\f$ denotes the element logical frame and \\f$i\\f$ denotes\n/// the inertial frame.\ntemplate <size_t Dim>\nstruct InverseHessianLogicalToInertial : db::SimpleTag {\n  static std::string name() { return \"InverseHessian(Logical,Inertial)\"; }\n  using type =\n      ::InverseHessian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>;\n};\n\n/// Compute item for the inverse jacobian matrix from logical to\n/// grid coordinates\ntemplate <typename MapTagLogicalToGrid, size_t Dim>\nstruct InverseJacobianLogicalToGridCompute : InverseJacobianLogicalToGrid<Dim>,\n                                             db::ComputeTag {\n  static constexpr size_t dim = Dim;\n  using base = InverseJacobianLogicalToGrid<Dim>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      MapTagLogicalToGrid,\n      evolution::dg::subcell::Tags::Coordinates<dim, Frame::ElementLogical>>;\n  static void function(\n      const gsl::not_null<return_type*> inverse_jacobian_logical_to_grid,\n      const ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n      const tnsr::I<DataVector, dim, Frame::ElementLogical>& logical_coords) {\n    *inverse_jacobian_logical_to_grid =\n        logical_to_grid_map.inv_jacobian(logical_coords);\n  }\n};\n\n/// Compute item for the determinant of the inverse jacobian matrix\n/// from logical to grid coordinates\ntemplate <size_t Dim>\nstruct DetInverseJacobianLogicalToGridCompute\n    : DetInverseJacobianLogicalToGrid,\n      db::ComputeTag {\n  static constexpr size_t dim = Dim;\n  using base = DetInverseJacobianLogicalToGrid;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<InverseJacobianLogicalToGrid<Dim>>;\n  static void function(\n      const gsl::not_null<return_type*> det_inverse_jacobian_logical_to_grid,\n      const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                              Frame::Grid>& inverse_jacobian_logical_to_grid) {\n    *det_inverse_jacobian_logical_to_grid =\n        determinant(inverse_jacobian_logical_to_grid);\n  }\n};\n\n/// Compute item for the inverse jacobian matrix from logical to\n/// inertial coordinates\ntemplate <typename MapTagGridToInertial, size_t Dim>\nstruct InverseJacobianLogicalToInertialCompute\n    : InverseJacobianLogicalToInertial<Dim>,\n      db::ComputeTag {\n  static constexpr size_t dim = Dim;\n  using base = InverseJacobianLogicalToInertial<Dim>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<MapTagGridToInertial,\n                 evolution::dg::subcell::Tags::Coordinates<dim, Frame::Grid>,\n                 ::Tags::Time, ::domain::Tags::FunctionsOfTime,\n                 InverseJacobianLogicalToGrid<Dim>>;\n  static void function(\n      const gsl::not_null<return_type*> inverse_jacobian_logical_to_inertial,\n      const ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, dim>&\n          grid_to_inertial_map,\n      const tnsr::I<DataVector, dim, Frame::Grid>& grid_coords,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                              Frame::Grid>& inverse_jacobian_logical_to_grid) {\n    if (grid_to_inertial_map.is_identity()) {\n      // Optimization for time-independent maps; we just point to the\n      // logical-to-grid inverse jacobian.\n      const size_t num_pts = inverse_jacobian_logical_to_grid[0].size();\n      for (size_t storage_index = 0;\n           storage_index < inverse_jacobian_logical_to_grid.size();\n           ++storage_index) {\n        make_const_view(\n            make_not_null(&std::as_const(\n                (*inverse_jacobian_logical_to_inertial)[storage_index])),\n            inverse_jacobian_logical_to_grid[storage_index], 0, num_pts);\n      }\n    } else {\n      set_number_of_grid_points(inverse_jacobian_logical_to_inertial,\n                                get<0>(grid_coords).size());\n      // Get grid to inertial inverse jacobian\n      const auto& inverse_jacobian_grid_to_inertial =\n          grid_to_inertial_map.inv_jacobian(grid_coords, time,\n                                            functions_of_time);\n      for (size_t i = 0; i < Dim; i++) {\n        for (size_t j = 0; j < Dim; j++) {\n          auto& inv_jacobian_component =\n              inverse_jacobian_logical_to_inertial->get(i, j);\n          inv_jacobian_component = 0.;\n          for (size_t k = 0; k < Dim; k++) {\n            inv_jacobian_component +=\n                inverse_jacobian_logical_to_grid.get(i, k) *\n                inverse_jacobian_grid_to_inertial.get(k, j);\n          }\n        }\n      }\n    }\n  }\n};\n\n/// Compute item for the determinant of the inverse jacobian matrix\n/// from logical to inertial coordinates\ntemplate <typename MapTagGridToInertial, size_t Dim>\nstruct DetInverseJacobianLogicalToInertialCompute\n    : DetInverseJacobianLogicalToInertial,\n      db::ComputeTag {\n  static constexpr size_t dim = Dim;\n  using base = DetInverseJacobianLogicalToInertial;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<MapTagGridToInertial, InverseJacobianLogicalToInertial<Dim>,\n                 DetInverseJacobianLogicalToGrid>;\n  static void function(\n      const gsl::not_null<return_type*>\n          det_inverse_jacobian_logical_to_inertial,\n      const ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, dim>&\n          grid_to_inertial_map,\n      const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                              Frame::Inertial>&\n          inverse_jacobian_logical_to_inertial,\n      const Scalar<DataVector>& det_inverse_jacobian_logical_to_grid) {\n    if (grid_to_inertial_map.is_identity()) {\n      const size_t num_pts = get(det_inverse_jacobian_logical_to_grid).size();\n      make_const_view(make_not_null(&std::as_const(\n                          get(*det_inverse_jacobian_logical_to_inertial))),\n                      get(det_inverse_jacobian_logical_to_grid), 0, num_pts);\n    } else {\n      *det_inverse_jacobian_logical_to_inertial =\n          determinant(inverse_jacobian_logical_to_inertial);\n    }\n  }\n};\n\n#ifdef SPECTRE_AUTODIFF\n/// Compute item for the inverse hessian matrix from logical to\n/// grid coordinates\ntemplate <typename MapTagLogicalToGrid, size_t Dim>\nstruct InverseHessianLogicalToGridCompute : InverseHessianLogicalToGrid<Dim>,\n                                            db::ComputeTag {\n  static constexpr size_t dim = Dim;\n  using base = InverseHessianLogicalToGrid<Dim>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      MapTagLogicalToGrid,\n      evolution::dg::subcell::Tags::Coordinates<dim, Frame::ElementLogical>>;\n  static void function(\n      const gsl::not_null<return_type*> inverse_hessian_logical_to_grid,\n      const ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n      const tnsr::I<DataVector, dim, Frame::ElementLogical>& logical_coords) {\n    *inverse_hessian_logical_to_grid =\n        logical_to_grid_map.inv_hessian(logical_coords);\n  }\n};\n\n/// Compute item for the inverse hessian matrix from logical to\n/// inertial coordinates\ntemplate <typename MapTagGridToInertial, size_t Dim>\nstruct InverseHessianLogicalToInertialCompute\n    : InverseHessianLogicalToInertial<Dim>,\n      db::ComputeTag {\n  static constexpr size_t dim = Dim;\n  using base = InverseHessianLogicalToInertial<Dim>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<MapTagGridToInertial,\n                 evolution::dg::subcell::Tags::Coordinates<dim, Frame::Grid>,\n                 ::Tags::Time, ::domain::Tags::FunctionsOfTime,\n                 InverseJacobianLogicalToGrid<Dim>,\n                 InverseHessianLogicalToGrid<Dim>>;\n  static void function(\n      const gsl::not_null<return_type*> inverse_hessian_logical_to_inertial,\n      const ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, dim>&\n          grid_to_inertial_map,\n      const tnsr::I<DataVector, dim, Frame::Grid>& grid_coords,\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                              Frame::Grid>& inverse_jacobian_logical_to_grid,\n      const ::InverseHessian<DataVector, Dim, Frame::ElementLogical,\n                             Frame::Grid>& inverse_hessian_logical_to_grid) {\n    if (grid_to_inertial_map.is_identity()) {\n      // Optimization for time-independent maps; we just point to the\n      // logical-to-grid inverse hessian.\n      const size_t num_pts = inverse_hessian_logical_to_grid[0].size();\n      for (size_t storage_index = 0;\n           storage_index < inverse_hessian_logical_to_grid.size();\n           ++storage_index) {\n        make_const_view(\n            make_not_null(&std::as_const(\n                (*inverse_hessian_logical_to_inertial)[storage_index])),\n            inverse_hessian_logical_to_grid[storage_index], 0, num_pts);\n      }\n    } else {\n      set_number_of_grid_points(inverse_hessian_logical_to_inertial,\n                                get<0>(grid_coords).size());\n      // Get grid to inertial inverse jacobian\n      const auto& inverse_jacobian_grid_to_inertial =\n          grid_to_inertial_map.inv_jacobian(grid_coords, time,\n                                            functions_of_time);\n      // Get grid to inertial inverse hessian\n      const auto& inverse_hessian_grid_to_inertial =\n          grid_to_inertial_map.inv_hessian(grid_coords,\n                                           inverse_jacobian_grid_to_inertial,\n                                           time, functions_of_time);\n      for (size_t i = 0; i < Dim; i++) {\n        for (size_t j = 0; j < Dim; j++) {\n          for (size_t k = j; k < Dim; k++) {\n            auto& inv_hessian_component =\n                inverse_hessian_logical_to_inertial->get(i, j, k);\n            inv_hessian_component = 0.;\n            for (size_t n = 0; n < Dim; n++) {\n              inv_hessian_component +=\n                  inverse_hessian_grid_to_inertial.get(n, j, k) *\n                  inverse_jacobian_logical_to_grid.get(i, n);\n              for (size_t m = 0; m < Dim; m++) {\n                inv_hessian_component +=\n                    inverse_jacobian_grid_to_inertial.get(n, k) *\n                    inverse_hessian_logical_to_grid.get(i, m, n) *\n                    inverse_jacobian_grid_to_inertial.get(m, j);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n};\n#endif  // SPECTRE_AUTODIFF\n}  // namespace evolution::dg::subcell::fd::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/Mesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\ntemplate <size_t VolumeDim>\nvoid MeshCompute<VolumeDim>::function(\n    const gsl::not_null<return_type*> subcell_mesh,\n    const ::Mesh<VolumeDim>& dg_mesh) {\n  *subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template struct MeshCompute<GET_DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef GET_DIM\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/Mesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// @{\n/// The mesh on the subcells\ntemplate <size_t VolumeDim>\nstruct Mesh : db::SimpleTag {\n  static std::string name() { return \"Subcell(Mesh)\"; }\n  using type = ::Mesh<VolumeDim>;\n};\n\ntemplate <size_t VolumeDim>\nstruct MeshCompute : Mesh<VolumeDim>, db::ComputeTag {\n  using base = Mesh<VolumeDim>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<::domain::Tags::Mesh<VolumeDim>>;\n  static void function(gsl::not_null<return_type*> subcell_mesh,\n                       const ::Mesh<VolumeDim>& dg_mesh);\n};\n/// @}\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/MeshForGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\n/// \\cond\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/*!\n * \\brief Holds the volume Mesh used by each neighbor for communicating subcell\n * ghost data.\n *\n * \\details This will be the subcell Mesh unless an Element doing DG has only\n * neighbors also doing DG\n */\ntemplate <size_t Dim>\nstruct MeshForGhostData : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, ::Mesh<Dim>>;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/MethodOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Tags/MethodOrder.hpp\"\n\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\ntemplate <size_t Dim>\nvoid MethodOrderCompute<Dim>::function(\n    const gsl::not_null<return_type*> method_order, const ::Mesh<Dim>& dg_mesh,\n    const ::Mesh<Dim>& subcell_mesh, const subcell::ActiveGrid active_grid,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::ElementLogical>>&\n        reconstruction_order,\n    const subcell::SubcellOptions& subcell_options) {\n  if (not method_order->has_value()) {\n    *method_order = typename return_type::value_type{};\n  }\n  if (active_grid == subcell::ActiveGrid::Dg) {\n    set_number_of_grid_points(make_not_null(&method_order->value()),\n                              dg_mesh.number_of_grid_points());\n    for (size_t i = 0; i < Dim; ++i) {\n      method_order->value()[i] = dg_mesh.extents(i);\n    }\n  } else {\n    set_number_of_grid_points(make_not_null(&method_order->value()),\n                              subcell_mesh.number_of_grid_points());\n    if (reconstruction_order.has_value()) {\n      *method_order = reconstruction_order;\n    } else {\n      if (UNLIKELY(static_cast<int>(\n                       subcell_options.finite_difference_derivative_order()) <\n                   0)) {\n        ERROR(\n            \"Using adaptive order reconstruction but the reconstruction order \"\n            \"was not set.\");\n      }\n      for (size_t i = 0; i < Dim; ++i) {\n        method_order->value()[i] = static_cast<int>(\n            subcell_options.finite_difference_derivative_order());\n      }\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template struct MethodOrderCompute<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/MethodOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ReconstructionOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <size_t Dim>\nclass Mesh;\nnamespace evolution::dg::subcell {\nclass SubcellOptions;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// @{\n/// The order of the numerical method used.\n///\n/// \\note This is intended to be used during observations.\ntemplate <size_t Dim>\nstruct MethodOrder : db::SimpleTag {\n  using type = std::optional<tnsr::I<DataVector, Dim, Frame::ElementLogical>>;\n};\n\ntemplate <size_t Dim>\nstruct MethodOrderCompute : db::ComputeTag, MethodOrder<Dim> {\n  using base = MethodOrder<Dim>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<::domain::Tags::Mesh<Dim>, Mesh<Dim>, ActiveGrid,\n                 ReconstructionOrder<Dim>, SubcellOptions<Dim>>;\n  static void function(\n      gsl::not_null<return_type*> method_order, const ::Mesh<Dim>& dg_mesh,\n      const ::Mesh<Dim>& subcell_mesh, subcell::ActiveGrid active_grid,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::ElementLogical>>&\n          reconstruction_order,\n      const subcell::SubcellOptions& subcell_options);\n};\n/// @}\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/ObserverCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/*!\n * \\brief \"Computes\" the active coordinates by setting the `DataVector`s to\n * point into the coordinates of either the DG or subcell grid.\n */\ntemplate <size_t Dim, typename Fr>\nstruct ObserverCoordinatesCompute\n    : db::ComputeTag,\n      ::Events::Tags::ObserverCoordinates<Dim, Fr> {\n  using base = ::Events::Tags::ObserverCoordinates<Dim, Fr>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<ActiveGrid, Coordinates<Dim, Fr>,\n                                   ::domain::Tags::Coordinates<Dim, Fr>>;\n  static void function(const gsl::not_null<return_type*> active_coords,\n                       const subcell::ActiveGrid active_grid,\n                       const tnsr::I<DataVector, Dim, Fr>& subcell_coords,\n                       const tnsr::I<DataVector, Dim, Fr>& dg_coords) {\n    const auto set_to_refs =\n        [&active_coords](const tnsr::I<DataVector, Dim, Fr>& coords) {\n          for (size_t i = 0; i < Dim; ++i) {\n            active_coords->get(i).set_data_ref(\n                make_not_null(&const_cast<DataVector&>(coords.get(i))));\n          }\n        };\n    if (active_grid == subcell::ActiveGrid::Dg) {\n      set_to_refs(dg_coords);\n    } else {\n      ASSERT(active_grid == subcell::ActiveGrid::Subcell,\n             \"ActiveGrid should be subcell if it isn't DG. Maybe an extra enum \"\n             \"entry was added?\");\n      set_to_refs(subcell_coords);\n    }\n  }\n};\n\n/*!\n * \\brief Computes the active inverse Jacobian.\n */\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct ObserverInverseJacobianCompute\n    : ::Events::Tags::ObserverInverseJacobian<Dim, SourceFrame, TargetFrame>,\n      db::ComputeTag {\n  using base =\n      ::Events::Tags::ObserverInverseJacobian<Dim, SourceFrame, TargetFrame>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<::Events::Tags::ObserverCoordinates<Dim, SourceFrame>,\n                 ::Tags::Time, domain::Tags::FunctionsOfTime,\n                 domain::Tags::ElementMap<Dim, Frame::Grid>,\n                 domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                             Frame::Inertial>>;\n  static void function(\n      const gsl::not_null<return_type*> observer_inverse_jacobian,\n      const tnsr::I<DataVector, Dim, SourceFrame>& source_coords,\n      [[maybe_unused]] const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&\n          grid_to_inertial_map) {\n    static_assert(std::is_same_v<SourceFrame, Frame::ElementLogical> or\n                  std::is_same_v<SourceFrame, Frame::Grid>);\n    static_assert(std::is_same_v<TargetFrame, Frame::Inertial> or\n                  std::is_same_v<TargetFrame, Frame::Grid>);\n    static_assert(not std::is_same_v<SourceFrame, TargetFrame>);\n    if constexpr (std::is_same_v<SourceFrame, Frame::ElementLogical>) {\n      if constexpr (std::is_same_v<TargetFrame, Frame::Inertial>) {\n        if (grid_to_inertial_map.is_identity()) {\n          auto logical_to_grid_inv_jac =\n              logical_to_grid_map.inv_jacobian(source_coords);\n          for (size_t i = 0; i < observer_inverse_jacobian->size(); ++i) {\n            (*observer_inverse_jacobian)[i] =\n                std::move(logical_to_grid_inv_jac[i]);\n          }\n        } else {\n          const auto logical_to_grid_inv_jac =\n              logical_to_grid_map.inv_jacobian(source_coords);\n          const auto grid_to_inertial_inv_jac =\n              grid_to_inertial_map.inv_jacobian(\n                  logical_to_grid_map(source_coords), time, functions_of_time);\n          for (size_t logical_i = 0; logical_i < Dim; ++logical_i) {\n            for (size_t inertial_i = 0; inertial_i < Dim; ++inertial_i) {\n              observer_inverse_jacobian->get(logical_i, inertial_i) =\n                  logical_to_grid_inv_jac.get(logical_i, 0) *\n                  grid_to_inertial_inv_jac.get(0, inertial_i);\n              for (size_t grid_i = 1; grid_i < Dim; ++grid_i) {\n                observer_inverse_jacobian->get(logical_i, inertial_i) +=\n                    logical_to_grid_inv_jac.get(logical_i, grid_i) *\n                    grid_to_inertial_inv_jac.get(grid_i, inertial_i);\n              }\n            }\n          }\n        }\n      } else {\n        *observer_inverse_jacobian =\n            logical_to_grid_map.inv_jacobian(source_coords);\n      }\n    } else {\n      *observer_inverse_jacobian = grid_to_inertial_map.inv_jacobian(\n          source_coords, time, functions_of_time);\n    }\n  }\n};\n\n/*!\n * \\brief Computes the active Jacobian and determinant of the inverse Jacobian.\n */\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct ObserverJacobianAndDetInvJacobianCompute\n    : ::Tags::Variables<tmpl::list<\n          ::Events::Tags::ObserverDetInvJacobian<SourceFrame, TargetFrame>,\n          ::Events::Tags::ObserverJacobian<Dim, SourceFrame, TargetFrame>>>,\n      db::ComputeTag {\n  using base = ::Tags::Variables<tmpl::list<\n      ::Events::Tags::ObserverDetInvJacobian<SourceFrame, TargetFrame>,\n      ::Events::Tags::ObserverJacobian<Dim, SourceFrame, TargetFrame>>>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      ::Events::Tags::ObserverInverseJacobian<Dim, SourceFrame, TargetFrame>>;\n  static void function(const gsl::not_null<return_type*> result,\n                       const InverseJacobian<DataVector, Dim, SourceFrame,\n                                             TargetFrame>& inv_jacobian) {\n    determinant_and_inverse(result, inv_jacobian);\n  }\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/ObserverMesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Tags/ObserverMesh.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\ntemplate <size_t Dim>\nvoid ObserverMeshCompute<Dim>::function(\n    const gsl::not_null<return_type*> active_mesh, const ::Mesh<Dim>& dg_mesh,\n    const ::Mesh<Dim>& subcell_mesh, const subcell::ActiveGrid active_grid) {\n  if (active_grid == subcell::ActiveGrid::Dg) {\n    *active_mesh = dg_mesh;\n  } else {\n    ASSERT(active_grid == subcell::ActiveGrid::Subcell,\n           \"The active grid must be either DG or subcell\");\n    *active_mesh = subcell_mesh;\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class ObserverMeshCompute<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/ObserverMesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// \\brief Computes the active mesh, which is the DG mesh if `ActiveGrid` is\n/// `Dg` and the subcell mesh if `ActiveGrid` is `Subcell`.\ntemplate <size_t Dim>\nstruct ObserverMeshCompute : ::Events::Tags::ObserverMesh<Dim>, db::ComputeTag {\n  using base = ::Events::Tags::ObserverMesh<Dim>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<::domain::Tags::Mesh<Dim>, subcell::Tags::Mesh<Dim>,\n                 subcell::Tags::ActiveGrid>;\n  static void function(gsl::not_null<return_type*> active_mesh,\n                       const ::Mesh<Dim>& dg_mesh,\n                       const ::Mesh<Dim>& subcell_mesh,\n                       const subcell::ActiveGrid active_grid);\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/ObserverMeshVelocity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Tags/ObserverMeshVelocity.hpp\"\n\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\ntemplate <size_t Dim>\nvoid ObserverMeshVelocityCompute<Dim>::function(\n    const gsl::not_null<return_type*> active_mesh_velocity,\n    const subcell::ActiveGrid active_grid,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        dg_mesh_velocity,\n    const ::Mesh<Dim>& dg_mesh, const ::Mesh<Dim>& subcell_mesh) {\n  if (active_grid == subcell::ActiveGrid::Dg) {\n    *active_mesh_velocity = dg_mesh_velocity;\n  } else if (dg_mesh_velocity.has_value()) {\n    // PROJECT\n    *active_mesh_velocity = tnsr::I<DataVector, Dim, Frame::Inertial>{};\n    for (size_t i = 0; i < Dim; ++i) {\n      active_mesh_velocity->value().get(i) =\n          evolution::dg::subcell::fd::project(dg_mesh_velocity.value().get(i),\n                                              dg_mesh, subcell_mesh.extents());\n    }\n  } else {\n    *active_mesh_velocity = std::nullopt;\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class ObserverMeshVelocityCompute<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/ObserverMeshVelocity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/*!\n * \\brief Computes the mesh velocity on the active grid.\n */\ntemplate <size_t Dim>\nstruct ObserverMeshVelocityCompute\n    : db::ComputeTag,\n      ::Events::Tags::ObserverMeshVelocity<Dim, Frame::Inertial> {\n  using base = ::Events::Tags::ObserverMeshVelocity<Dim, Frame::Inertial>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<ActiveGrid, ::domain::Tags::MeshVelocity<Dim, Frame::Inertial>,\n                 ::domain::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>>;\n\n  static void function(\n      gsl::not_null<return_type*> active_mesh_velocity,\n      subcell::ActiveGrid active_grid,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          dg_mesh_velocity,\n      const ::Mesh<Dim>& dg_mesh, const ::Mesh<Dim>& subcell_mesh);\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/OnSubcellFaces.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n\n/// \\cond\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// Mark a tag as the being on the subcell faces\ntemplate <typename Tag, size_t Dim>\nstruct OnSubcellFaces : db::PrefixTag, db::SimpleTag {\n  using type = std::array<typename Tag::type, Dim>;\n  using tag = Tag;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/OnSubcells.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n\n/// \\cond\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// Mark a tag as the being on the subcell grid\ntemplate <typename Tag>\nstruct OnSubcells : db::PrefixTag, db::SimpleTag {\n  static_assert(tt::is_a_v<Tensor, typename Tag::type>,\n                \"A subcell tag must be either a Tensor or a Variables \"\n                \"currently, though this can be generalized if needed.\");\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// \\cond\n// This tag currently exposes its constituents as subitems. Possible\n// compile-time optimization: Remove this template specialization and use\n// `OnSubcells<VariablesTag<tmpl::list<Tag1, Tag2>>>` instead of the individual\n// `OnSubcells<Tag1>`.\ntemplate <typename TagList>\nstruct OnSubcells<::Tags::Variables<TagList>>\n    : ::Tags::Variables<db::wrap_tags_in<OnSubcells, TagList>> {\n private:\n  using wrapped_tags_list = db::wrap_tags_in<OnSubcells, TagList>;\n\n public:\n  using tag = ::Tags::Variables<TagList>;\n  using type = Variables<wrapped_tags_list>;\n};\n/// \\endcond\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/ReconstructionOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// The reconstruction order\n///\n/// Only set if the reconstruction method actually returns the reconstruction\n/// order used. There can be a different reconstruction order in each logical\n/// dimension of an element.\ntemplate <size_t Dim>\nstruct ReconstructionOrder : db::SimpleTag {\n  using type = std::optional<tnsr::I<DataVector, Dim, Frame::ElementLogical>>;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/StepsSinceTciCall.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/// \\brief Keeps track of the number of steps since the TCI was called on the\n/// FD grid. This is not used on the DG grid.\nstruct StepsSinceTciCall : db::SimpleTag {\n  using type = size_t;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/SubcellOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/OptionsGroup.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace OptionTags {\n/// System-agnostic options for DG-subcell\nstruct SubcellOptions {\n  static std::string name() { return \"Subcell\"; }\n  using type = evolution::dg::subcell::SubcellOptions;\n  static constexpr Options::String help =\n      \"System-agnostic options for DG-subcell\";\n  using group = ::dg::OptionTags::DiscontinuousGalerkinGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// System-agnostic options for DG-subcell\ntemplate <size_t Dim>\nstruct SubcellOptions : db::SimpleTag {\n  using type = evolution::dg::subcell::SubcellOptions;\n\n  using option_tags = tmpl::list<OptionTags::SubcellOptions,\n                                 ::domain::OptionTags::DomainCreator<Dim>>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const type& subcell_options,\n      const std::unique_ptr<DomainCreator<Dim>>& domain_creator) {\n    return {subcell_options, *domain_creator};\n  }\n};\n}  // namespace Tags\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/SubcellSolver.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/SpatialDiscretization/OptionTags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace evolution::dg::subcell::OptionTags {\n/*!\n * \\brief Group holding options for controlling the subcell solver\n * discretization.\n *\n * For example, this would hold the reconstruction scheme or order of the finite\n * difference derivatives.\n *\n * \\note The `SubcellSolverGroup` is a subgroup of\n * `SpatialDiscretization::OptionTags::SpatialDiscretizationGroup`.\n */\nstruct SubcellSolverGroup {\n  static std::string name() { return \"SubcellSolver\"; }\n  static constexpr Options::String help{\n      \"Options controlling the subcell solver spatial discretization \"\n      \"of the PDE system.\\n\\n\"\n      \"Contains options such as what reconstruction scheme to use or what \"\n      \"order of finite difference derivatives to apply.\"};\n  using group = SpatialDiscretization::OptionTags::SpatialDiscretizationGroup;\n};\n}  // namespace evolution::dg::subcell::OptionTags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace evolution::dg::subcell {\n/// \\ingroup DgSubcellGroup\n/// \\brief Option tags for the DG-subcell solver\nnamespace OptionTags {}\n/// \\ingroup DgSubcellGroup\n/// \\brief %Tags for the DG-subcell solver\nnamespace Tags {}\n\nnamespace fd {\n/// \\ingroup DgSubcellGroup\n/// \\brief %Tags for the DG-subcell finite difference solver\nnamespace Tags {}\n}  // namespace fd\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/TciCallsSinceRollback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/// \\brief Keeps track of the number of times the TCI was called after a\n/// rollback.\nstruct TciCallsSinceRollback : db::SimpleTag {\n  using type = size_t;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/TciGridHistory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <deque>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\n/// A record of which grid the TCI requested we use.\n///\n/// This is necessary because when using linear multistep methods for the time\n/// integration we need to wait until the entire history is smooth before\n/// returning to DG. For an Nth order integration in time, this means we need N\n/// steps where the TCI has decided the solution is representable using DG.\n///\n/// The front of the history is the most recent entry.\nstruct TciGridHistory : db::SimpleTag {\n  using type = std::deque<evolution::dg::subcell::ActiveGrid>;\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/TciStatus.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell::Tags {\ntemplate <size_t Dim>\nvoid TciStatusCompute<Dim>::function(const gsl::not_null<return_type*> result,\n                                     const int tci_decision,\n                                     const subcell::ActiveGrid active_grid,\n                                     const ::Mesh<Dim>& subcell_mesh,\n                                     const ::Mesh<Dim>& dg_mesh) {\n  get(*result).destructive_resize(active_grid == subcell::ActiveGrid::Dg\n                                      ? dg_mesh.number_of_grid_points()\n                                      : subcell_mesh.number_of_grid_points());\n  get(*result) = tci_decision;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class TciStatusCompute<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/Tags/TciStatus.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace evolution::dg::subcell::Tags {\n/// Stores the status of the troubled cell indicator in the element as an `int`.\n///\n/// A non-zero value indicates the TCI decided the element is troubled.\nstruct TciDecision : db::SimpleTag {\n  using type = int;\n};\n\n/// The TCI decision of neighboring elements.\ntemplate <size_t Dim>\nstruct NeighborTciDecisions : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, int>;\n};\n\n/// Stores the status of the troubled cell indicator in the element\n/// (TciDecision) as a `Scalar<DataVector>` so it can be observed.\nstruct TciStatus : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// Compute tag to get a `TciStatus` from a `TciDecision`.\ntemplate <size_t Dim>\nstruct TciStatusCompute : db::ComputeTag, TciStatus {\n  using base = TciStatus;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<Tags::TciDecision, Tags::ActiveGrid,\n                                   Tags::Mesh<Dim>, ::domain::Tags::Mesh<Dim>>;\n  static void function(gsl::not_null<return_type*> result, int tci_decision,\n                       subcell::ActiveGrid active_grid,\n                       const ::Mesh<Dim>& subcell_mesh,\n                       const ::Mesh<Dim>& dg_mesh);\n};\n}  // namespace evolution::dg::subcell::Tags\n"
  },
  {
    "path": "src/Evolution/DgSubcell/TwoMeshRdmpTci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\n/*!\n * \\brief Troubled cell indicator using a relaxed discrete maximum principle,\n * comparing the solution on two grids at the same point in time.\n *\n * Checks that the subcell solution \\f$\\underline{u}\\f$ and the DG solution\n * \\f$u\\f$ satisfy\n *\n * \\f{align*}{ \\min(u)-\\delta \\le \\underline{u} \\le \\max(u)+\\delta \\f}\n *\n * where\n *\n * \\f{align*}{ \\delta = \\max\\left[\\delta_0, \\epsilon(\\max(u) - \\min(u))\\right]\n * \\f}\n *\n * where \\f$\\delta_0\\f$ and \\f$\\epsilon\\f$ are constants controlling the maximum\n * absolute and relative change allowed when projecting the DG solution to the\n * subcell grid. We currently specify one value of \\f$\\delta_0\\f$ and\n * \\f$\\epsilon\\f$ for all variables, but this could be generalized to choosing\n * the allowed variation in a variable-specific manner.\n *\n * If all checks are passed and cell is not troubled, returns an integer `0`.\n * Otherwise returns 1-based index of the tag in the input Variables that fails\n * the check. For instance, if we have\n *\n *  - `Variables<tmpl::list<DgVar1, DgVar2, DgVar3>>` for `dg_evolved_vars`\n *  - `Variables<tmpl::list<SubVar1, SubVar2, SubVar3>>` for\n *    `subcell_evolved_vars`\n *\n * as inputs and TCI flags the second pair `DgVar2` and `SubVar2` not satisfying\n * two-mesh RDMP criteria, returned value is `2` since the second pair of tags\n * failed the check.\n *\n * \\note Once a single pair of tags fails to satisfy the check, checks for the\n * remaining part of the input variables are skipped. In the example above, for\n * instance if the second pair (`DgVar2`,`SubVar2`) is flagged, the third pair\n * (`DgVar3`,`SubVar3`) is ignored and not checked.\n *\n */\ntemplate <typename... DgEvolvedVarsTags, typename... SubcellEvolvedVarsTags>\nint two_mesh_rdmp_tci(\n    const Variables<tmpl::list<DgEvolvedVarsTags...>>& dg_evolved_vars,\n    const Variables<tmpl::list<SubcellEvolvedVarsTags...>>&\n        subcell_evolved_vars,\n    const double rdmp_delta0, const double rdmp_epsilon) {\n  static_assert(sizeof...(DgEvolvedVarsTags) ==\n                sizeof...(SubcellEvolvedVarsTags));\n  ASSERT(rdmp_delta0 > 0.0, \"The RDMP delta0 parameter must be positive.\");\n  ASSERT(rdmp_epsilon > 0.0, \"The RDMP epsilon parameter must be positive.\");\n\n  bool cell_is_troubled = false;\n  int tci_status = 0;\n  size_t tag_index = 0;\n\n  tmpl::for_each<\n      tmpl::list<tmpl::list<DgEvolvedVarsTags, SubcellEvolvedVarsTags>...>>(\n      [&cell_is_troubled, &tag_index, &dg_evolved_vars, rdmp_delta0,\n       rdmp_epsilon, &subcell_evolved_vars, &tci_status](auto tag_v) {\n        if (cell_is_troubled) {\n          return;\n        }\n\n        using tags_list = tmpl::type_from<decltype(tag_v)>;\n        using tag = tmpl::front<tags_list>;\n        using inactive_tag = tmpl::back<tags_list>;\n        const auto& dg_var = get<tag>(dg_evolved_vars);\n        const auto& subcell_var = get<inactive_tag>(subcell_evolved_vars);\n\n        for (auto dg_it = dg_var.begin(), subcell_it = subcell_var.begin();\n             dg_it != dg_var.end() and subcell_it != subcell_var.end();\n             (void)++dg_it, (void)++subcell_it) {\n          ASSERT(not cell_is_troubled,\n                 \"If a cell has already been marked as troubled during the \"\n                 \"two mesh RDMP TCI, we should not be continuing to check \"\n                 \"other variables.\");\n          using std::max;\n          using std::min;\n\n          const double max_dg = max(*dg_it);\n          const double min_dg = min(*dg_it);\n          const double max_subcell = max(*subcell_it);\n          const double min_subcell = min(*subcell_it);\n          const double delta =\n              max(rdmp_delta0, rdmp_epsilon * (max_dg - min_dg));\n          cell_is_troubled =\n              max_subcell > max_dg + delta or min_subcell < min_dg - delta;\n          if (cell_is_troubled) {\n            tci_status = static_cast<int>(tag_index + 1);\n            return;\n          }\n          ++tag_index;\n        }\n      });\n  return tci_status;\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <atomic>\n#include <cstddef>\n#include <limits>\n#include <map>\n#include <mutex>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/UsingSubcell.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/SelfStart.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\n\nnamespace evolution::dg::subcell {\n// We use a forward declaration instead of including a header file to avoid\n// coupling to the DG-subcell libraries for executables that don't use subcell.\ntemplate <size_t VolumeDim, typename DgComputeSubcellNeighborPackagedData>\nvoid neighbor_reconstructed_face_solution(gsl::not_null<db::Access*> box);\ntemplate <size_t Dim>\nvoid neighbor_tci_decision(\n    gsl::not_null<db::Access*> box,\n    const DirectionalId<Dim>& directional_element_id,\n    const evolution::dg::BoundaryData<Dim>& neighbor_data);\ntemplate <size_t VolumeDim>\nvoid receive_subcell_data_for_dg(\n    gsl::not_null<db::Access*> box, const DirectionalId<VolumeDim>& mortar_id,\n    const evolution::dg::BoundaryData<VolumeDim>& received_mortar_data);\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace evolution::dg {\nnamespace detail {\ntemplate <typename BoundaryCorrectionClass>\nstruct get_dg_boundary_terms {\n  using type = typename BoundaryCorrectionClass::dg_boundary_terms_volume_tags;\n};\n\ntemplate <typename Tag, typename Type = db::const_item_type<Tag, tmpl::list<>>>\nstruct TemporaryReference {\n  using tag = Tag;\n  using type = const Type&;\n};\n}  // namespace detail\n\n/// Move boundary data from the inbox to the DataBox.  Returns true if\n/// all necessary data has been received.\n///\n/// Setting \\p DenseOutput to true receives data required for output\n/// at `::Tags::Time` instead of `::Tags::Next<::Tags::TimeStepId>`.\n///\n/// If \\p LocalTimeStepping is true, it will process all data\n/// necessary for conservative LTS, otherwise it will process all data\n/// necessary for GTS.  Some data for the other mode may also be\n/// processed to simplify the message handling.\ntemplate <bool UseNodegroupDgElements, typename Metavariables,\n          bool LocalTimeStepping, bool DenseOutput, typename DbTagsList,\n          typename... InboxTags>\nbool receive_boundary_data(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes) {\n  constexpr size_t volume_dim = Metavariables::system::volume_dim;\n  constexpr size_t face_dim = volume_dim - 1;\n  static_assert(LocalTimeStepping or not DenseOutput,\n                \"Should not be receiving data for dense output with GTS.\");\n\n  auto& inbox =\n      tuples::get<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n          volume_dim, UseNodegroupDgElements>>(*inboxes);\n\n  const auto& volume_mesh = db::get<domain::Tags::Mesh<volume_dim>>(*box);\n  const auto& mortar_infos = db::get<Tags::MortarInfo<volume_dim>>(*box);\n  const auto& mortar_next_time_step_ids =\n      db::get<evolution::dg::Tags::MortarNextTemporalId<volume_dim>>(*box);\n\n  for (;;) {\n    std::optional<TimeStepId> time_to_process{};\n    for (const auto& [mortar_id, mortar_next_time_step_id] :\n         mortar_next_time_step_ids) {\n      if (time_to_process.has_value() and\n          mortar_next_time_step_id > *time_to_process) {\n        continue;\n      }\n\n      const auto& time_stepping_policy =\n          mortar_infos.at(mortar_id).time_stepping_policy();\n      switch (time_stepping_policy) {\n        case TimeSteppingPolicy::EqualRate:\n          if (LocalTimeStepping or\n              mortar_next_time_step_id > db::get<::Tags::TimeStepId>(*box)) {\n            continue;\n          }\n          break;\n        case TimeSteppingPolicy::Conservative:\n          if constexpr (not LocalTimeStepping) {\n            continue;\n          } else {\n            const LtsTimeStepper& time_stepper =\n                db::get<::Tags::TimeStepper<LtsTimeStepper>>(*box);\n            using goal_tag =\n                tmpl::conditional_t<DenseOutput, ::Tags::Time,\n                                    ::Tags::Next<::Tags::TimeStepId>>;\n            const auto& goal = db::get<goal_tag>(*box);\n            if (not time_stepper.neighbor_data_required(\n                    goal, mortar_next_time_step_id)) {\n              continue;\n            }\n          }\n          break;\n        default:\n          ERROR(\"Unhandled TimeSteppingPolicy: \" << time_stepping_policy);\n      }\n\n      time_to_process.emplace(mortar_next_time_step_id);\n    }\n\n    if (not time_to_process.has_value()) {\n      if constexpr (using_subcell_v<Metavariables> and not LocalTimeStepping) {\n        evolution::dg::subcell::neighbor_reconstructed_face_solution<\n            volume_dim, typename Metavariables::SubcellOptions::\n                            DgComputeSubcellNeighborPackagedData>(\n            &db::as_access(*box));\n      }\n      return true;\n    }\n\n    const auto expected_messages = static_cast<size_t>(alg::count_if(\n        mortar_next_time_step_ids,\n        [&](const auto& entry) { return entry.second == *time_to_process; }));\n\n    // This is a\n    //\n    // std::map<TimeStepId,\n    //          V<std::pair<DirectionalId<volume_dim>,\n    //                      evolution::dg::BoundaryData<volume_dim>>>,\n    //\n    // where V<> is a vector-like type the details of which we don't\n    // want to hardcode here.\n    auto& inbox_data = inbox.messages;\n    auto messages_to_process = inbox_data.end();\n\n    {\n      size_t missing_messages{};\n      do {\n        inbox.collect_messages();\n        if (messages_to_process == inbox_data.end()) {\n          messages_to_process = inbox_data.find(*time_to_process);\n        }\n        const size_t available_messages =\n            messages_to_process == inbox_data.end()\n                ? 0\n                : messages_to_process->second.size();\n        ASSERT(available_messages <= expected_messages,\n               \"Too many boundary messages at \" << *time_to_process << \": \"\n                                                << available_messages << \"/\"\n                                                << expected_messages);\n        missing_messages = expected_messages - available_messages;\n      } while (missing_messages != 0 and\n               inbox.set_missing_messages(missing_messages));\n      if (missing_messages != 0) {\n        return false;\n      }\n    }\n\n    // *time_to_process represents the same temporal event as this,\n    // but may have an out-of-date slab size because the\n    // MortarNextTemporalId data can be sent before the slab size is\n    // chosen.  It is important that the corrected version be what is\n    // inserted into the boundary history.\n    const TimeStepId processing_time = messages_to_process->first;\n\n    for (auto& mortar_id_and_data : messages_to_process->second) {\n      const auto& mortar_id = mortar_id_and_data.first;\n      auto& received_mortar_data = mortar_id_and_data.second;\n      const size_t sliced_away_dim = mortar_id.direction().dimension();\n      const Mesh<face_dim> face_mesh = volume_mesh.slice_away(sliced_away_dim);\n      ASSERT(mortar_next_time_step_ids.at(mortar_id) == processing_time,\n             \"Processing wrong time for mortar \"\n                 << mortar_id << \"\\nExpected \"\n                 << mortar_next_time_step_ids.at(mortar_id)\n                 << \" but processing \" << processing_time);\n\n      const auto& time_stepping_policy =\n          mortar_infos.at(mortar_id).time_stepping_policy();\n\n      if constexpr (using_subcell_v<Metavariables>) {\n        if (time_stepping_policy == TimeSteppingPolicy::EqualRate) {\n          evolution::dg::subcell::receive_subcell_data_for_dg<volume_dim>(\n              &db::as_access(*box), mortar_id, received_mortar_data);\n          evolution::dg::subcell::neighbor_tci_decision<volume_dim>(\n              make_not_null(&db::as_access(*box)), mortar_id,\n              received_mortar_data);\n        }\n      }\n\n      db::mutate<evolution::dg::Tags::MortarMesh<volume_dim>,\n                 evolution::dg::Tags::MortarData<volume_dim>,\n                 evolution::dg::Tags::MortarDataHistory<volume_dim>,\n                 evolution::dg::Tags::MortarNextTemporalId<volume_dim>,\n                 domain::Tags::NeighborMesh<volume_dim>>(\n          [&](const gsl::not_null<DirectionalIdMap<volume_dim, Mesh<face_dim>>*>\n                  mortar_meshes,\n              const gsl::not_null<DirectionalIdMap<\n                  volume_dim, evolution::dg::MortarDataHolder<volume_dim>>*>\n                  gts_mortar_data,\n              const gsl::not_null<DirectionalIdMap<\n                  volume_dim,\n                  TimeSteppers::BoundaryHistory<\n                      evolution::dg::MortarData<volume_dim>,\n                      evolution::dg::MortarData<volume_dim>, DataVector>>*>\n                  boundary_data_history,\n              const gsl::not_null<DirectionalIdMap<volume_dim, TimeStepId>*>\n                  mortar_next_time_step_ids_mutable,\n              const gsl::not_null<\n                  DirectionalIdMap<volume_dim, Mesh<volume_dim>>*>\n                  neighbor_mesh) {\n            const Mesh<face_dim> neighbor_face_mesh =\n                received_mortar_data.volume_mesh.slice_away(sliced_away_dim);\n            const Mesh<face_dim> mortar_mesh =\n                ::dg::mortar_mesh(face_mesh, neighbor_face_mesh);\n\n            const auto project_boundary_mortar_data =\n                [&mortar_mesh](const TimeStepId& /*id*/,\n                               const gsl::not_null<\n                                   ::evolution::dg::MortarData<volume_dim>*>\n                                   mortar_data) {\n                  return p_project_mortar_data(mortar_data, mortar_mesh);\n                };\n\n            mortar_meshes->at(mortar_id) = mortar_mesh;\n            switch (time_stepping_policy) {\n              case TimeSteppingPolicy::EqualRate:\n                p_project_mortar_data(\n                    make_not_null(&gts_mortar_data->at(mortar_id).local()),\n                    mortar_mesh);\n                break;\n              case TimeSteppingPolicy::Conservative:\n                boundary_data_history->at(mortar_id).local().for_each(\n                    project_boundary_mortar_data);\n                break;\n              default:\n                ERROR(\"Unhandled TimeSteppingPolicy: \" << time_stepping_policy);\n            }\n\n            neighbor_mesh->insert_or_assign(mortar_id,\n                                            received_mortar_data.volume_mesh);\n            mortar_next_time_step_ids_mutable->at(mortar_id) =\n                received_mortar_data.validity_range;\n\n            ASSERT(\n                using_subcell_v<Metavariables> or\n                    received_mortar_data.boundary_correction_data.has_value(),\n                \"Must receive neighbor boundary correction data when \"\n                \"not using DG-subcell. Mortar ID is: (\"\n                    << mortar_id.direction() << \",\" << mortar_id.id()\n                    << \") and TimeStepId is \" << processing_time);\n            MortarData<volume_dim> neighbor_mortar_data{};\n            neighbor_mortar_data.face_mesh = neighbor_face_mesh;\n            neighbor_mortar_data.mortar_mesh =\n                received_mortar_data.boundary_correction_mesh;\n            neighbor_mortar_data.mortar_data =\n                std::move(received_mortar_data.boundary_correction_data);\n            switch (time_stepping_policy) {\n              case TimeSteppingPolicy::EqualRate:\n                if (neighbor_mortar_data.mortar_data.has_value()) {\n                  p_project_mortar_data(make_not_null(&neighbor_mortar_data),\n                                        mortar_mesh);\n                }\n                gts_mortar_data->at(mortar_id).neighbor() =\n                    std::move(neighbor_mortar_data);\n                break;\n              case TimeSteppingPolicy::Conservative:\n                ASSERT(neighbor_mortar_data.mortar_data.has_value(),\n                       \"Did not receive mortar data for \" << mortar_id);\n                boundary_data_history->at(mortar_id).remote().insert(\n                    processing_time, received_mortar_data.integration_order,\n                    std::move(neighbor_mortar_data));\n                boundary_data_history->at(mortar_id).remote().for_each(\n                    project_boundary_mortar_data);\n                break;\n              default:\n                ERROR(\"Unhandled TimeSteppingPolicy: \" << time_stepping_policy);\n            }\n          },\n          box);\n    }\n\n    inbox_data.erase(messages_to_process);\n  }\n}\n\n/// Apply corrections from boundary communication.\n///\n/// This is usually used indirectly through\n/// `ApplyBoundaryCorrectionsToTimeDerivative`,\n/// `ApplyLtsBoundaryCorrections`, or `ApplyLtsDenseBoundaryCorrections`.\n///\n/// If `LocalTimeStepping` is false, updates the derivative of the variables,\n/// which should be done before taking a time step.  If\n/// `LocalTimeStepping` is true, updates the variables themselves, which should\n/// be done after the volume update.\n///\n/// Setting \\p DenseOutput to true receives data required for output\n/// at ::Tags::Time instead of performing a full step.  This is only\n/// used for local time-stepping.\ntemplate <bool LocalTimeStepping, typename Metavariables, bool DenseOutput>\nstruct ApplyBoundaryCorrections {\n  static constexpr bool local_time_stepping = LocalTimeStepping;\n  static_assert(local_time_stepping or not DenseOutput,\n                \"GTS does not use ApplyBoundaryCorrections for dense output.\");\n\n  using system = typename Metavariables::system;\n  static constexpr size_t volume_dim = system::volume_dim;\n  using variables_tag = typename system::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  using DtVariables = typename dt_variables_tag::type;\n  using derived_boundary_corrections =\n      tmpl::at<typename Metavariables::factory_creation::factory_classes,\n               evolution::BoundaryCorrection>;\n  using volume_tags_for_dg_boundary_terms = tmpl::remove_duplicates<\n      tmpl::flatten<tmpl::transform<derived_boundary_corrections,\n                                    detail::get_dg_boundary_terms<tmpl::_1>>>>;\n\n  using TimeStepperType =\n      tmpl::conditional_t<local_time_stepping, LtsTimeStepper, TimeStepper>;\n\n  using tag_to_update =\n      tmpl::conditional_t<local_time_stepping, variables_tag, dt_variables_tag>;\n  using mortar_data_tag =\n      tmpl::conditional_t<local_time_stepping,\n                          evolution::dg::Tags::MortarDataHistory<volume_dim>,\n                          evolution::dg::Tags::MortarData<volume_dim>>;\n\n  using return_tags = tmpl::list<tag_to_update>;\n  using argument_tags = tmpl::append<\n      tmpl::flatten<tmpl::list<\n          mortar_data_tag, domain::Tags::Mesh<volume_dim>,\n          domain::Tags::Element<volume_dim>, Tags::MortarMesh<volume_dim>,\n          Tags::MortarInfo<volume_dim>, ::dg::Tags::Formulation,\n          evolution::dg::Tags::NormalCovectorAndMagnitude<volume_dim>,\n          ::Tags::TimeStepper<TimeStepperType>,\n          evolution::Tags::BoundaryCorrection,\n          tmpl::conditional_t<DenseOutput, ::Tags::Time, ::Tags::TimeStep>,\n          tmpl::conditional_t<local_time_stepping, tmpl::list<>,\n                              domain::Tags::DetInvJacobian<\n                                  Frame::ElementLogical, Frame::Inertial>>>>,\n      volume_tags_for_dg_boundary_terms>;\n\n  // full step\n  template <typename... VolumeArgs>\n  static void apply(\n      const gsl::not_null<typename tag_to_update::type*> vars_to_update,\n      const typename mortar_data_tag::type& mortar_data,\n      const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,\n      const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,\n      const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,\n      const ::dg::Formulation dg_formulation,\n      const DirectionMap<\n          volume_dim, std::optional<Variables<tmpl::list<\n                          evolution::dg::Tags::MagnitudeOfNormal,\n                          evolution::dg::Tags::NormalCovector<volume_dim>>>>>&\n          face_normal_covector_and_magnitude,\n      const TimeStepperType& time_stepper,\n      const evolution::BoundaryCorrection& boundary_correction,\n      const TimeDelta& time_step,\n      const Scalar<DataVector>& gts_det_inv_jacobian,\n      const VolumeArgs&... volume_args) {\n    apply_impl(vars_to_update, mortar_data, volume_mesh, element, mortar_meshes,\n               mortar_infos, dg_formulation, face_normal_covector_and_magnitude,\n               time_stepper, boundary_correction, time_step,\n               std::numeric_limits<double>::signaling_NaN(),\n               gts_det_inv_jacobian, volume_args...);\n  }\n\n  template <typename... VolumeArgs>\n  static void apply(\n      const gsl::not_null<typename tag_to_update::type*> vars_to_update,\n      const typename mortar_data_tag::type& mortar_data,\n      const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,\n      const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,\n      const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,\n      const ::dg::Formulation dg_formulation,\n      const DirectionMap<\n          volume_dim, std::optional<Variables<tmpl::list<\n                          evolution::dg::Tags::MagnitudeOfNormal,\n                          evolution::dg::Tags::NormalCovector<volume_dim>>>>>&\n          face_normal_covector_and_magnitude,\n      const TimeStepperType& time_stepper,\n      const evolution::BoundaryCorrection& boundary_correction,\n      const TimeDelta& time_step, const VolumeArgs&... volume_args) {\n    apply_impl(vars_to_update, mortar_data, volume_mesh, element, mortar_meshes,\n               mortar_infos, dg_formulation, face_normal_covector_and_magnitude,\n               time_stepper, boundary_correction, time_step,\n               std::numeric_limits<double>::signaling_NaN(), {},\n               volume_args...);\n  }\n\n  // dense output (LTS only)\n  template <typename... VolumeArgs>\n  static void apply(\n      const gsl::not_null<typename variables_tag::type*> vars_to_update,\n      const typename mortar_data_tag::type& mortar_data,\n      const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,\n      const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,\n      const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,\n      const ::dg::Formulation dg_formulation,\n      const DirectionMap<\n          volume_dim, std::optional<Variables<tmpl::list<\n                          evolution::dg::Tags::MagnitudeOfNormal,\n                          evolution::dg::Tags::NormalCovector<volume_dim>>>>>&\n          face_normal_covector_and_magnitude,\n      const LtsTimeStepper& time_stepper,\n      const evolution::BoundaryCorrection& boundary_correction,\n      const double dense_output_time, const VolumeArgs&... volume_args) {\n    apply_impl(vars_to_update, mortar_data, volume_mesh, element, mortar_meshes,\n               mortar_infos, dg_formulation, face_normal_covector_and_magnitude,\n               time_stepper, boundary_correction, TimeDelta{},\n               dense_output_time, {}, volume_args...);\n  }\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ParallelComponent>\n  static bool is_ready(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> inboxes,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*component*/) {\n    return receive_boundary_data<\n        Parallel::is_dg_element_collection_v<ParallelComponent>, Metavariables,\n        local_time_stepping, DenseOutput>(box, inboxes);\n  }\n\n private:\n  template <typename... VolumeArgs>\n  static void apply_impl(\n      const gsl::not_null<typename tag_to_update::type*> vars_to_update,\n      const typename mortar_data_tag::type& mortar_data,\n      const Mesh<volume_dim>& volume_mesh, const Element<volume_dim>& element,\n      const typename Tags::MortarMesh<volume_dim>::type& mortar_meshes,\n      const typename Tags::MortarInfo<volume_dim>::type& mortar_infos,\n      const ::dg::Formulation dg_formulation,\n      const DirectionMap<\n          volume_dim, std::optional<Variables<tmpl::list<\n                          evolution::dg::Tags::MagnitudeOfNormal,\n                          evolution::dg::Tags::NormalCovector<volume_dim>>>>>&\n          face_normal_covector_and_magnitude,\n      const TimeStepperType& time_stepper,\n      const evolution::BoundaryCorrection& boundary_correction,\n      const TimeDelta& time_step, const double dense_output_time,\n      const Scalar<DataVector>& gts_det_inv_jacobian,\n      const VolumeArgs&... volume_args) {\n    // We treat this as a set, but use a map because we don't have a\n    // non-allocating set type.\n    DirectionalIdMap<volume_dim, bool> mortars_to_act_on{};\n    for (const auto& [mortar, info] : mortar_infos) {\n      const auto& time_stepping_policy = info.time_stepping_policy();\n      switch (time_stepping_policy) {\n        case TimeSteppingPolicy::EqualRate:\n          if (not local_time_stepping) {\n            mortars_to_act_on.emplace(mortar, true);\n          }\n          break;\n        case TimeSteppingPolicy::Conservative:\n          if (local_time_stepping) {\n            mortars_to_act_on.emplace(mortar, true);\n          }\n          break;\n        default:\n          ERROR(\"Unhandled TimeSteppingPolicy: \" << time_stepping_policy);\n      }\n    }\n    if (mortars_to_act_on.empty()) {\n      return;\n    }\n\n    tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n        detail::TemporaryReference, volume_tags_for_dg_boundary_terms>>\n        volume_args_tuple{volume_args...};\n\n    // Set up helper lambda that will compute and lift the boundary corrections\n    ASSERT(\n        volume_mesh.quadrature() ==\n                make_array<volume_dim>(volume_mesh.quadrature(0)) or\n            element.topologies() != domain::topologies::hypercube<volume_dim>,\n        \"Must have isotropic quadrature, but got volume mesh: \" << volume_mesh);\n    Scalar<DataVector> volume_det_inv_jacobian{};\n    Scalar<DataVector> volume_det_jacobian{};\n    if constexpr (not local_time_stepping) {\n      // Need volume Jacobian for any face whose normal direction uses Gauss\n      // points (i.e. not GaussLobatto or GaussRadauUpper). This means\n      // mixed-quadrature non-hypercube elements (e.g. full_cylinder) where\n      // some directions have collocated face points and others do not.\n      const bool any_direction_uses_gauss = alg::any_of(\n          volume_mesh.quadrature(), [](const Spectral::Quadrature q) {\n            return q == Spectral::Quadrature::Gauss;\n          });\n      if (any_direction_uses_gauss) {\n        get(volume_det_inv_jacobian)\n            .set_data_ref(make_not_null(\n                // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n                &const_cast<DataVector&>(get(gts_det_inv_jacobian))));\n        get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);\n      }\n    }\n\n    static_assert(\n        tmpl::all<derived_boundary_corrections, std::is_final<tmpl::_1>>::value,\n        \"All createable classes for boundary corrections must be marked \"\n        \"final.\");\n    call_with_dynamic_type<void, derived_boundary_corrections>(\n        &boundary_correction,\n        [&dense_output_time, &dg_formulation, &element,\n         &face_normal_covector_and_magnitude, &mortar_data, &mortar_meshes,\n         &mortar_infos, &mortars_to_act_on, &time_step, &time_stepper,\n         &vars_to_update, &volume_args_tuple, &volume_det_jacobian,\n         &volume_det_inv_jacobian,\n         &volume_mesh](auto* typed_boundary_correction) {\n          using BcType = std::decay_t<decltype(*typed_boundary_correction)>;\n          // Compute internal boundary quantities on the mortar for sides of\n          // the element that have neighbors, i.e. they are not an external\n          // side.\n          using mortar_tags_list = typename BcType::dg_package_field_tags;\n\n          // Variables for reusing allocations.  The actual values are\n          // not reused.\n          DtVariables dt_boundary_correction_on_mortar{};\n          DtVariables volume_dt_correction{};\n          // These variables may change size for each mortar and require\n          // a new memory allocation, but they may also happen to need\n          // to be the same size twice in a row, in which case holding\n          // on to the allocation is a win.\n          Scalar<DataVector> face_det_jacobian{};\n          Variables<mortar_tags_list> local_data_on_mortar{};\n          Variables<mortar_tags_list> neighbor_data_on_mortar{};\n\n          for (const auto& mortar_id_and_data : mortar_data) {\n            const auto& mortar_id = mortar_id_and_data.first;\n            if (not mortars_to_act_on.contains(mortar_id)) {\n              continue;\n            }\n            const auto& direction = mortar_id.direction();\n            if (UNLIKELY(mortar_id.id() ==\n                         ElementId<volume_dim>::external_boundary_id())) {\n              ERROR(\n                  \"Cannot impose boundary conditions on external boundary in \"\n                  \"direction \"\n                  << direction\n                  << \" in the ApplyBoundaryCorrections action. Boundary \"\n                     \"conditions are applied in the ComputeTimeDerivative \"\n                     \"action \"\n                     \"instead. You may have unintentionally added external \"\n                     \"mortars in one of the initialization actions.\");\n            }\n            if (volume_mesh.basis(direction.dimension()) ==\n                    Spectral::Basis::ZernikeB2 and\n                volume_mesh.quadrature(direction.dimension()) ==\n                    Spectral::Quadrature::GaussRadauUpper and\n                direction.side() != Side::Upper) {\n              ERROR(\n                  \"Trying to use ZernikeB2 basis with GaussRadauUpper \"\n                  \"quadrature on the lower side: there is not a boundary here. \"\n                  \"volume mesh: \"\n                  << volume_mesh << \", element ID \" << element.id());\n            }\n\n            const Mesh<volume_dim - 1> face_mesh =\n                volume_mesh.slice_away(direction.dimension());\n\n            // Whether the mesh has a collocation point on this face. True for\n            // GaussLobatto (points on both faces) and GaussRadauUpper (point\n            // on the upper face only). When true, lifting is done via\n            // lift_flux on the slice; otherwise the full Gauss-point lifting\n            // path is used.\n            const bool using_points_on_face =\n                volume_mesh.quadrature(direction.dimension()) ==\n                    Spectral::Quadrature::GaussLobatto or\n                volume_mesh.quadrature(direction.dimension()) ==\n                    Spectral::Quadrature::GaussRadauUpper;\n\n            const auto compute_correction_coupling =\n                [&typed_boundary_correction, &direction, dg_formulation,\n                 &dt_boundary_correction_on_mortar, &face_det_jacobian,\n                 &face_mesh, &face_normal_covector_and_magnitude,\n                 &local_data_on_mortar, &mortar_id, &mortar_meshes,\n                 &mortar_infos, &neighbor_data_on_mortar, using_points_on_face,\n                 &volume_args_tuple, &volume_det_jacobian,\n                 &volume_det_inv_jacobian, &volume_dt_correction, &volume_mesh](\n                    const MortarData<volume_dim>& local_mortar_data,\n                    const MortarData<volume_dim>& neighbor_mortar_data)\n                -> DtVariables {\n              if (local_time_stepping and not using_points_on_face) {\n                // This needs to be updated every call because the Jacobian\n                // may be time-dependent. In the case of time-independent maps\n                // and local time stepping we could first perform the integral\n                // on the boundaries, and then lift to the volume. This is\n                // left as a future optimization.\n                volume_det_inv_jacobian =\n                    local_mortar_data.volume_det_inv_jacobian.value();\n                get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);\n              }\n              const auto& mortar_mesh = mortar_meshes.at(mortar_id);\n\n              // Extract local and neighbor data, copy into Variables because\n              // we store them in a std::vector for type erasure.\n              ASSERT(*local_mortar_data.mortar_mesh ==\n                             *neighbor_mortar_data.mortar_mesh and\n                         *local_mortar_data.mortar_mesh == mortar_mesh,\n                     \"local mortar mesh: \" << *local_mortar_data.mortar_mesh\n                                           << \"\\nneighbor mortar mesh: \"\n                                           << *neighbor_mortar_data.mortar_mesh\n                                           << \"\\nmortar mesh: \" << mortar_mesh\n                                           << \"\\n\");\n              const DataVector& local_data = *local_mortar_data.mortar_data;\n              const DataVector& neighbor_data =\n                  *neighbor_mortar_data.mortar_data;\n              ASSERT(local_data.size() == neighbor_data.size(),\n                     \"local data size: \"\n                         << local_data.size()\n                         << \"\\nneighbor_data: \" << neighbor_data.size()\n                         << \"\\n mortar_mesh: \" << mortar_mesh << \"\\n\");\n              ASSERT(local_data_on_mortar.number_of_grid_points() ==\n                         neighbor_data_on_mortar.number_of_grid_points(),\n                     \"Local data size = \"\n                         << local_data_on_mortar.number_of_grid_points()\n                         << \", but neighbor size = \"\n                         << neighbor_data_on_mortar.number_of_grid_points());\n              local_data_on_mortar.set_data_ref(\n                  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n                  const_cast<double*>(local_data.data()), local_data.size());\n              neighbor_data_on_mortar.set_data_ref(\n                  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n                  const_cast<double*>(neighbor_data.data()),\n                  neighbor_data.size());\n\n              // The boundary computations and lifting can be further\n              // optimized by in the h-refinement case having only one\n              // allocation for the face and having the projection from the\n              // mortar to the face be done in place. E.g.\n              // local_data_on_mortar and neighbor_data_on_mortar could be\n              // allocated fewer times, as well as `needs_projection` section\n              // below could do an in-place projection.\n              dt_boundary_correction_on_mortar.initialize(\n                  mortar_mesh.number_of_grid_points());\n\n              call_boundary_correction(\n                  make_not_null(&dt_boundary_correction_on_mortar),\n                  local_data_on_mortar, neighbor_data_on_mortar,\n                  *typed_boundary_correction, dg_formulation, volume_args_tuple,\n                  typename BcType::dg_boundary_terms_volume_tags{});\n\n              const std::array<Spectral::SegmentSize, volume_dim - 1>&\n                  mortar_size = mortar_infos.at(mortar_id).mortar_size();\n\n              // This cannot reuse an allocation because it is initialized\n              // via move-assignment.  (If it is used at all.)\n              DtVariables dt_boundary_correction_projected_onto_face{};\n              auto& dt_boundary_correction =\n                  [&dt_boundary_correction_on_mortar,\n                   &dt_boundary_correction_projected_onto_face, &face_mesh,\n                   &mortar_mesh, &mortar_size]() -> DtVariables& {\n                if (Spectral::needs_projection(face_mesh, mortar_mesh,\n                                               mortar_size)) {\n                  dt_boundary_correction_projected_onto_face =\n                      ::dg::project_from_mortar(\n                          dt_boundary_correction_on_mortar, face_mesh,\n                          mortar_mesh, mortar_size);\n                  return dt_boundary_correction_projected_onto_face;\n                }\n                return dt_boundary_correction_on_mortar;\n              }();\n\n              // Both paths initialize this to be non-owning.\n              Scalar<DataVector> magnitude_of_face_normal{};\n              if constexpr (local_time_stepping) {\n                (void)face_normal_covector_and_magnitude;\n                get(magnitude_of_face_normal)\n                    .set_data_ref(make_not_null(&const_cast<DataVector&>(\n                        get(local_mortar_data.face_normal_magnitude.value()))));\n              } else {\n                ASSERT(\n                    face_normal_covector_and_magnitude.count(direction) == 1 and\n                        face_normal_covector_and_magnitude.at(direction)\n                            .has_value(),\n                    \"Face normal covector and magnitude not set in \"\n                    \"direction: \"\n                        << direction);\n                get(magnitude_of_face_normal)\n                    .set_data_ref(make_not_null(&const_cast<DataVector&>(\n                        get(get<evolution::dg::Tags::MagnitudeOfNormal>(\n                            *face_normal_covector_and_magnitude.at(\n                                direction))))));\n              }\n\n              if (using_points_on_face) {\n                // The lift_flux function lifts only on the slice, it does not\n                // add the contribution to the volume.\n                ::dg::lift_flux(make_not_null(&dt_boundary_correction),\n                                volume_mesh.extents(direction.dimension()),\n                                magnitude_of_face_normal,\n                                volume_mesh.basis(direction.dimension()));\n                return std::move(dt_boundary_correction);\n              } else {\n                // We are using Gauss points.\n                //\n                // Notes:\n                // - We should really lift both sides simultaneously since this\n                //   reduces memory accesses. Lifting all sides at the same\n                //   time is unlikely to improve performance since we lift by\n                //   jumping through slices. There may also be compatibility\n                //   issues with local time stepping.\n                // - If we lift both sides at the same time we first need to\n                //   deal with projecting from mortars to the face, then lift\n                //   off the faces. With non-owning Variables memory\n                //   allocations could be significantly reduced in this code.\n                if constexpr (local_time_stepping) {\n                  ASSERT(get(volume_det_inv_jacobian).size() > 0,\n                         \"For local time stepping the volume determinant of \"\n                         \"the inverse Jacobian has not been set.\");\n\n                  get(face_det_jacobian)\n                      .set_data_ref(make_not_null(&const_cast<DataVector&>(\n                          get(local_mortar_data.face_det_jacobian.value()))));\n                } else {\n                  // Project the determinant of the Jacobian to the face. This\n                  // could be optimized by caching in the time-independent case.\n                  get(face_det_jacobian)\n                      .destructive_resize(face_mesh.number_of_grid_points());\n                  const Matrix identity{};\n                  auto interpolation_matrices =\n                      make_array<volume_dim>(std::cref(identity));\n                  const std::pair<Matrix, Matrix>& matrices =\n                      Spectral::boundary_interpolation_matrices(\n                          volume_mesh.slice_through(direction.dimension()));\n                  gsl::at(interpolation_matrices, direction.dimension()) =\n                      direction.side() == Side::Upper ? matrices.second\n                                                      : matrices.first;\n                  apply_matrices(make_not_null(&get(face_det_jacobian)),\n                                 interpolation_matrices,\n                                 get(volume_det_jacobian),\n                                 volume_mesh.extents());\n                }\n\n                volume_dt_correction.initialize(\n                    volume_mesh.number_of_grid_points(), 0.0);\n                ::dg::lift_boundary_terms_gauss_points(\n                    make_not_null(&volume_dt_correction),\n                    volume_det_inv_jacobian, volume_mesh, direction,\n                    dt_boundary_correction, magnitude_of_face_normal,\n                    face_det_jacobian);\n                return std::move(volume_dt_correction);\n              }\n            };\n\n            if constexpr (local_time_stepping) {\n              typename variables_tag::type boundary_lifted_data{};\n              auto& lifted_data =\n                  using_points_on_face ? boundary_lifted_data : *vars_to_update;\n              if (using_points_on_face) {\n                lifted_data.initialize(face_mesh.number_of_grid_points(), 0.0);\n              }\n\n              const auto& mortar_data_history = mortar_id_and_data.second;\n              if constexpr (DenseOutput) {\n                (void)time_step;\n                time_stepper.boundary_dense_output(\n                    &lifted_data, mortar_data_history, dense_output_time,\n                    compute_correction_coupling);\n              } else {\n                (void)dense_output_time;\n                time_stepper.add_boundary_delta(&lifted_data,\n                                                mortar_data_history, time_step,\n                                                compute_correction_coupling);\n              }\n\n              if (using_points_on_face) {\n                // Add the flux contribution to the volume data\n                add_slice_to_data(\n                    vars_to_update, lifted_data, volume_mesh.extents(),\n                    direction.dimension(),\n                    index_to_slice_at(volume_mesh.extents(), direction));\n              }\n            } else {\n              (void)time_step;\n              (void)time_stepper;\n              (void)dense_output_time;\n\n              // Choose an allocation cache that may be empty, so we\n              // might be able to reuse the allocation obtained for the\n              // lifted data.  This may result in a self assignment,\n              // depending on the code paths taken, but handling the\n              // results this way makes the GTS and LTS paths more\n              // similar because the LTS code always stores the result\n              // in the history and so sometimes benefits from moving\n              // into the return value of compute_correction_coupling.\n              auto& lifted_data = using_points_on_face\n                                      ? dt_boundary_correction_on_mortar\n                                      : volume_dt_correction;\n              lifted_data = compute_correction_coupling(\n                  mortar_id_and_data.second.local(),\n                  mortar_id_and_data.second.neighbor());\n\n              if (using_points_on_face) {\n                // Add the flux contribution to the volume data\n                add_slice_to_data(\n                    vars_to_update, lifted_data, volume_mesh.extents(),\n                    direction.dimension(),\n                    index_to_slice_at(volume_mesh.extents(), direction));\n              } else {\n                *vars_to_update += lifted_data;\n              }\n            }\n          }\n        });\n  }\n\n  template <typename... BoundaryCorrectionTags, typename... Tags,\n            typename BoundaryCorrection, typename... AllVolumeArgs,\n            typename... VolumeTagsForCorrection>\n  static void call_boundary_correction(\n      const gsl::not_null<Variables<tmpl::list<BoundaryCorrectionTags...>>*>\n          boundary_corrections_on_mortar,\n      const Variables<tmpl::list<Tags...>>& local_boundary_data,\n      const Variables<tmpl::list<Tags...>>& neighbor_boundary_data,\n      const BoundaryCorrection& boundary_correction,\n      const ::dg::Formulation dg_formulation,\n      const tuples::TaggedTuple<detail::TemporaryReference<AllVolumeArgs>...>&\n          volume_args_tuple,\n      tmpl::list<VolumeTagsForCorrection...> /*meta*/) {\n    boundary_correction.dg_boundary_terms(\n        make_not_null(\n            &get<BoundaryCorrectionTags>(*boundary_corrections_on_mortar))...,\n        get<Tags>(local_boundary_data)..., get<Tags>(neighbor_boundary_data)...,\n        dg_formulation,\n        tuples::get<detail::TemporaryReference<VolumeTagsForCorrection>>(\n            volume_args_tuple)...);\n  }\n};\n\n/// Apply corrections from boundary communication for LTS dense output.\ntemplate <typename Metavariables>\nstruct ApplyLtsDenseBoundaryCorrections\n    : ApplyBoundaryCorrections<true, Metavariables, true> {};\n\nnamespace Actions {\nnamespace ApplyBoundaryCorrections_detail {\ntemplate <bool LocalTimeStepping, size_t VolumeDim, bool DenseOutput,\n          bool UseNodegroupDgElements>\nstruct ActionImpl {\n  using inbox_tags =\n      tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n          VolumeDim, UseNodegroupDgElements>>;\n  using const_global_cache_tags =\n      tmpl::list<evolution::Tags::BoundaryCorrection, ::dg::Tags::Formulation>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(Metavariables::system::volume_dim == VolumeDim);\n    static_assert(\n        UseNodegroupDgElements ==\n            Parallel::is_dg_element_collection_v<ParallelComponent>,\n        \"The action is told by the template parameter UseNodegroupDgElements \"\n        \"that it is being used with a DgElementCollection, but the \"\n        \"ParallelComponent is not a DgElementCollection. You need to change \"\n        \"the template parameter on the action in your action list.\");\n    constexpr size_t volume_dim = Metavariables::system::volume_dim;\n    const Element<volume_dim>& element =\n        db::get<domain::Tags::Element<volume_dim>>(box);\n\n    if (UNLIKELY(element.number_of_neighbors() == 0)) {\n      // We have no neighbors, yay!\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    if (not receive_boundary_data<\n            Parallel::is_dg_element_collection_v<ParallelComponent>,\n            Metavariables, LocalTimeStepping, false>(make_not_null(&box),\n                                                     make_not_null(&inboxes))) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // LTS updates the evolved variables, so we can skip that if they\n    // are unused.  GTS updates the derivatives, which are always\n    // needed to update the history.\n    if (LocalTimeStepping and\n        ::SelfStart::step_unused(\n            db::get<::Tags::TimeStepId>(box),\n            db::get<::Tags::Next<::Tags::TimeStepId>>(box))) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    db::mutate_apply<ApplyBoundaryCorrections<LocalTimeStepping, Metavariables,\n                                              DenseOutput>>(\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace ApplyBoundaryCorrections_detail\n\n/*!\n * \\brief Computes the boundary corrections for global time-stepping\n * and adds them to the time derivative.\n */\ntemplate <size_t VolumeDim, bool UseNodegroupDgElements>\nstruct ApplyBoundaryCorrectionsToTimeDerivative\n    : ApplyBoundaryCorrections_detail::ActionImpl<false, VolumeDim, false,\n                                                  UseNodegroupDgElements> {};\n\n/*!\n * \\brief Computes the boundary corrections for local time-stepping\n * and adds them to the variables.\n *\n * When using local time stepping the neighbor sends data at the neighbor's\n * current temporal id. Along with the boundary data, the next temporal id at\n * which the neighbor will send data is also sent. This is equal to the\n * neighbor's `::Tags::Next<::Tags::TimeStepId>`. When inserting into the mortar\n * data history, we insert the received temporal id, that is, the current time\n * of the neighbor, along with the boundary correction data.\n */\ntemplate <size_t VolumeDim, bool UseNodegroupDgElements>\nstruct ApplyLtsBoundaryCorrections\n    : ApplyBoundaryCorrections_detail::ActionImpl<true, VolumeDim, false,\n                                                  UseNodegroupDgElements> {};\n}  // namespace Actions\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/BoundaryConditionsImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/InterpolateFromBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::dg::Actions::detail {\ntemplate <typename BoundaryConditionHelper, typename AllTagsOnFaceList,\n          typename... TagsFromFace, typename... VolumeArgs>\nstd::optional<std::string> apply_boundary_condition_impl(\n    BoundaryConditionHelper& boundary_condition_helper,\n    const Variables<AllTagsOnFaceList>& fields_on_interior_face,\n    tmpl::list<TagsFromFace...> /*meta*/, const VolumeArgs&... volume_args) {\n  return boundary_condition_helper(\n      get<TagsFromFace>(fields_on_interior_face)..., volume_args...);\n}\n\ntemplate <typename System, size_t Dim, typename DbTagsList,\n          typename BoundaryCorrection, typename BoundaryCondition,\n          typename... EvolvedVariablesTags, typename... PackageDataVolumeTags,\n          typename... BoundaryConditionVolumeTags, typename... PackageFieldTags,\n          typename... BoundaryTermsVolumeTags,\n          typename... BoundaryCorrectionPackagedDataInputTags>\nvoid apply_boundary_condition_on_face(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    [[maybe_unused]] const BoundaryCorrection& boundary_correction,\n    const BoundaryCondition& boundary_condition,\n    const Direction<Dim>& direction,\n    [[maybe_unused]] const Variables<tmpl::list<EvolvedVariablesTags...>>&\n        volume_evolved_vars,\n    [[maybe_unused]] const Variables<\n        db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,\n                         tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes,\n    [[maybe_unused]] const Variables<\n        db::wrap_tags_in<::Tags::deriv, typename System::gradient_variables,\n                         tmpl::size_t<Dim>, Frame::Inertial>>& partial_derivs,\n    [[maybe_unused]] const Variables<\n        typename System::compute_volume_time_derivative_terms::temporary_tags>&\n        volume_temporaries,\n    [[maybe_unused]] const Variables<\n        detail::get_primitive_vars_tags_from_system<System>>* const\n        volume_primitive_variables,\n    [[maybe_unused]] const ::dg::Formulation dg_formulation,\n    const Mesh<Dim>& volume_mesh, [[maybe_unused]] const Element<Dim>& element,\n    [[maybe_unused]] const ::ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&\n        moving_mesh_map,\n    [[maybe_unused]] const double time,\n    [[maybe_unused]] const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::optional<tnsr::I<DataVector, Dim>>& volume_mesh_velocity,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& volume_inverse_jacobian,\n    [[maybe_unused]] const Scalar<DataVector>& volume_det_inv_jacobian,\n    tmpl::list<PackageDataVolumeTags...> /*meta*/,\n    tmpl::list<PackageFieldTags...> /*meta*/,\n    tmpl::list<BoundaryTermsVolumeTags...> /*meta*/,\n    tmpl::list<BoundaryCorrectionPackagedDataInputTags...> /*meta*/,\n    tmpl::list<BoundaryConditionVolumeTags...> /*meta*/) {\n  using variables_tag = typename System::variables_tag;\n  using variables_tags = typename variables_tag::tags_list;\n  using flux_variables = typename System::flux_variables;\n  using dt_variables_tags = db::wrap_tags_in<::Tags::dt, variables_tags>;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n\n  const Mesh<Dim - 1> face_mesh = volume_mesh.slice_away(direction.dimension());\n  const size_t number_of_points_on_face = face_mesh.number_of_grid_points();\n\n  // We figure out all the tags we need to project from the interior, both for\n  // the boundary condition computation and for the boundary correction. We do\n  // this by:\n  // 1. get all interior tags for the boundary condition\n  // 2. get all interior tags for the boundary correction (if ghost condition)\n  // 3. combine these lists\n  // 4. project from the interior\n  //\n  // Note: we only need to consider the boundary correction tags if a ghost\n  // boundary condition is imposed.\n\n  constexpr bool uses_ghost_condition =\n      BoundaryCondition::bc_type ==\n          evolution::BoundaryConditions::Type::Ghost or\n      BoundaryCondition::bc_type ==\n          evolution::BoundaryConditions::Type::GhostAndTimeDerivative;\n  constexpr bool uses_time_derivative_condition =\n      BoundaryCondition::bc_type ==\n          evolution::BoundaryConditions::Type::TimeDerivative or\n      BoundaryCondition::bc_type ==\n          evolution::BoundaryConditions::Type::GhostAndTimeDerivative;\n  constexpr bool needs_coordinates = tmpl::list_contains_v<\n      typename BoundaryCondition::dg_interior_temporary_tags,\n      ::domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n\n  // List that holds the inverse spatial metric if it's needed\n  using inverse_spatial_metric_list =\n      detail::inverse_spatial_metric_tag<System>;\n  constexpr bool has_inv_spatial_metric =\n      detail::has_inverse_spatial_metric_tag_v<System>;\n\n  // Set up tags for boundary conditions\n  using bcondition_interior_temp_tags =\n      typename BoundaryCondition::dg_interior_temporary_tags;\n  using bcondition_interior_prim_tags =\n      detail::boundary_condition_primitive_tags<\n          System::has_primitive_and_conservative_vars, BoundaryCondition>;\n  using bcondition_interior_evolved_vars_tags =\n      typename BoundaryCondition::dg_interior_evolved_variables_tags;\n  using bcondition_interior_dt_evolved_vars_tags =\n      detail::get_dt_vars_from_boundary_condition<BoundaryCondition>;\n  using bcondition_interior_deriv_evolved_vars_tags =\n      detail::get_deriv_vars_from_boundary_condition<BoundaryCondition>;\n  using bcondition_interior_tags = tmpl::append<\n      tmpl::conditional_t<has_inv_spatial_metric,\n                          tmpl::list<detail::NormalVector<Dim>>, tmpl::list<>>,\n      bcondition_interior_evolved_vars_tags, bcondition_interior_prim_tags,\n      bcondition_interior_temp_tags, bcondition_interior_dt_evolved_vars_tags,\n      bcondition_interior_deriv_evolved_vars_tags>;\n\n  // Set up tags for boundary correction\n  using correction_temp_tags = tmpl::conditional_t<\n      uses_ghost_condition,\n      typename BoundaryCorrection::dg_package_data_temporary_tags,\n      tmpl::list<>>;\n  using correction_prim_tags = tmpl::conditional_t<\n      uses_ghost_condition,\n      detail::boundary_correction_primitive_tags<\n          System::has_primitive_and_conservative_vars, BoundaryCorrection>,\n      tmpl::list<>>;\n  using correction_evolved_vars_tags =\n      tmpl::conditional_t<uses_ghost_condition,\n                          typename System::variables_tag::tags_list,\n                          tmpl::list<>>;\n\n  // Now combine the tags lists for each type of tag. These are all the tags\n  // we need to project from the interior, excluding the inverse spatial\n  // metric. They are the input to `dg_package_data` in the boundary\n  // correction.\n  using interior_temp_tags = tmpl::remove_duplicates<\n      tmpl::append<bcondition_interior_temp_tags, correction_temp_tags>>;\n  using interior_prim_tags = tmpl::remove_duplicates<\n      tmpl::append<bcondition_interior_prim_tags, correction_prim_tags>>;\n  using interior_evolved_vars_tags = tmpl::remove_duplicates<tmpl::append<\n      correction_evolved_vars_tags, bcondition_interior_evolved_vars_tags>>;\n\n  // List tags on the interior of the face. We list the exterior side\n  // separately in the `else` branch of the if-constexpr where we actually use\n  // the exterior fields.\n  using fluxes_tags =\n      tmpl::conditional_t<uses_ghost_condition,\n                          db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                           tmpl::size_t<Dim>, Frame::Inertial>,\n                          tmpl::list<>>;\n  using tags_on_interior_face = tmpl::remove_duplicates<tmpl::append<\n      fluxes_tags, interior_temp_tags, interior_prim_tags,\n      interior_evolved_vars_tags, bcondition_interior_dt_evolved_vars_tags,\n      bcondition_interior_deriv_evolved_vars_tags, inverse_spatial_metric_list,\n      tmpl::list<detail::OneOverNormalVectorMagnitude,\n                 detail::NormalVector<Dim>>>>;\n\n  Variables<tags_on_interior_face> interior_face_fields{\n      number_of_points_on_face};\n\n  // Perform projection into `interior_face_fields`. This also covers all the\n  // fields for the exterior except for the time derivatives that might be\n  // needed for Bjorhus/TimeDerivative boundary conditions.\n  //\n  // Note on the ordering of the data to project: if we are using a ghost\n  // boundary condition with a boundary correction, then we know that all the\n  // evolved variables are needed, whereas when using DemandOutgoingCharSpeeds\n  // or Bjorhus boundary conditions none of the evolved variables might be\n  // needed (or only some subset). Also, the way the typelist is assembled, the\n  // evolved vars are guaranteed to be contiguous, but only if we are doing a\n  // ghost boundary condition.\n  if constexpr (uses_ghost_condition) {\n    ::dg::project_contiguous_data_to_boundary(\n        make_not_null(&interior_face_fields), volume_evolved_vars, volume_mesh,\n        direction);\n  } else {\n    ::dg::project_tensors_to_boundary<interior_evolved_vars_tags>(\n        make_not_null(&interior_face_fields), volume_evolved_vars, volume_mesh,\n        direction);\n  }\n  if constexpr (tmpl::size<fluxes_tags>::value != 0) {\n    ::dg::project_contiguous_data_to_boundary(\n        make_not_null(&interior_face_fields), volume_fluxes, volume_mesh,\n        direction);\n  } else {\n    (void)volume_fluxes;\n  }\n  using temp_tags_no_coordinates =\n      tmpl::remove<interior_temp_tags,\n                   domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n  if constexpr (tmpl::size<tmpl::append<\n                    temp_tags_no_coordinates,\n                    detail::inverse_spatial_metric_tag<System>>>::value != 0) {\n    ::dg::project_tensors_to_boundary<tmpl::append<\n        temp_tags_no_coordinates, detail::inverse_spatial_metric_tag<System>>>(\n        make_not_null(&interior_face_fields), volume_temporaries, volume_mesh,\n        direction);\n  }\n  if constexpr (System::has_primitive_and_conservative_vars and\n                tmpl::size<interior_prim_tags>::value != 0) {\n    ASSERT(volume_primitive_variables != nullptr,\n           \"The volume primitive variables are not set even though the \"\n           \"system has primitive variables.\");\n    ::dg::project_tensors_to_boundary<interior_prim_tags>(\n        make_not_null(&interior_face_fields), *volume_primitive_variables,\n        volume_mesh, direction);\n  } else {\n    (void)volume_primitive_variables;\n  }\n  if constexpr (tmpl::size<\n                    bcondition_interior_deriv_evolved_vars_tags>::value != 0) {\n    ::dg::project_tensors_to_boundary<\n        bcondition_interior_deriv_evolved_vars_tags>(\n        make_not_null(&interior_face_fields), partial_derivs, volume_mesh,\n        direction);\n  }\n  if constexpr (tmpl::size<bcondition_interior_dt_evolved_vars_tags>::value !=\n                0) {\n    ::dg::project_tensors_to_boundary<bcondition_interior_dt_evolved_vars_tags>(\n        make_not_null(&interior_face_fields), db::get<dt_variables_tag>(*box),\n        volume_mesh, direction);\n  }\n\n  std::optional<tnsr::I<DataVector, Dim>> face_mesh_velocity{};\n  if (volume_mesh_velocity.has_value()) {\n    face_mesh_velocity = tnsr::I<DataVector, Dim>{number_of_points_on_face};\n    ::dg::project_tensor_to_boundary(make_not_null(&*face_mesh_velocity),\n                                     *volume_mesh_velocity, volume_mesh,\n                                     direction);\n  }\n\n  // Normalize the normal vectors. We cache the unit normal covector For\n  // flat geometry and static meshes.\n  const auto normalize_normal_vectors =\n      [&direction, mesh_is_moving = not moving_mesh_map.is_identity(),\n       number_of_points_on_face, &volume_inverse_jacobian,\n       &volume_mesh](const auto normal_covector_magnitude_in_direction_ptr,\n                     auto fields_on_face_ptr) {\n        if (auto& normal_covector_quantity =\n                *normal_covector_magnitude_in_direction_ptr;\n            has_inv_spatial_metric or mesh_is_moving or\n            not normal_covector_quantity.has_value()) {\n          if (not normal_covector_quantity.has_value()) {\n            normal_covector_quantity =\n                Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                     evolution::dg::Tags::NormalCovector<Dim>>>{\n                    number_of_points_on_face};\n          }\n          tnsr::i<DataVector, Dim> volume_unnormalized_normal_covector{};\n\n          for (size_t inertial_index = 0; inertial_index < Dim;\n               ++inertial_index) {\n            volume_unnormalized_normal_covector.get(inertial_index)\n                .set_data_ref(\n                    const_cast<double*>(  // NOLINT\n                        volume_inverse_jacobian\n                            .get(direction.dimension(), inertial_index)\n                            .data()),\n                    volume_mesh.number_of_grid_points());\n          }\n          ::dg::project_tensor_to_boundary(\n              make_not_null(&get<evolution::dg::Tags::NormalCovector<Dim>>(\n                  *normal_covector_quantity)),\n              volume_unnormalized_normal_covector, volume_mesh, direction);\n\n          if (const double sign = direction.sign(); sign != 1.0) {\n            for (auto& normal_covector_component :\n                 get<evolution::dg::Tags::NormalCovector<Dim>>(\n                     *normal_covector_quantity)) {\n              normal_covector_component *= sign;\n            }\n          }\n\n          detail::unit_normal_vector_and_covector_and_magnitude_impl<System>(\n              make_not_null(&get<evolution::dg::Tags::MagnitudeOfNormal>(\n                  *normal_covector_quantity)),\n              make_not_null(&get<evolution::dg::Tags::NormalCovector<Dim>>(\n                  *normal_covector_quantity)),\n              fields_on_face_ptr,\n              get<evolution::dg::Tags::NormalCovector<Dim>>(\n                  *normal_covector_quantity));\n        }\n      };\n  // Normalize the outward facing normal vector on the interior side\n  db::mutate<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(\n      [&direction, &interior_face_fields, &normalize_normal_vectors](\n          const auto normal_covector_and_magnitude_ptr) {\n        normalize_normal_vectors(\n            make_not_null(&normal_covector_and_magnitude_ptr->at(direction)),\n            make_not_null(&interior_face_fields));\n      },\n      box);\n\n  const tnsr::i<DataVector, Dim, Frame::Inertial>& interior_normal_covector =\n      get<evolution::dg::Tags::NormalCovector<Dim>>(\n          *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box)\n               .at(direction));\n\n  if constexpr (needs_coordinates) {\n    // Compute the coordinates on the interface\n    get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(interior_face_fields) =\n        moving_mesh_map(logical_to_grid_map(interface_logical_coordinates(\n                            face_mesh, direction)),\n                        time, functions_of_time);\n  }\n\n  if constexpr (BoundaryCondition::bc_type ==\n                evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds) {\n    // DemandOutgoingCharSpeeds boundary conditions only check that all\n    // characteristic speeds are directed out of the element. If there are any\n    // inward directed fields then the boundary condition should error.\n    const auto apply_bc =\n        [&boundary_condition, &face_mesh_velocity,\n         &interior_normal_covector](const auto&... face_and_volume_args) {\n          return boundary_condition.dg_demand_outgoing_char_speeds(\n              face_mesh_velocity, interior_normal_covector,\n              face_and_volume_args...);\n        };\n    const std::optional<std::string> error_message =\n        apply_boundary_condition_impl(\n            apply_bc, interior_face_fields, bcondition_interior_tags{},\n            db::get<BoundaryConditionVolumeTags>(*box)...);\n    if (error_message.has_value()) {\n      ERROR(*error_message << \"\\n\\nIn element:\" << element.id()\n                           << \"\\nIn direction: \" << direction);\n    }\n    return;\n  }\n\n  // We add the time derivative boundary conditions and lift the ghost boundary\n  // conditions after both have been computed in case either depends on the\n  // time derivatives in the volume projected on to the face.\n\n  Variables<dt_variables_tags> dt_time_derivative_correction{};\n  if constexpr (uses_time_derivative_condition) {\n    dt_time_derivative_correction.initialize(number_of_points_on_face);\n    auto apply_bc = [&boundary_condition, &dt_time_derivative_correction,\n                     &face_mesh_velocity, &interior_normal_covector](\n                        const auto&... interior_face_and_volume_args) {\n      return boundary_condition.dg_time_derivative(\n          make_not_null(&get<::Tags::dt<EvolvedVariablesTags>>(\n              dt_time_derivative_correction))...,\n          face_mesh_velocity, interior_normal_covector,\n          interior_face_and_volume_args...);\n    };\n    const std::optional<std::string> error_message =\n        apply_boundary_condition_impl(\n            apply_bc, interior_face_fields, bcondition_interior_tags{},\n            db::get<BoundaryConditionVolumeTags>(*box)...);\n    if (error_message.has_value()) {\n      ERROR(*error_message << \"\\n\\nIn element:\" << element.id()\n                           << \"\\nIn direction: \" << direction);\n    }\n  } else {\n    (void)dt_time_derivative_correction;\n  }\n\n  // Now we populate the fields on the exterior side of the face using the\n  // boundary condition.\n  using tags_on_exterior_face = tmpl::remove_duplicates<\n      tmpl::append<variables_tags, fluxes_tags, correction_temp_tags,\n                   correction_prim_tags, inverse_spatial_metric_list,\n                   tmpl::list<detail::OneOverNormalVectorMagnitude,\n                              detail::NormalVector<Dim>,\n                              evolution::dg::Tags::NormalCovector<Dim>>>>;\n  Variables<tags_on_exterior_face> exterior_face_fields{\n      number_of_points_on_face};\n\n  const bool has_collocation_points_on_side =\n      volume_mesh.quadrature(direction.dimension()) ==\n          Spectral::Quadrature::GaussLobatto or\n      (volume_mesh.quadrature(direction.dimension()) ==\n           Spectral::Quadrature::GaussRadauUpper and\n       direction.side() == Side::Upper);\n\n  if constexpr (uses_ghost_condition) {\n    using mortar_tags_list = tmpl::list<PackageFieldTags...>;\n    using dg_package_data_projected_tags =\n        tmpl::append<variables_tags, fluxes_tags, correction_temp_tags,\n                     correction_prim_tags>;\n\n    Variables<mortar_tags_list> internal_packaged_data{\n        number_of_points_on_face};\n    const double max_abs_char_speed_on_face = detail::dg_package_data<System>(\n        make_not_null(&internal_packaged_data), boundary_correction,\n        interior_face_fields, interior_normal_covector, face_mesh_velocity,\n        dg_package_data_projected_tags{},\n        db::get<PackageDataVolumeTags>(*box)...);\n    (void)max_abs_char_speed_on_face;\n\n    // Notes:\n    // - we pass the outward directed normal vector normalized using the\n    //   interior variables to the boundary condition. This is because the\n    //   boundary condition should only need the normal vector for computing\n    //   things like reflecting BCs where the normal component of an interior\n    //   quantity is reversed.\n    // - if needed, the boundary condition returns the inverse spatial metric on\n    //   the exterior side, which is then used to normalize the normal vector on\n    //   the exterior side. We need the exterior normal vector for computing\n    //   flux terms. The inverse spatial metric on the exterior side can be\n    //   equal to the inverse spatial metric on the interior side. This would be\n    //   true when, e.g. imposing reflecting boundary conditions.\n    // - in addition to the evolved variables and fluxes, the boundary condition\n    //   must compute the `dg_packaged_data_temporary_tags` and the primitive\n    //   tags that the boundary correction needs.\n    // - For systems with constraint damping parameters, the constraint damping\n    //   parameters are just copied from the projected values from the interior.\n    auto apply_bc = [&boundary_condition, &exterior_face_fields,\n                     &face_mesh_velocity, &interior_normal_covector](\n                        const auto&... interior_face_and_volume_args) {\n      if constexpr (has_inv_spatial_metric) {\n        return boundary_condition.dg_ghost(\n            make_not_null(&get<BoundaryCorrectionPackagedDataInputTags>(\n                exterior_face_fields))...,\n            make_not_null(\n                &get<tmpl::front<detail::inverse_spatial_metric_tag<System>>>(\n                    exterior_face_fields)),\n            face_mesh_velocity, interior_normal_covector,\n            interior_face_and_volume_args...);\n      } else {\n        return boundary_condition.dg_ghost(\n            make_not_null(&get<BoundaryCorrectionPackagedDataInputTags>(\n                exterior_face_fields))...,\n            face_mesh_velocity, interior_normal_covector,\n            interior_face_and_volume_args...);\n      }\n    };\n    const std::optional<std::string> error_message =\n        apply_boundary_condition_impl(\n            apply_bc, interior_face_fields, bcondition_interior_tags{},\n            db::get<BoundaryConditionVolumeTags>(*box)...);\n    if (error_message.has_value()) {\n      ERROR(*error_message << \"\\n\\nIn element:\" << element.id()\n                           << \"\\nIn direction: \" << direction);\n    }\n    // Subtract mesh velocity from the _exterior_ fluxes\n    if (face_mesh_velocity.has_value()) {\n      tmpl::for_each<flux_variables>(\n          [&face_mesh_velocity, &exterior_face_fields](auto tag_v) {\n            // Modify fluxes for moving mesh\n            using var_tag = typename decltype(tag_v)::type;\n            using flux_var_tag =\n                db::add_tag_prefix<::Tags::Flux, var_tag, tmpl::size_t<Dim>,\n                                   Frame::Inertial>;\n            auto& flux_var = get<flux_var_tag>(exterior_face_fields);\n            const auto& var = get<var_tag>(exterior_face_fields);\n            const auto& mesh_velocity = *face_mesh_velocity;\n            // Loop over all independent components of flux_var\n            for (size_t flux_var_storage_index = 0;\n                 flux_var_storage_index < flux_var.size();\n                 ++flux_var_storage_index) {\n              // Get the flux variable's tensor index, e.g. (i,j) for a F^i of\n              // the spatial velocity (or some other spatial tensor).\n              const auto flux_var_tensor_index =\n                  flux_var.get_tensor_index(flux_var_storage_index);\n              // Remove the first index from the flux tensor index, gets back\n              // (j)\n              const auto var_tensor_index =\n                  all_but_specified_element_of(flux_var_tensor_index, 0);\n              // Set flux_index to (i)\n              const size_t flux_index = gsl::at(flux_var_tensor_index, 0);\n\n              // We now need to index flux(i,j) -= u(j) * v_g(i)\n              flux_var[flux_var_storage_index] -=\n                  var.get(var_tensor_index) * mesh_velocity.get(flux_index);\n            }\n          });\n    }\n    // Now that we have computed the inverse spatial metric on the exterior, we\n    // can compute the normalized normal (co)vector on the exterior side. If\n    // there is no inverse spatial metric, then we just copy from the interior\n    // and reverse the sign.\n    for (size_t i = 0; i < Dim; ++i) {\n      get<evolution::dg::Tags::NormalCovector<Dim>>(exterior_face_fields)\n          .get(i) = -interior_normal_covector.get(i);\n    }\n    if constexpr (has_inv_spatial_metric) {\n      const tnsr::II<DataVector, Dim, Frame::Inertial>& inv_spatial_metric =\n          get<tmpl::front<inverse_spatial_metric_list>>(exterior_face_fields);\n      tnsr::i<DataVector, Dim, Frame::Inertial>& exterior_normal_covector =\n          get<evolution::dg::Tags::NormalCovector<Dim>>(exterior_face_fields);\n      tnsr::I<DataVector, Dim, Frame::Inertial>& exterior_normal_vector =\n          get<detail::NormalVector<Dim>>(exterior_face_fields);\n\n      // Since the spatial metric is different on the exterior side of the\n      // interface, we need to normalize the direction-reversed interior normal\n      // vector using the exterior inverse spatial metric.\n      for (size_t i = 0; i < Dim; ++i) {\n        exterior_normal_vector.get(i) =\n            get<0>(exterior_normal_covector) * inv_spatial_metric.get(i, 0);\n        for (size_t j = 1; j < Dim; ++j) {\n          exterior_normal_vector.get(i) +=\n              exterior_normal_covector.get(j) * inv_spatial_metric.get(i, j);\n        }\n      }\n      // Use detail::OneOverNormalVectorMagnitude as a buffer for the\n      // magnitude. We don't need one over the normal magnitude on the\n      // exterior side since we aren't lifting there.\n      Scalar<DataVector>& magnitude =\n          get<detail::OneOverNormalVectorMagnitude>(exterior_face_fields);\n      dot_product(make_not_null(&magnitude), exterior_normal_covector,\n                  exterior_normal_vector);\n      get(magnitude) = sqrt(get(magnitude));\n      for (size_t i = 0; i < Dim; ++i) {\n        exterior_normal_covector.get(i) /= get(magnitude);\n        exterior_normal_vector.get(i) /= get(magnitude);\n      }\n    }\n\n    // Package the external-side data for the boundary correction\n    Variables<mortar_tags_list> external_packaged_data{\n        number_of_points_on_face};\n    detail::dg_package_data<System>(\n        make_not_null(&external_packaged_data), boundary_correction,\n        exterior_face_fields,\n        get<evolution::dg::Tags::NormalCovector<Dim>>(exterior_face_fields),\n        face_mesh_velocity, dg_package_data_projected_tags{},\n        db::get<PackageDataVolumeTags>(*box)...);\n\n    Variables<dt_variables_tags> boundary_corrections_on_face{\n        number_of_points_on_face};\n\n    // Compute boundary correction\n    boundary_correction.dg_boundary_terms(\n        make_not_null(&get<::Tags::dt<EvolvedVariablesTags>>(\n            boundary_corrections_on_face))...,\n        get<PackageFieldTags>(internal_packaged_data)...,\n        get<PackageFieldTags>(external_packaged_data)..., dg_formulation,\n        get<BoundaryTermsVolumeTags>(*box)...);\n\n    // Lift the boundary correction\n    const auto& magnitude_of_interior_face_normal =\n        get<evolution::dg::Tags::MagnitudeOfNormal>(\n            *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box)\n                 .at(direction));\n    if (has_collocation_points_on_side) {\n      // The lift_flux function lifts only on the slice, it does not add\n      // the contribution to the volume.\n      ::dg::lift_flux(make_not_null(&boundary_corrections_on_face),\n                      volume_mesh.extents(direction.dimension()),\n                      magnitude_of_interior_face_normal,\n                      volume_mesh.basis(direction.dimension()));\n\n      // Add the flux contribution to the volume data\n      db::mutate<dt_variables_tag>(\n          [&direction, &boundary_corrections_on_face,\n           &volume_mesh](const auto dt_variables_ptr) {\n            add_slice_to_data(\n                dt_variables_ptr, boundary_corrections_on_face,\n                volume_mesh.extents(), direction.dimension(),\n                index_to_slice_at(volume_mesh.extents(), direction));\n          },\n          box);\n    } else {\n      // We are using Gauss points.\n      //\n      // Optimization note: eliminate allocations for volume and face det\n      // jacobian. Should probably compute face det inv jacobian, then divide\n      // (fewer grid points => fewer FLOPs).\n      const DataVector volume_det_jacobian = 1.0 / get(volume_det_inv_jacobian);\n\n      // Project the determinant of the Jacobian to the face. This could\n      // be optimized by caching in the time-independent case.\n      Scalar<DataVector> face_det_jacobian{face_mesh.number_of_grid_points()};\n      const Matrix identity{};\n      auto interpolation_matrices = make_array<Dim>(std::cref(identity));\n      const std::pair<Matrix, Matrix>& matrices =\n          Spectral::boundary_interpolation_matrices(\n              volume_mesh.slice_through(direction.dimension()));\n      gsl::at(interpolation_matrices, direction.dimension()) =\n          direction.side() == Side::Upper ? matrices.second : matrices.first;\n      apply_matrices(make_not_null(&get(face_det_jacobian)),\n                     interpolation_matrices, volume_det_jacobian,\n                     volume_mesh.extents());\n\n      db::mutate<dt_variables_tag>(\n          [&direction, &boundary_corrections_on_face, &face_det_jacobian,\n           &magnitude_of_interior_face_normal, &volume_det_inv_jacobian,\n           &volume_mesh](const auto dt_variables_ptr) {\n            ::dg::lift_boundary_terms_gauss_points(\n                dt_variables_ptr, volume_det_inv_jacobian, volume_mesh,\n                direction, boundary_corrections_on_face,\n                magnitude_of_interior_face_normal, face_det_jacobian);\n          },\n          box);\n    }\n  }\n  // Add TimeDerivative correction to volume time derivatives.\n  if constexpr (uses_time_derivative_condition) {\n    if (has_collocation_points_on_side) {\n      db::mutate<dt_variables_tag>(\n          [&direction, &dt_time_derivative_correction,\n           &volume_mesh](const auto dt_variables_ptr) {\n            add_slice_to_data(\n                dt_variables_ptr, dt_time_derivative_correction,\n                volume_mesh.extents(), direction.dimension(),\n                index_to_slice_at(volume_mesh.extents(), direction));\n          },\n          box);\n    } else {\n      db::mutate<dt_variables_tag>(\n          [&direction, &dt_time_derivative_correction,\n           &volume_mesh](const auto dt_variables_ptr) {\n            ::dg::interpolate_dt_terms_gauss_points(\n                dt_variables_ptr, volume_mesh, direction,\n                dt_time_derivative_correction);\n          },\n          box);\n    }\n  }\n}\n\n/*!\n * \\brief Applies the boundary conditions using the `boundary_correction`\n * on all external faces.\n *\n * A `tmpl::for_each` loop along with a `typeid` comparison checks which of the\n * known boundary conditions is being used. Since each direction can have a\n * different boundary condition, we must check each boundary condition in\n * each external direction.\n */\ntemplate <typename System, size_t Dim, typename DbTagsList,\n          typename BoundaryCorrection>\nvoid apply_boundary_conditions_on_all_external_faces(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    const BoundaryCorrection& boundary_correction,\n    const Variables<\n        typename System::compute_volume_time_derivative_terms::temporary_tags>&\n        temporaries,\n    const Variables<\n        db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,\n                         tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes,\n    const Variables<\n        db::wrap_tags_in<::Tags::deriv, typename System::gradient_variables,\n                         tmpl::size_t<Dim>, Frame::Inertial>>& partial_derivs,\n    const Variables<detail::get_primitive_vars_tags_from_system<System>>* const\n        primitive_vars) {\n  using factory_classes =\n      typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n          *box))>::factory_creation::factory_classes;\n  using derived_boundary_conditions = tmpl::remove_if<\n      tmpl::at<factory_classes, typename System::boundary_conditions_base>,\n      tmpl::or_<\n          std::is_base_of<domain::BoundaryConditions::MarkAsPeriodic, tmpl::_1>,\n          std::is_base_of<domain::BoundaryConditions::MarkAsNone, tmpl::_1>>>;\n\n  using variables_tag = typename System::variables_tag;\n  using flux_variables = typename System::flux_variables;\n  using fluxes_tags = db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                       tmpl::size_t<Dim>, Frame::Inertial>;\n\n  const Element<Dim>& element = db::get<domain::Tags::Element<Dim>>(*box);\n  size_t number_of_boundaries_left = element.external_boundaries().size();\n\n  if (number_of_boundaries_left == 0) {\n    return;\n  }\n\n  const auto& external_boundary_conditions =\n      db::get<domain::Tags::ExternalBoundaryConditions<Dim>>(*box).at(\n          element.id().block_id());\n  tmpl::for_each<derived_boundary_conditions>(\n      [&boundary_correction, &box, &element, &external_boundary_conditions,\n       &number_of_boundaries_left, &partial_derivs, &primitive_vars,\n       &temporaries, &volume_fluxes](auto derived_boundary_condition_v) {\n        using DerivedBoundaryCondition =\n            tmpl::type_from<decltype(derived_boundary_condition_v)>;\n\n        if (number_of_boundaries_left == 0) {\n          return;\n        }\n\n        for (const Direction<Dim>& direction : element.external_boundaries()) {\n          const auto& boundary_condition =\n              *external_boundary_conditions.at(direction);\n          if (typeid(boundary_condition) == typeid(DerivedBoundaryCondition)) {\n            detail::apply_boundary_condition_on_face<System>(\n                box, boundary_correction,\n                dynamic_cast<const DerivedBoundaryCondition&>(\n                    boundary_condition),\n                direction, db::get<variables_tag>(*box), volume_fluxes,\n                partial_derivs, temporaries, primitive_vars,\n                db::get<::dg::Tags::Formulation>(*box),\n                db::get<::domain::Tags::Mesh<Dim>>(*box),\n                db::get<::domain::Tags::Element<Dim>>(*box),\n                db::get<::domain::Tags::ElementMap<Dim, Frame::Grid>>(*box),\n                db::get<::domain::CoordinateMaps::Tags::CoordinateMap<\n                    Dim, Frame::Grid, Frame::Inertial>>(*box),\n                db::get<::Tags::Time>(*box),\n                db::get<::domain::Tags::FunctionsOfTime>(*box),\n                db::get<::domain::Tags::MeshVelocity<Dim>>(*box),\n                db::get<::domain::Tags::InverseJacobian<\n                    Dim, Frame::ElementLogical, Frame::Inertial>>(*box),\n                db::get<::domain::Tags::DetInvJacobian<Frame::ElementLogical,\n                                                       Frame::Inertial>>(*box),\n                typename BoundaryCorrection::dg_package_data_volume_tags{},\n                typename BoundaryCorrection::dg_package_field_tags{},\n                typename BoundaryCorrection::dg_boundary_terms_volume_tags{},\n                tmpl::remove_duplicates<tmpl::append<\n                    typename variables_tag::tags_list, fluxes_tags,\n                    typename BoundaryCorrection::dg_package_data_temporary_tags,\n                    typename detail::get_primitive_vars<\n                        System::has_primitive_and_conservative_vars>::\n                        template f<BoundaryCorrection>>>{},\n                typename DerivedBoundaryCondition::dg_gridless_tags{});\n            --number_of_boundaries_left;\n          }\n          if (number_of_boundaries_left == 0) {\n            return;\n          }\n        }\n      });\n}\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApplyBoundaryCorrections.hpp\n  BoundaryConditionsImpl.hpp\n  ComputeTimeDerivative.hpp\n  ComputeTimeDerivativeHelpers.hpp\n  InternalMortarDataImpl.hpp\n  NormalCovectorAndMagnitude.hpp\n  PackageDataImpl.hpp\n  VolumeTermsImpl.hpp\n  VolumeTermsImpl.tpp\n  )\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/InterfaceHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/BoundaryConditionsImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/InternalMortarDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/UsingSubcell.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/SendDataToElement.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\nstruct TimeStepId;\n}  // namespace Tags\nnamespace evolution::dg::Tags {\ntemplate <size_t Dim>\nstruct MortarInfo;\n}  // namespace evolution::dg::Tags\n\nnamespace evolution::dg::subcell {\n// We use a forward declaration instead of including a header file to avoid\n// coupling to the DG-subcell libraries for executables that don't use subcell.\ntemplate <typename Metavariables, typename DbTagsList, size_t Dim>\nvoid prepare_neighbor_data(\n    gsl::not_null<DirectionMap<Dim, DataVector>*>\n        all_neighbor_data_for_reconstruction,\n    gsl::not_null<Mesh<Dim>*> ghost_data_mesh,\n    gsl::not_null<db::DataBox<DbTagsList>*> box,\n    [[maybe_unused]] const Variables<db::wrap_tags_in<\n        ::Tags::Flux, typename Metavariables::system::flux_variables,\n        tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes);\ntemplate <typename DbTagsList>\nint get_tci_decision(const db::DataBox<DbTagsList>& box);\n}  // namespace evolution::dg::subcell\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace evolution::dg::Actions {\nnamespace detail {\ntemplate <typename T>\nstruct get_dg_package_temporary_tags {\n  using type = typename T::dg_package_data_temporary_tags;\n};\ntemplate <typename T>\nstruct get_dg_package_field_tags {\n  using type = typename T::dg_package_field_tags;\n};\ntemplate <typename System, typename T>\nstruct get_primitive_tags_for_face {\n  using type = typename get_primitive_vars<\n      System::has_primitive_and_conservative_vars>::template f<T>;\n};\n}  // namespace detail\n\n/*!\n * \\brief Computes the time derivative for a DG time step.\n *\n * Computes the volume fluxes, the divergence of the fluxes and all additional\n * interior contributions to the time derivatives (both nonconservative products\n * and source terms). The internal mortar data is also computed.\n *\n * The general first-order hyperbolic evolution equation solved for conservative\n * systems is:\n *\n * \\f{align*}{\n * \\frac{\\partial u_\\alpha}{\\partial \\hat{t}}\n *  + \\partial_{i}\n *   \\left(F^i_\\alpha - v^i_g u_\\alpha\\right)\n *   = S_\\alpha-u_\\alpha\\partial_i v^i_g,\n * \\f}\n *\n * where \\f$F^i_{\\alpha}\\f$ are the fluxes when the mesh isn't moving,\n * \\f$v^i_g\\f$ is the velocity of the mesh, \\f$u_{\\alpha}\\f$ are the evolved\n * variables, \\f$S_{\\alpha}\\f$ are the source terms, \\f$\\hat{t}\\f$ is the\n * time in the logical frame, \\f$t\\f$ is the time in the inertial frame, hatted\n * indices correspond to logical frame quantites, and unhatted indices to\n * inertial frame quantities (e.g. \\f$\\partial_i\\f$ is the derivative with\n * respect to the inertial coordinates). For evolution equations that do not\n * have any fluxes and only nonconservative products we evolve:\n *\n * \\f{align*}{\n * \\frac{\\partial u_\\alpha}{\\partial \\hat{t}}\n *   +\\left(B^i_{\\alpha\\beta}-v^i_g \\delta_{\\alpha\\beta}\n *   \\right)\\partial_{i}u_\\beta = S_\\alpha.\n * \\f}\n *\n * Finally, for equations with both conservative terms and nonconservative\n * products we use:\n *\n * \\f{align*}{\n * \\frac{\\partial u_\\alpha}{\\partial \\hat{t}}\n *   + \\partial_{i}\n *   \\left(F^i_\\alpha - v^i_g u_\\alpha\\right)\n *   +B^i_{\\alpha\\beta}\\partial_{i}u_\\beta\n *   = S_\\alpha-u_\\alpha\\partial_i v^i_g,\n * \\f}\n *\n * where \\f$B^i_{\\alpha\\beta}\\f$ is the matrix for the nonconservative products.\n *\n * ### Volume Terms\n *\n * The mesh velocity is added to the flux automatically if the mesh is moving.\n * That is,\n *\n * \\f{align*}{\n *  F^i_{\\alpha}\\to F^i_{\\alpha}-v^i_{g} u_{\\alpha}\n * \\f}\n *\n * The source terms are also altered automatically by adding:\n *\n * \\f{align*}{\n *  -u_\\alpha \\partial_i v^i_g,\n * \\f}\n *\n * For systems with equations that only contain nonconservative products, the\n * following mesh velocity is automatically added to the time derivative:\n *\n * \\f{align*}{\n *  v^i_g \\partial_i u_\\alpha,\n * \\f}\n *\n * \\note The term is always added in the `Frame::Inertial` frame, and the plus\n * sign arises because we add it to the time derivative.\n *\n * \\warning The mesh velocity terms are added to the time derivatives before\n * invoking the boundary conditions. This means that the time derivatives passed\n * to the boundary conditions are with respect to \\f$\\hat{t}\\f$, not \\f$t\\f$.\n * This is especially important in the TimeDerivative/Bjorhus boundary\n * conditions.\n *\n * Here are examples of the `TimeDerivative` struct used to compute the volume\n * time derivative. This struct is what the type alias\n * `System::compute_volume_time_derivative` points to. The time derivatives are\n * as `gsl::not_null` first, then the temporary tags as `gsl::not_null`,\n * followed by the `argument_tags`. These type aliases are given by\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp dt_ta\n *\n * for the examples. For a conservative system without primitives the `apply`\n * function would look like\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp dt_con\n *\n * For a nonconservative system it would be\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp dt_nc\n *\n * And finally, for a mixed conservative-nonconservative system with primitive\n * variables\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp dt_mp\n *\n * In addition to each variable being passed individually, if the time\n * derivative struct inherits from `evolution::PassVariables`, then the time\n * derivatives, fluxes, and temporaries are passed as\n * `gsl::not_null<Variables<...>>`. This is useful for systems where\n * additional quantities are sometimes evolved, and just generally nice for\n * keeping the number of arguments reasonable. Below are the above examples\n * but with `Variables` being passed.\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp dt_con_variables\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp dt_nc_variables\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp dt_mp_variables\n *\n * Uses:\n * - System:\n *   - `variables_tag`\n *   - `flux_variables`\n *   - `gradient_variables`\n *   - `compute_volume_time_derivative_terms`\n *\n * - DataBox:\n *   - Items in `system::compute_volume_time_derivative_terms::argument_tags`\n *   - `domain::Tags::MeshVelocity<Metavariables::volume_dim>`\n *   - `Metavariables::system::variables_tag`\n *   - `Metavariables::system::flux_variables`\n *   - `Metavariables::system::gradient_variables`\n *   - `domain::Tags::DivMeshVelocity`\n *   - `DirectionsTag`,\n *   - Required interface items for `Metavariables::system::normal_dot_fluxes`\n *\n * DataBox changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - db::add_tag_prefix<Tags::Flux, variables_tag,\n *                        tmpl::size_t<system::volume_dim>, Frame::Inertial>\n *   - `Tags::dt<system::variable_tags>`\n *   - Tags::Interface<\n *     DirectionsTag, db::add_tag_prefix<Tags::NormalDotFlux, variables_tag>>\n *   - `Tags::Mortars<typename BoundaryScheme::mortar_data_tag, VolumeDim>`\n *\n * ### Internal Boundary Terms\n *\n * Internal boundary terms must be derived from\n * `evolution::BoundaryCorrection`.  Each concrete boundary correction\n * must specify:\n *\n * - type alias `dg_package_field_tags`. These are what will be returned by\n *   `gsl::not_null` from the `dg_package_data` member function.\n *\n * - type alias `dg_package_data_temporary_tags`. These are temporary tags\n *   that are projected to the face and then passed to the `dg_package_data`\n *   function.\n *\n * - type alias `dg_package_data_primitive_tags`. These are the primitive\n *   variables (if any) that are projected to the face and then passed to\n *   `dg_package_data`.\n *\n * - type alias `dg_package_data_volume_tags`. These are tags that are not\n *   projected to the interface and are retrieved directly from the `DataBox`.\n *   The equation of state for hydrodynamics systems is an example of what\n *   would be a \"volume tag\".\n *\n * A `static constexpr bool need_normal_vector` must be specified. If `true`\n * then the normal vector is computed from the normal covector. This is\n * currently not implemented.\n *\n * The `dg_package_data` function takes as arguments `gsl::not_null` of the\n * `dg_package_field_tags`, then the projected evolved variables, the\n * projected fluxes, the projected temporaries, the projected primitives, the\n * unit normal covector, mesh velocity, normal dotted into the mesh velocity,\n * the `volume_tags`, and finally the `dg::Formulation`. The `dg_package_data`\n * function must compute all ingredients for the boundary correction, including\n * mesh-velocity-corrected characteristic speeds. However, the projected fluxes\n * passed in are \\f$F^i - u v^i_g\\f$ (the mesh velocity term is already\n * included). The `dg_package_data` function must also return a `double` that is\n * the maximum absolute characteristic speed over the entire face. This will be\n * used for checking that the time step doesn't violate the CFL condition.\n *\n * Here is an example of the type aliases and `bool`:\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp bt_ta\n *\n * The normal vector requirement is:\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp bt_nnv\n *\n * For a conservative system with primitive variables and using the `TimeStepId`\n * as a volume tag the `dg_package_data` function looks like:\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp bt_cp\n *\n * For a mixed conservative-nonconservative system with primitive variables and\n * using the `TimeStepId` as a volume tag the `dg_package_data` function looks\n * like:\n *\n * \\snippet ComputeTimeDerivativeImpl.tpp bt_mp\n *\n * Uses:\n * - System:\n *   - `boundary_correction`\n *   - `variables_tag`\n *   - `flux_variables`\n *   - `gradients_tags`\n *   - `compute_volume_time_derivative`\n *   - `has_primitive_and_conservative_vars`\n *   - `primitive_variables_tag` if system has primitive variables\n *\n * - DataBox:\n *   - `domain::Tags::Element<Dim>`\n *   - `domain::Tags::Mesh<Dim>`\n *   - `evolution::dg::Tags::MortarMesh<Dim>`\n *   - `evolution::dg::Tags::MortarData<Dim>`\n *   - `Tags::TimeStepId`\n *   - \\code{.cpp}\n *      domain::Tags::Interface<domain::Tags::InternalDirections<Dim>,\n *                                       domain::Tags::Mesh<Dim - 1>>\n *     \\endcode\n *   - \\code{.cpp}\n *     domain::Tags::Interface<\n *         domain::Tags::InternalDirections<Dim>,\n *         ::Tags::Normalized<\n *             domain::Tags::UnnormalizedFaceNormal<Dim, Frame::Inertial>>>\n *     \\endcode\n *   - \\code{.cpp}\n *     domain::Tags::Interface<\n *              domain::Tags::InternalDirections<Dim>,\n *              domain::Tags::MeshVelocity<Dim, Frame::Inertial>>\n *     \\endcode\n *   - `Metavariables::system::variables_tag`\n *   - `Metavariables::system::flux_variables`\n *   - `Metavariables::system::primitive_tags` if exists\n *   - boundary correction `dg_package_data_volume_tags`\n *\n * DataBox changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `evolution::dg::Tags::MortarData<Dim>`\n */\ntemplate <size_t Dim, typename EvolutionSystem, typename DgStepChoosers,\n          bool LocalTimeStepping, bool UseNodegroupDgElements>\nstruct ComputeTimeDerivative {\n  using inbox_tags =\n      tmpl::list<evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n          Dim, UseNodegroupDgElements>>;\n  using const_global_cache_tags = tmpl::append<\n      tmpl::list<::dg::Tags::Formulation, evolution::Tags::BoundaryCorrection,\n                 domain::Tags::ExternalBoundaryConditions<Dim>>,\n      tmpl::conditional_t<\n          LocalTimeStepping,\n          typename ChangeStepSize<DgStepChoosers>::const_global_cache_tags,\n          tmpl::list<>>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* /*meta*/);  // NOLINT const\n\n private:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables>\n  static void send_data_for_fluxes(\n      gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,\n      gsl::not_null<db::DataBox<DbTagsList>*> box,\n      [[maybe_unused]] const Variables<db::wrap_tags_in<\n          ::Tags::Flux, typename EvolutionSystem::flux_variables,\n          tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes);\n};\n\ntemplate <size_t Dim, typename EvolutionSystem, typename DgStepChoosers,\n          bool LocalTimeStepping, bool UseNodegroupDgElements>\ntemplate <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n          typename ActionList, typename ParallelComponent,\n          typename Metavariables>\nParallel::iterable_action_return_t\nComputeTimeDerivative<Dim, EvolutionSystem, DgStepChoosers, LocalTimeStepping,\n                      UseNodegroupDgElements>::\n    apply(db::DataBox<DbTagsList>& box,\n          tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n          Parallel::GlobalCache<Metavariables>& cache,\n          const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n          const ParallelComponent* const /*meta*/) {  // NOLINT const\n  static_assert(UseNodegroupDgElements ==\n                    Parallel::is_dg_element_collection_v<ParallelComponent>,\n                \"The action ComputeTimeDerivative is told by the \"\n                \"template parameter UseNodegroupDgElements that it is being \"\n                \"used with a DgElementCollection, but the ParallelComponent \"\n                \"is not a DgElementCollection. You need to change the \"\n                \"template parameter on the ComputeTimeDerivative action \"\n                \"in your action list.\");\n\n  using variables_tag = typename EvolutionSystem::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  using partial_derivative_tags = typename EvolutionSystem::gradient_variables;\n  using flux_variables = typename EvolutionSystem::flux_variables;\n  using compute_volume_time_derivative_terms =\n      typename EvolutionSystem::compute_volume_time_derivative_terms;\n\n  const Mesh<Dim>& mesh = db::get<::domain::Tags::Mesh<Dim>>(box);\n  const Element<Dim>& element = db::get<domain::Tags::Element<Dim>>(box);\n  const ::dg::Formulation dg_formulation =\n      db::get<::dg::Tags::Formulation>(box);\n  ASSERT(alg::all_of(mesh.basis(),\n                     [&mesh](const Spectral::Basis current_basis) {\n                       return current_basis == mesh.basis(0);\n                     }) or\n             element.topologies() != domain::topologies::hypercube<Dim>,\n         \"An isotropic basis must be used in the evolution code. While \"\n         \"theoretically this restriction could be lifted, the simplification \"\n         \"it offers are quite substantial. Relaxing this assumption is likely \"\n         \"to require quite a bit of careful code refactoring and debugging.\");\n  ASSERT(alg::all_of(mesh.quadrature(),\n                     [&mesh](const Spectral::Quadrature current_quadrature) {\n                       return current_quadrature == mesh.quadrature(0);\n                     }) or\n             element.topologies() != domain::topologies::hypercube<Dim>,\n         \"An isotropic quadrature must be used in the evolution code. While \"\n         \"theoretically this restriction could be lifted, the simplification \"\n         \"it offers are quite substantial. Relaxing this assumption is likely \"\n         \"to require quite a bit of careful code refactoring and debugging.\");\n\n  const auto& boundary_correction =\n      db::get<evolution::Tags::BoundaryCorrection>(box);\n  using derived_boundary_corrections =\n      tmpl::at<typename Metavariables::factory_creation::factory_classes,\n               evolution::BoundaryCorrection>;\n\n  // To avoid a second allocation in internal_mortar_data, we allocate the\n  // variables needed to construct the fields on the faces here along with\n  // everything else. This requires us to know all the tags necessary to apply\n  // boundary corrections. However, since we pick boundary corrections at\n  // runtime, we just gather all possible tags from all possible boundary\n  // corrections and lump them into the allocation. This may result in a\n  // larger-than-necessary allocation, but it won't be that much larger.\n  using all_dg_package_temporary_tags =\n      tmpl::transform<derived_boundary_corrections,\n                      detail::get_dg_package_temporary_tags<tmpl::_1>>;\n  using all_primitive_tags_for_face =\n      tmpl::transform<derived_boundary_corrections,\n                      detail::get_primitive_tags_for_face<\n                          tmpl::pin<EvolutionSystem>, tmpl::_1>>;\n  using fluxes_tags = db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                       tmpl::size_t<Dim>, Frame::Inertial>;\n  using dg_package_data_projected_tags =\n      tmpl::list<typename variables_tag::tags_list, fluxes_tags,\n                 all_dg_package_temporary_tags, all_primitive_tags_for_face>;\n  using all_face_temporary_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::push_back<\n          tmpl::list<dg_package_data_projected_tags,\n                     detail::inverse_spatial_metric_tag<EvolutionSystem>>,\n          detail::OneOverNormalVectorMagnitude, detail::NormalVector<Dim>>>>;\n  // To avoid additional allocations in internal_mortar_data, we provide a\n  // buffer used to compute the packaged data before it has to be projected to\n  // the mortar. We get all mortar tags for similar reasons as described above\n  using all_mortar_tags = tmpl::remove_duplicates<tmpl::flatten<\n      tmpl::transform<derived_boundary_corrections,\n                      detail::get_dg_package_field_tags<tmpl::_1>>>>;\n\n  // We also don't use the number of volume mesh grid points. We instead use the\n  // max number of grid points from each face. That way, our allocation will be\n  // large enough to hold any face and we can reuse the allocation for each face\n  // without having to resize it.\n  size_t num_face_temporary_grid_points = 0;\n  {\n    for (const auto& [direction, neighbors_in_direction] :\n         element.neighbors()) {\n      (void)neighbors_in_direction;\n      const auto face_mesh = mesh.slice_away(direction.dimension());\n      num_face_temporary_grid_points = std::max(\n          num_face_temporary_grid_points, face_mesh.number_of_grid_points());\n    }\n  }\n\n  // Allocate the Variables classes needed for the time derivative\n  // computation.\n  //\n  // This is factored out so that we will be able to do ADER-DG/CG where a\n  // spacetime polynomial is constructed by solving implicit equations in time\n  // using a Picard iteration. A high-order initial guess is needed to\n  // efficiently construct the ADER spacetime solution. This initial guess is\n  // obtained using continuous RK methods, and so we will want to reuse\n  // buffers. Thus, the volume_terms function returns by reference rather than\n  // by value.\n  using VarsTemporaries =\n      Variables<typename compute_volume_time_derivative_terms::temporary_tags>;\n  using VarsFluxes =\n      Variables<db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                 tmpl::size_t<Dim>, Frame::Inertial>>;\n  using VarsPartialDerivatives =\n      Variables<db::wrap_tags_in<::Tags::deriv, partial_derivative_tags,\n                                 tmpl::size_t<Dim>, Frame::Inertial>>;\n  using VarsDivFluxes = Variables<db::wrap_tags_in<\n      ::Tags::div, db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                    tmpl::size_t<Dim>, Frame::Inertial>>>;\n  using VarsFaceTemporaries = Variables<all_face_temporary_tags>;\n  using DgPackagedDataVarsOnFace = Variables<all_mortar_tags>;\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const size_t buffer_size =\n      (VarsTemporaries::number_of_independent_components +\n       VarsFluxes::number_of_independent_components +\n       VarsPartialDerivatives::number_of_independent_components +\n       VarsDivFluxes::number_of_independent_components) *\n          number_of_grid_points +\n      // Different number of grid points. See explanation above where\n      // num_face_temporary_grid_points is defined\n      (VarsFaceTemporaries::number_of_independent_components +\n       DgPackagedDataVarsOnFace::number_of_independent_components) *\n          num_face_temporary_grid_points;\n  auto buffer = cpp20::make_unique_for_overwrite<double[]>(buffer_size);\n#ifdef SPECTRE_NAN_INIT\n  std::fill(&buffer[0], &buffer[buffer_size],\n            std::numeric_limits<double>::signaling_NaN());\n#endif\n  VarsTemporaries temporaries{\n      &buffer[0], VarsTemporaries::number_of_independent_components *\n                      number_of_grid_points};\n  VarsFluxes volume_fluxes{\n      &buffer[VarsTemporaries::number_of_independent_components *\n              number_of_grid_points],\n      VarsFluxes::number_of_independent_components * number_of_grid_points};\n  VarsPartialDerivatives partial_derivs{\n      &buffer[(VarsTemporaries::number_of_independent_components +\n               VarsFluxes::number_of_independent_components) *\n              number_of_grid_points],\n      VarsPartialDerivatives::number_of_independent_components *\n          number_of_grid_points};\n  VarsDivFluxes div_fluxes{\n      &buffer[(VarsTemporaries::number_of_independent_components +\n               VarsFluxes::number_of_independent_components +\n               VarsPartialDerivatives::number_of_independent_components) *\n              number_of_grid_points],\n      VarsDivFluxes::number_of_independent_components * number_of_grid_points};\n  // Lighter weight data structure than a Variables to avoid passing even more\n  // templates to internal_mortar_data.\n  gsl::span<double> face_temporaries = gsl::make_span<double>(\n      &buffer[(VarsTemporaries::number_of_independent_components +\n               VarsFluxes::number_of_independent_components +\n               VarsPartialDerivatives::number_of_independent_components +\n               VarsDivFluxes::number_of_independent_components) *\n              number_of_grid_points],\n      // Different number of grid points. See explanation above where\n      // num_face_temporary_grid_points is defined\n      VarsFaceTemporaries::number_of_independent_components *\n          num_face_temporary_grid_points);\n  gsl::span<double> packaged_data_buffer = gsl::make_span<double>(\n      &buffer[(VarsTemporaries::number_of_independent_components +\n               VarsFluxes::number_of_independent_components +\n               VarsPartialDerivatives::number_of_independent_components +\n               VarsDivFluxes::number_of_independent_components) *\n                  number_of_grid_points +\n              VarsFaceTemporaries::number_of_independent_components *\n                  num_face_temporary_grid_points],\n      // Different number of grid points. See explanation above where\n      // num_face_temporary_grid_points is defined\n      DgPackagedDataVarsOnFace::number_of_independent_components *\n          num_face_temporary_grid_points);\n\n  const Scalar<DataVector>* det_inverse_jacobian = nullptr;\n  if constexpr (tmpl::size<flux_variables>::value != 0) {\n    if (dg_formulation == ::dg::Formulation::WeakInertial) {\n      det_inverse_jacobian = &db::get<\n          domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>>(\n          box);\n    }\n  }\n  db::mutate_apply<\n      tmpl::list<dt_variables_tag>,\n      typename compute_volume_time_derivative_terms::argument_tags>(\n      [&dg_formulation, &div_fluxes, &det_inverse_jacobian,\n       &div_mesh_velocity = db::get<::domain::Tags::DivMeshVelocity>(box),\n       &evolved_variables = db::get<variables_tag>(box),\n       &inertial_coordinates =\n           db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box),\n       &logical_to_inertial_inv_jacobian =\n           db::get<::domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                   Frame::Inertial>>(box),\n       &mesh, &mesh_velocity = db::get<::domain::Tags::MeshVelocity<Dim>>(box),\n       &partial_derivs, &temporaries, &volume_fluxes](\n          const gsl::not_null<Variables<\n              db::wrap_tags_in<::Tags::dt, typename variables_tag::tags_list>>*>\n              dt_vars_ptr,\n          const auto&... time_derivative_args) {\n        detail::volume_terms<compute_volume_time_derivative_terms>(\n            dt_vars_ptr, make_not_null(&volume_fluxes),\n            make_not_null(&partial_derivs), make_not_null(&temporaries),\n            make_not_null(&div_fluxes), evolved_variables, dg_formulation, mesh,\n            inertial_coordinates, logical_to_inertial_inv_jacobian,\n            det_inverse_jacobian, mesh_velocity, div_mesh_velocity,\n            time_derivative_args...);\n      },\n      make_not_null(&box));\n\n  const Variables<detail::get_primitive_vars_tags_from_system<EvolutionSystem>>*\n      primitive_vars{nullptr};\n  if constexpr (EvolutionSystem::has_primitive_and_conservative_vars) {\n    primitive_vars =\n        &db::get<typename EvolutionSystem::primitive_variables_tag>(box);\n  }\n\n  static_assert(\n      tmpl::all<derived_boundary_corrections, std::is_final<tmpl::_1>>::value,\n      \"All createable classes for boundary corrections must be marked \"\n      \"final.\");\n  tmpl::for_each<derived_boundary_corrections>(\n      [&boundary_correction, &box, &partial_derivs, &primitive_vars,\n       &temporaries, &volume_fluxes, &packaged_data_buffer,\n       &face_temporaries](auto derived_correction_v) {\n        using DerivedCorrection =\n            tmpl::type_from<decltype(derived_correction_v)>;\n        if (typeid(boundary_correction) == typeid(DerivedCorrection)) {\n          // Compute internal boundary quantities on the mortar for sides\n          // of the element that have neighbors, i.e. they are not an\n          // external side.\n          // Note: this call mutates:\n          //  - evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>,\n          //  - evolution::dg::Tags::MortarData<Dim>\n          detail::internal_mortar_data<EvolutionSystem, Dim>(\n              make_not_null(&box), make_not_null(&face_temporaries),\n              make_not_null(&packaged_data_buffer),\n              dynamic_cast<const DerivedCorrection&>(boundary_correction),\n              db::get<variables_tag>(box), volume_fluxes, temporaries,\n              primitive_vars,\n              typename DerivedCorrection::dg_package_data_volume_tags{});\n\n          detail::apply_boundary_conditions_on_all_external_faces<\n              EvolutionSystem, Dim>(\n              make_not_null(&box),\n              dynamic_cast<const DerivedCorrection&>(boundary_correction),\n              temporaries, volume_fluxes, partial_derivs, primitive_vars);\n        }\n      });\n\n  if constexpr (LocalTimeStepping) {\n    db::mutate_apply<ChangeStepSize<DgStepChoosers>>(make_not_null(&box));\n  }\n\n  send_data_for_fluxes<ParallelComponent>(make_not_null(&cache),\n                                          make_not_null(&box), volume_fluxes);\n  return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n}\n\ntemplate <size_t Dim, typename EvolutionSystem, typename DgStepChoosers,\n          bool LocalTimeStepping, bool UseNodegroupDgElements>\ntemplate <typename ParallelComponent, typename DbTagsList,\n          typename Metavariables>\nvoid ComputeTimeDerivative<Dim, EvolutionSystem, DgStepChoosers,\n                           LocalTimeStepping, UseNodegroupDgElements>::\n    send_data_for_fluxes(\n        const gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,\n        const gsl::not_null<db::DataBox<DbTagsList>*> box,\n        [[maybe_unused]] const Variables<db::wrap_tags_in<\n            ::Tags::Flux, typename EvolutionSystem::flux_variables,\n            tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes) {\n  using variables_tag = typename EvolutionSystem::variables_tag;\n\n  auto& receiver_proxy =\n      Parallel::get_parallel_component<ParallelComponent>(*cache);\n  const auto& element = db::get<domain::Tags::Element<Dim>>(*box);\n\n  const auto& time_step_id = db::get<::Tags::TimeStepId>(*box);\n  const auto integration_order =\n      db::get<::Tags::HistoryEvolvedVariables<variables_tag>>(*box)\n          .integration_order();\n  const auto& all_mortar_data =\n      db::get<evolution::dg::Tags::MortarData<Dim>>(*box);\n  const auto& mortar_meshes = get<evolution::dg::Tags::MortarMesh<Dim>>(*box);\n\n  std::optional<DirectionMap<Dim, DataVector>>\n      all_neighbor_data_for_reconstruction = std::nullopt;\n  int tci_decision = 0;\n  const Mesh<Dim>& volume_mesh = db::get<domain::Tags::Mesh<Dim>>(*box);\n  std::optional<Mesh<Dim>> ghost_data_mesh = std::nullopt;\n  if constexpr (using_subcell_v<Metavariables>) {\n    if (not all_neighbor_data_for_reconstruction.has_value()) {\n      all_neighbor_data_for_reconstruction = DirectionMap<Dim, DataVector>{};\n    }\n\n    evolution::dg::subcell::prepare_neighbor_data<Metavariables>(\n        make_not_null(&all_neighbor_data_for_reconstruction.value()),\n        make_not_null(&ghost_data_mesh), box, volume_fluxes);\n    tci_decision = evolution::dg::subcell::get_tci_decision(*box);\n  }\n\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    DataVector ghost_and_subcell_data{};\n    if constexpr (using_subcell_v<Metavariables>) {\n      ASSERT(all_neighbor_data_for_reconstruction.has_value(),\n             \"Trying to do DG-subcell but the ghost and subcell data for the \"\n             \"neighbor has not been set.\");\n      ghost_and_subcell_data =\n          std::move(all_neighbor_data_for_reconstruction.value()[direction]);\n    }\n\n    const size_t total_neighbors = neighbors.size();\n    size_t neighbor_count = 1;\n    for (const auto& neighbor : neighbors) {\n      const auto& orientation = neighbors.orientation(neighbor);\n      const auto direction_from_neighbor = orientation(direction.opposite());\n      const DirectionalId<Dim> mortar_id{direction, neighbor};\n\n      const Mesh<Dim - 1>& mortar_mesh = mortar_meshes.at(mortar_id);\n      const auto volume_mesh_for_neighbor = orientation(volume_mesh);\n      const auto mortar_mesh_for_neighbor =\n          orient_mesh_on_slice(mortar_mesh, direction.dimension(), orientation);\n      DataVector neighbor_boundary_data_on_mortar{};\n\n      if (LIKELY(orientation.is_aligned())) {\n        neighbor_boundary_data_on_mortar =\n            *all_mortar_data.at(mortar_id).local().mortar_data.value();\n      } else {\n        const auto& slice_extents = mortar_mesh.extents();\n        neighbor_boundary_data_on_mortar = orient_variables_on_slice(\n            all_mortar_data.at(mortar_id).local().mortar_data.value(),\n            slice_extents, direction.dimension(), orientation);\n      }\n\n      const TimeStepId& next_time_step_id =\n          db::get<::Tags::Next<::Tags::TimeStepId>>(*box);\n\n      using SendData = evolution::dg::BoundaryData<Dim>;\n      SendData data{};\n\n      if (neighbor_count == total_neighbors) {\n        data = SendData{volume_mesh_for_neighbor,\n                        ghost_data_mesh,\n                        mortar_mesh_for_neighbor,\n                        std::move(ghost_and_subcell_data),\n                        {std::move(neighbor_boundary_data_on_mortar)},\n                        next_time_step_id,\n                        tci_decision,\n                        integration_order,\n                        std::nullopt};\n      } else {\n        data = SendData{volume_mesh_for_neighbor,\n                        ghost_data_mesh,\n                        mortar_mesh_for_neighbor,\n                        ghost_and_subcell_data,\n                        {std::move(neighbor_boundary_data_on_mortar)},\n                        next_time_step_id,\n                        tci_decision,\n                        integration_order,\n                        std::nullopt};\n      }\n\n      // Send mortar data (the `std::tuple` named `data`) to neighbor\n      if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {\n        Parallel::local_synchronous_action<\n            Parallel::Actions::SendDataToElement>(\n            receiver_proxy, cache,\n            evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                Dim, UseNodegroupDgElements>{},\n            neighbor, time_step_id,\n            std::make_pair(DirectionalId{direction_from_neighbor, element.id()},\n                           std::move(data)));\n      } else {\n        Parallel::receive_data<\n            evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                Dim, UseNodegroupDgElements>>(\n            receiver_proxy[neighbor], time_step_id,\n            std::make_pair(DirectionalId{direction_from_neighbor, element.id()},\n                           std::move(data)));\n      }\n      ++neighbor_count;\n    }\n  }\n\n  const auto& mortar_info = db::get<Tags::MortarInfo<Dim>>(*box);\n  // We treat this as a set, but use a map because we don't have a\n  // non-allocating set type.\n  DirectionMap<Dim, bool> mortar_history_directions{};\n  for (const auto& [mortar, info] : mortar_info) {\n    if (info.time_stepping_policy() == TimeSteppingPolicy::Conservative) {\n      mortar_history_directions.emplace(mortar.direction(), true);\n    }\n  }\n\n  if (not mortar_history_directions.empty()) {\n    // Need volume Jacobian for any face whose normal direction uses Gauss\n    // points. This means mixed-quadrature non-hypercube elements (e.g.\n    // full_cylinder) where some directions have collocated face points and\n    // others do not.\n    const bool any_direction_uses_gauss =\n        alg::any_of(volume_mesh.quadrature(), [](const Spectral::Quadrature q) {\n          return q == Spectral::Quadrature::Gauss;\n        });\n\n    const Scalar<DataVector> volume_det_inv_jacobian{};\n    if (any_direction_uses_gauss) {\n      // NOLINTNEXTLINE\n      const_cast<DataVector&>(get(volume_det_inv_jacobian))\n          .set_data_ref(make_not_null(&const_cast<DataVector&>(  // NOLINT\n              get(db::get<domain::Tags::DetInvJacobian<\n                      Frame::ElementLogical, Frame::Inertial>>(*box)))));\n    }\n\n    // Add face normal and Jacobian determinants to the local mortar data. We\n    // only need the Jacobians for directions using Gauss points. Then copy\n    // over into the boundary history, since that's what the LTS steppers use.\n    //\n    // The boundary history coupling computation (which computes the _lifted_\n    // boundary correction) returns a Variables<dt<EvolvedVars>> instead of\n    // using the `NormalDotNumericalFlux` prefix tag. This is because the\n    // returned quantity is more a `dt` quantity than a\n    // `NormalDotNormalDotFlux` since it's been lifted to the volume.\n    db::mutate<evolution::dg::Tags::MortarData<Dim>,\n               evolution::dg::Tags::MortarDataHistory<Dim>>(\n        [&element, integration_order, &mortar_history_directions, &mortar_info,\n         &time_step_id, any_direction_uses_gauss, &volume_det_inv_jacobian,\n         &volume_mesh](\n            const gsl::not_null<\n                DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n                mortar_data,\n            const gsl::not_null<DirectionalIdMap<\n                Dim, TimeSteppers::BoundaryHistory<\n                         evolution::dg::MortarData<Dim>,\n                         evolution::dg::MortarData<Dim>, DataVector>>*>\n                boundary_data_history,\n            const DirectionMap<Dim,\n                               std::optional<Variables<tmpl::list<\n                                   evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>&\n                normal_covector_and_magnitude) {\n          Scalar<DataVector> volume_det_jacobian{};\n          Scalar<DataVector> face_det_jacobian{};\n          if (any_direction_uses_gauss) {\n            get(volume_det_jacobian) = 1.0 / get(volume_det_inv_jacobian);\n          }\n          for (const auto& [direction, neighbors_in_direction] :\n               element.neighbors()) {\n            if (not mortar_history_directions.contains(direction)) {\n              continue;\n            }\n            // We can perform projections once for all neighbors in the\n            // direction because we care about the _face_ mesh, not the mortar\n            // mesh.\n            ASSERT(normal_covector_and_magnitude.at(direction).has_value(),\n                   \"The normal covector and magnitude have not been computed.\");\n            const Scalar<DataVector>& face_normal_magnitude =\n                get<evolution::dg::Tags::MagnitudeOfNormal>(\n                    *normal_covector_and_magnitude.at(direction));\n            if (volume_mesh.quadrature(direction.dimension()) ==\n                Spectral::Quadrature::Gauss) {\n              const Matrix identity{};\n              auto interpolation_matrices =\n                  make_array<Dim>(std::cref(identity));\n              const std::pair<Matrix, Matrix>& matrices =\n                  Spectral::boundary_interpolation_matrices(\n                      volume_mesh.slice_through(direction.dimension()));\n              gsl::at(interpolation_matrices, direction.dimension()) =\n                  direction.side() == Side::Upper ? matrices.second\n                                                  : matrices.first;\n              if (get(face_det_jacobian).size() !=\n                  get(face_normal_magnitude).size()) {\n                get(face_det_jacobian) =\n                    DataVector{get(face_normal_magnitude).size()};\n              }\n              apply_matrices(make_not_null(&get(face_det_jacobian)),\n                             interpolation_matrices, get(volume_det_jacobian),\n                             volume_mesh.extents());\n            }\n\n            for (const auto& neighbor : neighbors_in_direction) {\n              const DirectionalId<Dim> mortar_id{direction, neighbor};\n              if (mortar_info.at(mortar_id).time_stepping_policy() !=\n                  TimeSteppingPolicy::Conservative) {\n                continue;\n              }\n              auto& local_mortar_data = mortar_data->at(mortar_id).local();\n              local_mortar_data.face_normal_magnitude = face_normal_magnitude;\n              if (volume_mesh.quadrature(direction.dimension()) ==\n                  Spectral::Quadrature::Gauss) {\n                local_mortar_data.volume_mesh = volume_mesh;\n                local_mortar_data.volume_det_inv_jacobian =\n                    volume_det_inv_jacobian;\n                local_mortar_data.face_det_jacobian = face_det_jacobian;\n              }\n              ASSERT(boundary_data_history->count(mortar_id) != 0,\n                     \"Could not insert the mortar data for \"\n                         << mortar_id\n                         << \" because the unordered map has not been \"\n                            \"initialized \"\n                            \"to have the mortar id.\");\n              boundary_data_history->at(mortar_id).local().insert(\n                  time_step_id, integration_order,\n                  std::move(mortar_data->at(mortar_id).local()));\n              mortar_data->at(mortar_id) = MortarDataHolder<Dim>{};\n            }\n          }\n        },\n        box,\n        db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box));\n  }\n}\n}  // namespace evolution::dg::Actions\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n\nnamespace evolution::dg::Actions::detail {\nCREATE_HAS_TYPE_ALIAS(boundary_conditions_base)\nCREATE_HAS_TYPE_ALIAS_V(boundary_conditions_base)\n\nCREATE_HAS_TYPE_ALIAS(inverse_spatial_metric_tag)\nCREATE_HAS_TYPE_ALIAS_V(inverse_spatial_metric_tag)\n\ntemplate <bool HasInverseSpatialMetricTag = false>\nstruct inverse_spatial_metric_tag_impl {\n  template <typename System>\n  using f = tmpl::list<>;\n};\n\ntemplate <>\nstruct inverse_spatial_metric_tag_impl<true> {\n  template <typename System>\n  using f = tmpl::list<typename System::inverse_spatial_metric_tag>;\n};\n\ntemplate <typename System>\nusing inverse_spatial_metric_tag = typename inverse_spatial_metric_tag_impl<\n    has_inverse_spatial_metric_tag_v<System>>::template f<System>;\n\ntemplate <bool HasPrimitiveVars = false>\nstruct get_primitive_vars {\n  template <typename BoundaryCorrection>\n  using f = tmpl::list<>;\n\n  template <typename BoundaryCondition>\n  using boundary_condition_interior_tags = tmpl::list<>;\n};\n\ntemplate <>\nstruct get_primitive_vars<true> {\n  template <typename BoundaryCorrection>\n  using f = typename BoundaryCorrection::dg_package_data_primitive_tags;\n\n  template <typename BoundaryCondition>\n  using boundary_condition_interior_tags =\n      typename BoundaryCondition::dg_interior_primitive_variables_tags;\n};\n\ntemplate <bool HasPrimitiveAndConservativeVars, typename BoundaryCorrection>\nusing boundary_correction_primitive_tags = typename get_primitive_vars<\n    HasPrimitiveAndConservativeVars>::template f<BoundaryCorrection>;\n\ntemplate <bool HasPrimitiveAndConservativeVars, typename BoundaryCondition>\nusing boundary_condition_primitive_tags =\n    typename get_primitive_vars<HasPrimitiveAndConservativeVars>::\n        template boundary_condition_interior_tags<BoundaryCondition>;\n\ntemplate <typename BoundaryCorrection, typename = std::void_t<>>\nstruct interior_tags_for_boundary_correction {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename BoundaryCorrection>\nstruct interior_tags_for_boundary_correction<\n    BoundaryCorrection,\n    std::void_t<typename BoundaryCorrection::\n                    dg_project_from_interior_for_boundary_condition>> {\n  using type = typename BoundaryCorrection::\n      dg_project_from_interior_for_boundary_condition;\n};\n\ntemplate <typename BoundaryCondition, typename = std::void_t<>>\nstruct derivative_tags_for_boundary_condition {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename BoundaryCondition>\nstruct derivative_tags_for_boundary_condition<\n    BoundaryCondition,\n    std::void_t<typename BoundaryCondition::dg_interior_derivative_tags>> {\n  using type = typename BoundaryCondition::dg_interior_derivative_tags;\n};\n\ntemplate <typename System, bool = System::has_primitive_and_conservative_vars>\nstruct get_primitive_vars_tags_from_system_impl {\n  using type = typename System::primitive_variables_tag::tags_list;\n};\n\ntemplate <typename System>\nstruct get_primitive_vars_tags_from_system_impl<System, false> {\n  using type = tmpl::list<>;\n};\n\n/// Returns a `tmpl::list` of the primitive tags. The list is empty if the\n/// system does not have primitive tags.\ntemplate <typename System>\nusing get_primitive_vars_tags_from_system =\n    typename get_primitive_vars_tags_from_system_impl<System>::type;\n\ntemplate <typename BoundaryCondition, typename = std::void_t<>>\nstruct get_dt_vars_from_boundary_condition_impl {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename BoundaryCondition>\nstruct get_dt_vars_from_boundary_condition_impl<\n    BoundaryCondition,\n    std::void_t<typename BoundaryCondition::dg_interior_dt_vars_tags>> {\n  using type = typename BoundaryCondition::dg_interior_dt_vars_tags;\n};\n\n/// Returns the `dg_interior_dt_vars_tags` if the boundary condition specifies\n/// them, otherwise returns an empty list.\ntemplate <typename BoundaryCondition>\nusing get_dt_vars_from_boundary_condition =\n    typename get_dt_vars_from_boundary_condition_impl<BoundaryCondition>::type;\n\ntemplate <typename BoundaryCondition, typename = std::void_t<>>\nstruct get_deriv_vars_from_boundary_condition_impl {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename BoundaryCondition>\nstruct get_deriv_vars_from_boundary_condition_impl<\n    BoundaryCondition,\n    std::void_t<typename BoundaryCondition::dg_interior_deriv_vars_tags>> {\n  using type = typename BoundaryCondition::dg_interior_deriv_vars_tags;\n};\n\n/// Returns the `dg_interior_deriv_vars_tags` if the boundary condition\n/// specifies them, otherwise returns an empty list.\ntemplate <typename BoundaryCondition>\nusing get_deriv_vars_from_boundary_condition =\n    typename get_deriv_vars_from_boundary_condition_impl<\n        BoundaryCondition>::type;\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/InternalMortarDataImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace evolution::dg::Actions::detail {\ntemplate <typename System, size_t Dim, typename BoundaryCorrection,\n          typename TemporaryTags, typename... PackageDataVolumeArgs>\nvoid internal_mortar_data_impl(\n    const gsl::not_null<\n        DirectionMap<Dim, std::optional<Variables<tmpl::list<\n                              evolution::dg::Tags::MagnitudeOfNormal,\n                              evolution::dg::Tags::NormalCovector<Dim>>>>>*>\n        normal_covector_and_magnitude_ptr,\n    const gsl::not_null<\n        DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n        mortar_data_ptr,\n    const gsl::not_null<gsl::span<double>*> face_temporaries,\n    const gsl::not_null<gsl::span<double>*> packaged_data_buffer,\n    const BoundaryCorrection& boundary_correction,\n    const Variables<typename System::variables_tag::tags_list>&\n        volume_evolved_vars,\n    const Variables<\n        db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,\n                         tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes,\n    const Variables<TemporaryTags>& volume_temporaries,\n    const Variables<get_primitive_vars_tags_from_system<System>>* const\n        volume_primitive_variables,\n    const Element<Dim>& element, const Mesh<Dim>& volume_mesh,\n    const DirectionalIdMap<Dim, Mesh<Dim - 1>>& mortar_meshes,\n    const DirectionalIdMap<Dim, MortarInfo<Dim>>& mortar_infos,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&\n        moving_mesh_map,\n    const std::optional<tnsr::I<DataVector, Dim>>& volume_mesh_velocity,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& volume_inverse_jacobian,\n    const PackageDataVolumeArgs&... package_data_volume_args) {\n  using variables_tags = typename System::variables_tag::tags_list;\n  using flux_variables = typename System::flux_variables;\n  using fluxes_tags = db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                       tmpl::size_t<Dim>, Frame::Inertial>;\n  using temporary_tags_for_face =\n      typename BoundaryCorrection::dg_package_data_temporary_tags;\n  using primitive_tags_for_face = typename detail::get_primitive_vars<\n      System::has_primitive_and_conservative_vars>::\n      template f<BoundaryCorrection>;\n  using mortar_tags_list = typename BoundaryCorrection::dg_package_field_tags;\n\n  using dg_package_data_projected_tags =\n      tmpl::append<variables_tags, fluxes_tags, temporary_tags_for_face,\n                   primitive_tags_for_face>;\n  using FieldsOnFace = Variables<tmpl::remove_duplicates<tmpl::push_back<\n      tmpl::append<dg_package_data_projected_tags,\n                   detail::inverse_spatial_metric_tag<System>>,\n      detail::OneOverNormalVectorMagnitude, detail::NormalVector<Dim>>>>;\n  FieldsOnFace fields_on_face{};\n  std::optional<tnsr::I<DataVector, Dim>> face_mesh_velocity{};\n  for (const auto& [direction, neighbors_in_direction] : element.neighbors()) {\n    const Mesh<Dim - 1> face_mesh =\n        volume_mesh.slice_away(direction.dimension());\n\n    // The face_temporaries buffer is guaranteed to be big enough because we\n    // allocated it in ComputeTimeDerivative with the max number of grid points\n    // over all faces. We still check anyways in Debug mode to be safe\n    ASSERT(face_temporaries->size() >=\n               FieldsOnFace::number_of_independent_components *\n                   face_mesh.number_of_grid_points(),\n           \"The buffer for computing fields on faces which was allocated in \"\n           \"ComputeTimeDerivative is not large enough. It's size is \"\n               << face_temporaries->size() << \", but needs to be at least \"\n               << FieldsOnFace::number_of_independent_components *\n                      face_mesh.number_of_grid_points());\n\n    fields_on_face.set_data_ref(face_temporaries->data(),\n                                FieldsOnFace::number_of_independent_components *\n                                    face_mesh.number_of_grid_points());\n\n    // We may not need to bring the volume fluxes or temporaries to the\n    // boundary since that depends on the specific boundary correction we\n    // are using. Silence compilers warnings about them being unused.\n    (void)volume_fluxes;\n    (void)volume_temporaries;\n\n    // This does the following:\n    //\n    // 1. Use a helper function to get data onto the faces. Done either by\n    //    slicing (Gauss-Lobatto points) or interpolation (Gauss points).\n    //    This is done using the `project_contiguous_data_to_boundary` and\n    //    `project_tensors_to_boundary` functions.\n    //\n    // 2. Invoke the boundary correction to get the packaged data. Note\n    //    that this is done on the *face* and NOT the mortar.\n    //\n    // 3. Project the packaged data onto the DG mortars (these might need\n    //    re-projection onto subcell mortars later).\n\n    // Perform step 1\n    ::dg::project_contiguous_data_to_boundary(make_not_null(&fields_on_face),\n                                              volume_evolved_vars, volume_mesh,\n                                              direction);\n    if constexpr (tmpl::size<fluxes_tags>::value != 0) {\n      ::dg::project_contiguous_data_to_boundary(make_not_null(&fields_on_face),\n                                                volume_fluxes, volume_mesh,\n                                                direction);\n    }\n    if constexpr (tmpl::size<tmpl::append<\n                      temporary_tags_for_face,\n                      detail::inverse_spatial_metric_tag<System>>>::value !=\n                  0) {\n      ::dg::project_tensors_to_boundary<tmpl::append<\n          temporary_tags_for_face, detail::inverse_spatial_metric_tag<System>>>(\n          make_not_null(&fields_on_face), volume_temporaries, volume_mesh,\n          direction);\n    }\n    if constexpr (System::has_primitive_and_conservative_vars and\n                  tmpl::size<primitive_tags_for_face>::value != 0) {\n      ASSERT(volume_primitive_variables != nullptr,\n             \"The volume primitive variables are not set even though the \"\n             \"system has primitive variables.\");\n      ::dg::project_tensors_to_boundary<primitive_tags_for_face>(\n          make_not_null(&fields_on_face), *volume_primitive_variables,\n          volume_mesh, direction);\n    } else {\n      (void)volume_primitive_variables;\n    }\n    if (volume_mesh_velocity.has_value()) {\n      if (not face_mesh_velocity.has_value() or\n          (*face_mesh_velocity)[0].size() !=\n              face_mesh.number_of_grid_points()) {\n        face_mesh_velocity =\n            tnsr::I<DataVector, Dim>{face_mesh.number_of_grid_points()};\n      }\n      ::dg::project_tensor_to_boundary(make_not_null(&*face_mesh_velocity),\n                                       *volume_mesh_velocity, volume_mesh,\n                                       direction);\n    }\n\n    // Normalize the normal vectors. We cache the unit normal covector For\n    // flat geometry and static meshes.\n    const bool mesh_is_moving = not moving_mesh_map.is_identity();\n    if (auto& normal_covector_quantity =\n            normal_covector_and_magnitude_ptr->at(direction);\n        detail::has_inverse_spatial_metric_tag_v<System> or mesh_is_moving or\n        not normal_covector_quantity.has_value()) {\n      if (not normal_covector_quantity.has_value()) {\n        normal_covector_quantity =\n            Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                 evolution::dg::Tags::NormalCovector<Dim>>>{\n                fields_on_face.number_of_grid_points()};\n      }\n      tnsr::i<DataVector, Dim> volume_unnormalized_normal_covector{};\n\n      for (size_t inertial_index = 0; inertial_index < Dim; ++inertial_index) {\n        volume_unnormalized_normal_covector.get(inertial_index)\n            .set_data_ref(const_cast<double*>(  // NOLINT\n                              volume_inverse_jacobian\n                                  .get(direction.dimension(), inertial_index)\n                                  .data()),\n                          volume_mesh.number_of_grid_points());\n      }\n      ::dg::project_tensor_to_boundary(\n          make_not_null(&get<evolution::dg::Tags::NormalCovector<Dim>>(\n              *normal_covector_quantity)),\n          volume_unnormalized_normal_covector, volume_mesh, direction);\n\n      if (direction.side() == Side::Lower) {\n        for (auto& normal_covector_component :\n             get<evolution::dg::Tags::NormalCovector<Dim>>(\n                 *normal_covector_quantity)) {\n          normal_covector_component *= -1.0;\n        }\n      }\n\n      detail::unit_normal_vector_and_covector_and_magnitude_impl<System>(\n          make_not_null(&get<evolution::dg::Tags::MagnitudeOfNormal>(\n              *normal_covector_quantity)),\n          make_not_null(&get<evolution::dg::Tags::NormalCovector<Dim>>(\n              *normal_covector_quantity)),\n          make_not_null(&fields_on_face),\n          get<evolution::dg::Tags::NormalCovector<Dim>>(\n              *normal_covector_quantity));\n    }\n\n    // Perform step 2\n    ASSERT(normal_covector_and_magnitude_ptr->at(direction).has_value(),\n           \"The magnitude of the normal vector and the unit normal \"\n           \"covector have not been computed, even though they should \"\n           \"have been. Direction: \"\n               << direction);\n\n    const size_t total_face_size =\n        face_mesh.number_of_grid_points() *\n        Variables<mortar_tags_list>::number_of_independent_components;\n    Variables<mortar_tags_list> packaged_data{};\n\n    // This is the case where we only have one neighbor in this direction, so we\n    // may or may not have to do any projection. If we don't have to do\n    // projection, then we can use the local_mortar_data itself to calculate the\n    // dg_package_data. However, if we need to project, then we hae to use the\n    // packaged_data_buffer that was passed in.\n    if (neighbors_in_direction.size() == 1) {\n      const auto& neighbor = *neighbors_in_direction.begin();\n      const auto mortar_id = DirectionalId<Dim>{direction, neighbor};\n      const auto& mortar_mesh = mortar_meshes.at(mortar_id);\n      const auto& mortar_size = mortar_infos.at(mortar_id).mortar_size();\n\n      // Have to use packaged_data_buffer\n      if (Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)) {\n        // The face mesh will be assigned below along with ensuring the size of\n        // the mortar data is correct\n        packaged_data.set_data_ref(packaged_data_buffer->data(),\n                                   total_face_size);\n      } else {\n        // Can use the local_mortar_data\n        auto& local_mortar = mortar_data_ptr->at(mortar_id).local();\n        local_mortar.face_mesh = face_mesh;\n        local_mortar.mortar_mesh = mortar_mesh;\n        // If this is the first time, initialize the data. If we don't do this,\n        // then the DataVector will be non-owning which we don't want\n        if (UNLIKELY(not local_mortar.mortar_data.has_value())) {\n          local_mortar.mortar_data = DataVector{};\n        }\n\n        DataVector& local_mortar_data = local_mortar.mortar_data.value();\n\n        // Do a destructive resize to account for potential p-refinement\n        local_mortar_data.destructive_resize(total_face_size);\n\n        packaged_data.set_data_ref(local_mortar_data.data(),\n                                   local_mortar_data.size());\n      }\n    } else {\n      // In this case, we have multiple neighbors in this direction so all will\n      // need to project their data which means we use the\n      // packaged_data_buffer to calculate the dg_package_data\n      packaged_data.set_data_ref(packaged_data_buffer->data(), total_face_size);\n    }\n\n    detail::dg_package_data<System>(\n        make_not_null(&packaged_data), boundary_correction, fields_on_face,\n        get<evolution::dg::Tags::NormalCovector<Dim>>(\n            *normal_covector_and_magnitude_ptr->at(direction)),\n        face_mesh_velocity, dg_package_data_projected_tags{},\n        package_data_volume_args...);\n\n    // Perform step 3\n    // This will only do something if\n    //  a) we have multiple neighbors in this direction\n    // or\n    //  b) the one (and only) neighbor in this direction needed projection\n    for (const auto& neighbor : neighbors_in_direction) {\n      const DirectionalId<Dim> mortar_id{direction, neighbor};\n      const auto& mortar_mesh = mortar_meshes.at(mortar_id);\n      const auto& mortar_size = mortar_infos.at(mortar_id).mortar_size();\n\n      if (Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)) {\n        auto& local_mortar = mortar_data_ptr->at(mortar_id).local();\n        local_mortar.face_mesh = face_mesh;\n        local_mortar.mortar_mesh = mortar_mesh;\n        // If this is the first time, initialize the data. If we don't do this,\n        // then the DataVector will be non-owning which we don't want\n        if (UNLIKELY(not local_mortar.mortar_data.has_value())) {\n          local_mortar.mortar_data = DataVector{};\n        }\n\n        DataVector& local_mortar_data = local_mortar.mortar_data.value();\n\n        // Do a destructive resize to account for potential p-refinement\n        local_mortar_data.destructive_resize(\n            mortar_mesh.number_of_grid_points() *\n            Variables<mortar_tags_list>::number_of_independent_components);\n\n        Variables<mortar_tags_list> projected_packaged_data{\n            local_mortar_data.data(), local_mortar_data.size()};\n        ::dg::project_to_mortar(make_not_null(&projected_packaged_data),\n                                packaged_data, face_mesh, mortar_mesh,\n                                mortar_size);\n      }\n    }\n  }\n}\n\ntemplate <typename System, size_t Dim, typename BoundaryCorrection,\n          typename DbTagsList, typename... PackageDataVolumeTags>\nvoid internal_mortar_data(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    const gsl::not_null<gsl::span<double>*> face_temporaries,\n    const gsl::not_null<gsl::span<double>*> packaged_data_buffer,\n    const BoundaryCorrection& boundary_correction,\n    const Variables<typename System::variables_tag::tags_list>\n        evolved_variables,\n    const Variables<\n        db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,\n                         tmpl::size_t<Dim>, Frame::Inertial>>& volume_fluxes,\n    const Variables<\n        typename System::compute_volume_time_derivative_terms::temporary_tags>&\n        temporaries,\n    const Variables<get_primitive_vars_tags_from_system<System>>* const\n        primitive_vars,\n    tmpl::list<PackageDataVolumeTags...> /*meta*/) {\n  db::mutate<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>,\n             evolution::dg::Tags::MortarData<Dim>>(\n      [&boundary_correction, &face_temporaries, &packaged_data_buffer,\n       &element = db::get<domain::Tags::Element<Dim>>(*box), &evolved_variables,\n       &logical_to_inertial_inverse_jacobian =\n           db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                 Frame::Inertial>>(*box),\n       &mesh = db::get<domain::Tags::Mesh<Dim>>(*box),\n       &mesh_velocity = db::get<domain::Tags::MeshVelocity<Dim>>(*box),\n       &mortar_meshes = db::get<Tags::MortarMesh<Dim>>(*box),\n       &mortar_infos = db::get<Tags::MortarInfo<Dim>>(*box),\n       &moving_mesh_map = db::get<domain::CoordinateMaps::Tags::CoordinateMap<\n           Dim, Frame::Grid, Frame::Inertial>>(*box),\n       &primitive_vars, &temporaries, &volume_fluxes](\n          const auto normal_covector_and_magnitude_ptr,\n          const auto mortar_data_ptr, const auto&... package_data_volume_args) {\n        detail::internal_mortar_data_impl<System>(\n            normal_covector_and_magnitude_ptr, mortar_data_ptr,\n            face_temporaries, packaged_data_buffer, boundary_correction,\n            evolved_variables, volume_fluxes, temporaries, primitive_vars,\n            element, mesh, mortar_meshes, mortar_infos, moving_mesh_map,\n            mesh_velocity, logical_to_inertial_inverse_jacobian,\n            package_data_volume_args...);\n      },\n      box, db::get<PackageDataVolumeTags>(*box)...);\n}\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::Actions::detail {\nstruct OneOverNormalVectorMagnitude : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct NormalVector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n\n/*!\n * \\brief Computes the normal covector, magnitude of the unnormalized normal\n * covector, and in curved spacetimes the normal vector.\n *\n * The `fields_on_face` argument is used as a temporary buffer via the\n * `OneOverNormalVectorMagnitude` tag and also used to return the normalized\n * normal vector via the `NormalVector<Dim>` tag. The `fields_on_face` argument\n * also serves as the input for the inverse spatial metric when the spacetime is\n * curved. The `fields_on_face` argument must have tags `NormalVector<Dim>` and\n * `OneOverNormalVectorMagnitude`. If the system specifies\n * `System::inverse_spatial_metric_tag` then this tag must also be in the\n * Variables.\n */\ntemplate <typename System, size_t Dim, typename FieldsOnFaceTags>\nvoid unit_normal_vector_and_covector_and_magnitude_impl(\n    const gsl::not_null<Scalar<DataVector>*> face_normal_magnitude,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        unit_normal_covector,\n    const gsl::not_null<Variables<FieldsOnFaceTags>*> fields_on_face,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>&\n        unnormalized_normal_covector) {\n  if constexpr (has_inverse_spatial_metric_tag_v<System>) {\n    using inverse_spatial_metric_tag =\n        typename System::inverse_spatial_metric_tag;\n    auto& normal_vector = get<NormalVector<Dim>>(*fields_on_face);\n    const auto& inverse_spatial_metric =\n        get<inverse_spatial_metric_tag>(*fields_on_face);\n    // Compute unnormalized normal vector\n    for (size_t i = 0; i < Dim; ++i) {\n      normal_vector.get(i) = inverse_spatial_metric.get(i, 0) *\n                             get<0>(unnormalized_normal_covector);\n      for (size_t j = 1; j < Dim; ++j) {\n        normal_vector.get(i) += inverse_spatial_metric.get(i, j) *\n                                unnormalized_normal_covector.get(j);\n      }\n    }\n    // Perform normalization\n    dot_product(face_normal_magnitude, normal_vector,\n                unnormalized_normal_covector);\n    get(*face_normal_magnitude) = sqrt(get(*face_normal_magnitude));\n    auto& one_over_normal_vector_magnitude =\n        get<OneOverNormalVectorMagnitude>(*fields_on_face);\n    get(one_over_normal_vector_magnitude) = 1.0 / get(*face_normal_magnitude);\n    for (size_t i = 0; i < Dim; ++i) {\n      unit_normal_covector->get(i) = unnormalized_normal_covector.get(i) *\n                                     get(one_over_normal_vector_magnitude);\n      normal_vector.get(i) *= get(one_over_normal_vector_magnitude);\n    }\n  } else {\n    magnitude(face_normal_magnitude, unnormalized_normal_covector);\n    auto& one_over_normal_vector_magnitude =\n        get<OneOverNormalVectorMagnitude>(*fields_on_face);\n    get(one_over_normal_vector_magnitude) = 1.0 / get(*face_normal_magnitude);\n    for (size_t i = 0; i < Dim; ++i) {\n      unit_normal_covector->get(i) = unnormalized_normal_covector.get(i) *\n                                     get(one_over_normal_vector_magnitude);\n    }\n  }\n}\n\n/*!\n * \\brief Computes the normal vector, covector, and their magnitude on the face\n * in the given direction.\n *\n * The normal covector and its magnitude are returned using the\n * `normal_covector_quantities` function argument. The `fields_on_face` argument\n * is used as a temporary buffer via the `OneOverNormalVectorMagnitude` tag and\n * also used to return the normalized normal vector via the `NormalVector<Dim>`\n * tag. The `fields_on_face` argument also serves as the input for the inverse\n * spatial metric when the spacetime is curved. The `fields_on_face` argument\n * must have tags `NormalVector<Dim>` and `OneOverNormalVectorMagnitude`. If the\n * system specifies `System::inverse_spatial_metric_tag` then this tag must also\n * be in the Variables.\n */\ntemplate <typename System, size_t Dim, typename FieldsOnFaceTags>\nvoid unit_normal_vector_and_covector_and_magnitude(\n    const gsl::not_null<\n        DirectionMap<Dim, std::optional<Variables<tmpl::list<\n                              evolution::dg::Tags::MagnitudeOfNormal,\n                              evolution::dg::Tags::NormalCovector<Dim>>>>>*>\n        normal_covector_quantities,\n    const gsl::not_null<Variables<FieldsOnFaceTags>*> fields_on_face,\n    const Direction<Dim>& direction,\n    const std::unordered_map<Direction<Dim>, tnsr::i<DataVector, Dim>>&\n        unnormalized_normal_covectors,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&\n        moving_mesh_map) {\n  const bool mesh_is_moving = not moving_mesh_map.is_identity();\n  const auto& unnormalized_normal_covector =\n      unnormalized_normal_covectors.at(direction);\n\n  if (auto& normal_covector_quantity =\n          normal_covector_quantities->at(direction);\n      detail::has_inverse_spatial_metric_tag_v<System> or mesh_is_moving or\n      not normal_covector_quantity.has_value()) {\n    if (not normal_covector_quantity.has_value()) {\n      normal_covector_quantity =\n          Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                               evolution::dg::Tags::NormalCovector<Dim>>>{\n              fields_on_face->number_of_grid_points()};\n    }\n    detail::unit_normal_vector_and_covector_and_magnitude_impl<System>(\n        make_not_null(&get<evolution::dg::Tags::MagnitudeOfNormal>(\n            *normal_covector_quantity)),\n        make_not_null(&get<evolution::dg::Tags::NormalCovector<Dim>>(\n            *normal_covector_quantity)),\n        fields_on_face, unnormalized_normal_covector);\n  }\n}\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::Actions::detail {\n// Helper function to get parameter packs so we can forward `Tensor`s instead\n// of `Variables` to the boundary corrections. Returns the maximum absolute\n// char speed on the face, which can be used for setting or monitoring the CFL\n// without having to compute the speeds for each dimension in the volume.\n// Whether using only the face speeds is accurate enough to guarantee\n// stability is yet to be determined. However, if the CFL condition is\n// violated on the boundaries we are definitely in trouble, so it can at least\n// be used as a cheap diagnostic.\ntemplate <typename System, typename BoundaryCorrection,\n          typename... PackagedFieldTags, typename... ProjectedFieldTags,\n          typename... ProjectedFieldTagsForCorrection, size_t Dim,\n          typename... VolumeArgs>\ndouble dg_package_data(\n    const gsl::not_null<Variables<tmpl::list<PackagedFieldTags...>>*>\n        packaged_data,\n    const BoundaryCorrection& boundary_correction,\n    const Variables<tmpl::list<ProjectedFieldTags...>>& projected_fields,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    tmpl::list<ProjectedFieldTagsForCorrection...> /*meta*/,\n    const VolumeArgs&... volume_args) {\n  std::optional<Scalar<DataVector>> normal_dot_mesh_velocity{};\n  if (mesh_velocity.has_value()) {\n    normal_dot_mesh_velocity =\n        dot_product(*mesh_velocity, unit_normal_covector);\n  }\n\n  if constexpr (evolution::dg::Actions::detail::\n                    has_inverse_spatial_metric_tag_v<System>) {\n    return boundary_correction.dg_package_data(\n        make_not_null(&get<PackagedFieldTags>(*packaged_data))...,\n        get<ProjectedFieldTagsForCorrection>(projected_fields)...,\n        unit_normal_covector,\n        get<evolution::dg::Actions::detail::NormalVector<Dim>>(\n            projected_fields),\n        mesh_velocity, normal_dot_mesh_velocity, volume_args...);\n  } else {\n    return boundary_correction.dg_package_data(\n        make_not_null(&get<PackagedFieldTags>(*packaged_data))...,\n        get<ProjectedFieldTagsForCorrection>(projected_fields)...,\n        unit_normal_covector, mesh_velocity, normal_dot_mesh_velocity,\n        volume_args...);\n  }\n}\n\ntemplate <typename System, typename BoundaryCorrection,\n          typename... PackagedFieldTags, typename... ProjectedFieldTags,\n          typename... ProjectedFieldTagsForCorrection, size_t Dim,\n          typename... VolumeTags>\ndouble dg_package_data(\n    const gsl::not_null<Variables<tmpl::list<PackagedFieldTags...>>*>\n        packaged_data,\n    const BoundaryCorrection& boundary_correction,\n    const Variables<tmpl::list<ProjectedFieldTags...>>& projected_fields,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    const db::Access& box, tmpl::list<VolumeTags...> /*meta*/,\n    tmpl::list<ProjectedFieldTagsForCorrection...> /*meta*/) {\n  return dg_package_data<System>(\n      packaged_data, boundary_correction, projected_fields,\n      unit_normal_covector, mesh_velocity,\n      tmpl::list<ProjectedFieldTagsForCorrection...>{},\n      db::get<VolumeTags>(box)...);\n}\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <ostream>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::Actions::detail {\n/*\n * Computes the volume terms for a discontinuous Galerkin scheme.\n *\n * The function does the following (in order):\n *\n * 1. Compute the partial derivatives of the `System::gradient_variables`.\n *\n *    The partial derivatives are needed in the nonconservative product terms\n *    of the evolution equations. Any variable whose evolution equation does\n *    not contain a flux must contain a nonconservative product and the\n *    variable must be listed in the `System::gradient_variables` type alias.\n *    The partial derivatives are also needed for adding the moving mesh terms\n *    to the equations that do not have a flux term.\n *\n * 2. The volume time derivatives are calculated from\n *    `System::compute_volume_time_derivative_terms`\n *\n *    The source terms and nonconservative products are contributed directly\n *    to the `dt_vars` arguments passed to the time derivative function, while\n *    the volume fluxes are computed into the `volume_fluxes` arguments. The\n *    divergence of the volume fluxes will be computed and added to the time\n *    derivatives later in the function.\n *\n * 3. If the mesh is moving the appropriate mesh velocity terms are added to\n *    the equations.\n *\n *    For equations with fluxes this means that \\f$-v^i_g u_\\alpha\\f$ is\n *    added to the fluxes and \\f$-u_\\alpha \\partial_i v^i_g\\f$ is added\n *    to the time derivatives. For equations without fluxes\n *    \\f$v^i\\partial_i u_\\alpha\\f$ is added to the time derivatives.\n *\n * 4. Compute flux divergence contribution and add it to the time derivatives.\n *\n *    Either the weak or strong form can be used. Currently only the strong\n *    form is coded, but adding the weak form is quite easy.\n *\n *    Note that the computation of the flux divergence and adding that to the\n *    time derivative must be done *after* the mesh velocity is subtracted\n *    from the fluxes.\n */\ntemplate <typename ComputeVolumeTimeDerivativeTerms, size_t Dim,\n          typename... TimeDerivativeArguments, typename... VariablesTags,\n          typename... PartialDerivTags, typename... FluxVariablesTags,\n          typename... TemporaryTags>\nvoid volume_terms(\n    const gsl::not_null<Variables<tmpl::list<::Tags::dt<VariablesTags>...>>*>\n        dt_vars_ptr,\n    [[maybe_unused]] const gsl::not_null<Variables<tmpl::list<::Tags::Flux<\n        FluxVariablesTags, tmpl::size_t<Dim>, Frame::Inertial>...>>*>\n        volume_fluxes,\n    [[maybe_unused]] const gsl::not_null<Variables<tmpl::list<::Tags::deriv<\n        PartialDerivTags, tmpl::size_t<Dim>, Frame::Inertial>...>>*>\n        partial_derivs,\n    [[maybe_unused]] const gsl::not_null<\n        Variables<tmpl::list<TemporaryTags...>>*>\n        temporaries,\n    [[maybe_unused]] const gsl::not_null<\n        Variables<tmpl::list<::Tags::div<::Tags::Flux<\n            FluxVariablesTags, tmpl::size_t<Dim>, Frame::Inertial>>...>>*>\n        div_fluxes,\n    const Variables<tmpl::list<VariablesTags...>>& evolved_vars,\n    const ::dg::Formulation dg_formulation, const Mesh<Dim>& mesh,\n    [[maybe_unused]] const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        inertial_coordinates,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>&\n        logical_to_inertial_inverse_jacobian,\n    [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    const std::optional<Scalar<DataVector>>& div_mesh_velocity,\n    const TimeDerivativeArguments&... time_derivative_args);\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdlib>\n#include <optional>\n#include <ostream>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/PassVariables.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/WeakDivergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::Actions::detail {\n/*\n * Computes the volume terms for a discontinuous Galerkin scheme.\n *\n * The function does the following (in order):\n *\n * 1. Compute the partial derivatives of the `System::gradient_variables`.\n *\n *    The partial derivatives are needed in the nonconservative product terms\n *    of the evolution equations. Any variable whose evolution equation does\n *    not contain a flux must contain a nonconservative product and the\n *    variable must be listed in the `System::gradient_variables` type alias.\n *    The partial derivatives are also needed for adding the moving mesh terms\n *    to the equations that do not have a flux term.\n *\n * 2. The volume time derivatives are calculated from\n *    `System::compute_volume_time_derivative_terms`\n *\n *    The source terms and nonconservative products are contributed directly\n *    to the `dt_vars` arguments passed to the time derivative function, while\n *    the volume fluxes are computed into the `volume_fluxes` arguments. The\n *    divergence of the volume fluxes will be computed and added to the time\n *    derivatives later in the function.\n *\n * 3. If the mesh is moving the appropriate mesh velocity terms are added to\n *    the equations.\n *\n *    For equations with fluxes this means that \\f$-v^i_g u_\\alpha\\f$ is\n *    added to the fluxes and \\f$-u_\\alpha \\partial_i v^i_g\\f$ is added\n *    to the time derivatives. For equations without fluxes\n *    \\f$v^i\\partial_i u_\\alpha\\f$ is added to the time derivatives.\n *\n * 4. Compute flux divergence contribution and add it to the time derivatives.\n *\n *    Either the weak or strong form can be used. Currently only the strong\n *    form is coded, but adding the weak form is quite easy.\n *\n *    Note that the computation of the flux divergence and adding that to the\n *    time derivative must be done *after* the mesh velocity is subtracted\n *    from the fluxes.\n */\ntemplate <typename ComputeVolumeTimeDerivativeTerms, size_t Dim,\n          typename... TimeDerivativeArguments, typename... VariablesTags,\n          typename... PartialDerivTags, typename... FluxVariablesTags,\n          typename... TemporaryTags>\nvoid volume_terms(\n    const gsl::not_null<Variables<tmpl::list<::Tags::dt<VariablesTags>...>>*>\n        dt_vars_ptr,\n    [[maybe_unused]] const gsl::not_null<Variables<tmpl::list<::Tags::Flux<\n        FluxVariablesTags, tmpl::size_t<Dim>, Frame::Inertial>...>>*>\n        volume_fluxes,\n    [[maybe_unused]] const gsl::not_null<Variables<tmpl::list<::Tags::deriv<\n        PartialDerivTags, tmpl::size_t<Dim>, Frame::Inertial>...>>*>\n        partial_derivs,\n    [[maybe_unused]] const gsl::not_null<\n        Variables<tmpl::list<TemporaryTags...>>*>\n        temporaries,\n    [[maybe_unused]] const gsl::not_null<\n        Variables<tmpl::list<::Tags::div<::Tags::Flux<\n            FluxVariablesTags, tmpl::size_t<Dim>, Frame::Inertial>>...>>*>\n        div_fluxes,\n    const Variables<tmpl::list<VariablesTags...>>& evolved_vars,\n    const ::dg::Formulation dg_formulation, const Mesh<Dim>& mesh,\n    [[maybe_unused]] const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        inertial_coordinates,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>&\n        logical_to_inertial_inverse_jacobian,\n    [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    const std::optional<Scalar<DataVector>>& div_mesh_velocity,\n    const TimeDerivativeArguments&... time_derivative_args) {\n  static constexpr bool has_partial_derivs = sizeof...(PartialDerivTags) != 0;\n  static constexpr bool has_fluxes = sizeof...(FluxVariablesTags) != 0;\n  static_assert(\n      has_fluxes or has_partial_derivs,\n      \"Must have either fluxes or partial derivatives in a \"\n      \"DG evolution scheme. This means the evolution system struct (usually in \"\n      \"Evolution/Systems/YourSystem/System.hpp) being used does not specify \"\n      \"any flux_variables or gradient_variables. Make sure the type aliases \"\n      \"are defined, and that at least one of them is a non-empty list of \"\n      \"tags.\");\n\n  using flux_variables = tmpl::list<FluxVariablesTags...>;\n\n  // Compute d_i u_\\alpha for nonconservative products\n  if constexpr (has_partial_derivs) {\n    partial_derivatives(partial_derivs, evolved_vars, mesh,\n                        logical_to_inertial_inverse_jacobian,\n                        inertial_coordinates);\n  }\n\n  // For now just zero dt_vars. If this is a performance bottle neck we\n  // can re-evaluate in the future.\n  dt_vars_ptr->initialize(mesh.number_of_grid_points(), 0.0);\n  evolution::dg::TimeDerivativeDecisions<Dim> time_derivative_decisions{};\n\n  // Compute volume du/dt and fluxes\n  if constexpr (std::is_base_of_v<evolution::PassVariables,\n                                  ComputeVolumeTimeDerivativeTerms>) {\n    if constexpr (sizeof...(FluxVariablesTags) != 0) {\n      time_derivative_decisions = ComputeVolumeTimeDerivativeTerms::apply(\n          dt_vars_ptr, volume_fluxes, temporaries,\n          get<::Tags::deriv<PartialDerivTags, tmpl::size_t<Dim>,\n                            Frame::Inertial>>(*partial_derivs)...,\n          time_derivative_args...);\n    } else {\n      time_derivative_decisions = ComputeVolumeTimeDerivativeTerms::apply(\n          dt_vars_ptr, temporaries,\n          get<::Tags::deriv<PartialDerivTags, tmpl::size_t<Dim>,\n                            Frame::Inertial>>(*partial_derivs)...,\n          time_derivative_args...);\n    }\n  } else {\n    time_derivative_decisions = ComputeVolumeTimeDerivativeTerms::apply(\n        make_not_null(&get<::Tags::dt<VariablesTags>>(*dt_vars_ptr))...,\n        make_not_null(&get<::Tags::Flux<FluxVariablesTags, tmpl::size_t<Dim>,\n                                        Frame::Inertial>>(*volume_fluxes))...,\n        make_not_null(&get<TemporaryTags>(*temporaries))...,\n        get<::Tags::deriv<PartialDerivTags, tmpl::size_t<Dim>,\n                          Frame::Inertial>>(*partial_derivs)...,\n        time_derivative_args...);\n  }\n\n  // Add volume terms for moving meshes\n  if (mesh_velocity.has_value()) {\n    if (not time_derivative_decisions.compute_flux_divergence) {\n      goto end_of_flux_mesh_velocity;  // NOLINT(cppcoreguidelines-avoid-goto)\n    }\n    tmpl::for_each<flux_variables>([&div_mesh_velocity, &dt_vars_ptr,\n                                    &evolved_vars, &mesh_velocity,\n                                    &volume_fluxes](auto tag_v) {\n      // Modify fluxes for moving mesh\n      using var_tag = typename decltype(tag_v)::type;\n      using flux_var_tag =\n          db::add_tag_prefix<::Tags::Flux, var_tag, tmpl::size_t<Dim>,\n                             Frame::Inertial>;\n      auto& flux_var = get<flux_var_tag>(*volume_fluxes);\n      // Loop over all independent components of flux_var\n      for (size_t flux_var_storage_index = 0;\n           flux_var_storage_index < flux_var.size(); ++flux_var_storage_index) {\n        // Get the flux variable's tensor index, e.g. (i,j) for a F^i of\n        // the spatial velocity (or some other spatial tensor).\n        const auto flux_var_tensor_index =\n            flux_var.get_tensor_index(flux_var_storage_index);\n        // Remove the first index from the flux tensor index, gets back\n        // (j)\n        const auto var_tensor_index =\n            all_but_specified_element_of(flux_var_tensor_index, 0);\n        // Set flux_index to (i)\n        const size_t flux_index = gsl::at(flux_var_tensor_index, 0);\n\n        // We now need to index flux(i,j) -= u(j) * v_g(i)\n        flux_var[flux_var_storage_index] -=\n            get<var_tag>(evolved_vars).get(var_tensor_index) *\n            mesh_velocity->get(flux_index);\n      }\n\n      // Modify time derivative (i.e. source terms) for moving mesh\n      auto& dt_var = get<::Tags::dt<var_tag>>(*dt_vars_ptr);\n      for (size_t dt_var_storage_index = 0;\n           dt_var_storage_index < dt_var.size(); ++dt_var_storage_index) {\n        // This is S -> S - u d_i v^i_g\n        dt_var[dt_var_storage_index] -=\n            get<var_tag>(evolved_vars)[dt_var_storage_index] *\n            get(*div_mesh_velocity);\n      }\n    });\n  end_of_flux_mesh_velocity:\n\n    // We add the mesh velocity to all equations that don't have flux terms.\n    // This doesn't need to be equal to the equations that have partial\n    // derivatives. For example, the scalar field evolution equation in\n    // first-order form does not have any partial derivatives but still needs\n    // the velocity term added. This is because the velocity term arises from\n    // transforming the time derivative.\n    using non_flux_tags =\n        tmpl::list_difference<tmpl::list<VariablesTags...>, flux_variables>;\n\n    tmpl::for_each<non_flux_tags>([&dt_vars_ptr, &mesh_velocity,\n                                   &partial_derivs](auto var_tag_v) {\n      using var_tag = typename decltype(var_tag_v)::type;\n      using dt_var_tag = ::Tags::dt<var_tag>;\n      using deriv_var_tag =\n          ::Tags::deriv<var_tag, tmpl::size_t<Dim>, Frame::Inertial>;\n\n      const auto& deriv_var = get<deriv_var_tag>(*partial_derivs);\n      auto& dt_var = get<dt_var_tag>(*dt_vars_ptr);\n\n      // Loop over all independent components of the derivative of the\n      // variable.\n      for (size_t deriv_var_storage_index = 0;\n           deriv_var_storage_index < deriv_var.size();\n           ++deriv_var_storage_index) {\n        // We grab the `deriv_tensor_index`, which would be e.g.\n        // `(i, a, b)`, so `(0, 2, 3)`\n        const auto deriv_var_tensor_index =\n            deriv_var.get_tensor_index(deriv_var_storage_index);\n        // Then we drop the derivative index (the first entry) to get\n        // `(a, b)` (or `(2, 3)`)\n        const auto dt_var_tensor_index =\n            all_but_specified_element_of(deriv_var_tensor_index, 0);\n        // Set `deriv_index` to `i` (or `0` in the example)\n        const size_t deriv_index = gsl::at(deriv_var_tensor_index, 0);\n        dt_var.get(dt_var_tensor_index) += mesh_velocity->get(deriv_index) *\n                                           deriv_var[deriv_var_storage_index];\n      }\n    });\n  }\n\n  // Add the flux divergence term to du_\\alpha/dt, which must be done\n  // after the corrections for the moving mesh are made.\n  if constexpr (has_fluxes) {\n    if (not time_derivative_decisions.compute_flux_divergence) {\n      goto end_of_flux_div_calculation;  // NOLINT(cppcoreguidelines-avoid-goto)\n    }\n    if (dg_formulation == ::dg::Formulation::StrongInertial) {\n      divergence(div_fluxes, *volume_fluxes, mesh,\n                 logical_to_inertial_inverse_jacobian);\n    } else if (dg_formulation == ::dg::Formulation::WeakInertial) {\n      // We should ideally not recompute the\n      // det_jac_times_inverse_jacobian for non-moving meshes.\n      if constexpr (Dim == 1) {\n        weak_divergence(div_fluxes, *volume_fluxes, mesh, {});\n      } else {\n        // The Jacobian should be computed as a compute tag\n        const auto jacobian =\n            determinant_and_inverse(logical_to_inertial_inverse_jacobian)\n                .second;\n        InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n            det_jac_times_inverse_jacobian{};\n        ::dg::metric_identity_det_jac_times_inv_jac(\n            make_not_null(&det_jac_times_inverse_jacobian), mesh,\n            inertial_coordinates, jacobian);\n        weak_divergence(div_fluxes, *volume_fluxes, mesh,\n                        det_jac_times_inverse_jacobian);\n      }\n      ASSERT(det_inverse_jacobian != nullptr,\n             \"The determinant of the inverse Jacobian shouldn't be nullptr \"\n             \"when using the weak form.\");\n      (*div_fluxes) *= get(*det_inverse_jacobian);\n    } else {\n      // Note: when adding support for other DG formulations here, also check\n      // that the implementations of `BoundaryCorrection` classes support the\n      // added formulation.\n      ERROR(\"Unsupported DG formulation: \"\n            << dg_formulation\n            << \"\\nSupported formulations are: StrongInertial, WeakInertial.\");\n    }\n    tmpl::for_each<flux_variables>(\n        [&dg_formulation, &dt_vars_ptr, &div_fluxes](auto var_tag_v) {\n          using var_tag = typename decltype(var_tag_v)::type;\n          auto& dt_var = get<::Tags::dt<var_tag>>(*dt_vars_ptr);\n          const auto& div_flux = get<::Tags::div<\n              ::Tags::Flux<var_tag, tmpl::size_t<Dim>, Frame::Inertial>>>(\n              *div_fluxes);\n          if (dg_formulation == ::dg::Formulation::StrongInertial) {\n            for (size_t storage_index = 0; storage_index < dt_var.size();\n                 ++storage_index) {\n              dt_var[storage_index] -= div_flux[storage_index];\n            }\n          } else {\n            for (size_t storage_index = 0; storage_index < dt_var.size();\n                 ++storage_index) {\n              dt_var[storage_index] += div_flux[storage_index];\n            }\n          }\n        });\n  end_of_flux_div_calculation:\n    (void)5;  // null statement required after label at end of block until C++23\n  } else {\n    (void)dg_formulation;\n  }\n}\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/AtomicInboxBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/AtomicInboxBoundaryData.hpp\"\n\n#include <atomic>\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/System/Abort.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t Dim>\nAtomicInboxBoundaryData<Dim>::AtomicInboxBoundaryData(\n    AtomicInboxBoundaryData<Dim>&& rhs) noexcept {\n  if (rhs.missing_messages.load(std::memory_order_acquire) !=\n          rhs.passed_missing_messages - rhs.processed_messages or\n      not messages.empty()) {\n    sys::abort(\n        \"You cannot move an AtomicInboxBoundaryData with non-zero message \"\n        \"count.\");\n  }\n  for (size_t i = 0; i < rhs.boundary_data_in_directions.size(); ++i) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)\n    if (not rhs.boundary_data_in_directions[i].empty()) {\n      sys::abort(\n          \"You cannot move an AtomicInboxBoundaryData with data in \"\n          \"boundary_data_in_directions.\");\n    }\n  }\n  missing_messages.store(0, std::memory_order_release);\n  processed_messages = 0;\n  passed_missing_messages = 0;\n}\n\ntemplate <size_t Dim>\nsize_t AtomicInboxBoundaryData<Dim>::index(\n    const DirectionalId<Dim>& neighbor_directional_id) {\n  if constexpr (Dim == 1) {\n    // Note that in 1d:\n    //     pow<Dim>(2) * neighbor_directional_id.direction.dimension() == 0\n    //     pow<Dim - 1>(2) == 1\n    // so:\n    //   pow<Dim>(2) * neighbor_directional_id.direction.dimension() +\n    //   pow<Dim - 1>(2) *\n    //     (neighbor_directional_id.direction.side() == Side::Lower ? 0 : 1)\n    //\n    // is just:\n    //    (neighbor_directional_id.direction.side() == Side::Lower ? 0 : 1)\n    return neighbor_directional_id.direction().side() == Side::Lower ? 0_st\n                                                                     : 1_st;\n  } else {\n    size_t result = 0;\n    for (size_t i = 0, j = 0; i < Dim; ++i) {\n      if (i == neighbor_directional_id.direction().dimension()) {\n        continue;\n      }\n      result = result | (neighbor_directional_id.id().segment_id(i).index() & 1)\n                            << j;\n      ++j;\n    }\n    return pow<Dim>(2_st) * neighbor_directional_id.direction().dimension() +\n           pow<Dim - 1>(2_st) *\n               (neighbor_directional_id.direction().side() == Side::Lower\n                    ? 0_st\n                    : 1_st) +\n           result;\n  }\n}\n\ntemplate <size_t Dim>\nvoid AtomicInboxBoundaryData<Dim>::collect_messages() {\n  for (auto& spsc_in_direction : boundary_data_in_directions) {\n    auto* data_in_direction = spsc_in_direction.front();\n    while (data_in_direction != nullptr) {\n      const auto& time_step_id = get<0>(*data_in_direction);\n      auto& data = get<1>(*data_in_direction);\n      auto& directional_element_id = get<2>(*data_in_direction);\n      auto& current_inbox = messages[time_step_id];\n      current_inbox.emplace_back(std::move(directional_element_id),\n                                 std::move(data));\n\n      spsc_in_direction.pop();\n      data_in_direction = spsc_in_direction.front();\n      ++processed_messages;\n    }  // while data_in_direction != nullptr\n  }  //   for spsc_in_direction : boundary_data_in_directions\n}\n\ntemplate <size_t Dim>\nbool AtomicInboxBoundaryData<Dim>::set_missing_messages(const size_t count) {\n  const int old_missing_messages = missing_messages.fetch_add(\n      static_cast<int>(count) + processed_messages - passed_missing_messages,\n      std::memory_order_acq_rel);\n  const int queued_messages =\n      passed_missing_messages - old_missing_messages - processed_messages;\n  processed_messages = 0;\n  passed_missing_messages = static_cast<int>(count);\n  return queued_messages >= static_cast<int>(count);\n}\n\ntemplate <size_t Dim>\nvoid AtomicInboxBoundaryData<Dim>::pup(PUP::er& p) {\n  const auto missing = missing_messages.load(std::memory_order_acquire);\n  if (UNLIKELY(missing > 0 or\n               missing != passed_missing_messages - processed_messages or\n               not messages.empty())) {\n    ERROR(\n        \"Can only serialize AtomicInboxBoundaryData if there are no messages. \"\n        \"We need to be very careful about serializing atomics since \"\n        \"serialization requires strong synchronization like a lock.\");\n  }\n  for (size_t i = 0; i < boundary_data_in_directions.size(); ++i) {\n    if (UNLIKELY(not gsl::at(boundary_data_in_directions, i).empty())) {\n      ERROR(\n          \"We can only serialize empty StaticSpscQueues but the queue in \"\n          \"element \"\n          << i << \" is not empty.\");\n    }\n  }\n  if (p.isUnpacking()) {\n    // We only need to preserve the combination of these quantities\n    // representing the number of queued messages, which we checked\n    // during serialization was zero, and the fact that we are not\n    // waiting on messages.  Exactly how it was split between the\n    // fields is unimportant.\n    missing_messages.store(0, std::memory_order_release);\n    processed_messages = 0;\n    passed_missing_messages = 0;\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class AtomicInboxBoundaryData<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/AtomicInboxBoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is tested in Test_InboxTags.cpp\n\n#pragma once\n\n#include <atomic>\n#include <boost/container/small_vector.hpp>\n#include <cstddef>\n#include <map>\n#include <tuple>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/MaxNumberOfNeighbors.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Parallel/StaticSpscQueue.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace evolution::dg {\n/*!\n * \\brief Holds the data in the different directions for the nodegroup\n * `DgElementArray` implementation.\n *\n * The reason for this class is to reduce contention between cores and to\n * allow the use of a Single-Producer-Single-Consumer (SPSC) queue instead of\n * an MPMC queue. This has significant performance improvements since it\n * drastically reduces contention.\n *\n * \\warning Only `AtomicInboxBoundaryData` with zero messages can be\n * move constructed or serialized.  This is necessary in order to be\n * able to serialize a\n * `std::unordered_map<Key,AtomicInboxBoundaryData>`.\n */\ntemplate <size_t Dim>\nstruct AtomicInboxBoundaryData {\n  using stored_type = evolution::dg::BoundaryData<Dim>;\n  AtomicInboxBoundaryData() = default;\n  AtomicInboxBoundaryData(const AtomicInboxBoundaryData&) = delete;\n  AtomicInboxBoundaryData& operator=(const AtomicInboxBoundaryData&) = delete;\n  AtomicInboxBoundaryData(AtomicInboxBoundaryData&& rhs) noexcept;\n  AtomicInboxBoundaryData& operator=(AtomicInboxBoundaryData&&) noexcept =\n      delete;\n  ~AtomicInboxBoundaryData() = default;\n\n  /*!\n   * Computes the 1d index into the `boundary_data_in_directions` array\n   * for a specific `directional_id` that has been re-oriented using the\n   * `OrientationMap` to be put in the same block frame as the element that is\n   * receiving the data (i.e. that whose inbox this is being inserted into).\n   *\n   * The hash is computed as\n   * \\f{align}{\n   * 2^D d + 2^{D-1} s + e\n   * \\f}\n   * where \\f$D\\f$ is the number of spatial dimensions, \\f$d\\f$ is the logical\n   * dimension of the direction to the neighbor from the element whose inbox\n   * this is, \\f$s\\f$ is the side in the logical dimension \\f$d\\f$ with a value\n   * of 1 for upper and 0 for lower, and \\f$e\\f$ is a hash of the index of the\n   * `SegmentId`'s of the neighbor's `ElementId` for the dimensions other than\n   * \\f$d\\f$. In particular: for \\f$d=1\\f$, \\f$e\\f$ is 0 (1) if\n   * the `SegmentId` index along the face is even (odd); and for \\f$d = 3\\f$\n   * \\f$e\\f$ is 0 (1, 2, 3) if the `SegmentId` indices along the face are both\n   * even (lower dim odd, higher dim odd, both dims odd). The element segment\n   * hash is computed as the logical `and` of the `SegmentID`'s index in that\n   * direction, left shifted by which direction on the face it is.\n   */\n  static size_t index(const DirectionalId<Dim>& directional_id);\n\n  /*!\n   * Moves data from the SPSC queues into the `messages` map.\n   */\n  void collect_messages();\n\n  /*!\n   * Set a lower bound on the number of messages required for the\n   * algorithm to make progress since the most recent call to\n   * `collect_messages`.  After that number of new messages have been\n   * received, `BoundaryCorrectionAndGhostCellsInbox` will restart\n   * the algorithm.\n   *\n   * \\return whether enough messages have been received.\n   */\n  bool set_missing_messages(size_t count);\n\n  void pup(PUP::er& p);\n\n  // We use 20 entries in the SPSC under the assumption that each neighbor\n  // will never insert more than 20 entries before the element uses\n  // them. While in practice a smaller buffer could be used, this is to\n  // safeguard against future features.\n  std::array<Parallel::StaticSpscQueue<\n                 std::tuple<::TimeStepId, stored_type, DirectionalId<Dim>>, 20>,\n             maximum_number_of_neighbors(Dim)>\n      boundary_data_in_directions{};\n  std::map<TimeStepId, boost::container::small_vector<\n                           std::pair<DirectionalId<Dim>, stored_type>,\n                           maximum_number_of_neighbors(Dim)>>\n      messages{};\n  std::atomic_int missing_messages{};\n\n  // The number of messages in the SPSC queues is\n  // passed_missing_messages - missing_messages - processed_messages.\n  // The various manipulations change these fields as follows:\n  //\n  // New message => --missing_messages (by insert_into_inbox)\n  // collect_messages => processed_messages += num unqueued messages\n  // set_missing_messages(count) =>\n  //   missing_messages += count + processed_messages - passed_missing_messages\n  //   processed_messages = 0\n  //   passed_missing_messages = count\n  //\n  // The processed_messages field exists (rather than just including\n  // it in missing_messages) so that missing_messages is positive if\n  // and only if the element is blocked on messages in this queue.\n  int processed_messages{};\n  int passed_missing_messages{};\n};\n\n/// \\brief `std::true` if `T` is a `AtomicInboxBoundaryData`\ntemplate <typename T>\nstruct is_atomic_inbox_boundary_data : std::false_type {};\n\n/// \\cond\ntemplate <size_t Dim>\nstruct is_atomic_inbox_boundary_data<AtomicInboxBoundaryData<Dim>>\n    : std::true_type {};\n/// \\endcond\n\n/// \\brief `true` if `T` is a `AtomicInboxBoundaryData`\ntemplate <typename T>\nconstexpr size_t is_atomic_inbox_boundary_data_v =\n    is_atomic_inbox_boundary_data<T>::value;\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/BackgroundGrVars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n\nnamespace evolution::dg {\n\nnamespace {\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(flux_spacetime_variables_tag)\n\nstruct EmptyStruct {\n  using tags_list = tmpl::list<>;\n};\n}  // namespace\n\n/*!\n * \\brief Allocate or assign background general relativity quantities needed\n * for evolution systems run on a curved spacetime without solving Einstein\n * equations (e.g. ValenciaDivclean, ForceFree).\n *\n * \\warning This mutator assumes that the GR analytic data or solution\n * specifying background spacetime metric is time-independent.\n *\n */\ntemplate <typename System, typename Metavariables>\nstruct BackgroundGrVars : tt::ConformsTo<db::protocols::Mutator> {\n  static constexpr size_t volume_dim = System::volume_dim;\n\n  // Collect all the GR quantities used in the templated evolution system\n  using gr_variables_tag = ::Tags::Variables<tmpl::remove_duplicates<\n      tmpl::append<typename System::spacetime_variables_tag::tags_list,\n                   typename get_flux_spacetime_variables_tag_or_default_t<\n                       System, EmptyStruct>::tags_list,\n                   tmpl::list<typename System::inverse_spatial_metric_tag>>>>;\n\n  using GrVars = typename gr_variables_tag::type;\n\n  using argument_tags =\n      tmpl::list<::Tags::Time, domain::Tags::Domain<volume_dim>,\n                 domain::Tags::Coordinates<volume_dim, Frame::Inertial>,\n                 domain::Tags::Mesh<volume_dim>,\n                 domain::Tags::Element<volume_dim>,\n                 evolution::initial_data::Tags::InitialData>;\n\n  using return_tags = tmpl::list<gr_variables_tag>;\n\n  template <typename T>\n  static void apply(\n      const gsl::not_null<GrVars*> background_gr_vars, const double time,\n      const Domain<volume_dim>& domain,\n      const tnsr::I<DataVector, volume_dim, Frame::Inertial>& inertial_coords,\n      const Mesh<volume_dim>& mesh, const Element<volume_dim>& element,\n      const T& solution_or_data) {\n    const size_t num_grid_pts = mesh.number_of_grid_points();\n\n    if (background_gr_vars->number_of_grid_points() != 0) {  // Evolution phase\n      ASSERT((*background_gr_vars).number_of_grid_points() == num_grid_pts,\n             \"The size of GR variables (\"\n                 << (*background_gr_vars).number_of_grid_points()\n                 << \") is not equal to the number of grid points (\"\n                 << mesh.number_of_grid_points() << \").\");\n\n      // Check if the mesh is actually moving i.e. block coordinate map is\n      // time-dependent. If not, we can skip the evaluation of GR variables\n      // since they may stay with their values assigned at the initialization\n      // phase.\n      const auto& element_id = element.id();\n      const size_t block_id = element_id.block_id();\n      const Block<volume_dim>& block = domain.blocks()[block_id];\n      if (block.is_time_dependent()) {\n        impl(background_gr_vars, time, inertial_coords, solution_or_data);\n      }\n    } else {\n      // Initialization phase\n      (*background_gr_vars).initialize(num_grid_pts);\n      impl(background_gr_vars, time, inertial_coords, solution_or_data);\n    }\n  }\n\n private:\n  template <typename T>\n  static void impl(\n      const gsl::not_null<GrVars*> background_gr_vars, const double time,\n      const tnsr::I<DataVector, volume_dim, Frame::Inertial>& inertial_coords,\n      const T& solution_or_data) {\n    using derived_classes =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 evolution::initial_data::InitialData>;\n    call_with_dynamic_type<void, derived_classes>(\n        &solution_or_data, [&background_gr_vars, &inertial_coords,\n                            &time](const auto* const solution_or_data_ptr) {\n          (*background_gr_vars)\n              .assign_subset(evolution::Initialization::initial_data(\n                  *solution_or_data_ptr, inertial_coords, time,\n                  typename GrVars::tags_list{}));\n        });\n  }\n};\n\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/BoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Evolution/DiscontinuousGalerkin/InterpolatedBoundaryData.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t Dim>\nvoid BoundaryData<Dim>::pup(PUP::er& p) {\n  p | volume_mesh;\n  p | volume_mesh_ghost_cell_data;\n  p | boundary_correction_mesh;\n  p | ghost_cell_data;\n  p | boundary_correction_data;\n  p | validity_range;\n  p | tci_status;\n  p | integration_order;\n  p | interpolated_boundary_data;\n}\n\ntemplate <size_t Dim>\nbool operator==(const BoundaryData<Dim>& lhs, const BoundaryData<Dim>& rhs) {\n  return lhs.volume_mesh == rhs.volume_mesh and\n         lhs.volume_mesh_ghost_cell_data == rhs.volume_mesh_ghost_cell_data and\n         lhs.boundary_correction_mesh == rhs.boundary_correction_mesh and\n         lhs.ghost_cell_data == rhs.ghost_cell_data and\n         lhs.boundary_correction_data == rhs.boundary_correction_data and\n         lhs.validity_range == rhs.validity_range and\n         lhs.tci_status == rhs.tci_status and\n         lhs.integration_order == rhs.integration_order and\n         lhs.interpolated_boundary_data == rhs.interpolated_boundary_data;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const BoundaryData<Dim>& lhs, const BoundaryData<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os, const BoundaryData<Dim>& value) {\n  using ::operator<<;\n  return os << \"Volume mesh: \" << value.volume_mesh << '\\n'\n            << \"Ghost mesh: \" << value.volume_mesh_ghost_cell_data << '\\n'\n            << \"Boundary correction mesh: \" << value.boundary_correction_mesh\n            << '\\n'\n            << \"Ghost cell data: \" << value.ghost_cell_data << '\\n'\n            << \"Boundary correction data: \" << value.boundary_correction_data\n            << '\\n'\n            << \"Validy range: \" << value.validity_range << '\\n'\n            << \"TCI status: \" << value.tci_status << '\\n'\n            << \"Integration order: \" << value.integration_order << '\\n'\n            << \"Interpolated boundary data: \"\n            << value.interpolated_boundary_data;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                        \\\n  template class BoundaryData<DIM(data)>;                             \\\n  template std::ostream& operator<<(                                  \\\n      std::ostream& os, const BoundaryData<DIM(data)>& BoundaryData); \\\n  template bool operator==(const BoundaryData<DIM(data)>& lhs,        \\\n                           const BoundaryData<DIM(data)>& rhs);       \\\n  template bool operator!=(const BoundaryData<DIM(data)>& lhs,        \\\n                           const BoundaryData<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/BoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InterpolatedBoundaryData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\nnamespace evolution::dg {\n/*!\n * \\brief The data communicated between neighber elements.\n *\n * The stored data consists of the following:\n *\n * 1. the volume mesh of the element.\n * 2. the volume mesh corresponding to the ghost cell data. This allows eliding\n *    projection when all neighboring elements are doing DG.\n * 3. the mortar mesh of the data on the mortar\n * 4. the variables at the ghost zone cells for finite difference/volume\n *    reconstruction\n * 5. the data on the mortar needed for computing the boundary corrections (e.g.\n *    fluxes, characteristic speeds, conserved variables)\n * 6. the TimeStepId beyond which the boundary terms are no longer valid, when\n *    using local time stepping.\n * 7. the troubled cell indicator status used for determining halos around\n *    troubled cells.\n * 8. the integration order of the time-stepper\n * 9. the InterpolatedBoundaryData sent by a non-conforming Element that\n *    interpolates its data to a subset of the points of the Element receiving\n *    this BoundaryData\n */\ntemplate <size_t Dim>\nstruct BoundaryData {\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  Mesh<Dim> volume_mesh{};\n  std::optional<Mesh<Dim>> volume_mesh_ghost_cell_data{};\n  std::optional<Mesh<Dim - 1>> boundary_correction_mesh{};\n  std::optional<DataVector> ghost_cell_data{};\n  std::optional<DataVector> boundary_correction_data{};\n  ::TimeStepId validity_range{};\n  int tci_status{};\n  size_t integration_order{std::numeric_limits<size_t>::max()};\n  std::optional<InterpolatedBoundaryData<Dim>> interpolated_boundary_data{};\n};\n\ntemplate <size_t Dim>\nbool operator==(const BoundaryData<Dim>& lhs, const BoundaryData<Dim>& rhs);\ntemplate <size_t Dim>\nbool operator!=(const BoundaryData<Dim>& lhs, const BoundaryData<Dim>& rhs);\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os, const BoundaryData<Dim>& value);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AtomicInboxBoundaryData.hpp\n  BackgroundGrVars.hpp\n  BoundaryData.hpp\n  CleanMortarHistory.hpp\n  DgElementArray.hpp\n  InboxBoundaryData.hpp\n  InboxTags.hpp\n  InterfaceDataPolicy.hpp\n  InterpolatedBoundaryData.hpp\n  MortarData.hpp\n  MortarDataHolder.hpp\n  MortarInfo.hpp\n  MortarTags.hpp\n  NormalVectorTags.hpp\n  TimeDerivativeDecisions.hpp\n  TimeSteppingPolicy.hpp\n  UsingSubcell.hpp\n  )\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AtomicInboxBoundaryData.cpp\n  BoundaryData.cpp\n  CleanMortarHistory.cpp\n  InboxBoundaryData.cpp\n  InboxTags.cpp\n  InstantiateBoundaryHistory.cpp\n  InterfaceDataPolicy.cpp\n  InterpolatedBoundaryData.cpp\n  MortarData.cpp\n  MortarDataHolder.cpp\n  MortarInfo.cpp\n  TimeSteppingPolicy.cpp\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Initialization)\nadd_subdirectory(Limiters)\nadd_subdirectory(Messages)\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/CleanMortarHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t Dim>\nvoid CleanMortarHistory<Dim>::apply(\n    const gsl::not_null<DirectionalIdMap<\n        Dim, TimeSteppers::BoundaryHistory<::evolution::dg::MortarData<Dim>,\n                                           ::evolution::dg::MortarData<Dim>,\n                                           DataVector>>*>\n        history,\n    const LtsTimeStepper& time_stepper,\n    const DirectionalIdMap<Dim, MortarInfo<Dim>>& mortar_info) {\n  for (auto& [mortar_id, hist] : *history) {\n    const auto time_stepping_policy =\n        mortar_info.at(mortar_id).time_stepping_policy();\n    switch (time_stepping_policy) {\n      case TimeSteppingPolicy::EqualRate:\n        break;\n      case TimeSteppingPolicy::Conservative:\n        time_stepper.clean_boundary_history(make_not_null(&hist));\n        break;\n      default:\n        ERROR(\"Unhandled TimeSteppingPolicy: \" << time_stepping_policy);\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template struct CleanMortarHistory<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\nclass LtsTimeStepper;\nnamespace Tags {\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace TimeSteppers {\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nclass BoundaryHistory;\n}  // namespace TimeSteppers\nnamespace evolution::dg {\ntemplate <size_t Dim>\nstruct MortarData;\ntemplate <size_t Dim>\nclass MortarInfo;\n}  // namespace evolution::dg\nnamespace evolution::dg::Tags {\ntemplate <size_t Dim>\nstruct MortarDataHistory;\ntemplate <size_t Dim>\nstruct MortarInfo;\n}  // namespace evolution::dg::Tags\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace evolution::dg {\n/// Mutator to remove old entries from the mortar histories in a\n/// local-time-stepping DG evolution.\ntemplate <size_t Dim>\nstruct CleanMortarHistory {\n  using return_tags = tmpl::list<evolution::dg::Tags::MortarDataHistory<Dim>>;\n  using argument_tags =\n      tmpl::list<::Tags::TimeStepper<LtsTimeStepper>, Tags::MortarInfo<Dim>>;\n\n  static void apply(\n      gsl::not_null<DirectionalIdMap<\n          Dim, TimeSteppers::BoundaryHistory<::evolution::dg::MortarData<Dim>,\n                                             ::evolution::dg::MortarData<Dim>,\n                                             DataVector>>*>\n          history,\n      const LtsTimeStepper& time_stepper,\n      const DirectionalIdMap<Dim, MortarInfo<Dim>>& mortar_info);\n};\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/DgElementArray.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementDistribution.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Tags/ElementDistribution.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/ArrayCollection/CreateElementsUsingDistribution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Tags/Parallelization.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\brief The parallel component responsible for managing the DG elements that\n * compose the computational domain\n *\n * This parallel component will perform the actions specified by the\n * `PhaseDepActionList`.\n *\n * An unordered set of `size_t`s can be passed to the `allocate_array`\n * function which represents physical processors to avoid placing\n * elements on.\n */\ntemplate <class Metavariables, class PhaseDepActionList>\nstruct DgElementArray {\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = PhaseDepActionList;\n  using array_index = ElementId<volume_dim>;\n\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<volume_dim>,\n                                             domain::Tags::ElementDistribution>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_items,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n          array_allocation_items = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {});\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const bool force = false) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<DgElementArray>(local_cache)\n        .start_phase(next_phase, force);\n  }\n};\n\ntemplate <class Metavariables, class PhaseDepActionList>\nvoid DgElementArray<Metavariables, PhaseDepActionList>::allocate_array(\n    Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n    const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n        initialization_items,\n    const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n    /*array_allocation_items*/,\n    const std::unordered_set<size_t>& procs_to_ignore) {\n  auto& local_cache = *Parallel::local_branch(global_cache);\n  auto& dg_element_array =\n      Parallel::get_parallel_component<DgElementArray>(local_cache);\n\n  const auto& domain =\n      Parallel::get<domain::Tags::Domain<volume_dim>>(local_cache);\n  const auto& initial_refinement_levels =\n      get<domain::Tags::InitialRefinementLevels<volume_dim>>(\n          initialization_items);\n  const auto& initial_extents =\n      get<domain::Tags::InitialExtents<volume_dim>>(initialization_items);\n  const auto i1_basis = Spectral::Basis::Legendre;\n  const auto& i1_quadrature =\n      get<evolution::dg::Tags::Quadrature>(initialization_items);\n  const std::optional<domain::ElementWeight>& element_weight =\n      Parallel::get<domain::Tags::ElementDistribution>(local_cache);\n\n  const size_t number_of_procs = Parallel::number_of_procs<size_t>(local_cache);\n  const size_t number_of_nodes = Parallel::number_of_nodes<size_t>(local_cache);\n  const size_t num_of_procs_to_use = number_of_procs - procs_to_ignore.size();\n\n  const auto& blocks = domain.blocks();\n\n  Parallel::create_elements_using_distribution(\n      [&dg_element_array, &global_cache, &initialization_items](\n          const ElementId<volume_dim>& element_id, const size_t target_proc,\n          const size_t /*target_node*/) {\n        dg_element_array(element_id)\n            .insert(global_cache, initialization_items, target_proc);\n      },\n      element_weight, blocks, initial_extents, initial_refinement_levels,\n      i1_basis, i1_quadrature,\n\n      procs_to_ignore, number_of_procs, number_of_nodes, num_of_procs_to_use,\n      local_cache, true);\n  dg_element_array.doneInserting();\n}\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InboxBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/InboxBoundaryData.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupBoost.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t Dim>\nbool InboxBoundaryData<Dim>::empty() const {\n  return messages.empty();\n}\n\ntemplate <size_t Dim>\nvoid InboxBoundaryData<Dim>::collect_messages() {\n  missing_messages = 0;\n}\n\ntemplate <size_t Dim>\nbool InboxBoundaryData<Dim>::set_missing_messages(const size_t count) {\n  // In normal use, missing_messages should be zero when this is\n  // called, but it is convenient to allow nonzero values in the test.\n  missing_messages += static_cast<int>(count);\n  return missing_messages <= 0;\n}\n\ntemplate <size_t Dim>\nvoid InboxBoundaryData<Dim>::pup(PUP::er& p) {\n  p | messages;\n  p | missing_messages;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template struct InboxBoundaryData<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InboxBoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is tested in Test_InboxTags.cpp\n\n#pragma once\n\n#include <boost/container/small_vector.hpp>\n#include <cstddef>\n#include <map>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/MaxNumberOfNeighbors.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace evolution::dg {\n/// Class wrapping a map and mirroring the AtomicInboxBoundaryData\n/// interface so that code accessing the inbox doesn't need to care\n/// which implementation is in use.\ntemplate <size_t Dim>\nstruct InboxBoundaryData {\n  using mapped_type = boost::container::small_vector<\n      std::pair<DirectionalId<Dim>, evolution::dg::BoundaryData<Dim>>,\n      maximum_number_of_neighbors(Dim)>;\n\n  std::map<TimeStepId, mapped_type> messages;\n\n  /// Number of messages needed to restart the algorithm.  This should\n  /// be decremented whenever a new message is added to the inbox.\n  int missing_messages = 0;\n\n  bool empty() const;\n\n  /// In AtomicInboxBoundaryData, this moves elements from the\n  /// threadsafe structure to the `messages` field.  This class stores\n  /// messages in the `messages` field directly, so this method just\n  /// zeros the missing message count.\n  void collect_messages();\n\n  /// Set a lower bound on the number of messages required for the\n  /// algorithm to make progress since the most recent call to\n  /// `collect_messages`.  After that number of new messages have been\n  /// received, `BoundaryCorrectionAndGhostCellsInbox` will restart\n  /// the algorithm.\n  ///\n  /// \\return whether enough messages have been received.\n  bool set_missing_messages(size_t count);\n\n  void pup(PUP::er& p);\n};\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InboxTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n\n#include <cstddef>\n#include <iomanip>\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::Tags {\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nbool BoundaryCorrectionAndGhostCellsInbox<Dim, UseNodegroupDgElements>::\n    insert_into_inbox(\n        const gsl::not_null<type_spsc*> inbox, const temporal_id& time_step_id,\n        std::pair<DirectionalId<Dim>, evolution::dg::BoundaryData<Dim>> data) {\n  const DirectionalId<Dim>& neighbor_id = data.first;\n  // Note: This assumes the neighbor_id is oriented into our (the element\n  // whose inbox this is) frame.\n  const size_t neighbor_index = inbox->index(neighbor_id);\n  if (UNLIKELY(not gsl::at(inbox->boundary_data_in_directions, neighbor_index)\n                       .try_emplace(time_step_id, std::move(data.second),\n                                    std::move(data.first)))) {\n    ERROR(\n        \"Failed to emplace data into inbox. neighbor_id: (\"\n        << neighbor_id.direction() << ',' << neighbor_id.id()\n        << \") at TimeStepID: \" << time_step_id << \" the size of the inbox is \"\n        << gsl::at(inbox->boundary_data_in_directions, neighbor_index).size());\n  }\n  // Notes:\n  // 1. fetch_sub does a post-decrement.\n  // 2. We need thread synchronization here, so doing relaxed_order would be a\n  //    bug.\n  return inbox->missing_messages.fetch_sub(1, std::memory_order_acq_rel) == 1;\n}\n\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nbool BoundaryCorrectionAndGhostCellsInbox<Dim, UseNodegroupDgElements>::\n    insert_into_inbox(\n        const gsl::not_null<type_map*> inbox, const temporal_id& time_step_id,\n        std::pair<DirectionalId<Dim>, evolution::dg::BoundaryData<Dim>> data) {\n  auto& current_inbox = inbox->messages[time_step_id];\n  current_inbox.emplace_back(std::move(data));\n  --inbox->missing_messages;\n  return inbox->missing_messages == 0;\n}\n\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nstd::string\nBoundaryCorrectionAndGhostCellsInbox<Dim, UseNodegroupDgElements>::output_inbox(\n    const type_spsc& inbox, const size_t padding_size) {\n  std::stringstream ss{};\n  const std::string pad(padding_size, ' ');\n  ss << std::scientific << std::setprecision(16);\n  ss << pad << \"BoundaryCorrectionAndGhostCellInbox:\\n\";\n  ss << pad\n     << \"Warning: Printing atomic state is not possible in general so data \"\n        \"printed is limited.\\n\";\n  for (size_t i = 0; i < inbox.boundary_data_in_directions.size(); ++i) {\n    const auto& data_in_direction =\n        gsl::at(inbox.boundary_data_in_directions, i);\n    ss << pad << \"Id: \"\n       << \"Approximate size: \" << data_in_direction.size() << \"\\n\";\n  }\n\n  return ss.str();\n}\n\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nstd::string\nBoundaryCorrectionAndGhostCellsInbox<Dim, UseNodegroupDgElements>::output_inbox(\n    const type_map& inbox, const size_t padding_size) {\n  std::stringstream ss{};\n  const std::string pad(padding_size, ' ');\n  ss << std::scientific << std::setprecision(16);\n  ss << pad << \"BoundaryCorrectionAndGhostCellInbox:\\n\";\n\n  for (const auto& [current_time_step_id, hash_map] : inbox.messages) {\n    ss << pad << \" Current time: \" << current_time_step_id << \"\\n\";\n    // We only care about the next time because that's important for deadlock\n    // detection. The data itself isn't super important\n    for (const auto& [key, boundary_data] : hash_map) {\n      ss << pad << \"  Key: \" << key\n         << \", next time: \" << boundary_data.validity_range << \"\\n\";\n    }\n  }\n\n  return ss.str();\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define USE_NODEGROUP_DG_ELEMENTS(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                            \\\n  template struct BoundaryCorrectionAndGhostCellsInbox< \\\n      DIM(data), USE_NODEGROUP_DG_ELEMENTS(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (true, false))\n\n#undef INSTANTIATE\n#undef USE_NODEGROUP_DG_ELEMENTS\n#undef DIM\n}  // namespace evolution::dg::Tags\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InboxTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/AtomicInboxBoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxBoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::Tags {\n/*!\n * \\brief The inbox tag for boundary correction communication and DG-subcell\n * ghost zone cells.\n *\n * The stored data consists of the following:\n *\n * 1. the volume mesh of the element.\n * 2. the volume mesh corresponding to the ghost cell data. This allows eliding\n *    projection when all neighboring elements are doing DG.\n * 3. the mortar mesh of the data on the mortar\n * 4. the variables at the ghost zone cells for finite difference/volume\n *    reconstruction\n * 5. the data on the mortar needed for computing the boundary corrections (e.g.\n *    fluxes, characteristic speeds, conserved variables)\n * 6. the TimeStepId beyond which the boundary terms are no longer valid, when\n *    using local time stepping.\n * 7. the troublade cell indicator status using for determining halos around\n *    troubled cells.\n * 8. the integration order of the time-stepper.\n * 9. the InterpolatedBoundaryData sent by a non-conforming Element that\n *    interpolates its data to a subset of the points of the Element receiving\n *    this BoundaryData\n *\n * The TimeStepId is the neighboring element's next time step. When using local\n * time stepping, the neighbor's boundary data is valid up until this time,\n * which may include multiple local time steps. By receiving and storing the\n * neighbor time step, the local element knows whether or not it should remove\n * boundary data and expect new data to be sent from the neighbor.\n *\n * The ghost cell data will be valid whenever a DG-subcell scheme is being used.\n * Whenever a DG-subcell scheme is being used, elements using DG and not FD/FV\n * always send both the ghost cells and boundary correction data together.\n * Elements using FD/FV send the ghost cells first followed by the boundary\n * correction data once the element has received all neighbor ghost cell data.\n * Note that the second send/receive only modifies the flux and the TimeStepId\n * used for the flux validity range.\n *\n * When only a DG scheme (not a DG-subcell scheme) is used the ghost cell data\n * will never be valid.\n *\n * In the DG-subcell scheme this tag is used both for communicating the ghost\n * cell data needed for the FD/FV reconstruction step and the data needed for\n * the boundary corrections.\n * - For an element using DG, both ghost cells and boundary corrections are\n *   sent using a single communication. After receiving all neighbor\n *   boundary corrections the element can finish computing the time step.\n *   The ghost cell data from neighbors is unused.\n * - For an element using FD/FV, first the ghost cells are sent. Once all\n *   neighboring ghost cell data is received, reconstruction is done and the\n *   boundary terms are computed and sent to the neighbors. After receiving all\n *   neighbor boundary corrections the element can finish computing the time\n *   step.\n * - Whether or not an extra communication is needed when an element switches\n *   from DG to FD/FV depends on how exactly the decision to switch is\n *   implemented. If the volume terms are integrated and verified to be\n *   valid before a DG element sends ghost cell and boundary data then no\n *   additional communication is needed when switching from DG to FD/FV. In this\n *   case a second check of the data that includes the boundary correction needs\n *   to be done. If the second check determines a switch from DG to FD/FV is\n *   needed, we can continue to use the DG fluxes since the evolution in the\n *   small was valid, thus avoiding an additional communication. However, to\n *   fully guarantee physical realizability a second communication or evolving\n *   the neighboring ghost cells needs to be done. We have not yet decided how\n *   to deal with the possible need for an additional communication since it\n *   also depends on whether or not we decide to couple to Voronoi instead of\n *   just Neumann neighbors.\n * - The data for the inbox tags is erased after the boundary correction is\n *   complete and the solution has been verified to be valid at the new time\n *   step. The ghost cells could be invalidated immediately after\n *   reconstruction, thus using the ghost cell data after reconstruction is\n *   complete is considered undefined behavior. That is, we make no guarantee as\n *   to whether or not it will work.\n * - The reason for minimizing the number of communications rather than having a\n *   more uniform implementation between DG and FD/FV is that it is the number\n *   of communications that adds the most overhead, not the size of each\n *   communication. Thus, one large communication is cheaper than several small\n *   communications.\n *\n * #### DG Element Nodegroup Support\n * If you are using the `DgElementCollection` then you must set\n * `UseNodegroupDgElements` to `true`. The actions that use this tag check\n * that the parallel component and the `UseNodegroupDgElements` is consistent.\n */\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nstruct BoundaryCorrectionAndGhostCellsInbox {\n  using stored_type = evolution::dg::BoundaryData<Dim>;\n\n public:\n  using temporal_id = TimeStepId;\n  // Used by array implementation\n  using type_map = evolution::dg::InboxBoundaryData<Dim>;\n\n  // Used by nodegroup implementation\n  using type_spsc = evolution::dg::AtomicInboxBoundaryData<Dim>;\n\n  // The actual type being used.\n  using type = tmpl::conditional_t<UseNodegroupDgElements, type_spsc, type_map>;\n  using value_type = type;\n\n  static bool insert_into_inbox(\n      gsl::not_null<type_spsc*> inbox, const temporal_id& time_step_id,\n      std::pair<DirectionalId<Dim>, evolution::dg::BoundaryData<Dim>> data);\n\n  static bool insert_into_inbox(\n      gsl::not_null<type_map*> inbox, const temporal_id& time_step_id,\n      std::pair<DirectionalId<Dim>, evolution::dg::BoundaryData<Dim>> data);\n\n  static std::string output_inbox(const type_spsc& inbox, size_t padding_size);\n\n  static std::string output_inbox(const type_map& inbox, size_t padding_size);\n};\n\n/*!\n * \\brief The inbox tag for boundary correction communication and DG-subcell\n * ghost zone cells using a `BoundaryMessage` object\n *\n * To see what is stored within a `BoundaryMessage`, see its documentation.\n *\n * This inbox tag is very similar to `BoundaryCorrectionAndGhostCellsInbox` in\n * that it stores subcell/DG data sent from neighboring elements. To see exactly\n * when data is stored and how it's used, see the docs for\n * `BoundaryCorrectionAndGhostCellsInbox`. This inbox tag is different than\n * `BoundaryCorrectionAndGhostCellsInbox` in that it only takes a pointer to a\n * `BoundaryMessage` as an argument to `insert_into_inbox` and stores a\n * `std::unique_ptr<BoundaryMessage>` inside the inbox.\n *\n * This inbox tag is meant to be used to avoid unnecessary copies between\n * elements on the same node which share a block of memory. If two elements\n * aren't on the same node, then a copy/send is done regardless.\n *\n * \\warning The `boundary_message` argument to `insert_into_inbox()` will be\n * invalid after the function is called because a `std::unique_ptr` now controls\n * the memory. Calling a method on the `boundary_message` pointer after the\n * `insert_into_inbox()` function is called can result in undefined behaviour.\n */\ntemplate <size_t Dim>\nstruct BoundaryMessageInbox {\n  using stored_type = std::unique_ptr<BoundaryMessage<Dim>>;\n\n public:\n  using temporal_id = TimeStepId;\n  using type = std::map<TimeStepId, DirectionalIdMap<Dim, stored_type>>;\n  using message_type = BoundaryMessage<Dim>;\n\n  template <typename Inbox>\n  static bool insert_into_inbox(const gsl::not_null<Inbox*> inbox,\n                                BoundaryMessage<Dim>* boundary_message) {\n    const auto& time_step_id = boundary_message->current_time_step_id;\n    auto& current_inbox = (*inbox)[time_step_id];\n\n    const auto key = DirectionalId<Dim>{boundary_message->neighbor_direction,\n                                        boundary_message->element_id};\n\n    // Once we insert boundary_message into the unique_ptr we cannot use\n    // boundary_message anymore because it is invalidated. The unique_ptr now\n    // owns the memory.\n    if (not current_inbox\n                .insert(std::pair{key, std::unique_ptr<BoundaryMessage<Dim>>(\n                                           boundary_message)})\n                .second) {\n      ERROR(\"Failed to insert data to receive at instance '\"\n            << time_step_id << \"' with tag 'BoundaryMessageInbox'.\\n\");\n    }\n    return true;\n  }\n};\n}  // namespace evolution::dg::Tags\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Initialization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Mortars.hpp\n  QuadratureTag.hpp\n  )\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Mortars.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Initialization/Mortars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/FaceType.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InterfaceDataPolicy.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace evolution::dg::Initialization {\nnamespace detail {\n\ntemplate <size_t Dim>\n::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>> empty_mortar_data(\n    const Element<Dim>& element) {\n  ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>> mortar_data{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    const domain::FaceType face_type = element.face_types().at(direction);\n    switch (face_type) {\n      // NOLINTNEXTLINE(bugprone-branch-clone)\n      case (domain::FaceType::Uninitialized):\n        [[fallthrough]];\n      case (domain::FaceType::External):\n        [[fallthrough]];\n      case (domain::FaceType::Topological): {\n        const std::string message =\n            MakeString{} << \"Cannot have FaceType \" << face_type\n                         << \" in direction \" << direction\n                         << \" in which there are neighboring elements.\";\n        ERROR(message);\n      }\n      // NOLINTNEXTLINE(bugprone-branch-clone)\n      case (domain::FaceType::ConformingAligned):\n        [[fallthrough]];\n      case (domain::FaceType::ConformingUnaligned):\n        [[fallthrough]];\n      case (domain::FaceType::SingleNonconforming): {\n        for (const auto& neighbor : neighbors) {\n          const DirectionalId<Dim> mortar_id{direction, neighbor};\n          mortar_data.emplace(mortar_id, MortarDataHolder<Dim>{});\n        }\n        break;\n      }\n      case (domain::FaceType::MultipleNonconforming): {\n        // Although there are multiple neighbors, we only create a single\n        // mortar labeled by the host ElementId.  This is done because\n        // the data from all neighbors will be combined onto a single mortar\n        // as it makes no sense to have multiple mortars between\n        // non-conforming Elements\n        const DirectionalId<Dim> mortar_id{direction, element.id()};\n        mortar_data.emplace(mortar_id, MortarDataHolder<Dim>{});\n        break;\n      }\n      default:\n        ERROR(\"Invalid face type\" << face_type);\n    }\n  }\n  return mortar_data;\n}\n\ntemplate <size_t Dim>\n::dg::MortarMap<Dim, MortarInfo<Dim>> mortar_infos(\n    const Domain<Dim>& domain, const Element<Dim>& element,\n    const Mesh<Dim>& volume_mesh,\n    const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh,\n    const bool local_time_stepping) {\n  ::dg::MortarMap<Dim, MortarInfo<Dim>> result{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    const domain::FaceType face_type = element.face_types().at(direction);\n    switch (face_type) {\n      // NOLINTNEXTLINE(bugprone-branch-clone)\n      case (domain::FaceType::Uninitialized):\n        [[fallthrough]];\n      case (domain::FaceType::External):\n        [[fallthrough]];\n      case (domain::FaceType::Topological): {\n        const std::string message =\n            MakeString{} << \"Cannot have FaceType \" << face_type\n                         << \" in direction \" << direction\n                         << \" in which there are neighboring elements.\";\n        ERROR(message);\n      }\n      case (domain::FaceType::ConformingAligned):\n        [[fallthrough]];\n      case (domain::FaceType::ConformingUnaligned):\n        for (const auto& neighbor : neighbors) {\n          const DirectionalId<Dim> mortar_id{direction, neighbor};\n          const auto& neighbor_orientation = neighbors.orientation(neighbor);\n          result.emplace(\n              mortar_id,\n              MortarInfo<Dim>{\n                  {.mortar_size = ::dg::mortar_size(element.id(), neighbor,\n                                                    direction.dimension(),\n                                                    neighbor_orientation),\n                   .interface_data_policy =\n                       neighbor_orientation.is_aligned()\n                           ? InterfaceDataPolicy::CopyProject\n                           : InterfaceDataPolicy::OrientCopyProject,\n                   .time_stepping_policy =\n                       local_time_stepping ? TimeSteppingPolicy::Conservative\n                                           : TimeSteppingPolicy::EqualRate}});\n        }\n        break;\n      case (domain::FaceType::SingleNonconforming):\n        if constexpr (Dim > 1) {\n          for (const auto& neighbor : neighbors) {\n            const DirectionalId<Dim> mortar_id{direction, neighbor};\n            const auto& neighbor_orientation = neighbors.orientation(neighbor);\n            result.emplace(\n                mortar_id,\n                MortarInfo<Dim>{\n                    {.interpolator =\n                         ::dg::MortarInterpolator<Dim>{\n                             element.id(), mortar_id, domain,\n                             volume_mesh.slice_away(direction.dimension()),\n                             neighbor_mesh.at(mortar_id).slice_away(\n                                 neighbor_orientation(direction.dimension()))},\n                     .interface_data_policy =\n                         InterfaceDataPolicy::NonconformingSelfInterpolates,\n                     .time_stepping_policy =\n                         local_time_stepping ? TimeSteppingPolicy::Conservative\n                                             : TimeSteppingPolicy::EqualRate}});\n          }\n        }\n        break;\n      case (domain::FaceType::MultipleNonconforming): {\n        // Although there are multiple neighbors, we only create a single\n        // mortar labeled by the host ElementId.  This is done because\n        // the data from all neighbors will be combined onto a single mortar\n        // as it makes no sense to have multiple mortars between\n        // non-conforming Elements\n        const DirectionalId<Dim> mortar_id{direction, element.id()};\n        result.emplace(\n            mortar_id,\n            MortarInfo<Dim>{\n                {.interface_data_policy =\n                     InterfaceDataPolicy::NonconformingNeighborInterpolates,\n                 .time_stepping_policy = local_time_stepping\n                                             ? TimeSteppingPolicy::Conservative\n                                             : TimeSteppingPolicy::EqualRate}});\n        break;\n      }\n      default:\n        ERROR(\"Invalid face type\" << face_type);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::tuple<::dg::MortarMap<Dim, Mesh<Dim - 1>>,\n           ::dg::MortarMap<Dim, TimeStepId>,\n           DirectionMap<Dim, std::optional<Variables<tmpl::list<\n                                 evolution::dg::Tags::MagnitudeOfNormal,\n                                 evolution::dg::Tags::NormalCovector<Dim>>>>>>\nmortars_apply_impl(const Element<Dim>& element,\n                   const TimeStepId& next_temporal_id,\n                   const Mesh<Dim>& volume_mesh,\n                   const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh) {\n  ::dg::MortarMap<Dim, Mesh<Dim - 1>> mortar_meshes{};\n  ::dg::MortarMap<Dim, TimeStepId> mortar_next_temporal_ids{};\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      normal_covector_quantities{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    normal_covector_quantities[direction] = std::nullopt;\n    const Mesh<Dim - 1> face_mesh =\n        volume_mesh.slice_away(direction.dimension());\n    const domain::FaceType face_type = element.face_types().at(direction);\n    switch (face_type) {\n      // NOLINTNEXTLINE(bugprone-branch-clone)\n      case (domain::FaceType::Uninitialized):\n        [[fallthrough]];\n      case (domain::FaceType::External):\n        [[fallthrough]];\n      case (domain::FaceType::Topological): {\n        const std::string message =\n            MakeString{} << \"Cannot have FaceType \" << face_type\n                         << \" in direction \" << direction\n                         << \" in which there are neighboring elements.\";\n        ERROR(message);\n      }\n      case (domain::FaceType::ConformingAligned):\n        [[fallthrough]];\n      case (domain::FaceType::ConformingUnaligned):\n        for (const auto& neighbor : neighbors) {\n          const DirectionalId<Dim> mortar_id{direction, neighbor};\n          mortar_meshes.emplace(\n              mortar_id, ::dg::mortar_mesh(\n                             face_mesh, neighbor_mesh.at(mortar_id).slice_away(\n                                            direction.dimension())));\n          // Since no communication needs to happen for boundary conditions\n          // the temporal id is not advanced on the boundary, so we only need\n          // to initialize it on internal boundaries\n          mortar_next_temporal_ids.insert({mortar_id, next_temporal_id});\n        }\n        break;\n      case (domain::FaceType::SingleNonconforming):\n        for (const auto& neighbor : neighbors) {\n          const DirectionalId<Dim> mortar_id{direction, neighbor};\n          mortar_meshes.emplace(mortar_id, face_mesh);\n          // Since no communication needs to happen for boundary conditions\n          // the temporal id is not advanced on the boundary, so we only need\n          // to initialize it on internal boundaries\n          mortar_next_temporal_ids.insert({mortar_id, next_temporal_id});\n        }\n        break;\n      case (domain::FaceType::MultipleNonconforming): {\n        // Although there are multiple neighbors, we only create a single\n        // mortar labeled by the host ElementId.  This is done because\n        // the data from all neighbors will be combined onto a single mortar\n        // as it makes no sense to have multiple mortars between\n        // non-conforming Elements\n        const DirectionalId<Dim> mortar_id{direction, element.id()};\n        mortar_meshes.emplace(mortar_id, face_mesh);\n        // Since no communication needs to happen for boundary conditions\n        // the temporal id is not advanced on the boundary, so we only need\n        // to initialize it on internal boundaries\n        mortar_next_temporal_ids.insert({mortar_id, next_temporal_id});\n        break;\n      }\n      default:\n        ERROR(\"Invalid face type\" << face_type);\n    }\n  }\n\n  for (const auto& direction : element.external_boundaries()) {\n    normal_covector_quantities[direction] = std::nullopt;\n  }\n\n  return {std::move(mortar_meshes), std::move(mortar_next_temporal_ids),\n          std::move(normal_covector_quantities)};\n}\n\ntemplate <size_t Dim>\nvoid h_refine_structure(\n    const gsl::not_null<\n        ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n        mortar_data,\n    const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_mesh,\n    const gsl::not_null<::dg::MortarMap<Dim, MortarInfo<Dim>>*> mortar_infos,\n    const gsl::not_null<::dg::MortarMap<Dim, TimeStepId>*>\n        mortar_next_temporal_id,\n    const gsl::not_null<\n        DirectionMap<Dim, std::optional<::Variables<tmpl::list<\n                              ::evolution::dg::Tags::MagnitudeOfNormal,\n                              ::evolution::dg::Tags::NormalCovector<Dim>>>>>*>\n        normal_covector_and_magnitude,\n    const Domain<Dim>& domain, const Mesh<Dim>& new_mesh,\n    const Element<Dim>& new_element,\n    const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh,\n    const TimeStepId& current_temporal_id, const bool local_time_stepping) {\n  *mortar_data = detail::empty_mortar_data(new_element);\n  *mortar_infos = detail::mortar_infos(domain, new_element, new_mesh,\n                                       neighbor_mesh, local_time_stepping);\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    (*normal_covector_and_magnitude)[direction] = std::nullopt;\n  }\n\n  for (const auto& [direction, neighbors] : new_element.neighbors()) {\n    const auto sliced_away_dimension = direction.dimension();\n    const auto new_face_mesh = new_mesh.slice_away(sliced_away_dimension);\n    for (const auto& neighbor : neighbors) {\n      const DirectionalId<Dim> mortar_id{direction, neighbor};\n      const auto& new_neighbor_mesh = neighbor_mesh.at(mortar_id);\n      const auto new_mortar_mesh = ::dg::mortar_mesh(\n          new_face_mesh, new_neighbor_mesh.slice_away(sliced_away_dimension));\n      mortar_mesh->emplace(mortar_id, new_mortar_mesh);\n      // We only do h refinement at a slab boundary, so we know all\n      // the neighbors are aligned with us temporally.\n      mortar_next_temporal_id->emplace(mortar_id, current_temporal_id);\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                               \\\n  template ::dg::MortarMap<DIM(data),                                        \\\n                           evolution::dg::MortarDataHolder<DIM(data)>>       \\\n  empty_mortar_data(const Element<DIM(data)>& element);                      \\\n  template ::dg::MortarMap<DIM(data), MortarInfo<DIM(data)>> mortar_infos(   \\\n      const Domain<DIM(data)>& domain, const Element<DIM(data)>& element,    \\\n      const Mesh<DIM(data)>& volume_mesh,                                    \\\n      const ::dg::MortarMap<DIM(data), Mesh<DIM(data)>>& neighbor_mesh,      \\\n      bool local_time_stepping);                                             \\\n  template std::tuple<                                                       \\\n      ::dg::MortarMap<DIM(data), Mesh<DIM(data) - 1>>,                       \\\n      ::dg::MortarMap<DIM(data), TimeStepId>,                                \\\n      DirectionMap<DIM(data),                                                \\\n                   std::optional<Variables<tmpl::list<                       \\\n                       evolution::dg::Tags::MagnitudeOfNormal,               \\\n                       evolution::dg::Tags::NormalCovector<DIM(data)>>>>>>   \\\n  mortars_apply_impl(                                                        \\\n      const Element<DIM(data)>& element, const TimeStepId& next_temporal_id, \\\n      const Mesh<DIM(data)>& volume_mesh,                                    \\\n      const ::dg::MortarMap<DIM(data), Mesh<DIM(data)>>& neighbor_mesh);     \\\n  template void h_refine_structure(                                          \\\n      gsl::not_null<::dg::MortarMap<                                         \\\n          DIM(data), evolution::dg::MortarDataHolder<DIM(data)>>*>           \\\n          mortar_data,                                                       \\\n      gsl::not_null<::dg::MortarMap<DIM(data), Mesh<DIM(data) - 1>>*>        \\\n          mortar_mesh,                                                       \\\n      gsl::not_null<::dg::MortarMap<DIM(data), MortarInfo<DIM(data)>>*>      \\\n          mortar_infos,                                                      \\\n      gsl::not_null<::dg::MortarMap<DIM(data), TimeStepId>*>                 \\\n          mortar_next_temporal_id,                                           \\\n      gsl::not_null<DirectionMap<                                            \\\n          DIM(data),                                                         \\\n          std::optional<::Variables<tmpl::list<                              \\\n              ::evolution::dg::Tags::MagnitudeOfNormal,                      \\\n              ::evolution::dg::Tags::NormalCovector<DIM(data)>>>>>*>         \\\n          normal_covector_and_magnitude,                                     \\\n      const Domain<DIM(data)>& domain, const Mesh<DIM(data)>& new_mesh,      \\\n      const Element<DIM(data)>& new_element,                                 \\\n      const ::dg::MortarMap<DIM(data), Mesh<DIM(data)>>& neighbor_mesh,      \\\n      const TimeStepId& current_temporal_id, bool local_time_stepping);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace detail\n\ntemplate <size_t Dim, bool LocalTimeStepping>\nvoid ProjectMortars<Dim, LocalTimeStepping>::apply(\n    const gsl::not_null<\n        ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n        mortar_data,\n    const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_mesh,\n    const gsl::not_null<::dg::MortarMap<Dim, MortarInfo<Dim>>*> mortar_infos,\n    const gsl::not_null<::dg::MortarMap<Dim, TimeStepId>*>\n        mortar_next_temporal_id,\n    const gsl::not_null<\n        DirectionMap<Dim, std::optional<magnitude_and_normal_type>>*>\n        normal_covector_and_magnitude,\n    const gsl::not_null<mortar_data_history_type*> mortar_data_history,\n    const Domain<Dim>& domain, const Mesh<Dim>& new_mesh,\n    const Element<Dim>& new_element,\n    const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh,\n    const TimeStepId& current_temporal_id,\n    const std::pair<Mesh<Dim>, Element<Dim>>& old_mesh_and_element) {\n  const auto& [old_mesh, old_element] = old_mesh_and_element;\n  ASSERT(old_element.id() == new_element.id(),\n         \"p-refinement should not have changed the element id\");\n\n  const bool mesh_changed = old_mesh != new_mesh;\n\n  auto new_mortar_infos = detail::mortar_infos(\n      domain, new_element, new_mesh, neighbor_mesh, LocalTimeStepping);\n\n  // The old mortars must be removed from the MortarMaps before the\n  // new ones can be added to avoid potentially exceeding the\n  // MortarMap capacity, so we collect the new data and add it at\n  // the end.\n  using NewMortarEntry =\n      std::tuple<DirectionalId<Dim>, Mesh<Dim - 1>,\n                 std::optional<typename mortar_data_history_type::mapped_type>>;\n  std::vector<NewMortarEntry> new_mortars{};\n\n  for (const auto& [direction, neighbors] : new_element.neighbors()) {\n    const auto sliced_away_dimension = direction.dimension();\n    const auto old_face_mesh = old_mesh.slice_away(sliced_away_dimension);\n    const auto new_face_mesh = new_mesh.slice_away(sliced_away_dimension);\n    const bool face_mesh_changed = old_face_mesh != new_face_mesh;\n    if (face_mesh_changed) {\n      (*normal_covector_and_magnitude)[direction] = std::nullopt;\n    }\n    for (const auto& neighbor : neighbors) {\n      const DirectionalId<Dim> mortar_id{direction, neighbor};\n      const auto& new_neighbor_mesh = neighbor_mesh.at(mortar_id);\n      const auto new_mortar_mesh = ::dg::mortar_mesh(\n          new_face_mesh, new_neighbor_mesh.slice_away(sliced_away_dimension));\n      if (mortar_mesh->contains(mortar_id)) {\n        // Set the mortar mesh, but do not project any existing mesh\n        // data.  The mesh needs to have a valid value in order to\n        // project data when we send to our neighbors.  If the mesh\n        // resolution needs to be changed afterwards because of a\n        // change in the neighbor, that's fine, because\n        // element-to-mortar projections are lossless.  Projecting\n        // existing data would be bad, because we might erroneously\n        // decrease the mesh resolution, losing the high-order\n        // modes.\n        mortar_mesh->at(mortar_id) = new_mortar_mesh;\n        // mortar_data does not need projecting as it has already been used\n        // and will be resized automatically\n        if (mesh_changed and not mortar_data_history->empty()) {\n          auto& boundary_history = mortar_data_history->at(mortar_id);\n          auto local_history = boundary_history.local();\n          const auto project_local_boundary_data =\n              [&new_face_mesh, &new_mesh](\n                  const TimeStepId& /* id */,\n                  const gsl::not_null<::evolution::dg::MortarData<Dim>*> data) {\n                return p_project_geometric_data(data, new_face_mesh, new_mesh);\n              };\n          local_history.for_each(project_local_boundary_data);\n        }\n      } else {\n        const auto& new_mortar_size =\n            new_mortar_infos.at(mortar_id).mortar_size();\n\n        std::optional<typename mortar_data_history_type::mapped_type>\n            new_history{};\n        for (const auto& [old_mortar_id, old_history] : *mortar_data_history) {\n          if (old_mortar_id.direction() != direction or\n              not overlapping(old_mortar_id.id(), neighbor)) {\n            continue;\n          }\n          const auto& old_mortar_size =\n              mortar_infos->at(old_mortar_id).mortar_size();\n          if (not new_history.has_value()) {\n            new_history.emplace(old_history);\n            new_history->remote().clear();\n            auto local_history = new_history->local();\n            if (mesh_changed) {\n              const auto project_face_data =\n                  [&new_face_mesh, &new_mesh](\n                      const TimeStepId& /* id */,\n                      const gsl::not_null<::evolution::dg::MortarData<Dim>*>\n                          data) {\n                    return p_project_geometric_data(data, new_face_mesh,\n                                                    new_mesh);\n                  };\n              local_history.for_each(project_face_data);\n            }\n            const auto project_mortar_data =\n                [&new_mortar_mesh, &new_mortar_size, &old_mortar_size](\n                    const TimeStepId& /* id */,\n                    const gsl::not_null<::evolution::dg::MortarData<Dim>*>\n                        data) {\n                  const auto& old_mortar_mesh = data->mortar_mesh.value();\n                  const auto mortar_projection_matrices =\n                      Spectral::projection_matrices(\n                          old_mortar_mesh, new_mortar_mesh, old_mortar_size,\n                          new_mortar_size);\n                  DataVector& vars = data->mortar_data.value();\n                  vars = apply_matrices(mortar_projection_matrices, vars,\n                                        old_mortar_mesh.extents());\n                  data->mortar_mesh = new_mortar_mesh;\n                  return true;\n                };\n            local_history.for_each(project_mortar_data);\n          } else {\n            auto local_history = new_history->local();\n            const auto old_local_history = old_history.local();\n            const auto project_local_mortar_data =\n                [&new_mortar_mesh, &new_mortar_size, &old_local_history,\n                 &old_mortar_size](\n                    const TimeStepId& id,\n                    const gsl::not_null<::evolution::dg::MortarData<Dim>*>\n                        data) {\n                  const auto& old_data = old_local_history.data(id);\n                  const auto& old_mortar_mesh = old_data.mortar_mesh.value();\n                  const auto mortar_projection_matrices =\n                      Spectral::projection_matrices(\n                          old_mortar_mesh, new_mortar_mesh, old_mortar_size,\n                          new_mortar_size);\n                  data->mortar_data.value() += apply_matrices(\n                      mortar_projection_matrices, old_data.mortar_data.value(),\n                      old_mortar_mesh.extents());\n                  return true;\n                };\n            local_history.for_each(project_local_mortar_data);\n          }\n        }\n\n        new_mortars.emplace_back(mortar_id, new_mortar_mesh, new_history);\n      }\n    }\n  }\n\n  if (not new_mortars.empty()) {\n    domain::remove_nonexistent_neighbors(mortar_mesh, new_element);\n    domain::remove_nonexistent_neighbors(mortar_next_temporal_id, new_element);\n    domain::remove_nonexistent_neighbors(mortar_data_history, new_element);\n    for (auto& [mortar_id, new_mortar_mesh, new_history] : new_mortars) {\n      mortar_mesh->emplace(mortar_id, std::move(new_mortar_mesh));\n      // We only do h refinement at a slab boundary, so we know all\n      // the neighbors are aligned with us temporally.\n      mortar_next_temporal_id->emplace(mortar_id, current_temporal_id);\n      if (new_history.has_value()) {\n        mortar_data_history->emplace(mortar_id, *new_history);\n      }\n    }\n  }\n\n  *mortar_data = detail::empty_mortar_data(new_element);\n  *mortar_infos = std::move(new_mortar_infos);\n\n  for (const auto& direction : new_element.external_boundaries()) {\n    const auto sliced_away_dimension = direction.dimension();\n    const auto old_face_mesh = old_mesh.slice_away(sliced_away_dimension);\n    const auto new_face_mesh = new_mesh.slice_away(sliced_away_dimension);\n    const bool face_mesh_changed = old_face_mesh != new_face_mesh;\n    if (face_mesh_changed) {\n      (*normal_covector_and_magnitude)[direction] = std::nullopt;\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define LTS(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data) \\\n  template struct ProjectMortars<DIM(data), LTS(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (true, false))\n\n#undef INSTANTIATION\n#undef LTS\n#undef DIM\n}  // namespace evolution::dg::Initialization\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/TrimMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Domain;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Spectral {\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace evolution::dg::Initialization {\nnamespace detail {\ntemplate <size_t Dim>\n::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>> empty_mortar_data(\n    const Element<Dim>& element);\n\ntemplate <size_t Dim>\n::dg::MortarMap<Dim, MortarInfo<Dim>> mortar_infos(\n    const Domain<Dim>& domain, const Element<Dim>& element,\n    const Mesh<Dim>& volume_mesh,\n    const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh,\n    bool local_time_stepping);\n\ntemplate <size_t Dim>\nstd::tuple<::dg::MortarMap<Dim, Mesh<Dim - 1>>,\n           ::dg::MortarMap<Dim, TimeStepId>,\n           DirectionMap<Dim, std::optional<Variables<tmpl::list<\n                                 evolution::dg::Tags::MagnitudeOfNormal,\n                                 evolution::dg::Tags::NormalCovector<Dim>>>>>>\nmortars_apply_impl(const Element<Dim>& element,\n                   const TimeStepId& next_temporal_id,\n                   const Mesh<Dim>& volume_mesh,\n                   const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh);\n\ntemplate <size_t Dim>\nvoid h_refine_structure(\n    gsl::not_null<::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n        mortar_data,\n    gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_mesh,\n    gsl::not_null<::dg::MortarMap<Dim, MortarInfo<Dim>>*> mortar_infos,\n    gsl::not_null<::dg::MortarMap<Dim, TimeStepId>*> mortar_next_temporal_id,\n    gsl::not_null<\n        DirectionMap<Dim, std::optional<::Variables<tmpl::list<\n                              ::evolution::dg::Tags::MagnitudeOfNormal,\n                              ::evolution::dg::Tags::NormalCovector<Dim>>>>>*>\n        normal_covector_and_magnitude,\n    const Domain<Dim>& domain, const Mesh<Dim>& new_mesh,\n    const Element<Dim>& new_element,\n    const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh,\n    const TimeStepId& current_temporal_id, bool local_time_stepping);\n}  // namespace detail\n\n/*!\n * \\brief Initialize mortars between elements for exchanging boundary correction\n * terms.\n *\n * Uses:\n * - DataBox:\n *   - `Tags::Element<Dim>`\n *   - `Tags::Mesh<Dim>`\n *   - `BoundaryScheme::receive_temporal_id`\n *\n * DataBox changes:\n * - Adds:\n *   - `Tags::MortarData<Dim>`\n *   - `Tags::MortarMesh<Dim>`\n *   - `Tags::MortarInfo<Dim>`\n *   - `Tags::MortarNextTemporalId<Dim>`\n *   - `evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>`\n * - Removes: nothing\n * - Modifies: nothing\n */\ntemplate <size_t Dim>\nstruct Mortars {\n public:\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<Dim>>;\n  using simple_tags_from_options = tmpl::list<>;\n\n  using simple_tags =\n      tmpl::list<Tags::MortarData<Dim>, Tags::MortarMesh<Dim>,\n                 Tags::MortarInfo<Dim>, Tags::MortarNextTemporalId<Dim>,\n                 evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>,\n                 Tags::MortarDataHistory<Dim>>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& domain = db::get<domain::Tags::Domain<Dim>>(box);\n    const auto& element = db::get<::domain::Tags::Element<Dim>>(box);\n    const auto& volume_mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    const auto& neighbor_mesh = db::get<domain::Tags::NeighborMesh<Dim>>(box);\n    auto mortar_data = detail::empty_mortar_data(element);\n    auto mortar_infos =\n        detail::mortar_infos(domain, element, volume_mesh, neighbor_mesh,\n                             Metavariables::local_time_stepping);\n    auto [mortar_meshes, mortar_next_temporal_ids, normal_covector_quantities] =\n        detail::mortars_apply_impl(\n            element, db::get<::Tags::Next<::Tags::TimeStepId>>(box),\n            db::get<::domain::Tags::Mesh<Dim>>(box),\n            db::get<::domain::Tags::NeighborMesh<Dim>>(box));\n    typename Tags::MortarDataHistory<Dim>::type boundary_data_history{};\n    for (const auto& mortar_id_and_data : mortar_data) {\n      if (mortar_infos.at(mortar_id_and_data.first).time_stepping_policy() ==\n          TimeSteppingPolicy::Conservative) {\n        // default initialize data\n        boundary_data_history[mortar_id_and_data.first];\n      }\n    }\n    ::Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), std::move(mortar_data), std::move(mortar_meshes),\n        std::move(mortar_infos), std::move(mortar_next_temporal_ids),\n        std::move(normal_covector_quantities),\n        std::move(boundary_data_history));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\brief Initialize/update items related to mortars after an AMR change\n///\n/// Mutates:\n///   - Tags::MortarData<dim>\n///   - Tags::MortarMesh<dim>\n///   - Tags::MortarInfo<dim>\n///   - Tags::MortarNextTemporalId<dim>\n///   - evolution::dg::Tags::NormalCovectorAndMagnitude<dim>\n///   - Tags::MortarDataHistory<dim>>\n///\n/// For p-refined interfaces:\n///   - Regenerates MortarData and MortarInfo (should have no effect)\n///   - Sets the NormalCovectorAndMagnitude to std::nullopt if the face mesh\n///     changed\n///   - Projects the local geometric data (but not the data on the mortar-mesh)\n///     in the MortarDataHistory, if present\n///   - Does nothing to MortarMesh and MortarNextTemporalId\n///\n/// For h-refined interfaces:\n///   - Regenerates MortarData and MortarInfo\n///   - Sets the NormalCovectorAndMagnitude to std::nullopt\n///   - Calculates MortarMesh\n///   - Sets MortarNextTemporalId to the current temporal id\n///   - For local time-stepping:\n///     - Removes MortarDataHistory data corresponding to split or joined\n///       elements\n///     - Projects MortarDataHistory data corresponding to non-h-refined\n///       elements onto refined mortars (both geometric and mortar-mesh data)\n///     - Creates empty histories for new mortars between two h-refined\n///       elements\ntemplate <size_t Dim, bool LocalTimeStepping>\nstruct ProjectMortars : tt::ConformsTo<amr::protocols::Projector> {\n private:\n  using magnitude_and_normal_type =\n      ::Variables<tmpl::list<::evolution::dg::Tags::MagnitudeOfNormal,\n                             ::evolution::dg::Tags::NormalCovector<Dim>>>;\n\n public:\n  using mortar_data_history_tag = Tags::MortarDataHistory<Dim>;\n  using mortar_data_history_type = typename mortar_data_history_tag::type;\n\n  using return_tags =\n      tmpl::list<Tags::MortarData<Dim>, Tags::MortarMesh<Dim>,\n                 Tags::MortarInfo<Dim>, Tags::MortarNextTemporalId<Dim>,\n                 evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>,\n                 Tags::MortarDataHistory<Dim>>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Domain<Dim>, domain::Tags::Mesh<Dim>,\n                 domain::Tags::Element<Dim>, domain::Tags::NeighborMesh<Dim>,\n                 ::Tags::TimeStepId>;\n\n  static void apply(\n      gsl::not_null<::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n          mortar_data,\n      gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_mesh,\n      gsl::not_null<::dg::MortarMap<Dim, MortarInfo<Dim>>*> mortar_infos,\n      gsl::not_null<::dg::MortarMap<Dim, TimeStepId>*> mortar_next_temporal_id,\n      gsl::not_null<\n          DirectionMap<Dim, std::optional<magnitude_and_normal_type>>*>\n          normal_covector_and_magnitude,\n      gsl::not_null<mortar_data_history_type*> mortar_data_history,\n      const Domain<Dim>& domain, const Mesh<Dim>& new_mesh,\n      const Element<Dim>& new_element,\n      const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh,\n      const TimeStepId& current_temporal_id,\n      const std::pair<Mesh<Dim>, Element<Dim>>& old_mesh_and_element);\n\n  template <typename... ParentTags>\n  static void apply(\n      const gsl::not_null<\n          ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n          mortar_data,\n      const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_mesh,\n      const gsl::not_null<::dg::MortarMap<Dim, MortarInfo<Dim>>*> mortar_infos,\n      const gsl::not_null<::dg::MortarMap<Dim, TimeStepId>*>\n          mortar_next_temporal_id,\n      const gsl::not_null<\n          DirectionMap<Dim, std::optional<magnitude_and_normal_type>>*>\n          normal_covector_and_magnitude,\n      const gsl::not_null<mortar_data_history_type*> mortar_data_history,\n      const Domain<Dim>& domain, const Mesh<Dim>& new_mesh,\n      const Element<Dim>& new_element,\n      const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh,\n      const TimeStepId& /*possibly_unset*/,\n      const tuples::TaggedTuple<ParentTags...>& parent_items) {\n    detail::h_refine_structure(\n        mortar_data, mortar_mesh, mortar_infos, mortar_next_temporal_id,\n        normal_covector_and_magnitude, domain, new_mesh, new_element,\n        neighbor_mesh, get<::Tags::TimeStepId>(parent_items),\n        LocalTimeStepping);\n\n    const auto& old_element = get<domain::Tags::Element<Dim>>(parent_items);\n    const auto& old_histories = get<mortar_data_history_tag>(parent_items);\n    for (const auto& [direction, neighbors] : new_element.neighbors()) {\n      for (const auto& neighbor : neighbors) {\n        const DirectionalId<Dim> mortar_id{direction, neighbor};\n        if (mortar_infos->at(mortar_id).time_stepping_policy() !=\n            TimeSteppingPolicy::Conservative) {\n          continue;\n        }\n        if (const auto old_history = old_histories.find(mortar_id);\n            old_history != old_histories.end()) {\n          // The neighbor did not h-refine, so we have to project\n          // its mortar data from our parent.\n          auto& new_history =\n              mortar_data_history->emplace(mortar_id, old_history->second)\n                  .first->second;\n          new_history.local().clear();\n          auto remote_history = new_history.remote();\n          const auto& new_mortar_mesh = mortar_mesh->at(mortar_id);\n          const auto& orientation = neighbors.orientation(neighbor);\n          const auto new_mortar_size = domain::child_size(\n              ::dg::mortar_segments(new_element.id(), neighbor,\n                                    direction.dimension(), orientation),\n              ::dg::mortar_segments(old_element.id(), neighbor,\n                                    direction.dimension(), orientation));\n          const auto project_mortar_data =\n              [&new_mortar_mesh, &new_mortar_size](\n                  const TimeStepId& /* id */,\n                  const gsl::not_null<::evolution::dg::MortarData<Dim>*> data) {\n                const auto& old_mortar_mesh = data->mortar_mesh.value();\n                const auto mortar_projection_matrices =\n                    Spectral::projection_matrices(\n                        old_mortar_mesh, new_mortar_mesh,\n                        make_array<Dim - 1>(Spectral::SegmentSize::Full),\n                        new_mortar_size);\n                DataVector& vars = data->mortar_data.value();\n                vars = apply_matrices(mortar_projection_matrices, vars,\n                                      old_mortar_mesh.extents());\n                data->mortar_mesh = new_mortar_mesh;\n                return true;\n              };\n          remote_history.for_each(project_mortar_data);\n        } else {\n          // Neither this element nor the neighbor existed before\n          // refinement.\n          mortar_data_history->emplace(\n              mortar_id, typename mortar_data_history_type::mapped_type{});\n        }\n      }\n    }\n  }\n\n  template <typename... ChildTags>\n  static void apply(\n      const gsl::not_null<\n          ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>*>\n          mortar_data,\n      const gsl::not_null<::dg::MortarMap<Dim, Mesh<Dim - 1>>*> mortar_mesh,\n      const gsl::not_null<::dg::MortarMap<Dim, MortarInfo<Dim>>*> mortar_infos,\n      const gsl::not_null<::dg::MortarMap<Dim, TimeStepId>*>\n          mortar_next_temporal_id,\n      const gsl::not_null<\n          DirectionMap<Dim, std::optional<magnitude_and_normal_type>>*>\n          normal_covector_and_magnitude,\n      const gsl::not_null<mortar_data_history_type*> mortar_data_history,\n      const Domain<Dim>& domain, const Mesh<Dim>& new_mesh,\n      const Element<Dim>& new_element,\n      const ::dg::MortarMap<Dim, Mesh<Dim>>& neighbor_mesh,\n      const TimeStepId& /*possibly_unset*/,\n      const std::unordered_map<\n          ElementId<Dim>, tuples::TaggedTuple<ChildTags...>>& children_items) {\n    detail::h_refine_structure(\n        mortar_data, mortar_mesh, mortar_infos, mortar_next_temporal_id,\n        normal_covector_and_magnitude, domain, new_mesh, new_element,\n        neighbor_mesh, get<::Tags::TimeStepId>(children_items.begin()->second),\n        LocalTimeStepping);\n\n    for (const auto& [direction, neighbors] : new_element.neighbors()) {\n      for (const auto& neighbor : neighbors) {\n        const DirectionalId<Dim> mortar_id{direction, neighbor};\n        if (mortar_infos->at(mortar_id).time_stepping_policy() !=\n            TimeSteppingPolicy::Conservative) {\n          continue;\n        }\n        std::optional<typename mortar_data_history_type::mapped_type>\n            new_history{};\n        for (const auto& [child, child_items] : children_items) {\n          const auto& old_histories =\n              get<mortar_data_history_tag>(child_items);\n          if (const auto old_history = old_histories.find(mortar_id);\n              old_history != old_histories.end()) {\n            // The neighbor did not h-refine, so we have to project\n            // its mortar data from our children.\n            const auto& new_mortar_mesh = mortar_mesh->at(mortar_id);\n            const auto& orientation = neighbors.orientation(neighbor);\n            const auto old_mortar_size = domain::child_size(\n                ::dg::mortar_segments(child, neighbor, direction.dimension(),\n                                      orientation),\n                ::dg::mortar_segments(new_element.id(), neighbor,\n                                      direction.dimension(), orientation));\n            if (not new_history.has_value()) {\n              new_history.emplace(old_history->second);\n              new_history->local().clear();\n              auto remote_history = new_history->remote();\n              const auto project_mortar_data =\n                  [&new_mortar_mesh, &old_mortar_size](\n                      const TimeStepId& /* id */,\n                      const gsl::not_null<::evolution::dg::MortarData<Dim>*>\n                          data) {\n                    const auto& old_mortar_mesh = data->mortar_mesh.value();\n                    const auto mortar_projection_matrices =\n                        Spectral::projection_matrices(\n                            old_mortar_mesh, new_mortar_mesh, old_mortar_size,\n                            make_array<Dim - 1>(Spectral::SegmentSize::Full));\n                    DataVector& vars = data->mortar_data.value();\n                    vars = apply_matrices(mortar_projection_matrices, vars,\n                                          old_mortar_mesh.extents());\n                    data->mortar_mesh = new_mortar_mesh;\n                    return true;\n                  };\n              remote_history.for_each(project_mortar_data);\n            } else {\n              auto remote_history = new_history->remote();\n              const auto old_remote_history = old_history->second.remote();\n              const auto project_mortar_data =\n                  [&new_mortar_mesh, &old_mortar_size, &old_remote_history](\n                      const TimeStepId& id,\n                      const gsl::not_null<::evolution::dg::MortarData<Dim>*>\n                          data) {\n                    const auto& old_data = old_remote_history.data(id);\n                    const auto& old_mortar_mesh =\n                        old_data.mortar_mesh.value();\n                    const auto mortar_projection_matrices =\n                        Spectral::projection_matrices(\n                            old_mortar_mesh, new_mortar_mesh, old_mortar_size,\n                            make_array<Dim - 1>(Spectral::SegmentSize::Full));\n                    data->mortar_data.value() +=\n                        apply_matrices(mortar_projection_matrices,\n                                       old_data.mortar_data.value(),\n                                       old_mortar_mesh.extents());\n                    return true;\n                  };\n              remote_history.for_each(project_mortar_data);\n            }\n          }\n        }\n\n        if (new_history.has_value()) {\n          mortar_data_history->emplace(mortar_id, std::move(*new_history));\n        } else {\n          // Neither this element nor the neighbor existed before\n          // refinement.\n          mortar_data_history->emplace(\n              mortar_id, typename mortar_data_history_type::mapped_type{});\n        }\n      }\n    }\n  }\n};\n}  // namespace evolution::dg::Initialization\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/OptionsGroup.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg {\nnamespace OptionTags {\n/// The quadrature points to use.\nstruct Quadrature {\n  using type = Spectral::Quadrature;\n  using group = ::dg::OptionTags::DiscontinuousGalerkinGroup;\n  static constexpr Options::String help =\n      \"The point distribution/quadrature rule used.\";\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// The quadrature points to use initially.\n///\n/// While they could be changed during the evolution, it is unclear there is any\n/// reason to do so or that changing them during an evolution would even be\n/// stable.\nstruct Quadrature : db::SimpleTag {\n  using type = Spectral::Quadrature;\n\n  using option_tags = tmpl::list<OptionTags::Quadrature>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& quadrature) { return quadrature; }\n};\n}  // namespace Tags\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InstantiateBoundaryHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Time/BoundaryHistory.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  INSTANTIATE_BOUNDARY_HISTORY(::evolution::dg::MortarData<DIM(data)>, \\\n                               ::evolution::dg::MortarData<DIM(data)>, \\\n                               DataVector)\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InterfaceDataPolicy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/InterfaceDataPolicy.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace evolution::dg {\nstd::ostream& operator<<(std::ostream& os, const InterfaceDataPolicy value) {\n  switch (value) {\n    case InterfaceDataPolicy::Uninitialized:\n      return os << \"Uninitialized\";\n    case InterfaceDataPolicy::CopyProject:\n      return os << \"CopyProject\";\n    case InterfaceDataPolicy::OrientCopyProject:\n      return os << \"OrientCopyProject\";\n    case InterfaceDataPolicy::NonconformingBothInterpolate:\n      return os << \"NonconformingBothInterpolate\";\n    case InterfaceDataPolicy::NonconformingSelfInterpolates:\n      return os << \"NonconformingSelfInterpolates\";\n    case InterfaceDataPolicy::NonconformingNeighborInterpolates:\n      return os << \"NonconformingNeighborInterpolates\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\n          \"An unknown value of InterfaceDataPolicy was passed to the stream \"\n          \"operator.\");\n      // LCOV_EXCL_STOP\n  }\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InterfaceDataPolicy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <iosfwd>\n\nnamespace evolution::dg {\n/// \\brief Label for a neighboring Element (or Block) that determines how\n/// information is exchanged between neighboring Elements\n///\n/// \\details The specific label is determined by the relationship between the\n/// block logical coordinates of the neighboring Elements (Blocks) for the\n/// points on the interface between them.  In two cases there is a simple\n/// relationship between the coordinates:\n/// - CopyProject: in this case the block logical coordinates are identical.\n///   Therefore a DataVector can be either copied (if the Mesh on each side of\n///   the interface are at the same points) or projected to the Mesh of the\n///   neighbor.\n/// - OrientCopyProject: in this case the block logical coordinates are related\n///   by a discrete rotation (represented by an OrientationMap). Therefore a\n///   DataVector can be reoriented (with the OrientationMap), and then either\n///   copied or projected to the Mesh of the neighbor.\n///\n/// In the following cases, there is no simple relationship between the block\n/// logical coordinates.  Therefore a DataVector must be interpolated to the\n/// points of the neighboring Mesh.  The cases differ in which Element does the\n/// interpolation:\n/// - NonconformingBothInterpolate:  in this case both the Element and its\n///   neighbor interpolate data to the grid points of each others Mesh.\n/// - NonconformingSelfInterpolates:  in this case the Element will receive the\n///   neighbor's boundary data and will interpolate it to the Mesh of the\n///   Element.  The Element will then need to send boundary correction data to\n///   the neighbor.\n/// - NonconformingNeighborInterpolates:  in this case the Element send its\n///   boundary data to the neighbor who will then interpolate it to its own\n///   Mesh. The neighbor will need to send boundary correction data back to the\n///   Element.\n///\n/// The Element and its neighbor will need to use consistent values of this\n/// enum:\n/// - In the cases CopyProject, OrientCopyProject, and\n///   NonconformingBothInterpolate, neighboring elements should agree on the\n///   values.\n/// - For NonconformingSelfInterpolates and NonconformingNeighborInterpolates\n///   neighboring elements should have different values.  These cases should be\n///   used when one Element has many neighboring Elements (e.g. when a single\n///   spherical shell abuts a cubes sphere).  In this case it should be more\n///   efficient for the single element to send its boundary data to its\n///   neighbors which then do the interpolation to their meshes.\nenum class InterfaceDataPolicy : uint8_t {\n  /// default value is uninitialized\n  Uninitialized = 0,\n  /// Boundary data can be copied or projected to Mesh of neighbor\n  CopyProject = 1,\n  /// Boundary data should be reoriented, and then copied or projected to Mesh\n  /// of neighbor\n  OrientCopyProject = 2,\n  /// Boundary data should be interpolated to Mesh of neighbor\n  NonconformingBothInterpolate = 3,\n  /// Neighbor will send boundary data to be interpolated onto the Mesh of this\n  /// Element.  Boundary correction data will then need to be sent to the\n  /// neighbor.\n  NonconformingSelfInterpolates = 4,\n  /// Boundary data should be sent to the neighbor, who will interpolate the\n  /// data to its own Mesh.  The neighbor will send boundary correction data\n  /// back.\n  NonconformingNeighborInterpolates = 5\n};\n\n/// Output operator for a InterfaceDataPolicy.\nstd::ostream& operator<<(std::ostream& os, InterfaceDataPolicy value);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InterpolatedBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/InterpolatedBoundaryData.hpp\"\n\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <vector>\n\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t VolumeDim>\nInterpolatedBoundaryData<VolumeDim>::InterpolatedBoundaryData(\n    InterpolatedBoundaryData::Info info)\n    : info_(std::move(info)) {}\n\ntemplate <size_t VolumeDim>\nvoid InterpolatedBoundaryData<VolumeDim>::Info::pup(PUP::er& p) {\n  p | data;\n  p | target_mesh;\n  p | offsets;\n}\n\ntemplate <size_t VolumeDim>\nvoid InterpolatedBoundaryData<VolumeDim>::pup(PUP::er& p) {\n  p | info_;\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const InterpolatedBoundaryData<VolumeDim>& lhs,\n                const InterpolatedBoundaryData<VolumeDim>& rhs) {\n  return lhs.boundary_data() == rhs.boundary_data() and\n         lhs.target_mesh() == rhs.target_mesh() and\n         lhs.offsets() == rhs.offsets();\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const InterpolatedBoundaryData<VolumeDim>& lhs,\n                const InterpolatedBoundaryData<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const InterpolatedBoundaryData<VolumeDim>& value) {\n  using ::operator<<;\n  os << \"boundary data = \" << value.boundary_data()\n     << \"\\ntarget mesh = \" << value.target_mesh()\n     << \"\\noffsets = \" << value.offsets();\n  return os;\n}\n\ntemplate class InterpolatedBoundaryData<1>;\ntemplate class InterpolatedBoundaryData<2>;\ntemplate class InterpolatedBoundaryData<3>;\ntemplate bool operator==(const InterpolatedBoundaryData<1>&,\n                         const InterpolatedBoundaryData<1>&);\ntemplate bool operator==(const InterpolatedBoundaryData<2>&,\n                         const InterpolatedBoundaryData<2>&);\ntemplate bool operator==(const InterpolatedBoundaryData<3>&,\n                         const InterpolatedBoundaryData<3>&);\ntemplate bool operator!=(const InterpolatedBoundaryData<1>&,\n                         const InterpolatedBoundaryData<1>&);\ntemplate bool operator!=(const InterpolatedBoundaryData<2>&,\n                         const InterpolatedBoundaryData<2>&);\ntemplate bool operator!=(const InterpolatedBoundaryData<3>&,\n                         const InterpolatedBoundaryData<3>&);\ntemplate std::ostream& operator<<(std::ostream&,\n                                  const InterpolatedBoundaryData<1>&);\ntemplate std::ostream& operator<<(std::ostream&,\n                                  const InterpolatedBoundaryData<2>&);\ntemplate std::ostream& operator<<(std::ostream&,\n                                  const InterpolatedBoundaryData<3>&);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/InterpolatedBoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace evolution::dg {\n/// \\brief Information sent by a non-conforming Element that interpolates its\n/// boundary data to a subset of the points of the Element receiving this\n///\n/// \\details The following information is sent:\n/// - the interpolated data (as a DataVector representing a type-erased\n///   Variables)\n/// - the Mesh that was used to compute the target points of the boundary face\n///   of the receiving Element.  This is sent so that the receiving Element can\n///   check if the data was interpolated to the correct points.\n/// - the offsets of the interpolated data with respect to the target Mesh\ntemplate <size_t VolumeDim>\nclass InterpolatedBoundaryData {\n  struct Info {\n    DataVector data{};\n    Mesh<VolumeDim - 1> target_mesh{};\n    std::vector<size_t> offsets{};\n    // NOLINTNEXTLINE(google-runtime-references)\n    void pup(PUP::er& p);\n  };\n\n public:\n  InterpolatedBoundaryData() = default;\n  InterpolatedBoundaryData(const InterpolatedBoundaryData&) = default;\n  InterpolatedBoundaryData(InterpolatedBoundaryData&&) = default;\n  InterpolatedBoundaryData& operator=(const InterpolatedBoundaryData&) =\n      default;\n  InterpolatedBoundaryData& operator=(InterpolatedBoundaryData&&) = default;\n  ~InterpolatedBoundaryData() = default;\n\n  explicit InterpolatedBoundaryData(InterpolatedBoundaryData::Info info);\n\n  const DataVector& boundary_data() const { return info_.data; }\n  const Mesh<VolumeDim - 1>& target_mesh() const { return info_.target_mesh; }\n  const std::vector<size_t>& offsets() const { return info_.offsets; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  Info info_;\n};\n\ntemplate <size_t VolumeDim>\nbool operator==(const InterpolatedBoundaryData<VolumeDim>& lhs,\n                const InterpolatedBoundaryData<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const InterpolatedBoundaryData<VolumeDim>& lhs,\n                const InterpolatedBoundaryData<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const InterpolatedBoundaryData<VolumeDim>& value);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Limiters)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  HwenoImpl.cpp\n  MinmodHelpers.cpp\n  MinmodImpl.cpp\n  MinmodTci.cpp\n  MinmodType.cpp\n  WenoGridHelpers.cpp\n  WenoHelpers.cpp\n  WenoOscillationIndicator.cpp\n  WenoType.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  HwenoImpl.hpp\n  Krivodonova.hpp\n  Limiters.hpp\n  Minmod.hpp\n  MinmodHelpers.hpp\n  MinmodImpl.hpp\n  MinmodTci.hpp\n  MinmodType.hpp\n  SimpleWenoImpl.hpp\n  Tags.hpp\n  Tci.hpp\n  Weno.hpp\n  WenoGridHelpers.hpp\n  WenoHelpers.hpp\n  WenoOscillationIndicator.hpp\n  WenoType.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  Domain\n  DomainStructure\n  Interpolation\n  LinearOperators\n  Options\n  Spectral\n  )\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.hpp\"\n\n#include <array>\n#include <bitset>\n#include <cstddef>\n#include <exception>\n#include <ostream>\n#include <vector>\n\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\n// Compute the VolumeDim-dimensional quadrature weights, by taking the tensor\n// product of the 1D quadrature weights in each logical dimension of the Mesh.\ntemplate <size_t VolumeDim>\nDataVector volume_quadrature_weights(const Mesh<VolumeDim>& mesh) {\n  std::array<DataVector, VolumeDim> quadrature_weights_1d{};\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    gsl::at(quadrature_weights_1d, d) =\n        Spectral::quadrature_weights(mesh.slice_through(d));\n  }\n\n  DataVector result{mesh.number_of_grid_points(), 1.};\n  for (IndexIterator<VolumeDim> s(mesh.extents()); s; ++s) {\n    result[s.collapsed_index()] = quadrature_weights_1d[0][s()[0]];\n    for (size_t d = 1; d < VolumeDim; ++d) {\n      result[s.collapsed_index()] *= gsl::at(quadrature_weights_1d, d)[s()[d]];\n    }\n  }\n  return result;\n}\n\n// Compute the matrix that interpolates data in VolumeDim dimensions from\n// source_mesh to the grid points of target_mesh, by taking the tensor product\n// of the 1D interpolation matrices in each logical dimension of the meshes.\n//\n// Note: typically it is more efficient to interpolate from source_mesh to\n// target_mesh in one logical direction at a time, and to avoid computing this\n// matrix. However, the HWENO algorithm requires constructing this matrix\n// explicitly.\n//\n// In particular, for this HWENO application:\n// - source_mesh is the mesh of local element.\n// - target_mesh is the mesh of the neighboring element in direction. Note that\n//   there can only be one neighbor in this direction, and this is checked in\n//   the call to `neighbor_grid_points_in_local_logical_coordinates`.\n// - rectilinear elements are assumed.\ntemplate <size_t VolumeDim>\nMatrix volume_interpolation_matrix(const Mesh<VolumeDim>& source_mesh,\n                                   const Mesh<VolumeDim>& target_mesh,\n                                   const Element<VolumeDim>& element,\n                                   const Direction<VolumeDim>& direction) {\n  // The grid points of source_mesh and target_mesh must be in the same\n  // coordinates to construct the interpolation matrix. Here we get the points\n  // of target_mesh in the local logical coordinates.\n  const auto target_1d_coords =\n      Limiters::Weno_detail::neighbor_grid_points_in_local_logical_coords(\n          source_mesh, target_mesh, element, direction);\n\n  const auto interpolation_matrices_1d =\n      intrp::RegularGrid<VolumeDim>(source_mesh, target_mesh, target_1d_coords)\n          .interpolation_matrices();\n\n  // The 1D interpolation matrices will be empty if there is no need to\n  // interpolate in that particular direction (i.e., the interpolation in that\n  // direction is identity). This function undoes the optimization by returning\n  // elements of an identity matrix if an empty matrix is found.\n  const auto matrix_element = [&interpolation_matrices_1d](const size_t dim,\n                                                           const size_t r,\n                                                           const size_t s) {\n    const auto& matrix = gsl::at(interpolation_matrices_1d, dim);\n    if (matrix.rows() * matrix.columns() == 0) {\n      return (r == s) ? 1. : 0.;\n    } else {\n      return matrix(r, s);\n    }\n  };\n\n  Matrix result(target_mesh.number_of_grid_points(),\n                source_mesh.number_of_grid_points(), 0.);\n  for (IndexIterator<VolumeDim> r(target_mesh.extents()); r; ++r) {\n    for (IndexIterator<VolumeDim> s(source_mesh.extents()); s; ++s) {\n      result(r.collapsed_index(), s.collapsed_index()) =\n          matrix_element(0, r()[0], s()[0]);\n      for (size_t d = 1; d < VolumeDim; ++d) {\n        result(r.collapsed_index(), s.collapsed_index()) *=\n            matrix_element(d, r()[d], s()[d]);\n      }\n    }\n  }\n  return result;\n}\n\n}  // namespace\n\nnamespace Limiters::Weno_detail {\n\ntemplate <size_t VolumeDim>\nMatrix inverse_a_matrix(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const DataVector& quadrature_weights,\n    const DirectionMap<VolumeDim, Matrix>& interpolation_matrices,\n    const DirectionMap<VolumeDim, DataVector>&\n        quadrature_weights_dot_interpolation_matrices,\n    const Direction<VolumeDim>& primary_direction,\n    const std::vector<Direction<VolumeDim>>& directions_to_exclude) {\n  ASSERT(not alg::found(directions_to_exclude, primary_direction),\n         \"Logical inconsistency: trying to exclude the primary direction.\");\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  Matrix a(number_of_grid_points, number_of_grid_points, 0.);\n\n  // Loop only over directions where there is a neighbor\n  const std::vector<Direction<VolumeDim>> directions_with_neighbors =\n      [&element]() {\n        std::vector<Direction<VolumeDim>> result;\n        for (const auto& dir_and_neighbors : element.neighbors()) {\n          result.push_back(dir_and_neighbors.first);\n        }\n        return result;\n      }();\n\n  // Sanity check that directions_to_exclude is consistent with the element\n  ASSERT(\n      (not directions_to_exclude.empty()) or\n          directions_with_neighbors.size() == 1,\n      \"directions_to_exclude can only be empty if there is a single neighbor\");\n\n  for (const auto& dir : directions_with_neighbors) {\n    if (alg::found(directions_to_exclude, dir)) {\n      continue;\n    }\n\n    const auto& neighbor_mesh = mesh;\n    const auto& neighbor_quadrature_weights = quadrature_weights;\n    const auto& interpolation_matrix = interpolation_matrices.at(dir);\n    const auto& weights_dot_interpolation_matrix =\n        quadrature_weights_dot_interpolation_matrices.at(dir);\n\n    // Add terms from the primary neighbor\n    if (dir == primary_direction) {\n      for (size_t r = 0; r < neighbor_mesh.number_of_grid_points(); ++r) {\n        for (size_t s = 0; s < number_of_grid_points; ++s) {\n          for (size_t t = 0; t < number_of_grid_points; ++t) {\n            a(s, t) += neighbor_quadrature_weights[r] *\n                       interpolation_matrix(r, s) * interpolation_matrix(r, t);\n          }\n        }\n      }\n    }\n    // Add terms from the secondary neighbors\n    else {\n      for (size_t s = 0; s < number_of_grid_points; ++s) {\n        for (size_t t = 0; t < number_of_grid_points; ++t) {\n          a(s, t) += weights_dot_interpolation_matrix[s] *\n                     weights_dot_interpolation_matrix[t];\n        }\n      }\n    }\n  }\n\n  // Invert matrix in-place before returning\n  try {\n    blaze::invert<blaze::asSymmetric>(a);\n  } catch (const std::exception& e) {\n    ERROR(\"Blaze matrix inversion failed with exception:\\n\" << e.what());\n  }\n\n  return a;\n}\n\ntemplate <size_t VolumeDim>\nConstrainedFitCache<VolumeDim>::ConstrainedFitCache(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element)\n    : quadrature_weights(volume_quadrature_weights(mesh)) {\n  // Cache will only store quantities for directions that have neighbors.\n  const std::vector<Direction<VolumeDim>> directions_with_neighbors =\n      [&element]() {\n        std::vector<Direction<VolumeDim>> result;\n        for (const auto& dir_and_neighbors : element.neighbors()) {\n          result.push_back(dir_and_neighbors.first);\n        }\n        return result;\n      }();\n\n  for (const auto& dir : directions_with_neighbors) {\n    interpolation_matrices[dir] =\n        volume_interpolation_matrix(mesh, mesh, element, dir);\n    quadrature_weights_dot_interpolation_matrices[dir] = apply_matrices(\n        std::array<Matrix, 1>{{trans(interpolation_matrices.at(dir))}},\n        quadrature_weights, Index<1>(quadrature_weights.size()));\n  }\n\n  for (const auto& primary_dir : directions_with_neighbors) {\n    if (directions_with_neighbors.size() == 1) {\n      // With a single neighbor, there can be no neighbors to exclude.\n      std::vector<Direction<VolumeDim>> nothing_to_exclude{};\n      // To reuse the same data structure from the more common `else` branch,\n      // here we stick the data into the (normally nonsensical) slot where\n      // `dir_to_exclude == primary_dir`.\n      inverse_a_matrices[primary_dir][primary_dir] = inverse_a_matrix(\n          mesh, element, quadrature_weights, interpolation_matrices,\n          quadrature_weights_dot_interpolation_matrices, primary_dir,\n          nothing_to_exclude);\n    } else {\n      // Cache only handles the case of 1 neighbor to exclude.\n      for (const auto& dir_to_exclude : directions_with_neighbors) {\n        // Skip the nonsensical case where the primary and excluded neighbors\n        // are the same.\n        if (dir_to_exclude == primary_dir) {\n          continue;\n        }\n        inverse_a_matrices[primary_dir][dir_to_exclude] = inverse_a_matrix(\n            mesh, element, quadrature_weights, interpolation_matrices,\n            quadrature_weights_dot_interpolation_matrices, primary_dir,\n            {{dir_to_exclude}});\n      }\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\nconst Matrix& ConstrainedFitCache<VolumeDim>::retrieve_inverse_a_matrix(\n    const Direction<VolumeDim>& primary_direction,\n    const std::vector<Direction<VolumeDim>>& directions_to_exclude) const {\n  if (LIKELY(directions_to_exclude.size() == 1)) {\n    return inverse_a_matrices.at(primary_direction)\n        .at(directions_to_exclude[0]);\n  } else if (directions_to_exclude.empty()) {\n    return inverse_a_matrices.at(primary_direction).at(primary_direction);\n  } else {\n    ERROR(\n        \"Cache misuse error: asked to retrieve a cached A^{-1} matrix for a\\n\"\n        \"configuration where multiple neighboring elements are excluded from\\n\"\n        \"the HWENO fit. Because this case is so rare, it is not handled by\\n\"\n        \"the cache. The caller should check for multiple neighbors being\\n\"\n        \"excluded, and, if this occurs, should bypass the cache and compute\\n\"\n        \"A^{-1} directly.\");\n  }\n}\n\nnamespace {\n\ntemplate <size_t VolumeDim, size_t DummyIndex>\nconst ConstrainedFitCache<VolumeDim>& constrained_fit_cache_impl(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element) {\n  // For the cache to be valid,\n  // - the mesh must always be the same, so we check it below\n  // - the element can be different, as long as it has the same configuration\n  //   of internal/external boundaries. this is handled in the calling code\n  //   because the boundary configuration sets the value of DummyIndex\n  static const Mesh<VolumeDim> mesh_for_cached_matrix = mesh;\n  ASSERT(mesh_for_cached_matrix == mesh,\n         \"This call to constrained_fit_cache_impl received a different Mesh\\n\"\n         \"than was previously cached, suggesting that multiple meshes are\\n\"\n         \"used in the computational domain. This is not (yet) supported.\\n\"\n         \"Cached mesh: \"\n             << mesh_for_cached_matrix\n             << \"\\n\"\n                \"Argument mesh: \"\n             << mesh);\n  static const ConstrainedFitCache<VolumeDim> result(mesh, element);\n  return result;\n}\n\ntemplate <size_t VolumeDim, size_t... Is>\nconst ConstrainedFitCache<VolumeDim>& constrained_fit_cache(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    std::index_sequence<Is...> /*dummy_indices*/) {\n  static const std::array<\n      const ConstrainedFitCache<VolumeDim>& (*)(const Mesh<VolumeDim>&,\n                                                const Element<VolumeDim>&),\n      sizeof...(Is)>\n      cache{{&constrained_fit_cache_impl<VolumeDim, Is>...}};\n\n  // Use std::bitset to compute an integer based on the configuration of\n  // internal/external boundaries to the element. This is sort of like a hash\n  // for indexing into the std::array of ConstrainedFitCache's.\n  const size_t index_from_boundary_types = [&element]() {\n    std::bitset<2 * VolumeDim> bits;\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      for (const Side& side : {Side::Lower, Side::Upper}) {\n        // Index into bitset\n        const size_t bit_index = 2 * d + (side == Side::Lower ? 0 : 1);\n        // Is there a neighbor in this direction?\n        const Direction<VolumeDim> dir(d, side);\n        const bool neighbor_exists =\n            (element.neighbors().find(dir) != element.neighbors().end());\n        bits[bit_index] = neighbor_exists;\n      }\n    }\n    return static_cast<size_t>(bits.to_ulong());\n  }();\n  ASSERT(index_from_boundary_types >= 0 and\n             index_from_boundary_types < sizeof...(Is),\n         \"Got index_from_boundary_types = \"\n             << index_from_boundary_types << \", but expect only \"\n             << sizeof...(Is) << \" configurations\");\n  return gsl::at(cache, index_from_boundary_types)(mesh, element);\n}\n\n}  // namespace\n\ntemplate <size_t VolumeDim>\nconst ConstrainedFitCache<VolumeDim>& constrained_fit_cache(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element) {\n  return constrained_fit_cache<VolumeDim>(\n      mesh, element, std::make_index_sequence<two_to_the(2 * VolumeDim)>{});\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template Matrix inverse_a_matrix<DIM(data)>(                                 \\\n      const Mesh<DIM(data)>&, const Element<DIM(data)>&, const DataVector&,    \\\n      const DirectionMap<DIM(data), Matrix>&,                                  \\\n      const DirectionMap<DIM(data), DataVector>&, const Direction<DIM(data)>&, \\\n      const std::vector<Direction<DIM(data)>>&);                               \\\n  template class ConstrainedFitCache<DIM(data)>;                               \\\n  template const ConstrainedFitCache<DIM(data)>& constrained_fit_cache(        \\\n      const Mesh<DIM(data)>&, const Element<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <functional>\n#include <limits>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Element;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\nnamespace Limiters::Weno_detail {\n\n// Caching class that holds various precomputed terms used in the constrained-\n// fit algebra on each element.\n//\n// The terms to precompute and cache depend on the configuration of neighbors to\n// the element (how many neighbors, in which directions, of what resolution).\n// Each instance of the caching class represents one particular neighbor\n// configuration, and typical simulations will create many instances of\n// the caching class: one instance for each neighbor-element configuration\n// present in the computational domain.\n//\n// Because the current implementation of the HWENO fitting makes the simplifying\n// restrictions of no h/p-refinement, the structure of the caching is also\n// simplified. In particular, with no h/p-refinement, the allowable neighbor\n// configurations satisfy,\n// 1. the element has at most one neighbor per dimension, AND\n// 2. the mesh on every neighbor is the same as on the element.\n// With these restrictions, the number of independent configurations to be\n// cached is greatly reduced. Because an element has 2*VolumeDim boundaries,\n// it has 2^(2*VolumeDim) possible configurations of internal/external\n// boundaries, and therefore there are 2^(2*VolumeDim) configurations to cache.\n// Different elements with the same configuration of internal/external\n// boundaries vs. direction can share the same caching-class instance.\n//\n// Each instance of the caching class holds several terms, some of which also\n// depend on the neighbor configuration. The restriction of no h/p-refinement\n// therefore simplifies each cache instance, as well as reducing the necessary\n// number of instances.\n//\n// The most complicated term to cache is the A^{-1} matrix. The complexity\n// arises because the matrix can take many values depending on (runtime) choices\n// made for each individual HWNEO fit: which element is the primary neighbor,\n// and which element(s) are the excluded neighbors.\n// Fits with one excluded neighbor are overwhelmingly the most likely, and the\n// cache is designed for this particular case. Fits with no excluded neighbors\n// can arise in elements with only one neighbor (always the primary neighbor);\n// these cases are also handled. However, the very rare case in which more than\n// one neighbor is excluded is not handled by the cache; the caller must compute\n// A^{-1} from scratch if this scenario arises.\ntemplate <size_t VolumeDim>\nclass ConstrainedFitCache {\n public:\n  ConstrainedFitCache(const Mesh<VolumeDim>& mesh,\n                      const Element<VolumeDim>& element);\n\n  // Valid calls must satisfy these constraints on directions_to_exclude:\n  // - the vector is empty, OR\n  // - the vector contains one element, and this element is a direction\n  //   different from the direction to the primary neighbor.\n  // The very rare case where more than one neighbor is excluded from the fit is\n  // not cached. In this case, A^{-1} must be computed from scratch.\n  const Matrix& retrieve_inverse_a_matrix(\n      const Direction<VolumeDim>& primary_direction,\n      const std::vector<Direction<VolumeDim>>& directions_to_exclude) const;\n\n  DataVector quadrature_weights;\n  DirectionMap<VolumeDim, Matrix> interpolation_matrices;\n  DirectionMap<VolumeDim, DataVector>\n      quadrature_weights_dot_interpolation_matrices;\n  // The many possible values of A^{-1} are stored in a map of maps. The outer\n  // map indexes over the primary neighbor, and the inner map indexes over the\n  // excluded neighbor.\n  // This data structure is not perfect: configurations with only one neighbor\n  // (always the primary neighbor) lead to no excluded neighbors, and there is\n  // no natural place to hold A^{-1} in the maps. For this case, we simply store\n  // the data in the normally-nonsensical slot where\n  // excluded_neighbor == primary_neighbor.\n  DirectionMap<VolumeDim, DirectionMap<VolumeDim, Matrix>> inverse_a_matrices;\n};\n\n// Return the appropriate cache for the given mesh and element.\ntemplate <size_t VolumeDim>\nconst ConstrainedFitCache<VolumeDim>& constrained_fit_cache(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element);\n\n// Compute the inverse of the matrix A_st for the constrained fit.\n// See the documentation of `hweno_modified_neighbor_solution`.\ntemplate <size_t VolumeDim>\nMatrix inverse_a_matrix(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const DataVector& quadrature_weights,\n    const DirectionMap<VolumeDim, Matrix>& interpolation_matrices,\n    const DirectionMap<VolumeDim, DataVector>&\n        quadrature_weights_dot_interpolation_matrices,\n    const Direction<VolumeDim>& primary_direction,\n    const std::vector<Direction<VolumeDim>>& directions_to_exclude);\n\n// Not all secondary neighbors (i.e., neighbors that are not the primary) are\n// included in the HWENO constrained fit. In particular, for the tensor\n// component specified by `Tag` and `tensor_index`, the secondary neighbor whose\n// mean is most different from the troubled cell's mean is excluded.\n//\n// Zhu2016 indicate that when multiple secondary neighbors share the property of\n// being \"most different\" from the troubled cell, then all of these secondary\n// neighbors should be excluded from the minimization. The probability of this\n// occuring is exceedingly low, but we handle it anyway. We return the excluded\n// secondary neighbors in a vector.\n//\n// Note that if there is only one neighbor, it is the primary neighbor, and so\n// there are no secondary neighbors to exclude. We return an empty vector. This\n// scenario can arise in various test cases, but is unlikely to arise in science\n// cases.\ntemplate <typename Tag, size_t VolumeDim, typename Package>\nstd::vector<DirectionalId<VolumeDim>> secondary_neighbors_to_exclude_from_fit(\n    const double local_mean, const size_t tensor_index,\n    const std::unordered_map<DirectionalId<VolumeDim>, Package,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data,\n    const DirectionalId<VolumeDim>& primary_neighbor) {\n  // Rare case: with only one neighbor, there is no secondary to exclude\n  if (UNLIKELY(neighbor_data.size() == 1)) {\n    return std::vector<DirectionalId<VolumeDim>>{};\n  }\n\n  // Identify element with maximum mean difference\n  const auto mean_difference =\n      [&tensor_index,\n       &local_mean](const std::pair<DirectionalId<VolumeDim>, Package>&\n                        neighbor_and_data) {\n        return fabs(get<::Tags::Mean<Tag>>(\n                        neighbor_and_data.second.means)[tensor_index] -\n                    local_mean);\n      };\n\n  double max_difference = std::numeric_limits<double>::lowest();\n  DirectionalId<VolumeDim> neighbor_max_difference{};\n  for (const auto& neighbor_and_data : neighbor_data) {\n    const auto& neighbor = neighbor_and_data.first;\n    if (neighbor == primary_neighbor) {\n      continue;\n    }\n    const double difference = mean_difference(neighbor_and_data);\n    if (difference > max_difference) {\n      max_difference = difference;\n      neighbor_max_difference = neighbor;\n    }\n  }\n\n  std::vector<DirectionalId<VolumeDim>> neighbors_to_exclude{\n      {neighbor_max_difference}};\n\n  // See if other elements share this maximum mean difference. This loop should\n  // only rarely find other neighbors with the same maximal mean difference to\n  // add to the vector, so it will usually not change the vector.\n  for (const auto& neighbor_and_data : neighbor_data) {\n    const auto& neighbor = neighbor_and_data.first;\n    if (neighbor == primary_neighbor or neighbor == neighbor_max_difference) {\n      continue;\n    }\n    const double difference = mean_difference(neighbor_and_data);\n    if (UNLIKELY(equal_within_roundoff(difference, max_difference))) {\n      neighbors_to_exclude.push_back(neighbor);\n    }\n  }\n\n  ASSERT(not alg::found(neighbors_to_exclude, primary_neighbor),\n         \"Logical inconsistency: trying to exclude the primary neighbor.\");\n\n  return neighbors_to_exclude;\n}\n\n// Compute the vector b_s for the constrained fit. For details, see the\n// documentation of `hweno_modified_neighbor_solution` below.\ntemplate <typename Tag, size_t VolumeDim, typename Package>\nDataVector b_vector(\n    const size_t tensor_index, const Mesh<VolumeDim>& mesh,\n    const DataVector& quadrature_weights,\n    const DirectionMap<VolumeDim, Matrix>& interpolation_matrices,\n    const DirectionMap<VolumeDim, DataVector>&\n        quadrature_weights_dot_interpolation_matrices,\n    const std::unordered_map<DirectionalId<VolumeDim>, Package,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data,\n    const DirectionalId<VolumeDim>& primary_neighbor,\n    const std::vector<DirectionalId<VolumeDim>>& neighbors_to_exclude) {\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  DataVector b(number_of_grid_points, 0.);\n\n  for (const auto& neighbor_and_data : neighbor_data) {\n    // Generally, neighbors_to_exclude contains just one neighbor to exclude,\n    // and the loop over neighbor_data will hit this (in 3D) roughly 1/6 times.\n    // So the condition is somewhat, but not overwhelmingly, unlikely:\n    if (UNLIKELY(alg::found(neighbors_to_exclude, neighbor_and_data.first))) {\n      continue;\n    }\n\n    const auto& direction = neighbor_and_data.first.direction();\n    ASSERT(interpolation_matrices.contains(direction),\n           \"interpolation_matrices does not contain key: \" << direction);\n    ASSERT(\n        quadrature_weights_dot_interpolation_matrices.contains(direction),\n        \"quadrature_weights_dot_interpolation_matrices does not contain key: \"\n            << direction);\n\n    const auto& neighbor_mesh = mesh;\n    const auto& neighbor_quadrature_weights = quadrature_weights;\n    const auto& interpolation_matrix = interpolation_matrices.at(direction);\n    const auto& quadrature_weights_dot_interpolation_matrix =\n        quadrature_weights_dot_interpolation_matrices.at(direction);\n\n    const auto& neighbor_tensor_component =\n        get<Tag>(neighbor_and_data.second.volume_data)[tensor_index];\n\n    // Add terms from the primary neighbor\n    if (neighbor_and_data.first == primary_neighbor) {\n      for (size_t r = 0; r < neighbor_mesh.number_of_grid_points(); ++r) {\n        for (size_t s = 0; s < number_of_grid_points; ++s) {\n          b[s] += neighbor_tensor_component[r] *\n                  neighbor_quadrature_weights[r] * interpolation_matrix(r, s);\n        }\n      }\n    }\n    // Add terms from the secondary neighbors\n    else {\n      const double quadrature_weights_dot_u = [&]() {\n        double result = 0.;\n        for (size_t r = 0; r < neighbor_mesh.number_of_grid_points(); ++r) {\n          result +=\n              neighbor_tensor_component[r] * neighbor_quadrature_weights[r];\n        }\n        return result;\n      }();\n      b += quadrature_weights_dot_u *\n           quadrature_weights_dot_interpolation_matrix;\n    }\n  }\n  return b;\n}\n\n// Solve the constrained fit problem that gives the HWENO modified solution,\n// for one particular tensor component. For details, see documentation of\n// `hweno_modified_neighbor_solution` below.\ntemplate <typename Tag, size_t VolumeDim, typename Package>\nvoid solve_constrained_fit(\n    const gsl::not_null<DataVector*> constrained_fit_result,\n    const DataVector& u, const size_t tensor_index, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::unordered_map<DirectionalId<VolumeDim>, Package,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data,\n    const DirectionalId<VolumeDim>& primary_neighbor,\n    const std::vector<DirectionalId<VolumeDim>>& neighbors_to_exclude) {\n  ASSERT(not alg::found(neighbors_to_exclude, primary_neighbor),\n         \"Logical inconsistency: trying to exclude the primary neighbor.\");\n  ASSERT(not neighbors_to_exclude.empty() or neighbor_data.size() == 1,\n         \"The HWENO constrained fit algorithm expects at least one neighbor \\n\"\n         \"to exclude from the fit (unless if the element has a single \\n\"\n         \"neighbor, which would automatically be the primary neighbor).\");\n\n  // Get the cache of linear algebra quantities for this element\n  const ConstrainedFitCache<VolumeDim>& cache =\n      constrained_fit_cache(mesh, element);\n\n  // Because we don't support h-refinement, the direction is the only piece\n  // of the neighbor information that we actually need.\n  const Direction<VolumeDim> primary_direction = primary_neighbor.direction();\n  const std::vector<Direction<VolumeDim>> directions_to_exclude =\n      [&neighbors_to_exclude]() {\n        std::vector<Direction<VolumeDim>> result(neighbors_to_exclude.size());\n        for (size_t i = 0; i < result.size(); ++i) {\n          result[i] = neighbors_to_exclude[i].direction();\n        }\n        return result;\n      }();\n\n  const DataVector& w = cache.quadrature_weights;\n  const DirectionMap<VolumeDim, Matrix>& interp_matrices =\n      cache.interpolation_matrices;\n  const DirectionMap<VolumeDim, DataVector>& w_dot_interp_matrices =\n      cache.quadrature_weights_dot_interpolation_matrices;\n\n  // Use cache if possible, or compute matrix if we are in edge case\n  const Matrix& inverse_a =\n      LIKELY(directions_to_exclude.size() < 2)\n          ? cache.retrieve_inverse_a_matrix(primary_direction,\n                                            directions_to_exclude)\n          : inverse_a_matrix(mesh, element, w, interp_matrices,\n                             w_dot_interp_matrices, primary_direction,\n                             directions_to_exclude);\n\n  const DataVector b = b_vector<Tag>(tensor_index, mesh, w, interp_matrices,\n                                     w_dot_interp_matrices, neighbor_data,\n                                     primary_neighbor, neighbors_to_exclude);\n\n  const size_t number_of_points = b.size();\n  const DataVector inverse_a_times_b = apply_matrices(\n      std::array<std::reference_wrapper<const Matrix>, 1>{{inverse_a}}, b,\n      Index<1>(number_of_points));\n  const DataVector inverse_a_times_w = apply_matrices(\n      std::array<std::reference_wrapper<const Matrix>, 1>{{inverse_a}}, w,\n      Index<1>(number_of_points));\n\n  // Compute Lagrange multiplier:\n  // Note: we take w as an argument (instead of as a lambda capture), because\n  //       some versions of Clang incorrectly warn about capturing w.\n  const double lagrange_multiplier = [&number_of_points, &inverse_a_times_b,\n                                      &inverse_a_times_w,\n                                      &u](const DataVector& local_w) {\n    double numerator = 0.;\n    double denominator = 0.;\n    for (size_t s = 0; s < number_of_points; ++s) {\n      numerator += local_w[s] * (inverse_a_times_b[s] - u[s]);\n      denominator += local_w[s] * inverse_a_times_w[s];\n    }\n    return -numerator / denominator;\n  }(w);\n\n  // Compute solution:\n  *constrained_fit_result =\n      inverse_a_times_b + lagrange_multiplier * inverse_a_times_w;\n}\n\n/*!\n * \\ingroup LimitersGroup\n * \\brief Compute the HWENO solution for one tensor\n *\n * The HWENO limiter reconstructs a new solution from the linear combination of\n * the local DG solution and a \"modified\" solution from each neighbor element.\n * This function computes the modified solution for a particular tensor and\n * neighbor, following Section 3 of \\cite Zhu2016.\n *\n * The modified solution associated with a particular neighbor (the \"primary\"\n * neighbor) is obtained by solving a constrained fit over the local element,\n * the primary neighbor, and the other (\"secondary\") neighbors of the local\n * element. This fit seeks to minimize in a least-squared sense:\n * 1. The distance between the modified solution and the original solution on\n *    the primary neighbor.\n * 2. The distance between the cell average of the modified solution and the\n *    cell average of the original solution on each secondary neighbor. Note\n *    however that one secondary neighbor (or, rarely, several) is excluded from\n *    this minimization: the secondary neighbor(s) where the original solution\n *    has the most different cell average from the local element. This helps to\n *    prevent an outlier (e.g., near a shock) from biasing the fit.\n *\n * The constraint on the minimization is the following: the cell average of the\n * modified solution on the local element must equal the cell average of the\n * local element's original solution.\n *\n * Below we give the mathematical form of the constraints described above and\n * show how these are translated into a numerical algorithm.\n *\n * Consider an element \\f$I_0\\f$ with neighbors \\f$I_1, I_2, ...\\f$. For a\n * given tensor component \\f$u\\f$, the values on each of these elements are\n * \\f$u^{(0)}, u^{(1)}, u^{(2)}, ...\\f$. Taking for the sake of example the\n * primary neighbor to be \\f$I_1\\f$, the modified solution \\f$\\phi\\f$ must\n * minimize\n *\n * \\f[\n * \\chi^2 = \\int_{I_1} (\\phi - u^{(1)})^2 dV\n *          + \\sum_{\\ell \\in L} \\left(\n *                              \\int_{I_{\\ell}} ( \\phi - u^{(\\ell)} ) dV\n *                              \\right)^2,\n * \\f]\n *\n * subject to the constaint\n *\n * \\f[\n * C = \\int_{I_0} ( \\phi - u^{(0)} ) dV = 0.\n * \\f]\n *\n * where \\f$\\ell\\f$ ranges over a subset \\f$L\\f$ of the secondary neighbors.\n * \\f$L\\f$ excludes the one (or more) secondary neighbor(s) where the mean of\n * \\f$u\\f$ is the most different from the mean of \\f$u^{(0)}\\f$. Typically, only\n * one secondary neighbor is excluded, so \\f$L\\f$ contains two fewer neighbors\n * than the total number of neighbors to the element. Note that in 1D, this\n * implies that \\f$L\\f$ is the empty set; for each modified solution, one\n * neighbor is the primary neighbor and the other is the excluded neighbor.\n *\n * The integrals are evaluated by quadrature. We denote the quadrature weights\n * by \\f$w_s\\f$ and the values of some data \\f$X\\f$ at the quadrature\n * nodes by \\f$X_s\\f$. We use subscripts \\f$r,s\\f$ to denote quadrature nodes\n * on the neighbor and local elements, respectively. The minimization becomes\n *\n * \\f[\n * \\chi^2 = \\sum_r w^{(1)}_r ( \\phi_r - u^{(1)}_r )^2\n *          + \\sum_{\\ell \\in L} \\left(\n *                              \\sum_r w^{(\\ell)}_r ( \\phi_r - u^{(\\ell)}_r )\n *                              \\right)^2,\n * \\f]\n *\n * subject to the constraint\n *\n * \\f[\n * C = \\sum_s w^{(0)}_s ( \\phi_s - u^{(0)}_s ) = 0.\n * \\f]\n *\n * Note that \\f$\\phi\\f$ is a function defined on the local element \\f$I_0\\f$,\n * and so is fully represented by its values \\f$\\phi_s\\f$ at the quadrature\n * points on this element. When evaluating \\f$\\phi\\f$ on element \\f$I_{\\ell}\\f$,\n * we obtain the function values \\f$\\phi_r\\f$ by polynomial extrapolation,\n * \\f$\\phi_r = \\sum_s \\mathcal{I}^{(\\ell)}_{rs} \\phi_s\\f$, where\n * \\f$\\mathcal{I}^{(\\ell)}_{rs}\\f$ is the interpolation/extrapolation matrix\n * that interpolates data defined at grid points \\f$x_s\\f$ and evaluates it at\n * grid points \\f$x_r\\f$ of \\f$I_{\\ell}\\f$. Thus,\n *\n * \\f[\n * \\chi^2 = \\sum_r w^{(1)}_r\n *                 \\left(\n *                 \\sum_s \\mathcal{I}^{(1)}_{rs} \\phi_s - u^{(1)}_r\n *                 \\right)^2\n *          + \\sum_{\\ell \\in L} \\left(\n *                              \\sum_r w^{(\\ell)}_r\n *                                     \\left(\n *                                     \\sum_s \\mathcal{I}^{(\\ell)}_{rs} \\phi_s\n *                                             - u^{(\\ell)}_r\n *                                     \\right)\n *                      \\right)^2.\n * \\f]\n *\n * The solution to this optimization problem is found in the standard way,\n * using a Lagrange multiplier \\f$\\lambda\\f$ to impose the constraint:\n *\n * \\f[\n * 0 = \\frac{d}{d \\phi_s} \\left( \\chi^2 + \\lambda C \\right).\n * \\f]\n *\n * Working out the differentiation with respect to \\f$\\phi_s\\f$ leads to the\n * linear problem that must be inverted to obtain the solution,\n *\n * \\f[\n * 0 = A_{st} \\phi_t - b_s - \\lambda w^{(0)}_s,\n * \\f]\n *\n * where\n *\n * \\f{align*}{\n * A_{st} &= \\sum_r \\left( w^{(1)}_r\n *                  \\mathcal{I}^{(1)}_{rs} \\mathcal{I}^{(1)}_{rt}\n *                  \\right)\n *          + \\sum_{\\ell \\in L} \\left(\n *                              \\sum_r \\left(\n *                                     w^{(\\ell)}_r \\mathcal{I}^{(\\ell)}_{rt}\n *                                     \\right)\n *                              \\cdot\n *                              \\sum_r \\left(\n *                                     w^{(\\ell)}_r \\mathcal{I}^{(\\ell)}_{rs}\n *                                     \\right)\n *                              \\right)\n * \\\\\n * b_s &= \\sum_r \\left( w^{(1)}_r u^{(1)}_r \\mathcal{I}^{(1)}_{rs} \\right)\n *         + \\sum_{\\ell \\in L} \\left(\n *                             \\sum_r \\left( w^{(\\ell)}_r u^{(\\ell)}_r \\right)\n *                             \\cdot\n *                             \\sum_r \\left(\n *                                    w^{(\\ell)}_r \\mathcal{I}^{(\\ell)}_{rs}\n *                                    \\right)\n *                             \\right).\n * \\f}\n *\n * Finally, the solution to the constrained fit is\n *\n * \\f{align*}{\n * \\lambda &= - \\frac{ \\sum_s w^{(0)}_s\n *                            \\left( (A^{-1})_{st} b_t - u^{(0)}_s \\right)\n *              }{ \\sum_s w^{(0)}_s (A^{-1})_{st} w^{(0)}_t }\n * \\\\\n * \\phi_s &= (A^{-1})_{st} ( b_t + \\lambda w^{(0)}_t ).\n * \\f}\n *\n * Note that the matrix \\f$A\\f$ does not depend on the values of the tensor\n * \\f$u\\f$, so its inverse \\f$A^{-1}\\f$ can be precomputed and stored.\n *\n * \\warning\n * Note also that the implementation currently does not support h- or\n * p-refinement; this is checked by some assertions. The implementation is\n * untested for grids where elements are curved, and it should not be expected\n * to work in these cases.\n *\n * When calling `hweno_impl`, `modified_neighbor_solution_buffer` should contain\n * one DataVector for each neighboring element (i.e. for each entry in\n * `neighbor_data`).\n */\ntemplate <typename Tag, size_t VolumeDim, typename PackagedData>\nvoid hweno_impl(const gsl::not_null<\n                    std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                                       boost::hash<DirectionalId<VolumeDim>>>*>\n                    modified_neighbor_solution_buffer,\n                const gsl::not_null<typename Tag::type*> tensor,\n                const double neighbor_linear_weight,\n                const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n                const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                                         boost::hash<DirectionalId<VolumeDim>>>&\n                    neighbor_data) {\n  // Check that basis is LGL or LG\n  // The Hweno reconstruction implemented here is specialized to the case of a\n  // Legendre basis. While the underlying algorithm should work for other bases\n  // as well, the matrix computations would need to be generalized.\n  ASSERT(mesh.basis() == make_array<VolumeDim>(Spectral::Basis::Legendre),\n         \"Unsupported basis: \" << mesh);\n  ASSERT(mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::GaussLobatto) or\n             mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::Gauss),\n         \"Unsupported quadrature: \" << mesh);\n\n  ASSERT(\n      modified_neighbor_solution_buffer->size() == neighbor_data.size(),\n      \"modified_neighbor_solution_buffer->size() = \"\n          << modified_neighbor_solution_buffer->size()\n          << \"\\nneighbor_data.size() = \" << neighbor_data.size()\n          << \"\\nmodified_neighbor_solution_buffer was incorrectly initialized \"\n             \"before calling hweno_impl.\");\n\n  alg::for_each(\n      neighbor_data, [&mesh, &element](const auto& neighbor_and_data) {\n        ASSERT(Weno_detail::check_element_has_one_similar_neighbor_in_direction(\n                   element, neighbor_and_data.first.direction()),\n               \"Found some amount of h-refinement; this is not supported\");\n        ASSERT(neighbor_and_data.second.mesh == mesh,\n               \"Found some amount of p-refinement; this is not supported\");\n      });\n\n  for (size_t tensor_index = 0; tensor_index < tensor->size(); ++tensor_index) {\n    const auto& tensor_component = (*tensor)[tensor_index];\n    for (const auto& neighbor_and_data : neighbor_data) {\n      const auto& primary_neighbor = neighbor_and_data.first;\n      const auto neighbors_to_exclude =\n          secondary_neighbors_to_exclude_from_fit<Tag>(\n              mean_value(tensor_component, mesh), tensor_index, neighbor_data,\n              primary_neighbor);\n\n      DataVector& buffer =\n          modified_neighbor_solution_buffer->at(primary_neighbor);\n      solve_constrained_fit<Tag>(make_not_null(&buffer), tensor_component,\n                                 tensor_index, mesh, element, neighbor_data,\n                                 primary_neighbor, neighbors_to_exclude);\n    }\n\n    // Sum local and modified neighbor polynomials for the WENO reconstruction\n    Weno_detail::reconstruct_from_weighted_sum(\n        make_not_null(&((*tensor)[tensor_index])), neighbor_linear_weight,\n        Weno_detail::DerivativeWeight::PowTwoEllOverEllFactorial, mesh,\n        *modified_neighbor_solution_buffer);\n  }\n}\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/Krivodonova.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <ostream>\n#include <pup.h>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Limiters {\ntemplate <size_t VolumeDim, typename TagsToLimit>\nclass Krivodonova;\n}  // namespace Limiters\n/// \\endcond\n\nnamespace Limiters {\n/*!\n * \\ingroup LimitersGroup\n * \\brief An implementation of the Krivodonova limiter.\n *\n * The limiter is described in \\cite Krivodonova2007. The Krivodonova limiter\n * works by limiting the highest derivatives/modal coefficients using an\n * aggressive minmod approach, decreasing in derivative/modal coefficient order\n * until no more limiting is necessary. In 3d, the function being limited is\n * expanded as:\n *\n * \\f{align}{\n * u^{l,m,n}=\\sum_{i,j,k=0,0,0}^{N_i,N_j,N_k}c^{l,m,n}_{i,j,k}\n *  P_{i}(\\xi)P_{j}(\\eta)P_{k}(\\zeta)\n * \\f}\n *\n * where \\f$\\left\\{\\xi, \\eta, \\zeta\\right\\}\\f$ are the logical coordinates,\n * \\f$P_{i}\\f$ are the Legendre polynomials, the superscript \\f$\\{l,m,n\\}\\f$\n * represents the element indexed by \\f$l,m,n\\f$, and \\f$N_i,N_j\\f$ and\n * \\f$N_k\\f$ are the number of collocation points minus one in the\n * \\f$\\xi,\\eta,\\f$ and \\f$\\zeta\\f$ direction, respectively. The coefficients are\n * limited according to:\n *\n * \\f{align}{\n * \\tilde{c}^{l,m,n}_{i,j,k}=\\mathrm{minmod}\n *   &\\left(c_{i,j,k}^{l,m,n},\n *          \\alpha_i\\left(c^{l+1,m,n}_{i-1,j,k}-c^{l,m,n}_{i-1,j,k}\\right),\n *          \\alpha_i\\left(c^{l,m,n}_{i-1,j,k}-c^{l-1,m,n}_{i-1,j,k}\\right),\n *     \\right.\\notag \\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_j\\left(c^{l,m+1,n}_{i,j-1,k}-c^{l,m,n}_{i,j-1,k}\\right),\n *          \\alpha_j\\left(c^{l,m,n}_{i,j-1,k}-c^{l,m-1,n}_{i,j-1,k}\\right),\n *     \\notag \\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_k\\left(c^{l,m,n+1}_{i,j,k-1}-c^{l,m,n}_{i,j,k-1}\\right),\n *          \\alpha_k\\left(c^{l,m,n}_{i,j,k-1}-c^{l,m,n-1}_{i,j,k-1}\\right)\n *     \\right),\n * \\label{eq:krivodonova 3d minmod}\n * \\f}\n *\n * where \\f$\\mathrm{minmod}\\f$ is the minmod function defined as\n *\n * \\f{align}{\n *  \\mathrm{minmod}(a,b,c,\\ldots)=\n *  \\left\\{\n *  \\begin{array}{ll}\n *    \\mathrm{sgn}(a)\\min(\\lvert a\\rvert, \\lvert b\\rvert,\n *    \\lvert c\\rvert, \\ldots) & \\mathrm{if} \\;\n *    \\mathrm{sgn}(a)=\\mathrm{sgn}(b)=\\mathrm{sgn}(c)=\\mathrm{sgn}(\\ldots) \\\\\n *    0 & \\mathrm{otherwise}\n *  \\end{array}\\right.\n * \\f}\n *\n * Krivodonova \\cite Krivodonova2007 requires \\f$\\alpha_i\\f$ to be in the range\n *\n * \\f{align*}{\n * \\frac{1}{2(2i-1)}\\le \\alpha_i \\le 1\n * \\f}\n *\n * where the lower bound comes from finite differencing the coefficients between\n * neighbor elements when using Legendre polynomials (see \\cite Krivodonova2007\n * for details). Note that we normalize our Legendre polynomials by \\f$P_i(1) =\n * 1\\f$; this is the normalization \\cite Krivodonova2007 uses in 1D, (but not in\n * 2D), which is why our bounds on \\f$\\alpha_i\\f$ match Eq. 14 of\n * \\cite Krivodonova2007 (but not Eq. 23). We relax the lower bound:\n *\n * \\f{align*}{\n * 0 \\le \\alpha_i \\le 1\n * \\f}\n *\n * to allow different basis functions (e.g. Chebyshev polynomials) and to allow\n * the limiter to be more dissipative if necessary. The same \\f$\\alpha_i\\f$s are\n * used in all dimensions.\n *\n * \\note The only place where the specific choice of 1d basis\n * comes in is the lower bound for the \\f$\\alpha_i\\f$s, and so in general the\n * limiter can be applied to any 1d or tensor product of 1d basis functions.\n *\n * The limiting procedure must be applied from the highest derivatives to the\n * lowest, i.e. the highest coefficients to the lowest. Let us consider a 3d\n * element with \\f$N+1\\f$ coefficients in each dimension and denote the\n * coefficients as \\f$c_{i,j,k}\\f$. Then the limiting procedure starts at\n * \\f$c_{N,N,N}\\f$, followed by \\f$c_{N,N,N-1}\\f$, \\f$c_{N,N-1,N}\\f$, and\n * \\f$c_{N-1,N,N}\\f$. A detailed example is given below. Limiting is stopped if\n * all symmetric pairs of coefficients are left unchanged, i.e.\n * \\f$c_{i,j,k}=\\tilde{c}_{i,j,k}\\f$. By all symmetric coefficients we mean\n * that, for example, \\f$c_{N-i,N-j,N-k}\\f$, \\f$c_{N-j,N-i,N-k}\\f$,\n * \\f$c_{N-k,N-j,N-i}\\f$, \\f$c_{N-j,N-k,N-i}\\f$, \\f$c_{N-i,N-k,N-j}\\f$, and\n * \\f$c_{N-k,N-i,N-j}\\f$ are not limited. As a concrete example, consider a 3d\n * element with 3 collocation points per dimension. Each limited coefficient is\n * defined as (though only computed if needed):\n *\n * \\f{align*}{\n * \\tilde{c}^{l,m,n}_{2,2,2}=\\mathrm{minmod}\n *   &\\left(c_{2,2,2}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,2,2}-c^{l,m,n}_{1,2,2}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,2,2}-c^{l-1,m,n}_{1,2,2}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_2\\left(c^{l,m+1,n}_{2,1,2}-c^{l,m,n}_{2,1,2}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{2,1,2}-c^{l,m-1,n}_{2,1,2}\\right),\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m,n+1}_{2,2,1}-c^{l,m,n}_{2,2,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{2,2,1}-c^{l,m,n-1}_{2,2,1}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{2,2,1}=\\mathrm{minmod}\n *   &\\left(c_{2,2,1}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,2,1}-c^{l,m,n}_{1,2,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,2,1}-c^{l-1,m,n}_{1,2,1}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_2\\left(c^{l,m+1,n}_{2,1,1}-c^{l,m,n}_{2,1,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{2,1,1}-c^{l,m-1,n}_{2,1,1}\\right),\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m,n+1}_{2,2,0}-c^{l,m,n}_{2,2,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{2,2,0}-c^{l,m,n-1}_{2,2,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{2,1,2}=\\mathrm{minmod}\n *   &\\left(c_{2,1,2}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,1,2}-c^{l,m,n}_{1,1,2}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,1,2}-c^{l-1,m,n}_{1,1,2}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_1\\left(c^{l,m+1,n}_{2,0,2}-c^{l,m,n}_{2,0,2}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{2,0,2}-c^{l,m-1,n}_{2,0,2}\\right),\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m,n+1}_{2,1,1}-c^{l,m,n}_{2,1,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{2,1,1}-c^{l,m,n-1}_{2,1,1}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,2,2}=\\mathrm{minmod}\n *   &\\left(c_{1,2,2}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,2,2}-c^{l,m,n}_{0,2,2}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,2,2}-c^{l-1,m,n}_{0,2,2}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_2\\left(c^{l,m+1,n}_{1,1,2}-c^{l,m,n}_{1,1,2}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,1,2}-c^{l,m-1,n}_{1,1,2}\\right),\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m,n+1}_{1,2,1}-c^{l,m,n}_{1,2,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,2,1}-c^{l,m,n-1}_{1,2,1}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{2,2,0}=\\mathrm{minmod}\n *   &\\left(c_{2,2,0}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,2,0}-c^{l,m,n}_{1,2,0}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,2,0}-c^{l-1,m,n}_{1,2,0}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m+1,n}_{2,1,0}-c^{l,m,n}_{2,1,0}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{2,1,0}-c^{l,m-1,n}_{2,1,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{2,0,2}=\\mathrm{minmod}\n *   &\\left(c_{2,0,2}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,0,2}-c^{l,m,n}_{1,0,2}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,0,2}-c^{l-1,m,n}_{1,0,2}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m,n+1}_{2,0,1}-c^{l,m,n}_{2,0,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{2,0,1}-c^{l,m,n-1}_{2,0,1}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{0,2,2}=\\mathrm{minmod}\n *   &\\left(c_{0,2,2}^{l,m,n},\n *          \\alpha_2\\left(c^{l,m+1,n}_{0,1,2}-c^{l,m,n}_{0,1,2}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{0,1,2}-c^{l,m-1,n}_{0,1,2}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m,n+1}_{0,2,1}-c^{l,m,n}_{0,2,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{0,2,1}-c^{l,m,n-1}_{0,2,1}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{2,1,1}=\\mathrm{minmod}\n *   &\\left(c_{2,1,1}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,1,1}-c^{l,m,n}_{1,1,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,1,1}-c^{l-1,m,n}_{1,1,1}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_1\\left(c^{l,m+1,n}_{2,0,1}-c^{l,m,n}_{2,0,1}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{2,0,1}-c^{l,m-1,n}_{2,0,1}\\right),\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m,n+1}_{2,1,0}-c^{l,m,n}_{2,1,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{2,1,0}-c^{l,m,n-1}_{2,1,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,2,1}=\\mathrm{minmod}\n *   &\\left(c_{1,2,1}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,2,1}-c^{l,m,n}_{0,2,1}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,2,1}-c^{l-1,m,n}_{0,2,1}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_2\\left(c^{l,m+1,n}_{1,1,1}-c^{l,m,n}_{1,1,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,1,1}-c^{l,m-1,n}_{1,1,1}\\right),\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m,n+1}_{1,2,0}-c^{l,m,n}_{1,2,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{1,2,0}-c^{l,m,n-1}_{1,2,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,1,2}=\\mathrm{minmod}\n *   &\\left(c_{1,1,2}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,1,2}-c^{l,m,n}_{0,1,2}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,1,2}-c^{l-1,m,n}_{0,1,2}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_1\\left(c^{l,m+1,n}_{1,0,2}-c^{l,m,n}_{1,0,2}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{1,0,2}-c^{l,m-1,n}_{1,0,2}\\right),\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m,n+1}_{1,1,1}-c^{l,m,n}_{1,1,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,1,1}-c^{l,m,n-1}_{1,1,1}\\right)\n *     \\right),\n * \\f}\n * \\f{align*}{\n * \\tilde{c}^{l,m,n}_{2,1,0}=\\mathrm{minmod}\n *   &\\left(c_{2,1,0}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,1,0}-c^{l,m,n}_{1,1,0}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,1,0}-c^{l-1,m,n}_{1,1,0}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m+1,n}_{2,0,0}-c^{l,m,n}_{2,0,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{2,0,0}-c^{l,m-1,n}_{2,0,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{2,0,1}=\\mathrm{minmod}\n *   &\\left(c_{2,0,1}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,0,1}-c^{l,m,n}_{1,0,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,0,1}-c^{l-1,m,n}_{1,0,1}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m,n+1}_{2,0,0}-c^{l,m,n}_{2,0,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{2,0,0}-c^{l,m,n-1}_{2,0,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,2,0}=\\mathrm{minmod}\n *   &\\left(c_{1,2,0}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,2,0}-c^{l,m,n}_{0,2,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,2,0}-c^{l-1,m,n}_{0,2,0}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m+1,n}_{1,1,0}-c^{l,m,n}_{1,1,0}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,1,0}-c^{l,m-1,n}_{1,1,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,0,2}=\\mathrm{minmod}\n *   &\\left(c_{1,0,2}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,0,2}-c^{l,m,n}_{0,0,2}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,0,2}-c^{l-1,m,n}_{0,0,2}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m,n+1}_{1,0,1}-c^{l,m,n}_{1,0,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,0,1}-c^{l,m,n-1}_{1,0,1}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{0,1,2}=\\mathrm{minmod}\n *   &\\left(c_{0,1,2}^{l,m,n},\n *          \\alpha_1\\left(c^{l,m+1,n}_{0,0,2}-c^{l,m,n}_{0,0,2}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,0,2}-c^{l,m-1,n}_{0,0,2}\\right),\n *   \\right. \\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_2\\left(c^{l,m,n+1}_{0,1,1}-c^{l,m,n}_{0,1,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{0,1,1}-c^{l,m,n-1}_{0,1,1}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{0,2,1}=\\mathrm{minmod}\n *   &\\left(c_{0,2,1}^{l,m,n},\n *          \\alpha_2\\left(c^{l,m+1,n}_{0,1,1}-c^{l,m,n}_{0,1,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{0,1,1}-c^{l,m-1,n}_{0,1,1}\\right),\n *   \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m,n+1}_{0,2,0}-c^{l,m,n}_{0,2,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,2,0}-c^{l,m,n-1}_{0,2,0}\\right)\n *     \\right),\n * \\f}\n * \\f{align*}{\n * \\tilde{c}^{l,m,n}_{2,0,0}=\\mathrm{minmod}\n *   &\\left(c_{2,0,0}^{l,m,n},\n *          \\alpha_2\\left(c^{l+1,m,n}_{1,0,0}-c^{l,m,n}_{1,0,0}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{1,0,0}-c^{l-1,m,n}_{1,0,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{0,2,0}=\\mathrm{minmod}\n *   &\\left(c_{0,2,0}^{l,m,n},\n *          \\alpha_2\\left(c^{l,m+1,n}_{0,1,0}-c^{l,m,n}_{0,1,0}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{0,1,0}-c^{l,m-1,n}_{0,1,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{0,0,2}=\\mathrm{minmod}\n *   &\\left(c_{0,0,2}^{l,m,n},\n *          \\alpha_2\\left(c^{l,m,n+1}_{0,0,1}-c^{l,m,n}_{0,0,1}\\right),\n *          \\alpha_2\\left(c^{l,m,n}_{0,0,1}-c^{l,m,n-1}_{0,0,1}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,1,1}=\\mathrm{minmod}\n *   &\\left(c_{1,1,1}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,1,1}-c^{l,m,n}_{0,1,1}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,1,1}-c^{l-1,m,n}_{0,1,1}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\n *          \\alpha_1\\left(c^{l,m+1,n}_{1,0,1}-c^{l,m,n}_{1,0,1}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{1,0,1}-c^{l,m-1,n}_{1,0,1}\\right),\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m,n+1}_{1,1,0}-c^{l,m,n}_{1,1,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{1,1,0}-c^{l,m,n-1}_{1,1,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,1,0}=\\mathrm{minmod}\n *   &\\left(c_{1,1,0}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,1,0}-c^{l,m,n}_{0,1,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,1,0}-c^{l-1,m,n}_{0,1,0}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m+1,n}_{1,0,0}-c^{l,m,n}_{1,0,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{1,0,0}-c^{l,m-1,n}_{1,0,0}\\right),\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,0,1}=\\mathrm{minmod}\n *   &\\left(c_{1,0,1}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,0,1}-c^{l,m,n}_{0,0,1}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,0,1}-c^{l-1,m,n}_{0,0,1}\\right),\n *     \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m,n+1}_{1,0,0}-c^{l,m,n}_{1,0,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{1,0,0}-c^{l,m,n-1}_{1,0,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{0,1,1}=\\mathrm{minmod}\n *   &\\left(c_{0,1,1}^{l,m,n},\n *          \\alpha_1\\left(c^{l,m+1,n}_{0,0,1}-c^{l,m,n}_{0,0,1}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,0,1}-c^{l,m-1,n}_{0,0,1}\\right),\n *   \\right.\\\\\n *   &\\;\\;\\;\\;\\left.\n *          \\alpha_1\\left(c^{l,m,n+1}_{0,1,0}-c^{l,m,n}_{0,1,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,1,0}-c^{l,m,n-1}_{0,1,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{1,0,0}=\\mathrm{minmod}\n *   &\\left(c_{1,0,0}^{l,m,n},\n *          \\alpha_1\\left(c^{l+1,m,n}_{0,0,0}-c^{l,m,n}_{0,0,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,0,0}-c^{l-1,m,n}_{0,0,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{0,1,0}=\\mathrm{minmod}\n *   &\\left(c_{0,1,0}^{l,m,n},\n *          \\alpha_1\\left(c^{l,m+1,n}_{0,0,0}-c^{l,m,n}_{0,0,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,0,0}-c^{l,m-1,n}_{0,0,0}\\right)\n *     \\right),\\\\\n * \\tilde{c}^{l,m,n}_{0,0,1}=\\mathrm{minmod}\n *   &\\left(c_{0,0,1}^{l,m,n},\n *          \\alpha_1\\left(c^{l,m,n+1}_{0,0,0}-c^{l,m,n}_{0,0,0}\\right),\n *          \\alpha_1\\left(c^{l,m,n}_{0,0,0}-c^{l,m,n-1}_{0,0,0}\\right)\n *     \\right),\n * \\f}\n *\n *\n * The algorithm to perform the limiting is as follows:\n *\n * - limit \\f$c_{2,2,2}\\f$ (i.e. \\f$c_{2,2,2}\\leftarrow\\tilde{c}_{2,2,2}\\f$), if\n *   not changed, stop\n * - limit \\f$c_{2,2,1}\\f$, \\f$c_{2,1,2}\\f$, and \\f$c_{1,2,2}\\f$, if all not\n * changed, stop\n * - limit \\f$c_{2,2,0}\\f$, \\f$c_{2,0,2}\\f$, and \\f$c_{0,2,2}\\f$, if all not\n * changed, stop\n * - limit \\f$c_{2,1,1}\\f$, \\f$c_{1,2,1}\\f$, and \\f$c_{1,1,2}\\f$, if all not\n * changed, stop\n * - limit \\f$c_{2,1,0}\\f$, \\f$c_{2,0,1}\\f$, \\f$c_{1,2,0}\\f$, \\f$c_{1,0,2}\\f$,\n * \\f$c_{0,1,2}\\f$, and \\f$c_{0,2,1}\\f$, if all not changed, stop\n * - limit \\f$c_{2,0,0}\\f$, \\f$c_{0,2,0}\\f$, and \\f$c_{0,0,2}\\f$, if all not\n * changed, stop\n * - limit \\f$c_{1,1,1}\\f$, if not changed, stop\n * - limit \\f$c_{1,1,0}\\f$, \\f$c_{1,0,1}\\f$, and \\f$c_{0,1,1}\\f$, if all not\n * changed, stop\n * - limit \\f$c_{1,0,0}\\f$, \\f$c_{0,1,0}\\f$, and \\f$c_{0,0,1}\\f$, if all not\n * changed, stop\n *\n * The 1d and 2d implementations are straightforward restrictions of the\n * described algorithm.\n *\n * #### Limitations:\n *\n * - We currently recompute the spectral coefficients for local use, after\n *   having computed these same coefficients for sending to the neighbors. We\n *   should be able to avoid this either by storing the coefficients in the\n *   DataBox or by allowing the limiters' `packaged_data` function to return an\n *   object to be passed as an additional argument to the `operator()` (still\n *   would need to be stored in the DataBox).\n * - We cannot handle the case where neighbors have more/fewer\n *   coefficients. In the case that the neighbor has more coefficients we could\n *   just ignore the higher coefficients. In the case that the neighbor has\n *   fewer coefficients we have a few choices.\n * - Having a different number of collocation points in different directions is\n *   not supported. However, it is straightforward to handle this case. The\n *   outermost loop should be in the direction with the most collocation points,\n *   while the inner most loop should be over the direction with the fewest\n *   collocation points. The highest to lowest coefficients can then be limited\n *   appropriately again.\n * - h-refinement is not supported, but there is one reasonably straightforward\n *   implementation that may work. In this case we would ignore refinement that\n *   is not in the direction of the neighbor, treating the element as\n *   simply having multiple neighbors in that direction. The only change would\n *   be accounting for different refinement in the direction of the neighor,\n *   which should be easy to add since the differences in coefficients in\n *   Eq.\\f$\\ref{eq:krivodonova 3d minmod}\\f$ will just be multiplied by\n *   non-unity factors.\n */\ntemplate <size_t VolumeDim, typename... Tags>\nclass Krivodonova<VolumeDim, tmpl::list<Tags...>> {\n public:\n  /*!\n   * \\brief The \\f$\\alpha_i\\f$ values in the Krivodonova algorithm.\n   */\n  struct Alphas {\n    using type = std::array<\n        double, Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>;\n    static constexpr Options::String help = {\n        \"The alpha parameters of the Krivodonova limiter\"};\n  };\n  /*!\n   * \\brief Turn the limiter off\n   *\n   * This option exists to temporarily disable the limiter for debugging\n   * purposes. For problems where limiting is not needed, the preferred\n   * approach is to not compile the limiter into the executable.\n   */\n  struct DisableForDebugging {\n    using type = bool;\n    static type suggested_value() { return false; }\n    static constexpr Options::String help = {\"Disable the limiter\"};\n  };\n\n  using options = tmpl::list<Alphas, DisableForDebugging>;\n  static constexpr Options::String help = {\n      \"The hierarchical limiter of Krivodonova.\\n\\n\"\n      \"This limiter works by limiting the highest modal \"\n      \"coefficients/derivatives using an aggressive minmod approach, \"\n      \"decreasing in modal coefficient order until no more limiting is \"\n      \"necessary.\"};\n\n  explicit Krivodonova(\n      std::array<double,\n                 Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>\n          alphas,\n      bool disable_for_debugging = false, const Options::Context& context = {});\n\n  Krivodonova() = default;\n  Krivodonova(const Krivodonova&) = default;\n  Krivodonova& operator=(const Krivodonova&) = default;\n  Krivodonova(Krivodonova&&) = default;\n  Krivodonova& operator=(Krivodonova&&) = default;\n  ~Krivodonova() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool operator==(const Krivodonova& rhs) const;\n\n  struct PackagedData {\n    Variables<tmpl::list<::Tags::Modal<Tags>...>> modal_volume_data;\n    Mesh<VolumeDim> mesh;\n\n    // NOLINTNEXTLINE(google-runtime-references)\n    void pup(PUP::er& p) {\n      p | modal_volume_data;\n      p | mesh;\n    }\n  };\n\n  using package_argument_tags =\n      tmpl::list<Tags..., domain::Tags::Mesh<VolumeDim>>;\n\n  /// \\brief Package data for sending to neighbor elements.\n  void package_data(gsl::not_null<PackagedData*> packaged_data,\n                    const typename Tags::type&... tensors,\n                    const Mesh<VolumeDim>& mesh,\n                    const OrientationMap<VolumeDim>& orientation_map) const;\n\n  using limit_tags = tmpl::list<Tags...>;\n  using limit_argument_tags = tmpl::list<domain::Tags::Element<VolumeDim>,\n                                         domain::Tags::Mesh<VolumeDim>>;\n\n  bool operator()(\n      const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,\n      const Element<VolumeDim>& element, const Mesh<VolumeDim>& mesh,\n      const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                               boost::hash<DirectionalId<VolumeDim>>>&\n          neighbor_data) const;\n\n private:\n  template <typename Tag>\n  char limit_one_tensor(\n      gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*> coeffs_self,\n      gsl::not_null<bool*> limited_any_component, const Mesh<1>& mesh,\n      const std::unordered_map<DirectionalId<1>, PackagedData,\n                               boost::hash<DirectionalId<1>>>& neighbor_data)\n      const;\n  template <typename Tag>\n  char limit_one_tensor(\n      gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*> coeffs_self,\n      gsl::not_null<bool*> limited_any_component, const Mesh<2>& mesh,\n      const std::unordered_map<DirectionalId<2>, PackagedData,\n                               boost::hash<DirectionalId<2>>>& neighbor_data)\n      const;\n  template <typename Tag>\n  char limit_one_tensor(\n      gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*> coeffs_self,\n      gsl::not_null<bool*> limited_any_component, const Mesh<3>& mesh,\n      const std::unordered_map<DirectionalId<3>, PackagedData,\n                               boost::hash<DirectionalId<3>>>& neighbor_data)\n      const;\n\n  template <typename Tag, size_t Dim>\n  char fill_variables_tag_with_spectral_coeffs(\n      gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>\n          modal_coeffs,\n      const typename Tag::type& nodal_tensor, const Mesh<Dim>& mesh) const;\n\n  std::array<double,\n             Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>\n      alphas_ = make_array<\n          Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(\n          std::numeric_limits<double>::signaling_NaN());\n  bool disable_for_debugging_{false};\n};\n\ntemplate <size_t VolumeDim, typename... Tags>\nKrivodonova<VolumeDim, tmpl::list<Tags...>>::Krivodonova(\n    std::array<double,\n               Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>\n        alphas,\n    bool disable_for_debugging, const Options::Context& context)\n    : alphas_(alphas), disable_for_debugging_(disable_for_debugging) {\n  // See the main documentation for an explanation of why these bounds are\n  // different from those of Krivodonova 2007\n  if (alg::any_of(alphas_,\n                  [](const double t) { return t > 1.0 or t <= 0.0; })) {\n    PARSE_ERROR(context,\n                \"The alphas in the Krivodonova limiter must be in the range \"\n                \"(0,1].\");\n  }\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nvoid Krivodonova<VolumeDim, tmpl::list<Tags...>>::package_data(\n    const gsl::not_null<PackagedData*> packaged_data,\n    const typename Tags::type&... tensors, const Mesh<VolumeDim>& mesh,\n    const OrientationMap<VolumeDim>& orientation_map) const {\n  if (UNLIKELY(disable_for_debugging_)) {\n    // Do not initialize packaged_data\n    return;\n  }\n\n  packaged_data->modal_volume_data.initialize(mesh.number_of_grid_points());\n  // perform nodal coefficients to modal coefficients transformation on each\n  // tensor component\n  expand_pack(fill_variables_tag_with_spectral_coeffs<Tags>(\n      &(packaged_data->modal_volume_data), tensors, mesh)...);\n\n  packaged_data->modal_volume_data = orient_variables(\n      packaged_data->modal_volume_data, mesh.extents(), orientation_map);\n\n  // This doesn't currently do anything because the mesh is assumed to be the\n  // same in all dimensions\n  packaged_data->mesh = orientation_map(mesh);\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nbool Krivodonova<VolumeDim, tmpl::list<Tags...>>::operator()(\n    const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,\n    const Element<VolumeDim>& element, const Mesh<VolumeDim>& mesh,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) const {\n  if (UNLIKELY(disable_for_debugging_)) {\n    // Do not modify input tensors\n    return false;\n  }\n  if (UNLIKELY(mesh != Mesh<VolumeDim>(mesh.extents()[0], mesh.basis()[0],\n                                       mesh.quadrature()[0]))) {\n    ERROR(\n        \"The Krivodonova limiter does not yet support non-uniform number of \"\n        \"collocation points, bases, and quadrature in each direction. The \"\n        \"mesh is: \"\n        << mesh);\n  }\n  if (UNLIKELY(\n          alg::any_of(element.neighbors(), [](const auto& direction_neighbors) {\n            return direction_neighbors.second.size() != 1;\n          }))) {\n    ERROR(\"The Krivodonova limiter does not yet support h-refinement\");\n  }\n  alg::for_each(neighbor_data, [&mesh](const auto& id_packaged_data) {\n    if (UNLIKELY(id_packaged_data.second.mesh != mesh)) {\n      ERROR(\n          \"The Krivodonova limiter does not yet support differing meshes \"\n          \"between neighbors. Self mesh is: \"\n          << mesh << \" neighbor mesh is: \" << id_packaged_data.second.mesh);\n    }\n  });\n\n  // Compute local modal coefficients\n  Variables<tmpl::list<::Tags::Modal<Tags>...>> coeffs_self(\n      mesh.number_of_grid_points(), 0.0);\n  expand_pack(fill_variables_tag_with_spectral_coeffs<Tags>(&coeffs_self,\n                                                            *tensors, mesh)...);\n\n  // Perform the limiting on the modal coefficients\n  bool limited_any_component = false;\n  expand_pack(limit_one_tensor<::Tags::Modal<Tags>>(\n      make_not_null(&coeffs_self), make_not_null(&limited_any_component), mesh,\n      neighbor_data)...);\n\n  // transform back to nodal coefficients\n  const auto wrap_copy_nodal_coeffs =\n      [&mesh, &coeffs_self](auto tag, const auto tensor) {\n        auto& coeffs_tensor = get<decltype(tag)>(coeffs_self);\n        auto tensor_it = tensor->begin();\n        for (auto coeffs_it = coeffs_tensor.begin();\n             coeffs_it != coeffs_tensor.end();\n             (void)++coeffs_it, (void)++tensor_it) {\n          to_nodal_coefficients(make_not_null(&*tensor_it), *coeffs_it, mesh);\n        }\n        return '0';\n      };\n  expand_pack(wrap_copy_nodal_coeffs(::Tags::Modal<Tags>{}, tensors)...);\n\n  return limited_any_component;\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\ntemplate <typename Tag>\nchar Krivodonova<VolumeDim, tmpl::list<Tags...>>::limit_one_tensor(\n    const gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>\n        coeffs_self,\n    const gsl::not_null<bool*> limited_any_component, const Mesh<1>& mesh,\n    const std::unordered_map<DirectionalId<1>, PackagedData,\n                             boost::hash<DirectionalId<1>>>& neighbor_data)\n    const {\n  using tensor_type = typename Tag::type;\n  for (size_t storage_index = 0; storage_index < tensor_type::size();\n       ++storage_index) {\n    // for each coefficient...\n    for (size_t i = mesh.extents()[0] - 1; i > 0; --i) {\n      auto& self_coeffs = get<Tag>(*coeffs_self)[storage_index];\n      double min_abs_coeff = std::abs(self_coeffs[i]);\n      const double sgn_of_coeff = sgn(self_coeffs[i]);\n      bool sgns_all_equal = true;\n      for (const auto& kv : neighbor_data) {\n        const auto& neighbor_coeffs =\n            get<Tag>(kv.second.modal_volume_data)[storage_index];\n        const double tmp = kv.first.direction().sign() * gsl::at(alphas_, i) *\n                           (neighbor_coeffs[i - 1] - self_coeffs[i - 1]);\n\n        min_abs_coeff = std::min(min_abs_coeff, std::abs(tmp));\n        sgns_all_equal &= sgn(tmp) == sgn_of_coeff;\n        if (not sgns_all_equal) {\n          self_coeffs[i] = 0.0;\n          break;\n        }\n      }\n      if (sgns_all_equal) {\n        const double tmp = sgn_of_coeff * min_abs_coeff;\n        if (tmp == self_coeffs[i]) {\n          break;\n        }\n        *limited_any_component |= true;\n        self_coeffs[i] = tmp;\n      }\n    }\n  }\n  return '0';\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\ntemplate <typename Tag>\nchar Krivodonova<VolumeDim, tmpl::list<Tags...>>::limit_one_tensor(\n    const gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>\n        coeffs_self,\n    const gsl::not_null<bool*> limited_any_component, const Mesh<2>& mesh,\n    const std::unordered_map<DirectionalId<2>, PackagedData,\n                             boost::hash<DirectionalId<2>>>& neighbor_data)\n    const {\n  using tensor_type = typename Tag::type;\n  const auto minmod = [&coeffs_self, &mesh, &neighbor_data, this](\n                          const size_t local_i, const size_t local_j,\n                          const size_t local_tensor_storage_index) {\n    const auto& self_coeffs =\n        get<Tag>(*coeffs_self)[local_tensor_storage_index];\n    double min_abs_coeff = std::abs(\n        self_coeffs[mesh.storage_index(Index<VolumeDim>{local_i, local_j})]);\n    const double sgn_of_coeff = sgn(\n        self_coeffs[mesh.storage_index(Index<VolumeDim>{local_i, local_j})]);\n    for (const auto& kv : neighbor_data) {\n      const Direction<2>& dir = kv.first.direction();\n      const auto& neighbor_coeffs =\n          get<Tag>(kv.second.modal_volume_data)[local_tensor_storage_index];\n      const size_t index_i =\n          dir.axis() == Direction<VolumeDim>::Axis::Xi ? local_i - 1 : local_i;\n      const size_t index_j =\n          dir.axis() == Direction<VolumeDim>::Axis::Eta ? local_j - 1 : local_j;\n      // skip neighbors where we cannot compute a finite difference in that\n      // direction because we are already at the lowest coefficient.\n      if (index_i == std::numeric_limits<size_t>::max() or\n          index_j == std::numeric_limits<size_t>::max()) {\n        continue;\n      }\n      const size_t alpha_index =\n          dir.axis() == Direction<VolumeDim>::Axis::Xi ? local_i : local_j;\n      const double tmp =\n          dir.sign() * gsl::at(alphas_, alpha_index) *\n          (neighbor_coeffs[mesh.storage_index(\n               Index<VolumeDim>{index_i, index_j})] -\n           self_coeffs[mesh.storage_index(Index<VolumeDim>{index_i, index_j})]);\n\n      min_abs_coeff = std::min(min_abs_coeff, std::abs(tmp));\n      if (sgn(tmp) != sgn_of_coeff) {\n        return 0.0;\n      }\n    }\n    return sgn_of_coeff * min_abs_coeff;\n  };\n\n  for (size_t tensor_storage_index = 0;\n       tensor_storage_index < tensor_type::size(); ++tensor_storage_index) {\n    // for each coefficient...\n    for (size_t i = mesh.extents()[0] - 1; i > 0; --i) {\n      for (size_t j = i; j < mesh.extents()[1]; --j) {\n        // Check if we are done limiting, and if so we move on to the next\n        // tensor component.\n        auto& self_coeffs = get<Tag>(*coeffs_self)[tensor_storage_index];\n        // We treat the different cases separately to reduce the number of\n        // times we call minmod, not because it is required for correctness.\n        if (UNLIKELY(i == j)) {\n          const double tmp = minmod(i, j, tensor_storage_index);\n          if (tmp == self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j})]) {\n            goto next_tensor_index;\n          }\n          *limited_any_component |= true;\n          self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j})] = tmp;\n        } else {\n          const double tmp_ij = minmod(i, j, tensor_storage_index);\n          const double tmp_ji = minmod(j, i, tensor_storage_index);\n          if (tmp_ij ==\n                  self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j})] and\n              tmp_ji ==\n                  self_coeffs[mesh.storage_index(Index<VolumeDim>{j, i})]) {\n            goto next_tensor_index;\n          }\n          *limited_any_component |= true;\n          self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j})] = tmp_ij;\n          self_coeffs[mesh.storage_index(Index<VolumeDim>{j, i})] = tmp_ji;\n        }\n      }\n    }\n  next_tensor_index:\n    continue;\n  }\n  return '0';\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\ntemplate <typename Tag>\nchar Krivodonova<VolumeDim, tmpl::list<Tags...>>::limit_one_tensor(\n    const gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>\n        coeffs_self,\n    const gsl::not_null<bool*> limited_any_component, const Mesh<3>& mesh,\n    const std::unordered_map<DirectionalId<3>, PackagedData,\n                             boost::hash<DirectionalId<3>>>& neighbor_data)\n    const {\n  using tensor_type = typename Tag::type;\n  const auto minmod = [&coeffs_self, &mesh, &neighbor_data, this](\n                          const size_t local_i, const size_t local_j,\n                          const size_t local_k,\n                          const size_t local_tensor_storage_index) {\n    const auto& self_coeffs =\n        get<Tag>(*coeffs_self)[local_tensor_storage_index];\n    double min_abs_coeff = std::abs(self_coeffs[mesh.storage_index(\n        Index<VolumeDim>{local_i, local_j, local_k})]);\n    const double sgn_of_coeff = sgn(self_coeffs[mesh.storage_index(\n        Index<VolumeDim>{local_i, local_j, local_k})]);\n    for (const auto& kv : neighbor_data) {\n      const Direction<3>& dir = kv.first.direction();\n      const auto& neighbor_coeffs =\n          get<Tag>(kv.second.modal_volume_data)[local_tensor_storage_index];\n      const size_t index_i =\n          dir.axis() == Direction<VolumeDim>::Axis::Xi ? local_i - 1 : local_i;\n      const size_t index_j =\n          dir.axis() == Direction<VolumeDim>::Axis::Eta ? local_j - 1 : local_j;\n      const size_t index_k = dir.axis() == Direction<VolumeDim>::Axis::Zeta\n                                 ? local_k - 1\n                                 : local_k;\n      // skip neighbors where we cannot compute a finite difference in that\n      // direction because we are already at the lowest coefficient.\n      if (index_i == std::numeric_limits<size_t>::max() or\n          index_j == std::numeric_limits<size_t>::max() or\n          index_k == std::numeric_limits<size_t>::max()) {\n        continue;\n      }\n      const size_t alpha_index =\n          dir.axis() == Direction<VolumeDim>::Axis::Xi\n              ? local_i\n              : dir.axis() == Direction<VolumeDim>::Axis::Eta ? local_j\n                                                              : local_k;\n      const double tmp = dir.sign() * gsl::at(alphas_, alpha_index) *\n                         (neighbor_coeffs[mesh.storage_index(\n                              Index<VolumeDim>{index_i, index_j, index_k})] -\n                          self_coeffs[mesh.storage_index(\n                              Index<VolumeDim>{index_i, index_j, index_k})]);\n\n      min_abs_coeff = std::min(min_abs_coeff, std::abs(tmp));\n      if (sgn(tmp) != sgn_of_coeff) {\n        return 0.0;\n      }\n    }\n    return sgn_of_coeff * min_abs_coeff;\n  };\n\n  for (size_t tensor_storage_index = 0;\n       tensor_storage_index < tensor_type::size(); ++tensor_storage_index) {\n    // for each coefficient...\n    for (size_t i = mesh.extents()[0] - 1; i > 0; --i) {\n      for (size_t j = i; j < mesh.extents()[1]; --j) {\n        for (size_t k = j; k < mesh.extents()[2]; --k) {\n          // Check if we are done limiting, and if so we move on to the next\n          // tensor component.\n          auto& self_coeffs = get<Tag>(*coeffs_self)[tensor_storage_index];\n          // We treat the different cases separately to reduce the number of\n          // times we call minmod, not because it is required for correctness.\n          //\n          // Note that the case `i == k and i != j` cannot be encountered since\n          // the loop bounds are `i >= j >= k`.\n          if (UNLIKELY(i == j and i == k)) {\n            const double tmp = minmod(i, j, k, tensor_storage_index);\n            if (tmp ==\n                self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})]) {\n              goto next_tensor_index;\n            }\n            *limited_any_component |= true;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})] = tmp;\n          } else if (i == j and i != k) {\n            const double tmp_ijk = minmod(i, j, k, tensor_storage_index);\n            const double tmp_ikj = minmod(i, k, j, tensor_storage_index);\n            const double tmp_kij = minmod(k, i, j, tensor_storage_index);\n            if (tmp_ijk == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{i, j, k})] and\n                tmp_ikj == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{i, k, j})] and\n                tmp_kij == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{k, i, j})]) {\n              goto next_tensor_index;\n            }\n            *limited_any_component |= true;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})] =\n                tmp_ijk;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{i, k, j})] =\n                tmp_ikj;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{k, i, j})] =\n                tmp_kij;\n          } else if (i != j and j == k) {\n            const double tmp_ijk = minmod(i, j, k, tensor_storage_index);\n            const double tmp_kij = minmod(k, i, j, tensor_storage_index);\n            const double tmp_kji = minmod(k, j, i, tensor_storage_index);\n            if (tmp_ijk == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{i, j, k})] and\n                tmp_kij == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{k, i, j})] and\n                tmp_kji == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{k, j, i})]) {\n              goto next_tensor_index;\n            }\n            *limited_any_component |= true;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})] =\n                tmp_ijk;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{k, i, j})] =\n                tmp_kij;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{k, j, i})] =\n                tmp_kji;\n          } else {\n            const double tmp_ijk = minmod(i, j, k, tensor_storage_index);\n            const double tmp_jik = minmod(j, i, k, tensor_storage_index);\n            const double tmp_ikj = minmod(i, k, j, tensor_storage_index);\n            const double tmp_jki = minmod(j, k, i, tensor_storage_index);\n            const double tmp_kij = minmod(k, i, j, tensor_storage_index);\n            const double tmp_kji = minmod(k, j, i, tensor_storage_index);\n            if (tmp_ijk == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{i, j, k})] and\n                tmp_jik == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{j, i, k})] and\n                tmp_ikj == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{i, k, j})] and\n                tmp_jki == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{j, k, i})] and\n                tmp_kij == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{k, i, j})] and\n                tmp_kji == self_coeffs[mesh.storage_index(\n                               Index<VolumeDim>{k, j, i})]) {\n              goto next_tensor_index;\n            }\n            *limited_any_component |= true;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{i, j, k})] =\n                tmp_ijk;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{j, i, k})] =\n                tmp_jik;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{i, k, j})] =\n                tmp_ikj;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{j, k, i})] =\n                tmp_jki;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{k, i, j})] =\n                tmp_kij;\n            self_coeffs[mesh.storage_index(Index<VolumeDim>{k, j, i})] =\n                tmp_kji;\n          }\n        }\n      }\n    }\n  next_tensor_index:\n    continue;\n  }\n  return '0';\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\ntemplate <typename Tag, size_t Dim>\nchar Krivodonova<VolumeDim, tmpl::list<Tags...>>::\n    fill_variables_tag_with_spectral_coeffs(\n        const gsl::not_null<Variables<tmpl::list<::Tags::Modal<Tags>...>>*>\n            modal_coeffs,\n        const typename Tag::type& nodal_tensor, const Mesh<Dim>& mesh) const {\n  auto& coeffs_tensor = get<::Tags::Modal<Tag>>(*modal_coeffs);\n  auto tensor_it = nodal_tensor.begin();\n  for (auto coeffs_it = coeffs_tensor.begin(); coeffs_it != coeffs_tensor.end();\n       (void)++coeffs_it, (void)++tensor_it) {\n    to_modal_coefficients(make_not_null(&*coeffs_it), *tensor_it, mesh);\n  }\n  return '0';\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nvoid Krivodonova<VolumeDim, tmpl::list<Tags...>>::pup(PUP::er& p) {\n  p | alphas_;\n  p | disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nbool Krivodonova<VolumeDim, tmpl::list<Tags...>>::operator==(\n    const Krivodonova<VolumeDim, tmpl::list<Tags...>>& rhs) const {\n  return alphas_ == rhs.alphas_ and\n         disable_for_debugging_ == rhs.disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nbool operator!=(const Krivodonova<VolumeDim, tmpl::list<Tags...>>& lhs,\n                const Krivodonova<VolumeDim, tmpl::list<Tags...>>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace Limiters\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/Limiters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup LimitersGroup\n/// \\brief Things relating to limiting.\nnamespace Limiters {}\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/Minmod.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Direction;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t VolumeDim>\nclass Element;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass Mesh;\ntemplate <size_t VolumeDim>\nclass OrientationMap;\n\nnamespace boost {\ntemplate <class T>\nstruct hash;\n}  // namespace boost\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace Limiters::Minmod_detail {\ntemplate <size_t VolumeDim>\nclass BufferWrapper;\n}  // namespace Limiters::Minmod_detail\n\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\ntemplate <size_t VolumeDim>\nstruct Element;\ntemplate <size_t VolumeDim>\nstruct Mesh;\ntemplate <size_t VolumeDim>\nstruct SizeOfElement;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace Limiters {\n/// \\ingroup LimitersGroup\n/// \\brief A minmod-based generalized slope limiter\n///\n/// Implements the three minmod-based generalized slope limiters from\n/// \\cite Cockburn1999 Sec. 2.4: \\f$\\Lambda\\Pi^1\\f$, \\f$\\Lambda\\Pi^N\\f$, and\n/// MUSCL. The implementation is system-agnostic and can act on an arbitrary\n/// set of tensors.\n///\n/// #### Summary of the generalized slope limiter algorithms:\n///\n/// The MUSCL and \\f$\\Lambda\\Pi^1\\f$ limiters are both intended for use on\n/// piecewise-linear solutions, i.e., on linear-order elements with two points\n/// per dimension. These limiters operate by reducing the spatial slope of the\n/// tensor components if the data look like they may contain oscillations.\n/// Between these two, MUSCL is more dissipative --- it more aggressively\n/// reduces the slopes of the data, so it may better handle strong shocks, but\n/// it correspondingly produces the most broadening of features in the solution.\n///\n/// Note that we do not _require_ the MUSCL and \\f$\\Lambda\\Pi^1\\f$ limiters to\n/// be used on linear-order elements. However, when they are used on a\n/// higher-resolution grid, the limiters act to linearize the solution (by\n/// discarding higher-order mode content) whether or not the slopes must be\n/// reduced.\n///\n/// The \\f$\\Lambda\\Pi^N\\f$ limiter is intended for use with higher-order\n/// elements (with more than two points per dimension), where the solution is a\n/// piecewise polynomial of higher-than-linear order. This limiter generalizes\n/// \\f$\\Lambda\\Pi^1\\f$: the post-limiter solution is the linearized solution of\n/// \\f$\\Lambda\\Pi^1\\f$ in the case that the slopes must be reduced, but is the\n/// original (higher-order) data in the case that the slopes are acceptable.\n///\n/// For all three types of minmod limiter, the algorithm can be relaxed from\n/// TVD (total variation diminishing) in the means to TVB (total variation\n/// bound) in the means. This may avoid limiting away smooth extrema in the\n/// solution that would otherwise look like spurious oscillations. When this\n/// correction is enabled, the limiter will not reduce the slope (but may still\n/// linearize) on elements where the slope is less than \\f$m h^2\\f$, where\n/// \\f$m\\f$ is the TVB constant and \\f$h\\f$ is the size of the DG element.\n/// Note the \"in the means\" qualifier: the limiter controls the oscillation\n/// between the mean solution values across neighboring cells, but may not\n/// control oscillations within the cells.\n///\n/// The choice of the TVB constant \\f$m\\f$ is difficult. Larger values result in\n/// fewer limiter activations, especially near smooth extrema in the solution\n/// --- this can help to avoid incorrectly limiting away these smooth extrema,\n/// but can also result in insufficient limiting of truly spurious oscillations.\n/// The reference uses a value of 50 when presenting the limiter with simple\n/// shock tests, but in general the value \\f$m\\f$ that optimizes between\n/// robustness and minimal loss of accuracy is problem dependent.\n///\n/// #### Notes on the SpECTRE implementation of the generalized slope limiters:\n///\n/// This implementation can act on an arbitrary set of tensors; the limiting\n/// algorithm is applied to each component of each tensor independently. This\n/// is a convenient and general interface. However, when the evolution system\n/// has multiple evolved variables, the recommendation of the reference is to\n/// apply the limiter to the system's characteristic variables to reduce\n/// spurious post-limiting oscillations. In SpECTRE, applying the limiter to\n/// the characteristic variables requires specializing the limiter to each\n/// evolution system.\n///\n/// The limiter acts in the `Frame::ElementLogical` coordinates, because in\n/// these coordinates it is straightforward to formulate the algorithm. This\n/// means the limiter can operate on generic deformed grids --- however, some\n/// things can start to break down, especially on strongly deformed grids:\n/// 1. When the Jacobian (from `Frame::ElementLogical` to `Frame::Inertial`)\n///    varies across the element, then the limiter fails to be conservative.\n///    This is because the integral of a tensor `u` over the element will change\n///    after the limiter activates on `u`.\n/// 2. When there is a sudden change in the size of the elements (perhaps at an\n///    h-refinement boundary, or at the boundary between two blocks with very\n///    different mappings), a smooth solution in `Frame::Inertial` can appear\n///    to have a kink in `Frame::ElementLogical`. The Minmod implementation\n///    includes some (tested but unproven) corrections based on the size of the\n///    elements that try to reduce spurious limiter activations near these fake\n///    kinks.\n///\n/// When an element has multiple neighbors in any direction, an effective mean\n/// and neighbor size in this direction are computed by averaging over the\n/// multiple neighbors. This simple generalization of the minmod limiter enables\n/// it to operate on h-refined grids.\n///\n/// \\tparam VolumeDim The number of spatial dimensions.\n/// \\tparam TagsToLimit A typelist of tags specifying the tensors to limit.\ntemplate <size_t VolumeDim, typename TagsToLimit>\nclass Minmod;\n\ntemplate <size_t VolumeDim, typename... Tags>\nclass Minmod<VolumeDim, tmpl::list<Tags...>> {\n public:\n  /// \\brief The MinmodType\n  ///\n  /// One of `Limiters::MinmodType`. See `Limiters::Minmod`\n  /// documentation for details. Note in particular that on grids with more than\n  /// two points per dimension, the recommended type is `LambdaPiN`.\n  struct Type {\n    using type = MinmodType;\n    static constexpr Options::String help = {\"Type of minmod\"};\n  };\n  /// \\brief The TVB constant\n  ///\n  /// See `Limiters::Minmod` documentation for details. The optimal value of\n  /// this parameter is unfortunately problem-dependent.\n  struct TvbConstant {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\"TVB constant 'm'\"};\n  };\n  /// \\brief Turn the limiter off\n  ///\n  /// This option exists to temporarily disable the limiter for debugging\n  /// purposes. For problems where limiting is not needed, the preferred\n  /// approach is to not compile the limiter into the executable.\n  struct DisableForDebugging {\n    using type = bool;\n    static type suggested_value() { return false; }\n    static constexpr Options::String help = {\"Disable the limiter\"};\n  };\n  using options = tmpl::list<Type, TvbConstant, DisableForDebugging>;\n  static constexpr Options::String help = {\n      \"A minmod-based generalized slope limiter.\\n\"\n      \"The different types of minmod are more or less aggressive in trying\\n\"\n      \"to reduce slopes. The TVB correction allows the limiter to ignore\\n\"\n      \"'small' slopes, and helps to avoid limiting of smooth extrema in the\\n\"\n      \"solution.\\n\"};\n\n  /// \\brief Constuct a Minmod slope limiter\n  ///\n  /// \\param minmod_type The type of Minmod slope limiter.\n  /// \\param tvb_constant The value of the TVB constant.\n  /// \\param disable_for_debugging Switch to turn the limiter off.\n  explicit Minmod(MinmodType minmod_type, double tvb_constant,\n                  bool disable_for_debugging = false);\n\n  Minmod() = default;\n  Minmod(const Minmod& /*rhs*/) = default;\n  Minmod& operator=(const Minmod& /*rhs*/) = default;\n  Minmod(Minmod&& /*rhs*/) = default;\n  Minmod& operator=(Minmod&& /*rhs*/) = default;\n  ~Minmod() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  // To facilitate testing\n  /// \\cond\n  MinmodType minmod_type() const { return minmod_type_; }\n  /// \\endcond\n\n  /// \\brief Data to send to neighbor elements.\n  struct PackagedData {\n    tuples::TaggedTuple<::Tags::Mean<Tags>...> means;\n    std::array<double, VolumeDim> element_size =\n        make_array<VolumeDim>(std::numeric_limits<double>::signaling_NaN());\n\n    // NOLINTNEXTLINE(google-runtime-references)\n    void pup(PUP::er& p) {\n      p | means;\n      p | element_size;\n    }\n  };\n\n  using package_argument_tags =\n      tmpl::list<Tags..., domain::Tags::Mesh<VolumeDim>,\n                 domain::Tags::SizeOfElement<VolumeDim>>;\n\n  /// \\brief Package data for sending to neighbor elements.\n  ///\n  /// The following quantities are stored in `PackagedData` and communicated\n  /// between neighboring elements:\n  /// - the cell-averaged mean of each tensor component, and\n  /// - the size of the cell along each logical coordinate direction.\n  ///\n  /// \\param packaged_data The data package to fill with this element's values.\n  /// \\param tensors The tensors to be averaged and packaged.\n  /// \\param mesh The mesh on which the tensor values are measured.\n  /// \\param element_size The size of the element in inertial coordinates, along\n  ///        each dimension of logical coordinates.\n  /// \\param orientation_map The orientation of the neighbor\n  void package_data(gsl::not_null<PackagedData*> packaged_data,\n                    const typename Tags::type&... tensors,\n                    const Mesh<VolumeDim>& mesh,\n                    const std::array<double, VolumeDim>& element_size,\n                    const OrientationMap<VolumeDim>& orientation_map) const;\n\n  using limit_tags = tmpl::list<Tags...>;\n  using limit_argument_tags =\n      tmpl::list<domain::Tags::Mesh<VolumeDim>,\n                 domain::Tags::Element<VolumeDim>,\n                 domain::Tags::Coordinates<VolumeDim, Frame::ElementLogical>,\n                 domain::Tags::SizeOfElement<VolumeDim>>;\n\n  /// \\brief Limits the solution on the element.\n  ///\n  /// For each component of each tensor, the limiter will (in general) linearize\n  /// the data, then possibly reduce its slope, dimension-by-dimension, until it\n  /// no longer looks oscillatory.\n  ///\n  /// \\param tensors The tensors to be limited.\n  /// \\param mesh The mesh on which the tensor values are measured.\n  /// \\param element The element on which the tensors to limit live.\n  /// \\param logical_coords The element logical coordinates of the mesh\n  ///        gridpoints.\n  /// \\param element_size The size of the element, in the inertial coordinates.\n  /// \\param neighbor_data The data from each neighbor.\n  ///\n  /// \\return whether the limiter modified the solution or not.\n  ///\n  /// \\note The return value is false if the limiter knows it has not modified\n  /// the solution. True return values can indicate:\n  /// - The solution was limited to reduce the slope, whether by a large factor\n  ///   or by a factor only roundoff away from unity.\n  /// - The solution was linearized but not limited.\n  /// - The solution is identical to the input, if the input was a linear\n  ///   function on a higher-order mesh, so that the limiter cannot know that\n  ///   the linearization step did not actually modify the data. This is\n  ///   somewhat contrived and is unlikely to occur outside of code tests or\n  ///   test cases with very clean initial data.\n  bool operator()(\n      const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,\n      const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n      const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>&\n          logical_coords,\n      const std::array<double, VolumeDim>& element_size,\n      const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                               boost::hash<DirectionalId<VolumeDim>>>&\n          neighbor_data) const;\n\n private:\n  template <size_t LocalDim, typename LocalTagList>\n  // NOLINTNEXTLINE(readability-redundant-declaration) false positive\n  friend bool operator==(const Minmod<LocalDim, LocalTagList>& lhs,\n                         const Minmod<LocalDim, LocalTagList>& rhs);\n\n  MinmodType minmod_type_;\n  double tvb_constant_;\n  bool disable_for_debugging_;\n};\n\ntemplate <size_t VolumeDim, typename... Tags>\nMinmod<VolumeDim, tmpl::list<Tags...>>::Minmod(const MinmodType minmod_type,\n                                               const double tvb_constant,\n                                               const bool disable_for_debugging)\n    : minmod_type_(minmod_type),\n      tvb_constant_(tvb_constant),\n      disable_for_debugging_(disable_for_debugging) {\n  ASSERT(tvb_constant >= 0.0, \"The TVB constant must be non-negative.\");\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nvoid Minmod<VolumeDim, tmpl::list<Tags...>>::pup(PUP::er& p) {\n  p | minmod_type_;\n  p | tvb_constant_;\n  p | disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nvoid Minmod<VolumeDim, tmpl::list<Tags...>>::package_data(\n    const gsl::not_null<PackagedData*> packaged_data,\n    const typename Tags::type&... tensors, const Mesh<VolumeDim>& mesh,\n    const std::array<double, VolumeDim>& element_size,\n    const OrientationMap<VolumeDim>& orientation_map) const {\n  if (UNLIKELY(disable_for_debugging_)) {\n    // Do not initialize packaged_data\n    return;\n  }\n\n  const auto wrap_compute_means = [&mesh, &packaged_data](auto tag,\n                                                          const auto tensor) {\n    for (size_t i = 0; i < tensor.size(); ++i) {\n      // Compute the mean using the local orientation of the tensor and mesh:\n      // this avoids the work of reorienting the tensor while giving the same\n      // result.\n      get<::Tags::Mean<decltype(tag)>>(packaged_data->means)[i] =\n          mean_value(tensor[i], mesh);\n    }\n    return '0';\n  };\n  expand_pack(wrap_compute_means(Tags{}, tensors)...);\n  packaged_data->element_size =\n      orientation_map.permute_from_neighbor(element_size);\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nbool Minmod<VolumeDim, tmpl::list<Tags...>>::operator()(\n    const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, VolumeDim>& element_size,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) const {\n  if (UNLIKELY(disable_for_debugging_)) {\n    // Do not modify input tensors\n    return false;\n  }\n\n  DataVector u_lin_buffer(mesh.number_of_grid_points());\n  Minmod_detail::BufferWrapper<VolumeDim> buffer(mesh);\n\n  bool limiter_activated = false;\n  const auto wrap_minmod_impl = [this, &limiter_activated, &element, &mesh,\n                                 &logical_coords, &element_size, &neighbor_data,\n                                 &u_lin_buffer,\n                                 &buffer](auto tag, const auto tensor) {\n    limiter_activated =\n        Minmod_detail::minmod_impl<VolumeDim, decltype(tag)>(\n            &u_lin_buffer, &buffer, tensor, minmod_type_, tvb_constant_, mesh,\n            element, logical_coords, element_size, neighbor_data) or\n        limiter_activated;\n    return '0';\n  };\n  expand_pack(wrap_minmod_impl(Tags{}, tensors)...);\n  return limiter_activated;\n}\n\ntemplate <size_t LocalDim, typename LocalTagList>\nbool operator==(const Minmod<LocalDim, LocalTagList>& lhs,\n                const Minmod<LocalDim, LocalTagList>& rhs) {\n  return lhs.minmod_type_ == rhs.minmod_type_ and\n         lhs.tvb_constant_ == rhs.tvb_constant_ and\n         lhs.disable_for_debugging_ == rhs.disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim, typename TagList>\nbool operator!=(const Minmod<VolumeDim, TagList>& lhs,\n                const Minmod<VolumeDim, TagList>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Limiters\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace Limiters::Minmod_detail {\n\nMinmodResult tvb_corrected_minmod(const double a, const double b,\n                                  const double c, const double tvb_scale) {\n  if (fabs(a) <= tvb_scale) {\n    return {a, false};\n  }\n  if ((std::signbit(a) == std::signbit(b)) and\n      (std::signbit(a) == std::signbit(c))) {\n    // The if/else group below could be more simply written as\n    //   std::copysign(std::min({fabs(a), fabs(b), fabs(c)}), a);\n    // however, by separating different cases, we gain the ability to\n    // distinguish whether or not the limiter activated.\n    if (fabs(a) <= fabs(b) and fabs(a) <= fabs(c)) {\n      return {a, false};\n    } else {\n      return {std::copysign(std::min(fabs(b), fabs(c)), a), true};\n    }\n  } else {\n    return {0.0, true};\n  }\n}\n\ntemplate <size_t VolumeDim>\nBufferWrapper<VolumeDim>::BufferWrapper(const Mesh<VolumeDim>& mesh)\n    : volume_and_slice_buffer_and_indices_(\n          ::volume_and_slice_indices(mesh.extents())),\n      volume_and_slice_indices(volume_and_slice_buffer_and_indices_.second) {\n  const size_t half_number_boundary_points = alg::accumulate(\n      alg::iota(std::array<size_t, VolumeDim>{{}}, 0_st), 0_st,\n      [&mesh](const size_t state, const size_t d) {\n        return state + mesh.slice_away(d).number_of_grid_points();\n      });\n  contiguous_boundary_buffer_ =\n      // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n      cpp20::make_unique_for_overwrite<double[]>(half_number_boundary_points);\n  size_t alloc_offset = 0;\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const size_t num_points = mesh.slice_away(d).number_of_grid_points();\n    gsl::at(boundary_buffers, d)\n        .set_data_ref(contiguous_boundary_buffer_.get() + alloc_offset,\n                      num_points);\n    alloc_offset += num_points;\n  }\n}\n\ntemplate <size_t VolumeDim>\ndouble effective_difference_to_neighbor(\n    const double u_mean, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size, const size_t dim,\n    const Side& side,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes) {\n  const auto dir = Direction<VolumeDim>(dim, side);\n  ASSERT(element.neighbors().contains(dir),\n         \"Minmod helper found no neighbors in direction: \" << dir);\n  const double neighbor_mean = effective_neighbor_means.at(dir);\n  const double neighbor_size = effective_neighbor_sizes.at(dir);\n  const double distance_factor =\n      0.5 * (1.0 + neighbor_size / gsl::at(element_size, dim));\n  return (side == Side::Lower ? -1.0 : 1.0) * (neighbor_mean - u_mean) /\n         distance_factor;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template class Minmod_detail::BufferWrapper<DIM(data)>;                      \\\n  template double effective_difference_to_neighbor<DIM(data)>(                 \\\n      double, const Element<DIM(data)>&, const std::array<double, DIM(data)>&, \\\n      size_t, const Side&, const DirectionMap<DIM(data), double>&,             \\\n      const DirectionMap<DIM(data), double>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Limiters::Minmod_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <memory>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Element;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass Mesh;\nenum class Side : uint8_t;\n\nnamespace boost {\ntemplate <class T>\nstruct hash;\n}  // namespace boost\n/// \\endcond\n\nnamespace Limiters::Minmod_detail {\n\n// Encodes the return status of the tvb_corrected_minmod function.\nstruct MinmodResult {\n  const double value;\n  const bool activated;\n};\n\n// The TVB-corrected minmod function, see e.g. Cockburn reference Eq. 2.26.\nMinmodResult tvb_corrected_minmod(double a, double b, double c,\n                                  double tvb_scale);\n\n// Holds various optimization-related allocations for the Minmod TCI.\n// There is no pup::er, because these allocations should be short-lived (i.e.,\n// scoped within a single limiter invocation).\ntemplate <size_t VolumeDim>\nclass BufferWrapper {\n public:\n  BufferWrapper() = delete;\n  explicit BufferWrapper(const Mesh<VolumeDim>& mesh);\n\n private:\n  std::unique_ptr<double[]> contiguous_boundary_buffer_;\n  const std::pair<std::unique_ptr<std::pair<size_t, size_t>[]>,\n                  std::array<std::pair<gsl::span<std::pair<size_t, size_t>>,\n                                       gsl::span<std::pair<size_t, size_t>>>,\n                             VolumeDim>>\n      volume_and_slice_buffer_and_indices_ {};\n\n public:\n  std::array<DataVector, VolumeDim> boundary_buffers{};\n  const std::array<std::pair<gsl::span<std::pair<size_t, size_t>>,\n                             gsl::span<std::pair<size_t, size_t>>>,\n                   VolumeDim>& volume_and_slice_indices{};\n};\n\n// In each direction, average the size of all different neighbors in that\n// direction. Note that only the component of neighor_size that is normal\n// to the face is needed (and, therefore, computed).\n//\n// Expects type `PackagedData` to contain a variable `element_size` that is a\n// `std::array<double, VolumeDim>`.\ntemplate <size_t VolumeDim, typename PackagedData>\nDirectionMap<VolumeDim, double> compute_effective_neighbor_sizes(\n    const Element<VolumeDim>& element,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) {\n  DirectionMap<VolumeDim, double> result;\n  for (const auto& dir : Direction<VolumeDim>::all_directions()) {\n    const bool neighbors_in_this_dir = element.neighbors().contains(dir);\n    if (neighbors_in_this_dir) {\n      const double effective_neighbor_size = [&dir, &element,\n                                              &neighbor_data]() {\n        const size_t dim = dir.dimension();\n        const auto& neighbor_ids = element.neighbors().at(dir).ids();\n        double size_accumulate = 0.;\n        for (const auto& id : neighbor_ids) {\n          size_accumulate += gsl::at(\n              neighbor_data.at(DirectionalId<VolumeDim>{dir, id}).element_size,\n              dim);\n        }\n        return size_accumulate / neighbor_ids.size();\n      }();\n      result.insert(std::make_pair(dir, effective_neighbor_size));\n    }\n  }\n  return result;\n}\n\n// In each direction, average the mean of the specified tensor component over\n// all different neighbors that direction. This produces one effective neighbor\n// per direction.\n//\n// Expects type `PackagedData` to contain a variable `means` that is a\n// `TaggedTuple<Tags::Mean<Tags>...>`. Tags... must contain Tag, the tag\n// specifying the tensor to work with.\ntemplate <typename Tag, size_t VolumeDim, typename PackagedData>\nDirectionMap<VolumeDim, double> compute_effective_neighbor_means(\n    const size_t tensor_storage_index, const Element<VolumeDim>& element,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) {\n  DirectionMap<VolumeDim, double> result;\n  for (const auto& dir : Direction<VolumeDim>::all_directions()) {\n    const bool neighbors_in_this_dir = element.neighbors().contains(dir);\n    if (neighbors_in_this_dir) {\n      const double effective_neighbor_mean =\n          [&dir, &element, &neighbor_data, &tensor_storage_index]() {\n            const auto& neighbor_ids = element.neighbors().at(dir).ids();\n            double mean_accumulate = 0.0;\n            for (const auto& id : neighbor_ids) {\n              mean_accumulate += tuples::get<::Tags::Mean<Tag>>(\n                  neighbor_data.at(DirectionalId<VolumeDim>{dir, id})\n                      .means)[tensor_storage_index];\n            }\n            return mean_accumulate / neighbor_ids.size();\n          }();\n      result.insert(std::make_pair(dir, effective_neighbor_mean));\n    }\n  }\n  return result;\n}\n\n// Compute an effective element-center-to-neighbor-center distance that accounts\n// for the possibility of different refinement levels or discontinuous maps\n// (e.g., at Block boundaries). Treated naively, these domain features can make\n// a smooth solution appear to be non-smooth in the logical coordinates, which\n// could potentially lead to the limiter triggering erroneously. This effective\n// distance is used to scale the difference in the means, so that a linear\n// function at a refinement or Block boundary will still appear smooth to the\n// limiter. The factor is normalized to be 1.0 on a uniform grid.\n//\n// Note that this is not \"by the book\" Minmod, but an attempt to\n// generalize Minmod to work on non-uniform grids.\ntemplate <size_t VolumeDim>\ndouble effective_difference_to_neighbor(\n    double u_mean, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size, size_t dim,\n    const Side& side,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes);\n\n}  // namespace Limiters::Minmod_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/MinmodImpl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Minmod.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <limits>\n\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Linearize.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Limiters::Minmod_detail {\n\ntemplate <size_t VolumeDim>\nbool minmod_limited_slopes(\n    const gsl::not_null<DataVector*> u_lin_buffer,\n    const gsl::not_null<BufferWrapper<VolumeDim>*> buffer,\n    const gsl::not_null<double*> u_mean,\n    const gsl::not_null<std::array<double, VolumeDim>*> u_limited_slopes,\n    const Limiters::MinmodType minmod_type, const double tvb_constant,\n    const DataVector& u, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes) {\n  // Check that basis is LGL or LG. Note that...\n  // - non-Legendre bases may work \"out of the box\", but are untested\n  // - mixed bases may be okay in principle, but are untested\n  ASSERT(mesh.basis() == make_array<VolumeDim>(Spectral::Basis::Legendre),\n         \"Unsupported basis: \" << mesh);\n  ASSERT(mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::GaussLobatto) or\n             mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::Gauss),\n         \"Unsupported quadrature: \" << mesh);\n\n  const double tvb_scale = [&tvb_constant, &element_size]() {\n    const double max_h =\n        *std::max_element(element_size.begin(), element_size.end());\n    return tvb_constant * square(max_h);\n  }();\n\n  // Results from SpECTRE paper (https://arxiv.org/abs/1609.00098) used a\n  // max_slope_factor a factor of 2.0 too small, so that LambdaPi1 behaved\n  // like MUSCL, and MUSCL was even more dissipative.\n  const double max_slope_factor =\n      (minmod_type == Limiters::MinmodType::Muscl) ? 1.0 : 2.0;\n\n  *u_mean = mean_value(u, mesh);\n\n  const auto difference_to_neighbor =\n      [&u_mean, &element, &element_size, &effective_neighbor_means,\n       &effective_neighbor_sizes](const size_t dim, const Side& side) {\n        return effective_difference_to_neighbor(\n            *u_mean, element, element_size, dim, side, effective_neighbor_means,\n            effective_neighbor_sizes);\n      };\n\n  // The LambdaPiN limiter calls a simple troubled-cell indicator to avoid\n  // limiting solutions that appear smooth:\n  if (minmod_type == Limiters::MinmodType::LambdaPiN) {\n    const bool u_needs_limiting = Tci::tvb_minmod_indicator(\n        buffer, tvb_constant, u, mesh, element, element_size,\n        effective_neighbor_means, effective_neighbor_sizes);\n\n    if (not u_needs_limiting) {\n      // Skip the limiting step for this tensor component\n#ifdef SPECTRE_NAN_INIT\n      *u_mean = std::numeric_limits<double>::signaling_NaN();\n      *u_limited_slopes =\n          make_array<VolumeDim>(std::numeric_limits<double>::signaling_NaN());\n#endif  // ifdef SPECTRE_NAN_INIT\n      return false;\n    }\n  }  // end if LambdaPiN\n\n  // If the LambdaPiN check did not skip the limiting, then proceed as normal\n  // to determine whether the slopes need to be reduced.\n  //\n  // Note that we expect the Muscl and LambdaPi1 limiters to linearize the\n  // solution whether or not the slope needed reduction. To permit this\n  // linearization, we always return (by reference) the slopes when these\n  // limiters are in use. In contrast, for LambdaPiN, we only return the slopes\n  // when they do in fact need to be reduced.\n  bool slopes_need_reducing = false;\n\n  linearize(u_lin_buffer, u, mesh);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const bool has_lower_neighbors =\n        element.neighbors().contains(Direction<VolumeDim>(d, Side::Lower));\n    const bool has_upper_neighbors =\n        element.neighbors().contains(Direction<VolumeDim>(d, Side::Upper));\n    // If there are no neighbors on either side, then there isn't enough data\n    // to apply the limiter. Skip this case.\n    if (UNLIKELY(not has_lower_neighbors and not has_upper_neighbors)) {\n      continue;\n    }\n\n    auto& boundary_buffer_d = gsl::at(buffer->boundary_buffers, d);\n    const auto& volume_and_slice_indices_d =\n        gsl::at(buffer->volume_and_slice_indices, d);\n\n    // Compute slope of the linearized U by finite differencing across the\n    // first and last grid points. When using Gauss points, we also need to\n    // compute the the distance separating these grid points as it will be\n    // less than 2.0.\n    const double u_lower = mean_value_on_boundary(\n        &boundary_buffer_d, volume_and_slice_indices_d.first, *u_lin_buffer,\n        mesh, d, Side::Lower);\n    const double u_upper = mean_value_on_boundary(\n        &boundary_buffer_d, volume_and_slice_indices_d.second, *u_lin_buffer,\n        mesh, d, Side::Upper);\n    const double first_to_last_distance =\n        2.0 *\n        (mesh.quadrature(d) == Spectral::Quadrature::GaussLobatto\n             ? 1.0\n             : fabs(Spectral::collocation_points(mesh.slice_through(d))[0]));\n    const double local_slope = (u_upper - u_lower) / first_to_last_distance;\n\n    // If one side is an external boundary, we can't define a mean-to-mean\n    // difference on that side. We reuse the value of the difference from the\n    // internal side, so that the external side has no effect on the result of\n    // the minmod function. One alternative implementation would be to use a\n    // two-argument minmod function without any arguments corresponding to the\n    // external side, but this requires writing more code. Note that we already\n    // excluded above the case where both sides are external boundaries.\n    //\n    // For the effective slopes to neighboring elements, we don't care about the\n    // grid point distribution, only the element's width in logical coordinates,\n    // which will always be 2.0.\n    double lower_slope = 0.0;\n    double upper_slope = 0.0;\n    if (LIKELY(has_lower_neighbors)) {\n      lower_slope = 0.5 * difference_to_neighbor(d, Side::Lower);\n      upper_slope =\n          (has_upper_neighbors ? 0.5 * difference_to_neighbor(d, Side::Upper)\n                               : lower_slope);\n    } else {\n      upper_slope = 0.5 * difference_to_neighbor(d, Side::Upper);\n      lower_slope = upper_slope;  // no lower neighbors in this branch\n    }\n\n    const MinmodResult result =\n        tvb_corrected_minmod(local_slope, max_slope_factor * upper_slope,\n                             max_slope_factor * lower_slope, tvb_scale);\n    gsl::at(*u_limited_slopes, d) = result.value;\n    if (result.activated) {\n      slopes_need_reducing = true;\n    }\n  }\n\n#ifdef SPECTRE_NAN_INIT\n  // Guard against incorrect use of returned (by reference) slopes in a\n  // LambdaPiN limiter, by setting these to NaN when they should not be used.\n  if (minmod_type == Limiters::MinmodType::LambdaPiN and\n      not slopes_need_reducing) {\n    *u_mean = std::numeric_limits<double>::signaling_NaN();\n    *u_limited_slopes =\n        make_array<VolumeDim>(std::numeric_limits<double>::signaling_NaN());\n  }\n#endif  // ifdef SPECTRE_NAN_INIT\n\n  return slopes_need_reducing;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template bool minmod_limited_slopes<DIM(data)>(                  \\\n      const gsl::not_null<DataVector*>,                            \\\n      const gsl::not_null<BufferWrapper<DIM(data)>*>,              \\\n      const gsl::not_null<double*>,                                \\\n      const gsl::not_null<std::array<double, DIM(data)>*>,         \\\n      const Limiters::MinmodType, const double, const DataVector&, \\\n      const Mesh<DIM(data)>&, const Element<DIM(data)>&,           \\\n      const std::array<double, DIM(data)>&,                        \\\n      const DirectionMap<DIM(data), double>&,                      \\\n      const DirectionMap<DIM(data), double>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Limiters::Minmod_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/MinmodImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Limiters::Minmod_detail {\n\n// This function combines the evaluation of the troubled-cell indicator with the\n// computation of the post-limiter reduced slopes. The returned bool indicates\n// whether the slopes are to be reduced. The slopes themselves are returned by\n// pointer.\n//\n// Note: This function is only made available in this header file to facilitate\n// testing.\ntemplate <size_t VolumeDim>\nbool minmod_limited_slopes(\n    gsl::not_null<DataVector*> u_lin_buffer,\n    gsl::not_null<BufferWrapper<VolumeDim>*> buffer,\n    gsl::not_null<double*> u_mean,\n    gsl::not_null<std::array<double, VolumeDim>*> u_limited_slopes,\n    Limiters::MinmodType minmod_type, double tvb_constant, const DataVector& u,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes);\n\n// Implements the minmod limiter for one Tensor<DataVector> at a time.\ntemplate <size_t VolumeDim, typename Tag, typename PackagedData>\nbool minmod_impl(\n    const gsl::not_null<DataVector*> u_lin_buffer,\n    const gsl::not_null<BufferWrapper<VolumeDim>*> buffer,\n    const gsl::not_null<typename Tag::type*> tensor,\n    const Limiters::MinmodType minmod_type, const double tvb_constant,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, VolumeDim>& element_size,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) {\n  // True if the mesh is linear-order in every direction\n  const bool mesh_is_linear = (mesh.extents() == Index<VolumeDim>(2));\n  const bool minmod_type_is_linear =\n      (minmod_type != Limiters::MinmodType::LambdaPiN);\n  const bool using_linear_limiter_on_non_linear_mesh =\n      minmod_type_is_linear and not mesh_is_linear;\n\n  // In each direction, average the size of all different neighbors in that\n  // direction. Note that only the component of neighor_size that is normal\n  // to the face is needed (and, therefore, computed). Note that this average\n  // does not depend on the solution on the neighboring elements, so could be\n  // precomputed outside of `limit_one_tensor`. Changing the code to\n  // precompute the average may or may not be a measurable optimization.\n  const auto effective_neighbor_sizes =\n      compute_effective_neighbor_sizes(element, neighbor_data);\n\n  bool some_component_was_limited = false;\n  for (size_t i = 0; i < tensor->size(); ++i) {\n    // In each direction, average the mean of the i'th tensor component over\n    // all different neighbors in that direction. This produces one effective\n    // neighbor per direction.\n    const auto effective_neighbor_means =\n        compute_effective_neighbor_means<Tag>(i, element, neighbor_data);\n\n    DataVector& u = (*tensor)[i];\n    double u_mean = std::numeric_limits<double>::signaling_NaN();\n    std::array<double, VolumeDim> u_limited_slopes{};\n    const bool reduce_slopes = minmod_limited_slopes(\n        u_lin_buffer, buffer, make_not_null(&u_mean),\n        make_not_null(&u_limited_slopes), minmod_type, tvb_constant, u, mesh,\n        element, element_size, effective_neighbor_means,\n        effective_neighbor_sizes);\n\n    if (reduce_slopes or using_linear_limiter_on_non_linear_mesh) {\n      u = u_mean;\n      for (size_t d = 0; d < VolumeDim; ++d) {\n        u += logical_coords.get(d) * gsl::at(u_limited_slopes, d);\n      }\n      some_component_was_limited = true;\n    }\n  }  // end for loop over tensor components\n\n  return some_component_was_limited;\n}\n\n}  // namespace Limiters::Minmod_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.hpp\"\n\n#include <algorithm>\n#include <array>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Limiters::Tci {\n\ntemplate <size_t VolumeDim>\nbool tvb_minmod_indicator(\n    const gsl::not_null<Minmod_detail::BufferWrapper<VolumeDim>*> buffer,\n    const double tvb_constant, const DataVector& u, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes) {\n  // Check that basis is LGL or LG. Note that...\n  // - non-Legendre bases may work \"out of the box\", but are untested\n  // - mixed bases may be okay in principle, but are untested\n  ASSERT(mesh.basis() == make_array<VolumeDim>(Spectral::Basis::Legendre),\n         \"Unsupported basis: \" << mesh);\n  ASSERT(mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::GaussLobatto) or\n             mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::Gauss),\n         \"Unsupported quadrature: \" << mesh);\n\n  const double tvb_scale = [&tvb_constant, &element_size]() {\n    const double max_h =\n        *std::max_element(element_size.begin(), element_size.end());\n    return tvb_constant * square(max_h);\n  }();\n  const double u_mean = mean_value(u, mesh);\n\n  const auto difference_to_neighbor =\n      [&u_mean, &element, &element_size, &effective_neighbor_means,\n       &effective_neighbor_sizes](const size_t dim, const Side& side) {\n        return Minmod_detail::effective_difference_to_neighbor(\n            u_mean, element, element_size, dim, side, effective_neighbor_means,\n            effective_neighbor_sizes);\n      };\n\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const bool has_lower_neighbors =\n        element.neighbors().contains(Direction<VolumeDim>(d, Side::Lower));\n    const bool has_upper_neighbors =\n        element.neighbors().contains(Direction<VolumeDim>(d, Side::Upper));\n    // If there are no neighbors on either side, then there isn't enough data\n    // to even check if the limiter needs to be applied. Skip this case.\n    if (UNLIKELY(not has_lower_neighbors and not has_upper_neighbors)) {\n      continue;\n    }\n\n    double u_lower = 0.;\n    double u_upper = 0.;\n    auto& boundary_buffers_d = gsl::at(buffer->boundary_buffers, d);\n\n    // This TCI compares mean-to-neighbor vs mean-to-cell-boundary differences.\n    // In the case of an LGL mesh, the boundary values can be read off directly,\n    // but in the case of a LG mesh, we must interpolate to the boundary.\n    if (mesh.quadrature(d) == Spectral::Quadrature::GaussLobatto) {\n      const auto& volume_and_slice_indices_d =\n          gsl::at(buffer->volume_and_slice_indices, d);\n      u_lower = mean_value_on_boundary(&boundary_buffers_d,\n                                       volume_and_slice_indices_d.first, u,\n                                       mesh, d, Side::Lower);\n      u_upper = mean_value_on_boundary(&boundary_buffers_d,\n                                       volume_and_slice_indices_d.second, u,\n                                       mesh, d, Side::Upper);\n    } else {\n      // We have Spectral::Quadrature::Gauss, so interpolate to boundary\n      const Matrix identity{};\n      auto interpolation_matrices = make_array<VolumeDim>(std::cref(identity));\n      const auto& matrices =\n          Spectral::boundary_interpolation_matrices(mesh.slice_through(d));\n      gsl::at(interpolation_matrices, d) = matrices.first;\n      apply_matrices(make_not_null(&boundary_buffers_d), interpolation_matrices,\n                     u, mesh.extents());\n      const auto boundary_mesh = mesh.slice_away(d);\n      u_lower = mean_value(boundary_buffers_d, boundary_mesh);\n\n      gsl::at(interpolation_matrices, d) = matrices.second;\n      apply_matrices(make_not_null(&boundary_buffers_d), interpolation_matrices,\n                     u, mesh.extents());\n      u_upper = mean_value(boundary_buffers_d, boundary_mesh);\n    }\n\n    // If one side is an external boundary, we can't define a mean-to-mean\n    // difference on that side. We reuse the value of the difference from the\n    // internal side, so that the external side has no effect on the result of\n    // the minmod function. One alternative implementation would be to use a\n    // two-argument minmod function without any arguments corresponding to the\n    // external side, but this requires writing more code. Note that we already\n    // excluded above the case where both sides are external boundaries.\n    double diff_lower = 0.0;\n    double diff_upper = 0.0;\n    if (LIKELY(has_lower_neighbors)) {\n      diff_lower = difference_to_neighbor(d, Side::Lower);\n      diff_upper = (has_upper_neighbors ? difference_to_neighbor(d, Side::Upper)\n                                        : diff_lower);\n    } else {\n      diff_upper = difference_to_neighbor(d, Side::Upper);\n      diff_lower = diff_upper;  // no lower neighbors in this branch\n    }\n\n    // Results from SpECTRE paper (https://arxiv.org/abs/1609.00098) used\n    // tvb_corrected_minmod(..., 0.0), rather than\n    // tvb_corrected_minmod(..., tvb_scale)\n    const bool activated_lower =\n        Minmod_detail::tvb_corrected_minmod(u_mean - u_lower, diff_lower,\n                                            diff_upper, tvb_scale)\n            .activated;\n    const bool activated_upper =\n        Minmod_detail::tvb_corrected_minmod(u_upper - u_mean, diff_lower,\n                                            diff_upper, tvb_scale)\n            .activated;\n    if (activated_lower or activated_upper) {\n      return true;\n    }\n  }\n  return false;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template bool tvb_minmod_indicator<DIM(data)>(                       \\\n      const gsl::not_null<Minmod_detail::BufferWrapper<DIM(data)>*>,   \\\n      const double, const DataVector&, const Mesh<DIM(data)>&,         \\\n      const Element<DIM(data)>&, const std::array<double, DIM(data)>&, \\\n      const DirectionMap<DIM(data), double>&,                          \\\n      const DirectionMap<DIM(data), double>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Limiters::Tci\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <unordered_map>\n#include <utility>\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Direction;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t VolumeDim>\nclass Element;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass Mesh;\n\nnamespace boost {\ntemplate <class T>\nstruct hash;\n}  // namespace boost\n/// \\endcond\n\nnamespace Limiters::Tci {\n\n// Implements the TVB troubled-cell indicator from Cockburn1999.\ntemplate <size_t VolumeDim>\nbool tvb_minmod_indicator(\n    gsl::not_null<Minmod_detail::BufferWrapper<VolumeDim>*> buffer,\n    double tvb_constant, const DataVector& u, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes);\n\n// Implements the TVB troubled-cell indicator from Cockburn1999 for several\n// tensors. Returns true if any component of any tensor needs limiting.\n//\n// Expects type `PackagedData` to contain, as in Limiters::Minmod:\n// - a variable `means` that is a `TaggedTuple<Tags::Mean<Tags>...>`\n// - a variable `element_size` that is a `std::array<double, VolumeDim>`\ntemplate <size_t VolumeDim, typename PackagedData, typename... Tags>\nbool tvb_minmod_indicator(\n    const double tvb_constant, const typename Tags::type&... tensors,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) {\n  Minmod_detail::BufferWrapper<VolumeDim> buffer(mesh);\n  const auto effective_neighbor_sizes =\n      Minmod_detail::compute_effective_neighbor_sizes(element, neighbor_data);\n\n  // Ideally, as soon as one component is found that needs limiting, then we\n  // would exit the TCI early with return value `true`. But there is no natural\n  // way to return early from the pack expansion. So we use this bool to keep\n  // track of whether a previous component needed limiting... and if so, then\n  // we simply skip any work.\n  bool some_component_needs_limiting = false;\n  const auto wrap_tci_one_tensor = [&](auto tag, const auto tensor) {\n    if (some_component_needs_limiting) {\n      // Skip this tensor completely\n      return '0';\n    }\n\n    for (size_t tensor_storage_index = 0; tensor_storage_index < tensor.size();\n         ++tensor_storage_index) {\n      const auto effective_neighbor_means =\n          Minmod_detail::compute_effective_neighbor_means<decltype(tag)>(\n              tensor_storage_index, element, neighbor_data);\n\n      const DataVector& u = tensor[tensor_storage_index];\n      const bool component_needs_limiting = tvb_minmod_indicator(\n          make_not_null(&buffer), tvb_constant, u, mesh, element, element_size,\n          effective_neighbor_means, effective_neighbor_sizes);\n\n      if (component_needs_limiting) {\n        some_component_needs_limiting = true;\n        // Skip remaining components of this tensor\n        return '0';\n      }\n    }\n    return '0';\n  };\n  expand_pack(wrap_tci_one_tensor(Tags{}, tensors)...);\n  return some_component_needs_limiting;\n}\n\n}  // namespace Limiters::Tci\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/MinmodType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nstd::ostream& Limiters::operator<<(std::ostream& os,\n                                   const Limiters::MinmodType minmod_type) {\n  switch (minmod_type) {\n    case Limiters::MinmodType::LambdaPi1:\n      return os << \"LambdaPi1\";\n    case Limiters::MinmodType::LambdaPiN:\n      return os << \"LambdaPiN\";\n    case Limiters::MinmodType::Muscl:\n      return os << \"Muscl\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Missing a case for operator<<(MinmodType)\");\n      // LCOV_EXCL_STOP\n  }\n}\n\ntemplate <>\nLimiters::MinmodType\nOptions::create_from_yaml<Limiters::MinmodType>::create<void>(\n    const Options::Option& options) {\n  const auto minmod_type_read = options.parse_as<std::string>();\n  if (minmod_type_read == \"LambdaPi1\") {\n    return Limiters::MinmodType::LambdaPi1;\n  } else if (minmod_type_read == \"LambdaPiN\") {\n    return Limiters::MinmodType::LambdaPiN;\n  } else if (minmod_type_read == \"Muscl\") {\n    return Limiters::MinmodType::Muscl;\n  }\n  PARSE_ERROR(options.context(), \"Failed to convert \\\"\"\n                                     << minmod_type_read\n                                     << \"\\\" to MinmodType. Expected one of: \"\n                                        \"{LambdaPi1, LambdaPiN, Muscl}.\");\n}\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n\nnamespace Limiters {\n/// \\ingroup LimitersGroup\n/// \\brief Possible types of the minmod slope limiter and/or troubled-cell\n/// indicator.\n///\n/// \\see Limiters::Minmod for a description and reference.\nenum class MinmodType { LambdaPi1, LambdaPiN, Muscl };\n\nstd::ostream& operator<<(std::ostream& os, Limiters::MinmodType minmod_type);\n}  // namespace Limiters\n\ntemplate <>\nstruct Options::create_from_yaml<Limiters::MinmodType> {\n  template <typename Metavariables>\n  static Limiters::MinmodType create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nLimiters::MinmodType\nOptions::create_from_yaml<Limiters::MinmodType>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/SimpleWenoImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Limiters::Weno_detail {\n\n// Compute the Simple WENO solution for one tensor component\n//\n// This interface is intended for use in limiters that check the troubled-cell\n// indicator independently for each tensor component. These limiters generally\n// need to limit only a subset of the tensor components.\n//\n// When calling `simple_weno_impl`,\n// - `interpolator_buffer` may be empty\n// - `modified_neighbor_solution_buffer` should contain one DataVector for each\n//   neighboring element (i.e. for each entry in `neighbor_data`)\ntemplate <typename Tag, size_t VolumeDim, typename PackagedData>\nvoid simple_weno_impl(\n    const gsl::not_null<std::unordered_map<\n        DirectionalId<VolumeDim>, intrp::RegularGrid<VolumeDim>,\n        boost::hash<DirectionalId<VolumeDim>>>*>\n        interpolator_buffer,\n    const gsl::not_null<\n        std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                           boost::hash<DirectionalId<VolumeDim>>>*>\n        modified_neighbor_solution_buffer,\n    const gsl::not_null<typename Tag::type*> tensor,\n    const double neighbor_linear_weight, const size_t tensor_storage_index,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) {\n  // Check that basis is LGL or LG\n  // Note that the SimpleWeno implementation should generalize well to other\n  // bases beyond LGL or LG, once the oscillation_indicator function has been\n  // generalized.\n  ASSERT(mesh.basis() == make_array<VolumeDim>(Spectral::Basis::Legendre),\n         \"Unsupported basis: \" << mesh);\n  ASSERT(mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::GaussLobatto) or\n             mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::Gauss),\n         \"Unsupported quadrature: \" << mesh);\n\n  ASSERT(\n      modified_neighbor_solution_buffer->size() == neighbor_data.size(),\n      \"modified_neighbor_solution_buffer->size() = \"\n          << modified_neighbor_solution_buffer->size()\n          << \"\\nneighbor_data.size() = \" << neighbor_data.size()\n          << \"\\nmodified_neighbor_solution_buffer was incorrectly initialized \"\n             \"before calling simple_weno_impl.\");\n\n  // Compute the modified neighbor solutions.\n  // First extrapolate neighbor data onto local grid points, then shift the\n  // extrapolated data so its mean matches the local mean.\n  DataVector& component_to_limit = (*tensor)[tensor_storage_index];\n  const double local_mean = mean_value(component_to_limit, mesh);\n  for (const auto& neighbor_and_data : neighbor_data) {\n    const auto& neighbor = neighbor_and_data.first;\n    const auto& data = neighbor_and_data.second;\n\n    if (interpolator_buffer->find(neighbor) == interpolator_buffer->end()) {\n      // No interpolator found => create one\n      const auto& direction = neighbor.direction();\n      const auto& source_mesh = data.mesh;\n      const auto target_1d_logical_coords =\n          Weno_detail::local_grid_points_in_neighbor_logical_coords(\n              mesh, source_mesh, element, direction);\n      interpolator_buffer->insert(std::make_pair(\n          neighbor, intrp::RegularGrid<VolumeDim>(source_mesh, mesh,\n                                                  target_1d_logical_coords)));\n    }\n\n    // Avoid allocations by working directly in the preallocated buffer\n    DataVector& buffer = modified_neighbor_solution_buffer->at(neighbor);\n\n    interpolator_buffer->at(neighbor).interpolate(\n        make_not_null(&buffer),\n        get<Tag>(data.volume_data)[tensor_storage_index]);\n    const double neighbor_mean = mean_value(buffer, mesh);\n    buffer += (local_mean - neighbor_mean);\n  }\n\n  // Sum local and modified neighbor polynomials for the WENO reconstruction\n  Weno_detail::reconstruct_from_weighted_sum(\n      make_not_null(&component_to_limit), neighbor_linear_weight,\n      Weno_detail::DerivativeWeight::PowTwoEll, mesh,\n      *modified_neighbor_solution_buffer);\n}\n\n// Compute the Simple WENO solution for one tensor\n//\n// This interface is intended for use in limiters that check the troubled-cell\n// indicator for the whole cell, and apply the limiter to all fields.\n//\n// When calling `simple_weno_impl`,\n// - `interpolator_buffer` may be empty\n// - `modified_neighbor_solution_buffer` should contain one DataVector for each\n//   neighboring element (i.e. for each entry in `neighbor_data`)\ntemplate <typename Tag, size_t VolumeDim, typename PackagedData>\nvoid simple_weno_impl(\n    const gsl::not_null<std::unordered_map<\n        DirectionalId<VolumeDim>, intrp::RegularGrid<VolumeDim>,\n        boost::hash<DirectionalId<VolumeDim>>>*>\n        interpolator_buffer,\n    const gsl::not_null<\n        std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                           boost::hash<DirectionalId<VolumeDim>>>*>\n        modified_neighbor_solution_buffer,\n    const gsl::not_null<typename Tag::type*> tensor,\n    const double neighbor_linear_weight, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) {\n  for (size_t tensor_storage_index = 0; tensor_storage_index < tensor->size();\n       ++tensor_storage_index) {\n    simple_weno_impl<Tag>(interpolator_buffer,\n                          modified_neighbor_solution_buffer, tensor,\n                          neighbor_linear_weight, tensor_storage_index, mesh,\n                          element, neighbor_data);\n  }\n}\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Holds the `OptionTags::Limiter` option in the input file\n */\nstruct LimiterGroup {\n  static std::string name() { return \"Limiter\"; }\n  static constexpr Options::String help = \"Options for limiting troubled cells\";\n};\n\n/*!\n * \\ingroup OptionTagsGroup\n * \\brief The global cache tag that retrieves the parameters for the limiter\n * from the input file\n */\ntemplate <typename LimiterType>\nstruct Limiter {\n  static std::string name() { return pretty_type::name<LimiterType>(); }\n  static constexpr Options::String help = \"Options for the limiter\";\n  using type = LimiterType;\n  using group = LimiterGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief The global cache tag for the limiter\n */\ntemplate <typename LimiterType>\nstruct Limiter : db::SimpleTag {\n  using type = LimiterType;\n  using option_tags = tmpl::list<::OptionTags::Limiter<LimiterType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static LimiterType create_from_options(const LimiterType& limiter) {\n    return limiter;\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/Tci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Limiters {\n\n/// \\ingroup LimitersGroup\n/// \\brief Troubled Cell Indicators that identify when limiting is needed\nnamespace Tci {}\n\n}  // namespace Limiters\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/Weno.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/SimpleWenoImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Direction;\ntemplate <size_t VolumeDim>\nclass ElementId;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Limiters {\n/// \\ingroup LimitersGroup\n/// \\brief A compact-stencil WENO limiter for DG\n///\n/// Implements the simple WENO limiter of \\cite Zhong2013 and the Hermite WENO\n/// (HWENO) limiter of \\cite Zhu2016. The implementation is system-agnostic and\n/// can act on an arbitrary set of tensors.\n///\n/// #### Summary of the compact-stencil WENO algorithms:\n//\n/// The compact-stencil WENO limiters require communication only between\n/// nearest-neighbor elements, but aim to preserve the full order of the DG\n/// solution when the solution is smooth. To achieve this, full volume data is\n/// communicated between neighbors.\n//\n/// For each tensor component to limit, the new solution is obtained by a\n/// standard WENO procedure --- the new solution is a linear combination of\n/// different polynomials, with weights chosen so that the smoother (i.e., less\n/// oscillatory) polynomials contribute the most to the sum.\n///\n/// For the simple WENO and HWENO limiters, the polynomials used are the local\n/// DG solution as well as a \"modified\" solution from each neighbor element. For\n/// the simple WENO limiter, the modified solution is obtained by simply\n/// extrapolating the neighbor solution onto the troubled element. For the HWENO\n/// limiter, the modified solution is obtained by a least-squares fit to the\n/// solution across multiple neighboring elements.\n///\n/// #### Notes on the SpECTRE implemention of the WENO limiters:\n///\n/// There are a few differences between the limiters as implemented in SpECTRE\n/// and as presented in the references. We list them here and discuss them\n/// further below.\n/// 1. The choice of basis to represent the DG solution\n/// 2. The system-agnostic implementation\n/// 3. The oscillation indicator\n///\n/// Finally, in 4., we will discuss the geometric limitations of the\n/// implementation (which are not a deviation from the references).\n///\n/// ##### 1. The choice of basis\n///\n/// SpECTRE uses a Legendre basis, rather than the polynomial basis that we\n/// understand to be used in the references. Because the construction of the\n/// modified neighbor solutions and the WENO sum is geometrically motivated, the\n/// overall algorithm should work similarly. However, the precise numerics may\n/// differ.\n///\n/// ##### 2. The system-agnostic implementation\n//\n/// This implementation can act on an arbitrary set of tensors. To reach this\n/// generality, our HWENO implementation uses a different troubled-cell\n/// indicator (TCI) than the reference, which instead specializes the TCI to the\n/// Newtonian Euler system of equations.\n///\n/// This implementation uses the minmod-based TVB TCI of \\cite Cockburn1999 to\n/// identify elements that need limiting. The simple WENO implementation follows\n/// its reference: it checks the TCI independently for each tensor component, so\n/// that only certain tensor components may be limited. The HWENO implementation\n/// checks the TVB TCI for all tensor components, and if any single component is\n/// troubled, then all components of all tensors are limited.\n///\n/// When the evolution system has multiple evolved variables, the recommendation\n/// of the references is to apply the limiter to the system's characteristic\n/// variables to reduce spurious post-limiting oscillations. In SpECTRE,\n/// applying the limiter to the characteristic variables requires specializing\n/// the limiter to each evolution system. The system-specific limiter can also\n/// implement a system-specific TCI (as the HWENO reference does) to more\n/// precisely trigger the limiter.\n///\n/// ##### 3. The oscillation indicator\n///\n/// We use the oscillation indicator of \\cite Dumbser2007, modified for use on\n/// the square/cube grids of SpECTRE. We favor this indicator because portions\n/// of the work can be precomputed, leading to an oscillation measure that is\n/// efficient to evaluate.\n///\n/// ##### 4. The geometric limitations\n///\n/// Does not support non-Legendre bases; this is checked in DEBUG mode. In\n/// principle other bases could be supported, but this would require\n/// generalizing the many internal algorithms that assume a Legendre basis.\n///\n/// Does not support h- or p-refinement; this is checked always. In principle\n/// this could be supported. The modified neighbor solution algorithm would\n/// need to be generalized (reasonable for simple WENO, extremely tedious for\n/// HWENO), and the sum of neighbor solutions may need to be updated as well.\n///\n/// Does not support curved elements; this is not enforced. The code will run\n/// but we make no guarantees about the results. Specifically, the limiter acts\n/// in the `Frame::ElementLogical` coordinates, because in these coordinates it\n/// is straightforward to formulate the algorithm. This means the limiter can\n/// operate on generic deformed grids --- however, some things can start to\n/// break down, especially on strongly deformed grids:\n/// 1. When the Jacobian (from `Frame::ElementLogical` to `Frame::Inertial`)\n///    varies across the element, then the limiter fails to be conservative.\n///    This is because the integral of a tensor `u` over the element will change\n///    after the limiter activates on `u`.\n/// 2. When computing the modified neighbor solution for the WENO sum, the\n///    extrapolation or fitting procedure may not properly account for the\n///    coordinates of the source data. If the coordinate map of the neighbor\n///    differs from that of the local element, then the logical-coordinate\n///    representation of the neighbor data may be incorrect. This may be a\n///    large error at Block boundaries with discontinuous map changes, and may\n///    be a small error from smoothly-varying maps that are not sufficiently\n///    resolved from one element to the next.\ntemplate <size_t VolumeDim, typename TagsToLimit>\nclass Weno;\n\ntemplate <size_t VolumeDim, typename... Tags>\nclass Weno<VolumeDim, tmpl::list<Tags...>> {\n public:\n  /// \\brief The WenoType\n  ///\n  /// One of `Limiters::WenoType`. See the `Limiters::Weno`\n  /// documentation for details.\n  struct Type {\n    using type = WenoType;\n    static constexpr Options::String help = {\"Type of WENO limiter\"};\n  };\n  /// \\brief The linear weight given to each neighbor\n  ///\n  /// This linear weight gets combined with the oscillation indicator to\n  /// compute the weight for each WENO estimated solution. The standard value\n  /// in the literature is 0.001; larger values may be better suited for\n  /// problems with strong shocks, and smaller values may be better suited to\n  /// smooth problems.\n  struct NeighborWeight {\n    using type = double;\n    static type lower_bound() { return 1e-6; }\n    static type upper_bound() { return 0.1; }\n    static constexpr Options::String help = {\n        \"Linear weight for each neighbor element's solution\"};\n  };\n  /// \\brief The TVB constant for the minmod TCI\n  ///\n  /// See `Limiters::Minmod` documentation for details.\n  struct TvbConstant {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\"TVB constant 'm'\"};\n  };\n  /// \\brief Turn the limiter off\n  ///\n  /// This option exists to temporarily disable the limiter for debugging\n  /// purposes. For problems where limiting is not needed, the preferred\n  /// approach is to not compile the limiter into the executable.\n  struct DisableForDebugging {\n    using type = bool;\n    static type suggested_value() { return false; }\n    static constexpr Options::String help = {\"Disable the limiter\"};\n  };\n  using options =\n      tmpl::list<Type, NeighborWeight, TvbConstant, DisableForDebugging>;\n  static constexpr Options::String help = {\"A WENO limiter for DG\"};\n\n  Weno(WenoType weno_type, double neighbor_linear_weight, double tvb_constant,\n       bool disable_for_debugging = false);\n\n  Weno() = default;\n  Weno(const Weno& /*rhs*/) = default;\n  Weno& operator=(const Weno& /*rhs*/) = default;\n  Weno(Weno&& /*rhs*/) = default;\n  Weno& operator=(Weno&& /*rhs*/) = default;\n  ~Weno() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// \\brief Data to send to neighbor elements\n  struct PackagedData {\n    Variables<tmpl::list<Tags...>> volume_data;\n    tuples::TaggedTuple<::Tags::Mean<Tags>...> means;\n    Mesh<VolumeDim> mesh;\n    std::array<double, VolumeDim> element_size =\n        make_array<VolumeDim>(std::numeric_limits<double>::signaling_NaN());\n\n    // NOLINTNEXTLINE(google-runtime-references)\n    void pup(PUP::er& p) {\n      p | volume_data;\n      p | means;\n      p | mesh;\n      p | element_size;\n    }\n  };\n\n  using package_argument_tags =\n      tmpl::list<Tags..., domain::Tags::Mesh<VolumeDim>,\n                 domain::Tags::SizeOfElement<VolumeDim>>;\n\n  /// \\brief Package data for sending to neighbor elements\n  void package_data(gsl::not_null<PackagedData*> packaged_data,\n                    const typename Tags::type&... tensors,\n                    const Mesh<VolumeDim>& mesh,\n                    const std::array<double, VolumeDim>& element_size,\n                    const OrientationMap<VolumeDim>& orientation_map) const;\n\n  using limit_tags = tmpl::list<Tags...>;\n  using limit_argument_tags =\n      tmpl::list<domain::Tags::Mesh<VolumeDim>,\n                 domain::Tags::Element<VolumeDim>,\n                 domain::Tags::SizeOfElement<VolumeDim>>;\n\n  /// \\brief Limit the solution on the element\n  bool operator()(\n      const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,\n      const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n      const std::array<double, VolumeDim>& element_size,\n      const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                               boost::hash<DirectionalId<VolumeDim>>>&\n          neighbor_data) const;\n\n private:\n  template <size_t LocalDim, typename LocalTagList>\n  // NOLINTNEXTLINE(readability-redundant-declaration) false positive\n  friend bool operator==(const Weno<LocalDim, LocalTagList>& lhs,\n                         const Weno<LocalDim, LocalTagList>& rhs);\n\n  WenoType weno_type_;\n  double neighbor_linear_weight_;\n  double tvb_constant_;\n  bool disable_for_debugging_;\n};\n\ntemplate <size_t VolumeDim, typename... Tags>\nWeno<VolumeDim, tmpl::list<Tags...>>::Weno(const WenoType weno_type,\n                                           const double neighbor_linear_weight,\n                                           const double tvb_constant,\n                                           const bool disable_for_debugging)\n    : weno_type_(weno_type),\n      neighbor_linear_weight_(neighbor_linear_weight),\n      tvb_constant_(tvb_constant),\n      disable_for_debugging_(disable_for_debugging) {}\n\ntemplate <size_t VolumeDim, typename... Tags>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid Weno<VolumeDim, tmpl::list<Tags...>>::pup(PUP::er& p) {\n  p | weno_type_;\n  p | neighbor_linear_weight_;\n  p | tvb_constant_;\n  p | disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nvoid Weno<VolumeDim, tmpl::list<Tags...>>::package_data(\n    const gsl::not_null<PackagedData*> packaged_data,\n    const typename Tags::type&... tensors, const Mesh<VolumeDim>& mesh,\n    const std::array<double, VolumeDim>& element_size,\n    const OrientationMap<VolumeDim>& orientation_map) const {\n  // By always initializing the PackagedData Variables member, we avoid an\n  // assertion that arises from having a default-constructed Variables in a\n  // disabled limiter. There is a performance cost, because the package_data()\n  // function does non-zero work even for a disabled limiter... but since the\n  // limiter should never be disabled in a production simulation, this cost\n  // should never matter.\n  (packaged_data->volume_data).initialize(mesh.number_of_grid_points());\n\n  if (UNLIKELY(disable_for_debugging_)) {\n    // Do not initialize packaged_data\n    // (except for the Variables member \"volume_data\", see above)\n    return;\n  }\n\n  const auto wrap_compute_means = [&mesh, &packaged_data](auto tag,\n                                                          const auto tensor) {\n    for (size_t i = 0; i < tensor.size(); ++i) {\n      // Compute the mean using the local orientation of the tensor and mesh.\n      get<::Tags::Mean<decltype(tag)>>(packaged_data->means)[i] =\n          mean_value(tensor[i], mesh);\n    }\n    return '0';\n  };\n  expand_pack(wrap_compute_means(Tags{}, tensors)...);\n\n  packaged_data->element_size =\n      orientation_map.permute_from_neighbor(element_size);\n\n  const auto wrap_copy_tensor = [&packaged_data](auto tag, const auto tensor) {\n    get<decltype(tag)>(packaged_data->volume_data) = tensor;\n    return '0';\n  };\n  expand_pack(wrap_copy_tensor(Tags{}, tensors)...);\n  packaged_data->volume_data = orient_variables(\n      packaged_data->volume_data, mesh.extents(), orientation_map);\n\n  // Warning: the WENO limiter is currently only tested with aligned meshes.\n  // The orientation of the mesh, the `element_size` computed above, and the\n  // variables should be carefully tested when used with domains that involve\n  // orientation maps\n  packaged_data->mesh = orientation_map(mesh);\n}\n\ntemplate <size_t VolumeDim, typename... Tags>\nbool Weno<VolumeDim, tmpl::list<Tags...>>::operator()(\n    const gsl::not_null<std::add_pointer_t<typename Tags::type>>... tensors,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) const {\n  if (UNLIKELY(disable_for_debugging_)) {\n    // Do not modify input tensors\n    return false;\n  }\n\n  // Check that basis is LGL or LG\n  // A Legendre basis is assumed for the oscillation indicator (used in both\n  // SimpleWeno and Hweno) and in the Hweno reconstruction.\n  ASSERT(mesh.basis() == make_array<VolumeDim>(Spectral::Basis::Legendre),\n         \"Unsupported basis: \" << mesh);\n  ASSERT(mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::GaussLobatto) or\n             mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::Gauss),\n         \"Unsupported quadrature: \" << mesh);\n\n  // Enforce restrictions on h-refinement, p-refinement\n  if (UNLIKELY(\n          alg::any_of(element.neighbors(), [](const auto& direction_neighbors) {\n            return direction_neighbors.second.size() != 1;\n          }))) {\n    ERROR(\"The Weno limiter does not yet support h-refinement\");\n    // Removing this limitation will require:\n    // - Generalizing the computation of the modified neighbor solutions.\n    // - Generalizing the WENO weighted sum for multiple neighbors in each\n    //   direction.\n  }\n  alg::for_each(neighbor_data, [&mesh](const auto& neighbor_and_data) {\n    if (UNLIKELY(neighbor_and_data.second.mesh != mesh)) {\n      ERROR(\"The Weno limiter does not yet support p-refinement\");\n      // Removing this limitation will require generalizing the\n      // computation of the modified neighbor solutions.\n    }\n  });\n\n  if (weno_type_ == WenoType::Hweno) {\n    // Troubled-cell detection for HWENO flags the element for limiting if any\n    // component of any tensor needs limiting.\n    const bool cell_is_troubled =\n        Tci::tvb_minmod_indicator<VolumeDim, PackagedData, Tags...>(\n            tvb_constant_, (*tensors)..., mesh, element, element_size,\n            neighbor_data);\n    if (not cell_is_troubled) {\n      // No limiting is needed\n      return false;\n    }\n\n    std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                       boost::hash<DirectionalId<VolumeDim>>>\n        modified_neighbor_solution_buffer{};\n    for (const auto& neighbor_and_data : neighbor_data) {\n      const auto& neighbor = neighbor_and_data.first;\n      modified_neighbor_solution_buffer.insert(\n          std::make_pair(neighbor, DataVector(mesh.number_of_grid_points())));\n    }\n\n    EXPAND_PACK_LEFT_TO_RIGHT(Weno_detail::hweno_impl<Tags>(\n        make_not_null(&modified_neighbor_solution_buffer), tensors,\n        neighbor_linear_weight_, mesh, element, neighbor_data));\n    return true;  // cell_is_troubled\n\n  } else if (weno_type_ == WenoType::SimpleWeno) {\n    // Buffers and pre-computations for TCI\n    Minmod_detail::BufferWrapper<VolumeDim> tci_buffer(mesh);\n    const auto effective_neighbor_sizes =\n        Minmod_detail::compute_effective_neighbor_sizes(element, neighbor_data);\n\n    // Buffers for simple WENO implementation\n    std::unordered_map<DirectionalId<VolumeDim>, intrp::RegularGrid<VolumeDim>,\n                       boost::hash<DirectionalId<VolumeDim>>>\n        interpolator_buffer{};\n    std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                       boost::hash<DirectionalId<VolumeDim>>>\n        modified_neighbor_solution_buffer{};\n\n    bool some_component_was_limited = false;\n\n    const auto wrap_minmod_tci_and_simple_weno_impl =\n        [this, &some_component_was_limited, &tci_buffer, &interpolator_buffer,\n         &modified_neighbor_solution_buffer, &mesh, &element, &element_size,\n         &neighbor_data,\n         &effective_neighbor_sizes](auto tag, const auto tensor) {\n          for (size_t tensor_storage_index = 0;\n               tensor_storage_index < tensor->size(); ++tensor_storage_index) {\n            // Check TCI\n            const auto effective_neighbor_means =\n                Minmod_detail::compute_effective_neighbor_means<decltype(tag)>(\n                    tensor_storage_index, element, neighbor_data);\n            const bool component_needs_limiting = Tci::tvb_minmod_indicator(\n                make_not_null(&tci_buffer), tvb_constant_,\n                (*tensor)[tensor_storage_index], mesh, element, element_size,\n                effective_neighbor_means, effective_neighbor_sizes);\n\n            if (component_needs_limiting) {\n              if (modified_neighbor_solution_buffer.empty()) {\n                // Allocate the neighbor solution buffers only if the limiter is\n                // triggered. This reduces allocation when no limiting occurs.\n                for (const auto& neighbor_and_data : neighbor_data) {\n                  const auto& neighbor = neighbor_and_data.first;\n                  modified_neighbor_solution_buffer.insert(std::make_pair(\n                      neighbor, DataVector(mesh.number_of_grid_points())));\n                }\n              }\n              Weno_detail::simple_weno_impl<decltype(tag)>(\n                  make_not_null(&interpolator_buffer),\n                  make_not_null(&modified_neighbor_solution_buffer), tensor,\n                  neighbor_linear_weight_, tensor_storage_index, mesh, element,\n                  neighbor_data);\n              some_component_was_limited = true;\n            }\n          }\n          return '0';\n        };\n    expand_pack(wrap_minmod_tci_and_simple_weno_impl(Tags{}, tensors)...);\n    return some_component_was_limited;  // cell_is_troubled\n  } else {\n    ERROR(\"WENO limiter not implemented for WenoType: \" << weno_type_);\n  }\n\n  return false;  // cell_is_troubled\n}\n\ntemplate <size_t LocalDim, typename LocalTagList>\nbool operator==(const Weno<LocalDim, LocalTagList>& lhs,\n                const Weno<LocalDim, LocalTagList>& rhs) {\n  return lhs.weno_type_ == rhs.weno_type_ and\n         lhs.neighbor_linear_weight_ == rhs.neighbor_linear_weight_ and\n         lhs.tvb_constant_ == rhs.tvb_constant_ and\n         lhs.disable_for_debugging_ == rhs.disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim, typename TagList>\nbool operator!=(const Weno<VolumeDim, TagList>& lhs,\n                const Weno<VolumeDim, TagList>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Limiters\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Limiters::Weno_detail {\n\ntemplate <size_t VolumeDim>\nbool check_element_has_one_similar_neighbor_in_direction(\n    const Element<VolumeDim>& element, const Direction<VolumeDim>& direction) {\n  ASSERT(element.neighbors().contains(direction),\n         \"Inconsistent call: element \"\n             << element << \" has no neighbors in direction \" << direction);\n  const auto& neighbors = element.neighbors().at(direction);\n  if (neighbors.size() > 1) {\n    // More than one neighbor\n    return false;\n  } else {\n    const auto& neighbor_id = *(neighbors.ids().cbegin());\n    const auto& orientation_map = neighbors.orientation(neighbor_id);\n    const auto neighbor_segment_ids = neighbor_id.segment_ids();\n    const auto reoriented_neighbor_segment_ids =\n        orientation_map.inverse_map()(neighbor_segment_ids);\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      if (gsl::at(reoriented_neighbor_segment_ids, d).refinement_level() !=\n          element.id().segment_id(d).refinement_level()) {\n        // One neighbor, but of a different refinement level\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\ntemplate <size_t VolumeDim>\nstd::array<DataVector, VolumeDim> neighbor_grid_points_in_local_logical_coords(\n    const Mesh<VolumeDim>& local_mesh, const Mesh<VolumeDim>& neighbor_mesh,\n    const Element<VolumeDim>& element,\n    const Direction<VolumeDim>& direction_to_neighbor) {\n  ASSERT(check_element_has_one_similar_neighbor_in_direction(\n             element, direction_to_neighbor),\n         \"Found some amount of h-refinement; this is not supported\");\n\n  constexpr double logical_element_width = 2.0;\n  std::array<DataVector, VolumeDim> result{{{}}};\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const auto neighbor_mesh_1d = neighbor_mesh.slice_through(d);\n    if (d == direction_to_neighbor.dimension()) {\n      // Coordinates normal to the interface cannot line up, so we must provide\n      // the coordinates to interpolate to\n      gsl::at(result, d) =\n          get<0>(logical_coordinates(neighbor_mesh_1d)) +\n          (direction_to_neighbor.side() == Side::Upper ? 1.0 : -1.0) *\n              logical_element_width;\n    } else if (neighbor_mesh_1d != local_mesh.slice_through(d)) {\n      // Coordinates parallel to the interface may or may not line up\n      gsl::at(result, d) = get<0>(logical_coordinates(neighbor_mesh_1d));\n    }\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nstd::array<DataVector, VolumeDim> local_grid_points_in_neighbor_logical_coords(\n    const Mesh<VolumeDim>& local_mesh, const Mesh<VolumeDim>& neighbor_mesh,\n    const Element<VolumeDim>& element,\n    const Direction<VolumeDim>& direction_to_neighbor) {\n  ASSERT(check_element_has_one_similar_neighbor_in_direction(\n             element, direction_to_neighbor),\n         \"Found some amount of h-refinement; this is not supported\");\n\n  constexpr double logical_element_width = 2.0;\n  std::array<DataVector, VolumeDim> result{{{}}};\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const auto local_mesh_1d = local_mesh.slice_through(d);\n    if (d == direction_to_neighbor.dimension()) {\n      // Coordinates normal to the interface cannot line up, so we must provide\n      // the coordinates to interpolate to\n      gsl::at(result, d) =\n          get<0>(logical_coordinates(local_mesh_1d)) +\n          (direction_to_neighbor.side() == Side::Upper ? -1.0 : 1.0) *\n              logical_element_width;\n    } else if (local_mesh_1d != neighbor_mesh.slice_through(d)) {\n      // Coordinates parallel to the interface may or may not line up\n      gsl::at(result, d) = get<0>(logical_coordinates(local_mesh_1d));\n    }\n  }\n  return result;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template bool check_element_has_one_similar_neighbor_in_direction( \\\n      const Element<DIM(data)>&, const Direction<DIM(data)>&);       \\\n  template std::array<DataVector, DIM(data)>                         \\\n  neighbor_grid_points_in_local_logical_coords(                      \\\n      const Mesh<DIM(data)>&, const Mesh<DIM(data)>&,                \\\n      const Element<DIM(data)>&, const Direction<DIM(data)>&);       \\\n  template std::array<DataVector, DIM(data)>                         \\\n  local_grid_points_in_neighbor_logical_coords(                      \\\n      const Mesh<DIM(data)>&, const Mesh<DIM(data)>&,                \\\n      const Element<DIM(data)>&, const Direction<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Direction;\ntemplate <size_t>\nclass Element;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\nnamespace Limiters::Weno_detail {\n\n// Check that an element has just one neighbor in a particular direction, and\n// that this neighbor has the same refinement level as the element.\ntemplate <size_t VolumeDim>\nbool check_element_has_one_similar_neighbor_in_direction(\n    const Element<VolumeDim>& element, const Direction<VolumeDim>& direction);\n\n// Compute the coordinate positions of a neighbor element's grid points in the\n// logical coordinates of the local element.\n//\n// However, the results are organized to match the expected input of a\n// RegularGridInterpolator constructor:\n// - The coordinates are given as an array of 1D coordinate values.\n// - For any dimension where the local and neighbor meshes share the same 1D\n//   submesh, the array is empty. (This tells the RegularGridInterpolator that\n//   no interpolation needs to be done in this dimension.)\n//\n// Limitations:\n// - No support for h-refinement. It is ASSERT'ed that the element has only one\n//   neighbor in the specified direction, and that this neighbor is of the same\n//   refinement level as the local element.\n// - Assumes a uniform rectilinear grid. No support for elements of different\n//   sizes (as could occur if all elements have the same refinement level but\n//   different blocks have different physical size), or curvilinear elements.\n//   This is not checked.\ntemplate <size_t VolumeDim>\nstd::array<DataVector, VolumeDim> neighbor_grid_points_in_local_logical_coords(\n    const Mesh<VolumeDim>& local_mesh, const Mesh<VolumeDim>& neighbor_mesh,\n    const Element<VolumeDim>& element,\n    const Direction<VolumeDim>& direction_to_neighbor);\n\n// Compute the coordinate positions of the local element's grid points in the\n// logical coordinates of the a neighbor element.\n//\n// See documentation of `neighbor_grid_points_in_local_logical_coords` for\n// further details and limitations.\ntemplate <size_t VolumeDim>\nstd::array<DataVector, VolumeDim> local_grid_points_in_neighbor_logical_coords(\n    const Mesh<VolumeDim>& local_mesh, const Mesh<VolumeDim>& neighbor_mesh,\n    const Element<VolumeDim>& element,\n    const Direction<VolumeDim>& direction_to_neighbor);\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\n// Compute the unnormalized nonlinear WENO weights. This is a fairly standard\n// choice of weights; see e.g., Eq. 3.9 of Zhong2013 or Eq. 3.6 of Zhu2016.\ninline double unnormalized_nonlinear_weight(\n    const double linear_weight, const double oscillation_indicator) {\n  return linear_weight / square(1.e-6 + oscillation_indicator);\n}\n\n}  // namespace\n\nnamespace Limiters::Weno_detail {\n\ntemplate <size_t VolumeDim>\nvoid reconstruct_from_weighted_sum(\n    const gsl::not_null<DataVector*> local_polynomial,\n    const double neighbor_linear_weight,\n    const DerivativeWeight derivative_weight, const Mesh<VolumeDim>& mesh,\n    const std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_polynomials) {\n#ifdef SPECTRE_DEBUG\n  ASSERT(local_polynomial->size() > 0,\n         \"The local_polynomial values are missing - was the input correctly\\n\"\n         \"initialized before calling reconstruct_from_weighted_sum?\");\n  // Check inputs match requirements\n  const double local_mean = mean_value(*local_polynomial, mesh);\n  for (const auto& kv : neighbor_polynomials) {\n    const auto& neighbor_polynomial = kv.second;\n    const double neighbor_mean = mean_value(neighbor_polynomial, mesh);\n    ASSERT(equal_within_roundoff(local_mean, neighbor_mean),\n           \"Invalid inputs to Weno_detail::reconstruct_from_weighted_sum:\\n\"\n           \"The neighbor polynomials should have the same mean as the local\\n\"\n           \"polynomial.\");\n  }\n#endif  // ifdef SPECTRE_DEBUG\n\n  // Store linear weights in `local_weights` and `neighbor_weights`\n  // These weights will have to be generalized for multiple neighbors per\n  // face for use with h-refinement and AMR.\n  double local_weight = 1.;\n  std::unordered_map<DirectionalId<VolumeDim>, double,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_weights;\n  for (const auto& kv : neighbor_polynomials) {\n    local_weight -= neighbor_linear_weight;\n    neighbor_weights[kv.first] = neighbor_linear_weight;\n  }\n\n  // Update `local_weights` and `neighbor_weights` to hold the unnormalized\n  // nonlinear weights.\n  local_weight = unnormalized_nonlinear_weight(\n      local_weight,\n      oscillation_indicator(derivative_weight, *local_polynomial, mesh));\n  for (const auto& kv : neighbor_polynomials) {\n    const auto& key = kv.first;\n    const auto& neighbor_polynomial = kv.second;\n    neighbor_weights[key] = unnormalized_nonlinear_weight(\n        neighbor_weights[key],\n        oscillation_indicator(derivative_weight, neighbor_polynomial, mesh));\n  }\n\n  // Update `local_weights` and `neighbor_weights` to hold the normalized\n  // weights; these are the final weights of the WENO reconstruction.\n  double normalization = local_weight;\n  for (const auto& kv : neighbor_weights) {\n    normalization += kv.second;\n  }\n  local_weight /= normalization;\n  for (auto& kv : neighbor_weights) {\n    kv.second /= normalization;\n  }\n\n  // Perform reconstruction by combining the local and neighbor polynomials.\n  *local_polynomial *= local_weight;\n  for (const auto& kv : neighbor_polynomials) {\n    const auto& key = kv.first;\n    const auto& neighbor_polynomial = kv.second;\n    *local_polynomial += neighbor_weights.at(key) * neighbor_polynomial;\n  }\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void reconstruct_from_weighted_sum(                                \\\n      const gsl::not_null<DataVector*>, const double, const DerivativeWeight, \\\n      const Mesh<DIM(data)>&,                                                 \\\n      const std::unordered_map<DirectionalId<DIM(data)>, DataVector,          \\\n                               boost::hash<DirectionalId<DIM(data)>>>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_map>\n#include <utility>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\n\nnamespace boost {\ntemplate <class T>\nstruct hash;\n}  // namespace boost\n/// \\endcond\n\nnamespace Limiters::Weno_detail {\n\n// Compute the WENO weighted reconstruction of a DataVector, see e.g.,\n// Eq. 4.3 of Zhu2016. This is fairly standard, though different references can\n// differ in their choice of oscillation/smoothness indicator. The\n// `DerivativeWeight` enum specifies the relative weight of each derivative\n// term when computing the oscillation indicator.\n//\n// The reconstruction modifies the DataVector in `local_polynomial` by adding\n// to it a weighted combination of one or more neighbor contributions, passed\n// in as several DataVectors in `neighbor_polynommials`. Each neighbor\n// polynomial must have the same mean as the local polynomial; this is checked\n// with an ASSERT.\ntemplate <size_t VolumeDim>\nvoid reconstruct_from_weighted_sum(\n    gsl::not_null<DataVector*> local_polynomial, double neighbor_linear_weight,\n    DerivativeWeight derivative_weight, const Mesh<VolumeDim>& mesh,\n    const std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_polynomials);\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace {\n// Given the modal expansion corresponding to some function f(x), compute the\n// modal expansion corresponding to the derivative d/dx f(x), where x are the\n// logical coordinates.\n//\n// Note: this function assumes a Legendre basis, i.e., assumes that the modes\n// are the amplitudes of a Legendre polynomial expansion.\nModalVector modal_derivative(const ModalVector& coeffs) {\n  const size_t number_of_modes = coeffs.size();\n  ModalVector deriv_coeffs(number_of_modes, 0.);\n  for (size_t i = 0; i < number_of_modes; ++i) {\n    for (size_t j = (i % 2 == 0 ? 1 : 0); j < i; j += 2) {\n      deriv_coeffs[j] += coeffs[i] * (2. * j + 1.);\n    }\n  }\n  return deriv_coeffs;\n}\n\n// For a given pair of Legendre polynomial basis functions (P_m, P_n), compute\n// the sum of the integrals of all their derivatives:\n// \\sum_{l=0}^{N} w(l) \\int d^(l)/dx^(l) P_m(x) * d^(l)/dx^(l) P_n(x) * dx\n// where w(l) is some weight given to each term in the sum.\n//\n// The implemented algorithm computes the l'th derivative of P_m (and P_n) as a\n// modal expansion in lower-order Legendre polynomials. The integrals can be\n// carried out using the orthogonality relations between these basis functions.\ndouble compute_sum_of_legendre_derivs(\n    const size_t number_of_modes, const size_t m, const size_t n,\n    const std::array<\n        double, Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>&\n        weights_for_derivatives) {\n  ModalVector coeffs_m(number_of_modes, 0.);\n  coeffs_m[m] = 1.;\n  ModalVector coeffs_n(number_of_modes, 0.);\n  coeffs_n[n] = 1.;\n  double result = 0.;\n  // This is the l == 0 term of the sum\n  if (m == n) {\n    result += weights_for_derivatives[0] *\n              Spectral::compute_basis_function_normalization_square<\n                  Spectral::Basis::Legendre>(m);\n  }\n  // This is the main l > 0 part of the sum\n  for (size_t l = 1; l < number_of_modes; ++l) {\n    const double weight = gsl::at(weights_for_derivatives, l);\n    coeffs_m = modal_derivative(coeffs_m);\n    coeffs_n = modal_derivative(coeffs_n);\n    for (size_t i = 0; i < number_of_modes; ++i) {\n      result += weight * coeffs_m[i] * coeffs_n[i] *\n                Spectral::compute_basis_function_normalization_square<\n                    Spectral::Basis::Legendre>(i);\n    }\n  }\n  return result;\n}\n\n// Compute the indicator matrix, similar to that of Dumbser2007 Eq. 25, but\n// adapted for use on square/cube grids. A Legendre basis is assumed.\n//\n// The reference computes the indicator on a triangle/tetrahedral grid, so\n// the basis functions (and their derivatives) do not factor into a tensor\n// product across dimensions. We instead compute the indicator on a square/cube\n// grid, where the basis functions (and their derivatives) do factor. We use\n// this factoring to write the indicator as a product of 1D contributions from\n// each xi/eta/zeta component of the basis function.\n//\n// Note that we compute the product using derivatives 0...N in each dimension.\n// We then subtract off the term with 0 derivatives in all dimensions, because\n// that term is just the original data and should be omitted.\n//\n// Note also that because the mean value of the data does not contribute to its\n// oscillation content, any matrix element associated with basis functions\n// m == 0 or n == 0 vanishes. We thus slightly reduce the necessary storage by\n// computing only the (N-1)^2 matrix where we start at m, n >= 1.\n//\n// Finally, note that the matrix measures the oscillation content in modes of\n// a Legendre basis. Therefore, the matrix must be multiplied by ModalVectors\n// of Legendre modes to compute the oscillation indicator.\ntemplate <size_t VolumeDim>\nMatrix compute_indicator_matrix(\n    const Limiters::Weno_detail::DerivativeWeight derivative_weight,\n    const Index<VolumeDim>& extents) {\n  Matrix result(extents.product() - 1, extents.product() - 1);\n\n  const std::array<\n      double, Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>\n      weights_for_derivatives = [&derivative_weight]() {\n        auto weights = make_array<\n            Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(1.);\n        if (derivative_weight ==\n            Limiters::Weno_detail::DerivativeWeight::PowTwoEll) {\n          for (size_t l = 0; l < weights.size(); ++l) {\n            gsl::at(weights, l) = pow(2., 2. * l - 1.);\n          }\n        } else if (derivative_weight ==\n                   Limiters::Weno_detail::DerivativeWeight::\n                       PowTwoEllOverEllFactorial) {\n          for (size_t l = 0; l < weights.size(); ++l) {\n            gsl::at(weights, l) = 0.5 * square(pow(2., l) / factorial(l));\n          }\n        }\n        return weights;\n      }();\n\n  for (IndexIterator<VolumeDim> m(extents); m; ++m) {\n    if (m.collapsed_index() == 0) {\n      // Skip the terms associated with the cell average of the data\n      continue;\n    }\n    for (IndexIterator<VolumeDim> n(extents); n; ++n) {\n      if (n.collapsed_index() == 0) {\n        // Skip the terms associated with the cell average of the data\n        continue;\n      }\n      // Compute each indicator matrix term by tensor-product of 1D Legendre\n      // polynomial math\n      auto& result_mn =\n          result(m.collapsed_index() - 1, n.collapsed_index() - 1);\n      result_mn = compute_sum_of_legendre_derivs(extents[0], m()[0], n()[0],\n                                                 weights_for_derivatives);\n      for (size_t dim = 1; dim < VolumeDim; ++dim) {\n        result_mn *= compute_sum_of_legendre_derivs(\n            extents[dim], m()[dim], n()[dim], weights_for_derivatives);\n      }\n    }\n    // Subtract the tensor-product term that has no derivatives\n    double term_with_no_derivs =\n        weights_for_derivatives[0] *\n        Spectral::compute_basis_function_normalization_square<\n            Spectral::Basis::Legendre>(m()[0]);\n    for (size_t dim = 1; dim < VolumeDim; ++dim) {\n      term_with_no_derivs *=\n          weights_for_derivatives[0] *\n          Spectral::compute_basis_function_normalization_square<\n              Spectral::Basis::Legendre>(m()[dim]);\n    }\n    result(m.collapsed_index() - 1, m.collapsed_index() - 1) -=\n        term_with_no_derivs;\n  }\n  return result;\n}\n\n// Helper function for caching a matrix that depends on the extents of a Mesh.\n// The helper handles converting from the extents (passed in as an Index) to a\n// series of integers (used to call into the StaticCache) and back to an Index\n// (used to compute the matrix).\ntemplate <size_t VolumeDim>\nconst Matrix& cached_indicator_matrix_from_mesh_index(\n    const Limiters::Weno_detail::DerivativeWeight derivative_weight,\n    const Index<VolumeDim>& extents) {\n  using CacheEnumerationDerivativeWeight = CacheEnumeration<\n      Limiters::Weno_detail::DerivativeWeight,\n      Limiters::Weno_detail::DerivativeWeight::Unity,\n      Limiters::Weno_detail::DerivativeWeight::PowTwoEll,\n      Limiters::Weno_detail::DerivativeWeight::PowTwoEllOverEllFactorial>;\n  // Oscillation indicator needs at least two grid points\n  constexpr size_t min = 2;\n  constexpr size_t max =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n  if constexpr (VolumeDim == 1) {\n    const auto cache = make_static_cache<CacheEnumerationDerivativeWeight,\n                                         CacheRange<min, max>>(\n        [](const Limiters::Weno_detail::DerivativeWeight dw,\n           const size_t nx) -> Matrix {\n          return compute_indicator_matrix(dw, Index<1>(nx));\n        });\n    return cache(derivative_weight, extents[0]);\n  } else if constexpr (VolumeDim == 2) {\n    const auto cache =\n        make_static_cache<CacheEnumerationDerivativeWeight,\n                          CacheRange<min, max>, CacheRange<min, max>>(\n            [](const Limiters::Weno_detail::DerivativeWeight dw,\n               const size_t nx, const size_t ny) -> Matrix {\n              return compute_indicator_matrix(dw, Index<2>(nx, ny));\n            });\n    return cache(derivative_weight, extents[0], extents[1]);\n  } else {\n    const auto cache =\n        make_static_cache<CacheEnumerationDerivativeWeight,\n                          CacheRange<min, max>, CacheRange<min, max>,\n                          CacheRange<min, max>>(\n            [](const Limiters::Weno_detail::DerivativeWeight dw,\n               const size_t nx, const size_t ny, const size_t nz) -> Matrix {\n              return compute_indicator_matrix(dw, Index<3>(nx, ny, nz));\n            });\n    return cache(derivative_weight, extents[0], extents[1], extents[2]);\n  }\n}\n\n}  // namespace\n\nnamespace Limiters::Weno_detail {\n\nstd::ostream& operator<<(std::ostream& os, DerivativeWeight derivative_weight) {\n  switch (derivative_weight) {\n    case DerivativeWeight::Unity:\n      return os << \"Unity\";\n    case DerivativeWeight::PowTwoEll:\n      return os << \"PowTwoEll\";\n    case DerivativeWeight::PowTwoEllOverEllFactorial:\n      return os << \"PowTwoEllOverEllFactorial\";\n    default:\n      ERROR(\"Unknown DerivativeWeight\");\n  }\n}\n\ntemplate <size_t VolumeDim>\ndouble oscillation_indicator(const DerivativeWeight derivative_weight,\n                             const DataVector& data,\n                             const Mesh<VolumeDim>& mesh) {\n  ASSERT(mesh.basis() == make_array<VolumeDim>(Spectral::Basis::Legendre),\n         \"Unsupported basis: \" << mesh);\n  ASSERT(mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::GaussLobatto) or\n             mesh.quadrature() ==\n                 make_array<VolumeDim>(Spectral::Quadrature::Gauss),\n         \"Unsupported quadrature: \" << mesh);\n  // The oscillation indicator is computed from the N>0 spectral modes of the\n  // input data, so we need at least two modes => at least two grid points.\n  ASSERT(*alg::min_element(mesh.extents().indices()) > 1,\n         \"Unsupported extents: \" << mesh);\n\n  const Matrix& indicator_matrix = cached_indicator_matrix_from_mesh_index(\n      derivative_weight, mesh.extents());\n  const ModalVector coeffs = to_modal_coefficients(data, mesh);\n\n  double result = 0.;\n  // Note: because the 0'th modal coefficient encodes the mean of the data and\n  // does not contribute to the oscillation, it is safe to exclude it from the\n  // sum and start summing at m == 1, n == 1. Note also that the indicator\n  // matrix is computed excluding the m == 0, n == 0 elements, so the indexing\n  // is offset by 1.\n  for (size_t m = 1; m < mesh.number_of_grid_points(); ++m) {\n    for (size_t n = 1; n < mesh.number_of_grid_points(); ++n) {\n      result += coeffs[m] * coeffs[n] * indicator_matrix(m - 1, n - 1);\n    }\n  }\n  return result;\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                        \\\n  template double oscillation_indicator<DIM(data)>( \\\n      DerivativeWeight, const DataVector&, const Mesh<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <ostream>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\nnamespace Limiters::Weno_detail {\n\n// Denote different schemes for computing related oscillation indicators by\n// changing the relative weight given to each derivative of the input data.\n// - Unity: l'th derivative has weight 1\n// - PowTwoEll: l'th derivative has weight 2^(2 l - 1)\n//   This penalizes higher derivatives more strongly: w(l=4) = 128\n// - PowTwoEllOverEllFactorial: l'th derivative has weight 2^(2 l - 1) / (l!)^2\n//   This penalizes the 1st and 2nd derivatives most strongly, but then higher\n//   derivatives have decreasing weights so are weakly penalized: w(l=4) = 0.222\nenum class DerivativeWeight { Unity, PowTwoEll, PowTwoEllOverEllFactorial };\n\nstd::ostream& operator<<(std::ostream& os, DerivativeWeight derivative_weight);\n\n// Compute the WENO oscillation indicator (also called the smoothness indicator)\n//\n// The oscillation indicator measures the amount of variation in the input data,\n// with larger indicator values corresponding to a larger amount of variation\n// (either from large monotonic slopes or from oscillations).\n//\n// Implements an indicator similar to that of Eq. 23 of Dumbser2007, but with\n// the necessary adaptations for use on square/cube grids. We favor this\n// indicator because it is formulated in the reference coordinates, which we\n// use for the WENO reconstruction, and because it lends itself to an efficient\n// implementation.\ntemplate <size_t VolumeDim>\ndouble oscillation_indicator(DerivativeWeight derivative_weight,\n                             const DataVector& data,\n                             const Mesh<VolumeDim>& mesh);\n\n}  // namespace Limiters::Weno_detail\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/WenoType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nstd::ostream& Limiters::operator<<(std::ostream& os,\n                                   const Limiters::WenoType weno_type) {\n  switch (weno_type) {\n    case Limiters::WenoType::Hweno:\n      return os << \"Hweno\";\n    case Limiters::WenoType::SimpleWeno:\n      return os << \"SimpleWeno\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Missing a case for operator<<(WenoType)\");\n      // LCOV_EXCL_STOP\n  }\n}\n\ntemplate <>\nLimiters::WenoType Options::create_from_yaml<Limiters::WenoType>::create<void>(\n    const Options::Option& options) {\n  const auto weno_type_read = options.parse_as<std::string>();\n  if (weno_type_read == \"Hweno\") {\n    return Limiters::WenoType::Hweno;\n  } else if (weno_type_read == \"SimpleWeno\") {\n    return Limiters::WenoType::SimpleWeno;\n  }\n  PARSE_ERROR(options.context(), \"Failed to convert \\\"\"\n                                     << weno_type_read\n                                     << \"\\\" to WenoType. Expected one of: \"\n                                        \"{Hweno, SimpleWeno}.\");\n}\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace Limiters {\n/// \\ingroup LimitersGroup\n/// \\brief Possible types of the WENO limiter\n///\n/// \\see Limiters::Weno for a description and references.\nenum class WenoType { Hweno, SimpleWeno };\n\nstd::ostream& operator<<(std::ostream& os, Limiters::WenoType weno_type);\n}  // namespace Limiters\n\ntemplate <>\nstruct Options::create_from_yaml<Limiters::WenoType> {\n  template <typename Metavariables>\n  static Limiters::WenoType create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nLimiters::WenoType Options::create_from_yaml<Limiters::WenoType>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmodule BoundaryMessage {\n  include \"cstddef\";\n\n  namespace evolution{ namespace dg {\n    template <size_t Dim>\n    message BoundaryMessage;\n  }\n  }\n}\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.hpp\"\n\n#include <ios>\n#include <pup.h>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t Dim>\nBoundaryMessage<Dim>::BoundaryMessage(\n    const size_t subcell_ghost_data_size_in, const size_t dg_flux_data_size_in,\n    const bool owning_in, const bool enable_if_disabled_in,\n    const size_t sender_node_in, const size_t sender_core_in,\n    const int tci_status_in, const size_t integration_order_in,\n    const ::TimeStepId& current_time_step_id_in,\n    const ::TimeStepId& next_time_step_id_in,\n    const Direction<Dim>& neighbor_direction_in,\n    const ElementId<Dim>& element_id_in,\n    const Mesh<Dim>& volume_or_ghost_mesh_in,\n    const Mesh<Dim - 1>& interface_mesh_in, double* subcell_ghost_data_in,\n    double* dg_flux_data_in)\n    : subcell_ghost_data_size(subcell_ghost_data_size_in),\n      dg_flux_data_size(dg_flux_data_size_in),\n      owning(owning_in),\n      enable_if_disabled(enable_if_disabled_in),\n      sender_node(sender_node_in),\n      sender_core(sender_core_in),\n      tci_status(tci_status_in),\n      integration_order(integration_order_in),\n      current_time_step_id(current_time_step_id_in),\n      next_time_step_id(next_time_step_id_in),\n      neighbor_direction(neighbor_direction_in),\n      element_id(element_id_in),\n      volume_or_ghost_mesh(volume_or_ghost_mesh_in),\n      interface_mesh(interface_mesh_in),\n      subcell_ghost_data(subcell_ghost_data_in),\n      dg_flux_data(dg_flux_data_in) {}\n\ntemplate <size_t Dim>\nsize_t BoundaryMessage<Dim>::total_bytes_with_data(const size_t subcell_size,\n                                                   const size_t dg_size) {\n  size_t totalsize = sizeof(BoundaryMessage<Dim>);\n  totalsize += (subcell_size + dg_size) * sizeof(double);\n  return totalsize;\n}\n\ntemplate <size_t Dim>\nvoid* BoundaryMessage<Dim>::pack(BoundaryMessage<Dim>* in_msg) {\n  // If this is the case, then in_msg is already in the correct memory layout\n  // with the data appended to one contiguous buffer (aka owning) so we can just\n  // return the message itself\n  if (in_msg->owning) {\n    return static_cast<void*>(in_msg);\n  }\n\n  const size_t subcell_size = in_msg->subcell_ghost_data_size;\n  const size_t dg_size = in_msg->dg_flux_data_size;\n\n  const size_t totalsize = total_bytes_with_data(subcell_size, dg_size);\n\n  // The fact that we call the pack() function means we are sending data across\n  // address boundaries (nodes) which means we will be owning the data the\n  // pointers point to.\n  in_msg->owning = true;\n\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  auto* out_msg = reinterpret_cast<BoundaryMessage<Dim>*>(\n      CkAllocBuffer(in_msg, static_cast<int>(totalsize)));\n\n  // We cast to char* here to avoid a GCC compiler error\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  memcpy(reinterpret_cast<char*>(out_msg), &in_msg->subcell_ghost_data_size,\n         sizeof(BoundaryMessage<Dim>));\n\n  if (subcell_size != 0) {\n    // double* + 1 == char* + 8 because double* is 8 bytes\n    // Place subcell data right after dg pointer\n    out_msg->subcell_ghost_data =\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n        reinterpret_cast<double*>(std::addressof(out_msg->dg_flux_data))\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        + 1;\n    memcpy(out_msg->subcell_ghost_data, in_msg->subcell_ghost_data,\n           subcell_size * sizeof(double));\n  }\n  if (dg_size != 0) {\n    // Place dg data right after subcell data\n    out_msg->dg_flux_data =\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n        reinterpret_cast<double*>(std::addressof(out_msg->dg_flux_data))\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        + 1 + subcell_size;\n    memcpy(out_msg->dg_flux_data, in_msg->dg_flux_data,\n           dg_size * sizeof(double));\n  }\n\n  // Gotta clean up\n  // A possible future optimization is if we know we have to send to another\n  // node that we define a BoundaryMessage::allocate function to immediately\n  // allocate a message of the right size. This will reduce the number of memory\n  // allocations when sending data internode from 3 (2 on send 1 on receive) to\n  // 2 (1 on send and 1 on receive).\n  delete in_msg;  // NOLINT\n  return static_cast<void*>(out_msg);\n}\n\ntemplate <size_t Dim>\nBoundaryMessage<Dim>* BoundaryMessage<Dim>::unpack(void* in_buf) {\n  // We expect in_buf to be in a format where we can interpret it as a\n  // BoundaryMessage which is why we can immediately unpack the data as if it\n  // was a BoundaryMessage. All we have to do is set the pointers to the\n  // beginning of their respective data\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  auto* buffer = reinterpret_cast<BoundaryMessage<Dim>*>(in_buf);\n\n  const size_t subcell_size = buffer->subcell_ghost_data_size;\n  const size_t dg_size = buffer->dg_flux_data_size;\n\n  if (subcell_size != 0) {\n    // double* + 1 == char* + 8 because double* is 8 bytes\n    // Subcell data is located right after dg pointer\n    buffer->subcell_ghost_data =\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n        reinterpret_cast<double*>(std::addressof(buffer->dg_flux_data))\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        + 1;\n  } else {\n    buffer->subcell_ghost_data = nullptr;\n  }\n  if (dg_size != 0) {\n    // Dg data is located right after subcell data\n    buffer->dg_flux_data =\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n        reinterpret_cast<double*>(std::addressof(buffer->dg_flux_data))\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        + 1 + subcell_size;\n  } else {\n    buffer->dg_flux_data = nullptr;\n  }\n\n  // We don't delete in_buf here because it is actually the data we want. We\n  // didn't do any new allocations/memcpy's so no need to clean up\n  return buffer;\n}\n\ntemplate <size_t Dim>\nbool operator==(const BoundaryMessage<Dim>& lhs,\n                const BoundaryMessage<Dim>& rhs) {\n  return lhs.subcell_ghost_data_size == rhs.subcell_ghost_data_size and\n         lhs.dg_flux_data_size == rhs.dg_flux_data_size and\n         lhs.owning == rhs.owning and\n         lhs.enable_if_disabled == rhs.enable_if_disabled and\n         lhs.sender_node == rhs.sender_node and\n         lhs.sender_core == rhs.sender_core and\n         lhs.tci_status == rhs.tci_status and\n         lhs.integration_order == rhs.integration_order and\n         lhs.current_time_step_id == rhs.current_time_step_id and\n         lhs.next_time_step_id == rhs.next_time_step_id and\n         lhs.neighbor_direction == rhs.neighbor_direction and\n         lhs.element_id == rhs.element_id and\n         lhs.volume_or_ghost_mesh == rhs.volume_or_ghost_mesh and\n         lhs.interface_mesh == rhs.interface_mesh and\n         // We are guaranteed that lhs.subcell_size == rhs.subcell_size and\n         // lhs.dg_size == rhs.dg_size at this point so it's safe to loop over\n         // everything\n         std::equal(\n             lhs.subcell_ghost_data,\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             lhs.subcell_ghost_data + lhs.subcell_ghost_data_size,\n             rhs.subcell_ghost_data) and\n         std::equal(\n             lhs.dg_flux_data,\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             lhs.dg_flux_data + lhs.dg_flux_data_size, rhs.dg_flux_data);\n}\n\ntemplate <size_t Dim>\nbool operator!=(const BoundaryMessage<Dim>& lhs,\n                const BoundaryMessage<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os,\n                         const BoundaryMessage<Dim>& message) {\n  os << \"subcell_ghost_data_size = \" << message.subcell_ghost_data_size << \"\\n\";\n  os << \"dg_flux_data_size = \" << message.dg_flux_data_size << \"\\n\";\n  os << \"owning = \" << std::boolalpha << message.owning << \"\\n\";\n  os << \"enable_if_disabled = \" << std::boolalpha << message.enable_if_disabled\n     << \"\\n\";\n  os << \"sender_node = \" << message.sender_node << \"\\n\";\n  os << \"sender_core = \" << message.sender_core << \"\\n\";\n  os << \"tci_status = \" << message.tci_status << \"\\n\";\n  os << \"integration_order = \" << message.integration_order << \"\\n\";\n  os << \"current_time_ste_id = \" << message.current_time_step_id << \"\\n\";\n  os << \"next_time_ste_id = \" << message.next_time_step_id << \"\\n\";\n  os << \"neighbor_direction = \" << message.neighbor_direction << \"\\n\";\n  os << \"element_id = \" << message.element_id << \"\\n\";\n  os << \"volume_or_ghost_mesh = \" << message.volume_or_ghost_mesh << \"\\n\";\n  os << \"interface_mesh = \" << message.interface_mesh << \"\\n\";\n\n  os << \"subcell_ghost_data = (\";\n  if (message.subcell_ghost_data_size > 0) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    os << message.subcell_ghost_data[0];\n    for (size_t i = 1; i < message.subcell_ghost_data_size; i++) {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      os << \",\" << message.subcell_ghost_data[i];\n    }\n  }\n  os << \")\\n\";\n\n  os << \"dg_flux_data = (\";\n  if (message.dg_flux_data_size > 0) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    os << message.dg_flux_data[0];\n    for (size_t i = 1; i < message.dg_flux_data_size; i++) {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      os << \",\" << message.dg_flux_data[i];\n    }\n  }\n  os << \")\";\n\n  return os;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template struct BoundaryMessage<DIM(data)>;                      \\\n  template bool operator==(const BoundaryMessage<DIM(data)>& lhs,  \\\n                           const BoundaryMessage<DIM(data)>& rhs); \\\n  template bool operator!=(const BoundaryMessage<DIM(data)>& lhs,  \\\n                           const BoundaryMessage<DIM(data)>& rhs); \\\n  template std::ostream& operator<<(                               \\\n      std::ostream& os, const BoundaryMessage<DIM(data)>& message);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <ostream>\n#include <type_traits>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\n#include \"Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\"\n\nnamespace evolution::dg {\n/*!\n * \\brief [Charm++ Message]\n * (https://charm.readthedocs.io/en/latest/charm%2B%2B/manual.html#messages)\n * intended to be used in `receive_data` calls on the elements to send boundary\n * data from one element on one node, to a different element on a (potentially)\n * different node.\n *\n * If this message is to be sent across nodes, the `pack()` and `unpack()`\n * methods will be called on the sending and receiving node, respectively.\n */\ntemplate <size_t Dim>\nstruct BoundaryMessage : public CMessage_BoundaryMessage<Dim> {\n  using base = CMessage_BoundaryMessage<Dim>;\n\n  // Needed for charm registration\n  static std::string name() {\n    return \"BoundaryMessage<\" + get_output(Dim) + \">\";\n  };\n\n  size_t subcell_ghost_data_size;\n  size_t dg_flux_data_size;\n  // Whether or not this BoundaryMessage owns the data that the subcell and dg\n  // pointers point to\n  bool owning;\n  bool enable_if_disabled;\n  size_t sender_node;\n  size_t sender_core;\n  int tci_status;\n  size_t integration_order;\n  ::TimeStepId current_time_step_id;\n  ::TimeStepId next_time_step_id;\n  Direction<Dim> neighbor_direction;\n  ElementId<Dim> element_id;\n  Mesh<Dim> volume_or_ghost_mesh;\n  Mesh<Dim - 1> interface_mesh;\n\n  // If set to nullptr then we aren't sending that type of data.\n  double* subcell_ghost_data;\n  double* dg_flux_data;\n\n  BoundaryMessage() = default;\n\n  BoundaryMessage(size_t subcell_ghost_data_size_in,\n                  size_t dg_flux_data_size_in, bool owning_in,\n                  bool enable_if_disabled_in, size_t sender_node_in,\n                  size_t sender_core_in, int tci_status_in,\n                  size_t integration_order_in,\n                  const ::TimeStepId& current_time_step_id_in,\n                  const ::TimeStepId& next_time_step_id_in,\n                  const Direction<Dim>& neighbor_direction_in,\n                  const ElementId<Dim>& element_id_in,\n                  const Mesh<Dim>& volume_or_ghost_mesh_in,\n                  const Mesh<Dim - 1>& interface_mesh_in,\n                  double* subcell_ghost_data_in, double* dg_flux_data_in);\n\n  /*!\n   * \\brief This is the size (in bytes) necessary to allocate a BoundaryMessage\n   * including the arrays of data as well.\n   *\n   * This will add `(subcell_size + dg_size) * sizeof(double)` number of bytes\n   * to `sizeof(BoundaryMessage<Dim>)`.\n   */\n  static size_t total_bytes_with_data(size_t subcell_size, size_t dg_size);\n\n  static void* pack(BoundaryMessage*);\n  static BoundaryMessage* unpack(void*);\n};\n\ntemplate <size_t Dim>\nbool operator==(const BoundaryMessage<Dim>& lhs,\n                const BoundaryMessage<Dim>& rhs);\ntemplate <size_t Dim>\nbool operator!=(const BoundaryMessage<Dim>& lhs,\n                const BoundaryMessage<Dim>& rhs);\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os, const BoundaryMessage<Dim>& message);\n\n}  // namespace evolution::dg\n\n#define CK_TEMPLATES_ONLY\n#include \"Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.def.h\"\n#undef CK_TEMPLATES_ONLY\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/Messages/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_charm_module(BoundaryMessage)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryMessage.hpp\n  )\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryMessage.cpp\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_BoundaryMessage\n  )\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/MortarData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t Dim>\nvoid MortarData<Dim>::pup(PUP::er& p) {\n  p | mortar_data;\n  p | face_normal_magnitude;\n  p | face_det_jacobian;\n  p | volume_det_inv_jacobian;\n  p | mortar_mesh;\n  p | face_mesh;\n  p | volume_mesh;\n}\n\ntemplate <size_t Dim>\nbool p_project_geometric_data(\n    const gsl::not_null<::evolution::dg::MortarData<Dim>*> mortar_data,\n    const Mesh<Dim - 1>& new_face_mesh, const Mesh<Dim>& new_volume_mesh) {\n  bool changed = false;\n  // nothing needs to be done in 1D as mortars/faces are a single point...\n  if constexpr (Dim > 1) {\n    if (mortar_data->face_normal_magnitude.has_value()) {\n      const auto& old_face_mesh = mortar_data->face_mesh.value();\n      if (old_face_mesh != new_face_mesh) {\n        const auto face_projection_matrices =\n            Spectral::p_projection_matrices(old_face_mesh, new_face_mesh);\n        DataVector& n = get(mortar_data->face_normal_magnitude.value());\n        n = apply_matrices(face_projection_matrices, n,\n                           old_face_mesh.extents());\n        if (mortar_data->face_det_jacobian.has_value()) {\n          DataVector& det_j = get(mortar_data->face_det_jacobian.value());\n          det_j = apply_matrices(face_projection_matrices, det_j,\n                                 old_face_mesh.extents());\n        }\n        mortar_data->face_mesh = new_face_mesh;\n        changed = true;\n      }\n    }\n  }\n  if (mortar_data->volume_det_inv_jacobian.has_value()) {\n    const auto& old_volume_mesh = mortar_data->volume_mesh.value();\n    if (old_volume_mesh != new_volume_mesh) {\n      const auto volume_projection_matrices =\n          Spectral::p_projection_matrices(old_volume_mesh, new_volume_mesh);\n      DataVector& det_inv_j = get(mortar_data->volume_det_inv_jacobian.value());\n      det_inv_j = apply_matrices(volume_projection_matrices, det_inv_j,\n                                 old_volume_mesh.extents());\n      mortar_data->volume_mesh = new_volume_mesh;\n      changed = true;\n    }\n  }\n  return changed;\n}\n\ntemplate <size_t Dim>\nbool p_project_mortar_data(\n    const gsl::not_null<::evolution::dg::MortarData<Dim>*> mortar_data,\n    const Mesh<Dim - 1>& new_mortar_mesh) {\n  // nothing needs to be done in 1D as mortars are a single point...\n  if constexpr (Dim > 1) {\n    const auto& old_mortar_mesh = mortar_data->mortar_mesh.value();\n    if (old_mortar_mesh == new_mortar_mesh) {\n      return false;\n    }\n    const auto mortar_projection_matrices =\n        Spectral::p_projection_matrices(old_mortar_mesh, new_mortar_mesh);\n    DataVector& vars = mortar_data->mortar_data.value();\n    vars = apply_matrices(mortar_projection_matrices, vars,\n                          old_mortar_mesh.extents());\n    mortar_data->mortar_mesh = new_mortar_mesh;\n    return true;\n  } else {\n    (void)mortar_data;\n    (void)new_mortar_mesh;\n    return false;\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const MortarData<Dim>& lhs, const MortarData<Dim>& rhs) {\n  return lhs.mortar_data == rhs.mortar_data and\n         lhs.face_normal_magnitude == rhs.face_normal_magnitude and\n         lhs.face_det_jacobian == rhs.face_det_jacobian and\n         lhs.volume_det_inv_jacobian == rhs.volume_det_inv_jacobian and\n         lhs.mortar_mesh == rhs.mortar_mesh and\n         lhs.face_mesh == rhs.face_mesh and lhs.volume_mesh == rhs.volume_mesh;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const MortarData<Dim>& lhs, const MortarData<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os, const MortarData<Dim>& mortar_data) {\n  os << \"Mortar data: \" << mortar_data.mortar_data << \"\\n\";\n  os << \"Mortar mesh: \" << mortar_data.mortar_mesh << \"\\n\";\n  os << \"Face normal magnitude: \" << mortar_data.face_normal_magnitude << \"\\n\";\n  os << \"Face det(J): \" << mortar_data.face_det_jacobian << \"\\n\";\n  os << \"Face mesh: \" << mortar_data.face_mesh << \"\\n\";\n  os << \"Volume det(invJ): \" << mortar_data.volume_det_inv_jacobian << \"\\n\";\n  os << \"Volume mesh: \" << mortar_data.volume_mesh << \"\\n\";\n  return os;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                     \\\n  template class MortarData<DIM(data)>;                            \\\n  template bool p_project_geometric_data(                          \\\n      const gsl::not_null<::evolution::dg::MortarData<DIM(data)>*> \\\n          mortar_data,                                             \\\n      const Mesh<DIM(data) - 1>& new_face_mesh,                    \\\n      const Mesh<DIM(data)>& volume_mesh);                         \\\n  template bool p_project_mortar_data(                             \\\n      const gsl::not_null<::evolution::dg::MortarData<DIM(data)>*> \\\n          mortar_data,                                             \\\n      const Mesh<DIM(data) - 1>& new_mortar_mesh);                 \\\n  template bool operator==(const MortarData<DIM(data)>& lhs,       \\\n                           const MortarData<DIM(data)>& rhs);      \\\n  template bool operator!=(const MortarData<DIM(data)>& lhs,       \\\n                           const MortarData<DIM(data)>& rhs);      \\\n  template std::ostream& operator<<(std::ostream& os,              \\\n                                    const MortarData<DIM(data)>& mortar_data);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/MortarData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace evolution::dg {\n/*!\n * \\brief Data on the mortar used to compute the boundary correction for the\n * DG scheme.\n *\n * The class holds the data that has been projected to one side of the mortar.\n * It is meant to be used in a container (either MortarDataHolder or\n * TimeSteppers::BoundaryHistory) that holds MortarData on each side of the\n * mortar. The data is later used to compute the same unique boundary correction\n * on the mortar for both elements. That is, the final boundary correction\n * computation is done twice: once on each element touching the mortar. However,\n * the computation is done in such a way that the results agree.\n *\n * For local time stepping, the magnitude of the face normal is stored.\n *\n * The magnitude of the face normal is given by:\n *\n * \\f{align*}{\n *  \\sqrt{\n *   \\frac{\\partial\\xi}{\\partial x^i} \\gamma^{ij}\n *   \\frac{\\partial\\xi}{\\partial x^j}}\n * \\f}\n *\n * for a face in the \\f$\\xi\\f$-direction, with inverse spatial metric\n * \\f$\\gamma^{ij}\\f$.\n *\n * In addition, for local time stepping with Gauss points, the determinants of\n * the volume inverse Jacobian and the face Jacobian are stored.\n *\n * In addition to the (type-erased) fields on the mortar, the appropriate meshes\n * are stored.  When setting the mortar data and mortar mesh, the face mesh\n * should also be set as it is used when hybridizing DG with finite difference\n * or finite volume schemes (DG-subcell).\n *\n * If the element and its neighbor have unaligned logical coordinate\n * systems then the data and meshes are stored in the local logical\n * coordinate's orientation (\\f$\\xi\\f$ varies fastest). This means the\n * action sending the data is responsible for reorienting the data on\n * the mortar so it matches the neighbor's orientation.\n *\n * \\tparam Dim the volume dimension\n */\ntemplate <size_t Dim>\nstruct MortarData {\n  std::optional<DataVector> mortar_data{std::nullopt};\n  std::optional<Scalar<DataVector>> face_normal_magnitude{std::nullopt};\n  std::optional<Scalar<DataVector>> face_det_jacobian{std::nullopt};\n  std::optional<Scalar<DataVector>> volume_det_inv_jacobian{std::nullopt};\n  std::optional<Mesh<Dim - 1>> mortar_mesh{std::nullopt};\n  std::optional<Mesh<Dim - 1>> face_mesh{std::nullopt};\n  std::optional<Mesh<Dim>> volume_mesh{std::nullopt};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n\n/// \\brief Projects the geometric data (but not the data on the mortar\n/// grid) when p-refined\n///\n/// \\details only updates the stored mesh if the corresponding data exists\n///\n/// \\note The DG-subcell code stores the face mesh in the MortarData even when\n/// the geometric data are not used.  Currently projection of MortarData is only\n///  needed for local time-stepping which is not yet supported for DG-subcell.\n///\n/// \\returns If the data was modified\ntemplate <size_t Dim>\nbool p_project_geometric_data(\n    gsl::not_null<::evolution::dg::MortarData<Dim>*> mortar_data,\n    const Mesh<Dim - 1>& new_face_mesh, const Mesh<Dim>& new_volume_mesh);\n\n/// \\brief Projects the mortar data (but not the geometric data) when p-refined\n///\n/// \\details Used to re-project mortar data when the mortar mesh changes\n/// reactively after the neighbor face mesh is received.  In this case, the\n/// geometric data does not need to be updated as it already used the correct\n/// face/volume mesh.\n///\n/// \\returns If the data was modified\ntemplate <size_t Dim>\nbool p_project_mortar_data(\n    gsl::not_null<::evolution::dg::MortarData<Dim>*> mortar_data,\n    const Mesh<Dim - 1>& new_mortar_mesh);\n\ntemplate <size_t Dim>\nbool operator==(const MortarData<Dim>& lhs, const MortarData<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const MortarData<Dim>& lhs, const MortarData<Dim>& rhs);\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os, const MortarData<Dim>& mortar_data);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/MortarDataHolder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t Dim>\nvoid MortarDataHolder<Dim>::pup(PUP::er& p) {\n  p | local_data_;\n  p | neighbor_data_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const MortarDataHolder<Dim>& lhs,\n                const MortarDataHolder<Dim>& rhs) {\n  return lhs.local() == rhs.local() and lhs.neighbor() == rhs.neighbor();\n}\n\ntemplate <size_t Dim>\nbool operator!=(const MortarDataHolder<Dim>& lhs,\n                const MortarDataHolder<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os,\n                         const MortarDataHolder<Dim>& mortar_data_holder) {\n  os << \"Local mortar data:\\n\" << mortar_data_holder.local() << \"\\n\";\n  os << \"Neighbor mortar data:\\n\" << mortar_data_holder.neighbor() << \"\\n\";\n  return os;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                      \\\n  template class MortarDataHolder<DIM(data)>;                       \\\n  template bool operator==(const MortarDataHolder<DIM(data)>& lhs,  \\\n                           const MortarDataHolder<DIM(data)>& rhs); \\\n  template bool operator!=(const MortarDataHolder<DIM(data)>& lhs,  \\\n                           const MortarDataHolder<DIM(data)>& rhs); \\\n  template std::ostream& operator<<(                                \\\n      std::ostream& os,                                             \\\n      const MortarDataHolder<DIM(data)>& mortar_data_holder);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace evolution::dg {\n/// \\brief Data on each side of the mortar used to compute the boundary\n/// correction for the DG scheme using global time stepping.\ntemplate <size_t Dim>\nclass MortarDataHolder {\n public:\n  /// Access the data on the local side.\n  /// @{\n  const MortarData<Dim>& local() const { return local_data_; }\n  MortarData<Dim>& local() { return local_data_; }\n  /// @}\n\n  /// Access the data on the neighbor side.\n  /// @{\n  const MortarData<Dim>& neighbor() const { return neighbor_data_; }\n  MortarData<Dim>& neighbor() { return neighbor_data_; }\n  /// @}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  MortarData<Dim> local_data_{};\n  MortarData<Dim> neighbor_data_{};\n};\n\ntemplate <size_t Dim>\nbool operator==(const MortarDataHolder<Dim>& lhs,\n                const MortarDataHolder<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const MortarDataHolder<Dim>& lhs,\n                const MortarDataHolder<Dim>& rhs);\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os,\n                         const MortarDataHolder<Dim>& mortar_data_holder);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/MortarInfo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarInterpolator.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace evolution::dg {\ntemplate <size_t VolumeDim>\nMortarInfo<VolumeDim>::MortarInfo(MortarInfoData data)\n    : data_(std::move(data)) {}\n\ntemplate <size_t VolumeDim>\nvoid MortarInfo<VolumeDim>::MortarInfoData::pup(PUP::er& p) {\n  p | interpolator;\n  p | mortar_size;\n  p | interface_data_policy;\n  p | time_stepping_policy;\n}\n\ntemplate <size_t VolumeDim>\nvoid MortarInfo<VolumeDim>::pup(PUP::er& p) {\n  p | data_;\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const MortarInfo<VolumeDim>& lhs,\n                const MortarInfo<VolumeDim>& rhs) {\n  return lhs.interpolator() == rhs.interpolator() and\n         lhs.mortar_size() == rhs.mortar_size() and\n         lhs.interface_data_policy() == rhs.interface_data_policy() and\n         lhs.time_stepping_policy() == rhs.time_stepping_policy();\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const MortarInfo<VolumeDim>& lhs,\n                const MortarInfo<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const MortarInfo<VolumeDim>& mortar_info) {\n  using ::operator<<;\n  os << mortar_info.mortar_size() << \", \" << mortar_info.interface_data_policy()\n     << \", \" << mortar_info.time_stepping_policy();\n  return os;\n}\n\ntemplate class MortarInfo<1>;\ntemplate class MortarInfo<2>;\ntemplate class MortarInfo<3>;\ntemplate bool operator==(const MortarInfo<1>&, const MortarInfo<1>&);\ntemplate bool operator==(const MortarInfo<2>&, const MortarInfo<2>&);\ntemplate bool operator==(const MortarInfo<3>&, const MortarInfo<3>&);\ntemplate bool operator!=(const MortarInfo<1>&, const MortarInfo<1>&);\ntemplate bool operator!=(const MortarInfo<2>&, const MortarInfo<2>&);\ntemplate bool operator!=(const MortarInfo<3>&, const MortarInfo<3>&);\ntemplate std::ostream& operator<<(std::ostream&, const MortarInfo<1>&);\ntemplate std::ostream& operator<<(std::ostream&, const MortarInfo<2>&);\ntemplate std::ostream& operator<<(std::ostream&, const MortarInfo<3>&);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/MortarInfo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Evolution/DiscontinuousGalerkin/InterfaceDataPolicy.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarInterpolator.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace evolution::dg {\n/// \\brief Information about the mortar between two Elements\n///\n/// \\details The following information is stored:\n/// - an optional MortarInterpolator; used by a non-conforming element that has\n///   InterfaceDataPolicy::NonconformingSelfInterpolates\n/// - the mortar size; for conforming neighbors this is (in each dimension of\n///   the mortar) the SegmentSize of the mortar with respect to the face of the\n///   host Element\n/// - the InterfaceDataPolicy\n/// - the TimeSteppingPolicy\ntemplate <size_t VolumeDim>\nclass MortarInfo {\n  struct MortarInfoData {\n    std::optional<::dg::MortarInterpolator<VolumeDim>> interpolator{\n        std::nullopt};\n    std::array<Spectral::SegmentSize, VolumeDim - 1> mortar_size{};\n    InterfaceDataPolicy interface_data_policy{\n        InterfaceDataPolicy::Uninitialized};\n    TimeSteppingPolicy time_stepping_policy{TimeSteppingPolicy::Uninitialized};\n    // NOLINTNEXTLINE(google-runtime-references)\n    void pup(PUP::er& p);\n  };\n\n public:\n  MortarInfo() = default;\n  MortarInfo(const MortarInfo&) = default;\n  MortarInfo(MortarInfo&&) = default;\n  MortarInfo& operator=(const MortarInfo&) = default;\n  MortarInfo& operator=(MortarInfo&&) = default;\n  ~MortarInfo() = default;\n\n  explicit MortarInfo(MortarInfoData data);\n\n  /// @{\n  /// Interpolator used by a non-conforming element that has\n  ///   InterfaceDataPolicy::NonconformingSelfInterpolates\n  const std::optional<::dg::MortarInterpolator<VolumeDim>>& interpolator()\n      const {\n    return data_.interpolator;\n  }\n  std::optional<::dg::MortarInterpolator<VolumeDim>>& interpolator() {\n    return data_.interpolator;\n  }\n  /// @}\n\n  /// For conforming neighbors, the SegmentSize of the mortar with respect to\n  /// the face of the host Element\n  const std::array<Spectral::SegmentSize, VolumeDim - 1>& mortar_size() const {\n    return data_.mortar_size;\n  }\n\n  /// The InterfaceDataPolicy of the host Element for the mortar\n  const InterfaceDataPolicy& interface_data_policy() const {\n    return data_.interface_data_policy;\n  }\n\n  /// @{\n  /// The TimeSteppingPolicy of the mortar\n  const TimeSteppingPolicy& time_stepping_policy() const {\n    return data_.time_stepping_policy;\n  }\n  TimeSteppingPolicy& time_stepping_policy() {\n    return data_.time_stepping_policy;\n  }\n  /// @}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  MortarInfoData data_;\n};\n\ntemplate <size_t VolumeDim>\nbool operator==(const MortarInfo<VolumeDim>& lhs,\n                const MortarInfo<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nbool operator!=(const MortarInfo<VolumeDim>& lhs,\n                const MortarInfo<VolumeDim>& rhs);\n\ntemplate <size_t VolumeDim>\nstd::ostream& operator<<(std::ostream& os,\n                         const MortarInfo<VolumeDim>& mortar_info);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/MortarTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace evolution::dg {\ntemplate <size_t Dim>\nclass BoundaryMessage;\ntemplate <size_t Dim>\nclass MortarData;\ntemplate <size_t Dim>\nclass MortarDataHolder;\ntemplate <size_t Dim>\nclass MortarInfo;\n}  // namespace evolution::dg\nclass TimeStepId;\nnamespace TimeSteppers {\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nclass BoundaryHistory;\n}  // namespace TimeSteppers\n/// \\endcond\n\n/// %Tags used for DG evolution scheme.\nnamespace evolution::dg::Tags {\n/// Data on mortars, indexed by (Direction, ElementId) pairs\n///\n/// The `Dim` is the volume dimension, not the face dimension.\ntemplate <size_t Dim>\nstruct MortarData : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>>;\n};\n\n/// History of the data on mortars, indexed by (Direction, ElementId) pairs, and\n/// used by the linear multistep local time stepping code.\n///\n/// The `Dim` is the volume dimension, not the face dimension.\ntemplate <size_t Dim>\nstruct MortarDataHistory : db::SimpleTag {\n  using type =\n      DirectionalIdMap<Dim, TimeSteppers::BoundaryHistory<\n                                ::evolution::dg::MortarData<Dim>,\n                                ::evolution::dg::MortarData<Dim>, DataVector>>;\n};\n\n/// Mesh on the mortars, indexed by (Direction, ElementId) pairs\n///\n/// The `Dim` is the volume dimension, not the face dimension.\ntemplate <size_t Dim>\nstruct MortarMesh : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, Mesh<Dim - 1>>;\n};\n\n/// The ::evolution::dg::MortarInfo for each mortar\n///\n/// The `Dim` is the volume dimension, not the face dimension.\ntemplate <size_t Dim>\nstruct MortarInfo : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, ::evolution::dg::MortarInfo<Dim>>;\n};\n\n/// The next temporal id at which to receive data on the specified mortar.\n///\n/// The `Dim` is the volume dimension, not the face dimension.\ntemplate <size_t Dim>\nstruct MortarNextTemporalId : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, TimeStepId>;\n};\n\n/// \\brief The BoundaryMessage received from the inbox\n///\n/// We must store the `std::unique_ptr` in the DataBox so the memory persists in\n/// case data was sent from another node\n/// \\tparam Dim The volume dimension, not the face dimension\ntemplate <size_t Dim>\nstruct BoundaryMessageFromInbox : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, std::unique_ptr<BoundaryMessage<Dim>>>;\n};\n}  // namespace evolution::dg::Tags\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::Tags {\n/// The magnitude of the unnormalized normal covector to the interface\nstruct MagnitudeOfNormal : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The normal covector to the interface\ntemplate <size_t Dim>\nstruct NormalCovector : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\n/// The normal covector and its magnitude for all internal faces of an element.\n///\n/// The combined tag is used to make the allocations be in a Variables\n///\n/// We use a `std::optional` to keep track of whether or not these values are\n/// up-to-date.\ntemplate <size_t Dim>\nstruct NormalCovectorAndMagnitude : db::SimpleTag {\n  using type = DirectionMap<\n      Dim, std::optional<\n               Variables<tmpl::list<MagnitudeOfNormal, NormalCovector<Dim>>>>>;\n};\n}  // namespace evolution::dg::Tags\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace evolution::dg {\n/*!\n * \\brief Runtime control over time derivative work done.\n *\n * - `compute_flux_divergence`: if `true` then we compute and add the flux\n     divergence to the volume time derivative. Set to `false` to elide work\n     where you know the solution is spatially constant.\n */\ntemplate <size_t Dim>\nstruct TimeDerivativeDecisions {\n  bool compute_flux_divergence = true;\n};\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace evolution::dg {\nstd::ostream& operator<<(std::ostream& os, const TimeSteppingPolicy value) {\n  switch (value) {\n    case TimeSteppingPolicy::Uninitialized:\n      return os << \"Uninitialized\";\n    case TimeSteppingPolicy::EqualRate:\n      return os << \"EqualRate\";\n    case TimeSteppingPolicy::Conservative:\n      return os << \"Conservative\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\n          \"An unknown value of TimeSteppingPolicy was passed to the stream \"\n          \"operator.\");\n      // LCOV_EXCL_STOP\n  }\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <iosfwd>\n\nnamespace evolution::dg {\n/// \\brief Treatment of time stepping across an element boundary.\n///\n/// \\details Indicates how boundary corrections should be handled for\n/// local time-stepping across a mortar.  This controls the\n/// communication algorithm and data structures used.\n///\n/// \\note The code controlling the time step will not directly check\n/// this policy, but must be configured to make choices consistent\n/// with it.\nenum class TimeSteppingPolicy : uint8_t {\n  /// Default value is uninitialized.\n  Uninitialized,\n  /// The elements on the two sides of the mortar must have the same\n  /// step size.  Communicated boundary corrections will be added\n  /// directly to the volume RHS calculation, and algorithms that are\n  /// not local-time-stepping aware (such as subcell) can be used at\n  /// this boundary.  This is the only valid policy in a global\n  /// time-stepping evolution.\n  EqualRate,\n  /// The elements on the two sides of the mortar may have different\n  /// step sizes.  A flux-conservative boundary integral will be\n  /// performed to couple the elements.\n  Conservative,\n};\n\n/// Output operator for a TimeSteppingPolicy.\nstd::ostream& operator<<(std::ostream& os, TimeSteppingPolicy value);\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/DiscontinuousGalerkin/UsingSubcell.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\nnamespace evolution::dg {\n/*!\n * \\brief If `Metavars` has a `SubcellOptions` member struct and\n * `SubcellOptions::subcell_enabled` is `true` then inherits from\n * `std::true_type`, otherwise inherits from `std::false_type`.\n *\n * \\note This check is intentionally not inside the `DgSubcell` library so that\n * executables that do not use subcell do not need to link against it.\n */\ntemplate <typename Metavars, typename = std::void_t<>>\nstruct using_subcell : std::false_type {};\n\n/// \\cond\ntemplate <typename Metavars>\nstruct using_subcell<Metavars, std::void_t<typename Metavars::SubcellOptions>>\n    : std::bool_constant<Metavars::SubcellOptions::subcell_enabled> {};\n/// \\endcond\n\n/*!\n * \\brief If `Metavars` has a `SubcellOptions` member struct and\n * `SubcellOptions::subcell_enabled` is `true` then is `true`, otherwise\n * `false`.\n *\n * \\note This check is intentionally not inside the `DgSubcell` library so that\n * executables that do not use subcell do not need to link against it.\n */\ntemplate <typename Metavars>\nconstexpr bool using_subcell_v = using_subcell<Metavars>::value;\n}  // namespace evolution::dg\n"
  },
  {
    "path": "src/Evolution/Evolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Documents the `evolution`, `evolution::dg`, `evolution::dg::Actions`, and\n/// `evolution::dg::Initialization` namespaces\n\n#pragma once\n\n/// Functionality for evolving hyperbolic partial differential equations.\nnamespace evolution {\n/// Functionality for evolving hyperbolic partial differential equations using\n/// the discontinuous Galerkin method.\nnamespace dg {\n/// %Actions for using the discontinuous Galerkin to evolve hyperbolic partial\n/// differential equations.\nnamespace Actions {}\n/// Functionality for initializing the discontinuous Galerkin to evolve\n/// hyperbolic partial differential equations.\nnamespace Initialization {}\n}  // namespace dg\n}  // namespace evolution\n"
  },
  {
    "path": "src/Evolution/Executables/Burgers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE EvolveBurgers)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  EvolveBurgers.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Actions\n  Burgers\n  BurgersAnalyticData\n  BurgersSolutions\n  Charmxx::main\n  DgSubcell\n  DiscontinuousGalerkin\n  DomainCreators\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  FiniteDifference\n  Informer\n  LinearOperators\n  Observer\n  Options\n  Parallel\n  PhaseControl\n  Serialization\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "src/Evolution/Executables/Burgers/EvolveBurgers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/Burgers/EvolveBurgers.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<EvolutionMetavars>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &Burgers::fd::register_derived_with_charm,\n       &register_factory_classes_with_charm<EvolutionMetavars>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/Burgers/EvolveBurgers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DgSubcell/Actions/Initialize.hpp\"\n#include \"Evolution/DgSubcell/Actions/Labels.hpp\"\n#include \"Evolution/DgSubcell/Actions/ReconstructionCommunication.hpp\"\n#include \"Evolution/DgSubcell/Actions/SelectNumericalMethod.hpp\"\n#include \"Evolution/DgSubcell/Actions/TakeTimeStep.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndRollback.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndSwitchToDg.hpp\"\n#include \"Evolution/DgSubcell/DisableLts.hpp\"\n#include \"Evolution/DgSubcell/GetTciDecision.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp\"\n#include \"Evolution/DgSubcell/NeighborTciDecision.hpp\"\n#include \"Evolution/DgSubcell/PrepareNeighborData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/GhostData.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/TciOnDgGrid.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/TciOnFdGrid.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Burgers/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Burgers/Sinusoid.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Bump.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Linear.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Step.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = 1;\n  using system = Burgers::System;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = LtsTimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  // The use_dg_subcell flag controls whether to use unlimited DG (false)\n  // or a DG-FD hybrid scheme (true).\n  static constexpr bool use_dg_subcell = true;\n\n  using initial_data_list = tmpl::append<Burgers::Solutions::all_solutions,\n                                         Burgers::AnalyticData::all_data>;\n\n  using analytic_variables_tags = typename system::variables_tag::tags_list;\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_variables_tags, use_dg_subcell, initial_data_list>;\n  using error_compute = Tags::ErrorsCompute<analytic_variables_tags>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_variables_tags>;\n  using observe_fields = tmpl::push_back<\n      tmpl::append<\n          typename system::variables_tag::tags_list, error_tags,\n          tmpl::conditional_t<use_dg_subcell,\n                              tmpl::list<evolution::dg::subcell::Tags::\n                                             TciStatusCompute<volume_dim>>,\n                              tmpl::list<>>>,\n      ::Events::Tags::ObserverDetInvJacobianCompute<Frame::ElementLogical,\n                                                    Frame::Inertial>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                                   Frame::Grid>,\n          domain::Tags::Coordinates<volume_dim, Frame::Grid>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n              volume_dim, Frame::Inertial>,\n          domain::Tags::Coordinates<volume_dim, Frame::Inertial>>>;\n  using non_tensor_compute_tags = tmpl::list<\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverMeshCompute<volume_dim>,\n          ::Events::Tags::ObserverMeshCompute<volume_dim>>,\n      analytic_compute, error_compute>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Burgers::BoundaryConditions::BoundaryCondition,\n                   Burgers::BoundaryConditions::standard_boundary_conditions>,\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, non_tensor_compute_tags>,\n                       Events::time_events<system>>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   Burgers::BoundaryCorrections::standard_boundary_corrections>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>,\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   StepChoosers::standard_step_choosers<system>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::append<StepChoosers::standard_slab_choosers<\n                                    system, local_time_stepping>,\n                                tmpl::conditional_t<\n                                    use_dg_subcell and local_time_stepping,\n                                    tmpl::list<StepChoosers::FixedLtsRatio>,\n                                    tmpl::list<>>>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled = use_dg_subcell;\n    static constexpr bool subcell_enabled_at_external_boundary = true;\n\n    // We send `ghost_zone_size` cell-centered grid points for variable\n    // reconstruction, of which we need `ghost_zone_size-1` for reconstruction\n    // to the internal side of the element face, and `ghost_zone_size` for\n    // reconstruction to the external side of the element face.\n    template <typename DbTagsList>\n    static constexpr size_t ghost_zone_size(\n        const db::DataBox<DbTagsList>& box) {\n      return db::get<Burgers::fd::Tags::Reconstructor>(box).ghost_zone_size();\n    }\n\n    using DgComputeSubcellNeighborPackagedData =\n        Burgers::subcell::NeighborPackagedData;\n\n    using GhostVariables = Burgers::subcell::GhostVariables;\n  };\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>>;\n\n  using dg_step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             EvolutionMetavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      tmpl::conditional_t<use_dg_subcell,\n                          evolution::dg::subcell::Actions::TciAndRollback<\n                              Burgers::subcell::TciOnDgGrid>,\n                          tmpl::list<>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>>>;\n\n  using dg_subcell_step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::subcell::Actions::SelectNumericalMethod,\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginDg>,\n      dg_step_actions,\n      Actions::Goto<evolution::dg::subcell::Actions::Labels::EndOfSolvers>,\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginSubcell>,\n      tmpl::conditional_t<local_time_stepping,\n                          // This is just to adjust for FixedLtsRatio, so we\n                          // can pass an empty list of StepChoosers.\n                          Actions::MutateApply<ChangeStepSize<tmpl::list<>>>,\n                          tmpl::list<>>,\n      evolution::dg::subcell::Actions::SendDataForReconstruction<\n          volume_dim, Burgers::subcell::GhostVariables,\n          use_dg_element_collection>,\n      evolution::dg::subcell::Actions::ReceiveDataForReconstruction<volume_dim>,\n      Actions::Label<\n          evolution::dg::subcell::Actions::Labels::BeginSubcellAfterDgRollback>,\n      evolution::dg::subcell::fd::Actions::TakeTimeStep<\n          Burgers::subcell::TimeDerivative>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n      Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      evolution::dg::subcell::Actions::TciAndSwitchToDg<\n          Burgers::subcell::TciOnFdGrid>,\n      Actions::Label<evolution::dg::subcell::Actions::Labels::EndOfSolvers>>>;\n\n  using step_actions =\n      tmpl::conditional_t<use_dg_subcell, dg_subcell_step_actions,\n                          dg_step_actions>;\n\n  using const_global_cache_tags = tmpl::list<\n      evolution::initial_data::Tags::InitialData,\n      tmpl::conditional_t<use_dg_subcell,\n                          tmpl::list<Burgers::fd::Tags::Reconstructor>,\n                          tmpl::list<>>>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::ConservativeSystem<system>,\n\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Actions::SetSubcellGrid<volume_dim,\n                                                              system, false>,\n              evolution::dg::subcell::Actions::SetAndCommunicateInitialRdmpData<\n                  volume_dim, Burgers::subcell::SetInitialRdmpData>,\n              evolution::dg::subcell::Actions::ComputeAndSendTciOnInitialGrid<\n                  volume_dim, system, Burgers::subcell::TciOnFdGrid>,\n              evolution::dg::subcell::Actions::SetInitialGridFromTciData<\n                  volume_dim, system>>,\n          tmpl::list<evolution::Initialization::Actions::SetVariables<\n              domain::Tags::Coordinates<1, Frame::ElementLogical>>>>,\n\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<EvolutionMetavars,\n                                                  local_time_stepping>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      tmpl::conditional_t<use_dg_subcell and local_time_stepping,\n                          Initialization::Actions::InitializeItems<\n                              evolution::dg::subcell::DisableLts<1>>,\n                          tmpl::list<>>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n\n  using dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::list<observers::Observer<EvolutionMetavars>,\n                 observers::ObserverWriter<EvolutionMetavars>,\n                 dg_element_array>;\n\n  static constexpr Options::String help{\n      \"Evolve the Burgers equation.\\n\\n\"\n      \"The analytic solution is: Linear\\n\"\n      \"The numerical flux is:    LocalLaxFriedrichs\\n\"};\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Register,\n       Parallel::Phase::Evolve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Burgers)\nadd_subdirectory(Cce)\nadd_subdirectory(CurvedScalarWave)\nadd_subdirectory(ForceFree)\nadd_subdirectory(GeneralizedHarmonic)\nadd_subdirectory(GrMhd)\nadd_subdirectory(NewtonianEuler)\nadd_subdirectory(RadiationTransport)\nadd_subdirectory(ScalarAdvection)\nadd_subdirectory(ScalarTensor)\nadd_subdirectory(ScalarWave)\n"
  },
  {
    "path": "src/Evolution/Executables/Cce/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Cce\n  Charmxx::main\n  Informer\n  Interpolation\n  LinearOperators\n  Observer\n  Options\n  Parallel\n  Serialization\n  Spectral\n  SpinWeightedSphericalHarmonics\n  Time\n  Utilities\n  )\n\nfunction(add_cce_executable EXECUTABLE BOUNDARY_COMPONENT SYSTEM)\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    ${SYSTEM}.cpp\n  )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    BOUNDARY_COMPONENT=${BOUNDARY_COMPONENT}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction()\n\nadd_cce_executable(\n  CharacteristicExtract\n  Cce::H5WorldtubeBoundary\n  CharacteristicExtract\n  )\nadd_cce_executable(\n  AnalyticTestCharacteristicExtract\n  Cce::AnalyticWorldtubeBoundary\n  CharacteristicExtract\n  )\nadd_cce_executable(\n  KleinGordonCharacteristicExtract\n  Cce::KleinGordonH5WorldtubeBoundary\n  KleinGordonCharacteristicExtract\n  )\n"
  },
  {
    "path": "src/Evolution/Executables/Cce/CharacteristicExtract.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/Cce/CharacteristicExtract.hpp\"\n\n#include <vector>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/RegisterInitializeJWithCharm.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<BOUNDARY_COMPONENT>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&Cce::register_initialize_j_with_charm<\n           metavariables::evolve_ccm, metavariables::cce_boundary_component>,\n       &register_derived_classes_with_charm<Cce::WorldtubeBufferUpdater<\n           Cce::cce_metric_input_tags<ComplexModalVector>>>,\n       &register_derived_classes_with_charm<Cce::WorldtubeBufferUpdater<\n           Cce::Tags::worldtube_boundary_tags_for_writing<\n               Spectral::Swsh::Tags::SwshTransform>>>,\n       &register_derived_classes_with_charm<Cce::WorldtubeDataManager<\n           Cce::Tags::characteristic_worldtube_boundary_tags<\n               Cce::Tags::BoundaryValue>>>,\n       &register_derived_classes_with_charm<intrp::SpanInterpolator>,\n       &register_derived_classes_with_charm<Cce::Solutions::WorldtubeData>,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/Cce/CharacteristicExtract.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Executables/Cce/CharacteristicExtractBase.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/BouncingBlackHole.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/GaugeWave.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Events/ObserveFields.hpp\"\n#include \"Evolution/Systems/Cce/Events/ObserveTimeStep.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ConformalFactor.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/NoIncomingRadiation.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ZeroNonSmooth.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/System.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\ntemplate <template <typename> class BoundaryComponent>\nstruct EvolutionMetavars : CharacteristicExtractDefaults<false> {\n  using system = Cce::System<evolve_ccm>;\n  static constexpr bool local_time_stepping = true;\n  using cce_boundary_component = BoundaryComponent<EvolutionMetavars>;\n\n  using component_list =\n      tmpl::list<observers::ObserverWriter<EvolutionMetavars>,\n                 cce_boundary_component,\n                 Cce::CharacteristicEvolution<EvolutionMetavars>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, cce_step_choosers>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   StepChoosers::standard_slab_choosers<\n                       system, local_time_stepping, false>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<Event, tmpl::list<Cce::Events::ObserveFields,\n                                     Cce::Events::ObserveTimeStep>>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  static constexpr Options::String help{\n      \"Perform Cauchy Characteristic Extraction using .h5 input data.\\n\"\n      \"Uses regularity-preserving formulation.\"};\n\n  static constexpr std::array<Parallel::Phase, 4> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Evolve,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/Cce/CharacteristicExtractBase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/System.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Time/StepChoosers/Constant.hpp\"\n#include \"Time/StepChoosers/ErrorControl.hpp\"\n#include \"Time/StepChoosers/LimitIncrease.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <bool EvolveCcm>\nstruct CharacteristicExtractDefaults {\n  static constexpr bool evolve_ccm = EvolveCcm;\n  using evolved_swsh_tags = tmpl::list<Cce::Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Cce::Tags::BondiH>;\n  using evolved_coordinates_variables_tag = Tags::Variables<\n      tmpl::conditional_t<evolve_ccm,\n                          tmpl::list<Cce::Tags::CauchyCartesianCoords,\n                                     Cce::Tags::PartiallyFlatCartesianCoords,\n                                     Cce::Tags::InertialRetardedTime>,\n                          tmpl::list<Cce::Tags::CauchyCartesianCoords,\n                                     Cce::Tags::InertialRetardedTime>>>;\n\n  struct swsh_vars_selector {\n    static std::string name() { return \"SwshVars\"; }\n  };\n\n  struct coord_vars_selector {\n    static std::string name() { return \"CoordVars\"; }\n  };\n\n  using cce_boundary_communication_tags =\n      Cce::Tags::characteristic_worldtube_boundary_tags<\n          Cce::Tags::BoundaryValue>;\n\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Cce::Tags::BondiR, Cce::Tags::DuRDividedByR,\n                     Cce::Tags::BondiJ, Cce::Tags::Dr<Cce::Tags::BondiJ>,\n                     Cce::Tags::BondiBeta, Cce::Tags::BondiQ, Cce::Tags::BondiU,\n                     Cce::Tags::BondiW, Cce::Tags::BondiH>,\n          tmpl::bind<Cce::Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Cce::Tags::BondiUAtScri, Cce::Tags::PartiallyFlatGaugeC,\n      Cce::Tags::PartiallyFlatGaugeD, Cce::Tags::PartiallyFlatGaugeOmega,\n      Cce::Tags::Du<Cce::Tags::PartiallyFlatGaugeOmega>,\n      tmpl::conditional_t<\n          evolve_ccm,\n          tmpl::list<\n              Cce::Tags::CauchyGaugeC, Cce::Tags::CauchyGaugeD,\n              Cce::Tags::CauchyGaugeOmega,\n              Spectral::Swsh::Tags::Derivative<Cce::Tags::CauchyGaugeOmega,\n                                               Spectral::Swsh::Tags::Eth>>,\n          tmpl::list<>>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>,\n      Cce::all_boundary_pre_swsh_derivative_tags_for_scri,\n      Cce::all_boundary_swsh_derivative_tags_for_scri>>;\n\n  using scri_values_to_observe =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::Du<Cce::Tags::TimeIntegral<\n                     Cce::Tags::ScriPlus<Cce::Tags::Psi4>>>,\n                 Cce::Tags::EthInertialRetardedTime>;\n\n  using cce_scri_tags =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>,\n                 Cce::Tags::EthInertialRetardedTime>;\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      Cce::bondi_hypersurface_step_tags,\n      tmpl::bind<Cce::integrand_terms_to_compute_for_bondi_variable,\n                 tmpl::_1>>>;\n  using ccm_matching_tags = tmpl::list<\n      Cce::Tags::BondiJCauchyView, Cce::Tags::Psi0Match,\n      Cce::Tags::Dy<Cce::Tags::Psi0Match>,\n      Cce::Tags::Psi0, Cce::Tags::Dy<Cce::Tags::BondiJCauchyView>,\n      Cce::Tags::Dy<Cce::Tags::Dy<Cce::Tags::BondiJCauchyView>>>;\n\n  using cce_integration_independent_tags = tmpl::conditional_t<\n      evolve_ccm,\n      tmpl::append<Cce::pre_computation_tags, ccm_matching_tags,\n                   tmpl::list<Cce::Tags::DuRDividedByR>>,\n      tmpl::push_back<Cce::pre_computation_tags, Cce::Tags::DuRDividedByR>>;\n\n  using cce_temporary_equations_tags = tmpl::remove_duplicates<tmpl::flatten<\n      tmpl::transform<cce_integrand_tags,\n                      tmpl::bind<Cce::integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = Cce::all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = Cce::all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = Cce::all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags =\n      tmpl::conditional_t<evolve_ccm,\n                          tmpl::list<Cce::Tags::CauchyAngularCoords,\n                                     Cce::Tags::PartiallyFlatAngularCoords>,\n                          tmpl::list<Cce::Tags::CauchyAngularCoords>>;\n  using cce_step_choosers =\n      tmpl::list<StepChoosers::Constant, StepChoosers::LimitIncrease,\n                 StepChoosers::ErrorControl<StepChooserUse::LtsStep,\n                                            Tags::Variables<evolved_swsh_tags>,\n                                            swsh_vars_selector>,\n                 StepChoosers::ErrorControl<StepChooserUse::LtsStep,\n                                            evolved_coordinates_variables_tag,\n                                            coord_vars_selector>>;\n\n  using ccm_psi0 = tmpl::list<\n      Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n      Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n};\n"
  },
  {
    "path": "src/Evolution/Executables/Cce/KleinGordonCharacteristicExtract.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/Cce/KleinGordonCharacteristicExtract.hpp\"\n\n#include <vector>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/RegisterInitializeJWithCharm.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<BOUNDARY_COMPONENT>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&Cce::register_initialize_j_with_charm<\n           metavariables::evolve_ccm, metavariables::cce_boundary_component>,\n       &register_derived_classes_with_charm<Cce::WorldtubeBufferUpdater<\n           Cce::cce_metric_input_tags<ComplexModalVector>>>,\n       &register_derived_classes_with_charm<Cce::WorldtubeBufferUpdater<\n           Cce::Tags::worldtube_boundary_tags_for_writing<\n               Spectral::Swsh::Tags::SwshTransform>>>,\n       &register_derived_classes_with_charm<\n           Cce::WorldtubeBufferUpdater<Cce::klein_gordon_input_tags>>,\n       &register_derived_classes_with_charm<Cce::WorldtubeDataManager<\n           Cce::Tags::characteristic_worldtube_boundary_tags<\n               Cce::Tags::BoundaryValue>>>,\n       &register_derived_classes_with_charm<Cce::WorldtubeDataManager<\n           Cce::Tags::klein_gordon_worldtube_boundary_tags>>,\n       &register_derived_classes_with_charm<intrp::SpanInterpolator>,\n       &register_derived_classes_with_charm<Cce::Solutions::WorldtubeData>,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/Cce/KleinGordonCharacteristicExtract.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Executables/Cce/CharacteristicExtractBase.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/BouncingBlackHole.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/GaugeWave.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.hpp\"\n#include \"Evolution/Systems/Cce/Components/KleinGordonCharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Events/ObserveFields.hpp\"\n#include \"Evolution/Systems/Cce/Events/ObserveTimeStep.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ConformalFactor.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\ntemplate <template <typename> class BoundaryComponent>\nstruct EvolutionMetavars : CharacteristicExtractDefaults<false> {\n  using system = Cce::System<evolve_ccm>;\n  static constexpr bool local_time_stepping = true;\n  using cce_boundary_component = BoundaryComponent<EvolutionMetavars>;\n  using cce_base = CharacteristicExtractDefaults<false>;\n\n  using evolved_swsh_tags = tmpl::append<cce_base::evolved_swsh_tags,\n                                         tmpl::list<Cce::Tags::KleinGordonPsi>>;\n  using evolved_swsh_dt_tags =\n      tmpl::append<cce_base::evolved_swsh_dt_tags,\n                   tmpl::list<Cce::Tags::KleinGordonPi>>;\n\n  using klein_gordon_boundary_communication_tags =\n      Cce::Tags::klein_gordon_worldtube_boundary_tags;\n\n  using klein_gordon_gauge_boundary_tags = tmpl::list<\n      Cce::Tags::EvolutionGaugeBoundaryValue<Cce::Tags::KleinGordonPsi>,\n      Cce::Tags::EvolutionGaugeBoundaryValue<Cce::Tags::KleinGordonPi>>;\n\n  using klein_gordon_scri_tags =\n      tmpl::list<Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPi>>;\n\n  using cce_step_choosers =\n      tmpl::list<StepChoosers::Constant, StepChoosers::LimitIncrease,\n                 StepChoosers::ErrorControl<StepChooserUse::LtsStep,\n                                            Tags::Variables<evolved_swsh_tags>,\n                                            swsh_vars_selector>,\n                 StepChoosers::ErrorControl<StepChooserUse::LtsStep,\n                                            evolved_coordinates_variables_tag,\n                                            coord_vars_selector>>;\n\n  using klein_gordon_pre_swsh_derivative_tags =\n      tmpl::list<Cce::Tags::Dy<Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>>,\n                 Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>>;\n\n  using klein_gordon_swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::EthEth>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::EthEthbar>>;\n  using klein_gordon_transform_buffer_tags = tmpl::list<\n      Spectral::Swsh::Tags::SwshTransform<Cce::Tags::KleinGordonPsi>,\n      Spectral::Swsh::Tags::SwshTransform<\n          Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::KleinGordonPsi, Spectral::Swsh::Tags::Eth>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::KleinGordonPsi, Spectral::Swsh::Tags::Ethbar>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>, Spectral::Swsh::Tags::Eth>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>,\n          Spectral::Swsh::Tags::Ethbar>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEth>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEthbar>>>;\n\n  using klein_gordon_source_tags = tmpl::flatten<\n      tmpl::transform<Cce::bondi_hypersurface_step_tags,\n                      tmpl::bind<Cce::Tags::KleinGordonSource, tmpl::_1>>>;\n\n  using klein_gordon_cce_integrand_tags =\n      tmpl::list<Cce::Tags::PoleOfIntegrand<Cce::Tags::KleinGordonPi>,\n                 Cce::Tags::RegularIntegrand<Cce::Tags::KleinGordonPi>>;\n\n  using scri_values_to_observe =\n      tmpl::append<cce_base::scri_values_to_observe,\n                   tmpl::list<Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPsi>>>;\n\n  using cce_scri_tags =\n      tmpl::append<cce_base::cce_scri_tags,\n                   tmpl::list<Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPsi>>>;\n\n  using component_list =\n      tmpl::list<observers::ObserverWriter<EvolutionMetavars>,\n                 cce_boundary_component,\n                 Cce::KleinGordonCharacteristicEvolution<EvolutionMetavars>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, cce_step_choosers>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   StepChoosers::standard_slab_choosers<\n                       system, local_time_stepping, false>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<Event, tmpl::list<Cce::Events::ObserveFields,\n                                     Cce::Events::ObserveTimeStep>>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  static constexpr Options::String help{\n      \"Perform Cauchy Characteristic Extraction for the Klein-Gordon system \"\n      \"coupled with General Relativity, using .h5 input data.\"};\n\n  static constexpr std::array<Parallel::Phase, 4> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Evolve,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/CurvedScalarWave/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Actions\n  Charmxx::main\n  CoordinateMaps\n  CurvedScalarWave\n  CurvedWaveEquationAnalyticData\n  DiscontinuousGalerkin\n  DomainCreators\n  Events\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  GeneralRelativity\n  Informer\n  LinearOperators\n  MathFunctions\n  Observer\n  Options\n  Parallel\n  PhaseControl\n  Serialization\n  Time\n  Utilities\n  WaveEquationSolutions\n)\n\nset(INTERPOLATION_LIBS_TO_LINK\n  ApparentHorizonFinder\n  ParallelInterpolation\n)\n\nfunction(add_curved_scalar_wave_executable EXECUTABLE_SUFFIX DIM BACKGROUND)\n  set(EXECUTABLE \"EvolveCurvedScalarWave${EXECUTABLE_SUFFIX}${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    EvolveCurvedScalarWave.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    BACKGROUND=${BACKGROUND}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\n  if (DIM EQUAL 3)\n    target_link_libraries(${EXECUTABLE} PRIVATE ${INTERPOLATION_LIBS_TO_LINK})\n  endif()\nendfunction(add_curved_scalar_wave_executable)\n\nfunction(add_flat_plane_wave_executable DIM)\n  add_curved_scalar_wave_executable(\n    Minkowski\n    ${DIM}\n    gr::Solutions::Minkowski<${DIM}>\n  )\nendfunction(add_flat_plane_wave_executable)\n\nadd_flat_plane_wave_executable(1)\nadd_flat_plane_wave_executable(2)\nadd_flat_plane_wave_executable(3)\n\nadd_curved_scalar_wave_executable(\n  KerrSchild\n  3\n  gr::Solutions::KerrSchild)\n\nset(WORLDTUBE_EXECUTABLE EvolveWorldtubeCurvedScalarWaveKerrSchild3D)\nadd_spectre_executable(\n  ${WORLDTUBE_EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  EvolveWorldtubeCurvedScalarWave.cpp\n  )\ntarget_link_libraries(\n  ${WORLDTUBE_EXECUTABLE}\n  PRIVATE\n  ${LIBS_TO_LINK}\n  ${INTERPOLATION_LIBS_TO_LINK}\n  ControlSystem\n  ScalarWaveWorldtube\n  )\n"
  },
  {
    "path": "src/Evolution/Executables/CurvedScalarWave/EvolveCurvedScalarWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/CurvedScalarWave/EvolveCurvedScalarWave.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<DIM, BACKGROUND>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/CurvedScalarWave/EvolveCurvedScalarWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Norms.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BackgroundSpacetime.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/CalculateGrVars.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Constraints.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Initialize.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/PsiSquared.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"IO/Importers/Actions/RegisterWithElementDataReader.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveNorms.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/ByBlock.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\ntemplate <size_t Dim, typename BackgroundSpacetime>\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = Dim;\n  using background_spacetime = BackgroundSpacetime;\n\n  using system = CurvedScalarWave::System<Dim>;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = LtsTimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using deriv_compute = ::Tags::DerivCompute<\n      typename system::variables_tag, domain::Tags::Mesh<volume_dim>,\n      domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      typename system::gradient_variables>;\n\n  using observe_fields = tmpl::push_back<\n      tmpl::flatten<tmpl::list<\n          tmpl::append<typename system::variables_tag::tags_list,\n                       typename deriv_compute::type::tags_list>,\n          ::Events::Tags::ObserverDetInvJacobianCompute<Frame::ElementLogical,\n                                                        Frame::Inertial>,\n          CurvedScalarWave::Tags::OneIndexConstraintCompute<volume_dim>,\n          CurvedScalarWave::Tags::TwoIndexConstraintCompute<volume_dim>,\n          ::Tags::PointwiseL2NormCompute<\n              CurvedScalarWave::Tags::OneIndexConstraint<volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              CurvedScalarWave::Tags::TwoIndexConstraint<volume_dim>>>>,\n      domain::Tags::Coordinates<volume_dim, Frame::Grid>,\n      domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n  using non_tensor_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 deriv_compute>;\n\n  static constexpr bool interpolate = volume_dim == 3;\n  struct SphericalSurface\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target =\n        tmpl::list<gr::Tags::SpatialMetric<DataVector, Dim>,\n                   CurvedScalarWave::Tags::Psi>;\n    using compute_items_on_target =\n        tmpl::list<CurvedScalarWave::Tags::PsiSquaredCompute,\n                   gr::surfaces::Tags::AreaElementCompute<::Frame::Inertial>,\n                   gr::surfaces::Tags::SurfaceIntegralCompute<\n                       CurvedScalarWave::Tags::PsiSquared, ::Frame::Inertial>>;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<SphericalSurface, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<gr::surfaces::Tags::SurfaceIntegral<\n                CurvedScalarWave::Tags::PsiSquared, ::Frame::Inertial>>,\n            SphericalSurface>>;\n    template <typename metavariables>\n    using interpolating_component = typename metavariables::dg_element_array;\n  };\n\n  using initial_data_list = tmpl::flatten<tmpl::list<\n      ScalarWave::Solutions::all_solutions<Dim>,\n      tmpl::conditional_t<\n          Dim == 3,\n          tmpl::list<CurvedScalarWave::AnalyticData::PureSphericalHarmonic,\n                     CurvedScalarWave::NumericInitialData>,\n          tmpl::list<>>>>;\n  using interpolation_target_tags = tmpl::list<SphericalSurface>;\n  using interpolator_source_vars =\n      tmpl::list<gr::Tags::SpatialMetric<DataVector, volume_dim>,\n                 CurvedScalarWave::Tags::Psi>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            CurvedScalarWave::BoundaryConditions::BoundaryCondition<volume_dim>,\n            CurvedScalarWave::BoundaryConditions::standard_boundary_conditions<\n                volume_dim>>,\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<\n            Event,\n            tmpl::flatten<tmpl::list<\n                Events::Completion,\n                dg::Events::field_observations<volume_dim, observe_fields,\n                                               non_tensor_compute_tags>,\n                tmpl::conditional_t<\n                    interpolate,\n                    intrp::Events::InterpolateWithoutInterpComponent<\n                        volume_dim, SphericalSurface, interpolator_source_vars>,\n                    tmpl::list<>>,\n                Events::time_events<system>,\n                dg::Events::ObserveTimeStepVolume<system>>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   CurvedScalarWave::BoundaryCorrections::\n                       standard_boundary_corrections<volume_dim>>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>,\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   MathFunctions::all_math_functions<1, Frame::Inertial>>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   tmpl::push_back<StepChoosers::standard_step_choosers<system>,\n                                   StepChoosers::ByBlock<volume_dim>>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::push_back<StepChoosers::standard_slab_choosers<\n                                       system, local_time_stepping>,\n                                   StepChoosers::ByBlock<volume_dim>>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  using observed_reduction_data_tags = observers::collect_reduction_data_tags<\n      tmpl::at<typename factory_creation::factory_classes, Event>>;\n\n  static constexpr bool use_filtering = true;\n\n  using step_actions = tmpl::flatten<tmpl::list<\n      CurvedScalarWave::Actions::CalculateGrVars<system, true>,\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             EvolutionMetavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      tmpl::conditional_t<\n          use_filtering,\n          dg::Actions::Filter<Filters::Exponential<0>,\n                              tmpl::list<CurvedScalarWave::Tags::Psi,\n                                         CurvedScalarWave::Tags::Pi,\n                                         CurvedScalarWave::Tags::Phi<Dim>>>,\n          tmpl::list<>>>>;\n\n  using const_global_cache_tags = tmpl::list<\n      CurvedScalarWave::Tags::BackgroundSpacetime<BackgroundSpacetime>,\n      evolution::initial_data::Tags::InitialData>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::NonconservativeSystem<system>,\n      CurvedScalarWave::Actions::CalculateGrVars<system, false>,\n      Initialization::Actions::AddSimpleTags<\n          CurvedScalarWave::Initialization::InitializeConstraintDampingGammas<\n              volume_dim>>,\n      Initialization::Actions::AddComputeTags<\n          tmpl::flatten<tmpl::list<StepChoosers::step_chooser_compute_tags<\n              EvolutionMetavars, local_time_stepping>>>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      intrp::Actions::ElementInitInterpPoints<volume_dim,\n                                              interpolation_target_tags>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n\n  using dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::RegisterWithElementDataReader,\n              tmpl::list<importers::Actions::RegisterWithElementDataReader,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::ImportInitialData,\n              tmpl::list<CurvedScalarWave::Actions::SetInitialData,\n                         CurvedScalarWave::Actions::ReceiveNumericInitialData,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list = tmpl::flatten<\n      tmpl::list<observers::Observer<EvolutionMetavars>,\n                 observers::ObserverWriter<EvolutionMetavars>,\n                 importers::ElementDataReader<EvolutionMetavars>,\n                 tmpl::conditional_t<interpolate,\n                                     intrp::InterpolationTarget<\n                                         EvolutionMetavars, SphericalSurface>,\n                                     tmpl::list<>>,\n                 dg_element_array>>;\n\n  static constexpr Options::String help{\n      \"Evolve a scalar wave in Dim spatial dimension on a curved background \"\n      \"spacetime.\"};\n\n  static constexpr auto default_phase_order =\n      std::array{Parallel::Phase::Initialization,\n                 Parallel::Phase::RegisterWithElementDataReader,\n                 Parallel::Phase::ImportInitialData,\n                 Parallel::Phase::InitializeTimeStepperHistory,\n                 Parallel::Phase::Register,\n                 Parallel::Phase::Evolve,\n                 Parallel::Phase::Exit};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/CurvedScalarWave/EvolveWorldtubeCurvedScalarWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/CurvedScalarWave/EvolveWorldtubeCurvedScalarWave.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/InitialData/ZerothOrderPuncture.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nusing metavariables =\n    EvolutionMetavars<gr::Solutions::KerrSchild,\n                      CurvedScalarWave::AnalyticData::ZerothOrderPuncture>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/CurvedScalarWave/EvolveWorldtubeCurvedScalarWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/ElementDistribution.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Executables/GeneralizedHarmonic/Deadlock.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BackgroundSpacetime.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Worldtube.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/CalculateGrVars.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Constraints.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Initialize.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/PsiSquared.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/InitializeConstraintGammas.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/InitializeCurrentIteration.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/IteratePunctureField.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/SendToWorldtube.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/InsideHorizon.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/OrbitRadius.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveNorms.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveLineSegment.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveSurfaceData.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/PointInfoTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/LineSegment.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/ByBlock.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\ntemplate <typename BackgroundSpacetime, typename InitialData>\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = 3;\n  using background_spacetime = BackgroundSpacetime;\n  static_assert(\n      is_analytic_data_v<InitialData> xor is_analytic_solution_v<InitialData>,\n      \"initial_data must be either an analytic_data or an analytic_solution\");\n\n  using solutions_and_data = tmpl::list<InitialData>;\n  using system = CurvedScalarWave::System<volume_dim>;\n  using temporal_id = Tags::TimeStepId;\n  // LTS not implemented yet\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using deriv_compute = ::Tags::DerivCompute<\n      typename system::variables_tag, domain::Tags::Mesh<volume_dim>,\n      domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      typename system::gradient_variables>;\n\n  using observe_fields = tmpl::push_back<\n      tmpl::flatten<tmpl::list<\n          tmpl::append<typename system::variables_tag::tags_list,\n                       typename deriv_compute::type::tags_list>,\n          ::Events::Tags::ObserverDetInvJacobianCompute<Frame::ElementLogical,\n                                                        Frame::Inertial>,\n          CurvedScalarWave::Tags::OneIndexConstraintCompute<volume_dim>,\n          CurvedScalarWave::Tags::TwoIndexConstraintCompute<volume_dim>>>,\n      domain::Tags::Coordinates<volume_dim, Frame::Grid>,\n      domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n  using non_tensor_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 deriv_compute>;\n\n  struct Spheres : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    static std::string name() { return \"Spheres\"; }\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target =\n        tmpl::list<CurvedScalarWave::Tags::Psi,\n                   domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n    using compute_items_on_target = tmpl::list<>;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<Spheres, Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveSurfaceData<\n            tmpl::list<CurvedScalarWave::Tags::Psi>, Spheres, Frame::Inertial>>;\n    template <typename metavariables>\n    using interpolating_component = typename metavariables::dg_element_array;\n  };\n\n  using interpolation_target_tags = tmpl::list<Spheres>;\n  using interpolator_source_vars =\n      tmpl::list<CurvedScalarWave::Tags::Psi,\n                 domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            CurvedScalarWave::BoundaryConditions::BoundaryCondition<volume_dim>,\n            tmpl::flatten<tmpl::list<\n                CurvedScalarWave::BoundaryConditions::\n                    standard_boundary_conditions<volume_dim>,\n                CurvedScalarWave::BoundaryConditions::Worldtube<volume_dim>>>>,\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>,\n                   tmpl::list<domain::creators::BinaryCompactObject<true>>>,\n        tmpl::pair<\n            Event,\n            tmpl::flatten<tmpl::list<\n                Events::time_events<system>, Events::Completion,\n                intrp::Events::InterpolateWithoutInterpComponent<\n                    volume_dim, Spheres, interpolator_source_vars>,\n                dg::Events::field_observations<volume_dim, observe_fields,\n                                               non_tensor_compute_tags>>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   CurvedScalarWave::BoundaryCorrections::\n                       standard_boundary_corrections<volume_dim>>,\n        tmpl::pair<evolution::initial_data::InitialData, solutions_and_data>,\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   MathFunctions::all_math_functions<1, Frame::Inertial>>,\n        tmpl::pair<PhaseChange,\n                   tmpl::list<PhaseControl::VisitAndReturn<\n                                  Parallel::Phase::LoadBalancing>,\n                              PhaseControl::CheckpointAndExitAfterWallclock>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::push_back<StepChoosers::standard_slab_choosers<\n                                       system, local_time_stepping>,\n                                   StepChoosers::ByBlock<volume_dim>>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger,\n                   tmpl::flatten<tmpl::list<\n                       Triggers::logical_triggers, Triggers::time_triggers,\n                       Triggers::OrbitRadius, Triggers::InsideHorizon>>>>;\n  };\n  using observed_reduction_data_tags = observers::collect_reduction_data_tags<\n      tmpl::at<typename factory_creation::factory_classes, Event>>;\n  static constexpr bool use_filtering = true;\n\n  using step_actions = tmpl::flatten<tmpl::list<\n      CurvedScalarWave::Actions::CalculateGrVars<system, true>,\n      CurvedScalarWave::Worldtube::Actions::SendToWorldtube,\n      CurvedScalarWave::Worldtube::Actions::IteratePunctureField,\n      CurvedScalarWave::Worldtube::Actions::ReceiveWorldtubeData,\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n      Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      tmpl::conditional_t<\n          use_filtering,\n          dg::Actions::Filter<\n              Filters::Exponential<0>,\n              tmpl::list<CurvedScalarWave::Tags::Psi,\n                         CurvedScalarWave::Tags::Pi,\n                         CurvedScalarWave::Tags::Phi<volume_dim>>>,\n          tmpl::list<>>>>;\n\n  using const_global_cache_tags = tmpl::list<\n      CurvedScalarWave::Tags::BackgroundSpacetime<BackgroundSpacetime>,\n      evolution::initial_data::Tags::InitialData,\n      CurvedScalarWave::Worldtube::Tags::ExcisionSphere<volume_dim>,\n      CurvedScalarWave::Worldtube::Tags::ExpansionOrder,\n      CurvedScalarWave::Worldtube::Tags::WorldtubeRadiusParameters,\n      CurvedScalarWave::Worldtube::Tags::BlackHoleRadiusParameters,\n      CurvedScalarWave::Worldtube::Tags::Charge,\n      CurvedScalarWave::Worldtube::Tags::SelfForceTurnOnTime,\n      CurvedScalarWave::Worldtube::Tags::SelfForceTurnOnInterval,\n      CurvedScalarWave::Worldtube::Tags::Mass,\n      CurvedScalarWave::Worldtube::Tags::MaxIterations,\n      CurvedScalarWave::Worldtube::Tags::ObserveCoefficientsTrigger,\n      CurvedScalarWave::Worldtube::Tags::Verbosity>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::NonconservativeSystem<system>,\n      CurvedScalarWave::Actions::CalculateGrVars<system, false>,\n      Initialization::Actions::AddSimpleTags<\n          CurvedScalarWave::Worldtube::Initialization::\n              InitializeCurrentIteration,\n          CurvedScalarWave::Initialization::InitializeEvolvedVariables<\n              volume_dim, solutions_and_data>>,\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<EvolutionMetavars,\n                                                  local_time_stepping>>,\n      Initialization::Actions::AddComputeTags<tmpl::list<\n          CurvedScalarWave::Worldtube::Tags::ParticlePositionVelocityCompute<\n              volume_dim>,\n          CurvedScalarWave::Worldtube::Tags::ConstraintGamma1Compute,\n          CurvedScalarWave::Worldtube::Tags::ConstraintGamma2Compute,\n          CurvedScalarWave::Worldtube::Tags::FaceCoordinatesCompute<\n              volume_dim, Frame::Inertial, true>,\n          CurvedScalarWave::Worldtube::Tags::FaceCoordinatesCompute<\n              volume_dim, Frame::Inertial, true>,\n          CurvedScalarWave::Worldtube::Tags::GeodesicAccelerationCompute<3>,\n          CurvedScalarWave::Worldtube::Tags::PunctureFieldCompute<volume_dim>,\n          CurvedScalarWave::Worldtube::Tags::FaceQuantitiesCompute,\n          ::domain::Tags::GridToInertialInverseJacobian<volume_dim>>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      intrp::Actions::ElementInitInterpPoints<volume_dim,\n                                              interpolation_target_tags>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n\n  using dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  domain::Actions::CheckFunctionsOfTimeAreReady<volume_dim>,\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  static void run_deadlock_analysis_simple_actions(\n      Parallel::GlobalCache<EvolutionMetavars>& cache,\n      const std::vector<std::string>& deadlocked_components) {\n    gh::deadlock::run_deadlock_analysis_simple_actions<\n        dg_element_array, tmpl::list<>, interpolation_target_tags,\n        tmpl::list<>>(cache, deadlocked_components);\n  }\n\n  using component_list = tmpl::flatten<tmpl::list<\n      observers::Observer<EvolutionMetavars>,\n      observers::ObserverWriter<EvolutionMetavars>,\n      intrp::InterpolationTarget<EvolutionMetavars, Spheres>,\n      CurvedScalarWave::Worldtube::WorldtubeSingleton<EvolutionMetavars>,\n      dg_element_array>>;\n\n  static constexpr Options::String help{\n      \"Evolve a scalar point charge in circular orbit around a Schwarzschild \"\n      \"black hole.\"};\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Register,\n       Parallel::Phase::Evolve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/ForceFree/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE \"EvolveForceFree\")\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  EvolveForceFree.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Actions\n  Charmxx::main\n  DiscontinuousGalerkin\n  DomainCreators\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  ForceFree\n  ForceFreeAnalyticData\n  ForceFreeSolutions\n  GeneralRelativitySolutions\n  IO\n  Informer\n  LinearOperators\n  Options\n  Parallel\n  PhaseControl\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "src/Evolution/Executables/ForceFree/EvolveForceFree.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/ForceFree/EvolveForceFree.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<EvolutionMetavars>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_factory_classes_with_charm<EvolutionMetavars>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/ForceFree/EvolveForceFree.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BackgroundGrVars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/ElectricCurrentDensity.hpp\"\n#include \"Evolution/Systems/ForceFree/ElectromagneticVariables.hpp\"\n#include \"Evolution/Systems/ForceFree/MaskNeutronStarInterior.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = 3;\n  using system = ForceFree::System;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using initial_data_list = tmpl::append<ForceFree::Solutions::all_solutions,\n                                         ForceFree::AnalyticData::all_data>;\n\n  using analytic_variables_tags = typename system::variables_tag::tags_list;\n\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_variables_tags, false, initial_data_list>;\n\n  using error_compute = Tags::ErrorsCompute<analytic_variables_tags>;\n\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_variables_tags>;\n\n  using observe_fields = tmpl::push_back<\n      tmpl::append<typename system::variables_tag::tags_list, error_tags>,\n      domain::Tags::Coordinates<volume_dim, Frame::Grid>,\n      domain::Tags::Coordinates<volume_dim, Frame::Inertial>,\n      ForceFree::Tags::ElectricFieldCompute,\n      ForceFree::Tags::MagneticFieldCompute,\n      ForceFree::Tags::ChargeDensityCompute,\n      ForceFree::Tags::ElectricCurrentDensityCompute>;\n\n  using non_tensor_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 ::Events::Tags::ObserverDetInvJacobianCompute<\n                     Frame::ElementLogical, Frame::Inertial>,\n                 analytic_compute, error_compute>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<ForceFree::BoundaryConditions::BoundaryCondition,\n                   ForceFree::BoundaryConditions::standard_boundary_conditions>,\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, non_tensor_compute_tags>,\n                       Events::time_events<system>>>>,\n        tmpl::pair<\n            evolution::BoundaryCorrection,\n            ForceFree::BoundaryCorrections::standard_boundary_corrections>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>,\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<PhaseChange,\n                   tmpl::list<PhaseControl::VisitAndReturn<\n                                  Parallel::Phase::LoadBalancing>,\n                              PhaseControl::CheckpointAndExitAfterWallclock>>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   StepChoosers::standard_step_choosers<system>>,\n        tmpl::pair<\n            StepChooser<StepChooserUse::Slab>,\n            StepChoosers::standard_slab_choosers<system, local_time_stepping>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>>;\n\n  using dg_step_actions = tmpl::flatten<tmpl::list<\n      Actions::MutateApply<\n          evolution::dg::BackgroundGrVars<system, EvolutionMetavars>>,\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             EvolutionMetavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n\n      dg::Actions::Filter<\n          Filters::Exponential<0>,\n          tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                     ForceFree::Tags::TildePsi, ForceFree::Tags::TildePhi,\n                     ForceFree::Tags::TildeQ>>\n\n      >>;\n\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData,\n                 ForceFree::Tags::KappaPsi, ForceFree::Tags::KappaPhi,\n                 ForceFree::Tags::ParallelConductivity>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::AddSimpleTags<\n          evolution::dg::BackgroundGrVars<system, EvolutionMetavars>>,\n      Initialization::Actions::ConservativeSystem<system>,\n      evolution::Initialization::Actions::SetVariables<\n          domain::Tags::Coordinates<3, Frame::ElementLogical>>,\n\n      Initialization::Actions::AddSimpleTags<\n          ForceFree::MaskNeutronStarInterior<EvolutionMetavars, false>>,\n\n      Initialization::Actions::AddComputeTags<\n          tmpl::list<ForceFree::Tags::ComputeTildeJ>>,\n\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<EvolutionMetavars,\n                                                  local_time_stepping>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n\n  using dg_element_array_component = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<dg_step_actions, system>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, dg_step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array_component, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::list<observers::Observer<EvolutionMetavars>,\n                 observers::ObserverWriter<EvolutionMetavars>,\n                 dg_element_array_component>;\n\n  static constexpr Options::String help{\n      \"Evolve the GRFFE system with divergence cleaning.\\n\"};\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Register,\n       Parallel::Phase::Evolve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Actions\n  AmrEvents\n  Charmxx::main\n  CoordinateMaps\n  DiscontinuousGalerkin\n  Domain\n  DomainCreators\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  GeneralRelativity\n  GeneralRelativityAnalyticData\n  GeneralRelativitySolutions\n  GeneralizedHarmonic\n  Importers\n  Informer\n  Interpolation\n  LinearOperators\n  MathFunctions\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelInterpolation\n  PhaseControl\n  Printf\n  Serialization\n  Time\n  Utilities\n  )\n\n# GH without horizons\nfunction(add_generalized_harmonic_executable_without_horizon DIM)\n  set(EXECUTABLE \"EvolveGhNoBlackHole${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    EvolveGhNoBlackHole.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    USE_LTS=false\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction()\n\nadd_generalized_harmonic_executable_without_horizon(1)\nadd_generalized_harmonic_executable_without_horizon(2)\nadd_generalized_harmonic_executable_without_horizon(3)\n\n# Single BH\nset(SINGLE_BH_EXECUTABLE EvolveGhSingleBlackHole)\nadd_spectre_executable(\n  ${SINGLE_BH_EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  EvolveGhSingleBlackHole.cpp\n  )\ntarget_link_libraries(\n  ${SINGLE_BH_EXECUTABLE}\n  PRIVATE\n  ${LIBS_TO_LINK}\n  ApparentHorizonFinder\n  ApparentHorizonFinderCriteria\n  Cce\n  ControlSystem\n  Deadlock\n  )\ntarget_compile_definitions(\n  ${SINGLE_BH_EXECUTABLE}\n  PRIVATE\n  USE_LTS=true\n  )\n\n\n# BBH\nset(BBH_EXECUTABLE EvolveGhBinaryBlackHole)\nadd_spectre_executable(\n  ${BBH_EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  EvolveGhBinaryBlackHole.cpp\n)\ntarget_link_libraries(\n  ${BBH_EXECUTABLE}\n  PRIVATE\n  ${LIBS_TO_LINK}\n  ApparentHorizonFinder\n  ApparentHorizonFinderCriteria\n  Cce\n  ControlSystem\n  Deadlock\n  EvolutionTriggers\n  )\nif (TARGET SpEC::Exporter)\n  target_compile_definitions(\n    ${BBH_EXECUTABLE} PRIVATE HAS_SPEC_EXPORTER)\nendif()\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/Deadlock.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"ControlSystem/Actions/PrintCurrentMeasurement.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Evolution/Deadlock/PrintDgElementArray.hpp\"\n#include \"Evolution/Deadlock/PrintFunctionsOfTime.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/SimpleActionOnElement.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/PrintDeadlockAnalysis.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/PrintInterpolationTargetForDeadlock.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace intrp {\ntemplate <class Metavariables, typename InterpolationTargetTag>\nstruct InterpolationTarget;\n}  // namespace intrp\nnamespace observers {\ntemplate <class Metavariables>\nstruct ObserverWriter;\n}  // namespace observers\n/// \\endcond\n\nnamespace gh::deadlock {\ntemplate <typename DgElementArray, typename ControlComponents,\n          typename InterpolationTargetTags, typename AhFinders,\n          typename Metavariables>\nvoid run_deadlock_analysis_simple_actions(\n    Parallel::GlobalCache<Metavariables>& cache,\n    const std::vector<std::string>& deadlocked_components) {\n  const std::string deadlock_dir{\"deadlock\"};\n  if (file_system::check_if_dir_exists(deadlock_dir)) {\n    file_system::rm(deadlock_dir, true);\n  }\n\n  file_system::create_directory(deadlock_dir);\n\n  Parallel::simple_action<::deadlock::PrintFunctionsOfTime>(\n      Parallel::get_parallel_component<\n          observers::ObserverWriter<Metavariables>>(cache),\n      deadlock_dir + \"/functions_of_time.out\");\n\n  {\n    auto cache_proxy = cache.get_this_proxy();\n\n    cache_proxy.print_mutable_cache_callbacks(deadlock_dir +\n                                              \"/mutable_cache_callbacks.out\");\n  }\n\n  if constexpr (tmpl::size<InterpolationTargetTags>::value > 0) {\n    const std::string intrp_target_file =\n        deadlock_dir + \"/interpolation_targets.out\";\n    tmpl::for_each<InterpolationTargetTags>(\n        [&cache, &intrp_target_file](const auto tag_v) {\n          using TargetTag = tmpl::type_from<decltype(tag_v)>;\n\n          Parallel::simple_action<::deadlock::PrintInterpolationTarget>(\n              Parallel::get_parallel_component<\n                  intrp::InterpolationTarget<Metavariables, TargetTag>>(cache),\n              intrp_target_file);\n        });\n  }\n\n  if constexpr (tmpl::size<AhFinders>::value > 0) {\n    tmpl::for_each<AhFinders>([&cache,\n                               &deadlock_dir](const auto horizon_metavars_v) {\n      using HorizonMetavars = tmpl::type_from<decltype(horizon_metavars_v)>;\n      const std::string ah_finder_file =\n          deadlock_dir + \"/\" + pretty_type::name<HorizonMetavars>() + \".out\";\n      Parallel::simple_action<ah::Actions::PrintDeadlockAnalysis>(\n          Parallel::get_parallel_component<\n              ah::Component<Metavariables, HorizonMetavars>>(cache),\n          ah_finder_file);\n    });\n  }\n\n  if (alg::count(deadlocked_components, pretty_type::name<DgElementArray>()) ==\n      1) {\n    if constexpr (tmpl::size<ControlComponents>::value > 0) {\n      tmpl::for_each<ControlComponents>(\n          [&cache, &deadlock_dir](auto component_v) {\n            using component = tmpl::type_from<decltype(component_v)>;\n            Parallel::simple_action<\n                control_system::Actions::PrintCurrentMeasurement>(\n                Parallel::get_parallel_component<component>(cache),\n                deadlock_dir + \"/control_systems.out\");\n          });\n    }\n\n    const std::string element_array_file =\n        deadlock_dir + \"/dg_element_array.out\";\n    if constexpr (Parallel::is_dg_element_collection_v<DgElementArray>) {\n      Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n          ::deadlock::PrintElementInfo, true>>(\n          Parallel::get_parallel_component<DgElementArray>(cache),\n          element_array_file);\n    } else {\n      Parallel::simple_action<::deadlock::PrintElementInfo>(\n          Parallel::get_parallel_component<DgElementArray>(cache),\n          element_array_file);\n    }\n  }\n}\n}  // namespace gh::deadlock\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/EvolveGhBinaryBlackHole.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/GeneralizedHarmonic/EvolveGhBinaryBlackHole.hpp\"\n\n#include <vector>\n\n#include \"ControlSystem/ControlErrors/Size/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/System/AttachDebugger.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<EvolutionMetavars>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&sys::attach_debugger, &domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &ConstraintDamping::register_derived_with_charm,\n       &control_system::size::register_derived_with_charm,\n       &register_factory_classes_with_charm<EvolutionMetavars>,\n       &amr::register_callbacks<EvolutionMetavars,\n                                EvolutionMetavars::gh_dg_element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/EvolveGhBinaryBlackHole.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <optional>\n#include <vector>\n\n#include \"ControlSystem/Actions/InitializeMeasurements.hpp\"\n#include \"ControlSystem/Actions/LimitTimeStep.hpp\"\n#include \"ControlSystem/CleanFunctionsOfTime.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Factory.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Systems/Expansion.hpp\"\n#include \"ControlSystem/Systems/Rotation.hpp\"\n#include \"ControlSystem/Systems/Shape.hpp\"\n#include \"ControlSystem/Systems/Size.hpp\"\n#include \"ControlSystem/Systems/Skew.hpp\"\n#include \"ControlSystem/Systems/Translation.hpp\"\n#include \"ControlSystem/Trigger.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/CylindricalBinaryCompactObject.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsCharacteristicSpeeds.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/Executables/GeneralizedHarmonic/Deadlock.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Evolution/Systems/Cce/Callbacks/DumpBondiSachsOnWorldtube.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/Callbacks/UpdateCompletionCriteria.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionCriteria.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionSingleton.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/Events/CheckConstraintThresholds.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/PhaseControl/CheckpointAndExitIfComplete.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletMinkowski.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Equations.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Initialize.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"Evolution/Triggers/SeparationLessThan.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"IO/Importers/Actions/RegisterWithElementDataReader.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/ArrayCollection/DgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/SimpleActionOnElement.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ContributeMemoryData.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Constraints.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrCriteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrStats.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/RefineMesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Tensors.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Variables.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveCenters.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveFieldsOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveTimeSeriesOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/SendDependencyToObserverWriter.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindCommonHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/MonitorMemory.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Actions/RunEventsOnFailure.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveSurfaceData.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.tpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4Real.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylTypeD1.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// Check if SpEC is linked and therefore we can load SpEC initial data\n#ifdef HAS_SPEC_EXPORTER\n#include \"PointwiseFunctions/AnalyticData/GeneralRelativity/SpecInitialData.hpp\"\nusing SpecInitialData = gr::AnalyticData::SpecInitialData;\n#else\nusing SpecInitialData = NoSuchType;\n#endif\n\n/// \\cond\nnamespace Frame {\n\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\n// Note: this executable does not use GeneralizedHarmonicBase.hpp, because\n// using it would require a number of changes in GeneralizedHarmonicBase.hpp\n// that would apply only when evolving binary black holes. This would\n// require adding a number of compile-time switches, an outcome we would prefer\n// to avoid.\nstruct EvolutionMetavars {\n  struct BondiSachs;\n\n  static constexpr size_t volume_dim = 3;\n  static constexpr bool use_damped_harmonic_rollon = false;\n  using system = gh::System<volume_dim>;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = LtsTimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using initialize_initial_data_dependent_quantities_actions =\n      tmpl::list<gh::gauges::SetPiAndPhiFromConstraints<\n                     gh::Solutions::all_solutions<volume_dim>, volume_dim>,\n                 Parallel::Actions::TerminatePhase>;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  template <::domain::ObjectLabel Horizon, typename Frame>\n  struct Ah : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n    static constexpr size_t index = 10 + static_cast<size_t>(Horizon);\n\n    using time_tag = ah::Tags::ObservationTime<index>;\n\n    using frame = Frame;\n\n    using horizon_find_callbacks = tmpl::append<\n        tmpl::conditional_t<\n            Horizon == ::domain::ObjectLabel::C,\n            tmpl::list<ah::callbacks::SendDependencyToObserverWriter<Ah, true>,\n                       gh::bbh::callbacks::UpdateCompletionCriteria<Ah>>,\n            tmpl::list<>>,\n        tmpl::list<ah::callbacks::ObserveFieldsOnHorizon<\n                       ::ah::surface_tags_for_observing, Ah>,\n                   ah::callbacks::ObserveTimeSeriesOnHorizon<\n                       ::ah::tags_for_observing<Frame>, Ah>>>;\n    using horizon_find_failure_callbacks = tmpl::append<\n        // Only ignore errors for AhC\n        tmpl::list<ah::callbacks::FailedHorizonFind<\n            Ah, Horizon == ::domain::ObjectLabel::C>>,\n        tmpl::conditional_t<\n            Horizon == ::domain::ObjectLabel::C,\n            tmpl::list<\n                ah::callbacks::SendDependencyToObserverWriter<Ah, false>>,\n            tmpl::list<>>>;\n\n    using compute_tags_on_element =\n        tmpl::list<ah::Tags::ObservationTimeCompute<index>>;\n\n    static constexpr ah::Destination destination = ah::Destination::Observation;\n\n    static std::string name() {\n      return \"ObservationAh\" + ::domain::name(Horizon);\n    }\n  };\n\n  using AhA = Ah<::domain::ObjectLabel::A, ::Frame::Distorted>;\n  using AhB = Ah<::domain::ObjectLabel::B, ::Frame::Distorted>;\n  using AhC = Ah<::domain::ObjectLabel::C, ::Frame::Inertial>;\n\n  template <::domain::ObjectLabel Excision>\n  struct ExcisionBoundary\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using tags_to_observe =\n        tmpl::list<gr::Tags::Lapse<DataVector>,\n                   gr::Tags::Shift<DataVector, 3, Frame::Grid>>;\n    using compute_vars_to_interpolate =\n        intrp::ComputeExcisionBoundaryVolumeQuantities;\n    using vars_to_interpolate_to_target = tags_to_observe;\n    using compute_items_on_source = tmpl::list<>;\n    using compute_items_on_target = tmpl::list<>;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<ExcisionBoundary<Excision>, ::Frame::Grid>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveSurfaceData<\n            tags_to_observe, ExcisionBoundary<Excision>, ::Frame::Grid>>;\n    // run_callbacks\n    template <typename metavariables>\n    using interpolating_component = typename metavariables::gh_dg_element_array;\n    static std::string name() {\n      return \"ObservationExcisionBoundary\" + ::domain::name(Excision);\n    }\n  };\n\n  using ExcisionBoundaryA = ExcisionBoundary<::domain::ObjectLabel::A>;\n  using ExcisionBoundaryB = ExcisionBoundary<::domain::ObjectLabel::B>;\n  using both_horizons = control_system::measurements::BothHorizons;\n  using control_systems =\n      tmpl::list<control_system::Systems::Rotation<3, both_horizons>,\n                 control_system::Systems::Expansion<2, both_horizons>,\n                 control_system::Systems::Translation<2, both_horizons, 2>,\n                 control_system::Systems::Skew<2, both_horizons>,\n                 control_system::Systems::Shape<::domain::ObjectLabel::A, 2,\n                                                both_horizons>,\n                 control_system::Systems::Shape<::domain::ObjectLabel::B, 2,\n                                                both_horizons>,\n                 control_system::Systems::Size<::domain::ObjectLabel::A, 2>,\n                 control_system::Systems::Size<::domain::ObjectLabel::B, 2>>;\n\n  static constexpr bool use_control_systems =\n      tmpl::size<control_systems>::value > 0;\n\n  using source_vars_no_deriv =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, volume_dim>,\n                 gh::Tags::Pi<DataVector, volume_dim>,\n                 gh::Tags::Phi<DataVector, volume_dim>>;\n\n  struct BondiSachs : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    static std::string name() { return \"BondiSachsInterpolation\"; }\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target = source_vars_no_deriv;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<BondiSachs, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::DumpBondiSachsOnWorldtube<BondiSachs>>;\n    using compute_items_on_target = tmpl::list<>;\n    template <typename metavariables>\n    using interpolating_component = typename metavariables::gh_dg_element_array;\n  };\n\n  using interpolation_target_tags = tmpl::push_back<\n      control_system::metafunctions::interpolation_target_tags<control_systems>,\n      BondiSachs, ExcisionBoundaryA, ExcisionBoundaryB>;\n\n  using observe_fields = tmpl::append<\n      tmpl::list<\n          gr::Tags::SpacetimeMetric<DataVector, volume_dim>,\n          gh::Tags::Pi<DataVector, volume_dim>,\n          gh::Tags::Phi<DataVector, volume_dim>,\n          gh::Tags::GaugeH<DataVector, volume_dim>,\n          gh::Tags::SpacetimeDerivGaugeH<DataVector, volume_dim>,\n          gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, volume_dim>,\n          gr::Tags::SpatialMetric<DataVector, volume_dim>,\n          gr::Tags::DetSpatialMetric<DataVector>,\n          gr::Tags::InverseSpatialMetric<DataVector, volume_dim>,\n          gr::Tags::SqrtDetSpatialMetricCompute<DataVector, volume_dim,\n                                                ::Frame::Inertial>,\n          gr::Tags::SpacetimeNormalOneFormCompute<DataVector, volume_dim,\n                                                  ::Frame::Inertial>,\n          gr::Tags::SpacetimeNormalVectorCompute<DataVector, volume_dim,\n                                                 ::Frame::Inertial>,\n          gr::Tags::InverseSpacetimeMetricCompute<DataVector, volume_dim,\n                                                  ::Frame::Inertial>,\n          gh::Tags::DerivLapseCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::DerivShiftCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::DerivSpatialMetricCompute<volume_dim, Frame::Inertial>,\n          gr::Tags::DerivInverseSpatialMetricCompute<volume_dim,\n                                                     Frame::Inertial>,\n          gh::Tags::GaugeConstraintCompute<volume_dim, ::Frame::Inertial>,\n          gh::Tags::TwoIndexConstraintCompute<volume_dim, ::Frame::Inertial>,\n          gh::Tags::ThreeIndexConstraintCompute<volume_dim, ::Frame::Inertial>,\n          gh::Tags::DerivSpatialMetricCompute<volume_dim, ::Frame::Inertial>,\n          gr::Tags::SpatialChristoffelFirstKindCompute<DataVector, volume_dim,\n                                                       ::Frame::Inertial>,\n          gr::Tags::SpatialChristoffelSecondKindCompute<DataVector, volume_dim,\n                                                        ::Frame::Inertial>,\n          ::Tags::DerivTensorCompute<\n              gr::Tags::SpatialChristoffelSecondKind<DataVector, volume_dim>,\n              ::domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                              Frame::Inertial>,\n              ::domain::Tags::Mesh<volume_dim>>,\n          gr::Tags::SpatialRicciCompute<DataVector, volume_dim,\n                                        ::Frame::Inertial>,\n          gr::Tags::SpatialRicciScalarCompute<DataVector, volume_dim,\n                                              ::Frame::Inertial>,\n          // observe norms of tensors\n          ::Tags::PointwiseL2NormCompute<\n              gr::Tags::Shift<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gr::Tags::SpatialMetric<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gr::Tags::SpacetimeMetric<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<gh::Tags::Pi<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<gh::Tags::Phi<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::GaugeH<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::SpacetimeDerivGaugeH<DataVector, volume_dim>>,\n          // following tags added to observe constraints\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::GaugeConstraint<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::TwoIndexConstraint<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::ThreeIndexConstraint<DataVector, volume_dim>>,\n          ::domain::Tags::Coordinates<volume_dim, Frame::Grid>,\n          ::domain::Tags::Coordinates<volume_dim, Frame::Inertial>>,\n      // The 4-index constraint is only implemented in 3d\n      tmpl::conditional_t<\n          volume_dim == 3,\n          tmpl::list<\n              gh::Tags::FourIndexConstraintCompute<3, ::Frame::Inertial>,\n              gh::Tags::FConstraintCompute<3, ::Frame::Inertial>,\n              ::Tags::PointwiseL2NormCompute<\n                  gh::Tags::FConstraint<DataVector, 3>>,\n              ::Tags::PointwiseL2NormCompute<\n                  gh::Tags::FourIndexConstraint<DataVector, 3>>,\n              gh::Tags::ConstraintEnergyCompute<3, ::Frame::Inertial>,\n              gh::Tags::ExtrinsicCurvatureCompute<3, ::Frame::Inertial>,\n              ::Tags::DerivTensorCompute<\n                  gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                  ::domain::Tags::InverseJacobian<\n                      volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                  ::domain::Tags::Mesh<volume_dim>>,\n              gr::Tags::WeylElectricCompute<DataVector, 3, Frame::Inertial>,\n              gr::Tags::WeylElectricScalarCompute<DataVector, 3,\n                                                  Frame::Inertial>,\n              gr::Tags::WeylTypeD1Compute<DataVector, 3, Frame::Inertial>,\n              gr::Tags::WeylTypeD1ScalarCompute<DataVector, 3, Frame::Inertial>,\n              gr::Tags::Psi4RealCompute<Frame::Inertial>>,\n          tmpl::list<>>>;\n  using non_tensor_compute_tags = tmpl::list<\n      ::Events::Tags::ObserverMeshCompute<volume_dim>,\n      ::Events::Tags::ObserverCoordinatesCompute<volume_dim, Frame::Inertial>,\n      ::Events::Tags::ObserverInverseJacobianCompute<\n          volume_dim, Frame::ElementLogical, Frame::Inertial>,\n      ::Events::Tags::ObserverJacobianCompute<volume_dim, Frame::ElementLogical,\n                                              Frame::Inertial>,\n      ::Events::Tags::ObserverDetInvJacobianCompute<Frame::ElementLogical,\n                                                    Frame::Inertial>,\n      ::Events::Tags::ObserverMeshVelocityCompute<volume_dim, Frame::Inertial>,\n      gh::gauges::Tags::GaugeAndDerivativeCompute<\n          volume_dim, gh::Solutions::all_solutions<volume_dim>>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<ah::Criterion, ah::Criteria::standard_criteria>,\n        tmpl::pair<\n            amr::Criterion,\n            tmpl::push_back<\n                amr::Criteria::standard_criteria<\n                    volume_dim, typename system::variables_tag::tags_list>,\n                amr::Criteria::Constraints<\n                    volume_dim,\n                    tmpl::list<gh::Tags::ThreeIndexConstraintCompute<\n                        volume_dim, Frame::Inertial>>>>>,\n        tmpl::pair<\n            evolution::initial_data::InitialData,\n            tmpl::flatten<tmpl::list<\n                gh::NumericInitialData,\n                tmpl::conditional_t<std::is_same_v<SpecInitialData, NoSuchType>,\n                                    tmpl::list<>, SpecInitialData>>>>,\n        tmpl::pair<DenseTrigger,\n                   tmpl::flatten<tmpl::list<\n                       control_system::control_system_triggers<control_systems>,\n                       DenseTriggers::standard_dense_triggers>>>,\n        tmpl::pair<\n            DomainCreator<volume_dim>,\n            tmpl::list<::domain::creators::BinaryCompactObject<false>,\n                       ::domain::creators::CylindricalBinaryCompactObject>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       ah::Events::FindApparentHorizon<AhA>,\n                       ah::Events::FindApparentHorizon<AhB>,\n                       ah::Events::FindCommonHorizon<AhC, observe_fields,\n                                                     non_tensor_compute_tags>,\n                       gh::bbh::Events::CheckConstraintThresholds,\n                       intrp::Events::InterpolateWithoutInterpComponent<\n                           3, BondiSachs, source_vars_no_deriv>,\n                       intrp::Events::InterpolateWithoutInterpComponent<\n                           3, ExcisionBoundaryA, ah::source_vars<3>>,\n                       intrp::Events::InterpolateWithoutInterpComponent<\n                           3, ExcisionBoundaryB, ah::source_vars<3>>,\n                       Events::MonitorMemory<3>, Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, non_tensor_compute_tags>,\n                       control_system::metafunctions::control_system_events<\n                           control_systems>,\n                       control_system::CleanFunctionsOfTime,\n                       Events::time_events<system>,\n                       dg::Events::ObserveTimeStepVolume<system>,\n                       amr::Events::RefineMesh,\n                       amr::Events::ObserveAmrStats<volume_dim>,\n                       amr::Events::ObserveAmrCriteria<EvolutionMetavars>>>>,\n        tmpl::pair<\n            evolution::BoundaryCorrection,\n            gh::BoundaryCorrections::standard_boundary_corrections<volume_dim>>,\n        tmpl::pair<control_system::size::State,\n                   control_system::size::States::factory_creatable_states>,\n        tmpl::pair<\n            gh::BoundaryConditions::BoundaryCondition<volume_dim>,\n            tmpl::list<\n                gh::BoundaryConditions::ConstraintPreservingBjorhus<volume_dim>,\n                gh::BoundaryConditions::DirichletMinkowski<volume_dim>,\n                gh::BoundaryConditions::DemandOutgoingCharSpeeds<volume_dim>>>,\n        tmpl::pair<\n            gh::gauges::GaugeCondition,\n            tmpl::list<gh::gauges::DampedHarmonic, gh::gauges::Harmonic>>,\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   MathFunctions::all_math_functions<1, Frame::Inertial>>,\n        // Restrict to monotonic time steppers in LTS to avoid control\n        // systems deadlocking.\n        tmpl::pair<LtsTimeStepper, TimeSteppers::monotonic_lts_time_steppers>,\n        tmpl::pair<PhaseChange,\n                   tmpl::push_back<\n                       PhaseControl::factory_creatable_classes,\n                       gh::bbh::phase_control::CheckpointAndExitIfComplete>>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   StepChoosers::standard_step_choosers<system>>,\n        tmpl::pair<\n            StepChooser<StepChooserUse::Slab>,\n            StepChoosers::standard_slab_choosers<system, local_time_stepping>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<\n            Trigger,\n            tmpl::append<Triggers::logical_triggers, Triggers::time_triggers,\n                         tmpl::list<Triggers::SeparationLessThan<false>>>>>;\n  };\n\n  // A tmpl::list of tags to be added to the GlobalCache by the\n  // metavariables\n  using const_global_cache_tags =\n      tmpl::list<gh::gauges::Tags::GaugeCondition,\n                 gh::Tags::DampingFunctionGamma0<volume_dim, Frame::Grid>,\n                 gh::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n                 gh::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>>;\n\n  using mutable_global_cache_tags = tmpl::list<>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  static constexpr auto default_phase_order =\n      std::array{Parallel::Phase::Initialization,\n                 Parallel::Phase::RegisterWithElementDataReader,\n                 Parallel::Phase::ImportInitialData,\n                 Parallel::Phase::InitializeInitialDataDependentQuantities,\n                 Parallel::Phase::Register,\n                 Parallel::Phase::InitializeTimeStepperHistory,\n                 Parallel::Phase::CheckDomain,\n                 Parallel::Phase::Evolve,\n                 Parallel::Phase::Exit};\n\n  using step_actions = tmpl::list<\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         ::domain::CheckFunctionsOfTimeAreReadyPostprocessor<\n                             volume_dim>,\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             EvolutionMetavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                  ::domain::CheckFunctionsOfTimeAreReadyPostprocessor<\n                      volume_dim>>>,\n              control_system::Actions::LimitTimeStep<control_systems>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      dg::Actions::Filter<\n          Filters::Exponential<0>,\n          tmpl::list<gr::Tags::SpacetimeMetric<DataVector, volume_dim>,\n                     gh::Tags::Pi<DataVector, volume_dim>,\n                     gh::Tags::Phi<DataVector, volume_dim>>>>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars,\n                                                use_control_systems>,\n          ::amr::Initialization::Initialize<volume_dim, EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::NonconservativeSystem<system>,\n      Initialization::Actions::AddComputeTags<tmpl::list<::Tags::DerivCompute<\n          typename system::variables_tag, ::domain::Tags::Mesh<volume_dim>,\n          ::domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                          Frame::Inertial>,\n          typename system::gradient_variables>>>,\n      gh::Actions::InitializeGhAnd3Plus1Variables<volume_dim>,\n      Initialization::Actions::AddComputeTags<\n          tmpl::push_back<StepChoosers::step_chooser_compute_tags<\n              EvolutionMetavars, local_time_stepping>>>,\n      Initialization::Actions::AddSimpleTags<\n          gh::bbh::Actions::InitializeElementCompletionRequested>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      intrp::Actions::ElementInitInterpPoints<volume_dim,\n                                              interpolation_target_tags>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      control_system::Actions::InitializeMeasurements<control_systems>,\n      Parallel::Actions::TerminatePhase>;\n\n  using gh_dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::flatten<tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::RegisterWithElementDataReader,\n              tmpl::list<importers::Actions::RegisterWithElementDataReader,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::ImportInitialData,\n              tmpl::list<gh::Actions::SetInitialData,\n                         gh::Actions::ReceiveNumericInitialData,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeInitialDataDependentQuantities,\n              initialize_initial_data_dependent_quantities_actions>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  ::domain::Actions::CheckFunctionsOfTimeAreReady<volume_dim>,\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::PostFailureCleanup,\n              tmpl::list<Actions::RunEventsOnFailure<::Tags::Time>,\n                         Parallel::Actions::TerminatePhase>>>>>;\n\n  using observed_reduction_data_tags = observers::collect_reduction_data_tags<\n      tmpl::at<typename factory_creation::factory_classes, Event>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<gh_dg_element_array, dg_registration_list>>;\n  };\n\n  using control_system_horizon_metavars =\n      control_system::metafunctions::horizon_metavars<control_systems>;\n  using control_components =\n      control_system::control_components<EvolutionMetavars, control_systems>;\n\n  static void run_deadlock_analysis_simple_actions(\n      Parallel::GlobalCache<EvolutionMetavars>& cache,\n      const std::vector<std::string>& deadlocked_components) {\n    gh::deadlock::run_deadlock_analysis_simple_actions<\n        gh_dg_element_array, control_components, interpolation_target_tags,\n        control_system_horizon_metavars>(cache, deadlocked_components);\n  }\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = gh_dg_element_array;\n    using projectors = tmpl::list<\n        Initialization::ProjectTimeStepping<volume_dim>,\n        evolution::dg::Initialization::ProjectDomain<volume_dim>,\n        ::amr::projectors::ProjectVariables<volume_dim,\n                                            typename system::variables_tag>,\n        evolution::dg::Initialization::ProjectMortars<volume_dim,\n                                                      local_time_stepping>,\n        Initialization::ProjectTimeStepperHistory<EvolutionMetavars>,\n        evolution::Actions::ProjectRunEventsAndDenseTriggers,\n        ::amr::projectors::DefaultInitialize<\n            Initialization::Tags::InitialTimeDelta,\n            Initialization::Tags::InitialSlabSize<local_time_stepping>,\n            ::domain::Tags::InitialExtents<volume_dim>,\n            ::domain::Tags::InitialRefinementLevels<volume_dim>,\n            evolution::dg::Tags::Quadrature,\n            Tags::StepperErrors<typename system::variables_tag>,\n            SelfStart::Tags::InitialValue<typename system::variables_tag>,\n            SelfStart::Tags::InitialValue<Tags::TimeStep>>,\n        ::amr::projectors::CopyFromCreatorOrLeaveAsIs<tmpl::push_back<\n            tmpl::append<\n                typename control_system::Actions::InitializeMeasurements<\n                    control_systems>::simple_tags,\n                tmpl::transform<\n                    intrp::InterpolationTarget_detail::\n                        get_non_sequential_target_tags<\n                            interpolation_target_tags>,\n                    tmpl::bind<intrp::Tags::PointInfo, tmpl::_1,\n                               tmpl::pin<tmpl::size_t<volume_dim>>>>>,\n            gh::bbh::Tags::ElementCompletionRequested,\n            Tags::ChangeSlabSize::NumberOfExpectedMessages,\n            Tags::ChangeSlabSize::NewSlabSize>>>;\n    static constexpr bool keep_coarse_grids = false;\n    static constexpr bool p_refine_only_in_event = true;\n  };\n\n  using component_list = tmpl::flatten<tmpl::list<\n      ::amr::Component<EvolutionMetavars>,\n      observers::Observer<EvolutionMetavars>,\n      observers::ObserverWriter<EvolutionMetavars>,\n      importers::ElementDataReader<EvolutionMetavars>,\n      mem_monitor::MemoryMonitor<EvolutionMetavars>,\n      gh::bbh::CompletionSingleton<EvolutionMetavars>,\n      ah::Component<EvolutionMetavars, AhA>,\n      ah::Component<EvolutionMetavars, AhB>,\n      ah::Component<EvolutionMetavars, AhC>,\n      tmpl::transform<\n          control_system_horizon_metavars,\n          tmpl::bind<ah::Component, tmpl::pin<EvolutionMetavars>, tmpl::_1>>,\n      tmpl::transform<interpolation_target_tags,\n                      tmpl::bind<intrp::InterpolationTarget,\n                                 tmpl::pin<EvolutionMetavars>, tmpl::_1>>,\n      control_system::control_components<EvolutionMetavars, control_systems>,\n      gh_dg_element_array>>;\n\n  static constexpr Options::String help{\n      \"Evolve a binary black hole using the Generalized Harmonic \"\n      \"formulation\\n\"};\n};\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/EvolveGhNoBlackHole.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/GeneralizedHarmonic/EvolveGhNoBlackHole.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<DIM, USE_LTS>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &ConstraintDamping::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>,\n       &amr::register_callbacks<metavariables,\n                                metavariables::gh_dg_element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/EvolveGhNoBlackHole.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <vector>\n\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/Executables/GeneralizedHarmonic/GeneralizedHarmonicBase.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayCollection/DgElementCollection.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\ntemplate <size_t VolumeDim, bool UseLts>\nstruct EvolutionMetavars\n    : public GeneralizedHarmonicTemplateBase<VolumeDim, UseLts> {\n  static constexpr size_t volume_dim = VolumeDim;\n  using gh_base = GeneralizedHarmonicTemplateBase<volume_dim, UseLts>;\n  using typename gh_base::const_global_cache_tags;\n  using typename gh_base::dg_registration_list;\n  using initialization_actions =\n      typename gh_base::template initialization_actions<EvolutionMetavars,\n                                                        false>;\n  using typename gh_base::initialize_initial_data_dependent_quantities_actions;\n  using typename gh_base::observed_reduction_data_tags;\n  using typename gh_base::system;\n  static constexpr bool local_time_stepping = gh_base::local_time_stepping;\n  static constexpr bool use_dg_element_collection =\n      gh_base::use_dg_element_collection;\n\n  using step_actions =\n      typename gh_base::template step_actions<EvolutionMetavars, tmpl::list<>>;\n\n  using gh_dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::flatten<tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::RegisterWithElementDataReader,\n              tmpl::list<importers::Actions::RegisterWithElementDataReader,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::ImportInitialData,\n              tmpl::list<\n                  gh::Actions::SetInitialData,\n                  tmpl::conditional_t<VolumeDim == 3,\n                                      gh::Actions::ReceiveNumericInitialData,\n                                      tmpl::list<>>,\n                  Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeInitialDataDependentQuantities,\n              initialize_initial_data_dependent_quantities_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  ::evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = gh_dg_element_array;\n    using projectors = tmpl::list<\n        Initialization::ProjectTimeStepping<volume_dim>,\n        evolution::dg::Initialization::ProjectDomain<volume_dim>,\n        ::amr::projectors::ProjectVariables<volume_dim,\n                                            typename system::variables_tag>,\n        evolution::dg::Initialization::ProjectMortars<volume_dim,\n                                                      local_time_stepping>,\n        Initialization::ProjectTimeStepperHistory<EvolutionMetavars>,\n        evolution::Actions::ProjectRunEventsAndDenseTriggers,\n        ::amr::projectors::DefaultInitialize<\n            Initialization::Tags::InitialTimeDelta,\n            Initialization::Tags::InitialSlabSize<local_time_stepping>,\n            ::domain::Tags::InitialExtents<volume_dim>,\n            ::domain::Tags::InitialRefinementLevels<volume_dim>,\n            evolution::dg::Tags::Quadrature,\n            Tags::StepperErrors<typename system::variables_tag>,\n            SelfStart::Tags::InitialValue<typename system::variables_tag>,\n            SelfStart::Tags::InitialValue<Tags::TimeStep>>,\n        ::amr::projectors::CopyFromCreatorOrLeaveAsIs<\n            Tags::ChangeSlabSize::NumberOfExpectedMessages,\n            Tags::ChangeSlabSize::NewSlabSize>>;\n    static constexpr bool keep_coarse_grids = false;\n    static constexpr bool p_refine_only_in_event = true;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<gh_dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::flatten<tmpl::list<::amr::Component<EvolutionMetavars>,\n                               observers::Observer<EvolutionMetavars>,\n                               observers::ObserverWriter<EvolutionMetavars>,\n                               mem_monitor::MemoryMonitor<EvolutionMetavars>,\n                               importers::ElementDataReader<EvolutionMetavars>,\n                               gh_dg_element_array>>;\n\n  static constexpr Options::String help{\n      \"Evolve the Einstein field equations using the Generalized Harmonic \"\n      \"formulation\\n\"};\n};\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/EvolveGhSingleBlackHole.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/GeneralizedHarmonic/EvolveGhSingleBlackHole.hpp\"\n\n#include <vector>\n\n#include \"ControlSystem/ControlErrors/Size/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<USE_LTS>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &ConstraintDamping::register_derived_with_charm,\n       &control_system::size::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>,\n       &amr::register_callbacks<metavariables,\n                                metavariables::gh_dg_element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/EvolveGhSingleBlackHole.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n#include <vector>\n\n#include \"ControlSystem/Actions/InitializeMeasurements.hpp\"\n#include \"ControlSystem/CleanFunctionsOfTime.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Factory.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"ControlSystem/Measurements/SingleHorizon.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Systems/Shape.hpp\"\n#include \"ControlSystem/Systems/Size.hpp\"\n#include \"ControlSystem/Systems/Translation.hpp\"\n#include \"ControlSystem/Trigger.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/Executables/GeneralizedHarmonic/Deadlock.hpp\"\n#include \"Evolution/Executables/GeneralizedHarmonic/GeneralizedHarmonicBase.hpp\"\n#include \"Evolution/Systems/Cce/Callbacks/DumpBondiSachsOnWorldtube.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Options/FactoryHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayCollection/DgElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrCriteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrStats.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/RefineMesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveFieldsOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveTimeSeriesOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Actions/RunEventsOnFailure.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveSurfaceData.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.tpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\ntemplate <bool UseLts>\nstruct EvolutionMetavars : public GeneralizedHarmonicTemplateBase<3, UseLts> {\n  static constexpr bool local_time_stepping = UseLts;\n  static constexpr size_t volume_dim = 3;\n  using gh_base = GeneralizedHarmonicTemplateBase<volume_dim, UseLts>;\n  using typename gh_base::initialize_initial_data_dependent_quantities_actions;\n  using typename gh_base::system;\n\n  static constexpr Options::String help{\n      \"Evolve the Einstein field equations using the Generalized Harmonic \"\n      \"formulation,\\n\"\n      \"on a domain with a single horizon and corresponding excised region\"};\n\n  struct ApparentHorizon : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n    using time_tag = ah::Tags::ObservationTime<0>;\n\n    using frame = ::Frame::Inertial;\n\n    using horizon_find_callbacks = tmpl::list<\n        ah::callbacks::ObserveTimeSeriesOnHorizon<\n            ::ah::tags_for_observing<Frame::Inertial>, ApparentHorizon>,\n        ah::callbacks::ObserveFieldsOnHorizon<::ah::surface_tags_for_observing,\n                                              ApparentHorizon>>;\n    using horizon_find_failure_callbacks =\n        tmpl::list<ah::callbacks::FailedHorizonFind<ApparentHorizon, false>>;\n\n    using compute_tags_on_element =\n        tmpl::list<ah::Tags::ObservationTimeCompute<0>>;\n\n    static constexpr ah::Destination destination = ah::Destination::Observation;\n\n    static std::string name() { return \"ApparentHorizon\"; }\n  };\n\n  struct ExcisionBoundary\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using tags_to_observe =\n        tmpl::list<gr::Tags::Lapse<DataVector>,\n                   gr::Tags::Shift<DataVector, 3, Frame::Grid>>;\n    using compute_vars_to_interpolate =\n        intrp::ComputeExcisionBoundaryVolumeQuantities;\n    using vars_to_interpolate_to_target = tags_to_observe;\n    using compute_items_on_source = tmpl::list<>;\n    using compute_items_on_target = tmpl::list<>;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<ExcisionBoundary, ::Frame::Grid>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveSurfaceData<\n            tags_to_observe, ExcisionBoundary, ::Frame::Grid>>;\n    // run_callbacks\n    template <typename metavariables>\n    using interpolating_component = typename metavariables::gh_dg_element_array;\n  };\n\n  using control_systems =\n      tmpl::list<control_system::Systems::Shape<\n                     ::domain::ObjectLabel::None, 2,\n                     control_system::measurements::SingleHorizon<\n                         ::domain::ObjectLabel::None>>,\n                 control_system::Systems::Translation<\n                     2,\n                     control_system::measurements::SingleHorizon<\n                         ::domain::ObjectLabel::None>,\n                     1>,\n                 control_system::Systems::Size<::domain::ObjectLabel::None, 2>>;\n\n  static constexpr bool use_control_systems =\n      tmpl::size<control_systems>::value > 0;\n\n  struct BondiSachs;\n\n  using interpolation_target_tags = tmpl::push_back<\n      control_system::metafunctions::interpolation_target_tags<control_systems>,\n      ExcisionBoundary, BondiSachs>;\n  using source_vars_no_deriv =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, volume_dim>,\n                 gh::Tags::Pi<DataVector, volume_dim>,\n                 gh::Tags::Phi<DataVector, volume_dim>>;\n\n  struct BondiSachs : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    static std::string name() { return \"BondiSachsInterpolation\"; }\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target = source_vars_no_deriv;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<BondiSachs, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::DumpBondiSachsOnWorldtube<BondiSachs>>;\n    using compute_items_on_target = tmpl::list<>;\n    template <typename Metavariables>\n    using interpolating_component = typename Metavariables::gh_dg_element_array;\n  };\n\n  // The interpolator_source_vars need to be the same in both the Interpolate\n  // event and the InterpolateWithoutInterpComponent event.  The Interpolate\n  // event interpolates to the horizon, and the\n  // InterpolateWithoutInterpComponent event interpolates to the excision\n  // boundary. Every Target gets the same interpolator_source_vars, so they need\n  // to be made the same. Otherwise a static assert is triggered.\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = Options::add_factory_classes<\n        // Restrict to monotonic time steppers in LTS to avoid control\n        // systems deadlocking.\n        tmpl::insert<\n            tmpl::erase<typename gh_base::factory_creation::factory_classes,\n                        LtsTimeStepper>,\n            tmpl::pair<LtsTimeStepper,\n                       TimeSteppers::monotonic_lts_time_steppers>>,\n        tmpl::pair<ah::Criterion, ah::Criteria::standard_criteria>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       ah::Events::FindApparentHorizon<ApparentHorizon>,\n                       control_system::metafunctions::control_system_events<\n                           control_systems>,\n                       control_system::CleanFunctionsOfTime,\n                       intrp::Events::InterpolateWithoutInterpComponent<\n                           3, BondiSachs, source_vars_no_deriv>,\n                       intrp::Events::InterpolateWithoutInterpComponent<\n                           3, ExcisionBoundary, ::ah::source_vars<volume_dim>>,\n                       amr::Events::RefineMesh,\n                       amr::Events::ObserveAmrStats<volume_dim>>>>,\n        tmpl::pair<DenseTrigger,\n                   control_system::control_system_triggers<control_systems>>,\n        tmpl::pair<control_system::size::State,\n                   control_system::size::States::factory_creatable_states>>;\n  };\n\n  using typename gh_base::const_global_cache_tags;\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::push_back<\n          tmpl::at<typename factory_creation::factory_classes, Event>,\n          typename ExcisionBoundary::post_interpolation_callbacks>>;\n\n  using dg_registration_list = typename gh_base::dg_registration_list;\n\n  using step_actions =\n      typename gh_base::template step_actions<EvolutionMetavars,\n                                              control_systems>;\n\n  using initialization_actions = tmpl::push_back<\n      tmpl::pop_back<typename gh_base::template initialization_actions<\n          EvolutionMetavars, use_control_systems>>,\n      control_system::Actions::InitializeMeasurements<control_systems>,\n      intrp::Actions::ElementInitInterpPoints<volume_dim,\n                                              interpolation_target_tags>,\n      tmpl::back<typename gh_base::template initialization_actions<\n          EvolutionMetavars, use_control_systems>>>;\n\n  using gh_dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::flatten<tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::RegisterWithElementDataReader,\n              tmpl::list<importers::Actions::RegisterWithElementDataReader,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::ImportInitialData,\n              tmpl::list<gh::Actions::SetInitialData,\n                         gh::Actions::ReceiveNumericInitialData,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeInitialDataDependentQuantities,\n              initialize_initial_data_dependent_quantities_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  ::domain::Actions::CheckFunctionsOfTimeAreReady<volume_dim>,\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::PostFailureCleanup,\n              tmpl::list<Actions::RunEventsOnFailure<::Tags::Time>,\n                         Parallel::Actions::TerminatePhase>>>>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = gh_dg_element_array;\n    using projectors = tmpl::list<\n        Initialization::ProjectTimeStepping<volume_dim>,\n        evolution::dg::Initialization::ProjectDomain<volume_dim>,\n        ::amr::projectors::ProjectVariables<volume_dim,\n                                            typename system::variables_tag>,\n        evolution::dg::Initialization::ProjectMortars<volume_dim,\n                                                      local_time_stepping>,\n        Initialization::ProjectTimeStepperHistory<EvolutionMetavars>,\n        evolution::Actions::ProjectRunEventsAndDenseTriggers,\n        ::amr::projectors::DefaultInitialize<\n            Initialization::Tags::InitialTimeDelta,\n            Initialization::Tags::InitialSlabSize<gh_base::local_time_stepping>,\n            ::domain::Tags::InitialExtents<volume_dim>,\n            ::domain::Tags::InitialRefinementLevels<volume_dim>,\n            evolution::dg::Tags::Quadrature,\n            Tags::StepperErrors<typename system::variables_tag>,\n            SelfStart::Tags::InitialValue<typename system::variables_tag>,\n            SelfStart::Tags::InitialValue<Tags::TimeStep>>,\n        ::amr::projectors::CopyFromCreatorOrLeaveAsIs<tmpl::push_back<\n            tmpl::append<\n                typename control_system::Actions::InitializeMeasurements<\n                    control_systems>::simple_tags,\n                tmpl::transform<\n                    intrp::InterpolationTarget_detail::\n                        get_non_sequential_target_tags<\n                            interpolation_target_tags>,\n                    tmpl::bind<intrp::Tags::PointInfo, tmpl::_1,\n                               tmpl::pin<tmpl::size_t<volume_dim>>>>>,\n            Tags::ChangeSlabSize::NumberOfExpectedMessages,\n            Tags::ChangeSlabSize::NewSlabSize>>>;\n    static constexpr bool keep_coarse_grids = false;\n    static constexpr bool p_refine_only_in_event = true;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<gh_dg_element_array, dg_registration_list>>;\n  };\n\n  using control_system_horizon_metavars =\n      control_system::metafunctions::horizon_metavars<control_systems>;\n  using control_components =\n      control_system::control_components<EvolutionMetavars, control_systems>;\n\n  static void run_deadlock_analysis_simple_actions(\n      Parallel::GlobalCache<EvolutionMetavars>& cache,\n      const std::vector<std::string>& deadlocked_components) {\n    gh::deadlock::run_deadlock_analysis_simple_actions<\n        gh_dg_element_array, control_components, interpolation_target_tags,\n        tmpl::list<ApparentHorizon>>(cache, deadlocked_components);\n  }\n\n  using component_list = tmpl::flatten<tmpl::list<\n      ::amr::Component<EvolutionMetavars>,\n      observers::Observer<EvolutionMetavars>,\n      observers::ObserverWriter<EvolutionMetavars>,\n      mem_monitor::MemoryMonitor<EvolutionMetavars>,\n      importers::ElementDataReader<EvolutionMetavars>, gh_dg_element_array,\n      ah::Component<EvolutionMetavars, ApparentHorizon>, control_components,\n      tmpl::transform<\n          control_system_horizon_metavars,\n          tmpl::bind<ah::Component, tmpl::pin<EvolutionMetavars>, tmpl::_1>>,\n      tmpl::transform<interpolation_target_tags,\n                      tmpl::bind<intrp::InterpolationTarget,\n                                 tmpl::pin<EvolutionMetavars>, tmpl::_1>>>>;\n};\n"
  },
  {
    "path": "src/Evolution/Executables/GeneralizedHarmonic/GeneralizedHarmonicBase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n#include \"ControlSystem/Actions/LimitTimeStep.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsCharacteristicSpeeds.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Equations.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Initialize.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"IO/Importers/Actions/RegisterWithElementDataReader.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ContributeMemoryData.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Constraints.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Tensors.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Variables.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/MonitorMemory.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4Real.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylTypeD1.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\n\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace detail {\n\ntemplate <size_t volume_dim>\nstruct ObserverTags {\n  using system = gh::System<volume_dim>;\n\n  using variables_tag = typename system::variables_tag;\n  using analytic_solution_fields = typename variables_tag::tags_list;\n\n  using initial_data_list = gh::Solutions::all_solutions<volume_dim>;\n\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_solution_fields, false, initial_data_list>;\n  using deriv_compute = ::Tags::DerivCompute<\n      variables_tag, domain::Tags::Mesh<volume_dim>,\n      domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      typename system::gradient_variables>;\n  using error_compute = Tags::ErrorsCompute<analytic_solution_fields>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_solution_fields>;\n\n  using observe_fields = tmpl::append<\n      tmpl::push_back<\n          analytic_solution_fields, gh::Tags::GaugeH<DataVector, volume_dim>,\n          gh::Tags::SpacetimeDerivGaugeH<DataVector, volume_dim>,\n          gr::Tags::SpatialMetric<DataVector, volume_dim>,\n          gr::Tags::DetSpatialMetric<DataVector>,\n          gr::Tags::InverseSpatialMetric<DataVector, volume_dim>,\n          gr::Tags::Shift<DataVector, volume_dim>, gr::Tags::Lapse<DataVector>,\n          gr::Tags::SqrtDetSpatialMetricCompute<DataVector, volume_dim,\n                                                Frame::Inertial>,\n          gr::Tags::SpacetimeNormalOneFormCompute<DataVector, volume_dim,\n                                                  Frame::Inertial>,\n          gr::Tags::SpacetimeNormalVectorCompute<DataVector, volume_dim,\n                                                 Frame::Inertial>,\n          gr::Tags::InverseSpacetimeMetricCompute<DataVector, volume_dim,\n                                                  Frame::Inertial>,\n          gh::Tags::DerivLapseCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::DerivShiftCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::DerivSpatialMetricCompute<volume_dim, Frame::Inertial>,\n          gr::Tags::DerivInverseSpatialMetricCompute<volume_dim,\n                                                     Frame::Inertial>,\n\n          gh::Tags::GaugeConstraintCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::TwoIndexConstraintCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::ThreeIndexConstraintCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::VSpacetimeMetricSpeedCompute<volume_dim, Frame::Inertial,\n                                                 Frame::ElementLogical>,\n          gh::Tags::VZeroSpeedCompute<volume_dim, Frame::Inertial,\n                                      Frame::ElementLogical>,\n          gh::Tags::VMinusSpeedCompute<volume_dim, Frame::Inertial,\n                                       Frame::ElementLogical>,\n          gh::Tags::VPlusSpeedCompute<volume_dim, Frame::Inertial,\n                                      Frame::ElementLogical>,\n          gh::Tags::DerivSpatialMetricCompute<volume_dim, ::Frame::Inertial>,\n          gr::Tags::SpatialChristoffelFirstKindCompute<DataVector, volume_dim,\n                                                       ::Frame::Inertial>,\n          gr::Tags::SpatialChristoffelSecondKindCompute<DataVector, volume_dim,\n                                                        ::Frame::Inertial>,\n          ::Tags::DerivTensorCompute<\n              gr::Tags::SpatialChristoffelSecondKind<DataVector, volume_dim>,\n              ::domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                              Frame::Inertial>,\n              ::domain::Tags::Mesh<volume_dim>>,\n          gr::Tags::SpatialRicciCompute<DataVector, volume_dim,\n                                        ::Frame::Inertial>,\n          gr::Tags::SpatialRicciScalarCompute<DataVector, volume_dim,\n                                              ::Frame::Inertial>,\n          // following tags added to observe constraints\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::GaugeConstraint<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::TwoIndexConstraint<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::ThreeIndexConstraint<DataVector, volume_dim>>,\n          ::domain::Tags::Coordinates<volume_dim, Frame::Grid>,\n          ::domain::Tags::Coordinates<volume_dim, Frame::Inertial>>,\n      error_tags,\n      // The 4-index constraint is only implemented in 3d\n      tmpl::conditional_t<\n          volume_dim == 3,\n          tmpl::list<\n              gh::Tags::FourIndexConstraintCompute<3, Frame::Inertial>,\n              gh::Tags::FConstraintCompute<3, Frame::Inertial>,\n              ::Tags::PointwiseL2NormCompute<\n                  gh::Tags::FConstraint<DataVector, 3>>,\n              ::Tags::PointwiseL2NormCompute<\n                  gh::Tags::FourIndexConstraint<DataVector, 3>>,\n              gh::Tags::ConstraintEnergyCompute<3, Frame::Inertial>,\n              gh::Tags::ExtrinsicCurvatureCompute<3, Frame::Inertial>,\n              ::Tags::DerivTensorCompute<\n                  gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                  ::domain::Tags::InverseJacobian<\n                      volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                  ::domain::Tags::Mesh<volume_dim>>,\n              gr::Tags::WeylElectricCompute<DataVector, 3, Frame::Inertial>,\n              gr::Tags::WeylElectricScalarCompute<DataVector, 3,\n                                                  Frame::Inertial>,\n              gr::Tags::WeylTypeD1Compute<DataVector, 3, Frame::Inertial>,\n              gr::Tags::WeylTypeD1ScalarCompute<DataVector, 3, Frame::Inertial>,\n              gr::Tags::Psi4RealCompute<Frame::Inertial>>,\n          tmpl::list<>>>;\n  using non_tensor_compute_tags = tmpl::list<\n      ::Events::Tags::ObserverMeshCompute<volume_dim>,\n      ::Events::Tags::ObserverCoordinatesCompute<volume_dim, Frame::Inertial>,\n      ::Events::Tags::ObserverInverseJacobianCompute<\n          volume_dim, Frame::ElementLogical, Frame::Inertial>,\n      ::Events::Tags::ObserverJacobianCompute<volume_dim, Frame::ElementLogical,\n                                              Frame::Inertial>,\n      ::Events::Tags::ObserverDetInvJacobianCompute<Frame::ElementLogical,\n                                                    Frame::Inertial>,\n      ::Events::Tags::ObserverMeshVelocityCompute<volume_dim, Frame::Inertial>,\n      analytic_compute, error_compute,\n      gh::gauges::Tags::GaugeAndDerivativeCompute<\n          volume_dim, gh::Solutions::all_solutions<volume_dim>>>;\n\n  using field_observations =\n      dg::Events::field_observations<volume_dim, observe_fields,\n                                     non_tensor_compute_tags>;\n};\n\ntemplate <size_t volume_dim, bool LocalTimeStepping>\nstruct FactoryCreation : tt::ConformsTo<Options::protocols::FactoryCreation> {\n  using system = gh::System<volume_dim>;\n\n  using factory_classes = tmpl::map<\n      tmpl::pair<\n          amr::Criterion,\n          tmpl::push_back<\n              amr::Criteria::standard_criteria<\n                  volume_dim, typename system::variables_tag::tags_list>,\n              amr::Criteria::Constraints<\n                  volume_dim, tmpl::list<gh::Tags::ThreeIndexConstraintCompute<\n                                  volume_dim, Frame::Inertial>>>>>,\n      tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n      tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n      tmpl::pair<\n          Event,\n          tmpl::flatten<tmpl::list<\n              Events::Completion, Events::MonitorMemory<volume_dim>,\n              typename detail::ObserverTags<volume_dim>::field_observations,\n              Events::time_events<system>,\n              dg::Events::ObserveTimeStepVolume<system>>>>,\n      tmpl::pair<\n          evolution::BoundaryCorrection,\n          gh::BoundaryCorrections::standard_boundary_corrections<volume_dim>>,\n      tmpl::pair<\n          gh::BoundaryConditions::BoundaryCondition<volume_dim>,\n          gh::BoundaryConditions::standard_boundary_conditions<volume_dim>>,\n      tmpl::pair<gh::gauges::GaugeCondition, gh::gauges::all_gauges>,\n      tmpl::pair<\n          evolution::initial_data::InitialData,\n          tmpl::append<gh::Solutions::all_solutions<volume_dim>,\n                       tmpl::conditional_t<volume_dim == 3,\n                                           tmpl::list<gh::NumericInitialData>,\n                                           tmpl::list<>>>>,\n      tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n      tmpl::pair<MathFunction<1, Frame::Inertial>,\n                 MathFunctions::all_math_functions<1, Frame::Inertial>>,\n      tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n      tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                 StepChoosers::standard_step_choosers<system>>,\n      tmpl::pair<\n          StepChooser<StepChooserUse::Slab>,\n          StepChoosers::standard_slab_choosers<system, LocalTimeStepping>>,\n      tmpl::pair<TimeSequence<double>,\n                 TimeSequences::all_time_sequences<double>>,\n      tmpl::pair<TimeSequence<std::uint64_t>,\n                 TimeSequences::all_time_sequences<std::uint64_t>>,\n      tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n      tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                       Triggers::time_triggers>>>;\n};\n}  // namespace detail\n\ntemplate <size_t VolumeDim, bool LocalTimeStepping>\nstruct GeneralizedHarmonicTemplateBase {\n  static constexpr size_t volume_dim = VolumeDim;\n  using system = gh::System<volume_dim>;\n  using TimeStepperBase =\n      tmpl::conditional_t<LocalTimeStepping, LtsTimeStepper, TimeStepper>;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  using factory_creation =\n      detail::FactoryCreation<volume_dim, local_time_stepping>;\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::push_back<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>;\n\n  using initialize_initial_data_dependent_quantities_actions =\n      tmpl::list<gh::gauges::SetPiAndPhiFromConstraints<\n                     gh::Solutions::all_solutions<volume_dim>, volume_dim>,\n                 Parallel::Actions::TerminatePhase>;\n\n  // A tmpl::list of tags to be added to the GlobalCache by the\n  // metavariables\n  using const_global_cache_tags =\n      tmpl::list<gh::gauges::Tags::GaugeCondition,\n                 evolution::initial_data::Tags::InitialData,\n                 gh::Tags::DampingFunctionGamma0<volume_dim, Frame::Grid>,\n                 gh::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n                 gh::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  // Register needs to be before InitializeTimeStepperHistory so that CCE is\n  // properly registered when the self-start happens\n  static constexpr auto default_phase_order =\n      std::array{Parallel::Phase::Initialization,\n                 Parallel::Phase::RegisterWithElementDataReader,\n                 Parallel::Phase::ImportInitialData,\n                 Parallel::Phase::InitializeInitialDataDependentQuantities,\n                 Parallel::Phase::Register,\n                 Parallel::Phase::InitializeTimeStepperHistory,\n                 Parallel::Phase::CheckDomain,\n                 Parallel::Phase::Evolve,\n                 Parallel::Phase::Exit};\n\n  template <typename DerivedMetavars, typename ControlSystems>\n  using step_actions = tmpl::list<\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         ::domain::CheckFunctionsOfTimeAreReadyPostprocessor<\n                             volume_dim>,\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             DerivedMetavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n              control_system::Actions::LimitTimeStep<ControlSystems>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      dg::Actions::Filter<\n          Filters::Exponential<0>,\n          tmpl::list<gr::Tags::SpacetimeMetric<DataVector, volume_dim>,\n                     gh::Tags::Pi<DataVector, volume_dim>,\n                     gh::Tags::Phi<DataVector, volume_dim>>>>;\n\n  template <typename DerivedMetavars, bool UseControlSystems>\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<DerivedMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<DerivedMetavars,\n                                                UseControlSystems>,\n          ::amr::Initialization::Initialize<volume_dim, DerivedMetavars>,\n          Initialization::TimeStepperHistory<DerivedMetavars>>,\n      Initialization::Actions::NonconservativeSystem<system>,\n      Initialization::Actions::AddComputeTags<::Tags::DerivCompute<\n          typename system::variables_tag, domain::Tags::Mesh<volume_dim>,\n          domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                        Frame::Inertial>,\n          typename system::gradient_variables>>,\n      gh::Actions::InitializeGhAnd3Plus1Variables<volume_dim>,\n      Initialization::Actions::AddComputeTags<\n          tmpl::push_back<StepChoosers::step_chooser_compute_tags<\n              GeneralizedHarmonicTemplateBase, local_time_stepping>>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n};\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(GhValenciaDivClean)\nadd_subdirectory(ValenciaDivClean)\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/GhValenciaDivClean/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Actions\n  ApparentHorizonFinder\n  ApparentHorizonFinderCriteria\n  Charmxx::main\n  Cce\n  CoordinateMaps\n  DiscontinuousGalerkin\n  DomainCreators\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  EvolutionTriggers\n  GeneralRelativitySolutions\n  GeneralizedHarmonic\n  GhGrMhdAnalyticData\n  GhGrMhdSolutions\n  GhRelativisticEulerSolutions\n  GhValenciaDivClean\n  GrMhdAnalyticData\n  GrMhdSolutions\n  Hydro\n  Informer\n  LinearOperators\n  MathFunctions\n  MonteCarloSolutions\n  Observer\n  Options\n  Parallel\n  ParallelInterpolation\n  PhaseControl\n  RelativisticEulerSolutions\n  Serialization\n  Time\n  Utilities\n  ValenciaDivClean\n  )\n\nset(LIBS_TO_LINK_WITH_CONTROL_SYSTEM\n  ControlSystem\n  ControlSystemMeasurements\n  )\n\nfunction(add_ghmhd_executable SUFFIX USE_CONTROL_SYSTEMS USE_PARAMETRIZED_DELEPTONIZATION)\n  set(EXECUTABLE \"EvolveGhValenciaDivClean${SUFFIX}\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    EvolveGhValenciaDivClean.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    USE_CONTROL_SYSTEMS=${USE_CONTROL_SYSTEMS}\n    USE_PARAMETRIZED_DELEPTONIZATION=${USE_PARAMETRIZED_DELEPTONIZATION}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\n  if (USE_CONTROL_SYSTEMS STREQUAL \"true\")\n    target_link_libraries(\n      ${EXECUTABLE}\n      PRIVATE\n      ${LIBS_TO_LINK_WITH_CONTROL_SYSTEM}\n      )\n  endif()\nendfunction()\n\nadd_ghmhd_executable(\n  \"\"\n  \"false\"\n  \"false\"\n  )\n\nadd_ghmhd_executable(\n  \"Bns\"\n   \"true\"\n   \"false\"\n  )\nadd_ghmhd_executable(\n  \"CoreCollapseSupernova\"\n   \"false\"\n   \"true\"\n  )\n\nfunction(add_ghmhd_with_horizon_executable SUFFIX)\n  set(EXECUTABLE \"EvolveGhValenciaDivClean${SUFFIX}\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    EvolveGhValenciaDivCleanWithHorizon.cpp\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction()\n\nadd_ghmhd_with_horizon_executable(\n  \"WithHorizon\"\n  \"false\"\n  )\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/GhValenciaDivClean/EvolveGhValenciaDivClean.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/GrMhd/GhValenciaDivClean/EvolveGhValenciaDivClean.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables =\n    EvolutionMetavars<USE_CONTROL_SYSTEMS, USE_PARAMETRIZED_DELEPTONIZATION,\n                      BondiSachs>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &grmhd::GhValenciaDivClean::fd::register_derived_with_charm<\n           typename metavariables::base::system>,\n       &EquationsOfState::register_derived_with_charm,\n       &ConstraintDamping::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/GhValenciaDivClean/EvolveGhValenciaDivClean.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <vector>\n\n#include \"Evolution/DgSubcell/GetTciDecision.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp\"\n#include \"Evolution/DgSubcell/NeighborTciDecision.hpp\"\n#include \"Evolution/Executables/GrMhd/GhValenciaDivClean/GhValenciaDivCleanBase.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <bool UseControlSystems, bool UseParametrizedDeleptonization,\n          typename... InterpolationTargetTags>\nstruct EvolutionMetavars\n    : public GhValenciaDivCleanTemplateBase<\n          EvolutionMetavars<UseControlSystems, UseParametrizedDeleptonization,\n                            InterpolationTargetTags...>,\n          true, UseControlSystems, UseParametrizedDeleptonization> {\n  using base = GhValenciaDivCleanTemplateBase<\n      EvolutionMetavars<UseControlSystems, UseParametrizedDeleptonization,\n                        InterpolationTargetTags...>,\n      true, UseControlSystems, UseParametrizedDeleptonization>;\n  using const_global_cache_tags = typename base::const_global_cache_tags;\n  using observed_reduction_data_tags =\n      typename base::observed_reduction_data_tags;\n  using component_list = typename base::component_list;\n  using factory_creation = typename base::factory_creation;\n  using registration = typename base::registration;\n\n  static constexpr Options::String help{\n      \"Evolve the Valencia formulation of the GRMHD system with divergence \"\n      \"cleaning, coupled to a dynamic spacetime evolved with the Generalized \"\n      \"Harmonic formulation\\n\"};\n};\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/GhValenciaDivClean/EvolveGhValenciaDivCleanWithHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/GrMhd/GhValenciaDivClean/EvolveGhValenciaDivCleanWithHorizon.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<false, false, BondiSachs>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &grmhd::GhValenciaDivClean::fd::register_derived_with_charm<\n           typename metavariables::base::system>,\n       &EquationsOfState::register_derived_with_charm,\n       &ConstraintDamping::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/GhValenciaDivClean/EvolveGhValenciaDivCleanWithHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <vector>\n\n#include \"Evolution/Executables/GrMhd/GhValenciaDivClean/GhValenciaDivCleanBase.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"Options/FactoryHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveFieldsOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveTimeSeriesOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <bool UseControlSystems, bool UseParametrizedDeleptonization,\n          typename... InterpolationTargetTags>\nstruct EvolutionMetavars\n    : public GhValenciaDivCleanTemplateBase<\n          EvolutionMetavars<UseControlSystems, UseParametrizedDeleptonization,\n                            InterpolationTargetTags...>,\n          false, false, UseParametrizedDeleptonization> {\n  static_assert(not UseControlSystems,\n                \"GhValenciaWithHorizon doesn't support control systems yet.\");\n  static constexpr bool use_dg_subcell = false;\n\n  using defaults = GhValenciaDivCleanDefaults<use_dg_subcell>;\n  using base = GhValenciaDivCleanTemplateBase<EvolutionMetavars, use_dg_subcell,\n                                              UseControlSystems,\n                                              UseParametrizedDeleptonization>;\n  static constexpr size_t volume_dim = defaults::volume_dim;\n  using domain_frame = typename defaults::domain_frame;\n  static constexpr bool use_damped_harmonic_rollon =\n      defaults::use_damped_harmonic_rollon;\n  using temporal_id = typename defaults::temporal_id;\n  static constexpr bool local_time_stepping = defaults::local_time_stepping;\n  using system = typename defaults::system;\n  using analytic_variables_tags = typename defaults::analytic_variables_tags;\n  using analytic_solution_fields = typename defaults::analytic_solution_fields;\n  using ordered_list_of_primitive_recovery_schemes =\n      typename defaults::ordered_list_of_primitive_recovery_schemes;\n  using initialize_initial_data_dependent_quantities_actions =\n      typename defaults::initialize_initial_data_dependent_quantities_actions;\n\n  static constexpr Options::String help{\n      \"Evolve the Valencia formulation of the GRMHD system with divergence \"\n      \"cleaning, coupled to a dynamic spacetime evolved with the Generalized \"\n      \"Harmonic formulation\\n\"\n      \"on a domain with a single horizon and corresponding excised region\"};\n\n  struct AhA : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n    using time_tag = ah::Tags::ObservationTime<0>;\n\n    using frame = domain_frame;\n\n    using horizon_find_callbacks =\n        tmpl::list<ah::callbacks::ObserveTimeSeriesOnHorizon<\n            ::ah::tags_for_observing<domain_frame>, AhA>>;\n    using horizon_find_failure_callbacks =\n        tmpl::list<ah::callbacks::FailedHorizonFind<AhA, false>>;\n\n    using compute_tags_on_element =\n        tmpl::list<ah::Tags::ObservationTimeCompute<0>>;\n\n    static constexpr ah::Destination destination = ah::Destination::Observation;\n\n    static std::string name() { return \"AhA\"; }\n  };\n\n  using interpolation_target_tags = tmpl::list<InterpolationTargetTags...>;\n\n  using observe_fields = typename base::observe_fields;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = Options::add_factory_classes<\n        typename base::factory_creation::factory_classes,\n        tmpl::pair<Event, tmpl::list<ah::Events::FindApparentHorizon<AhA>>>,\n        tmpl::pair<ah::Criterion, ah::Criteria::standard_criteria>>;\n  };\n\n  using initial_data_tag = typename base::initial_data_tag;\n\n  using const_global_cache_tags = tmpl::flatten<tmpl::list<\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions,\n      gh::gauges::Tags::GaugeCondition, initial_data_tag,\n      grmhd::ValenciaDivClean::Tags::ConstraintDampingParameter,\n      typename base::equation_of_state_tag,\n      gh::Tags::DampingFunctionGamma0<volume_dim, Frame::Grid>,\n      gh::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n      gh::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>,\n      ah::Tags::LMax>>;\n\n  using observed_reduction_data_tags = observers::collect_reduction_data_tags<\n      tmpl::at<typename factory_creation::factory_classes, Event>>;\n\n  using registration = typename base::registration;\n\n  using component_list = tmpl::push_back<typename base::component_list,\n                                         ah::Component<EvolutionMetavars, AhA>>;\n};\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/GhValenciaDivClean/GhValenciaDivCleanBase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n#include \"ControlSystem/Actions/GridCenters.hpp\"\n#include \"ControlSystem/Actions/InitializeMeasurements.hpp\"\n#include \"ControlSystem/Actions/LimitTimeStep.hpp\"\n#include \"ControlSystem/CleanFunctionsOfTime.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Measurements/BNSCenterOfMass.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Systems/Expansion.hpp\"\n#include \"ControlSystem/Systems/GridCenters.hpp\"\n#include \"ControlSystem/Systems/Rotation.hpp\"\n#include \"ControlSystem/Systems/Translation.hpp\"\n#include \"ControlSystem/Trigger.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/Conservative/UpdateConservatives.hpp\"\n#include \"Evolution/Conservative/UpdatePrimitives.hpp\"\n#include \"Evolution/DgSubcell/Actions/Initialize.hpp\"\n#include \"Evolution/DgSubcell/Actions/Labels.hpp\"\n#include \"Evolution/DgSubcell/Actions/ReconstructionCommunication.hpp\"\n#include \"Evolution/DgSubcell/Actions/SelectNumericalMethod.hpp\"\n#include \"Evolution/DgSubcell/Actions/TakeTimeStep.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndRollback.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndSwitchToDg.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/ComputeBoundaryTerms.hpp\"\n#include \"Evolution/DgSubcell/CorrectPackagedData.hpp\"\n#include \"Evolution/DgSubcell/DisableLts.hpp\"\n#include \"Evolution/DgSubcell/GetActiveTag.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/PrepareNeighborData.hpp\"\n#include \"Evolution/DgSubcell/SetInterpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMeshVelocity.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DgSubcell/TwoMeshRdmpTci.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/UsingSubcell.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/NumericInitialData.hpp\"\n#include \"Evolution/Systems/Cce/Callbacks/DumpBondiSachsOnWorldtube.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Equations.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Initialize.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/FixConservativesAndComputePrims.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimitiveGhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimsAfterRollback.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/ResizeAndComputePrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/TciOnDgGrid.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/TciOnFdGrid.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/ZeroTimeDerivatives.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/SystemM1.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/QuadrupoleFormula.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/SetVariablesNeededFixingToFalse.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"Evolution/Triggers/SeparationLessThan.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"Evolution/VariableFixing/Actions.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/LimitLorentzFactor.hpp\"\n#include \"Evolution/VariableFixing/ParameterizedDeleptonization.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/Actions/ReceiveVolumeData.hpp\"\n#include \"IO/Importers/Actions/RegisterWithElementDataReader.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveAtExtremum.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveDataBox.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Actions/RunEventsOnFailure.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/KerrHorizon.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GhGrMhd/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BlastWave.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticFieldLoop.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/OrszagTangVortex.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GhGrMhd/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/HomogeneousSphere.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SecondTimeDerivOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4Real.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/LowerSpatialFourVelocity.hpp\"\n#include \"PointwiseFunctions/Hydro/MassFlux.hpp\"\n#include \"PointwiseFunctions/Hydro/MassWeightedFluidItems.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TransportVelocity.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\n\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\ntemplate <bool UseDgSubcell>\nstruct GhValenciaDivCleanDefaults {\n public:\n  static constexpr size_t volume_dim = 3;\n  using domain_frame = Frame::Inertial;\n  static constexpr bool use_damped_harmonic_rollon = true;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using neutrino_system = RadiationTransport::NoNeutrinos::System;\n\n  using system = grmhd::GhValenciaDivClean::System<neutrino_system>;\n  using analytic_variables_tags =\n      typename system::primitive_variables_tag::tags_list;\n  using analytic_solution_fields =\n      tmpl::append<typename system::primitive_variables_tag::tags_list,\n                   typename system::gh_system::variables_tag::tags_list>;\n  using ordered_list_of_primitive_recovery_schemes = tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n\n  using initialize_initial_data_dependent_quantities_actions = tmpl::list<\n      gh::Actions::InitializeGhAnd3Plus1Variables<volume_dim>,\n      tmpl::conditional_t<UseDgSubcell,\n                          grmhd::GhValenciaDivClean::SetPiAndPhiFromConstraints,\n                          gh::gauges::SetPiAndPhiFromConstraints<\n                              ghmhd::GhValenciaDivClean::InitialData::\n                                  analytic_solutions_and_data_list,\n                              3>>,\n      Initialization::Actions::AddComputeTags<\n          tmpl::list<gr::Tags::SqrtDetSpatialMetricCompute<\n              DataVector, volume_dim, domain_frame>>>,\n      VariableFixing::Actions::FixVariables<\n          VariableFixing::FixToAtmosphere<volume_dim>>,\n      VariableFixing::Actions::FixVariables<VariableFixing::LimitLorentzFactor>,\n      Actions::UpdateConservatives,\n      tmpl::conditional_t<\n          UseDgSubcell,\n          tmpl::list<\n              Initialization::Actions::AddSimpleTags<\n                  grmhd::ValenciaDivClean::SetVariablesNeededFixingToFalse>,\n\n              evolution::dg::subcell::Actions::SetAndCommunicateInitialRdmpData<\n                  volume_dim,\n                  grmhd::ValenciaDivClean::subcell::SetInitialRdmpData>,\n              evolution::dg::subcell::Actions::ComputeAndSendTciOnInitialGrid<\n                  volume_dim, system,\n                  grmhd::GhValenciaDivClean::subcell::TciOnFdGrid>,\n              evolution::dg::subcell::Actions::SetInitialGridFromTciData<\n                  volume_dim, system>,\n              Actions::MutateApply<\n                  grmhd::GhValenciaDivClean::subcell::ResizeAndComputePrims<\n                      ordered_list_of_primitive_recovery_schemes>>,\n\n              VariableFixing::Actions::FixVariables<\n                  VariableFixing::FixToAtmosphere<volume_dim>>,\n              VariableFixing::Actions::FixVariables<\n                  VariableFixing::LimitLorentzFactor>,\n              Actions::UpdateConservatives,\n              grmhd::GhValenciaDivClean::SetPiAndPhiFromConstraints>,\n          tmpl::list<>>,\n      Parallel::Actions::TerminatePhase>;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\ntemplate <typename EvolutionMetavarsDerived, bool UseDgSubcell,\n          bool UseControlSystems, bool UseParametrizedDeleptonization>\nstruct GhValenciaDivCleanTemplateBase;\n\ntemplate <bool UseDgSubcell, bool UseControlSystems,\n          bool UseParametrizedDeleptonization,\n          template <bool, bool, typename...> class EvolutionMetavarsDerived,\n          typename... InterpolationTargetTags>\nstruct GhValenciaDivCleanTemplateBase<\n    EvolutionMetavarsDerived<UseControlSystems, UseParametrizedDeleptonization,\n                             InterpolationTargetTags...>,\n    UseDgSubcell, UseControlSystems, UseParametrizedDeleptonization>\n    : public virtual GhValenciaDivCleanDefaults<UseDgSubcell> {\n  using derived_metavars =\n      EvolutionMetavarsDerived<UseControlSystems,\n                               UseParametrizedDeleptonization,\n                               InterpolationTargetTags...>;\n  using defaults = GhValenciaDivCleanDefaults<UseDgSubcell>;\n  static constexpr size_t volume_dim = defaults::volume_dim;\n  using domain_frame = typename defaults::domain_frame;\n  static constexpr bool use_damped_harmonic_rollon =\n      defaults::use_damped_harmonic_rollon;\n  using temporal_id = typename defaults::temporal_id;\n  using TimeStepperBase = typename defaults::TimeStepperBase;\n  static constexpr bool local_time_stepping = defaults::local_time_stepping;\n  static constexpr bool use_dg_element_collection =\n      defaults::use_dg_element_collection;\n  using system = typename defaults::system;\n\n  using analytic_variables_tags = typename defaults::analytic_variables_tags;\n  using analytic_solution_fields = typename defaults::analytic_solution_fields;\n  using ordered_list_of_primitive_recovery_schemes =\n      typename defaults::ordered_list_of_primitive_recovery_schemes;\n  using initialize_initial_data_dependent_quantities_actions =\n      typename defaults::initialize_initial_data_dependent_quantities_actions;\n\n  static constexpr bool use_dg_subcell = UseDgSubcell;\n  static constexpr bool use_control_systems = UseControlSystems;\n  using initial_data_list =\n      ghmhd::GhValenciaDivClean::InitialData::initial_data_list;\n\n  // Boolean verifying if parameterized deleptonization will be active.  This\n  // will only be active for CCSN evolution.\n  using parameterized_deleptonization =\n      tmpl::conditional_t<UseParametrizedDeleptonization,\n                          VariableFixing::Actions::FixVariables<\n                              VariableFixing::ParameterizedDeleptonization>,\n                          tmpl::list<>>;\n\n  using initial_data_tag = evolution::initial_data::Tags::InitialData;\n  using equation_of_state_tag = hydro::Tags::GrmhdEquationOfState;\n  using measurement = control_system::measurements::BothNSCenters;\n  using control_systems = tmpl::conditional_t<\n      use_control_systems,\n      tmpl::list<control_system::Systems::Rotation<3, measurement>,\n                 control_system::Systems::Expansion<2, measurement>,\n                 control_system::Systems::Translation<2, measurement, 2>,\n                 control_system::Systems::GridCenters<2, measurement>>,\n      tmpl::list<>>;\n\n  using interpolator_source_vars =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::list<\n          typename InterpolationTargetTags::vars_to_interpolate_to_target...>>>;\n\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_solution_fields, use_dg_subcell, initial_data_list>;\n  using error_compute = Tags::ErrorsCompute<analytic_solution_fields>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_solution_fields>;\n  using observe_fields = tmpl::append<\n      typename system::variables_tag::tags_list,\n      typename system::primitive_variables_tag::tags_list, error_tags,\n      tmpl::list<\n          hydro::Tags::TildeDInHalfPlaneCompute<\n              DataVector, volume_dim,\n              ::hydro::HalfPlaneIntegralMask::PositiveXOnly,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>>,\n          hydro::Tags::TildeDInHalfPlaneCompute<\n              DataVector, volume_dim,\n              ::hydro::HalfPlaneIntegralMask::NegativeXOnly,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>>,\n          hydro::Tags::MassWeightedCoordsCompute<\n              DataVector, volume_dim,\n              ::hydro::HalfPlaneIntegralMask::PositiveXOnly,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>,\n              Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n              Frame::Inertial>,\n          hydro::Tags::MassWeightedCoordsCompute<\n              DataVector, volume_dim,\n              ::hydro::HalfPlaneIntegralMask::NegativeXOnly,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>,\n              Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n              Frame::Inertial>,\n\n          hydro::Tags::MassWeightedInternalEnergyCompute<DataVector>,\n          hydro::Tags::MassWeightedKineticEnergyCompute<DataVector>,\n          hydro::Tags::TildeDUnboundUtCriterionCompute<DataVector, volume_dim,\n                                                       domain_frame>,\n          hydro::Tags::LowerSpatialFourVelocityCompute,\n          hydro::Tags::MassWeightedCoordsCompute<\n              DataVector, volume_dim, ::hydro::HalfPlaneIntegralMask::None,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>,\n              Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n              Frame::Inertial>,\n          gr::Tags::SpacetimeNormalOneFormCompute<DataVector, volume_dim,\n                                                  domain_frame>,\n          gr::Tags::SpacetimeNormalVectorCompute<DataVector, volume_dim,\n                                                 domain_frame>,\n          gr::Tags::InverseSpacetimeMetricCompute<DataVector, volume_dim,\n                                                  domain_frame>,\n          gr::Tags::Lapse<DataVector>,\n          gr::Tags::Shift<DataVector, volume_dim, domain_frame>,\n          gr::Tags::SpatialMetric<DataVector, volume_dim, domain_frame>,\n          gr::Tags::DetSpatialMetric<DataVector>,\n          gr::Tags::InverseSpatialMetric<DataVector, volume_dim>,\n          gh::Tags::ExtrinsicCurvatureCompute<volume_dim, domain_frame>,\n          gh::Tags::DerivSpatialMetricCompute<volume_dim, ::Frame::Inertial>,\n          gr::Tags::SpatialChristoffelFirstKindCompute<DataVector, volume_dim,\n                                                       ::Frame::Inertial>,\n          gr::Tags::SpatialChristoffelSecondKindCompute<DataVector, volume_dim,\n                                                        ::Frame::Inertial>,\n          ::Tags::DerivTensorCompute<\n              gr::Tags::SpatialChristoffelSecondKind<DataVector, volume_dim>,\n              ::Events::Tags::ObserverInverseJacobian<\n                  volume_dim, Frame::ElementLogical, Frame::Inertial>,\n              ::Events::Tags::ObserverMesh<volume_dim>>,\n          gr::Tags::SpatialRicciCompute<DataVector, volume_dim,\n                                        ::Frame::Inertial>,\n          gr::Tags::SpatialRicciScalarCompute<DataVector, volume_dim,\n                                              ::Frame::Inertial>,\n\n          // Constraints\n          gh::Tags::GaugeConstraintCompute<volume_dim, domain_frame>,\n          gh::Tags::TwoIndexConstraintCompute<volume_dim, ::Frame::Inertial>,\n          gh::Tags::ThreeIndexConstraintCompute<volume_dim, ::Frame::Inertial>,\n          gh::Tags::FourIndexConstraintCompute<3, ::Frame::Inertial>,\n          gh::Tags::FConstraintCompute<3, ::Frame::Inertial>,\n\n          // L2 norms of constraints\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::GaugeConstraint<DataVector, volume_dim, domain_frame>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::TwoIndexConstraint<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::ThreeIndexConstraint<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::FourIndexConstraint<DataVector, 3>>,\n          gh::Tags::ConstraintEnergyCompute<3, ::Frame::Inertial>,\n          ::Tags::PointwiseL2NormCompute<gh::Tags::FConstraint<DataVector, 3>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::GaugeH<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::ConstraintEnergy<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<gh::Tags::Phi<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              ::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, volume_dim>,\n                            tmpl::size_t<volume_dim>, Frame::Inertial>>,\n\n          // GW Tags\n          grmhd::ValenciaDivClean::Tags::QuadrupoleMomentCompute<\n              DataVector, volume_dim,\n              ::Events::Tags::ObserverCoordinates<volume_dim, Frame::Inertial>>,\n          grmhd::ValenciaDivClean::Tags::QuadrupoleMomentDerivativeCompute<\n              DataVector, volume_dim,\n              ::Events::Tags::ObserverCoordinates<volume_dim, Frame::Inertial>,\n              hydro::Tags::SpatialVelocity<DataVector, volume_dim,\n                                           Frame::Inertial>>,\n          hydro::Tags::TransportVelocityCompute<DataVector, volume_dim,\n                                                Frame::Inertial>,\n          grmhd::ValenciaDivClean::Tags::QuadrupoleMomentDerivativeCompute<\n              DataVector, volume_dim,\n              ::Events::Tags::ObserverCoordinates<volume_dim, Frame::Inertial>,\n              hydro::Tags::TransportVelocity<DataVector, volume_dim,\n                                             Frame::Inertial>>,\n          gh::Tags::TimeDerivLapseCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::TimeDerivShiftCompute<volume_dim, Frame::Inertial>,\n          ::Tags::dt<gh::Tags::Phi<DataVector, volume_dim, Frame::Inertial>>,\n          ::Tags::dt<gh::Tags::Pi<DataVector, volume_dim, Frame::Inertial>>,\n          gh::Tags::SecondTimeDerivOfSpacetimeMetricCompute<volume_dim,\n                                                            Frame::Inertial>,\n          ::Tags::DerivTensorCompute<\n              gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n              ::Events::Tags::ObserverInverseJacobian<\n                  volume_dim, Frame::ElementLogical, Frame::Inertial>,\n              ::Events::Tags::ObserverMesh<volume_dim>>,\n          gr::Tags::WeylElectricCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::Psi4RealCompute<Frame::Inertial>,\n          ::Events::Tags::ObserverMeshVelocity<3>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<evolution::dg::subcell::Tags::TciStatusCompute<volume_dim>,\n                     evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n                         volume_dim, Frame::ElementLogical>,\n                     evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n                         volume_dim, Frame::Grid>,\n                     evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n                         volume_dim, Frame::Inertial>>,\n          tmpl::list<::Events::Tags::ObserverCoordinatesCompute<\n                         volume_dim, Frame::ElementLogical>,\n                     ::Events::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                                Frame::Grid>,\n                     ::Events::Tags::ObserverCoordinatesCompute<\n                         volume_dim, Frame::Inertial>>>>;\n  using integrand_fields = tmpl::append<\n      typename system::variables_tag::tags_list,\n      tmpl::list<\n          // Control system tags\n          hydro::Tags::TildeDInHalfPlaneCompute<\n              DataVector, volume_dim,\n              ::hydro::HalfPlaneIntegralMask::PositiveXOnly,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>>,\n          hydro::Tags::TildeDInHalfPlaneCompute<\n              DataVector, volume_dim,\n              ::hydro::HalfPlaneIntegralMask::NegativeXOnly,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>>,\n          hydro::Tags::MassWeightedCoordsCompute<\n              DataVector, volume_dim,\n              ::hydro::HalfPlaneIntegralMask::PositiveXOnly,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>,\n              Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n              Frame::Inertial>,\n          hydro::Tags::MassWeightedCoordsCompute<\n              DataVector, volume_dim,\n              ::hydro::HalfPlaneIntegralMask::NegativeXOnly,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>,\n              Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n              Frame::Inertial>,\n\n          // General tags\n          hydro::Tags::MassWeightedInternalEnergyCompute<DataVector>,\n          hydro::Tags::MassWeightedKineticEnergyCompute<DataVector>,\n          hydro::Tags::TildeDUnboundUtCriterionCompute<DataVector, volume_dim,\n                                                       domain_frame>,\n          hydro::Tags::MassWeightedCoordsCompute<\n              DataVector, volume_dim, ::hydro::HalfPlaneIntegralMask::None,\n              Events::Tags::ObserverCoordinates<3, Frame::Grid>,\n              Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n              Frame::Inertial>>>;\n\n  using non_tensor_compute_tags = tmpl::append<\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Tags::ObserverMeshCompute<volume_dim>,\n              evolution::dg::subcell::Tags::ObserverInverseJacobianCompute<\n                  volume_dim, Frame::ElementLogical, Frame::Inertial>,\n              evolution::dg::subcell::Tags::\n                  ObserverJacobianAndDetInvJacobianCompute<\n                      volume_dim, Frame::ElementLogical, Frame::Inertial>,\n              evolution::dg::subcell::Tags::ObserverMeshVelocityCompute<\n                  volume_dim>>,\n          tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                     ::Events::Tags::ObserverInverseJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverDetInvJacobianCompute<\n                         Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverMeshVelocityCompute<3>>>,\n      tmpl::list<analytic_compute, error_compute>,\n      tmpl::list<::Tags::DerivCompute<\n                     typename system::variables_tag,\n                     ::Events::Tags::ObserverMesh<volume_dim>,\n                     ::Events::Tags::ObserverInverseJacobian<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     typename system::gradient_variables>,\n                 gh::gauges::Tags::GaugeAndDerivativeCompute<\n                     volume_dim, ghmhd::GhValenciaDivClean::InitialData::\n                                     analytic_solutions_and_data_list>>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n   private:\n    using boundary_conditions = grmhd::GhValenciaDivClean::BoundaryConditions::\n        standard_boundary_conditions<system>;\n\n   public:\n    using factory_classes = tmpl::map<\n        tmpl::pair<DenseTrigger,\n                   tmpl::append<DenseTriggers::standard_dense_triggers,\n                                control_system::control_system_triggers<\n                                    control_systems>>>,\n        tmpl::pair<\n            DomainCreator<volume_dim>,\n            // Currently control systems can only be used with BCO\n            // domains\n            tmpl::conditional_t<\n                use_control_systems,\n                tmpl::list<::domain::creators::BinaryCompactObject<false>>,\n                domain_creators<volume_dim>>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion, ::Events::ObserveDataBox,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, non_tensor_compute_tags>,\n                       Events::ObserveAtExtremum<observe_fields,\n                                                 non_tensor_compute_tags>,\n                       Events::time_events<system>,\n                       dg::Events::ObserveTimeStepVolume<system>,\n                       control_system::metafunctions::control_system_events<\n                           control_systems>,\n                       tmpl::conditional_t<use_control_systems,\n                                           control_system::CleanFunctionsOfTime,\n                                           tmpl::list<>>,\n                       intrp::Events::InterpolateWithoutInterpComponent<\n                           volume_dim, InterpolationTargetTags,\n                           interpolator_source_vars>...>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   grmhd::GhValenciaDivClean::BoundaryCorrections::\n                       standard_boundary_corrections>,\n        tmpl::pair<\n            grmhd::GhValenciaDivClean::BoundaryConditions::BoundaryCondition,\n            boundary_conditions>,\n        tmpl::pair<\n            grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField,\n            grmhd::AnalyticData::InitialMagneticFields::\n                initial_magnetic_fields>,\n        tmpl::pair<gh::gauges::GaugeCondition, gh::gauges::all_gauges>,\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   MathFunctions::all_math_functions<1, Frame::Inertial>>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>,\n        // Restrict to monotonic time steppers in LTS to avoid control\n        // systems deadlocking.\n        tmpl::pair<\n            LtsTimeStepper,\n            tmpl::conditional_t<use_control_systems,\n                                TimeSteppers::monotonic_lts_time_steppers,\n                                TimeSteppers::lts_time_steppers>>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   StepChoosers::standard_step_choosers<system>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::append<StepChoosers::standard_slab_choosers<\n                                    system, local_time_stepping>,\n                                tmpl::conditional_t<\n                                    use_dg_subcell and local_time_stepping,\n                                    tmpl::list<StepChoosers::FixedLtsRatio>,\n                                    tmpl::list<>>>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<\n            Trigger,\n            tmpl::append<Triggers::logical_triggers, Triggers::time_triggers,\n                         tmpl::conditional_t<\n                             UseControlSystems,\n                             tmpl::list<Triggers::SeparationLessThan<true>>,\n                             tmpl::list<>>>>>;\n  };\n\n  using interpolation_target_tags = tmpl::list<InterpolationTargetTags...>;\n\n  using observed_reduction_data_tags = observers::collect_reduction_data_tags<\n      tmpl::at<typename factory_creation::factory_classes, Event>>;\n\n  using const_global_cache_tags = tmpl::flatten<tmpl::list<\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              grmhd::GhValenciaDivClean::fd::Tags::Reconstructor<system>,\n              grmhd::GhValenciaDivClean::fd::Tags::FilterOptions,\n              ::Tags::VariableFixer<grmhd::ValenciaDivClean::FixConservatives>,\n              grmhd::ValenciaDivClean::subcell::Tags::TciOptions>,\n          tmpl::list<>>,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions,\n      gh::gauges::Tags::GaugeCondition, initial_data_tag,\n      grmhd::ValenciaDivClean::Tags::ConstraintDampingParameter,\n      equation_of_state_tag,\n      gh::Tags::DampingFunctionGamma0<volume_dim, Frame::Grid>,\n      gh::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n      gh::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>>>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  static constexpr auto default_phase_order = std::array<Parallel::Phase, 8>{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::RegisterWithElementDataReader,\n       Parallel::Phase::Register, Parallel::Phase::ImportInitialData,\n       Parallel::Phase::InitializeInitialDataDependentQuantities,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Evolve,\n       Parallel::Phase::Exit}};\n\n  struct SubcellOptions {\n    using evolved_vars_tags = typename system::variables_tag::tags_list;\n    using prim_tags = typename system::primitive_variables_tag::tags_list;\n\n    static constexpr bool subcell_enabled = use_dg_subcell;\n    static constexpr bool subcell_enabled_at_external_boundary = true;\n\n    // We send `ghost_zone_size` cell-centered grid points for variable\n    // reconstruction, of which we need `ghost_zone_size-1` for reconstruction\n    // to the internal side of the element face, and `ghost_zone_size` for\n    // reconstruction to the external side of the element face.\n    template <typename DbTagsList>\n    static constexpr size_t ghost_zone_size(\n        const db::DataBox<DbTagsList>& box) {\n      return db::get<\n                 grmhd::GhValenciaDivClean::fd::Tags::Reconstructor<system>>(\n                 box)\n          .ghost_zone_size();\n    }\n\n    using DgComputeSubcellNeighborPackagedData =\n        grmhd::GhValenciaDivClean::subcell::NeighborPackagedData<system>;\n\n    using GhostVariables =\n        grmhd::GhValenciaDivClean::subcell::PrimitiveGhostVariables;\n  };\n\n  using events_and_dense_triggers_dg_postprocessors = tmpl::list<\n      ::domain::CheckFunctionsOfTimeAreReadyPostprocessor<volume_dim>,\n      AlwaysReadyPostprocessor<\n          typename system::template primitive_from_conservative<\n              ordered_list_of_primitive_recovery_schemes>>>;\n\n  using events_and_dense_triggers_subcell_postprocessors = tmpl::list<\n      ::domain::CheckFunctionsOfTimeAreReadyPostprocessor<volume_dim>,\n      AlwaysReadyPostprocessor<\n          grmhd::GhValenciaDivClean::subcell::FixConservativesAndComputePrims<\n              ordered_list_of_primitive_recovery_schemes, system>>>;\n\n  using dg_step_actions = tmpl::flatten<tmpl::list<\n      dg::Actions::Filter<::Filters::Exponential<0>,\n                          tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                                     gh::Tags::Pi<DataVector, 3>,\n                                     gh::Tags::Phi<DataVector, 3>>>,\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      tmpl::conditional_t<\n          UseControlSystems,\n          Actions::MutateApply<grmhd::GhValenciaDivClean::subcell::\n                                   ZeroMhdTimeDerivatives<system>>,\n          tmpl::list<>>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::push_front<\n                  events_and_dense_triggers_dg_postprocessors,\n                  evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                      derived_metavars>>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n              evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                  volume_dim, use_dg_element_collection>,\n              Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<\n                  events_and_dense_triggers_dg_postprocessors>,\n              control_system::Actions::LimitTimeStep<control_systems>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          // Note: The primitive variables are computed as part of the TCI.\n          evolution::dg::subcell::Actions::TciAndRollback<\n              grmhd::GhValenciaDivClean::subcell::TciOnDgGrid<\n                  tmpl::front<ordered_list_of_primitive_recovery_schemes>>>,\n          tmpl::list<>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<parameterized_deleptonization,\n                     VariableFixing::Actions::FixVariables<\n                         VariableFixing::FixToAtmosphere<volume_dim>>,\n                     VariableFixing::Actions::FixVariables<\n                         VariableFixing::LimitLorentzFactor>,\n                     Actions::UpdateConservatives>,\n          tmpl::list<parameterized_deleptonization,\n                     VariableFixing::Actions::FixVariables<\n                         grmhd::ValenciaDivClean::FixConservatives>,\n                     Actions::UpdatePrimitives>>>>;\n\n  using dg_subcell_step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::subcell::Actions::SelectNumericalMethod,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginDg>,\n      dg_step_actions,\n      Actions::Goto<evolution::dg::subcell::Actions::Labels::EndOfSolvers>,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginSubcell>,\n      tmpl::conditional_t<local_time_stepping,\n                          // This is just to adjust for FixedLtsRatio, so we\n                          // can pass an empty list of StepChoosers.\n                          Actions::MutateApply<ChangeStepSize<tmpl::list<>>>,\n                          tmpl::list<>>,\n      Actions::MutateApply<evolution::dg::subcell::fd::CellCenteredFlux<\n          system, grmhd::ValenciaDivClean::ComputeFluxes, volume_dim, false>>,\n      evolution::dg::subcell::Actions::SendDataForReconstruction<\n          volume_dim,\n          grmhd::GhValenciaDivClean::subcell::PrimitiveGhostVariables,\n          use_dg_element_collection>,\n      evolution::dg::subcell::Actions::ReceiveDataForReconstruction<volume_dim>,\n      Actions::Label<\n          evolution::dg::subcell::Actions::Labels::BeginSubcellAfterDgRollback>,\n      Actions::MutateApply<\n          grmhd::GhValenciaDivClean::subcell::PrimsAfterRollback<\n              ordered_list_of_primitive_recovery_schemes>>,\n      Actions::MutateApply<evolution::dg::subcell::fd::CellCenteredFlux<\n          system, grmhd::ValenciaDivClean::ComputeFluxes, volume_dim, true>>,\n      evolution::dg::subcell::fd::Actions::TakeTimeStep<\n          grmhd::GhValenciaDivClean::subcell::TimeDerivative<system>>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      evolution::Actions::RunEventsAndDenseTriggers<\n          events_and_dense_triggers_subcell_postprocessors>,\n      tmpl::conditional_t<\n          local_time_stepping, tmpl::list<>,\n          control_system::Actions::LimitTimeStep<control_systems>>,\n      Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      Actions::MutateApply<\n          grmhd::GhValenciaDivClean::subcell::FixConservativesAndComputePrims<\n              ordered_list_of_primitive_recovery_schemes, system>>,\n      evolution::dg::subcell::Actions::TciAndSwitchToDg<\n          grmhd::GhValenciaDivClean::subcell::TciOnFdGrid>,\n      Actions::MutateApply<\n          grmhd::GhValenciaDivClean::subcell::ResizeAndComputePrims<\n              ordered_list_of_primitive_recovery_schemes>>,\n      parameterized_deleptonization,\n      VariableFixing::Actions::FixVariables<\n          VariableFixing::FixToAtmosphere<volume_dim>>,\n      VariableFixing::Actions::FixVariables<VariableFixing::LimitLorentzFactor>,\n      Actions::UpdateConservatives,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::EndOfSolvers>>>;\n\n  using step_actions =\n      tmpl::conditional_t<use_dg_subcell, dg_subcell_step_actions,\n                          dg_step_actions>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<derived_metavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<derived_metavars,\n                                                use_control_systems>,\n          Initialization::TimeStepperHistory<derived_metavars>>,\n      Initialization::Actions::ConservativeSystem<system>,\n      // This conditional is untested and probably doesn't work if\n      // `use_dg_subcell` is `false`\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Actions::SetSubcellGrid<volume_dim,\n                                                              system, true>,\n              Actions::MutateApply<evolution::dg::subcell::SetInterpolators<\n                  volume_dim,\n                  grmhd::GhValenciaDivClean::fd::Tags::Reconstructor<system>>>>,\n          evolution::Initialization::Actions::SetVariables<\n              ::domain::Tags::Coordinates<volume_dim, Frame::ElementLogical>>>,\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<\n              GhValenciaDivCleanTemplateBase, local_time_stepping>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      tmpl::conditional_t<use_dg_subcell and local_time_stepping,\n                          Initialization::Actions::InitializeItems<\n                              evolution::dg::subcell::DisableLts<3>>,\n                          tmpl::list<>>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      intrp::Actions::ElementInitInterpPoints<volume_dim,\n                                              interpolation_target_tags>,\n      tmpl::conditional_t<\n          use_control_systems,\n          control_system::Actions::InitializeMeasurements<control_systems>,\n          tmpl::list<>>,\n      Parallel::Actions::TerminatePhase>;\n\n  using import_initial_data_action_lists = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::RegisterWithElementDataReader,\n          tmpl::list<importers::Actions::RegisterWithElementDataReader,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::ImportInitialData,\n          tmpl::list<\n              grmhd::GhValenciaDivClean::Actions::SetInitialData,\n              grmhd::GhValenciaDivClean::Actions::ReceiveNumericInitialData,\n              Parallel::Actions::TerminatePhase>>>;\n\n  using dg_element_array_component = DgElementArray<\n      derived_metavars,\n      tmpl::flatten<tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          import_initial_data_action_lists,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeInitialDataDependentQuantities,\n              initialize_initial_data_dependent_quantities_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  ::domain::Actions::CheckFunctionsOfTimeAreReady<volume_dim>,\n                  parameterized_deleptonization,\n                  VariableFixing::Actions::FixVariables<\n                      VariableFixing::FixToAtmosphere<volume_dim>>,\n                  VariableFixing::Actions::FixVariables<\n                      VariableFixing::LimitLorentzFactor>,\n                  Actions::UpdateConservatives,\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>,\n\n          tmpl::conditional_t<\n              UseControlSystems,\n              Parallel::PhaseActions<\n                  Parallel::Phase::DisableRotationControl,\n                  tmpl::list<\n                      control_system::Actions::SwitchGridRotationToSettle,\n                      Parallel::Actions::TerminatePhase>>,\n              tmpl::list<>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::PostFailureCleanup,\n              tmpl::list<Actions::RunEventsOnFailure<Tags::Time>,\n                         Parallel::Actions::TerminatePhase>>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array_component, dg_registration_list>>;\n  };\n\n  using component_list = tmpl::flatten<tmpl::list<\n      observers::Observer<derived_metavars>,\n      observers::ObserverWriter<derived_metavars>,\n      importers::ElementDataReader<derived_metavars>,\n      control_system::control_components<derived_metavars, control_systems>,\n      intrp::InterpolationTarget<derived_metavars, InterpolationTargetTags>...,\n      dg_element_array_component>>;\n};\n\nstruct BondiSachs : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  static std::string name() { return \"BondiSachsInterpolation\"; }\n  using temporal_id = ::Tags::Time;\n  using vars_to_interpolate_to_target =\n      typename gh::System<3>::variables_tag::tags_list;\n  using compute_target_points =\n      intrp::TargetPoints::Sphere<BondiSachs, ::Frame::Inertial>;\n  using post_interpolation_callbacks =\n      tmpl::list<intrp::callbacks::DumpBondiSachsOnWorldtube<BondiSachs>>;\n  using compute_items_on_target = tmpl::list<>;\n  template <typename Metavariables>\n  using interpolating_component =\n      typename Metavariables::dg_element_array_component;\n};\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/ValenciaDivClean/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  ApparentHorizonFinder\n  Charmxx::main\n  CoordinateMaps\n  DgSubcell\n  DiscontinuousGalerkin\n  DomainCreators\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  FiniteDifference\n  GeneralRelativitySolutions\n  GrMhdAnalyticData\n  GrMhdSolutions\n  Hydro\n  Informer\n  Interpolation\n  LinearOperators\n  MathFunctions\n  RelativisticEulerSolutions\n  Observer\n  Options\n  Parallel\n  ParallelInterpolation\n  PhaseControl\n  Serialization\n  Time\n  Utilities\n  ValenciaDivClean\n  )\n\nfunction(add_grmhd_executable SUFFIX INTERPOLATION_TARGET USE_PARAMETRIZED_DELEPTONIZATION)\n  set(EXECUTABLE \"EvolveValenciaDivClean${SUFFIX}\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    EvolveValenciaDivClean.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    INTERPOLATION_TARGET=${INTERPOLATION_TARGET}\n    USE_PARAMETRIZED_DELEPTONIZATION=${USE_PARAMETRIZED_DELEPTONIZATION}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction()\n\nadd_grmhd_executable(\n  WithHorizon\n  KerrHorizon\n  false\n)\n\nadd_grmhd_executable(\n  \"\"\n  \"\"\n  false\n)\n\n# Core-collpase SN uses parametrized deleptonization\nadd_grmhd_executable(\n  CoreCollapseSupernova\n  \"\"\n  true\n)\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/ValenciaDivClean/EvolveValenciaDivClean.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/GrMhd/ValenciaDivClean/EvolveValenciaDivClean.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<tmpl::list<INTERPOLATION_TARGET>,\n                                        USE_PARAMETRIZED_DELEPTONIZATION>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &grmhd::ValenciaDivClean::fd::register_derived_with_charm,\n       &EquationsOfState::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/GrMhd/ValenciaDivClean/EvolveValenciaDivClean.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <vector>\n\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/Conservative/UpdateConservatives.hpp\"\n#include \"Evolution/Conservative/UpdatePrimitives.hpp\"\n#include \"Evolution/DgSubcell/Actions/Initialize.hpp\"\n#include \"Evolution/DgSubcell/Actions/Labels.hpp\"\n#include \"Evolution/DgSubcell/Actions/ReconstructionCommunication.hpp\"\n#include \"Evolution/DgSubcell/Actions/SelectNumericalMethod.hpp\"\n#include \"Evolution/DgSubcell/Actions/TakeTimeStep.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndRollback.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndSwitchToDg.hpp\"\n#include \"Evolution/DgSubcell/BackgroundGrVars.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/ComputeBoundaryTerms.hpp\"\n#include \"Evolution/DgSubcell/CorrectPackagedData.hpp\"\n#include \"Evolution/DgSubcell/DisableLts.hpp\"\n#include \"Evolution/DgSubcell/GetTciDecision.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp\"\n#include \"Evolution/DgSubcell/NeighborTciDecision.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/PrepareNeighborData.hpp\"\n#include \"Evolution/DgSubcell/SetInterpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/MethodOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DgSubcell/TwoMeshRdmpTci.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BackgroundGrVars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComovingMagneticFieldMagnitude.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Flattener.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/QuadrupoleFormula.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/SetVariablesNeededFixingToFalse.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/FixConservativesAndComputePrims.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimitiveGhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimsAfterRollback.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ResizeAndComputePrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SwapGrTags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnDgGrid.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnFdGrid.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/Actions.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/ParameterizedDeleptonization.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveNorms.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Actions/RunEventsOnFailure.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/KerrHorizon.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/SpecifiedPoints.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BlastWave.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/KhInstability.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticFieldLoop.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/OrszagTangVortex.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/PolarMagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/RiemannProblem.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SlabJet.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"PointwiseFunctions/Hydro/InversePlasmaBeta.hpp\"\n#include \"PointwiseFunctions/Hydro/MassFlux.hpp\"\n#include \"PointwiseFunctions/Hydro/QuadrupoleFormula.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TransportVelocity.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\ntemplate <typename InterpolationTargetTags, bool UseParametrizedDeleptonization>\nstruct EvolutionMetavars;\n\ntemplate <typename... InterpolationTargetTags,\n          bool UseParametrizedDeleptonization>\nstruct EvolutionMetavars<tmpl::list<InterpolationTargetTags...>,\n                         UseParametrizedDeleptonization> {\n  // The use_dg_subcell flag controls whether to use unlimited DG (false)\n  // or a DG-FD hybrid scheme (true).\n  static constexpr bool use_parametrized_deleptonization =\n      UseParametrizedDeleptonization;\n  static constexpr bool use_dg_subcell = true;\n  static constexpr size_t volume_dim = 3;\n  using initial_data_list =\n      grmhd::ValenciaDivClean::InitialData::initial_data_list;\n\n  // Boolean verifying if parameterized deleptonization will be active.  This\n  // will only be active for CCSN evolution.\n  using parameterized_deleptonization =\n      tmpl::conditional_t<UseParametrizedDeleptonization,\n                          VariableFixing::Actions::FixVariables<\n                              VariableFixing::ParameterizedDeleptonization>,\n                          tmpl::list<>>;\n  using eos_base = EquationsOfState::EquationOfState<true, 3>;\n  using equation_of_state_type = typename std::unique_ptr<eos_base>;\n  using initial_data_tag = evolution::initial_data::Tags::InitialData;\n  using system = grmhd::ValenciaDivClean::System;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using analytic_variables_tags =\n      typename system::primitive_variables_tag::tags_list;\n  using equation_of_state_tag = hydro::Tags::GrmhdEquationOfState;\n\n  using interpolator_source_vars =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::list<\n          typename InterpolationTargetTags::vars_to_interpolate_to_target...>>>;\n\n  using ordered_list_of_primitive_recovery_schemes = tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n\n  using interpolation_target_tags = tmpl::list<InterpolationTargetTags...>;\n\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_variables_tags, use_dg_subcell, initial_data_list>;\n  using error_compute = Tags::ErrorsCompute<analytic_variables_tags>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_variables_tags>;\n  using observe_fields = tmpl::push_back<\n      tmpl::append<\n          typename system::variables_tag::tags_list,\n          typename system::primitive_variables_tag::tags_list,\n          typename system::flux_spacetime_variables_tag::tags_list,\n          tmpl::list<\n              grmhd::ValenciaDivClean::Tags::\n                  ComovingMagneticFieldMagnitudeCompute,\n              ::Tags::DivVectorCompute<\n                  hydro::Tags::MagneticField<DataVector, volume_dim>,\n                  ::Events::Tags::ObserverMesh<volume_dim>,\n                  ::Events::Tags::ObserverInverseJacobian<\n                      volume_dim, Frame::ElementLogical, Frame::Inertial>>>,\n          error_tags,\n          tmpl::conditional_t<\n              use_dg_subcell,\n              tmpl::list<\n                  evolution::dg::subcell::Tags::TciStatusCompute<volume_dim>,\n                  evolution::dg::subcell::Tags::MethodOrderCompute<volume_dim>>,\n              tmpl::list<>>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n              volume_dim, Frame::ElementLogical>,\n          ::Events::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                     Frame::ElementLogical>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                                   Frame::Grid>,\n          ::Events::Tags::ObserverCoordinatesCompute<volume_dim, Frame::Grid>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n              volume_dim, Frame::Inertial>,\n          ::Events::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                     Frame::Inertial>>,\n      grmhd::ValenciaDivClean::Tags::QuadrupoleMomentCompute<\n          DataVector, volume_dim,\n          ::Events::Tags::ObserverCoordinates<volume_dim, Frame::Inertial>>,\n      grmhd::ValenciaDivClean::Tags::QuadrupoleMomentDerivativeCompute<\n          DataVector, volume_dim,\n          ::Events::Tags::ObserverCoordinates<volume_dim, Frame::Inertial>,\n          hydro::Tags::SpatialVelocity<DataVector, volume_dim,\n                                       Frame::Inertial>>,\n      hydro::Tags::TransportVelocityCompute<DataVector, volume_dim,\n                                            Frame::Inertial>,\n      grmhd::ValenciaDivClean::Tags::QuadrupoleMomentDerivativeCompute<\n          DataVector, volume_dim,\n          ::Events::Tags::ObserverCoordinates<volume_dim, Frame::Inertial>,\n          hydro::Tags::TransportVelocity<DataVector, volume_dim,\n                                         Frame::Inertial>>,\n      hydro::Tags::InversePlasmaBetaCompute<DataVector>>;\n  using non_tensor_compute_tags = tmpl::list<\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Tags::ObserverMeshCompute<volume_dim>,\n              evolution::dg::subcell::Tags::ObserverInverseJacobianCompute<\n                  volume_dim, Frame::ElementLogical, Frame::Inertial>,\n              evolution::dg::subcell::Tags::\n                  ObserverJacobianAndDetInvJacobianCompute<\n                      volume_dim, Frame::ElementLogical, Frame::Inertial>>,\n          tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                     ::Events::Tags::ObserverInverseJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverDetInvJacobianCompute<\n                         Frame::ElementLogical, Frame::Inertial>>>,\n      analytic_compute, error_compute>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<\n            Event,\n            tmpl::flatten<tmpl::list<\n                Events::Completion,\n                dg::Events::field_observations<volume_dim, observe_fields,\n                                               non_tensor_compute_tags>,\n                Events::time_events<system>,\n                intrp::Events::InterpolateWithoutInterpComponent<\n                    3, InterpolationTargetTags, interpolator_source_vars>...>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   grmhd::ValenciaDivClean::BoundaryCorrections::\n                       standard_boundary_corrections>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>,\n        tmpl::pair<\n            grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition,\n            grmhd::ValenciaDivClean::BoundaryConditions::\n                standard_boundary_conditions>,\n        tmpl::pair<\n            grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField,\n            grmhd::AnalyticData::InitialMagneticFields::\n                initial_magnetic_fields>,\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   StepChoosers::standard_step_choosers<system>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::append<StepChoosers::standard_slab_choosers<\n                                    system, local_time_stepping>,\n                                tmpl::conditional_t<\n                                    use_dg_subcell and local_time_stepping,\n                                    tmpl::list<StepChoosers::FixedLtsRatio>,\n                                    tmpl::list<>>>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  using observed_reduction_data_tags = observers::collect_reduction_data_tags<\n      tmpl::at<typename factory_creation::factory_classes, Event>>;\n\n  struct SubcellOptions {\n    using evolved_vars_tags = typename system::variables_tag::tags_list;\n    using prim_tags = typename system::primitive_variables_tag::tags_list;\n    using recons_prim_tags = tmpl::push_back<\n        prim_tags,\n        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>;\n    using fluxes_tags =\n        db::wrap_tags_in<Tags::Flux, evolved_vars_tags,\n                         tmpl::size_t<volume_dim>, Frame::Inertial>;\n\n    static constexpr bool subcell_enabled = use_dg_subcell;\n    static constexpr bool subcell_enabled_at_external_boundary = true;\n\n    // We send `ghost_zone_size` cell-centered grid points for variable\n    // reconstruction, of which we need `ghost_zone_size-1` for reconstruction\n    // to the internal side of the element face, and `ghost_zone_size` for\n    // reconstruction to the external side of the element face.\n    template <typename DbTagsList>\n    static constexpr size_t ghost_zone_size(\n        const db::DataBox<DbTagsList>& box) {\n      return db::get<grmhd::ValenciaDivClean::fd::Tags::Reconstructor>(box)\n          .ghost_zone_size();\n    }\n\n    using DgComputeSubcellNeighborPackagedData =\n        grmhd::ValenciaDivClean::subcell::NeighborPackagedData;\n\n    using GhostVariables =\n        grmhd::ValenciaDivClean::subcell::PrimitiveGhostVariables;\n  };\n\n  using events_and_dense_triggers_dg_postprocessors =\n      tmpl::list<AlwaysReadyPostprocessor<system::primitive_from_conservative<\n          ordered_list_of_primitive_recovery_schemes>>>;\n\n  using events_and_dense_triggers_subcell_postprocessors =\n      tmpl::list<AlwaysReadyPostprocessor<\n          grmhd::ValenciaDivClean::subcell::FixConservativesAndComputePrims<\n              ordered_list_of_primitive_recovery_schemes>>>;\n\n  using dg_step_actions = tmpl::flatten<tmpl::list<\n      Actions::MutateApply<\n          evolution::dg::BackgroundGrVars<system, EvolutionMetavars>>,\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::push_front<\n                  events_and_dense_triggers_dg_postprocessors,\n                  evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                      EvolutionMetavars>>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n              evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                  volume_dim, use_dg_element_collection>,\n              Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<\n                  events_and_dense_triggers_dg_postprocessors>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          // Note: The primitive variables are computed as part of the TCI.\n          evolution::dg::subcell::Actions::TciAndRollback<\n              grmhd::ValenciaDivClean::subcell::TciOnDgGrid<\n                  tmpl::front<ordered_list_of_primitive_recovery_schemes>>>,\n          tmpl::list<>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<>,\n          tmpl::list<VariableFixing::Actions::FixVariables<\n              grmhd::ValenciaDivClean::Flattener<\n                  ordered_list_of_primitive_recovery_schemes>>>>,\n      parameterized_deleptonization,\n      VariableFixing::Actions::FixVariables<\n          VariableFixing::FixToAtmosphere<volume_dim>>,\n      Actions::UpdateConservatives>>;\n\n  using dg_subcell_step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::subcell::Actions::SelectNumericalMethod,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginDg>,\n      dg_step_actions,\n      Actions::Goto<evolution::dg::subcell::Actions::Labels::EndOfSolvers>,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginSubcell>,\n      tmpl::conditional_t<local_time_stepping,\n                          // This is just to adjust for FixedLtsRatio, so we\n                          // can pass an empty list of StepChoosers.\n                          Actions::MutateApply<ChangeStepSize<tmpl::list<>>>,\n                          tmpl::list<>>,\n      Actions::MutateApply<evolution::dg::subcell::BackgroundGrVars<\n          system, EvolutionMetavars, false>>,\n      Actions::MutateApply<evolution::dg::subcell::fd::CellCenteredFlux<\n          system, grmhd::ValenciaDivClean::ComputeFluxes, volume_dim, false>>,\n      evolution::dg::subcell::Actions::SendDataForReconstruction<\n          volume_dim, grmhd::ValenciaDivClean::subcell::PrimitiveGhostVariables,\n          use_dg_element_collection>,\n      evolution::dg::subcell::Actions::ReceiveAndSendDataForReconstruction<\n          volume_dim, grmhd::ValenciaDivClean::subcell::PrimitiveGhostVariables,\n          use_dg_element_collection>,\n      evolution::dg::subcell::Actions::ReceiveDataForReconstruction<volume_dim>,\n      Actions::Label<\n          evolution::dg::subcell::Actions::Labels::BeginSubcellAfterDgRollback>,\n      Actions::MutateApply<evolution::dg::subcell::BackgroundGrVars<\n          system, EvolutionMetavars, true>>,\n      Actions::MutateApply<grmhd::ValenciaDivClean::subcell::SwapGrTags>,\n      Actions::MutateApply<grmhd::ValenciaDivClean::subcell::PrimsAfterRollback<\n          ordered_list_of_primitive_recovery_schemes>>,\n      Actions::MutateApply<evolution::dg::subcell::fd::CellCenteredFlux<\n          system, grmhd::ValenciaDivClean::ComputeFluxes, volume_dim, true>>,\n      evolution::dg::subcell::fd::Actions::TakeTimeStep<\n          grmhd::ValenciaDivClean::subcell::TimeDerivative>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      evolution::Actions::RunEventsAndDenseTriggers<\n          events_and_dense_triggers_subcell_postprocessors>,\n      Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      Actions::MutateApply<\n          grmhd::ValenciaDivClean::subcell::FixConservativesAndComputePrims<\n              ordered_list_of_primitive_recovery_schemes>>,\n      evolution::dg::subcell::Actions::TciAndSwitchToDg<\n          grmhd::ValenciaDivClean::subcell::TciOnFdGrid>,\n      Actions::MutateApply<grmhd::ValenciaDivClean::subcell::SwapGrTags>,\n      Actions::MutateApply<\n          grmhd::ValenciaDivClean::subcell::ResizeAndComputePrims<\n              ordered_list_of_primitive_recovery_schemes>>,\n      parameterized_deleptonization,\n      VariableFixing::Actions::FixVariables<\n          VariableFixing::FixToAtmosphere<volume_dim>>,\n      Actions::UpdateConservatives,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::EndOfSolvers>>>;\n\n  using step_actions =\n      tmpl::conditional_t<use_dg_subcell, dg_subcell_step_actions,\n                          dg_step_actions>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::flatten<tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::AddSimpleTags<\n          evolution::dg::BackgroundGrVars<system, EvolutionMetavars>>,\n      Initialization::Actions::ConservativeSystem<system>,\n\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Actions::SetSubcellGrid<volume_dim,\n                                                              system, false>,\n              Actions::MutateApply<evolution::dg::subcell::SetInterpolators<\n                  volume_dim,\n                  grmhd::ValenciaDivClean::fd::Tags::Reconstructor>>,\n              Initialization::Actions::AddSimpleTags<\n                  evolution::dg::subcell::BackgroundGrVars<\n                      system, EvolutionMetavars, false>,\n                  grmhd::ValenciaDivClean::SetVariablesNeededFixingToFalse>,\n              Actions::MutateApply<\n                  grmhd::ValenciaDivClean::subcell::SwapGrTags>,\n              parameterized_deleptonization,\n              VariableFixing::Actions::FixVariables<\n                  VariableFixing::FixToAtmosphere<volume_dim>>,\n              Actions::UpdateConservatives,\n              evolution::dg::subcell::Actions::SetAndCommunicateInitialRdmpData<\n                  volume_dim,\n                  grmhd::ValenciaDivClean::subcell::SetInitialRdmpData>,\n              evolution::dg::subcell::Actions::ComputeAndSendTciOnInitialGrid<\n                  volume_dim, system,\n                  grmhd::ValenciaDivClean::subcell::TciOnFdGrid>,\n              evolution::dg::subcell::Actions::SetInitialGridFromTciData<\n                  volume_dim, system>,\n              Actions::MutateApply<\n                  grmhd::ValenciaDivClean::subcell::SwapGrTags>,\n              Actions::MutateApply<\n                  grmhd::ValenciaDivClean::subcell::ResizeAndComputePrims<\n                      ordered_list_of_primitive_recovery_schemes>>,\n              parameterized_deleptonization,\n              VariableFixing::Actions::FixVariables<\n                  VariableFixing::FixToAtmosphere<volume_dim>>,\n              Actions::UpdateConservatives>,\n          tmpl::list<evolution::Initialization::Actions::SetVariables<\n                         domain::Tags::Coordinates<3, Frame::ElementLogical>>,\n                     parameterized_deleptonization,\n                     VariableFixing::Actions::FixVariables<\n                         VariableFixing::FixToAtmosphere<volume_dim>>,\n                     Actions::UpdateConservatives>>,\n\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<EvolutionMetavars,\n                                                  local_time_stepping>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      tmpl::conditional_t<use_dg_subcell and local_time_stepping,\n                          Initialization::Actions::InitializeItems<\n                              evolution::dg::subcell::DisableLts<3>>,\n                          tmpl::list<>>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      intrp::Actions::ElementInitInterpPoints<volume_dim,\n                                              interpolation_target_tags>,\n      Parallel::Actions::TerminatePhase>>;\n\n  using dg_element_array_component = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialization_actions>,\n\n                 Parallel::PhaseActions<\n                     Parallel::Phase::InitializeTimeStepperHistory,\n                     SelfStart::self_start_procedure<step_actions, system>>,\n\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Register,\n                     tmpl::push_back<dg_registration_list,\n                                     Parallel::Actions::TerminatePhase>>,\n\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Restart,\n                     tmpl::push_back<dg_registration_list,\n                                     Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::PostFailureCleanup,\n              tmpl::list<Actions::RunEventsOnFailure<Tags::Time>,\n                         Parallel::Actions::TerminatePhase>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array_component, dg_registration_list>>;\n  };\n\n  using component_list = tmpl::list<\n      observers::Observer<EvolutionMetavars>,\n      observers::ObserverWriter<EvolutionMetavars>,\n      intrp::InterpolationTarget<EvolutionMetavars, InterpolationTargetTags>...,\n      dg_element_array_component>;\n\n  using const_global_cache_tags = tmpl::push_back<\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              grmhd::ValenciaDivClean::fd::Tags::Reconstructor,\n              ::Tags::VariableFixer<grmhd::ValenciaDivClean::FixConservatives>,\n              grmhd::ValenciaDivClean::subcell::Tags::TciOptions>,\n          tmpl::list<>>,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions,\n      equation_of_state_tag, initial_data_tag,\n      grmhd::ValenciaDivClean::Tags::ConstraintDampingParameter>;\n\n  static constexpr Options::String help{\n      \"Evolve the Valencia formulation of the GRMHD system with divergence \"\n      \"cleaning.\\n\\n\"};\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Register,\n       Parallel::Phase::Evolve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nstruct CenterOfStar : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  struct MaxOfScalar : db::SimpleTag {\n    using type = double;\n  };\n\n  template <typename TagOfScalar>\n  struct MaxOfScalarCompute : db::ComputeTag, MaxOfScalar {\n    using base = MaxOfScalar;\n    using return_type = double;\n    static void function(const gsl::not_null<double*> max_of_scalar,\n                         const Scalar<DataVector>& scalar) {\n      *max_of_scalar = max(get(scalar));\n    };\n    using argument_tags = tmpl::list<TagOfScalar>;\n  };\n\n  using temporal_id = ::Tags::Time;\n  using tags_to_observe =\n      tmpl::list<MaxOfScalarCompute<hydro::Tags::RestMassDensity<DataVector>>>;\n  using vars_to_interpolate_to_target =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>>;\n  using post_interpolation_callbacks =\n      tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<tags_to_observe,\n                                                              CenterOfStar>>;\n  using compute_target_points =\n      intrp::TargetPoints::SpecifiedPoints<CenterOfStar, 3>;\n  using compute_items_on_target = tags_to_observe;\n\n  template <typename Metavariables>\n  using interpolating_component =\n      typename Metavariables::dg_element_array_component;\n};\n\nstruct KerrHorizon : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  using temporal_id = ::Tags::Time;\n  using tags_to_observe = tmpl::list<ylm::Tags::EuclideanSurfaceIntegralVector<\n      hydro::Tags::MassFlux<DataVector, 3>, ::Frame::Inertial>>;\n  using vars_to_interpolate_to_target =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n  using compute_items_on_target = tmpl::push_front<\n      tags_to_observe,\n      ylm::Tags::EuclideanAreaElementCompute<::Frame::Inertial>,\n      hydro::Tags::MassFluxCompute<DataVector, 3, ::Frame::Inertial>>;\n  using compute_target_points =\n      intrp::TargetPoints::KerrHorizon<KerrHorizon, ::Frame::Inertial>;\n  using post_interpolation_callbacks =\n      tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<tags_to_observe,\n                                                              KerrHorizon>>;\n\n  template <typename Metavariables>\n  using interpolating_component =\n      typename Metavariables::dg_element_array_component;\n};\n"
  },
  {
    "path": "src/Evolution/Executables/NewtonianEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Actions\n  Charmxx::main\n  CoordinateMaps\n  DiscontinuousGalerkin\n  DomainCreators\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  FiniteDifference\n  Hydro\n  Informer\n  LinearOperators\n  MathFunctions\n  NewtonianEuler\n  NewtonianEulerAnalyticData\n  NewtonianEulerSolutions\n  NewtonianEulerSources\n  Observer\n  Options\n  Parallel\n  PhaseControl\n  Serialization\n  Time\n  Utilities\n  )\n\nfunction(add_newtonian_euler_executable DIM)\n  set(EXECUTABLE \"EvolveNewtonianEuler${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    EvolveNewtonianEuler.cpp\n    VolumeTermsInstantiation${DIM}D.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction()\n\nadd_newtonian_euler_executable(1)\nadd_newtonian_euler_executable(2)\nadd_newtonian_euler_executable(3)\n"
  },
  {
    "path": "src/Evolution/Executables/NewtonianEuler/EvolveNewtonianEuler.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/NewtonianEuler/EvolveNewtonianEuler.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<DIM>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &EquationsOfState::register_derived_with_charm,\n       &NewtonianEuler::fd::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/NewtonianEuler/EvolveNewtonianEuler.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/Conservative/UpdateConservatives.hpp\"\n#include \"Evolution/DgSubcell/Actions/Initialize.hpp\"\n#include \"Evolution/DgSubcell/Actions/Labels.hpp\"\n#include \"Evolution/DgSubcell/Actions/ReconstructionCommunication.hpp\"\n#include \"Evolution/DgSubcell/Actions/SelectNumericalMethod.hpp\"\n#include \"Evolution/DgSubcell/Actions/TakeTimeStep.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndRollback.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndSwitchToDg.hpp\"\n#include \"Evolution/DgSubcell/DisableLts.hpp\"\n#include \"Evolution/DgSubcell/GetTciDecision.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp\"\n#include \"Evolution/DgSubcell/NeighborTciDecision.hpp\"\n#include \"Evolution/DgSubcell/PrepareNeighborData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/AllSolutions.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/PrimitiveGhostData.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/PrimsAfterRollback.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/ResizeAndComputePrimitives.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/TciOnDgGrid.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/TciOnFdGrid.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/KhInstability.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/IsentropicVortex.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/RiemannProblem.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\ntemplate <size_t Dim>\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = Dim;\n  // The use_dg_subcell flag controls whether to use unlimited DG (false)\n  // or a DG-FD hybrid scheme (true).\n  static constexpr bool use_dg_subcell = true;\n\n  using system = NewtonianEuler::System<Dim>;\n\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using initial_data_tag = evolution::initial_data::Tags::InitialData;\n  using initial_data_list = NewtonianEuler::InitialData::initial_data_list<Dim>;\n\n  using analytic_variables_tags =\n      typename system::primitive_variables_tag::tags_list;\n\n  using equation_of_state_tag = hydro::Tags::EquationOfState<false, 2>;\n\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_variables_tags, use_dg_subcell, initial_data_list>;\n  using error_compute = Tags::ErrorsCompute<analytic_variables_tags>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_variables_tags>;\n  using observe_fields = tmpl::push_back<\n      tmpl::append<\n          typename system::variables_tag::tags_list,\n          typename system::primitive_variables_tag::tags_list, error_tags,\n          tmpl::conditional_t<use_dg_subcell,\n                              tmpl::list<evolution::dg::subcell::Tags::\n                                             TciStatusCompute<volume_dim>>,\n                              tmpl::list<>>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n              volume_dim, Frame::ElementLogical>,\n          ::Events::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                     Frame::ElementLogical>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                                   Frame::Grid>,\n          domain::Tags::Coordinates<volume_dim, Frame::Grid>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n              volume_dim, Frame::Inertial>,\n          domain::Tags::Coordinates<volume_dim, Frame::Inertial>>>;\n  using non_tensor_compute_tags = tmpl::append<\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Tags::ObserverMeshCompute<volume_dim>,\n              evolution::dg::subcell::Tags::ObserverInverseJacobianCompute<\n                  volume_dim, Frame::ElementLogical, Frame::Inertial>,\n              evolution::dg::subcell::Tags::\n                  ObserverJacobianAndDetInvJacobianCompute<\n                      volume_dim, Frame::ElementLogical, Frame::Inertial>>,\n          tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                     ::Events::Tags::ObserverInverseJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverDetInvJacobianCompute<\n                         Frame::ElementLogical, Frame::Inertial>>>,\n      tmpl::list<analytic_compute, error_compute>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<NewtonianEuler::Sources::Source<Dim>,\n                   NewtonianEuler::Sources::all_sources<Dim>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   NewtonianEuler::InitialData::initial_data_list<Dim>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, non_tensor_compute_tags>,\n                       Events::time_events<system>>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   NewtonianEuler::BoundaryCorrections::\n                       standard_boundary_corrections<volume_dim>>,\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<\n            NewtonianEuler::BoundaryConditions::BoundaryCondition<volume_dim>,\n            NewtonianEuler::BoundaryConditions::standard_boundary_conditions<\n                volume_dim>>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   StepChoosers::standard_step_choosers<system>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::append<StepChoosers::standard_slab_choosers<\n                                    system, local_time_stepping>,\n                                tmpl::conditional_t<\n                                    use_dg_subcell and local_time_stepping,\n                                    tmpl::list<StepChoosers::FixedLtsRatio>,\n                                    tmpl::list<>>>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::flatten<tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::ConservativeSystem<system>,\n\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Actions::SetSubcellGrid<volume_dim,\n                                                              system, false>,\n              Actions::UpdateConservatives,\n              evolution::dg::subcell::Actions::SetAndCommunicateInitialRdmpData<\n                  volume_dim,\n                  NewtonianEuler::subcell::SetInitialRdmpData<volume_dim>>,\n              evolution::dg::subcell::Actions::ComputeAndSendTciOnInitialGrid<\n                  volume_dim, system,\n                  NewtonianEuler::subcell::TciOnFdGrid<volume_dim>>,\n              evolution::dg::subcell::Actions::SetInitialGridFromTciData<\n                  volume_dim, system>,\n              Actions::MutateApply<\n                  NewtonianEuler::subcell::ResizeAndComputePrims<volume_dim>>,\n              Actions::UpdateConservatives>,\n          tmpl::list<evolution::Initialization::Actions::SetVariables<\n                         domain::Tags::Coordinates<Dim, Frame::ElementLogical>>,\n                     Actions::UpdateConservatives>>,\n      Initialization::Actions::AddComputeTags<\n          tmpl::list<NewtonianEuler::Tags::SoundSpeedSquaredCompute<DataVector>,\n                     NewtonianEuler::Tags::SoundSpeedCompute<DataVector>>>,\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<EvolutionMetavars,\n                                                  local_time_stepping>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      tmpl::conditional_t<use_dg_subcell and local_time_stepping,\n                          Initialization::Actions::InitializeItems<\n                              evolution::dg::subcell::DisableLts<Dim>>,\n                          tmpl::list<>>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>>;\n\n  using events_and_dense_triggers_postprocessors = tmpl::list<\n      AlwaysReadyPostprocessor<typename system::primitive_from_conservative>>;\n\n  using dg_step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::push_front<\n                  events_and_dense_triggers_postprocessors,\n                  evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                      EvolutionMetavars>>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n              evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                  volume_dim, use_dg_element_collection>,\n              Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<\n                  events_and_dense_triggers_postprocessors>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          // Note: The primitive variables are computed as part of the TCI.\n          evolution::dg::subcell::Actions::TciAndRollback<\n              NewtonianEuler::subcell::TciOnDgGrid<volume_dim>>,\n          tmpl::list<\n              // Conservative `UpdatePrimitives` expects system to\n              // possess list of recovery schemes so we use\n              // `MutateApply` instead.\n              Actions::MutateApply<\n                  typename system::primitive_from_conservative>>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>>>;\n\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled = use_dg_subcell;\n    static constexpr bool subcell_enabled_at_external_boundary = false;\n\n    // We send `ghost_zone_size` cell-centered grid points for variable\n    // reconstruction, of which we need `ghost_zone_size-1` for reconstruction\n    // to the internal side of the element face, and `ghost_zone_size` for\n    // reconstruction to the external side of the element face.\n    template <typename DbTagsList>\n    static constexpr size_t ghost_zone_size(\n        const db::DataBox<DbTagsList>& box) {\n      return db::get<NewtonianEuler::fd::Tags::Reconstructor<Dim>>(box)\n          .ghost_zone_size();\n    }\n\n    using DgComputeSubcellNeighborPackagedData =\n        NewtonianEuler::subcell::NeighborPackagedData;\n\n    using GhostVariables =\n        NewtonianEuler::subcell::PrimitiveGhostVariables<volume_dim>;\n  };\n\n  using dg_subcell_step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::subcell::Actions::SelectNumericalMethod,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginDg>,\n      dg_step_actions,\n      Actions::Goto<evolution::dg::subcell::Actions::Labels::EndOfSolvers>,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginSubcell>,\n      tmpl::conditional_t<local_time_stepping,\n                          // This is just to adjust for FixedLtsRatio, so we\n                          // can pass an empty list of StepChoosers.\n                          Actions::MutateApply<ChangeStepSize<tmpl::list<>>>,\n                          tmpl::list<>>,\n      evolution::dg::subcell::Actions::SendDataForReconstruction<\n          volume_dim,\n          NewtonianEuler::subcell::PrimitiveGhostVariables<volume_dim>,\n          use_dg_element_collection>,\n      evolution::dg::subcell::Actions::ReceiveDataForReconstruction<volume_dim>,\n      Actions::Label<\n          evolution::dg::subcell::Actions::Labels::BeginSubcellAfterDgRollback>,\n      Actions::MutateApply<\n          NewtonianEuler::subcell::PrimsAfterRollback<volume_dim>>,\n      evolution::dg::subcell::fd::Actions::TakeTimeStep<\n          NewtonianEuler::subcell::TimeDerivative<volume_dim>>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      evolution::Actions::RunEventsAndDenseTriggers<\n          events_and_dense_triggers_postprocessors>,\n      Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      Actions::MutateApply<typename system::primitive_from_conservative>,\n      evolution::dg::subcell::Actions::TciAndSwitchToDg<\n          NewtonianEuler::subcell::TciOnFdGrid<volume_dim>>,\n      Actions::MutateApply<\n          NewtonianEuler::subcell::ResizeAndComputePrims<volume_dim>>,\n\n      Actions::Label<evolution::dg::subcell::Actions::Labels::EndOfSolvers>>>;\n\n  using step_actions =\n      tmpl::conditional_t<use_dg_subcell, dg_subcell_step_actions,\n                          dg_step_actions>;\n\n  using dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::list<observers::Observer<EvolutionMetavars>,\n                 observers::ObserverWriter<EvolutionMetavars>,\n                 dg_element_array>;\n\n  using const_global_cache_tags = tmpl::push_back<\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<NewtonianEuler::fd::Tags::Reconstructor<volume_dim>>,\n          tmpl::list<>>,\n      initial_data_tag, equation_of_state_tag,\n      NewtonianEuler::Tags::SourceTerm<Dim>>;\n\n  static constexpr Options::String help{\n      \"Evolve the Newtonian Euler system in conservative form.\\n\\n\"};\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Register,\n       Parallel::Phase::Evolve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/NewtonianEuler/VolumeTermsInstantiation.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace evolution::dg::Actions::detail {\n#define VOLUME_TERMS_INSTANTIATION(DIM)                                        \\\n  template void volume_terms<::NewtonianEuler::TimeDerivativeTerms<DIM>>(      \\\n      gsl::not_null<Variables<db::wrap_tags_in<                                \\\n          ::Tags::dt,                                                          \\\n          typename ::NewtonianEuler::System<DIM>::variables_tag::tags_list>>*> \\\n          dt_vars_ptr,                                                         \\\n      gsl::not_null<Variables<db::wrap_tags_in<                                \\\n          ::Tags::Flux,                                                        \\\n          typename ::NewtonianEuler::System<DIM>::flux_variables,              \\\n          tmpl::size_t<DIM>, Frame::Inertial>>*>                               \\\n          volume_fluxes,                                                       \\\n      gsl::not_null<Variables<db::wrap_tags_in<                                \\\n          ::Tags::deriv,                                                       \\\n          typename ::NewtonianEuler::System<DIM>::gradient_variables,          \\\n          tmpl::size_t<DIM>, Frame::Inertial>>*>                               \\\n          partial_derivs,                                                      \\\n      gsl::not_null<Variables<typename ::NewtonianEuler::System<               \\\n          DIM>::compute_volume_time_derivative_terms::temporary_tags>*>        \\\n          temporaries,                                                         \\\n      gsl::not_null<Variables<db::wrap_tags_in<                                \\\n          ::Tags::div,                                                         \\\n          db::wrap_tags_in<                                                    \\\n              ::Tags::Flux,                                                    \\\n              typename ::NewtonianEuler::System<DIM>::flux_variables,          \\\n              tmpl::size_t<DIM>, Frame::Inertial>>>*>                          \\\n          div_fluxes,                                                          \\\n      const Variables<                                                         \\\n          typename ::NewtonianEuler::System<DIM>::variables_tag::tags_list>&   \\\n          evolved_vars,                                                        \\\n      const ::dg::Formulation dg_formulation, const Mesh<DIM>& mesh,           \\\n      [[maybe_unused]] const tnsr::I<DataVector, DIM, Frame::Inertial>&        \\\n          inertial_coordinates,                                                \\\n      const InverseJacobian<DataVector, DIM, Frame::ElementLogical,            \\\n                            Frame::Inertial>&                                  \\\n          logical_to_inertial_inverse_jacobian,                                \\\n      [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,   \\\n      const std::optional<tnsr::I<DataVector, DIM, Frame::Inertial>>&          \\\n          mesh_velocity,                                                       \\\n      const std::optional<Scalar<DataVector>>& div_mesh_velocity,              \\\n      const Scalar<DataVector>& mass_density_cons,                             \\\n      const tnsr::I<DataVector, DIM>& momentum_density,                        \\\n      const Scalar<DataVector>& energy_density,                                \\\n      const tnsr::I<DataVector, DIM>& velocity,                                \\\n      const Scalar<DataVector>& pressure,                                      \\\n      const Scalar<DataVector>& specific_internal_energy,                      \\\n      const EquationsOfState::EquationOfState<false, 2>& eos,                  \\\n      const tnsr::I<DataVector, DIM>& coords, const double& time,              \\\n      const ::NewtonianEuler::Sources::Source<DIM>& source);\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Executables/NewtonianEuler/VolumeTermsInstantiation1D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/NewtonianEuler/VolumeTermsInstantiation.tpp\"\n\nnamespace evolution::dg::Actions::detail {\nVOLUME_TERMS_INSTANTIATION(1)\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Executables/NewtonianEuler/VolumeTermsInstantiation2D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/NewtonianEuler/VolumeTermsInstantiation.tpp\"\n\nnamespace evolution::dg::Actions::detail {\nVOLUME_TERMS_INSTANTIATION(2)\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Executables/NewtonianEuler/VolumeTermsInstantiation3D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/NewtonianEuler/VolumeTermsInstantiation.tpp\"\n\nnamespace evolution::dg::Actions::detail {\nVOLUME_TERMS_INSTANTIATION(3)\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Executables/RadiationTransport/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(M1Grey)\nadd_subdirectory(MonteCarlo)\n"
  },
  {
    "path": "src/Evolution/Executables/RadiationTransport/M1Grey/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE EvolveM1Grey)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  EvolveM1Grey.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Actions\n  Charmxx::main\n  CoordinateMaps\n  DiscontinuousGalerkin\n  DomainCreators\n  Events\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  GeneralRelativitySolutions\n  Hydro\n  Imex\n  Informer\n  LinearOperators\n  MathFunctions\n  M1Grey\n  M1GreyAnalyticData\n  M1GreySolutions\n  Observer\n  Options\n  Parallel\n  PhaseControl\n  Serialization\n  Time\n  Utilities\n  )\n\n"
  },
  {
    "path": "src/Evolution/Executables/RadiationTransport/M1Grey/EvolveM1Grey.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/RadiationTransport/M1Grey/EvolveM1Grey.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<EvolutionMetavars>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_factory_classes_with_charm<EvolutionMetavars>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/RadiationTransport/M1Grey/EvolveM1Grey.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <vector>\n\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/DiscontinuousGalerkin/BackgroundGrVars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Imex/Actions/DoImplicitStep.hpp\"\n#include \"Evolution/Imex/Actions/RecordTimeStepperData.hpp\"\n#include \"Evolution/Imex/CleanHistory.hpp\"\n#include \"Evolution/Imex/ImplicitDenseOutput.hpp\"\n#include \"Evolution/Imex/Initialize.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Initialize.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1Closure.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1HydroCoupling.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/ConstantM1.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/ImexTimeStepper.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = 3;\n\n  // Set list of neutrino species to be used by M1 code\n  using neutrino_species = tmpl::list<neutrinos::ElectronNeutrinos<1>>;\n\n  using system = RadiationTransport::M1Grey::System<neutrino_species>;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = ImexTimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using initial_data_list =\n      tmpl::append<RadiationTransport::M1Grey::AnalyticData::all_data,\n                   RadiationTransport::M1Grey::Solutions::all_solutions>;\n\n  using analytic_variables_tags = typename system::variables_tag::tags_list;\n\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_variables_tags, false, initial_data_list>;\n  using error_compute = Tags::ErrorsCompute<analytic_variables_tags>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_variables_tags>;\n  using observe_fields = tmpl::push_back<\n      tmpl::append<typename system::variables_tag::tags_list,\n                   typename system::primitive_variables_tag::tags_list,\n                   error_tags>,\n      domain::Tags::Coordinates<volume_dim, Frame::Grid>,\n      domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n  using non_tensor_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 ::Events::Tags::ObserverDetInvJacobianCompute<\n                     Frame::ElementLogical, Frame::Inertial>,\n                 analytic_compute, error_compute>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, non_tensor_compute_tags>,\n                       Events::time_events<system>>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   RadiationTransport::M1Grey::BoundaryCorrections::\n                       standard_boundary_corrections<neutrino_species>>,\n        tmpl::pair<ImexTimeStepper, TimeSteppers::imex_time_steppers>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<RadiationTransport::M1Grey::BoundaryConditions::\n                       BoundaryCondition<neutrino_species>,\n                   RadiationTransport::M1Grey::BoundaryConditions::\n                       standard_boundary_conditions<neutrino_species>>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   StepChoosers::standard_step_choosers<system, false>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   StepChoosers::standard_slab_choosers<\n                       system, local_time_stepping, false>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>>;\n  };\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>>;\n\n  static_assert(not local_time_stepping);\n  using step_actions = tmpl::flatten<tmpl::list<\n      Actions::MutateApply<\n          evolution::dg::BackgroundGrVars<system, EvolutionMetavars>>,\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      imex::Actions::RecordTimeStepperData<system>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             EvolutionMetavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<\n                  tmpl::list<imex::ImplicitDenseOutput<system>>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      imex::Actions::DoImplicitStep<system>,\n      Actions::MutateApply<CleanHistory<system>>,\n      Actions::MutateApply<imex::CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      Actions::MutateApply<typename RadiationTransport::M1Grey::\n                               ComputeM1Closure<neutrino_species>>>>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::AddSimpleTags<\n          evolution::dg::BackgroundGrVars<system, EvolutionMetavars>>,\n      Initialization::Actions::ConservativeSystem<system>,\n      evolution::Initialization::Actions::SetVariables<\n          domain::Tags::Coordinates<volume_dim, Frame::ElementLogical>>,\n      RadiationTransport::M1Grey::Actions::InitializeM1Tags<system>,\n      Initialization::Actions::InitializeItems<imex::Initialize<system>>,\n      Actions::MutateApply<typename RadiationTransport::M1Grey::\n                               ComputeM1Closure<neutrino_species>>,\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<EvolutionMetavars,\n                                                  local_time_stepping>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n\n  using dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::list<observers::Observer<EvolutionMetavars>,\n                 observers::ObserverWriter<EvolutionMetavars>,\n                 dg_element_array>;\n\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n\n  static constexpr Options::String help{\"Evolve the M1Grey system. \\n\\n\"};\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Register,\n       Parallel::Phase::Evolve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/RadiationTransport/MonteCarlo/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE EvolveMonteCarlo)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  EvolveMonteCarlo.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Actions\n  Charmxx::main\n  CoordinateMaps\n  DataStructures\n  DgSubcell\n  DiscontinuousGalerkin\n  DomainCreators\n  Events\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  GeneralRelativity\n  GeneralRelativitySolutions\n  GrMhdAnalyticData\n  GrMhdSolutions\n  H5\n  Hydro\n  Informer\n  LinearOperators\n  MathFunctions\n  MonteCarlo\n  MonteCarloSolutions\n  Observer\n  Options\n  Parallel\n  PhaseControl\n  Serialization\n  Time\n  Utilities\n  ValenciaDivClean\n  )\n\n"
  },
  {
    "path": "src/Evolution/Executables/RadiationTransport/MonteCarlo/EvolveMonteCarlo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/RadiationTransport/MonteCarlo/EvolveMonteCarlo.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nvoid register_neutrino_tables() {\n  register_classes_with_charm(\n      tmpl::list<Particles::MonteCarlo::NeutrinoInteractionTable<2, 2>,\n                 Particles::MonteCarlo::NeutrinoInteractionTable<2, 3>,\n                 Particles::MonteCarlo::NeutrinoInteractionTable<4, 3>,\n                 Particles::MonteCarlo::NeutrinoInteractionTable<16, 3>>{});\n}\n\nvoid register_mc_options() {\n  register_classes_with_charm(\n      tmpl::list<Particles::MonteCarlo::MonteCarloOptions<2>,\n                 Particles::MonteCarlo::MonteCarloOptions<3>>{});\n}\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<EvolutionMetavars<4, 3>>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &EquationsOfState::register_derived_with_charm,\n       &register_factory_classes_with_charm<EvolutionMetavars<4, 3>>,\n       &register_neutrino_tables, &register_mc_options},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/RadiationTransport/MonteCarlo/EvolveMonteCarlo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <vector>\n\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DgSubcell/Actions/Initialize.hpp\"\n#include \"Evolution/DgSubcell/BackgroundGrVars.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/DiscontinuousGalerkin/BackgroundGrVars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Actions/InitializeMonteCarlo.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Actions/Labels.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Actions/TimeStepActions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Actions/TriggerMonteCarloEvolution.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CellCrossingTime.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunication.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunicationTags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MonteCarloOptions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/NeutrinoMomentsFromMonteCarlo.hpp\"\n#include \"Evolution/Particles/MonteCarlo/SwapGrTags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/System.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/AllSolutions.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveFields.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\n// NEED:\n// Initial data\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = 3;\n\n  using system = Particles::MonteCarlo::System;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = TimeStepper;\n  static constexpr bool use_dg_subcell = true;\n\n  using initial_data_list =\n      RadiationTransport::MonteCarlo::Solutions::all_solutions;\n  using equation_of_state_tag = hydro::Tags::GrmhdEquationOfState;\n\n  struct SubcellOptions {\n    using evolved_vars_tags = typename system::variables_tag::tags_list;\n\n    static constexpr bool subcell_enabled = use_dg_subcell;\n    static constexpr bool subcell_enabled_at_external_boundary = true;\n  };\n\n  using observe_fields = tmpl::push_back<\n      tmpl::append<tmpl::conditional_t<\n          // Use conditional template here in case we ever want\n          // something else than dg_subcell, but only add coordinates\n          // for dg_subcell (MC is not coded for DG)\n          use_dg_subcell,\n          tmpl::list<evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n                         volume_dim, Frame::ElementLogical>,\n                     evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n                         volume_dim, Frame::Grid>,\n                     evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n                         volume_dim, Frame::Inertial>>,\n          tmpl::list<>>>,\n      Particles::MonteCarlo::Tags::InertialFrameEnergyDensity>;\n  using non_tensor_compute_tags = tmpl::conditional_t<\n      use_dg_subcell,\n      tmpl::list<evolution::dg::subcell::Tags::ObserverMeshCompute<volume_dim>,\n                 evolution::dg::subcell::Tags::\n                     ObserverJacobianAndDetInvJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                 evolution::dg::subcell::Tags::ObserverInverseJacobianCompute<\n                     volume_dim, Frame::ElementLogical, Frame::Inertial>>,\n      tmpl::list<>>;\n\n  using analytic_variables_tags = typename system::variables_tag::tags_list;\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_variables_tags, false, initial_data_list>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       ::Events::ObserveNorms<observe_fields,\n                                              non_tensor_compute_tags>,\n                       dg::Events::ObserveFields<volume_dim, observe_fields,\n                                                 non_tensor_compute_tags>>>>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>,\n        tmpl::pair<\n            grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField,\n            grmhd::AnalyticData::InitialMagneticFields::\n                initial_magnetic_fields>,\n        tmpl::pair<PhaseChange,\n                   tmpl::list<PhaseControl::CheckpointAndExitAfterWallclock>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   StepChoosers::standard_slab_choosers<system, false, false>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, Triggers::time_triggers>>;\n  };\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>>,\n      Initialization::Actions::AddSimpleTags<\n          evolution::dg::BackgroundGrVars<system, EvolutionMetavars>>,\n      evolution::dg::subcell::Actions::SetSubcellGrid<volume_dim, system,\n                                                      false>,\n      Initialization::Actions::AddSimpleTags<\n          evolution::dg::subcell::BackgroundGrVars<system, EvolutionMetavars,\n                                                   false>>,\n      Actions::MutateApply<Particles::MonteCarlo::SwapGrTags>,\n      Initialization::Actions::InitializeMCTags<system, EnergyBins,\n                                                NeutrinoSpecies, true>,\n      Initialization::Actions::AddComputeTags<tmpl::list<\n          Particles::MonteCarlo::CellLightCrossingTimeCompute,\n          Particles::MonteCarlo::InertialFrameEnergyDensityCompute,\n          Particles::MonteCarlo::InverseJacobianInertialToFluidCompute,\n          domain::Tags::JacobianCompute<4, Frame::Inertial, Frame::Fluid>>>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n\n  using dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::list<\n                  Actions::MutateApply<AdvanceTime<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  // Monte-Carlo is only triggered at the end of a full time\n                  // step\n                  Particles::MonteCarlo::Actions::TriggerMonteCarloEvolution,\n                  Actions::Label<\n                      Particles::MonteCarlo::Actions::Labels::BeginMonteCarlo>,\n                  Particles::MonteCarlo::Actions::SendDataForMcCommunication<\n                      volume_dim,\n                      // No local time stepping\n                      false, Particles::MonteCarlo::CommunicationStep::PreStep>,\n                  Particles::MonteCarlo::Actions::ReceiveDataForMcCommunication<\n                      volume_dim,\n                      Particles::MonteCarlo::CommunicationStep::PreStep>,\n                  Particles::MonteCarlo::Actions::TakeTimeStep<EnergyBins,\n                                                               NeutrinoSpecies>,\n                  Particles::MonteCarlo::Actions::SendDataForMcCommunication<\n                      volume_dim,\n                      // No local time stepping\n                      false,\n                      Particles::MonteCarlo::CommunicationStep::PostStep>,\n                  Particles::MonteCarlo::Actions::ReceiveDataForMcCommunication<\n                      volume_dim,\n                      Particles::MonteCarlo::CommunicationStep::PostStep>,\n                  Actions::Label<\n                      Particles::MonteCarlo::Actions::Labels::EndMonteCarlo>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::list<observers::Observer<EvolutionMetavars>,\n                 observers::ObserverWriter<EvolutionMetavars>,\n                 dg_element_array>;\n\n  using const_global_cache_tags = tmpl::list<\n      Particles::MonteCarlo::Tags::MonteCarloOptions<NeutrinoSpecies>,\n      equation_of_state_tag, evolution::initial_data::Tags::InitialData,\n      Particles::MonteCarlo::Tags::InteractionRatesTable<EnergyBins,\n                                                         NeutrinoSpecies>>;\n\n  static constexpr Options::String help{\n      \"Evolve Monte Carlo transport (without coupling to hydro).\\n\\n\"};\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Register,\n       Parallel::Phase::Evolve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarAdvection/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Actions\n  Charmxx::main\n  DgSubcell\n  DiscontinuousGalerkin\n  DomainCreators\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  FiniteDifference\n  Informer\n  LinearOperators\n  Observer\n  Options\n  Parallel\n  PhaseControl\n  ScalarAdvection\n  ScalarAdvectionSolutions\n  Serialization\n  Time\n  Utilities\n  )\n\nfunction(add_scalar_advection_executable DIM)\n  set(EXECUTABLE \"EvolveScalarAdvection${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    EvolveScalarAdvection.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction()\n\nadd_scalar_advection_executable(1)\nadd_scalar_advection_executable(2)\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarAdvection/EvolveScalarAdvection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/ScalarAdvection/EvolveScalarAdvection.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<DIM>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &ScalarAdvection::fd::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarAdvection/EvolveScalarAdvection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DgSubcell/Actions/Initialize.hpp\"\n#include \"Evolution/DgSubcell/Actions/Labels.hpp\"\n#include \"Evolution/DgSubcell/Actions/ReconstructionCommunication.hpp\"\n#include \"Evolution/DgSubcell/Actions/SelectNumericalMethod.hpp\"\n#include \"Evolution/DgSubcell/Actions/TakeTimeStep.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndRollback.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndSwitchToDg.hpp\"\n#include \"Evolution/DgSubcell/DisableLts.hpp\"\n#include \"Evolution/DgSubcell/GetTciDecision.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp\"\n#include \"Evolution/DgSubcell/NeighborTciDecision.hpp\"\n#include \"Evolution/DgSubcell/PrepareNeighborData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/GhostData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOnDgGrid.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOnFdGrid.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/VelocityAtFace.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/VelocityField.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Krivodonova.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Kuzmin.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Sinusoid.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\ntemplate <size_t Dim>\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = Dim;\n  using system = ScalarAdvection::System<Dim>;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  // The use_dg_subcell flag controls whether to use unlimited DG (false)\n  // or a DG-FD hybrid scheme (true).\n  static constexpr bool use_dg_subcell = true;\n\n  using initial_data_list =\n      tmpl::conditional_t<Dim == 1,\n                          tmpl::list<ScalarAdvection::Solutions::Krivodonova,\n                                     ScalarAdvection::Solutions::Sinusoid>,\n                          tmpl::list<ScalarAdvection::Solutions::Kuzmin>>;\n\n  using analytic_variables_tags = typename system::variables_tag::tags_list;\n  using analytic_compute = evolution::Tags::AnalyticSolutionsCompute<\n      volume_dim, analytic_variables_tags, use_dg_subcell, initial_data_list>;\n  using error_compute = Tags::ErrorsCompute<analytic_variables_tags>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_variables_tags>;\n  using observe_fields = tmpl::push_back<\n      tmpl::append<\n          typename system::variables_tag::tags_list, error_tags,\n          tmpl::conditional_t<use_dg_subcell,\n                              tmpl::list<evolution::dg::subcell::Tags::\n                                             TciStatusCompute<volume_dim>>,\n                              tmpl::list<>>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n              volume_dim, Frame::ElementLogical>,\n          ::Events::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                     Frame::ElementLogical>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<volume_dim,\n                                                                   Frame::Grid>,\n          domain::Tags::Coordinates<volume_dim, Frame::Grid>>,\n      tmpl::conditional_t<\n          use_dg_subcell,\n          evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n              volume_dim, Frame::Inertial>,\n          domain::Tags::Coordinates<volume_dim, Frame::Inertial>>>;\n  using non_tensor_compute_tags = tmpl::list<\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Tags::ObserverMeshCompute<volume_dim>,\n              evolution::dg::subcell::Tags::ObserverInverseJacobianCompute<\n                  volume_dim, Frame::ElementLogical, Frame::Inertial>,\n              evolution::dg::subcell::Tags::\n                  ObserverJacobianAndDetInvJacobianCompute<\n                      volume_dim, Frame::ElementLogical, Frame::Inertial>>,\n          tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                     ::Events::Tags::ObserverInverseJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverJacobianCompute<\n                         volume_dim, Frame::ElementLogical, Frame::Inertial>,\n                     ::Events::Tags::ObserverDetInvJacobianCompute<\n                         Frame::ElementLogical, Frame::Inertial>>>,\n      tmpl::list<analytic_compute, error_compute>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, non_tensor_compute_tags>,\n                       Events::time_events<system>>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   ScalarAdvection::BoundaryCorrections::\n                       standard_boundary_corrections<Dim>>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>,\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<ScalarAdvection::BoundaryConditions::BoundaryCondition<Dim>,\n                   ScalarAdvection::BoundaryConditions::\n                       standard_boundary_conditions<Dim>>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   StepChoosers::standard_step_choosers<system>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::append<StepChoosers::standard_slab_choosers<\n                                    system, local_time_stepping>,\n                                tmpl::conditional_t<\n                                    use_dg_subcell and local_time_stepping,\n                                    tmpl::list<StepChoosers::FixedLtsRatio>,\n                                    tmpl::list<>>>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled = use_dg_subcell;\n    static constexpr bool subcell_enabled_at_external_boundary = false;\n\n    // We send `ghost_zone_size` cell-centered grid points for variable\n    // reconstruction, of which we need `ghost_zone_size-1` for reconstruction\n    // to the internal side of the element face, and `ghost_zone_size` for\n    // reconstruction to the external side of the element face.\n    template <typename DbTagsList>\n    static constexpr size_t ghost_zone_size(\n        const db::DataBox<DbTagsList>& box) {\n      return db::get<ScalarAdvection::fd::Tags::Reconstructor<volume_dim>>(box)\n          .ghost_zone_size();\n    }\n\n    using DgComputeSubcellNeighborPackagedData =\n        ScalarAdvection::subcell::NeighborPackagedData;\n\n    using GhostVariables = ScalarAdvection::subcell::GhostVariables;\n  };\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>>;\n\n  using dg_step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             EvolutionMetavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      tmpl::conditional_t<use_dg_subcell,\n                          evolution::dg::subcell::Actions::TciAndRollback<\n                              ScalarAdvection::subcell::TciOnDgGrid<Dim>>,\n                          tmpl::list<>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>>>;\n\n  using dg_subcell_step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::subcell::Actions::SelectNumericalMethod,\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginDg>,\n      dg_step_actions,\n      Actions::Goto<evolution::dg::subcell::Actions::Labels::EndOfSolvers>,\n      Actions::Label<evolution::dg::subcell::Actions::Labels::BeginSubcell>,\n      tmpl::conditional_t<local_time_stepping,\n                          // This is just to adjust for FixedLtsRatio, so we\n                          // can pass an empty list of StepChoosers.\n                          Actions::MutateApply<ChangeStepSize<tmpl::list<>>>,\n                          tmpl::list<>>,\n      evolution::dg::subcell::Actions::SendDataForReconstruction<\n          volume_dim, ScalarAdvection::subcell::GhostVariables,\n          use_dg_element_collection>,\n      evolution::dg::subcell::Actions::ReceiveDataForReconstruction<volume_dim>,\n      Actions::Label<\n          evolution::dg::subcell::Actions::Labels::BeginSubcellAfterDgRollback>,\n      evolution::dg::subcell::fd::Actions::TakeTimeStep<\n          ScalarAdvection::subcell::TimeDerivative<volume_dim>>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      evolution::dg::subcell::Actions::TciAndSwitchToDg<\n          ScalarAdvection::subcell::TciOnFdGrid<volume_dim>>,\n      Actions::Label<evolution::dg::subcell::Actions::Labels::EndOfSolvers>>>;\n\n  using step_actions =\n      tmpl::conditional_t<use_dg_subcell, dg_subcell_step_actions,\n                          dg_step_actions>;\n\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData,\n                 tmpl::conditional_t<\n                     use_dg_subcell,\n                     tmpl::list<ScalarAdvection::fd::Tags::Reconstructor<Dim>,\n                                ScalarAdvection::subcell::Tags::TciOptions>,\n                     tmpl::list<>>>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::ConservativeSystem<system>,\n\n      tmpl::conditional_t<\n          use_dg_subcell,\n          tmpl::list<\n              evolution::dg::subcell::Actions::SetSubcellGrid<volume_dim,\n                                                              system, false>,\n              Initialization::Actions::AddSimpleTags<\n                  ScalarAdvection::subcell::VelocityAtFace<volume_dim>>,\n              evolution::dg::subcell::Actions::SetAndCommunicateInitialRdmpData<\n                  volume_dim,\n                  ScalarAdvection::subcell::SetInitialRdmpData<volume_dim>>,\n              evolution::dg::subcell::Actions::ComputeAndSendTciOnInitialGrid<\n                  volume_dim, system,\n                  ScalarAdvection::subcell::TciOnFdGrid<volume_dim>>,\n              evolution::dg::subcell::Actions::SetInitialGridFromTciData<\n                  volume_dim, system>>,\n          tmpl::list<evolution::Initialization::Actions::SetVariables<\n              domain::Tags::Coordinates<Dim, Frame::ElementLogical>>>>,\n\n      Initialization::Actions::AddComputeTags<\n          tmpl::list<ScalarAdvection::Tags::VelocityFieldCompute<Dim>>>,\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<EvolutionMetavars,\n                                                  local_time_stepping>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      tmpl::conditional_t<use_dg_subcell and local_time_stepping,\n                          Initialization::Actions::InitializeItems<\n                              evolution::dg::subcell::DisableLts<Dim>>,\n                          tmpl::list<>>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n\n  using dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::list<observers::Observer<EvolutionMetavars>,\n                 observers::ObserverWriter<EvolutionMetavars>,\n                 dg_element_array>;\n\n  static constexpr Options::String help{\n      \"Evolve the scalar advection equation.\\n\\n\"};\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization,\n       Parallel::Phase::InitializeTimeStepperHistory, Parallel::Phase::Register,\n       Parallel::Phase::Evolve, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarTensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Actions\n  ApparentHorizonFinder\n  ApparentHorizonFinderCriteria\n  Charmxx::main\n  ControlSystem\n  CoordinateMaps\n  CurvedScalarWave\n  DiscontinuousGalerkin\n  Domain\n  DomainCreators\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  GeneralRelativity\n  GeneralRelativityAnalyticData\n  GeneralRelativitySolutions\n  GeneralizedHarmonic\n  GhScalarTensorAnalyticData\n  Informer\n  Interpolation\n  LinearOperators\n  MathFunctions\n  Observer\n  Options\n  Parallel\n  ParallelInterpolation\n  PhaseControl\n  ScalarGaussBonnetPointwise\n  ScalarTensor\n  ScalarTensorAnalyticData\n  ScalarTensorPointwise\n  Serialization\n  Time\n  Utilities\n  )\n\nset(SINGLE_BH_EXECUTABLE EvolveScalarTensorSingleBlackHole)\nadd_spectre_executable(\n  ${SINGLE_BH_EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  EvolveScalarTensorSingleBlackHole.cpp\n  )\ntarget_link_libraries(\n  ${SINGLE_BH_EXECUTABLE}\n  PRIVATE\n  ${LIBS_TO_LINK}\n  Cce\n  )\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarTensor/EvolveScalarTensorSingleBlackHole.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/ScalarTensor/EvolveScalarTensorSingleBlackHole.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<EvolutionMetavars>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &ConstraintDamping::register_derived_with_charm,\n       &register_factory_classes_with_charm<EvolutionMetavars>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarTensor/EvolveScalarTensorSingleBlackHole.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <vector>\n\n#include \"ControlSystem/Actions/InitializeMeasurements.hpp\"\n#include \"ControlSystem/CleanFunctionsOfTime.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Factory.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"ControlSystem/Measurements/SingleHorizon.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Systems/Shape.hpp\"\n#include \"ControlSystem/Systems/Size.hpp\"\n#include \"ControlSystem/Systems/Translation.hpp\"\n#include \"ControlSystem/Trigger.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/Executables/ScalarTensor/ScalarTensorBase.hpp\"\n#include \"Evolution/Systems/Cce/Callbacks/DumpBondiSachsOnWorldtube.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Actions/SetInitialData.hpp\"\n#include \"Options/FactoryHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveCenters.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveFieldsOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveTimeSeriesOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Actions/RunEventsOnFailure.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveSurfaceData.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.tpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nstruct EvolutionMetavars : public ScalarTensorTemplateBase<EvolutionMetavars> {\n  using st_base = ScalarTensorTemplateBase<EvolutionMetavars>;\n  using typename st_base::initialize_initial_data_dependent_quantities_actions;\n  using typename st_base::system;\n\n  static constexpr size_t volume_dim = 3_st;\n\n  static constexpr Options::String help{\n      \"Evolve the Einstein field equations in GH gauge coupled to a scalar \"\n      \"field \\n\"\n      \"on a domain with a single horizon and corresponding excised region\"};\n\n  struct ApparentHorizon : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n    using time_tag = ah::Tags::ObservationTime<0>;\n\n    using frame = ::Frame::Distorted;\n\n    using horizon_find_callbacks = tmpl::list<\n        ah::callbacks::ObserveTimeSeriesOnHorizon<\n            ::ah::tags_for_observing<Frame::Distorted>, ApparentHorizon>,\n        ah::callbacks::ObserveFieldsOnHorizon<::ah::surface_tags_for_observing,\n                                              ApparentHorizon>>;\n    using horizon_find_failure_callbacks =\n        tmpl::list<ah::callbacks::FailedHorizonFind<ApparentHorizon, false>>;\n\n    using compute_tags_on_element =\n        tmpl::list<ah::Tags::ObservationTimeCompute<0>>;\n\n    static constexpr ah::Destination destination = ah::Destination::Observation;\n\n    static std::string name() { return \"ApparentHorizon\"; }\n  };\n\n  struct ExcisionBoundaryA\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using tags_to_observe =\n        tmpl::list<gr::Tags::Lapse<DataVector>,\n                   gr::Tags::Shift<DataVector, 3, Frame::Grid>>;\n    using compute_vars_to_interpolate =\n        intrp::ComputeExcisionBoundaryVolumeQuantities;\n    using vars_to_interpolate_to_target = tags_to_observe;\n    using compute_items_on_source = tmpl::list<>;\n    using compute_items_on_target = tmpl::list<>;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<ExcisionBoundaryA, ::Frame::Grid>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveSurfaceData<\n            tags_to_observe, ExcisionBoundaryA, ::Frame::Grid>>;\n    // run_callbacks\n    template <typename metavariables>\n    using interpolating_component = typename metavariables::st_dg_element_array;\n  };\n\n  struct SphericalSurface\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n\n    using vars_to_interpolate_to_target =\n        detail::ObserverTags::scalar_charge_vars_to_interpolate_to_target;\n    using compute_items_on_target =\n        detail::ObserverTags::scalar_charge_compute_items_on_target;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<SphericalSurface, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            detail::ObserverTags::scalar_charge_surface_obs_tags,\n            SphericalSurface>>;\n    template <typename metavariables>\n    using interpolating_component = typename metavariables::st_dg_element_array;\n  };\n\n  using control_systems =\n      tmpl::list<control_system::Systems::Shape<\n                     ::domain::ObjectLabel::None, 2,\n                     control_system::measurements::SingleHorizon<\n                         ::domain::ObjectLabel::None>>,\n                 control_system::Systems::Translation<\n                     2,\n                     control_system::measurements::SingleHorizon<\n                         ::domain::ObjectLabel::None>,\n                     1>,\n                 control_system::Systems::Size<::domain::ObjectLabel::None, 2>>;\n\n  static constexpr bool use_control_systems =\n      tmpl::size<control_systems>::value > 0;\n\n  struct BondiSachs;\n\n  using interpolation_target_tags = tmpl::push_back<\n      control_system::metafunctions::interpolation_target_tags<control_systems>,\n      ExcisionBoundaryA, SphericalSurface, BondiSachs>;\n\n  using scalar_charge_interpolator_source_vars =\n      detail::ObserverTags::scalar_charge_vars_to_interpolate_to_target;\n  using source_vars_no_deriv = tmpl::list<\n      gr::Tags::SpacetimeMetric<DataVector, volume_dim>,\n      gh::Tags::Pi<DataVector, volume_dim>,\n      gh::Tags::Phi<DataVector, volume_dim>, CurvedScalarWave::Tags::Psi,\n      CurvedScalarWave::Tags::Pi, CurvedScalarWave::Tags::Phi<volume_dim>,\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, volume_dim>>;\n\n  struct BondiSachs : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    static std::string name() { return \"BondiSachsInterpolation\"; }\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target = source_vars_no_deriv;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<BondiSachs, ::Frame::Inertial>;\n    using post_interpolation_callbacks = tmpl::list<\n        intrp::callbacks::DumpBondiSachsOnWorldtube<BondiSachs, true>>;\n    using compute_items_on_target = tmpl::list<>;\n    template <typename Metavariables>\n    using interpolating_component = typename Metavariables::st_dg_element_array;\n  };\n  // The interpolator_source_vars need to be the same in both the\n  // Interpolate event and the InterpolateWithoutInterpComponent event.  The\n  // Interpolate event interpolates to the horizon, and the\n  // InterpolateWithoutInterpComponent event interpolates to the excision\n  // boundary. Every Target gets the same interpolator_source_vars, so they need\n  // to be made the same. Otherwise a static assert is triggered.\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = Options::add_factory_classes<\n        // Restrict to monotonic time steppers in LTS to avoid control\n        // systems deadlocking.\n        tmpl::insert<\n            tmpl::erase<typename st_base::factory_creation::factory_classes,\n                        LtsTimeStepper>,\n            tmpl::pair<LtsTimeStepper,\n                       TimeSteppers::monotonic_lts_time_steppers>>,\n        tmpl::pair<ah::Criterion, ah::Criteria::standard_criteria>,\n        tmpl::pair<\n            Event,\n            tmpl::flatten<tmpl::list<\n                ah::Events::FindApparentHorizon<ApparentHorizon>,\n                intrp::Events::InterpolateWithoutInterpComponent<\n                    3, BondiSachs, source_vars_no_deriv>,\n                control_system::metafunctions::control_system_events<\n                    control_systems>,\n                control_system::CleanFunctionsOfTime,\n                intrp::Events::InterpolateWithoutInterpComponent<\n                    volume_dim, ExcisionBoundaryA, ah::source_vars<volume_dim>>,\n                intrp::Events::InterpolateWithoutInterpComponent<\n                    volume_dim, SphericalSurface,\n                    scalar_charge_interpolator_source_vars>>>>,\n        tmpl::pair<DenseTrigger,\n                   control_system::control_system_triggers<control_systems>>,\n        tmpl::pair<control_system::size::State,\n                   control_system::size::States::factory_creatable_states>>;\n  };\n\n  using typename st_base::const_global_cache_tags;\n\n  using observed_reduction_data_tags = observers::collect_reduction_data_tags<\n      tmpl::at<typename factory_creation::factory_classes, Event>>;\n\n  using dg_registration_list = typename st_base::dg_registration_list;\n\n  using step_actions = typename st_base::template step_actions<control_systems>;\n\n  using initialization_actions = tmpl::push_back<\n      tmpl::pop_back<typename st_base::template initialization_actions<\n          use_control_systems>>,\n      control_system::Actions::InitializeMeasurements<control_systems>,\n      intrp::Actions::ElementInitInterpPoints<volume_dim,\n                                              interpolation_target_tags>,\n      tmpl::back<typename st_base::template initialization_actions<\n          use_control_systems>>>;\n\n  using st_dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::flatten<tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::RegisterWithElementDataReader,\n              tmpl::list<importers::Actions::RegisterWithElementDataReader,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::ImportInitialData,\n              tmpl::list<ScalarTensor::Actions::SetInitialData,\n                         ScalarTensor::Actions::ReceiveNumericInitialData,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeInitialDataDependentQuantities,\n              initialize_initial_data_dependent_quantities_actions>,\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  ::domain::Actions::CheckFunctionsOfTimeAreReady<volume_dim>,\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::PostFailureCleanup,\n              tmpl::list<Actions::RunEventsOnFailure<::Tags::Time>,\n                         Parallel::Actions::TerminatePhase>>>>>;\n\n  // ControlSystem/Measurements/CharSpeed.hpp assumes gh_dg_element_array\n  using gh_dg_element_array = st_dg_element_array;\n\n  template <typename ParallelComponent>\n  struct registration_list {\n    using type = std::conditional_t<\n        std::is_same_v<ParallelComponent, st_dg_element_array>,\n        dg_registration_list, tmpl::list<>>;\n  };\n\n  using control_system_horizon_metavars =\n      control_system::metafunctions::horizon_metavars<control_systems>;\n\n  using component_list = tmpl::flatten<tmpl::list<\n      observers::Observer<EvolutionMetavars>,\n      observers::ObserverWriter<EvolutionMetavars>,\n      mem_monitor::MemoryMonitor<EvolutionMetavars>,\n      importers::ElementDataReader<EvolutionMetavars>, st_dg_element_array,\n      ah::Component<EvolutionMetavars, ApparentHorizon>,\n      tmpl::transform<\n          control_system_horizon_metavars,\n          tmpl::bind<ah::Component, tmpl::pin<EvolutionMetavars>, tmpl::_1>>,\n      control_system::control_components<EvolutionMetavars, control_systems>,\n      tmpl::transform<interpolation_target_tags,\n                      tmpl::bind<intrp::InterpolationTarget,\n                                 tmpl::pin<EvolutionMetavars>, tmpl::_1>>>>;\n};\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarTensor/ScalarTensorBase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n#include \"ControlSystem/Actions/LimitTimeStep.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsCharacteristicSpeeds.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Initialize.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/PsiSquared.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Equations.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Initialize.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Constraints.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Initialize.hpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"IO/Importers/Actions/RegisterWithElementDataReader.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ContributeMemoryData.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/RandomizeVariables.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/MonitorMemory.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GhScalarTensor/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4Real.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarCharge.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/Tags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarSource.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/SourceTags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/StressEnergy.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\ntemplate <typename EvolutionMetavarsDerived>\nstruct ScalarTensorTemplateBase;\n\nnamespace detail {\nconstexpr auto make_default_phase_order() {\n  return std::array{Parallel::Phase::Initialization,\n                    Parallel::Phase::RegisterWithElementDataReader,\n                    Parallel::Phase::ImportInitialData,\n                    Parallel::Phase::InitializeInitialDataDependentQuantities,\n                    Parallel::Phase::Register,\n                    Parallel::Phase::InitializeTimeStepperHistory,\n                    Parallel::Phase::Evolve,\n                    Parallel::Phase::Exit};\n}\n\nstruct ObserverTags {\n  static constexpr size_t volume_dim = 3_st;\n  static constexpr bool backreaction_is_enabled = false;\n\n  using system = ScalarTensor::System;\n\n  using variables_tag = typename system::variables_tag;\n\n  using initial_data_list = gh::ScalarTensor::AnalyticData::all_analytic_data;\n\n  using deriv_compute = ::Tags::DerivCompute<\n      variables_tag, domain::Tags::Mesh<volume_dim>,\n      domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      typename system::gradient_variables>;\n\n  using observe_fields = tmpl::append<\n      tmpl::push_back<\n          system::gh_system::variables_tag::tags_list,\n          ScalarTensor::Tags::CswCompute<CurvedScalarWave::Tags::Psi>,\n          ScalarTensor::Tags::CswCompute<CurvedScalarWave::Tags::Pi>,\n          ScalarTensor::Tags::CswCompute<\n              CurvedScalarWave::Tags::Phi<volume_dim>>,\n          gh::Tags::GaugeH<DataVector, volume_dim, Frame::Inertial>,\n          gh::Tags::SpacetimeDerivGaugeH<DataVector, volume_dim,\n                                         Frame::Inertial>,\n          // 3 plus 1 Tags and derivatives\n          gr::Tags::SpatialMetric<DataVector, volume_dim, Frame::Inertial>,\n          gr::Tags::DetSpatialMetric<DataVector>,\n          gr::Tags::InverseSpatialMetric<DataVector, volume_dim,\n                                         Frame::Inertial>,\n          gr::Tags::Shift<DataVector, volume_dim, Frame::Inertial>,\n          gr::Tags::Lapse<DataVector>,\n          gr::Tags::SqrtDetSpatialMetric<DataVector>,\n          gr::Tags::SpacetimeNormalOneFormCompute<DataVector, volume_dim,\n                                                  Frame::Inertial>,\n          gr::Tags::SpacetimeNormalVector<DataVector, volume_dim,\n                                          Frame::Inertial>,\n          gr::Tags::InverseSpacetimeMetric<DataVector, volume_dim,\n                                           Frame::Inertial>,\n          ::Tags::deriv<\n              gr::Tags::SpatialMetric<DataVector, volume_dim, Frame::Inertial>,\n              tmpl::size_t<volume_dim>, Frame::Inertial>,\n          gr::Tags::SpatialChristoffelFirstKind<DataVector, volume_dim,\n                                                Frame::Inertial>,\n          gr::Tags::SpatialChristoffelSecondKind<DataVector, volume_dim,\n                                                 Frame::Inertial>,\n          // 3 plus 1 variables used by CSW\n          gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, volume_dim,\n                                                      Frame::Inertial>,\n          gr::Tags::ExtrinsicCurvature<DataVector, volume_dim, Frame::Inertial>,\n          gr::Tags::TraceExtrinsicCurvature<DataVector>,\n          // More 3 plus 1 variables\n          ::Tags::deriv<gr::Tags::SpatialChristoffelSecondKind<\n                            DataVector, volume_dim, Frame::Inertial>,\n                        tmpl::size_t<volume_dim>, Frame::Inertial>,\n          gr::Tags::SpatialRicci<DataVector, volume_dim, Frame::Inertial>,\n          gr::Tags::SpatialRicciScalar<DataVector>,\n          // Compute the constraints of GH\n          gh::Tags::GaugeConstraintCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::TwoIndexConstraintCompute<volume_dim, Frame::Inertial>,\n          gh::Tags::ThreeIndexConstraintCompute<volume_dim, Frame::Inertial>,\n          // Compute the constraints of CSW\n          ScalarTensor::Tags::CswOneIndexConstraintCompute<volume_dim>,\n          ScalarTensor::Tags::CswTwoIndexConstraintCompute<volume_dim>,\n          // GH constraint norms\n          ::Tags::PointwiseL2NormCompute<gh::Tags::GaugeConstraint<\n              DataVector, volume_dim, Frame::Inertial>>,\n          ::Tags::PointwiseL2NormCompute<gh::Tags::TwoIndexConstraint<\n              DataVector, volume_dim, Frame::Inertial>>,\n          ::Tags::PointwiseL2NormCompute<gh::Tags::ThreeIndexConstraint<\n              DataVector, volume_dim, Frame::Inertial>>,\n          // CSW constraint norms\n          ::Tags::PointwiseL2NormCompute<ScalarTensor::Tags::Csw<\n              CurvedScalarWave::Tags::OneIndexConstraint<volume_dim>>>,\n          ::Tags::PointwiseL2NormCompute<ScalarTensor::Tags::Csw<\n              CurvedScalarWave::Tags::TwoIndexConstraint<volume_dim>>>,\n          // Damping parameters\n          gh::Tags::ConstraintGamma0, gh::Tags::ConstraintGamma1,\n          gh::Tags::ConstraintGamma2,\n          ScalarTensor::Tags::CswCompute<\n              CurvedScalarWave::Tags::ConstraintGamma1>,\n          ScalarTensor::Tags::CswCompute<\n              CurvedScalarWave::Tags::ConstraintGamma2>,\n          // Sources\n          ScalarTensor::Tags::TraceReversedStressEnergyCompute,\n          ScalarTensor::Tags::ScalarSource,\n\n          ::domain::Tags::Coordinates<volume_dim, Frame::Grid>,\n          ::domain::Tags::Coordinates<volume_dim, Frame::Inertial>>,\n      // The 4-index constraint is only implemented in 3d\n      tmpl::list<\n          tmpl::conditional_t<\n              backreaction_is_enabled,\n              ScalarTensor::Tags::FConstraintCompute<\n                  volume_dim, Frame::Inertial>,\n              gh::Tags::FConstraintCompute<volume_dim, Frame::Inertial>>,\n          gh::Tags::FourIndexConstraintCompute<volume_dim, Frame::Inertial>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::FConstraint<DataVector, volume_dim>>,\n          ::Tags::PointwiseL2NormCompute<\n              gh::Tags::FourIndexConstraint<DataVector, volume_dim>>,\n          gh::Tags::ConstraintEnergyCompute<volume_dim, Frame::Inertial>,\n          ::Tags::deriv<gr::Tags::ExtrinsicCurvature<DataVector, volume_dim,\n                                                     Frame::Inertial>,\n                        tmpl::size_t<volume_dim>, Frame::Inertial>,\n          gr::Tags::CovariantDerivativeOfExtrinsicCurvature<\n              DataVector, volume_dim, Frame::Inertial>,\n          gr::Tags::WeylElectric<DataVector, volume_dim, Frame::Inertial>,\n          gr::Tags::WeylElectricScalar<DataVector>,\n          gr::Tags::WeylMagneticScalar<DataVector>,\n          gr::Tags::Psi4RealCompute<Frame::Inertial>>>;\n  using non_tensor_compute_tags = tmpl::list<\n      ::Events::Tags::ObserverMeshCompute<volume_dim>,\n      ::Events::Tags::ObserverCoordinatesCompute<volume_dim, Frame::Inertial>,\n      ::Events::Tags::ObserverInverseJacobianCompute<\n          volume_dim, Frame::ElementLogical, Frame::Inertial>,\n      ::Events::Tags::ObserverJacobianCompute<volume_dim, Frame::ElementLogical,\n                                              Frame::Inertial>,\n      ::Events::Tags::ObserverDetInvJacobianCompute<Frame::ElementLogical,\n                                                    Frame::Inertial>,\n      ::Events::Tags::ObserverMeshVelocityCompute<volume_dim, Frame::Inertial>,\n      gh::gauges::Tags::GaugeAndDerivativeCompute<\n          volume_dim, gh::ScalarTensor::AnalyticData::all_analytic_data>>;\n\n  using field_observations =\n      dg::Events::field_observations<volume_dim, observe_fields,\n                                     non_tensor_compute_tags>;\n\n  // We collect here all the tags needed for interpolation in all surfaces\n  using scalar_charge_vars_to_interpolate_to_target = tmpl::list<\n      gr::Tags::SpatialMetric<DataVector, volume_dim, Frame::Inertial>,\n      gr::Tags::InverseSpatialMetric<DataVector, volume_dim, Frame::Inertial>,\n      CurvedScalarWave::Tags::Phi<volume_dim>, CurvedScalarWave::Tags::Psi>;\n\n  using scalar_charge_compute_items_on_target = tmpl::list<\n      ylm::Tags::ThetaPhiCompute<::Frame::Inertial>,\n      ylm::Tags::RadiusCompute<::Frame::Inertial>,\n      ylm::Tags::RhatCompute<::Frame::Inertial>,\n      ylm::Tags::InvJacobianCompute<::Frame::Inertial>,\n      ylm::Tags::JacobianCompute<::Frame::Inertial>,\n      ylm::Tags::DxRadiusCompute<::Frame::Inertial>,\n      ylm::Tags::NormalOneFormCompute<::Frame::Inertial>,\n      ylm::Tags::OneOverOneFormMagnitudeCompute<DataVector, volume_dim,\n                                                ::Frame::Inertial>,\n      ylm::Tags::UnitNormalOneFormCompute<::Frame::Inertial>,\n      ylm::Tags::UnitNormalVectorCompute<::Frame::Inertial>,\n      gr::surfaces::Tags::AreaElementCompute<::Frame::Inertial>,\n      ScalarTensor::StrahlkorperScalar::Tags::ScalarChargeIntegrandCompute,\n      gr::surfaces::Tags::SurfaceIntegralCompute<\n          ScalarTensor::StrahlkorperScalar::Tags::ScalarChargeIntegrand,\n          ::Frame::Inertial>,\n      gr::surfaces::Tags::SurfaceIntegralCompute<CurvedScalarWave::Tags::Psi,\n                                                 ::Frame::Inertial>,\n      CurvedScalarWave::Tags::PsiSquaredCompute,\n      gr::surfaces::Tags::SurfaceIntegralCompute<\n          CurvedScalarWave::Tags::PsiSquared, ::Frame::Inertial>>;\n\n  using scalar_charge_surface_obs_tags = tmpl::list<\n      gr::surfaces::Tags::SurfaceIntegral<\n          ScalarTensor::StrahlkorperScalar::Tags::ScalarChargeIntegrand,\n          ::Frame::Inertial>,\n      gr::surfaces::Tags::SurfaceIntegral<CurvedScalarWave::Tags::Psi,\n                                          ::Frame::Inertial>,\n      gr::surfaces::Tags::SurfaceIntegral<CurvedScalarWave::Tags::PsiSquared,\n                                          ::Frame::Inertial>>;\n};\n\ntemplate <bool LocalTimeStepping>\nstruct FactoryCreation : tt::ConformsTo<Options::protocols::FactoryCreation> {\n  static constexpr size_t volume_dim = 3_st;\n\n  using system = ScalarTensor::System;\n\n  using initial_data_list = gh::ScalarTensor::AnalyticData::all_analytic_data;\n  using factory_classes = tmpl::map<\n      tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n      tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n      tmpl::pair<Event,\n                 tmpl::flatten<tmpl::list<\n                     Events::Completion, Events::MonitorMemory<volume_dim>,\n                     typename detail::ObserverTags::field_observations,\n                     Events::time_events<system>,\n                     dg::Events::ObserveTimeStepVolume<system>>>>,\n      tmpl::pair<\n          evolution::BoundaryCorrection,\n          ScalarTensor::BoundaryCorrections::standard_boundary_corrections>,\n      tmpl::pair<\n          ScalarTensor::BoundaryConditions::BoundaryCondition,\n          ScalarTensor::BoundaryConditions::standard_boundary_conditions>,\n      tmpl::pair<gh::gauges::GaugeCondition, gh::gauges::all_gauges>,\n      tmpl::pair<MathFunction<1, Frame::Inertial>, tmpl::list<>>,\n      tmpl::pair<\n          evolution::initial_data::InitialData,\n          tmpl::push_back<initial_data_list, ScalarTensor::NumericInitialData>>,\n      tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n      tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n      tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                 StepChoosers::standard_step_choosers<system>>,\n      tmpl::pair<\n          StepChooser<StepChooserUse::Slab>,\n          StepChoosers::standard_slab_choosers<system, LocalTimeStepping>>,\n      tmpl::pair<TimeSequence<double>,\n                 TimeSequences::all_time_sequences<double>>,\n      tmpl::pair<TimeSequence<std::uint64_t>,\n                 TimeSequences::all_time_sequences<std::uint64_t>>,\n      tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n      tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                       Triggers::time_triggers>>>;\n};\n}  // namespace detail\n\ntemplate <class EvolutionMetavarsDerived>\nstruct ScalarTensorTemplateBase {\n  using derived_metavars = EvolutionMetavarsDerived;\n\n  static constexpr size_t volume_dim = 3_st;\n  using system = ScalarTensor::System;\n  using TimeStepperBase = LtsTimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  using factory_creation = detail::FactoryCreation<local_time_stepping>;\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::push_back<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>;\n\n  using initialize_initial_data_dependent_quantities_actions = tmpl::list<\n      ScalarTensor::Actions::InitializeGhAnd3Plus1Variables,\n      gh::gauges::SetPiAndPhiFromConstraints<\n          gh::ScalarTensor::AnalyticData::all_analytic_data, volume_dim>,\n      Parallel::Actions::TerminatePhase>;\n\n  // A tmpl::list of tags to be added to the GlobalCache by the\n  // metavariables\n  using const_global_cache_tags = tmpl::list<\n      gh::gauges::Tags::GaugeCondition,\n      evolution::initial_data::Tags::InitialData,\n      gh::Tags::DampingFunctionGamma0<volume_dim, Frame::Grid>,\n      gh::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n      gh::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>,\n      ScalarTensor::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n      ScalarTensor::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>,\n      // Source parameters\n      ScalarTensor::Tags::RampUpParameters,\n      ScalarTensor::Tags::CouplingParameters, ScalarTensor::Tags::ScalarMass>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  static constexpr auto default_phase_order =\n      detail::make_default_phase_order();\n\n  template <typename ControlSystems>\n  using step_actions = tmpl::list<\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         ::domain::CheckFunctionsOfTimeAreReadyPostprocessor<\n                             volume_dim>,\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             derived_metavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n              control_system::Actions::LimitTimeStep<ControlSystems>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      // We allow for separate filtering of the system variables\n      dg::Actions::Filter<Filters::Exponential<0>,\n                          system::gh_system::variables_tag::tags_list>,\n      dg::Actions::Filter<Filters::Exponential<1>,\n                          system::scalar_system::variables_tag::tags_list>>;\n\n  template <bool UseControlSystems>\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<derived_metavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<derived_metavars,\n                                                UseControlSystems>,\n          Initialization::TimeStepperHistory<derived_metavars>>,\n      Initialization::Actions::NonconservativeSystem<system>,\n      Initialization::Actions::AddComputeTags<::Tags::DerivCompute<\n          typename system::variables_tag, domain::Tags::Mesh<volume_dim>,\n          domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                        Frame::Inertial>,\n          typename system::gradient_variables>>,\n      Initialization::Actions::AddComputeTags<\n          tmpl::push_back<StepChoosers::step_chooser_compute_tags<\n              ScalarTensorTemplateBase, local_time_stepping>>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n};\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarWave/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Actions\n  AmrEvents\n  Charmxx::main\n  CoordinateMaps\n  DiscontinuousGalerkin\n  DomainCreators\n  Events\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Evolution\n  Informer\n  InitialDataUtilities\n  LinearOperators\n  MathFunctions\n  ScalarWave\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  PhaseControl\n  Serialization\n  Time\n  Utilities\n  WaveEquationSolutions\n  )\n\nfunction(add_scalar_wave_executable DIM)\n  set(EXECUTABLE \"EvolveScalarWave${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    EvolveScalarWave.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction(add_scalar_wave_executable)\n\nadd_scalar_wave_executable(1)\nadd_scalar_wave_executable(2)\nadd_scalar_wave_executable(3)\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarWave/EvolveScalarWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Executables/ScalarWave/EvolveScalarWave.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Chosen in CMakeLists.txt\nusing metavariables = EvolutionMetavars<DIM>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>,\n       &amr::register_callbacks<metavariables,\n                                typename metavariables::dg_element_array>},\n      {});\n}\n"
  },
  {
    "path": "src/Evolution/Executables/ScalarWave/EvolveScalarWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ScalarWave/EnergyDensity.hpp\"\n#include \"Evolution/Systems/ScalarWave/Equations.hpp\"\n#include \"Evolution/Systems/ScalarWave/Initialize.hpp\"\n#include \"Evolution/Systems/ScalarWave/MomentumDensity.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayCollection/DgElementCollection.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrCriteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrStats.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/RefineMesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Tensors.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Variables.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/Factory.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/NumericData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/StepChoosers/ByBlock.hpp\"\n#include \"Time/StepChoosers/Factory.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/TimeTriggers.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\n\nstruct Inertial;\n}  // namespace Frame\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\ntemplate <size_t Dim>\nstruct EvolutionMetavars {\n  static constexpr size_t volume_dim = Dim;\n\n  using initial_data_list = ScalarWave::Solutions::all_solutions<Dim>;\n\n  using system = ScalarWave::System<Dim>;\n  using temporal_id = Tags::TimeStepId;\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n  static constexpr bool use_dg_element_collection = false;\n\n  using analytic_solution_fields = typename system::variables_tag::tags_list;\n  using deriv_compute = ::Tags::DerivCompute<\n      typename system::variables_tag, domain::Tags::Mesh<volume_dim>,\n      domain::Tags::InverseJacobian<volume_dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      typename system::gradient_variables,\n      domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n  using analytic_compute =\n      evolution::Tags::AnalyticSolutionsCompute<Dim, analytic_solution_fields,\n                                                false, initial_data_list>;\n  using error_compute = Tags::ErrorsCompute<analytic_solution_fields>;\n  using error_tags = db::wrap_tags_in<Tags::Error, analytic_solution_fields>;\n\n  using observe_fields = tmpl::push_back<\n      tmpl::append<typename system::variables_tag::tags_list,\n                   typename deriv_compute::type::tags_list, error_tags>,\n      ScalarWave::Tags::EnergyDensityCompute<volume_dim>,\n      ScalarWave::Tags::MomentumDensityCompute<volume_dim>,\n      ScalarWave::Tags::OneIndexConstraintCompute<volume_dim>,\n      ScalarWave::Tags::TwoIndexConstraintCompute<volume_dim>,\n      ::Tags::PointwiseL2NormCompute<\n          ScalarWave::Tags::OneIndexConstraint<volume_dim>>,\n      ::Tags::PointwiseL2NormCompute<\n          ScalarWave::Tags::TwoIndexConstraint<volume_dim>>,\n      domain::Tags::Coordinates<volume_dim, Frame::Grid>,\n      domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n  using non_tensor_compute_tags =\n      tmpl::list<::Events::Tags::ObserverMeshCompute<volume_dim>,\n                 ::Events::Tags::ObserverDetInvJacobianCompute<\n                     Frame::ElementLogical, Frame::Inertial>,\n                 deriv_compute, analytic_compute, error_compute>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<amr::Criterion,\n                   amr::Criteria::standard_criteria<\n                       volume_dim, typename system::variables_tag::tags_list>>,\n        tmpl::pair<DenseTrigger, DenseTriggers::standard_dense_triggers>,\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<Event,\n                   tmpl::flatten<tmpl::list<\n                       Events::Completion, amr::Events::RefineMesh,\n                       amr::Events::ObserveAmrStats<volume_dim>,\n                       amr::Events::ObserveAmrCriteria<EvolutionMetavars>,\n                       dg::Events::field_observations<\n                           volume_dim, observe_fields, non_tensor_compute_tags>,\n                       Events::time_events<system>>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   ScalarWave::BoundaryCorrections::\n                       standard_boundary_corrections<volume_dim>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   tmpl::push_back<initial_data_list,\n                                   evolution::initial_data::NumericData>>,\n        tmpl::pair<LtsTimeStepper, TimeSteppers::lts_time_steppers>,\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   MathFunctions::all_math_functions<1, Frame::Inertial>>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<\n            ScalarWave::BoundaryConditions::BoundaryCondition<volume_dim>,\n            ScalarWave::BoundaryConditions::standard_boundary_conditions<\n                volume_dim>>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   tmpl::push_back<StepChoosers::standard_step_choosers<system>,\n                                   StepChoosers::ByBlock<volume_dim>>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::push_back<StepChoosers::standard_slab_choosers<\n                                       system, local_time_stepping>,\n                                   StepChoosers::ByBlock<volume_dim>>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<TimeSequence<std::uint64_t>,\n                   TimeSequences::all_time_sequences<std::uint64_t>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers,\n                                         Triggers::time_triggers>>>;\n  };\n\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n          tmpl::at<typename factory_creation::factory_classes, Event>>>>;\n\n  // The scalar wave system generally does not require filtering, except\n  // possibly on certain deformed domains.  Here a filter is added in 2D for\n  // testing purposes.  When performing numerical experiments with the scalar\n  // wave system, the user should determine whether this filter can be removed.\n  static constexpr bool use_filtering = (2 == volume_dim);\n\n  using step_actions = tmpl::flatten<tmpl::list<\n      evolution::dg::Actions::ComputeTimeDerivative<\n          volume_dim, system, AllStepChoosers, local_time_stepping,\n          use_dg_element_collection>,\n      evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          volume_dim, use_dg_element_collection>,\n      Actions::MutateApply<RecordTimeStepperData<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<\n                         evolution::dg::ApplyLtsDenseBoundaryCorrections<\n                             EvolutionMetavars>>>,\n                     Actions::MutateApply<UpdateU<system, local_time_stepping>>,\n                     evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n                         volume_dim, use_dg_element_collection>,\n                     Actions::MutateApply<ChangeTimeStepperOrder<system>>>,\n          tmpl::list<\n              evolution::Actions::RunEventsAndDenseTriggers<tmpl::list<>>,\n              Actions::MutateApply<UpdateU<system, local_time_stepping>>>>,\n      Actions::MutateApply<CleanHistory<system>>,\n      tmpl::conditional_t<\n          local_time_stepping,\n          Actions::MutateApply<evolution::dg::CleanMortarHistory<volume_dim>>,\n          tmpl::list<>>,\n      tmpl::conditional_t<\n          use_filtering,\n          dg::Actions::Filter<\n              Filters::Exponential<0>,\n              tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                         ScalarWave::Tags::Phi<Dim>>>,\n          tmpl::list<>>>>;\n\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>;\n\n  using initialization_actions = tmpl::list<\n      Initialization::Actions::InitializeItems<\n          Initialization::TimeStepping<EvolutionMetavars, TimeStepperBase>,\n          evolution::dg::Initialization::Domain<EvolutionMetavars>,\n          ::amr::Initialization::Initialize<volume_dim, EvolutionMetavars>,\n          Initialization::TimeStepperHistory<EvolutionMetavars>>,\n      Initialization::Actions::NonconservativeSystem<system>,\n      evolution::Initialization::Actions::SetVariables<\n          domain::Tags::Coordinates<Dim, Frame::ElementLogical>>,\n      ScalarWave::Actions::InitializeConstraints<volume_dim>,\n      Initialization::Actions::AddComputeTags<\n          StepChoosers::step_chooser_compute_tags<EvolutionMetavars,\n                                                  local_time_stepping>>,\n      ::evolution::dg::Initialization::Mortars<volume_dim>,\n      evolution::Actions::InitializeRunEventsAndDenseTriggers,\n      Parallel::Actions::TerminatePhase>;\n\n  using dg_element_array = DgElementArray<\n      EvolutionMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::InitializeTimeStepperHistory,\n              SelfStart::self_start_procedure<step_actions, system>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<Parallel::Phase::Restart,\n                                 tmpl::list<dg_registration_list,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::WriteCheckpoint,\n              tmpl::list<evolution::Actions::RunEventsAndTriggers<\n                             Triggers::WhenToCheck::AtCheckpoints>,\n                         Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::flatten<tmpl::list<\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  Actions::ChangeSlabSize, step_actions,\n                  Actions::MutateApply<AdvanceTime<>>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = tmpl::list<\n        Initialization::ProjectTimeStepping<volume_dim>,\n        evolution::dg::Initialization::ProjectDomain<volume_dim>,\n        ::amr::projectors::ProjectVariables<volume_dim,\n                                            typename system::variables_tag>,\n        ::amr::projectors::ProjectTensors<volume_dim,\n                                          ::ScalarWave::Tags::ConstraintGamma2>,\n        evolution::dg::Initialization::ProjectMortars<volume_dim,\n                                                      local_time_stepping>,\n        Initialization::ProjectTimeStepperHistory<EvolutionMetavars>,\n        evolution::Actions::ProjectRunEventsAndDenseTriggers,\n        ::amr::projectors::DefaultInitialize<\n            Initialization::Tags::InitialTimeDelta,\n            Initialization::Tags::InitialSlabSize<local_time_stepping>,\n            ::domain::Tags::InitialExtents<volume_dim>,\n            ::domain::Tags::InitialRefinementLevels<volume_dim>,\n            evolution::dg::Tags::Quadrature,\n            Tags::StepperErrors<typename system::variables_tag>,\n            SelfStart::Tags::InitialValue<typename system::variables_tag>,\n            SelfStart::Tags::InitialValue<Tags::TimeStep>>,\n        ::amr::projectors::CopyFromCreatorOrLeaveAsIs<\n            Tags::ChangeSlabSize::NumberOfExpectedMessages,\n            Tags::ChangeSlabSize::NewSlabSize>>;\n    static constexpr bool keep_coarse_grids = false;\n    static constexpr bool p_refine_only_in_event = true;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::list<::amr::Component<EvolutionMetavars>,\n                 observers::Observer<EvolutionMetavars>,\n                 observers::ObserverWriter<EvolutionMetavars>,\n                 dg_element_array>;\n\n  static constexpr Options::String help{\n      \"Evolve a Scalar Wave in Dim spatial dimension.\\n\\n\"\n      \"The numerical flux is:    UpwindFlux\\n\"};\n\n  static constexpr auto default_phase_order = std::array<Parallel::Phase, 6>{\n      Parallel::Phase::Initialization,\n      Parallel::Phase::InitializeTimeStepperHistory,\n      Parallel::Phase::Register,\n      Parallel::Phase::CheckDomain,\n      Parallel::Phase::Evolve,\n      Parallel::Phase::Exit};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "src/Evolution/Imex/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  DoImplicitStep.hpp\n  NamespaceDocs.hpp\n  RecordTimeStepperData.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Imex/Actions/DoImplicitStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Imex/Protocols/ImexSystem.hpp\"\n#include \"Evolution/Imex/SolveImplicitSector.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/CleanupRoutine.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\ntemplate <typename Tag>\nstruct Next;\nstruct Time;\nstruct TimeStepId;\n}  // namespace Tags\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace imex::Actions {\n/// \\ingroup ActionsGroup\n/// \\brief Perform implicit variable updates for one substep\n///\n/// Uses:\n/// - DataBox:\n///   - Tags::Next<Tags::TimeStepId>\n///   - Tags::Time\n///   - Tags::TimeStep\n///   - Tags::TimeStepper<ImexTimeStepper>\n///   - imex::Tags::Mode\n///   - imex::Tags::SolveTolerance\n///   - as required by system implicit sectors\n///\n/// DataBox changes:\n/// - variables_tag\n/// - imex::Tags::ImplicitHistory<sector> for each sector\ntemplate <typename System>\nstruct DoImplicitStep {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(tt::assert_conforms_to_v<System, protocols::ImexSystem>);\n\n    const double original_time = db::get<::Tags::Time>(box);\n    const CleanupRoutine reset_time = [&]() {\n      db::mutate<::Tags::Time>(\n          [&](const gsl::not_null<double*> time) { *time = original_time; },\n          make_not_null(&box));\n    };\n    db::mutate<::Tags::Time>(\n        [](const gsl::not_null<double*> time,\n           const TimeStepId& next_time_step_id) {\n          *time = next_time_step_id.substep_time();\n        },\n        make_not_null(&box), db::get<::Tags::Next<::Tags::TimeStepId>>(box));\n\n    tmpl::for_each<typename System::implicit_sectors>([&](auto sector_v) {\n      using sector = tmpl::type_from<decltype(sector_v)>;\n      db::mutate_apply<\n          SolveImplicitSector<typename System::variables_tag, sector>>(\n          make_not_null(&box));\n    });\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace imex::Actions\n"
  },
  {
    "path": "src/Evolution/Imex/Actions/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Actions related to evolution using implicit-explicit time stepping.\nnamespace imex::Actions {}\n"
  },
  {
    "path": "src/Evolution/Imex/Actions/RecordTimeStepperData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Imex/Protocols/ImexSystem.hpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass TimeStepId;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace imex::Actions {\n/// \\ingroup ActionsGroup\n/// \\brief Records the implicit sources in the implicit time stepper history.\n///\n/// Uses:\n/// - GlobalCache: nothing\n/// - DataBox:\n///   - Tags::TimeStepId\n///   - system::variables_tag\n///   - as required by source terms\n///\n/// DataBox changes:\n/// - imex::Tags::ImplicitHistory<sector> for each sector\ntemplate <typename System>\nstruct RecordTimeStepperData {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {  // NOLINT const\n    static_assert(tt::assert_conforms_to_v<System, protocols::ImexSystem>);\n\n    const size_t number_of_grid_points =\n        db::get<typename System::variables_tag>(box).number_of_grid_points();\n\n    tmpl::for_each<typename System::implicit_sectors>([&](auto sector_v) {\n      using sector = tmpl::type_from<decltype(sector_v)>;\n      using source =\n          typename tmpl::front<typename sector::solve_attempts>::source;\n      using history_tag = Tags::ImplicitHistory<sector>;\n      using DtSectorVars =\n          Variables<db::wrap_tags_in<::Tags::dt, typename sector::tensors>>;\n      db::mutate_apply<\n          tmpl::list<history_tag>,\n          tmpl::push_front<typename source::argument_tags, ::Tags::TimeStepId>>(\n          [&](const gsl::not_null<typename history_tag::type*> history,\n              const TimeStepId& time_step_id, const auto&... source_arguments) {\n            history->insert_in_place(\n                time_step_id, history_tag::type::no_value,\n                [&](const gsl::not_null<DtSectorVars*> source_result) {\n                  source_result->initialize(number_of_grid_points);\n                  tmpl::as_pack<typename source::return_tags>(\n                      [&](auto... source_tags) {\n                        // The history stores derivatives as\n                        // ::Tags::dt<Var>, but the source provides\n                        // ::Tags::Source<Var>.  Since the only\n                        // implicit equations we support are source\n                        // terms, these quantities are equal, but we\n                        // still need to make the types match.\n                        source::apply(\n                            make_not_null(\n                                &get<::Tags::dt<db::remove_tag_prefix<\n                                    tmpl::type_from<decltype(source_tags)>>>>(\n                                    *source_result))...,\n                            source_arguments...);\n                      });\n                });\n          },\n          make_not_null(&box));\n    });\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace imex::Actions\n"
  },
  {
    "path": "src/Evolution/Imex/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Imex)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GuessResult.cpp\n  Mode.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CleanHistory.hpp\n  CleanHistory.tpp\n  GuessResult.hpp\n  ImplicitDenseOutput.hpp\n  Initialize.hpp\n  Mode.hpp\n  NamespaceDocs.hpp\n  SolveImplicitSector.hpp\n  SolveImplicitSector.tpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ErrorHandling\n  Options\n  INTERFACE\n  DataStructures\n  LinearSolver\n  RootFinding\n  Time\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Protocols)\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/Evolution/Imex/CleanHistory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ImexTimeStepper;\ntemplate <typename TagsList>\nclass Variables;\nnamespace Tags {\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace TimeSteppers {\ntemplate <typename Vars>\nclass History;\n}  // namespace TimeSteppers\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\nnamespace imex::Tags {\ntemplate <typename ImplicitSector>\nstruct ImplicitHistory;\n}  // namespace imex::Tags\n/// \\endcond\n\nnamespace imex {\n/// Mutator to clean history objects for each sector after an IMEX substep.\ntemplate <typename System,\n          typename ImplicitSectors = typename System::implicit_sectors>\nstruct CleanHistory;\n\n/// \\copydoc CleanHistory\ntemplate <typename System, typename... ImplicitSectors>\nstruct CleanHistory<System, tmpl::list<ImplicitSectors...>> {\n  using return_tags =\n      tmpl::list<imex::Tags::ImplicitHistory<ImplicitSectors>...>;\n  using argument_tags = tmpl::list<::Tags::TimeStepper<ImexTimeStepper>>;\n\n  static void apply(\n      const gsl::not_null<TimeSteppers::History<\n          Variables<typename ImplicitSectors::tensors>>*>... histories,\n      const ImexTimeStepper& time_stepper);\n};\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/CleanHistory.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Imex/CleanHistory.hpp\"\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/TimeSteppers/ImexTimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace imex {\ntemplate <typename System, typename... ImplicitSectors>\nvoid CleanHistory<System, tmpl::list<ImplicitSectors...>>::apply(\n    const gsl::not_null<TimeSteppers::History<\n        Variables<typename ImplicitSectors::tensors>>*>... histories,\n    const ImexTimeStepper& time_stepper) {\n  expand_pack((time_stepper.clean_history(histories), 0)...);\n}\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/GuessResult.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Imex/GuessResult.hpp\"\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace imex {\n[[noreturn]] void NoJacobianBecauseSolutionIsAnalytic::apply() {\n  ERROR(\n      \"NoJacobianBecauseSolutionIsAnalytic was used but initial guess did \"\n      \"not return GuessResult::ExactSolution\");\n}\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/GuessResult.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <vector>\n\n#include \"Utilities/TMPL.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSourceJacobian.hpp\"\n\nnamespace imex {\n/// Type of guess returned from an implicit sector's `initial_guess`\n/// mutator.  If `ExactSolution` is returned, the implicit solve is\n/// skipped.\nenum class GuessResult { InitialGuess, ExactSolution };\n\n/// Mutator for the `initial_guess` of an implicit sector that does\n/// not modify the variables.  The initial guess is therefore the\n/// result of the explicit step.\nstruct GuessExplicitResult {\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n  template <typename ImplicitVars>\n  static std::vector<GuessResult> apply(\n      const ImplicitVars& /*inhomogeneous_terms*/,\n      const double /*implicit_weight*/) {\n    return {};\n  }\n};\n\n/// Mutator for the `jacobian` of an implicit sector that has an\n/// analytic solution.  Such a sector never does numerical solves, and\n/// so does not need an available jacobian.\n///\n/// \\note The `source` mutator is always required, even if the\n/// implicit equation can be solved analytically.\nstruct NoJacobianBecauseSolutionIsAnalytic\n    : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n      tt::ConformsTo<::protocols::StaticReturnApplyable> {\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n  [[noreturn]] static void apply();\n};\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/ImplicitDenseOutput.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Imex/Protocols/ImexSystem.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace imex::Tags {\ntemplate <typename ImplicitSector>\nstruct ImplicitHistory;\n}  // namespace imex::Tags\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace imex {\n/// Mutator to apply the implicit portion of dense output, intended\n/// for use in `RunEventsAndDenseTriggers`.\ntemplate <typename ImexSystem>\nstruct ImplicitDenseOutput {\n  static_assert(tt::assert_conforms_to_v<ImexSystem, protocols::ImexSystem>);\n\n  using return_tags = tmpl::list<typename ImexSystem::variables_tag>;\n  using argument_tags = tmpl::push_front<\n      tmpl::transform<typename ImexSystem::implicit_sectors,\n                      tmpl::bind<Tags::ImplicitHistory, tmpl::_1>>,\n      ::Tags::TimeStepper<TimeStepper>, ::Tags::Time>;\n\n  template <typename... Histories>\n  static void apply(\n      const gsl::not_null<typename ImexSystem::variables_tag::type*> variables,\n      const TimeStepper& time_stepper, const double time,\n      const Histories&... histories) {\n    const auto update_sector = [&](auto sector_v, auto history) {\n      using sector = tmpl::type_from<decltype(sector_v)>;\n      auto sector_variables =\n          variables->template reference_subset<typename sector::tensors>();\n      const bool success = time_stepper.dense_update_u(\n          make_not_null(&sector_variables), history, time);\n      if (not success) {\n        ERROR(\"IMEX dense output failed with history:\\n\" << history);\n      }\n    };\n    tmpl::as_pack<typename ImexSystem::implicit_sectors>(\n        [&](auto... sectors_v) {\n          EXPAND_PACK_LEFT_TO_RIGHT(update_sector(sectors_v, histories));\n        });\n  }\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static bool is_ready(\n      const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,\n      const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* /*component*/) {\n    return true;\n  }\n};\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Imex/Protocols/ImexSystem.hpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Evolution/Imex/Tags/Mode.hpp\"\n#include \"Evolution/Imex/Tags/SolveFailures.hpp\"\n#include \"Evolution/Imex/Tags/SolveTolerance.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace imex {\n/// Create the IMEX structures and options.\ntemplate <typename System, typename = typename System::implicit_sectors>\nstruct Initialize;\n\n/// \\cond\ntemplate <typename System, typename... Sectors>\nstruct Initialize<System, tmpl::list<Sectors...>> {\n  static_assert(tt::assert_conforms_to_v<System, protocols::ImexSystem>);\n\n  using variables_tag = typename System::variables_tag;\n  using example_tensor_tag =\n      tmpl::front<typename tmpl::front<tmpl::list<Sectors...>>::tensors>;\n\n  using const_global_cache_tags = tmpl::list<Tags::Mode, Tags::SolveTolerance>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using simple_tags_from_options = tmpl::list<>;\n  using simple_tags = tmpl::list<Tags::ImplicitHistory<Sectors>...,\n                                 Tags::SolveFailures<Sectors>...>;\n  using compute_tags = tmpl::list<>;\n\n  using return_tags = simple_tags;\n  using argument_tags =\n      tmpl::list<::Tags::HistoryEvolvedVariables<variables_tag>,\n                 example_tensor_tag>;\n\n  static void apply(\n      const gsl::not_null<\n          typename Tags::ImplicitHistory<Sectors>::type*>... histories,\n      const gsl::not_null<\n          typename Tags::SolveFailures<Sectors>::type*>... solve_failures,\n      const TimeSteppers::History<typename variables_tag::type>&\n          explicit_history,\n      const typename example_tensor_tag::type& example_tensor) {\n    const auto order = explicit_history.integration_order();\n    expand_pack((histories->integration_order(order), 0)...);\n    expand_pack(*solve_failures = make_with_value<Scalar<DataVector>>(\n                    example_tensor, 0.0)...);\n  }\n};\n/// \\endcond\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/Mode.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Imex/Mode.hpp\"\n\n#include <string>\n\n#include \"Options/ParseOptions.hpp\"\n\ntemplate <>\nimex::Mode Options::create_from_yaml<imex::Mode>::create<void>(\n    const Options::Option& options) {\n  const auto mode = options.parse_as<std::string>();\n  if (mode == \"Implicit\") {\n    return imex::Mode::Implicit;\n  } else if (mode == \"SemiImplicit\") {\n    return imex::Mode::SemiImplicit;\n  } else {\n    PARSE_ERROR(options.context(),\n                \"Invalid IMEX mode.  Must be Implicit or SemiImplicit.\");\n  }\n}\n"
  },
  {
    "path": "src/Evolution/Imex/Mode.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Options/Options.hpp\"\n\nnamespace imex {\n/// IMEX implementations\nenum class Mode {\n  /// Solve the implicit equation using a nonlinear solver.\n  Implicit,\n  /// Solve a linearized version of the implicit equation.\n  SemiImplicit,\n};\n}  // namespace imex\n\ntemplate <>\nstruct Options::create_from_yaml<imex::Mode> {\n  template <typename Metavariables>\n  static imex::Mode create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nimex::Mode Options::create_from_yaml<imex::Mode>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Evolution/Imex/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Code related to evolution using implicit-explicit time stepping.\nnamespace imex {}\n"
  },
  {
    "path": "src/Evolution/Imex/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ImexSystem.hpp\n  ImplicitSector.hpp\n  ImplicitSource.hpp\n  ImplicitSourceJacobian.hpp\n  NamespaceDocs.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Imex/Protocols/ImexSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace imex::protocols {\n/// Protocol for an IMEX evolution system.\n///\n/// In addition to the usual requirements for an evolution system, an\n/// IMEX system must specify an `implicit_sectors` typelist of structs\n/// conforming to protocols::ImplicitSector, each of which describes\n/// an implicit solve to be performed during time steps.\n///\n/// For efficiency, the tags in the `tensors` type alias of each\n/// sector are required to be adjacent in the system's variables.\n///\n/// \\snippet DoImplicitStepSector.hpp ImexSystem\nstruct ImexSystem {\n  template <typename ConformingType>\n  struct test {\n    using implicit_sectors = typename ConformingType::implicit_sectors;\n    static_assert(\n        tmpl::all<implicit_sectors,\n                  tt::assert_conforms_to<tmpl::_1, ImplicitSector>>::value);\n\n    template <typename Sector>\n    struct sector_tensors {\n      using type = typename Sector::tensors;\n    };\n\n    using variables_tag = typename ConformingType::variables_tag;\n    static_assert(\n        tmpl::all<\n            implicit_sectors,\n            std::is_same<\n                tmpl::bind<tmpl::list_difference, sector_tensors<tmpl::_1>,\n                           tmpl::pin<typename variables_tag::type::tags_list>>,\n                tmpl::pin<tmpl::list<>>>>::value,\n        \"Implicit sector variables must be part of the system.\");\n\n    // There is a bug in brigand::lazy::reverse_find that it does not\n    // actually evaluate its arguments lazily, so we must\n    // bind<brigand::reverse_find> instead.\n    static_assert(\n        tmpl::all<\n            implicit_sectors,\n            std::is_same<\n                tmpl::bind<\n                    tmpl::reverse_find,\n                    tmpl::lazy::find<\n                        tmpl::pin<typename variables_tag::tags_list>,\n                        tmpl::defer<std::is_same<\n                            tmpl::_1,\n                            tmpl::bind<tmpl::front, sector_tensors<tmpl::parent<\n                                                        tmpl::_1>>>>>>,\n                    tmpl::defer<std::is_same<\n                        tmpl::_1,\n                        tmpl::bind<tmpl::back,\n                                   sector_tensors<tmpl::parent<tmpl::_1>>>>>>,\n                sector_tensors<tmpl::_1>>>::value,\n        \"Tensors in an implicit sector must be adjacent in the system's \"\n        \"variables.\");\n  };\n};\n}  // namespace imex::protocols\n"
  },
  {
    "path": "src/Evolution/Imex/Protocols/ImplicitSector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"ImplicitSource.hpp\"\n#include \"ImplicitSourceJacobian.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\nnamespace Tags {\ntemplate <typename Tag>\nstruct Source;\n}  // namespace Tags\n/// \\endcond\n\nnamespace imex::protocols {\n/// Protocol for an implicit sector of an IMEX system.\n///\n/// An implicit sector describes the sources for one implicit solve\n/// performed during IMEX evolution.  A system may have multiple\n/// implicit sectors, but they must be independent, i.e., their\n/// sources must not depend on any tensors in other sectors.\n///\n/// Classes implementing this protocol must define:\n///\n/// * a `tensors` type alias of tags for the variables to be solved for\n///\n/// * an `initial_guess` type to be passed to `db::mutate_apply`,\n///   taking additional arguments for the inhomogeneous terms \\f$X\\f$\n///   and implicit weight \\f$w\\f$ in the equation to be solved:\n///   \\f$u = X + w S(u)\\f$.  (See example below.)  It must return a\n///   `std::vector<GuessResult>` indicating, for each point, whether\n///   the implicit equation has been solved analytically or whether\n///   the numerical solve should continue.  An empty return is\n///   equivalent to `imex::GuessResult::InitialGuess` for every point,\n///   so numerical solves will be performed for each.  When this is\n///   called, the sector variables will have the value from the\n///   explicit part of the time step.  This mutator will not be called\n///   if the implicit weight is zero, as the solution is trivial in\n///   that case.  If using the value of the explicit step as an\n///   initial guess is acceptable, this can be the type\n///   `imex::GuessExplicitResult`.\n///\n/// * a `solve_attempts` list of sources that will be attempted to be\n///   solved, in order.  The first one that succeeds at each point\n///   will be used.  Pieces of code that need \"the\" source for the\n///   sector will use the source from the first entry.  Each attempt\n///   must be a struct with the following types:\n///\n///   * lists used to construct a DataBox during the pointwise\n///     implicit solve:\n///\n///     * `tags_from_evolution` for tags in addition to the sector\n///        tensors to be made available from the evolution DataBox.\n///        Volume quantities will be reduced to have one grid point,\n///        with the appropriate value for the point being solved for.\n///\n///     * `simple_tags` for temporaries (e.g., primitives)\n///\n///     * `compute_tags`\n///\n///   * a `source` type to be passed to `db::mutate_apply` to compute\n///     the sources. It must conform to imex::protocols::ImplicitSource.\n///\n///   * a `jacobian` type to be passed to `db::mutate_apply` to\n///     compute the source jacobian.  It must conform to\n///     imex::protocols::ImplicitSourceJacobian. If the implicit equation can\n///     always be solved analytically for the sector, the jacobian is not\n///     required and this may be the type\n///     `imex::NoJacobianBecauseSolutionIsAnalytic`.\n///\n///   * lists `source_prep` and `jacobian_prep` that will be called\n///     before the corresponding main mutator, e.g., for computing\n///     primitives.  Mutators appearing in multiple lists, as well as\n///     the `source` and `jacobian` mutators, will be skipped if they\n///     have already been applied for the current point.  Note that\n///     the `source_prep` mutators are only used during the implicit\n///     solve, and any preparation needed before the `source` call in\n///     the main action loop to record the history is the\n///     responsibility of the user.\n///\n/// All `Variables` in the DataBox, including the sources and source\n/// jacobian, will be initialized to zero with a single grid point.\n///\n/// \\snippet DoImplicitStepSector.hpp simple_sector\n///\n/// Examples of definitions of a complicated implicit source and\n/// jacobian:\n///\n/// \\snippet Test_SolveImplicitSector.cpp source\n/// \\snippet Test_SolveImplicitSector.cpp Jacobian\nstruct ImplicitSector {\n  template <typename ConformingType>\n  struct test {\n    using tensors = typename ConformingType::tensors;\n    using initial_guess = typename ConformingType::initial_guess;\n    using solve_attempts = typename ConformingType::solve_attempts;\n\n    static_assert(tt::is_a_v<tmpl::list, tensors>);\n    static_assert(tt::is_a_v<tmpl::list, solve_attempts>);\n    static_assert(tmpl::size<solve_attempts>::value >= 1);\n\n    static_assert(\n        tmpl::all<\n            tensors,\n            tt::is_a<Tensor, tmpl::bind<tmpl::type_from, tmpl::_1>>>::value);\n\n    using source_tensors =\n        tmpl::transform<tensors, tmpl::bind<::Tags::Source, tmpl::_1>>;\n\n    static_assert(\n        tmpl::size<tmpl::list_difference<typename initial_guess::return_tags,\n                                         tensors>>::value == 0,\n        \"initial_guess can only modify sector variables.\");\n\n    template <typename SolveAttempt>\n    struct test_solve_attempt {\n      using source = typename SolveAttempt::source;\n      using jacobian = typename SolveAttempt::jacobian;\n\n      using tags_from_evolution = typename SolveAttempt::tags_from_evolution;\n      using simple_tags = typename SolveAttempt::simple_tags;\n      using compute_tags = typename SolveAttempt::compute_tags;\n\n      using source_prep = typename SolveAttempt::source_prep;\n      using jacobian_prep = typename SolveAttempt::jacobian_prep;\n\n      // check protocol conformity\n      static_assert(\n          tt::assert_conforms_to_v<source, imex::protocols::ImplicitSource>);\n      static_assert(tt::assert_conforms_to_v<\n                    jacobian, imex::protocols::ImplicitSourceJacobian>);\n\n      static_assert(tt::is_a_v<tmpl::list, tags_from_evolution>);\n      static_assert(tt::is_a_v<tmpl::list, simple_tags>);\n      static_assert(tt::is_a_v<tmpl::list, compute_tags>);\n      static_assert(tt::is_a_v<tmpl::list, source_prep>);\n      static_assert(tt::is_a_v<tmpl::list, jacobian_prep>);\n\n      static_assert(\n          std::is_same_v<tmpl::list_difference<tags_from_evolution, tensors>,\n                         tags_from_evolution>,\n          \"tags_from_evolution cannot include the sector tensors.\");\n\n      static_assert(\n          std::is_same_v<tmpl::list_difference<source_tensors,\n                                               typename source::return_tags>,\n                         tmpl::list<>> and\n              std::is_same_v<tmpl::list_difference<typename source::return_tags,\n                                                   source_tensors>,\n                             tmpl::list<>>,\n          \"Implicit source must provide sources for the entire sector.\");\n\n      template <typename T>\n      struct is_a_tensor_of_data_vector : std::false_type {};\n\n      template <typename Symm, typename IndexList>\n      struct is_a_tensor_of_data_vector<Tensor<DataVector, Symm, IndexList>>\n          : std::true_type {};\n\n      static_assert(\n          tmpl::none<simple_tags, is_a_tensor_of_data_vector<tmpl::bind<\n                                      tmpl::type_from, tmpl::_1>>>::value,\n          \"Do not include tags for Tensor<DataVector> in simple_tags, because \"\n          \"they trigger many memory allocations.  Add the tensors as part of \"\n          \"a Variables instead.\");\n\n      using type = std::true_type;\n    };\n\n    static_assert(\n        tmpl::all<solve_attempts, test_solve_attempt<tmpl::_1>>::value);\n  };\n};\n}  // namespace imex::protocols\n"
  },
  {
    "path": "src/Evolution/Imex/Protocols/ImplicitSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Protocols/StaticReturnApplyable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct Source;\n}  // namespace Tags\n/// \\endcond\n\nnamespace imex::protocols {\n/// Protocol for implicit source terms.\n///\n/// A struct conforming to this protocol must also conform to\n/// ::protocols::StaticReturnApplyable.  The `return_tags` of this\n/// struct must be a list of `::Tags::Source<T>` for every tensor `T`\n/// in the implicit sector.\n///\n/// The `argument_tags` must not include compute items depending on\n/// tensors in the implicit sector.\n///\n/// \\snippet Test_SolveImplicitSector.cpp source\nstruct ImplicitSource {\n  template <typename ConformingType>\n  struct test {\n    static_assert(tt::assert_conforms_to_v<ConformingType,\n                                           ::protocols::StaticReturnApplyable>);\n\n    using return_tags = typename ConformingType::return_tags;\n    static_assert(\n        tmpl::all<return_tags, tt::is_a<::Tags::Source, tmpl::_1>>::value,\n        \"All return tags must be ::Tags::Source<...>\");\n  };\n};\n}  // namespace imex::protocols\n"
  },
  {
    "path": "src/Evolution/Imex/Protocols/ImplicitSourceJacobian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Imex/Tags/Jacobian.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Protocols/StaticReturnApplyable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct Source;\n}  // namespace Tags\n/// \\endcond\n\nnamespace imex::protocols {\n/// Protocol for the Jacobian of implicit source terms.\n///\n/// A struct conforming to this protocol must also conform to\n/// ::protocols::StaticReturnApplyable.  The `return_tags` of this\n/// struct must be a list of `Tags::Jacobian` tags for the dependence\n/// of the sector's `Tags::Sources` on the sector's tensors.  Portions\n/// of the Jacobian that are zero may be omitted from the list.\n///\n/// The `argument_tags` must not include compute items depending on\n/// tensors in the implicit sector.\n///\n/// \\snippet Test_SolveImplicitSector.cpp Jacobian\nstruct ImplicitSourceJacobian {\n  template <typename ConformingType>\n  struct test {\n    static_assert(tt::assert_conforms_to_v<ConformingType,\n                                           ::protocols::StaticReturnApplyable>);\n\n    using return_tags = typename ConformingType::return_tags;\n    static_assert(\n        tmpl::all<return_tags, tt::is_a<Tags::Jacobian, tmpl::_1>>::value);\n\n    template <typename T>\n    struct get_dependent {\n      using type = typename T::dependent;\n    };\n\n    static_assert(\n        tmpl::all<return_tags,\n                  tt::is_a<::Tags::Source, get_dependent<tmpl::_1>>>::value);\n  };\n};\n}  // namespace imex::protocols\n"
  },
  {
    "path": "src/Evolution/Imex/Protocols/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Protocols for imex\nnamespace imex::protocols {}\n"
  },
  {
    "path": "src/Evolution/Imex/SolveImplicitSector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <tuple>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Imex/Mode.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nclass ImexTimeStepper;\nclass TimeDelta;\ntemplate <typename TagsList>\nclass Variables;\nnamespace Tags {\nstruct TimeStep;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace TimeSteppers {\ntemplate <typename Vars>\nclass History;\n}  // namespace TimeSteppers\nnamespace imex::Tags {\ntemplate <typename ImplicitSector>\nstruct ImplicitHistory;\nstruct Mode;\ntemplate <typename Sector>\nstruct SolveFailures;\nstruct SolveTolerance;\n}  // namespace imex::Tags\n/// \\endcond\n\nnamespace imex {\nnamespace solve_implicit_sector_detail {\ntemplate <typename Tags>\nusing ForwardTuple = tmpl::wrap<\n    tmpl::transform<Tags, std::add_lvalue_reference<std::add_const<\n                              tmpl::bind<tmpl::type_from, tmpl::_1>>>>,\n    std::tuple>;\n}  // namespace solve_implicit_sector_detail\n\n/// Perform the implicit solve for one implicit sector.\n///\n/// This will update the tensors in the implicit sector and clean up\n/// the corresponding time stepper history.  A new history entry is\n/// not added, because that should be done with the same values of the\n/// variables used for the explicit portion of the time derivative,\n/// which may still undergo variable-fixing-like corrections.\n///\n/// \\warning\n/// This will use the value of `::Tags::Time` from the DataBox.  Most\n/// of the time, the value appropriate for evaluating the explicit RHS\n/// is stored there, so it will likely need to be set to the\n/// appropriate value for the implicit RHS for the duration of this\n/// mutation.\ntemplate <typename SystemVariablesTag, typename ImplicitSector>\nstruct SolveImplicitSector {\n  static_assert(\n      tt::assert_conforms_to_v<ImplicitSector, protocols::ImplicitSector>);\n\n public:\n  using SystemVariables = typename SystemVariablesTag::type;\n  using SectorVariables = Variables<typename ImplicitSector::tensors>;\n\n private:\n  template <typename Attempt>\n  struct get_tags_from_evolution {\n    using type = typename Attempt::tags_from_evolution;\n  };\n\n  using tags_for_each_attempt =\n      tmpl::transform<typename ImplicitSector::solve_attempts,\n                      get_tags_from_evolution<tmpl::_1>>;\n  // List of tags used for the initial guess followed by lists of tags\n  // used for each solve attempt.\n  using evolution_data_tags =\n      tmpl::push_front<tags_for_each_attempt,\n                       typename ImplicitSector::initial_guess::argument_tags>;\n\n  using EvolutionDataTuple = solve_implicit_sector_detail::ForwardTuple<\n      tmpl::join<evolution_data_tags>>;\n\n  static void apply_impl(\n      gsl::not_null<SystemVariables*> system_variables,\n      gsl::not_null<Scalar<DataVector>*> solve_failures,\n      const ImexTimeStepper& time_stepper, const TimeDelta& time_step,\n      const TimeSteppers::History<SectorVariables>& implicit_history,\n      Mode implicit_solve_mode, double implicit_solve_tolerance,\n      const EvolutionDataTuple& joined_evolution_data);\n\n public:\n  using return_tags =\n      tmpl::list<SystemVariablesTag, Tags::SolveFailures<ImplicitSector>>;\n  using argument_tags = tmpl::append<\n      tmpl::list<::Tags::TimeStepper<ImexTimeStepper>, ::Tags::TimeStep,\n                 imex::Tags::ImplicitHistory<ImplicitSector>, Tags::Mode,\n                 Tags::SolveTolerance>,\n      tmpl::join<evolution_data_tags>>;\n\n  template <typename... ForwardArgs>\n  static void apply(\n      const gsl::not_null<SystemVariables*> system_variables,\n      const gsl::not_null<Scalar<DataVector>*> solve_failures,\n      const ImexTimeStepper& time_stepper, const TimeDelta& time_step,\n      const TimeSteppers::History<SectorVariables>& implicit_history,\n      const Mode implicit_solve_mode, const double implicit_solve_tolerance,\n      const ForwardArgs&... forward_args) {\n    apply_impl(system_variables, solve_failures, time_stepper, time_step,\n               implicit_history, implicit_solve_mode, implicit_solve_tolerance,\n               std::forward_as_tuple(forward_args...));\n  }\n};\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/SolveImplicitSector.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Imex/SolveImplicitSector.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ExtractPoint.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Mode.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Tags/Jacobian.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/Lapack.hpp\"\n#include \"NumericalAlgorithms/RootFinding/GslMultiRoot.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/TimeSteppers/ImexTimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/SplitTuple.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nclass TimeDelta;\n/// \\endcond\n\nnamespace imex {\nnamespace solve_implicit_sector_detail {\n// Calculates the inhomogeneous terms and the implicit weight for the\n// equation to be solved (X and w in the sector documentation), and\n// writes the initial guess into system_variables (which generally\n// points into the evolution DateBox).\ntemplate <typename SectorVariables>\nclass ImplicitEquation {\n public:\n  template <typename ImplicitSector, typename SystemVariables>\n  ImplicitEquation(\n      const gsl::not_null<SystemVariables*> system_variables,\n      const ImexTimeStepper& time_stepper, const TimeDelta& time_step,\n      const TimeSteppers::History<SectorVariables>& implicit_history,\n      const ForwardTuple<typename ImplicitSector::initial_guess::argument_tags>&\n          initial_guess_arguments,\n      tmpl::type_<ImplicitSector> /*meta*/)\n      : implicit_weight_(\n            time_stepper.implicit_weight(implicit_history, time_step)) {\n    static_assert(std::is_same_v<typename ImplicitSector::tensors,\n                                 typename SectorVariables::tags_list>);\n\n    if (implicit_weight_ == 0.0) {\n      // Explicit substep.  No solves.  Just write the correct answer\n      // into the evolution box.\n      auto sector_subset = system_variables->template reference_subset<\n          typename SectorVariables::tags_list>();\n      time_stepper.add_inhomogeneous_implicit_terms(\n          make_not_null(&sector_subset), implicit_history, time_step);\n      return;\n    }\n\n    inhomogeneous_terms_ =\n        system_variables\n            ->template extract_subset<typename SectorVariables::tags_list>();\n    time_stepper.add_inhomogeneous_implicit_terms(\n        make_not_null(&inhomogeneous_terms_), implicit_history, time_step);\n\n    {\n      const auto initial_guess_return =\n          tmpl::as_pack<typename ImplicitSector::initial_guess::return_tags>(\n              [&](auto... tags) {\n                return std::make_tuple(\n                    make_not_null(&get<tmpl::type_from<decltype(tags)>>(\n                        *system_variables))...);\n              });\n      initial_guess_types_ = std::apply(\n          [&](const auto&... args) {\n            return ImplicitSector::initial_guess::apply(\n                args..., inhomogeneous_terms_, implicit_weight_);\n          },\n          std::tuple_cat(initial_guess_return, initial_guess_arguments));\n    }\n\n    ASSERT(initial_guess_types_.size() ==\n                   system_variables->number_of_grid_points() or\n               initial_guess_types_.empty(),\n           \"initial_guess must return one GuessResult per point or an empty \"\n           \"vector for GuessResult::InitialGuess everywhere.\");\n  }\n\n  double implicit_weight() const { return implicit_weight_; }\n  const SectorVariables& inhomogeneous_terms() const {\n    return inhomogeneous_terms_;\n  }\n\n  bool solve_needed() const { return implicit_weight_ != 0.0; }\n  GuessResult initial_guess_result(const size_t point_index) const {\n    return initial_guess_types_.empty() ? GuessResult::InitialGuess\n                                        : initial_guess_types_[point_index];\n  }\n\n private:\n  double implicit_weight_;\n  SectorVariables inhomogeneous_terms_{};\n  std::vector<GuessResult> initial_guess_types_{};\n};\n\n// Calculates the residual and jacobian for the ImplicitEquation\n// pointwise, using the source from the SolveAttempt.  This involved\n// setting up a local DataBox for the tags specified in the\n// SolveAttempt and pulling the requested values from the evolution\n// DataBox.\n//\n// This object performs no memory allocations, unless the SolveAttempt\n// adds a tag that allocates or allocates in one of its provided\n// functions.  Tags containing Variables or Tensors are optimized to\n// not allocate.\ntemplate <typename ImplicitSector, typename SolveAttempt>\nclass ImplicitSolver {\n  static_assert(\n      tt::assert_conforms_to_v<ImplicitSector, protocols::ImplicitSector>);\n\n  using sector_variables_tag =\n      ::Tags::Variables<typename ImplicitSector::tensors>;\n  using SectorVariables = typename sector_variables_tag::type;\n  static constexpr size_t solve_dimension =\n      SectorVariables::number_of_independent_components;\n\n  using tags_from_evolution = typename SolveAttempt::tags_from_evolution;\n  using EvolutionData = ForwardTuple<tags_from_evolution>;\n\n  struct EvolutionDataTag : db::SimpleTag {\n    using type = const EvolutionData*;\n  };\n\n  struct SolverPointIndex : db::SimpleTag {\n    using type = size_t;\n  };\n\n  template <typename Tag, typename = typename Tag::type>\n  struct FromEvolution : Tag, db::ReferenceTag {\n    using base = Tag;\n    using parent_tag = EvolutionDataTag;\n    using argument_tags = tmpl::list<parent_tag>;\n    static const typename base::type& get(\n        const EvolutionData* const evolution_data) {\n      return std::get<tmpl::index_of<tags_from_evolution, Tag>::value>(\n          *evolution_data);\n    }\n  };\n\n  template <typename Tag, typename VariablesTags>\n  struct FromEvolution<Tag, Variables<VariablesTags>> : Tag, db::ComputeTag {\n    using base = Tag;\n    using argument_tags = tmpl::list<EvolutionDataTag, SolverPointIndex>;\n    static constexpr auto function(\n        const gsl::not_null<typename base::type*> result,\n        const EvolutionData* const evolution_data, const size_t index) {\n      result->initialize(1);\n      extract_point(\n          result,\n          get<tmpl::index_of<tags_from_evolution, Tag>::value>(*evolution_data),\n          index);\n    }\n  };\n\n  // Tensor<DataVector> always allocates, so instead of creating one\n  // we create a one-tensor Variables, and the DataBox will allow\n  // access to the tensor transparently.  We could instead manually\n  // set all the DataVectors as non-owning, pointing at individual\n  // doubles, but that's more work and it's not clear it gains us\n  // anything.\n  template <typename Tag, typename Symm, typename IndexList>\n  struct FromEvolution<Tag, Tensor<DataVector, Symm, IndexList>>\n      : ::Tags::Variables<tmpl::list<Tag>>, db::ComputeTag {\n    using base = ::Tags::Variables<tmpl::list<Tag>>;\n    using argument_tags = tmpl::list<EvolutionDataTag, SolverPointIndex>;\n    static constexpr auto function(\n        const gsl::not_null<typename base::type*> result,\n        const EvolutionData* const evolution_data, const size_t index) {\n      result->initialize(1);\n      extract_point(\n          make_not_null(&get<Tag>(*result)),\n          get<tmpl::index_of<tags_from_evolution, Tag>::value>(*evolution_data),\n          index);\n    }\n  };\n\n  using all_mutators = tmpl::remove_duplicates<\n      tmpl::append<tmpl::list<typename SolveAttempt::source,\n                              typename SolveAttempt::jacobian>,\n                   typename SolveAttempt::source_prep,\n                   typename SolveAttempt::jacobian_prep>>;\n\n  using source_tag = db::add_tag_prefix<::Tags::Source, sector_variables_tag>;\n  using jacobian_tag =\n      ::Tags::Variables<jacobian_tags<typename ImplicitSector::tensors,\n                                      typename source_tag::type::tags_list>>;\n\n  using internal_simple_tags =\n      tmpl::list<EvolutionDataTag, SolverPointIndex, sector_variables_tag,\n                 source_tag, jacobian_tag>;\n  using wrapped_tags_from_evolution =\n      tmpl::transform<tags_from_evolution, tmpl::bind<FromEvolution, tmpl::_1>>;\n\n  using simple_tags =\n      tmpl::append<internal_simple_tags, typename SolveAttempt::simple_tags>;\n  using compute_tags = tmpl::append<wrapped_tags_from_evolution,\n                                    typename SolveAttempt::compute_tags>;\n\n  using SolveBox =\n      db::compute_databox_type<tmpl::append<simple_tags, compute_tags>>;\n\n public:\n  template <typename PassedEvolutionData>\n  ImplicitSolver(const ImplicitEquation<SectorVariables>& implicit_equation,\n                 PassedEvolutionData&& data_from_evolution)\n      : solve_box_(db::create<simple_tags, compute_tags>()),\n        implicit_equation_(&implicit_equation) {\n    static_assert(std::is_same_v<PassedEvolutionData, const EvolutionData&>,\n                  \"ImplicitSolver was passed a temporary.  \"\n                  \"This will lead to a dangling pointer.\");\n    db::mutate_apply<\n        tmpl::push_front<\n            tmpl::filter<\n                simple_tags,\n                tt::is_a<Variables, tmpl::bind<tmpl::type_from, tmpl::_1>>>,\n            EvolutionDataTag>,\n        tmpl::list<>>(\n        [&data_from_evolution](\n            const gsl::not_null<const EvolutionData**> evolution_data_pointer,\n            const auto... vars) {\n          *evolution_data_pointer = &data_from_evolution;\n          expand_pack((vars->initialize(1, 0.0), 0)...);\n        },\n        make_not_null(&solve_box_));\n  }\n\n  void set_index(const size_t index) {\n    db::mutate<SolverPointIndex>(\n        [&index](const gsl::not_null<size_t*> box_index) {\n          *box_index = index;\n        },\n        make_not_null(&solve_box_));\n\n    extract_point(make_not_null(&inhomogeneous_terms_),\n                  implicit_equation_->inhomogeneous_terms(), index);\n\n    completed_mutators_ = decltype(completed_mutators_){};\n  }\n\n  std::array<double, solve_dimension> operator()(\n      const std::array<double, solve_dimension>& sector_variables_array) const {\n    ASSERT(implicit_equation_->implicit_weight() != 0.0,\n           \"Should not be performing solves on explicit substeps\");\n    set_sector_variables(sector_variables_array);\n    run_mutators<tmpl::push_back<typename SolveAttempt::source_prep,\n                                 typename SolveAttempt::source>>();\n    std::array<double, solve_dimension> residual_array{};\n    SectorVariables residual(residual_array.data(), residual_array.size());\n    residual =\n        inhomogeneous_terms_ - db::get<sector_variables_tag>(solve_box_) +\n        implicit_equation_->implicit_weight() *\n            db::get<db::add_tag_prefix<::Tags::Source, sector_variables_tag>>(\n                solve_box_);\n    return residual_array;\n  }\n\n  std::array<std::array<double, solve_dimension>, solve_dimension> jacobian(\n      const std::array<double, solve_dimension>& sector_variables_array) const {\n    ASSERT(implicit_equation_->implicit_weight() != 0.0,\n           \"Should not be performing solves on explicit substeps\");\n    set_sector_variables(sector_variables_array);\n    run_mutators<tmpl::push_back<typename SolveAttempt::jacobian_prep,\n                                 typename SolveAttempt::jacobian>>();\n\n    std::array<std::array<double, solve_dimension>, solve_dimension>\n        jacobian_array{};\n    // The storage order for the tensors does not match the required\n    // order for the returned array, so we have to copy components\n    // individually.\n    //\n    // Despite repeated references to then, the result of this is\n    // independent of the *_for_offsets variables.  They are only used\n    // for calculating offsets into the returned array.\n    const auto& variables_for_offsets =\n        db::get<sector_variables_tag>(solve_box_);\n    tmpl::for_each<typename SectorVariables::tags_list>(\n        [&](auto dependent_tag_v) {\n          using dependent_tag = tmpl::type_from<decltype(dependent_tag_v)>;\n          const auto& dependent_for_offsets =\n              get<dependent_tag>(variables_for_offsets);\n          for (size_t dependent_component = 0;\n               dependent_component < dependent_for_offsets.size();\n               ++dependent_component) {\n            const auto dependent_index =\n                dependent_for_offsets.get_tensor_index(dependent_component);\n            auto& result_row = jacobian_array[static_cast<size_t>(\n                dependent_for_offsets[dependent_component].data() -\n                variables_for_offsets.data())];\n            tmpl::for_each<typename SectorVariables::tags_list>(\n                [&](auto independent_tag_v) {\n                  using independent_tag =\n                      tmpl::type_from<decltype(independent_tag_v)>;\n                  using jacobian_component_tag =\n                      imex::Tags::Jacobian<independent_tag,\n                                           ::Tags::Source<dependent_tag>>;\n                  const auto& independent_for_offsets =\n                      get<independent_tag>(variables_for_offsets);\n\n                  for (size_t independent_component = 0;\n                       independent_component < independent_for_offsets.size();\n                       ++independent_component) {\n                    const auto independent_index =\n                        independent_for_offsets.get_tensor_index(\n                            independent_component);\n                    result_row[static_cast<size_t>(\n                        independent_for_offsets[independent_component].data() -\n                        variables_for_offsets.data())] =\n                        get<jacobian_component_tag>(solve_box_)\n                            .get(concatenate(independent_index,\n                                             dependent_index))[0];\n                  }\n                });\n          }\n        });\n\n    jacobian_array *= implicit_equation_->implicit_weight();\n\n    for (size_t i = 0; i < solve_dimension; ++i) {\n      jacobian_array[i][i] -= 1.0;\n    }\n    return jacobian_array;\n  }\n\n private:\n  void set_sector_variables(\n      std::array<double, solve_dimension> sector_variables_array) const {\n    const SectorVariables sector_variables(sector_variables_array.data(),\n                                           sector_variables_array.size());\n    set_sector_variables(sector_variables);\n  }\n\n  void set_sector_variables(const SectorVariables& sector_variables) const {\n    if (sector_variables == most_recent_sector_variables_) {\n      return;\n    }\n    most_recent_sector_variables_ = sector_variables;\n    db::mutate<sector_variables_tag>(\n        [&sector_variables](const gsl::not_null<SectorVariables*> vars) {\n          *vars = sector_variables;\n        },\n        make_not_null(&solve_box_));\n    completed_mutators_ = decltype(completed_mutators_){};\n  }\n\n  template <typename Mutators>\n  void run_mutators() const {\n    tmpl::for_each<Mutators>([this](auto mutator_v) {\n      using mutator = tmpl::type_from<decltype(mutator_v)>;\n      if (not get<RanMutator<mutator>>(completed_mutators_)) {\n        db::mutate_apply<mutator>(make_not_null(&solve_box_));\n        get<RanMutator<mutator>>(completed_mutators_) = true;\n      }\n    });\n  }\n\n  template <typename Mutator>\n  struct RanMutator {\n    using type = bool;\n  };\n\n  // Re mutables: This struct is only used locally in serial\n  // single-threaded implicit solves.  The gsl_multiroot interface\n  // takes a const solver object, but we want to be able to share\n  // calculations between the source and jacobian calculations.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable SolveBox solve_box_;\n  SectorVariables inhomogeneous_terms_{1};\n  gsl::not_null<const ImplicitEquation<SectorVariables>*> implicit_equation_;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable SectorVariables most_recent_sector_variables_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable tuples::tagged_tuple_from_typelist<\n      tmpl::transform<all_mutators, tmpl::bind<RanMutator, tmpl::_1>>>\n      completed_mutators_{};\n};\n}  // namespace solve_implicit_sector_detail\n\ntemplate <typename SystemVariablesTag, typename ImplicitSector>\nvoid SolveImplicitSector<SystemVariablesTag, ImplicitSector>::apply_impl(\n    const gsl::not_null<SystemVariables*> system_variables,\n    const gsl::not_null<Scalar<DataVector>*> solve_failures,\n    const ImexTimeStepper& time_stepper, const TimeDelta& time_step,\n    const TimeSteppers::History<SectorVariables>& implicit_history,\n    const Mode implicit_solve_mode, const double implicit_solve_tolerance,\n    const EvolutionDataTuple& joined_evolution_data) {\n  get(*solve_failures) = 0.0;\n\n  const auto evolution_data = split_tuple<\n      tmpl::transform<evolution_data_tags, tmpl::bind<tmpl::size, tmpl::_1>>>(\n      joined_evolution_data);\n  const auto& initial_guess_arguments = std::get<0>(evolution_data);\n\n  const solve_implicit_sector_detail::ImplicitEquation<SectorVariables>\n      equation(system_variables, time_stepper, time_step, implicit_history,\n               initial_guess_arguments, tmpl::type_<ImplicitSector>{});\n  if (not equation.solve_needed()) {\n    return;\n  }\n\n  const size_t number_of_grid_points = get(*solve_failures).size();\n  // Only allocated if used.\n  Matrix semi_implicit_jacobian_matrix{};\n  // Only allocated if used.\n  std::vector<int> lapack_scratch{};\n\n  bool solve_succeeded = false;\n  tmpl::for_each<\n      typename ImplicitSector::solve_attempts>([&](auto solve_attempt_v) {\n    using solve_attempt = tmpl::type_from<decltype(solve_attempt_v)>;\n    if (solve_succeeded) {\n      return;\n    }\n    solve_succeeded = true;\n    constexpr bool have_fallback =\n        not std::is_same_v<solve_attempt,\n                           tmpl::back<typename ImplicitSector::solve_attempts>>;\n    constexpr auto attempt_number =\n        tmpl::index_of<typename ImplicitSector::solve_attempts,\n                       solve_attempt>::value;\n    // Entry 0 is for the initial guess.\n    const auto& attempt_evolution_data =\n        std::get<attempt_number + 1>(evolution_data);\n    solve_implicit_sector_detail::ImplicitSolver<ImplicitSector, solve_attempt>\n        solver(equation, attempt_evolution_data);\n\n    for (size_t point = 0; point < number_of_grid_points; ++point) {\n      if (get(*solve_failures)[point] < attempt_number) {\n        continue;\n      }\n      if (equation.initial_guess_result(point) == GuessResult::ExactSolution) {\n        // Initial guess was written into the evolution DataBox\n        // when it was computed.\n        continue;\n      }\n\n      // Dimension of the vector space the (non)linear solve is performed in.\n      constexpr size_t solve_dimension =\n          SectorVariables::number_of_independent_components;\n      std::array<double, solve_dimension> pointwise_vars_array;\n      SectorVariables pointwise_vars(pointwise_vars_array.data(),\n                                     pointwise_vars_array.size());\n      solver.set_index(point);\n      std::array<double, solve_dimension> initial_guess;\n      {\n        SectorVariables guess_vars(initial_guess.data(), initial_guess.size());\n        extract_point(make_not_null(&guess_vars),\n                      system_variables->template reference_subset<\n                          typename SectorVariables::tags_list>(),\n                      point);\n      }\n      switch (implicit_solve_mode) {\n        case Mode::Implicit: {\n          const size_t max_iterations = 100;\n          try {\n            pointwise_vars_array = RootFinder::gsl_multiroot(\n                solver, initial_guess,\n                RootFinder::StoppingConditions::Residual(\n                    implicit_solve_tolerance),\n                max_iterations);\n          } catch (const convergence_error&) {\n            if constexpr (have_fallback) {\n              ++get(*solve_failures)[point];\n              solve_succeeded = false;\n              continue;\n            } else {\n              throw;\n            }\n          }\n          break;\n        }\n        case Mode::SemiImplicit: {\n          std::array<double, solve_dimension> correction_array =\n              solver(initial_guess);\n          DataVector correction(correction_array.data(),\n                                correction_array.size());\n          correction *= -1.0;\n          const std::array<std::array<double, solve_dimension>, solve_dimension>\n              semi_implicit_jacobian = solver.jacobian(initial_guess);\n          // Copy into the dynamically allocated Matrix required by\n          // the LAPACK wrapper.\n          semi_implicit_jacobian_matrix = semi_implicit_jacobian;\n          // Allocate scratch buffer (storing pivots from the\n          // decomposition).  This does nothing after the first point.\n          lapack_scratch.resize(solve_dimension);\n          const int lapack_info = lapack::general_matrix_linear_solve(\n              &correction, &lapack_scratch, &semi_implicit_jacobian_matrix);\n          if (lapack_info != 0) {\n            if (lapack_info < 0) {\n              ERROR(\"LAPACK invalid argument: \" << -lapack_info);\n            } else {\n              if constexpr (have_fallback) {\n                ++get(*solve_failures)[point];\n                solve_succeeded = false;\n                continue;\n              } else {\n                ERROR(\"Semi-implicit inversion was singular at\\n\"\n                      << pointwise_vars);\n              }\n            }\n          }\n          pointwise_vars_array = initial_guess + correction_array;\n          break;\n        }\n        default:\n          ERROR(\"Invalid implicit mode\");\n      }\n\n      // Write the result into the evolution variables.\n      auto sector_reference = system_variables->template reference_subset<\n          typename SectorVariables::tags_list>();\n      overwrite_point(make_not_null(&sector_reference), pointwise_vars, point);\n    }\n  });\n}\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ImplicitHistory.hpp\n  Jacobian.hpp\n  Mode.hpp\n  NamespaceDocs.hpp\n  OptionGroup.hpp\n  SolveFailures.hpp\n  SolveTolerance.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Imex/Tags/ImplicitHistory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Time/History.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace imex::Tags {\n/// Tag for the history of one of the implicit sectors of an IMEX\n/// system.\ntemplate <typename ImplicitSector>\nstruct ImplicitHistory : db::SimpleTag {\n  static_assert(\n      tt::assert_conforms_to_v<ImplicitSector, protocols::ImplicitSector>);\n  using type =\n      TimeSteppers::History<Variables<typename ImplicitSector::tensors>>;\n};\n}  // namespace imex::Tags\n"
  },
  {
    "path": "src/Evolution/Imex/Tags/Jacobian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/OuterProduct.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n\nnamespace imex {\nnamespace Tags {\n/*!\n * Tag for the derivative of an implicit source (`Dependent`) with\n * respect to an implicit variable (`Independent`).\n *\n * The independent indices are given first, so\n * \\f{equation}\n *   \\frac{S(U_{j...})}{U_{i...}} = J_{i\\ldots,j\\ldots}\n * \\f}\n * with appropriate upper and lower indices.\n */\ntemplate <typename Independent, typename Dependent>\nstruct Jacobian : db::SimpleTag {\n  static_assert(std::is_same_v<typename Independent::type::value_type,\n                               typename Dependent::type::value_type>);\n  using independent = Independent;\n  using dependent = Dependent;\n\n private:\n  using Denominator =\n      TensorMetafunctions::change_all_valences<typename Independent::type>;\n\n public:\n  using type = OuterProductResultTensor<\n      typename Dependent::type::value_type, typename Denominator::symmetry,\n      typename Denominator::index_list, typename Dependent::type::symmetry,\n      typename Dependent::type::index_list>;\n};\n}  // namespace Tags\n\n/// Create a list of all jacobian tags for the dependence of \\p\n/// DependentList on \\p IndependentList.\n///\n/// This can be used to construct the argument for a `Variables` from\n/// the `tags_list`s of two other `Variables`.\ntemplate <typename IndependentList, typename DependentList>\nusing jacobian_tags = tmpl::join<tmpl::transform<\n    IndependentList,\n    tmpl::lazy::transform<\n        tmpl::pin<DependentList>,\n        tmpl::defer<tmpl::bind<::imex::Tags::Jacobian, tmpl::parent<tmpl::_1>,\n                               tmpl::_1>>>>>;\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/Tags/Mode.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/Imex/Mode.hpp\"\n#include \"Evolution/Imex/Tags/OptionGroup.hpp\"\n#include \"Options/Options.hpp\"\n\nnamespace imex {\nnamespace OptionTags {\n/// Tag for IMEX implementation to use\nstruct Mode {\n  static constexpr Options::String help{\"IMEX implementation to use\"};\n  using type = ::imex::Mode;\n  using group = Group;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// Tag for IMEX implementation to use\nstruct Mode : db::SimpleTag {\n  using type = ::imex::Mode;\n  using option_tags = tmpl::list<::imex::OptionTags::Mode>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& mode) { return mode; }\n};\n}  // namespace Tags\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/Tags/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace imex {\n/// Tags for IMEX\nnamespace Tags {}\n/// Input-file options for IMEX\nnamespace OptionTags {}\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Imex/Tags/OptionGroup.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace imex::OptionTags {\n/// Option group for IMEX options\nstruct Group {\n  static std::string name() { return \"Imex\"; }\n  static constexpr Options::String help{\"Options for IMEX evolution\"};\n  using group = evolution::OptionTags::Group;\n};\n}  // namespace imex::OptionTags\n"
  },
  {
    "path": "src/Evolution/Imex/Tags/SolveFailures.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace imex::Tags {\n/*!\n * Tag for a count of the pointwise implicit solve failures during the\n * most recent solve.  A value of 0 means the solve succeeded, a value\n * of 1 means the first fallback succeeded, and so on.\n */\ntemplate <typename Sector>\nstruct SolveFailures : db::SimpleTag {\n  static std::string name() {\n    return \"SolveFailures(\" + pretty_type::name<Sector>() + \")\";\n  }\n  using type = Scalar<DataVector>;\n};\n}  // namespace imex::Tags\n"
  },
  {
    "path": "src/Evolution/Imex/Tags/SolveTolerance.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/Imex/Tags/OptionGroup.hpp\"\n#include \"Options/Options.hpp\"\n\nnamespace imex {\nnamespace OptionTags {\nstruct SolveTolerance {\n  static constexpr Options::String help =\n      \"Absolute tolerance for IMEX solve (only used in Implicit mode)\";\n  using type = double;\n  using group = Group;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\nstruct SolveTolerance : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<::imex::OptionTags::SolveTolerance>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& tolerance) { return tolerance; }\n};\n}  // namespace Tags\n}  // namespace imex\n"
  },
  {
    "path": "src/Evolution/Initialization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  DgDomain.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ConservativeSystem.hpp\n  DgDomain.hpp\n  Evolution.hpp\n  InitialData.hpp\n  Limiter.hpp\n  NonconservativeSystem.hpp\n  SetVariables.hpp\n  Tags.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Initialization/ConservativeSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"  // Needs to be included somewhere and here seems most natural.\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain {\nnamespace Tags {\ntemplate <size_t VolumeDim, typename Frame>\nstruct Coordinates;\ntemplate <size_t VolumeDim>\nstruct Mesh;\n}  // namespace Tags\n}  // namespace domain\n\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace Initialization {\nnamespace Actions {\n/// \\ingroup InitializationGroup\n/// \\brief Allocate variables needed for evolution of conservative systems\n///\n/// Uses:\n/// - DataBox:\n///   * `Tags::Mesh<Dim>`\n///\n/// DataBox changes:\n/// - Adds:\n///   * System::variables_tag\n///   * db::add_tag_prefix<Tags::Flux, System::variables_tag>\n///   * db::add_tag_prefix<Tags::Source, System::variables_tag>\n///\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename System>\nstruct ConservativeSystem {\n private:\n  static constexpr size_t dim = System::volume_dim;\n\n  using variables_tag = typename System::variables_tag;\n\n  template <typename LocalSystem,\n            bool = LocalSystem::has_primitive_and_conservative_vars>\n  struct simple_tags_impl {\n    using type = tmpl::list<variables_tag>;\n  };\n\n  template <typename LocalSystem>\n  struct simple_tags_impl<LocalSystem, true> {\n    using type =\n        tmpl::list<variables_tag, typename System::primitive_variables_tag>;\n  };\n\n public:\n  using simple_tags = typename simple_tags_impl<System>::type;\n\n  using compute_tags = db::AddComputeTags<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t num_grid_points =\n        db::get<domain::Tags::Mesh<dim>>(box).number_of_grid_points();\n    typename variables_tag::type vars(num_grid_points);\n\n    if constexpr (System::has_primitive_and_conservative_vars) {\n      using PrimitiveVars = typename System::primitive_variables_tag::type;\n\n      PrimitiveVars primitive_vars{\n          db::get<domain::Tags::Mesh<dim>>(box).number_of_grid_points()};\n      Initialization::mutate_assign<simple_tags>(\n          make_not_null(&box), std::move(vars), std::move(primitive_vars));\n    } else {\n      Initialization::mutate_assign<simple_tags>(make_not_null(&box),\n                                                 std::move(vars));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Initialization\n"
  },
  {
    "path": "src/Evolution/Initialization/DgDomain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Initialization/DgDomain.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(r, data)                                              \\\n  template std::unique_ptr<                                               \\\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, DIM(data)>> \\\n      domain::make_coordinate_map_base<                                   \\\n          Frame::Grid, Frame::Inertial,                                   \\\n          domain::CoordinateMaps::Identity<DIM(data)>>(                   \\\n          domain::CoordinateMaps::Identity<DIM(data)> &&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Initialization/DgDomain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"ControlSystem/Tags/FunctionsOfTimeInitialize.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/MinimumGridSpacing.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/TagsDomain.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Tags/ArrayIndex.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace evolution::dg::Initialization {\n\n/// \\ingroup InitializationGroup\n/// \\brief Initialize items related to the basic structure of the element\n///\n/// \\details See the type aliases defined below for what items are added to the\n/// GlobalCache and DataBox and how they are initialized\n\ntemplate <typename Metavariables, bool UseControlSystems = false>\nstruct Domain {\n  static constexpr size_t dim = Metavariables::volume_dim;\n\n  /// Tags for constant items added to the GlobalCache.  These items are\n  /// initialized from input file options.\n  using const_global_cache_tags = tmpl::list<::domain::Tags::Domain<dim>>;\n\n  /// Tags for mutable items added to the GlobalCache.  These items are\n  /// initialized from input file options.\n  using mutable_global_cache_tags = tmpl::list<tmpl::conditional_t<\n      UseControlSystems, ::control_system::Tags::FunctionsOfTimeInitialize,\n      ::domain::Tags::FunctionsOfTimeInitialize>>;\n\n  /// Tags for simple DataBox items that are initialized from input file options\n  using simple_tags_from_options =\n      tmpl::list<::domain::Tags::InitialExtents<dim>,\n                 ::domain::Tags::InitialRefinementLevels<dim>,\n                 evolution::dg::Tags::Quadrature>;\n\n  /// Tags for simple DataBox items that are default initialized.\n  using default_initialized_simple_tags = tmpl::list<>;\n\n  /// Tags for items fetched by the DataBox and passed to the apply function\n  using argument_tags =\n      tmpl::append<const_global_cache_tags, simple_tags_from_options,\n                   tmpl::list<::Parallel::Tags::ArrayIndex<ElementId<dim>>>>;\n\n  /// Tags for items in the DataBox that are mutated by the apply function\n  using return_tags =\n      tmpl::list<::domain::Tags::Mesh<dim>, ::domain::Tags::Element<dim>,\n                 ::domain::Tags::ElementMap<dim, Frame::Grid>,\n                 ::domain::CoordinateMaps::Tags::CoordinateMap<dim, Frame::Grid,\n                                                               Frame::Inertial>,\n                 ::domain::Tags::NeighborMesh<dim>>;\n\n  /// Tags for mutable DataBox items that are either default initialized or\n  /// initialized by the apply function\n  using simple_tags =\n      tmpl::append<default_initialized_simple_tags, return_tags>;\n\n  /// Tags for immutable DataBox items (compute items or reference items) added\n  /// to the DataBox.\n  using compute_tags = tmpl::list<\n      ::domain::Tags::LogicalCoordinates<dim>,\n      // Compute tags for Frame::Grid quantities\n      ::domain::Tags::MappedCoordinates<\n          ::domain::Tags::ElementMap<dim, Frame::Grid>,\n          ::domain::Tags::Coordinates<dim, Frame::ElementLogical>>,\n      ::domain::Tags::InverseJacobianCompute<\n          ::domain::Tags::ElementMap<dim, Frame::Grid>,\n          ::domain::Tags::Coordinates<dim, Frame::ElementLogical>>,\n      // Compute tag to retrieve functions of time from global cache.\n      Parallel::Tags::FromGlobalCache<\n          tmpl::conditional_t<UseControlSystems,\n                              ::control_system::Tags::FunctionsOfTimeInitialize,\n                              ::domain::Tags::FunctionsOfTimeInitialize>,\n          Metavariables>,\n      // Compute tags for Frame::Inertial quantities\n      ::domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<\n          ::domain::CoordinateMaps::Tags::CoordinateMap<dim, Frame::Grid,\n                                                        Frame::Inertial>>,\n\n      ::domain::Tags::InertialFromGridCoordinatesCompute<dim>,\n      ::domain::Tags::ElementToInertialInverseJacobian<dim>,\n      ::domain::Tags::DetInvJacobianCompute<dim, Frame::ElementLogical,\n                                            Frame::Inertial>,\n      ::domain::Tags::InertialMeshVelocityCompute<dim>,\n      evolution::domain::Tags::DivMeshVelocityCompute<dim>,\n      // Compute tags for other mesh quantities\n      ::domain::Tags::MinimumGridSpacingCompute<dim, Frame::Inertial>>;\n\n  /// Given the items fetched from a DataBox by the argument_tags, mutate\n  /// the items in the DataBox corresponding to return_tags\n  static void apply(\n      const gsl::not_null<Mesh<dim>*> mesh,\n      const gsl::not_null<Element<dim>*> element,\n      const gsl::not_null<ElementMap<dim, Frame::Grid>*> element_map,\n      const gsl::not_null<std::unique_ptr<\n          ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, dim>>*>\n          grid_to_inertial_map,\n      const gsl::not_null<DirectionalIdMap<dim, Mesh<dim>>*> neighbor_mesh,\n      const ::Domain<dim>& domain,\n      const std::vector<std::array<size_t, dim>>& initial_extents,\n      const std::vector<std::array<size_t, dim>>& initial_refinement,\n      const Spectral::Quadrature& i1_quadrature,\n      const ElementId<dim>& element_id) {\n    *element = ::domain::create_initial_element(element_id, domain.blocks(),\n                                                initial_refinement);\n    const Spectral::Basis i1_basis{Spectral::Basis::Legendre};\n    *mesh = ::domain::create_initial_mesh(initial_extents, *element, i1_basis,\n                                          i1_quadrature);\n    const auto& my_block = domain.blocks()[element_id.block_id()];\n    *element_map = ElementMap<dim, Frame::Grid>{element_id, my_block};\n\n    if (my_block.is_time_dependent()) {\n      *grid_to_inertial_map =\n          my_block.moving_mesh_grid_to_inertial_map().get_clone();\n    } else {\n      *grid_to_inertial_map =\n          ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n              ::domain::CoordinateMaps::Identity<dim>{});\n    }\n\n    for (const auto& [direction, neighbors] : element->neighbors()) {\n      for (const auto& neighbor : neighbors) {\n        const auto& neighbor_block = domain.blocks()[neighbor.block_id()];\n        if (neighbors.are_conforming()) {\n          const auto& neighbor_orientation = neighbors.orientation(neighbor);\n          neighbor_mesh->emplace(\n              DirectionalId{direction, neighbor},\n              neighbor_orientation.inverse_map()(::domain::create_initial_mesh(\n                  initial_extents, neighbor_block, neighbor, i1_basis,\n                  i1_quadrature)));\n        } else {\n          neighbor_mesh->emplace(\n              DirectionalId{direction, neighbor},\n              ::domain::create_initial_mesh(initial_extents, neighbor_block,\n                                            neighbor, i1_basis, i1_quadrature));\n        }\n      }\n    }\n  }\n};\n\n/// \\brief Initialize/update items related to coordinate maps after an AMR\n/// change\ntemplate <size_t Dim>\nstruct ProjectDomain : tt::ConformsTo<amr::protocols::Projector> {\n  using return_tags = tmpl::list<::domain::Tags::ElementMap<Dim, Frame::Grid>,\n                                 ::domain::CoordinateMaps::Tags::CoordinateMap<\n                                     Dim, Frame::Grid, Frame::Inertial>>;\n  using argument_tags =\n      tmpl::list<::domain::Tags::Domain<Dim>, ::domain::Tags::Element<Dim>>;\n\n  static void apply(\n      const gsl::not_null<ElementMap<Dim, Frame::Grid>*> /*element_map*/,\n      const gsl::not_null<std::unique_ptr<\n          ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>>*>\n      /*grid_to_inertial_map*/,\n      const ::Domain<Dim>& /*domain*/, const Element<Dim>& /*element*/,\n      const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {\n    // Do not change anything for p-refinement\n  }\n\n  template <typename ParentOrChildrenItemsType>\n  static void apply(\n      const gsl::not_null<ElementMap<Dim, Frame::Grid>*> element_map,\n      const gsl::not_null<std::unique_ptr<\n          ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>>*>\n          grid_to_inertial_map,\n      const ::Domain<Dim>& domain, const Element<Dim>& element,\n      const ParentOrChildrenItemsType& /*parent_or_children_items*/) {\n    const ElementId<Dim>& element_id = element.id();\n    const auto& my_block = domain.blocks()[element_id.block_id()];\n    *element_map = ElementMap<Dim, Frame::Grid>{element_id, my_block};\n    if (my_block.is_time_dependent()) {\n      *grid_to_inertial_map =\n          my_block.moving_mesh_grid_to_inertial_map().get_clone();\n    } else {\n      *grid_to_inertial_map =\n          ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n              ::domain::CoordinateMaps::Identity<Dim>{});\n    }\n  }\n};\n}  // namespace evolution::dg::Initialization\n"
  },
  {
    "path": "src/Evolution/Initialization/Evolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/ChooseLtsStepSize.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/StepNumberWithinSlab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel::Tags {\ntemplate <typename Index>\nstruct ArrayIndex;\n}  // namespace Parallel::Tags\nnamespace Tags {\ntemplate <typename Tag>\nstruct StepperErrors;\n}  // namespace Tags\nnamespace amr::Tags {\ntemplate <size_t VolumeDim>\nstruct Info;\n}  // namespace amr::Tags\nnamespace domain::Tags {\ntemplate <size_t VolumeDim>\nstruct Element;\ntemplate <size_t VolumeDim>\nstruct Mesh;\n}  // namespace domain::Tags\nnamespace evolution::dg::Tags {\ntemplate <size_t Dim>\nstruct MortarInfo;\n}  // namespace evolution::dg::Tags\n/// \\endcond\n\nnamespace Initialization {\n\nnamespace detail {\ninline Time initial_time(const bool time_runs_forward,\n                         const double initial_time_value,\n                         const double initial_slab_size) {\n  const Slab initial_slab =\n      time_runs_forward\n          ? Slab::with_duration_from_start(initial_time_value,\n                                           initial_slab_size)\n          : Slab::with_duration_to_end(initial_time_value, initial_slab_size);\n  return time_runs_forward ? initial_slab.start() : initial_slab.end();\n}\n\ntemplate <typename TimeStepper>\nvoid set_next_time_step_id(const gsl::not_null<TimeStepId*> next_time_step_id,\n                           const Time& initial_time,\n                           const bool time_runs_forward,\n                           const TimeStepper& time_stepper) {\n  *next_time_step_id = TimeStepId(\n      time_runs_forward,\n      -static_cast<int64_t>(time_stepper.number_of_past_steps()), initial_time);\n}\n}  // namespace detail\n\n/// \\ingroup InitializationGroup\n/// \\brief Initialize items related to time stepping\n///\n/// \\details See the type aliases defined below for what items are added to the\n/// GlobalCache and DataBox and how they are initialized\n///\n/// Since the evolution has not started yet, initialize the state\n/// _before_ the initial time. So `Tags::TimeStepId` is undefined at this point,\n/// and `Tags::Next<Tags::TimeStepId>` is the initial time.\ntemplate <typename Metavariables, typename TimeStepperBase>\nstruct TimeStepping {\n  /// Tags for constant items added to the GlobalCache.  These items are\n  /// initialized from input file options.\n  using const_global_cache_tags =\n      tmpl::list<::Tags::ConcreteTimeStepper<TimeStepperBase>>;\n\n  /// Tags for mutable items added to the GlobalCache.  These items are\n  /// initialized from input file options.\n  using mutable_global_cache_tags = tmpl::list<>;\n\n  /// Tags for items fetched by the DataBox and passed to the apply function\n  using argument_tags =\n      tmpl::list<::Tags::Time, Tags::InitialTimeDelta,\n                 Tags::InitialSlabSize<TimeStepperBase::local_time_stepping>,\n                 ::Tags::ConcreteTimeStepper<TimeStepperBase>>;\n\n  /// Tags for simple DataBox items that are initialized from input file options\n  using simple_tags_from_options =\n      tmpl::list<::Tags::Time, Tags::InitialTimeDelta,\n                 Tags::InitialSlabSize<TimeStepperBase::local_time_stepping>>;\n\n  /// Tags for simple DataBox items that are default initialized.\n  using default_initialized_simple_tags =\n      tmpl::push_back<StepChoosers::step_chooser_simple_tags<\n                          Metavariables, TimeStepperBase::local_time_stepping>,\n                      ::Tags::TimeStepId, ::Tags::StepNumberWithinSlab,\n                      ::Tags::AdaptiveSteppingDiagnostics>;\n\n  /// Tags for items in the DataBox that are mutated by the apply function\n  using return_tags =\n      tmpl::list<::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n                 ::Tags::ChangeSlabSize::SlabSizeGoal>;\n\n  /// Tags for mutable DataBox items that are either default initialized or\n  /// initialized by the apply function\n  using simple_tags =\n      tmpl::append<default_initialized_simple_tags, return_tags>;\n\n  /// Tags for immutable DataBox items (compute items or reference items) added\n  /// to the DataBox.\n  using compute_tags = time_stepper_ref_tags<TimeStepperBase>;\n\n  /// Given the items fetched from a DataBox by the argument_tags when using\n  /// LTS, mutate the items in the DataBox corresponding to return_tags\n  static void apply(const gsl::not_null<TimeStepId*> next_time_step_id,\n                    const gsl::not_null<TimeDelta*> time_step,\n                    const gsl::not_null<double*> slab_size_goal,\n                    const double initial_time_value,\n                    const double initial_dt_value,\n                    const double initial_slab_size,\n                    const LtsTimeStepper& time_stepper) {\n    const bool time_runs_forward = initial_dt_value > 0.0;\n    const Time initial_time = detail::initial_time(\n        time_runs_forward, initial_time_value, initial_slab_size);\n    detail::set_next_time_step_id(next_time_step_id, initial_time,\n                                  time_runs_forward, time_stepper);\n    *time_step = choose_lts_step_size(initial_time, initial_dt_value);\n    *slab_size_goal =\n        time_runs_forward ? initial_slab_size : -initial_slab_size;\n  }\n\n  /// Given the items fetched from a DataBox by the argument_tags, when not\n  /// using LTS, mutate the items in the DataBox corresponding to return_tags\n  static void apply(const gsl::not_null<TimeStepId*> next_time_step_id,\n                    const gsl::not_null<TimeDelta*> time_step,\n                    const gsl::not_null<double*> slab_size_goal,\n                    const double initial_time_value,\n                    const double initial_dt_value,\n                    const double initial_slab_size,\n                    const TimeStepper& time_stepper) {\n    const bool time_runs_forward = initial_dt_value > 0.0;\n    const Time initial_time = detail::initial_time(\n        time_runs_forward, initial_time_value, initial_slab_size);\n    detail::set_next_time_step_id(next_time_step_id, initial_time,\n                                  time_runs_forward, time_stepper);\n    *time_step = (time_runs_forward ? 1 : -1) * initial_time.slab().duration();\n    *slab_size_goal =\n        time_runs_forward ? initial_slab_size : -initial_slab_size;\n  }\n};\n\n/// \\brief Initialize/update items related to time stepping after an AMR change\ntemplate <size_t Dim>\nstruct ProjectTimeStepping : tt::ConformsTo<amr::protocols::Projector> {\n  using return_tags =\n      tmpl::list<::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n                 ::Tags::TimeStep, ::Tags::Time, ::Tags::StepNumberWithinSlab,\n                 ::Tags::AdaptiveSteppingDiagnostics,\n                 ::Tags::ChangeSlabSize::SlabSizeGoal>;\n  using argument_tags = tmpl::list<Parallel::Tags::ArrayIndex<ElementId<Dim>>>;\n\n  static void apply(\n      const gsl::not_null<TimeStepId*> /*time_step_id*/,\n      const gsl::not_null<TimeStepId*> /*next_time_step_id*/,\n      const gsl::not_null<TimeDelta*> /*time_step*/,\n      const gsl::not_null<double*> /*time*/,\n      const gsl::not_null<uint64_t*> /*step_number_within_slab*/,\n      const gsl::not_null<AdaptiveSteppingDiagnostics*>\n      /*adaptive_stepping_diagnostics*/,\n      const gsl::not_null<double*> /*slab_size_goal*/,\n      const ElementId<Dim>& /*element_id*/,\n      const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {\n    // Do not change anything for p-refinement\n  }\n\n  template <typename... Tags>\n  static void apply(const gsl::not_null<TimeStepId*> time_step_id,\n                    const gsl::not_null<TimeStepId*> next_time_step_id,\n                    const gsl::not_null<TimeDelta*> time_step,\n                    const gsl::not_null<double*> time,\n                    const gsl::not_null<uint64_t*> step_number_within_slab,\n                    const gsl::not_null<AdaptiveSteppingDiagnostics*>\n                        adaptive_stepping_diagnostics,\n                    const gsl::not_null<double*> slab_size_goal,\n                    const ElementId<Dim>& element_id,\n                    const tuples::TaggedTuple<Tags...>& parent_items) {\n    *time_step_id = get<::Tags::TimeStepId>(parent_items);\n    *next_time_step_id = get<::Tags::Next<::Tags::TimeStepId>>(parent_items);\n    *time_step = get<::Tags::TimeStep>(parent_items);\n    *time = get<::Tags::Time>(parent_items);\n    *slab_size_goal = get<::Tags::ChangeSlabSize::SlabSizeGoal>(parent_items);\n    *step_number_within_slab = get<::Tags::StepNumberWithinSlab>(parent_items);\n\n    // Since AdaptiveSteppingDiagnostics are reduced over all elements, we\n    // set the slab quantities to the same value over all children, and the\n    // step quantities to belong to the first child\n    const auto& parent_diagnostics =\n        get<::Tags::AdaptiveSteppingDiagnostics>(parent_items);\n    const auto& parent_amr_flags =\n        get<amr::Tags::Info<Dim>>(parent_items).flags;\n    const auto& parent_id =\n        get<Parallel::Tags::ArrayIndex<ElementId<Dim>>>(parent_items);\n    auto children_ids = amr::ids_of_children(parent_id, parent_amr_flags);\n    if (element_id == children_ids.front()) {\n      *adaptive_stepping_diagnostics = parent_diagnostics;\n    } else {\n      adaptive_stepping_diagnostics->number_of_slabs =\n          parent_diagnostics.number_of_slabs;\n      adaptive_stepping_diagnostics->number_of_slab_size_changes =\n          parent_diagnostics.number_of_slab_size_changes;\n    }\n  }\n\n  template <typename... Tags>\n  static void apply(\n      const gsl::not_null<TimeStepId*> time_step_id,\n      const gsl::not_null<TimeStepId*> next_time_step_id,\n      const gsl::not_null<TimeDelta*> time_step,\n      const gsl::not_null<double*> time,\n      const gsl::not_null<uint64_t*> step_number_within_slab,\n      const gsl::not_null<AdaptiveSteppingDiagnostics*>\n          adaptive_stepping_diagnostics,\n      const gsl::not_null<double*> slab_size_goal,\n      const ElementId<Dim>& /*element_id*/,\n      const std::unordered_map<ElementId<Dim>, tuples::TaggedTuple<Tags...>>&\n          children_items) {\n    const auto slowest_child =\n        alg::min_element(children_items, [](const auto& a, const auto& b) {\n          const auto& time_step_a = get<::Tags::TimeStep>(a.second);\n          const auto& time_step_b = get<::Tags::TimeStep>(b.second);\n          ASSERT(time_step_a.is_positive() == time_step_b.is_positive(),\n                 \"Elements are not taking time steps in the same direction!\");\n          return time_step_a.is_positive() ? (time_step_a < time_step_b)\n                                           : (time_step_a > time_step_b);\n        });\n    const auto& slowest_child_items = (*slowest_child).second;\n    *time_step_id = get<::Tags::TimeStepId>(slowest_child_items);\n    *next_time_step_id =\n        get<::Tags::Next<::Tags::TimeStepId>>(slowest_child_items);\n    *time_step = get<::Tags::TimeStep>(slowest_child_items);\n    *time = get<::Tags::Time>(slowest_child_items);\n    *slab_size_goal =\n        get<::Tags::ChangeSlabSize::SlabSizeGoal>(slowest_child_items);\n    *step_number_within_slab =\n        get<::Tags::StepNumberWithinSlab>(slowest_child_items);\n    const auto& slowest_child_diagnostics =\n        get<::Tags::AdaptiveSteppingDiagnostics>(slowest_child_items);\n\n    adaptive_stepping_diagnostics->number_of_slabs =\n        slowest_child_diagnostics.number_of_slabs;\n    adaptive_stepping_diagnostics->number_of_slab_size_changes =\n        slowest_child_diagnostics.number_of_slab_size_changes;\n    for (const auto& [_, child_items] : children_items) {\n      *adaptive_stepping_diagnostics +=\n          get<::Tags::AdaptiveSteppingDiagnostics>(child_items);\n    }\n  }\n};\n\n/// \\ingroup InitializationGroup\n/// \\brief Initialize time-stepper items\n///\n/// DataBox changes:\n/// - Adds:\n///   * `db::add_tag_prefix<Tags::dt, variables_tag>`\n///   * `Tags::HistoryEvolvedVariables<variables_tag>`\n/// - Removes: nothing\n/// - Modifies: nothing\n///\n/// \\note HistoryEvolvedVariables is allocated, but needs to be initialized\ntemplate <typename Metavariables>\nstruct TimeStepperHistory {\n  static constexpr size_t dim = Metavariables::volume_dim;\n  using variables_tag = typename Metavariables::system::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using simple_tags_from_options = tmpl::list<>;\n  using simple_tags =\n      tmpl::list<dt_variables_tag,\n                 ::Tags::HistoryEvolvedVariables<variables_tag>>;\n  using compute_tags = tmpl::list<>;\n\n  using argument_tags =\n      tmpl::list<::Tags::TimeStepper<TimeStepper>, domain::Tags::Mesh<dim>>;\n  using return_tags = simple_tags;\n\n  static void apply(\n      const gsl::not_null<typename dt_variables_tag::type*> dt_vars,\n      const gsl::not_null<TimeSteppers::History<typename variables_tag::type>*>\n          history,\n      const TimeStepper& time_stepper, const Mesh<dim>& mesh) {\n    // Will be overwritten before use\n    dt_vars->initialize(mesh.number_of_grid_points());\n\n    // All steppers we have that need to start at low order require\n    // one additional point per order, so this is the order that\n    // requires no initial past steps.\n    const size_t starting_order =\n        visit(\n            []<typename Tag>(\n                const std::pair<tmpl::type_<Tag>, typename Tag::type&&> order) {\n              if constexpr (std::is_same_v<Tag,\n                                           TimeSteppers::Tags::FixedOrder>) {\n                return order.second;\n              } else {\n                return order.second.minimum;\n              }\n            },\n            time_stepper.order()) -\n        time_stepper.number_of_past_steps();\n    history->integration_order(starting_order);\n  }\n};\n\n/// \\brief Initialize/update items related to time stepper history after an AMR\n/// change\n///\n/// \\note `Tags::TimeStep` and `Tags::Next<Tags::TimeStepId>` are not\n/// initially set by this projector.  They are only updated if the\n/// time stepper must be restarted because of LTS h-refinement.\ntemplate <typename Metavariables>\nstruct ProjectTimeStepperHistory : tt::ConformsTo<amr::protocols::Projector> {\n  static constexpr size_t dim = Metavariables::volume_dim;\n  using variables_tag = typename Metavariables::system::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  using history_tag = ::Tags::HistoryEvolvedVariables<variables_tag>;\n\n  using return_tags =\n      tmpl::list<dt_variables_tag, history_tag,\n                 ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep>;\n  using argument_tags = tmpl::list<\n      domain::Tags::Mesh<dim>, Parallel::Tags::ArrayIndex<ElementId<dim>>,\n      ::Tags::TimeStepper<TimeStepper>, evolution::dg::Tags::MortarInfo<dim>>;\n\n  static void apply(\n      const gsl::not_null<typename dt_variables_tag::type*> dt_vars,\n      const gsl::not_null<typename history_tag::type*> history,\n      const gsl::not_null<TimeStepId*> /*next_time_step_id*/,\n      const gsl::not_null<TimeDelta*> /*time_step*/, const Mesh<dim>& new_mesh,\n      const ElementId<dim>& /*element_id*/, const TimeStepper& /*time_stepper*/,\n      const DirectionalIdMap<dim, evolution::dg::MortarInfo<dim>>&\n      /*mortar_info*/,\n      const std::pair<Mesh<dim>, Element<dim>>& old_mesh_and_element) {\n    const auto& old_mesh = old_mesh_and_element.first;\n    if (old_mesh == new_mesh) {\n      return;  // mesh was not refined, so no projection needed\n    }\n    const auto projection_matrices =\n        Spectral::p_projection_matrices(old_mesh, new_mesh);\n    const auto& old_extents = old_mesh.extents();\n    history->map_entries(\n        [&projection_matrices, &old_extents](const auto entry) {\n          *entry = apply_matrices(projection_matrices, *entry, old_extents);\n        });\n    dt_vars->initialize(new_mesh.number_of_grid_points());\n  }\n\n  template <typename... Tags>\n  static void apply(\n      const gsl::not_null<typename dt_variables_tag::type*> dt_vars,\n      const gsl::not_null<typename history_tag::type*> history,\n      const gsl::not_null<TimeStepId*> next_time_step_id,\n      const gsl::not_null<TimeDelta*> time_step, const Mesh<dim>& new_mesh,\n      const ElementId<dim>& element_id, const TimeStepper& time_stepper,\n      const DirectionalIdMap<dim, evolution::dg::MortarInfo<dim>>& mortar_info,\n      const tuples::TaggedTuple<Tags...>& parent_items) {\n    dt_vars->initialize(new_mesh.number_of_grid_points());\n    ASSERT(element_id.refinement_levels() == make_array<dim>(0_st) or\n               not mortar_info.empty(),\n           \"Element has no neighbors but is not a full block\");\n    const auto time_stepping_policy = time_stepping_policy_for_h_refinement(\n        mortar_info, &get<evolution::dg::Tags::MortarInfo<dim>>(parent_items),\n        {});\n    switch (time_stepping_policy) {\n      case evolution::dg::TimeSteppingPolicy::Conservative: {\n        if (time_stepper.number_of_past_steps() != 0) {\n          ERROR_NO_TRACE(\n              \"Cannot perform h-refinement with LTS steppers requiring \"\n              \"initialization.\");\n        }\n        const auto integrator_order = time_stepper.order();\n        if (variants::holds_alternative<TimeSteppers::Tags::FixedOrder>(\n                integrator_order)) {\n          *history = typename history_tag::type{\n              get<TimeSteppers::Tags::FixedOrder>(integrator_order)};\n          return;\n        }\n        const auto start_order =\n            get<TimeSteppers::Tags::VariableOrder>(integrator_order).minimum;\n        *history = typename history_tag::type{start_order};\n\n        const auto reduced_step = restart_time_step(parent_items, start_order);\n        if (abs(reduced_step) < abs(*time_step)) {\n          *time_step = reduced_step;\n          *next_time_step_id = time_stepper.next_time_id(\n              get<::Tags::TimeStepId>(parent_items), *time_step);\n        }\n        break;\n      }\n      case evolution::dg::TimeSteppingPolicy::EqualRate: {\n        const auto& parent_id =\n            get<domain::Tags::Element<dim>>(parent_items).id();\n        const auto& parent_mesh = get<domain::Tags::Mesh<dim>>(parent_items);\n        const auto child_sizes = domain::child_size(element_id.segment_ids(),\n                                                    parent_id.segment_ids());\n        const auto projection_matrices =\n            Spectral::projection_matrix_parent_to_child(parent_mesh, new_mesh,\n                                                        child_sizes);\n        transform(history, get<history_tag>(parent_items),\n                  [&](const auto& source_entry) {\n                    return apply_matrices(projection_matrices, source_entry,\n                                          parent_mesh.extents());\n                  });\n        break;\n      }\n      default:\n        ERROR(\"Unhandled time-stepping policy: \" << time_stepping_policy);\n    }\n  }\n\n  template <typename... Tags>\n  static void apply(\n      const gsl::not_null<typename dt_variables_tag::type*> dt_vars,\n      const gsl::not_null<typename history_tag::type*> history,\n      const gsl::not_null<TimeStepId*> next_time_step_id,\n      const gsl::not_null<TimeDelta*> time_step, const Mesh<dim>& new_mesh,\n      const ElementId<dim>& element_id, const TimeStepper& time_stepper,\n      const DirectionalIdMap<dim, evolution::dg::MortarInfo<dim>>& mortar_info,\n      const std::unordered_map<ElementId<dim>, tuples::TaggedTuple<Tags...>>&\n          children_items) {\n    dt_vars->initialize(new_mesh.number_of_grid_points());\n    ASSERT(element_id.refinement_levels() == make_array<dim>(0_st) or\n               not mortar_info.empty(),\n           \"Element has no neighbors but is not a full block\");\n    const auto time_stepping_policy =\n        time_stepping_policy_for_h_refinement<Tags...>(mortar_info, {},\n                                                       {&children_items});\n    switch (time_stepping_policy) {\n      case evolution::dg::TimeSteppingPolicy::Conservative: {\n        if (time_stepper.number_of_past_steps() != 0) {\n          ERROR_NO_TRACE(\n              \"Cannot perform h-refinement with LTS steppers requiring \"\n              \"initialization.\");\n        }\n        const auto integrator_order = time_stepper.order();\n        if (variants::holds_alternative<TimeSteppers::Tags::FixedOrder>(\n                integrator_order)) {\n          *history = typename history_tag::type{\n              get<TimeSteppers::Tags::FixedOrder>(integrator_order)};\n          return;\n        }\n        const auto start_order =\n            get<TimeSteppers::Tags::VariableOrder>(integrator_order).minimum;\n        *history = typename history_tag::type{start_order};\n\n        for (const auto& [child, child_items] : children_items) {\n          const auto reduced_step = restart_time_step(child_items, start_order);\n          if (abs(reduced_step) < abs(*time_step)) {\n            *time_step = reduced_step;\n            *next_time_step_id = time_stepper.next_time_id(\n                get<::Tags::TimeStepId>(children_items.begin()->second),\n                *time_step);\n          }\n        }\n        break;\n      }\n      case evolution::dg::TimeSteppingPolicy::EqualRate: {\n        bool first_child = true;\n        for (const auto& [child_id, child_items] : children_items) {\n          const auto& child_mesh = get<domain::Tags::Mesh<dim>>(child_items);\n          const auto child_sizes = domain::child_size(child_id.segment_ids(),\n                                                      element_id.segment_ids());\n          const auto projection_matrices =\n              Spectral::projection_matrix_child_to_parent(child_mesh, new_mesh,\n                                                          child_sizes);\n          if (first_child) {\n            transform(history, get<history_tag>(child_items),\n                      [&](const auto& source_entry) {\n                        return apply_matrices(projection_matrices, source_entry,\n                                              child_mesh.extents());\n                      });\n            first_child = false;\n          } else {\n            transform_mutate(\n                history, get<history_tag>(child_items),\n                [&](const auto dest_entry, const auto& source_entry) {\n                  *dest_entry += apply_matrices(\n                      projection_matrices, source_entry, child_mesh.extents());\n                });\n          }\n        }\n        break;\n      }\n      default:\n        ERROR(\"Unhandled time-stepping policy: \" << time_stepping_policy);\n    }\n  }\n\n private:\n  template <typename... Tags>\n  static evolution::dg::TimeSteppingPolicy\n  time_stepping_policy_for_h_refinement(\n      const DirectionalIdMap<dim, evolution::dg::MortarInfo<dim>>&\n          element_infos,\n      const std::optional<gsl::not_null<\n          const DirectionalIdMap<dim, evolution::dg::MortarInfo<dim>>*>>&\n          parent_infos,\n      const std::optional<gsl::not_null<const std::unordered_map<\n          ElementId<dim>, tuples::TaggedTuple<Tags...>>*>>& children_items) {\n    std::optional<evolution::dg::TimeSteppingPolicy> policy{};\n    const auto process_infos =\n        [&policy](const DirectionalIdMap<dim, evolution::dg::MortarInfo<dim>>&\n                      infos) {\n          for (const auto& info : infos) {\n            const auto mortar_policy = info.second.time_stepping_policy();\n            ASSERT(not policy.has_value() or *policy == mortar_policy,\n                   \"Inconsistent policies: \"\n                       << *policy << \" and \" << mortar_policy\n                       << \"\\nWe currently require all mortars to have the \"\n                          \"same policy when doing h-refinement.  This might \"\n                          \"be relaxed when we add an LTS policy other than \"\n                          \"Conservative.\");\n            policy.emplace(mortar_policy);\n          }\n        };\n\n    process_infos(element_infos);\n    if (parent_infos.has_value()) {\n      process_infos(**parent_infos);\n    }\n    if (children_items.has_value()) {\n      if constexpr (sizeof...(Tags) > 0) {\n        for (const auto& child : **children_items) {\n          process_infos(\n              get<evolution::dg::Tags::MortarInfo<dim>>(child.second));\n        }\n      } else {\n        ERROR(\"Children but no child data\");\n      }\n    }\n\n    ASSERT(policy.has_value(),\n           \"Found no mortars, either before or after h-refinement.  A mortar \"\n           \"should have been created or destroyed, so this is not possible.\");\n    return *policy;\n  }\n\n  template <typename... Tags>\n  static TimeDelta restart_time_step(\n      const tuples::TaggedTuple<Tags...>& old_items, const size_t start_order) {\n    const auto& old_history = get<history_tag>(old_items);\n    if (old_history.integration_order() == start_order) {\n      return get<::Tags::TimeStep>(old_items);\n    }\n\n    const auto& time_step_id = get<::Tags::TimeStepId>(old_items);\n    const auto& errors = get<::Tags::StepperErrors<variables_tag>>(old_items);\n    if (not errors[1].has_value()) {\n      ERROR_NO_TRACE(\n          \"Evolutions performing h-refinement with variable-order local \"\n          \"time-stepping must use ErrorControl for step size choosing.\");\n    }\n    ASSERT(errors[1]->errors[start_order - 1].has_value(),\n           \"Start-order estimate not available.\");\n\n    // At this low an order, we are almost certainly step-size-limited\n    // by accuracy rather than stability, so ignore things like the\n    // change to the grid spacing.\n    return choose_lts_step_size(\n        time_step_id.step_time(),\n        errors[1]->step_size.value() *\n            pow(1.0 / std::max(*errors[1]->errors[start_order - 1], 1e-14),\n                1.0 / static_cast<double>(start_order)));\n  }\n};\n}  // namespace Initialization\n"
  },
  {
    "path": "src/Evolution/Initialization/InitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <utility>\n\n#include \"Evolution/TypeTraits.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::Initialization {\n/// Extract initial data either from analytic data or from an analytic\n/// solution at a specified time.\ntemplate <typename SolutionOrData, typename Coordinates, typename... Tags,\n          Requires<is_analytic_solution_v<SolutionOrData>> = nullptr>\ndecltype(auto) initial_data(const SolutionOrData& solution_or_data,\n                            Coordinates&& coordinates, const double time,\n                            const tmpl::list<Tags...> tags) {\n  return solution_or_data.variables(std::forward<Coordinates>(coordinates),\n                                    time, tags);\n}\n\n/// \\cond\ntemplate <typename SolutionOrData, typename Coordinates, typename... Tags,\n          Requires<is_analytic_data_v<SolutionOrData> or\n                   is_numeric_initial_data_v<SolutionOrData>> = nullptr>\ndecltype(auto) initial_data(const SolutionOrData& solution_or_data,\n                            Coordinates&& coordinates, const double /*time*/,\n                            const tmpl::list<Tags...> tags) {\n  return solution_or_data.variables(std::forward<Coordinates>(coordinates),\n                                    tags);\n}\n/// \\endcond\n}  // namespace evolution::Initialization\n"
  },
  {
    "path": "src/Evolution/Initialization/Limiter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Initialization {\nnamespace Actions {\n/// \\ingroup InitializationGroup\n/// \\brief Allocate items for minmod limiter\n///\n/// DataBox changes:\n/// - Adds:\n///   * `Tags::SizeOfElement<Dim>`\n///\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <size_t Dim>\nstruct Minmod {\n  using simple_tags = db::AddSimpleTags<>;\n  using compute_tags = tmpl::list<domain::Tags::SizeOfElementCompute<Dim>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Initialization\n"
  },
  {
    "path": "src/Evolution/Initialization/NonconservativeSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace Tags {\ntemplate <size_t VolumeDim>\nstruct Mesh;\n}  // namespace Tags\n}  // namespace domain\n\n/// \\endcond\n\nnamespace Initialization {\nnamespace Actions {\n/// \\ingroup InitializationGroup\n/// \\brief Allocate variables needed for evolution of nonconservative systems\n///\n/// Uses:\n/// - DataBox:\n///   * `Tags::Mesh<Dim>`\n///\n/// DataBox changes:\n/// - Adds:\n///   * System::variables_tag\n///\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename System>\nstruct NonconservativeSystem {\n  static_assert(not System::is_in_flux_conservative_form,\n                \"System is in flux conservative form\");\n  static constexpr size_t dim = System::volume_dim;\n  using variables_tag = typename System::variables_tag;\n  using simple_tags = db::AddSimpleTags<variables_tag>;\n  using compute_tags = db::AddComputeTags<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    using Vars = typename variables_tag::type;\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box),\n        Vars{db::get<domain::Tags::Mesh<dim>>(box).number_of_grid_points()});\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Initialization\n"
  },
  {
    "path": "src/Evolution/Initialization/SetVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace evolution::Initialization::Actions {\n/// \\ingroup InitializationGroup\n/// \\brief Sets variables needed for evolution of hyperbolic systems\n///\n/// Uses:\n/// - DataBox:\n///   * `CoordinatesTag`\n/// - GlobalCache:\n///   * `evolution::initial_data::Tags::InitialData`\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies:\n///   * System::variables_tag (if system has no primitive variables)\n///   * System::primitive_variables_tag (if system has primitive variables)\ntemplate <typename LogicalCoordinatesTag>\nstruct SetVariables {\n  using simple_tags_from_options = tmpl::list<::Tags::Time>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    using derived_classes =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 evolution::initial_data::InitialData>;\n    call_with_dynamic_type<void, derived_classes>(\n        &db::get<evolution::initial_data::Tags::InitialData>(box),\n        [&box](const auto* const data_or_solution) {\n          using initial_data_subclass =\n              std::decay_t<decltype(*data_or_solution)>;\n          if constexpr (is_analytic_data_v<initial_data_subclass> or\n                        is_analytic_solution_v<initial_data_subclass>) {\n            impl<Metavariables>(make_not_null(&box), *data_or_solution);\n          } else {\n            ERROR(\n                \"Trying to use \"\n                \"'evolution::Initialization::Actions::SetVariables' with a \"\n                \"class that's not marked as analytic solution or analytic \"\n                \"data. To support numeric initial data, add a \"\n                \"system-specific initialization routine to your executable.\");\n          }\n        });\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n private:\n  template <typename Metavariables, typename DbTagsList, typename T>\n  static void impl(const gsl::not_null<db::DataBox<DbTagsList>*> box,\n                   const T& solution_or_data) {\n    const double initial_time = db::get<::Tags::Time>(*box);\n    const auto inertial_coords =\n        db::get<::domain::CoordinateMaps::Tags::CoordinateMap<\n            Metavariables::volume_dim, Frame::Grid, Frame::Inertial>>(*box)(\n            db::get<::domain::Tags::ElementMap<Metavariables::volume_dim,\n                                               Frame::Grid>>(*box)(\n                db::get<LogicalCoordinatesTag>(*box)),\n            initial_time, db::get<::domain::Tags::FunctionsOfTime>(*box));\n\n    using system = typename Metavariables::system;\n\n    if constexpr (Metavariables::system::has_primitive_and_conservative_vars) {\n      using primitives_tag = typename system::primitive_variables_tag;\n      // Set initial data from analytic solution\n      db::mutate<primitives_tag>(\n          [&initial_time, &inertial_coords, &solution_or_data](\n              const gsl::not_null<typename primitives_tag::type*>\n                  primitive_vars) {\n            primitive_vars->assign_subset(\n                evolution::Initialization::initial_data(\n                    solution_or_data, inertial_coords, initial_time,\n                    typename Metavariables::analytic_variables_tags{}));\n          },\n          box);\n      using non_conservative_variables =\n          typename system::non_conservative_variables;\n      using variables_tag = typename system::variables_tag;\n      if constexpr (not std::is_same_v<non_conservative_variables,\n                                       tmpl::list<>>) {\n        db::mutate<variables_tag>(\n            [&initial_time, &inertial_coords, &solution_or_data](\n                const gsl::not_null<typename variables_tag::type*>\n                    evolved_vars) {\n              evolved_vars->assign_subset(\n                  evolution::Initialization::initial_data(\n                      solution_or_data, inertial_coords, initial_time,\n                      non_conservative_variables{}));\n            },\n            box);\n      }\n    } else {\n      using variables_tag = typename system::variables_tag;\n\n      // Set initial data from analytic solution\n      if constexpr (not std::is_same_v<typename variables_tag::tags_list,\n                                       tmpl::list<>>) {\n        using Vars = typename variables_tag::type;\n        db::mutate<variables_tag>(\n            [&initial_time, &inertial_coords,\n             &solution_or_data](const gsl::not_null<Vars*> vars) {\n              vars->assign_subset(evolution::Initialization::initial_data(\n                  solution_or_data, inertial_coords, initial_time,\n                  typename Vars::tags_list{}));\n            },\n            box);\n      }\n    }\n  }\n};\n}  // namespace evolution::Initialization::Actions\n"
  },
  {
    "path": "src/Evolution/Initialization/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Time/OptionTags/InitialSlabSize.hpp\"\n#include \"Time/OptionTags/InitialTimeStep.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Initialization {\n/// \\ingroup InitializationGroup\n/// \\brief %Tags used during initialization of parallel components.\nnamespace Tags {\nstruct InitialTimeDelta : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::InitialTimeStep>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double initial_time_step) {\n    return initial_time_step;\n  }\n};\n\ntemplate <bool UsingLocalTimeStepping>\nstruct InitialSlabSize : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::InitialSlabSize>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double initial_slab_size) {\n    return initial_slab_size;\n  }\n};\n\ntemplate <>\nstruct InitialSlabSize<false> : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::InitialTimeStep>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double initial_time_step) {\n    return std::abs(initial_time_step);\n  }\n};\n}  // namespace Tags\n}  // namespace Initialization\n"
  },
  {
    "path": "src/Evolution/NumericInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Protocols.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace evolution {\n\n/// Use in place of an analytic solution or analytic data to start an evolution\n/// with numeric initial data loaded from a data file.\nstruct NumericInitialData : tt::ConformsTo<protocols::NumericInitialData> {};\n\n}  // namespace evolution\n"
  },
  {
    "path": "src/Evolution/Particles/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(MonteCarlo)\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FluidCouplingAction.hpp\n  InitializeMonteCarlo.hpp\n  Labels.hpp\n  TimeStepActions.hpp\n  TriggerMonteCarloEvolution.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Actions/FluidCouplingAction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CellVolume.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n\nnamespace Particles::MonteCarlo {\n\n/// Mutator adding the Monte-Carlo contribution\n/// to the evolution of the fluid.\nstruct FluidCouplingMutator {\n  static const size_t Dim = 3;\n\n  // We modify the fluid evolved variables, and reset the coupling\n  // terms to zero.\n  using return_tags =\n      tmpl::list<grmhd::ValenciaDivClean::Tags::TildeTau,\n                 grmhd::ValenciaDivClean::Tags::TildeYe,\n                 grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>,\n                 Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>>;\n  using argument_tags = tmpl::list<\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::ActiveGrid,\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToInertial>;\n\n  static void apply(\n      const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*> tilde_s,\n      const gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n      const gsl::not_null<Scalar<DataVector>*> coupling_tilde_rho_ye,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*> coupling_tilde_s,\n      const Mesh<Dim>& mesh,\n      const evolution::dg::subcell::ActiveGrid& active_grid,\n      const Scalar<DataVector>& det_inverse_jacobian_logical_to_inertial) {\n    // Currently, MC skips all non-communication actions when not using\n    // Subcell.\n    if (active_grid != evolution::dg::subcell::ActiveGrid::Subcell) {\n      return;\n    }\n\n    // Coupling terms are on the mesh with ghost zones, while the evolved\n    // variables are not. We also need to normalize the coupling terms with the\n    // cell volume to get the evolution of energy/momentum density\n    const Index<3>& extents = mesh.extents();\n    const size_t num_ghost_zones = 1;\n    const Index<3> extents_with_ghost{extents[0] + 2 * num_ghost_zones,\n                                      extents[1] + 2 * num_ghost_zones,\n                                      extents[2] + 2 * num_ghost_zones};\n\n    Scalar<DataVector> det_jacobian_logical_to_inertial(*tilde_tau);\n    get(det_jacobian_logical_to_inertial) =\n        1.0 / get(det_inverse_jacobian_logical_to_inertial);\n    Scalar<DataVector> cell_inertial_three_volume =\n        make_with_value<Scalar<DataVector>>(*tilde_tau, 0.0);\n    cell_inertial_coordinate_three_volume_finite_difference(\n        &cell_inertial_three_volume, mesh, det_jacobian_logical_to_inertial);\n\n    for (size_t i = 0; i < extents[0]; i++) {\n      for (size_t j = 0; j < extents[1]; j++) {\n        for (size_t k = 0; k < extents[2]; k++) {\n          // The coupling terms are computed on a grid with ghost zone points,\n          // the fluid variables are on the grid without GZ points\n          // Index without GZ\n          const size_t local_idx = collapsed_index(Index<3>{i, j, k}, extents);\n          // Index with GZ\n          const size_t extended_idx =\n              collapsed_index(Index<3>{i + num_ghost_zones, j + num_ghost_zones,\n                                       k + num_ghost_zones},\n                              extents_with_ghost);\n          // The MC coupling calculates the change in energy, momentum, and\n          // lepton number. We need to divide by the cell 3-volume to get\n          // the change in the evolved variables.\n          const double& volume = get(cell_inertial_three_volume)[local_idx];\n          get(*tilde_tau)[local_idx] +=\n              get(*coupling_tilde_tau)[extended_idx] / volume;\n          get(*tilde_ye)[local_idx] +=\n              get(*coupling_tilde_rho_ye)[extended_idx] / volume;\n          for (size_t d = 0; d < 3; d++) {\n            tilde_s->get(d)[local_idx] +=\n                coupling_tilde_s->get(d)[extended_idx] / volume;\n          }\n        }\n      }\n    }\n    // Reset coupling terms to 0 after use\n    get(*coupling_tilde_tau) = 0.0;\n    get(*coupling_tilde_rho_ye) = 0.0;\n    for (size_t d = 0; d < 3; d++) {\n      coupling_tilde_s->get(d) = 0.0;\n    }\n  }\n};\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Actions/InitializeMonteCarlo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <random>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunicationTags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MonteCarloOptions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MortarData.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace evolution::initial_data::Tags {\nstruct InitialData;\n}  // namespace evolution::initial_data::Tags\n\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Initialization::Actions {\n\n/// \\ingroup InitializationGroup\n/// \\brief Allocate variables needed for evolution of Monte Carlo transport\n///\n/// Uses:\n/// - evolution::dg::subcell::Tags::Mesh<dim>\n/// - evolution::dg::subcell::Tags::Coordinates<dim, Frame::Inertial>\n/// - evolution::dg::subcell::Tags::ActiveGrid\n/// - ::Tags::Time\n/// - evolution::initial_data::Tags::InitialData\n/// - Particles::MonteCarlo::Tags::MonteCarloOptions<EnergyBins,\n/// NeutrinoSpecies>\n/// - domain::Tags::Element<dim>\n///\n/// DataBox changes:\n/// - Adds:\n///   * Particles::MonteCarlo::Tags::PacketsOnElement\n///   * Particles::MonteCarlo::Tags::RandomNumberGenerator\n///   * Particles::MonteCarlo::Tags::DesiredPacketEnergyAtEmission<\n///                                  NeutrinoSpecies>\n///   * Background hydro variables\n///   * Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>\n///   * Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>\n///   * Particles::MonteCarlo::Tags::CouplingTildeS<DataVector,dim>\n///   * Particles::MonteCarlo::Tags::MortarDataTag<dim>\n///   * Particles::MonteCarlo::Tags::GhostZoneCouplingData<dim>\n///   * Particles::MonteCarlo::Tags::McGhostZoneDataTag<dim>\n///\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename System, size_t EnergyBins, size_t NeutrinoSpecies,\n          bool InitializeBackground>\nstruct InitializeMCTags {\n public:\n  using hydro_variables_tag = typename System::hydro_variables_tag;\n\n  static constexpr size_t dim = System::volume_dim;\n  using simple_tags =\n      tmpl::list<Particles::MonteCarlo::Tags::PacketsOnElement,\n                 Particles::MonteCarlo::Tags::RandomNumberGenerator,\n                 Particles::MonteCarlo::Tags::DesiredPacketEnergyAtEmission<\n                     NeutrinoSpecies>,\n                 hydro_variables_tag,\n                 Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, dim>,\n                 Particles::MonteCarlo::Tags::MortarDataTag<dim>,\n                 Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<dim>,\n                 Particles::MonteCarlo::Tags::McGhostZoneDataTag<dim>,\n                 evolution::dg::subcell::Tags::ActiveGrid>;\n\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (db::get<evolution::dg::subcell::Tags::ActiveGrid>(box) !=\n        evolution::dg::subcell::ActiveGrid::Subcell) {\n      ERROR(\"MC requires all elements to use Subcell\");\n    }\n    const Mesh<dim>& mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<dim>>(box);\n    const size_t num_grid_points = mesh.number_of_grid_points();\n    // Number of ghost zones for MC is assumed to be 1 for now.\n    const size_t num_ghost_zones = 1;\n    size_t mesh_size_with_ghost_zones = 1;\n    for (size_t d = 0; d < dim; d++) {\n      mesh_size_with_ghost_zones *= (mesh.extents()[d] + 2 * num_ghost_zones);\n    }\n    const DataVector zero_dv_with_ghost_zones(mesh_size_with_ghost_zones, 0.0);\n    const Scalar<DataVector> zero_scalar_with_ghost_zones =\n        make_with_value<Scalar<DataVector>>(zero_dv_with_ghost_zones, 0.0);\n    const tnsr::i<DataVector, dim, Frame::Inertial> zero_tnsr_with_ghost_zones =\n        make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(\n            zero_dv_with_ghost_zones, 0.0);\n    if constexpr (InitializeBackground) {\n      using derived_classes =\n          tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                   evolution::initial_data::InitialData>;\n      using HydroVars = typename hydro_variables_tag::type;\n      call_with_dynamic_type<void, derived_classes>(\n          &db::get<evolution::initial_data::Tags::InitialData>(box),\n          [&box, &num_grid_points](const auto* const data_or_solution) {\n            static constexpr size_t dim = System::volume_dim;\n            const double initial_time = db::get<::Tags::Time>(box);\n            const auto& inertial_coords =\n                db::get<evolution::dg::subcell::Tags::Coordinates<\n                    dim, Frame::Inertial>>(box);\n            // Get hydro variables\n            HydroVars hydro_variables{num_grid_points};\n            hydro_variables.assign_subset(\n                evolution::Initialization::initial_data(\n                    *data_or_solution, inertial_coords, initial_time,\n                    typename hydro_variables_tag::tags_list{}));\n            Initialization::mutate_assign<tmpl::list<hydro_variables_tag>>(\n                make_not_null(&box), std::move(hydro_variables));\n          });\n    }\n\n    Initialization::mutate_assign<\n        tmpl::list<Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>>>(\n        make_not_null(&box), zero_scalar_with_ghost_zones);\n    Initialization::mutate_assign<tmpl::list<\n        Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>>>(\n        make_not_null(&box), zero_scalar_with_ghost_zones);\n    Initialization::mutate_assign<tmpl::list<\n        Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, dim>>>(\n        make_not_null(&box), zero_tnsr_with_ghost_zones);\n\n    // Read global options for Monte-Carlo evolution\n    const auto mc_options = db::get<\n        Particles::MonteCarlo::Tags::MonteCarloOptions<NeutrinoSpecies>>(box);\n    const auto& initial_packet_energy = mc_options.get_initial_packet_energy();\n\n    typename Particles::MonteCarlo::Tags::PacketsOnElement::type all_packets;\n    Initialization::mutate_assign<\n        tmpl::list<Particles::MonteCarlo::Tags::PacketsOnElement>>(\n        make_not_null(&box), std::move(all_packets));\n\n    const unsigned long seed = std::random_device{}();\n    typename Particles::MonteCarlo::Tags::RandomNumberGenerator::type rng(seed);\n\n    Initialization::mutate_assign<\n        tmpl::list<Particles::MonteCarlo::Tags::RandomNumberGenerator>>(\n        make_not_null(&box), std::move(rng));\n\n    // Initial energy of packets, read from MC options\n    typename Particles::MonteCarlo::Tags::DesiredPacketEnergyAtEmission<\n        NeutrinoSpecies>::type packet_energy_at_emission =\n        make_with_value<std::array<DataVector, NeutrinoSpecies>>(\n            DataVector{num_grid_points}, 0.0);\n    for (size_t s = 0; s < NeutrinoSpecies; s++) {\n      packet_energy_at_emission[s] = initial_packet_energy[s];\n    }\n    Initialization::mutate_assign<\n        tmpl::list<Particles::MonteCarlo::Tags::DesiredPacketEnergyAtEmission<\n            NeutrinoSpecies>>>(make_not_null(&box),\n                               std::move(packet_energy_at_emission));\n\n    // Initialize mortar data and coupling data.\n    // Currently assumes a single neighbor on each face (i.e. no h-refinement)\n    using MortarData =\n        typename Particles::MonteCarlo::Tags::MortarDataTag<dim>::type;\n    MortarData mortar_data;\n    using CouplingData =\n        typename Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<\n            dim>::type;\n    CouplingData coupling_data;\n    const Element<dim>& element = db::get<::domain::Tags::Element<dim>>(box);\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      const size_t sliced_mesh_size =\n          mesh.slice_away(direction.dimension()).number_of_grid_points();\n      const DataVector zero_dv_slice(sliced_mesh_size, 0.0);\n      const Index<dim - 1> sliced_mesh_extents =\n          mesh.slice_away(direction.dimension()).extents();\n      size_t sliced_mesh_size_with_ghost_zone = 1;\n      for (size_t d = 0; d < dim - 1; d++) {\n        sliced_mesh_size_with_ghost_zone *= ( sliced_mesh_extents[d]\n                                             + 2 * num_ghost_zones );\n      }\n      const DataVector zero_dv_ghost_zones(sliced_mesh_size_with_ghost_zone,\n                                           0.0);\n      const tnsr::i<DataVector, dim, Frame::Inertial> zero_tnsr_ghost_zones =\n          make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(\n              zero_dv_ghost_zones, 0.0);\n\n      for (const auto& neighbor : neighbors) {\n        const DirectionalId<dim> mortar_id{direction, neighbor};\n        mortar_data.rest_mass_density.emplace(mortar_id, zero_dv_slice);\n        mortar_data.electron_fraction.emplace(mortar_id, zero_dv_slice);\n        mortar_data.temperature.emplace(mortar_id, zero_dv_slice);\n        mortar_data.cell_light_crossing_time.emplace(mortar_id, zero_dv_slice);\n        coupling_data.coupling_tilde_tau.emplace(mortar_id,\n                                                 zero_dv_ghost_zones);\n        coupling_data.coupling_tilde_rho_ye.emplace(mortar_id,\n                                                    zero_dv_ghost_zones);\n        coupling_data.coupling_tilde_s.emplace(mortar_id,\n                                               zero_tnsr_ghost_zones);\n      }\n    }\n    Initialization::mutate_assign<\n        tmpl::list<Particles::MonteCarlo::Tags::MortarDataTag<dim>>>(\n        make_not_null(&box), std::move(mortar_data));\n    Initialization::mutate_assign<\n        tmpl::list<Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<dim>>>(\n        make_not_null(&box), std::move(coupling_data));\n\n    using GhostZoneData =\n        typename Particles::MonteCarlo::Tags::McGhostZoneDataTag<dim>::type;\n    GhostZoneData ghost_zone_data{};\n    Initialization::mutate_assign<\n        tmpl::list<Particles::MonteCarlo::Tags::McGhostZoneDataTag<dim>>>(\n        make_not_null(&box), std::move(ghost_zone_data));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Initialization::Actions\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Actions/Labels.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Labels used to navigate the action list when using MC\nnamespace Particles::MonteCarlo::Actions::Labels {\n/// Beginning of the MC algorithm\nstruct BeginMonteCarlo {};\n/// End of the MC algorithm\nstruct EndMonteCarlo {};\n}  // namespace Particles::MonteCarlo::Actions::Labels\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Actions/TimeStepActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Particles/MonteCarlo/InverseJacobianInertialToFluidCompute.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MortarData.hpp\"\n#include \"Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\nnamespace Particles::MonteCarlo {\n\n/// Mutator advancing neutrinos by a single step\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nstruct TimeStepMutator {\n  static const size_t Dim = 3;\n\n  using return_tags =\n      tmpl::list<Particles::MonteCarlo::Tags::PacketsOnElement,\n                 Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>,\n                 Particles::MonteCarlo::Tags::RandomNumberGenerator,\n                 Particles::MonteCarlo::Tags::DesiredPacketEnergyAtEmission<\n                     NeutrinoSpecies>>;\n  // To do : check carefully DG vs Subcell quantities... everything should\n  // be on the Subcell grid!\n  using argument_tags = tmpl::list<\n      ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n      hydro::Tags::GrmhdEquationOfState,\n      Particles::MonteCarlo::Tags::InteractionRatesTable<EnergyBins,\n                                                         NeutrinoSpecies>,\n      hydro::Tags::ElectronFraction<DataVector>,\n      hydro::Tags::RestMassDensity<DataVector>,\n      hydro::Tags::Temperature<DataVector>,\n      hydro::Tags::LorentzFactor<DataVector>,\n      hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>,\n      gr::Tags::Lapse<DataVector>,\n      gr::Tags::Shift<DataVector, Dim, Frame::Inertial>,\n      gh::Tags::Phi<DataVector, 3, Frame::Inertial>,\n      gr::Tags::SpatialMetric<DataVector, Dim, Frame::Inertial>,\n      gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame::Inertial>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      Particles::MonteCarlo::Tags::CellLightCrossingTime<DataVector>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      domain::Tags::MeshVelocity<Dim>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<Dim>,\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToInertial,\n      domain::Tags::InverseJacobian<Dim + 1, Frame::Inertial, Frame::Fluid>,\n      domain::Tags::Jacobian<Dim + 1, Frame::Inertial, Frame::Fluid>,\n      Particles::MonteCarlo::Tags::MortarDataTag<Dim>>;\n\n  static void apply(\n      const gsl::not_null<std::vector<Packet>*> packets,\n      const gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n      const gsl::not_null<Scalar<DataVector>*> coupling_tilde_rho_ye,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*> coupling_tilde_s,\n      const gsl::not_null<std::mt19937*> random_number_generator,\n      const gsl::not_null<std::array<DataVector, NeutrinoSpecies>*>\n          single_packet_energy,\n      const TimeStepId& current_step_id, const TimeStepId& next_step_id,\n\n      const EquationsOfState::EquationOfState<true, 3>& equation_of_state,\n      const NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>&\n          interaction_table,\n      const Scalar<DataVector>& electron_fraction,\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& temperature,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& spatial_velocity,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n\n      const tnsr::ii<DataVector, Dim, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, Dim, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_determinant_spatial_metric,\n      const Scalar<DataVector>& cell_light_crossing_time, const Mesh<Dim>& mesh,\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical>& mesh_coordinates,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>&\n          inverse_jacobian_logical_to_inertial,\n      const Scalar<DataVector>& det_inverse_jacobian_logical_to_inertial,\n      const InverseJacobian<DataVector, Dim + 1, Frame::Inertial, Frame::Fluid>&\n          inertial_to_fluid_inverse_jacobian,\n      const Jacobian<DataVector, Dim + 1, Frame::Inertial, Frame::Fluid>&\n          inertial_to_fluid_jacobian,\n      const MortarData<Dim>& mortar_data) {\n    // Number of ghost zones for MC is assumed to be 1 for now.\n    const size_t num_ghost_zones = 1;\n    // Get information stored in various databox containers in\n    // the format expected by take_time_step_on_element\n    const double start_time = current_step_id.step_time().value();\n    const double end_time = next_step_id.step_time().value();\n    Scalar<DataVector> det_jacobian_logical_to_inertial(lapse);\n    get(det_jacobian_logical_to_inertial) =\n        1.0 / get(det_inverse_jacobian_logical_to_inertial);\n    const DirectionalIdMap<Dim, std::optional<DataVector>>&\n        electron_fraction_ghost = mortar_data.electron_fraction;\n    const DirectionalIdMap<Dim, std::optional<DataVector>>&\n        baryon_density_ghost = mortar_data.rest_mass_density;\n    const DirectionalIdMap<Dim, std::optional<DataVector>>& temperature_ghost =\n        mortar_data.temperature;\n    const DirectionalIdMap<Dim, std::optional<DataVector>>&\n        cell_light_crossing_time_ghost = mortar_data.cell_light_crossing_time;\n\n    // Calculate temporary tensors needed for MC evolution\n    using deriv_lapse = ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                      tmpl::size_t<3>, Frame::Inertial>;\n    using deriv_shift = ::Tags::deriv<gr::Tags::Shift<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>;\n    using deriv_spatial_metric =\n        ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>;\n    using deriv_inverse_spatial_metric =\n        ::Tags::deriv<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                      tmpl::size_t<3>, Frame::Inertial>;\n    using temporary_tags = tmpl::list<\n        hydro::Tags::LowerSpatialFourVelocity<DataVector, Dim, Frame::Inertial>,\n        gr::Tags::SpacetimeNormalVector<DataVector, 3>,\n        gr::Tags::InverseSpacetimeMetric<DataVector, 3>, deriv_lapse,\n        deriv_shift, deriv_spatial_metric, deriv_inverse_spatial_metric>;\n    Variables<temporary_tags> temp_tags{mesh.number_of_grid_points(), 0.0};\n\n    // u_i = \\gamma_{ij} v^j W\n    auto& lower_spatial_four_velocity =\n        get<hydro::Tags::LowerSpatialFourVelocity<DataVector, Dim,\n                                                  Frame::Inertial>>(temp_tags);\n    raise_or_lower_index(make_not_null(&lower_spatial_four_velocity),\n                         spatial_velocity, spatial_metric);\n    for (size_t i = 0; i < Dim; i++) {\n      lower_spatial_four_velocity.get(i) *= get(lorentz_factor);\n    }\n    // For the metric, we adapt the calculations performed for the time\n    // derivative of in GhGrMhd. First get n^a and g^ab\n    auto& spacetime_normal_vector =\n        get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(temp_tags);\n    auto& inv_spacetime_metric =\n        get<gr::Tags::InverseSpacetimeMetric<DataVector, 3>>(temp_tags);\n    gr::spacetime_normal_vector(make_not_null(&spacetime_normal_vector), lapse,\n                                shift);\n    gr::inverse_spacetime_metric(make_not_null(&inv_spacetime_metric), lapse,\n                                 shift, inv_spatial_metric);\n\n    auto& d_lapse = get<deriv_lapse>(temp_tags);\n    auto& d_shift = get<deriv_shift>(temp_tags);\n    // Temporary store phi_iab n^a n^b in d_lapse. This is phi_two_normals in GH\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t a = 0; a < Dim + 1; a++) {\n        for (size_t b = 0; b < Dim + 1; b++) {\n          d_lapse.get(i) += phi.get(i, a, b) * spacetime_normal_vector.get(a) *\n                            spacetime_normal_vector.get(b);\n        }\n      }\n    }\n    // Shift derivative using stored quantity in d_lapse\n    // We use d_i shift^j =\n    // (g^{j+1 b} phi_{iba} n^a + n^{j+1} phi_{iab} n^a n_b) * lapse\n    // as in TimeDerivative.hpp in GhGrMhd\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t j = 0; j < Dim; j++) {\n        d_shift.get(i, j) +=\n            d_lapse.get(i) * spacetime_normal_vector.get(j + 1);\n        for (size_t a = 0; a < Dim + 1; a++) {\n          for (size_t b = 0; b < Dim + 1; b++) {\n            d_shift.get(i, j) += inv_spacetime_metric.get(j + 1, b) *\n                                 phi.get(i, b, a) *\n                                 spacetime_normal_vector.get(a);\n          }\n        }\n        d_shift.get(i, j) *= get(lapse);\n      }\n    }\n    // Now use d_i lapse = - lapse * 0.5 * phi_{iab} n^a n^b\n    // As we already stored phi_{iab} n^a n^b in d_i lapse,\n    // we just multiply by (-0.5 * lapse)\n    for (size_t i = 0; i < Dim; i++) {\n      d_lapse.get(i) *= (-0.5) * get(lapse);\n    }\n\n    // Extract d_i \\gamma_{jk} from phi_{i,j+1,k+1}\n    auto& d_spatial_metric = get<deriv_spatial_metric>(temp_tags);\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t j = 0; j < Dim; j++) {\n        for (size_t k = j; k < Dim; k++) {\n          d_spatial_metric.get(i, j, k) = phi.get(i, j + 1, k + 1);\n        }\n      }\n    }\n\n    auto& d_inv_spatial_metric = get<deriv_inverse_spatial_metric>(temp_tags);\n    gr::deriv_inverse_spatial_metric(make_not_null(&d_inv_spatial_metric),\n                                     inv_spatial_metric, d_spatial_metric);\n\n    TemplatedLocalFunctions<EnergyBins, NeutrinoSpecies> templated_functions;\n    templated_functions.take_time_step_on_element(\n        packets, coupling_tilde_tau, coupling_tilde_rho_ye, coupling_tilde_s,\n        random_number_generator, single_packet_energy, start_time, end_time,\n        equation_of_state, interaction_table, electron_fraction,\n        rest_mass_density, temperature, lorentz_factor,\n        lower_spatial_four_velocity, lapse, shift, d_lapse, d_shift,\n        d_inv_spatial_metric, spatial_metric, inv_spatial_metric,\n        sqrt_determinant_spatial_metric, cell_light_crossing_time, mesh,\n        mesh_coordinates, num_ghost_zones, mesh_velocity,\n        inverse_jacobian_logical_to_inertial, det_jacobian_logical_to_inertial,\n        inertial_to_fluid_jacobian, inertial_to_fluid_inverse_jacobian,\n        electron_fraction_ghost, baryon_density_ghost, temperature_ghost,\n        cell_light_crossing_time_ghost);\n  }\n};\n\nnamespace Actions {\n\n/// Action taking a single time step of the Monte-Carlo evolution\n/// algorithm, assuming that the fluid and metric data in the ghost\n/// zones have been communicated and that packets are on the elements\n/// that owns them.\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nstruct TakeTimeStep {\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    ASSERT(db::get<evolution::dg::subcell::Tags::ActiveGrid>(box) ==\n               evolution::dg::subcell::ActiveGrid::Subcell,\n           \"MC assumes that we are using the Subcell grid!\");\n\n    db::mutate_apply(TimeStepMutator<EnergyBins, NeutrinoSpecies>{},\n                     make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Actions/TriggerMonteCarloEvolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Actions/Labels.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace Particles::MonteCarlo::Actions {\n/*!\n * \\brief Goes to `Labels::BeginMonteCarlo` or `Labels::EndMonteCarlo` depending\n * on whether we are at the end of a full time step or at an intermediate step\n * of the timestepping algorithm.\n *\n * GlobalCache: nothing\n *\n * DataBox:\n * - Uses:\n *   - Tags::Next<::Tags::TimeStepId>\n */\nstruct TriggerMonteCarloEvolution {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& next_time_id = db::get<::Tags::Next<::Tags::TimeStepId>>(box);\n    // We only run MC if we are at the beginning of a full time step\n    const bool trigger_mc = (next_time_id.substep() == 0);\n    // Note: we jump to the `Label+1` because the label actions don't do\n    // anything anyway\n    if (trigger_mc) {\n      const size_t mc_index =\n          tmpl::index_of<ActionList,\n                         ::Actions::Label<Labels::BeginMonteCarlo>>::value +\n          1;\n      return {Parallel::AlgorithmExecution::Continue, mc_index};\n    } else {\n      const size_t post_mc_index =\n          tmpl::index_of<ActionList,\n                         ::Actions::Label<Labels::EndMonteCarlo>>::value +\n          1;\n      return {Parallel::AlgorithmExecution::Continue, post_mc_index};\n    }\n  }\n};\n}  // namespace Particles::MonteCarlo::Actions\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY MonteCarlo)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CellCrossingTime.cpp\n  CellVolume.cpp\n  CouplingTermsForPropagation.cpp\n  EvolvePackets.cpp\n  InverseJacobianInertialToFluidCompute.cpp\n  MonteCarloOptions.cpp\n  NeutrinoInteractionTable.cpp\n  NeutrinoMomentsFromMonteCarlo.cpp\n  Packet.cpp\n  Scattering.cpp\n  SwapGrTags.cpp\n  TemplatedLocalFunctions.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CellCrossingTime.hpp\n  CellVolume.hpp\n  CouplingTermsForPropagation.hpp\n  EmitPackets.tpp\n  EvolvePackets.hpp\n  EvolvePacketsInElement.tpp\n  GhostZoneCommunication.hpp\n  GhostZoneCommunicationStep.hpp\n  GhostZoneCommunicationTags.hpp\n  ImplicitMonteCarloCorrections.tpp\n  InverseJacobianInertialToFluidCompute.hpp\n  MonteCarloOptions.hpp\n  MortarData.hpp\n  NeutrinoInteractionTable.hpp\n  NeutrinoMomentsFromMonteCarlo.hpp\n  Packet.hpp\n  Scattering.hpp\n  SwapGrTags.hpp\n  System.hpp\n  Tags.hpp\n  TakeTimeStep.tpp\n  TemplatedLocalFunctions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  Domain\n  ErrorHandling\n  GeneralRelativity\n  H5\n  Hydro\n  Options\n  Utilities\n  PRIVATE\n  )\n\nadd_subdirectory(Actions)\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/CellCrossingTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/CellCrossingTime.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace Particles::MonteCarlo {\n\nvoid cell_light_crossing_time(\n    gsl::not_null<Scalar<DataVector>*> cell_light_crossing_time,\n    const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coordinates,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric) {\n  const Index<3>& extents = mesh.extents();\n  const size_t n_pts = mesh.number_of_grid_points();\n  const std::array<size_t, 3> step{1, extents[0], extents[0] * extents[1]};\n  const std::array<double, 3> dx_inertial{\n      inertial_coordinates.get(0)[step[0]] - inertial_coordinates.get(0)[0],\n      inertial_coordinates.get(1)[step[1]] - inertial_coordinates.get(1)[0],\n      inertial_coordinates.get(2)[step[2]] - inertial_coordinates.get(2)[0]};\n\n  get(*cell_light_crossing_time) = DataVector(n_pts);\n  // Estimate light-crossing time in the cell.\n  for (size_t i = 0; i < n_pts; i++) {\n    double& min_crossing_time = get(*cell_light_crossing_time)[i];\n\n    min_crossing_time = dx_inertial[0] /\n                        (fabs(shift.get(0)[i]) +\n                         sqrt(inv_spatial_metric.get(0, 0)[i]) * get(lapse)[i]);\n    for (size_t d = 1; d < 3; d++) {\n      const double dim_crossing_time =\n        gsl::at(dx_inertial,d) /\n          (fabs(shift.get(d)[i]) +\n           sqrt(inv_spatial_metric.get(d, d)[i]) * get(lapse)[i]);\n      min_crossing_time = std::min(min_crossing_time, dim_crossing_time);\n    }\n  }\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/CellCrossingTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace Particles::MonteCarlo {\n\nvoid cell_light_crossing_time(\n    gsl::not_null<Scalar<DataVector>*> cell_light_crossing_time,\n    const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coordinates,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric);\n\nstruct CellLightCrossingTimeCompute : Tags::CellLightCrossingTime<DataVector>,\n                                           db::ComputeTag {\n  using base = Tags::CellLightCrossingTime<DataVector>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n    evolution::dg::subcell::Tags::Mesh<3>,\n    evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n    gr::Tags::Lapse<DataVector>,\n    gr::Tags::Shift<DataVector, 3, Frame::Inertial>,\n    gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>>;\n\n  static void function(\n    gsl::not_null<return_type*> cell_light_crossing_time_,\n    const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coordinates,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric){\n    cell_light_crossing_time(cell_light_crossing_time_, mesh,\n        inertial_coordinates, lapse, shift, inv_spatial_metric);\n  }\n};\n\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/CellVolume.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/CellVolume.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace Particles::MonteCarlo {\n\nvoid cell_proper_four_volume_finite_difference(\n    const gsl::not_null<Scalar<DataVector>*> cell_proper_four_volume,\n    const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_determinant_spatial_metric,\n    const double time_step, const Mesh<3>& mesh,\n    const Scalar<DataVector>& det_jacobian_logical_to_inertial) {\n  const double cell_logical_volume =\n    8.0 / static_cast<double>(mesh.number_of_grid_points());\n  cell_proper_four_volume->get() =\n      get(lapse) * get(sqrt_determinant_spatial_metric) * time_step *\n      cell_logical_volume * get(det_jacobian_logical_to_inertial);\n}\n\nvoid cell_inertial_coordinate_three_volume_finite_difference(\n    gsl::not_null<Scalar<DataVector>*> cell_inertial_three_volume,\n    const Mesh<3>& mesh,\n    const Scalar<DataVector>& det_jacobian_logical_to_inertial) {\n  const double cell_logical_volume =\n    8.0 / static_cast<double>(mesh.number_of_grid_points());\n  cell_inertial_three_volume->get() =\n      cell_logical_volume * get(det_jacobian_logical_to_inertial);\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/CellVolume.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace gsl {\n  template <typename T>\n  class not_null;\n}  // namespace gsl\n\nclass DataVector;\n\ntemplate<size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace Particles::MonteCarlo {\n\n/// Proper 4-volume of a cell for a time step time_step.\n/// We assume that determinant_spatial_metric is given\n/// in inertial coordinates, hence the need for\n/// det_jacobian_logical_to_inertial\nvoid cell_proper_four_volume_finite_difference(\n    gsl::not_null<Scalar<DataVector>*> cell_proper_four_volume,\n    const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_determinant_spatial_metric, double time_step,\n    const Mesh<3>& mesh,\n    const Scalar<DataVector>& det_jacobian_logical_to_inertial);\n\n/// 3-volume of a cell in inertial coordinate. Note that this is\n/// the coordinate volume, not the proper volume. This quantity\n/// is needed as a normalization factor for the terms coupling\n/// Monte-Carlo transport to the fluid evolution (as we evolved\n/// densitized fluid variables in inertial coordinates).\nvoid cell_inertial_coordinate_three_volume_finite_difference(\n  gsl::not_null<Scalar<DataVector>* > cell_inertial_three_volume,\n  const Mesh<3>& mesh,\n  const Scalar<DataVector>& det_jacobian_logical_to_inertial);\n\n} // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/CouplingTermsForPropagation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CouplingTermsForPropagation.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nusing hydro::units::nuclear::proton_mass;\n\nnamespace Particles::MonteCarlo {\n\nvoid AddCouplingTermsForPropagation(\n    const gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        coupling_tilde_s,\n    const gsl::not_null<Scalar<DataVector>*> coupling_rho_ye,\n    const Packet& packet, size_t extended_idx, const double dt,\n    const double absorption_opacity, const double scattering_opacity,\n    const double fluid_frame_energy, const double lapse,\n    const double lorentz_factor,\n    const std::array<double, 3>& lower_spatial_four_velocity_packet) {\n  // Energy coupling term.\n  coupling_tilde_tau->get()[extended_idx] +=\n      dt * absorption_opacity * packet.number_of_neutrinos *\n          fluid_frame_energy * lapse +\n      dt * scattering_opacity * packet.number_of_neutrinos *\n          fluid_frame_energy *\n          (lapse -\n           fluid_frame_energy * lorentz_factor / packet.momentum_upper_t);\n  // Momentum coupling term\n  for (size_t d = 0; d < 3; d++) {\n    coupling_tilde_s->get(d)[extended_idx] +=\n        dt / packet.momentum_upper_t * packet.number_of_neutrinos *\n        fluid_frame_energy *\n        (packet.momentum.get(d) * (absorption_opacity + scattering_opacity) -\n         scattering_opacity * fluid_frame_energy *\n             gsl::at(lower_spatial_four_velocity_packet, d));\n  }\n  // Lepton number coupling term\n  if (packet.species < 2) {\n    coupling_rho_ye->get()[extended_idx] +=\n        (packet.species == 0 ? 1.0 : -1.0) * proton_mass * dt /\n        packet.momentum_upper_t * absorption_opacity * fluid_frame_energy *\n        packet.number_of_neutrinos;\n  }\n}\n\n} // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/CouplingTermsForPropagation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\n  template <typename T>\n  class not_null;\n}  // namespace gsl\n\nclass DataVector;\nnamespace Particles::MonteCarlo {\n  class Packet;\n} // namespace Particles::MonteCarlo\n/// \\endcond\n\nnamespace Particles::MonteCarlo {\n\n/// \\brief Contribution to the neutrino-matter coupling\n/// terms from evolving a packet by time dt, given\n/// absorption and scattering opacities.\n///\n/// \\details We calculate total momentum exchanges, not\n/// densities; thus when coupling to the evolution of the\n/// energy/momentum density, division by the spatial volume is\n/// necessary. We calculate the source terms of Eqs (62-64) of\n/// \\cite Foucart2021mcb, with integrals calculated as in\n/// Eqs (6-10) of that manuscript (except that we do not\n/// divide by the coordinate volume V).\nvoid AddCouplingTermsForPropagation(\n    gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n    gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> coupling_tilde_s,\n    gsl::not_null<Scalar<DataVector>*> coupling_rho_ye, const Packet& packet,\n    size_t extended_idx, double dt, double absorption_opacity,\n    double scattering_opacity, double fluid_frame_energy, double lapse,\n    double lorentz_factor,\n    const std::array<double, 3>& lower_spatial_four_velocity_packet);\n\n} // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/EmitPackets.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nusing hydro::units::nuclear::proton_mass;\n\nnamespace Particles::MonteCarlo {\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nvoid TemplatedLocalFunctions<EnergyBins, NeutrinoSpecies>::emit_packets(\n    const gsl::not_null<std::vector<Packet>*> packets,\n    const gsl::not_null<std::mt19937*> random_number_generator,\n    const gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        coupling_tilde_s,\n    const gsl::not_null<Scalar<DataVector>*> coupling_rho_ye,\n    const double& time_start_step, const double& time_step, const Mesh<3>& mesh,\n    const size_t num_ghost_zones,\n    const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n        emissivity_in_cell,\n    const std::array<DataVector, NeutrinoSpecies>& single_packet_energy,\n    const std::array<double, EnergyBins>& energy_at_bin_center,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lower_spatial_four_velocity,\n    const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_jacobian,\n    const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_inverse_jacobian,\n    const Scalar<DataVector>& cell_proper_four_volume) {\n  std::uniform_real_distribution<double> rng_uniform_zero_to_one(0.0, 1.0);\n  // We begin by determining how many packets to create for each cell, neutrino\n  // species, and energy group.\n  const size_t grid_size = mesh.number_of_grid_points();\n  const Index<3>& extents = mesh.extents();\n  const Index<3> extents_with_ghost{\n    extents[0] + 2 * num_ghost_zones,\n    extents[1] + 2 * num_ghost_zones,\n    extents[2] + 2 * num_ghost_zones};\n  std::array<std::array<std::vector<size_t>, EnergyBins>, NeutrinoSpecies>\n      number_of_packets_to_create_per_cell;\n  size_t number_of_packets_to_create_total = 0;\n  for (size_t s = 0; s < NeutrinoSpecies; s++) {\n    for (size_t g = 0; g < EnergyBins; g++) {\n      gsl::at(gsl::at(number_of_packets_to_create_per_cell, s), g)\n          .resize(grid_size);\n      for (size_t i = 0; i < extents[0]; i++){\n        for (size_t j = 0; j < extents[1]; j++){\n          for (size_t k = 0; k < extents[2]; k++){\n            const size_t local_idx = collapsed_index(Index<3>{i,j,k}, extents);\n            const size_t extended_idx = collapsed_index(\n              Index<3>{num_ghost_zones + i,\n                num_ghost_zones + j, num_ghost_zones + k},\n              extents_with_ghost);\n            const double& emission_this_cell =\n              gsl::at(gsl::at(emissivity_in_cell, s), g)[extended_idx] *\n              get(cell_proper_four_volume)[local_idx];\n            const double packets_to_create_double =\n              emission_this_cell / gsl::at(single_packet_energy, s)[local_idx];\n            size_t packets_to_create_int = floor(packets_to_create_double);\n            if (rng_uniform_zero_to_one(*random_number_generator) <\n              packets_to_create_double -\n                static_cast<double>(packets_to_create_int)) {\n              packets_to_create_int++;\n            }\n            gsl::at(gsl::at(number_of_packets_to_create_per_cell, s), g)\n              [local_idx] = packets_to_create_int;\n            number_of_packets_to_create_total += packets_to_create_int;\n\n            // Coupling to hydro variables. These will need to be divided by the\n            // coordinate volume when the coupling is performed, as here\n            // we consider total emission, rather than emission densities.\n            coupling_tilde_tau->get()[extended_idx] -=\n              emission_this_cell * get(lorentz_factor)[local_idx];\n            for (int d = 0; d < 3; d++) {\n              coupling_tilde_s->get(d)[extended_idx] -= emission_this_cell *\n                lower_spatial_four_velocity.get(d)[local_idx];\n            }\n            if (NeutrinoSpecies >= 2 && s < 2) {\n              coupling_rho_ye->get()[extended_idx] -=\n                (s == 0 ? 1.0 : -1.0) * emission_this_cell /\n                gsl::at(energy_at_bin_center, g) * proton_mass;\n            }\n          }\n        }\n      }\n    }\n  }\n\n  // With the number of packets known, resize the vector holding the packets\n  Packet default_packet(0, 0.0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);\n  const size_t initial_size = packets->size();\n  packets->resize(initial_size + number_of_packets_to_create_total,\n                  default_packet);\n\n  // Allocate memory for some temporary data.\n  double time_normalized = 0.0;\n  std::array<double, 3> coord_normalized{{0.0, 0.0, 0.0}};\n  std::array<double, 3> three_momentum_normalized{{0.0, 0.0, 0.0}};\n  std::array<double, 4> four_momentum_fluid_frame{{0.0, 0.0, 0.0, 0.0}};\n  const std::array<double, 3> logical_dx{2.0 / static_cast<double>(extents[0]),\n                                         2.0 / static_cast<double>(extents[1]),\n                                         2.0 / static_cast<double>(extents[2])};\n  std::array<double, 3> coord_bottom_cell{{0.0, 0.0, 0.0}};\n\n  // Now create the packets\n  size_t next_packet_index = initial_size;\n  for (size_t x_idx = 0; x_idx < extents[0]; x_idx++) {\n    gsl::at(coord_bottom_cell, 0) =\n        -1.0 + static_cast<double>(x_idx) * gsl::at(logical_dx, 0);\n    for (size_t y_idx = 0; y_idx < extents[1]; y_idx++) {\n      gsl::at(coord_bottom_cell, 1) =\n          -1.0 + static_cast<double>(y_idx) * gsl::at(logical_dx, 1);\n      for (size_t z_idx = 0; z_idx < extents[2]; z_idx++) {\n        const size_t idx = mesh.storage_index(Index<3>{{x_idx, y_idx, z_idx}});\n        gsl::at(coord_bottom_cell, 2) =\n            -1.0 + static_cast<double>(z_idx) * gsl::at(logical_dx, 2);\n        for (size_t s = 0; s < NeutrinoSpecies; s++) {\n          for (size_t g = 0; g < EnergyBins; g++) {\n            for (size_t p = 0;\n                 p < gsl::at(gsl::at(number_of_packets_to_create_per_cell, s),\n                             g)[idx];\n                 p++) {\n              detail::draw_single_packet(&time_normalized, &coord_normalized,\n                                         &three_momentum_normalized,\n                                         random_number_generator);\n              Packet& current_packet = (*packets)[next_packet_index];\n              current_packet.species = s;\n              current_packet.time =\n                  time_start_step + time_normalized * time_step;\n              current_packet.number_of_neutrinos =\n                  gsl::at(gsl::at(single_packet_energy, s), idx) /\n                  gsl::at(energy_at_bin_center, g);\n              current_packet.index_of_closest_grid_point = idx;\n              // 4_momentum_fluid_frame contains p^mu in an orthornormal\n              // coordinate system moving with the fluid.\n              gsl::at(four_momentum_fluid_frame, 0) =\n                  gsl::at(energy_at_bin_center, g);\n              for (size_t d = 0; d < 3; d++) {\n                current_packet.coordinates.get(d) =\n                    gsl::at(coord_bottom_cell, d) +\n                    gsl::at(coord_normalized, d) * gsl::at(logical_dx, d);\n                gsl::at(four_momentum_fluid_frame, d + 1) =\n                    gsl::at(three_momentum_normalized, d) *\n                    gsl::at(four_momentum_fluid_frame, 0);\n              }\n\n              // The packet stores p^t and p_i in the inertial frame. We\n              // transform the momentum accordingly.\n              current_packet.momentum_upper_t = 0.0;\n              for (size_t d = 0; d < 4; d++) {\n                current_packet.momentum_upper_t +=\n                    inertial_to_fluid_inverse_jacobian.get(0, d)[idx] *\n                    gsl::at(four_momentum_fluid_frame, d);\n              }\n              for (size_t d = 0; d < 3; d++) {\n                // Multiply by -1.0 because p_t = -p^t in an orthonormal frame\n                current_packet.momentum.get(d) =\n                    (-1.0) * gsl::at(four_momentum_fluid_frame, 0) *\n                    inertial_to_fluid_jacobian.get(d + 1, 0)[idx];\n                for (size_t dd = 0; dd < 3; dd++) {\n                  current_packet.momentum.get(d) +=\n                      gsl::at(four_momentum_fluid_frame, dd + 1) *\n                      inertial_to_fluid_jacobian.get(d + 1, dd + 1)[idx];\n                }\n              }\n              next_packet_index++;\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/EvolvePackets.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/EvolvePackets.hpp\"\n\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Scattering.hpp\"\n\nnamespace Particles::MonteCarlo {\n\nnamespace detail {\n\n// Time derivative of p_i in a packet, according to geodesic equation\nvoid time_derivative_momentum_geodesic(\n    const gsl::not_null<std::array<double, 3>*> dt_momentum,\n    const Packet& packet, const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric) {\n  const size_t& closest_point_index = packet.index_of_closest_grid_point;\n  for (size_t i = 0; i < 3; i++) {\n    gsl::at(*dt_momentum, i) = (-1.0) * d_lapse.get(i)[closest_point_index] *\n                               get(lapse)[closest_point_index] *\n                               packet.momentum_upper_t;\n    for (size_t j = 0; j < 3; j++) {\n      gsl::at(*dt_momentum, i) +=\n          d_shift.get(i, j)[closest_point_index] * packet.momentum[j];\n      for (size_t k = 0; k < 3; k++) {\n        gsl::at(*dt_momentum, i) -=\n            0.5 * d_inv_spatial_metric.get(i, j, k)[closest_point_index] *\n            packet.momentum[j] * packet.momentum[k] / packet.momentum_upper_t;\n      }\n    }\n  }\n}\n\n}  // namespace detail\n\nvoid evolve_single_packet_on_geodesic(\n    const gsl::not_null<Packet*> packet, const double time_step,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        inverse_jacobian_logical_to_inertial) {\n  const size_t& closest_point_index = packet->index_of_closest_grid_point;\n  // Temporary variables\n  std::array<double, 3> dpdt{0, 0, 0};\n  std::array<double, 3> dxdt_inertial{0, 0, 0};\n  std::array<double, 3> dxdt_logical{0, 0, 0};\n  std::array<double, 3> p0{0, 0, 0};\n\n  // Calculate p^t from normalization of 4-momentum\n  packet->renormalize_momentum(inv_spatial_metric, lapse);\n\n  // Calculate time derivative of 3-momentum at beginning of time step\n  detail::time_derivative_momentum_geodesic(&dpdt, *packet, lapse, d_lapse,\n                                            d_shift, d_inv_spatial_metric);\n  // Take half-step (momentum only, as time derivative is independent of\n  // position)\n  for (size_t i = 0; i < 3; i++) {\n    // Store momentum at beginning of step\n    gsl::at(p0, i) = packet->momentum[i];\n    packet->momentum[i] += gsl::at(dpdt, i) * time_step * 0.5;\n  }\n  // Calculate p^t from normalization of 4-momentum\n  packet->renormalize_momentum(inv_spatial_metric, lapse);\n\n  // Calculate time derivative of 3-momentum and position at half-step\n  detail::time_derivative_momentum_geodesic(&dpdt, *packet, lapse, d_lapse,\n                                            d_shift, d_inv_spatial_metric);\n  for (size_t i = 0; i < 3; i++) {\n    gsl::at(dxdt_inertial, i) = (-1.0) * shift.get(i)[closest_point_index];\n    if (mesh_velocity.has_value()) {\n      gsl::at(dxdt_inertial, i) -=\n          mesh_velocity.value().get(i)[closest_point_index];\n    }\n    for (size_t j = 0; j < 3; j++) {\n      gsl::at(dxdt_inertial, i) +=\n          inv_spatial_metric.get(i, j)[closest_point_index] *\n          packet->momentum[j] / packet->momentum_upper_t;\n    }\n  }\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      gsl::at(dxdt_logical, i) +=\n          gsl::at(dxdt_inertial, j) *\n          inverse_jacobian_logical_to_inertial.get(i, j)[closest_point_index];\n    }\n  }\n  // Take full time step\n  for (size_t i = 0; i < 3; i++) {\n    packet->momentum[i] = gsl::at(p0, i) + gsl::at(dpdt, i) * time_step;\n    packet->coordinates[i] += gsl::at(dxdt_logical, i) * time_step;\n  }\n  packet->time += time_step;\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/EvolvePackets.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Frame {\nstruct Fluid;\n}  // namespace Frame\n\n/// Items related to the evolution of particles\n/// Items related to Monte-Carlo radiation transport\nnamespace Particles::MonteCarlo {\n\nstruct Packet;\n\nnamespace detail {\n\n// Time derivative of the spatial component of the momentum one-form on a null\n// geodesic\nvoid time_derivative_momentum_geodesic(\n    gsl::not_null<std::array<double, 3>*> dt_momentum, const Packet& packet,\n    const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric);\n\n}  // namespace detail\n\n// Advances a single packet by time time_step along a geodesic\nvoid evolve_single_packet_on_geodesic(\n    gsl::not_null<Packet*> packet, double time_step,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        inverse_jacobian_logical_to_inertial);\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/EvolvePacketsInElement.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CouplingTermsForPropagation.hpp\"\n#include \"Evolution/Particles/MonteCarlo/EvolvePackets.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Scattering.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Particles::MonteCarlo {\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nvoid TemplatedLocalFunctions<EnergyBins, NeutrinoSpecies>::\n    interpolate_opacities_at_fluid_energy(\n        gsl::not_null<double*> absorption_opacity_packet,\n        gsl::not_null<double*> scattering_opacity_packet,\n        const double fluid_frame_energy, const size_t species,\n        const size_t index,\n        const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n            absorption_opacity_table,\n        const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n            scattering_opacity_table,\n        const std::array<double, EnergyBins>& energy_at_bin_center) {\n  const auto upper_bracket =\n      std::lower_bound(energy_at_bin_center.begin(), energy_at_bin_center.end(),\n                       fluid_frame_energy);\n  if (upper_bracket == energy_at_bin_center.begin()) {\n    *absorption_opacity_packet =\n        gsl::at(gsl::at(absorption_opacity_table, species), 0)[index];\n    *scattering_opacity_packet =\n        gsl::at(gsl::at(scattering_opacity_table, species), 0)[index];\n  } else {\n    if (upper_bracket == energy_at_bin_center.end()) {\n      *absorption_opacity_packet = gsl::at(\n          gsl::at(absorption_opacity_table, species), EnergyBins - 1)[index];\n      *scattering_opacity_packet = gsl::at(\n          gsl::at(scattering_opacity_table, species), EnergyBins - 1)[index];\n    } else {\n      const auto lower_bracket = std::prev(upper_bracket);\n      const size_t lower_index = static_cast<size_t>(\n          std::distance(energy_at_bin_center.begin(), lower_bracket));\n      const size_t upper_index = lower_index + 1;\n      const double inter_coef =\n          (fluid_frame_energy - gsl::at(energy_at_bin_center, lower_index)) /\n          (gsl::at(energy_at_bin_center, upper_index) -\n           gsl::at(energy_at_bin_center, lower_index));\n      *absorption_opacity_packet =\n          log(std::max(gsl::at(gsl::at(absorption_opacity_table, species),\n                               upper_index)[index],\n                       opacity_floor)) *\n              inter_coef +\n          log(std::max(gsl::at(gsl::at(absorption_opacity_table, species),\n                               lower_index)[index],\n                       opacity_floor)) *\n              (1.0 - inter_coef);\n      *scattering_opacity_packet =\n          log(std::max(gsl::at(gsl::at(scattering_opacity_table, species),\n                               upper_index)[index],\n                       opacity_floor)) *\n              inter_coef +\n          log(std::max(gsl::at(gsl::at(scattering_opacity_table, species),\n                               lower_index)[index],\n                       opacity_floor)) *\n              (1.0 - inter_coef);\n      *absorption_opacity_packet = exp(*absorption_opacity_packet);\n      *scattering_opacity_packet = exp(*scattering_opacity_packet);\n    }\n  }\n  *absorption_opacity_packet =\n      std::max(*absorption_opacity_packet, opacity_floor);\n  *scattering_opacity_packet =\n      std::max(*scattering_opacity_packet, opacity_floor);\n}\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nvoid TemplatedLocalFunctions<EnergyBins, NeutrinoSpecies>::evolve_packets(\n    const gsl::not_null<std::vector<Packet>*> packets,\n    const gsl::not_null<std::mt19937*> random_number_generator,\n    const gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        coupling_tilde_s,\n    const gsl::not_null<Scalar<DataVector>*> coupling_rho_ye,\n    const double final_time, const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& mesh_coordinates,\n    const size_t num_ghost_zones,\n    const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n        absorption_opacity_table,\n    const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n        scattering_opacity_table,\n    const std::array<double, EnergyBins>& energy_at_bin_center,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lower_spatial_four_velocity,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& cell_light_crossing_time,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        inverse_jacobian_logical_to_inertial,\n    const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_jacobian,\n    const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_inverse_jacobian) {\n  // RNG from uniform distribution in [eps=1.e-100,1[\n  // Eps used to avoid log(0).\n  std::uniform_real_distribution<double> rng_uniform_eps_to_one(1.e-100, 1.0);\n\n  // Struct used for diffusion pre-computations\n  const DiffusionMonteCarloParameters diffusion_params;\n  auto prefactor_diffusion_time_step = make_with_value<DataVector>(lapse, 0.0);\n  auto prefactor_diffusion_four_velocity =\n      make_with_value<DataVector>(lapse, 0.0);\n  auto prefactor_diffusion_time_vector =\n      make_with_value<DataVector>(lapse, 0.0);\n\n  DiffusionPrecomputeForElement(\n      &prefactor_diffusion_time_vector, &prefactor_diffusion_four_velocity,\n      &prefactor_diffusion_time_step, lorentz_factor,\n      lower_spatial_four_velocity, lapse, shift, spatial_metric);\n\n  // Mesh information.\n  const Index<3>& extents = mesh.extents();\n  const std::array<double, 3> bottom_coord_mesh{mesh_coordinates.get(0)[0],\n                                                mesh_coordinates.get(1)[0],\n                                                mesh_coordinates.get(2)[0]};\n  const Index<3> extents_with_ghost{\n    extents[0] + 2 * num_ghost_zones,\n    extents[1] + 2 * num_ghost_zones,\n    extents[2] + 2 * num_ghost_zones};\n  const std::array<size_t, 3> step{1, extents[0], extents[0] * extents[1]};\n  const std::array<size_t, 3> step_with_ghost_zones{1,\n    extents[0] + 2 * num_ghost_zones,\n    (extents[0] + 2 * num_ghost_zones) * (extents[1] + 2 * num_ghost_zones)};\n  const std::array<double, 3> dx_mesh{\n      mesh_coordinates.get(0)[step[0]] - bottom_coord_mesh[0],\n      mesh_coordinates.get(1)[step[1]] - bottom_coord_mesh[1],\n      mesh_coordinates.get(2)[step[2]] - bottom_coord_mesh[2]};\n\n  // Temporary variables keeping track of opacities and times to next events\n  double fluid_frame_energy = -1.0;\n  double absorption_opacity = 0.0;\n  double scattering_opacity = 0.0;\n  double dt_end_step = -1.0;\n  double dt_cell_check = -1.0;\n  double dt_absorption = -1.0;\n  double dt_scattering = -1.0;\n  double dt_min = -1.0;\n  double initial_time = -1.0;\n  // Loop over packets\n  size_t n_packets = packets->size();\n  for (size_t p = 0; p < n_packets; p++) {\n    Packet& packet = (*packets)[p];\n\n    initial_time = packet.time;\n    dt_end_step = final_time - initial_time;\n\n    // Find closest grid point to packet at current time, using\n    // extents for live points only.\n    {\n      Index<3> closest_point_index_3d{0, 0, 0};\n      for (size_t d = 0; d < 3; d++) {\n        closest_point_index_3d[d] =\n          std::floor((packet.coordinates[d] - gsl::at(bottom_coord_mesh, d)) /\n                         gsl::at(dx_mesh, d) +\n                     0.5);\n      }\n      packet.index_of_closest_grid_point = collapsed_index(\n        closest_point_index_3d, extents);\n    }\n\n    // Get quantities that we do NOT update if the packet\n    // changes cell.\n    // local_idx is the index on the mesh without ghost zones\n    // extended_idx is the index on the mesh with ghost zones\n    // We only update extended_idx during a time step, as we\n    // only recompute the opacities during a step (and they\n    // live on the extended grid). We also need the extended\n    // index for coupling to the fluid.\n    const size_t& local_idx = packet.index_of_closest_grid_point;\n    Index<3> index_3d = expanded_index(local_idx,extents);\n    for(size_t d=0; d<3 ; d++){\n      index_3d[d] += num_ghost_zones;\n    }\n    size_t extended_idx = collapsed_index(\n      index_3d, extents_with_ghost);\n\n    // Bookkeeping variable to know whether opacities should be\n    // recomputed.\n    size_t previous_extended_idx = extended_idx;\n\n    const double& lapse_packet = get(lapse)[local_idx];\n    const double& lorentz_factor_packet = get(lorentz_factor)[local_idx];\n    const std::array<double, 3> lower_spatial_four_velocity_packet = {\n        lower_spatial_four_velocity.get(0)[local_idx],\n        lower_spatial_four_velocity.get(1)[local_idx],\n        lower_spatial_four_velocity.get(2)[local_idx]};\n\n    // Estimate light-crossing time in the cell.\n    const double& cell_light_crossing_time_packet =\n      get(cell_light_crossing_time)[local_idx];\n\n    // Get fluid frame energy of neutrinos in packet, then retrieve\n    // opacities at current points and neighboring points. We do\n    // not have interactions that modify the fluid frame energy\n    // of the packets so far, so we precompute it.\n    packet.renormalize_momentum(inv_spatial_metric, lapse);\n    fluid_frame_energy = compute_fluid_frame_energy(\n        packet, lorentz_factor, lower_spatial_four_velocity, lapse,\n        inv_spatial_metric);\n\n    // Find maximum total opacity in current cell and\n    // neighbors, to limit time step in high opacity regions\n    // Need to properly deal with ghost zones.\n    // Opacities calculated at the current location of the packet\n    this->interpolate_opacities_at_fluid_energy(\n        &absorption_opacity, &scattering_opacity, fluid_frame_energy,\n        packet.species, extended_idx, absorption_opacity_table,\n        scattering_opacity_table, energy_at_bin_center);\n    double max_opacity = absorption_opacity + scattering_opacity;\n    for (size_t d = 0; d < 3; d++) {\n      double ka_neighbor = 0.0;\n      double ks_neighbor = 0.0;\n      this->interpolate_opacities_at_fluid_energy(\n          &ka_neighbor, &ks_neighbor, fluid_frame_energy, packet.species,\n          extended_idx - step_with_ghost_zones[d],\n          absorption_opacity_table, scattering_opacity_table,\n          energy_at_bin_center);\n      max_opacity = std::max(max_opacity, ka_neighbor + ks_neighbor);\n      this->interpolate_opacities_at_fluid_energy(\n          &ka_neighbor, &ks_neighbor, fluid_frame_energy, packet.species,\n          extended_idx + step_with_ghost_zones[d],\n          absorption_opacity_table, scattering_opacity_table,\n          energy_at_bin_center);\n      max_opacity = std::max(max_opacity, ka_neighbor + ks_neighbor);\n    }\n    // Minimum fraction of light crossing time used for time step.\n    // This is a compromise between taking very small steps in high\n    // opacity regions close to cell boundaries, and minimizing\n    // computational costs.\n    const double fmin = std::max(\n        0.03, 0.1 / (max_opacity * cell_light_crossing_time_packet\n                     + opacity_floor));\n\n    // We evolve until at least 95 percent of the desired step.\n    // We don't require the full step because diffusion in the fluid\n    // frame leads to unpredictable time steps in the inertial frame,\n    // and we want to avoid taking a lot of potentially small steps\n    // when reaching the end of the desired step.\n    while (dt_end_step > 0.05 * (final_time - initial_time)) {\n\n      // Shortest distance to a grid boundary, in units of the grid spacing.\n      // Note that this does not use the extents, so it is safe to use in the\n      // ghost zones... if we allow negative indices.\n      double frac_min_grid = 1.0;\n      {\n        std::array<int, 3> closest_point_index_3d{0, 0, 0};\n        for (size_t d = 0; d < 3; d++) {\n          gsl::at(closest_point_index_3d, d) = std::floor(\n              (packet.coordinates[d] - gsl::at(bottom_coord_mesh, d)) /\n                  gsl::at(dx_mesh, d) +\n              0.5);\n          double frac_grid =\n              (packet.coordinates[d] - gsl::at(bottom_coord_mesh, d) -\n               gsl::at(closest_point_index_3d, d) * gsl::at(dx_mesh, d)) /\n                  gsl::at(dx_mesh, d) +\n              0.5;\n          frac_min_grid = std::min(frac_min_grid, frac_grid + fmin);\n          frac_min_grid = std::min(frac_min_grid, 1.0 - frac_grid + fmin);\n        }\n      }\n\n      // Recompute opacities if needed\n      if(previous_extended_idx != extended_idx){\n        this->interpolate_opacities_at_fluid_energy(\n          &absorption_opacity, &scattering_opacity, fluid_frame_energy,\n          packet.species, extended_idx, absorption_opacity_table,\n          scattering_opacity_table, energy_at_bin_center);\n      }\n\n      // Determine time to next events\n      dt_min = dt_end_step;\n      // Limit time step close to cell boundary in high-opacity regions\n      dt_cell_check = frac_min_grid * cell_light_crossing_time_packet;\n      dt_min = std::min(dt_cell_check, dt_min);\n      // Time step to next absorption is\n      // -ln(r)/K_a*p^t/nu\n      dt_absorption =\n          absorption_opacity > opacity_floor\n              ? -log(rng_uniform_eps_to_one(*random_number_generator)) /\n                    (absorption_opacity)*packet.momentum_upper_t /\n                    fluid_frame_energy\n              : 10.0 * dt_end_step;\n      dt_min = std::min(dt_absorption, dt_min);\n      // Time step to next scattering is\n      // -ln(r)/K_s*p^t/nu\n      dt_scattering =\n          scattering_opacity > opacity_floor\n              ? -log(rng_uniform_eps_to_one(*random_number_generator)) /\n                    (scattering_opacity)*packet.momentum_upper_t /\n                    fluid_frame_energy\n              : 10.0 * dt_end_step;\n      dt_min = std::min(dt_scattering, dt_min);\n\n      // Propagation to the next event, whatever it is\n      evolve_single_packet_on_geodesic(&packet, dt_min, lapse, shift, d_lapse,\n                                       d_shift, d_inv_spatial_metric,\n                                       inv_spatial_metric, mesh_velocity,\n                                       inverse_jacobian_logical_to_inertial);\n      AddCouplingTermsForPropagation(\n          coupling_tilde_tau, coupling_tilde_s, coupling_rho_ye, packet,\n          extended_idx, dt_min,\n          absorption_opacity, scattering_opacity, fluid_frame_energy,\n          lapse_packet, lorentz_factor_packet,\n          lower_spatial_four_velocity_packet);\n\n      // If absorption is the first event, we just delete\n      // the packet.\n      //\n      // To remove this packet we swap the current and last packet,\n      // then pop the last packet from the end of the vector of packets.\n      // We then decrease the counter `p` so that we check the packet\n      // from the end that we just swapped into the current slot.\n      //\n      // Note: This works fine even if p==0 since unsigned ints (size_t)\n      // wrap at zero.\n      if (dt_min == dt_absorption) {\n        std::swap((*packets)[p], (*packets)[n_packets - 1]);\n        packets->pop_back();\n        p--;\n        n_packets--;\n        break;\n      }\n      // If the next event was a scatter, perform that scatter and\n      // continue evolution\n      if (dt_min == dt_scattering) {\n        // Next event is a scatter. Calculate the time step to the next\n        // non-scattering event, and the scattering optical depth over\n        // that period.\n        dt_end_step -= dt_min;\n        dt_absorption -= dt_min;\n        dt_cell_check -= dt_min;\n        dt_min = dt_end_step;\n        dt_min = std::min(dt_cell_check, dt_min);\n        dt_min = std::min(dt_absorption, dt_min);\n        const double scattering_optical_depth = dt_min * scattering_opacity *\n                                         lapse_packet / lorentz_factor_packet;\n        // High optical depth: use approximate diffusion method to move packet\n        // The scatterig depth of 3.0 was found to be sufficient for diffusion\n        // to be accurate (see Foucart 2018, 10.1093/mnras/sty108)\n        if (scattering_optical_depth > 3.0) {\n          diffuse_packet(\n              &packet, random_number_generator, &fluid_frame_energy,\n              coupling_tilde_tau, coupling_tilde_s, coupling_rho_ye,\n              extended_idx, dt_min,\n              diffusion_params, absorption_opacity, scattering_opacity,\n              lorentz_factor, lower_spatial_four_velocity, lapse, shift,\n              d_lapse, d_shift, d_inv_spatial_metric, spatial_metric,\n              inv_spatial_metric, mesh_velocity,\n              inverse_jacobian_logical_to_inertial, inertial_to_fluid_jacobian,\n              inertial_to_fluid_inverse_jacobian, prefactor_diffusion_time_step,\n              prefactor_diffusion_four_velocity,\n              prefactor_diffusion_time_vector);\n        } else {\n          // Low optical depth; perform scatterings one by one.\n          do {\n            scatter_packet(&packet, random_number_generator, fluid_frame_energy,\n                           inertial_to_fluid_jacobian,\n                           inertial_to_fluid_inverse_jacobian);\n            // Time to next scattering event.\n            dt_scattering =\n                scattering_opacity > opacity_floor\n                    ? -log(rng_uniform_eps_to_one(*random_number_generator)) /\n                          (scattering_opacity)*packet.momentum_upper_t /\n                          fluid_frame_energy\n                    : 10.0 * dt_end_step;\n            dt_min = std::min(dt_scattering, dt_min);\n            // Propagation to the next event, whatever it is\n            evolve_single_packet_on_geodesic(\n                &packet, dt_min, lapse, shift, d_lapse, d_shift,\n                d_inv_spatial_metric, inv_spatial_metric, mesh_velocity,\n                inverse_jacobian_logical_to_inertial);\n            AddCouplingTermsForPropagation(\n                coupling_tilde_tau, coupling_tilde_s, coupling_rho_ye, packet,\n                extended_idx, dt_min, absorption_opacity, scattering_opacity,\n                fluid_frame_energy, lapse_packet, lorentz_factor_packet,\n                lower_spatial_four_velocity_packet);\n            dt_end_step -= dt_min;\n            dt_absorption -= dt_min;\n            dt_cell_check -= dt_min;\n            dt_min = dt_end_step;\n            dt_min = std::min(dt_cell_check, dt_min);\n            dt_min = std::min(dt_absorption, dt_min);\n          } while (dt_min > 0.0);\n        }\n        // If absorption is the next event; delete the packet\n        if (dt_min == dt_absorption) {\n          (*packets)[p] = (*packets)[n_packets - 1];\n          packets->pop_back();\n          p--;\n          n_packets--;\n          break;\n        }\n      }\n\n      // Index of the new cells including ghost zones.\n      Index<3> closest_point_index_3d{0, 0, 0};\n      for (size_t d = 0; d < 3; d++) {\n        closest_point_index_3d[d] =\n            num_ghost_zones +\n            std::floor((packet.coordinates[d] - gsl::at(bottom_coord_mesh, d)) /\n                           gsl::at(dx_mesh, d) +\n                       0.5);\n      }\n      previous_extended_idx = extended_idx;\n      extended_idx = collapsed_index(\n        closest_point_index_3d, extents_with_ghost);\n\n      // Update time to end of step\n      dt_end_step = final_time - packet.time;\n    }\n\n    // Find closest grid point to packet at current time, using\n    // extents for live points only.\n    Index<3> closest_point_index_3d{0, 0, 0};\n    bool packet_out_of_bounds = false;\n    for (size_t d = 0; d < 3; d++) {\n      if(packet.coordinates[d]<-1.0 || packet.coordinates[d]>1.0){\n        packet_out_of_bounds = true;\n        break;\n      }\n      closest_point_index_3d[d] =\n          std::floor((packet.coordinates[d] - gsl::at(bottom_coord_mesh, d)) /\n                         gsl::at(dx_mesh, d) +\n                     0.5);\n    }\n    // Update index of packet at the end of a step.\n    // Note that we mark out of bounds packets with an out of bound\n    // index.\n    if(packet_out_of_bounds){\n      packet.index_of_closest_grid_point = mesh.number_of_grid_points();\n    } else{\n      packet.index_of_closest_grid_point = collapsed_index(\n        closest_point_index_3d, extents);\n    }\n  }\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/GhostZoneCommunication.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunicationStep.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunicationTags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MortarData.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Particles::MonteCarlo::Actions {\n\n/// Mutator to get required volume data for communication\n/// before a MC step; i.e. data sent from live points\n/// to ghost points in neighbors.\nstruct GhostDataMutatorPreStep {\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<\n      hydro::Tags::RestMassDensity<DataVector>,\n      hydro::Tags::ElectronFraction<DataVector>,\n      hydro::Tags::Temperature<DataVector>,\n      Particles::MonteCarlo::Tags::CellLightCrossingTime<DataVector>>;\n  static const size_t number_of_vars = 4;\n\n  static DataVector apply(const Scalar<DataVector>& rest_mass_density,\n                          const Scalar<DataVector>& electron_fraction,\n                          const Scalar<DataVector>& temperature,\n                          const Scalar<DataVector>& cell_light_crossing_time) {\n    const size_t dv_size = get(rest_mass_density).size();\n    DataVector buffer{dv_size * number_of_vars};\n    std::copy(\n        get(rest_mass_density).data(),\n        std::next(get(rest_mass_density).data(), static_cast<int>(dv_size)),\n        buffer.data());\n    std::copy(\n        get(electron_fraction).data(),\n        std::next(get(electron_fraction).data(), static_cast<int>(dv_size)),\n        std::next(buffer.data(), static_cast<int>(dv_size)));\n    std::copy(get(temperature).data(),\n              std::next(get(temperature).data(), static_cast<int>(dv_size)),\n              std::next(buffer.data(), static_cast<int>(dv_size * 2)));\n    std::copy(get(cell_light_crossing_time).data(),\n              std::next(get(cell_light_crossing_time).data(),\n                        static_cast<int>(dv_size)),\n              std::next(buffer.data(), static_cast<int>(dv_size * 3)));\n    return buffer;\n  }\n};\n\n/// Mutator to get required volume data for communication\n/// after a MC step; i.e. data sent from ghost points\n/// in neighbors to live points evolving the fluid. The\n/// data contains information about the back reaction of\n/// neutrinos on the fluid\ntemplate <size_t Dim>\nstruct GhostDataMutatorPostStep {\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>>;\n  static const size_t number_of_components = 2 + Dim;\n\n  static DataVector apply(const Scalar<DataVector>& coupling_tilde_tau,\n                          const Scalar<DataVector>& coupling_tilde_rho_ye,\n                          const tnsr::i<DataVector, Dim>& coupling_tilde_s) {\n    const size_t dv_size = get(coupling_tilde_tau).size();\n    DataVector buffer{dv_size * number_of_components};\n    std::copy(\n        get(coupling_tilde_tau).data(),\n        std::next(get(coupling_tilde_tau).data(), static_cast<int>(dv_size)),\n        buffer.data());\n    std::copy(\n        get(coupling_tilde_rho_ye).data(),\n        std::next(get(coupling_tilde_rho_ye).data(), static_cast<int>(dv_size)),\n        std::next(buffer.data(), static_cast<int>(dv_size)));\n    for (size_t d = 0; d < Dim; d++) {\n      std::copy(\n          coupling_tilde_s.get(d).data(),\n          std::next(coupling_tilde_s.get(d).data(), static_cast<int>(dv_size)),\n          std::next(buffer.data(), static_cast<int>(dv_size * (2 + d))));\n    }\n    return buffer;\n  }\n};\n\n/// Mutator that returns packets currently in ghost zones in a\n/// DirectionMap<Dim,std::vector<Particles::MonteCarlo::Packet>>\n/// and remove them from the list of packets of the current\n/// element\ntemplate <size_t Dim>\nstruct GhostDataMcPackets {\n  using return_tags = tmpl::list<Particles::MonteCarlo::Tags::PacketsOnElement>;\n  using argument_tags = tmpl::list<::domain::Tags::Element<Dim>>;\n\n  static DirectionMap<Dim, std::vector<Particles::MonteCarlo::Packet>> apply(\n      const gsl::not_null<std::vector<Particles::MonteCarlo::Packet>*> packets,\n      const Element<Dim>& element) {\n    DirectionMap<Dim, std::vector<Particles::MonteCarlo::Packet>> output{};\n    for (const auto& [direction, neighbors_in_direction] :\n         element.neighbors()) {\n      output[direction] = std::vector<Particles::MonteCarlo::Packet>{};\n    }\n    // Loop over packets. If a packet is out of the current element,\n    // remove it from the element and copy it to the appropriate\n    // neighbor list if it exists.\n    // Note: At this point, we only handle simple boundaries\n    // (one neighbor per direction, with the logical coordinates\n    // being transformed by adding/removing 2). We assume that\n    // packets that go out of the element but do not match any\n    // neighbor have reached the domain boundary and can be removed.\n    size_t n_packets = packets->size();\n    for (size_t p = 0; p < n_packets; p++) {\n      Packet& packet = (*packets)[p];\n      std::optional<Direction<Dim>> max_distance_direction = std::nullopt;\n      double max_distance = 0.0;\n      // TO DO: Deal with dimensions higher than the grid dimension\n      // e.g. for axisymmetric evolution\n      for (size_t d = 0; d < Dim; d++) {\n        if (packet.coordinates.get(d) < -1.0) {\n          const double distance = -1.0 - packet.coordinates.get(d);\n          if (max_distance < distance) {\n            max_distance = distance;\n            max_distance_direction = Direction<Dim>(d, Side::Lower);\n          }\n        }\n        if (packet.coordinates.get(d) > 1.0) {\n          const double distance = packet.coordinates.get(d) - 1.0;\n          if (max_distance < distance) {\n            max_distance = distance;\n            max_distance_direction = Direction<Dim>(d, Side::Upper);\n          }\n        }\n      }\n      // If a packet should be moved along an edge / corner, we move\n      // it into the direct neighbor it is effectively closest to\n      // (in topological coordinates). This may need improvements.\n      if (max_distance_direction != std::nullopt) {\n        const size_t d = max_distance_direction.value().dimension();\n        const Side side = max_distance_direction.value().side();\n        packet.coordinates.get(d) += (side == Side::Lower) ? (2.0) : (-2.0);\n        // Corner/edge treatment; move packet in a live point for now\n        // Definitely needs improvement...\n        for (size_t dd = 0; dd < Dim; dd++) {\n          if (dd != d && packet.coordinates.get(dd) < -1.0) {\n            packet.coordinates.get(dd) = -2.0 - packet.coordinates.get(dd);\n          }\n          if (dd != d && packet.coordinates.get(dd) > 1.0) {\n            packet.coordinates.get(dd) = 2.0 - packet.coordinates.get(dd);\n          }\n        }\n        if (output.contains(max_distance_direction.value())) {\n          output[max_distance_direction.value()].push_back(packet);\n        }\n        std::swap((*packets)[p], (*packets)[n_packets - 1]);\n        packets->pop_back();\n        p--;\n        n_packets--;\n      }\n    }\n    return output;\n  }\n};\n\n// Take the data for coupling MC to hydro gathered from neighboring\n// ghost zones (stored after communication in GhostZoneCouplingDataTag)\n// and add them to the coupling terms in live zones. After calling this\n// action, the coupling terms CouplingTildeTau, CouplingTildeRhoYe,\n// CouplingTildeS should contain the changes to the energy, RhoYe, and\n// momentum of the fluid due to MC packets evolved locally and those\n// that evolved within ghost zones on neighboring elements.\n// THIS FUNCTION IS CURRENTLY ONLY IMPLENTED IN 3D!\n// As other parts of the MC communication, it is also incompatible with\n// mesh refinement.\ntemplate <size_t Dim>\nstruct CombineCouplingDataPostStep {\n  using return_tags =\n      tmpl::list<Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,\n                 Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>>;\n  using argument_tags =\n      tmpl::list<Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<Dim>,\n                 ::domain::Tags::Element<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n      gsl::not_null<Scalar<DataVector>*> coupling_tilde_rho_ye,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          coupling_tilde_s,\n      const Particles::MonteCarlo::GhostZoneCouplingData<Dim>& coupling_data,\n      const Element<Dim>& element, const Mesh<Dim>& subcell_mesh) {\n    // Check that we do not use this function for 1D/2D runs\n    ASSERT(Dim == 3, \"CombineCouplingDataPostStep only coded in 3D so far\");\n\n    const size_t num_ghost_zones = 1;\n    const Index<Dim> local_extents = subcell_mesh.extents();\n    Index<Dim> ghost_extents = local_extents;\n    for (size_t d = 0; d < 3; d++) {\n      ghost_extents[d] += 2 * num_ghost_zones;\n    }\n    // Loop over neighboring elements\n    for (const auto& [direction, neighbors_in_direction] :\n         element.neighbors()) {\n      for (const auto& neighbor : neighbors_in_direction) {\n        DirectionalId<Dim> directional_element_id{direction, neighbor};\n        if (coupling_data.coupling_tilde_tau.at(directional_element_id) ==\n            std::nullopt) {\n          continue;\n        }\n        const size_t dimension = directional_element_id.direction().dimension();\n        const Side side = directional_element_id.direction().side();\n        // Extents of coupling data: mesh with ghost points, sliced\n        // in direction 'dimension'\n        Index<Dim> ghost_zone_extents = ghost_extents;\n        ghost_zone_extents[dimension] = num_ghost_zones;\n        // These loops only work in 3D; needs fixing for other dimensions\n        for (size_t i = 0; i < ghost_zone_extents[0]; ++i) {\n          for (size_t j = 0; j < ghost_zone_extents[1]; ++j) {\n            for (size_t k = 0; k < ghost_zone_extents[2]; ++k) {\n              Index<Dim> index_3d{i, j, k};\n              // Index of point in ghost zone data\n              const size_t ghost_index =\n                  collapsed_index(index_3d, ghost_zone_extents);\n              // Index of corresponding live point within the full\n              // mesh, with ghost zones.\n              // For two ghost zones e.g., the 1d mesh looks like\n              //        x x o o o o o o x x\n              // with neighbors\n              //  o o o o o x x     x x o o o o o\n              // with x = GZ, o = live points. On the lower face,\n              // we thus add data from the first ghost point to\n              // index num_ghost_zones. On the upper face, the first\n              // ghost point goes to index local_extents (the size of\n              // the mesh without ghost zone).\n              index_3d[dimension] =\n                  (side == Side::Lower)\n                      ? index_3d[dimension] + num_ghost_zones\n                      : local_extents[dimension] + index_3d[dimension];\n              const size_t extended_index =\n                  collapsed_index(index_3d, ghost_extents);\n              // Add gathered data to existing coupling terms\n              coupling_tilde_tau->get()[extended_index] +=\n                  coupling_data.coupling_tilde_tau.at(directional_element_id)\n                      .value()[ghost_index];\n              coupling_tilde_rho_ye->get()[extended_index] +=\n                  coupling_data.coupling_tilde_rho_ye.at(directional_element_id)\n                      .value()[ghost_index];\n              for (size_t d = 0; d < Dim; d++) {\n                coupling_tilde_s->get(d)[extended_index] +=\n                    coupling_data.coupling_tilde_s.at(directional_element_id)\n                        .value()\n                        .get(d)[ghost_index];\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n};\n\n/// Action responsible for the Send operation of ghost\n/// zone communication for Monte-Carlo transport.\n/// If CommStep == PreStep, this sends the fluid and\n/// metric variables needed for evolution of MC packets.\n/// If CommStep == PostStep, this sends packets that\n/// have moved from one element to another as well\n/// as coupling information from the ghost zone to the\n/// live points.\ntemplate <size_t Dim, bool LocalTimeStepping,\n          Particles::MonteCarlo::CommunicationStep CommStep>\nstruct SendDataForMcCommunication {\n  using inbox_tags =\n      tmpl::list<Particles::MonteCarlo::McGhostZoneDataInboxTag<Dim, CommStep>>;\n\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(not LocalTimeStepping,\n                  \"Monte Carlo is not tested with local time stepping.\");\n\n    ASSERT(db::get<evolution::dg::subcell::Tags::ActiveGrid>(box) ==\n               evolution::dg::subcell::ActiveGrid::Subcell,\n           \"The SendDataForReconstructionPreStep action in MC \"\n           \"can only be called when Subcell is the active scheme.\");\n    db::mutate<Particles::MonteCarlo::Tags::McGhostZoneDataTag<Dim>>(\n        [](const auto ghost_data_ptr) {\n          // Clear the previous neighbor data and add current local data\n          ghost_data_ptr->clear();\n        },\n        make_not_null(&box));\n\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    const size_t ghost_zone_size = 1;\n\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n    const Mesh<Dim>& subcell_mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(box);\n    const TimeStepId& time_step_id = db::get<::Tags::TimeStepId>(box);\n    Index<Dim> extents_with_ghost_zone = subcell_mesh.extents();\n    for (size_t d = 0; d < Dim; d++) {\n      extents_with_ghost_zone[d] += 2 * ghost_zone_size;\n    }\n\n    // Fill volume data that should be fed to neighbor ghost zones\n    // PreStep, we send fluid data from the live points to the ghost points.\n    // PostStep, we send coupling data from the ghost points to the live\n    // points (note the different DataVector sizes; the PostStep data has\n    // different dimensions because it is taken from DataVectors including\n    // ghost points.\n    DataVector volume_data_to_slice =\n        CommStep == Particles::MonteCarlo::CommunicationStep::PreStep\n            ? db::mutate_apply(GhostDataMutatorPreStep{}, make_not_null(&box))\n            : db::mutate_apply(GhostDataMutatorPostStep<Dim>{},\n                               make_not_null(&box));\n    const DirectionMap<Dim, DataVector> all_sliced_data =\n        CommStep == Particles::MonteCarlo::CommunicationStep::PreStep\n            ? evolution::dg::subcell::slice_data(\n                  volume_data_to_slice, subcell_mesh.extents(), ghost_zone_size,\n                  element.internal_boundaries(), 0,\n                  db::get<evolution::dg::subcell::Tags::\n                              InterpolatorsFromFdToNeighborFd<Dim>>(box))\n            : evolution::dg::subcell::slice_data(\n                  volume_data_to_slice, extents_with_ghost_zone,\n                  ghost_zone_size, element.internal_boundaries(), 0,\n                  db::get<evolution::dg::subcell::Tags::\n                              InterpolatorsFromFdToNeighborFd<Dim>>(box));\n    const DirectionMap<Dim, std::vector<Particles::MonteCarlo::Packet>>\n        all_packets_ghost_zone =\n            CommStep == Particles::MonteCarlo::CommunicationStep::PostStep\n                ? db::mutate_apply(GhostDataMcPackets<Dim>{},\n                                   make_not_null(&box))\n                : DirectionMap<Dim,\n                               std::vector<Particles::MonteCarlo::Packet>>{};\n\n    for (const auto& [direction, neighbors_in_direction] :\n         element.neighbors()) {\n      for (const ElementId<Dim>& neighbor : neighbors_in_direction) {\n        const auto& orientation = neighbors_in_direction.orientation(neighbor);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        std::optional<std::vector<Particles::MonteCarlo::Packet>>\n            packets_to_send = std::nullopt;\n        DataVector subcell_data_to_send{};\n\n        // PreStep and PostStep both send slice data to neighboring domains\n        const auto& sliced_data_in_direction = all_sliced_data.at(direction);\n        subcell_data_to_send = DataVector{sliced_data_in_direction.size()};\n        std::copy(sliced_data_in_direction.begin(),\n                  sliced_data_in_direction.end(), subcell_data_to_send.begin());\n        if (CommStep == Particles::MonteCarlo::CommunicationStep::PostStep) {\n          packets_to_send = all_packets_ghost_zone.at(direction);\n          if (packets_to_send.value().empty()) {\n            packets_to_send = std::nullopt;\n          }\n        }\n\n        McGhostZoneData<Dim> data{subcell_data_to_send, packets_to_send};\n\n        Parallel::receive_data<\n            Particles::MonteCarlo::McGhostZoneDataInboxTag<Dim, CommStep>>(\n            receiver_proxy[neighbor], time_step_id,\n            std::pair{DirectionalId<Dim>{direction_from_neighbor, element.id()},\n                      std::move(data)});\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// Action responsible for the Receive operation of ghost\n/// zone communication for Monte-Carlo transport.\n/// If CommStep == PreStep, this gets the fluid and\n/// metric variables needed for evolution of MC packets.\n/// If CommStep == PostStep, this gets packets that\n/// have moved into the current element.\n/// The current code does not yet deal with data required\n/// to calculate the backreaction on the fluid.\ntemplate <size_t Dim, CommunicationStep CommStep>\nstruct ReceiveDataForMcCommunication {\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const Element<Dim>& element = db::get<::domain::Tags::Element<Dim>>(box);\n    const auto number_of_expected_messages = element.neighbors().size();\n    if (UNLIKELY(number_of_expected_messages == 0)) {\n      // We have no neighbors, so just continue without doing any work\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const auto& current_time_step_id = db::get<::Tags::TimeStepId>(box);\n    std::map<TimeStepId, DirectionalIdMap<\n                             Dim, Particles::MonteCarlo::McGhostZoneData<Dim>>>&\n        inbox = tuples::get<Particles::MonteCarlo::McGhostZoneDataInboxTag<\n            Metavariables::volume_dim, CommStep>>(inboxes);\n    const auto& received = inbox.find(current_time_step_id);\n    // Check we have at least some data from correct time, and then check that\n    // we have received all data\n    if (received == inbox.end() or\n        received->second.size() != number_of_expected_messages) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Now that we have received all the data, copy it over as needed.\n    DirectionalIdMap<Dim, Particles::MonteCarlo::McGhostZoneData<Dim>>\n        received_data = std::move(inbox[current_time_step_id]);\n    inbox.erase(current_time_step_id);\n\n    // Copy the received PreStep data in the MortarData container\n    if (CommStep == Particles::MonteCarlo::CommunicationStep::PreStep) {\n      db::mutate<Particles::MonteCarlo::Tags::MortarDataTag<Dim>>(\n          [&element,\n           &received_data](const gsl::not_null<MortarData<Dim>*> mortar_data) {\n            for (const auto& [direction, neighbors_in_direction] :\n                 element.neighbors()) {\n              for (const auto& neighbor : neighbors_in_direction) {\n                DirectionalId<Dim> directional_element_id{direction, neighbor};\n                const DataVector& received_data_direction =\n                    received_data[directional_element_id]\n                        .ghost_zone_hydro_variables;\n                if (mortar_data->rest_mass_density[directional_element_id] ==\n                    std::nullopt) {\n                  continue;\n                }\n                const size_t mortar_data_size =\n                    mortar_data->rest_mass_density[directional_element_id]\n                        .value()\n                        .size();\n                ASSERT(received_data_direction.size() == mortar_data_size * 4,\n                       \"Inconsistent sizes between inbox and mortar data\");\n                std::copy(received_data_direction.data(),\n                          std::next(received_data_direction.data(),\n                                    static_cast<int>(mortar_data_size)),\n                          mortar_data->rest_mass_density[directional_element_id]\n                              .value()\n                              .data());\n                std::copy(std::next(received_data_direction.data(),\n                                    static_cast<int>(mortar_data_size)),\n                          std::next(received_data_direction.data(),\n                                    static_cast<int>(mortar_data_size * 2)),\n                          mortar_data->electron_fraction[directional_element_id]\n                              .value()\n                              .data());\n                std::copy(std::next(received_data_direction.data(),\n                                    static_cast<int>(mortar_data_size * 2)),\n                          std::next(received_data_direction.data(),\n                                    static_cast<int>(mortar_data_size * 3)),\n                          mortar_data->temperature[directional_element_id]\n                              .value()\n                              .data());\n                std::copy(std::next(received_data_direction.data(),\n                                    static_cast<int>(mortar_data_size * 3)),\n                          std::next(received_data_direction.data(),\n                                    static_cast<int>(mortar_data_size * 4)),\n                          mortar_data\n                              ->cell_light_crossing_time[directional_element_id]\n                              .value()\n                              .data());\n              }\n            }\n          },\n          make_not_null(&box));\n    } else {\n      const Mesh<Dim>& subcell_mesh =\n          db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(box);\n      db::mutate<Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<Dim>,\n                 Particles::MonteCarlo::Tags::PacketsOnElement>(\n          [&element, &received_data, &subcell_mesh](\n              const gsl::not_null<GhostZoneCouplingData<Dim>*> coupling_data,\n              const gsl::not_null<std::vector<Particles::MonteCarlo::Packet>*>\n                  packet_list) {\n            // Two parts here: first deal with coupling data brought back\n            // from ghost zones to live points; then handle packet\n            // communication.\n            const Index<Dim>& extents = subcell_mesh.extents();\n            for (const auto& [direction, neighbors_in_direction] :\n                 element.neighbors()) {\n              for (const auto& neighbor : neighbors_in_direction) {\n                DirectionalId<Dim> directional_element_id{direction, neighbor};\n                // Move boundary data to the coupling_data structure for later\n                // use\n                const DataVector& received_data_direction =\n                    received_data[directional_element_id]\n                        .ghost_zone_hydro_variables;\n                if (coupling_data->coupling_tilde_tau[directional_element_id] !=\n                    std::nullopt) {\n                  const size_t coupling_data_size =\n                      coupling_data->coupling_tilde_tau[directional_element_id]\n                          .value()\n                          .size();\n                  ASSERT(received_data_direction.size() ==\n                             coupling_data_size * (2 + Dim),\n                         \"Inconsistent sizes between inbox and coupling data\");\n                  std::copy(\n                      received_data_direction.data(),\n                      std::next(received_data_direction.data(),\n                                static_cast<int>(coupling_data_size)),\n                      coupling_data->coupling_tilde_tau[directional_element_id]\n                          .value()\n                          .data());\n                  std::copy(std::next(received_data_direction.data(),\n                                      static_cast<int>(coupling_data_size)),\n                            std::next(received_data_direction.data(),\n                                      static_cast<int>(coupling_data_size * 2)),\n                            coupling_data\n                                ->coupling_tilde_rho_ye[directional_element_id]\n                                .value()\n                                .data());\n                  for (size_t d = 0; d < Dim; d++) {\n                    std::copy(\n                        std::next(\n                            received_data_direction.data(),\n                            static_cast<int>(coupling_data_size * (2 + d))),\n                        std::next(\n                            received_data_direction.data(),\n                            static_cast<int>(coupling_data_size * (3 + d))),\n                        coupling_data->coupling_tilde_s[directional_element_id]\n                            .value()\n                            .get(d)\n                            .data());\n                  }\n                }\n                // Get packet data\n                std::optional<std::vector<Particles::MonteCarlo::Packet>>&\n                    received_data_packets =\n                        received_data[directional_element_id]\n                            .packets_entering_this_element;\n                if (received_data_packets == std::nullopt) {\n                  continue;\n                } else {\n                  const size_t n_packets = received_data_packets.value().size();\n                  for (size_t p = 0; p < n_packets; p++) {\n                    // Find closest grid point to received packet at current\n                    // time, using extents for live points only.\n                    Packet& packet = received_data_packets.value()[p];\n                    Index<Dim> closest_point_index_3d;\n                    for (size_t d = 0; d < Dim; d++) {\n                      closest_point_index_3d[d] =\n                          std::floor((packet.coordinates[d] + 1.0) *\n                                     static_cast<double>(extents[d]) / 2.0);\n                      if (UNLIKELY(closest_point_index_3d[d] == extents[d])) {\n                        closest_point_index_3d[d]--;\n                      }\n                    }\n                    packet.index_of_closest_grid_point =\n                        collapsed_index(closest_point_index_3d, extents);\n                    // Now add packets to list in current element\n                    packet_list->push_back(received_data_packets.value()[p]);\n                  }\n                }\n              }\n            }\n          },\n          make_not_null(&box));\n      // Combine ghost zone data with live points data. Currently only done in\n      // 3D\n      if constexpr (Dim == 3) {\n        db::mutate_apply(CombineCouplingDataPostStep<Dim>{},\n                         make_not_null(&box));\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Particles::MonteCarlo::Actions\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/GhostZoneCommunicationStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Items related to the evolution of particles\n/// Items related to Monte-Carlo radiation transport\nnamespace Particles::MonteCarlo {\n\n/// This class is used to template communication actions\n/// in the Monte-Carlo code, to know which of the two\n/// communication steps we need to take care of\n/// (just before or just after the MC step).\nenum class CommunicationStep {\n  /// PreStep should occur just before the MC step.\n  /// It should send to the ghost zones of each\n  /// element the fluid and metric data.\n  PreStep,\n  /// PostStep should occur just after a MC step.\n  /// It sends packets that have left their current\n  /// element to the element they now belong to.\n  /// NOT CODE YET: This is also when information\n  /// about energy/momentum/lepton number exchance\n  /// in the ghost zones should be communicated back\n  /// to the corresponding live point for coupling to\n  /// the fluid evolution.\n  PostStep\n};\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/GhostZoneCommunicationTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <map>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunicationStep.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\n/// Items related to the evolution of particles\n/// Items related to Monte-Carlo radiation transport\nnamespace Particles::MonteCarlo {\n\n/*!\n * \\brief The container for ghost zone data to be communicated\n *        in the Monte-Carlo algorithm\n *\n * The stored data consists of\n * (1) The fluid variables in those ghost cells, in the form\n *     of a DataVector. The data should contain the\n *     scalars (baryon density, temperature, electron fraction,\n *     and cell light-crossing time) in pre-step communication\n *     and (coupling_tilde_tau, coupling_tilde_s, coupling_rho_ye)\n *     in post-step communication\n * (2) The packets moved from a neighbor to this element. This\n *     can be std::null_ptr in pre-step communication\n */\ntemplate <size_t Dim>\nstruct McGhostZoneData {\n  void pup(PUP::er& p) {\n    p | ghost_zone_hydro_variables;\n    p | packets_entering_this_element;\n  }\n\n  DataVector ghost_zone_hydro_variables{};\n  std::optional<std::vector<Particles::MonteCarlo::Packet>>\n      packets_entering_this_element{};\n};\n\n/// Inbox tag to be used before MC step\ntemplate <size_t Dim, CommunicationStep CommStep>\nstruct McGhostZoneDataInboxTag {\n  using stored_type = McGhostZoneData<Dim>;\n\n  using temporal_id = TimeStepId;\n  using type = std::map<TimeStepId, DirectionalIdMap<Dim, stored_type>>;\n  using value_type = type;\n\n  CommunicationStep comm_step = CommStep;\n\n  template <typename ReceiveDataType>\n  static bool insert_into_inbox(const gsl::not_null<type*> inbox,\n                                const temporal_id& time_step_id,\n                                ReceiveDataType&& data) {\n    auto& current_inbox = (*inbox)[time_step_id];\n    if (not current_inbox.insert(std::forward<ReceiveDataType>(data))\n                  .second) {\n     ERROR(\"Failed to insert data to receive at instance '\"\n           << time_step_id\n           << \" in McGhostZonePreStepDataInboxTag.\\n\");\n    }\n    return true;\n  }\n\n  void pup(PUP::er& /*p*/) {}\n};\n\n// Tags to put in DataBox for MC communication.\nnamespace Tags {\n\n/// Simple tag for the structure containing ghost zone data\n/// for Monte Carlo radiation transport.\ntemplate <size_t Dim>\nstruct McGhostZoneDataTag : db::SimpleTag {\n  using type = DirectionalIdMap<Dim, McGhostZoneData<Dim>>;\n};\n\n}  // namespace Tags\n\n} // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/ImplicitMonteCarloCorrections.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n\n#include <array>\n\n#include \"Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n\nusing hydro::units::nuclear::proton_mass;\n\nnamespace Particles::MonteCarlo {\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nvoid TemplatedLocalFunctions<EnergyBins, NeutrinoSpecies>::\n    implicit_monte_carlo_interaction_rates(\n        gsl::not_null<\n            std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n            emissivity_in_cell,\n        gsl::not_null<\n            std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n            absorption_opacity,\n        gsl::not_null<\n            std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n            scattering_opacity,\n        gsl::not_null<\n            std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n            fraction_ka_to_ks,\n        const Scalar<DataVector>& cell_light_crossing_time,\n        const Scalar<DataVector>& electron_fraction,\n        const Scalar<DataVector>& rest_mass_density,\n        const Scalar<DataVector>& temperature,\n        const double minimum_temperature,\n        const NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>&\n            interaction_table,\n        const EquationsOfState::EquationOfState<true, 3>& equation_of_state) {\n  // Initial guess for interaction rates: no implicit MC corrections\n  interaction_table.get_neutrino_matter_interactions(\n      emissivity_in_cell, absorption_opacity, scattering_opacity,\n      electron_fraction, rest_mass_density, temperature,\n      minimum_temperature);\n\n  const std::array<double, EnergyBins>& neutrino_energies =\n      interaction_table.get_neutrino_energies();\n\n  // Apply implicit MC corrections as needed\n  const size_t dv_size = get(rest_mass_density).size();\n\n  // Calculate beta parameter (relative change of MC vs fluid variables)\n  // For photon transport,\n  // beta = d(radiation_energy_density)/d(fluid_energy_density)\n  // at constant rest mass density, which is well defined. For\n  // neutrinos, because we can vary both temperatures and electron\n  // fraction, it is not as clear what should be done. Here, we\n  // calculate both\n  // d(radiation_energy_density)/d(fluid_energy_density)\n  // at constant Ye, and\n  // d(neutrino_lepton_number_density)/d(proton_number_density)\n  // at constant T as a measure of how 'impactful' neutrino\n  // emission/absorption can be on the fluid.\n  Scalar<DataVector> beta =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  Scalar<DataVector> fluid_energy_0 =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  Scalar<DataVector> fluid_lepton_number_0 =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  Scalar<DataVector> neutrino_energy_0 =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  Scalar<DataVector> neutrino_lepton_number_0 =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  // Fluid densities at initial T,Ye\n  Scalar<DataVector> specific_internal_energy =\n      equation_of_state.specific_internal_energy_from_density_and_temperature(\n          rest_mass_density, temperature, electron_fraction);\n  for (size_t i = 0; i < dv_size; i++) {\n    get(fluid_energy_0)[i] =\n        get(rest_mass_density)[i] * get(specific_internal_energy)[i];\n    get(fluid_lepton_number_0)[i] =\n        get(rest_mass_density)[i] * get(electron_fraction)[i] / proton_mass;\n    // Radiation densities at initial T,Ye\n    for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n      for (size_t ng = 0; ng < EnergyBins; ng++) {\n        get(neutrino_energy_0)[i] +=\n            gsl::at(gsl::at(*emissivity_in_cell, ns), ng)[i] /\n            gsl::at(gsl::at(*absorption_opacity, ns), ng)[i];\n        if (ns < 2) {\n          get(neutrino_lepton_number_0)[i] +=\n              (ns == 0 ? 1.0 : -1.0) *\n              gsl::at(gsl::at(*emissivity_in_cell, ns), ng)[i] /\n              gsl::at(gsl::at(*absorption_opacity, ns), ng)[i] /\n              neutrino_energies[ns];\n        }\n      }\n    }\n  }\n  // Calculate energy as we vary temperature\n  const double TEMP_EPS = 1.e-6;\n  Scalar<DataVector> temperature_1 = temperature;\n  get(temperature_1) += TEMP_EPS;\n  Scalar<DataVector> fluid_energy_1 =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  Scalar<DataVector> neutrino_energy_1 =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  // Neutrino interaction rates at T+eps\n  interaction_table.get_neutrino_matter_interactions(\n      emissivity_in_cell, absorption_opacity, scattering_opacity,\n      electron_fraction, rest_mass_density, temperature_1,\n      minimum_temperature);\n  specific_internal_energy =\n      equation_of_state.specific_internal_energy_from_density_and_temperature(\n          rest_mass_density, temperature_1, electron_fraction);\n  for (size_t i = 0; i < dv_size; i++) {\n    get(fluid_energy_1)[i] =\n        get(rest_mass_density)[i] * get(specific_internal_energy)[i];\n    for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n      for (size_t ng = 0; ng < EnergyBins; ng++) {\n        get(neutrino_energy_1)[i] +=\n            gsl::at(gsl::at(*emissivity_in_cell, ns), ng)[i] /\n            gsl::at(gsl::at(*absorption_opacity, ns), ng)[i];\n      }\n    }\n  }\n  // Calculate lepton number as we vary electron fraction\n  Scalar<DataVector> fluid_lepton_number_1 =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  Scalar<DataVector> neutrino_lepton_number_1 =\n      make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  const double YE_EPS = 1.e-8;\n  Scalar<DataVector> electron_fraction_1 = electron_fraction;\n  get(electron_fraction_1) += YE_EPS;\n  // Neutrino interaction rates at Ye+eps\n  interaction_table.get_neutrino_matter_interactions(\n      emissivity_in_cell, absorption_opacity, scattering_opacity,\n      electron_fraction_1, rest_mass_density, temperature,\n      minimum_temperature);\n  for (size_t i = 0; i < dv_size; i++) {\n    get(fluid_lepton_number_1)[i] =\n        get(rest_mass_density)[i] * get(electron_fraction_1)[i] / proton_mass;\n    // Radiation densities at initial T,Ye\n    for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n      for (size_t ng = 0; ng < EnergyBins; ng++) {\n        if (ns < 2) {\n          get(neutrino_lepton_number_1)[i] +=\n              (ns == 0 ? 1.0 : -1.0) *\n              gsl::at(gsl::at(*emissivity_in_cell, ns), ng)[i] /\n              gsl::at(gsl::at(*absorption_opacity, ns), ng)[i] /\n              neutrino_energies[ns];\n        }\n      }\n    }\n    // We have all we need to calculate beta here. We take the largest of the\n    // two variations\n    get(beta)[i] = fabs(get(neutrino_energy_1)[i] - get(neutrino_energy_0)[i]) /\n                   (fabs(get(fluid_energy_1)[i] - get(fluid_energy_0)[i]) +\n                    1.e-16);\n    const double beta_lepton =\n        fabs(get(neutrino_lepton_number_1)[i] -\n             get(neutrino_lepton_number_0)[i]) /\n        (fabs(get(fluid_lepton_number_1)[i] - get(fluid_lepton_number_0)[i]) +\n         1.e-16);\n    get(beta)[i] =\n        std::max(get(beta)[i], beta_lepton);\n  }\n\n  // Correct interaction rates, group by group\n  interaction_table.get_neutrino_matter_interactions(\n      emissivity_in_cell, absorption_opacity, scattering_opacity,\n      electron_fraction, rest_mass_density, temperature,\n      minimum_temperature);\n  for (size_t i = 0; i < dv_size; i++) {\n    const double max_opacity_for_implicit_mc =\n      std::min(100.0 ,1.0/get(cell_light_crossing_time)[i]);\n    for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n      for (size_t ng = 0; ng < EnergyBins; ng++) {\n        const double ka = gsl::at(gsl::at(*absorption_opacity, ns), ng)[i];\n        // Fraction of the absorption opacity to be moved to\n        // scattering opacity.\n        double frac_ka_to_ks = (ka > max_opacity_for_implicit_mc)\n                                   ? 1.0 - max_opacity_for_implicit_mc / ka\n                                   : 0.0;\n        const double frac_from_beta =\n            1.0 - 1.0 / ka / get(cell_light_crossing_time)[i] /\n              (1.0 + get(beta)[i]);\n        frac_ka_to_ks = std::max(frac_ka_to_ks, frac_from_beta);\n        gsl::at(gsl::at(*scattering_opacity, ns), ng)[i] +=\n          frac_ka_to_ks * ka;\n        gsl::at(gsl::at(*absorption_opacity, ns), ng)[i] *=\n            (1.0 - frac_ka_to_ks);\n        gsl::at(gsl::at(*emissivity_in_cell, ns), ng)[i] *=\n            (1.0 - frac_ka_to_ks);\n        gsl::at(gsl::at(*fraction_ka_to_ks, ns), ng)[i] =\n          frac_ka_to_ks;\n      }\n    }\n  }\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/InverseJacobianInertialToFluidCompute.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/InverseJacobianInertialToFluidCompute.hpp\"\n\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace Particles::MonteCarlo {\n\nvoid InverseJacobianInertialToFluidCompute::function(\n    gsl::not_null<return_type*> inv_jacobian,\n    const tnsr::I<DataVector, 3>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::ii<DataVector, 3>& spatial_metric) {\n  // The components of the inverse jacobian are just the vector components\n  // of the orthonormal tetrad comoving with the fluid in the inertial frame.\n\n  // First, inv_jacobian(a,0) is just u^a\n  inv_jacobian->get(0, 0) = get(lorentz_factor) / get(lapse);\n  for (size_t d = 0; d < 3; d++) {\n    inv_jacobian->get(d + 1, 0) =\n        get(lorentz_factor) *\n        (spatial_velocity.get(d) - shift.get(d) / get(lapse));\n  }\n\n  // Then, the other members of the tetrad are constructed using Gram-Schmidt\n\n  // Temporary memory allocation\n  auto temp_dot_product = make_with_value<DataVector>(lapse, 0.0);\n  tnsr::aa<DataVector, 3, Frame::Inertial> spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n\n  // d = 1,2,3 for tetrad components built from x,y,z\n  for (size_t d = 1; d < 4; d++) {\n    // Base vector for Gram-Shmidt\n    for (size_t i = 0; i < 4; i++) {\n      set_number_of_grid_points(make_not_null(&inv_jacobian->get(i, d)), lapse);\n      inv_jacobian->get(i, d) = (i==d ? 1.0 : 0.0);\n    }\n    // Projection orthogonal to u^\\mu\n    temp_dot_product = 0.0;\n    for (size_t i = 0; i < 3; i++) {\n      temp_dot_product +=\n          spatial_metric.get(d - 1, i) * spatial_velocity.get(i);\n    }\n    temp_dot_product *= get(lorentz_factor);\n    // Note: + sign when projecting here, because the 0th tetrad vector has norm\n    // -1.\n    for (size_t i = 0; i < 4; i++) {\n      inv_jacobian->get(i, d) += temp_dot_product * inv_jacobian->get(i, 0);\n    }\n\n    // Loop over other existing tetrad vectors to get orthogonal projection\n    for (size_t a = 0; a < d; a++) {\n      temp_dot_product = 0.0;\n      for (size_t b = 0; b < 4; b++) {\n        for (size_t c = 0; c < 4; c++) {\n          temp_dot_product += spacetime_metric.get(b, c) *\n                              inv_jacobian->get(b, d) * inv_jacobian->get(c, a);\n        }\n      }\n      for (size_t i = 0; i < 4; i++) {\n        inv_jacobian->get(i, d) -= temp_dot_product * inv_jacobian->get(i, a);\n      }\n    }\n\n    // Normalize tetrad vector\n    temp_dot_product = 0.0;\n    for (size_t a = 0; a < 4; a++) {\n      temp_dot_product += spacetime_metric.get(a, a) * inv_jacobian->get(a, d) *\n                          inv_jacobian->get(a, d);\n      for (size_t b = a + 1; b < 4; b++) {\n        temp_dot_product += 2.0 * spacetime_metric.get(a, b) *\n                            inv_jacobian->get(a, d) * inv_jacobian->get(b, d);\n      }\n    }\n    temp_dot_product = sqrt(temp_dot_product);\n    for (size_t a = 0; a < 4; a++) {\n      inv_jacobian->get(a, d) = inv_jacobian->get(a, d) / temp_dot_product;\n    }\n  }\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/InverseJacobianInertialToFluidCompute.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines tags related to domain quantities\n\n#pragma once\n\n#include \"Domain/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace Frame {\nstruct Fluid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace Particles::MonteCarlo {\n\n/// Inverse Jacobian of the map from inertial coordinate to an orthonormal frame\n/// comoving with the fluid. That frame uses the 4-velocity as its time axis,\n/// and constructs the other members of the tetrads using Gram-Schmidt's\n/// algorithm.\nstruct InverseJacobianInertialToFluidCompute\n    : domain::Tags::InverseJacobian<4, Frame::Inertial, Frame::Fluid>,\n      db::ComputeTag {\n  using base = domain::Tags::InverseJacobian<4, typename Frame::Inertial,\n                                             typename Frame::Fluid>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, 3, Frame::Inertial>,\n                 gr::Tags::SpatialMetric<DataVector, 3> >;\n\n  static void function(gsl::not_null<return_type*> inv_jacobian,\n                       const tnsr::I<DataVector, 3>& spatial_velocity,\n                       const Scalar<DataVector>& lorentz_factor,\n                       const Scalar<DataVector>& lapse,\n                       const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n                       const tnsr::ii<DataVector, 3>& spatial_metric);\n};\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/MonteCarloOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/MonteCarloOptions.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n\nnamespace Particles::MonteCarlo {\n\ntemplate <size_t NeutrinoSpecies>\nPUP::able::PUP_ID MonteCarloOptions<NeutrinoSpecies>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <size_t NeutrinoSpecies>\nvoid MonteCarloOptions<NeutrinoSpecies>::pup(PUP::er& p) {\n  PUP::able::pup(p);\n  p | initial_packet_energy_;\n}\n\n}  // namespace Particles::MonteCarlo\n\ntemplate class Particles::MonteCarlo::MonteCarloOptions<2>;\ntemplate class Particles::MonteCarlo::MonteCarloOptions<3>;\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/MonteCarloOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <pup.h>\n\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Particles::MonteCarlo {\n\ntemplate <size_t NeutrinoSpecies>\nclass MonteCarloOptions : public PUP::able {\n public:\n  explicit MonteCarloOptions(\n      const std::array<double, NeutrinoSpecies> initial_packet_energy)\n      : initial_packet_energy_(initial_packet_energy) {}\n\n  static constexpr Options::String help = {\n      \"Global options for Monte-Carlo evolution.\\n\"\n      \"InitialPacketEnergy: [double, double, double]    \\n\"};\n\n  struct InitialPacketEnergy {\n    using type = std::array<double, NeutrinoSpecies>;\n    static constexpr Options::String help{\n        \"Initial energy used to create packets\"};\n  };\n\n  using options = tmpl::list<InitialPacketEnergy>;\n\n  explicit MonteCarloOptions(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  using PUP::able::register_constructor;\n  void pup(PUP::er& p) override;\n  WRAPPED_PUPable_decl_template(MonteCarloOptions);\n\n  const std::array<double, NeutrinoSpecies>& get_initial_packet_energy() const {\n    return initial_packet_energy_;\n  }\n\n private:\n  std::array<double, NeutrinoSpecies> initial_packet_energy_;\n};\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/MortarData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Particles::MonteCarlo {\n\n/// Structure used to gather ghost zone data for Monte-Carlo evolution.\n/// We need the rest mass density, electron fraction, temperature, and\n/// an estimate of the light-crossing time one cell deep within each\n/// neighboring element.\ntemplate <size_t Dim>\nstruct MortarData {\n  DirectionalIdMap<Dim, std::optional<DataVector>> rest_mass_density{};\n  DirectionalIdMap<Dim, std::optional<DataVector>> electron_fraction{};\n  DirectionalIdMap<Dim, std::optional<DataVector>> temperature{};\n  DirectionalIdMap<Dim, std::optional<DataVector>> cell_light_crossing_time{};\n\n  void pup(PUP::er& p) {\n    p | rest_mass_density;\n    p | electron_fraction;\n    p | temperature;\n    p | cell_light_crossing_time;\n  }\n};\n\n/// Structure used to gather fluid coupling data for Monte-Carlo evolution.\n/// We need the energy, momentum, and composition coupling\ntemplate <size_t Dim>\nstruct GhostZoneCouplingData {\n  DirectionalIdMap<Dim, std::optional<DataVector>> coupling_tilde_tau{};\n  DirectionalIdMap<Dim, std::optional<DataVector>> coupling_tilde_rho_ye{};\n  DirectionalIdMap<Dim,\n                   std::optional<tnsr::i<DataVector, Dim, Frame::Inertial>>>\n      coupling_tilde_s{};\n\n  void pup(PUP::er& p) {\n    p | coupling_tilde_tau;\n    p | coupling_tilde_rho_ye;\n    p | coupling_tilde_s;\n  }\n};\n\nnamespace Tags {\n\n/// Simple tag containing the fluid and metric data in the ghost zones\n/// for Monte-Carlo packets evolution.\ntemplate <size_t Dim>\nstruct MortarDataTag : db::SimpleTag {\n  using type = MortarData<Dim>;\n};\n\n/// Simple tag containing the coupling data in the ghost zones\n/// for Monte-Carlo packets evolution.\ntemplate <size_t Dim>\nstruct GhostZoneCouplingDataTag : db::SimpleTag {\n  using type = GhostZoneCouplingData<Dim>;\n};\n\n}  // namespace Tags\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <hdf5.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nusing hydro::units::cgs::length_unit;\nusing hydro::units::cgs::mass_unit;\nusing hydro::units::cgs::rest_mass_density_unit;\nusing hydro::units::cgs::time_unit;\n\nnamespace Particles::MonteCarlo {\n\nnamespace {\nconst double emissivity_NuLib_to_code =\n    length_unit * cube(time_unit) / mass_unit * 4.0 * M_PI;\n}  // namespace\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nNeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>::NeutrinoInteractionTable(\n    std::vector<double> table_data_,\n    const std::array<double, EnergyBins>& table_neutrino_energies_,\n    std::vector<double> table_log_density_,\n    std::vector<double> table_log_temperature_,\n    std::vector<double> table_electron_fraction_)\n    : table_data(std::move(table_data_)),\n      table_neutrino_energies(table_neutrino_energies_),\n      table_log_density(std::move(table_log_density_)),\n      table_log_temperature(std::move(table_log_temperature_)),\n      table_electron_fraction(std::move(table_electron_fraction_)) {\n  initialize_interpolator();\n}\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nNeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>::NeutrinoInteractionTable(\n    const std::string& filename) {\n  const hid_t file_id =\n      H5Fopen(filename.c_str(), H5F_ACC_RDONLY, h5::h5p_default());\n\n  const size_t n_points_density = static_cast<size_t>(\n      h5::read_data<1, std::vector<int> >(file_id, \"nrho\")[0]);\n  const size_t n_points_temperature = static_cast<size_t>(\n      h5::read_data<1, std::vector<int> >(file_id, \"ntemp\")[0]);\n  const size_t n_points_electron_fraction = static_cast<size_t>(\n      h5::read_data<1, std::vector<int> >(file_id, \"nye\")[0]);\n  const size_t n_energy_groups = static_cast<size_t>(\n      h5::read_data<1, std::vector<int> >(file_id, \"number_groups\")[0]);\n  const size_t n_neutrino_species = static_cast<size_t>(\n      h5::read_data<1, std::vector<int> >(file_id, \"number_species\")[0]);\n\n  if (n_energy_groups != EnergyBins) {\n    ERROR(\"Table has \" << n_energy_groups\n                       << \" energy bins, while the executable assumed \"\n                       << EnergyBins);\n  }\n  if (n_neutrino_species != NeutrinoSpecies) {\n    ERROR(\"Table has \" << n_neutrino_species\n                       << \" neutrino species, while the executable assumed \"\n                       << NeutrinoSpecies);\n  }\n\n  table_log_density =\n      h5::read_data<1, std::vector<double> >(file_id, \"rho_points\");\n  if (table_log_density.size() != n_points_density) {\n    ERROR(\"Table has inconsistent number of points in density. Expected \"\n          << n_points_density << \" but got \" << table_log_density.size());\n  }\n  for (size_t i = 0; i < n_points_density; i++) {\n    table_log_density[i] = log(table_log_density[i] / rest_mass_density_unit);\n  }\n\n  table_log_temperature =\n      h5::read_data<1, std::vector<double> >(file_id, \"temp_points\");\n  if (table_log_temperature.size() != n_points_temperature) {\n    ERROR(\"Table has inconsistent number of points in temperature. Expected \"\n          << n_points_temperature << \" but got \"\n          << table_log_temperature.size());\n  }\n  for (size_t i = 0; i < n_points_temperature; i++) {\n    table_log_temperature[i] = log(table_log_temperature[i]);\n  }\n\n  table_electron_fraction =\n      h5::read_data<1, std::vector<double> >(file_id, \"ye_points\");\n  if (table_electron_fraction.size() != n_points_electron_fraction) {\n    ERROR(\n        \"Table has inconsistent number of points in electron fraction. \"\n        \"Expected \"\n        << n_points_electron_fraction << \" but got \"\n        << table_electron_fraction.size());\n  }\n\n  const std::vector<double> energy_bin_lower_bound =\n      h5::read_data<1, std::vector<double> >(file_id, \"bin_bottom\");\n  const std::vector<double> energy_bin_upper_bound =\n      h5::read_data<1, std::vector<double> >(file_id, \"bin_top\");\n  if ((energy_bin_lower_bound.size() != EnergyBins) or\n      (energy_bin_upper_bound.size() != EnergyBins)) {\n    ERROR(\"Table has inconsistent number of points in energy. Expected\"\n          << EnergyBins << \" but got \" << energy_bin_lower_bound.size()\n          << \" (lower bound) and \" << energy_bin_upper_bound.size()\n          << \" (upper bound).\");\n  }\n  for (size_t i = 0; i < EnergyBins; i++) {\n    gsl::at(table_neutrino_energies, i) =\n        0.5 * (energy_bin_lower_bound[i] + energy_bin_upper_bound[i]);\n  }\n\n  // 5-dimensional array, with dimensions\n  // (NeutrinoSpecies, EnergyBins, n_points_electron_fraction,\n  //  n_points_temperature, n_points_density)\n  boost::multi_array<double, 5> buffer =\n      h5::read_data<5, boost::multi_array<double, 5> >(file_id, \"emissivities\");\n\n  const size_t n_pts_per_var =\n      n_points_density * n_points_temperature * n_points_electron_fraction;\n  const size_t number_of_vars = 3 * NeutrinoSpecies * EnergyBins;\n  table_data.resize(number_of_vars * n_pts_per_var);\n  size_t idx = 0;\n  size_t idx_emissivity = 0;\n  for (size_t ng = 0; ng < EnergyBins; ng++) {\n    for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n      for (size_t ny = 0; ny < n_points_electron_fraction; ny++) {\n        for (size_t nt = 0; nt < n_points_temperature; nt++) {\n          for (size_t nr = 0; nr < n_points_density; nr++) {\n            idx = (ns * EnergyBins + ng) +\n                  (ny * n_points_temperature * n_points_density +\n                   nt * n_points_density + nr) *\n                      number_of_vars;\n            table_data[idx] =\n                buffer[ng][ns][ny][nt][nr] * emissivity_NuLib_to_code *\n                (energy_bin_upper_bound[ng] - energy_bin_lower_bound[ng]);\n          }\n        }\n      }\n    }\n  }\n\n  buffer = h5::read_data<5, boost::multi_array<double, 5> >(\n      file_id, \"absorption_opacity\");\n  for (size_t ng = 0; ng < EnergyBins; ng++) {\n    for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n      for (size_t ny = 0; ny < n_points_electron_fraction; ny++) {\n        for (size_t nt = 0; nt < n_points_temperature; nt++) {\n          for (size_t nr = 0; nr < n_points_density; nr++) {\n            idx_emissivity = (ns * EnergyBins + ng) +\n                             (ny * n_points_temperature * n_points_density +\n                              nt * n_points_density + nr) *\n                                 number_of_vars;\n            idx = (NeutrinoSpecies * EnergyBins) + idx_emissivity;\n            table_data[idx] = buffer[ng][ns][ny][nt][nr] * length_unit;\n            if (table_data[idx] < min_kappa) {\n              table_data[idx_emissivity] = 0.0;\n              table_data[idx] = min_kappa;\n            }\n            if (table_data[idx] > max_kappa) {\n              table_data[idx_emissivity] *= max_kappa / table_data[idx];\n              table_data[idx] = max_kappa;\n            }\n          }\n        }\n      }\n    }\n  }\n\n  buffer = h5::read_data<5, boost::multi_array<double, 5> >(\n      file_id, \"scattering_opacity\");\n  for (size_t ng = 0; ng < EnergyBins; ng++) {\n    for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n      for (size_t ny = 0; ny < n_points_electron_fraction; ny++) {\n        for (size_t nt = 0; nt < n_points_temperature; nt++) {\n          for (size_t nr = 0; nr < n_points_density; nr++) {\n            idx_emissivity = (ns * EnergyBins + ng) +\n                             (ny * n_points_temperature * n_points_density +\n                              nt * n_points_density + nr) *\n                                 number_of_vars;\n            idx = 2 * (NeutrinoSpecies * EnergyBins) + idx_emissivity;\n            table_data[idx] = buffer[ng][ns][ny][nt][nr] * length_unit;\n            table_data[idx] =\n              std::clamp(table_data[idx], min_kappa, max_kappa);\n          }\n        }\n      }\n    }\n  }\n\n  // Correction to emissivity/absorption/scattering at end of table to keep\n  // Ye within the neutrino table bounds (in this regime, we cannot evolve Ye\n  // according to the true tabulated value; we correct the tabulated\n  // values in the hope of at least getting lepton number conservation).\n  // Note that this could be insufficient if the EoS table bounds are\n  // more restrictive than the neutrino table bounds; but this table\n  // does not know the EoS bounds. Ideally, the neutrino and EoS tables\n  // would have the same bounds.\n  if (NeutrinoSpecies >= 2) {\n    // Correction at bounds of table in electron fraction\n    // assume that we have at least electron neutrinos and\n    // antineutrinos.\n    // We correct the emissivity at both bounds, and the\n    // absorption only when absorption would drive the\n    // electron fraction outside of the table bounds.\n    // Note that absorbing an electron neutrino (ns==0)\n    // increases the electron fraction.\n    for (size_t ng = 0; ng < EnergyBins; ng++) {\n      for (size_t nt = 0; nt < n_points_temperature; nt++) {\n        for (size_t nr = 0; nr < n_points_density; nr++) {\n          idx_emissivity = ng + (nt * n_points_density + nr) * number_of_vars;\n          idx = (NeutrinoSpecies * EnergyBins) + idx_emissivity;\n          // nu_e\n          table_data[idx_emissivity] = 0.0;\n          // nu_a\n          table_data[idx_emissivity + EnergyBins] = 0.0;\n          table_data[idx + EnergyBins] = min_kappa;\n\n          idx += ((n_points_electron_fraction - 1) * n_points_temperature *\n                  n_points_density) *\n                 number_of_vars;\n          idx_emissivity += ((n_points_electron_fraction - 1) *\n                             n_points_temperature * n_points_density) *\n                            number_of_vars;\n          // nu_e\n          table_data[idx_emissivity] = 0.0;\n          table_data[idx] = min_kappa;\n          // nu_a\n          table_data[idx_emissivity + EnergyBins] = 0.0;\n        }\n      }\n    }\n  }\n  initialize_interpolator();\n}\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nvoid NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>::pup(PUP::er& p) {\n  PUP::able::pup(p);\n  p | table_data;\n  p | table_neutrino_energies;\n  p | table_log_density;\n  p | table_log_temperature;\n  p | table_electron_fraction;\n  if (p.isUnpacking()) {\n    initialize_interpolator();\n  }\n}\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nPUP::able::PUP_ID NeutrinoInteractionTable<EnergyBins,\n  NeutrinoSpecies>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nvoid NeutrinoInteractionTable<EnergyBins,\n                              NeutrinoSpecies>::initialize_interpolator() {\n  // Initialize interpolator\n  Index<3> num_x_points;\n  // The order is rho, T, Ye\n  num_x_points[0] = table_log_density.size();\n  num_x_points[1] = table_log_temperature.size();\n  num_x_points[2] = table_electron_fraction.size();\n  std::array<gsl::span<double const>, 3> independent_data_view;\n  independent_data_view[0] =\n      gsl::span<double const>{table_log_density.data(), num_x_points[0]};\n  independent_data_view[1] =\n      gsl::span<double const>{table_log_temperature.data(), num_x_points[1]};\n  independent_data_view[2] =\n      gsl::span<double const>{table_electron_fraction.data(), num_x_points[2]};\n  interpolator_ =\n      intrp::UniformMultiLinearSpanInterpolation<3, 3 * EnergyBins *\n                                                        NeutrinoSpecies>(\n          independent_data_view, {table_data.data(), table_data.size()},\n          num_x_points);\n}\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nvoid NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>::\n    get_neutrino_matter_interactions(\n        const gsl::not_null<\n            std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n            emissivity_in_cell,\n        const gsl::not_null<\n            std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n            absorption_opacity,\n        const gsl::not_null<\n            std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n            scattering_opacity,\n        const Scalar<DataVector>& electron_fraction,\n        const Scalar<DataVector>& rest_mass_density,\n        const Scalar<DataVector>& temperature,\n        const double& minimum_temperature) const {\n  const size_t n_rho_points = table_log_density.size();\n  const size_t n_temp_points = table_log_temperature.size();\n  const size_t n_ye_points = table_electron_fraction.size();\n  double temperature_correction_factor = 0.0;\n\n  const double dye = table_electron_fraction[1] - table_electron_fraction[0];\n  double ye = 0.0;\n  double log_rho = 0.0;\n  double log_temp = 0.0;\n  const double minimum_density = exp(table_log_density[0]);\n  for (size_t p = 0; p < get(electron_fraction).size(); p++) {\n    ye = get(electron_fraction)[p];\n    log_rho = get(rest_mass_density)[p] > minimum_density\n                  ? log(get(rest_mass_density)[p])\n                  : log(minimum_density);\n    log_temp = get(temperature)[p] > minimum_temperature\n                   ? log(get(temperature)[p])\n                   : log(minimum_temperature);\n    temperature_correction_factor =\n        get(temperature)[p] > minimum_temperature\n            ? 1.0\n            : get(temperature)[p] / minimum_temperature;\n\n    ye = std::clamp(ye, table_electron_fraction[0] + 1.e-8 * dye,\n                    table_electron_fraction[n_ye_points - 1] - 1.e-8 * dye);\n    log_rho = std::clamp(log_rho, table_log_density[0],\n                         table_log_density[n_rho_points - 1]);\n    log_temp = std::clamp(log_temp, table_log_temperature[0],\n                          table_log_temperature[n_temp_points - 1]);\n\n    const auto weights = interpolator_.get_weights(log_rho, log_temp, ye);\n\n    for (size_t ng = 0; ng < EnergyBins; ng++) {\n      for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n        gsl::at(gsl::at(*emissivity_in_cell, ns), ng)[p] =\n            interpolator_.interpolate(weights, ns * EnergyBins + ng);\n        gsl::at(gsl::at(*absorption_opacity, ns), ng)[p] =\n            interpolator_.interpolate(\n                weights, ns * EnergyBins + ng + EnergyBins * NeutrinoSpecies);\n        gsl::at(gsl::at(*scattering_opacity, ns), ng)[p] =\n            interpolator_.interpolate(\n                weights,\n                ns * EnergyBins + ng + EnergyBins * NeutrinoSpecies * 2);\n      }\n    }\n    // Apply corrections for low-temperature points.\n    for (size_t ng = 0; ng < EnergyBins; ng++) {\n      for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n        gsl::at(gsl::at(*emissivity_in_cell, ns), ng)[p] *=\n            square(cube(temperature_correction_factor));\n        gsl::at(gsl::at(*absorption_opacity, ns), ng)[p] *=\n            square(square(temperature_correction_factor));\n        gsl::at(gsl::at(*scattering_opacity, ns), ng)[p] *=\n            square(square(temperature_correction_factor));\n        gsl::at(gsl::at(*absorption_opacity, ns), ng)[p] =\n            std::clamp(gsl::at(gsl::at(*absorption_opacity, ns), ng)[p],\n                       min_kappa, max_kappa);\n        gsl::at(gsl::at(*scattering_opacity, ns), ng)[p] =\n            std::clamp(gsl::at(gsl::at(*scattering_opacity, ns), ng)[p],\n                       min_kappa, max_kappa);\n      }\n    }\n  }\n}\n\n}  // namespace Particles::MonteCarlo\n\ntemplate class Particles::MonteCarlo::NeutrinoInteractionTable<2, 2>;\ntemplate class Particles::MonteCarlo::NeutrinoInteractionTable<2, 3>;\ntemplate class Particles::MonteCarlo::NeutrinoInteractionTable<4, 3>;\ntemplate class Particles::MonteCarlo::NeutrinoInteractionTable<16, 3>;\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/Interpolation/MultiLinearSpanInterpolation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Particles::MonteCarlo {\n\n/// Class responsible for reading neutrino-matter interaction\n/// tables.\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nclass NeutrinoInteractionTable : public PUP::able {\n public:\n  /// Read table from disk and stores interaction rates.\n  explicit NeutrinoInteractionTable(const std::string& filename);\n\n  static constexpr Options::String help = {\n      \"Tabulated neutrino-matter interactions in NuLib format.\\n\"\n      \"Emissivity, absorption and scattering opacities are    \\n\"\n      \"tabulated as a function of density, eletron fraction   \\n\"\n      \"and temperature.\"};\n\n  struct TableFilename {\n    using type = std::string;\n    static constexpr Options::String help{\"File name of the NuLib table\"};\n  };\n\n  using options = tmpl::list<TableFilename>;\n\n  /// Explicit instantiation from table values, for tests\n  NeutrinoInteractionTable(\n      std::vector<double> table_data_,\n      const std::array<double, EnergyBins>& table_neutrino_energies_,\n      std::vector<double> table_log_density_,\n      std::vector<double> table_log_temperature_,\n      std::vector<double> table_electron_fraction_);\n\n  explicit NeutrinoInteractionTable(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  using PUP::able::register_constructor;\n  void pup(PUP::er& p) override;\n  WRAPPED_PUPable_decl_template(NeutrinoInteractionTable);\n\n  /// Interpolate interaction rates to given values of density,\n  /// temperature and electron fraction.\n  void get_neutrino_matter_interactions(\n      gsl::not_null<\n          std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n          emissivity_in_cell,\n      gsl::not_null<\n          std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n          absorption_opacity,\n      gsl::not_null<\n          std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n          scattering_opacity,\n      const Scalar<DataVector>& electron_fraction,\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& temperature,\n      const double& minimum_temperature) const;\n\n  const std::array<double, EnergyBins>& get_neutrino_energies() const {\n    return table_neutrino_energies;\n  }\n\n private:\n  void initialize_interpolator();\n\n  // Stores emissivity, absorption_opacity, scattering_opacity\n  // For each quantities, there are NeutrinoSpecies * EnergyBins\n  // variables store as a function of log(density), log(temperature)\n  // and electron fraction.\n  // The indexing varies fastest in EnergyBins, then Species, then\n  // log(density), then log(temperature), and finally Ye.\n  std::vector<double> table_data{};\n  // Central energy of each bin\n  std::array<double, EnergyBins> table_neutrino_energies;\n  // Table discretization\n  std::vector<double> table_log_density{};\n  std::vector<double> table_log_temperature{};\n  std::vector<double> table_electron_fraction{};\n\n  intrp::UniformMultiLinearSpanInterpolation<3,\n                                             3 * EnergyBins * NeutrinoSpecies>\n      interpolator_{};\n\n  const double min_kappa = 1.e-70;\n  const double max_kappa = 1.e70;\n};\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/NeutrinoMomentsFromMonteCarlo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/NeutrinoMomentsFromMonteCarlo.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CellVolume.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace Particles::MonteCarlo {\n\nvoid inertial_frame_energy_density(\n    gsl::not_null<Scalar<DataVector>*> fluid_frame_energy_density,\n    const std::vector<Packet>& packets, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_determinant_spatial_metric,\n    const Mesh<3>& mesh,\n    const Scalar<DataVector>& det_jacobian_logical_to_inertial) {\n  Scalar<DataVector> cell_inertial_three_volume(lapse);\n  cell_inertial_coordinate_three_volume_finite_difference(\n      &cell_inertial_three_volume, mesh, det_jacobian_logical_to_inertial);\n  *fluid_frame_energy_density = make_with_value<Scalar<DataVector>>(lapse, 0.0);\n  ;\n  for (const auto& packet : packets) {\n    const size_t& idx = packet.index_of_closest_grid_point;\n    get(*fluid_frame_energy_density)[idx] +=\n        get(lapse)[idx] / get(cell_inertial_three_volume)[idx] /\n        get(sqrt_determinant_spatial_metric)[idx] * packet.momentum_upper_t *\n        packet.number_of_neutrinos;\n  }\n}\n\nvoid InertialFrameEnergyDensityCompute::function(\n    gsl::not_null<return_type*> inertial_frame_density,\n    const std::vector<Packet>& packets, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_determinant_spatial_metric,\n    const Mesh<3>& mesh,\n    const Scalar<DataVector>& det_inv_jacobian_logical_to_inertial) {\n  Scalar<DataVector> det_jacobian_logical_to_inertial(lapse);\n  get(det_jacobian_logical_to_inertial) =\n      1.0 / get(det_inv_jacobian_logical_to_inertial);\n  inertial_frame_energy_density(inertial_frame_density, packets, lapse,\n                                sqrt_determinant_spatial_metric, mesh,\n                                det_jacobian_logical_to_inertial);\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/NeutrinoMomentsFromMonteCarlo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace Particles::MonteCarlo {\n\nvoid inertial_frame_energy_density(\n    gsl::not_null<Scalar<DataVector>*> fluid_frame_energy_density,\n    const std::vector<Packet>& packets, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_determinant_spatial_metric,\n    const Mesh<3>& mesh,\n    const Scalar<DataVector>& det_jacobian_logical_to_inertial);\n\nnamespace Tags {\n/// Simple tag containing the inertial frame energy\n/// density on the grid for Monte Carlo packets\nstruct InertialFrameEnergyDensity : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace Tags\n\nstruct InertialFrameEnergyDensityCompute : Tags::InertialFrameEnergyDensity,\n                                           db::ComputeTag {\n  using base = Tags::InertialFrameEnergyDensity;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      Particles::MonteCarlo::Tags::PacketsOnElement,\n      gr::Tags::Lapse<DataVector>, gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToInertial>;\n\n  static void function(\n      gsl::not_null<return_type*> inertial_frame_density,\n      const std::vector<Packet>& packets, const Scalar<DataVector>& lapse,\n      const Scalar<DataVector>& sqrt_determinant_spatial_metric,\n      const Mesh<3>& mesh,\n      const Scalar<DataVector>& det_inv_jacobian_logical_to_inertial);\n};\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Packet.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n\nnamespace Particles::MonteCarlo {\n\nvoid Packet::pup(PUP::er& p) {\n  p | species;\n  p | number_of_neutrinos;\n  p | index_of_closest_grid_point;\n  p | time;\n  p | momentum_upper_t;\n  p | coordinates;\n  p | momentum;\n}\n\nvoid Packet::renormalize_momentum(\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& lapse) {\n  momentum_upper_t = 0.0;\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      momentum_upper_t +=\n          inv_spatial_metric.get(i, j)[index_of_closest_grid_point] *\n          momentum[i] * momentum[j];\n    }\n  }\n  momentum_upper_t =\n      sqrt(momentum_upper_t) / get(lapse)[index_of_closest_grid_point];\n}\n\ndouble compute_fluid_frame_energy(\n    const Packet& packet, const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lower_spatial_four_velocity,\n    const Scalar<DataVector>& lapse,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric) {\n  const size_t idx = packet.index_of_closest_grid_point;\n  double fluid_frame_energy =\n      get(lorentz_factor)[idx] * get(lapse)[idx] * packet.momentum_upper_t;\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      fluid_frame_energy -= inv_spatial_metric.get(i, j)[idx] *\n                            lower_spatial_four_velocity.get(i)[idx] *\n                            packet.momentum[j];\n    }\n  }\n  return fluid_frame_energy;\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Packet.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// Items related to the evolution of particles\n/// Items related to Monte-Carlo radiation transport\nnamespace Particles::MonteCarlo {\n\n/// Struct representing a single Monte Carlo packet of neutrinos\nstruct Packet {\n  /// Constructor\n  Packet(const size_t& species_,\n         const double& number_of_neutrinos_,\n         const size_t& index_of_closest_grid_point_, const double& time_,\n         const double& coord_x_, const double& coord_y_, const double& coord_z_,\n         const double& p_upper_t_, const double& p_x_, const double& p_y_,\n         const double& p_z_)\n      : species(species_),\n        number_of_neutrinos(number_of_neutrinos_),\n        index_of_closest_grid_point(index_of_closest_grid_point_),\n        time(time_),\n        momentum_upper_t(p_upper_t_) {\n    coordinates[0] = coord_x_;\n    coordinates[1] = coord_y_;\n    coordinates[2] = coord_z_;\n    momentum[0] = p_x_;\n    momentum[1] = p_y_;\n    momentum[2] = p_z_;\n  }\n\n  /// Default constructor needed to make Packet puppable\n  /// For the same reason, we have default initialization\n  /// for a number of member variables to unphysical values.\n  Packet()\n      : species{0},\n        number_of_neutrinos{0},\n        index_of_closest_grid_point{0},\n        time{-1.0},\n        momentum_upper_t{0.0} {\n    coordinates[0] = 0.0;\n    coordinates[1] = 0.0;\n    coordinates[2] = 0.0;\n    momentum[0] = 0.0;\n    momentum[1] = 0.0;\n    momentum[2] = 0.0;\n  }\n\n  /// Species of neutrinos (in the code, just an index used to access the\n  /// right interaction rates; typically \\f$0=\\nu_e, 1=\\nu_a, 2=\\nu_x\\f$)\n  size_t species = std::numeric_limits<size_t>::max();\n\n  /// Number of neutrinos represented by current packet\n  /// Note that this number is rescaled so that\n  /// `Energy_of_packet = N * Energy_of_neutrinos`\n  /// with the packet energy in G=Msun=c=1 units but\n  /// the neutrino energy in MeV!\n  double number_of_neutrinos = std::numeric_limits<double>::signaling_NaN();\n\n  /// Index of the closest point on the FD grid.\n  size_t index_of_closest_grid_point = std::numeric_limits<size_t>::max();\n\n  /// Current time\n  double time = std::numeric_limits<double>::signaling_NaN();\n\n  /// Stores \\f$p^t\\f$\n  double momentum_upper_t = std::numeric_limits<double>::signaling_NaN();\n\n  /// Coordinates of the packet, in element logical coordinates\n  tnsr::I<double, 3, Frame::ElementLogical> coordinates;\n\n  /// Spatial components of the 4-momentum \\f$p_i\\f$, in Inertial coordinates\n  tnsr::i<double, 3, Frame::Inertial> momentum;\n\n  /*!\n   * Recalculte \\f$p^t\\f$ using the fact that the 4-momentum is a null vector\n   * \\f{align}{\n   * p^t = \\sqrt{\\gamma^{ij} p_i p_j}/\\alpha\n   * \\f}\n   */\n  void renormalize_momentum(\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& lapse);\n\n  void pup(PUP::er& p);\n\n  /// Overloaded comparison operator. Useful for test; in an actual simulation\n  /// two distinct packets are not truly \"identical\" as they represent different\n  /// particles.\n  bool operator==(const Packet& rhs) const {\n    return (this->species == rhs.species) and\n           (this->number_of_neutrinos == rhs.number_of_neutrinos) and\n           (this->index_of_closest_grid_point ==\n            rhs.index_of_closest_grid_point) and\n           (this->time == rhs.time) and\n           (this->momentum_upper_t == rhs.momentum_upper_t) and\n           (this->coordinates == rhs.coordinates) and\n           (this->momentum == rhs.momentum);\n  };\n};\n\n/*!\n * Calculate energy of neutrinos in a frame comoving with the fluid\n *\n * \\f{align}{\n * E = W \\alpha p^t - \\gamma^{ij} u_i p_j\n * \\f}\n */\ndouble compute_fluid_frame_energy(\n    const Packet& packet, const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lower_spatial_four_velocity,\n    const Scalar<DataVector>& lapse,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric);\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Scattering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/Scattering.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CouplingTermsForPropagation.hpp\"\n#include \"Evolution/Particles/MonteCarlo/EvolvePackets.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nusing hydro::units::nuclear::proton_mass;\n\nnamespace Particles::MonteCarlo {\n\n// All equations refer to Foucart 2018 (10.1093/mnras/sty108)\nDiffusionMonteCarloParameters::DiffusionMonteCarloParameters()\n    : ScatteringRofP(std::array<double, 1001>()),\n      OpacityDependentCorrectionToRofP(std::array<double, 101>()) {\n  // Calculate function r(P), implicitly defined by Eq. (30)\n  // We integrate using the trapezoid rule to find where\n  // r(P) = i * 0.001\n  const double dr = 1.e-5;\n  double r = dr;\n  double integrand_low = 0.0;\n  double integrand_high = 4. / sqrt(M_PI) * square(r) * exp(-square(r));\n  double integral = 0.5 * (integrand_low + integrand_high) * dr;\n  gsl::at(ScatteringRofP, 0) = 0.0;\n  const size_t p_pts = ScatteringRofP.size();\n  for (size_t i = 1; i < p_pts - 1; i++) {\n    while (integral * static_cast<double>(p_pts - 1) < static_cast<double>(i)) {\n      r += dr;\n      integrand_low = integrand_high;\n      integrand_high = 4. / sqrt(M_PI) * square(r) * exp(-square(r));\n      integral += 0.5 * (integrand_low + integrand_high) * dr;\n    }\n    gsl::at(ScatteringRofP, i) = r - 0.5 * dr;\n  }\n  // Technically, r(1) = infinity... we instead interpolate from the last\n  // two points (and will later enforce causality to make sure that\n  // we never `diffuse' packets faster than the speed of light).\n  gsl::at(ScatteringRofP, p_pts - 1) =\n      2.0 * gsl::at(ScatteringRofP, p_pts - 2) -\n      gsl::at(ScatteringRofP, p_pts - 3);\n\n  // For optical depth between MinimumOpacityForDiffusion and\n  // MaximumOpacityForCorrection, we renormalize r(P)\n  // so that there is a probability exp(-tau) of superluminal motion,\n  // which is interpreted as the absence of scattering events.\n  // See Eq.(30); we precompute here the numerical factor on the\n  // rhs of Eq (30) as a function of the optical depth.\n  const size_t tau_pts = OpacityDependentCorrectionToRofP.size();\n  const double dTau =\n      (MaximumOpacityForCorrection - MinimumOpacityForDiffusion) /\n      (static_cast<double>(tau_pts) - 1.);\n  for (size_t nt = 0; nt < tau_pts; nt++) {\n    const double cTau =\n        MinimumOpacityForDiffusion + static_cast<double>(nt) * dTau;\n    const double TargetRho = sqrt(3. * cTau / 4.);\n    // Find A such that F[TargetRho]=A\n    // i.e. ScatteringRofP[A]=TargetRho\n    // Note that as TargetRho > 0 and ScatteringRofP[0]=0\n    // upper_bracket >= 1; so there is always a previous\n    // element available.\n    const auto upper_bracket = std::lower_bound(\n        ScatteringRofP.begin(), ScatteringRofP.end(), TargetRho);\n    if (upper_bracket == ScatteringRofP.end()) {\n      gsl::at(OpacityDependentCorrectionToRofP, nt) = (1. - exp(-cTau));\n    } else {\n      // Linear interpolation between two closest points to solution.\n      // The division by (p_pts - 1) is needed to get the normalization\n      // right (i.e. a number in [0,1]).\n      const auto lower_bracket = std::prev(upper_bracket);\n      const auto lower_index = static_cast<size_t>(\n          std::distance(ScatteringRofP.begin(), lower_bracket));\n      const size_t upper_index = lower_index + 1;\n      const double TargetF =\n          ((gsl::at(ScatteringRofP, upper_index) - TargetRho) *\n               static_cast<double>(lower_index) +\n           (TargetRho - gsl::at(ScatteringRofP, lower_index)) *\n               static_cast<double>(upper_index)) /\n          (gsl::at(ScatteringRofP, upper_index) -\n           gsl::at(ScatteringRofP, lower_index)) /\n          static_cast<double>(p_pts - 1);\n      gsl::at(OpacityDependentCorrectionToRofP, nt) =\n          TargetF / (1. - exp(-cTau));\n    }\n  }\n}\n\nvoid DiffusionPrecomputeForElement(\n    gsl::not_null<DataVector*> prefactor_diffusion_time_vector,\n    gsl::not_null<DataVector*> prefactor_diffusion_four_velocity,\n    gsl::not_null<DataVector*> prefactor_diffusion_time_step,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lower_spatial_four_velocity,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric) {\n  // g_tt, stored in prefactor_diffusion_time_step\n  // g_tt = \\alpha^2 - \\beta^i \\beta_i\n  *prefactor_diffusion_time_step =\n      -square(get(lapse)) + get(dot_product(shift, shift, spatial_metric));\n  // u_t stored in prefactor_diffusion_four_velocity\n  // u_t = - W * (lapse - beta^i v_i)\n  *prefactor_diffusion_four_velocity =\n      get(dot_product(shift, lower_spatial_four_velocity)) -\n      get(lorentz_factor) * get(lapse);\n  // Precompute u_t^2 + g_tt\n  *prefactor_diffusion_time_vector =\n      square(*prefactor_diffusion_four_velocity) +\n      (*prefactor_diffusion_time_step);\n\n  for (size_t i = 0; i < prefactor_diffusion_time_vector->size(); i++) {\n    // First, deal with case with small transport velocity. In that case,\n    // Eq 32 is singular, but we also do not need to move packets with\n    // the fluid in the diffusion step (as the fluid is not moving on\n    // the grid). Set prefactor_diffusion_time_step to -1, which we will\n    // check for when performing diffusion.\n    if (fabs((*prefactor_diffusion_time_vector)[i]) < 1.e-10) {\n      (*prefactor_diffusion_time_step)[i] = -1.0;\n      (*prefactor_diffusion_time_vector)[i] =\n          std::numeric_limits<double>::signaling_NaN();\n      (*prefactor_diffusion_four_velocity)[i] =\n          std::numeric_limits<double>::signaling_NaN();\n      continue;\n    }\n    // Eq 31 stored in prefactor_diffusion_time_step\n    // A/B = - (u_t + sqrt(u_t^2 + g_tt))/g_tt\n    const double A_over_B = -((*prefactor_diffusion_four_velocity)[i] +\n                              sqrt((*prefactor_diffusion_time_vector)[i])) /\n                            (*prefactor_diffusion_time_step)[i];\n    // Eq. 32 for neutrinos of 1MeV energy (the result scale linearly with\n    // energy, and can thus be modified as needed during packet evolution) B = 1\n    // / (1 - (A/B) u_t)\n    (*prefactor_diffusion_four_velocity)[i] =\n        1.0 / (1.0 - A_over_B * (*prefactor_diffusion_four_velocity)[i]);\n    // A from Eq. 32 and 31\n    // A= (A/B) * B\n    (*prefactor_diffusion_time_vector)[i] =\n        A_over_B * (*prefactor_diffusion_four_velocity)[i];\n    // Eq. 33\n    // prefactor_diffusion_time_step = (A + B u^t)/(B u^t) = 1 + A*alpha/(B*W)\n    (*prefactor_diffusion_time_step)[i] =\n        (get(lapse)[i] * A_over_B / get(lorentz_factor)[i]) + 1.0;\n  }\n}\n\nstd::array<double, 4> scatter_packet(\n    const gsl::not_null<Packet*> packet,\n    const gsl::not_null<std::mt19937*> random_number_generator,\n    const double& fluid_frame_energy,\n    const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_jacobian,\n    const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_inverse_jacobian) {\n  const size_t& idx = packet->index_of_closest_grid_point;\n  std::uniform_real_distribution<double> rng_uniform_zero_to_one(0.0, 1.0);\n  const double cos_theta =\n      2.0 * rng_uniform_zero_to_one(*random_number_generator) - 1.0;\n  const double phi =\n      2.0 * M_PI * rng_uniform_zero_to_one(*random_number_generator);\n  const double cos_phi = cos(phi);\n  const double sin_phi = sin(phi);\n  const double sin_theta = sqrt(1.0 - cos_theta * cos_theta);\n  std::array<double, 4> four_momentum_fluid{\n      fluid_frame_energy, sin_theta * cos_phi * fluid_frame_energy,\n      sin_theta * sin_phi * fluid_frame_energy, cos_theta * fluid_frame_energy};\n\n  packet->momentum_upper_t = 0.0;\n  for (size_t d = 0; d < 4; d++) {\n    packet->momentum_upper_t +=\n        inertial_to_fluid_inverse_jacobian.get(0, d)[idx] *\n        gsl::at(four_momentum_fluid, d);\n  }\n  for (size_t d = 0; d < 3; d++) {\n    // Multiply by -1.0 because p_t = -p^t in an orthonormal frame\n    packet->momentum.get(d) = -gsl::at(four_momentum_fluid, 0) *\n                              inertial_to_fluid_jacobian.get(d + 1, 0)[idx];\n    for (size_t dd = 0; dd < 3; dd++) {\n      packet->momentum.get(d) +=\n          gsl::at(four_momentum_fluid, dd + 1) *\n          inertial_to_fluid_jacobian.get(d + 1, dd + 1)[idx];\n    }\n  }\n  return std::array<double, 4>{cos_theta, sin_theta, cos_phi, sin_phi};\n}\n\nvoid diffuse_packet(\n    const gsl::not_null<Packet*> packet,\n    const gsl::not_null<std::mt19937*> random_number_generator,\n    const gsl::not_null<double*> neutrino_energy,\n    const gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        coupling_tilde_s,\n    const gsl::not_null<Scalar<DataVector>*> coupling_rho_ye,\n    const size_t extended_idx, const double time_step,\n    const DiffusionMonteCarloParameters& diffusion_params,\n    const double absorption_opacity, const double scattering_opacity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lower_spatial_four_velocity,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        inverse_jacobian_logical_to_inertial,\n    const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_jacobian,\n    const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_inverse_jacobian,\n    const DataVector& prefactor_diffusion_time_step,\n    const DataVector& prefactor_diffusion_four_velocity,\n    const DataVector& prefactor_diffusion_time_vector) {\n  const size_t idx = packet->index_of_closest_grid_point;\n  double scattering_optical_depth = scattering_opacity * time_step *\n                                    get(lapse)[idx] * get(lorentz_factor)[idx];\n  if (scattering_optical_depth <\n      diffusion_params.MinimumOpacityForDiffusion * (1.0 - 1.e-12)) {\n    ERROR(\"Optical depth too low for diffusion approximation\");\n  }\n  if (scattering_optical_depth < diffusion_params.MinimumOpacityForDiffusion) {\n    scattering_optical_depth = diffusion_params.MinimumOpacityForDiffusion;\n  }\n  // Draw random number P in Eq (29)\n  std::uniform_real_distribution<double> rng_uniform_zero_to_one(0.0, 1.0);\n  double p_diffusion = rng_uniform_zero_to_one(*random_number_generator);\n  // Correct for finite opacity, as in Eq (30)\n  if (scattering_optical_depth < diffusion_params.MaximumOpacityForCorrection) {\n    // Find location of current opacity in vector of stored value\n    // As we only reach this point if the opacity is between\n    // MinimumOpacityForDiffusion and MaximumOpacityForCorrection,\n    // nti and nti+1 are guaranteed to be in bounds.\n    const double nt =\n        (scattering_optical_depth -\n         diffusion_params.MinimumOpacityForDiffusion) /\n        (diffusion_params.MaximumOpacityForCorrection -\n         diffusion_params.MinimumOpacityForDiffusion) *\n        static_cast<double>(\n            diffusion_params.OpacityDependentCorrectionToRofP.size() - 1);\n    const auto nti = static_cast<size_t>(floor(nt));\n    // Interpolate linearly between closest value\n    const double correction_factor =\n        gsl::at(diffusion_params.OpacityDependentCorrectionToRofP, nti) *\n            (static_cast<double>(nti) + 1. - nt) +\n        gsl::at(diffusion_params.OpacityDependentCorrectionToRofP, nti + 1) *\n            (nt - static_cast<double>(nti));\n    p_diffusion *= correction_factor;\n  }\n\n  if (p_diffusion < 0.0) {\n    ERROR(\"p_diffusion should be a positive number\");\n  }\n  // Effective speed of the packet in the fluid frame as it diffuses\n  double dr_over_dt_free = 1.0;\n  // If p_diffusion >= 1.0, we assume no scattering (maintains causality)\n  // Otherwise, apply normalization with optical depth to calculate dr/dt\n  // i.e. Eq 28\n  if (p_diffusion < 1.0) {\n    // As p_diffusion is between 0 and 1 (not included) nri and nri+1\n    // are guaranteed to be in bounds.\n    const double nr =\n        p_diffusion *\n        static_cast<double>(diffusion_params.ScatteringRofP.size() - 1);\n    const auto nri = static_cast<size_t>(floor(nr));\n    dr_over_dt_free = sqrt(4.0 / 3.0 / scattering_optical_depth) *\n                      (gsl::at(diffusion_params.ScatteringRofP, nri + 1) *\n                           (nr - static_cast<double>(nri)) +\n                       gsl::at(diffusion_params.ScatteringRofP, nri) *\n                           (static_cast<double>(nri + 1) - nr));\n    dr_over_dt_free = std::min(1.0, dr_over_dt_free);\n  }\n\n  // Evolution comoving with fluid\n  // We first evolve the packet along a null geodesic in such a way as to end up\n  // at the same location as the fluid after (1.0 - dr_over_dt_free) * time_step\n  const double dt_comoving =\n      (1.0 - dr_over_dt_free) * time_step * prefactor_diffusion_time_step[idx];\n  // First case, prefactor_diffusion_time_step>0, which correpsonds to non-zero\n  // transport velocities\n  if (dt_comoving > 0.0) {\n    // Calculate p_i = nu * (prefactor_diffusion_four_velocity* u_i +\n    // prefactor_diffusion_time_vector * shift_i) We do not need p^t, which is\n    // recalculated by evolve_single_packet_on_geodesic\n    for (size_t i = 0; i < 3; i++) {\n      packet->momentum[i] = (prefactor_diffusion_four_velocity[idx] *\n                             lower_spatial_four_velocity.get(i)[idx]);\n      for (size_t j = 0; j < 3; j++) {\n        packet->momentum[i] += prefactor_diffusion_time_vector[idx] *\n                               spatial_metric.get(i, j)[idx] *\n                               shift.get(j)[idx];\n      }\n      packet->momentum[i] *= (*neutrino_energy);\n    }\n    evolve_single_packet_on_geodesic(packet, dt_comoving, lapse, shift, d_lapse,\n                                     d_shift, d_inv_spatial_metric,\n                                     inv_spatial_metric, mesh_velocity,\n                                     inverse_jacobian_logical_to_inertial);\n    // For the rest of the 'comoving time', the packet is modeled as remaining\n    // stationary.\n    packet->time += (1.0 - dr_over_dt_free) * time_step - dt_comoving;\n    packet->renormalize_momentum(inv_spatial_metric, lapse);\n    (*neutrino_energy) = compute_fluid_frame_energy(*packet, lorentz_factor,\n                                                    lower_spatial_four_velocity,\n                                                    lapse, inv_spatial_metric);\n  } else {\n    // Zero transport velocity; we only need to update the time.\n    packet->time += (1.0 - dr_over_dt_free) * time_step;\n    // ... and potentially correct for grid motion\n    if (mesh_velocity.has_value()) {\n      for (size_t i = 0; i < 3; i++) {\n        for (size_t j = 0; j < 3; j++) {\n          packet->coordinates[i] -=\n              mesh_velocity.value().get(j)[idx] * (1.0 - dr_over_dt_free) *\n              time_step * inverse_jacobian_logical_to_inertial.get(i, j)[idx];\n        }\n      }\n    }\n  }\n  // Coupling with the fluid for a packet comoving with the fluid\n  // Energy coupling\n  const double energy_coupling = (1.0 - dr_over_dt_free) * time_step *\n    get(lapse)[idx] * absorption_opacity * packet->number_of_neutrinos *\n    (*neutrino_energy);\n  coupling_tilde_tau->get()[extended_idx] += energy_coupling;\n  // Momentum coupling term\n  for (size_t d = 0; d < 3; d++) {\n    coupling_tilde_s->get(d)[extended_idx] +=\n        energy_coupling / get(lorentz_factor)[idx] *\n        lower_spatial_four_velocity.get(d)[idx];\n  }\n  // Lepton number coupling term\n  if (packet->species < 2) {\n    coupling_rho_ye->get()[extended_idx] +=\n        (packet->species == 0 ? 1.0 : -1.0) * proton_mass * energy_coupling /\n        (*neutrino_energy) / get(lorentz_factor)[idx];\n  }\n\n  // Scatter in fluid frame. Output array contains (cos_theta, sin_theta,\n  // cos_phi, sin_phi) for direction of momentum in fluid frame spherical\n  // coordinates.\n  const std::array<double, 4> fluid_frame_angles = scatter_packet(\n      packet, random_number_generator, *neutrino_energy,\n      inertial_to_fluid_jacobian, inertial_to_fluid_inverse_jacobian);\n\n  // Free evolution away from fluid frame\n  // We correct the remaining time so that all packets are evolved for the\n  // same time in the fluid frame, and not the grid frame. This is needed\n  // to recover the proper average velocity of the packets.\n  const double dt_corr =\n      std::clamp(get(lapse)[idx] / get(lorentz_factor)[idx] *\n                     packet->momentum_upper_t / (*neutrino_energy),\n                 0.0, 2.0);\n  const double dt_free = dr_over_dt_free * time_step * dt_corr;\n  evolve_single_packet_on_geodesic(\n      packet, dt_free, lapse, shift, d_lapse, d_shift, d_inv_spatial_metric,\n      inv_spatial_metric, mesh_velocity, inverse_jacobian_logical_to_inertial);\n  packet->renormalize_momentum(inv_spatial_metric, lapse);\n  (*neutrino_energy) = compute_fluid_frame_energy(*packet, lorentz_factor,\n                                                  lower_spatial_four_velocity,\n                                                  lapse, inv_spatial_metric);\n  // Add coupling term for free propagation\n  const double& lapse_packet = get(lapse)[idx];\n  const double& lorentz_factor_packet = get(lorentz_factor)[idx];\n  const std::array<double, 3> lower_spatial_four_velocity_packet = {\n    lower_spatial_four_velocity.get(0)[idx],\n    lower_spatial_four_velocity.get(1)[idx],\n    lower_spatial_four_velocity.get(2)[idx]};\n  AddCouplingTermsForPropagation(\n      coupling_tilde_tau, coupling_tilde_s, coupling_rho_ye, *packet,\n      extended_idx, dt_free, absorption_opacity, 0.0, (*neutrino_energy),\n      lapse_packet, lorentz_factor_packet, lower_spatial_four_velocity_packet);\n\n  // Get final momentum in fluid frame then transform to inertial frame\n  const size_t b_pts = diffusion_params.BvsRhoForScattering.size();\n  const double rb = dr_over_dt_free * static_cast<double>(b_pts - 1);\n  const auto nb = static_cast<size_t>(floor(rb));\n  // Two cases used to avoid potential out-of-bound indexing\n  // when dr_over_dt_free is exactly 1 (its maximum potential value)\n  const double B =\n      (nb == b_pts - 1)\n          ? gsl::at(diffusion_params.BvsRhoForScattering, nb)\n          : gsl::at(diffusion_params.BvsRhoForScattering, nb) *\n                    (static_cast<double>(nb) + 1.0 - rb) +\n                gsl::at(diffusion_params.BvsRhoForScattering, nb + 1) *\n                    (rb - static_cast<double>(nb));\n  // Theta2, phi2 = angles for packet propagation in spherical coordinates\n  // with axis along the direction of propagation of the packet after the\n  // first scattering event; Eq. (34)\n  double random_number = rng_uniform_zero_to_one(*random_number_generator);\n  const double cth2 = std::clamp(\n      B - (B + 1.) * exp(random_number * log((B - 1.) / (B + 1.))), -1.0, 1.0);\n  const double sth2 = sqrt(1.0 - square(cth2));\n  random_number =\n      rng_uniform_zero_to_one(*random_number_generator) * 2.0 * M_PI;\n  const double cph2 = cos(random_number);\n  const double sph2 = sin(random_number);\n\n  const auto& [cth, sth, cph, sph] = fluid_frame_angles;\n\n  // Final fluid frame momentum (between Eq 33 and 34)\n  std::array<double, 4> final_fluid_frame_momentum{\n      (*neutrino_energy),\n      (*neutrino_energy) *\n          (-sth2 * cph2 * cth * cph + sth2 * sph2 * sph + cth2 * sth * cph),\n      (*neutrino_energy) *\n          (-sth2 * cph2 * cth * sph - sth2 * sph2 * cph + cth2 * sth * sph),\n      (*neutrino_energy) * (sth2 * cph2 * sth + cth2 * cth)};\n  // Convert from fluid to inertial frame\n  packet->momentum_upper_t = 0.0;\n  for (size_t d = 0; d < 4; d++) {\n    packet->momentum_upper_t +=\n        inertial_to_fluid_inverse_jacobian.get(0, d)[idx] *\n        gsl::at(final_fluid_frame_momentum, d);\n  }\n  for (size_t d = 0; d < 3; d++) {\n    // Multiply by -1.0 because p_t = -p^t in an orthonormal frame\n    packet->momentum.get(d) = -gsl::at(final_fluid_frame_momentum, 0) *\n                              inertial_to_fluid_jacobian.get(d + 1, 0)[idx];\n    for (size_t dd = 0; dd < 3; dd++) {\n      packet->momentum.get(d) +=\n          gsl::at(final_fluid_frame_momentum, dd + 1) *\n          inertial_to_fluid_jacobian.get(d + 1, dd + 1)[idx];\n    }\n  }\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Scattering.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Frame {\nstruct Fluid;\n}  // namespace Frame\n\n/// Items related to the evolution of particles\n/// Items related to Monte-Carlo radiation transport\nnamespace Particles::MonteCarlo {\n\nstruct Packet;\n\n/// Precomputed quantities useful for the diffusion approximation\n/// in high-scattering opacity regions.\n/// We follow \\cite Foucart2017mbt\n/// Note that r_d in that manuscript should be\n/// (distance diffused)/(time elapsed)\n/// i.e. the paper is missing a normalization\n/// of r_d by delta_t. The upper bound of the integral in Eq (30)\n/// should just be sqrt(3*tau/4)\n/// All quantities in this struct are fixed; we could also just\n/// hard-code them to save us the on-the-fly calculation, or\n/// have a single instance of this struct in the global cache.\nstruct DiffusionMonteCarloParameters {\n  DiffusionMonteCarloParameters();\n\n  const double MinimumOpacityForDiffusion = 3.0;\n  const double MaximumOpacityForCorrection = 11.0;\n  /// Definition of the vector B_i, equation (35)\n  const std::array<double, 21> BvsRhoForScattering{\n      1000., 18.74, 7.52, 4.75, 3.51, 2.78, 2.32, 2.00,  1.77,   1.60,     1.47,\n      1.36,  1.28,  1.21, 1.15, 1.10, 1.07, 1.04, 1.019, 1.0027, 1.0000001};\n  /// Storage for the function r(P) implicitly defined by Eq (29)\n  /// ScatteringRofP[i] = r(0.001*i)\n  /// Calculation performed in constructor\n  std::array<double, 1001> ScatteringRofP;\n  /// Storage for the opacity dependent correction on the\n  /// right-hand side of Eq (30)\n  /// We use 101 points between the min and max opacities\n  /// defined above.\n  /// Calculation performed in constructor\n  std::array<double, 101> OpacityDependentCorrectionToRofP;\n};\n\n/// Precompute quantities needed for evolving packets in the diffusion\n/// approximation, i.e. the transport velocity in logical coordinates\n/// and the coefficients defined by Eq (31-33)\nvoid DiffusionPrecomputeForElement(\n    gsl::not_null<DataVector*> prefactor_diffusion_time_vector,\n    gsl::not_null<DataVector*> prefactor_diffusion_four_velocity,\n    gsl::not_null<DataVector*> prefactor_diffusion_time_step,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lower_spatial_four_velocity,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric);\n\n/// Elastic scattering, which is just redrawing the momentum from\n/// an isotropic distribution in the fluid frame, at constant\n/// fluid frame energy.\nstd::array<double, 4> scatter_packet(\n    gsl::not_null<Packet*> packet,\n    gsl::not_null<std::mt19937*> random_number_generator,\n    const double& fluid_frame_energy,\n    const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_jacobian,\n    const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_inverse_jacobian);\n\n/// Evolve a packet for dt = time_step, assuming that we can use\n/// the diffusion approximation.\n///\n/// The coupling terms include ghost zone information, while other\n/// tensors only include live points.\n/// extended_idx is the index of the packet in DataVectors including\n/// ghost zones.\nvoid diffuse_packet(\n    gsl::not_null<Packet*> packet,\n    gsl::not_null<std::mt19937*> random_number_generator,\n    gsl::not_null<double*> neutrino_energy,\n    gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n    gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> coupling_tilde_s,\n    gsl::not_null<Scalar<DataVector>*> coupling_rho_ye, size_t extended_idx,\n    double time_step, const DiffusionMonteCarloParameters& diffusion_params,\n    double absorption_opacity, double scattering_opacity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lower_spatial_four_velocity,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        inverse_jacobian_logical_to_inertial,\n    const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_jacobian,\n    const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_inverse_jacobian,\n    const DataVector& prefactor_diffusion_time_step,\n    const DataVector& prefactor_diffusion_four_velocity,\n    const DataVector& prefactor_diffusion_time_vector);\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/SwapGrTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/SwapGrTags.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Particles::MonteCarlo {\nvoid SwapGrTags::apply(\n    const gsl::not_null<\n        Variables<typename System::spacetime_variables_tag::tags_list>*>\n        active_gr_vars,\n    const gsl::not_null<typename evolution::dg::subcell::Tags::Inactive<\n        typename System::spacetime_variables_tag>::type*>\n        inactive_gr_vars,\n    const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n    const evolution::dg::subcell::ActiveGrid active_grid) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    // We might request a switch to the DG grid even if we are already on the DG\n    // grid, and in this case we do nothing. This can occur when applying\n    // SwapGrTags to a collection of elements that may have different TCI\n    // results.\n    if (active_gr_vars->number_of_grid_points() !=\n        dg_mesh.number_of_grid_points()) {\n      ASSERT(\n          active_gr_vars->number_of_grid_points() ==\n              subcell_mesh.number_of_grid_points(),\n          \"When swapping the GR variables from subcell to DG, the active \"\n          \"GR variables should be holding the subcell variables and be of size \"\n              << subcell_mesh.number_of_grid_points()\n              << \" but they are of size \"\n              << active_gr_vars->number_of_grid_points());\n      using std::swap;\n      swap(*active_gr_vars, *inactive_gr_vars);\n    }\n  } else {\n    if (active_gr_vars->number_of_grid_points() !=\n        subcell_mesh.number_of_grid_points()) {\n      ASSERT(active_gr_vars->number_of_grid_points() ==\n                 dg_mesh.number_of_grid_points(),\n             \"When swapping the GR variables from DG to subcell, the active \"\n             \"GR variables should be holding the DG variables and be of size \"\n                 << dg_mesh.number_of_grid_points() << \" but they are of size \"\n                 << active_gr_vars->number_of_grid_points());\n      using std::swap;\n      swap(*active_gr_vars, *inactive_gr_vars);\n    }\n  }\n}\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/SwapGrTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Particles/MonteCarlo/System.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace Particles::MonteCarlo {\n/*!\n * \\brief Swaps the inactive and active GR variables.\n *\n * The values on the subcells are at the cell-centers.\n *\n * It should be possible to reduce memory usage by deallocating the GR variables\n * on the DG grid when switching to subcell. However, the opposite case is not\n * true since the GR variables are needed on the subcells if a neighbor is using\n * subcell in order to compute the neighbor's fluxes.\n *\n * \\note The `active_grid` is the grid we are swapping to, which may be the same\n * as the current grid. On output the `active_gr_vars` will match the grid that\n * `active_grid` is. This mutator is a no-op if they matched on input.\n */\nstruct SwapGrTags {\n  using return_tags = tmpl::list<typename System::spacetime_variables_tag,\n                                 evolution::dg::subcell::Tags::Inactive<\n                                     typename System::spacetime_variables_tag>>;\n  using argument_tags =\n      tmpl::list<::domain::Tags::Mesh<3>, evolution::dg::subcell::Tags::Mesh<3>,\n                 evolution::dg::subcell::Tags::ActiveGrid>;\n\n  static void apply(\n      gsl::not_null<\n          Variables<typename System::spacetime_variables_tag::tags_list>*>\n          active_gr_vars,\n      gsl::not_null<typename evolution::dg::subcell::Tags::Inactive<\n          typename System::spacetime_variables_tag>::type*>\n          inactive_gr_vars,\n      const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n      evolution::dg::subcell::ActiveGrid active_grid);\n};\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Particles::MonteCarlo {\n\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = false;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = 3;\n  // The EoS is used within the MC code itself, even if we provide\n  // the fluid variables as a background.\n  static constexpr size_t thermodynamic_dim = 3;\n\n  using mc_variables_tag = ::Tags::Variables<\n      tmpl::list<Particles::MonteCarlo::Tags::PacketsOnElement>>;\n  using variables_tag = ::Tags::Variables<tmpl::list<>>;  // mc_variables_tag;\n  using flux_variables = tmpl::list<>;\n  using gradient_variables = tmpl::list<>;\n  // GR tags needed for background metric\n  using spacetime_variables_tag = ::Tags::Variables<tmpl::list<\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, volume_dim>,\n      gr::Tags::SpatialMetric<DataVector, volume_dim>,\n      gr::Tags::InverseSpatialMetric<DataVector, volume_dim>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gh::Tags::Phi<DataVector, volume_dim, Frame::Inertial>>>;\n  using flux_spacetime_variables_tag = ::Tags::Variables<tmpl::list<>>;\n  // Hydro tags needed for background fluid\n  using hydro_variables_tag = ::Tags::Variables<hydro::grmhd_tags<DataVector>>;\n  using primitive_variables_tag = hydro_variables_tag;\n\n  using inverse_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, 3>;\n};\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <random>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MonteCarloOptions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n\n/// Items related to the evolution of particles\n/// Items related to Monte-Carlo radiation transport\n/// Tags for MC\nnamespace Particles::MonteCarlo::Tags {\n\n/// Simple tag containing the vector of Monte-Carlo\n/// packets belonging to an element.\nstruct PacketsOnElement : db::SimpleTag {\n  using type = std::vector<Particles::MonteCarlo::Packet>;\n};\n\n/// Simple tag containing an approximation of the light\n/// crossing time for each cell (the shortest time among\n/// all coordinate axis directions).\ntemplate <typename DataType>\nstruct CellLightCrossingTime : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// Simple tag storing the coupling term between\n/// MC and tilde_Tau (i.e. the energy variable)\ntemplate <typename DataType>\nstruct CouplingTildeTau : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// Simple tag storing the coupling term between\n/// MC and tilde_RhoYe (i.e. the composition variable)\ntemplate <typename DataType>\nstruct CouplingTildeRhoYe : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// Simple tag storing the coupling term between\n/// MC and tilde_S (i.e. the momentum variable)\ntemplate <typename DataType, size_t Dim>\nstruct CouplingTildeS : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame::Inertial>;\n};\n\n/// Simple tag storing the random number generator\n/// used by Monte-Carlo\nstruct RandomNumberGenerator : db::SimpleTag {\n  using type = std::mt19937;\n};\n\n/// Simple tag containing the desired energy of\n/// packets in low-density regions. The energy\n/// can be different for each neutrino species.\ntemplate <size_t NeutrinoSpecies>\nstruct DesiredPacketEnergyAtEmission : db::SimpleTag {\n  using type = std::array<DataVector, NeutrinoSpecies>;\n};\n\n/// Simple tag for the table of neutrino-matter interaction\n/// rates (emission, absorption and scattering for each\n/// energy bin and neutrino species).\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nstruct InteractionRatesTable : db::SimpleTag {\n  using type =\n      std::unique_ptr<NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags =\n      typename NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>::options;\n  static type create_from_options(const std::string filename) {\n    std::unique_ptr<Particles::MonteCarlo::NeutrinoInteractionTable<\n        EnergyBins, NeutrinoSpecies>>\n        interaction_table_ptr =\n            std::make_unique<Particles::MonteCarlo::NeutrinoInteractionTable<\n                EnergyBins, NeutrinoSpecies>>(filename);\n    return interaction_table_ptr;\n    ;\n  }\n};\n\ntemplate <size_t NeutrinoSpecies>\nstruct MonteCarloOptions : db::SimpleTag {\n  using type = std::unique_ptr<\n      Particles::MonteCarlo::MonteCarloOptions<NeutrinoSpecies>>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = typename Particles::MonteCarlo::MonteCarloOptions<\n      NeutrinoSpecies>::options;\n  static type create_from_options(\n      const std::array<double, NeutrinoSpecies> initial_packet_energy) {\n    std::unique_ptr<Particles::MonteCarlo::MonteCarloOptions<NeutrinoSpecies>>\n        mc_options_ptr = std::make_unique<\n            Particles::MonteCarlo::MonteCarloOptions<NeutrinoSpecies>>(\n            initial_packet_energy);\n    return mc_options_ptr;\n    ;\n  }\n};\n\n}  // namespace Particles::MonteCarlo::Tags\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/TakeTimeStep.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CellVolume.hpp\"\n#include \"Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Particles::MonteCarlo {\n\nnamespace {\n\nvoid combine_ghost_data(\n    gsl::not_null<DataVector*> with_ghost_data, const Mesh<3> local_mesh,\n    const size_t num_ghost_zones, const DataVector& local_data,\n    const DirectionalIdMap<3, std::optional<DataVector>>& ghost_data) {\n  const Index<3> local_extents = local_mesh.extents();\n  Index<3> ghost_extents = local_extents;\n  for (size_t d = 0; d < 3; d++) {\n    ghost_extents[d] += 2 * num_ghost_zones;\n  }\n  for (size_t i = 0; i < local_mesh.extents(0); ++i) {\n    for (size_t j = 0; j < local_mesh.extents(1); ++j) {\n      for (size_t k = 0; k < local_mesh.extents(2); ++k) {\n        (*with_ghost_data)[collapsed_index(\n            Index<3>{i + num_ghost_zones, j + num_ghost_zones,\n                     k + num_ghost_zones},\n            ghost_extents)] =\n            local_data[collapsed_index(Index<3>{i, j, k}, local_extents)];\n      }\n    }\n  }\n  // Loop over each direction. We assume at most one neighbor in each\n  // direction.\n  for (auto& [direction_id, ghost_data_dir] : ghost_data) {\n    if (ghost_data_dir) {\n      const size_t dimension = direction_id.direction().dimension();\n      const Side side = direction_id.direction().side();\n      Index<3> ghost_zone_extents = local_extents;\n      ghost_zone_extents[dimension] = num_ghost_zones;\n      for (size_t i = 0; i < ghost_zone_extents[0]; ++i) {\n        for (size_t j = 0; j < ghost_zone_extents[1]; ++j) {\n          for (size_t k = 0; k < ghost_zone_extents[2]; ++k) {\n            const Index<3> ghost_index_3d{i, j, k};\n            const size_t ghost_index =\n                collapsed_index(ghost_index_3d, ghost_zone_extents);\n            Index<3> extended_index_3d{i + num_ghost_zones, j + num_ghost_zones,\n                                       k + num_ghost_zones};\n            extended_index_3d[dimension] = (side == Side::Lower)\n                                               ? ghost_index_3d[dimension]\n                                               : local_extents[dimension] +\n                                                     num_ghost_zones +\n                                                     ghost_index_3d[dimension];\n            const size_t extended_index =\n                collapsed_index(extended_index_3d, ghost_extents);\n            (*with_ghost_data)[extended_index] =\n                ghost_data_dir.value()[ghost_index];\n          }\n        }\n      }\n    }\n  }\n}\n\n}  // namespace\n\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nvoid TemplatedLocalFunctions<EnergyBins, NeutrinoSpecies>::\n    take_time_step_on_element(\n        const gsl::not_null<std::vector<Packet>*> packets,\n        const gsl::not_null<Scalar<DataVector>* > coupling_tilde_tau,\n        const gsl::not_null<Scalar<DataVector>* > coupling_tilde_rho_ye,\n        const gsl::not_null<tnsr::i<DataVector,3>* > coupling_tilde_s,\n        const gsl::not_null<std::mt19937*> random_number_generator,\n        const gsl::not_null<std::array<DataVector, NeutrinoSpecies>*>\n            single_packet_energy,\n\n        const double start_time, const double target_end_time,\n        const EquationsOfState::EquationOfState<true, 3>& equation_of_state,\n        const NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>&\n            interaction_table,\n\n        const Scalar<DataVector>& electron_fraction,\n        const Scalar<DataVector>& rest_mass_density,\n        const Scalar<DataVector>& temperature,\n        const Scalar<DataVector>& lorentz_factor,\n        const tnsr::i<DataVector, 3, Frame::Inertial>&\n            lower_spatial_four_velocity,\n\n        const Scalar<DataVector>& lapse,\n        const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n        const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n        const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n        const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric,\n        const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n        const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n        const Scalar<DataVector>& sqrt_determinant_spatial_metric,\n        const Scalar<DataVector>& cell_light_crossing_time,\n\n        const Mesh<3>& mesh,\n        const tnsr::I<DataVector, 3, Frame::ElementLogical>& mesh_coordinates,\n        const size_t num_ghost_zones,\n        const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n            mesh_velocity,\n        const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                              Frame::Inertial>&\n            inverse_jacobian_logical_to_inertial,\n        const Scalar<DataVector>& det_jacobian_logical_to_inertial,\n        const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n            inertial_to_fluid_jacobian,\n        const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n            inertial_to_fluid_inverse_jacobian,\n\n        const DirectionalIdMap<3, std::optional<DataVector>>&\n            electron_fraction_ghost,\n        const DirectionalIdMap<3, std::optional<DataVector>>&\n            rest_mass_density_ghost,\n        const DirectionalIdMap<3, std::optional<DataVector>>&\n            temperature_ghost,\n        const DirectionalIdMap<3, std::optional<DataVector>>&\n            cell_light_crossing_time_ghost) {\n  // Minimum  temperature for use of the NuLib table\n  // (Could be an option).\n  const double minimum_temperature_table = 0.5;\n  const double time_step = target_end_time - start_time;\n\n  // Calculate volume elements. Proper volume is needed\n  // to calculate the total emission in a cell over the\n  // given time step. The 3-volume will be needed to\n  // calculate the coupling to the fluid.\n  Scalar<DataVector> cell_proper_four_volume =\n      make_with_value<Scalar<DataVector>>(lapse, 0.0);\n  cell_proper_four_volume_finite_difference(\n      &cell_proper_four_volume, lapse, sqrt_determinant_spatial_metric,\n      time_step, mesh, det_jacobian_logical_to_inertial);\n\n  Index<3> extents_with_ghost_zone = mesh.extents();\n  size_t mesh_size_with_ghost_zones = 1;\n  for (size_t d = 0; d < 3; d++) {\n    extents_with_ghost_zone[d] += 2 * num_ghost_zones;\n    mesh_size_with_ghost_zones *= extents_with_ghost_zone[d];\n  }\n\n  // Get interaction rates\n  std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>\n      emissivity_in_cell;\n  std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>\n      absorption_opacity;\n  std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>\n      scattering_opacity;\n  std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>\n      frac_ka_to_ks;\n  DataVector zero_dv_ghost_zones(mesh_size_with_ghost_zones, 0.0);\n  for (size_t ns = 0; ns < NeutrinoSpecies; ns++) {\n    for (size_t ng = 0; ng < EnergyBins; ng++) {\n      gsl::at(gsl::at(emissivity_in_cell, ns), ng) = zero_dv_ghost_zones;\n      gsl::at(gsl::at(absorption_opacity, ns), ng) = zero_dv_ghost_zones;\n      gsl::at(gsl::at(scattering_opacity, ns), ng) = zero_dv_ghost_zones;\n      gsl::at(gsl::at(frac_ka_to_ks, ns), ng) = zero_dv_ghost_zones;\n    }\n  }\n\n  // Join live points and ghost zones for fluid variables\n  // We initialize cell_light_crossing_time_with_ghost to time_step\n  // to avoid division by zero on outer boundaries\n  Scalar<DataVector> rest_mass_density_with_ghost =\n      make_with_value<Scalar<DataVector>>(zero_dv_ghost_zones, 0.0);\n  Scalar<DataVector> electron_fraction_with_ghost =\n      make_with_value<Scalar<DataVector>>(zero_dv_ghost_zones, 0.0);\n  Scalar<DataVector> temperature_with_ghost =\n      make_with_value<Scalar<DataVector>>(zero_dv_ghost_zones, 0.0);\n  Scalar<DataVector> cell_light_crossing_time_with_ghost =\n      make_with_value<Scalar<DataVector>>(zero_dv_ghost_zones, time_step);\n  combine_ghost_data(&get(rest_mass_density_with_ghost), mesh, num_ghost_zones,\n                     get(rest_mass_density), rest_mass_density_ghost);\n  combine_ghost_data(&get(electron_fraction_with_ghost), mesh, num_ghost_zones,\n                     get(electron_fraction), electron_fraction_ghost);\n  combine_ghost_data(&get(temperature_with_ghost), mesh, num_ghost_zones,\n                     get(temperature), temperature_ghost);\n  combine_ghost_data(&get(cell_light_crossing_time_with_ghost), mesh,\n                     num_ghost_zones, get(cell_light_crossing_time),\n                     cell_light_crossing_time_ghost);\n\n  this->implicit_monte_carlo_interaction_rates(\n      &emissivity_in_cell, &absorption_opacity, &scattering_opacity,\n      &frac_ka_to_ks, cell_light_crossing_time_with_ghost,\n      electron_fraction_with_ghost, rest_mass_density_with_ghost,\n      temperature_with_ghost, minimum_temperature_table,\n      interaction_table, equation_of_state);\n  const std::array<double, EnergyBins>& energy_at_bin_center =\n      interaction_table.get_neutrino_energies();\n\n  // Emit new MC packets\n  this->emit_packets(\n      packets, random_number_generator, coupling_tilde_tau, coupling_tilde_s,\n      coupling_tilde_rho_ye, start_time, time_step, mesh, num_ghost_zones,\n      emissivity_in_cell, *single_packet_energy, energy_at_bin_center,\n      lorentz_factor, lower_spatial_four_velocity, inertial_to_fluid_jacobian,\n      inertial_to_fluid_inverse_jacobian, cell_proper_four_volume);\n\n  // Propagate packets\n  evolve_packets(\n      packets, random_number_generator,\n      coupling_tilde_tau, coupling_tilde_s, coupling_tilde_rho_ye,\n      target_end_time, mesh, mesh_coordinates, num_ghost_zones,\n      absorption_opacity, scattering_opacity, energy_at_bin_center,\n      lorentz_factor, lower_spatial_four_velocity, lapse, shift, d_lapse,\n      d_shift, d_inv_spatial_metric, spatial_metric, inv_spatial_metric,\n      cell_light_crossing_time,\n      mesh_velocity, inverse_jacobian_logical_to_inertial,\n      inertial_to_fluid_jacobian, inertial_to_fluid_inverse_jacobian);\n}\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n\n#include <cmath>\n\n#include \"Evolution/Particles/MonteCarlo/EmitPackets.tpp\"\n#include \"Evolution/Particles/MonteCarlo/EvolvePacketsInElement.tpp\"\n#include \"Evolution/Particles/MonteCarlo/ImplicitMonteCarloCorrections.tpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/TakeTimeStep.tpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Particles::MonteCarlo::detail {\n// Draw a single packet with homogeneouse spatial distribution\n// in [0,1]x[0,1]x[0,1], energy of 1, isotropic momentum\n// distribution, and time in [0,1].\nvoid draw_single_packet(\n    const gsl::not_null<double*> time,\n    const gsl::not_null<std::array<double, 3>*> coord,\n    const gsl::not_null<std::array<double, 3>*> momentum,\n    const gsl::not_null<std::mt19937*> random_number_generator) {\n  std::uniform_real_distribution<double> rng_uniform_zero_to_one(0.0, 1.0);\n\n  *time = rng_uniform_zero_to_one(*random_number_generator);\n  for (size_t i = 0; i < 3; i++) {\n    gsl::at(*coord, i) = rng_uniform_zero_to_one(*random_number_generator);\n  }\n  const double cos_theta =\n      -1.0 + 2.0 * rng_uniform_zero_to_one(*random_number_generator);\n  const double phi =\n      2.0 * M_PI * rng_uniform_zero_to_one(*random_number_generator);\n  const double sin_theta = sqrt(1.0 - cos_theta * cos_theta);\n  gsl::at(*momentum, 0) = sin_theta * cos(phi);\n  gsl::at(*momentum, 1) = sin_theta * sin(phi);\n  gsl::at(*momentum, 2) = cos_theta;\n}\n}  // namespace Particles::MonteCarlo::detail\n\ntemplate struct Particles::MonteCarlo::TemplatedLocalFunctions<2, 2>;\ntemplate struct Particles::MonteCarlo::TemplatedLocalFunctions<2, 3>;\ntemplate struct Particles::MonteCarlo::TemplatedLocalFunctions<4, 3>;\ntemplate struct Particles::MonteCarlo::TemplatedLocalFunctions<16, 3>;\n"
  },
  {
    "path": "src/Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <optional>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\n\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\n\nnamespace Frame {\nstruct Fluid;\n}  // namespace Frame\n\nnamespace gsl {\n  template <typename T>\n  class not_null;\n}  // namespace gsl\n\ntemplate<size_t Dim>\nclass Mesh;\n\nnamespace Particles::MonteCarlo {\ntemplate<size_t EnergyBins,size_t NeutrinoSpecies>\nclass NeutrinoInteractionTable;\n\nstruct Packet;\n}  // namespace Particles::MonteCarlo\n\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\n/// \\endcond\n\n/// Items related to the evolution of particles\n/// Items related to Monte-Carlo radiation transport\nnamespace Particles::MonteCarlo {\n\nnamespace detail {\n\nvoid draw_single_packet(gsl::not_null<double*> time,\n                        gsl::not_null<std::array<double, 3>*> coord,\n                        gsl::not_null<std::array<double, 3>*> momentum,\n                        gsl::not_null<std::mt19937*> random_number_generator);\n}  // namespace detail\n\n\n/// Structure containing Monte-Carlo function templated on EnergyBins\n/// and/or NeutrinoSpecies\ntemplate <size_t EnergyBins, size_t NeutrinoSpecies>\nstruct TemplatedLocalFunctions {\n  /*!\n   * \\brief Function to take a single Monte Carlo time step on a\n   * finite difference element.\n   */\n  void take_time_step_on_element(\n      gsl::not_null<std::vector<Packet>*> packets,\n      gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n      gsl::not_null<Scalar<DataVector>*> coupling_tilde_ye,\n      gsl::not_null<tnsr::i<DataVector, 3>*> coupling_tilde_s,\n      gsl::not_null<std::mt19937*> random_number_generator,\n      gsl::not_null<std::array<DataVector, NeutrinoSpecies>*>\n          single_packet_energy,\n\n      double start_time, double target_end_time,\n      const EquationsOfState::EquationOfState<true, 3>& equation_of_state,\n      const NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>&\n          interaction_table,\n      const Scalar<DataVector>& electron_fraction,\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& temperature,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          lower_spatial_four_velocity,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n      const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n      const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_determinant_spatial_metric,\n      const Scalar<DataVector>& cell_light_crossing_time, const Mesh<3>& mesh,\n      const tnsr::I<DataVector, 3, Frame::ElementLogical>& mesh_coordinates,\n      size_t num_ghost_zones,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          mesh_velocity,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>&\n          inverse_jacobian_logical_to_inertial,\n      const Scalar<DataVector>& det_jacobian_logical_to_inertial,\n      const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n          inertial_to_fluid_jacobian,\n      const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n          inertial_to_fluid_inverse_jacobian,\n      const DirectionalIdMap<3, std::optional<DataVector>>&\n          electron_fraction_ghost,\n      const DirectionalIdMap<3, std::optional<DataVector>>&\n          baryon_density_ghost,\n      const DirectionalIdMap<3, std::optional<DataVector>>& temperature_ghost,\n      const DirectionalIdMap<3, std::optional<DataVector>>&\n          cell_light_crossing_time_ghost);\n\n  /*!\n   * \\brief Function emitting Monte Carlo packets\n   *\n   * We emit a total energy emissivity_in_cell * cell_proper_four_volume\n   * for each grid\n   * cell, neutrino species, and neutrino energy bin. We aim for packets of\n   * energy single_packet_energy in the fluid frame. The number of packets\n   * is, in each bin b and for each species s,\n   * `N = emission_in_cell[s][b] / single_packet_energy[s]`\n   * and randomly rounded up or down to get integer number of packets (i.e.\n   * if we want N=2.6 packets, there is a 60 percent chance of creating a 3rd\n   * packet). The position of the packets is drawn from a homogeneous\n   * distribution in the logical frame, and the direction of propagation of the\n   * neutrinos is drawn from an isotropic distribution in the fluid frame.\n   * Specifically, the 4-momentum of a packet of energy nu in the fluid frame\n   * is\n   * \\f{align}\n   * p^t &= \\nu \\\\\n   * p^x &= \\nu * sin(\\theta) * cos(\\phi) \\\\\n   * p^y &= \\nu * sin(\\theta) * sin(\\phi) \\\\\n   * p^z &= \\nu * cos(theta)\n   * \\f}\n   * with \\f$cos(\\theta)\\f$ drawn from a uniform distribution in [-1,1] and\n   * \\f$\\phi\\f$ from a uniform distribution in \\f$[0,2*\\pi]\\f$. We\n   * transform to the inertial frame \\f$p_t\\f$ and \\f$p^x,p^y,p^z\\f$ using the\n   * jacobian/inverse jacobian passed as option. The number of neutrinos in\n   * each packet is defined as\n   * `n = single_packet_energy[s] / energy_at_bin_center[b]`\n   * Note that the packet energy is in code units and energy of a bin in MeV.\n   *\n   * All tensors are assumed to correspond to live points only, except for\n   * the coupling terms and emissivity, which include ghost zones.\n   */\n  void emit_packets(\n      gsl::not_null<std::vector<Packet>*> packets,\n      gsl::not_null<std::mt19937*> random_number_generator,\n      gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> coupling_tilde_s,\n      gsl::not_null<Scalar<DataVector>*> coupling_rho_ye,\n      const double& time_start_step, const double& time_step,\n      const Mesh<3>& mesh, size_t num_ghost_zones,\n      const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n          emissivity_in_cell,\n      const std::array<DataVector, NeutrinoSpecies>& single_packet_energy,\n      const std::array<double, EnergyBins>& energy_at_bin_center,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          lower_spatial_four_velocity,\n      const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n          inertial_to_fluid_jacobian,\n      const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n        inertial_to_fluid_inverse_jacobian,\n      const Scalar<DataVector>& cell_proper_four_volume);\n\n  /*!\n   * \\brief Evolve Monte-Carlo packets within an element.\n   *\n   * Evolve packets until (approximately) the provided final time\n   * following the methods of \\cite Foucart2021mcb.\n   * The vector of Packets should contain all MC packets that we wish\n   * to advance in time. Note that this function only handles\n   * propagation / absorption / scattering of packets, but not\n   * emission. The final time of the packet may differ from the\n   * desired final time by up to 5 percent, due to the fact that when\n   * using the diffusion approximation (for large scattering\n   * opacities) we take fixed time steps in the fluid frame,\n   * leading to unpredictable time steps in the inertial frame.\n   *\n   * The absorption and scattering opacity tables include ghost\n   * zones, and so do the coupling terms. Other variables are\n   * only using live points.\n   */\n  void evolve_packets(\n      gsl::not_null<std::vector<Packet>*> packets,\n      gsl::not_null<std::mt19937*> random_number_generator,\n      gsl::not_null<Scalar<DataVector>*> coupling_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> coupling_tilde_s,\n      gsl::not_null<Scalar<DataVector>*> coupling_rho_ye, double final_time,\n      const Mesh<3>& mesh,\n      const tnsr::I<DataVector, 3, Frame::ElementLogical>& mesh_coordinates,\n      size_t num_ghost_zones,\n      const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n          absorption_opacity_table,\n      const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n          scattering_opacity_table,\n      const std::array<double, EnergyBins>& energy_at_bin_center,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          lower_spatial_four_velocity,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n      const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n      const tnsr::iJJ<DataVector, 3, Frame::Inertial>& d_inv_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& cell_light_crossing_time,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          mesh_velocity,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>&\n          inverse_jacobian_logical_to_inertial,\n      const Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n          inertial_to_fluid_jacobian,\n      const InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>&\n          inertial_to_fluid_inverse_jacobian);\n\n  /// Interpolate the opacities from values tabulated as a function of\n  /// neutrino energy in the fluid frame, to the value at the given\n  /// fluid frame energy.\n  /// We interpolate the logarithm of the opacity, linearly in fluid\n  /// frame energy.\n  void interpolate_opacities_at_fluid_energy(\n      gsl::not_null<double*> absorption_opacity_packet,\n      gsl::not_null<double*> scattering_opacity_packet,\n      double fluid_frame_energy, size_t species, size_t index,\n      const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n          absorption_opacity_table,\n      const std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>&\n          scattering_opacity_table,\n      const std::array<double, EnergyBins>& energy_at_bin_center);\n\n  /// Function responsible to correct emissivity, absorption, and scattering\n  /// rates in regions where there is a stiff coupling between the neutrinos\n  /// and the fluid. This is done by transferring a fraction\n  /// fraction_ka_to_ks of the absorption opacity to scattering opacity,\n  /// while multiplying the emissivity by ( 1 - fraction_ka_to_ks ) to\n  /// keep the equilibrium energy density constant.\n  void implicit_monte_carlo_interaction_rates(\n      gsl::not_null<\n          std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n          emissivity_in_cell,\n      gsl::not_null<\n          std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n          absorption_opacity,\n      gsl::not_null<\n          std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n          scattering_opacity,\n      gsl::not_null<\n          std::array<std::array<DataVector, EnergyBins>, NeutrinoSpecies>*>\n          fraction_ka_to_ks,\n      const Scalar<DataVector>& cell_light_crossing_time,\n      const Scalar<DataVector>& electron_fraction,\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& temperature, double minimum_temperature,\n      const NeutrinoInteractionTable<EnergyBins, NeutrinoSpecies>&\n          interaction_table,\n      const EquationsOfState::EquationOfState<true, 3>& equation_of_state);\n\n  // Floor for opacity values (absorption or scattering).\n  // This is used in two places. First, when interpolating opacities\n  // in energy space, we interpolate log(max(kappa,opacity_floor)),\n  // with kappa the tabulated value. Second, if kappa = opacity_floor\n  // for some interaction, we assume that the interaction never happens.\n  const double opacity_floor = 1.e-100;\n};\n\n}  // namespace Particles::MonteCarlo\n"
  },
  {
    "path": "src/Evolution/PassVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace evolution {\n/// Used to make a time derivative mutator as requesting the time derivatives\n/// and temporaries in a Variables.\nstruct PassVariables {};\n}  // namespace evolution\n"
  },
  {
    "path": "src/Evolution/Protocols.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ref protocols related to evolution systems\nnamespace evolution::protocols {\n\n/*!\n *\\brief Indicates the `ConformingType` represents the choice to start an\n * evolution with numeric initial data.\n *\n * Currently no requirements are imposed on the `ConformingType`.\n *\n * Here's an example of a class that conforms to this protocol:\n *\n * \\snippet Evolution/Test_Protocols.cpp conforming_type_example\n */\nstruct NumericInitialData {\n  template <typename ConformingType>\n  struct test {};\n};\n\n}  // namespace evolution::protocols\n"
  },
  {
    "path": "src/Evolution/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  Evolution\n  PYTHON_FILES\n  __init__.py\n)\n"
  },
  {
    "path": "src/Evolution/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "src/Evolution/Ringdown/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Ringdown)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  StrahlkorperCoefsAndCenters.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  StrahlkorperCoefsAndCenters.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  CoordinateMaps\n  DataStructures\n  DomainCreators\n  Domain\n  H5\n  Spectral\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  Utilities\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/Evolution/Ringdown/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Evolution/Ringdown/StrahlkorperCoefsAndCenters.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace py = pybind11;\n\nnamespace evolution::Ringdown::py_bindings {  // NOLINT\n// Silence warning about no previous declaration\nvoid bind_strahlkorper_coefs_and_centers(py::module& m);\n\nvoid bind_strahlkorper_coefs_and_centers(py::module& m) {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  m.def(\"strahlkorper_coefs_and_centers\",\n        &evolution::Ringdown::strahlkorper_coefs_and_centers,\n        py::arg(\"path_to_volume_data\"), py::arg(\"volume_subfile_name\"),\n        py::arg(\"path_to_horizons_h5\"), py::arg(\"surface_subfile_name\"),\n        py::arg(\"requested_number_of_times_from_end\"), py::arg(\"match_time\"),\n        py::arg(\"settling_timescale\"), py::arg(\"exp_func_and_2_derivs\"),\n        py::arg(\"exp_outer_bdry_func_and_2_derivs\"),\n        py::arg(\"rot_func_and_2_derivs\"), py::arg(\"trans_func_and_2_derivs\"));\n}\n}  // namespace evolution::Ringdown::py_bindings\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  // So return types are converted to DataVectors\n  py::module_::import(\"spectre.DataStructures\");\n  evolution::Ringdown::py_bindings::bind_strahlkorper_coefs_and_centers(m);\n}\n"
  },
  {
    "path": "src/Evolution/Ringdown/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyRingdown\")\n\nspectre_python_add_module(\n    Ringdown\n    LIBRARY_NAME ${LIBRARY}\n    MODULE_PATH \"Evolution\"\n    SOURCES\n    Bindings.cpp\n    PYTHON_FILES\n    __init__.py\n    ComputeRingdownShapeAndTranslationFoT.py\n)\n\nspectre_python_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n)\n\nspectre_python_link_libraries(\n    ${LIBRARY}\n    PRIVATE\n    Boost::boost\n    CoordinateMaps\n    DataStructures\n    Domain\n    DomainCreators\n    DomainStructure\n    ErrorHandling\n    Options\n    Ringdown\n    Serialization\n    Spectral\n    SphericalHarmonics\n    SphericalHarmonicsIO\n    Utilities\n    pybind11::module\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyH5\n  PySpectral\n  PyTensor\n  )\n"
  },
  {
    "path": "src/Evolution/Ringdown/Python/ComputeRingdownShapeAndTranslationFoT.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport warnings\nfrom pathlib import Path\nfrom typing import Optional, Union\n\nimport click\nimport numpy as np\nimport yaml\nfrom rich.pretty import pretty_repr\n\nimport spectre.Evolution.Ringdown as Ringdown\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import ModalVector\nfrom spectre.SphericalHarmonics import (\n    Frame,\n    Strahlkorper,\n    read_surface_ylm,\n    ylm_legend_and_data,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef cubic(x, a, b, c, d):\n    return a * x**3 + b * x**2 + c * x + d\n\n\ndef dt_cubic(x, a, b, c, d):\n    return 3 * a * x**2 + 2 * b * x + c\n\n\ndef dt2_cubic(x, a, b, c, d):\n    return 6 * a * x + 2 * b\n\n\n# Cubic fit transformed coefs to get first and second time derivatives\ndef fit_to_a_cubic(times, coefs, match_time, zero_coefs_eps):\n    fits = []\n    fit_ahc = []\n    fit_dt_ahc = []\n    fit_dt2_ahc = []\n    for j in np.arange(0, coefs.shape[-1], 1):\n        # Optionally, avoid fitting coefficients sufficiently close to zero by\n        # just setting these coefficients and their time derivatives to zero.\n        if (\n            zero_coefs_eps is not None\n            and sum(np.abs(coefs[:, j])) < zero_coefs_eps\n        ):\n            fits.append(np.zeros(4))\n            fit_ahc.append(0.0)\n            fit_dt_ahc.append(0.0)\n            fit_dt2_ahc.append(0.0)\n            continue\n        # Ignore RankWarnings suggesting the fit might not be good enough;\n        # for equal-mass non-spinning, sufficiently good fits for starting\n        # a ringdown, even though RankWarnings were triggered\n        with warnings.catch_warnings():\n            # In numpy 2.0+, RankWarning was moved to np.exceptions.RankWarning\n            try:\n                warnings.simplefilter(\"ignore\", np.RankWarning)\n            except AttributeError:\n                warnings.simplefilter(\"ignore\", np.exceptions.RankWarning)\n            fit = np.polyfit(times, coefs[:, j], 3)\n        fits.append(fit)\n        fit_ahc.append(cubic(match_time, *(fit)))\n        fit_dt_ahc.append(dt_cubic(match_time, *(fit)))\n        fit_dt2_ahc.append(dt2_cubic(match_time, *(fit)))\n\n    return fit_ahc, fit_dt_ahc, fit_dt2_ahc\n\n\ndef compute_ringdown_shape_and_translation_fot(\n    path_to_volume_data,\n    volume_subfile_name,\n    ahc_reductions_path,\n    ahc_subfile,\n    evaluated_fot_dict,\n    number_of_ahc_finds_for_fit,\n    match_time,\n    settling_timescale,\n    zero_coefs_eps,\n):\n    \"\"\"Computes the AhC Ylm Coefficients in the Ringdown distorted frame and\n    translation function of time that will be used for the Ringdown using the\n    functions of time and the Inertial-frame AhC strahlkorper measured from the\n    inspiral.\n\n    Arguments:\n    path_to_volume_data: The full path to any volume data containing the\n    functions of time at the time of AhC finds.\n    volume_subfile_name: Subfile containing volume data at times of AhC finds.\n    ahc_reductions_path: Path to reduction file where AhC Coefficients will be\n    written.\n    ahc_subfile: The subfile of the reductions file where AhC coefficients will\n    be placed.\n    expansion_func_and_2_derivs: Expansion functions of time from volume\n    data.\n    exp_outer_bdry_func_and_2_derivs: Outer boundary expansion function of time\n    from volume data\n    rot_func_and_2_derivs: Rotation function of time from volume data\n    number_of_ahc_finds_for_fit: The number of ahc finds that will be used in\n    the fit.\n    match_time: Time to match functions of time.\n    settling_timescale: Timescale for settle to constant functions of time.\n    zero_coefs_eps: Approximate limit to compare coefficients to 0.0\n\n    \"\"\"\n\n    ahc_times = []\n    ahc_lmax = 0\n    with spectre_h5.H5File(ahc_reductions_path, \"r\") as h5file:\n        datfile = h5file.get_dat(ahc_subfile)\n        datfile_np = np.array(datfile.get_data())\n        ahc_times = datfile_np[:, 0]\n        ahc_lmax = int(datfile_np[0][4])\n\n    # Transform AhC coefs to ringdown distorted frame and get other data\n    # needed to start a ringdown, such as initial values for functions of time\n    shape_and_translation_coefs = Ringdown.strahlkorper_coefs_and_centers(\n        path_to_volume_data=str(path_to_volume_data),\n        volume_subfile_name=volume_subfile_name,\n        path_to_horizons_h5=ahc_reductions_path,\n        surface_subfile_name=ahc_subfile,\n        requested_number_of_times_from_end=number_of_ahc_finds_for_fit,\n        match_time=match_time,\n        settling_timescale=settling_timescale,\n        exp_func_and_2_derivs=evaluated_fot_dict[\"Expansion\"],\n        exp_outer_bdry_func_and_2_derivs=evaluated_fot_dict[\n            \"ExpansionOuterBoundary\"\n        ],\n        rot_func_and_2_derivs=evaluated_fot_dict[\"Rotation\"],\n        trans_func_and_2_derivs=evaluated_fot_dict[\"Translation\"],\n    )\n\n    shape_coefs_at_different_times_for_fit = np.array(\n        shape_and_translation_coefs[0]\n    )\n    ahc_inertial_centers_for_fit = np.array(shape_and_translation_coefs[1])\n    ahc_times_for_fit_list = []\n    for i, time in enumerate(ahc_times[-number_of_ahc_finds_for_fit:]):\n        if time <= match_time:\n            ahc_times_for_fit_list.append(time)\n    ahc_times_for_fit = np.array(ahc_times_for_fit_list)\n\n    logger.debug(\"AhC times available: \" + str(ahc_times.shape[0]))\n    logger.debug(\n        \"AhC available time range: \"\n        + str(np.min(ahc_times))\n        + \" - \"\n        + str(np.max(ahc_times))\n    )\n    logger.debug(\"AhC times used: \" + str(ahc_times_for_fit.shape[0]))\n    logger.debug(\n        \"AhC used time range: \"\n        + str(np.min(ahc_times_for_fit))\n        + \" - \"\n        + str(np.max(ahc_times_for_fit))\n    )\n    logger.debug(\n        \"Coef times used: \"\n        + str(shape_coefs_at_different_times_for_fit.shape[0])\n    )\n\n    fit_ahc_coefs, fit_ahc_dt_coefs, fit_ahc_dt2_coefs = fit_to_a_cubic(\n        ahc_times_for_fit,\n        shape_coefs_at_different_times_for_fit,\n        match_time,\n        zero_coefs_eps,\n    )\n    ahc_translation_fot = fit_to_a_cubic(\n        ahc_times_for_fit,\n        ahc_inertial_centers_for_fit,\n        match_time,\n        zero_coefs_eps,\n    )\n\n    # Sets the AhC Strahlkorper center to the center point at the match time.\n    fit_ahc_coef_mv = ModalVector(fit_ahc_coefs)\n    fit_ahc_dt_coef_mv = ModalVector(fit_ahc_dt_coefs)\n    fit_ahc_dt2_coef_mv = ModalVector(fit_ahc_dt2_coefs)\n    fit_ahc_strahlkorper = Strahlkorper[Frame.Inertial](\n        ahc_lmax, ahc_lmax, fit_ahc_coef_mv, ahc_inertial_centers_for_fit[-1]\n    )\n    fit_ahc_dt_strahlkorper = Strahlkorper[Frame.Inertial](\n        ahc_lmax, ahc_lmax, fit_ahc_dt_coef_mv, ahc_inertial_centers_for_fit[-1]\n    )\n    fit_ahc_dt2_strahlkorper = Strahlkorper[Frame.Inertial](\n        ahc_lmax,\n        ahc_lmax,\n        fit_ahc_dt2_coef_mv,\n        ahc_inertial_centers_for_fit[-1],\n    )\n    legend_ahc, fit_ahc_ylm_coefs_to_write = ylm_legend_and_data(\n        fit_ahc_strahlkorper, match_time, ahc_lmax\n    )\n    legend_ahc_dt, fit_ahc_dt_ylm_coefs_to_write = ylm_legend_and_data(\n        fit_ahc_dt_strahlkorper, match_time, ahc_lmax\n    )\n    legend_ahc_dt2, fit_ahc_dt2_ylm_coefs_to_write = ylm_legend_and_data(\n        fit_ahc_dt2_strahlkorper, match_time, ahc_lmax\n    )\n\n    ringdown_ylm_coefs = [\n        fit_ahc_ylm_coefs_to_write,\n        fit_ahc_dt_ylm_coefs_to_write,\n        fit_ahc_dt2_ylm_coefs_to_write,\n    ]\n    ringdown_ylm_legend = [legend_ahc, legend_ahc_dt, legend_ahc_dt2]\n\n    for i in range(1, 4, 1):\n        legend_ahc[i] = legend_ahc[i].replace(\"Inertial\", \"Distorted\")\n        legend_ahc_dt[i] = legend_ahc_dt[i].replace(\"Inertial\", \"Distorted\")\n        legend_ahc_dt2[i] = legend_ahc_dt2[i].replace(\"Inertial\", \"Distorted\")\n\n    return (\n        ringdown_ylm_coefs,\n        ringdown_ylm_legend,\n        ahc_translation_fot,\n    )\n"
  },
  {
    "path": "src/Evolution/Ringdown/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/Evolution/Ringdown/StrahlkorperCoefsAndCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n#include \"Evolution/Ringdown/StrahlkorperCoefsAndCenters.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordsToDifferentFrame.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/StrahlkorperTransformations.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/ChangeCenterOfStrahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/ReadSurfaceYlm.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace evolution::Ringdown {\nstd::pair<std::vector<DataVector>, std::vector<std::array<double, 3>>>\nstrahlkorper_coefs_and_centers(\n    const std::string& path_to_volume_data,\n    const std::string& volume_subfile_name,\n    const std::string& path_to_horizons_h5,\n    const std::string& surface_subfile_name,\n    const size_t requested_number_of_times_from_end, const double match_time,\n    const double settling_timescale,\n    const std::optional<std::array<double, 3>>& exp_func_and_2_derivs,\n    const std::optional<std::array<double, 3>>&\n        exp_outer_bdry_func_and_2_derivs,\n    const std::optional<std::vector<std::array<double, 4>>>&\n        rot_func_and_2_derivs,\n    const std::optional<std::array<std::array<double, 3>, 3>>&\n        trans_func_and_2_derivs) {\n  // Read the AhC coefficients from the H5 file\n  const std::vector<ylm::Strahlkorper<Frame::Inertial>>& ahc_inertial_h5 =\n      ylm::read_surface_ylm<Frame::Inertial>(\n          path_to_horizons_h5, surface_subfile_name,\n          requested_number_of_times_from_end);\n  std::vector<double> ahc_times{};\n  {\n    // Read the AhC times from the H5 file\n    const h5::H5File<h5::AccessType::ReadOnly> ahc_h5_file{path_to_horizons_h5};\n    const auto& dat = ahc_h5_file.get<h5::Dat>(surface_subfile_name);\n    const Matrix& coefs_for_times = dat.get_data_subset(\n        {0}, dat.get_dimensions()[0] - requested_number_of_times_from_end,\n        ahc_inertial_h5.size());\n    for (size_t i = 0; i < coefs_for_times.rows(); ++i) {\n      ahc_times.push_back(coefs_for_times(i, 0));\n    }\n  }\n\n  // Create a time-dependent domain; only the the time-dependent map options\n  // matter; the domain is just a spherical shell with inner and outer\n  // radii chosen so any conceivable common horizon will fit between them.\n  const auto expansion_map_options =\n      exp_func_and_2_derivs.has_value()\n          ? domain::creators::time_dependent_options::ExpansionMapOptions<\n                true>{exp_func_and_2_derivs.value(), settling_timescale,\n                      exp_outer_bdry_func_and_2_derivs.value(),\n                      settling_timescale}\n          : std::optional<domain::creators::time_dependent_options::\n                              ExpansionMapOptions<true>>{};\n  const auto rotation_map_options =\n      rot_func_and_2_derivs.has_value()\n          ? domain::creators::time_dependent_options::RotationMapOptions<\n                true>{rot_func_and_2_derivs.value(), settling_timescale}\n          : std::optional<domain::creators::time_dependent_options::\n                              RotationMapOptions<true>>{};\n  const auto& translation_fot_from_volume =\n      trans_func_and_2_derivs.has_value()\n          ? domain::creators::time_dependent_options::FromVolumeFile(\n                path_to_volume_data, volume_subfile_name, true)\n          : std::optional<\n                domain::creators::time_dependent_options::FromVolumeFile>{};\n  const domain::creators::sphere::TimeDependentMapOptions\n      time_dependent_map_options{match_time,\n                                 std::nullopt,\n                                 rotation_map_options,\n                                 expansion_map_options,\n                                 translation_fot_from_volume,\n                                 true};\n  // We construct a temporary ringdown domain that will be used to transform the\n  // common horizon from the inspiral inertial frame to the temporary frame\n  // (ringdown intermediate frame in SpEC) for shape coefficients and then from\n  // the temporary frame to the inertial frame for geometric center positions\n  // for the ringdown translation map. We choose an inner radius at machine\n  // precision so that every point on any Strahlkorper transformed to this\n  // temporary ringdown domain will be mapped to a block. It's assumed that the\n  // Strahlkorper has not translated beyond 200M and has a radius < 200M in the\n  // inertial frame. The resolution on this domain does not matter because the\n  // actual grid points in the temporary ringdown domain are never used. We are\n  // only after how the shape coefficients and center changes as you transform\n  // the Strahlkorper to this temporary ringdown domain. The only member\n  // functions of the domain that are used are the coordinate maps (and\n  // associated functions of time) that map the \"grid frame\" of the temporary\n  // ringdown domain to the inertial frame of the temporary ringdown domain. The\n  // \"grid frame\" of the temporary ringdown domain is the inertial frame pushed\n  // through the inverse-rotation-expansion-translation map, where \"translation\"\n  // is the inspiral's translation map, and \"rotation\" and \"expansion\" are new\n  // rotation and expansion maps that match the inspiral's rotation and\n  // expansion at the outer boundary at the transition time, have some smooth\n  // falloff in the interior, and have a \"settle to constant\" time dependence.\n  // Thus the \"grid frame\" of the temporary domain is almost (except for an\n  // uncorrected translation map) the distorted frame of the ringdown. The\n  // \"almost\" is because in the \"grid frame\" of the temporary ringdown domain,\n  // the center of the excision boundary moves, and is not at the origin.\n  const domain::creators::Sphere domain_creator{\n      // Inner radius and outer radius chosen so that every point on a\n      // strahlkorper transformed to this domain will be mapped to a block.\n      std::numeric_limits<double>::epsilon(),\n      200.0,\n      // nullptr because no boundary condition\n      domain::creators::Sphere::Excision{nullptr},\n      static_cast<size_t>(0),\n      static_cast<size_t>(5),\n      false,\n      std::nullopt,\n      // Radial partition used to separate Shell0/1 in the ringdown domain.\n      // This value is what is currently used in the Bbh pipeline\n      {50.0},\n      domain::CoordinateMaps::Distribution::Linear,\n      ShellWedges::All,\n      time_dependent_map_options};\n  const auto temporary_ringdown_domain = domain_creator.create_domain();\n  const auto ringdown_functions_of_time = domain_creator.functions_of_time();\n  // Loop over the selected horizons, transforming each to the\n  // temporary frame\n  std::vector<DataVector> ahc_ringdown_distorted_coefs{};\n  std::vector<std::array<double, 3>> ahc_inertial_centers{};\n  // Here we transform the inertial strahlkorper into the temporary frame. In\n  // order to do this, the inertial coords of the strahlkorper are mapped to the\n  // logical frame to determine which block map to use, and then into the\n  // temporary frame. This technically requires a shape map; however, we do not\n  // yet know the shape map for the ringdown domain. Here, we use an identity\n  // shape map instead because we are only concerned with the temporary frame,\n  // not the correct ringdown grid frame. To avoid an unnecessary identity shape\n  // map, we omit it in the domain above. This now makes the grid frame of this\n  // temporary domain equivalent to the temporary frame. This is why we map the\n  // strahlkorper into the \"grid\" frame instead of the \"distorted\" frame. It is\n  // a simplification to avoid an unnecessary identity shape map.\n  ylm::Strahlkorper<Frame::Grid> distorted_ahc;\n  for (size_t i = 0; i < requested_number_of_times_from_end; ++i) {\n    if (gsl::at(ahc_times, i) <= match_time) {\n      strahlkorper_in_different_frame(\n          make_not_null(&distorted_ahc), gsl::at(ahc_inertial_h5, i),\n          temporary_ringdown_domain, ringdown_functions_of_time,\n          gsl::at(ahc_times, i));\n      // Relative tolerance is set to the value used in SpEC\n      ylm::change_expansion_center_of_strahlkorper_to_physical(\n          make_not_null(&distorted_ahc), expansion_center_tolerance);\n      ahc_ringdown_distorted_coefs.push_back(distorted_ahc.coefficients());\n\n      tnsr::I<DataVector, 3, ::Frame::Grid> grid_center_point{\n          DataVector{1, 0.0}};\n      grid_center_point[0] = distorted_ahc.expansion_center()[0];\n      grid_center_point[1] = distorted_ahc.expansion_center()[1];\n      grid_center_point[2] = distorted_ahc.expansion_center()[2];\n      tnsr::I<DataVector, 3, ::Frame::Inertial> inertial_center_point{\n          DataVector{1, 0.0}};\n      // The center point is mapped to the inertial frame so that the geometric\n      // center of AhC accounts for the inspiral's rotation, scaling and\n      // translation. This ensures that the center of the excision is at the\n      // correct location at the match time\n      coords_to_different_frame(make_not_null(&inertial_center_point),\n                                grid_center_point, temporary_ringdown_domain,\n                                ringdown_functions_of_time,\n                                gsl::at(ahc_times, i));\n\n      ahc_inertial_centers.push_back(std::array<double, 3>{\n          get<0>(inertial_center_point)[0], get<1>(inertial_center_point)[0],\n          get<2>(inertial_center_point)[0]});\n    }\n  }\n\n  return std::pair{ahc_ringdown_distorted_coefs, ahc_inertial_centers};\n}\n}  // namespace evolution::Ringdown\n"
  },
  {
    "path": "src/Evolution/Ringdown/StrahlkorperCoefsAndCenters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n\n/*!\n * \\brief Functionality for evolving a ringdown following a compact-binary\n * merger.\n */\nnamespace evolution::Ringdown {\n/*!\n * \\brief This function is used to transition from inspiral to ringdown. It\n * reads inertial frame common horizon Strahlkorper coefs from a file and\n * returns the Strahlkorper's temporary frame coefs and the inertial frame\n * geometric centers at multiple times which will be used to initialize the\n * shape and translation function of time for the ringdown.\n *\n * \\details Reads common horizon Strahlkorpers (assumed to be in the inertial\n * frame) from a file, then transforms them to a temporary ringdown domain\n * defined by the expansion, rotation, and translation maps from the inspiral\n * specified by `exp_func_and_2_derivs`, `exp_outer_bdry_func_and_2_derivs`,\n * `rot_func_and_2_derivs`, and `trans_func_and_2_derivs`. The expansion and\n * rotation functions of time from the inspiral are the same as the ringdown\n * frame's expansion and rotation maps at the given `match_time`, but later\n * settle to constant values by the given `settling_timescale`. The translation\n * function of time supplied from the inspiral is not the translation map we'll\n * use in the final ringdown, it is only used to correctly map the common\n * horizon's geometric center so that we can make the corrected translation map\n * for the final ringdown. How to correct the translation map is explained in\n * detail below. A shape map is not specified, because we do not yet know the\n * shape coefficients of the common horizon for the ringdown. There are 4 frames\n * we transition between to find the shape coefficients and correct the\n * translation map. The frames are as follows:\n *\n * $\\textbf{Inertial frame}$: This is the same for ringdown and inspiral, by\n * definition. It's the frame where we measure the waveforms, and it's the frame\n * in which we define the tensor components (e.g. the 'x' and 'y' in 'g_xy').\n *\n * $\\textbf{Ringdown Grid Frame}$: This is the frame where the ringdown excision\n * boundary is spherical and where the ringdown grid lives.\n *\n * $\\textbf{Temporary frame}$: The inertial frame pushed through the inverse\n * translation-rotation-expansion map, where \"translation\" is the inspiral's\n * translation map, and \"rotation\" and \"expansion\" are new rotation and\n * expansion maps that match the inspiral's rotation and expansion at the outer\n * boundary at the match time, have some smooth falloff in the interior, and\n * have a \"settle to constant\" time dependence. This frame is almost the\n * ringdown distorted frame, except for an uncorrected translation map which is\n * explained in detail below.\n *\n * $\\textbf{Ringdown distorted frame}$: This is the ringdown grid frame pushed\n * through the (final) forward distortion map. Or equivalently, this is the\n * inertial frame pushed through the inverse translation-rotation-expansion map,\n * where here \"translation\" means the final corrected translation map, not the\n * inspiral's translation map, and \"rotation\" and \"expansion\" are the same as in\n * the temporary frame.\n *\n * We get the shape coefficients by transforming the common horizon to the\n * temporary frame which is almost the ringdown distorted frame except for the\n * uncorrected translation map. The translation map is corrected by transforming\n * the common horizon from the temporary frame to the inertial frame and saving\n * the geometric center points at multiple times. We then take those geometric\n * center points and build the corrected translation function of time. This is\n * done because the center of the Strahlkorper in this temporary frame is NOT\n * the origin (this is the case whether or not you say Recenter=true when\n * transforming the Strahlkorper), but the distortion map does its distortion\n * about the origin.\n * Possible ways to account for this:\n *\n * 1) Put a translation map before the distortion map.\n *\n * 2) Change the center of the distortion map.  (then we need to live with\n * this during the ringdown).\n *\n * 3) Correct the current translation map so that the excision boundary maps to\n * the correct place.\n *\n * We choose 3). Section 6 of \\cite Hemberger2012jz explains in more\n * detail of how we initialize the shape/translation map, but idea is that in\n * Eq. (104), the horizon can be written as\n * \\f{equation}{\n * x^{\\bar{i}}_{AH} = x^{\\bar{i}}_{AHc} + n^{\\bar{i}} \\sum_{lm}S_{lm}\n * Y_{lm}(\\theta,\\phi)\n * \\f}\n * where $x^{\\bar{i}}_{AH}$ is the common horizon Strahklorper in the temporary\n * frame, $x^{\\bar{i}}_{AHc}$ is the expansion center of the Strahlkorper in the\n * temporary frame, which is time-dependent and not zero, $n^{\\bar{i}}$ is the\n * direction unit vector in the ($\\theta,\\phi$) direction relative to\n * $x^{\\bar{i}}_{AHc}$, $\\bar{i}$ is the index corresponding to the\n * temporary frame, and $S_{lm}$ are the Strahlkorper coefficients.\n * Now,\n * \\f{equation}{\n * x^i = T0^i + M^i_{\\bar{i}} x^{\\bar{i}}\n * \\f}\n * where $T0^i$ is the inspiral translation map, and $M^i_{\\bar{i}}$ is\n * scaling+rotation. Thus,\n * \\f{equation}{\n * x^i_AH = T0^i + M^i_{\\bar{i}} x^{\\bar{i}}_{AH}\n *        = T0^i + M^i_{\\bar{i}} x^{\\bar{i}}_{AHc} + M^i_{\\bar{i}} n^{\\bar{i}}\n *          \\sum_{lm} S_{lm} Y_{lm}\n * \\f}\n * Therefore if you define a new translation map $T^i$ as in Eq. (107)\n * (corrected translation map that will be used in the final ringdown)\n * $T^i = T0^i + M^i_{\\bar{i}} x^{\\bar{i}}_{AHc}$ (that is, you define $T^i$ to\n * be the same as $x^i_{AHc}$), then you can rewrite the relationship as\n * \\f{equation}{\n * x^{i}_{AH} = T^i + M^i_{\\bar{i}} \\sum_{lm} S_{lm} Y_{lm} n^{\\bar{i}}\n * \\f}\n * Therefore we use a new map $x^i = T^i + M^i_{\\bar{i}} x^{\\tilde{i}}$ where\n * $\\tilde{i}$ refers to the final ringdown distorted frame, $x^{\\tilde{i}}$ is\n * a new coordinate where $x^{\\tilde{i}}_{AHc} = 0$ and the coefficients of the\n * AH in the $x^{\\tilde{i}}$ frame can be used unchanged (except for a minus\n * sign) in the distortion map that connects $x^{i_{grid}}$ and $x^{\\tilde{i}}$.\n * This new map for $x^i$ ensures that the excision boundary is mapped to the\n * correct position.\n * \\note Only temporary frame common horizon coefs and inertial frame geometric\n * center points within `requested_number_of_times_from_end` times from the\n * final time are returned. The functions of time are computed by\n * ComputeRingdownShapeAndTranslationFoT.py using the output from this function\n */\nstd::pair<std::vector<DataVector>, std::vector<std::array<double, 3>>>\nstrahlkorper_coefs_and_centers(\n    const std::string& path_to_volume_data,\n    const std::string& volume_subfile_name,\n    const std::string& path_to_horizons_h5,\n    const std::string& surface_subfile_name,\n    size_t requested_number_of_times_from_end, double match_time,\n    double settling_timescale,\n    const std::optional<std::array<double, 3>>& exp_func_and_2_derivs =\n        std::nullopt,\n    const std::optional<std::array<double, 3>>&\n        exp_outer_bdry_func_and_2_derivs = std::nullopt,\n    const std::optional<std::vector<std::array<double, 4>>>&\n        rot_func_and_2_derivs = std::nullopt,\n    const std::optional<std::array<std::array<double, 3>, 3>>&\n        trans_func_and_2_derivs = std::nullopt);\n\n// Hardcoded value used in SpEC\nconstexpr double expansion_center_tolerance = 1e-8;\n}  // namespace evolution::Ringdown\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace Burgers::BoundaryConditions {\nBoundaryCondition::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\nvoid BoundaryCondition::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n}  // namespace Burgers::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace Burgers {\n/// \\brief Boundary conditions for the Burgers system\nnamespace BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace BoundaryConditions\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  DemandOutgoingCharSpeeds.cpp\n  Dirichlet.cpp\n  DirichletAnalytic.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  DemandOutgoingCharSpeeds.hpp\n  Dirichlet.hpp\n  DirichletAnalytic.hpp\n  Factory.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace Burgers::BoundaryConditions {\nDemandOutgoingCharSpeeds::DemandOutgoingCharSpeeds(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDemandOutgoingCharSpeeds::get_clone() const {\n  return std::make_unique<DemandOutgoingCharSpeeds>(*this);\n}\n\nvoid DemandOutgoingCharSpeeds::pup(PUP::er& p) { BoundaryCondition::pup(p); }\n\nstd::optional<std::string>\nDemandOutgoingCharSpeeds::dg_demand_outgoing_char_speeds(\n    const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 1, Frame::Inertial>&\n        outward_directed_normal_covector,\n    const Scalar<DataVector>& u) {\n  double min_speed = std::numeric_limits<double>::signaling_NaN();\n  if (face_mesh_velocity.has_value()) {\n    min_speed = min(get<0>(outward_directed_normal_covector) *\n                    (get(u) - get<0>(*face_mesh_velocity)));\n  } else {\n    min_speed = min(get<0>(outward_directed_normal_covector) * get(u));\n  }\n  if (min_speed < 0.0) {\n    return {MakeString{} << \"DemandOutgoingCharSpeeds boundary condition \"\n                            \"violated with speed U ingoing: \"\n                         << min_speed << \"\\nU: \" << u << \"\\nn_i: \"\n                         << outward_directed_normal_covector << \"\\n\"};\n  }\n  return std::nullopt;\n}\n\nvoid DemandOutgoingCharSpeeds::fd_demand_outgoing_char_speeds(\n    const gsl::not_null<Scalar<DataVector>*> u, const Direction<1>& direction,\n    const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 1, Frame::Inertial>&\n        outward_directed_normal_covector,\n    const Scalar<DataVector>& u_interior, const Mesh<1>& subcell_mesh) {\n  // The boundary condition here simply uses the outermost values on\n  // cell-centered FD grid points to compute face values on the external\n  // boundary. This is equivalent to adopting the piecewise constant FD\n  // reconstruction for FD cells at the external boundaries.\n  //\n  const double u_val_at_boundary = get(\n      u_interior)[direction.side() == Side::Upper ? subcell_mesh.extents(0) - 1\n                                                  : 0];\n\n  double min_char_speed = std::numeric_limits<double>::signaling_NaN();\n  if (face_mesh_velocity.has_value()) {\n    min_char_speed = min(get<0>(outward_directed_normal_covector) *\n                         (u_val_at_boundary - get<0>(*face_mesh_velocity)));\n  } else {\n    min_char_speed =\n        min(get<0>(outward_directed_normal_covector) * u_val_at_boundary);\n  }\n  if (min_char_speed < 0.0) {\n    ERROR(\n        \"DemandOutgoingCharSpeeds boundary condition (subcell) violated with \"\n        \"speed U ingoing:\"\n        << min_char_speed << \"\\nU: \" << u_val_at_boundary\n        << \"\\nn_i: \" << outward_directed_normal_covector << \"\\n\");\n  } else {\n    // Once the DemandOutgoingCharSpeeds condition has been checked, we fill the\n    // ghost data with the boundary values. This does not mirror the data across\n    // the boundary and so is quite low-order.\n    //\n    // The reason that we need this step is to prevent floating point exceptions\n    // being raised while computing the subcell time derivative because of NaN\n    // or uninitialized values in ghost data.\n\n    get(*u) = u_val_at_boundary;\n  }\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DemandOutgoingCharSpeeds::my_PUP_ID = 0;\n}  // namespace Burgers::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/DemandOutgoingCharSpeeds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace Burgers::BoundaryConditions {\n/*!\n * \\brief A boundary condition that only verifies that all characteristic speeds\n * are directed out of the domain; no boundary data is altered by this boundary\n * condition.\n */\nclass DemandOutgoingCharSpeeds final : public BoundaryCondition {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A boundary condition that only verifies the characteristic speeds \"\n      \"are all directed out of the domain.\"};\n\n  DemandOutgoingCharSpeeds() = default;\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<Tags::U>;\n  using dg_interior_temporary_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 1, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& u);\n\n  using fd_interior_evolved_variables_tags = tmpl::list<Tags::U>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<1>>;\n  using fd_gridless_tags = tmpl::list<>;\n\n  static void fd_demand_outgoing_char_speeds(\n      gsl::not_null<Scalar<DataVector>*> u, const Direction<1>& direction,\n      const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 1, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& u_interior, const Mesh<1>& subcell_mesh);\n};\n}  // namespace Burgers::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/Dirichlet.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Dirichlet.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Burgers::BoundaryConditions {\nDirichlet::Dirichlet(const double u_value) : u_value_(u_value) {}\n\nDirichlet::Dirichlet(CkMigrateMessage* const msg) : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichlet::get_clone() const {\n  return std::make_unique<Dirichlet>(*this);\n}\n\nvoid Dirichlet::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | u_value_;\n}\n\nstd::optional<std::string> Dirichlet::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> u,\n    const gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux_u,\n    const std::optional<\n        tnsr::I<DataVector, 1, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 1, Frame::Inertial>& /*normal_covector*/) const {\n  get(*u) = u_value_;\n  Burgers::Fluxes::apply(flux_u, *u);\n  return {};\n}\n\nvoid Dirichlet::fd_ghost(const gsl::not_null<Scalar<DataVector>*> u,\n                         const Direction<1>& /*direction*/) const {\n  get(*u) = u_value_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Dirichlet::my_PUP_ID = 0;\n}  // namespace Burgers::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/Dirichlet.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <typename>\nclass Variables;\n/// \\endcond\n\nnamespace Burgers::BoundaryConditions {\n/*\n * \\brief Dirichlet boundary condition setting the value of U to a\n * time-independent constant.\n */\nclass Dirichlet final : public BoundaryCondition {\n private:\n  using flux_tag =\n      ::Tags::Flux<Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>;\n\n public:\n  struct U {\n    using type = double;\n    static constexpr Options::String help{\"The value for U on the boundary\"};\n  };\n\n  using options = tmpl::list<U>;\n  static constexpr Options::String help{\n      \"Dirichlet boundary condition setting the value of U to \"\n      \"a time-independent constant.\"};\n\n  Dirichlet(double u_value);\n\n  Dirichlet() = default;\n  Dirichlet(Dirichlet&&) = default;\n  Dirichlet& operator=(Dirichlet&&) = default;\n  Dirichlet(const Dirichlet&) = default;\n  Dirichlet& operator=(const Dirichlet&) = default;\n  ~Dirichlet() override = default;\n\n  explicit Dirichlet(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(BoundaryCondition, Dirichlet);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<Scalar<DataVector>*> u,\n      gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux_u,\n      const std::optional<\n          tnsr::I<DataVector, 1, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, 1, Frame::Inertial>& /*normal_covector*/) const;\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags = tmpl::list<>;\n  using fd_gridless_tags = tmpl::list<>;\n\n  void fd_ghost(gsl::not_null<Scalar<DataVector>*> u,\n                const Direction<1>& /*direction*/) const;\n\n private:\n  void dg_ghost_impl(\n      gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux,\n      gsl::not_null<Scalar<DataVector>*> u) const;\n\n  double u_value_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace Burgers::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Burgers/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Factory.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n\nnamespace Burgers::BoundaryConditions {\nDirichletAnalytic::DirichletAnalytic(const DirichletAnalytic& rhs)\n    : BoundaryCondition{dynamic_cast<const BoundaryCondition&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\nDirichletAnalytic& DirichletAnalytic::operator=(const DirichletAnalytic& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\nDirichletAnalytic::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\nDirichletAnalytic::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic::get_clone() const {\n  return std::make_unique<DirichletAnalytic>(*this);\n}\n\nstd::optional<std::string> DirichletAnalytic::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> u,\n    const gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux_u,\n    const std::optional<\n        tnsr::I<DataVector, 1, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 1, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, 1, Frame::Inertial>& coords,\n    [[maybe_unused]] const double time) const {\n  call_with_dynamic_type<void, tmpl::append<Burgers::Solutions::all_solutions,\n                                            Burgers::AnalyticData::all_data>>(\n      analytic_prescription_.get(),\n      [&coords, &time, &u](const auto* const analytic_solution_or_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*analytic_solution_or_data)>>) {\n          *u = get<Burgers::Tags::U>(analytic_solution_or_data->variables(\n              coords, time, tmpl::list<Burgers::Tags::U>{}));\n        } else {\n          *u = get<Burgers::Tags::U>(analytic_solution_or_data->variables(\n              coords, tmpl::list<Burgers::Tags::U>{}));\n          (void)time;\n        }\n      });\n  flux_impl(flux_u, *u);\n  return {};\n}\n\nvoid DirichletAnalytic::fd_ghost(\n    const gsl::not_null<Scalar<DataVector>*> u, const Direction<1>& direction,\n    const Mesh<1> subcell_mesh, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const ElementMap<1, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 1>&\n        grid_to_inertial_map,\n    const fd::Reconstructor& reconstructor) const {\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n\n  const auto ghost_logical_coords =\n      evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n          subcell_mesh, ghost_zone_size, direction);\n\n  const auto ghost_inertial_coords = grid_to_inertial_map(\n      logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n  call_with_dynamic_type<void, tmpl::append<Burgers::Solutions::all_solutions,\n                                            Burgers::AnalyticData::all_data>>(\n      analytic_prescription_.get(),\n      [&ghost_inertial_coords, &time,\n       &u](const auto* const analytic_solution_or_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*analytic_solution_or_data)>>) {\n          *u = get<Burgers::Tags::U>(analytic_solution_or_data->variables(\n              ghost_inertial_coords, time, tmpl::list<Burgers::Tags::U>{}));\n        } else {\n          *u = get<Burgers::Tags::U>(analytic_solution_or_data->variables(\n              ghost_inertial_coords, tmpl::list<Burgers::Tags::U>{}));\n          (void)time;\n        }\n      });\n}\n\nvoid DirichletAnalytic::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | analytic_prescription_;\n}\n\nvoid DirichletAnalytic::flux_impl(\n    const gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux,\n    const Scalar<DataVector>& u_analytic) {\n  Burgers::Fluxes::apply(flux, u_analytic);\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic::my_PUP_ID = 0;\n}  // namespace Burgers::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Burgers::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\nclass DirichletAnalytic final : public BoundaryCondition {\n private:\n  using flux_tag =\n      ::Tags::Flux<Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>;\n\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n\n  using options = tmpl::list<AnalyticPrescription>;\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions setting the value of U to \"\n      \"the analytic solution or analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<1, Frame::Inertial>>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> u,\n      const gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux_u,\n      const std::optional<\n          tnsr::I<DataVector, 1, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, 1, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, 1, Frame::Inertial>& coords,\n      [[maybe_unused]] const double time) const;\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<1>>;\n  using fd_gridless_tags =\n      tmpl::list<::Tags::Time, domain::Tags::FunctionsOfTime,\n                 domain::Tags::ElementMap<1, Frame::Grid>,\n                 domain::CoordinateMaps::Tags::CoordinateMap<1, Frame::Grid,\n                                                             Frame::Inertial>,\n                 fd::Tags::Reconstructor>;\n\n  void fd_ghost(const gsl::not_null<Scalar<DataVector>*> u,\n                const Direction<1>& direction, const Mesh<1> subcell_mesh,\n                const double time,\n                const std::unordered_map<\n                    std::string,\n                    std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n                    functions_of_time,\n                const ElementMap<1, Frame::Grid>& logical_to_grid_map,\n                const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial,\n                                                1>& grid_to_inertial_map,\n                const fd::Reconstructor& reconstructor) const;\n\n private:\n  static void flux_impl(\n      gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux,\n      const Scalar<DataVector>& u_analytic);\n\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace Burgers::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Dirichlet.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\nusing standard_boundary_conditions =\n    tmpl::list<DemandOutgoingCharSpeeds, Dirichlet, DirichletAnalytic,\n               domain::BoundaryConditions::Periodic<BoundaryCondition>>;\n}  // namespace Burgers::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Hll.cpp\n  Rusanov.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Hll.hpp\n  NamespaceDocs.hpp\n  Rusanov.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::BoundaryCorrections {\nusing standard_boundary_corrections = tmpl::list<Hll, Rusanov>;\n}  // namespace Burgers::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryCorrections/Hll.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Hll.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Burgers::BoundaryCorrections {\nHll::Hll(CkMigrateMessage* msg) : BoundaryCorrection(msg) {}\n\nstd::unique_ptr<evolution::BoundaryCorrection> Hll::get_clone() const {\n  return std::make_unique<Hll>(*this);\n}\n\nvoid Hll::pup(PUP::er& p) { BoundaryCorrection::pup(p); }\n\ndouble Hll::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_u,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_u,\n    const gsl::not_null<Scalar<DataVector>*> packaged_char_speed,\n    const Scalar<DataVector>& u,\n    const tnsr::I<DataVector, 1, Frame::Inertial>& flux_u,\n    const tnsr::i<DataVector, 1, Frame::Inertial>& normal_covector,\n    const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) {\n  get(*packaged_char_speed) = sign(get<0>(normal_covector)) * get(u);\n  if (normal_dot_mesh_velocity.has_value()) {\n    get(*packaged_char_speed) -= get(*normal_dot_mesh_velocity);\n  }\n  *packaged_u = u;\n  normal_dot_flux(packaged_normal_dot_flux_u, normal_covector, flux_u);\n  return max(abs(get(*packaged_char_speed)));\n}\n\nvoid Hll::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_u,\n    const Scalar<DataVector>& u_int,\n    const Scalar<DataVector>& normal_dot_flux_u_int,\n    const Scalar<DataVector>& char_speed_int, const Scalar<DataVector>& u_ext,\n    const Scalar<DataVector>& normal_dot_flux_u_ext,\n    const Scalar<DataVector>& char_speed_ext,\n    const dg::Formulation dg_formulation) {\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>>> temps{\n      get(u_int).size()};\n  get(get<::Tags::TempScalar<0>>(temps)) =\n      min(0.0, get(char_speed_int), -get(char_speed_ext));\n  get(get<::Tags::TempScalar<1>>(temps)) =\n      max(0.0, get(char_speed_int), -get(char_speed_ext));\n  const DataVector& lambda_min = get(get<::Tags::TempScalar<0>>(temps));\n  const DataVector& lambda_max = get(get<::Tags::TempScalar<1>>(temps));\n\n  get(*boundary_correction_u) =\n      ((lambda_max * get(normal_dot_flux_u_int) +\n        lambda_min * get(normal_dot_flux_u_ext)) +\n       lambda_max * lambda_min * (get(u_ext) - get(u_int))) /\n      (lambda_max - lambda_min);\n  if (dg_formulation == dg::Formulation::StrongInertial) {\n    get(*boundary_correction_u) -= get(normal_dot_flux_u_int);\n  }\n}\n}  // namespace Burgers::BoundaryCorrections\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Burgers::BoundaryCorrections::Hll::my_PUP_ID = 0;\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryCorrections/Hll.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Burgers::BoundaryCorrections {\n/*!\n * \\brief An HLL (Harten-Lax-van Leer) Riemann solver\n *\n * Let \\f$U\\f$ be the evolved variable, \\f$F^i\\f$ the flux, and \\f$n_i\\f$ be\n * the outward directed unit normal to the interface. Denoting \\f$F := n_i\n * F^i\\f$, the HLL boundary correction is \\cite Harten1983\n *\n * \\f{align*}\n * G_\\text{HLL} = \\frac{\\lambda_\\text{max} F_\\text{int} +\n * \\lambda_\\text{min} F_\\text{ext}}{\\lambda_\\text{max} - \\lambda_\\text{min}}\n * - \\frac{\\lambda_\\text{min}\\lambda_\\text{max}}{\\lambda_\\text{max} -\n * \\lambda_\\text{min}} \\left(U_\\text{int} - U_\\text{ext}\\right)\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior, and \\cite Davis1988\n *\n * \\f{align*}\n * \\lambda_\\text{min} &= \\text{min}\\left(U_\\text{int},-U_\\text{ext}, 0\\right) \\\\\n * \\lambda_\\text{max} &= \\text{max}\\left(U_\\text{int},-U_\\text{ext}, 0\\right),\n * \\f}\n *\n * The positive sign in front of the \\f$F_{\\text{ext}}\\f$ in \\f$G_\\text{HLL}\\f$\n * and the minus signs in front of the \\f$U_\\text{ext}\\f$ terms in\n * \\f$\\lambda_\\text{min}\\f$ and \\f$\\lambda_\\text{max}\\f$ terms are necessary\n * because the outward directed normal of the neighboring element has the\n * opposite sign, i.e. \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$. Note that for\n * either \\f$\\lambda_\\text{min} = 0\\f$ or \\f$\\lambda_\\text{max} = 0\\f$ (i.e. all\n * characteristics move in the same direction) the HLL boundary correction\n * reduces to pure upwinding.\n *\n * \\note\n * - In the strong form the `dg_boundary_terms` function returns\n * \\f$G - F_\\text{int}\\f$\n * - Some references use \\f$S\\f$ instead of \\f$\\lambda\\f$ for the\n * signal/characteristic speeds\n */\nclass Hll final : public evolution::BoundaryCorrection {\n private:\n  struct CharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the HLL boundary correction term for the Burgers system.\"};\n\n  Hll() = default;\n  Hll(const Hll&) = default;\n  Hll& operator=(const Hll&) = default;\n  Hll(Hll&&) = default;\n  Hll& operator=(Hll&&) = default;\n  ~Hll() override = default;\n\n  /// \\cond\n  explicit Hll(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Hll);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::U, ::Tags::NormalDotFlux<Tags::U>, CharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  static double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_u,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux,\n      gsl::not_null<Scalar<DataVector>*> packaged_char_speed,\n      const Scalar<DataVector>& u,\n      const tnsr::I<DataVector, 1, Frame::Inertial>& flux_u,\n      const tnsr::i<DataVector, 1, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity);\n\n  static void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_u,\n      const Scalar<DataVector>& u_int,\n      const Scalar<DataVector>& normal_dot_flux_u_int,\n      const Scalar<DataVector>& abs_char_speed_int,\n      const Scalar<DataVector>& u_ext,\n      const Scalar<DataVector>& normal_dot_flux_u_ext,\n      const Scalar<DataVector>& abs_char_speed_ext,\n      dg::Formulation dg_formulation);\n};\n}  // namespace Burgers::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace Burgers::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Burgers::BoundaryCorrections {\nRusanov::Rusanov(CkMigrateMessage* msg) : BoundaryCorrection(msg) {}\n\nstd::unique_ptr<evolution::BoundaryCorrection> Rusanov::get_clone() const {\n  return std::make_unique<Rusanov>(*this);\n}\n\nvoid Rusanov::pup(PUP::er& p) { BoundaryCorrection::pup(p); }\n\ndouble Rusanov::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_u,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_u,\n    const gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n    const Scalar<DataVector>& u,\n    const tnsr::I<DataVector, 1, Frame::Inertial>& flux_u,\n    const tnsr::i<DataVector, 1, Frame::Inertial>& normal_covector,\n    const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) {\n  if (normal_dot_mesh_velocity.has_value()) {\n    get(*packaged_abs_char_speed) =\n        abs(get(u) - get(*normal_dot_mesh_velocity));\n  } else {\n    get(*packaged_abs_char_speed) = abs(get(u));\n  }\n  *packaged_u = u;\n  normal_dot_flux(packaged_normal_dot_flux_u, normal_covector, flux_u);\n  return max(get(*packaged_abs_char_speed));\n}\n\nvoid Rusanov::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_u,\n    const Scalar<DataVector>& u_int,\n    const Scalar<DataVector>& normal_dot_flux_u_int,\n    const Scalar<DataVector>& abs_char_speed_int,\n    const Scalar<DataVector>& u_ext,\n    const Scalar<DataVector>& normal_dot_flux_u_ext,\n    const Scalar<DataVector>& abs_char_speed_ext,\n    const dg::Formulation dg_formulation) {\n  if (dg_formulation == dg::Formulation::WeakInertial) {\n    get(*boundary_correction_u) =\n        0.5 * (get(normal_dot_flux_u_int) - get(normal_dot_flux_u_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(u_ext) - get(u_int));\n  } else {\n    get(*boundary_correction_u) =\n        -0.5 * (get(normal_dot_flux_u_int) + get(normal_dot_flux_u_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(u_ext) - get(u_int));\n  }\n}\n}  // namespace Burgers::BoundaryCorrections\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Burgers::BoundaryCorrections::Rusanov::my_PUP_ID = 0;\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Burgers::BoundaryCorrections {\n/*!\n * \\brief A Rusanov/local Lax-Friedrichs Riemann solver\n *\n * Let \\f$U\\f$ be the evolved variable, \\f$F^i\\f$ the flux, and \\f$n_i\\f$ be\n * the outward directed unit normal to the interface. Denoting \\f$F := n_i\n * F^i\\f$, the %Rusanov boundary correction is\n *\n * \\f{align*}\n * G_\\text{Rusanov} = \\frac{F_\\text{int} - F_\\text{ext}}{2} -\n * \\frac{\\text{max}\\left(|U_\\text{int}|, |U_\\text{ext}|\\right)}{2}\n * \\left(U_\\text{ext} - U_\\text{int}\\right),\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior. The minus sign in\n * front of the \\f$F_{\\text{ext}}\\f$ is necessary because the outward directed\n * normal of the neighboring element has the opposite sign, i.e.\n * \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$.\n *\n * \\note In the strong form the `dg_boundary_terms` function returns\n * \\f$G - F_\\text{int}\\f$\n */\nclass Rusanov final : public evolution::BoundaryCorrection {\n private:\n  struct AbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the Rusanov or local Lax-Friedrichs boundary correction term \"\n      \"for the Burgers system.\"};\n\n  Rusanov() = default;\n  Rusanov(const Rusanov&) = default;\n  Rusanov& operator=(const Rusanov&) = default;\n  Rusanov(Rusanov&&) = default;\n  Rusanov& operator=(Rusanov&&) = default;\n  ~Rusanov() override = default;\n\n  /// \\cond\n  explicit Rusanov(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Rusanov);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::U, ::Tags::NormalDotFlux<Tags::U>, AbsCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  static double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_u,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux,\n      gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n      const Scalar<DataVector>& u,\n      const tnsr::I<DataVector, 1, Frame::Inertial>& flux_u,\n      const tnsr::i<DataVector, 1, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity);\n\n  static void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_u,\n      const Scalar<DataVector>& u_int,\n      const Scalar<DataVector>& normal_dot_flux_u_int,\n      const Scalar<DataVector>& abs_char_speed_int,\n      const Scalar<DataVector>& u_ext,\n      const Scalar<DataVector>& normal_dot_flux_u_ext,\n      const Scalar<DataVector>& abs_char_speed_ext,\n      dg::Formulation dg_formulation);\n};\n}  // namespace Burgers::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Burgers)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Characteristics.cpp\n  Fluxes.cpp\n  TimeDerivativeTerms.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Characteristics.hpp\n  Fluxes.hpp\n  System.hpp\n  Tags.hpp\n  TimeDerivativeTerms.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DgSubcell\n  DiscontinuousGalerkin\n  Domain\n  DomainBoundaryConditions\n  ErrorHandling\n  Events\n  Evolution\n  FiniteDifference\n  Options\n  Parallel\n  Serialization\n  Utilities\n  PRIVATE\n  BurgersAnalyticData\n  BurgersSolutions\n  LinearOperators\n  )\n\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Instantiations)\nadd_subdirectory(Subcell)\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/Characteristics.hpp\"\n\n#include <array>\n#include <ostream>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace Burgers::Tags {\nvoid CharacteristicSpeedsCompute::function(\n    const gsl::not_null<return_type*> result, const Scalar<DataVector>& u,\n    const tnsr::i<DataVector, 1>& normal) {\n  ASSERT(get(u).size() == 1 and get<0>(normal).size() == 1,\n         \"Char speeds only written for 1d boundaries.  \"\n         \"Got wrong number of points: \"\n         << get(u).size() << \" and \" << get<0>(normal).size());\n  gsl::at(*result, 0) = get<0>(normal)[0] > 0.0 ? get(u) : -get(u);\n}\n\nvoid ComputeLargestCharacteristicSpeed::function(\n    const gsl::not_null<double*> speed, const Scalar<DataVector>& u) {\n  *speed = max(abs(get(u)));\n}\n}  // namespace Burgers::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace Burgers {\nnamespace Tags {\nstruct U;\n}  // namespace Tags\n}  // namespace Burgers\n/// \\endcond\n\nnamespace Burgers::Tags {\n/// Computes the characteristic speeds\nstruct CharacteristicSpeedsCompute : CharacteristicSpeeds, db::ComputeTag {\n  using base = CharacteristicSpeeds;\n  using argument_tags =\n      tmpl::list<Tags::U, domain::Tags::UnnormalizedFaceNormal<1>>;\n  using return_type = std::array<DataVector, 1>;\n  static void function(gsl::not_null<return_type*> result,\n                       const Scalar<DataVector>& u,\n                       const tnsr::i<DataVector, 1>& normal);\n};\n\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\nstruct ComputeLargestCharacteristicSpeed : LargestCharacteristicSpeed,\n                                           db::ComputeTag {\n  using argument_tags = tmpl::list<Tags::U>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(const gsl::not_null<double*> speed,\n                       const Scalar<DataVector>& u);\n};\n}  // namespace Burgers::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/BoundaryConditionGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::fd {\n/*!\n * \\brief Computes finite difference ghost data for external boundary\n * conditions.\n *\n * If the element is at the external boundary, computes FD ghost data with a\n * given boundary condition and stores it into neighbor data with {direction,\n * ElementId::external_boundary_id()} as the mortar_id key.\n *\n * \\note Subcell needs to be enabled for boundary elements (see\n * Burgers::subcell::TimeDerivative). Otherwise this function would be never\n * called.\n *\n */\nstruct BoundaryConditionGhostData {\n  template <typename DbTagsList>\n  static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box,\n                    const Element<1>& element,\n                    const Burgers::fd::Reconstructor& reconstructor);\n\n private:\n  template <typename FdBoundaryConditionHelper, typename DbTagsList,\n            typename... FdBoundaryConditionArgsTags>\n  // A helper function for calling fd_ghost() of BoundaryCondition subclasses\n  static void apply_subcell_boundary_condition_impl(\n      FdBoundaryConditionHelper& fd_boundary_condition_helper,\n      const gsl::not_null<db::DataBox<DbTagsList>*>& box,\n      tmpl::list<FdBoundaryConditionArgsTags...>) {\n    return fd_boundary_condition_helper(\n        db::get<FdBoundaryConditionArgsTags>(*box)...);\n  }\n};\n\ntemplate <typename DbTagsList>\nvoid BoundaryConditionGhostData::apply(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    const Element<1>& element,\n    const Burgers::fd::Reconstructor& reconstructor) {\n  const auto& external_boundary_condition =\n      db::get<domain::Tags::ExternalBoundaryConditions<1>>(*box).at(\n          element.id().block_id());\n\n  // Check if the element is on the external boundary. If not, the caller is\n  // doing something wrong (e.g. trying to compute FD ghost data with boundary\n  // conditions at an element which is not on the external boundary).\n  ASSERT(not element.external_boundaries().empty(),\n         \"The element (ID : \" << element.id()\n                              << \") is not on external boundaries\");\n\n  const Mesh<1> subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<1>>(*box);\n\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n\n  for (const auto& direction : element.external_boundaries()) {\n    const auto& boundary_condition_at_direction =\n        *external_boundary_condition.at(direction);\n\n    const size_t num_face_pts{\n        subcell_mesh.extents().slice_away(direction.dimension()).product()};\n\n    // a Variables object to store the computed FD ghost data\n    Variables<tmpl::list<Burgers::Tags::U>> ghost_data_vars{ghost_zone_size *\n                                                            num_face_pts};\n\n    // We don't need to care about boundary ghost data when using the periodic\n    // condition, so exclude it from the type list\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>::factory_creation::factory_classes;\n    using derived_boundary_conditions_for_subcell = tmpl::remove_if<\n        tmpl::at<factory_classes,\n                 typename Burgers::System::boundary_conditions_base>,\n        tmpl::or_<\n            std::is_base_of<domain::BoundaryConditions::MarkAsPeriodic,\n                            tmpl::_1>,\n            std::is_base_of<domain::BoundaryConditions::MarkAsNone, tmpl::_1>>>;\n\n    // Now apply subcell boundary conditions\n    call_with_dynamic_type<void, derived_boundary_conditions_for_subcell>(\n        &boundary_condition_at_direction,\n        [&box, &direction, &ghost_data_vars](const auto* boundary_condition) {\n          using BoundaryCondition = std::decay_t<decltype(*boundary_condition)>;\n          using bcondition_interior_evolved_vars_tags =\n              typename BoundaryCondition::fd_interior_evolved_variables_tags;\n          using bcondition_interior_temporary_tags =\n              typename BoundaryCondition::fd_interior_temporary_tags;\n          using bcondition_gridless_tags =\n              typename BoundaryCondition::fd_gridless_tags;\n\n          using bcondition_interior_tags =\n              tmpl::append<bcondition_interior_evolved_vars_tags,\n                           bcondition_interior_temporary_tags,\n                           bcondition_gridless_tags>;\n\n          if constexpr (BoundaryCondition::bc_type ==\n                        evolution::BoundaryConditions::Type::Ghost) {\n            const auto apply_fd_ghost =\n                [&boundary_condition, &direction,\n                 &ghost_data_vars](const auto&... boundary_ghost_data_args) {\n                  (*boundary_condition)\n                      .fd_ghost(make_not_null(\n                                    &get<Burgers::Tags::U>(ghost_data_vars)),\n                                direction, boundary_ghost_data_args...);\n                };\n            apply_subcell_boundary_condition_impl(apply_fd_ghost, box,\n                                                  bcondition_interior_tags{});\n          } else if constexpr (BoundaryCondition::bc_type ==\n                               evolution::BoundaryConditions::Type::\n                                   DemandOutgoingCharSpeeds) {\n            // This boundary condition only checks if all the characteristic\n            // speeds are directed outward.\n            const auto& volume_mesh_velocity =\n                db::get<domain::Tags::MeshVelocity<1, Frame::Inertial>>(*box);\n            if (volume_mesh_velocity.has_value()) {\n              ERROR(\"Subcell currently does not support moving mesh\");\n            }\n\n            std::optional<tnsr::I<DataVector, 1>> face_mesh_velocity{};\n\n            const auto& normal_covector_and_magnitude =\n                db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<1>>(\n                    *box);\n            const auto& outward_directed_normal_covector =\n                get<evolution::dg::Tags::NormalCovector<1>>(\n                    normal_covector_and_magnitude.at(direction).value());\n            const auto apply_fd_demand_outgoing_char_speeds =\n                [&boundary_condition, &direction, &face_mesh_velocity,\n                 &ghost_data_vars, &outward_directed_normal_covector](\n                    const auto&... boundary_ghost_data_args) {\n                  return (*boundary_condition)\n                      .fd_demand_outgoing_char_speeds(\n                          make_not_null(\n                              &get<Burgers::Tags::U>(ghost_data_vars)),\n                          direction, face_mesh_velocity,\n                          outward_directed_normal_covector,\n                          boundary_ghost_data_args...);\n                };\n            apply_subcell_boundary_condition_impl(\n                apply_fd_demand_outgoing_char_speeds, box,\n                bcondition_interior_tags{});\n\n            return;\n          } else {\n            ERROR(\"Unsupported boundary condition \"\n                  << pretty_type::short_name<BoundaryCondition>()\n                  << \" when using finite-difference\");\n          }\n        });\n\n    // Put the computed ghost data into neighbor data with {direction,\n    // ElementId::external_boundary_id()} as the mortar_id key\n    const DirectionalId<1> mortar_id{direction,\n                                     ElementId<1>::external_boundary_id()};\n\n    db::mutate<evolution::dg::subcell::Tags::GhostDataForReconstruction<1>>(\n        [&mortar_id, &ghost_data_vars](auto ghost_data_ptr) {\n          (*ghost_data_ptr)[mortar_id] = evolution::dg::subcell::GhostData{1};\n          DataVector& neighbor_data =\n              ghost_data_ptr->at(mortar_id)\n                  .neighbor_ghost_data_for_reconstruction();\n          neighbor_data.destructive_resize(ghost_data_vars.size());\n          std::copy(get(get<Burgers::Tags::U>(ghost_data_vars)).begin(),\n                    get(get<Burgers::Tags::U>(ghost_data_vars)).end(),\n                    neighbor_data.begin());\n        },\n        box);\n  }\n}\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  MonotonisedCentral.cpp\n  Reconstructor.cpp\n  RegisterDerivedWithCharm.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryConditionGhostData.hpp\n  Factory.hpp\n  FiniteDifference.hpp\n  MonotonisedCentral.hpp\n  Reconstructor.hpp\n  ReconstructWork.hpp\n  ReconstructWork.tpp\n  RegisterDerivedWithCharm.hpp\n  Tags.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Burgers/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/FiniteDifference.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Burgers {\n/*!\n * \\brief Finite difference functionality for Burgers system\n */\nnamespace fd {}\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/FiniteDifference/MonotonisedCentral.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::fd {\nMonotonisedCentral::MonotonisedCentral(CkMigrateMessage* const msg)\n    : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor> MonotonisedCentral::get_clone() const {\n  return std::make_unique<MonotonisedCentral>(*this);\n}\n\nvoid MonotonisedCentral::pup(PUP::er& p) { Reconstructor::pup(p); }\n\nPUP::able::PUP_ID MonotonisedCentral::my_PUP_ID = 0;\n\nvoid MonotonisedCentral::reconstruct(\n    const gsl::not_null<std::array<Variables<face_vars_tags>, 1>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<face_vars_tags>, 1>*>\n        vars_on_upper_face,\n    const Variables<tmpl::list<Burgers::Tags::U>>& volume_vars,\n    const Element<1>& element,\n    const DirectionalIdMap<1, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<1>& subcell_mesh) const {\n  reconstruct_work(\n      vars_on_lower_face, vars_on_upper_face,\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_variables, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::monotonised_central(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_variables,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      volume_vars, element, ghost_data, subcell_mesh, ghost_zone_size());\n}\n\nvoid MonotonisedCentral::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<face_vars_tags>*> vars_on_face,\n    const Variables<volume_vars_tags>& volume_vars, const Element<1>& element,\n    const DirectionalIdMap<1, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<1>& subcell_mesh,\n    const Direction<1> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work(\n      vars_on_face,\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor, const auto& subcell_extents,\n         const auto& ghost_data_extents,\n         const auto& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor, const auto& subcell_extents,\n         const auto& ghost_data_extents,\n         const auto& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      volume_vars, element, ghost_data, subcell_mesh, direction_to_reconstruct,\n      ghost_zone_size());\n}\n\nbool operator==(const MonotonisedCentral& /*lhs*/,\n                const MonotonisedCentral& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const MonotonisedCentral& lhs, const MonotonisedCentral& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/MonotonisedCentral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace Burgers::fd {\n/*!\n * \\brief Monotonised central reconstruction. See\n * ::fd::reconstruction::monotonised_central() for details.\n */\nclass MonotonisedCentral : public Reconstructor {\n private:\n  using face_vars_tags = tmpl::list<\n      Burgers::Tags::U,\n      ::Tags::Flux<Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>>;\n  using volume_vars_tags = tmpl::list<Burgers::Tags::U>;\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Monotonised central reconstruction scheme.\"};\n\n  MonotonisedCentral() = default;\n  MonotonisedCentral(MonotonisedCentral&&) = default;\n  MonotonisedCentral& operator=(MonotonisedCentral&&) = default;\n  MonotonisedCentral(const MonotonisedCentral&) = default;\n  MonotonisedCentral& operator=(const MonotonisedCentral&) = default;\n  ~MonotonisedCentral() override = default;\n\n  void pup(PUP::er& p) override;\n\n  /// \\cond\n  explicit MonotonisedCentral(CkMigrateMessage* msg);\n  WRAPPED_PUPable_decl_base_template(Reconstructor, MonotonisedCentral);\n  /// \\endcond\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  size_t ghost_zone_size() const override { return 2; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<volume_vars_tags>, domain::Tags::Element<1>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<1>,\n                 evolution::dg::subcell::Tags::Mesh<1>>;\n\n  void reconstruct(\n      gsl::not_null<std::array<Variables<face_vars_tags>, 1>*>\n          vars_on_lower_face,\n      gsl::not_null<std::array<Variables<face_vars_tags>, 1>*>\n          vars_on_upper_face,\n      const Variables<tmpl::list<Burgers::Tags::U>>& volume_vars,\n      const Element<1>& element,\n      const DirectionalIdMap<1, evolution::dg::subcell::GhostData>& ghost_data,\n      const Mesh<1>& subcell_mesh) const;\n\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<face_vars_tags>*> vars_on_face,\n      const Variables<volume_vars_tags>& volume_vars, const Element<1>& element,\n      const DirectionalIdMap<1, evolution::dg::subcell::GhostData>& ghost_data,\n      const Mesh<1>& subcell_mesh,\n      const Direction<1> direction_to_reconstruct) const;\n};\n\nbool operator==(const MonotonisedCentral& /*lhs*/,\n                const MonotonisedCentral& /*rhs*/);\n\nbool operator!=(const MonotonisedCentral& lhs, const MonotonisedCentral& rhs);\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/ReconstructWork.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace Burgers::fd {\n/*!\n * \\brief Reconstructs \\f$U\\f$. All results are written into\n * `vars_on_lower_face` and `vars_on_upper_face`.\n */\ntemplate <typename TagsList, typename Reconstructor>\nvoid reconstruct_work(\n    gsl::not_null<std::array<Variables<TagsList>, 1>*> vars_on_lower_face,\n    gsl::not_null<std::array<Variables<TagsList>, 1>*> vars_on_upper_face,\n    const Reconstructor& reconstruct,\n    const Variables<tmpl::list<Tags::U>> volume_vars, const Element<1>& element,\n    const DirectionalIdMap<1, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<1>& subcell_mesh, const size_t ghost_zone_size);\n\n/*!\n * \\brief Reconstructs \\f$U\\f$. All results are written into `vars_on_face`.\n *\n * This is used on DG elements to reconstruct their subcell neighbors' solution\n * on the shared faces.\n */\ntemplate <typename TagsList, typename ReconstructLower,\n          typename ReconstructUpper>\nvoid reconstruct_fd_neighbor_work(\n    gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const ReconstructLower& reconstruct_lower_neighbor,\n    const ReconstructUpper& reconstruct_upper_neighbor,\n    const Variables<tmpl::list<Tags::U>>& subcell_volume_vars,\n    const Element<1>& element,\n    const DirectionalIdMap<1, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<1>& subcell_mesh, const Direction<1>& direction_to_reconstruct,\n    const size_t ghost_zone_size);\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/ReconstructWork.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::fd {\ntemplate <typename TagsList, typename Reconstructor>\nvoid reconstruct_work(\n    const gsl::not_null<std::array<Variables<TagsList>, 1>*> vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, 1>*> vars_on_upper_face,\n    const Reconstructor& reconstruct,\n    const Variables<tmpl::list<Tags::U>> volume_vars, const Element<1>& element,\n    const DirectionalIdMap<1, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<1>& subcell_mesh, const size_t ghost_zone_size) {\n  const size_t volume_num_pts = subcell_mesh.number_of_grid_points();\n  const size_t reconstructed_num_pts = volume_num_pts + 1;\n\n  // create views/spans into the face data, which will be filled by the\n  // reconstructor\n  std::array<gsl::span<double>, 1> upper_face_vars{};\n  std::array<gsl::span<double>, 1> lower_face_vars{};\n  for (size_t i = 0; i < 1; i++) {\n    gsl::at(upper_face_vars, i) =\n        gsl::make_span(get<Tags::U>(gsl::at(*vars_on_upper_face, i))[0].data(),\n                       reconstructed_num_pts);\n    gsl::at(lower_face_vars, i) =\n        gsl::make_span(get<Tags::U>(gsl::at(*vars_on_lower_face, i))[0].data(),\n                       reconstructed_num_pts);\n  }\n\n  const size_t number_of_variables = 1;\n\n  // make span of ghost cell variables for each direction\n  DirectionMap<1, gsl::span<const double>> ghost_cell_vars{};\n\n  // for all Directions, make the pointers in ghost_cell_vars span to point\n  // the received neighbor data\n  for (const auto& direction : Direction<1>::all_directions()) {\n    if (element.neighbors().contains(direction)) {\n      const auto& neighbors_in_direction = element.neighbors().at(direction);\n\n      ASSERT(neighbors_in_direction.size() == 1,\n             \"Currently only support one neighbor in each direction, but \"\n             \"got \"\n                 << neighbors_in_direction.size() << \" in direction \"\n                 << direction);\n\n      const DataVector& neighbor_data =\n          ghost_data\n              .at(DirectionalId<1>{direction, *neighbors_in_direction.begin()})\n              .neighbor_ghost_data_for_reconstruction();\n\n      ASSERT(neighbor_data.size() != 0,\n             \"The neighbor data is empty in direction \"\n                 << direction << \" on element id \" << element.id());\n\n      ghost_cell_vars[direction] = gsl::make_span(\n          &neighbor_data[0], number_of_variables * ghost_zone_size);\n    } else {\n      // retrieve boundary ghost data from ghost_data\n      const DataVector& neighbor_data =\n          ghost_data\n              .at(DirectionalId<1>{direction,\n                                 ElementId<1>::external_boundary_id()})\n              .neighbor_ghost_data_for_reconstruction();\n      ghost_cell_vars[direction] = gsl::make_span(\n          &neighbor_data[0], number_of_variables * ghost_zone_size);\n    }\n  }\n\n  // make span of volume variables\n  auto& volume_tensor = get<Tags::U>(volume_vars);\n  const gsl::span<const double> volume_vars_span =\n      gsl::make_span((volume_tensor)[0].data(), volume_num_pts);\n\n  // perform reconstruction\n  reconstruct(make_not_null(&upper_face_vars), make_not_null(&lower_face_vars),\n              volume_vars_span, ghost_cell_vars, subcell_mesh.extents(), 1);\n}\n\ntemplate <typename TagsList, typename ReconstructLower,\n          typename ReconstructUpper>\nvoid reconstruct_fd_neighbor_work(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const ReconstructLower& reconstruct_lower_neighbor,\n    const ReconstructUpper& reconstruct_upper_neighbor,\n    const Variables<tmpl::list<Tags::U>>& subcell_volume_vars,\n    const Element<1>& element,\n    const DirectionalIdMap<1, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<1>& subcell_mesh, const Direction<1>& direction_to_reconstruct,\n    const size_t ghost_zone_size) {\n  const DirectionalId<1> mortar_id{\n      direction_to_reconstruct,\n      *element.neighbors().at(direction_to_reconstruct).begin()};\n\n  Index<1> ghost_data_extents = subcell_mesh.extents();\n  ghost_data_extents[direction_to_reconstruct.dimension()] = ghost_zone_size;\n\n  // allocate Variable for storing data from neighbor\n  Variables<tmpl::list<Tags::U>> neighbor_vars{ghost_data_extents.product()};\n\n  {\n    ASSERT(ghost_data.contains(mortar_id),\n           \"The neighbor data does not contain the mortar: \" << mortar_id);\n\n    const DataVector& neighbor_data_in_direction =\n        ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    std::copy(\n        neighbor_data_in_direction.begin(),\n        std::next(neighbor_data_in_direction.begin(),\n                  static_cast<std::ptrdiff_t>(ghost_data_extents.product())),\n        neighbor_vars.data());\n  }\n\n  const auto& tensor_volume = get<Tags::U>(subcell_volume_vars);\n  const auto& tensor_neighbor = get<Tags::U>(neighbor_vars);\n  auto& tensor_on_face = get<Tags::U>(*vars_on_face);\n\n  if (direction_to_reconstruct.side() == Side::Upper) {\n    for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n         ++tensor_index) {\n      reconstruct_upper_neighbor(\n          make_not_null(&tensor_on_face[tensor_index]),\n          tensor_volume[tensor_index], tensor_neighbor[tensor_index],\n          subcell_mesh.extents(), ghost_data_extents, direction_to_reconstruct);\n    }\n  } else {\n    for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n         ++tensor_index) {\n      reconstruct_lower_neighbor(\n          make_not_null(&tensor_on_face[tensor_index]),\n          tensor_volume[tensor_index], tensor_neighbor[tensor_index],\n          subcell_mesh.extents(), ghost_data_extents, direction_to_reconstruct);\n    }\n  }\n}\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/Reconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n\n#include <pup.h>\n\nnamespace Burgers::fd {\nReconstructor::Reconstructor(CkMigrateMessage* const msg) : PUP::able(msg) {}\n\nvoid Reconstructor::pup(PUP::er& p) { PUP::able::pup(p); }\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::fd {\n/// \\cond\nclass MonotonisedCentral;\n/// \\endcond\n\n/*!\n * \\brief The base class from which all reconstruction schemes must inherit\n */\nclass Reconstructor : public PUP::able {\n public:\n  Reconstructor() = default;\n  Reconstructor(const Reconstructor&) = default;\n  Reconstructor& operator=(const Reconstructor&) = default;\n  Reconstructor(Reconstructor&&) = default;\n  Reconstructor& operator=(Reconstructor&&) = default;\n  ~Reconstructor() override = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// \\cond\n  explicit Reconstructor(CkMigrateMessage* msg);\n  WRAPPED_PUPable_abstract(Reconstructor);  // NOLINT\n  /// \\endcond\n\n  using creatable_classes = tmpl::list<MonotonisedCentral>;\n\n  virtual std::unique_ptr<Reconstructor> get_clone() const = 0;\n\n  virtual size_t ghost_zone_size() const = 0;\n};\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n\n#include \"Evolution/Systems/Burgers/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace Burgers::fd {\nvoid register_derived_with_charm() {\n  register_derived_classes_with_charm<Reconstructor>();\n}\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Burgers::fd {\nvoid register_derived_with_charm();\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/FiniteDifference/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellSolver.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::fd {\nnamespace OptionTags {\n/*!\n * \\brief Holds the subcell reconstructor in the input file\n */\nstruct Reconstructor {\n  using type = std::unique_ptr<fd::Reconstructor>;\n\n  static constexpr Options::String help = {\"The reconstruction scheme to use.\"};\n  using group = evolution::dg::subcell::OptionTags::SubcellSolverGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief Tag for the reconstructor\n */\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<fd::Reconstructor>;\n  using option_tags = tmpl::list<OptionTags::Reconstructor>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& reconstructor) {\n    return reconstructor->get_clone();\n  }\n};\n}  // namespace Tags\n}  // namespace Burgers::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Burgers {\nvoid Fluxes::apply(const gsl::not_null<tnsr::I<DataVector, 1>*> flux,\n                   const Scalar<DataVector>& u) {\n  get<0>(*flux) = 0.5 * square(get(u));\n}\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Fluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace Burgers {\nnamespace Tags {\nstruct U;\n}  // namespace Tags\n}  // namespace Burgers\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace Burgers {\n/// The flux of \\f$U\\f$ is \\f$\\frac{1}{2} U^2\\f$.\nstruct Fluxes {\n  using argument_tags = tmpl::list<Tags::U>;\n  using return_tags =\n      tmpl::list<::Tags::Flux<Tags::U, tmpl::size_t<1>, Frame::Inertial>>;\n  static void apply(gsl::not_null<tnsr::I<DataVector, 1>*> flux,\n                    const Scalar<DataVector>& u);\n};\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Divergence.cpp\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Instantiations/Divergence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n\ntemplate Variables<tmpl::list<\n    Tags::div<Tags::Flux<Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>>>>\ndivergence(\n    const Variables<tmpl::list<\n        Tags::Flux<Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>>>& F,\n    const Mesh<1>& mesh,\n    const InverseJacobian<DataVector, 1, Frame::ElementLogical,\n                          Frame::Inertial>& inverse_jacobian);\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n\ntemplate class Events::ObserveTimeStep<Burgers::System>;\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n\ntemplate class ChangeTimeStepperOrder<Burgers::System>;\ntemplate class CleanHistory<Burgers::System>;\ntemplate class RecordTimeStepperData<Burgers::System>;\ntemplate class UpdateU<Burgers::System, false>;\ntemplate class UpdateU<Burgers::System, true>;\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Evolution/Systems/Burgers/TimeDerivativeTerms.hpp\"\n\nnamespace evolution::dg::Actions::detail {\ntemplate void volume_terms<::Burgers::TimeDerivativeTerms>(\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::dt, typename ::Burgers::System::variables_tag::tags_list>>*>\n        dt_vars_ptr,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::Flux, typename ::Burgers::System::flux_variables,\n        tmpl::size_t<1>, Frame::Inertial>>*>\n        volume_fluxes,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::deriv, typename ::Burgers::System::gradient_variables,\n        tmpl::size_t<1>, Frame::Inertial>>*>\n        partial_derivs,\n    const gsl::not_null<\n        Variables<typename ::Burgers::System::\n                      compute_volume_time_derivative_terms::temporary_tags>*>\n        temporaries,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::div,\n        db::wrap_tags_in<::Tags::Flux,\n                         typename ::Burgers::System::flux_variables,\n                         tmpl::size_t<1>, Frame::Inertial>>>*>\n        div_fluxes,\n    const Variables<typename ::Burgers::System::variables_tag::tags_list>&\n        evolved_vars,\n    const ::dg::Formulation dg_formulation, const Mesh<1>& mesh,\n    [[maybe_unused]] const tnsr::I<DataVector, 1, Frame::Inertial>&\n        inertial_coordinates,\n    const InverseJacobian<DataVector, 1, Frame::ElementLogical,\n                          Frame::Inertial>&\n        logical_to_inertial_inverse_jacobian,\n    [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>& mesh_velocity,\n    const std::optional<Scalar<DataVector>>& div_mesh_velocity,\n    const Scalar<DataVector>& u);\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GhostData.cpp\n  NeighborPackagedData.cpp\n  SetInitialRdmpData.cpp\n  TciOnDgGrid.cpp\n  TciOnFdGrid.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComputeFluxes.hpp\n  GhostData.hpp\n  NeighborPackagedData.hpp\n  SetInitialRdmpData.hpp\n  Subcell.hpp\n  TciOnDgGrid.hpp\n  TciOnFdGrid.hpp\n  TimeDerivative.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/ComputeFluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::subcell {\nnamespace detail {\n/*!\n * \\brief Helper function that calls `Burgers::Fluxes::apply` by\n * retrieving the return and argument tags from the Variables object `vars`.\n */\ntemplate <typename TagsList, typename... ReturnTags, typename... ArgumentTags>\nvoid compute_fluxes_impl(const gsl::not_null<Variables<TagsList>*> vars,\n                         tmpl::list<ReturnTags...> /*meta*/,\n                         tmpl::list<ArgumentTags...> /*meta*/) {\n  Fluxes::apply(make_not_null(&get<ReturnTags>(*vars))...,\n                get<ArgumentTags>(*vars)...);\n}\n}  // namespace detail\n\n/*!\n * \\brief Compute fluxes for subcell variables.\n */\ntemplate <typename TagsList>\nvoid compute_fluxes(const gsl::not_null<Variables<TagsList>*> vars) {\n  detail::compute_fluxes_impl(vars, typename Fluxes::return_tags{},\n                              typename Fluxes::argument_tags{});\n}\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/Subcell/GhostData.hpp\"\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::subcell {\nDataVector GhostVariables::apply(\n    const Variables<tmpl::list<Burgers::Tags::U>>& vars,\n    const size_t rdmp_size) {\n  DataVector buffer{vars.number_of_grid_points() + rdmp_size};\n  DataVector var_view{buffer.data(), vars.number_of_grid_points()};\n  var_view = get(get<Burgers::Tags::U>(vars));\n  return buffer;\n}\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/GhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename T>\nclass Variables;\nnamespace Tags {\ntemplate <typename TagsList>\nstruct Variables;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Burgers::subcell {\n/*!\n * \\brief Returns \\f$U\\f$, the variables needed for reconstruction.\n *\n * This mutator is passed to\n * `evolution::dg::subcell::Actions::SendDataForReconstruction`.\n */\nclass GhostVariables {\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::Variables<tmpl::list<Burgers::Tags::U>>>;\n\n  static DataVector apply(const Variables<tmpl::list<Burgers::Tags::U>>& vars,\n                          size_t rdmp_size);\n};\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/Subcell/NeighborPackagedData.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.tpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::subcell {\nDirectionalIdMap<1, DataVector> NeighborPackagedData::apply(\n    const db::Access& box,\n    const std::vector<DirectionalId<1>>& mortars_to_reconstruct_to) {\n  // The object to return\n  DirectionalIdMap<1, DataVector> neighbor_package_data{};\n  if (mortars_to_reconstruct_to.empty()) {\n    return neighbor_package_data;\n  }\n\n  using evolved_vars_tags = typename System::variables_tag::tags_list;\n  using fluxes_tags = typename Fluxes::return_tags;\n\n  // subcell currently does not support moving mesh\n  ASSERT(not db::get<domain::Tags::MeshVelocity<1>>(box).has_value(),\n         \"Haven't yet added support for moving mesh to DG-subcell. This \"\n         \"should be easy to generalize, but we will want to consider \"\n         \"storing the mesh velocity on the faces instead of \"\n         \"re-slicing/projecting.\");\n\n  // Project volume variables from DG to subcell mesh\n  const Mesh<1>& dg_mesh = db::get<domain::Tags::Mesh<1>>(box);\n  const Mesh<1>& subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<1>>(box);\n  const auto volume_vars_subcell = evolution::dg::subcell::fd::project(\n      db::get<typename System::variables_tag>(box), dg_mesh,\n      subcell_mesh.extents());\n\n  const auto& ghost_subcell_data =\n      db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<1>>(box);\n\n  const Burgers::fd::Reconstructor& recons =\n      db::get<Burgers::fd::Tags::Reconstructor>(box);\n\n  const auto& boundary_correction =\n      db::get<evolution::Tags::BoundaryCorrection>(box);\n  using derived_boundary_corrections =\n      Burgers::BoundaryCorrections::standard_boundary_corrections;\n  // perform reconstruction\n  tmpl::for_each<derived_boundary_corrections>([&](auto derived_correction_v) {\n    using derived_correction = tmpl::type_from<decltype(derived_correction_v)>;\n    if (typeid(boundary_correction) == typeid(derived_correction)) {\n      using dg_package_field_tags =\n          typename derived_correction::dg_package_field_tags;\n      using dg_package_data_argument_tags =\n          tmpl::append<evolved_vars_tags, fluxes_tags>;\n\n      const auto& element = db::get<domain::Tags::Element<1>>(box);\n\n      // Variables to store packaged data\n      Variables<dg_package_field_tags> packaged_data{};\n      // Variables to be reconstructed on the shared interfaces\n      Variables<dg_package_data_argument_tags> vars_on_face{};\n\n      for (const auto& mortar_id : mortars_to_reconstruct_to) {\n        // Note : 1D mortar has only one face point\n        const size_t num_face_pts = 1;\n        vars_on_face.initialize(num_face_pts);\n\n        // Reconstruct field variables on faces\n        call_with_dynamic_type<\n            void, typename Burgers::fd::Reconstructor::creatable_classes>(\n            &recons,\n            [&element, &mortar_id, &ghost_subcell_data, &subcell_mesh,\n             &vars_on_face, &volume_vars_subcell](const auto& reconstructor) {\n              reconstructor->reconstruct_fd_neighbor(\n                  make_not_null(&vars_on_face), volume_vars_subcell, element,\n                  ghost_subcell_data, subcell_mesh, mortar_id.direction());\n            });\n\n        // Compute fluxes\n        Burgers::subcell::compute_fluxes(make_not_null(&vars_on_face));\n\n        tnsr::i<DataVector, 1, Frame::Inertial> normal_covector =\n            get<evolution::dg::Tags::NormalCovector<1>>(\n                *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<1>>(\n                     box)\n                     .at(mortar_id.direction()));\n        for (auto& t : normal_covector) {\n          t *= -1.0;\n        }\n\n        // Compute the packaged data\n        packaged_data.initialize(num_face_pts);\n        evolution::dg::Actions::detail::dg_package_data<System>(\n            make_not_null(&packaged_data),\n            dynamic_cast<const derived_correction&>(boundary_correction),\n            vars_on_face, normal_covector, {std::nullopt}, box,\n            typename derived_correction::dg_package_data_volume_tags{},\n            dg_package_data_argument_tags{});\n\n        // Make a view so we can use iterators with std::copy\n        DataVector packaged_data_view{packaged_data.data(),\n                                      packaged_data.size()};\n        neighbor_package_data[mortar_id] = DataVector{packaged_data.size()};\n        std::copy(packaged_data_view.begin(), packaged_data_view.end(),\n                  neighbor_package_data[mortar_id].begin());\n      }\n    }\n  });\n\n  return neighbor_package_data;\n}\n}  // namespace Burgers::subcell\n\ntemplate void evolution::dg::subcell::neighbor_reconstructed_face_solution<\n    1, Burgers::subcell::NeighborPackagedData>(gsl::not_null<db::Access*> box);\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/NeighborPackagedData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n/// \\cond\nnamespace db {\nclass Access;\n}  // namespace db\nclass DataVector;\ntemplate <size_t VolumeDim>\nstruct DirectionalId;\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\n/// \\endcond\n\nnamespace Burgers::subcell {\n/*!\n * \\brief On elements using DG, reconstructs the interface data from a\n * neighboring element doing subcell.\n *\n * The neighbor's packaged data needed by the boundary correction is computed\n * and returned so that it can be used for solving the Riemann problem on the\n * interfaces.\n *\n * Note that for strict conservation the Riemann solve should be done on the\n * subcells, with the correction being projected back to the DG interface.\n * However, in practice such strict conservation doesn't seem to be necessary\n * and can be explained by that we only need strict conservation at shocks,\n * and if one element is doing DG, then we aren't at a shock.\n */\nstruct NeighborPackagedData {\n  static DirectionalIdMap<1, DataVector> apply(\n      const db::Access& box,\n      const std::vector<DirectionalId<1>>& mortars_to_reconstruct_to);\n};\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/Subcell/SetInitialRdmpData.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/TwoMeshRdmpTci.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Burgers::subcell {\nvoid SetInitialRdmpData::apply(\n    const gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n    const Scalar<DataVector>& u,\n    const evolution::dg::subcell::ActiveGrid active_grid,\n    const Mesh<1>& dg_mesh, const Mesh<1>& subcell_mesh) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Subcell) {\n    *rdmp_tci_data = {{max(get(u))}, {min(get(u))}};\n  } else {\n    using std::max;\n    using std::min;\n    const auto subcell_u = evolution::dg::subcell::fd::project(\n        get(u), dg_mesh, subcell_mesh.extents());\n\n    *rdmp_tci_data = {{max(max(get(u)), max(subcell_u))},\n                      {min(min(get(u)), min(subcell_u))}};\n  }\n}\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/SetInitialRdmpData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace Burgers::subcell {\n/// \\brief Sets the initial RDMP data.\n///\n/// Used on the subcells after the TCI marked the DG solution as inadmissible.\nstruct SetInitialRdmpData {\n  using argument_tags =\n      tmpl::list<Burgers::Tags::U, evolution::dg::subcell::Tags::ActiveGrid,\n                 ::domain::Tags::Mesh<1>,\n                 evolution::dg::subcell::Tags::Mesh<1>>;\n  using return_tags = tmpl::list<evolution::dg::subcell::Tags::DataForRdmpTci>;\n\n  static void apply(\n      gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n      const Scalar<DataVector>& u,\n      evolution::dg::subcell::ActiveGrid active_grid, const Mesh<1>& dg_mesh,\n      const Mesh<1>& subcell_mesh);\n};\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/Subcell.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Burgers {\n/*!\n * \\brief Code required by the DG-subcell/FD hybrid solver for Burgers system\n */\nnamespace subcell {}\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/Subcell/TciOnDgGrid.hpp\"\n\n#include <algorithm>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n\nnamespace Burgers::subcell {\nstd::tuple<bool, evolution::dg::subcell::RdmpTciData> TciOnDgGrid::apply(\n    const Scalar<DataVector>& dg_u, const Mesh<1>& dg_mesh,\n    const Mesh<1>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    double persson_exponent, [[maybe_unused]] const bool element_stays_on_dg) {\n  // Don't use buffer since we have only one memory allocation right now (until\n  // persson_tci can use a buffer)\n  const Scalar<DataVector> subcell_u{::evolution::dg::subcell::fd::project(\n      get(dg_u), dg_mesh, subcell_mesh.extents())};\n\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{\n      {max(max(get(dg_u)), max(get(subcell_u)))},\n      {min(min(get(dg_u)), min(get(subcell_u)))}};\n\n  const bool cell_is_troubled =\n      static_cast<bool>(evolution::dg::subcell::rdmp_tci(\n          rdmp_tci_data.max_variables_values,\n          rdmp_tci_data.min_variables_values,\n          past_rdmp_tci_data.max_variables_values,\n          past_rdmp_tci_data.min_variables_values,\n          subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon())) or\n      ::evolution::dg::subcell::persson_tci(\n          dg_u, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes());\n\n  return {cell_is_troubled, std::move(rdmp_tci_data)};\n}\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/TciOnDgGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace Burgers::subcell {\n/*!\n * \\brief The troubled-cell indicator run on the DG grid to check if the\n * solution is admissible.\n *\n * Applies the Persson and RDMP TCI to \\f$U\\f$.\n */\nstruct TciOnDgGrid {\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<Burgers::Tags::U, domain::Tags::Mesh<1>,\n                 evolution::dg::subcell::Tags::Mesh<1>,\n                 evolution::dg::subcell::Tags::DataForRdmpTci,\n                 evolution::dg::subcell::Tags::SubcellOptions<1>>;\n\n  static std::tuple<bool, evolution::dg::subcell::RdmpTciData> apply(\n      const Scalar<DataVector>& dg_u, const Mesh<1>& dg_mesh,\n      const Mesh<1>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      double persson_exponent, bool element_stays_on_dg);\n};\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/Subcell/TciOnFdGrid.hpp\"\n\n#include <algorithm>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace Burgers::subcell {\nstd::tuple<bool, evolution::dg::subcell::RdmpTciData> TciOnFdGrid::apply(\n    const Scalar<DataVector>& subcell_u, const Mesh<1>& dg_mesh,\n    const Mesh<1>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const double persson_exponent, const bool need_rdmp_data_only) {\n  const Scalar<DataVector> dg_u{evolution::dg::subcell::fd::reconstruct(\n      get(subcell_u), dg_mesh, subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)};\n\n  using std::max;\n  using std::min;\n  const evolution::dg::subcell::RdmpTciData rdmp_data_for_tci{\n      {max(max(get(dg_u)), max(get(subcell_u)))},\n      {min(min(get(dg_u)), min(get(subcell_u)))}};\n\n  if (need_rdmp_data_only) {\n    return {false, rdmp_data_for_tci};\n  }\n\n  const auto cell_is_troubled =\n      static_cast<bool>(evolution::dg::subcell::rdmp_tci(\n          rdmp_data_for_tci.max_variables_values,\n          rdmp_data_for_tci.min_variables_values,\n          past_rdmp_tci_data.max_variables_values,\n          past_rdmp_tci_data.min_variables_values,\n          subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon()));\n\n  return {cell_is_troubled or ::evolution::dg::subcell::persson_tci(\n                                  dg_u, dg_mesh, persson_exponent,\n                                  subcell_options.persson_num_highest_modes()),\n          {{max(get(subcell_u))}, {min(get(subcell_u))}}};\n}\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/TciOnFdGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace Burgers::subcell {\n/*!\n * \\brief Troubled-cell indicator applied to the finite difference subcell\n * solution to check if the corresponding DG solution is admissible.\n *\n * Applies the Persson to \\f$U\\f$ on the DG grid, and the RDMP TCI to \\f$U\\f$.\n */\nstruct TciOnFdGrid {\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<Burgers::Tags::U, ::domain::Tags::Mesh<1>,\n                 evolution::dg::subcell::Tags::Mesh<1>,\n                 evolution::dg::subcell::Tags::DataForRdmpTci,\n                 evolution::dg::subcell::Tags::SubcellOptions<1>>;\n\n  static std::tuple<bool, evolution::dg::subcell::RdmpTciData> apply(\n      const Scalar<DataVector>& subcell_u, const Mesh<1>& dg_mesh,\n      const Mesh<1>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      double persson_exponent, bool need_rdmp_data_only);\n};\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Subcell/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/ComputeBoundaryTerms.hpp\"\n#include \"Evolution/DgSubcell/CorrectPackagedData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/BoundaryConditionGhostData.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Burgers::subcell {\n/*!\n * \\brief Compute the time derivative on the subcell grid using FD\n * reconstruction.\n *\n * The code makes the following unchecked assumptions:\n * - Assumes Cartesian coordinates with a diagonal Jacobian matrix\n * from the logical to the inertial frame\n * - Assumes the mesh is not moving (grid and inertial frame are the same)\n */\nstruct TimeDerivative {\n  template <typename DbTagsList>\n  static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n    using metavariables =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>;\n    ASSERT((db::get<::domain::CoordinateMaps::Tags::CoordinateMap<\n                1, Frame::Grid, Frame::Inertial>>(*box))\n               .is_identity(),\n           \"Do not yet support moving mesh with DG-subcell.\");\n    const Element<1>& element = db::get<domain::Tags::Element<1>>(*box);\n\n    const bool element_is_interior = element.external_boundaries().size() == 0;\n    constexpr bool subcell_enabled_at_external_boundary =\n        metavariables::SubcellOptions::subcell_enabled_at_external_boundary;\n\n    ASSERT(element_is_interior or subcell_enabled_at_external_boundary,\n           \"Subcell time derivative is called at a boundary element while \"\n           \"using subcell is disabled at external boundaries.\"\n           \"ElementID \"\n               << element.id());\n\n    using evolved_vars_tags = typename System::variables_tag::tags_list;\n    using fluxes_tags = typename Fluxes::return_tags;\n\n    // The copy of Mesh is intentional to avoid a GCC-7 internal compiler error.\n    const Mesh<1> subcell_mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<1>>(*box);\n    const size_t num_reconstructed_pts =\n        subcell_mesh.number_of_grid_points() + 1;\n\n    const Burgers::fd::Reconstructor& recons =\n        db::get<Burgers::fd::Tags::Reconstructor>(*box);\n\n    const auto& boundary_correction =\n        db::get<evolution::Tags::BoundaryCorrection>(*box);\n    using derived_boundary_corrections =\n        tmpl::at<typename metavariables::factory_creation::factory_classes,\n                 evolution::BoundaryCorrection>;\n    // Variables to store the boundary correction terms on FD subinterfaces\n    std::array<Variables<evolved_vars_tags>, 1> fd_boundary_corrections{};\n\n    // If the element has external boundaries and subcell is enabled for\n    // boundary elements, compute FD ghost data with a given boundary condition.\n    if constexpr (subcell_enabled_at_external_boundary) {\n      if (element.external_boundaries().size() != 0) {\n        fd::BoundaryConditionGhostData::apply(box, element, recons);\n      }\n    }\n\n    // package the data and compute the boundary correction\n    tmpl::for_each<derived_boundary_corrections>(\n        [&boundary_correction, &box, &element, &fd_boundary_corrections,\n         &num_reconstructed_pts, &recons,\n         &subcell_mesh](auto derived_correction_v) {\n          using derived_correction =\n              tmpl::type_from<decltype(derived_correction_v)>;\n          if (typeid(boundary_correction) == typeid(derived_correction)) {\n            using dg_package_field_tags =\n                typename derived_correction::dg_package_field_tags;\n            using dg_package_data_argument_tags =\n                tmpl::append<evolved_vars_tags, fluxes_tags>;\n\n            // Variables that need to be reconstructed for dg_package_data()\n            auto package_data_argvars_lower_face =\n                make_array<1>(Variables<dg_package_data_argument_tags>(\n                    num_reconstructed_pts));\n            auto package_data_argvars_upper_face =\n                make_array<1>(Variables<dg_package_data_argument_tags>(\n                    num_reconstructed_pts));\n\n            // Reconstruct the fields on interfaces\n            call_with_dynamic_type<\n                void, typename Burgers::fd::Reconstructor::creatable_classes>(\n                &recons,\n                [&box, &package_data_argvars_lower_face,\n                 &package_data_argvars_upper_face](const auto& reconstructor) {\n                  db::apply<typename std::decay_t<decltype(\n                      *reconstructor)>::reconstruction_argument_tags>(\n                      [&package_data_argvars_lower_face,\n                       &package_data_argvars_upper_face,\n                       &reconstructor](const auto&... args) {\n                        reconstructor->reconstruct(\n                            make_not_null(&package_data_argvars_lower_face),\n                            make_not_null(&package_data_argvars_upper_face),\n                            args...);\n                      },\n                      *box);\n                });\n\n            // Variables to store packaged data. Allocate outside of loop to\n            // reduce allocations\n            Variables<dg_package_field_tags> upper_packaged_data{\n                num_reconstructed_pts};\n            Variables<dg_package_field_tags> lower_packaged_data{\n                num_reconstructed_pts};\n\n            auto& vars_upper_face = gsl::at(package_data_argvars_upper_face, 0);\n            auto& vars_lower_face = gsl::at(package_data_argvars_lower_face, 0);\n\n            // Compute fluxes on each faces\n            Burgers::subcell::compute_fluxes(make_not_null(&vars_upper_face));\n            Burgers::subcell::compute_fluxes(make_not_null(&vars_lower_face));\n\n            // Note that we use the sign convention on the normal vectors to\n            // be compatible with DG.\n            tnsr::i<DataVector, 1, Frame::Inertial> lower_outward_conormal{\n                num_reconstructed_pts, 0.0};\n            lower_outward_conormal.get(0) = 1.0;\n            tnsr::i<DataVector, 1, Frame::Inertial> upper_outward_conormal{\n                num_reconstructed_pts, 0.0};\n            upper_outward_conormal.get(0) = -1.0;\n\n            // Compute the packaged data\n            evolution::dg::Actions::detail::dg_package_data<System>(\n                make_not_null(&upper_packaged_data),\n                dynamic_cast<const derived_correction&>(boundary_correction),\n                vars_upper_face, upper_outward_conormal, {std::nullopt}, *box,\n                typename derived_correction::dg_package_data_volume_tags{},\n                dg_package_data_argument_tags{});\n            evolution::dg::Actions::detail::dg_package_data<System>(\n                make_not_null(&lower_packaged_data),\n                dynamic_cast<const derived_correction&>(boundary_correction),\n                vars_lower_face, lower_outward_conormal, {std::nullopt}, *box,\n                typename derived_correction::dg_package_data_volume_tags{},\n                dg_package_data_argument_tags{});\n\n            // Now need to check if any of our neighbors are doing DG, because\n            // if so then we need to use whatever boundary data they sent\n            // instead of what we computed locally.\n            //\n            // Note: We could check this beforehand to avoid the extra work of\n            // reconstruction and flux computations at the boundaries.\n            evolution::dg::subcell::correct_package_data<true>(\n                make_not_null(&lower_packaged_data),\n                make_not_null(&upper_packaged_data), 0, element, subcell_mesh,\n                db::get<evolution::dg::Tags::MortarData<1>>(*box), 0);\n\n            // Compute the corrections on the faces. We only need to compute\n            // this once because we can just flip the normal vectors then\n            gsl::at(fd_boundary_corrections, 0)\n                .initialize(num_reconstructed_pts);\n            evolution::dg::subcell::compute_boundary_terms(\n                make_not_null(&gsl::at(fd_boundary_corrections, 0)),\n                dynamic_cast<const derived_correction&>(boundary_correction),\n                upper_packaged_data, lower_packaged_data, db::as_access(*box),\n                typename derived_correction::dg_boundary_terms_volume_tags{});\n          }\n        });\n\n    std::array<double, 1> one_over_delta_xi{};\n    {\n      const tnsr::I<DataVector, 1, Frame::ElementLogical>&\n          cell_centered_logical_coords =\n              db::get<evolution::dg::subcell::Tags::Coordinates<\n                  1, Frame::ElementLogical>>(*box);\n\n      gsl::at(one_over_delta_xi, 0) =\n          1.0 / (get<0>(cell_centered_logical_coords)[1] -\n                 get<0>(cell_centered_logical_coords)[0]);\n    }\n\n    // Now compute the actual time derivative\n    using dt_variables_tag =\n        db::add_tag_prefix<::Tags::dt, typename System::variables_tag>;\n    const size_t num_pts = subcell_mesh.number_of_grid_points();\n    db::mutate<dt_variables_tag>(\n        [&num_pts, &fd_boundary_corrections, &subcell_mesh, &one_over_delta_xi](\n            const auto dt_vars_ptr,\n            const InverseJacobian<DataVector, 1, Frame::ElementLogical,\n                                  Frame::Grid>&\n                cell_centered_logical_to_grid_inv_jacobian) {\n          dt_vars_ptr->initialize(num_pts, 0.0);\n          auto& dt_u = get<::Tags::dt<::Burgers::Tags::U>>(*dt_vars_ptr);\n\n          Scalar<DataVector>& u_correction =\n              get<::Burgers::Tags::U>(gsl::at(fd_boundary_corrections, 0));\n          evolution::dg::subcell::add_cartesian_flux_divergence(\n              make_not_null(&get(dt_u)), gsl::at(one_over_delta_xi, 0),\n              cell_centered_logical_to_grid_inv_jacobian.get(0, 0),\n              get(u_correction), subcell_mesh.extents(), 0);\n        },\n        box,\n        db::get<\n            evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGrid<1>>(\n            *box));\n  }\n};\n}  // namespace Burgers::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/Characteristics.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/TimeDerivativeTerms.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup EvolutionSystemsGroup\n/// \\brief Items related to evolving the %Burgers equation\n/// \\f$0 = \\partial_t U + \\partial_x\\left(U^2/2\\right)\\f$.\n///\n/// \\note For this definition (i.e., with the factor of one half in the flux)\n/// of the Burgers system, the local characteristic speed is \\f$U\\f$.\nnamespace Burgers {\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = true;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = 1;\n\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition;\n\n  using variables_tag = ::Tags::Variables<tmpl::list<Tags::U>>;\n  using flux_variables = tmpl::list<Tags::U>;\n  using gradient_variables = tmpl::list<>;\n\n  using compute_volume_time_derivative_terms = TimeDerivativeTerms;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed;\n};\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\nnamespace Burgers {\n/// \\brief Tags for the Burgers system\nnamespace Tags {\nstruct U : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The characteristic speeds\nstruct CharacteristicSpeeds : db::SimpleTag {\n  using type = std::array<DataVector, 1>;\n};\n}  // namespace Tags\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Burgers/TimeDerivativeTerms.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Burgers {\nevolution::dg::TimeDerivativeDecisions<1> TimeDerivativeTerms::apply(\n    const gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_vars*/,\n    const gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux_u,\n    const Scalar<DataVector>& u) {\n  get<0>(*flux_u) = 0.5 * square(get(u));\n  return {true};\n}\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/Burgers/TimeDerivativeTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace Burgers {\nnamespace Tags {\nstruct U;\n}  // namespace Tags\n}  // namespace Burgers\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace Burgers {\n/// Computes the time derivative terms needed for the Burgers system, which are\n/// just the fluxes.\nstruct TimeDerivativeTerms {\n  using temporary_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<Tags::U>;\n\n  static evolution::dg::TimeDerivativeDecisions<1> apply(\n      // Time derivatives returned by reference. No source terms or\n      // nonconservative products, so not used. All the tags in the\n      // variables_tag in the system struct.\n      gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_vars*/,\n\n      // Fluxes returned by reference. Listed in the system struct as\n      // flux_variables.\n      gsl::not_null<tnsr::I<DataVector, 1, Frame::Inertial>*> flux_u,\n\n      // Arguments listed in argument_tags above\n      const Scalar<DataVector>& u);\n};\n}  // namespace Burgers\n"
  },
  {
    "path": "src/Evolution/Systems/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Burgers)\nadd_subdirectory(Cce)\nadd_subdirectory(Ccz4)\nadd_subdirectory(CurvedScalarWave)\nadd_subdirectory(ForceFree)\nadd_subdirectory(GeneralizedHarmonic)\nadd_subdirectory(GrMhd)\nadd_subdirectory(NewtonianEuler)\nadd_subdirectory(RadiationTransport)\nadd_subdirectory(ScalarAdvection)\nadd_subdirectory(ScalarTensor)\nadd_subdirectory(ScalarWave)\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/ReceiveTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"IO/Observer/Actions/GetLockPointer.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Time/SelfStart.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Obtains the CCE boundary data at the specified `time`, and reports it\n * to the `EvolutionComponent` via `Actions::ReceiveWorldtubeData`.\n *\n * \\details See the template partial specializations of this class for details\n * on the different strategies for each component type.\n */\ntemplate <typename BoundaryComponent, typename EvolutionComponent>\nstruct BoundaryComputeAndSendToEvolution;\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Computes Bondi boundary data from GH evolution variables and sends the\n * result to the `EvolutionComponent` (template argument).\n *\n * \\details After the computation, this action will call\n * `Cce::Actions::ReceiveWorldtubeData` on the `EvolutionComponent` with each of\n * the types from `typename Metavariables::cce_boundary_communication_tags` sent\n * as arguments\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `Tags::Variables<typename\n *   Metavariables::cce_boundary_communication_tags>` (every tensor)\n */\ntemplate <typename BoundaryComponent, typename EvolutionComponent>\nstruct SendToEvolution;\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Obtains the CCE boundary data at the specified `time`, and reports it\n * to the `EvolutionComponent` via `Actions::ReceiveWorldtubeData`.\n *\n * \\details This uses the `WorldtubeDataManager` to perform all of the work of\n * managing the file buffer, interpolating to the desired time point, and\n * compute the Bondi quantities on the boundary. Once readied, it sends each\n * tensor from the the full `Variables<typename\n * Metavariables::cce_boundary_communication_tags>` back to the\n * `EvolutionComponent`\n *\n * Uses:\n * - DataBox:\n *  - `Tags::H5WorldtubeBoundaryDataManager`\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `Tags::Variables<typename\n * Metavariables::cce_boundary_communication_tags>` (every tensor)\n */\ntemplate <typename Metavariables, typename EvolutionComponent>\nstruct BoundaryComputeAndSendToEvolution<H5WorldtubeBoundary<Metavariables>,\n                                         EvolutionComponent> {\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const TimeStepId& time) {\n    auto hdf5_lock = Parallel::local_branch(\n                         Parallel::get_parallel_component<\n                             observers::ObserverWriter<Metavariables>>(cache))\n                         ->template local_synchronous_action<\n                             observers::Actions::GetLockPointer<\n                                 observers::Tags::H5FileLock>>();\n    bool successfully_populated = false;\n    db::mutate<Tags::H5WorldtubeBoundaryDataManager,\n               ::Tags::Variables<\n                   typename Metavariables::cce_boundary_communication_tags>>(\n        [&successfully_populated, &time, &hdf5_lock](\n            const gsl::not_null<std::unique_ptr<Cce::WorldtubeDataManager<\n                Tags::characteristic_worldtube_boundary_tags<\n                    Tags::BoundaryValue>>>*>\n                worldtube_data_manager,\n            const gsl::not_null<Variables<\n                typename Metavariables::cce_boundary_communication_tags>*>\n                boundary_variables) {\n          successfully_populated =\n              (*worldtube_data_manager)\n                  ->populate_hypersurface_boundary_data(boundary_variables,\n                                                        time.substep_time(),\n                                                        hdf5_lock);\n        },\n        make_not_null(&box));\n    if (not successfully_populated) {\n      ERROR(\"Insufficient boundary data to proceed, exiting early at time \" +\n            std::to_string(time.substep_time()));\n    }\n    Parallel::receive_data<Cce::ReceiveTags::BoundaryData<\n        typename Metavariables::cce_boundary_communication_tags>>(\n        Parallel::get_parallel_component<EvolutionComponent>(cache), time,\n        db::get<::Tags::Variables<\n            typename Metavariables::cce_boundary_communication_tags>>(box),\n        true);\n  }\n};\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Obtains the Klein-Gordon CCE boundary data at the specified `time`,\n * and reports it to the `EvolutionComponent` via\n * `Actions::ReceiveWorldtubeData`.\n *\n * \\details This uses the `WorldtubeDataManager` to perform all of the work of\n * managing the file buffer, interpolating to the desired time point, and\n * compute the Bondi and Klein-Gordon quantities on the boundary. Once readied,\n * it sends each tensor or scalar from the the full `Variables<typename\n * Metavariables::cce_boundary_communication_tags>` or `Variables<typename\n * Metavariables::klein_gordon_boundary_communication_tags>` back to the\n * `EvolutionComponent`\n *\n * Uses:\n * - DataBox:\n *  - `Tags::H5WorldtubeBoundaryDataManager`\n *  - `Tags::KleinGordonH5WorldtubeBoundaryDataManager`\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `Tags::Variables<typename\n * Metavariables::cce_boundary_communication_tags>` (every tensor)\n *   - `Tags::Variables<typename\n * Metavariables::klein_gordon_boundary_communication_tags>` (every scalar)\n */\ntemplate <typename Metavariables, typename EvolutionComponent>\nstruct BoundaryComputeAndSendToEvolution<\n    KleinGordonH5WorldtubeBoundary<Metavariables>, EvolutionComponent> {\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const TimeStepId& time) {\n    auto hdf5_lock = Parallel::local_branch(\n                         Parallel::get_parallel_component<\n                             observers::ObserverWriter<Metavariables>>(cache))\n                         ->template local_synchronous_action<\n                             observers::Actions::GetLockPointer<\n                                 observers::Tags::H5FileLock>>();\n    bool tensor_successfully_populated = false;\n    bool klein_gordon_successfully_populated = false;\n    db::mutate<\n        Tags::H5WorldtubeBoundaryDataManager,\n        Tags::KleinGordonH5WorldtubeBoundaryDataManager,\n        ::Tags::Variables<\n            typename Metavariables::cce_boundary_communication_tags>,\n        ::Tags::Variables<\n            typename Metavariables::klein_gordon_boundary_communication_tags>>(\n        [&tensor_successfully_populated, &klein_gordon_successfully_populated,\n         &time, &hdf5_lock](\n            const gsl::not_null<std::unique_ptr<Cce::WorldtubeDataManager<\n                Tags::characteristic_worldtube_boundary_tags<\n                    Tags::BoundaryValue>>>*>\n                tensor_worldtube_data_manager,\n            const gsl::not_null<std::unique_ptr<Cce::WorldtubeDataManager<\n                Tags::klein_gordon_worldtube_boundary_tags>>*>\n                klein_gordon_worldtube_data_manager,\n            const gsl::not_null<Variables<\n                typename Metavariables::cce_boundary_communication_tags>*>\n                tensor_boundary_variables,\n            const gsl::not_null<\n                Variables<typename Metavariables::\n                              klein_gordon_boundary_communication_tags>*>\n                klein_gordon_boundary_variables) {\n          tensor_successfully_populated =\n              (*tensor_worldtube_data_manager)\n                  ->populate_hypersurface_boundary_data(\n                      tensor_boundary_variables, time.substep_time(),\n                      hdf5_lock);\n\n          klein_gordon_successfully_populated =\n              (*klein_gordon_worldtube_data_manager)\n                  ->populate_hypersurface_boundary_data(\n                      klein_gordon_boundary_variables, time.substep_time(),\n                      hdf5_lock);\n        },\n        make_not_null(&box));\n    if (not tensor_successfully_populated) {\n      ERROR(\n          \"Insufficient tensor boundary data to proceed, exiting early at \"\n          \"time \" +\n          std::to_string(time.substep_time()));\n    }\n\n    if (not klein_gordon_successfully_populated) {\n      ERROR(\n          \"Insufficient scalar boundary data to proceed, exiting early at \"\n          \"time \" +\n          std::to_string(time.substep_time()));\n    }\n    Parallel::receive_data<Cce::ReceiveTags::BoundaryData<\n        typename Metavariables::cce_boundary_communication_tags>>(\n        Parallel::get_parallel_component<EvolutionComponent>(cache), time,\n        db::get<::Tags::Variables<\n            typename Metavariables::cce_boundary_communication_tags>>(box),\n        true);\n\n    Parallel::receive_data<Cce::ReceiveTags::BoundaryData<\n        typename Metavariables::klein_gordon_boundary_communication_tags>>(\n        Parallel::get_parallel_component<EvolutionComponent>(cache), time,\n        db::get<::Tags::Variables<\n            typename Metavariables::klein_gordon_boundary_communication_tags>>(\n            box),\n        true);\n  }\n};\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Calculates the analytic boundary data at the specified `time`, and\n * sends the resulting Bondi-Sachs boundary data to the `EvolutionComponent`\n *\n * \\details This uses the `Cce::AnalyticBoundaryDataManager` to\n * perform all of the work of calculating the analytic boundary solution, which\n * in turn uses derived classes of `Cce::Solutions::WorldtubeData` to calculate\n * the metric data before it is transformed to Bondi-Sachs variables.\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `Tags::AnalyticWordltubeBoundaryDataManager`\n */\ntemplate <typename Metavariables, typename EvolutionComponent>\nstruct BoundaryComputeAndSendToEvolution<\n    AnalyticWorldtubeBoundary<Metavariables>, EvolutionComponent> {\n  template <typename ParallelComponent, typename DbTagList, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const TimeStepId& time) {\n    bool successfully_populated = false;\n    db::mutate<Tags::AnalyticBoundaryDataManager,\n               ::Tags::Variables<\n                   typename Metavariables::cce_boundary_communication_tags>>(\n        [&successfully_populated, &time](\n            const gsl::not_null<Cce::AnalyticBoundaryDataManager*>\n                worldtube_data_manager,\n            const gsl::not_null<Variables<\n                typename Metavariables::cce_boundary_communication_tags>*>\n                boundary_variables) {\n          successfully_populated =\n              (*worldtube_data_manager)\n                  .populate_hypersurface_boundary_data(boundary_variables,\n                                                       time.substep_time());\n        },\n        make_not_null(&box));\n\n    if (not successfully_populated) {\n      ERROR(\"Insufficient boundary data to proceed, exiting early at time \"\n            << time.substep_time());\n    }\n    Parallel::receive_data<Cce::ReceiveTags::BoundaryData<\n        typename Metavariables::cce_boundary_communication_tags>>(\n        Parallel::get_parallel_component<EvolutionComponent>(cache), time,\n        db::get<::Tags::Variables<\n            typename Metavariables::cce_boundary_communication_tags>>(box),\n        true);\n  }\n};\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Submits a request for CCE boundary data at the specified `time` to the\n * `Cce::InterfaceManagers::GhInterfaceManager`, and sends the data to the\n * `EvolutionComponent` (template argument) if it is ready.\n *\n * \\details This uses the `Cce::InterfaceManagers::GhInterfaceManager` to\n * perform all of the work of managing the buffer of data sent from the GH\n * system and interpolating if necessary and supported. This dispatches then to\n * `Cce::Actions::SendToEvolution<GhWorldtubeBoundary<Metavariables>,\n * EvolutionComponent>` if the boundary data is ready, otherwise\n * simply submits the request and waits for data to become available via\n * `Cce::Actions::ReceiveGhWorldtubeData`, which will call\n * `Cce::Actions::SendToEvolution<GhWorldtubeBoundary<Metavariables>,\n * EvolutionComponent>` as soon as the data becomes available.\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `Tags::GhInterfaceManager`\n */\ntemplate <typename Metavariables, typename EvolutionComponent>\nstruct BoundaryComputeAndSendToEvolution<GhWorldtubeBoundary<Metavariables>,\n                                         EvolutionComponent> {\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const TimeStepId& time) {\n    auto retrieve_data_and_send_to_evolution =\n        [&time,\n         &cache](const gsl::not_null<InterfaceManagers::GhInterfaceManager*>\n                     interface_manager) {\n          interface_manager->request_gh_data(time);\n          const auto gh_data =\n              interface_manager->retrieve_and_remove_first_ready_gh_data();\n          if (static_cast<bool>(gh_data)) {\n            Parallel::simple_action<Actions::SendToEvolution<\n                GhWorldtubeBoundary<Metavariables>, EvolutionComponent>>(\n                Parallel::get_parallel_component<\n                    GhWorldtubeBoundary<Metavariables>>(cache),\n                get<0>(*gh_data), get<1>(*gh_data));\n          }\n        };\n    if (SelfStart::is_self_starting(time)) {\n      db::mutate<Tags::SelfStartGhInterfaceManager>(\n          retrieve_data_and_send_to_evolution, make_not_null(&box));\n    } else {\n      db::mutate<Tags::GhInterfaceManager>(retrieve_data_and_send_to_evolution,\n                                           make_not_null(&box));\n    }\n  }\n};\n\n/// \\cond\ntemplate <typename Metavariables, typename EvolutionComponent>\nstruct SendToEvolution<GhWorldtubeBoundary<Metavariables>, EvolutionComponent> {\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex>\n  static void apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const TimeStepId& time,\n      const InterfaceManagers::GhInterfaceManager::gh_variables& gh_variables) {\n    apply<ParallelComponent>(\n        box, cache, array_index, time,\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(gh_variables),\n        get<gh::Tags::Phi<DataVector, 3>>(gh_variables),\n        get<gh::Tags::Pi<DataVector, 3>>(gh_variables));\n  }\n\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex>\n  static void apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const TimeStepId& time,\n      const tnsr::aa<DataVector, 3, ::Frame::Inertial>& spacetime_metric,\n      const tnsr::iaa<DataVector, 3, ::Frame::Inertial>& phi,\n      const tnsr::aa<DataVector, 3, ::Frame::Inertial>& pi) {\n    db::mutate<::Tags::Variables<\n        typename Metavariables::cce_boundary_communication_tags>>(\n        [&spacetime_metric, &phi, &pi](\n            const gsl::not_null<Variables<\n                typename Metavariables::cce_boundary_communication_tags>*>\n                boundary_variables,\n            const double extraction_radius, const double l_max) {\n          create_bondi_boundary_data(boundary_variables, phi, pi,\n                                     spacetime_metric, extraction_radius,\n                                     l_max);\n        },\n        make_not_null(&box), db::get<Tags::ExtractionRadius>(box),\n        db::get<Tags::LMax>(box));\n    Parallel::receive_data<Cce::ReceiveTags::BoundaryData<\n        typename Metavariables::cce_boundary_communication_tags>>(\n        Parallel::get_parallel_component<EvolutionComponent>(cache), time,\n        db::get<::Tags::Variables<\n            typename Metavariables::cce_boundary_communication_tags>>(box),\n        true);\n  }\n};\n/// \\endcond\n\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ScriObserveInterpolated.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryComputeAndSendToEvolution.hpp\n  CalculateScriInputs.hpp\n  CharacteristicEvolutionBondiCalculations.hpp\n  FilterSwshVolumeQuantity.hpp\n  InitializeCharacteristicEvolutionScri.hpp\n  InitializeCharacteristicEvolutionTime.hpp\n  InitializeCharacteristicEvolutionVariables.hpp\n  InitializeFirstHypersurface.hpp\n  InitializeKleinGordonFirstHypersurface.hpp\n  InitializeKleinGordonVariables.hpp\n  InitializeWorldtubeBoundary.hpp\n  InsertInterpolationScriData.hpp\n  PrecomputeKleinGordonSourceVariables.hpp\n  Psi0Matching.hpp\n  ReceiveGhWorldtubeData.hpp\n  ReceiveWorldtubeData.hpp\n  RequestBoundaryData.hpp\n  ScriObserveInterpolated.hpp\n  SendGhVarsToCce.hpp\n  TimeManagement.hpp\n  UpdateGauge.hpp\n  WriteScriBondiQuantities.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/CalculateScriInputs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Calculates the Bondi quantities that are required for any of the\n * `CalculateScriPlusValue` mutators.\n *\n * \\details Internally dispatches to the `PreSwshDerivatives` and\n * `Spectral::Swsh::AngularDerivatives` utilities to perform the radial and\n * angular differentiation that is required to prepare all of the Bondi\n * quantities needed for evaluating the scri+ values. This relies on the\n * typelists `Cce::all_pre_swsh_derivative_tags_for_scri`,\n * `Cce::all_boundary_pre_swsh_derivative_tags_for_scri`,\n * `Cce::all_swsh_derivative_tags_for_scri`, and\n * `Cce::all_boundary_swsh_derivative_tags_for_scri` to determine which\n * calculations to perform.\n */\nstruct CalculateScriInputs {\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    tmpl::for_each<\n        tmpl::append<all_pre_swsh_derivative_tags_for_scri,\n                     all_boundary_pre_swsh_derivative_tags_for_scri>>(\n        [&box](auto pre_swsh_derivative_tag_v) {\n          using pre_swsh_derivative_tag =\n              typename decltype(pre_swsh_derivative_tag_v)::type;\n          db::mutate_apply<PreSwshDerivatives<pre_swsh_derivative_tag>>(\n              make_not_null(&box));\n        });\n\n    db::mutate_apply<\n        Spectral::Swsh::AngularDerivatives<all_swsh_derivative_tags_for_scri>>(\n        make_not_null(&box));\n    boundary_derivative_impl(box, db::get<Tags::LMax>(box),\n                             all_boundary_swsh_derivative_tags_for_scri{});\n\n    tmpl::for_each<\n        all_swsh_derivative_tags_for_scri>([&box](auto derivative_tag_v) {\n      using derivative_tag = typename decltype(derivative_tag_v)::type;\n      ::Cce::detail::apply_swsh_jacobian_helper<derivative_tag>(\n          make_not_null(&box), typename ApplySwshJacobianInplace<\n                                   derivative_tag>::on_demand_argument_tags{});\n    });\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  template <typename DbTags, typename... TagPack>\n  static void boundary_derivative_impl(db::DataBox<DbTags>& box,\n                                       const size_t l_max,\n                                       tmpl::list<TagPack...> /*meta*/) {\n    db::mutate<TagPack...>(\n        [&l_max](const gsl::not_null<typename TagPack::type*>... derivatives,\n                 const typename TagPack::derivative_of::type&... arguments) {\n          Spectral::Swsh::angular_derivatives<\n              tmpl::list<typename TagPack::derivative_kind...>>(\n              l_max, 1, make_not_null(&get(*derivatives))...,\n              get(arguments)...);\n        },\n        make_not_null(&box), db::get<typename TagPack::derivative_of>(box)...);\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/CharacteristicEvolutionBondiCalculations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Evolution/Systems/Cce/SwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Prepare the input quantities in the \\ref DataBoxGroup for the\n * evaluation of the hypersurface integral used to compute `BondiTag`.\n *\n * \\details Internally this calls the\n * `mutate_all_pre_swsh_derivatives_for_tag<BondiTag>()` and\n * `mutate_all_swsh_derivatives_for_tag<BondiTag>()` utility functions, which\n * determine which quantities are necessary for each of the hypersurface\n * computations.\n */\ntemplate <typename BondiTag>\nstruct CalculateIntegrandInputsForTag {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    mutate_all_pre_swsh_derivatives_for_tag<BondiTag>(make_not_null(&box));\n    mutate_all_swsh_derivatives_for_tag<BondiTag>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Perform all of the computations for dependencies of the hypersurface\n * equations that do not themselves depend on any hypersurface integrations.\n *\n * \\details This is to be called as a 'pre-computation' step prior to looping\n * over the hypersurface integration set. Internally, this uses\n * `Cce::GaugeAdjustedBoundaryValue<Tag>` and\n * `Cce::mutate_all_precompute_cce_dependencies\n * <Tags::EvolutionGaugeBoundaryValue>()`\n * to calculate the requisite dependencies.\n */\nstruct PrecomputeGlobalCceDependencies {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    tmpl::for_each<gauge_adjustments_setup_tags>([&box](auto tag_v) {\n      using tag = typename decltype(tag_v)::type;\n      db::mutate_apply<GaugeAdjustedBoundaryValue<tag>>(make_not_null(&box));\n    });\n    mutate_all_precompute_cce_dependencies<Tags::EvolutionGaugeBoundaryValue>(\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/FilterSwshVolumeQuantity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Filters the spherical volume data stored in `BondiTag` according to\n * the filter parameters in the `Parallel::GlobalCache`.\n *\n * \\details This action dispatches to the function\n * `filter_swsh_volume_quantity()` to perform the mathematics of\n * the filtering\n *\n * Uses:\n * - DataBox:\n *   - `Cce::Tags::LMax`\n * - GlobalCache:\n *   - `InitializationTags::FilterLMax`\n *   - `InitializationTags::RadialFilterAlpha`\n *   - `InitializationTags::RadialFilterHalfPower`\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies: `BondiTag`\n */\ntemplate <typename BondiTag>\nstruct FilterSwshVolumeQuantity {\n  using const_global_cache_tags =\n      tmpl::list<Tags::FilterLMax, Tags::RadialFilterAlpha,\n                 Tags::RadialFilterHalfPower>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t l_max = db::get<Tags::LMax>(box);\n    const size_t l_filter_start = get<Tags::FilterLMax>(box);\n    const double radial_filter_alpha = get<Tags::RadialFilterAlpha>(box);\n    const size_t radial_filter_half_power =\n        get<Tags::RadialFilterHalfPower>(box);\n    db::mutate<BondiTag>(\n        [&l_max, &l_filter_start, &radial_filter_alpha,\n         &radial_filter_half_power](\n            const gsl::not_null<typename BondiTag::type*> bondi_quantity) {\n          Spectral::Swsh::filter_swsh_volume_quantity(\n              make_not_null(&get(*bondi_quantity)), l_max, l_filter_start,\n              radial_filter_alpha, radial_filter_half_power);\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionScri.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/ScriPlusInterpolationManager.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Rational.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace Cce {\n\n/// \\cond\ntemplate <class Metavariables>\nstruct AnalyticWorldtubeBoundary;\n/// \\endcond\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initializes the `CharacteristicEvolution` component with contents\n * needed to perform the interpolation at scri+.\n *\n * \\details Sets up the \\ref DataBoxGroup to be ready to store data in the scri+\n * interpolators and perform interpolation for the final scri+ outputs.\n *\n * \\ref DataBoxGroup changes:\n * - Modifies: nothing\n * - Adds:\n *  - `Cce::Tags::InterpolationManager<ComplexDataVector, Tag>` for each `Tag`\n * in `scri_values_to_observe`\n * - Removes: nothing\n */\ntemplate <typename ScriValuesToObserve, typename BoundaryComponent>\nstruct InitializeCharacteristicEvolutionScri {\n  using simple_tags_from_options = tmpl::flatten<tmpl::list<\n      InitializationTags::ScriInterpolationOrder,\n      tmpl::conditional_t<\n          tt::is_a_v<AnalyticWorldtubeBoundary, BoundaryComponent>,\n          tmpl::list<Tags::AnalyticBoundaryDataManager>, tmpl::list<>>>>;\n\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  using simple_tags =\n      tmpl::transform<ScriValuesToObserve,\n                      tmpl::bind<Tags::InterpolationManager,\n                                 tmpl::pin<ComplexDataVector>, tmpl::_1>>;\n\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    initialize_impl(make_not_null(&box),\n                    typename Metavariables::scri_values_to_observe{});\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  template <typename TagList, typename... TagPack>\n  static void initialize_impl(const gsl::not_null<db::DataBox<TagList>*> box,\n                              tmpl::list<TagPack...> /*meta*/) {\n    const size_t target_number_of_points =\n        db::get<InitializationTags::ScriInterpolationOrder>(*box);\n    const size_t vector_size =\n        Spectral::Swsh::number_of_swsh_collocation_points(\n            db::get<Spectral::Swsh::Tags::LMax>(*box));\n    // silence compiler warnings when pack is empty\n    (void)vector_size;\n    if constexpr (sizeof...(TagPack) > 0) {\n      Initialization::mutate_assign<simple_tags>(\n          box, ScriPlusInterpolationManager<ComplexDataVector, TagPack>{\n                   target_number_of_points, vector_size,\n                   std::make_unique<intrp::BarycentricRationalSpanInterpolator>(\n                       2 * target_number_of_points - 1,\n                       2 * target_number_of_points + 2)}...);\n    }\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/ChooseLtsStepSize.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/StepNumberWithinSlab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Rational.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initializes the contents of the `CharacteristicEvolution` component\n * for performing the time evolution of the system, which is the singleton that\n * handles the main evolution system for CCE computations.\n *\n * \\details Sets up the \\ref DataBoxGroup to be ready to perform the\n * time-stepping associated with the CCE system.\n *\n * \\ref DataBoxGroup changes:\n * - Modifies: nothing\n * - Adds:\n *  - `Tags::TimeStepId`\n *  - `Tags::Next<Tags::TimeStepId>`\n *  - `Tags::TimeStep`\n *  - `Tags::Time`\n *  - `Tags::AdaptiveSteppingDiagnostics`\n * ```\n * Tags::HistoryEvolvedVariables<\n * metavariables::evolved_coordinates_variables_tag,\n * db::add_tag_prefix<Tags::dt,\n * metavariables::evolved_coordinates_variables_tag>>\n * ```\n *  -\n * ```\n * Tags::HistoryEvolvedVariables<\n * ::Tags::Variables<metavariables::evolved_swsh_tags>,\n * ::Tags::Variables<metavariables::evolved_swsh_dt_tags>>\n * ```\n * - Removes: nothing\n */\ntemplate <typename EvolvedCoordinatesVariablesTag, typename EvolvedSwshTag,\n          bool local_time_stepping>\nstruct InitializeCharacteristicEvolutionTime {\n  using simple_tags_from_options =\n      tmpl::list<Initialization::Tags::InitialSlabSize<local_time_stepping>,\n                 ::Initialization::Tags::InitialTimeDelta>;\n\n  using const_global_cache_tags = tmpl::list<\n      Tags::CceEvolutionPrefix<::Tags::ConcreteTimeStepper<LtsTimeStepper>>>;\n\n  using evolved_swsh_variables_tag = ::Tags::Variables<EvolvedSwshTag>;\n  using simple_tags = tmpl::list<\n      ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n      ::Tags::Time, ::Tags::StepNumberWithinSlab,\n      ::Tags::AdaptiveSteppingDiagnostics,\n      ::Tags::HistoryEvolvedVariables<EvolvedCoordinatesVariablesTag>,\n      ::Tags::HistoryEvolvedVariables<evolved_swsh_variables_tag>>;\n  using compute_tags =\n      tmpl::transform<time_stepper_ref_tags<LtsTimeStepper>,\n                      tmpl::bind<Tags::CceEvolutionPrefix, tmpl::_1>>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const double initial_time_value = db::get<Tags::StartTime>(box);\n    const double slab_size =\n        db::get<::Initialization::Tags::InitialSlabSize<local_time_stepping>>(\n            box);\n\n    const Slab single_step_slab{initial_time_value,\n                                initial_time_value + slab_size};\n    const Time initial_time = single_step_slab.start();\n    TimeDelta initial_time_step;\n    const double initial_time_delta =\n        db::get<Initialization::Tags::InitialTimeDelta>(box);\n    if constexpr (local_time_stepping) {\n      initial_time_step =\n          choose_lts_step_size(initial_time, initial_time_delta);\n    } else {\n      (void)initial_time_delta;\n      initial_time_step = initial_time.slab().duration();\n    }\n\n    const auto& time_stepper =\n        db::get<Tags::CceEvolutionPrefix<::Tags::TimeStepper<TimeStepper>>>(\n            box);\n\n    const size_t starting_order =\n        visit(\n            []<typename Tag>(\n                const std::pair<tmpl::type_<Tag>, typename Tag::type&&> order) {\n              if constexpr (std::is_same_v<Tag,\n                                           TimeSteppers::Tags::FixedOrder>) {\n                return order.second;\n              } else {\n                return order.second.minimum;\n              }\n            },\n            time_stepper.order()) -\n        time_stepper.number_of_past_steps();\n\n    typename ::Tags::HistoryEvolvedVariables<EvolvedCoordinatesVariablesTag>::\n        type coordinate_history(starting_order);\n\n    typename ::Tags::HistoryEvolvedVariables<evolved_swsh_variables_tag>::type\n        swsh_history(starting_order);\n    Initialization::mutate_assign<tmpl::list<\n        ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n        ::Tags::Time,\n        ::Tags::HistoryEvolvedVariables<EvolvedCoordinatesVariablesTag>,\n        ::Tags::HistoryEvolvedVariables<evolved_swsh_variables_tag>>>(\n        make_not_null(&box), TimeStepId{},\n        TimeStepId{true,\n                   -static_cast<int64_t>(time_stepper.number_of_past_steps()),\n                   initial_time},\n        initial_time_step, initial_time_value, std::move(coordinate_history),\n        std::move(swsh_history));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n/// \\brief The set of actions for use in the CCE evolution system\nnamespace Actions {\n\nnamespace detail {\nCREATE_HAS_TYPE_ALIAS(compute_tags)\nCREATE_HAS_TYPE_ALIAS_V(compute_tags)\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(compute_tags)\n}  // namespace detail\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initializes the main data storage for the  `CharacteristicEvolution`\n * component, which is the singleton that handles the main evolution system for\n * CCE computations.\n *\n * \\details Sets up the \\ref DataBoxGroup to be ready to take data from the\n * worldtube component, calculate initial data, and start the hypersurface\n * computations.\n *\n * \\ref DataBoxGroup changes:\n * - Modifies: nothing\n * - Adds:\n *  - `metavariables::evolved_coordinates_variables_tag`\n *  -\n * ```\n * db::add_tag_prefix<Tags::dt,\n * metavariables::evolved_coordinates_variables_tag>\n * ```\n *  - `Tags::Variables<metavariables::cce_angular_coordinate_tags>`\n *  - `Tags::Variables<metavariables::cce_scri_tags>`\n *  -\n * ```\n * Tags::Variables<tmpl::append<\n * metavariables::cce_integrand_tags,\n * metavariables::cce_integration_independent_tags,\n * metavariables::cce_temporary_equations_tags>>\n * ```\n *  - `Tags::Variables<metavariables::cce_pre_swsh_derivatives_tags>`\n *  - `Tags::Variables<metavariables::cce_transform_buffer_tags>`\n *  - `Tags::Variables<metavariables::cce_swsh_derivative_tags>`\n *  - `Spectral::Swsh::Tags::SwshInterpolator< Tags::CauchyAngularCoords>`\n *  - `Spectral::Swsh::Tags::SwshInterpolator<Tags::PartiallyFlatAngularCoords>`\n * - Removes: nothing\n */\ntemplate <typename Metavariables>\nstruct InitializeCharacteristicEvolutionVariables {\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  using boundary_value_variables_tag = ::Tags::Variables<\n      tmpl::append<typename Metavariables::cce_boundary_communication_tags,\n                   typename Metavariables::cce_gauge_boundary_tags>>;\n  using scri_variables_tag =\n      ::Tags::Variables<typename Metavariables::cce_scri_tags>;\n  using volume_variables_tag = ::Tags::Variables<\n      tmpl::append<typename Metavariables::cce_integrand_tags,\n                   typename Metavariables::cce_integration_independent_tags,\n                   typename Metavariables::cce_temporary_equations_tags>>;\n  using pre_swsh_derivatives_variables_tag =\n      ::Tags::Variables<typename Metavariables::cce_pre_swsh_derivatives_tags>;\n  using transform_buffer_variables_tag =\n      ::Tags::Variables<typename Metavariables::cce_transform_buffer_tags>;\n  using swsh_derivative_variables_tag =\n      ::Tags::Variables<typename Metavariables::cce_swsh_derivative_tags>;\n  using angular_coordinates_variables_tag =\n      ::Tags::Variables<typename Metavariables::cce_angular_coordinate_tags>;\n  using coordinate_variables_tag =\n      typename Metavariables::evolved_coordinates_variables_tag;\n  using dt_coordinate_variables_tag =\n      db::add_tag_prefix<::Tags::dt, coordinate_variables_tag>;\n  using evolved_swsh_variables_tag =\n      ::Tags::Variables<typename Metavariables::evolved_swsh_tags>;\n  using evolved_swsh_dt_variables_tag =\n      db::add_tag_prefix<::Tags::dt, evolved_swsh_variables_tag>;\n  using ccm_tag = ::Tags::Variables<typename Metavariables::ccm_psi0>;\n\n  using simple_tags_for_evolution = tmpl::list<\n      boundary_value_variables_tag, coordinate_variables_tag,\n      dt_coordinate_variables_tag, evolved_swsh_variables_tag,\n      evolved_swsh_dt_variables_tag, angular_coordinates_variables_tag,\n      scri_variables_tag, volume_variables_tag,\n      pre_swsh_derivatives_variables_tag, transform_buffer_variables_tag,\n      swsh_derivative_variables_tag,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::PartiallyFlatAngularCoords>,\n      ccm_tag>;\n  using simple_tags =\n      tmpl::append<StepChoosers::step_chooser_simple_tags<Metavariables, true>,\n                   simple_tags_for_evolution>;\n\n  using compute_tags = tmpl::remove_duplicates<tmpl::join<\n      tmpl::transform<typename Metavariables::cce_step_choosers,\n                      tmpl::bind<detail::get_compute_tags_or_default_t,\n                                 tmpl::_1, tmpl::pin<tmpl::list<>>>>>>;\n\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    initialize_impl(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  template <typename TagList>\n  static void initialize_impl(const gsl::not_null<db::DataBox<TagList>*> box) {\n    const size_t l_max = db::get<Spectral::Swsh::Tags::LMax>(*box);\n    const size_t number_of_radial_points =\n        db::get<Spectral::Swsh::Tags::NumberOfRadialPoints>(*box);\n    const size_t boundary_size =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n    const size_t volume_size = boundary_size * number_of_radial_points;\n    const size_t transform_buffer_size =\n        number_of_radial_points *\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n    Initialization::mutate_assign<simple_tags_for_evolution>(\n        box, typename boundary_value_variables_tag::type{boundary_size},\n        typename coordinate_variables_tag::type{boundary_size},\n        typename dt_coordinate_variables_tag::type{boundary_size},\n        typename evolved_swsh_variables_tag::type{volume_size},\n        typename evolved_swsh_dt_variables_tag::type{volume_size},\n        typename angular_coordinates_variables_tag::type{boundary_size},\n        typename scri_variables_tag::type{boundary_size},\n        typename volume_variables_tag::type{volume_size},\n        typename pre_swsh_derivatives_variables_tag::type{volume_size, 0.0},\n        typename transform_buffer_variables_tag::type{transform_buffer_size,\n                                                      0.0},\n        typename swsh_derivative_variables_tag::type{volume_size, 0.0},\n        Spectral::Swsh::SwshInterpolator{}, Spectral::Swsh::SwshInterpolator{},\n        typename ccm_tag::type{boundary_size});\n  }\n};\n\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/InitializeFirstHypersurface.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/ScriPlusValues.hpp\"\n#include \"IO/Observer/Actions/GetLockPointer.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Given initial boundary data for \\f$J\\f$ and \\f$\\partial_r J\\f$,\n * computes the initial hypersurface quantities \\f$J\\f$ and gauge values.\n *\n * \\details This action is to be called after boundary data has been received,\n * but before the time-stepping evolution loop. So, it should be either late in\n * an initialization phase or early (before a `Actions::Goto` loop or similar)\n * in the `Evolve` phase.\n *\n * Internally, this dispatches to the call function of\n * `Tags::InitializeJ`, which designates a hypersurface initial data generator\n * chosen by input file options, `InitializeGauge`, and\n * `InitializeScriPlusValue<Tags::InertialRetardedTime>` to perform the\n * computations. Refer to the documentation for those mutators for mathematical\n * details.\n */\ntemplate <bool EvolveCcm, typename BoundaryComponent>\nstruct InitializeFirstHypersurface {\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // In some contexts, this action may get re-run (e.g. self-start procedure)\n    // In those cases, we do not want to alter the existing hypersurface data,\n    // so we just exit. However, we do want to re-run the action each time\n    // the self start 'reset's from the beginning\n    if (db::get<::Tags::TimeStepId>(box).slab_number() > 0 or\n        not db::get<::Tags::TimeStepId>(box).is_at_slab_boundary()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    // some initialization schemes need the hdf5_lock so that they can read\n    // their own input data from disk.\n    auto hdf5_lock = Parallel::local_branch(\n                         Parallel::get_parallel_component<\n                             observers::ObserverWriter<Metavariables>>(cache))\n                         ->template local_synchronous_action<\n                             observers::Actions::GetLockPointer<\n                                 observers::Tags::H5FileLock>>();\n    if constexpr (tt::is_a_v<AnalyticWorldtubeBoundary, BoundaryComponent>) {\n      db::mutate_apply<typename InitializeJ::InitializeJ<false>::mutate_tags,\n                       typename InitializeJ::InitializeJ<false>::argument_tags>(\n          db::get<Tags::InitializeJ<false>>(box), make_not_null(&box),\n          make_not_null(hdf5_lock));\n    } else {\n      db::mutate_apply<\n          typename InitializeJ::InitializeJ<EvolveCcm>::mutate_tags,\n          typename InitializeJ::InitializeJ<EvolveCcm>::argument_tags>(\n          db::get<Tags::InitializeJ<EvolveCcm>>(box), make_not_null(&box),\n          make_not_null(hdf5_lock));\n    }\n    db::mutate_apply<InitializeScriPlusValue<Tags::InertialRetardedTime>>(\n        make_not_null(&box), db::get<::Tags::TimeStepId>(box).substep_time());\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/InitializeKleinGordonFirstHypersurface.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Given initial boundary data for the Klein-Gordon variable \\f$\\Psi\\f$,\n * computes its initial hypersurface data.\n *\n * \\details This action is to be called after boundary data has been received,\n * but before the time-stepping evolution loop. So, it should be either late in\n * an initialization phase or early (before a `Actions::Goto` loop or similar)\n * in the `Evolve` phase.\n */\nstruct InitializeKleinGordonFirstHypersurface {\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // In some contexts, this action may get re-run (e.g. self-start procedure)\n    // In those cases, we do not want to alter the existing hypersurface data,\n    // so we just exit. However, we do want to re-run the action each time\n    // the self start 'reset's from the beginning\n    if (db::get<::Tags::TimeStepId>(box).slab_number() > 0 or\n        not db::get<::Tags::TimeStepId>(box).is_at_slab_boundary()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    db::mutate<Tags::KleinGordonPsi>(\n        [](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n               kg_psi_volume,\n           const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_kg_psi,\n           const size_t l_max, const size_t number_of_radial_points) {\n          const DataVector one_minus_y_collocation =\n              1.0 -\n              Spectral::collocation_points<Spectral::Basis::Legendre,\n                                           Spectral::Quadrature::GaussLobatto>(\n                  number_of_radial_points);\n\n          const size_t boundary_size =\n              Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n          for (size_t i = 0; i < number_of_radial_points; i++) {\n            ComplexDataVector angular_view_kg_psi{\n                get(*kg_psi_volume).data().data() + boundary_size * i,\n                boundary_size};\n\n            // this gives psi = (boundary value) / r\n            angular_view_kg_psi =\n                get(boundary_kg_psi).data() * one_minus_y_collocation[i] / 2.;\n          }\n        },\n        make_not_null(&box),\n        db::get<Tags::BoundaryValue<Tags::KleinGordonPsi>>(box),\n        db::get<Tags::LMax>(box), db::get<Tags::NumberOfRadialPoints>(box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Cce::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/InitializeKleinGordonVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n\nnamespace Cce::Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initialize the data storage for the scalar field in the\n * `KleinGordonCharacteristicExtract` component, which is the singleton that\n * handles the main evolution system for Klein-Gordon CCE computations.\n *\n * \\details Sets up the \\ref DataBoxGroup to be ready to take data from the\n * worldtube component, calculate initial data, and start the hypersurface\n * computations.\n *\n * \\ref DataBoxGroup changes:\n * - Modifies: nothing\n * - Adds:\n *  - `Tags::Variables<metavariables::klein_gordon_boundary_communication_tags>`\n *  - `Tags::Variables<metavariables::klein_gordon_gauge_boundary_tags>`\n *  - `Tags::Variables<metavariables::klein_gordon_scri_tags>`\n *  - `Tags::Variables<metavariables::klein_gordon_pre_swsh_derivative_tags>`\n *  - `Tags::Variables<metavariables::klein_gordon_swsh_derivative_tags>`\n *  - `Tags::Variables<metavariables::klein_gordon_transform_buffer_tags>`\n *  - `Tags::Variables<metavariables::klein_gordon_source_tags>`\n *  - `Tags::Variables<metavariables::klein_gordon_cce_integrand_tags>`\n * - Removes: nothing\n */\ntemplate <typename Metavariables>\nstruct InitializeKleinGordonVariables {\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  using klein_gordon_boundary_communication_tags = ::Tags::Variables<\n      typename Metavariables::klein_gordon_boundary_communication_tags>;\n  using klein_gordon_gauge_boundary_tags = ::Tags::Variables<\n      typename Metavariables::klein_gordon_gauge_boundary_tags>;\n  using klein_gordon_scri_tags =\n      ::Tags::Variables<typename Metavariables::klein_gordon_scri_tags>;\n  using klein_gordon_pre_swsh_derivatives_variables_tag = ::Tags::Variables<\n      typename Metavariables::klein_gordon_pre_swsh_derivative_tags>;\n  using klein_gordon_swsh_derivatives_variables_tag = ::Tags::Variables<\n      typename Metavariables::klein_gordon_swsh_derivative_tags>;\n  using klein_gordon_transform_buffer_variables_tag = ::Tags::Variables<\n      typename Metavariables::klein_gordon_transform_buffer_tags>;\n  using klein_gordon_source_variables_tag =\n      ::Tags::Variables<typename Metavariables::klein_gordon_source_tags>;\n  using klein_gordon_integrand_variables_tag = ::Tags::Variables<\n      typename Metavariables::klein_gordon_cce_integrand_tags>;\n\n  using simple_tags =\n      tmpl::list<klein_gordon_boundary_communication_tags,\n                 klein_gordon_gauge_boundary_tags, klein_gordon_scri_tags,\n                 klein_gordon_pre_swsh_derivatives_variables_tag,\n                 klein_gordon_swsh_derivatives_variables_tag,\n                 klein_gordon_transform_buffer_variables_tag,\n                 klein_gordon_source_variables_tag,\n                 klein_gordon_integrand_variables_tag>;\n\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t l_max = db::get<Spectral::Swsh::Tags::LMax>(box);\n    const size_t number_of_radial_points =\n        db::get<Spectral::Swsh::Tags::NumberOfRadialPoints>(box);\n    const size_t boundary_size =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n    const size_t volume_size = boundary_size * number_of_radial_points;\n    const size_t transform_buffer_size =\n        number_of_radial_points *\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box),\n        typename klein_gordon_boundary_communication_tags::type{boundary_size},\n        typename klein_gordon_gauge_boundary_tags::type{boundary_size},\n        typename klein_gordon_scri_tags::type{boundary_size},\n        typename klein_gordon_pre_swsh_derivatives_variables_tag::type{\n            volume_size},\n        typename klein_gordon_swsh_derivatives_variables_tag::type{volume_size},\n        typename klein_gordon_transform_buffer_variables_tag::type{\n            transform_buffer_size},\n        typename klein_gordon_source_variables_tag::type{volume_size},\n        typename klein_gordon_integrand_variables_tag::type{volume_size});\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Cce::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticBoundaryDataManager.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLocalTimeStepping.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n/// \\cond\ntemplate <typename Metavariables>\nstruct H5WorldtubeBoundary;\ntemplate <typename Metavariables>\nstruct AnalyticWorldtubeBoundary;\ntemplate <typename Metavariables>\nstruct KleinGordonH5WorldtubeBoundary;\ntemplate <typename Metavariables>\nstruct GhWorldtubeBoundary;\n/// \\endcond\nnamespace Actions {\n\nnamespace detail {\ntemplate <typename Initializer, typename ManagerTags,\n          typename... BoundaryCommunicationTagsList>\nstruct InitializeWorldtubeBoundaryBase {\n  using simple_tags_from_options = ManagerTags;\n  using const_global_cache_tags = tmpl::list<Tags::LMax>;\n\n  using simple_tags =\n      tmpl::list<::Tags::Variables<BoundaryCommunicationTagsList>...>;\n\n  template <typename DataBoxTagsList, typename... InboxTags,\n            typename ArrayIndex, typename Metavariables, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DataBoxTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if constexpr (std::is_same_v<Tags::AnalyticBoundaryDataManager,\n                                 tmpl::front<ManagerTags>>) {\n      if (dynamic_cast<const Solutions::RobinsonTrautman*>(\n              &(db::get<Tags::AnalyticBoundaryDataManager>(box)\n                    .get_generator())) != nullptr) {\n        if (db::get<Tags::CceEvolutionPrefix<\n                ::Tags::ConcreteTimeStepper<LtsTimeStepper>>>(box)\n                .number_of_substeps() != 1) {\n          ERROR(\n              \"Do not use RobinsonTrautman analytic solution with a \"\n              \"substep-based timestepper. This is to prevent severe slowdowns \"\n              \"in the current RobinsonTrautman implementation. See the \"\n              \"documentation for the RobinsonTrautman solution for details.\");\n        }\n      }\n    }\n    const size_t l_max = db::get<Tags::LMax>(box);\n\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box),\n        Variables<BoundaryCommunicationTagsList>{\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max)}...);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace detail\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Generic action for initializing various worldtube boundary components.\n *\n * \\details See specializations of this class for initialization details for\n * individual worldtube components.\n */\ntemplate <typename WorldtubeComponent>\nstruct InitializeWorldtubeBoundary;\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initializes a H5WorldtubeBoundary\n *\n * \\details Uses:\n * - simple tags from options\n * `Cce::Tags::H5WorldtubeBoundaryDataManager`,\n * - const global cache tag `Cce::Tags::LMax`, `Tags::ExtractionRadiusFromH5`.\n *\n * Databox changes:\n * - Adds:\n *   - `Cce::Tags::H5WorldtubeBoundaryDataManager`\n *   - `Tags::Variables<typename\n * Metavariables::cce_boundary_communication_tags>`\n * - Removes: nothing\n * - Modifies: nothing\n */\ntemplate <typename Metavariables>\nstruct InitializeWorldtubeBoundary<H5WorldtubeBoundary<Metavariables>>\n    : public detail::InitializeWorldtubeBoundaryBase<\n          InitializeWorldtubeBoundary<H5WorldtubeBoundary<Metavariables>>,\n          tmpl::list<Tags::H5WorldtubeBoundaryDataManager>,\n          typename Metavariables::cce_boundary_communication_tags> {\n  using base_type = detail::InitializeWorldtubeBoundaryBase<\n      InitializeWorldtubeBoundary<H5WorldtubeBoundary<Metavariables>>,\n      tmpl::list<Tags::H5WorldtubeBoundaryDataManager>,\n      typename Metavariables::cce_boundary_communication_tags>;\n  using base_type::apply;\n  using typename base_type::simple_tags;\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::ExtractionRadiusFromH5,\n                 Tags::EndTimeFromFile, Tags::StartTimeFromFile>;\n  using typename base_type::simple_tags_from_options;\n};\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initializes a KleinGordonH5WorldtubeBoundary\n *\n * \\details Uses:\n * - simple tags from options\n * `Cce::Tags::H5WorldtubeBoundaryDataManager`,\n * `Cce::Tags::KleinGordonH5WorldtubeBoundaryDataManager`.\n * - const global cache tag `Cce::Tags::LMax`, `Tags::ExtractionRadiusFromH5`.\n *\n * Databox changes:\n * - Adds:\n *   - `Cce::Tags::H5WorldtubeBoundaryDataManager`\n *   - `Cce::Tags::KleinGordonH5WorldtubeBoundaryDataManager`\n *   - `Tags::Variables<typename\n * Metavariables::cce_boundary_communication_tags>`\n *   - `Tags::Variables<typename\n * Metavariables::klein_gordon_boundary_communication_tags>`\n * - Removes: nothing\n * - Modifies: nothing\n */\ntemplate <typename Metavariables>\nstruct InitializeWorldtubeBoundary<\n    KleinGordonH5WorldtubeBoundary<Metavariables>>\n    : public detail::InitializeWorldtubeBoundaryBase<\n          InitializeWorldtubeBoundary<\n              KleinGordonH5WorldtubeBoundary<Metavariables>>,\n          tmpl::list<Tags::H5WorldtubeBoundaryDataManager,\n                     Tags::KleinGordonH5WorldtubeBoundaryDataManager>,\n          typename Metavariables::cce_boundary_communication_tags,\n          typename Metavariables::klein_gordon_boundary_communication_tags> {\n  using base_type = detail::InitializeWorldtubeBoundaryBase<\n      InitializeWorldtubeBoundary<\n          KleinGordonH5WorldtubeBoundary<Metavariables>>,\n      tmpl::list<Tags::H5WorldtubeBoundaryDataManager,\n                 Tags::KleinGordonH5WorldtubeBoundaryDataManager>,\n      typename Metavariables::cce_boundary_communication_tags,\n      typename Metavariables::klein_gordon_boundary_communication_tags>;\n  using base_type::apply;\n  using typename base_type::simple_tags;\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::ExtractionRadiusFromH5,\n                 Tags::EndTimeFromFile, Tags::StartTimeFromFile>;\n  using typename base_type::simple_tags_from_options;\n};\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initializes a GhWorldtubeBoundary\n *\n * \\details Uses:\n * - simple tags from options\n * `Cce::Tags::GhWorldtubeBoundaryDataManager`, `Tags::GhInterfaceManager`\n * - const global cache tags `Tags::LMax`, `Tags::ExtractionRadiusSimple`.\n *\n * Databox changes:\n * - Adds:\n *   - `Tags::Variables<typename\n * Metavariables::cce_boundary_communication_tags>`\n * - Removes: nothing\n * - Modifies: nothing\n */\ntemplate <typename Metavariables>\nstruct InitializeWorldtubeBoundary<GhWorldtubeBoundary<Metavariables>>\n    : public detail::InitializeWorldtubeBoundaryBase<\n          InitializeWorldtubeBoundary<GhWorldtubeBoundary<Metavariables>>,\n          tmpl::list<Tags::GhInterfaceManager,\n                     Tags::SelfStartGhInterfaceManager>,\n          typename Metavariables::cce_boundary_communication_tags> {\n  using base_type = detail::InitializeWorldtubeBoundaryBase<\n      InitializeWorldtubeBoundary<GhWorldtubeBoundary<Metavariables>>,\n      tmpl::list<Tags::GhInterfaceManager, Tags::SelfStartGhInterfaceManager>,\n      typename Metavariables::cce_boundary_communication_tags>;\n  using base_type::apply;\n  using typename base_type::simple_tags;\n\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::ExtractionRadiusSimple, Tags::NoEndTime,\n                 Tags::SpecifiedStartTime>;\n  using typename base_type::simple_tags_from_options;\n};\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initializes an AnalyticWorldtubeBoundary\n *\n * \\details Uses:\n * - simple tags from options\n * `Cce::Tags::AnalyticBoundaryDataManager`,\n * - const global cache tags `Tags::LMax`,\n * `Tags::ExtractionRadiusSimple`.\n *\n * Databox changes:\n * - Adds:\n *   - `Tags::Variables<typename\n *     Metavariables::cce_boundary_communication_tags>`\n * - Removes: nothing\n * - Modifies: nothing\n */\ntemplate <typename Metavariables>\nstruct InitializeWorldtubeBoundary<AnalyticWorldtubeBoundary<Metavariables>>\n    : public detail::InitializeWorldtubeBoundaryBase<\n          InitializeWorldtubeBoundary<AnalyticWorldtubeBoundary<Metavariables>>,\n          tmpl::list<Tags::AnalyticBoundaryDataManager>,\n          typename Metavariables::cce_boundary_communication_tags> {\n  using base_type = detail::InitializeWorldtubeBoundaryBase<\n      InitializeWorldtubeBoundary<AnalyticWorldtubeBoundary<Metavariables>>,\n      tmpl::list<Tags::AnalyticBoundaryDataManager>,\n      typename Metavariables::cce_boundary_communication_tags>;\n  using base_type::apply;\n  using typename base_type::simple_tags;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::ExtractionRadiusSimple,\n                 Tags::SpecifiedEndTime, Tags::SpecifiedStartTime>;\n  using typename base_type::simple_tags_from_options;\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/InsertInterpolationScriData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStep;\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Cce {\nnamespace Actions {\n\nnamespace detail {\ntemplate <typename Tag>\nstruct get_interpolator_argument_tag {\n  using type = Tag;\n};\n\ntemplate <typename Tag>\nstruct get_interpolator_argument_tag<Tags::Du<Tag>> {\n  using type = Tag;\n};\n\ntemplate <typename Tag>\nstruct InsertIntoInterpolationManagerImpl {\n  using return_tags =\n      tmpl::list<Tags::InterpolationManager<ComplexDataVector, Tag>>;\n  using argument_tags =\n      tmpl::list<typename get_interpolator_argument_tag<Tag>::type,\n                 Tags::InertialRetardedTime>;\n  static void apply(\n      const gsl::not_null<ScriPlusInterpolationManager<ComplexDataVector, Tag>*>\n          interpolation_manager,\n      const typename Tag::type& scri_data,\n      const Scalar<DataVector>& inertial_retarded_time) {\n    interpolation_manager->insert_data(get(inertial_retarded_time),\n                                       get(scri_data).data());\n  }\n};\n\ntemplate <typename LhsTag, typename RhsTag>\nstruct InsertIntoInterpolationManagerImpl<::Tags::Multiplies<LhsTag, RhsTag>> {\n  using return_tags = tmpl::list<Tags::InterpolationManager<\n      ComplexDataVector, ::Tags::Multiplies<LhsTag, RhsTag>>>;\n  using argument_tags =\n      tmpl::list<typename get_interpolator_argument_tag<LhsTag>::type, RhsTag,\n                 Tags::InertialRetardedTime>;\n  static void apply(const gsl::not_null<ScriPlusInterpolationManager<\n                        ComplexDataVector, ::Tags::Multiplies<LhsTag, RhsTag>>*>\n                        interpolation_manager,\n                    const typename LhsTag::type& lhs_data,\n                    const typename RhsTag::type& rhs_data,\n                    const Scalar<DataVector>& inertial_retarded_time) {\n    interpolation_manager->insert_data(get(inertial_retarded_time),\n                                       get(lhs_data).data(),\n                                       get(rhs_data).data());\n  }\n};\n\ntemplate <typename Tag, typename ParallelComponent, typename Metavariables>\nvoid output_impl(const size_t observation_l_max, const size_t l_max,\n                 const TimeStepId& time_id, const typename Tag::type& tag_data,\n                 Parallel::GlobalCache<Metavariables>& cache) {\n  std::vector<double> data_to_write(2 * square(observation_l_max + 1) + 1);\n  std::vector<std::string> file_legend;\n  file_legend.reserve(2 * square(observation_l_max + 1) + 1);\n  file_legend.emplace_back(\"time\");\n  for (int l = 0; l <= static_cast<int>(observation_l_max); ++l) {\n    for (int m = -l; m <= l; ++m) {\n      file_legend.push_back(MakeString{} << \"Real Y_\" << l << \",\" << m);\n      file_legend.push_back(MakeString{} << \"Imag Y_\" << l << \",\" << m);\n    }\n  }\n  auto observer_proxy = Parallel::get_parallel_component<\n      observers::ObserverWriter<Metavariables>>(cache)[0];\n  // swsh transform\n  const ComplexModalVector goldberg_modes =\n      Spectral::Swsh::libsharp_to_goldberg_modes(\n          Spectral::Swsh::swsh_transform(l_max, 1, get(tag_data)), l_max)\n          .data();\n\n  data_to_write[0] = time_id.substep_time();\n  for (size_t i = 0; i < square(observation_l_max + 1); ++i) {\n    data_to_write[2 * i + 1] = real(goldberg_modes[i]);\n    data_to_write[2 * i + 2] = imag(goldberg_modes[i]);\n  }\n  Parallel::threaded_action<observers::ThreadedActions::WriteReductionDataRow>(\n      observer_proxy, \"/Cce/\" + db::tag_name<Tag>() + \"_Noninertial\",\n      file_legend, std::make_tuple(data_to_write));\n}\n}  // namespace detail\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Places the data from the current hypersurface necessary to compute\n * `Tag` in the `ScriPlusInterpolationManager` associated with the `Tag`.\n *\n * \\details Adds both the appropriate scri+ value(s) and a number of target\n * inertial times to interpolate of quantity equal to the\n * `InitializationTags::ScriOutputDensity` determined from options, equally\n * spaced between the current time and the next time in the algorithm.\n *\n * Uses:\n * - `::Tags::TimeStepId`\n * - `::Tags::Next<::Tags::TimeStepId>`\n * - `Cce::InitializationTags::ScriOutputDensity`\n * - if `Tag` is `::Tags::Multiplies<Lhs, Rhs>`:\n *   - `Lhs` and `Rhs`\n * - if `Tag` has `Cce::Tags::Du<Argument>`:\n *   - `Argument`\n * - otherwise uses `Tag`\n *\n * \\ref DataBoxGroup changes:\n * - Modifies:\n *   - `Tags::InterpolationManager<ComplexDataVector, Tag>`\n * - Adds: nothing\n * - Removes: nothing\n */\ntemplate <typename Tag, typename BoundaryComponent>\nstruct InsertInterpolationScriData {\n  using const_global_cache_tags = tmpl::flatten<\n      tmpl::list<InitializationTags::ScriOutputDensity,\n                 std::conditional_t<\n                     tt::is_a_v<AnalyticWorldtubeBoundary, BoundaryComponent>,\n                     tmpl::list<Tags::OutputNoninertialNews>, tmpl::list<>>>>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if constexpr(tt::is_a_v<AnalyticWorldtubeBoundary, BoundaryComponent>) {\n      if (db::get<Tags::OutputNoninertialNews>(box) and\n          db::get<::Tags::TimeStepId>(box).substep() == 0 and\n          std::is_same_v<Tag, Tags::News>) {\n        detail::output_impl<Tags::News, ParallelComponent>(\n            db::get<Tags::ObservationLMax>(box), db::get<Tags::LMax>(box),\n            db::get<::Tags::TimeStepId>(box), db::get<Tags::News>(box), cache);\n        db::get<Tags::AnalyticBoundaryDataManager>(box)\n            .template write_news<ParallelComponent>(\n                cache, db::get<::Tags::TimeStepId>(box).substep_time());\n      }\n    }\n    if (db::get<::Tags::TimeStepId>(box).substep() == 0) {\n      // insert the data points into the interpolator.\n      db::mutate_apply<detail::InsertIntoInterpolationManagerImpl<Tag>>(\n          make_not_null(&box));\n\n      const auto& time_span_deque =\n          db::get<Tags::InterpolationManager<ComplexDataVector, Tag>>(box)\n              .get_u_bondi_ranges();\n\n      const double this_time = time_span_deque.back().first;\n      double time_delta_estimate = db::get<::Tags::TimeStep>(box).value();\n      if (time_span_deque.size() > 1) {\n        time_delta_estimate =\n            this_time - time_span_deque[time_span_deque.size() - 2].first;\n      }\n\n      // insert the target times into the interpolator.\n      db::mutate<Tags::InterpolationManager<ComplexDataVector, Tag>>(\n          [&this_time, &time_delta_estimate](\n              const gsl::not_null<\n                  ScriPlusInterpolationManager<ComplexDataVector, Tag>*>\n                  interpolation_manager,\n              const size_t number_of_interpolated_times) {\n            for (size_t i = 0; i < number_of_interpolated_times; ++i) {\n              interpolation_manager->insert_target_time(\n                  this_time +\n                  time_delta_estimate * static_cast<double>(i) /\n                      static_cast<double>(number_of_interpolated_times));\n            }\n          },\n          make_not_null(&box),\n          db::get<InitializationTags::ScriOutputDensity>(box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/PrecomputeKleinGordonSourceVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/SwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n\nnamespace Cce::Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Compute the set of inputs to `ComputeKleinGordonSource`.\n *\n * \\details \\ref DataBoxGroup changes:\n * - Modifies:\n *  - `Tags::Dy<Tags::KleinGordonPsi>`\n *  - `Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::Eth>`\n * - Adds: nothing\n * - Removes: nothing\n */\nstruct PrecomputeKleinGordonSourceVariables {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    mutate_all_pre_swsh_derivatives_for_tag<\n        Tags::KleinGordonSource<Tags::BondiBeta>>(make_not_null(&box));\n    mutate_all_swsh_derivatives_for_tag<Tags::KleinGordonSource<Tags::BondiQ>>(\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Cce::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/Psi0Matching.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/NewmanPenrose.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Calculate \\f$\\Psi_0\\f$ and its radial derivative\n * \\f$\\partial_\\underline{\\lambda}\\Psi_0\\f$ at the inner boundary of\n * the CCE grid.\n *\n * \\details This action is to be called after `Tags::BondiJ` has been updated.\n * Note that `Tags::BondiJ` is in the asymptotically inertial frame, while the\n * returned \\f$\\Psi_0\\f$ and its radial derivative are in the Cauchy frame.\n * \\f$\\underline \\lambda\\f$ is an affine parameter along the null\n * rays generated by \\f$l\\f$, see Eq. (19a) of \\cite Moxon2020gha.\n */\nstruct CalculatePsi0AndDerivAtInnerBoundary {\n  using const_global_cache_tags = tmpl::list<Tags::LMax>;\n  using mutators =\n      tmpl::list<TransformBondiJToCauchyCoords,\n                 PreSwshDerivatives<Tags::Dy<Tags::BondiJCauchyView>>,\n                 PreSwshDerivatives<Tags::Dy<Tags::Dy<Tags::BondiJCauchyView>>>,\n                 VolumeWeyl<Tags::Psi0Match>,\n                 PreSwshDerivatives<Tags::Dy<Tags::Psi0Match>>,\n                 InnerBoundaryWeyl>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    tmpl::for_each<mutators>([&box](auto mutator_v) {\n      using mutator = typename decltype(mutator_v)::type;\n      db::mutate_apply<mutator>(make_not_null(&box));\n    });\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/ReceiveGhWorldtubeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Stores the boundary data from the GH evolution in the\n * `Cce::InterfaceManagers::GhInterfaceManager`, and sends to the\n * `EvolutionComponent` (template argument) if the data fulfills a prior\n * request.\n *\n * \\details If the new data fulfills a prior request submitted to the\n * `Cce::InterfaceManagers::GhInterfaceManager`, this will dispatch the result\n * to `Cce::Actions::SendToEvolution<GhWorldtubeBoundary<Metavariables>,\n * EvolutionComponent>` for sending the processed boundary data to\n * the `EvolutionComponent`.\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - `Tags::GhInterfaceManager`\n */\ntemplate <typename EvolutionComponent, bool DuringSelfStart>\nstruct ReceiveGhWorldtubeData {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const tmpl::conditional_t<DuringSelfStart, TimeStepId,\n                                              LinkedMessageId<double>>\n                        time,\n                    const tnsr::aa<DataVector, 3>& spacetime_metric,\n                    const tnsr::iaa<DataVector, 3>& phi,\n                    const tnsr::aa<DataVector, 3>& pi) {\n    auto insert_gh_data_to_interface_manager =\n        [&spacetime_metric, &phi, &pi, &time,\n         &cache](const auto interface_manager) {\n          interface_manager->insert_gh_data(time, spacetime_metric, phi, pi);\n          const auto gh_data =\n              interface_manager->retrieve_and_remove_first_ready_gh_data();\n          if (static_cast<bool>(gh_data)) {\n            Parallel::simple_action<Actions::SendToEvolution<\n                GhWorldtubeBoundary<Metavariables>, EvolutionComponent>>(\n                Parallel::get_parallel_component<\n                    GhWorldtubeBoundary<Metavariables>>(cache),\n                get<0>(*gh_data), get<1>(*gh_data));\n          }\n        };\n    if constexpr (DuringSelfStart) {\n      db::mutate<Tags::SelfStartGhInterfaceManager>(\n          insert_gh_data_to_interface_manager, make_not_null(&box));\n    } else {\n      db::mutate<Tags::GhInterfaceManager>(insert_gh_data_to_interface_manager,\n                                           make_not_null(&box));\n    }\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/ReceiveWorldtubeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/ReceiveTags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Takes the boundary data needed to perform the CCE linear solves as\n * arguments and puts them in the \\ref DataBoxGroup, updating the\n * `Cce::Tags::BoundaryTime` accordingly.\n *\n * \\details The boundary data is computed by a separate component, and packaged\n * into a `Variables<TagList>` which is sent in the argument\n * of the simple action invocation. The `TimeStepId` is also provided to confirm\n * the time associated with the passed boundary data.\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies:\n *   - All tags in `TagList`\n */\ntemplate <typename Metavariables, typename TagList>\nstruct ReceiveWorldtubeData {\n  using inbox_tags = tmpl::list<Cce::ReceiveTags::BoundaryData<TagList>>;\n\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox = tuples::get<Cce::ReceiveTags::BoundaryData<TagList>>(inboxes);\n    if (inbox.count(db::get<::Tags::TimeStepId>(box)) != 1) {\n      return {Parallel::AlgorithmExecution::Pause,\n              tmpl::index_of<ActionList, ReceiveWorldtubeData>::value};\n    }\n\n    tmpl::for_each<TagList>(\n        [&inbox, &box](auto tag_v) {\n          using tag = typename decltype(tag_v)::type;\n          db::mutate<tag>(\n              [&inbox](const gsl::not_null<typename tag::type*> destination,\n                       const TimeStepId& time) {\n                *destination = get<tag>(inbox[time]);\n              },\n              make_not_null(&box), db::get<::Tags::TimeStepId>(box));\n        });\n    inbox.erase(db::get<::Tags::TimeStepId>(box));\n    return {Parallel::AlgorithmExecution::Continue,\n            tmpl::index_of<ActionList, ReceiveWorldtubeData>::value + 1};\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/RequestBoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Requests boundary data be sent from `WorldtubeBoundaryComponent` to\n * `EvolutionComponent` (template parameters).\n *\n * \\details Calls the simple action\n * `Cce::Actions::BoundaryComputeAndSendToEvolution<WorldtubeBoundaryComponent,\n * EvolutionComponent>` on the `WorldtubeBoundaryComponent`, which performs\n * boundary computations then sends data to the `EvolutionComponent` via\n * `Cce::Actions::ReceiveWorldtubeData`. Requests the current\n * `Tags::TimeStepId`. For the majority of these requests, it's better to issue\n * them as early as possible to maximize the degree of parallelism for the\n * system, so most calls should use `Cce::Actions::RequestNextBoundaryData`,\n * because it can be called the substep prior to when the data will actually be\n * used.\n *\n * Uses:\n *  - DataBox:\n *    - `Tags::TimeStepId`\n *\n * \\ref DataBoxGroup changes\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies: nothing\n */\ntemplate <typename WorldtubeBoundaryComponent, typename EvolutionComponent>\nstruct RequestBoundaryData {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Parallel::simple_action<Actions::BoundaryComputeAndSendToEvolution<\n        WorldtubeBoundaryComponent, EvolutionComponent>>(\n        Parallel::get_parallel_component<WorldtubeBoundaryComponent>(cache),\n        db::get<::Tags::TimeStepId>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Requests boundary data be sent from `WorldtubeBoundaryComponent` to\n * `EvolutionComponent`.\n *\n * \\details Calls the simple action\n * `Cce::Actions::BoundaryComputeAndSendToEvolution<WorldtubeBoundaryComponent,\n * EvolutionComponent>` on the `WorldtubeBoundaryComponent`, which performs\n * boundary computations then sends data to the `EvolutionComponent` via\n * `Cce::Actions::ReceiveWorldtubeData`. Requests the\n * `Tags::Next<Tags::TimeStepId>` (for the next timestep).\n *\n * Uses:\n *  - DataBox:\n *    - `Tags::Next<Tags::TimeStepId>`\n *\n * \\ref DataBoxGroup changes\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies: nothing\n */\ntemplate <typename WorldtubeBoundaryComponent, typename EvolutionComponent>\nstruct RequestNextBoundaryData {\n  using const_global_cache_tags =\n      tmpl::list<typename WorldtubeBoundaryComponent::end_time_tag>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // only request the data if the next step is not after the end time.\n    const double substep_time =\n        db::get<::Tags::Next<::Tags::TimeStepId>>(box).substep_time();\n    const double end_time = db::get<Tags::EndTime>(box);\n    if (substep_time < end_time) {\n      Parallel::simple_action<Actions::BoundaryComputeAndSendToEvolution<\n          WorldtubeBoundaryComponent, EvolutionComponent>>(\n          Parallel::get_parallel_component<WorldtubeBoundaryComponent>(cache),\n          db::get<::Tags::Next<::Tags::TimeStepId>>(box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/ScriObserveInterpolated.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/Actions/ScriObserveInterpolated.hpp\"\n\nnamespace Cce::Actions::detail {\nvoid correct_weyl_scalars_for_inertial_time(\n    const gsl::not_null<Variables<weyl_correction_list>*>\n        weyl_correction_variables) {\n  const auto& psi_4 =\n      get<Tags::Du<Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>>>(\n          *weyl_correction_variables);\n  auto& psi_3 = get<Tags::ScriPlus<Tags::Psi3>>(*weyl_correction_variables);\n  auto& psi_2 = get<Tags::ScriPlus<Tags::Psi2>>(*weyl_correction_variables);\n  auto& psi_1 = get<Tags::ScriPlus<Tags::Psi1>>(*weyl_correction_variables);\n  auto& psi_0 = get<Tags::ScriPlus<Tags::Psi0>>(*weyl_correction_variables);\n  // note the variable `eth_u` corresponds to $\\eth u^\\prime$ in the\n  // documentation\n  const auto& eth_u =\n      get<Tags::EthInertialRetardedTime>(*weyl_correction_variables);\n  get(psi_0) += 2.0 * get(eth_u) * get(psi_1) +\n               0.75 * square(get(eth_u)) * get(psi_2) +\n               0.5 * pow<3>(get(eth_u)) * get(psi_3) +\n               0.0625 * pow<4>(get(eth_u)) * get(psi_4);\n  get(psi_1) += 1.5 * get(eth_u) * get(psi_2) +\n                0.75 * square(get(eth_u)) * get(psi_3) +\n                0.125 * pow<3>(get(eth_u)) * get(psi_4);\n  get(psi_2) +=\n      get(eth_u) * get(psi_3) + 0.25 * square(get(eth_u)) * get(psi_4);\n  get(psi_3) += 0.5 * get(eth_u) * get(psi_4);\n}\n}  // namespace Cce::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/ScriObserveInterpolated.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Evolution/Systems/Cce/Actions/WriteScriBondiQuantities.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/ScriPlusInterpolationManager.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n/// \\cond\ntemplate <typename Metavariables>\nstruct AnalyticWorldtubeBoundary;\n/// \\endcond\nnamespace Actions {\nnamespace detail {\n// Provide a nicer name for the output h5 files for some of the uglier\n// combinations we need\ntemplate <typename Tag>\nstruct ScriOutput {\n  static std::string name() { return db::tag_name<Tag>(); }\n};\ntemplate <typename Tag>\nstruct ScriOutput<Tags::ScriPlus<Tag>> {\n  static std::string name() { return pretty_type::short_name<Tag>(); }\n};\ntemplate <>\nstruct ScriOutput<Tags::Du<Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>>> {\n  static std::string name() { return \"Psi4\"; }\n};\n\nusing weyl_correction_list =\n    tmpl::list<Tags::Du<Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>>,\n               Tags::ScriPlus<Tags::Psi3>, Tags::ScriPlus<Tags::Psi2>,\n               Tags::ScriPlus<Tags::Psi1>, Tags::ScriPlus<Tags::Psi0>,\n               Tags::EthInertialRetardedTime>;\n\nvoid correct_weyl_scalars_for_inertial_time(\n    gsl::not_null<Variables<weyl_correction_list>*> weyl_correction_variables);\n}  // namespace detail\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Checks the interpolation managers and if they are ready, performs the\n * interpolation and sends the data to file.\n *\n * \\details This uses the `ScriPlusInterpolationManager` to perform the\n * interpolations of all requested scri quantities (determined by\n * `scri_values_to_observe` in the metavariables), and write them to disk using\n * `observers::threadedActions::WriteSimpleData`. When using an analytic\n * worldtube solution, this action also uses the `AnalyticBoundaryDataManager`\n * to output the expected News value at the appropriate asymptotically inertial\n * time.\n *\n * \\note This action also uses the `Tags::EthInertialRetardedTime`, interpolated\n * to the inertial frame, to perform the coordinate transformations presented in\n * \\cite Boyle:2015nqa to the Weyl scalars after interpolation. For our\n * formulas, we need to adjust the signs and factors of two to be compatible\n * with our definitions of \\f$\\eth\\f$ and choice of Newman-Penrose tetrad.\n *\n * \\f{align*}{\n * \\Psi_0^{\\prime (5)}\n * =&  \\Psi_0^{(5)} + 2 \\eth u^\\prime \\Psi_1^{(4)}\n * + \\frac{3}{4} \\left(\\eth u^\\prime\\right)^2 \\Psi_2^{(3)}\n * + \\frac{1}{2} \\left( \\eth u^\\prime\\right)^3  \\Psi_3^{(2)}\n * + \\frac{1}{16} \\left(\\eth u^\\prime\\right)^4 \\Psi_4^{(1)}, \\\\\n * \\Psi_1^{\\prime (4)}\n * =&  \\Psi_1^{(4)} + \\frac{3}{2} \\eth u^\\prime \\Psi_2^{(3)}\n * + \\frac{3}{4} \\left(\\eth u^\\prime\\right)^2  \\Psi_3^{(2)}\n * + \\frac{1}{8} \\left(\\eth u^\\prime\\right)^3 \\Psi_4^{(1)}, \\\\\n * \\Psi_2^{\\prime (3)}\n * =&  \\Psi_2^{(3)}\n * + \\eth u^\\prime  \\Psi_3^{(2)}\n * + \\frac{1}{4} \\left(\\eth  u^\\prime\\right)^2 \\Psi_4^{(1)}, \\\\\n * \\Psi_3^{\\prime (2)}\n * =& \\Psi_3^{(2)} + \\frac{1}{2} \\eth u^{\\prime} \\Psi_4^{ (1)}, \\\\\n * \\Psi_4^{\\prime (1)}\n * =& \\Psi_4^{(1)}.\n * \\f}\n *\n * \\note If \\p WriteSynchronously is true, then a local synchronous action will\n * be used to write the News value rather than a threaded action.\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies: `InterpolagionManager<ComplexDataVector, Tag>` for each `Tag` in\n * `Metavariables::scri_values_to_observe`\n */\ntemplate <typename ObserverWriterComponent, typename BoundaryComponent,\n          bool WriteSynchronously = true>\nstruct ScriObserveInterpolated {\n  using const_global_cache_tags = tmpl::flatten<\n      tmpl::list<Tags::ObservationLMax,\n                 std::conditional_t<\n                     tt::is_a_v<AnalyticWorldtubeBoundary, BoundaryComponent>,\n                     tmpl::list<Tags::OutputNoninertialNews>, tmpl::list<>>>>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t observation_l_max = db::get<Tags::ObservationLMax>(box);\n    const size_t l_max = db::get<Tags::LMax>(box);\n    const double extraction_radius =\n        Parallel::get<Tags::ExtractionRadius>(cache);\n    const std::string subfile_name =\n        MakeString{} << \"SpectreR\" << std::setfill('0') << std::setw(4)\n                     << std::lround(extraction_radius);\n    ComplexModalVector goldberg_modes{square(l_max + 1)};\n\n    // alternative for the coordinate transformation getting scri+ values of the\n    // weyl scalars:\n    // need to obtain the eth of the inertial retarded time, each of the Weyl\n    // scalars, and then we'll perform a transformation on that temporary\n    // variables object, then output.\n    // it won't be as general, but that's largely fine. The main frustration is\n    // the loss of precision.\n    Variables<detail::weyl_correction_list> corrected_scri_plus_weyl{\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n    while (\n        db::get<Tags::InterpolationManager<\n            ComplexDataVector,\n            tmpl::front<typename Metavariables::scri_values_to_observe>>>(box)\n            .first_time_is_ready_to_interpolate()) {\n      // first get the weyl scalars and correct them\n      double interpolation_time = 0.0;\n      tmpl::for_each<detail::weyl_correction_list>(\n          [&interpolation_time, &corrected_scri_plus_weyl, &box](auto tag_v) {\n            using tag = typename decltype(tag_v)::type;\n            std::pair<double, ComplexDataVector> interpolation;\n            db::mutate<Tags::InterpolationManager<ComplexDataVector, tag>>(\n                [&interpolation](\n                    const gsl::not_null<\n                        ScriPlusInterpolationManager<ComplexDataVector, tag>*>\n                        interpolation_manager) {\n                  interpolation =\n                      interpolation_manager->interpolate_and_pop_first_time();\n                },\n                make_not_null(&box));\n            interpolation_time = interpolation.first;\n            get(get<tag>(corrected_scri_plus_weyl)).data() =\n                interpolation.second;\n          });\n\n      detail::correct_weyl_scalars_for_inertial_time(\n          make_not_null(&corrected_scri_plus_weyl));\n\n      // add them to the data to write\n      std::unordered_map<std::string, std::vector<double>> data_to_write{};\n      tmpl::for_each<detail::weyl_correction_list>(\n          [&data_to_write, &corrected_scri_plus_weyl, &interpolation_time,\n           &observation_l_max, &l_max, &goldberg_modes](auto tag_v) {\n            using tag = typename decltype(tag_v)::type;\n            if constexpr (tmpl::list_contains_v<\n                              typename Metavariables::scri_values_to_observe,\n                              tag>) {\n              data_to_write[detail::ScriOutput<tag>::name()] =\n                  transform_nodal_data(\n                      make_not_null(&goldberg_modes),\n                      get(get<tag>(corrected_scri_plus_weyl)).data(),\n                      interpolation_time, l_max, observation_l_max, tag{});\n            }\n          });\n\n      // then do the interpolation and add the rest of the tags.\n      tmpl::for_each<\n          tmpl::list_difference<typename Metavariables::scri_values_to_observe,\n                                detail::weyl_correction_list>>(\n          [&box, &data_to_write, &observation_l_max, &l_max,\n           &goldberg_modes](auto tag_v) {\n            using tag = typename decltype(tag_v)::type;\n            std::pair<double, ComplexDataVector> interpolation;\n            db::mutate<Tags::InterpolationManager<ComplexDataVector, tag>>(\n                [&interpolation](\n                    const gsl::not_null<\n                        ScriPlusInterpolationManager<ComplexDataVector, tag>*>\n                        interpolation_manager) {\n                  interpolation =\n                      interpolation_manager->interpolate_and_pop_first_time();\n                },\n                make_not_null(&box));\n            data_to_write[detail::ScriOutput<tag>::name()] =\n                transform_nodal_data(make_not_null(&goldberg_modes),\n                                     interpolation.second, interpolation.first,\n                                     l_max, observation_l_max, tag{});\n          });\n\n      auto observer_proxy =\n          Parallel::get_parallel_component<ObserverWriterComponent>(cache)[0];\n      if constexpr (WriteSynchronously) {\n        Parallel::local_synchronous_action<\n            Cce::Actions::WriteScriBondiQuantities>(\n            observer_proxy, cache, subfile_name, observation_l_max,\n            std::move(data_to_write));\n      } else {\n        Parallel::threaded_action<Cce::Actions::WriteScriBondiQuantities>(\n            observer_proxy, subfile_name, observation_l_max,\n            std::move(data_to_write));\n      }\n\n      // output the expected news associated with the time value interpolated at\n      // scri+.\n      if constexpr (tmpl::list_contains_v<DbTags,\n                                          Tags::AnalyticBoundaryDataManager>) {\n        if (not db::get<Tags::OutputNoninertialNews>(box)) {\n          db::get<Tags::AnalyticBoundaryDataManager>(box)\n              .template write_news<ParallelComponent>(cache,\n                                                      interpolation_time);\n        }\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n private:\n  template <typename Tag>\n  static std::vector<double> transform_nodal_data(\n      const gsl::not_null<ComplexModalVector*> goldberg_mode_buffer,\n      const ComplexDataVector& data, const double time, const size_t l_max,\n      const size_t observation_l_max, const Tag& /*meta*/) {\n    constexpr int Spin = Tag::type::type::spin;\n    const SpinWeighted<ComplexDataVector, Spin> to_transform;\n    make_const_view(make_not_null(&to_transform.data()), data, 0, data.size());\n    SpinWeighted<ComplexModalVector, Spin> goldberg_modes;\n    goldberg_modes.set_data_ref(goldberg_mode_buffer);\n    Spectral::Swsh::libsharp_to_goldberg_modes(\n        make_not_null(&goldberg_modes),\n        Spectral::Swsh::swsh_transform(l_max, 1, to_transform), l_max);\n\n    std::vector<double> modal_data(2 * square(observation_l_max + 1) + 1);\n\n    modal_data[0] = time;\n    for (size_t i = 0; i < square(observation_l_max + 1); ++i) {\n      modal_data[2 * i + 1] = real(goldberg_modes.data()[i]);\n      modal_data[2 * i + 2] = imag(goldberg_modes.data()[i]);\n    }\n\n    return modal_data;\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/SendGhVarsToCce.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace Cce {\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// \\brief Interpolates and sends points to the `CceWorldtubeTarget`\n///\n/// This is invoked on DgElementArray for the GH system.\n/// This action should appear only in the self-start action list, and\n/// is an unfortunate hack needed by the contradictory constraints\n/// of a locally-stepped CCE system and the events and dense triggers\n/// during the self start procedure.\ntemplate <typename CceWorltubeTargetTag>\nstruct SendGhVarsToCce {\n  template <typename DbTags, typename Metavariables, typename... InboxTags,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const component) {\n    // not used by interpolation\n    const Event::ObservationValue observation_value{};\n    auto interpolate_event = intrp::Events::InterpolateWithoutInterpComponent<\n        Metavariables::volume_dim, CceWorltubeTargetTag,\n        typename CceWorltubeTargetTag::vars_to_interpolate_to_target>{};\n    ::apply(interpolate_event,\n            make_observation_box<db::AddComputeTags<\n                Events::Tags::ObserverMeshCompute<Metavariables::volume_dim>>>(\n                make_not_null(&box)),\n            cache, array_index, component, observation_value);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/TimeManagement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Terminates if the current `::Tags::TimeStepId` has time value later or\n * equal to `Tags::EndTime`.\n *\n * \\details Uses:\n * - DataBox:\n *   - `Cce::Tags::EndTime`\n *   - `Tags::TimeStepId`\n *\n * \\ref DataBoxGroup changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies: nothing\n *\n */\nstruct ExitIfEndTimeReached {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {db::get<::Tags::TimeStepId>(box).substep_time() >=\n                    db::get<Tags::EndTime>(box)\n                ? Parallel::AlgorithmExecution::Pause\n                : Parallel::AlgorithmExecution::Continue,\n            std::nullopt};\n  }\n};\n\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/UpdateGauge.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Updates all of the gauge quantities associated with the additional\n * regularity-preserving gauge transformation on the boundaries for a new set of\n * Cauchy and partially flat Bondi-like coordinates.\n *\n * \\details This action is to be called after `Tags::CauchyCartesianCoords`\n * and `Tags::PartiallyFlatCartesianCoords` have been updated, typically via a\n * time step of a set of coordinate evolution equations. It prepares the\n * gauge quantities in the \\ref DataBoxGroup for calls to the individual\n * `GaugeAdjustedBoundaryValue` specializations.\n *\n * Internally, this dispatches to `GaugeUpdateAngularFromCartesian`,\n * `GaugeUpdateJacobianFromCoordinates`, `GaugeUpdateInterpolator`, and\n * `GaugeUpdateOmega` to perform the computations. Refer to the documentation\n * for those mutators for mathematical details.\n */\ntemplate <bool EvolveCcm>\nstruct UpdateGauge {\n  using const_global_cache_tags = tmpl::list<Tags::LMax>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n        make_not_null(&box));\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n        Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n        make_not_null(&box));\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n        make_not_null(&box));\n    db::mutate_apply<\n        GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                         Tags::PartiallyFlatGaugeOmega>>(make_not_null(&box));\n\n    if constexpr (EvolveCcm) {\n      db::mutate_apply<\n          GaugeUpdateAngularFromCartesian<Tags::PartiallyFlatAngularCoords,\n                                          Tags::PartiallyFlatCartesianCoords>>(\n          make_not_null(&box));\n      db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n          Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n          Tags::PartiallyFlatAngularCoords,\n          Tags::PartiallyFlatCartesianCoords>>(make_not_null(&box));\n      db::mutate_apply<\n          GaugeUpdateInterpolator<Tags::PartiallyFlatAngularCoords>>(\n          make_not_null(&box));\n      db::mutate_apply<GaugeUpdateOmega<Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n                                        Tags::CauchyGaugeOmega>>(\n          make_not_null(&box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Actions/WriteScriBondiQuantities.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Cce.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce::Actions {\n/*!\n * \\brief Write a single row of data into an `h5::Cce` subfile within an H5\n * file.\n *\n * \\details This action can be called as either a threaded action or a local\n * synchronous action. In both cases, invoke this action on the\n * `observers::ObserverWriter` component on node 0. You must pass the following\n * arguments when invoking this action\n *\n * - `subfile_name`: the name of the `h5::Cce` subfile in the HDF5 file.\n * - `l_max`: the number of spherical harmonics of the data\n * - `data`: a `std::unordered_map<std::string, std::vector<double>>` where the\n *   keys are the names of all the bondi quantities to write and the data are\n *   the spherical harmonic coefficients. See `h5::Cce` for exactly what names\n *   need to be used and the layout of the data.\n *\n * \\note If you want to write data into an `h5::Dat` file, use\n * `observers::ThreadedActions::WriteReductionDataRow`.\n */\nstruct WriteScriBondiQuantities {\n  /// \\brief The apply call for the threaded action\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> node_lock,\n                    const std::string& subfile_name, const size_t l_max,\n                    std::unordered_map<std::string, std::vector<double>> data) {\n    apply<ParallelComponent>(box, node_lock, cache, subfile_name, l_max,\n                             std::move(data));\n  }\n\n  // The local synchronous action\n  using return_type = void;\n\n  /// \\brief The apply call for the local synchronous action\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables>\n  static return_type apply(\n      db::DataBox<DbTagList>& box,\n      const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const std::string& subfile_name, const size_t l_max,\n      std::unordered_map<std::string, std::vector<double>> data) {\n    auto& reduction_file_lock =\n        db::get_mutable_reference<observers::Tags::H5FileLock>(\n            make_not_null(&box));\n    const std::lock_guard hold_lock(reduction_file_lock);\n\n    // Make sure all data is the proper size\n    ASSERT(\n        alg::all_of(data,\n                    [&l_max](const auto& name_and_data) {\n                      return name_and_data.second.size() ==\n                             2 * square(l_max + 1) + 1;\n                    }),\n        \"Some data sent to WriteScriBondiQuantities is not of the proper size \"\n            << 2 * square(l_max + 1) + 1);\n\n    const std::string input_source = observers::input_source_from_cache(cache);\n    const std::string& reduction_file_prefix =\n        Parallel::get<observers::Tags::ReductionFileName>(cache);\n    h5::H5File<h5::AccessType::ReadWrite> h5file(reduction_file_prefix + \".h5\",\n                                                 true, input_source);\n    constexpr size_t version_number = 0;\n    auto& cce_file =\n        h5file.try_insert<h5::Cce>(subfile_name, l_max, version_number);\n    cce_file.append(data);\n  }\n};\n}  // namespace Cce::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticBoundaryDataManager.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticBoundaryDataManager.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n\nnamespace Cce {\nAnalyticBoundaryDataManager::AnalyticBoundaryDataManager(\n    const size_t l_max, const double extraction_radius,\n    std::unique_ptr<Solutions::WorldtubeData> generator)\n    : l_max_{l_max},\n      generator_{std::move(generator)},\n      extraction_radius_{extraction_radius} {}\n\nbool AnalyticBoundaryDataManager::populate_hypersurface_boundary_data(\n    const gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        boundary_data_variables,\n    const double time) const {\n  const auto boundary_tuple = generator_->variables(\n      l_max_, time,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>{});\n  const auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_tuple);\n  const auto& pi = get<gh::Tags::Pi<DataVector, 3>>(boundary_tuple);\n  const auto& phi = get<gh::Tags::Phi<DataVector, 3>>(boundary_tuple);\n  create_bondi_boundary_data(boundary_data_variables, phi, pi, spacetime_metric,\n                             extraction_radius_, l_max_);\n  return true;\n}\n\nvoid AnalyticBoundaryDataManager::pup(PUP::er& p) {\n  p | l_max_;\n  p | extraction_radius_;\n  p | generator_;\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticBoundaryDataManager.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace Cce {\nnamespace Tags {\n/// \\cond\nstruct ObservationLMax;\n/// \\endcond\n}  // namespace Tags\n\n/// A boundary data manager that constructs the desired boundary data into\n/// the `Variables` from the data provided by the analytic solution.\nclass AnalyticBoundaryDataManager {\n public:\n  // charm needs an empty constructor.\n  AnalyticBoundaryDataManager() = default;\n\n  AnalyticBoundaryDataManager(\n      size_t l_max, double extraction_radius,\n      std::unique_ptr<Solutions::WorldtubeData> generator);\n\n  /*!\n   * \\brief Update the `boundary_data_variables` entries for all tags in\n   * `Tags::characteristic_worldtube_boundary_tags` to the boundary data from\n   * the analytic solution at  `time`.\n   *\n   * \\details This class retrieves metric boundary data from the\n   * `Cce::Solutions::WorldtubeData` derived class that represents an analytic\n   * solution, then dispatches to `Cce::create_bondi_boundary_data()` to\n   * construct the Bondi values into the provided `Variables`\n   */\n  bool populate_hypersurface_boundary_data(\n      gsl::not_null<Variables<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n          boundary_data_variables,\n      double time) const;\n\n  /// Use `observers::ThreadedActions::WriteSimpleData` to output the expected\n  /// news at `time` from the analytic data to dataset `/expected_news.dat`.\n  template <typename ParallelComponent, typename Metavariables>\n  void write_news(Parallel::GlobalCache<Metavariables>& cache,\n                  double time) const;\n\n  size_t get_l_max() const { return l_max_; }\n\n  const Solutions::WorldtubeData& get_generator() const { return *generator_; }\n\n  /// Serialization for Charm++.\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  size_t l_max_ = 0;\n  std::unique_ptr<Solutions::WorldtubeData> generator_;\n  double extraction_radius_ = std::numeric_limits<double>::signaling_NaN();\n};\n\ntemplate <typename ParallelComponent, typename Metavariables>\nvoid AnalyticBoundaryDataManager::write_news(\n    Parallel::GlobalCache<Metavariables>& cache, const double time) const {\n  const auto news = get<Tags::News>(\n      generator_->variables(l_max_, time, tmpl::list<Tags::News>{}));\n  const size_t observation_l_max = Parallel::get<Tags::ObservationLMax>(cache);\n  std::vector<double> data_to_write(2 * square(observation_l_max + 1) + 1);\n  std::vector<std::string> file_legend;\n  file_legend.reserve(2 * square(observation_l_max + 1) + 1);\n  file_legend.emplace_back(\"time\");\n  for (int i = 0; i <= static_cast<int>(observation_l_max); ++i) {\n    for (int j = -i; j <= i; ++j) {\n      file_legend.push_back(MakeString{} << \"Real Y_\" << i << \",\" << j);\n      file_legend.push_back(MakeString{} << \"Imag Y_\" << i << \",\" << j);\n    }\n  }\n  const ComplexModalVector goldberg_modes =\n      Spectral::Swsh::libsharp_to_goldberg_modes(\n          Spectral::Swsh::swsh_transform(l_max_, 1, get(news)), l_max_)\n          .data();\n  data_to_write[0] = time;\n  for (size_t i = 0; i < square(observation_l_max + 1); ++i) {\n    data_to_write[2 * i + 1] = real(goldberg_modes[i]);\n    data_to_write[2 * i + 2] = imag(goldberg_modes[i]);\n  }\n  auto observer_proxy = Parallel::get_parallel_component<\n      observers::ObserverWriter<Metavariables>>(cache)[0];\n  const std::string prefix =\n      generator_->use_noninertial_news() ? \"Noninertial_\" : \"\";\n  Parallel::threaded_action<observers::ThreadedActions::WriteReductionDataRow>(\n      observer_proxy, \"/Cce/News_\" + prefix + \"expected\"s, file_legend,\n      std::make_tuple(data_to_write));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/BouncingBlackHole.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/BouncingBlackHole.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/BouncingBlackHole.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\n\nBouncingBlackHole::BouncingBlackHole(const double amplitude,\n                                     const double extraction_radius,\n                                     const double mass, const double period)\n    : WorldtubeData(extraction_radius),\n      amplitude_{amplitude},\n      mass_{mass},\n      frequency_{2.0 * M_PI / period} {}\n\nstd::unique_ptr<WorldtubeData> BouncingBlackHole::get_clone() const {\n  return std::make_unique<BouncingBlackHole>(*this);\n}\n\nvoid BouncingBlackHole::variables_impl(\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n    const size_t l_max, const double time,\n    tmpl::type_<gr::Tags::SpacetimeMetric<DataVector, 3>> /*meta*/) const {\n  const auto& cartesian_coordinates =\n      cache_or_compute<Tags::CauchyCartesianCoords>(l_max, time);\n\n  const double dt_adjusted_x_coordinate = 4.0 * amplitude_ * frequency_ *\n                                          cos(frequency_ * time) *\n                                          pow<3>(sin(frequency_ * time));\n\n  const DataVector adjusted_x_coordinate =\n      amplitude_ * pow<4>(sin(frequency_ * time)) +\n      get<0>(cartesian_coordinates);\n\n  const DataVector r = sqrt(square(adjusted_x_coordinate) +\n                            square(get<1>(cartesian_coordinates)) +\n                            square(get<2>(cartesian_coordinates)));\n\n  const DataVector inverse_r_cubed = 1.0 / pow<3>(r);\n\n  get<0, 0>(*spacetime_metric) =\n      -1.0 + 2.0 * mass_ / r +\n      square(dt_adjusted_x_coordinate) *\n          (1.0 +\n           2.0 * mass_ * square(adjusted_x_coordinate) * inverse_r_cubed) +\n      4.0 * mass_ * dt_adjusted_x_coordinate * adjusted_x_coordinate /\n          square(r);\n\n  get<0, 1>(*spacetime_metric) =\n      dt_adjusted_x_coordinate +\n      2.0 * mass_ * adjusted_x_coordinate / square(r) +\n      2.0 * mass_ * dt_adjusted_x_coordinate * square(adjusted_x_coordinate) *\n          inverse_r_cubed;\n\n  for (size_t i = 1; i < 3; ++i) {\n    spacetime_metric->get(0, i + 1) =\n        2.0 * mass_ * (dt_adjusted_x_coordinate * adjusted_x_coordinate + r) *\n        cartesian_coordinates.get(i) * inverse_r_cubed;\n\n    spacetime_metric->get(i + 1, i + 1) =\n        1.0 +\n        2.0 * mass_ * square(cartesian_coordinates.get(i)) * inverse_r_cubed;\n\n    spacetime_metric->get(1, i + 1) = 2.0 * mass_ * adjusted_x_coordinate *\n                                      cartesian_coordinates.get(i) *\n                                      inverse_r_cubed;\n  }\n\n  get<1, 1>(*spacetime_metric) =\n      1.0 + 2.0 * mass_ * square(adjusted_x_coordinate) * inverse_r_cubed;\n\n  get<2, 3>(*spacetime_metric) = 2.0 * mass_ * get<1>(cartesian_coordinates) *\n                                 get<2>(cartesian_coordinates) *\n                                 inverse_r_cubed;\n}\n\nvoid BouncingBlackHole::variables_impl(\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> dt_spacetime_metric,\n    const size_t l_max, const double time,\n    tmpl::type_<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>> /*meta*/)\n    const {\n  const auto& cartesian_coordinates =\n      cache_or_compute<Tags::CauchyCartesianCoords>(l_max, time);\n\n  const double dt_adjusted_x_coordinate = 4.0 * amplitude_ * frequency_ *\n                                          cos(frequency_ * time) *\n                                          pow<3>(sin(frequency_ * time));\n  const double dt_dt_adjusted_x_coordinate =\n      4.0 * amplitude_ * square(frequency_) *\n      (3.0 * square(cos(frequency_ * time)) * square(sin(frequency_ * time)) -\n       pow<4>(sin(frequency_ * time)));\n\n  const DataVector adjusted_x_coordinate =\n      amplitude_ * pow<4>(sin(frequency_ * time)) +\n      get<0>(cartesian_coordinates);\n  const DataVector r = sqrt(square(adjusted_x_coordinate) +\n                            square(get<1>(cartesian_coordinates)) +\n                            square(get<2>(cartesian_coordinates)));\n  const DataVector inverse_r_cubed = 1.0 / pow<3>(r);\n\n  get<0, 0>(*dt_spacetime_metric) =\n      2.0 * dt_dt_adjusted_x_coordinate * dt_adjusted_x_coordinate *\n          (2.0 * mass_ * square(adjusted_x_coordinate) * inverse_r_cubed +\n           1.0) +\n      2.0 * dt_dt_adjusted_x_coordinate * 2.0 * mass_ * adjusted_x_coordinate /\n          square(r) +\n      pow<3>(dt_adjusted_x_coordinate) *\n          (4.0 * mass_ * adjusted_x_coordinate * inverse_r_cubed -\n           6.0 * mass_ * pow<3>(adjusted_x_coordinate) / pow<5>(r)) +\n      2.0 * square(dt_adjusted_x_coordinate) *\n          (2.0 * mass_ / square(r) -\n           4.0 * mass_ * square(adjusted_x_coordinate) / pow<4>(r)) -\n      dt_adjusted_x_coordinate * 2.0 * mass_ * adjusted_x_coordinate *\n          inverse_r_cubed;\n\n  get<0, 1>(*dt_spacetime_metric) =\n      dt_dt_adjusted_x_coordinate +\n      2.0 * mass_ / square(r) *\n          (dt_adjusted_x_coordinate -\n           2.0 * square(adjusted_x_coordinate) * dt_adjusted_x_coordinate /\n               square(r) +\n           square(adjusted_x_coordinate) * dt_dt_adjusted_x_coordinate / r +\n           2.0 * adjusted_x_coordinate * square(dt_adjusted_x_coordinate) / r -\n           3.0 * pow<3>(adjusted_x_coordinate) *\n               square(dt_adjusted_x_coordinate) * inverse_r_cubed);\n\n  for (size_t i = 1; i < 3; ++i) {\n    dt_spacetime_metric->get(0, i + 1) =\n        2.0 * mass_ * cartesian_coordinates.get(i) * inverse_r_cubed *\n        (dt_dt_adjusted_x_coordinate * adjusted_x_coordinate +\n         square(dt_adjusted_x_coordinate) -\n         2.0 * adjusted_x_coordinate * dt_adjusted_x_coordinate / r -\n         3.0 * square(adjusted_x_coordinate) *\n             square(dt_adjusted_x_coordinate) / square(r));\n\n    dt_spacetime_metric->get(1, i + 1) =\n        2.0 * mass_ * dt_adjusted_x_coordinate * cartesian_coordinates.get(i) *\n        inverse_r_cubed *\n        (1.0 - 3.0 * square(adjusted_x_coordinate) / square(r));\n\n    dt_spacetime_metric->get(i + 1, i + 1) =\n        -6.0 * mass_ * adjusted_x_coordinate *\n        square(cartesian_coordinates.get(i)) * dt_adjusted_x_coordinate /\n        pow<5>(r);\n  }\n  get<1, 1>(*dt_spacetime_metric) =\n      2.0 * mass_ * adjusted_x_coordinate * dt_adjusted_x_coordinate *\n      inverse_r_cubed * (2.0 - 3.0 * square(adjusted_x_coordinate) / square(r));\n\n  get<2, 3>(*dt_spacetime_metric) =\n      -6.0 * mass_ * adjusted_x_coordinate * get<1>(cartesian_coordinates) *\n      get<2>(cartesian_coordinates) * dt_adjusted_x_coordinate / pow<5>(r);\n}\n\nvoid BouncingBlackHole::variables_impl(\n    const gsl::not_null<tnsr::iaa<DataVector, 3>*> d_spacetime_metric,\n    const size_t l_max, const double time,\n    tmpl::type_<gh::Tags::Phi<DataVector, 3>> /*meta*/) const {\n  const auto& cartesian_coordinates =\n      cache_or_compute<Tags::CauchyCartesianCoords>(l_max, time);\n\n  const double dt_adjusted_x_coordinate = 4.0 * amplitude_ * frequency_ *\n                                          cos(frequency_ * time) *\n                                          pow<3>(sin(frequency_ * time));\n\n  const DataVector adjusted_x_coordinate =\n      amplitude_ * pow<4>(sin(frequency_ * time)) +\n      get<0>(cartesian_coordinates);\n  const DataVector r = sqrt(square(adjusted_x_coordinate) +\n                            square(get<1>(cartesian_coordinates)) +\n                            square(get<2>(cartesian_coordinates)));\n  const DataVector inverse_r_cubed = 1.0 / pow<3>(r);\n\n  get<0, 0, 0>(*d_spacetime_metric) =\n      2.0 * mass_ / square(r) *\n      (2.0 * dt_adjusted_x_coordinate +\n       (2.0 * square(dt_adjusted_x_coordinate) * adjusted_x_coordinate -\n        adjusted_x_coordinate) /\n           r -\n       4.0 * dt_adjusted_x_coordinate * square(adjusted_x_coordinate) /\n           square(r) -\n       3.0 * square(dt_adjusted_x_coordinate * adjusted_x_coordinate) *\n           adjusted_x_coordinate * inverse_r_cubed);\n\n  get<0, 0, 1>(*d_spacetime_metric) =\n      2.0 * mass_ / square(r) *\n      (1.0 + 2.0 * dt_adjusted_x_coordinate * adjusted_x_coordinate / r -\n       2.0 * square(adjusted_x_coordinate) / square(r) -\n       3.0 * dt_adjusted_x_coordinate * pow<3>(adjusted_x_coordinate) *\n           inverse_r_cubed);\n\n  get<0, 1, 1>(*d_spacetime_metric) =\n      2.0 * mass_ *\n      (2.0 * adjusted_x_coordinate -\n       3.0 * pow<3>(adjusted_x_coordinate) / square(r)) *\n      inverse_r_cubed;\n\n  for (size_t i = 1; i < 3; ++i) {\n    d_spacetime_metric->get(i, 0, 0) =\n        -2.0 * mass_ * cartesian_coordinates.get(i) * inverse_r_cubed *\n        (1.0 + 4.0 * dt_adjusted_x_coordinate * adjusted_x_coordinate / r +\n         3.0 * square(dt_adjusted_x_coordinate * adjusted_x_coordinate) /\n             square(r));\n\n    d_spacetime_metric->get(i, 0, 1) =\n        -2.0 * mass_ * adjusted_x_coordinate * cartesian_coordinates.get(i) /\n        pow<4>(r) *\n        (2.0 + 3.0 * dt_adjusted_x_coordinate * adjusted_x_coordinate / r);\n\n    d_spacetime_metric->get(i, 1, 1) = -6.0 * mass_ *\n                                       square(adjusted_x_coordinate) *\n                                       cartesian_coordinates.get(i) / pow<5>(r);\n\n    d_spacetime_metric->get(0, 1, i + 1) =\n        2.0 * mass_ * cartesian_coordinates.get(i) *\n        (1.0 - 3.0 * square(adjusted_x_coordinate) / square(r)) *\n        inverse_r_cubed;\n\n    d_spacetime_metric->get(0, 0, i + 1) =\n        2.0 * mass_ * cartesian_coordinates.get(i) * inverse_r_cubed *\n        (dt_adjusted_x_coordinate - 2.0 * adjusted_x_coordinate / r -\n         3.0 * dt_adjusted_x_coordinate * square(adjusted_x_coordinate) /\n             square(r));\n\n    for (size_t j = 1; j < 3; ++j) {\n      if (i == j) {\n        d_spacetime_metric->get(i, 0, j + 1) =\n            2.0 * mass_ / square(r) *\n            (1.0 + dt_adjusted_x_coordinate * adjusted_x_coordinate / r -\n             2.0 * square(cartesian_coordinates.get(i)) / square(r) -\n             3.0 * dt_adjusted_x_coordinate * adjusted_x_coordinate *\n                 square(cartesian_coordinates.get(i)) * inverse_r_cubed);\n\n        d_spacetime_metric->get(i, 1, j + 1) =\n            2.0 * mass_ * adjusted_x_coordinate *\n            (1.0 - 3.0 * square(cartesian_coordinates.get(i)) / square(r)) *\n            inverse_r_cubed;\n\n        d_spacetime_metric->get(0, i + 1, j + 1) =\n            -6.0 * mass_ * square(cartesian_coordinates.get(i)) *\n            adjusted_x_coordinate / pow<5>(r);\n      } else {\n        d_spacetime_metric->get(i, 0, j + 1) =\n            -2.0 * mass_ * cartesian_coordinates.get(i) *\n            cartesian_coordinates.get(j) / pow<4>(r) *\n            (3.0 * dt_adjusted_x_coordinate * adjusted_x_coordinate / r + 2.0);\n\n        d_spacetime_metric->get(i, 1, j + 1) =\n            -6.0 * mass_ * adjusted_x_coordinate *\n            cartesian_coordinates.get(i) * cartesian_coordinates.get(j) /\n            pow<5>(r);\n\n        d_spacetime_metric->get(0, i + 1, j + 1) =\n            -6.0 * mass_ * cartesian_coordinates.get(i) *\n            cartesian_coordinates.get(j) * adjusted_x_coordinate / pow<5>(r);\n      }\n      for (size_t k = 1; k < 3; ++k) {\n        if (i == j and j == k) {\n          d_spacetime_metric->get(k, i + 1, j + 1) =\n              2.0 * mass_ *\n              (2.0 * cartesian_coordinates.get(i) -\n               3.0 * pow<3>(cartesian_coordinates.get(i)) / square(r)) *\n              inverse_r_cubed;\n\n        } else if (i == j) {\n          d_spacetime_metric->get(i, j + 1, k + 1) =\n              2.0 * mass_ *\n              (cartesian_coordinates.get(k) -\n               3.0 * square(cartesian_coordinates.get(i)) *\n                   cartesian_coordinates.get(k) / square(r)) *\n              inverse_r_cubed;\n          d_spacetime_metric->get(k, i + 1, j + 1) =\n              -6.0 * mass_ * square(cartesian_coordinates.get(i)) *\n              cartesian_coordinates.get(k) / pow<5>(r);\n        }\n      }\n    }\n  }\n}\n\nvoid BouncingBlackHole::variables_impl(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n    const size_t /*output_l_max*/, const double /*time*/,\n    tmpl::type_<Tags::News> /*meta*/) const {\n  get(*news).data() = 0.0;\n}\n\nvoid BouncingBlackHole::pup(PUP::er& p) {\n  WorldtubeData::pup(p);\n  p | amplitude_;\n  p | mass_;\n  p | frequency_;\n}\n\nPUP::able::PUP_ID BouncingBlackHole::my_PUP_ID = 0;\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/BouncingBlackHole.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Cce::Solutions {\n\n/*!\n * \\brief Analytic solution representing a coordinate oscillation about a\n * stationary Schwarzschild black hole.\n *\n * \\details As the oscillation in the metric data at the worldtube is a pure\n * coordinate effect, the system evolved using this worldtube data should\n * produce zero news. The solution is a coordinate transform applied to the\n * Schwarzschild solution in Kerr-Schild coordinates.\n */\nstruct BouncingBlackHole : public WorldtubeData {\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help{\n        \"The coordinate distance of the gauge oscillation\"};\n    static type lower_bound() { return 0.0; }\n    static type suggested_value() { return 2.0; }\n  };\n  struct ExtractionRadius {\n    using type = double;\n    static constexpr Options::String help{\n        \"The extraction radius of the spherical solution\"};\n    static type lower_bound() { return 0.0; }\n    static type suggested_value() { return 20.0; }\n  };\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help{\n        \"The mass of the Schwarzschild black hole\"};\n    static type lower_bound() { return 0.0; }\n    static type suggested_value() { return 1.0; }\n  };\n  struct Period {\n    using type = double;\n    static constexpr Options::String help{\n        \"The period of the coordinate oscillation\"};\n    static type lower_bound() { return 0.0; }\n    static type suggested_value() { return 40.0; }\n  };\n\n  static constexpr Options::String help{\n      \"Analytic solution in which a static black hole is placed in an \"\n      \"oscillating coordinate system\"};\n\n  using options = tmpl::list<Amplitude, ExtractionRadius, Mass, Period>;\n\n  WRAPPED_PUPable_decl_template(BouncingBlackHole);  // NOLINT\n\n  explicit BouncingBlackHole(CkMigrateMessage* msg) : WorldtubeData(msg) {}\n\n  // clang doesn't manage to use = default correctly in this case\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  BouncingBlackHole() {}\n\n  BouncingBlackHole(double amplitude, double extraction_radius, double mass,\n                    double period);\n\n  std::unique_ptr<WorldtubeData> get_clone() const override;\n\n  void pup(PUP::er& p) override;\n\n protected:\n  // The bouncing black hole solution is easily computed directly, so requires\n  // no additional preparation.\n  void prepare_solution(const size_t /*l_max*/,\n                        const double /*time*/) const override{};\n\n  using WorldtubeData::variables_impl;\n\n  /*!\n   * \\brief The implementation function that computes the spacetime metric on\n   * the extraction sphere at collocation points associated with angular\n   * resolution `l_max`.\n   *\n   * \\details The spacetime metric \\f$g_{a b}\\f$ is determined by evaluating the\n   * Kerr-Schild metric at a set of transformed coordinates \\f$t^\\prime = t,\n   * y^\\prime = y, z^\\prime = z\\f$, and\n   *\n   * \\f{align*}{\n   * x = x^\\prime + A \\left(\\sin\\left(\\frac{2 \\pi t}{T}\\right)\\right)^4,\n   * \\f}\n   *\n   * where the amplitude \\f$A\\f$ is set by the option `Amplitude` and the period\n   * \\f$T\\f$ is set by the option `Period`. In this notation we take\n   * the primed coordinates to be the coordinates for which the black hole has\n   * time-dependent coordinate position.\n   */\n  void variables_impl(\n      gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric, size_t l_max,\n      double time,\n      tmpl::type_<gr::Tags::SpacetimeMetric<DataVector, 3>> /*meta*/)\n      const override;\n\n  /*!\n   * \\brief The implementation function that computes the first time derivative\n   * of the spacetime metric on the extraction sphere.\n   *\n   * \\details The time derivative of the spacetime metric\n   * \\f$\\partial_t g_{a b}\\f$ comes entirely from the Jacobian factor:\n   *\n   * \\f{align*}{\n   * \\partial_t x = \\frac{8 \\pi A}{T} \\cos\\left(\\frac{2 \\pi t}{T}\\right)\n   * \\left(\\sin\\left(\\frac{2 \\pi t}{T}\\right)\\right)^3,\n   * \\f}\n   *\n   * so the transformed metric derivative is,\n   *\n   * \\f{align*}{\n   * \\partial_t g_{a^\\prime b^\\prime} = 2 \\partial_{(a^\\prime} \\partial_t x\n   * \\partial_{b^\\prime)} x^a g_{x a}.\n   * \\f}\n   *\n   * In this notation we take the primed coordinates to be the coordinates for\n   * which the black hole has time-dependent coordinate position.\n   */\n  void variables_impl(\n      gsl::not_null<tnsr::aa<DataVector, 3>*> dt_spacetime_metric, size_t l_max,\n      double time,\n      tmpl::type_<\n          ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>> /*meta*/)\n      const override;\n\n  /*!\n   * \\brief The implementation function that computes the first spatial\n   * derivative of the spacetime metric on the extraction sphere.\n   *\n   * \\details The calculation proceeds by standard coordinate transform\n   * techniques for the transformation given by \\f$t^\\prime = t,\n   * y^\\prime = y, z^\\prime = z\\f$, and\n   *\n   * \\f{align*}{\n   * x = x^\\prime + A \\left(\\sin\\left(\\frac{2 \\pi t}{T}\\right)\\right)^4,\n   * \\f}\n   *\n   * The general coordinate transformation formula that gives the metric\n   * is then\n   * \\f{align*}{\n   * \\partial_a g_{b c} =\n   * \\partial_a \\partial_b x^{\\prime a^\\prime} \\partial_c x^{\\prime b^\\prime}\n   * g_{a^\\prime b^\\prime}\n   * + \\partial_b x^{\\prime a^\\prime} \\partial_a \\partial_c x^{\\prime b^\\prime}\n   * g_{a^\\prime b^\\prime}\n   * + \\partial_a x^{\\prime a^\\prime} \\partial_b x^{\\prime b^\\prime}\n   * \\partial_c x^{\\prime c^\\prime} \\partial_a g_{b c}\n   * \\f}\n   */\n  void variables_impl(\n      gsl::not_null<tnsr::iaa<DataVector, 3>*> d_spacetime_metric, size_t l_max,\n      double time,\n      tmpl::type_<gh::Tags::Phi<DataVector, 3>> /*meta*/) const override;\n\n  /// The News in the bouncing black hole solution vanishes, as the oscillation\n  /// comes entirely from a coordinate transform.\n  void variables_impl(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n      size_t output_l_max, double time,\n      tmpl::type_<Tags::News> /*meta*/) const override;\n\n  double amplitude_ = std::numeric_limits<double>::signaling_NaN();\n  double mass_ = std::numeric_limits<double>::signaling_NaN();\n  double frequency_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BouncingBlackHole.cpp\n  LinearizedBondiSachs.cpp\n  GaugeWave.cpp\n  RobinsonTrautman.cpp\n  RotatingSchwarzschild.cpp\n  SphericalMetricData.cpp\n  TeukolskyWave.cpp\n  WorldtubeData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BouncingBlackHole.hpp\n  LinearizedBondiSachs.hpp\n  GaugeWave.hpp\n  RobinsonTrautman.hpp\n  RotatingSchwarzschild.hpp\n  SphericalMetricData.hpp\n  TeukolskyWave.hpp\n  WorldtubeData.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/GaugeWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/GaugeWave.hpp\"\n\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\n\nGaugeWave::GaugeWave(const double extraction_radius, const double mass,\n                     const double frequency, const double amplitude,\n                     const double peak_time, const double duration)\n    : SphericalMetricData{extraction_radius},\n      mass_{mass},\n      frequency_{frequency},\n      amplitude_{amplitude},\n      peak_time_{peak_time},\n      duration_{duration} {}\n\nstd::unique_ptr<WorldtubeData> GaugeWave::get_clone() const {\n  return std::make_unique<GaugeWave>(*this);\n}\n\ndouble GaugeWave::coordinate_wave_function(const double time) const {\n  const auto retarded_time = time - extraction_radius_;\n  return amplitude_ * sin(frequency_ * retarded_time) *\n         exp(-square(retarded_time - peak_time_) / square(duration_));\n}\n\ndouble GaugeWave::du_coordinate_wave_function(const double time) const {\n  const auto retarded_time = time - extraction_radius_;\n  return amplitude_ *\n         (-2.0 * (retarded_time - peak_time_) / square(duration_) *\n              sin(frequency_ * retarded_time) +\n          frequency_ * cos(frequency_ * retarded_time)) *\n         exp(-square(retarded_time - peak_time_) / square(duration_));\n}\n\ndouble GaugeWave::du_du_coordinate_wave_function(const double time) const {\n  const auto retarded_time = time - extraction_radius_;\n  return amplitude_ *\n         (-4.0 * square(duration_) * (retarded_time - peak_time_) * frequency_ *\n              cos(frequency_ * retarded_time) +\n          (-2.0 * square(duration_) + 4.0 * square(retarded_time - peak_time_) -\n           pow<4>(duration_) * square(frequency_)) *\n              sin(frequency_ * retarded_time)) /\n         pow<4>(duration_) *\n         exp(-square(retarded_time - peak_time_) / square(duration_));\n}\n\nvoid GaugeWave::spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        spherical_metric,\n    const size_t /*l_max*/, const double time) const {\n  const auto wave_f = coordinate_wave_function(time);\n  const auto du_wave_f = du_coordinate_wave_function(time);\n  get<0, 0>(*spherical_metric) = -(extraction_radius_ - 2.0 * mass_) *\n                                 square(extraction_radius_ + du_wave_f) /\n                                 pow<3>(extraction_radius_);\n  get<0, 1>(*spherical_metric) =\n      (extraction_radius_ + du_wave_f) *\n      (2.0 * mass_ * square(extraction_radius_) +\n       (extraction_radius_ - 2.0 * mass_) *\n           (extraction_radius_ * du_wave_f + wave_f)) /\n      pow<4>(extraction_radius_);\n  get<0, 2>(*spherical_metric) = 0.0;\n  get<0, 3>(*spherical_metric) = 0.0;\n\n  get<1, 1>(*spherical_metric) =\n      (square(extraction_radius_) - extraction_radius_ * du_wave_f - wave_f) *\n      (pow<3>(extraction_radius_) + 2.0 * mass_ * square(extraction_radius_) +\n       (extraction_radius_ - 2.0 * mass_) *\n           (extraction_radius_ * du_wave_f + wave_f)) /\n      pow<5>(extraction_radius_);\n  get<1, 2>(*spherical_metric) = 0.0;\n  get<1, 3>(*spherical_metric) = 0.0;\n  get<2, 2>(*spherical_metric) = square(extraction_radius_);\n  get<2, 3>(*spherical_metric) = 0.0;\n  get<3, 3>(*spherical_metric) = square(extraction_radius_);\n}\n\nvoid GaugeWave::dr_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dr_spherical_metric,\n    const size_t l_max, const double time) const {\n  const auto wave_f = coordinate_wave_function(time);\n  const auto du_wave_f = du_coordinate_wave_function(time);\n  // for simpler expressions, we take advantage of the F derivatives evaluated\n  // in the dt function (because the F function depends only on retarded time\n  // t - r)\n  dt_spherical_metric(dr_spherical_metric, l_max, time);\n\n  get<0, 0>(*dr_spherical_metric) =\n      -get<0, 0>(*dr_spherical_metric) +\n      2.0 / pow<4>(extraction_radius_) * (extraction_radius_ + du_wave_f) *\n          (-mass_ * extraction_radius_ +\n           (extraction_radius_ - 3.0 * mass_) * du_wave_f);\n\n  get<0, 1>(*dr_spherical_metric) =\n      -get<0, 1>(*dr_spherical_metric) -\n      (2.0 * mass_ * pow<3>(extraction_radius_) +\n       2.0 * extraction_radius_ * wave_f * (extraction_radius_ - 3.0 * mass_) +\n       du_wave_f * (pow<3>(extraction_radius_) +\n                    wave_f * (3.0 * extraction_radius_ - 8.0 * mass_)) +\n       2.0 * extraction_radius_ * square(du_wave_f) *\n           (extraction_radius_ - 3.0 * mass_)) /\n          pow<5>(extraction_radius_);\n\n  get<0, 2>(*dr_spherical_metric) = 0.0;\n  get<0, 3>(*dr_spherical_metric) = 0.0;\n\n  get<1, 1>(*dr_spherical_metric) =\n      -get<1, 1>(*dr_spherical_metric) +\n      2.0 *\n          (-mass_ * pow<4>(extraction_radius_) +\n           square(wave_f) * (2.0 * extraction_radius_ - 5.0 * mass_) +\n           du_wave_f * square(extraction_radius_) *\n               (4.0 * mass_ * extraction_radius_ +\n                du_wave_f * (extraction_radius_ - 3.0 * mass_)) +\n           wave_f * extraction_radius_ *\n               (6.0 * mass_ * extraction_radius_ +\n                du_wave_f * (3.0 * extraction_radius_ - 8.0 * mass_))) /\n          pow<6>(extraction_radius_);\n\n  get<1, 2>(*dr_spherical_metric) = 0.0;\n  get<1, 3>(*dr_spherical_metric) = 0.0;\n  get<2, 2>(*dr_spherical_metric) = 2.0 * extraction_radius_;\n  get<2, 3>(*dr_spherical_metric) = 0.0;\n  get<3, 3>(*dr_spherical_metric) = 2.0 * extraction_radius_;\n}\n\nvoid GaugeWave::dt_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dt_spherical_metric,\n    const size_t /*l_max*/, const double time) const {\n  const auto wave_f = coordinate_wave_function(time);\n  const auto du_wave_f = du_coordinate_wave_function(time);\n  const auto du_du_wave_f = du_du_coordinate_wave_function(time);\n\n  get<0, 0>(*dt_spherical_metric) =\n      -2.0 * du_du_wave_f / pow<3>(extraction_radius_) *\n      (extraction_radius_ - 2.0 * mass_) * (extraction_radius_ + du_wave_f);\n  get<0, 1>(*dt_spherical_metric) =\n      (du_du_wave_f * (2.0 * mass_ * square(extraction_radius_) +\n                       (extraction_radius_ - 2.0 * mass_) *\n                           (extraction_radius_ * du_wave_f + wave_f)) +\n       (extraction_radius_ + du_wave_f) * (extraction_radius_ - 2.0 * mass_) *\n           (extraction_radius_ * du_du_wave_f + du_wave_f)) /\n      pow<4>(extraction_radius_);\n\n  get<0, 2>(*dt_spherical_metric) = 0.0;\n  get<0, 3>(*dt_spherical_metric) = 0.0;\n\n  get<1, 1>(*dt_spherical_metric) =\n      (-(extraction_radius_ * du_du_wave_f + du_wave_f) *\n           (pow<3>(extraction_radius_) +\n            2.0 * mass_ * square(extraction_radius_) +\n            (extraction_radius_ - 2.0 * mass_) *\n                (extraction_radius_ * du_wave_f + wave_f)) +\n       (square(extraction_radius_) - extraction_radius_ * du_wave_f - wave_f) *\n           (extraction_radius_ - 2.0 * mass_) *\n           (extraction_radius_ * du_du_wave_f + du_wave_f)) /\n      pow<5>(extraction_radius_);\n\n  get<1, 2>(*dt_spherical_metric) = 0.0;\n  get<1, 3>(*dt_spherical_metric) = 0.0;\n  get<2, 2>(*dt_spherical_metric) = 0.0;\n  get<2, 3>(*dt_spherical_metric) = 0.0;\n  get<3, 3>(*dt_spherical_metric) = 0.0;\n}\n\nvoid GaugeWave::variables_impl(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n    const size_t /*l_max*/, const double /*time*/,\n    tmpl::type_<Tags::News> /*meta*/) const {\n  get(*news).data() = 0.0;\n}\n\nvoid GaugeWave::pup(PUP::er& p) {\n  SphericalMetricData::pup(p);\n  p | mass_;\n  p | frequency_;\n  p | amplitude_;\n  p | peak_time_;\n  p | duration_;\n}\n\nPUP::able::PUP_ID GaugeWave::my_PUP_ID = 0;\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/GaugeWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\nnamespace Solutions {\n\n/*!\n * \\brief Computes the analytic data for a gauge wave solution described in\n * \\cite Barkett2019uae.\n *\n * \\details This test computes an analytic solution of a pure-gauge perturbation\n * of the Schwarzschild metric. The gauge perturbation is constructed using the\n * time-dependent coordinate transformation of the ingoing Eddington-Finklestein\n * coordinate \\f$\\nu \\rightarrow \\nu + F(t - r) / r\\f$, where\n *\n * \\f[\n * F(u) = A \\sin(\\omega u) e^{- (u - u_0)^2 / k^2}.\n * \\f]\n *\n * \\note In the paper \\cite Barkett2019uae, a translation map was\n * applied to the solution to make the test more demanding. For simplicity, we\n * omit that extra map. The behavior of translation-independence is\n * tested by the `Cce::Solutions::BouncingBlackHole` solution.\n */\nstruct GaugeWave : public SphericalMetricData {\n  struct ExtractionRadius {\n    using type = double;\n    static constexpr Options::String help{\n        \"The extraction radius of the spherical solution\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help{\n        \"The mass of the Schwarzschild solution.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Frequency {\n    using type = double;\n    static constexpr Options::String help{\n        \"The frequency of the oscillation of the gauge wave.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help{\n      \"The amplitude of the gauge wave.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct PeakTime {\n    using type = double;\n    static constexpr Options::String help{\n        \"The time of the peak of the Gaussian envelope.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Duration {\n    using type = double;\n    static constexpr Options::String help{\n        \"The characteristic duration of the Gaussian envelope.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options = tmpl::list<ExtractionRadius, Mass, Frequency, Amplitude,\n                             PeakTime, Duration>;\n\n  static constexpr Options::String help = {\n      \"Analytic solution representing worldtube data for a pure-gauge \"\n      \"perturbation near a Schwarzschild metric in spherical coordinates\"};\n\n  WRAPPED_PUPable_decl_template(GaugeWave);  // NOLINT\n\n  explicit GaugeWave(CkMigrateMessage* msg) : SphericalMetricData(msg) {}\n\n  // clang doesn't manage to use = default correctly in this case\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  GaugeWave() {}\n\n  GaugeWave(double extraction_radius, double mass, double frequency,\n            double amplitude, double peak_time, double duration);\n\n  std::unique_ptr<WorldtubeData> get_clone() const override;\n\n  void pup(PUP::er& p) override;\n\n private:\n  double coordinate_wave_function(double time) const;\n\n  double du_coordinate_wave_function(double time) const;\n\n  double du_du_coordinate_wave_function(double time) const;\n\n protected:\n  /// A no-op as the gauge wave solution does not have substantial\n  /// shared computation to prepare before the separate component calculations.\n  void prepare_solution(const size_t /*output_l_max*/,\n                        const double /*time*/) const override {}\n\n  /*!\n   * \\brief Compute the spherical coordinate metric from the closed-form gauge\n   * wave metric.\n   *\n   * \\details The transformation of the ingoing Eddington-Finkelstein coordinate\n   * produces metric components in spherical coordinates (identical up to minor\n   * manipulations of the metric given in Eq. (149) of \\cite Barkett2019uae):\n   *\n   * \\f{align*}{\n   * g_{tt} &= \\frac{-1}{r^3}\\left(r - 2 M\\right)\n   * \\left[r + \\partial_u F(u)\\right]^2\\\\\n   * g_{rt} &= \\frac{1}{r^4} \\left[r + \\partial_u F(u)\\right]\n   * \\left\\{2 M r^2 + \\left(r - 2 M\\right)\n   * \\left[r \\partial_u F(u) + F(u)\\right]\\right\\} \\\\\n   * g_{rr} &= \\frac{1}{r^5} \\left[r^2 - r \\partial_u F(u) - F(u)\\right]\n   * \\left\\{r^3 + 2 M r^2 + \\left(r - 2 M\\right)\n   * \\left[r \\partial_u F(u) + F(u)\\right]\\right\\} \\\\\n   * g_{\\theta \\theta} &= r^2 \\\\\n   * g_{\\phi \\phi} &= r^2 \\sin^2(\\theta),\n   * \\f}\n   *\n   * and all other components vanish. Here, \\f$F(u)\\f$ is defined as\n   *\n   * \\f{align*}{\n   * F(u) &= A \\sin(\\omega u) e^{-(u - u_0)^2 /k^2},\\\\\n   * \\partial_u F(u) &= A \\left[-2 \\frac{u - u_0}{k^2} \\sin(\\omega u)\n   * + \\omega \\cos(\\omega u)\\right] e^{-(u - u_0)^2 / k^2}.\n   * \\f}\n   *\n   * \\warning The \\f$\\phi\\f$ components are returned in a form for which the\n   * \\f$\\sin(\\theta)\\f$ factors are omitted, assuming that derivatives and\n   * Jacobians will be applied similarly omitting those factors (and therefore\n   * improving precision of the tensor expression). If you require the\n   * \\f$\\sin(\\theta)\\f$ factors, be sure to put them in by hand in the calling\n   * code.\n   */\n  void spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the radial derivative of the spherical coordinate metric\n   * from the closed-form gauge wave metric.\n   *\n   * \\details The transformation of the ingoing Eddington-Finkelstein coordinate\n   * produces the radial derivative of the metric components in spherical\n   * coordinates:\n   *\n   * \\f{align*}{\n   * \\partial_r g_{tt} + \\partial_t g_{tt} =& \\frac{2}{r^4}\n   * \\left[r + \\partial_u F(u)\\right]\n   * \\left[- M r + (r - 3 M)\\partial_u F(u)\\right] \\\\\n   * \\partial_r g_{rt} + \\partial_t g_{rt} =& - \\frac{1}{r^5}\n   * \\left\\{2 M r^3 + 2 r F(u) (r - 3M) + \\partial_u F(u)[r^3 + F(u)(3r - 8 M)]\n   * + 2 r [\\partial_u F(u)]^2 (r - 3M)\\right\\}\\\\\n   * \\partial_r g_{rr} + \\partial_t g_{rr} =& \\frac{2}{r^6}\n   * \\left\\{- M r^4 + F(u)^2 (2r - 5M)\n   * + \\partial_u F(u) r^2 \\left[4 M r + \\partial_u F(u) (r - 3 M)\\right]\n   * + F(u) r \\left[6 M r + \\partial_u F(u) (3r - 8 M)\\right]\\right\\} \\\\\n   * g_{\\theta \\theta} =& 2 r \\\\\n   * g_{\\phi \\phi} =& 2 r \\sin^2(\\theta),\n   * \\f}\n   *\n   * and all other components vanish (these formulae are obtained simply by\n   * applying radial derivatives to those given in\n   * `GaugeWave::spherical_metric()`). Here, \\f$F(u)\\f$ is defined as\n   *\n   * \\f{align*}{\n   * F(u) &= A \\sin(\\omega u) e^{-(u - u_0)^2 /k^2},\\\\\n   * \\partial_u F(u) &= A \\left[-2 \\frac{u - u_0}{k^2} \\sin(\\omega u)\n   * + \\omega \\cos(\\omega u)\\right] e^{-(u - u_0)^2 / k^2}.\n   * \\f}\n   *\n   * \\warning The \\f$\\phi\\f$ components are returned in a form for which the\n   * \\f$\\sin(\\theta)\\f$ factors are omitted, assuming that derivatives and\n   * Jacobians will be applied similarly omitting those factors (and therefore\n   * improving precision of the tensor expression). If you require the\n   * \\f$\\sin(\\theta)\\f$ factors, be sure to put them in by hand in the calling\n   * code.\n   */\n  void dr_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dr_spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the spherical coordinate metric from the closed-form gauge\n   * wave metric.\n   *\n   * \\details The transformation of the ingoing Eddington-Finkelstein coordinate\n   * produces metric components in spherical coordinates:\n   *\n   * \\f{align*}{\n   * \\partial_t g_{tt} =& \\frac{-2 \\partial_u^2 F(u)}{r^3}\n   * \\left(r - 2 M\\right) \\left(r + \\partial_u F(u)\\right) \\\\\n   * \\partial_t g_{rt} =& \\frac{1}{r^4} \\Bigg\\{\\partial_u^2 F(u)\n   * \\left[2 M r^2 + \\left(r - 2 M\\right)\n   * (r \\partial_u F(u) + F(u))\\right] \\\\\n   * &+ \\left[r + \\partial_u F(u)\\right]\n   * \\left(r - 2M\\right) \\left[r \\partial_u^2 F(u) + \\partial_u F(u)\n   * \\right]\\Bigg\\} \\\\\n   * \\partial_t g_{rr} =&\n   * \\frac{1}{r^5}\\Bigg\\{-\\left[r \\partial_u^2 F(u) + \\partial_u F(u)\\right]\n   * \\left[r^3 + 2 M r^2  + \\left(r - 2 M\\right)\n   * \\left(r \\partial_u F(u) + F(u)\\right)\\right]\\\\\n   * &+ \\left[r^2 - r \\partial_u F(u) - F(u)\\right]\n   * \\left(r - 2 M\\right)\n   * \\left[r \\partial_u^2 F(u) + \\partial_u F(u)\\right]\\Bigg\\} \\\\\n   * \\partial_t g_{\\theta \\theta} =& 0 \\\\\n   * \\partial_t g_{\\phi \\phi} =& 0,\n   * \\f}\n   *\n   * and all other components vanish. Here, \\f$F(u)\\f$ is defined as\n   *\n   * \\f{align*}{\n   * F(u) &= A \\sin(\\omega u) e^{-(u - u_0)^2 /k^2},\\\\\n   * \\partial_u F(u) &= A \\left[-2 \\frac{u - u_0}{k^2} \\sin(\\omega u)\n   * + \\omega \\cos(\\omega u)\\right] e^{-(u - u_0)^2 / k^2},\\\\\n   * \\partial^2_u F(u) &= \\frac{A}{k^4} \\left\\{-4 k^2 \\omega (u - u_0)\n   * \\cos(\\omega u) + \\left[-2 k^2 + 4 (u - u_0)^2 - k^4 \\omega^2\\right]\n   * \\sin(\\omega u)\\right\\}  e^{-(u - u_0) / k^2}\n   * \\f}\n   *\n   * \\warning The \\f$\\phi\\f$ components are returned in a form for which the\n   * \\f$\\sin(\\theta)\\f$ factors are omitted, assuming that derivatives and\n   * Jacobians will be applied similarly omitting those factors (and therefore\n   * improving precision of the tensor expression). If you require the\n   * \\f$\\sin(\\theta)\\f$ factors, be sure to put them in by hand in the calling\n   * code.\n   */\n  void dt_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dt_spherical_metric,\n      size_t l_max, double time) const override;\n\n  using WorldtubeData::variables_impl;\n\n  using SphericalMetricData::variables_impl;\n\n  /// The News vanishes, because the wave is pure gauge.\n  void variables_impl(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> News,\n      size_t l_max, double time,\n      tmpl::type_<Tags::News> /*meta*/) const override;\n\n  double mass_ = std::numeric_limits<double>::signaling_NaN();\n  double frequency_ = std::numeric_limits<double>::signaling_NaN();\n  double amplitude_ = std::numeric_limits<double>::signaling_NaN();\n  double peak_time_ = std::numeric_limits<double>::signaling_NaN();\n  double duration_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace Solutions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\nnamespace LinearizedBondiSachs_detail {\nnamespace InitializeJ {\nLinearizedBondiSachs::LinearizedBondiSachs(const double start_time,\n                                           const double frequency,\n                                           const std::complex<double> c_2a,\n                                           const std::complex<double> c_2b,\n                                           const std::complex<double> c_3a,\n                                           const std::complex<double> c_3b)\n    : c_2a_{c_2a},\n      c_2b_{c_2b},\n      c_3a_{c_3a},\n      c_3b_{c_3b},\n      frequency_{frequency},\n      time_{start_time} {}\n\nvoid LinearizedBondiSachs::operator()(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& /*boundary_j*/,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& /*boundary_dr_j*/,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& /*beta*/,\n    const size_t l_max, const size_t number_of_radial_points,\n    const gsl::not_null<Parallel::NodeLock*> /*hdf5_lock*/) const {\n  const DataVector one_minus_y_collocation =\n      1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n                number_of_radial_points);\n  SpinWeighted<ComplexDataVector, 2> angular_view_j;\n  Variables<tmpl::list<::Tags::TempScalar<0, ComplexDataVector>,\n                       ::Tags::TempScalar<1, ComplexDataVector>,\n                       ::Tags::TempScalar<2, ComplexDataVector>>>\n      temp_buffer{Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  ComplexDataVector& one_over_two_r =\n      get(get<::Tags::TempScalar<0, ComplexDataVector>>(temp_buffer));\n  ComplexDataVector& l_2_coefficient_buffer =\n      get(get<::Tags::TempScalar<1, ComplexDataVector>>(temp_buffer));\n  ComplexDataVector& l_3_coefficient_buffer =\n      get(get<::Tags::TempScalar<2, ComplexDataVector>>(temp_buffer));\n\n  one_over_two_r = 1.0 / (2.0 * get(r).data());\n  for (size_t i = 0; i < number_of_radial_points; i++) {\n    angular_view_j.set_data_ref(\n        get(*j).data().data() +\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max) * i,\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n    l_2_coefficient_buffer =\n        sqrt(0.75) *\n        (c_2a_ * one_over_two_r * one_minus_y_collocation[i] -\n         c_2b_ / 3.0 * pow<3>(one_over_two_r * one_minus_y_collocation[i]));\n\n    l_3_coefficient_buffer =\n        sqrt(60.0) *\n        (0.1 * c_3a_ * one_over_two_r * one_minus_y_collocation[i] -\n         0.25 * c_3b_ * pow<4>(one_over_two_r * one_minus_y_collocation[i]) -\n         std::complex<double>(0.0, 1.0) * frequency_ *\n             (c_3b_ / 6.0 *\n              pow<3>(one_over_two_r * one_minus_y_collocation[i])));\n\n    Solutions::LinearizedBondiSachs_detail::assign_components_from_l_factors(\n        make_not_null(&angular_view_j), l_2_coefficient_buffer,\n        l_3_coefficient_buffer, l_max, frequency_, time_);\n  }\n  Spectral::Swsh::create_angular_and_cartesian_coordinates(\n      cartesian_cauchy_coordinates, angular_cauchy_coordinates, l_max);\n}\n\nvoid LinearizedBondiSachs::pup(PUP::er& p) {\n  p | c_2a_;\n  p | c_2b_;\n  p | c_3a_;\n  p | c_3b_;\n  p | frequency_;\n  p | time_;\n}\n\nstd::unique_ptr<::Cce::InitializeJ::InitializeJ<false>>\nLinearizedBondiSachs::get_clone() const {\n  return std::make_unique<LinearizedBondiSachs>(*this);\n}\n\nPUP::able::PUP_ID LinearizedBondiSachs::my_PUP_ID = 0;\n}  // namespace InitializeJ\n\nnamespace {\nconst size_t z_harmonics_maximum_l_max = 64_st;\nconst ComplexDataVector& cached_z_harmonics(const size_t l_max, const size_t l,\n                                            const int spin) {\n  const static auto lazy_z_harmonics_cache =\n      make_static_cache<CacheRange<2_st, z_harmonics_maximum_l_max>,\n                        CacheRange<2_st, 4_st>, CacheRange<-2, 3>>(\n          [](const size_t local_l_max, const size_t local_l,\n             const int local_spin) {\n            Spectral::Swsh::SpinWeightedSphericalHarmonic y_plus_m{\n                local_spin, local_l, static_cast<int>(local_l)};\n            Spectral::Swsh::SpinWeightedSphericalHarmonic y_minus_m{\n                local_spin, local_l, -static_cast<int>(local_l)};\n            ComplexDataVector z_harmonic{\n                Spectral::Swsh::number_of_swsh_collocation_points(local_l_max)};\n            const auto& collocation_metadata =\n                Spectral::Swsh::cached_collocation_metadata<\n                    Spectral::Swsh::ComplexRepresentation::Interleaved>(\n                    local_l_max);\n            for (const auto& collocation_point : collocation_metadata) {\n              z_harmonic[collocation_point.offset] =\n                  (y_plus_m.evaluate(collocation_point.theta,\n                                     collocation_point.phi) +\n                   (local_l % 2 == 0 ? 1.0 : -1.0) *\n                       y_minus_m.evaluate(collocation_point.theta,\n                                          collocation_point.phi));\n            }\n            return z_harmonic;\n          });\n  return lazy_z_harmonics_cache(l_max, l, spin);\n}\n}  // namespace\n\ntemplate <int Spin, typename FactorType>\nvoid assign_components_from_l_factors(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> bondi_quantity,\n    const FactorType& l_2_factor, const FactorType& l_3_factor,\n    const size_t l_max, const double frequency, const double time) {\n  const std::complex<double> time_factor =\n      cos(frequency * time) +\n      std::complex<double>(0.0, 1.0) * sin(frequency * time);\n\n  // mode constants for the computation of the linearized solution.\n  const auto& z_22_harmonic = cached_z_harmonics(l_max, 2_st, Spin);\n  const auto& z_33_harmonic = cached_z_harmonics(l_max, 3_st, Spin);\n  bondi_quantity->data() = z_22_harmonic * real(time_factor * l_2_factor) +\n                           z_33_harmonic * real(time_factor * l_3_factor);\n}\n\ntemplate <int Spin>\nvoid assign_du_components_from_l_factors(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*>\n        du_bondi_quantity,\n    const std::complex<double>& l_2_factor,\n    const std::complex<double>& l_3_factor, const size_t l_max,\n    const double frequency, const double time) {\n  const std::complex<double> time_factor =\n      frequency * (std::complex<double>(0.0, 1.0) * cos(frequency * time) -\n                   sin(frequency * time));\n\n  // mode constants for the computation of the linearized solution.\n  const auto& z_22_harmonic = cached_z_harmonics(l_max, 2_st, Spin);\n  const auto& z_33_harmonic = cached_z_harmonics(l_max, 3_st, Spin);\n  du_bondi_quantity->data() = z_22_harmonic * real(time_factor * l_2_factor) +\n                              z_33_harmonic * real(time_factor * l_3_factor);\n}\n}  // namespace LinearizedBondiSachs_detail\n\nLinearizedBondiSachs::LinearizedBondiSachs(\n    const std::array<std::complex<double>, 2>& mode_constants,\n    const double extraction_radius, const double frequency)\n    : SphericalMetricData{extraction_radius}, frequency_{frequency} {\n  c_2a_ = mode_constants[0];\n  c_3a_ = mode_constants[1];\n  c_2b_ = 3.0 * c_2a_ / square(frequency_);\n  c_3b_ = std::complex<double>(0.0, -3.0) * c_3a_ / pow<3>(frequency_);\n}\n\nstd::unique_ptr<WorldtubeData> LinearizedBondiSachs::get_clone() const {\n  return std::make_unique<LinearizedBondiSachs>(*this);\n}\n\nvoid LinearizedBondiSachs::linearized_bondi_j(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> bondi_j,\n    const size_t l_max, const double time) const {\n  const std::complex<double> j_2_coefficient =\n      sqrt(0.75) *\n      (c_2a_ / extraction_radius_ - c_2b_ / (3.0 * pow<3>(extraction_radius_)));\n\n  const std::complex<double> j_3_coefficient =\n      sqrt(60.0) * (0.1 * c_3a_ / extraction_radius_ -\n                    0.25 * c_3b_ / pow<4>(extraction_radius_) -\n                    std::complex<double>(0.0, 1.0) * frequency_ *\n                        (c_3b_ / (6.0 * pow<3>(extraction_radius_))));\n\n  LinearizedBondiSachs_detail::assign_components_from_l_factors(\n      bondi_j, j_2_coefficient, j_3_coefficient, l_max, frequency_, time);\n}\n\nvoid LinearizedBondiSachs::linearized_bondi_u(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> bondi_u,\n    const size_t l_max, const double time) const {\n  const std::complex<double> u_2_coefficient =\n      sqrt(3.0) * (0.5 * c_2a_ / square(extraction_radius_) +\n                   0.25 * c_2b_ / pow<4>(extraction_radius_) +\n                   std::complex<double>(0.0, 1.0) * frequency_ *\n                       (c_2b_ / (3.0 * pow<3>(extraction_radius_))));\n\n  const std::complex<double> u_3_coefficient =\n      sqrt(6.0) *\n      (0.5 * c_3a_ / square(extraction_radius_) -\n       2.0 * square(frequency_) * c_3b_ / (3.0 * pow<3>(extraction_radius_)) +\n       c_3b_ / pow<5>(extraction_radius_) +\n       std::complex<double>(0.0, 1.0) * frequency_ *\n           (1.25 * c_3b_ / pow<4>(extraction_radius_)));\n\n  LinearizedBondiSachs_detail::assign_components_from_l_factors(\n      bondi_u, u_2_coefficient, u_3_coefficient, l_max, frequency_, time);\n}\n\nvoid LinearizedBondiSachs::linearized_bondi_w(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> bondi_w,\n    const size_t l_max, const double time) const {\n  const std::complex<double> w_2_coefficient =\n      (-square(frequency_) * c_2b_ / square(extraction_radius_) +\n       0.5 * c_2b_ / pow<4>(extraction_radius_) +\n       std::complex<double>(0.0, 1.0) * frequency_ *\n           (c_2b_ / pow<3>(extraction_radius_))) /\n      sqrt(2.0);\n\n  const std::complex<double> w_3_coefficient =\n      (2.5 * frequency_ * c_3b_ / pow<4>(extraction_radius_) +\n       3.0 * c_3b_ / pow<5>(extraction_radius_) -\n       std::complex<double>(0.0, 2.0) * frequency_ *\n           (square(frequency_) * c_3b_ / square(extraction_radius_) +\n            2.0 * frequency_ * c_3b_ / pow<3>(extraction_radius_))) /\n      sqrt(2.0);\n\n  LinearizedBondiSachs_detail::assign_components_from_l_factors(\n      bondi_w, w_2_coefficient, w_3_coefficient, l_max, frequency_, time);\n}\n\nvoid LinearizedBondiSachs::linearized_dr_bondi_j(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> dr_bondi_j,\n    const size_t l_max, const double time) const {\n  const std::complex<double> dr_j_2_coefficient =\n      -sqrt(0.75) *\n      (c_2a_ / square(extraction_radius_) - c_2b_ / pow<4>(extraction_radius_));\n\n  const std::complex<double> dr_j_3_coefficient =\n      sqrt(60.0) * (-0.1 * c_3a_ / square(extraction_radius_) +\n                    c_3b_ / pow<5>(extraction_radius_) +\n                    std::complex<double>(0.0, 0.5) * frequency_ * c_3b_ /\n                        (pow<4>(extraction_radius_)));\n\n  LinearizedBondiSachs_detail::assign_components_from_l_factors(\n      dr_bondi_j, dr_j_2_coefficient, dr_j_3_coefficient, l_max, frequency_,\n      time);\n}\n\nvoid LinearizedBondiSachs::linearized_dr_bondi_u(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> dr_bondi_u,\n    const size_t l_max, const double time) const {\n  const std::complex<double> dr_u_2_coefficient =\n      sqrt(3.0) * (-c_2a_ / pow<3>(extraction_radius_) -\n                   c_2b_ / pow<5>(extraction_radius_) +\n                   std::complex<double>(0.0, 1.0) * frequency_ *\n                       (-c_2b_ / (pow<4>(extraction_radius_))));\n\n  const std::complex<double> dr_u_3_coefficient =\n      sqrt(6.0) *\n      (-c_3a_ / pow<3>(extraction_radius_) +\n       2.0 * square(frequency_) * c_3b_ / (pow<4>(extraction_radius_)) -\n       5.0 * c_3b_ / pow<6>(extraction_radius_) +\n       std::complex<double>(0.0, 1.0) * frequency_ *\n           (-5.0 * c_3b_ / pow<5>(extraction_radius_)));\n\n  LinearizedBondiSachs_detail::assign_components_from_l_factors(\n      dr_bondi_u, dr_u_2_coefficient, dr_u_3_coefficient, l_max, frequency_,\n      time);\n}\n\nvoid LinearizedBondiSachs::linearized_dr_bondi_w(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> dr_bondi_w,\n    const size_t l_max, const double time) const {\n  const std::complex<double> dr_w_2_coefficient =\n      (2.0 * square(frequency_) * c_2b_ / pow<3>(extraction_radius_) -\n       2.0 * c_2b_ / pow<5>(extraction_radius_) +\n       std::complex<double>(0.0, 1.0) * frequency_ *\n           (-3.0 * c_2b_ / pow<4>(extraction_radius_))) /\n      sqrt(2.0);\n\n  const std::complex<double> dr_w_3_coefficient =\n      (-10.0 * frequency_ * c_3b_ / pow<5>(extraction_radius_) -\n       15.0 * c_3b_ / pow<6>(extraction_radius_) +\n       std::complex<double>(0.0, 4.0) * square(frequency_) * c_3b_ *\n           (frequency_ / pow<3>(extraction_radius_) +\n            3.0 / pow<4>(extraction_radius_))) /\n      sqrt(2.0);\n\n  LinearizedBondiSachs_detail::assign_components_from_l_factors(\n      dr_bondi_w, dr_w_2_coefficient, dr_w_3_coefficient, l_max, frequency_,\n      time);\n}\n\nvoid LinearizedBondiSachs::linearized_du_bondi_j(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> du_bondi_j,\n    const size_t l_max, const double time) const {\n  const std::complex<double> j_2_coefficient =\n      sqrt(0.75) *\n      (c_2a_ / extraction_radius_ - c_2b_ / (3.0 * pow<3>(extraction_radius_)));\n\n  const std::complex<double> j_3_coefficient =\n      sqrt(60.0) * (0.1 * c_3a_ / extraction_radius_ -\n                    0.25 * c_3b_ / pow<4>(extraction_radius_) +\n                    std::complex<double>(0.0, 1.0) * frequency_ *\n                        (-c_3b_ / (6.0 * pow<3>(extraction_radius_))));\n\n  LinearizedBondiSachs_detail::assign_du_components_from_l_factors(\n      du_bondi_j, j_2_coefficient, j_3_coefficient, l_max, frequency_, time);\n}\n\nvoid LinearizedBondiSachs::linearized_du_bondi_u(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> du_bondi_u,\n    const size_t l_max, const double time) const {\n  const std::complex<double> u_2_coefficient =\n      sqrt(3.0) * (0.5 * c_2a_ / square(extraction_radius_) +\n                   0.25 * c_2b_ / pow<4>(extraction_radius_) +\n                   std::complex<double>(0.0, 1.0) * frequency_ *\n                       (c_2b_ / (3.0 * pow<3>(extraction_radius_))));\n\n  const std::complex<double> u_3_coefficient =\n      sqrt(6.0) *\n      (0.5 * c_3a_ / square(extraction_radius_) -\n       2.0 * square(frequency_) * c_3b_ / (3.0 * pow<3>(extraction_radius_)) +\n       c_3b_ / pow<5>(extraction_radius_) +\n       std::complex<double>(0.0, 1.0) * frequency_ *\n           (1.25 * c_3b_ / pow<4>(extraction_radius_)));\n\n  LinearizedBondiSachs_detail::assign_du_components_from_l_factors(\n      du_bondi_u, u_2_coefficient, u_3_coefficient, l_max, frequency_, time);\n}\n\nvoid LinearizedBondiSachs::linearized_du_bondi_w(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> du_bondi_w,\n    const size_t l_max, const double time) const {\n  const std::complex<double> w_2_coefficient =\n      (-square(frequency_) * c_2b_ / square(extraction_radius_) +\n       0.5 * c_2b_ / pow<4>(extraction_radius_) +\n       std::complex<double>(0.0, 1.0) * frequency_ *\n           (c_2b_ / pow<3>(extraction_radius_))) /\n      sqrt(2.0);\n\n  const std::complex<double> w_3_coefficient =\n      (2.5 * frequency_ * c_3b_ / pow<4>(extraction_radius_) +\n       3.0 * c_3b_ / pow<5>(extraction_radius_) +\n       std::complex<double>(0.0, 1.0) * frequency_ *\n           (-2.0 * square(frequency_) * c_3b_ / square(extraction_radius_) -\n            4.0 * frequency_ * c_3b_ / pow<3>(extraction_radius_))) /\n      sqrt(2.0);\n\n  LinearizedBondiSachs_detail::assign_du_components_from_l_factors(\n      du_bondi_w, w_2_coefficient, w_3_coefficient, l_max, frequency_, time);\n}\n\nvoid LinearizedBondiSachs::spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        spherical_metric,\n    const size_t l_max, const double time) const {\n  Variables<tmpl::list<Tags::BondiJ, Tags::BondiU, Tags::BondiK, Tags::BondiW>>\n      temporary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  auto& bondi_j = get(get<Tags::BondiJ>(temporary_variables));\n  auto& bondi_u = get(get<Tags::BondiU>(temporary_variables));\n  auto& bondi_k = get(get<Tags::BondiK>(temporary_variables));\n  auto& bondi_w = get(get<Tags::BondiW>(temporary_variables));\n\n  linearized_bondi_j(make_not_null(&bondi_j), l_max, time);\n  linearized_bondi_u(make_not_null(&bondi_u), l_max, time);\n  linearized_bondi_w(make_not_null(&bondi_w), l_max, time);\n  bondi_k = sqrt(1.0 + bondi_j * conj(bondi_j));\n\n  get<0, 0>(*spherical_metric) =\n      -real(1.0 + extraction_radius_ * bondi_w.data() -\n            square(extraction_radius_) *\n                (conj(bondi_j.data()) * square(bondi_u.data()) +\n                 bondi_k.data() * bondi_u.data() * conj(bondi_u.data())));\n  get<0, 1>(*spherical_metric) = -1.0 - get<0, 0>(*spherical_metric);\n  get<0, 2>(*spherical_metric) =\n      square(extraction_radius_) * real(bondi_j.data() * conj(bondi_u.data()) +\n                                        bondi_k.data() * bondi_u.data());\n  get<0, 3>(*spherical_metric) =\n      square(extraction_radius_) * imag(bondi_j.data() * conj(bondi_u.data()) +\n                                        bondi_k.data() * bondi_u.data());\n  get<1, 1>(*spherical_metric) = get<0, 0>(*spherical_metric) + 2.0;\n  get<1, 2>(*spherical_metric) = -get<0, 2>(*spherical_metric);\n  get<1, 3>(*spherical_metric) = -get<0, 3>(*spherical_metric);\n  get<2, 2>(*spherical_metric) =\n      square(extraction_radius_) * real(bondi_j.data() + bondi_k.data());\n  get<2, 3>(*spherical_metric) =\n      imag(bondi_j.data() * square(extraction_radius_));\n  get<3, 3>(*spherical_metric) =\n      square(extraction_radius_) * real(bondi_k.data() - bondi_j.data());\n}\n\nvoid LinearizedBondiSachs::dr_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dr_spherical_metric,\n    const size_t l_max, const double time) const {\n  Variables<tmpl::list<Tags::BondiJ, Tags::BondiU, Tags::BondiK, Tags::BondiW,\n                       Tags::Dr<Tags::BondiJ>, Tags::Dr<Tags::BondiU>,\n                       Tags::Dr<Tags::BondiK>, Tags::Dr<Tags::BondiW>>>\n      temporary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  auto& bondi_j = get(get<Tags::BondiJ>(temporary_variables));\n  auto& bondi_u = get(get<Tags::BondiU>(temporary_variables));\n  auto& bondi_k = get(get<Tags::BondiK>(temporary_variables));\n  auto& bondi_w = get(get<Tags::BondiW>(temporary_variables));\n  auto& dr_bondi_j = get(get<Tags::Dr<Tags::BondiJ>>(temporary_variables));\n  auto& dr_bondi_u = get(get<Tags::Dr<Tags::BondiU>>(temporary_variables));\n  auto& dr_bondi_k = get(get<Tags::Dr<Tags::BondiK>>(temporary_variables));\n  auto& dr_bondi_w = get(get<Tags::Dr<Tags::BondiW>>(temporary_variables));\n\n  dt_spherical_metric(dr_spherical_metric, l_max, time);\n\n  linearized_bondi_j(make_not_null(&bondi_j), l_max, time);\n  linearized_bondi_u(make_not_null(&bondi_u), l_max, time);\n  linearized_bondi_w(make_not_null(&bondi_w), l_max, time);\n  bondi_k = sqrt(1.0 + bondi_j * conj(bondi_j));\n\n  linearized_dr_bondi_j(make_not_null(&dr_bondi_j), l_max, time);\n  linearized_dr_bondi_u(make_not_null(&dr_bondi_u), l_max, time);\n  linearized_dr_bondi_w(make_not_null(&dr_bondi_w), l_max, time);\n  dr_bondi_k =\n      0.5 * (dr_bondi_j * conj(bondi_j) + conj(dr_bondi_j) * bondi_j) / bondi_k;\n\n  // we introduce expression templates for clarity - each of these is only used\n  // once.\n  const auto dr_r_squared_jbar_u_squared =\n      conj(dr_bondi_j.data()) * square(extraction_radius_) *\n          square(bondi_u.data()) +\n      2.0 * conj(bondi_j.data()) * extraction_radius_ * square(bondi_u.data()) +\n      2.0 * conj(bondi_j.data()) * square(extraction_radius_) * bondi_u.data() *\n          dr_bondi_u.data();\n  const auto dr_r_squared_k_u_ubar =\n      dr_bondi_k.data() * square(extraction_radius_) * bondi_u.data() *\n          conj(bondi_u.data()) +\n      2.0 * bondi_k.data() * extraction_radius_ * bondi_u.data() *\n          conj(bondi_u.data()) +\n      bondi_k.data() * square(extraction_radius_) *\n          (dr_bondi_u.data() * conj(bondi_u.data()) +\n           bondi_u.data() * conj(dr_bondi_u.data()));\n  get<0, 0>(*dr_spherical_metric) =\n      -get<0, 0>(*dr_spherical_metric) +\n      real(dr_r_squared_jbar_u_squared + dr_r_squared_k_u_ubar -\n           bondi_w.data() - extraction_radius_ * dr_bondi_w.data());\n\n  get<0, 1>(*dr_spherical_metric) = -get<0, 0>(*dr_spherical_metric);\n\n  const ComplexDataVector dr_r_squared_j_ubar =\n      2.0 * bondi_j.data() * extraction_radius_ * conj(bondi_u.data()) +\n      square(extraction_radius_) * (dr_bondi_j.data() * conj(bondi_u.data()) +\n                                    bondi_j.data() * conj(dr_bondi_u.data()));\n  const ComplexDataVector dr_r_squared_k_u =\n      2.0 * bondi_k.data() * extraction_radius_ * bondi_u.data() +\n      square(extraction_radius_) * (dr_bondi_k.data() * bondi_u.data() +\n                                    bondi_k.data() * dr_bondi_u.data());\n  get<0, 2>(*dr_spherical_metric) =\n      -get<0, 2>(*dr_spherical_metric) +\n      real(dr_r_squared_j_ubar + dr_r_squared_k_u);\n\n  get<0, 3>(*dr_spherical_metric) =\n      -get<0, 3>(*dr_spherical_metric) +\n      imag(dr_r_squared_j_ubar + dr_r_squared_k_u);\n\n  get<1, 1>(*dr_spherical_metric) = get<0, 0>(*dr_spherical_metric);\n  get<1, 2>(*dr_spherical_metric) = -get<0, 2>(*dr_spherical_metric);\n  get<1, 3>(*dr_spherical_metric) = -get<0, 3>(*dr_spherical_metric);\n  get<2, 2>(*dr_spherical_metric) =\n      -get<2, 2>(*dr_spherical_metric) +\n      2.0 * extraction_radius_ * real(bondi_j.data() + bondi_k.data()) +\n      square(extraction_radius_) * real(dr_bondi_j.data() + dr_bondi_k.data());\n  get<2, 3>(*dr_spherical_metric) =\n      -get<2, 3>(*dr_spherical_metric) +\n      imag(2.0 * bondi_j.data() * extraction_radius_ +\n           dr_bondi_j.data() * square(extraction_radius_));\n  get<3, 3>(*dr_spherical_metric) =\n      -get<3, 3>(*dr_spherical_metric) +\n      2.0 * extraction_radius_ * real(bondi_k.data() - bondi_j.data()) +\n      square(extraction_radius_) * real(dr_bondi_k.data() - dr_bondi_j.data());\n}\n\nvoid LinearizedBondiSachs::dt_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dt_spherical_metric,\n    const size_t l_max, const double time) const {\n  Variables<tmpl::list<Tags::BondiJ, Tags::BondiU, Tags::BondiK, Tags::BondiW,\n                       Tags::Du<Tags::BondiJ>, Tags::Du<Tags::BondiU>,\n                       Tags::Du<Tags::BondiK>, Tags::Du<Tags::BondiW>>>\n      temporary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  auto& bondi_j = get(get<Tags::BondiJ>(temporary_variables));\n  auto& bondi_u = get(get<Tags::BondiU>(temporary_variables));\n  auto& bondi_k = get(get<Tags::BondiK>(temporary_variables));\n  auto& bondi_w = get(get<Tags::BondiW>(temporary_variables));\n  auto& du_bondi_j = get(get<Tags::Du<Tags::BondiJ>>(temporary_variables));\n  auto& du_bondi_u = get(get<Tags::Du<Tags::BondiU>>(temporary_variables));\n  auto& du_bondi_k = get(get<Tags::Du<Tags::BondiK>>(temporary_variables));\n  auto& du_bondi_w = get(get<Tags::Du<Tags::BondiW>>(temporary_variables));\n\n  linearized_bondi_j(make_not_null(&bondi_j), l_max, time);\n  linearized_bondi_u(make_not_null(&bondi_u), l_max, time);\n  linearized_bondi_w(make_not_null(&bondi_w), l_max, time);\n  bondi_k = sqrt(1.0 + bondi_j * conj(bondi_j));\n\n  linearized_du_bondi_j(make_not_null(&du_bondi_j), l_max, time);\n  linearized_du_bondi_u(make_not_null(&du_bondi_u), l_max, time);\n  linearized_du_bondi_w(make_not_null(&du_bondi_w), l_max, time);\n  du_bondi_k =\n      0.5 * (du_bondi_j * conj(bondi_j) + conj(du_bondi_j) * bondi_j) / bondi_k;\n\n  // we introduce expression templates for clarity - each of these is only used\n  // once.\n  const auto du_r_squared_jbar_u_squared =\n      conj(du_bondi_j.data()) * square(extraction_radius_) *\n          square(bondi_u.data()) +\n      2.0 * conj(bondi_j.data()) * square(extraction_radius_) * bondi_u.data() *\n          du_bondi_u.data();\n  const auto du_r_squared_k_u_ubar =\n      du_bondi_k.data() * square(extraction_radius_) * bondi_u.data() *\n          conj(bondi_u.data()) +\n      bondi_k.data() * square(extraction_radius_) *\n          (du_bondi_u.data() * conj(bondi_u.data()) +\n           bondi_u.data() * conj(du_bondi_u.data()));\n  get<0, 0>(*dt_spherical_metric) =\n      real(du_r_squared_jbar_u_squared + du_r_squared_k_u_ubar -\n           extraction_radius_ * du_bondi_w.data());\n\n  get<0, 1>(*dt_spherical_metric) = -get<0, 0>(*dt_spherical_metric);\n\n  const ComplexDataVector du_r_squared_j_ubar =\n      square(extraction_radius_) * (du_bondi_j.data() * conj(bondi_u.data()) +\n                                    bondi_j.data() * conj(du_bondi_u.data()));\n  const ComplexDataVector du_r_squared_k_u =\n      square(extraction_radius_) *\n      (du_bondi_k.data() * bondi_u.data() + bondi_k.data() * du_bondi_u.data());\n  get<0, 2>(*dt_spherical_metric) =\n      real(du_r_squared_j_ubar + du_r_squared_k_u);\n  get<0, 3>(*dt_spherical_metric) =\n      imag(du_r_squared_j_ubar + du_r_squared_k_u);\n  get<1, 1>(*dt_spherical_metric) = get<0, 0>(*dt_spherical_metric);\n  get<1, 2>(*dt_spherical_metric) = -get<0, 2>(*dt_spherical_metric);\n  get<1, 3>(*dt_spherical_metric) = -get<0, 3>(*dt_spherical_metric);\n  get<2, 2>(*dt_spherical_metric) =\n      square(extraction_radius_) * real(du_bondi_j.data() + du_bondi_k.data());\n  get<2, 3>(*dt_spherical_metric) =\n      imag(du_bondi_j.data() * square(extraction_radius_));\n  get<3, 3>(*dt_spherical_metric) =\n      square(extraction_radius_) * real(du_bondi_k.data() - du_bondi_j.data());\n}\n\nvoid LinearizedBondiSachs::variables_impl(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n    const size_t l_max, const double time,\n    tmpl::type_<Tags::News> /*meta*/) const {\n  const std::complex<double> news_2_coefficient =\n      std::complex<double>(0.0, 0.5) * pow<3>(frequency_) * c_2b_ / sqrt(3.0);\n\n  const std::complex<double> news_3_coefficient =\n      -pow<4>(frequency_) * c_3b_ / sqrt(15.0);\n\n  LinearizedBondiSachs_detail::assign_components_from_l_factors(\n      make_not_null(&get(*news)), news_2_coefficient, news_3_coefficient, l_max,\n      frequency_, time);\n}\n\nstd::unique_ptr<Cce::InitializeJ::InitializeJ<false>>\nLinearizedBondiSachs::get_initialize_j(const double start_time) const {\n  return std::make_unique<\n      LinearizedBondiSachs_detail::InitializeJ::LinearizedBondiSachs>(\n      start_time, frequency_, c_2a_, c_2b_, c_3a_, c_3b_);\n}\n\nvoid LinearizedBondiSachs::pup(PUP::er& p) {\n  SphericalMetricData::pup(p);\n  p | c_2a_;\n  p | c_3a_;\n  p | c_2b_;\n  p | c_3b_;\n  p | frequency_;\n}\n\nPUP::able::PUP_ID LinearizedBondiSachs::my_PUP_ID = 0;\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Options/StdComplex.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce::Solutions {\nnamespace LinearizedBondiSachs_detail {\nnamespace InitializeJ {\n// First hypersurface Initialization for the\n// `Cce::Solutions::LinearizedBondiSachs` analytic solution.\n//\n// This initialization procedure should not be used except when the\n// `Cce::Solutions::LinearizedBondiSachs` analytic solution is used,\n// as a consequence, this initial data generator is deliberately not\n// option-creatable; it should only be obtained from the `get_initialize_j`\n// function of `Cce::InitializeJ::LinearizedBondiSachs`.\nstruct LinearizedBondiSachs : ::Cce::InitializeJ::InitializeJ<false> {\n  WRAPPED_PUPable_decl_template(LinearizedBondiSachs);  // NOLINT\n  explicit LinearizedBondiSachs(CkMigrateMessage* /*unused*/) {}\n\n  LinearizedBondiSachs() = default;\n\n  LinearizedBondiSachs(double start_time, double frequency,\n                       std::complex<double> c_2a, std::complex<double> c_2b,\n                       std::complex<double> c_3a, std::complex<double> c_3b);\n\n  std::unique_ptr<InitializeJ> get_clone() const override;\n\n  void operator()(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, size_t l_max,\n      size_t number_of_radial_points,\n      gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  std::complex<double> c_2a_ = std::numeric_limits<double>::signaling_NaN();\n  std::complex<double> c_2b_ = std::numeric_limits<double>::signaling_NaN();\n  std::complex<double> c_3a_ = std::numeric_limits<double>::signaling_NaN();\n  std::complex<double> c_3b_ = std::numeric_limits<double>::signaling_NaN();\n  double frequency_ = std::numeric_limits<double>::signaling_NaN();\n  double time_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace InitializeJ\n\n// combine the (2,2) and (3,3) modes to collocation values for\n// `bondi_quantity`\ntemplate <int Spin, typename FactorType>\nvoid assign_components_from_l_factors(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> bondi_quantity,\n    const FactorType& l_2_factor, const FactorType& l_3_factor, size_t l_max,\n    double frequency, double time);\n\n// combine the (2,2) and (3,3) modes to time derivative collocation values for\n// `bondi_quantity`\ntemplate <int Spin>\nvoid assign_du_components_from_l_factors(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> du_bondi_quantity,\n    const std::complex<double>& l_2_factor,\n    const std::complex<double>& l_3_factor, size_t l_max, double frequency,\n    double time);\n}  // namespace LinearizedBondiSachs_detail\n\n/*!\n * \\brief Computes the analytic data for a Linearized solution to the\n * Bondi-Sachs equations described in \\cite Barkett2019uae.\n *\n * \\details The solution represented by this function is generated with only\n * \\f$(2,\\pm2)\\f$ and \\f$(3,\\pm3)\\f$ modes, and is constructed according to the\n * linearized solution documented in Section VI of \\cite Barkett2019uae. For\n * this solution, we impose additional restrictions that the linearized solution\n * be asymptotically flat so that it is compatible with the gauge\n * transformations performed in the SpECTRE regularity-preserving CCE. Using the\n * notation of \\cite Barkett2019uae, we set:\n *\n * \\f{align*}{\n * B_2 &= B_3 = 0\\\\\n * C_{2b} &= 3 C_{2a} / \\nu^2\\\\\n * C_{3b} &= -3 i C_{3a} / \\nu^3\n * \\f}\n *\n * where \\f$C_{2a}\\f$ and \\f$C_{3a}\\f$ may be specified freely and are taken via\n * input option `InitialModes`.\n */\nstruct LinearizedBondiSachs : public SphericalMetricData {\n  struct InitialModes {\n    using type = std::array<std::complex<double>, 2>;\n    static constexpr Options::String help{\n        \"The initial modes of the Robinson-Trautman scalar\"};\n  };\n  struct ExtractionRadius {\n    using type = double;\n    static constexpr Options::String help{\n        \"The extraction radius of the spherical solution\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Frequency {\n    using type = double;\n    static constexpr Options::String help{\n        \"The frequency of the linearized modes.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  static constexpr Options::String help{\n    \"A linearized Bondi-Sachs analytic solution\"};\n\n  using options = tmpl::list<InitialModes, ExtractionRadius, Frequency>;\n\n  WRAPPED_PUPable_decl_template(LinearizedBondiSachs);  // NOLINT\n\n  explicit LinearizedBondiSachs(CkMigrateMessage* msg)\n      : SphericalMetricData(msg) {}\n\n  // clang doesn't manage to use = default correctly in this case\n  // NOLINTNEXTLINE(hicpp-use-equals-default,modernize-use-equals-default)\n  LinearizedBondiSachs() {}\n\n  LinearizedBondiSachs(\n      const std::array<std::complex<double>, 2>& mode_constants,\n      double extraction_radius, double frequency);\n\n  std::unique_ptr<WorldtubeData> get_clone() const override;\n\n  void pup(PUP::er& p) override;\n\n  std::unique_ptr<Cce::InitializeJ::InitializeJ<false>> get_initialize_j(\n      double start_time) const override;\n\n protected:\n  /// A no-op as the linearized solution does not have substantial shared\n  /// computation to prepare before the separate component calculations.\n  void prepare_solution(const size_t /*output_l_max*/,\n                        const double /*time*/) const override {}\n\n  /*!\n   * \\brief Computes the linearized solution for \\f$J\\f$.\n   *\n   * \\details The linearized solution for \\f$J\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * J = \\sqrt{12} ({}_2 Y_{2\\,2} + {}_2 Y_{2\\, -2})\n   *  \\mathrm{Re}(J_2(r) e^{i \\nu u})\n   * + \\sqrt{60} ({}_2 Y_{3\\,3} - {}_2 Y_{3\\, -3})\n   * \\mathrm{Re}(J_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * J_2(r) &= \\frac{C_{2a}}{4 r} - \\frac{C_{2b}}{12 r^3}, \\\\\n   * J_3(r) &= \\frac{C_{3a}}{10 r} - \\frac{i \\nu C_{3 b}}{6 r^3}\n   * - \\frac{C_{3 b}}{4 r^4}.\n   * \\f}\n   */\n  void linearized_bondi_j(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> bondi_j, size_t l_max,\n      double time) const;\n\n  /*!\n   * \\brief Compute the linearized solution for \\f$U\\f$\n   *\n   * \\details The linearized solution for \\f$U\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * U = \\sqrt{3} ({}_1 Y_{2\\,2} + {}_1 Y_{2\\, -2})\n   *  \\mathrm{Re}(U_2(r) e^{i \\nu u})\n   * + \\sqrt{6} ({}_1 Y_{3\\,3} - {}_1 Y_{3\\, -3})\n   * \\mathrm{Re}(U_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * U_2(r) &= \\frac{C_{2a}}{2 r^2} + \\frac{i \\nu C_{2 b}}{3 r^3}\n   * + \\frac{C_{2b}}{4 r^4} \\\\\n   * U_3(r) &= \\frac{C_{3a}}{2 r^2} - \\frac{2 \\nu^2 C_{3b}}{3 r^3}\n   * + \\frac{5 i \\nu C_{3b}}{4 r^4} + \\frac{C_{3 b}}{r^5}\n   * \\f}\n   */\n  void linearized_bondi_u(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> bondi_u, size_t l_max,\n      double time) const;\n\n  /*!\n   * \\brief Computes the linearized solution for \\f$W\\f$.\n   *\n   * \\details The linearized solution for \\f$W\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * W = \\frac{1}{\\sqrt{2}} ({}_0 Y_{2\\,2} + {}_0 Y_{2\\, -2})\n   *  \\mathrm{Re}(W_2(r) e^{i \\nu u})\n   * + \\frac{1}{\\sqrt{2}} ({}_0 Y_{3\\,3} - {}_0 Y_{3\\, -3})\n   * \\mathrm{Re}(W_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * W_2(r) &= - \\frac{\\nu^2 C_{2b}}{r^2} + \\frac{i \\nu C_{2 b}}{r^3}\n   * + \\frac{C_{2b}}{2 r^4}, \\\\\n   * W_3(r) &= -\\frac{2 i \\nu^3 C_{3b}}{r^2} - \\frac{4 i \\nu^2 C_{3b}}{r^3}\n   * + \\frac{5 \\nu C_{3b}}{2 r^4} + \\frac{3 C_{3b}}{r^5}.\n   * \\f}\n   */\n  void linearized_bondi_w(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> bondi_w, size_t l_max,\n      double time) const;\n\n  /*!\n   * \\brief Computes the linearized solution for \\f$\\partial_r J\\f$.\n   *\n   * \\details The linearized solution for \\f$\\partial_r J\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * \\partial_r J = \\sqrt{12} ({}_2 Y_{2\\,2} + {}_2 Y_{2\\, -2})\n   *  \\mathrm{Re}(\\partial_r J_2(r) e^{i \\nu u})\n   * + \\sqrt{60} ({}_2 Y_{3\\,3} - {}_2 Y_{3\\, -3})\n   * \\mathrm{Re}(\\partial_r J_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * \\partial_r J_2(r) &= - \\frac{C_{2a}}{4 r^2} + \\frac{C_{2b}}{4 r^4}, \\\\\n   * \\partial_r J_3(r) &= -\\frac{C_{3a}}{10 r^2} + \\frac{i \\nu C_{3 b}}{2 r^4}\n   * + \\frac{C_{3 b}}{r^5}.\n   * \\f}\n   */\n  void linearized_dr_bondi_j(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> dr_bondi_j,\n      size_t l_max, double time) const;\n\n  /*!\n   * \\brief Compute the linearized solution for \\f$\\partial_r U\\f$\n   *\n   * \\details The linearized solution for \\f$\\partial_r U\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * \\partial_r U = \\sqrt{3} ({}_1 Y_{2\\,2} + {}_1 Y_{2\\, -2})\n   *  \\mathrm{Re}(\\partial_r U_2(r) e^{i \\nu u})\n   * + \\sqrt{6} ({}_1 Y_{3\\,3} - {}_1 Y_{3\\, -3})\n   * \\mathrm{Re}(\\partial_r U_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * \\partial_r U_2(r) &= -\\frac{C_{2a}}{r^3} - \\frac{i \\nu C_{2 b}}{r^4}\n   * - \\frac{C_{2b}}{r^5} \\\\\n   * \\partial_r U_3(r) &= -\\frac{C_{3a}}{r^3} + \\frac{2 \\nu^2 C_{3b}}{r^4}\n   * - \\frac{5 i \\nu C_{3b}}{r^5} - \\frac{5 C_{3 b}}{r^6}\n   * \\f}\n   */\n  void linearized_dr_bondi_u(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> dr_bondi_u,\n      size_t l_max, double time) const;\n\n  /*!\n   * \\brief Computes the linearized solution for \\f$\\partial_r W\\f$.\n   *\n   * \\details The linearized solution for \\f$W\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * \\partial_r W = \\frac{1}{\\sqrt{2}} ({}_0 Y_{2\\,2} + {}_0 Y_{2\\, -2})\n   *  \\mathrm{Re}(\\partial_r W_2(r) e^{i \\nu u})\n   * + \\frac{1}{\\sqrt{2}} ({}_0 Y_{3\\,3} - {}_0 Y_{3\\, -3})\n   * \\mathrm{Re}(\\partial_r W_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * \\partial_r W_2(r) &= \\frac{2 \\nu^2 C_{2b}}{r^3}\n   * - \\frac{3 i \\nu C_{2 b}}{r^4} - \\frac{2 C_{2b}}{r^5}, \\\\\n   * \\partial_r W_3(r) &= \\frac{4 i \\nu^3 C_{3b}}{r^3}\n   * + \\frac{12 i \\nu^2  C_{3b}}{r^4} - \\frac{10 \\nu C_{3b}}{r^5}\n   * - \\frac{15 C_{3b}}{r^6}.\n   * \\f}\n   */\n  void linearized_dr_bondi_w(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> dr_bondi_w,\n      size_t l_max, double time) const;\n\n  /*!\n   * \\brief Computes the linearized solution for \\f$\\partial_u J\\f$.\n   *\n   * \\details The linearized solution for \\f$\\partial_u J\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * \\partial_u J = \\sqrt{12} ({}_2 Y_{2\\,2} + {}_2 Y_{2\\, -2})\n   *  \\mathrm{Re}(i \\nu J_2(r)  e^{i \\nu u})\n   * + \\sqrt{60} ({}_2 Y_{3\\,3} - {}_2 Y_{3\\, -3})\n   * \\mathrm{Re}(i \\nu J_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * J_2(r) &= \\frac{C_{2a}}{4 r} - \\frac{C_{2b}}{12 r^3}, \\\\\n   * J_3(r) &= \\frac{C_{3a}}{10 r} - \\frac{i \\nu C_{3 b}}{6 r^3}\n   * - \\frac{C_{3 b}}{4 r^4}.\n   * \\f}\n   */\n  void linearized_du_bondi_j(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> du_bondi_j,\n      size_t l_max, double time) const;\n\n  /*!\n   * \\brief Compute the linearized solution for \\f$\\partial_u U\\f$\n   *\n   * \\details The linearized solution for \\f$U\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * \\partial_u U = \\sqrt{3} ({}_2 Y_{2\\,2} + {}_2 Y_{2\\, -2})\n   *  \\mathrm{Re}(i \\nu U_2(r) e^{i \\nu u})\n   * + \\sqrt{6} ({}_2 Y_{3\\,3} - {}_2 Y_{3\\, -3})\n   * \\mathrm{Re}(i \\nu U_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * U_2(r) &= \\frac{C_{2a}}{2 r^2} + \\frac{i \\nu C_{2 b}}{3 r^3}\n   * + \\frac{C_{2b}}{4 r^4} \\\\\n   * U_3(r) &= \\frac{C_{3a}}{2 r^2} - \\frac{2 \\nu^2 C_{3b}}{3 r^3}\n   * + \\frac{5 i \\nu C_{3b}}{4 r^4} + \\frac{C_{3 b}}{r^5}\n   * \\f}\n   */\n  void linearized_du_bondi_u(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> du_bondi_u,\n      size_t l_max, double time) const;\n\n  /*!\n   * \\brief Computes the linearized solution for \\f$\\partial_u W\\f$.\n   *\n   * \\details The linearized solution for \\f$\\partial_u W\\f$ is given by\n   * \\cite Barkett2019uae,\n   *\n   * \\f[\n   * \\partial_u W = \\frac{1}{\\sqrt{2}} ({}_1 Y_{2\\,2} + {}_1 Y_{2\\, -2})\n   *  \\mathrm{Re}(i \\nu W_2(r) e^{i \\nu u})\n   * + \\frac{1}{\\sqrt{2}} ({}_1 Y_{3\\,3} - {}_1 Y_{3\\, -3})\n   * \\mathrm{Re}(i \\nu W_3(r) e^{i \\nu u}),\n   * \\f]\n   *\n   * where\n   *\n   * \\f{align*}{\n   * W_2(r) &= \\frac{\\nu^2 C_{2b}}{r^2} + \\frac{i \\nu C_{2 b}}{r^3}\n   * + \\frac{C_{2b}}{2 r^4}, \\\\\n   * W_3(r) &= \\frac{2 i \\nu^3 C_{3b}}{r^2} - \\frac{4 i \\nu^2 C_{3b}}{r^3}\n   * + \\frac{5 \\nu C_{3b}}{2 r^4} + \\frac{3 C_{3b}}{r^5}.\n   * \\f}\n   */\n  void linearized_du_bondi_w(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> du_bondi_w,\n      size_t l_max, double time) const;\n\n  /*!\n   * \\brief Compute the spherical coordinate metric from the linearized\n   * Bondi-Sachs system.\n   *\n   * \\details This function dispatches to the individual computations in this\n   * class to determine the Bondi-Sachs scalars for the linearized solution.\n   * Once the scalars are determined, the metric is assembled via (note\n   * \\f$\\beta = 0\\f$ in this solution)\n   *\n   * \\f{align*}{\n   * ds^2 =& - ((1 + r W) - r^2 h_{A B} U^A U^B) (dt - dr)^2\n   * - 2  (dt - dr) dr \\\\\n   * &- 2 r^2 h_{A B} U^B (dt - dr) dx^A + r^2 h_{A B} dx^A dx^B,\n   * \\f}\n   *\n   * where indices with capital letters refer to angular coordinates and the\n   * angular tensors may be written in terms of spin-weighted scalars. Doing so\n   * gives the metric components,\n   *\n   * \\f{align*}{\n   * g_{t t} &= -\\left(1 + r W\n   * - r^2 \\Re\\left(\\bar J U^2 + K U \\bar U\\right)\\right)\\\\\n   * g_{t r} &= -1 - g_{t t}\\\\\n   * g_{r r} &= 2 + g_{t t}\\\\\n   * g_{t \\theta} &= r^2 \\Re\\left(K U + J \\bar U\\right)\\\\\n   * g_{t \\phi} &= r^2 \\Im\\left(K U + J \\bar U\\right)\\\\\n   * g_{r \\theta} &= -g_{t \\theta}\\\\\n   * g_{r \\phi} &= -g_{t \\phi}\\\\\n   * g_{\\theta \\theta} &= r^2 \\Re\\left(J + K\\right)\\\\\n   * g_{\\theta \\phi} &= r^2 \\Im\\left(J\\right)\\\\\n   * g_{\\phi \\phi} &= r^2 \\Re\\left(K - J\\right),\n   * \\f}\n   *\n   * and all other components are zero.\n   */\n  void spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the radial derivative of the spherical coordinate metric\n   * from the linearized Bondi-Sachs system.\n   *\n   * \\details This function dispatches to the individual computations in this\n   * class to determine the Bondi-Sachs scalars for the linearized solution.\n   * Once the scalars are determined, the radial derivative of the metric is\n   * assembled via (note \\f$\\beta = 0\\f$ in this solution)\n   *\n   * \\f{align*}{\n   * \\partial_r g_{a b} dx^a dx^b =& - (W + r \\partial_r W\n   * - 2 r h_{A B} U^A U^B - r^2 (\\partial_r h_{A B}) U^A U^B\n   * - 2 r^2 h_{A B} U^A \\partial_r U^B) (dt - dr)^2 \\\\\n   * &- (4 r h_{A B} U^B + 2 r^2 ((\\partial_r h_{A B}) U^B\n   * + h_{AB} \\partial_r U^B) ) (dt - dr) dx^A\n   * + (2 r h_{A B} + r^2 \\partial_r h_{A B}) dx^A dx^B,\n   * \\f}\n   *\n   * where indices with capital letters refer to angular coordinates and the\n   * angular tensors may be written in terms of spin-weighted scalars. Doing so\n   * gives the metric components,\n   *\n   * \\f{align*}{\n   * \\partial_r g_{t t} &= -\\left( W + r \\partial_r W\n   *  - 2 r \\Re\\left(\\bar J U^2 + K U \\bar U\\right)\n   * - r^2 \\partial_r \\Re\\left(\\bar J U^2 + K U \\bar U\\right)\\right) \\\\\n   * \\partial_r g_{t r} &= -\\partial_r g_{t t}\\\\\n   * \\partial_r g_{t \\theta} &= 2 r \\Re\\left(K U + J \\bar U\\right)\n   *   + r^2 \\partial_r \\Re\\left(K U + J \\bar U\\right) \\\\\n   * \\partial_r g_{t \\phi} &= 2r \\Im\\left(K U + J \\bar U\\right)\n   *   + r^2 \\partial_r \\Im\\left(K U + J \\bar U\\right) \\\\\n   * \\partial_r g_{r r} &= \\partial_r g_{t t}\\\\\n   * \\partial_r g_{r \\theta} &= -\\partial_r g_{t \\theta}\\\\\n   * \\partial_r g_{r \\phi} &= -\\partial_r g_{t \\phi}\\\\\n   * \\partial_r g_{\\theta \\theta} &= 2 r \\Re\\left(J + K\\right)\n   *   + r^2 \\Re\\left(\\partial_r J + \\partial_r K\\right) \\\\\n   * \\partial_r g_{\\theta \\phi} &= 2 r \\Im\\left(J\\right)\n   *  + r^2 \\Im\\left(\\partial_r J\\right)\\\\\n   * \\partial_r g_{\\phi \\phi} &= 2 r \\Re\\left(K - J\\right)\n   *  + r^2 \\Re\\left(\\partial_r K - \\partial_r J\\right),\n   * \\f}\n   *\n   * and all other components are zero.\n   */\n  void dr_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dr_spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the time derivative of the spherical coordinate metric from\n   * the linearized Bondi-Sachs system.\n   *\n   * \\details This function dispatches to the individual computations in this\n   * class to determine the Bondi-Sachs scalars for the linearized solution.\n   * Once the scalars are determined, the metric is assembled via (note\n   * \\f$\\beta = 0\\f$ in this solution, and note that we take coordinate\n   * \\f$t=u\\f$ in converting to the Cartesian coordinates)\n   *\n   * \\f{align*}{\n   * \\partial_t g_{a b} dx^a dx^b =& - (r \\partial_u W\n   * - r^2 \\partial_u h_{A B} U^A U^B\n   * - 2 r^2 h_{A B} U^B \\partial_u U^A) (dt - dr)^2 \\\\\n   * &- 2 r^2 (\\partial_u h_{A B} U^B + h_{A B} \\partial_u U^B) (dt - dr) dx^A\n   * + r^2 \\partial_u h_{A B} dx^A dx^B,\n   * \\f}\n   *\n   * where indices with capital letters refer to angular coordinates and the\n   * angular tensors may be written in terms of spin-weighted scalars. Doing so\n   * gives the metric components,\n   *\n   * \\f{align*}{\n   * \\partial_t g_{t t} &= -\\left(r \\partial_u W\n   * - r^2 \\partial_u \\Re\\left(\\bar J U^2 + K U \\bar U\\right)\\right)\\\\\n   * \\partial_t g_{t r} &= -\\partial_t g_{t t}\\\\\n   * \\partial_t g_{t \\theta} &= r^2 \\partial_u \\Re\\left(K U + J \\bar U\\right)\\\\\n   * \\partial_t g_{t \\phi} &= r^2 \\partial_u \\Im\\left(K U + J \\bar U\\right)\\\\\n   * \\partial_t g_{r r} &= \\partial_t g_{t t}\\\\\n   * \\partial_t g_{r \\theta} &= -\\partial_t g_{t \\theta}\\\\\n   * \\partial_t g_{r \\phi} &= -\\partial_t g_{t \\phi}\\\\\n   * \\partial_t g_{\\theta \\theta} &= r^2 \\Re\\left(\\partial_u J\n   *  + \\partial_u K\\right)\\\\\n   * \\partial_t g_{\\theta \\phi} &= r^2 \\Im\\left(\\partial_u J\\right)\\\\\n   * \\partial_t g_{\\phi \\phi} &= r^2 \\Re\\left(\\partial_u K\n   *  - \\partial_u J\\right),\n   * \\f}\n   *\n   * and all other components are zero.\n   */\n  void dt_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dt_spherical_metric,\n      size_t l_max, double time) const override;\n\n  using WorldtubeData::variables_impl;\n\n  using SphericalMetricData::variables_impl;\n\n  /*!\n   * \\brief Determines the News function from the linearized solution\n   * parameters.\n   *\n   * \\details The News is determined from the formula given in\n   * \\cite Barkett2019uae,\n   *\n   * \\f{align*}{\n   * N = \\frac{1}{2 \\sqrt{3}} ({}_{-2} Y_{2\\, 2} + {}_{-2} Y_{2\\,-2})\n   * \\Re\\left(i \\nu^3 C_{2 b} e^{i \\nu u}\\right)\n   * + \\frac{1}{\\sqrt{15}} ({}_{-2} Y_{3\\, 3} - {}_{-2} Y_{3\\, -3})\n   * \\Re\\left(- \\nu^4 C_{3 b} e^{i \\nu u} \\right)\n   * \\f}\n   */\n  void variables_impl(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n      size_t l_max, double time,\n      tmpl::type_<Tags::News> /*meta*/) const override;\n\n  std::complex<double> c_2a_ = std::numeric_limits<double>::signaling_NaN();\n  std::complex<double> c_3a_ = std::numeric_limits<double>::signaling_NaN();\n  std::complex<double> c_2b_ = std::numeric_limits<double>::signaling_NaN();\n  std::complex<double> c_3b_ = std::numeric_limits<double>::signaling_NaN();\n\n  double frequency_ = 0.0;\n};\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.hpp\"\n\n#include <algorithm>\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/OdeIntegration/OdeIntegration.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\n\nRobinsonTrautman::RobinsonTrautman(\n    std::vector<std::complex<double>> initial_modes,\n    const double extraction_radius, const size_t l_max, const double tolerance,\n    const double start_time, const Options::Context& context)\n    : SphericalMetricData{extraction_radius},\n      dense_output_rt_scalar_{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)},\n      dense_output_du_rt_scalar_{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)},\n      l_max_{l_max},\n      tolerance_{tolerance},\n      start_time_{start_time},\n      initial_modes_{std::move(initial_modes)} {\n  if (initial_modes_.size() > square(l_max + 1)) {\n    PARSE_ERROR(context,\n                \"There must not be more than (l_max + 1)^2 modes specified for \"\n                \"InitialModes\");\n  }\n  if (tolerance == 0.0) {\n    PARSE_ERROR(context,\n                \"A target tolerance of 0.0 is not permitted, as it will cause \"\n                \"the time-stepper to enter an infinite loop.\");\n  }\n  initialize_stepper_from_start();\n}\n\nvoid RobinsonTrautman::initialize_stepper_from_start() const {\n  // create the initial data\n  SpinWeighted<ComplexModalVector, 0> goldberg_modes{square(l_max_ + 1), 0.0};\n  for (size_t i = 0; i < std::min(initial_modes_.size(), goldberg_modes.size());\n       ++i) {\n    goldberg_modes.data()[i] = gsl::at(initial_modes_, i);\n  }\n\n  SpinWeighted<ComplexModalVector, 0> libsharp_modes{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max_)};\n  const auto& coefficients_metadata =\n      Spectral::Swsh::cached_coefficients_metadata(l_max_);\n  for (const auto& coefficient : coefficients_metadata) {\n    Spectral::Swsh::goldberg_modes_to_libsharp_modes_single_pair(\n        coefficient, make_not_null(&libsharp_modes), 0,\n        goldberg_modes.data()[Spectral::Swsh::goldberg_mode_index(\n            l_max_, coefficient.l, static_cast<int>(coefficient.m), 0)],\n        goldberg_modes.data()[Spectral::Swsh::goldberg_mode_index(\n            l_max_, coefficient.l, -static_cast<int>(coefficient.m), 0)]);\n  }\n\n  {\n    // re-use the `dense_output_rt_scalar_` buffers to reduce allocations\n    auto& initial_rt_scalar = get(dense_output_rt_scalar_);\n    auto& initial_du_rt_scalar = get(dense_output_du_rt_scalar_);\n\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max_, 1, make_not_null(&initial_rt_scalar), libsharp_modes);\n    // Add the modes specified in the input file to the leading-order solution\n    // of 1.0 for the Robinson-Trautman scalar.\n    initial_rt_scalar.data() =\n        1.0 + std::complex<double>(1.0, 0.0) * real(initial_rt_scalar.data());\n    du_rt_scalar(make_not_null(&initial_du_rt_scalar), initial_rt_scalar);\n    stepper_ = boost::numeric::odeint::make_dense_output(\n        tolerance_ * 0.01, tolerance_,\n        boost::numeric::odeint::runge_kutta_dopri5<ComplexDataVector>{});\n    const double rt_scalar_scale = max(abs(initial_rt_scalar.data()));\n    const double du_rt_scalar_scale = max(abs(initial_du_rt_scalar.data()));\n\n    // Take as an initial step guess a conservative factor multiplied by the\n    // rough scaling of the grid spacing. Typically, the initial_step should be\n    // set by the guess based on the scale of the fields inside the `if`, but\n    // this guess is typically acceptable when the field scale is not usable for\n    // step estimation.\n    double initial_step = extraction_radius_ * 1.0e-4 / square(l_max_);\n    if (rt_scalar_scale > 1.0e-5 and du_rt_scalar_scale > 1.0e-5) {\n      // set initial step size according to the first couple of steps in section\n      // II.4 of Solving Ordinary Differential equations by Hairer, Norsett, and\n      // Wanner\n      initial_step =\n          0.1 * rt_scalar_scale / (du_rt_scalar_scale * square(l_max_));\n    }\n    stepper_.initialize(initial_rt_scalar.data(), start_time_, initial_step);\n    // scoped to destruct buffer reference\n  }\n  const auto rt_system = [this](const ComplexDataVector& local_rt_scalar,\n                                ComplexDataVector& local_du_rt_scalar,\n                                const double /*t*/) {\n    local_du_rt_scalar.destructive_resize(local_rt_scalar.size());\n    const SpinWeighted<ComplexDataVector, 0> rt_scalar_reference;\n    make_const_view(make_not_null(&rt_scalar_reference.data()), local_rt_scalar,\n                    0, local_rt_scalar.size());\n    SpinWeighted<ComplexDataVector, 0> du_rt_scalar_reference;\n    du_rt_scalar_reference.set_data_ref(local_du_rt_scalar.data(),\n                                        local_du_rt_scalar.size());\n    du_rt_scalar(make_not_null(&du_rt_scalar_reference), rt_scalar_reference);\n    Spectral::Swsh::filter_swsh_boundary_quantity(\n        make_not_null(&du_rt_scalar_reference), l_max_, l_max_ - 3);\n  };\n  step_range_ = stepper_.do_step(rt_system);\n}\n\nstd::unique_ptr<WorldtubeData> RobinsonTrautman::get_clone() const {\n  return std::make_unique<RobinsonTrautman>(*this);\n}\n\nvoid RobinsonTrautman::prepare_solution(const size_t l_max,\n                                        const double time) const {\n  ASSERT(l_max == l_max_,\n         \"The Robinson-Trautman solution only supports the l_max resolution \"\n         \"specified at construction, as it must internally store the evolved \"\n         \"scalar at the desired resolution\");\n  if (step_range_.first > time) {\n    initialize_stepper_from_start();\n  }\n  // step until the target time is within the current timestep\n  const auto rt_system = [this](const ComplexDataVector& local_rt_scalar,\n                                ComplexDataVector& local_du_rt_scalar,\n                                const double /*t*/) {\n    local_du_rt_scalar.destructive_resize(local_rt_scalar.size());\n    const SpinWeighted<ComplexDataVector, 0> rt_scalar_reference;\n    make_const_view(make_not_null(&rt_scalar_reference.data()), local_rt_scalar,\n                    0, local_rt_scalar.size());\n    SpinWeighted<ComplexDataVector, 0> du_rt_scalar_reference;\n    du_rt_scalar_reference.set_data_ref(local_du_rt_scalar.data(),\n                                        local_du_rt_scalar.size());\n    du_rt_scalar(make_not_null(&du_rt_scalar_reference), rt_scalar_reference);\n    Spectral::Swsh::filter_swsh_boundary_quantity(\n        make_not_null(&du_rt_scalar_reference), l_max_, l_max_ - 3);\n  };\n  while (step_range_.second < time) {\n    step_range_ = stepper_.do_step(rt_system);\n  }\n  stepper_.calc_state(time, get(dense_output_rt_scalar_).data());\n  du_rt_scalar(make_not_null(&get(dense_output_du_rt_scalar_)),\n               get(dense_output_rt_scalar_));\n  Spectral::Swsh::filter_swsh_boundary_quantity(\n      make_not_null(&get(dense_output_du_rt_scalar_)), l_max_, l_max_ - 3);\n  prepared_time_ = time;\n}\n\nvoid RobinsonTrautman::variables_impl(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n    const size_t l_max, const double time,\n    tmpl::type_<Tags::News> /*meta*/) const {\n  ASSERT(time == prepared_time_,\n         \"The Robinson-Trautman solution is being calculated in an \"\n         \"inconsistent state. The public interface should always call \"\n         \"`prepare_solution` before calculating any metric components, so this \"\n         \"may indicate inconsistent use of the internal Robinson-Trautman \"\n         \"calculation functions.\");\n  ASSERT(l_max == l_max_,\n         \"The Robinson-Trautman solution only supports the l_max resolution \"\n         \"specified at construction, as it must internally store the evolved \"\n         \"scalar at the desired resolution\");\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::EthbarEthbar>>(\n      l_max_, 1, make_not_null(&get(*news)), get(dense_output_rt_scalar_));\n  get(*news).data() /= get(dense_output_rt_scalar_).data();\n}\n\nvoid RobinsonTrautman::du_rt_scalar(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> local_du_rt_scalar,\n    const SpinWeighted<ComplexDataVector, 0>& rt_scalar) const {\n  using rt_tag = ::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>;\n  using ethbar_ethbar_rt_tag =\n      Spectral::Swsh::Tags::Derivative<rt_tag,\n                                       Spectral::Swsh::Tags::EthbarEthbar>;\n  using eth_eth_ethbar_ethbar_rt_tag =\n      Spectral::Swsh::Tags::Derivative<ethbar_ethbar_rt_tag,\n                                       Spectral::Swsh::Tags::EthEth>;\n  Variables<tmpl::list<ethbar_ethbar_rt_tag, eth_eth_ethbar_ethbar_rt_tag>>\n      temporary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max_)};\n  auto& ethbar_ethbar_rt_scalar =\n      get<ethbar_ethbar_rt_tag>(temporary_variables);\n  auto& eth_eth_ethbar_ethbar_rt_scalar =\n      get<eth_eth_ethbar_ethbar_rt_tag>(temporary_variables);\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::EthbarEthbar>>(\n      l_max_, 1, make_not_null(&get(ethbar_ethbar_rt_scalar)), rt_scalar);\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::EthEth>>(\n      l_max_, 1, make_not_null(&get(eth_eth_ethbar_ethbar_rt_scalar)),\n      get(ethbar_ethbar_rt_scalar));\n  *local_du_rt_scalar =\n      (-pow<4>(rt_scalar) * get(eth_eth_ethbar_ethbar_rt_scalar) +\n       pow<3>(rt_scalar) * get(ethbar_ethbar_rt_scalar) *\n           conj(get(ethbar_ethbar_rt_scalar))) /\n      12.0;\n}\n\nvoid RobinsonTrautman::bondi_u(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& rt_scalar) const {\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max_, 1, make_not_null(&get(*bondi_u)), get(rt_scalar));\n  get(*bondi_u).data() /= extraction_radius_;\n}\n\nvoid RobinsonTrautman::bondi_w(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& rt_scalar) const {\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::EthEthbar>>(\n      l_max_, 1, make_not_null(&get(*bondi_w)), get(rt_scalar));\n  get(*bondi_w) = (get(rt_scalar) + get(*bondi_w) - 1.0) / extraction_radius_ -\n                  2.0 / square(extraction_radius_ * get(rt_scalar));\n}\n\nvoid RobinsonTrautman::dr_bondi_w(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> dr_bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& rt_scalar) const {\n  SpinWeighted<ComplexDataVector, 0> bondi_w{get(*dr_bondi_w).size()};\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::EthEthbar>>(\n      l_max_, 1, make_not_null(&bondi_w), get(rt_scalar));\n  get(*dr_bondi_w) =\n      -(get(rt_scalar) + bondi_w - 1.0) / square(extraction_radius_) +\n      4.0 / (pow<3>(extraction_radius_) * square(get(rt_scalar)));\n}\n\nvoid RobinsonTrautman::du_bondi_w(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> du_bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& local_du_rt_scalar,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& rt_scalar) const {\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::EthEthbar>>(\n      l_max_, 1, make_not_null(&get(*du_bondi_w)), get(local_du_rt_scalar));\n  get(*du_bondi_w) =\n      (get(local_du_rt_scalar) + get(*du_bondi_w)) / extraction_radius_ +\n      4.0 * get(local_du_rt_scalar) /\n          (square(extraction_radius_) * pow<3>(get(rt_scalar)));\n}\n\nvoid RobinsonTrautman::spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        spherical_metric,\n    const size_t l_max, const double time) const {\n  ASSERT(time == prepared_time_,\n         \"The Robinson-Trautman solution is being calculated in an \"\n         \"inconsistent state. The public interface should always call \"\n         \"`prepare_solution` before calculating any metric components, so this \"\n         \"may indicate inconsistent use of the internal Robinson-Trautman \"\n         \"calculation functions.\");\n  ASSERT(l_max == l_max_,\n         \"The Robinson-Trautman solution only supports the l_max resolution \"\n         \"specified at construction, as it must internally store the evolved \"\n         \"scalar at the desired resolution\");\n  Variables<tmpl::list<Tags::BondiW, Tags::BondiU>> temporary_variables{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max_)};\n  auto& local_bondi_u = get<Tags::BondiU>(temporary_variables);\n  auto& local_bondi_w = get<Tags::BondiW>(temporary_variables);\n\n  bondi_u(make_not_null(&local_bondi_u), dense_output_rt_scalar_);\n  bondi_w(make_not_null(&local_bondi_w), dense_output_rt_scalar_);\n\n  get<0, 0>(*spherical_metric) =\n      -real(get(dense_output_rt_scalar_).data() *\n                (extraction_radius_ * get(local_bondi_w).data() + 1.0) -\n            square(extraction_radius_) * get(local_bondi_u).data() *\n                conj(get(local_bondi_u).data()));\n  get<0, 1>(*spherical_metric) =\n      -get<0, 0>(*spherical_metric) - real(get(dense_output_rt_scalar_).data());\n  get<1, 1>(*spherical_metric) =\n      get<0, 0>(*spherical_metric) +\n      2.0 * real(get(dense_output_rt_scalar_).data());\n\n  get<0, 2>(*spherical_metric) =\n      square(extraction_radius_) * real(get(local_bondi_u).data());\n  get<1, 2>(*spherical_metric) =\n      -square(extraction_radius_) * real(get(local_bondi_u).data());\n  // note: using 'pfaffian' components, because otherwise the sin(theta)s lose\n  // precision at the poles\n  get<0, 3>(*spherical_metric) =\n      square(extraction_radius_) * imag(get(local_bondi_u).data());\n  get<1, 3>(*spherical_metric) =\n      -square(extraction_radius_) * imag(get(local_bondi_u).data());\n\n  // note: pfaffian components\n  get<2, 2>(*spherical_metric) = square(extraction_radius_);\n  get<2, 3>(*spherical_metric) = 0.0;\n  get<3, 3>(*spherical_metric) = square(extraction_radius_);\n}\n\nvoid RobinsonTrautman::dr_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dr_spherical_metric,\n    const size_t l_max, const double time) const {\n  ASSERT(time == prepared_time_,\n         \"The Robinson-Trautman solution is being calculated in an \"\n         \"inconsistent state. The public interface should always call \"\n         \"`prepare_solution` before calculating any metric components, so this \"\n         \"may indicate inconsistent use of the internal Robinson-Trautman \"\n         \"calculation functions.\");\n  ASSERT(l_max == l_max_,\n         \"The Robinson-Trautman solution only supports the l_max resolution \"\n         \"specified at construction, as it must internally store the evolved \"\n         \"scalar at the desired resolution\");\n  Variables<tmpl::list<Tags::BondiW, Tags::Dr<Tags::BondiW>, Tags::BondiU>>\n      temporary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max_)};\n  auto& local_dr_bondi_w = get<Tags::Dr<Tags::BondiW>>(temporary_variables);\n  auto& local_bondi_w = get<Tags::BondiW>(temporary_variables);\n  auto& local_bondi_u = get<Tags::BondiU>(temporary_variables);\n\n  bondi_u(make_not_null(&local_bondi_u), dense_output_rt_scalar_);\n  dr_bondi_w(make_not_null(&local_dr_bondi_w), dense_output_rt_scalar_);\n  bondi_w(make_not_null(&local_bondi_w), dense_output_rt_scalar_);\n\n  dt_spherical_metric(dr_spherical_metric, l_max, time);\n\n  // note: bondi_u is proportional to 1/r, so when comparing to the values in\n  // `spherical_metric`, this (correctly) doesn't have a factor of two where you\n  // might naively expect one.\n  get<0, 0>(*dr_spherical_metric) =\n      -get<0, 0>(*dr_spherical_metric) -\n      real(get(dense_output_rt_scalar_).data() *\n           (extraction_radius_ * get(local_dr_bondi_w).data() +\n            get(local_bondi_w).data()));\n  get<0, 1>(*dr_spherical_metric) =\n      real(get(dense_output_rt_scalar_).data() *\n           (extraction_radius_ * get(local_dr_bondi_w).data() +\n            get(local_bondi_w).data())) -\n      get<0, 1>(*dr_spherical_metric);\n  get<1, 1>(*dr_spherical_metric) =\n      -real(get(dense_output_rt_scalar_).data() *\n            (extraction_radius_ * get(local_dr_bondi_w).data() +\n             get(local_bondi_w).data())) -\n      get<1, 1>(*dr_spherical_metric);\n\n  get<0, 2>(*dr_spherical_metric) =\n      -get<0, 2>(*dr_spherical_metric) +\n      extraction_radius_ * real(get(local_bondi_u).data());\n  get<1, 2>(*dr_spherical_metric) =\n      -get<1, 2>(*dr_spherical_metric) +\n      -extraction_radius_ * real(get(local_bondi_u).data());\n  // note: using 'pfaffian' components, because otherwise the factors of\n  // sin(theta) lose precision at the poles\n  get<0, 3>(*dr_spherical_metric) =\n      -get<0, 3>(*dr_spherical_metric) +\n      extraction_radius_ * imag(get(local_bondi_u).data());\n  get<1, 3>(*dr_spherical_metric) =\n      -get<1, 3>(*dr_spherical_metric) +\n      -extraction_radius_ * imag(get(local_bondi_u).data());\n\n  // note: pfaffian components\n  get<2, 2>(*dr_spherical_metric) =\n      -get<2, 2>(*dr_spherical_metric) + 2.0 * extraction_radius_;\n  get<2, 3>(*dr_spherical_metric) = 0.0;\n  get<3, 3>(*dr_spherical_metric) =\n      -get<3, 3>(*dr_spherical_metric) + 2.0 * extraction_radius_;\n}\n\nvoid RobinsonTrautman::dt_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dt_spherical_metric,\n    const size_t l_max, const double time) const {\n  ASSERT(time == prepared_time_,\n         \"The Robinson-Trautman solution is being calculated in an \"\n         \"inconsistent state. The public interface should always call \"\n         \"`prepare_solution` before calculating any metric components, so this \"\n         \"may indicate inconsistent use of the internal Robinson-Trautman \"\n         \"calculation functions.\");\n  ASSERT(l_max == l_max_,\n         \"The Robinson-Trautman solution only supports the l_max resolution \"\n         \"specified at construction, as it must internally store the evolved \"\n         \"scalar at the desired resolution\");\n  Variables<tmpl::list<Tags::BondiW, Tags::Du<Tags::BondiW>, Tags::BondiU,\n                       Tags::Du<Tags::BondiU>>>\n      temporary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max_)};\n  auto& local_bondi_w = get<Tags::BondiW>(temporary_variables);\n  auto& local_bondi_u = get<Tags::BondiU>(temporary_variables);\n  auto& local_du_bondi_w = get<Tags::Du<Tags::BondiW>>(temporary_variables);\n  auto& local_du_bondi_u = get<Tags::Du<Tags::BondiU>>(temporary_variables);\n  bondi_u(make_not_null(&local_du_bondi_u), dense_output_du_rt_scalar_);\n  bondi_u(make_not_null(&local_bondi_u), dense_output_rt_scalar_);\n  du_bondi_w(make_not_null(&local_du_bondi_w), dense_output_du_rt_scalar_,\n             dense_output_rt_scalar_);\n  bondi_w(make_not_null(&local_bondi_w), dense_output_rt_scalar_);\n\n  get<0, 0>(*dt_spherical_metric) = -real(\n      get(dense_output_du_rt_scalar_).data() *\n          (extraction_radius_ * get(local_bondi_w).data() + 1.0) +\n      get(dense_output_rt_scalar_).data() * extraction_radius_ *\n          get(local_du_bondi_w).data() -\n      square(extraction_radius_) *\n          (get(local_du_bondi_u).data() * conj(get(local_bondi_u).data()) +\n           conj(get(local_du_bondi_u).data()) * get(local_bondi_u).data()));\n  get<0, 1>(*dt_spherical_metric) =\n      -get<0, 0>(*dt_spherical_metric) -\n      real(get(dense_output_du_rt_scalar_).data());\n  get<1, 1>(*dt_spherical_metric) =\n      get<0, 0>(*dt_spherical_metric) +\n      2.0 * real(get(dense_output_du_rt_scalar_).data());\n\n  get<0, 2>(*dt_spherical_metric) =\n      square(extraction_radius_) * real(get(local_du_bondi_u).data());\n  get<1, 2>(*dt_spherical_metric) =\n      -square(extraction_radius_) * real(get(local_du_bondi_u).data());\n  get<0, 3>(*dt_spherical_metric) =\n      square(extraction_radius_) * imag(get(local_du_bondi_u).data());\n  get<1, 3>(*dt_spherical_metric) =\n      -square(extraction_radius_) * imag(get(local_du_bondi_u).data());\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = A; B < 2; ++B) {\n      dt_spherical_metric->get(A + 2, B + 2) = 0.0;\n    }\n  }\n}\n\nvoid RobinsonTrautman::pup(PUP::er& p) {\n  SphericalMetricData::pup(p);\n  p | tolerance_;\n  p | start_time_;\n  p | dense_output_rt_scalar_;\n  p | dense_output_du_rt_scalar_;\n  p | l_max_;\n  p | initial_modes_;\n  // it is difficult to serialize the stepper, but because it will usually not\n  // migrate, it should be fine to just re-initialize it with the candidate\n  // starting step size at the starting time. It will need to 'catch up' to the\n  // current evolution each time it is migrated.\n  // This procedure should be reconsidered if this analytic solution is used in\n  // a context (e.g. in a chare array) where it may need to migrate frequently.\n  if (p.isUnpacking()) {\n    initialize_stepper_from_start();\n  }\n}\n\nPUP::able::PUP_ID RobinsonTrautman::my_PUP_ID = 0;\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"NumericalAlgorithms/OdeIntegration/OdeIntegration.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace Solutions {\n/*!\n * \\brief An analytic solution representing a specialization of the radiative\n * Robinson-Trautman solution described in \\cite Derry1970fk.\n *\n * \\details This solution is not quite analytic, in the sense that there is a\n * single scalar field that must be evolved. Ultimately, it is a partial\n * specialization of the Characteristic equations such that \\f$J = 0\\f$ and the\n * evolution equations have been manipulated to give a time evolution equation\n * for \\f$e^{-2 \\beta}\\f$, which is equivalent to the Robinson-Trautman scalar\n * \\f$\\omega_{\\text{RT}}\\f$ (denoted \\f$W\\f$ in \\cite Derry1970fk -- we deviate\n * from their notation because the symbol \\f$W\\f$ is already used elsewhere in\n * the CCE system).\n *\n * \\note The value of \\f$\\omega_{\\text{RT}}\\f$ should be real and near 1, which\n * is imposed by the solution itself, any modes specified in the input file are\n * treated as perturbations to the leading value of 1.0 for the\n * Robinson-Trautman scalar \\f$\\omega_{\\text{RT}}\\f$.\n *\n * \\note The use of substep time-steppers in conjunction with `RobinsonTrautman`\n * analytic data is explictly forbidden by checks in\n * `Cce::Actions::InitializeWorldtubeBoundary`. The reason is that any time the\n * `RobinsonTrautman` receives a request for data at a time in the past of its\n * current state, it must re-start its internal time stepper from the beginning\n * of the evolution. This is a reasonable cost for multistep methods that only\n * have non-ordered time steps during self-start, but will lead to\n * \\f$\\mathcal O(N^2)\\f$ internal steps in the `RobinsonTrautman` solution if a\n * substep method is used, where \\f$N\\f$ is the number of steps performed by the\n * substep method. If you find an unavoidable need to use `RobinsonTrautman`\n * with a substep method, you will either need to only do so for very short\n * evolutions, or need to write more sophisticated caching of the internal time\n * step data.\n */\nstruct RobinsonTrautman : public SphericalMetricData {\n  struct InitialModes {\n    using type = std::vector<std::complex<double>>;\n    static constexpr Options::String help{\n        \"The initial modes of the Robinson-Trautman scalar, denoted W in \"\n        \"[Derry 1970] and omega_RT in the rendered documentation. \"\n        \"These are taken in ascending l and m order, m varies fastest. Note \"\n        \"that the modes are treated as perturbations to the leading-order \"\n        \"solution of 1.0 for omega_RT and only the real part of the field is \"\n        \"used.\"};\n  };\n  struct ExtractionRadius {\n    using type = double;\n    static constexpr Options::String help{\n        \"The extraction radius of the spherical solution\"};\n    static type lower_bound() { return 0.0; }\n    static type suggested_value() { return 20.0; }\n  };\n  struct LMax {\n    using type = size_t;\n    static constexpr Options::String help{\n        \"The maximum l value for the internal computation of the analytic \"\n        \"solution\"};\n    static type lower_bound() { return 4; }\n  };\n  struct Tolerance {\n    using type = double;\n    static constexpr Options::String help{\n        \"The tolerance for the time evolution part of the calculation of the \"\n        \"semi-analytic Robinson-Trautman solution\"};\n    static type lower_bound() { return 0.0; }\n    static type suggested_value() { return 1.0e-11; }\n  };\n  struct StartTime {\n    using type = double;\n    static constexpr Options::String help{\n        \"The starting time for the Robinson-Trautman evolution\"};\n    static type lower_bound() { return 0.0; }\n    static type suggested_value() { return 0.0; }\n  };\n\n  using options =\n      tmpl::list<InitialModes, ExtractionRadius, LMax, Tolerance, StartTime>;\n\n  static constexpr Options::String help = {\n      \"Analytic solution representing worldtube data for the nonlinear \"\n      \"semi-analytic Robinson-Trautman metric, which requires a single \"\n      \"scalar on the boundary to be evolved to determine the metric\"};\n\n  WRAPPED_PUPable_decl_template(RobinsonTrautman);  // NOLINT\n\n  explicit RobinsonTrautman(CkMigrateMessage* msg) : SphericalMetricData(msg) {}\n\n  RobinsonTrautman() = default;\n\n  RobinsonTrautman(std::vector<std::complex<double>> initial_modes,\n                   double extraction_radius, size_t l_max, double tolerance,\n                   double start_time, const Options::Context& context);\n\n  std::unique_ptr<WorldtubeData> get_clone() const override;\n\n  void pup(PUP::er& p) override;\n\n  bool use_noninertial_news() const override { return true; }\n\n private:\n  void du_rt_scalar(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> local_du_rt_scalar,\n      const SpinWeighted<ComplexDataVector, 0>& rt_scalar) const;\n\n  void du_bondi_w(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> du_bondi_w,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& local_du_rt_scalar,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& rt_scalar) const;\n\n  void bondi_u(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> bondi_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& rt_scalar) const;\n\n  void bondi_w(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> bondi_w,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& rt_scalar) const;\n\n  void dr_bondi_w(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> dr_bondi_w,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& rt_scalar) const;\n\n  void initialize_stepper_from_start() const;\n\n protected:\n  /*!\n   * \\brief The Robinson-Trautman solution performs the time-stepping to advance\n   * the internal member scalar used to generate the metric solution to the\n   * correct state for `time`.\n   *\n   * \\details The generating scalar \\f$\\omega_{\\text{RT}}\\f$ is evolved\n   * using equation (2.5) from \\cite Derry1970fk (manipulated to a form\n   * convenient for our numerical utilities)\n   *\n   * \\f[\n   * \\partial_u \\omega_{\\text{RT}} = -\n   * \\left(\\omega^4_{\\text{RT}} \\eth^2 \\bar \\eth^2 \\omega_{\\text{RT}}\n   *  - \\omega_{\\text{RT}}^3 (\\eth^2 \\omega_{\\text{RT}})\n   * (\\bar \\eth^2  \\omega_{\\text{RT}}) \\right)\n   * \\f]\n   *\n   * As the scalar \\f$\\omega_{\\text{RT}}\\f$ is evolved, it is filtered by\n   * zeroing the highest two angular modes.\n   */\n  void prepare_solution(size_t output_l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the spherical coordinate metric of the Robinson-Trautman\n   * solution generated by the time-evolved scalar \\f$\\omega_{\\text{RT}}\\f$.\n   *\n   * \\details The spacetime metric of the Robinson-Trautman solution can be\n   * expressed as a specialization of the Bondi-Sachs metric (note the metric\n   * signature change as compared to equation (1.2) from \\cite Derry1970fk)\n   *\n   * \\f[\n   * ds^2 = -((r W + 1) \\omega_{\\text{RT}} - r^2 U \\bar U) (dt - dr)^2\n   * - 2 \\omega_{\\text{RT}} (dt - dr) dr\n   * - 2 r^2 U^A q_{AB} dx^B (dt - dr) + r^2 q_{A B} dx^A dx^B,\n   * \\f]\n   *\n   * where \\f$q_{A B}\\f$ represents the angular unit sphere metric, and the\n   * remaining Bondi-Sachs scalars and angular tensors are defined in terms of\n   * the Robinson-Trautman scalar \\f$\\omega_{\\text{RT}}\\f$\n   *\n   * \\f{align*}{\n   * W &= \\frac{1}{r}\\left(\\omega_{\\text{RT}} + \\eth \\bar \\eth\n   * \\omega_{\\text{RT}} - 1\\right) - \\frac{2}{r^2 \\omega_{\\text{RT}}^2}\\\\\n   * U &\\equiv U^A q_A = \\frac{\\eth \\omega_{\\text{RT}}}{r}.\n   * \\f}\n   * and \\f$q_A\\f$ is the angular dyad on the unit sphere.\n   *\n   * The angular part of the metric can be expressed in terms of the \\f$U\\f$\n   * scalar as\n   *\n   * \\f{align*}{\n   * g_{u \\theta} &= r^2 \\Re U\\\\\n   * g_{u \\phi} &= r^2 \\Im U\n   * \\f}\n   */\n  void spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute radial derivative of the spherical coordinate metric of the\n   * Robinson-Trautman solution generated by the time-evolved scalar\n   * \\f$\\omega_{\\text{RT}}\\f$.\n   *\n   * \\details The radial derivative (at constant time t) of the\n   * Robinson-Trautman solution is obtained by differentiating the expressions\n   * from the documentation for `RobinsonTrautman::spherical_metric()`:\n   *\n   * \\f{align*}{\n   * (\\partial_r g_{a b} + \\partial_t g_{a b}) dx^a dx^b =\n   * - (\\omega_{\\text{RT}} (r \\partial_r W + W) - 2 r U \\bar U\n   * - r^2 (\\bar U\\partial_r U + U \\partial_r \\bar U)) (dt - dr)^2\n   * - 2 r U^A q_{A B} dx^B (dt - dr) +  2 r q_{A B} dx^A dx^B\n   * \\f}\n   *\n   * where \\f$q_{A B}\\f$ represents the angular unit sphere metric, and the\n   * remaining Bondi-Sachs scalars and angular tensors are defined in terms of\n   * the Robinson-Trautman scalar \\f$\\omega_{\\text{RT}}\\f$\n   *\n   * \\f{align*}{\n   * W &= \\frac{1}{r}\\left(\\omega_{\\text{RT}}\n   * + \\eth \\bar \\eth \\omega_{\\text{RT}} - 1\\right)\n   * - \\frac{2}{r^2 \\omega_{\\text{RT}}^2}\\\\\n   * \\partial_r W &= -\\frac{1}{r^2} \\left(\\omega_{\\text{RT}}\n   * + \\eth \\bar \\eth \\omega_{\\text{RT}} - 1\\right)\n   * + \\frac{4}{r^3 \\omega_{\\text{RT}}^2}\\\\\n   * U &\\equiv U^A q_A = \\frac{\\eth \\omega_{\\text{RT}}}{r}.\n   * \\f}\n   * and \\f$q_A\\f$ is the angular dyad on the unit sphere. The Robinson-Trautman\n   * scalar \\f$\\omega_{\\text{RT}}\\f$ is independent of the Bondi radius \\f$r\\f$,\n   * so all radial derivatives of \\f$\\omega_{\\text{RT}}\\f$ have been dropped\n   */\n  void dr_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dr_spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute time derivative of the spherical coordinate metric of the\n   * Robinson-Trautman solution generated by the time-evolved scalar\n   * \\f$\\omega_{\\text{RT}}\\f$.\n   *\n   * \\details The time derivative of the Robinson-Trautman solution is obtained\n   * by differentiating the expressions from the documentation for\n   * `RobinsonTrautman::spherical_metric()`:\n   *\n   * \\f{align*}{\n   * \\partial_t g_{a b} dx^a dx^b =  -( \\partial_u \\omega_{\\text{RT}} (r W + 1)\n   *  + \\omega_{\\text{RT}} \\partial_u W\n   * - r^2 (\\bar U \\partial_u U + U \\partial_u \\bar U)) (dt - dr)^2\n   * - 2 \\partial_u \\omega_{\\text{RT}} (dt - dr) dr\n   * - 2 r^2 \\partial_u U^A q_{AB} dx^B (dt - dr),\n   * \\f}\n   *\n   * where \\f$q_{A B}\\f$ represents the angular unit sphere metric, and the\n   * remaining Bondi-Sachs scalars and angular tensors are defined in terms of\n   * the Robinson-Trautman scalar \\f$\\omega_{\\text{RT}}\\f$\n   *\n   * \\f{align*}{\n   * W &= \\frac{1}{r}\\left(\\omega_{\\text{RT}}\n   * + \\eth \\bar \\eth \\omega_{\\text{RT}} - 1\\right)\n   * - \\frac{2}{r^2 \\omega_{\\text{RT}}^2}\\\\\n   * \\partial_u W &= \\frac{1}{r}\\left(\\partial_u \\omega_{\\text{RT}}\n   * + \\eth \\bar \\eth \\partial_u \\omega_{\\text{RT}}\\right)\n   * + \\frac{4 \\partial_u \\omega_{\\text{RT}}}{r^2 \\omega_{\\text{RT}}^3} \\\\\n   * \\partial_u U &= q_A \\partial_u U^A = \\frac{\\eth \\partial_u\n   * \\omega_{\\text{RT}}}{r}, \\f}\n   *\n   * and \\f$q_A\\f$ is the angular dyad on the unit sphere; and the time\n   * derivative of the Robinson-Trautman scalar \\f$\\omega_{\\text{RT}}\\f$ is\n   *\n   * \\f[\n   * \\partial_u \\omega_{\\text{RT}} =\n   * \\frac{1}{12} \\left(-\\omega^4_{\\text{RT}} \\eth^2 \\bar \\eth^2\n   * \\omega_{\\text{RT}} + \\omega_{\\text{RT}}^3 (\\eth^2 \\omega_{\\text{RT}})\n   * (\\bar \\eth^2  \\omega_{\\text{RT}}) \\right)\n   * \\f]\n   */\n  void dt_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dt_spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the news associated with the Robinson-Trautman solution\n   * generated by the time-evolved scalar \\f$\\omega_{\\text{RT}}\\f$.\n   *\n   * \\details The Bondi-Sachs news in the Robinson-Trautman solution is\n   *\n   * \\f{align*}{\n   * N = \\frac{\\bar \\eth \\bar \\eth \\omega_{\\text{RT}}}{\\omega_{\\text{RT}}}\n   * \\f}\n   */\n  void variables_impl(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> News,\n      size_t l_max, double time,\n      tmpl::type_<Tags::News> /*meta*/) const override;\n\n  using WorldtubeData::variables_impl;\n\n  using SphericalMetricData::variables_impl;\n private:\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::pair<double, double> step_range_;\n\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable boost::numeric::odeint::dense_output_runge_kutta<\n      boost::numeric::odeint::controlled_runge_kutta<\n          boost::numeric::odeint::runge_kutta_dopri5<ComplexDataVector>>>\n      stepper_;\n\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Scalar<SpinWeighted<ComplexDataVector, 0>> dense_output_rt_scalar_;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Scalar<SpinWeighted<ComplexDataVector, 0>> dense_output_du_rt_scalar_;\n  size_t l_max_ = 0;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable double prepared_time_ = std::numeric_limits<double>::signaling_NaN();\n  double tolerance_ = std::numeric_limits<double>::signaling_NaN();\n  double start_time_ = std::numeric_limits<double>::signaling_NaN();\n  std::vector<std::complex<double>> initial_modes_;\n};\n}  // namespace Solutions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n\n#include <complex>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\n\nRotatingSchwarzschild::RotatingSchwarzschild(const double extraction_radius,\n                                             const double mass,\n                                             const double frequency)\n    : SphericalMetricData{extraction_radius},\n      frequency_{frequency},\n      mass_{mass} {}\n\nstd::unique_ptr<WorldtubeData> RotatingSchwarzschild::get_clone() const {\n  return std::make_unique<RotatingSchwarzschild>(*this);\n}\n\nvoid RotatingSchwarzschild::spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        spherical_metric,\n    const size_t l_max, double /*time*/) const {\n  get<0, 1>(*spherical_metric) = 0.0;\n  get<0, 2>(*spherical_metric) = 0.0;\n\n  get<1, 1>(*spherical_metric) = 1.0 / (1.0 - 2.0 * mass_ / extraction_radius_);\n  get<1, 2>(*spherical_metric) = 0.0;\n  get<1, 3>(*spherical_metric) = 0.0;\n\n  get<2, 2>(*spherical_metric) = square(extraction_radius_);\n  get<2, 3>(*spherical_metric) = 0.0;\n\n  // note: omit the sin factors for the phi components due to pfaffian\n  // Jacobian factors\n  get<3, 3>(*spherical_metric) = square(extraction_radius_);\n\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get<0, 0>(*spherical_metric)[collocation_point.offset] =\n        -(1.0 - 2.0 * mass_ / extraction_radius_ -\n          square(frequency_) * square(extraction_radius_) *\n              square(sin(collocation_point.theta)));\n    get<0, 3>(*spherical_metric)[collocation_point.offset] =\n        square(extraction_radius_) * frequency_ * sin(collocation_point.theta);\n  }\n}\n\nvoid RotatingSchwarzschild::dr_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dr_spherical_metric,\n    const size_t l_max, double /*time*/) const {\n  get<0, 1>(*dr_spherical_metric) = 0.0;\n  get<0, 2>(*dr_spherical_metric) = 0.0;\n\n  get<1, 1>(*dr_spherical_metric) =\n      -2.0 * mass_ / (square(extraction_radius_ - 2.0 * mass_));\n  get<1, 2>(*dr_spherical_metric) = 0.0;\n  get<1, 3>(*dr_spherical_metric) = 0.0;\n\n  get<2, 2>(*dr_spherical_metric) = 2.0 * extraction_radius_;\n  get<2, 3>(*dr_spherical_metric) = 0.0;\n\n  // note: omit the sin factors for the phi components due to pfaffian\n  // Jacobian factors\n  get<3, 3>(*dr_spherical_metric) = 2.0 * extraction_radius_;\n\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get<0, 0>(*dr_spherical_metric)[collocation_point.offset] =\n        -(2.0 * mass_ / square(extraction_radius_) -\n          2.0 * square(frequency_) * extraction_radius_ *\n              square(sin(collocation_point.theta)));\n    get<0, 3>(*dr_spherical_metric)[collocation_point.offset] =\n        2.0 * extraction_radius_ * frequency_ * sin(collocation_point.theta);\n  }\n}\n\nvoid RotatingSchwarzschild::dt_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dt_spherical_metric,\n    size_t /*l_max*/, double /*time*/) const {\n  for(auto& component : *dt_spherical_metric) {\n    component = 0.0;\n  }\n}\n\nvoid RotatingSchwarzschild::variables_impl(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n    size_t /*output_l_max*/, double /*time*/,\n    tmpl::type_<Tags::News> /*meta*/) const {\n  get(*news).data() = 0.0;\n}\n\nvoid RotatingSchwarzschild::pup(PUP::er& p) {\n  SphericalMetricData::pup(p);\n  p | mass_;\n  p | frequency_;\n}\n\nPUP::able::PUP_ID RotatingSchwarzschild::my_PUP_ID = 0;\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce::Solutions {\n\n/*!\n * \\brief Computes the analytic data for the rotating Schwarzschild solution\n * described in \\cite Barkett2019uae, section VI.C.\n *\n * \\details This is a comparatively simple test which simply determines the\n * Schwarzschild metric in transformed coordinates given by \\f$\\phi\\rightarrow\n * \\phi + \\omega u\\f$, where \\f$u\\f$ is the retarded time.\n */\nstruct RotatingSchwarzschild : public SphericalMetricData {\n  struct ExtractionRadius {\n    using type = double;\n    static constexpr Options::String help{\n        \"The extraction radius of the spherical solution\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help{\n        \"The mass of the Schwarzschild black hole\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Frequency {\n    using type = double;\n    static constexpr Options::String help{\n        \"The frequency of the coordinate rotation.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options = tmpl::list<ExtractionRadius, Mass, Frequency>;\n\n  static constexpr Options::String help = {\n      \"Analytic solution representing a Schwarzschild black hole in a rotating \"\n      \"frame\"};\n\n  WRAPPED_PUPable_decl_template(RotatingSchwarzschild);  // NOLINT\n\n  explicit RotatingSchwarzschild(CkMigrateMessage* msg)\n      : SphericalMetricData(msg) {}\n\n  // clang doesn't manage to use = default correctly in this case\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  RotatingSchwarzschild() {}\n\n  RotatingSchwarzschild(double extraction_radius, double mass,\n                        double frequency);\n\n  ~RotatingSchwarzschild() override = default;\n\n  std::unique_ptr<WorldtubeData> get_clone() const override;\n\n  void pup(PUP::er& p) override;\n\n protected:\n  /// A no-op as the rotating Schwarzschild solution does not have substantial\n  /// shared computation to prepare before the separate component calculations.\n  void prepare_solution(const size_t /*output_l_max*/,\n                        const double /*time*/) const override {}\n\n  /*!\n   * \\brief Compute the spherical coordinate metric from the closed-form\n   * rotating Schwarzschild metric.\n   *\n   * \\details The rotating Schwarzschild takes the coordinate form\n   * \\cite Barkett2019uae,\n   *\n   * \\f{align}{\n   * ds^2 = -\\left(1 - \\frac{2 M}{r} - \\omega^2 r^2 \\sin^2 \\theta\\right) dt^2\n   * + \\frac{1}{1 - \\frac{2 M}{r}} dr^2\n   * + 2 \\omega r^2 \\sin^2 \\theta dt d\\phi + r^2 d\\Omega^2\n   * \\f}\n   */\n  void spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the radial derivative of the spherical coordinate metric\n   * from the closed-form rotating Schwarzschild metric.\n   *\n   * \\details The rotating Schwarzschild takes the coordinate form\n   * \\cite Barkett2019uae,\n   *\n   * \\f{align}{\n   * \\partial_r g_{a b} dx^a dx^b =& -\\left(\\frac{2 M}{r^2} - 2 \\omega^2 r\n   * \\sin^2 \\theta\\right) dt^2 - \\frac{2 M}{(r - 2 M)^2} dr^2\n   * + 4 \\omega r \\sin^2 \\theta dt d\\phi + 2 r d\\Omega^2\n   * \\f}\n   */\n  void dr_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dr_spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief The time derivative of the spherical coordinate metric in the\n   * rotating Schwarzschild metric vanishes.\n   */\n  void dt_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dt_spherical_metric,\n      size_t l_max, double time) const override;\n\n  using WorldtubeData::variables_impl;\n\n  using SphericalMetricData::variables_impl;\n\n  /// The News vanishes in the rotating Schwarzschild metric\n  void variables_impl(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n      size_t l_max, double time,\n      tmpl::type_<Tags::News> /*meta*/) const override;\n\n  double frequency_ = std::numeric_limits<double>::signaling_NaN();\n  double mass_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryDataTags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\n\nvoid SphericalMetricData::variables_impl(\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n    const size_t l_max, const double time,\n    tmpl::type_<gr::Tags::SpacetimeMetric<DataVector, 3>> /*meta*/) const {\n  Variables<\n      tmpl::list<Tags::detail::InverseCartesianToSphericalJacobian,\n                 gr::Tags::SpacetimeMetric<\n                     DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>>\n      intermediate_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  auto& intermediate_spherical_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3,\n                                    ::Frame::Spherical<::Frame::Inertial>>>(\n          intermediate_variables);\n  auto& intermediate_jacobian =\n      get<Tags::detail::InverseCartesianToSphericalJacobian>(\n          intermediate_variables);\n\n  spherical_metric(make_not_null(&intermediate_spherical_metric), l_max, time);\n  inverse_jacobian(make_not_null(&intermediate_jacobian), l_max);\n\n  get<0, 0>(*spacetime_metric) = get<0, 0>(intermediate_spherical_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    spacetime_metric->get(0, i + 1) = intermediate_jacobian.get(i, 0) *\n                                      get<0, 1>(intermediate_spherical_metric);\n    for (size_t k = 1; k < 3; ++k) {\n      spacetime_metric->get(0, i + 1) +=\n          intermediate_jacobian.get(i, k) *\n          intermediate_spherical_metric.get(0, k + 1);\n    }\n    for (size_t j = i; j < 3; ++j) {\n      spacetime_metric->get(i + 1, j + 1) =\n          intermediate_jacobian.get(i, 0) * intermediate_jacobian.get(j, 0) *\n          get<1, 1>(intermediate_spherical_metric);\n      for (size_t k = 1; k < 3; ++k) {\n        spacetime_metric->get(i + 1, j + 1) +=\n            (intermediate_jacobian.get(i, k) * intermediate_jacobian.get(j, 0) +\n             intermediate_jacobian.get(i, 0) *\n                 intermediate_jacobian.get(j, k)) *\n            intermediate_spherical_metric.get(k + 1, 1);\n        for (size_t l = 1; l < 3; ++l) {\n          spacetime_metric->get(i + 1, j + 1) +=\n              intermediate_jacobian.get(i, k) *\n              intermediate_jacobian.get(j, l) *\n              intermediate_spherical_metric.get(k + 1, l + 1);\n        }\n      }\n    }\n  }\n}\n\nvoid SphericalMetricData::variables_impl(\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> dt_spacetime_metric,\n    const size_t l_max, const double time,\n    tmpl::type_<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>> /*meta*/)\n    const {\n  Variables<\n      tmpl::list<Tags::detail::InverseCartesianToSphericalJacobian,\n                 ::Tags::dt<gr::Tags::SpacetimeMetric<\n                     DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>>>\n      intermediate_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  auto& intermediate_dt_spherical_metric =\n      get<::Tags::dt<gr::Tags::SpacetimeMetric<\n          DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>>(\n          intermediate_variables);\n  auto& intermediate_jacobian =\n      get<Tags::detail::InverseCartesianToSphericalJacobian>(\n          intermediate_variables);\n\n  dt_spherical_metric(make_not_null(&intermediate_dt_spherical_metric), l_max,\n                      time);\n  inverse_jacobian(make_not_null(&intermediate_jacobian), l_max);\n\n  get<0, 0>(*dt_spacetime_metric) = get<0, 0>(intermediate_dt_spherical_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    dt_spacetime_metric->get(0, i + 1) =\n        intermediate_jacobian.get(i, 0) *\n        get<0, 1>(intermediate_dt_spherical_metric);\n    for (size_t k = 1; k < 3; ++k) {\n      dt_spacetime_metric->get(0, i + 1) +=\n          intermediate_jacobian.get(i, k) *\n          intermediate_dt_spherical_metric.get(0, k + 1);\n    }\n    for (size_t j = i; j < 3; ++j) {\n      dt_spacetime_metric->get(i + 1, j + 1) =\n          intermediate_jacobian.get(i, 0) * intermediate_jacobian.get(j, 0) *\n          get<1, 1>(intermediate_dt_spherical_metric);\n      for (size_t k = 1; k < 3; ++k) {\n        dt_spacetime_metric->get(i + 1, j + 1) +=\n            (intermediate_jacobian.get(i, k) * intermediate_jacobian.get(j, 0) +\n             intermediate_jacobian.get(i, 0) *\n                 intermediate_jacobian.get(j, k)) *\n            intermediate_dt_spherical_metric.get(k + 1, 1);\n        for (size_t l = 1; l < 3; ++l) {\n          dt_spacetime_metric->get(i + 1, j + 1) +=\n              intermediate_jacobian.get(i, k) *\n              intermediate_jacobian.get(j, l) *\n              intermediate_dt_spherical_metric.get(k + 1, l + 1);\n        }\n      }\n    }\n  }\n}\n\nvoid SphericalMetricData::variables_impl(\n    const gsl::not_null<tnsr::iaa<DataVector, 3>*> d_spacetime_metric,\n    const size_t l_max, const double time,\n    tmpl::type_<gh::Tags::Phi<DataVector, 3>> /*meta*/) const {\n  Variables<\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gr::Tags::SpacetimeMetric<\n                     DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>,\n                 Tags::Dr<gr::Tags::SpacetimeMetric<\n                     DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>,\n                 Tags::Dr<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                 Tags::detail::InverseCartesianToSphericalJacobian,\n                 Tags::Dr<Tags::detail::InverseCartesianToSphericalJacobian>>>\n      intermediate_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n  auto& intermediate_cartesian_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(intermediate_variables);\n  variables_impl(make_not_null(&intermediate_cartesian_metric), l_max, time,\n                 tmpl::type_<gr::Tags::SpacetimeMetric<DataVector, 3>>{});\n  auto& intermediate_spherical_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3,\n                                    ::Frame::Spherical<::Frame::Inertial>>>(\n          intermediate_variables);\n  auto& intermediate_dr_spherical_metric =\n      get<Tags::Dr<gr::Tags::SpacetimeMetric<\n          DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>>(\n          intermediate_variables);\n  auto& intermediate_dr_cartesian_metric =\n      get<Tags::Dr<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n          intermediate_variables);\n  auto& intermediate_jacobian =\n      get<Tags::detail::InverseCartesianToSphericalJacobian>(\n          intermediate_variables);\n  auto& intermediate_dr_jacobian =\n      get<Tags::Dr<Tags::detail::InverseCartesianToSphericalJacobian>>(\n          intermediate_variables);\n  spherical_metric(make_not_null(&intermediate_spherical_metric), l_max, time);\n  dr_spherical_metric(make_not_null(&intermediate_dr_spherical_metric), l_max,\n                      time);\n  inverse_jacobian(make_not_null(&intermediate_jacobian), l_max);\n  dr_inverse_jacobian(make_not_null(&intermediate_dr_jacobian), l_max);\n\n  get<0, 0>(intermediate_dr_cartesian_metric) =\n      get<0, 0>(intermediate_dr_spherical_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    intermediate_dr_cartesian_metric.get(0, i + 1) =\n        intermediate_jacobian.get(i, 0) *\n            get<0, 1>(intermediate_dr_spherical_metric) +\n        intermediate_dr_jacobian.get(i, 0) *\n            get<0, 1>(intermediate_spherical_metric);\n    for (size_t k = 1; k < 3; ++k) {\n      intermediate_dr_cartesian_metric.get(0, i + 1) +=\n          intermediate_jacobian.get(i, k) *\n              intermediate_dr_spherical_metric.get(0, k + 1) +\n          intermediate_dr_jacobian.get(i, k) *\n              intermediate_spherical_metric.get(0, k + 1);\n    }\n    for (size_t j = i; j < 3; ++j) {\n      intermediate_dr_cartesian_metric.get(i + 1, j + 1) =\n          intermediate_jacobian.get(i, 0) * intermediate_jacobian.get(j, 0) *\n              get<1, 1>(intermediate_dr_spherical_metric) +\n          (intermediate_dr_jacobian.get(i, 0) *\n               intermediate_jacobian.get(j, 0) +\n           intermediate_jacobian.get(i, 0) *\n               intermediate_dr_jacobian.get(j, 0)) *\n              get<1, 1>(intermediate_spherical_metric);\n      for (size_t k = 1; k < 3; ++k) {\n        intermediate_dr_cartesian_metric.get(i + 1, j + 1) +=\n            (intermediate_jacobian.get(i, k) * intermediate_jacobian.get(j, 0) +\n             intermediate_jacobian.get(i, 0) *\n                 intermediate_jacobian.get(j, k)) *\n                intermediate_dr_spherical_metric.get(k + 1, 1) +\n            (intermediate_dr_jacobian.get(i, k) *\n                 intermediate_jacobian.get(j, 0) +\n             intermediate_jacobian.get(i, k) *\n                 intermediate_dr_jacobian.get(j, 0) +\n             intermediate_dr_jacobian.get(i, 0) *\n                 intermediate_jacobian.get(j, k) +\n             intermediate_jacobian.get(i, 0) *\n                 intermediate_dr_jacobian.get(j, k)) *\n                intermediate_spherical_metric.get(1, k + 1);\n\n        for (size_t l = 1; l < 3; ++l) {\n          intermediate_dr_cartesian_metric.get(i + 1, j + 1) +=\n              intermediate_jacobian.get(i, k) *\n                  intermediate_jacobian.get(j, l) *\n                  intermediate_dr_spherical_metric.get(k + 1, l + 1) +\n              (intermediate_dr_jacobian.get(i, k) *\n                   intermediate_jacobian.get(j, l) +\n               intermediate_jacobian.get(i, k) *\n                   intermediate_dr_jacobian.get(j, l)) *\n                  intermediate_spherical_metric.get(k + 1, l + 1);\n        }\n      }\n    }\n  }\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>>>\n      buffers{Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n  auto& derivative_buffer =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(buffers));\n  auto& pre_derivative_buffer =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(buffers));\n\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      pre_derivative_buffer.data() = std::complex<double>(1.0, 0.0) *\n                                     intermediate_cartesian_metric.get(a, b);\n      Spectral::Swsh::angular_derivatives<\n          tmpl::list<Spectral::Swsh::Tags::Eth>>(\n          l_max, 1, make_not_null(&derivative_buffer), pre_derivative_buffer);\n      for (size_t i = 0; i < 3; ++i) {\n        d_spacetime_metric->get(i, a, b) =\n            intermediate_jacobian.get(i, 0) *\n                intermediate_dr_cartesian_metric.get(a, b) -\n            intermediate_jacobian.get(i, 1) * real(derivative_buffer.data()) -\n            intermediate_jacobian.get(i, 2) * imag(derivative_buffer.data());\n      }\n    }\n  }\n}\n\nvoid SphericalMetricData::inverse_jacobian(\n    const gsl::not_null<CartesianiSphericalJ*> inverse_jacobian,\n    const size_t l_max) const {\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    // dr/dx   dtheta/dx   dphi/dx * sin(theta)\n    get<0, 0>(*inverse_jacobian)[collocation_point.offset] =\n        cos(collocation_point.phi) * sin(collocation_point.theta);\n    get<0, 1>(*inverse_jacobian)[collocation_point.offset] =\n        cos(collocation_point.phi) * cos(collocation_point.theta) /\n        extraction_radius_;\n    get<0, 2>(*inverse_jacobian)[collocation_point.offset] =\n        -sin(collocation_point.phi) / extraction_radius_;\n    // dr/dy   dtheta/dy   dphi/dy * sin(theta)\n    get<1, 0>(*inverse_jacobian)[collocation_point.offset] =\n        sin(collocation_point.phi) * sin(collocation_point.theta);\n    get<1, 1>(*inverse_jacobian)[collocation_point.offset] =\n        cos(collocation_point.theta) * sin(collocation_point.phi) /\n        extraction_radius_;\n    get<1, 2>(*inverse_jacobian)[collocation_point.offset] =\n        cos(collocation_point.phi) / (extraction_radius_);\n    // dr/dz   dtheta/dz   dphi/dz * sin(theta)\n    get<2, 0>(*inverse_jacobian)[collocation_point.offset] =\n        cos(collocation_point.theta);\n    get<2, 1>(*inverse_jacobian)[collocation_point.offset] =\n        -sin(collocation_point.theta) / extraction_radius_;\n    get<2, 2>(*inverse_jacobian)[collocation_point.offset] = 0.0;\n  }\n}\n\nvoid SphericalMetricData::jacobian(\n    const gsl::not_null<SphericaliCartesianJ*> jacobian,\n    const size_t l_max) const {\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    // dx/dr   dy/dr  dz/dr\n    get<0, 0>(*jacobian)[collocation_point.offset] =\n        sin(collocation_point.theta) * cos(collocation_point.phi);\n    get<0, 1>(*jacobian)[collocation_point.offset] =\n        sin(collocation_point.theta) * sin(collocation_point.phi);\n    get<0, 2>(*jacobian)[collocation_point.offset] =\n        cos(collocation_point.theta);\n    // dx/dtheta   dy/dtheta  dz/dtheta\n    get<1, 0>(*jacobian)[collocation_point.offset] =\n        extraction_radius_ * cos(collocation_point.theta) *\n        cos(collocation_point.phi);\n    get<1, 1>(*jacobian)[collocation_point.offset] =\n        extraction_radius_ * cos(collocation_point.theta) *\n        sin(collocation_point.phi);\n    get<1, 2>(*jacobian)[collocation_point.offset] =\n        -extraction_radius_ * sin(collocation_point.theta);\n    // (1/sin(theta)) { dx/dphi,   dy/dphi,  dz/dphi }\n    get<2, 0>(*jacobian)[collocation_point.offset] =\n        -extraction_radius_ * sin(collocation_point.phi);\n    get<2, 1>(*jacobian)[collocation_point.offset] =\n        extraction_radius_ * cos(collocation_point.phi);\n    get<2, 2>(*jacobian)[collocation_point.offset] = 0.0;\n  }\n}\n\nvoid SphericalMetricData::dr_inverse_jacobian(\n    const gsl::not_null<CartesianiSphericalJ*> dr_inverse_jacobian,\n    const size_t l_max) const {\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    // radial derivatives of:\n    // dr/dx   dtheta/dx   dphi/dx * sin(theta)\n    get<0, 0>(*dr_inverse_jacobian)[collocation_point.offset] = 0.0;\n    get<0, 1>(*dr_inverse_jacobian)[collocation_point.offset] =\n        -cos(collocation_point.phi) * cos(collocation_point.theta) /\n        square(extraction_radius_);\n    get<0, 2>(*dr_inverse_jacobian)[collocation_point.offset] =\n        sin(collocation_point.phi) / square(extraction_radius_);\n    // radial derivatives of:\n    // dr/dy   dtheta/dy   dphi/dy * sin(theta)\n    get<1, 0>(*dr_inverse_jacobian)[collocation_point.offset] = 0.0;\n    get<1, 1>(*dr_inverse_jacobian)[collocation_point.offset] =\n        -cos(collocation_point.theta) * sin(collocation_point.phi) /\n        square(extraction_radius_);\n    get<1, 2>(*dr_inverse_jacobian)[collocation_point.offset] =\n        -cos(collocation_point.phi) / square(extraction_radius_);\n    // radial derivatives of:\n    // dr/dz   dtheta/dz   dphi/dz * sin(theta)\n    get<2, 0>(*dr_inverse_jacobian)[collocation_point.offset] = 0.0;\n    get<2, 1>(*dr_inverse_jacobian)[collocation_point.offset] =\n        sin(collocation_point.theta) / square(extraction_radius_);\n    get<2, 2>(*dr_inverse_jacobian)[collocation_point.offset] = 0.0;\n  }\n}\n\nvoid SphericalMetricData::dr_jacobian(\n    const gsl::not_null<SphericaliCartesianJ*> dr_jacobian,\n    const size_t l_max) {\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    // dx/dr   dy/dr  dz/dr\n    get<0, 0>(*dr_jacobian)[collocation_point.offset] = 0.0;\n    get<0, 1>(*dr_jacobian)[collocation_point.offset] = 0.0;\n    get<0, 2>(*dr_jacobian)[collocation_point.offset] = 0.0;\n    // dx/dtheta   dy/dtheta  dz/dtheta\n    get<1, 0>(*dr_jacobian)[collocation_point.offset] =\n        cos(collocation_point.theta) * cos(collocation_point.phi);\n    get<1, 1>(*dr_jacobian)[collocation_point.offset] =\n        cos(collocation_point.theta) * sin(collocation_point.phi);\n    get<1, 2>(*dr_jacobian)[collocation_point.offset] =\n        -sin(collocation_point.theta);\n    // (1/sin(theta)) { dx/dphi,   dy/dphi,  dz/dphi }\n    get<2, 0>(*dr_jacobian)[collocation_point.offset] =\n        -sin(collocation_point.phi);\n    get<2, 1>(*dr_jacobian)[collocation_point.offset] =\n        cos(collocation_point.phi);\n    get<2, 2>(*dr_jacobian)[collocation_point.offset] = 0.0;\n  }\n}\n\nvoid SphericalMetricData::pup(PUP::er& p) { WorldtubeData::pup(p); }\n\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryDataTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Cce {\nnamespace Solutions {\n\n/*!\n * \\brief Abstract base class for analytic worldtube data most easily derived in\n * spherical coordinate form.\n *\n * \\details This class provides the functions required by the `WorldtubeData`\n * interface that convert from a spherical coordinate spacetime metric to\n * Cartesian coordinates. Derived classes of `SphericalMetricData` need not\n * implement the `variables_impl`s for the Cartesian quantities. Instead, the\n * derived classes must override the protected functions:\n * - `SphericalMetricData::spherical_metric()`\n * - `SphericalMetricData::dr_spherical_metric()`\n * - `SphericalMetricData::dt_spherical_metric()`\n *\n * Derived classes are still responsible for overriding\n * `WorldtubeData::get_clone()`, `WorldtubeData::variables_impl()` for tag\n * `Cce::Tags::News`, and `WorldtubeData::prepare_solution()`.\n */\nstruct SphericalMetricData : public WorldtubeData {\n\n  WRAPPED_PUPable_abstract(SphericalMetricData);  // NOLINT\n\n  SphericalMetricData() = default;\n\n  explicit SphericalMetricData(CkMigrateMessage* msg) : WorldtubeData(msg) {}\n\n  explicit SphericalMetricData(const double extraction_radius)\n      : WorldtubeData{extraction_radius} {}\n\n  ~SphericalMetricData() override = default;\n\n  /*!\n   * Computes the Jacobian\n   * \\f$\\partial x_{\\mathrm{Cartesian}}^j / \\partial x_{\\mathrm{spherical}}^i\\f$\n   *\n   * \\details The Jacobian (with \\f$ \\sin \\theta \\f$\n   * scaled out of \\f$\\phi\\f$ components) in question is\n   *\n   * \\f{align*}{\n   * \\frac{\\partial x_{\\mathrm{Cartesian}}^j}{\\partial x_{\\mathrm{spherical}}^i}\n   * = \\left[\n   * \\begin{array}{ccc}\n   * \\frac{\\partial x}{\\partial r} & \\frac{\\partial y}{\\partial r} &\n   *  \\frac{\\partial z}{\\partial r} \\\\\n   * \\frac{\\partial x}{\\partial \\theta} & \\frac{\\partial y}{\\partial \\theta} &\n   *  \\frac{\\partial z}{\\partial \\theta} \\\\\n   * \\frac{\\partial x}{\\sin \\theta \\partial \\phi} &\n   *  \\frac{\\partial y}{\\sin \\theta \\partial \\phi} &\n   *  \\frac{\\partial y}{\\sin \\theta \\partial \\phi}\n   * \\end{array}\n   * \\right]\n   * = \\left[\n   * \\begin{array}{ccc}\n   * \\sin \\theta \\cos \\phi & \\sin \\theta \\sin \\phi & \\cos \\theta \\\\\n   * r \\cos \\theta \\cos \\phi & r \\cos \\theta \\sin \\phi & -r \\sin \\theta \\\\\n   * -r \\sin \\phi & r \\cos \\phi & 0\n   * \\end{array}\n   * \\right]\n   * \\f}\n   */\n  void jacobian(gsl::not_null<SphericaliCartesianJ*> jacobian,\n                size_t l_max) const;\n\n  /*!\n   * Computes the first radial derivative of the\n   * Jacobian: \\f$\\partial_r (\\partial x_{\\mathrm{Cartesian}}^j /\n   * \\partial x_{\\mathrm{Spherical}}^i)\\f$\n   *\n   * \\details The radial derivative of the Jacobian (with \\f$ \\sin \\theta \\f$\n   * scaled out of \\f$\\phi\\f$ components) in question is\n   *\n   * \\f{align*}{\n   * \\frac{\\partial}{\\partial r}\n   * \\frac{\\partial x_{\\mathrm{Cartesian}}^j}{\\partial x_{\\mathrm{spherical}}^i}\n   * = \\left[\n   * \\begin{array}{ccc}\n   * \\frac{\\partial^2 x}{(\\partial r)^2} & \\frac{\\partial^2 y}{(\\partial r)^2} &\n   *  \\frac{\\partial^2 z}{(\\partial r)^2} \\\\\n   * \\frac{\\partial^2 x}{\\partial r \\partial \\theta} &\n   * \\frac{\\partial^2 y}{\\partial r \\partial \\theta} &\n   *  \\frac{\\partial^2 z}{\\partial r \\partial \\theta} \\\\\n   * \\frac{\\partial^2 x}{\\sin \\theta \\partial r \\partial \\phi} &\n   *  \\frac{\\partial^2 y}{\\sin \\theta \\partial r \\partial \\phi} &\n   *  \\frac{\\partial^2 y}{\\sin \\theta \\partial r \\partial \\phi}\n   * \\end{array}\n   * \\right]\n   * = \\left[\n   * \\begin{array}{ccc}\n   * 0 & 0 & 0 \\\\\n   * \\cos \\theta \\cos \\phi & \\cos \\theta \\sin \\phi & - \\sin \\theta \\\\\n   * - \\sin \\phi & \\cos \\phi & 0\n   * \\end{array}\n   * \\right]\n   * \\f}\n   */\n  static void dr_jacobian(gsl::not_null<SphericaliCartesianJ*> dr_jacobian,\n                          size_t l_max);\n\n  /*!\n   * Computes the Jacobian\n   * \\f$\\partial x_{\\mathrm{spherical}}^j / \\partial\n   * x_{\\mathrm{Cartesian}}^i\\f$\n   *\n   * \\details The Jacobian (with \\f$ \\sin \\theta \\f$\n   * scaled out of \\f$\\phi\\f$ components) in question is\n   *\n   * \\f{align*}{\n   * \\frac{\\partial x_{\\mathrm{spherical}}^j}{\\partial x_{\\mathrm{Cartesian}}^i}\n   * = \\left[\n   * \\begin{array}{ccc}\n   * \\frac{\\partial r}{\\partial x} & \\frac{\\partial \\theta}{\\partial x} &\n   *  \\frac{\\sin \\theta \\partial \\phi}{\\partial x} \\\\\n   * \\frac{\\partial r}{\\partial y} & \\frac{\\partial \\theta}{\\partial y} &\n   *  \\frac{\\sin \\theta \\partial \\phi}{\\partial y} \\\\\n   * \\frac{\\partial r}{\\partial z} & \\frac{\\partial \\theta}{\\partial z} &\n   *  \\frac{\\sin \\theta \\partial \\phi}{\\partial z}\n   * \\end{array}\n   * \\right]\n   * = \\left[\n   * \\begin{array}{ccc}\n   * \\cos \\phi \\sin \\theta & \\frac{\\cos \\phi \\cos \\theta}{r} &\n   *  - \\frac{\\sin \\phi}{r} \\\\\n   * \\sin \\phi \\sin \\theta & \\frac{\\cos \\theta \\sin \\phi}{r} &\n   *  \\frac{\\cos \\phi}{r} \\\\\n   * \\cos \\theta & -\\frac{\\sin \\theta}{r} & 0\n   * \\end{array}\n   * \\right]\n   * \\f}\n   */\n  void inverse_jacobian(gsl::not_null<CartesianiSphericalJ*> inverse_jacobian,\n                        size_t l_max) const;\n\n  /*!\n   * Computes the first radial derivative of the\n   * Jacobian: \\f$\\partial_r (\\partial x_{\\mathrm{spherical}}^j / \\partial\n   * x_{\\mathrm{Cartesian}}^i)\\f$\n   *\n   * \\details The first radial derivative of the Jacobian (with\n   * \\f$ \\sin \\theta \\f$  scaled out of \\f$\\phi\\f$ components) in question is\n   *\n   * \\f{align*}{\n   * \\frac{\\partial}{\\partial r}\n   * \\frac{\\partial x_{\\mathrm{spherical}}^j}{\\partial x_{\\mathrm{Cartesian}}^i}\n   * = \\left[\n   * \\begin{array}{ccc}\n   * \\frac{\\partial}{\\partial r} \\frac{\\partial r}{\\partial x} &\n   * \\frac{\\partial}{\\partial r} \\frac{\\partial \\theta}{\\partial x} &\n   * \\frac{\\partial}{\\partial r} \\frac{\\sin \\theta \\partial \\phi}{\\partial x} \\\\\n   * \\frac{\\partial}{\\partial r} \\frac{\\partial r}{\\partial y} &\n   * \\frac{\\partial}{\\partial r} \\frac{\\partial \\theta}{\\partial y} &\n   * \\frac{\\partial}{\\partial r} \\frac{\\sin \\theta \\partial \\phi}{\\partial y} \\\\\n   * \\frac{\\partial}{\\partial r} \\frac{\\partial r}{\\partial z} &\n   * \\frac{\\partial}{\\partial r} \\frac{\\partial \\theta}{\\partial z} &\n   * \\frac{\\partial}{\\partial r} \\frac{\\sin \\theta \\partial \\phi}{\\partial z}\n   * \\end{array}\n   * \\right]\n   * = \\left[\n   * \\begin{array}{ccc}\n   * 0 & - \\frac{\\cos \\phi \\cos \\theta}{r^2} & \\frac{\\sin \\phi}{r^2} \\\\\n   * 0 & - \\frac{\\cos \\theta \\sin \\phi}{r^2} & -\\frac{\\cos \\phi}{r^2} \\\\\n   * 0 & \\frac{\\sin \\theta}{r^2} & 0\n   * \\end{array}\n   * \\right]\n   * \\f}\n   */\n  void dr_inverse_jacobian(\n      gsl::not_null<CartesianiSphericalJ*> dr_inverse_jacobian,\n      size_t l_max) const;\n\n  void pup(PUP::er& p) override;\n\n protected:\n  using WorldtubeData::variables_impl;\n\n  /*!\n   * \\brief Computes the Cartesian spacetime metric from the spherical solution\n   * provided by the derived classes.\n   *\n   * \\details The derived classes provide spherical metric data via the virtual\n   * function `SphericalMetricData::spherical_metric()` at a resolution\n   * determined by the `l_max` argument. This function performs the\n   * coordinate transformation using the Jacobian computed from\n   * `SphericalMetricData::inverse_jacobian()`.\n   */\n  void variables_impl(\n      gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric, size_t l_max,\n      double time,\n      tmpl::type_<gr::Tags::SpacetimeMetric<DataVector, 3>> /*meta*/)\n      const override;\n\n  /*!\n   * \\brief Computes the time derivative of the Cartesian spacetime metric from\n   * the spherical solution provided by the derived classes.\n   *\n   * \\details The derived classes provide the time derivative of the spherical\n   * metric data via the virtual function\n   * `SphericalMetricData::dt_spherical_metric()` at a resolution determined by\n   * the `l_max` argument. This function performs the coordinate\n   * transformation using the Jacobian computed from\n   * `SphericalMetricData::inverse_jacobian()`.\n   */\n  void variables_impl(\n      gsl::not_null<tnsr::aa<DataVector, 3>*> dt_spacetime_metric, size_t l_max,\n      double time,\n      tmpl::type_<\n          ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>> /*meta*/)\n      const override;\n\n  /*!\n   * \\brief Computes the spatial derivatives of the Cartesian spacetime metric\n   * from the spherical solution provided by the derived classes.\n   *\n   * \\details The derived classes provide the radial derivative of the spherical\n   * metric data via the virtual function\n   * `SphericalMetricData::dr_spherical_metric()` at a resolution determined by\n   * the `l_max_` argument. This function performs the additional angular\n   * derivatives necessary to assemble the full spatial derivative and performs\n   * the coordinate transformation to Cartesian coordinates via the Jacobians\n   * computed in `SphericalMetricData::inverse_jacobian()` and\n   * `SphericalMetricData::inverse_jacobian()`.\n   */\n  void variables_impl(\n      gsl::not_null<tnsr::iaa<DataVector, 3>*> d_spacetime_metric, size_t l_max,\n      double time,\n      tmpl::type_<gh::Tags::Phi<DataVector, 3>> /*meta*/) const override;\n\n  /// Must be overriden in the derived class; should compute the spacetime\n  /// metric of the analytic solution in spherical coordinates.\n  virtual void spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          spherical_metric,\n      size_t l_max, double time) const = 0;\n\n  /// Must be overriden in the derived class; should compute the first radial\n  /// derivative of the spacetime metric of the analytic solution in spherical\n  /// coordinates.\n  virtual void dr_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dr_spherical_metric,\n      size_t l_max, double time) const = 0;\n\n  /// Must be overriden in the derived class; should compute the first time\n  /// derivative of the spacetime metric of the analytic solution in spherical\n  /// coordinates.\n  virtual void dt_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dt_spherical_metric,\n      size_t l_max, double time) const = 0;\n};\n\n}  // namespace Solutions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\n\nTeukolskyWave::TeukolskyWave(const double extraction_radius,\n                             const double amplitude, const double duration)\n    : SphericalMetricData{extraction_radius},\n      amplitude_{amplitude},\n      duration_{duration} {}\n\nstd::unique_ptr<WorldtubeData> TeukolskyWave::get_clone() const {\n  return std::make_unique<TeukolskyWave>(*this);\n}\n\ndouble TeukolskyWave::pulse_profile_coefficient_a(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return 3.0 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n         (pow<4>(duration_) * pow<5>(extraction_radius_)) *\n         (3.0 * pow<4>(duration_) +\n          4.0 * square(extraction_radius_) * square(retarded_time) -\n          2.0 * square(duration_) * extraction_radius_ *\n              (extraction_radius_ + 3.0 * retarded_time));\n}\n\ndouble TeukolskyWave::pulse_profile_coefficient_b(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return 2.0 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n         (pow<6>(duration_) * pow<5>(extraction_radius_)) *\n         (-3.0 * pow<6>(duration_) +\n          4.0 * pow<3>(extraction_radius_) * pow<3>(retarded_time) -\n          6.0 * square(duration_) * pow<2>(extraction_radius_) * retarded_time *\n              (extraction_radius_ + retarded_time) +\n          3.0 * pow<4>(duration_) * extraction_radius_ *\n              (extraction_radius_ + 2.0 * retarded_time));\n}\n\ndouble TeukolskyWave::pulse_profile_coefficient_c(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return 0.25 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n         (pow<8>(duration_) * pow<5>(extraction_radius_)) *\n         (21.0 * pow<8>(duration_) +\n          16.0 * pow<4>(extraction_radius_) * pow<4>(retarded_time) -\n          16.0 * square(duration_) * pow<3>(extraction_radius_) *\n              square(retarded_time) *\n              (3.0 * extraction_radius_ + retarded_time) -\n          6.0 * pow<6>(duration_) * extraction_radius_ *\n              (3.0 * extraction_radius_ + 7.0 * retarded_time) +\n          12.0 * pow<4>(duration_) * square(extraction_radius_) *\n              (square(extraction_radius_) +\n               2.0 * extraction_radius_ * retarded_time +\n               3.0 * square(retarded_time)));\n}\n\ndouble TeukolskyWave::dr_pulse_profile_coefficient_a(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return -dt_pulse_profile_coefficient_a(time) -\n         9.0 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n             (pow<4>(duration_) * pow<6>(extraction_radius_)) *\n             (5.0 * pow<4>(duration_) +\n              4.0 * square(extraction_radius_) * square(retarded_time) -\n              2.0 * square(duration_) * extraction_radius_ *\n                  (extraction_radius_ + 4.0 * retarded_time));\n}\n\ndouble TeukolskyWave::dr_pulse_profile_coefficient_b(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return -dt_pulse_profile_coefficient_b(time) +\n         2.0 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n             (pow<6>(duration_ * extraction_radius_)) *\n             (15.0 * pow<6>(duration_) -\n              8.0 * pow<3>(extraction_radius_) * pow<3>(retarded_time) +\n              6.0 * square(duration_) * square(extraction_radius_) *\n                  retarded_time *\n                  (2.0 * extraction_radius_ + 3.0 * retarded_time) -\n              3.0 * pow<4>(duration_) * extraction_radius_ *\n                  (3.0 * extraction_radius_ + 8.0 * retarded_time));\n}\n\ndouble TeukolskyWave::dr_pulse_profile_coefficient_c(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return -dt_pulse_profile_coefficient_c(time) -\n         0.25 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n             (pow<8>(duration_) * pow<6>(extraction_radius_)) *\n             (105.0 * pow<8>(duration_) +\n              16.0 * pow<4>(extraction_radius_ * retarded_time) -\n              16.0 * square(duration_) * pow<3>(extraction_radius_) *\n                  square(retarded_time) *\n                  (3.0 * extraction_radius_ + 2.0 * retarded_time) -\n              6.0 * pow<6>(duration_) * extraction_radius_ *\n                  (9.0 * extraction_radius_ + 28.0 * retarded_time) +\n              12.0 * pow<4>(duration_) * square(extraction_radius_) *\n                  (square(extraction_radius_) +\n                   4.0 * extraction_radius_ * retarded_time +\n                   9.0 * square(retarded_time)));\n}\n\ndouble TeukolskyWave::dt_pulse_profile_coefficient_a(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return -2.0 * retarded_time / square(duration_) *\n             pulse_profile_coefficient_a(time) +\n         3.0 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n             (pow<4>(duration_) * pow<5>(extraction_radius_)) *\n             (8.0 * square(extraction_radius_) * retarded_time -\n              6.0 * square(duration_) * extraction_radius_);\n}\n\ndouble TeukolskyWave::dt_pulse_profile_coefficient_b(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return -2.0 * retarded_time / square(duration_) *\n             pulse_profile_coefficient_b(time) +\n         2.0 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n             (pow<6>(duration_) * pow<5>(extraction_radius_)) *\n             (12.0 * pow<3>(extraction_radius_) * square(retarded_time) -\n              6.0 * square(duration_) * square(extraction_radius_) *\n                  (extraction_radius_ + 2.0 * retarded_time) +\n              6.0 * pow<4>(duration_) * extraction_radius_);\n}\n\ndouble TeukolskyWave::dt_pulse_profile_coefficient_c(const double time) const {\n  const double retarded_time = time - extraction_radius_;\n  return -2.0 * retarded_time / square(duration_) *\n             pulse_profile_coefficient_c(time) +\n         0.25 * amplitude_ * exp(-square(retarded_time) / square(duration_)) /\n             (pow<8>(duration_) * pow<5>(extraction_radius_)) *\n             (64.0 * pow<4>(extraction_radius_) * pow<3>(retarded_time) -\n              16.0 * square(duration_) * pow<3>(extraction_radius_) *\n                  retarded_time *\n                  (6.0 * extraction_radius_ + 3.0 * retarded_time) -\n              42.0 * pow<6>(duration_) * extraction_radius_ +\n              12.0 * pow<4>(duration_) * square(extraction_radius_) *\n                  (2.0 * extraction_radius_ + 6.0 * retarded_time));\n}\n\nDataVector TeukolskyWave::sin_theta(const size_t l_max) {\n  DataVector sin_theta_result{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    sin_theta_result[collocation_point.offset] = sin(collocation_point.theta);\n  }\n  return sin_theta_result;\n}\n\nDataVector TeukolskyWave::cos_theta(const size_t l_max) {\n  DataVector cos_theta_result{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    cos_theta_result[collocation_point.offset] = cos(collocation_point.theta);\n  }\n  return cos_theta_result;\n}\n\nvoid TeukolskyWave::spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        spherical_metric,\n    const size_t l_max, const double time) const {\n  const auto coefficient_a = pulse_profile_coefficient_a(time);\n  const auto coefficient_b = pulse_profile_coefficient_b(time);\n  const auto coefficient_c = pulse_profile_coefficient_c(time);\n\n  const auto local_sin_theta = sin_theta(l_max);\n  const auto local_cos_theta = cos_theta(l_max);\n\n  get<0, 0>(*spherical_metric) = -1.0;\n  get<0, 1>(*spherical_metric) = 0.0;\n  get<0, 2>(*spherical_metric) = 0.0;\n  get<0, 3>(*spherical_metric) = 0.0;\n\n  get<1, 3>(*spherical_metric) = 0.0;\n  get<2, 3>(*spherical_metric) = 0.0;\n  get<1, 1>(*spherical_metric) =\n      1.0 + coefficient_a * (2.0 - 3.0 * square(local_sin_theta));\n  get<1, 2>(*spherical_metric) = -3.0 * local_sin_theta * local_cos_theta *\n                                 extraction_radius_ * coefficient_b;\n  get<2, 2>(*spherical_metric) =\n      (1.0 + 3.0 * square(local_sin_theta) * coefficient_c - coefficient_a) *\n      square(extraction_radius_);\n  // note: omitted sin^2(theta) to optimize the precision in common case where\n  // it would cancel with derivative or Jacobian factors.\n  get<3, 3>(*spherical_metric) =\n      (1.0 - 3.0 * square(local_sin_theta) * coefficient_c +\n       (3.0 * square(local_sin_theta) - 1.0) * coefficient_a) *\n      square(extraction_radius_);\n}\n\nvoid TeukolskyWave::dr_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dr_spherical_metric,\n    const size_t l_max, const double time) const {\n  const auto coefficient_a = pulse_profile_coefficient_a(time);\n  const auto coefficient_b = pulse_profile_coefficient_b(time);\n  const auto coefficient_c = pulse_profile_coefficient_c(time);\n\n  const auto dr_coefficient_a = dr_pulse_profile_coefficient_a(time);\n  const auto dr_coefficient_b = dr_pulse_profile_coefficient_b(time);\n  const auto dr_coefficient_c = dr_pulse_profile_coefficient_c(time);\n\n  const auto local_sin_theta = sin_theta(l_max);\n  const auto local_cos_theta = cos_theta(l_max);\n\n  for (size_t a = 0; a < 4; ++a) {\n    dr_spherical_metric->get(0, a) = 0.0;\n  }\n\n  get<1, 1>(*dr_spherical_metric) =\n      dr_coefficient_a * (2.0 - 3.0 * square(local_sin_theta));\n  get<1, 3>(*dr_spherical_metric) = 0.0;\n  get<2, 3>(*dr_spherical_metric) = 0.0;\n\n  get<1, 2>(*dr_spherical_metric) =\n      -3.0 * local_sin_theta * local_cos_theta *\n      (coefficient_b + extraction_radius_ * dr_coefficient_b);\n  get<2, 2>(*dr_spherical_metric) =\n      (2.0 *\n           (1.0 - coefficient_a +\n            3.0 * square(local_sin_theta) * coefficient_c) *\n           extraction_radius_ +\n       (-dr_coefficient_a + 3.0 * square(local_sin_theta) * dr_coefficient_c) *\n           square(extraction_radius_));\n  // note: omitted sin^2(theta) to optimize the precision in common case where\n  // it would cancel with derivative or Jacobian factors.\n  get<3, 3>(*dr_spherical_metric) =\n      (2.0 *\n           (1.0 + (3.0 * square(local_sin_theta) - 1.0) * coefficient_a -\n            3.0 * square(local_sin_theta) * coefficient_c) *\n           extraction_radius_ +\n       ((3.0 * square(local_sin_theta) - 1.0) * dr_coefficient_a -\n        3.0 * square(local_sin_theta) * dr_coefficient_c) *\n           square(extraction_radius_));\n}\n\nvoid TeukolskyWave::dt_spherical_metric(\n    const gsl::not_null<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n        dt_spherical_metric,\n    const size_t l_max, const double time) const {\n  const auto dt_coefficient_a = dt_pulse_profile_coefficient_a(time);\n  const auto dt_coefficient_b = dt_pulse_profile_coefficient_b(time);\n  const auto dt_coefficient_c = dt_pulse_profile_coefficient_c(time);\n\n  const auto local_sin_theta = sin_theta(l_max);\n  const auto local_cos_theta = cos_theta(l_max);\n\n  for (size_t a = 0; a < 4; ++a) {\n    dt_spherical_metric->get(0, a) = 0.0;\n  }\n\n  get<1, 1>(*dt_spherical_metric) =\n      dt_coefficient_a * (2.0 - 3.0 * square(local_sin_theta));\n  get<1, 3>(*dt_spherical_metric) = 0.0;\n  get<2, 3>(*dt_spherical_metric) = 0.0;\n  get<1, 2>(*dt_spherical_metric) = -3.0 * local_sin_theta * local_cos_theta *\n                                    extraction_radius_ * dt_coefficient_b;\n  get<2, 2>(*dt_spherical_metric) =\n      ((-dt_coefficient_a + 3.0 * square(local_sin_theta) * dt_coefficient_c) *\n       square(extraction_radius_));\n  // note: omitted sin^2(theta) to optimize the precision in common case where\n  // it would cancel with derivative or Jacobian factors.\n  get<3, 3>(*dt_spherical_metric) =\n      (((3.0 * square(local_sin_theta) - 1.0) * dt_coefficient_a -\n        3.0 * square(local_sin_theta) * dt_coefficient_c) *\n       square(extraction_radius_));\n}\n\nvoid TeukolskyWave::variables_impl(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n    const size_t l_max, const double time,\n    tmpl::type_<Tags::News> /*meta*/) const {\n  const auto local_sin_theta = sin_theta(l_max);\n  get(*news).data() =\n      -std::complex<double>{6.0, 0.0} * square(local_sin_theta) * amplitude_ *\n      exp(-square(time - extraction_radius_) / square(duration_)) *\n      (time - extraction_radius_) / pow<10>(duration_) *\n      (15.0 * pow<4>(duration_) -\n       20.0 * square(duration_ * (time - extraction_radius_)) +\n       4.0 * pow<4>(time - extraction_radius_));\n}\n\nvoid TeukolskyWave::pup(PUP::er& p) {\n  SphericalMetricData::pup(p);\n  p | amplitude_;\n  p | duration_;\n}\n\nPUP::able::PUP_ID TeukolskyWave::my_PUP_ID = 0;\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\nnamespace Solutions {\n\n/*!\n * \\brief Computes the analytic data for a Teukolsky wave solution described in\n * \\cite Barkett2019uae.\n *\n * \\details This test computes an outgoing perturbative wave solution in\n * spherical coordinates with wave profile\n *\n * \\f[\n * F(u) = A e^{- u^2 / k^2}.\n * \\f]\n */\nstruct TeukolskyWave : public SphericalMetricData {\n  struct ExtractionRadius {\n    using type = double;\n    static constexpr Options::String help{\n        \"The extraction radius of the spherical solution\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help{\n        \"The amplitude of the Teukolsky wave.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Duration {\n    using type = double;\n    static constexpr Options::String help{\n        \"The characteristic duration of the Gaussian envelope.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  static constexpr Options::String help{\n      \"An analytic solution derived from the linearized Teukolsky equation\"};\n\n  using options = tmpl::list<ExtractionRadius, Amplitude, Duration>;\n\n  WRAPPED_PUPable_decl_template(TeukolskyWave);  // NOLINT\n\n  explicit TeukolskyWave(CkMigrateMessage* msg) : SphericalMetricData(msg) {}\n\n  // clang doesn't manage to use = default correctly in this case\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  TeukolskyWave() {}\n\n  TeukolskyWave(double extraction_radius, double amplitude, double duration);\n\n  std::unique_ptr<WorldtubeData> get_clone() const override;\n\n  void pup(PUP::er& p) override;\n\n private:\n  /*\n   * The A coefficient is calculated as\n   *\n   * \\f[\n   * A = 3 (3 k^4 + 4 r^2 u^2 - 2 k^2 r^2 (r + 3 u)) a e^{-u^2/k^2} / (k^4 r^5)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double pulse_profile_coefficient_a(double time) const;\n\n  /*\n   * The B coefficient is calculated as\n   *\n   * \\f[\n   * B = 2 (-3 k^6 + 4 r^4 u^3 - 6 k^2 r^3 u (r + u) + 3 k^4 r^2 (r + 2 u))\n   *  * a e^{-u^2/k^2} / (k^6 r^6)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double pulse_profile_coefficient_b(double time) const;\n\n  /*\n   * The C coefficient is calculated as\n   *\n   * \\f[\n   * C = (1/4) (21 k^8 + 16 r^4 u^4 - 16 k^2 r^3 u^2 (3 r + u)\n   * - 6 k^6 r (3 r + 7 u) + 12 k^4 r^2 (r^2 + 2 r u + 3 u^2))\n   *  * a e^{-u^2/k^2} / (k^8 r^5)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double pulse_profile_coefficient_c(double time) const;\n\n  /*\n   * The time derivative of the A coefficient is calculated as\n   *\n   * \\f[\n   * \\partial_t A = -6 (4 r^2 u^3 + 3 k^4 (r + u) - 6 k^2 r u (r + u))\n   *  a e^{-u^2/k^2} / (k^6 r^5)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double dt_pulse_profile_coefficient_a(double time) const;\n\n  /*\n   * The time derivative of the B coefficient is calculated as\n   *\n   * \\f[\n   * \\partial_t B = 4 (-4 r^4 u^4 + 6 k ^2 r^3 u^2 (2 r + u)\n   * + 3 k^6 (r^2 + u) - 3 k^4 r^2 (r + u) (r + 2u))\n   *  a e^{-u^2/k^2} / (k^8 r^6)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double dt_pulse_profile_coefficient_b(double time) const;\n\n  /*\n   * The time derivative of the C coefficient is calculated as\n   *\n   * \\f[\n   * \\partial_t C = (1/2) (-16 r^4 u^5 - 21 k^8 (r + u)\n   * + 16 k^2 r^3 u^3 (5 r + u) + 6 k^6 r (r + u) (2 r + 7 u)\n   * - 12 k^4 r^2 u (4 r^2 + 4 r u + 3 u^2))\n   *  a e^{-u^2/k^2} / (k^10 r^5)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double dt_pulse_profile_coefficient_c(double time) const;\n\n  /*\n   * The radial derivative of the A coefficient is calculated as\n   *\n   * \\f[\n   * \\partial_r A + \\partial_t A = - 9 (5 k^4 + 4 r^2 u^2 - 2 k^2 r (r + 4 u))\n   * a e^{-u^2/k^2} / (k^4 r^6)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double dr_pulse_profile_coefficient_a(double time) const;\n\n  /*\n   * The radial derivative of the B coefficient is calculated as\n   *\n   * \\f[\n   * \\partial_r B + \\partial_t B  = 2 (18 k^6 - 8 r^4 u^3 + 6 k^2 r^3 u (2 r + 3\n   * u)\n   * - 3 k^4 r^2 (3 r + 8 u)) a e^{-u^2/k^2} / (k^6 r^7)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double dr_pulse_profile_coefficient_b(double time) const;\n\n  /*\n   * The radial derivative of the C coefficient is calculated as\n   *\n   * \\f[\n   * \\partial_r C + \\partial_t C  = -(1/4) (105 * k^8 + 16 r^4 u^4\n   * - 16 k^2 r^3 u^2 (3 r + 2 u) - 6 k^6 r (9 r + 28 u)\n   * + 12 k^4 r^2 (r^2 + 4 r u + 9 u^2))\n   *  a e^{-u^2/k^2} / (k^8 r^6)\n   * \\f]\n   *\n   * where \\f$r\\f$ is the extraction radius, \\f$u\\f$ the retarded time, \\f$a\\f$\n   * the amplitude of the pulse and \\f$k\\f$ the duration of the pulse.\n   */\n  double dr_pulse_profile_coefficient_c(double time) const;\n\n  static DataVector sin_theta(size_t l_max);\n\n  static DataVector cos_theta(size_t l_max);\n\n protected:\n  /// A no-op as the Teukolsky wave solution does not have substantial\n  /// shared computation to prepare before the separate component calculations.\n  void prepare_solution(const size_t /*output_l_max*/,\n                        const double /*time*/) const override {}\n\n  /*!\n   * \\brief Compute the spherical coordinate metric from the closed-form\n   * perturbative Teukolsky wave metric.\n   *\n   * \\details The specific outgoing wave selected in this analytic solution is\n   * constructed from a (2,0) mode as in \\cite Barkett2019uae, and takes the\n   * form\n   *\n   * \\f{align*}{\n   * g_{tt} &= -1\\\\\n   * g_{rr} &= (1 + A f_{rr}) \\\\\n   * g_{r \\theta} &= 2 B f_{r \\theta} r\\\\\n   * g_{\\theta \\theta} &= (1 + C f_{\\theta \\theta}^{(C)}\n   * + A f_{\\theta \\theta}^{(A)}) r^2\\\\\n   * g_{\\phi \\phi} &= (1 + C f_{\\phi \\phi}^{(C)}\n   * + A f_{\\phi \\phi}^{(A)}) r^2 \\sin^2 \\theta\\\\\n   * \\f}\n   *\n   * and all other components vanish. The angular factors generated by the\n   * choice of spin-weighted spherical harmonic are\n   *\n   * \\f{align*}{\n   * f_{rr} &= 2 - 3 \\sin^2 \\theta \\\\\n   * f_{r \\theta} &=  -3 \\sin \\theta \\cos \\theta \\\\\n   * f_{\\theta \\theta}^{(C)} &= 3 \\sin^2 \\theta \\\\\n   * f_{\\theta \\theta}^{(A)} &= -1 \\\\\n   * f_{\\phi \\phi}^{(C)} &= - 3 \\sin^2 \\theta \\\\\n   * f_{\\phi \\phi}^{(A)} &= 3 \\sin^2 \\theta -1,\n   * \\f}\n   *\n   * the radial and time dependent factors are\n   *\n   * \\f{align*}{\n   * A &= 3 \\left(\\frac{\\partial_u^2 F(u)}{r^3}\n   * + \\frac{3 \\partial_u F(u)}{r^4} + \\frac{3 F(u)}{r^5} \\right),\\\\\n   * B &= - \\left(\\frac{\\partial_u^3 F(u)}{r^2}\n   * + \\frac{3 \\partial_u^2 F(u)}{r^3} + \\frac{6 \\partial_uF(u)}{r^4}\n   * + \\frac{6 F(u)}{r^5}\\right), \\\\\n   * C &= \\frac{1}{4} \\left(\\frac{\\partial_u^4 F(u)}{r}\n   * + \\frac{2 \\partial_u^3 F(u)}{r^2} + \\frac{9 \\partial_u^2 F(u)}{r^3}\n   * + \\frac{21 \\partial_u F(u)}{r^4} + \\frac{21 F(u)}{r}\\right),\n   * \\f}\n   *\n   * and the pulse profile is\n   *\n   * \\f[\n   * F(u) = a e^{-u^2 /k^2}.\n   * \\f]\n   *\n   * So, the pulse profile factors expand to\n   *\n   * \\f{align*}{\n   * A &= \\frac{3 a e^{-u^2/k^2}}{k^4 r^5} \\left(3 k^4 + 4 r^2 u^2\n   * - 2 k^2 r (r + 3 u)\\right),\\\\\n   * B &= \\frac{2 a e^{-u^2/k^2}}{k^6 r^5} \\left(-3 k^6 + 4 r^3 u^3\n   * - 6 k^2 r^2 u (r + u) + 3 k^4 r (r + 2 u)\\right), \\\\\n   * C &= \\frac{a e^{-u^2/k^2}}{4 k^8 r^5} \\left(21 k^8 + 16 r^4 u^4\n   * - 16 k^2 r^3 u^2 (3 r + u) - 6 k^6 r (3 r + 7 u)\n   * + 12 k^4 r^2 (r^2 + 2 r u + 3 u^2)\\right),\n   * \\f}\n   *\n   * \\note The \\f$\\phi\\f$ components are returned in a form for which the\n   * \\f$\\sin(\\theta)\\f$ factors are omitted, assuming that derivatives and\n   * Jacobians will be applied similarly omitting those factors (and therefore\n   * improving precision of the tensor expression). If you require the\n   * \\f$\\sin(\\theta)\\f$ factors, be sure to put them in by hand in the calling\n   * code.\n   */\n  void spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the radial derivative of the spherical coordinate metric\n   * from the closed-form perturbative Teukolsky wave metric.\n   *\n   * \\details The specific outgoing wave selected in this analytic solution is\n   * constructed from a (2,0) mode as in \\cite Barkett2019uae, and takes the\n   * form\n   *\n   * \\f{align*}{\n   * \\partial_r g_{rr} &= f_{r r} \\partial_r A \\\\\n   * \\partial_r g_{r \\theta} &= f_{r \\theta} (B + r \\partial_r B)\\\\\n   * \\partial_r g_{\\theta \\theta} &= 2 (1 + C f_{\\theta \\theta}^{(C)}\n   * + A f_{\\theta \\theta}^{(A)}) r\n   * + (\\partial_r C f_{\\theta \\theta}^{(C)}\n   * + \\partial_r A f_{\\theta \\theta}^{(A)}) r^2 \\\\\n   * \\partial_r g_{\\phi \\phi} &= 2 (1 + C f_{\\phi \\phi}^{(C)}\n   * + A f_{\\phi \\phi}^{(A)}) r \\sin^2 \\theta\n   * + (\\partial_r C f_{\\phi \\phi}^{(C)}\n   * + \\partial_r A f_{\\phi \\phi}^{(A)}) r^2 \\sin^2 \\theta\\\\\n   * \\f}\n   *\n   * and all other components vanish. The angular factors \\f$f_{a b}\\f$ and the\n   * metric component functions \\f$A, B,\\f$ and \\f$C\\f$ are defined as in\n   * `TeukolskyWave::spherical_metric()`.\n   * The radial derivatives of the pulse profile functions are obtained by:\n   *\n   * \\f{align*}{\n   * \\partial_r A + \\partial_t A &= \\frac{-9 a e^{-u^2/k^2}}{k^4 r^6} \\left(\n   *  5 k^4 + 4 r^2 u^2 - 2 k^2 r (r + 4 u)\\right), \\\\\n   * \\partial_r B + \\partial_t B &= \\frac{2 a e^{-u^2/k^2}}{k^6 r^6} \\left(\n   *  15 k^6 - 8 r^3 u^3 + 6 k^2 r^2 u (2 r + 3 u)\n   *  - 3 k^4 r (3 r + 8 u)\\right), \\\\\n   * \\partial_r C + \\partial_t C &= \\frac{-a e^{-u^2/k^2}}{4 k^8 r^6} \\left(\n   *  105 k^8 + 16 k^4 u^4 - 16 k^2 r^3 u^2 (3 r + 2 u) - 6 k^6 r (9 r + 28 u)\n   *  + 12 k^4 r^2 (r^2 + 4 r u + 9 u^2)\\right),\n   * \\f}\n   *\n   * and the time derivatives of the pulse profile functions are given in\n   * `TeukolskyWave::dt_spherical_metric()`.\n   *\n   * \\note The \\f$\\phi\\f$ components are returned in a form for which the\n   * \\f$\\sin(\\theta)\\f$ factors are omitted, assuming that derivatives and\n   * Jacobians will be applied similarly omitting those factors (and therefore\n   * improving precision of the tensor expression). If you require the\n   * \\f$\\sin(\\theta)\\f$ factors, be sure to put them in by hand in the calling\n   * code.\n   */\n  void dr_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dr_spherical_metric,\n      size_t l_max, double time) const override;\n\n  /*!\n   * \\brief Compute the time derivative of the spherical coordinate metric\n   * from the closed-form perturbative Teukolsky wave metric.\n   *\n   * \\details The specific outgoing wave selected in this analytic solution is\n   * constructed from a (2,0) mode as in \\cite Barkett2019uae, and takes the\n   * form\n   *\n   * \\f{align*}{\n   * \\partial_t g_{rr} &= f_{r r} \\partial_t A \\\\\n   * \\partial_t g_{r \\theta} &= f_{r \\theta} r \\partial_t B\\\\\n   * \\partial_t g_{\\theta \\theta} &=\n   * (\\partial_t C f_{\\theta \\theta}^{(C)}\n   * + \\partial_t A f_{\\theta \\theta}^{(A)}) r^2 \\\\\n   * \\partial_t g_{\\phi \\phi} &= (\\partial_t C f_{\\phi \\phi}^{(C)}\n   * + \\partial_t A f_{\\phi \\phi}^{(A)}) r^2 \\sin^2 \\theta\\\\\n   * \\f}\n   *\n   * and all other components vanish. The angular factors \\f$f_{a b}\\f$ and the\n   * metric component functions \\f$A, B,\\f$ and \\f$C\\f$ are defined as in\n   * `TeukolskyWave::spherical_metric()`.\n   * The time derivatives of the pulse profile functions are:\n   *\n   * \\f{align*}{\n   * \\partial_t A &=  \\frac{-2 u}{k^2} A + \\frac{3 a e^{-u^2/k^2}}{k^4 r^5}\n   *  \\left( 8 r^2 u - 6 k^2 r \\right), \\\\\n   * \\partial_t B &= \\frac{-2 u}{k^2} B + \\frac{2 a e^{-u^2/k^2}}{k^6 r^5}\n   *  \\left(12 r^3 u^2 - 6 k^2 r^2 (r + 2 u) + 6 k^4 r\\right), \\\\\n   * \\partial_t C &= \\frac{-2 u}{k^2} C + \\frac{-a e^{-u^2/k^2}}{4 k^8 r^5}\n   * \\left(64 k^4 u^3 - 16 k^2 r^3 u (6 r +  3 u) - 42 k^6 r\n   *  + 12 k^4 r^2 (2 r + 6 u)\\right),\n   * \\f}\n   *\n   * \\note The \\f$\\phi\\f$ components are returned in a form for which the\n   * \\f$\\sin(\\theta)\\f$ factors are omitted, assuming that derivatives and\n   * Jacobians will be applied similarly omitting those factors (and therefore\n   * improving precision of the tensor expression). If you require the\n   * \\f$\\sin(\\theta)\\f$ factors, be sure to put them in by hand in the calling\n   * code.\n   */\n  void dt_spherical_metric(\n      gsl::not_null<\n          tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>*>\n          dt_spherical_metric,\n      size_t l_max, double time) const override;\n\n  using WorldtubeData::variables_impl;\n\n  using SphericalMetricData::variables_impl;\n\n  /*!\n   * \\brief Compute the news associated with the (2,0)-mode Teukolsky wave.\n   *\n   * \\details The value of the news is\n   *\n   * \\f{align*}{\n   * N = \\frac{3 \\sin^2 \\theta}{4} \\partial_u^5 F(u)\n   * \\f}\n   *\n   * where \\f$F(u)\\f$ is the pulse profile, taken to be\n   *\n   * \\f[\n   * F(u) = a e^{-u^2 /k^2},\n   * \\f]\n   *\n   * So, the news expands to\n   *\n   * \\f[\n   * N = -\\frac{6 a e^{-u^2/k^2} u}{k^{10}} \\left(15 k^4 - 20 k^2 u^2\n   * + 4 u^4\\right)\n   * \\f]\n   *\n   * in this analytic solution.\n   */\n  void variables_impl(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n      size_t l_max, double time,\n      tmpl::type_<Tags::News> /*meta*/) const override;\n\n  double amplitude_ = std::numeric_limits<double>::signaling_NaN();\n  double duration_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace Solutions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_coordinates,\n    const size_t output_l_max, double /*time*/,\n    tmpl::type_<Tags::CauchyCartesianCoords> /*meta*/) const {\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(output_l_max);\n  for (const auto collocation_point : collocation) {\n    get<0>(*cartesian_coordinates)[collocation_point.offset] =\n        extraction_radius_ * cos(collocation_point.phi) *\n        sin(collocation_point.theta);\n    get<1>(*cartesian_coordinates)[collocation_point.offset] =\n        extraction_radius_ * sin(collocation_point.phi) *\n        sin(collocation_point.theta);\n    get<2>(*cartesian_coordinates)[collocation_point.offset] =\n        extraction_radius_ * cos(collocation_point.theta);\n  }\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> dr_cartesian_coordinates,\n    const size_t output_l_max, const double /*time*/,\n    tmpl::type_<Tags::Dr<Tags::CauchyCartesianCoords>> /*meta*/) const {\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(output_l_max);\n  for (const auto collocation_point : collocation) {\n    get<0>(*dr_cartesian_coordinates)[collocation_point.offset] =\n        cos(collocation_point.phi) * sin(collocation_point.theta);\n    get<1>(*dr_cartesian_coordinates)[collocation_point.offset] =\n        sin(collocation_point.phi) * sin(collocation_point.theta);\n    get<2>(*dr_cartesian_coordinates)[collocation_point.offset] =\n        cos(collocation_point.theta);\n  }\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> pi, const size_t output_l_max,\n    const double time,\n    tmpl::type_<gh::Tags::Pi<DataVector, 3>> /*meta*/) const {\n  const auto& d_spacetime_metric =\n      cache_or_compute<gh::Tags::Phi<DataVector, 3>>(output_l_max, time);\n  const auto& dt_spacetime_metric =\n      cache_or_compute<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n          output_l_max, time);\n  const auto& lapse =\n      cache_or_compute<gr::Tags::Lapse<DataVector>>(output_l_max, time);\n  const auto& shift =\n      cache_or_compute<gr::Tags::Shift<DataVector, 3>>(output_l_max, time);\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      pi->get(a, b) = -dt_spacetime_metric.get(a, b) / get(lapse);\n      for (size_t i = 0; i < 3; ++i) {\n        pi->get(a, b) +=\n            shift.get(i) * d_spacetime_metric.get(i, a, b) / get(lapse);\n      }\n    }\n  }\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> spatial_metric,\n    const size_t output_l_max, const double time,\n    tmpl::type_<gr::Tags::SpatialMetric<DataVector, 3>> /*meta*/) const {\n  gr::spatial_metric(spatial_metric,\n                     cache_or_compute<gr::Tags::SpacetimeMetric<DataVector, 3>>(\n                         output_l_max, time));\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> dt_spatial_metric,\n    const size_t output_l_max, const double time,\n    tmpl::type_<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>> /*meta*/)\n    const {\n  const auto& dt_spacetime_metric =\n      cache_or_compute<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n          output_l_max, time);\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      dt_spatial_metric->get(i, j) = dt_spacetime_metric.get(i + 1, j + 1);\n    }\n  }\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> dr_spatial_metric,\n    const size_t output_l_max, const double time,\n    tmpl::type_<Tags::Dr<gr::Tags::SpatialMetric<DataVector, 3>>> /*meta*/)\n    const {\n  const auto& dr_cartesian_coordinates =\n      cache_or_compute<Tags::Dr<Tags::CauchyCartesianCoords>>(output_l_max,\n                                                              time);\n  const auto& d_spacetime_metric =\n      cache_or_compute<gh::Tags::Phi<DataVector, 3>>(output_l_max, time);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      dr_spatial_metric->get(i, j) = get<0>(dr_cartesian_coordinates) *\n                                     d_spacetime_metric.get(0, i + 1, j + 1);\n      for (size_t k = 1; k < 3; ++k) {\n        dr_spatial_metric->get(i, j) += dr_cartesian_coordinates.get(k) *\n                                        d_spacetime_metric.get(k, i + 1, j + 1);\n      }\n    }\n  }\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift,\n    const size_t output_l_max, const double time,\n    tmpl::type_<gr::Tags::Shift<DataVector, 3>> /*meta*/) const {\n  const auto& spacetime_metric =\n      cache_or_compute<gr::Tags::SpacetimeMetric<DataVector, 3>>(output_l_max,\n                                                                 time);\n  const auto& spatial_metric =\n      cache_or_compute<gr::Tags::SpatialMetric<DataVector, 3>>(output_l_max,\n                                                               time);\n  gr::shift(shift, spacetime_metric,\n            determinant_and_inverse(spatial_metric).second);\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> dt_shift,\n    const size_t output_l_max, const double time,\n    tmpl::type_<::Tags::dt<gr::Tags::Shift<DataVector, 3>>> /*meta*/) const {\n  const auto& lapse =\n      cache_or_compute<gr::Tags::Lapse<DataVector>>(output_l_max, time);\n  const auto& shift =\n      cache_or_compute<gr::Tags::Shift<DataVector, 3>>(output_l_max, time);\n  gh::time_deriv_of_shift(\n      dt_shift, lapse, shift,\n      determinant_and_inverse(\n          cache_or_compute<gr::Tags::SpatialMetric<DataVector, 3>>(output_l_max,\n                                                                   time))\n          .second,\n      gr::spacetime_normal_vector(lapse, shift),\n      cache_or_compute<gh::Tags::Phi<DataVector, 3>>(output_l_max, time),\n      cache_or_compute<gh::Tags::Pi<DataVector, 3>>(output_l_max, time));\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> dr_shift,\n    const size_t output_l_max, const double time,\n    tmpl::type_<Tags::Dr<gr::Tags::Shift<DataVector, 3>>> /*meta*/) const {\n  const auto& lapse =\n      cache_or_compute<gr::Tags::Lapse<DataVector>>(output_l_max, time);\n  const auto& shift =\n      cache_or_compute<gr::Tags::Shift<DataVector, 3>>(output_l_max, time);\n  const auto d_shift = gh::spatial_deriv_of_shift(\n      lapse,\n      gr::inverse_spacetime_metric(\n          lapse, shift,\n          determinant_and_inverse(\n              cache_or_compute<gr::Tags::SpatialMetric<DataVector, 3>>(\n                  output_l_max, time))\n              .second),\n      gr::spacetime_normal_vector(lapse, shift),\n      cache_or_compute<gh::Tags::Phi<DataVector, 3>>(output_l_max, time));\n  const auto& dr_cartesian_coordinates =\n      cache_or_compute<Tags::Dr<Tags::CauchyCartesianCoords>>(output_l_max,\n                                                              time);\n\n  for (size_t i = 0; i < 3; ++i) {\n    dr_shift->get(i) = get<0>(dr_cartesian_coordinates) * d_shift.get(0, i);\n    for (size_t j = 1; j < 3; ++j) {\n      dr_shift->get(i) += dr_cartesian_coordinates.get(j) * d_shift.get(j, i);\n    }\n  }\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<Scalar<DataVector>*> lapse, const size_t output_l_max,\n    const double time,\n    tmpl::type_<gr::Tags::Lapse<DataVector>> /*meta*/) const {\n  gr::lapse(\n      lapse,\n      cache_or_compute<gr::Tags::Shift<DataVector, 3>>(output_l_max, time),\n      cache_or_compute<gr::Tags::SpacetimeMetric<DataVector, 3>>(output_l_max,\n                                                                 time));\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<Scalar<DataVector>*> dt_lapse,\n    const size_t output_l_max, const double time,\n    tmpl::type_<::Tags::dt<gr::Tags::Lapse<DataVector>>> /*meta*/) const {\n  const auto& lapse =\n      cache_or_compute<gr::Tags::Lapse<DataVector>>(output_l_max, time);\n  const auto& shift =\n      cache_or_compute<gr::Tags::Shift<DataVector, 3>>(output_l_max, time);\n  gh::time_deriv_of_lapse(\n      dt_lapse, lapse, shift, gr::spacetime_normal_vector(lapse, shift),\n      cache_or_compute<gh::Tags::Phi<DataVector, 3>>(output_l_max, time),\n      cache_or_compute<gh::Tags::Pi<DataVector, 3>>(output_l_max, time));\n}\n\nvoid WorldtubeData::variables_impl(\n    const gsl::not_null<Scalar<DataVector>*> dr_lapse,\n    const size_t output_l_max, const double time,\n    tmpl::type_<Tags::Dr<gr::Tags::Lapse<DataVector>>> /*meta*/) const {\n  const auto& lapse =\n      cache_or_compute<gr::Tags::Lapse<DataVector>>(output_l_max, time);\n  const auto& shift =\n      cache_or_compute<gr::Tags::Shift<DataVector, 3>>(output_l_max, time);\n  const auto d_lapse = gh::spatial_deriv_of_lapse(\n      lapse, gr::spacetime_normal_vector(lapse, shift),\n      cache_or_compute<gh::Tags::Phi<DataVector, 3>>(output_l_max, time));\n  const auto& dr_cartesian_coordinates =\n      cache_or_compute<Tags::Dr<Tags::CauchyCartesianCoords>>(output_l_max,\n                                                              time);\n\n  get(*dr_lapse) = get<0>(dr_cartesian_coordinates) * get<0>(d_lapse) +\n                   get<1>(dr_cartesian_coordinates) * get<1>(d_lapse) +\n                   get<2>(dr_cartesian_coordinates) * get<2>(d_lapse);\n}\n\nvoid WorldtubeData::pup(PUP::er& p) {\n  p | extraction_radius_;\n  if (p.isUnpacking()) {\n    intermediate_cache_ = IntermediateCacheTuple{};\n  }\n}\n\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n/// Analytic solutions for CCE worldtube data and corresponding waveform News\nnamespace Solutions {\n\n/// \\cond\nclass BouncingBlackHole;\nclass GaugeWave;\nclass LinearizedBondiSachs;\nclass RobinsonTrautman;\nclass RotatingSchwarzschild;\nclass TeukolskyWave;\n/// \\endcond\n\n/*!\n * \\brief Abstract base class for analytic worldtube data for verifying the CCE\n * system.\n *\n * \\details All of the boundary data quantities are provided by the\n * `WorldtubeData::variables()` function.\n *\n * This class provides caching and conversion between different\n * representations of the metric data needed for the worldtube computation and\n * evolution. The set of pure virtual functions (required to be overriden in the\n * derived classes) is:\n * - `WorldtubeData::get_clone()`: should return a\n * `std::unique_ptr<WorldtubeData>` with cloned state\n * - `WorldtubeData::variables_impl()` (a `protected` function): should compute\n * and return by `not_null` pointer the spacetime metric quantity requested in\n * the final (metavariable) tag argument. The function overloads that are\n * required to be overriden in the derived class are\n * `gr::Tags::SpacetimeMetric<DataVector, 3>`,\n * `::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>`,\n * `gh::Tags::Phi<DataVector, 3>`, and\n * `Cce::Tags::News`.\n * - `prepare_solution()`: Any initial precomputation needed to determine all of\n * the solutions efficiently. This function is called by the base class prior to\n * computing or retrieving from the internal cache the requested quantities.\n *\n * \\warning This class is not intended to be threadsafe! Therefore, using\n * instances of this class placed into the const global cache results in\n * undefined behavior. The analytic data for CCE is not easily represented as a\n * full closed-form solution for the entire Bondi-Sachs-like metric over the\n * domain, so this class and its descendants perform numerical calculations such\n * as spin-weighted derivatives over the sphere. Instead, it makes best sense to\n * compute the global solution over the extraction sphere, and cache\n * intermediate steps to avoid repeating potentially expensive tensor\n * calculations.\n */\nstruct WorldtubeData : public PUP::able {\n  using creatable_classes =\n      tmpl::list<BouncingBlackHole, GaugeWave, LinearizedBondiSachs,\n                 RobinsonTrautman, RotatingSchwarzschild, TeukolskyWave>;\n\n  /// The set of available tags provided by the analytic solution\n  using tags = tmpl::list<\n      Tags::CauchyCartesianCoords, Tags::Dr<Tags::CauchyCartesianCoords>,\n      gr::Tags::SpacetimeMetric<DataVector, 3>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n      gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>,\n      Tags::Dr<gr::Tags::SpatialMetric<DataVector, 3>>,\n      gr::Tags::Shift<DataVector, 3>,\n      ::Tags::dt<gr::Tags::Shift<DataVector, 3>>,\n      Tags::Dr<gr::Tags::Shift<DataVector, 3>>, gr::Tags::Lapse<DataVector>,\n      ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n      Tags::Dr<gr::Tags::Lapse<DataVector>>, Tags::News>;\n\n  WRAPPED_PUPable_abstract(WorldtubeData);  // NOLINT\n\n  // clang doesn't manage to use = default correctly in this case\n  // NOLINTNEXTLINE(modernize-use-equals-default)\n  WorldtubeData() {}\n\n  explicit WorldtubeData(const double extraction_radius)\n      : extraction_radius_{extraction_radius} {}\n\n  explicit WorldtubeData(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  ~WorldtubeData() override = default;\n\n  virtual std::unique_ptr<WorldtubeData> get_clone() const = 0;\n\n  /*!\n   * \\brief Retrieve worldtube data represented by the analytic solution, at\n   * boundary angular resolution `l_max` and time `time`\n   *\n   * \\details The set of requested tags are specified by the final argument,\n   * which must be a `tmpl::list` of tags to be retrieved. The set of available\n   * tags is found in `WorldtubeData::tags`, and includes coordinate and\n   * Jacobian quantities as well as metric quantities and derivatives thereof.\n   */\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const size_t output_l_max, const double time,\n      tmpl::list<Tags...> /*meta*/) const {\n    prepare_solution(output_l_max, time);\n    return {cache_or_compute<Tags>(output_l_max, time)...};\n  }\n\n  void pup(PUP::er& p) override;\n\n  virtual std::unique_ptr<Cce::InitializeJ::InitializeJ<false>>\n  get_initialize_j(const double /*start_time*/) const {\n    return std::make_unique<Cce::InitializeJ::InverseCubic<false>>();\n  };\n\n  virtual bool use_noninertial_news() const { return false; }\n\n protected:\n  template <typename Tag>\n  const auto& cache_or_compute(const size_t output_l_max,\n                               const double time) const {\n    auto& item_cache = get<IntermediateCacheTag<Tag>>(intermediate_cache_);\n    if (item_cache.l_max == output_l_max and item_cache.time == time) {\n      return item_cache.data;\n    }\n    auto& item = item_cache.data;\n    set_number_of_grid_points(\n        make_not_null(&item),\n        Spectral::Swsh::number_of_swsh_collocation_points(output_l_max));\n    variables_impl(make_not_null(&item), output_l_max, time,\n                   tmpl::type_<Tag>{});\n    item_cache.l_max = output_l_max;\n    item_cache.time = time;\n    return item;\n  }\n  virtual void prepare_solution(size_t output_l_max, double time) const = 0;\n\n  // note that function template cannot be virtual, so we have to emulate\n  // template specializations through function overloads\n  virtual void variables_impl(\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_coordinates,\n      size_t output_l_max, double time,\n      tmpl::type_<Tags::CauchyCartesianCoords> /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::i<DataVector, 3>*> dr_cartesian_coordinates,\n      size_t output_l_max, double time,\n      tmpl::type_<Tags::Dr<Tags::CauchyCartesianCoords>> /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n      size_t output_l_max, double time,\n      tmpl::type_<gr::Tags::SpacetimeMetric<DataVector, 3>>\n      /*meta*/) const = 0;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::aa<DataVector, 3>*> dt_spacetime_metric,\n      size_t output_l_max, double time,\n      tmpl::type_<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>\n      /*meta*/) const = 0;\n\n  virtual void variables_impl(gsl::not_null<tnsr::aa<DataVector, 3>*> pi,\n                              size_t output_l_max, double time,\n                              tmpl::type_<gh::Tags::Pi<DataVector, 3>>\n                              /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::iaa<DataVector, 3>*> d_spacetime_metric,\n      size_t output_l_max, double time,\n      tmpl::type_<gh::Tags::Phi<DataVector, 3>>\n      /*meta*/) const = 0;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::ii<DataVector, 3>*> spatial_metric,\n      size_t output_l_max, double time,\n      tmpl::type_<gr::Tags::SpatialMetric<DataVector, 3>>\n      /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::ii<DataVector, 3>*> dt_spatial_metric,\n      size_t output_l_max, double time,\n      tmpl::type_<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>\n      /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::ii<DataVector, 3>*> dr_spatial_metric,\n      size_t output_l_max, double time,\n      tmpl::type_<Tags::Dr<gr::Tags::SpatialMetric<DataVector, 3>>>\n      /*meta*/) const;\n\n  virtual void variables_impl(gsl::not_null<tnsr::I<DataVector, 3>*> shift,\n                              size_t output_l_max, double time,\n                              tmpl::type_<gr::Tags::Shift<DataVector, 3>>\n                              /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::I<DataVector, 3>*> dt_shift, size_t output_l_max,\n      double time, tmpl::type_<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>\n      /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<tnsr::I<DataVector, 3>*> dr_shift, size_t output_l_max,\n      double time, tmpl::type_<Tags::Dr<gr::Tags::Shift<DataVector, 3>>>\n      /*meta*/) const;\n\n  virtual void variables_impl(gsl::not_null<Scalar<DataVector>*> lapse,\n                              size_t output_l_max, double time,\n                              tmpl::type_<gr::Tags::Lapse<DataVector>>\n                              /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<Scalar<DataVector>*> dt_lapse, size_t output_l_max,\n      double time, tmpl::type_<::Tags::dt<gr::Tags::Lapse<DataVector>>>\n      /*meta*/) const;\n\n  virtual void variables_impl(gsl::not_null<Scalar<DataVector>*> dr_lapse,\n                              size_t output_l_max, double time,\n                              tmpl::type_<Tags::Dr<gr::Tags::Lapse<DataVector>>>\n                              /*meta*/) const;\n\n  virtual void variables_impl(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n      size_t output_l_max, double time,\n      tmpl::type_<Tags::News> /*meta*/) const = 0;\n\n  template <typename Tag>\n  struct IntermediateCache {\n    typename Tag::type data;\n    size_t l_max = 0;\n    double time = -std::numeric_limits<double>::infinity();\n  };\n\n  template <typename Tag>\n  struct IntermediateCacheTag {\n    using type = IntermediateCache<Tag>;\n  };\n\n  using IntermediateCacheTuple =\n      tuples::tagged_tuple_from_typelist<tmpl::transform<\n          tmpl::list<Tags::CauchyCartesianCoords,\n                     Tags::Dr<Tags::CauchyCartesianCoords>,\n                     gr::Tags::SpacetimeMetric<DataVector, 3>,\n                     gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                     gr::Tags::SpatialMetric<DataVector, 3>,\n                     gr::Tags::Shift<DataVector, 3>,\n                     gr::Tags::Lapse<DataVector>,\n                     ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                     ::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>,\n                     ::Tags::dt<gr::Tags::Shift<DataVector, 3>>,\n                     ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n                     Tags::Dr<gr::Tags::SpatialMetric<DataVector, 3>>,\n                     Tags::Dr<gr::Tags::Shift<DataVector, 3>>,\n                     Tags::Dr<gr::Tags::Lapse<DataVector>>, Tags::News>,\n          tmpl::bind<IntermediateCacheTag, tmpl::_1>>>;\n\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable IntermediateCacheTuple intermediate_cache_;\n  double extraction_radius_ = std::numeric_limits<double>::quiet_NaN();\n};\n}  // namespace Solutions\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/BoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryDataTags.hpp\"\n#include \"Evolution/Systems/Cce/SpecBoundaryData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nvoid trigonometric_functions_on_swsh_collocation(\n    const gsl::not_null<Scalar<DataVector>*> cos_phi,\n    const gsl::not_null<Scalar<DataVector>*> cos_theta,\n    const gsl::not_null<Scalar<DataVector>*> sin_phi,\n    const gsl::not_null<Scalar<DataVector>*> sin_theta, const size_t l_max) {\n  const size_t size = Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  set_number_of_grid_points(cos_phi, size);\n  set_number_of_grid_points(cos_theta, size);\n  set_number_of_grid_points(sin_phi, size);\n  set_number_of_grid_points(sin_theta, size);\n\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get(*sin_theta)[collocation_point.offset] = sin(collocation_point.theta);\n    get(*cos_theta)[collocation_point.offset] = cos(collocation_point.theta);\n    get(*sin_phi)[collocation_point.offset] = sin(collocation_point.phi);\n    get(*cos_phi)[collocation_point.offset] = cos(collocation_point.phi);\n  }\n}\n\nvoid cartesian_to_spherical_coordinates_and_jacobians(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> unit_cartesian_coords,\n    const gsl::not_null<SphericaliCartesianJ*> cartesian_to_spherical_jacobian,\n    const gsl::not_null<CartesianiSphericalJ*>\n        inverse_cartesian_to_spherical_jacobian,\n    const Scalar<DataVector>& cos_phi, const Scalar<DataVector>& cos_theta,\n    const Scalar<DataVector>& sin_phi, const Scalar<DataVector>& sin_theta,\n    const double extraction_radius) {\n  const size_t size = get(cos_phi).size();\n  set_number_of_grid_points(unit_cartesian_coords, size);\n  set_number_of_grid_points(cartesian_to_spherical_jacobian, size);\n  set_number_of_grid_points(inverse_cartesian_to_spherical_jacobian, size);\n\n  // note: factor of r scaled out\n  get<0>(*unit_cartesian_coords) = get(sin_theta) * get(cos_phi);\n  get<1>(*unit_cartesian_coords) = get(sin_theta) * get(sin_phi);\n  get<2>(*unit_cartesian_coords) = get(cos_theta);\n\n  // dx/dr   dy/dr  dz/dr\n  get<0, 0>(*cartesian_to_spherical_jacobian) = get(sin_theta) * get(cos_phi);\n  get<0, 1>(*cartesian_to_spherical_jacobian) = get(sin_theta) * get(sin_phi);\n  get<0, 2>(*cartesian_to_spherical_jacobian) = get(cos_theta);\n  // dx/dtheta   dy/dtheta  dz/dtheta\n  get<1, 0>(*cartesian_to_spherical_jacobian) =\n      extraction_radius * get(cos_theta) * get(cos_phi);\n  get<1, 1>(*cartesian_to_spherical_jacobian) =\n      extraction_radius * get(cos_theta) * get(sin_phi);\n  get<1, 2>(*cartesian_to_spherical_jacobian) =\n      -extraction_radius * get(sin_theta);\n  // (1/sin(theta)) { dx/dphi,   dy/dphi,  dz/dphi }\n  get<2, 0>(*cartesian_to_spherical_jacobian) =\n      -extraction_radius * get(sin_phi);\n  get<2, 1>(*cartesian_to_spherical_jacobian) =\n      extraction_radius * get(cos_phi);\n  get<2, 2>(*cartesian_to_spherical_jacobian) = 0.0;\n\n  // dr/dx   dtheta/dx   dphi/dx * sin(theta)\n  get<0, 0>(*inverse_cartesian_to_spherical_jacobian) =\n      get(cos_phi) * get(sin_theta);\n  get<0, 1>(*inverse_cartesian_to_spherical_jacobian) =\n      get(cos_phi) * get(cos_theta) / extraction_radius;\n  get<0, 2>(*inverse_cartesian_to_spherical_jacobian) =\n      -get(sin_phi) / (extraction_radius);\n  // dr/dy   dtheta/dy   dphi/dy * sin(theta)\n  get<1, 0>(*inverse_cartesian_to_spherical_jacobian) =\n      get(sin_phi) * get(sin_theta);\n  get<1, 1>(*inverse_cartesian_to_spherical_jacobian) =\n      get(cos_theta) * get(sin_phi) / extraction_radius;\n  get<1, 2>(*inverse_cartesian_to_spherical_jacobian) =\n      get(cos_phi) / (extraction_radius);\n  // dr/dz   dtheta/dz   dphi/dz * sin(theta)\n  get<2, 0>(*inverse_cartesian_to_spherical_jacobian) = get(cos_theta);\n  get<2, 1>(*inverse_cartesian_to_spherical_jacobian) =\n      -get(sin_theta) / extraction_radius;\n  get<2, 2>(*inverse_cartesian_to_spherical_jacobian) = 0.0;\n}\n\nvoid cartesian_spatial_metric_and_derivatives_from_modes(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> cartesian_spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, 3>*>\n        inverse_cartesian_spatial_metric,\n    const gsl::not_null<tnsr::ijj<DataVector, 3>*> d_cartesian_spatial_metric,\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> dt_cartesian_spatial_metric,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const tnsr::ii<ComplexModalVector, 3>& spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dr_spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dt_spatial_metric_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const size_t l_max) {\n  const size_t size = get<0, 0>(inverse_cartesian_to_spherical_jacobian).size();\n  set_number_of_grid_points(cartesian_spatial_metric, size);\n  set_number_of_grid_points(d_cartesian_spatial_metric, size);\n  set_number_of_grid_points(dt_cartesian_spatial_metric, size);\n\n  set_number_of_grid_points(interpolation_buffer, size);\n  set_number_of_grid_points(interpolation_modal_buffer, size);\n  set_number_of_grid_points(eth_buffer, size);\n\n  // Allocation\n  SphericaliCartesianjj spherical_d_cartesian_spatial_metric{size};\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      // copy the modes to a spin-weighted type for interpolation\n      get(*interpolation_modal_buffer).data() =\n          spatial_metric_coefficients.get(i, j);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&get(*interpolation_buffer)),\n          get(*interpolation_modal_buffer));\n      cartesian_spatial_metric->get(i, j) =\n          real(get(*interpolation_buffer).data());\n\n      get(*interpolation_modal_buffer).data() =\n          dt_spatial_metric_coefficients.get(i, j);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&get(*interpolation_buffer)),\n          get(*interpolation_modal_buffer));\n      dt_cartesian_spatial_metric->get(i, j) =\n          real(get(*interpolation_buffer).data());\n\n      get(*interpolation_modal_buffer).data() =\n          dr_spatial_metric_coefficients.get(i, j);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&get(*interpolation_buffer)),\n          get(*interpolation_modal_buffer));\n      spherical_d_cartesian_spatial_metric.get(0, i, j) =\n          real(get(*interpolation_buffer).data());\n    }\n  }\n\n  *inverse_cartesian_spatial_metric =\n      determinant_and_inverse(*cartesian_spatial_metric).second;\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      // reusing the interpolation buffer for taking the angular derivatives\n      get(*interpolation_buffer) =\n          std::complex<double>(1.0, 0.0) * cartesian_spatial_metric->get(i, j);\n      Spectral::Swsh::angular_derivatives<\n          tmpl::list<Spectral::Swsh::Tags::Eth>>(\n          l_max, 1, make_not_null(&get(*eth_buffer)),\n          get(*interpolation_buffer));\n      spherical_d_cartesian_spatial_metric.get(1, i, j) =\n          -real(get(*eth_buffer).data());\n      spherical_d_cartesian_spatial_metric.get(2, i, j) =\n          -imag(get(*eth_buffer).data());\n    }\n  }\n\n  // convert derivatives to cartesian form\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        d_cartesian_spatial_metric->get(k, i, j) =\n            inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n            spherical_d_cartesian_spatial_metric.get(0, i, j);\n        for (size_t A = 0; A < 2; ++A) {\n          d_cartesian_spatial_metric->get(k, i, j) +=\n              inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n              spherical_d_cartesian_spatial_metric.get(A + 1, i, j);\n        }\n      }\n    }\n  }\n}\n\nvoid cartesian_shift_and_derivatives_from_modes(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> cartesian_shift,\n    const gsl::not_null<tnsr::iJ<DataVector, 3>*> d_cartesian_shift,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> dt_cartesian_shift,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const tnsr::I<ComplexModalVector, 3>& shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dr_shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dt_shift_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const size_t l_max) {\n  const size_t size = get<0, 0>(inverse_cartesian_to_spherical_jacobian).size();\n  set_number_of_grid_points(cartesian_shift, size);\n  set_number_of_grid_points(d_cartesian_shift, size);\n  set_number_of_grid_points(dt_cartesian_shift, size);\n\n  set_number_of_grid_points(interpolation_buffer, size);\n  set_number_of_grid_points(interpolation_modal_buffer, size);\n  set_number_of_grid_points(eth_buffer, size);\n\n  // Allocation\n  SphericaliCartesianJ spherical_d_cartesian_shift{size};\n\n  for (size_t i = 0; i < 3; ++i) {\n    // copy the modes to a spin-weighted type for interpolation\n    get(*interpolation_modal_buffer).data() = shift_coefficients.get(i);\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max, 1, make_not_null(&get(*interpolation_buffer)),\n        get(*interpolation_modal_buffer));\n    cartesian_shift->get(i) = real(get(*interpolation_buffer).data());\n\n    get(*interpolation_modal_buffer).data() = dt_shift_coefficients.get(i);\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max, 1, make_not_null(&get(*interpolation_buffer)),\n        get(*interpolation_modal_buffer));\n    dt_cartesian_shift->get(i) = real(get(*interpolation_buffer).data());\n\n    get(*interpolation_modal_buffer).data() = dr_shift_coefficients.get(i);\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max, 1, make_not_null(&get(*interpolation_buffer)),\n        get(*interpolation_modal_buffer));\n    spherical_d_cartesian_shift.get(0, i) =\n        real(get(*interpolation_buffer).data());\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    // reusing the interpolation buffer for taking the angular derivatives\n    get(*interpolation_buffer) =\n        std::complex<double>(1.0, 0.0) * cartesian_shift->get(i);\n    Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n        l_max, 1, make_not_null(&get(*eth_buffer)), get(*interpolation_buffer));\n    spherical_d_cartesian_shift.get(1, i) = -real(get(*eth_buffer).data());\n    spherical_d_cartesian_shift.get(2, i) = -imag(get(*eth_buffer).data());\n  }\n\n  // convert derivatives to cartesian form\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t k = 0; k < 3; ++k) {\n      d_cartesian_shift->get(k, i) =\n          inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n          spherical_d_cartesian_shift.get(0, i);\n      for (size_t A = 0; A < 2; ++A) {\n        d_cartesian_shift->get(k, i) +=\n            inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n            spherical_d_cartesian_shift.get(A + 1, i);\n      }\n    }\n  }\n}\n\nvoid cartesian_lapse_and_derivatives_from_modes(\n    const gsl::not_null<Scalar<DataVector>*> cartesian_lapse,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> d_cartesian_lapse,\n    const gsl::not_null<Scalar<DataVector>*> dt_cartesian_lapse,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const Scalar<ComplexModalVector>& lapse_coefficients,\n    const Scalar<ComplexModalVector>& dr_lapse_coefficients,\n    const Scalar<ComplexModalVector>& dt_lapse_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const size_t l_max) {\n  const size_t size = get<0, 0>(inverse_cartesian_to_spherical_jacobian).size();\n  set_number_of_grid_points(cartesian_lapse, size);\n  set_number_of_grid_points(d_cartesian_lapse, size);\n  set_number_of_grid_points(dt_cartesian_lapse, size);\n\n  set_number_of_grid_points(interpolation_buffer, size);\n  set_number_of_grid_points(interpolation_modal_buffer, size);\n  set_number_of_grid_points(eth_buffer, size);\n\n  // Allocation\n  tnsr::i<DataVector, 3> spherical_d_cartesian_lapse{size};\n  // copy the modes to a spin-weighted type for interpolation\n  get(*interpolation_modal_buffer).data() = get(lapse_coefficients);\n  Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, make_not_null(&get(*interpolation_buffer)),\n      get(*interpolation_modal_buffer));\n  get(*cartesian_lapse) = real(get(*interpolation_buffer).data());\n\n  get(*interpolation_modal_buffer).data() = get(dt_lapse_coefficients);\n  Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, make_not_null(&get(*interpolation_buffer)),\n      get(*interpolation_modal_buffer));\n  get(*dt_cartesian_lapse) = real(get(*interpolation_buffer).data());\n\n  get(*interpolation_modal_buffer).data() = get(dr_lapse_coefficients);\n  Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, make_not_null(&get(*interpolation_buffer)),\n      get(*interpolation_modal_buffer));\n  get<0>(spherical_d_cartesian_lapse) = real(get(*interpolation_buffer).data());\n\n  // reusing the interpolation buffer for taking the angular derivatives\n  get(*interpolation_buffer) =\n      std::complex<double>(1.0, 0.0) * get(*cartesian_lapse);\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&get(*eth_buffer)), get(*interpolation_buffer));\n  spherical_d_cartesian_lapse.get(1) = -real(get(*eth_buffer).data());\n  spherical_d_cartesian_lapse.get(2) = -imag(get(*eth_buffer).data());\n\n  // convert derivatives to cartesian form\n  for (size_t k = 0; k < 3; ++k) {\n    d_cartesian_lapse->get(k) =\n        inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n        get<0>(spherical_d_cartesian_lapse);\n    for (size_t A = 0; A < 2; ++A) {\n      d_cartesian_lapse->get(k) +=\n          inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n          spherical_d_cartesian_lapse.get(A + 1);\n    }\n  }\n}\n\nvoid deriv_cartesian_metric_lapse_shift_from_nodes(\n    const gsl::not_null<tnsr::ijj<DataVector, 3>*> d_cartesian_spatial_metric,\n    const gsl::not_null<tnsr::iJ<DataVector, 3>*> d_cartesian_shift,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> d_cartesian_lapse,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const tnsr::ii<DataVector, 3>& cartesian_spatial_metric,\n    const tnsr::ii<DataVector, 3>& dr_cartesian_spatial_metric,\n    const tnsr::I<DataVector, 3>& cartesian_shift,\n    const tnsr::I<DataVector, 3>& dr_cartesian_shift,\n    const Scalar<DataVector>& cartesian_lapse,\n    const Scalar<DataVector>& dr_cartesian_lapse,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const size_t l_max) {\n  const size_t size = get<0, 0>(inverse_cartesian_to_spherical_jacobian).size();\n  set_number_of_grid_points(buffer, size);\n  set_number_of_grid_points(eth_buffer, size);\n\n  set_number_of_grid_points(d_cartesian_spatial_metric, size);\n  set_number_of_grid_points(d_cartesian_shift, size);\n  set_number_of_grid_points(d_cartesian_lapse, size);\n\n  // Allocations\n  SphericaliCartesianjj spherical_d_cartesian_spatial_metric{size};\n  SphericaliCartesianJ spherical_d_cartesian_shift{size};\n  tnsr::i<DataVector, 3> spherical_d_cartesian_lapse{size};\n\n  // Radial derivative is just a copy\n  get<0>(spherical_d_cartesian_lapse) = get(dr_cartesian_lapse);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      spherical_d_cartesian_spatial_metric.get(0, i, j) =\n          dr_cartesian_spatial_metric.get(i, j);\n    }\n    spherical_d_cartesian_shift.get(0, i) = dr_cartesian_shift.get(i);\n  }\n\n  // Compute angular derivatives\n  get(*buffer) = std::complex<double>(1.0, 0.0) * get(cartesian_lapse);\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&get(*eth_buffer)), get(*buffer));\n  get<1>(spherical_d_cartesian_lapse) = -real(get(*eth_buffer).data());\n  get<2>(spherical_d_cartesian_lapse) = -imag(get(*eth_buffer).data());\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      get(*buffer) =\n          std::complex<double>(1.0, 0.0) * cartesian_spatial_metric.get(i, j);\n      Spectral::Swsh::angular_derivatives<\n          tmpl::list<Spectral::Swsh::Tags::Eth>>(\n          l_max, 1, make_not_null(&get(*eth_buffer)), get(*buffer));\n      spherical_d_cartesian_spatial_metric.get(1, i, j) =\n          -real(get(*eth_buffer).data());\n      spherical_d_cartesian_spatial_metric.get(2, i, j) =\n          -imag(get(*eth_buffer).data());\n    }\n\n    get(*buffer) = std::complex<double>(1.0, 0.0) * cartesian_shift.get(i);\n    Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n        l_max, 1, make_not_null(&get(*eth_buffer)), get(*buffer));\n    spherical_d_cartesian_shift.get(1, i) = -real(get(*eth_buffer).data());\n    spherical_d_cartesian_shift.get(2, i) = -imag(get(*eth_buffer).data());\n  }\n\n  // Convert derivatives to cartesian form\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        d_cartesian_spatial_metric->get(k, i, j) =\n            inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n            spherical_d_cartesian_spatial_metric.get(0, i, j);\n        for (size_t A = 0; A < 2; ++A) {\n          d_cartesian_spatial_metric->get(k, i, j) +=\n              inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n              spherical_d_cartesian_spatial_metric.get(A + 1, i, j);\n        }\n      }\n\n      d_cartesian_shift->get(k, i) =\n          inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n          spherical_d_cartesian_shift.get(0, i);\n      for (size_t A = 0; A < 2; ++A) {\n        d_cartesian_shift->get(k, i) +=\n            inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n            spherical_d_cartesian_shift.get(A + 1, i);\n      }\n    }\n\n    d_cartesian_lapse->get(k) =\n        inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n        get<0>(spherical_d_cartesian_lapse);\n    for (size_t A = 0; A < 2; ++A) {\n      d_cartesian_lapse->get(k) +=\n          inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n          spherical_d_cartesian_lapse.get(A + 1);\n    }\n  }\n}\n\nvoid null_metric_and_derivative(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::RadialNull>*>\n        du_null_metric,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::RadialNull>*>\n        null_metric,\n    const SphericaliCartesianJ& cartesian_to_spherical_jacobian,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::aa<DataVector, 3>& spacetime_metric) {\n  const size_t size = get<0, 0>(spacetime_metric).size();\n  set_number_of_grid_points(null_metric, size);\n  set_number_of_grid_points(du_null_metric, size);\n\n  get<0, 0>(*null_metric) = get<0, 0>(spacetime_metric);\n  get<0, 0>(*du_null_metric) = get<0, 0>(dt_spacetime_metric);\n\n  get<0, 1>(*null_metric) = -1.0;\n  get<0, 1>(*du_null_metric) = 0.0;\n\n  for (size_t i = 0; i < 3; ++i) {\n    null_metric->get(1, i + 1) = 0.0;\n    du_null_metric->get(1, i + 1) = 0.0;\n  }\n\n  for (size_t A = 0; A < 2; ++A) {\n    null_metric->get(0, A + 2) = cartesian_to_spherical_jacobian.get(A + 1, 0) *\n                                 spacetime_metric.get(0, 1);\n    du_null_metric->get(0, A + 2) =\n        cartesian_to_spherical_jacobian.get(A + 1, 0) *\n        dt_spacetime_metric.get(0, 1);\n    for (size_t i = 1; i < 3; ++i) {\n      null_metric->get(0, A + 2) +=\n          cartesian_to_spherical_jacobian.get(A + 1, i) *\n          spacetime_metric.get(0, i + 1);\n      du_null_metric->get(0, A + 2) +=\n          cartesian_to_spherical_jacobian.get(A + 1, i) *\n          dt_spacetime_metric.get(0, i + 1);\n    }\n  }\n\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = A; B < 2; ++B) {\n      null_metric->get(A + 2, B + 2) =\n          cartesian_to_spherical_jacobian.get(A + 1, 0) *\n          cartesian_to_spherical_jacobian.get(B + 1, 0) *\n          spacetime_metric.get(1, 1);\n      du_null_metric->get(A + 2, B + 2) =\n          cartesian_to_spherical_jacobian.get(A + 1, 0) *\n          cartesian_to_spherical_jacobian.get(B + 1, 0) *\n          dt_spacetime_metric.get(1, 1);\n\n      for (size_t i = 1; i < 3; ++i) {\n        null_metric->get(A + 2, B + 2) +=\n            cartesian_to_spherical_jacobian.get(A + 1, i) *\n            cartesian_to_spherical_jacobian.get(B + 1, i) *\n            spacetime_metric.get(i + 1, i + 1);\n        du_null_metric->get(A + 2, B + 2) +=\n            cartesian_to_spherical_jacobian.get(A + 1, i) *\n            cartesian_to_spherical_jacobian.get(B + 1, i) *\n            dt_spacetime_metric.get(i + 1, i + 1);\n      }\n\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = i + 1; j < 3; ++j) {\n          // the off-diagonal pieces must be explicitly symmetrized\n          null_metric->get(A + 2, B + 2) +=\n              (cartesian_to_spherical_jacobian.get(A + 1, i) *\n                   cartesian_to_spherical_jacobian.get(B + 1, j) +\n               cartesian_to_spherical_jacobian.get(A + 1, j) *\n                   cartesian_to_spherical_jacobian.get(B + 1, i)) *\n              spacetime_metric.get(i + 1, j + 1);\n          du_null_metric->get(A + 2, B + 2) +=\n              (cartesian_to_spherical_jacobian.get(A + 1, i) *\n                   cartesian_to_spherical_jacobian.get(B + 1, j) +\n               cartesian_to_spherical_jacobian.get(A + 1, j) *\n                   cartesian_to_spherical_jacobian.get(B + 1, i)) *\n              dt_spacetime_metric.get(i + 1, j + 1);\n        }\n      }\n    }\n  }\n\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b < a; ++b) {\n      null_metric->get(a, b) = null_metric->get(b, a);\n      du_null_metric->get(a, b) = du_null_metric->get(b, a);\n    }\n  }\n}\n\nvoid worldtube_normal_and_derivatives(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> worldtube_normal,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> dt_worldtube_normal,\n    const Scalar<DataVector>& cos_phi, const Scalar<DataVector>& cos_theta,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const Scalar<DataVector>& sin_phi, const Scalar<DataVector>& sin_theta,\n    const tnsr::II<DataVector, 3>& inverse_spatial_metric) {\n  const size_t size = get<0, 0>(spacetime_metric).size();\n\n  // Allocation\n  Variables<tmpl::list<::Tags::Tempi<0, 3>, ::Tags::TempScalar<1>>>\n      aggregated_buffers{size};\n  tnsr::i<DataVector, 3>& sigma = get<::Tags::Tempi<0, 3>>(aggregated_buffers);\n  get<0>(sigma) = get(cos_phi) * square(get(sin_theta));\n  get<1>(sigma) = get(sin_phi) * square(get(sin_theta));\n  get<2>(sigma) = get(sin_theta) * get(cos_theta);\n\n  // Allocation\n  magnitude(make_not_null(&get<::Tags::TempScalar<1>>(aggregated_buffers)),\n            sigma, inverse_spatial_metric);\n  const DataVector& norm_of_sigma =\n      get(get<::Tags::TempScalar<1>>(aggregated_buffers));\n\n  get<0>(sigma) /= norm_of_sigma;\n  get<1>(sigma) /= norm_of_sigma;\n  get<2>(sigma) /= norm_of_sigma;\n\n  for (size_t i = 0; i < 3; ++i) {\n    worldtube_normal->get(i) = inverse_spatial_metric.get(i, 0) * sigma.get(0);\n    for (size_t j = 1; j < 3; ++j) {\n      worldtube_normal->get(i) +=\n          inverse_spatial_metric.get(i, j) * sigma.get(j);\n    }\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t m = 0; m < 3; ++m) {\n      for (size_t n = 0; n < 3; ++n) {\n        if (UNLIKELY(m == 0 and n == 0)) {\n          dt_worldtube_normal->get(i) =\n              (0.5 * worldtube_normal->get(i) * get<0>(*worldtube_normal) -\n               inverse_spatial_metric.get(i, 0)) *\n              get<0>(*worldtube_normal) * get<1, 1>(dt_spacetime_metric);\n        } else {\n          dt_worldtube_normal->get(i) +=\n              (0.5 * worldtube_normal->get(i) * worldtube_normal->get(m) -\n               inverse_spatial_metric.get(i, m)) *\n              worldtube_normal->get(n) * dt_spacetime_metric.get(m + 1, n + 1);\n        }\n      }\n    }\n  }\n}\n\nvoid null_vector_l_and_derivatives(\n    const gsl::not_null<tnsr::A<DataVector, 3>*> du_null_l,\n    const gsl::not_null<tnsr::A<DataVector, 3>*> null_l,\n    const tnsr::I<DataVector, 3>& dt_worldtube_normal,\n    const Scalar<DataVector>& dt_lapse,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::I<DataVector, 3>& dt_shift, const Scalar<DataVector>& lapse,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::I<DataVector, 3>& shift,\n    const tnsr::I<DataVector, 3>& worldtube_normal) {\n  const size_t size = get(lapse).size();\n  CAPTURE_FOR_ERROR(lapse);\n  CAPTURE_FOR_ERROR(dt_lapse);\n  CAPTURE_FOR_ERROR(shift);\n  CAPTURE_FOR_ERROR(dt_shift);\n\n  // Allocation\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>>>\n      aggregated_buffer{size};\n  DataVector& denominator = get(get<::Tags::TempScalar<0>>(aggregated_buffer));\n  DataVector& du_denominator =\n      get(get<::Tags::TempScalar<1>>(aggregated_buffer));\n  DataVector& one_divided_by_lapse =\n      get(get<::Tags::TempScalar<2>>(aggregated_buffer));\n  one_divided_by_lapse = 1.0 / get(lapse);\n  denominator = get(lapse);\n  for (size_t i = 0; i < 3; ++i) {\n    // off-diagonal\n    for (size_t j = i + 1; j < 3; ++j) {\n      denominator -= spacetime_metric.get(i + 1, j + 1) *\n                     (shift.get(i) * worldtube_normal.get(j) +\n                      shift.get(j) * worldtube_normal.get(i));\n    }\n    // diagonal\n    denominator -= spacetime_metric.get(i + 1, i + 1) * shift.get(i) *\n                   worldtube_normal.get(i);\n  }\n  CAPTURE_FOR_ERROR(denominator);\n  // buffer re-use because we won't need the uninverted denominator after this.\n  DataVector& one_divided_by_denominator =\n      get(get<::Tags::TempScalar<0>>(aggregated_buffer));\n  one_divided_by_denominator = 1.0 / denominator;\n  get<0>(*null_l) = one_divided_by_denominator * one_divided_by_lapse;\n  for (size_t i = 0; i < 3; ++i) {\n    null_l->get(i + 1) =\n        (worldtube_normal.get(i) - shift.get(i) * one_divided_by_lapse) *\n        one_divided_by_denominator;\n  }\n\n  du_denominator = -get(dt_lapse);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i + 1; j < 3; ++j) {\n      // symmetry\n      du_denominator += (dt_shift.get(i) * worldtube_normal.get(j) +\n                         dt_shift.get(j) * worldtube_normal.get(i)) *\n                            spacetime_metric.get(i + 1, j + 1) +\n                        (shift.get(i) * worldtube_normal.get(j) +\n                         shift.get(j) * worldtube_normal.get(i)) *\n                            dt_spacetime_metric.get(i + 1, j + 1) +\n                        (shift.get(i) * dt_worldtube_normal.get(j) +\n                         shift.get(j) * dt_worldtube_normal.get(i)) *\n                            spacetime_metric.get(i + 1, j + 1);\n    }\n    // diagonal\n    du_denominator += dt_shift.get(i) * spacetime_metric.get(i + 1, i + 1) *\n                          worldtube_normal.get(i) +\n                      shift.get(i) * dt_spacetime_metric.get(i + 1, i + 1) *\n                          worldtube_normal.get(i) +\n                      shift.get(i) * spacetime_metric.get(i + 1, i + 1) *\n                          dt_worldtube_normal.get(i);\n  }\n  du_denominator *= square(one_divided_by_denominator);\n\n  get<0>(*du_null_l) = (du_denominator - get(dt_lapse) * one_divided_by_lapse *\n                                             one_divided_by_denominator) *\n                       one_divided_by_lapse;\n  for (size_t i = 0; i < 3; ++i) {\n    du_null_l->get(i + 1) =\n        (dt_worldtube_normal.get(i) - dt_shift.get(i) * one_divided_by_lapse) *\n        one_divided_by_denominator;\n    du_null_l->get(i + 1) += shift.get(i) * get(dt_lapse) *\n                             square(one_divided_by_lapse) *\n                             one_divided_by_denominator;\n    du_null_l->get(i + 1) +=\n        (-shift.get(i) * one_divided_by_lapse + worldtube_normal.get(i)) *\n        du_denominator;\n  }\n}\n\nvoid dlambda_null_metric_and_inverse(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::RadialNull>*>\n        dlambda_null_metric,\n    const gsl::not_null<tnsr::AA<DataVector, 3, Frame::RadialNull>*>\n        dlambda_inverse_null_metric,\n    const AngulariCartesianA& angular_d_null_l,\n    const SphericaliCartesianJ& cartesian_to_spherical_jacobian,\n    const tnsr::iaa<DataVector, 3>& phi,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::A<DataVector, 3>& du_null_l,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric,\n    const tnsr::A<DataVector, 3>& null_l,\n    const tnsr::aa<DataVector, 3>& spacetime_metric) {\n  // first, the (down-index) null metric\n  const size_t size = get<0, 0>(spacetime_metric).size();\n  set_number_of_grid_points(dlambda_null_metric, size);\n  set_number_of_grid_points(dlambda_inverse_null_metric, size);\n\n  get<0, 0>(*dlambda_null_metric) =\n      get<0>(null_l) * get<0, 0>(dt_spacetime_metric) +\n      2.0 * get<0>(du_null_l) * get<0, 0>(spacetime_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    get<0, 0>(*dlambda_null_metric) +=\n        null_l.get(i + 1) * phi.get(i, 0, 0) +\n        2.0 * du_null_l.get(i + 1) * spacetime_metric.get(i + 1, 0);\n  }\n  // A0 component\n  for (size_t A = 0; A < 2; ++A) {\n    dlambda_null_metric->get(0, A + 2) =\n        cartesian_to_spherical_jacobian.get(A + 1, 0) *\n            (get<0>(du_null_l) * get<1, 0>(spacetime_metric) +\n             get<0>(null_l) * get<1, 0>(dt_spacetime_metric)) +\n        angular_d_null_l.get(A, 1) * get<1, 0>(spacetime_metric) +\n        angular_d_null_l.get(A, 0) * get<0, 0>(spacetime_metric);\n    for (size_t k = 1; k < 3; ++k) {\n      dlambda_null_metric->get(0, A + 2) +=\n          cartesian_to_spherical_jacobian.get(A + 1, k) *\n              (get<0>(du_null_l) * spacetime_metric.get(k + 1, 0) +\n               get<0>(null_l) * dt_spacetime_metric.get(k + 1, 0)) +\n          angular_d_null_l.get(A, k + 1) * spacetime_metric.get(k + 1, 0);\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t k = 0; k < 3; ++k) {\n        dlambda_null_metric->get(0, A + 2) +=\n            cartesian_to_spherical_jacobian.get(A + 1, k) *\n            (du_null_l.get(i + 1) * spacetime_metric.get(k + 1, i + 1) +\n             null_l.get(i + 1) * phi.get(i, k + 1, 0));\n      }\n    }\n    dlambda_null_metric->get(A + 2, 0) = dlambda_null_metric->get(0, A + 2);\n  }\n  // zero the null directions\n  get<0, 1>(*dlambda_null_metric) = 0.0;\n  for (size_t a = 1; a < 4; ++a) {\n    dlambda_null_metric->get(1, a) = 0.0;\n  }\n\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      dlambda_null_metric->get(A + 2, B + 2) = 0.0;\n    }\n  }\n\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = A; B < 2; ++B) {\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = 0; j < 3; ++j) {\n          dlambda_null_metric->get(A + 2, B + 2) +=\n              get<0>(null_l) * cartesian_to_spherical_jacobian.get(A + 1, i) *\n              cartesian_to_spherical_jacobian.get(B + 1, j) *\n              dt_spacetime_metric.get(i + 1, j + 1);\n        }\n      }\n    }\n  }\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = A; B < 2; ++B) {\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = 0; j < 3; ++j) {\n          for (size_t k = 0; k < 3; ++k) {\n            dlambda_null_metric->get(A + 2, B + 2) +=\n                null_l.get(k + 1) *\n                cartesian_to_spherical_jacobian.get(A + 1, i) *\n                cartesian_to_spherical_jacobian.get(B + 1, j) *\n                phi.get(k, i + 1, j + 1);\n          }\n        }\n      }\n    }\n  }\n\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = A; B < 2; ++B) {\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t a = 0; a < 4; ++a) {\n          dlambda_null_metric->get(A + 2, B + 2) +=\n              (angular_d_null_l.get(A, a) *\n                   cartesian_to_spherical_jacobian.get(B + 1, i) +\n               angular_d_null_l.get(B, a) *\n                   cartesian_to_spherical_jacobian.get(A + 1, i)) *\n              spacetime_metric.get(a, i + 1);\n        }\n      }\n    }\n  }\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b < a; ++b) {\n      dlambda_null_metric->get(a, b) = dlambda_null_metric->get(b, a);\n    }\n  }\n\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b < 4; ++b) {\n      dlambda_inverse_null_metric->get(a, b) = 0.0;\n    }\n  }\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = A; B < 2; ++B) {\n      for (size_t C = 0; C < 2; ++C) {\n        for (size_t D = 0; D < 2; ++D) {\n          dlambda_inverse_null_metric->get(A + 2, B + 2) -=\n              inverse_null_metric.get(A + 2, C + 2) *\n              inverse_null_metric.get(B + 2, D + 2) *\n              dlambda_null_metric->get(C + 2, D + 2);\n        }\n      }\n    }\n  }\n\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      dlambda_inverse_null_metric->get(1, A + 2) +=\n          inverse_null_metric.get(A + 2, B + 2) *\n          dlambda_null_metric->get(0, B + 2);\n      for (size_t C = 0; C < 2; ++C) {\n        dlambda_inverse_null_metric->get(1, A + 2) -=\n            inverse_null_metric.get(A + 2, B + 2) *\n            inverse_null_metric.get(1, C + 2) *\n            dlambda_null_metric->get(C + 2, B + 2);\n      }\n    }\n  }\n\n  get<1, 1>(*dlambda_inverse_null_metric) -= get<0, 0>(*dlambda_null_metric);\n\n  for (size_t A = 0; A < 2; ++A) {\n    get<1, 1>(*dlambda_inverse_null_metric) +=\n        2.0 * inverse_null_metric.get(1, A + 2) *\n        dlambda_null_metric->get(0, A + 2);\n    for (size_t B = 0; B < 2; ++B) {\n      get<1, 1>(*dlambda_inverse_null_metric) -=\n          inverse_null_metric.get(1, A + 2) *\n          inverse_null_metric.get(1, B + 2) *\n          dlambda_null_metric->get(A + 2, B + 2);\n    }\n  }\n}\n\nvoid bondi_r(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> bondi_r,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& null_metric) {\n  // the inclusion of the std::complex<double> informs the expression\n  // templates to turn the result into a ComplexDataVector\n  get(*bondi_r).data() = std::complex<double>(1.0, 0) *\n                         pow(get<2, 2>(null_metric) * get<3, 3>(null_metric) -\n                                 square(get<2, 3>(null_metric)),\n                             0.25);\n}\n\nvoid d_bondi_r(\n    const gsl::not_null<tnsr::a<DataVector, 3, Frame::RadialNull>*> d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& dlambda_null_metric,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& du_null_metric,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric,\n    const size_t l_max) {\n  // compute the time derivative part\n  get<0>(*d_bondi_r) =\n      0.25 * real(get(bondi_r).data()) *\n      (get<2, 2>(inverse_null_metric) * get<2, 2>(du_null_metric) +\n       2.0 * get<2, 3>(inverse_null_metric) * get<2, 3>(du_null_metric) +\n       get<3, 3>(inverse_null_metric) * get<3, 3>(du_null_metric));\n  // compute the lambda derivative part\n  get<1>(*d_bondi_r) =\n      0.25 * real(get(bondi_r).data()) *\n      (get<2, 2>(inverse_null_metric) * get<2, 2>(dlambda_null_metric) +\n       2.0 * get<2, 3>(inverse_null_metric) * get<2, 3>(dlambda_null_metric) +\n       get<3, 3>(inverse_null_metric) * get<3, 3>(dlambda_null_metric));\n\n  // Allocation (of result and coefficient buffer)\n  const auto eth_of_r =\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::Eth>(\n          l_max, 1, get(bondi_r));\n  d_bondi_r->get(2) = -real(eth_of_r.data());\n  d_bondi_r->get(3) = -imag(eth_of_r.data());\n}\n\nvoid dyads(\n    const gsl::not_null<tnsr::i<ComplexDataVector, 2, Frame::RadialNull>*>\n        down_dyad,\n    const gsl::not_null<tnsr::I<ComplexDataVector, 2, Frame::RadialNull>*>\n        up_dyad) {\n  // implicit factors of sin_theta omitted (still normalized as desired, though)\n  get<0>(*down_dyad) = -1.0;\n  get<1>(*down_dyad) = std::complex<double>(0.0, -1.0);\n  get<0>(*up_dyad) = -1.0;\n  get<1>(*up_dyad) = std::complex<double>(0.0, -1.0);\n}\n\nvoid beta_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> beta,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r) {\n  get(*beta).data() = std::complex<double>(-0.5, 0.0) * log(get<1>(d_bondi_r));\n}\n\nvoid bondi_u_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> bondi_u,\n    const tnsr::i<ComplexDataVector, 2, Frame::RadialNull>& dyad,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric) {\n  get(*bondi_u).data() = -get<0>(dyad) * get<1, 2>(inverse_null_metric) -\n                         get<1>(dyad) * get<1, 3>(inverse_null_metric);\n\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      get(*bondi_u).data() -= d_bondi_r.get(2 + A) * dyad.get(B) *\n                              inverse_null_metric.get(A + 2, B + 2) /\n                              get<1>(d_bondi_r);\n    }\n  }\n}\n\nvoid bondi_w_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> bondi_w,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r) {\n  get(*bondi_w).data() =\n      std::complex<double>(1.0, 0.0) *\n      (-1.0 + get<1>(d_bondi_r) * get<1, 1>(inverse_null_metric) -\n       2.0 * get<0>(d_bondi_r));\n\n  for (size_t A = 0; A < 2; ++A) {\n    get(*bondi_w).data() +=\n        2.0 * d_bondi_r.get(A + 2) * inverse_null_metric.get(1, A + 2);\n  }\n\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      get(*bondi_w).data() += d_bondi_r.get(A + 2) * d_bondi_r.get(B + 2) *\n                              inverse_null_metric.get(A + 2, B + 2) /\n                              get<1>(d_bondi_r);\n    }\n  }\n  get(*bondi_w).data() /= get(bondi_r).data();\n}\n\nvoid bondi_j_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> bondi_j,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::I<ComplexDataVector, 2, Frame::RadialNull>& dyad) {\n  get(*bondi_j).data() =\n      0.5 *\n      (square(get<0>(dyad)) * get<2, 2>(null_metric) +\n       2.0 * get<0>(dyad) * get<1>(dyad) * get<2, 3>(null_metric) +\n       square(get<1>(dyad)) * get<3, 3>(null_metric)) /\n      square(get(bondi_r).data());\n}\n\nvoid dr_bondi_j(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> dr_bondi_j,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        denominator_buffer,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& dlambda_null_metric,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::I<ComplexDataVector, 2, Frame::RadialNull>& dyad) {\n  get(*dr_bondi_j) = -2.0 * get(bondi_j) / get(bondi_r);\n  get(*denominator_buffer).data() =\n      1.0 / (square(get(bondi_r).data()) * get<1>(d_bondi_r));\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      get(*dr_bondi_j).data() += 0.5 * dyad.get(A) * dyad.get(B) *\n                                 dlambda_null_metric.get(A + 2, B + 2) *\n                                 get(*denominator_buffer).data();\n    }\n  }\n}\n\nvoid d2lambda_bondi_r(\n    const gsl::not_null<Scalar<DataVector>*> d2lambda_bondi_r,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dr_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r) {\n  get(*d2lambda_bondi_r) =\n      real(-0.25 * get(bondi_r).data() *\n           (get(dr_bondi_j).data() * conj(get(dr_bondi_j).data()) -\n            0.25 *\n                square(conj(get(bondi_j).data()) * get(dr_bondi_j).data() +\n                       get(bondi_j).data() * conj(get(dr_bondi_j).data())) /\n                (1.0 + get(bondi_j).data() * conj(get(bondi_j).data()))));\n  get(*d2lambda_bondi_r) *= square(get<1>(d_bondi_r));\n}\n\nvoid bondi_q_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> bondi_q,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> dr_bondi_u,\n    const Scalar<DataVector>& d2lambda_r,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>&\n        dlambda_inverse_null_metric,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const tnsr::i<ComplexDataVector, 2, Frame::RadialNull>& dyad,\n    const tnsr::i<DataVector, 2, Frame::RadialNull>& angular_d_dlambda_r,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_u) {\n  // Allocation\n  Scalar<SpinWeighted<ComplexDataVector, 1>> dlambda_bondi_u{\n      get(bondi_j).data().size()};\n\n  get(dlambda_bondi_u).data() =\n      -get(bondi_u).data() * get(d2lambda_r) / get<1>(d_bondi_r);\n\n  for (size_t A = 0; A < 2; ++A) {\n    get(dlambda_bondi_u) -=\n        (dlambda_inverse_null_metric.get(1, A + 2) +\n         get(d2lambda_r) * inverse_null_metric.get(1, A + 2) /\n             get<1>(d_bondi_r)) *\n        dyad.get(A);\n    for (size_t B = 0; B < 2; ++B) {\n      get(dlambda_bondi_u) -=\n          (d_bondi_r.get(B + 2) *\n           dlambda_inverse_null_metric.get(A + 2, B + 2) / get<1>(d_bondi_r)) *\n          dyad.get(A);\n      get(dlambda_bondi_u) -= angular_d_dlambda_r.get(B) *\n                              inverse_null_metric.get(A + 2, B + 2) *\n                              dyad.get(A) / get<1>(d_bondi_r);\n    }\n  }\n  get(*dr_bondi_u).data() = get(dlambda_bondi_u).data() / get<1>(d_bondi_r);\n\n  get(*bondi_q).data() =\n      square(get(bondi_r).data()) *\n      (get(bondi_j).data() * conj(get(dlambda_bondi_u).data()) +\n       sqrt(1.0 + get(bondi_j).data() * conj(get(bondi_j).data())) *\n           get(dlambda_bondi_u).data());\n}\n\nvoid bondi_h_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> bondi_h,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& du_null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::I<ComplexDataVector, 2, Frame::RadialNull>& dyad) {\n  get(*bondi_h).data() =\n      -2.0 * get<0>(d_bondi_r) / get(bondi_r).data() * get(bondi_j).data();\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      get(*bondi_h).data() += (0.5 / square(get(bondi_r).data())) *\n                              dyad.get(A) * dyad.get(B) *\n                              du_null_metric.get(A + 2, B + 2);\n    }\n  }\n}\n\nvoid du_j_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> du_bondi_j,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& /*bondi_j*/,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& du_null_metric,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& dlambda_null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::I<ComplexDataVector, 2, Frame::RadialNull>& dyad) {\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      if (UNLIKELY(A == 0 and B == 0)) {\n        get(*du_bondi_j).data() = -(0.5 / square(get(bondi_r).data())) *\n                                  square(get<0>(dyad)) *\n                                  (get<0>(d_bondi_r) / get<1>(d_bondi_r) *\n                                       get<2, 2>(dlambda_null_metric) -\n                                   get<2, 2>(du_null_metric));\n\n      } else {\n        get(*du_bondi_j).data() -= (0.5 / square(get(bondi_r).data())) *\n                                   dyad.get(A) * dyad.get(B) *\n                                   (get<0>(d_bondi_r) / get<1>(d_bondi_r) *\n                                        dlambda_null_metric.get(A + 2, B + 2) -\n                                    du_null_metric.get(A + 2, B + 2));\n      }\n    }\n  }\n}\n\nvoid klein_gordon_psi_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_psi,\n    const Scalar<DataVector>& csw_psi) {\n  get(*kg_psi).data() = std::complex<double>(1.0, 0.0) * get(csw_psi);\n}\n\nvoid klein_gordon_pi_worldtube_data(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_pi,\n    const Scalar<DataVector>& csw_pi, const tnsr::i<DataVector, 3>& csw_phi,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift) {\n  // Pure time derivative\n  // dt Psi = - lapse * Pi + shift^{i} Phi_{i}\n  get(*kg_pi).data() =\n      std::complex<double>(-1.0, 0.0) * get(lapse) * get(csw_pi);\n  for (size_t i = 0; i < 3; i++) {\n    get(*kg_pi).data() +=\n        std::complex<double>(1.0, 0.0) * shift.get(i) * csw_phi.get(i);\n  }\n}\n\nnamespace {\n// the common step between the modal input and the Generalized harmonic input\n// that performs the final gauge processing to Bondi scalars and places them in\n// the Variables.\ntemplate <typename BufferTagList, typename ComplexBufferTagList>\nvoid create_bondi_boundary_data(\n    const gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const gsl::not_null<Variables<BufferTagList>*> computation_variables,\n    const gsl::not_null<Variables<ComplexBufferTagList>*> derivative_buffers,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::iaa<DataVector, 3>& phi,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::A<DataVector, 3>& null_l,\n    const tnsr::A<DataVector, 3>& du_null_l,\n    const SphericaliCartesianJ& cartesian_to_spherical_jacobian,\n    const size_t l_max, const double extraction_radius) {\n  const size_t size = Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  // unfortunately, because the dyads are not themselves spin-weighted, they\n  // need a separate Variables\n  Variables<tmpl::list<Tags::detail::DownDyad, Tags::detail::UpDyad>>\n      dyad_variables{size};\n\n  auto& null_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>(\n          *computation_variables);\n  auto& du_null_metric = get<\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>>(\n      *computation_variables);\n  null_metric_and_derivative(\n      make_not_null(&du_null_metric), make_not_null(&null_metric),\n      cartesian_to_spherical_jacobian, dt_spacetime_metric, spacetime_metric);\n\n  auto& inverse_null_metric =\n      get<gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>>(\n          *computation_variables);\n\n  // the below scaling process is used to reduce accumulation of numerical\n  // error in the determinant evaluation\n\n  // buffer reuse because the scaled null metric is only needed until the\n  // `determinant_and_inverse` call\n  auto& scaled_null_metric =\n      get<gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>>(\n          *computation_variables);\n  for (size_t i = 0; i < 4; ++i) {\n    for (size_t j = i; j < 4; ++j) {\n      if (i > 1 and j > 1) {\n        scaled_null_metric.get(i, j) =\n            null_metric.get(i, j) / square(extraction_radius);\n      } else if (i > 1 or j > 1) {\n        scaled_null_metric.get(i, j) =\n            null_metric.get(i, j) / extraction_radius;\n      } else {\n        scaled_null_metric.get(i, j) = null_metric.get(i, j);\n      }\n    }\n  }\n  // Allocation\n  const auto scaled_inverse_null_metric =\n      determinant_and_inverse(scaled_null_metric).second;\n  for (size_t i = 0; i < 4; ++i) {\n    for (size_t j = i; j < 4; ++j) {\n      if (i > 1 and j > 1) {\n        inverse_null_metric.get(i, j) =\n            scaled_inverse_null_metric.get(i, j) / square(extraction_radius);\n      } else if (i > 1 or j > 1) {\n        inverse_null_metric.get(i, j) =\n            scaled_inverse_null_metric.get(i, j) / extraction_radius;\n      } else {\n        inverse_null_metric.get(i, j) = scaled_inverse_null_metric.get(i, j);\n      }\n    }\n  }\n\n  auto& angular_d_null_l =\n      get<Tags::detail::AngularDNullL>(*computation_variables);\n  auto& buffer_for_derivatives =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          *derivative_buffers));\n  auto& eth_buffer =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          *derivative_buffers));\n  for (size_t a = 0; a < 4; ++a) {\n    buffer_for_derivatives.data() =\n        std::complex<double>(1.0, 0.0) * null_l.get(a);\n    Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n        l_max, 1, make_not_null(&eth_buffer), buffer_for_derivatives);\n    angular_d_null_l.get(0, a) = -real(eth_buffer.data());\n    angular_d_null_l.get(1, a) = -imag(eth_buffer.data());\n  }\n\n  auto& dlambda_null_metric = get<Tags::detail::DLambda<\n      gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>>(\n      *computation_variables);\n  auto& dlambda_inverse_null_metric = get<Tags::detail::DLambda<\n      gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>>>(\n      *computation_variables);\n  dlambda_null_metric_and_inverse(\n      make_not_null(&dlambda_null_metric),\n      make_not_null(&dlambda_inverse_null_metric), angular_d_null_l,\n      cartesian_to_spherical_jacobian, phi, dt_spacetime_metric, du_null_l,\n      inverse_null_metric, null_l, spacetime_metric);\n\n  auto& r = get<Tags::BoundaryValue<Tags::BondiR>>(*bondi_boundary_data);\n  bondi_r(make_not_null(&r), null_metric);\n\n  auto& d_r =\n      get<::Tags::spacetime_deriv<Tags::detail::RealBondiR, tmpl::size_t<3>,\n                                  Frame::RadialNull>>(*computation_variables);\n  d_bondi_r(make_not_null(&d_r), r, dlambda_null_metric, du_null_metric,\n            inverse_null_metric, l_max);\n  get(get<Tags::BoundaryValue<Tags::DuRDividedByR>>(*bondi_boundary_data))\n      .data() = std::complex<double>(1.0, 0.0) * get<0>(d_r) / get(r).data();\n  get(get<Tags::BoundaryValue<Tags::Du<Tags::BondiR>>>(*bondi_boundary_data))\n      .data() = std::complex<double>(1.0, 0.0) * get<0>(d_r);\n\n  auto& down_dyad = get<Tags::detail::DownDyad>(dyad_variables);\n  auto& up_dyad = get<Tags::detail::UpDyad>(dyad_variables);\n  dyads(make_not_null(&down_dyad), make_not_null(&up_dyad));\n\n  beta_worldtube_data(make_not_null(&get<Tags::BoundaryValue<Tags::BondiBeta>>(\n                          *bondi_boundary_data)),\n                      d_r);\n\n  auto& bondi_u = get<Tags::BoundaryValue<Tags::BondiU>>(*bondi_boundary_data);\n  bondi_u_worldtube_data(make_not_null(&bondi_u), down_dyad, d_r,\n                         inverse_null_metric);\n\n  bondi_w_worldtube_data(make_not_null(&get<Tags::BoundaryValue<Tags::BondiW>>(\n                             *bondi_boundary_data)),\n                         d_r, inverse_null_metric, r);\n\n  auto& bondi_j = get<Tags::BoundaryValue<Tags::BondiJ>>(*bondi_boundary_data);\n  bondi_j_worldtube_data(make_not_null(&bondi_j), null_metric, r, up_dyad);\n\n  auto& dr_j =\n      get<Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>>(*bondi_boundary_data);\n  auto& denominator_buffer =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                               std::integral_constant<int, 0>>>(\n          *derivative_buffers);\n  dr_bondi_j(make_not_null(&get<Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>>(\n                 *bondi_boundary_data)),\n             make_not_null(&denominator_buffer), dlambda_null_metric, d_r,\n             bondi_j, r, up_dyad);\n\n  auto& d2lambda_r = get<\n      Tags::detail::DLambda<Tags::detail::DLambda<Tags::detail::RealBondiR>>>(\n      *computation_variables);\n  d2lambda_bondi_r(make_not_null(&d2lambda_r), d_r, dr_j, bondi_j, r);\n\n  auto& angular_d_dlambda_r =\n      get<::Tags::deriv<Tags::detail::DLambda<Tags::detail::RealBondiR>,\n                        tmpl::size_t<2>, Frame::RadialNull>>(\n          *computation_variables);\n  buffer_for_derivatives.data() = std::complex<double>(1.0, 0.0) * get<1>(d_r);\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&eth_buffer), buffer_for_derivatives);\n  angular_d_dlambda_r.get(0) = -real(eth_buffer.data());\n  angular_d_dlambda_r.get(1) = -imag(eth_buffer.data());\n\n  bondi_q_worldtube_data(\n      make_not_null(\n          &get<Tags::BoundaryValue<Tags::BondiQ>>(*bondi_boundary_data)),\n      make_not_null(&get<Tags::BoundaryValue<Tags::Dr<Tags::BondiU>>>(\n          *bondi_boundary_data)),\n      d2lambda_r, dlambda_inverse_null_metric, d_r, down_dyad,\n      angular_d_dlambda_r, inverse_null_metric, bondi_j, r, bondi_u);\n\n  bondi_h_worldtube_data(make_not_null(&get<Tags::BoundaryValue<Tags::BondiH>>(\n                             *bondi_boundary_data)),\n                         d_r, bondi_j, du_null_metric, r, up_dyad);\n\n  du_j_worldtube_data(\n      make_not_null(&get<Tags::BoundaryValue<Tags::Du<Tags::BondiJ>>>(\n          *bondi_boundary_data)),\n      d_r, bondi_j, du_null_metric, dlambda_null_metric, r, up_dyad);\n}\n}  // namespace\n\nvoid create_bondi_boundary_data(\n    const gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const tnsr::iaa<DataVector, 3>& phi, const tnsr::aa<DataVector, 3>& pi,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const double extraction_radius, const size_t l_max) {\n  const size_t size = Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  // Most allocations required for the full boundary computation are merged into\n  // a single, large Variables allocation. There remain a handful of cases in\n  // the computational functions called where an intermediate quantity that is\n  // not re-used is allocated rather than taking a buffer. These cases are\n  // marked with code comments 'Allocation'; In the future, if allocations are\n  // identified as a point to optimize, those buffers may be allocated here and\n  // passed as function arguments\n  Variables<tmpl::list<\n      Tags::detail::CosPhi, Tags::detail::CosTheta, Tags::detail::SinPhi,\n      Tags::detail::SinTheta, Tags::detail::CartesianCoordinates,\n      Tags::detail::CartesianToSphericalJacobian,\n      Tags::detail::InverseCartesianToSphericalJacobian,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::Shift<DataVector, 3>,\n      ::Tags::dt<gr::Tags::Shift<DataVector, 3>>, gr::Tags::Lapse<DataVector>,\n      ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n      Tags::detail::WorldtubeNormal, ::Tags::dt<Tags::detail::WorldtubeNormal>,\n      gr::Tags::SpacetimeNormalVector<DataVector, 3>, Tags::detail::NullL,\n      ::Tags::dt<Tags::detail::NullL>,\n      // for the detail function called at the end\n      gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>,\n      Tags::detail::AngularDNullL,\n      Tags::detail::DLambda<\n          gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      Tags::detail::DLambda<\n          gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      ::Tags::spacetime_deriv<Tags::detail::RealBondiR, tmpl::size_t<3>,\n                              Frame::RadialNull>,\n      Tags::detail::DLambda<Tags::detail::DLambda<Tags::detail::RealBondiR>>,\n      ::Tags::deriv<Tags::detail::DLambda<Tags::detail::RealBondiR>,\n                    tmpl::size_t<2>, Frame::RadialNull>>>\n      computation_variables{size};\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>>>\n      derivative_buffers{size};\n\n  auto& cos_phi = get<Tags::detail::CosPhi>(computation_variables);\n  auto& cos_theta = get<Tags::detail::CosTheta>(computation_variables);\n  auto& sin_phi = get<Tags::detail::SinPhi>(computation_variables);\n  auto& sin_theta = get<Tags::detail::SinTheta>(computation_variables);\n  trigonometric_functions_on_swsh_collocation(\n      make_not_null(&cos_phi), make_not_null(&cos_theta),\n      make_not_null(&sin_phi), make_not_null(&sin_theta), l_max);\n\n  // NOTE: to handle the singular values of polar coordinates, the phi\n  // components of all tensors are scaled according to their sin(theta)\n  // prefactors.\n  // so, any down-index component get<2>(A) represents 1/sin(theta) A_\\phi,\n  // and any up-index component get<2>(A) represents sin(theta) A^\\phi.\n  // This holds for Jacobians, and so direct application of the Jacobians\n  // brings the factors through.\n  auto& cartesian_coords =\n      get<Tags::detail::CartesianCoordinates>(computation_variables);\n  auto& cartesian_to_spherical_jacobian =\n      get<Tags::detail::CartesianToSphericalJacobian>(computation_variables);\n  auto& inverse_cartesian_to_spherical_jacobian =\n      get<Tags::detail::InverseCartesianToSphericalJacobian>(\n          computation_variables);\n  cartesian_to_spherical_coordinates_and_jacobians(\n      make_not_null(&cartesian_coords),\n      make_not_null(&cartesian_to_spherical_jacobian),\n      make_not_null(&inverse_cartesian_to_spherical_jacobian), cos_phi,\n      cos_theta, sin_phi, sin_theta, extraction_radius);\n\n  auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(computation_variables);\n  gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n\n  auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(computation_variables);\n  // Allocation\n  inverse_spatial_metric = determinant_and_inverse(spatial_metric).second;\n\n  auto& shift = get<gr::Tags::Shift<DataVector, 3>>(computation_variables);\n  gr::shift(make_not_null(&shift), spacetime_metric, inverse_spatial_metric);\n\n  auto& lapse = get<gr::Tags::Lapse<DataVector>>(computation_variables);\n  gr::lapse(make_not_null(&lapse), shift, spacetime_metric);\n\n  auto& dt_spacetime_metric =\n      get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n          computation_variables);\n\n  gh::time_derivative_of_spacetime_metric(make_not_null(&dt_spacetime_metric),\n                                          lapse, shift, pi, phi);\n\n  auto& dt_worldtube_normal =\n      get<::Tags::dt<Tags::detail::WorldtubeNormal>>(computation_variables);\n  auto& worldtube_normal =\n      get<Tags::detail::WorldtubeNormal>(computation_variables);\n  worldtube_normal_and_derivatives(\n      make_not_null(&worldtube_normal), make_not_null(&dt_worldtube_normal),\n      cos_phi, cos_theta, spacetime_metric, dt_spacetime_metric, sin_phi,\n      sin_theta, inverse_spatial_metric);\n  auto& spacetime_unit_normal =\n      get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(\n          computation_variables);\n  gr::spacetime_normal_vector(make_not_null(&spacetime_unit_normal), lapse,\n                              shift);\n  auto& dt_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(computation_variables);\n  gh::time_deriv_of_lapse(make_not_null(&dt_lapse), lapse, shift,\n                          spacetime_unit_normal, phi, pi);\n  auto& dt_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(computation_variables);\n  gh::time_deriv_of_shift(make_not_null(&dt_shift), lapse, shift,\n                          inverse_spatial_metric, spacetime_unit_normal, phi,\n                          pi);\n\n  auto& du_null_l = get<::Tags::dt<Tags::detail::NullL>>(computation_variables);\n  auto& null_l = get<Tags::detail::NullL>(computation_variables);\n  null_vector_l_and_derivatives(make_not_null(&du_null_l),\n                                make_not_null(&null_l), dt_worldtube_normal,\n                                dt_lapse, dt_spacetime_metric, dt_shift, lapse,\n                                spacetime_metric, shift, worldtube_normal);\n\n  // pass to the next step that is common between the 'modal' input and 'GH'\n  // input strategies\n  create_bondi_boundary_data(\n      bondi_boundary_data, make_not_null(&computation_variables),\n      make_not_null(&derivative_buffers), dt_spacetime_metric, phi,\n      spacetime_metric, null_l, du_null_l, cartesian_to_spherical_jacobian,\n      l_max, extraction_radius);\n}\n\nvoid create_bondi_boundary_data(\n    const gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const tnsr::ii<ComplexModalVector, 3>& spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dt_spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dr_spatial_metric_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dt_shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dr_shift_coefficients,\n    const Scalar<ComplexModalVector>& lapse_coefficients,\n    const Scalar<ComplexModalVector>& dt_lapse_coefficients,\n    const Scalar<ComplexModalVector>& dr_lapse_coefficients,\n    const double extraction_radius, const size_t l_max) {\n  const size_t size = Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  // Most allocations required for the full boundary computation are merged into\n  // a single, large Variables allocation. There remain a handful of cases in\n  // the computational functions called where an intermediate quantity that is\n  // not re-used is allocated rather than taking a buffer. These cases are\n  // marked with code comments 'Allocation'; In the future, if allocations are\n  // identified as a point to optimize, those buffers may be allocated here and\n  // passed as function arguments\n  Variables<tmpl::list<\n      Tags::detail::CosPhi, Tags::detail::CosTheta, Tags::detail::SinPhi,\n      Tags::detail::SinTheta, Tags::detail::CartesianCoordinates,\n      Tags::detail::CartesianToSphericalJacobian,\n      Tags::detail::InverseCartesianToSphericalJacobian,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>,\n      gr::Tags::Shift<DataVector, 3>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      ::Tags::dt<gr::Tags::Shift<DataVector, 3>>, gr::Tags::Lapse<DataVector>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n      gr::Tags::SpacetimeMetric<DataVector, 3>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n      gh::Tags::Phi<DataVector, 3>, Tags::detail::WorldtubeNormal,\n      ::Tags::dt<Tags::detail::WorldtubeNormal>, Tags::detail::NullL,\n      ::Tags::dt<Tags::detail::NullL>,\n      // for the detail function called at the end\n      gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>,\n      Tags::detail::AngularDNullL,\n      Tags::detail::DLambda<\n          gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      Tags::detail::DLambda<\n          gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      ::Tags::spacetime_deriv<Tags::detail::RealBondiR, tmpl::size_t<3>,\n                              Frame::RadialNull>,\n      Tags::detail::DLambda<Tags::detail::DLambda<Tags::detail::RealBondiR>>,\n      ::Tags::deriv<Tags::detail::DLambda<Tags::detail::RealBondiR>,\n                    tmpl::size_t<2>, Frame::RadialNull>>>\n      computation_variables{size};\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>>>\n      derivative_buffers{size};\n  auto& cos_phi = get<Tags::detail::CosPhi>(computation_variables);\n  auto& cos_theta = get<Tags::detail::CosTheta>(computation_variables);\n  auto& sin_phi = get<Tags::detail::SinPhi>(computation_variables);\n  auto& sin_theta = get<Tags::detail::SinTheta>(computation_variables);\n  trigonometric_functions_on_swsh_collocation(\n      make_not_null(&cos_phi), make_not_null(&cos_theta),\n      make_not_null(&sin_phi), make_not_null(&sin_theta), l_max);\n\n  // NOTE: to handle the singular values of polar coordinates, the phi\n  // components of all tensors are scaled according to their sin(theta)\n  // prefactors.\n  // so, any down-index component get<2>(A) represents 1/sin(theta) A_\\phi,\n  // and any up-index component get<2>(A) represents sin(theta) A^\\phi.\n  // This holds for Jacobians, and so direct application of the Jacobians\n  // brings the factors through.\n  auto& cartesian_coords =\n      get<Tags::detail::CartesianCoordinates>(computation_variables);\n  auto& cartesian_to_spherical_jacobian =\n      get<Tags::detail::CartesianToSphericalJacobian>(computation_variables);\n  auto& inverse_cartesian_to_spherical_jacobian =\n      get<Tags::detail::InverseCartesianToSphericalJacobian>(\n          computation_variables);\n  cartesian_to_spherical_coordinates_and_jacobians(\n      make_not_null(&cartesian_coords),\n      make_not_null(&cartesian_to_spherical_jacobian),\n      make_not_null(&inverse_cartesian_to_spherical_jacobian), cos_phi,\n      cos_theta, sin_phi, sin_theta, extraction_radius);\n\n  auto& cartesian_spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(computation_variables);\n  auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(computation_variables);\n  auto& d_cartesian_spatial_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n  auto& dt_cartesian_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n          computation_variables);\n  auto& interpolation_buffer =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                               std::integral_constant<int, 0>>>(\n          derivative_buffers);\n  Scalar<SpinWeighted<ComplexModalVector, 0>> interpolation_modal_buffer{size};\n  auto& eth_buffer =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                               std::integral_constant<int, 1>>>(\n          derivative_buffers);\n  cartesian_spatial_metric_and_derivatives_from_modes(\n      make_not_null(&cartesian_spatial_metric),\n      make_not_null(&inverse_spatial_metric),\n      make_not_null(&d_cartesian_spatial_metric),\n      make_not_null(&dt_cartesian_spatial_metric),\n      make_not_null(&interpolation_modal_buffer),\n      make_not_null(&interpolation_buffer), make_not_null(&eth_buffer),\n      spatial_metric_coefficients, dr_spatial_metric_coefficients,\n      dt_spatial_metric_coefficients, inverse_cartesian_to_spherical_jacobian,\n      l_max);\n\n  auto& cartesian_shift =\n      get<gr::Tags::Shift<DataVector, 3>>(computation_variables);\n  auto& d_cartesian_shift =\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n  auto& dt_cartesian_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(computation_variables);\n\n  cartesian_shift_and_derivatives_from_modes(\n      make_not_null(&cartesian_shift), make_not_null(&d_cartesian_shift),\n      make_not_null(&dt_cartesian_shift),\n      make_not_null(&interpolation_modal_buffer),\n      make_not_null(&interpolation_buffer), make_not_null(&eth_buffer),\n      shift_coefficients, dr_shift_coefficients, dt_shift_coefficients,\n      inverse_cartesian_to_spherical_jacobian, l_max);\n\n  auto& cartesian_lapse =\n      get<gr::Tags::Lapse<DataVector>>(computation_variables);\n  auto& d_cartesian_lapse =\n      get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n  auto& dt_cartesian_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(computation_variables);\n  cartesian_lapse_and_derivatives_from_modes(\n      make_not_null(&cartesian_lapse), make_not_null(&d_cartesian_lapse),\n      make_not_null(&dt_cartesian_lapse),\n      make_not_null(&interpolation_modal_buffer),\n      make_not_null(&interpolation_buffer), make_not_null(&eth_buffer),\n      lapse_coefficients, dr_lapse_coefficients, dt_lapse_coefficients,\n      inverse_cartesian_to_spherical_jacobian, l_max);\n\n  auto& phi = get<gh::Tags::Phi<DataVector, 3>>(computation_variables);\n  auto& dt_spacetime_metric =\n      get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n          computation_variables);\n  auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(computation_variables);\n  gh::phi(make_not_null(&phi), cartesian_lapse, d_cartesian_lapse,\n          cartesian_shift, d_cartesian_shift, cartesian_spatial_metric,\n          d_cartesian_spatial_metric);\n  gr::time_derivative_of_spacetime_metric(\n      make_not_null(&dt_spacetime_metric), cartesian_lapse, dt_cartesian_lapse,\n      cartesian_shift, dt_cartesian_shift, cartesian_spatial_metric,\n      dt_cartesian_spatial_metric);\n  gr::spacetime_metric(make_not_null(&spacetime_metric), cartesian_lapse,\n                       cartesian_shift, cartesian_spatial_metric);\n\n  auto& dt_worldtube_normal =\n      get<::Tags::dt<Tags::detail::WorldtubeNormal>>(computation_variables);\n  auto& worldtube_normal =\n      get<Tags::detail::WorldtubeNormal>(computation_variables);\n  worldtube_normal_and_derivatives(\n      make_not_null(&worldtube_normal), make_not_null(&dt_worldtube_normal),\n      cos_phi, cos_theta, spacetime_metric, dt_spacetime_metric, sin_phi,\n      sin_theta, inverse_spatial_metric);\n\n  auto& du_null_l = get<::Tags::dt<Tags::detail::NullL>>(computation_variables);\n  auto& null_l = get<Tags::detail::NullL>(computation_variables);\n  null_vector_l_and_derivatives(\n      make_not_null(&du_null_l), make_not_null(&null_l), dt_worldtube_normal,\n      dt_cartesian_lapse, dt_spacetime_metric, dt_cartesian_shift,\n      cartesian_lapse, spacetime_metric, cartesian_shift, worldtube_normal);\n\n  // pass to the next step that is common between the 'modal' input and 'GH'\n  // input strategies\n  create_bondi_boundary_data(\n      bondi_boundary_data, make_not_null(&computation_variables),\n      make_not_null(&derivative_buffers), dt_spacetime_metric, phi,\n      spacetime_metric, null_l, du_null_l, cartesian_to_spherical_jacobian,\n      l_max, extraction_radius);\n}\n\nvoid create_bondi_boundary_data(\n    const gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const tnsr::ii<DataVector, 3>& cartesian_spatial_metric,\n    const tnsr::ii<DataVector, 3>& cartesian_dt_spatial_metric,\n    const tnsr::ii<DataVector, 3>& cartesian_dr_spatial_metric,\n    const tnsr::I<DataVector, 3>& cartesian_shift,\n    const tnsr::I<DataVector, 3>& cartesian_dt_shift,\n    const tnsr::I<DataVector, 3>& cartesian_dr_shift,\n    const Scalar<DataVector>& cartesian_lapse,\n    const Scalar<DataVector>& cartesian_dt_lapse,\n    const Scalar<DataVector>& cartesian_dr_lapse,\n    const double extraction_radius, const size_t l_max) {\n  const size_t size = Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Variables<tmpl::list<\n      Tags::detail::CosPhi, Tags::detail::CosTheta, Tags::detail::SinPhi,\n      Tags::detail::SinTheta, Tags::detail::CartesianCoordinates,\n      Tags::detail::CartesianToSphericalJacobian,\n      Tags::detail::InverseCartesianToSphericalJacobian,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SpacetimeMetric<DataVector, 3>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n      gh::Tags::Phi<DataVector, 3>, Tags::detail::WorldtubeNormal,\n      ::Tags::dt<Tags::detail::WorldtubeNormal>, Tags::detail::NullL,\n      ::Tags::dt<Tags::detail::NullL>,\n      // for the detail function called at the end\n      gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>,\n      Tags::detail::AngularDNullL,\n      Tags::detail::DLambda<\n          gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      Tags::detail::DLambda<\n          gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      ::Tags::spacetime_deriv<Tags::detail::RealBondiR, tmpl::size_t<3>,\n                              Frame::RadialNull>,\n      Tags::detail::DLambda<Tags::detail::DLambda<Tags::detail::RealBondiR>>,\n      ::Tags::deriv<Tags::detail::DLambda<Tags::detail::RealBondiR>,\n                    tmpl::size_t<2>, Frame::RadialNull>>>\n      computation_variables{size};\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>>>\n      derivative_buffers{size};\n\n  auto& cos_phi = get<Tags::detail::CosPhi>(computation_variables);\n  auto& cos_theta = get<Tags::detail::CosTheta>(computation_variables);\n  auto& sin_phi = get<Tags::detail::SinPhi>(computation_variables);\n  auto& sin_theta = get<Tags::detail::SinTheta>(computation_variables);\n  trigonometric_functions_on_swsh_collocation(\n      make_not_null(&cos_phi), make_not_null(&cos_theta),\n      make_not_null(&sin_phi), make_not_null(&sin_theta), l_max);\n\n  // NOTE: to handle the singular values of polar coordinates, the phi\n  // components of all tensors are scaled according to their sin(theta)\n  // prefactors.\n  // so, any down-index component get<2>(A) represents 1/sin(theta) A_\\phi,\n  // and any up-index component get<2>(A) represents sin(theta) A^\\phi.\n  // This holds for Jacobians, and so direct application of the Jacobians\n  // brings the factors through.\n  auto& unused_cartesian_coords =\n      get<Tags::detail::CartesianCoordinates>(computation_variables);\n  auto& inverse_cartesian_to_spherical_jacobian =\n      get<Tags::detail::InverseCartesianToSphericalJacobian>(\n          computation_variables);\n  auto& cartesian_to_spherical_jacobian =\n      get<Tags::detail::CartesianToSphericalJacobian>(computation_variables);\n  cartesian_to_spherical_coordinates_and_jacobians(\n      make_not_null(&unused_cartesian_coords),\n      make_not_null(&cartesian_to_spherical_jacobian),\n      make_not_null(&inverse_cartesian_to_spherical_jacobian), cos_phi,\n      cos_theta, sin_phi, sin_theta, extraction_radius);\n\n  auto& phi = get<gh::Tags::Phi<DataVector, 3>>(computation_variables);\n  auto& dt_spacetime_metric =\n      get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n          computation_variables);\n  auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(computation_variables);\n\n  auto& interpolation_buffer =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                               std::integral_constant<int, 0>>>(\n          derivative_buffers);\n  auto& eth_buffer =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                               std::integral_constant<int, 1>>>(\n          derivative_buffers);\n\n  auto& d_cartesian_spatial_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n  auto& d_cartesian_shift =\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n  auto& d_cartesian_lapse =\n      get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n\n  deriv_cartesian_metric_lapse_shift_from_nodes(\n      make_not_null(&d_cartesian_spatial_metric),\n      make_not_null(&d_cartesian_shift), make_not_null(&d_cartesian_lapse),\n      make_not_null(&interpolation_buffer), make_not_null(&eth_buffer),\n      cartesian_spatial_metric, cartesian_dr_spatial_metric, cartesian_shift,\n      cartesian_dr_shift, cartesian_lapse, cartesian_dr_lapse,\n      inverse_cartesian_to_spherical_jacobian, l_max);\n\n  gh::phi(make_not_null(&phi), cartesian_lapse, d_cartesian_lapse,\n          cartesian_shift, d_cartesian_shift, cartesian_spatial_metric,\n          d_cartesian_spatial_metric);\n  gr::time_derivative_of_spacetime_metric(\n      make_not_null(&dt_spacetime_metric), cartesian_lapse, cartesian_dt_lapse,\n      cartesian_shift, cartesian_dt_shift, cartesian_spatial_metric,\n      cartesian_dt_spatial_metric);\n  gr::spacetime_metric(make_not_null(&spacetime_metric), cartesian_lapse,\n                       cartesian_shift, cartesian_spatial_metric);\n  auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(computation_variables);\n  inverse_spatial_metric =\n      determinant_and_inverse(cartesian_spatial_metric).second;\n\n  auto& dt_worldtube_normal =\n      get<::Tags::dt<Tags::detail::WorldtubeNormal>>(computation_variables);\n  auto& worldtube_normal =\n      get<Tags::detail::WorldtubeNormal>(computation_variables);\n  worldtube_normal_and_derivatives(\n      make_not_null(&worldtube_normal), make_not_null(&dt_worldtube_normal),\n      cos_phi, cos_theta, spacetime_metric, dt_spacetime_metric, sin_phi,\n      sin_theta, inverse_spatial_metric);\n\n  auto& du_null_l = get<::Tags::dt<Tags::detail::NullL>>(computation_variables);\n  auto& null_l = get<Tags::detail::NullL>(computation_variables);\n  null_vector_l_and_derivatives(\n      make_not_null(&du_null_l), make_not_null(&null_l), dt_worldtube_normal,\n      cartesian_dt_lapse, dt_spacetime_metric, cartesian_dt_shift,\n      cartesian_lapse, spacetime_metric, cartesian_shift, worldtube_normal);\n\n  // pass to the next step that is common between the 'modal' input and 'GH'\n  // input strategies\n  create_bondi_boundary_data(\n      bondi_boundary_data, make_not_null(&computation_variables),\n      make_not_null(&derivative_buffers), dt_spacetime_metric, phi,\n      spacetime_metric, null_l, du_null_l, cartesian_to_spherical_jacobian,\n      l_max, extraction_radius);\n}\n\nvoid create_klein_gordon_boundary_data(\n    const gsl::not_null<Variables<Tags::characteristic_worldtube_boundary_tags<\n        Tags::BoundaryValue, true>>*>\n        bondi_boundary_data,\n    const tnsr::i<DataVector, 3>& csw_phi, const Scalar<DataVector>& csw_pi,\n    const Scalar<DataVector>& csw_psi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3>& shift) {\n  klein_gordon_psi_worldtube_data(\n      make_not_null(&get<Tags::BoundaryValue<Tags::KleinGordonPsi>>(\n          *bondi_boundary_data)),\n      csw_psi);\n\n  klein_gordon_pi_worldtube_data(\n      make_not_null(\n          &get<Tags::BoundaryValue<Tags::KleinGordonPi>>(*bondi_boundary_data)),\n      csw_pi, csw_phi, lapse, shift);\n}\n\nvoid create_bondi_boundary_data_from_unnormalized_spec_modes(\n    const gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const tnsr::ii<ComplexModalVector, 3>& spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dt_spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dr_spatial_metric_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dt_shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dr_shift_coefficients,\n    const Scalar<ComplexModalVector>& lapse_coefficients,\n    const Scalar<ComplexModalVector>& dt_lapse_coefficients,\n    const Scalar<ComplexModalVector>& dr_lapse_coefficients,\n    const double extraction_radius, const size_t l_max) {\n  const size_t size = Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  // Most allocations required for the full boundary computation are merged into\n  // a single, large Variables allocation. There remain a handful of cases in\n  // the computational functions called where an intermediate quantity that is\n  // not re-used is allocated rather than taking a buffer. These cases are\n  // marked with code comments 'Allocation'; In future, allocations are\n  // identified as a point to optimize, those buffers may be allocated here and\n  // passed as function arguments\n  Variables<tmpl::list<\n      Tags::detail::CosPhi, Tags::detail::CosTheta, Tags::detail::SinPhi,\n      Tags::detail::SinTheta, Tags::detail::CartesianCoordinates,\n      Tags::detail::CartesianToSphericalJacobian,\n      Tags::detail::InverseCartesianToSphericalJacobian,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>,\n      gr::Tags::Shift<DataVector, 3>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      ::Tags::dt<gr::Tags::Shift<DataVector, 3>>, gr::Tags::Lapse<DataVector>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                    ::Frame::Inertial>,\n      ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n      gr::Tags::SpacetimeMetric<DataVector, 3>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n      gh::Tags::Phi<DataVector, 3>, Tags::detail::WorldtubeNormal,\n      ::Tags::dt<Tags::detail::WorldtubeNormal>, Tags::detail::NullL,\n      ::Tags::dt<Tags::detail::NullL>,\n      // for the detail function called at the end\n      gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>,\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>,\n      Tags::detail::AngularDNullL,\n      Tags::detail::DLambda<\n          gr::Tags::SpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      Tags::detail::DLambda<\n          gr::Tags::InverseSpacetimeMetric<DataVector, 3, Frame::RadialNull>>,\n      ::Tags::spacetime_deriv<Tags::detail::RealBondiR, tmpl::size_t<3>,\n                              Frame::RadialNull>,\n      Tags::detail::DLambda<Tags::detail::DLambda<Tags::detail::RealBondiR>>,\n      ::Tags::deriv<Tags::detail::DLambda<Tags::detail::RealBondiR>,\n                    tmpl::size_t<2>, Frame::RadialNull>,\n      ::Tags::TempScalar<0, DataVector>>>\n      computation_variables{size};\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>>>\n      derivative_buffers{size};\n  auto& cos_phi = get<Tags::detail::CosPhi>(computation_variables);\n  auto& cos_theta = get<Tags::detail::CosTheta>(computation_variables);\n  auto& sin_phi = get<Tags::detail::SinPhi>(computation_variables);\n  auto& sin_theta = get<Tags::detail::SinTheta>(computation_variables);\n  trigonometric_functions_on_swsh_collocation(\n      make_not_null(&cos_phi), make_not_null(&cos_theta),\n      make_not_null(&sin_phi), make_not_null(&sin_theta), l_max);\n\n  // NOTE: to handle the singular values of polar coordinates, the phi\n  // components of all tensors are scaled according to their sin(theta)\n  // prefactors.\n  // so, any down-index component get<2>(A) represents 1/sin(theta) A_\\phi,\n  // and any up-index component get<2>(A) represents sin(theta) A^\\phi.\n  // This holds for Jacobians, and so direct application of the Jacobians\n  // brings the factors through.\n  auto& cartesian_coords =\n      get<Tags::detail::CartesianCoordinates>(computation_variables);\n  auto& cartesian_to_spherical_jacobian =\n      get<Tags::detail::CartesianToSphericalJacobian>(computation_variables);\n  auto& inverse_cartesian_to_spherical_jacobian =\n      get<Tags::detail::InverseCartesianToSphericalJacobian>(\n          computation_variables);\n  cartesian_to_spherical_coordinates_and_jacobians(\n      make_not_null(&cartesian_coords),\n      make_not_null(&cartesian_to_spherical_jacobian),\n      make_not_null(&inverse_cartesian_to_spherical_jacobian), cos_phi,\n      cos_theta, sin_phi, sin_theta, extraction_radius);\n\n  auto& cartesian_spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(computation_variables);\n  auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(computation_variables);\n  auto& d_cartesian_spatial_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n  auto& dt_cartesian_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n          computation_variables);\n  auto& interpolation_buffer =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                               std::integral_constant<int, 0>>>(\n          derivative_buffers);\n  Scalar<SpinWeighted<ComplexModalVector, 0>> interpolation_modal_buffer{size};\n  auto& eth_buffer =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                               std::integral_constant<int, 1>>>(\n          derivative_buffers);\n  auto& radial_correction_factor =\n      get<::Tags::TempScalar<0, DataVector>>(computation_variables);\n  cartesian_spatial_metric_and_derivatives_from_unnormalized_spec_modes(\n      make_not_null(&cartesian_spatial_metric),\n      make_not_null(&inverse_spatial_metric),\n      make_not_null(&d_cartesian_spatial_metric),\n      make_not_null(&dt_cartesian_spatial_metric),\n      make_not_null(&interpolation_modal_buffer),\n      make_not_null(&interpolation_buffer), make_not_null(&eth_buffer),\n      make_not_null(&radial_correction_factor), spatial_metric_coefficients,\n      dr_spatial_metric_coefficients, dt_spatial_metric_coefficients,\n      inverse_cartesian_to_spherical_jacobian, cartesian_coords, l_max);\n\n  auto& cartesian_shift =\n      get<gr::Tags::Shift<DataVector, 3>>(computation_variables);\n  auto& d_cartesian_shift =\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n  auto& dt_cartesian_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(computation_variables);\n\n  cartesian_shift_and_derivatives_from_unnormalized_spec_modes(\n      make_not_null(&cartesian_shift), make_not_null(&d_cartesian_shift),\n      make_not_null(&dt_cartesian_shift),\n      make_not_null(&interpolation_modal_buffer),\n      make_not_null(&interpolation_buffer), make_not_null(&eth_buffer),\n      shift_coefficients, dr_shift_coefficients, dt_shift_coefficients,\n      inverse_cartesian_to_spherical_jacobian, radial_correction_factor, l_max);\n\n  auto& cartesian_lapse =\n      get<gr::Tags::Lapse<DataVector>>(computation_variables);\n  auto& d_cartesian_lapse =\n      get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        ::Frame::Inertial>>(computation_variables);\n  auto& dt_cartesian_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(computation_variables);\n  cartesian_lapse_and_derivatives_from_unnormalized_spec_modes(\n      make_not_null(&cartesian_lapse), make_not_null(&d_cartesian_lapse),\n      make_not_null(&dt_cartesian_lapse),\n      make_not_null(&interpolation_modal_buffer),\n      make_not_null(&interpolation_buffer), make_not_null(&eth_buffer),\n      lapse_coefficients, dr_lapse_coefficients, dt_lapse_coefficients,\n      inverse_cartesian_to_spherical_jacobian, radial_correction_factor, l_max);\n\n  auto& phi = get<gh::Tags::Phi<DataVector, 3>>(computation_variables);\n  auto& dt_spacetime_metric =\n      get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n          computation_variables);\n  auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(computation_variables);\n  gh::phi(make_not_null(&phi), cartesian_lapse, d_cartesian_lapse,\n          cartesian_shift, d_cartesian_shift, cartesian_spatial_metric,\n          d_cartesian_spatial_metric);\n  gr::time_derivative_of_spacetime_metric(\n      make_not_null(&dt_spacetime_metric), cartesian_lapse, dt_cartesian_lapse,\n      cartesian_shift, dt_cartesian_shift, cartesian_spatial_metric,\n      dt_cartesian_spatial_metric);\n  gr::spacetime_metric(make_not_null(&spacetime_metric), cartesian_lapse,\n                       cartesian_shift, cartesian_spatial_metric);\n\n  auto& dt_worldtube_normal =\n      get<::Tags::dt<Tags::detail::WorldtubeNormal>>(computation_variables);\n  auto& worldtube_normal =\n      get<Tags::detail::WorldtubeNormal>(computation_variables);\n  worldtube_normal_and_derivatives(\n      make_not_null(&worldtube_normal), make_not_null(&dt_worldtube_normal),\n      cos_phi, cos_theta, spacetime_metric, dt_spacetime_metric, sin_phi,\n      sin_theta, inverse_spatial_metric);\n\n  auto& du_null_l = get<::Tags::dt<Tags::detail::NullL>>(computation_variables);\n  auto& null_l = get<Tags::detail::NullL>(computation_variables);\n  null_vector_l_and_derivatives(\n      make_not_null(&du_null_l), make_not_null(&null_l), dt_worldtube_normal,\n      dt_cartesian_lapse, dt_spacetime_metric, dt_cartesian_shift,\n      cartesian_lapse, spacetime_metric, cartesian_shift, worldtube_normal);\n\n  // pass to the next step that is common between the 'modal' input and 'GH'\n  // input strategies\n  create_bondi_boundary_data(\n      bondi_boundary_data, make_not_null(&computation_variables),\n      make_not_null(&derivative_buffers), dt_spacetime_metric, phi,\n      spacetime_metric, null_l, du_null_l, cartesian_to_spherical_jacobian,\n      l_max, extraction_radius);\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/BoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryDataTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace Cce {\n\n/*!\n * \\brief Constructs the collocation values for \\f$\\cos(\\phi)\\f$,\n * \\f$\\cos(\\theta)\\f$, \\f$\\sin(\\phi)\\f$, and \\f$\\sin(\\theta)\\f$, returned by\n * `not_null` pointer in that order.\n *\n * \\details These are needed for coordinate transformations from the input\n * Cartesian-like coordinates.\n */\nvoid trigonometric_functions_on_swsh_collocation(\n    gsl::not_null<Scalar<DataVector>*> cos_phi,\n    gsl::not_null<Scalar<DataVector>*> cos_theta,\n    gsl::not_null<Scalar<DataVector>*> sin_phi,\n    gsl::not_null<Scalar<DataVector>*> sin_theta, size_t l_max);\n\n/*!\n * \\brief Creates both the Jacobian and inverse Jacobian between Cartesian and\n * spherical coordinates, and the coordinates themselves\n *\n * \\details The `cartesian_to_spherical_jacobian` is\n * \\f$dx^i/d\\tilde{x}^{\\tilde j}\\f$,\n * where the Cartesian components are in order \\f$x^i = \\{x, y, z\\}\\f$\n * and the spherical coordinates are\n * \\f$\\tilde{x}^{\\tilde j} = \\{r, \\theta, \\phi\\}\\f$.\n * The Cartesian coordinates given are the standard unit sphere coordinates:\n *\n * \\f{align*}{\n *  x &= \\cos(\\phi) \\sin(\\theta)\\\\\n *  y &= \\sin(\\phi) \\sin(\\theta)\\\\\n *  z &= \\cos(\\theta)\n * \\f}\n *\n * \\note These Jacobians are adjusted to improve regularity near the pole, in\n * particular the \\f$\\partial \\phi / \\partial x^i\\f$ components have been scaled\n * by \\f$\\sin \\theta\\f$ (omitting a \\f$1/\\sin(\\theta)\\f$) and the\n * \\f$\\partial x^i/\\partial \\phi\\f$ components have been scaled by\n * \\f$1/\\sin(\\theta)\\f$ (omitting a \\f$\\sin(\\theta)\\f$). The reason is that in\n * most careful calculations, these problematic sin factors can actually be\n * omitted because they cancel. In cases where they are actually required, they\n * must be put in by hand.\n */\nvoid cartesian_to_spherical_coordinates_and_jacobians(\n    gsl::not_null<tnsr::I<DataVector, 3>*> unit_cartesian_coords,\n    gsl::not_null<SphericaliCartesianJ*> cartesian_to_spherical_jacobian,\n    gsl::not_null<CartesianiSphericalJ*>\n        inverse_cartesian_to_spherical_jacobian,\n    const Scalar<DataVector>& cos_phi, const Scalar<DataVector>& cos_theta,\n    const Scalar<DataVector>& sin_phi, const Scalar<DataVector>& sin_theta,\n    double extraction_radius);\n\n/*\n * \\brief Compute \\f$g_{i j}\\f$, \\f$g^{i j}\\f$, \\f$\\partial_i g_{j k}\\f$, and\n * \\f$\\partial_t g_{i j}\\f$ from input libsharp-compatible modal spatial\n * metric quantities.\n *\n * \\details This function interpolates the modes of\n * input \\f$g_{ij}\\f$, \\f$\\partial_r g_{i j}\\f$, and \\f$\\partial_r g_{i j}\\f$ to\n * the libsharp-compatible grid. This function then applies the necessary\n * jacobian factors and angular derivatives to determine the full \\f$\\partial_i\n * g_{j k}\\f$.\n */\nvoid cartesian_spatial_metric_and_derivatives_from_modes(\n    gsl::not_null<tnsr::ii<DataVector, 3>*> cartesian_spatial_metric,\n    gsl::not_null<tnsr::II<DataVector, 3>*> inverse_cartesian_spatial_metric,\n    gsl::not_null<tnsr::ijj<DataVector, 3>*> d_cartesian_spatial_metric,\n    gsl::not_null<tnsr::ii<DataVector, 3>*> dt_cartesian_spatial_metric,\n    gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const tnsr::ii<ComplexModalVector, 3>& spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dr_spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dt_spatial_metric_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    size_t l_max);\n\n/*!\n * \\brief Compute \\f$\\beta^{i}\\f$, \\f$\\partial_i \\beta^{j}\\f$, and\n * \\f$\\partial_t \\beta^i\\f$ from input libsharp-compatible modal spatial\n * metric quantities.\n *\n * \\details This function interpolates the modes of\n * input \\f$\\beta^i\\f$, \\f$\\partial_r \\beta^i\\f$, and \\f$\\partial_r \\beta^i\\f$\n * to the libsharp-compatible grid. This function then applies the necessary\n * jacobian factors and angular derivatives to determine the full \\f$\\partial_i\n * \\beta^i\\f$.\n */\nvoid cartesian_shift_and_derivatives_from_modes(\n    gsl::not_null<tnsr::I<DataVector, 3>*> cartesian_shift,\n    gsl::not_null<tnsr::iJ<DataVector, 3>*> d_cartesian_shift,\n    gsl::not_null<tnsr::I<DataVector, 3>*> dt_cartesian_shift,\n    gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const tnsr::I<ComplexModalVector, 3>& shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dr_shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dt_shift_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    size_t l_max);\n\n/*!\n * \\brief Compute \\f$\\alpha\\f$, \\f$\\partial_i \\alpha\\f$, and\n * \\f$\\partial_t \\beta^i\\f$ from input libsharp-compatible modal spatial\n * metric quantities.\n *\n * \\details This function interpolates the modes of input \\f$\\alpha\\f$,\n * \\f$\\partial_r \\alpha\\f$, and \\f$\\partial_r \\alpha\\f$ to the\n * libsharp-compatible grid. This function then applies the necessary jacobian\n * factors and angular derivatives to determine the full \\f$\\partial_i\n * \\alpha\\f$.\n */\nvoid cartesian_lapse_and_derivatives_from_modes(\n    gsl::not_null<Scalar<DataVector>*> cartesian_lapse,\n    gsl::not_null<tnsr::i<DataVector, 3>*> d_cartesian_lapse,\n    gsl::not_null<Scalar<DataVector>*> dt_cartesian_lapse,\n    gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const Scalar<ComplexModalVector>& lapse_coefficients,\n    const Scalar<ComplexModalVector>& dr_lapse_coefficients,\n    const Scalar<ComplexModalVector>& dt_lapse_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    size_t l_max);\n\n/*!\n * \\brief Computes spatial derivatives of cartesian metric, shift, and lapse\n * from nodal metric quantities on a spherical worldtube.\n *\n * \\details See the details for\n * `cartesian_spatial_metric_and_derivatives_from_modes`,\n * `cartesian_shift_and_derivatives_from_modes`, and\n * `cartesian_lapse_and_derivatives_from_modes` ignoring the transformation from\n * modal to nodal (since the metric quantities are already in nodal form).\n */\nvoid deriv_cartesian_metric_lapse_shift_from_nodes(\n    gsl::not_null<tnsr::ijj<DataVector, 3>*> d_cartesian_spatial_metric,\n    gsl::not_null<tnsr::iJ<DataVector, 3>*> d_cartesian_shift,\n    gsl::not_null<tnsr::i<DataVector, 3>*> d_cartesian_lapse,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const tnsr::ii<DataVector, 3>& cartesian_spatial_metric,\n    const tnsr::ii<DataVector, 3>& dr_cartesian_spatial_metric,\n    const tnsr::I<DataVector, 3>& cartesian_shift,\n    const tnsr::I<DataVector, 3>& dr_cartesian_shift,\n    const Scalar<DataVector>& cartesian_lapse,\n    const Scalar<DataVector>& dr_cartesian_lapse,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    size_t l_max);\n\n/*!\n * \\brief Computes the spacetime metric and its first derivative in the\n * intermediate radial null coordinates\n *\n * \\details These components are obtained by the steps in\n * Section II-A of \\cite Barkett2019uae, which is based on the computation from\n * Section 4.3 of \\cite Bishop1998uk. The most direct comparison is to be made\n * with equation (31) of \\cite Barkett2019uae, which gives the null metric\n * components explicitly. The time derivative is then (using notation from\n * equation (31)  of \\cite Barkett2019uae):\n *\n * \\f{align}{\n * \\partial_{\\bar u} g_{\\bar u \\bar \\lambda} =\n * \\partial_{\\bar u} g_{\\bar \\lambda \\bar \\lambda} =\n * \\partial_{\\bar u} g_{\\bar \\lambda \\bar A} &= 0 \\\\\n * \\partial_{\\bar u} g_{\\bar u \\bar u} &=\n * \\partial_{\\breve t} g_{\\breve t \\breve t} \\\\\n * \\partial_{\\bar u} g_{\\bar u \\bar A} &=\n * \\frac{\\partial \\breve x^{\\breve i}}{\\partial \\bar x^{\\bar A}}\\\\\n * g_{\\breve i \\breve t}\n * \\partial_{\\bar u} g_{\\bar A \\bar B}\n * &= \\frac{\\partial \\breve x^{\\breve i}}{\\partial \\bar x^{\\bar A}}\n * \\frac{\\partial \\breve x^{\\breve j}}{\\partial \\bar x^{\\bar B}}\n * g_{\\breve i \\breve j}\n * \\f}\n */\nvoid null_metric_and_derivative(\n    gsl::not_null<tnsr::aa<DataVector, 3, Frame::RadialNull>*> du_null_metric,\n    gsl::not_null<tnsr::aa<DataVector, 3, Frame::RadialNull>*> null_metric,\n    const SphericaliCartesianJ& cartesian_to_spherical_jacobian,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::aa<DataVector, 3>& spacetime_metric);\n\n/*!\n * \\brief Computes the spatial unit normal vector \\f$s^i\\f$ to the spherical\n * worldtube surface and its first time derivative.\n *\n * \\details Refer to equation (20) of \\cite Barkett2019uae for the expression of\n * the spatial unit normal vector, and equation (23) of \\cite Barkett2019uae for\n * the first time derivative. Refer to \\cite Bishop1998uk for more exposition\n * about the overall construction of the coordinate transformations used for the\n * intermediate null coordinates.\n */\nvoid worldtube_normal_and_derivatives(\n    gsl::not_null<tnsr::I<DataVector, 3>*> worldtube_normal,\n    gsl::not_null<tnsr::I<DataVector, 3>*> dt_worldtube_normal,\n    const Scalar<DataVector>& cos_phi, const Scalar<DataVector>& cos_theta,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const Scalar<DataVector>& sin_phi, const Scalar<DataVector>& sin_theta,\n    const tnsr::II<DataVector, 3>& inverse_spatial_metric);\n\n/*!\n * \\brief Computes the null 4-vector \\f$l^\\mu\\f$ on the worldtube surface that\n * is to be used as the CCE hypersurface generator, and the first time\n * derivative \\f$\\partial_u l^\\mu\\f$.\n *\n * \\details For mathematical description of our choice of the null generator,\n * refer to equation (22) of \\cite Barkett2019uae, and for the first time\n * derivative see (25) of \\cite Barkett2019uae.  Refer to \\cite Bishop1998uk for\n * more exposition about the overall construction of the coordinate\n * transformations used for the intermediate null coordinates.\n */\nvoid null_vector_l_and_derivatives(\n    gsl::not_null<tnsr::A<DataVector, 3>*> du_null_l,\n    gsl::not_null<tnsr::A<DataVector, 3>*> null_l,\n    const tnsr::I<DataVector, 3>& dt_worldtube_normal,\n    const Scalar<DataVector>& dt_lapse,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::I<DataVector, 3>& dt_shift, const Scalar<DataVector>& lapse,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::I<DataVector, 3>& shift,\n    const tnsr::I<DataVector, 3>& worldtube_normal);\n\n/*!\n * \\brief Computes the partial derivative of the spacetime metric and inverse\n * spacetime metric in the intermediate null radial coordinates with respect to\n * the null generator \\f$l^\\mu\\f$\n *\n * \\details For full expressions of the \\f$l^\\mu \\partial_\\mu g_{a b}\\f$ and\n * \\f$l^\\mu \\partial_\\mu g^{a b}\\f$ computed in this function, see equation (31)\n * and (32) of \\cite Barkett2019uae.  Refer to \\cite Bishop1998uk for more\n * exposition about the overall construction of the coordinate transformations\n * used for the intermediate null coordinates.\n */\nvoid dlambda_null_metric_and_inverse(\n    gsl::not_null<tnsr::aa<DataVector, 3, Frame::RadialNull>*>\n        dlambda_null_metric,\n    gsl::not_null<tnsr::AA<DataVector, 3, Frame::RadialNull>*>\n        dlambda_inverse_null_metric,\n    const AngulariCartesianA& angular_d_null_l,\n    const SphericaliCartesianJ& cartesian_to_spherical_jacobian,\n    const tnsr::iaa<DataVector, 3>& phi,\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::A<DataVector, 3>& du_null_l,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric,\n    const tnsr::A<DataVector, 3>& null_l,\n    const tnsr::aa<DataVector, 3>& spacetime_metric);\n\n/*!\n * \\brief Computes the Bondi radius of the worldtube.\n *\n * \\details Note that unlike the Cauchy coordinate radius, the Bondi radius is\n * not constant over the worldtube. Instead, it is obtained by the determinant\n * of the angular part of the metric in the intermediate null coordinates (see\n * \\cite Barkett2019uae).\n *\n * \\f[\n *  r = \\left(\\frac{\\det g_{A B}}{ q_{A B}}\\right)^{1/4},\n * \\f]\n *\n * where \\f$q_{A B}\\f$ is the unit sphere metric.\n */\nvoid bondi_r(gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> bondi_r,\n             const tnsr::aa<DataVector, 3, Frame::RadialNull>& null_metric);\n\n/*!\n * \\brief Computes the full 4-dimensional partial of the Bondi radius with\n * respect to the intermediate null coordinates.\n *\n * \\details The expression evaluated is obtained from differentiating the\n * determinant equation for `bondi_r`, from (35) of \\cite Barkett2019uae :\n *\n * \\f[\n * \\partial_\\alpha r = \\frac{r}{4} \\left(g^{A B} \\partial_\\alpha g_{A B}\n * - \\frac{\\partial_\\alpha \\det q_{A B}}{\\det q_{A B}}\\right)\n * \\f]\n *\n * Note that for the angular derivatives, we just numerically differentiate\n * using the utilities in `Spectral::Swsh::angular_derivative()`. For the time\n * and radial derivatives, the second term in the above equation vanishes.\n */\nvoid d_bondi_r(\n    gsl::not_null<tnsr::a<DataVector, 3, Frame::RadialNull>*> d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& dlambda_null_metric,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& du_null_metric,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric,\n    size_t l_max);\n\n/*!\n * \\brief Compute the complex angular dyads used to define the spin-weighted\n * scalars in the CCE system.\n *\n * \\details We use the typically chosen angular dyads in CCE\n * \\cite Barkett2019uae \\cite Bishop1997ik :\n *\n * \\f{align*}{\n * q_A &= \\{-1, -i \\sin(\\theta)\\}\\\\\n * q^A &= \\left\\{-1, -i \\frac{1}{\\sin \\theta}\\right\\}\n * \\f}\n *\n * However, to maintain regularity and for compatibility with the more regular\n * Jacobians from `Cce::cartesian_to_spherical_coordinates_and_jacobians()`, in\n * the code we omit the factors of \\f$\\sin \\theta\\f$ from the above equations.\n */\nvoid dyads(\n    gsl::not_null<tnsr::i<ComplexDataVector, 2, Frame::RadialNull>*> down_dyad,\n    gsl::not_null<tnsr::I<ComplexDataVector, 2, Frame::RadialNull>*> up_dyad);\n\n/*!\n * \\brief Compute the \\f$\\beta\\f$ (lapse) function for the CCE Bondi-like\n * metric.\n *\n * \\details The Bondi-like metric has \\f$g^{u r} = - e^{2 \\beta}\\f$, and the\n * value of \\f$\\beta\\f$ is obtained from the intermediate null metric by (see\n * equation (51) of \\cite Barkett2019uae) using:\n *\n * \\f[\n * \\beta = -\\frac{1}{2} \\ln \\partial_{\\lambda} r\n * \\f]\n */\nvoid beta_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> beta,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r);\n\n/*!\n * \\brief Compute the \\f$U\\f$ (shift) function for the CCE Bondi-like metric.\n *\n * \\details The Bondi-like metric has \\f$g^{r A} = -e^{-2 \\beta} U^A\\f$, and the\n * spin-weighted vector \\f$U = U^A q_A\\f$. The value of \\f$U^A\\f$ can be\n * computed from the intermediate null metric quantities (see equation (54) of\n * \\cite Barkett2019uae) using:\n *\n * \\f[\n * U = -(\\partial_\\lambda r g^{\\lambda A} + \\partial_B r g^{A B}) q_A\n * / \\partial_\\lambda r \\f]\n *\n */\nvoid bondi_u_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> bondi_u,\n    const tnsr::i<ComplexDataVector, 2, Frame::RadialNull>& dyad,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric);\n\n/*!\n * \\brief Compute the \\f$W\\f$ (mass aspect) function for the CCE Bondi-like\n * metric.\n *\n * \\details The Bondi-like metric has \\f$g^{rr} = e^{-2 \\beta}(1 + r W)\\f$. The\n * value of \\f$W\\f$ can be computed from the null metric quantities (see\n * equation (55) of \\cite Barkett2019uae) using:\n *\n * \\f[\n * W = \\frac{1}{r} \\left(-1\n * + \\frac{g^{\\lambda \\lambda} (\\partial_\\lambda r)^2\n * + 2 \\partial_\\lambda r \\left(\\partial_A r g^{\\lambda A}\n * - \\partial_u r\\right) + \\partial_A r \\partial_B r g^{A B}}\n * {\\partial_\\lambda r}\\right) \\f]\n */\nvoid bondi_w_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> bondi_w,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r);\n\n/*!\n * \\brief Compute the \\f$J\\f$ (intuitively similar to the transverse-traceless\n * part of the angular metric) function for the CCE Bondi-like metric.\n *\n * \\details The Bondi-like metric has \\f$J = \\frac{1}{2 r^2} q^A q^B g_{A B}\\f$.\n * This expression holds both for the right-hand side in the Bondi coordinates\n * and for the right-hand side in the intermediate null coordinates (see\n * equation (45) of \\cite Barkett2019uae).\n */\nvoid bondi_j_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> bondi_j,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::I<ComplexDataVector, 2, Frame::RadialNull>& dyad);\n\n/*!\n * \\brief Compute the radial derivative of the angular metric spin-weighted\n * scalar \\f$\\partial_r J\\f$ in the CCE Bondi-like metric.\n *\n * \\details The radial derivative of the angular spin-weighted scalar \\f$J\\f$\n * can be computed from the null metric components by (c.f. equation (47) of\n * \\cite Barkett2019uae):\n *\n * \\f[\n * \\partial_r J = \\frac{\\partial_\\lambda J}{\\partial_\\lambda r} =\n *  \\frac{q^A q^B \\partial_\\lambda g_{A B} / (2 r^2)\n * - 2 \\partial_\\lambda r J / r}{\\partial_\\lambda r}\n * \\f]\n */\nvoid dr_bondi_j(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> dr_bondi_j,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        denominator_buffer,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& dlambda_null_metric,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::I<ComplexDataVector, 2, Frame::RadialNull>& dyad);\n\n/*!\n * \\brief Compute the second derivative of the Bondi radius with respect to the\n * intermediate null coordinate radius \\f$\\partial_\\lambda^2 r\\f$.\n *\n * \\details To determine this second derivative quantity without resorting to\n * depending on second-derivative metric inputs, we need to take advantage of\n * one of the Einstein field equations. Combining equations (53) and (52) of\n * \\cite Barkett2019uae, we have:\n *\n * \\f[\n * \\partial_\\lambda^2 r = \\frac{-r}{4} \\left(\n * \\partial_\\lambda J \\partial_\\lambda \\bar J - (\\partial_\\lambda K)^2\\right)\n * \\f],\n *\n * where the first derivative of \\f$K\\f$ can be obtained from \\f$K = \\sqrt{1 + J\n * \\bar J}\\f$ and the first derivative of \\f$J\\f$ can be obtained from (47) of\n * \\cite Barkett2019uae\n */\nvoid d2lambda_bondi_r(\n    gsl::not_null<Scalar<DataVector>*> d2lambda_bondi_r,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dr_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r);\n\n/*!\n * \\brief Compute the Bondi metric contribution \\f$Q\\f$ (radial derivative of\n * shift).\n *\n * \\details The definition of \\f$Q\\f$ in terms of the Bondi metric components is\n *\n * \\f[\n *  Q = q^A e^{-2 \\beta} g_{A B} \\partial_r U^B.\n * \\f]\n *\n * $Q$ can be derived from the intermediate null metric quantities via (see\n * equations (56) and (57) of \\cite Barkett2019uae)\n *\n * \\f[\n * \\partial_\\lambda U = - \\left(\\partial_\\lambda g^{\\lambda A}\n * + \\frac{\\partial_A \\partial_\\lambda r}{\\partial_\\lambda r} g^{A B}\n * + \\frac{\\partial_B r}{\\partial_\\lambda r} \\partial_\\lambda g^{A B}\\right) q_A\n * + 2 \\partial_\\lambda \\beta (U + g^{\\lambda A} q_A)\n * \\f]\n *\n * and\n *\n * \\f[\n * Q = r^2 (J \\partial_\\lambda \\bar U + K \\partial_\\lambda U)\n * \\f]\n *\n * also provided is \\f$\\partial_r U\\f$, which is separately useful to cache for\n * other intermediate steps in the CCE computation.\n */\nvoid bondi_q_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> bondi_q,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> dr_bondi_u,\n    const Scalar<DataVector>& d2lambda_r,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>&\n        dlambda_inverse_null_metric,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const tnsr::i<ComplexDataVector, 2, Frame::RadialNull>& dyad,\n    const tnsr::i<DataVector, 2, Frame::RadialNull>& angular_d_dlambda_r,\n    const tnsr::AA<DataVector, 3, Frame::RadialNull>& inverse_null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_u);\n\n/*!\n * \\brief Compute the Bondi metric contribution \\f$(\\partial_u J)_{y} \\equiv\n * H\\f$ (the retarded time derivative evaluated at fixed $y$ coordinate) on the\n * worldtube boundary.\n *\n * \\details The numerical time derivative (along the worldtube, rather than\n * along the surface of constant Bondi \\f$r\\f$) is computed by (see equation\n * (48) of \\cite Barkett2019uae)\n *\n * \\f[\n * (\\partial_u J)_y = \\frac{1}{2 r^2} q^A q^B \\partial_u g_{A B}\n * - \\frac{2 \\partial_u r}{r} J\n * \\f]\n *\n * \\note There is the regrettable notation difference with the primary reference\n * for these formulas \\cite Barkett2019uae in that we denote with \\f$H\\f$ the\n * time derivative at constant numerical radius, where \\cite Barkett2019uae uses\n * \\f$H\\f$ to denote the time derivative at constant Bondi radius.\n */\nvoid bondi_h_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> bondi_h,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& du_null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::I<ComplexDataVector, 2, Frame::RadialNull>& dyad);\n\n/*!\n * \\brief Compute the Bondi metric contribution \\f$(\\partial_u J)_r\\f$ (the\n * retarded time derivative at fixed coordinate $r$) on the worldtube boundary.\n *\n * \\details The numerical time derivative (along the surface of constant r, not\n * along the worldtube) is computed by (see equation (50) of\n * \\cite Barkett2019uae)\n *\n * \\f[\n * \\partial_u J = \\frac{1}{2 r^2} q^A q^B \\left(\\partial_u g_{A B}\n * - \\frac{ \\partial_u r}{ \\partial_\\lambda r} \\partial_\\lambda g_{A B}\\right)\n * \\f]\n *\n * \\note There is the regrettable notation difference with the primary reference\n * for these formulas \\cite Barkett2019uae in that we denote with \\f$H\\f$ the\n * time derivative at constant numerical radius, where \\cite Barkett2019uae uses\n * \\f$H\\f$ to denote the time derivative at constant Bondi radius.\n */\nvoid du_j_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> du_bondi_j,\n    const tnsr::a<DataVector, 3, Frame::RadialNull>& d_bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& du_null_metric,\n    const tnsr::aa<DataVector, 3, Frame::RadialNull>& dlambda_null_metric,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const tnsr::I<ComplexDataVector, 2, Frame::RadialNull>& dyad);\n\n/*!\n * \\brief Convert real scalar to complex spinweighted scalar\n */\nvoid klein_gordon_psi_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_psi,\n    const Scalar<DataVector>& csw_psi);\n\n/*!\n * \\brief Compute the time derivative of the scalar for cce and store as a\n * complex spinweighted scalar\n */\nvoid klein_gordon_pi_worldtube_data(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_pi,\n    const Scalar<DataVector>& csw_pi, const tnsr::i<DataVector, 3>& csw_phi,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift);\n\nnamespace Tags {\n/*!\n * \\brief The collection of tags mutated by `create_bondi_boundary_data` (and\n * `create_klein_gordon_boundary_data` if Klein-Gordon variables are included)\n *\n * \\details This list is used in the evolution of `CharacteristicExtract`\n */\ntemplate <template <typename> class BoundaryPrefix,\n          bool IncludeKleinGordon = false>\nusing characteristic_worldtube_boundary_tags = db::wrap_tags_in<\n    BoundaryPrefix,\n    tmpl::append<\n        tmpl::list<Tags::BondiBeta, Tags::BondiU, Tags::Dr<Tags::BondiU>,\n                   Tags::BondiQ, Tags::BondiW, Tags::BondiJ,\n                   Tags::Dr<Tags::BondiJ>, Tags::BondiH, Tags::Du<Tags::BondiJ>,\n                   Tags::BondiR, Tags::Du<Tags::BondiR>, Tags::DuRDividedByR>,\n        tmpl::conditional_t<\n            IncludeKleinGordon,\n            tmpl::list<Cce::Tags::KleinGordonPsi, Cce::Tags::KleinGordonPi>,\n            tmpl::list<>>>>;\n\n/*!\n * \\brief The collection of tags for worldtube quantities that need to be\n * written to disk during the Cauchy evolution that the `CharacateristicExtract`\n * need.\n *\n * \\details This list is used when writing Bondi quantities to disk. For the\n * combined ScalarTensor system, this list also includes the Klein-Gordon scalar\n * variables.\n */\ntemplate <template <typename> class BoundaryPrefix = Cce::Tags::BoundaryValue,\n          bool IncludeKleinGordon = false>\nusing worldtube_boundary_tags_for_writing = db::wrap_tags_in<\n    BoundaryPrefix,\n    tmpl::append<\n        tmpl::list<Cce::Tags::BondiBeta, Cce::Tags::Dr<Cce::Tags::BondiJ>,\n                   Cce::Tags::Du<Cce::Tags::BondiR>, Cce::Tags::BondiJ,\n                   Cce::Tags::Du<Cce::Tags::BondiJ>, Cce::Tags::BondiQ,\n                   Cce::Tags::BondiR, Cce::Tags::BondiU, Cce::Tags::BondiW>,\n        tmpl::conditional_t<\n            IncludeKleinGordon,\n            tmpl::list<Cce::Tags::KleinGordonPsi, Cce::Tags::KleinGordonPi>,\n            tmpl::list<>>>>;\n\nusing klein_gordon_worldtube_boundary_tags =\n    tmpl::list<Tags::BoundaryValue<Tags::KleinGordonPsi>,\n               Tags::BoundaryValue<Tags::KleinGordonPi>>;\n}  // namespace Tags\n\n/*!\n * \\brief Process the worldtube data from generalized harmonic quantities\n *  to desired Bondi quantities, placing the result in the passed\n * `Variables`.\n *\n * \\details\n * The mathematics are a bit complicated for all of the coordinate\n * transformations that are necessary to obtain the Bondi gauge quantities.\n * For full mathematical details, see the documentation for functions in\n * `BoundaryData.hpp` and \\cite Barkett2019uae \\cite Bishop1998uk.\n *\n * This function takes as input the full set of Generalized harmonic metric data\n * on a two-dimensional surface of constant \\f$r\\f$ and \\f$t\\f$ in numerical\n * coordinates.\n *\n * Sufficient tags to provide full worldtube boundary data at a particular\n * time are set in `bondi_boundary_data`. In particular, the set of tags in\n * `Tags::characteristic_worldtube_boundary_tags` in the provided `Variables`\n * are assigned to the worldtube boundary values associated with the input\n * metric components.\n *\n * The majority of the mathematical transformations are implemented as a set of\n * individual cascaded functions below. The details of the manipulations that\n * are performed to the input data may be found in the individual functions\n * themselves, which are called in the following order:\n * - `trigonometric_functions_on_swsh_collocation()`\n * - `gr::shift()`\n * - `gr::lapse()`\n * - `worldtube_normal_and_derivatives()`\n * - `gr::spacetime_normal_vector()`\n * - `gh::time_deriv_of_lapse()`\n * - `gh::time_deriv_of_shift()`\n * - `null_vector_l_and_derivatives()`\n * - `cartesian_to_spherical_coordinates_and_jacobians()`\n * - `null_metric_and_derivative()`\n * - `dlambda_null_metric_and_inverse()`\n * - `bondi_r()`\n * - `d_bondi_r()`\n * - `dyads()`\n * - `beta_worldtube_data()`\n * - `bondi_u_worldtube_data()`\n * - `bondi_w_worldtube_data()`\n * - `bondi_j_worldtube_data()`\n * - `dr_bondi_j()`\n * - `d2lambda_bondi_r()`\n * - `bondi_q_worldtube_data()`\n * - `bondi_h_worldtube_data()`\n * - `du_j_worldtube_data()`\n */\nvoid create_bondi_boundary_data(\n    gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const tnsr::iaa<DataVector, 3>& phi, const tnsr::aa<DataVector, 3>& pi,\n    const tnsr::aa<DataVector, 3>& spacetime_metric, double extraction_radius,\n    size_t l_max);\n\n/*!\n * \\brief Process the worldtube data from modal metric components and\n * derivatives to desired Bondi quantities, placing the result in the passed\n * `Variables`.\n *\n * \\details\n * The mathematics are a bit complicated for all of the coordinate\n * transformations that are necessary to obtain the Bondi gauge quantities.\n * For full mathematical details, see the documentation for functions in\n * `BoundaryData.hpp` and \\cite Barkett2019uae \\cite Bishop1998uk.\n *\n * This function takes as input the full set of ADM metric data and its radial\n * and time derivatives on a two-dimensional surface of constant \\f$r\\f$ and\n * \\f$t\\f$ in numerical coordinates. This data must be provided as spherical\n * harmonic coefficients in the libsharp format. This data is provided in nine\n * `Tensor`s.\n *\n * Sufficient tags to provide full worldtube boundary data at a particular\n * time are set in `bondi_boundary_data`. In particular, the set of tags in\n * `Tags::characteristic_worldtube_boundary_tags` in the provided `Variables`\n * are assigned to the worldtube boundary values associated with the input\n * metric components.\n *\n * The majority of the mathematical transformations are implemented as a set of\n * individual cascaded functions below. The details of the manipulations that\n * are performed to the input data may be found in the individual functions\n * themselves, which are called in the following order:\n * - `trigonometric_functions_on_swsh_collocation()`\n * - `cartesian_to_spherical_coordinates_and_jacobians()`\n * - `cartesian_spatial_metric_and_derivatives_from_modes()`\n * - `cartesian_shift_and_derivatives_from_modes()`\n * - `cartesian_lapse_and_derivatives_from_modes()`\n * - `gh::phi()`\n * - `gr::time_derivative_of_spacetime_metric`\n * - `gr::spacetime_metric`\n * - `generalized_harmonic_quantities()`\n * - `worldtube_normal_and_derivatives()`\n * - `null_vector_l_and_derivatives()`\n * - `null_metric_and_derivative()`\n * - `dlambda_null_metric_and_inverse()`\n * - `bondi_r()`\n * - `d_bondi_r()`\n * - `dyads()`\n * - `beta_worldtube_data()`\n * - `bondi_u_worldtube_data()`\n * - `bondi_w_worldtube_data()`\n * - `bondi_j_worldtube_data()`\n * - `dr_bondi_j()`\n * - `d2lambda_bondi_r()`\n * - `bondi_q_worldtube_data()`\n * - `bondi_h_worldtube_data()`\n * - `du_j_worldtube_data()`\n */\nvoid create_bondi_boundary_data(\n    gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const tnsr::ii<ComplexModalVector, 3>& spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dt_spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dr_spatial_metric_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dt_shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dr_shift_coefficients,\n    const Scalar<ComplexModalVector>& lapse_coefficients,\n    const Scalar<ComplexModalVector>& dt_lapse_coefficients,\n    const Scalar<ComplexModalVector>& dr_lapse_coefficients,\n    double extraction_radius, size_t l_max);\n\n/*!\n * \\brief Process the worldtube data from nodal metric components and\n * derivatives to desired Bondi quantities, placing the result in the passed\n * `Variables`.\n *\n * \\details\n * The mathematics are a bit complicated for all of the coordinate\n * transformations that are necessary to obtain the Bondi gauge quantities.\n * For full mathematical details, see the documentation for functions in\n * `BoundaryData.hpp` and \\cite Barkett2019uae \\cite Bishop1998uk.\n *\n * This function takes as input the full set of ADM metric data and its radial\n * and time derivatives on a two-dimensional surface of constant \\f$r\\f$ and\n * \\f$t\\f$ in numerical coordinates. This data must be provided as values at\n * the collocation points compatible with the libsharp format. This data is\n * provided in nine `Tensor`s.\n *\n * Sufficient tags to provide full worldtube boundary data at a particular\n * time are set in `bondi_boundary_data`. In particular, the set of tags in\n * `Tags::characteristic_worldtube_boundary_tags` in the provided `Variables`\n * are assigned to the worldtube boundary values associated with the input\n * metric components.\n *\n * The majority of the mathematical transformations are implemented as a set of\n * individual cascaded functions below. The details of the manipulations that\n * are performed to the input data may be found in the individual functions\n * themselves, which are called in the following order:\n * - `trigonometric_functions_on_swsh_collocation()`\n * - `gh::phi()`\n * - `gr::time_derivative_of_spacetime_metric`\n * - `gr::spacetime_metric`\n * - `worldtube_normal_and_derivatives()`\n * - `null_vector_l_and_derivatives()`\n * - `cartesian_to_spherical_coordinates_and_jacobians()`\n * - `null_metric_and_derivative()`\n * - `dlambda_null_metric_and_inverse()`\n * - `bondi_r()`\n * - `d_bondi_r()`\n * - `dyads()`\n * - `beta_worldtube_data()`\n * - `bondi_u_worldtube_data()`\n * - `bondi_w_worldtube_data()`\n * - `bondi_j_worldtube_data()`\n * - `dr_bondi_j()`\n * - `d2lambda_bondi_r()`\n * - `bondi_q_worldtube_data()`\n * - `bondi_h_worldtube_data()`\n * - `du_j_worldtube_data()`\n */\nvoid create_bondi_boundary_data(\n    gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const tnsr::ii<DataVector, 3>& cartesian_spatial_metric,\n    const tnsr::ii<DataVector, 3>& cartesian_dt_spatial_metric,\n    const tnsr::ii<DataVector, 3>& cartesian_dr_spatial_metric,\n    const tnsr::I<DataVector, 3>& cartesian_shift,\n    const tnsr::I<DataVector, 3>& cartesian_dt_shift,\n    const tnsr::I<DataVector, 3>& cartesian_dr_shift,\n    const Scalar<DataVector>& cartesian_lapse,\n    const Scalar<DataVector>& cartesian_dt_lapse,\n    const Scalar<DataVector>& cartesian_dr_lapse, double extraction_radius,\n    size_t l_max);\n\n/*!\n * \\brief Process the worldtube data from CurvedScalarWave quantities\n *  to desired cce scalar quantities\n */\nvoid create_klein_gordon_boundary_data(\n    gsl::not_null<Variables<Tags::characteristic_worldtube_boundary_tags<\n        Tags::BoundaryValue, true>>*>\n        bondi_boundary_data,\n    const tnsr::i<DataVector, 3>& csw_phi, const Scalar<DataVector>& csw_pi,\n    const Scalar<DataVector>& csw_psi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3>& shift);\n\n/*!\n * \\brief Process the worldtube data from modal metric components and\n * derivatives with incorrectly normalized radial derivatives from an old\n * version of SpEC to desired Bondi quantities, placing the result in the passed\n * \\ref DataBoxGroup.\n *\n * \\details\n * The mathematics are a bit complicated for all of the coordinate\n * transformations that are necessary to obtain the Bondi gauge quantities.\n * For full mathematical details, see the documentation for functions in\n * `BoundaryData.hpp` and \\cite Barkett2019uae \\cite Bishop1998uk.\n *\n * This function takes as input the full set of ADM metric data and its radial\n * and time derivatives on a two-dimensional surface of constant \\f$r\\f$ and\n * \\f$t\\f$ in numerical coordinates. This data must be provided as spherical\n * harmonic coefficients in the libsharp format. This data is provided in nine\n * `Tensor`s.\n *\n * Sufficient tags to provide full worldtube boundary data at a particular\n * time are set in `bondi_boundary_data`. In particular, the set of tags in\n * `Tags::characteristic_worldtube_boundary_tags` in the provided \\ref\n * DataBoxGroup are assigned to the worldtube boundary values associated with\n * the input metric components.\n *\n * The majority of the mathematical transformations are implemented as a set of\n * individual cascaded functions below. The details of the manipulations that\n * are performed to the input data may be found in the individual functions\n * themselves, which are called in the following order:\n * - `trigonometric_functions_on_swsh_collocation()`\n * - `cartesian_to_spherical_coordinates_and_jacobians()`\n * - `cartesian_spatial_metric_and_derivatives_from_unnormalized_spec_modes()`\n * - `cartesian_shift_and_derivatives_from_unnormalized_spec_modes()`\n * - `cartesian_lapse_and_derivatives_from_unnormalized_spec_modes()`\n * - `gh::phi()`\n * - `gr::time_derivative_of_spacetime_metric`\n * - `gr::spacetime_metric`\n * - `generalized_harmonic_quantities()`\n * - `worldtube_normal_and_derivatives()`\n * - `null_vector_l_and_derivatives()`\n * - `null_metric_and_derivative()`\n * - `dlambda_null_metric_and_inverse()`\n * - `bondi_r()`\n * - `d_bondi_r()`\n * - `dyads()`\n * - `beta_worldtube_data()`\n * - `bondi_u_worldtube_data()`\n * - `bondi_w_worldtube_data()`\n * - `bondi_j_worldtube_data()`\n * - `dr_bondi_j()`\n * - `d2lambda_bondi_r()`\n * - `bondi_q_worldtube_data()`\n * - `bondi_h_worldtube_data()`\n * - `du_j_worldtube_data()`\n */\nvoid create_bondi_boundary_data_from_unnormalized_spec_modes(\n    gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        bondi_boundary_data,\n    const tnsr::ii<ComplexModalVector, 3>& spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dt_spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dr_spatial_metric_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dt_shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dr_shift_coefficients,\n    const Scalar<ComplexModalVector>& lapse_coefficients,\n    const Scalar<ComplexModalVector>& dt_lapse_coefficients,\n    const Scalar<ComplexModalVector>& dr_lapse_coefficients,\n    double extraction_radius, size_t l_max);\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/BoundaryDataTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\nclass DataVector;\n/// \\endcond\n\nnamespace Cce {\nnamespace Frame {\n/// The frame for the spherical metric in which the radial coordinate is an\n/// affine parameter along outward-pointing null geodesics.\nstruct RadialNull {};\n}  // namespace Frame\n\n// tensor aliases for brevity\nusing SphericaliCartesianJ = Tensor<\n    DataVector, tmpl::integral_list<std::int32_t, 2, 1>,\n    index_list<SpatialIndex<3, UpLo::Lo, ::Frame::Spherical<::Frame::Inertial>>,\n               SpatialIndex<3, UpLo::Lo, ::Frame::Inertial>>>;\n\nusing CartesianiSphericalJ =\n    Tensor<DataVector, tmpl::integral_list<std::int32_t, 2, 1>,\n           index_list<SpatialIndex<3, UpLo::Lo, ::Frame::Inertial>,\n                      SpatialIndex<3, UpLo::Up,\n                                   ::Frame::Spherical<::Frame::Inertial>>>>;\n\nusing AngulariCartesianA = Tensor<\n  DataVector, tmpl::integral_list<std::int32_t, 2, 1>,\n  index_list<SpatialIndex<2, UpLo::Lo, ::Frame::Spherical<::Frame::Inertial>>,\n             SpacetimeIndex<3, UpLo::Lo, ::Frame::Inertial>>>;\n\nusing SphericaliCartesianjj = Tensor<\n    DataVector, tmpl::integral_list<std::int32_t, 2, 1, 1>,\n    index_list<SpatialIndex<3, UpLo::Lo, ::Frame::Spherical<::Frame::Inertial>>,\n               SpatialIndex<3, UpLo::Lo, ::Frame::Inertial>,\n               SpatialIndex<3, UpLo::Lo, ::Frame::Inertial>>>;\n\nnamespace Tags {\nnamespace detail {\n// this provides a set of tags for the purposes of allocating once in the entire\n// Boundary data computation; these tags are currently not used outside\n// intermediate steps of the procedure in `BoundaryData.hpp`\n\nstruct CosPhi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct CosTheta : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct SinPhi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct SinTheta : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct CartesianCoordinates : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\n\nstruct CartesianToSphericalJacobian : db::SimpleTag {\n  using type = SphericaliCartesianJ;\n};\n\nstruct InverseCartesianToSphericalJacobian : db::SimpleTag {\n  using type = CartesianiSphericalJ;\n};\n\nstruct WorldtubeNormal : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\n\nstruct UpDyad : db::SimpleTag {\n  using type = tnsr::I<ComplexDataVector, 2, Frame::RadialNull>;\n};\n\nstruct DownDyad : db::SimpleTag {\n  using type = tnsr::i<ComplexDataVector, 2, Frame::RadialNull>;\n};\n\nstruct RealBondiR : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct AngularDNullL : db::SimpleTag {\n  using type = AngulariCartesianA;\n};\n\nstruct NullL : db::SimpleTag {\n  using type = tnsr::A<DataVector, 3>;\n};\n\ntemplate <typename Tag>\nstruct DLambda : db::SimpleTag {\n  using type = typename Tag::type;\n};\n\n}  // namespace detail\n}  // namespace Tags\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Cce)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AnalyticBoundaryDataManager.cpp\n  BoundaryData.cpp\n  Equations.cpp\n  ExtractionRadius.cpp\n  GaugeTransformBoundaryData.cpp\n  KleinGordonSource.cpp\n  LinearOperators.cpp\n  LinearSolve.cpp\n  NewmanPenrose.cpp\n  PrecomputeCceDependencies.cpp\n  ScriPlusValues.cpp\n  SpecBoundaryData.cpp\n  WorldtubeBufferUpdater.cpp\n  WorldtubeDataManager.cpp\n  WorldtubeModeRecorder.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticBoundaryDataManager.hpp\n  BoundaryData.hpp\n  BoundaryDataTags.hpp\n  Equations.hpp\n  ExtractionRadius.hpp\n  GaugeTransformBoundaryData.hpp\n  IntegrandInputSteps.hpp\n  KleinGordonSystem.hpp\n  LinearOperators.hpp\n  LinearSolve.hpp\n  NewmanPenrose.hpp\n  OptionTags.hpp\n  PreSwshDerivatives.hpp\n  PrecomputeCceDependencies.hpp\n  ReceiveTags.hpp\n  ScriPlusInterpolationManager.hpp\n  ScriPlusValues.hpp\n  SpecBoundaryData.hpp\n  SwshDerivatives.hpp\n  System.hpp\n  KleinGordonSource.hpp\n  KleinGordonSystem.hpp\n  Tags.hpp\n  WorldtubeBufferUpdater.hpp\n  WorldtubeDataManager.hpp\n  WorldtubeModeRecorder.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  LinearSolver\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  GeneralizedHarmonic\n  GeneralRelativity\n  H5\n  Interpolation\n  LinearOperators\n  Observer\n  Options\n  ParallelInterpolation\n  Printf\n  Serialization\n  Spectral\n  SpinWeightedSphericalHarmonics\n  Utilities\n  INTERFACE\n  SystemUtilities\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_GlobalCache\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(AnalyticSolutions)\nadd_subdirectory(Callbacks)\nadd_subdirectory(Components)\nadd_subdirectory(Events)\nadd_subdirectory(Initialize)\nadd_subdirectory(Instantiations)\nadd_subdirectory(InterfaceManagers)\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Callbacks/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  DumpBondiSachsOnWorldtube.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  DumpBondiSachsOnWorldtube.hpp\n  SendGhWorldtubeData.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Callbacks/DumpBondiSachsOnWorldtube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/Callbacks/DumpBondiSachsOnWorldtube.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <iomanip>\n#include <mutex>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeModeRecorder.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace intrp::callbacks::DumpBondiSachsOnWorldtube_detail {\ntemplate <bool IncludeKleinGordon>\nvoid apply_impl(\n    const gsl::not_null<Parallel::NodeLock*> hdf5_lock, const double time,\n    const OptionHolders::Sphere& sphere, const std::string& filename_prefix,\n    const tnsr::aa<DataVector, 3, ::Frame::Inertial>& all_spacetime_metric,\n    const tnsr::aa<DataVector, 3, ::Frame::Inertial>& all_pi,\n    const tnsr::iaa<DataVector, 3, ::Frame::Inertial>& all_phi,\n    const Scalar<DataVector>& all_csw_psi, const Scalar<DataVector>& all_csw_pi,\n    const tnsr::i<DataVector, 3, ::Frame::Inertial>& all_csw_phi,\n    const Scalar<DataVector>& all_lapse,\n    const tnsr::I<DataVector, 3, ::Frame::Inertial>& all_shift) {\n  using cce_boundary_tags = Cce::Tags::characteristic_worldtube_boundary_tags<\n      Cce::Tags::BoundaryValue, IncludeKleinGordon>;\n  using tags_for_writing =\n      Cce::Tags::worldtube_boundary_tags_for_writing<Cce::Tags::BoundaryValue,\n                                                     IncludeKleinGordon>;\n  static_assert(\n      std::is_same_v<tmpl::list_difference<tags_for_writing, cce_boundary_tags>,\n                     tmpl::list<>>,\n      \"Cce tags to dump are not in the boundary tags.\");\n\n  if (sphere.angular_ordering != ylm::AngularOrdering::Cce) {\n    ERROR(\n        \"To use the DumpBondiSachsOnWorldtube post interpolation callback, \"\n        \"the angular ordering of the Spheres must be Cce, not \"\n        << sphere.angular_ordering);\n  }\n\n  const auto& radii = sphere.radii;\n  const size_t l_max = sphere.l_max;\n  const size_t num_points_single_sphere =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  // Bondi data\n  Variables<cce_boundary_tags> bondi_boundary_data{num_points_single_sphere};\n\n  size_t offset = 0;\n  for (const auto& radius : radii) {\n    const tnsr::aa<DataVector, 3, ::Frame::Inertial> spacetime_metric;\n    const tnsr::aa<DataVector, 3, ::Frame::Inertial> pi;\n    const tnsr::iaa<DataVector, 3, ::Frame::Inertial> phi;\n\n    // Set data references so we don't copy data unnecessarily\n    for (size_t a = 0; a < 4; a++) {\n      for (size_t b = 0; b < 4; b++) {\n        make_const_view(make_not_null(&spacetime_metric.get(a, b)),\n                        all_spacetime_metric.get(a, b), offset,\n                        num_points_single_sphere);\n        make_const_view(make_not_null(&pi.get(a, b)), all_pi.get(a, b), offset,\n                        num_points_single_sphere);\n        for (size_t i = 0; i < 3; i++) {\n          make_const_view(make_not_null(&phi.get(i, a, b)),\n                          all_phi.get(i, a, b), offset,\n                          num_points_single_sphere);\n        }\n      }\n    }\n\n    {\n      auto non_klein_gordon_data =\n          bondi_boundary_data.template reference_subset<\n              Cce::Tags::characteristic_worldtube_boundary_tags<\n                  Cce::Tags::BoundaryValue>>();\n      Cce::create_bondi_boundary_data(make_not_null(&non_klein_gordon_data),\n                                      phi, pi, spacetime_metric, radius, l_max);\n    }\n\n    if constexpr (IncludeKleinGordon) {\n      const Scalar<DataVector> csw_psi;\n      const Scalar<DataVector> csw_pi;\n      const Scalar<DataVector> lapse;\n      const tnsr::i<DataVector, 3, ::Frame::Inertial> csw_phi;\n      const tnsr::I<DataVector, 3, ::Frame::Inertial> shift;\n\n      make_const_view(make_not_null(&csw_psi.get()), all_csw_psi.get(), offset,\n                      num_points_single_sphere);\n      make_const_view(make_not_null(&csw_pi.get()), all_csw_pi.get(), offset,\n                      num_points_single_sphere);\n      make_const_view(make_not_null(&lapse.get()), all_lapse.get(), offset,\n                      num_points_single_sphere);\n      for (size_t i = 0; i < 3; i++) {\n        make_const_view(make_not_null(&csw_phi.get(i)), all_csw_phi.get(i),\n                        offset, num_points_single_sphere);\n        make_const_view(make_not_null(&shift.get(i)), all_shift.get(i), offset,\n                        num_points_single_sphere);\n      }\n\n      Cce::create_klein_gordon_boundary_data(\n          make_not_null(&bondi_boundary_data), csw_phi, csw_pi, csw_psi, lapse,\n          shift);\n    }\n\n    offset += num_points_single_sphere;\n\n    const std::string filename =\n        MakeString{} << filename_prefix << \"CceR\" << std::setfill('0')\n                     << std::setw(4) << std::lround(radius) << \".h5\";\n\n    // Lock now and it'll be unlocked for this radius after we finish writing\n    // the data to disk\n    const std::lock_guard lock(*hdf5_lock);\n    Cce::WorldtubeModeRecorder recorder{l_max, filename};\n\n    tmpl::for_each<tags_for_writing>(\n        [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n          constexpr int spin = Tag::tag::type::type::spin;\n\n          const ComplexDataVector& bondi_nodal_data =\n              get(get<Tag>(bondi_boundary_data)).data();\n\n          recorder.append_modal_data<spin>(\n              Cce::dataset_label_for_tag<typename Tag::tag>(), time,\n              bondi_nodal_data, l_max);\n        });\n  }\n}\n\n#define INCLUDE_KLEIN_GORDON(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void apply_impl<INCLUDE_KLEIN_GORDON(data)>(                        \\\n      const gsl::not_null<Parallel::NodeLock*> hdf5_lock, const double time,   \\\n      const OptionHolders::Sphere& sphere, const std::string& filename_prefix, \\\n      const tnsr::aa<DataVector, 3, ::Frame::Inertial>& all_spacetime_metric,  \\\n      const tnsr::aa<DataVector, 3, ::Frame::Inertial>& all_pi,                \\\n      const tnsr::iaa<DataVector, 3, ::Frame::Inertial>& all_phi,              \\\n      const Scalar<DataVector>& all_csw_psi,                                   \\\n      const Scalar<DataVector>& all_csw_pi,                                    \\\n      const tnsr::i<DataVector, 3, ::Frame::Inertial>& all_csw_phi,            \\\n      const Scalar<DataVector>& all_lapse,                                     \\\n      const tnsr::I<DataVector, 3, ::Frame::Inertial>& all_shift);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (true, false))\n\n#undef INSTANTIATE\n#undef INCLUDE_KLEIN_GORDON\n}  // namespace intrp::callbacks::DumpBondiSachsOnWorldtube_detail\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Callbacks/DumpBondiSachsOnWorldtube.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TagsDeclarations.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TagsDeclarations.hpp\"\n#include \"IO/Observer/Actions/GetLockPointer.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\nclass NodeLock;\n}  // namespace Parallel\nnamespace intrp::OptionHolders {\nstruct Sphere;\n}  // namespace intrp::OptionHolders\nnamespace intrp::Tags {\ntemplate <typename InterpolationTargetTag>\nstruct Sphere;\n}  // namespace intrp::Tags\nnamespace intrp::TargetPoints {\ntemplate <typename InterpolationTargetTag, typename Frame>\nstruct Sphere;\n}  // namespace intrp::TargetPoints\nnamespace observers::Tags {\nstruct H5FileLock;\n}  // namespace observers::Tags\n/// \\endcond\n\nnamespace intrp::callbacks {\nnamespace DumpBondiSachsOnWorldtube_detail {\ntemplate <bool IncludeKleinGordon>\nvoid apply_impl(\n    gsl::not_null<Parallel::NodeLock*> hdf5_lock, double time,\n    const OptionHolders::Sphere& sphere, const std::string& filename_prefix,\n    const tnsr::aa<DataVector, 3, ::Frame::Inertial>& all_spacetime_metric,\n    const tnsr::aa<DataVector, 3, ::Frame::Inertial>& all_pi,\n    const tnsr::iaa<DataVector, 3, ::Frame::Inertial>& all_phi,\n    const Scalar<DataVector>& all_csw_psi = {},\n    const Scalar<DataVector>& all_csw_pi = {},\n    const tnsr::i<DataVector, 3, ::Frame::Inertial>& all_csw_phi = {},\n    const Scalar<DataVector>& all_lapse = {},\n    const tnsr::I<DataVector, 3, ::Frame::Inertial>& all_shift = {});\n}  // namespace DumpBondiSachsOnWorldtube_detail\n\n/*!\n * \\brief Post interpolation callback that dumps metric data in Bondi-Sachs form\n * on a number of extraction radii given by the `intrp::TargetPoints::Sphere`\n * target.\n *\n * To use this callback, the target must be the `intrp::TargetPoints::Sphere`\n * target in the inertial frame. This callback also expects that the GH source\n * vars on each of the target spheres are:\n *\n * - `gr::Tags::SpacetimeMetric`\n * - `gh::Tags::Pi`\n * - `gh::Tags::Phi`\n *\n * If IncludeKleinGordon is true, also expect:\n * - `CurvedScalarWave::Tags::Psi`\n * - `CurvedScalarWave::Tags::Pi`\n * - `CurvedScalarWave::Tags::Phi`\n * - `gr::Tags::Lapse`\n * - `gr::Tags::Shift`\n *\n * This callback will write a new `H5` file for each extraction radius in the\n * Sphere target. The name of this file will be a file prefix specified by the\n * Cce::Tags::FilePrefix prepended onto `CceRXXXX.h5` where the XXXX is the\n * zero-padded extraction radius rounded to the nearest integer. The quantities\n * that will be written are\n *\n * - `Cce::Tags::BondiBeta`\n * - `Cce::Tags::Dr<Cce::Tags::BondiJ>`\n * - `Cce::Tags::Du<Cce::Tags::BondiR>`\n * - `Cce::Tags::BondiH`\n * - `Cce::Tags::BondiJ`\n * - `Cce::Tags::BondiQ`\n * - `Cce::Tags::BondiR`\n * - `Cce::Tags::BondiU`\n * - `Cce::Tags::BondiW`\n *\n * If IncludeKleinGordon is true, also writes:\n * - `Cce::Tags::KleinGordonPsi`\n * - `Cce::Tags::KleinGordonPi`\n *\n * \\note For all real quantities (Beta, DuR, R, W) (as well as the Klein-Gordon\n * Psi an Pi if included) we omit writing the negative m modes, and the\n * imaginary part of the m = 0 mode.\n */\ntemplate <typename InterpolationTargetTag, bool IncludeKleinGordon = false>\nstruct DumpBondiSachsOnWorldtube\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {\n  static constexpr bool include_klein_gordon = IncludeKleinGordon;\n  static constexpr double fill_invalid_points_with =\n      std::numeric_limits<double>::quiet_NaN();\n\n  using const_global_cache_tags = tmpl::list<Cce::Tags::FilePrefix>;\n\n  using cce_boundary_tags = Cce::Tags::characteristic_worldtube_boundary_tags<\n      Cce::Tags::BoundaryValue, include_klein_gordon>;\n\n  using extra_klein_gordon_cce_tags =\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<3>, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, 3>>;\n\n  using gh_source_vars_for_cce = tmpl::append<\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>,\n      tmpl::conditional_t<include_klein_gordon, extra_klein_gordon_cce_tags,\n                          tmpl::list<>>>;\n\n  using gh_source_vars_from_interpolation =\n      typename InterpolationTargetTag::vars_to_interpolate_to_target;\n\n  static_assert(\n      tmpl::and_<\n          std::is_same<tmpl::list_difference<gh_source_vars_from_interpolation,\n                                             gh_source_vars_for_cce>,\n                       tmpl::list<>>,\n          std::is_same<tmpl::list_difference<gh_source_vars_for_cce,\n                                             gh_source_vars_from_interpolation>,\n                       tmpl::list<>>>::type::value,\n      \"To use DumpBondiSachsOnWorldtube, the GH source variables must be the \"\n      \"spacetime metric, pi, and phi. If Klein Gordon variables are included, \"\n      \"the source variables must also include the CurvedScalarWave Psi and Pi, \"\n      \"as well as Lapse and shift.\");\n\n  static_assert(\n      std::is_same_v<typename InterpolationTargetTag::compute_target_points,\n                     intrp::TargetPoints::Sphere<InterpolationTargetTag,\n                                                 ::Frame::Inertial>>,\n      \"To use the DumpBondiSachsOnWorldtube post interpolation callback, you \"\n      \"must use the intrp::TargetPoints::Sphere target in the inertial \"\n      \"frame\");\n\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const TemporalId& temporal_id) {\n    // Even though no other cores should be writing to this file, we\n    // still need to get the h5 file lock so the system hdf5 doesn't get\n    // upset\n    auto* hdf5_lock = Parallel::local_synchronous_action<\n        observers::Actions::GetLockPointer<observers::Tags::H5FileLock>>(\n        Parallel::get_parallel_component<\n            observers::ObserverWriter<Metavariables>>(cache));\n\n    const double time =\n        intrp::InterpolationTarget_detail::get_temporal_id_value(temporal_id);\n    const auto& sphere =\n        Parallel::get<Tags::Sphere<InterpolationTargetTag>>(cache);\n    const auto& filename_prefix = Parallel::get<Cce::Tags::FilePrefix>(cache);\n    const auto& all_spacetime_metric =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(box);\n    const auto& all_pi = get<gh::Tags::Pi<DataVector, 3>>(box);\n    const auto& all_phi = get<gh::Tags::Phi<DataVector, 3>>(box);\n    if constexpr (IncludeKleinGordon) {\n      const auto& all_csw_psi = get<CurvedScalarWave::Tags::Psi>(box);\n      const auto& all_csw_pi = get<CurvedScalarWave::Tags::Pi>(box);\n      const auto& all_csw_phi = get<CurvedScalarWave::Tags::Phi<3>>(box);\n      const auto& all_lapse = get<gr::Tags::Lapse<DataVector>>(box);\n      const auto& all_shift = get<gr::Tags::Shift<DataVector, 3>>(box);\n      DumpBondiSachsOnWorldtube_detail::apply_impl<IncludeKleinGordon>(\n          hdf5_lock, time, sphere, filename_prefix, all_spacetime_metric,\n          all_pi, all_phi, all_csw_psi, all_csw_pi, all_csw_phi, all_lapse,\n          all_shift);\n    } else {\n      DumpBondiSachsOnWorldtube_detail::apply_impl<IncludeKleinGordon>(\n          hdf5_lock, time, sphere, filename_prefix, all_spacetime_metric,\n          all_pi, all_phi);\n    }\n  }\n};\n}  // namespace intrp::callbacks\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Callbacks/SendGhWorldtubeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ReceiveGhWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace intrp {\n/// \\cond\nnamespace Tags {\ntemplate <typename InterpolationTargetTag>\nstruct Sphere;\n}\n/// \\endcond\n\nnamespace callbacks {\n\n/// \\brief post_interpolation_callback that calls Cce::ReceiveGhWorldTubeData\n///\n/// Uses:\n/// - DataBox:\n///   - `::gr::Tags::SpacetimeMetric<DataVector, 3, Frame::Inertial>`\n///   - `::gh::Tags::Pi<DataVector, 3>`\n///   - `::gh::Tags::Phi<DataVector, 3>`\n///\n/// Conforms to the intrp::protocols::PostInterpolationCallback protocol\n///\n/// For requirements on InterpolationTargetTag, see\n/// intrp::protocols::InterpolationTargetTag\n///\n/// \\note This callback requires the temporal ID in an InterpolationTargetTag be\n/// a TimeStepId.\ntemplate <typename CceEvolutionComponent, typename InterpolationTargetTag,\n          bool DuringSelfStart, bool LocalTimeStepping>\nstruct SendGhWorldtubeData\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const TemporalId& temporal_id) {\n    // This is an ERROR and not an ASSERT because even in Release mode we want\n    // to stop execution if there were wrong inputs.\n    if (Parallel::get<Tags::Sphere<InterpolationTargetTag>>(cache)\n            .radii.size() > 1) {\n      ERROR(\"SendGhWorldtubeData expects a single worldtube radius, not \"\n            << Parallel::get<Tags::Sphere<InterpolationTargetTag>>(cache)\n                   .radii.size());\n    }\n\n    auto& cce_gh_boundary_component = Parallel::get_parallel_component<\n        Cce::GhWorldtubeBoundary<Metavariables>>(cache);\n    if constexpr (DuringSelfStart or LocalTimeStepping) {\n      Parallel::simple_action<typename Cce::Actions::ReceiveGhWorldtubeData<\n          CceEvolutionComponent, DuringSelfStart>>(\n          cce_gh_boundary_component, temporal_id,\n          db::get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(box),\n          db::get<::gh::Tags::Phi<DataVector, 3>>(box),\n          db::get<::gh::Tags::Pi<DataVector, 3>>(box));\n    } else {\n      // We want to avoid interface manager for global time stepping\n      Parallel::simple_action<Cce::Actions::SendToEvolution<\n          Cce::GhWorldtubeBoundary<Metavariables>, CceEvolutionComponent>>(\n          cce_gh_boundary_component, temporal_id,\n          db::get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(box),\n          db::get<::gh::Tags::Phi<DataVector, 3>>(box),\n          db::get<::gh::Tags::Pi<DataVector, 3>>(box));\n    }\n  }\n};\n}  // namespace callbacks\n}  // namespace intrp\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Components/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CharacteristicEvolution.hpp\n  KleinGordonCharacteristicEvolution.hpp\n  WorldtubeBoundary.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Actions/CalculateScriInputs.hpp\"\n#include \"Evolution/Systems/Cce/Actions/CharacteristicEvolutionBondiCalculations.hpp\"\n#include \"Evolution/Systems/Cce/Actions/FilterSwshVolumeQuantity.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionScri.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeFirstHypersurface.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InsertInterpolationScriData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/Psi0Matching.hpp\"\n#include \"Evolution/Systems/Cce/Actions/RequestBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ScriObserveInterpolated.hpp\"\n#include \"Evolution/Systems/Cce/Actions/TimeManagement.hpp\"\n#include \"Evolution/Systems/Cce/Actions/UpdateGauge.hpp\"\n#include \"Evolution/Systems/Cce/LinearSolve.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Evolution/Systems/Cce/ScriPlusValues.hpp\"\n#include \"Evolution/Systems/Cce/SwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/System.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nstruct CceEvolutionLabelTag {};\n\n/*!\n * \\brief The component for handling the CCE evolution and waveform output.\n *\n * \\details The \\ref DataBoxGroup associated with the CharacteristicEvolution\n * will contain many spin-weighted volume tags associated with the ongoing CCE\n * computation, as well as storage for the boundary values and quantities\n * related to managing the evolution.\n *\n * Metavariables requirements:\n * - Phases:\n *  - `Initialization`\n *  - `Evolve`\n * - Type aliases:\n *  - `evolved_coordinates_variables_tag`: A `Tags::Variables` with real-valued\n * tensors associated with coordinates that must be evolved.\n *  - `evolved_swsh_tags`: The spin-weighted quantities to be evolved (typically\n * `BondiJ`).\n *  - `evolved_swsh_dt_tags`: The spin-weighed quantities associated that are to\n * act as the time derivative to evolve `evolved_swsh_tags` (typically\n * `BondiH`).\n *  - `cce_boundary_communication_tags`: A typelist of tags that will be\n * communicated between the worldtube boundary component and the extraction\n * component (typically\n * `Cce::Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>`)\n *  - `cce_gauge_boundary_tags`: A typelist of tags that will be derived via\n * `GaugeAdjustedBoundaryValue` and corresponding gauge utilities\n *  - `cce_integrand_tags`: A typelist of tags needed as inputs to the\n * linear solve. Obtainable from the metafunction\n * `Cce::integrand_terms_to_compute_for_bondi_variable`.\n *  - `cce_integration_independent_tags`: A typelist of tags that are to be\n * computed and stored for each hypersurface iteration, but have no dependencies\n * on the intermediate hypersurface steps (typically\n * `Cce::pre_computation_steps`).\n *  - `cce_temporary_equations_tags`: A typelist of temporary buffers maintained\n * for intermediate steps in the integrand equations. Obtainable from the\n * metafunction `Cce::integrand_terms_to_compute_for_bondi_variable`.\n *  - `cce_pre_swsh_derivatives_tags`: A typelist of inputs to spin-weighted\n * derivative calculations to compute and cache for intermediate steps of the\n * CCE calculation. (typically `Cce::all_pre_swsh_derivative_tags`)\n *  - `cce_swsh_derivative_tags`: A typelist of spin-weighted derivatives to\n * compute and cache for intermediate steps of the CCE calculation. (typically\n * `Cce::all_swsh_derivative_tags`)\n *  - `cce_transform_buffer_tags`: A typelist of spin-weighted spherical\n * harmonic transform modes used to compute the spin-weighted derivatives in the\n * modal representation. (typically `Cce::all_transform_buffer_tags`).\n *  - `cce_angular_coordinate_tags`: A typelist of real-valued angular\n * coordinates that are not evolved.\n *  - `cce_scri_tags`: the tags of quantities to compute at scri+\n *  - `cce_hypersurface_initialization`: a mutator (for use with\n * `::Actions::MutateApply`) that is used to compute the initial hypersurface\n * data from the boundary data.\n */\ntemplate <class Metavariables>\nstruct CharacteristicEvolution {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  static constexpr bool evolve_ccm = Metavariables::evolve_ccm;\n  using cce_system = Cce::System<evolve_ccm>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags,\n          Metavariables::local_time_stepping>,\n      Actions::InitializeCharacteristicEvolutionScri<\n          typename Metavariables::scri_values_to_observe,\n          typename Metavariables::cce_boundary_component>,\n      Parallel::Actions::TerminatePhase>;\n\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  // the list of actions that occur for each of the hypersurface-integrated\n  // Bondi tags\n  template <typename BondiTag>\n  using hypersurface_computation = tmpl::list<\n      ::Actions::MutateApply<GaugeAdjustedBoundaryValue<BondiTag>>,\n      Actions::CalculateIntegrandInputsForTag<BondiTag>,\n      tmpl::transform<integrand_terms_to_compute_for_bondi_variable<BondiTag>,\n                      tmpl::bind<::Actions::MutateApply,\n                                 tmpl::bind<ComputeBondiIntegrand, tmpl::_1>>>,\n      ::Actions::MutateApply<\n          RadialIntegrateBondi<Tags::EvolutionGaugeBoundaryValue, BondiTag>>,\n      // Once we finish the U computation, we need to update all the quantities\n      // that depend on the time derivative of the gauge\n      tmpl::conditional_t<\n          std::is_same_v<BondiTag, Tags::BondiU>,\n          tmpl::list<\n              ::Actions::MutateApply<GaugeUpdateTimeDerivatives>,\n              tmpl::conditional_t<\n                  tt::is_a_v<AnalyticWorldtubeBoundary,\n                             typename Metavariables::cce_boundary_component>,\n                  tmpl::list<>,\n                  tmpl::conditional_t<evolve_ccm,\n                                      ::Actions::MutateApply<\n                                          GaugeUpdateInertialTimeDerivatives>,\n                                      tmpl::list<>>>,\n              ::Actions::MutateApply<\n                  GaugeAdjustedBoundaryValue<Tags::DuRDividedByR>>,\n              ::Actions::MutateApply<PrecomputeCceDependencies<\n                  Tags::EvolutionGaugeBoundaryValue, Tags::DuRDividedByR>>>,\n          tmpl::list<>>>;\n\n  using compute_scri_quantities_and_observe = tmpl::list<\n      ::Actions::MutateApply<\n          CalculateScriPlusValue<::Tags::dt<Tags::InertialRetardedTime>>>,\n      Actions::CalculateScriInputs,\n      tmpl::transform<typename metavariables::cce_scri_tags,\n                      tmpl::bind<::Actions::MutateApply,\n                                 tmpl::bind<CalculateScriPlusValue, tmpl::_1>>>,\n      tmpl::transform<\n          typename metavariables::scri_values_to_observe,\n          tmpl::bind<\n              Actions::InsertInterpolationScriData, tmpl::_1,\n              tmpl::pin<typename Metavariables::cce_boundary_component>>>,\n      Actions::ScriObserveInterpolated<\n          observers::ObserverWriter<Metavariables>,\n          typename Metavariables::cce_boundary_component>>;\n\n  using self_start_extract_action_list = tmpl::list<\n      Actions::RequestBoundaryData<\n          typename Metavariables::cce_boundary_component,\n          CharacteristicEvolution<Metavariables>>,\n      Actions::ReceiveWorldtubeData<\n          Metavariables,\n          typename Metavariables::cce_boundary_communication_tags>,\n      // note that the initialization will only actually happen on the\n      // iterations immediately following restarts\n      Actions::InitializeFirstHypersurface<\n          evolve_ccm, typename Metavariables::cce_boundary_component>,\n      tmpl::conditional_t<\n          tt::is_a_v<AnalyticWorldtubeBoundary,\n                     typename Metavariables::cce_boundary_component>,\n          Actions::UpdateGauge<false>, Actions::UpdateGauge<evolve_ccm>>,\n      Actions::PrecomputeGlobalCceDependencies,\n      tmpl::conditional_t<evolve_ccm,\n                          Actions::CalculatePsi0AndDerivAtInnerBoundary,\n                          tmpl::list<>>,\n      tmpl::transform<bondi_hypersurface_step_tags,\n                      tmpl::bind<hypersurface_computation, tmpl::_1>>,\n      Actions::FilterSwshVolumeQuantity<Tags::BondiH>,\n      ::Actions::MutateApply<\n          CalculateScriPlusValue<::Tags::dt<Tags::InertialRetardedTime>>>,\n      Actions::CalculateScriInputs,\n      tmpl::transform<typename metavariables::cce_scri_tags,\n                      tmpl::bind<::Actions::MutateApply,\n                                 tmpl::bind<CalculateScriPlusValue, tmpl::_1>>>,\n      ::Actions::MutateApply<RecordTimeStepperData<cce_system>>,\n      ::Actions::MutateApply<\n          UpdateU<cce_system, Metavariables::local_time_stepping,\n                  Tags::CceEvolutionPrefix>>,\n      ::Actions::MutateApply<\n          CleanHistory<cce_system, Tags::CceEvolutionPrefix>>>;\n\n  using extract_action_list = tmpl::list<\n      Actions::RequestBoundaryData<\n          typename Metavariables::cce_boundary_component,\n          CharacteristicEvolution<Metavariables>>,\n      ::Actions::Label<CceEvolutionLabelTag>,\n      Actions::ReceiveWorldtubeData<\n          Metavariables,\n          typename Metavariables::cce_boundary_communication_tags>,\n      Actions::InitializeFirstHypersurface<\n          evolve_ccm, typename Metavariables::cce_boundary_component>,\n      tmpl::conditional_t<\n          tt::is_a_v<AnalyticWorldtubeBoundary,\n                     typename Metavariables::cce_boundary_component>,\n          Actions::UpdateGauge<false>, Actions::UpdateGauge<evolve_ccm>>,\n      Actions::PrecomputeGlobalCceDependencies,\n      tmpl::conditional_t<evolve_ccm,\n                          Actions::CalculatePsi0AndDerivAtInnerBoundary,\n                          tmpl::list<>>,\n      tmpl::transform<bondi_hypersurface_step_tags,\n                      tmpl::bind<hypersurface_computation, tmpl::_1>>,\n      Actions::FilterSwshVolumeQuantity<Tags::BondiH>,\n      tmpl::conditional_t<\n          evolve_ccm, tmpl::list<>,\n          tmpl::flatten<tmpl::list<\n              std::conditional_t<Metavariables::local_time_stepping,\n                                 evolution::Actions::RunEventsAndTriggers<\n                                     Triggers::WhenToCheck::AtSteps>,\n                                 tmpl::list<>>,\n              evolution::Actions::RunEventsAndTriggers<\n                  Triggers::WhenToCheck::AtSlabs>>>>,\n      compute_scri_quantities_and_observe,\n      ::Actions::MutateApply<ChangeStepSize<\n          typename Metavariables::cce_step_choosers, Tags::CceEvolutionPrefix>>,\n      ::Actions::MutateApply<RecordTimeStepperData<cce_system>>,\n      ::Actions::MutateApply<\n          UpdateU<cce_system, Metavariables::local_time_stepping,\n                  Tags::CceEvolutionPrefix>>,\n      ::Actions::MutateApply<\n          ChangeTimeStepperOrder<cce_system, Tags::CceEvolutionPrefix>>,\n      ::Actions::MutateApply<\n          CleanHistory<cce_system, Tags::CceEvolutionPrefix>>,\n      // We cannot know our next step for certain until after we've performed\n      // step size selection, as we may need to reject a step.\n      Actions::RequestNextBoundaryData<\n          typename Metavariables::cce_boundary_component,\n          CharacteristicEvolution<Metavariables>>,\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Actions::ExitIfEndTimeReached, ::Actions::Goto<CceEvolutionLabelTag>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<Parallel::Phase::InitializeTimeStepperHistory,\n                             SelfStart::self_start_procedure<\n                                 self_start_extract_action_list, cce_system,\n                                 Tags::CceEvolutionPrefix>>,\n      Parallel::PhaseActions<Parallel::Phase::Evolve, extract_action_list>>;\n\n  static void initialize(\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<CharacteristicEvolution<Metavariables>>(\n        local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Components/KleinGordonCharacteristicEvolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Evolution/Systems/Cce/Actions/InitializeKleinGordonFirstHypersurface.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeKleinGordonVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/PrecomputeKleinGordonSourceVariables.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/KleinGordonSource.hpp\"\n#include \"Evolution/Systems/Cce/KleinGordonSystem.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\n/*!\n * \\brief The component for handling the CCE evolution for the Klein-Gordon\n * system coupled with General Relativity.\n *\n * \\details The \\ref DataBoxGroup associated with\n * KleinGordonCharacteristicEvolution will contain all the tags of\n * CharacteristicEvolution, with additional tags related to the scalar field.\n *\n * Metavariables requirements:\n * - Phases:\n *  - `Initialization`\n *  - `Evolve`\n * - Modified type aliases in comparison to CharacteristicEvolution:\n *  - `evolved_swsh_tags`: The spin-weighted quantities to be evolved (\n * `KleinGordonPsi` and `BondiJ`).\n *  - `evolved_swsh_dt_tags`: The spin-weighed quantities associated that are to\n * act as the time derivative to evolve `evolved_swsh_tags` (`KleinGordonPi` and\n * `BondiH`).\n * - Additional type aliases related to the scalar field:\n *  - `klein_gordon_boundary_communication_tags`:  A typelist of tags that will\n * be communicated between the worldtube boundary component and the extraction\n * component (`Cce::Tags::klein_gordon_worldtube_boundary_tags`).\n *  - `klein_gordon_gauge_boundary_tags`: A typelist of tags that will be\n * derived via `GaugeAdjustedBoundaryValue` and corresponding gauge utilities\n *  - `klein_gordon_scri_tags`: the tags of quantities to compute at scri+\n */\ntemplate <class Metavariables>\nstruct KleinGordonCharacteristicEvolution\n    : CharacteristicEvolution<Metavariables> {\n  using metavariables = Metavariables;\n  static constexpr bool evolve_ccm = Metavariables::evolve_ccm;\n  using cce_system = Cce::KleinGordonSystem<evolve_ccm>;\n\n  using cce_base = CharacteristicEvolution<Metavariables>;\n  using initialize_action_list = tmpl::append<\n      tmpl::list<Actions::InitializeKleinGordonVariables<Metavariables>>,\n      typename cce_base::initialize_action_list>;\n\n  template <typename BondiTag>\n  using hypersurface_computation =\n      typename cce_base::template hypersurface_computation<BondiTag>;\n\n  using klein_gordon_hypersurface_computation = tmpl::list<\n      ::Actions::MutateApply<GaugeAdjustedBoundaryValue<Tags::KleinGordonPi>>,\n      Actions::CalculateIntegrandInputsForTag<Tags::KleinGordonPi>,\n      tmpl::transform<\n          integrand_terms_to_compute_for_bondi_variable<Tags::KleinGordonPi>,\n          tmpl::bind<::Actions::MutateApply,\n                     tmpl::bind<ComputeBondiIntegrand, tmpl::_1>>>>;\n\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using typename cce_base::compute_scri_quantities_and_observe;\n\n  using self_start_extract_action_list = tmpl::list<\n      Actions::RequestBoundaryData<\n          typename Metavariables::cce_boundary_component,\n          KleinGordonCharacteristicEvolution<Metavariables>>,\n      Actions::ReceiveWorldtubeData<\n          Metavariables,\n          typename Metavariables::cce_boundary_communication_tags>,\n      Actions::ReceiveWorldtubeData<\n          Metavariables,\n          typename Metavariables::klein_gordon_boundary_communication_tags>,\n      // note that the initialization will only actually happen on the\n      // iterations immediately following restarts\n      Actions::InitializeFirstHypersurface<\n          evolve_ccm, typename Metavariables::cce_boundary_component>,\n      Actions::InitializeKleinGordonFirstHypersurface,\n      tmpl::conditional_t<\n          tt::is_a_v<AnalyticWorldtubeBoundary,\n                     typename Metavariables::cce_boundary_component>,\n          Actions::UpdateGauge<false>, Actions::UpdateGauge<evolve_ccm>>,\n      Actions::PrecomputeGlobalCceDependencies,\n      tmpl::conditional_t<evolve_ccm,\n                          Actions::CalculatePsi0AndDerivAtInnerBoundary,\n                          tmpl::list<>>,\n      Actions::PrecomputeKleinGordonSourceVariables,\n      tmpl::transform<\n          bondi_hypersurface_step_tags,\n          tmpl::bind<::Actions::MutateApply,\n                     tmpl::bind<ComputeKleinGordonSource, tmpl::_1>>>,\n      tmpl::transform<bondi_hypersurface_step_tags,\n                      tmpl::bind<hypersurface_computation, tmpl::_1>>,\n      klein_gordon_hypersurface_computation,\n      Actions::FilterSwshVolumeQuantity<Tags::BondiH>,\n      Actions::FilterSwshVolumeQuantity<Tags::KleinGordonPi>,\n      ::Actions::MutateApply<\n          CalculateScriPlusValue<::Tags::dt<Tags::InertialRetardedTime>>>,\n      Actions::CalculateScriInputs,\n      tmpl::transform<typename metavariables::cce_scri_tags,\n                      tmpl::bind<::Actions::MutateApply,\n                                 tmpl::bind<CalculateScriPlusValue, tmpl::_1>>>,\n      ::Actions::MutateApply<RecordTimeStepperData<cce_system>>,\n      ::Actions::MutateApply<\n          UpdateU<cce_system, Metavariables::local_time_stepping,\n                  Tags::CceEvolutionPrefix>>>;\n\n  using extract_action_list = tmpl::list<\n      Actions::RequestBoundaryData<\n          typename Metavariables::cce_boundary_component,\n          KleinGordonCharacteristicEvolution<Metavariables>>,\n      ::Actions::Label<CceEvolutionLabelTag>,\n      tmpl::conditional_t<\n          evolve_ccm, tmpl::list<>,\n          tmpl::flatten<tmpl::list<\n              std::conditional_t<Metavariables::local_time_stepping,\n                                 evolution::Actions::RunEventsAndTriggers<\n                                     Triggers::WhenToCheck::AtSteps>,\n                                 tmpl::list<>>,\n              evolution::Actions::RunEventsAndTriggers<\n                  Triggers::WhenToCheck::AtSlabs>>>>,\n      Actions::ReceiveWorldtubeData<\n          Metavariables,\n          typename Metavariables::cce_boundary_communication_tags>,\n      Actions::ReceiveWorldtubeData<\n          Metavariables,\n          typename Metavariables::klein_gordon_boundary_communication_tags>,\n      Actions::InitializeFirstHypersurface<\n          evolve_ccm, typename Metavariables::cce_boundary_component>,\n      Actions::InitializeKleinGordonFirstHypersurface,\n      tmpl::conditional_t<\n          tt::is_a_v<AnalyticWorldtubeBoundary,\n                     typename Metavariables::cce_boundary_component>,\n          Actions::UpdateGauge<false>, Actions::UpdateGauge<evolve_ccm>>,\n      Actions::PrecomputeGlobalCceDependencies,\n      tmpl::conditional_t<evolve_ccm,\n                          Actions::CalculatePsi0AndDerivAtInnerBoundary,\n                          tmpl::list<>>,\n      Actions::PrecomputeKleinGordonSourceVariables,\n      tmpl::transform<\n          bondi_hypersurface_step_tags,\n          tmpl::bind<::Actions::MutateApply,\n                     tmpl::bind<ComputeKleinGordonSource, tmpl::_1>>>,\n      tmpl::transform<bondi_hypersurface_step_tags,\n                      tmpl::bind<hypersurface_computation, tmpl::_1>>,\n      klein_gordon_hypersurface_computation,\n      Actions::FilterSwshVolumeQuantity<Tags::BondiH>,\n      Actions::FilterSwshVolumeQuantity<Tags::KleinGordonPi>,\n      compute_scri_quantities_and_observe,\n      ::Actions::MutateApply<ChangeStepSize<\n          typename Metavariables::cce_step_choosers, Tags::CceEvolutionPrefix>>,\n      ::Actions::MutateApply<RecordTimeStepperData<cce_system>>,\n      ::Actions::MutateApply<\n          UpdateU<cce_system, Metavariables::local_time_stepping,\n                  Tags::CceEvolutionPrefix>>,\n      ::Actions::MutateApply<\n          ChangeTimeStepperOrder<cce_system, Tags::CceEvolutionPrefix>>,\n      ::Actions::MutateApply<\n          CleanHistory<cce_system, Tags::CceEvolutionPrefix>>,\n      // We cannot know our next step for certain until after we've performed\n      // step size selection, as we may need to reject a step.\n      Actions::RequestNextBoundaryData<\n          typename Metavariables::cce_boundary_component,\n          KleinGordonCharacteristicEvolution<Metavariables>>,\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Actions::ExitIfEndTimeReached, ::Actions::Goto<CceEvolutionLabelTag>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<Parallel::Phase::InitializeTimeStepperHistory,\n                             SelfStart::self_start_procedure<\n                                 self_start_extract_action_list, cce_system,\n                                 Tags::CceEvolutionPrefix>>,\n      Parallel::PhaseActions<Parallel::Phase::Evolve, extract_action_list>>;\n\n  static void initialize(\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<\n        KleinGordonCharacteristicEvolution<Metavariables>>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n\nnamespace Cce {\n\n/*!\n * \\brief Generic base class for components that supply CCE worldtube boundary\n * data. See class specializations for specific worldtube boundary components.\n */\ntemplate <typename WorldtubeComponent, typename Metavariables>\nstruct WorldtubeComponentBase {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using initialize_action_list =\n      tmpl::list<Actions::InitializeWorldtubeBoundary<WorldtubeComponent>,\n                 Parallel::Actions::TerminatePhase>;\n\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using worldtube_boundary_computation_steps = tmpl::list<>;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve,\n                                        worldtube_boundary_computation_steps>>;\n\n  using options = tmpl::list<>;\n\n  static void initialize(\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    if (next_phase == Parallel::Phase::Evolve) {\n      Parallel::get_parallel_component<WorldtubeComponent>(local_cache)\n          .start_phase(next_phase);\n    }\n  }\n};\n\n/*!\n * \\brief Component that supplies CCE worldtube boundary data.\n *\n * \\details The \\ref DataBoxGroup associated with the worldtube boundary\n * component contains a data manager (e.g. `WorldtubeDataManager`) linked to\n * an H5 file. The data manager handles buffering and interpolating to desired\n * target time points when requested via the simple action\n * `BoundaryComputeAndSendToEvolution`, at which point it will send the required\n * collection of boundary quantities to the identified 'CharacteristicEvolution'\n * component. It is assumed that the simple action\n * `BoundaryComputeAndSendToEvolution` will only be called during the\n * `Evolve` phase.\n *\n * Uses const global tags:\n * - `Tags::LMax`\n *\n * `Metavariables` must contain:\n * - the `enum` `Phase` with at least `Initialization` and `Evolve` phases.\n * - a type alias `cce_boundary_communication_tags` for the set of tags to send\n * from the worldtube to the evolution component. This will typically be\n * `Cce::Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>`.\n */\ntemplate <class Metavariables>\nstruct H5WorldtubeBoundary\n    : public WorldtubeComponentBase<H5WorldtubeBoundary<Metavariables>,\n                                    Metavariables> {\n  using base_type =\n      WorldtubeComponentBase<H5WorldtubeBoundary<Metavariables>, Metavariables>;\n  using base_type::execute_next_phase;\n  using base_type::initialize;\n  using typename base_type::chare_type;\n  using const_global_cache_tags =\n      tmpl::list<Tags::InitializeJ<Metavariables::evolve_ccm>>;\n  using typename base_type::metavariables;\n  using typename base_type::options;\n  using typename base_type::phase_dependent_action_list;\n  using typename base_type::simple_tags_from_options;\n  using end_time_tag = Tags::EndTimeFromFile;\n};\n\n/*!\n * \\brief Component that supplies scalar-tensor worldtube boundary data.\n *\n * \\details The \\ref DataBoxGroup associated with the worldtube boundary\n * component contains two data managers (`WorldtubeDataManager`) linked to\n * one or two H5 files (scalar and tensor sectors, respectively). The data\n * managers handle buffering and interpolating to desired target time points\n * when requested via the simple action `BoundaryComputeAndSendToEvolution`, at\n * which point they will send the required collection of boundary quantities to\n * the identified `KleinGordonCharacteristicEvolution` component. It is assumed\n * that the simple action `BoundaryComputeAndSendToEvolution` will only be\n * called during the `Evolve` phase.\n *\n * Uses const global tags:\n * - `Tags::LMax`\n *\n * `Metavariables` must contain:\n * - the `enum` `Phase` with at least `Initialization` and `Evolve` phases.\n * - a type alias `cce_boundary_communication_tags` for the set of tensor tags\n * to send from the worldtube to the evolution component. This will typically be\n * `Cce::Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>`.\n * - a type alias `klein_gordon_boundary_communication_tags` for the set of\n * scalar tags to send from the worldtube to the evolution component. This will\n * typically be `Cce::Tags::klein_gordon_worldtube_boundary_tags`.\n */\ntemplate <class Metavariables>\nstruct KleinGordonH5WorldtubeBoundary\n    : public WorldtubeComponentBase<\n          KleinGordonH5WorldtubeBoundary<Metavariables>, Metavariables> {\n  using base_type =\n      WorldtubeComponentBase<KleinGordonH5WorldtubeBoundary<Metavariables>,\n                             Metavariables>;\n  using base_type::execute_next_phase;\n  using base_type::initialize;\n  using typename base_type::chare_type;\n  using const_global_cache_tags =\n      tmpl::list<Tags::InitializeJ<Metavariables::evolve_ccm>>;\n  using typename base_type::metavariables;\n  using typename base_type::options;\n  using typename base_type::phase_dependent_action_list;\n  using typename base_type::simple_tags_from_options;\n  using end_time_tag = Tags::EndTimeFromFile;\n};\n\n/*!\n * \\brief Component that supplies CCE worldtube boundary data sourced from an\n * analytic solution.\n *\n * \\details The \\ref DataBoxGroup associated with the worldtube boundary\n * component contains a data manager (e.g. `AnalyticBoundaryDataManager`) that\n * contains the analytic solution being provided. The data manager handles\n * computation of the analytic solution boundary data when requested via\n * the simple action `BoundaryComputeAndSendToEvolution`, at which point it will\n * send the required collection of boundary quantities to the identified\n * 'CharacteristicEvolution' component. It is assumed that the simple action\n * `BoundaryComputeAndSendToEvolution` will only be called during the\n * `Evolve` phase.\n *\n * Uses const global tags:\n * - `Tags::LMax`\n * - `Tags::SpecifiedEndTime`\n *\n * `Metavariables` must contain:\n * - the `enum` `Phase` with at least `Initialization` and `Evolve` phases.\n * - a type alias `cce_boundary_communication_tags` for the set of tags to send\n *   from the worldtube to the evolution component. This will typically be\n *   `Cce::Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>`.\n */\ntemplate <class Metavariables>\nstruct AnalyticWorldtubeBoundary\n    : public WorldtubeComponentBase<AnalyticWorldtubeBoundary<Metavariables>,\n                                    Metavariables> {\n  using base_type =\n      WorldtubeComponentBase<AnalyticWorldtubeBoundary<Metavariables>,\n                             Metavariables>;\n  using base_type::execute_next_phase;\n  using base_type::initialize;\n  using typename base_type::chare_type;\n  using const_global_cache_tags = tmpl::list<Tags::AnalyticInitializeJ>;\n  using typename base_type::metavariables;\n  using typename base_type::options;\n  using typename base_type::phase_dependent_action_list;\n  using typename base_type::simple_tags_from_options;\n  using end_time_tag = Tags::SpecifiedEndTime;\n};\n\n/*!\n * \\brief Component that supplies CCE worldtube boundary data sourced from a\n * running GH system.\n *\n * \\details The \\ref DataBoxGroup associated with the worldtube boundary\n * component contains an interface manager (derived from\n * `Cce::GhWorldtubeInterfaceManager`) that stores and provides the data\n * received from the GH system. The data manager handles buffering\n * and interpolating to desired target time points when requested via the simple\n * action `Cce::Actions::BoundaryComputeAndSendToEvolution`, at which point it\n * will send the required collection of boundary quantities to the identified\n * `CharacteristicEvolution` component. It is assumed that the simple action\n * `Cce::Actions::BoundaryComputeAndSendToEvolution` will only be called during\n * the `Evolve` phase.\n *\n * Uses const global tags:\n * - `InitializationTags::LMax`\n * - `InitializationTags::ExtractionRadius`\n *\n * `Metavariables` must contain:\n * - the `enum` `Phase` with at least `Initialization` and `Evolve` phases.\n * - a type alias `cce_boundary_communication_tags` for the set of tags to send\n * from the worldtube to the evolution component. This will typically be\n * `Cce::Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>`.\n */\ntemplate <class Metavariables>\nstruct GhWorldtubeBoundary\n    : public WorldtubeComponentBase<GhWorldtubeBoundary<Metavariables>,\n                                    Metavariables> {\n  using base_type =\n      WorldtubeComponentBase<GhWorldtubeBoundary<Metavariables>, Metavariables>;\n  using base_type::execute_next_phase;\n  using base_type::initialize;\n  using typename base_type::chare_type;\n  using const_global_cache_tags =\n      tmpl::list<Tags::InitializeJ<Metavariables::evolve_ccm>>;\n  using typename base_type::metavariables;\n  using typename base_type::options;\n  using typename base_type::phase_dependent_action_list;\n  using typename base_type::simple_tags_from_options;\n  using end_time_tag = Tags::NoEndTime;\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/Equations.hpp\"\n\n#include <complex>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace Cce {\n\nnamespace detail {\nvoid klein_gordon_rhs_npsi1(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k) {\n  (*result).data() = (2. * real(eth_beta.data() * conj(eth_kg_psi).data()) +\n                      eth_ethbar_kg_psi.data()) *\n                     bondi_k.data();\n}\n\nvoid klein_gordon_rhs_npsi2(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 2>& eth_eth_kg_psi,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j) {\n  (*result).data() = real(conj(j).data() * eth_eth_kg_psi.data());\n  (*result).data() +=\n      2 * real(conj(eth_beta).data() * conj(eth_kg_psi).data() * j.data());\n  (*result).data() += real(ethbar_j.data() * conj(eth_kg_psi).data());\n}\n\nvoid klein_gordon_rhs_npsi3(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 1>& eth_k,\n    const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi) {\n  (*result).data() = real(eth_k.data() * conj(eth_kg_psi).data());\n}\n\nvoid klein_gordon_rhs_npsi4_divided_by_one_minues_y_squared(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n    const SpinWeighted<ComplexDataVector, 1>& dy_bondi_u,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_u,\n    const SpinWeighted<ComplexDataVector, 1>& eth_dy_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& dy_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r) {\n  auto dr_u = 0.5 / bondi_r * dy_bondi_u;\n  auto dr_psi = 0.5 / bondi_r * dy_kg_psi;\n  auto eth_dr_psi =\n      0.5 / bondi_r * (eth_dy_kg_psi + eth_r_divided_by_r * dy_kg_psi);\n\n  auto res = conj(eth_kg_psi) * dr_u + conj(ethbar_u) * dr_psi +\n             2.0 * conj(bondi_u) * eth_dr_psi;\n  (*result).data() = 2.0 * real(res.data());\n}\n\nvoid klein_gordon_rhs_tau(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y,\n    const SpinWeighted<ComplexDataVector, 0>& dy_w,\n    const SpinWeighted<ComplexDataVector, 0>& dy_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& dy_dy_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_w,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r) {\n  *result = one_minus_y * dy_w * dy_kg_psi +\n            0.5 * square(one_minus_y) / bondi_r * dy_dy_kg_psi +\n            one_minus_y * bondi_w * dy_dy_kg_psi + bondi_w * dy_kg_psi;\n  (*result).data() *= 0.5;\n}\n}  // namespace detail\n\n// suppresses doxygen problems with these functions\n\nvoid ComputeBondiIntegrand<Tags::Integrand<Tags::BondiBeta>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> integrand_for_beta,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n  *integrand_for_beta =\n      0.125 * one_minus_y *\n      (dy_j * conj(dy_j) -\n       0.25 * square(j * conj(dy_j) + conj(j) * dy_j) / (1.0 + j * conj(j)));\n}\n\nvoid ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::BondiQ>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*>\n        pole_of_integrand_for_q,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta) {\n  *pole_of_integrand_for_q = -4.0 * eth_beta;\n}\n\nvoid ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::BondiQ>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*>\n        regular_integrand_for_q,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> script_aq,\n    const SpinWeighted<ComplexDataVector, 0>& dy_beta,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 1>& eth_dy_beta,\n    const SpinWeighted<ComplexDataVector, 1>& eth_j_jbar,\n    const SpinWeighted<ComplexDataVector, 1>& eth_jbar_dy_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_dy_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n    const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& k) {\n  *script_aq =\n      0.25 * (j * conj(ethbar_dy_j) - eth_jbar_dy_j - conj(ethbar_j) * dy_j +\n              0.5 * eth_j_jbar * (conj(j) * dy_j + j * conj(dy_j)) /\n                  (1.0 + j * conj(j)) -\n              (conj(j) * dy_j - j * conj(dy_j)) * eth_r_divided_by_r);\n\n  *regular_integrand_for_q =\n      -2.0 * (*script_aq + j * conj(*script_aq) / k - eth_dy_beta +\n              0.5 * ethbar_dy_j / k - dy_beta * eth_r_divided_by_r +\n              0.5 * dy_j * conj(eth_r_divided_by_r) / k);\n}\n\nvoid ComputeBondiIntegrand<Tags::Integrand<Tags::BondiU>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*>\n        regular_integrand_for_u,\n    const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 1>& q,\n    const SpinWeighted<ComplexDataVector, 0>& k,\n    const SpinWeighted<ComplexDataVector, 0>& r) {\n  *regular_integrand_for_u = 0.5 * exp_2_beta / r * (k * q - j * conj(q));\n}\n\nvoid ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::BondiW>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n        pole_of_integrand_for_w,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_u) {\n  *pole_of_integrand_for_w = ethbar_u + conj(ethbar_u);\n}\n\nvoid ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::BondiW>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n        regular_integrand_for_w,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> script_av,\n    const SpinWeighted<ComplexDataVector, 1>& dy_u,\n    const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 1>& q,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 2>& eth_eth_beta,\n    const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_beta,\n    const SpinWeighted<ComplexDataVector, 2>& eth_ethbar_j,\n    const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_j_jbar,\n    const SpinWeighted<ComplexDataVector, 1>& eth_j_jbar,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_dy_u,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_ethbar_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n    const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& k,\n    const SpinWeighted<ComplexDataVector, 0>& r) {\n  // this computation is split over two lines because GCC-10 on release mode\n  // optimizes the long expression templates in such a way to cause segfaults.\n  *script_av =\n      eth_beta * conj(ethbar_j) + 0.5 * ethbar_ethbar_j +\n      j * square(conj(eth_beta)) + j * conj(eth_eth_beta) +\n      0.125 * eth_j_jbar * conj(eth_j_jbar) / (k * (1.0 + j * conj(j))) +\n      0.5 *\n          (1.0 - 0.25 * eth_ethbar_j_jbar - eth_j_jbar * conj(eth_beta) -\n           0.5 * conj(ethbar_j) * ethbar_j - 0.5 * conj(j) * eth_ethbar_j) /\n      k;\n  *script_av += k * (0.5 - eth_ethbar_beta - eth_beta * conj(eth_beta) -\n                     0.25 * q * conj(q)) +\n                0.25 * j * square(conj(q));\n\n  *regular_integrand_for_w =\n      0.5 * (0.5 * (ethbar_dy_u + conj(ethbar_dy_u) +\n                    conj(dy_u) * eth_r_divided_by_r +\n                    dy_u * conj(eth_r_divided_by_r)) -\n             1.0 / r + 0.5 * exp_2_beta * (*script_av + conj(*script_av)) / r);\n}\n\nvoid ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::BondiH>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*>\n        pole_of_integrand_for_h,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 1>& u,\n    const SpinWeighted<ComplexDataVector, 0>& w,\n    const SpinWeighted<ComplexDataVector, 2>& eth_u,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n    const SpinWeighted<ComplexDataVector, -2>& ethbar_jbar_u,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n    const SpinWeighted<ComplexDataVector, 0>& k) {\n  *pole_of_integrand_for_h = -0.5 * conj(ethbar_jbar_u) - j * conj(ethbar_u) -\n                             0.5 * j * ethbar_u - k * eth_u -\n                             0.5 * u * ethbar_j + 2.0 * j * w;\n}\n\nvoid ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::BondiH>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*>\n        regular_integrand_for_h,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> script_aj,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> script_bj,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> script_cj,\n    const SpinWeighted<ComplexDataVector, 2>& dy_dy_j,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 0>& dy_w,\n    const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 1>& q,\n    const SpinWeighted<ComplexDataVector, 1>& u,\n    const SpinWeighted<ComplexDataVector, 0>& w,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 2>& eth_eth_beta,\n    const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_beta,\n    const SpinWeighted<ComplexDataVector, 2>& eth_ethbar_j,\n    const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_j_jbar,\n    const SpinWeighted<ComplexDataVector, 1>& eth_j_jbar,\n    const SpinWeighted<ComplexDataVector, 2>& eth_q,\n    const SpinWeighted<ComplexDataVector, 2>& eth_u,\n    const SpinWeighted<ComplexDataVector, 2>& eth_ubar_dy_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_dy_j,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_ethbar_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n    const SpinWeighted<ComplexDataVector, -1>& ethbar_jbar_dy_j,\n    const SpinWeighted<ComplexDataVector, -2>& ethbar_jbar_q_minus_2_eth_beta,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_q,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n    const SpinWeighted<ComplexDataVector, 0>& du_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& k,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y,\n    const SpinWeighted<ComplexDataVector, 0>& r) {\n  *script_aj =\n      0.25 *\n      (conj(ethbar_ethbar_j) -\n       0.25 * (4.0 + eth_ethbar_j_jbar - j * conj(eth_ethbar_j)) /\n           (k * (1.0 + j * conj(j))) +\n       (3.0 - eth_ethbar_beta -\n        conj(j) * eth_ethbar_j * (1.0 - 0.25 / (1.0 + j * conj(j)))) /\n           k +\n       conj(ethbar_j) * (2.0 * eth_beta +\n                         0.5 *\n                             (j * conj(eth_j_jbar) -\n                              ethbar_j * (2.0 * (1.0 + j * conj(j)) - 1.0)) /\n                             (k * (1.0 + j * conj(j))) -\n                         q));\n\n  // this computation is split over multiple lines because GCC-10 on release\n  // mode optimizes the long expression templates in such a way to cause\n  // segfaults.\n  *script_bj =\n      0.25 * (2.0 * dy_w -\n              conj(j) * eth_u * (conj(j) * dy_j + j * conj(dy_j)) / k +\n              1.0 / r + u * ethbar_j * conj(dy_j) -\n              0.5 * u * conj(eth_j_jbar) * (conj(j) * dy_j + j * conj(dy_j)) /\n                  (1.0 + j * conj(j)) +\n              conj(u) * (conj(ethbar_jbar_dy_j) - j * conj(ethbar_dy_j)));\n  *script_bj +=\n      square(one_minus_y) * 0.125 *\n      (0.25 * square(conj(j) * dy_j + j * conj(dy_j)) / (1.0 + j * conj(j)) -\n       dy_j * conj(dy_j)) /\n      r;\n  *script_bj +=\n      one_minus_y * 0.25 *\n      (du_r_divided_by_r * dy_j *\n           (conj(j) * (conj(j) * dy_j + j * conj(dy_j)) / (1.0 + j * conj(j)) -\n            2.0 * conj(dy_j)) -\n       w * (dy_j * conj(dy_j) - 0.25 *\n                                    square((conj(j) * dy_j + j * conj(dy_j))) /\n                                    (1.0 + j * conj(j))));\n\n  *script_cj = 0.5 * ethbar_j * k * (eth_beta - 0.5 * q);\n\n  *regular_integrand_for_h =\n      j * (*script_bj + conj(*script_bj)) -\n      0.5 * (eth_ubar_dy_j + u * ethbar_dy_j +\n             u * dy_j * conj(eth_r_divided_by_r)) +\n      0.5 * exp_2_beta / r *\n          (*script_cj + square(j) / (1.0 + j * conj(j)) * conj(*script_cj) -\n           j * (*script_aj + conj(*script_aj)) + eth_eth_beta - 0.5 * eth_q +\n           0.25 * (conj(ethbar_jbar_q_minus_2_eth_beta) - j * conj(ethbar_q)) /\n               k +\n           square(eth_beta - 0.5 * q)) -\n      dy_j * 0.5 *\n          (conj(j) * eth_u / k - j * k * conj(eth_u) +\n           0.5 * (1.0 + j * conj(j)) * (conj(ethbar_u) - ethbar_u) +\n           conj(u) * eth_r_divided_by_r - w) +\n      conj(dy_j) * (0.5 * j * eth_u * (j * conj(j) / k) -\n                    0.25 * square(j) * (ethbar_u - conj(ethbar_u))) +\n      one_minus_y *\n          (0.5 * (dy_dy_j * (w + 2.0 * du_r_divided_by_r) - dy_j / r) +\n           0.5 * dy_j * (dy_w + 1.0 / r)) +\n      square(one_minus_y) * 0.25 * dy_dy_j / r;\n}\n\nvoid ComputeBondiIntegrand<Tags::LinearFactor<Tags::BondiH>>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n        linear_factor_for_h,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> script_djbar,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n  *script_djbar = 0.25 * one_minus_y *\n                  (-2.0 * dy_j +\n                   j * (conj(j) * dy_j + j * conj(dy_j)) / (1.0 + j * conj(j)));\n  *linear_factor_for_h = 1.0 + j * conj(*script_djbar);\n}\n\nvoid ComputeBondiIntegrand<Tags::LinearFactorForConjugate<Tags::BondiH>>::\n    apply_impl(\n        const gsl::not_null<SpinWeighted<ComplexDataVector, 4>*>\n            linear_factor_for_conjugate_h,\n        const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> script_djbar,\n        const SpinWeighted<ComplexDataVector, 2>& dy_j,\n        const SpinWeighted<ComplexDataVector, 2>& j,\n        const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n  *script_djbar = 0.25 * one_minus_y *\n                  (-2.0 * dy_j +\n                   j * (conj(j) * dy_j + j * conj(dy_j)) / (1.0 + j * conj(j)));\n  *linear_factor_for_conjugate_h = j * (*script_djbar);\n}\n\nvoid ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::KleinGordonPi>>::\n    apply_impl(gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n                   pole_of_integrand_for_kg_pi,\n               const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n               const SpinWeighted<ComplexDataVector, 1>& bondi_u) {\n  *pole_of_integrand_for_kg_pi =\n      -real(bondi_u.data() * conj(eth_kg_psi).data());\n}\n\nvoid ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::KleinGordonPi>>::\n    apply_impl(gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n                   regular_integrand_for_kg_pi,\n               // pre_swsh_derivative_tags\n               const SpinWeighted<ComplexDataVector, 0>& dy_dy_kg_psi,\n               const SpinWeighted<ComplexDataVector, 0>& dy_kg_psi,\n               const SpinWeighted<ComplexDataVector, 1>& dy_bondi_u,\n               const SpinWeighted<ComplexDataVector, 0>& dy_w,\n               // swsh_derivative_tags\n               const SpinWeighted<ComplexDataVector, 1>& eth_dy_kg_psi,\n               const SpinWeighted<ComplexDataVector, 2>& eth_eth_kg_psi,\n               const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n               const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n               const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n               const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n               const SpinWeighted<ComplexDataVector, 1>& eth_j_jbar,\n               const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_kg_psi,\n               // integration_independent_tags\n               const SpinWeighted<ComplexDataVector, 2>& j,\n               const SpinWeighted<ComplexDataVector, 0>& exp2beta,\n               const SpinWeighted<ComplexDataVector, 0>& du_r_divided_by_r,\n               const SpinWeighted<ComplexDataVector, 0>& one_minus_y,\n               const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n               const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n               const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n               const SpinWeighted<ComplexDataVector, 1>& bondi_u,\n               const SpinWeighted<ComplexDataVector, 0>& bondi_w) {\n  SpinWeighted<ComplexDataVector, 1> eth_k = 0.5 * eth_j_jbar / bondi_k;\n  // `from_lhs` comes from switching \\Pi to \\breve{\\Pi} on the left-hand side\n  SpinWeighted<ComplexDataVector, 0> from_lhs =\n      du_r_divided_by_r * one_minus_y * dy_dy_kg_psi;\n\n  SpinWeighted<ComplexDataVector, 0> n_psi1;\n  SpinWeighted<ComplexDataVector, 0> n_psi2;\n  SpinWeighted<ComplexDataVector, 0> n_psi3;\n  SpinWeighted<ComplexDataVector, 0> n_psi4;\n  SpinWeighted<ComplexDataVector, 0> tau;\n\n  detail::klein_gordon_rhs_npsi1(make_not_null(&n_psi1), eth_beta, eth_kg_psi,\n                                 eth_ethbar_kg_psi, bondi_k);\n  detail::klein_gordon_rhs_npsi2(make_not_null(&n_psi2), j, eth_eth_kg_psi,\n                                 eth_beta, eth_kg_psi, ethbar_j);\n  detail::klein_gordon_rhs_npsi3(make_not_null(&n_psi3), eth_k, eth_kg_psi);\n  detail::klein_gordon_rhs_npsi4_divided_by_one_minues_y_squared(\n      make_not_null(&n_psi4), eth_kg_psi, dy_bondi_u, ethbar_u, bondi_u,\n      eth_dy_kg_psi, dy_kg_psi, bondi_r, eth_r_divided_by_r);\n  detail::klein_gordon_rhs_tau(make_not_null(&tau), one_minus_y, dy_w,\n                               dy_kg_psi, dy_dy_kg_psi, bondi_w, bondi_r);\n\n  *regular_integrand_for_kg_pi =\n      0.25 * exp2beta / bondi_r * (n_psi1 - n_psi2 + n_psi3) -\n      0.5 * bondi_r * n_psi4 + tau + from_lhs;\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\n\nnamespace detail {\ntemplate <typename BondiVariable>\nstruct integrand_terms_to_compute_for_bondi_variable_impl;\n\n// template specializations for the individual tags\ntemplate <>\nstruct integrand_terms_to_compute_for_bondi_variable_impl<Tags::BondiBeta> {\n  using type = tmpl::list<Tags::Integrand<Tags::BondiBeta>>;\n};\ntemplate <>\nstruct integrand_terms_to_compute_for_bondi_variable_impl<Tags::BondiQ> {\n  using type = tmpl::list<Tags::PoleOfIntegrand<Tags::BondiQ>,\n                          Tags::RegularIntegrand<Tags::BondiQ>>;\n};\ntemplate <>\nstruct integrand_terms_to_compute_for_bondi_variable_impl<Tags::BondiU> {\n  using type = tmpl::list<Tags::Integrand<Tags::BondiU>>;\n};\ntemplate <>\nstruct integrand_terms_to_compute_for_bondi_variable_impl<Tags::BondiW> {\n  using type = tmpl::list<Tags::PoleOfIntegrand<Tags::BondiW>,\n                          Tags::RegularIntegrand<Tags::BondiW>>;\n};\ntemplate <>\nstruct integrand_terms_to_compute_for_bondi_variable_impl<Tags::BondiH> {\n  using type = tmpl::list<Tags::PoleOfIntegrand<Tags::BondiH>,\n                          Tags::RegularIntegrand<Tags::BondiH>,\n                          Tags::LinearFactor<Tags::BondiH>,\n                          Tags::LinearFactorForConjugate<Tags::BondiH>>;\n};\n\ntemplate <>\nstruct integrand_terms_to_compute_for_bondi_variable_impl<Tags::KleinGordonPi> {\n  using type = tmpl::list<Tags::PoleOfIntegrand<Tags::KleinGordonPi>,\n                          Tags::RegularIntegrand<Tags::KleinGordonPi>>;\n};\n\nvoid klein_gordon_rhs_npsi1(\n    gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k);\n\nvoid klein_gordon_rhs_npsi2(\n    gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 2>& j,\n    const SpinWeighted<ComplexDataVector, 2>& eth_eth_kg_psi,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j);\n\nvoid klein_gordon_rhs_npsi3(\n    gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 1>& eth_k,\n    const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi);\n\nvoid klein_gordon_rhs_npsi4_divided_by_one_minues_y_squared(\n    gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n    const SpinWeighted<ComplexDataVector, 1>& dy_bondi_u,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_u,\n    const SpinWeighted<ComplexDataVector, 1>& eth_dy_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& dy_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r);\n\nvoid klein_gordon_rhs_tau(\n    gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> result,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y,\n    const SpinWeighted<ComplexDataVector, 0>& dy_w,\n    const SpinWeighted<ComplexDataVector, 0>& dy_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& dy_dy_kg_psi,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_w,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r);\n}  // namespace detail\n\n/// \\brief A struct for providing a `tmpl::list` of integrand tags that need to\n/// be computed before integration can proceed for a given Bondi variable tag.\ntemplate <typename BondiVariable>\nusing integrand_terms_to_compute_for_bondi_variable =\n    typename detail::integrand_terms_to_compute_for_bondi_variable_impl<\n        BondiVariable>::type;\n\n/*!\n * \\brief Computes one of the inputs for the integration of one of the\n * Characteristic hypersurface equations.\n *\n * \\details The template argument must be one of the integrand-related prefix\n * tags templated on a Bondi quantity tag for which that integrand is required.\n * The relevant prefix tags are `Tags::Integrand`, `Tags::PoleOfIntegrand`,\n * `Tags::RegularIntegrand`, `Tags::LinearFactor`, and\n * `Tags::LinearFactorForConjugate`. The Bondi quantity tags that these tags may\n * wrap are `Tags::BondiBeta`, `Tags::BondiQ`, `Tags::BondiU`, `Tags::BondiW`,\n * and `Tags::BondiH`.\n *\n * The integrand terms which may be computed for a given Bondi variable are\n * enumerated in the type alias `integrand_terms_to_compute_for_bondi_variable`,\n * which takes as a single template argument the tag for which integrand terms\n * would be computed, and is a `tmpl::list` of the integrand terms needed.\n *\n * The resulting quantity is returned by `not_null` pointer, and the required\n * argument tags are given in `return_tags` and `argument_tags` type aliases,\n * where the `return_tags` are passed by `not_null` pointer (so include\n * temporary buffers) and the `argument_tags` are passed by const reference.\n *\n * Additional mathematical details for each of the computations can be found in\n * the template specializations of this struct. All of the specializations have\n * a static `apply` function which takes as arguments\n * `SpinWeighted<ComplexDataVector, N>`s for each of the Bondi arguments.\n */\ntemplate <typename IntegrandTag>\nstruct ComputeBondiIntegrand;\n\n/*!\n * \\brief Computes the integrand (right-hand side) of the equation which\n * determines the radial (y) dependence of the Bondi quantity \\f$\\beta\\f$.\n *\n * \\details The quantity \\f$\\beta\\f$ is defined via the Bondi form of the\n * metric:\n * \\f[\n * ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A\n * \\bar{q}^B.\\f]\n * See \\cite Bishop1997ik \\cite Handmer2014qha for full details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$\\beta\\f$ on a surface of\n * constant \\f$u\\f$ given \\f$J\\f$ on the same surface is\n * \\f[\\partial_y (\\beta) =\n * \\frac{1}{8} (-1 + y) \\left(\\partial_y (J) \\partial_y(\\bar{J})\n * - \\frac{(\\partial_y (J \\bar{J}))^2}{4 K^2}\\right). \\f]\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::Integrand<Tags::BondiBeta>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiJ>, Tags::BondiJ>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<Tags::OneMinusY>;\n  using temporary_tags = tmpl::list<>;\n\n  using return_tags = tmpl::append<tmpl::list<Tags::Integrand<Tags::BondiBeta>>,\n                                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          integrand_for_beta,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*integrand_for_beta)), get(args)...);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> integrand_for_beta,\n      const SpinWeighted<ComplexDataVector, 2>& dy_j,\n      const SpinWeighted<ComplexDataVector, 2>& j,\n      const SpinWeighted<ComplexDataVector, 0>& one_minus_y);\n};\n\n/*!\n * \\brief Computes the pole part of the integrand (right-hand side) of the\n * equation which determines the radial (y) dependence of the Bondi quantity\n * \\f$Q\\f$.\n *\n * \\details The quantity \\f$Q\\f$ is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B,\\f]\n * and \\f$Q\\f$ is defined as a supplemental variable for radial integration of\n * \\f$U\\f$:\n * \\f[ Q_A = r^2 e^{-2\\beta} h_{AB} \\partial_r U^B\\f]\n * and \\f$Q = Q_A q^A\\f$. See \\cite Bishop1997ik \\cite Handmer2014qha for full\n * details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$Q\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$ and \\f$\\beta\\f$ on the same surface is written as\n * \\f[(1 - y) \\partial_y Q + 2 Q = A_Q + (1 - y) B_Q.\\f]\n * We refer to \\f$A_Q\\f$ as the \"pole part\" of the integrand and \\f$B_Q\\f$\n * as the \"regular part\". The pole part is computed by this function, and has\n * the expression\n * \\f[A_Q = -4 \\eth \\beta.\\f]\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::BondiQ>> {\n public:\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>>;\n  using integration_independent_tags = tmpl::list<>;\n  using temporary_tags = tmpl::list<>;\n\n  using return_tags =\n      tmpl::append<tmpl::list<Tags::PoleOfIntegrand<Tags::BondiQ>>,\n                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          pole_of_integrand_for_q,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*pole_of_integrand_for_q)), get(args)...);\n  }\n\n private:\n  static void apply_impl(gsl::not_null<SpinWeighted<ComplexDataVector, 1>*>\n                             pole_of_integrand_for_q,\n                         const SpinWeighted<ComplexDataVector, 1>& eth_beta);\n};\n\n/*!\n * \\brief Computes the regular part of the integrand (right-hand side) of the\n * equation which determines the radial (y) dependence of the Bondi quantity\n * \\f$Q\\f$.\n *\n * \\details The quantity \\f$Q\\f$ is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B,\\f]\n * and \\f$Q\\f$ is defined as a supplemental variable for radial integration of\n * \\f$U\\f$:\n * \\f[ Q_A = r^2 e^{-2\\beta} h_{AB} \\partial_r U^B\\f]\n * and \\f$Q = Q_A q^A\\f$. See \\cite Bishop1997ik \\cite Handmer2014qha for\n * full details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$Q\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$ and \\f$\\beta\\f$ on the same surface is written as\n * \\f[(1 - y) \\partial_y Q + 2 Q = A_Q + (1 - y) B_Q. \\f]\n * We refer to \\f$A_Q\\f$ as the \"pole part\" of the integrand and \\f$B_Q\\f$ as\n * the \"regular part\". The regular part is computed by this function, and has\n * the expression\n * \\f[  B_Q = - \\left(2 \\mathcal{A}_Q + \\frac{2\n * \\bar{\\mathcal{A}_Q} J}{K} -  2 \\partial_y (\\eth (\\beta)) +\n * \\frac{\\partial_y (\\bar{\\eth} (J))}{K}\\right), \\f]\n * where\n * \\f[ \\mathcal{A}_Q = - \\tfrac{1}{4} \\eth (\\bar{J} \\partial_y (J)) +\n * \\tfrac{1}{4} J \\partial_y (\\eth (\\bar{J})) -  \\tfrac{1}{4} \\eth (\\bar{J})\n * \\partial_y (J) + \\frac{\\eth (J \\bar{J}) \\partial_y (J \\bar{J})}{8 K^2} -\n * \\frac{\\bar{J} \\eth (R) \\partial_y (J)}{4 R}. \\f].\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::BondiQ>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiBeta>, Tags::Dy<Tags::BondiJ>,\n                 Tags::BondiJ>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiJ>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n  using integration_independent_tags =\n      tmpl::list<Tags::EthRDividedByR, Tags::BondiK>;\n  using temporary_tags =\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>>;\n\n  using return_tags =\n      tmpl::append<tmpl::list<Tags::RegularIntegrand<Tags::BondiQ>>,\n                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          regular_integrand_for_q,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          script_aq,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*regular_integrand_for_q)),\n               make_not_null(&get(*script_aq)), get(args)...);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 1>*>\n          regular_integrand_for_q,\n      gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> script_aq,\n      const SpinWeighted<ComplexDataVector, 0>& dy_beta,\n      const SpinWeighted<ComplexDataVector, 2>& dy_j,\n      const SpinWeighted<ComplexDataVector, 2>& j,\n      const SpinWeighted<ComplexDataVector, 1>& eth_dy_beta,\n      const SpinWeighted<ComplexDataVector, 1>& eth_j_jbar,\n      const SpinWeighted<ComplexDataVector, 1>& eth_jbar_dy_j,\n      const SpinWeighted<ComplexDataVector, 1>& ethbar_dy_j,\n      const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n      const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n      const SpinWeighted<ComplexDataVector, 0>& k);\n};\n\n/*!\n * \\brief Computes the integrand (right-hand side) of the equation which\n * determines the radial (y) dependence of the Bondi quantity \\f$U\\f$.\n *\n * \\details The quantity \\f$U\\f$ is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B,\\f]\n * and \\f$Q\\f$ is defined as a supplemental variable for radial integration of\n * \\f$U\\f$:\n * \\f[ Q_A = r^2 e^{-2\\beta} h_{AB} \\partial_r U^B\\f]\n * and \\f$U = U_A q^A\\f$. See \\cite Bishop1997ik \\cite Handmer2014qha for full\n * details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$U\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$, \\f$\\beta\\f$, and \\f$Q\\f$ on the same surface is\n * written as\n * \\f[\\partial_y U = \\frac{e^{2\\beta}}{2 R} (K Q - J \\bar{Q}). \\f]\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::Integrand<Tags::BondiU>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Exp2Beta, Tags::BondiJ, Tags::BondiQ>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<Tags::BondiK, Tags::BondiR>;\n  using temporary_tags = tmpl::list<>;\n\n  using return_tags =\n      tmpl::append<tmpl::list<Tags::Integrand<Tags::BondiU>>, temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          regular_integrand_for_u,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*regular_integrand_for_u)), get(args)...);\n  }\n\n private:\n  static void apply_impl(gsl::not_null<SpinWeighted<ComplexDataVector, 1>*>\n                             regular_integrand_for_u,\n                         const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n                         const SpinWeighted<ComplexDataVector, 2>& j,\n                         const SpinWeighted<ComplexDataVector, 1>& q,\n                         const SpinWeighted<ComplexDataVector, 0>& k,\n                         const SpinWeighted<ComplexDataVector, 0>& r);\n};\n\n/*!\n * \\brief Computes the pole part of the integrand (right-hand side) of the\n * equation which determines the radial (y) dependence of the Bondi quantity\n * \\f$W\\f$.\n *\n * \\details The quantity \\f$W\\f$ is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B.\\f]\n * See \\cite Bishop1997ik \\cite Handmer2014qha for full details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$W\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$,\\f$\\beta\\f$, \\f$Q\\f$, and \\f$U\\f$ on the same surface\n * is written as\n * \\f[(1 - y) \\partial_y W + 2 W = A_W + (1 - y) B_W.\\f] We refer\n * to \\f$A_W\\f$ as the \"pole part\" of the integrand and \\f$B_W\\f$ as the\n * \"regular part\". The pole part is computed by this function, and has the\n * expression\n * \\f[A_W = \\eth (\\bar{U}) + \\bar{\\eth} (U).\\f]\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::BondiW>> {\n public:\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<Spectral::Swsh::Tags::Derivative<\n      Tags::BondiU, Spectral::Swsh::Tags::Ethbar>>;\n  using integration_independent_tags = tmpl::list<>;\n  using temporary_tags = tmpl::list<>;\n\n  using return_tags =\n      tmpl::append<tmpl::list<Tags::PoleOfIntegrand<Tags::BondiW>>,\n                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          pole_of_integrand_for_w,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*pole_of_integrand_for_w)), get(args)...);\n  }\n\n private:\n  static void apply_impl(gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n                             pole_of_integrand_for_w,\n                         const SpinWeighted<ComplexDataVector, 0>& ethbar_u);\n};\n\n/*!\n * \\brief Computes the regular part of the integrand (right-hand side) of the\n * equation which determines the radial (y) dependence of the Bondi quantity\n * \\f$W\\f$.\n *\n * \\details The quantity \\f$W\\f$ is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B,\\f]\n * See \\cite Bishop1997ik \\cite Handmer2014qha for full details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$W\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$, \\f$\\beta\\f$, \\f$Q\\f$, \\f$U\\f$ on the same surface is\n * written as\n * \\f[(1 - y) \\partial_y W + 2 W = A_W + (1 - y) B_W. \\f]\n * We refer to \\f$A_W\\f$ as the \"pole part\" of the integrand and \\f$B_W\\f$ as\n * the \"regular part\". The regular part is computed by this function, and has\n * the expression\n * \\f[  B_W = \\tfrac{1}{4} \\partial_y (\\eth (\\bar{U})) + \\tfrac{1}{4} \\partial_y\n * (\\bar{\\eth} (U)) -  \\frac{1}{2 R} + \\frac{e^{2 \\beta} (\\mathcal{A}_W +\n * \\bar{\\mathcal{A}_W})}{4 R}, \\f]\n * where\n * \\f{align*}\n * \\mathcal{A}_W =& - \\eth (\\beta) \\eth (\\bar{J}) + \\tfrac{1}{2} \\bar{\\eth}\n * (\\bar{\\eth} (J)) + 2 \\bar{\\eth} (\\beta) \\bar{\\eth} (J) + (\\bar{\\eth}\n * (\\beta))^2 J + \\bar{\\eth}\n * (\\bar{\\eth} (\\beta)) J + \\frac{\\eth (J \\bar{J}) \\bar{\\eth} (J \\bar{J})}{8\n * K^3} + \\frac{1}{2 K} -  \\frac{\\eth (\\bar{\\eth} (J \\bar{J}))}{8 K} -\n * \\frac{\\eth (J\n * \\bar{J}) \\bar{\\eth} (\\beta)}{2 K} \\nonumber \\\\\n * &-  \\frac{\\eth (\\bar{J}) \\bar{\\eth} (J)}{4 K} -  \\frac{\\eth (\\bar{\\eth} (J))\n * \\bar{J}}{4 K} + \\tfrac{1}{2} K  -  \\eth (\\bar{\\eth} (\\beta)) K -  \\eth\n * (\\beta) \\bar{\\eth} (\\beta) K + \\tfrac{1}{4} (- K Q \\bar{Q} + J \\bar{Q}^2).\n * \\f}\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::BondiW>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiU>, Tags::Exp2Beta, Tags::BondiJ,\n                 Tags::BondiQ>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::EthEth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::EthEthbar>,\n      Spectral::Swsh::Tags::Derivative<\n          Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                           Spectral::Swsh::Tags::Ethbar>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n          Spectral::Swsh::Tags::EthEthbar>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiU>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::EthbarEthbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n  using integration_independent_tags =\n      tmpl::list<Tags::EthRDividedByR, Tags::BondiK, Tags::BondiR>;\n  using temporary_tags =\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>>;\n\n  using return_tags =\n      tmpl::append<tmpl::list<Tags::RegularIntegrand<Tags::BondiW>>,\n                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          regular_integrand_for_w,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          script_av,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*regular_integrand_for_w)),\n               make_not_null(&get(*script_av)), get(args)...);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n          regular_integrand_for_w,\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> script_av,\n      const SpinWeighted<ComplexDataVector, 1>& dy_u,\n      const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n      const SpinWeighted<ComplexDataVector, 2>& j,\n      const SpinWeighted<ComplexDataVector, 1>& q,\n      const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n      const SpinWeighted<ComplexDataVector, 2>& eth_eth_beta,\n      const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_beta,\n      const SpinWeighted<ComplexDataVector, 2>& eth_ethbar_j,\n      const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_j_jbar,\n      const SpinWeighted<ComplexDataVector, 1>& eth_j_jbar,\n      const SpinWeighted<ComplexDataVector, 0>& ethbar_dy_u,\n      const SpinWeighted<ComplexDataVector, 0>& ethbar_ethbar_j,\n      const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n      const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n      const SpinWeighted<ComplexDataVector, 0>& k,\n      const SpinWeighted<ComplexDataVector, 0>& r);\n};\n\n/*!\n * \\brief Computes the pole part of the integrand (right-hand side) of the\n * equation which determines the radial (y) dependence of the Bondi quantity\n * \\f$H\\f$.\n *\n * \\details The quantity \\f$H \\equiv \\partial_u J\\f$ (evaluated at constant y)\n * is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B.\\f]\n * See \\cite Bishop1997ik \\cite Handmer2014qha for full details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$H\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$,\\f$\\beta\\f$, \\f$Q\\f$, \\f$U\\f$, and \\f$W\\f$ on the same\n * surface is written as\n * \\f[(1 - y) \\partial_y H + H + (1 - y)(\\mathcal{D}_J H\n * + \\bar{\\mathcal{D}}_J \\bar{H}) = A_J + (1 - y) B_J.\\f]\n *\n * We refer to \\f$A_J\\f$ as the \"pole part\" of the integrand\n * and \\f$B_J\\f$ as the \"regular part\". The pole part is computed by this\n * function, and has the expression\n * \\f{align*}\n * A_J =& - \\tfrac{1}{2} \\eth (J \\bar{U}) -  \\eth (\\bar{U}) J -  \\tfrac{1}{2}\n * \\bar{\\eth} (U) J -  \\eth (U) K -  \\tfrac{1}{2} (\\bar{\\eth} (J) U) + 2 J W\n * \\f}\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::BondiH>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::BondiJ, Tags::BondiU, Tags::BondiW>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU, Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJbar, Tags::BondiU>,\n          Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n  using integration_independent_tags = tmpl::list<Tags::BondiK>;\n  using temporary_tags = tmpl::list<>;\n\n  using return_tags =\n      tmpl::append<tmpl::list<Tags::PoleOfIntegrand<Tags::BondiH>>,\n                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          pole_of_integrand_for_h,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*pole_of_integrand_for_h)), get(args)...);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*>\n          pole_of_integrand_for_h,\n      const SpinWeighted<ComplexDataVector, 2>& j,\n      const SpinWeighted<ComplexDataVector, 1>& u,\n      const SpinWeighted<ComplexDataVector, 0>& w,\n      const SpinWeighted<ComplexDataVector, 2>& eth_u,\n      const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n      const SpinWeighted<ComplexDataVector, -2>& ethbar_jbar_u,\n      const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n      const SpinWeighted<ComplexDataVector, 0>& k);\n};\n\n/*!\n * \\brief Computes the pole part of the integrand (right-hand side) of the\n * equation which determines the radial (y) dependence of the Bondi quantity\n * \\f$H\\f$.\n *\n * \\details The quantity \\f$H \\equiv \\partial_u J\\f$ (evaluated at constant y)\n * is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B.\\f]\n * See \\cite Bishop1997ik \\cite Handmer2014qha for full details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$H\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$,\\f$\\beta\\f$, \\f$Q\\f$, \\f$U\\f$, and \\f$W\\f$ on the same\n * surface is written as\n * \\f[(1 - y) \\partial_y H + H + (1 - y)(\\mathcal{D}_J H + \\bar{\\mathcal{D}}_J\n * \\bar{H}) = A_J + (1 - y) B_J.\\f]\n * We refer to \\f$A_J\\f$ as the \"pole part\" of the integrand\n * and \\f$B_J\\f$ as the \"regular part\". The pole part is computed by this\n * function, and has the expression\n * \\f{align*}\n * B_J =& -\\tfrac{1}{2} \\left(\\eth(\\partial_y (J) \\bar{U}) +  \\partial_y\n * (\\bar{\\eth} (J)) U \\right) + J (\\mathcal{B}_J + \\bar{\\mathcal{B}}_J) \\notag\\\\\n * &+ \\frac{e^{2 \\beta}}{2 R} \\left(\\mathcal{C}_J + \\eth (\\eth (\\beta)) -\n * \\tfrac{1}{2} \\eth (Q) -  (\\mathcal{A}_J + \\bar{\\mathcal{A}_J}) J +\n * \\frac{\\bar{\\mathcal{C}_J} J^2}{K^2} + \\frac{\\eth (J (-2 \\bar{\\eth} (\\beta) +\n * \\bar{Q}))}{4 K} -  \\frac{\\eth (\\bar{Q}) J}{4 K} + (\\eth (\\beta) -\n * \\tfrac{1}{2} Q)^2\\right) \\notag\\\\\n * &-  \\partial_y (J)  \\left(\\frac{\\eth (U) \\bar{J}}{2 K} -  \\tfrac{1}{2}\n * \\bar{\\eth} (\\bar{U}) J K + \\tfrac{1}{4} (\\eth (\\bar{U}) -  \\bar{\\eth} (U))\n * K^2 + \\frac{1}{2} \\frac{\\eth (R) \\bar{U}}{R} -  \\frac{1}{2}\n * W\\right)\\notag\\\\\n * &+  \\partial_y (\\bar{J}) \\left(- \\tfrac{1}{4} (- \\eth (\\bar{U}) + \\bar{\\eth}\n * (U)) J^2 + \\eth (U) J \\left(- \\frac{1}{2 K} + \\tfrac{1}{2}\n * K\\right)\\right)\\notag\\\\\n * &+ (1 - y) \\bigg[\\frac{1}{2} \\left(- \\frac{\\partial_y (J)}{R} + \\frac{2\n * \\partial_{u} (R) \\partial_y (\\partial_y (J))}{R} + \\partial_y\n * (\\partial_y (J)) W\\right)  + \\partial_y (J) \\left(\\tfrac{1}{2} \\partial_y (W)\n * + \\frac{1}{2 R}\\right)\\bigg]\\notag\\\\\n * &+ (1 - y)^2 \\bigg[ \\frac{\\partial_y (\\partial_y (J)) }{4 R} \\bigg],\n * \\f}\n * where\n * \\f{align*}\n * \\mathcal{A}_J =& \\tfrac{1}{4} \\eth (\\eth (\\bar{J})) -  \\frac{1}{4 K^3} -\n * \\frac{\\eth (\\bar{\\eth} (J \\bar{J})) -  (\\eth (\\bar{\\eth} (\\bar{J})) - 4\n \\bar{J})\n * J}{16 K^3} + \\frac{3}{4 K} -  \\frac{\\eth (\\bar{\\eth} (\\beta))}{4 K} \\notag\\\\\n * &-  \\frac{\\eth (\\bar{\\eth} (J)) \\bar{J} (1 -  \\frac{1}{4 K^2})}{4 K} +\n * \\tfrac{1}{2} \\eth (\\bar{J}) \\left(\\eth (\\beta) + \\frac{\\bar{\\eth} (J \\bar{J})\n * J}{4 K^3} -  \\frac{\\bar{\\eth} (J) (-1 + 2 K^2)}{4 K^3} -  \\tfrac{1}{2}\n * Q\\right)\\\\\n * \\mathcal{B}_J =& - \\frac{\\eth (U) \\bar{J} \\partial_y (J \\bar{J})}{4 K} +\n * \\tfrac{1}{2} \\partial_y (W) + \\frac{1}{4 R} + \\tfrac{1}{4} \\bar{\\eth} (J)\n * \\partial_y (\\bar{J}) U -  \\frac{\\bar{\\eth} (J \\bar{J}) \\partial_y (J \\bar{J})\n * U}{8 K^2} \\notag\\\\&-  \\tfrac{1}{4} J \\partial_y (\\eth (\\bar{J})) \\bar{U} +\n * \\tfrac{1}{4} (\\eth (J \\partial_y (\\bar{J})) + \\frac{J \\eth (R)\n * \\partial_y(\\bar{J})}{R}) \\bar{U} \\\\\n * &+ (1 - y) \\bigg[ \\frac{\\mathcal{D}_J \\partial_{u} (R)\n * \\partial_y (J)}{R} -  \\tfrac{1}{4} \\partial_y (J) \\partial_y (\\bar{J}) W +\n * \\frac{(\\partial_y (J \\bar{J}))^2 W}{16 K^2} \\bigg] \\\\\n * &+ (1 - y)^2 \\bigg[ - \\frac{\\partial_y (J) \\partial_y (\\bar{J})}{8 R} +\n * \\frac{(\\partial_y (J \\bar{J}))^2}{32 K^2 R} \\bigg]\\\\\n * \\mathcal{C}_J =& \\tfrac{1}{2} \\bar{\\eth} (J) K (\\eth (\\beta) -  \\tfrac{1}{2}\n * Q)\\\\ \\mathcal{D}_J =& \\tfrac{1}{4} \\left(-2 \\partial_y (\\bar{J}) +\n * \\frac{\\bar{J} \\partial_y (J \\bar{J})}{K^2}\\right)\n * \\f}\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::BondiH>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::Dy<Tags::BondiJ>>, Tags::Dy<Tags::BondiJ>,\n                 Tags::Dy<Tags::BondiW>, Tags::Exp2Beta, Tags::BondiJ,\n                 Tags::BondiQ, Tags::BondiU, Tags::BondiW>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::EthEth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::EthEthbar>,\n      Spectral::Swsh::Tags::Derivative<\n          Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                           Spectral::Swsh::Tags::Ethbar>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n          Spectral::Swsh::Tags::EthEthbar>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiQ, Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU, Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiUbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiJ>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::EthbarEthbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::JbarQMinus2EthBeta,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiQ,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n  using integration_independent_tags =\n      tmpl::list<Tags::DuRDividedByR, Tags::EthRDividedByR, Tags::BondiK,\n                 Tags::OneMinusY, Tags::BondiR>;\n  using temporary_tags =\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>>;\n\n  using return_tags =\n      tmpl::append<tmpl::list<Tags::RegularIntegrand<Tags::BondiH>>,\n                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          regular_integrand_for_h,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          script_aj,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          script_bj,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          script_cj,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*regular_integrand_for_h)),\n               make_not_null(&get(*script_aj)), make_not_null(&get(*script_bj)),\n               make_not_null(&get(*script_cj)), get(args)...);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*>\n          regular_integrand_for_h,\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> script_aj,\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> script_bj,\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> script_cj,\n      const SpinWeighted<ComplexDataVector, 2>& dy_dy_j,\n      const SpinWeighted<ComplexDataVector, 2>& dy_j,\n      const SpinWeighted<ComplexDataVector, 0>& dy_w,\n      const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n      const SpinWeighted<ComplexDataVector, 2>& j,\n      const SpinWeighted<ComplexDataVector, 1>& q,\n      const SpinWeighted<ComplexDataVector, 1>& u,\n      const SpinWeighted<ComplexDataVector, 0>& w,\n      const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n      const SpinWeighted<ComplexDataVector, 2>& eth_eth_beta,\n      const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_beta,\n      const SpinWeighted<ComplexDataVector, 2>& eth_ethbar_j,\n      const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_j_jbar,\n      const SpinWeighted<ComplexDataVector, 1>& eth_j_jbar,\n      const SpinWeighted<ComplexDataVector, 2>& eth_q,\n      const SpinWeighted<ComplexDataVector, 2>& eth_u,\n      const SpinWeighted<ComplexDataVector, 2>& eth_ubar_dy_j,\n      const SpinWeighted<ComplexDataVector, 1>& ethbar_dy_j,\n      const SpinWeighted<ComplexDataVector, 0>& ethbar_ethbar_j,\n      const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n      const SpinWeighted<ComplexDataVector, -1>& ethbar_jbar_dy_j,\n      const SpinWeighted<ComplexDataVector, -2>& ethbar_jbar_q_minus_2_eth_beta,\n      const SpinWeighted<ComplexDataVector, 0>& ethbar_q,\n      const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n      const SpinWeighted<ComplexDataVector, 0>& du_r_divided_by_r,\n      const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n      const SpinWeighted<ComplexDataVector, 0>& k,\n      const SpinWeighted<ComplexDataVector, 0>& one_minus_y,\n      const SpinWeighted<ComplexDataVector, 0>& r);\n};\n\n/*!\n * \\brief Computes the linear factor which multiplies \\f$H\\f$ in the\n * equation which determines the radial (y) dependence of the Bondi quantity\n * \\f$H\\f$.\n *\n * \\details The quantity \\f$H \\equiv \\partial_u J\\f$ (evaluated at constant y)\n * is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B.\\f]\n * See \\cite Bishop1997ik \\cite Handmer2014qha for full details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$H\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$,\\f$\\beta\\f$, \\f$Q\\f$, \\f$U\\f$, and \\f$W\\f$ on the same\n * surface is written as\n * \\f[(1 - y) \\partial_y H + H + (1 - y) J (\\mathcal{D}_J\n * H + \\bar{\\mathcal{D}}_J \\bar{H}) = A_J + (1 - y) B_J.\\f]\n * The quantity \\f$1 +(1 - y) J \\mathcal{D}_J\\f$ is the linear factor\n * for the non-conjugated \\f$H\\f$, and is computed from the equation:\n * \\f[\\mathcal{D}_J = \\frac{1}{4}(-2 \\partial_y \\bar{J} + \\frac{\\bar{J}\n * \\partial_y (J \\bar{J})}{K^2})\\f]\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::LinearFactor<Tags::BondiH>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiJ>, Tags::BondiJ>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<Tags::OneMinusY>;\n  using temporary_tags =\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>>;\n\n  using return_tags = tmpl::append<tmpl::list<Tags::LinearFactor<Tags::BondiH>>,\n                                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          linear_factor_for_h,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          script_djbar,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*linear_factor_for_h)),\n               make_not_null(&get(*script_djbar)), get(args)...);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> linear_factor_for_h,\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> script_djbar,\n      const SpinWeighted<ComplexDataVector, 2>& dy_j,\n      const SpinWeighted<ComplexDataVector, 2>& j,\n      const SpinWeighted<ComplexDataVector, 0>& one_minus_y);\n};\n\n/*!\n * \\brief Computes the linear factor which multiplies \\f$\\bar{H}\\f$ in the\n * equation which determines the radial (y) dependence of the Bondi quantity\n * \\f$H\\f$.\n *\n * \\details The quantity \\f$H \\equiv \\partial_u J\\f$ (evaluated at constant y)\n * is defined via the Bondi form of the metric:\n * \\f[ds^2 = - \\left(e^{2 \\beta} (1 + r W) - r^2 h_{AB} U^A U^B\\right) du^2 - 2\n * e^{2 \\beta} du dr - 2 r^2 h_{AB} U^B du dx^A + r^2 h_{A B} dx^A dx^B. \\f]\n * Additional quantities \\f$J\\f$ and \\f$K\\f$ are defined using a spherical\n * angular dyad \\f$q^A\\f$:\n * \\f[ J \\equiv h_{A B} q^A q^B, K \\equiv h_{A B} q^A \\bar{q}^B.\\f]\n * See \\cite Bishop1997ik \\cite Handmer2014qha for full details.\n *\n * We write the equations of motion in the compactified coordinate \\f$ y \\equiv\n * 1 - 2 R/ r\\f$, where \\f$r(u, \\theta, \\phi)\\f$ is the Bondi radius of the\n * \\f$y=\\f$ constant surface and \\f$R(u,\\theta,\\phi)\\f$ is the Bondi radius of\n * the worldtube. The equation which determines \\f$H\\f$ on a surface of constant\n * \\f$u\\f$ given \\f$J\\f$,\\f$\\beta\\f$, \\f$Q\\f$, \\f$U\\f$, and \\f$W\\f$ on the same\n * surface is written as\n * \\f[(1 - y) \\partial_y H + H + (1 - y) J (\\mathcal{D}_J H +\n * \\bar{\\mathcal{D}}_J \\bar{H}) = A_J + (1 - y) B_J.\\f]\n * The quantity \\f$ (1 - y) J \\bar{\\mathcal{D}}_J\\f$ is the linear factor\n * for the non-conjugated \\f$H\\f$, and is computed from the equation:\n * \\f[\\mathcal{D}_J = \\frac{1}{4}(-2 \\partial_y \\bar{J} + \\frac{\\bar{J}\n * \\partial_y (J \\bar{J})}{K^2})\\f]\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::LinearFactorForConjugate<Tags::BondiH>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiJ>, Tags::BondiJ>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<Tags::OneMinusY>;\n  using temporary_tags =\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>>;\n\n  using return_tags =\n      tmpl::append<tmpl::list<Tags::LinearFactorForConjugate<Tags::BondiH>>,\n                   temporary_tags>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 4>>*>\n          linear_factor_for_conjugate_h,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          script_djbar,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*linear_factor_for_conjugate_h)),\n               make_not_null(&get(*script_djbar)), get(args)...);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 4>*>\n          linear_factor_for_conjugate_h,\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> script_djbar,\n      const SpinWeighted<ComplexDataVector, 2>& dy_j,\n      const SpinWeighted<ComplexDataVector, 2>& j,\n      const SpinWeighted<ComplexDataVector, 0>& one_minus_y);\n};\n\n/*!\n *\\brief Computes the pole part of the integrand (right-hand side) of the\n * equation which determines the radial (y) dependence of the scalar quantity\n * \\f$\\Pi\\f$.\n *\n * \\details The evolution equation for \\f$\\Pi\\f$ is written as\n * \\f[(1 - y) \\partial_y \\Pi + \\Pi = A_\\Pi + (1 - y) B_\\Pi.\\f]\n * We refer to \\f$A_\\Pi\\f$ as the \"pole part\" of the integrand and \\f$B_\\Pi\\f$\n * as the \"regular part\". The pole part is computed by this function, and has\n * the expression\n * \\f[A_\\Pi = - \\Re \\left(U \\bar{\\eth}\\psi\\right).\\f]\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::KleinGordonPi>> {\n public:\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::Eth>>;\n  using integration_independent_tags = tmpl::list<Tags::BondiU>;\n\n  using return_tags = tmpl::list<Tags::PoleOfIntegrand<Tags::KleinGordonPi>>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          pole_of_integrand_for_kg_pi,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*pole_of_integrand_for_kg_pi)), get(args)...);\n  }\n\n private:\n  static void apply_impl(gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n                             pole_of_integrand_for_kg_pi,\n                         const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n                         const SpinWeighted<ComplexDataVector, 1>& bondi_u);\n};\n\n/*!\n * \\brief Computes the regular part of the integrand (right-hand side) of the\n * equation which determines the radial (y) dependence of the scalar quantity\n * \\f$\\Pi\\f$.\n *\n * \\details The evolution equation for \\f$\\Pi\\f$ is written as\n * \\f[(1 - y) \\partial_y \\Pi + \\Pi = A_\\Pi + (1 - y) B_\\Pi.\\f]\n * We refer to \\f$A_\\Pi\\f$ as the \"pole part\" of the integrand and \\f$B_\\Pi\\f$\n * as the \"regular part\". The regular part is computed by this function, and has\n * the expression:\n * \\f[ B_\\Pi = \\frac{1}{4R}e^{2\\beta}\\left(N_{\\psi 1}-N_{\\psi 2} +N_{\\psi\n * 3}\\right) -\\frac{R}{2}\\frac{N_{\\psi 4}}{(1-y)^2}\n * +\\tau+\\frac{\\partial_{u}R}{R}(1-y)\\partial_y^2\\psi, \\f]\n * where\n * \\f{align*}{\n * & N_{\\psi 1}= K(2\\Re \\eth\\beta\\bar{\\eth}\\psi + \\eth\\bar{\\eth}\\psi) \\\\\n * & N_{\\psi 2}=\\Re \\bar{J}\\eth\\eth\\psi + 2\\Re \\bar{\\eth}\\beta \\bar{\\eth}\\psi\n * J + \\Re \\bar{\\eth}J\\bar{\\eth}\\psi \\\\\n * & N_{\\psi 3}=\\Re \\eth K\\bar{\\eth}\\psi \\\\\n * & N_{\\psi 4}=2\\Re (\\bar{\\eth}\\psi ) \\partial_rU + 2\\Re \\eth\n * \\bar{U}\\partial_r\\psi + 4\\Re \\bar{U}\\eth\\partial_r\\psi \\\\\n * & \\tau=\\frac{1}{2}(1-y)\\partial_y W\\partial_y\\psi +\n * \\frac{(1-y)^2}{4R}\\partial_y^2\\psi +\n * \\frac{1}{2}(1-y)W\\partial_y^2\\psi + \\frac{1}{2}W\\partial_y\\psi\n * \\f}\n */\ntemplate <>\nstruct ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::KleinGordonPi>> {\n public:\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::Dy<Tags::KleinGordonPsi>>,\n                 Tags::Dy<Tags::KleinGordonPsi>, Tags::Dy<Tags::BondiU>,\n                 Tags::Dy<Tags::BondiW>>;\n  using swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<\n                     Tags::Dy<Tags::KleinGordonPsi>, Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::EthEth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<\n                     ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n                     Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<\n                     Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEthbar>>;\n  using integration_independent_tags =\n      tmpl::list<Tags::BondiJ, Tags::Exp2Beta, Tags::DuRDividedByR,\n                 Tags::OneMinusY, Tags::EthRDividedByR, Tags::BondiR,\n                 Tags::BondiK, Tags::BondiU, Tags::BondiW>;\n\n  using return_tags = tmpl::list<Tags::RegularIntegrand<Tags::KleinGordonPi>>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags,\n                   integration_independent_tags>;\n\n  template <typename... Args>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          regular_integrand_for_kg_pi,\n      const Args&... args) {\n    apply_impl(make_not_null(&get(*regular_integrand_for_kg_pi)), get(args)...);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>\n          regular_integrand_for_kg_pi,\n      // pre_swsh_derivative_tags\n      const SpinWeighted<ComplexDataVector, 0>& dy_dy_kg_psi,\n      const SpinWeighted<ComplexDataVector, 0>& dy_kg_psi,\n      const SpinWeighted<ComplexDataVector, 1>& dy_bondi_u,\n      const SpinWeighted<ComplexDataVector, 0>& dy_w,\n      // swsh_derivative_tags\n      const SpinWeighted<ComplexDataVector, 1>& eth_dy_kg_psi,\n      const SpinWeighted<ComplexDataVector, 2>& eth_eth_kg_psi,\n      const SpinWeighted<ComplexDataVector, 1>& eth_kg_psi,\n      const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n      const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n      const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n      const SpinWeighted<ComplexDataVector, 1>& eth_j_jbar,\n      const SpinWeighted<ComplexDataVector, 0>& eth_ethbar_kg_psi,\n      // integration_independent_tags\n      const SpinWeighted<ComplexDataVector, 2>& j,\n      const SpinWeighted<ComplexDataVector, 0>& exp2beta,\n      const SpinWeighted<ComplexDataVector, 0>& du_r_divided_by_r,\n      const SpinWeighted<ComplexDataVector, 0>& one_minus_y,\n      const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n      const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n      const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n      const SpinWeighted<ComplexDataVector, 1>& bondi_u,\n      const SpinWeighted<ComplexDataVector, 0>& bondi_w);\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Events.hpp\n  ObserveFields.hpp\n  ObserveTimeStep.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Events/Events.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\brief Events for CCE\nnamespace Cce::Events {}\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Events/ObserveFields.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/Cce/LinearOperators.hpp\"\n#include \"Evolution/Systems/Cce/NewmanPenrose.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/SwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace Cce::Events {\nnamespace detail {\ntemplate <typename Tag>\nstd::string name() {\n  if constexpr (std::is_same_v<Tag, Tags::ComplexInertialRetardedTime>) {\n    return db::tag_name<Tags::InertialRetardedTime>();\n  } else {\n    return db::tag_name<Tag>();\n  }\n}\n}  // namespace detail\n\n/*!\n * \\brief Event to observe fields/variables in a characteristic evolution.\n *\n * \\details Similar to `dg::Events::ObserveFields`, this event will write volume\n * data from the characteristic domain to disk when triggered. However, there\n * are several differences which are important to highlight.\n *\n * First is the fields themselves. The DG event takes the fields to observe as\n * template parameters because the event must work with many evolution systems.\n * However, since this event is specific to the characteristic evolution system,\n * we can hardcode the list of fields that are available to observe. The fields\n * available to observe are the following tags along with their first and second\n * `Cce::Tags::Dy` derivatives (see `Cce::Tags::Dy` for a definition of `y`):\n *\n * - `Cce::Tags::BondiBeta`\n * - `Cce::Tags::BondiU`\n * - `Cce::Tags::BondiQ`\n * - `Cce::Tags::BondiW`\n * - `Cce::Tags::BondiH` (no second derivative)\n * - `Cce::Tags::BondiJ`\n * - `Cce::Tags::Du<Cce::Tags::BondiJ>`\n *\n * Some more fields to observe are:\n *\n * - `Cce::Tags::Psi0`\n * - `Cce::Tags::Psi1`\n * - `Cce::Tags::Psi2`\n * - `Cce::Tags::ComplexInertialRetardedTime`\n * - `Cce::Tags::OneMinusY`\n * - `Cce::Tags::BondiR`\n * - `Cce::Tags::EthRDividedByR`\n * - `Cce::Tags::DuRDividedByR`\n *\n * The main reason that this event is separate from the DG one is because this\n * event writes modal data over the sphere for every radial grid point, while\n * the DG event writes nodal data. Every tag above is a\n * `Scalar<SpinWeighted<ComplexDataVector, Spin>>` for some `Spin`. While this\n * data itself is in nodal form, it is more convenient to transform to modal\n * data and decompose in spherical harmonics before writing. This means\n * our typical way of writing/storing volume data won't work.\n *\n * All data will be written into the `observers::OptionTags::VolumeFileName`\n * file. If CCE is run on a single core, then this will write the volume data\n * immediately (synchronously) instead of sending it to the ObserverWriter to be\n * written asynchronously.  The option `SubgroupName` controls the name of the\n * H5 group where this volume data is written. For example, if `SubgroupName` is\n * \"CceVolumeData\", then the volume file will contain\n * `/CceVolumeData/VolumeData.vol` for most fields; it would contain\n * `/CceVolumeData/InertialRetardedTime.vol` for the inertial retarded time; and\n * it would contain `/CceVolumeData/OneMinusY.vol` for the compactified radial\n * coordinate. The structure of the .vol subfiles is the same as that for DG\n * volume data. However, the extents of the three different volume files are all\n * different, and for modal directions, they take the values l_max in both of\n * the two extent slots corresponding to angular modes. This  also means that\n * complex modal data (which has real/imag parts interleaved, as described\n * below) has twice as much data as the products of the extents suggests.\n *\n * The formats for `Cce::Tags::ComplexInertialRetardedTime` and\n * `Cce::Tags::OneMinusY` are special and are described below. Every other field\n * follows the same format. Each ObservationId contains one time slice. Within\n * an ObservationId, each field is an unraveled vector of complex modal\n * coefficients at compactified radial slices (the compactified coordinate is $y\n * = 1 - 2R/r$ where $r$ is your coordinate radius and $R$ is the coordinate\n * radius of your worldtube; it is recommended to always dump the quantity\n * `Cce::Tags::OneMinusY` so the values of the compactified coordinates are\n * available as well). The ordering of the coefficients for a field $f$ at\n * constant observation event are, for example:\n * Re f_{0,0}(1-y_0), Im f_{0,0}(1-y_0), Re f_{1,-1}(1-y_0), Im f_{1,-1}(1-y_0),\n * Re f_{1,0}(1-y_0), Im f_{1,0}(1-y_0), ...\n * Re f_{l_{max},l_{max}}(1-y_0), Im f_{l_{max},l_{max}}(1-y_0),\n * Re f_{0,0}(1-y_1), Im f_{0,0}(1-y_1), ...\n * Re f_{l_{max},l_{max}}(1-y_{max)), Im f_{l_{max},l_{max}}(1-y_{max}).\n * That is, the radial slice indexes the slowest, followed by the $\\ell$ number\n * (always starting at 0, even for s≠0), followed by the azimuthal m number\n * (from $-\\ell$ to $+\\ell$ inclusive), followed by interleaving real and\n * imaginary parts of the complex field.\n *\n * There are two notable exceptions to this format. One is\n * `Cce::Tags::ComplexInertialRetardedTime`. The quantity we are actually\n * interested in is `Cce::Tags::InertialRetardedTime` which is real and only\n * defined once for every direction $\\theta,\\phi$ (meaning it does not have\n * different values at the different radial grid points). However, we use\n * `Cce::Tags::ComplexInertialRetardedTime` because it has the same data type as\n * the other tags which makes the internals of the class simpler. This quantity\n * is stored in `/<SubgroupName>/InertialRetardedTime.vol`.\n *\n * The second is `Cce::Tags::OneMinusY`. Even though this quantity is stored as\n * a `Scalar<SpinWeighted<ComplexDataVector, 0>>` like the others, there is only\n * one meaningful value per radial grid point. All angular grid points for a\n * given radius are set to this value, namely $1-y$. Thus we only need to write\n * this value once for each radial grid point. We do this in a volume subfile\n * `/<SubgroupName>/OneMinusY.vol` with the elements in the same order as the\n * radial index order for the spin weighted quantities above.\n */\nclass ObserveFields : public Event {\n  template <typename Tag, bool IncludeSecondDeriv = true>\n  // clang-format off\n  using zero_one_two_radial_derivs = tmpl::flatten<tmpl::list<\n      Tag,\n      Tags::Dy<Tag>,\n      tmpl::conditional_t<IncludeSecondDeriv,\n                          Tags::Dy<Tags::Dy<Tag>>,\n                          tmpl::list<>>>>;\n  using spin_weighted_tags_to_observe = tmpl::flatten<\n      tmpl::list<zero_one_two_radial_derivs<Tags::BondiBeta>,\n                 zero_one_two_radial_derivs<Tags::BondiU>,\n                 zero_one_two_radial_derivs<Tags::BondiQ>,\n                 zero_one_two_radial_derivs<Tags::BondiW>,\n                 zero_one_two_radial_derivs<Tags::BondiH, false>,\n                 zero_one_two_radial_derivs<Tags::BondiJ>,\n                 zero_one_two_radial_derivs<Tags::Du<Tags::BondiJ>>,\n                 Tags::BondiR,\n                 Tags::Psi0,\n                 Tags::Psi1,\n                 Tags::Psi2,\n                 Tags::NewmanPenroseAlpha,\n                 Tags::NewmanPenroseBeta,\n                 Tags::NewmanPenroseGamma,\n                 Tags::NewmanPenroseEpsilon,\n                 // Tags::NewmanPenroseKappa,\n                 // in our choice of tetrad, \\kappa=0\n                 Tags::NewmanPenroseTau,\n                 Tags::NewmanPenroseSigma,\n                 Tags::NewmanPenroseRho,\n                 Tags::NewmanPenrosePi,\n                 Tags::NewmanPenroseNu,\n                 Tags::NewmanPenroseMu,\n                 Tags::NewmanPenroseLambda,\n                 Tags::EthRDividedByR,\n                 Tags::DuRDividedByR>>;\n  // clang-format on\n\n public:\n  using available_tags_to_observe =\n      tmpl::push_back<spin_weighted_tags_to_observe,\n                      Tags::ComplexInertialRetardedTime, Tags::OneMinusY>;\n\n  /// \\cond\n  explicit ObserveFields(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveFields);  // NOLINT\n  /// \\endcond\n\n  /// The name of the subgroup inside the HDF5 file\n  struct SubgroupName {\n    using type = std::string;\n    static constexpr Options::String help = {\n      \"The name of the subgroup inside the HDF5 file without an extension and \"\n      \"without a preceding '/'.\"};\n  };\n\n  struct VariablesToObserve {\n    static constexpr Options::String help = \"Subset of variables to observe\";\n    using type = std::vector<std::string>;\n    static size_t lower_bound_on_size() { return 1; }\n  };\n\n  using options = tmpl::list<SubgroupName, VariablesToObserve>;\n\n  static constexpr Options::String help =\n      \"Observe volume tensor fields on the characteristic grid. Writes volume \"\n      \"quantities from the tensors listed in the 'VariablesToObserve' \"\n      \"option to volume subfiles in the subgroup named by the option \"\n      \"'SubgroupName', into the volume h5 file named by the option \"\n      \"'VolumeFileName' of Observers.\\n\";\n\n  ObserveFields() = default;\n\n  ObserveFields(const std::string& subgroup_name,\n                const std::vector<std::string>& variables_to_observe,\n                const Options::Context& context = {});\n\n  using compute_tags_for_observation_box =\n    tmpl::list<Tags::Psi0Compute, Tags::Psi1Compute, Tags::Psi2Compute,\n               Tags::SwshDerivativeCompute<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Eth>,\n               Tags::SwshDerivativeCompute<Tags::BondiW,\n                                                  Spectral::Swsh::Tags::Eth>,\n               Tags::NewmanPenroseAlphaCompute, Tags::NewmanPenroseBetaCompute,\n               Tags::NewmanPenroseGammaCompute,\n               Tags::NewmanPenroseEpsilonCompute,\n               // Tags::NewmanPenroseKappaCompute,\n               // in our choice of tetrad, \\kappa=0\n               Tags::NewmanPenroseTauCompute,\n               Tags::NewmanPenroseSigmaCompute, Tags::NewmanPenroseRhoCompute,\n               Tags::NewmanPenrosePiCompute, Tags::NewmanPenroseNuCompute,\n               Tags::NewmanPenroseMuCompute, Tags::NewmanPenroseLambdaCompute,\n               Tags::SwshDerivativeCompute<Tags::NewmanPenrosePi,\n                                                 Spectral::Swsh::Tags::Eth>,\n               Tags::SwshDerivativeCompute<Tags::NewmanPenrosePi,\n                                                 Spectral::Swsh::Tags::Ethbar>,\n               Tags::DyCompute<Tags::NewmanPenrosePi>,\n               Tags::DyCompute<Tags::NewmanPenroseMu>\n      >;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::ObservationBox>;\n\n  template <typename DataBoxType, typename ComputeTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(const ObservationBox<DataBoxType, ComputeTagsList>& box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& /*array_index*/,\n                  const ParallelComponent* const /*component*/,\n                  const ObservationValue& /*observation_value*/) const {\n    const bool write_synchronously =\n        Parallel::number_of_procs<size_t>(cache) == 1;\n\n    // Number of points\n    const size_t l_max = get<Tags::LMax>(box);\n    const size_t l_max_plus_one_squared = square(l_max + 1);\n    const size_t number_of_angular_points =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n    const size_t number_of_radial_grid_points =\n        get<Tags::NumberOfRadialPoints>(box);\n\n    // Time\n    const double time = get<::Tags::Time>(box);\n\n    // Observer writer\n    auto observer_proxy = Parallel::get_parallel_component<\n        ::observers::ObserverWriter<Metavariables>>(cache)[0];\n\n    ////////////////////////////////////////////////////////////\n    // The inertial retarded time is special because it's stored as a\n    // Scalar<DataVector> because it's only real and only has one set of angular\n    // points worth of data to write. However, all the machinery is for a\n    // SpinWeighted<ComplexDataVector>. Luckily there is a\n    // ComplexInertialRetardedTime where the real part is the\n    // InertialRetardedTime and the imaginary part is 0, so we use that instead,\n    // swapping the names where necessary.\n    // Put into volume subfile named /<SubgroupName>/InertialRetardedTime.vol\n    const std::string inertial_retarded_time_name =\n        detail::name<Tags::ComplexInertialRetardedTime>();\n    if (variables_to_observe_.count(inertial_retarded_time_name) == 1) {\n      const std::string subfile_name = subgroup_path_\n                                       + \"/\" + inertial_retarded_time_name;\n      const observers::ObservationId observation_id{time,\n        subfile_name + \".vol\"};\n      const std::vector<size_t> extents_vector{{l_max, l_max}};\n      const std::vector<Spectral::Basis> bases_vector{\n        {Spectral::Basis::SphericalHarmonic,\n         Spectral::Basis::SphericalHarmonic}};\n      const std::vector<Spectral::Quadrature> quadratures_vector{\n        {Spectral::Quadrature::Gauss,\n         Spectral::Quadrature::Equiangular}};\n\n      const SpinWeighted<ComplexDataVector, 0>& complex_inertial_retarded_time =\n          get(get<Tags::ComplexInertialRetardedTime>(box));\n\n      // Allocate a buffer to receive the transformed data, since\n      // WriteVolumeData only understands DataVectors, not\n      // ComplexDataVectors.\n      DataVector goldberg_modes_interleaved_dv(2 * l_max_plus_one_squared);\n\n      // A non-owning view of goldberg_modes_interleaved_dv,\n      // with the correct spin\n      SpinWeighted<ComplexModalVector, 0> goldberg_mode_view;\n      goldberg_mode_view.set_data_ref(\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n        reinterpret_cast<std::complex<double>*>(\n          goldberg_modes_interleaved_dv.data()),\n        l_max_plus_one_squared);\n\n      Spectral::Swsh::libsharp_to_goldberg_modes(\n          make_not_null(&goldberg_mode_view),\n          Spectral::Swsh::swsh_transform(\n              l_max, 1, complex_inertial_retarded_time),\n          l_max);\n\n      const std::vector<TensorComponent> tensor_components{\n        {inertial_retarded_time_name, goldberg_modes_interleaved_dv}};\n\n      if (write_synchronously) {\n        Parallel::local_synchronous_action<\n          observers::ThreadedActions::WriteVolumeData>(\n              observer_proxy, cache,\n              Parallel::get<observers::Tags::VolumeFileName>(cache),\n              subfile_name, observation_id,\n              std::vector<ElementVolumeData>{\n                {inertial_retarded_time_name, tensor_components,\n                 extents_vector, bases_vector,\n                 quadratures_vector}});\n      } else {\n        // Send to observer writer\n        Parallel::threaded_action<\n          observers::ThreadedActions::WriteVolumeData>(\n              observer_proxy,\n              Parallel::get<observers::Tags::VolumeFileName>(cache),\n              subfile_name, observation_id,\n              std::vector<ElementVolumeData>{\n                {inertial_retarded_time_name, tensor_components,\n                 extents_vector, bases_vector,\n                 quadratures_vector}});\n      }\n    }\n\n    ////////////////////////////////////////////////////////////\n    // One minus y is also special because every angular grid point for a given\n    // radius holds the same value. Thus we only need to write one double per\n    // radial grid point corresponding to 1 - y. Put into volume subfile named\n    // /<SubgroupName>/OneMinusY.vol\n    const std::string one_minus_y_name = detail::name<Tags::OneMinusY>();\n    if (variables_to_observe_.count(one_minus_y_name) == 1) {\n      const std::string subfile_name = subgroup_path_ + \"/\" + one_minus_y_name;\n      const observers::ObservationId observation_id{time,\n        subfile_name + \".vol\"};\n      const std::vector<size_t> extents_vector{number_of_radial_grid_points};\n      const std::vector<Spectral::Basis> bases_vector{\n        Spectral::Basis::Legendre};\n      const std::vector<Spectral::Quadrature> quadratures_vector{\n        Spectral::Quadrature::GaussLobatto};\n\n      const ComplexDataVector& one_minus_y =\n          get(get<Tags::OneMinusY>(box)).data();\n\n      DataVector one_minus_y_to_write(number_of_radial_grid_points);\n\n      for (size_t radial_index = 0; radial_index < number_of_radial_grid_points;\n           radial_index++) {\n        one_minus_y_to_write[radial_index] =\n            real(one_minus_y[radial_index * number_of_angular_points]);\n      }\n\n      const std::vector<TensorComponent> tensor_components{\n        {one_minus_y_name, one_minus_y_to_write}};\n\n      if (write_synchronously) {\n        Parallel::local_synchronous_action<\n          observers::ThreadedActions::WriteVolumeData>(\n              observer_proxy, cache,\n              Parallel::get<observers::Tags::VolumeFileName>(cache),\n              subfile_name, observation_id,\n              std::vector<ElementVolumeData>{\n                {one_minus_y_name, tensor_components,\n                 extents_vector, bases_vector,\n                 quadratures_vector}});\n      } else {\n        // Send to observer writer\n        Parallel::threaded_action<\n          observers::ThreadedActions::WriteVolumeData>(\n              observer_proxy,\n              Parallel::get<observers::Tags::VolumeFileName>(cache),\n              subfile_name, observation_id,\n              std::vector<ElementVolumeData>{\n                {one_minus_y_name, tensor_components,\n                 extents_vector, bases_vector,\n                 quadratures_vector}});\n      }\n    }\n\n    ////////////////////////////////////////////////////////////\n    // Everything else gets written together into the volume subfile named\n    // /<SubgroupName>/VolumeData.vol\n\n    // Field-independent info for writing into volume data file\n    const std::string subfile_name = subgroup_path_ + \"/VolumeData\";\n    const observers::ObservationId observation_id{time,\n      subfile_name + \".vol\"};\n    const std::vector<size_t> extents_vector{\n      {number_of_radial_grid_points, l_max, l_max}};\n    const std::vector<Spectral::Basis> bases_vector{\n      {Spectral::Basis::Legendre,\n       Spectral::Basis::SphericalHarmonic,\n       Spectral::Basis::SphericalHarmonic}};\n    const std::vector<Spectral::Quadrature> quadratures_vector{\n      {Spectral::Quadrature::GaussLobatto,\n       Spectral::Quadrature::Gauss,\n       Spectral::Quadrature::Equiangular}};\n\n    // Create tensor_components by looping over all available spin\n    // weighted tags and checking if we are observing this tag.\n    std::vector<TensorComponent> tensor_components;\n    tmpl::for_each<spin_weighted_tags_to_observe>([&](auto tag_v) {\n      using tag = tmpl::type_from<decltype(tag_v)>;\n      constexpr int spin = tag::type::type::spin;\n      const std::string name = detail::name<tag>();\n\n      // If we aren't observing this tag, then skip it\n      if (not variables_to_observe_.contains(name)) {\n        return;\n      }\n\n      const SpinWeighted<ComplexDataVector, spin>& field =\n        get(get<tag>(box));\n\n      // Allocate a buffer to receive the transformed data, since\n      // WriteVolumeData only understands DataVectors, not\n      // ComplexDataVectors.\n      DataVector goldberg_modes_interleaved_dv(2 *\n        l_max_plus_one_squared * number_of_radial_grid_points);\n\n      // A non-owning view of goldberg_modes_interleaved_dv,\n      // with the correct spin\n      SpinWeighted<ComplexModalVector, spin> goldberg_mode_view;\n      goldberg_mode_view.set_data_ref(\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n        reinterpret_cast<std::complex<double>*>(\n          goldberg_modes_interleaved_dv.data()),\n        l_max_plus_one_squared * number_of_radial_grid_points);\n\n      Spectral::Swsh::libsharp_to_goldberg_modes(\n        make_not_null(&goldberg_mode_view),\n        Spectral::Swsh::swsh_transform(l_max,\n          number_of_radial_grid_points, field), l_max);\n\n      tensor_components.emplace_back(\n        name, std::move(goldberg_modes_interleaved_dv));\n\n    });\n\n    if (write_synchronously) {\n      Parallel::local_synchronous_action<\n        observers::ThreadedActions::WriteVolumeData>(\n        observer_proxy, cache,\n        Parallel::get<observers::Tags::VolumeFileName>(cache),\n        subfile_name, observation_id,\n        std::vector<ElementVolumeData>{\n          {\"VolumeData\", tensor_components,\n           extents_vector, bases_vector,\n           quadratures_vector}});\n    } else {\n      // Send to observer writer\n      Parallel::threaded_action<\n        observers::ThreadedActions::WriteVolumeData>(\n        observer_proxy,\n        Parallel::get<observers::Tags::VolumeFileName>(cache),\n        subfile_name, observation_id,\n        std::vector<ElementVolumeData>{\n          {\"VolumeData\", tensor_components,\n           extents_vector, bases_vector,\n           quadratures_vector}});\n    }\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | subgroup_path_;\n    p | variables_to_observe_;\n  }\n\n private:\n  std::string subgroup_path_;\n  std::unordered_set<std::string> variables_to_observe_;\n};\n\nObserveFields::ObserveFields(\n    const std::string& subgroup_name,\n    const std::vector<std::string>& variables_to_observe,\n    const Options::Context& context)\n    : subgroup_path_(\"/\" + subgroup_name),\n      variables_to_observe_([&context, &variables_to_observe]() {\n        std::unordered_set<std::string> result{};\n        for (const auto& tensor : variables_to_observe) {\n          if (result.contains(tensor)) {\n            PARSE_ERROR(\n                context,\n                \"Listed variable '\"\n                    << tensor\n                    << \"' more than once in list of variables to observe.\");\n          }\n          result.insert(tensor);\n        }\n        return result;\n      }()) {\n  std::unordered_set<std::string> valid_tensors{};\n  tmpl::for_each<available_tags_to_observe>([&valid_tensors](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    valid_tensors.insert(detail::name<tag>());\n  });\n\n  for (const auto& name : variables_to_observe_) {\n    if (not valid_tensors.contains(name)) {\n      PARSE_ERROR(\n          context,\n          name << \" is not an available variable. Available variables:\\n\"\n               << valid_tensors);\n    }\n  }\n}\n\n/// \\cond\nPUP::able::PUP_ID ObserveFields::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace Cce::Events\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Events/ObserveTimeStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStep;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Cce::Events {\n\n/*!\n * \\brief %Observe the size of the time steps on the characteristic evolution.\n *\n * Writes reduction quantities:\n * - `%Time`\n * - `Time Step`\n *\n * The subfile will be written into the `/Cce` subgroup.\n */\nclass ObserveTimeStep : public Event {\n public:\n  /// The name of the subfile inside the HDF5 file\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The name of the subfile inside the HDF5 file without an extension and \"\n        \"without a preceding '/'. The subfile will be written into the \"\n        \"subgroup '/Cce'.\"};\n  };\n\n  struct PrintTimeToTerminal {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether to print the time to screen.\"};\n  };\n\n  /// \\cond\n  explicit ObserveTimeStep(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveTimeStep);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<SubfileName, PrintTimeToTerminal>;\n  static constexpr Options::String help =\n      \"Observe the size of the time step for the characteristic evolution.\\n\"\n      \"\\n\"\n      \"Writes quantities:\\n\"\n      \"- Time\\n\"\n      \"- Time Step\\n\"\n      \"\\n\"\n      \"The subfile will be written into the subgroup '/Cce'.\";\n\n  ObserveTimeStep() = default;\n  explicit ObserveTimeStep(const std::string& subfile_name,\n                           const bool output_time);\n\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::TimeStep>;\n\n  template <typename ArrayIndex, typename ParallelComponent,\n            typename Metavariables>\n  void operator()(const TimeDelta& time_step,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& /*array_index*/,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    std::vector<double> data_to_write{observation_value.value,\n                                      time_step.value()};\n\n    auto& writer = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        writer[0], subfile_path_, legend_,\n        std::make_tuple(std::move(data_to_write)));\n\n    if (output_time_) {\n      Parallel::printf(\n          \"Simulation time: %s\\n\"\n          \"  Wall time: %s\\n\",\n          std::to_string(observation_value.value), sys::pretty_wall_time());\n    }\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | subfile_path_;\n    p | output_time_;\n    p | legend_;\n  }\n\n private:\n  std::string subfile_path_;\n  bool output_time_;\n  std::vector<std::string> legend_;\n};\n\nObserveTimeStep::ObserveTimeStep(const std::string& subfile_name,\n                                 const bool output_time)\n    : subfile_path_(\"/Cce/\" + subfile_name),\n      output_time_(output_time),\n      legend_(std::vector<std::string>{\"Time\", \"Time Step\"}) {}\n\n/// \\cond\nPUP::able::PUP_ID ObserveTimeStep::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace Cce::Events\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/ExtractionRadius.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/ExtractionRadius.hpp\"\n\n#include <optional>\n#include <stdexcept>\n#include <string>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Cce {\nstd::string get_text_radius(const std::string& cce_data_filename) {\n  const size_t r_pos = cce_data_filename.find_last_of('R');\n  const size_t dot_pos = cce_data_filename.find_last_of('.');\n  return cce_data_filename.substr(r_pos + 1, dot_pos - r_pos - 1);\n}\n\nstd::optional<double> get_extraction_radius(\n    const std::string& cce_data_filename,\n    const std::optional<double>& extraction_radius, const bool error) {\n  const std::string text_radius = get_text_radius(cce_data_filename);\n  std::optional<double> result{};\n  try {\n    result = extraction_radius.has_value() ? extraction_radius.value()\n                                           : std::stod(text_radius);\n  } catch (const std::invalid_argument&) {\n    if (error) {\n      ERROR(\n          \"Either specify an extraction radius, or the CCE worldtube filename \"\n          \"must encode the extraction radius as an integer between the last \"\n          \"instance of 'R' and the last instance of '.' (SpEC CCE filename \"\n          \"format). Provided filename: \"\n          << cce_data_filename);\n    }\n  }\n\n  return result;\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/ExtractionRadius.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <string>\n\nnamespace Cce {\n/// @{\n/*!\n * \\brief Retrieves the extraction radius from the specified file name.\n *\n * \\details We assume that the filename has the extraction radius encoded as an\n * integer between the last occurrence of 'R' and the last occurrence of '.'.\n * This is the format provided by SpEC.\n */\nstd::string get_text_radius(const std::string& cce_data_filename);\n\nstd::optional<double> get_extraction_radius(\n    const std::string& cce_data_filename,\n    const std::optional<double>& extraction_radius, bool error = true);\n/// @}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/GaugeTransformBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n\nnamespace Cce {\n\nvoid GaugeAdjustedBoundaryValue<Tags::BondiR>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        evolution_gauge_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& cauchy_gauge_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n    const Spectral::Swsh::SwshInterpolator& interpolator) {\n  interpolator.interpolate(make_not_null(&get(*evolution_gauge_r)),\n                           get(cauchy_gauge_r));\n  get(*evolution_gauge_r) = get(*evolution_gauge_r) * get(omega);\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::DuRDividedByR>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        evolution_gauge_du_r_divided_by_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>&\n        cauchy_gauge_du_r_divided_by_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_u_at_scri,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& du_omega,\n    const Spectral::Swsh::SwshInterpolator& interpolator, const size_t l_max) {\n  interpolator.interpolate(\n      make_not_null(&get(*evolution_gauge_du_r_divided_by_r)),\n      get(cauchy_gauge_du_r_divided_by_r));\n\n  // Allocation\n  const SpinWeighted<ComplexDataVector, 0> r_buffer =\n      get(evolution_gauge_r) / get(omega);\n  const auto eth_r =\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::Eth>(l_max, 1,\n                                                                    r_buffer);\n  get(*evolution_gauge_du_r_divided_by_r) +=\n      0.5 *\n          (get(bondi_u_at_scri) * conj(eth_r) +\n           conj(get(bondi_u_at_scri)) * eth_r) /\n          r_buffer +\n      get(du_omega) / get(omega);\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::BondiJ>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n        evolution_gauge_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& cauchy_gauge_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n    const Spectral::Swsh::SwshInterpolator& interpolator) {\n  interpolator.interpolate(make_not_null(&get(*evolution_gauge_j)),\n                           get(cauchy_gauge_j));\n\n  get(*evolution_gauge_j).data() =\n      0.25 *\n      (square(conj(get(gauge_d).data())) * get(*evolution_gauge_j).data() +\n       square(get(gauge_c).data()) * conj(get(*evolution_gauge_j).data()) +\n       2.0 * get(gauge_c).data() * conj(get(gauge_d).data()) *\n           sqrt(1.0 + get(*evolution_gauge_j).data() *\n                          conj(get(*evolution_gauge_j).data()))) /\n      square(get(omega).data());\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::Dr<Tags::BondiJ>>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n        evolution_gauge_dr_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& cauchy_gauge_dr_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& cauchy_gauge_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n    const Spectral::Swsh::SwshInterpolator& interpolator, const size_t l_max) {\n  interpolator.interpolate(make_not_null(&get(*evolution_gauge_dr_j)),\n                           get(cauchy_gauge_dr_j));\n\n  SpinWeighted<ComplexDataVector, 2> interpolated_j{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  interpolator.interpolate(make_not_null(&interpolated_j), get(cauchy_gauge_j));\n  get(*evolution_gauge_dr_j).data() =\n      (0.25 * square(conj(get(gauge_d).data())) *\n           get(*evolution_gauge_dr_j).data() +\n       0.25 * square(get(gauge_c).data()) *\n           conj(get(*evolution_gauge_dr_j).data()) +\n       0.25 * get(gauge_c).data() * conj(get(gauge_d).data()) *\n           (get(*evolution_gauge_dr_j).data() * conj(interpolated_j.data()) +\n            conj(get(*evolution_gauge_dr_j).data()) * interpolated_j.data()) /\n           sqrt(1.0 + interpolated_j.data() * conj(interpolated_j.data()))) /\n      pow<3>(get(omega).data());\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::BondiBeta>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        evolution_gauge_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& cauchy_gauge_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n    const Spectral::Swsh::SwshInterpolator& interpolator) {\n  interpolator.interpolate(make_not_null(&get(*evolution_gauge_beta)),\n                           get(cauchy_gauge_beta));\n  get(*evolution_gauge_beta).data() -= 0.5 * log(get(omega).data());\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::BondiQ>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> evolution_gauge_q,\n    const SpinWeighted<ComplexDataVector, 1>& cauchy_gauge_dr_u,\n    const SpinWeighted<ComplexDataVector, 2>& volume_j,\n    const SpinWeighted<ComplexDataVector, 2>& volume_dy_j,\n    const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_r,\n    const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_beta,\n    const SpinWeighted<ComplexDataVector, 2>& gauge_c,\n    const SpinWeighted<ComplexDataVector, 0>& gauge_d,\n    const SpinWeighted<ComplexDataVector, 0>& omega,\n    const SpinWeighted<ComplexDataVector, 1>& eth_omega,\n    const Spectral::Swsh::SwshInterpolator& interpolator, const size_t l_max) {\n  interpolator.interpolate(evolution_gauge_q, cauchy_gauge_dr_u);\n\n  const SpinWeighted<ComplexDataVector, 2> evolution_gauge_j;\n  make_const_view(make_not_null(&evolution_gauge_j), volume_j, 0,\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const SpinWeighted<ComplexDataVector, 2> evolution_gauge_dy_j;\n  make_const_view(make_not_null(&evolution_gauge_dy_j), volume_dy_j, 0,\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  // Allocation\n  // optimization note: this allocation can be eliminated in favor of more\n  // make_const_view s at the cost of more tightly constraining the order of\n  // operations between this gauge transformation function and the volume\n  // computations\n  SpinWeighted<ComplexDataVector, 0> evolution_gauge_k;\n  evolution_gauge_k.data() =\n      sqrt(1.0 + evolution_gauge_j.data() * conj(evolution_gauge_j.data()));\n\n  // we reuse the storage for the `evolution_gauge_q`. After the interpolation\n  // it is \\partial_r U(\\hat x), and after the below assignment it is\n  // \\partial_{\\hat r} \\hat U.\n  // Note also that by necessity we use derivatives with respect to y, which are\n  // related to derivatives with respect to r by\n  // 2.0 / r \\partial_y = \\partial_r\n  *evolution_gauge_q =\n      0.5 / pow<3>(omega) *\n          (conj(gauge_d) * *evolution_gauge_q -\n           gauge_c * conj(*evolution_gauge_q)) -\n      ((eth_omega * evolution_gauge_k - conj(eth_omega) * evolution_gauge_j) *\n           (-1.0 + (evolution_gauge_dy_j * conj(evolution_gauge_dy_j) -\n                    0.25 *\n                        square(evolution_gauge_j * conj(evolution_gauge_dy_j) +\n                               evolution_gauge_dy_j * conj(evolution_gauge_j)) /\n                        square(evolution_gauge_k))) +\n       2.0 * (-conj(eth_omega) * evolution_gauge_dy_j +\n              0.5 * eth_omega / evolution_gauge_k *\n                  (evolution_gauge_j * conj(evolution_gauge_dy_j) +\n                   conj(evolution_gauge_j) * evolution_gauge_dy_j))) *\n          exp(2.0 * evolution_gauge_beta) / (square(evolution_gauge_r) * omega);\n\n  *evolution_gauge_q = square(evolution_gauge_r) *\n                       exp(-2.0 * evolution_gauge_beta) *\n                       (evolution_gauge_j * conj(*evolution_gauge_q) +\n                        evolution_gauge_k * *evolution_gauge_q);\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::BondiU>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n        evolution_gauge_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& cauchy_gauge_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_omega,\n    const Spectral::Swsh::SwshInterpolator& interpolator, const size_t l_max) {\n  interpolator.interpolate(make_not_null(&get(*evolution_gauge_u)),\n                           get(cauchy_gauge_u));\n\n  const SpinWeighted<ComplexDataVector, 2> evolution_gauge_j;\n  make_const_view(make_not_null(&evolution_gauge_j), get(volume_j), 0,\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  get(*evolution_gauge_u) =\n      0.5 / square(get(omega)) *\n          (-get(gauge_c) * conj(get(*evolution_gauge_u)) +\n           conj(get(gauge_d)) * get(*evolution_gauge_u)) +\n      exp(2.0 * get(evolution_gauge_beta)) /\n          (get(evolution_gauge_r) * get(omega)) *\n          (conj(get(eth_omega)) * evolution_gauge_j -\n           get(eth_omega) *\n               sqrt(1.0 + evolution_gauge_j * conj(evolution_gauge_j)));\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::BondiW>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> evolution_gauge_w,\n    const SpinWeighted<ComplexDataVector, 0>& cauchy_gauge_w,\n    const SpinWeighted<ComplexDataVector, 2>& volume_j,\n    const SpinWeighted<ComplexDataVector, 1>& evolution_gauge_u,\n    const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_beta,\n    const SpinWeighted<ComplexDataVector, 1>& evolution_gauge_u_at_scri,\n    const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_r,\n    const SpinWeighted<ComplexDataVector, 0>& omega,\n    const SpinWeighted<ComplexDataVector, 0>& du_omega,\n    const SpinWeighted<ComplexDataVector, 1>& eth_omega,\n    const Spectral::Swsh::SwshInterpolator& interpolator, const size_t l_max) {\n  interpolator.interpolate(evolution_gauge_w, cauchy_gauge_w);\n\n  const SpinWeighted<ComplexDataVector, 2> evolution_gauge_j;\n  make_const_view(make_not_null(&evolution_gauge_j), volume_j, 0,\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  // note that at this point in the computation `evolution_gauge_u` is \\mathcal\n  // U from the documentation, so\n  // `evolution_gauge_u - evolution_gauge_u_at_scri`\n  // is \\mathcal U - \\mathcal U^{(0)} = \\hat U\n  *evolution_gauge_w +=\n      (omega - 1.0) / evolution_gauge_r - 2.0 * du_omega / omega -\n      (conj(eth_omega) * (evolution_gauge_u - evolution_gauge_u_at_scri) +\n       eth_omega * conj(evolution_gauge_u - evolution_gauge_u_at_scri)) /\n          omega +\n      0.5 * exp(2.0 * evolution_gauge_beta) /\n          (square(omega) * evolution_gauge_r) *\n          (square(conj(eth_omega)) * evolution_gauge_j +\n           square(eth_omega) * conj(evolution_gauge_j) -\n           2.0 * eth_omega * conj(eth_omega) *\n               sqrt(1.0 + evolution_gauge_j * conj(evolution_gauge_j)));\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::BondiH>::apply_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> evolution_gauge_h,\n    const SpinWeighted<ComplexDataVector, 2>& volume_j,\n    const SpinWeighted<ComplexDataVector, 2>& cauchy_gauge_du_j,\n    const SpinWeighted<ComplexDataVector, 2>& volume_dy_j,\n    const SpinWeighted<ComplexDataVector, 1>& evolution_gauge_u_at_scri,\n    const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_r,\n    const SpinWeighted<ComplexDataVector, 2>& gauge_c,\n    const SpinWeighted<ComplexDataVector, 0>& gauge_d,\n    const SpinWeighted<ComplexDataVector, 0>& omega,\n    const SpinWeighted<ComplexDataVector, 0>& du_omega,\n    const SpinWeighted<ComplexDataVector, 1>& eth_omega,\n    const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_du_r_divided_by_r,\n    const Spectral::Swsh::SwshInterpolator& interpolator, const size_t l_max) {\n  // optimization note: this has several spin-weighted derivatives, they can\n  // be aggregated\n\n  const SpinWeighted<ComplexDataVector, 2> evolution_gauge_j;\n  make_const_view(make_not_null(&evolution_gauge_j), volume_j, 0,\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const SpinWeighted<ComplexDataVector, 2> evolution_gauge_dy_j;\n  make_const_view(make_not_null(&evolution_gauge_dy_j), volume_dy_j, 0,\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  // optimization note: this allocation could potentially be moved to the\n  // DataBox and this function would then have an additional pointer argument\n  // for the buffer.\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<3, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<4, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<5, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<6, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<7, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<8, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<9, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<10, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>>>\n      computation_buffers{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n  auto& interpolated_j =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 2>>>(\n          computation_buffers));\n  interpolated_j =\n      0.25 * (square(gauge_d) * evolution_gauge_j +\n              square(gauge_c) * conj(evolution_gauge_j) -\n              2.0 * gauge_c * gauge_d *\n                  sqrt(1.0 + evolution_gauge_j * conj(evolution_gauge_j)));\n\n  auto& interpolated_k =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  interpolated_k = sqrt(1.0 + interpolated_j * conj(interpolated_j));\n\n  auto& evolution_gauge_k =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  evolution_gauge_k = sqrt(1.0 + evolution_gauge_j * conj(evolution_gauge_j));\n\n  auto& evolution_gauge_u_at_scri_bar_j =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<3, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  evolution_gauge_u_at_scri_bar_j =\n      conj(evolution_gauge_u_at_scri) * evolution_gauge_j;\n\n  auto& eth_evolution_gauge_u_at_scri_bar_j =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<6, ComplexDataVector>,\n                                   std::integral_constant<int, 2>>>(\n          computation_buffers));\n  auto& eth_evolution_gauge_r =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<7, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& eth_evolution_gauge_u_at_scri =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<8, ComplexDataVector>,\n                                   std::integral_constant<int, 2>>>(\n          computation_buffers));\n  auto& ethbar_evolution_gauge_u_at_scri =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<9, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  auto& ethbar_evolution_gauge_j =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<10, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::Eth, Spectral::Swsh::Tags::Eth,\n                 Spectral::Swsh::Tags::Eth, Spectral::Swsh::Tags::Ethbar,\n                 Spectral::Swsh::Tags::Ethbar>>(\n      l_max, 1, make_not_null(&eth_evolution_gauge_u_at_scri_bar_j),\n      make_not_null(&eth_evolution_gauge_r),\n      make_not_null(&eth_evolution_gauge_u_at_scri),\n      make_not_null(&ethbar_evolution_gauge_u_at_scri),\n      make_not_null(&ethbar_evolution_gauge_j),\n      evolution_gauge_u_at_scri_bar_j, evolution_gauge_r,\n      evolution_gauge_u_at_scri, evolution_gauge_u_at_scri, evolution_gauge_j);\n\n  auto& evolution_gauge_u_at_scri_bar_eth_j =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<4, ComplexDataVector>,\n                                   std::integral_constant<int, 2>>>(\n          computation_buffers));\n  // this is \\bar{\\mathcal U}^{(0)} \\eth J, but we have to calculate it\n  // using quantities of spin 2 or less.\n  // note the conversion Jacobian for the angular derivative\n  evolution_gauge_u_at_scri_bar_eth_j =\n      eth_evolution_gauge_u_at_scri_bar_j -\n      evolution_gauge_j * conj(ethbar_evolution_gauge_u_at_scri) -\n      2.0 * conj(evolution_gauge_u_at_scri) * eth_evolution_gauge_r *\n          (evolution_gauge_dy_j) / evolution_gauge_r;\n\n  auto& cauchy_du_omega =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<5, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  cauchy_du_omega =\n      du_omega - 0.5 * (evolution_gauge_u_at_scri * conj(eth_omega) +\n                              conj(evolution_gauge_u_at_scri) * eth_omega);\n\n  // Note that by necessity we use derivatives with respect to y, which are\n  // related to derivatives with respect to r by\n  // 2.0 / r \\partial_y = \\partial_r\n\n  // Note also that when angular derivatives are taken numerically, they must be\n  // corrected to the Bondi \\eth with jacobians proportional to \\eth R / R\n\n  // `cauchy_du_omega` is \\partial_u \\hat \\omega, determined by an angular\n  // jacobian that depends on the \\mathcal U^{(0)}.\n  interpolator.interpolate(evolution_gauge_h, cauchy_gauge_du_j);\n  *evolution_gauge_h =\n      0.5 * (evolution_gauge_u_at_scri * ethbar_evolution_gauge_j -\n             2.0 * evolution_gauge_u_at_scri * conj(eth_evolution_gauge_r) /\n                 evolution_gauge_r * evolution_gauge_dy_j +\n             evolution_gauge_u_at_scri_bar_eth_j) -\n      2.0 * cauchy_du_omega / omega *\n          (evolution_gauge_dy_j - evolution_gauge_j) -\n      ethbar_evolution_gauge_u_at_scri * (evolution_gauge_j) +\n      eth_evolution_gauge_u_at_scri * evolution_gauge_k +\n      0.25 / square(omega) *\n          (square(conj(gauge_d)) * *evolution_gauge_h +\n           square(gauge_c) * conj(*evolution_gauge_h) +\n           gauge_c * conj(gauge_d) *\n               (*evolution_gauge_h * conj(interpolated_j) +\n                interpolated_j * conj(*evolution_gauge_h)) /\n               interpolated_k) +\n      2.0 * evolution_gauge_du_r_divided_by_r * (evolution_gauge_dy_j);\n}\n\nvoid GaugeAdjustedBoundaryValue<Tags::KleinGordonPi>::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> evolution_kg_pi,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& cauchy_kg_pi,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& evolution_gauge_u_at_scri,\n    const Spectral::Swsh::SwshInterpolator& interpolator, const size_t l_max,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& volume_psi) {\n  interpolator.interpolate(make_not_null(&get(*evolution_kg_pi)),\n                           get(cauchy_kg_pi));\n\n  const SpinWeighted<ComplexDataVector, 0> surface_psi;\n  make_const_view(make_not_null(&surface_psi), get(volume_psi), 0,\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n  SpinWeighted<ComplexDataVector, 1> eth_psi =\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::Eth>(\n          l_max, 1, surface_psi);\n\n  get(*evolution_kg_pi).data() +=\n      real(get(evolution_gauge_u_at_scri).data() * conj(eth_psi).data());\n}\n\nvoid GaugeUpdateTimeDerivatives::apply(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_du_x,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n        evolution_gauge_u_at_scri,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> volume_u,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> du_omega,\n    const tnsr::i<DataVector, 3>& cartesian_cauchy_coordinates,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_omega,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_radial_points =\n      get(*volume_u).size() / number_of_angular_points;\n\n  const ComplexDataVector u_scri_view;\n  make_const_view(make_not_null(&u_scri_view), get(*volume_u).data(),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  get(*evolution_gauge_u_at_scri).data() = u_scri_view;\n\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    ComplexDataVector angular_view_u{\n        get(*volume_u).data().data() +\n            i * Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    angular_view_u -= get(*evolution_gauge_u_at_scri).data();\n  }\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<3, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<4, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<5, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<6, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<7, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>>>\n      computation_buffers{number_of_angular_points};\n\n  auto& x =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  auto& y =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  auto& z =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  // Switch to complex so we can take spin-weighted derivatives\n  x.data() =\n      std::complex<double>(1.0, 0.0) * get<0>(cartesian_cauchy_coordinates);\n  y.data() =\n      std::complex<double>(1.0, 0.0) * get<1>(cartesian_cauchy_coordinates);\n  z.data() =\n      std::complex<double>(1.0, 0.0) * get<2>(cartesian_cauchy_coordinates);\n\n  auto& eth_x =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<3, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& eth_y =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<4, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& eth_z =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<5, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& eth_evolution_gauge_u_at_scri =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<6, ComplexDataVector>,\n                                   std::integral_constant<int, 2>>>(\n          computation_buffers));\n\n  auto& ethbar_evolution_gauge_u_at_scri =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<7, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::Eth, Spectral::Swsh::Tags::Eth,\n                 Spectral::Swsh::Tags::Eth, Spectral::Swsh::Tags::Eth,\n                 Spectral::Swsh::Tags::Ethbar>>(\n      l_max, 1, make_not_null(&eth_x), make_not_null(&eth_y),\n      make_not_null(&eth_z), make_not_null(&eth_evolution_gauge_u_at_scri),\n      make_not_null(&ethbar_evolution_gauge_u_at_scri), x, y, z,\n      get(*evolution_gauge_u_at_scri), get(*evolution_gauge_u_at_scri));\n  get<0>(*cartesian_cauchy_du_x) =\n      real(conj(get(*evolution_gauge_u_at_scri).data()) * eth_x.data());\n  get<1>(*cartesian_cauchy_du_x) =\n      real(conj(get(*evolution_gauge_u_at_scri).data()) * eth_y.data());\n  get<2>(*cartesian_cauchy_du_x) =\n      real(conj(get(*evolution_gauge_u_at_scri).data()) * eth_z.data());\n\n  get(*du_omega) =\n      0.25 *\n          (ethbar_evolution_gauge_u_at_scri +\n           conj(ethbar_evolution_gauge_u_at_scri)) *\n          get(omega) +\n      0.5 * (get(*evolution_gauge_u_at_scri) * conj(get(eth_omega)) +\n             conj(get(*evolution_gauge_u_at_scri)) * get(eth_omega));\n}\n\nvoid GaugeUpdateInertialTimeDerivatives::apply(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_inertial_du_x,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n        evolution_gauge_u_at_scri,\n    const tnsr::i<DataVector, 3>& cartesian_inertial_coordinates,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_cauchy_c,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_cauchy_d,\n    const size_t l_max, const Spectral::Swsh::SwshInterpolator& interpolator) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<3, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<4, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<5, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<6, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<7, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>>>\n      computation_buffers{number_of_angular_points};\n\n  auto& x_inertial =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  auto& y_inertial =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  auto& z_inertial =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n\n  x_inertial.data() =\n      std::complex<double>(1.0, 0.0) * get<0>(cartesian_inertial_coordinates);\n  y_inertial.data() =\n      std::complex<double>(1.0, 0.0) * get<1>(cartesian_inertial_coordinates);\n  z_inertial.data() =\n      std::complex<double>(1.0, 0.0) * get<2>(cartesian_inertial_coordinates);\n\n  auto& eth_x_inertial =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<3, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& eth_y_inertial =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<4, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& eth_z_inertial =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<5, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::Eth, Spectral::Swsh::Tags::Eth,\n                 Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&eth_x_inertial), make_not_null(&eth_y_inertial),\n      make_not_null(&eth_z_inertial), x_inertial, y_inertial, z_inertial);\n\n  // Interpolate evolution_gauge_u_at_scri and omega to the Cauchy coordinates\n  auto& original_u_at_scri =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<6, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& ome_inte =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<7, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  interpolator.interpolate(make_not_null(&original_u_at_scri),\n                           get(*evolution_gauge_u_at_scri));\n  interpolator.interpolate(make_not_null(&ome_inte), get(omega));\n\n  // Eq. (79) of \\cite Moxon2020gha\n  original_u_at_scri = 0.5 * (-get(gauge_cauchy_c) * conj(original_u_at_scri) +\n                              conj(get(gauge_cauchy_d)) * original_u_at_scri);\n  original_u_at_scri.data() *= square(ome_inte.data());\n\n  get<0>(*cartesian_inertial_du_x) =\n      -real(conj(original_u_at_scri.data()) * eth_x_inertial.data());\n  get<1>(*cartesian_inertial_du_x) =\n      -real(conj(original_u_at_scri.data()) * eth_y_inertial.data());\n  get<2>(*cartesian_inertial_du_x) =\n      -real(conj(original_u_at_scri.data()) * eth_z_inertial.data());\n}\n\nnamespace detail {\nvoid gauge_update_jacobian_from_coordinates_apply_impl(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n        gauge_factor_spin_2,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        gauge_factor_spin_0,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n        angular_source_coordinates,\n    const tnsr::i<DataVector, 3>& cartesian_source_coordinates,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  Variables<\n      tmpl::list<::Tags::TempiJ<0, 3>,\n                 ::Tags::TempiJ<0, 2, ::Frame::Spherical<::Frame::Inertial>>>>\n      tensor_buffers{number_of_angular_points};\n  auto& angular_derivative_cartesian_source_coordinates =\n      get<::Tags::TempiJ<0, 3>>(tensor_buffers);\n  auto& angular_derivative_angular_source_coordinates =\n      get<::Tags::TempiJ<0, 2, ::Frame::Spherical<::Frame::Inertial>>>(\n          tensor_buffers);\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<3, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<4, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<5, ComplexDataVector>,\n                                      std::integral_constant<int, 1>>>>\n      computation_buffers{number_of_angular_points};\n\n  auto& x =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  auto& y =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  auto& z =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          computation_buffers));\n  // Switch to complex so we can take spin-weighted derivatives\n  x.data() =\n      std::complex<double>(1.0, 0.0) * get<0>(cartesian_source_coordinates);\n  y.data() =\n      std::complex<double>(1.0, 0.0) * get<1>(cartesian_source_coordinates);\n  z.data() =\n      std::complex<double>(1.0, 0.0) * get<2>(cartesian_source_coordinates);\n\n  auto& eth_x =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<3, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& eth_y =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<4, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  auto& eth_z =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<5, ComplexDataVector>,\n                                   std::integral_constant<int, 1>>>(\n          computation_buffers));\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::Eth, Spectral::Swsh::Tags::Eth,\n                 Spectral::Swsh::Tags::Eth>>(l_max, 1, make_not_null(&eth_x),\n                                             make_not_null(&eth_y),\n                                             make_not_null(&eth_z), x, y, z);\n  get<0, 0>(angular_derivative_cartesian_source_coordinates) =\n      -real(eth_x.data());\n  get<1, 0>(angular_derivative_cartesian_source_coordinates) =\n      -imag(eth_x.data());\n  get<0, 1>(angular_derivative_cartesian_source_coordinates) =\n      -real(eth_y.data());\n  get<1, 1>(angular_derivative_cartesian_source_coordinates) =\n      -imag(eth_y.data());\n  get<0, 2>(angular_derivative_cartesian_source_coordinates) =\n      -real(eth_z.data());\n  get<1, 2>(angular_derivative_cartesian_source_coordinates) =\n      -imag(eth_z.data());\n\n  for (size_t i = 0; i < 2; ++i) {\n    angular_derivative_angular_source_coordinates.get(i, 0) =\n        cos(get<1>(angular_source_coordinates)) *\n            cos(get<0>(angular_source_coordinates)) *\n            angular_derivative_cartesian_source_coordinates.get(i, 0) +\n        cos(get<0>(angular_source_coordinates)) *\n            sin(get<1>(angular_source_coordinates)) *\n            angular_derivative_cartesian_source_coordinates.get(i, 1) -\n        sin(get<0>(angular_source_coordinates)) *\n            angular_derivative_cartesian_source_coordinates.get(i, 2);\n    angular_derivative_angular_source_coordinates.get(i, 1) =\n        -sin(get<1>(angular_source_coordinates)) *\n            angular_derivative_cartesian_source_coordinates.get(i, 0) +\n        cos(get<1>(angular_source_coordinates)) *\n            angular_derivative_cartesian_source_coordinates.get(i, 1);\n  }\n\n  // in the standard evaluation, this is \\hat c\n  get(*gauge_factor_spin_2).data() =\n      std::complex<double>(1.0, 0.0) *\n          (get<0, 0>(angular_derivative_angular_source_coordinates) -\n           get<1, 1>(angular_derivative_angular_source_coordinates)) +\n      std::complex<double>(0.0, 1.0) *\n          (get<1, 0>(angular_derivative_angular_source_coordinates) +\n           get<0, 1>(angular_derivative_angular_source_coordinates));\n  // in the standard evaluation, this is \\hat d\n  get(*gauge_factor_spin_0).data() =\n      std::complex<double>(1.0, 0.0) *\n          (get<0, 0>(angular_derivative_angular_source_coordinates) +\n           get<1, 1>(angular_derivative_angular_source_coordinates)) +\n      std::complex<double>(0.0, 1.0) *\n          (-get<1, 0>(angular_derivative_angular_source_coordinates) +\n           get<0, 1>(angular_derivative_angular_source_coordinates));\n}\n}  // namespace detail\n\ntemplate <typename GaugeC, typename GaugeD, typename GaugeOmega>\nvoid GaugeUpdateOmega<GaugeC, GaugeD, GaugeOmega>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> omega,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_omega,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n    const size_t l_max) {\n  get(*omega) = 0.5 * sqrt(get(gauge_d).data() * conj(get(gauge_d).data()) -\n                           get(gauge_c).data() * conj(get(gauge_c).data()));\n\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&get(*eth_omega)), get(*omega));\n}\n\nvoid InitializeGauge::apply(\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> gauge_c,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> gauge_d,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> omega,\n    const size_t l_max) {\n  Spectral::Swsh::create_angular_and_cartesian_coordinates(\n      cartesian_cauchy_coordinates, angular_cauchy_coordinates, l_max);\n  get(*omega).data() = 1.0;\n  get(*gauge_c).data() = 0.0;\n  get(*gauge_d).data() = 2.0;\n}\n\ntemplate struct GaugeUpdateOmega<Tags::PartiallyFlatGaugeC,\n                                 Tags::PartiallyFlatGaugeD,\n                                 Tags::PartiallyFlatGaugeOmega>;\ntemplate struct GaugeUpdateOmega<Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n                                 Tags::CauchyGaugeOmega>;\n}  // namespace Cce\n\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n/// The set of tags that should be calculated before the initial data is\n/// computed on the first hypersurface.\nusing gauge_adjustments_setup_tags =\n    tmpl::list<Tags::BondiR, Tags::BondiJ, Tags::Dr<Tags::BondiJ>>;\n\n/*!\n * \\brief Computes the gauge-transformed\n * `Tags::EvolutionGaugeBoundaryValue<Tag>` for any of the boundary tags needed\n * in the evolution.\n *\n * \\details Most of these computations involve first interpolating via a\n * `Spectral::Swsh::SwshInterpolator` to the new angular grid, followed by\n * manipulations associated with the tensor transformation of the metric and its\n * derivatives. Individual template specializations contain detailed\n * explanations about the respective gauge transformations.\n */\ntemplate <typename Tag>\nstruct GaugeAdjustedBoundaryValue;\n\n/*!\n * \\brief Computes the evolution gauge Bondi \\f$\\hat R\\f$ on the worldtube from\n * Cauchy gauge quantities\n *\n * \\details The evolution gauge Bondi \\f$\\hat R\\f$ obeys:\n *\n * \\f{align*}{\n * \\hat R = \\hat \\omega R(\\hat x^{\\hat A}),\n * \\f}\n *\n * where the evaluation of \\f$R\\f$ at \\f$\\hat x^{\\hat A}\\f$ requires an\n * interpolation to the evolution coordinates, and \\f$\\hat \\omega\\f$ is the\n * conformal factor associated with the angular part of the gauge\n * transformation.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::BondiR> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>>;\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::BondiR>, Tags::PartiallyFlatGaugeOmega,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          evolution_gauge_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& cauchy_gauge_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator);\n};\n\n/*!\n * \\brief Computes the evolution gauge \\f$\\partial_{\\hat u} \\hat R / \\hat R\\f$\n * on the worldtube.\n *\n * \\details The evolution gauge quantity \\f$ \\partial_{\\hat u} \\hat R / \\hat\n * R\\f$ obeys\n *\n * \\f{align*}{\n *  \\frac{\\partial_{\\hat u} \\hat R}{ \\hat R}\n * = \\frac{\\partial_u R (\\hat x^{\\hat A})}{R(\\hat x^{\\hat A})}\n * + \\frac{\\partial_{\\hat u} \\hat \\omega}{\\hat \\omega}\n * + \\frac{\\mathcal U^{(0)} \\bar \\eth R(\\hat x^{\\hat A})\n * + \\bar{\\mathcal U}^{(0)} \\eth R(\\hat x^{\\hat A}) }{2 R(\\hat x^{\\hat A})}\n * \\f}\n *\n * note that the terms proportional to \\f$\\eth R\\f$ or its conjugate arise from\n * the conversion between \\f$\\partial_u\\f$ and \\f$\\partial_{\\hat u}f\\f$. The\n * right-hand side quantities with explicit \\f$\\hat x\\f$ require interpolation.\n * \\f$\\mathcal U^{(0)}\\f$ is the asymptotic quantity determined by\n * `GaugeUpdateTimeDerivatives`.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::DuRDividedByR> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::DuRDividedByR>>;\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::DuRDividedByR>, Tags::BondiUAtScri,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Tags::LMax>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          evolution_gauge_du_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&\n          cauchy_gauge_du_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_u_at_scri,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& du_omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator, size_t l_max);\n};\n\n/*!\n * \\brief Computes the evolution gauge quantity \\f$\\hat J\\f$ on the worldtube\n *\n * \\details The evolution gauge quantity \\f$\\hat J\\f$ obeys\n *\n * \\f{align*}{\n * \\hat J = \\frac{1}{4 \\hat{\\omega}^2} \\left( \\bar{\\hat d}^2  J(\\hat x^{\\hat A})\n * + \\hat c^2 \\bar J(\\hat x^{\\hat A})\n * + 2 \\hat c \\bar{\\hat d} K(\\hat x^{\\hat A}) \\right)\n * \\f}\n *\n * Where \\f$\\hat c\\f$ and \\f$\\hat d\\f$ are the spin-weighted angular Jacobian\n * factors computed by `GaugeUpdateJacobianFromCoords`, and \\f$\\hat \\omega\\f$ is\n * the conformal factor associated with the angular coordinate transformation.\n * Note that the right-hand sides with explicit \\f$\\hat x^{\\hat A}\\f$ dependence\n * must be interpolated and that \\f$K = \\sqrt{1 + J \\bar J}\\f$.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::BondiJ> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::BondiJ>>;\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::BondiJ>, Tags::PartiallyFlatGaugeC,\n      Tags::PartiallyFlatGaugeD, Tags::PartiallyFlatGaugeOmega,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          evolution_gauge_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& cauchy_gauge_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator);\n};\n\n/*!\n * \\brief Computes the evolution gauge quantity \\f$\\partial_{\\hat r} \\hat J\\f$\n * on the worldtube\n *\n * \\details The evolution gauge quantity \\f$\\partial_{\\hat r} \\hat J\\f$ is\n * determined from \\f$\\partial_{\\hat r} = \\frac{\\partial_r}{\\hat \\omega}\\f$ and\n * the expression for \\f$\\hat J\\f$ given in the documentation for\n * `GaugeAdjustedBoundaryValue<Tags::BondiJ>`\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::Dr<Tags::BondiJ>> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::Dr<Tags::BondiJ>>>;\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>,\n      Tags::BoundaryValue<Tags::BondiJ>, Tags::PartiallyFlatGaugeC,\n      Tags::PartiallyFlatGaugeD, Tags::PartiallyFlatGaugeOmega,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Tags::LMax>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          evolution_gauge_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& cauchy_gauge_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& cauchy_gauge_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator, size_t l_max);\n};\n\n/*!\n * \\brief Computes the evolution gauge quantity \\f$\\hat \\beta\\f$ on the\n * worldtube\n *\n * \\details The evolution gauge quantity \\f$\\hat \\beta\\f$ obeys\n *\n * \\f{align*}{\n * e^{2 \\hat \\beta} = e^{2 \\beta(\\hat x^{\\hat A})} / \\hat \\omega.\n * \\f}\n *\n * The explicit evaluation at \\f$\\hat x^{\\hat A}\\f$ on the right-hand side\n * indicates the requirement of an interpolation step, and \\f$\\hat \\omega\\f$ is\n * the conformal factor associated with the angular transformation.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::BondiBeta> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::BondiBeta>>;\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::BondiBeta>, Tags::PartiallyFlatGaugeOmega,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          evolution_gauge_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& cauchy_gauge_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator);\n};\n\n/*!\n * \\brief Computes the evolution gauge quantity \\f$\\hat Q\\f$ on the worldtube.\n *\n * \\details The evolution gauge quantity \\f$\\hat Q\\f$ obeys\n *\n * \\f{align*}{\n * \\hat Q =& \\hat r^2 e^{-2 \\hat \\beta} (\\hat K \\partial_{\\hat r} \\hat U\n * + \\hat J \\partial_{\\hat r} \\hat{\\bar U}),\\\\\n * \\partial_{\\hat r} \\hat U\n * =& \\frac{1}{2 \\hat \\omega^3}\\left(\\hat{\\bar d} \\partial_r U(\\hat x^{\\hat A})\n * - \\hat c \\partial_r \\bar U(\\hat x^{\\hat A})\\right)\n * + \\frac{e^{2\\hat \\beta}}{\\hat r^2 \\hat \\omega}\n * \\left(\\hat J \\hat{\\bar{\\eth}} \\hat \\omega\n * - \\hat K \\hat \\eth \\hat \\omega\\right)\n * \\left(-1 + \\partial_{\\hat y} \\hat{\\bar{J}} \\partial_{\\hat y} \\hat J\n * - \\left[\\frac{\\partial_{\\hat y}(\\hat J \\hat{\\bar{J}})}\n * {2 \\hat K}\\right]^2\\right) \\notag \\\\\n * & + 2 \\frac{e^{2 \\hat \\beta}}{\\hat \\omega \\hat r^2}\n * \\left[ \\hat{\\bar{\\eth}} \\hat \\omega \\partial_{\\hat y} \\hat J\n * + \\hat{\\eth} \\hat \\omega \\left(-\\frac{\\hat J \\partial_{\\hat y}\n * \\hat{\\bar J}\n * + \\hat{\\bar J} \\partial_{\\hat y} \\hat J}{2 \\hat K}\\right) \\right].\n * \\f}\n *\n * where the explicit argument \\f$\\hat x^{\\hat A}\\f$ on the right-hand side\n * implies the need for an interpolation operation, and\n * \\f$K = \\sqrt{1 + J \\bar J}\\f$.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::BondiQ> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::BondiQ>>;\n\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::Dr<Tags::BondiU>>, Tags::BondiJ,\n      Tags::Dy<Tags::BondiJ>, Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiBeta>,\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Tags::LMax>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          evolution_gauge_q,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& cauchy_gauge_dr_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_dy_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator,\n      const size_t l_max) {\n    apply_impl(make_not_null(&get(*evolution_gauge_q)), get(cauchy_gauge_dr_u),\n               get(volume_j), get(volume_dy_j), get(evolution_gauge_r),\n               get(evolution_gauge_beta), get(gauge_c), get(gauge_d),\n               get(omega), get(eth_omega), interpolator, l_max);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> evolution_gauge_q,\n      const SpinWeighted<ComplexDataVector, 1>& cauchy_gauge_dr_u,\n      const SpinWeighted<ComplexDataVector, 2>& volume_j,\n      const SpinWeighted<ComplexDataVector, 2>& volume_dy_j,\n      const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_r,\n      const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_beta,\n      const SpinWeighted<ComplexDataVector, 2>& gauge_c,\n      const SpinWeighted<ComplexDataVector, 0>& gauge_d,\n      const SpinWeighted<ComplexDataVector, 0>& omega,\n      const SpinWeighted<ComplexDataVector, 1>& eth_omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator, size_t l_max);\n};\n\n/*!\n * \\brief Computes the evolution gauge quantity \\f$\\mathcal U\\f$ on the\n * worldtube.\n *\n * \\details Note that the boundary quantity computed by this function is, by\n * necessity, NOT the evolution gauge bondi \\f$\\hat U\\f$, because there is\n * insufficient information at the point in the computation this will be\n * evaluated to completely determine \\f$\\hat{U}\\f$. Instead, this determines\n * the boundary value of \\f$\\mathcal U\\f$, which satisfies,\n *\n * \\f{align*}{\n * \\mathcal{U} - \\mathcal{U}^{(0)} = \\hat U,\n * \\f}\n *\n * where the superscript \\f$(0)\\f$ denotes evaluation at \\f$\\mathcal I^+\\f$. In\n * particular, the result of this computation may be used with the same\n * hypersurface equations as the full evolution gauge \\f$\\hat U\\f$, because they\n * satisfy the same radial differential equation.\n *\n * \\f$\\mathcal U\\f$ is computed by,\n *\n * \\f{align*}{\n * \\mathcal U = \\frac{1}{2\\hat \\omega^2} \\left(\\hat{\\bar d} U(\\hat x^{\\hat A})\n * - \\hat c \\bar U(\\hat x^{\\hat A}) \\right)\n * - \\frac{e^{2 \\hat \\beta}}{\\hat r \\hat \\omega}\n * \\left(\\hat K \\hat \\eth \\hat \\omega\n * -  \\hat J\\hat{\\bar{\\eth}} \\hat \\omega\\right),\n * \\f}\n *\n * where the explicit argument \\f$\\hat x^{\\hat A}\\f$ on the right-hand side\n * implies the need for an interpolation operation, and\n * \\f$K = \\sqrt{1 + J \\bar J}\\f$.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::BondiU> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::BondiU>>;\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::BondiU>, Tags::BondiJ,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiBeta>,\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Tags::LMax>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          evolution_gauge_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& cauchy_gauge_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator, size_t l_max);\n};\n\n/*!\n * \\brief Computes the evolution gauge quantity \\f$\\hat W\\f$ on the worldtube.\n *\n * \\details The evolution gauge value \\f$\\hat W\\f$ obeys\n *\n * \\f{align*}{\n * \\hat W =& W(\\hat x^{\\hat A}) + (\\hat \\omega - 1) / \\hat r\n * + \\frac{e^{2 \\hat \\beta}}{2 \\hat \\omega^2 \\hat r}\n * \\left(\\hat J \\left(\\hat{\\bar \\eth} \\hat \\omega\\right)^2\n * + \\hat{\\bar{J}} \\left(\\hat \\eth \\hat \\omega\\right) ^2\n * - 2 K \\left( \\hat \\eth \\hat \\omega\\right) \\left(\\hat{\\bar \\eth} \\hat\n * \\omega\\right) \\right)\n * - \\frac{2 \\partial_{u} \\hat \\omega}{\\hat \\omega}\n * - \\frac{ \\hat U \\bar \\eth \\hat \\omega + \\hat{\\bar U} \\eth \\hat \\omega }\n * {\\hat \\omega},\n * \\f}\n *\n * where the explicit argument \\f$\\hat x^{\\hat A}\\f$ on the right-hand side\n * implies the need for an interpolation operation and\n * \\f$K = \\sqrt{1 + J \\bar J}\\f$.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::BondiW> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::BondiW>>;\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::BondiW>, Tags::BondiJ,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiU>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiBeta>, Tags::BondiUAtScri,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Tags::LMax>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          evolution_gauge_w,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& cauchy_gauge_w,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& evolution_gauge_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&\n          evolution_gauge_u_at_scri,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& du_omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator,\n      const size_t l_max) {\n    apply_impl(make_not_null(&get(*evolution_gauge_w)), get(cauchy_gauge_w),\n               get(volume_j), get(evolution_gauge_u), get(evolution_gauge_beta),\n               get(evolution_gauge_u_at_scri), get(evolution_gauge_r),\n               get(omega), get(du_omega), get(eth_omega), interpolator, l_max);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> evolution_gauge_w,\n      const SpinWeighted<ComplexDataVector, 0>& cauchy_gauge_w,\n      const SpinWeighted<ComplexDataVector, 2>& volume_j,\n      const SpinWeighted<ComplexDataVector, 1>& evolution_gauge_u,\n      const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_beta,\n      const SpinWeighted<ComplexDataVector, 1>& evolution_gauge_u_at_scri,\n      const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_r,\n      const SpinWeighted<ComplexDataVector, 0>& omega,\n      const SpinWeighted<ComplexDataVector, 0>& du_omega,\n      const SpinWeighted<ComplexDataVector, 1>& eth_omega,\n      const Spectral::Swsh::SwshInterpolator& interpolator, size_t l_max);\n};\n\n/*!\n * \\brief Computes the evolution gauge quantity \\f$\\hat H\\f$ on the worldtube.\n *\n * \\details The evolution gauge \\f$\\hat H\\f$ obeys\n *\n * \\f{align*}{\n *   \\hat H =&\n * \\frac{1}{2} \\left(\\mathcal{U}^{(0)} \\hat{\\bar \\eth} \\hat J\n * + \\bar{\\mathcal{U}}^{(0)} \\hat{\\eth} \\hat J\\right)\n * + \\frac{\\partial_{\\hat u} \\hat \\omega\n * - \\tfrac{1}{2} \\left(\\mathcal{U}^{(0)} \\bar{\\hat \\eth}\\hat \\omega\n * + \\bar{\\mathcal{U}}^{(0)} \\hat \\eth \\hat \\omega \\right) }{\\hat \\omega}\n * \\left(2 \\hat J - 2 \\partial_{\\hat y} \\hat J\\right)\n * - \\hat J\\hat{\\bar \\eth} \\mathcal{U}^{(0)}\n * + \\hat K \\hat \\eth \\bar{\\mathcal{U}}^{(0)}  \\notag\\\\\n * &+ \\frac{1}{4 \\hat \\omega^2} \\left(\\hat{\\bar d}^2 H(\\hat x^{\\hat A})\n * + \\hat c^2 \\bar H(\\hat x^{\\hat A})\n * + \\hat{\\bar d} \\hat c \\frac{H(\\hat x^{\\hat A}) \\bar J(\\hat x^{\\hat A})\n * + J(\\hat x^{\\hat A}) \\bar H(\\hat x^{\\hat A})}{K}\\right)\n * + 2 \\frac{\\partial_u R}{R} \\partial_{\\hat y} J\n * \\f}\n *\n * where the superscript \\f$(0)\\f$ denotes evaluation at \\f$\\mathcal I^+\\f$ and\n * the explicit \\f$\\hat x^{\\hat A}\\f$ arguments on the right-hand side imply\n * interpolation operations, and \\f$K = \\sqrt{1 + J \\bar J}\\f$,\n * \\f$\\hat K = \\sqrt{1 + \\hat J \\hat{\\bar J}}\\f$.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::BondiH> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::BondiH>>;\n  using argument_tags = tmpl::list<\n      Tags::BondiJ, Tags::BoundaryValue<Tags::Du<Tags::BondiJ>>,\n      Tags::Dy<Tags::BondiJ>, Tags::BondiUAtScri,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>,\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::DuRDividedByR>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Tags::LMax>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          evolution_gauge_h,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& cauchy_gauge_du_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_dy_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&\n          evolution_gauge_u_at_scri,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& evolution_gauge_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& du_omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&\n          evolution_gauge_du_r_divided_by_r,\n      const Spectral::Swsh::SwshInterpolator& interpolator,\n      const size_t l_max) {\n    apply_impl(make_not_null(&get(*evolution_gauge_h)), get(volume_j),\n               get(cauchy_gauge_du_j), get(volume_dy_j),\n               get(evolution_gauge_u_at_scri), get(evolution_gauge_r),\n               get(gauge_c), get(gauge_d), get(omega), get(du_omega),\n               get(eth_omega), get(evolution_gauge_du_r_divided_by_r),\n               interpolator, l_max);\n  }\n\n private:\n  static void apply_impl(\n      gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> evolution_gauge_h,\n      const SpinWeighted<ComplexDataVector, 2>& volume_j,\n      const SpinWeighted<ComplexDataVector, 2>& cauchy_gauge_du_j,\n      const SpinWeighted<ComplexDataVector, 2>& volume_dy_j,\n      const SpinWeighted<ComplexDataVector, 1>& evolution_gauge_u_at_scri,\n      const SpinWeighted<ComplexDataVector, 0>& evolution_gauge_r,\n      const SpinWeighted<ComplexDataVector, 2>& gauge_c,\n      const SpinWeighted<ComplexDataVector, 0>& gauge_d,\n      const SpinWeighted<ComplexDataVector, 0>& omega,\n      const SpinWeighted<ComplexDataVector, 0>& du_omega,\n      const SpinWeighted<ComplexDataVector, 1>& eth_omega,\n      const SpinWeighted<ComplexDataVector, 0>&\n          evolution_gauge_du_r_divided_by_r,\n      const Spectral::Swsh::SwshInterpolator& interpolator, size_t l_max);\n};\n\n/*!\n * \\brief Computes the evolution gauge quantity \\f$\\hat \\Pi\\f$ for the scalar\n * field on the worldtube.\n *\n * \\details The evolution gauge \\f$\\hat \\Pi\\f$ obeys\n * \\f{align*}{\n *   \\hat \\Pi = \\partial_{t^\\prime} \\psi + \\Re\n *   \\left(\\mathcal{U}^{(0)}\\bar{\\eth}\\psi\\right)\n * \\f}\n *\n * where \\f$\\partial_{t^\\prime} \\psi\\f$ comes from the Cauchy evolution.\n */\ntemplate <>\nstruct GaugeAdjustedBoundaryValue<Tags::KleinGordonPi> {\n  using return_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::KleinGordonPi>>;\n  using argument_tags = tmpl::list<\n      Tags::BoundaryValue<Tags::KleinGordonPi>, Tags::BondiUAtScri,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Tags::LMax, Tags::KleinGordonPsi>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          evolution_kg_pi,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& cauchy_kg_pi,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&\n          evolution_gauge_u_at_scri,\n      const Spectral::Swsh::SwshInterpolator& interpolator, size_t l_max,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& volume_psi);\n};\n\n/*!\n * \\brief Update the Cauchy gauge cartesian coordinate derivative \\f$\\partial_u\n * x(\\hat x)\\f$, as well as remaining gauge quantities \\f$\\mathcal U^{(0)}\\f$,\n * \\f$\\hat U \\equiv \\mathcal U - \\mathcal U^{(0)}\\f$, and \\f$\\partial_{\\hat u}\n * \\hat \\omega\\f$ to maintain asymptotically inertial angular coordinates.\n *\n * \\details The constraint we must satisfy to maintain the asymptotically\n * inertial angular coordinates is\n *\n * \\f{align*}{\n * \\partial_{\\hat u} x^A =  \\mathcal U^{(0) \\hat A} \\partial_{\\hat A} x^{A},\n * \\f}\n *\n * which we compute for a representative Cartesian coordinate set on the unit\n * sphere, to maintain representability and ensure that angular transform and\n * derivative operations keep the desired precision. The equation we use for the\n * Cartesian analog is:\n *\n * \\f{align*}{\n * \\partial_{\\hat u} x^i &= \\frac{1}{2} (\\bar{\\mathcal U}^{(0)} \\hat \\eth x^i +\n * \\mathcal U^{(0)} \\hat{\\bar \\eth} x^i ) \\\\\n * &= \\text{Re}(\\bar{\\mathcal U}^{(0)} \\hat \\eth x^i)\n * \\f}\n *\n * This computation completes the unfixed degrees of freedom for the coordinate\n * transformation at the boundary, so also computes the gauge quantities that\n * rely on this information \\f$\\mathcal U^{(0)}\\f$,\n * \\f$\\hat U\\f$, and \\f$\\partial_{\\hat u} \\hat \\omega\\f$.\n *\n * The time derivative of \\f$\\hat \\omega\\f$ is calculated from the equation\n * \\f{align*}{\n * \\partial_{\\hat u} \\hat \\omega\n * = \\frac{\\hat \\omega}{4} (\\hat{\\bar \\eth} \\mathcal U^{(0)}\n * + \\hat \\eth \\bar{\\mathcal U}^{(0)})\n * + \\frac{1}{2} (\\mathcal U^{(0)} \\hat{\\bar \\eth} \\hat \\omega\n * + \\bar{\\mathcal U}^{(0)} \\hat \\eth \\hat \\omega)\n * \\f}\n * \\warning Before this update call the quantity stored in the tag\n * `Cce::Tags::BondiU` represents \\f$\\mathcal U\\f$, and after this update call,\n * it represents \\f$\\hat U\\f$ (the true evolution gauge quantity).\n */\nstruct GaugeUpdateTimeDerivatives {\n  using return_tags =\n      tmpl::list<::Tags::dt<Tags::CauchyCartesianCoords>, Tags::BondiUAtScri,\n                 Tags::BondiU, Tags::Du<Tags::PartiallyFlatGaugeOmega>>;\n  using argument_tags =\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::PartiallyFlatGaugeOmega,\n                 Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::LMax>;\n\n  static void apply(\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_du_x,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          evolution_gauge_u_at_scri,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> volume_u,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> du_omega,\n      const tnsr::i<DataVector, 3>& cartesian_cauchy_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_omega,\n      size_t l_max);\n};\n\n/*!\n * \\brief Update the inertial gauge cartesian coordinate derivative\n * \\f$\\partial_u \\hat x(x)\\f$.\n *\n * \\details For the asymptotically inertial angular coordinates\n * \\f$\\hat{x}^{\\hat{A}}\\f$, we have:\n *\n * \\f{align*}{\n * \\partial_u \\hat{x}^{\\hat{A}} = -U^{(0)B}\\partial_B \\hat{x}^{\\hat{A}}\n * \\f}\n *\n * and the Cartesian version reads\n *\n * \\f{align*}{\n * \\partial_u \\hat{x}^{\\hat{i}}= - \\text{Re}(\\bar{U}^{(0)}\n * \\eth \\hat{x}^{\\hat{i}})\n * \\f}\n *\n * Note that \\f$U^{0}\\f$ and \\f$\\mathcal U^{(0)}\\f$ are related by\n *\n * \\f{align*}{\n * U^{(0)} &= \\frac{1}{2\\omega^2} \\left( \\bar{d} \\mathcal U^{(0)} -\n * c \\bar{\\mathcal U}^{(0)} \\right) \\\\\n * &= \\frac{\\hat \\omega^2}{2} \\left( \\bar{d} \\mathcal U^{(0)} -\n * c \\bar{\\mathcal U}^{(0)} \\right)\n * \\f}\n *\n * see Eq. (79) of \\cite Moxon2020gha.\n */\nstruct GaugeUpdateInertialTimeDerivatives {\n  using return_tags = tmpl::list<::Tags::dt<Tags::PartiallyFlatCartesianCoords>,\n                                 Tags::BondiUAtScri>;\n  using argument_tags = tmpl::list<\n      Tags::PartiallyFlatCartesianCoords, Tags::CauchyGaugeC,\n      Tags::PartiallyFlatGaugeOmega, Tags::CauchyGaugeD, Tags::LMax,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::PartiallyFlatAngularCoords>>;\n  static void apply(\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_inertial_du_x,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          evolution_gauge_u_at_scri,\n      const tnsr::i<DataVector, 3>& cartesian_inertial_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_cauchy_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_cauchy_d,\n      size_t l_max, const Spectral::Swsh::SwshInterpolator& interpolator);\n};\n\n/*!\n * \\brief Update the angular coordinates stored in `AngularTag` via\n * trigonometric operations applied to the Cartesian coordinates stored in\n * `CartesianTag`.\n *\n * \\details This function also normalizes the Cartesian coordinates stored in\n * `CartesianTag`, which is the desired behavior for the CCE boundary\n * computation.\n */\ntemplate <typename AngularTag, typename CartesianTag>\nstruct GaugeUpdateAngularFromCartesian {\n  using argument_tags = tmpl::list<>;\n  using return_tags = tmpl::list<AngularTag, CartesianTag>;\n\n  static void apply(\n      const gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_coordinates,\n      const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_coordinates) {\n    // normalize the cartesian coordinates\n    const DataVector one_over_cartesian_r =\n        1.0 / sqrt(square(get<0>(*cartesian_coordinates)) +\n                   square(get<1>(*cartesian_coordinates)) +\n                   square(get<2>(*cartesian_coordinates)));\n\n    get<0>(*cartesian_coordinates) *= one_over_cartesian_r;\n    get<1>(*cartesian_coordinates) *= one_over_cartesian_r;\n    get<2>(*cartesian_coordinates) *= one_over_cartesian_r;\n\n    const auto& x = get<0>(*cartesian_coordinates);\n    const auto& y = get<1>(*cartesian_coordinates);\n    const auto& z = get<2>(*cartesian_coordinates);\n\n    get<0>(*angular_coordinates) = atan2(sqrt(square(x) + square(y)), z);\n    get<1>(*angular_coordinates) = atan2(y, x);\n  }\n};\n\nnamespace detail {\nvoid gauge_update_jacobian_from_coordinates_apply_impl(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n        gauge_factor_spin_2,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        gauge_factor_spin_0,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n        angular_source_coordinates,\n    const tnsr::i<DataVector, 3>& cartesian_source_coordinates, size_t l_max);\n}  // namespace detail\n\n/*!\n * \\brief From the angular coordinates `AngularCoordinateTag` and the Cartesian\n * coordinates `CartesianCoordinateTag`, determine the spin-weighted Jacobian\n * factors `GaugeFactorSpin2` and `GaugeFactorSpin0`.\n *\n * \\details This is most often used in the context of generating the Jacobians\n * in the evolution-gauge coordinates from the Cauchy collocation points as a\n * function of the evolution gauge coordinates. In this concrete case, the\n * `GaugeFactorSpin2` is the gauge factor \\f$\\hat c\\f$ and takes the value\n *\n * \\f{align*}{\n * \\hat c = \\hat q^{\\hat A} \\partial_{\\hat A}(x^A) q_A,\n * \\f}\n *\n * and the `GaugeFactorSpin0` is the gauge factor \\f$\\hat d\\f$ and takes the\n * value\n *\n * \\f{align*}{\n * \\hat d = \\hat{\\bar q}^{\\hat A} \\partial_{\\hat A}(x^A) q_A.\n * \\f}\n *\n * The more generic template construction is employed so that the spin-weighted\n * Jacobians can also be computed between two arbitrary gauges, including the\n * inverse Jacobians associated with moving from the evolution gauge to the\n * Cauchy gauge.\n */\ntemplate <typename GaugeFactorSpin2, typename GaugeFactorSpin0,\n          typename AngularCoordinateTag, typename CartesianCoordinateTag>\nstruct GaugeUpdateJacobianFromCoordinates {\n  using return_tags = tmpl::list<GaugeFactorSpin2, GaugeFactorSpin0>;\n  using argument_tags =\n      tmpl::list<AngularCoordinateTag, CartesianCoordinateTag, Tags::LMax>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          gauge_factor_spin_2,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          gauge_factor_spin_0,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n          angular_source_coordinates,\n      const tnsr::i<DataVector, 3>& cartesian_source_coordinates,\n      const size_t l_max) {\n    detail::gauge_update_jacobian_from_coordinates_apply_impl(\n        gauge_factor_spin_2, gauge_factor_spin_0, angular_source_coordinates,\n        cartesian_source_coordinates, l_max);\n  }\n};\n\n/*!\n * \\brief Update the interpolator stored in\n * `Spectral::Swsh::Tags::SwshInterpolator<AngularCoordinates>`.\n *\n * \\details Note that the `AngularCoordinates` associated with the interpolator\n * should be the source coordinates. For instance, when interpolating a quantity\n * defined on the Cauchy gauge collocation points to the evolution gauge\n * collocation points, the interpolator input should be the Cauchy coordinates\n * points as a function of the evolution gauge coordinates (at the evolution\n * gauge collocation points).\n */\ntemplate <typename AngularCoordinates>\nstruct GaugeUpdateInterpolator {\n  using return_tags =\n      tmpl::list<Spectral::Swsh::Tags::SwshInterpolator<AngularCoordinates>>;\n  using argument_tags = tmpl::list<AngularCoordinates, Tags::LMax>;\n\n  static void apply(\n      const gsl::not_null<Spectral::Swsh::SwshInterpolator*> interpolator,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n          angular_coordinates,\n      const size_t l_max) {\n    // throw away the old interpolator and generate a new one for the current\n    // grid points.\n    *interpolator = Spectral::Swsh::SwshInterpolator(\n        get<0>(angular_coordinates), get<1>(angular_coordinates), l_max);\n  }\n};\n\n/*!\n * \\brief Update the quantity \\f$\\hat \\omega\\f$ and \\f$\\hat \\eth \\hat \\omega\\f$\n * for updated spin-weighted Jacobian quantities \\f$\\hat c\\f$ and \\f$\\hat d\\f$.\n *\n * \\details The conformal factor \\f$\\hat \\omega\\f$ can be determined by the\n * angular determinant from the spin-weighted Jacobian factors as\n *\n * \\f{align*}{\n * \\hat \\omega = \\frac{1}{2} \\sqrt{\\hat d \\hat{\\bar d} - \\hat c \\hat{\\bar c}}.\n * \\f}\n */\ntemplate <typename GaugeC, typename GaugeD, typename GaugeOmega>\nstruct GaugeUpdateOmega {\n  using argument_tags = tmpl::list<GaugeC, GaugeD, Tags::LMax>;\n  using return_tags = tmpl::list<\n      GaugeOmega,\n      Spectral::Swsh::Tags::Derivative<GaugeOmega, Spectral::Swsh::Tags::Eth>>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> omega,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_omega,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d, size_t l_max);\n};\n\n/*!\n * \\brief Initialize to default values (identity transform) all of the angular\n * gauge quantities for the boundary gauge transforms.\n *\n * \\details The updated quantities are the Cauchy angular and Cartesian\n * coordinates, as well as the spin-weighted gauge factors and the conformal\n * factor. All quantities are initialized to the appropriate value for the\n * identity transform of angular coordinates. Using this initialization function\n * ensures that the evolution gauge and the Cauchy gauge angular coordinates\n * agree on the first evaluated time.\n * - `CauchyAngularCoords` are set to the angular collocation values for the\n * spin-weighted spherical harmonic library\n * - `CauchyCartesianCoords` are set to the Cartesian coordinates for the\n * `CauchyAngularCoords` evaluated on a unit sphere.\n * - `GaugeC` is set to 0\n * - `GaugeD` is set to 2\n * - `GaugeOmega` is set to 1\n */\nstruct InitializeGauge {\n  using return_tags =\n      tmpl::list<Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords,\n                 Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                 Tags::PartiallyFlatGaugeOmega>;\n  using argument_tags = tmpl::list<Tags::LMax>;\n\n  static void apply(\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> gauge_c,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> gauge_d,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> omega,\n      size_t l_max);\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ConformalFactor.cpp\n  InitializeJ.cpp\n  InverseCubic.cpp\n  NoIncomingRadiation.cpp\n  ZeroNonSmooth.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ConformalFactor.hpp\n  InitializeJ.hpp\n  InverseCubic.hpp\n  NoIncomingRadiation.hpp\n  RegisterInitializeJWithCharm.hpp\n  ZeroNonSmooth.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/ConformalFactor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/Initialize/ConformalFactor.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <mutex>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::InitializeJ {\nnamespace {\nvoid read_modes_from_input_file(\n    const gsl::not_null<ComplexModalVector*> input_modes,\n    const std::string& input_filename) {\n  Matrix initial_j_modes;\n  {\n    h5::H5File<h5::AccessType::ReadOnly> cce_data_file{input_filename};\n    auto& dat_file = cce_data_file.get<h5::Dat>(\"/InitialJ\");\n    initial_j_modes = dat_file.get_data();\n    cce_data_file.close_current_object();\n  }\n  for (size_t i = 0; i < input_modes->size(); ++i) {\n    (*input_modes)[i] = std::complex<double>(initial_j_modes(0, 2 * i),\n                                             initial_j_modes(0, 2 * i + 1));\n  }\n}\n\n// This is a choice of heuristic for generating the angular\n// coordinates based on assuming the spin-weighted jacobians are\n// approximately \\eth and \\ethbar of a common spin-weight-1 quantity.\n// This holds if the perturbation \\delta x^A q_A happens to be representable\n// as a spin-weight 1 quantity, which is an acceptable choice of\n// perturbation, but is not generic to all possible coordinate\n// transformations\nvoid spin_weight_1_coord_perturbation_heuristic(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> gauge_c_step,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> gauge_d_step,\n    const SpinWeighted<ComplexDataVector, 0>& full_omega,\n    const SpinWeighted<ComplexDataVector, 0>& omega_filtered,\n    const SpinWeighted<ComplexDataVector, 0>& target_omega,\n    const SpinWeighted<ComplexDataVector, 2>& /*gauge_c*/,\n    const SpinWeighted<ComplexDataVector, 0>& gauge_d, const size_t l_max) {\n  SpinWeighted<ComplexDataVector, 1> jacobian_supplement_f{full_omega.size()};\n\n  // The alteration in each of the spin-weighted Jacobian factors determined\n  // by linearizing the system in small \\Delta \\omega\n  gauge_d_step->data() = full_omega.data() *\n                         (target_omega.data() - omega_filtered.data()) /\n                         gauge_d.data();\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::InverseEthbar>>(\n      l_max, 1, make_not_null(&jacobian_supplement_f), *gauge_d_step);\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, gauge_c_step, jacobian_supplement_f);\n}\n\nvoid only_vary_gauge_d_heuristic(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> gauge_c_step,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> gauge_d_step,\n    const SpinWeighted<ComplexDataVector, 0>& full_omega,\n    const SpinWeighted<ComplexDataVector, 0>& omega_filtered,\n    const SpinWeighted<ComplexDataVector, 0>& target_omega,\n    const SpinWeighted<ComplexDataVector, 2>& /*gauge_c*/,\n    const SpinWeighted<ComplexDataVector, 0>& gauge_d,\n    const size_t /*l_max*/) {\n  // The alteration in each of the spin-weighted Jacobian factors determined\n  // by linearizing the system in small \\Delta \\omega\n  gauge_d_step->data() = full_omega.data() *\n                         (target_omega.data() - omega_filtered.data()) /\n                         gauge_d.data();\n  gauge_c_step->data() = 0;\n}\n}  // namespace\n\nConformalFactor::ConformalFactor(CkMigrateMessage* msg)\n    : InitializeJ<false>(msg) {}\n\nConformalFactor::ConformalFactor(\n    const double angular_coordinate_tolerance, const size_t max_iterations,\n    const bool require_convergence, const bool optimize_l_0_mode,\n    const bool use_beta_integral_estimate,\n    const ::Cce::InitializeJ::ConformalFactorIterationHeuristic\n        iteration_heuristic,\n    const bool use_input_modes, std::string input_mode_filename)\n    : angular_coordinate_tolerance_{angular_coordinate_tolerance},\n      max_iterations_{max_iterations},\n      require_convergence_{require_convergence},\n      optimize_l_0_mode_{optimize_l_0_mode},\n      use_beta_integral_estimate_{use_beta_integral_estimate},\n      iteration_heuristic_{iteration_heuristic},\n      use_input_modes_{use_input_modes},\n      input_mode_filename_{std::move(input_mode_filename)} {}\n\nConformalFactor::ConformalFactor(\n    const double angular_coordinate_tolerance, const size_t max_iterations,\n    const bool require_convergence, const bool optimize_l_0_mode,\n    const bool use_beta_integral_estimate,\n    const ::Cce::InitializeJ::ConformalFactorIterationHeuristic\n        iteration_heuristic,\n    const bool use_input_modes,\n    std::vector<std::complex<double>> input_modes)\n    : angular_coordinate_tolerance_{angular_coordinate_tolerance},\n      max_iterations_{max_iterations},\n      require_convergence_{require_convergence},\n      optimize_l_0_mode_{optimize_l_0_mode},\n      use_beta_integral_estimate_{use_beta_integral_estimate},\n      iteration_heuristic_{iteration_heuristic},\n      use_input_modes_{use_input_modes},\n      input_modes_{std::move(input_modes)} {}\n\nstd::unique_ptr<InitializeJ<false>> ConformalFactor::get_clone() const {\n  return std::make_unique<ConformalFactor>(*this);\n}\n\nvoid ConformalFactor::operator()(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, const size_t l_max,\n    const size_t number_of_radial_points,\n    const gsl::not_null<Parallel::NodeLock*> hdf5_lock) const {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Variables<tmpl::list<::Tags::TempSpinWeightedScalar<0, 2>,\n                       ::Tags::TempSpinWeightedScalar<1, 2>,\n                       ::Tags::TempSpinWeightedScalar<2, 2>,\n                       ::Tags::TempSpinWeightedScalar<3, 0>,\n                       ::Tags::TempSpinWeightedScalar<4, 0>,\n                       ::Tags::TempSpinWeightedScalar<5, 0>,\n                       ::Tags::TempSpinWeightedScalar<6, 0>,\n                       ::Tags::TempSpinWeightedScalar<7, 0>,\n                       ::Tags::TempSpinWeightedScalar<8, 0>,\n                       ::Tags::TempSpinWeightedScalar<9, 2>,\n                       ::Tags::TempSpinWeightedScalar<10, 2>,\n                       ::Tags::TempSpinWeightedScalar<11, 2>>>\n      buffers{number_of_angular_points};\n  auto& surface_j_buffer = get<::Tags::TempSpinWeightedScalar<0, 2>>(buffers);\n  auto& surface_dr_j_buffer =\n      get<::Tags::TempSpinWeightedScalar<1, 2>>(buffers);\n  auto& input_j_buffer =\n      get(get<::Tags::TempSpinWeightedScalar<2, 2>>(buffers));\n  auto& gauge_omega = get<::Tags::TempSpinWeightedScalar<4, 0>>(buffers);\n  auto& filtered_gauge_omega =\n      get(get<::Tags::TempSpinWeightedScalar<5, 0>>(buffers));\n  auto& target_omega = get(get<::Tags::TempSpinWeightedScalar<6, 0>>(buffers));\n  auto& interpolated_target_gauge_omega =\n      get(get<::Tags::TempSpinWeightedScalar<7, 0>>(buffers));\n  auto& surface_r_buffer = get<::Tags::TempSpinWeightedScalar<8, 0>>(buffers);\n\n  auto& one_minus_y_coefficient =\n      get(get<::Tags::TempSpinWeightedScalar<9, 2>>(buffers));\n  auto& one_minus_y_cubed_coefficient =\n      get(get<::Tags::TempSpinWeightedScalar<10, 2>>(buffers));\n  auto& one_minus_y_fourth_coefficient =\n      get(get<::Tags::TempSpinWeightedScalar<11, 2>>(buffers));\n\n  Variables<tmpl::list<::Tags::ModalTempSpinWeightedScalar<0, 2>,\n                       ::Tags::ModalTempSpinWeightedScalar<1, 0>>>\n      modal_buffers{Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n  auto& input_j_libsharp_modes =\n      get(get<::Tags::ModalTempSpinWeightedScalar<0, 2>>(modal_buffers));\n  auto& gauge_omega_transform_buffer =\n      get(get<::Tags::ModalTempSpinWeightedScalar<1, 0>>(modal_buffers));\n\n  SpinWeighted<ComplexModalVector, 2> goldberg_modes{square(l_max + 1)};\n  if (use_input_modes_) {\n    if (input_mode_filename_.has_value()) {\n      const std::lock_guard hold_lock(*hdf5_lock);\n      read_modes_from_input_file(make_not_null(&(goldberg_modes.data())),\n                                 input_mode_filename_.value());\n    } else {\n      ASSERT(input_modes_.size() <= goldberg_modes.size(),\n             \"The size of the input modes is too large. Specify at most  \"\n             \"(l_max + 1)^2 modes in the input file.\");\n      std::fill(goldberg_modes.data().begin(), goldberg_modes.data().end(),\n                0.0);\n      std::copy(input_modes_.begin(), input_modes_.end(),\n                goldberg_modes.data().begin());\n    }\n    Spectral::Swsh::goldberg_to_libsharp_modes(\n        make_not_null(&input_j_libsharp_modes), goldberg_modes, l_max);\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max, 1_st, make_not_null(&input_j_buffer), input_j_libsharp_modes);\n    // input modes fix the  j^(1) / r = (j^(1) / (2 R)) (1 - y)\n  }\n\n  // The asymptotic value of beta is beta|_scri = beta|_\\Gamma + \\int_-1^1 dy\n  // \\partial_y \\beta, and this estimates the second term.\n  // The coordinate transform acts to set e^(2 \\hat \\beta) = e^(2 \\beta) /\n  // \\omega. We organize the iteration to try to find the coordinate transform\n  // that fixes \\omega = e^(2 \\beta - 2\\hat \\beta) a simple approximation is to\n  // choose the coordinate transform such that at the boundary, \\hat \\beta = 0,\n  // so we just seek a value set by the original boundary beta Alternatively, we\n  // can try to choose values such that the asymptotic value of beta is zeroed.\n  // The equation of motion for \\beta is the same before and after the\n  // coordinate transformation, but in the two cases, it is easier to compute\n  // the contributions from the integral to \\beta for inverse cubic or to \\hat\n  // \\beta for input file specification.\n\n  // So, in either case we want to fix\n  // e^(2 \\hat \\beta|_scri+) = 1\n  //\n  // in the first case, we write that as\n  // e^(2 \\beta|_\\scri^+) / \\omega = 1\n  // => e^(2(\\int dy \\partial_y \\beta + \\beta|_\\Gamma))|_{x(\\hat x)} = \\omega\n  // so in this case, we should compute the estimated addition to beta as a\n  // one-time addition as an input to the algorithm.\n  //\n  // in the second case we want to write it as\n  // e^(2 \\hat \\beta|_\\Gamma + 2 \\int dy \\partial_y \\hat \\beta) = 1\n  // => e^(2 \\beta|_\\Gamma)|_{x(\\hat x)} *\n  //    e^(2\\int dy \\partial_y \\hat \\beta) = \\omega\n  // so in this case, we can still compute the integral up front to determine\n  // the estimated addition, but it should be multiplied into the target _after_\n  // interpolation\n  //\n  // Either way, it just represents a small alteration to the method by which\n  // the target is computed on each iteration, so can largely be absorbed into\n  // the main algorithm.\n\n  // we use the estimate:\n  // \\int dy \\partial_y \\beta \\approx -1/16 ln(1 + 4.0 * j jbar)\n\n  target_omega.data() = exp(2.0 * get(beta).data());\n  if (not use_input_modes_ and use_beta_integral_estimate_) {\n    // use buffer for the (1-y) coefficient that we'd generate in the original\n    // gauge.\n    get(surface_j_buffer) = 0.25 * (3.0 * get(boundary_j).data() +\n                                    get(r).data() * get(boundary_dr_j).data());\n    target_omega.data() /= pow(1.0 + 4.0 * get(surface_j_buffer).data() *\n                                         conj(get(surface_j_buffer).data()),\n                               0.125);\n  }\n\n  void (*iteration_heuristic_function)(\n      const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*>,\n      const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*>,\n      const SpinWeighted<ComplexDataVector, 0>&,\n      const SpinWeighted<ComplexDataVector, 0>&,\n      const SpinWeighted<ComplexDataVector, 0>&,\n      const SpinWeighted<ComplexDataVector, 2>&,\n      const SpinWeighted<ComplexDataVector, 0>&, size_t) = nullptr;\n  if (iteration_heuristic_ ==\n      ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n          SpinWeight1CoordPerturbation) {\n    iteration_heuristic_function = &spin_weight_1_coord_perturbation_heuristic;\n  } else if (iteration_heuristic_ ==\n             ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n                 OnlyVaryGaugeD) {\n    iteration_heuristic_function = &only_vary_gauge_d_heuristic;\n  } else {  // LCOV_EXCL_LINE\n    // LCOV_EXCL_START\n    ERROR(\"Unknown ConformalFactorIterationHeuristic\");\n    // LCOV_EXCL_STOP\n  }\n\n  auto iteration_function =\n      [&iteration_heuristic_function, &filtered_gauge_omega, &gauge_omega,\n       &target_omega, &interpolated_target_gauge_omega,\n       &gauge_omega_transform_buffer, &l_max, &surface_r_buffer,\n       &input_j_buffer, &r,\n       this](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n                 gauge_c_step,\n             const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n                 gauge_d_step,\n             const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n             const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n             const Spectral::Swsh::SwshInterpolator& iteration_interpolator) {\n        get(gauge_omega).data() =\n            0.5 * sqrt(get(gauge_d).data() * conj(get(gauge_d).data()) -\n                       get(gauge_c).data() * conj(get(gauge_c).data()));\n        iteration_interpolator.interpolate(\n            make_not_null(&interpolated_target_gauge_omega), target_omega);\n        if (use_input_modes_ and use_beta_integral_estimate_) {\n          // when using input modes, the `input_j_buffer` stores the\n          // 1/r part of J in the evolution gauge\n          iteration_interpolator.interpolate(\n              make_not_null(&get(surface_r_buffer)), get(r));\n          get(surface_r_buffer).data() *= get(gauge_omega).data();\n          interpolated_target_gauge_omega.data() /= pow(\n              1.0 + real(input_j_buffer.data() * conj(input_j_buffer.data()) /\n                         (square(get(surface_r_buffer).data()))),\n              0.125);\n        }\n        filtered_gauge_omega = get(gauge_omega);\n        if (not optimize_l_0_mode_) {\n          Spectral::Swsh::filter_swsh_boundary_quantity(\n              make_not_null(&filtered_gauge_omega), l_max, 1_st, l_max,\n              make_not_null(&gauge_omega_transform_buffer));\n          Spectral::Swsh::filter_swsh_boundary_quantity(\n              make_not_null(&interpolated_target_gauge_omega), l_max, 1_st,\n              l_max, make_not_null(&gauge_omega_transform_buffer));\n        }\n        double max_error = max(abs(filtered_gauge_omega.data() -\n                                   interpolated_target_gauge_omega.data()));\n        iteration_heuristic_function(make_not_null(&get(*gauge_c_step)),\n                                     make_not_null(&get(*gauge_d_step)),\n                                     get(gauge_omega), filtered_gauge_omega,\n                                     interpolated_target_gauge_omega,\n                                     get(gauge_c), get(gauge_d), l_max);\n        return max_error;\n      };\n\n  auto finalize_function =\n      [&gauge_omega, &l_max, &surface_dr_j_buffer, &boundary_dr_j, &boundary_j,\n       &surface_j_buffer, &surface_r_buffer,\n       &r](const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n           const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n           const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n           /*angular_cauchy_coordinates*/,\n           const Spectral::Swsh::SwshInterpolator& interpolator) {\n        get(gauge_omega).data() =\n            0.5 * sqrt(get(gauge_d).data() * conj(get(gauge_d).data()) -\n                       get(gauge_c).data() * conj(get(gauge_c).data()));\n        GaugeAdjustedBoundaryValue<Tags::Dr<Tags::BondiJ>>::apply(\n            make_not_null(&surface_dr_j_buffer), boundary_dr_j, boundary_j,\n            gauge_c, gauge_d, gauge_omega, interpolator, l_max);\n        GaugeAdjustedBoundaryValue<Tags::BondiJ>::apply(\n            make_not_null(&surface_j_buffer), boundary_j, gauge_c, gauge_d,\n            gauge_omega, interpolator);\n        GaugeAdjustedBoundaryValue<Tags::BondiR>::apply(\n            make_not_null(&surface_r_buffer), r, gauge_omega, interpolator);\n      };\n\n  detail::iteratively_adapt_angular_coordinates(\n      cartesian_cauchy_coordinates, angular_cauchy_coordinates, l_max,\n      angular_coordinate_tolerance_, max_iterations_, 1.0e-2,\n      iteration_function, require_convergence_, finalize_function);\n\n  const DataVector one_minus_y_collocation =\n      1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n                number_of_radial_points);\n  if (not use_input_modes_) {\n    one_minus_y_coefficient =\n        0.25 * (3.0 * get(surface_j_buffer) +\n                get(surface_r_buffer) * get(surface_dr_j_buffer));\n    one_minus_y_cubed_coefficient =\n        -0.0625 * (get(surface_j_buffer) +\n                   get(surface_r_buffer) * get(surface_dr_j_buffer));\n    for (size_t i = 0; i < number_of_radial_points; i++) {\n      ComplexDataVector angular_view_j{\n          get(*j).data().data() + get(boundary_j).size() * i,\n          get(boundary_j).size()};\n      angular_view_j =\n          one_minus_y_collocation[i] * one_minus_y_coefficient.data() +\n          pow<3>(one_minus_y_collocation[i]) *\n              one_minus_y_cubed_coefficient.data();\n    }\n  } else {\n    // chosen so that:\n    // - asymptotic 1/r part matches input modes\n    // - matches j and dr_j on the worldtube\n    one_minus_y_coefficient = 0.5 * input_j_buffer / get(surface_r_buffer);\n    one_minus_y_cubed_coefficient =\n        -0.75 * one_minus_y_coefficient + 0.5 * get(surface_j_buffer) +\n        0.125 * get(surface_r_buffer) * get(surface_dr_j_buffer);\n    one_minus_y_fourth_coefficient =\n        0.25 * one_minus_y_coefficient - 0.1875 * get(surface_j_buffer) -\n        0.0625 * get(surface_r_buffer) * get(surface_dr_j_buffer);\n    for (size_t i = 0; i < number_of_radial_points; i++) {\n      ComplexDataVector angular_view_j{\n          get(*j).data().data() + get(boundary_j).size() * i,\n          get(boundary_j).size()};\n      angular_view_j =\n          one_minus_y_collocation[i] * one_minus_y_coefficient.data() +\n          pow<3>(one_minus_y_collocation[i]) *\n              one_minus_y_cubed_coefficient.data() +\n          pow<4>(one_minus_y_collocation[i]) *\n              one_minus_y_fourth_coefficient.data();\n    }\n  }\n}\n\nvoid ConformalFactor::pup(PUP::er& p) {\n  p | angular_coordinate_tolerance_;\n  p | max_iterations_;\n  p | require_convergence_;\n  p | optimize_l_0_mode_;\n  p | use_beta_integral_estimate_;\n  p | iteration_heuristic_;\n  p | use_input_modes_;\n  p | input_modes_;\n  p | input_mode_filename_;\n}\n\nPUP::able::PUP_ID ConformalFactor::my_PUP_ID = 0;\nstd::ostream& operator<<(\n    std::ostream& os,\n    const Cce::InitializeJ::ConformalFactorIterationHeuristic& heuristic_type) {\n  switch (heuristic_type) {\n    case Cce::InitializeJ::ConformalFactorIterationHeuristic::\n        SpinWeight1CoordPerturbation:\n      return os << \"SpinWeight1CoordPerturbation\";\n    case Cce::InitializeJ::ConformalFactorIterationHeuristic::OnlyVaryGaugeD:\n      return os << \"OnlyVaryGaugeD\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Unknown ConformalFactorIterationHeuristic\");\n      // LCOV_EXCL_STOP\n  }\n}\n}  // namespace Cce::InitializeJ\n\ntemplate <>\nCce::InitializeJ::ConformalFactorIterationHeuristic\nOptions::create_from_yaml<Cce::InitializeJ::ConformalFactorIterationHeuristic>::\n    create<void>(const Options::Option& options) {\n  const auto heuristic_read = options.parse_as<std::string>();\n  if (\"SpinWeight1CoordPerturbation\" == heuristic_read) {\n    return Cce::InitializeJ::ConformalFactorIterationHeuristic::\n        SpinWeight1CoordPerturbation;\n  } else if (\"OnlyVaryGaugeD\" == heuristic_read) {\n    return Cce::InitializeJ::ConformalFactorIterationHeuristic::OnlyVaryGaugeD;\n  }\n  // LCOV_EXCL_START\n  PARSE_ERROR(\n      options.context(),\n      \"Failed to convert \\\"\"\n          << heuristic_read\n          << \"\\\" to Cce::InitializeJ::ConformalFactorIterationHeuristic. \"\n             \"Must be one of SpinWeight1CoordPerturbation, OnlyVaryGaugeD.\");\n  // LCOV_EXCL_STOP\n}\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/ConformalFactor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\nnamespace InitializeJ {\n\n/// Possible iteration heuristics to use for optimizing the value of the\n/// conformal factor \\f$\\omega\\f$ to fix the initial data.\nenum class ConformalFactorIterationHeuristic {\n  /// Assumes that the spin-weighted Jacobian perturbations obey\n  /// \\f$c = \\hat \\eth f\\f$,\\f$d = \\hat{\\bar\\eth} f\\f$, for some spin-weight-1\n  /// value\\f$f\\f$.\n  SpinWeight1CoordPerturbation,\n  /// Varies only the \\f$d\\f$ spin-weighted Jacobian when constructing the\n  /// itertion heuristic, leaving \\f$c\\f$ fixed\n  OnlyVaryGaugeD\n};\n\n/*!\n * \\brief Generate initial data that has a conformal factor \\f$\\omega\\f$ chosen\n * to compensate for the boundary value of \\f$\\beta\\f$ so that the initial time\n * coordinate is approximately inertial at \\f$I^+\\f$.\n *\n * \\details The core calculation for this initial data choice is the iterative\n * optimization of the angular conformal factor \\f$\\omega\\f$ such that it\n * cancels some portion of the value of \\f$e^{2\\beta}\\f$ that contributes to the\n * definition of the asymptotically inertial time.\n * The initial data generation process proceeds slightly differently depending\n * on the set of input options that are used:\n * - If `UseBetaIntegralEstimate` is false, the conformal factor will be\n * optimized to minimize the transformed value of \\f$\\beta\\f$ on the worldtube\n * boundary.\n * - If `UseBetaIntegralEstimate` is true, the conformal factor will be\n * optimized to minimize an estimate of the asymptotic value of \\f$\\beta\\f$ in\n * the evolved coordinates.\n * - `OptimizeL0Mode` indicates whether the \\f$l=0\\f$ mode of the conformal\n * factor shoudl be included in the optimization. This option is useful because\n * the optimization can usually find a better solution when the \\f$l=0\\f$ mode\n * is ignored, and the \\f$l=0\\f$ should not contribute significantly to the\n * resulting waveform.\n *\n * - If `UseInputModes` is false, the \\f$J\\f$ value on the initial hypersurface\n * will be set by an \\f$A/r + B/r^3\\f$ ansatz, chosen to match the worldtube\n * boundary value of \\f$J\\f$ and \\f$\\partial_r J\\f$ in the new coordinates.\n * In this case, the alternative arguments `InputModes` or `InputModesFromFile`\n * are ignored.\n * - If `UseInputModes` is true, the \\f$1/r\\f$ part of \\f$J\\f$ will be set to\n * spin-weighted spherical harmonic modes specified by either an input h5 file\n * (in the case of using the input option `InputModesFromFile`) or from a list\n * of complex values specified in the input file (in the case of using the input\n * option `InputModes`). Then, the \\f$1/r^3\\f$ and \\f$1/r^4\\f$ parts of \\f$J\\f$\n * are chosen to match the boundary value of \\f$J\\f$ and \\f$\\partial_r J\\f$ on\n * the worldtube boundary in the new coordinates.\n */\nstruct ConformalFactor : InitializeJ<false> {\n  struct AngularCoordinateTolerance {\n    using type = double;\n    static std::string name() { return \"AngularCoordTolerance\"; }\n    static constexpr Options::String help = {\n        \"Tolerance of initial angular coordinates for CCE\"};\n    static type lower_bound() { return 1.0e-14; }\n    static type upper_bound() { return 1.0e-3; }\n  };\n  struct MaxIterations {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Number of linearized inversion iterations.\"};\n    static type lower_bound() { return 10; }\n    static type upper_bound() { return 1000; }\n    static type suggested_value() { return 300; }\n  };\n  struct RequireConvergence {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If true, initialization will error if it hits MaxIterations\"};\n    static type suggested_value() { return true; }\n  };\n  struct OptimizeL0Mode {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If true, the average value of the conformal factor will be included \"\n        \"during optimization; otherwise it will be omitted (filtered).\"};\n    static type suggested_value() { return false; }\n  };\n  struct UseBetaIntegralEstimate {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If true, the iterative algorithm will calculate an estimate of the \"\n        \"asymptotic beta value using the 1/r part of the initial J.\"};\n    static type suggested_value() { return true; }\n  };\n  struct ConformalFactorIterationHeuristic {\n    using type = ::Cce::InitializeJ::ConformalFactorIterationHeuristic;\n    static constexpr Options::String help = {\n        \"The heuristic method used to set the spin-weighted Jacobian factors \"\n        \"when iterating to minimize the asymptotic conformal factor.\"};\n    static type suggested_value() {\n      return ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n          SpinWeight1CoordPerturbation;\n    }\n  };\n  struct UseInputModes {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If true, the 1/r part of J will be set using modes read from the \"\n        \"input file, or from a specified h5 file. If false, the inverse cubic \"\n        \"scheme will determine the 1/r part of J.\"};\n  };\n  struct InputModesFromFile {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"A filename from which to retrieve a set of modes (from InitialJ.dat) \"\n        \"to use to determine the 1/r part of J on the initial hypersurface. \"\n        \"The modes are parsed in l-ascending, m-ascending, m-varies-fastest, \"\n        \"real then imaginary part order.\"};\n  };\n  struct InputModes {\n    using type = std::vector<std::complex<double>>;\n    static constexpr Options::String help = {\n        \"An explicit list of modes to use to set the 1/r part of J on the \"\n        \"initial hypersurface. They are parsed in l-ascending, m-ascending, \"\n        \"m-varies-fastest order.\"};\n  };\n\n  using options =\n      tmpl::list<AngularCoordinateTolerance, MaxIterations, RequireConvergence,\n                 OptimizeL0Mode, UseBetaIntegralEstimate,\n                 ConformalFactorIterationHeuristic, UseInputModes,\n                 Options::Alternatives<tmpl::list<InputModesFromFile>,\n                                       tmpl::list<InputModes>>>;\n  static constexpr Options::String help = {\n      \"Generate CCE initial data based on choosing an angular conformal factor \"\n      \"based on the value of the CCE scalar beta in an attempt to make the \"\n      \"time variable approximately asymptotically inertial\"};\n\n  WRAPPED_PUPable_decl_template(ConformalFactor);  // NOLINT\n  explicit ConformalFactor(CkMigrateMessage* msg);\n\n  ConformalFactor() = default;\n  ConformalFactor(\n      double angular_coordinate_tolerance, size_t max_iterations,\n      bool require_convergence, bool optimize_l_0_mode,\n      bool use_beta_integral_estimate,\n      ::Cce::InitializeJ::ConformalFactorIterationHeuristic iteration_heuristic,\n      bool use_input_modes, std::string input_mode_filename);\n\n  ConformalFactor(\n      double angular_coordinate_tolerance, size_t max_iterations,\n      bool require_convergence, bool optimize_l_0_mode,\n      bool use_beta_integral_estimate,\n      ::Cce::InitializeJ::ConformalFactorIterationHeuristic iteration_heuristic,\n      bool use_input_modes, std::vector<std::complex<double>> input_modes);\n\n  std::unique_ptr<InitializeJ> get_clone() const override;\n\n  void operator()(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, size_t l_max,\n      size_t number_of_radial_points,\n      gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  void pup(PUP::er& p) override;\n\n private:\n  double angular_coordinate_tolerance_ = 1.0e-11;\n  size_t max_iterations_ = 300;\n  bool require_convergence_ = true;\n  bool optimize_l_0_mode_ = false;\n  bool use_beta_integral_estimate_ = true;\n  ::Cce::InitializeJ::ConformalFactorIterationHeuristic iteration_heuristic_ =\n      ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n          SpinWeight1CoordPerturbation;\n  bool use_input_modes_ = false;\n  std::vector<std::complex<double>> input_modes_;\n  std::optional<std::string> input_mode_filename_;\n};\n\nstd::ostream& operator<<(\n    std::ostream& os,\n    const Cce::InitializeJ::ConformalFactorIterationHeuristic& heuristic_type);\n\n}  // namespace InitializeJ\n}  // namespace Cce\n\ntemplate <>\nstruct Options::create_from_yaml<\n    Cce::InitializeJ::ConformalFactorIterationHeuristic> {\n  template <typename Metavariables>\n  static Cce::InitializeJ::ConformalFactorIterationHeuristic create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nCce::InitializeJ::ConformalFactorIterationHeuristic\nOptions::create_from_yaml<Cce::InitializeJ::ConformalFactorIterationHeuristic>::\n    create<void>(const Options::Option& options);\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/InitializeJ.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::InitializeJ {\n\nvoid GaugeAdjustInitialJ::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> volume_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_omega,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n    /*cauchy_angular_coordinates*/,\n    const Spectral::Swsh::SwshInterpolator& interpolator, const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_radial_points =\n      get(*volume_j).size() /\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Scalar<SpinWeighted<ComplexDataVector, 2>> evolution_coords_j_buffer{\n      number_of_angular_points};\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    Scalar<SpinWeighted<ComplexDataVector, 2>> angular_view_j;\n    get(angular_view_j)\n        .set_data_ref(\n            get(*volume_j).data().data() + i * number_of_angular_points,\n            number_of_angular_points);\n    get(evolution_coords_j_buffer) = get(angular_view_j);\n    GaugeAdjustedBoundaryValue<Tags::BondiJ>::apply(\n        make_not_null(&angular_view_j), evolution_coords_j_buffer, gauge_c,\n        gauge_d, gauge_omega, interpolator);\n  }\n}\n}  // namespace Cce::InitializeJ\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/InitializeJ.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\n/// Contains utilities and \\ref DataBoxGroup mutators for generating data for\n/// \\f$J\\f$ on the initial CCE hypersurface.\nnamespace InitializeJ {\n\nnamespace detail {\n// used to provide a default for the finalize functor in\n// `iteratively_adapt_angular_coordinates`\nstruct NoOpFinalize {\n  void operator()(\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& /*gauge_c*/,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& /*gauge_d*/,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n      /*angular_cauchy_coordinates*/,\n      const Spectral::Swsh::SwshInterpolator& /*interpolator*/) const {\n  }\n};\n\n// perform an iterative solve for the set of angular coordinates. The iteration\n// callable `iteration_function` must have function signature:\n//\n// double iteration_function(\n//     const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n//         gauge_c_step,\n//     const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n//         gauge_d_step,\n//     const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n//     const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n//     const Spectral::Swsh::SwshInterpolator& iteration_interpolator);\n//\n// but need not be a function pointer -- a callable class or lambda will also\n// suffice.\n// For each step specified by the iteration function, the coordinates are\n// updated via \\hat \\eth \\delta x^i = \\delta c \\eth x^i|_{x^i=\\hat x^i}\n//                        + \\delta \\bar d (\\eth x^i)|_{x^i=\\hat x^i}\n// This coordinate update is exact, and comes from expanding the chain rule to\n// determine Jacobian factors. However, the result is not guaranteed to\n// produce the desired Jacobian c and d, because \\delta c and \\delta d are\n// not necessarily consistent with the underlying coordinates.\n// We then update the x^i by inverting \\hat \\eth, which is also exact, but\n// assumes a no l=0 contribution to the transformation.\n// Depending on the choice of approximations used to specify\n// `iteration_function`, though, the method can be slow to converge.\n\n// However, the iterations are typically fast, and the computation is for\n// initial data that needs to be computed only once during a simulation, so it\n// is not currently an optimization priority. If this function becomes a\n// bottleneck, the numerical procedure of the iterative method or the choice of\n// approximation used for `iteration_function` should be revisited.\ntemplate <typename IterationFunctor, typename FinalizeFunctor = NoOpFinalize>\ndouble iteratively_adapt_angular_coordinates(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const size_t l_max, const double tolerance, const size_t max_steps,\n    const double error_threshold, const IterationFunctor& iteration_function,\n    const bool require_convergence,\n    const FinalizeFunctor finalize_function = NoOpFinalize{}) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Spectral::Swsh::create_angular_and_cartesian_coordinates(\n      cartesian_cauchy_coordinates, angular_cauchy_coordinates, l_max);\n\n  Variables<tmpl::list<\n      // cartesian coordinates\n      ::Tags::TempSpinWeightedScalar<0, 0>,\n      ::Tags::TempSpinWeightedScalar<1, 0>,\n      ::Tags::TempSpinWeightedScalar<2, 0>,\n      // eth of cartesian coordinates\n      ::Tags::TempSpinWeightedScalar<3, 1>,\n      ::Tags::TempSpinWeightedScalar<4, 1>,\n      ::Tags::TempSpinWeightedScalar<5, 1>,\n      // eth of gauge-transformed cartesian coordinates\n      ::Tags::TempSpinWeightedScalar<6, 1>,\n      ::Tags::TempSpinWeightedScalar<7, 1>,\n      ::Tags::TempSpinWeightedScalar<8, 1>,\n      // gauge Jacobians\n      ::Tags::TempSpinWeightedScalar<9, 2>,\n      ::Tags::TempSpinWeightedScalar<10, 0>,\n      // gauge Jacobians on next iteration\n      ::Tags::TempSpinWeightedScalar<11, 2>,\n      ::Tags::TempSpinWeightedScalar<12, 0>,\n      // cartesian coordinates steps\n      ::Tags::TempSpinWeightedScalar<13, 0>,\n      ::Tags::TempSpinWeightedScalar<14, 0>,\n      ::Tags::TempSpinWeightedScalar<15, 0>>>\n      computation_buffers{number_of_angular_points};\n\n  auto& x = get(get<::Tags::TempSpinWeightedScalar<0, 0>>(computation_buffers));\n  auto& y = get(get<::Tags::TempSpinWeightedScalar<1, 0>>(computation_buffers));\n  auto& z = get(get<::Tags::TempSpinWeightedScalar<2, 0>>(computation_buffers));\n\n  x.data() =\n      std::complex<double>(1.0, 0.0) * get<0>(*cartesian_cauchy_coordinates);\n  y.data() =\n      std::complex<double>(1.0, 0.0) * get<1>(*cartesian_cauchy_coordinates);\n  z.data() =\n      std::complex<double>(1.0, 0.0) * get<2>(*cartesian_cauchy_coordinates);\n\n  auto& eth_x =\n      get(get<::Tags::TempSpinWeightedScalar<3, 1>>(computation_buffers));\n  auto& eth_y =\n      get(get<::Tags::TempSpinWeightedScalar<4, 1>>(computation_buffers));\n  auto& eth_z =\n      get(get<::Tags::TempSpinWeightedScalar<5, 1>>(computation_buffers));\n\n  Spectral::Swsh::angular_derivatives<\n      tmpl::list<Spectral::Swsh::Tags::Eth, Spectral::Swsh::Tags::Eth,\n                 Spectral::Swsh::Tags::Eth>>(l_max, 1, make_not_null(&eth_x),\n                                             make_not_null(&eth_y),\n                                             make_not_null(&eth_z), x, y, z);\n\n  auto& evolution_gauge_eth_x_step =\n      get(get<::Tags::TempSpinWeightedScalar<6, 1>>(computation_buffers));\n  auto& evolution_gauge_eth_y_step =\n      get(get<::Tags::TempSpinWeightedScalar<7, 1>>(computation_buffers));\n  auto& evolution_gauge_eth_z_step =\n      get(get<::Tags::TempSpinWeightedScalar<8, 1>>(computation_buffers));\n\n  auto& gauge_c =\n      get<::Tags::TempSpinWeightedScalar<9, 2>>(computation_buffers);\n  auto& gauge_d =\n      get<::Tags::TempSpinWeightedScalar<10, 0>>(computation_buffers);\n\n  auto& gauge_c_step =\n      get<::Tags::TempSpinWeightedScalar<11, 2>>(computation_buffers);\n  auto& gauge_d_step =\n      get<::Tags::TempSpinWeightedScalar<12, 0>>(computation_buffers);\n\n  auto& x_step =\n      get(get<::Tags::TempSpinWeightedScalar<13, 0>>(computation_buffers));\n  auto& y_step =\n      get(get<::Tags::TempSpinWeightedScalar<14, 0>>(computation_buffers));\n  auto& z_step =\n      get(get<::Tags::TempSpinWeightedScalar<15, 0>>(computation_buffers));\n\n  double max_error = 1.0;\n  size_t number_of_steps = 0;\n  Spectral::Swsh::SwshInterpolator iteration_interpolator;\n  while (true) {\n    GaugeUpdateAngularFromCartesian<\n        Tags::CauchyAngularCoords,\n        Tags::CauchyCartesianCoords>::apply(angular_cauchy_coordinates,\n                                            cartesian_cauchy_coordinates);\n\n    iteration_interpolator = Spectral::Swsh::SwshInterpolator{\n        get<0>(*angular_cauchy_coordinates),\n        get<1>(*angular_cauchy_coordinates), l_max};\n\n    GaugeUpdateJacobianFromCoordinates<\n        Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n        Tags::CauchyAngularCoords,\n        Tags::CauchyCartesianCoords>::apply(make_not_null(&gauge_c),\n                                            make_not_null(&gauge_d),\n                                            *angular_cauchy_coordinates,\n                                            *cartesian_cauchy_coordinates,\n                                            l_max);\n\n    max_error = iteration_function(make_not_null(&gauge_c_step),\n                                   make_not_null(&gauge_d_step), gauge_c,\n                                   gauge_d, iteration_interpolator);\n\n    if (max_error > error_threshold) {\n      ERROR(\n          \"Iterative solve for surface coordinates of initial data failed. The \"\n          \"strain is too large to be fully eliminated by a well-behaved \"\n          \"alteration of the spherical mesh. This could be an indication that \"\n          \"there is an issue with the worldtube data. If you are confident \"\n          \"the worldtube data is correct, then please use an alternative \"\n          \"initial data generator such as `InverseCubic`. If that fails, \"\n          \"please double check that your spherical harmonic modes are decaying \"\n          \"correctly with increasing (l,m).\\nError: \"\n          << max_error << \"\\nError threshold: \" << error_threshold);\n    }\n    ++number_of_steps;\n    if (max_error < tolerance or number_of_steps > max_steps) {\n      break;\n    }\n    // using the evolution_gauge_.._step as temporary buffers for the\n    // interpolation results\n    iteration_interpolator.interpolate(\n        make_not_null(&evolution_gauge_eth_x_step), eth_x);\n    iteration_interpolator.interpolate(\n        make_not_null(&evolution_gauge_eth_y_step), eth_y);\n    iteration_interpolator.interpolate(\n        make_not_null(&evolution_gauge_eth_z_step), eth_z);\n\n    evolution_gauge_eth_x_step =\n        0.5 * ((get(gauge_c_step)) * conj(evolution_gauge_eth_x_step) +\n               conj((get(gauge_d_step))) * evolution_gauge_eth_x_step);\n    evolution_gauge_eth_y_step =\n        0.5 * ((get(gauge_c_step)) * conj(evolution_gauge_eth_y_step) +\n               conj((get(gauge_d_step))) * evolution_gauge_eth_y_step);\n    evolution_gauge_eth_z_step =\n        0.5 * ((get(gauge_c_step)) * conj(evolution_gauge_eth_z_step) +\n               conj((get(gauge_d_step))) * evolution_gauge_eth_z_step);\n\n    Spectral::Swsh::angular_derivatives<tmpl::list<\n        Spectral::Swsh::Tags::InverseEth, Spectral::Swsh::Tags::InverseEth,\n        Spectral::Swsh::Tags::InverseEth>>(\n        l_max, 1, make_not_null(&x_step), make_not_null(&y_step),\n        make_not_null(&z_step), evolution_gauge_eth_x_step,\n        evolution_gauge_eth_y_step, evolution_gauge_eth_z_step);\n\n    get<0>(*cartesian_cauchy_coordinates) += real(x_step.data());\n    get<1>(*cartesian_cauchy_coordinates) += real(y_step.data());\n    get<2>(*cartesian_cauchy_coordinates) += real(z_step.data());\n  }\n\n  finalize_function(gauge_c, gauge_d, *angular_cauchy_coordinates,\n                    iteration_interpolator);\n\n  if (tolerance < max_error) {\n    if (require_convergence) {\n      ERROR(\n          \"Initial data iterative angular solve did not reach \"\n          \"target tolerance \"\n          << tolerance << \".\\n\"\n          << \"Exited after \" << max_steps\n          << \" iterations, achieving final\\n\"\n             \"maximum over collocation points deviation of J from target of \"\n          << max_error);\n    } else {\n      Parallel::printf(\n          \"Warning: iterative angular solve did not reach \"\n          \"target tolerance %e.\\n\"\n          \"Exited after %zu iterations, achieving final maximum over \"\n          \"collocation points for deviation from target of %e\\n\"\n          \"Proceeding with evolution using the partial result from partial \"\n          \"angular solve.\\n\",\n          tolerance, max_steps, max_error);\n    }\n  }\n  return max_error;\n}\n\ndouble adjust_angular_coordinates_for_j(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> volume_j,\n    gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const SpinWeighted<ComplexDataVector, 2>& surface_j, size_t l_max,\n    double tolerance, size_t max_steps, bool adjust_volume_gauge);\n}  // namespace detail\n\n/*!\n * \\brief Apply a radius-independent angular gauge transformation to a volume\n * \\f$J\\f$, for use with initial data generation.\n *\n * \\details Performs the gauge transformation to \\f$\\hat J\\f$,\n *\n * \\f{align*}{\n * \\hat J = \\frac{1}{4 \\hat{\\omega}^2} \\left( \\bar{\\hat d}^2  J(\\hat x^{\\hat A})\n *  + \\hat c^2 \\bar J(\\hat x^{\\hat A})\n *  + 2 \\hat c \\bar{\\hat d} K(\\hat x^{\\hat A}) \\right).\n * \\f}\n *\n * Where \\f$\\hat c\\f$ and \\f$\\hat d\\f$ are the spin-weighted angular Jacobian\n * factors computed by `GaugeUpdateJacobianFromCoords`, and \\f$\\hat \\omega\\f$ is\n * the conformal factor associated with the angular coordinate transformation.\n * Note that the right-hand sides with explicit \\f$\\hat x^{\\hat A}\\f$ dependence\n * must be interpolated and that \\f$K = \\sqrt{1 + J \\bar J}\\f$.\n */\nstruct GaugeAdjustInitialJ {\n  using boundary_tags =\n      tmpl::list<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                 Tags::PartiallyFlatGaugeOmega, Tags::CauchyAngularCoords,\n                 Spectral::Swsh::Tags::LMax>;\n  using return_tags = tmpl::list<Tags::BondiJ>;\n  using argument_tags = tmpl::append<boundary_tags>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> volume_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_omega,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n          cauchy_angular_coordinates,\n      const Spectral::Swsh::SwshInterpolator& interpolator, size_t l_max);\n};\n\n/// \\cond\nstruct NoIncomingRadiation;\nstruct ZeroNonSmooth;\ntemplate <bool evolve_ccm>\nstruct InverseCubic;\ntemplate <bool evolve_ccm>\nstruct InitializeJ;\nstruct ConformalFactor;\n/// \\endcond\n\n/*!\n * \\brief Abstract base class for an initial hypersurface data generator for\n * Cce, when the partially flat Bondi-like coordinates are evolved.\n *\n * \\details The algorithm is same as `InitializeJ<false>`, but with an\n * additional initialization for the partially flat Bondi-like coordinates. The\n * functions that are required to be overriden in the derived classes are:\n * - `InitializeJ::get_clone()`: should return a\n * `std::unique_ptr<InitializeJ<true>>` with cloned state.\n * - `InitializeJ::operator() const`: should take as arguments, first a\n * set of `gsl::not_null` pointers represented by `mutate_tags`, followed by a\n * set of `const` references to quantities represented by `argument_tags`. \\note\n * The `InitializeJ::operator()` should be const, and therefore not alter\n * the internal state of the generator. This is compatible with all known\n * use-cases and permits the `InitializeJ` generator to be placed in the\n * `GlobalCache`.\n */\ntemplate <>\nstruct InitializeJ<true> : public PUP::able {\n  using boundary_tags = tmpl::list<Tags::BoundaryValue<Tags::BondiJ>,\n                                   Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>,\n                                   Tags::BoundaryValue<Tags::BondiR>,\n                                   Tags::BoundaryValue<Tags::BondiBeta>>;\n\n  using mutate_tags =\n      tmpl::list<Tags::BondiJ, Tags::CauchyCartesianCoords,\n                 Tags::CauchyAngularCoords, Tags::PartiallyFlatCartesianCoords,\n                 Tags::PartiallyFlatAngularCoords>;\n  using argument_tags =\n      tmpl::push_back<boundary_tags, Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  // The evolution of inertial coordinates are allowed only when InverseCubic is\n  // used\n  using creatable_classes = tmpl::list<InverseCubic<true>>;\n\n  InitializeJ() = default;\n  explicit InitializeJ(CkMigrateMessage* /*msg*/) {}\n\n  WRAPPED_PUPable_abstract(InitializeJ);  // NOLINT\n\n  virtual std::unique_ptr<InitializeJ<true>> get_clone() const = 0;\n\n  virtual void operator()(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_inertial_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_inertial_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, size_t l_max,\n      size_t number_of_radial_points,\n      gsl::not_null<Parallel::NodeLock*> hdf5_lock) const = 0;\n};\n\n/*!\n * \\brief Abstract base class for an initial hypersurface data generator for\n * Cce, when the partially flat Bondi-like coordinates are not evolved.\n *\n * \\details The functions that are required to be overriden in the derived\n * classes are:\n * - `InitializeJ::get_clone()`: should return a\n * `std::unique_ptr<InitializeJ<false>>` with cloned state.\n * - `InitializeJ::operator() const`: should take as arguments, first a\n * set of `gsl::not_null` pointers represented by `mutate_tags`, followed by a\n * set of `const` references to quantities represented by `argument_tags`. \\note\n * The `InitializeJ::operator()` should be const, and therefore not alter\n * the internal state of the generator. This is compatible with all known\n * use-cases and permits the `InitializeJ` generator to be placed in the\n * `GlobalCache`.\n */\ntemplate <>\nstruct InitializeJ<false> : public PUP::able {\n  using boundary_tags = tmpl::list<Tags::BoundaryValue<Tags::BondiJ>,\n                                   Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>,\n                                   Tags::BoundaryValue<Tags::BondiR>,\n                                   Tags::BoundaryValue<Tags::BondiBeta>>;\n\n  using mutate_tags = tmpl::list<Tags::BondiJ, Tags::CauchyCartesianCoords,\n                                 Tags::CauchyAngularCoords>;\n  using argument_tags =\n      tmpl::push_back<boundary_tags, Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  using creatable_classes = tmpl::list<ConformalFactor, InverseCubic<false>,\n                                       NoIncomingRadiation, ZeroNonSmooth>;\n\n  InitializeJ() = default;\n  explicit InitializeJ(CkMigrateMessage* /*msg*/) {}\n\n  WRAPPED_PUPable_abstract(InitializeJ);  // NOLINT\n\n  virtual std::unique_ptr<InitializeJ<false>> get_clone() const = 0;\n\n  virtual void operator()(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, size_t l_max,\n      size_t number_of_radial_points,\n      gsl::not_null<Parallel::NodeLock*> hdf5_lock) const = 0;\n};\n}  // namespace InitializeJ\n}  // namespace Cce\n\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/NoIncomingRadiation.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ZeroNonSmooth.hpp\"\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/InverseCubic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::InitializeJ {\n\nstd::unique_ptr<InitializeJ<true>> InverseCubic<true>::get_clone() const {\n  return std::make_unique<InverseCubic>();\n}\nstd::unique_ptr<InitializeJ<false>> InverseCubic<false>::get_clone() const {\n  return std::make_unique<InverseCubic>();\n}\n\nvoid InverseCubic<true>::operator()(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_inertial_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_inertial_coordinates,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& /*beta*/,\n    const size_t l_max, const size_t number_of_radial_points,\n    const gsl::not_null<Parallel::NodeLock*> /*hdf5_lock*/) const {\n  const DataVector one_minus_y_collocation =\n      1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n                number_of_radial_points);\n  for (size_t i = 0; i < number_of_radial_points; i++) {\n    ComplexDataVector angular_view_j{\n        get(*j).data().data() + get(boundary_j).size() * i,\n        get(boundary_j).size()};\n    // auto is acceptable here as these two values are only used once in the\n    // below computation. `auto` causes an expression template to be\n    // generated, rather than allocating.\n    const auto one_minus_y_coefficient =\n        0.25 * (3.0 * get(boundary_j).data() +\n                get(r).data() * get(boundary_dr_j).data());\n    const auto one_minus_y_cubed_coefficient =\n        -0.0625 *\n        (get(boundary_j).data() + get(r).data() * get(boundary_dr_j).data());\n    angular_view_j =\n        one_minus_y_collocation[i] * one_minus_y_coefficient +\n        pow<3>(one_minus_y_collocation[i]) * one_minus_y_cubed_coefficient;\n  }\n  Spectral::Swsh::create_angular_and_cartesian_coordinates(\n      cartesian_cauchy_coordinates, angular_cauchy_coordinates, l_max);\n  // Same as the Cauchy coordinates\n  Spectral::Swsh::create_angular_and_cartesian_coordinates(\n      cartesian_inertial_coordinates, angular_inertial_coordinates, l_max);\n}\n\nvoid InverseCubic<false>::operator()(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& /*beta*/,\n    const size_t l_max, const size_t number_of_radial_points,\n    const gsl::not_null<Parallel::NodeLock*> /*hdf5_lock*/) const {\n  const DataVector one_minus_y_collocation =\n      1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n                number_of_radial_points);\n  for (size_t i = 0; i < number_of_radial_points; i++) {\n    ComplexDataVector angular_view_j{\n        get(*j).data().data() + get(boundary_j).size() * i,\n        get(boundary_j).size()};\n    // auto is acceptable here as these two values are only used once in the\n    // below computation. `auto` causes an expression template to be\n    // generated, rather than allocating.\n    const auto one_minus_y_coefficient =\n        0.25 * (3.0 * get(boundary_j).data() +\n                get(r).data() * get(boundary_dr_j).data());\n    const auto one_minus_y_cubed_coefficient =\n        -0.0625 *\n        (get(boundary_j).data() + get(r).data() * get(boundary_dr_j).data());\n    angular_view_j =\n        one_minus_y_collocation[i] * one_minus_y_coefficient +\n        pow<3>(one_minus_y_collocation[i]) * one_minus_y_cubed_coefficient;\n  }\n  Spectral::Swsh::create_angular_and_cartesian_coordinates(\n      cartesian_cauchy_coordinates, angular_cauchy_coordinates, l_max);\n}\n\nvoid InverseCubic<true>::pup(PUP::er& /*p*/) {}\nvoid InverseCubic<false>::pup(PUP::er& /*p*/) {}\n\nPUP::able::PUP_ID InverseCubic<true>::my_PUP_ID = 0;\nPUP::able::PUP_ID InverseCubic<false>::my_PUP_ID = 0;\n}  // namespace Cce::InitializeJ\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/InverseCubic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\nnamespace InitializeJ {\n\n/*!\n * \\brief Initialize \\f$J\\f$ on the first hypersurface from provided boundary\n * values of \\f$J\\f$, \\f$R\\f$, and \\f$\\partial_r J\\f$.\n *\n * \\details This initial data is chosen to take the function:\n *\n * \\f[ J = \\frac{A}{r} + \\frac{B}{r^3},\\f]\n *\n * where\n *\n * \\f{align*}{\n * A &= R \\left( \\frac{3}{2} J|_{r = R} + \\frac{1}{2} R \\partial_r J|_{r =\n * R}\\right) \\notag\\\\\n * B &= - \\frac{1}{2} R^3 (J|_{r = R} + R \\partial_r J|_{r = R})\n * \\f}\n */\ntemplate <>\nstruct InverseCubic<true> : InitializeJ<true> {\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Initialization process where J is set to a simple Ansatz with a\\n\"\n      \" A/r + B/r^3 piece such that it is smooth with the Cauchy data at the \\n\"\n      \"worldtube\"};\n\n  WRAPPED_PUPable_decl_template(InverseCubic);  // NOLINT\n  explicit InverseCubic(CkMigrateMessage* /*unused*/) {}\n\n  InverseCubic() = default;\n\n  std::unique_ptr<InitializeJ> get_clone() const override;\n\n  void operator()(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_inertial_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_inertial_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, size_t l_max,\n      size_t number_of_radial_points,\n      gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  void pup(PUP::er& /*p*/) override;\n};\n\n/*!\n * \\brief Initialize \\f$J\\f$ on the first hypersurface from provided boundary\n * values of \\f$J\\f$, \\f$R\\f$, and \\f$\\partial_r J\\f$.\n *\n * \\details This initial data is chosen to take the function:\n *\n * \\f[ J = \\frac{A}{r} + \\frac{B}{r^3},\\f]\n *\n * where\n *\n * \\f{align*}{\n * A &= R \\left( \\frac{3}{2} J|_{r = R} + \\frac{1}{2} R \\partial_r J|_{r =\n * R}\\right) \\notag\\\\\n * B &= - \\frac{1}{2} R^3 (J|_{r = R} + R \\partial_r J|_{r = R})\n * \\f}\n */\ntemplate <>\nstruct InverseCubic<false> : InitializeJ<false> {\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Initialization process where J is set to a simple Ansatz with a\\n\"\n      \" A/r + B/r^3 piece such that it is smooth with the Cauchy data at the \\n\"\n      \"worldtube\"};\n\n  WRAPPED_PUPable_decl_template(InverseCubic);  // NOLINT\n  explicit InverseCubic(CkMigrateMessage* /*unused*/) {}\n\n  InverseCubic() = default;\n\n  std::unique_ptr<InitializeJ> get_clone() const override;\n\n  void operator()(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, size_t l_max,\n      size_t number_of_radial_points,\n      gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  void pup(PUP::er& /*p*/) override;\n};\n}  // namespace InitializeJ\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/NoIncomingRadiation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/Initialize/NoIncomingRadiation.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"NumericalAlgorithms/OdeIntegration/OdeIntegration.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::InitializeJ {\nnamespace {\n\nvoid radial_evolve_psi0_condition(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> volume_j_id,\n    const SpinWeighted<ComplexDataVector, 2>& boundary_j,\n    const SpinWeighted<ComplexDataVector, 2>& boundary_dr_j,\n    const SpinWeighted<ComplexDataVector, 0>& r, const size_t l_max,\n    const size_t number_of_radial_points) {\n  // use the maximum to measure the scale for the vector quantities\n  const double j_scale = max(abs(boundary_j.data()));\n  const double dy_j_scale = max(abs(0.5 * boundary_dr_j.data() * r.data()));\n  // set initial step size according to the first couple of steps in section\n  // II.4 of Solving Ordinary Differential equations by Hairer, Norsett, and\n  // Wanner\n  double initial_radial_step = 1.0e-6;\n  if (j_scale > 1.0e-5 and dy_j_scale > 1.0e-5) {\n    initial_radial_step = 0.01 * j_scale / dy_j_scale;\n  }\n\n  const auto psi_0_condition_system =\n      [](const std::array<ComplexDataVector, 2>& bondi_j_and_i,\n         std::array<ComplexDataVector, 2>& dy_j_and_dy_i, const double y) {\n        dy_j_and_dy_i[0] = bondi_j_and_i[1];\n        const auto& bondi_j = bondi_j_and_i[0];\n        const auto& bondi_i = bondi_j_and_i[1];\n        dy_j_and_dy_i[1] =\n            -0.0625 *\n            (square(conj(bondi_i) * bondi_j) + square(conj(bondi_j) * bondi_i) -\n             2.0 * bondi_i * conj(bondi_i) * (2.0 + bondi_j * conj(bondi_j))) *\n            (4.0 * bondi_j + bondi_i * (1.0 - y)) /\n            (1.0 + bondi_j * conj(bondi_j));\n      };\n\n  boost::numeric::odeint::dense_output_runge_kutta<\n      boost::numeric::odeint::controlled_runge_kutta<\n          boost::numeric::odeint::runge_kutta_dopri5<\n              std::array<ComplexDataVector, 2>>>>\n      dense_stepper = boost::numeric::odeint::make_dense_output(\n          1.0e-14, 1.0e-14,\n          boost::numeric::odeint::runge_kutta_dopri5<\n              std::array<ComplexDataVector, 2>>{});\n  dense_stepper.initialize(\n      std::array<ComplexDataVector, 2>{\n          {boundary_j.data(), 0.5 * boundary_dr_j.data() * r.data()}},\n      -1.0, initial_radial_step);\n  auto state_buffer =\n      std::array<ComplexDataVector, 2>{{ComplexDataVector{boundary_j.size()},\n                                        ComplexDataVector{boundary_j.size()}}};\n\n  std::pair<double, double> step_range =\n      dense_stepper.do_step(psi_0_condition_system);\n  const auto& y_collocation =\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(\n                                       number_of_radial_points);\n  for (size_t y_collocation_point = 0;\n       y_collocation_point < number_of_radial_points; ++y_collocation_point) {\n    while(step_range.second < y_collocation[y_collocation_point]) {\n      step_range = dense_stepper.do_step(psi_0_condition_system);\n    }\n    if (step_range.second < y_collocation[y_collocation_point] or\n        step_range.first > y_collocation[y_collocation_point]) {\n      ERROR(\n          \"Psi 0 radial integration failed. The current y value is \"\n          \"incompatible with the required Gauss-Lobatto point.\");\n    }\n    dense_stepper.calc_state(y_collocation[y_collocation_point], state_buffer);\n    ComplexDataVector angular_view{\n        volume_j_id->data().data() +\n            y_collocation_point *\n                Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    angular_view = state_buffer[0];\n  }\n}\n}  // namespace\n\nNoIncomingRadiation::NoIncomingRadiation(\n    const double angular_coordinate_tolerance, const size_t max_iterations,\n    const bool require_convergence)\n    : require_convergence_{require_convergence},\n      angular_coordinate_tolerance_{angular_coordinate_tolerance},\n      max_iterations_{max_iterations} {}\n\nstd::unique_ptr<InitializeJ<false>> NoIncomingRadiation::get_clone() const {\n  return std::make_unique<NoIncomingRadiation>(*this);\n}\n\nvoid NoIncomingRadiation::operator()(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& /*beta*/,\n    const size_t l_max, const size_t number_of_radial_points,\n    const gsl::not_null<Parallel::NodeLock*> /*hdf5_lock*/) const {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  radial_evolve_psi0_condition(make_not_null(&get(*j)), get(boundary_j),\n                               get(boundary_dr_j), get(r), l_max,\n                               number_of_radial_points);\n  const SpinWeighted<ComplexDataVector, 2> j_at_scri_view;\n  make_const_view(make_not_null(&j_at_scri_view), get(*j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>>>\n      iteration_buffers{number_of_angular_points};\n\n  auto& evolution_gauge_surface_j =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 2>>>(\n          iteration_buffers));\n  auto& interpolated_k =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          iteration_buffers));\n  auto& gauge_omega =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                               std::integral_constant<int, 0>>>(\n          iteration_buffers);\n\n  // find a coordinate transformation such that in the new coordinates,\n  // J is zero at scri+\n  auto iteration_function =\n      [&interpolated_k, &gauge_omega, &evolution_gauge_surface_j,\n       &j_at_scri_view](\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n              gauge_c_step,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              gauge_d_step,\n          const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n          const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n          const Spectral::Swsh::SwshInterpolator& iteration_interpolator) {\n        iteration_interpolator.interpolate(\n            make_not_null(&evolution_gauge_surface_j), j_at_scri_view);\n        interpolated_k.data() =\n            sqrt(1.0 + evolution_gauge_surface_j.data() *\n                           conj(evolution_gauge_surface_j.data()));\n        get(gauge_omega).data() =\n            0.5 * sqrt(get(gauge_d).data() * conj(get(gauge_d).data()) -\n                       get(gauge_c).data() * conj(get(gauge_c).data()));\n        evolution_gauge_surface_j.data() =\n            0.25 *\n            (square(conj(get(gauge_d).data())) *\n                 evolution_gauge_surface_j.data() +\n             square(get(gauge_c).data()) *\n                 conj(evolution_gauge_surface_j.data()) +\n             2.0 * get(gauge_c).data() * conj(get(gauge_d).data()) *\n                 interpolated_k.data()) /\n            square(get(gauge_omega).data());\n\n        double max_error = max(abs(evolution_gauge_surface_j.data()));\n\n        // The alteration in each of the spin-weighted Jacobian factors\n        // determined by linearizing the system in small J\n        get(*gauge_c_step).data() =\n            -0.5 * evolution_gauge_surface_j.data() *\n            square(get(gauge_omega).data()) /\n            (get(gauge_d).data() * interpolated_k.data());\n        get(*gauge_d_step).data() = get(*gauge_c_step).data() *\n                                    conj(get(gauge_c).data()) /\n                                    conj(get(gauge_d).data());\n        return max_error;\n      };\n\n  auto finalize_function =\n      [&j, &gauge_omega, &l_max](\n          const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n          const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n          const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n              local_angular_cauchy_coordinates,\n          const Spectral::Swsh::SwshInterpolator& interpolator) {\n        get(gauge_omega).data() =\n            0.5 * sqrt(get(gauge_d).data() * conj(get(gauge_d).data()) -\n                       get(gauge_c).data() * conj(get(gauge_c).data()));\n\n        GaugeAdjustInitialJ::apply(j, gauge_c, gauge_d, gauge_omega,\n                                   local_angular_cauchy_coordinates,\n                                   interpolator, l_max);\n      };\n\n  detail::iteratively_adapt_angular_coordinates(\n      cartesian_cauchy_coordinates, angular_cauchy_coordinates, l_max,\n      angular_coordinate_tolerance_, max_iterations_, 1.0e-2,\n      iteration_function, require_convergence_, finalize_function);\n}\n\nvoid NoIncomingRadiation::pup(PUP::er& p) {\n  p | require_convergence_;\n  p | angular_coordinate_tolerance_;\n  p | max_iterations_;\n}\n\nPUP::able::PUP_ID NoIncomingRadiation::my_PUP_ID = 0;\n}  // namespace Cce::InitializeJ\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/NoIncomingRadiation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\nnamespace InitializeJ {\n\n/*!\n * \\brief Initialize \\f$J\\f$ on the first hypersurface by constraining\n * \\f$\\Psi_0 = 0\\f$.\n *\n * \\details This algorithm first radially evolves the \\f$\\Psi_0 = 0\\f$\n * condition, which can be converted to a second-order radial ODE for J. Then,\n * the initial data generator performs an iterative solve for the angular\n * coordinates necessary to ensure asymptotic flatness. The parameters for the\n * iterative procedure are determined by options\n * `AngularCoordinateTolerance` and `MaxIterations`.\n */\nstruct NoIncomingRadiation : InitializeJ<false> {\n  struct AngularCoordinateTolerance {\n    using type = double;\n    static std::string name() { return \"AngularCoordTolerance\"; }\n    static constexpr Options::String help = {\n        \"Tolerance of initial angular coordinates for CCE\"};\n    static type lower_bound() { return 1.0e-14; }\n    static type upper_bound() { return 1.0e-3; }\n    static type suggested_value() { return 1.0e-10; }\n  };\n\n  struct MaxIterations {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Number of linearized inversion iterations.\"};\n    static type lower_bound() { return 10; }\n    static type upper_bound() { return 1000; }\n    static type suggested_value() { return 300; }\n  };\n\n  struct RequireConvergence {\n    using type = bool;\n    static constexpr Options::String help = {\n      \"If true, initialization will error if it hits MaxIterations\"};\n    static type suggested_value() { return true; }\n  };\n\n  using options =\n      tmpl::list<AngularCoordinateTolerance, MaxIterations, RequireConvergence>;\n  static constexpr Options::String help = {\n      \"Initialization process where J is set so Psi0 is vanishing\\n\"\n      \"(roughly a no incoming radiation condition)\"};\n\n  WRAPPED_PUPable_decl_template(NoIncomingRadiation);  // NOLINT\n  explicit NoIncomingRadiation(CkMigrateMessage* /*unused*/) {}\n\n  NoIncomingRadiation(double angular_coordinate_tolerance,\n                      size_t max_iterations, bool require_convergence = false);\n\n  NoIncomingRadiation() = default;\n\n  std::unique_ptr<InitializeJ> get_clone() const override;\n\n  void operator()(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, size_t l_max,\n      size_t number_of_radial_points,\n      gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  void pup(PUP::er& p) override;\n\n private:\n  bool require_convergence_ = false;\n  double angular_coordinate_tolerance_ = 1.0e-10;\n  size_t max_iterations_ = 300;\n};\n}  // namespace InitializeJ\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/RegisterInitializeJWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n/// \\cond\nnamespace Cce {\nnamespace Solutions::LinearizedBondiSachs_detail::InitializeJ {\nstruct LinearizedBondiSachs;\n}\n/// \\endcond\n\n/// A function for registering all of the InitializeJ derived classes with\n/// charm, including the ones not intended to be directly option-creatable\ntemplate <bool EvolveCcm, typename BoundaryComponent>\nvoid register_initialize_j_with_charm() {\n  PUPable_reg(SINGLE_ARG(Solutions::LinearizedBondiSachs_detail::InitializeJ::\n                         LinearizedBondiSachs));\n\n  if constexpr (tt::is_a_v<AnalyticWorldtubeBoundary, BoundaryComponent>) {\n    register_derived_classes_with_charm<Cce::InitializeJ::InitializeJ<false>>();\n  } else {\n    register_derived_classes_with_charm<\n        Cce::InitializeJ::InitializeJ<EvolveCcm>>();\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/ZeroNonSmooth.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/Initialize/ZeroNonSmooth.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::InitializeJ {\n\nZeroNonSmooth::ZeroNonSmooth(const double angular_coordinate_tolerance,\n                             const size_t max_iterations,\n                             const bool require_convergence)\n    : angular_coordinate_tolerance_{angular_coordinate_tolerance},\n      max_iterations_{max_iterations},\n      require_convergence_{require_convergence} {}\n\nstd::unique_ptr<InitializeJ<false>> ZeroNonSmooth::get_clone() const {\n  return std::make_unique<ZeroNonSmooth>(angular_coordinate_tolerance_,\n                                         max_iterations_);\n}\n\nvoid ZeroNonSmooth::operator()(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_cauchy_coordinates,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& /* boundary_dr_j*/,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& /*r*/,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& /*beta*/,\n    const size_t l_max, const size_t /*number_of_radial_points*/,\n    const gsl::not_null<Parallel::NodeLock*> /*hdf5_lock*/) const {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 2> j_boundary_view;\n  make_const_view(make_not_null(&j_boundary_view), get(*j), 0,\n                  number_of_angular_points);\n\n  get(*j).data() = 0.0;\n\n  Variables<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                      std::integral_constant<int, 2>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>,\n                 ::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                                      std::integral_constant<int, 0>>>>\n      iteration_buffers{number_of_angular_points};\n\n  auto& evolution_gauge_surface_j =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<0, ComplexDataVector>,\n                                   std::integral_constant<int, 2>>>(\n          iteration_buffers));\n  auto& interpolated_k =\n      get(get<::Tags::SpinWeighted<::Tags::TempScalar<1, ComplexDataVector>,\n                                   std::integral_constant<int, 0>>>(\n          iteration_buffers));\n  auto& gauge_omega =\n      get<::Tags::SpinWeighted<::Tags::TempScalar<2, ComplexDataVector>,\n                               std::integral_constant<int, 0>>>(\n          iteration_buffers);\n\n  // find a coordinate transformation such that in the new coordinates,\n  // J is zero at the worldtube (as we have set it to above)\n  auto iteration_function =\n      [&interpolated_k, &gauge_omega, &evolution_gauge_surface_j, &boundary_j](\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n              gauge_c_step,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              gauge_d_step,\n          const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_c,\n          const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_d,\n          const Spectral::Swsh::SwshInterpolator& iteration_interpolator) {\n        iteration_interpolator.interpolate(\n            make_not_null(&evolution_gauge_surface_j), get(boundary_j));\n        interpolated_k.data() =\n            sqrt(1.0 + evolution_gauge_surface_j.data() *\n                           conj(evolution_gauge_surface_j.data()));\n        get(gauge_omega).data() =\n            0.5 * sqrt(get(gauge_d).data() * conj(get(gauge_d).data()) -\n                       get(gauge_c).data() * conj(get(gauge_c).data()));\n        evolution_gauge_surface_j.data() =\n            0.25 *\n            (square(conj(get(gauge_d).data())) *\n                 evolution_gauge_surface_j.data() +\n             square(get(gauge_c).data()) *\n                 conj(evolution_gauge_surface_j.data()) +\n             2.0 * get(gauge_c).data() * conj(get(gauge_d).data()) *\n                 interpolated_k.data()) /\n            square(get(gauge_omega).data());\n\n        double max_error = max(abs(evolution_gauge_surface_j.data()));\n\n        // The alteration in each of the spin-weighted Jacobian factors\n        // determined by linearizing the system in small J\n        get(*gauge_c_step).data() =\n            -0.5 * evolution_gauge_surface_j.data() *\n            square(get(gauge_omega).data()) /\n            (get(gauge_d).data() * interpolated_k.data());\n        get(*gauge_d_step).data() = get(*gauge_c_step).data() *\n                                    conj(get(gauge_c).data()) /\n                                    conj(get(gauge_d).data());\n        return max_error;\n      };\n\n  detail::iteratively_adapt_angular_coordinates(\n      cartesian_cauchy_coordinates, angular_cauchy_coordinates, l_max,\n      angular_coordinate_tolerance_, max_iterations_, 1.0e-2,\n      iteration_function, require_convergence_);\n}\n\nvoid ZeroNonSmooth::pup(PUP::er& p) {\n  p | angular_coordinate_tolerance_;\n  p | max_iterations_;\n}\n\nPUP::able::PUP_ID ZeroNonSmooth::my_PUP_ID = 0;\n}  // namespace Cce::InitializeJ\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Initialize/ZeroNonSmooth.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\nnamespace InitializeJ {\n\n/*!\n * \\brief Initialize \\f$J\\f$ on the first hypersurface to be vanishing, finding\n * the appropriate angular coordinates to be continuous with the provided\n * worldtube boundary data.\n *\n * \\details Internally, this performs an iterative solve for the angular\n * coordinates necessary to give rise to a vanishing gauge-transformed J on the\n * worldtube boundary. The parameters for the iterative procedure are determined\n * by options `ZeroNonSmooth::AngularCoordinateTolerance` and\n * `ZeroNonSmooth::MaxIterations`. The resulting `J` will necessarily\n * have vanishing first radial derivative, and so will typically not be smooth\n * (only continuous) with the provided Cauchy data at the worldtube boundary.\n */\nstruct ZeroNonSmooth : InitializeJ<false> {\n  struct AngularCoordinateTolerance {\n    using type = double;\n    static std::string name() { return \"AngularCoordTolerance\"; }\n    static constexpr Options::String help = {\n        \"Tolerance of initial angular coordinates for CCE\"};\n    static type lower_bound() { return 1.0e-14; }\n    static type upper_bound() { return 1.0e-3; }\n    static type suggested_value() { return 1.0e-10; }\n  };\n\n  struct MaxIterations {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Number of linearized inversion iterations.\"};\n    static type lower_bound() { return 10; }\n    static type upper_bound() { return 1000; }\n    static type suggested_value() { return 300; }\n  };\n\n  struct RequireConvergence {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If true, initialization will error if it hits MaxIterations\"};\n    static type suggested_value() { return true; }\n  };\n  using options =\n      tmpl::list<AngularCoordinateTolerance, MaxIterations, RequireConvergence>;\n\n  static constexpr Options::String help = {\n      \"Initialization process where J is set so Psi0 is vanishing\\n\"\n      \"(roughly a no incoming radiation condition)\"};\n\n  WRAPPED_PUPable_decl_template(ZeroNonSmooth);  // NOLINT\n  explicit ZeroNonSmooth(CkMigrateMessage* /*unused*/) {}\n\n  ZeroNonSmooth(double angular_coordinate_tolerance, size_t max_iterations,\n                bool require_convergence = false);\n\n  ZeroNonSmooth() = default;\n\n  std::unique_ptr<InitializeJ> get_clone() const override;\n\n  void operator()(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_cauchy_coordinates,\n      gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n          angular_cauchy_coordinates,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta, size_t l_max,\n      size_t number_of_radial_points,\n      gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  void pup(PUP::er& p) override;\n\n private:\n  double angular_coordinate_tolerance_ = 1.0e-10;\n  size_t max_iterations_ = 300;\n  bool require_convergence_ = false;\n};\n}  // namespace InitializeJ\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  TimeStepping.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/KleinGordonSystem.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define EVOLVE_CCM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                            \\\n  template class ChangeTimeStepperOrder<                                  \\\n      Cce::KleinGordonSystem<EVOLVE_CCM(data)>,                           \\\n      Cce::Tags::CceEvolutionPrefix>;                                     \\\n  template class CleanHistory<Cce::KleinGordonSystem<EVOLVE_CCM(data)>,   \\\n                              Cce::Tags::CceEvolutionPrefix>;             \\\n  template class RecordTimeStepperData<                                   \\\n      Cce::KleinGordonSystem<EVOLVE_CCM(data)>>;                          \\\n  template class UpdateU<Cce::KleinGordonSystem<EVOLVE_CCM(data)>, false, \\\n                         Cce::Tags::CceEvolutionPrefix>;                  \\\n  template class UpdateU<Cce::KleinGordonSystem<EVOLVE_CCM(data)>, true,  \\\n                         Cce::Tags::CceEvolutionPrefix>;                  \\\n  template class ChangeTimeStepperOrder<Cce::System<EVOLVE_CCM(data)>,    \\\n                                        Cce::Tags::CceEvolutionPrefix>;   \\\n  template class CleanHistory<Cce::System<EVOLVE_CCM(data)>,              \\\n                              Cce::Tags::CceEvolutionPrefix>;             \\\n  template class RecordTimeStepperData<Cce::System<EVOLVE_CCM(data)>>;    \\\n  template class UpdateU<Cce::System<EVOLVE_CCM(data)>, false,            \\\n                         Cce::Tags::CceEvolutionPrefix>;                  \\\n  template class UpdateU<Cce::System<EVOLVE_CCM(data)>, true,             \\\n                         Cce::Tags::CceEvolutionPrefix>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (false, true))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/IntegrandInputSteps.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"Evolution/Systems/Cce/Equations.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\n/*!\n * The set of Bondi quantities computed by hypersurface step, in the required\n * order of computation\n */\nusing bondi_hypersurface_step_tags =\n    tmpl::list<Tags::BondiBeta, Tags::BondiQ, Tags::BondiU, Tags::BondiW,\n               Tags::BondiH>;\n\n/*!\n * Metafunction that is a `tmpl::list` of the temporary tags taken by the\n * `ComputeBondiIntegrand` computational struct.\n */\ntemplate <typename Tag>\nusing integrand_temporary_tags =\n    typename ComputeBondiIntegrand<Tag>::temporary_tags;\n\nnamespace detail {\n// structs containing typelists for organizing the set of tags needed at each\n// step of the CCE hypersurface evaluation steps. These are not directly the\n// tags that are inputs in the `Equations.hpp`, as those would contain\n// redundancy, and not necessarily obtain the derivatives during the most ideal\n// steps. So, the ordering in these structs has been slightly 'designed' in ways\n// that are not trivial to automate with tmpl list manipulation\ntemplate <typename Tag>\nstruct TagsToComputeForImpl;\n\ntemplate <>\nstruct TagsToComputeForImpl<Tags::BondiBeta> {\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiJ>, Tags::Dy<Tags::Dy<Tags::BondiJ>>>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n};\n\n// Note: Due to the order in which Jacobians for the conversion between\n// numerical and Bondi spin-weighted derivatives are evaluated, all of the\n// higher (second) spin-weighted derivatives must be computed AFTER the\n// eth(dy(bondi)) values (which act as inputs to the second derivative\n// conversions to fixed Bondi radius), so must appear later in the\n// `swsh_derivative_tags` typelists.\ntemplate <>\nstruct TagsToComputeForImpl<Tags::BondiQ> {\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiBeta>, Tags::Dy<Tags::Dy<Tags::BondiBeta>>,\n                 ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n                 ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiJ>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n};\n\ntemplate <>\nstruct TagsToComputeForImpl<Tags::BondiU> {\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Exp2Beta, Tags::Dy<Tags::BondiQ>,\n                 Tags::Dy<Tags::Dy<Tags::BondiQ>>>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n};\n\ntemplate <>\nstruct TagsToComputeForImpl<Tags::BondiW> {\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiU>, Tags::Dy<Tags::Dy<Tags::BondiU>>,\n                 Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>,\n                 Tags::Dy<Spectral::Swsh::Tags::Derivative<\n                     Tags::BondiJ, Spectral::Swsh::Tags::Ethbar>>>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<\n          Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>,\n          Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<\n          Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiU>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n  // Currently, the `eth_ethbar_j` term is the single instance of a swsh\n  // derivative needing nested `Spectral::Swsh::Tags::Derivatives` steps to\n  // compute. The reason is that if we do not compute the derivative in two\n  // steps, there are intermediate terms in the Jacobian which depend on eth_j,\n  // which is a spin-weight 3 quantity and therefore cannot be computed with\n  // libsharp (the SWSH library being used). If `eth_ethbar_j` becomes not\n  // needed, the remaining `second_swsh_derivative_tags` can be merged to the\n  // end of `swsh_derivative_tags` and the corresponding computational steps\n  // from `SwshDerivatives.hpp` removed.\n  using second_swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::EthEth>,\n                 Spectral::Swsh::Tags::Derivative<\n                     Tags::BondiBeta, Spectral::Swsh::Tags::EthEthbar>,\n                 Spectral::Swsh::Tags::Derivative<\n                     ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n                     Spectral::Swsh::Tags::EthEthbar>,\n                 Spectral::Swsh::Tags::Derivative<\n                     Tags::BondiJ, Spectral::Swsh::Tags::EthbarEthbar>,\n                 Spectral::Swsh::Tags::Derivative<\n                     Spectral::Swsh::Tags::Derivative<\n                         Tags::BondiJ, Spectral::Swsh::Tags::Ethbar>,\n                     Spectral::Swsh::Tags::Eth>>;\n};\n\ntemplate <>\nstruct TagsToComputeForImpl<Tags::BondiH> {\n  using pre_swsh_derivative_tags =\n      tmpl::list<::Tags::Multiplies<Tags::BondiJbar, Tags::BondiU>,\n                 ::Tags::Multiplies<Tags::BondiUbar, Tags::Dy<Tags::BondiJ>>,\n                 Tags::JbarQMinus2EthBeta, Tags::Dy<Tags::BondiW>>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::BondiQ, Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU, Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiUbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::JbarQMinus2EthBeta,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJbar, Tags::BondiU>,\n          Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiQ,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n};\n\ntemplate <>\nstruct TagsToComputeForImpl<Tags::KleinGordonSource<Tags::BondiBeta>> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::Dy<Tags::KleinGordonPsi>>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n};\n\ntemplate <>\nstruct TagsToComputeForImpl<Tags::KleinGordonSource<Tags::BondiQ>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::Eth>>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n};\n\ntemplate <>\nstruct TagsToComputeForImpl<Tags::KleinGordonPi> {\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::Dy<Tags::KleinGordonPsi>>>;\n  using second_swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::EthEth>,\n                 Spectral::Swsh::Tags::Derivative<\n                     Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEthbar>>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::KleinGordonPsi>,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::KleinGordonPsi>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n};\n}  // namespace detail\n\n/*!\n * \\brief A typelist for the set of `BoundaryValue` tags needed as an input to\n * any of the template specializations of `PrecomputeCceDependencies`.\n *\n * \\details This is provided for easy and maintainable\n * construction of a `Variables` or \\ref DataBoxGroup with all of the quantities\n * necessary for a CCE computation or portion thereof.\n * A container of these tags should have size\n * `Spectral::Swsh::number_of_swsh_collocation_points(l_max)`.\n */\ntemplate <template <typename> class BoundaryPrefix>\nusing pre_computation_boundary_tags =\n    tmpl::list<BoundaryPrefix<Tags::BondiR>,\n               BoundaryPrefix<Tags::DuRDividedByR>>;\n\n/*!\n * \\brief A typelist for the set of tags computed by the set of\n * template specializations of `PrecomputeCceDepedencies`.\n *\n * \\details This is provided for easy and maintainable construction of a\n * `Variables` or \\ref DataBoxGroup with all of the quantities needed for a CCE\n * computation or component. The data structures represented by these tags\n * should each have size `number_of_radial_points *\n * Spectral::Swsh::number_of_swsh_collocation_points(l_max)`. All of these tags\n * may be computed at once if using a \\ref DataBoxGroup using the template\n * `mutate_all_precompute_cce_dependencies` or individually using\n * the template specializations `PrecomputeCceDependencies`.\n *\n * \\note the tag `Tags::DuRDividedByR` is omitted from this list because in the\n * case where a gauge transformation must be applied, the time derivative\n * quantities must wait until later in the computation.\n */\nusing pre_computation_tags =\n    tmpl::list<Tags::EthRDividedByR, Tags::EthEthRDividedByR,\n               Tags::EthEthbarRDividedByR, Tags::BondiK, Tags::OneMinusY,\n               Tags::BondiR>;\n\n/// @{\n/*!\n * \\brief A typelist for the set of tags computed by the set of\n * template specializations of `ComputePreSwshDerivatives`.\n *\n * \\details This is provided for easy and maintainable construction of a\n * `Variables` or \\ref DataBoxGroup with all of the quantities needed for a CCE\n * computation or component. The data structures represented by these tags\n * should each have size `number_of_radial_points *\n * Spectral::Swsh::number_of_swsh_collocation_points(l_max)`. All of these tags\n * (for a given integrated Bondi quantity) may be computed at once if using a\n * \\ref DataBoxGroup using the template\n * `mutate_all_pre_swsh_derivatives_for_tag` or individually using the template\n * specializations of `ComputePreSwshDerivatives`. The full set of integrated\n * Bondi quantities is available from the typelist\n * `bondi_hypersurface_step_tags`.\n */\ntemplate <typename Tag>\nstruct pre_swsh_derivative_tags_to_compute_for {\n  using type =\n      typename detail::TagsToComputeForImpl<Tag>::pre_swsh_derivative_tags;\n};\n\ntemplate <typename Tag>\nusing pre_swsh_derivative_tags_to_compute_for_t =\n    typename pre_swsh_derivative_tags_to_compute_for<Tag>::type;\n/// @}\n\n/// @{\n/*!\n * \\brief A typelist for the set of tags computed by single spin-weighted\n * differentiation using utilities from the `Swsh` namespace.\n */\ntemplate <typename Tag>\nstruct single_swsh_derivative_tags_to_compute_for {\n  using type = typename detail::TagsToComputeForImpl<Tag>::swsh_derivative_tags;\n};\n\ntemplate <typename Tag>\nusing single_swsh_derivative_tags_to_compute_for_t =\n    typename single_swsh_derivative_tags_to_compute_for<Tag>::type;\n/// @}\n\n/// @{\n/*!\n * \\brief A typelist for the set of tags computed by multiple spin-weighted\n * differentiation using utilities from the `Swsh` namespace.\n */\ntemplate <typename Tag>\nstruct second_swsh_derivative_tags_to_compute_for {\n  using type =\n      typename detail::TagsToComputeForImpl<Tag>::second_swsh_derivative_tags;\n};\n\ntemplate <typename Tag>\nusing second_swsh_derivative_tags_to_compute_for_t =\n    typename second_swsh_derivative_tags_to_compute_for<Tag>::type;\n/// @}\n\n/// Typelist of steps for `SwshDerivatives` mutations called on volume\n/// quantities needed for scri+ computations\nusing all_swsh_derivative_tags_for_scri = tmpl::list<\n    Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiU>,\n                                     Spectral::Swsh::Tags::Eth>,\n    Spectral::Swsh::Tags::Derivative<\n        Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                         Spectral::Swsh::Tags::EthEthbar>,\n        Spectral::Swsh::Tags::Ethbar>,\n    Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::Du<Tags::BondiJ>>,\n                                     Spectral::Swsh::Tags::Ethbar>,\n    Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::Dy<Tags::BondiU>>,\n                                     Spectral::Swsh::Tags::Ethbar>,\n    Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiQ>,\n                                     Spectral::Swsh::Tags::Ethbar>,\n    Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::Dy<Tags::BondiBeta>>,\n                                     Spectral::Swsh::Tags::Eth>>;\n\n/// Typelist of steps for `PreSwshDerivatives` mutations called on boundary\n/// (angular grid only) quantities needed for scri+ computations\nusing all_boundary_pre_swsh_derivative_tags_for_scri =\n    tmpl::list<Tags::ComplexInertialRetardedTime>;\n\n/// Typelist of steps for `SwshDerivatives` mutations called on boundary\n/// (angular grid only) quantities needed for scri+ computations\nusing all_boundary_swsh_derivative_tags_for_scri =\n    tmpl::list<Spectral::Swsh::Tags::Derivative<\n        Tags::ComplexInertialRetardedTime, Spectral::Swsh::Tags::EthEth>>;\n\n/*!\n * \\brief A typelist for the set of tags computed by spin-weighted\n * differentiation using utilities from the `Swsh` namespace.\n *\n * \\details This is provided for easy and maintainable construction of a\n * `Variables` or \\ref DataBoxGroup with all of the quantities needed for a CCE\n * computation or component. The data structures represented by these tags\n * should each have size `number_of_radial_points *\n * Spectral::Swsh::number_of_swsh_collocation_points(l_max)`. All of these tags\n * (for a given integrated Bondi quantity) may be computed at once if using a\n * \\ref DataBoxGroup using the template `mutate_all_swsh_derivatives_for_tag`.\n * Individual tag computation is not provided in a convenient interface, as\n * there is significant savings in aggregating spin-weighted differentiation\n * steps. The full set of integrated Bondi quantities is available from the\n * typelist `bondi_hypersurface_step_tags`.\n */\nusing all_swsh_derivative_tags =\n    tmpl::remove_duplicates<tmpl::flatten<tmpl::list<\n        tmpl::transform<\n            bondi_hypersurface_step_tags,\n            tmpl::bind<tmpl::list,\n                       single_swsh_derivative_tags_to_compute_for<tmpl::_1>,\n                       second_swsh_derivative_tags_to_compute_for<tmpl::_1>>>,\n        all_swsh_derivative_tags_for_scri>>>;\n\n/*!\n * \\brief A typelist for the full set of coefficient buffers needed to process\n * all of the tags in `all_swsh_derivative_tags` using batch processing provided\n * in `mutate_all_swsh_derivatives_for_tag`.\n *\n * \\details This is provided for easy and maintainable construction of a\n * `Variables` or \\ref DataBoxGroup with all of the quantities needed for a CCE\n * computation or component. The data structures represented by these tags\n * should each have size `number_of_radial_points *\n * Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)`. Providing\n * buffers associated with these tags is necessary for the use of the aggregated\n * computation `mutate_all_swsh_derivatives_for_tag`.\n */\nusing all_transform_buffer_tags =\n    tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n        all_swsh_derivative_tags,\n        tmpl::bind<Spectral::Swsh::coefficient_buffer_tags_for_derivative_tag,\n                   tmpl::_1>>>>;\n\nnamespace detail {\ntemplate <typename Tag>\nstruct additional_pre_swsh_derivative_tags_for {\n  using type = tmpl::conditional_t<std::is_same_v<Tag, Tags::BondiH>,\n                                   tmpl::list<Tags::Dy<Tag>>,\n                                   tmpl::list<Tag, Tags::Dy<Tag>>>;\n};\n}  // namespace detail\n\n/// Typelist of steps for `PreSwshDerivatives` mutations needed for scri+\n/// computations\nusing all_pre_swsh_derivative_tags_for_scri =\n    tmpl::list<Tags::Du<Tags::BondiJ>, Tags::Dy<Tags::Du<Tags::BondiJ>>,\n               Tags::Dy<Tags::Dy<Tags::Du<Tags::BondiJ>>>,\n               Tags::Dy<Tags::Dy<Tags::BondiW>>,\n               Tags::Dy<Tags::Dy<Tags::BondiQ>>,\n               Tags::Dy<Tags::Dy<Tags::BondiU>>,\n               Tags::Dy<Tags::Dy<Tags::Dy<Tags::BondiJ>>>,\n               Tags::Dy<Tags::Dy<Tags::Dy<Tags::BondiU>>>,\n               Tags::Dy<Tags::Dy<Tags::Dy<Tags::BondiBeta>>>,\n               Tags::Dy<Spectral::Swsh::Tags::Derivative<\n                   Tags::BondiBeta, Spectral::Swsh::Tags::EthEthbar>>>;\n\n/*!\n * \\brief A typelist for the full set of tags needed as direct or indirect\n * input to any `ComputeBondiIntegrand` that are computed any specialization of\n * `ComputePreSwshDerivatives`.\n *\n * \\details This is provided for easy and maintainable construction of a\n * `Variables` or \\ref DataBoxGroup with all of the quantities needed for a CCE\n * computation or component. The data structures represented by these tags\n * should each have size `number_of_radial_points *\n * Spectral::Swsh::number_of_swsh_collocation_points(l_max)`.\n */\nusing all_pre_swsh_derivative_tags =\n    tmpl::remove_duplicates<tmpl::flatten<tmpl::list<\n        tmpl::transform<\n            bondi_hypersurface_step_tags,\n            tmpl::bind<\n                tmpl::list, pre_swsh_derivative_tags_to_compute_for<tmpl::_1>,\n                detail::additional_pre_swsh_derivative_tags_for<tmpl::_1>>>,\n        all_pre_swsh_derivative_tags_for_scri>>>;\n\n\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/InterfaceManagers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GhLocalTimeStepping.cpp\n  GhLockstep.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GhInterfaceManager.hpp\n  GhLocalTimeStepping.hpp\n  GhLockstep.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace Cce {\n/// \\brief Code for interfacing between the characteristic and Cachy systems.\nnamespace InterfaceManagers {\n\n/// \\cond\nclass GhLocalTimeStepping;\nclass GhLockstep;\n/// \\endcond\n\n/*!\n * \\brief Abstract base class for storage and retrieval of generalized harmonic\n * quantities communicated from a Cauchy simulation to the Cce system.\n *\n * \\details The functions that are required to be overriden in the derived\n * classes are:\n * - `GhInterfaceManager::get_clone()`: should return a\n * `std::unique_ptr<GhInterfaceManager>` with cloned state.\n * - `GhInterfaceManager::insert_gh_data()`: should store the portions\n * of the provided generalized harmonic data that are required to provide useful\n * boundary values for the CCE evolution at requested timesteps.\n * - `GhInterfaceManager::request_gh_data()`: should register requests\n * from the CCE evolution for boundary data.\n * - `GhInterfaceManager::retrieve_and_remove_first_ready_gh_data()`:\n * should return a `std::optional<std::tuple<TimeStepId,\n * tnsr::aa<DataVector, 3>, tnsr::iaa<DataVector, 3>, tnsr::aa<DataVector, 3>>>`\n * containing the boundary data associated with the oldest requested timestep if\n * enough data has been supplied via `insert_gh_data()` to determine the\n * boundary data. Otherwise, return a `std::nullopt` to indicate that the CCE\n * system must continue waiting for generalized harmonic input.\n * - `GhInterfaceManager::number_of_pending_requests()`: should return\n * the number of requests that have been registered to the class that do not yet\n * been retrieved via `retrieve_and_remove_first_ready_gh_data()`.\n * - `GhInterfaceManager::number_of_gh_times()`: should return the\n * number of time steps sent to `insert_gh_data()` that have not yet been\n * retrieved via `retrieve_and_remove_first_ready_gh_data()`.\n */\nclass GhInterfaceManager : public PUP::able {\n public:\n  using gh_variables = Variables<\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>>;\n\n  WRAPPED_PUPable_abstract(GhInterfaceManager);  // NOLINT\n\n  virtual std::unique_ptr<GhInterfaceManager> get_clone() const = 0;\n\n  virtual void request_gh_data(const TimeStepId&) = 0;\n\n  virtual auto retrieve_and_remove_first_ready_gh_data()\n      -> std::optional<std::tuple<TimeStepId, gh_variables>> = 0;\n\n  virtual size_t number_of_pending_requests() const = 0;\n\n  virtual size_t number_of_gh_times() const = 0;\n};\n\n}  // namespace InterfaceManagers\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/InterfaceManagers/GhLocalTimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLocalTimeStepping.hpp\"\n\n#include <cstddef>\n#include <deque>\n#include <map>\n#include <memory>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace Cce::InterfaceManagers {\n\nnamespace detail {\ntemplate <typename VarsToInterpolate>\nauto create_span_for_time_value(\n    double time, int interpolator_length,\n    const std::map<LinkedMessageId<double>, VarsToInterpolate,\n                   LinkedMessageIdLessComparator<double>>& gh_data) {\n  const auto now = gh_data.upper_bound(time);\n  if (std::distance(gh_data.begin(), now) < interpolator_length) {\n    return gh_data.begin();\n  } else if (std::distance(now, gh_data.end()) < interpolator_length) {\n    return std::prev(gh_data.end(), 2 * interpolator_length);\n  }\n  return std::prev(now, interpolator_length);\n}\n\ntemplate <typename VarsToInterpolate>\nbool interpolate_to_time(\n    const gsl::not_null<VarsToInterpolate*> vars_to_interpolate,\n    const std::map<LinkedMessageId<double>, VarsToInterpolate,\n                   LinkedMessageIdLessComparator<double>>& gh_data,\n    const std::unique_ptr<intrp::SpanInterpolator>& interpolator,\n    const double target_time) {\n  auto iterator_start = detail::create_span_for_time_value(\n      target_time, interpolator->required_number_of_points_before_and_after(),\n      gh_data);\n  for (auto gh_data_it = gh_data.begin();\n       gh_data_it !=\n       std::next(\n           iterator_start,\n           2 * static_cast<int>(\n                   interpolator->required_number_of_points_before_and_after()) -\n               1);\n       ++gh_data_it) {\n    if (gh_data_it->first.id != std::next(gh_data_it)->first.previous) {\n      return false;\n    }\n  }\n  DataVector time_points{\n      2 * interpolator->required_number_of_points_before_and_after()};\n  DataVector tensor_component_values{\n      2 * interpolator->required_number_of_points_before_and_after()};\n  for (auto [i, map_it] = std::make_tuple(0_st, iterator_start);\n       i < time_points.size(); ++i, ++map_it) {\n    time_points[i] = map_it->first.id;\n  }\n  tmpl::for_each<typename VarsToInterpolate::tags_list>(\n      [&vars_to_interpolate, &interpolator, &time_points,\n       &tensor_component_values, &target_time,\n       &iterator_start](auto tensor_tag_v) {\n        using tensor_tag = typename decltype(tensor_tag_v)::type;\n        auto& tensor = get<tensor_tag>(*vars_to_interpolate);\n        for (size_t i = 0; i < tensor.size(); ++i) {\n          for (size_t offset = 0;\n               offset < vars_to_interpolate->number_of_grid_points();\n               ++offset) {\n            // assemble data into easily interpolated structures\n            for (auto [time_index, gh_data_it] =\n                     std::make_tuple(0_st, iterator_start);\n                 time_index <\n                 2 * interpolator->required_number_of_points_before_and_after();\n                 ++time_index, ++gh_data_it) {\n              tensor_component_values[time_index] =\n                  get<tensor_tag>(gh_data_it->second)[i][offset];\n            }\n            tensor[i][offset] = interpolator->interpolate(\n                gsl::span<const double>{time_points.data(), time_points.size()},\n                gsl::span<const double>{tensor_component_values.data(),\n                                        tensor_component_values.size()},\n                target_time);\n          }\n        }\n      });\n  return true;\n}\n}  // namespace detail\n\nstd::unique_ptr<GhInterfaceManager> GhLocalTimeStepping::get_clone() const {\n  return std::make_unique<GhLocalTimeStepping>(*this);\n}\n\nvoid GhLocalTimeStepping::insert_gh_data(\n    const LinkedMessageId<double>& time_and_previous,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::iaa<DataVector, 3>& phi, const tnsr::aa<DataVector, 3>& pi) {\n  gh_variables input_gh_variables{get<0, 0>(spacetime_metric).size()};\n  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(input_gh_variables) =\n      spacetime_metric;\n  get<gh::Tags::Pi<DataVector, 3>>(input_gh_variables) = pi;\n  get<gh::Tags::Phi<DataVector, 3>>(input_gh_variables) = phi;\n  gh_data_.insert({time_and_previous, std::move(input_gh_variables)});\n  clean_up_gh_data();\n}\n\nvoid GhLocalTimeStepping::request_gh_data(const TimeStepId& time_id) {\n  requests_.insert(time_id);\n  if (requests_.size() == 1) {\n    clean_up_gh_data();\n  }\n}\n\nauto GhLocalTimeStepping::retrieve_and_remove_first_ready_gh_data()\n    -> std::optional<std::tuple<TimeStepId, gh_variables>> {\n  if (requests_.empty()) {\n    return std::nullopt;\n  }\n  const double first_request = requests_.begin()->substep_time();\n  if (gh_data_.size() >=\n          interpolator_->required_number_of_points_before_and_after() * 2 and\n      gh_data_.rbegin()->first.id > first_request) {\n    gh_variables requested_values{\n        gh_data_.rbegin()->second.number_of_grid_points()};\n    bool success =\n        gh_data_.begin()->first.previous == latest_removed_ and\n        detail::interpolate_to_time(make_not_null(&requested_values), gh_data_,\n                                    interpolator_, first_request);\n    if (not success) {\n      return std::nullopt;\n    }\n    std::tuple requested_data{*requests_.begin(), std::move(requested_values)};\n    requests_.erase(requests_.begin());\n    clean_up_gh_data();\n    return requested_data;\n  }\n  return std::nullopt;\n}\n\nvoid GhLocalTimeStepping::clean_up_gh_data() {\n  if (requests_.empty() or gh_data_.empty()) {\n    return;\n  }\n  // count the number of elements to remove from the start\n  const size_t max_to_remove =\n      gh_data_.size() -\n      interpolator_->required_number_of_points_before_and_after() * 2;\n  size_t number_of_points_before_first_request =\n      static_cast<size_t>(std::distance(\n          gh_data_.begin(),\n          gh_data_.upper_bound(requests_.begin()->substep_time())));\n  const size_t number_of_points_to_remove = std::min(\n      max_to_remove, std::max(number_of_points_before_first_request, 1_st) - 1);\n  for (size_t i = 0; i < number_of_points_to_remove; ++i) {\n    if (gh_data_.begin()->first.previous != latest_removed_) {\n      // times are currently not ordered, so it is not safe to remove any more.\n      break;\n    }\n    latest_removed_ = gh_data_.begin()->first.id;\n    gh_data_.erase(gh_data_.begin());\n  }\n}\n\nvoid GhLocalTimeStepping::pup(PUP::er& p) {\n  pup_override(p, gh_data_);\n  p | requests_;\n  p | interpolator_;\n  p | latest_removed_;\n}\n\nPUP::able::PUP_ID GhLocalTimeStepping::my_PUP_ID = 0;\n}  // namespace Cce::InterfaceManagers\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/InterfaceManagers/GhLocalTimeStepping.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <deque>\n#include <map>\n#include <memory>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::InterfaceManagers {\n\n/*!\n * \\brief Implementation of a `GhInterfaceManager` that provides data according\n * to interpolation between provided GH data.\n *\n * \\details This class receives data from the Generalized Harmonic system\n * sufficient to perform interpolation to arbitrary times required by CCE. From\n * the Generalized Harmonic system, it receives the spacetime metric \\f$g_{a\n * b}\\f$ and Generalized Harmonic \\f$\\Phi_{i a b}\\f$ and \\f$\\Pi_{ab}\\f$ and the\n * current time via `GhLocalTimeStepping::insert_gh_data()`. The CCE\n * system supplies requests for time steps via\n * `GhLocalTimeStepping::request_gh_data()` and receives interpolated boundary\n * data via `GhLocalTimeStepping::retrieve_and_remove_first_ready_gh_data()`.\n */\nclass GhLocalTimeStepping : public GhInterfaceManager {\n public:\n  struct BoundaryInterpolator {\n    using type = std::unique_ptr<intrp::SpanInterpolator>;\n    static constexpr Options::String help = {\n        \"Interpolator for computing CCE data from GH time points\"};\n  };\n\n  static constexpr Options::String help{\n      \"Interpolate data from the GH system to generate CCE inputs\"};\n\n  using options = tmpl::list<BoundaryInterpolator>;\n\n  GhLocalTimeStepping() = default;\n  GhLocalTimeStepping(const GhLocalTimeStepping& rhs)\n      : gh_data_{rhs.gh_data_},\n        requests_{rhs.requests_},\n        latest_removed_{rhs.latest_removed_} {\n    if (rhs.interpolator_.get() != nullptr) {\n      interpolator_ = rhs.interpolator_->get_clone();\n    }\n  }\n\n  explicit GhLocalTimeStepping(\n      std::unique_ptr<intrp::SpanInterpolator> interpolator)\n      : interpolator_{std::move(interpolator)} {}\n  GhLocalTimeStepping& operator=(const GhLocalTimeStepping& rhs) {\n    if (rhs.interpolator_.get() != nullptr) {\n      interpolator_ = rhs.interpolator_->get_clone();\n    }\n    gh_data_ = rhs.gh_data_;\n    requests_ = rhs.requests_;\n    latest_removed_ = rhs.latest_removed_;\n    return *this;\n  }\n\n  explicit GhLocalTimeStepping(CkMigrateMessage* /*unused*/) {}\n\n  WRAPPED_PUPable_decl_template(GhLocalTimeStepping);  // NOLINT\n\n  std::unique_ptr<GhInterfaceManager> get_clone() const override;\n\n  /// \\brief Store the provided data set to prepare for interpolation.\n  void insert_gh_data(const LinkedMessageId<double>& time_and_previous,\n                      const tnsr::aa<DataVector, 3>& spacetime_metric,\n                      const tnsr::iaa<DataVector, 3>& phi,\n                      const tnsr::aa<DataVector, 3>& pi);\n\n  /// \\brief Store the next time step that will be required by the CCE system to\n  /// proceed with the evolution.\n  void request_gh_data(const TimeStepId& time_id) override;\n\n  /// \\brief Return a `std::optional` of either the dense-output data at the\n  /// least recently requested time, or `std::nullopt` if not enough GH data has\n  /// been supplied yet.\n  auto retrieve_and_remove_first_ready_gh_data()\n      -> std::optional<std::tuple<TimeStepId, gh_variables>> override;\n\n  /// The number of requests that have been submitted and not yet retrieved.\n  size_t number_of_pending_requests() const override {\n    return requests_.size();\n  }\n\n  /// \\brief  The number of times for which data from the GH system is stored.\n  size_t number_of_gh_times() const override { return gh_data_.size(); }\n\n  /// Serialization for Charm++.\n  void pup(PUP::er& p) override;\n\n private:\n  void clean_up_gh_data();\n\n  std::map<::LinkedMessageId<double>, gh_variables,\n           LinkedMessageIdLessComparator<double>>\n      gh_data_;\n  std::set<TimeStepId> requests_;\n  std::unique_ptr<intrp::SpanInterpolator> interpolator_;\n  std::optional<double> latest_removed_ = std::nullopt;\n};\n}  // namespace Cce::InterfaceManagers\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/InterfaceManagers/GhLockstep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp\"\n\n#include <deque>\n#include <memory>\n#include <tuple>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace Cce::InterfaceManagers {\n\nstd::unique_ptr<GhInterfaceManager> GhLockstep::get_clone() const {\n  return std::make_unique<GhLockstep>(*this);\n}\n\nvoid GhLockstep::insert_gh_data(TimeStepId time_id,\n                                const tnsr::aa<DataVector, 3>& spacetime_metric,\n                                const tnsr::iaa<DataVector, 3>& phi,\n                                const tnsr::aa<DataVector, 3>& pi) {\n  gh_variables input_gh_variables{get<0, 0>(spacetime_metric).size()};\n  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(input_gh_variables) =\n      spacetime_metric;\n  get<gh::Tags::Pi<DataVector, 3>>(input_gh_variables) = pi;\n  get<gh::Tags::Phi<DataVector, 3>>(input_gh_variables) = phi;\n  // NOLINTNEXTLINE(performance-move-const-arg)\n  provided_data_.insert({std::move(time_id), std::move(input_gh_variables)});\n}\n\nvoid GhLockstep::request_gh_data(const TimeStepId& time_id) {\n  requests_.insert(time_id);\n}\n\nauto GhLockstep::retrieve_and_remove_first_ready_gh_data()\n    -> std::optional<std::tuple<TimeStepId, gh_variables>> {\n  if (provided_data_.empty() or requests_.empty()) {\n    return std::nullopt;\n  }\n  if (provided_data_.count(*requests_.begin()) == 0_st) {\n    return std::nullopt;\n  }\n  const std::tuple<TimeStepId, gh_variables> return_data{\n      *requests_.begin(), std::move(provided_data_[*requests_.begin()])};\n  provided_data_.erase(*requests_.begin());\n  requests_.erase(requests_.begin());\n  return return_data;\n}\n\nvoid GhLockstep::pup(PUP::er& p) {\n  p | provided_data_;\n  p | requests_;\n}\n\nPUP::able::PUP_ID GhLockstep::my_PUP_ID = 0;\n}  // namespace Cce::InterfaceManagers\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <deque>\n#include <memory>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace Cce::InterfaceManagers {\n\n/*!\n * \\brief Simple implementation of a `GhInterfaceManager` that only\n * provides boundary data on matching `TimeStepId`s\n *\n * \\details This version of the interface manager assumes that the CCE system\n * and the generalized harmonic system that it communicates with evolve with an\n * identical time stepper and on identical time step intervals (they evolve in\n * 'lock step'). As a result, and to streamline communications, new data is\n * always immediately 'ready' and requests are ignored to produce the behavior\n * of just immediately gauge-transforming and sending the data to the CCE\n * component as soon as it becomes available from the GH system.\n *\n * \\warning Using this interface manager when the GH components and the CCE\n * evolution are not identically stepped is considered undefined behavior. For\n * current implementations involving dense output and local time-stepping, this\n * interface manager can only be used during self-start.\n */\nclass GhLockstep : public GhInterfaceManager {\n public:\n  using GhInterfaceManager::gh_variables;\n\n  static constexpr Options::String help{\n      \"Pass data between GH and CCE systems on matching timesteps only.\"};\n\n  using options = tmpl::list<>;\n\n  GhLockstep() = default;\n\n  explicit GhLockstep(CkMigrateMessage* /*unused*/) {}\n\n  WRAPPED_PUPable_decl_template(GhLockstep);  // NOLINT\n\n  std::unique_ptr<GhInterfaceManager> get_clone() const override;\n\n  /// \\brief Store a provided data set in a `std::deque`.\n  ///\n  /// \\details The lock-step constraint ensures that only the generalized\n  /// harmonic variables `spacetime_metric`, `phi`, and `pi` are used. The\n  /// remaining variables are accepted to comply with the more general abstract\n  /// interface.\n  void insert_gh_data(TimeStepId time_id,\n                      const tnsr::aa<DataVector, 3>& spacetime_metric,\n                      const tnsr::iaa<DataVector, 3>& phi,\n                      const tnsr::aa<DataVector, 3>& pi);\n\n  void request_gh_data(const TimeStepId& time_id) override;\n\n  /// \\brief Return a `std::optional<std::tuple>` of the least recently\n  /// submitted generalized harmonic boundary data if any exists and removes it\n  /// from the internal `std::deque`, otherwise returns `std::nullopt`.\n  auto retrieve_and_remove_first_ready_gh_data()\n      -> std::optional<std::tuple<TimeStepId, gh_variables>> override;\n\n  /// \\brief This class ignores requests to ensure a one-way communication\n  /// pattern, so the number of requests is always 0.\n  size_t number_of_pending_requests() const override { return 0; }\n\n  /// \\brief The number of times at which data from a GH evolution have been\n  /// stored and not yet retrieved\n  size_t number_of_gh_times() const override { return provided_data_.size(); }\n\n  /// Serialization for Charm++.\n  void pup(PUP::er& p) override;\n\n private:\n  std::map<TimeStepId, gh_variables> provided_data_;\n  std::set<TimeStepId> requests_;\n};\n\n}  // namespace Cce::InterfaceManagers\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/KleinGordonSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/KleinGordonSource.hpp\"\n\nnamespace Cce {\n\nvoid ComputeKleinGordonSource<Tags::BondiBeta>::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_source_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_kg_psi,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  get(*kg_source_beta) = 2. * M_PI * get(one_minus_y) * square(get(dy_kg_psi));\n}\n\nvoid ComputeKleinGordonSource<Tags::BondiQ>::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> kg_source_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_kg_psi,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_kg_psi) {\n  get(*kg_source_q) = 16. * M_PI * get(eth_kg_psi) * get(dy_kg_psi);\n}\n\nvoid ComputeKleinGordonSource<Tags::BondiU>::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> kg_source_u,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t volume_size =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n      number_of_radial_points;\n  get(*kg_source_u) = SpinWeighted<ComplexDataVector, 1>{volume_size, 0.0};\n}\n\nvoid ComputeKleinGordonSource<Tags::BondiW>::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_source_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_kg_psi) {\n  SpinWeighted<ComplexDataVector, 0> complex_part;\n  SpinWeighted<ComplexDataVector, 0> real_part;\n\n  complex_part.data() =\n      2.0 * real(get(bondi_j).data() * square(conj(get(eth_kg_psi))).data());\n  real_part.data() =\n      -2.0 * get(bondi_k).data() * square(abs(get(eth_kg_psi).data()));\n  get(*kg_source_w) =\n      M_PI * get(exp_2_beta) / get(bondi_r) * (complex_part + real_part);\n}\n\nvoid ComputeKleinGordonSource<Tags::BondiH>::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> kg_source_h,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_kg_psi) {\n  get(*kg_source_h) =\n      2 * M_PI * get(exp_2_beta) / get(bondi_r) * square(get(eth_kg_psi));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/KleinGordonSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\n/*!\n * \\brief Computes `Tags::KleinGordonSource<Tag>` for the tags evolved by\n * Klein-Gordon Cce.\n *\n * \\details In scalar-tensor theory, the Cce hypersurface equations get\n * additional source terms contributed by the stress-energy tensor of the scalar\n * field. The tag `Tags::KleinGordonSource<Tag>` stores the corresponding volume\n * data.\n */\ntemplate <typename Tag>\nstruct ComputeKleinGordonSource;\n\n/*!\n * \\brief Computes the Klein-Gordon source of the Bondi \\f$\\beta\\f$\n *\n * \\details The source reads:\n *\n * \\f{align*}{\n * 2 \\pi (1-y) (\\partial_y\\psi)^2,\n * \\f}\n * where \\f$\\psi\\f$ is the Klein-Gordon (scalar) field.\n */\ntemplate <>\nstruct ComputeKleinGordonSource<Tags::BondiBeta> {\n  using return_tags = tmpl::list<Tags::KleinGordonSource<Tags::BondiBeta>>;\n  using argument_tags =\n      tmpl::list<Tags::Dy<Tags::KleinGordonPsi>, Tags::OneMinusY>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_source_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_kg_psi,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n};\n\n/*!\n * \\brief Computes the Klein-Gordon source of the Bondi \\f$Q\\f$\n *\n * \\details Following the nomenclature of \\cite Moxon2020gha and their Eq. (49),\n * the scalar field contributes only to the regular part of the source term\n * \\f$S_2^R\\f$. The expression reads:\n *\n * \\f{align*}{\n * 16 \\pi \\eth\\psi \\partial_y\\psi,\n * \\f}\n * where \\f$\\psi\\f$ is the Klein-Gordon (scalar) field.\n */\ntemplate <>\nstruct ComputeKleinGordonSource<Tags::BondiQ> {\n  using return_tags = tmpl::list<Tags::KleinGordonSource<Tags::BondiQ>>;\n  using argument_tags =\n      tmpl::list<Tags::Dy<Tags::KleinGordonPsi>,\n                 Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::Eth>>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> kg_source_q,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_kg_psi,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_kg_psi);\n};\n\n/*!\n * \\brief Computes the Klein-Gordon source of the Bondi \\f$U\\f$\n *\n * \\details The source vanishes.\n */\ntemplate <>\nstruct ComputeKleinGordonSource<Tags::BondiU> {\n  using return_tags = tmpl::list<Tags::KleinGordonSource<Tags::BondiU>>;\n  using argument_tags = tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> kg_source_u,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n/*!\n * \\brief Computes the Klein-Gordon source of the Bondi \\f$W\\f$\n *\n * \\details Following the nomenclature of \\cite Moxon2020gha and their Eq. (49),\n * the scalar field contributes only to the regular part of the source term\n * \\f$S_2^R\\f$. The expression reads:\n *\n * \\f{align*}{\n * \\frac{\\pi e^{2\\beta}}{R} \\left[J(\\bar{\\eth}\\psi)^2 + \\bar{J}(\\eth\n * \\psi)^2-2K \\eth\\psi\\bar{\\eth}\\psi\\right],\n * \\f}\n * where \\f$\\psi\\f$ is the Klein-Gordon (scalar) field.\n */\ntemplate <>\nstruct ComputeKleinGordonSource<Tags::BondiW> {\n  using return_tags = tmpl::list<Tags::KleinGordonSource<Tags::BondiW>>;\n  using argument_tags =\n      tmpl::list<Tags::Exp2Beta, Tags::BondiR, Tags::BondiK, Tags::BondiJ,\n                 Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::Eth>>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_source_w,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_kg_psi);\n};\n\n/*!\n * \\brief Computes the Klein-Gordon source of the Bondi \\f$H\\f$\n *\n * \\details Following the nomenclature of \\cite Moxon2020gha and their Eq. (50),\n * the scalar field contributes only to the regular part of the source term\n * \\f$S_3^R\\f$. The expression reads:\n *\n * \\f{align*}{\n * 2 \\pi \\frac{e^{2\\beta}}{R}(\\eth\\psi)^2,\n * \\f}\n * where \\f$\\psi\\f$ is the Klein-Gordon (scalar) field.\n */\ntemplate <>\nstruct ComputeKleinGordonSource<Tags::BondiH> {\n  using return_tags = tmpl::list<Tags::KleinGordonSource<Tags::BondiH>>;\n  using argument_tags =\n      tmpl::list<Tags::Exp2Beta, Tags::BondiR,\n                 Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                  Spectral::Swsh::Tags::Eth>>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> kg_source_h,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_kg_psi);\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/KleinGordonSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\n/*!\n * \\brief Performing Cauchy characteristic evolution and Cauchy characteristic\n * matching for Einstein-Klein-Gordon system.\n *\n * \\details The code adopts the characteristic formulation to solve the field\n * equations for scalar-tensor theory as considered in \\cite Ma2023sok. Working\n * in the Einstein frame, a real-valued scalar field \\f$\\psi\\f$ is minimally\n * coupled with the spacetime metric \\f$g_{\\mu\\nu}\\f$. The corresponding action\n * is expressed as follows:\n *\n * \\f[\n * S = \\int d^4x \\sqrt{-g} \\left(\\frac{R}{16 \\pi}  - \\frac{1}{2} \\nabla_\\mu \\psi\n * \\nabla^\\mu \\psi\\right).\n * \\f]\n *\n * The system consists of two sectors: scalar and tensor (metric). The scalar\n * field follows the Klein-Gordon (KG) equation\n *\n * \\f[\n * \\Box \\psi = 0.\n * \\f]\n *\n * Its characteristic expression is given in \\cite Barreto2004fn, yielding\n * the hypersurface equation for \\f$\\partial_u\\psi=\\Pi\\f$, where\n * \\f$\\partial_u\\f$ represents differentiation with respect to retarded time $u$\n * at fixed numerical radius \\f$y\\f$. The code first integrates the KG equation\n * radially to determine \\f$\\Pi\\f$. Subsequently, the time integration is\n * performed to evolve the scalar field \\f$\\psi\\f$ forward in time.\n *\n * The tensor (metric) sector closely aligns with the current GR CCE system,\n * incorporating additional source terms that depend only on the scalar field\n * \\f$\\psi\\f$ and its spatial derivatives, rather than its time derivative\n * \\f$(\\Pi)\\f$. This feature preserves the hierarchical structure of the\n * equations. As a result, the Einstein-Klein-Gordon system can be divided into\n * three major sequential steps:\n *  - Integrate the metric hypersurface equations with the existing\n *    infrastructure\n *  - Integrate the KG equation for \\f$\\Pi\\f$\n *  - Evolve two variables, \\f$\\psi\\f$ (scalar) and \\f$J\\f$ (tensor) to the next\n *    time step\n *\n */\ntemplate <bool EvolveCcm>\nstruct KleinGordonSystem {\n  static constexpr size_t volume_dim = 3;\n  using variables_tag = tmpl::list<\n      ::Tags::Variables<tmpl::list<Tags::BondiJ, Tags::KleinGordonPsi>>,\n      ::Tags::Variables<tmpl::conditional_t<\n          EvolveCcm,\n          tmpl::list<Cce::Tags::CauchyCartesianCoords,\n                     Cce::Tags::PartiallyFlatCartesianCoords,\n                     Cce::Tags::InertialRetardedTime>,\n          tmpl::list<Cce::Tags::CauchyCartesianCoords,\n                     Cce::Tags::InertialRetardedTime>>>>;\n\n  static constexpr bool has_primitive_and_conservative_vars = false;\n};\n} // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/LinearOperators.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/LinearOperators.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Cce {\nvoid logical_partial_directional_derivative_of_complex(\n    const gsl::not_null<ComplexDataVector*> d_u, const ComplexDataVector& u,\n    const Mesh<3>& mesh, const size_t dimension_to_differentiate) {\n  const auto empty_matrix = Matrix{};\n  std::array<std::reference_wrapper<const Matrix>, 3> matrix_array{\n    {std::ref(empty_matrix), std::ref(empty_matrix), std::ref(empty_matrix)}};\n  gsl::at(matrix_array, dimension_to_differentiate) =\n      std::ref(Spectral::differentiation_matrix(\n          mesh.slice_through(dimension_to_differentiate)));\n  apply_matrices(d_u, matrix_array, u, mesh.extents());\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/LinearOperators.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\n/*!\n * \\brief Computes the partial derivative along a particular direction\n * determined by the `dimension_to_differentiate`.\n * The input `u` is differentiated with the spectral matrix and the solution is\n * placed in `d_u`.\n *\n * \\note This is placed in Cce Utilities for its currently narrow use-case. If\n * more general uses desire a single partial derivative of complex values, this\n * should be moved to `NumericalAlgorithms`. This utility currently assumes the\n * spatial dimensionality is 3, which would also need to be generalized, likely\n * by creating a wrapping struct with partial template specializations.\n */\nvoid logical_partial_directional_derivative_of_complex(\n    gsl::not_null<ComplexDataVector*> d_u, const ComplexDataVector& u,\n    const Mesh<3>& mesh, size_t dimension_to_differentiate);\n\nnamespace Tags {\n/*!\n * \\brief Compute tag for a manually-handled partial y derivative in the volume.\n *\n * \\details Most of the partial y derivatives are handled by PreSwshDerivatives.\n * However, that infrastructure is specific to the CCE evolution system itself,\n * so it won't work for other uses (e.g. observers that are invoked\n * periodically). This compute tag instead manually computes a partial\n * y derivative.\n */\ntemplate <typename Tag>\nstruct DyCompute\n    : Dy<Tag>, db::ComputeTag {\n  using base = Dy<Tag>;\n  using return_type = typename base::type;\n\n  using argument_tags = tmpl::list<Tag, Tags::LMax>;\n\n  static constexpr auto function(\n      const gsl::not_null<\n          Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>*>\n          dy_val,\n      const Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>& val,\n      const size_t l_max) {\n    if (get(*dy_val).size() != get(val).size()) {\n      get(*dy_val).destructive_resize(get(val).size());\n    };\n    logical_partial_directional_derivative_of_complex(\n        make_not_null(&get(*dy_val).data()), get(val).data(),\n        Mesh<3>{\n            {{Spectral::Swsh::number_of_swsh_theta_collocation_points(l_max),\n              Spectral::Swsh::number_of_swsh_phi_collocation_points(l_max),\n              get(val).size() /\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max)}},\n            Spectral::Basis::Legendre,\n            Spectral::Quadrature::GaussLobatto},\n        // 2 for differentiating in y; coordinate ordering is:\n        // {\\phi, \\theta, y}.\n        2);\n  }\n};\n\n}  // namespace Tags\n\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/LinearSolve.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/LinearSolve.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/IndefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/Lapack.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\nnamespace {\n// This builds up the spectral representation of the matrix associated with the\n// linear operator (1 - y) d_y f + 2 f.\n// Broadly, this is accomplished by manipulating both the right and left hand\n// sides of the Legendre identity:\n// (2n + 1) P_n = d_x (P_{n + 1} - P_{n - 1})\n// Writing this as operations on the modal coefficients,\n//  \\sum_n (A * m)_n P_n = \\sum_n (B * m)_n d_x P_n\n// So, in particular, we can take advantage of this to obtain a formula for the\n// coefficients of the integral given coefficients m_1 of the input:\n//   \\sum_n (m_1)_n P_n = \\sum_n (B * A^{-1} * m_1)_n d_x P_n,\n// Therefore the matrix we wish to act with is B * A^{-1}.\n// In this function we calculate the matrix M such that\n//   \\sum_n (m_2)_n P_n = \\sum_n (M * m_2) ((1 - x) d_x P_n  + 2 * P_n)\n// Using\n//   \\sum_n  m_n  (1 + x) P_n  = \\sum_n (C * m)_n P_n,\n// so\n//  \\sum_n (C * A * m)_n P_n = \\sum_n (B * m)_n ((1 - x) d_x P_n)\n// Adding \\sum_n (2* B * m_n P_n) to both sides,\n//   \\sum_n ((C * A + 2 B ) * m)_n P_n\n//        = \\sum_n (B * m)_n ((1 - x) d_x P_n + 2 * P_n)\n// Therefore, the matrix M = B * (C * A + 2 B)^{-1} .\nMatrix q_integration_matrix(const size_t number_of_points) {\n  Matrix inverse_one_minus_y = Matrix(number_of_points, number_of_points, 0.0);\n  for (size_t i = 1; i < number_of_points - 1; ++i) {\n    inverse_one_minus_y(i, i - 1) = i / -(2.0 * i - 1.0);\n    inverse_one_minus_y(i, i) = 1.0;\n    inverse_one_minus_y(i, i + 1) = (i + 1.0) / -(2.0 * i + 3.0);\n  }\n  inverse_one_minus_y(0, 0) = 1.0;\n  inverse_one_minus_y(0, 1) = -1.0 / 3.0;\n  inverse_one_minus_y(number_of_points - 1, number_of_points - 2) =\n      -(number_of_points - 1.0) / (2.0 * (number_of_points - 1.0) - 1.0);\n  inverse_one_minus_y(number_of_points - 1, number_of_points - 1) = 1.0;\n\n  Matrix indefinite_integral(number_of_points, number_of_points, 0.0);\n  for (size_t i = 1; i < number_of_points - 1; ++i) {\n    indefinite_integral(i, i - 1) = 1.0;\n    indefinite_integral(i, i + 1) = -1.0;\n  }\n  indefinite_integral(0, 1) = -1.0;\n  indefinite_integral(number_of_points - 1, number_of_points - 2) = 1.0;\n\n  Matrix dy_identity_lhs(number_of_points, number_of_points, 0.0);\n  for (size_t i = 0; i < number_of_points - 1; ++i) {\n    dy_identity_lhs(i, i) = 2.0 * i + 1.0;\n  }\n\n  Matrix lhs_mat = inverse_one_minus_y * dy_identity_lhs;\n\n  for (size_t i = 1; i < number_of_points - 1; ++i) {\n    lhs_mat(i, i - 1) += 2.0;\n    lhs_mat(i, i + 1) += -2.0;\n  }\n  lhs_mat(0, 1) += -2.0;\n  lhs_mat(number_of_points - 1, number_of_points - 2) += 2.0;\n\n  return Spectral::modal_to_nodal_matrix<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n             number_of_points) *\n         indefinite_integral * inv(lhs_mat) *\n         Spectral::nodal_to_modal_matrix<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n             number_of_points);\n}\n}  // namespace\n\nconst Matrix& precomputed_cce_q_integrator(\n    const size_t number_of_radial_grid_points) {\n  static const auto lazy_matrix_cache = make_static_cache<CacheRange<\n      1_st, Spectral::maximum_number_of_points<Spectral::Basis::Legendre> + 1>>(\n      [](const size_t local_number_of_radial_points) {\n        return q_integration_matrix(local_number_of_radial_points);\n      });\n  return lazy_matrix_cache(number_of_radial_grid_points);\n}\n\nvoid radial_integrate_cce_pole_equations(\n    const gsl::not_null<ComplexDataVector*> integral_result,\n    const ComplexDataVector& pole_of_integrand,\n    const ComplexDataVector& regular_integrand,\n    const ComplexDataVector& boundary, const ComplexDataVector& one_minus_y,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const ComplexDataVector integrand =\n      pole_of_integrand + one_minus_y * regular_integrand;\n\n  apply_matrices(integral_result,\n                 std::array<Matrix, 3>{\n                     {Matrix{}, Matrix{},\n                      precomputed_cce_q_integrator(number_of_radial_points)}},\n                 integrand,\n                 Spectral::Swsh::swsh_volume_mesh_for_radial_operations(\n                     l_max, number_of_radial_points)\n                     .extents());\n\n  // apply boundary condition\n  const ComplexDataVector boundary_correction =\n      0.25 * (boundary -\n              ComplexDataVector{\n                  integral_result->data(),\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n  const ComplexDataVector one_minus_y_squared = square(\n      1.0 -\n      std::complex<double>(1.0, 0.0) *\n          Spectral::collocation_points<Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::GaussLobatto>(\n              number_of_radial_points));\n  *integral_result += outer_product(boundary_correction, one_minus_y_squared);\n}\n\nnamespace detail {\nvoid transpose_to_reals_then_imags_radial_stripes(\n    const gsl::not_null<DataVector*> result, const ComplexDataVector& input,\n    const size_t number_of_radial_points,\n    const size_t number_of_angular_points) {\n  for (size_t i = 0; i < input.size() * 2; ++i) {\n    (*result)[i] = ((i / number_of_radial_points) % 2) == 0\n                       ? real(input[number_of_angular_points *\n                                        (i % number_of_radial_points) +\n                                    i / (2 * number_of_radial_points)])\n                       : imag(input[number_of_angular_points *\n                                        (i % number_of_radial_points) +\n                                    i / (2 * number_of_radial_points)]);\n  }\n}\n}  // namespace detail\n\n// generic template applies to `Tags::BondiBeta` and `Tags::BondiU`\ntemplate <template <typename> class BoundaryPrefix, typename Tag>\nvoid RadialIntegrateBondi<BoundaryPrefix, Tag>::apply(\n    const gsl::not_null<\n        Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>*>\n        integral_result,\n    const Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>&\n        integrand,\n    const Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>&\n        boundary,\n    const size_t l_max, const size_t number_of_radial_points) {\n  indefinite_integral(make_not_null(&get(*integral_result).data()),\n                      get(integrand).data(),\n                      Spectral::Swsh::swsh_volume_mesh_for_radial_operations(\n                          l_max, number_of_radial_points),\n                      2);\n  // add in the boundary data to each angular slice\n  for (size_t i = 0; i < number_of_radial_points; i++) {\n    ComplexDataVector angular_view{\n        get(*integral_result).data().data() +\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max) * i,\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    angular_view += get(boundary).data();\n  }\n}\n\ntemplate <template <typename> class BoundaryPrefix>\nvoid RadialIntegrateBondi<BoundaryPrefix, Tags::BondiQ>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n        integral_result,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& pole_of_integrand,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& regular_integrand,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& boundary,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n    const size_t l_max, const size_t number_of_radial_points) {\n  radial_integrate_cce_pole_equations(\n      make_not_null(&get(*integral_result).data()),\n      get(pole_of_integrand).data(), get(regular_integrand).data(),\n      get(boundary).data(), get(one_minus_y).data(), l_max,\n      number_of_radial_points);\n}\n\ntemplate <template <typename> class BoundaryPrefix>\nvoid RadialIntegrateBondi<BoundaryPrefix, Tags::BondiW>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        integral_result,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& pole_of_integrand,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& regular_integrand,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n    const size_t l_max, const size_t number_of_radial_points) {\n  radial_integrate_cce_pole_equations(\n      make_not_null(&get(*integral_result).data()),\n      get(pole_of_integrand).data(), get(regular_integrand).data(),\n      get(boundary).data(), get(one_minus_y).data(), l_max,\n      number_of_radial_points);\n}\n\ntemplate <template <typename> class BoundaryPrefix>\nvoid RadialIntegrateBondi<BoundaryPrefix, Tags::BondiH>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n        integral_result,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& pole_of_integrand,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& regular_integrand,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& linear_factor,\n    const Scalar<SpinWeighted<ComplexDataVector, 4>>&\n        linear_factor_of_conjugate,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Matrix operator_matrix(2 * number_of_radial_points,\n                         2 * number_of_radial_points);\n\n  ComplexDataVector integrand =\n      get(pole_of_integrand).data() +\n      get(one_minus_y).data() * get(regular_integrand).data();\n\n  DataVector transpose_buffer{2 * get(pole_of_integrand).size()};\n  DataVector linear_solve_buffer{2 * get(pole_of_integrand).size()};\n\n  // transpose such that each radial slice is split up into the order:\n  // (real radial slice 00) (imag radial slice 00) (real radial slice 01) ...\n  detail::transpose_to_reals_then_imags_radial_stripes(\n      make_not_null(&linear_solve_buffer), integrand, number_of_radial_points,\n      number_of_angular_points);\n\n  raw_transpose(make_not_null(transpose_buffer.data()),\n                linear_solve_buffer.data(), number_of_radial_points,\n                2 * number_of_angular_points);\n\n  const auto& derivative_matrix =\n      Spectral::differentiation_matrix<Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::GaussLobatto>(\n          number_of_radial_points);\n  for (size_t offset = 0; offset < number_of_angular_points; ++offset) {\n    // on repeated evaluations, the matrix gets permuted by the dgesv routine.\n    // We'll ignore its pivots and just overwrite the whole thing on each\n    // pass. There are probably optimizations that can be made which make use\n    // of the pivots.\n\n    // first we apply the (1 - y) \\partial_y part of the matrix\n    // to the upper right (real-real) and lower left (imag-imag) part of the\n    // matrix\n    for (size_t matrix_block = 0; matrix_block < 2; ++matrix_block) {\n      for (size_t i = 0; i < number_of_radial_points; ++i) {\n        for (size_t j = 0; j < number_of_radial_points; ++j) {\n          operator_matrix(i + matrix_block * number_of_radial_points,\n                          j + matrix_block * number_of_radial_points) =\n              derivative_matrix(i, j) *\n              real(get(one_minus_y).data()[i * number_of_angular_points]);\n        }\n      }\n    }\n\n    // zero out the lower left and upper right part of the matrix\n    for (size_t i = 0; i < number_of_radial_points; ++i) {\n      for (size_t j = 0; j < number_of_radial_points; ++j) {\n        operator_matrix(i + number_of_radial_points, j) = 0.0;\n        operator_matrix(i, j + number_of_radial_points) = 0.0;\n      }\n    }\n\n    // gather the contributions to the matrix blocks from the linear factors\n    // each, we zero the first row\n    for (size_t i = 0; i < number_of_radial_points; ++i) {\n      const size_t linear_factor_index = offset + i * number_of_angular_points;\n      // upper left\n      operator_matrix(i, i) +=\n          real(get(linear_factor).data()[linear_factor_index] +\n               get(linear_factor_of_conjugate).data()[linear_factor_index]);\n      operator_matrix(0, i) = 0.0;\n      // upper right\n      operator_matrix(i, number_of_radial_points + i) -=\n          imag(get(linear_factor).data()[linear_factor_index] -\n               get(linear_factor_of_conjugate).data()[linear_factor_index]);\n      operator_matrix(0, number_of_radial_points + i) = 0.0;\n      // lower left\n      operator_matrix(number_of_radial_points + i, i) +=\n          imag(get(linear_factor).data()[linear_factor_index] +\n               get(linear_factor_of_conjugate).data()[linear_factor_index]);\n      operator_matrix(number_of_radial_points, i) = 0.0;\n      // lower right\n      operator_matrix(number_of_radial_points + i,\n                      number_of_radial_points + i) +=\n          real(get(linear_factor).data()[linear_factor_index] -\n               get(linear_factor_of_conjugate).data()[linear_factor_index]);\n      operator_matrix(number_of_radial_points, number_of_radial_points + i) =\n          0.0;\n    }\n    operator_matrix(0, 0) = 1.0;\n    operator_matrix(number_of_radial_points, number_of_radial_points) = 1.0;\n    // put the data currently in integrand into a real DataVector of twice the\n    // length\n    linear_solve_buffer[offset * 2 * number_of_radial_points] =\n        real(get(boundary).data()[offset]);\n    linear_solve_buffer[(offset * 2 + 1) * number_of_radial_points] =\n        imag(get(boundary).data()[offset]);\n    DataVector linear_solve_buffer_view{\n        linear_solve_buffer.data() + offset * 2 * number_of_radial_points,\n        2 * number_of_radial_points};\n    lapack::general_matrix_linear_solve(\n        make_not_null(&linear_solve_buffer_view),\n        make_not_null(&operator_matrix));\n  }\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  raw_transpose(make_not_null(reinterpret_cast<double*>(\n                    get(*integral_result).data().data())),\n                linear_solve_buffer.data(), number_of_radial_points,\n                2 * number_of_angular_points);\n}\n\ntemplate struct RadialIntegrateBondi<Tags::BoundaryValue, Tags::BondiBeta>;\ntemplate struct RadialIntegrateBondi<Tags::BoundaryValue, Tags::BondiQ>;\ntemplate struct RadialIntegrateBondi<Tags::BoundaryValue, Tags::BondiU>;\ntemplate struct RadialIntegrateBondi<Tags::BoundaryValue, Tags::BondiW>;\ntemplate struct RadialIntegrateBondi<Tags::BoundaryValue, Tags::BondiH>;\ntemplate struct RadialIntegrateBondi<Tags::EvolutionGaugeBoundaryValue,\n                                     Tags::BondiBeta>;\ntemplate struct RadialIntegrateBondi<Tags::EvolutionGaugeBoundaryValue,\n                                     Tags::BondiQ>;\ntemplate struct RadialIntegrateBondi<Tags::EvolutionGaugeBoundaryValue,\n                                     Tags::BondiU>;\ntemplate struct RadialIntegrateBondi<Tags::EvolutionGaugeBoundaryValue,\n                                     Tags::BondiW>;\ntemplate struct RadialIntegrateBondi<Tags::EvolutionGaugeBoundaryValue,\n                                     Tags::BondiH>;\n\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/LinearSolve.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\nclass Matrix;\n/// \\endcond\n\nnamespace Cce {\n\n/*!\n * \\brief Provides access to a lazily cached integration matrix for the \\f$Q\\f$\n * and \\f$W\\f$ equations in CCE hypersurface evaluation.\n *\n * \\details The provided matrix acts on the integrand collocation points and\n * solves the equation,\n *\n * \\f[\n * (1 - y) \\partial_y f + 2 f = g,\n * \\f]\n *\n * for \\f$f\\f$ given integrand \\f$g\\f$.\n */\nconst Matrix& precomputed_cce_q_integrator(size_t number_of_radial_grid_points);\n\n/*!\n * \\brief A utility function for evaluating the \\f$Q\\f$ and \\f$W\\f$ hypersurface\n * integrals during CCE evolution.\n *\n * \\details Computes and returns by `not_null` pointer the solution to the\n * equation\n *\n * \\f[\n * (1 - y) \\partial_y f + 2 f = A + (1 - y) B,\n * \\f]\n *\n * where \\f$A\\f$ is provided as `pole_of_integrand` and \\f$B\\f$ is provided as\n * `regular_integrand`. The value `one_minus_y` is required for determining the\n * integrand and `l_max` is required to determine the shape of the spin-weighted\n * spherical harmonic mesh.\n */\nvoid radial_integrate_cce_pole_equations(\n    gsl::not_null<ComplexDataVector*> integral_result,\n    const ComplexDataVector& pole_of_integrand,\n    const ComplexDataVector& regular_integrand,\n    const ComplexDataVector& boundary, const ComplexDataVector& one_minus_y,\n    size_t l_max, size_t number_of_radial_points);\n\nnamespace detail {\n// needed because the standard transpose utility cannot create an arbitrary\n// ordering of blocks of data. This returns by pointer the configuration useful\n// for the linear solve step for H integration\nvoid transpose_to_reals_then_imags_radial_stripes(\n    gsl::not_null<DataVector*> result, const ComplexDataVector& input,\n    size_t number_of_radial_points, size_t number_of_angular_points);\n}  // namespace detail\n\n/// @{\n/*!\n * \\brief Computational structs for evaluating the hypersurface integrals during\n * CCE evolution. These are compatible with use in `db::mutate_apply`.\n *\n * \\details\n * The integral evaluated and the corresponding inputs required depend on the\n * CCE quantity being computed. In any of these, the only mutated tag is `Tag`,\n * where the result of the integration is placed. The supported `Tag`s act in\n * the following ways:\n * - If the `Tag` is `Tags::BondiBeta` or `Tags::BondiU`, the integral to be\n * evaluated is simply \\f[ \\partial_y f = A, \\f] where \\f$A\\f$ is retrieved with\n * `Tags::Integrand<Tag>`.\n * - If the `Tag` is `Tags::BondiQ` or `Tags::BondiW`, the integral to be\n * evaluated is \\f[ (1 - y) \\partial_y f + 2 f = A + (1 - y) B, \\f] where\n * \\f$A\\f$ is retrieved with `Tags::PoleOfIntegrand<Tag>` and \\f$B\\f$ is\n * retrieved with `Tags::RegularIntegrand<Tag>`.\n * - If `Tag` is `Tags::BondiH`, the integral to be evaluated is:\n *\n * \\f[\n * (1 - y) \\partial_y f + L f + L^\\prime \\bar{f} = A + (1 - y) B,\n * \\f]\n *\n * for \\f$f\\f$, where \\f$A\\f$ is retrieved with `Tags::PoleOfIntegrand<Tag>`,\n * \\f$B\\f$ is retrieved with `Tags::RegularIntegrand<Tag>`, \\f$L\\f$ is retrieved\n * with `Tags::LinearFactor<Tag>`, and \\f$L^\\prime\\f$ is retrieved with\n * `Tags::LinearFactorForConjugate<Tag>`. The presence of \\f$L\\f$ and\n * \\f$L^\\prime\\f$ ensure that the only current method we have for evaluating the\n * \\f$H\\f$ hypersurface equation is a direct linear solve, rather than the\n * spectral matrix multiplications which are available for the other integrals.\n *\n * In each case, the boundary value at the world tube for the integration is\n * retrieved from `BoundaryPrefix<Tag>`.\n *\n * Additional type aliases `boundary_tags` and `integrand_tags` are provided for\n * template processing of the required input tags necessary for these functions.\n * These type aliases are `tmpl::list`s with the subsets of `argument_tags` from\n * specific other parts of the CCE computation. Because they play different\n * roles, and have different extents, it is better for tag management to give\n * separated lists for the dependencies.\n */\ntemplate <template <typename> class BoundaryPrefix, typename Tag>\nstruct RadialIntegrateBondi {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tag>>;\n  using integrand_tags = tmpl::list<Tags::Integrand<Tag>>;\n\n  using return_tags = tmpl::list<Tag>;\n  using argument_tags =\n      tmpl::append<integrand_tags, boundary_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n  static void apply(\n      gsl::not_null<\n          Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>*>\n          integral_result,\n      const Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>&\n          integrand,\n      const Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>&\n          boundary,\n      size_t l_max, size_t number_of_radial_points);\n};\n\ntemplate <template <typename> class BoundaryPrefix>\nstruct RadialIntegrateBondi<BoundaryPrefix, Tags::BondiQ> {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tags::BondiQ>>;\n  using integrand_tags = tmpl::list<Tags::PoleOfIntegrand<Tags::BondiQ>,\n                                    Tags::RegularIntegrand<Tags::BondiQ>>;\n  using integration_independent_tags = tmpl::list<Tags::OneMinusY>;\n\n  using return_tags = tmpl::list<Tags::BondiQ>;\n  using argument_tags =\n      tmpl::append<integrand_tags, boundary_tags, integration_independent_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          integral_result,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& pole_of_integrand,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& regular_integrand,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& boundary,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      size_t l_max, size_t number_of_radial_points);\n};\n\ntemplate <template <typename> class BoundaryPrefix>\nstruct RadialIntegrateBondi<BoundaryPrefix, Tags::BondiW> {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tags::BondiW>>;\n  using integrand_tags = tmpl::list<Tags::PoleOfIntegrand<Tags::BondiW>,\n                                    Tags::RegularIntegrand<Tags::BondiW>>;\n  using integration_independent_tags = tmpl::list<Tags::OneMinusY>;\n\n  using return_tags = tmpl::list<Tags::BondiW>;\n  using argument_tags =\n      tmpl::append<integrand_tags, boundary_tags, integration_independent_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          integral_result,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& pole_of_integrand,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& regular_integrand,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      size_t l_max, size_t number_of_radial_points);\n};\n\ntemplate <template <typename> class BoundaryPrefix>\nstruct RadialIntegrateBondi<BoundaryPrefix, Tags::BondiH> {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tags::BondiH>>;\n  using integrand_tags =\n      tmpl::list<Tags::PoleOfIntegrand<Tags::BondiH>,\n                 Tags::RegularIntegrand<Tags::BondiH>,\n                 Tags::LinearFactor<Tags::BondiH>,\n                 Tags::LinearFactorForConjugate<Tags::BondiH>>;\n  using integration_independent_tags = tmpl::list<Tags::OneMinusY>;\n\n  using return_tags = tmpl::list<Tags::BondiH>;\n  using argument_tags =\n      tmpl::append<integrand_tags, boundary_tags, integration_independent_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          integral_result,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& pole_of_integrand,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& regular_integrand,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& linear_factor,\n      const Scalar<SpinWeighted<ComplexDataVector, 4>>&\n          linear_factor_of_conjugate,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      size_t l_max, size_t number_of_radial_points);\n};\n/// @}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/NewmanPenrose.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/NewmanPenrose.hpp\"\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace {\nvoid newman_penrose_alpha_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, -1>*> np_alpha,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 3>& eth_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_q,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  // Intentionally using expressions with auto. This code is only used for\n  // observing, and should be profiled if used more often\n  const auto one_plus_k = 1. + bondi_k;\n  const auto sqrt_one_plus_k = sqrt(one_plus_k);\n  const auto q_plus_two_eth_beta = bondi_q + 2. * eth_beta;\n\n  *np_alpha =\n      one_minus_y / (32. * bondi_r)\n      * (\n          1. / sqrt_one_plus_k *\n          (\n            (square(conj(bondi_j)) * eth_j) / (bondi_k * one_plus_k)\n            +\n            1. / bondi_k *\n            ( bondi_j * conj(eth_j) + conj(bondi_j) * ethbar_j\n              - conj(ethbar_j))\n            + ( 2. * conj(bondi_j) * q_plus_two_eth_beta\n                - 3. * conj(ethbar_j) )\n          )\n          - 2. * sqrt_one_plus_k * conj(q_plus_two_eth_beta)\n        );\n}\n\nvoid newman_penrose_beta_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, +1>*> np_beta,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 3>& eth_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_q,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  // Intentionally using expressions with auto. This code is only used for\n  // observing, and should be profiled if used more often\n  const auto one_plus_k = 1. + bondi_k;\n  const auto sqrt_one_plus_k = sqrt(one_plus_k);\n  const auto q_plus_two_eth_beta = bondi_q + 2. * eth_beta;\n\n  *np_beta =\n      one_minus_y / (32. * bondi_r)\n      * (\n          1. / sqrt_one_plus_k *\n          (\n            ( - square(bondi_j) * conj(eth_j) / (bondi_k * one_plus_k)\n              +\n              1. / bondi_k *\n              ( - bondi_j * conj(ethbar_j) - conj(bondi_j) * eth_j\n                + ethbar_j)\n              + ( 2. * bondi_j * conj(q_plus_two_eth_beta)\n                  - 3. * ethbar_j )\n            )\n          )\n          - 2. * sqrt_one_plus_k * q_plus_two_eth_beta\n       );\n}\n\nvoid newman_penrose_gamma_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> np_gamma,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 3>& eth_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_h,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_u,\n    const SpinWeighted<ComplexDataVector, 2>& eth_u,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_w,\n    const SpinWeighted<ComplexDataVector, 0>& dy_w,\n    const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  // Intentionally using expressions with auto. This code is only used for\n  // observing, and should be profiled if used more often\n  const auto one_plus_k = 1. + bondi_k;\n\n  *np_gamma =\n    1. / ( sqrt(32.) * exp_2_beta )\n      * ( 0.5 / one_plus_k\n          * ( one_minus_y * ( 0.5 * one_minus_y / bondi_r + bondi_w )\n              * ( conj(bondi_j) * dy_j - bondi_j * conj(dy_j) )\n              + ( 2. * conj(bondi_h) * bondi_j - 2. * bondi_h * conj(bondi_j)\n                  + bondi_u * ( bondi_j * conj(eth_j)\n                                - conj(bondi_j) * ethbar_j)\n                  + conj(bondi_u) * ( bondi_j * conj(ethbar_j)\n                                      - conj(bondi_j) * eth_j )\n                )\n             )\n          + 2. * one_minus_y * dy_w\n          + ( 2. * bondi_w + bondi_j * conj(eth_u) - conj(bondi_j) * eth_u\n              + bondi_k * ( ethbar_u - conj(ethbar_u) ) )\n    );\n}\n\nvoid newman_penrose_epsilon_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> np_epsilon,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 0>& dy_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  *np_epsilon =\n      square(one_minus_y) / ( sqrt(8.) * bondi_r)\n      * ( dy_beta +\n          ( bondi_j * conj(dy_j) - conj(bondi_j) * dy_j )\n          * 0.125 / (1. + bondi_k)\n      );\n}\n\nvoid newman_penrose_tau_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, +1>*> np_tau,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_q,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  // Intentionally using expressions with auto. This code is only used for\n  // observing, and should be profiled if used more often\n  const auto one_plus_k = 1. + bondi_k;\n  const auto sqrt_one_plus_k = sqrt(one_plus_k);\n  const auto two_eth_beta_minus_q = 2. * eth_beta - bondi_q;\n\n  *np_tau =\n      0.125 * one_minus_y / bondi_r *\n      ( sqrt_one_plus_k * two_eth_beta_minus_q\n        - bondi_j * conj(two_eth_beta_minus_q) / sqrt_one_plus_k\n      );\n}\n\nvoid newman_penrose_sigma_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, +2>*> np_sigma,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  // Intentionally using expressions with auto. This code is only used for\n  // observing, and should be profiled if used more often\n  const auto one_plus_k = 1. + bondi_k;\n\n  *np_sigma =\n      square(one_minus_y) / ( sqrt(128.) * bondi_k * bondi_r ) *\n      ( square(bondi_j) * conj(dy_j) / one_plus_k\n        - one_plus_k * dy_j\n      );\n}\n\nvoid newman_penrose_rho_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> np_rho,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  *np_rho = - one_minus_y / ( sqrt(8.) * bondi_r );\n}\n\nvoid newman_penrose_pi_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, -1>*> np_pi,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_q,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  // Intentionally using expressions with auto. This code is only used for\n  // observing, and should be profiled if used more often\n  const auto one_plus_k = 1. + bondi_k;\n  const auto sqrt_one_plus_k = sqrt(one_plus_k);\n  const auto q_plus_two_eth_beta = bondi_q + 2. * eth_beta;\n\n  *np_pi =\n      0.125 * one_minus_y / bondi_r *\n      ( conj(bondi_j) * q_plus_two_eth_beta / sqrt_one_plus_k\n        - sqrt_one_plus_k * conj(q_plus_two_eth_beta)\n      );\n}\n\nvoid newman_penrose_nu_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, -1>*> np_nu,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 1>& eth_w,\n    const SpinWeighted<ComplexDataVector, 0>& exp_2_beta) {\n\n  // Intentionally using expressions with auto. This code is only used for\n  // observing, and should be profiled if used more often\n  const auto one_plus_k = 1. + bondi_k;\n  const auto sqrt_one_plus_k = sqrt(one_plus_k);\n\n  *np_nu = 0.5 / exp_2_beta *\n           ( conj(bondi_j) * eth_w / sqrt_one_plus_k\n             - sqrt_one_plus_k * conj(eth_w) );\n}\n\nvoid newman_penrose_mu_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> np_mu,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_w,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n    const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  *np_mu = 1. / ( sqrt(8.) * exp_2_beta ) *\n           ( conj(ethbar_u) + ethbar_u - one_minus_y / bondi_r - 2. * bondi_w );\n}\n\nvoid newman_penrose_lambda_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, -2>*> np_lambda,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 3>& eth_j,\n    const SpinWeighted<ComplexDataVector, 1>& ethbar_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_h,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_u,\n    const SpinWeighted<ComplexDataVector, 2>& eth_u,\n    const SpinWeighted<ComplexDataVector, 0>& ethbar_u,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_w,\n    const SpinWeighted<ComplexDataVector, 0>& exp_2_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  // Intentionally using expressions with auto. This code is only used for\n  // observing, and should be profiled if used more often\n  const auto one_plus_k = 1. + bondi_k;\n\n  const auto inner1 =\n      0.5 * one_minus_y / one_plus_k *\n      ( ( square(conj(bondi_j)) * dy_j - conj(dy_j) ) / bondi_k\n        - (2. + bondi_k) * conj(dy_j) );\n\n  const auto inner2 = 2. * bondi_h + bondi_u * ethbar_j + conj(bondi_u) * eth_j;\n\n  *np_lambda =\n      1. / ( sqrt(32.) * exp_2_beta ) *\n      ( ( one_minus_y / bondi_r + 2. * bondi_w ) * inner1\n        + 2. * one_plus_k * conj(eth_u)\n        + ( conj(inner2) + 2. * conj(bondi_j) * ( ethbar_u - conj(ethbar_u) ) )\n        + conj(inner2) / bondi_k\n        - square(conj(bondi_j)) * ( inner2 + 2. * bondi_k * eth_u ) /\n          ( bondi_k * one_plus_k ) );\n}\n\nvoid weyl_psi0_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> psi_0,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 2>& dy_dy_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n  *psi_0 = pow<4>(one_minus_y) * 0.0625 / (square(bondi_r) * bondi_k) *\n           (0.125 * one_minus_y *\n                (dy_j * conj(dy_j) -\n                 0.25 * square(bondi_j * conj(dy_j) + conj(bondi_j) * dy_j) /\n                     square(bondi_k)) *\n                ((1.0 + bondi_k) * dy_j -\n                 square(bondi_j) * conj(dy_j) / (1.0 + bondi_k)) -\n            0.5 * (1.0 + bondi_k) * dy_dy_j +\n            0.5 * square(bondi_j) * conj(dy_dy_j) / (1.0 + bondi_k) +\n            (-0.25 * bondi_j *\n                 (square(conj(bondi_j)) * square(dy_j) +\n                  square(bondi_j) * square(conj(dy_j))) +\n             0.5 * bondi_j * (1.0 + square(bondi_k)) * dy_j * conj(dy_j)) /\n                square(bondi_k));\n}\n\nvoid weyl_psi1_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 1>*> psi_1,\n    const SpinWeighted<ComplexDataVector, 2>& bondi_j,\n    const SpinWeighted<ComplexDataVector, 2>& dy_j,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_k,\n    const SpinWeighted<ComplexDataVector, 1>& bondi_q,\n    const SpinWeighted<ComplexDataVector, 1>& dy_q,\n    const SpinWeighted<ComplexDataVector, 0>& bondi_r,\n    const SpinWeighted<ComplexDataVector, 1>& eth_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& dy_beta,\n    const SpinWeighted<ComplexDataVector, 1>& eth_beta,\n    const SpinWeighted<ComplexDataVector, 1>& eth_dy_beta,\n    const SpinWeighted<ComplexDataVector, 0>& one_minus_y) {\n\n  const double prefac = 1./sqrt(128.); // compile time const, but\n                                       // sqrt is not constexpr yet\n  const auto one_plus_k = 1. + bondi_k;\n  const auto eth_beta_plus_half_q = eth_beta + 0.5 * bondi_q;\n  const auto conj_j_times_dy_j = conj(bondi_j) * dy_j;\n\n  const auto inner_expr =\n    bondi_j\n    * (-2. * conj(dy_q)\n       + conj(dy_j) * (2. * eth_beta_plus_half_q\n                       + bondi_j * conj(eth_beta_plus_half_q)))\n    + one_plus_k\n    * (eth_beta_plus_half_q * (conj_j_times_dy_j\n                               - conj(conj_j_times_dy_j))\n       + 2. * (dy_q + bondi_j * conj(dy_q))\n       - one_plus_k * (2. * dy_q + dy_j * conj(eth_beta_plus_half_q)));\n\n  *psi_1 = prefac * square(one_minus_y) / (square(bondi_r) * sqrt(one_plus_k))\n    * (bondi_j * conj(eth_beta_plus_half_q)\n       - one_plus_k * eth_beta_plus_half_q\n       + one_minus_y\n       * (eth_dy_beta * one_plus_k\n          - bondi_j * conj(eth_dy_beta)\n          + dy_beta * (one_plus_k * eth_r_divided_by_r\n                       - bondi_j * conj(eth_r_divided_by_r))\n          + 0.25 * inner_expr / bondi_k));\n}\n\nvoid weyl_psi2_impl(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> psi_2,\n    const SpinWeighted<ComplexDataVector, +2>& bondi_j,\n    const SpinWeighted<ComplexDataVector,  0>& bondi_k,\n    const SpinWeighted<ComplexDataVector,  0>& bondi_r,\n    const SpinWeighted<ComplexDataVector,  0>& dy_mu,\n    const SpinWeighted<ComplexDataVector,  0>& eth_pi,\n    const SpinWeighted<ComplexDataVector, -2>& ethbar_pi,\n    const SpinWeighted<ComplexDataVector, -1>& np_alpha,\n    const SpinWeighted<ComplexDataVector, +1>& np_beta,\n    const SpinWeighted<ComplexDataVector,  0>& np_epsilon,\n    const SpinWeighted<ComplexDataVector, +2>& np_sigma,\n    const SpinWeighted<ComplexDataVector,  0>& np_rho,\n    const SpinWeighted<ComplexDataVector, -1>& np_pi,\n    const SpinWeighted<ComplexDataVector,  0>& np_mu,\n    const SpinWeighted<ComplexDataVector, -2>& np_lambda,\n    const SpinWeighted<ComplexDataVector,  0>& one_minus_y) {\n\n  const auto sqrt_one_plus_k = sqrt(1. + bondi_k);\n\n  *psi_2 = 0.25 * one_minus_y / bondi_r *\n           ( sqrt(2.) * one_minus_y * dy_mu\n             + sqrt_one_plus_k * eth_pi\n             - bondi_j * ethbar_pi / sqrt_one_plus_k )\n           + ( np_epsilon + conj(np_epsilon) - conj(np_rho) ) * np_mu\n           + ( conj(np_alpha) - np_beta - conj(np_pi) ) * np_pi\n           - np_sigma * np_lambda;\n}\n}  // namespace\n\n\nvoid newman_penrose_alpha(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> np_alpha,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 3>>& eth_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_alpha_impl(\n      make_not_null(&get(*np_alpha)), get(bondi_j), get(eth_j),\n      get(ethbar_j), get(bondi_k), get(bondi_r), get(bondi_q),\n      get(eth_beta), get(one_minus_y));\n}\n\nvoid newman_penrose_beta(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +1>>*> np_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 3>>& eth_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_beta_impl(\n      make_not_null(&get(*np_beta)), get(bondi_j), get(eth_j),\n      get(ethbar_j), get(bondi_k), get(bondi_r), get(bondi_q),\n      get(eth_beta), get(one_minus_y));\n}\n\nvoid newman_penrose_gamma(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> np_gamma,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 3>>& eth_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_h,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_gamma_impl(\n      make_not_null(&get(*np_gamma)), get(bondi_j), get(dy_j),\n      get(eth_j), get(ethbar_j), get(bondi_k), get(bondi_h),\n      get(bondi_r), get(bondi_u), get(eth_u), get(ethbar_u),\n      get(bondi_w), get(dy_w), get(exp_2_beta), get(one_minus_y));\n}\n\nvoid newman_penrose_epsilon(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> np_epsilon,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_epsilon_impl(\n      make_not_null(&get(*np_epsilon)), get(bondi_j), get(dy_j),\n      get(bondi_k), get(bondi_r), get(dy_beta), get(one_minus_y));\n}\n\n// There is no newmpan_penrose_kappa because in our conventions, it's 0.\n\nvoid newman_penrose_tau(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +1>>*> np_tau,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_tau_impl(\n      make_not_null(&get(*np_tau)), get(bondi_j), get(bondi_k),\n      get(bondi_r), get(bondi_q), get(eth_beta), get(one_minus_y));\n}\n\nvoid newman_penrose_sigma(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +2>>*> np_sigma,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_sigma_impl(\n      make_not_null(&get(*np_sigma)), get(bondi_j), get(dy_j),\n      get(bondi_k), get(bondi_r), get(one_minus_y));\n}\n\nvoid newman_penrose_rho(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> np_rho,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_rho_impl(\n      make_not_null(&get(*np_rho)), get(bondi_r), get(one_minus_y));\n}\n\nvoid newman_penrose_pi(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> np_pi,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_pi_impl(\n      make_not_null(&get(*np_pi)), get(bondi_j), get(bondi_k),\n      get(bondi_r), get(bondi_q), get(eth_beta), get(one_minus_y));\n}\n\nvoid newman_penrose_nu(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> np_nu,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta) {\n  newman_penrose_nu_impl(\n      make_not_null(&get(*np_nu)), get(bondi_j), get(bondi_k),\n      get(eth_w), get(exp_2_beta));\n}\n\nvoid newman_penrose_mu(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> np_mu,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_mu_impl(\n      make_not_null(&get(*np_mu)), get(bondi_r), get(bondi_w),\n      get(ethbar_u), get(exp_2_beta), get(one_minus_y));\n}\n\n\nvoid newman_penrose_lambda(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> np_lambda,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 3>>& eth_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_h,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  newman_penrose_lambda_impl(\n      make_not_null(&get(*np_lambda)), get(bondi_j), get(dy_j),\n      get(eth_j), get(ethbar_j), get(bondi_k), get(bondi_h),\n      get(bondi_r), get(bondi_u), get(eth_u), get(ethbar_u),\n      get(bondi_w), get(exp_2_beta), get(one_minus_y));\n}\n\nvoid VolumeWeyl<Tags::Psi0>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> psi_0,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  weyl_psi0_impl(make_not_null(&get(*psi_0)), get(bondi_j), get(dy_j),\n                 get(dy_dy_j), get(bondi_k), get(bondi_r), get(one_minus_y));\n}\n\nvoid VolumeWeyl<Tags::Psi1>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> psi_1,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_dy_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n  weyl_psi1_impl(make_not_null(&get(*psi_1)), get(bondi_j), get(dy_j),\n                 get(bondi_k), get(bondi_q), get(dy_q),\n                 get(bondi_r), get(eth_r_divided_by_r), get(dy_beta),\n                 get(eth_beta), get(eth_dy_beta), get(one_minus_y));\n}\n\nvoid VolumeWeyl<Tags::Psi2>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> psi_2,\n    const Scalar<SpinWeighted<ComplexDataVector, +2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector,  0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector,  0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector,  0>>& dy_mu,\n    const Scalar<SpinWeighted<ComplexDataVector,  0>>& eth_pi,\n    const Scalar<SpinWeighted<ComplexDataVector, -2>>& ethbar_pi,\n    const Scalar<SpinWeighted<ComplexDataVector, -1>>& np_alpha,\n    const Scalar<SpinWeighted<ComplexDataVector, +1>>& np_beta,\n    const Scalar<SpinWeighted<ComplexDataVector,  0>>& np_epsilon,\n    const Scalar<SpinWeighted<ComplexDataVector, +2>>& np_sigma,\n    const Scalar<SpinWeighted<ComplexDataVector,  0>>& np_rho,\n    const Scalar<SpinWeighted<ComplexDataVector, -1>>& np_pi,\n    const Scalar<SpinWeighted<ComplexDataVector,  0>>& np_mu,\n    const Scalar<SpinWeighted<ComplexDataVector, -2>>& np_lambda,\n    const Scalar<SpinWeighted<ComplexDataVector,  0>>& one_minus_y) {\n  weyl_psi2_impl(make_not_null(&get(*psi_2)), get(bondi_j), get(bondi_k),\n                 get(bondi_r), get(dy_mu), get(eth_pi), get(ethbar_pi),\n                 get(np_alpha), get(np_beta), get(np_epsilon), get(np_sigma),\n                 get(np_rho), get(np_pi), get(np_mu), get(np_lambda),\n                 get(one_minus_y));\n}\n\nvoid TransformBondiJToCauchyCoords::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n        cauchy_view_volume_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_cauchy_c,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_cauchy_d,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega_cauchy,\n    const Spectral::Swsh::SwshInterpolator& interpolator,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_radial_points =\n      get(volume_j).size() / number_of_angular_points;\n\n  SpinWeighted<ComplexDataVector, 2> target_angular_view;\n  const SpinWeighted<ComplexDataVector, 2> source_angular_view;\n  // Iterate for each spherical shell\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    target_angular_view.set_data_ref(\n        get(*cauchy_view_volume_j).data().data() +\n            i * Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n    make_const_view(make_not_null(&source_angular_view), get(volume_j),\n                    i * number_of_angular_points, number_of_angular_points);\n    interpolator.interpolate(make_not_null(&target_angular_view),\n                             source_angular_view);\n    target_angular_view.data() =\n        target_angular_view.data() * conj(square(get(gauge_cauchy_d).data())) +\n        conj(target_angular_view.data()) * square(get(gauge_cauchy_c).data()) +\n        2.0 * get(gauge_cauchy_c).data() * conj(get(gauge_cauchy_d).data()) *\n            sqrt(1.0 +\n                 target_angular_view.data() * conj(target_angular_view.data()));\n    target_angular_view.data() *= 0.25 / square(get(omega_cauchy).data());\n  }\n}\n\nvoid VolumeWeyl<Tags::Psi0Match>::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> psi_0,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j_cauchy,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j_cauchy,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_dy_j_cauchy,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r_cauchy,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_radial_points =\n      get(bondi_j_cauchy).size() / number_of_angular_points;\n\n  // Get bondi_k in the Cauchy coordinates\n  SpinWeighted<ComplexDataVector, 0> bondi_k_cauchy;\n  bondi_k_cauchy.data() =\n      sqrt(1.0 + get(bondi_j_cauchy).data() * conj(get(bondi_j_cauchy).data()));\n\n  const SpinWeighted<ComplexDataVector, 2> bondi_j_cauchy_view;\n  const SpinWeighted<ComplexDataVector, 2> dy_j_cauchy_view;\n  const SpinWeighted<ComplexDataVector, 2> dy_dy_j_cauchy_view;\n  const SpinWeighted<ComplexDataVector, 0> bondi_k_cauchy_view;\n  const SpinWeighted<ComplexDataVector, 0> one_minus_y_view;\n\n  SpinWeighted<ComplexDataVector, 2> psi0_view;\n\n  // Iterate for each spherical shell\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    // Note that bondi_r_cauchy, bondi_j_cauchy, dy_j_cauchy, dy_dy_j_cauchy,\n    // one_minus_y and bondi_k_cauchy are available only as surface quantities\n    make_const_view(make_not_null(&bondi_j_cauchy_view), get(bondi_j_cauchy),\n                    i * number_of_angular_points, number_of_angular_points);\n    make_const_view(make_not_null(&dy_j_cauchy_view), get(dy_j_cauchy),\n                    i * number_of_angular_points, number_of_angular_points);\n    make_const_view(make_not_null(&dy_dy_j_cauchy_view), get(dy_dy_j_cauchy),\n                    i * number_of_angular_points, number_of_angular_points);\n    make_const_view(make_not_null(&bondi_k_cauchy_view), bondi_k_cauchy,\n                    i * number_of_angular_points, number_of_angular_points);\n    make_const_view(make_not_null(&one_minus_y_view), get(one_minus_y),\n                    i * number_of_angular_points, number_of_angular_points);\n\n    psi0_view.set_data_ref(\n        get(*psi_0).data().data() +\n            i * Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n    weyl_psi0_impl(make_not_null(&psi0_view), bondi_j_cauchy_view,\n                   dy_j_cauchy_view, dy_dy_j_cauchy_view, bondi_k_cauchy_view,\n                   get(bondi_r_cauchy), one_minus_y_view);\n  }\n}\n\nvoid InnerBoundaryWeyl::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> psi_0_boundary,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n        dlambda_psi_0_boundary,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& psi_0,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_psi_0,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r_cauchy,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_beta_cauchy,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 0> one_minus_y_boundary;\n  const SpinWeighted<ComplexDataVector, 0> bondi_beta_cauchy_boundary;\n  const SpinWeighted<ComplexDataVector, 2> psi_0_boundary_view;\n  const SpinWeighted<ComplexDataVector, 2> dy_psi_0_boundary_view;\n\n  // Take the boundary data\n  make_const_view(make_not_null(&psi_0_boundary_view), get(psi_0), 0,\n                  number_of_angular_points);\n  make_const_view(make_not_null(&dy_psi_0_boundary_view), get(dy_psi_0), 0,\n                  number_of_angular_points);\n  make_const_view(make_not_null(&one_minus_y_boundary), get(one_minus_y), 0,\n                  number_of_angular_points);\n  make_const_view(make_not_null(&bondi_beta_cauchy_boundary),\n                  get(bondi_beta_cauchy), 0, number_of_angular_points);\n\n  get(*psi_0_boundary) = psi_0_boundary_view;\n  get(*dlambda_psi_0_boundary) = dy_psi_0_boundary_view.data() *\n                              square(one_minus_y_boundary.data()) /\n                              (2.0 * get(bondi_r_cauchy).data()) *\n                              exp(-2.0 * bondi_beta_cauchy_boundary.data());\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/NewmanPenrose.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\n\n/// \\cond\ntemplate <typename Tag>\nstruct VolumeWeyl;\n/// \\endcond\n\n/*!\n * \\brief Compute the (adapted) Newman-Penrose spin coefficient\n * $\\alpha^{SW}$ in the volume, in the conventions of \\cite Moxon2020gha .\n *\n * \\details The definition of $\\alpha$ is:\n *\n * \\begin{align}\n * \\alpha = \\frac{1}{2}\\left( \\bar{m}^\\mu \\bar{m}^\\nu \\nabla_\\nu m_\\mu\n *   - n^\\mu \\bar{m}^\\nu \\nabla_\\nu l_\\mu \\right) .\n * \\end{align}\n *\n * It does not have a well-defined spin weight; but by using a reference\n * spin-connection, the adapted spin coefficient $\\alpha^{SW} \\equiv \\alpha -\n * \\alpha_{ref}$ does have a well-defined spin weight of -1. In the language\n * of \\cite Moxon2020gha, that is equivalent to setting $\\Theta=0$. This\n * gives the expression\n *\n * \\begin{align}\n *   \\alpha^{SW} = \\frac{1-y}{32R} \\Bigg\\{\n *     & \\frac{1}{\\sqrt{1+K}} \\Bigg[\n *       \\frac{\\bar{J}^2\\eth J}{K(1+K)}\n *       +\\frac{\\bar\\eth(J\\bar{J}) - \\eth\\bar{J}}{K} \\\\ \\nonumber\n *      & {}+ \\left( 2\\bar{J}(Q+2\\eth\\beta)-3\\eth\\bar{J}\\right)\n *     \\Bigg]\n *     -2\\sqrt{1+K}\\left(\\overline{Q+2\\eth\\beta}\\right)\n *   \\Bigg\\}\n * \\end{align}\n */\nvoid newman_penrose_alpha(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> np_alpha,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 3>>& eth_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n/*!\n * \\brief Compute the (adapted) Newman-Penrose spin coefficient\n * $\\beta_{NP}^{SW}$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\beta_{NP}$ is:\n *\n * \\begin{align}\n * \\beta_{NP} = \\frac{1}{2}\\left( \\bar{m}^\\mu m^\\nu \\nabla_\\nu m_\\mu\n *   - n^\\mu m^\\nu \\nabla_\\nu l_\\mu \\right) .\n * \\end{align}\n *\n * It does not have a well-defined spin weight; but by using a reference\n * spin-connection, the adapted spin coefficient $\\beta_{NP}^{SW} \\equiv\n * \\beta_{NP} - \\beta_{NP,ref}$ does have a well-defined spin weight of +1. In\n * the language of \\cite Moxon2020gha, that is equivalent to setting\n * $\\Theta=0$. This gives the expression\n *\n * \\begin{align}\n *   \\beta_{NP}^{SW} = \\frac{1-y}{32R} \\Bigg\\{\n *     & \\frac{1}{\\sqrt{1+K}} \\Bigg[\n *       -\\frac{J^2\\bar\\eth\\bar J}{K(1+K)}\n *       +\\frac{-\\eth(J\\bar{J}) + \\bar\\eth J}{K} \\\\ \\nonumber\n *      & {}+ \\left( 2J(\\overline{Q+2\\eth\\beta})+3\\bar\\eth J\\right)\n *     \\Bigg]\n *     -2\\sqrt{1+K}\\left(Q+2\\eth\\beta\\right)\n *   \\Bigg\\}\n * \\end{align}\n */\nvoid newman_penrose_beta(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +1>>*> np_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 3>>& eth_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n/*!\n * \\brief Compute the (adapted) Newman-Penrose spin coefficient\n * $\\gamma^{SW}$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\gamma$ is:\n *\n * \\begin{align}\n * \\gamma = \\frac{1}{2}\\left( \\bar{m}^\\mu n^\\nu \\nabla_\\nu m_\\mu\n *   - n^\\mu n^\\nu \\nabla_\\nu l_\\mu \\right) .\n * \\end{align}\n *\n * It does not have a well-defined spin weight; but by using a reference\n * spin-connection, the adapted spin coefficient $\\gamma^{SW} \\equiv\n * \\gamma - \\gamma_{ref}$ does have a well-defined spin weight of 0. In\n * the language of \\cite Moxon2020gha, that is equivalent to setting\n * $\\Theta=0$. This gives the expression\n *\n * \\begin{align}\n *   \\gamma^{SW} = \\frac{e^{-2\\beta}}{4\\sqrt{2}} \\Bigg\\{\n *        & \\frac{1}{2(1+K)} \\Bigg[\n *          (1-y)\\left(\\frac{1-y}{2R}+W\\right) (\\bar{J}\\partial_y J\n *                     - J\\partial_y\\bar{J}) \\\\ \\nonumber\n *       & + \\left( 2\\bar{H} J - 2H\\bar{J} + U J \\overline{\\eth J}\n *                  - U \\bar{J} \\bar{\\eth}J\n *        + \\bar{U} J \\eth \\bar{J} - \\bar{U} \\bar{J}\\eth J \\right)\n *        \\Bigg] \\\\ \\nonumber\n *      & + 2(1-y) \\partial_y W \\\\ \\nonumber\n *      & + \\left(2W+J\\overline{\\eth U}-\\bar{J}\\eth U\n *                -K\\eth\\bar{U}+K\\bar{\\eth}U\\right)\n *      \\Bigg\\}\n * \\end{align}\n */\nvoid newman_penrose_gamma(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> np_gamma,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 3>>& eth_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_h,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n/*!\n * \\brief Compute the (adapted) Newman-Penrose spin coefficient\n * $\\epsilon^{SW}$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\epsilon$ is:\n *\n * \\begin{align}\n * \\epsilon = \\frac{1}{2}\\left( \\bar{m}^\\mu l^\\nu \\nabla_\\nu m_\\mu\n *   - n^\\mu l^\\nu \\nabla_\\nu l_\\mu \\right) .\n * \\end{align}\n *\n * It does not have a well-defined spin weight; but by using a reference\n * spin-connection, the adapted spin coefficient $\\epsilon^{SW} \\equiv\n * \\epsilon - \\epsilon_{ref}$ does have a well-defined spin weight of 0. In\n * the language of \\cite Moxon2020gha, that is equivalent to setting\n * $\\Theta=0$. This gives the expression\n *\n * \\begin{align}\n *   \\epsilon^{SW} = \\frac{(1-y)^2}{2\\sqrt{2} R} \\left(\n *     \\partial_y \\beta + \\frac{J\\partial_y \\bar{J}\n *         - \\bar{J}\\partial_y J}{8(1+K)}\n *     \\right)\n * \\end{align}\n */\nvoid newman_penrose_epsilon(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> np_epsilon,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n// We do not define newman_penrose_kappa since in our choice of tetrad, it's 0\n\n/*!\n * \\brief Compute the Newman-Penrose spin coefficient\n * $\\tau$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\tau$ is:\n *\n * \\begin{align}\n * \\tau = - m^\\mu n^\\nu \\nabla_\\nu l_\\mu .\n * \\end{align}\n *\n * It has a spin weight of +1. This gives the expression\n *\n * \\begin{align}\n *   \\tau = \\frac{1-y}{8 R} \\left(\n *     \\sqrt{1+K} (2\\eth\\beta - Q)\n *     - \\frac{J (\\overline{2\\eth\\beta-Q})}{\\sqrt{1+K}}\n *     \\right)\n * \\end{align}\n */\nvoid newman_penrose_tau(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +1>>*> np_tau,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n/*!\n * \\brief Compute the Newman-Penrose spin coefficient\n * $\\sigma$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\sigma$ is:\n *\n * \\begin{align}\n * \\sigma = - m^\\mu m^\\nu \\nabla_\\nu l_\\mu .\n * \\end{align}\n *\n * It has a spin weight of +2. This gives the expression\n *\n * \\begin{align}\n *   \\sigma = \\frac{(1-y)^2}{8 \\sqrt{2} K R} \\left(\n *     \\frac{J^2 \\partial_y \\bar{J}}{1+K}\n *     - (1+K)\\partial_y J\n *     \\right)\n * \\end{align}\n */\nvoid newman_penrose_sigma(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +2>>*> np_sigma,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n/*!\n * \\brief Compute the Newman-Penrose spin coefficient\n * $\\rho$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\rho$ is:\n *\n * \\begin{align}\n * \\rho = - m^\\mu \\bar{m}^\\nu \\nabla_\\nu l_\\mu .\n * \\end{align}\n *\n * It has a spin weight of 0. This gives the expression\n *\n * \\begin{align}\n *   \\rho = - \\frac{(1-y)}{2 \\sqrt{2} R}\n * \\end{align}\n */\nvoid newman_penrose_rho(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> np_rho,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n/*!\n * \\brief Compute the Newman-Penrose spin coefficient\n * $\\pi$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\pi$ is:\n *\n * \\begin{align}\n * \\pi = \\bar{m}^\\mu l^\\nu \\nabla_\\nu n_\\mu .\n * \\end{align}\n *\n * It has a spin weight of -1. This gives the expression\n *\n * \\begin{align}\n *   \\pi = \\frac{(1-y)}{8 R} \\left[\n *     \\frac{\\bar{J}(Q+2\\eth\\beta)}{\\sqrt{1+K}}\n *     - \\sqrt{1+K} (\\overline{Q+2\\eth\\beta})\n *   \\right]\n * \\end{align}\n */\nvoid newman_penrose_pi(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> np_pi,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n/*!\n * \\brief Compute the Newman-Penrose spin coefficient\n * $\\nu$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\nu$ is:\n *\n * \\begin{align}\n * \\nu = \\bar{m}^\\mu n^\\nu \\nabla_\\nu n_\\mu .\n * \\end{align}\n *\n * It has a spin weight of -1. This gives the expression\n *\n * \\begin{align}\n *   \\nu = \\frac{e^{-2\\beta}}{2} \\left[\n *     \\frac{\\bar{J}\\eth W}{\\sqrt{1+K}}\n *     - \\sqrt{1+K} \\bar{\\eth}W\n *   \\right]\n * \\end{align}\n */\nvoid newman_penrose_nu(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> np_nu,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta);\n\n/*!\n * \\brief Compute the Newman-Penrose spin coefficient\n * $\\mu$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\mu$ is:\n *\n * \\begin{align}\n * \\mu = \\bar{m}^\\mu m^\\nu \\nabla_\\nu n_\\mu .\n * \\end{align}\n *\n * It has a spin weight of 0. This gives the expression\n *\n * \\begin{align}\n *   \\mu = \\frac{e^{-2\\beta}}{2\\sqrt{2}} \\left[\n *     \\eth\\bar{U} + \\bar{\\eth}U - \\frac{1-y}{R} - 2W\n *   \\right]\n * \\end{align}\n */\nvoid newman_penrose_mu(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> np_mu,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\n/*!\n * \\brief Compute the Newman-Penrose spin coefficient\n * $\\lambda$ in the volume, in the conventions of\n * \\cite Moxon2020gha .\n *\n * \\details The definition of $\\lambda$ is:\n *\n * \\begin{align}\n * \\lambda = \\bar{m}^\\mu \\bar{m}^\\nu \\nabla_\\nu n_\\mu .\n * \\end{align}\n *\n * It has a spin weight of -2. This gives the expression\n *\n * \\begin{align}\n *   \\lambda = \\frac{e^{-2\\beta}}{4\\sqrt{2}} \\Bigg\\{ &\n *       \\left[\\frac{1-y}{R}+2W\\right] \\frac{1-y}{2(1+K)}\n *       \\left[ \\frac{\\bar{J}^2\\partial_y J - \\partial_y \\bar{J}}{K} -\n *         (2+K) \\partial_y\\bar{J} \\right] \\\\ \\nonumber\n *       &{}+ 2(1+K)\\bar{\\eth}\\bar{U}  + \\frac{\\bar{I}}{K}\n *       +\\left[ \\bar{I} +2\\bar{J}(\\bar\\eth U - \\eth\\bar U) \\right]\n *       - \\frac{\\bar{J}^2}{K(1+K)}(I + 2K\\eth U)\n *     \\Bigg\\}\n * \\end{align}\n *\n * where we have defined the temporary quantity\n *\n * \\begin{align}\n *   I = 2H+U\\bar\\eth{J}+\\bar{U}\\eth J\n * \\end{align}\n *\n */\nvoid newman_penrose_lambda(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> np_lambda,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 3>>& eth_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_h,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n\nnamespace Tags {\n/*!\n * \\brief Compute tag for $\\alpha^{SW}$ in the volume.\n *\n * \\details See documentation of `newman_penrose_alpha()` for definition.\n */\nstruct NewmanPenroseAlphaCompute : Tags::NewmanPenroseAlpha, db::ComputeTag {\n  using base = Tags::NewmanPenroseAlpha;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Tags::BondiK, Tags::BondiR, Tags::BondiQ,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 3>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_alpha);\n};\n\n/*!\n * \\brief Compute tag for $\\beta_{NP}^{SW}$ in the volume.\n *\n * \\details See documentation of `newman_penrose_beta()` for definition.\n */\nstruct NewmanPenroseBetaCompute : Tags::NewmanPenroseBeta, db::ComputeTag {\n  using base = Tags::NewmanPenroseBeta;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Tags::BondiK, Tags::BondiR, Tags::BondiQ,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +1>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 3>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_beta);\n};\n\n/*!\n * \\brief Compute tag for $\\gamma^{SW}$ in the volume.\n *\n * \\details See documentation of `newman_penrose_gamma()` for definition.\n */\nstruct NewmanPenroseGammaCompute : Tags::NewmanPenroseGamma, db::ComputeTag {\n  using base = Tags::NewmanPenroseGamma;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ, Tags::Dy<Tags::BondiJ>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Tags::BondiK, Tags::BondiH, Tags::BondiR,\n                 Tags::BondiU,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Tags::BondiW, Tags::Dy<Tags::BondiW>,\n                 Tags::Exp2Beta, Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 3>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_gamma);\n};\n\n/*!\n * \\brief Compute tag for $\\epsilon^{SW}$ in the volume.\n *\n * \\details See documentation of `newman_penrose_epsilon()` for definition.\n */\nstruct NewmanPenroseEpsilonCompute :\n      Tags::NewmanPenroseEpsilon, db::ComputeTag {\n  using base = Tags::NewmanPenroseEpsilon;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ, Tags::Dy<Tags::BondiJ>,\n                 Tags::BondiK, Tags::BondiR,\n                 Tags::Dy<Tags::BondiBeta>, Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_epsilon);\n};\n\n// We do not implement a compute tag for NewmanPenroseKappa, since it vanishes\n\n/*!\n * \\brief Compute tag for $\\tau$ in the volume.\n *\n * \\details See documentation of `newman_penrose_tau()` for definition.\n */\nstruct NewmanPenroseTauCompute : Tags::NewmanPenroseTau, db::ComputeTag {\n  using base = Tags::NewmanPenroseTau;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ, Tags::BondiK,\n                 Tags::BondiR, Tags::BondiQ,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +1>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_tau);\n};\n\n/*!\n * \\brief Compute tag for $\\sigma$ in the volume.\n *\n * \\details See documentation of `newman_penrose_sigma()` for definition.\n */\nstruct NewmanPenroseSigmaCompute : Tags::NewmanPenroseSigma, db::ComputeTag {\n  using base = Tags::NewmanPenroseSigma;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ, Tags::Dy<Tags::BondiJ>,\n                 Tags::BondiK, Tags::BondiR, Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, +2>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_sigma);\n};\n\n/*!\n * \\brief Compute tag for $\\rho$ in the volume.\n *\n * \\details See documentation of `newman_penrose_rho()` for definition.\n */\nstruct NewmanPenroseRhoCompute : Tags::NewmanPenroseRho, db::ComputeTag {\n  using base = Tags::NewmanPenroseRho;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<Tags::BondiR, Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_rho);\n};\n\n/*!\n * \\brief Compute tag for $\\pi$ in the volume.\n *\n * \\details See documentation of `newman_penrose_pi()` for definition.\n */\nstruct NewmanPenrosePiCompute : Tags::NewmanPenrosePi, db::ComputeTag {\n  using base = Tags::NewmanPenrosePi;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ, Tags::BondiK,\n                 Tags::BondiR, Tags::BondiQ,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_pi);\n};\n\n/*!\n * \\brief Compute tag for $\\nu$ in the volume.\n *\n * \\details See documentation of `newman_penrose_nu()` for definition.\n */\nstruct NewmanPenroseNuCompute : Tags::NewmanPenroseNu, db::ComputeTag {\n  using base = Tags::NewmanPenroseNu;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ, Tags::BondiK,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiW,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::Exp2Beta>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_nu);\n};\n\n/*!\n * \\brief Compute tag for $\\mu$ in the volume.\n *\n * \\details See documentation of `newman_penrose_mu()` for definition.\n */\nstruct NewmanPenroseMuCompute : Tags::NewmanPenroseMu, db::ComputeTag {\n  using base = Tags::NewmanPenroseMu;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiR, Tags::BondiW,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Tags::Exp2Beta, Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_mu);\n};\n\n/*!\n * \\brief Compute tag for $\\lambda$ in the volume.\n *\n * \\details See documentation of `newman_penrose_lambda()` for definition.\n */\nstruct NewmanPenroseLambdaCompute : Tags::NewmanPenroseLambda, db::ComputeTag {\n  using base = Tags::NewmanPenroseLambda;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ, Tags::Dy<Tags::BondiJ>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Tags::BondiK, Tags::BondiH,\n                 Tags::BondiR, Tags::BondiU,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Tags::BondiW,\n                 Tags::Exp2Beta, Tags::OneMinusY>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 3>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &newman_penrose_lambda);\n};\n\n}  // namespace Tags\n\n/*!\n * \\brief Compute the Weyl scalar \\f$\\Psi_0\\f$ in the volume according to a\n * standard set of Newman-Penrose vectors.\n *\n * \\details The Bondi forms of the Newman-Penrose vectors that are needed for\n * \\f$\\Psi_0\\f$ are:\n *\n * \\f{align}{\n * \\mathbf{l} &= \\partial_r / \\sqrt{2}\\\\\n * \\mathbf{m} &= \\frac{-1}{2 r} \\left(\\sqrt{1 + K} q^A \\partial_A -\n *   \\frac{J}{\\sqrt{1 + K}}\\bar{q}^A \\partial_A \\right)\n * \\f}\n *\n * Then, we may compute \\f$\\Psi_0 =  l^\\alpha m^\\beta l^\\mu m^\\nu C_{\\alpha\n * \\beta \\mu \\nu}\\f$ from the Bondi system, giving\n *\n * \\f{align*}{\n * \\Psi_0 = \\frac{(1 - y)^4}{16 r^2 K}\n * \\bigg[& \\partial_y \\beta \\left((1 + K) (\\partial_y J)\n * - \\frac{J^2 \\partial_y \\bar J}{1 + K}\\right)\n * - \\frac{1}{2} (1 + K) (\\partial_y^2 J)\n * + \\frac{J^2 \\partial_y^2 \\bar J}{2(K + 1)}\\\\\n * & + \\frac{1}{K^2} \\left(- \\frac{1}{4} J \\left(\\bar{J}^2 \\left(\\partial_y\n * J\\right)^2 + J^2 \\left(\\partial_y \\bar J\\right)^2\\right)\n * + \\frac{1 + K^2}{2} J (\\partial_y J) (\\partial_y \\bar J)\n * \\right)\\bigg].\n * \\f}\n */\ntemplate <>\nstruct VolumeWeyl<Tags::Psi0> {\n  using return_tags = tmpl::list<Tags::Psi0>;\n  using argument_tags = tmpl::list<Tags::BondiJ, Tags::Dy<Tags::BondiJ>,\n                                   Tags::Dy<Tags::Dy<Tags::BondiJ>>,\n                                   Tags::BondiK, Tags::BondiR, Tags::OneMinusY>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> psi_0,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_dy_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n};\n\nnamespace Tags {\n/*!\n * \\brief Compute tag for $\\Psi_0$ in the volume.\n *\n * \\details Uses `apply` function from `VolumeWeyl<Tags::Psi0>` for the\n * computation.\n */\nstruct Psi0Compute : Tags::Psi0, db::ComputeTag {\n  using base = Tags::Psi0;\n  using return_type = typename base::type;\n  using argument_tags = typename VolumeWeyl<Tags::Psi0>::argument_tags;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &VolumeWeyl<Tags::Psi0>::apply);\n};\n}  // namespace Tags\n\n/*!\n * \\brief Compute the Weyl scalar \\f$\\Psi_1\\f$ in the volume according to the\n * standard set of Newman-Penrose vectors.\n *\n * \\details Our convention is \\f$\\Psi_1 =\n * l^\\alpha n^\\beta l^\\mu m^\\nu C_{\\alpha\n * \\beta \\mu \\nu}\\f$.\n *\n * \\f{align*}{\n * \\Psi_1 =\n * &\\frac{(1-y)^2}{\\sqrt{128} \\sqrt{1 + K} R^2}\\Bigg\\{\n * J(\\bar{\\eth }\\beta + \\tfrac{1}{2} \\bar{Q})\n * - (1 + K) (\\eth \\beta + \\tfrac{1}{2} Q) \\\\\n * &+(1-y)\\Bigg[\n * (1 + K)\\eth\\partial_{y}\\beta - J\\bar{\\eth }\\partial_{y}\\beta\n * + \\left(- J \\frac{\\bar{\\eth} R}{R} + (1 + K) \\frac{\\eth R}{R}\\right)\n * \\partial_{y}\\beta \\\\\n * &\\quad + \\frac{1}{4K}\\Bigg(\n * J\\left\\{\n * -2 \\partial_{y}\\bar{Q} + \\partial_{y}\\bar{J} [2 (\\eth\\beta + \\tfrac{1}{2}Q) +\n * J(\\bar{\\eth}\\beta + \\tfrac{1}{2} \\bar{Q})]\n * \\right\\}\\\\\n * &\\qquad +(1+K)\\Big\\{\n * 2 (\\partial_{y}Q + J \\partial_{y}\\bar{Q}) + (\\bar{J} \\partial_{y}J - J\n * \\partial_{y}\\bar{J}) (\\eth\\beta + \\tfrac{1}{2} Q) \\\\\n * &\\qquad\\quad - (1+K)\n * [2 \\partial_{y}Q + \\partial _{y}J (\\bar{\\eth }\\beta + \\tfrac{1}{2} \\bar{Q})]\n * \\Big\\}\\Bigg)\\Bigg]\\Bigg\\}\n * \\f}\n */\ntemplate <>\nstruct VolumeWeyl<Tags::Psi1> {\n  using return_tags = tmpl::list<Tags::Psi1>;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ, Tags::Dy<Tags::BondiJ>,\n                 Tags::BondiK, Tags::BondiQ,\n                 Tags::Dy<Tags::BondiQ>, Tags::BondiR, Tags::EthRDividedByR,\n                 Tags::Dy<Tags::BondiBeta>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::OneMinusY>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> psi_1,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_k,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& bondi_q,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_q,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_dy_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y);\n};\n\nnamespace Tags {\n/*!\n * \\brief Compute tag for $\\Psi_1$ in the volume.\n *\n * \\details Uses `apply` function from `VolumeWeyl<Tags::Psi1>` for the\n * computation.\n */\nstruct Psi1Compute : Tags::Psi1, db::ComputeTag {\n  using base = Tags::Psi1;\n  using return_type = typename base::type;\n  using argument_tags = typename VolumeWeyl<Tags::Psi1>::argument_tags;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&)>(\n      &VolumeWeyl<Tags::Psi1>::apply);\n};\n}  // namespace Tags\n\n/*!\n * \\brief Compute the Weyl scalar $\\Psi_2$ in the volume according to the\n * standard set of Newman-Penrose vectors.\n *\n * \\details Our convention is $\\Psi_2 =\n * l^\\alpha m^\\beta \\bar{m}^\\mu n^\\nu\n * C_{\\alpha \\beta \\mu \\nu}$.\n *\n * \\begin{align}\n *   \\Psi_2 = {}&\\frac{1-y}{4 R} \\left[ \\sqrt{2}(1-y)\\partial_y \\mu +\n *   \\sqrt{1+K}\\eth\\pi - \\frac{J}{\\sqrt{1+K}}\\bar{\\eth}\\pi\\right]\n *   \\nonumber \\\\\n *   & {}+ (\\epsilon^{SW}+\\bar{\\epsilon}^{SW}-\\bar{\\rho})\\mu +\n *    (\\bar{\\alpha}^{SW}-\\beta^{SW}_{NP}-\\bar{\\pi})\\pi\n *    - \\sigma\\lambda + \\nu\\kappa\n * \\end{align}\n *\n * In our choice of tetrad, $\\kappa=0$, so the final term is omitted.\n */\ntemplate <>\nstruct VolumeWeyl<Tags::Psi2> {\n  using return_tags = tmpl::list<Tags::Psi2>;\n  using argument_tags =\n      tmpl::list<Tags::BondiJ,\n                 Tags::BondiK,\n                 Tags::BondiR,\n                 Tags::Dy<Tags::NewmanPenroseMu>,\n                 Spectral::Swsh::Tags::Derivative<Tags::NewmanPenrosePi,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::NewmanPenrosePi,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Tags::NewmanPenroseAlpha,\n                 Tags::NewmanPenroseBeta,\n                 Tags::NewmanPenroseEpsilon,\n                 Tags::NewmanPenroseSigma,\n                 Tags::NewmanPenroseRho,\n                 Tags::NewmanPenrosePi,\n                 Tags::NewmanPenroseMu,\n                 Tags::NewmanPenroseLambda,\n                 Tags::OneMinusY>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> psi_2,\n      const Scalar<SpinWeighted<ComplexDataVector, +2>>& bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>& bondi_k,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>& bondi_r,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>& dy_mu,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>& eth_pi,\n      const Scalar<SpinWeighted<ComplexDataVector, -2>>& ethbar_pi,\n      const Scalar<SpinWeighted<ComplexDataVector, -1>>& np_alpha,\n      const Scalar<SpinWeighted<ComplexDataVector, +1>>& np_beta,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>& np_epsilon,\n      const Scalar<SpinWeighted<ComplexDataVector, +2>>& np_sigma,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>& np_rho,\n      const Scalar<SpinWeighted<ComplexDataVector, -1>>& np_pi,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>& np_mu,\n      const Scalar<SpinWeighted<ComplexDataVector, -2>>& np_lambda,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>& one_minus_y);\n};\n\nnamespace Tags {\n/*!\n * \\brief Compute tag for $\\Psi_2$ in the volume.\n *\n * \\details Uses `VolumeWeyl<Tags::Psi2>::apply()` for the\n * computation.\n */\nstruct Psi2Compute : Tags::Psi2, db::ComputeTag {\n  using base = Tags::Psi2;\n  using return_type = typename base::type;\n  using argument_tags = typename VolumeWeyl<Tags::Psi2>::argument_tags;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>,\n      const Scalar<SpinWeighted<ComplexDataVector,  2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, -2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, -1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, +1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, +2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, -1>>&,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>&,\n      const Scalar<SpinWeighted<ComplexDataVector, -2>>&,\n      const Scalar<SpinWeighted<ComplexDataVector,  0>>&)>(\n      &VolumeWeyl<Tags::Psi2>::apply);\n};\n}  // namespace Tags\n\n/*!\n * \\brief Transform `Tags::BondiJ` from the partially flat coordinates\n * to the Cauchy coordinates.\n *\n * \\details The spin-2 quantity \\f$\\hat J\\f$ transforms as\n * \\f{align*}{\n * J = \\frac{1}{4 \\omega^2} (\\bar d^2 \\hat J + c^2 \\bar{\\hat J}\n * + 2 c \\bar d \\hat K )\n * \\f}\n *\n * with\n * \\f{align*}{\n * \\hat K = \\sqrt{1+\\hat J \\bar{\\hat J}}\n * \\f}\n */\nstruct TransformBondiJToCauchyCoords {\n  using return_tags = tmpl::list<Tags::BondiJCauchyView>;\n  using argument_tags = tmpl::list<\n      Tags::CauchyGaugeC, Tags::BondiJ, Tags::CauchyGaugeD,\n      Tags::CauchyGaugeOmega,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::PartiallyFlatAngularCoords>,\n      Tags::LMax>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          cauchy_view_volume_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& gauge_cauchy_c,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& volume_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& gauge_cauchy_d,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega_cauchy,\n      const Spectral::Swsh::SwshInterpolator& interpolator,\n      const size_t l_max);\n};\n\n/*!\n * \\brief Compute the Weyl scalar \\f$\\Psi_0\\f$ in the volume for the purpose\n * of CCM, the quantity is in the Cauchy coordinates.\n *\n * \\details The Weyl scalar \\f$\\Psi_0\\f$ is given by:\n *\n * \\f{align*}{\n * \\Psi_0 = \\frac{(1 - y)^4}{16 r^2 K}\n * \\bigg[& \\partial_y \\beta \\left((1 + K) (\\partial_y J)\n * - \\frac{J^2 \\partial_y \\bar J}{1 + K}\\right)\n * - \\frac{1}{2} (1 + K) (\\partial_y^2 J)\n * + \\frac{J^2 \\partial_y^2 \\bar J}{2(K + 1)}\\\\\n * & + \\frac{1}{K^2} \\left(- \\frac{1}{4} J \\left(\\bar{J}^2 \\left(\\partial_y\n * J\\right)^2 + J^2 \\left(\\partial_y \\bar J\\right)^2\\right)\n * + \\frac{1 + K^2}{2} J (\\partial_y J) (\\partial_y \\bar J)\n * \\right)\\bigg].\n * \\f}\n *\n * The quantities above are all in the Cauchy coordinates, where \\f$K\\f$ is\n * updated from \\f$J\\f$ and \\f$\\bar J\\f$, \\f$(1-y)\\f$ is invariant under\n * the coordinate transformation. \\f$r\\f$ transforms as\n *\n * \\f{align*}{\n * r = \\omega \\hat r\n * \\f}\n */\ntemplate <>\nstruct VolumeWeyl<Tags::Psi0Match> {\n  using return_tags = tmpl::list<Tags::Psi0Match>;\n  using argument_tags =\n      tmpl::list<Tags::BondiJCauchyView, Tags::Dy<Tags::BondiJCauchyView>,\n                 Tags::Dy<Tags::Dy<Tags::BondiJCauchyView>>,\n                 Tags::BoundaryValue<Tags::BondiR>, Tags::OneMinusY,\n                 Tags::LMax>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> psi_0,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_j_cauchy,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_j_cauchy,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_dy_j_cauchy,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r_cauchy,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const size_t l_max);\n};\n\n/*!\n * \\brief Compute the Weyl scalar \\f$\\Psi_0\\f$ and its radial derivative\n * \\f$\\partial_\\lambda \\Psi_0\\f$ on the inner boundary of CCE domain.\n * The quantities are in the Cauchy coordinates.\n *\n * \\details The radial derivative of the Weyl scalar \\f$\\partial_\\lambda\n * \\Psi_0\\f$ is given by\n *\n * \\f{align*}{\n * \\partial_\\lambda \\Psi_0 = \\frac{(1-y)^2}{2r}e^{-2\\beta}\n * \\partial_y \\Psi_0\n * \\f}\n *\n * Note that \\f$(1-y)\\f$, \\f$r\\f$, and \\f$\\beta\\f$ are in the Cauchy\n * coordinates, where \\f$(1-y)\\f$ is invariant under the coordinate\n * transformation, while \\f$r\\f$ and \\f$\\beta\\f$ transform as\n *\n * \\f{align*}{\n * &r = \\omega \\hat r\n * & \\beta = \\hat \\beta - \\frac{1}{2} \\log \\omega\n * \\f}\n */\nstruct InnerBoundaryWeyl {\n  using return_tags =\n      tmpl::list<Tags::BoundaryValue<Tags::Psi0Match>,\n                 Tags::BoundaryValue<Tags::Dlambda<Tags::Psi0Match>>>;\n  using argument_tags =\n      tmpl::list<Tags::Psi0Match, Tags::Dy<Tags::Psi0Match>, Tags::OneMinusY,\n                 Tags::BoundaryValue<Tags::BondiR>,\n                 Tags::BoundaryValue<Tags::BondiBeta>, Tags::LMax>;\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> psi_0_boundary,\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          dlambda_psi_0_boundary,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& psi_0,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_psi_0,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_r_cauchy,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& bondi_beta_cauchy,\n      const size_t l_max);\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/OptionTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <stdexcept>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticBoundaryDataManager.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/ExtractionRadius.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLocalTimeStepping.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/InitializationTag.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n/// \\brief %Option tags for CCE\nnamespace OptionTags {\n\n/// %Option group\nstruct Cce {\n  static constexpr Options::String help = {\n      \"Options for the Cce evolution system\"};\n};\n\n/// %Option group\nstruct Filtering {\n  static constexpr Options::String help = {\"Options for the filtering in Cce\"};\n  using group = Cce;\n};\n\n/// %Option group for evolution-related quantities in the CCE system\nstruct Evolution {\n  static constexpr Options::String help = {\"Options for the CCE evolution\"};\n  using group = Cce;\n};\n\n/// A prefix for common tags (e.g. from Time/Tags.hpp) that are specific to CCE,\n/// so should be in the Cce::Evolution group.\ntemplate <typename OptionTag>\nstruct CceEvolutionPrefix {\n  using type = typename OptionTag::type;\n  static std::string name() { return pretty_type::name<OptionTag>(); }\n  static constexpr Options::String help = OptionTag::help;\n  using group = Evolution;\n};\n\nstruct BondiSachsOutputFilePrefix {\n  using type = std::string;\n  static constexpr Options::String help{\n      \"Filename prefix for dumping Bondi-Sachs data on worltube radii. Files \"\n      \"will have this prefix prepended to 'CceRXXXX.h5' where XXXX will be the \"\n      \"zero-padded extraction radius to the nearest integer.\"};\n  using group = Cce;\n};\n\nstruct FilterLMax {\n  using type = size_t;\n  static constexpr Options::String help{\"l mode cutoff for angular filtering\"};\n  using group = Filtering;\n};\n\nstruct RadialFilterAlpha {\n  using type = double;\n  static constexpr Options::String help{\n      \"alpha parameter in exponential radial filter\"};\n  using group = Filtering;\n};\n\nstruct RadialFilterHalfPower {\n  using type = size_t;\n  static constexpr Options::String help{\n      \"Half-power of the exponential radial filter argument\"};\n  using group = Filtering;\n};\n\nstruct ObservationLMax {\n  using type = size_t;\n  static constexpr Options::String help{\"Maximum l value for swsh output\"};\n  using group = Cce;\n};\n\nstruct ExtractionRadius {\n  using type = double;\n  static constexpr Options::String help{\"Extraction radius of the CCE system.\"};\n  using group = Cce;\n};\n\nstruct StandaloneExtractionRadius {\n  static std::string name() { return \"ExtractionRadius\"; }\n  using type = Options::Auto<double>;\n\n  static constexpr Options::String help{\n      \"Extraction radius of the CCE system for a standalone run. This may be \"\n      \"set to \\\"Auto\\\" to infer the radius from the filename (often used for \"\n      \"SpEC worldtube data).\"};\n  using group = Cce;\n};\n\nstruct EndTime {\n  using type = Options::Auto<double>;\n  static constexpr Options::String help{\"End time for the Cce Evolution.\"};\n  static type suggested_value() { return {}; }\n  using group = Cce;\n};\n\nstruct StartTime {\n  using type = Options::Auto<double>;\n  static constexpr Options::String help{\n      \"Cce Start time (default to earliest possible time).\"};\n  static type suggested_value() { return {}; }\n  using group = Cce;\n};\n\nstruct BoundaryDataFilename {\n  using type = std::string;\n  static constexpr Options::String help{\n      \"H5 file to read the wordltube data from.\"};\n  using group = Cce;\n};\n\nstruct KleinGordonBoundaryDataFilename {\n  using type = std::string;\n  static constexpr Options::String help{\n      \"H5 file to read the Klein-Gordon wordltube data from. It could be the \"\n      \"same as/different from `BoundaryDataFilename`.\"};\n  using group = Cce;\n};\n\nstruct H5LookaheadTimes {\n  using type = size_t;\n  static constexpr Options::String help{\n      \"Number of times steps from the h5 to cache each read.\"};\n  static size_t suggested_value() { return 200; }\n  using group = Cce;\n};\n\nstruct H5Interpolator {\n  using type = std::unique_ptr<intrp::SpanInterpolator>;\n  static constexpr Options::String help{\n      \"The interpolator for imported h5 worldtube data.\"};\n  using group = Cce;\n};\n\nstruct AnalyticSolution {\n  using type = std::unique_ptr<Solutions::WorldtubeData>;\n  static constexpr Options::String help{\n      \"Analytic worldtube data for tests of CCE.\"};\n  using group = Cce;\n};\n\nstruct GhInterfaceManager {\n  using type = InterfaceManagers::GhLocalTimeStepping;\n  static constexpr Options::String help{\n      \"Class to manage worldtube data from a GH system.\"};\n  using group = Cce;\n};\n\nstruct ScriInterpolationOrder {\n  static std::string name() { return \"ScriInterpOrder\"; }\n  using type = size_t;\n  static constexpr Options::String help{\n      \"Order of time interpolation at scri+.\"};\n  static size_t suggested_value() { return 5; }\n  using group = Cce;\n};\n\nstruct ScriOutputDensity {\n  using type = size_t;\n  static constexpr Options::String help{\n      \"Number of scri output points per timestep.\"};\n  static size_t suggested_value() { return 1; }\n  using group = Cce;\n};\n\ntemplate <bool evolve_ccm>\nstruct InitializeJ {\n  using type = std::unique_ptr<::Cce::InitializeJ::InitializeJ<evolve_ccm>>;\n  static constexpr Options::String help{\n      \"The initialization for the first hypersurface for J\"};\n  using group = Cce;\n};\n}  // namespace OptionTags\n\n/// \\brief Initialization tags for CCE\nnamespace InitializationTags {\nstruct ScriInterpolationOrder : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::ScriInterpolationOrder>;\n\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(\n      const size_t scri_plus_interpolation_order) {\n    return scri_plus_interpolation_order;\n  }\n};\n\nstruct ScriOutputDensity : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::ScriOutputDensity>;\n\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(const size_t scri_output_density) {\n    return scri_output_density;\n  }\n};\n}  // namespace InitializationTags\n\nnamespace Tags {\nstruct ExtractionRadius : db::SimpleTag {\n  using type = double;\n};\n\nstruct ExtractionRadiusSimple : ExtractionRadius {\n  using base = ExtractionRadius;\n  using option_tags = tmpl::list<OptionTags::ExtractionRadius>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double extraction_radius) {\n    return extraction_radius;\n  }\n};\n\nstruct ExtractionRadiusFromH5 : ExtractionRadius {\n  using base = ExtractionRadius;\n  using option_tags = tmpl::list<OptionTags::BoundaryDataFilename,\n                                 OptionTags::StandaloneExtractionRadius>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(\n      const std::string& filename,\n      const std::optional<double>& extraction_radius) {\n    const std::optional<double> radius =\n        Cce::get_extraction_radius(filename, extraction_radius);\n    return radius.value();\n  }\n};\n\nstruct FilePrefix : db::SimpleTag {\n  using type = std::string;\n  using option_tags = tmpl::list<OptionTags::BondiSachsOutputFilePrefix>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n\n/// Tag for duplicating functionality of another tag, but allows creation from\n/// options in the Cce::Evolution option group.\n/// @{\ntemplate <typename Tag>\nstruct CceEvolutionPrefix;\n\ntemplate <db::simple_tag Tag>\nstruct CceEvolutionPrefix<Tag> : db::SimpleTag {\n  using type = typename Tag::type;\n  static std::string name() { return db::tag_name<Tag>(); }\n};\n\ntemplate <Parallel::untemplated_initialization_tag Tag>\nstruct CceEvolutionPrefix<Tag> : db::SimpleTag {\n  using type = typename Tag::type;\n  using option_tags = db::wrap_tags_in<OptionTags::CceEvolutionPrefix,\n                                       typename Tag::option_tags>;\n  static std::string name() { return db::tag_name<Tag>(); }\n\n  static constexpr bool pass_metavariables = Tag::pass_metavariables;\n  template <typename... Args>\n  static type create_from_options(const Args&... args) {\n    return Tag::create_from_options(args...);\n  }\n};\n\ntemplate <Parallel::templated_initialization_tag Tag>\nstruct CceEvolutionPrefix<Tag> : db::SimpleTag {\n  using type = typename Tag::type;\n  template <typename Metavariables>\n  using option_tags =\n      db::wrap_tags_in<OptionTags::CceEvolutionPrefix,\n                       typename Tag::template option_tags<Metavariables>>;\n  static std::string name() { return db::tag_name<Tag>(); }\n\n  static constexpr bool pass_metavariables = Tag::pass_metavariables;\n  template <typename Metavariables, typename... Args>\n  static type create_from_options(const Args&... args) {\n    return Tag::template create_from_options<Metavariables>(args...);\n  }\n};\n\ntemplate <db::reference_tag Tag>\nstruct CceEvolutionPrefix<Tag> : CceEvolutionPrefix<typename Tag::base>,\n                                 db::ReferenceTag {\n  using base = CceEvolutionPrefix<typename Tag::base>;\n  using argument_tags =\n      tmpl::transform<typename Tag::argument_tags,\n                      tmpl::bind<CceEvolutionPrefix, tmpl::_1>>;\n  template <typename... Args>\n  static const auto& get(const Args&... args) {\n    return Tag::get(args...);\n  }\n};\n/// @}\n\n/// A tag that constructs a `MetricWorldtubeDataManager` or\n/// `BondiWorldtubeDataManager` from options\nstruct H5WorldtubeBoundaryDataManager : db::SimpleTag {\n  using type = std::unique_ptr<WorldtubeDataManager<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>>;\n  using option_tags =\n      tmpl::list<Spectral::Swsh::OptionTags::LMax,\n                 OptionTags::BoundaryDataFilename, OptionTags::H5LookaheadTimes,\n                 OptionTags::H5Interpolator,\n                 OptionTags::StandaloneExtractionRadius>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const size_t l_max, const std::string& filename,\n      const size_t number_of_lookahead_times,\n      const std::unique_ptr<intrp::SpanInterpolator>& interpolator,\n      const std::optional<double> extraction_radius) {\n    const std::string text_radius_str = Cce::get_text_radius(filename);\n    try {\n      // If this doesn't throw an exception, then an extraction radius was\n      // supplied in the filename. We don't actually need the value.\n      const double text_radius = std::stod(text_radius_str);\n      (void)text_radius;\n      if (extraction_radius.has_value()) {\n        Parallel::printf(\n            \"Warning: Option ExtractionRadius is set to a specific value and \"\n            \"there is an extraction radius in the H5 filename. The value in \"\n            \"the file name will be ignored.It is recommended to set \"\n            \"`ExtractionRadius` to `\\\"Auto\\\"` if the H5 filename has the \"\n            \"extraction radius in it to make the input file clearer.\\n\");\n      }\n    } catch (const std::invalid_argument&) {\n    }\n\n    return std::make_unique<BondiWorldtubeDataManager>(\n        std::make_unique<BondiWorldtubeH5BufferUpdater<ComplexModalVector>>(\n            filename, extraction_radius),\n        l_max, number_of_lookahead_times, interpolator->get_clone());\n  }\n};\n\n/// A tag that constructs a `KleinGordonWorldtubeDataManager` from options\nstruct KleinGordonH5WorldtubeBoundaryDataManager : db::SimpleTag {\n  using type = std::unique_ptr<\n      WorldtubeDataManager<Tags::klein_gordon_worldtube_boundary_tags>>;\n  using option_tags =\n      tmpl::list<Spectral::Swsh::OptionTags::LMax,\n                 OptionTags::KleinGordonBoundaryDataFilename,\n                 OptionTags::H5LookaheadTimes, OptionTags::H5Interpolator,\n                 OptionTags::StandaloneExtractionRadius>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const size_t l_max, const std::string& filename,\n      const size_t number_of_lookahead_times,\n      const std::unique_ptr<intrp::SpanInterpolator>& interpolator,\n      const std::optional<double> extraction_radius) {\n    return std::make_unique<KleinGordonWorldtubeDataManager>(\n        std::make_unique<KleinGordonWorldtubeH5BufferUpdater>(\n            filename, extraction_radius),\n        l_max, number_of_lookahead_times, interpolator->get_clone());\n  }\n};\n\nstruct ObservationLMax : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::ObservationLMax>;\n\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(const size_t observation_l_max) {\n    return observation_l_max;\n  }\n};\n\nstruct FilterLMax : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::FilterLMax>;\n\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(const size_t filter_l_max) {\n    return filter_l_max;\n  }\n};\n\nstruct RadialFilterAlpha : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::RadialFilterAlpha>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double radial_filter_alpha) {\n    return radial_filter_alpha;\n  }\n};\n\nstruct RadialFilterHalfPower : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::RadialFilterHalfPower>;\n\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(const size_t radial_filter_half_power) {\n    return radial_filter_half_power;\n  }\n};\n\n/// \\brief Represents the start time of a bounded CCE evolution, determined\n/// either from option specification or from the file\n///\n/// \\details If no start time is specified in the input file (so the option\n/// `OptionTags::StartTime` is set to \"Auto\"), this will find the start time\n/// from the provided H5 file. If `OptionTags::StartTime` takes any other value,\n/// it will be used directly as the start time for the CCE evolution instead.\nstruct StartTimeFromFile : Tags::StartTime {\n  using base = Tags::StartTime;\n  using option_tags =\n      tmpl::list<OptionTags::StartTime, OptionTags::BoundaryDataFilename,\n                 OptionTags::StandaloneExtractionRadius>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(\n      const std::optional<double> start_time, const std::string& filename,\n      const std::optional<double>& extraction_radius) {\n    if (start_time.has_value()) {\n      return *start_time;\n    }\n\n    BondiWorldtubeH5BufferUpdater<ComplexModalVector> h5_boundary_updater{\n        filename, extraction_radius};\n    const auto& time_buffer = h5_boundary_updater.get_time_buffer();\n    return time_buffer[0];\n  }\n};\n\n/// \\brief Represents the start time of a bounded CCE evolution that must be\n/// supplied in the input file (for e.g. analytic tests).\nstruct SpecifiedStartTime : Tags::StartTime {\n  using base = Tags::StartTime;\n  using option_tags = tmpl::list<OptionTags::StartTime>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const std::optional<double> start_time) {\n    if (not start_time.has_value()) {\n      ERROR(\n          \"The start time must be explicitly specified for the tag \"\n          \"`SpecifiedStartTime`\");\n    }\n    return *start_time;\n  }\n};\n\n/// \\brief Represents the final time of a bounded CCE evolution, determined\n/// either from option specification or from the file\n///\n/// \\details If no end time is specified in the input file (so the option\n/// `OptionTags::EndTime` is set to \"Auto\"), this will find the end time\n/// from the provided H5 file. If `OptionTags::EndTime` takes any other value,\n/// it will be used directly as the final time for the CCE evolution instead.\nstruct EndTimeFromFile : Tags::EndTime {\n  using base = Tags::EndTime;\n  using option_tags =\n      tmpl::list<OptionTags::EndTime, OptionTags::BoundaryDataFilename,\n                 OptionTags::StandaloneExtractionRadius>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(\n      const std::optional<double> end_time, const std::string& filename,\n      const std::optional<double>& extraction_radius) {\n    if (end_time) {\n      return *end_time;\n    }\n    BondiWorldtubeH5BufferUpdater<ComplexModalVector> h5_boundary_updater{\n        filename, extraction_radius};\n    const auto& time_buffer = h5_boundary_updater.get_time_buffer();\n    return time_buffer[time_buffer.size() - 1];\n  }\n};\n\n/// \\brief Represents the final time of a CCE evolution that should just proceed\n/// until it receives no more boundary data and becomes quiescent.\nstruct NoEndTime : Tags::EndTime {\n  using base = Tags::EndTime;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options() {\n    return std::numeric_limits<double>::infinity();\n  }\n};\n\n/// \\brief Represents the final time of a bounded CCE evolution that must be\n/// supplied in the input file (for e.g. analytic tests).\nstruct SpecifiedEndTime : Tags::EndTime {\n  using base = Tags::EndTime;\n  using option_tags = tmpl::list<OptionTags::EndTime>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const std::optional<double> end_time) {\n    if (not end_time.has_value()) {\n      ERROR(\n          \"The end time must be explicitly specified for the tag \"\n          \"`SpecifiedEndTime`\");\n    }\n    return *end_time;\n  }\n};\n\nstruct GhInterfaceManager : db::SimpleTag {\n  using type = InterfaceManagers::GhLocalTimeStepping;\n  using option_tags = tmpl::list<OptionTags::GhInterfaceManager>;\n\n  static constexpr bool pass_metavariables = false;\n  static InterfaceManagers::GhLocalTimeStepping create_from_options(\n      const InterfaceManagers::GhLocalTimeStepping& interface_manager) {\n    return interface_manager;\n  }\n};\n\n/// Tag for first-hypersurface initialization procedure specified by input\n/// options.\ntemplate <bool evolve_ccm>\nstruct InitializeJ : db::SimpleTag {\n  using type = std::unique_ptr<::Cce::InitializeJ::InitializeJ<evolve_ccm>>;\n  using option_tags = tmpl::list<OptionTags::InitializeJ<evolve_ccm>>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::unique_ptr<::Cce::InitializeJ::InitializeJ<evolve_ccm>>\n  create_from_options(\n      const std::unique_ptr<::Cce::InitializeJ::InitializeJ<evolve_ccm>>&\n          initialize_j) {\n    return initialize_j->get_clone();\n  }\n};\n\n// Tags that generates an `Cce::InitializeJ::InitializeJ` derived class from an\n// analytic solution.\nstruct AnalyticInitializeJ : InitializeJ<false> {\n  using base = InitializeJ<false>;\n  using option_tags =\n      tmpl::list<OptionTags::AnalyticSolution, OptionTags::StartTime>;\n  static constexpr bool pass_metavariables = false;\n  static std::unique_ptr<::Cce::InitializeJ::InitializeJ<false>>\n  create_from_options(\n      const std::unique_ptr<Cce::Solutions::WorldtubeData>& worldtube_data,\n      const std::optional<double> start_time) {\n    return worldtube_data->get_initialize_j(*start_time);\n  }\n};\n\n/// A tag that constructs a `AnalyticBoundaryDataManager` from options\nstruct AnalyticBoundaryDataManager : db::SimpleTag {\n  using type = ::Cce::AnalyticBoundaryDataManager;\n  using option_tags =\n      tmpl::list<OptionTags::ExtractionRadius, Spectral::Swsh::OptionTags::LMax,\n                 OptionTags::AnalyticSolution>;\n\n  static constexpr bool pass_metavariables = false;\n  static Cce::AnalyticBoundaryDataManager create_from_options(\n      const double extraction_radius, const size_t l_max,\n      const std::unique_ptr<Cce::Solutions::WorldtubeData>& worldtube_data) {\n    return ::Cce::AnalyticBoundaryDataManager(l_max, extraction_radius,\n                                              worldtube_data->get_clone());\n  }\n};\n\n/// Represents whether the news should be provided at noninertial times.\n///\n/// \\details Currently, this is only useful for analytic solutions for which the\n/// inertial-time news is difficult to compute.\nstruct OutputNoninertialNews : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::AnalyticSolution>;\n  static constexpr bool pass_metavariables = false;\n  static bool create_from_options(\n      const std::unique_ptr<Cce::Solutions::WorldtubeData>& worldtube_data) {\n    return worldtube_data->use_noninertial_news();\n  }\n};\n}  // namespace Tags\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/PreSwshDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/LinearOperators.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\n/*!\n * \\brief A set of procedures for computing the set of inputs to the CCE\n * integrand computations that are to be performed prior to the spin-weighted\n * spherical harmonic differentiation (and for the first step in the series of\n * integrations, after the `PrecomputeCceDependencies`)\n *\n * \\details For the storage model in which a set of `Variables` are stored in a\n * `DataBox`, there are type aliases provided in each of the specializations:\n * - output/input : `pre_swsh_derivatives` type alias. A \\ref DataBoxGroup with\n * tags `all_pre_swsh_derivative_tags` is compatible with all specializations.\n * - input : `swsh_derivative_tags` type alias. A \\ref DataBoxGroup with tags\n *   `all_swsh_derivative_tags` is compatible with all specializations.\n * - input : `integrand_tags` type alias. A \\ref DataBoxGroup with tags\n * `Tags::BondiU` and `Tags::BondiBeta` is compatible with all specializations.\n */\ntemplate <typename Tag>\nstruct PreSwshDerivatives;\n\n/*!\n * \\brief Compute \\f$\\bar{J}\\f$.\n *\n * \\note Computing \\f$\\bar{J}\\f$ should be unnecessary in most execution\n * procedures, as all quantities should be derived from \\f$J\\f$ and its\n * derivatives followed by explicit conjugation operations, which are expected\n * to be sufficiently cheap to avoid the storage cost of recording the\n * conjugates. The exception is caching for certain spin-weighted derivatives of\n * products\n */\ntemplate <>\nstruct PreSwshDerivatives<Tags::BondiJbar> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiJ>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::BondiJbar>;\n  using argument_tags = pre_swsh_derivative_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> jbar,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& j) {\n    get(*jbar) = conj(get(j));\n  }\n};\n\n/*!\n * \\brief Compute \\f$\\bar{U}\\f$.\n *\n * \\note Computing \\f$\\bar{U}\\f$ should be unnecessary in most execution\n * procedures, as all quantities should be derived from \\f$U\\f$ and its\n * derivatives followed by explicit conjugation operations, which are expected\n * to be sufficiently cheap to avoid the storage cost of recording the\n * conjugates.\n */\ntemplate <>\nstruct PreSwshDerivatives<Tags::BondiUbar> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiU>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::BondiUbar>;\n  using argument_tags = pre_swsh_derivative_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> ubar,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& u) {\n    get(*ubar) = conj(get(u));\n  }\n};\n\n/*!\n * Compute \\f$\\bar{Q}\\f$.\n *\n * \\note Computing \\f$\\bar{Q}\\f$ should be unnecessary in most execution\n * procedures, as all quantities should be derived from \\f$Q\\f$ and its\n * derivatives followed by explicit conjugation operations, which are expected\n * to be sufficiently cheap to avoid the storage cost of recording the\n * conjugates\n */\ntemplate <>\nstruct PreSwshDerivatives<Tags::BondiQbar> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiQ>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::BondiQbar>;\n  using argument_tags = pre_swsh_derivative_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> qbar,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& q) {\n    get(*qbar) = conj(get(q));\n  }\n};\n\n/// Compute the product of `Lhs` and `Rhs`.\ntemplate <typename Lhs, typename Rhs>\nstruct PreSwshDerivatives<::Tags::Multiplies<Lhs, Rhs>> {\n  using pre_swsh_derivative_tags = tmpl::list<Lhs, Rhs>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<::Tags::Multiplies<Lhs, Rhs>>;\n  using argument_tags = pre_swsh_derivative_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<\n          ComplexDataVector, ::Tags::Multiplies<Lhs, Rhs>::type::type::spin>>*>\n          result,\n      const Scalar<SpinWeighted<ComplexDataVector, Lhs::type::type::spin>>& lhs,\n      const Scalar<SpinWeighted<ComplexDataVector, Rhs::type::type::spin>>&\n          rhs) {\n    get(*result) = get(lhs) * get(rhs);\n  }\n};\n\n/*!\n * \\brief Compute the product of \\f$\\bar{J}\\f$ and the quantity represented by\n * `Rhs`.\n *\n * \\details In this function, \\f$\\bar{J}\\f$ is obtained via conjugation of\n * `Tags::BondiJ` inline, rather than accessing `Tags::BondiJbar` in storage.\n */\ntemplate <typename Rhs>\nstruct PreSwshDerivatives<::Tags::Multiplies<Tags::BondiJbar, Rhs>> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiJ, Rhs>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<::Tags::Multiplies<Tags::BondiJbar, Rhs>>;\n  using argument_tags = pre_swsh_derivative_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<\n          ComplexDataVector,\n          ::Tags::Multiplies<Tags::BondiJbar, Rhs>::type::type::spin>>*>\n          result,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& j,\n      const Scalar<SpinWeighted<ComplexDataVector, Rhs::type::type::spin>>&\n          rhs) {\n    get(*result) = conj(get(j)) * get(rhs);\n  }\n};\n\n/*!\n * \\brief Compute the product of \\f$\\bar{J}\\f$ and the quantity represented by\n * `Rhs`.\n *\n * \\details In this function, \\f$\\bar{J}\\f$ is obtained via conjugation of\n * `Tags::BondiJ` inline, rather than accessing `Tags::BondiJbar` in storage.\n */\ntemplate <typename Lhs>\nstruct PreSwshDerivatives<::Tags::Multiplies<Lhs, Tags::BondiJbar>> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiJ, Lhs>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<::Tags::Multiplies<Lhs, Tags::BondiJbar>>;\n  using argument_tags = pre_swsh_derivative_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<\n          ComplexDataVector,\n          ::Tags::Multiplies<Lhs, Tags::BondiJbar>::type::type::spin>>*>\n          result,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& j,\n      const Scalar<SpinWeighted<ComplexDataVector, Lhs::type::type::spin>>&\n          lhs) {\n    get(*result) = get(lhs) * conj(get(j));\n  }\n};\n\n/*!\n * \\brief Compute the product of \\f$\\bar{U}\\f$ and the quantity represented by\n * `Rhs`.\n *\n * \\details In this function, \\f$\\bar{U}\\f$ is obtained via conjugation of\n * `Tags::BondiU` inline, rather than accessing `Tags::BondiUbar` in storage.\n */\ntemplate <typename Rhs>\nstruct PreSwshDerivatives<::Tags::Multiplies<Tags::BondiUbar, Rhs>> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiU, Rhs>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<::Tags::Multiplies<Tags::BondiUbar, Rhs>>;\n  using argument_tags = pre_swsh_derivative_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<\n          ComplexDataVector,\n          ::Tags::Multiplies<Tags::BondiUbar, Rhs>::type::type::spin>>*>\n          result,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& u,\n      const Scalar<SpinWeighted<ComplexDataVector, Rhs::type::type::spin>>&\n          rhs) {\n    get(*result) = conj(get(u)) * get(rhs);\n  }\n};\n\n/*!\n * \\brief Compute \\f$\\bar{J} * (Q - 2 \\eth \\beta)\\f$.\n *\n * \\note the conjugates for this are accessed by their non-conjugate\n * counterparts (`Tags::BondiJ` and `Tags::BondiQ`) then conjugated inline\n */\ntemplate <>\nstruct PreSwshDerivatives<Tags::JbarQMinus2EthBeta> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiJ, Tags::BondiQ>;\n  using swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::JbarQMinus2EthBeta>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, swsh_derivative_tags>;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*>\n          jbar_q_minus_2_eth_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& j,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& q,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta) {\n    get(*jbar_q_minus_2_eth_beta) =\n        conj(get(j)) * (get(q) - 2.0 * get(eth_beta));\n  }\n};\n\n/// Compute \\f$\\exp(2 \\beta)\\f$\ntemplate <>\nstruct PreSwshDerivatives<Tags::Exp2Beta> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiBeta>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::Exp2Beta>;\n  using argument_tags = pre_swsh_derivative_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          exp_2_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta) {\n    get(*exp_2_beta).data() = exp(2.0 * get(beta).data());\n  }\n};\n\n/// Copies the values of the inertial retarded time into a spin-weighted\n/// container so that spin-weighted derivatives can be taken\ntemplate <>\nstruct PreSwshDerivatives<Tags::ComplexInertialRetardedTime> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::ComplexInertialRetardedTime>;\n  using argument_tags = tmpl::list<Tags::InertialRetardedTime>;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          complex_inertial_retarded_time,\n      const Scalar<DataVector>& inertial_retarded_time) {\n    get(*complex_inertial_retarded_time).data() =\n        std::complex<double>(1.0, 0.0) * get(inertial_retarded_time);\n  }\n};\n\n/// Compute the derivative of the quantity represented by `Tag` with respect to\n/// the numerical radial coordinate \\f$y\\f$.\ntemplate <typename Tag>\nstruct PreSwshDerivatives<Tags::Dy<Tag>> {\n  using pre_swsh_derivative_tags = tmpl::list<Tag>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::Dy<Tag>>;\n  using argument_tags =\n      tmpl::append<pre_swsh_derivative_tags, tmpl::list<Tags::LMax>>;\n\n  static void apply(\n      const gsl::not_null<\n          Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>*>\n          dy_val,\n      const Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>& val,\n      const size_t l_max) {\n    logical_partial_directional_derivative_of_complex(\n        make_not_null(&get(*dy_val).data()), get(val).data(),\n        Mesh<3>{\n            {{Spectral::Swsh::number_of_swsh_theta_collocation_points(l_max),\n              Spectral::Swsh::number_of_swsh_phi_collocation_points(l_max),\n              get(val).size() /\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max)}},\n            Spectral::Basis::Legendre,\n            Spectral::Quadrature::GaussLobatto},\n        // 2 for differentiating in y; coordinate ordering is:\n        // {\\phi, \\theta, y}.\n        2);\n  }\n};\n\n/*!\n * \\brief Computes the first derivative with respect to \\f$y\\f$ of\n * `Tags::BondiBeta`.\n *\n * \\details In a CCE evolution, the values of the first derivatives of\n * `Tags::BondiBeta` can just be copied from its integrand.\n */\ntemplate <>\nstruct PreSwshDerivatives<Tags::Dy<Tags::BondiBeta>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<Tags::Integrand<Tags::BondiBeta>>;\n\n  using return_tags = tmpl::list<Tags::Dy<Tags::BondiBeta>>;\n  using argument_tags = integrand_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> dy_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& integrand_beta) {\n    *dy_beta = integrand_beta;\n  }\n};\n\n/*!\n * \\brief Computes the first derivative with respect to \\f$y\\f$ of\n * `Tags::BondiU`.\n *\n * \\details In a CCE evolution, the values of the first derivatives of\n * `Tags::BondiU` can just be copied from its integrand.\n */\ntemplate <>\nstruct PreSwshDerivatives<Tags::Dy<Tags::BondiU>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<Tags::Integrand<Tags::BondiU>>;\n\n  using return_tags = tmpl::list<Tags::Dy<Tags::BondiU>>;\n  using argument_tags = integrand_tags;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> dy_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& integrand_u) {\n    *dy_u = integrand_u;\n  }\n};\n\n/// \\brief Compute \\f$\\partial_u J\\f$ from \\f$H\\f$ and the Jacobian factors.\ntemplate <>\nstruct PreSwshDerivatives<Tags::Du<Tags::BondiJ>> {\n  using pre_swsh_derivative_tags = tmpl::list<Tags::Dy<Tags::BondiJ>>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integrand_tags = tmpl::list<Tags::BondiH>;\n  using integration_independent_tags =\n      tmpl::list<Tags::DuRDividedByR, Tags::OneMinusY>;\n\n  using return_tags = tmpl::list<Tags::Du<Tags::BondiJ>>;\n  using argument_tags = tmpl::append<pre_swsh_derivative_tags, integrand_tags,\n                                     integration_independent_tags>;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> du_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& bondi_h,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& du_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y) {\n    get(*du_j) = get(bondi_h) -\n                 get(du_r_divided_by_r) * get(one_minus_y) * get(dy_bondi_j);\n  }\n};\n\n/*!\n * \\brief Compute the derivative with respect to the numerical radial coordinate\n * \\f$y\\f$ of a quantity which is a spin-weighted spherical harmonic\n * derivative.\n *\n * \\details  This is separate from the generic case of a derivative with\n * respect to \\f$y\\f$ because the included type aliases arrange the input tags\n * in different categories.\n */\ntemplate <typename Tag, typename DerivKind>\nstruct PreSwshDerivatives<\n    Tags::Dy<Spectral::Swsh::Tags::Derivative<Tag, DerivKind>>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<Tag, DerivKind>>;\n  using integrand_tags = tmpl::list<>;\n\n  using return_tags =\n      tmpl::list<Tags::Dy<Spectral::Swsh::Tags::Derivative<Tag, DerivKind>>>;\n  using argument_tags =\n      tmpl::append<swsh_derivative_tags, tmpl::list<Tags::LMax>>;\n\n  static constexpr int spin =\n      Spectral::Swsh::Tags::Derivative<Tag, DerivKind>::spin;\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, spin>>*>\n          dy_val,\n      const Scalar<SpinWeighted<ComplexDataVector, spin>>& val,\n      const size_t l_max) {\n    logical_partial_directional_derivative_of_complex(\n        make_not_null(&get(*dy_val).data()), get(val).data(),\n        Mesh<3>{\n            {{Spectral::Swsh::number_of_swsh_theta_collocation_points(l_max),\n              Spectral::Swsh::number_of_swsh_phi_collocation_points(l_max),\n              get(val).size() /\n                  Spectral::Swsh::number_of_swsh_collocation_points(l_max)}},\n            Spectral::Basis::Legendre,\n            Spectral::Quadrature::GaussLobatto},\n        // 2 for differentiating in y; coordinate ordering is:\n        // {\\phi, \\theta, y}.\n        2);\n  }\n};\n\n/*!\n * \\brief Evaluates the set of inputs to the CCE integrand for\n * `BondiValueTag` that do not involve spin-weighted angular differentiation.\n *\n * \\details This function is to be called on the `DataBox` holding the relevant\n * CCE data on each hypersurface integration step, prior to evaluating the\n * spin-weighted derivatives needed for the same CCE integrand. Provided a\n * `DataBox` with the appropriate tags (including\n * `all_pre_swsh_derivative_tags`, `all_swsh_derivative_tags` and\n * `Tags::LMax`), this function will apply all of the necessary\n * mutations to update `all_pre_swsh_derivatives_for_tag<BondiValueTag>` to\n * their correct values for the current values for the remaining (input) tags.\n */\ntemplate <typename BondiValueTag, typename DataBoxType>\nvoid mutate_all_pre_swsh_derivatives_for_tag(\n    const gsl::not_null<DataBoxType*> box) {\n  tmpl::for_each<pre_swsh_derivative_tags_to_compute_for_t<BondiValueTag>>(\n      [&box](auto pre_swsh_derivative_tag_v) {\n        using pre_swsh_derivative_tag =\n            typename decltype(pre_swsh_derivative_tag_v)::type;\n        using mutation = PreSwshDerivatives<pre_swsh_derivative_tag>;\n        db::mutate_apply<mutation>(box);\n      });\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/PrecomputeCceDependencies.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\n\nnamespace detail {\ntemplate <typename DerivKind>\nvoid angular_derivative_of_r_divided_by_r_impl(\n    const gsl::not_null<\n        SpinWeighted<ComplexDataVector,\n                     Spectral::Swsh::Tags::derivative_spin_weight<DerivKind>>*>\n        d_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& boundary_r, const size_t l_max,\n    const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  // first set the first angular view\n  SpinWeighted<ComplexDataVector,\n               Spectral::Swsh::Tags::derivative_spin_weight<DerivKind>>\n      d_r_divided_by_r_boundary{\n          {d_r_divided_by_r->data().data(), number_of_angular_points}};\n  Spectral::Swsh::angular_derivatives<tmpl::list<DerivKind>>(\n      l_max, 1, make_not_null(&d_r_divided_by_r_boundary), boundary_r);\n  d_r_divided_by_r_boundary.data() /= boundary_r.data();\n  // all of the angular shells after the innermost one\n  ComplexDataVector d_r_divided_by_r_tail_shells{\n      d_r_divided_by_r->data().data() + number_of_angular_points,\n      (number_of_radial_points - 1) * number_of_angular_points};\n  fill_with_n_copies(make_not_null(&d_r_divided_by_r_tail_shells),\n                     d_r_divided_by_r_boundary.data(),\n                     number_of_radial_points - 1);\n}\n}  // namespace detail\n\nnamespace detail {\n// explicit  template instantiations\ntemplate void\nangular_derivative_of_r_divided_by_r_impl<Spectral::Swsh::Tags::Eth>(\n    const gsl::not_null<SpinWeighted<\n        ComplexDataVector, Spectral::Swsh::Tags::derivative_spin_weight<\n                               Spectral::Swsh::Tags::Eth>>*>\n        d_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& boundary_r, const size_t l_max,\n    const size_t number_of_radial_points);\n\ntemplate void\nangular_derivative_of_r_divided_by_r_impl<Spectral::Swsh::Tags::EthEth>(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 2>*> d_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& boundary_r, const size_t l_max,\n    const size_t number_of_radial_points);\n\ntemplate void\nangular_derivative_of_r_divided_by_r_impl<Spectral::Swsh::Tags::EthEthbar>(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, 0>*> d_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& boundary_r, const size_t l_max,\n    const size_t number_of_radial_points);\n}  // namespace detail\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/PrecomputeCceDependencies.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\n\nnamespace detail {\n// A convenience function for computing the spin-weighted derivatives of \\f$R\\f$\n// divided by \\f$R\\f$, which appears often in Jacobians to transform between\n// Bondi coordinates and the numerical coordinates used in CCE.\ntemplate <typename DerivKind>\nvoid angular_derivative_of_r_divided_by_r_impl(\n    gsl::not_null<\n        SpinWeighted<ComplexDataVector,\n                     Spectral::Swsh::Tags::derivative_spin_weight<DerivKind>>*>\n        d_r_divided_by_r,\n    const SpinWeighted<ComplexDataVector, 0>& boundary_r, size_t l_max,\n    size_t number_of_radial_points);\n\n}  // namespace detail\n\n/*!\n * \\brief A set of procedures for computing the set of inputs to the CCE\n * integrand computations that can be computed before any of the intermediate\n * integrands are evaluated.\n *\n * \\details The template specializations of this template are\n * compatible with acting as a the mutator in a \\ref DataBoxGroup\n * `db::mutate_apply` operation. For flexibility in defining the \\ref\n * DataBoxGroup structure, the tags for `Tensor`s used in these functions are\n * also organized into type lists:\n * -  type alias `integration_independent_tags`: with a subset of\n * `Cce::pre_computation_tags`, used for both input and output.\n * -  type alias `boundary_values`: with a subset of\n *   `Cce::pre_computation_boundary_tags`, used only for input.\n * - type alias `pre_swsh_derivatives` containing hypersurface quantities. For\n * this struct, it will only ever contain `Cce::Tags::BondiJ`, and is used as\n * input.\n *\n * The `BoundaryPrefix` tag allows easy switching between the\n * regularity-preserving version and standard CCE\n *\n */\ntemplate <template <typename> class BoundaryPrefix, typename Tag>\nstruct PrecomputeCceDependencies;\n\n/// Computes \\f$1 - y\\f$ for the CCE system.\ntemplate <template <typename> class BoundaryPrefix>\nstruct PrecomputeCceDependencies<BoundaryPrefix, Tags::OneMinusY> {\n  using boundary_tags = tmpl::list<>;\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::OneMinusY>;\n  using argument_tags = tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          one_minus_y,\n      const size_t l_max, const size_t number_of_radial_points) {\n    const size_t number_of_angular_points =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n    const DataVector one_minus_y_collocation =\n        1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                           Spectral::Quadrature::GaussLobatto>(\n                  number_of_radial_points);\n    // iterate through the angular 'chunks' and set them to their 1-y value\n    for (size_t i = 0; i < number_of_radial_points; ++i) {\n      ComplexDataVector angular_view{\n          get(*one_minus_y).data().data() + number_of_angular_points * i,\n          number_of_angular_points};\n      angular_view = one_minus_y_collocation[i];\n    }\n  }\n};\n\n/// Computes a volume version of Bondi radius of the worldtube \\f$R\\f$ from its\n/// boundary value (by repeating it over the radial dimension)\ntemplate <template <typename> class BoundaryPrefix>\nstruct PrecomputeCceDependencies<BoundaryPrefix, Tags::BondiR> {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tags::BondiR>>;\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::BondiR>;\n  using argument_tags =\n      tmpl::append<boundary_tags, tmpl::list<Tags::NumberOfRadialPoints>>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      const size_t number_of_radial_points) {\n    fill_with_n_copies(make_not_null(&get(*r).data()), get(boundary_r).data(),\n                       number_of_radial_points);\n  }\n};\n\n/// Computes \\f$\\partial_u R / R\\f$ from its boundary value (by repeating it\n/// over the radial dimension).\ntemplate <template <typename> class BoundaryPrefix>\nstruct PrecomputeCceDependencies<BoundaryPrefix, Tags::DuRDividedByR> {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tags::DuRDividedByR>>;\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::DuRDividedByR>;\n  using argument_tags =\n      tmpl::append<boundary_tags, tmpl::list<Tags::NumberOfRadialPoints>>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          du_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&\n          boundary_du_r_divided_by_r,\n      const size_t number_of_radial_points) {\n    fill_with_n_copies(make_not_null(&get(*du_r_divided_by_r).data()),\n                       get(boundary_du_r_divided_by_r).data(),\n                       number_of_radial_points);\n  }\n};\n\n/// Computes \\f$\\eth R / R\\f$ by differentiating and repeating the boundary\n/// value of \\f$R\\f$.\ntemplate <template <typename> class BoundaryPrefix>\nstruct PrecomputeCceDependencies<BoundaryPrefix, Tags::EthRDividedByR> {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tags::BondiR>>;\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::EthRDividedByR>;\n  using argument_tags =\n      tmpl::append<boundary_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          eth_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      const size_t l_max, const size_t number_of_radial_points) {\n    detail::angular_derivative_of_r_divided_by_r_impl<\n        Spectral::Swsh::Tags::Eth>(make_not_null(&get(*eth_r_divided_by_r)),\n                                   get(boundary_r), l_max,\n                                   number_of_radial_points);\n  }\n};\n\n/// Computes \\f$\\eth \\eth R / R\\f$ by differentiating and repeating the boundary\n/// value of \\f$R\\f$.\ntemplate <template <typename> class BoundaryPrefix>\nstruct PrecomputeCceDependencies<BoundaryPrefix, Tags::EthEthRDividedByR> {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tags::BondiR>>;\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::EthEthRDividedByR>;\n  using argument_tags =\n      tmpl::append<boundary_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          eth_eth_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      const size_t l_max, const size_t number_of_radial_points) {\n    detail::angular_derivative_of_r_divided_by_r_impl<\n        Spectral::Swsh::Tags::EthEth>(\n        make_not_null(&get(*eth_eth_r_divided_by_r)), get(boundary_r), l_max,\n        number_of_radial_points);\n  }\n};\n\n/// Computes \\f$\\eth \\bar{\\eth} R / R\\f$ by differentiating and repeating the\n/// boundary value of \\f$R\\f$.\ntemplate <template <typename> class BoundaryPrefix>\nstruct PrecomputeCceDependencies<BoundaryPrefix, Tags::EthEthbarRDividedByR> {\n  using boundary_tags = tmpl::list<BoundaryPrefix<Tags::BondiR>>;\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::EthEthbarRDividedByR>;\n  using argument_tags =\n      tmpl::append<boundary_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          eth_ethbar_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      const size_t l_max, const size_t number_of_radial_points) {\n    detail::angular_derivative_of_r_divided_by_r_impl<\n        Spectral::Swsh::Tags::EthEthbar>(\n        make_not_null(&get(*eth_ethbar_r_divided_by_r)), get(boundary_r), l_max,\n        number_of_radial_points);\n  }\n};\n\n/// Computes \\f$K = \\sqrt{1 + J \\bar{J}}\\f$.\ntemplate <template <typename> class BoundaryPrefix>\nstruct PrecomputeCceDependencies<BoundaryPrefix, Tags::BondiK> {\n  using boundary_tags = tmpl::list<>;\n  using pre_swsh_derivative_tags = tmpl::list<Tags::BondiJ>;\n  using integration_independent_tags = tmpl::list<>;\n\n  using return_tags = tmpl::list<Tags::BondiK>;\n  using argument_tags = tmpl::push_front<pre_swsh_derivative_tags, Tags::LMax>;\n\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> k,\n      const size_t /*l_max*/,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& j) {\n    get(*k).data() = sqrt(1.0 + get(j).data() * conj(get(j)).data());\n  }\n};\n\n/*!\n * \\brief Convenience routine for computing all of the CCE inputs to integrand\n * computation that do not depend on intermediate integrand results. It should\n * be executed before moving through the hierarchy of integrands.\n *\n * \\details Provided a \\ref DataBoxGroup with the appropriate tags (including\n * `Cce::pre_computation_boundary_tags`, `Cce::pre_computation_tags`,\n * `Cce::Tags::BondiJ` and `Tags::LMax`), this function will\n * apply all of the necessary mutations to update the\n * `Cce::pre_computation_tags` to their correct values for the current values\n * for the remaining (input) tags.\n *\n * The `BoundaryPrefix` template template parameter is to be passed a prefix\n * tag associated with the boundary value prefix used in the computation (e.g.\n * `Cce::Tags::BoundaryValue`), and allows easy switching between the\n * regularity-preserving version and standard CCE.\n */\ntemplate <template <typename> class BoundaryPrefix, typename DataBoxType>\nvoid mutate_all_precompute_cce_dependencies(\n    const gsl::not_null<DataBoxType*> box) {\n  tmpl::for_each<pre_computation_tags>([&box](auto x) {\n    using integration_independent_tag = typename decltype(x)::type;\n    using mutation =\n        PrecomputeCceDependencies<BoundaryPrefix, integration_independent_tag>;\n    db::mutate_apply<mutation>(box);\n  });\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/ReceiveTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iomanip>\n#include <sstream>\n#include <string>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\nnamespace Cce {\n/// \\brief Tags used by CCE for communication.\nnamespace ReceiveTags {\n\n/// A receive tag for the data sent to the CCE evolution component from the CCE\n/// boundary component\ntemplate <typename CommunicationTagList>\nstruct BoundaryData\n    : Parallel::InboxInserters::Value<BoundaryData<CommunicationTagList>> {\n  using temporal_id = TimeStepId;\n  using type = std::unordered_map<temporal_id, Variables<CommunicationTagList>>;\n\n  static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n\n    ss << std::scientific << std::setprecision(16);\n    ss << pad << \"CceBoundaryDataInbox:\\n\";\n    // We don't really care about the variables, just the times\n    for (const auto& [current_time_step_id, variables] : inbox) {\n      (void)variables;\n      ss << pad << \" Time: \" << current_time_step_id << \"\\n\";\n    }\n\n    return ss.str();\n  }\n};\n\n}  // namespace ReceiveTags\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/ScriPlusInterpolationManager.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <complex>\n#include <cstddef>\n#include <deque>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename LhsTag, typename RhsTag>\nstruct Multiplies;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Cce {\n\n/*!\n * \\brief Stores necessary data and interpolates on to new time points at scri+.\n *\n * \\details The coordinate time used for the CCE evolution is not the same as\n * the asymptotic inertial retarded time, which is determined through a separate\n * evolution equation. This class manages the scri+ data passed in (via\n * `insert_data()`) along with the set of inertial retarded times associated\n * with that data, and interpolates to a set of requested times (supplied via\n * `insert_target_time()`).\n *\n * Template parameters:\n * - `VectorTypeToInterpolate`: the vector type associated with the values to\n * interpolate.\n * - `Tag`: The tag associated with the interpolation procedure. This determines\n * the behavior of the interpolation return value. The default is just\n * interpolation, if `Tag` has prefix `::Tags::Multiplies` or `Tags::Du`, the\n * interpolator performs the additional multiplication or time derivative as a\n * step in the interpolation procedure.\n */\ntemplate <typename VectorTypeToInterpolate, typename Tag>\nstruct ScriPlusInterpolationManager {\n public:\n  ScriPlusInterpolationManager() = default;\n\n  ScriPlusInterpolationManager(\n      const size_t target_number_of_points, const size_t vector_size,\n      std::unique_ptr<intrp::SpanInterpolator> interpolator)\n      : vector_size_{vector_size},\n        target_number_of_points_{target_number_of_points},\n        interpolator_{std::move(interpolator)} {}\n\n  /// \\brief provide data to the interpolation manager.\n  ///\n  /// \\details `u_bondi` is a vector of inertial times, and `to_interpolate` is\n  /// the vector of values that will be interpolated to target times.\n  void insert_data(const DataVector& u_bondi,\n                   const VectorTypeToInterpolate& to_interpolate) {\n    ASSERT(to_interpolate.size() == vector_size_,\n           \"Inserted data must be of size specified at construction: \"\n               << vector_size_\n               << \" and provided data is of size: \" << to_interpolate.size());\n    u_bondi_values_.push_back(u_bondi);\n    to_interpolate_values_.push_back(to_interpolate);\n    u_bondi_ranges_.emplace_back(min(u_bondi), max(u_bondi));\n  }\n\n  /// \\brief Request a target time to be interpolated to when enough data has\n  /// been accumulated.\n  ///\n  /// For optimization, we assume that these are inserted in ascending order.\n  void insert_target_time(const double time) {\n    target_times_.push_back(time);\n    // if this is the first time in the deque, we should use it to determine\n    // whether some of the old data is ready to be removed. In other cases, this\n    // check can be performed when a time is removed from the queue via\n    // `interpolate_and_pop_first_time`\n    if (target_times_.size() == 1) {\n      remove_unneeded_early_times();\n    }\n  }\n\n  /// \\brief Determines whether enough data before and after the first time in\n  /// the target time queue has been provided to interpolate.\n  ///\n  /// \\details If possible, this function will require that the target time to\n  /// be interpolated is reasonably centered on the range, but will settle for\n  /// non-centered data if the time is too early for the given data, which is\n  /// necessary to get the first couple of times out of the simulation. This\n  /// function always returns false if all of the provided data is earlier than\n  /// the next target time, indicating that the caller should wait until more\n  /// data has been provided before interpolating.\n  bool first_time_is_ready_to_interpolate() const;\n\n  const std::deque<std::pair<double, double>>& get_u_bondi_ranges() const {\n    return u_bondi_ranges_;\n  }\n\n  /// \\brief Interpolate to the first target time in the queue, returning both\n  /// the time and the interpolated data at that time.\n  ///\n  /// \\note If this function is not able to interpolate to the full accuracy\n  /// using a centered stencil, it will perform the best interpolation\n  /// available.\n  ///\n  /// \\warning This function will extrapolate if the target times are\n  /// outside the range of data the class has been provided. This is intentional\n  /// to support small extrapolation at the end of a simulation when no further\n  /// data is available, but for full accuracy, check\n  /// `first_time_is_ready_to_interpolate` before calling the interpolation\n  /// functions\n  std::pair<double, VectorTypeToInterpolate> interpolate_first_time();\n\n  /// \\brief Interpolate to the first target time in the queue, returning both\n  /// the time and the interpolated data at that time, and remove the first time\n  /// from the queue.\n  ///\n  /// \\note If this function is not able to interpolate to the full accuracy\n  /// using a centered stencil, it will perform the best interpolation\n  /// available.\n  ///\n  /// \\warning This function will extrapolate if the target times are\n  /// outside the range of data the class has been provided. This is intentional\n  /// to support small extrapolation at the end of a simulation when no further\n  /// data is available, but for full accuracy, check\n  /// `first_time_is_ready_to_interpolate` before calling the interpolation\n  /// functions\n  std::pair<double, VectorTypeToInterpolate> interpolate_and_pop_first_time();\n\n  /// \\brief return the number of times in the target times queue\n  size_t number_of_target_times() const { return target_times_.size(); }\n\n  /// \\brief return the number of data points that have been provided to the\n  /// interpolation manager\n  size_t number_of_data_points() const { return u_bondi_ranges_.size(); }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | u_bondi_values_;\n    p | to_interpolate_values_;\n    p | u_bondi_ranges_;\n    p | target_times_;\n    p | vector_size_;\n    p | target_number_of_points_;\n    p | interpolator_;\n  }\n\n private:\n  void remove_unneeded_early_times();\n\n  friend struct ScriPlusInterpolationManager<VectorTypeToInterpolate,\n                                             Tags::Du<Tag>>;\n\n  std::deque<DataVector> u_bondi_values_;\n  std::deque<VectorTypeToInterpolate> to_interpolate_values_;\n  std::deque<std::pair<double, double>> u_bondi_ranges_;\n  std::deque<double> target_times_;\n  size_t vector_size_ = 0_st;\n  size_t target_number_of_points_ = 0_st;\n  std::unique_ptr<intrp::SpanInterpolator> interpolator_;\n};\n\ntemplate <typename VectorTypeToInterpolate, typename Tag>\nbool ScriPlusInterpolationManager<\n    VectorTypeToInterpolate, Tag>::first_time_is_ready_to_interpolate() const {\n  if (target_times_.empty()) {\n    return false;\n  }\n  auto maxes_below = alg::count_if(\n      u_bondi_ranges_, [this](const std::pair<double, double> time) {\n        return time.second <= target_times_.front();\n      });\n  auto mins_above = alg::count_if(u_bondi_ranges_,\n                                  [this](const std::pair<double, double> time) {\n                                    return time.first > target_times_.front();\n                                  });\n\n  // we might ask for a time that's too close to the end or the beginning of\n  // our data, in which case we will settle for at least one point below and\n  // above and a sufficient number of total points.\n  // This will always be `false` if the earliest target time is later than all\n  // provided data points, which would require extrapolation\n  return ((static_cast<size_t>(maxes_below) > target_number_of_points_ or\n           static_cast<size_t>(maxes_below + mins_above) >\n               2 * target_number_of_points_) and\n          static_cast<size_t>(mins_above) > target_number_of_points_);\n}\n\ntemplate <typename VectorTypeToInterpolate, typename Tag>\nstd::pair<double, VectorTypeToInterpolate> ScriPlusInterpolationManager<\n    VectorTypeToInterpolate, Tag>::interpolate_first_time() {\n  if (target_times_.empty()) {\n    ERROR(\"There are no target times to interpolate.\");\n  }\n  if (to_interpolate_values_.size() < 2 * target_number_of_points_) {\n    ERROR(\"Insufficient data points to continue interpolation: have \"\n          << to_interpolate_values_.size() << \", need at least\"\n          << 2 * target_number_of_points_);\n  }\n\n  VectorTypeToInterpolate result{vector_size_};\n  const size_t interpolation_data_size = to_interpolate_values_.size();\n\n  VectorTypeToInterpolate interpolation_values{2 * target_number_of_points_};\n  DataVector interpolation_times{2 * target_number_of_points_};\n  for (size_t i = 0; i < vector_size_; ++i) {\n    // binary search assumes times placed in sorted order\n    auto upper_bound_offset = static_cast<size_t>(std::distance(\n        u_bondi_values_.begin(),\n        std::upper_bound(u_bondi_values_.begin(), u_bondi_values_.end(),\n                         target_times_.front(),\n                         [&i](const double rhs, const DataVector& lhs) {\n                           return rhs < lhs[i];\n                         })));\n    size_t lower_bound_offset =\n        upper_bound_offset == 0 ? 0 : upper_bound_offset - 1;\n\n    if (upper_bound_offset + target_number_of_points_ >\n        interpolation_data_size) {\n      upper_bound_offset = interpolation_data_size;\n      lower_bound_offset =\n          interpolation_data_size - 2 * target_number_of_points_;\n    } else if (lower_bound_offset < target_number_of_points_ - 1) {\n      lower_bound_offset = 0;\n      upper_bound_offset = 2 * target_number_of_points_;\n    } else {\n      lower_bound_offset = lower_bound_offset + 1 - target_number_of_points_;\n      upper_bound_offset = lower_bound_offset + 2 * target_number_of_points_;\n    }\n    auto interpolation_values_begin =\n        to_interpolate_values_.begin() +\n        static_cast<ptrdiff_t>(lower_bound_offset);\n    auto interpolation_times_begin =\n        u_bondi_values_.begin() + static_cast<ptrdiff_t>(lower_bound_offset);\n    auto interpolation_times_end =\n        u_bondi_values_.begin() + static_cast<ptrdiff_t>(upper_bound_offset);\n\n    // interpolate using the data sets in the restricted iterators\n    auto value_it = interpolation_values_begin;\n    size_t vector_position = 0;\n    for (auto time_it = interpolation_times_begin;\n         time_it != interpolation_times_end;\n         ++time_it, ++value_it, ++vector_position) {\n      interpolation_values[vector_position] = (*value_it)[i];\n      interpolation_times[vector_position] = (*time_it)[i];\n    }\n    result[i] = interpolator_->interpolate(\n        gsl::span<const double>(interpolation_times.data(),\n                                interpolation_times.size()),\n        gsl::span<const typename VectorTypeToInterpolate::value_type>(\n            interpolation_values.data(), interpolation_values.size()),\n        target_times_.front());\n  }\n  return std::make_pair(target_times_.front(), std::move(result));\n}\n\ntemplate <typename VectorTypeToInterpolate, typename Tag>\nstd::pair<double, VectorTypeToInterpolate> ScriPlusInterpolationManager<\n    VectorTypeToInterpolate, Tag>::interpolate_and_pop_first_time() {\n  std::pair<double, VectorTypeToInterpolate> interpolated =\n      interpolate_first_time();\n  target_times_.pop_front();\n\n  if (not target_times_.empty()) {\n    remove_unneeded_early_times();\n  }\n  return interpolated;\n}\n\ntemplate <typename VectorTypeToInterpolate, typename Tag>\nvoid ScriPlusInterpolationManager<VectorTypeToInterpolate,\n                                  Tag>::remove_unneeded_early_times() {\n  // pop times we no longer need because their maxes are too far in the past\n  auto time_it = u_bondi_ranges_.begin();\n  size_t times_counter = 0;\n  while (time_it < u_bondi_ranges_.end() and\n         (*time_it).second < target_times_.front()) {\n    if (times_counter > target_number_of_points_ and\n        u_bondi_ranges_.size() >= 2 * target_number_of_points_) {\n      u_bondi_ranges_.pop_front();\n      u_bondi_values_.pop_front();\n      to_interpolate_values_.pop_front();\n    } else {\n      ++times_counter;\n    }\n    ++time_it;\n  }\n}\n\n/*!\n * \\brief Stores necessary data and interpolates on to new time points at scri+,\n * multiplying two results together before supplying the result.\n *\n * \\details The coordinate time used for the CCE evolution is not the same as\n * the asymptotic inertial retarded time, which is determined through a separate\n * evolution equation. This class manages the two sets of  scri+ data passed in\n * (via `insert_data()`) along with the set of inertial retarded times\n * associated with that data, and interpolates to a set of inertial requested\n * times (supplied via `insert_target_time()`), multiplying the two\n * interpolation results together before returning.\n *\n * Template parameters:\n * - `VectorTypeToInterpolate`: the vector type associated with the values to\n * interpolate.\n * - `::Tags::Multiplies<MultipliesLhs, MultipliesRhs>`: The tag associated with\n * the interpolation procedure. This determines the behavior of the\n * interpolation return value. The default is just interpolation, if `Tag` has\n * prefix  `::Tags::Multiplies` (this case) or `Tags::Du`, the interpolator\n * performs the additional multiplication or time derivative as a step in the\n * interpolation procedure.\n */\ntemplate <typename VectorTypeToInterpolate, typename MultipliesLhs,\n          typename MultipliesRhs>\nstruct ScriPlusInterpolationManager<\n    VectorTypeToInterpolate, ::Tags::Multiplies<MultipliesLhs, MultipliesRhs>> {\n public:\n  ScriPlusInterpolationManager() = default;\n  ScriPlusInterpolationManager(\n      const size_t target_number_of_points, const size_t vector_size,\n      std::unique_ptr<intrp::SpanInterpolator> interpolator)\n      : interpolation_manager_lhs_{target_number_of_points, vector_size,\n                                   interpolator->get_clone()},\n        interpolation_manager_rhs_{target_number_of_points, vector_size,\n                                   std::move(interpolator)} {}\n\n  /// \\brief provide data to the interpolation manager.\n  ///\n  /// \\details `u_bondi` is a vector of inertial times, and `to_interpolate_lhs`\n  /// and `to_interpolate_rhs` are the vector of values that will be\n  /// interpolated to target times. The interpolation result will be the product\n  /// of the interpolated lhs and rhs vectors.\n  void insert_data(const DataVector& u_bondi,\n                   const VectorTypeToInterpolate& to_interpolate_lhs,\n                   const VectorTypeToInterpolate& to_interpolate_rhs) {\n    interpolation_manager_lhs_.insert_data(u_bondi, to_interpolate_lhs);\n    interpolation_manager_rhs_.insert_data(u_bondi, to_interpolate_rhs);\n  }\n\n  /// \\brief Request a target time to be interpolated to when enough data has\n  /// been accumulated.\n  ///\n  /// For optimization, we assume that these are inserted in ascending order.\n  void insert_target_time(const double time) {\n    interpolation_manager_lhs_.insert_target_time(time);\n    interpolation_manager_rhs_.insert_target_time(time);\n  }\n\n  /// \\brief Determines whether enough data before and after the first time in\n  /// the target time queue has been provided to interpolate.\n  ///\n  /// \\details If possible, this function will require that the target time to\n  /// be interpolated is reasonably centered on the range, but will settle for\n  /// non-centered data if the time is too early for the given data, which is\n  /// necessary to get the first couple of times out of the simulation. This\n  /// function always returns false if all of the provided data is earlier than\n  /// the next target time, indicating that the caller should wait until more\n  /// data has been provided before interpolating.\n  bool first_time_is_ready_to_interpolate() const {\n    return interpolation_manager_lhs_.first_time_is_ready_to_interpolate() and\n           interpolation_manager_rhs_.first_time_is_ready_to_interpolate();\n  }\n\n  const std::deque<std::pair<double, double>>& get_u_bondi_ranges() const {\n    return interpolation_manager_lhs_.get_u_bondi_ranges();\n  }\n\n  /// \\brief Interpolate to the first target time in the queue, returning both\n  /// the time and the interpolated-and-multiplied data at that time.\n  ///\n  /// \\note If this function is not able to interpolate to the full accuracy\n  /// using a centered stencil, it will perform the best interpolation\n  /// available.\n  ///\n  /// \\warning This function will extrapolate if the target times are\n  /// outside the range of data the class has been provided. This is intentional\n  /// to support small extrapolation at the end of a simulation when no further\n  /// data is available, but for full accuracy, check\n  /// `first_time_is_ready_to_interpolate` before calling the interpolation\n  /// functions\n  std::pair<double, VectorTypeToInterpolate> interpolate_first_time() {\n    const auto lhs_interpolation =\n        interpolation_manager_lhs_.interpolate_first_time();\n    const auto rhs_interpolation =\n        interpolation_manager_rhs_.interpolate_first_time();\n    return std::make_pair(lhs_interpolation.first,\n                          lhs_interpolation.second * rhs_interpolation.second);\n  }\n\n  /// \\brief Interpolate to the first target time in the queue, returning both\n  /// the time and the interpolated-and-multiplied data at that time, and remove\n  /// the first time from the queue.\n  ///\n  /// \\note If this function is not able to interpolate to the full accuracy\n  /// using a centered stencil, it will perform the best interpolation\n  /// available.\n  ///\n  /// \\warning This function will extrapolate if the target times are\n  /// outside the range of data the class has been provided. This is intentional\n  /// to support small extrapolation at the end of a simulation when no further\n  /// data is available, but for full accuracy, check\n  /// `first_time_is_ready_to_interpolate` before calling the interpolation\n  /// functions\n  std::pair<double, VectorTypeToInterpolate> interpolate_and_pop_first_time() {\n    const auto lhs_interpolation =\n        interpolation_manager_lhs_.interpolate_and_pop_first_time();\n    const auto rhs_interpolation =\n        interpolation_manager_rhs_.interpolate_and_pop_first_time();\n    return std::make_pair(lhs_interpolation.first,\n                          lhs_interpolation.second * rhs_interpolation.second);\n  }\n\n  /// \\brief return the number of times in the target times queue\n  size_t number_of_target_times() const {\n    return interpolation_manager_lhs_.number_of_target_times();\n  }\n\n  /// \\brief return the number of data points that have been provided to the\n  /// interpolation manager\n  size_t number_of_data_points() const {\n    return interpolation_manager_lhs_.number_of_data_points();\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | interpolation_manager_lhs_;\n    p | interpolation_manager_rhs_;\n  }\n\n private:\n  /// \\cond\n  ScriPlusInterpolationManager<VectorTypeToInterpolate, MultipliesLhs>\n      interpolation_manager_lhs_;\n  ScriPlusInterpolationManager<VectorTypeToInterpolate, MultipliesRhs>\n      interpolation_manager_rhs_;\n  /// \\endcond\n};\n\n/*!\n * \\brief Stores necessary data and interpolates on to new time points at scri+,\n * differentiating before supplying the result.\n *\n * \\details The coordinate time used for the CCE evolution is not the same as\n * the asymptotic inertial retarded time, which is determined through a separate\n * evolution equation. This class manages the scri+ data passed in\n * (via `insert_data()`) along with the set of inertial retarded times\n * associated with that data, and interpolates to a set of requested times\n * (supplied via `insert_target_time()`), differentiating the interpolation\n * before returning.\n *\n * Template parameters:\n * - `VectorTypeToInterpolate`: the vector type associated with the values to\n * interpolate.\n * - `Tags::Du<Tag>`: The tag associated with the interpolation procedure. This\n * determines the behavior of the interpolation return value. The default is\n * just interpolation, if `Tag` has prefix  `::Tags::Multiplies` or `Tags::Du`\n * (this case), the interpolator performs the additional multiplication or time\n * derivative as a step in the interpolation procedure.\n */\ntemplate <typename VectorTypeToInterpolate, typename Tag>\nstruct ScriPlusInterpolationManager<VectorTypeToInterpolate, Tags::Du<Tag>> {\n public:\n  ScriPlusInterpolationManager() = default;\n  ScriPlusInterpolationManager(\n      const size_t target_number_of_points, const size_t vector_size,\n      std::unique_ptr<intrp::SpanInterpolator> interpolator)\n      : argument_interpolation_manager_{target_number_of_points, vector_size,\n                                        std::move(interpolator)} {}\n\n  /// \\brief provide data to the interpolation manager.\n  ///\n  /// \\details `u_bondi` is a vector of\n  /// inertial times, and `to_interpolate_argument` is the vector of values that\n  /// will be interpolated to target times and differentiated.\n  void insert_data(const DataVector& u_bondi,\n                   const VectorTypeToInterpolate& to_interpolate_argument) {\n    argument_interpolation_manager_.insert_data(u_bondi,\n                                                to_interpolate_argument);\n  }\n\n  /// \\brief Request a target time to be interpolated to when enough data has\n  /// been accumulated.\n  ///\n  /// For optimization, we assume that these are inserted in ascending order.\n  void insert_target_time(const double time) {\n    argument_interpolation_manager_.insert_target_time(time);\n  }\n\n  /// \\brief Determines whether enough data before and after the first time in\n  /// the target time queue has been provided to interpolate.\n  ///\n  /// \\details If possible, this function will require that the target time to\n  /// be interpolated is reasonably centered on the range, but will settle for\n  /// non-centered data if the time is too early for the given data, which is\n  /// necessary to get the first couple of times out of the simulation. This\n  /// function always returns false if all of the provided data is earlier than\n  /// the next target time, indicating that the caller should wait until more\n  /// data has been provided before interpolating.\n  bool first_time_is_ready_to_interpolate() const {\n    return argument_interpolation_manager_.first_time_is_ready_to_interpolate();\n  }\n\n  const std::deque<std::pair<double, double>>& get_u_bondi_ranges() const {\n    return argument_interpolation_manager_.get_u_bondi_ranges();\n  }\n\n  /// \\brief Interpolate to the first target time in the queue, returning both\n  /// the time and the interpolated data at that time.\n  ///\n  /// \\note If this function is not able to interpolate to the full accuracy\n  /// using a centered stencil, it will perform the best interpolation\n  /// available.\n  ///\n  /// \\warning This function will extrapolate if the target times are\n  /// outside the range of data the class has been provided. This is intentional\n  /// to support small extrapolation at the end of a simulation when no further\n  /// data is available, but for full accuracy, check\n  /// `first_time_is_ready_to_interpolate` before calling the interpolation\n  /// functions\n  std::pair<double, VectorTypeToInterpolate> interpolate_first_time();\n\n  /// \\brief Interpolate to the first target time in the queue, returning both\n  /// the time and the interpolated data at that time, and remove the first time\n  /// from the queue.\n  ///\n  /// \\note If this function is not able to interpolate to the full accuracy\n  /// using a centered stencil, it will perform the best interpolation\n  /// available.\n  ///\n  /// \\warning This function will extrapolate if the target times are\n  /// outside the range of data the class has been provided. This is intentional\n  /// to support small extrapolation at the end of a simulation when no further\n  /// data is available, but for full accuracy, check\n  /// `first_time_is_ready_to_interpolate` before calling the interpolation\n  /// functions\n  std::pair<double, VectorTypeToInterpolate> interpolate_and_pop_first_time();\n\n  /// \\brief return the number of times in the target times queue\n  size_t number_of_target_times() const {\n    return argument_interpolation_manager_.number_of_target_times();\n  }\n\n  /// \\brief return the number of data points that have been provided to the\n  /// interpolation manager\n  size_t number_of_data_points() const {\n    return argument_interpolation_manager_.number_of_data_points();\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | argument_interpolation_manager_; }\n\n private:\n  // to avoid code duplication, most of the details are stored in an\n  // interpolation manager for the argument tag, and this class is a friend of\n  // the argument tag class\n  /// \\cond\n  ScriPlusInterpolationManager<VectorTypeToInterpolate, Tag>\n      argument_interpolation_manager_;\n  /// \\endcond\n};\n\ntemplate <typename VectorTypeToInterpolate, typename Tag>\nstd::pair<double, VectorTypeToInterpolate> ScriPlusInterpolationManager<\n    VectorTypeToInterpolate, Tags::Du<Tag>>::interpolate_first_time() {\n  const size_t target_number_of_points =\n      argument_interpolation_manager_.target_number_of_points_;\n  if (argument_interpolation_manager_.target_times_.empty()) {\n    ERROR(\"There are no target times to interpolate.\");\n  }\n  if (argument_interpolation_manager_.to_interpolate_values_.size() <\n      2 * argument_interpolation_manager_.target_number_of_points_) {\n    ERROR(\"Insufficient data points to continue interpolation: have \"\n          << argument_interpolation_manager_.to_interpolate_values_.size()\n          << \", need at least\"\n          << 2 * argument_interpolation_manager_.target_number_of_points_);\n  }\n  // note that because we demand at least a certain number before and at least\n  // a certain number after, we are likely to have a surfeit of points for the\n  // interpolator, but this should not cause significant trouble for a\n  // reasonable method.\n  VectorTypeToInterpolate result{argument_interpolation_manager_.vector_size_};\n\n  VectorTypeToInterpolate interpolation_values{2 * target_number_of_points};\n  VectorTypeToInterpolate lobatto_collocation_values{2 *\n                                                     target_number_of_points};\n  VectorTypeToInterpolate derivative_lobatto_collocation_values{\n      2 * target_number_of_points};\n  DataVector interpolation_times{2 * target_number_of_points};\n  DataVector collocation_points =\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(\n          2 * target_number_of_points);\n\n  const size_t interpolation_data_size =\n      argument_interpolation_manager_.to_interpolate_values_.size();\n\n  for (size_t i = 0; i < argument_interpolation_manager_.vector_size_; ++i) {\n    // binary search assumes times placed in sorted order\n    auto upper_bound_offset = static_cast<size_t>(std::distance(\n        argument_interpolation_manager_.u_bondi_values_.begin(),\n        std::upper_bound(\n            argument_interpolation_manager_.u_bondi_values_.begin(),\n            argument_interpolation_manager_.u_bondi_values_.end(),\n            argument_interpolation_manager_.target_times_.front(),\n            [&i](const double rhs, const DataVector& lhs) {\n              return rhs < lhs[i];\n            })));\n    size_t lower_bound_offset =\n        upper_bound_offset == 0 ? 0 : upper_bound_offset - 1;\n\n    if (upper_bound_offset + target_number_of_points >\n        interpolation_data_size) {\n      upper_bound_offset = interpolation_data_size;\n      lower_bound_offset =\n          interpolation_data_size - 2 * target_number_of_points;\n    } else if (lower_bound_offset < target_number_of_points - 1) {\n      lower_bound_offset = 0;\n      upper_bound_offset = 2 * target_number_of_points;\n    } else {\n      lower_bound_offset = lower_bound_offset + 1 - target_number_of_points;\n      upper_bound_offset = lower_bound_offset + 2 * target_number_of_points;\n    }\n    auto interpolation_values_begin =\n        argument_interpolation_manager_.to_interpolate_values_.begin() +\n        static_cast<ptrdiff_t>(lower_bound_offset);\n    auto interpolation_times_begin =\n        argument_interpolation_manager_.u_bondi_values_.begin() +\n        static_cast<ptrdiff_t>(lower_bound_offset);\n    auto interpolation_times_end =\n        argument_interpolation_manager_.u_bondi_values_.begin() +\n        static_cast<ptrdiff_t>(upper_bound_offset);\n\n    // interpolate using the data sets in the restricted iterators\n    auto value_it = interpolation_values_begin;\n    size_t vector_position = 0;\n    for (auto time_it = interpolation_times_begin;\n         time_it != interpolation_times_end;\n         ++time_it, ++value_it, ++vector_position) {\n      interpolation_values[vector_position] = (*value_it)[i];\n      interpolation_times[vector_position] = (*time_it)[i];\n    }\n    for (size_t j = 0; j < lobatto_collocation_values.size(); ++j) {\n      lobatto_collocation_values[j] =\n          argument_interpolation_manager_.interpolator_->interpolate(\n              gsl::span<const double>(interpolation_times.data(),\n                                      interpolation_times.size()),\n              gsl::span<const typename VectorTypeToInterpolate::value_type>(\n                  interpolation_values.data(), interpolation_values.size()),\n              // affine transformation between the Gauss-Lobatto collocation\n              // points and the physical times\n              (collocation_points[j] + 1.0) * 0.5 *\n                      (interpolation_times[interpolation_times.size() - 1] -\n                       interpolation_times[0]) +\n                  interpolation_times[0]);\n    }\n    // note the coordinate transformation to and from the Gauss-Lobatto basis\n    // range [-1, 1]\n    apply_matrices(\n        make_not_null(&derivative_lobatto_collocation_values),\n        make_array<1>(\n            Spectral::differentiation_matrix<\n                Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>(\n                lobatto_collocation_values.size())),\n        lobatto_collocation_values,\n        Index<1>(lobatto_collocation_values.size()));\n\n    result[i] =\n        argument_interpolation_manager_.interpolator_->interpolate(\n            gsl::span<const double>(collocation_points.data(),\n                                    collocation_points.size()),\n            gsl::span<const typename VectorTypeToInterpolate::value_type>(\n                derivative_lobatto_collocation_values.data(),\n                derivative_lobatto_collocation_values.size()),\n            2.0 *\n                    (argument_interpolation_manager_.target_times_.front() -\n                     interpolation_times[0]) /\n                    (interpolation_times[interpolation_times.size() - 1] -\n                     interpolation_times[0]) -\n                1.0) *\n        2.0 /\n        (interpolation_times[interpolation_times.size() - 1] -\n         interpolation_times[0]);\n  }\n  return std::make_pair(argument_interpolation_manager_.target_times_.front(),\n                        std::move(result));\n}\n\ntemplate <typename VectorTypeToInterpolate, typename Tag>\nstd::pair<double, VectorTypeToInterpolate> ScriPlusInterpolationManager<\n    VectorTypeToInterpolate, Tags::Du<Tag>>::interpolate_and_pop_first_time() {\n  std::pair<double, VectorTypeToInterpolate> interpolated =\n      interpolate_first_time();\n  argument_interpolation_manager_.target_times_.pop_front();\n\n  if (not argument_interpolation_manager_.target_times_.empty()) {\n    argument_interpolation_manager_.remove_unneeded_early_times();\n  }\n  return interpolated;\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/ScriPlusValues.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/ScriPlusValues.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n\nnamespace Cce {\n\nvoid CalculateScriPlusValue<Tags::News>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_du_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_du_j_at_scri;\n  make_const_view(make_not_null(&dy_du_j_at_scri), get(dy_du_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 0> beta_at_scri;\n  make_const_view(make_not_null(&beta_at_scri), get(beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> eth_beta_at_scri;\n  make_const_view(make_not_null(&eth_beta_at_scri), get(eth_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 2> eth_eth_beta_at_scri;\n  make_const_view(make_not_null(&eth_eth_beta_at_scri), get(eth_eth_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  // Note: -2 * r extra factor due to derivative l to y\n  // Note also: extra factor of 2.0 for conversion to strain.\n  get(*news) =\n      2.0 * conj(-get(boundary_r) * exp(-2.0 * beta_at_scri) * dy_du_j_at_scri +\n                 eth_eth_beta_at_scri + 2.0 * square(eth_beta_at_scri));\n}\n\nvoid CalculateScriPlusValue<Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>>::\n    apply(const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*>\n              integral_of_psi_4,\n          const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n          const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_bondi_u,\n          const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_dy_bondi_u,\n          const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_du_bondi_j,\n          const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n          const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n          const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 0> exp_2_beta_at_scri;\n  make_const_view(make_not_null(&exp_2_beta_at_scri), get(exp_2_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> dy_u_at_scri;\n  make_const_view(make_not_null(&dy_u_at_scri), get(dy_bondi_u),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n  const SpinWeighted<ComplexDataVector, 2> eth_dy_u_at_scri;\n  make_const_view(make_not_null(&eth_dy_u_at_scri), get(eth_dy_bondi_u),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> eth_r_divided_by_r_view;\n  make_const_view(make_not_null(&eth_r_divided_by_r_view),\n                  get(eth_r_divided_by_r),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_du_j_at_scri;\n  make_const_view(make_not_null(&dy_du_j_at_scri), get(dy_du_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  get(*integral_of_psi_4) =\n      2.0 * get(boundary_r) *\n      ((conj(eth_dy_u_at_scri) +\n        conj(eth_r_divided_by_r_view) * conj(dy_u_at_scri)) +\n       conj(dy_du_j_at_scri)) /\n      exp_2_beta_at_scri;\n}\n\nvoid CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi3>>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> psi_3,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& eth_ethbar_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, -1>>& ethbar_eth_ethbar_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_du_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_dy_du_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 0> exp_2_beta_at_scri;\n  make_const_view(make_not_null(&exp_2_beta_at_scri), get(exp_2_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> eth_beta_at_scri;\n  make_const_view(make_not_null(&eth_beta_at_scri), get(eth_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 0> eth_ethbar_beta_at_scri;\n  make_const_view(make_not_null(&eth_ethbar_beta_at_scri), get(eth_ethbar_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, -1> ethbar_eth_ethbar_beta_at_scri;\n  make_const_view(make_not_null(&ethbar_eth_ethbar_beta_at_scri),\n                  get(ethbar_eth_ethbar_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> eth_r_divided_by_r_view;\n  make_const_view(make_not_null(&eth_r_divided_by_r_view),\n                  get(eth_r_divided_by_r),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_du_j_at_scri;\n  make_const_view(make_not_null(&dy_du_j_at_scri), get(dy_du_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> ethbar_dy_du_j_at_scri;\n  make_const_view(make_not_null(&ethbar_dy_du_j_at_scri),\n                  get(ethbar_dy_du_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  // Attempting the consensus form; math still needs re-examining\n  // extra factor of * -sqrt(2) to agree with SpEC tetrad normalization\n  get(*psi_3) = 2.0 * conj(eth_beta_at_scri) +\n                4.0 * conj(eth_beta_at_scri) * eth_ethbar_beta_at_scri +\n                ethbar_eth_ethbar_beta_at_scri +\n                get(boundary_r) *\n                    (-(conj(ethbar_dy_du_j_at_scri) +\n                       eth_r_divided_by_r_view * conj(dy_du_j_at_scri)) +\n                     2.0 * eth_beta_at_scri * conj(dy_du_j_at_scri)) /\n                    exp_2_beta_at_scri;\n}\n\nvoid CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi2>>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> psi_2,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_dy_bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_dy_bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_dy_bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_dy_dy_bondi_u,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_dy_bondi_w,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_du_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_du_j_at_scri;\n  make_const_view(make_not_null(&dy_du_j_at_scri), get(dy_du_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 0> dy_dy_w_at_scri;\n  make_const_view(make_not_null(&dy_dy_w_at_scri), get(dy_dy_bondi_w),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_j_at_scri;\n  make_const_view(make_not_null(&dy_j_at_scri), get(dy_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> dy_q_at_scri;\n  make_const_view(make_not_null(&dy_q_at_scri), get(dy_bondi_q),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n  const SpinWeighted<ComplexDataVector, 0> ethbar_dy_q_at_scri;\n  make_const_view(make_not_null(&ethbar_dy_q_at_scri), get(ethbar_dy_bondi_q),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> dy_u_at_scri;\n  make_const_view(make_not_null(&dy_u_at_scri), get(dy_bondi_u),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n  const SpinWeighted<ComplexDataVector, 2> eth_dy_u_at_scri;\n  make_const_view(make_not_null(&eth_dy_u_at_scri), get(eth_dy_bondi_u),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> dy_dy_u_at_scri;\n  make_const_view(make_not_null(&dy_dy_u_at_scri), get(dy_dy_bondi_u),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n  const SpinWeighted<ComplexDataVector, 0> ethbar_dy_dy_u_at_scri;\n  make_const_view(make_not_null(&ethbar_dy_dy_u_at_scri),\n                  get(ethbar_dy_dy_bondi_u),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 0> exp_2_beta_at_scri;\n  make_const_view(make_not_null(&exp_2_beta_at_scri), get(exp_2_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> eth_r_divided_by_r_view;\n  make_const_view(make_not_null(&eth_r_divided_by_r_view),\n                  get(eth_r_divided_by_r),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  get(*psi_2) =\n      -0.5 * get(boundary_r) *\n      (-exp_2_beta_at_scri * (conj(ethbar_dy_q_at_scri) +\n                              eth_r_divided_by_r_view * conj(dy_q_at_scri)) +\n       get(boundary_r) *\n           (ethbar_dy_dy_u_at_scri +\n            2.0 * conj(eth_r_divided_by_r_view) * dy_dy_u_at_scri +\n            conj(ethbar_dy_dy_u_at_scri) +\n            2.0 * eth_r_divided_by_r_view * conj(dy_dy_u_at_scri)) +\n       2.0 * get(boundary_r) *\n           (dy_j_at_scri *\n                (conj(eth_dy_u_at_scri) +\n                 conj(eth_r_divided_by_r_view) * conj(dy_u_at_scri)) +\n            dy_j_at_scri * conj(dy_du_j_at_scri) - dy_dy_w_at_scri)) /\n      exp_2_beta_at_scri;\n}\n\nvoid CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi1>>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> psi_1,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_dy_bondi_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_dy_dy_bondi_beta,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_dy_bondi_q,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n    const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 0> dy_dy_beta_at_scri;\n  make_const_view(make_not_null(&dy_dy_beta_at_scri), get(dy_dy_bondi_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> eth_dy_dy_beta_at_scri;\n  make_const_view(make_not_null(&eth_dy_dy_beta_at_scri),\n                  get(eth_dy_dy_bondi_beta),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_j_at_scri;\n  make_const_view(make_not_null(&dy_j_at_scri), get(dy_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n  const SpinWeighted<ComplexDataVector, 1> dy_q_at_scri;\n  make_const_view(make_not_null(&dy_q_at_scri), get(dy_bondi_q),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> dy_dy_q_at_scri;\n  make_const_view(make_not_null(&dy_dy_q_at_scri), get(dy_dy_bondi_q),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 1> eth_r_divided_by_r_view;\n  make_const_view(make_not_null(&eth_r_divided_by_r_view),\n                  get(eth_r_divided_by_r),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  // extra -1/sqrt(2) factor to agree with SXS tetrad normalization\n  get(*psi_1) = -0.5 * square(get(boundary_r)) *\n                (6.0 * (eth_dy_dy_beta_at_scri +\n                        2.0 * eth_r_divided_by_r_view * dy_dy_beta_at_scri) -\n                 dy_j_at_scri * conj(dy_q_at_scri) - dy_dy_q_at_scri);\n}\n\nvoid CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi0>>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> psi_0,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_dy_dy_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_dy_dy_j_at_scri;\n  make_const_view(make_not_null(&dy_dy_dy_j_at_scri), get(dy_dy_dy_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_j_at_scri;\n  make_const_view(make_not_null(&dy_j_at_scri), get(dy_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  // extra 1/2 factor to agree with SXS tetrad normalization\n  get(*psi_0) = -pow<3>(get(boundary_r)) *\n                (3.0 * conj(dy_j_at_scri) * square(dy_j_at_scri) -\n                 2.0 * dy_dy_dy_j_at_scri);\n}\n\nvoid CalculateScriPlusValue<Tags::ScriPlus<Tags::Strain>>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> strain,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n    const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_eth_retarded_time,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 2> dy_j_at_scri;\n  make_const_view(make_not_null(&dy_j_at_scri), get(dy_bondi_j),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  // conjugate to retrieve the spin -2 quantity everyone else in the world\n  // uses.\n  get(*strain) =\n      conj(-2.0 * get(boundary_r) * dy_j_at_scri + get(eth_eth_retarded_time));\n}\n\nvoid CalculateScriPlusValue<Tags::EthInertialRetardedTime>::apply(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n        eth_inertial_time,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& inertial_time,\n    const size_t l_max) {\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&get(*eth_inertial_time)), get(inertial_time));\n}\n\nvoid CalculateScriPlusValue<::Tags::dt<Tags::InertialRetardedTime>>::apply(\n    const gsl::not_null<Scalar<DataVector>*> dt_inertial_time,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta) {\n  const SpinWeighted<ComplexDataVector, 0> exp_2_beta_at_scri;\n  make_const_view(make_not_null(&exp_2_beta_at_scri), get(exp_2_beta),\n                  get(exp_2_beta).size() - get(*dt_inertial_time).size(),\n                  get(*dt_inertial_time).size());\n  get(*dt_inertial_time) = real(exp_2_beta_at_scri.data());\n}\n\nvoid CalculateScriPlusValue<Tags::ScriPlus<Tags::KleinGordonPsi>>::apply(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        kg_psi_scri,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_kg_psi,\n    const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r, size_t l_max,\n    size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  const SpinWeighted<ComplexDataVector, 0> dy_kg_psi_scri;\n  make_const_view(make_not_null(&dy_kg_psi_scri), get(dy_kg_psi),\n                  (number_of_radial_points - 1) * number_of_angular_points,\n                  number_of_angular_points);\n\n  get(*kg_psi_scri) = -2. * get(boundary_r) * dy_kg_psi_scri;\n}\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/ScriPlusValues.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n\nnamespace Cce {\n\n/// The tags that are needed to be interpolated at scri+ for the available\n/// observation tags.\nusing scri_plus_interpolation_set =\n    tmpl::list<Tags::News, Tags::ScriPlus<Tags::Strain>,\n               Tags::ScriPlus<Tags::Psi3>, Tags::ScriPlus<Tags::Psi2>,\n               Tags::ScriPlus<Tags::Psi1>, Tags::ScriPlus<Tags::Psi0>,\n               Tags::Du<Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>>,\n               Tags::EthInertialRetardedTime>;\n\ntemplate <typename Tag>\nstruct CalculateScriPlusValue;\n\n/*!\n * \\brief Compute the Bondi news from the evolution quantities.\n *\n * \\details In the gauge used for regularity-preserving CCE,\n * the Bondi news takes the convenient form\n *\n * \\f{align*}{\n * N = e^{-2 \\beta^{(0)}} \\left( (\\partial_u \\bar J)^{(1)}\n * + \\bar \\eth \\bar \\eth e^{2 \\beta^{(0)}}\\right),\n * \\f}\n *\n * where \\f$(0)\\f$ and \\f$(1)\\f$ in the superscripts denote the zeroth and first\n * order in an expansion in \\f$1/r\\f$ near \\f$\\mathcal{I}^+\\f$.\n */\ntemplate <>\nstruct CalculateScriPlusValue<Tags::News> {\n  using return_tags = tmpl::list<Tags::News>;\n  // extra typelist for more convenient testing\n  using tensor_argument_tags =\n      tmpl::list<Tags::Dy<Tags::Du<Tags::BondiJ>>, Tags::BondiBeta,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::EthEth>,\n                 Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>>;\n  using argument_tags =\n      tmpl::append<tensor_argument_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> news,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_du_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_eth_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n/*!\n * \\brief Compute the contribution to the leading \\f$\\Psi_4\\f$ that corresponds\n * to a total time derivative.\n *\n * \\details The value \\f$\\Psi_4\\f$ scales asymptotically as \\f$r^{-1}\\f$, and\n * has the form\n *\n * \\f{align*}{\n * \\Psi_4^{(1)} = \\partial_{u_{\\text{inertial}}} B,\n * \\f}\n *\n * where superscripts denote orders in the expansion in powers of \\f$r^{-1}\\f$.\n * This mutator computes \\f$B\\f$:\n *\n * \\f{align*}{\n * B = 2 e^{-2 \\beta^{(0)}} (\\bar \\eth \\bar U^{(1)} + \\partial_u \\bar J^{(1)})\n * \\f}\n *\n * and the time derivative that appears the original equation obeys,\n *\n * \\f[\n * \\partial_{u_{\\text{inertial}}} = e^{-2 \\beta} \\partial_u\n * \\f]\n */\ntemplate <>\nstruct CalculateScriPlusValue<Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>> {\n  using return_tags =\n      tmpl::list<Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>>;\n  // extra typelist for more convenient testing\n  using tensor_argument_tags =\n      tmpl::list<Tags::Exp2Beta, Tags::Dy<Tags::BondiU>,\n                 Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiU>,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::Dy<Tags::Du<Tags::BondiJ>>,\n                 Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>,\n                 Tags::EthRDividedByR>;\n  using argument_tags =\n      tmpl::append<tensor_argument_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*>\n          integral_of_psi_4,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_bondi_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_dy_bondi_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_du_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n\n/*!\n * \\brief Computes the leading part of \\f$\\Psi_3\\f$ near \\f$\\mathcal I^+\\f$.\n *\n * \\details The value \\f$\\Psi_3\\f$ scales asymptotically as \\f$r^{-2}\\f$, and\n * has the form (in the coordinates used for regularity preserving CCE)\n *\n * \\f{align*}{\n * \\Psi_3^{(2)} = 2 \\bar \\eth \\beta^{(0)}\n * + 4 \\bar \\eth \\beta^{(0)} \\eth \\bar \\eth \\beta^{(0)}\n * + \\bar \\eth  \\eth \\bar \\eth \\beta^{(0)}\n * + \\frac{e^{-2  \\beta^{(0)}}}{2}  \\eth \\partial_u \\bar J^{(1)}\n * - e^{-2  \\beta^{(0)}}  \\eth  \\beta^{(0)}  \\partial_u \\bar J^{(1)}\n * \\f},\n *\n * where \\f$J^{(n)}\\f$ is the \\f$1/r^n\\f$ part of \\f$J\\f$ evaluated at\n * \\f$\\mathcal I^+\\f$, so\n *\n * \\f{align*}{\n * J^{(1)} = (-2 R \\partial_y J)|_{y = 1},\n * \\f}\n *\n * where the expansion is determined by the conversion between Bondi and\n * numerical radii \\f$r = 2 R / (1 - y)\\f$.\n */\ntemplate <>\nstruct CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi3>> {\n  using return_tags = tmpl::list<Tags::ScriPlus<Tags::Psi3>>;\n  // extra typelist for more convenient testing\n  using tensor_argument_tags = tmpl::list<\n      Tags::Exp2Beta,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::EthEthbar>,\n      Spectral::Swsh::Tags::Derivative<\n          Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                           Spectral::Swsh::Tags::EthEthbar>,\n          Spectral::Swsh::Tags::Ethbar>,\n      Tags::Dy<Tags::Du<Tags::BondiJ>>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::Du<Tags::BondiJ>>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>, Tags::EthRDividedByR>;\n  using argument_tags = tmpl::push_back<tensor_argument_tags, Tags::LMax,\n                                        Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -1>>*> psi_3,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& eth_ethbar_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, -1>>& ethbar_eth_ethbar_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_du_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& ethbar_dy_du_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n/*!\n * \\brief Computes the leading part of \\f$\\Psi_2\\f$ near \\f$\\mathcal I^+\\f$.\n *\n * \\details The value \\f$\\Psi_2\\f$ scales asymptotically as \\f$r^{-3}\\f$, and\n * has the form (in the coordinates used for regularity preserving CCE)\n *\n * \\f{align*}{\n * \\Psi_2^{(3)} = -\\frac{e^{-2  \\beta^{(0)}}}{4}\n * \\left(e^{2 \\beta^{(0)}} \\eth \\bar Q^{(1)} +  \\eth \\bar U^{(2)}\n * + \\bar \\eth  U^{(2)} + J^{(1)} \\bar \\eth \\bar U^{(1)}\n * +  J^{(1)} \\bar \\partial_u J^{(1)} - 2 W^{(2)}\\right)\n * \\f},\n *\n * where \\f$A^{(n)}\\f$ is the \\f$1/r^n\\f$ part of \\f$A\\f$ evaluated at\n * \\f$\\mathcal I^+\\f$, so for any quantity \\f$A\\f$,\n *\n * \\f{align*}{\n * \\eth  A^{(1)} &= (-2 R \\eth  \\partial_y  A\n * - 2 \\eth R \\partial_y  A)|_{y = 1} \\notag\\\\\n * \\eth  A^{(2)} &= (2 R^2 \\eth \\partial_y^2 A\n * + 2 R \\eth R \\partial^2_y  A)|_{y = 1}, \\notag\\\\\n * A^{(1)} &= (- 2 R \\partial_y A)|_{y = 1}, \\notag\\\\\n * A^{(2)} &= (2 R^2 \\partial_y^2 A)|_{y = 1},\n * \\f}\n *\n * where the expansion is determined by the conversion between Bondi and\n * numerical radii \\f$r = 2 R / (1 - y)\\f$.\n */\ntemplate <>\nstruct CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi2>> {\n  using return_tags = tmpl::list<Tags::ScriPlus<Tags::Psi2>>;\n  // extra typelist for more convenient testing\n  using tensor_argument_tags = tmpl::list<\n      Tags::Exp2Beta, Tags::Dy<Tags::BondiQ>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiQ>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Tags::Dy<Tags::BondiU>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiU>,\n                                       Spectral::Swsh::Tags::Eth>,\n      Tags::Dy<Tags::Dy<Tags::BondiU>>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::Dy<Tags::BondiU>>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Tags::Dy<Tags::Dy<Tags::BondiW>>, Tags::Dy<Tags::BondiJ>,\n      Tags::Dy<Tags::Du<Tags::BondiJ>>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>, Tags::EthRDividedByR>;\n  using argument_tags = tmpl::push_back<tensor_argument_tags, Tags::LMax,\n                                        Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> psi_2,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_bondi_q,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_dy_bondi_q,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_bondi_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_dy_bondi_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_dy_bondi_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& ethbar_dy_dy_bondi_u,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_dy_bondi_w,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_du_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n/*!\n * \\brief Computes the leading part of \\f$\\Psi_1\\f$ near \\f$\\mathcal I^+\\f$.\n *\n * \\details The value \\f$\\Psi_1\\f$ scales asymptotically as \\f$r^{-4}\\f$, and\n * has the form (in the coordinates used for regularity preserving CCE)\n *\n * \\f{align*}{\n * \\Psi_1^{(4)} = \\frac{1}{8} \\left(- 12 \\eth \\beta^{(2)} + J^{(1)} \\bar Q^{(1)}\n * + 2 Q^{(2)}\\right)\n * \\f}\n *\n * where \\f$A^{(n)}\\f$ is the \\f$1/r^n\\f$ part of \\f$A\\f$ evaluated at\n * \\f$\\mathcal I^+\\f$, so for any quantity \\f$A\\f$,\n *\n * \\f{align*}{\n * \\eth A^{(2)} &= (2 R^2 \\eth \\partial_y^2 A\n * + 2 R \\eth R \\partial^2_y  A)|_{y = 1}, \\notag\\\\\n * A^{(1)} &= (- 2 R \\partial_y A)|_{y = 1}, \\notag\\\\\n * A^{(2)} &= (2 R^2 \\partial_y^2 A)|_{y = 1},\n * \\f}\n *\n * where the expansion is determined by the conversion between Bondi and\n * numerical radii \\f$r = 2 R / (1 - y)\\f$.\n */\ntemplate <>\nstruct CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi1>> {\n  using return_tags = tmpl::list<Tags::ScriPlus<Tags::Psi1>>;\n  // extra typelist for more convenient testing\n  using tensor_argument_tags = tmpl::list<\n      Tags::Dy<Tags::Dy<Tags::BondiBeta>>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::Dy<Tags::BondiBeta>>,\n                                       Spectral::Swsh::Tags::Eth>,\n      Tags::Dy<Tags::BondiJ>, Tags::Dy<Tags::BondiQ>,\n      Tags::Dy<Tags::Dy<Tags::BondiQ>>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>, Tags::EthRDividedByR>;\n  using argument_tags = tmpl::push_back<tensor_argument_tags, Tags::LMax,\n                                        Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> psi_1,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_dy_bondi_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_dy_dy_bondi_beta,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_bondi_q,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& dy_dy_bondi_q,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n/*!\n * \\brief Computes the leading part of \\f$\\Psi_0\\f$ near \\f$\\mathcal I^+\\f$.\n *\n * \\details The value \\f$\\Psi_0\\f$ scales asymptotically as \\f$r^{-5}\\f$, and\n * has the form (in the coordinates used for regularity preserving CCE)\n *\n * \\f{align*}{\n * \\Psi_0^{(5)} = \\frac{3}{2}\\left(\\frac{1}{4}\\bar J^{(1)} J^{(1)} {}^2\n * - J^{(3)}\\right)\n * \\f}\n *\n * where \\f$A^{(n)}\\f$ is the \\f$1/r^n\\f$ part of \\f$A\\f$ evaluated at\n * \\f$\\mathcal I^+\\f$, so for any quantity \\f$A\\f$,\n *\n * \\f{align*}{\n * A^{(1)} &= (- 2 R \\partial_y A)|_{y = 1} \\notag\\\\\n * A^{(3)} &= \\left(-\\frac{4}{3} R^3 \\partial_y^3 A\\right)|_{y = 1},\n * \\f}\n *\n * where the expansion is determined by the conversion between Bondi and\n * numerical radii \\f$r = 2 R / (1 - y)\\f$.\n */\ntemplate <>\nstruct CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi0>> {\n  using return_tags = tmpl::list<Tags::ScriPlus<Tags::Psi0>>;\n  // extra typelist for more convenient testing\n  using tensor_argument_tags =\n      tmpl::list<Tags::Dy<Tags::BondiJ>,\n                 Tags::Dy<Tags::Dy<Tags::Dy<Tags::BondiJ>>>,\n                 Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>>;\n  using argument_tags = tmpl::push_back<tensor_argument_tags, Tags::LMax,\n                                        Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> psi_0,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_dy_dy_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n/*!\n * \\brief Computes the leading part of the strain \\f$h\\f$ near \\f$\\mathcal\n * I^+\\f$.\n *\n * \\details The value \\f$h\\f$ scales asymptotically as \\f$r^{-1}\\f$, and\n * has the form (in the coordinates used for regularity preserving CCE)\n *\n * \\f{align*}{\n * h = \\bar J^{(1)} + \\bar \\eth \\bar \\eth u^{(0)},\n * \\f}\n *\n * where \\f$u^{(0)}\\f$ is the asymptotically inertial retarded time, and\n * \\f$A^{(n)}\\f$ is the \\f$1/r^n\\f$ part of \\f$A\\f$ evaluated at\n * \\f$\\mathcal I^+\\f$, so for any quantity \\f$A\\f$,\n *\n * \\f{align*}{\n * A^{(1)} = (- 2 R \\partial_y A)|_{y = 1},\n * \\f}\n *\n * where the expansion is determined by the conversion between Bondi and\n * numerical radii \\f$r = 2 R / (1 - y)\\f$.\n */\ntemplate <>\nstruct CalculateScriPlusValue<Tags::ScriPlus<Tags::Strain>> {\n  using return_tags = tmpl::list<Tags::ScriPlus<Tags::Strain>>;\n  // extra typelist for more convenient testing\n  using tensor_argument_tags = tmpl::list<\n      Tags::Dy<Tags::BondiJ>,\n      Spectral::Swsh::Tags::Derivative<Tags::ComplexInertialRetardedTime,\n                                       Spectral::Swsh::Tags::EthEth>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>>;\n  using argument_tags = tmpl::push_back<tensor_argument_tags, Tags::LMax,\n                                        Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, -2>>*> strain,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& dy_bondi_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_eth_retarded_time,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n/*!\n * \\brief Assign the time derivative of the asymptotically inertial time\n * coordinate.\n *\n * \\details The asymptotically inertial time coordinate \\f$\\mathring u\\f$ obeys\n * the differential equation:\n *\n * \\f{align*}{\n * \\partial_u \\mathring u = e^{2 \\beta}.\n * \\f}\n */\ntemplate <>\nstruct CalculateScriPlusValue<::Tags::dt<Tags::InertialRetardedTime>> {\n  using return_tags = tmpl::list<::Tags::dt<Tags::InertialRetardedTime>>;\n  using argument_tags = tmpl::list<Tags::Exp2Beta>;\n\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> dt_inertial_time,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& exp_2_beta);\n};\n\n/// Determines the angular derivative of the asymptotic inertial time, useful\n/// for asymptotic coordinate transformations.\ntemplate <>\nstruct CalculateScriPlusValue<Tags::EthInertialRetardedTime> {\n  using return_tags = tmpl::list<Tags::EthInertialRetardedTime>;\n  using argument_tags =\n      tmpl::list<Tags::ComplexInertialRetardedTime, Tags::LMax>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n          eth_inertial_time,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& inertial_time,\n      size_t l_max);\n};\n\n/*!\n * \\brief Computes the leading part of the scalar field \\f$\\psi\\f$ near\n * \\f$\\mathcal I^+\\f$.\n *\n * \\details The value \\f$\\psi\\f$ scales asymptotically as \\f$r^{-1}\\f$. Assuming\n * \\f$\\psi^{(n)}\\f$ is the \\f$1/r^n\\f$ part of \\f$\\psi\\f$ evaluated at\n * \\f$\\mathcal I^+\\f$, so for any \\f$\\psi\\f$,\n *\n * \\f{align*}{\n * \\psi^{(1)} = (- 2 R \\partial_y \\psi)|_{y = 1},\n * \\f}\n *\n * where the expansion is determined by the conversion between Bondi and\n * numerical radii \\f$r = 2 R / (1 - y)\\f$.\n */\ntemplate <>\nstruct CalculateScriPlusValue<Tags::ScriPlus<Tags::KleinGordonPsi>> {\n  using return_tags = tmpl::list<Tags::ScriPlus<Tags::KleinGordonPsi>>;\n  using tensor_argument_tags =\n      tmpl::list<Tags::Dy<Tags::KleinGordonPsi>,\n                 Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>>;\n  using argument_tags = tmpl::push_back<tensor_argument_tags, Tags::LMax,\n                                        Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> kg_psi_scri,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& dy_kg_psi,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& boundary_r,\n      size_t l_max, size_t number_of_radial_points);\n};\n\n/// Initialize the \\f$\\mathcal I^+\\f$ value `Tag` for the first hypersurface.\ntemplate <typename Tag>\nstruct InitializeScriPlusValue;\n\n/// Initialize the inertial retarded time to the value provided in the mutator\n/// arguments.\ntemplate <>\nstruct InitializeScriPlusValue<Tags::InertialRetardedTime> {\n  using argument_tags = tmpl::list<>;\n  using return_tags = tmpl::list<Tags::InertialRetardedTime>;\n\n  static void apply(const gsl::not_null<Scalar<DataVector>*> inertial_time,\n                    const double initial_time = 0.0) {\n    // this is arbitrary, and has to do with choosing a BMS frame.\n    get(*inertial_time) = initial_time;\n  }\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/SpecBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/SpecBoundaryData.hpp\"\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryDataTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nvoid cartesian_spatial_metric_and_derivatives_from_unnormalized_spec_modes(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> cartesian_spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, 3>*>\n        inverse_cartesian_spatial_metric,\n    const gsl::not_null<tnsr::ijj<DataVector, 3>*> d_cartesian_spatial_metric,\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> dt_cartesian_spatial_metric,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const gsl::not_null<Scalar<DataVector>*> radial_correction_factor,\n    const tnsr::ii<ComplexModalVector, 3>& spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dr_spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dt_spatial_metric_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const tnsr::I<DataVector, 3>& unit_cartesian_coords, const size_t l_max) {\n  const size_t size = get<0, 0>(inverse_cartesian_to_spherical_jacobian).size();\n  set_number_of_grid_points(cartesian_spatial_metric, size);\n  set_number_of_grid_points(d_cartesian_spatial_metric, size);\n  set_number_of_grid_points(dt_cartesian_spatial_metric, size);\n\n  set_number_of_grid_points(interpolation_buffer, size);\n  set_number_of_grid_points(interpolation_modal_buffer, size);\n  set_number_of_grid_points(eth_buffer, size);\n  set_number_of_grid_points(radial_correction_factor, size);\n\n  // Allocation\n  SphericaliCartesianjj spherical_d_cartesian_spatial_metric{size};\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      // copy the modes to a spin-weighted type for interpolation\n      get(*interpolation_modal_buffer).data() =\n          spatial_metric_coefficients.get(i, j);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&get(*interpolation_buffer)),\n          get(*interpolation_modal_buffer));\n      cartesian_spatial_metric->get(i, j) =\n          real(get(*interpolation_buffer).data());\n\n      get(*interpolation_modal_buffer).data() =\n          dt_spatial_metric_coefficients.get(i, j);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&get(*interpolation_buffer)),\n          get(*interpolation_modal_buffer));\n      dt_cartesian_spatial_metric->get(i, j) =\n          real(get(*interpolation_buffer).data());\n\n      get(*interpolation_modal_buffer).data() =\n          dr_spatial_metric_coefficients.get(i, j);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&get(*interpolation_buffer)),\n          get(*interpolation_modal_buffer));\n      spherical_d_cartesian_spatial_metric.get(0, i, j) =\n          real(get(*interpolation_buffer).data());\n    }\n  }\n\n  *inverse_cartesian_spatial_metric =\n      determinant_and_inverse(*cartesian_spatial_metric).second;\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      // reusing the interpolation buffer for taking the angular derivatives\n      get(*interpolation_buffer) =\n          std::complex<double>(1.0, 0.0) * cartesian_spatial_metric->get(i, j);\n      Spectral::Swsh::angular_derivatives<\n          tmpl::list<Spectral::Swsh::Tags::Eth>>(\n          l_max, 1, make_not_null(&get(*eth_buffer)),\n          get(*interpolation_buffer));\n      spherical_d_cartesian_spatial_metric.get(1, i, j) =\n          -real(get(*eth_buffer).data());\n      spherical_d_cartesian_spatial_metric.get(2, i, j) =\n          -imag(get(*eth_buffer).data());\n    }\n  }\n\n  get(*radial_correction_factor) = square(get<0>(unit_cartesian_coords)) *\n                                   get<0, 0>(*inverse_cartesian_spatial_metric);\n  for (size_t i = 1; i < 3; ++i) {\n    get(*radial_correction_factor) +=\n        square(unit_cartesian_coords.get(i)) *\n        inverse_cartesian_spatial_metric->get(i, i);\n  }\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i + 1; j < 3; ++j) {\n      get(*radial_correction_factor) +=\n          2.0 * inverse_cartesian_spatial_metric->get(i, j) *\n          unit_cartesian_coords.get(i) * unit_cartesian_coords.get(j);\n    }\n  }\n  get(*radial_correction_factor) = sqrt(get(*radial_correction_factor));\n  // convert derivatives to cartesian form\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        d_cartesian_spatial_metric->get(k, i, j) =\n            inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n            spherical_d_cartesian_spatial_metric.get(0, i, j) *\n            get(*radial_correction_factor);\n        for (size_t A = 0; A < 2; ++A) {\n          d_cartesian_spatial_metric->get(k, i, j) +=\n              inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n              spherical_d_cartesian_spatial_metric.get(A + 1, i, j);\n        }\n      }\n    }\n  }\n}\n\nvoid cartesian_shift_and_derivatives_from_unnormalized_spec_modes(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> cartesian_shift,\n    const gsl::not_null<tnsr::iJ<DataVector, 3>*> d_cartesian_shift,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> dt_cartesian_shift,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const tnsr::I<ComplexModalVector, 3>& shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dr_shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dt_shift_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const Scalar<DataVector>& radial_derivative_correction_factor,\n    const size_t l_max) {\n  const size_t size = get<0, 0>(inverse_cartesian_to_spherical_jacobian).size();\n  set_number_of_grid_points(cartesian_shift, size);\n  set_number_of_grid_points(d_cartesian_shift, size);\n  set_number_of_grid_points(dt_cartesian_shift, size);\n\n  set_number_of_grid_points(interpolation_buffer, size);\n  set_number_of_grid_points(interpolation_modal_buffer, size);\n  set_number_of_grid_points(eth_buffer, size);\n\n  // Allocation\n  SphericaliCartesianJ spherical_d_cartesian_shift{size};\n\n  for (size_t i = 0; i < 3; ++i) {\n    // copy the modes to a spin-weighted type for interpolation\n    get(*interpolation_modal_buffer).data() = shift_coefficients.get(i);\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max, 1, make_not_null(&get(*interpolation_buffer)),\n        get(*interpolation_modal_buffer));\n    cartesian_shift->get(i) = real(get(*interpolation_buffer).data());\n\n    get(*interpolation_modal_buffer).data() = dt_shift_coefficients.get(i);\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max, 1, make_not_null(&get(*interpolation_buffer)),\n        get(*interpolation_modal_buffer));\n    dt_cartesian_shift->get(i) = real(get(*interpolation_buffer).data());\n\n    get(*interpolation_modal_buffer).data() = dr_shift_coefficients.get(i);\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max, 1, make_not_null(&get(*interpolation_buffer)),\n        get(*interpolation_modal_buffer));\n    spherical_d_cartesian_shift.get(0, i) =\n        real(get(*interpolation_buffer).data());\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    // reusing the interpolation buffer for taking the angular derivatives\n    get(*interpolation_buffer) =\n        std::complex<double>(1.0, 0.0) * cartesian_shift->get(i);\n    Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n        l_max, 1, make_not_null(&get(*eth_buffer)), get(*interpolation_buffer));\n    spherical_d_cartesian_shift.get(1, i) = -real(get(*eth_buffer).data());\n    spherical_d_cartesian_shift.get(2, i) = -imag(get(*eth_buffer).data());\n  }\n\n  // convert derivatives to cartesian form\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t k = 0; k < 3; ++k) {\n      d_cartesian_shift->get(k, i) =\n          inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n          spherical_d_cartesian_shift.get(0, i) *\n          get(radial_derivative_correction_factor);\n      for (size_t A = 0; A < 2; ++A) {\n        d_cartesian_shift->get(k, i) +=\n            inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n            spherical_d_cartesian_shift.get(A + 1, i);\n      }\n    }\n  }\n}\n\nvoid cartesian_lapse_and_derivatives_from_unnormalized_spec_modes(\n    const gsl::not_null<Scalar<DataVector>*> cartesian_lapse,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> d_cartesian_lapse,\n    const gsl::not_null<Scalar<DataVector>*> dt_cartesian_lapse,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const Scalar<ComplexModalVector>& lapse_coefficients,\n    const Scalar<ComplexModalVector>& dr_lapse_coefficients,\n    const Scalar<ComplexModalVector>& dt_lapse_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const Scalar<DataVector>& radial_derivative_correction_factor,\n    const size_t l_max) {\n  const size_t size = get<0, 0>(inverse_cartesian_to_spherical_jacobian).size();\n  set_number_of_grid_points(cartesian_lapse, size);\n  set_number_of_grid_points(d_cartesian_lapse, size);\n  set_number_of_grid_points(dt_cartesian_lapse, size);\n\n  set_number_of_grid_points(interpolation_buffer, size);\n  set_number_of_grid_points(interpolation_modal_buffer, size);\n  set_number_of_grid_points(eth_buffer, size);\n\n  // Allocation\n  tnsr::i<DataVector, 3> spherical_d_cartesian_lapse{size};\n  // copy the modes to a spin-weighted type for interpolation\n  get(*interpolation_modal_buffer).data() = get(lapse_coefficients);\n  Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, make_not_null(&get(*interpolation_buffer)),\n      get(*interpolation_modal_buffer));\n  get(*cartesian_lapse) = real(get(*interpolation_buffer).data());\n\n  get(*interpolation_modal_buffer).data() = get(dt_lapse_coefficients);\n  Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, make_not_null(&get(*interpolation_buffer)),\n      get(*interpolation_modal_buffer));\n  get(*dt_cartesian_lapse) = real(get(*interpolation_buffer).data());\n\n  get(*interpolation_modal_buffer).data() = get(dr_lapse_coefficients);\n  Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, make_not_null(&get(*interpolation_buffer)),\n      get(*interpolation_modal_buffer));\n  get<0>(spherical_d_cartesian_lapse) = real(get(*interpolation_buffer).data());\n\n  // reusing the interpolation buffer for taking the angular derivatives\n  get(*interpolation_buffer) =\n      std::complex<double>(1.0, 0.0) * get(*cartesian_lapse);\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&get(*eth_buffer)), get(*interpolation_buffer));\n  spherical_d_cartesian_lapse.get(1) = -real(get(*eth_buffer).data());\n  spherical_d_cartesian_lapse.get(2) = -imag(get(*eth_buffer).data());\n\n  // convert derivatives to cartesian form\n  for (size_t k = 0; k < 3; ++k) {\n    d_cartesian_lapse->get(k) =\n        inverse_cartesian_to_spherical_jacobian.get(k, 0) *\n        get<0>(spherical_d_cartesian_lapse) *\n        get(radial_derivative_correction_factor);\n    for (size_t A = 0; A < 2; ++A) {\n      d_cartesian_lapse->get(k) +=\n          inverse_cartesian_to_spherical_jacobian.get(k, A + 1) *\n          spherical_d_cartesian_lapse.get(A + 1);\n    }\n  }\n}\n\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/SpecBoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryDataTags.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace Cce {\n/*\n * \\brief Compute \\f$\\gamma_{i j}\\f$, \\f$\\gamma^{i j}\\f$,\n * \\f$\\partial_i \\gamma_{j k}\\f$, and\n * \\f$\\partial_t g_{i j}\\f$ from input libsharp-compatible modal spatial\n * metric quantities.\n *\n * \\details This function will apply a correction factor associated with a SpEC\n * bug.\n */\nvoid cartesian_spatial_metric_and_derivatives_from_unnormalized_spec_modes(\n    gsl::not_null<tnsr::ii<DataVector, 3>*> cartesian_spatial_metric,\n    gsl::not_null<tnsr::II<DataVector, 3>*> inverse_cartesian_spatial_metric,\n    gsl::not_null<tnsr::ijj<DataVector, 3>*> d_cartesian_spatial_metric,\n    gsl::not_null<tnsr::ii<DataVector, 3>*> dt_cartesian_spatial_metric,\n    gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    gsl::not_null<Scalar<DataVector>*> radial_correction_factor,\n    const tnsr::ii<ComplexModalVector, 3>& spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dr_spatial_metric_coefficients,\n    const tnsr::ii<ComplexModalVector, 3>& dt_spatial_metric_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const tnsr::I<DataVector, 3>& unit_cartesian_coords, size_t l_max);\n\n/*!\n * \\brief Compute \\f$\\beta^{i}\\f$, \\f$\\partial_i \\beta^{j}\\f$, and\n * \\f$\\partial_t \\beta^i\\f$ from input libsharp-compatible modal spatial\n * metric quantities.\n *\n * \\details This function will apply a correction factor associated with a SpEC\n * bug.\n */\nvoid cartesian_shift_and_derivatives_from_unnormalized_spec_modes(\n    gsl::not_null<tnsr::I<DataVector, 3>*> cartesian_shift,\n    gsl::not_null<tnsr::iJ<DataVector, 3>*> d_cartesian_shift,\n    gsl::not_null<tnsr::I<DataVector, 3>*> dt_cartesian_shift,\n    gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const tnsr::I<ComplexModalVector, 3>& shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dr_shift_coefficients,\n    const tnsr::I<ComplexModalVector, 3>& dt_shift_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const Scalar<DataVector>& radial_derivative_correction_factor,\n    size_t l_max);\n\n/*!\n * \\brief Compute \\f$\\alpha\\f$, \\f$\\partial_i \\alpha\\f$, and\n * \\f$\\partial_t \\beta^i\\f$ from input libsharp-compatible modal spatial\n * metric quantities.\n *\n * \\details This function will apply a correction factor associated with a SpEC\n * bug.\n */\nvoid cartesian_lapse_and_derivatives_from_unnormalized_spec_modes(\n    gsl::not_null<Scalar<DataVector>*> cartesian_lapse,\n    gsl::not_null<tnsr::i<DataVector, 3>*> d_cartesian_lapse,\n    gsl::not_null<Scalar<DataVector>*> dt_cartesian_lapse,\n    gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, 0>>*>\n        interpolation_modal_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        interpolation_buffer,\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*> eth_buffer,\n    const Scalar<ComplexModalVector>& lapse_coefficients,\n    const Scalar<ComplexModalVector>& dr_lapse_coefficients,\n    const Scalar<ComplexModalVector>& dt_lapse_coefficients,\n    const CartesianiSphericalJ& inverse_cartesian_to_spherical_jacobian,\n    const Scalar<DataVector>& radial_derivative_correction_factor,\n    size_t l_max);\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/SwshDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace Cce {\nnamespace detail {\n// Precomputation routines for supplying the additional quantities necessary\n// to correct the output of the angular derivative routines from the angular\n// derivatives evaluated at constant numerical coordinates (which is what is\n// returned after the libsharp evaluation) to the angular derivatives at\n// constant Bondi radius (which is what appears in the literature equations\n// and is simple to combine to obtain the hypersurface integrands).\n//\n// Warning: this 'on demand' template is a way of taking advantage of the blaze\n// expression templates in a generic, modular way. However, this can be\n// dangerous. The returned value MUST be a blaze expression template directly,\n// and not a wrapper type (like `SpinWeighted`). Otherwise, some information is\n// lost on the stack and the expression template is corrupted. So, in these 'on\n// demand' returns, the arguments must be fully unpacked to vector types.\n//\n// The `Select` operand is a `std::bool_constant` that ensures mutual\n// exclusivity of the template specializations.\ntemplate <typename Tag, typename SpinConstant, typename Select>\nstruct OnDemandInputsForSwshJacobianImpl;\n\n// default to just retrieving it from the box if not providing an expression\n// template shortcut\ntemplate <typename Tag>\nstruct OnDemandInputsForSwshJacobianImpl<\n    Tags::Dy<Tag>, std::integral_constant<int, Tag::type::type::spin>,\n    std::bool_constant<not tt::is_a_v<::Tags::Multiplies, Tag> and\n                       not tt::is_a_v<Tags::Dy, Tag>>> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    return get(db::get<Tags::Dy<Tag>>(box)).data();\n  }\n};\n\n// default to retrieving from the box if the requested tag is a second\n// derivative without an additional evaluation channel\ntemplate <typename Tag>\nstruct OnDemandInputsForSwshJacobianImpl<\n    Tags::Dy<Tags::Dy<Tag>>, std::integral_constant<int, Tag::type::type::spin>,\n    std::bool_constant<not tt::is_a_v<::Tags::Multiplies, Tag>>> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    return get(db::get<Tags::Dy<Tags::Dy<Tag>>>(box)).data();\n  }\n};\n\n// use the product rule to provide an expression template for derivatives of\n// products\ntemplate <typename LhsTag, typename RhsTag>\nstruct OnDemandInputsForSwshJacobianImpl<\n    Tags::Dy<::Tags::Multiplies<LhsTag, RhsTag>>,\n    std::integral_constant<int,\n                           LhsTag::type::type::spin + RhsTag::type::type::spin>,\n    std::bool_constant<not std::is_same_v<LhsTag, Tags::BondiJbar> and\n                       not std::is_same_v<LhsTag, Tags::BondiUbar> and\n                       not std::is_same_v<RhsTag, Tags::BondiJbar>>> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    decltype(auto) lhs = get(db::get<LhsTag>(box)).data();\n    decltype(auto) dy_lhs = get(db::get<Tags::Dy<LhsTag>>(box)).data();\n    decltype(auto) rhs = get(db::get<RhsTag>(box)).data();\n    decltype(auto) dy_rhs = get(db::get<Tags::Dy<RhsTag>>(box)).data();\n    return lhs * dy_rhs + dy_lhs * rhs;\n  }\n};\n\n// use the product rule and an explicit conjugate to provide an expression\n// template for derivatives of products with `Tags::BondiJbar` as the right-hand\n// operand\ntemplate <typename LhsTag>\nstruct OnDemandInputsForSwshJacobianImpl<\n    Tags::Dy<::Tags::Multiplies<LhsTag, Tags::BondiJbar>>,\n    std::integral_constant<int, LhsTag::type::type::spin - 2>, std::true_type> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    decltype(auto) lhs = get(get<LhsTag>(box)).data();\n    decltype(auto) dy_lhs = get(get<Tags::Dy<LhsTag>>(box)).data();\n    decltype(auto) jbar = conj(get(get<Tags::BondiJ>(box)).data());\n    decltype(auto) dy_jbar = conj(get(get<Tags::Dy<Tags::BondiJ>>(box)).data());\n    return lhs * dy_jbar + dy_lhs * jbar;\n  }\n};\n\n// use the product rule and an explicit conjugate to provide an expression\n// template for derivatives of products with `Tags::BondiJbar` as the left-hand\n// operand.\ntemplate <typename RhsTag>\nstruct OnDemandInputsForSwshJacobianImpl<\n    Tags::Dy<::Tags::Multiplies<Tags::BondiJbar, RhsTag>>,\n    std::integral_constant<int, RhsTag::type::type::spin - 2>, std::true_type> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    decltype(auto) rhs = get(get<RhsTag>(box)).data();\n    decltype(auto) dy_rhs = get(get<Tags::Dy<RhsTag>>(box)).data();\n    decltype(auto) jbar = conj(get(get<Tags::BondiJ>(box)).data());\n    decltype(auto) dy_jbar = conj(get(get<Tags::Dy<Tags::BondiJ>>(box)).data());\n    return dy_jbar * rhs + jbar * dy_rhs;\n  }\n};\n\n// use the product rule and an explicit conjugate to provide an expression\n// template for derivatives of products with `Tags::BondiUbar` as the left-hand\n// operand.\ntemplate <typename RhsTag>\nstruct OnDemandInputsForSwshJacobianImpl<\n    Tags::Dy<::Tags::Multiplies<Tags::BondiUbar, RhsTag>>,\n    std::integral_constant<int, RhsTag::type::type::spin - 1>, std::true_type> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    decltype(auto) ubar = conj(get(get<Tags::BondiU>(box)).data());\n    decltype(auto) dy_ubar = conj(get(get<Tags::Dy<Tags::BondiU>>(box)).data());\n    decltype(auto) rhs = get(get<RhsTag>(box)).data();\n    decltype(auto) dy_rhs = get(get<Tags::Dy<RhsTag>>(box)).data();\n    return ubar * dy_rhs + dy_ubar * rhs;\n  }\n};\n\n// use the product rule and an explicit conjugate to provide an expression\n// template for second derivatives of products with `Tags::BondiJbar` as the\n// right-hand operand.\ntemplate <typename LhsTag>\nstruct OnDemandInputsForSwshJacobianImpl<\n    Tags::Dy<Tags::Dy<::Tags::Multiplies<LhsTag, Tags::BondiJbar>>>,\n    std::integral_constant<int, LhsTag::type::type::spin - 2>, std::true_type> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    decltype(auto) lhs = get(get<LhsTag>(box)).data();\n    decltype(auto) dy_lhs = get(get<Tags::Dy<LhsTag>>(box)).data();\n    decltype(auto) dy_dy_lhs = get(get<Tags::Dy<Tags::Dy<LhsTag>>>(box)).data();\n    decltype(auto) jbar = conj(get(get<Tags::BondiJ>(box)).data());\n    decltype(auto) dy_jbar = conj(get(get<Tags::Dy<Tags::BondiJ>>(box)).data());\n    decltype(auto) dy_dy_jbar =\n        conj(get(get<Tags::Dy<Tags::Dy<Tags::BondiJ>>>(box)).data());\n    return lhs * dy_dy_jbar + 2.0 * dy_lhs * dy_jbar + dy_dy_lhs * jbar;\n  }\n};\n\n// default to extracting directly from the box for spin-weighted derivatives of\n// radial (y) derivatives\ntemplate <typename Tag, typename DerivKind>\nstruct OnDemandInputsForSwshJacobianImpl<\n    Spectral::Swsh::Tags::Derivative<Tags::Dy<Tag>, DerivKind>,\n    std::integral_constant<\n        int, Spectral::Swsh::Tags::Derivative<Tags::Dy<Tag>, DerivKind>::spin>,\n    std::true_type> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    return get(get<Spectral::Swsh::Tags::Derivative<Tags::Dy<Tag>, DerivKind>>(\n                   box))\n        .data();\n  }\n};\n\n// compute the derivative of the `jbar * (q - 2 eth_beta)` using the product\n// rule and the commutation rule for the partials with respect to y and the\n// spin_weighted derivatives\ntemplate <>\nstruct OnDemandInputsForSwshJacobianImpl<Tags::Dy<Tags::JbarQMinus2EthBeta>,\n                                         std::integral_constant<int, -1>,\n                                         std::true_type> {\n  template <typename DataBoxTagList>\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator()(\n      const db::DataBox<DataBoxTagList>& box) {\n    decltype(auto) dy_beta = get(get<Tags::Dy<Tags::BondiBeta>>(box)).data();\n    decltype(auto) dy_j = get(get<Tags::Dy<Tags::BondiJ>>(box)).data();\n    decltype(auto) dy_q = get(get<Tags::Dy<Tags::BondiQ>>(box)).data();\n    decltype(auto) eth_beta =\n        get(get<Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                 Spectral::Swsh::Tags::Eth>>(\n                box))\n            .data();\n    decltype(auto) eth_dy_beta =\n        get(get<Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                                 Spectral::Swsh::Tags::Eth>>(\n                box))\n            .data();\n    decltype(auto) eth_r_divided_by_r =\n        get(get<Tags::EthRDividedByR>(box)).data();\n    decltype(auto) j = get(get<Tags::BondiJ>(box)).data();\n    decltype(auto) q = get(get<Tags::BondiQ>(box)).data();\n    return conj(j) * dy_q + conj(dy_j) * q - 2.0 * conj(j) * eth_dy_beta -\n           2.0 * eth_beta * conj(dy_j) -\n           2.0 * conj(j) * eth_r_divided_by_r * dy_beta;\n  }\n};\n}  // namespace detail\n\n/// Provide an expression template or reference to `Tag`, intended for\n/// situations for which a repeated computation is more\n/// desirable than storing a value in the \\ref DataBoxGroup (e.g. for\n/// conjugation and simple product rule expansion).\ntemplate <typename Tag>\nusing OnDemandInputsForSwshJacobian = detail::OnDemandInputsForSwshJacobianImpl<\n    Tag, std::integral_constant<int, Tag::type::type::spin>, std::true_type>;\n\n/*!\n * \\brief Performs a mutation to a spin-weighted spherical harmonic derivative\n * value from the numerical coordinate (the spin-weighted derivative at\n * fixed \\f$y\\f$) to the Bondi coordinates (the spin-weighted derivative at\n * fixed \\f$r\\f$), inplace to the requested tag.\n *\n * \\details This should be performed only once for each derivative evaluation\n * for each tag, as a repeated inplace evaluation will compound and result in\n * incorrect values in the \\ref DataBoxGroup. This is compatible with acting as\n * a mutation in `db::mutate_apply`.\n * \\note In each specialization, there is an additional type alias\n * `on_demand_argument_tags` that contains tags that represent additional\n * quantities to be passed as arguments that need not be in the \\ref\n * DataBoxGroup. These quantities are suggested to be evaluated by the 'on\n * demand' mechanism provided by `Cce::OnDemandInputsForSwshJacobian`, which\n * provides the additional quantities as blaze expression templates rather than\n * unnecessarily caching intermediate results that aren't re-used.\n */\ntemplate <typename DerivativeTag>\nstruct ApplySwshJacobianInplace;\n\n/*!\n * \\brief Specialization for the spin-weighted derivative \\f$\\eth\\f$.\n *\n * \\details The implemented equation is:\n *\n * \\f[ \\eth F = \\eth^\\prime F - (1 - y) \\frac{\\eth R}{R} \\partial_y F,\n * \\f]\n *\n * where \\f$\\eth\\f$ is the derivative at constant Bondi radius \\f$r\\f$ and\n * \\f$\\eth^\\prime\\f$ is the derivative at constant numerical radius \\f$y\\f$.\n */\ntemplate <typename ArgumentTag>\nstruct ApplySwshJacobianInplace<\n    Spectral::Swsh::Tags::Derivative<ArgumentTag, Spectral::Swsh::Tags::Eth>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags =\n      tmpl::list<Tags::OneMinusY, Tags::EthRDividedByR>;\n\n  using return_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<ArgumentTag, Spectral::Swsh::Tags::Eth>>;\n  using argument_tags = tmpl::append<integration_independent_tags>;\n  using on_demand_argument_tags = tmpl::list<Tags::Dy<ArgumentTag>>;\n\n  static constexpr int spin =\n      Spectral::Swsh::Tags::Derivative<ArgumentTag,\n                                       Spectral::Swsh::Tags::Eth>::spin;\n  template <typename DyArgumentType>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, spin>>*>\n          eth_argument,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      const DyArgumentType& dy_argument) {\n    get(*eth_argument) -= get(one_minus_y) * get(eth_r_divided_by_r) *\n                          SpinWeighted<DyArgumentType, spin - 1>{dy_argument};\n  }\n};\n\n/*!\n * \\brief Specialization for the spin-weighted derivative \\f$\\bar{\\eth}\\f$.\n *\n * \\details The implemented equation is:\n *\n * \\f[\n * \\bar{\\eth} F = \\bar{\\eth}^\\prime F\n * - (1 - y) \\frac{\\bar{\\eth} R}{R} \\partial_y F,\n *\\f]\n *\n * where \\f$\\bar{\\eth}\\f$ is the derivative at constant Bondi radius \\f$r\\f$ and\n * \\f$\\bar{\\eth}^\\prime\\f$ is the derivative at constant numerical radius\n * \\f$y\\f$.\n */\ntemplate <typename ArgumentTag>\nstruct ApplySwshJacobianInplace<Spectral::Swsh::Tags::Derivative<\n    ArgumentTag, Spectral::Swsh::Tags::Ethbar>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags =\n      tmpl::list<Tags::OneMinusY, Tags::EthRDividedByR>;\n\n  using return_tags = tmpl::list<Spectral::Swsh::Tags::Derivative<\n      ArgumentTag, Spectral::Swsh::Tags::Ethbar>>;\n  using argument_tags = tmpl::append<integration_independent_tags>;\n  using on_demand_argument_tags = tmpl::list<Tags::Dy<ArgumentTag>>;\n\n  static constexpr int spin =\n      Spectral::Swsh::Tags::Derivative<ArgumentTag,\n                                       Spectral::Swsh::Tags::Ethbar>::spin;\n  template <typename DyArgumentType>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, spin>>*>\n          ethbar_argument,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      const DyArgumentType& dy_argument) {\n    get(*ethbar_argument) -=\n        get(one_minus_y) * conj(get(eth_r_divided_by_r)) *\n        SpinWeighted<DyArgumentType, spin + 1>{dy_argument};\n  }\n};\n\n/*!\n * \\brief Specialization for the spin-weighted derivative \\f$\\eth \\bar{\\eth}\\f$.\n *\n * \\details The implemented equation is:\n *\n * \\f[\n * \\eth \\bar{\\eth} F = \\eth^\\prime \\bar{\\eth}^\\prime F\n * - \\frac{\\eth R \\bar{\\eth} R}{R^2} (1 - y)^2 \\partial_y^2 F\n * - (1 - y)\\left(\\frac{\\eth R}{R} \\bar{\\eth} \\partial_y F\n * + \\frac{\\bar{\\eth} R}{R} \\eth \\partial_y F\n * + \\frac{\\eth \\bar\\eth R}{R} \\partial_y F\\right),\n * \\f]\n *\n * where \\f$\\eth \\bar{\\eth}\\f$ is the derivative at constant Bondi radius\n * \\f$r\\f$ and \\f$\\eth^\\prime \\bar{\\eth}^\\prime\\f$ is the derivative at constant\n * numerical radius \\f$y\\f$.\n */\ntemplate <typename ArgumentTag>\nstruct ApplySwshJacobianInplace<Spectral::Swsh::Tags::Derivative<\n    ArgumentTag, Spectral::Swsh::Tags::EthEthbar>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags =\n      tmpl::list<Tags::OneMinusY, Tags::EthRDividedByR,\n                 Tags::EthEthbarRDividedByR>;\n\n  using return_tags = tmpl::list<Spectral::Swsh::Tags::Derivative<\n      ArgumentTag, Spectral::Swsh::Tags::EthEthbar>>;\n  using argument_tags = tmpl::append<integration_independent_tags>;\n  using on_demand_argument_tags =\n      tmpl::list<Tags::Dy<ArgumentTag>, Tags::Dy<Tags::Dy<ArgumentTag>>,\n                 Spectral::Swsh::Tags::Derivative<Tags::Dy<ArgumentTag>,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<\n                     Tags::Dy<ArgumentTag>, Spectral::Swsh::Tags::Ethbar>>;\n\n  static constexpr int spin =\n      Spectral::Swsh::Tags::Derivative<ArgumentTag,\n                                       Spectral::Swsh::Tags::EthEthbar>::spin;\n  template <typename DyArgumentType, typename DyDyArgumentType,\n            typename EthDyArgumentType, typename EthbarDyArgumentType>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, spin>>*>\n          eth_ethbar_argument,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&\n          eth_ethbar_r_divided_by_r,\n      const DyArgumentType& dy_argument, const DyDyArgumentType& dy_dy_argument,\n      const EthDyArgumentType& eth_dy_argument,\n      const EthbarDyArgumentType ethbar_dy_argument) {\n    get(*eth_ethbar_argument) -=\n        get(eth_r_divided_by_r) * conj(get(eth_r_divided_by_r)) *\n            (square(get(one_minus_y)) *\n             SpinWeighted<DyDyArgumentType, spin>{dy_dy_argument}) +\n        get(one_minus_y) *\n            (get(eth_r_divided_by_r) *\n                 SpinWeighted<EthbarDyArgumentType, spin - 1>{\n                     ethbar_dy_argument} +\n             conj(get(eth_r_divided_by_r)) *\n                 SpinWeighted<EthDyArgumentType, spin + 1>{eth_dy_argument} +\n             get(eth_ethbar_r_divided_by_r) *\n                 SpinWeighted<DyArgumentType, spin>{dy_argument});\n  }\n};\n\n/*!\n * \\brief Specialization for the spin-weighted derivative \\f$\\bar{\\eth} \\eth\\f$.\n *\n * \\details The implemented equation is:\n *\n * \\f[\n * \\bar{\\eth} \\eth F = \\bar{\\eth}^\\prime \\eth^\\prime F\n * - \\frac{\\eth R \\bar{\\eth} R}{R^2} (1 - y)^2 \\partial_y^2 F\n * - (1 - y)\\left(\\frac{\\eth R}{R} \\bar{\\eth} \\partial_y F\n * + \\frac{\\bar{\\eth} R}{R} \\eth \\partial_y F\n * + \\frac{\\eth \\bar\\eth R}{R} \\partial_y F\\right),\n * \\f]\n *\n * where \\f$\\bar{\\eth} \\eth\\f$ is the derivative at constant Bondi radius\n * \\f$r\\f$ and \\f$\\bar{\\eth}^\\prime \\eth^\\prime\\f$ is the derivative at constant\n * numerical radius \\f$y\\f$.\n */\ntemplate <typename ArgumentTag>\nstruct ApplySwshJacobianInplace<Spectral::Swsh::Tags::Derivative<\n    ArgumentTag, Spectral::Swsh::Tags::EthbarEth>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags =\n      tmpl::list<Tags::OneMinusY, Tags::EthRDividedByR,\n                 Tags::EthEthbarRDividedByR>;\n\n  using return_tags = tmpl::list<Spectral::Swsh::Tags::Derivative<\n      ArgumentTag, Spectral::Swsh::Tags::EthbarEth>>;\n  using argument_tags = tmpl::append<integration_independent_tags>;\n  using on_demand_argument_tags =\n      tmpl::list<Tags::Dy<ArgumentTag>, Tags::Dy<Tags::Dy<ArgumentTag>>,\n                 Spectral::Swsh::Tags::Derivative<Tags::Dy<ArgumentTag>,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<\n                     Tags::Dy<ArgumentTag>, Spectral::Swsh::Tags::Ethbar>>;\n\n  static constexpr int spin =\n      Spectral::Swsh::Tags::Derivative<ArgumentTag,\n                                       Spectral::Swsh::Tags::EthbarEth>::spin;\n  template <typename DyArgumentType, typename DyDyArgumentType,\n            typename EthDyArgumentType, typename EthbarDyArgumentType>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, spin>>*>\n          ethbar_eth_argument,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>&\n          eth_ethbar_r_divided_by_r,\n      const DyArgumentType& dy_argument, const DyDyArgumentType& dy_dy_argument,\n      const EthDyArgumentType& eth_dy_argument,\n      const EthbarDyArgumentType ethbar_dy_argument) {\n    get(*ethbar_eth_argument) -=\n        get(eth_r_divided_by_r) * conj(get(eth_r_divided_by_r)) *\n            (square(get(one_minus_y)) *\n             SpinWeighted<DyDyArgumentType, spin>{dy_dy_argument}) +\n        get(one_minus_y) *\n            (get(eth_r_divided_by_r) *\n                 SpinWeighted<EthbarDyArgumentType, spin - 1>{\n                     ethbar_dy_argument} +\n             conj(get(eth_r_divided_by_r)) *\n                 SpinWeighted<EthDyArgumentType, spin + 1>{eth_dy_argument} +\n             get(eth_ethbar_r_divided_by_r) *\n                 SpinWeighted<DyArgumentType, spin>{dy_argument});\n  }\n};\n\n/*!\n * \\brief Specialization for the spin-weighted derivative \\f$\\eth \\eth\\f$.\n *\n * \\details The implemented equation is:\n *\n * \\f[\n * \\eth \\eth F = \\eth^\\prime \\eth^\\prime F\n * - (1 - y)^2 \\frac{(\\eth R)^2}{R^2} \\partial_y^2 F\n * - (1 - y) \\left( 2 \\frac{\\eth R}{R} \\eth \\partial_y F\n * + \\frac{\\eth \\eth R}{R} \\partial_y F\\right),\n * \\f]\n *\n * where \\f$\\eth \\eth\\f$ is the derivative at constant Bondi radius \\f$r\\f$ and\n * \\f$\\eth^\\prime \\eth^\\prime\\f$ is the derivative at constant numerical radius\n * \\f$y\\f$.\n */\ntemplate <typename ArgumentTag>\nstruct ApplySwshJacobianInplace<Spectral::Swsh::Tags::Derivative<\n    ArgumentTag, Spectral::Swsh::Tags::EthEth>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags =\n      tmpl::list<Tags::OneMinusY, Tags::EthRDividedByR,\n                 Tags::EthEthRDividedByR>;\n\n  using return_tags = tmpl::list<Spectral::Swsh::Tags::Derivative<\n      ArgumentTag, Spectral::Swsh::Tags::EthEth>>;\n  using argument_tags = tmpl::append<integration_independent_tags>;\n  using on_demand_argument_tags =\n      tmpl::list<Tags::Dy<ArgumentTag>, Tags::Dy<Tags::Dy<ArgumentTag>>,\n                 Spectral::Swsh::Tags::Derivative<Tags::Dy<ArgumentTag>,\n                                                  Spectral::Swsh::Tags::Eth>>;\n\n  static constexpr int spin =\n      Spectral::Swsh::Tags::Derivative<ArgumentTag,\n                                       Spectral::Swsh::Tags::EthEth>::spin;\n  template <typename DyArgumentType, typename DyDyArgumentType,\n            typename EthDyArgumentType>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, spin>>*>\n          eth_eth_argument,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_eth_r_divided_by_r,\n      const DyArgumentType& dy_argument, const DyDyArgumentType& dy_dy_argument,\n      const EthDyArgumentType& eth_dy_argument) {\n    get(*eth_eth_argument) -=\n        square(get(eth_r_divided_by_r)) *\n            (square(get(one_minus_y)) *\n             SpinWeighted<DyDyArgumentType, spin - 2>{dy_dy_argument}) +\n        get(one_minus_y) *\n            (2.0 * get(eth_r_divided_by_r) *\n                 SpinWeighted<EthDyArgumentType, spin - 1>{eth_dy_argument} +\n             get(eth_eth_r_divided_by_r) *\n                 SpinWeighted<DyArgumentType, spin - 2>{dy_argument});\n  }\n};\n\n/*!\n * \\brief Specialization for the spin-weighted derivative \\f$\\bar{\\eth}\n * \\bar{\\eth}\\f$.\n *\n * \\details The implemented equation is:\n *\n * \\f[\n * \\bar{\\eth} \\bar{\\eth} F = \\bar{\\eth}^\\prime \\bar{\\eth}^\\prime F\n * - (1 - y)^2 \\frac{(\\bar{\\eth} R)^2}{R^2} \\partial_y^2 F\n * - (1 - y) \\left( 2 \\frac{\\bar{\\eth} R}{R} \\bar{\\eth} \\partial_y F\n * + \\frac{\\bar{\\eth} \\bar{\\eth} R}{R} \\partial_y F\\right),\n * \\f]\n *\n * where \\f$\\bar{\\eth} \\bar{\\eth}\\f$ is the derivative at constant Bondi radius\n * \\f$r\\f$ and \\f$\\bar{\\eth}^\\prime \\bar{\\eth}^\\prime\\f$ is the derivative at\n * constant numerical radius \\f$y\\f$.\n */\ntemplate <typename ArgumentTag>\nstruct ApplySwshJacobianInplace<Spectral::Swsh::Tags::Derivative<\n    ArgumentTag, Spectral::Swsh::Tags::EthbarEthbar>> {\n  using pre_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n  using integration_independent_tags =\n      tmpl::list<Tags::OneMinusY, Tags::EthRDividedByR,\n                 Tags::EthEthRDividedByR>;\n\n  using return_tags = tmpl::list<Spectral::Swsh::Tags::Derivative<\n      ArgumentTag, Spectral::Swsh::Tags::EthbarEthbar>>;\n  using argument_tags = tmpl::append<integration_independent_tags>;\n  using on_demand_argument_tags =\n      tmpl::list<Tags::Dy<ArgumentTag>, Tags::Dy<Tags::Dy<ArgumentTag>>,\n                 Spectral::Swsh::Tags::Derivative<\n                     Tags::Dy<ArgumentTag>, Spectral::Swsh::Tags::Ethbar>>;\n\n  static constexpr int spin = Spectral::Swsh::Tags::Derivative<\n      ArgumentTag, Spectral::Swsh::Tags::EthbarEthbar>::spin;\n  template <typename DyArgumentType, typename DyDyArgumentType,\n            typename EthbarDyArgumentType>\n  static void apply(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, spin>>*>\n          ethbar_ethbar_argument,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& eth_eth_r_divided_by_r,\n      const DyArgumentType& dy_argument, const DyDyArgumentType& dy_dy_argument,\n      const EthbarDyArgumentType& ethbar_dy_argument) {\n    get(*ethbar_ethbar_argument) -=\n        square(conj(get(eth_r_divided_by_r))) *\n            (square(get(one_minus_y)) *\n             SpinWeighted<DyDyArgumentType, spin + 2>{dy_dy_argument}) +\n        get(one_minus_y) *\n            (2.0 * conj(get(eth_r_divided_by_r)) *\n                 SpinWeighted<EthbarDyArgumentType, spin + 1>{\n                     ethbar_dy_argument} +\n             conj(get(eth_eth_r_divided_by_r)) *\n                 SpinWeighted<DyArgumentType, spin + 2>{dy_argument});\n  }\n};\n\nnamespace detail {\n// A helper to forward to the `ApplySwshJacobianInplace` mutators that takes\n// advantage of the `OnDemandInputsForSwshJacobian`, which computes blaze\n// template expressions for those quantities for which it is anticipated to be\n// sufficiently cheap to repeatedly compute that it is worth saving the cost of\n// additional storage (e.g. conjugates and derivatives for which the product\n// rule applies)\ntemplate <typename DerivativeTag, typename DataBoxTagList,\n          typename... OnDemandTags>\nvoid apply_swsh_jacobian_helper(\n    const gsl::not_null<db::DataBox<DataBoxTagList>*> box,\n    tmpl::list<OnDemandTags...> /*meta*/) {\n  db::mutate_apply<ApplySwshJacobianInplace<DerivativeTag>>(\n      box, OnDemandInputsForSwshJacobian<OnDemandTags>{}(*box)...);\n}\n}  // namespace detail\n\n/*!\n * \\brief This routine evaluates the set of inputs to the CCE integrand for\n * `BondiValueTag` which are spin-weighted angular derivatives.\n\n * \\details This function is called on the \\ref DataBoxGroup holding the\n * relevant CCE data during each hypersurface integration step, after evaluating\n * `mutate_all_pre_swsh_derivatives_for_tag()` with template argument\n * `BondiValueTag` and before evaluating `ComputeBondiIntegrand<BondiValueTag>`.\n * Provided a \\ref DataBoxGroup with the appropriate tags (including\n * `Cce::all_pre_swsh_derivative_tags`, `Cce::all_swsh_derivative_tags`,\n * `Cce::all_transform_buffer_tags`,  `Cce::pre_computation_tags`, and\n * `Cce::Tags::LMax`), this function will apply all of the necessary\n * mutations to update\n * `Cce::single_swsh_derivative_tags_to_compute_for<BondiValueTag>` and\n * `Cce::second_swsh_derivative_tags_to_compute_for<BondiValueTag>` to their\n * correct values for the current values of the remaining (input) tags.\n */\ntemplate <typename BondiValueTag, typename DataBoxTagList>\nvoid mutate_all_swsh_derivatives_for_tag(\n    const gsl::not_null<db::DataBox<DataBoxTagList>*> box) {\n  // The collection of spin-weighted derivatives cannot be applied as individual\n  // compute items, because it is better to aggregate similar spins and dispatch\n  // to libsharp in groups. So, we supply a bulk mutate operation which takes in\n  // multiple Variables from the presumed DataBox, and alters their values as\n  // necessary.\n  db::mutate_apply<Spectral::Swsh::AngularDerivatives<\n      single_swsh_derivative_tags_to_compute_for_t<BondiValueTag>>>(box);\n  tmpl::for_each<single_swsh_derivative_tags_to_compute_for_t<BondiValueTag>>(\n      [&box](auto derivative_tag_v) {\n        using derivative_tag = typename decltype(derivative_tag_v)::type;\n        detail::apply_swsh_jacobian_helper<derivative_tag>(\n            box, typename ApplySwshJacobianInplace<\n                     derivative_tag>::on_demand_argument_tags{});\n      });\n\n  db::mutate_apply<Spectral::Swsh::AngularDerivatives<\n      second_swsh_derivative_tags_to_compute_for_t<BondiValueTag>>>(box);\n  tmpl::for_each<second_swsh_derivative_tags_to_compute_for_t<BondiValueTag>>(\n      [&box](auto derivative_tag_v) {\n        using derivative_tag = typename decltype(derivative_tag_v)::type;\n        detail::apply_swsh_jacobian_helper<derivative_tag>(\n            box, typename ApplySwshJacobianInplace<\n                     derivative_tag>::on_demand_argument_tags{});\n      });\n}\n\nnamespace Tags {\n\n/*!\n * \\brief Compute tag for a manually-handled Swsh derivative in the volume.\n *\n * \\details Numerical swsh derivatives are $\\breve\\eth$ (at fixed $y$), but we\n * prefer to write our calculations here in terms of $\\eth$ (at fixed $r$). This\n * involves a correction term that depends on the $\\partial_{\\breve{y}}$\n * derivative of the field being differentiated. Most of these are handled in\n * place by `ApplySwshJacobianInplace`. However, that infrastructure is specific\n * to the Cce evolution system itself, so it won't work for other uses\n * (e.g. observers that are invoked periodically). This compute tag instead\n * manually computes a certain swsh derivative (at fixed $r$).  Right now this\n * will only work when `DerivativeKind` is either `Spectral::Swsh::Tags::Eth` or\n * `Spectral::Swsh::Tags::Ethbar`. If you want to extend it to second\n * derivatives, there will have to be more template specializations.\n */\ntemplate <typename ArgumentTag, typename DerivativeKind>\nstruct SwshDerivativeCompute\n    : Spectral::Swsh::Tags::Derivative<ArgumentTag, DerivativeKind>,\n      db::ComputeTag {\n  using base = Spectral::Swsh::Tags::Derivative<ArgumentTag, DerivativeKind>;\n  using return_type = typename base::type;\n\n  using ApplySwshJac = ApplySwshJacobianInplace<base>;\n\n  using argument_tags = tmpl::append<\n      tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints, ArgumentTag>,\n      typename ApplySwshJac::argument_tags,\n      typename ApplySwshJac::on_demand_argument_tags>;\n\n  static constexpr auto function(\n      const gsl::not_null<return_type*> eth_field, size_t l_max,\n      size_t number_of_radial_points, const typename ArgumentTag::type& field,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& one_minus_y,\n      const Scalar<SpinWeighted<ComplexDataVector, 1>>& eth_r_divided_by_r,\n      const typename Tags::Dy<ArgumentTag>::type& dy_field) {\n    get(*eth_field) = Spectral::Swsh::angular_derivative<DerivativeKind>(\n        l_max, number_of_radial_points, get(field));\n\n    ApplySwshJac::apply(eth_field, one_minus_y, eth_r_divided_by_r,\n                        get(dy_field).data());\n  }\n};\n\n}  // namespace Tags\n\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup EvolutionSystemsGroup\n * \\brief The set of utilities for performing Cauchy characteristic evolution\n * and Cauchy characteristic matching.\n *\n * \\details Cauchy characteristic evolution (CCE) is a secondary nonlinear GR\n * evolution system that covers the domain extending from a spherical boundary\n * away from the strong-field regime, and extending all the way to future null\n * infinity \\f$\\mathcal I^+\\f$. The evolution system is governed by five\n * hypersurface equations that are integrated radially along future null slices,\n * and one evolution equation that governs the evolution of one hypersurface to\n * the next.\n *\n * The mathematics of CCE are intricate, and SpECTRE's version implements a\n * number of tricks and improvements that are not yet present in other contexts.\n * For introductions to CCE generally, see papers \\cite Bishop1997ik,\n * \\cite Bishop1998uk, and \\cite Barkett2019uae. Here we do not present a full\n * description of all of the mathematics, but instead just provide a high-level\n * roadmap of the SpECTRE utilities and how they come together in the CCE\n * system. This is intended as a map for maintainers of the codebase.\n *\n * First, worldtube data from a completed or running Cauchy evolution of the\n * Einstein field equations (currently the only one implemented in SpECTRE is\n * Generalized Harmonic) must be translated to Bondi spin-weighted scalars at\n * the extraction sphere. Relevant utilities for this conversion are\n * `Cce::WorldtubeDataManager`, `Cce::create_bondi_boundary_data`. Relevant\n * parts of the parallel infrastructure are `Cce::H5WorldtubeBoundary`,\n * `Cce::Actions::BoundaryComputeAndSendToEvolution`,\n * `Cce::Actions::RequestBoundaryData`, and\n * `Cce::Actions::ReceiveWorldtubeData`.\n *\n * The first hypersurface must be initialized with some reasonable starting\n * value for the evolved Bondi quantity \\f$J\\f$. There isn't a universal perfect\n * prescription for this, as a complete description would require, like the\n * Cauchy initial data problem, knowledge of the system arbitrarily far in the\n * past. A utility for assigning the initial data is `Cce::InitializeJ`.\n *\n * SpECTRE CCE is currently unique in implementing an additional gauge transform\n * after the worldtube boundary data is derived. This is performed to obtain an\n * asymptotically well-behaved gauge that is guaranteed to avoid logarithmic\n * behavior that has plagued other CCE implementations, and so that the\n * asymptotic computations can be as simple, fast, and reliable as possible.\n * Relevant utilities for the gauge transformation are\n * `Cce::GaugeAdjustedBoundaryValue` (see template specializations),\n * `Cce::GaugeUpdateTimeDerivatives`, `Cce::GaugeUpdateAngularFromCartesian`,\n * `Cce::GaugeUpdateJacobianFromCoordinates`, `Cce::GaugeUpdateInterpolator`,\n * `Cce::GaugeUpdateOmega`, `Cce::GaugeUpdateInertialTimeDerivatives`, and\n * `Cce::InitializeGauge`.\n *\n * Next, the CCE system must evaluate the hypersurface differential equations.\n * There are five, in sequence, deriving \\f$\\beta, Q, U, W,\\f$ and \\f$H\\f$. For\n * each of the five radial differential equations, first the products and\n * derivatives on the right-hand side must be evaluated, then the full\n * right-hand side of the equation must be computed, and finally the radial\n * differential equation is integrated. The equations have a hierarchical\n * structure, so the result for \\f$\\beta\\f$ feeds into the radial differential\n * equation for \\f$Q\\f$, and both feed into \\f$U\\f$, and so on.\n *\n * Relevant utilities for computing the inputs to the hypersurface equations are\n * `Cce::PrecomputeCceDependencies` (see template specializations),\n * `Cce::mutate_all_precompute_cce_dependencies`, `Cce::PreSwshDerivatives` (see\n * template specializations), `Cce::mutate_all_pre_swsh_derivatives_for_tag`,\n * and `Cce::mutate_all_swsh_derivatives_for_tag`. There are a number of\n * typelists in `IntegrandInputSteps.hpp` that determine the set of quantities\n * to be evaluated in each of the five hypersurface steps.\n * Once the hypersurface equation inputs are computed, then a hypersurface\n * equation right-hand side can be evaluated via `Cce::ComputeBondiIntegrand`\n * (see template specializations). Then, the hypersurface equation may be\n * integrated via `Cce::RadialIntegrateBondi` (see template specializations).\n *\n * Relevant parts of the parallel infrastructure for performing the hypersurface\n * steps are: `Cce::CharacteristicEvolution`,\n * `Cce::Actions::CalculateIntegrandInputsForTag`, and\n * `Cce::Actions::PrecomputeGlobalCceDependencies`. Note that most of the\n * algorithmic steps are laid out in order in the phase-dependent action list of\n * `Cce::CharacteristicEvolution`.\n *\n * The time integration for the hyperbolic part of the CCE equations is\n * performed via \\f$\\partial_u J = H\\f$, where \\f$\\partial_u\\f$ represents\n * differentiation with respect to retarded time at fixed numerical radius\n * \\f$y\\f$.\n *\n * At this point, all of the Bondi quantities on a given hypersurface have been\n * evaluated, and we wish to output the relevant waveform quantities at\n * \\f$\\mathcal I^+\\f$. This acts much like an additional step in the\n * hypersurface sequence, with inputs that need to be calculated before the\n * quantities of interest can be evaluated. The action\n * `Cce::Actions::CalculateScriInputs` performs the sequence of steps to obtain\n * those inputs, and the utilities `Cce::CalculateScriPlusValue` (see template\n * specializations) can be used to evaluate the desired outputs at\n * \\f$\\mathcal I^+\\f$.\n *\n * Unfortunately, those quantities at \\f$\\mathcal I^+\\f$ are not yet an\n * appropriate waveform output, because the time coordinate with which they are\n * evaluated is the simulation time, not an asymptotically inertial time. So,\n * instead of directly writing the waveform outputs, we must put them in a queue\n * to be interpolated once enough data points have been accumulated to perform a\n * reliable interpolation at a consistent cut of \\f$\\mathcal I^+\\f$ at constant\n * inertial time. Utilities for calculating and evolving the asymptotic inertial\n * time are `Cce::InitializeScriPlusValue` and `Cce::CalculateScriPlusValue`\n * using arguments involving `Cce::Tags::InertialRetardedTime`. A utility for\n * managing the interpolation is `Cce::ScriPlusInterpolationManager`, and\n * relevant parts of the parallel infrastructure for manipulating the data into\n * the interpolator and writing the results to disk are\n * `Cce::Actions::InsertInterpolationScriData` and\n * `Cce::Actions::ScriObserveInterpolated`.\n *\n * The template parameter `EvolveCcm` will add an extra evolved variable to the\n * characteristic system, namely `Cce::Tags::PartiallyFlatCartesianCoords`.\n *\n */\nnamespace Cce {\n\ntemplate <bool EvolveCcm>\nstruct System {\n  static constexpr size_t volume_dim = 3;\n  using variables_tag =\n      tmpl::list<::Tags::Variables<tmpl::list<Tags::BondiJ>>,\n                 ::Tags::Variables<tmpl::conditional_t<\n                     EvolveCcm,\n                     tmpl::list<Cce::Tags::CauchyCartesianCoords,\n                                Cce::Tags::PartiallyFlatCartesianCoords,\n                                Cce::Tags::InertialRetardedTime>,\n                     tmpl::list<Cce::Tags::CauchyCartesianCoords,\n                                Cce::Tags::InertialRetardedTime>>>>;\n\n  static constexpr bool has_primitive_and_conservative_vars = false;\n};\n}\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n\nnamespace Cce {\n\n/// \\cond\nstruct MetricWorldtubeDataManager;\ntemplate <typename ToInterpolate, typename Tag>\nstruct ScriPlusInterpolationManager;\n/// \\endcond\n\n/// Tags for Cauchy Characteristic Extraction routines\nnamespace Tags {\n\n// Bondi parameter tags\n\n/// Bondi parameter \\f$\\beta\\f$\nstruct BondiBeta : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// Bondi parameter \\f$J\\f$\nstruct BondiJ : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 2>>;\n  static std::string name() { return \"J\"; }\n};\n\n// The scalar field in scalar-tensor theory\nstruct KleinGordonPsi : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n  static std::string name() { return \"KGPsi\"; }\n};\n\n}  // namespace Tags\n}  // namespace Cce\n\nnamespace Tags {\n/// \\cond\ntemplate <>\nstruct dt<Cce::Tags::BondiJ> : db::PrefixTag, db::SimpleTag {\n  static std::string name() { return \"H\"; }\n  using type = Scalar<::SpinWeighted<ComplexDataVector, 2>>;\n  using tag = Cce::Tags::BondiJ;\n};\n\ntemplate <>\nstruct dt<Cce::Tags::KleinGordonPsi> : db::PrefixTag, db::SimpleTag {\n  static std::string name() { return \"KGPi\"; }\n  using type = Scalar<::SpinWeighted<ComplexDataVector, 0>>;\n  using tag = Cce::Tags::KleinGordonPsi;\n};\n/// \\endcond\n}  // namespace Tags\n\nnamespace Cce {\nnamespace Tags {\n/// \\brief Bondi parameter \\f$H = \\partial_u J\\f$.\n/// \\note The notation in the literature is not consistent regarding this\n/// quantity, or whether it is denoted by an \\f$H\\f$ at all. The SpECTRE CCE\n/// module consistently uses it to describe the (retarded) partial time\n/// derivative of \\f$J\\f$ at fixed compactified radius \\f$y\\f$ (to be contrasted\n/// with the physical Bondi radius, which is not directly used for numerical\n/// grids).\nusing BondiH = ::Tags::dt<BondiJ>;\n\n/// \\brief Klein-Gordon variable \\f$\\Pi = \\partial_u \\psi\\f$.\nusing KleinGordonPi = ::Tags::dt<KleinGordonPsi>;\n\n/// Bondi parameter \\f$\\bar{J}\\f$\nstruct BondiJbar : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -2>>;\n  static std::string name() { return \"Jbar\"; }\n};\n\n/// Bondi parameter \\f$K = \\sqrt{1 + J \\bar{J}}\\f$\nstruct BondiK : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n  static std::string name() { return \"K\"; }\n};\n\n/// Bondi parameter \\f$Q\\f$\nstruct BondiQ : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 1>>;\n  static std::string name() { return \"Q\"; }\n};\n\n/// Bondi parameter \\f$\\bar{Q}\\f$\nstruct BondiQbar : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n  static std::string name() { return \"Qbar\"; }\n};\n\n/// Bondi parameter \\f$U\\f$\nstruct BondiU : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 1>>;\n  static std::string name() { return \"U\"; }\n};\n\n/// The surface quantity of Bondi \\f$U\\f$ evaluated at the null spacetime\n/// boundary \\f$\\mathcal I^+\\f$\nstruct BondiUAtScri : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 1>>;\n};\n\n/// Bondi parameter \\f$\\bar{U}\\f$\nstruct BondiUbar : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n  static std::string name() { return \"Ubar\"; }\n};\n\n/// Bondi parameter \\f$W\\f$\nstruct BondiW : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n  static std::string name() { return \"W\"; }\n};\n\n/// Bondi parameter \\f$\\bar{J}\\f$ in the Cauchy frame\nstruct BondiJCauchyView : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 2>>;\n};\n\n/// The derivative with respect to the numerical coordinate \\f$y = 1 - 2R/r\\f$,\n/// where \\f$R(u, \\theta, \\phi)\\f$ is Bondi radius of the worldtube.\ntemplate <typename Tag>\nstruct Dy : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n  static const size_t dimension_to_differentiate = 2;\n};\n\n/// The derivative with respect to Bondi \\f$r\\f$\ntemplate <typename Tag>\nstruct Dr : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// The derivative with respect to \\f$\\lambda\\f$,\n///  where \\f$\\lambda\\f$ is an affine parameter along \\f$l\\f$, see\n///  Eq. (19a) of \\cite Moxon2020gha.\ntemplate <typename Tag>\nstruct Dlambda : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// The derivative with respect to Bondi retarded time \\f$u\\f$\ntemplate <typename Tag>\nstruct Du : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n/// The spin-weight 2 angular Jacobian factor in the partially flat Bondi-like\n/// coordinates, see Eq. (31a) of \\cite Moxon2020gha\nstruct PartiallyFlatGaugeC : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 2>>;\n};\n\n/// The spin-weight 0 angular Jacobian factor in the partially flat Bondi-like\n/// coordinates, see Eq. (31b) of \\cite Moxon2020gha\nstruct PartiallyFlatGaugeD : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The spin-weight 2 angular Jacobian factor in the Cauchy coordinates, similar\n/// to Eq. (31a) of \\cite Moxon2020gha, but without hat.\nstruct CauchyGaugeC : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 2>>;\n};\n\n/// The spin-weight 0 angular Jacobian factor in the Cauchy coordinates, similar\n/// to Eq. (31b) of \\cite Moxon2020gha, but without hat.\nstruct CauchyGaugeD : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The conformal factor in the partially flat Bondi-like coordinates,\n/// associated with an angular transformation, see Eq. (32) of\n/// \\cite Moxon2020gha\nstruct PartiallyFlatGaugeOmega : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The conformal factor in the Cauchy coordinates, similar to Eq. (32) of\n/// \\cite Moxon2020gha, but without hat.\nstruct CauchyGaugeOmega : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\nstruct News : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -2>>;\n};\n\n// For expressing the Cauchy angular coordinates for the worldtube data in terms\n// of the evolution angular coordinates.\nstruct CauchyAngularCoords : db::SimpleTag {\n  using type = tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>;\n};\n\n/// The angular coordinates for the partially flat Bondi-like coordinates.\nstruct PartiallyFlatAngularCoords : db::SimpleTag {\n  using type = tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>;\n};\n\n// For expressing the Cauchy Cartesian coordinates for the worldtube data in\n// terms of the evolution angular coordinates.\nstruct CauchyCartesianCoords : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3>;\n};\n\n/// The partially flat Bondi-like coordinates.\nstruct PartiallyFlatCartesianCoords : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3>;\n};\n\n/// The asymptotically inertial retarded time in terms of the evolution time\n/// variable\nstruct InertialRetardedTime : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// Represents \\f$\\eth u_{\\rm inertial}\\f$, which is a useful quantity for\n/// asymptotic coordinate transformations.\nstruct EthInertialRetardedTime : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 1>>;\n};\n\n/// Complex storage form for the asymptotically inertial retarded time, for\n/// taking spin-weighted derivatives\nstruct ComplexInertialRetardedTime : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n// prefix tags associated with the integrands which are used as input to solvers\n// for the CCE equations\n\n/// A prefix tag representing a quantity that will appear on the right-hand side\n/// of an explicitly regular differential equation\ntemplate <typename Tag>\nstruct Integrand : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing the boundary data for a quantity on the extraction\n/// surface.\ntemplate <typename Tag>\nstruct BoundaryValue : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing the gauge-transformed boundary data for a quantity\n/// on the extraction surface.\ntemplate <typename Tag>\nstruct EvolutionGaugeBoundaryValue : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing the coefficient of a pole part of the right-hand\n/// side of a singular differential equation\ntemplate <typename Tag>\nstruct PoleOfIntegrand : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing the regular part of the right-hand side of a\n/// regular differential equation\ntemplate <typename Tag>\nstruct RegularIntegrand : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing a linear factor that acts on `Tag`. To determine\n/// the spin weight, It is assumed that the linear factor plays the role of\n/// \\f$L\\f$ in an equation of the form,\n/// \\f$ (y - 1) \\partial_y H + L H + L^\\prime \\bar{H} = A + (1 - y) B \\f$\ntemplate <typename Tag>\nstruct LinearFactor : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing a linear factor that acts on `Tag`. To determine\n/// the spin weight, it is assumed that the linear factor plays the role of\n/// \\f$L^\\prime\\f$ in an equation of the form,\n/// \\f$ (y - 1) \\partial_y H + L H + L^\\prime \\bar{H} = A + (1 - y) B \\f$\ntemplate <typename Tag>\nstruct LinearFactorForConjugate : db::PrefixTag, db::SimpleTag {\n  using type =\n      Scalar<SpinWeighted<ComplexDataVector, 2 * Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n// Below are additional tags for values which are frequently used in CCE\n// calculations, and therefore worth caching\n\n/// Coordinate value \\f$(1 - y)\\f$, which will be cached and sent to the\n/// implementing functions\nstruct OneMinusY : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// A tag for the first time derivative of the worldtube parameter\n/// \\f$\\partial_u R\\f$, where \\f$R(u, \\theta, \\phi)\\f$ is Bondi\n/// radius of the worldtube.\nstruct DuR : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The value \\f$\\partial_u R / R\\f$, where \\f$R(u, \\theta, \\phi)\\f$ is Bondi\n/// radius of the worldtube.\nstruct DuRDividedByR : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The value \\f$\\eth R / R\\f$, where \\f$R(u, \\theta, \\phi)\\f$ is Bondi\n/// radius of the worldtube.\nstruct EthRDividedByR : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 1>>;\n  using derivative_kind = Spectral::Swsh::Tags::Eth;\n  static constexpr int spin = 1;\n};\n\n/// The value \\f$\\eth \\eth R / R\\f$, where \\f$R(u, \\theta, \\phi)\\f$ is Bondi\n/// radius of the worldtube.\nstruct EthEthRDividedByR : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 2>>;\n  using derivative_kind = Spectral::Swsh::Tags::EthEth;\n  static constexpr int spin = 2;\n};\n\n/// The value \\f$\\eth \\bar{\\eth} R / R\\f$, where \\f$R(u, \\theta, \\phi)\\f$ is\n/// Bondi radius of the worldtube.\nstruct EthEthbarRDividedByR : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n  using derivative_kind = Spectral::Swsh::Tags::EthEthbar;\n  static constexpr int spin = 0;\n};\n\n/// The value \\f$\\exp(2\\beta)\\f$.\nstruct Exp2Beta : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The value \\f$ \\bar{J} (Q - 2 \\eth \\beta ) \\f$.\nstruct JbarQMinus2EthBeta : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n};\n\n/// The Bondi radius \\f$R(u, \\theta, \\phi)\\f$ is of the worldtube.\nstruct BondiR : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n  static std::string name() { return \"R\"; }\n};\n\nstruct EndTime : db::SimpleTag {\n  using type = double;\n};\n\nstruct StartTime : db::SimpleTag {\n  using type = double;\n};\n\nusing LMax = Spectral::Swsh::Tags::LMax;\nusing NumberOfRadialPoints = Spectral::Swsh::Tags::NumberOfRadialPoints;\n\n/// The (adapted) Newman-Penrose spin coefficient $\\alpha^{SW}$. See\n/// documentation of `newman_penrose_alpha()` for definition.\nstruct NewmanPenroseAlpha : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n};\n\n/// The (adapted) Newman-Penrose spin coefficient $\\beta_{NP}^{SW}$. See\n/// documentation of `newman_penrose_beta()` for definition.\nstruct NewmanPenroseBeta : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, +1>>;\n};\n\n/// The (adapted) Newman-Penrose spin coefficient $\\gamma^{SW}$. See\n/// documentation of `newman_penrose_gamma()` for definition.\nstruct NewmanPenroseGamma : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The (adapted) Newman-Penrose spin coefficient $\\epsilon^{SW}$. See\n/// documentation of `newman_penrose_epsilon()` for definition.\nstruct NewmanPenroseEpsilon : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The Newman-Penrose spin coefficient $\\kappa$. In the choice of tetrad of\n/// \\cite Moxon2020gha, $\\kappa=0$. Therefore, this tag should never actually be\n/// used.  It is declared here so it does not seem like it is \"missing\", and to\n/// document that any calculation involving $\\kappa$ should be simplified with\n/// $\\kappa=0$.\nstruct NewmanPenroseKappa : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, +1>>;\n};\n\n/// The Newman-Penrose spin coefficient $\\tau$. See documentation of\n/// `newman_penrose_tau()` for definition.\nstruct NewmanPenroseTau : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, +1>>;\n};\n\n/// The Newman-Penrose spin coefficient $\\sigma$. See documentation of\n/// `newman_penrose_sigma()` for definition.\nstruct NewmanPenroseSigma : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, +2>>;\n};\n\n/// The Newman-Penrose spin coefficient $\\rho$. See documentation of\n/// `newman_penrose_rho()` for definition.\nstruct NewmanPenroseRho : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The Newman-Penrose spin coefficient $\\pi$. See documentation of\n/// `newman_penrose_pi()` for definition.\nstruct NewmanPenrosePi : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n};\n\n/// The Newman-Penrose spin coefficient $\\nu$. See documentation of\n/// `newman_penrose_nu()` for definition.\nstruct NewmanPenroseNu : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n};\n\n/// The Newman-Penrose spin coefficient $\\mu$. See documentation of\n/// `newman_penrose_mu()` for definition.\nstruct NewmanPenroseMu : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The Newman-Penrose spin coefficient $\\lambda$. See documentation of\n/// `newman_penrose_lambda()` for definition.\nstruct NewmanPenroseLambda : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -2>>;\n};\n\n/// The Weyl scalar \\f$\\Psi_0\\f$\nstruct Psi0 : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 2>>;\n};\n\n/// The Weyl scalar \\f$\\Psi_0\\f$ for matching (in the Cauchy frame)\nstruct Psi0Match : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 2>>;\n};\n/// The Weyl scalar \\f$\\Psi_1\\f$\nstruct Psi1 : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 1>>;\n};\n\n/// The Weyl scalar \\f$\\Psi_2\\f$\nstruct Psi2 : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n\n/// The Weyl scalar \\f$\\Psi_3\\f$\nstruct Psi3 : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n};\n\n/// The Weyl scalar \\f$\\Psi_4\\f$\nstruct Psi4 : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -2>>;\n};\n\n/// The gravitational wave strain \\f$h\\f$\nstruct Strain : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -2>>;\n};\n\n/// A prefix tag representing the time integral of the value it prefixes\ntemplate <typename Tag>\nstruct TimeIntegral : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing the value at \\f$\\mathcal I^+\\f$\ntemplate <typename Tag>\nstruct ScriPlus : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing an additional correction factor necessary to\n/// compute the quantity at \\f$\\mathcal I^+\\f$\ntemplate <typename Tag>\nstruct ScriPlusFactor : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n  using tag = Tag;\n};\n\n/// A prefix tag representing Klein-Gordon sources in Cce hypersurface equations\ntemplate <typename Tag>\nstruct KleinGordonSource : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Tag::type::type::spin>>;\n  using tag = Tag;\n};\n\ntemplate <typename ToInterpolate, typename ObservationTag>\nstruct InterpolationManager : db::SimpleTag {\n  using type = ScriPlusInterpolationManager<ToInterpolate, ObservationTag>;\n  static std::string name() {\n    return \"InterpolationManager(\" + db::tag_name<ObservationTag>() + \")\";\n  }\n};\n\n/// During self-start, we must be in lockstep with the GH system (if running\n/// concurrently), because the step size is unchangable during self-start.\nstruct SelfStartGhInterfaceManager : db::SimpleTag {\n  using type = InterfaceManagers::GhLockstep;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static InterfaceManagers::GhLockstep create_from_options() {\n    return Cce::InterfaceManagers::GhLockstep();\n  }\n};\n}  // namespace Tags\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/WorldtubeBufferUpdater.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n\n#include <algorithm>\n#include <complex>\n#include <cstddef>\n#include <stdexcept>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Evolution/Systems/Cce/ExtractionRadius.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace detail {\nstd::pair<size_t, size_t> create_span_for_time_value(\n    const double time, const size_t pad, const size_t interpolator_length,\n    const size_t lower_bound, const size_t upper_bound,\n    const DataVector& time_buffer) {\n  ASSERT(\n      lower_bound < upper_bound,\n      \"The supplied `lower_bound` is greater than `upper_bound`, which is not \"\n      \"permitted\");\n  ASSERT(2 * interpolator_length + pad <= upper_bound,\n         \"The combined `interpolator_length` and `pad` is too large for the \"\n         \"supplied `upper_bound`.\\nupper_bound=\"\n             << upper_bound << \"\\npad=\" << pad\n             << \"\\ninterpolator_length=\" << interpolator_length);\n\n  size_t range_start = lower_bound;\n  size_t range_end = upper_bound;\n  while (range_end - range_start > 1) {\n    if (time_buffer[(range_start + range_end) / 2] < time) {\n      range_start = (range_start + range_end) / 2;\n    } else {\n      range_end = (range_start + range_end) / 2;\n    }\n  }\n  // always keep the difference between start and end the same, even when\n  // the interpolations starts to get worse\n  size_t span_start = lower_bound;\n  size_t span_end =\n      std::min(interpolator_length * 2 + pad + lower_bound, upper_bound);\n  if (range_end + interpolator_length + pad > upper_bound) {\n    span_start =\n        std::max(upper_bound - (interpolator_length * 2 + pad), lower_bound);\n    span_end = upper_bound;\n  } else if (range_start + 1 > lower_bound + interpolator_length) {\n    span_start = range_start - interpolator_length;\n    span_end = range_end + interpolator_length + pad - 1;\n  }\n\n  return std::make_pair(span_start, span_end);\n}\n\nvoid set_time_buffer_and_lmax(const gsl::not_null<DataVector*> time_buffer,\n                              size_t& l_max, const h5::Dat& data,\n                              const bool is_real, const bool is_modal_data,\n                              const bool is_complex) {\n  const auto data_table_dimensions = data.get_dimensions();\n  const Matrix time_matrix =\n      data.get_data_subset(std::vector<size_t>{0}, 0, data_table_dimensions[0]);\n  *time_buffer = DataVector{data_table_dimensions[0]};\n\n  for (size_t i = 0; i < data_table_dimensions[0]; ++i) {\n    (*time_buffer)[i] = time_matrix(i, 0);\n  }\n\n  if (is_modal_data) {\n    // If the quantitiy is real, then there's no way for us to do a check just\n    // from the number of columns alone. If not, then we expect an even number\n    // of total modes (both real and imaginary), so the number of columns should\n    // be odd with the addition of the time column\n    if (not is_real and data_table_dimensions[1] % 2 != 1) {\n      ERROR(\"Dimensions of subfile \"\n            << data.subfile_path()\n            << \" for modal data are incorrect. Was expecting an odd number of \"\n               \"columns (because of the time), but got \"\n            << data_table_dimensions[1] << \" instead.\");\n    }\n\n    // If it's real the number of columns is (l+1)^2. If it's not, then it's\n    // 2(l+1)^2\n    const size_t l_plus_one_squared =\n        (data_table_dimensions[1] - 1) / (is_real ? 1 : 2);\n    l_max =\n        static_cast<size_t>(sqrt(static_cast<double>(l_plus_one_squared)) - 1);\n  } else {\n    // Can only check number of columns for nodal data if it's complex\n    if (is_complex and data_table_dimensions[1] % 2 != 1) {\n      ERROR(\"Dimensions of subfile \"\n            << data.subfile_path()\n            << \" for nodal data are incorrect. Was expecting an odd number of \"\n               \"columns (because of the time), but got \"\n            << data_table_dimensions[1] << \" instead.\");\n    }\n\n    // If number of real values N = (l+1)*(2l+1), then\n    // l = ( -3 + sqrt(9 + 8 * (N-1)) ) / 4. But the dimensions[1] includes time\n    // so we have to account for that by subtracting 1. Also, if this is complex\n    // nodal data, we have to do N=(dimensions[1]-1)/2 first\n    const size_t num_real_values = is_complex\n                                       ? (data_table_dimensions[1] - 1) / 2\n                                       : data_table_dimensions[1] - 1;\n    if (num_real_values == 0) {\n      ERROR(\"Not enough columns to read \"\n            << (is_complex ? \"complex\" : \"real\")\n            << \" nodal data from. Number of columns in file (\"\n            << data_table_dimensions[1]\n            << \"), number of columns without time column (\"\n            << data_table_dimensions[1] << \"), number of real valued columns (\"\n            << num_real_values << \")\");\n    }\n    const size_t discriminant = 9 + 8 * (num_real_values - 1);\n    l_max =\n        static_cast<size_t>((sqrt(static_cast<double>(discriminant)) - 3)) / 4;\n  }\n}\n\ntemplate <bool IsModal, int Spin, typename T>\nvoid update_buffer(const gsl::not_null<T*> buffer_to_update,\n                   const h5::Dat& read_data, const size_t computation_l_max,\n                   const size_t l_max, const size_t time_span_start,\n                   const size_t time_span_end,\n                   const bool time_varies_fastest = true) {\n  constexpr bool is_real = Spin == 0;\n  size_t number_of_columns = read_data.get_dimensions()[1];\n  const size_t result_l_max = std::min(l_max, computation_l_max);\n  // No x2 because the datatypes are std::complex\n  const size_t expected_size =\n      (time_span_end - time_span_start) *\n      (IsModal ? square(computation_l_max + 1)\n               : Spectral::Swsh::number_of_swsh_collocation_points(\n                     computation_l_max));\n\n  if (UNLIKELY(buffer_to_update->size() != expected_size)) {\n    ERROR(\"Incorrect storage size for the data to be loaded in. Expected \"\n          << expected_size << \", but got \" << buffer_to_update->size()\n          << \" instead.\");\n  }\n\n  std::vector<size_t> cols(number_of_columns - 1);\n  std::iota(cols.begin(), cols.end(), 1);\n  auto data_matrix =\n      read_data.get_data_subset<std::vector<std::vector<double>>>(\n          cols, time_span_start, time_span_end - time_span_start);\n  *buffer_to_update = 0.0;\n\n  // For modal, only x2 if it's not real. If nodal, always x2\n  const size_t expected_dat_modal_size = (is_real ? 1 : 2) * square(l_max + 1);\n  const size_t expected_dat_nodal_size =\n      2 * Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  if (cols.size() !=\n      (IsModal ? expected_dat_modal_size : expected_dat_nodal_size)) {\n    ERROR(\"Incorrect number of columns in Dat file \"\n          << read_data.subfile_path() << \". Expected (excluding time column) \"\n          << (IsModal ? expected_dat_modal_size : expected_dat_nodal_size)\n          << \", but got \" << cols.size());\n  }\n\n  for (size_t time_row = 0; time_row < time_span_end - time_span_start;\n       ++time_row) {\n    // For modal data we have to compute the correct goldberg mode indices for\n    // each l and m\n    if constexpr (IsModal) {\n      for (int l = 0; l <= static_cast<int>(result_l_max); ++l) {\n        for (int m = -l; m <= l; ++m) {\n          const size_t buffer_index =\n              time_varies_fastest\n                  ? Spectral::Swsh::goldberg_mode_index(\n                        computation_l_max, static_cast<size_t>(l), m) *\n                            (time_span_end - time_span_start) +\n                        time_row\n                  : time_row * square(computation_l_max + 1) +\n                        Spectral::Swsh::goldberg_mode_index(\n                            computation_l_max, static_cast<size_t>(l), m);\n          // NOLINTBEGIN\n          // If this quantity is real we don't expect to store the imaginary m=0\n          // modes in the H5 file so those have to be handled specially. Then\n          // for +/- and even/odd m-modes we have to have the correct sign for\n          // the coefficient.\n          if (is_real) {\n            if (m == 0) {\n              (*buffer_to_update)[buffer_index] = std::complex<double>(\n                  data_matrix[time_row][static_cast<size_t>(square(l))], 0.0);\n            } else {\n              const double factor = (m > 0 or abs(m) % 2 == 0) ? 1.0 : -1.0;\n              const size_t matrix_index =\n                  static_cast<size_t>(square(l) + 2 * abs(m));\n              (*buffer_to_update)[buffer_index] =\n                  factor * std::complex<double>(\n                               data_matrix[time_row][matrix_index - 1],\n                               sgn(m) * data_matrix[time_row][matrix_index]);\n            }\n          } else {\n            // If this quantity is complex, then it's straight forward to\n            // populate the buffer\n            const size_t matrix_goldberg_index =\n                Spectral::Swsh::goldberg_mode_index(l_max,\n                                                    static_cast<size_t>(l), m);\n            (*buffer_to_update)[buffer_index] = std::complex<double>(\n                data_matrix[time_row][2 * matrix_goldberg_index],\n                data_matrix[time_row][2 * matrix_goldberg_index + 1]);\n          }\n          // NOLINTEND\n        }\n      }\n    } else {\n      (void)result_l_max;\n      // For nodal data, it must be complex so for each grid point we just read\n      // in the real and imaginary components\n      const size_t number_of_angular_points =\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n      for (size_t i = 0; i < number_of_angular_points; i++) {\n        const size_t buffer_index =\n            time_varies_fastest\n                ? i * (time_span_end - time_span_start) + time_row\n                : time_row * number_of_angular_points + i;\n        (*buffer_to_update)[buffer_index] = std::complex<double>(\n            data_matrix[time_row][2 * i], data_matrix[time_row][2 * i + 1]);\n      }\n    }\n  }\n}\n\ntemplate <bool IsModal, typename InputTags>\ndouble update_buffers_for_time(\n    const gsl::not_null<Variables<InputTags>*> buffers,\n    const gsl::not_null<size_t*> time_span_start,\n    const gsl::not_null<size_t*> time_span_end, const double time,\n    const size_t computation_l_max, const size_t l_max,\n    const size_t interpolator_length, const size_t buffer_depth,\n    const DataVector& time_buffer,\n    const tuples::tagged_tuple_from_typelist<\n        db::wrap_tags_in<Tags::detail::InputDataSet, InputTags>>& dataset_names,\n    const h5::H5File<h5::AccessType::ReadOnly>& cce_data_file,\n    const bool time_varies_fastest = true) {\n  if (not IsModal and computation_l_max != l_max) {\n    ERROR(\n        \"When reading in nodal data, the LMax that \"\n        \"the BufferUpdater was constructed with (\"\n        << l_max\n        << \") must be the same as the computation LMax passed to the \"\n           \"update_buffers_for_time function (\"\n        << computation_l_max << \").\");\n  }\n  if (*time_span_end >= time_buffer.size()) {\n    return std::numeric_limits<double>::quiet_NaN();\n  }\n  if (*time_span_end > interpolator_length and\n      time_buffer[*time_span_end - interpolator_length] > time) {\n    // the next time an update will be required\n    return time_buffer[*time_span_end - interpolator_length + 1];\n  }\n  // find the time spans that are needed\n  auto new_span_pair = detail::create_span_for_time_value(\n      time, buffer_depth, interpolator_length, 0, time_buffer.size(),\n      time_buffer);\n  *time_span_start = new_span_pair.first;\n  *time_span_end = new_span_pair.second;\n  // load the desired time spans into the buffers\n  tmpl::for_each<InputTags>([&](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    update_buffer<IsModal, tag::type::type::spin>(\n        make_not_null(&get(get<tag>(*buffers)).data()),\n        cce_data_file.get<h5::Dat>(\n            \"/\" + get<Tags::detail::InputDataSet<tag>>(dataset_names)),\n        computation_l_max, l_max, *time_span_start, *time_span_end,\n        time_varies_fastest);\n    cce_data_file.close_current_object();\n  });\n  // the next time an update will be required\n  return time_buffer[std::min(*time_span_end - interpolator_length + 1,\n                              time_buffer.size() - 1)];\n}\n\n}  // namespace detail\n\ntemplate <typename T>\nMetricWorldtubeH5BufferUpdater<T>::MetricWorldtubeH5BufferUpdater(\n    const std::string& cce_data_filename,\n    const std::optional<double> extraction_radius, const bool descending_m,\n    const std::optional<AdmOptions>& adm_options)\n    : cce_data_file_{cce_data_filename},\n      filename_{cce_data_filename},\n      descending_m_(descending_m),\n      adm_options_(adm_options) {\n  if (is_modal and adm_options_.has_value()) {\n    ERROR(\n        \"Cannot read in modal data with ADM quantities (like extrinsic \"\n        \"curvature).\");\n  }\n\n  get<Tags::detail::InputDataSet<Tags::detail::SpatialMetric<T>>>(\n      dataset_names_) = \"/g\";\n  get<Tags::detail::InputDataSet<\n      Tags::detail::Dr<Tags::detail::SpatialMetric<T>>>>(dataset_names_) =\n      \"/Drg\";\n  get<Tags::detail::InputDataSet<\n      Tags::detail::Dx<Tags::detail::SpatialMetric<T>>>>(dataset_names_) =\n      \"/Dxg\";\n  get<Tags::detail::InputDataSet<\n      Tags::detail::Dy<Tags::detail::SpatialMetric<T>>>>(dataset_names_) =\n      \"/Dyg\";\n  get<Tags::detail::InputDataSet<\n      Tags::detail::Dz<Tags::detail::SpatialMetric<T>>>>(dataset_names_) =\n      \"/Dzg\";\n  get<Tags::detail::InputDataSet<::Tags::dt<Tags::detail::SpatialMetric<T>>>>(\n      dataset_names_) = \"/Dtg\";\n\n  get<Tags::detail::InputDataSet<Tags::detail::Shift<T>>>(dataset_names_) =\n      \"/Shift\";\n  get<Tags::detail::InputDataSet<Tags::detail::Dr<Tags::detail::Shift<T>>>>(\n      dataset_names_) = \"/DrShift\";\n  get<Tags::detail::InputDataSet<Tags::detail::Dx<Tags::detail::Shift<T>>>>(\n      dataset_names_) = \"/DxShift\";\n  get<Tags::detail::InputDataSet<Tags::detail::Dy<Tags::detail::Shift<T>>>>(\n      dataset_names_) = \"/DyShift\";\n  get<Tags::detail::InputDataSet<Tags::detail::Dz<Tags::detail::Shift<T>>>>(\n      dataset_names_) = \"/DzShift\";\n  get<Tags::detail::InputDataSet<::Tags::dt<Tags::detail::Shift<T>>>>(\n      dataset_names_) = \"/DtShift\";\n\n  get<Tags::detail::InputDataSet<Tags::detail::Lapse<T>>>(dataset_names_) =\n      \"/Lapse\";\n  get<Tags::detail::InputDataSet<Tags::detail::Dr<Tags::detail::Lapse<T>>>>(\n      dataset_names_) = \"/DrLapse\";\n  get<Tags::detail::InputDataSet<Tags::detail::Dx<Tags::detail::Lapse<T>>>>(\n      dataset_names_) = \"/DxLapse\";\n  get<Tags::detail::InputDataSet<Tags::detail::Dy<Tags::detail::Lapse<T>>>>(\n      dataset_names_) = \"/DyLapse\";\n  get<Tags::detail::InputDataSet<Tags::detail::Dz<Tags::detail::Lapse<T>>>>(\n      dataset_names_) = \"/DzLapse\";\n  get<Tags::detail::InputDataSet<::Tags::dt<Tags::detail::Lapse<T>>>>(\n      dataset_names_) = \"/DtLapse\";\n\n  get<Tags::detail::InputDataSet<Tags::detail::ExtrinsicCurvature<T>>>(\n      dataset_names_) = \"/K\";\n  get<Tags::detail::InputDataSet<Tags::detail::AuxiliaryShift<T>>>(\n      dataset_names_) = \"/AuxiliaryShift\";\n  get<Tags::detail::InputDataSet<Tags::detail::ConformalChristoffel<T>>>(\n      dataset_names_) = \"/ConformalChristoffel\";\n\n  // 'VersionHist' is a feature written by SpEC to indicate the details of the\n  // file format. This line determines whether or not the radial derivatives\n  // require renormalization based on whether the SpEC version that produced it\n  // was an old one that had a particular normalization bug\n  has_version_history_ = cce_data_file_.exists<h5::Version>(\"/VersionHist\");\n\n  extraction_radius_ =\n      Cce::get_extraction_radius(cce_data_filename, extraction_radius, true)\n          .value();\n\n  detail::set_time_buffer_and_lmax(make_not_null(&time_buffer_), l_max_,\n                                   cce_data_file_.get<h5::Dat>(\"/Lapse\"), false,\n                                   is_modal, is_modal);\n  cce_data_file_.close_current_object();\n}\n\ntemplate <typename T>\ndouble MetricWorldtubeH5BufferUpdater<T>::update_buffers_for_time(\n    const gsl::not_null<Variables<cce_metric_input_tags<T>>*> buffers,\n    const gsl::not_null<size_t*> time_span_start,\n    const gsl::not_null<size_t*> time_span_end, const double time,\n    const size_t computation_l_max, const size_t interpolator_length,\n    const size_t buffer_depth, const bool time_varies_fastest) const {\n  // We require these to be the same for nodal data since we can't easily\n  // extend nodal data like we can modal data.\n  if (not is_modal and computation_l_max != l_max_) {\n    ERROR(\n        \"When reading in nodal data, the LMax that \"\n        \"MetricWorldtubeH5BufferUpdater was constructed with (\"\n        << l_max_\n        << \") must be the same as the computation LMax passed to the \"\n           \"update_buffers_for_time function (\"\n        << computation_l_max << \").\");\n  }\n  if (*time_span_end >= time_buffer_.size()) {\n    return std::numeric_limits<double>::quiet_NaN();\n  }\n  if (*time_span_end > interpolator_length and\n      time_buffer_[*time_span_end - interpolator_length] > time) {\n    // the next time an update will be required\n    return time_buffer_[*time_span_end - interpolator_length + 1];\n  }\n  // find the time spans that are needed\n  auto new_span_pair = detail::create_span_for_time_value(\n      time, buffer_depth, interpolator_length, 0, time_buffer_.size(),\n      time_buffer_);\n  *time_span_start = new_span_pair.first;\n  *time_span_end = new_span_pair.second;\n  // load the desired time spans into the buffers\n  if (not adm_options_.has_value()) {\n    update_radial_formulation(buffers, computation_l_max, *time_span_start,\n                              *time_span_end, time_varies_fastest);\n  } else if constexpr (not is_modal) {\n    update_adm_formulation(buffers, computation_l_max, *time_span_start,\n                           *time_span_end, time_varies_fastest);\n  }\n\n  // the next time an update will be required\n  return time_buffer_[std::min(*time_span_end - interpolator_length + 1,\n                               time_buffer_.size() - 1)];\n}\n\ntemplate <typename T>\nvoid MetricWorldtubeH5BufferUpdater<T>::update_radial_formulation(\n    const gsl::not_null<Variables<cce_metric_input_tags<T>>*> buffers,\n    const size_t computation_l_max, const size_t time_span_start,\n    const size_t time_span_end, const bool time_varies_fastest) const {\n  // spatial metric\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      tmpl::for_each<\n          Tags::detail::apply_derivs_t<Tags::detail::SpatialMetric<T>>>(\n          [&, this](auto tag_v) {\n            using tag = typename decltype(tag_v)::type;\n            this->update_buffer(\n                make_not_null(&get<tag>(*buffers).get(i, j)),\n                cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n                    get<Tags::detail::InputDataSet<tag>>(dataset_names_), i,\n                    j)),\n                computation_l_max, time_span_start, time_span_end,\n                time_varies_fastest);\n            cce_data_file_.close_current_object();\n          });\n    }\n    // shift\n    tmpl::for_each<Tags::detail::apply_derivs_t<Tags::detail::Shift<T>>>(\n        [&, this](auto tag_v) {\n          using tag = typename decltype(tag_v)::type;\n          this->update_buffer(\n              make_not_null(&get<tag>(*buffers).get(i)),\n              cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n                  get<Tags::detail::InputDataSet<tag>>(dataset_names_), i)),\n              computation_l_max, time_span_start, time_span_end,\n              time_varies_fastest);\n          cce_data_file_.close_current_object();\n        });\n  }\n  // lapse\n  tmpl::for_each<Tags::detail::apply_derivs_t<Tags::detail::Lapse<T>>>(\n      [&, this](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        this->update_buffer(\n            make_not_null(&get(get<tag>(*buffers))),\n            cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n                get<Tags::detail::InputDataSet<tag>>(dataset_names_))),\n            computation_l_max, time_span_start, time_span_end,\n            time_varies_fastest);\n        cce_data_file_.close_current_object();\n      });\n}\n\ntemplate <typename T>\ntemplate <typename U>\ntypename std::enable_if_t<std::is_same_v<U, DataVector>>\nMetricWorldtubeH5BufferUpdater<T>::update_adm_formulation(\n    const gsl::not_null<Variables<cce_metric_input_tags<DataVector>>*> buffers,\n    const size_t computation_l_max, const size_t time_span_start,\n    const size_t time_span_end, const bool time_varies_fastest) const {\n  // This makes the code much less complicated because of contiguous memory for\n  // a given time\n  if (UNLIKELY(time_varies_fastest)) {\n    ERROR(\n        \"Time must not vary fastest when using ADM quantities for the metric \"\n        \"worldtube buffer updater.\");\n  }\n  const size_t number_of_times = time_span_end - time_span_start;\n  const size_t size_of_individual_time =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max_);\n  const size_t size_of_full_buffer =\n      get<0, 0>(get<Tags::detail::SpatialMetric<DataVector>>(*buffers)).size();\n\n  TempBuffer<tmpl::list<\n      ::Tags::Tempi<0, 3>,\n      ::Tags::Tempi<1, 2, ::Frame::Spherical<::Frame::Inertial>>,\n      ::Tags::Tempi<2, 3>, ::Tags::TempiJ<0, 3>, ::Tags::Tempijj<0, 3>,\n      ::Tags::Tempii<0, 3>, ::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n      ::Tags::TempII<0, 3>, ::Tags::TempI<0, 3>, ::Tags::TempI<1, 3>>>\n      temp_buffer{size_of_full_buffer};\n  auto& cartesian_cauchy_coordinates_buffer =\n      get<::Tags::Tempi<0, 3>>(temp_buffer);\n  auto& unused_angular_cauchy_coordinates_buffer =\n      get<::Tags::Tempi<1, 2, ::Frame::Spherical<::Frame::Inertial>>>(\n          temp_buffer);\n\n  // Have to compute angular coordinates for each time independently\n  for (size_t i = 0; i < number_of_times; i++) {\n    tnsr::i<DataVector, 3> cartesian_cauchy_coordinates{};\n    tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>\n        unused_angular_cauchy_coordinates{};\n    // NOLINTBEGIN\n    for (size_t j = 0; j < 3; j++) {\n      cartesian_cauchy_coordinates[j].set_data_ref(\n          cartesian_cauchy_coordinates_buffer[j].data() +\n              i * size_of_individual_time,\n          size_of_individual_time);\n      if (j < 2) {\n        unused_angular_cauchy_coordinates[j].set_data_ref(\n            unused_angular_cauchy_coordinates_buffer[j].data() +\n                i * size_of_individual_time,\n            size_of_individual_time);\n      }\n      // NOLINTEND\n    }\n\n    // Calling this function is easier than calling the individual parts\n    // that only compute the cartesian coords\n    Spectral::Swsh::create_angular_and_cartesian_coordinates(\n        make_not_null(&cartesian_cauchy_coordinates),\n        make_not_null(&unused_angular_cauchy_coordinates), l_max_);\n  }\n\n  // Need lots of temporaries to convert quantities\n  auto& cartesian_deriv_lapse = get<::Tags::Tempi<2, 3>>(temp_buffer);\n  auto& cartesian_deriv_shift = get<::Tags::TempiJ<0, 3>>(temp_buffer);\n  auto& cartesian_deriv_spatial_metric =\n      get<::Tags::Tempijj<0, 3>>(temp_buffer);\n  auto& extrinsic_curvature = get<::Tags::Tempii<0, 3>>(temp_buffer);\n  auto& trace_extrinsic_curvature = get<::Tags::TempScalar<0>>(temp_buffer);\n  auto& determinant_spatial_metric = get<::Tags::TempScalar<1>>(temp_buffer);\n  auto& inverse_spatial_metric = get<::Tags::TempII<0, 3>>(temp_buffer);\n  auto& auxiliary_shift = get<::Tags::TempI<0, 3>>(temp_buffer);\n  auto& conformal_christoffel = get<::Tags::TempI<1, 3>>(temp_buffer);\n\n  const auto set_radial_deriv =\n      [&cartesian_cauchy_coordinates_buffer](\n          const gsl::not_null<DataVector*> radial_deriv,\n          const DataVector& x_deriv, const DataVector& y_deriv,\n          const DataVector& z_deriv) {\n        // These are unit coords of the collocation points. So\n        // sin(theta)*cos(phi) = x/r = x, when the coords are unit (etc...)\n        (*radial_deriv) =\n            get<0>(cartesian_cauchy_coordinates_buffer) * x_deriv +\n            get<1>(cartesian_cauchy_coordinates_buffer) * y_deriv +\n            get<2>(cartesian_cauchy_coordinates_buffer) * z_deriv;\n      };\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      // extrinsic curvature\n      this->update_buffer(\n          make_not_null(&extrinsic_curvature.get(i, j)),\n          cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n              get<Tags::detail::InputDataSet<\n                  Tags::detail::ExtrinsicCurvature<T>>>(dataset_names_),\n              i, j)),\n          computation_l_max, time_span_start, time_span_end,\n          time_varies_fastest);\n      cce_data_file_.close_current_object();\n\n      // spatial metric\n      this->update_buffer(\n          make_not_null(\n              &get<Tags::detail::SpatialMetric<DataVector>>(*buffers).get(i,\n                                                                          j)),\n          cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n              get<Tags::detail::InputDataSet<Tags::detail::SpatialMetric<T>>>(\n                  dataset_names_),\n              i, j)),\n          computation_l_max, time_span_start, time_span_end,\n          time_varies_fastest);\n      cce_data_file_.close_current_object();\n\n      // dx spatial metric\n      tmpl::for_each<Tags::detail::cartesian_derivs_t<\n          Tags::detail::SpatialMetric<DataVector>>>([&, this](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        constexpr size_t index = tag::index;\n        this->update_buffer(\n            make_not_null(&cartesian_deriv_spatial_metric.get(index, i, j)),\n            cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n                get<Tags::detail::InputDataSet<tag>>(dataset_names_), i, j)),\n            computation_l_max, time_span_start, time_span_end,\n            time_varies_fastest);\n        cce_data_file_.close_current_object();\n      });\n\n      // dr spatial metric\n      set_radial_deriv(\n          make_not_null(\n              &get<Tags::detail::Dr<Tags::detail::SpatialMetric<DataVector>>>(\n                   *buffers)\n                   .get(i, j)),\n          cartesian_deriv_spatial_metric.get(0, i, j),\n          cartesian_deriv_spatial_metric.get(1, i, j),\n          cartesian_deriv_spatial_metric.get(2, i, j));\n    }\n\n    // auxiliary shift (first order) or conformal christoffel (other)\n    if (adm_options_->shift.is_first_order) {\n      this->update_buffer(\n          make_not_null(&auxiliary_shift.get(i)),\n          cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n              get<Tags::detail::InputDataSet<Tags::detail::AuxiliaryShift<T>>>(\n                  dataset_names_),\n              i)),\n          computation_l_max, time_span_start, time_span_end,\n          time_varies_fastest);\n      cce_data_file_.close_current_object();\n    } else {\n      this->update_buffer(\n          make_not_null(&conformal_christoffel.get(i)),\n          cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n              get<Tags::detail::InputDataSet<\n                  Tags::detail::ConformalChristoffel<T>>>(dataset_names_),\n              i)),\n          computation_l_max, time_span_start, time_span_end,\n          time_varies_fastest);\n      cce_data_file_.close_current_object();\n    }\n\n    // shift\n    this->update_buffer(\n        make_not_null(&get<Tags::detail::Shift<DataVector>>(*buffers).get(i)),\n        cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n            get<Tags::detail::InputDataSet<Tags::detail::Shift<T>>>(\n                dataset_names_),\n            i)),\n        computation_l_max, time_span_start, time_span_end, time_varies_fastest);\n    cce_data_file_.close_current_object();\n\n    // dx shift\n    tmpl::for_each<\n        Tags::detail::cartesian_derivs_t<Tags::detail::Shift<DataVector>>>(\n        [&, this](auto tag_v) {\n          using tag = typename decltype(tag_v)::type;\n          constexpr size_t index = tag::index;\n          this->update_buffer(\n              make_not_null(&cartesian_deriv_shift.get(index, i)),\n              cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n                  get<Tags::detail::InputDataSet<tag>>(dataset_names_), i)),\n              computation_l_max, time_span_start, time_span_end,\n              time_varies_fastest);\n          cce_data_file_.close_current_object();\n        });\n\n    // dr shift\n    set_radial_deriv(\n        make_not_null(\n            &get<Tags::detail::Dr<Tags::detail::Shift<DataVector>>>(*buffers)\n                 .get(i)),\n        cartesian_deriv_shift.get(0, i), cartesian_deriv_shift.get(1, i),\n        cartesian_deriv_shift.get(2, i));\n  }\n\n  // lapse\n  this->update_buffer(\n      make_not_null(&get(get<Tags::detail::Lapse<DataVector>>(*buffers))),\n      cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n          get<Tags::detail::InputDataSet<Tags::detail::Lapse<T>>>(\n              dataset_names_))),\n      computation_l_max, time_span_start, time_span_end, time_varies_fastest);\n  cce_data_file_.close_current_object();\n\n  // dx lapse\n  tmpl::for_each<\n      Tags::detail::cartesian_derivs_t<Tags::detail::Lapse<DataVector>>>(\n      [&, this](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        constexpr size_t index = tag::index;\n        this->update_buffer(\n            make_not_null(&get<index>(cartesian_deriv_lapse)),\n            cce_data_file_.get<h5::Dat>(detail::dataset_name_for_component(\n                get<Tags::detail::InputDataSet<tag>>(dataset_names_))),\n            computation_l_max, time_span_start, time_span_end,\n            time_varies_fastest);\n        cce_data_file_.close_current_object();\n      });\n\n  // dr lapse\n  set_radial_deriv(\n      make_not_null(&get(\n          get<Tags::detail::Dr<Tags::detail::Lapse<DataVector>>>(*buffers))),\n      get<0>(cartesian_deriv_lapse), get<1>(cartesian_deriv_lapse),\n      get<2>(cartesian_deriv_lapse));\n\n  const auto& spatial_metric =\n      get<Tags::detail::SpatialMetric<DataVector>>(*buffers);\n  const auto& lapse = get<Tags::detail::Lapse<DataVector>>(*buffers);\n  const auto& shift = get<Tags::detail::Shift<DataVector>>(*buffers);\n  auto& time_deriv_spatial_metric =\n      get<::Tags::dt<Tags::detail::SpatialMetric<DataVector>>>(*buffers);\n  auto& time_deriv_lapse =\n      get<::Tags::dt<Tags::detail::Lapse<DataVector>>>(*buffers);\n  auto& time_deriv_shift =\n      get<::Tags::dt<Tags::detail::Shift<DataVector>>>(*buffers);\n\n  // time deriv of spatial metric\n  gr::time_derivative_of_spatial_metric(\n      make_not_null(&time_deriv_spatial_metric), lapse, shift,\n      cartesian_deriv_shift, spatial_metric, cartesian_deriv_spatial_metric,\n      extrinsic_curvature);\n\n  determinant_and_inverse(make_not_null(&determinant_spatial_metric),\n                          make_not_null(&inverse_spatial_metric),\n                          spatial_metric);\n\n  // trace of extrinsic curvature\n  trace(make_not_null(&trace_extrinsic_curvature), extrinsic_curvature,\n        inverse_spatial_metric);\n\n  // time deriv of lapse and shift\n  get(time_deriv_lapse) = -2.0 * get(lapse) * get(trace_extrinsic_curvature);\n  for (size_t i = 0; i < 3; i++) {\n    if (adm_options_->shift.is_first_order) {\n      time_deriv_shift.get(i) =\n          adm_options_->shift.extra_factor * auxiliary_shift.get(i);\n    } else {\n      time_deriv_shift.get(i) =\n          adm_options_->shift.conformal_christoffel_factor *\n              conformal_christoffel.get(i) -\n          adm_options_->shift.extra_factor * shift.get(i);\n    }\n\n    if (adm_options_->lapse.is_advective) {\n      get(time_deriv_lapse) += shift.get(i) * cartesian_deriv_lapse.get(i);\n    }\n    if (adm_options_->shift.is_advective) {\n      for (size_t j = 0; j < 3; j++) {\n        time_deriv_shift.get(i) +=\n            shift.get(j) * cartesian_deriv_shift.get(j, i);\n      }\n    }\n  }\n}\n\ntemplate <typename T>\nstd::unique_ptr<WorldtubeBufferUpdater<cce_metric_input_tags<T>>>\nMetricWorldtubeH5BufferUpdater<T>::get_clone() const {\n  return std::make_unique<MetricWorldtubeH5BufferUpdater>(\n      MetricWorldtubeH5BufferUpdater{filename_});\n}\n\ntemplate <typename T>\nbool MetricWorldtubeH5BufferUpdater<T>::time_is_outside_range(\n    const double time) const {\n  return time < time_buffer_[0] or time > time_buffer_[time_buffer_.size() - 1];\n}\n\ntemplate <typename T>\nvoid MetricWorldtubeH5BufferUpdater<T>::pup(PUP::er& p) {\n  p | time_buffer_;\n  p | has_version_history_;\n  p | filename_;\n  p | descending_m_;\n  p | l_max_;\n  p | extraction_radius_;\n  p | dataset_names_;\n  p | adm_options_;\n  if (p.isUnpacking()) {\n    cce_data_file_ = h5::H5File<h5::AccessType::ReadOnly>{filename_};\n  }\n}\n\ntemplate <typename T>\nvoid MetricWorldtubeH5BufferUpdater<T>::update_buffer(\n    const gsl::not_null<T*> buffer_to_update, const h5::Dat& read_data,\n    const size_t computation_l_max, const size_t time_span_start,\n    const size_t time_span_end, const bool time_varies_fastest) const {\n  const size_t number_of_columns = read_data.get_dimensions()[1];\n  const size_t expected_size =\n      (time_span_end - time_span_start) *\n      (is_modal ? square(computation_l_max + 1)\n                : Spectral::Swsh::number_of_swsh_collocation_points(\n                      computation_l_max));\n  // We require these to be the same since for nodal data we can't easily extend\n  // nodal data like we can modal data.\n  if (not is_modal and computation_l_max != l_max_) {\n    ERROR(\n        \"When reading in nodal data, the LMax that \"\n        \"MetricWorldtubeH5BufferUpdater was constructed with (\"\n        << l_max_\n        << \") must be the same as the computation LMax passed to the \"\n           \"update_buffer functions (\"\n        << computation_l_max << \").\");\n  }\n  if (UNLIKELY(buffer_to_update->size() != expected_size)) {\n    ERROR(\n        \"Incorrect \" << (is_modal ? \"modal\" : \"nodal\")\n                     << \" storage size for the data to be loaded in. Expected \"\n                     << expected_size << \" but got \" << buffer_to_update->size()\n                     << \". Time span is \" << time_span_end << \"-\"\n                     << time_span_start << \"=\"\n                     << time_span_end - time_span_start);\n  }\n  auto cols = alg::iota(std::vector<size_t>(number_of_columns - 1), 1_st);\n  auto data_matrix =\n      read_data.get_data_subset<std::vector<std::vector<double>>>(\n          cols, time_span_start, time_span_end - time_span_start);\n\n  *buffer_to_update = 0.0;\n  for (size_t time_row = 0; time_row < time_span_end - time_span_start;\n       ++time_row) {\n    // If we have modal data, we must construct the ComplexModalVector\n    if constexpr (is_modal) {\n      for (int l = 0;\n           l <= static_cast<int>(std::min(computation_l_max, l_max_)); ++l) {\n        for (int m = -l; m <= l; ++m) {\n          // -m because SpEC format is stored in decending m.\n          const int em = descending_m_ ? -m : m;\n          const size_t matrix_mode_index = Spectral::Swsh::goldberg_mode_index(\n              l_max_, static_cast<size_t>(l), em);\n          const size_t buffer_mode_index = Spectral::Swsh::goldberg_mode_index(\n              computation_l_max, static_cast<size_t>(l), m);\n          const size_t buffer_index =\n              time_varies_fastest\n                  ? buffer_mode_index * (time_span_end - time_span_start) +\n                        time_row\n                  : time_row * square(computation_l_max + 1) +\n                        buffer_mode_index;\n\n          (*buffer_to_update)[buffer_index] = std::complex<double>(\n              data_matrix[time_row][2 * matrix_mode_index],\n              data_matrix[time_row][2 * matrix_mode_index + 1]);\n        }\n      }\n    } else {\n      // Otherwise we just read the nodal data in as is\n      for (size_t i = 0; i < cols.size(); i++) {\n        const size_t buffer_index =\n            time_varies_fastest\n                ? i * (time_span_end - time_span_start) + time_row\n                : time_row * cols.size() + i;\n        (*buffer_to_update)[buffer_index] = data_matrix[time_row][i];\n      }\n    }\n  }\n}\n\nnamespace {\n// Convenience metafunction to return the correct tag depending on the template\n// parameter T\ntemplate <typename T, typename Tag>\nstruct name_tag {\n  using type = Tags::detail::InputDataSet<tmpl::conditional_t<\n      std::is_same_v<T, ComplexModalVector>,\n      Spectral::Swsh::Tags::SwshTransform<Tag>, Tags::BoundaryValue<Tag>>>;\n};\n\ntemplate <typename T, typename Tag>\nusing name_tag_t = typename name_tag<T, Tag>::type;\n}  // namespace\n\ntemplate <typename T>\nBondiWorldtubeH5BufferUpdater<T>::BondiWorldtubeH5BufferUpdater(\n    const std::string& cce_data_filename,\n    const std::optional<double> extraction_radius)\n    : cce_data_file_{cce_data_filename}, filename_{cce_data_filename} {\n  get<name_tag_t<T, Tags::BondiBeta>>(dataset_names_) = \"Beta\";\n  get<name_tag_t<T, Tags::BondiU>>(dataset_names_) = \"U\";\n  get<name_tag_t<T, Tags::BondiQ>>(dataset_names_) = \"Q\";\n  get<name_tag_t<T, Tags::BondiW>>(dataset_names_) = \"W\";\n  get<name_tag_t<T, Tags::BondiJ>>(dataset_names_) = \"J\";\n  get<name_tag_t<T, Tags::Dr<Tags::BondiJ>>>(dataset_names_) = \"DrJ\";\n  get<name_tag_t<T, Tags::Du<Tags::BondiJ>>>(dataset_names_) = \"H\";\n  get<name_tag_t<T, Tags::BondiR>>(dataset_names_) = \"R\";\n  get<name_tag_t<T, Tags::Du<Tags::BondiR>>>(dataset_names_) = \"DuR\";\n\n  // the extraction radius is typically not used in the Bondi system, so we\n  // don't error if it isn't parsed from the filename. Instead, we'll just error\n  // if the invalid extraction radius value is ever retrieved using\n  // `get_extraction_radius`.\n  extraction_radius_ =\n      Cce::get_extraction_radius(cce_data_filename, extraction_radius, false);\n\n  detail::set_time_buffer_and_lmax(make_not_null(&time_buffer_), l_max_,\n                                   cce_data_file_.get<h5::Dat>(\"/U\"), false,\n                                   is_modal, true);\n  cce_data_file_.close_current_object();\n}\n\ntemplate <typename T>\ndouble BondiWorldtubeH5BufferUpdater<T>::update_buffers_for_time(\n    const gsl::not_null<Variables<tags_for_writing>*> buffers,\n    const gsl::not_null<size_t*> time_span_start,\n    const gsl::not_null<size_t*> time_span_end, const double time,\n    const size_t computation_l_max, const size_t interpolator_length,\n    const size_t buffer_depth, const bool time_varies_fastest) const {\n  return detail::update_buffers_for_time<is_modal, tags_for_writing>(\n      buffers, time_span_start, time_span_end, time, computation_l_max, l_max_,\n      interpolator_length, buffer_depth, time_buffer_, dataset_names_,\n      cce_data_file_, time_varies_fastest);\n}\n\ntemplate <typename T>\nvoid BondiWorldtubeH5BufferUpdater<T>::pup(PUP::er& p) {\n  p | time_buffer_;\n  p | filename_;\n  p | l_max_;\n  p | extraction_radius_;\n  p | dataset_names_;\n  if (p.isUnpacking()) {\n    cce_data_file_ = h5::H5File<h5::AccessType::ReadOnly>{filename_};\n  }\n}\n\nKleinGordonWorldtubeH5BufferUpdater::KleinGordonWorldtubeH5BufferUpdater(\n    const std::string& cce_data_filename,\n    const std::optional<double> extraction_radius)\n    : cce_data_file_{cce_data_filename}, filename_{cce_data_filename} {\n  get<Tags::detail::InputDataSet<\n      Spectral::Swsh::Tags::SwshTransform<Tags::KleinGordonPsi>>>(\n      dataset_names_) = \"KGPsi\";\n  get<Tags::detail::InputDataSet<\n      Spectral::Swsh::Tags::SwshTransform<Tags::KleinGordonPi>>>(\n      dataset_names_) = \"dtKGPsi\";\n\n  // the extraction radius is typically not used in the Klein-Gordon system, so\n  // we don't error if it isn't parsed from the filename. Instead, we'll just\n  // error if the invalid extraction radius value is ever retrieved using\n  // `get_extraction_radius`.\n  extraction_radius_ =\n      Cce::get_extraction_radius(cce_data_filename, extraction_radius, false);\n\n  detail::set_time_buffer_and_lmax(make_not_null(&time_buffer_), l_max_,\n                                   cce_data_file_.get<h5::Dat>(\"/KGPsi\"), true,\n                                   true, true);\n  cce_data_file_.close_current_object();\n}\n\ndouble KleinGordonWorldtubeH5BufferUpdater::update_buffers_for_time(\n    const gsl::not_null<Variables<klein_gordon_input_tags>*> buffers,\n    const gsl::not_null<size_t*> time_span_start,\n    const gsl::not_null<size_t*> time_span_end, const double time,\n    const size_t computation_l_max, const size_t interpolator_length,\n    const size_t buffer_depth, const bool time_varies_fastest) const {\n  if (UNLIKELY(not time_varies_fastest)) {\n    ERROR(\n        \"KleinGordon worldtube data can only be read from H5 in \"\n        \"time-varies-fastest form.\");\n  }\n  return detail::update_buffers_for_time<true, klein_gordon_input_tags>(\n      buffers, time_span_start, time_span_end, time, computation_l_max, l_max_,\n      interpolator_length, buffer_depth, time_buffer_, dataset_names_,\n      cce_data_file_);\n}\n\nvoid KleinGordonWorldtubeH5BufferUpdater::pup(PUP::er& p) {\n  p | time_buffer_;\n  p | filename_;\n  p | l_max_;\n  p | extraction_radius_;\n  p | dataset_names_;\n  if (p.isUnpacking()) {\n    cce_data_file_ = h5::H5File<h5::AccessType::ReadOnly>{filename_};\n  }\n}\n\ntemplate <typename T>\nPUP::able::PUP_ID MetricWorldtubeH5BufferUpdater<T>::my_PUP_ID = 0;  // NOLINT\ntemplate <typename T>\nPUP::able::PUP_ID BondiWorldtubeH5BufferUpdater<T>::my_PUP_ID = 0;     // NOLINT\nPUP::able::PUP_ID KleinGordonWorldtubeH5BufferUpdater::my_PUP_ID = 0;  // NOLINT\n\ntemplate class MetricWorldtubeH5BufferUpdater<ComplexModalVector>;\ntemplate class MetricWorldtubeH5BufferUpdater<DataVector>;\ntemplate class BondiWorldtubeH5BufferUpdater<ComplexModalVector>;\ntemplate class BondiWorldtubeH5BufferUpdater<ComplexDataVector>;\n\n#define SPIN(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define IS_MODAL(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define VEC_TYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template void detail::update_buffer<IS_MODAL(data), SPIN(data)>( \\\n      const gsl::not_null<VEC_TYPE(data)*> buffer_to_update,       \\\n      const h5::Dat& read_data, const size_t computation_l_max,    \\\n      const size_t l_max, const size_t time_span_start,            \\\n      const size_t time_span_end, const bool time_varies_fastest);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2), (true, false),\n                        (ComplexModalVector, ComplexDataVector))\n\n#undef INSTANTIATE\n#undef VEC_TYPE\n#undef IS_MODAL\n#undef SPIN\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace Tags::detail {\n// tags for use in the buffers for the input worldtube data management classes\ntemplate <typename T>\nusing SpatialMetric = gr::Tags::SpatialMetric<T, 3>;\ntemplate <typename T>\nusing Shift = gr::Tags::Shift<T, 3>;\ntemplate <typename T>\nusing Lapse = gr::Tags::Lapse<T>;\n// This is B^i in the 3+1 equations\ntemplate <typename T>\nstruct AuxiliaryShift : db::SimpleTag {\n  using type = tnsr::I<T, 3, ::Frame::Inertial>;\n};\n// This is \\Gamma^i in the Z4c equations\ntemplate <typename T>\nstruct ConformalChristoffel : db::SimpleTag {\n  using type = tnsr::I<T, 3, ::Frame::Inertial>;\n};\ntemplate <typename T>\nusing ExtrinsicCurvature = gr::Tags::ExtrinsicCurvature<T, 3>;\n\n// The three metric quantities we read in from disk (no derivatives)\ntemplate <typename T>\nusing metric_tags = tmpl::list<SpatialMetric<T>, Shift<T>, Lapse<T>>;\n\n// radial derivative prefix tag to be used with the input worldtube data\ntemplate <typename Tag>\nstruct Dr : db::SimpleTag, db::PrefixTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n// For cartesian derivs. Each dataset is stored separately in the file, so it\n// doesn't make sense to use ::Tags::deriv here\ntemplate <typename Tag>\nstruct Dx : db::SimpleTag, db::PrefixTag {\n  static constexpr size_t index = 0;\n  using type = typename Tag::type;\n};\ntemplate <typename Tag>\nstruct Dy : db::SimpleTag, db::PrefixTag {\n  static constexpr size_t index = 1;\n  using type = typename Tag::type;\n};\ntemplate <typename Tag>\nstruct Dz : db::SimpleTag, db::PrefixTag {\n  static constexpr size_t index = 2;\n  using type = typename Tag::type;\n};\ntemplate <typename Tag>\nusing cartesian_derivs_t = tmpl::list<Dx<Tag>, Dy<Tag>, Dz<Tag>>;\n\n// tag for the string for accessing the quantity associated with `Tag` in\n// worldtube h5 file\ntemplate <typename Tag>\nstruct InputDataSet : db::SimpleTag, db::PrefixTag {\n  using type = std::string;\n  using tag = Tag;\n};\n\n// Puts `Tag`, `::Tags::dt<Tag>`, and `Cce::Tags::Dr<Tag>` into a `tmpl::list`\ntemplate <typename Tag>\nstruct apply_derivs {\n  using type = tmpl::list<Tag, ::Tags::dt<Tag>, Dr<Tag>>;\n};\ntemplate <typename Tag>\nusing apply_derivs_t = typename apply_derivs<Tag>::type;\n\n// Puts `Tag`, `::Tags::dt<Tag>`, `Dx<Tag>`, `Dy<Tag>`, `Dz<Tag>` into a\n// `tmpl::list`\ntemplate <typename Tag>\nstruct apply_derivs_adm {\n  using type =\n      tmpl::flatten<tmpl::list<Tag, ::Tags::dt<Tag>, cartesian_derivs_t<Tag>>>;\n};\ntemplate <typename Tag>\nusing apply_derivs_adm_t = typename apply_derivs_adm<Tag>::type;\n}  // namespace Tags::detail\n\nnamespace detail {\n// generates the component dataset name in the worldtube file based on the\n// tensor indices requested. For instance, if called with arguments (\"/g\", 0,1),\n// it returns the dataset name \"/gxy\".\ntemplate <typename... T>\nstd::string dataset_name_for_component(std::string base_name,\n                                       const T... indices) {  // NOLINT\n  const auto add_index = [&base_name](size_t index) {\n    ASSERT(index < 3, \"The character-arithmetic index must be less than 3.\");\n    base_name += static_cast<char>('x' + index);\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(add_index(indices));\n  // void cast so that compilers can tell it's used.\n  (void)add_index;\n  return base_name;\n}\n\n// creates a pair of indices such that the difference is `2 *\n// interpolator_length + pad`, centered around `time`, and bounded by\n// `lower_bound` and `upper_bound`. If it cannot be centered, it gives a span\n// that is appropriately sized and bounded by the supplied bounds. If the bounds\n// are too constraining for the necessary size, it gives a span that is the\n// correct size starting at `lower bound`, but not constrained by `upper_bound`\nstd::pair<size_t, size_t> create_span_for_time_value(\n    double time, size_t pad, size_t interpolator_length, size_t lower_bound,\n    size_t upper_bound, const DataVector& time_buffer);\n\n// retrieves time stamps and lmax the from the specified file. the bools\n// `is_real`, `is_modal_data`, and `is_complex` are used to predict the number\n// of columns in the dat file and possibly error if that number is incorrect.\n// Note that if `is_modal_data` is true, then we use `is_real` but `is_complex`\n// is unneeded. If `is_modal_data` if false, then `is_complex` is used, but\n// `is_real` is unneeded.\nvoid set_time_buffer_and_lmax(gsl::not_null<DataVector*> time_buffer,\n                              size_t& l_max, const h5::Dat& data, bool is_real,\n                              bool is_modal_data, bool is_complex);\n\n// updates `time_span_start` and `time_span_end` based on the provided `time`,\n// and inserts the cooresponding modal data (for `InputTags`) from worldtube H5\n// file into `buffers`. The function is used by Bondi and Klein-Gordon systems.\ntemplate <typename InputTags>\ndouble update_buffers_for_time(\n    gsl::not_null<Variables<InputTags>*> buffers,\n    gsl::not_null<size_t*> time_span_start,\n    gsl::not_null<size_t*> time_span_end, double time, size_t computation_l_max,\n    size_t l_max, size_t interpolator_length, size_t buffer_depth,\n    const DataVector& time_buffer,\n    const tuples::tagged_tuple_from_typelist<\n        db::wrap_tags_in<Tags::detail::InputDataSet, InputTags>>& dataset_names,\n    const h5::H5File<h5::AccessType::ReadOnly>& cce_data_file);\n}  // namespace detail\n\n/// the full set of metric tensors to be extracted from the worldtube h5 file\ntemplate <typename T>\nusing cce_metric_input_tags =\n    tmpl::flatten<tmpl::transform<Tags::detail::metric_tags<T>,\n                                  Tags::detail::apply_derivs<tmpl::_1>>>;\n\ntemplate <typename T>\nusing cce_metric_adm_input_tags = tmpl::flatten<tmpl::push_back<\n    tmpl::transform<Tags::detail::metric_tags<T>,\n                    Tags::detail::apply_derivs_adm<tmpl::_1>>,\n    Tags::detail::ExtrinsicCurvature<T>, Tags::detail::AuxiliaryShift<T>,\n    Tags::detail::ConformalChristoffel<T>>>;\n\nusing klein_gordon_input_tags =\n    tmpl::list<Spectral::Swsh::Tags::SwshTransform<Tags::KleinGordonPsi>,\n               Spectral::Swsh::Tags::SwshTransform<Tags::KleinGordonPi>>;\n\n/// \\cond\ntemplate <typename T>\nclass MetricWorldtubeH5BufferUpdater;\ntemplate <typename T>\nclass BondiWorldtubeH5BufferUpdater;\nclass KleinGordonWorldtubeH5BufferUpdater;\n/// \\endcond\n\n/*!\n *  \\brief Abstract base class for utilities that are able to perform the buffer\n *  updating procedure needed by the `WorldtubeDataManager` or by the\n *  `PreprocessCceWorldtube` executable.\n *\n *  \\details The methods that are required to be overridden in the derived\n * classes are:\n *  - `WorldtubeBufferUpdater::update_buffers_for_time()`:\n *  updates the buffers passed by pointer and the `time_span_start` and\n *  `time_span_end` to be appropriate for the requested `time`,\n *  `interpolator_length`, and `buffer_depth`.\n *  - `WorldtubeBufferUpdater::get_clone()`\n *  clone function to obtain a `std::unique_ptr` of the base\n *  `WorldtubeBufferUpdater`, needed to pass around the factory-created\n *  object.\n *  - `WorldtubeBufferUpdater::time_is_outside_range()`\n *  the override should return `true` if the `time` could be used in a\n *  `update_buffers_for_time` call given the data available to the derived\n *  class, and `false` otherwise\n *  - `WorldtubeBufferUpdater::get_l_max()`\n *  The override should return the `l_max` it uses in the\n *  Goldberg modal data placed in the buffers.\n *  - `WorldtubeBufferUpdater::get_extraction_radius()`\n *  The override should return the coordinate radius associated with the modal\n *  worldtube data that it supplies in the buffer update function. This is\n *  currently assumed to be a single double, but may be generalized in future\n *  to be time-dependent.\n *  - `WorldtubeBufferUpdater::get_time_buffer`\n *  The override should return the vector of times that it can produce\n *  data at. For instance, if associated with a file input, this will be the\n *  times at each of the rows of the time-series data.\n */\ntemplate <typename BufferTags>\nclass WorldtubeBufferUpdater : public PUP::able {\n public:\n  using creatable_classes =\n      tmpl::list<MetricWorldtubeH5BufferUpdater<ComplexModalVector>,\n                 MetricWorldtubeH5BufferUpdater<DataVector>,\n                 BondiWorldtubeH5BufferUpdater<ComplexModalVector>,\n                 BondiWorldtubeH5BufferUpdater<ComplexDataVector>,\n                 KleinGordonWorldtubeH5BufferUpdater>;\n\n  WRAPPED_PUPable_abstract(WorldtubeBufferUpdater);  // NOLINT\n\n  virtual double update_buffers_for_time(\n      gsl::not_null<Variables<BufferTags>*> buffers,\n      gsl::not_null<size_t*> time_span_start,\n      gsl::not_null<size_t*> time_span_end, double time,\n      size_t computation_l_max, size_t interpolator_length, size_t buffer_depth,\n      bool time_varies_fastest = true) const = 0;\n\n  virtual std::unique_ptr<WorldtubeBufferUpdater> get_clone() const = 0;\n\n  virtual bool time_is_outside_range(double time) const = 0;\n\n  virtual size_t get_l_max() const = 0;\n\n  virtual double get_extraction_radius() const = 0;\n\n  virtual bool has_version_history() const = 0;\n\n  virtual DataVector& get_time_buffer() = 0;\n};\n\n/*!\n * \\brief A `WorldtubeBufferUpdater` specialized to CCE input worldtube H5 files\n * that have cartesian metric components stored in either modal or nodal form.\n *\n * \\details To read in modal data, template this class as\n * `MetricWorldtubeH5BufferUpdater<ComplexModalVector>`. To read in nodal data,\n * template the class as `MetricWorldtubeH5BufferUpdater<DataVector>`. This\n * class also has the ability to read in data specifically written by SpEC.\n */\ntemplate <typename T>\nclass MetricWorldtubeH5BufferUpdater\n    : public WorldtubeBufferUpdater<cce_metric_input_tags<T>> {\n  static_assert(std::is_same_v<T, ComplexModalVector> or\n                    std::is_same_v<T, DataVector>,\n                \"Can only use ComplexModalVector or DataVector in a \"\n                \"MetricWorldtubeH5BufferUpdater.\");\n\n public:\n  static constexpr bool is_modal = std::is_same_v<T, ComplexModalVector>;\n\n  /*!\n   * \\brief Options needed when reading in the extrinsic curvature and auxiliary\n   * shift (BSSN) or the trace of the conformal christoffel (Z4c) from a non-GH\n   * evolution code.\n   *\n   * \\details Can also be used as an option tag\n   */\n  struct AdmOptions {\n    static std::string name() { return \"AdmMetricNodal\"; }\n\n    struct Advective {\n      using type = bool;\n      static constexpr Options::String help =\n          \"Add advective term to time derivative.\";\n    };\n\n    struct Lapse {\n      using type = Lapse;\n      Lapse() = default;\n      using options = tmpl::list<Advective>;\n      static constexpr Options::String help =\n          \"Options for 1+log slicing of the lapse.\";\n      explicit Lapse(const bool advective) : is_advective(advective) {}\n\n      void pup(PUP::er& p) { p | is_advective; }\n\n      bool is_advective;\n    };\n\n    struct Shift {\n      using type = Shift;\n      struct SecondOrderDriverEta {\n        using type = double;\n        static constexpr Options::String help =\n            \"Factor 'eta' in front of shift vector in Eq. 12 of Hilditch 2013 \"\n            \"(typically 2/M). To use this, the trace conformal christoffel \"\n            \"must be dumped.\";\n      };\n      struct ConformalChristoffelFactor {\n        using type = double;\n        static constexpr Options::String help =\n            \"Factor A in mu_S=A/lapse^2 in front of conformal Christoffel term \"\n            \"in Eq. 12 of Hilditch 2013 (typically A=1). To use this, the \"\n            \"trace conformal Christoffel must be dumped.\";\n      };\n      struct FirstOrderDriverFactor {\n        using type = double;\n        static constexpr Options::String help =\n            \"Factor in front of auxiliary shift vector B^i in left Eq. 4.89 of \"\n            \"B&S (typically 0.75). To use this, the auxiliary shift vector B^i \"\n            \"must be dumped. Here mu_S is assumed to be 1/lapse^2.\";\n      };\n      Shift() = default;\n      using options = tmpl::list<\n          Advective,\n          Options::Alternatives<\n              tmpl::list<SecondOrderDriverEta, ConformalChristoffelFactor>,\n              tmpl::list<FirstOrderDriverFactor>>>;\n\n      static constexpr Options::String help =\n          \"Options for Gamma driver shift condition.\";\n      Shift(const bool advective, const double second_order_factor,\n            const double conformal_christoffel_factor_in)\n          : is_advective(advective),\n            is_first_order(false),\n            extra_factor(second_order_factor),\n            conformal_christoffel_factor(conformal_christoffel_factor_in) {}\n      Shift(const bool advective, const double first_order_factor)\n          : is_advective(advective),\n            is_first_order(true),\n            extra_factor(first_order_factor),\n            conformal_christoffel_factor(std::numeric_limits<double>::max()) {}\n\n      void pup(PUP::er& p) {\n        p | is_advective;\n        p | is_first_order;\n        p | extra_factor;\n      }\n\n      bool is_advective;\n      bool is_first_order;\n      double extra_factor;\n      double conformal_christoffel_factor;\n    };\n\n    using options = tmpl::list<Lapse, Shift>;\n    static constexpr Options::String help =\n        \"Gauge options when reading in extrinsic curvature additional variable \"\n        \"from non-GH evolutions.\";\n\n    AdmOptions() = default;\n    AdmOptions(const Lapse& lapse_in, const Shift& shift_in)\n        : lapse(lapse_in), shift(shift_in) {}\n\n    void pup(PUP::er& p) {\n      p | lapse;\n      p | shift;\n    }\n\n    Lapse lapse;\n    Shift shift;\n  };\n\n  // charm needs the empty constructor\n  MetricWorldtubeH5BufferUpdater() = default;\n\n  /// The constructor takes the filename of the H5 file that will be used\n  /// for boundary data. The extraction radius can either be passed in directly,\n  /// or if it takes the value `std::nullopt`, then the extraction radius is\n  /// retrieved as an integer in the filename. Also the user can specify if the\n  /// H5 file was written by SpEC or not, because SpEC has some different\n  /// conventions than we use here.\n  explicit MetricWorldtubeH5BufferUpdater(\n      const std::string& cce_data_filename,\n      std::optional<double> extraction_radius = std::nullopt,\n      bool descending_m = true,\n      const std::optional<AdmOptions>& adm_options = std::nullopt);\n\n  // NOLINTNEXTLINE\n  WRAPPED_PUPable_decl_base_template(\n      WorldtubeBufferUpdater<cce_metric_input_tags<T>>,\n      MetricWorldtubeH5BufferUpdater);\n\n  explicit MetricWorldtubeH5BufferUpdater(CkMigrateMessage* /*unused*/) {}\n\n  /// \\brief Update the `buffers`, `time_span_start`, and `time_span_end` with\n  /// data (either Goldberg modal data or just nodal data depending on the\n  /// template parameter to this class) and the start and end index in the\n  /// member `time_buffer_` covered by the newly updated `buffers`.\n  ///\n  /// The function returns the next time at which a full update will occur. If\n  /// called again at times earlier than the next full update time, it will\n  /// leave the `buffers` unchanged and again return the next needed time.\n  double update_buffers_for_time(\n      gsl::not_null<Variables<cce_metric_input_tags<T>>*> buffers,\n      gsl::not_null<size_t*> time_span_start,\n      gsl::not_null<size_t*> time_span_end, double time,\n      size_t computation_l_max, size_t interpolator_length, size_t buffer_depth,\n      bool time_varies_fastest = true) const override;\n\n  std::unique_ptr<WorldtubeBufferUpdater<cce_metric_input_tags<T>>> get_clone()\n      const override;\n\n  /// The time can only be supported in the buffer update if it is between the\n  /// first and last time of the input file.\n  bool time_is_outside_range(double time) const override;\n\n  /// retrieves the l_max of the input file\n  size_t get_l_max() const override { return l_max_; }\n\n  /// retrieves the extraction radius\n  double get_extraction_radius() const override { return extraction_radius_; }\n\n  /// The time buffer is supplied by non-const reference to allow views to\n  /// easily point into the buffer.\n  ///\n  /// \\warning Altering this buffer outside of the constructor of this class\n  /// results in undefined behavior! This should be supplied by const reference\n  /// once there is a convenient method of producing a const view of a vector\n  /// type.\n  DataVector& get_time_buffer() override { return time_buffer_; }\n\n  bool has_version_history() const override { return has_version_history_; }\n\n  /// Serialization for Charm++.\n  void pup(PUP::er& p) override;\n\n private:\n  void update_radial_formulation(\n      gsl::not_null<Variables<cce_metric_input_tags<T>>*> buffers,\n      size_t computation_l_max, size_t time_span_start, size_t time_span_end,\n      bool time_varies_fastest) const;\n  template <typename U = T>\n  typename std::enable_if_t<std::is_same_v<U, DataVector>>\n  update_adm_formulation(\n      gsl::not_null<Variables<cce_metric_input_tags<DataVector>>*> buffers,\n      size_t computation_l_max, size_t time_span_start, size_t time_span_end,\n      bool time_varies_fastest) const;\n  void update_buffer(gsl::not_null<T*> buffer_to_update,\n                     const h5::Dat& read_data, size_t computation_l_max,\n                     size_t time_span_start, size_t time_span_end,\n                     bool time_varies_fastest) const;\n\n  bool has_version_history_ = true;\n  double extraction_radius_ = std::numeric_limits<double>::signaling_NaN();\n  size_t l_max_ = 0;\n\n  h5::H5File<h5::AccessType::ReadOnly> cce_data_file_;\n  std::string filename_;\n  bool descending_m_ = true;\n  std::optional<AdmOptions> adm_options_;\n\n  tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n      Tags::detail::InputDataSet,\n      tmpl::remove_duplicates<tmpl::append<cce_metric_input_tags<T>,\n                                           cce_metric_adm_input_tags<T>>>>>\n      dataset_names_;\n\n  // stores all the times in the input file\n  DataVector time_buffer_;\n};\n\n/*!\n * \\brief A `WorldtubeBufferUpdater` specialized to CCE input worldtube H5 files\n * that have metric data stored in Bondi-Sachs format in either either modal or\n * nodal form.\n *\n * \\details To read in modal data, template this class as\n * `MetricWorldtubeH5BufferUpdater<ComplexModalVector>`. To read in nodal data,\n * template the class as `MetricWorldtubeH5BufferUpdater<ComplexDataVector>`.\n */\ntemplate <typename T>\nclass BondiWorldtubeH5BufferUpdater\n    : public WorldtubeBufferUpdater<tmpl::conditional_t<\n          std::is_same_v<T, ComplexModalVector>,\n          Tags::worldtube_boundary_tags_for_writing<\n              Spectral::Swsh::Tags::SwshTransform>,\n          Tags::worldtube_boundary_tags_for_writing<Tags::BoundaryValue>>> {\n  static_assert(std::is_same_v<T, ComplexModalVector> or\n                    std::is_same_v<T, ComplexDataVector>,\n                \"Can only use ComplexModalVector or ComplexDataVector in a \"\n                \"BondiWorldtubeH5BufferUpdater.\");\n\n public:\n  using tags_for_writing = tmpl::conditional_t<\n      std::is_same_v<T, ComplexModalVector>,\n      Tags::worldtube_boundary_tags_for_writing<\n          Spectral::Swsh::Tags::SwshTransform>,\n      Tags::worldtube_boundary_tags_for_writing<Tags::BoundaryValue>>;\n\n  static constexpr bool is_modal = std::is_same_v<T, ComplexModalVector>;\n\n  // charm needs the empty constructor\n  BondiWorldtubeH5BufferUpdater() = default;\n\n  /// The constructor takes the filename of the H5 file that will be used\n  /// for boundary data. The extraction radius can either be passed in directly,\n  /// or if it takes the value `std::nullopt`, then the extraction radius is\n  /// retrieved as an integer in the filename.\n  explicit BondiWorldtubeH5BufferUpdater(\n      const std::string& cce_data_filename,\n      std::optional<double> extraction_radius = std::nullopt);\n\n  // NOLINTNEXTLINE\n  WRAPPED_PUPable_decl_base_template(\n      SINGLE_ARG(WorldtubeBufferUpdater<tags_for_writing>),\n      BondiWorldtubeH5BufferUpdater);\n\n  explicit BondiWorldtubeH5BufferUpdater(CkMigrateMessage* /*unused*/) {}\n\n  /// update the `buffers`, `time_span_start`, and `time_span_end` with Goldberg\n  /// modal data (either Goldberg modal data or complex nodal data depending on\n  /// the template parameter to this class) and the start and end index in the\n  /// member `time_buffer_` covered by the newly updated `buffers`.\n  double update_buffers_for_time(\n      gsl::not_null<Variables<tags_for_writing>*> buffers,\n      gsl::not_null<size_t*> time_span_start,\n      gsl::not_null<size_t*> time_span_end, double time,\n      size_t computation_l_max, size_t interpolator_length, size_t buffer_depth,\n      bool time_varies_fastest = true) const override;\n\n  std::unique_ptr<WorldtubeBufferUpdater<tags_for_writing>> get_clone()\n      const override {\n    return std::make_unique<BondiWorldtubeH5BufferUpdater>(filename_);\n  }\n\n  /// The time can only be supported in the buffer update if it is between the\n  /// first and last time of the input file.\n  bool time_is_outside_range(const double time) const override {\n    return time < time_buffer_[0] or\n           time > time_buffer_[time_buffer_.size() - 1];\n  }\n\n  /// retrieves the l_max of the input file\n  size_t get_l_max() const override { return l_max_; }\n\n  /// retrieves the extraction radius. In most normal circumstances, this will\n  /// not be needed for Bondi data.\n  double get_extraction_radius() const override {\n    if (not extraction_radius_.has_value()) {\n      ERROR(\n          \"Extraction radius has not been set, and was not successfully parsed \"\n          \"from the filename. The extraction radius has been used, so must be \"\n          \"set either by the input file or via the filename.\");\n    }\n    return *extraction_radius_;\n  }\n\n  /// The time buffer is supplied by non-const reference to allow views to\n  /// easily point into the buffer.\n  ///\n  /// \\warning Altering this buffer outside of the constructor of this class\n  /// results in undefined behavior! This should be supplied by const reference\n  /// once there is a convenient method of producing a const view of a vector\n  /// type.\n  DataVector& get_time_buffer() override { return time_buffer_; }\n\n  bool has_version_history() const override { return true; }\n\n  /// Serialization for Charm++.\n  void pup(PUP::er& p) override;\n\n private:\n  std::optional<double> extraction_radius_ = std::nullopt;\n  size_t l_max_ = 0;\n\n  h5::H5File<h5::AccessType::ReadOnly> cce_data_file_;\n  std::string filename_;\n\n  tuples::tagged_tuple_from_typelist<\n      db::wrap_tags_in<Tags::detail::InputDataSet, tags_for_writing>>\n      dataset_names_;\n\n  // stores all the times in the input file\n  DataVector time_buffer_;\n};\n\n/// A `WorldtubeBufferUpdater` specialized to the Klein-Gordon input worldtube\n/// H5 file produced by the SpEC format. We assume the scalar field is\n/// real-valued.\nclass KleinGordonWorldtubeH5BufferUpdater\n    : public WorldtubeBufferUpdater<klein_gordon_input_tags> {\n public:\n  // charm needs the empty constructor\n  KleinGordonWorldtubeH5BufferUpdater() = default;\n\n  /// The constructor takes the filename of the SpEC h5 file that will be used\n  /// for boundary data. The extraction radius can either be passed in directly,\n  /// or if it takes the value `std::nullopt`, then the extraction radius is\n  /// retrieved as an integer in the filename.\n  explicit KleinGordonWorldtubeH5BufferUpdater(\n      const std::string& cce_data_filename,\n      std::optional<double> extraction_radius = std::nullopt);\n\n  WRAPPED_PUPable_decl_template(KleinGordonWorldtubeH5BufferUpdater);  // NOLINT\n\n  explicit KleinGordonWorldtubeH5BufferUpdater(CkMigrateMessage* /*unused*/) {}\n\n  /// update the `buffers`, `time_span_start`, and `time_span_end` with Goldberg\n  /// modal data and the start and end index in the member `time_buffer_`\n  /// covered by the newly updated `buffers`.\n  double update_buffers_for_time(\n      gsl::not_null<Variables<klein_gordon_input_tags>*> buffers,\n      gsl::not_null<size_t*> time_span_start,\n      gsl::not_null<size_t*> time_span_end, double time,\n      size_t computation_l_max, size_t interpolator_length, size_t buffer_depth,\n      bool time_varies_fastest = true) const override;\n\n  std::unique_ptr<WorldtubeBufferUpdater<klein_gordon_input_tags>> get_clone()\n      const override {\n    return std::make_unique<KleinGordonWorldtubeH5BufferUpdater>(filename_);\n  }\n\n  /// The time can only be supported in the buffer update if it is between the\n  /// first and last time of the input file.\n  bool time_is_outside_range(const double time) const override {\n    return time < time_buffer_[0] or\n           time > time_buffer_[time_buffer_.size() - 1];\n  }\n\n  /// retrieves the l_max of the input file\n  size_t get_l_max() const override { return l_max_; }\n\n  /// retrieves the extraction radius. In most normal circumstances, this will\n  /// not be needed for Klein-Gordon data.\n  double get_extraction_radius() const override {\n    if (not static_cast<bool>(extraction_radius_)) {\n      ERROR(\n          \"Extraction radius has not been set, and was not successfully parsed \"\n          \"from the filename. The extraction radius has been used, so must be \"\n          \"set either by the input file or via the filename.\");\n    }\n    return *extraction_radius_;\n  }\n\n  /// The time buffer is supplied by non-const reference to allow views to\n  /// easily point into the buffer.\n  ///\n  /// \\warning Altering this buffer outside of the constructor of this class\n  /// results in undefined behavior! This should be supplied by const reference\n  /// once there is a convenient method of producing a const view of a vector\n  /// type.\n  DataVector& get_time_buffer() override { return time_buffer_; }\n\n  bool has_version_history() const override { return true; }\n\n  /// Serialization for Charm++.\n  void pup(PUP::er& p) override;\n\n private:\n  std::optional<double> extraction_radius_ = std::nullopt;\n  size_t l_max_ = 0;\n\n  h5::H5File<h5::AccessType::ReadOnly> cce_data_file_;\n  std::string filename_;\n\n  tuples::tagged_tuple_from_typelist<\n      db::wrap_tags_in<Tags::detail::InputDataSet, klein_gordon_input_tags>>\n      dataset_names_;\n\n  // stores all the times in the input file\n  DataVector time_buffer_;\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/WorldtubeDataManager.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <mutex>\n#include <utility>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nnamespace detail {\ntemplate <typename InputTags>\nvoid set_non_pupped_members(\n    const gsl::not_null<size_t*> time_span_start,\n    const gsl::not_null<size_t*> time_span_end,\n    const gsl::not_null<Variables<InputTags>*> coefficients_buffers,\n    const gsl::not_null<Variables<InputTags>*> interpolated_coefficients,\n    const size_t buffer_depth, const size_t interpolator_length,\n    const size_t l_max) {\n  *time_span_start = 0;\n  *time_span_end = 0;\n  const size_t size_of_buffer =\n      square(l_max + 1) * (buffer_depth + 2 * interpolator_length);\n  *coefficients_buffers = Variables<InputTags>{size_of_buffer};\n  *interpolated_coefficients = Variables<InputTags>{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n}\n\ntemplate <typename InputTags>\nvoid initialize_buffers(\n    const gsl::not_null<size_t*> buffer_depth,\n    const gsl::not_null<Variables<InputTags>*> coefficients_buffers,\n    const size_t buffer_size, const size_t interpolator_length,\n    const size_t l_max) {\n  if (UNLIKELY(buffer_size < 2 * interpolator_length)) {\n    ERROR(\n        \"The specified buffer updater doesn't have enough time points to \"\n        \"supply the requested interpolator. This almost certainly \"\n        \"indicates that the corresponding file hasn't been created properly, \"\n        \"but might indicate that the specified Interpolator requests too many \"\n        \"points\");\n  }\n  // This will actually change the buffer depth in the case where the buffer\n  // depth passed to the constructor is too large for the worldtube file size.\n  // In that case, the worldtube data wouldn't be able to fill the buffer, so\n  // here we shrink the buffer depth down to be no larger than the length of the\n  // worldtube file.\n  if (UNLIKELY(buffer_size < 2 * interpolator_length + (*buffer_depth))) {\n    *buffer_depth = buffer_size - 2 * interpolator_length;\n  }\n\n  const size_t size_of_buffer =\n      square(l_max + 1) * (*buffer_depth + 2 * interpolator_length);\n  *coefficients_buffers = Variables<InputTags>{size_of_buffer};\n}\n\ntemplate <typename InputTags, typename OutputTags>\nvoid populate_hypersurface_boundary_data(\n    const gsl::not_null<Variables<OutputTags>*> boundary_data_variables,\n    const gsl::not_null<Variables<InputTags>*> interpolated_coefficients,\n    const gsl::not_null<Variables<InputTags>*> coefficients_buffers,\n    const gsl::not_null<size_t*> time_span_start,\n    const gsl::not_null<size_t*> time_span_end,\n    const gsl::not_null<Parallel::NodeLock*> hdf5_lock, const double time,\n    const std::unique_ptr<intrp::SpanInterpolator>& interpolator,\n    const std::unique_ptr<WorldtubeBufferUpdater<InputTags>>& buffer_updater,\n    const size_t l_max, const size_t buffer_depth) {\n  {\n    const std::lock_guard hold_lock(*hdf5_lock);\n    buffer_updater->update_buffers_for_time(\n        coefficients_buffers, time_span_start, time_span_end, time, l_max,\n        interpolator->required_number_of_points_before_and_after(),\n        buffer_depth);\n  }\n\n  auto interpolation_time_span = detail::create_span_for_time_value(\n      time, 0, interpolator->required_number_of_points_before_and_after(),\n      *time_span_start, *time_span_end, buffer_updater->get_time_buffer());\n\n  // search through and find the two interpolation points the time point is\n  // between. If we can, put the range for the interpolation centered on the\n  // desired point. If that can't be done (near the start or the end of the\n  // simulation), make the range terminated at the start or end of the cached\n  // data and extending for the desired range in the other direction.\n  const size_t buffer_span_size = (*time_span_end) - (*time_span_start);\n  const size_t interpolation_span_size =\n      interpolation_time_span.second - interpolation_time_span.first;\n\n  DataVector time_points{\n      buffer_updater->get_time_buffer().data() + interpolation_time_span.first,\n      interpolation_span_size};\n\n  auto interpolate_from_column = [&time, &time_points, &buffer_span_size,\n                                  &interpolation_time_span,\n                                  &interpolation_span_size, &time_span_start,\n                                  &interpolator](auto data, size_t column) {\n    const auto interp_val = interpolator->interpolate(\n        gsl::span<const double>(time_points.data(), time_points.size()),\n        gsl::span<const std::complex<double>>(\n            data + column * (buffer_span_size) +\n                (interpolation_time_span.first - (*time_span_start)),\n            interpolation_span_size),\n        time);\n    return interp_val;\n  };\n\n  // the ComplexModalVectors should be provided from the buffer_updater_ in\n  // 'Goldberg' format, so we iterate over modes and convert to libsharp\n  // format.\n\n  for (const auto libsharp_mode :\n       Spectral::Swsh::cached_coefficients_metadata(l_max)) {\n    tmpl::for_each<InputTags>([&libsharp_mode, &interpolate_from_column,\n                               &interpolated_coefficients, &l_max,\n                               &coefficients_buffers](auto tag_v) {\n      using tag = typename decltype(tag_v)::type;\n      Spectral::Swsh::goldberg_modes_to_libsharp_modes_single_pair(\n          libsharp_mode,\n          make_not_null(&get(get<tag>(*interpolated_coefficients))), 0,\n          interpolate_from_column(\n              get(get<tag>(*coefficients_buffers)).data().data(),\n              Spectral::Swsh::goldberg_mode_index(\n                  l_max, libsharp_mode.l, static_cast<int>(libsharp_mode.m))),\n          interpolate_from_column(\n              get(get<tag>(*coefficients_buffers)).data().data(),\n              Spectral::Swsh::goldberg_mode_index(\n                  l_max, libsharp_mode.l, -static_cast<int>(libsharp_mode.m))));\n    });\n  }\n  // just inverse transform the 'direct' tags\n  tmpl::for_each<\n      tmpl::transform<InputTags, tmpl::bind<db::remove_tag_prefix, tmpl::_1>>>(\n      [&boundary_data_variables, &interpolated_coefficients,\n       &l_max](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        Spectral::Swsh::inverse_swsh_transform(\n            l_max, 1,\n            make_not_null(\n                &get(get<Tags::BoundaryValue<tag>>(*boundary_data_variables))),\n            get(get<Spectral::Swsh::Tags::SwshTransform<tag>>(\n                *interpolated_coefficients)));\n      });\n}\n}  // namespace detail\n\nMetricWorldtubeDataManager::MetricWorldtubeDataManager(\n    std::unique_ptr<WorldtubeBufferUpdater<input_tags>> buffer_updater,\n    const size_t l_max, const size_t buffer_depth,\n    std::unique_ptr<intrp::SpanInterpolator> interpolator,\n    const bool fix_spec_normalization)\n    : buffer_updater_{std::move(buffer_updater)},\n      l_max_{l_max},\n      fix_spec_normalization_{fix_spec_normalization},\n      interpolated_coefficients_{\n          Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)},\n      buffer_depth_{buffer_depth},\n      interpolator_{std::move(interpolator)} {\n  detail::initialize_buffers<input_tags>(\n      make_not_null(&buffer_depth_), make_not_null(&coefficients_buffers_),\n      buffer_updater_->get_time_buffer().size(),\n      interpolator_->required_number_of_points_before_and_after(), l_max);\n}\n\nbool MetricWorldtubeDataManager::populate_hypersurface_boundary_data(\n    const gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        boundary_data_variables,\n    const double time,\n    const gsl::not_null<Parallel::NodeLock*> hdf5_lock) const {\n  if (buffer_updater_->time_is_outside_range(time)) {\n    return false;\n  }\n  {\n    const std::lock_guard hold_lock(*hdf5_lock);\n    buffer_updater_->update_buffers_for_time(\n        make_not_null(&coefficients_buffers_), make_not_null(&time_span_start_),\n        make_not_null(&time_span_end_), time, l_max_,\n        interpolator_->required_number_of_points_before_and_after(),\n        buffer_depth_);\n  }\n  const auto interpolation_time_span = detail::create_span_for_time_value(\n      time, 0, interpolator_->required_number_of_points_before_and_after(),\n      time_span_start_, time_span_end_, buffer_updater_->get_time_buffer());\n\n  // search through and find the two interpolation points the time point is\n  // between. If we can, put the range for the interpolation centered on the\n  // desired point. If that can't be done (near the start or the end of the\n  // simulation), make the range terminated at the start or end of the cached\n  // data and extending for the desired range in the other direction.\n  const size_t buffer_span_size = time_span_end_ - time_span_start_;\n  const size_t interpolation_span_size =\n      interpolation_time_span.second - interpolation_time_span.first;\n\n  const DataVector time_points{\n      buffer_updater_->get_time_buffer().data() + interpolation_time_span.first,\n      interpolation_span_size};\n\n  auto interpolate_from_column = [&time, &time_points, &buffer_span_size,\n                                  &interpolation_time_span,\n                                  &interpolation_span_size,\n                                  this](auto data, const size_t column) {\n    auto interp_val = interpolator_->interpolate(\n        gsl::span<const double>(time_points.data(), time_points.size()),\n        gsl::span<const std::complex<double>>(\n            data + column * buffer_span_size +\n                (interpolation_time_span.first - time_span_start_),\n            interpolation_span_size),\n        time);\n    return interp_val;\n  };\n\n  // the ComplexModalVectors should be provided from the buffer_updater_ in\n  // 'Goldberg' format, so we iterate over modes and convert to libsharp\n  // format.\n\n  // we'll just use this buffer to reference into the actual data to satisfy\n  // the swsh interface requirement that the spin-weight be labelled with\n  // `SpinWeighted`\n  SpinWeighted<ComplexModalVector, 0> spin_weighted_buffer;\n  for (const auto libsharp_mode :\n       Spectral::Swsh::cached_coefficients_metadata(l_max_)) {\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        tmpl::for_each<Tags::detail::apply_derivs_t<\n            Tags::detail::SpatialMetric<ComplexModalVector>>>(\n            [this, &i, &j, &libsharp_mode, &interpolate_from_column,\n             &spin_weighted_buffer](auto tag_v) {\n              using tag = typename decltype(tag_v)::type;\n              spin_weighted_buffer.set_data_ref(\n                  get<tag>(interpolated_coefficients_).get(i, j).data(),\n                  Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max_));\n              Spectral::Swsh::goldberg_modes_to_libsharp_modes_single_pair(\n                  libsharp_mode, make_not_null(&spin_weighted_buffer), 0,\n                  interpolate_from_column(\n                      get<tag>(coefficients_buffers_).get(i, j).data(),\n                      Spectral::Swsh::goldberg_mode_index(\n                          l_max_, libsharp_mode.l,\n                          static_cast<int>(libsharp_mode.m))),\n                  interpolate_from_column(\n                      get<tag>(coefficients_buffers_).get(i, j).data(),\n                      Spectral::Swsh::goldberg_mode_index(\n                          l_max_, libsharp_mode.l,\n                          -static_cast<int>(libsharp_mode.m))));\n            });\n      }\n      tmpl::for_each<Tags::detail::apply_derivs_t<\n          Tags::detail::Shift<ComplexModalVector>>>(\n          [this, &i, &libsharp_mode, &interpolate_from_column,\n           &spin_weighted_buffer](auto tag_v) {\n            using tag = typename decltype(tag_v)::type;\n            spin_weighted_buffer.set_data_ref(\n                get<tag>(interpolated_coefficients_).get(i).data(),\n                Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max_));\n            Spectral::Swsh::goldberg_modes_to_libsharp_modes_single_pair(\n                libsharp_mode, make_not_null(&spin_weighted_buffer), 0,\n                interpolate_from_column(\n                    get<tag>(coefficients_buffers_).get(i).data(),\n                    Spectral::Swsh::goldberg_mode_index(\n                        l_max_, libsharp_mode.l,\n                        static_cast<int>(libsharp_mode.m))),\n                interpolate_from_column(\n                    get<tag>(coefficients_buffers_).get(i).data(),\n                    Spectral::Swsh::goldberg_mode_index(\n                        l_max_, libsharp_mode.l,\n                        -static_cast<int>(libsharp_mode.m))));\n          });\n    }\n    tmpl::for_each<\n        Tags::detail::apply_derivs_t<Tags::detail::Lapse<ComplexModalVector>>>(\n        [this, &libsharp_mode, &interpolate_from_column,\n         &spin_weighted_buffer](auto tag_v) {\n          using tag = typename decltype(tag_v)::type;\n          spin_weighted_buffer.set_data_ref(\n              get(get<tag>(interpolated_coefficients_)).data(),\n              Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max_));\n          Spectral::Swsh::goldberg_modes_to_libsharp_modes_single_pair(\n              libsharp_mode, make_not_null(&spin_weighted_buffer), 0,\n              interpolate_from_column(\n                  get(get<tag>(coefficients_buffers_)).data(),\n                  Spectral::Swsh::goldberg_mode_index(\n                      l_max_, libsharp_mode.l,\n                      static_cast<int>(libsharp_mode.m))),\n              interpolate_from_column(\n                  get(get<tag>(coefficients_buffers_)).data(),\n                  Spectral::Swsh::goldberg_mode_index(\n                      l_max_, libsharp_mode.l,\n                      -static_cast<int>(libsharp_mode.m))));\n        });\n  }\n\n  // At this point, we have a collection of 9 tensors of libsharp\n  // coefficients. This is what the boundary data calculation utility takes\n  // as an input, so we now hand off the control flow to the boundary and\n  // gauge transform utility\n  // This relies on the ordering of tags in the `input_tags` type alias\n  const auto create_boundary_data = [&](const auto&... tags) {\n    if (not buffer_updater_->has_version_history() and\n        fix_spec_normalization_) {\n      create_bondi_boundary_data_from_unnormalized_spec_modes(\n          boundary_data_variables,\n          get<tmpl::type_from<std::decay_t<decltype(tags)>>>(\n              interpolated_coefficients_)...,\n          buffer_updater_->get_extraction_radius(), l_max_);\n    } else {\n      create_bondi_boundary_data(\n          boundary_data_variables,\n          get<tmpl::type_from<std::decay_t<decltype(tags)>>>(\n              interpolated_coefficients_)...,\n          buffer_updater_->get_extraction_radius(), l_max_);\n    }\n  };\n\n  tmpl::as_pack<input_tags>(create_boundary_data);\n\n  return true;\n}\n\nstd::unique_ptr<WorldtubeDataManager<\n    Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>>\nMetricWorldtubeDataManager::get_clone() const {\n  return std::make_unique<MetricWorldtubeDataManager>(\n      buffer_updater_->get_clone(), l_max_, buffer_depth_,\n      interpolator_->get_clone(), fix_spec_normalization_);\n}\n\nstd::pair<size_t, size_t> MetricWorldtubeDataManager::get_time_span() const {\n  return std::make_pair(time_span_start_, time_span_end_);\n}\n\nvoid MetricWorldtubeDataManager::pup(PUP::er& p) {\n  p | buffer_updater_;\n  p | time_span_start_;\n  p | time_span_end_;\n  p | l_max_;\n  p | buffer_depth_;\n  p | interpolator_;\n  p | fix_spec_normalization_;\n  if (p.isUnpacking()) {\n    detail::set_non_pupped_members<input_tags>(\n        make_not_null(&time_span_start_), make_not_null(&time_span_end_),\n        make_not_null(&coefficients_buffers_),\n        make_not_null(&interpolated_coefficients_), buffer_depth_,\n        interpolator_->required_number_of_points_before_and_after(), l_max_);\n  }\n}\n\nBondiWorldtubeDataManager::BondiWorldtubeDataManager(\n    std::unique_ptr<\n        WorldtubeBufferUpdater<Tags::worldtube_boundary_tags_for_writing<\n            Spectral::Swsh::Tags::SwshTransform>>>\n        buffer_updater,\n    const size_t l_max, const size_t buffer_depth,\n    std::unique_ptr<intrp::SpanInterpolator> interpolator)\n    : buffer_updater_{std::move(buffer_updater)},\n      l_max_{l_max},\n      interpolated_coefficients_{\n          Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)},\n      buffer_depth_{buffer_depth},\n      interpolator_{std::move(interpolator)} {\n  detail::initialize_buffers<Tags::worldtube_boundary_tags_for_writing<\n      Spectral::Swsh::Tags::SwshTransform>>(\n      make_not_null(&buffer_depth_), make_not_null(&coefficients_buffers_),\n      buffer_updater_->get_time_buffer().size(),\n      interpolator_->required_number_of_points_before_and_after(), l_max);\n}\n\nbool BondiWorldtubeDataManager::populate_hypersurface_boundary_data(\n    const gsl::not_null<Variables<\n        Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n        boundary_data_variables,\n    const double time,\n    const gsl::not_null<Parallel::NodeLock*> hdf5_lock) const {\n  if (buffer_updater_->time_is_outside_range(time)) {\n    return false;\n  }\n\n  detail::populate_hypersurface_boundary_data<\n      Tags::worldtube_boundary_tags_for_writing<\n          Spectral::Swsh::Tags::SwshTransform>,\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      boundary_data_variables, make_not_null(&interpolated_coefficients_),\n      make_not_null(&coefficients_buffers_), make_not_null(&time_span_start_),\n      make_not_null(&time_span_end_), hdf5_lock, time, interpolator_,\n      buffer_updater_, l_max_, buffer_depth_);\n\n  const auto& du_r = get(get<Tags::BoundaryValue<Tags::Du<Tags::BondiR>>>(\n      *boundary_data_variables));\n  const auto& bondi_r =\n      get(get<Tags::BoundaryValue<Tags::BondiR>>(*boundary_data_variables));\n\n  get(get<Tags::BoundaryValue<Tags::DuRDividedByR>>(*boundary_data_variables)) =\n      du_r / bondi_r;\n\n  // there's only a couple of tags desired by the core computation that aren't\n  // stored in the bondi format, so we perform the remaining computation\n  // in-line here.\n  const auto& du_bondi_j = get(get<Tags::BoundaryValue<Tags::Du<Tags::BondiJ>>>(\n      *boundary_data_variables));\n  const auto& dr_bondi_j = get(get<Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>>(\n      *boundary_data_variables));\n  get(get<Tags::BoundaryValue<Tags::BondiH>>(*boundary_data_variables)) =\n      du_bondi_j + du_r * dr_bondi_j;\n\n  const auto& bondi_j =\n      get(get<Tags::BoundaryValue<Tags::BondiJ>>(*boundary_data_variables));\n  const auto& bondi_beta =\n      get(get<Tags::BoundaryValue<Tags::BondiBeta>>(*boundary_data_variables));\n  const auto& bondi_q =\n      get(get<Tags::BoundaryValue<Tags::BondiQ>>(*boundary_data_variables));\n  const auto& bondi_k = sqrt(1.0 + bondi_j * conj(bondi_j));\n  get(get<Tags::BoundaryValue<Tags::Dr<Tags::BondiU>>>(\n      *boundary_data_variables)) =\n      exp(2.0 * bondi_beta.data()) / square(bondi_r.data()) *\n      (bondi_k.data() * bondi_q.data() - bondi_j.data() * conj(bondi_q.data()));\n  return true;\n}\n\nstd::unique_ptr<WorldtubeDataManager<\n    Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>>\nBondiWorldtubeDataManager::get_clone() const {\n  return std::make_unique<BondiWorldtubeDataManager>(\n      buffer_updater_->get_clone(), l_max_, buffer_depth_,\n      interpolator_->get_clone());\n}\n\nstd::pair<size_t, size_t> BondiWorldtubeDataManager::get_time_span() const {\n  return std::make_pair(time_span_start_, time_span_end_);\n}\n\nvoid BondiWorldtubeDataManager::pup(PUP::er& p) {\n  p | buffer_updater_;\n  p | time_span_start_;\n  p | time_span_end_;\n  p | l_max_;\n  p | buffer_depth_;\n  p | interpolator_;\n  if (p.isUnpacking()) {\n    detail::set_non_pupped_members<Tags::worldtube_boundary_tags_for_writing<\n        Spectral::Swsh::Tags::SwshTransform>>(\n        make_not_null(&time_span_start_), make_not_null(&time_span_end_),\n        make_not_null(&coefficients_buffers_),\n        make_not_null(&interpolated_coefficients_), buffer_depth_,\n        interpolator_->required_number_of_points_before_and_after(), l_max_);\n  }\n}\n\nKleinGordonWorldtubeDataManager::KleinGordonWorldtubeDataManager(\n    std::unique_ptr<WorldtubeBufferUpdater<klein_gordon_input_tags>>\n        buffer_updater,\n    const size_t l_max, const size_t buffer_depth,\n    std::unique_ptr<intrp::SpanInterpolator> interpolator)\n    : buffer_updater_{std::move(buffer_updater)},\n      l_max_{l_max},\n      interpolated_coefficients_{\n          Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)},\n      buffer_depth_{buffer_depth},\n      interpolator_{std::move(interpolator)} {\n  detail::initialize_buffers<klein_gordon_input_tags>(\n      make_not_null(&buffer_depth_), make_not_null(&coefficients_buffers_),\n      buffer_updater_->get_time_buffer().size(),\n      interpolator_->required_number_of_points_before_and_after(), l_max);\n}\n\nbool KleinGordonWorldtubeDataManager::populate_hypersurface_boundary_data(\n    const gsl::not_null<Variables<Tags::klein_gordon_worldtube_boundary_tags>*>\n        boundary_data_variables,\n    const double time,\n    const gsl::not_null<Parallel::NodeLock*> hdf5_lock) const {\n  if (buffer_updater_->time_is_outside_range(time)) {\n    return false;\n  }\n\n  detail::populate_hypersurface_boundary_data<\n      klein_gordon_input_tags, Tags::klein_gordon_worldtube_boundary_tags>(\n      boundary_data_variables, make_not_null(&interpolated_coefficients_),\n      make_not_null(&coefficients_buffers_), make_not_null(&time_span_start_),\n      make_not_null(&time_span_end_), hdf5_lock, time, interpolator_,\n      buffer_updater_, l_max_, buffer_depth_);\n\n  return true;\n}\n\nstd::unique_ptr<\n    WorldtubeDataManager<Tags::klein_gordon_worldtube_boundary_tags>>\nKleinGordonWorldtubeDataManager::get_clone() const {\n  return std::make_unique<KleinGordonWorldtubeDataManager>(\n      buffer_updater_->get_clone(), l_max_, buffer_depth_,\n      interpolator_->get_clone());\n}\n\nstd::pair<size_t, size_t> KleinGordonWorldtubeDataManager::get_time_span()\n    const {\n  return std::make_pair(time_span_start_, time_span_end_);\n}\n\nvoid KleinGordonWorldtubeDataManager::pup(PUP::er& p) {\n  p | buffer_updater_;\n  p | time_span_start_;\n  p | time_span_end_;\n  p | l_max_;\n  p | buffer_depth_;\n  p | interpolator_;\n  if (p.isUnpacking()) {\n    detail::set_non_pupped_members<klein_gordon_input_tags>(\n        make_not_null(&time_span_start_), make_not_null(&time_span_end_),\n        make_not_null(&coefficients_buffers_),\n        make_not_null(&interpolated_coefficients_), buffer_depth_,\n        interpolator_->required_number_of_points_before_and_after(), l_max_);\n  }\n}\n\nPUP::able::PUP_ID MetricWorldtubeDataManager::my_PUP_ID = 0;       // NOLINT\nPUP::able::PUP_ID BondiWorldtubeDataManager::my_PUP_ID = 0;        // NOLINT\nPUP::able::PUP_ID KleinGordonWorldtubeDataManager::my_PUP_ID = 0;  // NOLINT\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/WorldtubeDataManager.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nnamespace detail {\n\ntemplate <typename InputTags>\nvoid set_non_pupped_members(\n    gsl::not_null<size_t*> time_span_start,\n    gsl::not_null<size_t*> time_span_end,\n    gsl::not_null<Variables<InputTags>*> coefficients_buffers,\n    gsl::not_null<Variables<InputTags>*> interpolated_coefficients,\n    size_t buffer_depth, size_t interpolator_length, size_t l_max);\n\ntemplate <typename InputTags>\nvoid initialize_buffers(\n    gsl::not_null<size_t*> buffer_depth,\n    gsl::not_null<Variables<InputTags>*> coefficients_buffers,\n    size_t buffer_size, size_t interpolator_length, size_t l_max);\n\ntemplate <typename InputTags, typename OutputTags>\nvoid populate_hypersurface_boundary_data(\n    gsl::not_null<Variables<OutputTags>*> boundary_data_variables,\n    gsl::not_null<Variables<InputTags>*> interpolated_coefficients,\n    gsl::not_null<Variables<InputTags>*> coefficients_buffers,\n    gsl::not_null<size_t*> time_span_start,\n    gsl::not_null<size_t*> time_span_end,\n    gsl::not_null<Parallel::NodeLock*> hdf5_lock, double time,\n    const std::unique_ptr<intrp::SpanInterpolator>& interpolator,\n    const std::unique_ptr<WorldtubeBufferUpdater<InputTags>>& buffer_updater,\n    size_t l_max, size_t buffer_depth);\n}  // namespace detail\n\n/// \\cond\nclass MetricWorldtubeDataManager;\nclass BondiWorldtubeDataManager;\nclass KleinGordonWorldtubeDataManager;\n/// \\endcond\n\n/*!\n *  \\brief Abstract base class for managers of CCE worldtube data that is\n * provided in large time-series chunks, especially the type provided by input\n * h5 files. `BoundaryTags` is a `tmpl::list` of tags on the worldtube boundary.\n *\n *  \\details The methods that are required to be overridden in the derived\n * classes are:\n *\n * - `WorldtubeDataManager::populate_hypersurface_boundary_data()`:\n *   updates the Variables passed by pointer to contain correct boundary data\n *   for the time value passed in. This function should update all of the tags\n *   in `Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>`.\n * - `WorldtubeDataManager::get_clone()`: clone function to obtain a\n *   `std::unique_ptr` of the base `WorldtubeDataManager`, needed to pass around\n *   the factory-created object.\n * - `WorldtubeDataManager::get_l_max()`: The override should return the\n *   `l_max` that it computes for the collocation data calculated during\n *   `WorldtubeDataManager::populate_hypersurface_boundary_data()`.\n * - `WorldtubeBufferUpdater::get_time_span()`: The override should return the\n *   `std::pair` of indices that represent the start and end point of the\n *   underlying data source. This is primarily used for monitoring the frequency\n *   and size of the buffer updates.\n */\ntemplate <typename BoundaryTags>\nclass WorldtubeDataManager : public PUP::able {\n public:\n  using creatable_classes =\n      tmpl::list<MetricWorldtubeDataManager, BondiWorldtubeDataManager,\n                 KleinGordonWorldtubeDataManager>;\n\n  WRAPPED_PUPable_abstract(WorldtubeDataManager);  // NOLINT\n\n  virtual bool populate_hypersurface_boundary_data(\n      gsl::not_null<Variables<BoundaryTags>*> boundary_data_variables,\n      double time, gsl::not_null<Parallel::NodeLock*> hdf5_lock) const = 0;\n\n  virtual std::unique_ptr<WorldtubeDataManager> get_clone() const = 0;\n\n  virtual size_t get_l_max() const = 0;\n\n  virtual std::pair<size_t, size_t> get_time_span() const = 0;\n};\n\n/*!\n * \\brief Manages the cached buffer data associated with a CCE worldtube and\n * interpolates to requested time points to provide worldtube boundary data to\n * the main evolution routines.\n *\n * \\details The maintained buffer will be maintained at a length that is set by\n * the `Interpolator` and the `buffer_depth` also passed to the constructor. A\n * longer depth will ensure that the buffer updater is called less frequently,\n * which is useful for slow updaters (e.g. those that perform file access).\n * The main functionality is provided by the\n * `WorldtubeDataManager::populate_hypersurface_boundary_data()` member\n * function that handles buffer updating and boundary computation.\n */\nclass MetricWorldtubeDataManager\n    : public WorldtubeDataManager<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>> {\n  using input_tags = cce_metric_input_tags<ComplexModalVector>;\n\n public:\n  // charm needs an empty constructor.\n  MetricWorldtubeDataManager() = default;\n\n  MetricWorldtubeDataManager(\n      std::unique_ptr<WorldtubeBufferUpdater<input_tags>> buffer_updater,\n      size_t l_max, size_t buffer_depth,\n      std::unique_ptr<intrp::SpanInterpolator> interpolator,\n      bool fix_spec_normalization);\n\n  WRAPPED_PUPable_decl_template(MetricWorldtubeDataManager);  // NOLINT\n\n  explicit MetricWorldtubeDataManager(CkMigrateMessage* /*unused*/) {}\n\n  /*!\n   * \\brief Update the `boundary_data_box` entries for all tags in\n   * `Tags::characteristic_worldtube_boundary_tags` to the boundary data at\n   * `time`.\n   *\n   * \\details First, if the stored buffer requires updating, it will be updated\n   * via the `buffer_updater_` supplied in the constructor. Then, each of the\n   * spatial metric, shift, lapse, and each of their radial and time derivatives\n   * are interpolated across buffer points to the requested time value (via the\n   * `Interpolator` provided in the constructor). Finally, that data is supplied\n   * to the `create_bondi_boundary_data()`, which updates the\n   * `boundary_data_box` with the Bondi spin-weighted scalars determined from\n   * the interpolated Cartesian data.\n   *\n   * Returns `true` if the time can be supplied from the `buffer_updater_`, and\n   * `false` otherwise. No tags are updated if `false` is returned.\n   */\n  bool populate_hypersurface_boundary_data(\n      gsl::not_null<Variables<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n          boundary_data_variables,\n      double time, gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  std::unique_ptr<WorldtubeDataManager> get_clone() const override;\n\n  /// retrieves the l_max that will be supplied to the \\ref DataBoxGroup in\n  /// `populate_hypersurface_boundary_data()`\n  size_t get_l_max() const override { return l_max_; }\n\n  /// retrieves the current time span associated with the `buffer_updater_` for\n  /// diagnostics\n  std::pair<size_t, size_t> get_time_span() const override;\n\n  /// Serialization for Charm++.\n  void pup(PUP::er& p) override;  // NOLINT\n\n private:\n  std::unique_ptr<WorldtubeBufferUpdater<input_tags>> buffer_updater_;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable size_t time_span_start_ = 0;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable size_t time_span_end_ = 0;\n  size_t l_max_ = 0;\n  bool fix_spec_normalization_ = false;\n\n  // These buffers are just kept around to avoid allocations; they're\n  // updated every time a time is requested\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Variables<input_tags> interpolated_coefficients_;\n\n  // note: buffers store data in a 'time-varies-fastest' manner\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Variables<input_tags> coefficients_buffers_;\n\n  size_t buffer_depth_ = 0;\n\n  std::unique_ptr<intrp::SpanInterpolator> interpolator_;\n};\n\n/*!\n * \\brief Manages the bondi cached buffer dataset associated with a CCE\n * worldtube and interpolates to requested time points to provide worldtube\n * boundary data to the main evolution routines.\n *\n * \\details The maintained buffer will be kept at a length that is set by\n * the `Interpolator` and the `buffer_depth` also passed to the constructor. A\n * longer depth will ensure that the buffer updater is called less frequently,\n * which is useful for slow updaters (e.g. those that perform file access).\n * The main functionality is provided by the\n * `WorldtubeDataManager::populate_hypersurface_boundary_data()` member\n * function that handles buffer updating and boundary computation. This version\n * of the data manager handles the 9 scalars of\n * `worldtube_boundary_tags_for_writing<Spectral::Swsh::Tags::SwshTransform>`,\n * rather than direct metric components handled by `WorldtubeDataManager`. The\n * set of 9 scalars is a far leaner (factor of ~4) data storage format.\n */\nclass BondiWorldtubeDataManager\n    : public WorldtubeDataManager<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>> {\n public:\n  // charm needs an empty constructor.\n  BondiWorldtubeDataManager() = default;\n\n  BondiWorldtubeDataManager(\n      std::unique_ptr<\n          WorldtubeBufferUpdater<Tags::worldtube_boundary_tags_for_writing<\n              Spectral::Swsh::Tags::SwshTransform>>>\n          buffer_updater,\n      size_t l_max, size_t buffer_depth,\n      std::unique_ptr<intrp::SpanInterpolator> interpolator);\n\n  WRAPPED_PUPable_decl_template(BondiWorldtubeDataManager);  // NOLINT\n\n  explicit BondiWorldtubeDataManager(CkMigrateMessage* /*unused*/) {}\n\n  /*!\n   * \\brief Update the `boundary_data_box` entries for all tags in\n   * `Tags::characteristic_worldtube_boundary_tags` to the boundary data at\n   * `time`.\n   *\n   * \\details First, if the stored buffer requires updating, it will be updated\n   * via the `buffer_updater_` supplied in the constructor. Then, each of the\n   * 9 spin-weighted scalars in\n   * `worldtube_boundary_tags_for_writing<Spectral::Swsh::Tags::SwshTransform>`\n   * are interpolated across buffer points to the requested time value (via the\n   * `Interpolator` provided in the constructor). Finally, the remaining two\n   * scalars not directly supplied in the input file are calculated in-line and\n   * put in the \\ref DataBoxGroup.\n   *\n   * Returns `true` if the time can be supplied from the `buffer_updater_`, and\n   * `false` otherwise. No tags are updated if `false` is returned.\n   */\n  bool populate_hypersurface_boundary_data(\n      gsl::not_null<Variables<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>*>\n          boundary_data_variables,\n      double time, gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  std::unique_ptr<WorldtubeDataManager> get_clone() const override;\n\n  /// retrieves the l_max that will be supplied to the \\ref DataBoxGroup in\n  /// `populate_hypersurface_boundary_data()`\n  size_t get_l_max() const override { return l_max_; }\n\n  /// retrieves the current time span associated with the `buffer_updater_` for\n  /// diagnostics\n  std::pair<size_t, size_t> get_time_span() const override;\n\n  /// Serialization for Charm++.\n  void pup(PUP::er& p) override;  // NOLINT\n\n private:\n  std::unique_ptr<\n      WorldtubeBufferUpdater<Tags::worldtube_boundary_tags_for_writing<\n          Spectral::Swsh::Tags::SwshTransform>>>\n      buffer_updater_;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable size_t time_span_start_ = 0;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable size_t time_span_end_ = 0;\n  size_t l_max_ = 0;\n\n  // These buffers are just kept around to avoid allocations; they're\n  // updated every time a time is requested\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Variables<Tags::worldtube_boundary_tags_for_writing<\n      Spectral::Swsh::Tags::SwshTransform>>\n      interpolated_coefficients_;\n\n  // note: buffers store data in an 'time-varies-fastest' manner\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Variables<Tags::worldtube_boundary_tags_for_writing<\n      Spectral::Swsh::Tags::SwshTransform>>\n      coefficients_buffers_;\n\n  size_t buffer_depth_ = 0;\n\n  std::unique_ptr<intrp::SpanInterpolator> interpolator_;\n};\n\nclass KleinGordonWorldtubeDataManager\n    : public WorldtubeDataManager<Tags::klein_gordon_worldtube_boundary_tags> {\n public:\n  // charm needs an empty constructor.\n  KleinGordonWorldtubeDataManager() = default;\n\n  KleinGordonWorldtubeDataManager(\n      std::unique_ptr<WorldtubeBufferUpdater<klein_gordon_input_tags>>\n          buffer_updater,\n      size_t l_max, size_t buffer_depth,\n      std::unique_ptr<intrp::SpanInterpolator> interpolator);\n\n  WRAPPED_PUPable_decl_template(KleinGordonWorldtubeDataManager);  // NOLINT\n\n  explicit KleinGordonWorldtubeDataManager(CkMigrateMessage* /*unused*/) {}\n\n  /*!\n   * \\brief Update the `boundary_data_box` entries for all tags in\n   * `Tags::klein_gordon_worldtube_boundary_tags` to the boundary data at\n   * `time`.\n   *\n   * \\details First, if the stored buffer requires updating, it will be updated\n   * via the `buffer_updater_` supplied in the constructor. Then, each of the\n   * 2 spin-weighted scalars in `Tags::klein_gordon_worldtube_boundary_tags`\n   * are interpolated across buffer points to the requested time value (via the\n   * `Interpolator` provided in the constructor).\n   *\n   * Returns `true` if the time can be supplied from the `buffer_updater_`, and\n   * `false` otherwise. No tags are updated if `false` is returned.\n   */\n  bool populate_hypersurface_boundary_data(\n      gsl::not_null<Variables<Tags::klein_gordon_worldtube_boundary_tags>*>\n          boundary_data_variables,\n      double time, gsl::not_null<Parallel::NodeLock*> hdf5_lock) const override;\n\n  std::unique_ptr<WorldtubeDataManager> get_clone() const override;\n\n  /// retrieves the l_max that will be supplied to the \\ref DataBoxGroup in\n  /// `populate_hypersurface_boundary_data()`\n  size_t get_l_max() const override { return l_max_; }\n\n  /// retrieves the current time span associated with the `buffer_updater_` for\n  /// diagnostics\n  std::pair<size_t, size_t> get_time_span() const override;\n\n  /// Serialization for Charm++.\n  void pup(PUP::er& p) override;  // NOLINT\n\n private:\n  std::unique_ptr<WorldtubeBufferUpdater<klein_gordon_input_tags>>\n      buffer_updater_;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable size_t time_span_start_ = 0;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable size_t time_span_end_ = 0;\n  size_t l_max_ = 0;\n\n  // These buffers are just kept around to avoid allocations; they're\n  // updated every time a time is requested\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Variables<klein_gordon_input_tags> interpolated_coefficients_;\n\n  // note: buffers store data in an 'time-varies-fastest' manner\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Variables<klein_gordon_input_tags> coefficients_buffers_;\n\n  size_t buffer_depth_ = 0;\n\n  std::unique_ptr<intrp::SpanInterpolator> interpolator_;\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/WorldtubeModeRecorder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Cce/WorldtubeModeRecorder.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::BondiBeta>() {\n  return \"Beta\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::BondiU>() {\n  return \"U\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::BondiQ>() {\n  return \"Q\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::BondiW>() {\n  return \"W\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::BondiJ>() {\n  return \"J\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::Dr<Cce::Tags::BondiJ>>() {\n  return \"DrJ\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::Du<Cce::Tags::BondiJ>>() {\n  return \"H\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::BondiR>() {\n  return \"R\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::Du<Cce::Tags::BondiR>>() {\n  return \"DuR\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::KleinGordonPsi>() {\n  return \"KGPsi\";\n}\n\ntemplate <>\nstd::string dataset_label_for_tag<Cce::Tags::KleinGordonPi>() {\n  return \"dtKGPsi\";\n}\n\nWorldtubeModeRecorder::WorldtubeModeRecorder() = default;\nWorldtubeModeRecorder::WorldtubeModeRecorder(const size_t output_l_max,\n                                             const std::string& h5_filename)\n    : output_l_max_(output_l_max),\n      output_file_(h5_filename, true),\n      all_legend_(build_legend(false)),\n      real_legend_(build_legend(true)),\n      data_to_write_buffer_(data_to_write_size(false)),\n      goldberg_mode_buffer_(square(output_l_max_ + 1)) {}\n\ntemplate <int Spin>\nvoid WorldtubeModeRecorder::append_modal_data(\n    const std::string& subfile_path, const double time,\n    const ComplexDataVector& nodal_data, const size_t data_l_max) {\n  check_data_l_max(data_l_max);\n  // Set some views\n  SpinWeighted<ComplexDataVector, Spin> nodal_data_view;\n  nodal_data_view.set_data_ref(\n      make_not_null(&const_cast<ComplexDataVector&>(nodal_data)));  // NOLINT\n  const size_t number_of_data_modes = square(data_l_max + 1);\n  if (goldberg_mode_buffer_.size() < number_of_data_modes) {\n    goldberg_mode_buffer_.destructive_resize(number_of_data_modes);\n  }\n  SpinWeighted<ComplexModalVector, Spin> goldberg_modes;\n  goldberg_modes.set_data_ref(goldberg_mode_buffer_.data(),\n                              number_of_data_modes);\n\n  // First transform to coefficients using swsh_transform, and then convert\n  // libsharp coefficients into modes\n  Spectral::Swsh::libsharp_to_goldberg_modes(\n      make_not_null(&goldberg_modes),\n      Spectral::Swsh::swsh_transform(data_l_max, 1, nodal_data_view),\n      data_l_max);\n\n  append_modal_data<Spin>(subfile_path, time, goldberg_modes.data(),\n                          data_l_max);\n}\n\ntemplate <int Spin>\nvoid WorldtubeModeRecorder::append_modal_data(\n    const std::string& subfile_path, const double time,\n    const ComplexModalVector& modal_data, const size_t data_l_max) {\n  constexpr bool is_real = Spin == 0;\n  check_data_l_max(data_l_max);\n\n  ASSERT(data_to_write_buffer_.capacity() == data_to_write_size(false),\n         \"Buffer does not have the correct capactiy. Was expecting \"\n             << data_to_write_size(false) << \" but got \"\n             << data_to_write_buffer_.capacity());\n\n  // This won't remove the allocation, only removes the elements so we don't\n  // have to do complicated index tracking\n  data_to_write_buffer_.clear();\n  data_to_write_buffer_.push_back(time);\n\n  // Because the goldberg format is strictly increasing l modes, to restrict, we\n  // just take the first output_l_max_ modes.\n  // NOLINTBEGIN\n  const ComplexModalVector modal_data_view{\n      const_cast<ComplexModalVector&>(modal_data).data(),\n      square(output_l_max_ + 1)};\n  // NOLINTEND\n\n  // We loop over ell and m rather than just the total number of modes\n  // because we don't print negative m or the imaginary part of m=0\n  // for real quantities.\n  for (size_t ell = 0; ell <= output_l_max_; ell++) {\n    for (int m = is_real ? 0 : -static_cast<int>(ell);\n         m <= static_cast<int>(ell); m++) {\n      const size_t goldberg_index =\n          Spectral::Swsh::goldberg_mode_index(output_l_max_, ell, m);\n      data_to_write_buffer_.push_back(real(modal_data_view[goldberg_index]));\n      if (not is_real or m != 0) {\n        data_to_write_buffer_.push_back(imag(modal_data_view[goldberg_index]));\n      }\n    }\n  }\n\n  // Sanity check\n  ASSERT(data_to_write_buffer_.size() == data_to_write_size(is_real),\n         \"Buffer does not have the correct size. Was expecting \"\n             << data_to_write_size(is_real) << \" but got \"\n             << data_to_write_buffer_.size());\n\n  const std::vector<std::string>& legend =\n      is_real ? real_legend() : all_legend();\n  auto& output_mode_dataset =\n      output_file_.try_insert<h5::Dat>(subfile_path, legend, 0);\n  output_mode_dataset.append(data_to_write_buffer_);\n  output_file_.close_current_object();\n}\n\nsize_t WorldtubeModeRecorder::data_to_write_size(const bool is_real) const {\n  return 1 + square(output_l_max_ + 1) * (is_real ? 1 : 2);\n}\n\nconst std::vector<std::string>& WorldtubeModeRecorder::all_legend() const {\n  return all_legend_;\n}\nconst std::vector<std::string>& WorldtubeModeRecorder::real_legend() const {\n  return real_legend_;\n}\n\nstd::vector<std::string> WorldtubeModeRecorder::build_legend(\n    const bool is_real) const {\n  std::vector<std::string> legend;\n  legend.reserve(data_to_write_size(is_real));\n  legend.emplace_back(\"Time\");\n  for (int ell = 0; ell <= static_cast<int>(output_l_max_); ++ell) {\n    for (int m = is_real ? 0 : -ell; m <= ell; ++m) {\n      legend.push_back(MakeString{} << \"Re(\" << ell << \",\" << m << \")\");\n      // For real quantities, don't include the imaginary m=0\n      if (not is_real or m != 0) {\n        legend.push_back(MakeString{} << \"Im(\" << ell << \",\" << m << \")\");\n      }\n    }\n  }\n  return legend;\n}\n\nvoid WorldtubeModeRecorder::check_data_l_max(const size_t data_l_max) const {\n  if (UNLIKELY(data_l_max < output_l_max_)) {\n    ERROR(\n        \"WorldtubeModeRecorder can only do a restriction operation, not an \"\n        \"elongation operation. Said another way, the LMax of data passed to \"\n        \"WorldtubeModeRecorder (\"\n        << data_l_max\n        << \") must be greater than or equal to the LMax the class was \"\n           \"constructed with (\"\n        << output_l_max_ << \").\");\n  }\n}\n\n#define SPIN(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                          \\\n  template void WorldtubeModeRecorder::append_modal_data<SPIN(data)>( \\\n      const std::string& subfile_path, double time,                   \\\n      const ComplexDataVector& nodal_data, const size_t data_l_max);  \\\n  template void WorldtubeModeRecorder::append_modal_data<SPIN(data)>( \\\n      const std::string& subfile_path, double time,                   \\\n      const ComplexModalVector& modal_data, const size_t data_l_max);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2))\n\n#undef INSTANTIATE\n#undef SPIN\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Cce/WorldtubeModeRecorder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Cce {\n/*!\n * \\brief The dataset string associated with each scalar that will be output\n * from the `Cce::Tags::worldtube_boundary_tags_for_writing` list (from the tags\n * within the BoundaryPrefix).\n */\ntemplate <typename Tag>\nstd::string dataset_label_for_tag();\n\n/*!\n * \\brief Class that standardizes the output of our worldtube data into the\n * Bondi modal format that the CharacteristicExtract executable can read in.\n *\n * \\details Takes Bondi nodal data in `fill_data_to_write` and gives the modal\n * data as a `std::vector<double>` with the time as the 0th component. The \\p\n * output_l_max that this class is constructed with is the LMax that will be\n * written to disk.\n */\nclass WorldtubeModeRecorder {\n public:\n  WorldtubeModeRecorder();\n  WorldtubeModeRecorder(size_t output_l_max, const std::string& h5_filename);\n\n  /// @{\n  /*!\n   * \\brief Writes bondi modal data to the given \\p subfile_path\n   *\n   * \\details If nodal data is given (ComplexDataVector), uses `swsh_transform`\n   * to transform the data and `libsharp_to_goldberg_modes` to convert the\n   * libsharp formatted array into modes.\n   *\n   * There are exactly half the number of modes for \\p Spin = 0 quantities as\n   * their are for \\p Spin != 0 because we don't include imaginary or m=0 for \\p\n   * Spin = 0.\n   *\n   * The \\p data_l_max is the LMax of the \\p nodal_data or \\p modal_data and\n   * must be >= \\p output_l_max that this class was constructed with. A\n   * restriction operation will be performed on the data before it is written,\n   * if necessary.\n   */\n  template <int Spin>\n  void append_modal_data(const std::string& subfile_path, double time,\n                         const ComplexDataVector& nodal_data,\n                         size_t data_l_max);\n  template <int Spin>\n  void append_modal_data(const std::string& subfile_path, double time,\n                         const ComplexModalVector& modal_data,\n                         size_t data_l_max);\n  /// @}\n\n  /// @{\n  /// The legend for writing dat files for both spin = 0 (real) and spin != 0\n  /// (all) quantities.\n  const std::vector<std::string>& all_legend() const;\n  const std::vector<std::string>& real_legend() const;\n  /// @}\n\n private:\n  size_t data_to_write_size(bool is_real) const;\n  std::vector<std::string> build_legend(bool is_real) const;\n  void check_data_l_max(size_t data_l_max) const;\n\n  size_t output_l_max_{};\n  h5::H5File<h5::AccessType::ReadWrite> output_file_;\n  std::vector<std::string> all_legend_;\n  std::vector<std::string> real_legend_;\n  std::vector<double> data_to_write_buffer_;\n  ComplexModalVector goldberg_mode_buffer_;\n};\n}  // namespace Cce\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/ATilde.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/ATilde.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid a_tilde(const gsl::not_null<tnsr::ii<DataType, Dim, Frame>*> result,\n             const gsl::not_null<Scalar<DataType>*> buffer,\n             const Scalar<DataType>& conformal_factor_squared,\n             const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n             const tnsr::ii<DataType, Dim, Frame>& extrinsic_curvature,\n             const Scalar<DataType>& trace_extrinsic_curvature) {\n  ::tenex::evaluate(buffer, trace_extrinsic_curvature() / 3.0);\n  ::tenex::evaluate<ti::i, ti::j>(\n      result, conformal_factor_squared() *\n                  (extrinsic_curvature(ti::i, ti::j) -\n                   (*buffer)() * spatial_metric(ti::i, ti::j)));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::ii<DataType, Dim, Frame> a_tilde(\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& extrinsic_curvature,\n    const Scalar<DataType>& trace_extrinsic_curvature) {\n  tnsr::ii<DataType, Dim, Frame> result{};\n  Scalar<DataType> buffer{};\n  a_tilde(make_not_null(&result), make_not_null(&buffer),\n          conformal_factor_squared, spatial_metric, extrinsic_curvature,\n          trace_extrinsic_curvature);\n  return result;\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void Ccz4::a_tilde(                                             \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>  \\\n          result,                                                          \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> buffer,                    \\\n      const Scalar<DTYPE(data)>& conformal_factor_squared,                 \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          extrinsic_curvature,                                             \\\n      const Scalar<DTYPE(data)>& trace_extrinsic_curvature);               \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)> Ccz4::a_tilde(    \\\n      const Scalar<DTYPE(data)>& conformal_factor_squared,                 \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          extrinsic_curvature,                                             \\\n      const Scalar<DTYPE(data)>& trace_extrinsic_curvature);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/ATilde.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\n/// @{\n/*!\n * \\brief Computes the trace-free part of the extrinsic curvature\n *\n * \\details Computes the trace-free part as:\n *\n * \\f{align}\n *     \\tilde A_{ij} &= \\phi^2 \\left(K_{ij} - \\frac{1}{3} K \\gamma_{ij}\\right)\n * \\f}\n *\n * where \\f$\\phi^2\\f$ is the square of the conformal factor defined by\n * `Ccz4::Tags::ConformalFactorSquared`, \\f$\\gamma_{ij}\\f$ is the spatial metric\n * defined by `gr::Tags::SpatialMetric`, \\f$K_{ij}\\f$ is the extrinsic curvature\n * defined by `gr::Tags::ExtrinsicCurvature`, and \\f$K\\f$ is the trace of the\n * extrinsic curvature defined by `gr::Tags::TraceExtrinsicCurvature`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid a_tilde(const gsl::not_null<tnsr::ii<DataType, Dim, Frame>*> result,\n             const gsl::not_null<Scalar<DataType>*> buffer,\n             const Scalar<DataType>& conformal_factor_squared,\n             const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n             const tnsr::ii<DataType, Dim, Frame>& extrinsic_curvature,\n             const Scalar<DataType>& trace_extrinsic_curvature);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::ii<DataType, Dim, Frame> a_tilde(\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& extrinsic_curvature,\n    const Scalar<DataType>& trace_extrinsic_curvature);\n/// @}\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace Ccz4::BoundaryConditions {\nBoundaryCondition::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\nvoid BoundaryCondition::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n}  // namespace Ccz4::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\n/// \\brief Boundary conditions for the Ccz4 system\nnamespace Ccz4::BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace Ccz4::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  DirichletAnalytic.cpp\n  Sommerfeld.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  DirichletAnalytic.hpp\n  Sommerfeld.hpp\n  Factory.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/Solutions/Factory.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n\nnamespace Ccz4::BoundaryConditions {\n\n// LCOV_EXCL_START\nDirichletAnalytic::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n// LCOV_EXCL_STOP\nDirichletAnalytic::DirichletAnalytic(const DirichletAnalytic& rhs)\n    : BoundaryCondition{dynamic_cast<const BoundaryCondition&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\nDirichletAnalytic& DirichletAnalytic::operator=(const DirichletAnalytic& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\nDirichletAnalytic::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic::get_clone() const {\n  return std::make_unique<DirichletAnalytic>(*this);\n}\n\nvoid DirichletAnalytic::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | analytic_prescription_;\n}\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic::my_PUP_ID = 0;\n\nvoid DirichletAnalytic::fd_ghost(\n    const gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*>\n        conformal_metric,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor,\n    const gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*> a_tilde,\n    const gsl::not_null<Scalar<DataVector>*> trace_extrinsic_curvature,\n    const gsl::not_null<Scalar<DataVector>*> theta,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> gamma_hat,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        auxiliary_shift_b,\n    const Direction<3>& direction,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n\n    // fd_gridless_tags\n    double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n        grid_to_inertial_map,\n    const fd::Reconstructor& reconstructor) const {\n  const size_t ghost_zone_size = reconstructor.ghost_zone_size();\n\n  const auto ghost_logical_coords =\n      evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n          subcell_mesh, ghost_zone_size, direction);\n\n  const auto ghost_inertial_coords = grid_to_inertial_map(\n      logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n  // Compute FD ghost data with the analytic data or solution\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<\n          Tags::ConformalMetric<DataVector, 3>, gr::Tags::Lapse<DataVector>,\n          gr::Tags::Shift<DataVector, 3>, Tags::ConformalFactor<DataVector>,\n          Tags::ATilde<DataVector, 3>,\n          gr::Tags::TraceExtrinsicCurvature<DataVector>,\n          Tags::Theta<DataVector>, Tags::GammaHat<DataVector, 3>,\n          Tags::AuxiliaryShiftB<DataVector, 3>>,\n      Ccz4::Solutions::all_solutions>(\n      analytic_prescription_.get(),\n      [&ghost_inertial_coords, &time](const auto* const initial_data) {\n        using spacetime_tags = tmpl::list<\n            Tags::ConformalMetric<DataVector, 3>, gr::Tags::Lapse<DataVector>,\n            gr::Tags::Shift<DataVector, 3>, Tags::ConformalFactor<DataVector>,\n            Tags::ATilde<DataVector, 3>,\n            gr::Tags::TraceExtrinsicCurvature<DataVector>,\n            Tags::Theta<DataVector>, Tags::GammaHat<DataVector, 3>,\n            Tags::AuxiliaryShiftB<DataVector, 3>>;\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(ghost_inertial_coords, time,\n                                         spacetime_tags{});\n        } else if constexpr (evolution::is_numeric_initial_data_v<\n                                 std::decay_t<decltype(*initial_data)>>) {\n          ERROR(\n              \"Cannot currently use numeric initial data as an analytic \"\n              \"prescription for boundary conditions.\");\n        } else {\n          (void)time;\n          return initial_data->variables(ghost_inertial_coords,\n                                         spacetime_tags{});\n        }\n      });\n  *conformal_metric =\n      get<Tags::ConformalMetric<DataVector, 3>>(boundary_values);\n  *lapse = get<gr::Tags::Lapse<DataVector>>(boundary_values);\n  *shift = get<gr::Tags::Shift<DataVector, 3>>(boundary_values);\n  *conformal_factor = get<Tags::ConformalFactor<DataVector>>(boundary_values);\n  *a_tilde = get<Tags::ATilde<DataVector, 3>>(boundary_values);\n  *trace_extrinsic_curvature =\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(boundary_values);\n  *theta = get<Tags::Theta<DataVector>>(boundary_values);\n  *gamma_hat = get<Tags::GammaHat<DataVector, 3>>(boundary_values);\n  *auxiliary_shift_b =\n      get<Tags::AuxiliaryShiftB<DataVector, 3>>(boundary_values);\n}\n}  // namespace Ccz4::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Ccz4::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\nclass DirichletAnalytic final : public BoundaryCondition {\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n  using options = tmpl::list<AnalyticPrescription>;\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions using either analytic solution or \"\n      \"analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const\n      -> std::unique_ptr<\n          domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>>;\n  using fd_interior_primitive_variables_tags = tmpl::list<>;\n  using fd_gridless_tags =\n      tmpl::list<::Tags::Time, ::domain::Tags::FunctionsOfTime,\n                 domain::Tags::ElementMap<3, Frame::Grid>,\n                 domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                             Frame::Inertial>,\n                 ::Ccz4::fd::Tags::Reconstructor>;\n  void fd_ghost(\n      gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*>\n          conformal_metric,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<Scalar<DataVector>*> conformal_factor,\n      gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*> a_tilde,\n      gsl::not_null<Scalar<DataVector>*> trace_extrinsic_curvature,\n      gsl::not_null<Scalar<DataVector>*> theta,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> gamma_hat,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          auxiliary_shift_b,\n      const Direction<3>& direction,\n\n      // fd_interior_temporary_tags\n      const Mesh<3>& subcell_mesh,\n\n      // fd_gridless_tags\n      double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n          grid_to_inertial_map,\n      const fd::Reconstructor& reconstructor) const;\n\n private:\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace Ccz4::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\nusing standard_boundary_conditions =\n    tmpl::list<DirichletAnalytic,\n               domain::BoundaryConditions::Periodic<BoundaryCondition>,\n               Sommerfeld>;\n}  // namespace Ccz4::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/BoundaryConditions/Sommerfeld.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Sommerfeld.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n\nnamespace Ccz4::BoundaryConditions {\n\n// LCOV_EXCL_START\nSommerfeld::Sommerfeld(CkMigrateMessage* const msg) : BoundaryCondition(msg) {}\n// LCOV_EXCL_STOP\nSommerfeld::Sommerfeld(const Sommerfeld& rhs)\n    : BoundaryCondition{dynamic_cast<const BoundaryCondition&>(rhs)} {}\n\nSommerfeld& Sommerfeld::operator=(const Sommerfeld& /*rhs*/) { return *this; }\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nSommerfeld::get_clone() const {\n  return std::make_unique<Sommerfeld>(*this);\n}\n\nvoid Sommerfeld::pup(PUP::er& p) { BoundaryCondition::pup(p); }\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Sommerfeld::my_PUP_ID = 0;\n\nvoid Sommerfeld::fd_ghost(\n    const gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*>\n        conformal_metric,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor,\n    const gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*> a_tilde,\n    const gsl::not_null<Scalar<DataVector>*> trace_extrinsic_curvature,\n    const gsl::not_null<Scalar<DataVector>*> theta,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> gamma_hat,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        auxiliary_shift_b,\n    const Direction<3>& direction,\n\n    // fd_interior_evolved_variables_tags (variables_tag_list order)\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_conformal_metric,\n    const Scalar<DataVector>& interior_conformal_factor,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_a_tilde,\n    const Scalar<DataVector>& interior_trace_extrinsic_curvature,\n    const Scalar<DataVector>& interior_theta,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_gamma_hat,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_auxiliary_shift_b,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n\n    // fd_gridless_tags\n    const fd::Reconstructor& reconstructor) {\n  const size_t ghost_zone_size = reconstructor.ghost_zone_size();\n\n  const auto ghost_logical_coords =\n      evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n          subcell_mesh, ghost_zone_size, direction);\n\n  // Extrapolate the interior variables into the ghost zone of external bdry.\n  // Modification to the time derivatives per Sommerfeld BC is handled in the\n  // time derivative computation, not here.\n  // Should add option to use higher order interpolant once that PR (#6999) is\n  // merged.\n  const intrp::Irregular<3> irregular_interpolant(subcell_mesh,\n                                                  ghost_logical_coords);\n\n  Variables<typename ::Ccz4::fd::System::variables_tag_list> interior_var{\n      subcell_mesh.number_of_grid_points()};\n  get<Tags::ConformalMetric<DataVector, 3>>(interior_var) =\n      interior_conformal_metric;\n  get<gr::Tags::Lapse<DataVector>>(interior_var) = interior_lapse;\n  get<gr::Tags::Shift<DataVector, 3>>(interior_var) = interior_shift;\n  get<Tags::ConformalFactor<DataVector>>(interior_var) =\n      interior_conformal_factor;\n  get<Tags::ATilde<DataVector, 3>>(interior_var) = interior_a_tilde;\n  get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(interior_var) =\n      interior_trace_extrinsic_curvature;\n  get<Tags::Theta<DataVector>>(interior_var) = interior_theta;\n  get<Tags::GammaHat<DataVector, 3>>(interior_var) = interior_gamma_hat;\n  get<Tags::AuxiliaryShiftB<DataVector, 3>>(interior_var) =\n      interior_auxiliary_shift_b;\n\n  const auto boundary_values = irregular_interpolant.interpolate(interior_var);\n\n  *conformal_metric =\n      get<Tags::ConformalMetric<DataVector, 3>>(boundary_values);\n  *lapse = get<gr::Tags::Lapse<DataVector>>(boundary_values);\n  *shift = get<gr::Tags::Shift<DataVector, 3>>(boundary_values);\n  *conformal_factor = get<Tags::ConformalFactor<DataVector>>(boundary_values);\n  *a_tilde = get<Tags::ATilde<DataVector, 3>>(boundary_values);\n  *trace_extrinsic_curvature =\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(boundary_values);\n  *theta = get<Tags::Theta<DataVector>>(boundary_values);\n  *gamma_hat = get<Tags::GammaHat<DataVector, 3>>(boundary_values);\n  *auxiliary_shift_b =\n      get<Tags::AuxiliaryShiftB<DataVector, 3>>(boundary_values);\n}\n}  // namespace Ccz4::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/BoundaryConditions/Sommerfeld.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Ccz4::BoundaryConditions {\n/*!\n * \\brief Sets Sommerfeld boundary conditions per tensor component.\n *\n * Unlike time-independent subcell external boundary conditions,\n * the Sommerfeld boundary condition is applied at the outermost\n * interior points in the volume instead of the ghost zone.\n * This is because the Sommerfeld condition requires radial derivatives\n * of the fields and modify their time derivatives, which then need to\n * be time integrated. Under current infrastructure, it is therefore\n * the simplest to apply this boundary condition in the interior\n * rather than the ghost zone. This file extrapolates the interior\n * evolved variables into the ghost zone. Their radial derivatives and\n * time derivatives are then computed and applied in SoTimeDerivative.hpp for\n * the outermost interior points. All tensor components (including lapse and\n * shift) are treated as independent fields when applying this boundary\n * condition.\n *\n * \\warning This boundary condition assumes a complete sphere domain\n * (all wedges), as we only apply it on the upper_zeta\n * direction in blocks with external boundaries.\n */\nclass Sommerfeld final : public BoundaryCondition {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Sommerfeld boundary conditions applied per tensor component.\"};\n\n  Sommerfeld() = default;\n  Sommerfeld(Sommerfeld&&) = default;\n  Sommerfeld& operator=(Sommerfeld&&) = default;\n  Sommerfeld(const Sommerfeld&);\n  Sommerfeld& operator=(const Sommerfeld&);\n  ~Sommerfeld() override = default;\n\n  explicit Sommerfeld(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, Sommerfeld);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using fd_interior_evolved_variables_tags =\n      ::Ccz4::fd::System::variables_tag_list;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>>;\n  using fd_interior_primitive_variables_tags = tmpl::list<>;\n  using fd_gridless_tags = tmpl::list<::Ccz4::fd::Tags::Reconstructor>;\n  static void fd_ghost(\n      gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*> conformal_metric,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<Scalar<DataVector>*> conformal_factor,\n      gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*> a_tilde,\n      gsl::not_null<Scalar<DataVector>*> trace_extrinsic_curvature,\n      gsl::not_null<Scalar<DataVector>*> theta,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> gamma_hat,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> auxiliary_shift_b,\n      const Direction<3>& direction,\n\n      // fd_interior_evolved_variables_tags (variables_tag_list order)\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_conformal_metric,\n      const Scalar<DataVector>& interior_conformal_factor,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_a_tilde,\n      const Scalar<DataVector>& interior_trace_extrinsic_curvature,\n      const Scalar<DataVector>& interior_theta,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_gamma_hat,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_auxiliary_shift_b,\n\n      // fd_interior_temporary_tags\n      const Mesh<3>& subcell_mesh,\n\n      // fd_gridless_tags\n      const fd::Reconstructor& reconstructor);\n};\n}  // namespace Ccz4::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Ccz4)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ATilde.cpp\n  Ccz4WrappedGr.cpp\n  Christoffel.cpp\n  DerivChristoffel.cpp\n  DerivLapse.cpp\n  DerivZ4Constraint.cpp\n  RicciScalarPlusDivergenceZ4Constraint.cpp\n  TimeDerivative.cpp\n  Z4Constraint.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ATilde.hpp\n  Ccz4WrappedGr.hpp\n  Ccz4WrappedGr.tpp\n  Christoffel.hpp\n  DerivChristoffel.hpp\n  DerivLapse.hpp\n  DerivZ4Constraint.hpp\n  Ricci.hpp\n  Ricci.tpp\n  RicciScalarPlusDivergenceZ4Constraint.hpp\n  System.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  TempTags.hpp\n  TimeDerivative.hpp\n  Z4Constraint.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DgSubcell\n  DomainStructure\n  ErrorHandling\n  FiniteDifference\n  GeneralRelativity\n  GeneralRelativitySolutions\n  Options\n  Serialization\n  Spectral\n  Utilities\n  INTERFACE\n  )\n\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Instantiations)\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Ccz4WrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/Ccz4WrappedGr.hpp\"\n\n#include \"Evolution/Systems/Ccz4/Ccz4WrappedGr.tpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nGENERATE_INSTANTIATIONS(CCZ4_WRAPPED_GR_INSTANTIATE,\n                        (gr::Solutions::GaugeWave<3>,\n                         gr::Solutions::GaugePlaneWave<3>,\n                         gr::Solutions::KerrSchild, gr::Solutions::Minkowski<3>,\n                         gr::Solutions::TrumpetSchwarzschild))\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Ccz4WrappedGr.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Ccz4::Solutions {\n/*!\n * \\brief A wrapper for general-relativity analytic solutions that loads\n * the analytic solution and then adds a function that returns\n * any combination of the Ccz4 evolution variables.\n * Specifically, see `Ccz4::fd::System`.\n */\ntemplate <typename SolutionType>\nclass Ccz4WrappedGr : public virtual evolution::initial_data::InitialData,\n                      public SolutionType {\n public:\n  using SolutionType::SolutionType;\n\n  Ccz4WrappedGr() = default;\n  Ccz4WrappedGr(const Ccz4WrappedGr& /*rhs*/) = default;\n  Ccz4WrappedGr& operator=(const Ccz4WrappedGr& /*rhs*/) = default;\n  Ccz4WrappedGr(Ccz4WrappedGr&& /*rhs*/) = default;\n  Ccz4WrappedGr& operator=(Ccz4WrappedGr&& /*rhs*/) = default;\n  ~Ccz4WrappedGr() override = default;\n\n  explicit Ccz4WrappedGr(const SolutionType& wrapped_solution);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit Ccz4WrappedGr(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Ccz4WrappedGr);\n  /// \\endcond\n\n  static constexpr size_t volume_dim = SolutionType::volume_dim;\n  static_assert(volume_dim == 3,\n                \"Ccz4 evolution system has only been implemented in 3D!\");\n  using options = typename SolutionType::options;\n  static constexpr Options::String help = SolutionType::help;\n  static std::string name() {\n    return \"Ccz4(\" + pretty_type::name<SolutionType>() + \")\";\n  }\n\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataVector, volume_dim>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, volume_dim>,\n                    tmpl::size_t<volume_dim>, Frame::Inertial>;\n  using TimeDerivLapse = ::Tags::dt<gr::Tags::Lapse<DataVector>>;\n  using TimeDerivShift = ::Tags::dt<gr::Tags::Shift<DataVector, volume_dim>>;\n  using TimeDerivSpatialMetric =\n      ::Tags::dt<gr::Tags::SpatialMetric<DataVector, volume_dim>>;\n\n  using IntermediateVars = tuples::tagged_tuple_from_typelist<\n      typename SolutionType::template tags<DataVector>>;\n\n  template <typename DataType>\n  using tags =\n      tmpl::push_back<typename SolutionType::template tags<DataType>,\n                      Ccz4::Tags::ConformalMetric<DataType, volume_dim>,\n                      Ccz4::Tags::ConformalFactor<DataType>,\n                      Ccz4::Tags::ATilde<DataType, volume_dim>,\n                      gr::Tags::TraceExtrinsicCurvature<DataType>,\n                      Ccz4::Tags::Theta<DataType>,\n                      Ccz4::Tags::GammaHat<DataType, volume_dim>,\n                      Ccz4::Tags::AuxiliaryShiftB<DataType, volume_dim>>;\n\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataVector, volume_dim>& x, const double t,\n      tmpl::list<Tags...> /*meta*/) const {\n    // Get the underlying solution's variables using the solution's tags list,\n    // store in IntermediateVariables\n    const IntermediateVars& intermediate_vars = SolutionType::variables(\n        x, t, typename SolutionType::template tags<DataVector>{});\n\n    return {\n        get<Tags>(variables(x, t, tmpl::list<Tags>{}, intermediate_vars))...};\n  }\n\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, volume_dim>& x,\n                                     const double t,\n                                     tmpl::list<Tag> /*meta*/) const {\n    const IntermediateVars& intermediate_vars = SolutionType::variables(\n        x, t, typename SolutionType::template tags<DataVector>{});\n    return {get<Tag>(variables(x, t, tmpl::list<Tag>{}, intermediate_vars))};\n  }\n\n  // overloads for wrapping analytic data\n\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataVector, volume_dim>& x,\n      tmpl::list<Tags...> /*meta*/) const {\n    // Get the underlying solution's variables using the solution's tags list,\n    // store in IntermediateVariables\n    const IntermediateVars intermediate_vars = SolutionType::variables(\n        x, typename SolutionType::template tags<DataVector>{});\n\n    return {get<Tags>(variables(x, tmpl::list<Tags>{}, intermediate_vars))...};\n  }\n\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, volume_dim>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    const IntermediateVars intermediate_vars = SolutionType::variables(\n        x, typename SolutionType::template tags<DataVector>{});\n    return {get<Tag>(variables(x, tmpl::list<Tag>{}, intermediate_vars))};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  // Preprocessor logic to avoid declaring variables() functions for\n  // tags other than the three the wrapper adds (i.e., other than\n  // Ccz4::Tags::ConformalMetric, Ccz4::Tags::ConformalFactor,\n  // Ccz4:Tags::ATilde, gr::Tags::TraceExtrinsicCurvature,\n  // Ccz4::Tags::Theta, Ccz4::Tags::GammaHat)\n  using TagShift = gr::Tags::Shift<DataVector, volume_dim>;\n  using TagSpatialMetric = gr::Tags::SpatialMetric<DataVector, volume_dim>;\n  using TagInverseSpatialMetric =\n      gr::Tags::InverseSpatialMetric<DataVector, volume_dim>;\n  using TagExCurvature = gr::Tags::ExtrinsicCurvature<DataVector, volume_dim>;\n\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/, double /*t*/,\n      tmpl::list<Tag> /*meta*/,\n      const IntermediateVars& intermediate_vars) const {\n    static_assert(\n        tmpl::list_contains_v<typename SolutionType::template tags<DataVector>,\n                              Tag>);\n    return {get<Tag>(intermediate_vars)};\n  }\n\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/, tmpl::list<Tag> /*meta*/,\n      const IntermediateVars& intermediate_vars) const {\n    static_assert(\n        tmpl::list_contains_v<typename SolutionType::template tags<DataVector>,\n                              Tag>);\n    return {get<Tag>(intermediate_vars)};\n  }\n\n  tuples::TaggedTuple<Ccz4::Tags::ConformalMetric<DataVector, volume_dim>>\n  variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<Ccz4::Tags::ConformalMetric<DataVector, volume_dim>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n  tuples::TaggedTuple<Ccz4::Tags::ConformalFactor<DataVector>> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<Ccz4::Tags::ConformalFactor<DataVector>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n  tuples::TaggedTuple<Ccz4::Tags::ATilde<DataVector, volume_dim>> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<Ccz4::Tags::ATilde<DataVector, volume_dim>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n  tuples::TaggedTuple<gr::Tags::TraceExtrinsicCurvature<DataVector>> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<gr::Tags::TraceExtrinsicCurvature<DataVector>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n  tuples::TaggedTuple<Ccz4::Tags::Theta<DataVector>> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<Ccz4::Tags::Theta<DataVector>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n  tuples::TaggedTuple<Ccz4::Tags::GammaHat<DataVector, volume_dim>> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<Ccz4::Tags::GammaHat<DataVector, volume_dim>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n  tuples::TaggedTuple<Ccz4::Tags::AuxiliaryShiftB<DataVector, volume_dim>>\n  variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<Ccz4::Tags::AuxiliaryShiftB<DataVector, volume_dim>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n\n  tuples::TaggedTuple<Ccz4::Tags::ConformalMetric<DataVector, volume_dim>>\n  variables(\n      const tnsr::I<DataVector, volume_dim>& x, const double /*t*/,\n      tmpl::list<Ccz4::Tags::ConformalMetric<DataVector, volume_dim>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n  tuples::TaggedTuple<Ccz4::Tags::ConformalFactor<DataVector>> variables(\n      const tnsr::I<DataVector, volume_dim>& x, double /*t*/,\n      tmpl::list<Ccz4::Tags::ConformalFactor<DataVector>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n  tuples::TaggedTuple<Ccz4::Tags::ATilde<DataVector, volume_dim>> variables(\n      const tnsr::I<DataVector, volume_dim>& x, double /*t*/,\n      tmpl::list<Ccz4::Tags::ATilde<DataVector, volume_dim>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n  tuples::TaggedTuple<gr::Tags::TraceExtrinsicCurvature<DataVector>> variables(\n      const tnsr::I<DataVector, volume_dim>& x, const double /*t*/,\n      tmpl::list<gr::Tags::TraceExtrinsicCurvature<DataVector>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n  tuples::TaggedTuple<Ccz4::Tags::Theta<DataVector>> variables(\n      const tnsr::I<DataVector, volume_dim>& x, double /*t*/,\n      tmpl::list<Ccz4::Tags::Theta<DataVector>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n  tuples::TaggedTuple<Ccz4::Tags::GammaHat<DataVector, volume_dim>> variables(\n      const tnsr::I<DataVector, volume_dim>& x, double /*t*/,\n      tmpl::list<Ccz4::Tags::GammaHat<DataVector, volume_dim>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n  tuples::TaggedTuple<Ccz4::Tags::AuxiliaryShiftB<DataVector, volume_dim>>\n  variables(\n      const tnsr::I<DataVector, volume_dim>& x, double /*t*/,\n      tmpl::list<Ccz4::Tags::AuxiliaryShiftB<DataVector, volume_dim>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n};\n\ntemplate <typename SolutionType>\nbool operator==(const Ccz4WrappedGr<SolutionType>& lhs,\n                const Ccz4WrappedGr<SolutionType>& rhs);\n\ntemplate <typename SolutionType>\nbool operator!=(const Ccz4WrappedGr<SolutionType>& lhs,\n                const Ccz4WrappedGr<SolutionType>& rhs);\n\ntemplate <typename SolutionType>\nCcz4WrappedGr(SolutionType solution) -> Ccz4WrappedGr<SolutionType>;\n}  // namespace Ccz4::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Ccz4WrappedGr.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Ccz4/Ccz4WrappedGr.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/ATilde.hpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace Ccz4::Solutions {\ntemplate <typename SolutionType>\nCcz4WrappedGr<SolutionType>::Ccz4WrappedGr(const SolutionType& wrapped_solution)\n    : SolutionType(wrapped_solution) {}\n\ntemplate <typename SolutionType>\nCcz4WrappedGr<SolutionType>::Ccz4WrappedGr(CkMigrateMessage* msg)\n    : InitialData(msg), SolutionType(msg) {}\n\ntemplate <typename SolutionType>\nstd::unique_ptr<evolution::initial_data::InitialData>\nCcz4WrappedGr<SolutionType>::get_clone() const {\n  return std::make_unique<Ccz4WrappedGr<SolutionType>>(*this);\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<Ccz4::Tags::ConformalMetric<\n    DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>\nCcz4WrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<Ccz4::Tags::ConformalMetric<\n        DataVector,\n        Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& spatial_metric = get<gr::Tags::SpatialMetric<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(intermediate_vars);\n  Scalar<DataVector> conformal_factor;\n  get(conformal_factor) = pow(get(sqrt_det_spatial_metric), -1. / 3.);\n  tnsr::ii<DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>\n      conformal_spatial_metric;\n  ::tenex::evaluate<ti::i, ti::j>(make_not_null(&conformal_spatial_metric),\n                                  (conformal_factor)() * (conformal_factor)() *\n                                      (spatial_metric)(ti::i, ti::j));\n  return {std::move(conformal_spatial_metric)};\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<Ccz4::Tags::ConformalFactor<DataVector>>\nCcz4WrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<Ccz4::Tags::ConformalFactor<DataVector>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(intermediate_vars);\n  Scalar<DataVector> conformal_factor;\n  get(conformal_factor) = pow(get(sqrt_det_spatial_metric), -1. / 3.);\n\n  return {std::move(conformal_factor)};\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<Ccz4::Tags::ATilde<\n    DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>\nCcz4WrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<Ccz4::Tags::ATilde<\n        DataVector,\n        Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& spatial_metric = get<gr::Tags::SpatialMetric<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(intermediate_vars);\n  Scalar<DataVector> conformal_factor_squared;\n  get(conformal_factor_squared) = pow(get(sqrt_det_spatial_metric), -2. / 3.);\n  const auto& extrinsic_curvature = get<gr::Tags::ExtrinsicCurvature<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto& inverse_spatial_metric = get<gr::Tags::InverseSpatialMetric<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto trace_extrinsic_curvature =\n      trace(extrinsic_curvature, inverse_spatial_metric);\n\n  return {::Ccz4::a_tilde(conformal_factor_squared, spatial_metric,\n                          extrinsic_curvature, trace_extrinsic_curvature)};\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<gr::Tags::TraceExtrinsicCurvature<DataVector>>\nCcz4WrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<gr::Tags::TraceExtrinsicCurvature<DataVector>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& extrinsic_curvature = get<gr::Tags::ExtrinsicCurvature<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto& inverse_spatial_metric = get<gr::Tags::InverseSpatialMetric<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto trace_extrinsic_curvature =\n      trace(extrinsic_curvature, inverse_spatial_metric);\n\n  return {std::move(trace_extrinsic_curvature)};\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<Ccz4::Tags::Theta<DataVector>>\nCcz4WrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<Ccz4::Tags::Theta<DataVector>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(intermediate_vars);\n  // Theta won't be exactly zero due to numerical errors\n  // in the initial data leading to constraint violations.\n  // But in practice evolutions set this to zero; see e.g.\n  // Dumbser2017okk\n  const auto theta =\n      make_with_value<Scalar<DataVector>>(sqrt_det_spatial_metric, 0.0);\n\n  return {std::move(theta)};\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<Ccz4::Tags::GammaHat<\n    DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>\nCcz4WrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<Ccz4::Tags::GammaHat<\n        DataVector,\n        Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(intermediate_vars);\n  Scalar<DataVector> inverse_conformal_factor_squared;\n  get(inverse_conformal_factor_squared) =\n      pow(get(sqrt_det_spatial_metric), 2. / 3.);\n  const auto& inverse_spatial_metric = get<gr::Tags::InverseSpatialMetric<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  tnsr::II<DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>\n      inverse_conformal_spatial_metric;\n  ::tenex::evaluate<ti::I, ti::J>(\n      make_not_null(&inverse_conformal_spatial_metric),\n      inverse_conformal_factor_squared() *\n          inverse_spatial_metric(ti::I, ti::J));\n\n  const auto& d_spatial_metric = get<DerivSpatialMetric>(intermediate_vars);\n  tnsr::i<DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>\n      d_det_spatial_metric;\n  ::tenex::evaluate<ti::i>(make_not_null(&d_det_spatial_metric),\n                           sqrt_det_spatial_metric() *\n                               sqrt_det_spatial_metric() *\n                               inverse_spatial_metric(ti::J, ti::K) *\n                               d_spatial_metric(ti::i, ti::j, ti::k));\n  const auto& spatial_metric = get<gr::Tags::SpatialMetric<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  tnsr::ijj<DataVector,\n            Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>\n      field_d(get(inverse_conformal_factor_squared));\n  for (size_t k = 0;\n       k < Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim; k++) {\n    for (size_t i = 0;\n         i < Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim; i++) {\n      for (size_t j = i;\n           j < Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim; j++) {\n        field_d.get(k, i, j) = d_spatial_metric.get(k, i, j) /\n                                   get(inverse_conformal_factor_squared) -\n                               pow(get(inverse_conformal_factor_squared), -4) *\n                                   d_det_spatial_metric.get(k) *\n                                   spatial_metric.get(i, j) / 3.;\n        field_d.get(k, i, j) *= 0.5;\n      }\n    }\n  }\n\n  const auto conformal_christoffel_second_kind =\n      ::Ccz4::conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, field_d);\n  // \\tilde{Gamma}^i in Ccz4\n  const auto contracted_conformal_christoffel_second_kind =\n      ::Ccz4::contracted_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, conformal_christoffel_second_kind);\n\n  // Similar to Theta, we assume the spatial Z4 constraints are zero,\n  // so \\hat{Gamma}^i = \\tilde{Gamma}^i\n  return {std::move(contracted_conformal_christoffel_second_kind)};\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<Ccz4::Tags::AuxiliaryShiftB<\n    DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>\nCcz4WrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<Ccz4::Tags::AuxiliaryShiftB<\n        DataVector,\n        Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const double one_over_f = 1. / ::Ccz4::fd::System::f;\n  const bool shifting_shift = ::Ccz4::fd::System::shifting_shift;\n\n  tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>\n      auxiliary_shift_b;\n  const auto& shift = get<gr::Tags::Shift<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto& d_shift = get<DerivShift>(intermediate_vars);\n  const auto& dt_shift = get<::Tags::dt<gr::Tags::Shift<\n      DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>>>(\n      intermediate_vars);\n\n  if (shifting_shift) {\n    ::tenex::evaluate<ti::I>(\n        make_not_null(&auxiliary_shift_b),\n        one_over_f * dt_shift(ti::I) -\n            one_over_f * shift(ti::K) * d_shift(ti::k, ti::I));\n  } else {\n    ::tenex::evaluate<ti::I>(make_not_null(&auxiliary_shift_b),\n                             one_over_f * dt_shift(ti::I));\n  }\n\n  return {std::move(auxiliary_shift_b)};\n}\n\ntemplate <typename SolutionType>\nvoid Ccz4WrappedGr<SolutionType>::pup(PUP::er& p) {\n  InitialData::pup(p);\n  SolutionType::pup(p);\n}\n\ntemplate <typename SolutionType>\nPUP::able::PUP_ID Ccz4WrappedGr<SolutionType>::my_PUP_ID = 0;\n\ntemplate <typename SolutionType>\nbool operator==(const Ccz4WrappedGr<SolutionType>& lhs,\n                const Ccz4WrappedGr<SolutionType>& rhs) {\n  return static_cast<const SolutionType&>(lhs) ==\n         static_cast<const SolutionType&>(rhs);\n}\n\ntemplate <typename SolutionType>\nbool operator!=(const Ccz4WrappedGr<SolutionType>& lhs,\n                const Ccz4WrappedGr<SolutionType>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define CCZ4_WRAPPED_GR_SOLUTION_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define CCZ4_WRAPPED_GR_INSTANTIATE(_, data)                                   \\\n  template class Ccz4::Solutions::Ccz4WrappedGr<CCZ4_WRAPPED_GR_SOLUTION_TYPE( \\\n      data)>;                                                                  \\\n  template bool Ccz4::Solutions::operator==(                                   \\\n      const Ccz4WrappedGr<CCZ4_WRAPPED_GR_SOLUTION_TYPE(data)>& lhs,           \\\n      const Ccz4WrappedGr<CCZ4_WRAPPED_GR_SOLUTION_TYPE(data)>& rhs);          \\\n  template bool Ccz4::Solutions::operator!=(                                   \\\n      const Ccz4WrappedGr<CCZ4_WRAPPED_GR_SOLUTION_TYPE(data)>& lhs,           \\\n      const Ccz4WrappedGr<CCZ4_WRAPPED_GR_SOLUTION_TYPE(data)>& rhs);\n}  // namespace Ccz4::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Christoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid conformal_christoffel_second_kind(\n    const gsl::not_null<tnsr::Ijj<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d) {\n  ::tenex::evaluate<ti::K, ti::i, ti::j>(\n      result, inverse_conformal_spatial_metric(ti::K, ti::L) *\n                  (field_d(ti::i, ti::j, ti::l) + field_d(ti::j, ti::i, ti::l) -\n                   field_d(ti::l, ti::i, ti::j)));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::Ijj<DataType, Dim, Frame> conformal_christoffel_second_kind(\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d) {\n  tnsr::Ijj<DataType, Dim, Frame> result{};\n  conformal_christoffel_second_kind(make_not_null(&result),\n                                    inverse_conformal_spatial_metric, field_d);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid christoffel_second_kind(\n    const gsl::not_null<tnsr::Ijj<DataType, Dim, Frame>*> result,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::i<DataType, Dim, Frame>& field_p,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind) {\n  ::tenex::evaluate<ti::K, ti::i, ti::j>(\n      result,\n      conformal_christoffel_second_kind(ti::K, ti::i, ti::j) -\n          inverse_conformal_spatial_metric(ti::K, ti::L) *\n              (conformal_spatial_metric(ti::j, ti::l) * field_p(ti::i) +\n               conformal_spatial_metric(ti::i, ti::l) * field_p(ti::j) -\n               conformal_spatial_metric(ti::i, ti::j) * field_p(ti::l)));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::Ijj<DataType, Dim, Frame> christoffel_second_kind(\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::i<DataType, Dim, Frame>& field_p,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind) {\n  tnsr::Ijj<DataType, Dim, Frame> result{};\n  christoffel_second_kind(make_not_null(&result), conformal_spatial_metric,\n                          inverse_conformal_spatial_metric, field_p,\n                          conformal_christoffel_second_kind);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid contracted_conformal_christoffel_second_kind(\n    const gsl::not_null<tnsr::I<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind) {\n  ::tenex::evaluate<ti::I>(\n      result, inverse_conformal_spatial_metric(ti::J, ti::L) *\n                  conformal_christoffel_second_kind(ti::I, ti::j, ti::l));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::I<DataType, Dim, Frame> contracted_conformal_christoffel_second_kind(\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind) {\n  tnsr::I<DataType, Dim, Frame> result{};\n  contracted_conformal_christoffel_second_kind(\n      make_not_null(&result), inverse_conformal_spatial_metric,\n      conformal_christoffel_second_kind);\n  return result;\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void Ccz4::conformal_christoffel_second_kind(                   \\\n      const gsl::not_null<tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                          \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_conformal_spatial_metric,                                \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& field_d);      \\\n  template tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  Ccz4::conformal_christoffel_second_kind(                                 \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_conformal_spatial_metric,                                \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& field_d);      \\\n  template void Ccz4::christoffel_second_kind(                             \\\n      const gsl::not_null<tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                          \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          conformal_spatial_metric,                                        \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_conformal_spatial_metric,                                \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& field_p,         \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          conformal_christoffel_second_kind);                              \\\n  template tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  Ccz4::christoffel_second_kind(                                           \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          conformal_spatial_metric,                                        \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_conformal_spatial_metric,                                \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& field_p,         \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          conformal_christoffel_second_kind);                              \\\n  template void Ccz4::contracted_conformal_christoffel_second_kind(        \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*>   \\\n          result,                                                          \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_conformal_spatial_metric,                                \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          conformal_christoffel_second_kind);                              \\\n  template tnsr::I<DTYPE(data), DIM(data), FRAME(data)>                    \\\n  Ccz4::contracted_conformal_christoffel_second_kind(                      \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_conformal_spatial_metric,                                \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          conformal_christoffel_second_kind);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Christoffel.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\n/// @{\n/*!\n * \\brief Computes the conformal spatial christoffel symbols of the second kind.\n *\n * \\details Computes the christoffel symbols as:\n * \\f{align}\n *     \\tilde{\\Gamma}^k_{ij} &=\n *         \\tilde{\\gamma}^{kl} (D_{ijl} + D_{jil} - D_{lij})\n * \\f}\n * where \\f$\\tilde{\\gamma}^{ij}\\f$ and \\f$D_{ijk}\\f$ are the inverse conformal\n * spatial metric and the CCZ4 auxiliary variable defined by\n * `Ccz4::Tags::InverseConformalMetric` and `Ccz4::Tags::FieldD`, respectively.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid conformal_christoffel_second_kind(\n    const gsl::not_null<tnsr::Ijj<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::Ijj<DataType, Dim, Frame> conformal_christoffel_second_kind(\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the spatial christoffel symbols of the second kind.\n *\n * \\details Computes the christoffel symbols as:\n * \\f{align}\n *     \\Gamma^k_{ij} &= \\tilde{\\Gamma}^k_{ij} -\n *         \\tilde{\\gamma}^{kl} (\\tilde{\\gamma}_{jl} P_i +\n *                              \\tilde{\\gamma}_{il} P_j -\n *                              \\tilde{\\gamma}_{ij} P_l)\n * \\f}\n * where \\f$\\tilde{\\gamma}^{ij}\\f$, \\f$\\tilde{\\gamma}_{ij}\\f$,\n * \\f$\\tilde{\\Gamma}^k_{ij}\\f$, and \\f$P_i\\f$ are the conformal spatial metric,\n * the inverse conformal spatial metric, the conformal spatial christoffel\n * symbols of the second kind, and the CCZ4 auxiliary variable defined by\n * `Ccz4::Tags::ConformalMetric`, `Ccz4::Tags::InverseConformalMetric`,\n * `Ccz4::Tags::ConformalChristoffelSecondKind`, and `Ccz4::Tags::FieldP`,\n * respectively.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid christoffel_second_kind(\n    const gsl::not_null<tnsr::Ijj<DataType, Dim, Frame>*> result,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::i<DataType, Dim, Frame>& field_p,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::Ijj<DataType, Dim, Frame> christoffel_second_kind(\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::i<DataType, Dim, Frame>& field_p,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the contraction of the conformal spatial Christoffel symbols\n * of the second kind.\n *\n * \\details Computes the contraction as:\n *\n * \\f{align}\n *     \\tilde{\\Gamma}^i &= \\tilde{\\gamma}^{jl} \\tilde{\\Gamma}^i_{jl}\n * \\f}\n *\n * where \\f$\\tilde{\\gamma}^{ij}\\f$ is the inverse conformal spatial metric\n * defined by `Ccz4::Tags::InverseConformalMetric` and\n * \\f$\\tilde{\\Gamma}^k_{ij}\\f$ is the conformal spatial Christoffel symbols of\n * the second kind defined by `Ccz4::Tags::ConformalChristoffelSecondKind`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid contracted_conformal_christoffel_second_kind(\n    const gsl::not_null<tnsr::I<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::I<DataType, Dim, Frame> contracted_conformal_christoffel_second_kind(\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind);\n/// @}\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/DerivChristoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/DerivChristoffel.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\nvoid deriv_conformal_christoffel_second_kind(\n    const gsl::not_null<tnsr::iJkk<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d, const TensorType& d_field_d,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up) {\n  // We keep this for loop specialization for faster speed when no symmetry\n  // of d_field_d is present.\n  if constexpr (std::is_same_v<TensorType, tnsr::ijkk<DataType, Dim, Frame>>) {\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = i; j < Dim; ++j) {\n        for (size_t k = 0; k < Dim; ++k) {\n          for (size_t m = 0; m < Dim; ++m) {\n            (*result).get(k, m, i, j) =\n                -2.0 * field_d_up.get(k, m, 0) *\n                    (field_d.get(i, j, 0) + field_d.get(j, i, 0) -\n                     field_d.get(0, i, j)) +\n                0.5 * inverse_conformal_spatial_metric.get(m, 0) *\n                    (d_field_d.get(k, i, j, 0) + d_field_d.get(i, k, j, 0) +\n                     d_field_d.get(k, j, i, 0) + d_field_d.get(j, k, i, 0) -\n                     d_field_d.get(k, 0, i, j) - d_field_d.get(0, k, i, j));\n            for (size_t l = 1; l < Dim; ++l) {\n              (*result).get(k, m, i, j) +=\n                  -2.0 * field_d_up.get(k, m, l) *\n                      (field_d.get(i, j, l) + field_d.get(j, i, l) -\n                       field_d.get(l, i, j)) +\n                  0.5 * inverse_conformal_spatial_metric.get(m, l) *\n                      (d_field_d.get(k, i, j, l) + d_field_d.get(i, k, j, l) +\n                       d_field_d.get(k, j, i, l) + d_field_d.get(j, k, i, l) -\n                       d_field_d.get(k, l, i, j) - d_field_d.get(l, k, i, j));\n            }\n          }\n        }\n      }\n    }\n  } else {\n    ::tenex::evaluate<ti::k, ti::M, ti::i, ti::j>(\n        result,\n        -2.0 * field_d_up(ti::k, ti::M, ti::L) *\n                (field_d(ti::i, ti::j, ti::l) + field_d(ti::j, ti::i, ti::l) -\n                 field_d(ti::l, ti::i, ti::j)) +\n            inverse_conformal_spatial_metric(ti::M, ti::L) * 0.5 *\n                (d_field_d(ti::k, ti::i, ti::j, ti::l) +\n                 d_field_d(ti::i, ti::k, ti::j, ti::l) +\n                 d_field_d(ti::k, ti::j, ti::i, ti::l) +\n                 d_field_d(ti::j, ti::k, ti::i, ti::l) -\n                 d_field_d(ti::k, ti::l, ti::i, ti::j) -\n                 d_field_d(ti::l, ti::k, ti::i, ti::j)));\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\ntnsr::iJkk<DataType, Dim, Frame> deriv_conformal_christoffel_second_kind(\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d, const TensorType& d_field_d,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up) {\n  tnsr::iJkk<DataType, Dim, Frame> result{};\n  deriv_conformal_christoffel_second_kind(make_not_null(&result),\n                                          inverse_conformal_spatial_metric,\n                                          field_d, d_field_d, field_d_up);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid deriv_contracted_conformal_christoffel_second_kind(\n    const gsl::not_null<tnsr::iJ<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind,\n    const tnsr::iJkk<DataType, Dim, Frame>&\n        d_conformal_christoffel_second_kind) {\n  ::tenex::evaluate<ti::k, ti::I>(\n      result,\n      -2.0 * field_d_up(ti::k, ti::J, ti::L) *\n              conformal_christoffel_second_kind(ti::I, ti::j, ti::l) +\n          inverse_conformal_spatial_metric(ti::J, ti::L) *\n              d_conformal_christoffel_second_kind(ti::k, ti::I, ti::j, ti::l));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::iJ<DataType, Dim, Frame>\nderiv_contracted_conformal_christoffel_second_kind(\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind,\n    const tnsr::iJkk<DataType, Dim, Frame>&\n        d_conformal_christoffel_second_kind) {\n  tnsr::iJ<DataType, Dim, Frame> result{};\n  deriv_contracted_conformal_christoffel_second_kind(\n      make_not_null(&result), inverse_conformal_spatial_metric, field_d_up,\n      conformal_christoffel_second_kind, d_conformal_christoffel_second_kind);\n  return result;\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define TTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE_CONFORMAL_CHRISTOFFEL(_, data)                          \\\n  template void Ccz4::deriv_conformal_christoffel_second_kind(              \\\n      const gsl::not_null<tnsr::iJkk<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                           \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          inverse_conformal_spatial_metric,                                 \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& field_d,        \\\n      const TTYPE(data) < DTYPE(data), DIM(data), FRAME(data) > &d_field_d, \\\n      const tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>& field_d_up);    \\\n  template tnsr::iJkk<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  Ccz4::deriv_conformal_christoffel_second_kind(                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          inverse_conformal_spatial_metric,                                 \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& field_d,        \\\n      const TTYPE(data) < DTYPE(data), DIM(data), FRAME(data) > &d_field_d, \\\n      const tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>& field_d_up);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_CONFORMAL_CHRISTOFFEL, (1, 2, 3),\n                        (Frame::Grid, Frame::Inertial), (double, DataVector),\n                        (tnsr::iijj, tnsr::ijkk))\n\n#undef INSTANTIATE_CONFORMAL_CHRISTOFFEL\n#undef TTYPE\n\n#define INSTANTIATE_CONTRACTED_CHRISTOFFEL(_, data)                       \\\n  template void Ccz4::deriv_contracted_conformal_christoffel_second_kind( \\\n      const gsl::not_null<tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                         \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_conformal_spatial_metric,                               \\\n      const tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>& field_d_up,   \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          conformal_christoffel_second_kind,                              \\\n      const tnsr::iJkk<DTYPE(data), DIM(data), FRAME(data)>&              \\\n          d_conformal_christoffel_second_kind);                           \\\n  template tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  Ccz4::deriv_contracted_conformal_christoffel_second_kind(               \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_conformal_spatial_metric,                               \\\n      const tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>& field_d_up,   \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          conformal_christoffel_second_kind,                              \\\n      const tnsr::iJkk<DTYPE(data), DIM(data), FRAME(data)>&              \\\n          d_conformal_christoffel_second_kind);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_CONTRACTED_CHRISTOFFEL, (1, 2, 3),\n                        (Frame::Grid, Frame::Inertial), (double, DataVector))\n\n#undef INSTANTIATE_CONTRACTED_CHRISTOFFEL\n#undef DTYPE\n#undef FRAME\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/DerivChristoffel.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\n/// @{\n/*!\n * \\brief Computes the spatial derivative of the conformal spatial christoffel\n * symbols of the second kind\n *\n * \\details Computes the derivative as:\n * \\f{align}\n *     \\partial_k \\tilde{\\Gamma}^m{}_{ij} &=\n *       -2 D_k{}^{ml} (D_{ijl} + D_{jil} - D_{lij}) +\n *       \\tilde{\\gamma}^{ml}(\\partial_{(k} D_{i)jl} + \\partial_{(k} D_{j)il} -\n *       \\partial_{(k} D_{l)ij})\n * \\f}\n * where \\f$\\tilde{\\gamma}^{ij}\\f$, \\f$D_{ijk}\\f$, \\f$\\partial_l D_{ijk}\\f$, and\n * \\f$D_k{}^{ij}\\f$ are the inverse conformal spatial metric defined by\n * `Ccz4::Tags::InverseConformalMetric`, the CCZ4 auxiliary variable defined by\n * `Ccz4::Tags::FieldD`, its spatial derivative, and the CCZ4 identity defined\n * by `Ccz4::Tags::FieldDUp`.\n * \\note In second-order Ccz4, we impose symmetry of index k and l\n * in \\f$ \\partial_l D_{kij}=\\frac{1}{2}\\partial_l \\partial_k\n * \\tilde{\\gamma}_{ij} \\f$, because partial derivatives commute and to use\n * `second_partial_derivatives()`. \\f$ D_{kij} \\f$ is evolved in\n * the first-order system so no such symmetry is imposed.\n */\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\nvoid deriv_conformal_christoffel_second_kind(\n    const gsl::not_null<tnsr::iJkk<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d, const TensorType& d_field_d,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up);\n\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\ntnsr::iJkk<DataType, Dim, Frame> deriv_conformal_christoffel_second_kind(\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d, const TensorType& d_field_d,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up);\n\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the spatial derivative of the contraction of the conformal\n * spatial Christoffel symbols of the second kind\n *\n * \\details Computes the derivative as:\n *\n * \\f{align}\n *     \\partial_k \\tilde{\\Gamma}^i &= -2 D_k{}^{jl} \\tilde{\\Gamma}^i_{jl} +\n *       \\tilde{\\gamma}^{jl} \\partial_k \\tilde{\\Gamma}^i_{jl}\n * \\f}\n *\n * where \\f$\\tilde{\\gamma}^{ij}\\f$ is the inverse conformal spatial metric\n * defined by `Ccz4::Tags::InverseConformalMetric`, \\f$D_k{}^{ij}\\f$ is the CCZ4\n * identity defined by `Ccz4::Tags::FieldDUp`, \\f$\\tilde{\\Gamma}^k_{ij}\\f$ is\n * the conformal spatial Christoffel symbols of the second kind defined by\n * `Ccz4::Tags::ConformalChristoffelSecondKind`, and\n * \\f$\\partial_k \\tilde{\\Gamma}^k_{ij}\\f$ is its spatial derivative defined by\n * `Ccz4::Tags::DerivConformalChristoffelSecondKind`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid deriv_contracted_conformal_christoffel_second_kind(\n    const gsl::not_null<tnsr::iJ<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind,\n    const tnsr::iJkk<DataType, Dim, Frame>&\n        d_conformal_christoffel_second_kind);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::iJ<DataType, Dim, Frame>\nderiv_contracted_conformal_christoffel_second_kind(\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up,\n    const tnsr::Ijj<DataType, Dim, Frame>& conformal_christoffel_second_kind,\n    const tnsr::iJkk<DataType, Dim, Frame>&\n        d_conformal_christoffel_second_kind);\n/// @}\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/DerivLapse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/DerivLapse.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace Ccz4 {\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\nvoid grad_grad_lapse(\n    const gsl::not_null<tnsr::ij<DataType, Dim, Frame>*> result,\n    const Scalar<DataType>& lapse,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::i<DataType, Dim, Frame>& field_a, const TensorType& d_field_a) {\n  if constexpr (std::is_same_v<TensorType, tnsr::ij<DataType, Dim, Frame>>) {\n    // We keep this for loop specialization for faster speed when no symmetry\n    // of d_field_a is present.\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        result->get(i, j) = field_a.get(i) * field_a.get(j) +\n                            0.5 * (d_field_a.get(i, j) + d_field_a.get(j, i));\n        for (size_t k = 0; k < Dim; ++k) {\n          result->get(i, j) -=\n              christoffel_second_kind.get(k, i, j) * field_a.get(k);\n        }\n        result->get(i, j) *= get(lapse);\n      }\n    }\n  } else {\n    ::tenex::evaluate<ti::i, ti::j>(\n        result, lapse() * field_a(ti::i) * field_a(ti::j) -\n                    lapse() * christoffel_second_kind(ti::K, ti::i, ti::j) *\n                        field_a(ti::k) +\n                    lapse() * 0.5 *\n                        (d_field_a(ti::i, ti::j) + d_field_a(ti::j, ti::i)));\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\ntnsr::ij<DataType, Dim, Frame> grad_grad_lapse(\n    const Scalar<DataType>& lapse,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::i<DataType, Dim, Frame>& field_a, const TensorType& d_field_a) {\n  tnsr::ij<DataType, Dim, Frame> result{};\n  grad_grad_lapse(make_not_null(&result), lapse, christoffel_second_kind,\n                  field_a, d_field_a);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid divergence_lapse(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_metric,\n    const tnsr::ij<DataType, Dim, Frame>& grad_grad_lapse) {\n  set_number_of_grid_points(result, conformal_factor_squared);\n\n  get(*result) = 0.0;\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      get(*result) +=\n          inverse_conformal_metric.get(i, j) * grad_grad_lapse.get(i, j);\n    }\n  }\n  get(*result) *= get(conformal_factor_squared);\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> divergence_lapse(\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_metric,\n    const tnsr::ij<DataType, Dim, Frame>& grad_grad_lapse) {\n  Scalar<DataType> result{};\n  divergence_lapse(make_not_null(&result), conformal_factor_squared,\n                   inverse_conformal_metric, grad_grad_lapse);\n  return result;\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define TTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE_GRAD_GRAD_LAPSE(_, data)                                 \\\n  template void Ccz4::grad_grad_lapse(                                       \\\n      const gsl::not_null<tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          result,                                                            \\\n      const Scalar<DTYPE(data)>& lapse,                                      \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          christoffel_second_kind,                                           \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& field_a,           \\\n      const TTYPE(data) < DTYPE(data), DIM(data), FRAME(data) > &d_field_a); \\\n  template tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  Ccz4::grad_grad_lapse(                                                     \\\n      const Scalar<DTYPE(data)>& lapse,                                      \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          christoffel_second_kind,                                           \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& field_a,           \\\n      const TTYPE(data) < DTYPE(data), DIM(data), FRAME(data) > &d_field_a);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_GRAD_GRAD_LAPSE, (1, 2, 3),\n                        (Frame::Grid, Frame::Inertial), (double, DataVector),\n                        (tnsr::ii, tnsr::ij))\n\n#undef INSTANTIATE_GRAD_GRAD_LAPSE\n#undef TTYPE\n\n#define INSTANTIATE_DIV_LAPSE(_, data)                                       \\\n  template void Ccz4::divergence_lapse(                                      \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result,                      \\\n      const Scalar<DTYPE(data)>& conformal_factor_squared,                   \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_conformal_metric,                                          \\\n      const tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>& grad_grad_lapse); \\\n  template Scalar<DTYPE(data)> Ccz4::divergence_lapse(                       \\\n      const Scalar<DTYPE(data)>& conformal_factor_squared,                   \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_conformal_metric,                                          \\\n      const tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>& grad_grad_lapse);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_DIV_LAPSE, (1, 2, 3),\n                        (Frame::Grid, Frame::Inertial), (double, DataVector))\n\n#undef INSTANTIATE_GRAD_GRAD_LAPSE\n#undef DTYPE\n#undef FRAME\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/DerivLapse.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\n/// @{\n/*!\n * \\brief Computes the gradient of the gradient of the lapse.\n *\n * \\details Computes the gradient of the gradient as:\n * \\f{align}\n *     \\nabla_i \\nabla_j \\alpha &= \\alpha A_i A_j -\n *                 \\alpha \\Gamma^k{}_{ij} A_k + \\alpha \\partial_{(i} A_{j)}\n * \\f}\n * where \\f$\\alpha\\f$, \\f$\\Gamma^k{}_{ij}\\f$, \\f$A_i\\f$, and\n * \\f$\\partial_j A_i\\f$ are the lapse, spatial christoffel symbols of the second\n * kind, the CCZ4 auxiliary variable defined by `Ccz4::Tags::FieldA`, and its\n * spatial derivative, respectively.\n *\n * \\note In second-order Ccz4, we impose symmetry of index i and j\n * in \\f$ \\partial_i A_j=\\partial_i \\partial_j \\ln \\alpha \\f$,\n * because partial derivatives commute and to use\n * `second_partial_derivatives()`. \\f$ A_i \\f$ is evolved in\n * the first-order system so no such symmetry is imposed.\n */\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\nvoid grad_grad_lapse(\n    const gsl::not_null<tnsr::ij<DataType, Dim, Frame>*> result,\n    const Scalar<DataType>& lapse,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::i<DataType, Dim, Frame>& field_a, const TensorType& d_field_a);\n\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\ntnsr::ij<DataType, Dim, Frame> grad_grad_lapse(\n    const Scalar<DataType>& lapse,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::i<DataType, Dim, Frame>& field_a, const TensorType& d_field_a);\n\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the divergence of the lapse.\n *\n * \\details Computes the divergence as:\n * \\f{align}\n *     \\nabla^i \\nabla_i \\alpha &= \\phi^2 \\tilde{\\gamma}^{ij}\n *         (\\nabla_i \\nabla_j \\alpha)\n * \\f}\n * where \\f$\\phi\\f$, \\f$\\tilde{\\gamma}^{ij}\\f$, and\n * \\f$\\nabla_i \\nabla_j \\alpha\\f$ are the conformal factor, inverse conformal\n * spatial metric, and the gradient of the gradient of the lapse defined by\n * `Ccz4::Tags::ConformalFactor`, `Ccz4::Tags::InverseConformalMetric`, and\n * `Ccz4::Tags::GradGradLapse`, respectively.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid divergence_lapse(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_metric,\n    const tnsr::ij<DataType, Dim, Frame>& grad_grad_lapse);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> divergence_lapse(\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_metric,\n    const tnsr::ij<DataType, Dim, Frame>& grad_grad_lapse);\n/// @}\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/DerivZ4Constraint.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/DerivZ4Constraint.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid grad_spatial_z4_constraint(\n    const gsl::not_null<tnsr::ij<DataType, Dim, Frame>*> result,\n    const tnsr::i<DataType, Dim, Frame>& spatial_z4_constraint,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel,\n    const tnsr::iJ<DataType, Dim, Frame>&\n        d_gamma_hat_minus_contracted_conformal_christoffel) {\n  ::tenex::evaluate<ti::i, ti::j>(\n      result,\n      field_d(ti::i, ti::j, ti::l) *\n              gamma_hat_minus_contracted_conformal_christoffel(ti::L) +\n          0.5 * conformal_spatial_metric(ti::j, ti::l) *\n              d_gamma_hat_minus_contracted_conformal_christoffel(ti::i, ti::L) -\n          christoffel_second_kind(ti::L, ti::i, ti::j) *\n              spatial_z4_constraint(ti::l));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::ij<DataType, Dim, Frame> grad_spatial_z4_constraint(\n    const tnsr::i<DataType, Dim, Frame>& spatial_z4_constraint,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel,\n    const tnsr::iJ<DataType, Dim, Frame>&\n        d_gamma_hat_minus_contracted_conformal_christoffel) {\n  tnsr::ij<DataType, Dim, Frame> result{};\n  grad_spatial_z4_constraint(\n      make_not_null(&result), spatial_z4_constraint, conformal_spatial_metric,\n      christoffel_second_kind, field_d,\n      gamma_hat_minus_contracted_conformal_christoffel,\n      d_gamma_hat_minus_contracted_conformal_christoffel);\n  return result;\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void Ccz4::grad_spatial_z4_constraint(                         \\\n      const gsl::not_null<tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                         \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          spatial_z4_constraint,                                          \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          conformal_spatial_metric,                                       \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          christoffel_second_kind,                                        \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& field_d,      \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          gamma_hat_minus_contracted_conformal_christoffel,               \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          d_gamma_hat_minus_contracted_conformal_christoffel);            \\\n  template tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  Ccz4::grad_spatial_z4_constraint(                                       \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          spatial_z4_constraint,                                          \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          conformal_spatial_metric,                                       \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          christoffel_second_kind,                                        \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& field_d,      \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          gamma_hat_minus_contracted_conformal_christoffel,               \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          d_gamma_hat_minus_contracted_conformal_christoffel);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/DerivZ4Constraint.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\n/// @{\n/*!\n * \\brief Computes the gradient of the spatial part of the Z4 constraint\n *\n * \\details Computes the gradient as:\n *\n * \\f{align}\n *     \\nabla_i Z_j &=\n *         D_{ijl} \\left(\\hat{\\Gamma}^l - \\tilde{\\Gamma}^l\\right) +\n *         \\frac{1}{2} \\tilde{\\gamma}_{jl} \\left(\n *             \\partial_i \\hat{\\Gamma}^l - \\partial_i \\tilde{\\Gamma}^l\\right) -\n *         \\Gamma^l_{ij} Z_l\n * \\f}\n *\n * where \\f$Z_i\\f$ is the spatial Z4 constraint defined by\n * `Ccz4::Tags::SpatialZ4Constraint`, \\f$\\tilde{\\gamma}_{ij}\\f$ is the conformal\n * spatial metric defined by `Ccz4::Tags::ConformalMetric`, \\f$\\Gamma^k_{ij}\\f$\n * is the spatial Christoffel symbols of the second kind defined by\n * `Ccz4::Tags::ChristoffelSecondKind`, \\f$D_{ijk}\\f$ is the CCZ4 auxiliary\n * variable defined by `Ccz4::Tags::FieldD`,\n * \\f$\\left(\\hat{\\Gamma}^i - \\tilde{\\Gamma}^i\\right)\\f$ is the CCZ4 temporary\n * expression defined by\n * `Ccz4::Tags::GammaHatMinusContractedConformalChristoffel`, and\n * \\f$\\left(\\partial_i \\hat{\\Gamma}^j - \\partial_i \\tilde{\\Gamma}^j\\right)\\f$ is\n * its spatial derivative.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid grad_spatial_z4_constraint(\n    const gsl::not_null<tnsr::ij<DataType, Dim, Frame>*> result,\n    const tnsr::i<DataType, Dim, Frame>& spatial_z4_constraint,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel,\n    const tnsr::iJ<DataType, Dim, Frame>&\n        d_gamma_hat_minus_contracted_conformal_christoffel);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::ij<DataType, Dim, Frame> grad_spatial_z4_constraint(\n    const tnsr::i<DataType, Dim, Frame>& spatial_z4_constraint,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel,\n    const tnsr::iJ<DataType, Dim, Frame>&\n        d_gamma_hat_minus_contracted_conformal_christoffel);\n/// @}\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/ApplyFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/ApplyFilter.hpp\"\n\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Filter.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4::fd {\n\nvoid ApplyFilter::apply(\n    const gsl::not_null<typename System::variables_tag::type*> evolved_vars_ptr,\n    const Mesh<3>& subcell_mesh, const bool evolve_lapse_and_shift,\n    const double kreiss_oliger_epsilon,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data) {\n  constexpr size_t fd_order = 4;\n\n  if (kreiss_oliger_epsilon == 0.0) {\n    return;\n  } else if (kreiss_oliger_epsilon > 0.0 and kreiss_oliger_epsilon <= 1.0) {\n    typename System::variables_tag::type filtered_vars = *evolved_vars_ptr;\n\n    Ccz4::fd::ccz4_kreiss_oliger_filter(make_not_null(&filtered_vars),\n                                        *evolved_vars_ptr, ghost_data,\n                                        evolve_lapse_and_shift, subcell_mesh,\n                                        fd_order + 2, kreiss_oliger_epsilon);\n\n    *evolved_vars_ptr = filtered_vars;\n  } else {\n    ERROR(\"Kreiss-Oliger epsilon should be in the interval [0, 1]. Got \"\n          << kreiss_oliger_epsilon << \" instead.\");\n  }\n}\n\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/ApplyFilter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\n/*!\n * \\brief Apply the Kreiss-Oliger filter to the evolved variables\n *\n */\nstruct ApplyFilter : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<System::variables_tag>;\n  using argument_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>,\n                 Tags::EvolveLapseAndShift, Tags::KreissOligerEpsilon,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>;\n\n  static void apply(\n      gsl::not_null<typename System::variables_tag::type*>\n          evolved_vars_ptr,\n      const Mesh<3>& subcell_mesh, bool evolve_lapse_and_shift,\n      double kreiss_oliger_epsilon,\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data);\n};\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/BoundaryConditionGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\n/*!\n * \\brief Computes finite difference ghost data for external boundary\n * conditions.\n *\n * If the element is at the external boundary, computes FD ghost data with a\n * given boundary condition and stores it into neighbor data with {direction,\n * ElementId::external_boundary_id()} as the mortar_id key.\n *\n * \\note Subcell needs to be enabled for boundary elements. Otherwise this\n * function would be never called.\n *\n */\nstruct BoundaryConditionGhostData {\n  template <typename DbTagsList>\n  static void apply(gsl::not_null<db::DataBox<DbTagsList>*> box,\n                    const Element<3>& element,\n                    const Reconstructor& reconstructor);\n\n private:\n  template <typename FdBoundaryConditionHelper, typename DbTagsList,\n            typename... FdBoundaryConditionArgsTags>\n  // A helper function for calling fd_ghost() of BoundaryCondition subclasses\n  static void apply_subcell_boundary_condition_impl(\n      FdBoundaryConditionHelper& fd_boundary_condition_helper,\n      const gsl::not_null<db::DataBox<DbTagsList>*>& box,\n      tmpl::list<FdBoundaryConditionArgsTags...> /*unused*/) {\n    return fd_boundary_condition_helper(\n        db::get<FdBoundaryConditionArgsTags>(*box)...);\n  }\n};\n\ntemplate <typename DbTagsList>\nvoid BoundaryConditionGhostData::apply(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    const Element<3>& element, const Reconstructor& reconstructor) {\n  const auto& external_boundary_condition =\n      db::get<domain::Tags::ExternalBoundaryConditions<3>>(*box).at(\n          element.id().block_id());\n\n  // Check if the element is on the external boundary. If not, the caller is\n  // doing something wrong (e.g. trying to compute FD ghost data with boundary\n  // conditions at an element which is not on the external boundary).\n  ASSERT(not element.external_boundaries().empty(),\n         \"The element (ID : \" << element.id()\n                              << \") is not on external boundaries\");\n\n  const Mesh<3> subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<3>>(*box);\n\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n\n  // Tags and tags list for FD reconstruction\n  using variables_for_reconstruction = typename System::variables_tag_list;\n\n  size_t num_evolved_tensor_components = 0;\n  tmpl::for_each<variables_for_reconstruction>(\n        [&num_evolved_tensor_components]<typename Tag>\n            (const tmpl::type_<Tag> /*meta*/) {\n            num_evolved_tensor_components += Tag::type::size();\n        });\n\n  for (const auto& direction : element.external_boundaries()) {\n    const auto& boundary_condition_at_direction =\n        *external_boundary_condition.at(direction);\n\n    const size_t num_face_pts{\n        subcell_mesh.extents().slice_away(direction.dimension()).product()};\n\n    // Allocate a vector to store the computed FD ghost data and assign a\n    // non-owning Variables on it.\n    const size_t evolved_vars_size =\n        num_evolved_tensor_components * ghost_zone_size * num_face_pts;\n    const size_t fluxes_size = 0;  // CCZ4 doesn't use flux variables\n\n    auto& all_ghost_data = db::get_mutable_reference<\n        evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box);\n    // Put the computed ghost data into neighbor data with {direction,\n    // ElementId::external_boundary_id()} as the mortar_id key\n    const DirectionalId<3> mortar_id{direction,\n                                     ElementId<3>::external_boundary_id()};\n\n    all_ghost_data[mortar_id] = evolution::dg::subcell::GhostData{1};\n    DataVector& boundary_ghost_data =\n        all_ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    boundary_ghost_data.destructive_resize(evolved_vars_size + fluxes_size);\n    Variables<variables_for_reconstruction> ghost_data_vars{\n        boundary_ghost_data.data(), evolved_vars_size};\n\n    // We don't need to care about boundary ghost data when using the periodic\n    // condition, so exclude it from the type list\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>::factory_creation::factory_classes;\n    using derived_boundary_conditions_for_subcell = tmpl::remove_if<\n        tmpl::at<factory_classes, BoundaryConditions::BoundaryCondition>,\n        tmpl::or_<\n            std::is_base_of<domain::BoundaryConditions::MarkAsPeriodic,\n                            tmpl::_1>,\n            std::is_base_of<domain::BoundaryConditions::MarkAsNone, tmpl::_1>>>;\n\n    // Now apply subcell boundary conditions\n    call_with_dynamic_type<void, derived_boundary_conditions_for_subcell>(\n        &boundary_condition_at_direction,\n        [&box, &direction, &ghost_data_vars](const auto* boundary_condition) {\n          using BoundaryCondition = std::decay_t<decltype(*boundary_condition)>;\n          using bcondition_interior_evolved_vars_tags =\n              typename BoundaryCondition::fd_interior_evolved_variables_tags;\n          using bcondition_interior_temporary_tags =\n              typename BoundaryCondition::fd_interior_temporary_tags;\n          using bcondition_interior_primitive_vars_tags =\n              typename BoundaryCondition::fd_interior_primitive_variables_tags;\n          using bcondition_gridless_tags =\n              typename BoundaryCondition::fd_gridless_tags;\n\n          using bcondition_interior_tags =\n              tmpl::append<bcondition_interior_evolved_vars_tags,\n                           bcondition_interior_temporary_tags,\n                           bcondition_interior_primitive_vars_tags,\n                           bcondition_gridless_tags>;\n\n          if constexpr (BoundaryCondition::bc_type ==\n                        evolution::BoundaryConditions::Type::Ghost) {\n            const auto apply_fd_ghost =\n                [&boundary_condition, &direction,\n                 &ghost_data_vars](const auto&... boundary_ghost_data_args) {\n                  (*boundary_condition)\n                      .fd_ghost(\n                          make_not_null(\n                              &get<\n                                  ::Ccz4::Tags::ConformalMetric<DataVector, 3>>(\n                                  ghost_data_vars)),\n                          make_not_null(&get<gr::Tags::Lapse<DataVector>>(\n                              ghost_data_vars)),\n                          make_not_null(&get<gr::Tags::Shift<DataVector, 3>>(\n                              ghost_data_vars)),\n                          make_not_null(\n                              &get<::Ccz4::Tags::ConformalFactor<DataVector>>(\n                                  ghost_data_vars)),\n                          make_not_null(\n                              &get<::Ccz4::Tags::ATilde<DataVector, 3>>(\n                                  ghost_data_vars)),\n                          make_not_null(&get<gr::Tags::TraceExtrinsicCurvature<\n                                            DataVector>>(ghost_data_vars)),\n                          make_not_null(&get<::Ccz4::Tags::Theta<DataVector>>(\n                              ghost_data_vars)),\n                          make_not_null(\n                              &get<::Ccz4::Tags::GammaHat<DataVector, 3>>(\n                                  ghost_data_vars)),\n                          make_not_null(\n                              &get<\n                                  ::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(\n                                  ghost_data_vars)),\n                          direction, boundary_ghost_data_args...);\n                };\n            apply_subcell_boundary_condition_impl(apply_fd_ghost, box,\n                                                  bcondition_interior_tags{});\n          } else {\n            ERROR(\"Unsupported boundary condition \"\n                  << pretty_type::short_name<BoundaryCondition>()\n                  << \" when using finite-difference\");\n          }\n        });\n  }\n}\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SoCcz4)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ApplyFilter.cpp\n  Derivatives.cpp\n  DummyReconstructor.cpp\n  EnforceConstrainedEvolution.cpp\n  Filter.cpp\n  GhostData.cpp\n  Reconstructor.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApplyFilter.hpp\n  BoundaryConditionGhostData.hpp\n  Derivatives.hpp\n  DummyReconstructor.hpp\n  EnforceConstrainedEvolution.hpp\n  Filter.hpp\n  GhostData.hpp\n  Reconstructor.hpp\n  SoTimeDerivative.hpp\n  System.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Ccz4\n  DataStructures\n  DomainStructure\n  ErrorHandling\n  DgSubcell\n  FiniteDifference\n  GeneralRelativity\n  Options\n  Spectral\n  Utilities\n  INTERFACE\n  )\n\nadd_subdirectory(../BoundaryConditions ${CMAKE_CURRENT_BINARY_DIR}/BoundaryConditions)\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/Derivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Derivatives.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\nvoid spacetime_derivatives(\n    const gsl::not_null<Variables<\n        db::wrap_tags_in<::Tags::deriv, typename System::gradients_tags,\n                         tmpl::size_t<3>, Frame::Inertial>>*>\n        result,\n    const Variables<typename System::variables_tag::tags_list>&\n        volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const size_t& deriv_order, const Mesh<3>& volume_mesh,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        cell_centered_logical_to_inertial_inv_jacobian) {\n  using gradients_tags = typename System::gradients_tags;\n  if (UNLIKELY(result->number_of_grid_points() !=\n               volume_evolved_variables.number_of_grid_points())) {\n    result->initialize(volume_evolved_variables.number_of_grid_points());\n  }\n\n  using first_Ccz4_tag = tmpl::front<Tags::spacetime_reconstruction_tags>;\n  constexpr size_t number_of_Ccz4_components =\n      Variables<Tags::spacetime_reconstruction_tags>::\n          number_of_independent_components;\n\n  DirectionMap<3, gsl::span<const double>> ghost_cell_vars{};\n  for (const auto& [directional_element_id, ghost_data] : all_ghost_data) {\n    using NeighborVariables =\n        Variables<Tags::spacetime_reconstruction_tags>;\n    const DataVector& neighbor_data =\n        ghost_data.neighbor_ghost_data_for_reconstruction();\n    const size_t neighbor_number_of_points =\n        neighbor_data.size() /\n        NeighborVariables::number_of_independent_components;\n    ASSERT(\n        neighbor_data.size() %\n                NeighborVariables::number_of_independent_components ==\n            0,\n        \"Amount of reconstruction data sent (\"\n            << neighbor_data.size() << \") from \" << directional_element_id\n            << \" is not a multiple of the number of reconstruction variables \"\n            << NeighborVariables::number_of_independent_components);\n    // Use a Variables view to get offset into spacetime variables\n    // without having to do pointer math.\n    const NeighborVariables\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        view{const_cast<double*>(neighbor_data.data()),\n             neighbor_number_of_points *\n                 NeighborVariables::number_of_independent_components};\n    ghost_cell_vars.insert(std::pair{\n        directional_element_id.direction(),\n        gsl::make_span(get<first_Ccz4_tag>(view)[0].data(),\n                       number_of_Ccz4_components * neighbor_number_of_points)});\n  }\n\n  const auto volume_Ccz4_vars =\n      gsl::make_span(get<first_Ccz4_tag>(volume_evolved_variables)[0].data(),\n                     number_of_Ccz4_components *\n                         volume_evolved_variables.number_of_grid_points());\n\n  ::fd::partial_derivatives<gradients_tags>(\n      result, volume_Ccz4_vars, ghost_cell_vars, volume_mesh,\n      number_of_Ccz4_components, deriv_order,\n      cell_centered_logical_to_inertial_inv_jacobian);\n}\n\nvoid second_spacetime_derivatives(\n    const gsl::not_null<\n        Variables<db::wrap_tags_in<::Tags::second_deriv, System::gradients_tags,\n                                   tmpl::size_t<3>, Frame::Inertial>>*>\n        result,\n    const Variables<typename System::variables_tag::tags_list>&\n        volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const size_t& deriv_order, const Mesh<3>& volume_mesh,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        cell_centered_logical_to_inertial_inv_jacobian,\n    const InverseHessian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>&\n        cell_centered_logical_to_inertial_inv_hessian) {\n  ASSERT(deriv_order == 4,\n         \"Only fourth order second partial derivatives have been implemented.\");\n\n  using gradients_tags = typename System::gradients_tags;\n  if (UNLIKELY(result->number_of_grid_points() !=\n               volume_evolved_variables.number_of_grid_points())) {\n    result->initialize(volume_evolved_variables.number_of_grid_points());\n  }\n\n  using first_Ccz4_tag = tmpl::front<Tags::spacetime_reconstruction_tags>;\n  constexpr size_t number_of_Ccz4_components =\n      Variables<Tags::spacetime_reconstruction_tags>::\n          number_of_independent_components;\n\n  DirectionMap<3, gsl::span<const double>> ghost_cell_vars{};\n  for (const auto& [directional_element_id, ghost_data] : all_ghost_data) {\n    using NeighborVariables =\n        Variables<Tags::spacetime_reconstruction_tags>;\n    const DataVector& neighbor_data =\n        ghost_data.neighbor_ghost_data_for_reconstruction();\n    const size_t neighbor_number_of_points =\n        neighbor_data.size() /\n        NeighborVariables::number_of_independent_components;\n    ASSERT(\n        neighbor_data.size() %\n                NeighborVariables::number_of_independent_components ==\n            0,\n        \"Amount of reconstruction data sent (\"\n            << neighbor_data.size() << \") from \" << directional_element_id\n            << \" is not a multiple of the number of reconstruction variables \"\n            << NeighborVariables::number_of_independent_components);\n    // Use a Variables view to get offset into spacetime variables\n    // without having to do pointer math.\n    const NeighborVariables\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        view{const_cast<double*>(neighbor_data.data()),\n             neighbor_number_of_points *\n                 NeighborVariables::number_of_independent_components};\n    ghost_cell_vars.insert(std::pair{\n        directional_element_id.direction(),\n        gsl::make_span(get<first_Ccz4_tag>(view)[0].data(),\n                       number_of_Ccz4_components * neighbor_number_of_points)});\n  }\n\n  const auto volume_Ccz4_vars =\n      gsl::make_span(get<first_Ccz4_tag>(volume_evolved_variables)[0].data(),\n                     number_of_Ccz4_components *\n                         volume_evolved_variables.number_of_grid_points());\n\n  ::fd::second_partial_derivatives<gradients_tags>(\n      result, volume_Ccz4_vars, ghost_cell_vars, volume_mesh,\n      number_of_Ccz4_components, deriv_order,\n      cell_centered_logical_to_inertial_inv_jacobian,\n      cell_centered_logical_to_inertial_inv_hessian);\n}\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/Derivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/System.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace Ccz4::fd {\n/*!\n * \\brief Compute first partial derivatives of all the evolved variables in the\n * second order Ccz4 system.\n *\n * The derivatives are computed using FD of order deriv_order using stencil\n * the same as `fd::partial_derivatives()`.\n *\n */\nvoid spacetime_derivatives(\n    gsl::not_null<Variables<\n        db::wrap_tags_in<::Tags::deriv, typename System::gradients_tags,\n                         tmpl::size_t<3>, Frame::Inertial>>*>\n        result,\n    const Variables<typename System::variables_tag::tags_list>&\n        volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const size_t& deriv_order, const Mesh<3>& volume_mesh,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        cell_centered_logical_to_inertial_inv_jacobian);\n\n/*!\n * \\brief Compute second partial derivatives of all the evolved variables in the\n * second order Ccz4 system\n * \\details\n * The derivatives are computed using FD of order deriv_order.\n *\n * \\note Only 3D 4-th order second derivatives are implemented\n * in this stencil\n */\nvoid second_spacetime_derivatives(\n    gsl::not_null<\n        Variables<db::wrap_tags_in<::Tags::second_deriv, System::gradients_tags,\n                                   tmpl::size_t<3>, Frame::Inertial>>*>\n        result,\n    const Variables<typename System::variables_tag::tags_list>&\n        volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const size_t& deriv_order, const Mesh<3>& volume_mesh,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        cell_centered_logical_to_inertial_inv_jacobian,\n    const InverseHessian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>&\n        cell_centered_logical_to_inertial_inv_hessian);\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/DummyReconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/DummyReconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Reconstructor.hpp\"\n\nnamespace Ccz4::fd {\nDummyReconstructor::DummyReconstructor(CkMigrateMessage* const msg)\n    : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor> DummyReconstructor::get_clone() const {\n  return std::make_unique<DummyReconstructor>(*this);\n}\n\nvoid DummyReconstructor::pup(PUP::er& p) { Reconstructor::pup(p); }\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DummyReconstructor::my_PUP_ID = 0;\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/DummyReconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Reconstructor.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace Ccz4::fd {\n/*!\n * \\brief Dummy reconstructor just to return the ghost zone size.\n *\n * This class is needed to use the SetInterpolators action in\n * dg-subcell infrastructure.\n */\nclass DummyReconstructor : public Reconstructor {\n public:\n  using options = tmpl::list<>;\n\n  static constexpr Options::String help{\n      \"Dummy reconstructor that allows using the subcell infrastructure.\"};\n\n  DummyReconstructor() = default;\n  DummyReconstructor(DummyReconstructor&&) = default;\n  DummyReconstructor& operator=(DummyReconstructor&&) = default;\n  DummyReconstructor(const DummyReconstructor&) = default;\n  DummyReconstructor& operator=(const DummyReconstructor&) = default;\n  ~DummyReconstructor() override = default;\n\n  explicit DummyReconstructor(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor, DummyReconstructor);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  void pup(PUP::er& p) override;\n\n  // 3 ghost pts needed for 6th order KO filter\n  size_t ghost_zone_size() const override { return 3; }\n};\n\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/EnforceConstrainedEvolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/EnforceConstrainedEvolution.hpp\"\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Ccz4/TempTags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\n\nvoid EnforceConstrainedEvolution::apply(\n    const gsl::not_null<tnsr::ii<DataVector, dim>*> conformal_spatial_metric,\n    const gsl::not_null<tnsr::ii<DataVector, dim>*> a_tilde,\n    const bool constrained_evolution) {\n  if (constrained_evolution) {\n    // Allocate shared storage for temporaries in one block using existing tags\n    Variables<tmpl::list<Ccz4::Tags::DetConformalSpatialMetric<DataVector>,\n                         Ccz4::Tags::TraceATilde<DataVector>,\n                         Ccz4::Tags::InverseConformalMetric<DataVector, dim>>>\n        temporaries((conformal_spatial_metric->get(0, 0)).size());\n\n    auto& det_conformal_spatial_metric =\n        get<Ccz4::Tags::DetConformalSpatialMetric<DataVector>>(temporaries);\n    auto& trace_a_tilde = get<Ccz4::Tags::TraceATilde<DataVector>>(temporaries);\n    auto& inv_conformal_spatial_metric =\n        get<Ccz4::Tags::InverseConformalMetric<DataVector, dim>>(temporaries);\n\n    determinant(make_not_null(&det_conformal_spatial_metric),\n                *conformal_spatial_metric);\n    ASSERT(min(get(det_conformal_spatial_metric)) > 0.0,\n           \"The determinant of the conformal spatial metric is non-positive: \"\n               << get(det_conformal_spatial_metric));\n    get(det_conformal_spatial_metric) =\n        pow(get(det_conformal_spatial_metric), -1.0 / 3.0);\n    ::tenex::update<ti::i, ti::j>(\n        conformal_spatial_metric,\n        det_conformal_spatial_metric() *\n            (*conformal_spatial_metric)(ti::i, ti::j));\n\n    determinant_and_inverse(make_not_null(&det_conformal_spatial_metric),\n                            make_not_null(&inv_conformal_spatial_metric),\n                            *conformal_spatial_metric);\n    ::tenex::evaluate(make_not_null(&trace_a_tilde),\n                      (inv_conformal_spatial_metric)(ti::I, ti::J) *\n                          (*a_tilde)(ti::i, ti::j));\n    ::tenex::update<ti::i, ti::j>(\n        a_tilde,\n        (*a_tilde)(ti::i, ti::j) -\n            trace_a_tilde() * (*conformal_spatial_metric)(ti::i, ti::j) / 3.);\n  }\n}\n\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/EnforceConstrainedEvolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\n/*!\n * \\brief Mutator to enforce constrained evolution after every time step\n *\n * Togglable option in input file via option tag defined in Ccz4::fd::Tags.\n * If constrained_evolution is true, enforce\n * the unit determinant constraint on the conformal spatial metric\n * and traceless constraint on the ATilde\n */\nstruct EnforceConstrainedEvolution : tt::ConformsTo<db::protocols::Mutator> {\n  static constexpr size_t dim = System::volume_dim;\n  using return_tags = tmpl::list<::Ccz4::Tags::ConformalMetric<DataVector, dim>,\n                                 ::Ccz4::Tags::ATilde<DataVector, dim>>;\n  using argument_tags = tmpl::list<::Ccz4::fd::Tags::ConstrainedEvolution>;\n\n  static void apply(\n      gsl::not_null<tnsr::ii<DataVector, dim>*> conformal_spatial_metric,\n      gsl::not_null<tnsr::ii<DataVector, dim>*> a_tilde,\n      bool constrained_evolution);\n};\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/Filter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Filter.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Filter.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\nvoid ccz4_kreiss_oliger_filter(\n    const gsl::not_null<Variables<System::variables_tag_list>*> result,\n    const Variables<System::variables_tag_list>& volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const bool evolve_lapse_and_shift, const Mesh<3>& volume_mesh,\n    const size_t order, const double epsilon) {\n  // The result is assumed to be initialized to the volume_evolved_variables\n  ASSERT(result->number_of_grid_points() ==\n             volume_evolved_variables.number_of_grid_points(),\n         \"The result and volume_evolved_variables must have the same number of \"\n         \"grid points. Found \"\n             << result->number_of_grid_points() << \" and \"\n             << volume_evolved_variables.number_of_grid_points());\n\n  using first_ccz4_tag = tmpl::front<System::variables_tag_list>;\n  const size_t number_of_ccz4_components =\n      evolve_lapse_and_shift\n          ? Variables<\n                System::variables_tag_list>::number_of_independent_components\n          : Variables<\n                System::variables_tag_list>::number_of_independent_components -\n                7;\n\n  DirectionMap<3, gsl::span<const double>> ghost_cell_vars{};\n  for (const auto& [directional_element_id, ghost_data] : all_ghost_data) {\n    using NeighborVariables = Variables<System::variables_tag_list>;\n    const DataVector& neighbor_data =\n        ghost_data.neighbor_ghost_data_for_reconstruction();\n    const size_t neighbor_number_of_points =\n        neighbor_data.size() /\n        NeighborVariables::number_of_independent_components;\n    ASSERT(\n        neighbor_data.size() %\n                NeighborVariables::number_of_independent_components ==\n            0,\n        \"Amount of reconstruction data sent (\"\n            << neighbor_data.size() << \") from \" << directional_element_id\n            << \" is not a multiple of the number of reconstruction variables \"\n            << NeighborVariables::number_of_independent_components);\n    // Use a Variables view to get offset into spacetime variables\n    // without having to do pointer math.\n    const NeighborVariables\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        view{const_cast<double*>(neighbor_data.data()),\n             neighbor_number_of_points *\n                 NeighborVariables::number_of_independent_components};\n    ghost_cell_vars.insert(std::pair{\n        directional_element_id.direction(),\n        gsl::make_span(get<first_ccz4_tag>(view)[0].data(),\n                       number_of_ccz4_components * neighbor_number_of_points)});\n  }\n\n  const auto volume_ccz4_vars =\n      gsl::make_span(get<first_ccz4_tag>(volume_evolved_variables)[0].data(),\n                     number_of_ccz4_components *\n                         volume_evolved_variables.number_of_grid_points());\n\n  auto filtered_ccz4_vars =\n      gsl::make_span(get<first_ccz4_tag>(*result)[0].data(),\n                     number_of_ccz4_components *\n                         volume_evolved_variables.number_of_grid_points());\n  ::fd::kreiss_oliger_filter(make_not_null(&filtered_ccz4_vars),\n                             volume_ccz4_vars, ghost_cell_vars, volume_mesh,\n                             number_of_ccz4_components, order, epsilon);\n}\n\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/Filter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace Ccz4::fd {\n/*!\n * \\brief Apply a Kreiss-Oliger filter to System::variables_tag_list\n */\nvoid ccz4_kreiss_oliger_filter(\n    gsl::not_null<Variables<System::variables_tag_list>*> result,\n    const Variables<System::variables_tag_list>& volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    bool evolve_lapse_and_shift, const Mesh<3>& volume_mesh, size_t order,\n    double epsilon);\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/GhostData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\nDataVector GhostVariables::apply(\n    const Variables<Ccz4::fd::System::variables_tag_list>& evolved_vars,\n    const size_t rdmp_size) {\n  DataVector buffer{evolved_vars.number_of_grid_points() *\n                        Variables<Ccz4::fd::System::variables_tag_list>::\n                            number_of_independent_components +\n                    rdmp_size};\n  Variables<Ccz4::fd::System::variables_tag_list> vars_to_reconstruct(\n      buffer.data(), buffer.size() - rdmp_size);\n\n  get<Tags::ConformalMetric<DataVector, 3>>(vars_to_reconstruct) =\n      get<Tags::ConformalMetric<DataVector, 3>>(evolved_vars);\n  get<gr::Tags::Lapse<DataVector>>(vars_to_reconstruct) =\n      get<gr::Tags::Lapse<DataVector>>(evolved_vars);\n  get<gr::Tags::Shift<DataVector, 3>>(vars_to_reconstruct) =\n      get<gr::Tags::Shift<DataVector, 3>>(evolved_vars);\n  get<Tags::ConformalFactor<DataVector>>(vars_to_reconstruct) =\n      get<Tags::ConformalFactor<DataVector>>(evolved_vars);\n  get<Tags::ATilde<DataVector, 3>>(vars_to_reconstruct) =\n      get<Tags::ATilde<DataVector, 3>>(evolved_vars);\n  get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(vars_to_reconstruct) =\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars);\n  get<Tags::Theta<DataVector>>(vars_to_reconstruct) =\n      get<Tags::Theta<DataVector>>(evolved_vars);\n  get<Tags::GammaHat<DataVector, 3>>(vars_to_reconstruct) =\n      get<Tags::GammaHat<DataVector, 3>>(evolved_vars);\n  get<Tags::AuxiliaryShiftB<DataVector, 3>>(vars_to_reconstruct) =\n      get<Tags::AuxiliaryShiftB<DataVector, 3>>(evolved_vars);\n\n  return buffer;\n}\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/GhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename T>\nclass Variables;\n/// \\endcond\n\nnamespace Ccz4::fd {\n/*!\n * \\brief Get the Ccz4 evolution variables for ghost\n *\n * This mutator is passed to\n * `evolution::dg::subcell::Actions::SendDataForReconstruction`.\n */\nclass GhostVariables {\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::Variables<Ccz4::fd::System::variables_tag_list>>;\n\n  static DataVector apply(\n      const Variables<Ccz4::fd::System::variables_tag_list>& evolved_vars,\n      size_t rdmp_size);\n};\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/Reconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Reconstructor.hpp\"\n\n#include <pup.h>\n\nnamespace Ccz4::fd {\nReconstructor::Reconstructor(CkMigrateMessage* const msg) : PUP::able(msg) {}\n\nvoid Reconstructor::pup(PUP::er& p) { PUP::able::pup(p); }\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/Reconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\n/// \\cond\nclass DummyReconstructor;\n/// \\endcond\n\n/*!\n * \\brief The base class from which all reconstruction schemes must inherit\n */\nclass Reconstructor : public PUP::able {\n public:\n  Reconstructor() = default;\n  Reconstructor(const Reconstructor&) = default;\n  Reconstructor& operator=(const Reconstructor&) = default;\n  Reconstructor(Reconstructor&&) = default;\n  Reconstructor& operator=(Reconstructor&&) = default;\n  ~Reconstructor() override = default;\n\n  /// \\cond\n  explicit Reconstructor(CkMigrateMessage* msg);\n  WRAPPED_PUPable_abstract(Reconstructor);  // NOLINT\n  /// \\endcond\n\n  using creatable_classes = tmpl::list<DummyReconstructor>;\n\n  virtual std::unique_ptr<Reconstructor> get_clone() const = 0;\n\n  virtual size_t ghost_zone_size() const = 0;\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/SoTimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivChristoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivLapse.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivZ4Constraint.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/BoundaryConditionGhostData.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Derivatives.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/Ricci.hpp\"\n#include \"Evolution/Systems/Ccz4/RicciScalarPlusDivergenceZ4Constraint.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/TagsDeclarations.hpp\"\n#include \"Evolution/Systems/Ccz4/TempTags.hpp\"\n#include \"Evolution/Systems/Ccz4/TimeDerivative.hpp\"\n#include \"Evolution/Systems/Ccz4/Z4Constraint.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\brief The namespace for evolving the second-order Ccz4 system.\n * Spatial derivatives are computed using 4-th order finite\n * differencing. Currently this system only works in 3D.\n */\nnamespace Ccz4::fd {\nconst size_t Dim = 3;\n\nnamespace detail {\n// Calculate the time derivative of the evolved variables for the second-order\n// Ccz4 system. There is quite some overlap between this apply() funcion\n// and the apply() function in the first-order Ccz4 system. However,\n// it is not straightforward to directly reuse the first-order\n// apply() function as the function signatures differ significantly.\ntemplate <size_t Dim>\nstatic void apply(\n    // LHS time derivatives of evolved variables: eq 4(a) - 4(i)\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*> dt_conformal_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> dt_lapse,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_shift,\n    const gsl::not_null<Scalar<DataVector>*> dt_conformal_factor,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*> dt_a_tilde,\n    const gsl::not_null<Scalar<DataVector>*> dt_trace_extrinsic_curvature,\n    const gsl::not_null<Scalar<DataVector>*> dt_theta,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_gamma_hat,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_b,\n\n    // quantities we need for computing eq 4, 13-27\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_squared,\n    const gsl::not_null<Scalar<DataVector>*> det_conformal_spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, Dim>*>\n        inv_conformal_spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, Dim>*> inv_spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, Dim>*> inv_a_tilde,\n    // temporary expressions\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*> a_tilde_times_field_b,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde,\n    const gsl::not_null<Scalar<DataVector>*> contracted_field_b,\n    const gsl::not_null<tnsr::ijK<DataVector, Dim>*> symmetrized_d_field_b,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*>\n        contracted_symmetrized_d_field_b,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*> field_d_up_times_a_tilde,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        contracted_field_d_up,  // temp for eq 18 -20\n    const gsl::not_null<Scalar<DataVector>*>\n        half_conformal_factor_squared,  // temp for eq 25\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n        conformal_metric_times_field_b,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        conformal_metric_times_trace_a_tilde,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*>\n        inv_conformal_metric_times_d_a_tilde,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        gamma_hat_minus_contracted_conformal_christoffel,\n    const gsl::not_null<tnsr::iJ<DataVector, Dim>*>\n        d_gamma_hat_minus_contracted_conformal_christoffel,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*>\n        contracted_christoffel_second_kind,  // temp for eq 18 -20\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n        contracted_d_conformal_christoffel_difference,  // temp for eq 18 -20\n    const gsl::not_null<Scalar<DataVector>*> k_minus_2_theta_c,\n    const gsl::not_null<Scalar<DataVector>*> k_minus_k0_minus_2_theta_c,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*> lapse_times_a_tilde,\n    const gsl::not_null<tnsr::ijj<DataVector, Dim>*> lapse_times_d_a_tilde,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*> lapse_times_field_a,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        lapse_times_conformal_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> lapse_times_slicing_condition,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_ricci_scalar_plus_divergence_z4_constraint,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> shift_times_deriv_gamma_hat,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        inv_tau_times_conformal_metric,\n    // expressions and identities needed for evolution equations: eq 13 - 27\n    const gsl::not_null<Scalar<DataVector>*> trace_a_tilde,       // eq 13\n    const gsl::not_null<tnsr::iJJ<DataVector, Dim>*> field_d_up,  // eq 14\n    const gsl::not_null<tnsr::Ijj<DataVector, Dim>*>\n        conformal_christoffel_second_kind,  // eq 15\n    const gsl::not_null<tnsr::iJkk<DataVector, Dim>*>\n        d_conformal_christoffel_second_kind,  // eq 16\n    const gsl::not_null<tnsr::Ijj<DataVector, Dim>*>\n        christoffel_second_kind,  // eq 17\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        spatial_ricci_tensor,  // eq 18 - 20\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*> grad_grad_lapse,  // eq 21\n    const gsl::not_null<Scalar<DataVector>*> divergence_lapse,        // eq 22\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        contracted_conformal_christoffel_second_kind,  // eq 23\n    const gsl::not_null<tnsr::iJ<DataVector, Dim>*>\n        d_contracted_conformal_christoffel_second_kind,  // eq 24\n    const gsl::not_null<tnsr::i<DataVector, Dim>*>\n        spatial_z4_constraint,  // eq 25\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        upper_spatial_z4_constraint,  // eq 25\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n        grad_spatial_z4_constraint,  // eq 26\n    const gsl::not_null<Scalar<DataVector>*>\n        ricci_scalar_plus_divergence_z4_constraint,  // eq 27\n\n    // fixed params for SO-CCZ4\n    // c = 1.0, cleaning_speed = 1.0\n    // one_over_relaxation_time = 0.0\n    const double c, const double cleaning_speed,  // e in the paper\n    const double one_over_relaxation_time,        // \\tau^{-1}\n\n    // free params for SO-CCZ4\n    const Scalar<DataVector>& eta, const double f, const double kappa_1,\n    const double kappa_2, const double kappa_3, const Scalar<DataVector>& k_0,\n\n    // evolved variables\n    const tnsr::ii<DataVector, Dim>& conformal_spatial_metric,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, Dim>& shift,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::ii<DataVector, Dim>& a_tilde,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>& theta, const tnsr::I<DataVector, Dim>& gamma_hat,\n    const tnsr::I<DataVector, Dim>& b,\n\n    // auxilliary fields and their derivatives\n    const tnsr::i<DataVector, Dim>& field_a,\n    const tnsr::iJ<DataVector, Dim>& field_b,\n    const tnsr::ijj<DataVector, Dim>& field_d,\n    const tnsr::i<DataVector, Dim>& field_p,\n    const tnsr::ii<DataVector, Dim>& d_field_a,\n    const tnsr::iiJ<DataVector, Dim>& d_field_b,\n    const tnsr::iijj<DataVector, Dim>& d_field_d,\n    const tnsr::ii<DataVector, Dim>& d_field_p,\n\n    // spatial derivatives of other evolved variables\n    const tnsr::ijj<DataVector, Dim>& d_a_tilde,\n    const tnsr::i<DataVector, Dim>& d_trace_extrinsic_curvature,\n    const tnsr::i<DataVector, Dim>& d_theta,\n    const tnsr::iJ<DataVector, Dim>& d_gamma_hat,\n    const tnsr::iJ<DataVector, Dim>& d_b, const bool shifting_shift,\n    const bool evolve_lapse_and_shift) {\n  constexpr double one_third = 1.0 / 3.0;\n  // quantities we need for computing eq 4, 13 - 27\n\n  determinant_and_inverse(det_conformal_spatial_metric,\n                          inv_conformal_spatial_metric,\n                          conformal_spatial_metric);\n\n  get(*conformal_factor_squared) = square(get(conformal_factor));\n\n  ::tenex::evaluate<ti::I, ti::J>(\n      inv_spatial_metric, (*conformal_factor_squared)() *\n                              (*inv_conformal_spatial_metric)(ti::I, ti::J));\n\n  ::tenex::evaluate<ti::I, ti::J>(\n      inv_a_tilde, a_tilde(ti::k, ti::l) *\n                       (*inv_conformal_spatial_metric)(ti::I, ti::K) *\n                       (*inv_conformal_spatial_metric)(ti::J, ti::L));\n\n  ASSERT(min(get(lapse)) > 0.0,\n         \"The lapse must be positive when using 1+log slicing but is: \"\n             << get(lapse));\n\n  // slicing_condition and d_slicing_condition is not used in SO-CCZ4\n  // \\alpha g(\\alpha)  == 2\n  *lapse_times_slicing_condition =\n      make_with_value<Scalar<DataVector>>(lapse, 2.0);\n\n  // expressions and identities needed for evolution equations: eq 13 - 27\n\n  // eq 13\n  ::tenex::evaluate(\n      trace_a_tilde,\n      (*inv_conformal_spatial_metric)(ti::I, ti::J) * a_tilde(ti::i, ti::j));\n\n  // from eq 14: field_d_up is the D_k^{ij} tensor\n  ::tenex::evaluate<ti::k, ti::I, ti::J>(\n      field_d_up, (*inv_conformal_spatial_metric)(ti::I, ti::N) *\n                      (*inv_conformal_spatial_metric)(ti::M, ti::J) *\n                      field_d(ti::k, ti::n, ti::m));\n\n  // eq 15\n  ::Ccz4::conformal_christoffel_second_kind(conformal_christoffel_second_kind,\n                                            *inv_conformal_spatial_metric,\n                                            field_d);\n\n  // eq 16\n  ::Ccz4::deriv_conformal_christoffel_second_kind(\n      d_conformal_christoffel_second_kind, *inv_conformal_spatial_metric,\n      field_d, d_field_d, *field_d_up);\n\n  // eq 17\n  ::Ccz4::christoffel_second_kind(christoffel_second_kind,\n                                  conformal_spatial_metric,\n                                  *inv_conformal_spatial_metric, field_p,\n                                  *conformal_christoffel_second_kind);\n\n  // temporary expressions needed for eq 18 - 20\n  ::tenex::evaluate<ti::l>(contracted_christoffel_second_kind,\n                           (*christoffel_second_kind)(ti::M, ti::l, ti::m));\n\n  // comment for the future: we should probably ensure the traces are taken\n  // before computing the differences as the off-diagonal terms are not\n  // needed\n  ::tenex::evaluate<ti::i, ti::j>(\n      contracted_d_conformal_christoffel_difference,\n      (*d_conformal_christoffel_second_kind)(ti::m, ti::M, ti::i, ti::j) -\n          (*d_conformal_christoffel_second_kind)(ti::j, ti::M, ti::i, ti::m));\n\n  ::tenex::evaluate<ti::L>(contracted_field_d_up,\n                           (*field_d_up)(ti::m, ti::M, ti::L));\n\n  // eq 18 - 20\n  ::Ccz4::spatial_ricci_tensor(\n      spatial_ricci_tensor, *christoffel_second_kind,\n      *contracted_christoffel_second_kind,\n      *contracted_d_conformal_christoffel_difference, conformal_spatial_metric,\n      *inv_conformal_spatial_metric, field_d, *field_d_up,\n      *contracted_field_d_up, field_p, d_field_p);\n\n  // eq 21\n  ::Ccz4::grad_grad_lapse(grad_grad_lapse, lapse, *christoffel_second_kind,\n                          field_a, d_field_a);\n\n  // eq 22\n  ::Ccz4::divergence_lapse(divergence_lapse, *conformal_factor_squared,\n                           *inv_conformal_spatial_metric, *grad_grad_lapse);\n\n  // eq 23\n  ::Ccz4::contracted_conformal_christoffel_second_kind(\n      contracted_conformal_christoffel_second_kind,\n      *inv_conformal_spatial_metric, *conformal_christoffel_second_kind);\n\n  // eq 24\n  ::Ccz4::deriv_contracted_conformal_christoffel_second_kind(\n      d_contracted_conformal_christoffel_second_kind,\n      *inv_conformal_spatial_metric, *field_d_up,\n      *conformal_christoffel_second_kind, *d_conformal_christoffel_second_kind);\n\n  // temp for eq 25\n  ::tenex::evaluate<ti::I>(\n      gamma_hat_minus_contracted_conformal_christoffel,\n      gamma_hat(ti::I) -\n          (*contracted_conformal_christoffel_second_kind)(ti::I));\n\n  // eq 25\n  ::Ccz4::spatial_z4_constraint(\n      spatial_z4_constraint, conformal_spatial_metric,\n      *gamma_hat_minus_contracted_conformal_christoffel);\n\n  // temp for eq 25\n  ::tenex::evaluate(half_conformal_factor_squared,\n                    0.5 * (*conformal_factor_squared)());\n\n  // eq 25\n  ::Ccz4::upper_spatial_z4_constraint(\n      upper_spatial_z4_constraint, *half_conformal_factor_squared,\n      *gamma_hat_minus_contracted_conformal_christoffel);\n\n  // temp for eq 26\n  ::tenex::evaluate<ti::i, ti::L>(\n      d_gamma_hat_minus_contracted_conformal_christoffel,\n      d_gamma_hat(ti::i, ti::L) -\n          (*d_contracted_conformal_christoffel_second_kind)(ti::i, ti::L));\n\n  // eq 26\n  ::Ccz4::grad_spatial_z4_constraint(\n      grad_spatial_z4_constraint, *spatial_z4_constraint,\n      conformal_spatial_metric, *christoffel_second_kind, field_d,\n      *gamma_hat_minus_contracted_conformal_christoffel,\n      *d_gamma_hat_minus_contracted_conformal_christoffel);\n\n  // eq 27\n  ::Ccz4::ricci_scalar_plus_divergence_z4_constraint(\n      ricci_scalar_plus_divergence_z4_constraint, *conformal_factor_squared,\n      *inv_conformal_spatial_metric, *spatial_ricci_tensor,\n      *grad_spatial_z4_constraint);\n\n  // temporary expressions not already computed above\n\n  ::tenex::evaluate(contracted_field_b, field_b(ti::k, ti::K));\n\n  ::tenex::evaluate<ti::k, ti::j, ti::I>(\n      symmetrized_d_field_b,\n      0.5 * (d_field_b(ti::k, ti::j, ti::I) + d_field_b(ti::j, ti::k, ti::I)));\n\n  ::tenex::evaluate<ti::k>(contracted_symmetrized_d_field_b,\n                           (*symmetrized_d_field_b)(ti::k, ti::i, ti::I));\n\n  ::tenex::evaluate<ti::k>(\n      field_d_up_times_a_tilde,\n      (*field_d_up)(ti::k, ti::I, ti::J) * a_tilde(ti::i, ti::j));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      conformal_metric_times_field_b,\n      conformal_spatial_metric(ti::k, ti::i) * field_b(ti::j, ti::K));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      conformal_metric_times_trace_a_tilde,\n      conformal_spatial_metric(ti::i, ti::j) * (*trace_a_tilde)());\n\n  ::tenex::evaluate<ti::k>(inv_conformal_metric_times_d_a_tilde,\n                           (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                               d_a_tilde(ti::k, ti::i, ti::j));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      a_tilde_times_field_b, a_tilde(ti::k, ti::i) * field_b(ti::j, ti::K));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde,\n      a_tilde(ti::i, ti::j) -\n          one_third * (*conformal_metric_times_trace_a_tilde)(ti::i, ti::j));\n\n  ::tenex::evaluate(k_minus_2_theta_c,\n                    trace_extrinsic_curvature() - 2.0 * c * theta());\n\n  ::tenex::evaluate(k_minus_k0_minus_2_theta_c, (*k_minus_2_theta_c)() - k_0());\n\n  ::tenex::evaluate<ti::i, ti::j>(lapse_times_a_tilde,\n                                  (lapse)() * a_tilde(ti::i, ti::j));\n\n  tenex::evaluate<ti::k, ti::i, ti::j>(\n      lapse_times_d_a_tilde, (lapse)() * d_a_tilde(ti::k, ti::i, ti::j));\n\n  ::tenex::evaluate<ti::k>(lapse_times_field_a, (lapse)() * field_a(ti::k));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      lapse_times_conformal_spatial_metric,\n      (lapse)() * conformal_spatial_metric(ti::i, ti::j));\n\n  ::tenex::evaluate(\n      lapse_times_ricci_scalar_plus_divergence_z4_constraint,\n      (lapse)() * (*ricci_scalar_plus_divergence_z4_constraint)());\n\n  ::tenex::evaluate<ti::I>(shift_times_deriv_gamma_hat,\n                           shift(ti::K) * d_gamma_hat(ti::k, ti::I));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      inv_tau_times_conformal_metric,\n      one_over_relaxation_time * conformal_spatial_metric(ti::i, ti::j));\n\n  // time derivative computation: eq 4(a) - 4(i)\n  // The way we evaluate the following may seem weird compared\n  // to 4(a) - 4(i). This is because we try to reuse the first-order\n  // Ccz4 codes as much as possible. Hence, the following is written\n  // based on 12a-12i, with s=1, and red terms canceled.\n\n  // eq 12a : time derivative of the conformal spatial metric\n  // this reduces to eq 4a for our choice of \\tau^{-1}=0.\n  ::tenex::evaluate<ti::i, ti::j>(\n      dt_conformal_spatial_metric,\n      2.0 * shift(ti::K) * field_d(ti::k, ti::i, ti::j) +\n          (*conformal_metric_times_field_b)(ti::i, ti::j) +\n          (*conformal_metric_times_field_b)(ti::j, ti::i) -\n          2.0 * one_third * conformal_spatial_metric(ti::i, ti::j) *\n              (*contracted_field_b)() -\n          2.0 * (lapse)() *\n              (*a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde)(\n                  ti::i, ti::j) -\n          (*inv_tau_times_conformal_metric)(ti::i, ti::j) *\n              ((*det_conformal_spatial_metric)() - 1.0));\n\n  if (evolve_lapse_and_shift) {\n    // time derivative of the lapse\n    ::tenex::evaluate<>(dt_lapse, (shift(ti::K) * field_a(ti::k) -\n                                   (*lapse_times_slicing_condition)() *\n                                       (*k_minus_k0_minus_2_theta_c)()) *\n                                      lapse());\n    // time derivative of the shift\n    ::tenex::evaluate<ti::I>(dt_shift, f * b(ti::I));\n    if (shifting_shift) {\n      ::tenex::update<ti::I>(\n          dt_shift, (*dt_shift)(ti::I) + shift(ti::K) * field_b(ti::k, ti::I));\n    }\n  } else {\n    *dt_lapse = make_with_value<Scalar<DataVector>>(lapse, 0.0);\n    *dt_shift = make_with_value<tnsr::I<DataVector, Dim>>(shift, 0.0);\n  }\n\n  // time derivative of the the conformal factor\n  ::tenex::evaluate(dt_conformal_factor,\n                    (shift(ti::K) * field_p(ti::k) +\n                     one_third * ((lapse)() * trace_extrinsic_curvature() -\n                                  (*contracted_field_b)())) *\n                        conformal_factor());\n\n  // time derivative of the trace-free part of the extrinsic curvature\n  ::tenex::evaluate<ti::i, ti::j>(\n      dt_a_tilde,\n      shift(ti::K) * d_a_tilde(ti::k, ti::i, ti::j) +\n          (*conformal_factor_squared)() *\n              ((lapse)() * ((*spatial_ricci_tensor)(ti::i, ti::j) +\n                            (*grad_spatial_z4_constraint)(ti::i, ti::j) +\n                            (*grad_spatial_z4_constraint)(ti::j, ti::i)) -\n               (*grad_grad_lapse)(ti::i, ti::j)) -\n          one_third * conformal_spatial_metric(ti::i, ti::j) *\n              ((*lapse_times_ricci_scalar_plus_divergence_z4_constraint)() -\n               (*divergence_lapse)()) +\n          (*a_tilde_times_field_b)(ti::i, ti::j) +\n          (*a_tilde_times_field_b)(ti::j, ti::i) -\n          2.0 * one_third * a_tilde(ti::i, ti::j) * (*contracted_field_b)() +\n          (*lapse_times_a_tilde)(ti::i, ti::j) * (*k_minus_2_theta_c)() -\n          2.0 * (*lapse_times_a_tilde)(ti::i, ti::l) *\n              (*inv_conformal_spatial_metric)(ti::L, ti::M) *\n              a_tilde(ti::m, ti::j) -\n          (*inv_tau_times_conformal_metric)(ti::i, ti::j) * (*trace_a_tilde)());\n\n  // time derivative of the trace of the extrinsic curvature\n  ::tenex::evaluate(\n      dt_trace_extrinsic_curvature,\n      shift(ti::K) * d_trace_extrinsic_curvature(ti::k) -\n          (*divergence_lapse)() +\n          (*lapse_times_ricci_scalar_plus_divergence_z4_constraint)() +\n          (lapse)() * (trace_extrinsic_curvature() * (*k_minus_2_theta_c)() -\n                       3.0 * kappa_1 * (1.0 + kappa_2) * theta()));\n\n  // time derivative of the projection of the Z4 four-vector along\n  // the normal direction\n  ::tenex::evaluate(\n      dt_theta,\n      shift(ti::K) * d_theta(ti::k) +\n          (lapse)() *\n              (0.5 * square(cleaning_speed) *\n                   ((*ricci_scalar_plus_divergence_z4_constraint)() +\n                    2.0 * one_third * square(trace_extrinsic_curvature()) -\n                    a_tilde(ti::i, ti::j) * (*inv_a_tilde)(ti::I, ti::J)) -\n               c * theta() * trace_extrinsic_curvature() -\n               (*upper_spatial_z4_constraint)(ti::I)*field_a(ti::i) -\n               kappa_1 * (2.0 + kappa_2) * theta()));\n\n  // time derivative \\hat{\\Gamma}^i\n  // first, compute terms without s\n  ::tenex::evaluate<ti::I>(\n      dt_gamma_hat,\n      // terms without lapse nor s\n      (*shift_times_deriv_gamma_hat)(ti::I) +\n          2.0 * one_third *\n              (*contracted_conformal_christoffel_second_kind)(ti::I) *\n              (*contracted_field_b)() -\n          (*contracted_conformal_christoffel_second_kind)(ti::K)*field_b(\n              ti::k, ti::I) +\n          2.0 * kappa_3 * (*spatial_z4_constraint)(ti::j) *\n              (2.0 * one_third * (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                   (*contracted_field_b)() -\n               (*inv_conformal_spatial_metric)(ti::J, ti::K) *\n                   field_b(ti::k, ti::I)) +\n          // terms with lapse but not s\n          2.0 * (lapse)() *\n              (-2.0 * one_third *\n                   (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                   d_trace_extrinsic_curvature(ti::j) +\n               (*inv_conformal_spatial_metric)(ti::K, ti::I) * d_theta(ti::k) +\n               (*conformal_christoffel_second_kind)(ti::I, ti::j, ti::k) *\n                   (*inv_a_tilde)(ti::J, ti::K) -\n               3.0 * (*inv_a_tilde)(ti::I, ti::J) * field_p(ti::j) -\n               (*inv_conformal_spatial_metric)(ti::K, ti::I) *\n                   (theta() * field_a(ti::k) +\n                    2.0 * one_third * trace_extrinsic_curvature() *\n                        (*spatial_z4_constraint)(ti::k)) -\n               (*inv_a_tilde)(ti::I, ti::J) * field_a(ti::j) -\n               kappa_1 * (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                   (*spatial_z4_constraint)(ti::j)));\n  // We add the following since s=1 (Gamma-driver gauge) is assumed in SoCcz4\n  ::tenex::update<ti::I>(dt_gamma_hat,\n                         (*dt_gamma_hat)(ti::I) +\n                             // red terms should cancel\n                             // terms with s but not lapse\n                             (*inv_conformal_spatial_metric)(ti::K, ti::L) *\n                                 (*symmetrized_d_field_b)(ti::k, ti::l, ti::I) +\n                             one_third *\n                                 (*inv_conformal_spatial_metric)(ti::I, ti::K) *\n                                 (*contracted_symmetrized_d_field_b)(ti::k));\n\n  // time derivative b^i\n  if (evolve_lapse_and_shift) {\n    ::tenex::evaluate<ti::I>(dt_b, (*dt_gamma_hat)(ti::I)-eta() * b(ti::I));\n    if (shifting_shift) {\n      ::tenex::update<ti::I>(\n          dt_b, (*dt_b)(ti::I) + shift(ti::K) * (d_b(ti::k, ti::I) -\n                                                 d_gamma_hat(ti::k, ti::I)));\n    }\n  } else {\n    (*dt_b).get(0) = 0.0;\n    (*dt_b).get(1) = 0.0;\n    (*dt_b).get(2) = 0.0;\n  }\n\n  // Note that, we do not need to evolve the auxiliary variables in SO-CCZ4.\n}\n}  // namespace detail\n\n/*!\n * \\brief Compute the RHS of the second-order CCZ4 formulation of Einstein's\n * equations \\cite Dumbser2017okk with finite differencing. Also\n * update the time derivative at outermost interior points in a sphere domain\n * if Sommerfeld boundary conditions are applied.\n *\n * \\details The evolution equations are equations 4(a) - 4(i) of\n * \\cite Dumbser2017okk Equations 13 - 27 define identities\n * used in the evolution equations.\n *\n * \\note Different from the first-order system, the lapse\n * and the conformal factor are evolved instead of their natural logs.\n * The four auxiliary varialbels \\f$A_i\\f$, \\f$B_k{}^{i}\\f$,\n * \\f$D_{kij}\\f$, and \\f$P_i\\f$ are NOT evolved.\n */\nstruct SoTimeDerivative {\n  template <typename DbTagsList>\n  static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n    // compute the first spatial derivatives of the evolved variables\n    using evolved_vars_tag = typename System::variables_tag;\n    using gradients_tags = typename System::gradients_tags;\n\n    // only 4-th order accurate second derivatives have been implemented\n    // To keep the same order of accuracy we use the same order for\n    // both first and second derivatives\n    constexpr size_t fd_order = 4;\n    const auto& evolved_vars = db::get<evolved_vars_tag>(*box);\n    const Mesh<Dim>& subcell_mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(*box);\n    const size_t num_pts = subcell_mesh.number_of_grid_points();\n    using deriv_var_tag = db::wrap_tags_in<::Tags::deriv, gradients_tags,\n                                           tmpl::size_t<Dim>, Frame::Inertial>;\n    Variables<deriv_var_tag> cell_centered_Ccz4_derivs{num_pts};\n    const auto& cell_centered_logical_to_inertial_inv_jacobian =\n        db::get<evolution::dg::subcell::fd::Tags::\n                    InverseJacobianLogicalToInertial<Dim>>(*box);\n    const auto& cell_centered_logical_to_inertial_inv_hessian = db::get<\n        evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertial<Dim>>(\n        *box);\n\n    constexpr bool subcell_enabled_at_external_boundary =\n        std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>::SubcellOptions::subcell_enabled_at_external_boundary;\n\n    const Element<3>& element = db::get<domain::Tags::Element<3>>(*box);\n\n    const Ccz4::fd::Reconstructor& recons =\n        db::get<Ccz4::fd::Tags::Reconstructor>(*box);\n\n    // If the element has external boundaries and subcell is enabled for\n    // boundary elements, compute FD ghost data with a given boundary condition.\n    if constexpr (subcell_enabled_at_external_boundary) {\n      if (not element.external_boundaries().empty()) {\n        fd::BoundaryConditionGhostData::apply(box, element, recons);\n      }\n    }\n\n    const bool evolve_lapse_and_shift =\n        get<::Ccz4::fd::Tags::EvolveLapseAndShift>(*box);\n\n    ::Ccz4::fd::spacetime_derivatives(\n        make_not_null(&cell_centered_Ccz4_derivs), evolved_vars,\n        db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(\n            *box),\n        fd_order, subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian);\n\n    // calculate the four auxiliary fields in eq. 6\n    // auxiliary variables NOT evolved in SO-CCZ4\n    const auto& d_lapse =\n        get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<Dim>,\n                          Frame::Inertial>>(cell_centered_Ccz4_derivs);\n    const auto& lapse = get<gr::Tags::Lapse<DataVector>>(evolved_vars);\n    const auto field_a = ::tenex::evaluate<ti::i>(d_lapse(ti::i) / lapse());\n\n    const auto& field_b =\n        get<::Tags::deriv<gr::Tags::Shift<DataVector, Dim>, tmpl::size_t<Dim>,\n                          Frame::Inertial>>(cell_centered_Ccz4_derivs);\n\n    const auto& d_spatial_conformal_metric =\n        get<::Tags::deriv<::Ccz4::Tags::ConformalMetric<DataVector, Dim>,\n                          tmpl::size_t<Dim>, Frame::Inertial>>(\n            cell_centered_Ccz4_derivs);\n    tnsr::ijj<DataVector, Dim> field_d;\n    ::tenex::evaluate<ti::i, ti::j, ti::k>(\n        make_not_null(&field_d),\n        0.5 * d_spatial_conformal_metric(ti::i, ti::j, ti::k));\n\n    const auto& conformal_factor =\n        get<::Ccz4::Tags::ConformalFactor<DataVector>>(evolved_vars);\n    const auto& d_conformal_factor =\n        get<::Tags::deriv<::Ccz4::Tags::ConformalFactor<DataVector>,\n                          tmpl::size_t<Dim>, Frame::Inertial>>(\n            cell_centered_Ccz4_derivs);\n    const auto field_p = ::tenex::evaluate<ti::i>(d_conformal_factor(ti::i) /\n                                                  conformal_factor());\n\n    // compute second derivatives of the evolved variables\n    Variables<db::wrap_tags_in<::Tags::second_deriv, gradients_tags,\n                               tmpl::size_t<Dim>, Frame::Inertial>>\n        cell_centered_Ccz4_second_derivs{num_pts};\n\n    Ccz4::fd::second_spacetime_derivatives(\n        make_not_null(&cell_centered_Ccz4_second_derivs), evolved_vars,\n        db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(\n            *box),\n        fd_order, subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian,\n        cell_centered_logical_to_inertial_inv_hessian);\n\n    // compute spatial derivative of the four auxiliary fields\n    const auto& d_d_lapse =\n        get<::Tags::second_deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<Dim>,\n                                 Frame::Inertial>>(\n            cell_centered_Ccz4_second_derivs);\n    tnsr::ii<DataVector, Dim> d_field_a;\n    ::tenex::evaluate<ti::i, ti::j>(\n        make_not_null(&d_field_a),\n        (d_d_lapse(ti::i, ti::j) - d_lapse(ti::i) * d_lapse(ti::j) / lapse()) /\n            lapse());\n\n    const auto& d_field_b =\n        get<::Tags::second_deriv<gr::Tags::Shift<DataVector, Dim>,\n                                 tmpl::size_t<Dim>, Frame::Inertial>>(\n            cell_centered_Ccz4_second_derivs);\n\n    const auto& d_d_conformal_metric =\n        get<::Tags::second_deriv<::Ccz4::Tags::ConformalMetric<DataVector, Dim>,\n                                 tmpl::size_t<Dim>, Frame::Inertial>>(\n            cell_centered_Ccz4_second_derivs);\n\n    tnsr::iijj<DataVector, Dim> d_field_d;\n    ::tenex::evaluate<ti::i, ti::j, ti::k, ti::l>(\n        make_not_null(&d_field_d),\n        0.5 * d_d_conformal_metric(ti::i, ti::j, ti::k, ti::l));\n\n    const auto& d_d_conformal_factor =\n        get<::Tags::second_deriv<::Ccz4::Tags::ConformalFactor<DataVector>,\n                                 tmpl::size_t<Dim>, Frame::Inertial>>(\n            cell_centered_Ccz4_second_derivs);\n\n    tnsr::ii<DataVector, Dim> d_field_p;\n    ::tenex::evaluate<ti::i, ti::j>(\n        make_not_null(&d_field_p),\n        (d_d_conformal_factor(ti::i, ti::j) - d_conformal_factor(ti::i) *\n                                                  d_conformal_factor(ti::j) /\n                                                  conformal_factor()) /\n            conformal_factor());\n\n    // intialize containers to be supplied in the SO-CCZ4 TimeDerivative.cpp\n    // apply() function quantities we need for computing eq 4, 13 - 27\n    using TempVars = Variables<tmpl::list<\n        ::Ccz4::Tags::ConformalFactorSquared<DataVector>,\n        ::Ccz4::Tags::DetConformalSpatialMetric<DataVector>,\n        ::Ccz4::Tags::InverseConformalMetric<DataVector, Dim>,\n        gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n        ::Ccz4::Tags::InvATilde<DataVector, Dim>,\n        ::Ccz4::Tags::ATildeTimesFieldB<DataVector, Dim>,\n        ::Ccz4::Tags::ATildeMinusOneThirdConformalMetricTimesTraceATilde<\n            DataVector, Dim>,\n        ::Ccz4::Tags::ContractedFieldB<DataVector>,\n        ::Ccz4::Tags::SymmetrizedDerivFieldB<DataVector, Dim>,\n        ::Ccz4::Tags::ContractedSymmetrizedDerivFieldB<DataVector, Dim>,\n        ::Ccz4::Tags::FieldDUpTimesATilde<DataVector, Dim>,\n        ::Ccz4::Tags::ContractedFieldDUp<DataVector, Dim>,\n        ::Ccz4::Tags::HalfConformalFactorSquared<DataVector>,\n        ::Ccz4::Tags::ConformalMetricTimesFieldB<DataVector, Dim>,\n        ::Ccz4::Tags::ConformalMetricTimesTraceATilde<DataVector, Dim>,\n        ::Ccz4::Tags::InverseConformalMetricTimesDerivATilde<DataVector, Dim>,\n        ::Ccz4::Tags::GammaHatMinusContractedConformalChristoffel<DataVector,\n                                                                  Dim>,\n        ::Ccz4::Tags::DerivGammaHatMinusContractedConformalChristoffel<\n            DataVector, Dim>,\n        ::Ccz4::Tags::ContractedChristoffelSecondKind<DataVector, Dim>,\n        ::Ccz4::Tags::ContractedDerivConformalChristoffelDifference<DataVector,\n                                                                    Dim>,\n        ::Ccz4::Tags::KMinus2ThetaC<DataVector>,\n        ::Ccz4::Tags::KMinusK0Minus2ThetaC<DataVector>,\n        ::Ccz4::Tags::LapseTimesATilde<DataVector, Dim>,\n        ::Ccz4::Tags::LapseTimesDerivATilde<DataVector, Dim>,\n        ::Ccz4::Tags::LapseTimesFieldA<DataVector, Dim>,\n        ::Ccz4::Tags::LapseTimesConformalMetric<DataVector, Dim>,\n        ::Ccz4::Tags::LapseTimesSlicingCondition<DataVector>,\n        ::Ccz4::Tags::LapseTimesRicciScalarPlus2DivergenceZ4Constraint<\n            DataVector>,\n        ::Ccz4::Tags::ShiftTimesDerivGammaHat<DataVector, Dim>,\n        ::Ccz4::Tags::InverseTauTimesConformalMetric<DataVector, Dim>,\n        ::Ccz4::Tags::TraceATilde<DataVector>,\n        ::Ccz4::Tags::FieldDUp<DataVector, Dim>,\n        ::Ccz4::Tags::ConformalChristoffelSecondKind<DataVector, Dim>,\n        ::Ccz4::Tags::DerivConformalChristoffelSecondKind<DataVector, Dim>,\n        ::Ccz4::Tags::ChristoffelSecondKind<DataVector, Dim>,\n        ::Ccz4::Tags::SpatialRicciTensor<DataVector, Dim>,\n        ::Ccz4::Tags::GradGradLapse<DataVector, Dim>,\n        ::Ccz4::Tags::DivergenceLapse<DataVector>,\n        ::Ccz4::Tags::ContractedConformalChristoffelSecondKind<DataVector, Dim>,\n        ::Ccz4::Tags::DerivContractedConformalChristoffelSecondKind<DataVector,\n                                                                    Dim>,\n        ::Ccz4::Tags::SpatialZ4Constraint<DataVector, Dim>,\n        ::Ccz4::Tags::GradSpatialZ4Constraint<DataVector, Dim>,\n        ::Ccz4::Tags::RicciScalarPlusDivergenceZ4Constraint<DataVector>>>;\n\n    TempVars temp_vars(num_pts);\n    tnsr::I<DataVector, Dim> upper_spatial_z4_constraint(num_pts);\n\n    // free params\n    const double c = 1.0;               // c = 1.0 in SO-CCZ4\n    const double cleaning_speed = 1.0;  // e in the paper; e = 1.0 for SO-CCZ4\n    const Scalar<DataVector>& eta = get<::Ccz4::Tags::Eta<DataVector>>(*box);\n    const double f = Ccz4::fd::System::f;\n    const Scalar<DataVector>& k_0 = get<::Ccz4::Tags::K0<DataVector>>(*box);\n    const double kappa_1 = get<::Ccz4::Tags::Kappa1>(*box);\n    const double kappa_2 = get<::Ccz4::Tags::Kappa2>(*box);\n    const double kappa_3 = get<::Ccz4::Tags::Kappa3>(*box);\n    const double one_over_relaxation_time = 0.0;  // \\tau^{-1} = 0 in SO-CCZ4\n    const bool shifting_shift = Ccz4::fd::System::shifting_shift;\n\n    // we assume the databox already has tags corresponding to dt of the evolved\n    // variables\n    using dt_variables_tag = db::add_tag_prefix<::Tags::dt, evolved_vars_tag>;\n\n    db::mutate<dt_variables_tag,\n               ::Ccz4::Tags::SpatialZ4ConstraintUp<DataVector, Dim>>(\n        [&](const auto dt_vars_ptr,\n            const auto upper_spatial_z4_constraint_ptr) {\n          // resize here\n          dt_vars_ptr->initialize(subcell_mesh.number_of_grid_points());\n          auto& [conformal_factor_squared, det_conformal_spatial_metric,\n                 inv_conformal_spatial_metric, inv_spatial_metric, inv_a_tilde,\n                 a_tilde_times_field_b,\n                 a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde,\n                 contracted_field_b, symmetrized_d_field_b,\n                 contracted_symmetrized_d_field_b, field_d_up_times_a_tilde,\n                 contracted_field_d_up, half_conformal_factor_squared,\n                 conformal_metric_times_field_b,\n                 conformal_metric_times_trace_a_tilde,\n                 inv_conformal_metric_times_d_a_tilde,\n                 gamma_hat_minus_contracted_conformal_christoffel,\n                 d_gamma_hat_minus_contracted_conformal_christoffel,\n                 contracted_christoffel_second_kind,\n                 contracted_d_conformal_christoffel_difference,\n                 k_minus_2_theta_c, k_minus_k0_minus_2_theta_c,\n                 lapse_times_a_tilde, lapse_times_d_a_tilde,\n                 lapse_times_field_a, lapse_times_conformal_spatial_metric,\n                 lapse_times_slicing_condition,\n                 lapse_times_ricci_scalar_plus_divergence_z4_constraint,\n                 shift_times_deriv_gamma_hat, inv_tau_times_conformal_metric,\n                 trace_a_tilde, field_d_up, conformal_christoffel_second_kind,\n                 d_conformal_christoffel_second_kind, christoffel_second_kind,\n                 spatial_ricci_tensor, grad_grad_lapse, divergence_lapse,\n                 contracted_conformal_christoffel_second_kind,\n                 d_contracted_conformal_christoffel_second_kind,\n                 spatial_z4_constraint, grad_spatial_z4_constraint,\n                 ricci_scalar_plus_divergence_z4_constraint] = temp_vars;\n          detail::apply(\n              // LHS time derivatives of evolved variables: eq 4a - 4i\n              make_not_null(\n                  &get<::Tags::dt<\n                      ::Ccz4::Tags::ConformalMetric<DataVector, Dim>>>(\n                      *dt_vars_ptr)),  // eq 4a\n              make_not_null(&get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(\n                  *dt_vars_ptr)),  // eq 4g\n              make_not_null(&get<::Tags::dt<gr::Tags::Shift<DataVector, Dim>>>(\n                  *dt_vars_ptr)),  // eq 4h\n              make_not_null(\n                  &get<::Tags::dt<::Ccz4::Tags::ConformalFactor<DataVector>>>(\n                      *dt_vars_ptr)),  // eq 4c\n              make_not_null(\n                  &get<::Tags::dt<::Ccz4::Tags::ATilde<DataVector, Dim>>>(\n                      *dt_vars_ptr)),  // eq 4b\n              make_not_null(&get<::Tags::dt<\n                                gr::Tags::TraceExtrinsicCurvature<DataVector>>>(\n                  *dt_vars_ptr)),  // eq 4d\n              make_not_null(&get<::Tags::dt<::Ccz4::Tags::Theta<DataVector>>>(\n                  *dt_vars_ptr)),  // eq 4e\n              make_not_null(\n                  &get<::Tags::dt<::Ccz4::Tags::GammaHat<DataVector, Dim>>>(\n                      *dt_vars_ptr)),  // eq 4f\n              make_not_null(\n                  &get<::Tags::dt<\n                      ::Ccz4::Tags::AuxiliaryShiftB<DataVector, Dim>>>(\n                      *dt_vars_ptr)),  // eq 4i\n\n              // quantities we need for computing eq 4, 13 - 27\n              make_not_null(&conformal_factor_squared),\n              make_not_null(&det_conformal_spatial_metric),\n              make_not_null(&inv_conformal_spatial_metric),\n              make_not_null(&inv_spatial_metric), make_not_null(&inv_a_tilde),\n              // temporary expressions\n              make_not_null(&a_tilde_times_field_b),\n              make_not_null(\n                &a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde),\n              make_not_null(&contracted_field_b),\n              make_not_null(&symmetrized_d_field_b),\n              make_not_null(&contracted_symmetrized_d_field_b),\n              make_not_null(&field_d_up_times_a_tilde),\n              make_not_null(&contracted_field_d_up),  // temp for eq 18 -20\n              make_not_null(&half_conformal_factor_squared),  // temp for eq 25\n              make_not_null(&conformal_metric_times_field_b),\n              make_not_null(&conformal_metric_times_trace_a_tilde),\n              make_not_null(&inv_conformal_metric_times_d_a_tilde),\n              make_not_null(&gamma_hat_minus_contracted_conformal_christoffel),\n              make_not_null(\n                  &d_gamma_hat_minus_contracted_conformal_christoffel),\n              make_not_null(\n                  &contracted_christoffel_second_kind),  // temp for eq 18 -20\n              make_not_null(\n                  &contracted_d_conformal_christoffel_difference),  // temp for\n                                                                    // eq 18 -20\n              make_not_null(&k_minus_2_theta_c),\n              make_not_null(&k_minus_k0_minus_2_theta_c),\n              make_not_null(&lapse_times_a_tilde),\n              make_not_null(&lapse_times_d_a_tilde),\n              make_not_null(&lapse_times_field_a),\n              make_not_null(&lapse_times_conformal_spatial_metric),\n              make_not_null(&lapse_times_slicing_condition),\n              make_not_null(\n                  &lapse_times_ricci_scalar_plus_divergence_z4_constraint),\n              make_not_null(&shift_times_deriv_gamma_hat),\n              make_not_null(&inv_tau_times_conformal_metric),\n              // expressions and identities needed for evolution equations: eq\n              // 13\n              // - 27\n              make_not_null(&trace_a_tilde),                        // eq 13\n              make_not_null(&field_d_up),                           // eq 14\n              make_not_null(&conformal_christoffel_second_kind),    // eq 15\n              make_not_null(&d_conformal_christoffel_second_kind),  // eq 16\n              make_not_null(&christoffel_second_kind),              // eq 17\n              make_not_null(&spatial_ricci_tensor),  // eq 18 - 20\n              make_not_null(&grad_grad_lapse),       // eq 21\n              make_not_null(&divergence_lapse),      // eq 22\n              make_not_null(\n                  &contracted_conformal_christoffel_second_kind),  // eq 23\n              make_not_null(\n                  &d_contracted_conformal_christoffel_second_kind),  // eq 24\n              make_not_null(&spatial_z4_constraint),                 // eq 25\n              make_not_null(&upper_spatial_z4_constraint),           // eq 25\n              make_not_null(&grad_spatial_z4_constraint),            // eq 26\n              make_not_null(\n                  &ricci_scalar_plus_divergence_z4_constraint),  // eq 27\n              // fixed params\n              c, cleaning_speed,         // e in the paper\n              one_over_relaxation_time,  // \\tau^{-1}\n              // free params\n              eta, f, kappa_1, kappa_2, kappa_3, k_0,\n              // evolved variables\n              get<::Ccz4::Tags::ConformalMetric<DataVector, Dim>>(evolved_vars),\n              get<gr::Tags::Lapse<DataVector>>(evolved_vars),\n              get<gr::Tags::Shift<DataVector, Dim>>(evolved_vars),\n              get<::Ccz4::Tags::ConformalFactor<DataVector>>(evolved_vars),\n              get<::Ccz4::Tags::ATilde<DataVector, Dim>>(evolved_vars),\n              get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars),\n              get<::Ccz4::Tags::Theta<DataVector>>(evolved_vars),\n              get<::Ccz4::Tags::GammaHat<DataVector, Dim>>(evolved_vars),\n              get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, Dim>>(evolved_vars),\n\n              field_a,  // auxiliary variables NOT evolved in SO-CCZ4\n              field_b, field_d, field_p,\n              d_field_a,  // spatial derivative of auxiliary variables\n              d_field_b, d_field_d, d_field_p,\n\n              // spatial derivatives of other evolved variables\n              get<::Tags::deriv<::Ccz4::Tags::ATilde<DataVector, Dim>,\n                                tmpl::size_t<Dim>, Frame::Inertial>>(\n                  cell_centered_Ccz4_derivs),\n              get<::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                                tmpl::size_t<Dim>, Frame::Inertial>>(\n                  cell_centered_Ccz4_derivs),\n              get<::Tags::deriv<::Ccz4::Tags::Theta<DataVector>,\n                                tmpl::size_t<Dim>, Frame::Inertial>>(\n                  cell_centered_Ccz4_derivs),\n              get<::Tags::deriv<::Ccz4::Tags::GammaHat<DataVector, Dim>,\n                                tmpl::size_t<Dim>, Frame::Inertial>>(\n                  cell_centered_Ccz4_derivs),\n              get<::Tags::deriv<::Ccz4::Tags::AuxiliaryShiftB<DataVector, Dim>,\n                                tmpl::size_t<Dim>, Frame::Inertial>>(\n                  cell_centered_Ccz4_derivs),\n              shifting_shift, evolve_lapse_and_shift);\n\n          *upper_spatial_z4_constraint_ptr =\n              std::move(upper_spatial_z4_constraint);\n        },\n        box);\n\n    // handling Sommerfeld BCs\n    // WARNING: We assume a complete sphere domain (all wedges)\n    if constexpr (not subcell_enabled_at_external_boundary) {\n      return;\n    }\n    if (element.external_boundaries().empty()) {\n      return;\n    }\n\n    const auto& external_bcs =\n        db::get<domain::Tags::ExternalBoundaryConditions<Dim>>(*box).at(\n            element.id().block_id());\n    for (const auto& direction : element.external_boundaries()) {\n      ASSERT(direction == Direction<Dim>::upper_zeta(),\n             \"external bc direction is not upper_zeta but \" << direction);\n      // this is potentially expensive; we should add a boolean flag for\n      // time-dependent bc. This should be done in a future PR.\n      if (dynamic_cast<const Ccz4::BoundaryConditions::Sommerfeld*>(\n              external_bcs.at(direction).get()) == nullptr) {\n        return;  // no need to check more, as we do not allow mixed BCs\n      }\n    }\n\n    db::mutate<dt_variables_tag>(\n        [&](const auto dt_vars_ptr, const auto& inertial_coords,\n            const auto& evolved_var) {\n          const size_t num_face_pts =\n              subcell_mesh.extents(0) * subcell_mesh.extents(1);\n\n          ASSERT(inertial_coords.get(0).size() ==\n                     subcell_mesh.number_of_grid_points() and\n                 inertial_coords.get(1).size() ==\n                     subcell_mesh.number_of_grid_points() and\n                 inertial_coords.get(2).size() ==\n                     subcell_mesh.number_of_grid_points(),\n                 \"Inertial coords size [\"\n                     << inertial_coords.get(0).size()\n                     << \", \" << inertial_coords.get(1).size()\n                     << \", \" << inertial_coords.get(2).size()\n                     << \"] does not match number of subcell points \"\n                     << subcell_mesh.number_of_grid_points());\n\n          std::array<DataVector, Dim> outermost_inertial_coords{};\n          for (size_t i = 0; i < Dim; ++i) {\n            make_const_view<DataVector>(\n                make_not_null(&outermost_inertial_coords.at(i)),\n                inertial_coords.get(i),\n                (subcell_mesh.extents(2) - 1) * num_face_pts, num_face_pts);\n          }\n          const DataVector outermost_radial_coords =\n              sqrt(square(outermost_inertial_coords[0]) +\n                   square(outermost_inertial_coords[1]) +\n                   square(outermost_inertial_coords[2]));\n\n          // modify the time derivatives at the outermost interior points\n          // per Sommerfeld BCs\n          tmpl::for_each<Ccz4::fd::System::variables_tag_list>(\n              [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n                const auto& var = get<Tag>(evolved_var);\n                const auto& d_var =\n                    get<::Tags::deriv<Tag, tmpl::size_t<Dim>, Frame::Inertial>>(\n                        cell_centered_Ccz4_derivs);\n                auto& dt_var = get<::Tags::dt<Tag>>(*dt_vars_ptr);\n\n                for (size_t tensor_index = 0; tensor_index < Tag::type::size();\n                     ++tensor_index) {\n                  DataVector outermost_var{};\n                  make_const_view<DataVector>(\n                      make_not_null(&outermost_var), var[tensor_index],\n                      (subcell_mesh.extents(2) - 1) * num_face_pts,\n                      num_face_pts);\n\n                  std::array<DataVector, Dim> outermost_d_var{};\n                  for (size_t i = 0; i < Dim; ++i) {\n                    make_const_view<DataVector>(\n                        make_not_null(&outermost_d_var.at(i)),\n                        d_var[Dim * tensor_index + i],\n                        (subcell_mesh.extents(2) - 1) * num_face_pts,\n                        num_face_pts);\n                  }\n\n                  const DataVector outermost_dr_var =\n                      (outermost_inertial_coords[0] * outermost_d_var[0] +\n                       outermost_inertial_coords[1] * outermost_d_var[1] +\n                       outermost_inertial_coords[2] * outermost_d_var[2]) /\n                      outermost_radial_coords;\n                  DataVector outermost_dt_var{};\n                  outermost_dt_var.set_data_ref(\n                      dt_var[tensor_index].data() +\n                          (subcell_mesh.extents(2) - 1) * num_face_pts,\n                      num_face_pts);\n                  outermost_dt_var =\n                      outermost_var * (-1.0 / outermost_radial_coords) -\n                      outermost_dr_var;\n                }\n              });\n        },\n        box,\n        db::get<evolution::dg::subcell::Tags::Coordinates<\n            Dim, Frame::Inertial>>(*box),\n        db::get<evolved_vars_tag>(*box));\n  }\n};\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\nstruct System {\n  using flux_variables = tmpl::list<>;\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = 3;\n  static constexpr bool is_in_flux_conservative_form = false;\n  /* shifting_shift and f should be created from options in the future */\n  // whether to add the advective terms in the Gamma-driver condition\n  static constexpr bool shifting_shift = false;\n\n  // The free parameter f in the Gamma-driver condition.\n  static constexpr double f = 0.75;\n\n  // the order of the following evolved variables is important\n  // as it is assumed in the filter\n  using variables_tag = ::Tags::Variables<tmpl::list<\n      ::Ccz4::Tags::ConformalMetric<DataVector, 3>,\n      ::Ccz4::Tags::ConformalFactor<DataVector>,\n      ::Ccz4::Tags::ATilde<DataVector, 3>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      ::Ccz4::Tags::Theta<DataVector>, ::Ccz4::Tags::GammaHat<DataVector, 3>,\n      // gauge variables\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n      ::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>>;\n\n  using variables_tag_list = typename variables_tag::tags_list;\n\n  using gradients_tags = variables_tag_list;\n};\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellSolver.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/TagsDeclarations.hpp\"\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n\nnamespace Ccz4::fd {\n/// \\brief Option tags for evolving SoCcz4 with finite difference\nnamespace OptionTags {\n/// \\brief Option tag for the reconstructor\nstruct Reconstructor {\n  using type = std::unique_ptr<fd::Reconstructor>;\n\n  static constexpr Options::String help = {\"The reconstruction scheme to use.\"};\n  using group = evolution::dg::subcell::OptionTags::SubcellSolverGroup;\n};\n\n/// \\brief Option tag for whether to evolve the lapse and shift\nstruct EvolveLapseAndShift {\n  using type = bool;\n\n  static constexpr Options::String help = {\n      \"The option to use time-independent laspe and shift.\"};\n  using group = ::Ccz4::OptionTags::Ccz4Group;\n};\n\n/// \\brief Option tag for whether to use constrained evolution\n///\n/// When true, the determint of the conformal spatial metric is rescaled\n/// to one and the trace of ATilde is removed using the rescaled metric\n/// after every complete time step.\nstruct ConstrainedEvolution {\n  using type = bool;\n\n  static constexpr Options::String help = {\n      \"Whether to use constrained evolution.\"};\n  using group = ::Ccz4::OptionTags::Ccz4Group;\n};\n\n/// \\brief Option tag for the epsilon parameter of the Kreiss-Oliger dissipation\nstruct KreissOligerEpsilon {\n  using type = double;\n\n  static constexpr Options::String help = {\n      \"The epsilon parameter for Kreiss-Oliger dissipation.\"};\n  using group = ::Ccz4::OptionTags::Ccz4Group;\n};\n}  // namespace OptionTags\n\n/// \\brief Tags for evolving SoCcz4 with finite difference\nnamespace Tags {\n/// \\brief Tag for the reconstructor\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<fd::Reconstructor>;\n  using option_tags = tmpl::list<OptionTags::Reconstructor>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& reconstructor) {\n    return reconstructor->get_clone();\n  }\n};\n\n/*!\n * \\brief Tag for whether to evolve the lapse and shif\n */\nstruct EvolveLapseAndShift : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::EvolveLapseAndShift>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const bool evolve_lapse_and_shift) {\n    return evolve_lapse_and_shift;\n  }\n};\n\n/*!\n * \\brief Tag for whether to evolve the lapse and shift\n */\nstruct ConstrainedEvolution : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::ConstrainedEvolution>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const bool constrained_evolution) {\n    return constrained_evolution;\n  }\n};\n\n/*!\n * \\brief Tag for the epsilon parameter of the Kreiss-Oliger dissipation\n */\nstruct KreissOligerEpsilon : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::KreissOligerEpsilon>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const double kreiss_oliger_epsilon) {\n    return kreiss_oliger_epsilon;\n  }\n};\n\n/// \\brief Tags sent for second-order Ccz4 evolution.\nusing spacetime_reconstruction_tags = System::variables_tag_list;\n}  // namespace Tags\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Ricci1.cpp\n  Ricci2.cpp\n  Ricci3.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Instantiations/Ricci1.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/Ricci.tpp\"\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector), (tnsr::ij, tnsr::ii))\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Instantiations/Ricci2.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/Ricci.tpp\"\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector), (tnsr::ij, tnsr::ii))\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Instantiations/Ricci3.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/Ricci.tpp\"\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector), (tnsr::ij, tnsr::ii))\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Ricci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\n/// @{\n/*!\n * \\brief Computes the spatial Ricci tensor\n *\n * \\details Computes the spatial Ricci tensor as:\n *\n * \\f{align}\n *     R_{ij} &=\n *       \\partial_m \\Gamma^m_{ij} - \\partial_j \\Gamma^m_{im} +\n *       \\Gamma^l_{ij} \\Gamma^m_{lm} - \\Gamma^l_{im} \\Gamma^m_{lj}\n * \\f}\n *\n * where\n *\n * \\f{align}\n *     \\partial_k \\Gamma^m_{ij} &=\n *       \\partial_k \\tilde{\\Gamma}^m_{ij} +\n *       2 D_k{}^{ml} (\\tilde{\\gamma}_{jl} P_i + \\tilde{\\gamma}_{il} P_j -\n *                     \\tilde{\\gamma}_{ij} P_l)\\nonumber\\\\\n *       & - 2 \\tilde{\\gamma}^{ml} (D_{kjl} P_i + D_{kil} P_j - D_{kij} P_l) -\n *       \\tilde{\\gamma}^{ml} (\n *         \\tilde{\\gamma}_{jl} \\partial_{(k} P_{i)} +\n *         \\tilde{\\gamma}_{il} \\partial_{(k} P_{j)} -\n *         \\tilde{\\gamma}_{ij} \\partial_{(k} P_{l)})\n * \\f}\n *\n * \\f$\\Gamma^k_{ij}\\f$ is the spatial christoffel symbols of the second kind\n * defined by `Ccz4::Tags::ChristoffelSecondKind`,\n * \\f$\\partial_m \\tilde{\\Gamma}^k_{ij}\\f$ is the spatial derivative of the\n * conformal spatial christoffel symbols of the second kind defined by\n * `Ccz4::Tags::DerivConformalChristoffelSecondKind`, \\f$\\tilde{\\gamma}_{ij}\\f$\n * is the conformal spatial metric defined by `Ccz4::Tags::ConformalMetric`,\n * \\f$\\tilde{\\gamma}^{ij}\\f$ is the inverse conformal spatial metric defined by\n * `Ccz4::Tags::InverseConformalMetric`, \\f$D_{ijk}\\f$ is the CCZ4 auxiliary\n * variable defined by `Ccz4::Tags::FieldD`, \\f$D_k{}^{ij}\\f$ is the CCZ4\n * identity defined by `Ccz4::Tags::FieldDUp`, \\f$P_i\\f$ is the CCZ4 auxiliary\n * variable defined by `Ccz4::Tags::FieldP`, and \\f$\\partial_j P_{i}\\f$ is its\n * spatial derivative.\n *\n * After substituting in the full expressions for \\f$\\partial_m \\Gamma^m_{ij}\\f$\n * and \\f$\\partial_j \\Gamma^m_{im}\\f$ and commuting terms with common\n * coefficients, the full equation becomes and is implemented as:\n *\n *  \\f{align}{\n *     R_{ij} &=\n *       \\partial_m \\tilde{\\Gamma}^m_{ij} -\n *       \\partial_j \\tilde{\\Gamma}^m_{im}\\nonumber\\\\\n *       & + 2 D_m{}^{ml} (\\tilde{\\gamma}_{jl} P_i + \\tilde{\\gamma}_{il} P_j -\n *                         \\tilde{\\gamma}_{ij} P_l) -\n *       2 \\tilde{\\gamma}^{ml} (\n *         D_{mjl} P_i + D_{mil} P_j - D_{mij} P_l)\\nonumber\\\\\n *       & - 2 D_j{}^{ml} (\\tilde{\\gamma}_{ml} P_i + \\tilde{\\gamma}_{il} P_m -\n *                         \\tilde{\\gamma}_{im} P_l) +\n *       2 \\tilde{\\gamma}^{ml} (\n *         D_{jml} P_i + D_{jil} P_m - D_{jim} P_l)\\nonumber\\\\\n *       & - \\tilde{\\gamma}^{ml} (\n *         \\tilde{\\gamma}_{jl} \\partial_{(m} P_{i)} +\n *         \\tilde{\\gamma}_{il} \\partial_{(m} P_{j)} -\n *         \\tilde{\\gamma}_{ij} \\partial_{(m} P_{l)}) +\n *       \\tilde{\\gamma}^{ml} (\n *         \\tilde{\\gamma}_{ml} \\partial_{(j} P_{i)} +\n *         \\tilde{\\gamma}_{il} \\partial_{(j} P_{m)} -\n *         \\tilde{\\gamma}_{im} \\partial_{(j} P_{l)})\\nonumber\\\\\n *       & + \\Gamma^l_{ij} \\Gamma^m_{lm} - \\Gamma^l_{im} \\Gamma^m_{lj}\n * \\f}\n *\n * The argument `contracted_christoffel_second_kind` corresponds to the\n * \\f$\\Gamma^m_{lm}\\f$ term, the argument\n * `contracted_d_conformal_christoffel_difference` corresponds to the\n * \\f$\\partial_m \\tilde{\\Gamma}^m_{ij} - \\partial_j \\tilde{\\Gamma}^m_{im}\\f$\n * term, and the argument `contracted_field_d_up` corresponds to the\n * \\f$D_m{}^{ml}\\f$ term.\n *\n * \\note In second-order Ccz4, we impose symmetry of index i and j\n * in \\f$ \\partial_i P_j=\\partial_i \\partial_j \\ln \\phi \\f$,\n * because partial derivatives commute and to use\n * `second_partial_derivatives()`. \\f$ P_i \\f$ is evolved in\n * the first-order system so no such symmetry is imposed.\n */\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\nvoid spatial_ricci_tensor(\n    const gsl::not_null<tnsr::ii<DataType, Dim, Frame>*> result,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::i<DataType, Dim, Frame>& contracted_christoffel_second_kind,\n    const tnsr::ij<DataType, Dim, Frame>&\n        contracted_d_conformal_christoffel_difference,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up,\n    const tnsr::I<DataType, Dim, Frame>& contracted_field_d_up,\n    const tnsr::i<DataType, Dim, Frame>& field_p,\n    const TensorType& d_field_p);\n\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\ntnsr::ii<DataType, Dim, Frame> spatial_ricci_tensor(\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::i<DataType, Dim, Frame>& contracted_christoffel_second_kind,\n    const tnsr::ij<DataType, Dim, Frame>&\n        contracted_d_conformal_christoffel_difference,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up,\n    const tnsr::I<DataType, Dim, Frame>& contracted_field_d_up,\n    const tnsr::i<DataType, Dim, Frame>& field_p,\n    const TensorType& d_field_p);\n/// @}\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Ricci.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/Ricci.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\nvoid spatial_ricci_tensor(\n    const gsl::not_null<tnsr::ii<DataType, Dim, Frame>*> result,\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::i<DataType, Dim, Frame>& contracted_christoffel_second_kind,\n    const tnsr::ij<DataType, Dim, Frame>&\n        contracted_d_conformal_christoffel_difference,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up,\n    const tnsr::I<DataType, Dim, Frame>& contracted_field_d_up,\n    const tnsr::i<DataType, Dim, Frame>& field_p,\n    const TensorType& d_field_p) {\n  tenex::evaluate<ti::i, ti::j>(\n      result,\n      contracted_d_conformal_christoffel_difference(ti::i, ti::j) +\n          // Add terms of \\partial_m \\Gamma^m_{ij} and\n          // -\\partial_j \\Gamma^m_{im} that have a coefficient of 2\n          2.0 * (contracted_field_d_up(ti::L) *\n                     (conformal_spatial_metric(ti::j, ti::l) * field_p(ti::i) +\n                      conformal_spatial_metric(ti::i, ti::l) * field_p(ti::j) -\n                      conformal_spatial_metric(ti::i, ti::j) * field_p(ti::l)) -\n                 inverse_conformal_spatial_metric(ti::M, ti::L) *\n                     (field_d(ti::m, ti::j, ti::l) * field_p(ti::i) +\n                      field_d(ti::m, ti::i, ti::l) * field_p(ti::j) -\n                      field_d(ti::m, ti::i, ti::j) * field_p(ti::l)) -\n                 field_d_up(ti::j, ti::M, ti::L) *\n                     (conformal_spatial_metric(ti::m, ti::l) * field_p(ti::i) +\n                      conformal_spatial_metric(ti::i, ti::l) * field_p(ti::m) -\n                      conformal_spatial_metric(ti::i, ti::m) * field_p(ti::l)) +\n                 inverse_conformal_spatial_metric(ti::M, ti::L) *\n                     (field_d(ti::j, ti::m, ti::l) * field_p(ti::i) +\n                      field_d(ti::j, ti::i, ti::l) * field_p(ti::m) -\n                      field_d(ti::j, ti::i, ti::m) * field_p(ti::l))) -\n          // Add \\partial_{(i} P_{j)} type terms\n          0.5 * inverse_conformal_spatial_metric(ti::M, ti::L) *\n              (conformal_spatial_metric(ti::j, ti::l) *\n                   (d_field_p(ti::m, ti::i) + d_field_p(ti::i, ti::m)) +\n               conformal_spatial_metric(ti::i, ti::l) *\n                   (d_field_p(ti::m, ti::j) + d_field_p(ti::j, ti::m)) -\n               conformal_spatial_metric(ti::i, ti::j) *\n                   (d_field_p(ti::m, ti::l) + d_field_p(ti::l, ti::m)) -\n               conformal_spatial_metric(ti::m, ti::l) *\n                   (d_field_p(ti::j, ti::i) + d_field_p(ti::i, ti::j)) -\n               conformal_spatial_metric(ti::i, ti::l) *\n                   (d_field_p(ti::j, ti::m) + d_field_p(ti::m, ti::j)) +\n               conformal_spatial_metric(ti::i, ti::m) *\n                   (d_field_p(ti::j, ti::l) + d_field_p(ti::l, ti::j))) +\n          // Add last two terms for R_{ij}\n          christoffel_second_kind(ti::L, ti::i, ti::j) *\n              contracted_christoffel_second_kind(ti::l) -\n          christoffel_second_kind(ti::L, ti::i, ti::m) *\n              christoffel_second_kind(ti::M, ti::l, ti::j));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame, typename TensorType>\ntnsr::ii<DataType, Dim, Frame> spatial_ricci_tensor(\n    const tnsr::Ijj<DataType, Dim, Frame>& christoffel_second_kind,\n    const tnsr::i<DataType, Dim, Frame>& contracted_christoffel_second_kind,\n    const tnsr::ij<DataType, Dim, Frame>&\n        contracted_d_conformal_christoffel_difference,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& field_d,\n    const tnsr::iJJ<DataType, Dim, Frame>& field_d_up,\n    const tnsr::I<DataType, Dim, Frame>& contracted_field_d_up,\n    const tnsr::i<DataType, Dim, Frame>& field_p,\n    const TensorType& d_field_p) {\n  tnsr::ii<DataType, Dim, Frame> result{};\n  spatial_ricci_tensor(make_not_null(&result), christoffel_second_kind,\n                       contracted_christoffel_second_kind,\n                       contracted_d_conformal_christoffel_difference,\n                       conformal_spatial_metric,\n                       inverse_conformal_spatial_metric, field_d, field_d_up,\n                       contracted_field_d_up, field_p, d_field_p);\n  return result;\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define TTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void Ccz4::spatial_ricci_tensor(                               \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                         \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          christoffel_second_kind,                                        \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          contracted_christoffel_second_kind,                             \\\n      const tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          contracted_d_conformal_christoffel_difference,                  \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          conformal_spatial_metric,                                       \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_conformal_spatial_metric,                               \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& field_d,      \\\n      const tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>& field_d_up,   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          contracted_field_d_up,                                          \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& field_p,        \\\n      const TTYPE(data)<DTYPE(data), DIM(data), FRAME(data)>& d_field_p); \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  Ccz4::spatial_ricci_tensor(                                             \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          christoffel_second_kind,                                        \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          contracted_christoffel_second_kind,                             \\\n      const tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          contracted_d_conformal_christoffel_difference,                  \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          conformal_spatial_metric,                                       \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_conformal_spatial_metric,                               \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& field_d,      \\\n      const tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>& field_d_up,   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          contracted_field_d_up,                                          \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& field_p,        \\\n      const TTYPE(data)<DTYPE(data), DIM(data), FRAME(data)>& d_field_p);\n\n// Instantiations are split into several compilation units to reduce\n// compiler memory consumption.\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/RicciScalarPlusDivergenceZ4Constraint.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/RicciScalarPlusDivergenceZ4Constraint.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid ricci_scalar_plus_divergence_z4_constraint(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_ricci_tensor,\n    const tnsr::ij<DataType, Dim, Frame>& grad_spatial_z4_constraint) {\n  ::tenex::evaluate(result, conformal_factor_squared() *\n                                inverse_conformal_spatial_metric(ti::I, ti::J) *\n                                (spatial_ricci_tensor(ti::i, ti::j) +\n                                 grad_spatial_z4_constraint(ti::i, ti::j) +\n                                 grad_spatial_z4_constraint(ti::j, ti::i)));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> ricci_scalar_plus_divergence_z4_constraint(\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_ricci_tensor,\n    const tnsr::ij<DataType, Dim, Frame>& grad_spatial_z4_constraint) {\n  Scalar<DataType> result{};\n  ricci_scalar_plus_divergence_z4_constraint(\n      make_not_null(&result), conformal_factor_squared,\n      inverse_conformal_spatial_metric, spatial_ricci_tensor,\n      grad_spatial_z4_constraint);\n  return result;\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                      \\\n  template void Ccz4::ricci_scalar_plus_divergence_z4_constraint( \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result,           \\\n      const Scalar<DTYPE(data)>& conformal_factor_squared,        \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&        \\\n          inverse_conformal_spatial_metric,                       \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&        \\\n          spatial_ricci_tensor,                                   \\\n      const tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>&        \\\n          grad_spatial_z4_constraint);                            \\\n  template Scalar<DTYPE(data)>                                    \\\n  Ccz4::ricci_scalar_plus_divergence_z4_constraint(               \\\n      const Scalar<DTYPE(data)>& conformal_factor_squared,        \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&        \\\n          inverse_conformal_spatial_metric,                       \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&        \\\n          spatial_ricci_tensor,                                   \\\n      const tnsr::ij<DTYPE(data), DIM(data), FRAME(data)>&        \\\n          grad_spatial_z4_constraint);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/RicciScalarPlusDivergenceZ4Constraint.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\n/// @{\n/*!\n * \\brief Computes the sum of the Ricci scalar and twice the divergence of the\n * upper spatial Z4 constraint\n *\n * \\details Computes the expression as:\n *\n * \\f{align}\n *     R + 2 \\nabla_k Z^k &=\n *         \\phi^2 \\tilde{\\gamma}^{ij} (R_{ij} + \\nabla_i Z_j + \\nabla_j Z_i)\n * \\f}\n *\n * where \\f$R\\f$ is the spatial Ricci scalar, \\f$Z^i\\f$ is the upper spatial Z4\n * constraint defined by `Ccz4::Tags::Z4ConstraintUp`, \\f$phi^2\\f$ is the square\n * of the conformal factor defined by `Ccz4::Tags::ConformalFactorSquared`,\n * \\f$\\tilde{\\gamma}^{ij}\\f$ is the inverse conformal spatial metric defined by\n * `Ccz4::Tags::InverseConformalMetric`, \\f$R_{ij}\\f$ is the spatial Ricci\n * tensor defined by `Ccz4::Tags::Ricci`, and \\f$\\nabla_j Z_i\\f$ is the gradient\n * of the spatial Z4 constraint defined by `Ccz4::Tags::GradZ4Constraint`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid ricci_scalar_plus_divergence_z4_constraint(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_ricci_tensor,\n    const tnsr::ij<DataType, Dim, Frame>& grad_spatial_z4_constraint);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> ricci_scalar_plus_divergence_z4_constraint(\n    const Scalar<DataType>& conformal_factor_squared,\n    const tnsr::II<DataType, Dim, Frame>& inverse_conformal_spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_ricci_tensor,\n    const tnsr::ij<DataType, Dim, Frame>& grad_spatial_z4_constraint);\n/// @}\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Solutions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/Ccz4/Ccz4WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::Solutions {\n/// \\brief List of all Ccz4 analytic solutions\n/// Right now it only makes sense to do TrumpetSchwarzschild or time-independent\n/// solutions because we can either use 1+log slicing with Gamma-driver\n/// or not evolve the lapse and shift at all. We will allow analytic\n/// lapse and shift in the future.\nusing all_solutions =\n    tmpl::list<Ccz4WrappedGr<gr::Solutions::KerrSchild>,\n               Ccz4WrappedGr<gr::Solutions::Minkowski<3>>,\n               Ccz4WrappedGr<gr::Solutions::TrumpetSchwarzschild>>;\n}  // namespace Ccz4::Solutions\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\ingroup EvolutionSystemsGroup\n * \\brief Items related to evolving the first-order CCZ4 system.\n */\nnamespace Ccz4 {}\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Ccz4/TagsDeclarations.hpp\"\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n\nnamespace Ccz4 {\nnamespace OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * Groups option tags related to the CCZ4 evolution system.\n */\nstruct Ccz4Group {\n  static std::string name() { return \"Ccz4\"; }\n  static constexpr Options::String help{\n      \"Options for the CCZ4 evolution system\"};\n  using group = evolution::OptionTags::SystemGroup;\n};\n\n/// \\copydoc Tags::Kappa1\nstruct Kappa1 {\n  using type = double;\n\n  static constexpr Options::String help = {\n      \"The constraint damping parameter kappa_1.\"};\n  using group = Ccz4Group;\n};\n\n/// \\copydoc Tags::Kappa2\nstruct Kappa2 {\n  using type = double;\n\n  static constexpr Options::String help = {\n      \"The constraint damping parameter kappa_2.\"};\n  using group = Ccz4Group;\n};\n\n/// \\copydoc Tags::Kappa3\nstruct Kappa3 {\n  using type = double;\n\n  static constexpr Options::String help = {\n      \"The constraint damping parameter kappa_3.\"};\n  using group = Ccz4Group;\n};\n}  // namespace OptionTags\n\n/*!\n * \\brief Tags for the CCZ4 formulation of Einstein equations.\n * \\details The naming convention follows \\cite Dumbser2017okk\n * eq. 12a-12m.\n */\nnamespace Tags {\n/*!\n * \\brief The conformal factor that rescales the spatial metric\n *\n * \\details If \\f$\\gamma_{ij}\\f$ is the spatial metric, then we define\n * \\f$\\phi = (det(\\gamma_{ij}))^{-1/6}\\f$.\n */\ntemplate <typename DataType>\nstruct ConformalFactor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The square of the conformal factor that rescales the spatial metric\n *\n * \\details If \\f$\\gamma_{ij}\\f$ is the spatial metric, then we define\n * \\f$\\phi^2 = (det(\\gamma_{ij}))^{-1/3}\\f$.\n */\ntemplate <typename DataType>\nstruct ConformalFactorSquared : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The conformally scaled spatial metric\n *\n * \\details If \\f$\\phi\\f$ is the conformal factor and \\f$\\gamma_{ij}\\f$ is the\n * spatial metric, then we define\n * \\f$\\bar{\\gamma}_{ij} = \\phi^2 \\gamma_{ij}\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nusing ConformalMetric =\n    gr::Tags::Conformal<gr::Tags::SpatialMetric<DataType, Dim, Fr>, 2>;\n\n/*!\n * \\brief The conformally scaled inverse spatial metric\n *\n * \\details If \\f$\\phi\\f$ is the conformal factor and \\f$\\gamma^{ij}\\f$ is the\n * inverse spatial metric, then we define\n * \\f$\\bar{\\gamma}^{ij} = \\phi^{-2} \\gamma^{ij}\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nusing InverseConformalMetric =\n    gr::Tags::Conformal<gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>,\n                        -2>;\n\n/*!\n * \\brief The trace-free part of the extrinsic curvature\n *\n * \\details See `Ccz4::a_tilde()` for details.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ATilde : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The trace of the trace-free part of the extrinsic curvature\n *\n * \\details We define:\n *\n * \\f{align}\n *     tr\\tilde{A} &= \\tilde{\\gamma}^{ij} \\tilde{A}_{ij}\n * \\f}\n *\n * where \\f$\\tilde{\\gamma}^{ij}\\f$ is the inverse conformal spatial metric\n * defined by `Ccz4::Tags::InverseConformalMetric` and \\f$\\tilde{A}_{ij}\\f$ is\n * the trace-free part of the extrinsic curvature defined by\n * `Ccz4::Tags::ATilde`.\n */\ntemplate <typename DataType>\nstruct TraceATilde : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The natural log of the lapse\n */\ntemplate <typename DataType>\nstruct LogLapse : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief Auxiliary variable which is analytically the spatial derivative of the\n * natural log of the lapse\n *\n * \\details If \\f$ \\alpha \\f$ is the lapse, then we define\n * \\f$A_i = \\partial_i ln(\\alpha) = \\frac{\\partial_i \\alpha}{\\alpha}\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct FieldA : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Auxiliary variable which is analytically the spatial derivative of the\n * shift\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct FieldB : db::SimpleTag {\n  using type = tnsr::iJ<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Auxiliary variable which is analytically half the spatial derivative\n * of the conformal spatial metric\n *\n * \\details If \\f$\\bar{\\gamma}_{ij}\\f$ is the conformal spatial metric, then we\n * define\n * \\f$D_{kij} = \\frac{1}{2} \\partial_k \\bar{\\gamma}_{ij}\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct FieldD : db::SimpleTag {\n  using type = tnsr::ijj<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The natural log of the conformal factor\n */\ntemplate <typename DataType>\nstruct LogConformalFactor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief Auxiliary variable which is analytically the spatial derivative of the\n * natural log of the conformal factor\n *\n * \\details If \\f$\\phi\\f$ is the conformal factor, then we define\n * \\f$P_i = \\partial_i ln(\\phi) = \\frac{\\partial_i \\phi}{\\phi}\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct FieldP : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Identity which is analytically negative one half the spatial\n * derivative of the inverse conformal spatial metric\n *\n * \\details We define:\n * \\f{align}\n *     D_k{}^{ij} &=\n *         \\tilde{\\gamma}^{in} \\tilde{\\gamma}^{mj} D_{knm} =\n *         -\\frac{1}{2} \\partial_k \\tilde{\\gamma}^{ij}\n * \\f}\n * where \\f$\\tilde{\\gamma}^{ij}\\f$ and \\f$D_{ijk}\\f$ are the inverse conformal\n * spatial metric and the CCZ4 auxiliary variable defined by\n * `Ccz4::Tags::FieldD`, respectively.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct FieldDUp : db::SimpleTag {\n  using type = tnsr::iJJ<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The conformal spatial christoffel symbols of the second kind\n *\n * \\details We define:\n * \\f{align}\n *     \\tilde{\\Gamma}^k_{ij} &=\n *         \\tilde{\\gamma}^{kl} (D_{ijl} + D_{jil} - D_{lij})\n * \\f}\n * where \\f$\\tilde{\\gamma}^{ij}\\f$ and \\f$D_{ijk}\\f$ are the inverse conformal\n * spatial metric and the CCZ4 auxiliary variable defined by\n * `Ccz4::Tags::InverseConformalMetric` and `Ccz4::Tags::FieldD`, respectively.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ConformalChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::Ijj<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The spatial derivative of the conformal spatial christoffel symbols\n * of the second kind\n *\n * \\details We define:\n * \\f{align}\n *     \\partial_k \\tilde{\\Gamma}^m{}_{ij} &=\n *       -2 D_k{}^{ml} (D_{ijl} + D_{jil} - D_{lij}) +\n *       \\tilde{\\gamma}^{ml}(\\partial_{(k} D_{i)jl} + \\partial_{(k} D_{j)il} -\n *       \\partial_{(k} D_{l)ij})\n * \\f}\n * where \\f$\\tilde{\\gamma}^{ij}\\f$, \\f$D_{ijk}\\f$, \\f$\\partial_l D_{ijk}\\f$, and\n * \\f$D_k{}^{ij}\\f$ are the inverse conformal spatial metric defined by\n * `Ccz4::Tags::InverseConformalMetric`, the CCZ4 auxiliary variable defined by\n * `Ccz4::Tags::FieldD`, its spatial derivative, and the CCZ4 identity defined\n * by `Ccz4::Tags::FieldDUp`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct DerivConformalChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::iJkk<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The spatial christoffel symbols of the second kind\n *\n * \\details We define:\n * \\f{align}\n *     \\Gamma^k_{ij} &= \\tilde{\\Gamma}^k_{ij} -\n *         \\tilde{\\gamma}^{kl} (\\tilde{\\gamma}_{jl} P_i +\n *                              \\tilde{\\gamma}_{il} P_j -\n *                              \\tilde{\\gamma}_{ij} P_l)\n * \\f}\n * where \\f$\\tilde{\\gamma}^{ij}\\f$, \\f$\\tilde{\\gamma}_{ij}\\f$,\n * \\f$\\tilde{\\Gamma}^k_{ij}\\f$, and \\f$P_i\\f$ are the conformal spatial metric,\n * the inverse conformal spatial metric, the conformal spatial christoffel\n * symbols of the second kind, and the CCZ4 auxiliary variable defined by\n * `Ccz4::Tags::ConformalMetric`, `Ccz4::Tags::InverseConformalMetric`,\n * `Ccz4::Tags::ConformalChristoffelSecondKind`, and `Ccz4::Tags::FieldP`,\n * respectively.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::Ijj<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The spatial Ricci tensor\n *\n * \\details See `Ccz4::spatial_ricci_tensor()` for details.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Ricci : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The gradient of the gradient of the lapse\n *\n * \\details We define:\n * \\f{align}\n *     \\nabla_i \\nabla_j \\alpha &= \\alpha A_i A_j -\n *                 \\alpha \\Gamma^k{}_{ij} A_k + \\alpha \\partial_{(i} A_{j)}\n * \\f}\n * where \\f$\\alpha\\f$, \\f$\\Gamma^k{}_{ij}\\f$, \\f$A_i\\f$, and\n * \\f$\\partial_j A_i\\f$ are the lapse, spatial christoffel symbols of the second\n * kind, the CCZ4 auxiliary variable defined by `Ccz4::Tags::FieldA`, and its\n * spatial derivative, respectively.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct GradGradLapse : db::SimpleTag {\n  using type = tnsr::ij<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The divergence of the lapse\n *\n * \\details We define:\n * \\f{align}\n *     \\nabla^i \\nabla_i \\alpha &= \\phi^2 \\tilde{\\gamma}^{ij}\n *         (\\nabla_i \\nabla_j \\alpha)\n * \\f}\n * where \\f$\\phi\\f$, \\f$\\tilde{\\gamma}^{ij}\\f$, and\n * \\f$\\nabla_i \\nabla_j \\alpha\\f$ are the conformal factor, inverse conformal\n * spatial metric, and the gradient of the gradient of the lapse defined by\n * `Ccz4::Tags::ConformalFactor`, `Ccz4::Tags::InverseConformalMetric`, and\n * `Ccz4::Tags::GradGradLapse`, respectively.\n */\ntemplate <typename DataType>\nstruct DivergenceLapse : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The contraction of the conformal spatial Christoffel symbols of the\n * second kind\n *\n * \\details See `Ccz4::contracted_conformal_christoffel_second_kind()` for\n * details.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ContractedConformalChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The spatial derivative of the contraction of the conformal spatial\n * Christoffel symbols of the second kind\n *\n * \\details See `Ccz4::deriv_contracted_conformal_christoffel_second_kind()` for\n * details.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct DerivContractedConformalChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::iJ<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 evolved variable \\f$\\hat{\\Gamma}^i\\f$\n *\n * \\details This must satisfy the identity:\n *\n * \\f{align}\n *     \\hat{\\Gamma}^i &= \\tilde{\\Gamma}^i + 2 \\tilde{\\gamma}^{ij} Z_j\n * \\f}\n *\n * where \\f$\\tilde{\\gamma}^{ij}\\f$ is the inverse conformal spatial metric\n * defined by `Ccz4::Tags::InverseConformalMetric`, \\f$Z_i\\f$ is the spatial\n * part of the Z4 constraint defined by `Ccz4::Tags::SpatialZ4Constraint`, and\n * \\f$\\tilde{\\Gamma}^i\\f$ is the contraction of the conformal spatial\n * christoffel symbols of the second kind defined by\n * `Ccz4::Tags::ContractedConformalChristoffelSecondKind`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct GammaHat : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The spatial part of the Z4 constraint\n *\n * \\details See `Ccz4::spatial_z4_constraint` for details.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpatialZ4Constraint : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The spatial part of the upper Z4 constraint\n *\n * \\details See `Ccz4::upper_spatial_z4_constraint` for details.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpatialZ4ConstraintUp : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The gradient of the spatial part of the Z4 constraint\n *\n * \\details See `Ccz4::grad_spatial_z4_constraint` for details.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct GradSpatialZ4Constraint : db::SimpleTag {\n  using type = tnsr::ij<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The sum of the Ricci scalar and twice the divergence of the upper\n * spatial Z4 constraint\n *\n * \\details See `Ccz4::ricci_scalar_plus_divergence_z4_constraint` for details.\n */\ntemplate <typename DataType>\nstruct RicciScalarPlusDivergenceZ4Constraint : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The projection of the Z4 constraint vector along the normal direction.\n *\n *\n */\ntemplate <typename DataType>\nstruct Theta : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief Auxiliary variable b in the gamma-driver condition\n * eq. 12c of \\cite Dumbser2017okk.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct AuxiliaryShiftB : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The parameter f in the gamma-driver condition\n * eq. 12c of \\cite Dumbser2017okk.\n *\n */\nstruct GammaDriverParam\n    : db::SimpleTag {\n  using type = double;\n};\n\n/*!\n * \\brief Free parameter \\f$ kappa_1 \\f$ related to\n * constraint damping\n * in eq. 12f of \\cite Dumbser2017okk.\n */\nstruct Kappa1 : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::Kappa1>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const double kappa_1) { return kappa_1; }\n};\n\n/*!\n * \\brief Free parameter \\f$ kappa_2 \\f$ related to\n * constraint damping\n * in eq. 12f of \\cite Dumbser2017okk.\n */\nstruct Kappa2 : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::Kappa2>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const double kappa_2) { return kappa_2; }\n};\n\n/*!\n * \\brief Free parameter \\f$ kappa_3 \\f$ related to\n * constraint damping\n * in eq. 12h of \\cite Dumbser2017okk.\n */\nstruct Kappa3 : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::Kappa3>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const double kappa_3) { return kappa_3; }\n};\n\n/*!\n * \\brief Free parameter \\f$ K_0 \\f$ in 1+log slicing\n * in eq. 12b of \\cite Dumbser2017okk.\n */\ntemplate <typename DataType>\nstruct K0 : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The parameter \\f$ \\eta \\f$ in the gamma-driver\n * condition eq. 12i of \\cite Dumbser2017okk.\n *\n */\ntemplate <typename DataType>\nstruct Eta : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n}  // namespace Tags\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n\nclass DataVector;\n\nnamespace Ccz4 {\n/// \\brief Tags for the CCZ4 formulation of Einstein equations\nnamespace Tags {\n// Quantities of interest\ntemplate <typename DataType>\nstruct ConformalFactor;\ntemplate <typename DataType>\nstruct ConformalFactorSquared;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ATilde;\ntemplate <typename DataType>\nstruct TraceATilde;\ntemplate <typename DataType>\nstruct LogLapse;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct FieldA;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct FieldB;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct FieldD;\ntemplate <typename DataType>\nstruct LogConformalFactor;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct FieldP;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct FieldDUp;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ConformalChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct DerivConformalChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct Ricci;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct GradGradLapse;\ntemplate <typename DataType>\nstruct DivergenceLapse;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ContractedConformalChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct DerivContractedConformalChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct GammaHat;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpatialZ4Constraint;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpatialZ4ConstraintUp;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct GradSpatialZ4Constraint;\ntemplate <typename DataType>\nstruct RicciScalarPlusDivergenceZ4Constraint;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct AuxiliaryShiftB;\ntemplate <typename DataType>\nstruct K0;\ntemplate <typename DataType>\nstruct Eta;\nstruct GammaDriverParam;\nstruct Kappa1;\nstruct Kappa2;\nstruct Kappa3;\nstruct EvolveLapseAndShift;\nstruct ConstrainedEvolution;\nstruct KreissOligerEpsilon;\n\n// Temporary expressions for computing above quantities of interest\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct GammaHatMinusContractedConformalChristoffel;\ntemplate <typename DataType>\nstruct KMinus2ThetaC;\ntemplate <typename DataType>\nstruct KMinusK0Minus2ThetaC;\ntemplate <typename DataType>\nstruct ContractedFieldB;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ConformalMetricTimesFieldB;\ntemplate <typename DataType>\nstruct LapseTimesRicciScalarPlus2DivergenceZ4Constraint;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ConformalMetricTimesTraceATilde;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct LapseTimesATilde;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct FieldDUpTimesATilde;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct LapseTimesDerivATilde;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct InverseConformalMetricTimesDerivATilde;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ATildeMinusOneThirdConformalMetricTimesTraceATilde;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct LapseTimesFieldA;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ShiftTimesDerivGammaHat;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct InverseTauTimesConformalMetric;\ntemplate <typename DataType>\nstruct LapseTimesSlicingCondition;\ntemplate <typename DataType>\nstruct DetConformalSpatialMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct InvATilde;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ATildeTimesFieldB;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SymmetrizedDerivFieldB;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ContractedSymmetrizedDerivFieldB;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ContractedFieldDUp;\ntemplate <typename DataType>\nstruct HalfConformalFactorSquared;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct DerivGammaHatMinusContractedConformalChristoffel;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ContractedChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ContractedDerivConformalChristoffelDifference;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct LapseTimesConformalMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpatialRicciTensor;\n}  // namespace Tags\n\n/// \\brief Input option tags for the CCZ4 evolution system\nnamespace OptionTags {\nstruct Group;\n}  // namespace OptionTags\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/TempTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Ccz4/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace Ccz4 {\nnamespace Tags {\n/*!\n * \\brief The CCZ4 temporary expression\n * \\f$\\hat{\\Gamma}^i - \\tilde{\\Gamma}^i\\f$\n *\n * \\details We define:\n *\n * \\f{align}\n *     \\hat{\\Gamma}^i - \\tilde{\\Gamma}^i &= 2 \\tilde{\\gamma}^{ij} Z_j\n * \\f}\n *\n * where \\f$\\hat{\\Gamma}^{i}\\f$ is the CCZ4 evolved variable defined by\n * `Ccz4::Tags::GammaHat`, \\f$\\tilde{\\Gamma}^{i}\\f$ is the contraction of the\n * conformal spatial Christoffel symbols of the second kind defined by\n * `Ccz4::Tags::ContractedConformalChristoffelSecondKind`,\n * \\f$\\tilde{\\gamma}^{ij}\\f$ is the inverse conformal spatial metric defined by\n * `Ccz4::Tags::InverseConformalMetric`, and \\f$Z_i\\f$ is the spatial part of\n * the Z4 constraint defined by `Ccz4::Tags::SpatialZ4Constraint`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct GammaHatMinusContractedConformalChristoffel : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$K - 2 \\Theta c\\f$\n *\n * \\details Here, \\f$K\\f$ is the trace of the extrinsic curvature defined by\n * `gr::Tags::TraceExtrinsicCurvature`, \\f$\\Theta\\f$ is the projection of the Z4\n * four-vector along the normal direction, and \\f$c\\f$ controls whether to\n * include algebraic source terms proportional to \\f$\\Theta\\f$.\n */\ntemplate <typename DataType>\nstruct KMinus2ThetaC : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$K - K_0 - 2 \\Theta c\\f$\n *\n * \\details Here, \\f$K\\f$ is the trace of the extrinsic curvature defined by\n * `gr::Tags::TraceExtrinsicCurvature`, \\f$K_0\\f$ is the initial time derivative\n * of the lapse, \\f$\\Theta\\f$ is the projection of the Z4 four-vector along the\n * normal direction, and \\f$c\\f$ controls whether to include algebraic source\n * terms proportional to \\f$\\Theta\\f$.\n */\ntemplate <typename DataType>\nstruct KMinusK0Minus2ThetaC : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$B_k{}^k\\f$\n *\n * \\details Here, \\f$B_k{}^k\\f$ is the contraction of the CCZ4 auxiliary\n * variable defined by `Ccz4::Tags::FieldB`.\n */\ntemplate <typename DataType>\nstruct ContractedFieldB : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\tilde{\\gamma}_{ki} B_j{}^k\\f$\n *\n * \\details Here, \\f$\\tilde{\\gamma}_{ij}\\f$ is the conformal spatial metric\n * defined by `Ccz4::Tags::ConformalMetric` and \\f$B_i{}^j\\f$ is the CCZ4\n * auxiliary variable defined by `Ccz4::Tags::FieldB`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ConformalMetricTimesFieldB : db::SimpleTag {\n  using type = tnsr::ij<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\alpha (R + 2 \\nabla_k Z^k)\\f$\n *\n * \\details Here, \\f$\\alpha\\f$ is the lapse defined by `gr::Tags::Lapse` and\n * \\f$(R + 2 \\nabla_k Z^k)\\f$ is the Ricci scalar plus twice the divergence of\n * the spatial Z4 constraint defined by\n * `Ccz4::Tags::RicciScalarPlusDivergenceZ4Constraint`.\n */\ntemplate <typename DataType>\nstruct LapseTimesRicciScalarPlus2DivergenceZ4Constraint : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\tilde{\\gamma}_{ij} tr \\tilde{A}\\f$\n *\n * \\details Here, \\f$\\tilde{\\gamma}_{ij}\\f$ is the conformal spatial metric\n * defined by `Ccz4::Tags::ConformalMetric` and \\f$tr \\tilde{A}\\f$ is the trace\n * of the trace-free part of the extrinsic curvature defined by\n * `Ccz4::Tags::TraceATilde`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ConformalMetricTimesTraceATilde : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\alpha \\tilde{A}_{ij}\\f$\n *\n * \\details Here, \\f$\\alpha\\f$ is the lapse defined by `gr::Tags::Lapse` and\n * \\f$\\tilde{A}_{ij}\\f$ is the trace-free part of the extrinsic curvature\n * defined by `Ccz4::Tags::ATilde`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct LapseTimesATilde : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$D_k{}^{nm} \\tilde{A}_{nm}\\f$\n *\n * \\details Here, \\f$D_k{}^{nm}\\f$ is analytically negative one half the spatial\n * derivative of the inverse conformal spatial metric defined by\n * `Ccz4::Tags::FieldDUp` and \\f$\\tilde{A}_{nm}\\f$ is the trace-free part of the\n * extrinsic curvature defined by `Ccz4::Tags::ATilde`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct FieldDUpTimesATilde : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\alpha \\partial_k \\tilde{A}_{ij}\\f$\n *\n * \\details Here, \\f$\\alpha\\f$ is the lapse defined by `gr::Tags::Lapse` and\n * \\f$\\tilde{A}_{ij}\\f$ is the trace-free part of the extrinsic curvature\n * defined by `Ccz4::Tags::ATilde`, and \\f$\\partial_k \\tilde{A}_{ij}\\f$ is its\n * spatial derivative.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct LapseTimesDerivATilde : db::SimpleTag {\n  using type = tnsr::ijj<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression\n * \\f$\\tilde{\\gamma}^{nm} \\partial_k \\tilde{A}_{nm}\\f$\n *\n * \\details Here, \\f$\\tilde{\\gamma}^{nm}\\f$ is the inverse conformal spatial\n * metric defined by `Ccz4::Tags::InverseConformalMetric`, \\f$\\tilde{A}_{ij}\\f$\n * is the trace-free part of the extrinsic curvature defined by\n * `Ccz4::Tags::ATilde`, and \\f$\\partial_k \\tilde{A}_{ij}\\f$ is its spatial\n * derivative.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct InverseConformalMetricTimesDerivATilde : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression\n * \\f$\\tilde{A}_{ij} - \\frac{1}{3} \\tilde{\\gamma}_{ij} tr \\tilde{A}\\f$\n *\n * \\details Here, \\f$\\tilde{A}_{ij}\\f$ is the trace-free part of the extrinsic\n * curvature defined by `Ccz4::Tags::ATilde`, \\f$tr \\tilde{A}\\f$ is its trace\n * defined by `Ccz4::Tags::TraceATilde`, and \\f$\\tilde{\\gamma}_{ij}\\f$ is the\n * conformal spatial metric defined by `Ccz4::Tags::ConformalMetric`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ATildeMinusOneThirdConformalMetricTimesTraceATilde : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\alpha A_k\\f$\n *\n * \\details Here, \\f$\\alpha\\f$ is the lapse defined by `gr::Tags::Lapse` and\n * \\f$A_k\\f$ is the CCZ4 auxiliary variable defined by `Ccz4::Tags::FieldA`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct LapseTimesFieldA : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\beta^k \\partial_k \\hat{\\Gamma}^i\\f$\n *\n * \\details Here, \\f$\\beta^k\\f$ is the shift defined by `gr::Tags::Shift`,\n * \\f$\\hat{\\Gamma}^i\\f$ is the CCZ4 evolved variable defined by\n * `Ccz4::Tags::GammaHat`, and \\f$\\partial_k \\hat{\\Gamma}^i\\f$ is its spatial\n * derivative.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ShiftTimesDerivGammaHat : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\tau^{-1} \\tilde{\\gamma}_{ij}\\f$\n *\n * \\details Here, \\f$\\tilde{\\gamma}_{ij}\\f$ is the conformal spatial metric\n * defined by `Ccz4::Tags::ConformalMetric` and \\f$\\tau\\f$ is the relaxation\n * time to enforce the algebraic constraints on the determinant of the conformal\n * spatial metric and on the trace of the trace-free part of the extrinsic\n * curvature that is defined by `Ccz4::Tags::ATilde`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct InverseTauTimesConformalMetric : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$\\alpha g(\\alpha)\\f$\n *\n * \\details Here, \\f$\\alpha\\f$ is the lapse defined by `gr::Tags::Lapse` and\n * \\f$g(\\alpha)\\f$ is a constant that controls the slicing conditions.\n * \\f$g(\\alpha) = 1\\f$ leads to harmonic slicing and\n * \\f$g(\\alpha) = 2 / \\alpha\\f$ leads to 1 + log slicing.\n */\ntemplate <typename DataType>\nstruct LapseTimesSlicingCondition : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression\n * (\\f$ \\mathrm{det}\\tilde{\\gamma}_{ij} \\f$)\n */\ntemplate <typename DataType>\nstruct DetConformalSpatialMetric : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$ \\tilde{A}^{ij} \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct InvATilde : db::SimpleTag {\n  using type = tnsr::II<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$ \\tilde{A}_{ki}\\partial_j\\beta^k \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ATildeTimesFieldB : db::SimpleTag {\n  using type = tnsr::ij<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$ \\partial_k\\partial_l\\beta^i \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SymmetrizedDerivFieldB : db::SimpleTag {\n  using type = tnsr::ijK<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$ \\partial_k\\partial_l\\beta^l \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ContractedSymmetrizedDerivFieldB : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression\n * \\f$ \\tilde{\\gamma}^{kn}\\tilde{\\gamma}^{mj}D_{knm} \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ContractedFieldDUp : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$ (1/2)\\phi^2 \\f$\n */\ntemplate <typename DataType>\nstruct HalfConformalFactorSquared : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression\n * \\f$ \\hat{\\Gamma}^l-\\tilde{\\Gamma}^l \\f$ in eq. (26) of \\cite Dumbser2017okk\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct DerivGammaHatMinusContractedConformalChristoffel : db::SimpleTag {\n  using type = tnsr::iJ<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$ \\Gamma^k_{ik} \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ContractedChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression\n * \\f$ \\partial_m\\tilde{\\Gamma}^m_{ij}-\\partial_j\\tilde{Gamma}^m_{im} \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ContractedDerivConformalChristoffelDifference : db::SimpleTag {\n  using type = tnsr::ij<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$ \\alpha\\tilde{\\gamma}_{ij} \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct LapseTimesConformalMetric : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The CCZ4 temporary expression \\f$ R_{ij} \\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpatialRicciTensor : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n}  // namespace Tags\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/TimeDerivative.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivChristoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivLapse.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivZ4Constraint.hpp\"\n#include \"Evolution/Systems/Ccz4/Ricci.hpp\"\n#include \"Evolution/Systems/Ccz4/RicciScalarPlusDivergenceZ4Constraint.hpp\"\n#include \"Evolution/Systems/Ccz4/Z4Constraint.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\ntemplate <size_t Dim>\nvoid TimeDerivative<Dim>::apply(\n    // LHS time derivatives of evolved variables: eq 12a - 12m\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        dt_conformal_spatial_metric,                                  // eq 12a\n    const gsl::not_null<Scalar<DataVector>*> dt_ln_lapse,             // eq 12b\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_shift,          // eq 12c\n    const gsl::not_null<Scalar<DataVector>*> dt_ln_conformal_factor,  // eq 12d\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*> dt_a_tilde,       // eq 12e\n    const gsl::not_null<Scalar<DataVector>*>\n        dt_trace_extrinsic_curvature,                             // eq 12f\n    const gsl::not_null<Scalar<DataVector>*> dt_theta,            // eq 12g\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_gamma_hat,  // eq 12h\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_b,          // eq 12i\n    const gsl::not_null<tnsr::i<DataVector, Dim>*> dt_field_a,    // eq 12j\n    const gsl::not_null<tnsr::iJ<DataVector, Dim>*> dt_field_b,   // eq 12k\n    const gsl::not_null<tnsr::ijj<DataVector, Dim>*> dt_field_d,  // eq 12l\n    const gsl::not_null<tnsr::i<DataVector, Dim>*> dt_field_p,    // eq 12m\n    // quantities we need for computing eq 12 - 27\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor_squared,\n    const gsl::not_null<Scalar<DataVector>*> det_conformal_spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, Dim>*>\n        inv_conformal_spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, Dim>*> inv_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<Scalar<DataVector>*> slicing_condition,    // g(\\alpha)\n    const gsl::not_null<Scalar<DataVector>*> d_slicing_condition,  // g'(\\alpha)\n    const gsl::not_null<tnsr::II<DataVector, Dim>*> inv_a_tilde,\n    // temporary expressions\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*> a_tilde_times_field_b,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde,\n    const gsl::not_null<Scalar<DataVector>*> contracted_field_b,\n    const gsl::not_null<tnsr::ijK<DataVector, Dim>*> symmetrized_d_field_b,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*>\n        contracted_symmetrized_d_field_b,\n    const gsl::not_null<tnsr::ijk<DataVector, Dim>*> field_b_times_field_d,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*> field_d_up_times_a_tilde,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        contracted_field_d_up,  // temp for eq 18 -20\n    const gsl::not_null<Scalar<DataVector>*>\n        half_conformal_factor_squared,  // temp for eq 25\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n        conformal_metric_times_field_b,\n    const gsl::not_null<tnsr::ijk<DataVector, Dim>*>\n        conformal_metric_times_symmetrized_d_field_b,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        conformal_metric_times_trace_a_tilde,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*>\n        inv_conformal_metric_times_d_a_tilde,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        gamma_hat_minus_contracted_conformal_christoffel,\n    const gsl::not_null<tnsr::iJ<DataVector, Dim>*>\n        d_gamma_hat_minus_contracted_conformal_christoffel,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*>\n        contracted_christoffel_second_kind,  // temp for eq 18 -20\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n        contracted_d_conformal_christoffel_difference,  // temp for eq 18 -20\n    const gsl::not_null<Scalar<DataVector>*> k_minus_2_theta_c,\n    const gsl::not_null<Scalar<DataVector>*> k_minus_k0_minus_2_theta_c,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*> lapse_times_a_tilde,\n    const gsl::not_null<tnsr::ijj<DataVector, Dim>*> lapse_times_d_a_tilde,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*> lapse_times_field_a,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        lapse_times_conformal_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> lapse_times_slicing_condition,\n    const gsl::not_null<Scalar<DataVector>*>\n        lapse_times_ricci_scalar_plus_divergence_z4_constraint,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> shift_times_deriv_gamma_hat,\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        inv_tau_times_conformal_metric,\n    // expressions and identities needed for evolution equations: eq 13 - 27\n    const gsl::not_null<Scalar<DataVector>*> trace_a_tilde,       // eq 13\n    const gsl::not_null<tnsr::iJJ<DataVector, Dim>*> field_d_up,  // eq 14\n    const gsl::not_null<tnsr::Ijj<DataVector, Dim>*>\n        conformal_christoffel_second_kind,  // eq 15\n    const gsl::not_null<tnsr::iJkk<DataVector, Dim>*>\n        d_conformal_christoffel_second_kind,  // eq 16\n    const gsl::not_null<tnsr::Ijj<DataVector, Dim>*>\n        christoffel_second_kind,  // eq 17\n    const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n        spatial_ricci_tensor,  // eq 18 - 20\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*> grad_grad_lapse,  // eq 21\n    const gsl::not_null<Scalar<DataVector>*> divergence_lapse,        // eq 22\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        contracted_conformal_christoffel_second_kind,  // eq 23\n    const gsl::not_null<tnsr::iJ<DataVector, Dim>*>\n        d_contracted_conformal_christoffel_second_kind,  // eq 24\n    const gsl::not_null<tnsr::i<DataVector, Dim>*>\n        spatial_z4_constraint,  // eq 25\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        upper_spatial_z4_constraint,  // eq 25\n    const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n        grad_spatial_z4_constraint,  // eq 26\n    const gsl::not_null<Scalar<DataVector>*>\n        ricci_scalar_plus_divergence_z4_constraint,  // eq 27\n    // free params\n    const double c, const double cleaning_speed,  // e in the paper\n    const Scalar<DataVector>& eta, const double f,\n    const Scalar<DataVector>& k_0, const tnsr::i<DataVector, Dim>& d_k_0,\n    const double kappa_1, const double kappa_2, const double kappa_3,\n    const double mu, const double one_over_relaxation_time,  // \\tau^{-1}\n    const EvolveShift evolve_shift,\n    const SlicingConditionType slicing_condition_type,\n    // evolved variables\n    const tnsr::ii<DataVector, Dim>& conformal_spatial_metric,\n    const Scalar<DataVector>& ln_lapse, const tnsr::I<DataVector, Dim>& shift,\n    const Scalar<DataVector>& ln_conformal_factor,\n    const tnsr::ii<DataVector, Dim>& a_tilde,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>& theta, const tnsr::I<DataVector, Dim>& gamma_hat,\n    const tnsr::I<DataVector, Dim>& b, const tnsr::i<DataVector, Dim>& field_a,\n    const tnsr::iJ<DataVector, Dim>& field_b,\n    const tnsr::ijj<DataVector, Dim>& field_d,\n    const tnsr::i<DataVector, Dim>& field_p,\n    // spatial derivatives of evolved variables\n    const tnsr::ijj<DataVector, Dim>& d_a_tilde,\n    const tnsr::i<DataVector, Dim>& d_trace_extrinsic_curvature,\n    const tnsr::i<DataVector, Dim>& d_theta,\n    const tnsr::iJ<DataVector, Dim>& d_gamma_hat,\n    const tnsr::iJ<DataVector, Dim>& d_b,\n    const tnsr::ij<DataVector, Dim>& d_field_a,\n    const tnsr::ijK<DataVector, Dim>& d_field_b,\n    const tnsr::ijkk<DataVector, Dim>& d_field_d,\n    const tnsr::ij<DataVector, Dim>& d_field_p) {\n  ASSERT(\n      evolve_shift == EvolveShift::True or evolve_shift == EvolveShift::False,\n      \"Unknown Ccz4::EvolveShift.\");\n\n  constexpr double one_third = 1.0 / 3.0;\n\n  // quantities we need for computing eq 12 - 27\n\n  determinant_and_inverse(det_conformal_spatial_metric,\n                          inv_conformal_spatial_metric,\n                          conformal_spatial_metric);\n\n  const size_t num_points = get_size(get(ln_conformal_factor));\n  for (size_t i = 0; i < num_points; i++) {\n    get(*conformal_factor_squared)[i] = exp(2.0 * get(ln_conformal_factor)[i]);\n  }\n\n  ::tenex::evaluate<ti::I, ti::J>(\n      inv_spatial_metric, (*conformal_factor_squared)() *\n                              (*inv_conformal_spatial_metric)(ti::I, ti::J));\n\n  ::tenex::evaluate<ti::I, ti::J>(\n      inv_a_tilde, a_tilde(ti::k, ti::l) *\n                       (*inv_conformal_spatial_metric)(ti::I, ti::K) *\n                       (*inv_conformal_spatial_metric)(ti::J, ti::L));\n\n  for (size_t i = 0; i < num_points; i++) {\n    get(*lapse)[i] = exp(get(ln_lapse)[i]);\n  }\n\n  if (slicing_condition_type == SlicingConditionType::Harmonic) {\n    get(*slicing_condition) = 1.0;\n    get(*d_slicing_condition) = 0.0;\n    get(*lapse_times_slicing_condition) = get(*lapse);\n  } else if (slicing_condition_type == SlicingConditionType::Log) {\n    ASSERT(min(get(*lapse)) > 0.0,\n           \"The lapse must be positive when using \"\n           \"Ccz4::SlicingConditionType::Log.\");\n\n    // g(\\alpha) == 2 / \\alpha\n    get(*slicing_condition) = 2.0 / get(*lapse);\n    // g'(\\alpha)  == -2 / \\alpha^2 == -0.5 * g(\\alpha)^2\n    get(*d_slicing_condition) = -0.5 * square(get(*slicing_condition));\n    // \\alpha g(\\alpha)  == 2\n    get(*lapse_times_slicing_condition) = 2.0;\n  } else {\n    ERROR(\"Unknown Ccz4::SlicingConditionType\");  // LCOV_EXCL_LINE\n  }\n\n  // expressions and identities needed for evolution equations: eq 13 - 27\n\n  // eq 13\n  ::tenex::evaluate(\n      trace_a_tilde,\n      (*inv_conformal_spatial_metric)(ti::I, ti::J) * a_tilde(ti::i, ti::j));\n\n  // eq 14\n  ::tenex::evaluate<ti::k, ti::I, ti::J>(\n      field_d_up, (*inv_conformal_spatial_metric)(ti::I, ti::N) *\n                      (*inv_conformal_spatial_metric)(ti::M, ti::J) *\n                      field_d(ti::k, ti::n, ti::m));\n\n  // eq 15\n  ::Ccz4::conformal_christoffel_second_kind(conformal_christoffel_second_kind,\n                                            *inv_conformal_spatial_metric,\n                                            field_d);\n\n  // eq 16\n  ::Ccz4::deriv_conformal_christoffel_second_kind(\n      d_conformal_christoffel_second_kind, *inv_conformal_spatial_metric,\n      field_d, d_field_d, *field_d_up);\n\n  // eq 17\n  ::Ccz4::christoffel_second_kind(christoffel_second_kind,\n                                  conformal_spatial_metric,\n                                  *inv_conformal_spatial_metric, field_p,\n                                  *conformal_christoffel_second_kind);\n\n  // temporary expressions needed for eq 18 - 20\n  ::tenex::evaluate<ti::l>(contracted_christoffel_second_kind,\n                           (*christoffel_second_kind)(ti::M, ti::l, ti::m));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      contracted_d_conformal_christoffel_difference,\n      (*d_conformal_christoffel_second_kind)(ti::m, ti::M, ti::i, ti::j) -\n          (*d_conformal_christoffel_second_kind)(ti::j, ti::M, ti::i, ti::m));\n\n  ::tenex::evaluate<ti::L>(contracted_field_d_up,\n                           (*field_d_up)(ti::m, ti::M, ti::L));\n\n  // eq 18 - 20\n  ::Ccz4::spatial_ricci_tensor(\n      spatial_ricci_tensor, *christoffel_second_kind,\n      *contracted_christoffel_second_kind,\n      *contracted_d_conformal_christoffel_difference, conformal_spatial_metric,\n      *inv_conformal_spatial_metric, field_d, *field_d_up,\n      *contracted_field_d_up, field_p, d_field_p);\n\n  // eq 21\n  ::Ccz4::grad_grad_lapse(grad_grad_lapse, *lapse, *christoffel_second_kind,\n                          field_a, d_field_a);\n\n  // eq 22\n  ::Ccz4::divergence_lapse(divergence_lapse, *conformal_factor_squared,\n                           *inv_conformal_spatial_metric, *grad_grad_lapse);\n\n  // eq 23\n  ::Ccz4::contracted_conformal_christoffel_second_kind(\n      contracted_conformal_christoffel_second_kind,\n      *inv_conformal_spatial_metric, *conformal_christoffel_second_kind);\n\n  // eq 24\n  ::Ccz4::deriv_contracted_conformal_christoffel_second_kind(\n      d_contracted_conformal_christoffel_second_kind,\n      *inv_conformal_spatial_metric, *field_d_up,\n      *conformal_christoffel_second_kind, *d_conformal_christoffel_second_kind);\n\n  // temp for eq 25\n  ::tenex::evaluate<ti::I>(\n      gamma_hat_minus_contracted_conformal_christoffel,\n      gamma_hat(ti::I) -\n          (*contracted_conformal_christoffel_second_kind)(ti::I));\n\n  // eq 25\n  ::Ccz4::spatial_z4_constraint(\n      spatial_z4_constraint, conformal_spatial_metric,\n      *gamma_hat_minus_contracted_conformal_christoffel);\n\n  // temp for eq 25\n  ::tenex::evaluate(half_conformal_factor_squared,\n                    0.5 * (*conformal_factor_squared)());\n\n  // eq 25\n  ::Ccz4::upper_spatial_z4_constraint(\n      upper_spatial_z4_constraint, *half_conformal_factor_squared,\n      *gamma_hat_minus_contracted_conformal_christoffel);\n\n  // temp for eq 26\n  ::tenex::evaluate<ti::i, ti::L>(\n      d_gamma_hat_minus_contracted_conformal_christoffel,\n      d_gamma_hat(ti::i, ti::L) -\n          (*d_contracted_conformal_christoffel_second_kind)(ti::i, ti::L));\n\n  // eq 26\n  ::Ccz4::grad_spatial_z4_constraint(\n      grad_spatial_z4_constraint, *spatial_z4_constraint,\n      conformal_spatial_metric, *christoffel_second_kind, field_d,\n      *gamma_hat_minus_contracted_conformal_christoffel,\n      *d_gamma_hat_minus_contracted_conformal_christoffel);\n\n  // eq 27\n  ::Ccz4::ricci_scalar_plus_divergence_z4_constraint(\n      ricci_scalar_plus_divergence_z4_constraint, *conformal_factor_squared,\n      *inv_conformal_spatial_metric, *spatial_ricci_tensor,\n      *grad_spatial_z4_constraint);\n\n  // temporary expressions not already computed above\n\n  ::tenex::evaluate(contracted_field_b, field_b(ti::k, ti::K));\n\n  ::tenex::evaluate<ti::k, ti::j, ti::I>(\n      symmetrized_d_field_b,\n      0.5 * (d_field_b(ti::k, ti::j, ti::I) + d_field_b(ti::j, ti::k, ti::I)));\n\n  ::tenex::evaluate<ti::k>(contracted_symmetrized_d_field_b,\n                           (*symmetrized_d_field_b)(ti::k, ti::i, ti::I));\n\n  ::tenex::evaluate<ti::i, ti::j, ti::k>(\n      field_b_times_field_d,\n      field_b(ti::i, ti::L) * field_d(ti::j, ti::l, ti::k));\n\n  ::tenex::evaluate<ti::k>(\n      field_d_up_times_a_tilde,\n      (*field_d_up)(ti::k, ti::I, ti::J) * a_tilde(ti::i, ti::j));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      conformal_metric_times_field_b,\n      conformal_spatial_metric(ti::k, ti::i) * field_b(ti::j, ti::K));\n\n  ::tenex::evaluate<ti::i, ti::k, ti::j>(\n      conformal_metric_times_symmetrized_d_field_b,\n      conformal_spatial_metric(ti::m, ti::i) *\n          (*symmetrized_d_field_b)(ti::k, ti::j, ti::M));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      conformal_metric_times_trace_a_tilde,\n      conformal_spatial_metric(ti::i, ti::j) * (*trace_a_tilde)());\n\n  ::tenex::evaluate<ti::k>(inv_conformal_metric_times_d_a_tilde,\n                           (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                               d_a_tilde(ti::k, ti::i, ti::j));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      a_tilde_times_field_b, a_tilde(ti::k, ti::i) * field_b(ti::j, ti::K));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde,\n      a_tilde(ti::i, ti::j) -\n          one_third * (*conformal_metric_times_trace_a_tilde)(ti::i, ti::j));\n\n  ::tenex::evaluate(k_minus_2_theta_c,\n                    trace_extrinsic_curvature() - 2.0 * c * theta());\n\n  ::tenex::evaluate(k_minus_k0_minus_2_theta_c, (*k_minus_2_theta_c)() - k_0());\n\n  ::tenex::evaluate<ti::i, ti::j>(lapse_times_a_tilde,\n                                  (*lapse)() * a_tilde(ti::i, ti::j));\n\n  tenex::evaluate<ti::k, ti::i, ti::j>(\n      lapse_times_d_a_tilde, (*lapse)() * d_a_tilde(ti::k, ti::i, ti::j));\n\n  ::tenex::evaluate<ti::k>(lapse_times_field_a, (*lapse)() * field_a(ti::k));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      lapse_times_conformal_spatial_metric,\n      (*lapse)() * conformal_spatial_metric(ti::i, ti::j));\n\n  ::tenex::evaluate(\n      lapse_times_ricci_scalar_plus_divergence_z4_constraint,\n      (*lapse)() * (*ricci_scalar_plus_divergence_z4_constraint)());\n\n  ::tenex::evaluate<ti::I>(shift_times_deriv_gamma_hat,\n                           shift(ti::K) * d_gamma_hat(ti::k, ti::I));\n\n  ::tenex::evaluate<ti::i, ti::j>(\n      inv_tau_times_conformal_metric,\n      one_over_relaxation_time * conformal_spatial_metric(ti::i, ti::j));\n\n  // time derivative computation: eq 12a - 12m\n\n  // eq 12a : time derivative of the conformal spatial metric\n  ::tenex::evaluate<ti::i, ti::j>(\n      dt_conformal_spatial_metric,\n      2.0 * shift(ti::K) * field_d(ti::k, ti::i, ti::j) +\n          (*conformal_metric_times_field_b)(ti::i, ti::j) +\n          (*conformal_metric_times_field_b)(ti::j, ti::i) -\n          2.0 * one_third * conformal_spatial_metric(ti::i, ti::j) *\n              (*contracted_field_b)() -\n          2.0 * (*lapse)() *\n              (*a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde)(\n                  ti::i, ti::j) -\n          (*inv_tau_times_conformal_metric)(ti::i, ti::j) *\n              ((*det_conformal_spatial_metric)() - 1.0));\n\n  // eq 12b : time derivative of the natural log of the lapse\n  ::tenex::evaluate(dt_ln_lapse, shift(ti::K) * field_a(ti::k) -\n                                     (*lapse_times_slicing_condition)() *\n                                         (*k_minus_k0_minus_2_theta_c)());\n\n  // eq 12c : time derivative of the shift\n  // if s == 0\n  if (not static_cast<bool>(evolve_shift)) {\n    for (auto& component : *dt_shift) {\n      component = 0.0;\n    }\n  } else {\n    ::tenex::evaluate<ti::I>(\n        dt_shift, f * b(ti::I) + shift(ti::K) * field_b(ti::k, ti::I));\n  }\n\n  // eq 12d : time derivative of the natural log of the conformal factor\n  ::tenex::evaluate(dt_ln_conformal_factor,\n                    shift(ti::K) * field_p(ti::k) +\n                        one_third * ((*lapse)() * trace_extrinsic_curvature() -\n                                     (*contracted_field_b)()));\n\n  // eq 12e : time derivative of the trace-free part of the extrinsic curvature\n  ::tenex::evaluate<ti::i, ti::j>(\n      dt_a_tilde,\n      shift(ti::K) * d_a_tilde(ti::k, ti::i, ti::j) +\n          (*conformal_factor_squared)() *\n              ((*lapse)() * ((*spatial_ricci_tensor)(ti::i, ti::j) +\n                             (*grad_spatial_z4_constraint)(ti::i, ti::j) +\n                             (*grad_spatial_z4_constraint)(ti::j, ti::i)) -\n               (*grad_grad_lapse)(ti::i, ti::j)) -\n          one_third * conformal_spatial_metric(ti::i, ti::j) *\n              ((*lapse_times_ricci_scalar_plus_divergence_z4_constraint)() -\n               (*divergence_lapse)()) +\n          (*a_tilde_times_field_b)(ti::i, ti::j) +\n          (*a_tilde_times_field_b)(ti::j, ti::i) -\n          2.0 * one_third * a_tilde(ti::i, ti::j) * (*contracted_field_b)() +\n          (*lapse_times_a_tilde)(ti::i, ti::j) * (*k_minus_2_theta_c)() -\n          2.0 * (*lapse_times_a_tilde)(ti::i, ti::l) *\n              (*inv_conformal_spatial_metric)(ti::L, ti::M) *\n              a_tilde(ti::m, ti::j) -\n          (*inv_tau_times_conformal_metric)(ti::i, ti::j) * (*trace_a_tilde)());\n\n  // eq. (12f) : time derivative of the trace of the extrinsic curvature\n  ::tenex::evaluate(\n      dt_trace_extrinsic_curvature,\n      shift(ti::K) * d_trace_extrinsic_curvature(ti::k) -\n          (*divergence_lapse)() +\n          (*lapse_times_ricci_scalar_plus_divergence_z4_constraint)() +\n          (*lapse)() * (trace_extrinsic_curvature() * (*k_minus_2_theta_c)() -\n                        3.0 * kappa_1 * (1.0 + kappa_2) * theta()));\n\n  // eq. (12g) : time derivative of the projection of the Z4 four-vector along\n  // the normal direction\n  ::tenex::evaluate(\n      dt_theta,\n      shift(ti::K) * d_theta(ti::k) +\n          (*lapse)() *\n              (0.5 * square(cleaning_speed) *\n                   ((*ricci_scalar_plus_divergence_z4_constraint)() +\n                    2.0 * one_third * square(trace_extrinsic_curvature()) -\n                    a_tilde(ti::i, ti::j) * (*inv_a_tilde)(ti::I, ti::J)) -\n               c * theta() * trace_extrinsic_curvature() -\n               (*upper_spatial_z4_constraint)(ti::I)*field_a(ti::i) -\n               kappa_1 * (2.0 + kappa_2) * theta()));\n\n  // eq. (12h) : time derivative \\hat{\\Gamma}^i\n  // first, compute terms without s\n  ::tenex::evaluate<ti::I>(\n      dt_gamma_hat,\n      // terms without lapse nor s\n      (*shift_times_deriv_gamma_hat)(ti::I) +\n          2.0 * one_third *\n              (*contracted_conformal_christoffel_second_kind)(ti::I) *\n              (*contracted_field_b)() -\n          (*contracted_conformal_christoffel_second_kind)(ti::K)*field_b(\n              ti::k, ti::I) +\n          2.0 * kappa_3 * (*spatial_z4_constraint)(ti::j) *\n              (2.0 * one_third * (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                   (*contracted_field_b)() -\n               (*inv_conformal_spatial_metric)(ti::J, ti::K) *\n                   field_b(ti::k, ti::I)) +\n          // terms with lapse but not s\n          2.0 * (*lapse)() *\n              (-2.0 * one_third *\n                   (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                   d_trace_extrinsic_curvature(ti::j) +\n               (*inv_conformal_spatial_metric)(ti::K, ti::I) * d_theta(ti::k) +\n               (*conformal_christoffel_second_kind)(ti::I, ti::j, ti::k) *\n                   (*inv_a_tilde)(ti::J, ti::K) -\n               3.0 * (*inv_a_tilde)(ti::I, ti::J) * field_p(ti::j) -\n               (*inv_conformal_spatial_metric)(ti::K, ti::I) *\n                   (theta() * field_a(ti::k) +\n                    2.0 * one_third * trace_extrinsic_curvature() *\n                        (*spatial_z4_constraint)(ti::k)) -\n               (*inv_a_tilde)(ti::I, ti::J) * field_a(ti::j) -\n               kappa_1 * (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                   (*spatial_z4_constraint)(ti::j)));\n  // now, if s == 1, also add terms with s\n  if (static_cast<bool>(evolve_shift)) {\n    ::tenex::update<ti::I>(\n        dt_gamma_hat,\n        (*dt_gamma_hat)(ti::I) +\n            // terms with lapse and s\n            2.0 * (*lapse)() *\n                ((*inv_conformal_spatial_metric)(ti::I, ti::K) *\n                     (*inv_conformal_spatial_metric)(ti::N, ti::M) *\n                     d_a_tilde(ti::k, ti::n, ti::m) -\n                 2.0 * (*inv_conformal_spatial_metric)(ti::I, ti::K) *\n                     (*field_d_up)(ti::k, ti::N, ti::M) *\n                     a_tilde(ti::n, ti::m)) +\n            // terms with s but not not lapse\n            (*inv_conformal_spatial_metric)(ti::K, ti::L) *\n                (*symmetrized_d_field_b)(ti::k, ti::l, ti::I) +\n            one_third * (*inv_conformal_spatial_metric)(ti::I, ti::K) *\n                (*contracted_symmetrized_d_field_b)(ti::k));\n  }\n\n  // eq. (12i) : time derivative b^i\n  // if s == 0\n  if (not static_cast<bool>(evolve_shift)) {\n    for (auto& component : *dt_b) {\n      component = 0.0;\n    }\n  } else {\n    ::tenex::evaluate<ti::I>(\n        dt_b,\n        (*dt_gamma_hat)(ti::I)-eta() * b(ti::I) +\n            shift(ti::K) * (d_b(ti::k, ti::I) - d_gamma_hat(ti::k, ti::I)));\n  }\n\n  // eq. (12j) : time derivative of auxiliary variable A_i\n  // first, compute terms without s\n  ::tenex::evaluate<ti::k>(\n      dt_field_a,\n      shift(ti::L) * d_field_a(ti::l, ti::k) -\n          (*lapse_times_field_a)(ti::k) * (*k_minus_k0_minus_2_theta_c)() *\n              ((*slicing_condition)() + (*lapse)() * (*d_slicing_condition)()) +\n          field_b(ti::k, ti::L) * field_a(ti::l) -\n          (*lapse_times_slicing_condition)() *\n              (d_trace_extrinsic_curvature(ti::k) - d_k_0(ti::k) -\n               2.0 * c * d_theta(ti::k)));\n  // now, if s == 1, also add terms with s\n  if (static_cast<bool>(evolve_shift)) {\n    ::tenex::update<ti::k>(\n        dt_field_a, (*dt_field_a)(ti::k) -\n                        (*lapse_times_slicing_condition)() *\n                            ((*inv_conformal_metric_times_d_a_tilde)(ti::k) -\n                             (2.0 * (*field_d_up_times_a_tilde)(ti::k))));\n  }\n\n  // eq. (12k) : time derivative of auxiliary variable B_k{}^i\n  // if s == 0\n  if (not static_cast<bool>(evolve_shift)) {\n    for (auto& component : *dt_field_b) {\n      component = 0.0;\n    }\n  } else {\n    // first, compute expression without advective terms\n    ::tenex::evaluate<ti::k, ti::I>(\n        dt_field_b, shift(ti::L) * d_field_b(ti::l, ti::k, ti::I) +\n                        f * d_b(ti::k, ti::I) +\n                        mu * square((*lapse)()) *\n                            (*inv_conformal_spatial_metric)(ti::I, ti::J) *\n                            (d_field_p(ti::k, ti::j) - d_field_p(ti::j, ti::k) -\n                             (*inv_conformal_spatial_metric)(ti::N, ti::L) *\n                                 (d_field_d(ti::k, ti::l, ti::j, ti::n) -\n                                  d_field_d(ti::l, ti::k, ti::j, ti::n))) +\n                        field_b(ti::k, ti::L) * field_b(ti::l, ti::I));\n  }\n\n  // eq. (12l) : time derivative of auxiliary variable D_{kij}\n  // first, compute terms without s\n  ::tenex::evaluate<ti::k, ti::i, ti::j>(\n      dt_field_d,\n      shift(ti::L) * d_field_d(ti::l, ti::k, ti::i, ti::j) -\n          (*lapse_times_d_a_tilde)(ti::k, ti::i, ti::j) +\n          field_b(ti::k, ti::L) * field_d(ti::l, ti::i, ti::j) +\n          (*field_b_times_field_d)(ti::j, ti::k, ti::i) +\n          (*field_b_times_field_d)(ti::i, ti::k, ti::j) -\n          (*lapse_times_field_a)(ti::k) *\n              (*a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde)(\n                  ti::i, ti::j) +\n          one_third *\n              ((*lapse_times_conformal_spatial_metric)(ti::i, ti::j) *\n                   (*inv_conformal_metric_times_d_a_tilde)(ti::k) -\n               (2.0 * (*contracted_field_b)() * field_d(ti::k, ti::i, ti::j)) -\n               2.0 * (*lapse_times_conformal_spatial_metric)(ti::i, ti::j) *\n                   (*field_d_up_times_a_tilde)(ti::k)));\n  // now, if s == 1, also add terms with s\n  if (static_cast<bool>(evolve_shift)) {\n    ::tenex::update<ti::k, ti::i, ti::j>(\n        dt_field_d, (*dt_field_d)(ti::k, ti::i, ti::j) +\n                        0.5 * ((*conformal_metric_times_symmetrized_d_field_b)(\n                                   ti::i, ti::k, ti::j) +\n                               (*conformal_metric_times_symmetrized_d_field_b)(\n                                   ti::j, ti::k, ti::i)) -\n                        one_third * conformal_spatial_metric(ti::i, ti::j) *\n                            (*contracted_symmetrized_d_field_b)(ti::k));\n  }\n\n  // eq. (12m) : time derivative of auxiliary variable P_i\n  // first, compute terms without s\n  ::tenex::evaluate<ti::k>(\n      dt_field_p, shift(ti::L) * d_field_p(ti::l, ti::k) +\n                      field_b(ti::k, ti::L) * field_p(ti::l) +\n                      one_third * (*lapse)() *\n                          (d_trace_extrinsic_curvature(ti::k) +\n                           field_a(ti::k) * trace_extrinsic_curvature()));\n  // now, if s == 1, also add terms with s\n  if (static_cast<bool>(evolve_shift)) {\n    ::tenex::update<ti::k>(\n        dt_field_p,\n        (*dt_field_p)(ti::k) +\n            one_third *\n                ((*lapse)() * ((*inv_conformal_metric_times_d_a_tilde)(ti::k) -\n                               (2.0 * (*field_d_up_times_a_tilde)(ti::k))) -\n                 (*contracted_symmetrized_d_field_b)(ti::k)));\n  }\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data) template struct Ccz4::TimeDerivative<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/*!\n* \\brief The namespace for the first and second-order Ccz4 evolution system.\n*/\nnamespace Ccz4 {\n/*!\n * \\brief Indicates whether or not to evolve the shift in a system evolved using\n * first order CCZ4 \\cite Dumbser2017okk\n *\n * \\details In \\cite Dumbser2017okk , evolving the shift corresponds to\n * \\f$s = 1\\f$ and not evolving it corresponds to \\f$s = 0\\f$\n */\nenum class EvolveShift : bool { False = false, True = true };\n\n/*!\n * \\brief Indicates which slicing condition to use in a system evolved using\n * first order CCZ4 \\cite Dumbser2017okk\n *\n * \\details In \\cite Dumbser2017okk , harmonic slicing corresponds to\n * \\f$g(\\alpha) = 1\\f$ and 1 + log slicing corresponds to\n * \\f$g(\\alpha) = 2 / \\alpha\\f$ where \\f$\\alpha\\f$ is the lapse.\n */\nenum class SlicingConditionType : char { Harmonic, Log };\n\n/*!\n * \\brief Compute the RHS of the first-order CCZ4 formulation of Einstein's\n * equations with discontinuous Galerkin method. See equations 12a-12m of\n * \\cite Dumbser2017okk.\n * \\details We define \\f$\\phi = (\\det(\\gamma_{ij}))^{-1/6}\\f$ as the conformal\n * factor, \\f$\\alpha\\f$ as the lapse, \\f$\\beta^i\\f$ as the shift, \\f$K_{ij}\\f$\n * as the extrinsic curvature, and \\f$Z_{a}\\f$ as the Z4 constraint.\n *\n * The evolved variables are the conformal spatial metric\n * \\f$\\tilde{\\gamma}_{ij} = \\phi^2 \\gamma_{ij}\\f$, the natural log of the lapse\n * \\f$\\ln \\alpha\\f$, the shift \\f$\\beta^i\\f$, the natural log of the conformal\n * factor \\f$\\ln \\phi\\f$, the trace-free part of the extrinsic curvature\n * \\f$\\tilde A_{ij} = \\phi^2 \\left(K_{ij} - \\frac{1}{3} K \\gamma_{ij}\\right)\\f$,\n * the trace of the extrinsic curvature \\f$K = K_{ij} \\gamma^{ij}\\f$, the\n * projection of the Z4 four-vector along the normal direction\n * \\f$\\Theta = Z^0 \\alpha\\f$, \\f$\\hat{\\Gamma}^{i}\\f$ defined by\n * `Ccz4::Tags::GammaHat`, the free variable \\f$b^i\\f$ that controls the\n * evolution of the shift and its time derivative, the auxiliary variable\n * \\f$A_i = \\partial_i \\ln(\\alpha) = \\frac{\\partial_i \\alpha}{\\alpha}\\f$, the\n * auxiliary variable \\f$B_k{}^{i} = \\partial_k \\beta^i\\f$, the auxiliary\n * variable \\f$D_{kij} = \\frac{1}{2} \\partial_k \\tilde{\\gamma}_{ij}\\f$, and the\n * auxiliary variable\n * \\f$P_i = \\partial_i \\ln(\\phi) = \\frac{\\partial_i \\phi}{\\phi}\\f$.\n *\n * The evolution equations are equations 12a - 12m of \\cite Dumbser2017okk .\n * Equations 13 - 27 define identities used in the evolution equations.\n *\n * This evolution uses two settings that can be toggled:\n * - `evolve_shift` governs whether or not the shift is evolved by setting\n * \\f$s = 1\\f$ or \\f$s = 0\\f$\n * - `slicing_condition_type` governs which slicing condition to use by setting\n * the value of \\f$g(\\alpha)\\f$)\n */\ntemplate <size_t Dim>\nstruct TimeDerivative {\n  static void apply(\n      const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n          dt_conformal_spatial_metric,\n      const gsl::not_null<Scalar<DataVector>*> dt_ln_lapse,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_shift,\n      const gsl::not_null<Scalar<DataVector>*> dt_ln_conformal_factor,\n      const gsl::not_null<tnsr::ii<DataVector, Dim>*> dt_a_tilde,\n      const gsl::not_null<Scalar<DataVector>*> dt_trace_extrinsic_curvature,\n      const gsl::not_null<Scalar<DataVector>*> dt_theta,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_gamma_hat,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> dt_b,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*> dt_field_a,\n      const gsl::not_null<tnsr::iJ<DataVector, Dim>*> dt_field_b,\n      const gsl::not_null<tnsr::ijj<DataVector, Dim>*> dt_field_d,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*> dt_field_p,\n      const gsl::not_null<Scalar<DataVector>*> conformal_factor_squared,\n      const gsl::not_null<Scalar<DataVector>*> det_conformal_spatial_metric,\n      const gsl::not_null<tnsr::II<DataVector, Dim>*>\n          inv_conformal_spatial_metric,\n      const gsl::not_null<tnsr::II<DataVector, Dim>*> inv_spatial_metric,\n      const gsl::not_null<Scalar<DataVector>*> lapse,\n      const gsl::not_null<Scalar<DataVector>*> slicing_condition,\n      const gsl::not_null<Scalar<DataVector>*> d_slicing_condition,\n      const gsl::not_null<tnsr::II<DataVector, Dim>*> inv_a_tilde,\n      const gsl::not_null<tnsr::ij<DataVector, Dim>*> a_tilde_times_field_b,\n      const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n          a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde,\n      const gsl::not_null<Scalar<DataVector>*> contracted_field_b,\n      const gsl::not_null<tnsr::ijK<DataVector, Dim>*> symmetrized_d_field_b,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*>\n          contracted_symmetrized_d_field_b,\n      const gsl::not_null<tnsr::ijk<DataVector, Dim>*> field_b_times_field_d,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*> field_d_up_times_a_tilde,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> contracted_field_d_up,\n      const gsl::not_null<Scalar<DataVector>*> half_conformal_factor_squared,\n      const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n          conformal_metric_times_field_b,\n      const gsl::not_null<tnsr::ijk<DataVector, Dim>*>\n          conformal_metric_times_symmetrized_d_field_b,\n      const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n          conformal_metric_times_trace_a_tilde,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*>\n          inv_conformal_metric_times_d_a_tilde,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*>\n          gamma_hat_minus_contracted_conformal_christoffel,\n      const gsl::not_null<tnsr::iJ<DataVector, Dim>*>\n          d_gamma_hat_minus_contracted_conformal_christoffel,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*>\n          contracted_christoffel_second_kind,\n      const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n          contracted_d_conformal_christoffel_difference,\n      const gsl::not_null<Scalar<DataVector>*> k_minus_2_theta_c,\n      const gsl::not_null<Scalar<DataVector>*> k_minus_k0_minus_2_theta_c,\n      const gsl::not_null<tnsr::ii<DataVector, Dim>*> lapse_times_a_tilde,\n      const gsl::not_null<tnsr::ijj<DataVector, Dim>*> lapse_times_d_a_tilde,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*> lapse_times_field_a,\n      const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n          lapse_times_conformal_spatial_metric,\n      const gsl::not_null<Scalar<DataVector>*> lapse_times_slicing_condition,\n      const gsl::not_null<Scalar<DataVector>*>\n          lapse_times_ricci_scalar_plus_divergence_z4_constraint,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*>\n          shift_times_deriv_gamma_hat,\n      const gsl::not_null<tnsr::ii<DataVector, Dim>*>\n          inv_tau_times_conformal_metric,\n      const gsl::not_null<Scalar<DataVector>*> trace_a_tilde,\n      const gsl::not_null<tnsr::iJJ<DataVector, Dim>*> field_d_up,\n      const gsl::not_null<tnsr::Ijj<DataVector, Dim>*>\n          conformal_christoffel_second_kind,\n      const gsl::not_null<tnsr::iJkk<DataVector, Dim>*>\n          d_conformal_christoffel_second_kind,\n      const gsl::not_null<tnsr::Ijj<DataVector, Dim>*> christoffel_second_kind,\n      const gsl::not_null<tnsr::ii<DataVector, Dim>*> spatial_ricci_tensor,\n      const gsl::not_null<tnsr::ij<DataVector, Dim>*> grad_grad_lapse,\n      const gsl::not_null<Scalar<DataVector>*> divergence_lapse,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*>\n          contracted_conformal_christoffel_second_kind,\n      const gsl::not_null<tnsr::iJ<DataVector, Dim>*>\n          d_contracted_conformal_christoffel_second_kind,\n      const gsl::not_null<tnsr::i<DataVector, Dim>*> spatial_z4_constraint,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*>\n          upper_spatial_z4_constraint,\n      const gsl::not_null<tnsr::ij<DataVector, Dim>*>\n          grad_spatial_z4_constraint,\n      const gsl::not_null<Scalar<DataVector>*>\n          ricci_scalar_plus_divergence_z4_constraint,\n      const double c, const double cleaning_speed,\n      const Scalar<DataVector>& eta, const double f,\n      const Scalar<DataVector>& k_0, const tnsr::i<DataVector, Dim>& d_k_0,\n      const double kappa_1, const double kappa_2, const double kappa_3,\n      const double mu, const double one_over_relaxation_time,\n      const EvolveShift evolve_shift,\n      const SlicingConditionType slicing_condition_type,\n      const tnsr::ii<DataVector, Dim>& conformal_spatial_metric,\n      const Scalar<DataVector>& ln_lapse, const tnsr::I<DataVector, Dim>& shift,\n      const Scalar<DataVector>& ln_conformal_factor,\n      const tnsr::ii<DataVector, Dim>& a_tilde,\n      const Scalar<DataVector>& trace_extrinsic_curvature,\n      const Scalar<DataVector>& theta,\n      const tnsr::I<DataVector, Dim>& gamma_hat,\n      const tnsr::I<DataVector, Dim>& b,\n      const tnsr::i<DataVector, Dim>& field_a,\n      const tnsr::iJ<DataVector, Dim>& field_b,\n      const tnsr::ijj<DataVector, Dim>& field_d,\n      const tnsr::i<DataVector, Dim>& field_p,\n      const tnsr::ijj<DataVector, Dim>& d_a_tilde,\n      const tnsr::i<DataVector, Dim>& d_trace_extrinsic_curvature,\n      const tnsr::i<DataVector, Dim>& d_theta,\n      const tnsr::iJ<DataVector, Dim>& d_gamma_hat,\n      const tnsr::iJ<DataVector, Dim>& d_b,\n      const tnsr::ij<DataVector, Dim>& d_field_a,\n      const tnsr::ijK<DataVector, Dim>& d_field_b,\n      const tnsr::ijkk<DataVector, Dim>& d_field_d,\n      const tnsr::ij<DataVector, Dim>& d_field_p);\n};\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Z4Constraint.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/Ccz4/Z4Constraint.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid spatial_z4_constraint(\n    const gsl::not_null<tnsr::i<DataType, Dim, Frame>*> result,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel) {\n  ::tenex::evaluate<ti::i>(\n      result, 0.5 * (conformal_spatial_metric(ti::i, ti::j) *\n                     gamma_hat_minus_contracted_conformal_christoffel(ti::J)));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::i<DataType, Dim, Frame> spatial_z4_constraint(\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel) {\n  tnsr::i<DataType, Dim, Frame> result{};\n  spatial_z4_constraint(make_not_null(&result), conformal_spatial_metric,\n                        gamma_hat_minus_contracted_conformal_christoffel);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid upper_spatial_z4_constraint(\n    const gsl::not_null<tnsr::I<DataType, Dim, Frame>*> result,\n    const Scalar<DataType>& half_conformal_factor_squared,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel) {\n  ::tenex::evaluate<ti::I>(\n      result, half_conformal_factor_squared() *\n                  gamma_hat_minus_contracted_conformal_christoffel(ti::I));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::I<DataType, Dim, Frame> upper_spatial_z4_constraint(\n    const Scalar<DataType>& half_conformal_factor_squared,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel) {\n  tnsr::I<DataType, Dim, Frame> result{};\n  upper_spatial_z4_constraint(make_not_null(&result),\n                              half_conformal_factor_squared,\n                              gamma_hat_minus_contracted_conformal_christoffel);\n  return result;\n}\n}  // namespace Ccz4\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template void Ccz4::spatial_z4_constraint(                             \\\n      const gsl::not_null<tnsr::i<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                        \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          conformal_spatial_metric,                                      \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          gamma_hat_minus_contracted_conformal_christoffel);             \\\n  template tnsr::i<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  Ccz4::spatial_z4_constraint(                                           \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          conformal_spatial_metric,                                      \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          gamma_hat_minus_contracted_conformal_christoffel);             \\\n  template void Ccz4::upper_spatial_z4_constraint(                       \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                        \\\n      const Scalar<DTYPE(data)>& half_conformal_factor_squared,          \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          gamma_hat_minus_contracted_conformal_christoffel);             \\\n  template tnsr::I<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  Ccz4::upper_spatial_z4_constraint(                                     \\\n      const Scalar<DTYPE(data)>& half_conformal_factor_squared,          \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          gamma_hat_minus_contracted_conformal_christoffel);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/Ccz4/Z4Constraint.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4 {\n/// @{\n/*!\n * \\brief Computes the spatial part of the Z4 constraint\n *\n * \\details Computes the constraint as:\n *\n * \\f{align}\n *     Z_i &= \\frac{1}{2} \\tilde{\\gamma}_{ij} \\left(\n *         \\hat{\\Gamma}^j - \\tilde{\\Gamma}^j\\right)\n * \\f}\n *\n * where \\f$\\tilde{\\gamma}_{ij}\\f$ is the conformal spatial metric defined by\n * `Ccz4::Tags::ConformalMetric` and\n * \\f$\\left(\\hat{\\Gamma}^i - \\tilde{\\Gamma}^i\\right)\\f$ is the CCZ4 temporary\n * expression defined by\n * `Ccz4::Tags::GammaHatMinusContractedConformalChristoffel`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid spatial_z4_constraint(\n    const gsl::not_null<tnsr::i<DataType, Dim, Frame>*> result,\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::i<DataType, Dim, Frame> spatial_z4_constraint(\n    const tnsr::ii<DataType, Dim, Frame>& conformal_spatial_metric,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the spatial part of the upper Z4 constraint\n *\n * \\details Computes the constraint as:\n *\n * \\f{align}\n *     Z^i &= \\frac{1}{2} \\phi^2 \\left(\n *         \\hat{\\Gamma}^i - \\tilde{\\Gamma}^i\\right)\n * \\f}\n *\n * where \\f$\\phi^2 \\f$ is the square of the conformal factor defined by\n * `Ccz4::Tags::ConformalFactorSquared` and\n * \\f$\\left(\\hat{\\Gamma}^i - \\tilde{\\Gamma}^i\\right)\\f$ is the CCZ4 temporary\n * expression defined by\n * `Ccz4::Tags::GammaHatMinusContractedConformalChristoffel`.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid upper_spatial_z4_constraint(\n    const gsl::not_null<tnsr::I<DataType, Dim, Frame>*> result,\n    const Scalar<DataType>& half_conformal_factor_squared,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::I<DataType, Dim, Frame> upper_spatial_z4_constraint(\n    const Scalar<DataType>& half_conformal_factor_squared,\n    const tnsr::I<DataType, Dim, Frame>&\n        gamma_hat_minus_contracted_conformal_christoffel);\n/// @}\n}  // namespace Ccz4\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  SetInitialData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  SetInitialData.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Actions/SetInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Actions/SetInitialData.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <optional>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace CurvedScalarWave {\n\nNumericInitialData::NumericInitialData(\n    std::string file_glob, std::string subfile_name,\n    std::variant<double, importers::ObservationSelector> observation_value,\n    std::optional<double> observation_value_epsilon, bool enable_interpolation,\n    ScalarVars selected_variables)\n    : importer_options_(\n          std::move(file_glob), std::move(subfile_name), observation_value,\n          observation_value_epsilon.value_or(1.0e-12), enable_interpolation),\n      selected_variables_(std::move(selected_variables)) {}\n\nNumericInitialData::NumericInitialData(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nconst importers::ImporterOptions& NumericInitialData::importer_options() const {\n  return importer_options_;\n}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID NumericInitialData::my_PUP_ID = 0;\n\nsize_t NumericInitialData::volume_data_id() const {\n  size_t hash = 0;\n  boost::hash_combine(hash, pretty_type::get_name<NumericInitialData>());\n  boost::hash_combine(hash,\n                      get<importers::OptionTags::FileGlob>(importer_options_));\n  boost::hash_combine(hash,\n                      get<importers::OptionTags::Subgroup>(importer_options_));\n  return hash;\n}\n\nvoid NumericInitialData::pup(PUP::er& p) {\n  p | importer_options_;\n  p | selected_variables_;\n}\n\nbool operator==(const NumericInitialData& lhs, const NumericInitialData& rhs) {\n  return lhs.importer_options_ == rhs.importer_options_ and\n         lhs.selected_variables_ == rhs.selected_variables_;\n}\n\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Actions/SetInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave {\n\n/*!\n * \\brief Numeric initial data loaded from volume data files\n */\nclass NumericInitialData : public evolution::initial_data::InitialData {\n public:\n  template <typename Tag>\n  struct VarName {\n    using tag = Tag;\n    static std::string name() { return db::tag_name<Tag>(); }\n    using type = std::string;\n    static constexpr Options::String help =\n        \"Name of the variable in the volume data file\";\n  };\n\n  // These are the scalar variables that we support loading from volume\n  // data files\n  using all_vars =\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<3>>;\n  using optional_primitive_vars = tmpl::list<>;\n\n  struct ScalarVars : tuples::tagged_tuple_from_typelist<\n                          db::wrap_tags_in<VarName, all_vars>> {\n    static constexpr Options::String help =\n        \"Scalar variables: 'Psi', 'Pi' and 'Phi'.\";\n    using options = tags_list;\n    using TaggedTuple::TaggedTuple;\n  };\n\n  // Input-file options\n  struct Variables {\n    using type = ScalarVars;\n    static constexpr Options::String help =\n        \"Set of initial data variables for the CurvedScalarWave system.\";\n  };\n\n  using options =\n      tmpl::push_back<importers::ImporterOptions::tags_list, Variables>;\n\n  static constexpr Options::String help =\n      \"Numeric initial data loaded from volume data files\";\n\n  NumericInitialData() = default;\n  NumericInitialData(const NumericInitialData& rhs) = default;\n  NumericInitialData& operator=(const NumericInitialData& rhs) = default;\n  NumericInitialData(NumericInitialData&& /*rhs*/) = default;\n  NumericInitialData& operator=(NumericInitialData&& /*rhs*/) = default;\n  ~NumericInitialData() override = default;\n\n  /// \\cond\n  explicit NumericInitialData(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NumericInitialData);\n  /// \\endcond\n\n  std::unique_ptr<evolution::initial_data::InitialData> get_clone()\n      const override {\n    return std::make_unique<NumericInitialData>(*this);\n  }\n\n  NumericInitialData(\n      std::string file_glob, std::string subfile_name,\n      std::variant<double, importers::ObservationSelector> observation_value,\n      std::optional<double> observation_value_epsilon,\n      bool enable_interpolation, ScalarVars selected_variables);\n\n  const importers::ImporterOptions& importer_options() const;\n\n  const ScalarVars& selected_variables() const { return selected_variables_; }\n\n  size_t volume_data_id() const;\n\n  template <typename... AllTags>\n  void select_for_import(\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> fields) const {\n    // Select the subset of the available variables that we want to read from\n    // the volume data file\n    using selected_vars = std::decay_t<decltype(selected_variables_)>;\n    tmpl::for_each<typename selected_vars::tags_list>(\n        [&fields, this](const auto tag_v) {\n          using tag = typename std::decay_t<decltype(tag_v)>::type::tag;\n          get<importers::Tags::Selected<tag>>(*fields) =\n              get<VarName<tag>>(selected_variables_);\n        });\n  }\n\n  template <typename... AllTags>\n  void set_initial_data(const gsl::not_null<Scalar<DataVector>*> psi_scalar,\n                        const gsl::not_null<Scalar<DataVector>*> pi_scalar,\n                        const gsl::not_null<tnsr::i<DataVector, 3>*> phi_scalar,\n                        const gsl::not_null<tuples::TaggedTuple<AllTags...>*>\n                            numeric_data) const {\n    *psi_scalar = std::move(get<CurvedScalarWave::Tags::Psi>(*numeric_data));\n    *pi_scalar = std::move(get<CurvedScalarWave::Tags::Pi>(*numeric_data));\n    *phi_scalar = std::move(get<CurvedScalarWave::Tags::Phi<3>>(*numeric_data));\n  }\n\n  void pup(PUP::er& p) override;\n\n  friend bool operator==(const NumericInitialData& lhs,\n                         const NumericInitialData& rhs);\n\n private:\n  importers::ImporterOptions importer_options_{};\n  ScalarVars selected_variables_{};\n};\n\nnamespace Actions {\n\n/*!\n * \\brief Dispatch loading numeric initial data from files.\n *\n * Place this action before\n * CurvedScalarWave::Actions::SetNumericInitialData in the action list.\n * See importers::Actions::ReadAllVolumeDataAndDistribute for details, which is\n * invoked by this action.\n */\nstruct SetInitialData {\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const parallel_component) {\n    // Dispatch to the correct `apply` overload based on type of initial data\n    using initial_data_classes =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 evolution::initial_data::InitialData>;\n    return call_with_dynamic_type<Parallel::iterable_action_return_t,\n                                  initial_data_classes>(\n        &db::get<evolution::initial_data::Tags::InitialData>(box),\n        [&box, &cache, &array_index,\n         &parallel_component](const auto* const initial_data) {\n          return apply(make_not_null(&box), *initial_data, cache, array_index,\n                       parallel_component);\n        });\n  }\n\n private:\n  // Numeric initial data\n  template <typename DbTagsList, typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,\n      const NumericInitialData& initial_data,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*meta*/) {\n    // Select the subset of the available variables that we want to read from\n    // the volume data file\n    tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n        importers::Tags::Selected, NumericInitialData::all_vars>>\n        selected_fields{};\n    initial_data.select_for_import(make_not_null(&selected_fields));\n    auto& reader_component = Parallel::get_parallel_component<\n        importers::ElementDataReader<Metavariables>>(cache);\n    Parallel::simple_action<importers::Actions::ReadAllVolumeDataAndDistribute<\n        3, NumericInitialData::all_vars, ParallelComponent>>(\n        reader_component, initial_data.importer_options(),\n        initial_data.volume_data_id(), std::move(selected_fields));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  // \"AnalyticData\"-type initial data\n  template <typename DbTagsList, typename InitialData, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const InitialData& initial_data,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*meta*/) {\n    static constexpr size_t Dim = Metavariables::volume_dim;\n    using flat_variables_tag = typename ScalarWave::System<Dim>::variables_tag;\n    using curved_variables_tag =\n        typename CurvedScalarWave::System<Dim>::variables_tag;\n    const auto inertial_coords =\n        db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(*box);\n    const double initial_time = db::get<::Tags::Time>(*box);\n    if constexpr (is_analytic_data_v<InitialData> or\n                  is_analytic_solution_v<InitialData>) {\n      if constexpr (tmpl::list_contains_v<typename InitialData::tags,\n                                          CurvedScalarWave::Tags::Psi>) {\n        const auto curved_initial_data =\n            evolution::Initialization::initial_data(\n                initial_data, inertial_coords, initial_time,\n                typename curved_variables_tag::tags_list{});\n\n        db::mutate<typename CurvedScalarWave::System<Dim>::variables_tag>(\n            [&curved_initial_data](\n                const gsl::not_null<typename curved_variables_tag::type*>\n                    evolved_vars) {\n              evolved_vars->assign_subset(curved_initial_data);\n            },\n            box);\n      } else {\n        static_assert(tmpl::list_contains_v<typename InitialData::tags,\n                                            ScalarWave::Tags::Psi>,\n                      \"The initial data class must either calculate ScalarWave \"\n                      \"or CurvedScalarWave variables.\");\n        const auto flat_initial_data = evolution::Initialization::initial_data(\n            initial_data, inertial_coords, initial_time,\n            typename flat_variables_tag::tags_list{});\n        const auto& shift = db::get<gr::Tags::Shift<DataVector, Dim>>(*box);\n        const auto& lapse = db::get<gr::Tags::Lapse<DataVector>>(*box);\n        const auto shift_dot_dpsi = dot_product(\n            shift, get<ScalarWave::Tags::Phi<Dim>>(flat_initial_data));\n        db::mutate<typename CurvedScalarWave::System<Dim>::variables_tag>(\n            [&flat_initial_data, &shift_dot_dpsi,\n             &lapse](const gsl::not_null<typename curved_variables_tag::type*>\n                         evolved_vars) {\n              get<CurvedScalarWave::Tags::Psi>(*evolved_vars) =\n                  get<ScalarWave::Tags::Psi>(flat_initial_data);\n              get<CurvedScalarWave::Tags::Phi<Dim>>(*evolved_vars) =\n                  get<ScalarWave::Tags::Phi<Dim>>(flat_initial_data);\n              get(get<CurvedScalarWave::Tags::Pi>(*evolved_vars)) =\n                  (get(shift_dot_dpsi) +\n                   get(get<ScalarWave::Tags::Pi>(flat_initial_data))) /\n                  get(lapse);\n            },\n            box);\n      }\n    } else {\n      ERROR(\n          \"Trying to use \"\n          \"'evolution::Initialization::Actions::SetVariables' with a \"\n          \"class that's not marked as analytic solution or analytic \"\n          \"data. To support numeric initial data, add a \"\n          \"system-specific initialization routine to your executable.\");\n    }\n\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Receive numeric initial data loaded by\n * CurvedScalarWave::Actions::ReadNumericInitialData.\n */\nstruct ReceiveNumericInitialData {\n  static constexpr size_t Dim = 3;\n  using inbox_tags =\n      tmpl::list<importers::Tags::VolumeData<NumericInitialData::all_vars>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if constexpr (Metavariables::volume_dim != Dim) {\n      ERROR(\n          \"CurvedScalarWave numeric initial data currently requires a 3D \"\n          \"domain.\");\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    } else {\n      auto& inbox = tuples::get<\n          importers::Tags::VolumeData<NumericInitialData::all_vars>>(inboxes);\n      const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n          db::get<evolution::initial_data::Tags::InitialData>(box));\n      const size_t volume_data_id = initial_data.volume_data_id();\n      if (inbox.find(volume_data_id) == inbox.end()) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n      auto numeric_data = std::move(inbox.extract(volume_data_id).mapped());\n\n      db::mutate<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<Dim>>(\n          [&initial_data, &numeric_data](\n              const gsl::not_null<Scalar<DataVector>*> psi_scalar,\n              const gsl::not_null<Scalar<DataVector>*> pi_scalar,\n              const gsl::not_null<tnsr::i<DataVector, Dim>*> phi_scalar) {\n            initial_data.set_initial_data(psi_scalar, pi_scalar, phi_scalar,\n                                          make_not_null(&numeric_data));\n          },\n          make_not_null(&box));\n\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n  }\n};\n\n}  // namespace Actions\n\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Actions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\brief Actions for the curved scalar wave system\nnamespace CurvedScalarWave::Actions {}\n\n/// \\brief Actions for the worldtube-curved scalar wave system\nnamespace CurvedScalarWave::Worldtube::Actions {}\n\n/// \\brief Initialization mutators and actions for the worldtube-curved scalar\n/// wave system\nnamespace CurvedScalarWave::Worldtube::Initialization {}\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/ApplyTensorYlmFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/ApplyTensorYlmFilter.hpp\"\n\n#include <cstddef>\n#include <cstring>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n#include \"NumericalAlgorithms/SphericalHarmonics/ApplyTensorYlmFilter.tpp\"\n\nnamespace ylm::TensorYlm {\n\nnamespace filter_detail {\ntemplate <typename SrcFrame, typename DestFrame>\nvoid transform_spatial_tensors_to_different_frame_without_hessians(\n    const gsl::not_null<Variables<sw_vars_list<DestFrame>>*> dest,\n    const Variables<sw_vars_list<SrcFrame>>& src,\n    const InverseJacobian<DataVector, 3, SrcFrame, DestFrame>& jac) {\n  const auto& [src_psi, src_pi, src_phi] = src;\n  auto& [dest_psi, dest_pi, dest_phi] = *dest;\n\n  // Just copy the scalars.\n  get<>(dest_psi) = get<>(src_psi);\n  get<>(dest_pi) = get<>(src_pi);\n\n  // Now do the vector.\n  for (size_t i = 0; i < 3; ++i) {\n    dest_phi.get(i) = jac.get(0, i) * src_phi.get(0) +\n                      jac.get(1, i) * src_phi.get(1) +\n                      jac.get(2, i) * src_phi.get(2);\n  }\n}\n}  // namespace filter_detail\n\nvoid apply_tensor_ylm_filter(\n    const gsl::not_null<\n        Variables<filter_detail::sw_vars_list<Frame::Inertial>>*>\n        sw_vars,\n    const gsl::not_null<\n        Variables<filter_detail::sw_vars_list<Frame::Inertial>>*>\n        temp_storage,\n    const InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid>&\n        jac_inertial_to_grid,\n    const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>&\n        jac_grid_to_inertial,\n    const SimpleSparseMatrix& filter_matrix_scalar,\n    const SimpleSparseMatrix& filter_matrix_i, const size_t ell_max,\n    const size_t radial_extents) {\n  const auto& ylm = ylm::get_spherepack_cache(ell_max);\n  ASSERT(\n      radial_extents * ylm.physical_size() == sw_vars->number_of_grid_points(),\n      \"Mismatch \" << radial_extents * ylm.physical_size() << \" must equal \"\n                  << sw_vars->number_of_grid_points());\n  ASSERT(radial_extents * ylm.spectral_size() <=\n             temp_storage->number_of_grid_points(),\n         \"Mismatch \" << radial_extents * ylm.spectral_size() << \" must be <= \"\n                     << temp_storage->number_of_grid_points());\n\n  // Here we re-use the same memory multiple times.  Note that\n  // 1. sw_vars_to_filter has the same number of components as\n  //    sw_spatial_decomp_vars, even though the components are arranged\n  //    differently. So we can create a non-owning Variables of either\n  //    tag that points into the storage of a Variables with the opposite tag.\n  // 2. temp_storage has a larger size than sw_vars, because temp_storage\n  //    is sized to hold spectral coefficients (in S2) and sw_vars holds\n  //    collocation points (in S2).  This means that we can create a\n  //    non-owning Variables to hold collocation points but that points into\n  //    temp_storage (but we cannot create a non-owning Variables to hold\n  //    spectral coefficients that points into sw_vars).\n  //\n  // We define two different Variables that point into temp_storage\n  // (and we should not use any them simultaneously) and one\n  // Variables that points into sw_vars (which we should not use\n  // simultaneously with sw_vars).\n  Variables<filter_detail::sw_vars_list<Frame::Grid>> sw_spectral_vars(\n      temp_storage->data(), temp_storage->size());\n  Variables<filter_detail::sw_vars_list<Frame::Grid>> temp_grid_vars(\n      sw_vars->data(), sw_vars->size());\n  // The following Variables uses sw_vars->size() which is smaller\n  // than temp_storage->size().\n  ASSERT(sw_vars->size() <= temp_storage->size(),\n         \"Should have \" << sw_vars->size() << \" <= \" << temp_storage->size());\n  Variables<filter_detail::sw_vars_list<Frame::Grid>> temp_sw_vars(\n      temp_storage->data(), sw_vars->size());\n\n  // 1. Multiply by inverse Jacobians to get into (mostly) grid frame.\n  //    It's not really the grid frame because there are no Hessian\n  //    corrections, but those don't matter for this purpose.\n  // src: sw_vars\n  // dest: temp_sw_vars\n  filter_detail::transform_spatial_tensors_to_different_frame_without_hessians<\n      Frame::Inertial, Frame::Grid>(make_not_null(&temp_sw_vars), *sw_vars,\n                                    jac_inertial_to_grid);\n\n  // 1a. Copy\n  // src: temp_sw_vars\n  // dest: temp_grid_vars\n  std::memcpy(temp_grid_vars.data(), temp_sw_vars.data(),\n              temp_sw_vars.size() * sizeof(double));\n\n  // 2. Nodal to modal transformation.\n  // src: temp_grid_vars\n  // dest: sw_spectral_vars\n  filter_detail::nodal_to_modal_ylm(make_not_null(&sw_spectral_vars),\n                                    temp_grid_vars, ylm, radial_extents);\n\n  // 3. Filter\n  // src: sw_spectral_vars\n  // dest: sw_spectral_vars\n  // but using temp_grid_vars as temp storage for each tensor\n  tmpl::for_each<filter_detail::sw_vars_list<Frame::Grid>>(\n      [&sw_spectral_vars, &temp_grid_vars, radial_extents, &filter_matrix_i,\n       &filter_matrix_scalar]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        // Different compilers disagree on whether radial_extents\n        // needs to be in the capture list of this lambda, and\n        // whether radial_extents is 'used' in the lambda.\n        // Adding it to the capture list and adding a cast here\n        // satisfies everyone.\n        (void)radial_extents;\n        constexpr size_t num_independent_components =\n            Tag::type::structure::size();\n        // Create destination tensor: non-owning and pointing into\n        // temp_grid_vars.  temp_grid_vars is larger than any\n        // *SINGLE* tensor in sw_spectral_vars, so this is ok.\n        // Note that sw_spectral_vars.number_of_grid_points()\n        // is used for the size because that is the spectral size.\n        ASSERT(sw_spectral_vars.number_of_grid_points() *\n                       num_independent_components <=\n                   temp_grid_vars.size(),\n               \"Insufficient size: must have \"\n                   << sw_spectral_vars.number_of_grid_points() *\n                          num_independent_components\n                   << \" <= \" << temp_grid_vars.size());\n\n        Variables<tmpl::list<Tag>> dest_tensor(\n            temp_grid_vars.data(), sw_spectral_vars.number_of_grid_points() *\n                                       num_independent_components);\n\n        // Delta term\n        get<Tag>(dest_tensor) = get<Tag>(sw_spectral_vars);\n        // The rest of the terms.\n\n        // Here we assume that different components in a given\n        // tensor are stored contiguously in memory, so we can grab a\n        // pointer to the first component of the tensor and pass that\n        // pointer to increment_multiply_on_right.\n        const gsl::span<double> src(\n            get<Tag>(sw_spectral_vars)[0].data(),\n            num_independent_components *\n                sw_spectral_vars.number_of_grid_points());\n        gsl::span<double> dest(\n            get<Tag>(dest_tensor)[0].data(),\n            num_independent_components * dest_tensor.number_of_grid_points());\n        // If the mesh is 3-dimensional (i.e. radial_extents>1), then\n        // we need to loop over offsets.  If not, then there's only\n        // one loop iteration.\n        const size_t stride = radial_extents;\n        for (size_t offset = 0; offset < stride; ++offset) {\n          // Each type of tensor gets a different filter matrix.\n          if constexpr (std::is_same_v<typename Tag::type::structure::symmetry,\n                                       Symmetry<1>>) {\n            filter_matrix_i.increment_multiply_on_right(\n                make_not_null(&dest), offset, stride, src, offset, stride);\n          } else {\n            filter_matrix_scalar.increment_multiply_on_right(\n                make_not_null(&dest), offset, stride, src, offset, stride);\n          }\n        }\n        // Copy the result for this tensor back into sw_spectral_vars.\n        get<Tag>(sw_spectral_vars) = get<Tag>(dest_tensor);\n      });\n\n  // 4. Modal to nodal transformation.\n  // src: sw_spectral_vars\n  // dest: temp_grid_vars\n  filter_detail::modal_to_nodal_ylm(make_not_null(&temp_grid_vars),\n                                    sw_spectral_vars, ylm, radial_extents);\n\n  // 4a. Copy\n  // src: temp_grid_vars\n  // dest: temp_sw_vars\n  std::memcpy(temp_sw_vars.data(), temp_grid_vars.data(),\n              temp_grid_vars.size() * sizeof(double));\n\n  // 5. Multiply by Jacobians to get back into inertial frame.\n  // src: temp_sw_vars\n  // dest: sw_vars\n  filter_detail::transform_spatial_tensors_to_different_frame_without_hessians<\n      Frame::Grid, Frame::Inertial>(sw_vars, temp_sw_vars,\n                                    jac_grid_to_inertial);\n}\n\n// Explicit instantiations\n\nnamespace filter_detail {\n\ntemplate void nodal_to_modal_ylm<sw_vars_list<Frame::Grid>>(\n    gsl::not_null<Variables<sw_vars_list<Frame::Grid>>*> modal,\n    const Variables<sw_vars_list<Frame::Grid>>& nodal,\n    const ::ylm::Spherepack& ylm, size_t radial_extents);\n\ntemplate void nodal_to_modal_ylm<sw_vars_list<Frame::Inertial>>(\n    gsl::not_null<Variables<sw_vars_list<Frame::Inertial>>*> modal,\n    const Variables<sw_vars_list<Frame::Inertial>>& nodal,\n    const ::ylm::Spherepack& ylm, size_t radial_extents);\n\ntemplate void modal_to_nodal_ylm<sw_vars_list<Frame::Grid>>(\n    gsl::not_null<Variables<sw_vars_list<Frame::Grid>>*> modal,\n    const Variables<sw_vars_list<Frame::Grid>>& nodal,\n    const ::ylm::Spherepack& ylm, size_t radial_extents);\n\ntemplate void modal_to_nodal_ylm<sw_vars_list<Frame::Inertial>>(\n    gsl::not_null<Variables<sw_vars_list<Frame::Inertial>>*> modal,\n    const Variables<sw_vars_list<Frame::Inertial>>& nodal,\n    const ::ylm::Spherepack& ylm, size_t radial_extents);\n\ntemplate void transform_spatial_tensors_to_different_frame_without_hessians<\n    Frame::Grid, Frame::Inertial>(\n    gsl::not_null<Variables<sw_vars_list<Frame::Inertial>>*> dest,\n    const Variables<sw_vars_list<Frame::Grid>>& src,\n    const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>& jac);\n\ntemplate void transform_spatial_tensors_to_different_frame_without_hessians<\n    Frame::Inertial, Frame::Grid>(\n    gsl::not_null<Variables<sw_vars_list<Frame::Grid>>*> dest,\n    const Variables<sw_vars_list<Frame::Inertial>>& src,\n    const InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid>& jac);\n}  // namespace filter_detail\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/ApplyTensorYlmFilter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/ApplyTensorYlmFilter.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\nclass Spherepack;\n}  // namespace ylm\n/// \\endcond\n\nnamespace ylm::TensorYlm {\n\n/// Defines tags and functions used internally in filtering, but\n/// tested independently in the unit tests.\nnamespace filter_detail {\n\ntemplate <typename Frame>\nusing sw_vars_list =\n    tmpl::list<::CurvedScalarWave::Tags::Psi, ::CurvedScalarWave::Tags::Pi,\n               ::CurvedScalarWave::Tags::Phi<3, Frame>>;\n\n/*!\n * \\brief Transforms spatial tensors into a different frame, ignoring hessians.\n *\n * This is done for filtering, where having the correct (i.e. with hessians)\n * transformation doesn't matter; all that matters is that the tensor\n * indices correspond to the coordinates (or in other words, no dual frame).\n *\n * Assumes that all the variables have lower indices.\n *\n * Takes special care to re-use memory. The Variables arguments must\n * already be allocated to their correct sizes; no memory allocation\n * is done.\n *\n * \\tparam SrcFrame Source frame.\n * \\tparam DestFrame Destination frame.\n * \\param dest A Variables for the destination spatial variables.\n * \\param src A Variables containing the source spatial variables.\n * \\param jac The jacobian dx^src/dx^dest\n */\ntemplate <typename SrcFrame, typename DestFrame>\nvoid transform_spatial_tensors_to_different_frame_without_hessians(\n    gsl::not_null<Variables<sw_vars_list<DestFrame>>*> dest,\n    const Variables<sw_vars_list<SrcFrame>>& src,\n    const InverseJacobian<DataVector, 3, SrcFrame, DestFrame>& jac);\n\n}  // namespace filter_detail\n\n/*!\n * \\brief Applies TensorYlm filter in place to Curved Scalar Wave variables.\n *\n * When radial_extents is 1, sw_vars and temp_storage are assumed to\n * be defined on a spherical slice, with number of grid points\n * corresponding to a spherical-harmonic grid of ell_max, and the\n * filter happens only on that slice.\n *\n * When radial_extents is > 1, sw_vars and temp_storage are assumed to\n * be defined on a spherical shell of topology I1 x S2. The filter\n * happens in the entire volume, internally iterating over each\n * spherical slice at a time.\n *\n * For performance reasons, apply_tensor_ylm_filter does not allocate\n * or deallocate memory, but it does take a temp_storage buffer.  The\n * size of temp_storage should at least\n * radial_extents*spectral_size*num_components, where num_components\n * is the total number of independent components in the SW variable\n * list (i.e. 5), and spectral_size is the size of the S2 Spherepack\n * spectral coefficient array for ell_max, as obtained from the member\n * function ylm::Spherepack::spectral_size().  Note that for S2 on\n * Spherepack, the number of collocation points is different than the\n * number of spectral coefficients, and both are different than the\n * size of the Spherepack storage array.\n *\n * \\param sw_vars Scalar wave variables at collocation points.\n * \\param temp_storage Temporary storage for scalar wave variables,\n *   allocated outside apply_tensor_ylm_filter. See above for size requirements.\n * \\param jac_inertial_to_grid Jacobian taking V_x from inertial to grid.\n * \\param jac_grid_to_inertial Jacobian taking V_x from grid to inertial.\n * \\param filter_matrix_scalar The scalar filter matrix computed by fill_filter.\n * \\param filter_matrix_i The Rank-1 matrix computed by fill_filter.\n * \\param ell_max The maximum ylm ell.\n * \\param radial_extents The number of radial grid points, can be 1 for slices.\n */\nvoid apply_tensor_ylm_filter(\n    gsl::not_null<Variables<filter_detail::sw_vars_list<Frame::Inertial>>*>\n        sw_vars,\n    gsl::not_null<Variables<filter_detail::sw_vars_list<Frame::Inertial>>*>\n        temp_storage,\n    const InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid>&\n        jac_inertial_to_grid,\n    const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>&\n        jac_grid_to_inertial,\n    const SimpleSparseMatrix& filter_matrix_scalar,\n    const SimpleSparseMatrix& filter_matrix_i, size_t ell_max,\n    size_t radial_extents);\n\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BackgroundSpacetime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace CurvedScalarWave {\nnamespace OptionTags {\n\nstruct BackgroundSpacetimeGroup {\n  static constexpr Options::String help = {\n      \"The background spacetime on which the scalar wave \"\n      \"propagates.\"};\n  static std::string name() { return \"BackgroundSpacetime\"; }\n};\n\ntemplate <typename BackgroundType>\nstruct BackgroundSpacetime {\n  static constexpr Options::String help = {\n      \"Options for the background spacetime on which the scalar wave \"\n      \"propagates.\"};\n  static std::string name() {\n    return pretty_type::short_name<BackgroundType>();\n  }\n  using type = BackgroundType;\n  using group = BackgroundSpacetimeGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief The background spacetime on which the scalar wave propagates.\n */\ntemplate <typename BackgroundType>\nstruct BackgroundSpacetime : db::SimpleTag {\n  using type = BackgroundType;\n  using option_tags =\n      tmpl::list<OptionTags::BackgroundSpacetime<BackgroundType>>;\n  static constexpr bool pass_metavariables = false;\n  static BackgroundType create_from_options(const BackgroundType& background) {\n    return background;\n  }\n};\n}  // namespace Tags\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/AnalyticConstant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/AnalyticConstant.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace CurvedScalarWave::BoundaryConditions {\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nAnalyticConstant<Dim>::get_clone() const {\n  return std::make_unique<AnalyticConstant>(*this);\n}\n\ntemplate <size_t Dim>\nvoid AnalyticConstant<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n  p | amplitude_;\n}\n\ntemplate <size_t Dim>\nAnalyticConstant<Dim>::AnalyticConstant(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nAnalyticConstant<Dim>::AnalyticConstant(const double amplitude)\n    : amplitude_(amplitude) {}\n\ntemplate <size_t Dim>\nstd::optional<std::string> AnalyticConstant<Dim>::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> psi,\n    const gsl::not_null<Scalar<DataVector>*> pi,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> shift,\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n        inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, Dim>& /*normal_covector*/,\n    const tnsr::I<DataVector, Dim>& /*normal_vector*/,\n    const tnsr::II<DataVector, Dim, Frame::Inertial>&\n        inverse_spatial_metric_interior,\n    const Scalar<DataVector>& gamma1_interior,\n    const Scalar<DataVector>& gamma2_interior,\n    const Scalar<DataVector>& lapse_interior,\n    const tnsr::I<DataVector, Dim>& shift_interior) const {\n  *psi = make_with_value<Scalar<DataVector>>(gamma1_interior, amplitude_);\n  *pi = make_with_value<Scalar<DataVector>>(gamma1_interior, 0.0);\n  *phi = make_with_value<tnsr::i<DataVector, Dim, Frame::Inertial>>(\n      gamma1_interior, 0.0);\n  *lapse = lapse_interior;\n  *shift = shift_interior;\n  *inverse_spatial_metric = inverse_spatial_metric_interior;\n  *gamma1 = gamma1_interior;\n  *gamma2 = gamma2_interior;\n\n  return {};\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID AnalyticConstant<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class AnalyticConstant<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/AnalyticConstant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace CurvedScalarWave::BoundaryConditions {\n/// A `BoundaryCondition` that imposes the scalar to be constant at the\n/// outer boundary.\ntemplate <size_t Dim>\nclass AnalyticConstant final : public BoundaryCondition<Dim> {\n public:\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Amplitude of the scalar at the boundary\"};\n  };\n  using options = tmpl::list<Amplitude>;\n  static constexpr Options::String help{\n      \"Boundary conditions which impose a constant scalar at the boundary.\"};\n\n  AnalyticConstant(double amplitude);\n  AnalyticConstant() = default;\n  /// \\cond\n  AnalyticConstant(AnalyticConstant&&) = default;\n  AnalyticConstant& operator=(AnalyticConstant&&) = default;\n  AnalyticConstant(const AnalyticConstant&) = default;\n  AnalyticConstant& operator=(const AnalyticConstant&) = default;\n  /// \\endcond\n  ~AnalyticConstant() override = default;\n\n  explicit AnalyticConstant(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, AnalyticConstant);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags = tmpl::list<\n      gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame::Inertial>,\n      Tags::ConstraintGamma1, Tags::ConstraintGamma2,\n      gr::Tags::Lapse<DataVector>,\n      gr::Tags::Shift<DataVector, Dim, Frame::Inertial>>;\n  using dg_interior_dt_vars_tags = tmpl::list<>;\n  using dg_interior_deriv_vars_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> psi,\n      const gsl::not_null<Scalar<DataVector>*> pi,\n      const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> phi,\n      const gsl::not_null<Scalar<DataVector>*> lapse,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> shift,\n      const gsl::not_null<Scalar<DataVector>*> gamma1,\n      const gsl::not_null<Scalar<DataVector>*> gamma2,\n      const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n          inverse_spatial_metric,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, Dim>& /*normal_covector*/,\n      const tnsr::I<DataVector, Dim>& /*normal_vector*/,\n      const tnsr::II<DataVector, Dim, Frame::Inertial>&\n          inverse_spatial_metric_interior,\n      const Scalar<DataVector>& gamma1_interior,\n      const Scalar<DataVector>& gamma2_interior,\n      const Scalar<DataVector>& lapse_interior,\n      const tnsr::I<DataVector, Dim>& shift_interior) const;\n\n private:\n  double amplitude_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace CurvedScalarWave::BoundaryConditions {\ntemplate <size_t Dim>\nBoundaryCondition<Dim>::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\ntemplate <size_t Dim>\nvoid BoundaryCondition<Dim>::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class BoundaryCondition<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace CurvedScalarWave {\n/// \\brief Boundary conditions for the curved scalar wave system\nnamespace BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\ntemplate <size_t Dim>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace BoundaryConditions\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AnalyticConstant.cpp\n  ConstraintPreservingSphericalRadiation.cpp\n  BoundaryCondition.cpp\n  DemandOutgoingCharSpeeds.cpp\n  Worldtube.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticConstant.hpp\n  ConstraintPreservingSphericalRadiation.hpp\n  BoundaryCondition.hpp\n  DemandOutgoingCharSpeeds.hpp\n  Factory.hpp\n  Worldtube.hpp\n)\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace CurvedScalarWave::BoundaryConditions {\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nConstraintPreservingSphericalRadiation<Dim>::get_clone() const {\n  return std::make_unique<ConstraintPreservingSphericalRadiation>(*this);\n}\n\ntemplate <size_t Dim>\nvoid ConstraintPreservingSphericalRadiation<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\nConstraintPreservingSphericalRadiation<\n    Dim>::ConstraintPreservingSphericalRadiation(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::optional<std::string>\nConstraintPreservingSphericalRadiation<Dim>::dg_time_derivative(\n    const gsl::not_null<Scalar<DataVector>*> dt_psi_correction,\n    const gsl::not_null<Scalar<DataVector>*> dt_pi_correction,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        dt_phi_correction,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim>& normal_covector,\n    const tnsr::I<DataVector, Dim>& normal_vector,\n    const Scalar<DataVector>& psi, const tnsr::i<DataVector, Dim>& phi,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, Dim>& shift,\n    const Scalar<DataVector>& logical_dt_psi,\n    const Scalar<DataVector>& logical_dt_pi,\n    const tnsr::i<DataVector, Dim>& logical_dt_phi,\n    const tnsr::i<DataVector, Dim>& d_psi, const tnsr::i<DataVector, Dim>& d_pi,\n    const tnsr::ij<DataVector, Dim>& d_phi) const {\n  Variables<tmpl::list<\n      ::Tags::Tempa<0, 3>, ::Tags::TempScalar<1>, ::Tags::TempScalar<2>,\n      // Inertial time derivatives\n      ::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>, ::Tags::dt<Tags::Phi<Dim>>>>\n      buffer{get<0>(coords).size()};\n\n  Scalar<DataVector> dt_psi;\n  Scalar<DataVector> dt_pi;\n  tnsr::i<DataVector, Dim> dt_phi;\n  if (face_mesh_velocity.has_value()) {\n    get(dt_psi).set_data_ref(\n        make_not_null(&get(get<::Tags::dt<Tags::Psi>>(buffer))));\n    get(dt_pi).set_data_ref(\n        make_not_null(&get(get<::Tags::dt<Tags::Pi>>(buffer))));\n    for (size_t i = 0; i < Dim; ++i) {\n      dt_phi.get(i).set_data_ref(\n          make_not_null(&get<::Tags::dt<Tags::Phi<Dim>>>(buffer).get(i)));\n    }\n\n    // Compute inertial time derivative\n    get(dt_psi) = get(logical_dt_psi);\n    get(dt_pi) = get(logical_dt_pi);\n    for (size_t d = 0; d < Dim; ++d) {\n      get(dt_psi) -= face_mesh_velocity->get(d) * d_psi.get(d);\n      get(dt_pi) -= face_mesh_velocity->get(d) * d_pi.get(d);\n    }\n    for (size_t i = 0; i < Dim; ++i) {\n      dt_phi.get(i) = logical_dt_phi.get(i);\n      for (size_t d = 0; d < Dim; ++d) {\n        dt_phi.get(i) -= face_mesh_velocity->get(d) * d_phi.get(d, i);\n      }\n    }\n  } else {\n    get(dt_psi).set_data_ref(\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        make_not_null(&const_cast<DataVector&>(get(logical_dt_psi))));\n    get(dt_pi).set_data_ref(\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        make_not_null(&const_cast<DataVector&>(get(logical_dt_pi))));\n    for (size_t i = 0; i < Dim; ++i) {\n      dt_phi.get(i).set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          make_not_null(&const_cast<DataVector&>(logical_dt_phi.get(i))));\n    }\n  }\n\n  auto& char_speeds = get<::Tags::Tempa<0, 3>>(buffer);\n  auto& inv_radius = get<::Tags::TempScalar<1>>(buffer);\n  auto& face_speed = get<::Tags::TempScalar<2>>(buffer);\n\n  get(inv_radius) = 1. / get(magnitude(coords));\n  characteristic_speeds(make_not_null(&char_speeds), gamma1, lapse, shift,\n                        normal_covector);\n\n  if (face_mesh_velocity.has_value()) {\n    face_speed = dot_product(normal_covector, *face_mesh_velocity);\n    for (size_t i = 0; i < 4; ++i) {\n      char_speeds.get(i) -= get(face_speed);\n    }\n  }\n\n  get(*dt_psi_correction) =\n      get<0>(normal_vector) * (get<0>(d_psi) - get<0>(phi));\n  for (size_t i = 1; i < Dim; ++i) {\n    get(*dt_psi_correction) +=\n        normal_vector.get(i) * (d_psi.get(i) - phi.get(i));\n  }\n\n  for (size_t i = 0; i < Dim; ++i) {\n    dt_phi_correction->get(i) =\n        get<0>(normal_vector) * (d_phi.get(0, i) - d_phi.get(i, 0));\n    for (size_t j = 1; j < Dim; ++j) {\n      dt_phi_correction->get(i) +=\n          normal_vector.get(j) * (d_phi.get(j, i) - d_phi.get(i, j));\n    }\n  }\n\n  for (size_t i = 0; i < get(*dt_psi_correction).size(); ++i) {\n    get(*dt_psi_correction)[i] *= std::min(0., gsl::at(char_speeds[0], i));\n    for (size_t j = 0; j < Dim; ++j) {\n      dt_phi_correction->get(j)[i] *= std::min(0., gsl::at(char_speeds[1], i));\n    }\n  }\n\n  get(*dt_pi_correction) =\n      (2.0 * get(inv_radius) * get(psi) + 4.0 * get(dt_psi)) * get(inv_radius);\n  for (size_t i = 0; i < Dim; ++i) {\n    get(*dt_pi_correction) +=\n        normal_vector.get(i) *\n            (2. * dt_phi.get(i) + 4.0 * get(inv_radius) * phi.get(i)) +\n        shift.get(i) * dt_phi.get(i);\n    for (size_t j = 0; j < Dim; ++j) {\n      get(*dt_pi_correction) +=\n          normal_vector.get(i) * normal_vector.get(j) * d_phi.get(i, j);\n    }\n  }\n  get(*dt_pi_correction) /= get(lapse);\n  get(*dt_pi_correction) += get(gamma2) * get(*dt_psi_correction) - get(dt_pi);\n  return {};\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID ConstraintPreservingSphericalRadiation<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class ConstraintPreservingSphericalRadiation<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace CurvedScalarWave::BoundaryConditions {\n\n/*!\n * \\brief Implements constraint-preserving boundary conditions with a second\n * order Bayliss-Turkel radiation boundary condition.\n *\n * \\details The Bayliss-Turkel boundary conditions are technically only valid in\n * flat space and should therefore only be used at boundaries where the\n * background spacetime is approximately Minkwoski such as (sufficiently far\n * out) outer boundaries for asymptotically flat spacetimes. Small reflections\n * are still likely to occur.\n *\n * The constraint-preserving part of the boundary conditions are set on the time\n * derivatives of all evolved fields. The physical Bayliss-Turkel boundary\n * conditions are additionally set onto the time derivative of \\f$\\Pi\\f$.\n *\n * The constraints are defined as follows:\n *\n * \\f{align*}{\n *  \\mathcal{C}_i&=\\partial_i\\Psi - \\Phi_i=0, \\\\\n *  \\mathcal{C}_{ij}&=\\partial_{[i}\\Phi_{j]}=0\n * \\f}\n * Inspection of the constraint evolution system (Eqs. 29-30 in\n * \\cite Holst2004wt) shows that the constraints themselves are characteristic\n * fields. We can derive constraint boundary conditions the same way\n * \\cite Kidder2004rw does for the Einstein equations:\n *\n * We express the constraints in terms of (evolution) characeristic fields and\n * demand that the normal component of the constraint has to be zero when\n * flowing into the boundary i.e. there are no constraints flowing into our\n * numerical domain:\n *\n * \\f{align*}{\n * 0 &= n^i \\mathcal{C}_i &= n^i \\partial_i w^\\Psi - \\frac{1}{2}(w^{+} - w^-) +\n * n^i\n * w_i^0 \\\\\n * (n^i \\partial_i w^\\Psi)_{BC}  &= \\frac{1}{2}(w^{+} - w^-) - n^i w_i^0\n * \\f}\n *\n * and\n *\n * \\f{align*}{\n * 0 &= 2 n^i \\mathcal{C}_{ij} = n^i \\partial_i w^0_j + \\frac{1}{2}n^i n_j\n * (\\partial_i w^+ - \\partial_i w^-) - \\frac{1}{2}(\\partial_j w^+ - \\partial_j\n * w^-) - n^i \\partial_j w^0_i \\\\\n * (n^i \\partial_i w^0_j)_{BC} &= - \\frac{1}{2}n^i\n * n_j (\\partial_i w^+ - \\partial_i w^-) + \\frac{1}{2}(\\partial_j w^+ +\n * \\partial_j w^-) + n^i \\partial_j w^0_i \\f}\n *\n * This condition is applied to the time derivative using the Bjorhus condition\n * \\cite Bjorhus1995 :\n * \\f{align*}{\n * \\partial_t u^\\alpha + A^{i \\alpha}_\\beta \\partial_i u^\\beta &= F^\\alpha \\\\\n *  e^{\\hat{\\alpha}}_\\alpha (\\partial_t u^\\alpha + A^{i \\alpha}_\\beta\n * \\partial_i u^\\beta) &= e^{\\hat{\\alpha}}_\\alpha F^\\alpha  \\\\\n * d_t u^{\\hat{\\alpha}} + e^{\\hat{\\alpha}}_\\alpha A^{i\n * \\alpha}_\\beta(P^k_i + n^k n_i) \\partial_k u^\\beta &= e^{\\hat{\\alpha}}_\\alpha\n * F^\\alpha  \\\\\n * d_t u^{\\hat{\\alpha}} + \\lambda_{(\\hat{\\alpha})} n^k \\partial_k\n * u^{\\hat{\\alpha}} + e^{\\hat{\\alpha}}_\\alpha A^{i \\alpha}_\\beta P^k_i\n * \\partial_k u^\\beta &= e^{\\hat{\\alpha}}_\\alpha F^\\alpha\n * \\f}\n *\n * Defining the volume time derivative of the characteristic fields as:\n * \\f{equation*}{\n * D_t u^{\\hat{\\alpha}} \\equiv e^{\\hat{\\alpha}}_\\alpha (- A^{i \\alpha}_\\beta\n * \\partial_i u^\\beta + F^\\alpha)\n * \\f}\n *\n * The boundary conditions are now formulated as follows:\n *\n * \\f{equation*}{\n *  d_t u^{\\hat{\\alpha}} = D_t u^{\\hat{\\alpha}} + \\lambda_{(\\hat{\\alpha})}\n * (n^i\\partial_i u^{\\hat{\\alpha}} - (n^i\\partial_i u^{\\hat{\\alpha}})_{BC})\n * \\f}\n *\n * Using the condition that there are no incoming constraint fields, this gives:\n *\n * \\f{align*}{\n * d_t Z^1 &= D_t w^\\Psi + \\lambda_\\Psi n^i \\mathcal{C}_i \\\\\n * d_t Z^2_i &= D_t w^0_i + 2 \\lambda_0 n^i \\mathcal{C}_{ij}\n * \\f}\n *\n * The Bayliss-Turkel boundary conditions are given by:\n *\n * \\f{align*}{\n *  \\prod_{l=1}^m\\left(\\partial_t + \\partial_r + \\frac{2l-1}{r}\\right)\\Psi=0,\n * \\f}\n *\n * which we expand here to second order (\\f$m=2\\f$) to derive conditions for\n * \\f$\\partial_t\\Pi^{\\mathrm{BC}}\\f$:\n *\n * \\f{align*}{\n *  \\partial_t\\Pi^{\\mathrm{BC}}\n *   &=\\left(\\partial_t\\partial_r + \\partial_r\\partial_t +\n *     \\partial_r^2+\\frac{4}{r}\\partial_t\n *     +\\frac{4}{r}\\partial_r + \\frac{2}{r^2}\\right)\\Psi \\notag \\\\\n *     &=\\left((2n^i + \\beta^i) \\partial_t \\Phi_i + n^i n^j\\partial_i\\Phi_j +\n *     \\frac{4}{r}\\partial_t\\Psi + \\frac{4}{r}n^i\\Phi_i + \\frac{2}{r^2}\\Psi\n * \\right) / \\alpha.\n * \\f}\n *\n *\n * This derivation makes the following assumptions:\n *\n * - The lapse, shift, normal vector and radius are time-independent,\n * \\f$\\partial_t \\alpha = \\partial_t \\beta^i = \\partial_t n^i = \\partial_t r =\n * 0\\f$. If necessary, these time derivatives can be accounted for in the future\n * by inserting the appropriate terms in a straightforward manner.\n *\n * - The outer boundary is spherical. It might be possible to generalize this\n * condition but we have not tried this.\n *\n *  * The boundary conditions to the time derivative of the evolved variables\n * are then given by:\n *\n * The full boundary conditions, as applied to the time derivative of each\n * evolved field are then given by:\n * \\f{align*}{ \\partial_{t}\n * \\Psi&\\to\\partial_{t}\\Psi +\n *                    \\lambda_\\Psi n^i \\mathcal{C}_i, \\\\\n * \\partial_{t}\\Pi&\\to\\partial_{t}\\Pi-\\left(\\partial_t\\Pi\n *                  - \\partial_t\\Pi^{\\mathrm{BC}}\\right)\n *                  +\\gamma_2\\lambda_\\Psi n^i \\mathcal{C}_i\n *                  =\\partial_t\\Pi^{\\mathrm{BC}}\n *                  +\\gamma_2\\lambda_\\Psi n^i \\mathcal{C}_i, \\\\\n * \\partial_{t}\\Phi_i&\\to\\partial_{t}\\Phi_i+ 2 \\lambda_0 n^j \\mathcal{C}_{ji}.\n * \\f}\n *\n */\ntemplate <size_t Dim>\nclass ConstraintPreservingSphericalRadiation final\n    : public BoundaryCondition<Dim> {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Constraint-preserving boundary conditions with a second order \"\n      \"Bayliss-Turkel radiation boundary condition.\"};\n  ConstraintPreservingSphericalRadiation() = default;\n  ConstraintPreservingSphericalRadiation(\n      ConstraintPreservingSphericalRadiation&&) = default;\n  ConstraintPreservingSphericalRadiation& operator=(\n      ConstraintPreservingSphericalRadiation&&) = default;\n  ConstraintPreservingSphericalRadiation(\n      const ConstraintPreservingSphericalRadiation&) = default;\n  ConstraintPreservingSphericalRadiation& operator=(\n      const ConstraintPreservingSphericalRadiation&) = default;\n  ~ConstraintPreservingSphericalRadiation() override = default;\n\n  explicit ConstraintPreservingSphericalRadiation(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition,\n      ConstraintPreservingSphericalRadiation);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::TimeDerivative;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<Tags::Psi, Tags::Phi<Dim>>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 Tags::ConstraintGamma1, Tags::ConstraintGamma2,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>>;\n  using dg_interior_dt_vars_tags =\n      tmpl::list<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                 ::Tags::dt<Tags::Phi<Dim>>>;\n  using dg_interior_deriv_vars_tags = tmpl::list<\n      ::Tags::deriv<Tags::Psi, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::deriv<Tags::Pi, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::deriv<Tags::Phi<Dim>, tmpl::size_t<Dim>, Frame::Inertial>>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_time_derivative(\n      gsl::not_null<Scalar<DataVector>*> dt_psi_correction,\n      gsl::not_null<Scalar<DataVector>*> dt_pi_correction,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          dt_phi_correction,\n      const std::optional<tnsr::I<DataVector, Dim>>& face_mesh_velocity,\n      const tnsr::i<DataVector, Dim>& normal_covector,\n      const tnsr::I<DataVector, Dim>& normal_vector,\n      const Scalar<DataVector>& psi, const tnsr::i<DataVector, Dim>& phi,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n      const Scalar<DataVector>& lapse, const tnsr::I<DataVector, Dim>& shift,\n      const Scalar<DataVector>& logical_dt_psi,\n      const Scalar<DataVector>& logical_dt_pi,\n      const tnsr::i<DataVector, Dim>& logical_dt_phi,\n      const tnsr::i<DataVector, Dim>& d_psi,\n      const tnsr::i<DataVector, Dim>& d_pi,\n      const tnsr::ij<DataVector, Dim>& d_phi) const;\n};\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace CurvedScalarWave::BoundaryConditions {\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDemandOutgoingCharSpeeds<Dim>::get_clone() const {\n  return std::make_unique<DemandOutgoingCharSpeeds>(*this);\n}\n\ntemplate <size_t Dim>\nvoid DemandOutgoingCharSpeeds<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\nDemandOutgoingCharSpeeds<Dim>::DemandOutgoingCharSpeeds(\n    CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::optional<std::string>\nDemandOutgoingCharSpeeds<Dim>::dg_demand_outgoing_char_speeds(\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim>& normal_covector,\n    const tnsr::I<DataVector, Dim>& /*normal_vector*/,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim>& shift) const {\n  tnsr::a<DataVector, 3, Frame::Inertial> char_speeds{lapse.size()};\n  characteristic_speeds(make_not_null(&char_speeds), gamma1, lapse, shift,\n                        normal_covector);\n\n  if (face_mesh_velocity.has_value()) {\n    const auto face_speed = dot_product(normal_covector, *face_mesh_velocity);\n    for (auto& char_speed : char_speeds) {\n      char_speed -= get(face_speed);\n    }\n  }\n  for (size_t i = 0; i < char_speeds.size(); ++i) {\n    if (min(char_speeds[i]) < 0.) {\n      return MakeString{}\n             << \"Detected negative characteristic speed at boundary with \"\n                \"outgoing char speeds boundary conditions specified. The \"\n                \"speed is \"\n             << min(char_speeds[i]) << \" for index \" << i\n             << \". To see which characteristic field this corresponds to, \"\n                \"check the function `characteristic_speeds` in \"\n                \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp.\";\n    }\n  }\n  return std::nullopt;  // LCOV_EXCL_LINE\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DemandOutgoingCharSpeeds<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class DemandOutgoingCharSpeeds<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/DemandOutgoingCharSpeeds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace CurvedScalarWave::BoundaryConditions {\n/// A `BoundaryCondition` that only verifies that all characteristic speeds are\n/// directed out of the domain; no boundary data is altered by this boundary\n/// condition.\ntemplate <size_t Dim>\nclass DemandOutgoingCharSpeeds final : public BoundaryCondition<Dim> {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Boundary conditions which check that all characteristic \"\n      \"fields are outflowing.\"};\n  DemandOutgoingCharSpeeds() = default;\n  /// \\cond\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  /// \\endcond\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim>>;\n  using dg_interior_dt_vars_tags = tmpl::list<>;\n  using dg_interior_deriv_vars_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, Dim>& normal_covector,\n      const tnsr::I<DataVector, Dim>& /*normal_vector*/,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim>& shift) const;\n};\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/AnalyticConstant.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\ntemplate <size_t Dim>\nusing standard_boundary_conditions =\n    tmpl::list<AnalyticConstant<Dim>,\n               ConstraintPreservingSphericalRadiation<Dim>,\n               DemandOutgoingCharSpeeds<Dim>,\n               domain::BoundaryConditions::Periodic<BoundaryCondition<Dim>>>;\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/Worldtube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Worldtube.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\nnamespace CurvedScalarWave::BoundaryConditions {\n\ntemplate <size_t Dim>\nWorldtube<Dim>::Worldtube(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nWorldtube<Dim>::get_clone() const {\n  return std::make_unique<Worldtube>(*this);\n}\n\ntemplate <size_t Dim>\nvoid Worldtube<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Worldtube<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nstd::optional<std::string> Worldtube<Dim>::dg_time_derivative(\n    gsl::not_null<Scalar<DataVector>*> dt_psi_correction,\n    gsl::not_null<Scalar<DataVector>*> dt_pi_correction,\n    gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> dt_phi_correction,\n    const std::optional<tnsr::I<DataVector, Dim>>& face_mesh_velocity,\n    const tnsr::i<DataVector, Dim>& normal_covector,\n    const tnsr::I<DataVector, Dim>& normal_vector,\n    const Scalar<DataVector>& /*psi*/, const Scalar<DataVector>& /*pi*/,\n    const tnsr::i<DataVector, Dim>& phi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim>& shift,\n    const tnsr::II<DataVector, Dim,\n                   Frame::Inertial>& /*inverse_spatial_metric*/,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& /*dt_psi*/, const tnsr::i<DataVector, Dim>& d_psi,\n    const tnsr::ij<DataVector, Dim>& d_phi,\n    const Variables<tmpl::list<Tags::Psi, Tags::Pi,\n                               Tags::Phi<Dim>>>& /*worldtube_vars*/) const {\n  auto char_speeds =\n      characteristic_speeds(gamma1, lapse, shift, normal_covector);\n  if (face_mesh_velocity.has_value()) {\n    const auto face_speed = dot_product(normal_covector, *face_mesh_velocity);\n    for (size_t i = 0; i < 4; ++i) {\n      char_speeds.at(i) -= get(face_speed);\n    }\n  }\n  *dt_psi_correction =\n      tenex::evaluate(normal_vector(ti::I) * (d_psi(ti::i) - phi(ti::i)));\n  *dt_phi_correction = tenex::evaluate<ti::i>(\n      normal_vector(ti::J) * (d_phi(ti::j, ti::i) - d_phi(ti::i, ti::j)));\n\n  for (size_t i = 0; i < get(*dt_psi_correction).size(); ++i) {\n    get(*dt_psi_correction)[i] *= std::min(0., gsl::at(char_speeds[0], i));\n    for (size_t j = 0; j < Dim; ++j) {\n      dt_phi_correction->get(j)[i] *= std::min(0., gsl::at(char_speeds[1], i));\n    }\n  }\n  get(*dt_pi_correction) = get(gamma2) * get(*dt_psi_correction);\n  return {};\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string> Worldtube<Dim>::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> psi,\n    const gsl::not_null<Scalar<DataVector>*> pi,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> shift,\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n        inverse_spatial_metric,\n\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n    const Scalar<DataVector>& psi_interior,\n    const Scalar<DataVector>& pi_interior,\n    const tnsr::i<DataVector, Dim>& phi_interior,\n    const Scalar<DataVector>& lapse_interior,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift_interior,\n    const tnsr::II<DataVector, Dim, Frame::Inertial>&\n        inverse_spatial_metric_interior,\n    const Scalar<DataVector>& gamma1_interior,\n    const Scalar<DataVector>& gamma2_interior,\n    const Scalar<DataVector>& /*dt_psi*/,\n    const tnsr::i<DataVector, Dim>& /*d_psi*/,\n    const tnsr::ij<DataVector, Dim>& /*d_phi*/,\n    const Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>>&\n        worldtube_vars) const {\n  ASSERT(worldtube_vars.number_of_grid_points() == get(lapse_interior).size(),\n         \"The worldtube solution has the wrong number of grid points: \"\n             << worldtube_vars.number_of_grid_points());\n  *lapse = lapse_interior;\n  *shift = shift_interior;\n  *inverse_spatial_metric = inverse_spatial_metric_interior;\n  *gamma1 = gamma1_interior;\n  *gamma2 = gamma2_interior;\n\n  const auto& psi_worldtube = get<Tags::Psi>(worldtube_vars);\n  const auto& pi_worldtube = get<Tags::Pi>(worldtube_vars);\n  const auto& phi_worldtube = get<Tags::Phi<Dim>>(worldtube_vars);\n\n  // `VMinus` is calculated from the worldtube solution, the other\n  // characteristic fields are set to the corresponding values of the evolved\n  // variables. This is equivalent to giving no boundary conditions on those\n  // fields.\n  Variables<tmpl::list<Tags::VPsi, Tags::VZero<Dim>, Tags::VPlus, Tags::VMinus>>\n      char_fields_mixed(get(gamma1_interior).size());\n\n  auto& v_psi = get<Tags::VPsi>(char_fields_mixed);\n  auto& v_zero = get<Tags::VZero<Dim>>(char_fields_mixed);\n  auto& v_plus = get<Tags::VPlus>(char_fields_mixed);\n  auto& v_minus = get<Tags::VMinus>(char_fields_mixed);\n\n  // use allocation for temporary\n  v_psi = dot_product(normal_vector, phi_interior);\n  v_zero = tenex::evaluate<ti::i>(phi_interior(ti::i) -\n                                  normal_covector(ti::i) * v_psi());\n  v_plus = tenex::evaluate(pi_interior() + v_psi() -\n                           gamma2_interior() * psi_interior());\n  v_psi = psi_interior;\n  v_minus = tenex::evaluate(pi_worldtube() -\n                            normal_vector(ti::I) * phi_worldtube(ti::i) -\n                            gamma2_interior() * psi_worldtube());\n\n  evolved_fields_from_characteristic_fields(psi, pi, phi, gamma2_interior,\n                                            v_psi, v_zero, v_plus, v_minus,\n                                            normal_covector);\n  return {};\n}\n\ntemplate class Worldtube<3>;\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryConditions/Worldtube.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::BoundaryConditions {\n\n/*!\n * \\brief Sets boundary conditions for the elements abutting the worldtube using\n * a combination of constraint-preserving boundary conditions and the local\n * solution evolved inside the worldtube.\n *\n * \\details After extensive experimentation we found this set of boundary\n * conditions to be optimal. They are formulated in terms of characteristic\n * fields.\n *\n * Boundary conditions for \\f$w^0_\\psi\\f$ \\f$w^0_i\\f$ are formulated\n * by demanding that there are no constraint violations flowing into the\n * numerical domain and are formulated as corrections to the time derivative of\n * the evolved variables directly. The derivation is described in\n * \\ref ConstraintPreservingSphericalRadiation .\n *\n * Boundary conditions for \\f$\\w^-\\f$ are formulated by evaluating the\n * analytical solution of the worldtube at the grid points of each element\n * abutting the worldtube. The data is updated and saved to\n * \\ref CurvedScalarWave::Worldtube::Tags::WorldtubeSolution each time step.\n * It is then treated like a ghost field and applied with the chosen numerical\n * flux. So far only the upwind flux has been tried.\n *\n * We tried several other combinations such as setting all fields from the\n * worldtube solution but found that this caused major constraint violations to\n * flow out of the worldtube.\n *\n * \\note We found that, depending on the worldtube size, a fairly high value for\n * \\f$\\gamma_2\\f$ of ca. 10 is required near the worldtube to ensure a stable\n * evolution when using these boundary conditions.\n */\ntemplate <size_t Dim>\nclass Worldtube final : public BoundaryConditions::BoundaryCondition<Dim> {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Boundary conditions set by the worldtube. w^- will be set by the \"\n      \"internal worldtube solution, w^psi and w^0_i are fixed by constraint \"\n      \"preserving boundary conditions on the time derivative.\"};\n\n  Worldtube() = default;\n  explicit Worldtube(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, Worldtube);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::GhostAndTimeDerivative;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_temporary_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n                 Tags::ConstraintGamma1, Tags::ConstraintGamma2>;\n\n  using dg_gridless_tags =\n      tmpl::list<CurvedScalarWave::Worldtube::Tags::WorldtubeSolution<Dim>>;\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>;\n  using dg_interior_dt_vars_tags = tmpl::list<::Tags::dt<Tags::Psi>>;\n  using dg_interior_deriv_vars_tags = tmpl::list<\n      ::Tags::deriv<Tags::Psi, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::deriv<Tags::Phi<Dim>, tmpl::size_t<Dim>, Frame::Inertial>>;\n\n  std::optional<std::string> dg_time_derivative(\n      gsl::not_null<Scalar<DataVector>*> dt_psi_correction,\n      gsl::not_null<Scalar<DataVector>*> dt_pi_correction,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          dt_phi_correction,\n      const std::optional<tnsr::I<DataVector, Dim>>& face_mesh_velocity,\n      const tnsr::i<DataVector, Dim>& normal_covector,\n      const tnsr::I<DataVector, Dim>& normal_vector,\n      const Scalar<DataVector>& /*psi*/, const Scalar<DataVector>& /*pi*/,\n      const tnsr::i<DataVector, Dim>& phi, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim>& shift,\n      const tnsr::II<DataVector, Dim,\n                     Frame::Inertial>& /*inverse_spatial_metric*/,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n      const Scalar<DataVector>& /*dt_psi*/,\n      const tnsr::i<DataVector, Dim>& d_psi,\n      const tnsr::ij<DataVector, Dim>& d_phi,\n      const Variables<tmpl::list<Tags::Psi, Tags::Pi,\n                                 Tags::Phi<Dim>>>& /*worldtube_vars*/) const;\n\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> psi,\n      const gsl::not_null<Scalar<DataVector>*> pi,\n      const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> phi,\n      const gsl::not_null<Scalar<DataVector>*> lapse,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> shift,\n      const gsl::not_null<Scalar<DataVector>*> gamma1,\n      const gsl::not_null<Scalar<DataVector>*> gamma2,\n      const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n          inverse_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const Scalar<DataVector>& psi_interior,\n      const Scalar<DataVector>& pi_interior,\n      const tnsr::i<DataVector, Dim>& phi_interior,\n      const Scalar<DataVector>& lapse_interior,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& shift_interior,\n      const tnsr::II<DataVector, Dim, Frame::Inertial>&\n          inverse_spatial_metric_interior,\n      const Scalar<DataVector>& gamma1_interior,\n      const Scalar<DataVector>& gamma2_interior,\n      const Scalar<DataVector>& /*dt_psi*/,\n      const tnsr::i<DataVector, Dim>& d_psi,\n      const tnsr::ij<DataVector, Dim>& /*d_phi*/,\n      const Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>>&\n          worldtube_vars) const;\n};\n}  // namespace CurvedScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  UpwindPenalty.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  NamespaceDocs.hpp\n  UpwindPenalty.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::BoundaryCorrections {\ntemplate <size_t Dim>\nusing standard_boundary_corrections = tmpl::list<UpwindPenalty<Dim>>;\n}  // namespace CurvedScalarWave::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace CurvedScalarWave::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::BoundaryCorrections {\ntemplate <size_t Dim>\nUpwindPenalty<Dim>::UpwindPenalty(CkMigrateMessage* msg)\n    : BoundaryCorrection(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::BoundaryCorrection> UpwindPenalty<Dim>::get_clone()\n    const {\n  return std::make_unique<UpwindPenalty>(*this);\n}\n\ntemplate <size_t Dim>\nvoid UpwindPenalty<Dim>::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n}\n\ntemplate <size_t Dim>\ndouble UpwindPenalty<Dim>::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_v_psi,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        packaged_v_zero,\n    const gsl::not_null<Scalar<DataVector>*> packaged_v_plus,\n    const gsl::not_null<Scalar<DataVector>*> packaged_v_minus,\n    const gsl::not_null<Scalar<DataVector>*> packaged_gamma2,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        packaged_interface_unit_normal,\n    const gsl::not_null<tnsr::a<DataVector, 3, Frame::Inertial>*>\n        packaged_char_speeds,\n\n    const Scalar<DataVector>& psi, const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const Scalar<DataVector>& constraint_gamma1,\n    const Scalar<DataVector>& constraint_gamma2,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& interface_unit_normal,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        interface_unit_normal_vector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n  *packaged_gamma2 = constraint_gamma2;\n  *packaged_interface_unit_normal = interface_unit_normal;\n  characteristic_fields(packaged_v_psi, packaged_v_zero, packaged_v_plus,\n                        packaged_v_minus, constraint_gamma2, psi, pi, phi,\n                        interface_unit_normal, interface_unit_normal_vector);\n  characteristic_speeds(packaged_char_speeds, constraint_gamma1, lapse, shift,\n                        interface_unit_normal);\n  if (normal_dot_mesh_velocity.has_value()) {\n    get<0>(*packaged_char_speeds) -= get(*normal_dot_mesh_velocity);\n    get<1>(*packaged_char_speeds) -= get(*normal_dot_mesh_velocity);\n    get<2>(*packaged_char_speeds) -= get(*normal_dot_mesh_velocity);\n    get<3>(*packaged_char_speeds) -= get(*normal_dot_mesh_velocity);\n  }\n\n  return max(max(get<0>(*packaged_char_speeds), get<1>(*packaged_char_speeds),\n                 get<2>(*packaged_char_speeds)));\n}\n\ntemplate <size_t Dim>\nvoid UpwindPenalty<Dim>::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> psi_boundary_correction,\n    const gsl::not_null<Scalar<DataVector>*> pi_boundary_correction,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        phi_boundary_correction,\n\n    const Scalar<DataVector>& v_psi_int,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero_int,\n    const Scalar<DataVector>& v_plus_int, const Scalar<DataVector>& v_minus_int,\n    const Scalar<DataVector>& gamma2_int,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& interface_unit_normal_int,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds_int,\n\n    const Scalar<DataVector>& v_psi_ext,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero_ext,\n    const Scalar<DataVector>& v_plus_ext, const Scalar<DataVector>& v_minus_ext,\n    const Scalar<DataVector>& /*gamma2_ext*/,\n    const tnsr::i<DataVector, Dim,\n                  Frame::Inertial>& /*interface_unit_normal_ext*/,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds_ext,\n    dg::Formulation /*dg_formulation*/) const {\n  // The implementations assumes that the external unit normal vector is\n  // exactly negative the internal unit normal vector and that the internal\n  // gamma2 is equal to the external gamma2. For Gauss quadrature this will not\n  // be exactly true due to interpolation error but that should not matter.\n  get(*psi_boundary_correction) = -step_function(get<0>(char_speeds_ext)) *\n                                      get<0>(char_speeds_ext) * get(v_psi_ext) -\n                                  step_function(-get<0>(char_speeds_int)) *\n                                      get<0>(char_speeds_int) * get(v_psi_int);\n\n  auto& temp_1 = get<Dim - 1>(*phi_boundary_correction);\n  temp_1 = -step_function(get<2>(char_speeds_ext)) * get<2>(char_speeds_ext) *\n               get(v_plus_ext) -\n           step_function(-get<3>(char_speeds_int)) * get<3>(char_speeds_int) *\n               get(v_minus_int);\n\n  // in 2+ dimensions the calculation is done without any memory allocations\n  DataVector temp_2{};\n  if constexpr (Dim > 1) {\n    temp_2.set_data_ref(make_not_null(&get<Dim - 2>(*phi_boundary_correction)));\n  }\n  temp_2 = step_function(-get<2>(char_speeds_int)) * get<2>(char_speeds_int) *\n               get(v_plus_int) +\n           step_function(get<3>(char_speeds_ext)) * get<3>(char_speeds_ext) *\n               get(v_minus_ext);\n  get(*pi_boundary_correction) =\n      0.5 * (temp_1 - temp_2) + get(gamma2_int) * get(*psi_boundary_correction);\n\n  temp_1 = -0.5 * (temp_1 + temp_2);\n  for (size_t i = 0; i < Dim; ++i) {\n    phi_boundary_correction->get(i) =\n        temp_1 * interface_unit_normal_int.get(i) -\n        step_function(get<1>(char_speeds_ext)) * get<1>(char_speeds_ext) *\n            v_zero_ext.get(i) -\n        step_function(-get<1>(char_speeds_int)) * get<1>(char_speeds_int) *\n            v_zero_int.get(i);\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const UpwindPenalty<Dim>& /*lhs*/,\n                const UpwindPenalty<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const UpwindPenalty<Dim>& lhs, const UpwindPenalty<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID UpwindPenalty<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                                       \\\n  template class UpwindPenalty<DIM(data)>;                           \\\n  template bool operator==(const UpwindPenalty<DIM(data)>& /*lhs*/,  \\\n                           const UpwindPenalty<DIM(data)>& /*rhs*/); \\\n  template bool operator!=(const UpwindPenalty<DIM(data)>& /*lhs*/,  \\\n                           const UpwindPenalty<DIM(data)>& /*rhs*/);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace CurvedScalarWave::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace CurvedScalarWave::BoundaryCorrections {\n/*!\n * \\brief Computes the upwind multipenalty boundary correction for scalar wave\n * in curved spacetime.\n *\n * \\details This implements the upwind multipenalty boundary correction term.\n * The general form is given by:\n *\n * \\f[\n * G = T^{\\rm ext}\\Lambda^{\\rm ext,-} {T^{\\rm ext}}^{-1} U^{\\rm ext}\n *      + T^{\\rm int}\\Lambda^{\\rm int,+} {T^{\\rm int}}^{-1} U^{\\rm int}\n *   = T^{\\rm ext}\\Lambda^{\\rm ext,-} V^{\\rm ext}\n *      + T^{\\rm int}\\Lambda^{\\rm int,+} V^{\\rm int},\n * \\f]\n *\n * where\n *\n *  - \\f$G\\f$ is a vector of numerical upwind fluxes dotted with the interface\n *       normal for all evolved variables;\n *  - \\f$U\\f$ is a vector of all evolved variables;\n *  - \\f$T\\f$ is a matrix whose columns are the eigenvectors of the\n *       characteristic matrix for the evolution system. It maps the\n *       evolved variables to characteristic variables \\f$V\\f$, s.t.\n *       \\f$V := T^{-1}\\cdot U\\f$; and\n *  - \\f$\\Lambda^\\pm\\f$ are diagonal matrices containing positive / negative\n *       eigenvalues of \\f$T\\f$ as its diagonal elements, with the rest set to\n *       \\f$ 0\\f$.\n *\n * The superscripts \\f${\\rm int}\\f$ and \\f${\\rm ext}\\f$ indicate that the\n * corresponding variable at the element interface comes from the _interior_ or\n * _exterior_ of the element. Exterior of the element is naturally the interior\n * of its neighboring element. The sign of characteristic speeds indicate the\n * direction of propagation of the corresponding characteristic field with\n * respect to the interface normal that the field has been computed along, with\n * negative speeds indicating incoming characteristics, and positive speeds\n * indicating outgoing characteristics. The expressions implemented here differ\n * from Eq.(63) of \\cite Teukolsky2015ega as that boundary term does not\n * consistently treat both sides of the interface on the same footing. Unlike in\n * \\cite Teukolsky2015ega, in code the interface normal vector on the interior\n * side and the one on the exterior side point in opposite directions, and the\n * characteristic speeds end up having different signs. This class therefore\n * computes:\n *\n * \\f[\n * G = T^{\\rm ext}w_{\\rm ext} V^{\\rm ext} - T^{\\rm int}w_{\\rm int} V^{\\rm int},\n * \\f]\n *\n * with weights \\f$w_{\\rm ext} = -\\Theta(\\Lambda^{\\rm ext})\\cdot\\Lambda^{\\rm\n * ext}\\f$, and \\f$w_{\\rm int} = \\Theta(-\\Lambda^{\\rm int})\\cdot\\Lambda^{\\rm\n * int}\\f$, where \\f$\\Theta\\f$ is the Heaviside function centered at zero,\n * \\f$\\Lambda = \\Lambda^+ + \\Lambda^-\\f$, and the dot operator \\f$(\\cdot)\\f$\n * indicates an element-wise product.\n */\ntemplate <size_t Dim>\nclass UpwindPenalty final : public evolution::BoundaryCorrection {\n private:\n  struct CharSpeedsTensor : db::SimpleTag {\n    using type = tnsr::a<DataVector, 3, Frame::Inertial>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the UpwindPenalty boundary correction term for the scalar wave \"\n      \"system in curved spacetime.\"};\n\n  UpwindPenalty() = default;\n  UpwindPenalty(const UpwindPenalty&) = default;\n  UpwindPenalty& operator=(const UpwindPenalty&) = default;\n  UpwindPenalty(UpwindPenalty&&) = default;\n  UpwindPenalty& operator=(UpwindPenalty&&) = default;\n  ~UpwindPenalty() override = default;\n\n  /// \\cond\n  explicit UpwindPenalty(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(UpwindPenalty);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::VPsi, Tags::VZero<Dim>, Tags::VPlus, Tags::VMinus,\n                 Tags::ConstraintGamma2,\n                 ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<\n                     Dim, Frame::Inertial>>,\n                 CharSpeedsTensor>;\n  using dg_package_data_temporary_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n                 Tags::ConstraintGamma1, Tags::ConstraintGamma2>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_v_psi,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> packaged_v_zero,\n      gsl::not_null<Scalar<DataVector>*> packaged_v_plus,\n      gsl::not_null<Scalar<DataVector>*> packaged_v_minus,\n      gsl::not_null<Scalar<DataVector>*> packaged_gamma2,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_interface_unit_normal,\n      gsl::not_null<tnsr::a<DataVector, 3, Frame::Inertial>*>\n          packaged_char_speeds,\n\n      const Scalar<DataVector>& psi, const Scalar<DataVector>& pi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n      const Scalar<DataVector>& constraint_gamma1,\n      const Scalar<DataVector>& constraint_gamma2,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& interface_unit_normal,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          interface_unit_normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const;\n\n  void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> psi_boundary_correction,\n      gsl::not_null<Scalar<DataVector>*> pi_boundary_correction,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          phi_boundary_correction,\n\n      const Scalar<DataVector>& v_psi_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero_int,\n      const Scalar<DataVector>& v_plus_int,\n      const Scalar<DataVector>& v_minus_int,\n      const Scalar<DataVector>& gamma2_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          interface_unit_normal_int,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds_int,\n\n      const Scalar<DataVector>& v_psi_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero_ext,\n      const Scalar<DataVector>& v_plus_ext,\n      const Scalar<DataVector>& v_minus_ext,\n      const Scalar<DataVector>& gamma2_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          interface_unit_normal_ext,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds_ext,\n      dg::Formulation /*dg_formulation*/) const;\n};\n\ntemplate <size_t Dim>\nbool operator==(const UpwindPenalty<Dim>& lhs, const UpwindPenalty<Dim>& rhs);\ntemplate <size_t Dim>\nbool operator!=(const UpwindPenalty<Dim>& lhs, const UpwindPenalty<Dim>& rhs);\n}  // namespace CurvedScalarWave::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY CurvedScalarWave)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ApplyTensorYlmFilter.cpp\n  Characteristics.cpp\n  Constraints.cpp\n  TimeDerivative.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Actions.hpp\n  ApplyTensorYlmFilter.hpp\n  BackgroundSpacetime.hpp\n  Characteristics.hpp\n  CalculateGrVars.hpp\n  Constraints.hpp\n  Initialize.hpp\n  PsiSquared.hpp\n  System.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  TimeDerivative.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  ErrorHandling\n  Events\n  Evolution\n  LinearOperators\n  IO\n  Importers\n  Serialization\n  Utilities\n  INTERFACE\n  GeneralRelativity\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(Instantiations)\nadd_subdirectory(Worldtube)\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/CalculateGrVars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BackgroundSpacetime.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace CurvedScalarWave::Actions {\n\n/// \\ingroup ActionsGroup\n/// \\brief Action that initializes or updates items related to the\n/// spacetime background of the CurvedScalarWave system\n///\n/// If `SkipForStaticBlocks` is `true`, then this action does nothing if the\n/// block of the domain is time independent. This is a performance optimization\n/// to avoid updating the background spacetime in blocks that are time\n/// independent. Note that this assumes that the background spacetime is also\n/// time independent.\n///\n/// DataBox changes:\n/// - Adds:\n///   * `CurvedScalarWave::System::spacetime_tag_list`\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename System, bool SkipForStaticDomains>\nstruct CalculateGrVars {\n  static constexpr size_t Dim = System::volume_dim;\n  using simple_tags = db::AddSimpleTags<typename System::spacetime_tag_list>;\n  using compute_tags = db::AddComputeTags<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if constexpr (SkipForStaticDomains) {\n      const auto& domain = db::get<domain::Tags::Domain<Dim>>(box);\n      const auto& block = domain.blocks()[element_id.block_id()];\n      if (not block.is_time_dependent()) {\n        return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n      }\n    }\n    auto initial_data = evolution::Initialization::initial_data(\n        db::get<CurvedScalarWave::Tags::BackgroundSpacetime<\n            typename Metavariables::background_spacetime>>(box),\n        db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box),\n        db::get<::Tags::Time>(box), typename System::spacetime_tag_list{});\n    tmpl::for_each<typename System::spacetime_tag_list>(\n        [&box, &initial_data](auto spacetime_tag_v) {\n          using spacetime_tag = tmpl::type_from<decltype(spacetime_tag_v)>;\n          db::mutate<spacetime_tag>(\n              [&initial_data](const auto spacetime_tag_ptr) {\n                *spacetime_tag_ptr =\n                    std::move(get<spacetime_tag>(initial_data));\n              },\n              make_not_null(&box));\n        });\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace CurvedScalarWave::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace CurvedScalarWave {\ntemplate <size_t SpatialDim>\nvoid characteristic_speeds(\n    const gsl::not_null<tnsr::a<DataVector, 3, Frame::Inertial>*> char_speeds,\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form) {\n  const auto shift_dot_normal = get(dot_product(shift, unit_normal_one_form));\n  get<0>(*char_speeds) = -(1. + get(gamma_1)) * shift_dot_normal;  // v(VPsi)\n  get<1>(*char_speeds) = -shift_dot_normal;                        // v(VZero)\n  get<2>(*char_speeds) = -shift_dot_normal + get(lapse);           // v(VPlus)\n  get<3>(*char_speeds) = -shift_dot_normal - get(lapse);           // v(VMinus)\n}\n\ntemplate <size_t SpatialDim>\nvoid characteristic_speeds(\n    const gsl::not_null<std::array<DataVector, 4>*> char_speeds,\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form) {\n  const size_t size = get(gamma_1).size();\n  for (auto& char_speed : *char_speeds) {\n    char_speed.destructive_resize(size);\n  }\n  const auto shift_dot_normal = get(dot_product(shift, unit_normal_one_form));\n  (*char_speeds)[0] = -(1. + get(gamma_1)) * shift_dot_normal;  // v(VPsi)\n  (*char_speeds)[1] = -shift_dot_normal;                        // v(VZero)\n  (*char_speeds)[2] = -shift_dot_normal + get(lapse);           // v(VPlus)\n  (*char_speeds)[3] = -shift_dot_normal - get(lapse);           // v(VMinus)\n}\n\ntemplate <size_t SpatialDim>\nstd::array<DataVector, 4> characteristic_speeds(\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form) {\n  std::array<DataVector, 4> char_speeds{};\n  characteristic_speeds(make_not_null(&char_speeds), gamma_1, lapse, shift,\n                        unit_normal_one_form);\n  return char_speeds;\n}\n\ntemplate <size_t SpatialDim>\nvoid characteristic_fields(\n    const gsl::not_null<Variables<tmpl::list<\n        Tags::VPsi, Tags::VZero<SpatialDim>, Tags::VPlus, Tags::VMinus>>*>\n        char_fields,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_vector) {\n  char_fields->initialize(get(gamma_2).size());\n  dot_product(make_not_null(&get<Tags::VMinus>(*char_fields)),\n              unit_normal_vector, phi);\n  // Eq.(34) of Holst+ (2004) for VZero\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    get<Tags::VZero<SpatialDim>>(*char_fields).get(i) =\n        phi.get(i) -\n        unit_normal_one_form.get(i) * get(get<Tags::VMinus>(*char_fields));\n  }\n  // Eq.(33) of Holst+ (2004) for VPsi\n  get<Tags::VPsi>(*char_fields) = psi;\n  // Eq.(35) of Holst+ (2004) for VPlus and VMinus\n  get(get<Tags::VPlus>(*char_fields)) =\n      get(pi) + get(get<Tags::VMinus>(*char_fields)) - get(gamma_2) * get(psi);\n  get(get<Tags::VMinus>(*char_fields)) =\n      get(pi) - get(get<Tags::VMinus>(*char_fields)) - get(gamma_2) * get(psi);\n}\n\ntemplate <size_t SpatialDim>\nVariables<\n    tmpl::list<Tags::VPsi, Tags::VZero<SpatialDim>, Tags::VPlus, Tags::VMinus>>\ncharacteristic_fields(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_vector) {\n  Variables<tmpl::list<Tags::VPsi, Tags::VZero<SpatialDim>, Tags::VPlus,\n                       Tags::VMinus>>\n      char_fields{get(gamma_2).size()};\n  characteristic_fields(make_not_null(&char_fields), gamma_2, psi, pi, phi,\n                        unit_normal_one_form, unit_normal_vector);\n  return char_fields;\n}\n\ntemplate <size_t SpatialDim>\nvoid characteristic_fields(\n    const gsl::not_null<Scalar<DataVector>*>& v_psi,\n    const gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*>&\n        v_zero,\n    const gsl::not_null<Scalar<DataVector>*>& v_plus,\n    const gsl::not_null<Scalar<DataVector>*>& v_minus,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_vector) {\n  dot_product(v_minus, unit_normal_vector, phi);\n  // Eq.(34) of Holst+ (2004) for VZero\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    v_zero->get(i) = phi.get(i) - unit_normal_one_form.get(i) * get(*v_minus);\n  }\n  // Eq.(33) of Holst+ (2004) for VPsi\n  *v_psi = psi;\n  // Eq.(35) of Holst+ (2004) for VPlus and VMinus\n  get(*v_plus) = get(pi) + get(*v_minus) - get(gamma_2) * get(psi);\n  get(*v_minus) = get(pi) - get(*v_minus) - get(gamma_2) * get(psi);\n}\n\ntemplate <size_t SpatialDim>\nvoid evolved_fields_from_characteristic_fields(\n    gsl::not_null<Scalar<DataVector>*> psi,\n    gsl::not_null<Scalar<DataVector>*> pi,\n    gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*> phi,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form) {\n  // Eq.(36) of Holst+ (2005) for Psi\n  *psi = v_psi;\n  // Eq.(37) - (38) of Holst+ (2004) for Pi and Phi\n  pi->get() = 0.5 * (get(v_plus) + get(v_minus)) + get(gamma_2) * get(v_psi);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    phi->get(i) =\n        0.5 * (get(v_plus) - get(v_minus)) * unit_normal_one_form.get(i) +\n        v_zero.get(i);\n  }\n}\n\ntemplate <size_t SpatialDim>\nvoid evolved_fields_from_characteristic_fields(\n    const gsl::not_null<\n        Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<SpatialDim>>>*>\n        evolved_fields,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form) {\n  evolved_fields->initialize(get_size(get(gamma_2)));\n  // Eq.(36) of Holst+ (2005) for Psi\n  get<Tags::Psi>(*evolved_fields) = v_psi;\n\n  // Eq.(37) - (38) of Holst+ (2004) for Pi and Phi\n  get<Tags::Pi>(*evolved_fields).get() =\n      0.5 * (get(v_plus) + get(v_minus)) + get(gamma_2) * get(v_psi);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    get<Tags::Phi<SpatialDim>>(*evolved_fields).get(i) =\n        0.5 * (get(v_plus) - get(v_minus)) * unit_normal_one_form.get(i) +\n        v_zero.get(i);\n  }\n}\n\ntemplate <size_t SpatialDim>\nVariables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<SpatialDim>>>\nevolved_fields_from_characteristic_fields(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form) {\n  Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<SpatialDim>>>\n      evolved_fields(get(gamma_2).size());\n  evolved_fields_from_characteristic_fields(make_not_null(&evolved_fields),\n                                            gamma_2, v_psi, v_zero, v_plus,\n                                            v_minus, unit_normal_one_form);\n  return evolved_fields;\n}\n\nnamespace Tags {\ntemplate <size_t SpatialDim>\nvoid ComputeLargestCharacteristicSpeed<SpatialDim>::function(\n    const gsl::not_null<double*> max_speed, const Scalar<DataVector>& gamma_1,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::ii<DataVector, SpatialDim, Frame::Inertial>& spatial_metric) {\n  const auto shift_magnitude = magnitude(shift, spatial_metric);\n  *max_speed =\n      std::max(max(abs(1. + get(gamma_1)) * get(shift_magnitude)),  // v(VPsi)\n               max(get(shift_magnitude) +\n                   abs(get(lapse))));  // v(VZero), v(VPlus),v(VMinus)\n}\n}  // namespace Tags\n}  // namespace CurvedScalarWave\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void CurvedScalarWave::characteristic_speeds(                      \\\n      const gsl::not_null<tnsr::a<DataVector, 3, Frame::Inertial>*>           \\\n          char_speeds,                                                        \\\n      const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,     \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift,           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form);                                              \\\n  template void CurvedScalarWave::characteristic_speeds(                      \\\n      const gsl::not_null<std::array<DataVector, 4>*> char_speeds,            \\\n      const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,     \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift,           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form);                                              \\\n  template std::array<DataVector, 4> CurvedScalarWave::characteristic_speeds( \\\n      const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,     \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift,           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form);                                              \\\n  template struct CurvedScalarWave::CharacteristicSpeedsCompute<DIM(data)>;   \\\n  template void CurvedScalarWave::characteristic_fields(                      \\\n      const gsl::not_null<Variables<tmpl::list<                               \\\n          CurvedScalarWave::Tags::VPsi,                                       \\\n          CurvedScalarWave::Tags::VZero<DIM(data)>,                           \\\n          CurvedScalarWave::Tags::VPlus, CurvedScalarWave::Tags::VMinus>>*>   \\\n          char_fields,                                                        \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,       \\\n      const Scalar<DataVector>& pi,                                           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi,             \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form,                                               \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_vector);                                                \\\n  template Variables<tmpl::list<                                              \\\n      CurvedScalarWave::Tags::VPsi, CurvedScalarWave::Tags::VZero<DIM(data)>, \\\n      CurvedScalarWave::Tags::VPlus, CurvedScalarWave::Tags::VMinus>>         \\\n  CurvedScalarWave::characteristic_fields(                                    \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,       \\\n      const Scalar<DataVector>& pi,                                           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi,             \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form,                                               \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_vector);                                                \\\n  template void CurvedScalarWave::characteristic_fields(                      \\\n      const gsl::not_null<Scalar<DataVector>*>& v_psi,                        \\\n      const gsl::not_null<tnsr::i<DataVector, DIM(data), Frame::Inertial>*>&  \\\n          v_zero,                                                             \\\n      const gsl::not_null<Scalar<DataVector>*>& v_plus,                       \\\n      const gsl::not_null<Scalar<DataVector>*>& v_minus,                      \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,       \\\n      const Scalar<DataVector>& pi,                                           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi,             \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form,                                               \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_vector);                                                \\\n  template struct CurvedScalarWave::CharacteristicFieldsCompute<DIM(data)>;   \\\n  template void CurvedScalarWave::evolved_fields_from_characteristic_fields(  \\\n      const gsl::not_null<Variables<                                          \\\n          tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi, \\\n                     CurvedScalarWave::Tags::Phi<DIM(data)>>>*>               \\\n          evolved_fields,                                                     \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,     \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& v_zero,          \\\n      const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,    \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form);                                              \\\n  template Variables<                                                         \\\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,     \\\n                 CurvedScalarWave::Tags::Phi<DIM(data)>>>                     \\\n  CurvedScalarWave::evolved_fields_from_characteristic_fields(                \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,     \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& v_zero,          \\\n      const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,    \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form);                                              \\\n  template void CurvedScalarWave::evolved_fields_from_characteristic_fields(  \\\n      gsl::not_null<Scalar<DataVector>*> psi,                                 \\\n      gsl::not_null<Scalar<DataVector>*> pi,                                  \\\n      gsl::not_null<tnsr::i<DataVector, DIM(data), Frame::Inertial>*> phi,    \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,     \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& v_zero,          \\\n      const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,    \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                  \\\n          unit_normal_one_form);                                              \\\n  template struct CurvedScalarWave::                                          \\\n      EvolvedFieldsFromCharacteristicFieldsCompute<DIM(data)>;                \\\n  template struct CurvedScalarWave::Tags::ComputeLargestCharacteristicSpeed<  \\\n      DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Constraints.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave {\n/// @{\n/*!\n * \\brief Compute the characteristic speeds for the scalar wave system in curved\n * spacetime.\n *\n * Computes the speeds as described in \"Optimal constraint projection for\n * hyperbolic evolution systems\" by Holst et. al \\cite Holst2004wt\n * [see text following Eq. (32)]. The characteristic fields' names used here\n * are similar to the paper:\n *\n * \\f{align*}\n * \\mathrm{SpECTRE} && \\mathrm{Holst} \\\\\n * v^{\\hat \\psi} && Z^1 \\\\\n * v^{\\hat 0}_{i} && Z^{2}_{i} \\\\\n * v^{\\hat \\pm} && u^{1\\pm}\n * \\f}\n *\n * The corresponding characteristic speeds \\f$\\lambda\\f$ are given in the text\n * following Eq. (38) of \\cite Holst2004wt :\n *\n * \\f{align*}\n * \\lambda_{\\hat \\psi} =& -(1 + \\gamma_1) n_k N^k \\\\\n * \\lambda_{\\hat 0} =& -n_k N^k \\\\\n * \\lambda_{\\hat \\pm} =& -n_k N^k \\pm N\n * \\f}\n *\n * where \\f$n_k\\f$ is the unit normal to the surface.\n */\ntemplate <size_t SpatialDim>\nstd::array<DataVector, 4> characteristic_speeds(\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form);\n\ntemplate <size_t SpatialDim>\nvoid characteristic_speeds(\n    gsl::not_null<std::array<DataVector, 4>*> char_speeds,\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form);\n\ntemplate <size_t SpatialDim>\nvoid characteristic_speeds(\n    gsl::not_null<tnsr::a<DataVector, 3, Frame::Inertial>*> char_speeds,\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form);\n\ntemplate <size_t SpatialDim>\nstruct CharacteristicSpeedsCompute : Tags::CharacteristicSpeeds<SpatialDim>,\n                                     db::ComputeTag {\n  using base = Tags::CharacteristicSpeeds<SpatialDim>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n      gr::Tags::Shift<DataVector, SpatialDim>,\n      ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<SpatialDim>>>;\n\n  static constexpr void function(\n      gsl::not_null<return_type*> result, const Scalar<DataVector>& gamma_1,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n          unit_normal_one_form) {\n    characteristic_speeds<SpatialDim>(result, gamma_1, lapse, shift,\n                                      unit_normal_one_form);\n  }\n};\n/// @}\n\n/// @{\n/*!\n * \\brief Computes characteristic fields from evolved fields\n *\n * \\ref CharacteristicFieldsCompute and\n * \\ref EvolvedFieldsFromCharacteristicFieldsCompute convert between\n * characteristic and evolved fields for the scalar-wave system in curved\n * spacetime.\n *\n * \\ref CharacteristicFieldsCompute computes\n * characteristic fields as described in \"Optimal constraint projection for\n * hyperbolic evolution systems\" by Holst et. al \\cite Holst2004wt .\n * Their names used here differ from this paper:\n *\n * \\f{align*}\n * \\mathrm{SpECTRE} && \\mathrm{Holst} \\\\\n * v^{\\hat \\psi} && Z^1 \\\\\n * v^{\\hat 0}_{i} && Z^{2}_{i} \\\\\n * v^{\\hat \\pm} && u^{1\\pm}\n * \\f}\n *\n * The characteristic fields \\f$u\\f$ are given in terms of the evolved fields by\n * Eq. (33) - (35) of \\cite Holst2004wt, respectively:\n *\n * \\f{align*}\n * v^{\\hat \\psi} =& \\psi \\\\\n * v^{\\hat 0}_{i} =& (\\delta^k_i - n_i n^k) \\Phi_{k} := P^k_i \\Phi_{k} \\\\\n * v^{\\hat \\pm} =& \\Pi \\pm n^i \\Phi_{i} - \\gamma_2\\psi\n * \\f}\n *\n * where \\f$\\psi\\f$ is the scalar field, \\f$\\Pi\\f$ and \\f$\\Phi_{i}\\f$ are\n * evolved fields introduced by first derivatives of \\f$\\psi\\f$, \\f$\\gamma_2\\f$\n * is a constraint damping parameter, and \\f$n_k\\f$ is the unit normal to the\n * surface.\n *\n * \\ref EvolvedFieldsFromCharacteristicFieldsCompute computes evolved fields\n * \\f$w\\f$ in terms of the characteristic fields. This uses the inverse of\n * above relations (c.f. Eq. (36) - (38) of \\cite Holst2004wt ):\n *\n * \\f{align*}\n * \\psi =& v^{\\hat \\psi}, \\\\\n * \\Pi =& \\frac{1}{2}(v^{\\hat +} + v^{\\hat -}) + \\gamma_2 v^{\\hat \\psi}, \\\\\n * \\Phi_{i} =& \\frac{1}{2}(v^{\\hat +} - v^{\\hat -}) n_i + v^{\\hat 0}_{i}.\n * \\f}\n *\n * The corresponding characteristic speeds \\f$\\lambda\\f$ are computed by\n * \\ref CharacteristicSpeedsCompute .\n */\ntemplate <size_t SpatialDim>\nVariables<\n    tmpl::list<Tags::VPsi, Tags::VZero<SpatialDim>, Tags::VPlus, Tags::VMinus>>\ncharacteristic_fields(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& unit_normal_vector);\n\ntemplate <size_t SpatialDim>\nvoid characteristic_fields(\n    gsl::not_null<Variables<tmpl::list<Tags::VPsi, Tags::VZero<SpatialDim>,\n                                       Tags::VPlus, Tags::VMinus>>*>\n        char_fields,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& unit_normal_vector);\n\ntemplate <size_t SpatialDim>\nvoid characteristic_fields(\n    const gsl::not_null<Scalar<DataVector>*>& v_psi,\n    const gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*>&\n        v_zero,\n    const gsl::not_null<Scalar<DataVector>*>& v_plus,\n    const gsl::not_null<Scalar<DataVector>*>& v_minus,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& unit_normal_vector);\n\ntemplate <size_t SpatialDim>\nstruct CharacteristicFieldsCompute : Tags::CharacteristicFields<SpatialDim>,\n                                     db::ComputeTag {\n  using base = Tags::CharacteristicFields<SpatialDim>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      Tags::ConstraintGamma2,\n      gr::Tags::InverseSpatialMetric<DataVector, SpatialDim>, Tags::Psi,\n      Tags::Pi, Tags::Phi<SpatialDim>,\n      ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<SpatialDim>>>;\n\n  static constexpr void function(\n      gsl::not_null<return_type*> result, const Scalar<DataVector>& gamma_2,\n      const tnsr::II<DataVector, SpatialDim, Frame::Inertial>&\n          inverse_spatial_metric,\n      const Scalar<DataVector>& psi, const Scalar<DataVector>& pi,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n          unit_normal_one_form) {\n    const auto unit_normal_vector = tenex::evaluate<ti::I>(\n        inverse_spatial_metric(ti::I, ti::J) * unit_normal_one_form(ti::j));\n    characteristic_fields<SpatialDim>(result, gamma_2, psi, pi, phi,\n                                      unit_normal_one_form, unit_normal_vector);\n  }\n};\n/// @}\n\n/// @{\n/*!\n * \\brief For expressions used here to compute evolved fields from\n * characteristic ones, see \\ref CharacteristicFieldsCompute.\n */\ntemplate <size_t SpatialDim>\nVariables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<SpatialDim>>>\nevolved_fields_from_characteristic_fields(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form);\n\ntemplate <size_t SpatialDim>\nvoid evolved_fields_from_characteristic_fields(\n    gsl::not_null<\n        Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<SpatialDim>>>*>\n        evolved_fields,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form);\n\ntemplate <size_t SpatialDim>\nvoid evolved_fields_from_characteristic_fields(\n    gsl::not_null<Scalar<DataVector>*> psi,\n    gsl::not_null<Scalar<DataVector>*> pi,\n    gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*> phi,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        unit_normal_one_form);\n\ntemplate <size_t SpatialDim>\nstruct EvolvedFieldsFromCharacteristicFieldsCompute\n    : Tags::EvolvedFieldsFromCharacteristicFields<SpatialDim>,\n      db::ComputeTag {\n  using base = Tags::EvolvedFieldsFromCharacteristicFields<SpatialDim>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      Tags::ConstraintGamma2, Tags::VPsi, Tags::VZero<SpatialDim>, Tags::VPlus,\n      Tags::VMinus,\n      ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<SpatialDim>>>;\n\n  static constexpr void function(\n      gsl::not_null<return_type*> result, const Scalar<DataVector>& gamma_2,\n      const Scalar<DataVector>& v_psi,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& v_zero,\n      const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n          unit_normal_one_form) {\n    evolved_fields_from_characteristic_fields<SpatialDim>(\n        result, gamma_2, v_psi, v_zero, v_plus, v_minus, unit_normal_one_form);\n  }\n};\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Computes the largest magnitude of the characteristic speeds.\n *\n * \\details Returns the magnitude of the largest characteristic speed\n * along any direction at a given point in space, considering all\n * characteristic fields. This is useful, for e.g., in computing the\n * Courant factor. The coordinate characteristic speeds for this system are\n * \\f$\\{-(1+\\gamma_1)n_k N^k, -n_k N^k, -n_k N^k \\pm N\\}\\f$. At any point\n * in space, these are maximized when the normal vector is parallel to the\n * shift vector, i.e. \\f$ n^j = N^j / \\sqrt{N^i N_i}\\f$, and \\f$ n_k N^k\n * = g_{jk} N^j N^k / \\sqrt{N^i N_i} = \\sqrt{N^i N_i} =\\f$\n * `magnitude(shift, spatial_metric)`. The maximum characteristic speed\n * is therefore calculated as \\f$ \\rm{max}(\\vert 1+\\gamma_1\\vert\\sqrt{N^i\n * N_i},\\, \\sqrt{N^i N_i}+\\vert N\\vert) \\f$.\n */\ntemplate <size_t SpatialDim>\nstruct ComputeLargestCharacteristicSpeed : LargestCharacteristicSpeed,\n                                           db::ComputeTag {\n  using argument_tags =\n      tmpl::list<Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, SpatialDim>,\n                 gr::Tags::SpatialMetric<DataVector, SpatialDim>>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(\n      const gsl::not_null<double*> max_speed, const Scalar<DataVector>& gamma_1,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n      const tnsr::ii<DataVector, SpatialDim, Frame::Inertial>& spatial_metric);\n};\n}  // namespace Tags\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Constraints.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave {\ntemplate <size_t SpatialDim>\ntnsr::i<DataVector, SpatialDim, Frame::Inertial> one_index_constraint(\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi) {\n  tnsr::i<DataVector, SpatialDim, Frame::Inertial> constraint(\n      get_size(get<0>(phi)));\n  one_index_constraint<SpatialDim>(make_not_null(&constraint), d_psi, phi);\n  return constraint;\n}\n\ntemplate <size_t SpatialDim>\nvoid one_index_constraint(\n    const gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*>\n        constraint,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi) {\n  tenex::evaluate<ti::i>(constraint, d_psi(ti::i) - phi(ti::i));\n}\n\ntemplate <size_t SpatialDim>\ntnsr::ij<DataVector, SpatialDim, Frame::Inertial> two_index_constraint(\n    const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>& d_phi) {\n  tnsr::ij<DataVector, SpatialDim, Frame::Inertial> constraint{};\n  two_index_constraint<SpatialDim>(make_not_null(&constraint), d_phi);\n  return constraint;\n}\n\ntemplate <size_t SpatialDim>\nvoid two_index_constraint(\n    const gsl::not_null<tnsr::ij<DataVector, SpatialDim, Frame::Inertial>*>\n        constraint,\n    const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>& d_phi) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      constraint->get(i, j) = d_phi.get(i, j) - d_phi.get(j, i);\n    }\n  }\n}\n}  // namespace CurvedScalarWave\n\n// Explicit Instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template tnsr::i<DataVector, DIM(data), Frame::Inertial>                    \\\n  CurvedScalarWave::one_index_constraint(                                     \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&,                 \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&);                \\\n  template void CurvedScalarWave::one_index_constraint(                       \\\n      const gsl::not_null<tnsr::i<DataVector, DIM(data), Frame::Inertial>*>,  \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&,                 \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&);                \\\n  template tnsr::ij<DataVector, DIM(data), Frame::Inertial>                   \\\n  CurvedScalarWave::two_index_constraint(                                     \\\n      const tnsr::ij<DataVector, DIM(data), Frame::Inertial>&);               \\\n  template void CurvedScalarWave::two_index_constraint(                       \\\n      const gsl::not_null<tnsr::ij<DataVector, DIM(data), Frame::Inertial>*>, \\\n      const tnsr::ij<DataVector, DIM(data), Frame::Inertial>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Constraints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Defines functions to calculate the scalar wave constraints in\n/// curved spacetime\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave {\n/// @{\n/*!\n * \\brief Computes the scalar-wave one-index constraint.\n *\n * \\details Computes the scalar-wave one-index constraint,\n * \\f$C_{i} = \\partial_i\\psi - \\Phi_{i},\\f$ which is\n * given by Eq. (19) of \\cite Holst2004wt\n */\ntemplate <size_t SpatialDim>\ntnsr::i<DataVector, SpatialDim, Frame::Inertial> one_index_constraint(\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi);\n\ntemplate <size_t SpatialDim>\nvoid one_index_constraint(\n    gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*> constraint,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the scalar-wave 2-index constraint.\n *\n * \\details Computes the scalar-wave 2-index FOSH constraint\n * [Eq. (20) of \\cite Holst2004wt],\n *\n * \\f{eqnarray}{\n * C_{ij} &\\equiv& \\partial_i \\Phi_j - \\partial_j \\Phi_i\n * \\f}\n *\n * where \\f$\\Phi_{i} = \\partial_i\\psi\\f$; and \\f$\\psi\\f$ is the scalar field.\n */\ntemplate <size_t SpatialDim>\ntnsr::ij<DataVector, SpatialDim, Frame::Inertial> two_index_constraint(\n    const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>& d_phi);\n\ntemplate <size_t SpatialDim>\nvoid two_index_constraint(\n    gsl::not_null<tnsr::ij<DataVector, SpatialDim, Frame::Inertial>*>\n        constraint,\n    const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>& d_phi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get the one-index constraint for the scalar-wave\n * evolution system.\n *\n * \\details See `one_index_constraint()`. Can be retrieved using\n * `CurvedScalarWave::Tags::OneIndexConstraint`.\n */\ntemplate <size_t SpatialDim>\nstruct OneIndexConstraintCompute : OneIndexConstraint<SpatialDim>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::Tags::deriv<Psi, tmpl::size_t<SpatialDim>, Frame::Inertial>,\n                 Phi<SpatialDim>>;\n  using return_type = tnsr::i<DataVector, SpatialDim, Frame::Inertial>;\n  static constexpr void (*function)(\n      const gsl::not_null<return_type*> result,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&) =\n      &one_index_constraint<SpatialDim>;\n  using base = OneIndexConstraint<SpatialDim>;\n};\n\n/*!\n * \\brief Compute item to get the two-index constraint for the scalar-wave\n * evolution system.\n *\n * \\details See `two_index_constraint()`. Can be retrieved using\n * `CurvedScalarWave::Tags::TwoIndexConstraint`.\n */\ntemplate <size_t SpatialDim>\nstruct TwoIndexConstraintCompute : TwoIndexConstraint<SpatialDim>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::Tags::deriv<Phi<SpatialDim>, tmpl::size_t<SpatialDim>,\n                               Frame::Inertial>>;\n  using return_type = tnsr::ij<DataVector, SpatialDim, Frame::Inertial>;\n  static constexpr void (*function)(\n      const gsl::not_null<return_type*> result,\n      const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>&) =\n      &two_index_constraint<SpatialDim>;\n  using base = TwoIndexConstraint<SpatialDim>;\n};\n}  // namespace Tags\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BackgroundSpacetime.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Evolution/Systems/ScalarWave/TagsDeclarations.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\n/// \\brief Utilities for initializing the curved scalar wave system.\nnamespace CurvedScalarWave::Initialization {\n/// \\ingroup InitializationGroup\n/// \\brief Mutator meant to be used with\n/// `Initialization::Actions::AddSimpleTags` to initialize the constraint\n/// damping parameters of the CurvedScalarWave system\n///\n/// DataBox changes:\n/// - Adds:\n///   * `CurvedScalarWave::Tags::ConstraintGamma1`\n///   * `CurvedScalarWave::Tags::ConstraintGamma2`\n/// - Removes: nothing\n/// - Modifies: nothing\n\ntemplate <size_t Dim>\nstruct InitializeConstraintDampingGammas\n    : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags =\n      tmpl::list<Tags::ConstraintGamma1, Tags::ConstraintGamma2>;\n  using argument_tags = tmpl::list<domain::Tags::Mesh<Dim>>;\n\n  static void apply(const gsl::not_null<Scalar<DataVector>*> gamma1,\n                    const gsl::not_null<Scalar<DataVector>*> gamma2,\n                    const Mesh<Dim>& mesh) {\n    const size_t number_of_grid_points = mesh.number_of_grid_points();\n    *gamma1 = Scalar<DataVector>{number_of_grid_points, 0.};\n    *gamma2 = Scalar<DataVector>{number_of_grid_points, 1.};\n  }\n};\n\n/*!\n * \\brief Analytic initial data for scalar waves in curved spacetime\n *\n * \\details When evolving a scalar field propagating through curved spacetime,\n * this mutator initializes the scalar field and spacetime variables using\n *\n * 1. analytic solution(s) or data of the flat or curved scalar wave equation\n * for the evolution variables\n * 2. solutions of the Einstein equations for the spacetime background.\n *\n * If the scalar field initial data returns `CurvedScalarWave` tags, \\f$\\Psi\\f$,\n * \\f$\\Pi\\f$ and \\f$\\Phi_i\\f$ will simply be forwarded from the initial data\n * class. Alternatively, the scalar field initial data can be provided using any\n * member class of `ScalarWave::Solutions` which return `ScalarWave` tags. In\n * this case, \\f$\\Phi_i\\f$ and \\f$\\Psi\\f$ will also be forwarded but\n * \\f$\\Pi\\f$ will be adjusted to account for the curved background. Its\n * definition comes from requiring it to be the future-directed time derivative\n * of the scalar field in curved spacetime:\n *\n * \\f{align}\n * \\Pi :=& -n^a \\partial_a \\Psi \\\\\n *     =&  \\frac{1}{\\alpha}\\left(\\beta^k \\Phi_k - {\\partial_t\\Psi}\\right),\\\\\n *     =&  \\frac{1}{\\alpha}\\left(\\beta^k \\Phi_k + {\\Pi}_{\\mathrm{flat}}\\right),\n * \\f}\n *\n * where \\f$n^a\\f$ is the unit normal to spatial slices of the spacetime\n * foliation, and \\f${\\Pi}_{\\mathrm{flat}}\\f$ comes from the flat spacetime\n * solution.\n *\n * DataBox changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies: Tags::Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n * CurvedScalarWave::Tags::Pi, CurvedScalarWave::Tags::Phi<Dim>>>\n */\ntemplate <size_t Dim, typename DerivedClasses>\nstruct InitializeEvolvedVariables : tt::ConformsTo<db::protocols::Mutator> {\n  using flat_variables_tag = typename ScalarWave::System<Dim>::variables_tag;\n  using curved_variables_tag =\n      typename CurvedScalarWave::System<Dim>::variables_tag;\n\n  using return_tags = tmpl::list<curved_variables_tag>;\n  using argument_tags =\n      tmpl::list<::Tags::Time, domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 evolution::initial_data::Tags::InitialData,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>>;\n\n  static void apply(\n      const gsl::not_null<typename curved_variables_tag::type*> evolved_vars,\n      const double initial_time,\n      const tnsr::I<DataVector, Dim>& inertial_coords,\n      const evolution::initial_data::InitialData& solution_or_data,\n      [[maybe_unused]] const Scalar<DataVector>& lapse,\n      [[maybe_unused]] const tnsr::I<DataVector, Dim>& shift) {\n    call_with_dynamic_type<void, DerivedClasses>(\n        &solution_or_data,\n        [&evolved_vars, initial_time, &inertial_coords, &lapse,\n         &shift]<typename InitialDataSubclass>(\n            const InitialDataSubclass* const data_or_solution) {\n          if constexpr (is_analytic_data_v<InitialDataSubclass> or\n                        is_analytic_solution_v<InitialDataSubclass>) {\n            if constexpr (tmpl::list_contains_v<\n                              typename InitialDataSubclass::tags,\n                              CurvedScalarWave::Tags::Psi>) {\n              (void)lapse, (void)shift;\n              evolved_vars->assign_subset(\n                  evolution::Initialization::initial_data(\n                      *data_or_solution, inertial_coords, initial_time,\n                      typename curved_variables_tag::tags_list{}));\n            } else {\n              static_assert(\n                  tmpl::list_contains_v<typename InitialDataSubclass::tags,\n                                        ScalarWave::Tags::Psi>,\n                  \"The initial data class must either calculate ScalarWave \"\n                  \"or CurvedScalarWave variables.\");\n              const auto initial_data = evolution::Initialization::initial_data(\n                  *data_or_solution, inertial_coords, initial_time,\n                  typename flat_variables_tag::tags_list{});\n\n              get<CurvedScalarWave::Tags::Psi>(*evolved_vars) =\n                  get<ScalarWave::Tags::Psi>(initial_data);\n              get<CurvedScalarWave::Tags::Phi<Dim>>(*evolved_vars) =\n                  get<ScalarWave::Tags::Phi<Dim>>(initial_data);\n              const auto shift_dot_dpsi = dot_product(\n                  shift, get<ScalarWave::Tags::Phi<Dim>>(initial_data));\n              get(get<CurvedScalarWave::Tags::Pi>(*evolved_vars)) =\n                  (get(shift_dot_dpsi) +\n                   get(get<ScalarWave::Tags::Pi>(initial_data))) /\n                  get(lapse);\n            }\n          } else {\n            ERROR(\n                \"Trying to use \"\n                \"'evolution::Initialization::Actions::SetVariables' with a \"\n                \"class that's not marked as analytic solution or analytic \"\n                \"data. To support numeric initial data, add a \"\n                \"system-specific initialization routine to your executable.\");\n          }\n        });\n  }\n};\n}  // namespace CurvedScalarWave::Initialization\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template class Events::ObserveTimeStep<CurvedScalarWave::System<DIM(data)>>; \\\n  template class dg::Events::ObserveTimeStepVolume<                            \\\n      CurvedScalarWave::System<DIM(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                \\\n  template class ChangeTimeStepperOrder<CurvedScalarWave::System<DIM(data)>>; \\\n  template class CleanHistory<CurvedScalarWave::System<DIM(data)>>;           \\\n  template class RecordTimeStepperData<CurvedScalarWave::System<DIM(data)>>;  \\\n  template class UpdateU<CurvedScalarWave::System<DIM(data)>, false>;         \\\n  template class UpdateU<CurvedScalarWave::System<DIM(data)>, true>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TimeDerivative.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                \\\n  template void evolution::dg::Actions::detail::volume_terms<                 \\\n      ::CurvedScalarWave::TimeDerivative<DIM(data)>>(                         \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::dt, typename ::CurvedScalarWave::System<DIM(                \\\n                          data)>::variables_tag::tags_list>>*>                \\\n          dt_vars_ptr,                                                        \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::Flux,                                                       \\\n          typename ::CurvedScalarWave::System<DIM(data)>::flux_variables,     \\\n          tmpl::size_t<DIM(data)>, Frame::Inertial>>*>                        \\\n          volume_fluxes,                                                      \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::deriv,                                                      \\\n          typename ::CurvedScalarWave::System<DIM(data)>::gradient_variables, \\\n          tmpl::size_t<DIM(data)>, Frame::Inertial>>*>                        \\\n          partial_derivs,                                                     \\\n      const gsl::not_null<Variables<typename ::CurvedScalarWave::System<DIM(  \\\n          data)>::compute_volume_time_derivative_terms::temporary_tags>*>     \\\n          temporaries,                                                        \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::div,                                                        \\\n          db::wrap_tags_in<                                                   \\\n              ::Tags::Flux,                                                   \\\n              typename ::CurvedScalarWave::System<DIM(data)>::flux_variables, \\\n              tmpl::size_t<DIM(data)>, Frame::Inertial>>>*>                   \\\n          div_fluxes,                                                         \\\n      const Variables<typename ::CurvedScalarWave::System<DIM(                \\\n          data)>::variables_tag::tags_list>& evolved_vars,                    \\\n      const ::dg::Formulation dg_formulation, const Mesh<DIM(data)>& mesh,    \\\n      [[maybe_unused]] const tnsr::I<DataVector, DIM(data), Frame::Inertial>& \\\n          inertial_coordinates,                                               \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,     \\\n                            Frame::Inertial>&                                 \\\n          logical_to_inertial_inverse_jacobian,                               \\\n      [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,  \\\n      const std::optional<tnsr::I<DataVector, DIM(data), Frame::Inertial>>&   \\\n          mesh_velocity,                                                      \\\n      const std::optional<Scalar<DataVector>>& div_mesh_velocity,             \\\n      const Scalar<DataVector>& pi,                                           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi,             \\\n      const Scalar<DataVector>& lapse,                                        \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift,           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& deriv_lapse,     \\\n      const tnsr::iJ<DataVector, DIM(data), Frame::Inertial>& deriv_shift,    \\\n      const tnsr::II<DataVector, DIM(data), Frame::Inertial>&                 \\\n          upper_spatial_metric,                                               \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>&                  \\\n          trace_spatial_christoffel,                                          \\\n      const Scalar<DataVector>& trace_extrinsic_curvature,                    \\\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2);    \\\n  INSTANTIATE_PARTIAL_DERIVATIVES_WITH_SYSTEM(                                \\\n      CurvedScalarWave::System<DIM(data)>, DIM(data), Frame::Inertial)\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/PsiSquared.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Tags {\n/*!\n * \\brief The square of the scalar field \\f$\\Psi\\f$.\n */\nstruct PsiSquared : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Compute tag that calculates the square of the scalar field \\f$\\Psi\\f$.\n */\nstruct PsiSquaredCompute : PsiSquared, db::ComputeTag {\n  using base = PsiSquared;\n  using return_type = Scalar<DataVector>;\n  using argument_tags = tmpl::list<CurvedScalarWave::Tags::Psi>;\n  static void function(const gsl::not_null<Scalar<DataVector>*> psi_squared,\n                       const Scalar<DataVector>& psi) {\n    get(*psi_squared) = get(psi) * get(psi);\n  }\n};\n}  // namespace CurvedScalarWave::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TimeDerivative.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup EvolutionSystemsGroup\n * \\brief Items related to evolving a scalar wave on a curved background\n */\nnamespace CurvedScalarWave {\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = false;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = Dim;\n  static constexpr bool is_euclidean = false;\n\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition<Dim>;\n\n  using variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>>;\n  using flux_variables = tmpl::list<>;\n  using gradient_variables = tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>;\n\n  // Relic alias: needs to be removed once all evolution systems\n  // convert to using dg::ComputeTimeDerivative\n  using gradients_tags = gradient_variables;\n\n  using spacetime_tag_list = tmpl::list<\n      gr::Tags::Lapse<DataVector>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<Dim>,\n                    Frame::Inertial>,\n      gr::Tags::Shift<DataVector, volume_dim>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, Dim>, tmpl::size_t<Dim>,\n                    Frame::Inertial>,\n      gr::Tags::SpatialMetric<DataVector, volume_dim>,\n      gr::Tags::InverseSpatialMetric<DataVector, volume_dim>,\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, volume_dim>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>>;\n\n  using compute_volume_time_derivative_terms = TimeDerivative<Dim>;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed<Dim>;\n\n  using inverse_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, Dim>;\n};\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines DataBox tags for the curved scalar wave system\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TagsDeclarations.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <class>\nclass Variables;\n/// \\endcond\n\n/// \\brief Option tags for the curved scalar wave system.\nnamespace CurvedScalarWave::OptionTags {}\n\nnamespace CurvedScalarWave::Tags {\n\n/*!\n * \\brief The scalar field.\n */\nstruct Psi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The conjugate momentum of the scalar field.\n *\n * \\details Its definition comes from requiring it to be the future-directed\n * time derivative of the scalar field \\f$\\Psi\\f$ in curved spacetime, see\n * \\cite Scheel2003vs , Eq. 2.16:\n *\n * \\f{align}\n * \\Pi :=& -n^a \\partial_a \\Psi \\\\\n *     =&  \\frac{1}{\\alpha}\\left(\\beta^k \\partial_k \\Psi -\n * {\\partial_t\\Psi}\\right),\\\\ \\f}\n *\n * where \\f$n^a\\f$ is the unit normal to spatial slices of the spacetime\n * foliation, \\f$\\alpha\\f$ is the lapse and \\f$\\beta^i\\f$ is the shift vector.\n */\nstruct Pi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Auxiliary variable which is analytically the spatial derivative of the\n * scalar field.\n * \\details If \\f$\\Psi\\f$ is the scalar field then we define\n * \\f$\\Phi_{i} = \\partial_i \\Psi\\f$\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct Phi : db::SimpleTag {\n  using type = tnsr::i<DataVector, SpatialDim, Frame>;\n};\n\nstruct ConstraintGamma1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct ConstraintGamma2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Tag for the one-index constraint of the scalar wave\n * system in curved spacetime.\n *\n * For details on how this is defined and computed, see\n * `OneIndexConstraintCompute`.\n */\ntemplate <size_t SpatialDim>\nstruct OneIndexConstraint : db::SimpleTag {\n  using type = tnsr::i<DataVector, SpatialDim, Frame::Inertial>;\n};\n/*!\n * \\brief Tag for the two-index constraint of the scalar wave\n * system in curved spacetime.\n *\n * For details on how this is defined and computed, see\n * `TwoIndexConstraintCompute`.\n */\ntemplate <size_t SpatialDim>\nstruct TwoIndexConstraint : db::SimpleTag {\n  using type = tnsr::ij<DataVector, SpatialDim, Frame::Inertial>;\n};\n\n/// @{\n/// \\brief Tags corresponding to the characteristic fields of the\n/// scalar-wave system in curved spacetime.\n///\n/// \\details For details on how these are defined and computed, \\see\n/// CharacteristicSpeedsCompute\nstruct VPsi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\ntemplate <size_t Dim>\nstruct VZero : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\nstruct VPlus : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct VMinus : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n/// @}\n\ntemplate <size_t Dim>\nstruct CharacteristicSpeeds : db::SimpleTag {\n  using type = std::array<DataVector, 4>;\n};\n\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <size_t Dim>\nstruct CharacteristicFields : db::SimpleTag {\n  using type = Variables<tmpl::list<VPsi, VZero<Dim>, VPlus, VMinus>>;\n};\n\ntemplate <size_t Dim>\nstruct EvolvedFieldsFromCharacteristicFields : db::SimpleTag {\n  using type = Variables<tmpl::list<Psi, Pi, Phi<Dim>>>;\n};\n}  // namespace CurvedScalarWave::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\brief Tags for the curved scalar wave system\nnamespace CurvedScalarWave::Tags {\nstruct Psi;\nstruct Pi;\ntemplate <size_t Dim, typename Frame = Frame::Inertial>\nstruct Phi;\n\nstruct ConstraintGamma1;\nstruct ConstraintGamma2;\n\nstruct VPsi;\ntemplate <size_t Dim>\nstruct VZero;\nstruct VPlus;\nstruct VMinus;\n\ntemplate <size_t Dim>\nstruct CharacteristicSpeeds;\nstruct LargestCharacteristicSpeed;\ntemplate <size_t Dim>\nstruct CharacteristicFields;\ntemplate <size_t Dim>\nstruct EvolvedFieldsFromCharacteristicFields;\n}  // namespace CurvedScalarWave::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/TimeDerivative.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave {\ntemplate <size_t Dim>\nevolution::dg::TimeDerivativeDecisions<Dim> TimeDerivative<Dim>::apply(\n    const gsl::not_null<Scalar<DataVector>*> dt_psi,\n    const gsl::not_null<Scalar<DataVector>*> dt_pi,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> dt_phi,\n\n    const gsl::not_null<Scalar<DataVector>*> result_lapse,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> result_shift,\n    const gsl::not_null<tnsr::II<DataVector, Dim>*>\n        result_inverse_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> result_gamma1,\n    const gsl::not_null<Scalar<DataVector>*> result_gamma2,\n\n    const tnsr::i<DataVector, Dim>& d_psi, const tnsr::i<DataVector, Dim>& d_pi,\n    const tnsr::ij<DataVector, Dim>& d_phi, const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim>& phi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim>& shift,\n    const tnsr::i<DataVector, Dim>& deriv_lapse,\n    const tnsr::iJ<DataVector, Dim>& deriv_shift,\n    const tnsr::II<DataVector, Dim>& upper_spatial_metric,\n    const tnsr::I<DataVector, Dim>& trace_spatial_christoffel,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2) {\n  const auto& pi_times_lapse = result_lapse;\n  tenex::evaluate(pi_times_lapse, pi() * lapse());\n  const auto& d_psi_dot_shift = result_gamma1;\n  get(*d_psi_dot_shift) = get<0>(d_psi) * get<0>(shift);\n  for (size_t i = 1; i < Dim; ++i) {\n    get(*d_psi_dot_shift) += d_psi.get(i) * shift.get(i);\n  }\n  get(*dt_psi) = -get(*result_lapse) + get(*d_psi_dot_shift);\n  tenex::evaluate(\n      dt_pi,\n      (*result_lapse)() * trace_extrinsic_curvature() +\n          shift(ti::I) * d_pi(ti::i) +\n          lapse() * trace_spatial_christoffel(ti::I) * phi(ti::i) -\n          lapse() * upper_spatial_metric(ti::I, ti::J) * d_phi(ti::i, ti::j) -\n          upper_spatial_metric(ti::I, ti::J) * phi(ti::i) * deriv_lapse(ti::j));\n\n  const auto& lapse_times_gamma2 = result_gamma2;\n  get(*lapse_times_gamma2) = get(lapse) * get(gamma2);\n  tenex::evaluate<ti::i>(\n      dt_phi, -lapse() * d_pi(ti::i) + shift(ti::J) * d_phi(ti::j, ti::i) +\n                  (*lapse_times_gamma2)() * (d_psi(ti::i) - phi(ti::i)) -\n                  pi() * deriv_lapse(ti::i) +\n                  phi(ti::j) * deriv_shift(ti::i, ti::J));\n\n  // gamma1 is usually set to zero\n  if (get(gamma1) != 0.) {\n    for (size_t i = 0; i < Dim; ++i) {\n      get(*d_psi_dot_shift) -= phi.get(i) * shift.get(i);\n    }\n    get(*d_psi_dot_shift) *= get(gamma1);\n    get(*dt_psi) += get(*d_psi_dot_shift);\n    get(*dt_pi) += get(gamma2) * get(*d_psi_dot_shift);\n  }\n  *result_lapse = lapse;\n  *result_shift = shift;\n  *result_inverse_spatial_metric = upper_spatial_metric;\n  *result_gamma1 = gamma1;\n  *result_gamma2 = gamma2;\n  return {true};\n}\n}  // namespace CurvedScalarWave\n// Generate explicit instantiations of partial_derivatives function as well as\n// all other functions in Equations.cpp\n\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n\ntemplate <size_t Dim>\nusing derivative_tags = typename CurvedScalarWave::System<Dim>::gradients_tags;\n\ntemplate <size_t Dim>\nusing variables_tags =\n    typename CurvedScalarWave::System<Dim>::variables_tag::tags_list;\n\nusing derivative_frame = Frame::Inertial;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                                               \\\n  template class CurvedScalarWave::TimeDerivative<DIM(data)>;                \\\n  template Variables<                                                        \\\n      db::wrap_tags_in<::Tags::deriv, derivative_tags<DIM(data)>,            \\\n                       tmpl::size_t<DIM(data)>, derivative_frame>>           \\\n  partial_derivatives<derivative_tags<DIM(data)>, variables_tags<DIM(data)>, \\\n                      DIM(data), derivative_frame>(                          \\\n      const Variables<variables_tags<DIM(data)>>& u,                         \\\n      const Mesh<DIM(data)>& mesh,                                           \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,    \\\n                            derivative_frame>& inverse_jacobian);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\nnamespace CurvedScalarWave {\n/*!\n * \\brief Compute the time derivative of the evolved variables of the\n * first-order scalar wave system on a curved background.\n *\n * The evolution equations for the first-order scalar wave system are given by\n * \\cite Holst2004wt :\n *\n * \\f{align}\n * \\partial_t\\Psi = & - \\alpha \\Pi + \\beta^k \\partial_k \\Psi\n * + \\gamma_1 \\beta^k (\\partial_k \\Psi - \\Phi_k) \\\\\n *\n * \\partial_t\\Pi = & \\alpha K \\Pi + \\beta^i \\partial_i \\Pi\n * + \\alpha \\Gamma^i \\Phi_i\n * + \\gamma_1 \\gamma_2 \\beta^i ( \\partial_i \\Psi - \\Phi_i )\n * - \\alpha \\gamma^{ij} \\partial_i\\Phi_j\n * - \\gamma^{ij} \\Phi_i \\partial_j \\alpha \\\\\n *\n * \\partial_t\\Phi_i = & - \\alpha \\partial_i \\Pi  + \\beta^k \\partial_k \\Phi_i\n * + \\gamma_2 \\alpha ( \\partial_i \\Psi - \\Phi_i )\n * - \\Pi \\partial_i \\alpha + \\Phi_j \\partial_i \\beta^j \\\\\n * \\f}\n *\n * where \\f$\\Psi\\f$ is the scalar field, \\f$\\Pi\\f$ is the\n * conjugate momentum to \\f$\\Psi\\f$, \\f$\\Phi_i=\\partial_i\\Psi\\f$ is an\n * auxiliary variable, \\f$\\alpha\\f$ is the lapse, \\f$\\beta^k\\f$ is the shift,\n * \\f$ \\gamma_{ij} \\f$ is the spatial metric, \\f$ K \\f$ is the trace of the\n * extrinsic curvature, and \\f$ \\Gamma^i \\f$ is the trace of the spatial\n * Christoffel symbol of the second kind. \\f$\\gamma_1, \\gamma_2\\f$ are\n * constraint damping parameters.\n */\ntemplate <size_t Dim>\nstruct TimeDerivative {\n public:\n  using temporary_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n                 Tags::ConstraintGamma1, Tags::ConstraintGamma2>;\n\n  using argument_tags =\n      tmpl::list<Tags::Pi, Tags::Phi<Dim>, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<Dim>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, Dim>,\n                               tmpl::size_t<Dim>, Frame::Inertial>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n                 gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, Dim>,\n                 gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                 Tags::ConstraintGamma1, Tags::ConstraintGamma2>;\n\n  static evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      gsl::not_null<Scalar<DataVector>*> dt_psi,\n      gsl::not_null<Scalar<DataVector>*> dt_pi,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> dt_phi,\n\n      gsl::not_null<Scalar<DataVector>*> result_lapse,\n      gsl::not_null<tnsr::I<DataVector, Dim>*> result_shift,\n      gsl::not_null<tnsr::II<DataVector, Dim>*> result_inverse_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> result_gamma1,\n      gsl::not_null<Scalar<DataVector>*> result_gamma2,\n\n      const tnsr::i<DataVector, Dim>& d_psi,\n      const tnsr::i<DataVector, Dim>& d_pi,\n      const tnsr::ij<DataVector, Dim>& d_phi, const Scalar<DataVector>& pi,\n      const tnsr::i<DataVector, Dim>& phi, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim>& shift,\n      const tnsr::i<DataVector, Dim>& deriv_lapse,\n      const tnsr::iJ<DataVector, Dim>& deriv_shift,\n      const tnsr::II<DataVector, Dim>& upper_spatial_metric,\n      const tnsr::I<DataVector, Dim>& trace_spatial_christoffel,\n      const Scalar<DataVector>& trace_extrinsic_curvature,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2);\n};\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/AccelerationTermsOrder0.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicBuffer.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\nvoid acceleration_terms_0(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, const double ft,\n    const double fx, const double fy, const double dt_ft, const double dt_fx,\n    const double dt_fy, const double bh_mass) {\n  const size_t grid_size = get<0>(centered_coords).size();\n  result->initialize(grid_size);\n  const double xp = particle_position[0];\n  const double yp = particle_position[1];\n  const double xpdot = particle_velocity[0];\n  const double ypdot = particle_velocity[1];\n  const double xpddot = particle_acceleration[0];\n  const double ypddot = particle_acceleration[1];\n  const double rp = get(magnitude(particle_position));\n  const double rpdot = (xp * xpdot + yp * ypdot) / rp;\n\n  const auto& Dx = get<0>(centered_coords);\n  const auto& Dy = get<1>(centered_coords);\n  const auto& z = get<2>(centered_coords);\n\n  const double M = bh_mass;\n\n  DynamicBuffer<DataVector> temps(56, grid_size);\n\n  const double d_0 = rp * rp * rp;\n  const double d_1 = 1.0 / d_0;\n  const double d_2 = 2. * M;\n  const double d_3 = d_1 * d_2;\n  const double d_4 = xp * xpdot;\n  const double d_5 = yp * ypdot;\n  const double d_6 = d_4 + d_5;\n  const double d_7 = rp * rp;\n  const double d_8 = M * rp;\n  const double d_9 = 4. * d_8;\n  const double d_10 = d_6 * d_6;\n  const double d_11 = xpdot * xpdot;\n  const double d_12 = ypdot * ypdot;\n  const double d_13 = d_11 + d_12;\n  const double d_14 = d_0 * (d_13 - 1.) + d_10 * d_2 + d_2 * d_7 + d_6 * d_9;\n  const double d_15 = 1.0 / d_14;\n  const double d_16 = d_1 * d_15;\n  const double d_17 = d_2 * yp;\n  const double d_18 = d_0 + d_2 * (yp * yp);\n  const double d_19 = d_0 * d_14;\n  const double d_20 = rp * rp * rp * rp;\n  const double d_21 = d_20 * ft;\n  const double d_22 = rp * rp * rp * rp * rp * rp;\n  const double d_23 = 0.5 * d_15 / d_22;\n  const double d_24 = -d_14;\n  const double d_25 = sqrt(d_24);\n  const double d_26 = 1.0 / d_20;\n  const double d_27 = 3. * rpdot;\n  const double d_28 = M * d_27;\n  const double d_29 = xp * xpddot;\n  const double d_30 = yp * ypddot;\n  const double d_31 = 2. * rpdot;\n  const double d_32 = d_13 + d_29 + d_30;\n  const double d_33 =\n      -2. * M * (-d_11 - d_12 - d_29 - d_30 + 0.5 * rpdot) / d_7 -\n      d_10 * d_26 * d_28 + d_3 * d_6 * (-d_31 + d_32) + xpddot * xpdot +\n      ypddot * ypdot;\n  const double d_34 = d_0 * xpdot;\n  const double d_35 = d_2 * xp;\n  const double d_36 = d_35 * rp;\n  const double d_37 = d_34 + d_35 * d_6 + d_36;\n  const double d_38 = d_0 * ypdot;\n  const double d_39 = d_17 * rp;\n  const double d_40 = d_17 * d_6 + d_38 + d_39;\n  const double d_41 = d_27 * d_7;\n  const double d_42 = 1. / sqrt(rp);\n  const double d_43 = -d_5;\n  const double d_44 = xp * xp;\n  const double d_45 = d_35 * yp;\n  const double d_46 = d_14 * d_21;\n  const double d_47 = d_0 + d_2 * d_44;\n  DataVector& dv_0 = temps.at(0);\n  dv_0 = Dx * xp;\n  DataVector& dv_1 = temps.at(1);\n  dv_1 = Dy * yp;\n  DataVector& dv_2 = temps.at(2);\n  dv_2 = dv_0 + dv_1;\n  DataVector& dv_3 = temps.at(3);\n  dv_3 = dv_2 * dv_2;\n  DataVector& dv_4 = temps.at(4);\n  dv_4 = Dx * xpdot;\n  DataVector& dv_5 = temps.at(5);\n  dv_5 = Dy * ypdot;\n  DataVector& dv_6 = temps.at(6);\n  dv_6 = dv_4 + dv_5;\n  DataVector& dv_7 = temps.at(7);\n  dv_7 = d_2 * dv_2;\n  DataVector& dv_8 = temps.at(8);\n  dv_8 = dv_7 * rp;\n  DataVector& dv_9 = temps.at(9);\n  dv_9 = d_0 * dv_6 + d_6 * dv_7 + dv_8;\n  DataVector& dv_10 = temps.at(10);\n  dv_10 = dv_9 * dv_9;\n  DataVector& dv_11 = temps.at(11);\n  dv_11 = -d_16 * dv_10 + d_3 * dv_3 + Dx * Dx + Dy * Dy + z * z;\n  DataVector& dv_12 = temps.at(12);\n  dv_12 = dv_11 * sqrt(dv_11);\n  DataVector& dv_13 = temps.at(13);\n  dv_13 = dv_7 * xp;\n  DataVector& dv_14 = temps.at(14);\n  dv_14 = Dx * d_0 + dv_13;\n  DataVector& dv_15 = temps.at(15);\n  dv_15 = dv_10 * fx;\n  DataVector& dv_16 = temps.at(16);\n  dv_16 = dv_14 * dv_15;\n  DataVector& dv_17 = temps.at(0);\n  dv_17 = Dy * d_18 + d_17 * dv_0;\n  DataVector& dv_18 = temps.at(17);\n  dv_18 = dv_10 * fy;\n  DataVector& dv_19 = temps.at(18);\n  dv_19 = dv_17 * dv_18;\n  DataVector& dv_20 = temps.at(19);\n  dv_20 = dv_10 * ft;\n  DataVector& dv_21 = temps.at(20);\n  dv_21 = dv_14 * fx;\n  DataVector& dv_22 = temps.at(21);\n  dv_22 = d_19 * dv_11;\n  DataVector& dv_23 = temps.at(22);\n  dv_23 = dv_17 * fy;\n  DataVector& dv_24 = temps.at(23);\n  dv_24 = d_14 * dv_11;\n  DataVector& dv_25 = temps.at(24);\n  dv_25 = d_21 * dv_24;\n  DataVector& dv_26 = temps.at(25);\n  dv_26 = dv_21 * dv_22 + dv_22 * dv_23 + dv_25 * dv_7;\n  DataVector& dv_27 = temps.at(26);\n  dv_27 = dv_16 + dv_19 + dv_20 * dv_8 + dv_26;\n  DataVector& dv_28 = temps.at(27);\n  dv_28 = 1.0 / (dv_11 * dv_11);\n  DataVector& dv_29 = temps.at(28);\n  dv_29 = sqrt(dv_11);\n  DataVector& dv_30 = temps.at(29);\n  dv_30 = d_16 * dv_9;\n  DataVector& dv_31 = temps.at(13);\n  dv_31 = Dx + d_1 * dv_13 - d_37 * dv_30;\n  DataVector& dv_32 = temps.at(29);\n  dv_32 = Dy + d_1 * dv_7 * yp - d_40 * dv_30;\n  DataVector& dv_33 = temps.at(30);\n  dv_33 = Dx * xpddot + Dy * ypddot;\n  DataVector& dv_34 = temps.at(31);\n  dv_34 = d_2 * dv_6;\n  DataVector& dv_35 = temps.at(32);\n  dv_35 = M * dv_2;\n  DataVector& dv_36 = temps.at(33);\n  dv_36 = d_31 * dv_35;\n  DataVector& dv_37 = temps.at(34);\n  dv_37 = d_32 * dv_7 + d_41 * dv_6 + dv_36;\n  DataVector& dv_38 = temps.at(31);\n  dv_38 = -d_26 * (2. * M * dv_2 * dv_6 * rp + 3. * d_15 * dv_10 * rpdot -\n                   d_15 * dv_9 * rp *\n                       (d_0 * dv_33 + d_6 * dv_34 + dv_34 * rp + dv_37) +\n                   d_20 * d_33 * dv_10 / (d_14 * d_14) - d_28 * dv_3) +\n          dv_31 * xpdot + dv_32 * ypdot;\n  DataVector& dv_39 = temps.at(3);\n  dv_39 = 1.0 / dv_29;\n  DataVector& dv_40 = temps.at(35);\n  dv_40 = dv_14 * dt_fx;\n  DataVector& dv_41 = temps.at(6);\n  dv_41 = d_2 * (-d_4 + d_43 + dv_6);\n  DataVector& dv_42 = temps.at(36);\n  dv_42 = dv_41 * rp;\n  DataVector& dv_43 = temps.at(37);\n  dv_43 = dv_17 * dt_fy;\n  DataVector& dv_44 = temps.at(1);\n  dv_44 =\n      Dx * d_41 +\n      d_2 * (-d_44 * xpdot + dv_1 * xpdot + xp * (d_43 + 2. * dv_4 + dv_5)) -\n      d_34;\n  DataVector& dv_45 = temps.at(5);\n  dv_45 = Dy * d_41 + d_17 * (d_43 + dv_4 + 2. * dv_5) +\n          d_35 * (Dx * ypdot - xpdot * yp) - d_38;\n  DataVector& dv_46 = temps.at(34);\n  dv_46 = dv_9 * (d_0 * (-d_11 - d_12 + dv_33) + d_6 * dv_41 + dv_37 + dv_42);\n  DataVector& dv_47 = temps.at(30);\n  dv_47 = d_9 * dv_2 * ft;\n  DataVector& dv_48 = temps.at(4);\n  dv_48 = 2 * dv_21;\n  DataVector& dv_49 = temps.at(38);\n  dv_49 = 2 * dv_23;\n  DataVector& dv_50 = temps.at(39);\n  dv_50 = 6 * d_7 * dv_24 * rpdot;\n  DataVector& dv_51 = temps.at(40);\n  dv_51 = d_33 * dv_11;\n  DataVector& dv_52 = temps.at(41);\n  dv_52 = d_22 * dv_51;\n  DataVector& dv_53 = temps.at(42);\n  dv_53 = dv_20 * dv_29;\n  DataVector& dv_54 = temps.at(43);\n  dv_54 = dv_18 * dv_29;\n  DataVector& dv_55 = temps.at(44);\n  dv_55 = d_46 * dv_12;\n  DataVector& dv_56 = temps.at(45);\n  dv_56 = d_19 * dv_12;\n  DataVector& dv_57 = temps.at(46);\n  dv_57 = dv_56 * fy;\n  DataVector& dv_58 = temps.at(47);\n  dv_58 = dv_15 * dv_29;\n  DataVector& dv_59 = temps.at(45);\n  dv_59 = dv_56 * fx;\n  DataVector& dv_60 = temps.at(9);\n  dv_60 = dv_29 * dv_9;\n  DataVector& dv_61 = temps.at(48);\n  dv_61 = d_37 * dv_60;\n  DataVector& dv_62 = temps.at(49);\n  dv_62 = dv_31 * dv_39;\n  DataVector& dv_63 = temps.at(50);\n  dv_63 = 6. * d_8 * dv_2 * dv_20;\n  DataVector& dv_64 = temps.at(51);\n  dv_64 = d_46 * dv_29 * dv_7;\n  DataVector& dv_65 = temps.at(16);\n  dv_65 = 3. * dv_16;\n  DataVector& dv_66 = temps.at(18);\n  dv_66 = 3. * dv_19 + dv_63 + dv_65;\n  DataVector& dv_67 = temps.at(52);\n  dv_67 = d_19 * dv_29;\n  DataVector& dv_68 = temps.at(53);\n  dv_68 = dv_31 * dv_67;\n  DataVector& dv_69 = temps.at(54);\n  dv_69 = d_23 * dv_28;\n  DataVector& dv_70 = temps.at(9);\n  dv_70 = d_40 * dv_60;\n  DataVector& dv_71 = temps.at(55);\n  dv_71 = dv_32 * dv_39;\n  DataVector& dv_72 = temps.at(52);\n  dv_72 = dv_32 * dv_67;\n\n  get(get<CurvedScalarWave::Tags::Psi>(*result)) = -d_23 * dv_27 / dv_12;\n  get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(*result)) =\n      -0.5 * dv_28 *\n      (-3. * d_25 * d_42 * dv_27 * dv_38 * dv_39 +\n       d_25 * d_42 * dv_29 *\n           (2. * d_14 * dv_38 *\n                (2. * M * d_20 * dv_2 * ft + d_0 * dv_14 * fx +\n                 d_0 * dv_17 * fy) -\n            d_20 * dv_24 * dv_7 * dt_ft - dv_10 * dv_40 - dv_10 * dv_43 -\n            dv_10 * dv_8 * dt_ft - dv_15 * dv_44 - dv_18 * dv_45 -\n            dv_20 * dv_36 - dv_20 * dv_42 - dv_21 * dv_50 -\n            14 * dv_22 * dv_35 * ft * rpdot - dv_22 * dv_40 - dv_22 * dv_43 -\n            dv_22 * dv_44 * fx - dv_22 * dv_45 * fy - dv_23 * dv_50 -\n            dv_25 * dv_41 - 4 * dv_35 * dv_51 * ft * rp * square(cube(rp)) -\n            dv_46 * dv_47 - dv_46 * dv_48 - dv_46 * dv_49 - dv_48 * dv_52 -\n            dv_49 * dv_52) +\n       9. * d_25 * dv_27 * dv_29 * rpdot / (rp * sqrt(rp)) -\n       2. * d_33 * dv_27 * dv_29 * rp * rp * sqrt(rp) / d_25) /\n      (d_24 * sqrt(d_24) * rp * square(square(rp)) * sqrt(rp));\n  get<0>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -dv_69 * (d_35 * dv_55 + d_36 * dv_53 + d_45 * dv_54 + d_45 * dv_57 +\n                d_47 * dv_58 + d_47 * dv_59 - dv_21 * dv_68 - dv_23 * dv_68 -\n                dv_31 * dv_64 + dv_47 * dv_61 + dv_48 * dv_61 + dv_49 * dv_61 -\n                dv_62 * dv_66);\n  get<1>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -dv_69 * (d_17 * dv_55 + d_18 * dv_54 + d_18 * dv_57 + d_39 * dv_53 +\n                d_45 * dv_58 + d_45 * dv_59 - dv_21 * dv_72 - dv_23 * dv_72 -\n                dv_32 * dv_64 + dv_47 * dv_70 + dv_48 * dv_70 + dv_49 * dv_70 -\n                dv_66 * dv_71);\n  get<2>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      d_23 * z * (dv_26 + dv_66) / (dv_11 * dv_11 * sqrt(dv_11));\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/AccelerationTermsOrder1.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicBuffer.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\n// NOLINTNEXTLINE(google-readability-function-size, readability-function-size)\nvoid acceleration_terms_1(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, const double ft,\n    const double fx, const double fy, const double dt_ft, const double dt_fx,\n    const double dt_fy, const double Du_ft, const double Du_fx,\n    const double Du_fy, const double dt_Du_ft, const double dt_Du_fx,\n    const double dt_Du_fy, const double bh_mass) {\n  const size_t grid_size = get<0>(centered_coords).size();\n  result->initialize(grid_size);\n  const double xp = particle_position[0];\n  const double yp = particle_position[1];\n  const double xpdot = particle_velocity[0];\n  const double ypdot = particle_velocity[1];\n  const double xpddot = particle_acceleration[0];\n  const double ypddot = particle_acceleration[1];\n  const double rp = get(magnitude(particle_position));\n  const double rpdot = (xp * xpdot + yp * ypdot) / rp;\n\n  const auto& Dx = get<0>(centered_coords);\n  const auto& Dy = get<1>(centered_coords);\n  const auto& z = get<2>(centered_coords);\n\n  const double M = bh_mass;\n\n  DynamicBuffer<DataVector> temps(307, grid_size);\n\n  const double d_0 = rp * rp * rp;\n  const double d_1 = 2 * M;\n  const double d_2 = xp * xpdot;\n  const double d_3 = yp * ypdot;\n  const double d_4 = d_2 + d_3;\n  const double d_5 = 1.0 / d_0;\n  const double d_6 = rp * rp;\n  const double d_7 = 4 * rp;\n  const double d_8 = d_4 * d_4;\n  const double d_9 = xpdot * xpdot;\n  const double d_10 = ypdot * ypdot;\n  const double d_11 = d_10 + d_9;\n  const double d_12 = M * d_4 * d_7 + d_0 * (d_11 - 1) + d_1 * d_6 + d_1 * d_8;\n  const double d_13 = 1.0 / d_12;\n  const double d_14 = d_13 * d_5;\n  const double d_15 = ft * rp;\n  const double d_16 = d_1 * yp;\n  const double d_17 = yp * yp;\n  const double d_18 = d_1 * d_17;\n  const double d_19 = d_0 + d_18;\n  const double d_20 = rp * rp * rp * rp;\n  const double d_21 = M * ft;\n  const double d_22 = 12 * d_21;\n  const double d_23 = d_0 * d_12;\n  const double d_24 = rp * rp * rp * rp * rp * rp * rp * rp * rp * rp;\n  const double d_25 = fx * xp;\n  const double d_26 = fy * yp;\n  const double d_27 = -rp;\n  const double d_28 = d_1 + d_27;\n  const double d_29 = d_1 * (d_25 + d_26) + d_15 * d_28;\n  const double d_30 = d_12 * d_12;\n  const double d_31 = rp * rp * rp * rp * rp * rp * rp * rp * rp;\n  const double d_32 = d_15 + d_26;\n  const double d_33 = d_1 * xp;\n  const double d_34 = xp * xp;\n  const double d_35 = d_1 * d_34;\n  const double d_36 = d_0 + d_35;\n  const double d_37 = d_36 * fx;\n  const double d_38 = d_32 * d_33 + d_37;\n  const double d_39 = d_15 + d_25;\n  const double d_40 = d_19 * fy;\n  const double d_41 = d_16 * d_39 + d_40;\n  const double d_42 = 2 * rp;\n  const double d_43 = 2 * d_23;\n  const double d_44 = -d_12;\n  const double d_45 = d_44 * sqrt(d_44);\n  const double d_46 = d_0 * d_20 * sqrt(rp);\n  const double d_47 = d_45 * d_46;\n  const double d_48 = 12 * ft;\n  const double d_49 = d_29 * d_48;\n  const double d_50 = rp * rp * rp * rp * rp * rp * rp;\n  const double d_51 = d_12 * d_50;\n  const double d_52 = 12 * d_38;\n  const double d_53 = rp * rp * rp * rp * rp * rp;\n  const double d_54 = d_12 * d_53;\n  const double d_55 = 12 * d_41;\n  const double d_56 = d_46 * rp;\n  const double d_57 = Du_ft * d_56;\n  const double d_58 = d_45 * d_57;\n  const double d_59 = sqrt(d_44);\n  const double d_60 = d_20 * sqrt(rp);\n  const double d_61 = 3 * M;\n  const double d_62 = d_61 * fx;\n  const double d_63 = d_12 * d_20;\n  const double d_64 = rp * rp * rp * rp * rp;\n  const double d_65 = d_12 * d_64;\n  const double d_66 = d_60 * rp;\n  const double d_67 = d_13 * 1.0 / d_53;\n  const double d_68 = d_46 * d_20;\n  const double d_69 = d_45 * rpdot;\n  const double d_70 = 1.0 / d_20;\n  const double d_71 = 3 * rpdot;\n  const double d_72 = M * d_71;\n  const double d_73 = xp * xpddot;\n  const double d_74 = yp * ypddot;\n  const double d_75 = 2 * rpdot;\n  const double d_76 = d_11 + d_73 + d_74;\n  const double d_77 =\n      -M * (-2 * d_10 - 2 * d_73 - 2 * d_74 - 2 * d_9 + rpdot) / d_6 +\n      d_1 * d_4 * d_5 * (-d_75 + d_76) - d_70 * d_72 * d_8 + xpddot * xpdot +\n      ypddot * ypdot;\n  const double d_78 = 12 * d_77;\n  const double d_79 = d_0 * xpdot;\n  const double d_80 = d_33 * d_4 + d_33 * rp + d_79;\n  const double d_81 = d_0 * ypdot;\n  const double d_82 = d_16 * d_4 + d_16 * rp + d_81;\n  const double d_83 = 1.0 / d_30;\n  const double d_84 = -d_77;\n  const double d_85 = d_20 * d_84;\n  const double d_86 = d_45 * d_66;\n  const double d_87 = ft * rpdot;\n  const double d_88 = dt_ft * rp;\n  const double d_89 = -d_3;\n  const double d_90 = d_6 * rpdot;\n  const double d_91 = M * d_87;\n  const double d_92 = 4 * d_50;\n  const double d_93 = d_77 * d_92;\n  const double d_94 = 18 * rpdot;\n  const double d_95 = 1.0 / d_59;\n  const double d_96 = d_77 * d_95;\n  const double d_97 = 1. / sqrt(rp);\n  const double d_98 = rp * rp * rp * rp * rp * rp * rp * rp;\n  const double d_99 = fy * ypdot + dt_fy * yp;\n  const double d_100 = fx * xpdot + dt_fx * xp;\n  const double d_101 =\n      d_1 * (d_100 + d_99) + d_28 * d_88 + d_75 * ft * (M + d_27);\n  const double d_102 = 4 * M;\n  const double d_103 = d_6 * d_71;\n  const double d_104 = d_87 + d_88;\n  const double d_105 = d_1 * d_32 * xpdot + d_33 * (d_104 + d_99) +\n                       d_36 * dt_fx + fx * (d_102 * d_2 + d_103);\n  const double d_106 = d_1 * d_39 * ypdot + d_16 * (d_100 + d_104) +\n                       d_19 * dt_fy + fy * (d_102 * d_3 + d_103);\n  const double d_107 = 12 * d_58;\n  const double d_108 = d_0 * sqrt(rp);\n  const double d_109 = 24 * d_77;\n  const double d_110 = d_29 * ft;\n  const double d_111 = d_53 * d_53;\n  const double d_112 = d_46 * d_0;\n  const double d_113 = d_110 * d_51;\n  const double d_114 = d_38 * fx;\n  const double d_115 = d_41 * fy;\n  const double d_116 = M * d_94;\n  const double d_117 = 6 * M;\n  const double d_118 = d_50 * d_77;\n  const double d_119 = d_24 * d_30;\n  const double d_120 = d_30 * d_31;\n  const double d_121 = 14 * d_23 * rpdot;\n  const double d_122 = 4 * rpdot;\n  const double d_123 = 2 * yp;\n  const double d_124 = d_20 * xpdot;\n  const double d_125 = 7 * rpdot;\n  const double d_126 = d_20 * ypdot;\n  const double d_127 = M * xp;\n  const double d_128 = d_127 * d_48;\n  const double d_129 = 12 * d_120;\n  const double d_130 = Du_fy * d_127;\n  const double d_131 = Du_ft * d_66;\n  const double d_132 = 4 * yp;\n  const double d_133 = Du_fx * d_36;\n  const double d_134 = d_52 * fx;\n  const double d_135 = d_55 * fy;\n  const double d_136 = d_21 * d_65;\n  const double d_137 = d_21 * d_6;\n  const double d_138 = d_61 * fy;\n  const double d_139 = 9 * M;\n  const double d_140 = d_63 * fx;\n  const double d_141 = d_63 * fy;\n  const double d_142 = 4 * xp;\n  const double d_143 = 4 * d_23;\n  const double d_144 = d_33 * yp;\n  const double d_145 = d_83 / d_111;\n  const double d_146 = M * yp;\n  const double d_147 = d_146 * d_48;\n  const double d_148 = Du_fx * d_127;\n  const double d_149 = Du_fy * d_19;\n  const double d_150 = 4 * ft;\n  const double d_151 = 4 * d_38;\n  const double d_152 = 4 * d_41;\n  const double d_153 = d_17 + d_34;\n  const double d_154 = d_4 + rp;\n  DataVector& dv_0 = temps.at(0);\n  dv_0 = Dx * xpdot;\n  DataVector& dv_1 = temps.at(1);\n  dv_1 = Dy * ypdot;\n  DataVector& dv_2 = temps.at(2);\n  dv_2 = dv_0 + dv_1;\n  DataVector& dv_3 = temps.at(3);\n  dv_3 = Dx * xp;\n  DataVector& dv_4 = temps.at(4);\n  dv_4 = Dy * yp;\n  DataVector& dv_5 = temps.at(5);\n  dv_5 = dv_3 + dv_4;\n  DataVector& dv_6 = temps.at(6);\n  dv_6 = d_1 * dv_5;\n  DataVector& dv_7 = temps.at(7);\n  dv_7 = dv_6 * rp;\n  DataVector& dv_8 = temps.at(8);\n  dv_8 = d_0 * dv_2 + d_4 * dv_6 + dv_7;\n  DataVector& dv_9 = temps.at(9);\n  dv_9 = dv_8 * dv_8;\n  DataVector& dv_10 = temps.at(10);\n  dv_10 = dv_5 * dv_5;\n  DataVector& dv_11 = temps.at(11);\n  dv_11 = 2 * dv_10;\n  DataVector& dv_12 = temps.at(12);\n  dv_12 = Dx * Dx;\n  DataVector& dv_13 = temps.at(13);\n  dv_13 = Dy * Dy;\n  DataVector& dv_14 = temps.at(14);\n  dv_14 = z * z;\n  DataVector& dv_15 = temps.at(15);\n  dv_15 = dv_13 + dv_14;\n  DataVector& dv_16 = temps.at(16);\n  dv_16 = dv_12 + dv_15;\n  DataVector& dv_17 = temps.at(17);\n  dv_17 = M * d_5 * dv_11 - d_14 * dv_9 + dv_16;\n  DataVector& dv_18 = temps.at(18);\n  dv_18 = dv_17 * sqrt(dv_17);\n  DataVector& dv_19 = temps.at(19);\n  dv_19 = d_15 * dv_9;\n  DataVector& dv_20 = temps.at(20);\n  dv_20 = M * dv_5;\n  DataVector& dv_21 = temps.at(21);\n  dv_21 = 12 * dv_20;\n  DataVector& dv_22 = temps.at(22);\n  dv_22 = Dx * d_0;\n  DataVector& dv_23 = temps.at(23);\n  dv_23 = dv_6 * xp;\n  DataVector& dv_24 = temps.at(24);\n  dv_24 = dv_22 + dv_23;\n  DataVector& dv_25 = temps.at(25);\n  dv_25 = dv_9 * fx;\n  DataVector& dv_26 = temps.at(26);\n  dv_26 = dv_24 * dv_25;\n  DataVector& dv_27 = temps.at(27);\n  dv_27 = 6 * dv_26;\n  DataVector& dv_28 = temps.at(28);\n  dv_28 = Dy * d_19 + d_16 * dv_3;\n  DataVector& dv_29 = temps.at(29);\n  dv_29 = dv_9 * fy;\n  DataVector& dv_30 = temps.at(30);\n  dv_30 = dv_28 * dv_29;\n  DataVector& dv_31 = temps.at(31);\n  dv_31 = 6 * dv_30;\n  DataVector& dv_32 = temps.at(32);\n  dv_32 = d_12 * dv_5;\n  DataVector& dv_33 = temps.at(33);\n  dv_33 = d_20 * dv_32;\n  DataVector& dv_34 = temps.at(34);\n  dv_34 = dv_17 * dv_33;\n  DataVector& dv_35 = temps.at(35);\n  dv_35 = dv_24 * fx;\n  DataVector& dv_36 = temps.at(36);\n  dv_36 = d_23 * dv_17;\n  DataVector& dv_37 = temps.at(37);\n  dv_37 = dv_35 * dv_36;\n  DataVector& dv_38 = temps.at(38);\n  dv_38 = dv_28 * fy;\n  DataVector& dv_39 = temps.at(39);\n  dv_39 = dv_36 * dv_38;\n  DataVector& dv_40 = temps.at(40);\n  dv_40 = dv_17 * dv_17 * dv_17;\n  DataVector& dv_41 = temps.at(41);\n  dv_41 = d_6 * dv_16 - dv_11;\n  DataVector& dv_42 = temps.at(42);\n  dv_42 = dv_3 * dv_4;\n  DataVector& dv_43 = temps.at(43);\n  dv_43 = -dv_12;\n  DataVector& dv_44 = temps.at(15);\n  dv_44 = dv_15 + dv_43;\n  DataVector& dv_45 = temps.at(44);\n  dv_45 = -dv_13;\n  DataVector& dv_46 = temps.at(45);\n  dv_46 = dv_12 + dv_14 + dv_45;\n  DataVector& dv_47 = temps.at(46);\n  dv_47 = d_17 * dv_46 + d_34 * dv_44 - 4 * dv_42;\n  DataVector& dv_48 = temps.at(14);\n  dv_48 = 2 * dv_14;\n  DataVector& dv_49 = temps.at(43);\n  dv_49 = 2 * dv_13 + dv_43 + dv_48;\n  DataVector& dv_50 = temps.at(12);\n  dv_50 = 2 * dv_12 + dv_45 + dv_48;\n  DataVector& dv_51 = temps.at(14);\n  dv_51 = -6 * Dx * Dy * xp * yp + d_17 * dv_50 + d_34 * dv_49;\n  DataVector& dv_52 = temps.at(44);\n  dv_52 = -dv_51;\n  DataVector& dv_53 = temps.at(13);\n  dv_53 = -d_4 * dv_52 + d_42 * dv_47;\n  DataVector& dv_54 = temps.at(47);\n  dv_54 = -dv_53;\n  DataVector& dv_55 = temps.at(48);\n  dv_55 = dv_17 * dv_54;\n  DataVector& dv_56 = temps.at(49);\n  dv_56 = dv_32 * dv_52;\n  DataVector& dv_57 = temps.at(50);\n  dv_57 = -dv_54 * dv_8 + dv_56;\n  DataVector& dv_58 = temps.at(51);\n  dv_58 = -dv_57;\n  DataVector& dv_59 = temps.at(52);\n  dv_59 = 3 * dv_8;\n  DataVector& dv_60 = temps.at(53);\n  dv_60 = d_43 * dv_55 + dv_58 * dv_59;\n  DataVector& dv_61 = temps.at(54);\n  dv_61 = d_0 * d_12 * dv_17 * dv_41 * dv_8 - dv_20 * dv_60;\n  DataVector& dv_62 = temps.at(55);\n  dv_62 = M * dv_59;\n  DataVector& dv_63 = temps.at(56);\n  dv_63 = dv_62 * fx;\n  DataVector& dv_64 = temps.at(57);\n  dv_64 = d_12 * dv_52;\n  DataVector& dv_65 = temps.at(58);\n  dv_65 = dv_64 * xp;\n  DataVector& dv_66 = temps.at(59);\n  dv_66 = d_0 * dv_17;\n  DataVector& dv_67 = temps.at(60);\n  dv_67 = dv_65 * dv_66;\n  DataVector& dv_68 = temps.at(61);\n  dv_68 = dv_67 * dv_8;\n  DataVector& dv_69 = temps.at(62);\n  dv_69 = dv_24 * dv_60 + dv_68;\n  DataVector& dv_70 = temps.at(63);\n  dv_70 = dv_69 * rp;\n  DataVector& dv_71 = temps.at(64);\n  dv_71 = dv_62 * fy;\n  DataVector& dv_72 = temps.at(65);\n  dv_72 = dv_64 * yp;\n  DataVector& dv_73 = temps.at(66);\n  dv_73 = dv_66 * dv_72;\n  DataVector& dv_74 = temps.at(67);\n  dv_74 = dv_73 * dv_8;\n  DataVector& dv_75 = temps.at(68);\n  dv_75 = dv_28 * dv_60 + dv_74;\n  DataVector& dv_76 = temps.at(69);\n  dv_76 = dv_75 * rp;\n  DataVector& dv_77 = temps.at(70);\n  dv_77 = Du_fx * dv_24;\n  DataVector& dv_78 = temps.at(71);\n  dv_78 = 6 * dv_8;\n  DataVector& dv_79 = temps.at(72);\n  dv_79 = dv_17 * dv_17;\n  DataVector& dv_80 = temps.at(73);\n  dv_80 = d_47 * dv_79;\n  DataVector& dv_81 = temps.at(74);\n  dv_81 = dv_78 * dv_80;\n  DataVector& dv_82 = temps.at(75);\n  dv_82 = Du_fy * dv_28;\n  DataVector& dv_83 = temps.at(76);\n  dv_83 = dv_79 * dv_9;\n  DataVector& dv_84 = temps.at(77);\n  dv_84 = d_51 * dv_83;\n  DataVector& dv_85 = temps.at(78);\n  dv_85 = d_54 * dv_79;\n  DataVector& dv_86 = temps.at(79);\n  dv_86 = dv_25 * dv_85;\n  DataVector& dv_87 = temps.at(80);\n  dv_87 = dv_29 * dv_85;\n  DataVector& dv_88 = temps.at(81);\n  dv_88 = dv_79 * dv_8;\n  DataVector& dv_89 = temps.at(82);\n  dv_89 = d_58 * dv_21;\n  DataVector& dv_90 = temps.at(83);\n  dv_90 = d_59 * dv_77;\n  DataVector& dv_91 = temps.at(84);\n  dv_91 = 2 * dv_17;\n  DataVector& dv_92 = temps.at(85);\n  dv_92 = dv_8 * dv_8 * dv_8;\n  DataVector& dv_93 = temps.at(86);\n  dv_93 = d_60 * dv_92;\n  DataVector& dv_94 = temps.at(87);\n  dv_94 = dv_91 * dv_93;\n  DataVector& dv_95 = temps.at(88);\n  dv_95 = dv_22 * dv_56;\n  DataVector& dv_96 = temps.at(89);\n  dv_96 = dv_54 * dv_8;\n  DataVector& dv_97 = temps.at(90);\n  dv_97 = dv_22 * dv_96 + dv_23 * dv_58 + dv_67 - dv_95;\n  DataVector& dv_98 = temps.at(91);\n  dv_98 = d_63 * dv_17;\n  DataVector& dv_99 = temps.at(92);\n  dv_99 = dv_97 * dv_98;\n  DataVector& dv_100 = temps.at(93);\n  dv_100 = Dy * d_0;\n  DataVector& dv_101 = temps.at(94);\n  dv_101 = dv_100 * dv_56;\n  DataVector& dv_102 = temps.at(95);\n  dv_102 = dv_6 * yp;\n  DataVector& dv_103 = temps.at(96);\n  dv_103 = dv_100 * dv_96 - dv_101 + dv_102 * dv_58 + dv_73;\n  DataVector& dv_104 = temps.at(97);\n  dv_104 = dv_103 * fy;\n  DataVector& dv_105 = temps.at(98);\n  dv_105 = d_61 * dv_98;\n  DataVector& dv_106 = temps.at(42);\n  dv_106 = -d_17 * dv_50 - d_34 * dv_49 + 6 * dv_42;\n  DataVector& dv_107 = temps.at(99);\n  dv_107 = dv_106 * dv_32;\n  DataVector& dv_108 = temps.at(100);\n  dv_108 = -dv_47;\n  DataVector& dv_109 = temps.at(101);\n  dv_109 = d_4 * dv_106 + d_42 * dv_108;\n  DataVector& dv_110 = temps.at(102);\n  dv_110 = -dv_107 + dv_109 * dv_8;\n  DataVector& dv_111 = temps.at(103);\n  dv_111 = d_65 * dv_16;\n  DataVector& dv_112 = temps.at(11);\n  dv_112 = d_23 * dv_11;\n  DataVector& dv_113 = temps.at(104);\n  dv_113 = -dv_111 * dv_17 + dv_112 * dv_17;\n  DataVector& dv_114 = temps.at(105);\n  dv_114 = dv_110 * dv_20 + dv_113;\n  DataVector& dv_115 = temps.at(106);\n  dv_115 = d_21 * dv_114;\n  DataVector& dv_116 = temps.at(107);\n  dv_116 = d_65 * dv_17;\n  DataVector& dv_117 = temps.at(108);\n  dv_117 = 6 * dv_116;\n  DataVector& dv_118 = temps.at(109);\n  dv_118 = Du_ft * dv_20;\n  DataVector& dv_119 = temps.at(110);\n  dv_119 = d_66 * dv_118;\n  DataVector& dv_120 = temps.at(111);\n  dv_120 = 4 * dv_17;\n  DataVector& dv_121 = temps.at(87);\n  dv_121 = -d_59 * dv_119 * dv_120 * dv_92 - d_59 * dv_82 * dv_94 +\n           d_62 * dv_99 + dv_104 * dv_105 + dv_115 * dv_117 - dv_90 * dv_94;\n  DataVector& dv_122 = temps.at(112);\n  dv_122 = 6. * M * d_6 * dv_61 * dv_8 * ft +\n           12. * dv_40 *\n               (d_24 * d_29 * d_30 * ft + d_30 * d_31 * d_38 * fx +\n                d_30 * d_31 * d_41 * fy) -\n           d_49 * dv_84 - d_52 * dv_86 - d_55 * dv_87 - dv_121 - dv_63 * dv_70 -\n           dv_71 * dv_76 - dv_77 * dv_81 - dv_81 * dv_82 - dv_88 * dv_89;\n  DataVector& dv_123 = temps.at(113);\n  dv_123 = d_1 * dv_34;\n  DataVector& dv_124 = temps.at(114);\n  dv_124 = dv_123 * ft + dv_19 * dv_6 + dv_26 + dv_30 + dv_37 + dv_39;\n  DataVector& dv_125 = temps.at(115);\n  dv_125 = dv_124 * dv_18;\n  DataVector& dv_126 = temps.at(116);\n  dv_126 = d_14 * dv_8;\n  DataVector& dv_127 = temps.at(117);\n  dv_127 = Dx + d_5 * dv_23 - d_80 * dv_126;\n  DataVector& dv_128 = temps.at(116);\n  dv_128 = Dy + d_5 * dv_102 - d_82 * dv_126;\n  DataVector& dv_129 = temps.at(118);\n  dv_129 = Dx * xpddot + Dy * ypddot;\n  DataVector& dv_130 = temps.at(119);\n  dv_130 = d_1 * dv_2;\n  DataVector& dv_131 = temps.at(120);\n  dv_131 = d_6 * dv_2;\n  DataVector& dv_132 = temps.at(121);\n  dv_132 = d_71 * dv_131 + d_76 * dv_6 + dv_6 * rpdot;\n  DataVector& dv_133 = temps.at(122);\n  dv_133 = dv_8 * rp;\n  DataVector& dv_134 = temps.at(119);\n  dv_134 =\n      -d_70 * (2. * M * dv_2 * dv_5 * rp -\n               d_13 * dv_133 *\n                   (d_0 * dv_129 + d_4 * dv_130 + dv_130 * rp + dv_132) +\n               3. * d_13 * dv_9 * rpdot - d_72 * dv_10 - d_83 * d_85 * dv_9) +\n      dv_127 * xpdot + dv_128 * ypdot;\n  DataVector& dv_135 = temps.at(123);\n  dv_135 = sqrt(dv_17);\n  DataVector& dv_136 = temps.at(124);\n  dv_136 = 18 * dv_135;\n  DataVector& dv_137 = temps.at(125);\n  dv_137 = dv_6 * dv_9;\n  DataVector& dv_138 = temps.at(126);\n  dv_138 = dv_24 * dt_fx;\n  DataVector& dv_139 = temps.at(127);\n  dv_139 = -d_2 + d_89 + dv_2;\n  DataVector& dv_140 = temps.at(128);\n  dv_140 = d_1 * dv_139;\n  DataVector& dv_141 = temps.at(129);\n  dv_141 = dv_28 * dt_fy;\n  DataVector& dv_142 = temps.at(130);\n  dv_142 = Dx * d_6;\n  DataVector& dv_143 = temps.at(131);\n  dv_143 =\n      d_1 * (-d_34 * xpdot + dv_4 * xpdot + xp * (d_89 + 2 * dv_0 + dv_1)) +\n      d_71 * dv_142 - d_79;\n  DataVector& dv_144 = temps.at(132);\n  dv_144 = 3 * Dy;\n  DataVector& dv_145 = temps.at(133);\n  dv_145 = Dx * ypdot;\n  DataVector& dv_146 = temps.at(1);\n  dv_146 = 2 * dv_1;\n  DataVector& dv_147 = temps.at(134);\n  dv_147 = d_16 * (d_89 + dv_0 + dv_146) + d_33 * (dv_145 - xpdot * yp) - d_81 +\n           d_90 * dv_144;\n  DataVector& dv_148 = temps.at(118);\n  dv_148 = -d_10 - d_9 + dv_129;\n  DataVector& dv_149 = temps.at(121);\n  dv_149 = d_4 * dv_140 + dv_132 + dv_140 * rp;\n  DataVector& dv_150 = temps.at(135);\n  dv_150 = d_0 * dv_148 + dv_149;\n  DataVector& dv_151 = temps.at(136);\n  dv_151 = dv_150 * dv_8;\n  DataVector& dv_152 = temps.at(137);\n  dv_152 = 4 * dv_20;\n  DataVector& dv_153 = temps.at(138);\n  dv_153 = 2 * dv_8;\n  DataVector& dv_154 = temps.at(139);\n  dv_154 = dv_150 * dv_153;\n  DataVector& dv_155 = temps.at(59);\n  dv_155 = dv_32 * dv_66;\n  DataVector& dv_156 = temps.at(140);\n  dv_156 = 6 * d_12 * d_90 * dv_17;\n  DataVector& dv_157 = temps.at(141);\n  dv_157 = dv_36 * fx;\n  DataVector& dv_158 = temps.at(142);\n  dv_158 = dv_36 * fy;\n  DataVector& dv_159 = temps.at(143);\n  dv_159 = dv_17 * ft;\n  DataVector& dv_160 = temps.at(144);\n  dv_160 = d_77 * dv_91;\n  DataVector& dv_161 = temps.at(145);\n  dv_161 = d_53 * dv_160;\n  DataVector& dv_162 = temps.at(146);\n  dv_162 = 6 * dv_18;\n  DataVector& dv_163 = temps.at(147);\n  dv_163 = d_59 * dv_135;\n  DataVector& dv_164 = temps.at(148);\n  dv_164 = 1.0 / dv_135;\n  DataVector& dv_165 = temps.at(149);\n  dv_165 = d_59 * dv_134;\n  DataVector& dv_166 = temps.at(150);\n  dv_166 = d_69 * dv_88;\n  DataVector& dv_167 = temps.at(151);\n  dv_167 = M * dv_139;\n  DataVector& dv_168 = temps.at(152);\n  dv_168 = 72 * dv_166 * d_60 * rp * rp;\n  DataVector& dv_169 = temps.at(153);\n  dv_169 = d_54 * dv_83;\n  DataVector& dv_170 = temps.at(154);\n  dv_170 = d_38 * dv_25;\n  DataVector& dv_171 = temps.at(155);\n  dv_171 = d_65 * dv_79;\n  DataVector& dv_172 = temps.at(156);\n  dv_172 = 108 * dv_171 * rpdot;\n  DataVector& dv_173 = temps.at(157);\n  dv_173 = d_41 * dv_29;\n  DataVector& dv_174 = temps.at(76);\n  dv_174 = d_110 * dv_83;\n  DataVector& dv_175 = temps.at(158);\n  dv_175 = d_109 * d_31 * dv_79;\n  DataVector& dv_176 = temps.at(159);\n  dv_176 = 6 * dv_150 * dv_80;\n  DataVector& dv_177 = temps.at(144);\n  dv_177 = d_46 * d_95 * dv_160 * dv_92;\n  DataVector& dv_178 = temps.at(160);\n  dv_178 = 24 * dv_151;\n  DataVector& dv_179 = temps.at(78);\n  dv_179 = dv_178 * dv_85;\n  DataVector& dv_180 = temps.at(161);\n  dv_180 = dv_77 * dv_93;\n  DataVector& dv_181 = temps.at(162);\n  dv_181 = 4 * dv_165;\n  DataVector& dv_182 = temps.at(163);\n  dv_182 = dv_82 * dv_93;\n  DataVector& dv_183 = temps.at(164);\n  dv_183 = dv_17 * fx;\n  DataVector& dv_184 = temps.at(165);\n  dv_184 = 72 * dv_134;\n  DataVector& dv_185 = temps.at(166);\n  dv_185 = d_119 * dv_79;\n  DataVector& dv_186 = temps.at(167);\n  dv_186 = d_120 * dv_79;\n  DataVector& dv_187 = temps.at(168);\n  dv_187 = dv_184 * dv_186;\n  DataVector& dv_188 = temps.at(169);\n  dv_188 = dv_150 * rp;\n  DataVector& dv_189 = temps.at(170);\n  dv_189 = d_61 * dv_188;\n  DataVector& dv_190 = temps.at(62);\n  dv_190 = dv_69 * fx;\n  DataVector& dv_191 = temps.at(68);\n  dv_191 = dv_75 * fy;\n  DataVector& dv_192 = temps.at(171);\n  dv_192 = dv_10 * dv_17;\n  DataVector& dv_193 = temps.at(172);\n  dv_193 = dv_16 * rpdot;\n  DataVector& dv_194 = temps.at(173);\n  dv_194 = dv_139 * rp;\n  DataVector& dv_195 = temps.at(174);\n  dv_195 = -dv_134;\n  DataVector& dv_196 = temps.at(1);\n  dv_196 = dv_0 - dv_146;\n  DataVector& dv_197 = temps.at(132);\n  dv_197 = dv_0 * (d_123 + dv_144);\n  DataVector& dv_198 = temps.at(12);\n  dv_198 = dv_4 + dv_50;\n  DataVector& dv_199 = temps.at(175);\n  dv_199 = Dy - yp;\n  DataVector& dv_200 = temps.at(176);\n  dv_200 = -dv_145 * dv_199;\n  DataVector& dv_201 = temps.at(177);\n  dv_201 = 3 * dv_4;\n  DataVector& dv_202 = temps.at(43);\n  dv_202 = dv_201 + dv_49;\n  DataVector& dv_203 = temps.at(178);\n  dv_203 = -d_34 * dv_196 - xp * (3 * dv_200 + dv_202 * xpdot) +\n           yp * (dv_197 - dv_198 * ypdot);\n  DataVector& dv_204 = temps.at(179);\n  dv_204 = d_42 * dv_32;\n  DataVector& dv_205 = temps.at(180);\n  dv_205 = 2 * dv_5;\n  DataVector& dv_206 = temps.at(181);\n  dv_206 = 2 * Dy;\n  DataVector& dv_207 = temps.at(182);\n  dv_207 = 2 * dv_4;\n  DataVector& dv_208 = temps.at(15);\n  dv_208 = -d_34 * (Dy * ypdot - dv_0) +\n           xp * (2 * dv_200 + xpdot * (dv_207 + dv_44)) +\n           yp * (-dv_0 * (dv_206 + yp) + ypdot * (dv_4 + dv_46));\n  DataVector& dv_209 = temps.at(0);\n  dv_209 = Dx * d_20;\n  DataVector& dv_210 = temps.at(176);\n  dv_210 = dv_139 * dv_64;\n  DataVector& dv_211 = temps.at(1);\n  dv_211 = -d_34 * dv_196 - xp * (-3 * dv_145 * dv_199 + dv_202 * xpdot) +\n           yp * (dv_197 - dv_198 * ypdot);\n  DataVector& dv_212 = temps.at(175);\n  dv_212 = 2 * dv_32;\n  DataVector& dv_213 = temps.at(43);\n  dv_213 = d_77 * dv_52;\n  DataVector& dv_214 = temps.at(132);\n  dv_214 = dv_205 * dv_213;\n  DataVector& dv_215 = temps.at(12);\n  dv_215 = d_20 * dv_96;\n  DataVector& dv_216 = temps.at(89);\n  dv_216 = 4 * dv_96 * rpdot;\n  DataVector& dv_217 = temps.at(133);\n  dv_217 = dv_17 * dv_64;\n  DataVector& dv_218 = temps.at(4);\n  dv_218 = d_124 * dv_217;\n  DataVector& dv_219 = temps.at(45);\n  dv_219 = dv_150 * dv_54;\n  DataVector& dv_220 = temps.at(183);\n  dv_220 = d_63 * dv_91;\n  DataVector& dv_221 = temps.at(184);\n  dv_221 = dv_211 * xp;\n  DataVector& dv_222 = temps.at(185);\n  dv_222 = d_50 * dv_213;\n  DataVector& dv_223 = temps.at(7);\n  dv_223 = dv_58 * dv_7;\n  DataVector& dv_224 = temps.at(186);\n  dv_224 = dv_194 * dv_58;\n  DataVector& dv_225 = temps.at(46);\n  dv_225 = 2 * d_4 * dv_211 - d_7 * dv_208 - d_75 * dv_47 + d_76 * dv_52;\n  DataVector& dv_226 = temps.at(187);\n  dv_226 = dv_225 * dv_8;\n  DataVector& dv_227 = temps.at(188);\n  dv_227 = d_20 * dv_134;\n  DataVector& dv_228 = temps.at(189);\n  dv_228 = -d_122 * dv_56 - d_20 * dv_214 + dv_150 * dv_54 * rp -\n           dv_194 * dv_64 - dv_204 * dv_211 + dv_225 * dv_8 * rp +\n           dv_54 * dv_8 * rpdot;\n  DataVector& dv_229 = temps.at(190);\n  dv_229 = Dy * d_20;\n  DataVector& dv_230 = temps.at(133);\n  dv_230 = d_126 * dv_217;\n  DataVector& dv_231 = temps.at(191);\n  dv_231 = d_123 * dv_17;\n  DataVector& dv_232 = temps.at(192);\n  dv_232 = d_63 * dv_211 * dv_231;\n  DataVector& dv_233 = temps.at(191);\n  dv_233 = dv_222 * dv_231;\n  DataVector& dv_234 = temps.at(57);\n  dv_234 = d_123 * dv_64;\n  DataVector& dv_235 = temps.at(193);\n  dv_235 = d_23 * dv_41;\n  DataVector& dv_236 = temps.at(194);\n  dv_236 = dv_235 * dv_8;\n  DataVector& dv_237 = temps.at(195);\n  dv_237 = dv_153 * dv_17;\n  DataVector& dv_238 = temps.at(196);\n  dv_238 = d_63 * dv_237;\n  DataVector& dv_239 = temps.at(195);\n  dv_239 = d_50 * dv_237;\n  DataVector& dv_240 = temps.at(197);\n  dv_240 = dv_150 * dv_17;\n  DataVector& dv_241 = temps.at(198);\n  dv_241 = d_63 * dv_41;\n  DataVector& dv_242 = temps.at(199);\n  dv_242 = dv_60 * rp;\n  DataVector& dv_243 = temps.at(200);\n  dv_243 = 3 * dv_58;\n  DataVector& dv_244 = temps.at(48);\n  dv_244 = 4 * d_12 * d_20 * dv_134 * dv_54 - d_121 * dv_55 - d_93 * dv_55 -\n           dv_188 * dv_243 - dv_220 * dv_225 - dv_228 * dv_59;\n  DataVector& dv_245 = temps.at(169);\n  dv_245 = d_20 * dv_240;\n  DataVector& dv_246 = temps.at(46);\n  dv_246 = (1.0 / 12.0) * 1.0 / dv_40;\n  DataVector& dv_247 = temps.at(201);\n  dv_247 = square(dv_17) * sqrt(dv_17);\n  DataVector& dv_248 = temps.at(202);\n  dv_248 = d_119 * dv_247;\n  DataVector& dv_249 = temps.at(203);\n  dv_249 = d_127 * dv_247;\n  DataVector& dv_250 = temps.at(204);\n  dv_250 = 6 * dv_247;\n  DataVector& dv_251 = temps.at(205);\n  dv_251 = d_120 * dv_250;\n  DataVector& dv_252 = temps.at(206);\n  dv_252 = d_80 * dv_247;\n  DataVector& dv_253 = temps.at(204);\n  dv_253 = d_47 * dv_250;\n  DataVector& dv_254 = temps.at(207);\n  dv_254 = d_80 * dv_253;\n  DataVector& dv_255 = temps.at(208);\n  dv_255 = d_51 * dv_9;\n  DataVector& dv_256 = temps.at(209);\n  dv_256 = dv_18 * dv_255;\n  DataVector& dv_257 = temps.at(210);\n  dv_257 = d_107 * dv_8;\n  DataVector& dv_258 = temps.at(211);\n  dv_258 = d_54 * dv_18;\n  DataVector& dv_259 = temps.at(212);\n  dv_259 = 12 * dv_258 * dv_9;\n  DataVector& dv_260 = temps.at(213);\n  dv_260 = d_47 * dv_247;\n  DataVector& dv_261 = temps.at(214);\n  dv_261 = 12 * dv_8;\n  DataVector& dv_262 = temps.at(215);\n  dv_262 = dv_260 * dv_261 * yp;\n  DataVector& dv_263 = temps.at(216);\n  dv_263 = d_59 * dv_18;\n  DataVector& dv_264 = temps.at(217);\n  dv_264 = d_131 * dv_92;\n  DataVector& dv_265 = temps.at(218);\n  dv_265 = 4 * dv_263 * dv_264;\n  DataVector& dv_266 = temps.at(86);\n  dv_266 = dv_263 * dv_93;\n  DataVector& dv_267 = temps.at(219);\n  dv_267 = d_132 * dv_266;\n  DataVector& dv_268 = temps.at(220);\n  dv_268 = dv_162 * dv_9;\n  DataVector& dv_269 = temps.at(221);\n  dv_269 = d_54 * dv_268;\n  DataVector& dv_270 = temps.at(213);\n  dv_270 = dv_260 * dv_78;\n  DataVector& dv_271 = temps.at(86);\n  dv_271 = 2 * dv_266;\n  DataVector& dv_272 = temps.at(222);\n  dv_272 = 24 * dv_8;\n  DataVector& dv_273 = temps.at(223);\n  dv_273 = d_50 * dv_32;\n  DataVector& dv_274 = temps.at(224);\n  dv_274 = d_21 * dv_18 * dv_272 * dv_273;\n  DataVector& dv_275 = temps.at(216);\n  dv_275 = d_131 * dv_21 * dv_263 * dv_9;\n  DataVector& dv_276 = temps.at(214);\n  dv_276 = dv_258 * dv_261;\n  DataVector& dv_277 = temps.at(225);\n  dv_277 = d_80 * dv_276;\n  DataVector& dv_278 = temps.at(226);\n  dv_278 = dv_252 * dv_272;\n  DataVector& dv_279 = temps.at(83);\n  dv_279 = d_60 * dv_90;\n  DataVector& dv_280 = temps.at(227);\n  dv_280 = d_80 * dv_268;\n  DataVector& dv_281 = temps.at(228);\n  dv_281 = d_59 * d_60 * dv_82;\n  DataVector& dv_282 = temps.at(229);\n  dv_282 = d_54 * dv_278;\n  DataVector& dv_283 = temps.at(230);\n  dv_283 = dv_127 * dv_18;\n  DataVector& dv_284 = temps.at(231);\n  dv_284 = d_119 * d_48 * dv_20;\n  DataVector& dv_285 = temps.at(232);\n  dv_285 = d_120 * dv_127;\n  DataVector& dv_286 = temps.at(233);\n  dv_286 = dv_162 * dv_285;\n  DataVector& dv_287 = temps.at(234);\n  dv_287 = d_49 * dv_248;\n  DataVector& dv_288 = temps.at(232);\n  dv_288 = dv_247 * dv_285;\n  DataVector& dv_289 = temps.at(235);\n  dv_289 = dv_127 * dv_135;\n  DataVector& dv_290 = temps.at(223);\n  dv_290 = dv_273 * dv_9;\n  DataVector& dv_291 = temps.at(236);\n  dv_291 = 36 * d_21 * dv_290;\n  DataVector& dv_292 = temps.at(237);\n  dv_292 = dv_8 * dv_89;\n  DataVector& dv_293 = temps.at(217);\n  dv_293 = dv_163 * dv_21 * dv_264;\n  DataVector& dv_294 = temps.at(238);\n  dv_294 = d_54 * dv_127;\n  DataVector& dv_295 = temps.at(239);\n  dv_295 = dv_136 * dv_294;\n  DataVector& dv_296 = temps.at(240);\n  dv_296 = d_47 * dv_77;\n  DataVector& dv_297 = temps.at(241);\n  dv_297 = dv_162 * dv_8;\n  DataVector& dv_298 = temps.at(242);\n  dv_298 = dv_127 * dv_297;\n  DataVector& dv_299 = temps.at(243);\n  dv_299 = 6 * dv_163;\n  DataVector& dv_300 = temps.at(244);\n  dv_300 = dv_127 * dv_299;\n  DataVector& dv_301 = temps.at(245);\n  dv_301 = d_47 * dv_82;\n  DataVector& dv_302 = temps.at(238);\n  dv_302 = dv_18 * dv_294;\n  DataVector& dv_303 = temps.at(246);\n  dv_303 = d_52 * dv_25;\n  DataVector& dv_304 = temps.at(247);\n  dv_304 = d_55 * dv_29;\n  DataVector& dv_305 = temps.at(104);\n  dv_305 = d_136 * dv_136 * (dv_113 + dv_20 * dv_58);\n  DataVector& dv_306 = temps.at(248);\n  dv_306 = d_80 * dv_135;\n  DataVector& dv_307 = temps.at(249);\n  dv_307 = d_137 * dv_61;\n  DataVector& dv_308 = temps.at(250);\n  dv_308 = 6 * dv_307;\n  DataVector& dv_309 = temps.at(251);\n  dv_309 = d_62 * dv_70;\n  DataVector& dv_310 = temps.at(252);\n  dv_310 = d_138 * dv_76;\n  DataVector& dv_311 = temps.at(253);\n  dv_311 = d_139 * dv_289;\n  DataVector& dv_312 = temps.at(254);\n  dv_312 = dv_22 * dv_53;\n  DataVector& dv_313 = temps.at(255);\n  dv_313 = dv_36 * dv_51;\n  DataVector& dv_314 = temps.at(256);\n  dv_314 = d_140 * (Dx * d_0 * d_12 * dv_5 * dv_51 - dv_23 * dv_57 -\n                    dv_312 * dv_8 - dv_313 * xp);\n  DataVector& dv_315 = temps.at(13);\n  dv_315 = d_0 * dv_53;\n  DataVector& dv_316 = temps.at(257);\n  dv_316 = dv_315 * dv_8;\n  DataVector& dv_317 = temps.at(255);\n  dv_317 = d_141 * (Dy * d_0 * d_12 * dv_5 * dv_51 - Dy * dv_316 -\n                    dv_102 * dv_57 - dv_313 * yp);\n  DataVector& dv_318 = temps.at(258);\n  dv_318 = dv_127 * dv_164;\n  DataVector& dv_319 = temps.at(249);\n  dv_319 = dv_307 * dv_8;\n  DataVector& dv_320 = temps.at(259);\n  dv_320 = 30 * dv_319;\n  DataVector& dv_321 = temps.at(260);\n  dv_321 = M * dv_133;\n  DataVector& dv_322 = temps.at(261);\n  dv_322 = 15 * dv_321;\n  DataVector& dv_323 = temps.at(262);\n  dv_323 = dv_318 * dv_322;\n  DataVector& dv_324 = temps.at(263);\n  dv_324 = d_143 * dv_127;\n  DataVector& dv_325 = temps.at(264);\n  dv_325 = 2 * dv_111;\n  DataVector& dv_326 = temps.at(265);\n  dv_326 = Dx * d_34;\n  DataVector& dv_327 = temps.at(266);\n  dv_327 = Dx * d_17;\n  DataVector& dv_328 = temps.at(177);\n  dv_328 = dv_201 * xp + dv_326 - 2 * dv_327;\n  DataVector& dv_329 = temps.at(267);\n  dv_329 = dv_212 * dv_328;\n  DataVector& dv_330 = temps.at(182);\n  dv_330 = d_4 * dv_328 + d_42 * (dv_207 * xp + dv_326 - dv_327);\n  DataVector& dv_331 = temps.at(266);\n  dv_331 = -d_80 * dv_54 + dv_329 - 2 * dv_330 * dv_8 + dv_65;\n  DataVector& dv_332 = temps.at(265);\n  dv_332 = -dv_331;\n  DataVector& dv_333 = temps.at(268);\n  dv_333 = d_136 * dv_162;\n  DataVector& dv_334 = temps.at(269);\n  dv_334 = d_0 * dv_206 * dv_32;\n  DataVector& dv_335 = temps.at(13);\n  dv_335 = Dy * dv_315;\n  DataVector& dv_336 = temps.at(270);\n  dv_336 = d_123 * d_23 * dv_51;\n  DataVector& dv_337 = temps.at(271);\n  dv_337 = d_144 * dv_57;\n  DataVector& dv_338 = temps.at(272);\n  dv_338 = d_63 * dv_18;\n  DataVector& dv_339 = temps.at(273);\n  dv_339 = d_138 * dv_338;\n  DataVector& dv_340 = temps.at(274);\n  dv_340 = d_43 * dv_51 * xp;\n  DataVector& dv_341 = temps.at(257);\n  dv_341 = -d_0 * d_12 * dv_5 * dv_51 + dv_316;\n  DataVector& dv_342 = temps.at(272);\n  dv_342 = d_62 * dv_338;\n  DataVector& dv_343 = temps.at(275);\n  dv_343 = dv_153 * dv_36;\n  DataVector& dv_344 = temps.at(276);\n  dv_344 = 4 * dv_36;\n  DataVector& dv_345 = temps.at(277);\n  dv_345 = d_80 * dv_243 + dv_324 * dv_54 + dv_330 * dv_344 + dv_332 * dv_59;\n  DataVector& dv_346 = temps.at(71);\n  dv_346 = d_137 * dv_135 * dv_78;\n  DataVector& dv_347 = temps.at(278);\n  dv_347 = d_123 * dv_36;\n  DataVector& dv_348 = temps.at(279);\n  dv_348 = d_0 * dv_8;\n  DataVector& dv_349 = temps.at(280);\n  dv_349 = dv_234 * dv_348;\n  DataVector& dv_350 = temps.at(281);\n  dv_350 = d_144 * dv_60;\n  DataVector& dv_351 = temps.at(282);\n  dv_351 = dv_135 * rp;\n  DataVector& dv_352 = temps.at(64);\n  dv_352 = dv_351 * dv_71;\n  DataVector& dv_353 = temps.at(283);\n  dv_353 = dv_343 * xp;\n  DataVector& dv_354 = temps.at(284);\n  dv_354 = d_0 * dv_153 * dv_65;\n  DataVector& dv_355 = temps.at(282);\n  dv_355 = dv_351 * dv_63;\n  DataVector& dv_356 = temps.at(56);\n  dv_356 = d_145 * dv_246;\n  DataVector& dv_357 = temps.at(285);\n  dv_357 = d_146 * dv_247;\n  DataVector& dv_358 = temps.at(286);\n  dv_358 = d_82 * dv_247;\n  DataVector& dv_359 = temps.at(204);\n  dv_359 = d_82 * dv_253;\n  DataVector& dv_360 = temps.at(214);\n  dv_360 = d_82 * dv_276;\n  DataVector& dv_361 = temps.at(222);\n  dv_361 = dv_272 * dv_358;\n  DataVector& dv_362 = temps.at(220);\n  dv_362 = d_82 * dv_268;\n  DataVector& dv_363 = temps.at(287);\n  dv_363 = d_54 * dv_361;\n  DataVector& dv_364 = temps.at(288);\n  dv_364 = dv_128 * dv_18;\n  DataVector& dv_365 = temps.at(289);\n  dv_365 = d_120 * dv_128;\n  DataVector& dv_366 = temps.at(290);\n  dv_366 = dv_162 * dv_365;\n  DataVector& dv_367 = temps.at(201);\n  dv_367 = dv_247 * dv_365;\n  DataVector& dv_368 = temps.at(289);\n  dv_368 = dv_128 * dv_135;\n  DataVector& dv_369 = temps.at(291);\n  dv_369 = d_54 * dv_128 * dv_136;\n  DataVector& dv_370 = temps.at(241);\n  dv_370 = dv_128 * dv_297;\n  DataVector& dv_371 = temps.at(243);\n  dv_371 = dv_128 * dv_299;\n  DataVector& dv_372 = temps.at(211);\n  dv_372 = dv_128 * dv_258;\n  DataVector& dv_373 = temps.at(292);\n  dv_373 = d_82 * dv_135;\n  DataVector& dv_374 = temps.at(293);\n  dv_374 = d_139 * dv_368;\n  DataVector& dv_375 = temps.at(294);\n  dv_375 = dv_128 * dv_164;\n  DataVector& dv_376 = temps.at(261);\n  dv_376 = dv_322 * dv_375;\n  DataVector& dv_377 = temps.at(295);\n  dv_377 = d_143 * dv_128;\n  DataVector& dv_378 = temps.at(296);\n  dv_378 = Dy * d_17;\n  DataVector& dv_379 = temps.at(297);\n  dv_379 = -d_34 * dv_206 + 3 * dv_3 * yp + dv_378;\n  DataVector& dv_380 = temps.at(298);\n  dv_380 = dv_212 * dv_379;\n  DataVector& dv_381 = temps.at(3);\n  dv_381 = d_123 * dv_3;\n  DataVector& dv_382 = temps.at(296);\n  dv_382 = d_4 * dv_379 + d_42 * (-Dy * d_34 + dv_378 + dv_381);\n  DataVector& dv_383 = temps.at(299);\n  dv_383 = -d_82 * dv_54 + dv_380 - 2 * dv_382 * dv_8 + dv_72;\n  DataVector& dv_384 = temps.at(300);\n  dv_384 = -dv_383;\n  DataVector& dv_385 = temps.at(200);\n  dv_385 = d_82 * dv_243 + dv_344 * dv_382 + dv_377 * dv_54 + dv_384 * dv_59;\n  DataVector& dv_386 = temps.at(167);\n  dv_386 = 2 * dv_186;\n  DataVector& dv_387 = temps.at(276);\n  dv_387 = d_30 * dv_40;\n  DataVector& dv_388 = temps.at(47);\n  dv_388 = d_31 * dv_387;\n  DataVector& dv_389 = temps.at(260);\n  dv_389 = 5 * dv_321;\n  DataVector& dv_390 = temps.at(301);\n  dv_390 = d_54 * dv_17;\n  DataVector& dv_391 = temps.at(32);\n  dv_391 = d_153 * (-d_154 * dv_8 + dv_32);\n  DataVector& dv_392 = temps.at(73);\n  dv_392 = dv_153 * dv_80;\n  DataVector& dv_393 = temps.at(52);\n  dv_393 = -d_153 * d_154 * d_43 * dv_17 + d_23 * dv_109 + dv_391 * dv_59;\n  DataVector& dv_394 = temps.at(302);\n  dv_394 = 2 * dv_393;\n  DataVector& dv_395 = temps.at(303);\n  dv_395 = d_1 * dv_133;\n  DataVector& dv_396 = temps.at(304);\n  dv_396 = d_23 * dv_106;\n  DataVector& dv_397 = temps.at(305);\n  dv_397 = dv_20 * dv_391;\n  DataVector& dv_398 = temps.at(306);\n  dv_398 = d_1 * dv_79;\n\n  get(get<CurvedScalarWave::Tags::Psi>(*result)) =\n      -d_67 / dv_18 / 12. *\n      (d_22 * dv_34 + d_67 * dv_122 / dv_17 + dv_19 * dv_21 + dv_27 + dv_31 +\n       6. * dv_37 + 6. * dv_39);\n  get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(*result)) =\n      -dv_246 *\n      (-d_56 * d_59 * d_78 * dv_125 + 54 * d_60 * d_69 * dv_125 -\n       d_86 * dv_124 * dv_134 * dv_136 +\n       d_86 * dv_162 *\n           (4 * M * d_12 * d_20 * dv_134 * dv_5 * ft +\n            2 * d_0 * d_12 * dv_134 * dv_24 * fx +\n            2 * d_0 * d_12 * dv_134 * dv_28 * fy - d_15 * dv_151 * dv_152 -\n            d_87 * dv_137 - d_88 * dv_137 - 14 * d_91 * dv_155 -\n            d_93 * dv_159 * dv_20 - dv_123 * dt_ft - dv_138 * dv_36 -\n            dv_138 * dv_9 - dv_140 * dv_19 - dv_140 * dv_98 * ft -\n            dv_141 * dv_36 - dv_141 * dv_9 - dv_143 * dv_157 - dv_143 * dv_25 -\n            dv_147 * dv_158 - dv_147 * dv_29 - dv_154 * dv_35 - dv_154 * dv_38 -\n            dv_156 * dv_35 - dv_156 * dv_38 - dv_161 * dv_35 - dv_161 * dv_38) -\n       d_94 * dv_122 * dv_163 / (rp * sqrt(rp)) +\n       4 * d_96 * dv_122 * dv_135 * square(rp) * sqrt(rp) +\n       5 * d_97 * dv_122 * dv_164 * dv_165 +\n       d_97 * dv_163 *\n           (48 * Du_ft * M * d_45 * d_56 * dv_134 * dv_17 * dv_5 * dv_8 +\n            28 * Du_ft * M * d_59 * d_60 * dv_17 * dv_5 * dv_92 * rpdot +\n            4 * Du_ft * M * d_59 * d_66 * dv_139 * dv_17 * dv_92 +\n            12 * Du_ft * M * d_59 * d_66 * dv_150 * dv_17 * dv_5 * dv_9 +\n            36 * Du_ft * M * d_59 * d_68 * d_77 * dv_5 * dv_79 * dv_8 +\n            4 * dt_Du_ft * M * d_59 * d_66 * dv_17 * dv_5 * dv_92 -\n            dt_Du_ft * d_45 * d_56 * dv_21 * dv_88 +\n            12 * Du_fx * d_108 * d_59 * dv_17 * dv_24 * dv_92 * rpdot +\n            18 * Du_fx * d_112 * d_59 * d_77 * dv_24 * dv_79 * dv_8 +\n            24 * Du_fx * d_45 * d_46 * dv_134 * dv_17 * dv_24 * dv_8 +\n            2 * Du_fx * d_59 * d_60 * dv_143 * dv_17 * dv_92 +\n            6 * Du_fx * d_59 * d_60 * dv_150 * dv_17 * dv_24 * dv_9 -\n            Du_fx * dv_143 * dv_81 +\n            2 * dt_Du_fx * d_59 * d_60 * dv_17 * dv_24 * dv_92 -\n            dt_Du_fx * dv_24 * dv_81 +\n            12 * Du_fy * d_108 * d_59 * dv_17 * dv_28 * dv_92 * rpdot +\n            18 * Du_fy * d_112 * d_59 * d_77 * dv_28 * dv_79 * dv_8 +\n            24 * Du_fy * d_45 * d_46 * dv_134 * dv_17 * dv_28 * dv_8 +\n            2 * Du_fy * d_59 * d_60 * dv_147 * dv_17 * dv_92 +\n            6 * Du_fy * d_59 * d_60 * dv_150 * dv_17 * dv_28 * dv_9 -\n            Du_fy * dv_147 * dv_81 +\n            2 * dt_Du_fy * d_59 * d_60 * dv_17 * dv_28 * dv_92 -\n            dt_Du_fy * dv_28 * dv_81 +\n            6 * M * d_12 * d_20 * dv_103 * dv_134 * fy +\n            6 * M * d_12 * d_20 * dv_134 * dv_97 * fx +\n            12 * M * d_12 * d_64 * dv_114 * dv_134 * ft +\n            6 * M * d_6 * dv_150 * dv_61 * ft +\n            6 * M * d_6 * dv_61 * dv_8 * dt_ft - M * dv_114 * dv_117 * dt_ft +\n            6 * M * dv_61 * dv_8 * ft * rp * rpdot +\n            6 * M * dv_8 * ft * rp *\n                (d_125 * dv_17 * dv_236 + d_77 * dv_239 * dv_41 -\n                 dv_134 * dv_153 * dv_241 - dv_167 * dv_242 + dv_20 * dv_244 -\n                 dv_238 * (dv_131 + dv_139 * dv_205 - dv_193 * rp) +\n                 dv_240 * dv_241) +\n            3 * M * dv_8 * fx *\n                (2 * d_12 * d_20 * dv_134 * dv_52 * dv_8 * xp - d_125 * dv_68 -\n                 dv_143 * dv_242 - dv_213 * dv_239 * xp - dv_218 * dv_8 -\n                 dv_221 * dv_238 + dv_24 * dv_244 - dv_245 * dv_65) +\n            3 * M * dv_8 * fy *\n                (2 * d_12 * d_20 * dv_134 * dv_52 * dv_8 * yp - d_125 * dv_74 -\n                 dv_147 * dv_242 - dv_230 * dv_8 - dv_232 * dv_8 -\n                 dv_233 * dv_8 + dv_244 * dv_28 - dv_245 * dv_72) +\n            12 * d_101 * d_24 * d_30 * dv_40 * ft - d_101 * d_48 * dv_84 +\n            12 * d_105 * d_30 * d_31 * dv_40 * fx - 12 * d_105 * dv_86 +\n            12 * d_106 * d_30 * d_31 * dv_40 * fy - 12 * d_106 * dv_87 -\n            d_107 * dv_167 * dv_88 - d_109 * d_24 * dv_174 -\n            d_110 * dv_184 * dv_185 +\n            48 * d_111 * d_12 * d_38 * d_77 * dv_40 * fx +\n            48 * d_111 * d_12 * d_41 * d_77 * dv_40 * fy -\n            d_113 * dv_178 * dv_79 - d_114 * dv_179 - d_114 * dv_187 -\n            d_115 * dv_179 - d_115 * dv_187 - d_116 * dv_103 * dv_158 -\n            d_116 * dv_157 * dv_97 - d_117 * d_118 * dv_104 * dv_17 -\n            d_117 * d_118 * dv_183 * dv_97 +\n            48 * d_12 * d_29 * d_50 * dv_134 * dv_17 * dv_9 * ft +\n            48 * d_12 * d_29 * d_77 * dv_40 * ft * d_24 * d_0 +\n            48 * d_12 * d_38 * d_53 * dv_134 * dv_17 * dv_9 * fx +\n            48 * d_12 * d_41 * d_53 * dv_134 * dv_17 * dv_9 * fy -\n            6 * d_21 * dv_98 *\n                (M * dv_110 * dv_194 + d_121 * dv_192 +\n                 d_31 * d_84 * dv_16 * dv_91 - 2 * d_54 * dv_16 * dv_195 +\n                 d_54 * dv_2 * dv_91 + 4 * d_63 * dv_10 * dv_195 -\n                 d_84 * d_92 * dv_192 - 9 * dv_116 * dv_193 +\n                 dv_120 * dv_139 * dv_33 +\n                 dv_20 * (-d_12 * dv_106 * dv_194 - d_122 * dv_107 +\n                          d_85 * dv_106 * dv_205 + dv_109 * dv_8 * rpdot +\n                          dv_109 * rp * (d_0 * dv_148 + dv_149) +\n                          dv_133 * (2 * d_4 * dv_203 - d_7 * dv_208 +\n                                    d_75 * dv_108 + d_76 * dv_106) -\n                          dv_203 * dv_204)) +\n            12 * d_24 * d_29 * d_30 * dv_40 * dt_ft +\n            192 * d_29 * d_30 * d_31 * dv_40 * ft * rpdot -\n            120 * d_29 * d_87 * dv_169 - 12 * d_29 * dv_84 * dt_ft +\n            12 * d_30 * d_31 * d_38 * dv_40 * dt_fx +\n            12 * d_30 * d_31 * d_41 * dv_40 * dt_fy +\n            180 * d_30 * d_38 * d_98 * dv_40 * fx * rpdot +\n            180 * d_30 * d_41 * d_98 * dv_40 * fy * rpdot -\n            156 * d_46 * dv_118 * dv_166 - d_52 * dv_169 * dt_fx -\n            d_55 * dv_169 * dt_fy - d_57 * d_96 * dv_120 * dv_20 * dv_92 -\n            d_61 * dv_157 *\n                (-Dx * d_50 * dv_214 + d_124 * dv_56 + d_125 * dv_67 -\n                 d_125 * dv_95 + d_33 * dv_224 - dv_209 * dv_210 -\n                 dv_209 * dv_211 * dv_212 + dv_209 * dv_219 + dv_209 * dv_226 -\n                 dv_215 * xpdot + dv_216 * dv_22 + dv_218 + dv_220 * dv_221 +\n                 dv_222 * dv_91 * xp + dv_223 * xpdot - 2 * dv_227 * dv_65 +\n                 dv_228 * dv_23) -\n            d_61 * dv_158 *\n                (-d_125 * dv_101 + d_125 * dv_73 + d_126 * dv_56 +\n                 d_16 * dv_224 + dv_100 * dv_216 + dv_102 * dv_228 -\n                 dv_206 * dv_211 * dv_33 - dv_206 * dv_222 * dv_5 -\n                 dv_210 * dv_229 - dv_215 * ypdot + dv_219 * dv_229 +\n                 dv_223 * ypdot + dv_226 * dv_229 - dv_227 * dv_234 + dv_230 +\n                 dv_232 + dv_233) -\n            d_61 * dv_99 * dt_fx - d_78 * d_98 * dv_115 * dv_17 -\n            42 * d_91 * dv_114 * dv_98 - dv_103 * dv_105 * dt_fy -\n            8 * dv_119 * dv_165 * dv_92 - dv_150 * dv_79 * dv_89 -\n            dv_168 * dv_77 - dv_168 * dv_82 - dv_170 * dv_172 -\n            dv_170 * dv_175 - dv_172 * dv_173 - dv_173 * dv_175 -\n            dv_176 * dv_77 - dv_176 * dv_82 - dv_177 * dv_77 - dv_177 * dv_82 -\n            dv_180 * dv_181 - dv_181 * dv_182 - dv_189 * dv_190 -\n            dv_189 * dv_191 - dv_62 * dv_70 * dt_fx - dv_62 * dv_76 * dt_fy)) *\n      1.0 / d_68 / (square(d_44) * sqrt(d_44));\n  get<0>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -dv_356 *\n      (-d_113 * dv_278 - d_114 * dv_282 - d_115 * dv_282 +\n       d_127 * d_26 * dv_259 + d_127 * dv_265 + d_128 * dv_248 +\n       d_128 * dv_256 + d_129 * d_26 * dv_249 - d_130 * dv_262 +\n       d_130 * dv_267 - d_133 * dv_270 + d_133 * dv_271 + d_134 * dv_288 +\n       d_135 * dv_288 + d_37 * dv_251 + d_37 * dv_269 + d_49 * dv_255 * dv_283 +\n       d_80 * dv_274 + d_80 * dv_275 + dv_127 * dv_287 - dv_127 * dv_293 +\n       dv_127 * dv_305 - dv_180 * dv_300 - dv_182 * dv_300 + dv_190 * dv_323 +\n       dv_191 * dv_323 - dv_249 * dv_257 - dv_252 * dv_89 - dv_254 * dv_77 -\n       dv_254 * dv_82 - dv_26 * dv_295 + dv_277 * dv_35 + dv_277 * dv_38 +\n       dv_279 * dv_280 + dv_280 * dv_281 - dv_283 * dv_284 + dv_283 * dv_292 -\n       dv_286 * dv_35 - dv_286 * dv_38 - dv_289 * dv_291 - dv_295 * dv_30 +\n       dv_296 * dv_298 + dv_298 * dv_301 + dv_302 * dv_303 + dv_302 * dv_304 +\n       dv_306 * dv_308 - dv_306 * dv_309 - dv_306 * dv_310 + dv_311 * dv_314 +\n       dv_311 * dv_317 - dv_318 * dv_320 -\n       dv_333 * (-2 * Dx * dv_116 + d_127 * dv_58 + d_142 * dv_155 +\n                 dv_10 * dv_324 - dv_127 * dv_325 + dv_20 * dv_332) -\n       dv_339 * (Dy * d_0 * d_12 * dv_51 * xp + 2 * Dy * d_0 * dv_330 * dv_8 +\n                 2 * d_0 * d_12 * dv_17 * dv_328 * yp - d_80 * dv_335 -\n                 dv_102 * dv_331 - dv_127 * dv_336 - dv_328 * dv_334 - dv_337) -\n       dv_342 * (Dx * d_0 * d_12 * dv_51 * xp + 2 * Dx * d_0 * dv_330 * dv_8 +\n                 2 * d_0 * d_12 * dv_17 * dv_328 * xp - d_35 * dv_57 -\n                 d_80 * dv_312 - dv_127 * dv_340 - dv_22 * dv_329 -\n                 dv_23 * dv_331 - dv_341) +\n       dv_346 *\n           (-d_127 * dv_60 + d_80 * dv_17 * dv_235 + dv_127 * dv_153 * dv_235 -\n            dv_20 * dv_345 + dv_343 * (dv_142 - dv_205 * xp)) -\n       dv_352 * (d_80 * dv_73 + dv_127 * dv_349 + dv_28 * dv_345 +\n                 dv_328 * dv_347 * dv_8 + dv_350) -\n       dv_355 * (d_36 * dv_60 + d_80 * dv_67 + dv_127 * dv_354 +\n                 dv_24 * dv_345 + dv_328 * dv_353));\n  get<1>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -dv_356 *\n      (-d_113 * dv_361 - d_114 * dv_363 - d_115 * dv_363 +\n       d_129 * d_25 * dv_357 + d_134 * dv_367 + d_135 * dv_367 +\n       d_146 * d_25 * dv_259 + d_146 * dv_265 + d_147 * dv_248 +\n       d_147 * dv_256 - d_148 * dv_262 + d_148 * dv_267 - d_149 * dv_270 +\n       d_149 * dv_271 + d_40 * dv_251 + d_40 * dv_269 + d_49 * dv_128 * dv_256 +\n       d_82 * dv_274 + d_82 * dv_275 + dv_128 * dv_287 - dv_128 * dv_293 +\n       dv_128 * dv_305 - dv_180 * dv_371 - dv_182 * dv_371 + dv_190 * dv_376 +\n       dv_191 * dv_376 - dv_257 * dv_357 - dv_26 * dv_369 + dv_279 * dv_362 +\n       dv_281 * dv_362 - dv_284 * dv_364 - dv_291 * dv_368 + dv_292 * dv_364 +\n       dv_296 * dv_370 - dv_30 * dv_369 + dv_301 * dv_370 + dv_303 * dv_372 +\n       dv_304 * dv_372 + dv_308 * dv_373 - dv_309 * dv_373 - dv_310 * dv_373 +\n       dv_314 * dv_374 + dv_317 * dv_374 - dv_320 * dv_375 -\n       dv_333 * (d_132 * dv_155 + d_146 * dv_58 + dv_10 * dv_377 -\n                 dv_116 * dv_206 - dv_128 * dv_325 + dv_20 * dv_384) -\n       dv_339 * (Dy * d_0 * d_12 * dv_51 * yp + 2 * Dy * d_0 * dv_382 * dv_8 +\n                 2 * d_0 * d_12 * dv_17 * dv_379 * yp - d_18 * dv_57 -\n                 d_82 * dv_335 - dv_102 * dv_383 - dv_128 * dv_336 -\n                 dv_334 * dv_379 - dv_341) -\n       dv_342 * (Dx * d_0 * d_12 * dv_51 * yp + 2 * Dx * d_0 * dv_382 * dv_8 +\n                 2 * d_0 * d_12 * dv_17 * dv_379 * xp - d_82 * dv_312 -\n                 dv_128 * dv_340 - dv_22 * dv_380 - dv_23 * dv_383 - dv_337) +\n       dv_346 * (d_0 * d_12 * d_82 * dv_17 * dv_41 +\n                 2 * d_0 * d_12 * dv_128 * dv_41 * dv_8 - d_146 * dv_60 -\n                 dv_20 * dv_385 - dv_343 * (-Dy * (-2 * d_17 + d_6) + dv_381)) +\n       dv_35 * dv_360 - dv_35 * dv_366 -\n       dv_352 * (d_19 * dv_60 + d_82 * dv_73 + dv_128 * dv_349 +\n                 dv_28 * dv_385 + dv_347 * dv_379 * dv_8) -\n       dv_355 * (d_82 * dv_67 + dv_128 * dv_354 + dv_24 * dv_385 + dv_350 +\n                 dv_353 * dv_379) -\n       dv_358 * dv_89 - dv_359 * dv_77 - dv_359 * dv_82 + dv_360 * dv_38 -\n       dv_366 * dv_38);\n  get<2>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -1.0 / 4.0 * d_145 * z *\n      (d_102 * d_6 * dv_159 * dv_8 * (dv_116 * dv_8 + dv_236 - dv_393 * dv_6) -\n       d_102 * dv_171 * ft * (-dv_111 + dv_112 - dv_116 + dv_391 * dv_6) +\n       d_12 * d_92 * dv_174 +\n       d_140 * dv_398 *\n           (2 * Dx * d_0 * d_153 * d_154 * dv_8 +\n            2 * d_0 * d_12 * d_153 * dv_17 * xp - d_142 * dv_397 -\n            d_153 * dv_212 * dv_22 - dv_396 * xp) +\n       d_141 * dv_398 *\n           (2 * Dy * d_0 * d_153 * d_154 * dv_8 +\n            2 * d_0 * d_12 * d_153 * dv_17 * yp - d_132 * dv_397 -\n            d_153 * dv_334 - dv_396 * yp) +\n       d_150 * d_24 * d_29 * dv_387 - d_150 * dv_185 * dv_20 +\n       d_151 * dv_388 * fx + d_151 * dv_86 + d_152 * dv_388 * fy +\n       d_152 * dv_87 - d_22 * dv_17 * dv_290 + d_58 * dv_152 * dv_88 + dv_121 +\n       dv_17 * dv_395 * fy *\n           (2 * d_0 * d_12 * d_153 * dv_17 * dv_8 * yp - dv_28 * dv_394 -\n            dv_348 * dv_72) +\n       dv_183 * dv_395 *\n           (2 * d_0 * d_12 * d_153 * dv_17 * dv_8 * xp - dv_24 * dv_394 -\n            dv_348 * dv_65) +\n       dv_190 * dv_389 + dv_191 * dv_389 - dv_27 * dv_390 - dv_31 * dv_390 -\n       10 * dv_319 - dv_35 * dv_386 - dv_38 * dv_386 + dv_392 * dv_77 +\n       dv_392 * dv_82) /\n      (cube(dv_17) * sqrt(dv_17));\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarWaveWorldtube)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AccelerationTermsOrder0.cpp\n  AccelerationTermsOrder1.cpp\n  KerrSchildDerivatives.cpp\n  Tags.cpp\n  PunctureField.cpp\n  PunctureFieldOrder0.cpp\n  PunctureFieldOrder1.cpp\n  RadiusFunctions.cpp\n  SelfForce.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Inboxes.hpp\n  KerrSchildDerivatives.hpp\n  SingletonChare.hpp\n  Tags.hpp\n  PunctureField.hpp\n  RadiusFunctions.hpp\n  SelfForce.hpp\n  System.hpp\n  Worldtube.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  GeneralRelativity\n  GeneralRelativitySolutions\n  LinearOperators\n  Options\n  Parallel\n  Time\n  Utilities\n  )\n\nadd_subdirectory(SingletonActions)\nadd_subdirectory(ElementActions)\nadd_subdirectory(Instantiations)\nadd_subdirectory(Triggers)\nadd_subdirectory(InitialData)\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  InitializeConstraintGammas.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InitializeConstraintGammas.hpp\n  InitializeCurrentIteration.hpp\n  IteratePunctureField.hpp\n  ReceiveWorldtubeData.hpp\n  SendToWorldtube.hpp\n)\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/InitializeConstraintGammas.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/InitializeConstraintGammas.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube::Initialization {\n\ntemplate <size_t Dim>\nvoid InitializeConstraintDampingGammas<Dim>::apply(\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  const size_t number_of_grid_points = get<0>(inertial_coords).size();\n  *gamma1 = Scalar<DataVector>{number_of_grid_points, 0.};\n  const double amplitude = 10.;\n  const double sigma = 1.e-1;\n  const double constant = 1.e-4;\n  const auto radius = magnitude(inertial_coords);\n  get(*gamma2) = amplitude * exp(-square(sigma * radius.get())) + constant;\n}\n\ntemplate struct InitializeConstraintDampingGammas<1>;\ntemplate struct InitializeConstraintDampingGammas<2>;\ntemplate struct InitializeConstraintDampingGammas<3>;\n}  // namespace CurvedScalarWave::Worldtube::Initialization\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/InitializeConstraintGammas.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace CurvedScalarWave::Worldtube::Initialization {\n\n/*!\n * \\ingroup InitializationGroup\n * \\brief Initializes the constraint damping parameters \\f$\\gamma_1\\f$ and\n * \\f$\\gamma_2\\f$.\n *\n * \\details The constraint damping parameters  of the CurvedScalarWave system\n * are initialized according to a Gaussian\n * \\f{align*}\n *  \\gamma_1 &= 0 \\\\\n *  \\gamma_2 &= A e^{-(\\sigma r)^2 } + c,\n * \\f}\n * where \\f$r = \\sqrt{\\delta_{ij} x^i x^j}\\f$ and with \\f$A = 10\\f$, \\f$\\sigma =\n * 10^{-1}\\f$, \\f$c = 10^{-4}\\f$. These values were experimentally found to\n * ensure a stable evolution when using the worldtube scheme.\n *\n *  DataBox changes:\n * - Adds:\n *   * `CurvedScalarWave::Tags::ConstraintGamma1`\n *   * `CurvedScalarWave::Tags::ConstraintGamma2`\n * - Removes: nothing\n * - Modifies: nothing\n */\ntemplate <size_t Dim>\nstruct InitializeConstraintDampingGammas\n    : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<CurvedScalarWave::Tags::ConstraintGamma1,\n                                 CurvedScalarWave::Tags::ConstraintGamma2>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n  using simple_tags = return_tags;\n  using compute_tags = tmpl::list<>;\n  using simple_tags_from_options = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  static void apply(\n      const gsl::not_null<Scalar<DataVector>*> gamma1,\n      const gsl::not_null<Scalar<DataVector>*> gamma2,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n};\n}  // namespace CurvedScalarWave::Worldtube::Initialization\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/InitializeCurrentIteration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace CurvedScalarWave::Worldtube::Initialization {\n\n/*!\n * \\brief Sets the initial value of `CurrentIteration to 0.\n */\nstruct InitializeCurrentIteration : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags =\n      tmpl::list<CurvedScalarWave::Worldtube::Tags::CurrentIteration>;\n  using argument_tags = tmpl::list<>;\n  using simple_tags = return_tags;\n  using compute_tags = tmpl::list<>;\n  using simple_tags_from_options = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  static void apply(const gsl::not_null<size_t*> current_iteration) {\n    *current_iteration = 0;\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Initialization\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/IteratePunctureField.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/SendToWorldtube.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n/*!\n * \\brief Computes an updated iteration of the puncture field given the current\n * acceleration of the charge sent by the worldtube singleton.\n */\nstruct IteratePunctureField {\n  static constexpr size_t Dim = 3;\n\n  using inbox_tags = tmpl::list<Tags::SelfForceInbox<Dim>>;\n  using simple_tags = tmpl::list<Tags::IteratedPunctureField<Dim>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& centered_face_coordinates =\n        db::get<Tags::FaceCoordinates<Dim, Frame::Inertial, true>>(box);\n    if (not centered_face_coordinates.has_value()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    auto& inbox = get<Tags::SelfForceInbox<Dim>>(inboxes);\n    if (not inbox.count(time_step_id)) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    db::mutate<Tags::IteratedPunctureField<Dim>>(\n        [&self_force_data = get(inbox.at(time_step_id)),\n         &position_velocity = db::get<Tags::ParticlePositionVelocity<Dim>>(box),\n         &centered_face_coordinates, charge = db::get<Tags::Charge>(box),\n         order = db::get<Tags::ExpansionOrder>(box)](\n            const auto iterated_puncture_field) {\n          tnsr::I<double, Dim> iterated_acceleration{\n              {self_force_data[0], self_force_data[1], self_force_data[2]}};\n          const size_t face_size =\n              get<0>(centered_face_coordinates.value()).size();\n\n          if (not iterated_puncture_field->has_value()) {\n            iterated_puncture_field->emplace(face_size);\n          }\n\n          puncture_field(make_not_null(&iterated_puncture_field->value()),\n                         centered_face_coordinates.value(),\n                         position_velocity[0], position_velocity[1],\n                         iterated_acceleration, 1., order);\n          Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                               ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                               ::Tags::deriv<CurvedScalarWave::Tags::Psi,\n                                             tmpl::size_t<3>, Frame::Inertial>>>\n              acceleration_terms(face_size);\n          acceleration_terms_1(\n              make_not_null(&acceleration_terms),\n              centered_face_coordinates.value(), position_velocity[0],\n              position_velocity[1], iterated_acceleration, self_force_data[3],\n              self_force_data[4], self_force_data[5], self_force_data[6],\n              self_force_data[7], self_force_data[8], self_force_data[9],\n              self_force_data[10], self_force_data[11], self_force_data[12],\n              self_force_data[13], self_force_data[14], 1.);\n          iterated_puncture_field->value() += acceleration_terms;\n          iterated_puncture_field->value() *= charge;\n        },\n        make_not_null(&box));\n    inbox.erase(time_step_id);\n    return {Parallel::AlgorithmExecution::Continue,\n            tmpl::index_of<ActionList, SendToWorldtube>::value};\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/ReceiveWorldtubeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/AlgorithmMetafunctions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n\n/*!\n * \\brief Checks if the regular field has been received from the worldtube and\n * computes the retarded field for boundary conditions.\n *\n *  \\details This action checks whether the coefficients of Taylor Series of the\n * regular field have been sent by the worldtube. If so, the series is evaluated\n * at the face coordinate in the inertial frame  and the puncture field is added\n * to it to obtain the retarded field. This is stored in \\ref\n * Tags::WorldtubeSolution which is used to formulate boundary conditions in\n * \\ref CurvedScalarWave::BoundaryConditions::Worldtube.\n */\nstruct ReceiveWorldtubeData {\n  static constexpr size_t Dim = 3;\n  using psi_tag = CurvedScalarWave::Tags::Psi;\n  using dt_psi_tag = ::Tags::dt<CurvedScalarWave::Tags::Psi>;\n  template <typename Frame>\n  using di_psi_tag =\n      ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<Dim>, Frame>;\n  using evolved_tags_list =\n      typename CurvedScalarWave::System<Dim>::variables_tag::tags_list;\n  using simple_tags = tmpl::list<Tags::WorldtubeSolution<Dim>>;\n  using inbox_tags = tmpl::list<Tags::RegularFieldInbox<Dim>>;\n  using tags_to_slice_to_face =\n      tmpl::list<gr::Tags::Shift<DataVector, Dim>, gr::Tags::Lapse<DataVector>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& element_id = db::get<domain::Tags::Element<Dim>>(box).id();\n    const auto& excision_sphere = db::get<Tags::ExcisionSphere<Dim>>(box);\n    const auto direction = excision_sphere.abutting_direction(element_id);\n    if (direction.has_value()) {\n      const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n      auto& inbox = get<Tags::RegularFieldInbox<Dim>>(inboxes);\n      if (not inbox.count(time_step_id)) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n      const auto& puncture_field =\n          db::get<Tags::MaxIterations>(box) > 1\n              ? db::get<Tags::IteratedPunctureField<Dim>>(box)\n              : db::get<Tags::PunctureField<Dim>>(box);\n      ASSERT(puncture_field.has_value(),\n             \"The puncture field should be initialized!\");\n\n      const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n      const auto face_mesh = mesh.slice_away(direction->dimension());\n      const size_t face_size = face_mesh.number_of_grid_points();\n      Variables<tags_to_slice_to_face> vars_on_face(face_size);\n      tmpl::for_each<tags_to_slice_to_face>(\n          [&box, &vars_on_face, &mesh, &direction](auto tag_to_slice_v) {\n            using tag_to_slice = typename decltype(tag_to_slice_v)::type;\n            data_on_slice(make_not_null(&get<tag_to_slice>(vars_on_face)),\n                          db::get<tag_to_slice>(box), mesh.extents(),\n                          direction.value().dimension(),\n                          index_to_slice_at(mesh.extents(), direction.value()));\n          });\n      auto& received_data = inbox.at(time_step_id);\n      const auto& centered_face_coords =\n          db::get<Tags::FaceCoordinates<Dim, Frame::Inertial, true>>(box)\n              .value();\n\n      db::mutate<Tags::WorldtubeSolution<Dim>>(\n          [&received_data, &puncture_field, &vars_on_face,\n           &centered_face_coords,\n           &expansion_order = db::get<Tags::ExpansionOrder>(box)](\n              const gsl::not_null<Variables<evolved_tags_list>*>\n                  worldtube_solution) {\n            worldtube_solution->initialize(\n                puncture_field.value().number_of_grid_points());\n\n            auto& psi = get<psi_tag>(*worldtube_solution);\n            auto& pi = get<CurvedScalarWave::Tags::Pi>(*worldtube_solution);\n            auto& phi =\n                get<CurvedScalarWave::Tags::Phi<Dim>>(*worldtube_solution);\n\n            // the puncture field plus the monopole of the regular field\n            get(psi) = get(get<psi_tag>(puncture_field.value())) +\n                       get(get<psi_tag>(received_data))[0];\n            get(pi) = get(get<dt_psi_tag>(puncture_field.value())) +\n                      get(get<dt_psi_tag>(received_data))[0];\n            for (size_t i = 0; i < Dim; ++i) {\n              phi.get(i) =\n                  get<di_psi_tag<Frame::Inertial>>(puncture_field.value())\n                      .get(i);\n            }\n            if (expansion_order > 0) {\n              // add on the dipole of the regular field\n              for (size_t i = 0; i < Dim; ++i) {\n                get(psi) += get(get<psi_tag>(received_data))[i + 1] *\n                            centered_face_coords.get(i);\n                get(pi) += get(get<dt_psi_tag>(received_data))[i + 1] *\n                           centered_face_coords.get(i);\n                phi.get(i) += get(get<psi_tag>(received_data))[i + 1];\n              }\n            }\n            const auto& shift =\n                get<gr::Tags::Shift<DataVector, Dim>>(vars_on_face);\n            const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars_on_face);\n\n            // convert dt_psi -> pi\n            get(pi) *= -1.;\n            get(pi) += get(dot_product(shift, phi));\n            get(pi) /= get(lapse);\n          },\n          make_not_null(&box));\n      inbox.erase(time_step_id);\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/SendToWorldtube.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/math/special_functions/spherical_harmonic.hpp>\n#include <cstddef>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n/*!\n * \\brief Projects the regular field \\f$\\Psi^R\\f$ and its time derivative\n * \\f$\\partial_t \\Psi^R\\f$ onto real spherical harmonics and sends the result to\n * the worldtube.\n *\n * \\details The regular field is obtained by subtracting the singular/puncture\n * field from the numerical DG field.\n * All spherical harmonics are computed for \\f$l <= n\\f$, where \\f$n\\f$ is the\n * worldtube expansion order. The projection is done by integrating over the DG\n * grid of the element face using \\ref definite_integral with the euclidean area\n * element. The worldtube adds up all integrals from the different elements to\n * obtain the integral over the entire sphere.\n *\n * DataBox:\n * - Uses:\n *    - `tags_to_slice_on_face`\n *    - `Worldtube::Tags::ExpansionOrder`\n *    - `Worldtube::Tags::FaceCoordinates<Dim, Frame::Inertial, true>`\n *    - `Worldtube::Tags::PunctureField`\n *    - `Worldtube::Tags::ExcisionSphere`\n *    - `Tags::TimeStepId`\n */\nstruct SendToWorldtube {\n  static constexpr size_t Dim = 3;\n  using tags_to_send = tmpl::list<CurvedScalarWave::Tags::Psi,\n                                  ::Tags::dt<CurvedScalarWave::Tags::Psi>>;\n  using tags_to_slice_to_face =\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<Dim>,\n                 gr::Tags::Shift<DataVector, Dim>, gr::Tags::Lapse<DataVector>,\n                 domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& element_id = db::get<domain::Tags::Element<Dim>>(box).id();\n    const auto& excision_sphere = db::get<Tags::ExcisionSphere<Dim>>(box);\n    const auto direction = excision_sphere.abutting_direction(element_id);\n    if (not direction.has_value()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    const auto face_mesh = mesh.slice_away(direction->dimension());\n    const size_t face_size = face_mesh.number_of_grid_points();\n\n    Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::TempScalar<0>, ::Tags::TempScalar<1>, ::Tags::TempScalar<2>>>\n        temporaries(face_size);\n    auto& psi_regular_times_det =\n        get(get<CurvedScalarWave::Tags::Psi>(temporaries));\n    auto& dt_psi_regular_times_det =\n        get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(temporaries));\n    auto& theta = get(get<::Tags::TempScalar<0>>(temporaries));\n    auto& phi = get(get<::Tags::TempScalar<1>>(temporaries));\n    auto& spherical_harmonic = get(get<::Tags::TempScalar<2>>(temporaries));\n    const auto& face_quantities = db::get<Tags::FaceQuantities>(box).value();\n    const auto& psi_numerical_face =\n        get<CurvedScalarWave::Tags::Psi>(face_quantities);\n    const auto& dt_psi_numerical_face =\n        get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(face_quantities);\n    const auto& area_element =\n        get<gr::surfaces::Tags::AreaElement<DataVector>>(face_quantities);\n    const auto& puncture_field =\n        db::get<Tags::CurrentIteration>(box) > 0\n            ? db::get<Tags::IteratedPunctureField<Dim>>(box).value()\n            : db::get<Tags::PunctureField<Dim>>(box).value();\n    const auto& psi_puncture = get<CurvedScalarWave::Tags::Psi>(puncture_field);\n    const auto& dt_psi_puncture =\n        get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(puncture_field);\n\n    psi_regular_times_det =\n        (get(psi_numerical_face) - get(psi_puncture)) * get(area_element);\n    dt_psi_regular_times_det =\n        (get(dt_psi_numerical_face) - get(dt_psi_puncture)) * get(area_element);\n    const auto& centered_face_coords =\n        db::get<Tags::FaceCoordinates<Dim, Frame::Inertial, true>>(box);\n    ASSERT(centered_face_coords.has_value(),\n           \"Should be an abutting element here, but face coords are not \"\n           \"calculated!\");\n    const auto& x = get<0>(centered_face_coords.value());\n    const auto& y = get<1>(centered_face_coords.value());\n    const auto& z = get<2>(centered_face_coords.value());\n\n    const size_t order = db::get<Worldtube::Tags::ExpansionOrder>(box);\n    const size_t num_modes = (order + 1) * (order + 1);\n    Variables<tags_to_send> Ylm_coefs(num_modes);\n    theta = atan2(hypot(x, y), z);\n    phi = atan2(y, x);\n    size_t index = 0;\n    // project onto spherical harmonics\n    for (size_t l = 0; l <= order; ++l) {\n      // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)\n      for (int m = -l; m <= static_cast<int>(l); ++m, ++index) {\n        spherical_harmonic = ylm::real_spherical_harmonic(theta, phi, l, m);\n        get(get<CurvedScalarWave::Tags::Psi>(Ylm_coefs)).at(index) =\n            definite_integral(psi_regular_times_det * spherical_harmonic,\n                              face_mesh);\n        get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(Ylm_coefs)).at(index) =\n            definite_integral(dt_psi_regular_times_det * spherical_harmonic,\n                              face_mesh);\n      }\n    }\n    ASSERT(index == num_modes, \"Internal indexing error. \"\n                                   << num_modes\n                                   << \" modes should have been calculated but \"\n                                   << index << \" modes were computed.\");\n\n    auto& worldtube_component = Parallel::get_parallel_component<\n        Worldtube::WorldtubeSingleton<Metavariables>>(cache);\n    Parallel::receive_data<Worldtube::Tags::SphericalHarmonicsInbox<Dim>>(\n        worldtube_component, db::get<::Tags::TimeStepId>(box),\n        std::make_pair(element_id, std::move(Ylm_coefs)));\n    if (db::get<Tags::CurrentIteration>(box) + 1 <\n        db::get<Tags::MaxIterations>(box) ) {\n      db::mutate<Tags::CurrentIteration>(\n          [](const gsl::not_null<size_t*> current_iteration) {\n            *current_iteration += 1;\n          },\n          make_not_null(&box));\n      // still iterating, go to `IteratePunctureField`\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n\n    } else {\n      db::mutate<Tags::CurrentIteration>(\n          [](const gsl::not_null<size_t*> current_iteration) {\n            *current_iteration = 0;\n          },\n          make_not_null(&box));\n      // done iterating, get data for BCs\n      return {Parallel::AlgorithmExecution::Continue,\n              tmpl::index_of<ActionList, ReceiveWorldtubeData>::value};\n    }\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iomanip>\n#include <map>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\n}  // namespace Frame\n/// \\endcond\n\nnamespace CurvedScalarWave::Worldtube::Tags {\n/*!\n * \\brief Inbox of the worldtube singleton chare which receives quantities\n * projected onto spherical harmonics.\n *\n * \\details Each element abutting the worldtube projects both $\\Psi$ and the\n * time derivative of $\\partial_t Psi$ onto spherical harmonics by integrating\n * it over the worldtube surface/boundary it is touching. These coefficients are\n * sent to this inbox.\n */\ntemplate <size_t Dim>\nstruct SphericalHarmonicsInbox\n    : Parallel::InboxInserters::Map<SphericalHarmonicsInbox<Dim>> {\n  using temporal_id = TimeStepId;\n  using tags_list = tmpl::list<CurvedScalarWave::Tags::Psi,\n                               ::Tags::dt<CurvedScalarWave::Tags::Psi>>;\n  using type =\n      std::map<temporal_id,\n               std::unordered_map<ElementId<Dim>, Variables<tags_list>>>;\n\n  static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n\n    ss << std::scientific << std::setprecision(16);\n    ss << pad << \"SphericalHarmonicsInbox:\\n\";\n    for (const auto& [current_time_step_id, element_id_and_vars] : inbox) {\n      ss << pad << \" Time: \" << current_time_step_id << \"\\n\";\n      // We don't really care about the variables, just the elements\n      for (const auto& [element_id, variables] : element_id_and_vars) {\n        (void)variables;\n        ss << pad << \"  ElementId: \" << element_id << \"\\n\";\n      }\n    }\n\n    return ss.str();\n  }\n};\n\n/*!\n * \\brief Inbox of the element chares that contains the coefficients of a Taylor\n * Series of the regular field $\\Psi^R$ as well as its time derivative. The\n * elements may evaluate the coefficients at their inertial coordinates.\n */\ntemplate <size_t Dim>\nstruct RegularFieldInbox\n    : Parallel::InboxInserters::Value<RegularFieldInbox<Dim>> {\n  using tags_to_send = tmpl::list<CurvedScalarWave::Tags::Psi,\n                                  ::Tags::dt<CurvedScalarWave::Tags::Psi>>;\n  using temporal_id = TimeStepId;\n  using type = std::map<temporal_id, Variables<tags_to_send>>;\n\n  static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n\n    ss << std::scientific << std::setprecision(16);\n    ss << pad << \"RegularFieldInbox:\\n\";\n    // We don't really care about the variables, just the times\n    for (const auto& [current_time_step_id, variables] : inbox) {\n      (void)variables;\n      ss << pad << \" Time: \" << current_time_step_id << \"\\n\";\n    }\n\n    return ss.str();\n  }\n};\n\n/*!\n * \\brief Inbox of the element chares that contains the current iteration of the\n * acceleration of the particle.\n */\ntemplate <size_t Dim>\nstruct SelfForceInbox : Parallel::InboxInserters::Value<SelfForceInbox<Dim>> {\n  using temporal_id = TimeStepId;\n  using type = std::map<temporal_id, Scalar<DataVector>>;\n    static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n    ss << std::scientific << std::setprecision(16);\n    ss << pad << \"SelfForceInbox:\\n\";\n    // We don't really care about the variables, just the times\n    for (const auto& [current_time_step_id, self_force_data] : inbox) {\n      (void)self_force_data;\n      ss << pad << \" Time: \" << current_time_step_id << \"\\n\";\n    }\n    return ss.str();\n  }\n};\n\n}  // namespace CurvedScalarWave::Worldtube::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/InitialData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ZerothOrderPuncture.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ZerothOrderPuncture.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/InitialData/ZerothOrderPuncture.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/InitialData/ZerothOrderPuncture.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace CurvedScalarWave::AnalyticData {\n\nZerothOrderPuncture::ZerothOrderPuncture(\n    const std::array<double, 3> particle_position,\n    const std::array<double, 3> particle_velocity, const double particle_charge,\n    const Options::Context& /*context*/)\n    : particle_position_(particle_position),\n      particle_velocity_(particle_velocity),\n      particle_charge_(particle_charge) {\n  const auto background_vars = kerr_schild_.variables(\n      particle_position_, 0.,\n      tmpl::list<gr::Tags::SpacetimeChristoffelSecondKind<double, 3,\n                                                          Frame::Inertial>>{});\n  const auto& christoffel =\n      get<gr::Tags::SpacetimeChristoffelSecondKind<double, 3, Frame::Inertial>>(\n          background_vars);\n  geodesic_acceleration_ =\n      gr::geodesic_acceleration(particle_velocity_, christoffel);\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nZerothOrderPuncture::get_clone() const {\n  return std::make_unique<ZerothOrderPuncture>(*this);\n}\n\nZerothOrderPuncture::ZerothOrderPuncture(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\ntuples::TaggedTuple<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                    CurvedScalarWave::Tags::Phi<3>>\nZerothOrderPuncture::variables(const tnsr::I<DataVector, 3>& x,\n                               tags /*meta*/) const {\n  auto centered_coords = x;\n  for (size_t i = 0; i < 3; ++i) {\n    centered_coords.get(i) -= particle_position_.get(i);\n  }\n  Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                       ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                       ::Tags::deriv<CurvedScalarWave::Tags::Psi,\n                                     tmpl::size_t<3>, Frame::Inertial>>>\n      puncture(get<0>(centered_coords).size());\n  CurvedScalarWave::Worldtube::puncture_field_0(\n      make_not_null(&puncture), centered_coords, particle_position_,\n      particle_velocity_, geodesic_acceleration_, 1.);\n  puncture *= particle_charge_;\n  const auto background_vars =\n      kerr_schild_.variables(x, 0.,\n                             tmpl::list<gr::Tags::Shift<DataVector, 3>,\n                                        gr::Tags::Lapse<DataVector>>{});\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(background_vars);\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(background_vars);\n  const auto shift_dot_dpsi = dot_product(\n      shift, get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                               Frame::Inertial>>(puncture));\n\n  return tuples::TaggedTuple<CurvedScalarWave::Tags::Psi,\n                             CurvedScalarWave::Tags::Pi,\n                             CurvedScalarWave::Tags::Phi<3>>{\n      get<CurvedScalarWave::Tags::Psi>(puncture),\n      (get(shift_dot_dpsi) -\n       get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(puncture))) /\n          get(lapse),\n      get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                        Frame::Inertial>>(puncture)};\n}\n\nvoid ZerothOrderPuncture::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | particle_position_;\n  p | particle_velocity_;\n  p | geodesic_acceleration_;\n  p | particle_charge_;\n  p | kerr_schild_;\n}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID ZerothOrderPuncture::my_PUP_ID = 0;\n\nbool operator==(const ZerothOrderPuncture& lhs,\n                const ZerothOrderPuncture& rhs) {\n  return lhs.particle_position_ == rhs.particle_position_ and\n         lhs.particle_velocity_ == rhs.particle_velocity_ and\n         lhs.geodesic_acceleration_ == rhs.geodesic_acceleration_ and\n         lhs.particle_charge_ == rhs.particle_charge_ and\n         lhs.kerr_schild_ == rhs.kerr_schild_;\n}\n\nbool operator!=(const ZerothOrderPuncture& lhs,\n                const ZerothOrderPuncture& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace CurvedScalarWave::AnalyticData\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/InitialData/ZerothOrderPuncture.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for a scalar point charge in Kerr-Schild\n * coordinates. This assumes the charge is initially on a geodesic orbit.\n *\n * \\details The initial data corresponds to the zeroth order puncture field\n * which is effectively the Lorentz-boosted solution of a scalar charge in flat\n * space.\n */\n\nclass ZerothOrderPuncture : public evolution::initial_data::InitialData,\n                            public MarkAsAnalyticData {\n public:\n  struct ParticlePosition {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The initial position of the scalar charge.\"};\n  };\n\n  struct ParticleVelocity {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The initial velocity of the scalar charge\"};\n  };\n\n  struct ParticleCharge {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The value of the particle's charge.\"};\n    static constexpr double lower_bound() { return 0.; }\n    static constexpr double upper_bound() { return 1.; }\n  };\n\n  using options =\n      tmpl::list<ParticlePosition, ParticleVelocity, ParticleCharge>;\n\n  static constexpr Options::String help = {\n      \"Initial data for a scalar charge in Kerr-Schild coordinates. It \"\n      \"corresponds to the zeroth order puncture field which is the \"\n      \"Lorentz-boosted solution of a scalar charge in flat space.\"};\n\n  ZerothOrderPuncture() = default;\n\n  ZerothOrderPuncture(std::array<double, 3> particle_position,\n                      std::array<double, 3> particle_velocity,\n                      double particle_charge,\n                      const Options::Context& context = {});\n  ZerothOrderPuncture(const ZerothOrderPuncture&) = default;\n  ZerothOrderPuncture& operator=(const ZerothOrderPuncture&) = default;\n  ZerothOrderPuncture(ZerothOrderPuncture&&) = default;\n  ZerothOrderPuncture& operator=(ZerothOrderPuncture&&) = default;\n  ~ZerothOrderPuncture() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit ZerothOrderPuncture(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ZerothOrderPuncture);\n  /// \\endcond\n\n  static constexpr size_t volume_dim = 3;\n  using tags =\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<3>>;\n\n  /// Retrieve the evolution variables at spatial coordinates `x`\n  tuples::TaggedTuple<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                      CurvedScalarWave::Tags::Phi<3>>\n  variables(const tnsr::I<DataVector, 3>& x, tags /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  // assume a non-spinning black hole of mass 1M centered on the coordinate\n  // origin\n  gr::Solutions::KerrSchild kerr_schild_{1., {{0., 0., 0.}}, {{0., 0., 0.}}};\n  tnsr::I<double, 3> particle_position_{\n      std::numeric_limits<double>::signaling_NaN()};\n  tnsr::I<double, 3> particle_velocity_{\n      std::numeric_limits<double>::signaling_NaN()};\n  tnsr::I<double, 3> geodesic_acceleration_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double particle_charge_{std::numeric_limits<double>::signaling_NaN()};\n\n  friend bool operator==(const ZerothOrderPuncture& lhs,\n                         const ZerothOrderPuncture& rhs);\n\n  friend bool operator!=(const ZerothOrderPuncture& lhs,\n                         const ZerothOrderPuncture& rhs);\n};\n\n}  // namespace CurvedScalarWave::AnalyticData\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  TimeStepping.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template class ChangeTimeStepperOrder<                                       \\\n      CurvedScalarWave::Worldtube::System<DIM(data)>>;                         \\\n  template class CleanHistory<CurvedScalarWave::Worldtube::System<DIM(data)>>; \\\n  template class RecordTimeStepperData<                                        \\\n      CurvedScalarWave::Worldtube::System<DIM(data)>>;                         \\\n  template class UpdateU<CurvedScalarWave::Worldtube::System<DIM(data)>,       \\\n                         false>;                                               \\\n  template class UpdateU<CurvedScalarWave::Worldtube::System<DIM(data)>, true>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/KerrSchildDerivatives.cpp",
    "content": "\n// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/KerrSchildDerivatives.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\ntnsr::iAA<double, 3> spatial_derivative_inverse_ks_metric(\n    const tnsr::I<double, 3>& pos) {\n  const double r_sq = get(dot_product(pos, pos));\n  const double r = sqrt(r_sq);\n  const double one_over_r = 1. / r;\n  const double one_over_r_2 = 1. / r_sq;\n  const double one_over_r_3 = one_over_r_2 * one_over_r;\n\n  tnsr::iAA<double, 3> di_imetric{};\n  tnsr::ii<double, 3> delta_ll{0.};\n  tnsr::Ij<double, 3> delta_ul{0.};\n  tnsr::i<double, 3> pos_lower{};\n\n  for (size_t i = 0; i < 3; ++i) {\n    delta_ll.get(i, i) = 1.;\n    delta_ul.get(i, i) = 1.;\n    pos_lower.get(i) = pos.get(i);\n  }\n\n  const auto d_imetric_ij = tenex::evaluate<ti::i, ti::J, ti::K>(\n      one_over_r_3 *\n      (6. * pos(ti::J) * pos(ti::K) * pos_lower(ti::i) * one_over_r_2 -\n       2. * delta_ul(ti::J, ti::i) * pos(ti::K) -\n       2. * delta_ul(ti::K, ti::i) * pos(ti::J)));\n  const auto d_imetric_i0 = tenex::evaluate<ti::i, ti::J>(\n      one_over_r_2 * (-4. * pos_lower(ti::i) * pos(ti::J) * one_over_r_2 +\n                      2. * delta_ul(ti::J, ti::i)));\n  const auto d_imetric_00 =\n      tenex::evaluate<ti::i>(2. * pos_lower(ti::i) * one_over_r_3);\n  for (size_t i = 0; i < 3; ++i) {\n    di_imetric.get(i, 0, 0) = d_imetric_00.get(i);\n    for (size_t j = 0; j < 3; ++j) {\n      di_imetric.get(i, j + 1, 0) = d_imetric_i0.get(i, j);\n      for (size_t k = 0; k < 3; ++k) {\n        di_imetric.get(i, j + 1, k + 1) = d_imetric_ij.get(i, j, k);\n      }\n    }\n  }\n  return di_imetric;\n}\n\ntnsr::iaa<double, 3> spatial_derivative_ks_metric(\n    const tnsr::aa<double, 3>& metric,\n    const tnsr::iAA<double, 3>& di_inverse_metric) {\n  tnsr::iaa<double, 3> di_metric{};\n  tenex::evaluate<ti::i, ti::a, ti::b>(\n      make_not_null(&di_metric), -metric(ti::a, ti::c) * metric(ti::b, ti::d) *\n                                     di_inverse_metric(ti::i, ti::C, ti::D));\n  return di_metric;\n}\n\ntnsr::iiAA<double, 3> second_spatial_derivative_inverse_ks_metric(\n    const tnsr::I<double, 3>& pos) {\n  const double r_sq = get(dot_product(pos, pos));\n  const double r = sqrt(r_sq);\n  const double one_over_r = 1. / r;\n  const double one_over_r_2 = 1. / r_sq;\n  const double one_over_r_3 = one_over_r_2 * one_over_r;\n  const double one_over_r_4 = one_over_r_2 * one_over_r_2;\n\n  tnsr::iiAA<double, 3> dij_imetric{};\n  tnsr::ii<double, 3> delta_ll{0.};\n  tnsr::Ij<double, 3> delta_ul{0.};\n  tnsr::i<double, 3> pos_lower{};\n\n  for (size_t i = 0; i < 3; ++i) {\n    delta_ll.get(i, i) = 1.;\n    delta_ul.get(i, i) = 1.;\n    pos_lower.get(i) = pos.get(i);\n  }\n\n  const auto d2_imetric_ij = tenex::evaluate<ti::i, ti::j, ti::K, ti::L>(\n      one_over_r_3 *\n      (-2. * (delta_ul(ti::L, ti::i) * delta_ul(ti::K, ti::j) +\n              delta_ul(ti::K, ti::i) * delta_ul(ti::L, ti::j)) +\n       one_over_r_2 *\n           (6. * (delta_ll(ti::i, ti::j) * pos(ti::K) * pos(ti::L) +\n                  delta_ul(ti::K, ti::i) * pos_lower(ti::j) * pos(ti::L) +\n                  delta_ul(ti::K, ti::j) * pos_lower(ti::i) * pos(ti::L) +\n                  delta_ul(ti::L, ti::i) * pos_lower(ti::j) * pos(ti::K) +\n                  delta_ul(ti::L, ti::j) * pos_lower(ti::i) * pos(ti::K)) -\n            one_over_r_2 * 30. * pos_lower(ti::i) * pos_lower(ti::j) *\n                pos(ti::K) * pos(ti::L))));\n\n  const auto d2_imetric_i0 = tenex::evaluate<ti::j, ti::k, ti::I>(\n      one_over_r_4 *\n      (-4. * (delta_ll(ti::k, ti::j) * pos(ti::I) +\n              delta_ul(ti::I, ti::k) * pos_lower(ti::j) +\n              delta_ul(ti::I, ti::j) * pos_lower(ti::k)) +\n       one_over_r_2 * 16. * pos(ti::I) * pos_lower(ti::j) * pos_lower(ti::k)));\n  const auto d2_imetric_00 = tenex::evaluate<ti::i, ti::j>(\n      one_over_r_3 * (2. * delta_ll(ti::i, ti::j) -\n                      one_over_r_2 * 6. * pos_lower(ti::i) * pos_lower(ti::j)));\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      dij_imetric.get(i, j, 0, 0) = d2_imetric_00.get(i, j);\n      for (size_t k = 0; k < 3; ++k) {\n        dij_imetric.get(i, j, k + 1, 0) = d2_imetric_i0.get(i, j, k);\n        for (size_t l = 0; l < 3; ++l) {\n          dij_imetric.get(i, j, k + 1, l + 1) = d2_imetric_ij.get(i, j, k, l);\n        }\n      }\n    }\n  }\n  return dij_imetric;\n}\n\ntnsr::iiaa<double, 3> second_spatial_derivative_metric(\n    const tnsr::aa<double, 3>& metric, const tnsr::iaa<double, 3>& di_metric,\n    const tnsr::iAA<double, 3>& di_inverse_metric,\n    const tnsr::iiAA<double, 3>& dij_inverse_metric) {\n  tnsr::iiaa<double, 3> dij_metric{};\n  tenex::evaluate<ti::j, ti::i, ti::a, ti::b>(\n      make_not_null(&dij_metric),\n      -metric(ti::a, ti::c) * metric(ti::b, ti::d) *\n              dij_inverse_metric(ti::j, ti::i, ti::C, ti::D) -\n          2. * metric(ti::a, ti::c) * di_metric(ti::j, ti::b, ti::d) *\n              di_inverse_metric(ti::i, ti::C, ti::D));\n  return dij_metric;\n}\n\ntnsr::iAbb<double, 3> spatial_derivative_christoffel(\n    const tnsr::iaa<double, 3>& di_metric,\n    const tnsr::iiaa<double, 3>& dij_metric,\n    const tnsr::AA<double, 3>& inverse_metric,\n    const tnsr::iAA<double, 3>& di_inverse_metric) {\n  tnsr::iAbb<double, 3> di_christoffel{};\n  tnsr::abb<double, 3> d_metric{};\n  tnsr::iabb<double, 3> di_d_metric{};\n  for (size_t a = 0; a <= 3; ++a) {\n    for (size_t b = 0; b <= 3; ++b) {\n      d_metric.get(0, a, b) = 0.;\n      for (size_t i = 0; i < 3; ++i) {\n        d_metric.get(i + 1, a, b) = di_metric.get(i, a, b);\n        di_d_metric.get(i, 0, a, b) = 0.;\n        for (size_t j = 0; j < 3; ++j) {\n          di_d_metric.get(i, j + 1, a, b) = dij_metric.get(i, j, a, b);\n        }\n      }\n    }\n  }\n  tenex::evaluate<ti::i, ti::A, ti::b, ti::c>(\n      make_not_null(&di_christoffel),\n      0.5 * di_inverse_metric(ti::i, ti::A, ti::D) *\n              (d_metric(ti::b, ti::c, ti::d) + d_metric(ti::c, ti::b, ti::d) -\n               d_metric(ti::d, ti::b, ti::c)) +\n          0.5 * inverse_metric(ti::A, ti::D) *\n              (di_d_metric(ti::i, ti::b, ti::c, ti::d) +\n               di_d_metric(ti::i, ti::c, ti::b, ti::d) -\n               di_d_metric(ti::i, ti::d, ti::b, ti::c)));\n  return di_christoffel;\n}\n\ntnsr::iA<double, 3> spatial_derivative_ks_contracted_christoffel(\n    const tnsr::I<double, 3>& pos) {\n  const double r_sq = get(dot_product(pos, pos));\n  const double r = sqrt(r_sq);\n  const double one_over_r = 1. / r;\n  const double one_over_r_2 = 1. / r_sq;\n  const double one_over_r_3 = cube(one_over_r);\n  const double one_over_r_4 = square(one_over_r_2);\n  const double one_over_r_5 = one_over_r_4 * one_over_r;\n\n  tnsr::iA<double, 3> di_contracted_christoffel{};\n  for (size_t i = 0; i < 3; ++i) {\n    di_contracted_christoffel.get(i, 0) = 4. * pos.get(i) * one_over_r_4;\n    for (size_t j = 0; j < 3; ++j) {\n      di_contracted_christoffel.get(i, j + 1) =\n          -6. * pos.get(i) * pos.get(j) * one_over_r_5;\n    }\n    di_contracted_christoffel.get(i, i + 1) += 2. * one_over_r_3;\n  }\n  return di_contracted_christoffel;\n}\n\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/KerrSchildDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n/*!\n * \\brief The spatial derivative of the zero spin inverse Kerr Schild metric,\n * $\\partial_i g^{\\mu \\nu}$, assuming a black hole at the coordinate center with\n * mass M = 1.\n */\ntnsr::iAA<double, 3> spatial_derivative_inverse_ks_metric(\n    const tnsr::I<double, 3>& pos);\n\n/*!\n * \\brief The spatial derivative of the spacetime metric,\n * $\\partial_i g_{\\mu \\nu}$.\n */\ntnsr::iaa<double, 3> spatial_derivative_ks_metric(\n    const tnsr::aa<double, 3>& metric,\n    const tnsr::iAA<double, 3>& di_inverse_metric);\n\n/*!\n * \\brief The second spatial derivative of the zero spin inverse Kerr Schild\n * metric, $\\partial_i \\partial_j g^{\\mu \\nu}$, assuming a black hole at the\n * coordinate center with mass M = 1.\n */\ntnsr::iiAA<double, 3> second_spatial_derivative_inverse_ks_metric(\n    const tnsr::I<double, 3>& pos);\n\n/*!\n * \\brief The spatial derivative of the spacetime metric,\n * $\\partial_i \\partial_j g_{\\mu \\nu}$.\n */\ntnsr::iiaa<double, 3> second_spatial_derivative_metric(\n    const tnsr::aa<double, 3>& metric, const tnsr::iaa<double, 3>& di_metric,\n    const tnsr::iAA<double, 3>& di_inverse_metric,\n    const tnsr::iiAA<double, 3>& dij_inverse_metric);\n\n/*!\n * \\brief The spatial derivative of the Christoffel\n * symbols, $\\partial_i \\Gamma^\\rho_{\\mu \\nu}$.\n */\ntnsr::iAbb<double, 3> spatial_derivative_christoffel(\n    const tnsr::iaa<double, 3>& di_metric,\n    const tnsr::iiaa<double, 3>& dij_metric,\n    const tnsr::AA<double, 3>& inverse_metric,\n    const tnsr::iAA<double, 3>& di_inverse_metric);\n\n/*!\n * \\brief The spatial derivative of the zero spin Kerr Schild contracted\n * Christoffel symbols,\n * $\\partial_i g^{\\mu \\nu} \\Gamma^\\rho_{\\mu \\nu}$, assuming a black hole at the\n * coordinate center with mass M = 1.\n */\ntnsr::iA<double, 3> spatial_derivative_ks_contracted_christoffel(\n    const tnsr::I<double, 3>& pos);\n\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicBuffer.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\nvoid puncture_field(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, const double bh_mass,\n    const size_t order) {\n  if (order == 0) {\n    puncture_field_0(result, centered_coords, particle_position,\n                     particle_velocity, particle_acceleration, bh_mass);\n  } else if (order == 1) {\n    puncture_field_1(result, centered_coords, particle_position,\n                     particle_velocity, particle_acceleration, bh_mass);\n  } else {\n    ERROR(\n        \"The puncture field is only implemented up to expansion order 1 but \"\n        \"you requested order \"\n        << order);\n  }\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n/*!\n * \\brief Computes the puncture/singular field \\f$\\Psi^\\mathcal{P}\\f$ of a\n * scalar charge on a generic orbit in Schwarzschild spacetime.\n * described in \\cite Detweiler2003.\n *\n * \\details The field is computed using a Detweiler-Whiting singular\n * Green's function and perturbatively expanded in the geodesic distance from\n * the particle. It solves the inhomogeneous wave equation\n *\n * \\f{align*}{\n * \\Box \\Psi^\\mathcal{P} = -4 \\pi q \\int \\sqrt{-g} \\delta^4(x^i, z(\\tau)) d \\tau\n * \\f}\n *\n * where \\f$q\\f$ is the scalar charge and \\f$z(\\tau)\\f$ is the worldline of the\n * particle. The expression is expanded up to a certain order in geodesic\n * distance and transformed to Kerr-Schild coordinates.\n *\n * The function given here assumes that the particle has scalar charge \\f$q=1\\f$\n * and is on a fixed geodesic orbit. It returns the\n * singular field at the requested coordinates as well as its time and spatial\n * derivative. For non-geodesic orbits, corresponding acceleration terms have to\n * be added to the puncture field.\n *\n * \\note The expressions were computed with Mathematica and optimized by\n * applying common subexpression elimination with sympy. The memory allocations\n * of temporaries were optimized manually.\n */\nvoid puncture_field(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, double bh_mass,\n    size_t order);\n\n/*!\n * \\brief Computes the puncture/singular field \\f$\\Psi^\\mathcal{P}\\f$ of a\n * scalar charge on a generic orbit in Schwarzschild spacetime.\n * described in \\cite Detweiler2003.\n *\n * \\details The appropriate expression can be found in Eq. (36) of\n * \\cite Wittek:2024gxn. For non-geodesic orbits, there are\n * additional contributions, see `acceleration_terms_0`.\n */\nvoid puncture_field_0(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, double bh_mass);\n\n/*!\n * \\brief Computes the puncture/singular field \\f$\\Psi^\\mathcal{P}\\f$ of a\n * scalar charge on a generic orbit in Schwarzschild spacetime.\n * described in \\cite Detweiler2003.\n *\n * \\details For non-geodesic orbits, there are additional contributions, see\n * `acceleration_terms_0`.\n */\nvoid puncture_field_1(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, double bh_mass);\n\n/*!\n * \\brief Computes the acceleration terms of a puncture/singular field\n * \\f$\\Psi^\\mathcal{P}\\f$ of a scalar charge on a generic orbit in Schwarzschild\n * spacetime up to zeroth order in coordinate distance.\n * \\details The appropriate expression can be found in Eq. (37) of\n * \\cite Wittek:2024gxn. The values ft, fx, fy are the time, x and y component\n * of the self force per unit mass evaluated at the position of the particle;\n * dt_ft, dt_fx, dt_fy are the respective total time derivatives. The code in\n * this function was auto-generated by generating the full expressions with\n * Mathematica and employing common subexpression elimination with sympy. The\n * mathematica file and generating script can be found at\n * https://github.com/nikwit/puncture-field.\n */\nvoid acceleration_terms_0(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, double ft, double fx,\n    double fy, double dt_ft, double dt_fx, double dt_fy, double bh_mass);\n\n/*!\n * \\brief Computes the acceleration terms of a puncture/singular field\n * \\f$\\Psi^\\mathcal{P}\\f$ of a scalar charge on a generic orbit in Schwarzschild\n * spacetime up to first order in coordinate distance (i.e. zeroth and first\n * order).\n * \\details The appropriate expression can be found in Eq. (37) of\n * \\cite Wittek:2024gxn. The values ft, fx, fy are the time, x and y component\n * of the self force per unit mass evaluated at the position of the particle;\n * dt_ft, dt_fx, dt_fy are the respective total time derivatives. The code in\n * this function was auto-generated by generating the full expressions with\n * Mathematica and employing common subexpression elimination with sympy. The\n * mathematica file and generating script can be found at\n * https://github.com/nikwit/puncture-field.\n *\n */\nvoid acceleration_terms_1(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, double ft, double fx,\n    double fy, double dt_ft, double dt_fx, double dt_fy, double Du_ft,\n    double Du_fx, double Du_fy, double dt_Du_ft, double dt_Du_fx,\n    double dt_Du_fy, double bh_mass);\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/PunctureFieldOrder0.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicBuffer.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\nvoid puncture_field_0(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, const double bh_mass) {\n  const size_t grid_size = get<0>(centered_coords).size();\n  result->initialize(grid_size);\n  const double xp = particle_position[0];\n  const double yp = particle_position[1];\n  const double xpdot = particle_velocity[0];\n  const double ypdot = particle_velocity[1];\n  const double xpddot = particle_acceleration[0];\n  const double ypddot = particle_acceleration[1];\n  const double rp = get(magnitude(particle_position));\n  const double rpdot = (xp * xpdot + yp * ypdot) / rp;\n\n  const auto& Dx = get<0>(centered_coords);\n  const auto& Dy = get<1>(centered_coords);\n  // the particle is fixed in the xy-plane, so Dz = z\n  const auto& z = get<2>(centered_coords);\n\n  const double M = bh_mass;\n\n  DynamicBuffer<DataVector> temps(43, grid_size);\n\n  const double d_0 = rp * rp * rp;\n  const double d_1 = 2.0 * M;\n  const double d_2 = yp * ypdot;\n  const double d_3 = d_2 + xp * xpdot;\n  const double d_4 = 1.0 / d_0;\n  const double d_5 = rp * rp;\n  const double d_6 = 4.0 * rp;\n  const double d_7 = M * d_3;\n  const double d_8 = d_3 * d_3;\n  const double d_9 = xpdot * xpdot;\n  const double d_10 = ypdot * ypdot;\n  const double d_11 = d_10 + d_9;\n  const double d_12 = d_0 * (d_11 - 1.0) + d_1 * d_5 + d_1 * d_8 + d_6 * d_7;\n  const double d_13 = 1.0 / d_12;\n  const double d_14 = d_13 * d_4;\n  const double d_15 = xp * xp;\n  const double d_16 = yp * yp;\n  const double d_17 = rp * rp * rp * rp * rp;\n  const double d_18 = 1.0 / d_17;\n  const double d_19 = 2.0 * rp;\n  const double d_20 = d_13 * d_18;\n  const double d_21 = d_17 * sqrt(rp);\n  const double d_22 = -d_12;\n  const double d_23 = d_22 * sqrt(d_22);\n  const double d_24 = sqrt(d_22);\n  const double d_25 = 1. / (rp * sqrt(rp));\n  const double d_26 = d_24 * d_25 * rpdot;\n  const double d_27 = d_1 * xp;\n  const double d_28 = d_0 * xpdot + d_27 * d_3 + d_27 * rp;\n  const double d_29 = 2.0 * yp;\n  const double d_30 = M * d_29 * rp + d_0 * ypdot + d_29 * d_7;\n  const double d_31 = rp * rp * rp * rp;\n  const double d_32 = 1.0 / d_31;\n  const double d_33 = 3.0 * rpdot;\n  const double d_34 = M * d_33;\n  const double d_35 = 1.0 / d_5;\n  const double d_36 = xp * xpddot;\n  const double d_37 = yp * ypddot;\n  const double d_38 = 2.0 * (d_10 + d_36 + d_37 + d_9) - rpdot;\n  const double d_39 = 2.0 * rpdot;\n  const double d_40 = d_11 + d_36 + d_37;\n  const double d_41 = d_1 * d_3 * d_4 * (-d_39 + d_40) - d_32 * d_34 * d_8 +\n                      xpddot * xpdot + ypddot * ypdot;\n  const double d_42 = 1. / sqrt(rp);\n  const double d_43 = d_24 * d_42;\n  const double d_44 = 3.0 * M;\n  const double d_45 = d_23 * d_42;\n  const double d_46 = 2.0 * d_12 * d_17;\n  DataVector& dv_0 = temps.at(0);\n  dv_0 = Dx * Dx;\n  DataVector& dv_1 = temps.at(1);\n  dv_1 = Dy * ypdot;\n  DataVector& dv_2 = temps.at(2);\n  dv_2 = Dx * xpdot + dv_1;\n  DataVector& dv_3 = temps.at(3);\n  dv_3 = Dx * xp;\n  DataVector& dv_4 = temps.at(4);\n  dv_4 = Dy * yp;\n  DataVector& dv_5 = temps.at(5);\n  dv_5 = dv_3 + dv_4;\n  DataVector& dv_6 = temps.at(6);\n  dv_6 = d_1 * dv_5;\n  DataVector& dv_7 = temps.at(7);\n  dv_7 = dv_6 * rp;\n  DataVector& dv_8 = temps.at(8);\n  dv_8 = d_0 * dv_2 + d_3 * dv_6 + dv_7;\n  DataVector& dv_9 = temps.at(9);\n  dv_9 = dv_8 * dv_8;\n  DataVector& dv_10 = temps.at(10);\n  dv_10 = d_14 * dv_9;\n  DataVector& dv_11 = temps.at(11);\n  dv_11 = dv_5 * dv_5;\n  DataVector& dv_12 = temps.at(12);\n  dv_12 = d_4 * dv_11;\n  DataVector& dv_13 = temps.at(13);\n  dv_13 = Dy * Dy;\n  DataVector& dv_14 = temps.at(14);\n  dv_14 = z * z;\n  DataVector& dv_15 = temps.at(15);\n  dv_15 = dv_13 + dv_14;\n  DataVector& dv_16 = temps.at(16);\n  dv_16 = d_1 * dv_12 + dv_0 - dv_10 + dv_15;\n  DataVector& dv_17 = temps.at(17);\n  dv_17 = 2.0 * dv_0;\n  DataVector& dv_18 = temps.at(18);\n  dv_18 = dv_3 * dv_4;\n  DataVector& dv_19 = temps.at(19);\n  dv_19 = -dv_0;\n  DataVector& dv_20 = temps.at(20);\n  dv_20 = 2.0 * dv_14;\n  DataVector& dv_21 = temps.at(21);\n  dv_21 = 2.0 * dv_13 + dv_20;\n  DataVector& dv_22 = temps.at(22);\n  dv_22 = dv_19 + dv_21;\n  DataVector& dv_23 = temps.at(13);\n  dv_23 = -dv_13;\n  DataVector& dv_24 = temps.at(20);\n  dv_24 = dv_17 + dv_20 + dv_23;\n  DataVector& dv_25 = temps.at(23);\n  dv_25 = d_15 * dv_22 + d_16 * dv_24 - 6.0 * dv_18;\n  DataVector& dv_26 = temps.at(24);\n  dv_26 = M * dv_5;\n  DataVector& dv_27 = temps.at(19);\n  dv_27 = dv_15 + dv_19;\n  DataVector& dv_28 = temps.at(0);\n  dv_28 = dv_0 + dv_14 + dv_23;\n  DataVector& dv_29 = temps.at(18);\n  dv_29 = d_15 * dv_27 + d_16 * dv_28 - 4.0 * dv_18;\n  DataVector& dv_30 = temps.at(13);\n  dv_30 = d_19 * dv_29 + d_3 * dv_25;\n  DataVector& dv_31 = temps.at(14);\n  dv_31 = dv_30 * dv_8;\n  DataVector& dv_32 = temps.at(15);\n  dv_32 = M * dv_31;\n  DataVector& dv_33 = temps.at(25);\n  dv_33 = -dv_25;\n  DataVector& dv_34 = temps.at(26);\n  dv_34 = d_12 * dv_5;\n  DataVector& dv_35 = temps.at(27);\n  dv_35 = dv_33 * dv_34;\n  DataVector& dv_36 = temps.at(28);\n  dv_36 = dv_31 + dv_35;\n  DataVector& dv_37 = temps.at(29);\n  dv_37 = sqrt(dv_16);\n  DataVector& dv_38 = temps.at(30);\n  dv_38 = M * dv_37;\n  DataVector& dv_39 = temps.at(31);\n  dv_39 = d_14 * dv_8;\n  DataVector& dv_40 = temps.at(32);\n  dv_40 = Dx - d_28 * dv_39 + d_4 * dv_6 * xp;\n  DataVector& dv_41 = temps.at(31);\n  dv_41 = Dy + d_29 * d_4 * dv_26 - d_30 * dv_39;\n  DataVector& dv_42 = temps.at(33);\n  dv_42 = Dx * xpddot + Dy * ypddot;\n  DataVector& dv_43 = temps.at(34);\n  dv_43 = d_1 * dv_2;\n  DataVector& dv_44 = temps.at(6);\n  dv_44 = d_33 * d_5 * dv_2 + d_39 * dv_26 + d_40 * dv_6;\n  DataVector& dv_45 = temps.at(34);\n  dv_45 = d_32 * (-d_13 * d_33 * dv_9 +\n                  d_13 * dv_8 * rp *\n                      (d_0 * dv_42 + d_3 * dv_43 + dv_43 * rp + dv_44) +\n                  d_31 * dv_9 * (-M * d_35 * d_38 - d_41) / (d_12 * d_12) +\n                  d_34 * dv_11 - dv_2 * dv_7) +\n          dv_40 * xpdot + dv_41 * ypdot;\n  DataVector& dv_46 = temps.at(2);\n  dv_46 = d_44 * dv_36 / dv_37;\n  DataVector& dv_47 = temps.at(1);\n  dv_47 = -d_2 + dv_1 + xpdot * (Dx - xp);\n  DataVector& dv_48 = temps.at(7);\n  dv_48 = Dy * d_16;\n  DataVector& dv_49 = temps.at(11);\n  dv_49 = 2.0 * Dy;\n  DataVector& dv_50 = temps.at(9);\n  dv_50 = -d_15 * dv_49 + 3.0 * dv_3 * yp + dv_48;\n  DataVector& dv_51 = temps.at(35);\n  dv_51 = 3.0 * Dy;\n  DataVector& dv_52 = temps.at(36);\n  dv_52 = Dx * d_15;\n  DataVector& dv_53 = temps.at(37);\n  dv_53 = Dx * yp;\n  DataVector& dv_54 = temps.at(38);\n  dv_54 = 3.0 * dv_4;\n  DataVector& dv_55 = temps.at(35);\n  dv_55 =\n      2.0 * (dv_50 * ypdot +\n             xpdot * (dv_52 - dv_53 * (d_29 + dv_51) + xp * (dv_22 + dv_54)) -\n             ypdot * (-dv_24 * yp + dv_3 * dv_51));\n  DataVector& dv_56 = temps.at(22);\n  dv_56 = d_1 * dv_47;\n  DataVector& dv_57 = temps.at(7);\n  dv_57 = -Dy * d_15 + d_29 * dv_3 + dv_48;\n  DataVector& dv_58 = temps.at(4);\n  dv_58 = 2.0 * dv_4;\n  DataVector& dv_59 = temps.at(20);\n  dv_59 = 0.5 / (dv_16 * dv_16);\n  DataVector& dv_60 = temps.at(39);\n  dv_60 = d_46 * dv_37;\n  DataVector& dv_61 = temps.at(40);\n  dv_61 = Dx * d_16;\n  DataVector& dv_62 = temps.at(38);\n  dv_62 = dv_52 + dv_54 * xp - 2.0 * dv_61;\n  DataVector& dv_63 = temps.at(41);\n  dv_63 = 2.0 * dv_34;\n  DataVector& dv_64 = temps.at(25);\n  dv_64 = d_12 * dv_33;\n  DataVector& dv_65 = temps.at(42);\n  dv_65 = d_20 * dv_59;\n\n  get(get<CurvedScalarWave::Tags::Psi>(*result)) =\n      0.5 *\n      (4.0 * M * dv_12 - d_18 * dv_25 * dv_26 + d_20 * dv_32 - 2.0 * dv_10 +\n       dv_17 + dv_21) /\n      (dv_16 * sqrt(dv_16));\n  get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(*result)) =\n      -dv_59 / d_21 / d_23 *\n      (-2.0 * d_21 * d_23 * dv_37 * dv_45 - 7.0 * d_26 * dv_36 * dv_38 +\n       d_43 * dv_45 * dv_46 +\n       dv_38 * (d_23 * d_25 * d_39 * dv_25 * dv_5 - d_26 * dv_31 +\n                d_43 * dv_30 *\n                    (d_0 * (-d_10 - d_9 + dv_42) + d_3 * dv_56 + dv_44 +\n                     dv_56 * rp) +\n                d_43 * dv_8 *\n                    (d_3 * dv_55 + d_39 * dv_29 + d_40 * dv_25 +\n                     d_6 * (dv_57 * ypdot +\n                            xpdot * (dv_52 - dv_53 * (dv_49 + yp) +\n                                     xp * (dv_27 + dv_58)) -\n                            ypdot * (-dv_28 * yp + dv_3 * dv_49))) +\n                d_45 * dv_25 * dv_47 + d_45 * dv_5 * dv_55 +\n                2.0 * dv_31 * square(rp) * sqrt(rp) * (M * d_35 * d_38 + d_41) /\n                    d_24));\n  get<0>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -dv_65 *\n      (dv_38 *\n           (-d_28 * dv_30 - dv_62 * dv_63 - dv_64 * xp +\n            2.0 * dv_8 * (d_19 * (dv_52 + dv_58 * xp - dv_61) + d_3 * dv_62)) +\n       dv_40 * dv_46 + dv_40 * dv_60);\n  get<1>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -dv_65 * (dv_38 * (-d_30 * dv_30 - dv_50 * dv_63 - dv_64 * yp +\n                         2.0 * dv_8 * (d_19 * dv_57 + d_3 * dv_50)) +\n                dv_41 * dv_46 + dv_41 * dv_60);\n  get<2>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      0.5 * d_20 * z *\n      (4.0 * M * dv_16 * (d_15 + d_16) * (-dv_34 + dv_8 * (d_3 + rp)) -\n       d_44 * dv_35 - d_46 * dv_16 - 3.0 * dv_32) /\n      (square(dv_16) * sqrt(dv_16));\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/PunctureFieldOrder1.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicBuffer.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\n// NOLINTNEXTLINE(google-readability-function-size, readability-function-size)\nvoid puncture_field_1(\n    gsl::not_null<Variables<tmpl::list<\n        CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n        ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                      Frame::Inertial>>>*>\n        result,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& centered_coords,\n    const tnsr::I<double, 3>& particle_position,\n    const tnsr::I<double, 3>& particle_velocity,\n    const tnsr::I<double, 3>& particle_acceleration, const double bh_mass) {\n  const size_t grid_size = get<0>(centered_coords).size();\n  result->initialize(grid_size);\n  const double xp = particle_position[0];\n  const double yp = particle_position[1];\n  const double xpdot = particle_velocity[0];\n  const double ypdot = particle_velocity[1];\n  const double xpddot = particle_acceleration[0];\n  const double ypddot = particle_acceleration[1];\n  const double rp = get(magnitude(particle_position));\n  const double rpdot = (xp * xpdot + yp * ypdot) / rp;\n\n  const auto& Dx = get<0>(centered_coords);\n  const auto& Dy = get<1>(centered_coords);\n  // the particle is fixed in the xy-plane, so Dz = z\n  const auto& z = get<2>(centered_coords);\n\n  const double M = bh_mass;\n\n  DynamicBuffer<DataVector> temps(320, grid_size);\n\n  const double d_0 = rp * rp * rp;\n  const double d_1 = 2.0 * M;\n  const double d_2 = xp * xpdot;\n  const double d_3 = yp * ypdot;\n  const double d_4 = d_2 + d_3;\n  const double d_5 = 1.0 / d_0;\n  const double d_6 = rp * rp;\n  const double d_7 = 4.0 * M;\n  const double d_8 = d_4 * d_4;\n  const double d_9 = ypdot * ypdot;\n  const double d_10 = xpdot * xpdot;\n  const double d_11 = d_10 - 1;\n  const double d_12 =\n      d_0 * (d_11 + d_9) + d_1 * d_6 + d_1 * d_8 + d_4 * d_7 * rp;\n  const double d_13 = 1.0 / d_12;\n  const double d_14 = d_13 * d_5;\n  const double d_15 = rp * rp * rp * rp * rp;\n  const double d_16 = 1.0 / d_15;\n  const double d_17 = xp * xp;\n  const double d_18 = yp * yp;\n  const double d_19 = 2.0 * rp;\n  const double d_20 = rp * rp * rp * rp * rp * rp * rp * rp * rp * rp;\n  const double d_21 = 1.0 / d_20;\n  const double d_22 = rp * rp * rp * rp * rp * rp * rp;\n  const double d_23 = 1.0 / (d_12 * d_12);\n  const double d_24 = rp * rp * rp * rp * rp * rp;\n  const double d_25 = rp * rp * rp * rp;\n  const double d_26 = xp * yp;\n  const double d_27 = 2.0 * xpdot;\n  const double d_28 = d_27 * ypdot;\n  const double d_29 = rp * rp * rp * rp * rp * rp * rp * rp * rp;\n  const double d_30 = M * d_13;\n  const double d_31 = 2.0 * yp;\n  const double d_32 = M * d_31;\n  const double d_33 = d_6 * ypdot;\n  const double d_34 = 3.0 * d_3;\n  const double d_35 = 3.0 * yp;\n  const double d_36 = d_1 * ypdot;\n  const double d_37 = -d_35 + d_36;\n  const double d_38 = 8.0 * d_30;\n  const double d_39 = rp * rp * rp * rp * rp * rp * rp * rp;\n  const double d_40 = M * M;\n  const double d_41 = 2.0 * d_40;\n  const double d_42 = 3.0 * d_30 * d_6;\n  const double d_43 = yp * yp * yp;\n  const double d_44 = xp * xp * xp;\n  const double d_45 = xp * xp * xp * xp;\n  const double d_46 = yp * yp * yp * yp;\n  const double d_47 = d_17 * d_18;\n  const double d_48 = 4.0 * d_6;\n  const double d_49 = d_17 * yp;\n  const double d_50 = M * rp;\n  const double d_51 = 2.0 * xp;\n  const double d_52 = d_18 * xp;\n  const double d_53 = 3.0 * d_44;\n  const double d_54 = 8.0 * d_6;\n  const double d_55 = 6.0 * d_25;\n  const double d_56 = M * d_0;\n  const double d_57 = d_0 * d_13;\n  const double d_58 = 1.0 / d_6;\n  const double d_59 = 1.0 / d_24;\n  const double d_60 = M * d_4;\n  const double d_61 = d_51 * d_60;\n  const double d_62 = d_0 * xpdot + d_50 * d_51 + d_61;\n  const double d_63 = d_32 * d_4;\n  const double d_64 = d_0 * ypdot + d_32 * rp + d_63;\n  const double d_65 = 1.0 / d_25;\n  const double d_66 = 3.0 * rpdot;\n  const double d_67 = ypddot * ypdot;\n  const double d_68 = xp * xpddot;\n  const double d_69 = yp * ypddot;\n  const double d_70 = 2.0 * d_9;\n  const double d_71 = 2.0 * rpdot;\n  const double d_72 = d_10 + d_9;\n  const double d_73 = d_68 + d_69 + d_72;\n  const double d_74 = d_1 * d_4;\n  const double d_75 =\n      -M * d_58 * (-2 * d_10 - 2.0 * d_68 - 2.0 * d_69 - d_70 + rpdot) -\n      3.0 * M * d_65 * d_8 * rpdot + d_5 * d_74 * (-d_71 + d_73) + d_67 +\n      xpddot * xpdot;\n  const double d_76 = -d_75;\n  const double d_77 = d_6 * d_66;\n  const double d_78 = M * d_16;\n  const double d_79 = 36.0 * d_78;\n  const double d_80 = -yp;\n  const double d_81 = -d_9;\n  const double d_82 = 4.0 * rp;\n  const double d_83 = d_20 * rp;\n  const double d_84 = 3.0 * M;\n  const double d_85 = 12.0 * d_15;\n  const double d_86 = d_23 * d_76;\n  const double d_87 = d_18 * ypddot;\n  const double d_88 = d_9 + 1;\n  const double d_89 = d_71 * rp;\n  const double d_90 = 2.0 * d_18;\n  const double d_91 = ypdot * ypdot * ypdot;\n  const double d_92 = M * d_69;\n  const double d_93 = M * d_9;\n  const double d_94 = d_18 * d_67;\n  const double d_95 = d_18 * d_9;\n  const double d_96 = 8.0 * d_29;\n  const double d_97 = 2.0 * d_6;\n  const double d_98 = 6.0 * M;\n  const double d_99 = d_0 * rpdot;\n  const double d_100 = d_13 * d_25;\n  const double d_101 = 4.0 * yp;\n  const double d_102 = rp * rpdot;\n  const double d_103 = 2.0 * ypdot;\n  const double d_104 = 3.0 * d_52;\n  const double d_105 = 16.0 * d_40;\n  const double d_106 = d_105 * d_14;\n  const double d_107 = M * d_58;\n  const double d_108 = 8.0 * d_107;\n  const double d_109 = 1.0 / rp;\n  const double d_110 = d_21 * d_40;\n  const double d_111 = 45.0 * d_110;\n  const double d_112 = 1.0 / d_29;\n  const double d_113 = d_112 * d_84;\n  const double d_114 = d_1 * d_25;\n  const double d_115 = d_17 + d_18;\n  const double d_116 = d_10 * (d_17 - d_90) + 2.0 * d_2 * (d_1 + d_34) -\n                       ypdot * (d_103 * d_17 - yp * (d_3 + d_7));\n  const double d_117 = d_4 + rp;\n  DataVector& dv_0 = temps.at(0);\n  dv_0 = Dx * xpdot;\n  DataVector& dv_1 = temps.at(1);\n  dv_1 = Dy * ypdot;\n  DataVector& dv_2 = temps.at(2);\n  dv_2 = dv_0 + dv_1;\n  DataVector& dv_3 = temps.at(3);\n  dv_3 = d_0 * dv_2;\n  DataVector& dv_4 = temps.at(4);\n  dv_4 = Dx * xp;\n  DataVector& dv_5 = temps.at(5);\n  dv_5 = Dy * yp;\n  DataVector& dv_6 = temps.at(6);\n  dv_6 = dv_4 + dv_5;\n  DataVector& dv_7 = temps.at(7);\n  dv_7 = d_1 * dv_6;\n  DataVector& dv_8 = temps.at(8);\n  dv_8 = d_4 * dv_7;\n  DataVector& dv_9 = temps.at(9);\n  dv_9 = dv_3 + dv_7 * rp + dv_8;\n  DataVector& dv_10 = temps.at(10);\n  dv_10 = dv_9 * dv_9;\n  DataVector& dv_11 = temps.at(11);\n  dv_11 = d_14 * dv_10;\n  DataVector& dv_12 = temps.at(12);\n  dv_12 = dv_6 * dv_6;\n  DataVector& dv_13 = temps.at(13);\n  dv_13 = d_1 * dv_12;\n  DataVector& dv_14 = temps.at(14);\n  dv_14 = Dx * Dx;\n  DataVector& dv_15 = temps.at(15);\n  dv_15 = Dy * Dy;\n  DataVector& dv_16 = temps.at(16);\n  dv_16 = z * z;\n  DataVector& dv_17 = temps.at(17);\n  dv_17 = dv_15 + dv_16;\n  DataVector& dv_18 = temps.at(18);\n  dv_18 = dv_14 + dv_17;\n  DataVector& dv_19 = temps.at(19);\n  dv_19 = d_5 * dv_13 - dv_11 + dv_18;\n  DataVector& dv_20 = temps.at(20);\n  dv_20 = sqrt(dv_19);\n  DataVector& dv_21 = temps.at(21);\n  dv_21 = 1.0 / dv_20;\n  DataVector& dv_22 = temps.at(22);\n  dv_22 = 6.0 * dv_5;\n  DataVector& dv_23 = temps.at(23);\n  dv_23 = dv_22 * dv_4;\n  DataVector& dv_24 = temps.at(24);\n  dv_24 = -dv_14;\n  DataVector& dv_25 = temps.at(25);\n  dv_25 = 2.0 * dv_15;\n  DataVector& dv_26 = temps.at(26);\n  dv_26 = 2.0 * dv_16;\n  DataVector& dv_27 = temps.at(27);\n  dv_27 = dv_25 + dv_26;\n  DataVector& dv_28 = temps.at(28);\n  dv_28 = dv_24 + dv_27;\n  DataVector& dv_29 = temps.at(29);\n  dv_29 = -dv_15;\n  DataVector& dv_30 = temps.at(30);\n  dv_30 = 2.0 * dv_14;\n  DataVector& dv_31 = temps.at(31);\n  dv_31 = dv_26 + dv_30;\n  DataVector& dv_32 = temps.at(32);\n  dv_32 = dv_29 + dv_31;\n  DataVector& dv_33 = temps.at(33);\n  dv_33 = d_17 * dv_28 + d_18 * dv_32 - dv_23;\n  DataVector& dv_34 = temps.at(34);\n  dv_34 = dv_33 * dv_6;\n  DataVector& dv_35 = temps.at(35);\n  dv_35 = 4.0 * dv_5;\n  DataVector& dv_36 = temps.at(36);\n  dv_36 = dv_17 + dv_24;\n  DataVector& dv_37 = temps.at(37);\n  dv_37 = dv_14 + dv_16;\n  DataVector& dv_38 = temps.at(38);\n  dv_38 = dv_29 + dv_37;\n  DataVector& dv_39 = temps.at(39);\n  dv_39 = d_17 * dv_36 + d_18 * dv_38 - dv_35 * dv_4;\n  DataVector& dv_40 = temps.at(40);\n  dv_40 = d_19 * dv_39;\n  DataVector& dv_41 = temps.at(41);\n  dv_41 = -dv_33;\n  DataVector& dv_42 = temps.at(42);\n  dv_42 = -d_4 * dv_41 + dv_40;\n  DataVector& dv_43 = temps.at(43);\n  dv_43 = d_13 * dv_42 * dv_9 - dv_34;\n  DataVector& dv_44 = temps.at(44);\n  dv_44 = 1.0 / dv_19;\n  DataVector& dv_45 = temps.at(45);\n  dv_45 = M * dv_44;\n  DataVector& dv_46 = temps.at(46);\n  dv_46 = 4.0 * dv_16;\n  DataVector& dv_47 = temps.at(47);\n  dv_47 = d_23 * dv_10;\n  DataVector& dv_48 = temps.at(48);\n  dv_48 = d_22 * dv_47;\n  DataVector& dv_49 = temps.at(49);\n  dv_49 = 8.0 * dv_16;\n  DataVector& dv_50 = temps.at(50);\n  dv_50 = M * dv_47;\n  DataVector& dv_51 = temps.at(51);\n  dv_51 = d_24 * dv_50;\n  DataVector& dv_52 = temps.at(52);\n  dv_52 = Dx * Dy;\n  DataVector& dv_53 = temps.at(53);\n  dv_53 = -d_26 * dv_16 + d_6 * dv_52;\n  DataVector& dv_54 = temps.at(54);\n  dv_54 = Dx * yp;\n  DataVector& dv_55 = temps.at(55);\n  dv_55 = Dy * xp;\n  DataVector& dv_56 = temps.at(56);\n  dv_56 = d_17 * dv_17;\n  DataVector& dv_57 = temps.at(57);\n  dv_57 = d_18 * dv_15 + dv_56;\n  DataVector& dv_58 = temps.at(58);\n  dv_58 = d_17 * dv_14;\n  DataVector& dv_59 = temps.at(59);\n  dv_59 = d_10 * dv_57 + d_9 * (d_18 * dv_37 + dv_58) + square(dv_54 - dv_55);\n  DataVector& dv_60 = temps.at(60);\n  dv_60 = -d_28 * dv_53 + dv_59;\n  DataVector& dv_61 = temps.at(61);\n  dv_61 = 8.0 * dv_47;\n  DataVector& dv_62 = temps.at(62);\n  dv_62 = M * dv_61;\n  DataVector& dv_63 = temps.at(63);\n  dv_63 = d_13 * dv_19;\n  DataVector& dv_64 = temps.at(64);\n  dv_64 = d_20 * dv_63;\n  DataVector& dv_65 = temps.at(65);\n  dv_65 = d_30 * dv_19;\n  DataVector& dv_66 = temps.at(66);\n  dv_66 = d_29 * dv_65;\n  DataVector& dv_67 = temps.at(67);\n  dv_67 = -dv_26;\n  DataVector& dv_68 = temps.at(68);\n  dv_68 = dv_14 + dv_67;\n  DataVector& dv_69 = temps.at(69);\n  dv_69 = dv_29 + dv_30;\n  DataVector& dv_70 = temps.at(70);\n  dv_70 = d_9 * dv_68 + dv_69;\n  DataVector& dv_71 = temps.at(71);\n  dv_71 = -dv_25;\n  DataVector& dv_72 = temps.at(72);\n  dv_72 = d_9 * dv_37;\n  DataVector& dv_73 = temps.at(73);\n  dv_73 = dv_72 * yp;\n  DataVector& dv_74 = temps.at(74);\n  dv_74 = yp * (d_7 * dv_37 * ypdot + dv_73 - yp * (dv_14 + dv_71));\n  DataVector& dv_75 = temps.at(56);\n  dv_75 = d_18 * (dv_15 + dv_67) + dv_56;\n  DataVector& dv_76 = temps.at(75);\n  dv_76 = d_10 * dv_75;\n  DataVector& dv_77 = temps.at(52);\n  dv_77 = dv_52 * (d_32 + d_33) - xp * (d_1 * dv_17 + d_34 * dv_16);\n  DataVector& dv_78 = temps.at(76);\n  dv_78 = d_27 * dv_77;\n  DataVector& dv_79 = temps.at(77);\n  dv_79 = 2.0 * Dy;\n  DataVector& dv_80 = temps.at(78);\n  dv_80 = dv_4 * dv_79;\n  DataVector& dv_81 = temps.at(79);\n  dv_81 = d_37 * dv_80;\n  DataVector& dv_82 = temps.at(80);\n  dv_82 = -dv_74 - dv_76 + dv_78 + dv_81;\n  DataVector& dv_83 = temps.at(81);\n  dv_83 = -d_17 * dv_70 + dv_82;\n  DataVector& dv_84 = temps.at(82);\n  dv_84 = 4.0 * dv_83;\n  DataVector& dv_85 = temps.at(83);\n  dv_85 = d_15 * dv_47;\n  DataVector& dv_86 = temps.at(84);\n  dv_86 = d_39 * dv_63;\n  DataVector& dv_87 = temps.at(85);\n  dv_87 = dv_41 * dv_6;\n  DataVector& dv_88 = temps.at(86);\n  dv_88 = -dv_42;\n  DataVector& dv_89 = temps.at(87);\n  dv_89 = d_13 * dv_9;\n  DataVector& dv_90 = temps.at(88);\n  dv_90 = dv_88 * dv_89;\n  DataVector& dv_91 = temps.at(89);\n  dv_91 = dv_87 - dv_90;\n  DataVector& dv_92 = temps.at(90);\n  dv_92 = dv_91 * dv_91;\n  DataVector& dv_93 = temps.at(91);\n  dv_93 = dv_6 * dv_6 * dv_6 * dv_6;\n  DataVector& dv_94 = temps.at(92);\n  dv_94 = d_41 * dv_93;\n  DataVector& dv_95 = temps.at(93);\n  dv_95 = 3.0 * dv_14;\n  DataVector& dv_96 = temps.at(94);\n  dv_96 = -dv_95;\n  DataVector& dv_97 = temps.at(95);\n  dv_97 = 4.0 * dv_15;\n  DataVector& dv_98 = temps.at(96);\n  dv_98 = dv_46 + dv_97;\n  DataVector& dv_99 = temps.at(97);\n  dv_99 = dv_96 + dv_98;\n  DataVector& dv_100 = temps.at(98);\n  dv_100 = 3.0 * dv_15;\n  DataVector& dv_101 = temps.at(99);\n  dv_101 = -dv_100;\n  DataVector& dv_102 = temps.at(100);\n  dv_102 = 4.0 * dv_14;\n  DataVector& dv_103 = temps.at(101);\n  dv_103 = dv_102 + dv_46;\n  DataVector& dv_104 = temps.at(102);\n  dv_104 = dv_101 + dv_103;\n  DataVector& dv_105 = temps.at(103);\n  dv_105 = -14 * Dx * Dy * xp * yp + d_17 * dv_99 + d_18 * dv_104;\n  DataVector& dv_106 = temps.at(104);\n  dv_106 = M * dv_12;\n  DataVector& dv_107 = temps.at(105);\n  dv_107 = dv_106 * rp;\n  DataVector& dv_108 = temps.at(106);\n  dv_108 = Dy * dv_38;\n  DataVector& dv_109 = temps.at(107);\n  dv_109 = 30.0 * d_43 * dv_108 * dv_4;\n  DataVector& dv_110 = temps.at(108);\n  dv_110 = Dx * d_44;\n  DataVector& dv_111 = temps.at(109);\n  dv_111 = dv_110 * dv_5;\n  DataVector& dv_112 = temps.at(110);\n  dv_112 = 30.0 * dv_111;\n  DataVector& dv_113 = temps.at(111);\n  dv_113 = dv_112 * dv_36;\n  DataVector& dv_114 = temps.at(112);\n  dv_114 = Dx * Dx * Dx * Dx;\n  DataVector& dv_115 = temps.at(113);\n  dv_115 = 2.0 * dv_114;\n  DataVector& dv_116 = temps.at(114);\n  dv_116 = 11.0 * dv_14;\n  DataVector& dv_117 = temps.at(115);\n  dv_117 = dv_115 - dv_116 * dv_17 + 2.0 * (dv_17 * dv_17);\n  DataVector& dv_118 = temps.at(116);\n  dv_118 = 11.0 * dv_15;\n  DataVector& dv_119 = temps.at(117);\n  dv_119 = -dv_46;\n  DataVector& dv_120 = temps.at(118);\n  dv_120 = dv_118 + dv_119;\n  DataVector& dv_121 = temps.at(119);\n  dv_121 = Dy * Dy * Dy * Dy;\n  DataVector& dv_122 = temps.at(120);\n  dv_122 = z * z * z * z;\n  DataVector& dv_123 = temps.at(113);\n  dv_123 = dv_115 - dv_118 * dv_16 + 2.0 * dv_121 + 2.0 * dv_122;\n  DataVector& dv_124 = temps.at(121);\n  dv_124 = 68.0 * dv_15;\n  DataVector& dv_125 = temps.at(122);\n  dv_125 = 7.0 * dv_16;\n  DataVector& dv_126 = temps.at(123);\n  dv_126 = dv_124 - dv_125;\n  DataVector& dv_127 = temps.at(120);\n  dv_127 = 4.0 * dv_122;\n  DataVector& dv_128 = temps.at(124);\n  dv_128 = 11.0 * dv_114 + 11.0 * dv_121 + dv_125 * dv_15 - dv_127;\n  DataVector& dv_129 = temps.at(125);\n  dv_129 = -dv_126 * dv_14 + dv_128;\n  DataVector& dv_130 = temps.at(126);\n  dv_130 = -d_45 * dv_117 - d_46 * (-dv_120 * dv_14 + dv_123) + d_47 * dv_129 +\n           dv_109 + dv_113;\n  DataVector& dv_131 = temps.at(127);\n  dv_131 = Dy * d_45;\n  DataVector& dv_132 = temps.at(128);\n  dv_132 = 5.0 * dv_14;\n  DataVector& dv_133 = temps.at(129);\n  dv_133 = 5.0 * dv_16;\n  DataVector& dv_134 = temps.at(130);\n  dv_134 = dv_132 + dv_133;\n  DataVector& dv_135 = temps.at(131);\n  dv_135 = dv_134 + dv_29;\n  DataVector& dv_136 = temps.at(132);\n  dv_136 = d_46 * dv_79;\n  DataVector& dv_137 = temps.at(133);\n  dv_137 = 6.0 * dv_15;\n  DataVector& dv_138 = temps.at(134);\n  dv_138 = dv_137 + dv_24;\n  DataVector& dv_139 = temps.at(135);\n  dv_139 = dv_138 + dv_46;\n  DataVector& dv_140 = temps.at(136);\n  dv_140 = d_35 * dv_110;\n  DataVector& dv_141 = temps.at(137);\n  dv_141 = -34 * dv_14 + dv_49;\n  DataVector& dv_142 = temps.at(138);\n  dv_142 = dv_118 + dv_141;\n  DataVector& dv_143 = temps.at(139);\n  dv_143 = Dy * d_18;\n  DataVector& dv_144 = temps.at(140);\n  dv_144 = d_17 * dv_143;\n  DataVector& dv_145 = temps.at(141);\n  dv_145 = -9 * dv_15;\n  DataVector& dv_146 = temps.at(142);\n  dv_146 = dv_103 + dv_145;\n  DataVector& dv_147 = temps.at(143);\n  dv_147 = 3.0 * dv_4;\n  DataVector& dv_148 = temps.at(144);\n  dv_148 = d_43 * dv_146 * dv_147 + dv_142 * dv_144;\n  DataVector& dv_149 = temps.at(145);\n  dv_149 = dv_6 * dv_6 * dv_6;\n  DataVector& dv_150 = temps.at(146);\n  dv_150 = d_40 * dv_149;\n  DataVector& dv_151 = temps.at(147);\n  dv_151 = d_31 * dv_150;\n  DataVector& dv_152 = temps.at(148);\n  dv_152 = dv_143 * dv_4;\n  DataVector& dv_153 = temps.at(149);\n  dv_153 = dv_101 + dv_31;\n  DataVector& dv_154 = temps.at(150);\n  dv_154 = -dv_132 + dv_26 + dv_97;\n  DataVector& dv_155 = temps.at(151);\n  dv_155 = d_43 * dv_153 + d_49 * dv_154 + dv_110 * dv_79 - 12.0 * dv_152;\n  DataVector& dv_156 = temps.at(152);\n  dv_156 = d_50 * dv_6;\n  DataVector& dv_157 = temps.at(153);\n  dv_157 = dv_155 * dv_156;\n  DataVector& dv_158 = temps.at(154);\n  dv_158 = -dv_151 + dv_157;\n  DataVector& dv_159 = temps.at(155);\n  dv_159 = d_51 * dv_150;\n  DataVector& dv_160 = temps.at(156);\n  dv_160 = Dx * d_43;\n  DataVector& dv_161 = temps.at(157);\n  dv_161 = dv_160 * dv_79;\n  DataVector& dv_162 = temps.at(158);\n  dv_162 = Dx * d_17;\n  DataVector& dv_163 = temps.at(159);\n  dv_163 = 12.0 * dv_5;\n  DataVector& dv_164 = temps.at(160);\n  dv_164 = dv_162 * dv_163;\n  DataVector& dv_165 = temps.at(161);\n  dv_165 = dv_27 + dv_96;\n  DataVector& dv_166 = temps.at(162);\n  dv_166 = 5.0 * dv_15;\n  DataVector& dv_167 = temps.at(163);\n  dv_167 = dv_102 - dv_166 + dv_26;\n  DataVector& dv_168 = temps.at(164);\n  dv_168 = -d_44 * dv_165 - d_52 * dv_167 - dv_161 + dv_164;\n  DataVector& dv_169 = temps.at(129);\n  dv_169 = dv_133 + dv_166;\n  DataVector& dv_170 = temps.at(162);\n  dv_170 = 2.0 * Dx;\n  DataVector& dv_171 = temps.at(165);\n  dv_171 = d_45 * dv_170;\n  DataVector& dv_172 = temps.at(166);\n  dv_172 = Dx * d_46;\n  DataVector& dv_173 = temps.at(167);\n  dv_173 = 6.0 * dv_14;\n  DataVector& dv_174 = temps.at(168);\n  dv_174 = dv_173 + dv_29;\n  DataVector& dv_175 = temps.at(169);\n  dv_175 = -dv_174 - dv_46;\n  DataVector& dv_176 = temps.at(170);\n  dv_176 = d_43 * dv_55;\n  DataVector& dv_177 = temps.at(171);\n  dv_177 = -9 * dv_14;\n  DataVector& dv_178 = temps.at(172);\n  dv_178 = dv_177 + dv_98;\n  DataVector& dv_179 = temps.at(173);\n  dv_179 = -dv_178;\n  DataVector& dv_180 = temps.at(174);\n  dv_180 = -34 * dv_15 + dv_49;\n  DataVector& dv_181 = temps.at(175);\n  dv_181 = dv_116 + dv_180;\n  DataVector& dv_182 = temps.at(176);\n  dv_182 = d_18 * dv_162;\n  DataVector& dv_183 = temps.at(177);\n  dv_183 = d_53 * dv_179 * dv_5 + dv_171 * (-dv_169 - dv_24) + dv_172 * dv_32 +\n           3.0 * dv_175 * dv_176 - dv_181 * dv_182;\n  DataVector& dv_184 = temps.at(178);\n  dv_184 = d_6 * dv_183 + dv_156 * dv_168 + dv_159;\n  DataVector& dv_185 = temps.at(179);\n  dv_185 = d_40 * dv_12;\n  DataVector& dv_186 = temps.at(13);\n  dv_186 = dv_13 * rp;\n  DataVector& dv_187 = temps.at(180);\n  dv_187 = d_54 * dv_12 - d_55 * dv_18 - d_56 * dv_18 + dv_185 + dv_186;\n  DataVector& dv_188 = temps.at(181);\n  dv_188 = d_19 * dv_6;\n  DataVector& dv_189 = temps.at(182);\n  dv_189 = dv_184 * xpdot + dv_187 * dv_188;\n  DataVector& dv_190 = temps.at(183);\n  dv_190 = dv_189 - ypdot * (d_6 * (-dv_131 * dv_28 + dv_135 * dv_136 +\n                                    dv_139 * dv_140 + dv_148) +\n                             dv_158);\n  DataVector& dv_191 = temps.at(184);\n  dv_191 = -d_57 * dv_190;\n  DataVector& dv_192 = temps.at(8);\n  dv_192 = d_5 * dv_8 + d_58 * dv_7 + dv_2;\n  DataVector& dv_193 = temps.at(185);\n  dv_193 = 4.0 * dv_192;\n  DataVector& dv_194 = temps.at(186);\n  dv_194 = -d_42 * dv_88 * dv_88 - d_48 * dv_130 - dv_105 * dv_107 +\n           dv_191 * dv_193 + dv_94;\n  DataVector& dv_195 = temps.at(82);\n  dv_195 = d_22 * d_38 * dv_19 * dv_60 + d_25 * dv_60 * dv_62 + dv_194 * rp -\n           9.0 * dv_45 * dv_92 - dv_46 * dv_48 - dv_46 * dv_64 + dv_49 * dv_51 +\n           dv_49 * dv_66 - dv_84 * dv_85 - dv_84 * dv_86;\n  DataVector& dv_196 = temps.at(187);\n  dv_196 = M * dv_20;\n  DataVector& dv_197 = temps.at(188);\n  dv_197 = d_59 * dv_196;\n  DataVector& dv_198 = temps.at(189);\n  dv_198 = 72.0 * dv_91;\n  DataVector& dv_199 = temps.at(190);\n  dv_199 = M * dv_6;\n  DataVector& dv_200 = temps.at(191);\n  dv_200 = d_14 * dv_9;\n  DataVector& dv_201 = temps.at(192);\n  dv_201 = Dx + d_5 * d_51 * dv_199 - d_62 * dv_200;\n  DataVector& dv_202 = temps.at(193);\n  dv_202 = d_5 * dv_6;\n  DataVector& dv_203 = temps.at(194);\n  dv_203 = Dy + d_32 * dv_202 - d_64 * dv_200;\n  DataVector& dv_204 = temps.at(195);\n  dv_204 = d_66 * dv_106;\n  DataVector& dv_205 = temps.at(196);\n  dv_205 = d_76 * dv_47;\n  DataVector& dv_206 = temps.at(197);\n  dv_206 = Dx * xpddot;\n  DataVector& dv_207 = temps.at(198);\n  dv_207 = Dy * ypddot;\n  DataVector& dv_208 = temps.at(199);\n  dv_208 = dv_206 + dv_207;\n  DataVector& dv_209 = temps.at(200);\n  dv_209 = d_1 * dv_2;\n  DataVector& dv_210 = temps.at(201);\n  dv_210 = d_71 * dv_199 + d_73 * dv_7 + d_77 * dv_2;\n  DataVector& dv_211 = temps.at(202);\n  dv_211 = dv_89 * rp;\n  DataVector& dv_212 = temps.at(200);\n  dv_212 =\n      -d_65 * (2.0 * M * dv_2 * dv_6 * rp + 3.0 * d_13 * dv_10 * rpdot -\n               d_25 * dv_205 - dv_204 -\n               dv_211 * (d_0 * dv_208 + d_4 * dv_209 + dv_209 * rp + dv_210)) +\n      dv_201 * xpdot + dv_203 * ypdot;\n  DataVector& dv_213 = temps.at(203);\n  dv_213 = 24.0 * dv_20;\n  DataVector& dv_214 = temps.at(204);\n  dv_214 = dv_21 * dv_212;\n  DataVector& dv_215 = temps.at(205);\n  dv_215 = Dx - xp;\n  DataVector& dv_216 = temps.at(206);\n  dv_216 = Dy + d_80;\n  DataVector& dv_217 = temps.at(207);\n  dv_217 = dv_215 * xpdot + dv_216 * ypdot;\n  DataVector& dv_218 = temps.at(208);\n  dv_218 = dv_217 * rp;\n  DataVector& dv_219 = temps.at(209);\n  dv_219 = 3.0 * Dy;\n  DataVector& dv_220 = temps.at(210);\n  dv_220 = 3.0 * dv_5;\n  DataVector& dv_221 = temps.at(211);\n  dv_221 = 3.0 * Dx;\n  DataVector& dv_222 = temps.at(212);\n  dv_222 = xpdot * (dv_162 - dv_54 * (d_31 + dv_219) + xp * (dv_220 + dv_28)) +\n           ypdot * (dv_143 - dv_55 * (d_51 + dv_221) + yp * (dv_147 + dv_32));\n  DataVector& dv_223 = temps.at(213);\n  dv_223 = dv_188 * dv_222;\n  DataVector& dv_224 = temps.at(199);\n  dv_224 = -d_10 + d_81 + dv_208;\n  DataVector& dv_225 = temps.at(201);\n  dv_225 = d_1 * dv_218 + d_74 * dv_217 + dv_210;\n  DataVector& dv_226 = temps.at(214);\n  dv_226 = d_13 * dv_88;\n  DataVector& dv_227 = temps.at(215);\n  dv_227 = 2.0 * d_25 * dv_9;\n  DataVector& dv_228 = temps.at(216);\n  dv_228 = dv_79 + yp;\n  DataVector& dv_229 = temps.at(217);\n  dv_229 = 2.0 * dv_5;\n  DataVector& dv_230 = temps.at(212);\n  dv_230 = 2.0 * d_4 * dv_222 +\n           d_82 * (xpdot * (dv_162 - dv_228 * dv_54 + xp * (dv_229 + dv_36)) +\n                   ypdot * (dv_143 - dv_55 * (dv_170 + xp) +\n                            yp * (dv_38 + 2.0 * dv_4)));\n  DataVector& dv_231 = temps.at(218);\n  dv_231 = -dv_195;\n  DataVector& dv_232 = temps.at(219);\n  dv_232 = dv_196 * 1.0 / d_83;\n  DataVector& dv_233 = temps.at(220);\n  dv_233 = 20.0 * dv_16;\n  DataVector& dv_234 = temps.at(221);\n  dv_234 = dv_16 * rpdot;\n  DataVector& dv_235 = temps.at(53);\n  dv_235 = -dv_53;\n  DataVector& dv_236 = temps.at(59);\n  dv_236 = d_28 * dv_235 + dv_59;\n  DataVector& dv_237 = temps.at(222);\n  dv_237 = 16.0 * dv_236;\n  DataVector& dv_238 = temps.at(223);\n  dv_238 = d_76 * dv_10 * 1.0 / (d_12 * d_12 * d_12);\n  DataVector& dv_239 = temps.at(224);\n  dv_239 = 16.0 * dv_238;\n  DataVector& dv_240 = temps.at(223);\n  dv_240 = 32.0 * M * dv_238;\n  DataVector& dv_241 = temps.at(225);\n  dv_241 = d_0 * dv_224 + dv_225;\n  DataVector& dv_242 = temps.at(226);\n  dv_242 = d_23 * dv_9;\n  DataVector& dv_243 = temps.at(227);\n  dv_243 = dv_241 * dv_242;\n  DataVector& dv_244 = temps.at(228);\n  dv_244 = 16.0 * dv_16;\n  DataVector& dv_245 = temps.at(229);\n  dv_245 = M * dv_243;\n  DataVector& dv_246 = temps.at(80);\n  dv_246 = d_17 * (-d_9 * dv_68 + dv_15 - dv_30) + dv_82;\n  DataVector& dv_247 = temps.at(68);\n  dv_247 = dv_246 * rpdot;\n  DataVector& dv_248 = temps.at(230);\n  dv_248 = d_86 * dv_19;\n  DataVector& dv_249 = temps.at(231);\n  dv_249 = M * dv_248;\n  DataVector& dv_250 = temps.at(232);\n  dv_250 = dv_17 * xp * (xpdot * xpdot * xpdot);\n  DataVector& dv_251 = temps.at(233);\n  dv_251 = ypdot * (Dy * (-d_17 + d_6) - dv_143 + dv_17 * yp);\n  DataVector& dv_252 = temps.at(234);\n  dv_252 = dv_4 * yp;\n  DataVector& dv_253 = temps.at(235);\n  dv_253 = dv_14 * yp;\n  DataVector& dv_254 = temps.at(236);\n  dv_254 = Dy * dv_4;\n  DataVector& dv_255 = temps.at(237);\n  dv_255 = Dy * d_17;\n  DataVector& dv_256 = temps.at(238);\n  dv_256 = -dv_255;\n  DataVector& dv_257 = temps.at(239);\n  dv_257 = d_9 * dv_162;\n  DataVector& dv_258 = temps.at(240);\n  dv_258 = -d_6 * (-d_81 - dv_207) + d_89 * dv_1;\n  DataVector& dv_259 = temps.at(73);\n  dv_259 =\n      16.0 *\n      (d_10 * dv_251 + dv_250 +\n       xpdot * (Dx * (-dv_258 - yp * (Dy + d_88 * yp)) - dv_257 +\n                dv_57 * xpddot + xp * (Dy * (Dy + yp) + d_69 * dv_16 + dv_72)) +\n       ypdot * (d_87 * dv_14 + d_87 * dv_16 + dv_235 * xpddot + dv_252 +\n                dv_253 - dv_254 + dv_256 + dv_58 * ypddot + dv_73));\n  DataVector& dv_260 = temps.at(57);\n  dv_260 = 8.0 * dv_19;\n  DataVector& dv_261 = temps.at(53);\n  dv_261 = dv_162 * (d_9 + 2.0);\n  DataVector& dv_262 = temps.at(241);\n  dv_262 = dv_219 + yp;\n  DataVector& dv_263 = temps.at(69);\n  dv_263 =\n      M * d_70 * dv_4 - d_1 * dv_207 * dv_4 +\n      d_10 * (d_1 * (dv_17 + dv_5) + dv_251) - d_17 * d_67 * dv_26 +\n      d_17 * dv_1 - d_3 * dv_14 - d_3 * dv_147 + d_3 * dv_25 + d_67 * dv_58 -\n      d_90 * dv_1 + d_91 * dv_16 * yp + d_91 * dv_253 + d_92 * dv_26 +\n      d_92 * dv_30 + d_93 * dv_26 + d_93 * dv_30 + d_94 * dv_14 + d_94 * dv_16 +\n      dv_1 * dv_147 + dv_250 - dv_77 * xpddot +\n      xpdot *\n          (Dx * (-d_36 * dv_228 - d_95 - dv_258 + dv_262 * yp) - dv_261 +\n           dv_75 * xpddot +\n           xp * (-d_1 * dv_1 + d_35 * dv_16 * ypddot - dv_220 + dv_69 + dv_72));\n  DataVector& dv_264 = temps.at(143);\n  dv_264 = -dv_212;\n  DataVector& dv_265 = temps.at(235);\n  dv_265 = d_13 * dv_264;\n  DataVector& dv_266 = temps.at(72);\n  dv_266 = d_30 * dv_264;\n  DataVector& dv_267 = temps.at(240);\n  dv_267 = -dv_39;\n  DataVector& dv_268 = temps.at(56);\n  dv_268 = -dv_28;\n  DataVector& dv_269 = temps.at(23);\n  dv_269 = d_17 * dv_268 - d_18 * dv_32 + dv_23;\n  DataVector& dv_270 = temps.at(32);\n  dv_270 = d_19 * dv_267 + d_4 * dv_269;\n  DataVector& dv_271 = temps.at(52);\n  dv_271 = dv_270 * dv_270;\n  DataVector& dv_272 = temps.at(131);\n  dv_272 = -dv_135;\n  DataVector& dv_273 = temps.at(144);\n  dv_273 = dv_131 * dv_268 - dv_136 * dv_272 + dv_139 * dv_140 + dv_148;\n  DataVector& dv_274 = temps.at(154);\n  dv_274 = d_6 * dv_273 + dv_158;\n  DataVector& dv_275 = temps.at(182);\n  dv_275 = -dv_189 + dv_274 * ypdot;\n  DataVector& dv_276 = temps.at(132);\n  dv_276 = d_57 * dv_193;\n  DataVector& dv_277 = temps.at(136);\n  dv_277 = dv_4 * dv_5;\n  DataVector& dv_278 = temps.at(56);\n  dv_278 = -d_17 * dv_99 - d_18 * dv_104 + 14.0 * dv_277;\n  DataVector& dv_279 = temps.at(115);\n  dv_279 = d_45 * dv_117 + d_46 * (-dv_120 * dv_14 + dv_123) - d_47 * dv_129 -\n           dv_109 - dv_113;\n  DataVector& dv_280 = temps.at(92);\n  dv_280 = d_48 * dv_279 + dv_107 * dv_278 + dv_94;\n  DataVector& dv_281 = temps.at(107);\n  dv_281 = -d_42 * dv_271 + dv_275 * dv_276 + dv_280;\n  DataVector& dv_282 = temps.at(111);\n  dv_282 = dv_19 * dv_19;\n  DataVector& dv_283 = temps.at(118);\n  dv_283 = 1.0 / dv_282;\n  DataVector& dv_284 = temps.at(125);\n  dv_284 = dv_269 * dv_6 - dv_270 * dv_89;\n  DataVector& dv_285 = temps.at(205);\n  dv_285 = -dv_215 * xpdot - dv_216 * ypdot;\n  DataVector& dv_286 = temps.at(113);\n  dv_286 = d_97 * dv_281 * dv_44;\n  DataVector& dv_287 = temps.at(135);\n  dv_287 = d_13 * dv_270;\n  DataVector& dv_288 = temps.at(240);\n  dv_288 = d_73 * dv_269 - dv_230 + 2.0 * dv_267 * rpdot;\n  DataVector& dv_289 = temps.at(232);\n  dv_289 = d_1 * dv_285;\n  DataVector& dv_290 = temps.at(233);\n  dv_290 = -dv_102;\n  DataVector& dv_291 = temps.at(58);\n  dv_291 = 11.0 * dv_16;\n  DataVector& dv_292 = temps.at(216);\n  dv_292 = Dx * d_45;\n  DataVector& dv_293 = temps.at(242);\n  dv_293 = dv_292 * (dv_118 + dv_290 + dv_291);\n  DataVector& dv_294 = temps.at(116);\n  dv_294 = dv_103 - dv_118;\n  DataVector& dv_295 = temps.at(36);\n  dv_295 = Dy * dv_36;\n  DataVector& dv_296 = temps.at(121);\n  dv_296 = -dv_124 + dv_125;\n  DataVector& dv_297 = temps.at(243);\n  dv_297 = 22.0 * dv_14 + dv_296;\n  DataVector& dv_298 = temps.at(244);\n  dv_298 = d_17 * dv_54;\n  DataVector& dv_299 = temps.at(112);\n  dv_299 = 4.0 * dv_114;\n  DataVector& dv_300 = temps.at(245);\n  dv_300 = 45.0 * dv_5;\n  DataVector& dv_301 = temps.at(246);\n  dv_301 = 22.0 * dv_15;\n  DataVector& dv_302 = temps.at(247);\n  dv_302 = 15.0 * dv_5;\n  DataVector& dv_303 = temps.at(248);\n  dv_303 = -dv_116 + dv_98;\n  DataVector& dv_304 = temps.at(24);\n  dv_304 = dv_100 + dv_16 + dv_24;\n  DataVector& dv_305 = temps.at(249);\n  dv_305 = dv_101 + dv_37;\n  DataVector& dv_306 = temps.at(250);\n  dv_306 = 15.0 * dv_4;\n  DataVector& dv_307 = temps.at(251);\n  dv_307 = Dy * Dy * Dy;\n  DataVector& dv_308 = temps.at(252);\n  dv_308 = 68.0 * dv_5;\n  DataVector& dv_309 = temps.at(253);\n  dv_309 = dv_217 * dv_6;\n  DataVector& dv_310 = temps.at(145);\n  dv_310 = d_41 * dv_149;\n  DataVector& dv_311 = temps.at(179);\n  dv_311 = 6.0 * dv_185;\n  DataVector& dv_312 = temps.at(254);\n  dv_312 = dv_199 * rpdot;\n  DataVector& dv_313 = temps.at(255);\n  dv_313 = M * dv_218;\n  DataVector& dv_314 = temps.at(256);\n  dv_314 = Dx * ypdot;\n  DataVector& dv_315 = temps.at(257);\n  dv_315 = Dy * xpdot;\n  DataVector& dv_316 = temps.at(258);\n  dv_316 = -dv_97;\n  DataVector& dv_317 = temps.at(134);\n  dv_317 = -ypdot * (dv_138 + dv_26);\n  DataVector& dv_318 = temps.at(259);\n  dv_318 = 12.0 * dv_14;\n  DataVector& dv_319 = temps.at(260);\n  dv_319 = 12.0 * dv_16;\n  DataVector& dv_320 = temps.at(261);\n  dv_320 = 4.0 * Dy;\n  DataVector& dv_321 = temps.at(130);\n  dv_321 = dv_101 + dv_134;\n  DataVector& dv_322 = temps.at(262);\n  dv_322 = dv_137 + dv_46 + dv_96;\n  DataVector& dv_323 = temps.at(137);\n  dv_323 = dv_141 + 33.0 * dv_15;\n  DataVector& dv_324 = temps.at(138);\n  dv_324 = dv_142 * dv_79;\n  DataVector& dv_325 = temps.at(263);\n  dv_325 = dv_145 + dv_318 + dv_46;\n  DataVector& dv_326 = temps.at(264);\n  dv_326 = Dx * d_18;\n  DataVector& dv_327 = temps.at(265);\n  dv_327 = 12.0 * dv_15;\n  DataVector& dv_328 = temps.at(266);\n  dv_328 = dv_177 + dv_327 + dv_46;\n  DataVector& dv_329 = temps.at(169);\n  dv_329 = dv_175 * dv_219;\n  DataVector& dv_330 = temps.at(99);\n  dv_330 = yp * (dv_101 + dv_173 + dv_46);\n  DataVector& dv_331 = temps.at(129);\n  dv_331 = 2.0 * d_45 * (dv_169 + dv_96);\n  DataVector& dv_332 = temps.at(168);\n  dv_332 = dv_174 + dv_26;\n  DataVector& dv_333 = temps.at(174);\n  dv_333 = 33.0 * dv_14 + dv_180;\n  DataVector& dv_334 = temps.at(267);\n  dv_334 = (1.0 / 24.0) * dv_283;\n  DataVector& dv_335 = temps.at(210);\n  dv_335 = -d_18 * dv_170 + dv_162 + dv_220 * xp;\n  DataVector& dv_336 = temps.at(190);\n  dv_336 = d_16 * dv_199;\n  DataVector& dv_337 = temps.at(268);\n  dv_337 = dv_213 * dv_336;\n  DataVector& dv_338 = temps.at(269);\n  dv_338 = dv_33 * xp;\n  DataVector& dv_339 = temps.at(270);\n  dv_339 = d_16 * dv_196;\n  DataVector& dv_340 = temps.at(271);\n  dv_340 = 12.0 * dv_339;\n  DataVector& dv_341 = temps.at(239);\n  dv_341 = -d_6 * dv_1 * xpdot + d_88 * dv_326 + dv_257 - dv_5 * xp;\n  DataVector& dv_342 = temps.at(272);\n  dv_342 = sqrt(dv_19) * dv_19;\n  DataVector& dv_343 = temps.at(273);\n  dv_343 = d_106 * dv_342;\n  DataVector& dv_344 = temps.at(53);\n  dv_344 = Dx * d_95 - d_37 * dv_55 + dv_261 - yp * (d_1 * dv_315 + dv_54) +\n           ypdot * (-d_6 * dv_315 + d_7 * dv_54);\n  DataVector& dv_345 = temps.at(274);\n  dv_345 = d_108 * d_13 * dv_342;\n  DataVector& dv_346 = temps.at(275);\n  dv_346 = d_62 * dv_242;\n  DataVector& dv_347 = temps.at(276);\n  dv_347 = d_5 * dv_196 * dv_49;\n  DataVector& dv_348 = temps.at(277);\n  dv_348 = d_105 * dv_20;\n  DataVector& dv_349 = temps.at(278);\n  dv_349 = dv_346 * dv_348;\n  DataVector& dv_350 = temps.at(279);\n  dv_350 = d_65 * dv_16;\n  DataVector& dv_351 = temps.at(280);\n  dv_351 = d_105 * dv_47;\n  DataVector& dv_352 = temps.at(281);\n  dv_352 = d_59 * dv_20 * dv_351;\n  DataVector& dv_353 = temps.at(282);\n  dv_353 = -dv_326;\n  DataVector& dv_354 = temps.at(283);\n  dv_354 = d_19 * (d_51 * dv_5 + dv_162 + dv_353) + d_4 * dv_335;\n  DataVector& dv_355 = temps.at(284);\n  dv_355 = d_78 * dv_213 * dv_89;\n  DataVector& dv_356 = temps.at(285);\n  dv_356 = dv_339 * dv_61;\n  DataVector& dv_357 = temps.at(286);\n  dv_357 = d_13 * dv_201;\n  DataVector& dv_358 = temps.at(187);\n  dv_358 = dv_196 * dv_46;\n  DataVector& dv_359 = temps.at(287);\n  dv_359 = d_109 * d_40 * dv_49;\n  DataVector& dv_360 = temps.at(288);\n  dv_360 = dv_20 * dv_359;\n  DataVector& dv_361 = temps.at(289);\n  dv_361 = dv_201 * dv_21;\n  DataVector& dv_362 = temps.at(290);\n  dv_362 = d_79 * dv_361;\n  DataVector& dv_363 = temps.at(291);\n  dv_363 = d_59 * dv_60;\n  DataVector& dv_364 = temps.at(292);\n  dv_364 = d_5 * dv_319 * dv_50;\n  DataVector& dv_365 = temps.at(293);\n  dv_365 = 24.0 * dv_16;\n  DataVector& dv_366 = temps.at(294);\n  dv_366 = d_40 * dv_47;\n  DataVector& dv_367 = temps.at(295);\n  dv_367 = d_65 * dv_365 * dv_366;\n  DataVector& dv_368 = temps.at(60);\n  dv_368 = d_14 * d_40 * dv_60;\n  DataVector& dv_369 = temps.at(296);\n  dv_369 = 8.0 * dv_20 * dv_368;\n  DataVector& dv_370 = temps.at(79);\n  dv_370 = d_17 * dv_70 + dv_74 + dv_76 - dv_78 - dv_81;\n  DataVector& dv_371 = temps.at(270);\n  dv_371 = 8.0 * dv_339 * dv_370;\n  DataVector& dv_372 = temps.at(294);\n  dv_372 = 24.0 * dv_363 * dv_366;\n  DataVector& dv_373 = temps.at(20);\n  dv_373 = d_7 * dv_20;\n  DataVector& dv_374 = temps.at(75);\n  dv_374 = d_58 * dv_370;\n  DataVector& dv_375 = temps.at(74);\n  dv_375 = dv_373 * dv_374;\n  DataVector& dv_376 = temps.at(76);\n  dv_376 = 12.0 * d_78 * dv_47;\n  DataVector& dv_377 = temps.at(272);\n  dv_377 = 1.0 / dv_342;\n  DataVector& dv_378 = temps.at(70);\n  dv_378 = 2.0 * dv_89;\n  DataVector& dv_379 = temps.at(297);\n  dv_379 = 18.0 * d_110 * dv_21;\n  DataVector& dv_380 = temps.at(298);\n  dv_380 = -dv_294;\n  DataVector& dv_381 = temps.at(299);\n  dv_381 = 48.0 * d_0 * dv_12;\n  DataVector& dv_382 = temps.at(300);\n  dv_382 = d_4 * dv_311;\n  DataVector& dv_383 = temps.at(301);\n  dv_383 = dv_170 * dv_5 + xp * (dv_17 + dv_95);\n  DataVector& dv_384 = temps.at(302);\n  dv_384 = dv_1 * dv_172;\n  DataVector& dv_385 = temps.at(303);\n  dv_385 = dv_162 * (17.0 * d_3 + d_98);\n  DataVector& dv_386 = temps.at(304);\n  dv_386 = M * dv_173;\n  DataVector& dv_387 = temps.at(305);\n  dv_387 = dv_137 - 15.0 * dv_14 + dv_26;\n  DataVector& dv_388 = temps.at(306);\n  dv_388 = M * dv_137;\n  DataVector& dv_389 = temps.at(167);\n  dv_389 = -15 * dv_15 + dv_173 + dv_26;\n  DataVector& dv_390 = temps.at(236);\n  dv_390 = d_43 * dv_254;\n  DataVector& dv_391 = temps.at(20);\n  dv_391 = d_112 * dv_373;\n  DataVector& dv_392 = temps.at(307);\n  dv_392 = -d_17 * dv_79 + d_35 * dv_4 + dv_143;\n  DataVector& dv_393 = temps.at(308);\n  dv_393 = dv_41 * yp;\n  DataVector& dv_394 = temps.at(309);\n  dv_394 = -d_10 * dv_143 + d_33 * dv_0 + dv_252 - dv_255 * (d_10 + 1.0);\n  DataVector& dv_395 = temps.at(237);\n  dv_395 = Dx * d_6 * xpdot * ypdot - d_11 * dv_255 -\n           xp * (Dx * d_35 - d_1 * dv_314 + d_7 * dv_315) -\n           yp * (-d_1 * dv_0 + d_10 * dv_5 + dv_229);\n  DataVector& dv_396 = temps.at(226);\n  dv_396 = d_64 * dv_242;\n  DataVector& dv_397 = temps.at(277);\n  dv_397 = dv_348 * dv_396;\n  DataVector& dv_398 = temps.at(310);\n  dv_398 = d_19 * (d_31 * dv_4 + dv_143 + dv_256) + d_4 * dv_392;\n  DataVector& dv_399 = temps.at(311);\n  dv_399 = d_64 * dv_226;\n  DataVector& dv_400 = temps.at(312);\n  dv_400 = d_13 * dv_203;\n  DataVector& dv_401 = temps.at(313);\n  dv_401 = d_79 * dv_87;\n  DataVector& dv_402 = temps.at(314);\n  dv_402 = dv_203 * dv_21;\n  DataVector& dv_403 = temps.at(315);\n  dv_403 = d_79 * dv_90;\n  DataVector& dv_404 = temps.at(90);\n  dv_404 = d_111 * dv_92;\n  DataVector& dv_405 = temps.at(186);\n  dv_405 = d_113 * dv_194;\n  DataVector& dv_406 = temps.at(316);\n  dv_406 = d_44 * dv_54;\n  DataVector& dv_407 = temps.at(37);\n  dv_407 = dv_80 + yp * (dv_100 + dv_37);\n  DataVector& dv_408 = temps.at(98);\n  dv_408 = 12.0 * dv_1;\n  DataVector& dv_409 = temps.at(78);\n  dv_409 = 4.0 * Dx;\n  DataVector& dv_410 = temps.at(317);\n  dv_410 = 48.0 * d_115 * dv_19;\n  DataVector& dv_411 = temps.at(318);\n  dv_411 = d_13 * dv_282;\n  DataVector& dv_412 = temps.at(280);\n  dv_412 = dv_19 * dv_351;\n  DataVector& dv_413 = temps.at(319);\n  dv_413 = d_117 * dv_89;\n\n  get(get<CurvedScalarWave::Tags::Psi>(*result)) =\n      dv_21 *\n      (0.5 * d_16 * dv_43 * dv_45 - 1.0 / 24.0 * d_21 * dv_195 * dv_45 + 1.0);\n  get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(*result)) =\n      dv_334 *\n      (d_21 * d_84 * dv_214 * dv_231 + d_79 * dv_214 * dv_91 -\n       dv_197 * dv_198 * rpdot +\n       12.0 * dv_197 *\n           (d_23 * d_75 * dv_227 * dv_88 + d_71 * dv_90 -\n            dv_211 * (-d_71 * dv_39 + d_73 * dv_41 - dv_230) + dv_218 * dv_41 -\n            dv_223 - dv_226 * rp * (d_0 * dv_224 + dv_225) + dv_87 * rpdot) +\n       dv_212 * dv_213 - 14.0 * dv_231 * dv_232 * rpdot -\n       dv_232 *\n           (M * dv_259 * dv_85 + d_15 * dv_237 * dv_245 +\n            d_20 * dv_16 * dv_240 + d_20 * dv_244 * dv_266 +\n            64.0 * d_22 * dv_236 * dv_65 * rpdot + d_22 * dv_244 * dv_245 -\n            8.0 * d_24 * dv_243 * dv_246 + d_24 * dv_263 * dv_61 +\n            d_25 * dv_237 * dv_50 * rpdot - d_29 * dv_239 * dv_246 +\n            d_39 * dv_236 * dv_240 + d_39 * dv_237 * dv_266 -\n            d_39 * dv_243 * dv_49 + d_39 * dv_259 * dv_65 +\n            18.0 * d_50 * dv_264 * dv_283 * (dv_284 * dv_284) +\n            d_82 * dv_281 * rpdot - d_83 * dv_16 * dv_239 +\n            d_83 * dv_237 * dv_249 - d_83 * dv_265 * dv_49 -\n            d_85 * dv_247 * dv_47 - d_86 * dv_246 * dv_260 * d_83 * rp -\n            d_96 * dv_246 * dv_265 + d_96 * dv_263 * dv_63 -\n            dv_233 * dv_48 * rpdot + 32.0 * dv_234 * dv_51 -\n            44.0 * dv_234 * dv_64 + 80.0 * dv_234 * dv_66 +\n            dv_244 * dv_249 * d_83 * rp * rp - 36.0 * dv_247 * dv_86 -\n            dv_248 * dv_49 * d_83 * rp * rp * rp - dv_264 * dv_286 -\n            18.0 * dv_284 * dv_45 *\n                (d_13 * dv_270 * dv_9 * rpdot - d_86 * dv_227 * dv_270 -\n                 dv_211 * dv_288 + dv_217 * dv_269 * rp - dv_223 -\n                 dv_241 * dv_287 * rp + 2.0 * dv_269 * dv_6 * rpdot) +\n            dv_286 * (3.0 * d_13 * d_65 * dv_10 * rpdot - d_5 * dv_285 * dv_7 -\n                      d_65 * dv_204 - dv_2 - dv_200 * dv_241 - dv_205) +\n            rp *\n                (-d_0 * d_98 * dv_287 * dv_288 +\n                 d_100 * dv_193 *\n                     (-d_19 * dv_187 * dv_217 - d_71 * dv_187 * dv_6 -\n                      dv_184 * xpddot -\n                      dv_188 * (-M * d_77 * dv_18 + d_1 * dv_3 +\n                                16.0 * d_102 * dv_12 + 12.0 * d_25 * dv_2 +\n                                d_41 * dv_309 + 16.0 * d_6 * dv_309 +\n                                d_7 * dv_218 * dv_6 + d_71 * dv_106 -\n                                24.0 * d_99 * dv_18) +\n                      dv_274 * ypddot +\n                      xpdot *\n                          (6.0 * d_40 * dv_12 * dv_285 * xp -\n                           d_6 * (xpdot *\n                                      (d_43 * (dv_329 - dv_332 * yp) -\n                                       d_44 * dv_170 *\n                                           (20.0 * dv_15 + dv_233 + dv_290 +\n                                            27.0 * dv_5) +\n                                       d_49 * (-9 * Dy * dv_178 + dv_333 * yp) +\n                                       d_90 * dv_4 * (18.0 * Dy * yp - dv_181) +\n                                       dv_331) +\n                                  ypdot *\n                                      (20.0 * Dx * dv_131 +\n                                       d_104 * (dv_329 + dv_330) +\n                                       d_43 * dv_170 * (dv_103 + dv_5 + dv_71) +\n                                       d_53 * (Dy * dv_179 + dv_328 * yp) -\n                                       2.0 * dv_298 * (dv_181 + 34.0 * dv_5))) -\n                           d_89 * dv_183 -\n                           dv_156 *\n                               (d_103 * (-d_26 * (dv_167 + 5.0 * dv_5) +\n                                         d_44 * dv_79 + 6.0 * dv_162 * dv_216 +\n                                         dv_326 * (-d_80 - dv_219)) +\n                                xpdot * (8.0 * Dx * dv_262 * xp * yp -\n                                         3.0 * d_17 * (dv_165 + dv_35) +\n                                         d_18 * (2.0 * Dy * yp - dv_167) -\n                                         6.0 * dv_110)) -\n                           dv_168 * dv_312 - dv_168 * dv_313 - dv_310 * xpdot) +\n                      ypdot *\n                          (d_6 *\n                               (d_43 *\n                                    (-d_103 * (dv_272 * dv_320 + dv_321 * yp) +\n                                     dv_0 * (-27 * dv_15 + dv_318 + dv_319 -\n                                             20.0 * dv_5)) -\n                                d_44 *\n                                    (3.0 * dv_314 *\n                                         (dv_119 - dv_137 + dv_14 + dv_163) +\n                                     xpdot * (d_35 * dv_322 + dv_28 * dv_320)) +\n                                d_45 * (-dv_0 * dv_79 - dv_317) +\n                                d_49 * (dv_0 * (54.0 * dv_15 + 36.0 * dv_16 +\n                                                dv_177 + dv_308) +\n                                        ypdot * (-dv_323 * yp + dv_324)) +\n                                d_52 * (9.0 * dv_314 * (dv_146 + dv_22) +\n                                        xpdot * (-d_35 * dv_325 + dv_324))) +\n                           d_89 * dv_273 + dv_155 * dv_312 + dv_155 * dv_313 +\n                           dv_156 * (d_17 * (2.0 * dv_0 * (dv_219 + 5.0 * yp) +\n                                             ypdot * (-dv_132 - dv_316 -\n                                                      8.0 * dv_5 - dv_67)) +\n                                     d_18 * (-4 * dv_0 * dv_262 +\n                                             3.0 * ypdot * (dv_153 + dv_229)) +\n                                     d_31 * xp *\n                                         (6.0 * dv_314 * (-d_80 - dv_79) +\n                                          xpdot * (dv_154 + dv_22)) -\n                                     2.0 * d_44 * (dv_314 + dv_315)) +\n                           dv_285 * dv_311 * yp - dv_310 * ypdot)) +\n                 4.0 * d_100 * dv_275 *\n                     (2.0 * M * d_5 * d_73 * dv_6 - d_4 * d_5 * dv_289 -\n                      d_4 * d_65 * d_98 * dv_6 * rpdot - d_58 * dv_289 -\n                      d_7 * dv_202 * rpdot - d_72 + dv_206 + dv_207) +\n                 d_13 * d_99 * dv_193 * dv_275 +\n                 8.0 * d_22 * d_86 * dv_192 * dv_275 -\n                 d_24 * d_86 * d_98 * dv_271 + dv_280 * rpdot +\n                 rp * (8.0 * d_102 * dv_279 +\n                       d_54 *\n                           (xpdot *\n                                (d_44 * (-dv_14 *\n                                             (22.0 * dv_16 + dv_300 + dv_301) +\n                                         dv_17 * (dv_302 + dv_98) + dv_299) +\n                                 d_52 * (15.0 * Dy * yp * (-dv_15 + dv_16) -\n                                         dv_128 + dv_14 * (dv_126 + dv_300)) -\n                                 dv_160 * (15.0 * dv_108 + dv_294 * yp) +\n                                 dv_293 +\n                                 dv_298 * (-45 * dv_295 + dv_297 * yp)) -\n                            ypdot *\n                                (-d_18 * dv_306 *\n                                     (-dv_219 * dv_38 + dv_305 * yp) -\n                                 d_43 *\n                                     (-d_101 * dv_307 + 4.0 * dv_121 + dv_127 +\n                                      dv_14 * (-dv_301 + dv_49 + 11.0 * dv_5) -\n                                      dv_16 * dv_301 + dv_291 * dv_5 + dv_299) +\n                                 d_49 * (-dv_125 * dv_5 + dv_128 +\n                                         dv_14 * (dv_296 + dv_308) -\n                                         22.0 * dv_307 * yp) +\n                                 15.0 * dv_110 * (dv_295 - dv_304 * yp) +\n                                 dv_131 * dv_303)) +\n                       dv_106 * dv_278 * rpdot + 8.0 * dv_150 * dv_217 -\n                       dv_186 *\n                           (xpdot * (3.0 * dv_162 - dv_54 * (7.0 * Dy + d_101) +\n                                     xp * (7.0 * dv_5 + dv_99)) +\n                            ypdot *\n                                (d_18 * dv_219 - dv_55 * (7.0 * Dx + 4.0 * xp) +\n                                 yp * (dv_104 + 7.0 * dv_4))) +\n                       dv_218 * dv_278 * dv_7))));\n  get<0>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -dv_334 *\n      (d_111 * dv_201 * dv_377 * (dv_43 * dv_43) -\n       d_113 * dv_361 *\n           (2.0 * d_40 * dv_93 - d_42 * dv_42 * dv_42 - d_48 * dv_130 -\n            dv_105 * dv_107 - dv_190 * dv_276) -\n       d_13 * d_62 * dv_340 * dv_42 + dv_201 * dv_213 - dv_201 * dv_369 -\n       dv_335 * dv_337 + dv_338 * dv_340 - dv_34 * dv_362 + dv_341 * dv_343 +\n       dv_341 * dv_352 + dv_344 * dv_345 + dv_344 * dv_356 - dv_346 * dv_347 +\n       dv_346 * dv_371 + dv_349 * dv_350 + dv_349 * dv_363 + dv_354 * dv_355 +\n       dv_357 * dv_358 - dv_357 * dv_360 - dv_357 * dv_375 + dv_361 * dv_364 -\n       dv_361 * dv_367 - dv_361 * dv_370 * dv_376 - dv_361 * dv_372 +\n       dv_362 * dv_42 * dv_89 -\n       dv_379 * dv_43 *\n           (d_13 * d_62 * dv_42 + 2.0 * dv_335 * dv_6 - dv_338 -\n            dv_354 * dv_378) -\n       dv_391 *\n           (3.0 * M * d_13 * d_6 * dv_354 * dv_88 +\n            M * dv_6 * rp * (d_44 * dv_165 + d_52 * dv_167 + dv_161 - dv_164) +\n            2.0 * d_6 *\n                (d_44 * dv_302 * (dv_17 + dv_96) + dv_172 * dv_380 +\n                 15.0 * dv_176 * (dv_16 + dv_29 + dv_95) + dv_182 * dv_297 +\n                 dv_293) -\n            dv_159 - dv_191 * (d_107 * d_51 + d_5 * d_61 + xpdot) -\n            dv_89 *\n                (d_114 * dv_383 +\n                 d_50 * (-d_44 * (-d_3 * dv_387 + dv_386) -\n                         d_52 * (-d_3 * dv_389 + dv_388) + 4.0 * dv_1 * dv_292 -\n                         dv_229 * dv_385 + 4.0 * dv_384 +\n                         xpdot * (d_45 * (dv_177 + dv_27) + d_46 * dv_25 +\n                                  d_47 * (-17 * dv_15 + dv_26 + dv_318) -\n                                  dv_112 + 12.0 * dv_390)) +\n                 d_6 * (-d_104 * (M * dv_97 - d_3 * dv_325) -\n                        d_53 * (M * dv_102 - d_3 * dv_322) + dv_1 * dv_171 -\n                        dv_35 * dv_385 + 20.0 * dv_384 +\n                        xpdot * (-d_46 * dv_332 + d_47 * dv_333 -\n                                 54.0 * dv_111 + dv_331 + 36.0 * dv_390)) +\n                 d_85 * dv_383 - dv_381 * xp - dv_382 * xp)));\n  get<1>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      dv_334 *\n      (-dv_203 * dv_213 + dv_203 * dv_369 - dv_203 * dv_377 * dv_404 +\n       dv_337 * dv_392 + dv_340 * dv_393 - dv_340 * dv_399 + dv_343 * dv_394 +\n       dv_345 * dv_395 + dv_347 * dv_396 - dv_350 * dv_397 + dv_352 * dv_394 -\n       dv_355 * dv_398 + dv_356 * dv_395 - dv_358 * dv_400 + dv_360 * dv_400 -\n       dv_363 * dv_397 - dv_364 * dv_402 + dv_367 * dv_402 - dv_371 * dv_396 +\n       dv_372 * dv_402 + dv_375 * dv_400 - dv_376 * dv_402 * dv_83 +\n       dv_379 * dv_91 *\n           (-dv_378 * dv_398 + 2.0 * dv_392 * dv_6 + dv_393 - dv_399) -\n       dv_391 *\n           (-d_6 * d_84 * dv_226 * dv_398 +\n            d_97 *\n                (Dy * d_45 * dv_303 - Dy * d_46 * (dv_116 + dv_291 + dv_316) -\n                 d_43 * dv_305 * dv_306 -\n                 dv_144 * (dv_125 - 68.0 * dv_14 + dv_301) -\n                 15.0 * dv_304 * dv_406) +\n            dv_151 - dv_157 + dv_191 * (d_32 * d_58 + d_5 * d_63 + ypdot) +\n            dv_89 *\n                (d_114 * dv_407 +\n                 d_50 *\n                     (-d_43 * (-d_3 * (dv_145 + dv_31) + dv_388) +\n                      d_45 * dv_30 * ypdot -\n                      d_49 * (-d_3 * (-17 * dv_14 + dv_26 + dv_327) + dv_386) -\n                      6.0 * dv_152 * (d_1 + 5.0 * d_3) + dv_406 * dv_408 +\n                      xpdot * (d_43 * dv_389 * xp + d_44 * dv_387 * yp +\n                               dv_131 * dv_409 - 34.0 * dv_143 * dv_162 +\n                               dv_172 * dv_320)) +\n                 d_6 * (-d_104 *\n                            (Dx * dv_79 * (9.0 * d_3 + d_7) - dv_330 * xpdot) +\n                        d_35 * d_44 * (Dx * dv_408 + dv_328 * xpdot) +\n                        2.0 * d_43 *\n                            (Dy * (-Dy * d_98 + dv_0 * yp) + d_3 * dv_321) +\n                        d_45 * (20.0 * Dy * dv_0 + dv_317) -\n                        d_49 * (-d_3 * dv_323 +\n                                dv_409 * (M * dv_221 + 17.0 * dv_5 * xpdot))) +\n                 d_85 * dv_407 - dv_381 * yp - dv_382 * yp)) -\n       dv_401 * dv_402 + dv_402 * dv_403 + dv_402 * dv_405);\n  get<2>(get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                           Frame::Inertial>>(*result)) =\n      -1.0 / 24.0 * z *\n      (-M * dv_260 *\n           (3.0 * d_115 * d_117 * d_13 * d_50 * (d_4 * dv_33 + dv_40) +\n            d_115 * (dv_106 +\n                     rp * (-d_17 * dv_303 + d_18 * dv_380 + 30.0 * dv_277)) -\n            dv_378 * (d_115 * d_60 * dv_6 +\n                      d_115 * rp *\n                          (xpdot * (5.0 * dv_162 + dv_22 * xp + dv_353) +\n                           ypdot * (5.0 * dv_143 + 6.0 * dv_252 + dv_256)) +\n                      d_55 * dv_6 + d_56 * dv_6)) *\n           1.0 / d_39 +\n       d_105 * d_109 * dv_411 + d_106 * d_8 * dv_282 + d_108 * d_116 * dv_411 +\n       d_110 * d_115 * dv_198 * (-dv_413 + dv_6) +\n       d_116 * d_78 * dv_19 * dv_61 - d_38 * dv_282 + 48.0 * d_5 * dv_106 -\n       d_5 * dv_19 * dv_62 + d_59 * d_8 * dv_412 + d_65 * dv_412 -\n       d_7 * dv_374 * dv_63 - d_78 * dv_410 * dv_413 - 24.0 * dv_11 +\n       24.0 * dv_14 + 24.0 * dv_15 - dv_260 * dv_368 + dv_336 * dv_410 -\n       dv_359 * dv_63 + dv_364 + dv_365 - dv_367 - dv_370 * dv_376 - dv_372 +\n       dv_401 - dv_403 + dv_404 * dv_44 - dv_405 + dv_46 * dv_65) /\n      (square(dv_19) * sqrt(dv_19));\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/RadiusFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/RadiusFunctions.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\nnamespace detail {\n\nvoid check_alpha(const double alpha) {\n  if (alpha < 0. or alpha > 4.) {\n    ERROR(\n        \"Only exponents between 0 and 4 have been tested for the broken power \"\n        \"law. Values outside this range are likely not sensible.\");\n  }\n}\n\nvoid check_delta(const double delta) {\n  if (delta < 1e-2) {\n    ERROR(\n        \"A value of delta less than 0.01 is disabled as it can lead to \"\n        \"prohibitively large floating precision errors.\");\n  }\n}\n}  // namespace detail\n\ndouble smooth_broken_power_law(const double orbit_radius, const double alpha,\n                               const double radius_at_inf, const double rb,\n                               const double delta) {\n  detail::check_alpha(alpha);\n  detail::check_delta(delta);\n  const double r_by_rb = orbit_radius / rb;\n  return radius_at_inf * pow(r_by_rb, alpha) *\n         pow(1. + pow(r_by_rb, 1. / delta), -alpha * delta);\n}\n\ndouble smooth_broken_power_law_derivative(const double orbit_radius,\n                                          const double alpha,\n                                          const double radius_at_inf,\n                                          const double rb, const double delta) {\n  detail::check_alpha(alpha);\n  detail::check_delta(delta);\n  // During testing it was found that for large values of rb, the equation below\n  // can FPE even though the derivative is clearly zero up to double precision.\n  // To avoid this, we introduce this shortcut.\n  if (orbit_radius > rb + 1000. * delta) {\n    return 0.;\n  }\n  const double r_by_rb = orbit_radius / rb;\n  const double temp1 = pow(r_by_rb, 1. / delta - 1.);\n  const double temp2 = temp1 * r_by_rb + 1.;\n\n  return radius_at_inf * alpha * pow(r_by_rb, alpha - 1) / rb *\n         pow(temp2, -alpha * delta - 1.) * (temp2 - temp1 * r_by_rb);\n}\n\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/RadiusFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace CurvedScalarWave::Worldtube {\n\nnamespace detail {\n\nvoid check_alpha(double alpha);\n\nvoid check_delta(double delta);\n}  // namespace detail\n\n/*!\n * \\brief A smoothly broken power law that falls off to a constant value\n * for larger radii.\n *\n * \\details The function is given by Eq. (3) of \\cite Wittek:2024pis\n *\n * \\begin{equation}\n * f(r) = R_\\infty \\left(\\frac{r}{r_b}\\right)^{\\alpha} \\left( 1 +\n * \\left(\\frac{r}{r_b}\\right)^{1 / \\Delta}\\right)^{-\\alpha\\Delta}.\n * \\end{equation}\n *\n * For radii $r \\ll r_b$, the function obeys the power law $f(r) \\propto\n * r^{\\alpha}$. For radii $r \\gg r_b$, the function asymptotes to $R_\\infty$.\n * The parameter $\\Delta$ determines the width of the transition region with a\n * larger value of $\\Delta$ leading to a more gradual transition.\n *\n * This function is used to control the worldtube radius for more eccentric\n * orbits so the radius does not grow too large during the apoapsis passage\n * as this does not lead to performance gains and can cause problems with the\n * domain.\n */\ndouble smooth_broken_power_law(double orbit_radius, double alpha,\n                               double radius_at_inf, double rb, double delta);\n/*!\n * \\brief Returns the analytical derivative of `smooth_broken_power_law`.\n */\ndouble smooth_broken_power_law_derivative(double orbit_radius, double alpha,\n                                          double radius_at_inf, double rb,\n                                          double delta);\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.cpp",
    "content": "\n// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\nnamespace detail {\nvoid check_positive_time_and_timescale(const double t_minus_turn_on,\n                                       const double turn_on_timescale) {\n  if (t_minus_turn_on <= 0) {\n    ERROR(\"The time must be positive but is \" << time);\n  }\n  if (turn_on_timescale <= 0) {\n    ERROR(\"The turn on timescale must be positive but is \"\n          << turn_on_timescale);\n  }\n}\n\n}  // namespace detail\n\ntemplate <size_t Dim>\nvoid self_force_acceleration(\n    gsl::not_null<tnsr::I<double, Dim>*> self_force_acc,\n    const Scalar<double>& dt_psi_monopole,\n    const tnsr::i<double, Dim>& psi_dipole,\n    const tnsr::I<double, Dim>& particle_velocity, const double particle_charge,\n    const double particle_mass, const tnsr::AA<double, Dim>& inverse_metric,\n    const Scalar<double>& dilation_factor) {\n  const double factor =\n      particle_charge / particle_mass / square(get(dilation_factor));\n  for (size_t i = 0; i < Dim; ++i) {\n    self_force_acc->get(i) =\n        (inverse_metric.get(i + 1, 0) -\n         particle_velocity.get(i) * inverse_metric.get(0, 0)) *\n        get(dt_psi_monopole) * factor;\n    for (size_t j = 0; j < Dim; ++j) {\n      self_force_acc->get(i) +=\n          (inverse_metric.get(i + 1, j + 1) -\n           particle_velocity.get(i) * inverse_metric.get(0, j + 1)) *\n          psi_dipole.get(j) * factor;\n    }\n  }\n}\n\ntemplate <size_t Dim>\ntnsr::I<double, Dim> self_force_acceleration(\n    const Scalar<double>& dt_psi_monopole,\n    const tnsr::i<double, Dim>& psi_dipole,\n    const tnsr::I<double, Dim>& particle_velocity, const double particle_charge,\n    const double particle_mass, const tnsr::AA<double, Dim>& inverse_metric,\n    const Scalar<double>& dilation_factor) {\n  tnsr::I<double, Dim> self_force_acc{};\n  self_force_acceleration(make_not_null(&self_force_acc), dt_psi_monopole,\n                          psi_dipole, particle_velocity, particle_charge,\n                          particle_mass, inverse_metric, dilation_factor);\n  return self_force_acc;\n}\n\ntemplate <size_t Dim>\ntnsr::A<double, Dim> self_force_per_mass(\n    const tnsr::a<double, Dim>& d_psi,\n    const tnsr::A<double, Dim>& four_velocity, const double particle_charge,\n    const double particle_mass, const tnsr::AA<double, Dim>& inverse_metric) {\n  return tenex::evaluate<ti::B>(particle_charge / particle_mass * d_psi(ti::a) *\n                                (inverse_metric(ti::A, ti::B) +\n                                 four_velocity(ti::A) * four_velocity(ti::B)));\n}\n\ntemplate <size_t Dim>\ntnsr::A<double, Dim> dt_self_force_per_mass(\n    const tnsr::a<double, Dim>& d_psi, const tnsr::a<double, Dim>& dt_d_psi,\n    const tnsr::A<double, Dim>& four_velocity,\n    const tnsr::A<double, Dim>& dt_four_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, Dim>& inverse_metric,\n    const tnsr::AA<double, Dim>& dt_inverse_metric) {\n  return tenex::evaluate<ti::A>(\n      particle_charge / particle_mass *\n      ((dt_inverse_metric(ti::A, ti::B) +\n        four_velocity(ti::A) * dt_four_velocity(ti::B) +\n        dt_four_velocity(ti::A) * four_velocity(ti::B)) *\n           d_psi(ti::b) +\n       (inverse_metric(ti::A, ti::B) +\n        four_velocity(ti::A) * four_velocity(ti::B)) *\n           dt_d_psi(ti::b)));\n}\n\ntemplate <size_t Dim>\ntnsr::A<double, Dim> dt2_self_force_per_mass(\n    const tnsr::a<double, Dim>& d_psi, const tnsr::a<double, Dim>& dt_d_psi,\n    const tnsr::a<double, Dim>& dt2_d_psi,\n    const tnsr::A<double, Dim>& four_velocity,\n    const tnsr::A<double, Dim>& dt_four_velocity,\n    const tnsr::A<double, Dim>& dt2_four_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, Dim>& inverse_metric,\n    const tnsr::AA<double, Dim>& dt_inverse_metric,\n    const tnsr::AA<double, Dim>& dt2_inverse_metric) {\n  return tenex::evaluate<ti::A>(\n      particle_charge / particle_mass *\n      ((dt2_inverse_metric(ti::A, ti::B) +\n        dt2_four_velocity(ti::A) * four_velocity(ti::B) +\n        dt2_four_velocity(ti::B) * four_velocity(ti::A) +\n        2. * dt_four_velocity(ti::A) * dt_four_velocity(ti::B)) *\n           d_psi(ti::b) +\n       2. * dt_d_psi(ti::b) *\n           (dt_inverse_metric(ti::A, ti::B) +\n            dt_four_velocity(ti::A) * four_velocity(ti::B) +\n            dt_four_velocity(ti::B) * four_velocity(ti::A)) +\n       dt2_d_psi(ti::b) * (inverse_metric(ti::A, ti::B) +\n                           four_velocity(ti::A) * four_velocity(ti::B))));\n}\n\ntemplate <size_t Dim>\ntnsr::A<double, Dim> Du_self_force_per_mass(\n    const tnsr::A<double, Dim>& self_force,\n    const tnsr::A<double, Dim>& dt_self_force,\n    const tnsr::A<double, Dim>& four_velocity,\n    const tnsr::Abb<double, Dim>& christoffel) {\n  return tenex::evaluate<ti::A>(dt_self_force(ti::A) * get<0>(four_velocity) +\n                                christoffel(ti::A, ti::b, ti::c) *\n                                    four_velocity(ti::B) * self_force(ti::C));\n}\n\ntemplate <size_t Dim>\ntnsr::A<double, Dim> dt_Du_self_force_per_mass(\n    const tnsr::A<double, Dim>& self_force,\n    const tnsr::A<double, Dim>& dt_self_force,\n    const tnsr::A<double, Dim>& dt2_self_force,\n    const tnsr::A<double, Dim>& four_velocity,\n    const tnsr::A<double, Dim>& dt_four_velocity,\n    const tnsr::Abb<double, Dim>& christoffel,\n    const tnsr::Abb<double, Dim>& dt_christoffel) {\n  return tenex::evaluate<ti::A>(\n      dt2_self_force(ti::A) * get<0>(four_velocity) +\n      dt_self_force(ti::A) * get<0>(dt_four_velocity) +\n      dt_christoffel(ti::A, ti::b, ti::c) * four_velocity(ti::B) *\n          self_force(ti::C) +\n      christoffel(ti::A, ti::b, ti::c) * dt_four_velocity(ti::B) *\n          self_force(ti::C) +\n      christoffel(ti::A, ti::b, ti::c) * four_velocity(ti::B) *\n          dt_self_force(ti::C));\n}\n\ndouble turn_on_function(const double t_minus_turn_on,\n                        const double turn_on_timescale) {\n  detail::check_positive_time_and_timescale(t_minus_turn_on, turn_on_timescale);\n  const double t_over_timescale = t_minus_turn_on / turn_on_timescale;\n  return 1. - exp(-square(square(t_over_timescale)));\n}\n\ndouble dt_turn_on_function(const double t_minus_turn_on,\n                           const double turn_on_timescale) {\n  detail::check_positive_time_and_timescale(t_minus_turn_on, turn_on_timescale);\n  const double t_over_timescale_quart =\n      square(square(t_minus_turn_on / turn_on_timescale));\n  return 4. * t_over_timescale_quart * exp(-t_over_timescale_quart) /\n         t_minus_turn_on;\n}\n\ndouble dt2_turn_on_function(const double t_minus_turn_on,\n                            const double turn_on_timescale) {\n  detail::check_positive_time_and_timescale(t_minus_turn_on, turn_on_timescale);\n  const double t_squared = square(t_minus_turn_on);\n  const double t_quart = square(t_squared);\n  const double timescale_quart = square(square(turn_on_timescale));\n  const double t_over_timescale_quart = t_quart / timescale_quart;\n  return 4. * t_squared * exp(-t_over_timescale_quart) *\n         (3. * timescale_quart - 4. * t_quart) / square(timescale_quart);\n}\n\n// Instantiations\ntemplate void self_force_acceleration(\n    gsl::not_null<tnsr::I<double, 3>*> self_force_acc,\n    const Scalar<double>& dt_psi_monopole, const tnsr::i<double, 3>& psi_dipole,\n    const tnsr::I<double, 3>& particle_velocity, const double particle_charge,\n    const double particle_mass, const tnsr::AA<double, 3>& inverse_metric,\n    const Scalar<double>& dilation_factor);\n\ntemplate tnsr::I<double, 3> self_force_acceleration(\n    const Scalar<double>& dt_psi_monopole, const tnsr::i<double, 3>& psi_dipole,\n    const tnsr::I<double, 3>& particle_velocity, const double particle_charge,\n    const double particle_mass, const tnsr::AA<double, 3>& inverse_metric,\n    const Scalar<double>& dilation_factor);\n\ntemplate tnsr::A<double, 3> self_force_per_mass(\n    const tnsr::a<double, 3>& d_psi, const tnsr::A<double, 3>& four_velocity,\n    const double particle_charge, const double particle_mass,\n    const tnsr::AA<double, 3>& inverse_metric);\n\ntemplate tnsr::A<double, 3> dt_self_force_per_mass(\n    const tnsr::a<double, 3>& d_psi, const tnsr::a<double, 3>& dt_d_psi,\n    const tnsr::A<double, 3>& four_velocity,\n    const tnsr::A<double, 3>& dt_four_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, 3>& inverse_metric,\n    const tnsr::AA<double, 3>& dt_inverse_metric);\n\ntemplate tnsr::A<double, 3> dt2_self_force_per_mass(\n    const tnsr::a<double, 3>& d_psi, const tnsr::a<double, 3>& dt_d_psi,\n    const tnsr::a<double, 3>& dt2_d_psi,\n    const tnsr::A<double, 3>& four_velocity,\n    const tnsr::A<double, 3>& dt_four_velocity,\n    const tnsr::A<double, 3>& dt2_four_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, 3>& inverse_metric,\n    const tnsr::AA<double, 3>& dt_inverse_metric,\n    const tnsr::AA<double, 3>& dt2_inverse_metric);\n\ntemplate tnsr::A<double, 3> Du_self_force_per_mass(\n    const tnsr::A<double, 3>& self_force,\n    const tnsr::A<double, 3>& dt_self_force,\n    const tnsr::A<double, 3>& four_velocity,\n    const tnsr::Abb<double, 3>& christoffel);\n\ntemplate tnsr::A<double, 3> dt_Du_self_force_per_mass(\n    const tnsr::A<double, 3>& self_force,\n    const tnsr::A<double, 3>& dt_self_force,\n    const tnsr::A<double, 3>& dt2_self_force,\n    const tnsr::A<double, 3>& four_velocity,\n    const tnsr::A<double, 3>& dt_four_velocity,\n    const tnsr::Abb<double, 3>& christoffel,\n    const tnsr::Abb<double, 3>& dt_christoffel);\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\nnamespace detail {\nvoid check_positive_time_and_timescale(double t_minus_turn_on,\n                                       double turn_on_timescale);\n\n}  // namespace detail\n\n/// @{\n/*!\n * \\brief Computes the coordinate acceleration due to the scalar self-force onto\n * the charge.\n *\n * \\details It is given by\n *\n * \\begin{equation}\n * (u^0)^2 \\ddot{x}^i_p  = \\frac{q}{\\mu}(g^{i\n * \\alpha} - \\dot{x}^i_p g^{0 \\alpha} ) \\partial_\\alpha \\Psi^R\n * \\end{equation}\n *\n * where $\\dot{x}^i_p$ is the position of the scalar charge, $\\Psi^R$ is the\n * regular field, $q$ is the particle's charge, $\\mu$ is the particle's mass,\n * $u^\\alpha$ is the four-velocity and $g^{\\alpha \\beta}$ is the inverse\n * spacetime metric in the inertial frame, evaluated at the position of the\n * particle. An overdot denotes a derivative with respect to coordinate time.\n * Greek indices are spacetime indices and Latin indices are purely spatial.\n * Note that the coordinate geodesic acceleration is NOT included.\n */\ntemplate <size_t Dim>\nvoid self_force_acceleration(\n    gsl::not_null<tnsr::I<double, Dim>*> self_force_acc,\n    const Scalar<double>& dt_psi_monopole,\n    const tnsr::i<double, Dim>& psi_dipole,\n    const tnsr::I<double, Dim>& particle_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, Dim>& inverse_metric,\n    const Scalar<double>& dilation_factor);\n\ntemplate <size_t Dim>\ntnsr::I<double, Dim> self_force_acceleration(\n    const Scalar<double>& dt_psi_monopole,\n    const tnsr::i<double, Dim>& psi_dipole,\n    const tnsr::I<double, Dim>& particle_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, Dim>& inverse_metric,\n    const Scalar<double>& dilation_factor);\n\n/// @}\n/*!\n * \\brief Computes the scalar self-force per unit mass\n *\n * \\details It is given by\n * \\begin{equation}\n * f^\\alpha = \\frac{q}{\\mu} (g^{\\alpha \\beta} + u^\\alpha u^\\beta) \\partial_\\beta\n * \\Psi^R\n * \\end{equation}\n * where $\\Psi^R$ is the regular field at the position of the particle, $q$ is\n * the particle's charge, $\\mu$ is the particle's mass, $u^\\alpha$ is the\n * four-velocity and $g^{\\alpha \\beta}$ is the inverse spacetime metric in the\n * inertial frame, evaluated at the position of the particle.\n */\ntemplate <size_t Dim>\ntnsr::A<double, Dim> self_force_per_mass(\n    const tnsr::a<double, Dim>& d_psi,\n    const tnsr::A<double, Dim>& four_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, Dim>& inverse_metric);\n\n/*!\n * \\brief Computes the first time derivative of scalar self-force per unit mass,\n * see `self_force_per_mass`, by applying the chain rule.\n */\ntemplate <size_t Dim>\ntnsr::A<double, Dim> dt_self_force_per_mass(\n    const tnsr::a<double, Dim>& d_psi, const tnsr::a<double, Dim>& dt_d_psi,\n    const tnsr::A<double, Dim>& four_velocity,\n    const tnsr::A<double, Dim>& dt_four_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, Dim>& inverse_metric,\n    const tnsr::AA<double, Dim>& dt_inverse_metric);\n\n/*!\n * \\brief Computes the second time derivative of scalar self-force per unit\n * mass, see `self_force_per_mass`, by applying the chain rule.\n */\ntemplate <size_t Dim>\ntnsr::A<double, Dim> dt2_self_force_per_mass(\n    const tnsr::a<double, Dim>& d_psi, const tnsr::a<double, Dim>& dt_d_psi,\n    const tnsr::a<double, Dim>& dt2_d_psi,\n    const tnsr::A<double, Dim>& four_velocity,\n    const tnsr::A<double, Dim>& dt_four_velocity,\n    const tnsr::A<double, Dim>& dt2_four_velocity, double particle_charge,\n    double particle_mass, const tnsr::AA<double, Dim>& inverse_metric,\n    const tnsr::AA<double, Dim>& dt_inverse_metric,\n    const tnsr::AA<double, Dim>& dt2_inverse_metric);\n\n/*!\n * \\brief Computes the covariant derivative of the scalar self-force per unit\n * mass $f^\\alpha$, see `self_force_per_mass`, along the four velocity\n * $u^\\beta$, i.e. $u^\\beta \\nabla_\\beta f^\\alpha$.\n */\ntemplate <size_t Dim>\ntnsr::A<double, Dim> Du_self_force_per_mass(\n    const tnsr::A<double, Dim>& self_force,\n    const tnsr::A<double, Dim>& dt_self_force,\n    const tnsr::A<double, Dim>& four_velocity,\n    const tnsr::Abb<double, Dim>& christoffel);\n/*!\n * \\brief Computes the time derivative of the covariant derivative of the scalar\n * self-force per unit mass $f^\\alpha$, see `Du_self_force_per_mass`, along the\n * four velocity $u^\\beta$, i.e.\n * $\\frac{d}{dt}u^\\beta \\nabla_\\beta f^\\alpha$.\n */\ntemplate <size_t Dim>\ntnsr::A<double, Dim> dt_Du_self_force_per_mass(\n    const tnsr::A<double, Dim>& self_force,\n    const tnsr::A<double, Dim>& dt_self_force,\n    const tnsr::A<double, Dim>& dt2_self_force,\n    const tnsr::A<double, Dim>& four_velocity,\n    const tnsr::A<double, Dim>& dt_four_velocity,\n    const tnsr::Abb<double, Dim>& christoffel,\n    const tnsr::Abb<double, Dim>& dt_christoffel);\n\n/*!\n * \\brief A function used to roll-on the self-force continuously from 0 to 1\n *\n * \\details It is given by Eq.(60) of \\cite Wittek:2024gxn\n * \\begin{equation}\n * w(t) = 1 - \\exp \\left( - \\left( (t - t_{\\mathrm{turn_on}}) / \\sigma \\right)^4\n * \\right), \\end{equation} where $t$ is the current simulation time,\n * $t_{\\mathrm{turn_on}}$ is the time where the self-force is turned on and\n * $\\sigma$ dictates the timescale over which it is turned on. The function is\n * $\\mathcal{C}^3$, i.e. three times continuously differentiable, assuming $w(t)\n * = 0$ for $t < t_{\\mathrm{turn_on}}$.\n */\ndouble turn_on_function(double t_minus_turn_on, double turn_on_timescale);\n\n/*!\n * \\brief The first derivative of `turn_on_function`\n */\ndouble dt_turn_on_function(double t_minus_turn_on, double turn_on_timescale);\n\n/*!\n * \\brief The second derivative of `turn_on_function`\n */\ndouble dt2_turn_on_function(double t_minus_turn_on, double turn_on_timescale);\n\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  IterateAccelerationTerms.cpp\n  UpdateAcceleration.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ChangeSlabSize.hpp\n  InitializeElementFacesGridCoordinates.hpp\n  InitializeEvolvedVariables.hpp\n  IterateAccelerationTerms.hpp\n  ObserveWorldtubeSolution.hpp\n  ReceiveElementData.hpp\n  SendAccelerationTerms.hpp\n  SendToElements.hpp\n  UpdateAcceleration.hpp\n  UpdateFunctionsOfTime.hpp\n)\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ChangeSlabSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nclass TimeStepper;\n/// \\endcond\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n\n/*!\n * \\brief Waits for the data from all neighboring elements and changes the slab\n * size if a change in the global time step is detected.\n * \\details We check the slab size of the time step id sent by the elements. If\n * this is different from the slab size currently used by the worldtube\n * singleton, we assume a global slab size change has occurred in the elements\n * and adjust the worldtube slab size accordingly.\n */\nstruct ChangeSlabSize {\n  static constexpr size_t Dim = 3;\n  using inbox_tags = tmpl::list<\n      ::CurvedScalarWave::Worldtube::Tags::SphericalHarmonicsInbox<Dim>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    const auto& inbox =\n        tuples::get<Tags::SphericalHarmonicsInbox<Dim>>(inboxes);\n    if (inbox.empty()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    ASSERT(inbox.size() == 1,\n           \"Received data from two different time step ids.\");\n    const auto inbox_time_step_id = inbox.begin()->first;\n    const auto& inbox_slab = inbox_time_step_id.step_time().slab();\n    // we received data from a time step id with a different slab size,\n    // indicating a change in the global time step.\n    if (inbox_time_step_id.step_time().slab() !=\n        time_step_id.step_time().slab()) {\n      ASSERT(inbox_slab.start() == time_step_id.step_time().slab().start(),\n             \"The new slab should start at the same time as the old one.\");\n      // time_step_id comparison does NOT compare slabs.\n      ASSERT(\n          inbox_time_step_id == time_step_id,\n          \"Detected change in global time step id but the new time step id is \"\n          \"different.\");\n      const auto new_step = inbox_slab.duration();\n      const auto new_next_time_step_id =\n          db::get<::Tags::TimeStepper<TimeStepper>>(box).next_time_id(\n              inbox_time_step_id, new_step);\n      db::mutate<::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n                 ::Tags::TimeStepId>(\n          [&new_next_time_step_id, &new_step, &inbox_time_step_id](\n              const gsl::not_null<TimeStepId*> next_time_step_id,\n              const gsl::not_null<TimeDelta*> time_step,\n              const gsl::not_null<TimeStepId*> local_time_step_id) {\n            *next_time_step_id = new_next_time_step_id;\n            *time_step = new_step;\n            *local_time_step_id = inbox_time_step_id;\n          },\n          make_not_null(&box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeElementFacesGridCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube::Initialization {\n\n/*!\n * \\brief Initializes a map of the grid coordinates centered on the worldtube of\n * all faces that abut the worldtube with corresponding ElementIds.\n *\n * \\details The worldtube singleton computes an internal solution in the grid\n * frame and uses this map to evaluate compute it at the grid coordinates of\n * each element face abutting the worldtube each time step. This data is sent to\n * the corresponding element where it is used to apply pointwise boundary\n * conditions. The `ElementFacesGridCoordinates` holds a map of all the element\n * ids abutting the worldtube with the corresonding grid coordinates.\n *\n * \\warning This currently assumes that initial domain remains the same and\n * there is no AMR. To support this, the worldtube could send the\n * coefficients of its internal solution to each element which can evaluate\n * it on their current grid in the boundary conditions.\n *\n *  DataBox changes:\n * - Adds: nothing\n * - Removes: nothing\n * - Modifies: Tags::ElementFacesCoordinatesMap<Dim>\n */\ntemplate <size_t Dim>\nstruct InitializeElementFacesGridCoordinates {\n  using return_tags = tmpl::list<Tags::ElementFacesGridCoordinates<Dim>>;\n  using simple_tags = tmpl::list<Tags::ElementFacesGridCoordinates<Dim>>;\n  using compute_tags = tmpl::list<>;\n  using simple_tags_from_options =\n      tmpl::list<::domain::Tags::InitialExtents<Dim>,\n                 ::domain::Tags::InitialRefinementLevels<Dim>,\n                 evolution::dg::Tags::Quadrature>;\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using argument_tags = tmpl::flatten<\n      tmpl::list<simple_tags_from_options, ::domain::Tags::Domain<Dim>,\n                 Tags::ExcisionSphere<Dim>>>;\n  static void apply(\n      const gsl::not_null<std::unordered_map<\n          ElementId<Dim>, tnsr::I<DataVector, Dim, Frame::Grid>>*>\n          element_faces_grid_coords,\n      const std::vector<std::array<size_t, Dim>>& initial_extents,\n      const std::vector<std::array<size_t, Dim>>& initial_refinement,\n      const Spectral::Quadrature& i1_quadrature, const Domain<Dim>& domain,\n      const ::ExcisionSphere<Dim>& excision_sphere) {\n    const Spectral::Basis i1_basis{Spectral::Basis::Legendre};\n    const auto& blocks = domain.blocks();\n    const auto& worldtube_grid_coords = excision_sphere.center();\n    const auto& neighboring_blocks = excision_sphere.abutting_directions();\n    for (const auto& [block_id, _] : neighboring_blocks) {\n      const auto element_ids =\n          initial_element_ids(block_id, initial_refinement.at(block_id));\n      for (const auto element_id : element_ids) {\n        const auto direction = excision_sphere.abutting_direction(element_id);\n        if (direction.has_value()) {\n          const auto& current_block = blocks.at(block_id);\n          const auto mesh = ::domain::create_initial_mesh(\n              initial_extents, current_block, element_id, i1_basis,\n              i1_quadrature);\n          const auto face_mesh = mesh.slice_away(direction.value().dimension());\n          const ElementMap<Dim, Frame::Grid> element_map{\n              element_id,\n              current_block.is_time_dependent()\n                  ? current_block.moving_mesh_logical_to_grid_map().get_clone()\n                  : current_block.stationary_map().get_to_grid_frame()};\n          const auto face_logical_coords =\n              interface_logical_coordinates(face_mesh, direction.value());\n          auto faces_grid_coords = element_map(face_logical_coords);\n          for (size_t i = 0; i < 3; ++i) {\n            faces_grid_coords.get(i) -= worldtube_grid_coords.get(i);\n          }\n          element_faces_grid_coords->operator[](element_id) =\n              std::move(faces_grid_coords);\n        }\n      }\n    }\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Initialization\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeEvolvedVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n#include <cstddef>\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n/// \\cond\nnamespace Tags {\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\n/// \\endcond\nnamespace CurvedScalarWave::Worldtube::Initialization {\n/*!\n * \\brief Initializes the time stepper and evolved variables used by the\n * worldtube system. Also sets `Tags::CurrentIteration` to 0.\n *\n * \\details Sets the initial position and velocity of the particle to the values\n * specified in the input file. The time stepper history is set analogous to the\n * elements which use the same time stepper.\n */\nstruct InitializeEvolvedVariables {\n  static constexpr size_t Dim = 3;\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::EvolvedPosition<Dim>, Tags::EvolvedVelocity<Dim>>>;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n\n  using simple_tags =\n      tmpl::list<variables_tag, dt_variables_tag, Tags::CurrentIteration,\n                 Tags::ExpirationTime, Tags::WorldtubeRadius,\n                 ::Tags::HistoryEvolvedVariables<variables_tag>>;\n  using return_tags = simple_tags;\n\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using simple_tags_from_options = tmpl::list<Tags::InitialPositionAndVelocity>;\n  using argument_tags = tmpl::list<::Tags::TimeStepper<TimeStepper>,\n                                   Tags::InitialPositionAndVelocity,\n                                   ::Tags::Time, Tags::ExcisionSphere<Dim>>;\n  static void apply(\n      const gsl::not_null<Variables<\n          tmpl::list<Tags::EvolvedPosition<Dim>, Tags::EvolvedVelocity<Dim>>>*>\n          evolved_vars,\n      const gsl::not_null<\n          Variables<tmpl::list<::Tags::dt<Tags::EvolvedPosition<Dim>>,\n                               ::Tags::dt<Tags::EvolvedVelocity<Dim>>>>*>\n          dt_evolved_vars,\n      const gsl::not_null<size_t*> current_iteration,\n      const gsl::not_null<double*> expiration_time,\n      const gsl::not_null<double*> worldtube_radius,\n      const gsl::not_null<::Tags::HistoryEvolvedVariables<variables_tag>::type*>\n          time_stepper_history,\n      const TimeStepper& time_stepper,\n      const std::array<tnsr::I<double, Dim>, 2>& initial_pos_and_vel,\n      const double initial_time, const ExcisionSphere<Dim>& excision_sphere) {\n    *current_iteration = 0;\n\n    // the functions of time should be ready during the first step but not the\n    // second. We choose the arbitrary value of 1e-10 here to ensure this.\n    *expiration_time = initial_time + 1e-10;\n    *worldtube_radius = excision_sphere.radius();\n\n    const size_t starting_order =\n        visit(\n            []<typename Tag>(\n                const std::pair<tmpl::type_<Tag>, typename Tag::type&&> order) {\n              if constexpr (std::is_same_v<Tag,\n                                           TimeSteppers::Tags::FixedOrder>) {\n                return order.second;\n              } else {\n                return order.second.minimum;\n              }\n            },\n            time_stepper.order()) -\n        time_stepper.number_of_past_steps();\n    *time_stepper_history =\n        typename ::Tags::HistoryEvolvedVariables<variables_tag>::type{\n            starting_order};\n    evolved_vars->initialize(size_t(1), 0.);\n    dt_evolved_vars->initialize(size_t(1), 0.);\n    for (size_t i = 0; i < Dim; ++i) {\n      get<Tags::EvolvedPosition<Dim>>(*evolved_vars).get(i)[0] =\n          initial_pos_and_vel.at(0).get(i);\n      get<Tags::EvolvedVelocity<Dim>>(*evolved_vars).get(i)[0] =\n          initial_pos_and_vel.at(1).get(i);\n    }\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Initialization\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/IterateAccelerationTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/IterateAccelerationTerms.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/KerrSchildDerivatives.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nvoid IterateAccelerationTerms::apply(\n    gsl::not_null<Scalar<DataVector>*> acceleration_terms,\n    const std::array<tnsr::I<double, Dim>, 2>& pos_vel,\n    const tuples::TaggedTuple<\n        gr::Tags::SpacetimeMetric<double, Dim>,\n        gr::Tags::InverseSpacetimeMetric<double, Dim>,\n        gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>,\n        gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>,\n        Tags::TimeDilationFactor>& background,\n    const tnsr::I<double, Dim, Frame::Inertial>& geodesic_acc,\n    const Scalar<double>& psi_monopole, const Scalar<double>& dt_psi_monopole,\n    const tnsr::i<double, Dim, Frame::Inertial>& psi_dipole,\n    const tnsr::i<double, Dim, Frame::Inertial>& dt_psi_dipole,\n    const double charge, const std::optional<double> mass, const double time,\n    const std::optional<double> turn_on_time,\n    const std::optional<double> turn_on_interval, const size_t iteration) {\n  // size of the data that is sent to the elements to compute the acceleration\n  // terms of the puncture field. Consists of  the x, y and z components of the\n  // acceleration, as well the t, x and y components of the scalar self force\n  // and three different derivatives of it.\n  const size_t data_size = 15;\n  acceleration_terms->get() = DataVector(data_size, 0.);\n  auto acc = geodesic_acc;\n  double roll_on = 0.;\n  if (time > turn_on_time.value()) {\n    const auto& particle_position = pos_vel.at(0);\n    const auto& particle_velocity = pos_vel.at(1);\n    const auto& imetric =\n        get<gr::Tags::InverseSpacetimeMetric<double, Dim>>(background);\n    const auto& metric =\n        get<gr::Tags::SpacetimeMetric<double, Dim>>(background);\n    const auto& christoffel =\n        get<gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>(background);\n    const auto& trace_christoffel =\n        get<gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>>(\n            background);\n    const auto di_imetric =\n        spatial_derivative_inverse_ks_metric(particle_position);\n    const auto dij_imetric =\n        second_spatial_derivative_inverse_ks_metric(particle_position);\n    const auto di_trace_christoffel =\n        spatial_derivative_ks_contracted_christoffel(particle_position);\n    const auto di_metric = spatial_derivative_ks_metric(metric, di_imetric);\n    const auto dij_metric = second_spatial_derivative_metric(\n        metric, di_metric, di_imetric, dij_imetric);\n    const auto di_christoffel = spatial_derivative_christoffel(\n        di_metric, dij_metric, imetric, di_imetric);\n    const auto& u0 = get(get<Tags::TimeDilationFactor>(background));\n\n    // The evolution of the mass is a second order effect so we only include it\n    // starting at the 2nd iteration.\n    const double evolved_mass = iteration == 1\n                                    ? mass.value()\n                                    : mass.value() - charge * get(psi_monopole);\n\n    const auto self_force_acc = self_force_acceleration(\n        dt_psi_monopole, psi_dipole, particle_velocity, charge, evolved_mass,\n        imetric, get<Tags::TimeDilationFactor>(background));\n\n    const double t_minus_turn_on = time - turn_on_time.value();\n    roll_on = turn_on_function(t_minus_turn_on, turn_on_interval.value());\n    const double dt_roll_on =\n        dt_turn_on_function(t_minus_turn_on, turn_on_interval.value());\n    const double dt2_roll_on =\n        dt2_turn_on_function(t_minus_turn_on, turn_on_interval.value());\n    for (size_t i = 0; i < Dim; ++i) {\n      acc.get(i) += roll_on * self_force_acc.get(i);\n    }\n    const tnsr::A<double, Dim> u{{u0, get<0>(particle_velocity) * u0,\n                                  get<1>(particle_velocity) * u0,\n                                  get<2>(particle_velocity) * u0}};\n    const tnsr::a<double, Dim> d_psiR{{get(dt_psi_monopole), get<0>(psi_dipole),\n                                       get<1>(psi_dipole), get<2>(psi_dipole)}};\n\n    // Eq.(55) of Wittek:2024gxn\n    double dt2_psiR = get<0>(trace_christoffel) * get<0>(d_psiR);\n    for (size_t i = 0; i < Dim; ++i) {\n      dt2_psiR += -2. * imetric.get(0, i + 1) * dt_psi_dipole.get(i) +\n                  trace_christoffel.get(i + 1) * psi_dipole.get(i);\n    }\n    dt2_psiR /= get<0, 0>(imetric);\n\n    tnsr::a<double, Dim> dt_d_psiR{{dt2_psiR, get<0>(dt_psi_dipole),\n                                    get<1>(dt_psi_dipole),\n                                    get<2>(dt_psi_dipole)}};\n    for (size_t i = 0; i < Dim; ++i) {\n      get<0>(dt_d_psiR) += particle_velocity.get(i) * dt_psi_dipole.get(i);\n    }\n\n    double dt_evolved_mass = 0.;\n    double dt2_evolved_mass = 0.;\n    double dt_mass_factor = 0.;\n    double dt2_mass_factor = 0.;\n    if (iteration > 1) {\n      dt_evolved_mass = get(dt_psi_monopole);\n      dt2_evolved_mass = dt2_psiR;\n      for (size_t i = 0; i < Dim; ++i) {\n        dt_evolved_mass += psi_dipole.get(i) * particle_velocity.get(i);\n        dt2_evolved_mass +=\n            2. * dt_psi_dipole.get(i) * particle_velocity.get(i) +\n            psi_dipole.get(i) * acc.get(i);\n      }\n      dt_evolved_mass *= -charge;\n      dt2_evolved_mass *= -charge;\n\n      dt_mass_factor = -dt_evolved_mass / evolved_mass;\n      dt2_mass_factor = (2. * dt_evolved_mass * dt_evolved_mass -\n                         evolved_mass * dt2_evolved_mass) /\n                        square(evolved_mass);\n    }\n    const auto dt_christoffel = tenex::evaluate<ti::A, ti::b, ti::c>(\n        particle_velocity(ti::I) * di_christoffel(ti::i, ti::A, ti::b, ti::c));\n\n    const auto dt_imetric = tenex::evaluate<ti::A, ti::B>(\n        particle_velocity(ti::I) * di_imetric(ti::i, ti::A, ti::B));\n    const auto dt2_imetric = tenex::evaluate<ti::A, ti::B>(\n        particle_velocity(ti::I) * particle_velocity(ti::J) *\n            dij_imetric(ti::i, ti::j, ti::A, ti::B) +\n        acc(ti::I) * di_imetric(ti::i, ti::A, ti::B));\n    // Eq.(51a) of Wittek:2024gxn\n    const auto dt_u = tenex::evaluate<ti::A>(\n        charge / evolved_mass / u0 * imetric(ti::A, ti::B) * d_psiR(ti::b) -\n        christoffel(ti::A, ti::b, ti::c) * u(ti::B) * u(ti::C) / u0);\n    // Eq.(51b) of Wittek:2024gxn\n    const auto dt2_u = tenex::evaluate<ti::A>(\n        charge / evolved_mass / u0 *\n            (dt_imetric(ti::A, ti::B) * d_psiR(ti::b) +\n             imetric(ti::A, ti::B) * dt_d_psiR(ti::b) +\n             dt_mass_factor * imetric(ti::A, ti::B) * d_psiR(ti::b)) -\n        (dt_christoffel(ti::A, ti::b, ti::c) * u(ti::B) * u(ti::C) +\n         2. * christoffel(ti::A, ti::b, ti::c) * dt_u(ti::B) * u(ti::C) +\n         get<0>(dt_u) * dt_u(ti::A)) /\n            u0);\n    const auto dt_u_rollon = tenex::evaluate<ti::A>(roll_on * dt_u(ti::A));\n    const auto dt2_u_rollon = tenex::evaluate<ti::A>(dt_roll_on * dt_u(ti::A) +\n                                                     roll_on * dt2_u(ti::A));\n\n    // Eq.(56) of Wittek:2024gxn\n    tnsr::i<double, Dim> d_dt2_psiR{0.};\n    for (size_t i = 0; i < Dim; ++i) {\n      d_dt2_psiR.get(i) +=\n          -di_imetric.get(i, 0, 0) * dt2_psiR +\n          di_trace_christoffel.get(i, 0) * get(dt_psi_monopole) +\n          trace_christoffel.get(0) * dt_psi_dipole.get(i);\n      for (size_t j = 0; j < Dim; ++j) {\n        d_dt2_psiR.get(i) +=\n            -2. * di_imetric.get(i, 0, j + 1) * dt_psi_dipole.get(j) +\n            di_trace_christoffel.get(i, j + 1) * psi_dipole.get(j);\n      }\n      d_dt2_psiR.get(i) /= get<0, 0>(imetric);\n    }\n\n    // Eq.(57) of Wittek:2024gxn\n    double dt3_psiR = trace_christoffel.get(0) * dt2_psiR;\n    for (size_t i = 0; i < Dim; ++i) {\n      dt3_psiR += -2. * imetric.get(0, i + 1) * d_dt2_psiR.get(i) +\n                  trace_christoffel.get(i + 1) * dt_psi_dipole.get(i);\n    }\n    dt3_psiR /= get<0, 0>(imetric);\n    tnsr::a<double, Dim> dt2_d_psiR;\n    dt2_d_psiR.get(0) = dt3_psiR;\n    for (size_t i = 0; i < Dim; ++i) {\n      dt2_d_psiR.get(0) += 2. * d_dt2_psiR.get(i) * particle_velocity.get(i) +\n                           dt_psi_dipole.get(i) * acc.get(i);\n      dt2_d_psiR.get(i + 1) = d_dt2_psiR.get(i);\n    }\n    auto f = self_force_per_mass(d_psiR, u, charge, evolved_mass, imetric);\n    auto dt_f =\n        dt_self_force_per_mass(d_psiR, dt_d_psiR, u, dt_u_rollon, charge,\n                               evolved_mass, imetric, dt_imetric);\n    auto dt2_f = dt2_self_force_per_mass(\n        d_psiR, dt_d_psiR, dt2_d_psiR, u, dt_u_rollon, dt2_u_rollon, charge,\n        evolved_mass, imetric, dt_imetric, dt2_imetric);\n\n    if (iteration > 1) {\n      for (size_t i = 0; i < 4; ++i) {\n        dt_f.get(i) += dt_mass_factor * f.get(i);\n        dt2_f.get(i) +=\n            2. * dt_mass_factor * dt_f.get(i) + dt2_mass_factor * f.get(i);\n      }\n    }\n    const auto f_roll_on = tenex::evaluate<ti::A>(roll_on * f(ti::A));\n    const auto dt_f_roll_on =\n        tenex::evaluate<ti::A>(dt_roll_on * f(ti::A) + roll_on * dt_f(ti::A));\n    const auto dt2_f_roll_on = tenex::evaluate<ti::A>(\n        dt2_roll_on * f(ti::A) + 2. * dt_roll_on * dt_f(ti::A) +\n        roll_on * dt2_f(ti::A));\n\n    const auto cov_f =\n        Du_self_force_per_mass(f_roll_on, dt_f_roll_on, u, christoffel);\n    const auto dt_cov_f =\n        dt_Du_self_force_per_mass(f_roll_on, dt_f_roll_on, dt2_f_roll_on, u,\n                                  dt_u_rollon, christoffel, dt_christoffel);\n    for (size_t i = 0; i < Dim; ++i) {\n      get(*acceleration_terms)[i + 3] = f_roll_on.get(i);\n      get(*acceleration_terms)[i + 6] = dt_f_roll_on.get(i);\n      get(*acceleration_terms)[i + 9] = cov_f.get(i);\n      get(*acceleration_terms)[i + 12] = dt_cov_f.get(i);\n    }\n  }\n  for (size_t i = 0; i < Dim; ++i) {\n    get(*acceleration_terms)[i] = acc.get(i);\n  }\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/IterateAccelerationTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\n/*!\n * \\brief Computes the next iteration of the acceleration due to scalar self\n * force from the current iteration of the regular field, as well as the\n * quantities required to compute the acceleration terms of the puncture field.\n *\n * \\details Analytic expressions for the computed terms are given in Section V.B\n * of \\cite Wittek:2024gxn.\n */\nstruct IterateAccelerationTerms {\n  static constexpr size_t Dim = 3;\n  using simple_tags = tmpl::list<Tags::AccelerationTerms>;\n  using return_tags = tmpl::list<Tags::AccelerationTerms>;\n  using argument_tags = tmpl::list<\n      Tags::ParticlePositionVelocity<Dim>, Tags::BackgroundQuantities<Dim>,\n      Tags::GeodesicAcceleration<Dim>,\n      Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>,\n      Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                           Frame::Inertial>,\n      Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>,\n      Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 1, Dim,\n                           Frame::Inertial>,\n      Tags::Charge, Tags::Mass, ::Tags::Time, Tags::SelfForceTurnOnTime,\n      Tags::SelfForceTurnOnInterval, Tags::CurrentIteration>;\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> acceleration_terms,\n      const std::array<tnsr::I<double, Dim>, 2>& pos_vel,\n      const tuples::TaggedTuple<\n          gr::Tags::SpacetimeMetric<double, Dim>,\n          gr::Tags::InverseSpacetimeMetric<double, Dim>,\n          gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>,\n          gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>,\n          Tags::TimeDilationFactor>& background,\n      const tnsr::I<double, Dim, Frame::Inertial>& geodesic_acc,\n      const Scalar<double>& psi_monopole, const Scalar<double>& dt_psi_monopole,\n      const tnsr::i<double, Dim, Frame::Inertial>& psi_dipole,\n      const tnsr::i<double, Dim, Frame::Inertial>& dt_psi_dipole, double charge,\n      std::optional<double> mass, double time,\n      std::optional<double> turn_on_time,\n      std::optional<double> turn_on_interval, size_t iteration);\n};\n\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ObserveWorldtubeSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n\n/*!\n * \\brief When Tags::ObserveCoefficientsTrigger is triggered, write the\n * coefficients of the Taylor expansion of the regular field as well as the\n * current particle's position, velocity and acceleration to file. A brief\n * summary is printed to stdout.\n */\nstruct ObserveWorldtubeSolution {\n  using reduction_data = Parallel::ReductionData<\n      Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n      Parallel::ReductionDatum<std::vector<double>, funcl::AssertEqual<>>>;\n\n  using observed_reduction_data_tags =\n      observers::make_reduction_data_tags<tmpl::list<reduction_data>>;\n  static constexpr size_t Dim = 3;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (db::get<Tags::ObserveCoefficientsTrigger>(box).is_triggered(box) and\n        db::get<::Tags::TimeStepId>(box).substep() == 0) {\n      const auto& inertial_particle_position =\n          db::get<Tags::EvolvedPosition<Dim>>(box);\n      const auto& particle_velocity = db::get<Tags::EvolvedVelocity<Dim>>(box);\n      const auto& particle_acceleration =\n          db::get<::Tags::dt<Tags::EvolvedVelocity<Dim>>>(box);\n      const size_t expansion_order = db::get<Tags::ExpansionOrder>(box);\n      const auto& psi_monopole = db::get<\n          Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>>(\n          box);\n      const auto& dt_psi_monopole =\n          db::get<Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                                       Frame::Inertial>>(box);\n\n      // number of components in Taylor series\n      const size_t num_coefs = ((expansion_order + 3) * (expansion_order + 2) *\n                                (expansion_order + 1)) /\n                               6;\n      // offset everything by the 3 * 3 components for position, velocity and\n      // acceleration\n      const size_t pos_offset = 9;\n      std::vector<double> psi_coefs(2 * num_coefs + pos_offset);\n      for (size_t i = 0; i < 3; ++i) {\n        psi_coefs[i] = inertial_particle_position.get(i)[0];\n        psi_coefs[i + 3] = particle_velocity.get(i)[0];\n        psi_coefs[i + 6] = particle_acceleration.get(i)[0];\n      }\n      psi_coefs[0 + pos_offset] = get(psi_monopole);\n      psi_coefs[num_coefs + pos_offset] = get(dt_psi_monopole);\n      if (expansion_order > 0) {\n        const auto& psi_dipole = db::get<\n            Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>>(\n            box);\n        const auto& dt_psi_dipole =\n            db::get<Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 1, Dim,\n                                         Frame::Inertial>>(box);\n        for (size_t i = 0; i < Dim; ++i) {\n          psi_coefs[1 + i + pos_offset] = psi_dipole.get(i);\n          psi_coefs[num_coefs + 1 + i + pos_offset] = dt_psi_dipole.get(i);\n        }\n      }\n      const auto legend = [&expansion_order]() -> std::vector<std::string> {\n        switch (expansion_order) {\n          case (0):\n            return {\"Time\",           \"Position_x\",     \"Position_y\",\n                    \"Position_z\",     \"Velocity_x\",     \"Velocity_y\",\n                    \"Velocity_z\",     \"Acceleration_x\", \"Acceleration_y\",\n                    \"Acceleration_z\", \"Psi0\",           \"dtPsi0\"};\n            break;\n          case (1):\n            return {\"Time\",           \"Position_x\",     \"Position_y\",\n                    \"Position_z\",     \"Velocity_x\",     \"Velocity_y\",\n                    \"Velocity_z\",     \"Acceleration_x\", \"Acceleration_y\",\n                    \"Acceleration_z\", \"Psi0\",           \"Psix\",\n                    \"Psiy\",           \"Psiz\",           \"dtPsi0\",\n                    \"dtPsix\",         \"dtPsiy\",         \"dtPsiz\"};\n            break;\n          default:\n            ERROR(\"requested invalid expansion order\");\n        }\n      }();\n      const auto current_time = db::get<::Tags::Time>(box);\n      if (Parallel::get<Tags::Verbosity>(cache) >= ::Verbosity::Quiet) {\n        Parallel::printf(\n            \"Time: %.16f, field value: %.16f, worldtube radius: %.16f, orbital \"\n            \"radius: %.16f\\n\",\n            current_time, psi_coefs[9], db::get<Tags::WorldtubeRadius>(box),\n            get(magnitude(inertial_particle_position))[0]);\n      }\n\n      const auto observation_id =\n          observers::ObservationId(current_time, \"/Worldtube\");\n      auto& reduction_writer = Parallel::get_parallel_component<\n          observers::ObserverWriter<Metavariables>>(cache);\n\n      Parallel::threaded_action<\n          observers::ThreadedActions::WriteReductionDataRow>(\n          reduction_writer[0], std::string{\"/PsiTaylorCoefs\"}, legend,\n          std::make_tuple(current_time, psi_coefs));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ReceiveElementData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateAcceleration.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/YlmToStf.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n/*!\n * \\brief Adds up the spherical harmonic projections from the different elements\n * abutting the worldtube.\n *\n * \\details This action currently assumes that there is no h-refinement\n * ocurring in the elements abutting the worldtubes. This could be accounted for\n * by checking that data from at least one element has been sent from each\n * abutting block and then using its `ElementId` to figure out the current\n * refinement level and therefore how many elements are expected to send data\n * for each block.\n *\n * DataBox:\n * - Uses:\n *    - `Worldtube::Tags::ExpansionOrder`\n *    - `Worldtube::Tags::ExcisionSphere`\n *    - `Worldtube::Tags::ElementFacesGridCoordinates`\n *    - `Tags::TimeStepId`\n * - Mutates:\n *    - `Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>`\n *    - `Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n *                                     Frame::Inertial>`\n *    - `Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>`\n *    - `Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 1, Dim,\n *                                     Frame::Inertial>`\n */\nstruct ReceiveElementData {\n  static constexpr size_t Dim = 3;\n  using tags_list = tmpl::list<CurvedScalarWave::Tags::Psi,\n                               ::Tags::dt<CurvedScalarWave::Tags::Psi>>;\n  using inbox_tags = tmpl::list<\n      ::CurvedScalarWave::Worldtube::Tags::SphericalHarmonicsInbox<Dim>>;\n  using simple_tags = tmpl::list<\n      Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>,\n      Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                           Frame::Inertial>,\n      Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>,\n      Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 1, Dim,\n                           Frame::Inertial>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t expected_number_of_senders =\n        db::get<Tags::ElementFacesGridCoordinates<Dim>>(box).size();\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    auto& inbox = tuples::get<Tags::SphericalHarmonicsInbox<Dim>>(inboxes);\n    if (inbox.count(time_step_id) == 0 or\n        inbox.at(time_step_id).size() < expected_number_of_senders) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    ASSERT(inbox.at(time_step_id).size() == expected_number_of_senders,\n           \"Expected data from \"\n               << expected_number_of_senders << \" senders, but received \"\n               << inbox.at(time_step_id).size() << \" for TimeStepId \"\n               << time_step_id);\n    const size_t order = db::get<Tags::ExpansionOrder>(box);\n    const size_t num_modes = (order + 1) * (order + 1);\n\n    Variables<tags_list> external_ylm_coefs{num_modes, 0.};\n    for (const auto& [_, element_ylm_coefs] : inbox.at(time_step_id)) {\n      external_ylm_coefs += element_ylm_coefs;\n    }\n    const double wt_radius = db::get<Tags::WorldtubeRadius>(box);\n    external_ylm_coefs /= wt_radius * wt_radius;\n\n    DataVector& psi_ylm_coefs =\n        get(get<CurvedScalarWave::Tags::Psi>(external_ylm_coefs));\n    DataVector& dt_psi_ylm_coefs =\n        get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(external_ylm_coefs));\n\n    const ModalVector psi_ylm_l0(&psi_ylm_coefs[0], 1);\n    const ModalVector dt_psi_ylm_l0(&dt_psi_ylm_coefs[0], 1);\n    tnsr::i<double, Dim, Frame::Inertial> psi_stf_l1{};\n    tnsr::i<double, Dim, Frame::Inertial> dt_psi_stf_l1{};\n    if (order > 0) {\n      ModalVector psi_ylm_l1(&psi_ylm_coefs[1], 3);\n      ModalVector dt_psi_ylm_l1(&dt_psi_ylm_coefs[1], 3);\n      psi_ylm_l1 /= wt_radius;\n      dt_psi_ylm_l1 /= wt_radius;\n      psi_stf_l1 = ylm::ylm_to_stf_1<Frame::Inertial>(psi_ylm_l1);\n      dt_psi_stf_l1 = ylm::ylm_to_stf_1<Frame::Inertial>(dt_psi_ylm_l1);\n    }\n\n    ::Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), ylm::ylm_to_stf_0(psi_ylm_l0),\n        ylm::ylm_to_stf_0(dt_psi_ylm_l0), std::move(psi_stf_l1),\n        std::move(dt_psi_stf_l1));\n    inbox.erase(time_step_id);\n    if (db::get<Tags::CurrentIteration>(box) + 1 <\n        db::get<Tags::MaxIterations>(box)) {\n      db::mutate<Tags::CurrentIteration>(\n          [](const gsl::not_null<size_t*> current_iteration) {\n            *current_iteration += 1;\n          },\n          make_not_null(&box));\n      // still iterating, go to `IterateAccelerationTerms`\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n\n    } else {\n      db::mutate<Tags::CurrentIteration>(\n          [](const gsl::not_null<size_t*> current_iteration) {\n            *current_iteration = 0;\n          },\n          make_not_null(&box));\n      // done iterating\n      return {\n          Parallel::AlgorithmExecution::Continue,\n          tmpl::index_of<ActionList,\n                         ::Actions::MutateApply<UpdateAcceleration>>::value};\n    }\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/SendAccelerationTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ReceiveElementData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n\n/*!\n * \\brief Sends the acceleration terms to worldtube neighbors\n */\ntemplate <typename Metavariables>\nstruct SendAccelerationTerms {\n  static constexpr size_t Dim = Metavariables::volume_dim;\n  using simple_tags = tmpl::list<Tags::AccelerationTerms>;\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& faces_grid_coords =\n        get<Tags::ElementFacesGridCoordinates<Dim>>(box);\n    auto& element_proxies = Parallel::get_parallel_component<\n        typename Metavariables::dg_element_array>(cache);\n    for (const auto& [element_id, _] : faces_grid_coords) {\n      auto data_to_send = db::get<Tags::AccelerationTerms>(box);\n      Parallel::receive_data<Tags::SelfForceInbox<Dim>>(\n          element_proxies[element_id], db::get<::Tags::TimeStepId>(box),\n          std::move(data_to_send));\n    }\n    return {Parallel::AlgorithmExecution::Continue,\n            tmpl::index_of<ActionList, ReceiveElementData>::value};\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/SendToElements.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n/*!\n * \\brief Sends the regular field coefficients to each element abutting the\n * worldtube.\n */\ntemplate <typename Metavariables>\nstruct SendToElements {\n  static constexpr size_t Dim = Metavariables::volume_dim;\n  using psi_tag = CurvedScalarWave::Tags::Psi;\n  using dt_psi_tag = ::Tags::dt<CurvedScalarWave::Tags::Psi>;\n  using tags_to_send = tmpl::list<psi_tag, dt_psi_tag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t order = db::get<Tags::ExpansionOrder>(box);\n    auto& element_proxies = Parallel::get_parallel_component<\n        typename Metavariables::dg_element_array>(cache);\n    const auto& faces_grid_coords =\n        get<Tags::ElementFacesGridCoordinates<Dim>>(box);\n    const auto& psi_l0 =\n        get<Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>>(\n            box);\n    const auto& dt_psi_l0 =\n        get<Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                                 Frame::Inertial>>(box);\n    const auto& psi_l1 =\n        get<Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>>(\n            box);\n    const auto& dt_psi_l1 =\n        get<Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 1, Dim,\n                                 Frame::Inertial>>(box);\n    const size_t num_coefs = order == 0 ? 1 : 4;\n    Variables<tags_to_send> vars_to_send(num_coefs);\n    get(get<psi_tag>(vars_to_send))[0] = get(psi_l0);\n    get(get<dt_psi_tag>(vars_to_send))[0] = get(dt_psi_l0);\n    if (order > 0) {\n      for (size_t i = 0; i < Dim; ++i) {\n        get(get<psi_tag>(vars_to_send))[i + 1] = psi_l1.get(i);\n        get(get<dt_psi_tag>(vars_to_send))[i + 1] = dt_psi_l1.get(i);\n      }\n    }\n    for (const auto& [element_id, _] : faces_grid_coords) {\n      auto vars_to_send_copy = vars_to_send;\n      Parallel::receive_data<Tags::RegularFieldInbox<Dim>>(\n          element_proxies[element_id], db::get<::Tags::TimeStepId>(box),\n          std::move(vars_to_send_copy));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateAcceleration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateAcceleration.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nvoid UpdateAcceleration::apply(\n    const gsl::not_null<\n        Variables<tmpl::list<::Tags::dt<Tags::EvolvedPosition<Dim>>,\n                             ::Tags::dt<Tags::EvolvedVelocity<Dim>>>>*>\n        dt_evolved_vars,\n    const std::array<tnsr::I<double, Dim>, 2>& pos_vel,\n    const tuples::TaggedTuple<\n        gr::Tags::SpacetimeMetric<double, Dim>,\n        gr::Tags::InverseSpacetimeMetric<double, Dim>,\n        gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>,\n        gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>,\n        Tags::TimeDilationFactor>& background,\n    const tnsr::I<double, Dim, Frame::Inertial>& geodesic_acc,\n    const Scalar<double>& psi_monopole, const Scalar<double>& dt_psi_monopole,\n    const tnsr::i<double, Dim, Frame::Inertial>& psi_dipole,\n    const double charge, const std::optional<double> mass,\n    const size_t max_iterations, const double time,\n    const std::optional<double> turn_on_time,\n    const std::optional<double> turn_on_interval) {\n  tnsr::I<double, Dim> self_force_acc(0.);\n  const auto& particle_velocity = pos_vel.at(1);\n  double roll_on = 0.;\n  if (max_iterations > 0 and time > turn_on_time.value()) {\n    roll_on =\n        turn_on_function(time - turn_on_time.value(), turn_on_interval.value());\n    const auto& inverse_metric =\n        get<gr::Tags::InverseSpacetimeMetric<double, Dim>>(background);\n    const auto& dilation_factor = get<Tags::TimeDilationFactor>(background);\n    const double evolved_mass = mass.value() - charge * get(psi_monopole);\n    self_force_acceleration(make_not_null(&self_force_acc), dt_psi_monopole,\n                            psi_dipole, particle_velocity, charge, evolved_mass,\n                            inverse_metric, dilation_factor);\n  }\n  for (size_t i = 0; i < Dim; ++i) {\n    get<::Tags::dt<Tags::EvolvedPosition<Dim>>>(*dt_evolved_vars).get(i)[0] =\n        particle_velocity.get(i);\n    get<::Tags::dt<Tags::EvolvedVelocity<Dim>>>(*dt_evolved_vars).get(i)[0] =\n        geodesic_acc.get(i) + roll_on * self_force_acc.get(i);\n  }\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateAcceleration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\n/*!\n * \\brief Computes the final acceleration of the particle at this time step.\n * \\details If `max_iterations` is 0, the acceleration will simply be\n * geodesic, see `gr::geodesic_acceleration`.  Otherwise, the acceleration due\n * to the scalar self-force is additionally applied to it, see\n * `self_force_acceleration`. This mutator is run on the worldtube\n * singleton chare.\n */\nstruct UpdateAcceleration {\n  static constexpr size_t Dim = 3;\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::EvolvedPosition<Dim>, Tags::EvolvedVelocity<Dim>>>;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  using return_tags = tmpl::list<dt_variables_tag>;\n  using argument_tags = tmpl::list<\n      Tags::ParticlePositionVelocity<Dim>, Tags::BackgroundQuantities<Dim>,\n      Tags::GeodesicAcceleration<Dim>,\n      Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>,\n      Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                           Frame::Inertial>,\n      Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>,\n      Tags::Charge, Tags::Mass, Tags::MaxIterations, ::Tags::Time,\n      Tags::SelfForceTurnOnTime, Tags::SelfForceTurnOnInterval>;\n  static void apply(\n      gsl::not_null<\n          Variables<tmpl::list<::Tags::dt<Tags::EvolvedPosition<Dim>>,\n                               ::Tags::dt<Tags::EvolvedVelocity<Dim>>>>*>\n          dt_evolved_vars,\n      const std::array<tnsr::I<double, Dim>, 2>& pos_vel,\n      const tuples::TaggedTuple<\n          gr::Tags::SpacetimeMetric<double, Dim>,\n          gr::Tags::InverseSpacetimeMetric<double, Dim>,\n          gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>,\n          gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>,\n          Tags::TimeDilationFactor>& background,\n      const tnsr::I<double, Dim, Frame::Inertial>& geodesic_acc,\n      const Scalar<double>& psi_monopole, const Scalar<double>& dt_psi_monopole,\n      const tnsr::i<double, Dim, Frame::Inertial>& psi_dipole, double charge,\n      std::optional<double> mass, size_t max_iterations, double time,\n      std::optional<double> turn_on_time,\n      std::optional<double> turn_on_interval);\n};\n\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateFunctionsOfTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <unordered_map>\n#include <utility>\n\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/RadiusFunctions.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Worldtube.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system {\n// forward declare\nstruct UpdateMultipleFunctionsOfTime;\n}  // namespace control_system\n\nnamespace CurvedScalarWave::Worldtube::Actions {\n\n/*!\n * \\brief Updates the functions of time according to the motion of the\n * worldtube.\n *\n * \\details We demand that the scalar charge is always in the center of the\n * worldtube and therefore deform the grid to the track the particle's motion.\n * In addition, the worldtube and black hole excision sphere radii are adjusted\n * according to smooth_broken_power_law.\n */\nstruct UpdateFunctionsOfTime {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const double current_expiration_time = db::get<Tags::ExpirationTime>(box);\n    const double time = db::get<::Tags::Time>(box);\n    const auto& particle_pos_vel =\n        db::get<Tags::ParticlePositionVelocity<3>>(box);\n    const double& x = get<0>(particle_pos_vel[0]);\n    const double& y = get<1>(particle_pos_vel[0]);\n    const double& xdot = get<0>(particle_pos_vel[1]);\n    const double& ydot = get<1>(particle_pos_vel[1]);\n    const double r = hypot(x, y);\n    const double radial_vel = (xdot * x + ydot * y) / r;\n    const auto& excision_sphere = db::get<Tags::ExcisionSphere<3>>(box);\n    const double grid_radius_particle =\n        get(magnitude(excision_sphere.center()));\n\n    const DataVector angular_update{atan2(y, x),\n                                    (x * ydot - y * xdot) / square(r)};\n    const DataVector expansion_update{r / grid_radius_particle,\n                                      radial_vel / grid_radius_particle};\n\n    const auto& wt_radius_params =\n        db::get<Tags::WorldtubeRadiusParameters>(box);\n    const auto& bh_radius_params =\n        db::get<Tags::BlackHoleRadiusParameters>(box);\n    const double wt_radius_grid = excision_sphere.radius();\n    const double wt_radius_inertial =\n        smooth_broken_power_law(r, wt_radius_params[0], wt_radius_params[1],\n                                wt_radius_params[2], wt_radius_params[3]);\n    const double wt_radius_derivative = smooth_broken_power_law_derivative(\n        r, wt_radius_params[0], wt_radius_params[1], wt_radius_params[2],\n        wt_radius_params[3]);\n    const double wt_radius_time_derivative = wt_radius_derivative * radial_vel;\n\n    const auto& bh_excision_sphere =\n        db::get<domain::Tags::Domain<3>>(box).excision_spheres().at(\n            \"ExcisionSphereB\");\n    const double bh_excision_radius_grid = bh_excision_sphere.radius();\n    const double bh_excision_radius_inertial =\n        smooth_broken_power_law(r, bh_radius_params[0], bh_radius_params[1],\n                                bh_radius_params[2], bh_radius_params[3]);\n    const double bh_excision_radius_derivative =\n        smooth_broken_power_law_derivative(\n            r, bh_radius_params[0], bh_radius_params[1], bh_radius_params[2],\n            bh_radius_params[3]);\n    const double bh_excision_radius_time_derivative =\n        bh_excision_radius_derivative * radial_vel;\n\n    const double sqrt_4_pi = sqrt(4. * M_PI);\n    // The expansion map has already stretched the excision spheres and we need\n    // to account for that.\n    const DataVector size_a_update{\n        sqrt_4_pi * (wt_radius_grid - wt_radius_inertial / expansion_update[0]),\n        sqrt_4_pi *\n            (wt_radius_inertial * expansion_update[1] -\n             wt_radius_time_derivative * expansion_update[0]) /\n            square(expansion_update[0])};\n\n    const DataVector size_b_update{\n        sqrt_4_pi * (bh_excision_radius_grid -\n                     bh_excision_radius_inertial / expansion_update[0]),\n        sqrt_4_pi *\n            (bh_excision_radius_inertial * expansion_update[1] -\n             bh_excision_radius_time_derivative * expansion_update[0]) /\n            square(expansion_update[0])};\n\n    // we set the new expiration time to 1/100th of a time step next to the\n    // current time. This is small enough that it can handle rapid time step\n    // decreases but large enough to avoid floating point precision issues.\n    const double new_expiration_time =\n        time +\n        0.01 * (db::get<::Tags::Next<::Tags::TimeStepId>>(box).substep_time() -\n                time);\n\n    db::mutate<Tags::ExpirationTime>(\n        [&new_expiration_time](const auto expiration_time) {\n          *expiration_time = new_expiration_time;\n        },\n        make_not_null(&box));\n    db::mutate<Tags::WorldtubeRadius>(\n        [&wt_radius_inertial](const auto wt_radius) {\n          *wt_radius = wt_radius_inertial;\n        },\n        make_not_null(&box));\n    std::unordered_map<std::string, std::pair<DataVector, double>>\n        all_updates{};\n    all_updates[\"Rotation\"] =\n        std::make_pair(angular_update, new_expiration_time);\n    all_updates[\"Expansion\"] =\n        std::make_pair(expansion_update, new_expiration_time);\n    all_updates[\"SizeA\"] = std::make_pair(size_a_update, new_expiration_time);\n    all_updates[\"SizeB\"] = std::make_pair(size_b_update, new_expiration_time);\n\n    Parallel::mutate<::domain::Tags::FunctionsOfTime,\n                     control_system::UpdateMultipleFunctionsOfTime>(\n        cache, current_expiration_time, all_updates);\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ChangeSlabSize.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeElementFacesGridCoordinates.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeEvolvedVariables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/IterateAccelerationTerms.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ObserveWorldtubeSolution.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ReceiveElementData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/SendAccelerationTerms.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/SendToElements.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateAcceleration.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateFunctionsOfTime.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/System.hpp\"\n#include \"IO/Observer/Actions/RegisterSingleton.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Tags/ResourceInfo.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/SelfStart.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\nstruct Registration {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& /*box*/,\n                const ArrayIndex& /*array_index*/) {\n    return {observers::TypeOfObservation::Reduction,\n            observers::ObservationKey{\"/Worldtube\"}};\n  }\n};\n\n/*!\n * \\brief The singleton component that represents the worldtube.\n *\n * \\details The component receives from and sends data to the elements abutting\n * the worldtube. It holds and calculates a solution for the regular field\n * \\f$\\Psi^R\\f$ which valid in a neighborhood of the scalar charge.\n */\ntemplate <class Metavariables>\nstruct WorldtubeSingleton {\n  static constexpr size_t Dim = Metavariables::volume_dim;\n  using chare_type = ::Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using evolved_vars = ::Tags::Variables<\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi>>;\n  // LTS not currently supported\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n\n  using initialization_actions = tmpl::list<\n      ::Initialization::Actions::InitializeItems<\n          ::Initialization::TimeStepping<Metavariables, TimeStepperBase>,\n          Initialization::InitializeEvolvedVariables,\n          Initialization::InitializeElementFacesGridCoordinates<Dim>>,\n      ::Initialization::Actions::AddComputeTags<\n          tmpl::list<Tags::EvolvedParticlePositionVelocityCompute<Dim>,\n                     Tags::GeodesicAccelerationCompute<Dim>,\n                     Tags::BackgroundQuantitiesCompute<Dim>>>,\n      Parallel::Actions::TerminatePhase>;\n\n  using worldtube_system = System<Dim>;\n  using step_actions = tmpl::list<\n      Actions::UpdateFunctionsOfTime, Actions::ChangeSlabSize,\n      Actions::ReceiveElementData,\n      ::Actions::MutateApply<IterateAccelerationTerms>,\n      Actions::SendAccelerationTerms<Metavariables>,\n      ::Actions::MutateApply<UpdateAcceleration>,\n      ::Actions::MutateApply<RecordTimeStepperData<worldtube_system>>,\n      ::Actions::MutateApply<UpdateU<worldtube_system, local_time_stepping>>,\n      ::Actions::MutateApply<CleanHistory<worldtube_system>>,\n      Actions::SendToElements<Metavariables>,\n      domain::Actions::CheckFunctionsOfTimeAreReady<Dim>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialization_actions>,\n      Parallel::PhaseActions<\n          Parallel::Phase::InitializeTimeStepperHistory,\n          SelfStart::self_start_procedure<step_actions, worldtube_system>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<observers::Actions::RegisterSingletonWithObserverWriter<\n                         Registration>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Restart,\n          tmpl::list<observers::Actions::RegisterSingletonWithObserverWriter<\n                         Registration>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::ObserveWorldtubeSolution, step_actions,\n                     ::Actions::MutateApply<AdvanceTime<>>,\n                     PhaseControl::Actions::ExecutePhaseChange>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const typename Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<WorldtubeSingleton<metavariables>>(\n        local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::EvolvedPosition<Dim>, Tags::EvolvedVelocity<Dim>>>;\n};\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace CurvedScalarWave::Worldtube::OptionTags {\nSelfForceOptions::SelfForceOptions() = default;\nSelfForceOptions::SelfForceOptions(const double mass_in,\n                                   const size_t iterations_in,\n                                   const double turn_on_time_in,\n                                   const double turn_on_interval_in)\n    : mass(mass_in),\n      iterations(iterations_in),\n      turn_on_time(turn_on_time_in),\n      turn_on_interval(turn_on_interval_in) {}\n\nvoid SelfForceOptions::pup(PUP::er& p) {\n  p | mass;\n  p | iterations;\n  p | turn_on_time;\n  p | turn_on_interval;\n}\n\ntemplate <bool IsWorldtube>\nRadiusOptions<IsWorldtube>::RadiusOptions() = default;\n\ntemplate <bool IsWorldtube>\nRadiusOptions<IsWorldtube>::RadiusOptions(double exponent_in,\n                                          double amplitude_in,\n                                          double transition_radius_in,\n                                          double transition_width_in)\n    : exponent(exponent_in),\n      amplitude(amplitude_in),\n      transition_radius(transition_radius_in),\n      transition_width(transition_width_in) {}\n\ntemplate <bool IsWorldtube>\nvoid RadiusOptions<IsWorldtube>::pup(PUP::er& p) {\n  p | exponent;\n  p | amplitude;\n  p | transition_radius;\n  p | transition_width;\n}\n\ntemplate struct RadiusOptions<true>;\ntemplate struct RadiusOptions<false>;\n\n}  // namespace CurvedScalarWave::Worldtube::OptionTags\n\nnamespace CurvedScalarWave::Worldtube::Tags {\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\ntemplate <size_t Dim, typename Frame, bool Centered>\nvoid FaceCoordinatesCompute<Dim, Frame, Centered>::function(\n    const gsl::not_null<std::optional<tnsr::I<DataVector, Dim, Frame>>*> result,\n    const ::ExcisionSphere<Dim>& excision_sphere, const Element<Dim>& element,\n    const tnsr::I<DataVector, Dim, Frame>& coords, const Mesh<Dim>& mesh) {\n  const auto direction = excision_sphere.abutting_direction(element.id());\n  if (direction.has_value()) {\n    ASSERT(\n        mesh.quadrature(direction.value().dimension()) ==\n            Spectral::Quadrature::GaussLobatto,\n        \"Expected GaussLobatto quadrature. Other quadratures are disabled \"\n        \"because interpolating the coordinates incurs an unnecessary error.\");\n    const size_t grid_size =\n        mesh.slice_away(direction->dimension()).number_of_grid_points();\n    if (result->has_value()) {\n      set_number_of_grid_points(make_not_null(&(result->value())), grid_size);\n    } else {\n      result->emplace(grid_size);\n    }\n    data_on_slice(make_not_null(&(result->value())), coords, mesh.extents(),\n                  direction.value().dimension(),\n                  index_to_slice_at(mesh.extents(), direction.value()));\n    if constexpr (Centered) {\n      if constexpr (not std::is_same_v<Frame, ::Frame::Grid>) {\n        ERROR(\"Should be grid frame\");\n      }\n      for (size_t i = 0; i < Dim; ++i) {\n        result->value().get(i) -= excision_sphere.center().get(i);\n      }\n    }\n  } else {\n    result->reset();\n  }\n}\n\ntemplate <size_t Dim, typename Frame, bool Centered>\nvoid FaceCoordinatesCompute<Dim, Frame, Centered>::function(\n    const gsl::not_null<\n        std::optional<tnsr::I<DataVector, Dim, ::Frame::Inertial>>*>\n        result,\n    const ::ExcisionSphere<Dim>& excision_sphere, const Element<Dim>& element,\n    const tnsr::I<DataVector, Dim, ::Frame::Inertial>& inertial_coords,\n    const Mesh<Dim>& mesh,\n    const std::array<tnsr::I<double, Dim>, 2>& particle_position_velocity) {\n  if constexpr (not(Centered and std::is_same_v<Frame, ::Frame::Inertial>)) {\n    ERROR(\"Should be centered in inertial frame\");\n  }\n  const auto direction = excision_sphere.abutting_direction(element.id());\n  if (direction.has_value()) {\n    ASSERT(\n        mesh.quadrature(direction.value().dimension()) ==\n            Spectral::Quadrature::GaussLobatto,\n        \"Expected GaussLobatto quadrature. Other quadratures are disabled \"\n        \"because interpolating the coordinates incurs an unnecessary error.\");\n    const size_t grid_size =\n        mesh.slice_away(direction->dimension()).number_of_grid_points();\n    if (result->has_value()) {\n      set_number_of_grid_points(make_not_null(&(result->value())), grid_size);\n    } else {\n      result->emplace(grid_size);\n    }\n    data_on_slice(make_not_null(&(result->value())), inertial_coords,\n                  mesh.extents(), direction.value().dimension(),\n                  index_to_slice_at(mesh.extents(), direction.value()));\n    for (size_t i = 0; i < Dim; ++i) {\n      result->value().get(i) -= particle_position_velocity.at(0).get(i);\n    }\n  } else {\n    result->reset();\n  }\n}\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__)\n\ntemplate <size_t Dim>\nvoid PunctureFieldCompute<Dim>::function(\n    const gsl::not_null<return_type*> result,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        inertial_face_coords_centered,\n    const std::array<tnsr::I<double, Dim, ::Frame::Inertial>, 2>&\n        particle_position_velocity,\n    const tnsr::I<double, Dim>& particle_acceleration, const double charge,\n    const size_t expansion_order) {\n  if (inertial_face_coords_centered.has_value()) {\n    if (not result->has_value()) {\n      result->emplace(get<0>(inertial_face_coords_centered.value()).size());\n    }\n    puncture_field(make_not_null(&(result->value())),\n                   inertial_face_coords_centered.value(),\n                   particle_position_velocity[0], particle_position_velocity[1],\n                   particle_acceleration, 1., expansion_order);\n    result->value() *= charge;\n  } else {\n    result->reset();\n  }\n}\n\ntemplate <size_t Dim>\nvoid ParticlePositionVelocityCompute<Dim>::function(\n    gsl::not_null<std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>*>\n        position_and_velocity,\n    const ::ExcisionSphere<Dim>& excision_sphere, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  ASSERT(excision_sphere.is_time_dependent(),\n         \"The worldtube simulation requires time-dependent maps.\");\n  const auto& grid_to_inertial_map =\n      excision_sphere.moving_mesh_grid_to_inertial_map();\n  auto mapped_tuple = grid_to_inertial_map.coords_frame_velocity_jacobians(\n      excision_sphere.center(), time, functions_of_time);\n  (*position_and_velocity)[0] = std::move(std::get<0>(mapped_tuple));\n  (*position_and_velocity)[1] = std::move(std::get<3>(mapped_tuple));\n}\n\ntemplate <size_t Dim>\nvoid EvolvedParticlePositionVelocityCompute<Dim>::function(\n    gsl::not_null<std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>*>\n        position_velocity,\n    const tnsr::I<DataVector, Dim>& evolved_position,\n    const tnsr::I<DataVector, Dim>& evolved_velocity) {\n  for (size_t i = 0; i < Dim; ++i) {\n    (*position_velocity)[0].get(i) = evolved_position.get(i)[0];\n    (*position_velocity)[1].get(i) = evolved_velocity.get(i)[0];\n  }\n}\n\ntemplate <size_t Dim>\nvoid GeodesicAccelerationCompute<Dim>::function(\n    gsl::not_null<tnsr::I<double, Dim, Frame::Inertial>*> acceleration,\n    const std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>&\n        position_velocity,\n    const gr::Solutions::KerrSchild& background_spacetime) {\n  const auto christoffel = get<\n      gr::Tags::SpacetimeChristoffelSecondKind<double, Dim, Frame::Inertial>>(\n      background_spacetime.variables(\n          position_velocity.at(0), 0.,\n          tmpl::list<gr::Tags::SpacetimeChristoffelSecondKind<\n              double, Dim, Frame::Inertial>>{}));\n  gr::geodesic_acceleration(acceleration, position_velocity.at(1), christoffel);\n}\n\ntemplate <size_t Dim>\nvoid BackgroundQuantitiesCompute<Dim>::function(\n    gsl::not_null<return_type*> result,\n    const std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>&\n        position_velocity,\n    const gr::Solutions::KerrSchild& background_spacetime) {\n  auto kerr_schild_quantities = background_spacetime.variables(\n      position_velocity.at(0), 0.,\n      tmpl::list<gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                 gr::Tags::SpatialMetric<double, Dim>,\n                 gr::Tags::InverseSpatialMetric<double, Dim>,\n                 gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>{});\n  auto metric = gr::spacetime_metric(\n      get<gr::Tags::Lapse<double>>(kerr_schild_quantities),\n      get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities),\n      get<gr::Tags::SpatialMetric<double, Dim>>(kerr_schild_quantities));\n  auto inverse_metric = gr::inverse_spacetime_metric(\n      get<gr::Tags::Lapse<double>>(kerr_schild_quantities),\n      get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities),\n      get<gr::Tags::InverseSpatialMetric<double, Dim>>(kerr_schild_quantities));\n\n  const auto& velocity = position_velocity.at(1);\n  double temp = metric.get(0, 0);\n  for (size_t i = 0; i < Dim; ++i) {\n    temp += 2. * metric.get(i + 1, 0) * velocity.get(i);\n    for (size_t j = 0; j < Dim; ++j) {\n      temp += metric.get(i + 1, j + 1) * velocity.get(i) * velocity.get(j);\n    }\n  }\n  auto& christoffel =\n      get<gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>(\n          kerr_schild_quantities);\n  auto& trace_christoffel =\n      get<gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>>(*result);\n  tenex::evaluate<ti::C>(\n      make_not_null(&trace_christoffel),\n      inverse_metric(ti::A, ti::B) * christoffel(ti::C, ti::a, ti::b));\n  get(get<TimeDilationFactor>(*result)) = sqrt(-1. / temp);\n  get<gr::Tags::SpacetimeMetric<double, Dim>>(*result) = std::move(metric);\n  get<gr::Tags::InverseSpacetimeMetric<double, Dim>>(*result) =\n      std::move(inverse_metric);\n  get<gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>(*result) =\n      std::move(christoffel);\n}\n\nvoid FaceQuantitiesCompute::function(\n    gsl::not_null<return_type*> result, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi, const tnsr::i<DataVector, Dim>& phi,\n    const tnsr::I<DataVector, Dim>& shift, const Scalar<DataVector>& lapse,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian,\n    const ::ExcisionSphere<Dim>& excision_sphere, const Element<Dim>& element,\n    const Mesh<Dim>& mesh) {\n  const auto direction = excision_sphere.abutting_direction(element.id());\n  if (not direction.has_value()) {\n    result->reset();\n    return;\n  }\n  const auto face_mesh = mesh.slice_away(direction->dimension());\n  const size_t face_size = face_mesh.number_of_grid_points();\n  const auto slice_index = index_to_slice_at(mesh.extents(), direction.value());\n  const auto sliced_dim = direction.value().dimension();\n  Variables<tags_to_slice_to_face> vars_on_face(face_size);\n  // we use a templated lambda here. This could also be solved with a\n  // non-owning variables but this has less overhead.\n  const auto slice_to_face =\n      [&vars_on_face, &mesh, &sliced_dim, &slice_index]<typename tag_to_slice>(\n          const typename tag_to_slice::type& volume_field) ->\n      typename tag_to_slice::type& {\n        data_on_slice(make_not_null(&get<tag_to_slice>(vars_on_face)),\n                      volume_field, mesh.extents(), sliced_dim, slice_index);\n        return get<tag_to_slice>(vars_on_face);\n      };\n  const auto& face_psi =\n      slice_to_face.operator()<CurvedScalarWave::Tags::Psi>(psi);\n  const auto& face_pi =\n      slice_to_face.operator()<CurvedScalarWave::Tags::Pi>(pi);\n  const auto& face_phi =\n      slice_to_face.operator()<CurvedScalarWave::Tags::Phi<Dim>>(phi);\n  const auto& face_shift =\n      slice_to_face.operator()<gr::Tags::Shift<DataVector, Dim>>(shift);\n  const auto& face_lapse =\n      slice_to_face.operator()<gr::Tags::Lapse<DataVector>>(lapse);\n  const auto& face_inv_jacobian =\n      slice_to_face.operator()<domain::Tags::InverseJacobian<\n          Dim, Frame::ElementLogical, Frame::Inertial>>(inv_jacobian);\n\n  if (not result->has_value()) {\n    result->emplace(face_size);\n  }\n  get<CurvedScalarWave::Tags::Psi>(result->value()) = face_psi;\n  get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(result->value())) =\n      -get(face_lapse) * get(face_pi) + get(dot_product(face_shift, face_phi));\n  euclidean_area_element(\n      make_not_null(\n          &get<gr::surfaces::Tags::AreaElement<DataVector>>(result->value())),\n      face_inv_jacobian, direction.value());\n}\n\nvoid ConstraintGamma1Compute::function(\n    gsl::not_null<Scalar<DataVector>*> gamma1,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords) {\n  get(*gamma1).destructive_resize(get<0>(coords).size());\n  get(*gamma1) = 0.;\n}\n\nvoid ConstraintGamma2Compute::function(\n    gsl::not_null<Scalar<DataVector>*> gamma2,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    const std::array<tnsr::I<double, 3, Frame::Inertial>, 2>& pos_vel) {\n  get(*gamma2).destructive_resize(get<0>(coords).size());\n  const auto& pos = pos_vel[0];\n\n  // re-use allocation\n  auto& centered_radii = get(*gamma2);\n  centered_radii = sqrt(square(get<0>(coords) - get<0>(pos)) +\n                        square(get<1>(coords) - get<1>(pos)) +\n                        square(get<2>(coords) - get<2>(pos)));\n  const double amplitude = 10.;\n  const double sigma = 1e-1;\n  const double constant = 1e-3;\n  get(*gamma2) = amplitude * exp(-square(sigma * centered_radii)) + constant;\n}\n\ntemplate struct BackgroundQuantitiesCompute<3>;\ntemplate struct EvolvedParticlePositionVelocityCompute<3>;\ntemplate struct GeodesicAccelerationCompute<3>;\ntemplate struct ParticlePositionVelocityCompute<3>;\ntemplate struct PunctureFieldCompute<3>;\n\ntemplate struct FaceCoordinatesCompute<3, Frame::Grid, true>;\ntemplate struct FaceCoordinatesCompute<3, Frame::Grid, false>;\ntemplate struct FaceCoordinatesCompute<3, Frame::Inertial, true>;\ntemplate struct FaceCoordinatesCompute<3, Frame::Inertial, false>;\n\n}  // namespace CurvedScalarWave::Worldtube::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BackgroundSpacetime.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace OptionTags {\nstruct InitialTime;\n}  // namespace OptionTags\n/// \\endcond\n\nnamespace CurvedScalarWave::Worldtube {\n/*!\n * \\brief Option tags for the worldtube\n */\nnamespace OptionTags {\n/*!\n * \\brief Options for the worldtube\n */\nstruct Worldtube {\n  static constexpr Options::String help = {\"Options for the Worldtube\"};\n};\n\n/*!\n * \\brief The value of the scalar charge in units of the black hole mass M.\n */\nstruct Charge {\n  using type = double;\n  static constexpr Options::String help{\n      \"The value of the scalar charge in units of the black hole mass M.\"};\n  using group = Worldtube;\n};\n\n/*!\n * \\brief The dimensionless spin vector of the central black hole.\n */\nstruct Spin {\n  using type = std::array<double, 3>;\n  static constexpr Options::String help{\n      \"The value of the scalar charge in units of the black hole mass M.\"};\n  using group = Worldtube;\n};\n\n/*!\n * \\brief Options for the scalar self-force. Select `None` for a purely geodesic\n * evolution\n *\n *\\details The self force is turned on using the smooth transition function\n *\n * \\begin{equation}\n * w(t) = 1 - \\exp{ \\left(- \\left(\\frac{t - t_1}{\\sigma} \\right)^4 \\right)}.\n * \\end{equation}\n *\n * The turn on time is given by \\f$t_1\\f$ and the turn on interval is given by\n *\\f$sigma\\f$.\n */\nstruct SelfForceOptions {\n  static constexpr Options::String help = {\n      \"Options for the scalar self-force. Select `None` for a purely geodesic \"\n      \"evolution\"};\n  using group = Worldtube;\n  using type = Options::Auto<SelfForceOptions, Options::AutoLabel::None>;\n\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help{\n        \"The mass of the scalar particle in units of the black hole mass M.\"};\n    static double lower_bound() { return 0.; }\n  };\n\n  struct Iterations {\n    using type = size_t;\n    static constexpr Options::String help{\n        \"The number of iterations used to compute the particle acceleration. \"\n        \"Must be at least 1 as 0 iterations corresponds to the geodesic \"\n        \"acceleration.\"};\n    static size_t lower_bound() { return 1; }\n  };\n\n  struct TurnOnTime {\n    using type = double;\n    static constexpr Options::String help{\n        \"The time at which the scalar self force is turned on.\"};\n    static double lower_bound() { return 0.; }\n  };\n\n  struct TurnOnInterval {\n    using type = double;\n    static constexpr Options::String help{\n        \"The interval over which the scalar self force is smoothly turned on. \"\n        \"We require a minimum of 1 M for the interval.\"};\n    static double lower_bound() { return 1.; }\n  };\n\n  SelfForceOptions();\n  SelfForceOptions(double mass_in, size_t iterations_in, double turn_on_time_in,\n                   double turn_on_interval_in);\n  void pup(PUP::er& p);\n\n  using options = tmpl::list<Mass, Iterations, TurnOnTime, TurnOnInterval>;\n\n  double mass{};\n  size_t iterations{};\n  double turn_on_time{};\n  double turn_on_interval{};\n};\n\n/*!\n * \\brief Options for the excision sphere radii which are adjusted according to\n * `smooth_broken_power_law`. If `IsWorldtube` is true, these options control\n * the worldtube growth around the scalar charge. Else, they control the growth\n * of the excision sphere within the central black hole.\n */\ntemplate <bool IsWorldtube>\nstruct RadiusOptions {\n  static constexpr Options::String help = {\n      \"Options for the radii of the excision spheres\"};\n  using group = Worldtube;\n  using type = RadiusOptions;\n\n  static std::string name() {\n    if constexpr (IsWorldtube) {\n      return \"WorldtubeRadiusOptions\";\n    } else {\n      return \"BlackHoleRadiusOptions\";\n    }\n  }\n\n  struct Exponent {\n    using type = double;\n    static constexpr Options::String help{\n        \"The exponent alpha according to which the excision sphere grows with \"\n        \"orbital radius until the transition radius.\"};\n    static double lower_bound() { return 0.; }\n    static double upper_bound() { return 4.; }\n  };\n\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help{\n        \"The amplitude A of the smoothly broken power law.\"};\n    static double lower_bound() { return 0.; }\n  };\n\n  struct TransitionRadius {\n    using type = double;\n    static constexpr Options::String help{\n        \"The transition radius rb of the smoothly broken power law. At this \"\n        \"point the radius transitions to a constant value.\"};\n    static double lower_bound() { return 0.; }\n  };\n\n  struct TransitionWidth {\n    using type = double;\n    static constexpr Options::String help{\n        \"The width delta of the transition region.\"};\n    static double lower_bound() { return 1e-3; }\n  };\n\n  RadiusOptions();\n  RadiusOptions(double exponent_in, double amplitude_in,\n                double transition_radius_in, double transition_width_in);\n  void pup(PUP::er& p);\n\n  using options =\n      tmpl::list<Exponent, Amplitude, TransitionRadius, TransitionWidth>;\n\n  double exponent{};\n  double amplitude{};\n  double transition_radius{};\n  double transition_width{};\n};\n\n/*!\n * \\brief Name of the excision sphere designated to act as a worldtube\n */\nstruct ExcisionSphere {\n  using type = std::string;\n  static constexpr Options::String help{\n      \"The name of the excision sphere as returned by the domain.\"};\n  using group = Worldtube;\n};\n\n/*!\n * \\brief Triggers at which to write the coefficients of the worldtube's\n * internal Taylor series to file.\n */\nstruct ObserveCoefficientsTrigger {\n  using type = std::unique_ptr<Trigger>;\n  static constexpr Options::String help{\n      \"Specifies a non-dense trigger in which the coefficients of the internal \"\n      \"regular field expansion are written to file.\"};\n  using group = Worldtube;\n};\n\n/*!\n * \\brief The internal expansion order of the worldtube solution.\n */\nstruct ExpansionOrder {\n  using type = size_t;\n  static constexpr Options::String help{\n      \"The internal expansion order of the worldtube solution. Currently \"\n      \"orders 0 and 1 are implemented\"};\n  static size_t upper_bound() { return 1; }\n  using group = Worldtube;\n};\n\n/*!\n * \\brief The verbosity of the worldtube executable.\n */\nstruct Verbosity {\n  using type = ::Verbosity;\n  static constexpr Options::String help{\n      \"Gives the verbosity of the worldtube executable.\"};\n  using group = Worldtube;\n};\n}  // namespace OptionTags\n\n/*!\n * \\brief Tags related to the worldtube\n */\nnamespace Tags {\n/*!\n * \\brief The excision sphere corresponding to the worldtube\n */\ntemplate <size_t Dim>\nstruct ExcisionSphere : db::SimpleTag {\n  using type = ::ExcisionSphere<Dim>;\n  using option_tags = tmpl::list<domain::OptionTags::DomainCreator<Dim>,\n                                 OptionTags::ExcisionSphere>;\n  static constexpr bool pass_metavariables = false;\n  static ::ExcisionSphere<Dim> create_from_options(\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator,\n      const std::string& excision_sphere) {\n    const auto domain = domain_creator->create_domain();\n    const auto& excision_spheres = domain.excision_spheres();\n    if (excision_spheres.count(excision_sphere) == 0) {\n      ERROR(\"Specified excision sphere '\"\n            << excision_sphere\n            << \"' not available. Available excision spheres are: \"\n            << keys_of(excision_spheres));\n    }\n    return excision_spheres.at(excision_sphere);\n  }\n};\n\n/*!\n * \\brief Triggers at which to write the coefficients of the worldtube's\n * internal Taylor series to file.\n */\nstruct ObserveCoefficientsTrigger : db::SimpleTag {\n  using type = std::unique_ptr<Trigger>;\n  using option_tags = tmpl::list<OptionTags::ObserveCoefficientsTrigger>;\n  static constexpr bool pass_metavariables = false;\n  static std::unique_ptr<Trigger> create_from_options(\n      const std::unique_ptr<Trigger>& trigger) {\n    return serialize_and_deserialize<type>(trigger);\n  }\n};\n\n/*!\n * \\brief The value of the scalar charge\n */\nstruct Charge : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::Charge>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double charge) { return charge; };\n};\n\n/*!\n * \\brief The dimensionless spin vector of the central black hole.\n */\nstruct Spin : db::SimpleTag {\n  using type = std::array<double, 3>;\n  using option_tags = tmpl::list<OptionTags::Spin>;\n  static constexpr bool pass_metavariables = false;\n  static std::array<double, 3> create_from_options(\n      const std::array<double, 3> spin) {\n    return spin;\n  };\n};\n\n/*!\n * \\brief The time at which the self-force is smoothly turned on.\n *\n * \\details The self force is turned on using the smooth transition function\n *\n * \\begin{equation}\n * w(t) = 1 - \\exp{ \\left(- \\left(\\frac{t - t_1}{\\sigma} \\right)^4 \\right)}.\n * \\end{equation}\n *\n * The turn on time is given by \\f$t_1\\f$.\n */\nstruct SelfForceTurnOnTime : db::SimpleTag {\n  using type = std::optional<double>;\n  using option_tags = tmpl::list<OptionTags::SelfForceOptions>;\n  static constexpr bool pass_metavariables = false;\n  static std::optional<double> create_from_options(\n      const std::optional<OptionTags::SelfForceOptions>& self_force_options) {\n    return self_force_options.has_value()\n               ? std::make_optional(self_force_options->turn_on_time)\n               : std::nullopt;\n  };\n};\n\n/*!\n * \\brief The interval over which the self-force is smoothly turned on.\n *\n * \\details The self force is turned on using the smooth transition function\n *\n * \\begin{equation}\n * w(t) = 1 - \\exp{ \\left(- \\left(\\frac{t - t_1}{\\sigma} \\right)^4 \\right)}.\n * \\end{equation}\n *\n * The turn on interval is given by \\f$\\sigma\\f$.\n */\nstruct SelfForceTurnOnInterval : db::SimpleTag {\n  using type = std::optional<double>;\n  using option_tags = tmpl::list<OptionTags::SelfForceOptions>;\n  static constexpr bool pass_metavariables = false;\n  static std::optional<double> create_from_options(\n      const std::optional<OptionTags::SelfForceOptions>& self_force_options) {\n    return self_force_options.has_value()\n               ? std::make_optional(self_force_options->turn_on_interval)\n               : std::nullopt;\n  };\n};\n\n/*!\n * \\brief The mass of the scalar charge. Only has a value if the scalar self\n * force is applied.\n */\nstruct Mass : db::SimpleTag {\n  using type = std::optional<double>;\n  using option_tags = tmpl::list<OptionTags::SelfForceOptions>;\n  static constexpr bool pass_metavariables = false;\n  static std::optional<double> create_from_options(\n      const std::optional<OptionTags::SelfForceOptions>& self_force_options) {\n    return self_force_options.has_value()\n               ? std::make_optional(self_force_options->mass)\n               : std::nullopt;\n  }\n};\n\n/*!\n * \\brief The maximum number of iterations that will be applied to the\n * acceleration of the particle.\n */\nstruct MaxIterations : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::SelfForceOptions>;\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(\n      const std::optional<OptionTags::SelfForceOptions>& self_force_options) {\n    return self_force_options.has_value() ? self_force_options->iterations : 0;\n  }\n};\n\n/*!\n * \\brief The verbosity of the worldtube executable.\n */\nstruct Verbosity : db::SimpleTag {\n  using type = ::Verbosity;\n  using option_tags = tmpl::list<OptionTags::Verbosity>;\n  static constexpr bool pass_metavariables = false;\n  static ::Verbosity create_from_options(const ::Verbosity& verbosity) {\n    return verbosity;\n  }\n};\n\n/*!\n * \\brief The current number of iterations that has been applied to the\n * acceleration of the particle.\n */\nstruct CurrentIteration : db::SimpleTag {\n  using type = size_t;\n};\n\n/*!\n * \\brief The current expiration time of the functions of time which are\n * controlled by the worldtube singleton.\n */\nstruct ExpirationTime : db::SimpleTag {\n  using type = double;\n};\n\n/*!\n * \\brief The current worldtube radius held by the singleton.\n */\nstruct WorldtubeRadius : db::SimpleTag {\n  using type = double;\n};\n\n/*!\n * \\brief The initial position and velocity of the scalar charge in inertial\n * coordinates.\n */\nstruct InitialPositionAndVelocity : db::SimpleTag {\n  using type = std::array<tnsr::I<double, 3, Frame::Inertial>, 2>;\n  using option_tags =\n      tmpl::list<domain::OptionTags::DomainCreator<3>,\n                 OptionTags::ExcisionSphere, ::OptionTags::InitialTime>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<3>>& domain_creator,\n      const std::string& excision_sphere_name, const double initial_time) {\n    // only evaluated at initial time, so expiration times don't matter\n    const auto initial_fot = domain_creator->functions_of_time();\n    const auto domain = domain_creator->create_domain();\n    const auto& excision_sphere =\n        domain.excision_spheres().at(excision_sphere_name);\n    ASSERT(excision_sphere.is_time_dependent(),\n           \"excision_sphere not time dependent\");\n    const auto& maps = excision_sphere.moving_mesh_grid_to_inertial_map();\n    const auto mapped_tuple = maps.coords_frame_velocity_jacobians(\n        excision_sphere.center(), initial_time, initial_fot);\n    return {std::get<0>(mapped_tuple), std::get<3>(mapped_tuple)};\n  }\n};\n\n/// @{\n/*!\n * \\brief The position and velocity of the scalar charge particle orbiting a\n * central black hole given in inertial coordinates. This compute tag is meant\n * to be used by the elements.\n */\ntemplate <size_t Dim>\nstruct ParticlePositionVelocity : db::SimpleTag {\n  using type = std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>;\n};\n\ntemplate <size_t Dim>\nstruct ParticlePositionVelocityCompute : ParticlePositionVelocity<Dim>,\n                                         db::ComputeTag {\n  using base = ParticlePositionVelocity<Dim>;\n  using return_type = std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>;\n  using argument_tags = tmpl::list<ExcisionSphere<Dim>, ::Tags::Time,\n                                   domain::Tags::FunctionsOfTime>;\n  static void function(\n      gsl::not_null<std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>*>\n          position_velocity,\n      const ::ExcisionSphere<Dim>& excision_sphere, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time);\n};\n/// @}\n\n/*!\n * \\brief The position of the scalar charge evolved by the worldtube singleton.\n * This tag is meant to be used by the worldtube singleton to evolve the orbit.\n */\ntemplate <size_t Dim>\nstruct EvolvedPosition : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n\n/*!\n * \\brief The velocity of the scalar charge evolved by the worldtube singleton.\n * This tag is meant to be used by the worldtube singleton to evolve the orbit.\n */\ntemplate <size_t Dim>\nstruct EvolvedVelocity : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n\n/*!\n * \\brief The position and velocity of the scalar charge particle orbiting a\n * central black hole given in inertial coordinates. This compute tag is meant\n * to be used by the worldtube singleton which evolves the position and velocity\n * according to an ODE along with the DG evolution.\n */\ntemplate <size_t Dim>\nstruct EvolvedParticlePositionVelocityCompute : ParticlePositionVelocity<Dim>,\n                                                db::ComputeTag {\n  using base = ParticlePositionVelocity<Dim>;\n  using return_type = std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>;\n  using argument_tags = tmpl::list<EvolvedPosition<Dim>, EvolvedVelocity<Dim>>;\n  static void function(\n      gsl::not_null<std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>*>\n          position_velocity,\n      const tnsr::I<DataVector, Dim>& evolved_position,\n      const tnsr::I<DataVector, Dim>& evolved_velocity);\n};\n\n/// @{\n/*!\n * \\brief Computes the coordinate geodesic acceleration of the particle in the\n * inertial frame in Kerr-Schild coordinates.\n */\ntemplate <size_t Dim>\nstruct GeodesicAcceleration : db::SimpleTag {\n  using type = tnsr::I<double, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nstruct GeodesicAccelerationCompute : GeodesicAcceleration<Dim>, db::ComputeTag {\n  using base = GeodesicAcceleration<Dim>;\n  using return_type = tnsr::I<double, Dim, Frame::Inertial>;\n  using argument_tags = tmpl::list<\n      ParticlePositionVelocity<Dim>,\n      CurvedScalarWave::Tags::BackgroundSpacetime<gr::Solutions::KerrSchild>>;\n  static void function(\n      gsl::not_null<tnsr::I<double, Dim, Frame::Inertial>*> acceleration,\n      const std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>&\n          position_velocity,\n      const gr::Solutions::KerrSchild& background_spacetime);\n};\n/// @}\n\n/*!\n * \\brief The coordinate time dilation factor of the scalar charge, i.e. the 0th\n * component of its 4-velocity.\n */\nstruct TimeDilationFactor : db::SimpleTag {\n  using type = Scalar<double>;\n};\n\n/*!\n * \\brief The parameters controlling the growth of the worldtube excision\n * sphere, see smooth_broken_power_law. The parameters here are, in order, the\n * amplitude, the transition radius, the transition width and the exponent.\n */\nstruct WorldtubeRadiusParameters : db::SimpleTag {\n  using type = std::array<double, 4>;\n  using option_tags = tmpl::list<OptionTags::RadiusOptions<true>>;\n  static constexpr bool pass_metavariables = false;\n  static std::array<double, 4> create_from_options(\n      const OptionTags::RadiusOptions<true>& params) {\n    return {{params.exponent, params.amplitude, params.transition_radius,\n             params.transition_width}};\n  }\n};\n\n/*!\n * \\brief The parameters controlling the growth of the black holes excision\n * sphere, see smooth_broken_power_law. The parameters here are, in order, the\n * amplitude, the transition radius, the transition width and the exponent.\n */\nstruct BlackHoleRadiusParameters : db::SimpleTag {\n  using type = std::array<double, 4>;\n  using option_tags = tmpl::list<OptionTags::RadiusOptions<false>>;\n  static constexpr bool pass_metavariables = false;\n  static std::array<double, 4> create_from_options(\n      const OptionTags::RadiusOptions<false>& params) {\n    return {{params.exponent, params.amplitude, params.transition_radius,\n             params.transition_width}};\n  }\n};\n\n/// @{\n/*!\n * \\brief A tuple of Tensors evaluated at the charge depending only the\n * background and the particle's position and velocity. These values are\n * effectively cached between different iterations of the worldtube scheme.\n */\ntemplate <size_t Dim>\nstruct BackgroundQuantities : db::SimpleTag {\n  using type = tuples::TaggedTuple<\n      gr::Tags::SpacetimeMetric<double, Dim>,\n      gr::Tags::InverseSpacetimeMetric<double, Dim>,\n      gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>,\n      gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>,\n      Tags::TimeDilationFactor>;\n};\n\ntemplate <size_t Dim>\nstruct BackgroundQuantitiesCompute : BackgroundQuantities<Dim>, db::ComputeTag {\n  using base = BackgroundQuantities<Dim>;\n  using return_type = tuples::TaggedTuple<\n      gr::Tags::SpacetimeMetric<double, Dim>,\n      gr::Tags::InverseSpacetimeMetric<double, Dim>,\n      gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>,\n      gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>,\n      Tags::TimeDilationFactor>;\n\n  using argument_tags = tmpl::list<\n      ParticlePositionVelocity<Dim>,\n      CurvedScalarWave::Tags::BackgroundSpacetime<gr::Solutions::KerrSchild>>;\n  static void function(gsl::not_null<return_type*> result,\n                       const std::array<tnsr::I<double, Dim, Frame::Inertial>,\n                                        2>& position_velocity,\n                       const gr::Solutions::KerrSchild& background_spacetime);\n};\n/// @}\n\n/// @{\n/*!\n * \\brief An optional that holds the coordinates of an element face abutting the\n * worldtube excision sphere. If the element does not abut the worldtube, this\n * holds std::nullopt. This tag should be in the databox of element chares. The\n * available frames are Grid and Inertial. The Centered template tag can be\n * turned on to center the coordinates around the position of the scalar\n * charge.\n */\ntemplate <size_t Dim, typename Frame, bool Centered>\nstruct FaceCoordinates : db::SimpleTag {\n  using type = std::optional<tnsr::I<DataVector, Dim, Frame>>;\n};\n\ntemplate <size_t Dim, typename Frame, bool Centered>\nstruct FaceCoordinatesCompute : FaceCoordinates<Dim, Frame, Centered>,\n                                db::ComputeTag {\n  using base = FaceCoordinates<Dim, Frame, Centered>;\n  static constexpr bool needs_inertial_wt_coords =\n      (Centered and std::is_same_v<Frame, ::Frame::Inertial>);\n  using argument_tags = tmpl::flatten<\n      tmpl::list<ExcisionSphere<Dim>, domain::Tags::Element<Dim>,\n                 domain::Tags::Coordinates<Dim, Frame>, domain::Tags::Mesh<Dim>,\n                 tmpl::conditional_t<needs_inertial_wt_coords,\n                                     tmpl::list<ParticlePositionVelocity<Dim>>,\n                                     tmpl::list<>>>>;\n\n  using return_type = std::optional<tnsr::I<DataVector, Dim, Frame>>;\n  static void function(\n      const gsl::not_null<std::optional<tnsr::I<DataVector, Dim, Frame>>*>\n          result,\n      const ::ExcisionSphere<Dim>& excision_sphere, const Element<Dim>& element,\n      const tnsr::I<DataVector, Dim, Frame>& coords, const Mesh<Dim>& mesh);\n\n  static void function(\n      const gsl::not_null<\n          std::optional<tnsr::I<DataVector, Dim, ::Frame::Inertial>>*>\n          result,\n      const ::ExcisionSphere<Dim>& excision_sphere, const Element<Dim>& element,\n      const tnsr::I<DataVector, Dim, ::Frame::Inertial>& coords,\n      const Mesh<Dim>& mesh,\n      const std::array<tnsr::I<double, Dim, ::Frame::Inertial>, 2>&\n          particle_position_velocity);\n};\n/// @}\n\n/// @{\n/*!\n * \\brief The value of the scalar field and its time derivative on element faces\n * forming the worldtube boundary, as well as the Euclidean area element of the\n * face.\n *\n * \\details If the element does not abut the worldtube, this will be\n * `std::nullopt`.\n */\nstruct FaceQuantities : db::SimpleTag {\n  using type = std::optional<Variables<tmpl::list<\n      CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n      gr::surfaces::Tags::AreaElement<DataVector>>>>;\n};\n\nstruct FaceQuantitiesCompute : FaceQuantities, db::ComputeTag {\n  static constexpr size_t Dim = 3;\n  using base = FaceQuantities;\n  using return_type = std::optional<Variables<tmpl::list<\n      CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n      gr::surfaces::Tags::AreaElement<DataVector>>>>;\n  using tags_to_slice_to_face =\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<Dim>,\n                 gr::Tags::Shift<DataVector, Dim>, gr::Tags::Lapse<DataVector>,\n                 domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>>;\n  using argument_tags = tmpl::flatten<\n      tmpl::list<tags_to_slice_to_face, ExcisionSphere<Dim>,\n                 domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>>>;\n\n  static void function(\n      gsl::not_null<return_type*> result, const Scalar<DataVector>& psi,\n      const Scalar<DataVector>& pi, const tnsr::i<DataVector, Dim>& phi,\n      const tnsr::I<DataVector, Dim>& shift, const Scalar<DataVector>& lapse,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const ::ExcisionSphere<Dim>& excision_sphere, const Element<Dim>& element,\n      const Mesh<Dim>& mesh);\n};\n/// @}\n\n/*!\n * \\brief The internal expansion order of the worldtube solution.\n */\nstruct ExpansionOrder : db::SimpleTag {\n  using type = size_t;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::ExpansionOrder>;\n  static size_t create_from_options(const size_t order) { return order; }\n};\n\n/// @{\n/*!\n * Computes the puncture field on an element face abutting the worldtube\n * assuming geodesic acceleration. If the current element does not abut the\n * worldtube this holds a std::nullopt.\n */\ntemplate <size_t Dim>\nstruct PunctureField : db::SimpleTag {\n  using type = std::optional<Variables<tmpl::list<\n      CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n      ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                    Frame::Inertial>>>>;\n};\n\ntemplate <size_t Dim>\nstruct PunctureFieldCompute : PunctureField<Dim>, db::ComputeTag {\n  using base = PunctureField<Dim>;\n  using argument_tags =\n      tmpl::list<FaceCoordinates<Dim, Frame::Inertial, true>,\n                 ParticlePositionVelocity<Dim>, GeodesicAcceleration<Dim>,\n                 Charge, ExpansionOrder>;\n  using return_type = std::optional<Variables<tmpl::list<\n      CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n      ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                    Frame::Inertial>>>>;\n  static void function(\n      const gsl::not_null<return_type*> result,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          inertial_face_coords_centered,\n      const std::array<tnsr::I<double, Dim, ::Frame::Inertial>, 2>&\n          particle_position_velocity,\n      const tnsr::I<double, Dim>& particle_acceleration, double charge,\n      const size_t expansion_order);\n};\n/// @}\n\n/*!\n * \\brief Holds the current iteration of the puncture field computed with the\n * current iteration of the acceleration which includes the scalar self-force.\n * It is computed in `Actions::IteratePunctureField`.\n */\ntemplate <size_t Dim>\nstruct IteratedPunctureField : db::SimpleTag {\n  using type = std::optional<Variables<tmpl::list<\n      CurvedScalarWave::Tags::Psi, ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n      ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                    Frame::Inertial>>>>;\n};\n\n/*!\n * The acceleration terms computed by the worldtube singleton and sent to the\n * neighboring elements used to compute the puncture field.\n */\nstruct AccelerationTerms : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief A map that holds the grid coordinates centered on the worldtube of\n * all element faces abutting the worldtube with the corresponding ElementIds.\n */\ntemplate <size_t Dim>\nstruct ElementFacesGridCoordinates : db::SimpleTag {\n  using type =\n      std::unordered_map<ElementId<Dim>, tnsr::I<DataVector, Dim, Frame::Grid>>;\n};\n\n/*!\n * \\brief The solution inside the worldtube, evaluated at the face coordinates\n * of an abutting element. This tag is used to provide boundary conditions to\n * the element in \\ref CurvedScalarWave::BoundaryConditions::Worldtube .\n */\ntemplate <size_t Dim>\nstruct WorldtubeSolution : db::SimpleTag {\n  using type = Variables<\n      tmpl::list<::CurvedScalarWave::Tags::Psi, ::CurvedScalarWave::Tags::Pi,\n                 ::CurvedScalarWave::Tags::Phi<Dim>>>;\n};\n\n/*!\n * \\brief The scalar field inside the worldtube.\n *\n * \\details This tag is used as a base tag for Stf::Tags::StfTensor\n */\nstruct PsiWorldtube : db::SimpleTag {\n  using type = Scalar<double>;\n};\n\n/*!\n * \\brief Holds the constant coefficient of the regular field inside the\n * worldtube.\n *\n * \\details At orders n = 0 or 1 this is just equal to the monopole, but at n =\n * 2, the monopole gets an additional contribution from the trace of the second\n * order coefficient. At this point, this tag is used to solve an ODE based on\n * the expanded Klein-Gordon equation. It is implemented as a `Scalar` of size 1\n * because the evolution system does not work with doubles.\n */\nstruct Psi0 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Holds the time derivative of Psi0 which is used as a reduction\n * variable.\n */\nstruct dtPsi0 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Sets Gamma1 to zero throughout the domain. The equations are given in\n * Initialization::InitializeConstraintDampingGammas.\n */\nstruct ConstraintGamma1Compute : CurvedScalarWave::Tags::ConstraintGamma1,\n                                 db::ComputeTag {\n  static constexpr size_t Dim = 3;\n  using base = CurvedScalarWave::Tags::ConstraintGamma1;\n  using return_type = Scalar<DataVector>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n  static void function(gsl::not_null<Scalar<DataVector>*> gamma1,\n                       const tnsr::I<DataVector, Dim, Frame::Inertial>& coords);\n};\n\n/*!\n * \\brief Sets Gamma2 to a Gaussian that falls off to a constant value centered\n * on the position of the particle. This was found to be necessary for a stable\n * evolution. The equations are given in\n * Initialization::InitializeConstraintDampingGammas.\n */\nstruct ConstraintGamma2Compute : CurvedScalarWave::Tags::ConstraintGamma2,\n                                 db::ComputeTag {\n  static constexpr size_t Dim = 3;\n  using base = CurvedScalarWave::Tags::ConstraintGamma2;\n  using return_type = Scalar<DataVector>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 ParticlePositionVelocity<Dim>>;\n  static void function(\n      gsl::not_null<Scalar<DataVector>*> gamma2,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>& pos_vel);\n};\n\n}  // namespace Tags\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  InsideHorizon.cpp\n  OrbitRadius.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InsideHorizon.hpp\n  OrbitRadius.hpp\n)\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/InsideHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/InsideHorizon.hpp\"\n\n#include <array>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/RadiusFunctions.hpp\"\n\nnamespace Triggers {\n\nbool InsideHorizon::operator()(\n    const std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>&\n        position_and_velocity,\n    const std::array<double, 4>& worldtube_radius_params) const {\n  const double orbit_radius = get(magnitude(position_and_velocity[0]));\n  // optimization that will almost always be true\n  if (LIKELY(orbit_radius > 1.99)) {\n    return false;\n  }\n  const double wt_radius_inertial =\n      CurvedScalarWave::Worldtube::smooth_broken_power_law(\n          orbit_radius, worldtube_radius_params[0], worldtube_radius_params[1],\n          worldtube_radius_params[2], worldtube_radius_params[3]);\n  return orbit_radius + wt_radius_inertial < 1.99;\n}\n\nPUP::able::PUP_ID InsideHorizon::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/InsideHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <pup.h>\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace Triggers {\n\n/*!\n * \\brief This trigger is true when the worldtube is entirely within a\n * coordinate sphere of radius 1.99 M centered on the origin in the inertial\n * frame. This assumes a black hole mass of 1M.\n */\nclass InsideHorizon : public Trigger {\n public:\n  /// \\cond\n  InsideHorizon() = default;\n  explicit InsideHorizon(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(InsideHorizon);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help =\n      \"Triggers if the worldtube is entirely inside a coordinate sphere with \"\n      \"radius 1.99 M centered on the origin in the inertial frame.\";\n  static constexpr size_t Dim = 3;\n  using options = tmpl::list<>;\n  using argument_tags = tmpl::list<\n      CurvedScalarWave::Worldtube::Tags::ParticlePositionVelocity<Dim>,\n      CurvedScalarWave::Worldtube::Tags::WorldtubeRadiusParameters>;\n\n  bool operator()(const std::array<tnsr::I<double, Dim, Frame::Inertial>, 2>&\n                      position_and_velocity,\n                  const std::array<double, 4>& worldtube_radius_params) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override {}\n};\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/OrbitRadius.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/OrbitRadius.hpp\"\n\n#include <array>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace Triggers {\n\nOrbitRadius::OrbitRadius(const std::vector<double>& radii) : radii_(radii) {}\n\nbool OrbitRadius::operator()(\n    const std::array<tnsr::I<double, 3, Frame::Inertial>, 2>&\n        position_and_velocity,\n    const TimeDelta& time_step) const {\n  const auto& position = position_and_velocity[0];\n  const auto& velocity = position_and_velocity[1];\n  const double current_radius = get(magnitude(position));\n  const double radial_velocity = (get<0>(position) * get<0>(velocity) +\n                                  get<1>(position) * get<1>(velocity) +\n                                  get<2>(position) * get<2>(velocity)) /\n                                 current_radius;\n  // factor 1.2 is for safety because the approximation is just linear\n  // approximation and triggering it multiple times is not a problem\n  const double next_radius =\n      current_radius + 1.2 * radial_velocity * time_step.value();\n  // NOLINTNEXTLINE(readability-use-anyofallof)\n  for (const double radius : radii_) {\n    if ((current_radius - radius) * (next_radius - radius) < 0.) {\n      return true;\n    }\n  }\n  return false;\n}\n\n// NOLINTNEXTLINE(google-runtime-references)\nvoid OrbitRadius::pup(PUP::er& p) { p | radii_; }\n\nPUP::able::PUP_ID OrbitRadius::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/OrbitRadius.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace Triggers {\n/*!\n * \\brief This trigger returns true when the scalar charge is about to cross one\n * of the specified areal radii.\n *\n * \\details As the domain is adjusted to track the position of the scalar\n * charge, the time step needs to be dynamically adjusted accordingly. This\n * trigger can be used to set the time step according to the radial position of\n * the scalar charge which gives a good approximation of when the time step\n * should be adjusted.\n * The trigger only approximates whether the particle might cross during the\n * next time step and may therefore fire twice.\n */\nclass OrbitRadius : public Trigger {\n public:\n  /// \\cond\n  OrbitRadius() = default;\n  explicit OrbitRadius(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(OrbitRadius);  // NOLINT\n  /// \\endcond\n\n  struct Radii {\n    using type = std::vector<double>;\n    static constexpr Options::String help =\n        \"The orbital radii which should trigger when crossed by the \"\n        \"scalar charge.\";\n  };\n\n  static constexpr Options::String help =\n      \"Trigger that fires when the scalar charge crosses specified radii.\";\n  using options = tmpl::list<Radii>;\n\n  explicit OrbitRadius(const std::vector<double>& radii);\n\n  using argument_tags =\n      tmpl::list<CurvedScalarWave::Worldtube::Tags::ParticlePositionVelocity<3>,\n                 Tags::TimeStep>;\n\n  bool operator()(\n      const std::array<tnsr::I<double, 3>, 2>& position_and_velocity,\n      const TimeDelta& time_step) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<double> radii_{};\n};\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Evolution/Systems/CurvedScalarWave/Worldtube/Worldtube.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief The set of utilities for performing CurvedScalarWave evolution with a\n * worldtube excision scheme.\n *\n * \\details The worldtube excision scheme is a method that aims to enable NR\n * evolutions of intermediate mass ratio binary black hole simulations. In\n * standard BBH simulations two excision spheres are cut out from the domain\n * within the apparent horizons of the respective black holes. For larger mass\n * ratios, this introduces a scale disparity in the evolution system because the\n * small grid spacing in the elements near the smaller black hole constrain the\n * time step to be orders of magnitude smaller than near the larger black hole\n * due to the CFL condition. The worldtube excision scheme avoids this by\n * excising a much larger region (the worldtube) around the smaller black hole.\n * Since the excision boundary no longer lies within the apparent horizon,\n * boundary conditions are required. These are derived by approximating the\n * solution inside the worldtube using a perturbative solution - a black hole\n * perturbed by another black hole. The solution is calibrated by the evolved\n * metric on the worldtube boundary and in turn provides boundary conditions to\n * the evolution system.\n *\n * Here, we test this scheme using a toy problem of a scalar charge in\n * circular orbit around a Schwarzschild black hole.\n */\nnamespace CurvedScalarWave::Worldtube {\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ForceFree::BoundaryConditions {\nBoundaryCondition::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\nvoid BoundaryCondition::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n}  // namespace ForceFree::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace ForceFree {\n\n/*!\n * \\brief Boundary conditions for the GRFFE system\n */\nnamespace BoundaryConditions {\n\n/*!\n * \\brief The base class of which all boundary conditions must inherit\n */\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n\n}  // namespace BoundaryConditions\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryConditions/CMakeLists.txt",
    "content": "#\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  DemandOutgoingCharSpeeds.cpp\n  DirichletAnalytic.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  DemandOutgoingCharSpeeds.hpp\n  DirichletAnalytic.hpp\n  Factory.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryConditions/DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::BoundaryConditions {\nDemandOutgoingCharSpeeds::DemandOutgoingCharSpeeds(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDemandOutgoingCharSpeeds::get_clone() const {\n  return std::make_unique<DemandOutgoingCharSpeeds>(*this);\n}\n\nvoid DemandOutgoingCharSpeeds::pup(PUP::er& p) { BoundaryCondition::pup(p); }\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DemandOutgoingCharSpeeds::my_PUP_ID = 0;\n\nstd::optional<std::string>\nDemandOutgoingCharSpeeds::dg_demand_outgoing_char_speeds(\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        outward_directed_normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>&\n    /*outward_directed_normal_vector*/,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& lapse) {\n  double min_speed = std::numeric_limits<double>::signaling_NaN();\n\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>>> buffer{\n      get(lapse).size()};\n\n  auto& normal_dot_shift = get<::Tags::TempScalar<0>>(buffer);\n  dot_product(make_not_null(&normal_dot_shift),\n              outward_directed_normal_covector, shift);\n\n  if (face_mesh_velocity.has_value()) {\n    auto& normal_dot_mesh_velocity = get<::Tags::TempScalar<1>>(buffer);\n    dot_product(make_not_null(&normal_dot_mesh_velocity),\n                outward_directed_normal_covector, face_mesh_velocity.value());\n    get(normal_dot_shift) += get(normal_dot_mesh_velocity);\n  }\n\n  // The characteristic speeds are bounded by \\pm \\alpha - \\beta^i n_i,\n  // therefore minimum is given as `-\\alpha - \\beta^i n_i`.\n  min_speed = min(-get(lapse) - get(normal_dot_shift));\n\n  if (min_speed < 0.0) {\n    return {MakeString{}\n            << \"DemandOutgoingCharSpeeds boundary condition violated. Speed: \"\n            << min_speed << \"\\nn_i: \" << outward_directed_normal_covector\n            << \"\\n\"};\n  }\n\n  return std::nullopt;\n}\n\n}  // namespace ForceFree::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryConditions/DemandOutgoingCharSpeeds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree::BoundaryConditions {\n/*!\n * \\brief A boundary condition that only verifies that all characteristic speeds\n * are directed out of the domain; no boundary data is altered by this boundary\n * condition.\n */\nclass DemandOutgoingCharSpeeds final : public BoundaryCondition {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"DemandOutgoingCharSpeeds boundary condition that only verifies the \"\n      \"characteristic speeds are all directed out of the domain.\"};\n\n  DemandOutgoingCharSpeeds() = default;\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<gr::Tags::Shift<DataVector, 3>, gr::Tags::Lapse<DataVector>>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n      /*outward_directed_normal_vector*/,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const Scalar<DataVector>& lapse);\n};\n}  // namespace ForceFree::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ForceFree/ElectricCurrentDensity.hpp\"\n#include \"Evolution/Systems/ForceFree/Fluxes.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree::BoundaryConditions {\n\nDirichletAnalytic::DirichletAnalytic(const DirichletAnalytic& rhs)\n    : BoundaryCondition{dynamic_cast<const BoundaryCondition&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\nDirichletAnalytic& DirichletAnalytic::operator=(const DirichletAnalytic& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\nDirichletAnalytic::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\nDirichletAnalytic::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic::get_clone() const {\n  return std::make_unique<DirichletAnalytic>(*this);\n}\n\nvoid DirichletAnalytic::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | analytic_prescription_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic::my_PUP_ID = 0;\n\nstd::optional<std::string> DirichletAnalytic::dg_ghost(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_e,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> tilde_psi,\n    const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> tilde_q,\n\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_psi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_q_flux,\n\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<\n        tnsr::I<DataVector, 3, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    [[maybe_unused]] const double time,\n    const double parallel_conductivity) const {\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<Tags::TildeE, Tags::TildeB, Tags::TildePsi,\n                          Tags::TildePhi, Tags::TildeQ,\n                          gr::Tags::Lapse<DataVector>,\n                          gr::Tags::Shift<DataVector, 3>,\n                          gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                          gr::Tags::SpatialMetric<DataVector, 3>,\n                          gr::Tags::InverseSpatialMetric<DataVector, 3>>,\n      tmpl::append<ForceFree::Solutions::all_solutions,\n                   ForceFree::AnalyticData::all_data>>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const analytic_solution_or_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*analytic_solution_or_data)>>) {\n          return analytic_solution_or_data->variables(\n              coords, time,\n              tmpl::list<Tags::TildeE, Tags::TildeB, Tags::TildePsi,\n                         Tags::TildePhi, Tags::TildeQ,\n                         gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>>{});\n        } else {\n          (void)time;\n          return analytic_solution_or_data->variables(\n              coords,\n              tmpl::list<Tags::TildeE, Tags::TildeB, Tags::TildePsi,\n                         Tags::TildePhi, Tags::TildeQ,\n                         gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>>{});\n        }\n      });\n\n  *tilde_e = get<Tags::TildeE>(boundary_values);\n  *tilde_b = get<Tags::TildeB>(boundary_values);\n  *tilde_psi = get<Tags::TildePsi>(boundary_values);\n  *tilde_phi = get<Tags::TildePhi>(boundary_values);\n  *tilde_q = get<Tags::TildeQ>(boundary_values);\n\n  *lapse = get<gr::Tags::Lapse<DataVector>>(boundary_values);\n  *shift = get<gr::Tags::Shift<DataVector, 3>>(boundary_values);\n  *inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_values);\n\n  const auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(boundary_values);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_values);\n\n  // allocate a temp buffer to compute \\tilde{J}^i\n  Variables<tmpl::list<::Tags::TempI<0, 3>>> buffer{get(*tilde_q).size()};\n  auto& tilde_j = get<::Tags::TempI<0, 3>>(buffer);\n  Tags::ComputeTildeJ::function(make_not_null(&tilde_j), *tilde_q, *tilde_e,\n                                *tilde_b, parallel_conductivity, *lapse,\n                                sqrt_det_spatial_metric, spatial_metric);\n\n  // compute corresponding fluxes\n  ForceFree::Fluxes::apply(\n      tilde_e_flux, tilde_b_flux, tilde_psi_flux, tilde_phi_flux, tilde_q_flux,\n      *tilde_e, *tilde_b, *tilde_psi, *tilde_phi, *tilde_q, tilde_j, *lapse,\n      *shift, sqrt_det_spatial_metric, spatial_metric, *inv_spatial_metric);\n\n  return std::nullopt;\n}\n\n}  // namespace ForceFree::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace ForceFree::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\nclass DirichletAnalytic final : public BoundaryCondition {\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n\n  using options = tmpl::list<AnalyticPrescription>;\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions using either analytic solution or \"\n      \"analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time, Tags::ParallelConductivity>;\n\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_e,\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n      const gsl::not_null<Scalar<DataVector>*> tilde_psi,\n      const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n      const gsl::not_null<Scalar<DataVector>*> tilde_q,\n\n      const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*>\n          tilde_e_flux,\n      const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*>\n          tilde_b_flux,\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          tilde_psi_flux,\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          tilde_phi_flux,\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          tilde_q_flux,\n\n      const gsl::not_null<Scalar<DataVector>*> lapse,\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<\n          tnsr::I<DataVector, 3, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n      [[maybe_unused]] const double time,\n      const double parallel_conductivity) const;\n\n private:\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace ForceFree::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\nusing standard_boundary_conditions =\n    tmpl::list<domain::BoundaryConditions::Periodic<BoundaryCondition>,\n               DemandOutgoingCharSpeeds, DirichletAnalytic>;\n}  // namespace ForceFree::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryCorrections/CMakeLists.txt",
    "content": "#\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Rusanov.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  NamespaceDocs.hpp\n  Rusanov.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::BoundaryCorrections {\nusing standard_boundary_corrections = tmpl::list<Rusanov>;\n}  // namespace ForceFree::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief Boundary corrections/numerical fluxes for the GRFFE sytem.\n */\nnamespace ForceFree::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree::BoundaryCorrections {\n\nRusanov::Rusanov(CkMigrateMessage* msg) : BoundaryCorrection(msg) {}\n\nstd::unique_ptr<evolution::BoundaryCorrection> Rusanov::get_clone() const {\n  return std::make_unique<Rusanov>(*this);\n}\n\nvoid Rusanov::pup(PUP::er& p) { BoundaryCorrection::pup(p); }\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Rusanov::my_PUP_ID = 0;\n\ndouble Rusanov::dg_package_data(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        packaged_tilde_e,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        packaged_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_psi,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_q,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        packaged_normal_dot_flux_tilde_e,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        packaged_normal_dot_flux_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_psi,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_q,\n    const gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n\n    const tnsr::IJ<DataVector, 3, Frame::Inertial>& flux_tilde_e,\n    const tnsr::IJ<DataVector, 3, Frame::Inertial>& flux_tilde_b,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_psi,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_phi,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_q,\n\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) {\n  // Compute max abs char speed\n  Scalar<DataVector>& shift_dot_normal = *packaged_tilde_q;\n  dot_product(make_not_null(&shift_dot_normal), shift, normal_covector);\n  if (normal_dot_mesh_velocity.has_value()) {\n    get(*packaged_abs_char_speed) =\n        max(abs(-get(lapse) - get(shift_dot_normal) -\n                get(*normal_dot_mesh_velocity)),\n            abs(get(lapse) - get(shift_dot_normal) -\n                get(*normal_dot_mesh_velocity)));\n  } else {\n    get(*packaged_abs_char_speed) =\n        max(abs(-get(lapse) - get(shift_dot_normal)),\n            abs(get(lapse) - get(shift_dot_normal)));\n  }\n\n  *packaged_tilde_e = tilde_e;\n  *packaged_tilde_b = tilde_b;\n  *packaged_tilde_psi = tilde_psi;\n  *packaged_tilde_phi = tilde_phi;\n  *packaged_tilde_q = tilde_q;\n\n  normal_dot_flux(packaged_normal_dot_flux_tilde_e, normal_covector,\n                  flux_tilde_e);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_b, normal_covector,\n                  flux_tilde_b);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_psi, normal_covector,\n                  flux_tilde_psi);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_phi, normal_covector,\n                  flux_tilde_phi);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_q, normal_covector,\n                  flux_tilde_q);\n\n  return max(get(*packaged_abs_char_speed));\n}\n\nvoid Rusanov::dg_boundary_terms(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        boundary_correction_tilde_e,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        boundary_correction_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_psi,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_q,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e_int,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_int,\n    const Scalar<DataVector>& tilde_psi_int,\n    const Scalar<DataVector>& tilde_phi_int,\n    const Scalar<DataVector>& tilde_q_int,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_e_int,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_b_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_psi_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_phi_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_q_int,\n    const Scalar<DataVector>& abs_char_speed_int,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e_ext,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_ext,\n    const Scalar<DataVector>& tilde_psi_ext,\n    const Scalar<DataVector>& tilde_phi_ext,\n    const Scalar<DataVector>& tilde_q_ext,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_e_ext,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_b_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_psi_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_phi_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_q_ext,\n    const Scalar<DataVector>& abs_char_speed_ext,\n    dg::Formulation dg_formulation) {\n  if (dg_formulation == dg::Formulation::WeakInertial) {\n    for (size_t i = 0; i < 3; ++i) {\n      boundary_correction_tilde_e->get(i) =\n          0.5 * (normal_dot_flux_tilde_e_int.get(i) -\n                 normal_dot_flux_tilde_e_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (tilde_e_ext.get(i) - tilde_e_int.get(i));\n      boundary_correction_tilde_b->get(i) =\n          0.5 * (normal_dot_flux_tilde_b_int.get(i) -\n                 normal_dot_flux_tilde_b_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (tilde_b_ext.get(i) - tilde_b_int.get(i));\n    }\n    get(*boundary_correction_tilde_psi) =\n        0.5 * (get(normal_dot_flux_tilde_psi_int) -\n               get(normal_dot_flux_tilde_psi_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_psi_ext) - get(tilde_psi_int));\n    get(*boundary_correction_tilde_phi) =\n        0.5 * (get(normal_dot_flux_tilde_phi_int) -\n               get(normal_dot_flux_tilde_phi_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_phi_ext) - get(tilde_phi_int));\n    get(*boundary_correction_tilde_q) =\n        0.5 * (get(normal_dot_flux_tilde_q_int) -\n               get(normal_dot_flux_tilde_q_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_q_ext) - get(tilde_q_int));\n  } else {\n    for (size_t i = 0; i < 3; ++i) {\n      boundary_correction_tilde_e->get(i) =\n          -0.5 * (normal_dot_flux_tilde_e_int.get(i) +\n                  normal_dot_flux_tilde_e_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (tilde_e_ext.get(i) - tilde_e_int.get(i));\n      boundary_correction_tilde_b->get(i) =\n          -0.5 * (normal_dot_flux_tilde_b_int.get(i) +\n                  normal_dot_flux_tilde_b_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (tilde_b_ext.get(i) - tilde_b_int.get(i));\n    }\n    get(*boundary_correction_tilde_psi) =\n        -0.5 * (get(normal_dot_flux_tilde_psi_int) +\n                get(normal_dot_flux_tilde_psi_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_psi_ext) - get(tilde_psi_int));\n    get(*boundary_correction_tilde_phi) =\n        -0.5 * (get(normal_dot_flux_tilde_phi_int) +\n                get(normal_dot_flux_tilde_phi_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_phi_ext) - get(tilde_phi_int));\n    get(*boundary_correction_tilde_q) =\n        -0.5 * (get(normal_dot_flux_tilde_q_int) +\n                get(normal_dot_flux_tilde_q_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_q_ext) - get(tilde_q_int));\n  }\n}\n\nbool operator==(const Rusanov& /*lhs*/, const Rusanov& /*rhs*/) { return true; }\n\nbool operator!=(const Rusanov& lhs, const Rusanov& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::BoundaryCorrections {\n\n/*!\n * \\brief A Rusanov/local Lax-Friedrichs Riemann solver\n *\n * Let \\f$U\\f$ be the evolved variables, \\f$F^i\\f$ the corresponding fluxes, and\n * \\f$n_i\\f$ be the outward directed unit normal to the interface. Denoting \\f$F\n * := n_i F^i\\f$, the %Rusanov boundary correction is\n *\n * \\f{align*}\n * G_\\text{Rusanov} = \\frac{F_\\text{int} - F_\\text{ext}}{2} -\n * \\frac{\\text{max}\\left(|\\lambda_\\text{int}|,\n * |\\lambda_\\text{ext}|\\right)}{2} \\left(U_\\text{ext} - U_\\text{int}\\right),\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior, and\n * \\f$\\lambda\\f$ is the characteristic/signal speed. The minus sign in\n * front of the \\f$F_{\\text{ext}}\\f$ is necessary because the outward directed\n * normal of the neighboring element has the opposite sign, i.e.\n * \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$.\n *\n * For the GRFFE system the largest characteristic speeds \\f$\\lambda\\f$ of our\n * interest are given as\n *\n * \\f{align*}{\n *   \\lambda_{\\pm} = -\\beta^i n_i \\pm \\alpha.\n * \\f}\n *\n * which correspond to fast mode waves.\n *\n * \\note In the strong form the `dg_boundary_terms` function returns\n * \\f$G - F_\\text{int}\\f$\n *\n */\nclass Rusanov final : public evolution::BoundaryCorrection {\n private:\n  struct AbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the Rusanov or local Lax-Friedrichs boundary correction term \"\n      \"for the GRFFE system.\"};\n\n  Rusanov() = default;\n  Rusanov(const Rusanov&) = default;\n  Rusanov& operator=(const Rusanov&) = default;\n  Rusanov(Rusanov&&) = default;\n  Rusanov& operator=(Rusanov&&) = default;\n  ~Rusanov() override = default;\n\n  /// \\cond\n  explicit Rusanov(CkMigrateMessage* /*unused*/);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Rusanov);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::TildeE, Tags::TildeB, Tags::TildePsi, Tags::TildePhi,\n                 Tags::TildeQ, ::Tags::NormalDotFlux<Tags::TildeE>,\n                 ::Tags::NormalDotFlux<Tags::TildeB>,\n                 ::Tags::NormalDotFlux<Tags::TildePsi>,\n                 ::Tags::NormalDotFlux<Tags::TildePhi>,\n                 ::Tags::NormalDotFlux<Tags::TildeQ>, AbsCharSpeed>;\n  using dg_package_data_temporary_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>>;\n  using dg_package_data_primitive_tags = tmpl::list<>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  static double dg_package_data(\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> packaged_tilde_e,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> packaged_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_psi,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_phi,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_q,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          packaged_normal_dot_flux_tilde_e,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          packaged_normal_dot_flux_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_psi,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_phi,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_q,\n      gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n      const Scalar<DataVector>& tilde_q,\n\n      const tnsr::IJ<DataVector, 3, Frame::Inertial>& flux_tilde_e,\n      const tnsr::IJ<DataVector, 3, Frame::Inertial>& flux_tilde_b,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_psi,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_phi,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_q,\n\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity);\n\n  static void dg_boundary_terms(\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          boundary_correction_tilde_e,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          boundary_correction_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_psi,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_phi,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_q,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e_int,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_int,\n      const Scalar<DataVector>& tilde_psi_int,\n      const Scalar<DataVector>& tilde_phi_int,\n      const Scalar<DataVector>& tilde_q_int,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_e_int,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_b_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_psi_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_phi_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_q_int,\n      const Scalar<DataVector>& abs_char_speed_int,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e_ext,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_ext,\n      const Scalar<DataVector>& tilde_psi_ext,\n      const Scalar<DataVector>& tilde_phi_ext,\n      const Scalar<DataVector>& tilde_q_ext,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_e_ext,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_b_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_psi_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_phi_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_q_ext,\n      const Scalar<DataVector>& abs_char_speed_ext,\n      dg::Formulation dg_formulation);\n};\n\nbool operator==(const Rusanov& lhs, const Rusanov& rhs);\nbool operator!=(const Rusanov& lhs, const Rusanov& rhs);\n\n}  // namespace ForceFree::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ForceFree)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Characteristics.cpp\n  ElectricCurrentDensity.cpp\n  ElectromagneticVariables.cpp\n  Fluxes.cpp\n  Sources.cpp\n  TimeDerivativeTerms.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Characteristics.hpp\n  ElectricCurrentDensity.hpp\n  ElectromagneticVariables.hpp\n  Fluxes.hpp\n  MaskNeutronStarInterior.hpp\n  Sources.hpp\n  System.hpp\n  Tags.hpp\n  TimeDerivativeTerms.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  CoordinateMaps\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainBoundaryConditions\n  DomainStructure\n  DgSubcell\n  ErrorHandling\n  Events\n  Evolution\n  FiniteDifference\n  ForceFreeAnalyticData\n  ForceFreeSolutions\n  GeneralRelativity\n  Options\n  Parallel\n  Spectral\n  Utilities\n  )\n\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Imex)\nadd_subdirectory(Instantiations)\nadd_subdirectory(Subcell)\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Characteristics.hpp\"\n\n#include <algorithm>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree::Tags {\n\nvoid LargestCharacteristicSpeedCompute::function(\n    const gsl::not_null<double*> speed, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3>& shift,\n    const tnsr::ii<DataVector, 3>& spatial_metric) {\n  const auto shift_magnitude = magnitude(shift, spatial_metric);\n  *speed = max(get(shift_magnitude) + get(lapse));\n}\n\n}  // namespace ForceFree::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct Normalized;\n}  // namespace Tags\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree {\nnamespace Tags {\n/*!\n * \\brief Compute the largest characteristic speed of the GRFFE system with\n * divergence cleaning.\n *\n * Wave speeds of the fast modes of the GRFFE system are the speed of light.\n *\n * \\f{align*}\n *  \\lambda_\\pm = \\beta^i\\beta_i \\pm \\alpha\n * \\f}\n *\n * where \\f$\\alpha\\f$ is the lapse and \\f$\\beta^i\\f$ is the shift. Therefore the\n * largest characteristic speed is \\f$\\lambda_\\text{max} =\n * \\sqrt{\\beta_i\\beta^i}+\\alpha\\f$.\n */\nstruct LargestCharacteristicSpeedCompute : LargestCharacteristicSpeed,\n                                           db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(gsl::not_null<double*> speed,\n                       const Scalar<DataVector>& lapse,\n                       const tnsr::I<DataVector, 3>& shift,\n                       const tnsr::ii<DataVector, 3>& spatial_metric);\n};\n\n}  // namespace Tags\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/ElectricCurrentDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/ElectricCurrentDensity.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree {\n\nnamespace {\n\ntemplate <bool IncludeDriftCurrent, bool IncludeParallelCurrent>\nvoid tilde_j_impl(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_j,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const double parallel_conductivity, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric) {\n  static_assert(IncludeDriftCurrent or IncludeParallelCurrent);\n\n  Variables<tmpl::list<::Tags::Tempi<0, 3>, ::Tags::Tempi<1, 3>,\n                       ::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>>>\n      buffer{get(lapse).size()};\n\n  // compute one-forms of TildeE and TildeB in advance to reduce the number of\n  // dot products using spatial metric (which are slower than dot products\n  // without using spatial metric)\n  auto& tilde_e_one_form = get<::Tags::Tempi<0, 3>>(buffer);\n  auto& tilde_b_one_form = get<::Tags::Tempi<1, 3>>(buffer);\n  raise_or_lower_index(make_not_null(&tilde_e_one_form), tilde_e,\n                       spatial_metric);\n  raise_or_lower_index(make_not_null(&tilde_b_one_form), tilde_b,\n                       spatial_metric);\n\n  // Compute \\tilde{B}^2 = \\tilde{B}^j \\tilde{B}_j. We need this quantity for\n  // both drift (explicit, non-stiff) and parallel (implicit, stiff) components\n  // of J^i.\n  auto& tilde_b_squared = get<::Tags::TempScalar<0>>(buffer);\n  dot_product(make_not_null(&tilde_b_squared), tilde_b, tilde_b_one_form);\n\n  if constexpr (IncludeParallelCurrent) {\n    (void)tilde_q;                  // avoid compiler warnings\n    (void)sqrt_det_spatial_metric;  // avoid compiler warnings\n\n    auto& tilde_e_squared = get<::Tags::TempScalar<1>>(buffer);\n    auto& tilde_e_dot_tilde_b = get<::Tags::TempScalar<2>>(buffer);\n    dot_product(make_not_null(&tilde_e_squared), tilde_e, tilde_e_one_form);\n    dot_product(make_not_null(&tilde_e_dot_tilde_b), tilde_e, tilde_b_one_form);\n\n    for (size_t i = 0; i < 3; ++i) {\n      (*tilde_j).get(i) =\n          parallel_conductivity *\n          (get(tilde_e_dot_tilde_b) * tilde_b.get(i) +\n           max(get(tilde_e_squared) - get(tilde_b_squared), 0.0) *\n               tilde_e.get(i));\n    }\n  } else {\n    // tilde_j should be initialized to zero before the summation performed in\n    // the next `if constexpr` block\n    for (size_t i = 0; i < 3; ++i) {\n      (*tilde_j).get(i) = 0;\n    }\n  }\n\n  if constexpr (IncludeDriftCurrent) {\n    (void)parallel_conductivity;  // avoid compiler warnings\n\n    for (LeviCivitaIterator<3> it; it; ++it) {\n      const auto& i = it[0];\n      const auto& j = it[1];\n      const auto& k = it[2];\n      (*tilde_j).get(i) +=\n          it.sign() * get(tilde_q) * tilde_e_one_form.get(j) *\n          tilde_b_one_form.get(k) /\n          get(sqrt_det_spatial_metric);  // the extra 1/sqrt{gamma} factor comes\n                                         // from the spatial Levi-Civita tensor\n    }\n  }\n\n  // overall factor\n  for (size_t i = 0; i < 3; ++i) {\n    (*tilde_j).get(i) *= get(lapse) / get(tilde_b_squared);\n  }\n}\n\n}  // namespace\n\nvoid ComputeDriftTildeJ::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> drift_tilde_j,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const double parallel_conductivity, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric) {\n  tilde_j_impl<true, false>(drift_tilde_j, tilde_q, tilde_e, tilde_b,\n                            parallel_conductivity, lapse,\n                            sqrt_det_spatial_metric, spatial_metric);\n}\n\nvoid ComputeParallelTildeJ::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        parallel_tilde_j,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const double parallel_conductivity, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric) {\n  tilde_j_impl<false, true>(parallel_tilde_j, tilde_q, tilde_e, tilde_b,\n                            parallel_conductivity, lapse,\n                            sqrt_det_spatial_metric, spatial_metric);\n}\n\nnamespace Tags {\nvoid ComputeTildeJ::function(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_j,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const double parallel_conductivity, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric) {\n  tilde_j_impl<true, true>(tilde_j, tilde_q, tilde_e, tilde_b,\n                           parallel_conductivity, lapse,\n                           sqrt_det_spatial_metric, spatial_metric);\n}\n}  // namespace Tags\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/ElectricCurrentDensity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree {\n\n/*!\n * \\brief Computes the non-stiff part \\f$\\tilde{J}^i_\\mathrm{drift}\\f$ of the\n * generalized electric current density \\f$\\tilde{J}^i\\f$.\n *\n * \\f{align}\n *  \\tilde{J}^i_\\mathrm{drift}\n *    = \\alpha \\sqrt{\\gamma} q \\frac{\\epsilon^{ijk}_{(3)}E_jB_k}{B_lB^l}\n * \\f}\n *\n * where \\f$\\alpha\\f$ is lapse, \\f$\\gamma\\f$ is the determinant of the spatial\n * metric, \\f$q\\f$ is charge density, \\f$\\epsilon^{ijk}_{(3)}\\f$ is the spatial\n * Levi-Civita tensor, \\f$E^i\\f$ is the electric field, and \\f$B^i\\f$ is the\n * magnetic field.\n *\n */\nstruct ComputeDriftTildeJ {\n  using argument_tags =\n      tmpl::list<Tags::TildeQ, Tags::TildeE, Tags::TildeB,\n                 Tags::ParallelConductivity, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>>;\n  using return_type = tnsr::I<DataVector, 3>;\n\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> drift_tilde_j,\n      const Scalar<DataVector>& tilde_q,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      double parallel_conductivity, const Scalar<DataVector>& lapse,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric);\n};\n\n/*!\n * \\brief Computes the stiff part \\f$\\tilde{J}^i_\\mathrm{parallel}\\f$ of the\n * generalized electric current density \\f$\\tilde{J}^i\\f$.\n *\n * \\f{align*}\n *  \\tilde{J}^i_\\mathrm{parallel}\n *    & = \\alpha \\sqrt{\\gamma} \\eta \\left[\n *        \\frac{(E_lB^l)B^i}{B^2} + \\frac{\\mathcal{R}(E^2-B^2)}{B^2} E^i\n *      \\right]\n * \\f}\n *\n * where \\f$\\alpha\\f$ is lapse, \\f$\\gamma\\f$ is the determinant of the spatial\n * metric, \\f$E^i\\f$ is the electric field, \\f$B^i\\f$ is the magnetic field,\n * \\f$\\eta\\f$ is the parallel conductivity, and \\f$\\mathcal{R}(x) = \\max\n * (x,0)\\f$ is the rectifier function.\n *\n */\nstruct ComputeParallelTildeJ {\n  using argument_tags =\n      tmpl::list<Tags::TildeQ, Tags::TildeE, Tags::TildeB,\n                 Tags::ParallelConductivity, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>>;\n  using return_type = tnsr::I<DataVector, 3>;\n\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> parallel_tilde_j,\n      const Scalar<DataVector>& tilde_q,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      double parallel_conductivity, const Scalar<DataVector>& lapse,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric);\n};\n\nnamespace Tags {\n/*!\n * \\brief Computes the densitized electric current density \\f$\\tilde{J}^i\\f$.\n *\n * \\f{align}\n *  \\tilde{J}^i = \\tilde{J}^i_\\mathrm{drift} + \\tilde{J}^i_\\mathrm{parallel}\n *   = \\alpha \\left[\n *      \\tilde{q} \\frac{\\epsilon^{ijk}_{(3)}\\tilde{E}_j \\tilde{B}_k}\n *                     {\\tilde{B}_l \\tilde{B}^l}\n *      + \\eta \\left\\{\n *        \\frac{(\\tilde{E}_l\\tilde{B}^l)\\tilde{B}^i}{\\tilde{B}^2}\n *      + \\frac{\\mathcal{R}(\\tilde{E}^2-\\tilde{B}^2)}{\\tilde{B}^2} \\tilde{E}^i\n *      \\right\\}\n * \\right]\n * \\f}\n *\n * See ComputeDriftTildeJ and ComputeParallelTildeJ for the details of each\n * terms.\n *\n */\nstruct ComputeTildeJ : TildeJ, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<Tags::TildeQ, Tags::TildeE, Tags::TildeB,\n                 Tags::ParallelConductivity, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>>;\n  using return_type = tnsr::I<DataVector, 3>;\n  using base = TildeJ;\n\n  static void function(\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_j,\n      const Scalar<DataVector>& tilde_q,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      double parallel_conductivity, const Scalar<DataVector>& lapse,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric);\n};\n}  // namespace Tags\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/ElectromagneticVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/ElectromagneticVariables.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree {\n\nvoid em_field_from_evolved_fields(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> vector,\n    const tnsr::I<DataVector, 3>& densitized_vector,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  get<0>(*vector) = get<0>(densitized_vector) / get(sqrt_det_spatial_metric);\n  get<1>(*vector) = get<1>(densitized_vector) / get(sqrt_det_spatial_metric);\n  get<2>(*vector) = get<2>(densitized_vector) / get(sqrt_det_spatial_metric);\n}\n\nvoid charge_density_from_tilde_q(\n    const gsl::not_null<Scalar<DataVector>*> charge_density,\n    const Scalar<DataVector>& tilde_q,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  get(*charge_density) = get(tilde_q) / get(sqrt_det_spatial_metric);\n}\n\nvoid electric_current_density_from_tilde_j(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> electric_current_density,\n    const tnsr::I<DataVector, 3>& tilde_j,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const Scalar<DataVector>& lapse) {\n  em_field_from_evolved_fields(electric_current_density, tilde_j,\n                               sqrt_det_spatial_metric);\n  get<0>(*electric_current_density) =\n      get<0>(*electric_current_density) / get(lapse);\n  get<1>(*electric_current_density) =\n      get<1>(*electric_current_density) / get(lapse);\n  get<2>(*electric_current_density) =\n      get<2>(*electric_current_density) / get(lapse);\n}\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/ElectromagneticVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree {\n\n/*!\n * \\brief Computes electric field $E^i$ from TildeE or magnetic field $B^i$ from\n * TildeB.\n */\nvoid em_field_from_evolved_fields(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> vector,\n    const tnsr::I<DataVector, 3>& densitized_vector,\n    const Scalar<DataVector>& sqrt_det_spatial_metric);\n\n/*!\n * \\brief Computes electric charge density $q$ from TildeQ.\n */\nvoid charge_density_from_tilde_q(\n    const gsl::not_null<Scalar<DataVector>*> charge_density,\n    const Scalar<DataVector>& tilde_q,\n    const Scalar<DataVector>& sqrt_det_spatial_metric);\n\n/*!\n * \\brief Computes electric current density $J^i$ from TildeJ.\n */\nvoid electric_current_density_from_tilde_j(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> electric_current_density,\n    const tnsr::I<DataVector, 3>& tilde_j,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const Scalar<DataVector>& lapse);\n\nnamespace Tags {\n/*!\n * \\brief Compute item for electric field $E^i$ from TildeE.\n *\n * \\note This ComputeTag is solely for observation purpose, not related to\n * actual time evolution.\n */\nstruct ElectricFieldCompute : ElectricField, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<TildeE, gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n  using return_type = tnsr::I<DataVector, 3>;\n  using base = ElectricField;\n\n  static constexpr auto function = &em_field_from_evolved_fields;\n};\n\n/*!\n * \\brief Compute item for magnetic field $B^i$ from TildeB.\n *\n * \\note This ComputeTag is solely for observation purpose, not related to\n * actual time evolution.\n */\nstruct MagneticFieldCompute : MagneticField, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<TildeB, gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n  using return_type = tnsr::I<DataVector, 3>;\n  using base = MagneticField;\n\n  static constexpr auto function = &em_field_from_evolved_fields;\n};\n\n/*!\n * \\brief Compute item for electric charge density $q$ from TildeQ.\n *\n * \\note This ComputeTag is solely for observation purpose, not related to\n * actual time evolution.\n */\nstruct ChargeDensityCompute : ChargeDensity, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<TildeQ, gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n  using return_type = Scalar<DataVector>;\n  using base = ChargeDensity;\n\n  static constexpr auto function = &charge_density_from_tilde_q;\n};\n\n/*!\n * \\brief Compute item for electric current density $J^i$ from TildeJ.\n *\n * \\note This ComputeTag is solely for observation purpose, not related to\n * actual time evolution.\n */\nstruct ElectricCurrentDensityCompute : ElectricCurrentDensity, db::ComputeTag {\n  using argument_tags = tmpl::list<TildeJ, gr::Tags::Lapse<DataVector>,\n                                   gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n  using return_type = tnsr::I<DataVector, 3>;\n  using base = ElectricCurrentDensity;\n\n  static constexpr auto function = &electric_current_density_from_tilde_j;\n};\n\n}  // namespace Tags\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/AdaptiveOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/AdaptiveOrder.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <utility>\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace ForceFree::fd {\n\nAdaptiveOrder::AdaptiveOrder(\n    const double alpha_5, const std::optional<double> alpha_7,\n    const std::optional<double> alpha_9,\n    const ::fd::reconstruction::FallbackReconstructorType\n        low_order_reconstructor,\n    const Options::Context& context)\n    : four_to_the_alpha_5_(pow(4.0, alpha_5)),\n      low_order_reconstructor_(low_order_reconstructor) {\n  if (low_order_reconstructor_ ==\n      ::fd::reconstruction::FallbackReconstructorType::None) {\n    PARSE_ERROR(context, \"None is not an allowed low-order reconstructor.\");\n  }\n  if (alpha_7.has_value()) {\n    six_to_the_alpha_7_ = pow(6.0, alpha_7.value());\n  }\n  if (alpha_9.has_value()) {\n    eight_to_the_alpha_9_ = pow(8.0, alpha_9.value());\n  }\n  set_function_pointers();\n}\n\nAdaptiveOrder::AdaptiveOrder(CkMigrateMessage* const msg)\n    : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor> AdaptiveOrder::get_clone() const {\n  return std::make_unique<AdaptiveOrder>(*this);\n}\n\nvoid AdaptiveOrder::set_function_pointers() {\n  std::tie(reconstruct_, reconstruct_lower_neighbor_,\n           reconstruct_upper_neighbor_) = ::fd::reconstruction::\n      positivity_preserving_adaptive_order_function_pointers<3, false>(\n          false, eight_to_the_alpha_9_.has_value(),\n          six_to_the_alpha_7_.has_value(), low_order_reconstructor_);\n  std::tie(pp_reconstruct_, pp_reconstruct_lower_neighbor_,\n           pp_reconstruct_upper_neighbor_) = ::fd::reconstruction::\n      positivity_preserving_adaptive_order_function_pointers<3, true>(\n          true, eight_to_the_alpha_9_.has_value(),\n          six_to_the_alpha_7_.has_value(), low_order_reconstructor_);\n}\n\nvoid AdaptiveOrder::pup(PUP::er& p) {\n  Reconstructor::pup(p);\n  p | four_to_the_alpha_5_;\n  p | six_to_the_alpha_7_;\n  p | eight_to_the_alpha_9_;\n  p | low_order_reconstructor_;\n  if (p.isUnpacking()) {\n    set_function_pointers();\n  }\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID AdaptiveOrder::my_PUP_ID = 0;\n\nvoid AdaptiveOrder::reconstruct(\n    const gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n        vars_on_upper_face,\n    const Variables<volume_vars_tags>& volume_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh) const {\n  DirectionalIdMap<dim, Variables<volume_vars_tags>> neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_work(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_variables, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr, volume_variables,\n                     ghost_cell_vars, subcell_extents, number_of_variables,\n                     four_to_the_alpha_5_,\n                     six_to_the_alpha_7_.value_or(\n                         std::numeric_limits<double>::signaling_NaN()),\n                     eight_to_the_alpha_9_.value_or(\n                         std::numeric_limits<double>::signaling_NaN()));\n      },\n      volume_vars, tilde_j, element, ghost_data, subcell_mesh,\n      ghost_zone_size());\n}\n\nvoid AdaptiveOrder::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<recons_tags>*> vars_on_face,\n    const Variables<volume_vars_tags>& volume_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const Direction<dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor, const auto& subcell_extents,\n             const auto& ghost_data_extents,\n             const auto& local_direction_to_reconstruct) {\n        reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor, const auto& subcell_extents,\n             const auto& ghost_data_extents,\n             const auto& local_direction_to_reconstruct) {\n        reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      volume_vars, tilde_j, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size());\n}\n\nbool operator==(const AdaptiveOrder& lhs, const AdaptiveOrder& rhs) {\n  // Don't check function pointers since they are set from\n  // low_order_reconstructor_\n  return lhs.four_to_the_alpha_5_ == rhs.four_to_the_alpha_5_ and\n         lhs.six_to_the_alpha_7_ == rhs.six_to_the_alpha_7_ and\n         lhs.eight_to_the_alpha_9_ == rhs.eight_to_the_alpha_9_ and\n         lhs.low_order_reconstructor_ == rhs.low_order_reconstructor_;\n}\n\nbool operator!=(const AdaptiveOrder& lhs, const AdaptiveOrder& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/AdaptiveOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t dim>\nclass Direction;\ntemplate <size_t dim>\nclass Element;\ntemplate <size_t dim>\nclass ElementId;\ntemplate <size_t dim>\nclass Mesh;\ntemplate <typename recons_tags>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::fd {\n\n/*!\n * \\brief Adaptive order FD reconstruction. See\n * ::fd::reconstruction::positivity_preserving_adaptive_order() for details.\n * Note that in the ForceFree evolution system no variable needs to be kept\n * positive.\n *\n */\nclass AdaptiveOrder : public Reconstructor {\n private:\n  using TildeE = ForceFree::Tags::TildeE;\n  using TildeB = ForceFree::Tags::TildeB;\n  using TildePsi = ForceFree::Tags::TildePsi;\n  using TildePhi = ForceFree::Tags::TildePhi;\n  using TildeQ = ForceFree::Tags::TildeQ;\n  using TildeJ = ForceFree::Tags::TildeJ;\n\n  using volume_vars_tags =\n      tmpl::list<TildeE, TildeB, TildePsi, TildePhi, TildeQ>;\n\n  using recons_tags = ForceFree::fd::tags_list_for_reconstruction;\n\n  using FallbackReconstructorType =\n      ::fd::reconstruction::FallbackReconstructorType;\n\n public:\n  static constexpr size_t dim = 3;\n\n  struct Alpha5 {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations.\"};\n  };\n  struct Alpha7 {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations. If not specified then \"\n        \"7th-order reconstruction is not used.\"};\n  };\n  struct Alpha9 {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations. If not specified then \"\n        \"9th-order reconstruction is not used.\"};\n  };\n  struct LowOrderReconstructor {\n    using type = FallbackReconstructorType;\n    static constexpr Options::String help = {\n        \"The 2nd/3rd-order reconstruction scheme to use if unlimited 5th-order \"\n        \"isn't okay.\"};\n  };\n\n  using options = tmpl::list<Alpha5, Alpha7, Alpha9, LowOrderReconstructor>;\n\n  static constexpr Options::String help{\"Adaptive-order reconstruction.\"};\n  AdaptiveOrder() = default;\n  AdaptiveOrder(AdaptiveOrder&&) = default;\n  AdaptiveOrder& operator=(AdaptiveOrder&&) = default;\n  AdaptiveOrder(const AdaptiveOrder&) = default;\n  AdaptiveOrder& operator=(const AdaptiveOrder&) = default;\n  ~AdaptiveOrder() override = default;\n\n  AdaptiveOrder(double alpha_5, std::optional<double> alpha_7,\n                std::optional<double> alpha_9,\n                FallbackReconstructorType low_order_reconstructor,\n                const Options::Context& context = {});\n\n  explicit AdaptiveOrder(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor, AdaptiveOrder);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  static constexpr bool use_adaptive_order = true;\n  bool supports_adaptive_order() const override { return use_adaptive_order; }\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override {\n    return eight_to_the_alpha_9_.has_value()\n               ? 5\n               : (six_to_the_alpha_7_.has_value() ? 4 : 3);\n  }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<volume_vars_tags>, TildeJ,\n                 domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>>;\n\n  void reconstruct(\n      gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n          vars_on_lower_face,\n      gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n          vars_on_upper_face,\n      const Variables<volume_vars_tags>& volume_vars,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh) const;\n\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<recons_tags>*> vars_on_face,\n      const Variables<volume_vars_tags>& volume_vars,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const Direction<dim> direction_to_reconstruct) const;\n\n private:\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const AdaptiveOrder& lhs, const AdaptiveOrder& rhs);\n  friend bool operator!=(const AdaptiveOrder& lhs, const AdaptiveOrder& rhs);\n  void set_function_pointers();\n\n  double four_to_the_alpha_5_ = std::numeric_limits<double>::signaling_NaN();\n  std::optional<double> six_to_the_alpha_7_{};\n  std::optional<double> eight_to_the_alpha_9_{};\n  FallbackReconstructorType low_order_reconstructor_ =\n      FallbackReconstructorType::None;\n\n  using PointerReconsOrder = void (*)(\n      gsl::not_null<std::array<gsl::span<double>, dim>*>,\n      gsl::not_null<std::array<gsl::span<double>, dim>*>,\n      gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, dim>>*>,\n      const gsl::span<const double>&,\n      const DirectionMap<dim, gsl::span<const double>>&, const Index<dim>&,\n      size_t, double, double, double);\n  using PointerRecons =\n      void (*)(gsl::not_null<std::array<gsl::span<double>, dim>*>,\n               gsl::not_null<std::array<gsl::span<double>, dim>*>,\n               const gsl::span<const double>&,\n               const DirectionMap<dim, gsl::span<const double>>&,\n               const Index<dim>&, size_t, double, double, double);\n  PointerRecons reconstruct_ = nullptr;\n  PointerReconsOrder pp_reconstruct_ = nullptr;\n\n  using PointerNeighbor = void (*)(gsl::not_null<DataVector*>,\n                                   const DataVector&, const DataVector&,\n                                   const Index<dim>&, const Index<dim>&,\n                                   const Direction<dim>&, const double&,\n                                   const double&, const double&);\n  PointerNeighbor reconstruct_lower_neighbor_ = nullptr;\n  PointerNeighbor reconstruct_upper_neighbor_ = nullptr;\n  PointerNeighbor pp_reconstruct_lower_neighbor_ = nullptr;\n  PointerNeighbor pp_reconstruct_upper_neighbor_ = nullptr;\n};\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AdaptiveOrder.cpp\n  MonotonisedCentral.cpp\n  Reconstructor.cpp\n  RegisterDerivedWithCharm.cpp\n  Wcns5z.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AdaptiveOrder.hpp\n  Factory.hpp\n  FiniteDifference.hpp\n  MonotonisedCentral.hpp\n  Reconstructor.hpp\n  ReconstructWork.hpp\n  ReconstructWork.tpp\n  RegisterDerivedWithCharm.hpp\n  Tags.hpp\n  Wcns5z.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/AdaptiveOrder.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Wcns5z.hpp\"\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/FiniteDifference.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ForceFree {\n/*!\n * \\brief Finite difference functionality for ForceFree evolution system\n */\nnamespace fd {}\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/MonotonisedCentral.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree::fd {\n\nMonotonisedCentral::MonotonisedCentral(CkMigrateMessage* const msg)\n    : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor> MonotonisedCentral::get_clone() const {\n  return std::make_unique<MonotonisedCentral>(*this);\n}\n\nvoid MonotonisedCentral::pup(PUP::er& p) { Reconstructor::pup(p); }\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID MonotonisedCentral::my_PUP_ID = 0;\n\nvoid MonotonisedCentral::reconstruct(\n    const gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n        vars_on_upper_face,\n    const Variables<volume_vars_tags>& volume_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh) const {\n  DirectionalIdMap<dim, Variables<volume_vars_tags>> neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_work(\n      vars_on_lower_face, vars_on_upper_face,\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_variables, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::monotonised_central(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_variables,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      volume_vars, tilde_j, element, ghost_data, subcell_mesh,\n      ghost_zone_size());\n}\n\nvoid MonotonisedCentral::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<recons_tags>*> vars_on_face,\n    const Variables<volume_vars_tags>& volume_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const Direction<dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work(\n      vars_on_face,\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor, const auto& subcell_extents,\n         const auto& ghost_data_extents,\n         const auto& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor, const auto& subcell_extents,\n         const auto& ghost_data_extents,\n         const auto& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      volume_vars, tilde_j, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size());\n}\n\nbool operator==(const MonotonisedCentral& /*lhs*/,\n                const MonotonisedCentral& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const MonotonisedCentral& lhs, const MonotonisedCentral& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/MonotonisedCentral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t dim>\nclass Direction;\ntemplate <size_t dim>\nclass Element;\ntemplate <size_t dim>\nclass ElementId;\ntemplate <size_t dim>\nclass Mesh;\ntemplate <typename recons_tags>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::fd {\n\n/*!\n * \\brief Monotonised central reconstruction. See\n * ::fd::reconstruction::monotonised_central() for details.\n */\n\nclass MonotonisedCentral : public Reconstructor {\n private:\n  using TildeE = ForceFree::Tags::TildeE;\n  using TildeB = ForceFree::Tags::TildeB;\n  using TildePsi = ForceFree::Tags::TildePsi;\n  using TildePhi = ForceFree::Tags::TildePhi;\n  using TildeQ = ForceFree::Tags::TildeQ;\n  using TildeJ = ForceFree::Tags::TildeJ;\n\n  using volume_vars_tags =\n      tmpl::list<TildeE, TildeB, TildePsi, TildePhi, TildeQ>;\n\n  using recons_tags = ForceFree::fd::tags_list_for_reconstruction;\n\n public:\n  static constexpr size_t dim = 3;\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Monotonised central reconstruction scheme.\"};\n\n  MonotonisedCentral() = default;\n  MonotonisedCentral(MonotonisedCentral&&) = default;\n  MonotonisedCentral& operator=(MonotonisedCentral&&) = default;\n  MonotonisedCentral(const MonotonisedCentral&) = default;\n  MonotonisedCentral& operator=(const MonotonisedCentral&) = default;\n  ~MonotonisedCentral() override = default;\n\n  explicit MonotonisedCentral(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor, MonotonisedCentral);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  static constexpr bool use_adaptive_order = false;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 2; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<volume_vars_tags>, TildeJ,\n                 domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>>;\n\n  void reconstruct(\n      gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n          vars_on_lower_face,\n      gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n          vars_on_upper_face,\n      const Variables<volume_vars_tags>& volume_vars,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh) const;\n\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<recons_tags>*> vars_on_face,\n      const Variables<volume_vars_tags>& volume_vars,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const Direction<dim> direction_to_reconstruct) const;\n};\n\nbool operator==(const MonotonisedCentral& /*lhs*/,\n                const MonotonisedCentral& /*rhs*/);\n\nbool operator!=(const MonotonisedCentral& lhs, const MonotonisedCentral& rhs);\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/ReconstructWork.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree::fd {\n/*!\n * \\brief Reconstructs the evolved variables \\f$\\tilde{E}^i, \\tilde{B}^i,\n * \\tilde{\\psi}, \\tilde{\\phi}, \\tilde{q}\\f$ and the generalized electric current\n * density \\f$\\tilde{J}^i\\f$. All results are written into `vars_on_lower_face`\n * and `vars_on_upper_face`.\n *\n */\ntemplate <typename TagsList, typename Reconstructor>\nvoid reconstruct_work(\n    gsl::not_null<std::array<Variables<TagsList>, 3>*> vars_on_lower_face,\n    gsl::not_null<std::array<Variables<TagsList>, 3>*> vars_on_upper_face,\n    const Reconstructor& reconstruct,\n    const Variables<System::variables_tag::tags_list>& volume_evolved_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& volume_tilde_j,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& neighbor_data,\n    const Mesh<3>& subcell_mesh, const size_t ghost_zone_size);\n\n/*!\n * \\brief Reconstructs the evolved variables \\f$\\tilde{E}^i, \\tilde{B}^i,\n * \\tilde{\\psi}, \\tilde{\\phi}, \\tilde{q}\\f$ and the generalized electric current\n * density \\f$\\tilde{J}^i\\f$.\n *\n * All results are written into `vars_on_face`.\n *\n * This is used on DG elements to reconstruct their subcell neighbors' solution\n * on the shared faces.\n */\ntemplate <typename TagsList, typename ReconstructLower,\n          typename ReconstructUpper>\nvoid reconstruct_fd_neighbor_work(\n    gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const ReconstructLower& reconstruct_lower_neighbor,\n    const ReconstructUpper& reconstruct_upper_neighbor,\n    const Variables<System::variables_tag::tags_list>&\n        subcell_volume_evolved_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_volume_tilde_j,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh, const Direction<3>& direction_to_reconstruct,\n    const size_t ghost_zone_size);\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/ReconstructWork.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::fd {\n\ntemplate <typename TagsList, typename Reconstructor>\nvoid reconstruct_work(\n    const gsl::not_null<std::array<Variables<TagsList>, 3>*> vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, 3>*> vars_on_upper_face,\n    const Reconstructor& reconstruct,\n    const Variables<System::variables_tag::tags_list>& volume_evolved_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& volume_tilde_j,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& neighbor_data,\n    const Mesh<3>& subcell_mesh, const size_t ghost_zone_size) {\n  ASSERT(is_isotropic(subcell_mesh),\n         \"The subcell mesh should be isotropic but got \" << subcell_mesh);\n\n  const size_t volume_num_pts = subcell_mesh.number_of_grid_points();\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n  const size_t neighbor_num_pts =\n      ghost_zone_size * subcell_mesh.extents().slice_away(0).product();\n\n  // We reconstruct the evolved variables (TildeE, TildeB, TildePsi, TildePhi,\n  // TildeQ) which are contained in the function argument `volume_evolved_vars`,\n  // and also reconstruct the generalized current density TildeJ which is passed\n  // as the function argument `volume_tilde_j`.\n  //\n  // Core FD reconstruction routines (from NumericalAlgorithms) requires the\n  // input data for reconstruction to be consecutive on memory. This is\n  // true for `volume_evolved_vars` but not true for `volume_tilde_j` which is a\n  // SpECTRE tensor. Therefore we need to allocate a single block of data\n  // storing the values of TildeJ by means of a Variables object.\n  using VarTildeJ = Variables<tmpl::list<ForceFree::Tags::TildeJ>>;\n  VarTildeJ var_tilde_j{volume_num_pts};\n  for (size_t d = 0; d < 3; ++d) {\n    get<ForceFree::Tags::TildeJ>(var_tilde_j).get(d) = volume_tilde_j.get(d);\n  }\n\n  size_t vars_in_neighbor_count = 0;\n  tmpl::for_each<\n      ForceFree::fd::tags_list_for_reconstruction>([&element, &neighbor_data,\n                                                    neighbor_num_pts,\n                                                    reconstructed_num_pts,\n                                                    volume_num_pts,\n                                                    &reconstruct,\n                                                    &vars_in_neighbor_count,\n                                                    &volume_evolved_vars,\n                                                    &var_tilde_j,\n                                                    &vars_on_lower_face,\n                                                    &vars_on_upper_face,\n                                                    &subcell_mesh](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n\n    const typename tag::type* volume_tensor_ptr = [&volume_evolved_vars,\n                                                   &var_tilde_j]() {\n      if constexpr (std::is_same_v<tag, ForceFree::Tags::TildeJ>) {\n        (void)volume_evolved_vars;  // avoid compiler warnings\n        return &get<tag>(var_tilde_j);\n      } else {\n        (void)var_tilde_j;  // avoid compiler warnings\n        return &get<tag>(volume_evolved_vars);\n      }\n    }();\n\n    const size_t number_of_variables = volume_tensor_ptr->size();\n\n    const gsl::span<const double> volume_vars_span = gsl::make_span(\n        (*volume_tensor_ptr)[0].data(), number_of_variables * volume_num_pts);\n\n    std::array<gsl::span<double>, 3> upper_face_vars{};\n    std::array<gsl::span<double>, 3> lower_face_vars{};\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(upper_face_vars, i) =\n          gsl::make_span(get<tag>(gsl::at(*vars_on_upper_face, i))[0].data(),\n                         number_of_variables * reconstructed_num_pts);\n      gsl::at(lower_face_vars, i) =\n          gsl::make_span(get<tag>(gsl::at(*vars_on_lower_face, i))[0].data(),\n                         number_of_variables * reconstructed_num_pts);\n    }\n\n    DirectionMap<3, gsl::span<const double>> ghost_cell_vars{};\n\n    for (const auto& direction : Direction<3>::all_directions()) {\n      if (element.neighbors().contains(direction)) {\n        const auto& neighbors_in_direction = element.neighbors().at(direction);\n\n        ASSERT(neighbors_in_direction.size() == 1,\n               \"Currently only support one neighbor in each direction, but \"\n               \"got \"\n                   << neighbors_in_direction.size() << \" in direction \"\n                   << direction);\n\n        const DataVector& neighbor_data_dv =\n            neighbor_data\n                .at(DirectionalId<3>{direction,\n                                     *neighbors_in_direction.begin()})\n                .neighbor_ghost_data_for_reconstruction();\n\n        ASSERT(neighbor_data_dv.size() != 0,\n               \"The neighber data is empty in direction \"\n                   << direction << \" on element id \" << element.id());\n\n        ghost_cell_vars[direction] = gsl::make_span(\n            &neighbor_data_dv[vars_in_neighbor_count * neighbor_num_pts],\n            number_of_variables * neighbor_num_pts);\n      } else {\n        // retrieve boundary ghost data from neighbor_data\n        ASSERT(\n            element.external_boundaries().count(direction) == 1,\n            \"Element has neither neighbor nor external boundary to direction : \"\n                << direction);\n\n        const DataVector& neighbor_data_dv =\n            neighbor_data\n                .at(DirectionalId<3>{direction,\n                                   ElementId<3>::external_boundary_id()})\n                .neighbor_ghost_data_for_reconstruction();\n\n        ghost_cell_vars[direction] = gsl::make_span(\n            &neighbor_data_dv[0], number_of_variables * neighbor_num_pts);\n      }\n    }\n\n    reconstruct(make_not_null(&upper_face_vars),\n                make_not_null(&lower_face_vars), volume_vars_span,\n                ghost_cell_vars, subcell_mesh.extents(), number_of_variables);\n\n    vars_in_neighbor_count += number_of_variables;\n  });\n}\n\ntemplate <typename TagsList, typename ReconstructLower,\n          typename ReconstructUpper>\nvoid reconstruct_fd_neighbor_work(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const ReconstructLower& reconstruct_lower_neighbor,\n    const ReconstructUpper& reconstruct_upper_neighbor,\n    const Variables<System::variables_tag::tags_list>&\n        subcell_volume_evolved_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_volume_tilde_j,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh, const Direction<3>& direction_to_reconstruct,\n    const size_t ghost_zone_size) {\n  const DirectionalId<3> mortar_id{\n      direction_to_reconstruct,\n      *element.neighbors().at(direction_to_reconstruct).begin()};\n\n  Index<3> ghost_data_extents = subcell_mesh.extents();\n  ghost_data_extents[direction_to_reconstruct.dimension()] = ghost_zone_size;\n\n  Variables<ForceFree::fd::tags_list_for_reconstruction> neighbor_vars{};\n  {\n    ASSERT(ghost_data.contains(mortar_id),\n           \"The neighbor data does not contain the mortar: \" << mortar_id);\n    const DataVector& neighbor_data_on_mortar =\n        ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    neighbor_vars.set_data_ref(\n        const_cast<double*>(neighbor_data_on_mortar.data()),\n        neighbor_vars.number_of_independent_components *\n            ghost_data_extents.product());\n  }\n\n  tmpl::for_each<ForceFree::fd::tags_list_for_reconstruction>(\n      [&direction_to_reconstruct, &ghost_data_extents, &neighbor_vars,\n       &reconstruct_lower_neighbor, &reconstruct_upper_neighbor, &subcell_mesh,\n       &subcell_volume_evolved_vars, &subcell_volume_tilde_j,\n       &vars_on_face](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n\n        const typename tag::type* volume_tensor_ptr =\n            [&subcell_volume_evolved_vars, &subcell_volume_tilde_j]() {\n              if constexpr (std::is_same_v<tag, ForceFree::Tags::TildeJ>) {\n                (void)subcell_volume_evolved_vars;  // avoid compiler warnings\n                return &subcell_volume_tilde_j;\n              } else {\n                (void)subcell_volume_tilde_j;  // avoid compiler warnings\n                return &get<tag>(subcell_volume_evolved_vars);\n              }\n            }();\n\n        const auto& tensor_neighbor = get<tag>(neighbor_vars);\n        auto& tensor_on_face = get<tag>(*vars_on_face);\n\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_upper_neighbor(\n                make_not_null(&tensor_on_face[tensor_index]),\n                (*volume_tensor_ptr)[tensor_index],\n                tensor_neighbor[tensor_index], subcell_mesh.extents(),\n                ghost_data_extents, direction_to_reconstruct);\n          }\n        } else {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_lower_neighbor(\n                make_not_null(&tensor_on_face[tensor_index]),\n                (*volume_tensor_ptr)[tensor_index],\n                tensor_neighbor[tensor_index], subcell_mesh.extents(),\n                ghost_data_extents, direction_to_reconstruct);\n          }\n        }\n      });\n}\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n\n#include <pup.h>\n\nnamespace ForceFree::fd {\nReconstructor::Reconstructor(CkMigrateMessage* const msg) : PUP::able(msg) {}\n\nvoid Reconstructor::pup(PUP::er& p) { PUP::able::pup(p); }\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::fd {\n/// \\cond\nclass AdaptiveOrder;\nclass MonotonisedCentral;\nclass Wcns5z;\n/// \\endcond\n\n/*!\n * \\brief The base class from which all reconstruction schemes must inherit\n */\nclass Reconstructor : public PUP::able {\n public:\n  Reconstructor() = default;\n  Reconstructor(const Reconstructor&) = default;\n  Reconstructor& operator=(const Reconstructor&) = default;\n  Reconstructor(Reconstructor&&) = default;\n  Reconstructor& operator=(Reconstructor&&) = default;\n  ~Reconstructor() override = default;\n\n  void pup(PUP::er& p) override;\n\n  /// \\cond\n  explicit Reconstructor(CkMigrateMessage* msg);\n  WRAPPED_PUPable_abstract(Reconstructor);  // NOLINT\n  /// \\endcond\n\n  using creatable_classes =\n      tmpl::list<AdaptiveOrder, MonotonisedCentral, Wcns5z>;\n\n  virtual std::unique_ptr<Reconstructor> get_clone() const = 0;\n\n  virtual size_t ghost_zone_size() const = 0;\n\n  virtual bool supports_adaptive_order() const { return false; }\n};\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace ForceFree::fd {\nvoid register_derived_with_charm() {\n  register_classes_with_charm(typename Reconstructor::creatable_classes{});\n}\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ForceFree::fd {\nvoid register_derived_with_charm();\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellSolver.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::fd {\n\n// A tag list of variables used for FD reconstruction process. For the ForceFree\n// evolution system, we use the whole set of evolved variables and the\n// generalized current density TildeJ.\nusing tags_list_for_reconstruction =\n    tmpl::list<ForceFree::Tags::TildeJ, ForceFree::Tags::TildeE,\n               ForceFree::Tags::TildeB, ForceFree::Tags::TildePsi,\n               ForceFree::Tags::TildePhi, ForceFree::Tags::TildeQ>;\n\nnamespace OptionTags {\n/*!\n * \\brief Holds the subcell reconstructor in the input file\n */\nstruct Reconstructor {\n  using type = std::unique_ptr<fd::Reconstructor>;\n\n  static constexpr Options::String help = {\"The reconstruction scheme to use.\"};\n  using group = evolution::dg::subcell::OptionTags::SubcellSolverGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief Tag for the reconstructor\n */\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<fd::Reconstructor>;\n  using option_tags = tmpl::list<OptionTags::Reconstructor>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& reconstructor) {\n    return reconstructor->get_clone();\n  }\n};\n}  // namespace Tags\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/Wcns5z.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Wcns5z.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <utility>\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Wcns5z.hpp\"\n\nnamespace ForceFree::fd {\n\nWcns5z::Wcns5z(const size_t nonlinear_weight_exponent, const double epsilon,\n               const ::fd::reconstruction::FallbackReconstructorType\n                   fallback_reconstructor,\n               const size_t max_number_of_extrema)\n    : nonlinear_weight_exponent_(nonlinear_weight_exponent),\n      epsilon_(epsilon),\n      fallback_reconstructor_(fallback_reconstructor),\n      max_number_of_extrema_(max_number_of_extrema) {\n  std::tie(reconstruct_, reconstruct_lower_neighbor_,\n           reconstruct_upper_neighbor_) =\n      ::fd::reconstruction::wcns5z_function_pointers<3>(\n          nonlinear_weight_exponent_, fallback_reconstructor_);\n}\n\nWcns5z::Wcns5z(CkMigrateMessage* const msg) : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor> Wcns5z::get_clone() const {\n  return std::make_unique<Wcns5z>(*this);\n}\n\nvoid Wcns5z::pup(PUP::er& p) {\n  Reconstructor::pup(p);\n  p | nonlinear_weight_exponent_;\n  p | epsilon_;\n  p | fallback_reconstructor_;\n  p | max_number_of_extrema_;\n  if (p.isUnpacking()) {\n    std::tie(reconstruct_, reconstruct_lower_neighbor_,\n             reconstruct_upper_neighbor_) =\n        ::fd::reconstruction::wcns5z_function_pointers<3>(\n            nonlinear_weight_exponent_, fallback_reconstructor_);\n  }\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Wcns5z::my_PUP_ID = 0;\n\nvoid Wcns5z::reconstruct(\n    const gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n        vars_on_upper_face,\n    const Variables<volume_vars_tags>& volume_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh) const {\n  DirectionalIdMap<dim, Variables<volume_vars_tags>> neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_work(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_variables, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr, volume_variables,\n                     ghost_cell_vars, subcell_extents, number_of_variables,\n                     epsilon_, max_number_of_extrema_);\n      },\n      volume_vars, tilde_j, element, ghost_data, subcell_mesh,\n      ghost_zone_size());\n}\n\nvoid Wcns5z::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<recons_tags>*> vars_on_face,\n    const Variables<volume_vars_tags>& volume_vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const Direction<dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor, const auto& subcell_extents,\n             const auto& ghost_data_extents,\n             const auto& local_direction_to_reconstruct) {\n        reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, epsilon_, max_number_of_extrema_);\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor, const auto& subcell_extents,\n             const auto& ghost_data_extents,\n             const auto& local_direction_to_reconstruct) {\n        reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, epsilon_, max_number_of_extrema_);\n      },\n      volume_vars, tilde_j, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size());\n}\n\nbool operator==(const Wcns5z& lhs, const Wcns5z& rhs) {\n  // Don't check function pointers since they are set from\n  // nonlinear_weight_exponent_ and fallback_reconstructor_\n  return lhs.nonlinear_weight_exponent_ == rhs.nonlinear_weight_exponent_ and\n         lhs.epsilon_ == rhs.epsilon_ and\n         lhs.fallback_reconstructor_ == rhs.fallback_reconstructor_ and\n         lhs.max_number_of_extrema_ == rhs.max_number_of_extrema_;\n}\n\nbool operator!=(const Wcns5z& lhs, const Wcns5z& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/FiniteDifference/Wcns5z.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t dim>\nclass Direction;\ntemplate <size_t dim>\nclass Element;\ntemplate <size_t dim>\nclass ElementId;\ntemplate <size_t dim>\nclass Mesh;\ntemplate <typename recons_tags>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::fd {\n\n/*!\n * \\brief Fifth order weighted nonlinear compact scheme reconstruction using the\n * Z oscillation indicator. See ::fd::reconstruction::wcns5z() for details.\n */\nclass Wcns5z : public Reconstructor {\n private:\n  using TildeE = ForceFree::Tags::TildeE;\n  using TildeB = ForceFree::Tags::TildeB;\n  using TildePsi = ForceFree::Tags::TildePsi;\n  using TildePhi = ForceFree::Tags::TildePhi;\n  using TildeQ = ForceFree::Tags::TildeQ;\n  using TildeJ = ForceFree::Tags::TildeJ;\n\n  using volume_vars_tags =\n      tmpl::list<TildeE, TildeB, TildePsi, TildePhi, TildeQ>;\n\n  using recons_tags = ForceFree::fd::tags_list_for_reconstruction;\n\n  using FallbackReconstructorType =\n      ::fd::reconstruction::FallbackReconstructorType;\n\n public:\n  static constexpr size_t dim = 3;\n\n  struct NonlinearWeightExponent {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The exponent q to which the oscillation indicator term is raised\"};\n  };\n  struct Epsilon {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The parameter added to the oscillation indicators to avoid division \"\n        \"by zero\"};\n  };\n  struct FallbackReconstructor {\n    using type = FallbackReconstructorType;\n    static constexpr Options::String help = {\n        \"A reconstruction scheme to fallback to adaptively. Finite difference \"\n        \"will switch to this reconstruction scheme if there are more extrema \"\n        \"in a FD stencil than a specified number. See also the option \"\n        \"'MaxNumberOfExtrema' below. Adaptive fallback is disabled if 'None'.\"};\n  };\n  struct MaxNumberOfExtrema {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The maximum allowed number of extrema in FD stencil for using Wcns5z \"\n        \"reconstruction before switching to a low-order reconstruction. If \"\n        \"FallbackReconstructor=None, this option is ignored\"};\n  };\n\n  using options = tmpl::list<NonlinearWeightExponent, Epsilon,\n                             FallbackReconstructor, MaxNumberOfExtrema>;\n\n  static constexpr Options::String help{\"WCNS 5Z reconstruction scheme.\"};\n\n  Wcns5z() = default;\n  Wcns5z(Wcns5z&&) = default;\n  Wcns5z& operator=(Wcns5z&&) = default;\n  Wcns5z(const Wcns5z&) = default;\n  Wcns5z& operator=(const Wcns5z&) = default;\n  ~Wcns5z() override = default;\n\n  Wcns5z(size_t nonlinear_weight_exponent, double epsilon,\n         FallbackReconstructorType fallback_reconstructor,\n         size_t max_number_of_extrema);\n\n  explicit Wcns5z(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor, Wcns5z);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  static constexpr bool use_adaptive_order = false;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 3; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<volume_vars_tags>, TildeJ,\n                 domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>>;\n\n  void reconstruct(\n      gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n          vars_on_lower_face,\n      gsl::not_null<std::array<Variables<recons_tags>, dim>*>\n          vars_on_upper_face,\n      const Variables<volume_vars_tags>& volume_vars,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh) const;\n\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<recons_tags>*> vars_on_face,\n      const Variables<volume_vars_tags>& volume_vars,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const Direction<dim> direction_to_reconstruct) const;\n\n private:\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Wcns5z& lhs, const Wcns5z& rhs);\n  friend bool operator!=(const Wcns5z& lhs, const Wcns5z& rhs);\n\n  size_t nonlinear_weight_exponent_ = 0;\n  double epsilon_ = std::numeric_limits<double>::signaling_NaN();\n  FallbackReconstructorType fallback_reconstructor_ =\n      FallbackReconstructorType::None;\n  size_t max_number_of_extrema_ = 0;\n\n  void (*reconstruct_)(gsl::not_null<std::array<gsl::span<double>, dim>*>,\n                       gsl::not_null<std::array<gsl::span<double>, dim>*>,\n                       const gsl::span<const double>&,\n                       const DirectionMap<dim, gsl::span<const double>>&,\n                       const Index<dim>&, size_t, double, size_t) = nullptr;\n  void (*reconstruct_lower_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<dim>&, const Index<dim>&,\n                                      const Direction<dim>&, const double&,\n                                      const size_t&) = nullptr;\n  void (*reconstruct_upper_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<dim>&, const Index<dim>&,\n                                      const Direction<dim>&, const double&,\n                                      const size_t&) = nullptr;\n};\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Fluxes.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree {\n\nnamespace detail {\nvoid fluxes_impl(\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_psi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_q_flux,\n\n    // Temporaries\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        lapse_times_electric_field_one_form,\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        lapse_times_magnetic_field_one_form,\n\n    // extra args\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric) {\n  for (size_t j = 0; j < 3; ++j) {\n    tilde_psi_flux->get(j) =\n        -shift.get(j) * get(tilde_psi) + get(lapse) * tilde_e.get(j);\n\n    tilde_phi_flux->get(j) =\n        -shift.get(j) * get(tilde_phi) + get(lapse) * tilde_b.get(j);\n\n    tilde_q_flux->get(j) = tilde_j.get(j) - shift.get(j) * get(tilde_q);\n\n    for (size_t i = 0; i < 3; ++i) {\n      tilde_e_flux->get(j, i) =\n          -shift.get(j) * tilde_e.get(i) +\n          get(lapse) * inv_spatial_metric.get(j, i) * get(tilde_psi);\n\n      tilde_b_flux->get(j, i) =\n          -shift.get(j) * tilde_b.get(i) +\n          get(lapse) * inv_spatial_metric.get(j, i) * get(tilde_phi);\n    }\n  }\n\n  for (LeviCivitaIterator<3> it; it; ++it) {\n    const auto& i = it[0];\n    const auto& j = it[1];\n    const auto& k = it[2];\n\n    tilde_e_flux->get(j, i) +=\n        -it.sign() * lapse_times_magnetic_field_one_form.get(k);\n    tilde_b_flux->get(j, i) +=\n        it.sign() * lapse_times_electric_field_one_form.get(k);\n  }\n}\n}  // namespace detail\n\nvoid Fluxes::apply(\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_psi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_q_flux,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric) {\n  Variables<tmpl::list<::Tags::Tempi<0, 3>, ::Tags::Tempi<1, 3>,\n                       ::Tags::TempScalar<0>>>\n      buffer{get(lapse).size()};\n\n  auto& lapse_times_electric_field_one_form = get<::Tags::Tempi<0, 3>>(buffer);\n  auto& lapse_times_magnetic_field_one_form = get<::Tags::Tempi<1, 3>>(buffer);\n  raise_or_lower_index(make_not_null(&lapse_times_electric_field_one_form),\n                       tilde_e, spatial_metric);\n  raise_or_lower_index(make_not_null(&lapse_times_magnetic_field_one_form),\n                       tilde_b, spatial_metric);\n\n  auto& lapse_over_sqrt_det_spatial_metric = get<::Tags::TempScalar<0>>(buffer);\n  get(lapse_over_sqrt_det_spatial_metric) =\n      get(lapse) / get(sqrt_det_spatial_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    lapse_times_electric_field_one_form.get(i) *=\n        get(lapse_over_sqrt_det_spatial_metric);\n    lapse_times_magnetic_field_one_form.get(i) *=\n        get(lapse_over_sqrt_det_spatial_metric);\n  }\n\n  detail::fluxes_impl(\n      tilde_e_flux, tilde_b_flux, tilde_psi_flux, tilde_phi_flux, tilde_q_flux,\n      // temporaries\n      lapse_times_electric_field_one_form, lapse_times_magnetic_field_one_form,\n      // extra args\n      tilde_e, tilde_b, tilde_psi, tilde_phi, tilde_q, tilde_j, lapse, shift,\n      inv_spatial_metric);\n}\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Fluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree {\n\nnamespace detail {\nvoid fluxes_impl(\n    gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_psi_flux,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_q_flux,\n\n    // Temporaries\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        lapse_times_electric_field_one_form,\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        lapse_times_magnetic_field_one_form,\n\n    // extra args\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric);\n}  // namespace detail\n\n/*!\n * \\brief Compute the fluxes of the GRFFE system with divergence cleaning.\n *\n * \\f{align*}\n *  F^j(\\tilde{E}^i) & = -\\beta^j\\tilde{E}^i + \\alpha (\\gamma^{ij}\\tilde{\\psi}\n *      - \\epsilon^{ijk}_{(3)}\\tilde{B}_k) \\\\\n *  F^j(\\tilde{B}^i) & = -\\beta^j\\tilde{B}^i + \\alpha (\\gamma^{ij}\\tilde{\\phi}\n *      + \\epsilon^{ijk}_{(3)}\\tilde{E}_k) \\\\\n *  F^j(\\tilde{\\psi}) & = -\\beta^j \\tilde{\\psi} + \\alpha \\tilde{E}^j \\\\\n *  F^j(\\tilde{\\phi}) & = -\\beta^j \\tilde{\\phi} + \\alpha \\tilde{B}^j \\\\\n *  F^j(\\tilde{q}) & = \\alpha \\sqrt{\\gamma}J^j - \\beta^j \\tilde{q}\n * \\f}\n *\n * where the conserved variables \\f$\\tilde{E}^i, \\tilde{B}^i, \\tilde{\\psi},\n * \\tilde{\\phi}, \\tilde{q}\\f$ are densitized electric field, magnetic field,\n * electric divergence cleaning field, magnetic divergence cleaning field, and\n * electric charge density. \\f$J^i\\f$ is the spatial electric current density.\n *\n * \\f$\\epsilon_{(3)}^{ijk}\\f$ is the spatial Levi-Civita tensor defined as\n *\n * \\f{align*}\n *  \\epsilon_{(3)}^{ijk} \\equiv n_\\mu \\epsilon^{\\mu ijk}\n *   = -\\frac{1}{\\sqrt{-g}} n_\\mu [\\mu ijk] = \\frac{1}{\\sqrt{\\gamma}} [ijk]\n * \\f}\n *\n * where $\\epsilon^{\\mu\\nu\\rho\\sigma}$ is the Levi-Civita tensor, \\f$g\\f$ is the\n * determinant of spacetime metric, \\f$\\gamma\\f$ is the determinant of spatial\n * metric, \\f$n^\\mu\\f$ is the normal to spatial hypersurface. Also,\n * \\f$[abcd]\\f$ and \\f$[ijk]\\f$ are the usual antisymmetric _symbols_ (which\n * only have the value \\f$\\pm 1\\f$) with 4 and 3 indices, respectively, with the\n * sign \\f$[0123] = [123] = +1\\f$. Note that\n *\n * \\f{align*}\n *  \\epsilon_{\\mu\\nu\\rho\\sigma} = \\sqrt{-g} \\, [\\mu\\nu\\rho\\sigma]\n *  , \\quad \\text{and} \\quad\n *  \\epsilon^{\\mu\\nu\\rho\\sigma} = -\\frac{1}{\\sqrt{-g}} [\\mu\\nu\\rho\\sigma]\n * \\f}\n *\n */\nstruct Fluxes {\n  using return_tags =\n      tmpl::list<::Tags::Flux<Tags::TildeE, tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<Tags::TildeB, tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<Tags::TildePsi, tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<Tags::TildePhi, tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<Tags::TildeQ, tmpl::size_t<3>, Frame::Inertial>>;\n\n  using argument_tags =\n      tmpl::list<Tags::TildeE, Tags::TildeB, Tags::TildePsi, Tags::TildePhi,\n                 Tags::TildeQ, Tags::TildeJ, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n\n  static void apply(\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_psi_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_q_flux,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n      const Scalar<DataVector>& tilde_q,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric);\n};\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Imex/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Imex.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Imex/Imex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ForceFree {\n/*!\n * \\brief Code related to IMEX time integration of ForceFree evolution system.\n */\nnamespace Imex {}\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n\ntemplate class Events::ObserveTimeStep<ForceFree::System>;\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n\ntemplate class ChangeTimeStepperOrder<ForceFree::System>;\ntemplate class CleanHistory<ForceFree::System>;\ntemplate class RecordTimeStepperData<ForceFree::System>;\ntemplate class UpdateU<ForceFree::System, false>;\ntemplate class UpdateU<ForceFree::System, true>;\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/TimeDerivativeTerms.hpp\"\n\nnamespace evolution::dg::Actions::detail {\ntemplate void volume_terms<::ForceFree::TimeDerivativeTerms>(\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::dt, typename ::ForceFree::System::variables_tag::tags_list>>*>\n        dt_vars_ptr,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::Flux, typename ::ForceFree::System::flux_variables,\n        tmpl::size_t<3>, Frame::Inertial>>*>\n        volume_fluxes,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::deriv, typename ::ForceFree::System::gradient_variables,\n        tmpl::size_t<3>, Frame::Inertial>>*>\n        partial_derivs,\n    const gsl::not_null<\n        Variables<typename ::ForceFree::System::\n                      compute_volume_time_derivative_terms::temporary_tags>*>\n        temporaries,\n    [[maybe_unused]] const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::div,\n        db::wrap_tags_in<::Tags::Flux,\n                         typename ::ForceFree::System::flux_variables,\n                         tmpl::size_t<3>, Frame::Inertial>>>*>\n        div_fluxes,\n    const Variables<typename ::ForceFree::System::variables_tag::tags_list>&\n        evolved_vars,\n    const ::dg::Formulation dg_formulation, const Mesh<3>& mesh,\n    [[maybe_unused]] const tnsr::I<DataVector, 3, Frame::Inertial>&\n        inertial_coordinates,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        logical_to_inertial_inverse_jacobian,\n    [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const std::optional<Scalar<DataVector>>& div_mesh_velocity,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j_drift,\n    const double& kappa_psi, const double& kappa_phi,\n    const double& parallel_conductivity,\n\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric);\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/MaskNeutronStarInterior.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace ForceFree {\nnamespace detail {\nCREATE_IS_CALLABLE(interior_mask)\nCREATE_IS_CALLABLE_V(interior_mask)\nCREATE_IS_CALLABLE_R_V(interior_mask)\n}  // namespace detail\n\n/*!\n * \\brief Assign the masking scalar variable (see Tags::NsInteriorMask) at the\n * initialization phase in NS magnetosphere simulations.\n *\n * Run the `interior_mask()` member function of the initial data if it is\n * callable.\n */\ntemplate <typename Metavariables, bool UsedForFdGrid>\nstruct MaskNeutronStarInterior : tt::ConformsTo<db::protocols::Mutator> {\n  using argument_tags = tmpl::list<\n      tmpl::conditional_t<\n          UsedForFdGrid,\n          evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n          domain::Tags::Coordinates<3, Frame::Inertial>>,\n      evolution::initial_data::Tags::InitialData>;\n\n  using return_tags = tmpl::list<tmpl::conditional_t<\n      UsedForFdGrid,\n      evolution::dg::subcell::Tags::Inactive<Tags::NsInteriorMask>,\n      Tags::NsInteriorMask>>;\n\n  static void apply(\n      const gsl::not_null<std::optional<Scalar<DataVector>>*>\n          neutron_star_interior_mask,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n      const evolution::initial_data::InitialData& solution_or_data) {\n    using all_data_and_solutions =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 evolution::initial_data::InitialData>;\n\n    call_with_dynamic_type<void, all_data_and_solutions>(\n        &solution_or_data, [&neutron_star_interior_mask,\n                            &inertial_coords](const auto* initial_data_ptr) {\n          using InitialData = std::decay_t<decltype(*initial_data_ptr)>;\n\n          if constexpr (detail::is_interior_mask_callable_r_v<\n                            std::optional<Scalar<DataVector>>, InitialData,\n                            tnsr::I<DataVector, 3, Frame::Inertial>>) {\n            (*neutron_star_interior_mask) =\n                (*initial_data_ptr).interior_mask(inertial_coords);\n          }\n        });\n  }\n};\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Sources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Sources.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ForceFree/ElectricCurrentDensity.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree {\n\nnamespace detail {\nvoid sources_impl(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        source_tilde_e,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        source_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_psi,\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_phi,\n\n    // temp variables\n    const tnsr::I<DataVector, 3, Frame::Inertial>&\n        trace_spatial_christoffel_second,\n\n    // EM args\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j_drift,\n    const double kappa_psi, const double kappa_phi,\n    // GR args\n    const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature) {\n  // S(\\tilde{E}^i)\n  raise_or_lower_index(source_tilde_e, d_lapse, inv_spatial_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    source_tilde_e->get(i) -=\n        get(lapse) * trace_spatial_christoffel_second.get(i);\n    source_tilde_e->get(i) *= get(tilde_psi);\n    source_tilde_e->get(i) -= tilde_j_drift.get(i);\n    for (size_t m = 0; m < 3; ++m) {\n      source_tilde_e->get(i) -= tilde_e.get(m) * d_shift.get(m, i);\n    }\n  }\n\n  // S(\\tilde{B}^i)\n  raise_or_lower_index(source_tilde_b, d_lapse, inv_spatial_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    source_tilde_b->get(i) -=\n        get(lapse) * trace_spatial_christoffel_second.get(i);\n    source_tilde_b->get(i) *= get(tilde_phi);\n    for (size_t m = 0; m < 3; ++m) {\n      source_tilde_b->get(i) -= tilde_b.get(m) * d_shift.get(m, i);\n    }\n  }\n\n  // S(\\tilde{\\psi})\n  trace(source_tilde_psi, extrinsic_curvature, inv_spatial_metric);\n  get(*source_tilde_psi) += kappa_psi;\n  get(*source_tilde_psi) *= -1.0 * get(tilde_psi);\n  get(*source_tilde_psi) += get(tilde_q);\n  get(*source_tilde_psi) *= get(lapse);\n  for (size_t m = 0; m < 3; ++m) {\n    get(*source_tilde_psi) += tilde_e.get(m) * d_lapse.get(m);\n  }\n\n  // S(\\tilde{\\phi})\n  trace(source_tilde_phi, extrinsic_curvature, inv_spatial_metric);\n  get(*source_tilde_phi) += kappa_phi;\n  get(*source_tilde_phi) *= -1.0 * get(lapse) * get(tilde_phi);\n  for (size_t m = 0; m < 3; ++m) {\n    get(*source_tilde_phi) += tilde_b.get(m) * d_lapse.get(m);\n  }\n}\n}  // namespace detail\n\nvoid Sources::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        source_tilde_e,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        source_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_psi,\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_phi,\n    // EM variables\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q, const double kappa_psi,\n    const double kappa_phi, const double parallel_conductivity,\n    // GR variables\n    const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature) {\n  // temp variable to store metric derivative quantities\n  Variables<tmpl::list<\n      ::Tags::TempI<0, 3>, gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>,\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>,\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>>\n      temp_tensors(get<0>(tilde_e).size());\n\n  // compute the drift component of tilde_J\n  auto& tilde_j_drift = get<::Tags::TempI<0, 3>>(temp_tensors);\n  ComputeDriftTildeJ::apply(make_not_null(&tilde_j_drift), tilde_q, tilde_e,\n                            tilde_b, parallel_conductivity, lapse,\n                            sqrt_det_spatial_metric, spatial_metric);\n\n  // compute the product \\gamma^jk \\Gamma^i_{jk}\n  auto& spatial_christoffel_first_kind =\n      get<gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>>(temp_tensors);\n  gr::christoffel_first_kind(make_not_null(&spatial_christoffel_first_kind),\n                             d_spatial_metric);\n  auto& spatial_christoffel_second_kind =\n      get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>>(temp_tensors);\n  raise_or_lower_first_index(make_not_null(&spatial_christoffel_second_kind),\n                             spatial_christoffel_first_kind,\n                             inv_spatial_metric);\n  auto& trace_spatial_christoffel_second =\n      get<gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>(\n          temp_tensors);\n  trace_last_indices(make_not_null(&trace_spatial_christoffel_second),\n                     spatial_christoffel_second_kind, inv_spatial_metric);\n\n  detail::sources_impl(source_tilde_e, source_tilde_b, source_tilde_psi,\n                       source_tilde_phi, trace_spatial_christoffel_second,\n                       tilde_e, tilde_b, tilde_psi, tilde_phi, tilde_q,\n                       tilde_j_drift, kappa_psi, kappa_phi, lapse, d_lapse,\n                       d_shift, inv_spatial_metric, extrinsic_curvature);\n}\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Sources.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace ForceFree {\n\nnamespace detail {\nvoid sources_impl(\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> source_tilde_e,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> source_tilde_b,\n    gsl::not_null<Scalar<DataVector>*> source_tilde_psi,\n    gsl::not_null<Scalar<DataVector>*> source_tilde_phi,\n\n    // temp variables\n    const tnsr::I<DataVector, 3, Frame::Inertial>&\n        trace_spatial_christoffel_second,\n\n    // EM args\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j_drift,\n    double kappa_psi, double kappa_phi,\n    // GR args\n    const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature);\n}  // namespace detail\n\n/*!\n * \\brief Compute the source terms for the GRFFE system with divergence\n * cleaning.\n *\n * \\f{align*}\n *  S(\\tilde{E}^i) &= -\\alpha \\sqrt{\\gamma} J^i - \\tilde{E}^j \\partial_j \\beta^i\n *    + \\tilde{\\psi} ( \\gamma^{ij} \\partial_j \\alpha - \\alpha \\gamma^{jk}\n *    \\Gamma^i_{jk} ) \\\\\n *  S(\\tilde{B}^i) &= -\\tilde{B}^j \\partial_j \\beta^i + \\tilde{\\phi} (\n *    \\gamma^{ij} \\partial_j \\alpha - \\alpha \\gamma^{jk} \\Gamma^i_{jk} ) \\\\\n *  S(\\tilde{\\psi}) &= \\tilde{E}^k \\partial_k \\alpha + \\alpha \\tilde{q} - \\alpha\n *    \\tilde{\\phi} ( K + \\kappa_\\phi ) \\\\\n *  S(\\tilde{\\phi}) &= \\tilde{B}^k \\partial_k \\alpha - \\alpha \\tilde{\\phi} (K +\n *    \\kappa_\\phi ) \\\\\n *  S(\\tilde{q}) &= 0\n * \\f}\n *\n * where the conserved variables \\f$\\tilde{E}^i, \\tilde{B}^i, \\tilde{\\psi},\n * \\tilde{\\phi}, \\tilde{q}\\f$ are densitized electric field, magnetic field,\n * magnetic divergence cleaning field, electric divergence cleaning field, and\n * electric charge density.\n *\n * \\f$J^i\\f$ is the spatial electric current density, \\f$\\alpha\\f$ is the lapse,\n * \\f$\\beta^i\\f$ is the shift, \\f$\\gamma^{ij}\\f$ is the spatial metric,\n * \\f$\\gamma\\f$ is the determinant of spatial metric, \\f$\\Gamma^i_{jk}\\f$ is the\n * spatial Christoffel symbol, \\f$K\\f$ is the trace of extrinsic curvature.\n * \\f$\\kappa_\\phi\\f$ and \\f$\\kappa_\\psi\\f$ are damping parameters associated\n * with divergence cleaning of magnetic and electric fields, respectively.\n *\n */\nstruct Sources {\n  using return_tags =\n      tmpl::list<::Tags::Source<Tags::TildeE>, ::Tags::Source<Tags::TildeB>,\n                 ::Tags::Source<Tags::TildePsi>,\n                 ::Tags::Source<Tags::TildePhi>>;\n\n  using argument_tags = tmpl::list<\n      // EM variables\n      Tags::TildeE, Tags::TildeB, Tags::TildePsi, Tags::TildePhi, Tags::TildeQ,\n      Tags::KappaPsi, Tags::KappaPhi, Tags::ParallelConductivity,\n      // GR variables\n      gr::Tags::Lapse<DataVector>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::ExtrinsicCurvature<DataVector, 3>>;\n\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> source_tilde_e,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> source_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> source_tilde_psi,\n      gsl::not_null<Scalar<DataVector>*> source_tilde_phi,\n      // EM variables\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n      const Scalar<DataVector>& tilde_q, double kappa_psi, double kappa_phi,\n      double parallel_conductivity,\n      // GR variables\n      const Scalar<DataVector>& lapse,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n      const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n      const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature);\n};\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/CMakeLists.txt",
    "content": "#\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ComputeFluxes.hpp\n  GhostData.cpp\n  NeighborPackagedData.cpp\n  SetInitialRdmpData.cpp\n  TciOnDgGrid.cpp\n  TciOnFdGrid.cpp\n  TciOptions.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GhostData.hpp\n  NeighborPackagedData.hpp\n  SetInitialRdmpData.hpp\n  Subcell.hpp\n  TciOnDgGrid.hpp\n  TciOnFdGrid.hpp\n  TciOptions.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/ComputeFluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ForceFree/Fluxes.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::subcell {\nnamespace detail {\n/*!\n * \\brief Helper function that calls `ForceFree::Fluxes::apply` by\n * retrieving the return and argument tags from the Variables object `vars`.\n */\ntemplate <typename TagsList, typename... ReturnTags, typename... ArgumentTags>\nvoid compute_fluxes_impl(const gsl::not_null<Variables<TagsList>*> vars,\n                         tmpl::list<ReturnTags...> /*meta*/,\n                         tmpl::list<ArgumentTags...> /*meta*/) {\n  ForceFree::Fluxes::apply(make_not_null(&get<ReturnTags>(*vars))...,\n                           get<ArgumentTags>(*vars)...);\n}\n}  // namespace detail\n\n/*!\n * \\brief Compute fluxes for subcell variables.\n */\ntemplate <typename TagsList>\nvoid compute_fluxes(const gsl::not_null<Variables<TagsList>*> vars) {\n  detail::compute_fluxes_impl(vars, typename ForceFree::Fluxes::return_tags{},\n                              typename ForceFree::Fluxes::argument_tags{});\n}\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Subcell/GhostData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::subcell {\nDataVector GhostVariables::apply(\n    const Variables<evolved_vars>& vars,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const size_t rdmp_size) {\n  DataVector buffer{\n      vars.number_of_grid_points() *\n          (tilde_j.size() +\n           Variables<evolved_vars>::number_of_independent_components) +\n      rdmp_size};\n\n  Variables<tmpl::append<tmpl::list<ForceFree::Tags::TildeJ>, evolved_vars>>\n      vars_to_reconstruct(buffer.data(), buffer.size() - rdmp_size);\n\n  for (size_t i = 0; i < 3; ++i) {\n    get<ForceFree::Tags::TildeJ>(vars_to_reconstruct).get(i) = tilde_j.get(i);\n  }\n\n  tmpl::for_each<evolved_vars>([&vars, &vars_to_reconstruct](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    get<tag>(vars_to_reconstruct) = get<tag>(vars);\n  });\n\n  return buffer;\n}\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/GhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename T>\nclass Variables;\nnamespace Tags {\ntemplate <typename TagsList>\nstruct Variables;\n}  // namespace Tags\n/// \\endcond\n\nnamespace ForceFree::subcell {\n/*!\n * \\brief Returns \\f$\\tilde{J}^i\\f$, \\f$\\tilde{E}^i\\f$, \\f$\\tilde{B}^i\\f$,\n * \\f$\\tilde{\\psi}\\f$, \\f$\\tilde{\\phi}\\f$ and \\f$\\tilde{q}\\f$ for FD\n * reconstruction.\n *\n * This mutator is passed to\n * `evolution::dg::subcell::Actions::SendDataForReconstruction`.\n */\nclass GhostVariables {\n private:\n  using evolved_vars =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildePsi, ForceFree::Tags::TildePhi,\n                 ForceFree::Tags::TildeQ>;\n\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::Variables<evolved_vars>, ForceFree::Tags::TildeJ>;\n\n  static DataVector apply(\n      const Variables<evolved_vars>& vars,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j, size_t rdmp_size);\n};\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Subcell/NeighborPackagedData.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.tpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/Fluxes.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::subcell {\nDirectionalIdMap<3, DataVector> NeighborPackagedData::apply(\n    const db::Access& box,\n    const std::vector<DirectionalId<3>>& mortars_to_reconstruct_to) {\n  using evolved_vars_tags = typename System::variables_tag::tags_list;\n  using fluxes_tags = typename Fluxes::return_tags;\n\n  // Subcell with moving mesh is not supported in the ForceFree system yet.\n  ASSERT(not db::get<domain::Tags::MeshVelocity<3>>(box).has_value(),\n         \"Haven't yet added support for moving mesh to DG-subcell. This \"\n         \"should be easy to generalize, but we will want to consider \"\n         \"storing the mesh velocity on the faces instead of \"\n         \"re-slicing/projecting.\");\n\n  DirectionalIdMap<3, DataVector> neighbor_package_data{};\n  if (mortars_to_reconstruct_to.empty()) {\n    return neighbor_package_data;\n  }\n\n  // Project volume variables from DG to subcell mesh\n  const Mesh<3>& dg_mesh = db::get<domain::Tags::Mesh<3>>(box);\n  const Mesh<3>& subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<3>>(box);\n  const auto volume_vars_subcell = evolution::dg::subcell::fd::project(\n      db::get<typename System::variables_tag>(box), dg_mesh,\n      subcell_mesh.extents());\n\n  // In addition to evolved vars, compute TildeJ on the subcell grid so that\n  // it could be sent to neighbor elements doing FD\n  DataVector buffer{\n      Variables<tmpl::list<Tags::TildeJ>>::number_of_independent_components *\n      dg_mesh.number_of_grid_points()};\n  const auto volume_tildej_subcell = [&box, &buffer, &dg_mesh,\n                                      &subcell_mesh]() {\n    Variables<tmpl::list<Tags::TildeJ>> var{buffer.data(), buffer.size()};\n    for (size_t i = 0; i < 3; ++i) {\n      get<Tags::TildeJ>(var).get(i) = db::get<Tags::TildeJ>(box).get(i);\n    }\n    return get<Tags::TildeJ>(evolution::dg::subcell::fd::project(\n        var, dg_mesh, subcell_mesh.extents()));\n  }();\n\n  const auto& ghost_subcell_data =\n      db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box);\n\n  const ForceFree::fd::Reconstructor& recons =\n      db::get<ForceFree::fd::Tags::Reconstructor>(box);\n\n  const auto& boundary_correction =\n      db::get<evolution::Tags::BoundaryCorrection>(box);\n  using derived_boundary_corrections =\n      ForceFree::BoundaryCorrections::standard_boundary_corrections;\n\n  const auto& subcell_options =\n      db::get<evolution::dg::subcell::Tags::SubcellOptions<3>>(box);\n\n  tmpl::for_each<derived_boundary_corrections>([&box, &boundary_correction,\n                                                &dg_mesh,\n                                                &mortars_to_reconstruct_to,\n                                                &neighbor_package_data,\n                                                &ghost_subcell_data, &recons,\n                                                &subcell_mesh, &subcell_options,\n                                                &volume_vars_subcell,\n                                                &volume_tildej_subcell](\n                                                   auto derived_correction_v) {\n    using DerivedCorrection = tmpl::type_from<decltype(derived_correction_v)>;\n\n    using dg_package_field_tags =\n        typename DerivedCorrection::dg_package_field_tags;\n    using dg_package_data_temporary_tags =\n        typename DerivedCorrection::dg_package_data_temporary_tags;\n\n    if (typeid(boundary_correction) == typeid(DerivedCorrection)) {\n      Variables<dg_package_field_tags> packaged_data{0};\n\n      // Comprehensive tags list for computing fluxes and dg_package_data\n      using dg_package_data_argument_tags = tmpl::append<\n          evolved_vars_tags, fluxes_tags,\n          tmpl::remove_duplicates<tmpl::push_back<\n              dg_package_data_temporary_tags,\n              gr::Tags::SpatialMetric<DataVector, 3>,\n              gr::Tags::SqrtDetSpatialMetric<DataVector>,\n              gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>,\n              evolution::dg::Actions::detail::NormalVector<3>>>>;\n\n      // We allocate a Variables object with extra buffer for storing TildeJ.\n      // TildeJ is not an argument of dg_package_data() function, but it needs\n      // to be reconstructed for computing fluxes on faces.\n      Variables<tmpl::append<tmpl::list<ForceFree::Tags::TildeJ>,\n                             dg_package_data_argument_tags>>\n          vars_on_face_with_tildej_buffer{0};\n\n      for (const auto& mortar_id : mortars_to_reconstruct_to) {\n        const Direction<3>& direction = mortar_id.direction();\n\n        // Switch to face-centered points\n        Index<3> extents = subcell_mesh.extents();\n        ++extents[direction.dimension()];\n        const size_t num_face_pts =\n            subcell_mesh.extents().slice_away(direction.dimension()).product();\n        vars_on_face_with_tildej_buffer.initialize(num_face_pts);\n\n        // create a `view` of the face vars excluding the TildeJ tag, since\n        // boundary correction takes the flux F^i(TildeQ) as argument but not\n        // TildeJ itself.\n        auto vars_on_face =\n            vars_on_face_with_tildej_buffer\n                .template reference_subset<dg_package_data_argument_tags>();\n        auto reconstructed_vars_on_face =\n            vars_on_face_with_tildej_buffer.template reference_subset<\n                ForceFree::fd::tags_list_for_reconstruction>();\n\n        // Copy spacetime vars over from volume.\n        using spacetime_vars_to_copy = tmpl::list<\n            gr::Tags::Lapse<DataVector>,\n            gr::Tags::Shift<DataVector, 3, Frame::Inertial>,\n            gr::Tags::SpatialMetric<DataVector, 3>,\n            gr::Tags::SqrtDetSpatialMetric<DataVector>,\n            gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>>;\n        tmpl::for_each<spacetime_vars_to_copy>(\n            [&direction, &extents, &vars_on_face,\n             &spacetime_vars_on_faces =\n                 db::get<evolution::dg::subcell::Tags::OnSubcellFaces<\n                     typename System::flux_spacetime_variables_tag, 3>>(box)](\n                auto tag_v) {\n              using tag = tmpl::type_from<decltype(tag_v)>;\n              data_on_slice(make_not_null(&get<tag>(vars_on_face)),\n                            get<tag>(gsl::at(spacetime_vars_on_faces,\n                                             direction.dimension())),\n                            extents, direction.dimension(),\n                            direction.side() == Side::Lower\n                                ? 0\n                                : extents[direction.dimension()] - 1);\n            });\n\n        // Perform FD reconstruction of variables on cell interfaces. Note\n        // that we are using the vars object with TildeJ buffer so that\n        // TildeJ can be reconstructed in this step.\n        call_with_dynamic_type<\n            void, typename ForceFree::fd::Reconstructor::creatable_classes>(\n            &recons, [&element = db::get<domain::Tags::Element<3>>(box),\n                      &mortar_id, &ghost_subcell_data, &subcell_mesh,\n                      &reconstructed_vars_on_face, &volume_vars_subcell,\n                      &volume_tildej_subcell](const auto& reconstructor) {\n              reconstructor->reconstruct_fd_neighbor(\n                  make_not_null(&reconstructed_vars_on_face),\n                  volume_vars_subcell, volume_tildej_subcell, element,\n                  ghost_subcell_data, subcell_mesh, mortar_id.direction());\n            });\n        ForceFree::subcell::compute_fluxes(\n            make_not_null(&vars_on_face_with_tildej_buffer));\n\n        // Note: since the spacetime isn't dynamical we don't need to\n        // worry about different normal vectors on the different sides\n        // of the element. If the spacetime is dynamical, then the normal\n        // covector needs to be sent, or at least the inverse spatial metric\n        // from the neighbor so that the normal covector can be computed\n        // correctly.\n        tnsr::i<DataVector, 3, Frame::Inertial> normal_covector =\n            get<evolution::dg::Tags::NormalCovector<3>>(\n                *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<3>>(\n                     box)\n                     .at(mortar_id.direction()));\n        for (auto& t : normal_covector) {\n          t *= -1.0;\n        }\n\n        // Note: Only need to do the projection in 2d and 3d, but GRFFE is\n        // always 3d currently.\n        const auto dg_normal_covector = normal_covector;\n        for (size_t i = 0; i < 3; ++i) {\n          normal_covector.get(i) = evolution::dg::subcell::fd::project(\n              dg_normal_covector.get(i),\n              dg_mesh.slice_away(mortar_id.direction().dimension()),\n              subcell_mesh.extents().slice_away(\n                  mortar_id.direction().dimension()));\n        }\n\n        // Compute the packaged data\n        packaged_data.initialize(num_face_pts);\n        using dg_package_data_projected_tags =\n            tmpl::append<evolved_vars_tags, fluxes_tags,\n                         dg_package_data_temporary_tags>;\n        evolution::dg::Actions::detail::dg_package_data<System>(\n            make_not_null(&packaged_data),\n            dynamic_cast<const DerivedCorrection&>(boundary_correction),\n            vars_on_face, normal_covector, {std::nullopt}, box,\n            typename DerivedCorrection::dg_package_data_volume_tags{},\n            dg_package_data_projected_tags{});\n\n        // Reconstruct the DG solution. Really we should be solving the\n        // boundary correction and then reconstructing, but away from a shock\n        // this doesn't matter.\n        auto dg_packaged_data = evolution::dg::subcell::fd::reconstruct(\n            packaged_data,\n            dg_mesh.slice_away(mortar_id.direction().dimension()),\n            subcell_mesh.extents().slice_away(\n                mortar_id.direction().dimension()),\n            subcell_options.reconstruction_method());\n\n        // Make a view so we can use iterators with std::copy\n        DataVector dg_packaged_data_view{dg_packaged_data.data(),\n                                         dg_packaged_data.size()};\n        neighbor_package_data[mortar_id] = DataVector{dg_packaged_data.size()};\n        std::copy(dg_packaged_data_view.begin(), dg_packaged_data_view.end(),\n                  neighbor_package_data[mortar_id].begin());\n      }\n    }\n  });\n\n  return neighbor_package_data;\n}\n}  // namespace ForceFree::subcell\n\ntemplate void evolution::dg::subcell::neighbor_reconstructed_face_solution<\n    3, ForceFree::subcell::NeighborPackagedData>(\n    gsl::not_null<db::Access*> box);\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/NeighborPackagedData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n/// \\cond\nnamespace db {\nclass Access;\n}  // namespace db\nclass DataVector;\ntemplate <size_t VolumeDim>\nstruct DirectionalId;\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\n/// \\endcond\n\nnamespace ForceFree::subcell {\n\n/*!\n * \\brief On elements using DG, reconstructs the interface data from a\n * neighboring element doing subcell.\n *\n * The neighbor's packaged data needed by the boundary correction is computed\n * and returned so that it can be used for solving the Riemann problem on the\n * interfaces.\n *\n * Note that for strict conservation the Riemann solve should be done on the\n * subcells, with the correction being projected back to the DG interface.\n * However, in practice such strict conservation doesn't seem to be necessary\n * and can be explained by that we only need strict conservation at shocks, and\n * if one element is doing DG, then we aren't at a shock.\n */\nstruct NeighborPackagedData {\n  static DirectionalIdMap<3, DataVector> apply(\n      const db::Access& box,\n      const std::vector<DirectionalId<3>>& mortars_to_reconstruct_to);\n};\n\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Subcell/SetInitialRdmpData.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace ForceFree::subcell {\n\nvoid SetInitialRdmpData::apply(\n    const gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const evolution::dg::subcell::ActiveGrid active_grid,\n    const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Subcell) {\n    const Scalar<DataVector> tilde_e_magnitude = magnitude(tilde_e);\n    const Scalar<DataVector> tilde_b_magnitude = magnitude(tilde_b);\n\n    rdmp_tci_data->max_variables_values =\n        DataVector{max(get(tilde_e_magnitude)), max(get(tilde_b_magnitude))};\n    rdmp_tci_data->min_variables_values =\n        DataVector{min(get(tilde_e_magnitude)), min(get(tilde_b_magnitude))};\n  } else {\n    using std::max;\n    using std::min;\n    const Scalar<DataVector> tilde_e_magnitude = magnitude(tilde_e);\n    const Scalar<DataVector> tilde_b_magnitude = magnitude(tilde_b);\n    const auto subcell_tilde_e_mag = evolution::dg::subcell::fd::project(\n        get(tilde_e_magnitude), dg_mesh, subcell_mesh.extents());\n    const auto subcell_tilde_b_mag = evolution::dg::subcell::fd::project(\n        get(tilde_b_magnitude), dg_mesh, subcell_mesh.extents());\n\n    rdmp_tci_data->max_variables_values =\n        DataVector{max(max(get(tilde_e_magnitude)), max(subcell_tilde_e_mag)),\n                   max(max(get(tilde_b_magnitude)), max(subcell_tilde_b_mag))};\n    rdmp_tci_data->min_variables_values =\n        DataVector{min(min(get(tilde_e_magnitude)), min(subcell_tilde_e_mag)),\n                   min(min(get(tilde_b_magnitude)), min(subcell_tilde_b_mag))};\n  }\n}\n\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/SetInitialRdmpData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace ForceFree::subcell {\n/*!\n * \\brief Sets the initial RDMP data.\n *\n * Used on the subcells after the TCI marked the DG solution as inadmissible.\n */\nstruct SetInitialRdmpData {\n  using argument_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 evolution::dg::subcell::Tags::ActiveGrid,\n                 ::domain::Tags::Mesh<3>,\n                 evolution::dg::subcell::Tags::Mesh<3>>;\n  using return_tags = tmpl::list<evolution::dg::subcell::Tags::DataForRdmpTci>;\n\n  static void apply(\n      gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_b,\n      evolution::dg::subcell::ActiveGrid active_grid, const Mesh<3>& dg_mesh,\n      const Mesh<3>& subcell_mesh);\n};\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/Subcell.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ForceFree {\n/// \\brief Code required by the DG-FD hybrid solver.\nnamespace subcell {}\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Subcell/TciOnDgGrid.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/TciOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree::subcell {\n\nstd::tuple<int, evolution::dg::subcell::RdmpTciData> TciOnDgGrid::apply(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_q, const Mesh<3>& dg_mesh,\n    const Mesh<3>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const TciOptions& tci_options,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const double persson_exponent, bool /*element_stays_on_dg*/) {\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{};\n\n  const size_t num_dg_pts = dg_mesh.number_of_grid_points();\n  const size_t num_subcell_pts = subcell_mesh.number_of_grid_points();\n\n  DataVector temp_buffer{2 * num_dg_pts + 2 * num_subcell_pts};\n  size_t offset_into_temp_buffer = 0;\n  const auto assign_data =\n      [&temp_buffer, &offset_into_temp_buffer](\n          const gsl::not_null<Scalar<DataVector>*> to_assign,\n          const size_t size) {\n        ASSERT(offset_into_temp_buffer + size <= temp_buffer.size(),\n               \"Trying to assign data out of allocated memory size\");\n        get(*to_assign)\n            .set_data_ref(temp_buffer.data() + offset_into_temp_buffer, size);\n        offset_into_temp_buffer += size;\n      };\n\n  // Note : for RDMP TCI data, we compute mag(E), mag(B) on DG grid and then\n  // project their magnitude to FD grid, NOT projecting E^i and B^i on FD grid\n  // then compute magnitudes of them.\n\n  Scalar<DataVector> dg_mag_tilde_e{};\n  Scalar<DataVector> subcell_mag_tilde_e{};\n  assign_data(make_not_null(&dg_mag_tilde_e), num_dg_pts);\n  magnitude(make_not_null(&dg_mag_tilde_e), tilde_e);\n  assign_data(make_not_null(&subcell_mag_tilde_e), num_subcell_pts);\n  evolution::dg::subcell::fd::project(make_not_null(&get(subcell_mag_tilde_e)),\n                                      get(dg_mag_tilde_e), dg_mesh,\n                                      subcell_mesh.extents());\n\n  Scalar<DataVector> dg_mag_tilde_b{};\n  Scalar<DataVector> subcell_mag_tilde_b{};\n  assign_data(make_not_null(&dg_mag_tilde_b), num_dg_pts);\n  magnitude(make_not_null(&dg_mag_tilde_b), tilde_b);\n  assign_data(make_not_null(&subcell_mag_tilde_b), num_subcell_pts);\n  evolution::dg::subcell::fd::project(make_not_null(&get(subcell_mag_tilde_b)),\n                                      get(dg_mag_tilde_b), dg_mesh,\n                                      subcell_mesh.extents());\n\n  using std::max;\n  using std::min;\n  rdmp_tci_data.max_variables_values =\n      DataVector{max(max(get(subcell_mag_tilde_e)), max(get(dg_mag_tilde_e))),\n                 max(max(get(subcell_mag_tilde_b)), max(get(dg_mag_tilde_b)))};\n  rdmp_tci_data.min_variables_values =\n      DataVector{min(min(get(subcell_mag_tilde_e)), min(get(dg_mag_tilde_e))),\n                 min(min(get(subcell_mag_tilde_b)), min(get(dg_mag_tilde_b)))};\n\n  if (evolution::dg::subcell::persson_tci(\n          dg_mag_tilde_e, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    return {-1, std::move(rdmp_tci_data)};\n  }\n\n  if (evolution::dg::subcell::persson_tci(\n          dg_mag_tilde_b, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    return {-2, std::move(rdmp_tci_data)};\n  }\n\n  if (tci_options.tilde_q_cutoff.has_value() and\n      max(abs(get(tilde_q))) > tci_options.tilde_q_cutoff.value() and\n      evolution::dg::subcell::persson_tci(\n          tilde_q, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    return {-3, std::move(rdmp_tci_data)};\n  }\n\n  if (const int rdmp_tci_status = evolution::dg::subcell::rdmp_tci(\n          rdmp_tci_data.max_variables_values,\n          rdmp_tci_data.min_variables_values,\n          past_rdmp_tci_data.max_variables_values,\n          past_rdmp_tci_data.min_variables_values,\n          subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon())) {\n    return {-(3 + rdmp_tci_status), std::move(rdmp_tci_data)};\n  }\n\n  return {0, std::move(rdmp_tci_data)};\n}\n\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/TciOnDgGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree::subcell {\n/*!\n * \\brief The troubled-cell indicator run on the DG grid to check if the\n * solution is admissible.\n *\n * The following checks are done in the order they are listed:\n *\n * <table>\n * <caption>List of checks</caption>\n * <tr><th> Description <th> TCI status\n *\n * <tr><td> apply the Persson TCI to magnitude of `TildeE`\n * <td> `-1`\n *\n * <tr><td> apply the Persson TCI to magnitude of `TildeB`\n * <td> `-2`\n *\n * <tr><td> apply the Persson TCI to `TildeQ` if \\f$\\max(|\\tilde{q}|)\\f$ is\n * greater than `tci_options.tilde_q_cutoff`.\n * Check is skipped if `tci_options.tilde_q_cutoff == DoNotCheckTildeQ`.\n * <td> `-3`\n *\n * <tr><td> apply the RDMP TCI to magnitude of `TildeE`\n * <td> `-4`\n *\n * <tr><td> apply the RDMP TCI to magnitude of `TildeB`\n * <td> `-5`\n *\n * </table>\n *\n * The second column of the table above denotes the value of an integer stored\n * as the first element of the returned `std::tuple`, which indicates the\n * particular kind of check that failed. For example, if the 3rd check\n * (Persson TCI to TildeQ) fails and cell is marked as troubled, an integer with\n * value `-3` is stored in the first slot of the returned tuple. Note that this\n * integer is marking only the first check to fail since checks are done in a\n * particular sequence as listed above. If all checks are passed and cell is not\n * troubled, it is returned with the value `0`.\n *\n * When computing magnitudes of tensor quantities (TildeE, TildeB) here for TCI\n * checks, we simply use the square root of the sum of spatial components\n * squared, _not_ the square root of the scalar product using the spatial\n * metric.\n *\n * \\note We adopt negative integers to mark TCI status from DG grid returned by\n * TciOnDgGrid class. Positive integers are used for TCIs on FD grid; see\n * TciOnFdGrid and its documentation.\n *\n */\nclass TciOnDgGrid {\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildeQ, domain::Tags::Mesh<3>,\n                 evolution::dg::subcell::Tags::Mesh<3>,\n                 evolution::dg::subcell::Tags::DataForRdmpTci, Tags::TciOptions,\n                 evolution::dg::subcell::Tags::SubcellOptions<3>>;\n\n  static std::tuple<int, evolution::dg::subcell::RdmpTciData> apply(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_q, const Mesh<3>& dg_mesh,\n      const Mesh<3>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const TciOptions& tci_options,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      double persson_exponent, bool /*element_stays_on_dg*/);\n};\n\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Subcell/TciOnFdGrid.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace ForceFree::subcell {\n\nstd::tuple<int, evolution::dg::subcell::RdmpTciData> TciOnFdGrid::apply(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_b,\n    const Scalar<DataVector>& subcell_tilde_q, const Mesh<3>& dg_mesh,\n    const Mesh<3>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const TciOptions& tci_options,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const double persson_exponent, const bool need_rdmp_data_only) {\n  const size_t num_dg_pts = dg_mesh.number_of_grid_points();\n  const size_t num_subcell_pts = subcell_mesh.number_of_grid_points();\n\n  DataVector temp_buffer{3 * num_dg_pts + 2 * num_subcell_pts};\n  size_t offset_into_temp_buffer = 0;\n  const auto assign_data =\n      [&temp_buffer, &offset_into_temp_buffer](\n          const gsl::not_null<Scalar<DataVector>*> to_assign,\n          const size_t size) {\n        ASSERT(offset_into_temp_buffer + size <= temp_buffer.size(),\n               \"Trying to assign data out of allocated memory size\");\n        get(*to_assign)\n            .set_data_ref(temp_buffer.data() + offset_into_temp_buffer, size);\n        offset_into_temp_buffer += size;\n      };\n\n  Scalar<DataVector> subcell_mag_tilde_e{};\n  assign_data(make_not_null(&subcell_mag_tilde_e), num_subcell_pts);\n  magnitude(make_not_null(&subcell_mag_tilde_e), subcell_tilde_e);\n\n  Scalar<DataVector> subcell_mag_tilde_b{};\n  assign_data(make_not_null(&subcell_mag_tilde_b), num_subcell_pts);\n  magnitude(make_not_null(&subcell_mag_tilde_b), subcell_tilde_b);\n\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{};\n  rdmp_tci_data.max_variables_values =\n      DataVector{max(get(subcell_mag_tilde_e)), max(get(subcell_mag_tilde_b))};\n  rdmp_tci_data.min_variables_values =\n      DataVector{min(get(subcell_mag_tilde_e)), min(get(subcell_mag_tilde_b))};\n\n  if (need_rdmp_data_only) {\n    return {false, rdmp_tci_data};\n  }\n\n  // Note : we compute mag(E), mag(B) on FD grid first and then project their\n  // magnitude to DG grid, NOT projecting E^i and B^i on DG grid then compute\n  // magnitudes of them.\n\n  Scalar<DataVector> dg_mag_tilde_e{};\n  assign_data(make_not_null(&dg_mag_tilde_e), num_dg_pts);\n  evolution::dg::subcell::fd::reconstruct(\n      make_not_null(&get(dg_mag_tilde_e)), get(subcell_mag_tilde_e), dg_mesh,\n      subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n\n  Scalar<DataVector> dg_mag_tilde_b{};\n  assign_data(make_not_null(&dg_mag_tilde_b), num_dg_pts);\n  evolution::dg::subcell::fd::reconstruct(\n      make_not_null(&get(dg_mag_tilde_b)), get(subcell_mag_tilde_b), dg_mesh,\n      subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n\n  Scalar<DataVector> dg_tilde_q{};\n  assign_data(make_not_null(&dg_tilde_q), num_dg_pts);\n  evolution::dg::subcell::fd::reconstruct(\n      make_not_null(&get(dg_tilde_q)), get(subcell_tilde_q), dg_mesh,\n      subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n\n  if (evolution::dg::subcell::persson_tci(\n          dg_mag_tilde_e, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    return {+1, rdmp_tci_data};\n  }\n\n  if (evolution::dg::subcell::persson_tci(\n          dg_mag_tilde_b, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    return {+2, rdmp_tci_data};\n  }\n\n  if (tci_options.tilde_q_cutoff.has_value() and\n      max(abs(get(dg_tilde_q))) > tci_options.tilde_q_cutoff.value() and\n      evolution::dg::subcell::persson_tci(\n          dg_tilde_q, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    return {+3, rdmp_tci_data};\n  }\n\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data_for_check{};\n  rdmp_tci_data_for_check.max_variables_values = DataVector{\n      max(max(get(dg_mag_tilde_e)), rdmp_tci_data.max_variables_values[0]),\n      max(max(get(dg_mag_tilde_b)), rdmp_tci_data.max_variables_values[1])};\n  rdmp_tci_data_for_check.min_variables_values = DataVector{\n      min(min(get(dg_mag_tilde_e)), rdmp_tci_data.min_variables_values[0]),\n      min(min(get(dg_mag_tilde_b)), rdmp_tci_data.min_variables_values[1])};\n\n  if (const int rdmp_tci_status = evolution::dg::subcell::rdmp_tci(\n          rdmp_tci_data_for_check.max_variables_values,\n          rdmp_tci_data_for_check.min_variables_values,\n          past_rdmp_tci_data.max_variables_values,\n          past_rdmp_tci_data.min_variables_values,\n          subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon())) {\n    return {+3 + rdmp_tci_status, rdmp_tci_data};\n  }\n\n  return {0, std::move(rdmp_tci_data)};\n}\n\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/TciOnFdGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree::subcell {\n\n/*!\n * \\brief The troubled-cell indicator run on the FD grid to check if the\n * corresponding DG solution is admissible.\n *\n * The following checks are done in the order they are listed:\n *\n * <table>\n * <caption>List of checks</caption>\n * <tr><th> Description <th> TCI status\n *\n * <tr><td> apply the Persson TCI to magnitude of `TildeE`\n * <td> `+1`\n *\n * <tr><td> apply the Persson TCI to magnitude of `TildeB`\n * <td> `+2`\n *\n * <tr><td> apply the Persson TCI to `TildeQ` if \\f$\\max(|\\tilde{q}|)\\f$ is\n * larger than `tci_options.cutoff_tilde_q`.\n * Check is skipped if `tci_options.tilde_q_cutoff == DoNotCheckTildeQ`.\n * <td> `+3`\n *\n * <tr><td> apply the RDMP TCI to magnitude of `TildeE`\n * <td> `+4`\n *\n * <tr><td> apply the RDMP TCI to magnitude of `TildeB`\n * <td> `+5`\n *\n * </table>\n *\n * The second column of the table above denotes the value of an integer stored\n * as the first element of the returned `std::tuple`, which indicates the\n * particular kind of check that failed. For example, if the second check\n * (Persson TCI to `mag(TildeB)`) fails and element is marked as troubled, an\n * integer with value `+2` is stored in the first slot of the returned tuple.\n * Note that this integer is marking only the _first_ check to fail since\n * checks are done in a particular sequence as listed above. If all checks are\n * passed and cell is not troubled, it is returned with the value `0`.\n *\n * When computing magnitudes of tensor quantities (TildeE, TildeB) for TCI\n * checks, we simply use the square root of the sum of spatial components\n * squared, _not_ the square root of the scalar product using the spatial\n * metric.\n *\n * \\note We adopt positive integers to mark TCI status from FD grid returned by\n * TciOnFdGrid class. Negative integers are reserved for TCIs on DG grid; see\n * TciOnDgGrid and its documentation.\n *\n */\nclass TciOnFdGrid {\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildeQ, domain::Tags::Mesh<3>,\n                 evolution::dg::subcell::Tags::Mesh<3>,\n                 evolution::dg::subcell::Tags::DataForRdmpTci, Tags::TciOptions,\n                 evolution::dg::subcell::Tags::SubcellOptions<3>>;\n\n  static std::tuple<int, evolution::dg::subcell::RdmpTciData> apply(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_b,\n      const Scalar<DataVector>& subcell_tilde_q, const Mesh<3>& dg_mesh,\n      const Mesh<3>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const TciOptions& tci_options,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      double persson_exponent, bool need_rdmp_data_only);\n};\n\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/TciOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ForceFree/Subcell/TciOptions.hpp\"\n\n#include <pup.h>\n\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace ForceFree::subcell {\nTciOptions::TciOptions() = default;\n\nTciOptions::TciOptions(std::optional<double> tilde_q_cutoff_in)\n    : tilde_q_cutoff(std::move(tilde_q_cutoff_in)) {}\n\nvoid TciOptions::pup(PUP::er& p) { p | tilde_q_cutoff; }\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Subcell/TciOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/OptionsGroup.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::subcell {\n\nstruct TciOptions {\n private:\n  struct DoNotCheckTildeQ {};\n\n public:\n  /*!\n   * \\brief The cutoff of the absolute value of the generalized charge density\n   * \\f$\\tilde{Q}\\f$ in an element to apply the Persson TCI.\n   *\n   * If maximum absolute value of \\f$\\tilde{Q}\\f$ is below this option value,\n   * the Persson TCI is not triggered for it.\n   */\n  struct TildeQCutoff {\n    using type = Options::Auto<double, DoNotCheckTildeQ>;\n    static constexpr Options::String help = {\n        \"If maximum absolute value of TildeQ in an element is below this value \"\n        \"we do not apply the Persson TCI to TildeQ. To disable the check, set \"\n        \"this option to 'DoNotCheckTildeQ'.\"};\n  };\n\n  using options = tmpl::list<TildeQCutoff>;\n\n  static constexpr Options::String help = {\n      \"Options for the troubled-cell indicator\"};\n\n  TciOptions();\n  explicit TciOptions(std::optional<double> tilde_q_cutoff_in);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/);\n\n  std::optional<double> tilde_q_cutoff{\n      std::numeric_limits<double>::signaling_NaN()};\n};\n\nnamespace OptionTags {\nstruct TciOptions {\n  using type = subcell::TciOptions;\n  static constexpr Options::String help = \"TCI options for ForceFree system\";\n  using group = ::dg::OptionTags::DiscontinuousGalerkinGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\nstruct TciOptions : db::SimpleTag {\n  using type = subcell::TciOptions;\n  using option_tags = tmpl::list<typename OptionTags::TciOptions>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& tci_options) {\n    return tci_options;\n  }\n};\n}  // namespace Tags\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ForceFree/Characteristics.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/TimeDerivativeTerms.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\n///\\cond\nclass DataVector;\n///\\endcond\n\n/*!\n * \\ingroup EvolutionSystemsGroup\n * \\brief Items related to evolving the GRFFE system with divergence cleaning\n *\n */\nnamespace ForceFree {\n\n/*!\n * \\brief General relativistic force-free electrodynamics (GRFFE) system with\n * divergence cleaning.\n *\n * For electromagnetism in a curved spacetime, Maxwell equations are given as\n * \\f{align*}{\n *  \\nabla_a F^{ab} &= -J^b,\\\\\n *  \\nabla_a ^* F^{ab} &= 0.\n * \\f}\n *\n * where \\f$F^{ab}\\f$ is the electromagnetic field tensor, \\f$^*F^{ab}\\f$ is its\n * dual \\f$^*F^{ab} = \\epsilon^{abcd}F_{cd} / 2\\f$, and \\f$J^a\\f$ is the\n * 4-current.\n *\n * \\note\n * - We are using the electromagnetic variables with the scaling convention\n * that the factor \\f$4\\pi\\f$ does not appear in Maxwell equations and the\n * stress-energy tensor of the EM fields (geometrized Heaviside-Lorentz units).\n * - We adopt following definition of the Levi-Civita tensor by\n * \\cite Misner1973\n * \\f{align*}{\n *  \\epsilon_{abcd} &= \\sqrt{-g} \\, [abcd] ,\\\\\n *  \\epsilon^{abcd} &= -\\frac{1}{\\sqrt{-g}} \\, [abcd] ,\n * \\f}\n * where \\f$g\\f$ is the determinant of spacetime metric, and \\f$[abcd]=\\pm 1\\f$\n * is the antisymmetric symbol with \\f$[0123]=+1\\f$.\n *\n * In SpECTRE, we evolve 'extended' (or augmented) version of Maxwell equations\n * with two divergence cleaning scalar fields \\f$\\psi\\f$ and \\f$\\phi\\f$ :\n *\n * \\f{align*}{\n *  \\nabla_a(F^{ab}+g^{ab}\\psi) & = -J^b + \\kappa_\\psi n^b \\psi \\\\\n *  \\nabla_a(^* F^{ab}+ g^{ab}\\phi) & = \\kappa_\\phi n^b \\phi\n * \\f}\n *\n * which reduce to the original Maxwell equations when \\f$\\psi=\\phi=0\\f$. For\n * damping constants $\\kappa_{\\psi, \\phi} > 0$, Gauss constraint violations are\n * damped with timescales $\\kappa_{\\psi,\\phi}^{-1}$ and propagated away.\n *\n * We decompose the EM field tensor as follows\n * \\f{align*}{\n *  F^{ab} = n^a E^b - n^b E^a - \\epsilon^{abcd}B_c n_d,\n * \\f}\n *\n * where $n^a$ is the normal to spatial hypersurface, $E^a$ and $B^a$ are\n * electric and magnetic fields.\n *\n * Evolved variables are\n *\n * \\f{align*}{\n * \\mathbf{U} = \\sqrt{\\gamma}\\left[\\,\\begin{matrix}\n *      E^i \\\\\n *      B^i \\\\\n *      \\psi \\\\\n *      \\phi \\\\\n *      q \\\\\n * \\end{matrix}\\,\\right] \\equiv \\left[\\,\\,\\begin{matrix}\n *      \\tilde{E}^i \\\\\n *      \\tilde{B}^i \\\\\n *      \\tilde{\\psi} \\\\\n *      \\tilde{\\phi} \\\\\n *      \\tilde{q} \\\\\n * \\end{matrix}\\,\\,\\right] \\f}\n *\n * where \\f$E^i\\f$ is electric field, \\f$B^i\\f$ is magnetic field, $\\psi$ is\n * electric divergence cleaning field, $\\phi$ is magnetic divergence cleaning\n * field, \\f$q\\equiv-n_aJ^a\\f$ is electric charge density, and \\f$\\gamma\\f$ is\n * the determinant of spatial metric.\n *\n * Corresponding fluxes \\f$\\mathbf{F}^j\\f$ are\n *\n * \\f{align*}{\n *  F^j(\\tilde{E}^i) & = -\\beta^j\\tilde{E}^i + \\alpha\n *      (\\gamma^{ij}\\tilde{\\psi} - \\epsilon^{ijk}_{(3)}\\tilde{B}_k) \\\\\n *  F^j(\\tilde{B}^i) & = -\\beta^j\\tilde{B}^i + \\alpha (\\gamma^{ij}\\tilde{\\phi} +\n *      \\epsilon^{ijk}_{(3)}\\tilde{E}_k) \\\\\n *  F^j(\\tilde{\\psi}) & = -\\beta^j \\tilde{\\psi} + \\alpha \\tilde{E}^j \\\\\n *  F^j(\\tilde{\\phi}) & = -\\beta^j \\tilde{\\phi} + \\alpha \\tilde{B}^j \\\\\n *  F^j(\\tilde{q}) & = \\tilde{J}^j - \\beta^j \\tilde{q}\n * \\f}\n *\n * and source terms are\n *\n * \\f{align*}{\n *  S(\\tilde{E}^i) &= -\\tilde{J}^i - \\tilde{E}^j \\partial_j \\beta^i\n *    + \\tilde{\\psi} ( \\gamma^{ij} \\partial_j \\alpha - \\alpha \\gamma^{jk}\n *      \\Gamma^i_{jk} ) \\\\\n *  S(\\tilde{B}^i) &= -\\tilde{B}^j \\partial_j \\beta^i + \\tilde{\\phi} (\n *      \\gamma^{ij} \\partial_j \\alpha - \\alpha \\gamma^{jk} \\Gamma^i_{jk} ) \\\\\n *  S(\\tilde{\\psi}) &= \\tilde{E}^k \\partial_k \\alpha + \\alpha \\tilde{q} -\n *      \\alpha \\tilde{\\phi} ( K + \\kappa_\\phi ) \\\\\n *  S(\\tilde{\\phi}) &= \\tilde{B}^k \\partial_k \\alpha - \\alpha \\tilde{\\phi}\n *      (K + \\kappa_\\phi ) \\\\\n *  S(\\tilde{q}) &= 0\n * \\f}\n *\n * where $\\tilde{J}^i \\equiv \\alpha \\sqrt{\\gamma}J^i$.\n *\n * See the documentation of Fluxes and Sources for further details.\n *\n * In addition to Maxwell equations, general relativistic force-free\n * electrodynamics (GRFFE) assumes the following which are called the force-free\n * (FF) conditions.\n *\n * \\f{align*}{\n *  F^{ab}J_b & = 0, \\\\\n *  ^*F^{ab}F_{ab} & = 0, \\\\\n *  F^{ab}F_{ab} & > 0.\n * \\f}\n *\n * In terms of electric and magnetic fields, the FF conditions above read\n *\n * \\f{align*}{\n *  E_iJ^i & = 0 , \\\\\n *  qE^i + \\epsilon_{(3)}^{ijk} J_jB_k & = 0 , \\\\\n *  B_iE^i & = 0 , \\\\\n *  B^2 - E^2 & > 0.\n * \\f}\n *\n * where \\f$B^2=B^aB_a\\f$ and \\f$E^2 = E^aE_a\\f$. Also,\n * \\f$\\epsilon_{(3)}^{ijk}\\f$ is the spatial Levi-Civita tensor defined as\n *\n * \\f{align*}\n *  \\epsilon_{(3)}^{ijk} \\equiv n_\\mu \\epsilon^{\\mu ijk}\n *   = -\\frac{1}{\\sqrt{-g}} n_\\mu [\\mu ijk] = \\frac{1}{\\sqrt{\\gamma}} [ijk]\n * \\f}\n *\n * where \\f$n^\\mu\\f$ is the normal to spatial hypersurface and \\f$[ijk]\\f$ is\n * the antisymmetric symbol with \\f$[123] = +1\\f$.\n *\n * There are a number of different ways in literature to numerically treat the\n * FF conditions. For the constraint $B_iE^i = 0$, cleaning of the parallel\n * electric field after every time step (e.g. \\cite Palenzuela2010) or adopting\n * analytically determined parallel current density \\cite Komissarov2011\n * were explored. On the magnetic dominance condition $B^2 - E^2 > 0$, there\n * have been approaches with modification of the drift current\n * \\cite Komissarov2006 or manual rescaling of the electric field\n * \\cite Palenzuela2010.\n *\n * We take the strategy that introduces special driver terms in the electric\n * current density \\f$J^i\\f$ following \\cite Alic2012 :\n *\n * \\f{align}{\n *  J^i = J^i_\\mathrm{drift} + J^i_\\mathrm{parallel}\n * \\f}\n *\n * with\n *\n * \\f{align}{\n *  J^i_\\mathrm{drift} & = q \\frac{\\epsilon^{ijk}_{(3)}E_jB_k}{B_lB^l}, \\\\\n *  J^i_\\mathrm{parallel} & = \\eta \\left[ \\frac{E_jB^j}{B_lB^l}B^i\n *          + \\frac{\\mathcal{R}(E_lE^l-B_lB^l)}{B_lB^l}E^i \\right] .\n * \\f}\n *\n * where \\f$\\eta\\f$ is the parallel conductivity and \\f$\\eta\\rightarrow\\infty\\f$\n * corresponds to the ideal force-free limit. \\f$\\mathcal{R}(x)\\f$ is the ramp\n * (or rectifier) function defined as\n *\n * \\f{align*}\n *  \\mathcal{R}(x) = \\left\\{\\begin{array}{lc}\n *          x, & \\text{if } x \\geq 0 \\\\\n *          0, & \\text{if } x < 0 \\\\\n * \\end{array}\\right\\} = \\max (x, 0) .\n * \\f}\n *\n * Internally we handle each pieces \\f$\\tilde{J}^i_\\mathrm{drift} \\equiv\n * \\alpha\\sqrt{\\gamma}J^i_\\mathrm{drift}\\f$ and \\f$\\tilde{J}^i_\\mathrm{parallel}\n * \\equiv \\alpha\\sqrt{\\gamma}J^i_\\mathrm{parallel}\\f$ as two separate Tags\n * since the latter term is stiff and needs to be evolved in conjunction with\n * implicit time steppers.\n *\n */\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = true;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = 3;\n\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition;\n\n  using variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::TildeE, Tags::TildeB, Tags::TildePsi,\n                                   Tags::TildePhi, Tags::TildeQ>>;\n\n  using flux_variables = tmpl::list<Tags::TildeE, Tags::TildeB, Tags::TildePsi,\n                                    Tags::TildePhi, Tags::TildeQ>;\n\n  using non_conservative_variables = tmpl::list<>;\n  using gradient_variables = tmpl::list<>;\n\n  using spacetime_variables_tag =\n      ::Tags::Variables<gr::tags_for_hydro<volume_dim, DataVector>>;\n\n  using flux_spacetime_variables_tag = ::Tags::Variables<\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>>>;\n\n  using compute_volume_time_derivative_terms = TimeDerivativeTerms;\n\n  using compute_largest_characteristic_speed =\n      Tags::LargestCharacteristicSpeedCompute;\n\n  using inverse_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, volume_dim>;\n};\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace ForceFree {\n\n/*!\n * \\brief Tags for the GRFFE system with divergence cleaning.\n */\nnamespace Tags {\n\n/*!\n * \\brief The electric field \\f$E^i\\f$.\n */\nstruct ElectricField : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\n\n/*!\n * \\brief The magnetic field \\f$B^i\\f$.\n */\nstruct MagneticField : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\n\n/*!\n * \\brief The electric charge density \\f$q\\equiv-n_\\mu J^\\mu\\f$ where\n * \\f$n^\\mu\\f$ is the normal to hypersurface and \\f$J^\\mu\\f$ is the 4-current.\n */\nstruct ChargeDensity : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The spatial electric current density \\f$J^i\\f$.\n */\nstruct ElectricCurrentDensity : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\n\n/*!\n * \\brief The divergence cleaning scalar field \\f$\\psi\\f$ coupled to the\n * electric field.\n */\nstruct ElectricDivergenceCleaningField : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The divergence cleaning scalar field \\f$\\phi\\f$ coupled to the\n * magnetic field.\n */\nstruct MagneticDivergenceCleaningField : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The densitized electric field \\f$\\tilde{E}^i = \\sqrt{\\gamma}E^i\\f$.\n */\nstruct TildeE : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\n\n/*!\n * \\brief The densitized magnetic field \\f$\\tilde{B}^i = \\sqrt{\\gamma}B^i\\f$.\n */\nstruct TildeB : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\n\n/*!\n * \\brief The densitized divergence cleaning field \\f$\\tilde{\\psi} =\n * \\sqrt{\\gamma}\\psi\\f$ associated with the electric field.\n */\nstruct TildePsi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The densitized divergence cleaning field \\f$\\tilde{\\phi} =\n * \\sqrt{\\gamma}\\phi\\f$ associated with the magnetic field.\n */\nstruct TildePhi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The densitized electric charge density \\f$\\tilde{q} =\n * \\sqrt{\\gamma}q\\f$.\n */\nstruct TildeQ : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The densitized electric current density \\f$\\tilde{J}^i =\n * \\alpha\\sqrt{\\gamma}J^i\\f$.\n */\nstruct TildeJ : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\n\n/*!\n * \\brief The largest characteristic speed\n */\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\n/*!\n * \\brief An optional scalar variable used for masking the interior of neutron\n * star(s) when running neutron star magnetosphere simulations.\n *\n * For elements that contain any grid points inside the NS, we assign the value\n * +1.0 to the grid points located outside the NS and assign -1.0 if located\n * inside the NS.\n *\n * For elements that do not contain any grid points inside the NS, this tag is\n * not initialized and has `null` value i.e. `has_value() == false`.\n *\n */\nstruct NsInteriorMask : db::SimpleTag {\n  using type = std::optional<Scalar<DataVector>>;\n};\n\n}  // namespace Tags\n\nnamespace OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Groups option tags related to the GRFFE evolution system.\n */\nstruct ForceFreeGroup {\n  static std::string name() { return \"ForceFree\"; }\n  static constexpr Options::String help{\n      \"Options for the GRFFE evolution system\"};\n  using group = evolution::OptionTags::SystemGroup;\n};\n\n/*!\n * \\brief Groups option tags related to the divergence cleaning of the GRFFE\n * system.\n */\nstruct ConstraintDampingGroup {\n  static std::string name() { return \"ConstraintDamping\"; }\n  static constexpr Options::String help{\n      \"Options related to constraint damping\"};\n  using group = ForceFreeGroup;\n};\n\n/*!\n * \\brief The constraint damping parameter for divergence cleaning of electric\n * fields.\n */\nstruct KappaPsi {\n  static std::string name() { return \"KappaPsi\"; }\n  using type = double;\n  static constexpr Options::String help{\n      \"Constraint damping parameter for divergence cleaning of electric \"\n      \"fields\"};\n  using group = ConstraintDampingGroup;\n};\n\n/*!\n * \\brief The constraint damping parameter for divergence cleaning of magnetic\n * fields.\n */\nstruct KappaPhi {\n  static std::string name() { return \"KappaPhi\"; }\n  using type = double;\n  static constexpr Options::String help{\n      \"Constraint damping parameter for divergence cleaning of magnetic \"\n      \"fields\"};\n  using group = ConstraintDampingGroup;\n};\n\n/*!\n * \\brief Groups option tags related to the electric current of the GRFFE\n * system.\n */\nstruct ForceFreeCurrentGroup {\n  static std::string name() { return \"ForceFreeCurrent\"; }\n  static constexpr Options::String help{\n      \"Options related to specifying the force-free electric current\"};\n  using group = ForceFreeGroup;\n};\n\n/*!\n * \\brief The damping parameter in the electric current density to impose\n * force-free conditions. Physically, this parameter is the conductivity\n * parallel to magnetic field lines.\n */\nstruct ParallelConductivity {\n  static std::string name() { return \"ParallelConductivity\"; }\n  using type = double;\n  static constexpr Options::String help{\n      \"Damping parameter for J^i to impose the force-free conditions, which is \"\n      \"physically the conductivity parallel to B field\"};\n  using group = ForceFreeCurrentGroup;\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief The constraint damping parameter \\f$\\kappa_\\psi\\f$ for divergence\n * cleaning of electric fields.\n */\nstruct KappaPsi : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::KappaPsi>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double kappa_psi) {\n    return kappa_psi;\n  }\n};\n\n/*!\n * \\brief The constraint damping parameter \\f$\\kappa_\\phi\\f$ for divergence\n * cleaning of magnetic fields.\n */\nstruct KappaPhi : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::KappaPhi>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double kappa_phi) {\n    return kappa_phi;\n  }\n};\n\n/*!\n * \\brief The damping parameter \\f$\\eta\\f$ in the electric current density to\n * impose force-free conditions. Physically, this parameter is the conductivity\n * parallel to magnetic field lines.\n */\nstruct ParallelConductivity : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::ParallelConductivity>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double parallel_conductivity) {\n    return parallel_conductivity;\n  }\n};\n\n}  // namespace Tags\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ForceFree/ElectricCurrentDensity.hpp\"\n#include \"Evolution/Systems/ForceFree/Fluxes.hpp\"\n#include \"Evolution/Systems/ForceFree/Sources.hpp\"\n#include \"Evolution/Systems/ForceFree/TimeDerivativeTerms.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree {\n\nevolution::dg::TimeDerivativeDecisions<3> TimeDerivativeTerms::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_e,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_psi,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_tilde_q*/,\n\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_psi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_q_flux,\n\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        lapse_times_electric_field_one_form,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        lapse_times_magnetic_field_one_form,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_j_drift,\n    const gsl::not_null<tnsr::ijj<DataVector, 3, Frame::Inertial>*>\n        spatial_christoffel_first_kind,\n    const gsl::not_null<tnsr::Ijj<DataVector, 3, Frame::Inertial>*>\n        spatial_christoffel_second_kind,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        trace_spatial_christoffel_second,\n\n    const gsl::not_null<Scalar<DataVector>*> temp_lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> temp_shift,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        temp_inverse_spatial_metric,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n    const double kappa_psi, const double kappa_phi,\n    const double parallel_conductivity,\n\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric) {\n  // Note that if the temp_lapse and lapse arguments point to the same object\n  // then the copy is elided internally.\n  *temp_lapse = lapse;\n  *temp_shift = shift;\n  *temp_inverse_spatial_metric = inv_spatial_metric;\n\n  // Compute fluxes\n  raise_or_lower_index(lapse_times_electric_field_one_form, tilde_e,\n                       spatial_metric);\n  raise_or_lower_index(lapse_times_magnetic_field_one_form, tilde_b,\n                       spatial_metric);\n  for (size_t d = 0; d < 3; ++d) {\n    (*lapse_times_electric_field_one_form).get(d) *=\n        get(lapse) / get(sqrt_det_spatial_metric);\n    (*lapse_times_magnetic_field_one_form).get(d) *=\n        get(lapse) / get(sqrt_det_spatial_metric);\n  }\n  detail::fluxes_impl(\n      tilde_e_flux, tilde_b_flux, tilde_psi_flux, tilde_phi_flux, tilde_q_flux,\n      *lapse_times_electric_field_one_form,\n      *lapse_times_magnetic_field_one_form, tilde_e, tilde_b, tilde_psi,\n      tilde_phi, tilde_q, tilde_j, lapse, shift, inv_spatial_metric);\n\n  // Compute source terms\n  ComputeDriftTildeJ::apply(tilde_j_drift, tilde_q, tilde_e, tilde_b,\n                            parallel_conductivity, lapse,\n                            sqrt_det_spatial_metric, spatial_metric);\n  gr::christoffel_first_kind(spatial_christoffel_first_kind, d_spatial_metric);\n  raise_or_lower_first_index(spatial_christoffel_second_kind,\n                             *spatial_christoffel_first_kind,\n                             inv_spatial_metric);\n  trace_last_indices(trace_spatial_christoffel_second,\n                     *spatial_christoffel_second_kind, inv_spatial_metric);\n\n  detail::sources_impl(non_flux_terms_dt_tilde_e, non_flux_terms_dt_tilde_b,\n                       non_flux_terms_dt_tilde_psi, non_flux_terms_dt_tilde_phi,\n                       *trace_spatial_christoffel_second, tilde_e, tilde_b,\n                       tilde_psi, tilde_phi, tilde_q, *tilde_j_drift, kappa_psi,\n                       kappa_phi, lapse, d_lapse, d_shift, inv_spatial_metric,\n                       extrinsic_curvature);\n  return {true};\n}\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/ForceFree/TimeDerivativeTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ForceFree {\n\n/*!\n * \\brief Compute the time derivative of the conserved variables for the GRFFE\n * equations with divergence cleaning.\n *\n */\nstruct TimeDerivativeTerms {\n  struct LapseTimesElectricFieldOneForm : db::SimpleTag {\n    using type = tnsr::i<DataVector, 3, Frame::Inertial>;\n  };\n  struct LapseTimesMagneticFieldOneForm : db::SimpleTag {\n    using type = tnsr::i<DataVector, 3, Frame::Inertial>;\n  };\n  struct TildeJDrift : db::SimpleTag {\n    using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n  };\n\n  using temporary_tags = tmpl::list<\n      // Flux terms\n      LapseTimesElectricFieldOneForm, LapseTimesMagneticFieldOneForm,\n\n      // Source terms\n      TildeJDrift, gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>,\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>,\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>,\n\n      // Need lapse, shift, and inverse spatial metric to be projected to\n      // the boundary for Riemann solvers.\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n\n  using argument_tags = tmpl::list<\n      // EM tags\n      Tags::TildeE, Tags::TildeB, Tags::TildePsi, Tags::TildePhi, Tags::TildeQ,\n      Tags::TildeJ, Tags::KappaPsi, Tags::KappaPhi, Tags::ParallelConductivity,\n\n      // GR-related tags\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>>;\n\n  static evolution::dg::TimeDerivativeDecisions<3> apply(\n      // Time derivatives returned by reference.\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          non_flux_terms_dt_tilde_e,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          non_flux_terms_dt_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_psi,\n      gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_phi,\n      gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_tilde_q*/,\n\n      // Fluxes returned by reference. Listed in the system struct as\n      // flux_variables.\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_psi_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_q_flux,\n\n      // temporary tags\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          lapse_times_electric_field_one_form,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          lapse_times_magnetic_field_one_form,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_j_drift,\n      gsl::not_null<tnsr::ijj<DataVector, 3, Frame::Inertial>*>\n          spatial_christoffel_first_kind,\n      gsl::not_null<tnsr::Ijj<DataVector, 3, Frame::Inertial>*>\n          spatial_christoffel_second_kind,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          trace_spatial_christoffel_second,\n\n      gsl::not_null<Scalar<DataVector>*> temp_lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> temp_shift,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          temp_inverse_spatial_metric,\n\n      // argument tags\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n      const Scalar<DataVector>& tilde_q,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j,\n      const double kappa_psi, const double kappa_phi,\n      const double parallel_conductivity,\n\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n      const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n      const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric);\n};\n\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  SetInitialData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  SetInitialData.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace gh {\n\ntemplate <size_t Dim>\nvoid initial_gh_variables_from_adm(\n    const gsl::not_null<tnsr::aa<DataVector, Dim>*> spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, Dim>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim>*> phi,\n    const tnsr::ii<DataVector, Dim>& spatial_metric,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, Dim>& shift,\n    const tnsr::ii<DataVector, Dim>& extrinsic_curvature, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian) {\n  // Assemble spacetime metric from 3+1 vars\n  gr::spacetime_metric(spacetime_metric, lapse, shift, spatial_metric);\n\n  // Compute Phi from numerical derivative of the spacetime metric so it\n  // satisfies the 3-index constraint\n  partial_derivative(phi, *spacetime_metric, mesh, inv_jacobian);\n\n  // Compute Pi by choosing dt_lapse = 0 and dt_shift = 0 (for now).\n  // The mutator `SetPiAndPhiFromConstraints` should be combined with this.\n  const auto deriv_spatial_metric =\n      tenex::evaluate<ti::i, ti::j, ti::k>((*phi)(ti::i, ti::j, ti::k));\n  const auto deriv_shift = partial_derivative(shift, mesh, inv_jacobian);\n  const auto dt_lapse = make_with_value<Scalar<DataVector>>(lapse, 0.);\n  const auto dt_shift = make_with_value<tnsr::I<DataVector, Dim>>(shift, 0.);\n  const auto dt_spatial_metric = gr::time_derivative_of_spatial_metric(\n      lapse, shift, deriv_shift, spatial_metric, deriv_spatial_metric,\n      extrinsic_curvature);\n  gh::pi(pi, lapse, dt_lapse, shift, dt_shift, spatial_metric,\n         dt_spatial_metric, *phi);\n}\n\nNumericInitialData::NumericInitialData(\n    std::string file_glob, std::string subfile_name,\n    std::variant<double, importers::ObservationSelector> observation_value,\n    std::optional<double> observation_value_epsilon,\n    const bool enable_interpolation,\n    std::variant<AdmVars, GhVars> selected_variables)\n    : importer_options_(\n          std::move(file_glob), std::move(subfile_name), observation_value,\n          observation_value_epsilon.value_or(1.0e-12), enable_interpolation),\n      selected_variables_(std::move(selected_variables)) {}\n\nNumericInitialData::NumericInitialData(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nPUP::able::PUP_ID NumericInitialData::my_PUP_ID = 0;\n\nsize_t NumericInitialData::volume_data_id() const {\n  size_t hash = 0;\n  boost::hash_combine(hash, pretty_type::get_name<NumericInitialData>());\n  boost::hash_combine(hash,\n                      get<importers::OptionTags::FileGlob>(importer_options_));\n  boost::hash_combine(hash,\n                      get<importers::OptionTags::Subgroup>(importer_options_));\n  return hash;\n}\n\nvoid NumericInitialData::pup(PUP::er& p) {\n  p | importer_options_;\n  p | selected_variables_;\n}\n\nbool operator==(const NumericInitialData& lhs, const NumericInitialData& rhs) {\n  return lhs.importer_options_ == rhs.importer_options_ and\n         lhs.selected_variables_ == rhs.selected_variables_;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void initial_gh_variables_from_adm(                                \\\n      const gsl::not_null<tnsr::aa<DataVector, DIM(data)>*> spacetime_metric, \\\n      const gsl::not_null<tnsr::aa<DataVector, DIM(data)>*> pi,               \\\n      const gsl::not_null<tnsr::iaa<DataVector, DIM(data)>*> phi,             \\\n      const tnsr::ii<DataVector, DIM(data)>& spatial_metric,                  \\\n      const Scalar<DataVector>& lapse,                                        \\\n      const tnsr::I<DataVector, DIM(data)>& shift,                            \\\n      const tnsr::ii<DataVector, DIM(data)>& extrinsic_curvature,             \\\n      const Mesh<DIM(data)>& mesh,                                            \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,     \\\n                            Frame::Inertial>& inv_jacobian);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gh {\n\n/*!\n * \\brief Compute initial GH variables from ADM variables\n *\n * - The spacetime metric is assembled from the spatial metric, lapse, and\n *   shift. See `gr::spacetime_metric` for details.\n * - Phi is set to the numerical derivative of the spacetime metric. This\n *   ensures that the 3-index constraint is initially satisfied.\n * - Pi is computed by choosing the time derivatives of lapse and shift to be\n *   zero. The `gh::gauges::SetPiAndPhiFromConstraints` mutator exists to\n *   override Pi later in the algorithm (it should be combined with this\n *   function).\n */\ntemplate <size_t Dim>\nvoid initial_gh_variables_from_adm(\n    gsl::not_null<tnsr::aa<DataVector, Dim>*> spacetime_metric,\n    gsl::not_null<tnsr::aa<DataVector, Dim>*> pi,\n    gsl::not_null<tnsr::iaa<DataVector, Dim>*> phi,\n    const tnsr::ii<DataVector, Dim>& spatial_metric,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, Dim>& shift,\n    const tnsr::ii<DataVector, Dim>& extrinsic_curvature, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& inv_jacobian);\n\n/*!\n * \\brief Numeric initial data loaded from volume data files\n *\n * This class can be factory-created in the input file to start an evolution\n * from numeric initial data. It selects the set of variables to load from\n * the volume data file (ADM or GH variables).\n */\nclass NumericInitialData : public evolution::initial_data::InitialData {\n public:\n  /// Name of a variable in the volume data file\n  template <typename Tag>\n  struct VarName {\n    using tag = Tag;\n    static std::string name() { return db::tag_name<Tag>(); }\n    using type = std::string;\n    static constexpr Options::String help =\n        \"Name of the variable in the volume data file\";\n  };\n\n  // These are the sets of variables that we support loading from volume data\n  // files:\n  // - ADM variables\n  using adm_vars =\n      tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>>;\n  struct AdmVars : tuples::tagged_tuple_from_typelist<\n                       db::wrap_tags_in<VarName, adm_vars>> {\n    static constexpr Options::String help =\n        \"ADM variables: 'Lapse', 'Shift', 'SpatialMetric' and \"\n        \"'ExtrinsicCurvature'. The initial GH variables will be computed \"\n        \"from these numeric fields, as well as their numeric spatial \"\n        \"derivatives on the computational grid. The GH variable Pi will be set \"\n        \"to satisfy the gauge constraint using the evolution gauge. The GH \"\n        \"variable Phi will be set to satisfy the 3-index constraint.\";\n    using options = tags_list;\n    using TaggedTuple::TaggedTuple;\n  };\n\n  // - Generalized harmonic variables\n  using gh_vars = tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                             Tags::Pi<DataVector, 3>, Tags::Phi<DataVector, 3>>;\n  struct GhVars\n      : tuples::tagged_tuple_from_typelist<db::wrap_tags_in<VarName, gh_vars>> {\n    static constexpr Options::String help =\n        \"GH variables: 'SpacetimeMetric', 'Pi', and 'Phi'. These variables are \"\n        \"used to set the initial data directly.\";\n    using options = tags_list;\n    using TaggedTuple::TaggedTuple;\n  };\n\n  // Collect all variables that we support loading from volume data files.\n  // Remember to `tmpl::remove_duplicates` when adding overlapping sets of\n  // vars.\n  using all_vars = tmpl::append<adm_vars, gh_vars>;\n\n  // Input-file options\n  struct Variables {\n    // The user can supply any of these choices of variables in the input\n    // file\n    using type = std::variant<AdmVars, GhVars>;\n    static constexpr Options::String help =\n        \"Set of initial data variables from which the generalized harmonic \"\n        \"system variables are computed.\";\n  };\n\n  using options =\n      tmpl::push_back<importers::ImporterOptions::tags_list, Variables>;\n\n  static constexpr Options::String help =\n      \"Numeric initial data loaded from volume data files\";\n\n  NumericInitialData() = default;\n  NumericInitialData(const NumericInitialData& rhs) = default;\n  NumericInitialData& operator=(const NumericInitialData& rhs) = default;\n  NumericInitialData(NumericInitialData&& /*rhs*/) = default;\n  NumericInitialData& operator=(NumericInitialData&& /*rhs*/) = default;\n  ~NumericInitialData() = default;\n\n  /// \\cond\n  explicit NumericInitialData(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NumericInitialData);\n  /// \\endcond\n\n  std::unique_ptr<evolution::initial_data::InitialData> get_clone()\n      const override {\n    return std::make_unique<NumericInitialData>(*this);\n  }\n\n  NumericInitialData(\n      std::string file_glob, std::string subfile_name,\n      std::variant<double, importers::ObservationSelector> observation_value,\n      std::optional<double> observation_value_epsilon,\n      bool enable_interpolation,\n      std::variant<AdmVars, GhVars> selected_variables);\n\n  const importers::ImporterOptions& importer_options() const {\n    return importer_options_;\n  }\n\n  const std::variant<AdmVars, GhVars>& selected_variables() const {\n    return selected_variables_;\n  }\n\n  /*!\n   * \\brief Unique identifier for loading this volume data\n   *\n   * Involves a hash of the type name and the volume data file names.\n   */\n  size_t volume_data_id() const;\n\n  /*!\n   * \\brief Selects which of the `fields` to import based on the choices in the\n   * input-file options\n   *\n   * The `fields` are all datasets that are available to import, represented by\n   * `importers::Tags::Selected<Tag>` tags. We select only those that we need by\n   * setting their dataset name.\n   */\n  template <typename... AllTags>\n  void select_for_import(\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> fields) const {\n    // Select the subset of the available variables that we want to read from\n    // the volume data file\n    std::visit(\n        [&fields](const auto& vars) {\n          // This lambda is invoked with the set of vars selected in the input\n          // file, which map to the tensor names that should be read from the H5\n          // file\n          using selected_vars = std::decay_t<decltype(vars)>;\n          // Get the mapped tensor name from the input file and select it in the\n          // set of all possible vars.\n          tmpl::for_each<typename selected_vars::tags_list>(\n              [&fields, &vars](const auto tag_v) {\n                using tag = typename std::decay_t<decltype(tag_v)>::type::tag;\n                get<importers::Tags::Selected<tag>>(*fields) =\n                    get<VarName<tag>>(vars);\n              });\n        },\n        selected_variables_);\n  }\n\n  /*!\n   * \\brief Set GH initial data given numeric data loaded from files\n   *\n   * The `numeric_data` contains the datasets selected above (and possibly\n   * more). We either set the GH variables directly, or compute them from the\n   * ADM variables.\n   */\n  template <typename... AllTags>\n  void set_initial_data(\n      const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n      const gsl::not_null<tnsr::aa<DataVector, 3>*> pi,\n      const gsl::not_null<tnsr::iaa<DataVector, 3>*> phi,\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> numeric_data,\n      const Mesh<3>& mesh,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian) const {\n    if (std::holds_alternative<NumericInitialData::GhVars>(\n            selected_variables_)) {\n      // We have loaded the GH system variables from the file, so just move the\n      // data for spacetime_metric and Pi into the DataBox directly, with no\n      // conversion needed. Set Phi to the spatial derivative of the spacetime\n      // metric to enforce the 3-index constraint.\n      *spacetime_metric = std::move(\n          get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*numeric_data));\n      *pi = std::move(get<Tags::Pi<DataVector, 3>>(*numeric_data));\n      *phi = get<Tags::Phi<DataVector, 3>>(*numeric_data);\n    } else if (std::holds_alternative<NumericInitialData::AdmVars>(\n                   selected_variables_)) {\n      // We have loaded ADM variables from the file. Convert to GH variables.\n      const auto& spatial_metric =\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(*numeric_data);\n      const auto& lapse = get<gr::Tags::Lapse<DataVector>>(*numeric_data);\n      const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*numeric_data);\n      const auto& extrinsic_curvature =\n          get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(*numeric_data);\n\n      initial_gh_variables_from_adm(spacetime_metric, pi, phi, spatial_metric,\n                                    lapse, shift, extrinsic_curvature, mesh,\n                                    inv_jacobian);\n    } else {\n      ERROR(\n          \"These initial data variables are not implemented yet. Please add \"\n          \"an implementation to gh::NumericInitialData.\");\n    }\n  }\n\n  void pup(PUP::er& p) override;\n\n  friend bool operator==(const NumericInitialData& lhs,\n                         const NumericInitialData& rhs);\n\n private:\n  importers::ImporterOptions importer_options_;\n  std::variant<AdmVars, GhVars> selected_variables_{};\n};\n\nnamespace Actions {\n\n/*!\n * \\brief Dispatch loading numeric initial data from files or set analytic\n * initial data.\n *\n * Place this action before\n * gh::Actions::ReceiveNumericInitialData in the action list.\n * See importers::Actions::ReadAllVolumeDataAndDistribute for details, which is\n * invoked by this action.\n * Analytic initial data is set directly by this action and terminates the\n * phase.\n */\nstruct SetInitialData {\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const parallel_component) {\n    // Dispatch to the correct `apply` overload based on type of initial data\n    using initial_data_classes =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 evolution::initial_data::InitialData>;\n    return call_with_dynamic_type<Parallel::iterable_action_return_t,\n                                  initial_data_classes>(\n        &db::get<evolution::initial_data::Tags::InitialData>(box),\n        [&box, &cache, &array_index,\n         &parallel_component](const auto* const initial_data) {\n          return apply(make_not_null(&box), *initial_data, cache, array_index,\n                       parallel_component);\n        });\n  }\n\n private:\n  // Numeric initial data\n  template <typename DbTagsList, typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,\n      const NumericInitialData& initial_data,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ParallelComponent* const /*meta*/) {\n    // If we are using GH Numeric ID, then we don't have to set Pi and Phi since\n    // we are reading them in. Also we only need to mutate this tag once so do\n    // it on the first element.\n    if (is_zeroth_element(array_index) and\n        std::holds_alternative<NumericInitialData::GhVars>(\n            initial_data.selected_variables())) {\n      Parallel::mutate<Tags::SetPiAndPhiFromConstraints,\n                       gh::gauges::SetPiAndPhiFromConstraintsCacheMutator>(\n          cache, false);\n    }\n\n    // Select the subset of the available variables that we want to read from\n    // the volume data file\n    tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n        importers::Tags::Selected, NumericInitialData::all_vars>>\n        selected_fields{};\n    initial_data.select_for_import(make_not_null(&selected_fields));\n    // Dispatch loading the variables from the volume data file\n    // - Not using `ckLocalBranch` here to make sure the simple action\n    //   invocation is asynchronous.\n    auto& reader_component = Parallel::get_parallel_component<\n        importers::ElementDataReader<Metavariables>>(cache);\n    Parallel::simple_action<importers::Actions::ReadAllVolumeDataAndDistribute<\n        3, NumericInitialData::all_vars, ParallelComponent>>(\n        reader_component, initial_data.importer_options(),\n        initial_data.volume_data_id(), std::move(selected_fields));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  // \"AnalyticData\"-type initial data\n  template <typename DbTagsList, typename InitialData, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const InitialData& initial_data,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*meta*/) {\n    static constexpr size_t Dim = Metavariables::volume_dim;\n\n    // Get ADM variables from analytic data / solution\n    const auto& x =\n        db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(*box);\n    const auto adm_vars = evolution::Initialization::initial_data(\n        initial_data, x, db::get<::Tags::Time>(*box),\n        tmpl::list<gr::Tags::SpatialMetric<DataVector, Dim>,\n                   gr::Tags::Lapse<DataVector>,\n                   gr::Tags::Shift<DataVector, Dim>,\n                   gr::Tags::ExtrinsicCurvature<DataVector, Dim>>{});\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, Dim>>(adm_vars);\n    const auto& lapse = get<gr::Tags::Lapse<DataVector>>(adm_vars);\n    const auto& shift = get<gr::Tags::Shift<DataVector, Dim>>(adm_vars);\n    const auto& extrinsic_curvature =\n        get<gr::Tags::ExtrinsicCurvature<DataVector, Dim>>(adm_vars);\n\n    // Compute GH vars from ADM vars\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(*box);\n    const auto& inv_jacobian =\n        db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                              Frame::Inertial>>(*box);\n    db::mutate<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n               Tags::Pi<DataVector, Dim>, Tags::Phi<DataVector, Dim>>(\n        &gh::initial_gh_variables_from_adm<Dim>, box, spatial_metric, lapse,\n        shift, extrinsic_curvature, mesh, inv_jacobian);\n\n    // No need to import numeric initial data, so we terminate the phase by\n    // pausing the algorithm on this element\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Receive numeric initial data loaded by gh::Actions::SetInitialData.\n *\n * Place this action in the action list after\n * gh::Actions::SetInitialData to wait until the data\n * for this element has arrived, and then transform the data to GH variables and\n * store it in the DataBox to be used as initial data.\n *\n * This action modifies the following tags in the DataBox:\n * - gr::Tags::SpacetimeMetric<DataVector, 3>\n * - gh::Tags::Pi<DataVector, 3>\n * - gh::Tags::Phi<DataVector, 3>\n */\nstruct ReceiveNumericInitialData {\n  static constexpr size_t Dim = 3;\n  using inbox_tags =\n      tmpl::list<importers::Tags::VolumeData<NumericInitialData::all_vars>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox =\n        tuples::get<importers::Tags::VolumeData<NumericInitialData::all_vars>>(\n            inboxes);\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        db::get<evolution::initial_data::Tags::InitialData>(box));\n    const auto& volume_data_id = initial_data.volume_data_id();\n    if (inbox.find(volume_data_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    auto numeric_data = std::move(inbox.extract(volume_data_id).mapped());\n\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    const auto& inv_jacobian =\n        db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                              Frame::Inertial>>(box);\n\n    db::mutate<gr::Tags::SpacetimeMetric<DataVector, 3>,\n               Tags::Pi<DataVector, 3>, Tags::Phi<DataVector, 3>>(\n        [&initial_data, &numeric_data, &mesh, &inv_jacobian](\n            const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n            const gsl::not_null<tnsr::aa<DataVector, 3>*> pi,\n            const gsl::not_null<tnsr::iaa<DataVector, 3>*> phi) {\n          initial_data.set_initial_data(spacetime_metric, pi, phi,\n                                        make_not_null(&numeric_data), mesh,\n                                        inv_jacobian);\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/ApplyTensorYlmFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/ApplyTensorYlmFilter.hpp\"\n\n#include <cstddef>\n#include <cstring>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n#include \"NumericalAlgorithms/SphericalHarmonics/ApplyTensorYlmFilter.tpp\"\n\nnamespace ylm::TensorYlm {\n\nnamespace filter_detail {\n\nvoid break_spacetime_vars_into_spatial_pieces(\n    const gsl::not_null<Variables<gh_spatial_vars_list<Frame::Inertial>>*>\n        spatial_vars,\n    const Variables<gh_spacetime_vars_list>& spacetime_vars) {\n  const auto& [metric, pi, phi] = spacetime_vars;\n  auto& [g_00, g_0i, g_ij, pi_00, pi_0i, pi_ij, phi_k00, phi_ki0, phi_kij] =\n      *spatial_vars;\n\n  get<>(g_00) = get<0, 0>(metric);\n  get<>(pi_00) = get<0, 0>(pi);\n  for (size_t i = 0; i < 3; ++i) {\n    g_0i.get(i) = metric.get(i + 1, 0);\n    pi_0i.get(i) = pi.get(i + 1, 0);\n    for (size_t j = i; j < 3; ++j) {\n      g_ij.get(i, j) = metric.get(i + 1, j + 1);\n      pi_ij.get(i, j) = pi.get(i + 1, j + 1);\n    }\n  }\n  for (size_t k = 0; k < 3; ++k) {\n    phi_k00.get(k) = phi.get(k, 0, 0);\n    for (size_t i = 0; i < 3; ++i) {\n      phi_ki0.get(k, i) = phi.get(k, i + 1, 0);\n      for (size_t j = i; j < 3; ++j) {\n        phi_kij.get(k, i, j) = phi.get(k, i + 1, j + 1);\n      }\n    }\n  }\n}\n\nvoid assemble_spacetime_vars_from_spatial_pieces(\n    const gsl::not_null<Variables<gh_spacetime_vars_list>*> spacetime_vars,\n    const Variables<gh_spatial_vars_list<Frame::Inertial>>& spatial_vars) {\n  auto& [metric, pi, phi] = *spacetime_vars;\n  const auto& [g_00, g_0i, g_ij, pi_00, pi_0i, pi_ij, phi_k00, phi_ki0,\n               phi_kij] = spatial_vars;\n\n  get<0, 0>(metric) = get<>(g_00);\n  get<0, 0>(pi) = get<>(pi_00);\n  for (size_t i = 0; i < 3; ++i) {\n    metric.get(i + 1, 0) = g_0i.get(i);\n    pi.get(i + 1, 0) = pi_0i.get(i);\n    for (size_t j = i; j < 3; ++j) {\n      metric.get(i + 1, j + 1) = g_ij.get(i, j);\n      pi.get(i + 1, j + 1) = pi_ij.get(i, j);\n    }\n  }\n  for (size_t k = 0; k < 3; ++k) {\n    phi.get(k, 0, 0) = phi_k00.get(k);\n    for (size_t i = 0; i < 3; ++i) {\n      phi.get(k, i + 1, 0) = phi_ki0.get(k, i);\n      for (size_t j = i; j < 3; ++j) {\n        phi.get(k, i + 1, j + 1) = phi_kij.get(k, i, j);\n      }\n    }\n  }\n}\n\ntemplate <typename SrcFrame, typename DestFrame>\nvoid transform_spatial_tensors_to_different_frame_without_hessians(\n    const gsl::not_null<Variables<gh_spatial_vars_list<DestFrame>>*> dest,\n    const Variables<gh_spatial_vars_list<SrcFrame>>& src,\n    const InverseJacobian<DataVector, 3, SrcFrame, DestFrame>& jac) {\n  const auto& [src_g_00, src_g_0i, src_g_ij, src_pi_00, src_pi_0i, src_pi_ij,\n               src_phi_k00, src_phi_ki0, src_phi_kij] = src;\n  auto& [dest_g_00, dest_g_0i, dest_g_ij, dest_pi_00, dest_pi_0i, dest_pi_ij,\n         dest_phi_k00, dest_phi_ki0, dest_phi_kij] = *dest;\n\n  // Just copy the scalars.\n  get<>(dest_g_00) = get<>(src_g_00);\n  get<>(dest_pi_00) = get<>(src_pi_00);\n\n  // Do phi_kij first, using other vars as temp storage.\n  for (size_t k = 0; k < 3; ++k) {\n    // First index, putting result in dest_g_ij.\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        dest_g_ij.get(i, j) = jac.get(0, k) * src_phi_kij.get(0, i, j) +\n                              jac.get(1, k) * src_phi_kij.get(1, i, j) +\n                              jac.get(2, k) * src_phi_kij.get(2, i, j);\n      }\n    }\n    // 2nd index, putting result in dest_phi_ki0.\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        dest_phi_ki0.get(i, j) = jac.get(0, i) * dest_g_ij.get(0, j) +\n                                 jac.get(1, i) * dest_g_ij.get(1, j) +\n                                 jac.get(2, i) * dest_g_ij.get(2, j);\n      }\n    }\n    // 3rd index, putting result in dest_phi_kij.\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        dest_phi_kij.get(k, i, j) = jac.get(0, j) * dest_phi_ki0.get(i, 0) +\n                                    jac.get(1, j) * dest_phi_ki0.get(i, 1) +\n                                    jac.get(2, j) * dest_phi_ki0.get(i, 2);\n      }\n    }\n  }\n\n  // Do phi_ki0, using other vars as temp storage.\n  for (size_t k = 0; k < 3; ++k) {\n    // 1st index, putting result in dest_g_0i\n    for (size_t i = 0; i < 3; ++i) {\n      dest_g_0i.get(i) = jac.get(0, k) * src_phi_ki0.get(0, i) +\n                         jac.get(1, k) * src_phi_ki0.get(1, i) +\n                         jac.get(2, k) * src_phi_ki0.get(2, i);\n    }\n    // 2nd index, putting result in dest_phi_ki0\n    for (size_t i = 0; i < 3; ++i) {\n      dest_phi_ki0.get(k, i) = jac.get(0, i) * dest_g_0i.get(0) +\n                               jac.get(1, i) * dest_g_0i.get(1) +\n                               jac.get(2, i) * dest_g_0i.get(2);\n    }\n  }\n\n  // Do g_ij and pi_ij, using other vars as temp storage.\n  for (size_t i = 0; i < 3; ++i) {\n    // 1st index, putting result in dest_g_0i and dest_pi_0i\n    for (size_t j = 0; j < 3; ++j) {\n      dest_g_0i.get(j) = jac.get(0, i) * src_g_ij.get(0, j) +\n                         jac.get(1, i) * src_g_ij.get(1, j) +\n                         jac.get(2, i) * src_g_ij.get(2, j);\n      dest_pi_0i.get(j) = jac.get(0, i) * src_pi_ij.get(0, j) +\n                          jac.get(1, i) * src_pi_ij.get(1, j) +\n                          jac.get(2, i) * src_pi_ij.get(2, j);\n    }\n    // 2nd index, putting result in g_ij and pi_ij\n    for (size_t j = i; j < 3; ++j) {\n      dest_g_ij.get(i, j) = jac.get(0, j) * dest_g_0i.get(0) +\n                            jac.get(1, j) * dest_g_0i.get(1) +\n                            jac.get(2, j) * dest_g_0i.get(2);\n      dest_pi_ij.get(i, j) = jac.get(0, j) * dest_pi_0i.get(0) +\n                             jac.get(1, j) * dest_pi_0i.get(1) +\n                             jac.get(2, j) * dest_pi_0i.get(2);\n    }\n  }\n  // Now do vectors\n  for (size_t i = 0; i < 3; ++i) {\n    dest_g_0i.get(i) = jac.get(0, i) * src_g_0i.get(0) +\n                       jac.get(1, i) * src_g_0i.get(1) +\n                       jac.get(2, i) * src_g_0i.get(2);\n    dest_pi_0i.get(i) = jac.get(0, i) * src_pi_0i.get(0) +\n                        jac.get(1, i) * src_pi_0i.get(1) +\n                        jac.get(2, i) * src_pi_0i.get(2);\n    dest_phi_k00.get(i) = jac.get(0, i) * src_phi_k00.get(0) +\n                          jac.get(1, i) * src_phi_k00.get(1) +\n                          jac.get(2, i) * src_phi_k00.get(2);\n  }\n}\n\n}  // namespace filter_detail\n\nvoid apply_tensor_ylm_filter(\n    const gsl::not_null<Variables<filter_detail::gh_spacetime_vars_list>*>\n        gh_vars,\n    const gsl::not_null<Variables<filter_detail::gh_spacetime_vars_list>*>\n        temp_storage,\n    const InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid>&\n        jac_inertial_to_grid,\n    const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>&\n        jac_grid_to_inertial,\n    const SimpleSparseMatrix& filter_matrix_scalar,\n    const SimpleSparseMatrix& filter_matrix_i,\n    const SimpleSparseMatrix& filter_matrix_ii,\n    const SimpleSparseMatrix& filter_matrix_ij,\n    const SimpleSparseMatrix& filter_matrix_kii, const size_t ell_max,\n    const size_t radial_extents) {\n  const auto& ylm = ylm::get_spherepack_cache(ell_max);\n  ASSERT(\n      radial_extents * ylm.physical_size() == gh_vars->number_of_grid_points(),\n      \"Mismatch \" << radial_extents * ylm.physical_size() << \" must equal \"\n                  << gh_vars->number_of_grid_points());\n  ASSERT(radial_extents * ylm.spectral_size() <=\n             temp_storage->number_of_grid_points(),\n         \"Mismatch \" << radial_extents * ylm.spectral_size() << \" must be <= \"\n                     << temp_storage->number_of_grid_points());\n\n  // Here we re-use the same memory multiple times.  Note that\n  // 1. gh_vars_to_filter has the same number of components as\n  //    gh_spatial_decomp_vars, even though the components are arranged\n  //    differently. So we can create a non-owning Variables of either\n  //    tag that points into the storage of a Variables with the opposite tag.\n  // 2. temp_storage has a larger size than gh_vars, because temp_storage\n  //    is sized to hold spectral coefficients (in S2) and gh_vars holds\n  //    collocation points (in S2).  This means that we can create a\n  //    non-owning Variables to hold collocation points but that points into\n  //    temp_storage (but we cannot create a non-owning Variables to hold\n  //    spectral coefficients that points into gh_vars).\n  //\n  // We define three different Variables that point into temp_storage\n  // (and we should not use any two of them simultaneously) and one\n  // Variables that points into gh_vars (which we should not use\n  // simultaneously with gh_vars).\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Grid>>\n      gh_spatial_spectral_vars(temp_storage->data(), temp_storage->size());\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Grid>> temp_spatial_vars(\n      gh_vars->data(), gh_vars->size());\n  // The following two Variables use gh_vars->size() which is smaller\n  // than temp_storage->size().\n  ASSERT(gh_vars->size() <= temp_storage->size(),\n         \"Should have \" << gh_vars->size() << \" <= \" << temp_storage->size());\n  const Variables<filter_detail::gh_spacetime_vars_list> temp_gh_vars(\n      temp_storage->data(), gh_vars->size());\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Inertial>>\n      gh_spatial_vars(temp_storage->data(), gh_vars->size());\n\n  // 1. Break up into spatial pieces.\n  // src: gh_vars\n  // dest: gh_spatial_vars\n  filter_detail::break_spacetime_vars_into_spatial_pieces(\n      make_not_null(&gh_spatial_vars), *gh_vars);\n\n  // 2. Multiply by inverse Jacobians to get into (mostly) grid frame.\n  //    It's not really the grid frame because there are no Hessian\n  //    corrections, but those don't matter for this purpose.\n  // src: gh_spatial_vars\n  // dest: temp_spatial_vars\n  filter_detail::transform_spatial_tensors_to_different_frame_without_hessians<\n      Frame::Inertial, Frame::Grid>(make_not_null(&temp_spatial_vars),\n                                    gh_spatial_vars, jac_inertial_to_grid);\n\n  // 3. Nodal to modal transformation.\n  // src: temp_spatial_vars\n  // dest: gh_spatial_spectral_vars\n  filter_detail::nodal_to_modal_ylm(make_not_null(&gh_spatial_spectral_vars),\n                                    temp_spatial_vars, ylm, radial_extents);\n\n  // 4. Filter\n  // src: gh_spatial_spectral_vars\n  // dest: gh_spatial_spectral_vars\n  // but using temp_spatial_vars as temp storage for each tensor\n  tmpl::for_each<filter_detail::gh_spatial_vars_list<Frame::Grid>>(\n      [&gh_spatial_spectral_vars, &temp_spatial_vars, radial_extents,\n       &filter_matrix_i, &filter_matrix_ii, &filter_matrix_ij,\n       &filter_matrix_kii,\n       &filter_matrix_scalar]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        // Different compilers disagree on whether radial_extents\n        // needs to be in the capture list of this lambda, and\n        // whether radial_extents is 'used' in the lambda.\n        // Adding it to the capture list and adding a cast here\n        // satisfies everyone.\n        (void)radial_extents;\n        constexpr size_t num_independent_components =\n            Tag::type::structure::size();\n        // Create destination tensor: non-owning and pointing into\n        // temp_spatial_vars.  temp_spatial_vars is larger than any\n        // *SINGLE* tensor in gh_spatial_spectral_vars, so this is ok.\n        // Note that gh_spatial_spectral_vars.number_of_grid_points()\n        // is used for the size because that is the spectral size.\n        ASSERT(gh_spatial_spectral_vars.number_of_grid_points() *\n                       num_independent_components <=\n                   temp_spatial_vars.size(),\n               \"Insufficient size: must have \"\n                   << gh_spatial_spectral_vars.number_of_grid_points() *\n                          num_independent_components\n                   << \" <= \" << temp_spatial_vars.size());\n\n        Variables<tmpl::list<Tag>> dest_tensor(\n            temp_spatial_vars.data(),\n            gh_spatial_spectral_vars.number_of_grid_points() *\n                num_independent_components);\n\n        // Delta term\n        get<Tag>(dest_tensor) = get<Tag>(gh_spatial_spectral_vars);\n        // The rest of the terms.\n\n        // Here we assume that different components in a given\n        // tensor are stored contiguously in memory, so we can grab a\n        // pointer to the first component of the tensor and pass that\n        // pointer to increment_multiply_on_right.\n        const gsl::span<double> src(\n            get<Tag>(gh_spatial_spectral_vars)[0].data(),\n            num_independent_components *\n                gh_spatial_spectral_vars.number_of_grid_points());\n        gsl::span<double> dest(\n            get<Tag>(dest_tensor)[0].data(),\n            num_independent_components * dest_tensor.number_of_grid_points());\n        // If the mesh is 3-dimensional (i.e. radial_extents>1), then\n        // we need to loop over offsets.  If not, then there's only\n        // one loop iteration.\n        const size_t stride = radial_extents;\n        for (size_t offset = 0; offset < stride; ++offset) {\n          // Each type of tensor gets a different filter matrix.\n          if constexpr (std::is_same_v<typename Tag::type::structure::symmetry,\n                                       Symmetry<1>>) {\n            filter_matrix_i.increment_multiply_on_right(\n                make_not_null(&dest), offset, stride, src, offset, stride);\n          } else if constexpr (std::is_same_v<\n                                   typename Tag::type::structure::symmetry,\n                                   Symmetry<1, 1>>) {\n            filter_matrix_ii.increment_multiply_on_right(\n                make_not_null(&dest), offset, stride, src, offset, stride);\n          } else if constexpr (std::is_same_v<\n                                   typename Tag::type::structure::symmetry,\n                                   Symmetry<2, 1>>) {\n            filter_matrix_ij.increment_multiply_on_right(\n                make_not_null(&dest), offset, stride, src, offset, stride);\n          } else if constexpr (std::is_same_v<\n                                   typename Tag::type::structure::symmetry,\n                                   Symmetry<2, 1, 1>>) {\n            filter_matrix_kii.increment_multiply_on_right(\n                make_not_null(&dest), offset, stride, src, offset, stride);\n          } else {\n            filter_matrix_scalar.increment_multiply_on_right(\n                make_not_null(&dest), offset, stride, src, offset, stride);\n          }\n        }\n        // Copy the result for this tensor back into gh_spatial_spectral_vars.\n        get<Tag>(gh_spatial_spectral_vars) = get<Tag>(dest_tensor);\n      });\n\n  // 5. Modal to nodal transformation.\n  // src: gh_spatial_spectral_vars\n  // dest: temp_spatial_vars\n  filter_detail::modal_to_nodal_ylm(make_not_null(&temp_spatial_vars),\n                                    gh_spatial_spectral_vars, ylm,\n                                    radial_extents);\n\n  // 6. Multiply by Jacobians to get back into inertial frame.\n  // src: temp_spatial_vars\n  // dest: gh_spatial_vars\n  filter_detail::transform_spatial_tensors_to_different_frame_without_hessians<\n      Frame::Grid, Frame::Inertial>(make_not_null(&gh_spatial_vars),\n                                    temp_spatial_vars, jac_grid_to_inertial);\n\n  // 7. Put back into spacetime tensors.\n  // src: gh_spatial_vars\n  // dest: gh_vars\n  filter_detail::assemble_spacetime_vars_from_spatial_pieces(gh_vars,\n                                                             gh_spatial_vars);\n}\n\n// Explicit instantiations\n\nnamespace filter_detail {\n\ntemplate void nodal_to_modal_ylm<gh_spatial_vars_list<Frame::Grid>>(\n    gsl::not_null<Variables<gh_spatial_vars_list<Frame::Grid>>*> modal,\n    const Variables<gh_spatial_vars_list<Frame::Grid>>& nodal,\n    const ::ylm::Spherepack& ylm, size_t radial_extents);\n\ntemplate void modal_to_nodal_ylm<gh_spatial_vars_list<Frame::Grid>>(\n    gsl::not_null<Variables<gh_spatial_vars_list<Frame::Grid>>*> modal,\n    const Variables<gh_spatial_vars_list<Frame::Grid>>& nodal,\n    const ::ylm::Spherepack& ylm, size_t radial_extents);\n\ntemplate void nodal_to_modal_ylm<gh_spacetime_vars_list>(\n    gsl::not_null<Variables<gh_spacetime_vars_list>*> modal,\n    const Variables<gh_spacetime_vars_list>& nodal,\n    const ::ylm::Spherepack& ylm, size_t radial_extents);\n\ntemplate void modal_to_nodal_ylm<gh_spacetime_vars_list>(\n    gsl::not_null<Variables<gh_spacetime_vars_list>*> modal,\n    const Variables<gh_spacetime_vars_list>& nodal,\n    const ::ylm::Spherepack& ylm, size_t radial_extents);\n\ntemplate void transform_spatial_tensors_to_different_frame_without_hessians<\n    Frame::Grid, Frame::Inertial>(\n    gsl::not_null<Variables<gh_spatial_vars_list<Frame::Inertial>>*> dest,\n    const Variables<gh_spatial_vars_list<Frame::Grid>>& src,\n    const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>& jac);\n\ntemplate void transform_spatial_tensors_to_different_frame_without_hessians<\n    Frame::Inertial, Frame::Grid>(\n    gsl::not_null<Variables<gh_spatial_vars_list<Frame::Grid>>*> dest,\n    const Variables<gh_spatial_vars_list<Frame::Inertial>>& src,\n    const InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid>& jac);\n}  // namespace filter_detail\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/ApplyTensorYlmFilter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/ApplyTensorYlmFilter.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\nclass Spherepack;\n}  // namespace ylm\n/// \\endcond\n\nnamespace ylm::TensorYlm {\n\n/// Defines tags and functions used internally in filtering, but\n/// tested independently in the unit tests.\nnamespace filter_detail {\n\n/// Defines tags for internal use in filtering.\nnamespace Tags {\n\n/// The time-time part of the metric.\ntemplate <typename DataType>\nstruct Metric00 : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The time-time part of the generalized harmonic variable \\f$Pi\\f$\ntemplate <typename DataType>\nstruct Pi00 : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The time-space part of the metric.\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Metrick0 : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/// The time-space part of the generalized harmonic variable \\f$Pi\\f$\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Pik0 : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/// The space-space part of the metric.\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Metrickj : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/// The space-space part of the generalized harmonic variable \\f$Pi\\f$\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Pikj : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/// \\f$\\Phi_{k00}, where \\f$\\Phi\\f$ is the generalized harmonic variable.\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Phik00 : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\n/// \\f$\\Phi_{ki0}, where \\f$\\Phi\\f$ is the generalized harmonic variable.\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Phiki0 : db::SimpleTag {\n  using type = tnsr::ij<DataType, Dim, Frame>;\n};\n\n/// \\f$\\Phi_{kij}, where \\f$\\Phi\\f$ is the generalized harmonic variable.\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Phikij : db::SimpleTag {\n  using type = tnsr::ijj<DataType, Dim, Frame>;\n};\n\n}  // namespace Tags\n\nusing gh_spacetime_vars_list =\n    tmpl::list<::gr::Tags::SpacetimeMetric<DataVector, 3, Frame::Inertial>,\n               ::gh::Tags::Pi<DataVector, 3, Frame::Inertial>,\n               ::gh::Tags::Phi<DataVector, 3, Frame::Inertial>>;\n\ntemplate <typename Frame>\nusing gh_spatial_vars_list = tmpl::list<\n    Tags::Metric00<DataVector>, Tags::Metrick0<DataVector, 3, Frame>,\n    Tags::Metrickj<DataVector, 3, Frame>, Tags::Pi00<DataVector>,\n    Tags::Pik0<DataVector, 3, Frame>, Tags::Pikj<DataVector, 3, Frame>,\n    Tags::Phik00<DataVector, 3, Frame>, Tags::Phiki0<DataVector, 3, Frame>,\n    Tags::Phikij<DataVector, 3, Frame>>;\n\n/*!\n * \\brief Copies spacetime variables into their spatial pieces.\n *\n * For example, if one of the spacetime variables is the metric\n * $g_{ab}$, then the corresponding spatial variables are a spatial\n * scalar $g_{00}$, a spatial vector $g_{i0}$, and a spatial symmetric\n * 2-tensor $g_{ij}$.\n *\n * The arguments must already be allocated to their correct sizes; no\n * memory allocation is done.\n *\n * \\param spatial_vars Points to a Variables containing spatial pieces.\n * \\param spacetime_vars A Variables containing the spacetime variables.\n */\nvoid break_spacetime_vars_into_spatial_pieces(\n    gsl::not_null<Variables<gh_spatial_vars_list<Frame::Inertial>>*>\n        spatial_vars,\n    const Variables<gh_spacetime_vars_list>& spacetime_vars);\n\n/*!\n * \\brief Copies spatial pieces into the corresponding spacetime variables.\n *\n * This is the inverse of break_spacetime_vars_into_spatial_pieces.\n *\n * \\param spatial_vars A Variables containing spatial pieces.\n * \\param spacetime_vars A Variables containing the spacetime variables.\n */\nvoid assemble_spacetime_vars_from_spatial_pieces(\n    gsl::not_null<Variables<gh_spacetime_vars_list>*> spacetime_vars,\n    const Variables<gh_spatial_vars_list<Frame::Inertial>>& spatial_vars);\n\n/*!\n * \\brief Transforms spatial tensors into a different frame, ignoring hessians.\n *\n * This is done for filtering, where having the correct (i.e. with hessians)\n * transformation doesn't matter; all that matters is that the tensor\n * indices correspond to the coordinates (or in other words, no dual frame).\n *\n * Assumes that all the variables have lower indices.\n *\n * Takes special care to re-use memory. The Variables arguments must\n * already be allocated to their correct sizes; no memory allocation\n * is done.\n *\n * \\tparam SrcFrame Source frame.\n * \\tparam DestFrame Destination frame.\n * \\param dest A Variables for the destination spatial variables.\n * \\param src A Variables containing the source spatial variables.\n * \\param jac The jacobian dx^src/dx^dest\n */\ntemplate <typename SrcFrame, typename DestFrame>\nvoid transform_spatial_tensors_to_different_frame_without_hessians(\n    gsl::not_null<Variables<gh_spatial_vars_list<DestFrame>>*> dest,\n    const Variables<gh_spatial_vars_list<SrcFrame>>& src,\n    const InverseJacobian<DataVector, 3, SrcFrame, DestFrame>& jac);\n\n}  // namespace filter_detail\n\n/*!\n * \\brief Applies TensorYlm filter in place to Generalized Harmonic variables.\n *\n * When radial_extents is 1, gh_vars and temp_storage are assumed to\n * be defined on a spherical slice, with number of grid points\n * corresponding to a spherical-harmonic grid of ell_max, and the\n * filter happens only on that slice.\n *\n * When radial_extents is > 1, gh_vars and temp_storage are assumed to\n * be defined on a spherical shell of topology I1 x S2. The filter\n * happens in the entire volume, internally iterating over each\n * spherical slice at a time.\n *\n * For performance reasons, apply_tensor_ylm_filter does not allocate\n * or deallocate memory, but it does take a temp_storage buffer.  The\n * size of temp_storage should at least\n * radial_extents*spectral_size*num_components, where num_components\n * is the total number of independent components in the GH variable\n * list (i.e. 30), and spectral_size is the size of the S2 Spherepack\n * spectral coefficient array for ell_max, as obtained from the member\n * function ylm::Spherepack::spectral_size().  Note that for S2 on\n * Spherepack, the number of collocation points is different than the\n * number of spectral coefficients, and both are different than the\n * size of the Spherepack storage array.\n *\n * \\param gh_vars Generalized Harmonic variables at collocation points.\n * \\param temp_storage Temporary storage for generalized harmonic variables,\n *   allocated outside apply_tensor_ylm_filter. See above for size requirements.\n * \\param jac_inertial_to_grid Jacobian taking V_x from inertial to grid.\n * \\param jac_grid_to_inertial Jacobian taking V_x from grid to inertial.\n * \\param filter_matrix_scalar The scalar filter matrix computed by fill_filter.\n * \\param filter_matrix_i The Rank-1 matrix computed by fill_filter.\n * \\param filter_matrix_ii The Rank-2 symmetric matrix computed by fill_filter.\n * \\param filter_matrix_ij The Rank-2 matrix computed by fill_filter.\n * \\param filter_matrix_kii The Rank-3 matrix computed by fill_filter.\n * \\param ell_max The maximum ylm ell.\n * \\param radial_extents The number of radial grid points, can be 1 for slices.\n */\nvoid apply_tensor_ylm_filter(\n    gsl::not_null<Variables<filter_detail::gh_spacetime_vars_list>*> gh_vars,\n    gsl::not_null<Variables<filter_detail::gh_spacetime_vars_list>*>\n        temp_storage,\n    const InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid>&\n        jac_inertial_to_grid,\n    const InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>&\n        jac_grid_to_inertial,\n    const SimpleSparseMatrix& filter_matrix_scalar,\n    const SimpleSparseMatrix& filter_matrix_i,\n    const SimpleSparseMatrix& filter_matrix_ii,\n    const SimpleSparseMatrix& filter_matrix_ij,\n    const SimpleSparseMatrix& filter_matrix_kii, size_t ell_max,\n    size_t radial_extents);\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CompletionCriteria.hpp\n  CompletionSingleton.hpp\n  )\n\nadd_subdirectory(Callbacks)\nadd_subdirectory(Events)\nadd_subdirectory(PhaseControl)\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/Callbacks/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  UpdateCompletionCriteria.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/Callbacks/UpdateCompletionCriteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionCriteria.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionSingleton.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::bbh::callbacks {\n/*!\n * \\brief Apparent-horizon callback that updates binary-black-hole completion\n * criteria from successful common-horizon finds.\n *\n * \\details This callback forwards each successful AhC find (`temporal_id`,\n * `LMax`) to `gh::bbh::CompletionSingleton`, where completion criteria are\n * evaluated in a temporally robust, centralized way.\n */\ntemplate <typename HorizonMetavars>\nstruct UpdateCompletionCriteria : tt::ConformsTo<ah::protocols::Callback> {\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<>;\n\n  template <typename DbTags, typename Metavariables>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const FastFlow::Status /*status*/) {\n    const auto& current_time = db::get<ah::Tags::CurrentTime>(box);\n    ASSERT(current_time.has_value(),\n           \"AhC completion callback requires a current temporal id.\");\n    const size_t l_max =\n        db::get<ylm::Tags::Strahlkorper<typename HorizonMetavars::frame>>(box)\n            .l_max();\n    Parallel::simple_action<gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        Parallel::get_parallel_component<\n            gh::bbh::CompletionSingleton<Metavariables>>(cache),\n        *current_time, l_max);\n  }\n};\n}  // namespace gh::bbh::callbacks\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionCriteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::bbh {\nnamespace OptionTags {\n/// BBH inspiral-completion controls that are read from input options.\nstruct CompletionCriteria {\n  static std::string name() { return \"BbhCompletionCriteria\"; }\n  static constexpr Options::String help =\n      \"Options controlling inspiral termination based on global BBH criteria.\";\n};\n\nstruct MinCommonHorizonSuccessesBeforeChecks {\n  using type = size_t;\n  using group = CompletionCriteria;\n  static constexpr Options::String help =\n      \"Do not evaluate binary-black-hole completion checks before this many \"\n      \"successful common-horizon finds.\";\n};\n\nstruct MaxCommonHorizonSuccesses {\n  using type = size_t;\n  using group = CompletionCriteria;\n  static constexpr Options::String help =\n      \"When successful common-horizon finds reach this count in a \"\n      \"binary-black-hole simulation, request completion.\";\n};\n\nstruct GaugeConstraintLinfThreshold {\n  using type = double;\n  using group = CompletionCriteria;\n  static constexpr Options::String help =\n      \"When the reduced Linf norm of the gauge constraint is greater than or \"\n      \"equal to this threshold in a binary-black-hole simulation, request \"\n      \"completion.\";\n};\n\nstruct ThreeIndexConstraintLinfThreshold {\n  using type = double;\n  using group = CompletionCriteria;\n  static constexpr Options::String help =\n      \"When the reduced Linf norm of the three-index constraint is greater \"\n      \"than or equal to this threshold in a binary-black-hole simulation, \"\n      \"request completion.\";\n};\n\nstruct CommonHorizonLMaxThreshold {\n  using type = size_t;\n  using group = CompletionCriteria;\n  static constexpr Options::String help =\n      \"When the common-horizon LMax is less than or equal to this value in a \"\n      \"binary-black-hole simulation, request completion (after the minimum \"\n      \"common-horizon success count is reached).\";\n};\n\nstruct ConstraintCheckVerbose {\n  using type = bool;\n  using group = CompletionCriteria;\n  static constexpr Options::String help =\n      \"Whether to print reduced binary-black-hole constraint norms at each \"\n      \"constraint-threshold check.\";\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// Minimum number of successful common-horizon finds required before\n/// completion checks are evaluated.\nstruct MinCommonHorizonSuccessesBeforeChecks : db::SimpleTag {\n  using type = size_t;\n  using option_tags =\n      tmpl::list<OptionTags::MinCommonHorizonSuccessesBeforeChecks>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type value) { return value; }\n};\n\n/// Number of successful common-horizon finds that triggers completion.\n/// Controlled by `gh::bbh::OptionTags::MaxCommonHorizonSuccesses`.\nstruct MaxCommonHorizonSuccesses : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::MaxCommonHorizonSuccesses>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type value) { return value; }\n};\n\n/// Threshold for requesting completion from the reduced gauge-constraint Linf\n/// criterion:\n/// when Linf(gauge constraint) >= this threshold in a binary-black-hole\n/// simulation, request completion.\n/// Controlled by `gh::bbh::OptionTags::GaugeConstraintLinfThreshold`.\nstruct GaugeConstraintLinfThreshold : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::GaugeConstraintLinfThreshold>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type value) { return value; }\n};\n\n/// Threshold for requesting completion from the reduced three-index-constraint\n/// Linf criterion:\n/// when Linf(three-index constraint) >= this threshold in a binary-black-hole\n/// simulation, request completion.\n/// Controlled by `gh::bbh::OptionTags::ThreeIndexConstraintLinfThreshold`.\nstruct ThreeIndexConstraintLinfThreshold : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::ThreeIndexConstraintLinfThreshold>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type value) { return value; }\n};\n\n/// Common-horizon `LMax` threshold criterion for requesting completion\n/// (after the minimum common-horizon success count is reached):\n/// when common-horizon `LMax` <= this threshold in a binary-black-hole\n/// simulation, request completion.\n/// Controlled by `gh::bbh::OptionTags::CommonHorizonLMaxThreshold`.\nstruct CommonHorizonLMaxThreshold : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::CommonHorizonLMaxThreshold>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type value) { return value; }\n};\n\n/// Verbosity control for reduced constraint checks.\n/// Controlled by `gh::bbh::OptionTags::ConstraintCheckVerbose`.\nstruct ConstraintCheckVerbose : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::ConstraintCheckVerbose>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type value) { return value; }\n};\n\n/// Latch indicating that the reduced gauge-constraint criterion was met.\nstruct GaugeConstraintExceeded : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return false; }\n};\n\n/// Latch indicating that the reduced three-index-constraint criterion was met.\nstruct ThreeIndexConstraintExceeded : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return false; }\n};\n\n/// Latch indicating that a successful common-horizon find satisfied the `LMax`\n/// criterion.\nstruct CommonHorizonLMaxBelowOrEqualThreshold : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return false; }\n};\n\n/// Count of successful common-horizon finds.\nstruct CommonHorizonSuccessCount : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return 0; }\n};\n\n/// Latch used by phase control to checkpoint-and-exit the binary-black-hole\n/// simulation when completion is requested by any criterion.\nstruct CompletionRequested : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return false; }\n};\n\n/// Element-local latch mirrored from the BBH completion singleton and used by\n/// phase control to request checkpoint-and-exit.\nstruct ElementCompletionRequested : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return false; }\n};\n}  // namespace Tags\n}  // namespace gh::bbh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionSingleton.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionCriteria.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingletonDeclarations.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/SimpleActionOnElement.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::bbh {\nnamespace Tags {\n/// Successful AhC finds keyed by temporal id, storing the corresponding `LMax`.\nstruct CommonHorizonSuccessRecords : db::SimpleTag {\n  using type = std::map<LinkedMessageId<double>, size_t>;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return {}; }\n};\n\n/// Reduced constraint maxima keyed by time.\nstruct ConstraintCheckRecords : db::SimpleTag {\n  using key_type = double;\n  using mapped_type = std::pair<double, double>;\n  using type = std::map<key_type, mapped_type>;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return {}; }\n};\n\n/// Constraint checks already reported at verbose level.\nstruct ReportedConstraintCheckRecords : db::SimpleTag {\n  using type = std::set<gh::bbh::Tags::ConstraintCheckRecords::key_type>;\n  using option_tags = tmpl::list<>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return {}; }\n};\n}  // namespace Tags\n\nnamespace Actions {\n/// Element simple action that latches completion-request state in the DataBox.\nstruct SetElementCompletionRequested {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    db::mutate<gh::bbh::Tags::ElementCompletionRequested>(\n        [](const gsl::not_null<bool*> element_completion_requested) {\n          *element_completion_requested = true;\n        },\n        make_not_null(&box));\n  }\n};\n}  // namespace Actions\n\nnamespace detail {\ntemplate <typename Metavariables>\nstruct BroadcastCompletionRequestToElements {\n  static void apply(Parallel::GlobalCache<Metavariables>& cache) {\n    using dg_array = typename Metavariables::gh_dg_element_array;\n    if constexpr (Parallel::is_dg_element_collection_v<dg_array>) {\n      Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n          gh::bbh::Actions::SetElementCompletionRequested, true>>(\n          Parallel::get_parallel_component<dg_array>(cache));\n    } else {\n      Parallel::simple_action<gh::bbh::Actions::SetElementCompletionRequested>(\n          Parallel::get_parallel_component<dg_array>(cache));\n    }\n  }\n};\n\ntemplate <typename DbTags, typename Metavariables>\nvoid recompute_completion_state(const gsl::not_null<db::DataBox<DbTags>*> box,\n                                Parallel::GlobalCache<Metavariables>& cache) {\n  const size_t min_successes =\n      Parallel::get<gh::bbh::Tags::MinCommonHorizonSuccessesBeforeChecks>(\n          cache);\n  const size_t max_successes =\n      Parallel::get<gh::bbh::Tags::MaxCommonHorizonSuccesses>(cache);\n  const size_t l_max_threshold =\n      Parallel::get<gh::bbh::Tags::CommonHorizonLMaxThreshold>(cache);\n  const double gauge_constraint_threshold =\n      Parallel::get<gh::bbh::Tags::GaugeConstraintLinfThreshold>(cache);\n  const double three_index_constraint_threshold =\n      Parallel::get<gh::bbh::Tags::ThreeIndexConstraintLinfThreshold>(cache);\n  const bool verbose =\n      Parallel::get<gh::bbh::Tags::ConstraintCheckVerbose>(cache);\n  if (max_successes < min_successes) {\n    ERROR_NO_TRACE(\"MaxCommonHorizonSuccesses (\"\n                   << max_successes << \") must be >= \"\n                   << \"MinCommonHorizonSuccessesBeforeChecks (\" << min_successes\n                   << \").\");\n  }\n\n  const auto& horizon_records =\n      db::get<gh::bbh::Tags::CommonHorizonSuccessRecords>(*box);\n  const size_t success_count = horizon_records.size();\n  bool lmax_latched = false;\n  std::optional<std::pair<double, size_t>> first_lmax_match{};\n  for (const auto& [time_id, l_max] : horizon_records) {\n    if (l_max <= l_max_threshold) {\n      lmax_latched = true;\n      first_lmax_match = std::pair{time_id.id, l_max};\n      break;\n    }\n  }\n\n  std::optional<double> first_gauge_match_time{};\n  std::optional<double> first_three_index_match_time{};\n  size_t successes_up_to_constraint_time = 0;\n  auto horizon_it = horizon_records.begin();\n  const auto& constraint_records =\n      db::get<gh::bbh::Tags::ConstraintCheckRecords>(*box);\n  const auto& reported_constraint_records =\n      db::get<gh::bbh::Tags::ReportedConstraintCheckRecords>(*box);\n  std::vector<gh::bbh::Tags::ConstraintCheckRecords::key_type>\n      newly_reported_constraint_records{};\n  for (const auto& [constraint_time, maxima] : constraint_records) {\n    while (horizon_it != horizon_records.end() and\n           horizon_it->first.id <= constraint_time) {\n      ++successes_up_to_constraint_time;\n      ++horizon_it;\n    }\n    if (successes_up_to_constraint_time < min_successes) {\n      continue;\n    }\n    if (verbose and not reported_constraint_records.contains(constraint_time)) {\n      Parallel::printf(\n          \"BBH completion constraint check at t=%.16f: \"\n          \"Linf(GaugeConstraint)=%.16e (threshold %.16e), \"\n          \"Linf(ThreeIndexConstraint)=%.16e (threshold %.16e).\\n\",\n          constraint_time, maxima.first, gauge_constraint_threshold,\n          maxima.second, three_index_constraint_threshold);\n      newly_reported_constraint_records.push_back(constraint_time);\n    }\n    if (not first_gauge_match_time.has_value() and\n        maxima.first >= gauge_constraint_threshold) {\n      first_gauge_match_time = constraint_time;\n    }\n    if (not first_three_index_match_time.has_value() and\n        maxima.second >= three_index_constraint_threshold) {\n      first_three_index_match_time = constraint_time;\n    }\n  }\n  const bool horizon_completion_requested =\n      success_count >= min_successes and\n      (success_count >= max_successes or lmax_latched);\n  const bool completion_requested = horizon_completion_requested or\n                                    first_gauge_match_time.has_value() or\n                                    first_three_index_match_time.has_value();\n\n  const bool old_gauge_exceeded =\n      db::get<gh::bbh::Tags::GaugeConstraintExceeded>(*box);\n  const bool old_three_index_exceeded =\n      db::get<gh::bbh::Tags::ThreeIndexConstraintExceeded>(*box);\n  const bool old_lmax_latched =\n      db::get<gh::bbh::Tags::CommonHorizonLMaxBelowOrEqualThreshold>(*box);\n  const size_t old_success_count =\n      db::get<gh::bbh::Tags::CommonHorizonSuccessCount>(*box);\n  const bool old_completion_requested =\n      db::get<gh::bbh::Tags::CompletionRequested>(*box);\n  db::mutate<gh::bbh::Tags::GaugeConstraintExceeded,\n             gh::bbh::Tags::ThreeIndexConstraintExceeded,\n             gh::bbh::Tags::CommonHorizonLMaxBelowOrEqualThreshold,\n             gh::bbh::Tags::CommonHorizonSuccessCount,\n             gh::bbh::Tags::CompletionRequested>(\n      [&first_gauge_match_time, &first_three_index_match_time, &lmax_latched,\n       &success_count, &completion_requested](\n          const gsl::not_null<bool*> gauge_constraint_exceeded_flag,\n          const gsl::not_null<bool*> three_index_constraint_exceeded_flag,\n          const gsl::not_null<bool*> lmax_latched_flag,\n          const gsl::not_null<size_t*> common_horizon_success_count,\n          const gsl::not_null<bool*> completion_requested_flag) {\n        *gauge_constraint_exceeded_flag = first_gauge_match_time.has_value();\n        *three_index_constraint_exceeded_flag =\n            first_three_index_match_time.has_value();\n        *lmax_latched_flag = lmax_latched;\n        *common_horizon_success_count = success_count;\n        *completion_requested_flag = completion_requested;\n      },\n      box);\n  if (not newly_reported_constraint_records.empty()) {\n    db::mutate<gh::bbh::Tags::ReportedConstraintCheckRecords>(\n        [&newly_reported_constraint_records](\n            const gsl::not_null<\n                gh::bbh::Tags::ReportedConstraintCheckRecords::type*>\n                records) {\n          for (const auto& key : newly_reported_constraint_records) {\n            records->insert(key);\n          }\n        },\n        box);\n  }\n  if (old_success_count < min_successes and success_count >= min_successes) {\n    Parallel::printf(\n        \"BBH completion criterion armed: AhC successes reached %zu (minimum \"\n        \"required: %zu).\\n\",\n        success_count, min_successes);\n  }\n  if (not old_lmax_latched and lmax_latched and first_lmax_match.has_value()) {\n    Parallel::printf(\n        \"BBH completion criterion met at t=%.16f: AhC Lmax=%zu <= %zu.\\n\",\n        first_lmax_match->first, first_lmax_match->second, l_max_threshold);\n  }\n  if (not old_gauge_exceeded and first_gauge_match_time.has_value()) {\n    Parallel::printf(\n        \"BBH completion criterion met at t=%.16f: \"\n        \"Linf(GaugeConstraint) >= %.16e.\\n\",\n        *first_gauge_match_time, gauge_constraint_threshold);\n  }\n  if (not old_three_index_exceeded and\n      first_three_index_match_time.has_value()) {\n    Parallel::printf(\n        \"BBH completion criterion met at t=%.16f: \"\n        \"Linf(ThreeIndexConstraint) >= %.16e.\\n\",\n        *first_three_index_match_time, three_index_constraint_threshold);\n  }\n  if (not old_completion_requested and completion_requested) {\n    Parallel::printf(\"BBH completion criteria request latched.\\n\");\n    BroadcastCompletionRequestToElements<Metavariables>::apply(cache);\n  }\n}\n\nstruct InitializeCompletionState : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags =\n      tmpl::list<gh::bbh::Tags::GaugeConstraintExceeded,\n                 gh::bbh::Tags::ThreeIndexConstraintExceeded,\n                 gh::bbh::Tags::CommonHorizonLMaxBelowOrEqualThreshold,\n                 gh::bbh::Tags::CommonHorizonSuccessCount,\n                 gh::bbh::Tags::CompletionRequested,\n                 gh::bbh::Tags::CommonHorizonSuccessRecords,\n                 gh::bbh::Tags::ConstraintCheckRecords,\n                 gh::bbh::Tags::ReportedConstraintCheckRecords>;\n  using argument_tags = tmpl::list<>;\n\n  static void apply(\n      const gsl::not_null<bool*> gauge_constraint_exceeded,\n      const gsl::not_null<bool*> three_index_constraint_exceeded,\n      const gsl::not_null<bool*> common_horizon_lmax_below_or_equal_threshold,\n      const gsl::not_null<size_t*> common_horizon_success_count,\n      const gsl::not_null<bool*> completion_requested,\n      const gsl::not_null<gh::bbh::Tags::CommonHorizonSuccessRecords::type*>\n          common_horizon_success_records,\n      const gsl::not_null<gh::bbh::Tags::ConstraintCheckRecords::type*>\n          constraint_check_records,\n      const gsl::not_null<gh::bbh::Tags::ReportedConstraintCheckRecords::type*>\n          reported_constraint_check_records) {\n    *gauge_constraint_exceeded = false;\n    *three_index_constraint_exceeded = false;\n    *common_horizon_lmax_below_or_equal_threshold = false;\n    *common_horizon_success_count = 0;\n    *completion_requested = false;\n    common_horizon_success_records->clear();\n    constraint_check_records->clear();\n    reported_constraint_check_records->clear();\n  }\n};\n}  // namespace detail\n\nnamespace Actions {\n/// Records a successful AhC find and updates BBH completion state.\nstruct RecordCommonHorizonSuccess {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const LinkedMessageId<double>& temporal_id,\n                    const size_t l_max) {\n    auto& common_horizon_success_records =\n        db::get_mutable_reference<gh::bbh::Tags::CommonHorizonSuccessRecords>(\n            make_not_null(&box));\n    const bool inserted =\n        common_horizon_success_records.emplace(temporal_id, l_max).second;\n    if (not inserted) {\n      ERROR(\"Duplicate common-horizon completion record for temporal id \"\n            << temporal_id << \".\");\n    }\n    detail::recompute_completion_state(make_not_null(&box), cache);\n  }\n};\n\n/// Reduction target callback that records reduced constraint maxima and updates\n/// BBH completion state.\nstruct ProcessConstraintMaxima {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const double time,\n                    const double max_gauge_linf,\n                    const double max_three_index_linf) {\n    auto& constraint_check_records =\n        db::get_mutable_reference<gh::bbh::Tags::ConstraintCheckRecords>(\n            make_not_null(&box));\n    const bool inserted =\n        constraint_check_records\n            .emplace(time,\n                     gh::bbh::Tags::ConstraintCheckRecords::mapped_type{\n                         max_gauge_linf, max_three_index_linf})\n            .second;\n    if (not inserted) {\n      ERROR(\"Duplicate BBH completion constraint-max record at t=\" << time\n                                                                   << \".\");\n    }\n    detail::recompute_completion_state(make_not_null(&box), cache);\n  }\n};\n\n/// Initializes the element-local completion-request tag.\nstruct InitializeElementCompletionRequested\n    : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<gh::bbh::Tags::ElementCompletionRequested>;\n  using argument_tags = tmpl::list<>;\n\n  static void apply(const gsl::not_null<bool*> element_completion_requested) {\n    *element_completion_requested = false;\n  }\n};\n}  // namespace Actions\n\ntemplate <class Metavariables>\nstruct CompletionSingleton {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using const_global_cache_tags =\n      tmpl::list<gh::bbh::Tags::MinCommonHorizonSuccessesBeforeChecks,\n                 gh::bbh::Tags::MaxCommonHorizonSuccesses,\n                 gh::bbh::Tags::GaugeConstraintLinfThreshold,\n                 gh::bbh::Tags::ThreeIndexConstraintLinfThreshold,\n                 gh::bbh::Tags::CommonHorizonLMaxThreshold,\n                 gh::bbh::Tags::ConstraintCheckVerbose>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<Initialization::Actions::AddSimpleTags<\n                     gh::bbh::detail::InitializeCompletionState>,\n                 Parallel::Actions::TerminatePhase>>>;\n  using simple_tags_from_options = tmpl::list<>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<CompletionSingleton<Metavariables>>(\n        local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace gh::bbh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CheckConstraintThresholds.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CheckConstraintThresholds.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/Events/CheckConstraintThresholds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/Events/CheckConstraintThresholds.hpp\"\n\nnamespace gh::bbh::Events {\nPUP::able::PUP_ID CheckConstraintThresholds::my_PUP_ID = 0;  // NOLINT\n}  // namespace gh::bbh::Events\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/Events/CheckConstraintThresholds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionCriteria.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionSingleton.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::bbh::Events {\n/*!\n * \\brief Computes per-element Linf norms of the generalized-harmonic\n * constraints, latching BBH completion criteria when thresholds are exceeded.\n *\n * \\details This event is intended to run once common-horizon finding is\n * active (typically behind a separation-based trigger). It monitors the gauge\n * and three-index constraints, using a reduction to determine if their Linf\n * norms exceed completion thresholds.\n */\nclass CheckConstraintThresholds : public Event {\n  using ReductionData = Parallel::ReductionData<\n      Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n      Parallel::ReductionDatum<double, funcl::Max<>>,\n      Parallel::ReductionDatum<double, funcl::Max<>>>;\n\n public:\n  /// \\cond\n  explicit CheckConstraintThresholds(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CheckConstraintThresholds);  // NOLINT\n  /// \\endcond\n\n  using compute_tags_for_observation_box =\n      tmpl::list<gh::Tags::GaugeConstraintCompute<3, Frame::Inertial>,\n                 gh::Tags::ThreeIndexConstraintCompute<3, Frame::Inertial>>;\n  using options = tmpl::list<>;\n  static constexpr Options::String help =\n      \"Checks local Linf norms of constraints against BBH completion \"\n      \"thresholds and forwards reduced maxima to the BBH completion singleton \"\n      \"reduction callback.\";\n  static std::string name() { return \"BbhCheckConstraintThresholds\"; }\n\n  CheckConstraintThresholds() = default;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<\n      ::Tags::Time, gh::Tags::GaugeConstraint<DataVector, 3, Frame::Inertial>,\n      gh::Tags::ThreeIndexConstraint<DataVector, 3, Frame::Inertial>>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  void operator()(\n      const double time,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& gauge_constraint,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& three_index_constraint,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const Component* const /*component*/,\n      const ObservationValue& /*observation_value*/) const {\n    const double local_gauge_linf = local_linf_norm(gauge_constraint);\n    const double local_three_index_linf =\n        local_linf_norm(three_index_constraint);\n    if constexpr (Parallel::is_dg_element_collection_v<Component>) {\n      ERROR(\n          \"BbhCheckConstraintThresholds currently requires array components \"\n          \"(not DgElementCollection).\");\n    } else {\n      const auto& self_proxy =\n          Parallel::get_parallel_component<Component>(cache)[array_index];\n      auto& reduction_target_proxy = Parallel::get_parallel_component<\n          gh::bbh::CompletionSingleton<Metavariables>>(cache);\n      Parallel::contribute_to_reduction<\n          gh::bbh::Actions::ProcessConstraintMaxima>(\n          ReductionData{time, local_gauge_linf, local_three_index_linf},\n          self_proxy, reduction_target_proxy);\n    }\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n private:\n  template <typename TensorType>\n  static double local_linf_norm(const TensorType& tensor) {\n    double result = 0.0;\n    for (size_t storage_index = 0; storage_index < tensor.size();\n         ++storage_index) {\n      const auto& component = tensor[storage_index];\n      result = std::max(result, max(abs(component)));\n    }\n    return result;\n  }\n};\n}  // namespace gh::bbh::Events\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/PhaseControl/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CheckpointAndExitIfComplete.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CheckpointAndExitIfComplete.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/PhaseControl/CheckpointAndExitIfComplete.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/PhaseControl/CheckpointAndExitIfComplete.hpp\"\n\nnamespace gh::bbh::phase_control {\nvoid CheckpointAndExitIfComplete::pup(PUP::er& p) { PhaseChange::pup(p); }\n\nPUP::able::PUP_ID CheckpointAndExitIfComplete::my_PUP_ID = 0;  // NOLINT\n}  // namespace gh::bbh::phase_control\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Bbh/PhaseControl/CheckpointAndExitIfComplete.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionCriteria.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::bbh::phase_control {\nnamespace Tags {\n/// Storage in the phase-change decision tuple for whether the\n/// BBH completion path has requested completion.\nstruct CheckpointRequested {\n  using type = bool;\n  using combine_method = funcl::Or<>;\n  using main_combine_method = funcl::Or<>;\n};\n\n/// Storage in the phase-change decision tuple for jumping from\n/// `WriteCheckpoint` to `Exit`.\nstruct ExitAfterWriteCheckpoint {\n  using type = bool;\n\n  struct combine_method {\n    bool operator()(const bool /*first*/, const bool /*second*/) {\n      ERROR(\n          \"ExitAfterWriteCheckpoint should only be modified during \"\n          \"phase-change arbitration on Main.\");\n    }\n  };\n\n  using main_combine_method = combine_method;\n};\n}  // namespace Tags\n\n/*!\n * \\brief If BBH completion has been requested, jump from `Evolve` to\n * `WriteCheckpoint`, then immediately to `Exit`.\n *\n * \\details The `WriteCheckpoint` phase executes checkpoint actions, typically\n * including final volume data writes, as specified by\n * `EventsAndTriggersAtCheckpoints`.\n */\nstruct CheckpointAndExitIfComplete : public PhaseChange {\n  /// \\cond\n  CheckpointAndExitIfComplete() = default;\n  explicit CheckpointAndExitIfComplete(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CheckpointAndExitIfComplete);  // NOLINT\n  /// \\endcond\n\n  static std::string name() { return \"BbhCheckpointAndExitIfComplete\"; }\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"When BBH completion has been requested, jump from Evolve to \"\n      \"WriteCheckpoint so all elements write final volume data at a \"\n      \"synchronized time, then jump to Exit.\"};\n\n  using argument_tags = tmpl::list<gh::bbh::Tags::ElementCompletionRequested>;\n  using return_tags = tmpl::list<>;\n\n  using phase_change_tags_and_combines =\n      tmpl::list<Tags::CheckpointRequested, Tags::ExitAfterWriteCheckpoint>;\n\n  template <typename Metavariables>\n  using participating_components = typename Metavariables::component_list;\n\n  template <typename... DecisionTags>\n  void initialize_phase_data_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data) const {\n    tuples::get<Tags::CheckpointRequested>(*phase_change_decision_data) = false;\n    tuples::get<Tags::ExitAfterWriteCheckpoint>(*phase_change_decision_data) =\n        false;\n  }\n\n  template <typename ParallelComponent, typename ArrayIndex,\n            typename Metavariables>\n  void contribute_phase_data_impl(const bool element_completion_requested,\n                                  Parallel::GlobalCache<Metavariables>& cache,\n                                  const ArrayIndex& array_index) const {\n    if constexpr (std::is_same_v<typename ParallelComponent::chare_type,\n                                 Parallel::Algorithms::Array>) {\n      Parallel::contribute_to_phase_change_reduction<ParallelComponent>(\n          tuples::TaggedTuple<Tags::CheckpointRequested>{\n              element_completion_requested},\n          cache, array_index);\n    } else {\n      Parallel::contribute_to_phase_change_reduction<ParallelComponent>(\n          tuples::TaggedTuple<Tags::CheckpointRequested>{\n              element_completion_requested},\n          cache);\n    }\n  }\n\n  template <typename... DecisionTags, typename Metavariables>\n  std::optional<std::pair<Parallel::Phase, PhaseControl::ArbitrationStrategy>>\n  arbitrate_phase_change_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data,\n      const Parallel::Phase current_phase,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/) const {\n    auto& checkpoint_requested =\n        tuples::get<Tags::CheckpointRequested>(*phase_change_decision_data);\n    auto& exit_after_write_checkpoint =\n        tuples::get<Tags::ExitAfterWriteCheckpoint>(\n            *phase_change_decision_data);\n\n    if (current_phase == Parallel::Phase::WriteCheckpoint and\n        exit_after_write_checkpoint) {\n      exit_after_write_checkpoint = false;\n      return std::make_pair(\n          Parallel::Phase::Exit,\n          PhaseControl::ArbitrationStrategy::RunPhaseImmediately);\n    }\n\n    if (current_phase == Parallel::Phase::Evolve and checkpoint_requested) {\n      checkpoint_requested = false;\n      exit_after_write_checkpoint = true;\n      return std::make_pair(\n          Parallel::Phase::WriteCheckpoint,\n          PhaseControl::ArbitrationStrategy::RunPhaseImmediately);\n    }\n\n    checkpoint_requested = false;\n    return std::nullopt;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n};\n}  // namespace gh::bbh::phase_control\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp\"\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BjorhusImpl.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InterfaceNullNormal.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ProjectionOperators.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh::BoundaryConditions {\nnamespace {\ndouble min_characteristic_speed(const std::array<DataVector, 4>& char_speeds) {\n  std::array<double, 4> min_speeds{{min(char_speeds[0]), min(char_speeds[1]),\n                                    min(char_speeds[2]), min(char_speeds[3])}};\n  return *std::min_element(min_speeds.begin(), min_speeds.end());\n}\ntemplate <typename T>\nvoid set_bc_corr_zero_when_char_speed_is_positive(\n    const gsl::not_null<T*> dt_v_corr, const DataVector& char_speed_u) {\n  for (DataVector& component : *dt_v_corr) {\n    for (size_t i = 0; i < component.size(); ++i) {\n      if (char_speed_u[i] > 0.) {\n        component[i] = 0.;\n      }\n    }\n  }\n}\n}  // namespace\n\nnamespace detail {\nConstraintPreservingBjorhusType\nconvert_constraint_preserving_bjorhus_type_from_yaml(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (type_read == \"ConstraintPreserving\") {\n    return ConstraintPreservingBjorhusType::ConstraintPreserving;\n  } else if (type_read == \"ConstraintPreservingPhysical\") {\n    return ConstraintPreservingBjorhusType::ConstraintPreservingPhysical;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert input option to \"\n              \"ConstraintPreservingBjorhusType::Type. Must \"\n              \"be one of ConstraintPreserving or ConstraintPreservingPhysical\");\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nConstraintPreservingBjorhus<Dim>::ConstraintPreservingBjorhus(\n    const detail::ConstraintPreservingBjorhusType type,\n    std::optional<std::unique_ptr<::MathFunction<1, Frame::Inertial>>>\n        incoming_wave_profile)\n    : type_(type),\n      incoming_wave_profile_(incoming_wave_profile.has_value()\n                                 ? std::move(incoming_wave_profile.value())\n                                 : nullptr) {\n  if constexpr (Dim < 3) {\n    if (incoming_wave_profile_ != nullptr) {\n      ERROR(\"IncomingWaveProfile can only be used for \"\n            \"ConstraintPreservingBjorhus in 3 spatial dimensions.\");\n    }\n  }\n}\n\ntemplate <size_t Dim>\nConstraintPreservingBjorhus<Dim>::ConstraintPreservingBjorhus(\n    const ConstraintPreservingBjorhus& rhs)\n    : BoundaryCondition<Dim>{dynamic_cast<const BoundaryCondition<Dim>&>(rhs)},\n      type_(rhs.type_),\n      incoming_wave_profile_(rhs.incoming_wave_profile_ != nullptr\n                                 ? rhs.incoming_wave_profile_->get_clone()\n                                 : nullptr) {}\n\ntemplate <size_t Dim>\nConstraintPreservingBjorhus<Dim>& ConstraintPreservingBjorhus<Dim>::operator=(\n    const ConstraintPreservingBjorhus& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  type_ = rhs.type_;\n  incoming_wave_profile_ = rhs.incoming_wave_profile_ != nullptr\n                               ? rhs.incoming_wave_profile_->get_clone()\n                               : nullptr;\n  return *this;\n}\n\ntemplate <size_t Dim>\nConstraintPreservingBjorhus<Dim>::ConstraintPreservingBjorhus(\n    CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nConstraintPreservingBjorhus<Dim>::get_clone() const {\n  return std::make_unique<ConstraintPreservingBjorhus>(*this);\n}\n\ntemplate <size_t Dim>\nvoid ConstraintPreservingBjorhus<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n  p | type_;\n  p | incoming_wave_profile_;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string> ConstraintPreservingBjorhus<Dim>::dg_time_derivative(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        dt_spacetime_metric_correction,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        dt_pi_correction,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        dt_phi_correction,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& /*normal_vector*/,\n    // c.f. dg_interior_evolved_variables_tags\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n    // c.f. dg_interior_temporary_tags\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const tnsr::AA<DataVector, Dim, Frame::Inertial>& inverse_spacetime_metric,\n    const tnsr::A<DataVector, Dim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_constraint,\n    const tnsr::a<DataVector, Dim, Frame::Inertial>& gauge_source,\n    const tnsr::ab<DataVector, Dim, Frame::Inertial>&\n        spacetime_deriv_gauge_source,\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n        logical_dt_spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& logical_dt_pi,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& logical_dt_phi,\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& d_pi,\n    const tnsr::ijaa<DataVector, Dim, Frame::Inertial>& d_phi,\n    const double time) const {\n  TempBuffer<tmpl::list<::Tags::TempI<0, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempiaa<1, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::TempII<0, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempii<0, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempa<0, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempa<1, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::TempA<1, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::TempA<2, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempaa<0, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::TempAb<0, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::TempAA<1, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempaa<1, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempiaa<2, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempaa<2, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempaa<3, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempa<2, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempa<3, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempaa<4, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempiaa<3, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempaa<5, Dim, Frame::Inertial, DataVector>,\n                        ::Tags::Tempaa<6, Dim, Frame::Inertial, DataVector>,\n                        gr::Tags::SpacetimeNormalOneForm<DataVector, Dim>,\n                        // inertial time derivatives\n                        ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, Dim>>,\n                        ::Tags::dt<Tags::Pi<DataVector, Dim>>,\n                        ::Tags::dt<Tags::Phi<DataVector, Dim>>>>\n      local_buffer(get_size(get<0>(normal_covector)), 0.);\n  get<0>(get<gr::Tags::SpacetimeNormalOneForm<DataVector, Dim>>(local_buffer)) =\n      -get(lapse);\n  const tnsr::a<DataVector, Dim, Frame::Inertial>&\n      spacetime_unit_normal_one_form =\n          get<gr::Tags::SpacetimeNormalOneForm<DataVector, Dim>>(local_buffer);\n  tnsr::aa<DataVector, Dim, Frame::Inertial> dt_spacetime_metric;\n  tnsr::aa<DataVector, Dim, Frame::Inertial> dt_pi;\n  tnsr::iaa<DataVector, Dim, Frame::Inertial> dt_phi;\n  if (face_mesh_velocity.has_value()) {\n    for (size_t storage_index = 0; storage_index < dt_pi.size();\n         ++storage_index) {\n      dt_spacetime_metric[storage_index].set_data_ref(make_not_null(\n          &get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, Dim>>>(\n              local_buffer)[storage_index]));\n      dt_pi[storage_index].set_data_ref(\n          make_not_null(&get<::Tags::dt<Tags::Pi<DataVector, Dim>>>(\n              local_buffer)[storage_index]));\n    }\n    for (size_t storage_index = 0; storage_index < dt_phi.size();\n         ++storage_index) {\n      dt_phi[storage_index].set_data_ref(\n          make_not_null(&get<::Tags::dt<Tags::Phi<DataVector, Dim>>>(\n              local_buffer)[storage_index]));\n    }\n    // Compute inertial time derivative by subtracting mesh velocity from\n    // logical time derivative.\n    for (size_t a = 0; a < Dim + 1; ++a) {\n      for (size_t b = a; b < Dim + 1; ++b) {\n        dt_spacetime_metric.get(a, b) = logical_dt_spacetime_metric.get(a, b);\n        dt_pi.get(a, b) = logical_dt_pi.get(a, b);\n        for (size_t d = 0; d < Dim; ++d) {\n          dt_spacetime_metric.get(a, b) -=\n              face_mesh_velocity->get(d) * d_spacetime_metric.get(d, a, b);\n          dt_pi.get(a, b) -= face_mesh_velocity->get(d) * d_pi.get(d, a, b);\n        }\n        for (size_t i = 0; i < Dim; ++i) {\n          dt_phi.get(i, a, b) = logical_dt_phi.get(i, a, b);\n          for (size_t d = 0; d < Dim; ++d) {\n            dt_phi.get(i, a, b) -=\n                face_mesh_velocity->get(d) * d_phi.get(d, i, a, b);\n          }\n        }\n      }\n    }\n  } else {\n    for (size_t storage_index = 0; storage_index < dt_pi.size();\n         ++storage_index) {\n      dt_spacetime_metric[storage_index].set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          make_not_null(&const_cast<DataVector&>(\n              logical_dt_spacetime_metric[storage_index])));\n      dt_pi[storage_index].set_data_ref(make_not_null(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          &const_cast<DataVector&>(logical_dt_pi[storage_index])));\n    }\n    for (size_t storage_index = 0; storage_index < dt_phi.size();\n         ++storage_index) {\n      dt_phi[storage_index].set_data_ref(make_not_null(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          &const_cast<DataVector&>(logical_dt_phi[storage_index])));\n    }\n  }\n\n  auto& unit_interface_normal_vector =\n      get<::Tags::TempI<0, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& four_index_constraint =\n      get<::Tags::Tempiaa<1, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& inverse_spatial_metric =\n      get<::Tags::TempII<0, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& extrinsic_curvature =\n      get<::Tags::Tempii<0, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& incoming_null_one_form =\n      get<::Tags::Tempa<0, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& outgoing_null_one_form =\n      get<::Tags::Tempa<1, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& incoming_null_vector =\n      get<::Tags::TempA<1, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& outgoing_null_vector =\n      get<::Tags::TempA<2, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& projection_ab =\n      get<::Tags::Tempaa<0, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& projection_Ab =\n      get<::Tags::TempAb<0, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& projection_AB =\n      get<::Tags::TempAA<1, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& char_projected_rhs_dt_v_psi =\n      get<::Tags::Tempaa<1, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& char_projected_rhs_dt_v_zero =\n      get<::Tags::Tempiaa<2, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& char_projected_rhs_dt_v_plus =\n      get<::Tags::Tempaa<2, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& char_projected_rhs_dt_v_minus =\n      get<::Tags::Tempaa<3, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& constraint_char_zero_plus =\n      get<::Tags::Tempa<2, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& constraint_char_zero_minus =\n      get<::Tags::Tempa<3, Dim, Frame::Inertial, DataVector>>(local_buffer);\n\n  typename Tags::CharacteristicSpeeds<DataVector, Dim>::type char_speeds;\n\n  auto& bc_dt_v_psi =\n      get<::Tags::Tempaa<4, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& bc_dt_v_zero =\n      get<::Tags::Tempiaa<3, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& bc_dt_v_plus =\n      get<::Tags::Tempaa<5, Dim, Frame::Inertial, DataVector>>(local_buffer);\n  auto& bc_dt_v_minus =\n      get<::Tags::Tempaa<6, Dim, Frame::Inertial, DataVector>>(local_buffer);\n\n  compute_intermediate_vars(\n      make_not_null(&unit_interface_normal_vector),\n      make_not_null(&four_index_constraint),\n      make_not_null(&inverse_spatial_metric),\n      make_not_null(&extrinsic_curvature),\n      make_not_null(&incoming_null_one_form),\n      make_not_null(&outgoing_null_one_form),\n      make_not_null(&incoming_null_vector),\n      make_not_null(&outgoing_null_vector), make_not_null(&projection_ab),\n      make_not_null(&projection_Ab), make_not_null(&projection_AB),\n      make_not_null(&char_projected_rhs_dt_v_psi),\n      make_not_null(&char_projected_rhs_dt_v_zero),\n      make_not_null(&char_projected_rhs_dt_v_plus),\n      make_not_null(&char_projected_rhs_dt_v_minus),\n      make_not_null(&constraint_char_zero_plus),\n      make_not_null(&constraint_char_zero_minus), make_not_null(&char_speeds),\n      face_mesh_velocity, normal_covector, pi, phi, spacetime_metric, coords,\n      gamma1, gamma2, lapse, shift, inverse_spacetime_metric,\n      spacetime_unit_normal_vector, spacetime_unit_normal_one_form,\n      three_index_constraint, gauge_source, spacetime_deriv_gauge_source, dt_pi,\n      dt_phi, dt_spacetime_metric, d_pi, d_phi, d_spacetime_metric);\n\n  // If no point on the boundary has any incoming characteristic, return here\n  if (min_characteristic_speed(char_speeds) >= 0.) {\n    std::fill(dt_spacetime_metric_correction->begin(),\n              dt_spacetime_metric_correction->end(), 0.);\n    std::fill(dt_pi_correction->begin(), dt_pi_correction->end(), 0.);\n    std::fill(dt_phi_correction->begin(), dt_phi_correction->end(), 0.);\n    return {};\n  }\n\n  Bjorhus::constraint_preserving_corrections_dt_v_psi(\n      make_not_null(&bc_dt_v_psi), unit_interface_normal_vector,\n      three_index_constraint, char_speeds);\n\n  Bjorhus::constraint_preserving_corrections_dt_v_zero(\n      make_not_null(&bc_dt_v_zero), unit_interface_normal_vector,\n      four_index_constraint, char_speeds);\n\n  // In order to set dt<V+> = 0, the correction term returned here must be\n  // b_correction = -1*existing(dt<V+>), such that\n  // final(dt<V+>) = existing(dt<V+>) + b_correction = 0\n  for (size_t a = 0; a <= Dim; ++a) {\n    for (size_t b = a; b <= Dim; ++b) {\n      bc_dt_v_plus.get(a, b) = -char_projected_rhs_dt_v_plus.get(a, b);\n    }\n  }\n\n  if (type_ == detail::ConstraintPreservingBjorhusType::ConstraintPreserving) {\n    Bjorhus::constraint_preserving_gauge_corrections_dt_v_minus(\n        make_not_null(&bc_dt_v_minus), gamma2, coords, incoming_null_one_form,\n        outgoing_null_one_form, incoming_null_vector, outgoing_null_vector,\n        projection_ab, projection_Ab, projection_AB,\n        char_projected_rhs_dt_v_psi, char_projected_rhs_dt_v_minus,\n        constraint_char_zero_plus, constraint_char_zero_minus, char_speeds);\n  } else if (type_ == detail::ConstraintPreservingBjorhusType::\n                          ConstraintPreservingPhysical) {\n    Bjorhus::constraint_preserving_gauge_physical_corrections_dt_v_minus(\n        make_not_null(&bc_dt_v_minus), gamma2, coords, time, normal_covector,\n        unit_interface_normal_vector, spacetime_unit_normal_vector,\n        incoming_null_one_form, outgoing_null_one_form, incoming_null_vector,\n        outgoing_null_vector, projection_ab, projection_Ab, projection_AB,\n        inverse_spatial_metric, extrinsic_curvature, spacetime_metric,\n        inverse_spacetime_metric, three_index_constraint,\n        char_projected_rhs_dt_v_psi, char_projected_rhs_dt_v_minus,\n        constraint_char_zero_plus, constraint_char_zero_minus, phi, d_phi, d_pi,\n        char_speeds, incoming_wave_profile_.get());\n  } else {\n    ERROR(\n        \"Failed to set dtVMinus. Input option must be one of \"\n        \"ConstraintPreserving or ConstraintPreservingPhysical\");\n  }\n\n  // Only add corrections at grid points where the char speeds are negative\n  set_bc_corr_zero_when_char_speed_is_positive(make_not_null(&bc_dt_v_psi),\n                                               char_speeds[0]);\n  set_bc_corr_zero_when_char_speed_is_positive(make_not_null(&bc_dt_v_zero),\n                                               char_speeds[1]);\n  set_bc_corr_zero_when_char_speed_is_positive(make_not_null(&bc_dt_v_plus),\n                                               char_speeds[2]);\n  set_bc_corr_zero_when_char_speed_is_positive(make_not_null(&bc_dt_v_minus),\n                                               char_speeds[3]);\n\n  // The boundary conditions here are imposed as corrections to the projections\n  // of the right-hand-sides of the GH evolution equations (i.e. using Bjorhus'\n  // method), and are written down in Eq. (63) - (65) of Lindblom et al (2005).\n  // Now that we have calculated those corrections, we project them back as\n  // corrections to dt<evolved variables>\n  auto dt_evolved_vars = evolved_fields_from_characteristic_fields(\n      gamma2, bc_dt_v_psi, bc_dt_v_zero, bc_dt_v_plus, bc_dt_v_minus,\n      normal_covector);\n\n  *dt_pi_correction = get<Tags::Pi<DataVector, Dim>>(dt_evolved_vars);\n  *dt_phi_correction = get<Tags::Phi<DataVector, Dim>>(dt_evolved_vars);\n  *dt_spacetime_metric_correction =\n      get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(dt_evolved_vars);\n\n  if (face_mesh_velocity.has_value()) {\n    const auto radial_mesh_velocity =\n        get(dot_product(normal_covector, *face_mesh_velocity));\n    // we use 1e-10 instead of 0 below to allow for purely tangentially\n    // moving grids, eg a rotating sphere, with some leeway for\n    // floating-point errors.\n    if (max(radial_mesh_velocity) > 1.e-10) {\n      return {\n          \"We found the radial mesh velocity points in the direction \"\n          \"of the outward normal, i.e. we possibly have an expanding \"\n          \"domain. Its unclear if proper boundary conditions are \"\n          \"imposed in this case.\"};\n    }\n  }\n\n  return {};\n}\n\ntemplate <size_t Dim>\nvoid ConstraintPreservingBjorhus<Dim>::compute_intermediate_vars(\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        unit_interface_normal_vector,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        four_index_constraint,\n    const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n        inverse_spatial_metric,\n    const gsl::not_null<tnsr::ii<DataVector, Dim, Frame::Inertial>*>\n        extrinsic_curvature,\n    const gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*>\n        incoming_null_one_form,\n    const gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*>\n        outgoing_null_one_form,\n    const gsl::not_null<tnsr::A<DataVector, Dim, Frame::Inertial>*>\n        incoming_null_vector,\n    const gsl::not_null<tnsr::A<DataVector, Dim, Frame::Inertial>*>\n        outgoing_null_vector,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        projection_ab,\n    const gsl::not_null<tnsr::Ab<DataVector, Dim, Frame::Inertial>*>\n        projection_Ab,\n    const gsl::not_null<tnsr::AA<DataVector, Dim, Frame::Inertial>*>\n        projection_AB,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        char_projected_rhs_dt_v_psi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        char_projected_rhs_dt_v_zero,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        char_projected_rhs_dt_v_plus,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        char_projected_rhs_dt_v_minus,\n    const gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*>\n        constraint_char_zero_plus,\n    const gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*>\n        constraint_char_zero_minus,\n    const gsl::not_null<std::array<DataVector, 4>*> char_speeds,\n\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& /* coords */,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const tnsr::AA<DataVector, Dim, Frame::Inertial>& inverse_spacetime_metric,\n    const tnsr::A<DataVector, Dim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::a<DataVector, Dim, Frame::Inertial>&\n        spacetime_unit_normal_one_form,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_constraint,\n    const tnsr::a<DataVector, Dim, Frame::Inertial>& gauge_source,\n    const tnsr::ab<DataVector, Dim, Frame::Inertial>&\n        spacetime_deriv_gauge_source,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& dt_pi,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& dt_phi,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& dt_spacetime_metric,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& d_pi,\n    const tnsr::ijaa<DataVector, Dim, Frame::Inertial>& d_phi,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& /* d_spacetime_metric */)\n    const {\n  TempBuffer<tmpl::list<::Tags::TempScalar<0, DataVector>,\n                        ::Tags::Tempia<0, Dim, Frame::Inertial, DataVector>>>\n      local_buffer(get_size(get<0>(normal_covector)), 0.);\n  auto& one_over_lapse_sqrd =\n      get(get<::Tags::TempScalar<0, DataVector>>(local_buffer));\n  auto& two_index_constraint =\n      get<::Tags::Tempia<0, Dim, Frame::Inertial, DataVector>>(local_buffer);\n\n  one_over_lapse_sqrd = 1.0 / (get(lapse) * get(lapse));\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      inverse_spatial_metric->get(i, j) =\n          inverse_spacetime_metric.get(1 + i, 1 + j) +\n          (shift.get(i) * shift.get(j) * one_over_lapse_sqrd);\n    }\n  }\n\n  raise_or_lower_index(unit_interface_normal_vector, normal_covector,\n                       *inverse_spatial_metric);\n\n  gh::extrinsic_curvature(extrinsic_curvature, spacetime_unit_normal_vector, pi,\n                          phi);\n\n  if (LIKELY(Dim == 3)) {\n    gh::four_index_constraint(four_index_constraint, d_phi);\n  } else if (UNLIKELY(Dim == 2)) {\n    for (size_t a = 0; a <= Dim; ++a) {\n      for (size_t b = 0; b <= Dim; ++b) {\n        four_index_constraint->get(0, a, b) =\n            d_phi.get(0, 1, a, b) - d_phi.get(1, 0, a, b);\n        four_index_constraint->get(1, a, b) =\n            -four_index_constraint->get(0, a, b);\n      }\n    }\n  } else {\n    std::fill(four_index_constraint->begin(), four_index_constraint->end(), 0.);\n  }\n\n  gr::interface_null_normal(incoming_null_one_form,\n                            spacetime_unit_normal_one_form, normal_covector,\n                            shift, -1.);\n  gr::interface_null_normal(outgoing_null_one_form,\n                            spacetime_unit_normal_one_form, normal_covector,\n                            shift, 1.);\n  gr::interface_null_normal(incoming_null_vector, spacetime_unit_normal_vector,\n                            *unit_interface_normal_vector, -1.);\n  gr::interface_null_normal(outgoing_null_vector, spacetime_unit_normal_vector,\n                            *unit_interface_normal_vector, 1.);\n\n  gr::transverse_projection_operator(projection_ab, spacetime_metric,\n                                     spacetime_unit_normal_one_form,\n                                     normal_covector, shift);\n  gr::transverse_projection_operator(\n      projection_Ab, spacetime_unit_normal_vector,\n      spacetime_unit_normal_one_form, *unit_interface_normal_vector,\n      normal_covector, shift);\n  gr::transverse_projection_operator(projection_AB, inverse_spacetime_metric,\n                                     spacetime_unit_normal_vector,\n                                     *unit_interface_normal_vector);\n\n  const auto dt_char_fields = characteristic_fields(\n      gamma2, *inverse_spatial_metric, dt_spacetime_metric, dt_pi, dt_phi,\n      normal_covector);\n  *char_projected_rhs_dt_v_psi =\n      get<Tags::VSpacetimeMetric<DataVector, Dim>>(dt_char_fields);\n  *char_projected_rhs_dt_v_zero =\n      get<Tags::VZero<DataVector, Dim>>(dt_char_fields);\n  *char_projected_rhs_dt_v_plus =\n      get<Tags::VPlus<DataVector, Dim>>(dt_char_fields);\n  *char_projected_rhs_dt_v_minus =\n      get<Tags::VMinus<DataVector, Dim>>(dt_char_fields);\n\n  // c^{\\hat{0}-}_a = F_a + n^k C_{ka}\n  gh::two_index_constraint(\n      make_not_null(&two_index_constraint), spacetime_deriv_gauge_source,\n      spacetime_unit_normal_one_form, spacetime_unit_normal_vector,\n      *inverse_spatial_metric, inverse_spacetime_metric, pi, phi, d_pi, d_phi,\n      gamma2, three_index_constraint);\n  f_constraint(constraint_char_zero_plus, gauge_source,\n               spacetime_deriv_gauge_source, spacetime_unit_normal_one_form,\n               spacetime_unit_normal_vector, *inverse_spatial_metric,\n               inverse_spacetime_metric, pi, phi, d_pi, d_phi, gamma2,\n               three_index_constraint);\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    constraint_char_zero_minus->get(a) = constraint_char_zero_plus->get(a);\n    for (size_t i = 0; i < Dim; ++i) {\n      constraint_char_zero_plus->get(a) -=\n          unit_interface_normal_vector->get(i) * two_index_constraint.get(i, a);\n      constraint_char_zero_minus->get(a) +=\n          unit_interface_normal_vector->get(i) * two_index_constraint.get(i, a);\n    }\n  }\n\n  characteristic_speeds(char_speeds, gamma1, lapse, shift, normal_covector,\n                        face_mesh_velocity);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID ConstraintPreservingBjorhus<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class ConstraintPreservingBjorhus<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gh::BoundaryConditions::detail {\nenum class ConstraintPreservingBjorhusType {\n  ConstraintPreserving,\n  ConstraintPreservingPhysical\n};\n\nConstraintPreservingBjorhusType\nconvert_constraint_preserving_bjorhus_type_from_yaml(\n    const Options::Option& options);\n}  // namespace gh::BoundaryConditions::detail\n\nnamespace gh::BoundaryConditions {\n/*!\n * \\brief Sets constraint preserving boundary conditions using the Bjorhus\n * method.\n *\n * \\details Boundary conditions for the generalized harmonic evolution system\n * can be divided in to three parts, constraint-preserving, physical and gauge\n * boundary conditions.\n *\n * The generalized harmonic (GH) evolution system is a first-order reduction of\n * Einstein equations brought about by the imposition of GH gauge. This\n * introduces constraints on the free (evolved) variables in addition to the\n * standard Hamiltonian and momentum constraints. The constraint-preserving\n * portion of the boundary conditions is designed to prevent the influx of\n * constraint violations from external faces of the evolution domain, by damping\n * them away on a controlled and short time-scale. These conditions are imposed\n * as corrections to the characteristic projections of the right-hand-sides of\n * the GH evolution equations (i.e. using Bjorhus' method \\cite Bjorhus1995),\n * as written down in Eq. (63) - (65) of \\cite Lindblom2005qh . In addition to\n * these equations, the fourth projection is simply frozen in the unlikely case\n * its coordinate speed becomes negative, i.e. \\f$d_t u^{\\hat{1}+}{}_{ab}=0\\f$\n * (in the notation of \\cite Lindblom2005qh). The gauge degrees\n * of freedom are controlled by imposing a Sommerfeld-type condition (\\f$L=0\\f$\n * member of the hierarchy derived in \\cite BaylissTurkel) that allow gauge\n * perturbations to pass through the boundary without strong reflections. These\n * assume a spherical outer boundary, and can be written down as in Eq. (25) of\n * \\cite Rinne2007ui . Finally, the physical boundary conditions control the\n * influx of inward propagating gravitational-wave solutions from the external\n * boundaries. These are derived by considering the evolution system of the Weyl\n * curvature tensor, and controlling the inward propagating characteristics of\n * the system that are proportional to the Newman-Penrose curvature spinor\n * components \\f$\\Psi_4\\f$ and \\f$\\Psi_0\\f$. Here we use Eq. (68) of\n * \\cite Lindblom2005qh to disallow any incoming waves. It is to be noted that\n * all the above conditions are also imposed on characteristic modes with speeds\n * exactly zero.\n *\n * An optional injected incoming-wave contribution can be specified through\n * `IncomingWaveProfile`. The injected strain-rate tensor is\n * \\f[\n *   \\dot{h}_{ab} = \\dot{f}(t)\n *   \\left(\\hat{x}_a \\hat{x}_b + \\hat{y}_a \\hat{y}_b - 2 \\hat{z}_a\n * \\hat{z}_b\\right),\n * \\f]\n * where the configured profile is interpreted directly as \\f$\\dot{f}(t)\\f$ and\n * \\f$\\hat{x}_a\\f$, \\f$\\hat{y}_a\\f$ and \\f$\\hat{z}_a\\f$ are the components of\n * the coordinate basis vectors. This formula is taken from\n * \\cite Lindblom2005qh.  It should be considered an approximate perturbation\n * rather than an exact gravitational wave which would have to be constructed at\n * null-infinity. Note that the profile of the injected wave is specified at the\n * outer boundary and its amplitude should be adjusted according to the usual\n * 1/r scaling.\n *\n * This class provides two choices of combinations of the above corrections:\n *  - `ConstraintPreserving` : this imposes the constraint-preserving and\n * gauge-controlling corrections;\n *  - `ConstraintPreservingPhysical` : this additionally restricts the influx of\n * any physical gravitational waves from the outer boundary, in addition to\n * preventing the influx of constraint violations and gauge perturbations.\n *\n * We refer to `Bjorhus::constraint_preserving_corrections_dt_v_psi()`,\n * `Bjorhus::constraint_preserving_corrections_dt_v_zero()`,\n * `Bjorhus::constraint_preserving_gauge_corrections_dt_v_minus()`, and\n * `Bjorhus::constraint_preserving_gauge_physical_corrections_dt_v_minus()`\n * for the further details on implementation.\n *\n * \\note These boundary conditions assume a spherical outer boundary.\n */\ntemplate <size_t Dim>\nclass ConstraintPreservingBjorhus final : public BoundaryCondition<Dim> {\n public:\n  struct TypeOptionTag {\n    using type = detail::ConstraintPreservingBjorhusType;\n    static std::string name() { return \"Type\"; }\n    static constexpr Options::String help{\n        \"Whether to impose ConstraintPreserving, with or without physical \"\n        \"terms for VMinus.\"};\n  };\n\n  struct IncomingWaveProfileOptionTag {\n    using type =\n        Options::Auto<std::unique_ptr<::MathFunction<1, Frame::Inertial>>,\n                      Options::AutoLabel::None>;\n    static std::string name() { return \"IncomingWaveProfile\"; }\n    static constexpr Options::String help{\n        \"Optional incoming-wave profile interpreted as the first time \"\n        \"derivative for the injected physical wave. See the \"\n        \"ConstraintPreservingBjorhus class documentation for the injected-wave \"\n        \"formula. Specify `None` to disable injection. This option is only \"\n        \"supported in 3D.\"};\n  };\n\n  using options = tmpl::flatten<tmpl::list<\n      TypeOptionTag,\n      tmpl::conditional_t<Dim == 3, tmpl::list<IncomingWaveProfileOptionTag>,\n                          tmpl::list<>>>>;\n  static constexpr Options::String help{\n      \"ConstraintPreservingBjorhus boundary conditions setting the value of the\"\n      \"time derivatives of the spacetime metric, Phi and Pi to expressions that\"\n      \"prevent the influx of constraint violations and reflections.\"};\n  static std::string name() { return \"ConstraintPreservingBjorhus\"; }\n\n  explicit ConstraintPreservingBjorhus(\n      detail::ConstraintPreservingBjorhusType type,\n      std::optional<std::unique_ptr<::MathFunction<1, Frame::Inertial>>>\n          incoming_wave_profile = std::nullopt);\n\n  ConstraintPreservingBjorhus() = default;\n  /// \\cond\n  ConstraintPreservingBjorhus(ConstraintPreservingBjorhus&&) = default;\n  ConstraintPreservingBjorhus& operator=(ConstraintPreservingBjorhus&&) =\n      default;\n  ConstraintPreservingBjorhus(const ConstraintPreservingBjorhus&);\n  ConstraintPreservingBjorhus& operator=(const ConstraintPreservingBjorhus&);\n  /// \\endcond\n  ~ConstraintPreservingBjorhus() override = default;\n\n  explicit ConstraintPreservingBjorhus(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition,\n      ConstraintPreservingBjorhus);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::TimeDerivative;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                 Tags::Pi<DataVector, Dim>, Tags::Phi<DataVector, Dim>>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 Tags::ConstraintGamma1, Tags::ConstraintGamma2,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n                 gr::Tags::InverseSpacetimeMetric<DataVector, Dim>,\n                 gr::Tags::SpacetimeNormalVector<DataVector, Dim>,\n                 Tags::ThreeIndexConstraint<DataVector, Dim>,\n                 Tags::GaugeH<DataVector, Dim>,\n                 Tags::SpacetimeDerivGaugeH<DataVector, Dim>>;\n  using dg_interior_dt_vars_tags =\n      tmpl::list<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, Dim>>,\n                 ::Tags::dt<Tags::Pi<DataVector, Dim>>,\n                 ::Tags::dt<Tags::Phi<DataVector, Dim>>>;\n  using dg_interior_deriv_vars_tags =\n      tmpl::list<::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                               tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::Tags::deriv<Tags::Pi<DataVector, Dim>, tmpl::size_t<Dim>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<Tags::Phi<DataVector, Dim>, tmpl::size_t<Dim>,\n                               Frame::Inertial>>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_time_derivative(\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          dt_spacetime_metric_correction,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          dt_pi_correction,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n          dt_phi_correction,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& /*normal_vector*/,\n      // c.f. dg_interior_evolved_variables_tags\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n      // c.f. dg_interior_temporary_tags\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n      const tnsr::AA<DataVector, Dim, Frame::Inertial>&\n          inverse_spacetime_metric,\n      const tnsr::A<DataVector, Dim, Frame::Inertial>&\n          spacetime_unit_normal_vector,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_constraint,\n      const tnsr::a<DataVector, Dim, Frame::Inertial>& gauge_source,\n      const tnsr::ab<DataVector, Dim, Frame::Inertial>&\n          spacetime_deriv_gauge_source,\n      // c.f. dg_interior_dt_vars_tags\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n          logical_dt_spacetime_metric,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& logical_dt_pi,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& logical_dt_phi,\n      // c.f. dg_interior_deriv_vars_tags\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& d_spacetime_metric,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& d_pi,\n      const tnsr::ijaa<DataVector, Dim, Frame::Inertial>& d_phi,\n      double time = std::numeric_limits<double>::signaling_NaN()) const;\n\n private:\n  void compute_intermediate_vars(\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          unit_interface_normal_vector,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n          four_index_constraint,\n      gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n          inverse_spatial_metric,\n      gsl::not_null<tnsr::ii<DataVector, Dim, Frame::Inertial>*>\n          extrinsic_curvature,\n      gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*>\n          incoming_null_one_form,\n      gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*>\n          outgoing_null_one_form,\n      gsl::not_null<tnsr::A<DataVector, Dim, Frame::Inertial>*>\n          incoming_null_vector,\n      gsl::not_null<tnsr::A<DataVector, Dim, Frame::Inertial>*>\n          outgoing_null_vector,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*> projection_ab,\n      gsl::not_null<tnsr::Ab<DataVector, Dim, Frame::Inertial>*> projection_Ab,\n      gsl::not_null<tnsr::AA<DataVector, Dim, Frame::Inertial>*> projection_AB,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          char_projected_rhs_dt_v_psi,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n          char_projected_rhs_dt_v_zero,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          char_projected_rhs_dt_v_plus,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          char_projected_rhs_dt_v_minus,\n      gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*>\n          constraint_char_zero_plus,\n      gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*>\n          constraint_char_zero_minus,\n      gsl::not_null<std::array<DataVector, 4>*> char_speeds,\n\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n      const tnsr::AA<DataVector, Dim, Frame::Inertial>&\n          inverse_spacetime_metric,\n      const tnsr::A<DataVector, Dim, Frame::Inertial>&\n          spacetime_unit_normal_vector,\n      const tnsr::a<DataVector, Dim, Frame::Inertial>&\n          spacetime_unit_normal_one_form,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_constraint,\n      const tnsr::a<DataVector, Dim, Frame::Inertial>& gauge_source,\n      const tnsr::ab<DataVector, Dim, Frame::Inertial>&\n          spacetime_deriv_gauge_source,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& dt_pi,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& dt_phi,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& dt_spacetime_metric,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& d_pi,\n      const tnsr::ijaa<DataVector, Dim, Frame::Inertial>& d_phi,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& d_spacetime_metric)\n      const;\n\n  detail::ConstraintPreservingBjorhusType type_{\n      detail::ConstraintPreservingBjorhusType::ConstraintPreservingPhysical};\n  std::unique_ptr<::MathFunction<1, Frame::Inertial>> incoming_wave_profile_{};\n};\n}  // namespace gh::BoundaryConditions\n\ntemplate <>\nstruct Options::create_from_yaml<\n    gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType> {\n  template <typename Metavariables>\n  static\n      typename gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType\n      create(const Options::Option& options) {\n    return gh::BoundaryConditions::detail::\n        convert_constraint_preserving_bjorhus_type_from_yaml(options);\n  }\n};\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BjorhusImpl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BjorhusImpl.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/CovariantDerivOfExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ProjectionOperators.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylPropagating.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gh::BoundaryConditions::Bjorhus {\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_corrections_dt_v_psi(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_psi,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>&\n        three_index_constraint,\n    const std::array<DataType, 4>& char_speeds) {\n  for (size_t a = 0; a <= VolumeDim; ++a) {\n    for (size_t b = a; b <= VolumeDim; ++b) {\n      bc_dt_v_psi->get(a, b) = char_speeds[0] *\n                               unit_interface_normal_vector.get(0) *\n                               three_index_constraint.get(0, a, b);\n      for (size_t i = 1; i < VolumeDim; ++i) {\n        bc_dt_v_psi->get(a, b) += char_speeds[0] *\n                                  unit_interface_normal_vector.get(i) *\n                                  three_index_constraint.get(i, a, b);\n      }\n    }\n  }\n}\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_corrections_dt_v_zero(\n    const gsl::not_null<tnsr::iaa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_zero,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>&\n        four_index_constraint,\n    const std::array<DataType, 4>& char_speeds) {\n  set_number_of_grid_points(bc_dt_v_zero, unit_interface_normal_vector);\n  std::fill(bc_dt_v_zero->begin(), bc_dt_v_zero->end(), 0.);\n\n  if (VolumeDim == 3) {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = a; b <= VolumeDim; ++b) {\n        // Lets say this term is T2_{iab} := - n_l \\beta^l n^j C_{jiab}.\n        // But we store D_{iab} = LeviCivita^{ijk} dphi_{jkab},\n        // and C_{ijab} = LeviCivita^{kij} D_{kab}\n        // where D is `four_index_constraint`.\n        // therefore, T2_{iab} =  char_speed<VZero> n^j C_{jiab}\n        // (since char_speed<VZero> = - n_l \\beta^l), and therefore:\n        // T2_{iab} = char_speed<VZero> n^j LeviCivita^{ikj} D_{kab}.\n        // Let LeviCivitaIterator be indexed by\n        // it[0] <--> i,\n        // it[1] <--> j,\n        // it[2] <--> k, then\n        // T2_{it[0], ab} += char_speed<VZero> n^it[2] it.sign() D_{it[1], ab};\n        for (LeviCivitaIterator<VolumeDim> it; it; ++it) {\n          bc_dt_v_zero->get(it[0], a, b) +=\n              it.sign() * char_speeds[1] *\n              unit_interface_normal_vector.get(it[2]) *\n              four_index_constraint.get(it[1], a, b);\n        }\n      }\n    }\n  } else if (VolumeDim == 2) {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = a; b <= VolumeDim; ++b) {\n        // Lets say this term is T2_{kab} := - n_l \\beta^l n^j C_{jkab}.\n        // In 2+1 spacetime, we store the four index constraint to\n        // be D_{1ab} = C_{12ab}, C_{2ab} = C_{21ab}. Therefore,\n        // T_{kab} = -n_l \\beta^l (n^1 C_{1kab} + n^2 C_{2kab}), i.e.\n        // T_{1ab} = -n_l \\beta^l n^2 D_{2ab}, T_{2ab} = -n_l \\beta^l n^1\n        // D_{1ab}.\n        bc_dt_v_zero->get(0, a, b) +=\n            char_speeds[1] * (unit_interface_normal_vector.get(1) *\n                              four_index_constraint.get(1, a, b));\n        bc_dt_v_zero->get(1, a, b) +=\n            char_speeds[1] * (unit_interface_normal_vector.get(0) *\n                              four_index_constraint.get(0, a, b));\n      }\n    }\n  }\n}\n\nnamespace detail {\ntemplate <size_t VolumeDim, typename DataType>\nvoid add_gauge_sommerfeld_terms_to_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const Scalar<DataType>& gamma2,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>& inertial_coords,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& incoming_null_one_form,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_psi) {\n  // gauge_bc_coeff below is hard-coded here to its default value in SpEC\n  constexpr double gauge_bc_coeff = 1.;\n\n  DataType inertial_radius_or_scalar_factor(get_size(get<0>(inertial_coords)),\n                                            0.);\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    inertial_radius_or_scalar_factor += square(inertial_coords.get(i));\n  }\n  inertial_radius_or_scalar_factor =\n      get(gamma2) - (gauge_bc_coeff / sqrt(inertial_radius_or_scalar_factor));\n\n  for (size_t a = 0; a <= VolumeDim; ++a) {\n    for (size_t b = a; b <= VolumeDim; ++b) {\n      for (size_t c = 0; c <= VolumeDim; ++c) {\n        for (size_t d = 0; d <= VolumeDim; ++d) {\n          bc_dt_v_minus->get(a, b) +=\n              (incoming_null_one_form.get(a) * projection_Ab.get(c, b) *\n                   outgoing_null_vector.get(d) +\n               incoming_null_one_form.get(b) * projection_Ab.get(c, a) *\n                   outgoing_null_vector.get(d) -\n               (incoming_null_one_form.get(a) * outgoing_null_one_form.get(b) *\n                    incoming_null_vector.get(c) * outgoing_null_vector.get(d) +\n                incoming_null_one_form.get(b) * outgoing_null_one_form.get(a) *\n                    incoming_null_vector.get(c) * outgoing_null_vector.get(d) +\n                incoming_null_one_form.get(a) * incoming_null_one_form.get(b) *\n                    outgoing_null_vector.get(c) *\n                    outgoing_null_vector.get(d))) *\n              inertial_radius_or_scalar_factor *\n              char_projected_rhs_dt_v_psi.get(c, d);\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid add_constraint_dependent_terms_to_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const std::array<DataType, 4>& char_speeds) {\n  constexpr double mu = 0.;  // hard-coded value from SpEC Bbh input file Mu = 0\n  const double one_by_sqrt_2 = 1. / sqrt(2.);\n\n  // Add corrections c.f. Eq (64) of gr-qc/0512093\n  for (size_t a = 0; a <= VolumeDim; ++a) {\n    for (size_t b = a; b <= VolumeDim; ++b) {\n      for (size_t c = 0; c <= VolumeDim; ++c) {\n        for (size_t d = 0; d <= VolumeDim; ++d) {\n          bc_dt_v_minus->get(a, b) +=\n              0.5 *\n              (2. * incoming_null_vector.get(c) * incoming_null_vector.get(d) *\n                   outgoing_null_one_form.get(a) *\n                   outgoing_null_one_form.get(b) -\n               incoming_null_vector.get(c) * projection_Ab.get(d, a) *\n                   outgoing_null_one_form.get(b) -\n               incoming_null_vector.get(c) * projection_Ab.get(d, b) *\n                   outgoing_null_one_form.get(a) -\n               incoming_null_vector.get(d) * projection_Ab.get(c, a) *\n                   outgoing_null_one_form.get(b) -\n               incoming_null_vector.get(d) * projection_Ab.get(c, b) *\n                   outgoing_null_one_form.get(a) +\n               projection_AB.get(c, d) * projection_ab.get(a, b)) *\n              char_projected_rhs_dt_v_minus.get(c, d);\n        }\n      }\n      if constexpr (mu == 0.) {\n        for (size_t c = 0; c <= VolumeDim; ++c) {\n          bc_dt_v_minus->get(a, b) +=\n              one_by_sqrt_2 * char_speeds[3] *\n              constraint_char_zero_minus.get(c) *\n              (outgoing_null_one_form.get(a) * outgoing_null_one_form.get(b) *\n                   incoming_null_vector.get(c) +\n               projection_ab.get(a, b) * outgoing_null_vector.get(c) -\n               projection_Ab.get(c, b) * outgoing_null_one_form.get(a) -\n               projection_Ab.get(c, a) * outgoing_null_one_form.get(b));\n        }\n      } else {\n        for (size_t c = 0; c <= VolumeDim; ++c) {\n          bc_dt_v_minus->get(a, b) +=\n              one_by_sqrt_2 * char_speeds[3] *\n              (constraint_char_zero_minus.get(c) -\n               mu * constraint_char_zero_plus.get(c)) *\n              (outgoing_null_one_form.get(a) * outgoing_null_one_form.get(b) *\n                   incoming_null_vector.get(c) +\n               projection_ab.get(a, b) * outgoing_null_vector.get(c) -\n               projection_Ab.get(c, b) * outgoing_null_one_form.get(a) -\n               projection_Ab.get(c, a) * outgoing_null_one_form.get(b));\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid add_physical_terms_to_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const Scalar<DataType>& gamma2,\n    const tnsr::i<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::II<DataType, VolumeDim, Frame::Inertial>&\n        inverse_spatial_metric,\n    const tnsr::ii<DataType, VolumeDim, Frame::Inertial>& extrinsic_curvature,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& spacetime_metric,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>&\n        inverse_spacetime_metric,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>&\n        three_index_constraint,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>& phi,\n    const tnsr::ijaa<DataType, VolumeDim, Frame::Inertial>& d_phi,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>& d_pi,\n    const std::array<DataType, 4>& char_speeds, const double time,\n    const MathFunction<1, Frame::Inertial>* const incoming_wave_profile) {\n  // hard-coded value from SpEC Bbh input file Mu = MuPhys = 0\n  constexpr double mu_phys = 0.;\n  constexpr bool adjust_phys_using_c4 = true;\n  constexpr bool gamma2_in_phys = true;\n\n  // In what follows, we follow Kidder, Scheel & Teukolsky (2001)\n  // https://arxiv.org/pdf/gr-qc/0105031.pdf.\n  TempBuffer<tmpl::list<::Tags::Tempaa<0, VolumeDim, Frame::Inertial, DataType>,\n                        ::Tags::Tempaa<1, VolumeDim, Frame::Inertial, DataType>,\n                        ::Tags::Tempaa<2, VolumeDim, Frame::Inertial, DataType>,\n                        ::Tags::TempScalar<0, DataType>>>\n      u3_buffer(get_size(get<0>(unit_interface_normal_vector)), 0.);\n  auto& U3p =\n      get<::Tags::Tempaa<0, VolumeDim, Frame::Inertial, DataType>>(u3_buffer);\n  auto& U3m =\n      get<::Tags::Tempaa<1, VolumeDim, Frame::Inertial, DataType>>(u3_buffer);\n  auto& injected_wave =\n      get<::Tags::Tempaa<2, VolumeDim, Frame::Inertial, DataType>>(u3_buffer);\n\n  {\n    TempBuffer<\n        tmpl::list<::Tags::Tempijj<0, VolumeDim, Frame::Inertial, DataType>,\n                   // cov deriv of Kij\n                   ::Tags::Tempijj<1, VolumeDim, Frame::Inertial, DataType>,\n                   // spatial Ricci\n                   ::Tags::Tempii<0, VolumeDim, Frame::Inertial, DataType>,\n                   // spatial projection operators P_ij, P^ij, and P^i_j\n                   ::Tags::TempII<0, VolumeDim, Frame::Inertial, DataType>,\n                   ::Tags::Tempii<1, VolumeDim, Frame::Inertial, DataType>,\n                   ::Tags::TempIj<0, VolumeDim, Frame::Inertial, DataType>,\n                   // weyl propagating modes\n                   ::Tags::Tempii<2, VolumeDim, Frame::Inertial, DataType>,\n                   ::Tags::Tempii<3, VolumeDim, Frame::Inertial, DataType>,\n                   ::Tags::Tempii<4, VolumeDim, Frame::Inertial, DataType>>>\n        local_buffer(get_size(get<0>(unit_interface_normal_vector)), 0.);\n\n    auto& spatial_phi =\n        get<::Tags::Tempijj<0, VolumeDim, Frame::Inertial, DataType>>(\n            local_buffer);\n    auto& cov_deriv_ex_curv =\n        get<::Tags::Tempijj<1, VolumeDim, Frame::Inertial, DataType>>(\n            local_buffer);\n    auto& ricci_3 =\n        get<::Tags::Tempii<0, VolumeDim, Frame::Inertial, DataType>>(\n            local_buffer);\n    auto& spatial_projection_IJ =\n        get<::Tags::TempII<0, VolumeDim, Frame::Inertial, DataType>>(\n            local_buffer);\n    auto& spatial_projection_ij =\n        get<::Tags::Tempii<1, VolumeDim, Frame::Inertial, DataType>>(\n            local_buffer);\n    auto& spatial_projection_Ij =\n        get<::Tags::TempIj<0, VolumeDim, Frame::Inertial, DataType>>(\n            local_buffer);\n    auto weyl_prop_minus =\n        get<::Tags::Tempii<2, VolumeDim, Frame::Inertial, DataType>>(\n            local_buffer);\n    auto& spatial_metric =\n        get<::Tags::Tempii<3, VolumeDim, Frame::Inertial, DataType>>(\n            local_buffer);\n\n    // D_(k,i,j) = (1/2) \\partial_k g_(ij) and its derivative\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      for (size_t j = i; j < VolumeDim; ++j) {\n        for (size_t k = 0; k < VolumeDim; ++k) {\n          spatial_phi.get(k, i, j) = phi.get(k, i + 1, j + 1);\n        }\n      }\n    }\n\n    // Compute covariant deriv of extrinsic curvature\n    gh::covariant_deriv_of_extrinsic_curvature(\n        make_not_null(&cov_deriv_ex_curv), extrinsic_curvature,\n        spacetime_unit_normal_vector,\n        raise_or_lower_first_index(gr::christoffel_first_kind(spatial_phi),\n                                   inverse_spatial_metric),\n        inverse_spacetime_metric, phi, d_pi, d_phi);\n\n    // Compute spatial Ricci tensor\n    gh::spatial_ricci_tensor(make_not_null(&ricci_3), phi, d_phi,\n                             inverse_spatial_metric);\n\n    if (adjust_phys_using_c4) {\n      // This adds 4-index constraint terms to 3Ricci so as to cancel\n      // out normal derivatives from the final expression for U8.\n      // It is much easier to add them here than to recalculate U8\n      // from scratch.\n\n      // Add some 4-index constraint terms to 3Ricci.\n      for (size_t i = 0; i < VolumeDim; ++i) {\n        for (size_t j = i; j < VolumeDim; ++j) {\n          for (size_t k = 0; k < VolumeDim; ++k) {\n            for (size_t l = 0; l < VolumeDim; ++l) {\n              ricci_3.get(i, j) += 0.25 * inverse_spatial_metric.get(k, l) *\n                                   (d_phi.get(i, k, 1 + l, 1 + j) -\n                                    d_phi.get(k, i, 1 + l, 1 + j) +\n                                    d_phi.get(j, k, 1 + l, 1 + i) -\n                                    d_phi.get(k, j, 1 + l, 1 + i));\n            }\n          }\n        }\n      }\n\n      // Add more 4-index constraint terms to 3Ricci\n      // These compensate for some of the cov_deriv_ex_curv terms.\n      for (size_t i = 0; i < VolumeDim; ++i) {\n        for (size_t j = i; j < VolumeDim; ++j) {\n          for (size_t a = 0; a <= VolumeDim; ++a) {\n            for (size_t k = 0; k < VolumeDim; ++k) {\n              ricci_3.get(i, j) +=\n                  0.5 * unit_interface_normal_vector.get(k) *\n                  spacetime_unit_normal_vector.get(a) *\n                  (d_phi.get(i, k, j + 1, a) - d_phi.get(k, i, j + 1, a) +\n                   d_phi.get(j, k, i + 1, a) - d_phi.get(k, j, i + 1, a));\n            }\n          }\n        }\n      }\n    }\n\n    // Make spatial projection operators\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      for (size_t k = j; k < VolumeDim; ++k) {\n        spatial_metric.get(j, k) = spacetime_metric.get(1 + j, 1 + k);\n      }\n    }\n    gr::transverse_projection_operator(make_not_null(&spatial_projection_IJ),\n                                       inverse_spatial_metric,\n                                       unit_interface_normal_vector);\n    gr::transverse_projection_operator(make_not_null(&spatial_projection_ij),\n                                       spatial_metric,\n                                       unit_interface_normal_one_form);\n    gr::transverse_projection_operator(make_not_null(&spatial_projection_Ij),\n                                       unit_interface_normal_vector,\n                                       unit_interface_normal_one_form);\n\n    // Weyl propagating mode\n    gr::weyl_propagating(make_not_null(&weyl_prop_minus), ricci_3,\n                         extrinsic_curvature, inverse_spatial_metric,\n                         cov_deriv_ex_curv, unit_interface_normal_vector,\n                         spatial_projection_IJ, spatial_projection_ij,\n                         spatial_projection_Ij, -1);\n\n    if constexpr (mu_phys == 0.) {\n      // No need to compute U3p or weyl_prop_plus in this case\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = a; b <= VolumeDim; ++b) {\n          for (size_t i = 0; i < VolumeDim; ++i) {\n            for (size_t j = 0; j < VolumeDim; ++j) {\n              U3m.get(a, b) += 2. * projection_Ab.get(i + 1, a) *\n                               projection_Ab.get(j + 1, b) *\n                               weyl_prop_minus.get(i, j);\n            }\n          }\n        }\n      }\n    } else {\n      auto& weyl_prop_plus =\n          get<::Tags::Tempii<3, VolumeDim, Frame::Inertial, DataType>>(\n              local_buffer);\n      gr::weyl_propagating(make_not_null(&weyl_prop_plus), ricci_3,\n                           extrinsic_curvature, inverse_spatial_metric,\n                           cov_deriv_ex_curv, unit_interface_normal_vector,\n                           spatial_projection_IJ, spatial_projection_ij,\n                           spatial_projection_Ij, 1);\n\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = a; b <= VolumeDim; ++b) {\n          for (size_t i = 0; i < VolumeDim; ++i) {\n            for (size_t j = 0; j < VolumeDim; ++j) {\n              U3p.get(a, b) += 2. * projection_Ab.get(i + 1, a) *\n                               projection_Ab.get(j + 1, b) *\n                               weyl_prop_plus.get(i, j);\n              U3m.get(a, b) += 2. * projection_Ab.get(i + 1, a) *\n                               projection_Ab.get(j + 1, b) *\n                               weyl_prop_minus.get(i, j);\n            }\n          }\n        }\n      }\n    }\n  }\n\n  if (incoming_wave_profile != nullptr) {\n    if constexpr (VolumeDim == 3) {\n      const double injected_wave_profile_value = (*incoming_wave_profile)(time);\n      injected_wave.get(1, 1) = injected_wave_profile_value;\n      injected_wave.get(2, 2) = injected_wave_profile_value;\n      injected_wave.get(3, 3) = -2. * injected_wave_profile_value;\n    } else {\n      ERROR(\"IncomingWaveProfile can only be used in 3 spatial dimensions.\");\n    }\n  }\n\n  // Add physical boundary corrections\n  if (gamma2_in_phys) {\n    auto& normal_dot_three_index_constraint_gamma2 =\n        get(get<::Tags::TempScalar<0, DataType>>(u3_buffer));\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = a; b <= VolumeDim; ++b) {\n        for (size_t c = 0; c <= VolumeDim; ++c) {\n          for (size_t d = 0; d <= VolumeDim; ++d) {\n            normal_dot_three_index_constraint_gamma2 =\n                get<0>(unit_interface_normal_vector) *\n                three_index_constraint.get(0, c, d);\n            for (size_t i = 1; i < VolumeDim; ++i) {\n              normal_dot_three_index_constraint_gamma2 +=\n                  unit_interface_normal_vector.get(i) *\n                  three_index_constraint.get(i, c, d);\n            }\n            normal_dot_three_index_constraint_gamma2 *= get(gamma2);\n\n            if constexpr (mu_phys == 0.) {\n              bc_dt_v_minus->get(a, b) +=\n                  (projection_Ab.get(c, a) * projection_Ab.get(d, b) -\n                   0.5 * projection_ab.get(a, b) * projection_AB.get(c, d)) *\n                  (char_projected_rhs_dt_v_minus.get(c, d) +\n                   char_speeds[3] * (U3m.get(c, d) - injected_wave.get(c, d) -\n                                     normal_dot_three_index_constraint_gamma2));\n            } else {\n              bc_dt_v_minus->get(a, b) +=\n                  (projection_Ab.get(c, a) * projection_Ab.get(d, b) -\n                   0.5 * projection_ab.get(a, b) * projection_AB.get(c, d)) *\n                  (char_projected_rhs_dt_v_minus.get(c, d) +\n                   char_speeds[3] * (U3m.get(c, d) -\n                                     normal_dot_three_index_constraint_gamma2 -\n                                     mu_phys * U3p.get(c, d)));\n            }\n          }\n        }\n      }\n    }\n  } else {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = a; b <= VolumeDim; ++b) {\n        for (size_t c = 0; c <= VolumeDim; ++c) {\n          for (size_t d = 0; d <= VolumeDim; ++d) {\n            if constexpr (mu_phys == 0.) {\n              bc_dt_v_minus->get(a, b) +=\n                  (projection_Ab.get(c, a) * projection_Ab.get(d, b) -\n                   0.5 * projection_ab.get(a, b) * projection_AB.get(c, d)) *\n                  (char_projected_rhs_dt_v_minus.get(c, d) +\n                   char_speeds[3] * (U3m.get(c, d)));\n            } else {\n              bc_dt_v_minus->get(a, b) +=\n                  (projection_Ab.get(c, a) * projection_Ab.get(d, b) -\n                   0.5 * projection_ab.get(a, b) * projection_AB.get(c, d)) *\n                  (char_projected_rhs_dt_v_minus.get(c, d) +\n                   char_speeds[3] * (U3m.get(c, d) - mu_phys * U3p.get(c, d)));\n            }\n          }\n        }\n      }\n    }\n  }\n}\n}  // namespace detail\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_corrections_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const std::array<DataType, 4>& char_speeds) {\n  for (size_t a = 0; a <= VolumeDim; ++a) {\n    for (size_t b = a; b <= VolumeDim; ++b) {\n      bc_dt_v_minus->get(a, b) = -char_projected_rhs_dt_v_minus.get(a, b);\n    }\n  }\n  detail::add_constraint_dependent_terms_to_dt_v_minus(\n      bc_dt_v_minus, outgoing_null_one_form, incoming_null_vector,\n      outgoing_null_vector, projection_ab, projection_Ab, projection_AB,\n      constraint_char_zero_plus, constraint_char_zero_minus,\n      char_projected_rhs_dt_v_minus, char_speeds);\n}\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_gauge_corrections_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const Scalar<DataType>& gamma2,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>& inertial_coords,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& incoming_null_one_form,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_psi,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const std::array<DataType, 4>& char_speeds) {\n  for (size_t a = 0; a <= VolumeDim; ++a) {\n    for (size_t b = a; b <= VolumeDim; ++b) {\n      bc_dt_v_minus->get(a, b) = -char_projected_rhs_dt_v_minus.get(a, b);\n    }\n  }\n  detail::add_constraint_dependent_terms_to_dt_v_minus(\n      bc_dt_v_minus, outgoing_null_one_form, incoming_null_vector,\n      outgoing_null_vector, projection_ab, projection_Ab, projection_AB,\n      constraint_char_zero_plus, constraint_char_zero_minus,\n      char_projected_rhs_dt_v_minus, char_speeds);\n  detail::add_gauge_sommerfeld_terms_to_dt_v_minus(\n      bc_dt_v_minus, gamma2, inertial_coords, incoming_null_one_form,\n      outgoing_null_one_form, incoming_null_vector, outgoing_null_vector,\n      projection_Ab, char_projected_rhs_dt_v_psi);\n}\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_gauge_physical_corrections_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const Scalar<DataType>& gamma2,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>& inertial_coords,\n    const double time,\n    const tnsr::i<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& incoming_null_one_form,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::II<DataType, VolumeDim, Frame::Inertial>&\n        inverse_spatial_metric,\n    const tnsr::ii<DataType, VolumeDim, Frame::Inertial>& extrinsic_curvature,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& spacetime_metric,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>&\n        inverse_spacetime_metric,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>&\n        three_index_constraint,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_psi,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>& phi,\n    const tnsr::ijaa<DataType, VolumeDim, Frame::Inertial>& d_phi,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>& d_pi,\n    const std::array<DataType, 4>& char_speeds,\n    const MathFunction<1, Frame::Inertial>* const incoming_wave_profile) {\n  for (size_t a = 0; a <= VolumeDim; ++a) {\n    for (size_t b = a; b <= VolumeDim; ++b) {\n      bc_dt_v_minus->get(a, b) = -char_projected_rhs_dt_v_minus.get(a, b);\n    }\n  }\n  detail::add_constraint_dependent_terms_to_dt_v_minus(\n      bc_dt_v_minus, outgoing_null_one_form, incoming_null_vector,\n      outgoing_null_vector, projection_ab, projection_Ab, projection_AB,\n      constraint_char_zero_plus, constraint_char_zero_minus,\n      char_projected_rhs_dt_v_minus, char_speeds);\n  detail::add_physical_terms_to_dt_v_minus(\n      bc_dt_v_minus, gamma2, unit_interface_normal_one_form,\n      unit_interface_normal_vector, spacetime_unit_normal_vector, projection_ab,\n      projection_Ab, projection_AB, inverse_spatial_metric, extrinsic_curvature,\n      spacetime_metric, inverse_spacetime_metric, three_index_constraint,\n      char_projected_rhs_dt_v_minus, phi, d_phi, d_pi, char_speeds, time,\n      incoming_wave_profile);\n  detail::add_gauge_sommerfeld_terms_to_dt_v_minus(\n      bc_dt_v_minus, gamma2, inertial_coords, incoming_null_one_form,\n      outgoing_null_one_form, incoming_null_vector, outgoing_null_vector,\n      projection_Ab, char_projected_rhs_dt_v_psi);\n}\n}  // namespace gh::BoundaryConditions::Bjorhus\n\n// Explicit Instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void                                                                \\\n  gh::BoundaryConditions::Bjorhus::constraint_preserving_corrections_dt_v_psi( \\\n      const gsl::not_null<tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>*>  \\\n          bc_dt_v_psi,                                                         \\\n      const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>&                  \\\n          unit_interface_normal_vector,                                        \\\n      const tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>&                \\\n          three_index_constraint,                                              \\\n      const std::array<DTYPE(data), 4>& char_speeds);                          \\\n  template void gh::BoundaryConditions::Bjorhus::                              \\\n      constraint_preserving_corrections_dt_v_zero(                             \\\n          const gsl::not_null<                                                 \\\n              tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>*>             \\\n              bc_dt_v_zero,                                                    \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              unit_interface_normal_vector,                                    \\\n          const tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>&            \\\n              four_index_constraint,                                           \\\n          const std::array<DTYPE(data), 4>& char_speeds);                      \\\n  template void gh::BoundaryConditions::Bjorhus::detail::                      \\\n      add_gauge_sommerfeld_terms_to_dt_v_minus(                                \\\n          const gsl::not_null<                                                 \\\n              tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>*>              \\\n              bc_dt_v_minus,                                                   \\\n          const Scalar<DTYPE(data)>& gamma2,                                   \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              inertial_coords,                                                 \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              incoming_null_one_form,                                          \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_one_form,                                          \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              incoming_null_vector,                                            \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_vector,                                            \\\n          const tnsr::Ab<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_Ab,                                                   \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              char_projected_rhs_dt_v_psi);                                    \\\n  template void gh::BoundaryConditions::Bjorhus::detail::                      \\\n      add_constraint_dependent_terms_to_dt_v_minus(                            \\\n          const gsl::not_null<                                                 \\\n              tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>*>              \\\n              bc_dt_v_minus,                                                   \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_one_form,                                          \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              incoming_null_vector,                                            \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_vector,                                            \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_ab,                                                   \\\n          const tnsr::Ab<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_Ab,                                                   \\\n          const tnsr::AA<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_AB,                                                   \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              constraint_char_zero_plus,                                       \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              constraint_char_zero_minus,                                      \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              char_projected_rhs_dt_v_minus,                                   \\\n          const std::array<DTYPE(data), 4>& char_speeds);                      \\\n  template void                                                                \\\n  gh::BoundaryConditions::Bjorhus::detail::add_physical_terms_to_dt_v_minus(   \\\n      const gsl::not_null<tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>*>  \\\n          bc_dt_v_minus,                                                       \\\n      const Scalar<DTYPE(data)>& gamma2,                                       \\\n      const tnsr::i<DTYPE(data), DIM(data), Frame::Inertial>&                  \\\n          unit_interface_normal_one_form,                                      \\\n      const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>&                  \\\n          unit_interface_normal_vector,                                        \\\n      const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&                  \\\n          spacetime_unit_normal_vector,                                        \\\n      const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>& projection_ab,  \\\n      const tnsr::Ab<DTYPE(data), DIM(data), Frame::Inertial>& projection_Ab,  \\\n      const tnsr::AA<DTYPE(data), DIM(data), Frame::Inertial>& projection_AB,  \\\n      const tnsr::II<DTYPE(data), DIM(data), Frame::Inertial>&                 \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::ii<DTYPE(data), DIM(data), Frame::Inertial>&                 \\\n          extrinsic_curvature,                                                 \\\n      const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&                 \\\n          spacetime_metric,                                                    \\\n      const tnsr::AA<DTYPE(data), DIM(data), Frame::Inertial>&                 \\\n          inverse_spacetime_metric,                                            \\\n      const tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>&                \\\n          three_index_constraint,                                              \\\n      const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&                 \\\n          char_projected_rhs_dt_v_minus,                                       \\\n      const tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>& phi,           \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), Frame::Inertial>& d_phi,        \\\n      const tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>& d_pi,          \\\n      const std::array<DTYPE(data), 4>& char_speeds, const double time,        \\\n      const MathFunction<1, Frame::Inertial>* incoming_wave_profile);          \\\n  template void gh::BoundaryConditions::Bjorhus::                              \\\n      constraint_preserving_corrections_dt_v_minus(                            \\\n          const gsl::not_null<                                                 \\\n              tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>*>              \\\n              bc_dt_v_minus,                                                   \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_one_form,                                          \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              incoming_null_vector,                                            \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_vector,                                            \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_ab,                                                   \\\n          const tnsr::Ab<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_Ab,                                                   \\\n          const tnsr::AA<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_AB,                                                   \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              char_projected_rhs_dt_v_minus,                                   \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              constraint_char_zero_plus,                                       \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              constraint_char_zero_minus,                                      \\\n          const std::array<DTYPE(data), 4>& char_speeds);                      \\\n  template void gh::BoundaryConditions::Bjorhus::                              \\\n      constraint_preserving_gauge_corrections_dt_v_minus(                      \\\n          const gsl::not_null<                                                 \\\n              tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>*>              \\\n              bc_dt_v_minus,                                                   \\\n          const Scalar<DTYPE(data)>& gamma2,                                   \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              inertial_coords,                                                 \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              incoming_null_one_form,                                          \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_one_form,                                          \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              incoming_null_vector,                                            \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_vector,                                            \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_ab,                                                   \\\n          const tnsr::Ab<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_Ab,                                                   \\\n          const tnsr::AA<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_AB,                                                   \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              char_projected_rhs_dt_v_psi,                                     \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              char_projected_rhs_dt_v_minus,                                   \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              constraint_char_zero_plus,                                       \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              constraint_char_zero_minus,                                      \\\n          const std::array<DTYPE(data), 4>& char_speeds);                      \\\n  template void gh::BoundaryConditions::Bjorhus::                              \\\n      constraint_preserving_gauge_physical_corrections_dt_v_minus(             \\\n          const gsl::not_null<                                                 \\\n              tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>*>              \\\n              bc_dt_v_minus,                                                   \\\n          const Scalar<DTYPE(data)>& gamma2,                                   \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              inertial_coords,                                                 \\\n          const double time,                                                   \\\n          const tnsr::i<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              unit_interface_normal_one_form,                                  \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              unit_interface_normal_vector,                                    \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              spacetime_unit_normal_vector,                                    \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              incoming_null_one_form,                                          \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_one_form,                                          \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              incoming_null_vector,                                            \\\n          const tnsr::A<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              outgoing_null_vector,                                            \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_ab,                                                   \\\n          const tnsr::Ab<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_Ab,                                                   \\\n          const tnsr::AA<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              projection_AB,                                                   \\\n          const tnsr::II<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              inverse_spatial_metric,                                          \\\n          const tnsr::ii<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              extrinsic_curvature,                                             \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              spacetime_metric,                                                \\\n          const tnsr::AA<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              inverse_spacetime_metric,                                        \\\n          const tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>&            \\\n              three_index_constraint,                                          \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              char_projected_rhs_dt_v_psi,                                     \\\n          const tnsr::aa<DTYPE(data), DIM(data), Frame::Inertial>&             \\\n              char_projected_rhs_dt_v_minus,                                   \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              constraint_char_zero_plus,                                       \\\n          const tnsr::a<DTYPE(data), DIM(data), Frame::Inertial>&              \\\n              constraint_char_zero_minus,                                      \\\n          const tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>& phi,       \\\n          const tnsr::ijaa<DTYPE(data), DIM(data), Frame::Inertial>& d_phi,    \\\n          const tnsr::iaa<DTYPE(data), DIM(data), Frame::Inertial>& d_pi,      \\\n          const std::array<DTYPE(data), 4>& char_speeds,                       \\\n          const MathFunction<1, Frame::Inertial>* incoming_wave_profile);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BjorhusImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gh::BoundaryConditions {\n/// \\brief Detailed implementation of Bjorhus-type boundary corrections\nnamespace Bjorhus {\n/*!\n * \\brief Computes the expression needed to set boundary conditions on the time\n * derivative of the characteristic field \\f$v^{g}_{ab}\\f$\n *\n * \\details In the Bjorhus scheme, the time derivatives of evolved variables are\n * characteristic projected. A constraint-preserving correction term is added\n * here to the resulting characteristic (time-derivative) field:\n *\n * \\f{align}\n * \\Delta \\partial_t v^{g}_{ab} = \\lambda_{g} n^i C_{iab}\n * \\f}\n *\n * where \\f$n^i\\f$ is the local unit normal to the external boundary,\n * \\f$C_{iab} = \\partial_i g_{ab} - \\Phi_{iab}\\f$ is the three-index\n * constraint, and \\f$\\lambda_{g}\\f$ is the characteristic speed of the field\n * \\f$v^{g}_{ab}\\f$.\n */\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_corrections_dt_v_psi(\n    gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*> bc_dt_v_psi,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>&\n        three_index_constraint,\n    const std::array<DataType, 4>& char_speeds);\n\n/*!\n * \\brief Computes the expression needed to set boundary conditions on the time\n * derivative of the characteristic field \\f$v^{0}_{iab}\\f$\n *\n * \\details In the Bjorhus scheme, the time derivatives of evolved variables are\n * characteristic projected. A constraint-preserving correction term is added\n * here to the resulting characteristic (time-derivative) field \\f$v^0_{iab}\\f$:\n *\n * \\f{align}\n * \\Delta \\partial_t v^{0}_{iab} = \\lambda_{0} n^j C_{jiab}\n * \\f}\n *\n * where \\f$n^i\\f$ is the local unit normal to the external boundary,\n * \\f$C_{ijab} = \\partial_i\\Phi_{jab} - \\partial_j\\Phi_{iab}\\f$ is the\n * four-index constraint, and \\f$\\lambda_{0}\\f$ is the characteristic speed of\n * the field \\f$v^0_{iab}\\f$.\n *\n * \\note In 3D, only the non-zero and unique components of the four-index\n * constraint are stored, as \\f$\\hat{C}_{iab} = \\epsilon_i^{jk} C_{jkab}\\f$. In\n * 2D the input expected here is \\f$\\hat{C}_{0ab} = C_{01ab}, \\hat{C}_{1ab} =\n * C_{10ab}\\f$, and in 1D \\f$\\hat{C}_{0ab} = 0\\f$.\n */\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_corrections_dt_v_zero(\n    gsl::not_null<tnsr::iaa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_zero,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>&\n        four_index_constraint,\n    const std::array<DataType, 4>& char_speeds);\n\n/// @{\n/*!\n * \\brief Computes the expression needed to set boundary conditions on the time\n * derivative of the characteristic field \\f$v^{-}_{ab}\\f$\n *\n * \\details In the Bjorhus scheme, the time derivatives of evolved variables are\n * characteristic projected. Constraint-preserving correction terms\n * \\f$T^{\\mathrm C}_{ab}\\f$, Sommerfeld condition terms for gauge degrees of\n * freedom \\f$T^{\\mathrm G}_{ab}\\f$, and terms constraining physical degrees of\n * freedom \\f$T^{\\mathrm P}_{ab}\\f$ are added here to the resulting\n * characteristic (time-derivative) field:\n *\n * \\f{align}\n * \\Delta \\partial_t v^{-}_{ab} = T^{\\mathrm C}_{ab} + T^{\\mathrm G}_{ab}\n *                              + T^{\\mathrm P}_{ab}.\n * \\f}\n *\n * These terms are given by Eq. (64) of \\cite Lindblom2005qh :\n *\n * \\f{eqnarray}\n * T^{\\mathrm C}_{ab} &=& \\frac{1}{2}\n *      \\left(2 k^c k^d l_a l_b - k^c l_b P^d_a - k^c l_a P^d_b - k^d l_b P^c_a\n *            - k^d l_a P^c_b + P^{cd} P_{ab}\\right) \\partial_t v^{-}_{cd}\\\\\n *     &&+ \\frac{1}{\\sqrt{2}} \\lambda_{-}c^{\\hat{0}-}_c \\left(l_a l_b k^c +\n *          P_{ab} l^c - P^c_b l_a - P^c_a l_b \\right) \\nonumber\n * \\f}\n *\n * where \\f$l^a (l_a)\\f$ is the outgoing null vector (one-form), \\f$k^a (k_a)\\f$\n * is the incoming null vector (one-form), \\f$P_{ab}, P^{ab}, P^a_b\\f$ are\n * spacetime projection operators defined in `transverse_projection_operator()`,\n * \\f$\\partial_t v^{-}_{ab}\\f$ is the characteristic projected time derivative\n * of evolved variables (corresponding to the \\f$v^{-}\\f$ field), and\n * \\f$c^{\\hat{0}\\pm}_a\\f$ are characteristic modes of the constraint evolution\n * system:\n *\n * \\f{align}\\nonumber\n * c^{\\hat{0}\\pm}_a = F_a \\mp n^k C_{ka},\n * \\f}\n *\n * where \\f$F_a\\f$ is the generalized-harmonic (GH) F constraint [Eq. (43) of\n * \\cite Lindblom2005qh], \\f$C_{ka}\\f$ is the GH 2-index constraint [Eq. (44)\n * of \\cite Lindblom2005qh], and \\f$n^k\\f$ is the unit spatial normal to the\n * outer boundary. Boundary correction terms that prevent strong reflections of\n * gauge perturbations are given by Eq. (25) of \\cite Rinne2007ui :\n *\n * \\f{align}\n * T^{\\mathrm G}_{ab} =\n *     \\left(k_a P^c_b l^d + k_b P^c_a l^d -\n *          \\left(k_a l_b k^c l^d + k_b l_a k_c l^d + k_a k_b l^c l^d\n *          \\right) \\right) \\left(\\gamma_2 - \\frac{1}{r}\n *                          \\right) \\partial_t v^{g}_{cd}\n * \\f}\n *\n * where \\f$r\\f$ is the radial coordinate at the outer boundary, which is\n * assumed to be spherical, \\f$\\gamma_2\\f$ is a GH constraint damping parameter,\n * and \\f$\\partial_t v^{g}_{ab}\\f$ is the characteristic projected time\n * derivative of evolved variables (corresponding to the \\f$v^{g}\\f$ field).\n * Finally, we constrain physical degrees of freedom using corrections from\n * Eq. (68) of \\cite Lindblom2005qh :\n *\n * \\f{align}\n * T^{\\mathrm P}_{ab} = \\left( P^c_a P^d_b - \\frac{1}{2} P_{ab} P^{cd} \\right)\n *     \\left(\\partial_t v^{-}_{cd} + \\lambda_{-}\n *           \\left(U^{3-}_{cd} - \\gamma_2 n^i C_{icd}\\right)\\right),\n * \\f}\n *\n * where \\f$C_{icd}\\f$ is the GH 3-index constraint [c.f. Eq. (26) of\n * \\cite Lindblom2005qh, see also `three_index_constraint()`], and\n *\n * \\f{align}\\nonumber\n * U^{3-}_{ab} = 2 P^i_a P^j_b U^{8-}_{ij}\n * \\f}\n *\n * is the inward propagating characteristic mode of the Weyl tensor evolution\n * \\f$U^{8-}_{ab}\\f$ [c.f. Eq. 75 of \\cite Kidder2004rw ] projected onto the\n * outer boundary using the spatial-spacetime projection operators \\f$P^i_a\\f$.\n * Note that (A) the covariant derivative of extrinsic curvature needed to\n * get the Weyl propagating modes is calculated substituting the evolved\n * variable \\f$\\Phi_{iab}\\f$ for spatial derivatives of the spacetime metric;\n * and (B) the spatial Ricci tensor used in the same calculation is also\n * calculated using the same substituion, and also includes corrections\n * proportional to the GH 4-index constraint \\f$C_{ijab}\\f$ [c.f. Eq. (45) of\n * \\cite Lindblom2005qh , see also `four_index_constraint()`]:\n *\n * \\f{align}\\nonumber\n * R_{ij} \\rightarrow R_{ij} + C_{(iklj)} + \\frac{1}{2} n^k q^a C_{(ikj)a},\n * \\f}\n *\n * where \\f$q^a\\f$ is the future-directed spacetime normal vector.\n */\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_corrections_dt_v_minus(\n    gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const std::array<DataType, 4>& char_speeds);\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_gauge_corrections_dt_v_minus(\n    gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const Scalar<DataType>& gamma2,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>& inertial_coords,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& incoming_null_one_form,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_psi,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const std::array<DataType, 4>& char_speeds);\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid constraint_preserving_gauge_physical_corrections_dt_v_minus(\n    gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const Scalar<DataType>& gamma2,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>& inertial_coords,\n    double time,\n    const tnsr::i<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& incoming_null_one_form,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::II<DataType, VolumeDim, Frame::Inertial>&\n        inverse_spatial_metric,\n    const tnsr::ii<DataType, VolumeDim, Frame::Inertial>& extrinsic_curvature,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& spacetime_metric,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>&\n        inverse_spacetime_metric,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>&\n        three_index_constraint,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_psi,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>& phi,\n    const tnsr::ijaa<DataType, VolumeDim, Frame::Inertial>& d_phi,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>& d_pi,\n    const std::array<DataType, 4>& char_speeds,\n    const MathFunction<1, Frame::Inertial>* incoming_wave_profile = nullptr);\n/// @}\n\nnamespace detail {\ntemplate <size_t VolumeDim, typename DataType>\nvoid add_gauge_sommerfeld_terms_to_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const Scalar<DataType>& gamma2,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>& inertial_coords,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& incoming_null_one_form,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_psi);\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid add_constraint_dependent_terms_to_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>& outgoing_null_one_form,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataType, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const std::array<DataType, 4>& char_speeds);\n\ntemplate <size_t VolumeDim, typename DataType>\nvoid add_physical_terms_to_dt_v_minus(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame::Inertial>*>\n        bc_dt_v_minus,\n    const Scalar<DataType>& gamma2,\n    const tnsr::i<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::A<DataType, VolumeDim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataType, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::II<DataType, VolumeDim, Frame::Inertial>&\n        inverse_spatial_metric,\n    const tnsr::ii<DataType, VolumeDim, Frame::Inertial>& extrinsic_curvature,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>& spacetime_metric,\n    const tnsr::AA<DataType, VolumeDim, Frame::Inertial>&\n        inverse_spacetime_metric,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>&\n        three_index_constraint,\n    const tnsr::aa<DataType, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>& phi,\n    const tnsr::ijaa<DataType, VolumeDim, Frame::Inertial>& d_phi,\n    const tnsr::iaa<DataType, VolumeDim, Frame::Inertial>& d_pi,\n    const std::array<DataType, 4>& char_speeds,\n    double time = std::numeric_limits<double>::signaling_NaN(),\n    const MathFunction<1, Frame::Inertial>* incoming_wave_profile = nullptr);\n}  // namespace detail\n}  // namespace Bjorhus\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gh::BoundaryConditions {\ntemplate <size_t Dim>\nBoundaryCondition<Dim>::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\ntemplate <size_t Dim>\nvoid BoundaryCondition<Dim>::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class BoundaryCondition<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace gh {\n/// \\brief Boundary conditions for the generalized harmonic system\nnamespace BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\ntemplate <size_t Dim>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace BoundaryConditions\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Bjorhus.cpp\n  BjorhusImpl.cpp\n  BoundaryCondition.cpp\n  DemandOutgoingCharSpeeds.cpp\n  DirichletAnalytic.cpp\n  DirichletMinkowski.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Bjorhus.hpp\n  BjorhusImpl.hpp\n  BoundaryCondition.hpp\n  DemandOutgoingCharSpeeds.hpp\n  DirichletAnalytic.hpp\n  DirichletMinkowski.hpp\n  Factory.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::BoundaryConditions {\ntemplate <size_t Dim>\nDemandOutgoingCharSpeeds<Dim>::DemandOutgoingCharSpeeds(\n    CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDemandOutgoingCharSpeeds<Dim>::get_clone() const {\n  return std::make_unique<DemandOutgoingCharSpeeds>(*this);\n}\n\ntemplate <size_t Dim>\nvoid DemandOutgoingCharSpeeds<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string>\nDemandOutgoingCharSpeeds<Dim>::dg_demand_outgoing_char_speeds(\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>&\n        outward_directed_normal_covector,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>&\n    /*outward_directed_normal_vector*/,\n\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift) {\n  const auto char_speeds = characteristic_speeds(\n      gamma_1, lapse, shift, outward_directed_normal_covector,\n      face_mesh_velocity);\n  double min_speed = std::numeric_limits<double>::signaling_NaN();\n  for (size_t i = 0; i < char_speeds.size(); ++i) {\n    min_speed = min(gsl::at(char_speeds, i));\n    if (min_speed < 0.0) {\n      return {MakeString{}\n              << \"DemandOutgoingCharSpeeds boundary condition violated with \"\n                 \"speed index \"\n              << i << \" ingoing: \" << min_speed\n              << \"\\n speed: \" << gsl::at(char_speeds, i)\n              << \"\\nn_i: \" << outward_directed_normal_covector\n              << \"\\n\"\n                 \"See gh::characteristic_speeds for the \"\n                 \"index ordering of characteristic speeds\\n\"};\n    }\n  }\n  return std::nullopt;\n}\n\n// NOLINTNEXTLINE\ntemplate <size_t Dim>\nPUP::able::PUP_ID DemandOutgoingCharSpeeds<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class DemandOutgoingCharSpeeds<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DemandOutgoingCharSpeeds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::BoundaryConditions {\n/// A `BoundaryCondition` that only verifies that all characteristic speeds are\n/// directed out of the domain; no boundary data is altered by this boundary\n/// condition.\ntemplate <size_t Dim>\nclass DemandOutgoingCharSpeeds final : public BoundaryCondition<Dim> {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A boundary condition that only verifies the characteristic speeds are \"\n      \"all directed out of the domain.\"};\n\n  DemandOutgoingCharSpeeds() = default;\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<::gh::Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim>>;\n  using dg_gridless_tags = tmpl::list<>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n      /*outward_directed_normal_vector*/,\n\n      const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& shift);\n};\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gh::BoundaryConditions {\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(const DirichletAnalytic& rhs)\n    : BoundaryCondition<Dim>{dynamic_cast<const BoundaryCondition<Dim>&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>& DirichletAnalytic<Dim>::operator=(\n    const DirichletAnalytic& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic<Dim>::get_clone() const {\n  return std::make_unique<DirichletAnalytic>(*this);\n}\n\ntemplate <size_t Dim>\nvoid DirichletAnalytic<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n  p | analytic_prescription_;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string> DirichletAnalytic<Dim>::dg_ghost(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n        inv_spatial_metric,\n    const std::optional<\n        tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& /*normal_vector*/,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const Scalar<DataVector>& interior_gamma1,\n    const Scalar<DataVector>& interior_gamma2, const double time) const {\n  *gamma1 = interior_gamma1;\n  *gamma2 = interior_gamma2;\n  // Use .get() to avoid Clang-13 bug: with -Og in C++20 mode, the compiler\n  // rewrites unique_ptr != nullptr via std::operator== but fails to emit the\n  // out-of-line instantiation, causing a linker error.\n  ASSERT(analytic_prescription_.get() != nullptr,\n         \"The analytic prescription must be set.\");\n  using evolved_vars_tags = typename System<Dim>::variables_tag::tags_list;\n  auto boundary_values = call_with_dynamic_type<\n      tuples::tagged_tuple_from_typelist<evolved_vars_tags>,\n      gh::Solutions::all_solutions<Dim>>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const analytic_solution_or_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*analytic_solution_or_data)>>) {\n          return analytic_solution_or_data->variables(coords, time,\n                                                      evolved_vars_tags{});\n\n        } else {\n          (void)time;\n          return analytic_solution_or_data->variables(coords,\n                                                      evolved_vars_tags{});\n        }\n      });\n\n  *spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(boundary_values);\n  *pi = get<gh::Tags::Pi<DataVector, Dim>>(boundary_values);\n  *phi = get<gh::Tags::Phi<DataVector, Dim>>(boundary_values);\n\n  // Now compute lapse and shift...\n  lapse_shift_and_inv_spatial_metric(lapse, shift, inv_spatial_metric,\n                                     *spacetime_metric);\n  return {};\n}\n\ntemplate <size_t Dim>\nvoid DirichletAnalytic<Dim>::lapse_shift_and_inv_spatial_metric(\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n        inv_spatial_metric,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric) const {\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  determinant_and_inverse(lapse, inv_spatial_metric, spatial_metric);\n  gr::shift(shift, spacetime_metric, *inv_spatial_metric);\n  gr::lapse(lapse, *shift, spacetime_metric);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class DirichletAnalytic<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace gh::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\ntemplate <size_t Dim>\nclass DirichletAnalytic final : public BoundaryCondition<Dim> {\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n\n  using options = tmpl::list<AnalyticPrescription>;\n\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions setting the value of the \"\n      \"spacetime metric and its derivatives Phi and Pi to the analytic \"\n      \"solution or analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 ::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*> phi,\n      gsl::not_null<Scalar<DataVector>*> gamma1,\n      gsl::not_null<Scalar<DataVector>*> gamma2,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> shift,\n      gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n          inv_spatial_metric,\n      const std::optional<\n          tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& /*normal_vector*/,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const Scalar<DataVector>& interior_gamma1,\n      const Scalar<DataVector>& interior_gamma2, double time) const;\n\n private:\n  void lapse_shift_and_inv_spatial_metric(\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> shift,\n      gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n          inv_spatial_metric,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric) const;\n\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletMinkowski.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletMinkowski.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gh::BoundaryConditions {\ntemplate <size_t Dim>\nDirichletMinkowski<Dim>::DirichletMinkowski(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletMinkowski<Dim>::get_clone() const {\n  return std::make_unique<DirichletMinkowski>(*this);\n}\n\ntemplate <size_t Dim>\nvoid DirichletMinkowski<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string> DirichletMinkowski<Dim>::dg_ghost(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n        inv_spatial_metric,\n    const std::optional<\n        tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& /*normal_vector*/,\n    const Scalar<DataVector>& interior_gamma1,\n    const Scalar<DataVector>& interior_gamma2) const {\n  *gamma1 = interior_gamma1;\n  *gamma2 = interior_gamma2;\n\n  // Hard-code Minkowski values to avoid passing coords or time as arguments\n  *spacetime_metric =\n      make_with_value<tnsr::aa<DataVector, Dim, Frame::Inertial>>(\n          interior_gamma1, 0.0);\n  get<0, 0>(*spacetime_metric) = -1.0;\n  for (size_t i = 1; i < Dim + 1; ++i) {\n    (*spacetime_metric).get(i, i) = 1.0;\n  }\n  *pi = make_with_value<tnsr::aa<DataVector, Dim, Frame::Inertial>>(\n      interior_gamma1, 0.0);\n  *phi = make_with_value<tnsr::iaa<DataVector, Dim, Frame::Inertial>>(\n      interior_gamma1, 0.0);\n\n  *lapse = make_with_value<Scalar<DataVector>>(interior_gamma1, 1.0);\n  *shift = make_with_value<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n      interior_gamma1, 0.0);\n  *inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, Dim, Frame::Inertial>>(\n          interior_gamma1, 0.0);\n  for (size_t i = 0; i < Dim; ++i) {\n    (*inv_spatial_metric).get(i, i) = 1.0;\n  }\n\n  return {};\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletMinkowski<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class DirichletMinkowski<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletMinkowski.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace gh::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions to a Minkowski spacetime.\n */\ntemplate <size_t Dim>\nclass DirichletMinkowski final : public BoundaryCondition<Dim> {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"DirichletMinkowski boundary conditions setting the value of the \"\n      \"spacetime metric and its derivatives Phi and Pi to Minkowski (i.e., \"\n      \"flat spacetime).\"};\n\n  DirichletMinkowski() = default;\n  DirichletMinkowski(DirichletMinkowski&&) = default;\n  DirichletMinkowski& operator=(DirichletMinkowski&&) = default;\n  DirichletMinkowski(const DirichletMinkowski&) = default;\n  DirichletMinkowski& operator=(const DirichletMinkowski&) = default;\n  ~DirichletMinkowski() override = default;\n\n  explicit DirichletMinkowski(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletMinkowski);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          spacetime_metric,\n      const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*> pi,\n      const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*> phi,\n      const gsl::not_null<Scalar<DataVector>*> gamma1,\n      const gsl::not_null<Scalar<DataVector>*> gamma2,\n      const gsl::not_null<Scalar<DataVector>*> lapse,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> shift,\n      const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n          inv_spatial_metric,\n      const std::optional<\n          tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& /*normal_vector*/,\n      const Scalar<DataVector>& interior_gamma1,\n      const Scalar<DataVector>& interior_gamma2) const;\n};\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletMinkowski.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\ntemplate <size_t Dim>\nusing standard_boundary_conditions =\n    tmpl::list<ConstraintPreservingBjorhus<Dim>, DemandOutgoingCharSpeeds<Dim>,\n               DirichletAnalytic<Dim>, DirichletMinkowski<Dim>,\n               domain::BoundaryConditions::Periodic<BoundaryCondition<Dim>>>;\n}  // namespace gh::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/AveragedUpwindPenalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/AveragedUpwindPenalty.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::BoundaryCorrections {\ntemplate <size_t Dim>\nAveragedUpwindPenalty<Dim>::AveragedUpwindPenalty(CkMigrateMessage* msg)\n    : BoundaryCorrection(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::BoundaryCorrection>\nAveragedUpwindPenalty<Dim>::get_clone() const {\n  return std::make_unique<AveragedUpwindPenalty>(*this);\n}\n\ntemplate <size_t Dim>\nvoid AveragedUpwindPenalty<Dim>::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n}\n\ntemplate <size_t Dim>\ndouble AveragedUpwindPenalty<Dim>::dg_package_data(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        packaged_spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        packaged_pi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        packaged_phi,\n    const gsl::not_null<Scalar<DataVector>*> packaged_constraint_gamma1,\n    const gsl::not_null<Scalar<DataVector>*> packaged_constraint_gamma2,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        packaged_normal,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        packaged_mesh_velocity,\n\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n\n    const Scalar<DataVector>& constraint_gamma1,\n    const Scalar<DataVector>& constraint_gamma2,\n    const Scalar<DataVector>& /*lapse*/,\n    const tnsr::I<DataVector, Dim>& /*shift*/,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& /*normal_vector*/,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    const std::optional<Scalar<DataVector>>& /*normal_dot_mesh_velocity*/)\n    const {\n  *packaged_spacetime_metric = spacetime_metric;\n  *packaged_pi = pi;\n  *packaged_phi = phi;\n  *packaged_constraint_gamma1 = constraint_gamma1;\n  *packaged_constraint_gamma2 = constraint_gamma2;\n  *packaged_normal = normal_covector;\n  if (mesh_velocity.has_value()) {\n    *packaged_mesh_velocity = *mesh_velocity;\n  } else {\n    for (auto& component : *packaged_mesh_velocity) {\n      component = 0.0;\n    }\n  }\n\n  // This is supposed to return the max characteristic speed, but the\n  // result is unused, so we don't do all the additional work required\n  // just to throw it away.  We don't return NaN because the product\n  // corrections for the combined systems return the max of the\n  // results of each of the individual systems' corrections.  The max\n  // double should be sufficiently obviously wrong that if anyone\n  // actually tries to use it it will fail completely.\n  return std::numeric_limits<double>::max();\n}\n\ntemplate <size_t Dim>\nvoid AveragedUpwindPenalty<Dim>::dg_boundary_terms(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_pi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_phi,\n\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric_int,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi_int,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi_int,\n    const Scalar<DataVector>& constraint_gamma1_int,\n    const Scalar<DataVector>& constraint_gamma2_int,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_int,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& mesh_velocity_int,\n\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric_ext,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi_ext,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi_ext,\n    const Scalar<DataVector>& constraint_gamma1_ext,\n    const Scalar<DataVector>& constraint_gamma2_ext,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_ext,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& mesh_velocity_ext,\n\n    const dg::Formulation dg_formulation) const {\n  if (dg_formulation != dg::Formulation::StrongInertial) {\n    ERROR_NO_TRACE(\"AveragedUpwindPenalty only coded for StrongInertial form\");\n  }\n\n  const size_t num_pts = get<0, 0>(spacetime_metric_int).size();\n  Variables<tmpl::list<\n      ::Tags::Tempaa<0, Dim>, ::Tags::Tempii<1, Dim>, ::Tags::TempII<2, Dim>,\n      ::Tags::TempI<3, Dim>, ::Tags::TempScalar<4>, ::Tags::Tempi<5, Dim>,\n      ::Tags::TempScalar<6>, ::Tags::TempScalar<7>, ::Tags::TempScalar<8>,\n      ::Tags::TempScalar<9>, ::Tags::TempScalar<10>, ::Tags::Tempaa<11, Dim>>>\n      buffer(num_pts);\n\n  // Naming: Interior stuff is *_int, exterior stuff is *_ext, *_jump\n  // is _ext - _int.  Other things are averages of the function\n  // arguments or things computed from such averages.\n  auto& spacetime_metric = get<::Tags::Tempaa<0, Dim>>(buffer);\n  tenex::evaluate<ti::a, ti::b>(make_not_null(&spacetime_metric),\n                                0.5 * (spacetime_metric_int(ti::a, ti::b) +\n                                       spacetime_metric_ext(ti::a, ti::b)));\n  auto& spatial_metric = get<::Tags::Tempii<1, Dim>>(buffer);\n  gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n  auto& inverse_spatial_metric = get<::Tags::TempII<2, Dim>>(buffer);\n  {\n    auto& determinant = get<::Tags::TempScalar<4>>(buffer);\n    determinant_and_inverse(make_not_null(&determinant),\n                            make_not_null(&inverse_spatial_metric),\n                            spatial_metric);\n  }\n  auto& shift = get<::Tags::TempI<3, Dim>>(buffer);\n  gr::shift(make_not_null(&shift), spacetime_metric, inverse_spatial_metric);\n  auto& lapse = get<::Tags::TempScalar<4>>(buffer);\n  gr::lapse(make_not_null(&lapse), shift, spacetime_metric);\n  auto& normal_covector = get<::Tags::Tempi<5, Dim>>(buffer);\n  // Sign flip to make neighbor value outgoing for us.\n  tenex::evaluate<ti::i>(make_not_null(&normal_covector),\n                         0.5 * (normal_int(ti::i) - normal_ext(ti::i)));\n  {\n    auto& inverse_magnitude = get<::Tags::TempScalar<6>>(buffer);\n    tenex::evaluate(make_not_null(&inverse_magnitude),\n                    1.0 / sqrt(normal_covector(ti::i) *\n                               inverse_spatial_metric(ti::I, ti::J) *\n                               normal_covector(ti::j)));\n    tenex::update<ti::i>(make_not_null(&normal_covector),\n                         inverse_magnitude() * normal_covector(ti::i));\n  }\n  auto& constraint_gamma2 = get<::Tags::TempScalar<6>>(buffer);\n  get(constraint_gamma2) =\n      0.5 * (get(constraint_gamma2_int) + get(constraint_gamma2_ext));\n\n  auto& incoming_char_speed_0 = get<::Tags::TempScalar<7>>(buffer);\n  tenex::evaluate(make_not_null(&incoming_char_speed_0),\n                  -normal_covector(ti::i) *\n                      (shift(ti::I) + 0.5 * (mesh_velocity_int(ti::I) +\n                                             mesh_velocity_ext(ti::I))));\n  auto& incoming_char_speed_plus = get<::Tags::TempScalar<8>>(buffer);\n  get(incoming_char_speed_plus) = get(incoming_char_speed_0) + get(lapse);\n  auto& incoming_char_speed_minus = get<::Tags::TempScalar<9>>(buffer);\n  get(incoming_char_speed_minus) = get(incoming_char_speed_0) - get(lapse);\n\n  // Zero out the outgoing (average) speeds\n  get(incoming_char_speed_0) *= step_function(-get(incoming_char_speed_0));\n  get(incoming_char_speed_plus) *=\n      step_function(-get(incoming_char_speed_plus));\n  get(incoming_char_speed_minus) *=\n      step_function(-get(incoming_char_speed_minus));\n\n  auto& incoming_char_speed_g = get<::Tags::TempScalar<10>>(buffer);\n  get(incoming_char_speed_g) =\n      (1.0 + 0.5 * (get(constraint_gamma1_int) + get(constraint_gamma1_ext))) *\n      get(incoming_char_speed_0);\n\n  // lapse no longer needed, but scoping it to here would be hard, so\n  // just have to be careful.\n  auto& incoming_char_speed_sym = lapse;\n  get(incoming_char_speed_sym) =\n      0.5 * (get(incoming_char_speed_plus) + get(incoming_char_speed_minus));\n  // _plus no longer needed except for computing _antisym, but again,\n  // just have to be careful.\n  auto& incoming_char_speed_antisym = incoming_char_speed_plus;\n  get(incoming_char_speed_antisym) -= get(incoming_char_speed_minus);\n  get(incoming_char_speed_antisym) *= 0.5;\n\n  // The correction to the spatial_metric is proportional to the jump,\n  // so store that here for convenience.\n  auto& spacetime_metric_jump = *boundary_correction_spacetime_metric;\n  tenex::evaluate<ti::a, ti::b>(\n      make_not_null(&spacetime_metric_jump),\n      spacetime_metric_ext(ti::a, ti::b) - spacetime_metric_int(ti::a, ti::b));\n\n  auto& pi_jump = get<::Tags::Tempaa<11, Dim>>(buffer);\n  tenex::evaluate<ti::a, ti::b>(make_not_null(&pi_jump),\n                                pi_ext(ti::a, ti::b) - pi_int(ti::a, ti::b));\n\n  // boundary_correction_phi holds phi_jump, briefly\n  tenex::evaluate<ti::i, ti::a, ti::b>(\n      boundary_correction_phi,\n      phi_ext(ti::i, ti::a, ti::b) - phi_int(ti::i, ti::a, ti::b));\n  // spacetime_metric no longer needed, but again, just have to be\n  // careful.\n  auto& normal_phi_jump = spacetime_metric;\n  tenex::evaluate<ti::a, ti::b>(\n      make_not_null(&normal_phi_jump),\n      normal_covector(ti::i) * inverse_spatial_metric(ti::I, ti::J) *\n          (*boundary_correction_phi)(ti::j, ti::a, ti::b));\n\n  tenex::update<ti::i, ti::a, ti::b>(\n      boundary_correction_phi,\n      incoming_char_speed_0() *\n              (*boundary_correction_phi)(ti::i, ti::a, ti::b) +\n          normal_covector(ti::i) *\n              ((incoming_char_speed_sym() - incoming_char_speed_0()) *\n                   normal_phi_jump(ti::a, ti::b) +\n               incoming_char_speed_antisym() *\n                   (pi_jump(ti::a, ti::b) -\n                    constraint_gamma2() *\n                        spacetime_metric_jump(ti::a, ti::b))));\n\n  tenex::evaluate<ti::a, ti::b>(\n      boundary_correction_pi,\n      (incoming_char_speed_g() - incoming_char_speed_sym()) *\n              constraint_gamma2() * spacetime_metric_jump(ti::a, ti::b) +\n          incoming_char_speed_sym() * pi_jump(ti::a, ti::b) +\n          incoming_char_speed_antisym() * normal_phi_jump(ti::a, ti::b));\n\n  // Recall that boundary_correction_spacetime_metric already holds the jump\n  tenex::update<ti::a, ti::b>(\n      boundary_correction_spacetime_metric,\n      incoming_char_speed_g() *\n          (*boundary_correction_spacetime_metric)(ti::a, ti::b));\n}\n\ntemplate <size_t Dim>\nbool operator==(const AveragedUpwindPenalty<Dim>& /*lhs*/,\n                const AveragedUpwindPenalty<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const AveragedUpwindPenalty<Dim>& lhs,\n                const AveragedUpwindPenalty<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID AveragedUpwindPenalty<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                                               \\\n  template class AveragedUpwindPenalty<DIM(data)>;                           \\\n  template bool operator==(const AveragedUpwindPenalty<DIM(data)>& /*lhs*/,  \\\n                           const AveragedUpwindPenalty<DIM(data)>& /*rhs*/); \\\n  template bool operator!=(const AveragedUpwindPenalty<DIM(data)>& /*lhs*/,  \\\n                           const AveragedUpwindPenalty<DIM(data)>& /*rhs*/);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace gh::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/AveragedUpwindPenalty.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace gh::BoundaryCorrections {\n/*!\n * \\brief Computes the generalized harmonic upwind multipenalty boundary\n * correction using the averaged fields across the interface.\n *\n * The correction is given by\n *\n * \\f{equation}{\n *   D_\\beta =\n *   T^{-1}_{\\beta\\hat{\\beta}}(u^{\\text{avg}})\n *   \\Lambda^-_{\\hat{\\beta}\\hat{\\alpha}}(u^{\\text{avg}})\n *   T_{\\hat{\\alpha}\\alpha}(u^{\\text{avg}})\n *   \\Delta u_\\alpha,\n * \\f}\n *\n * where $u_\\alpha$ are the evolved fields, $\\Lambda^-$ is the\n * diagonal matrix of incoming characteristic speeds,\n * $T_{\\hat{\\alpha}\\alpha}$ transforms from evolved to characteristic\n * fields, and $\\Delta u_\\alpha = u_\\alpha^{\\text{ext}} -\n * u_\\alpha^{\\text{int}}$ is the discontinuity in the evolved fields.\n * All factors except the boundary discontinuity are calculated using\n * the averaged of the internal and external evolved fields:\n *\n * \\f{equation}\n *   u^{\\text{avg}} = \\frac{u^{\\text{int}} + u^{\\text{ext}}}{2}.\n * \\f}\n *\n * For the first-order generalized harmonic system the correction is:\n *\n * \\f{align}{\n *   D_{g_{ab}} &= \\lambda_{v^g}^- \\Delta g_{ab} \\\\\n *   D_{\\Pi_{ab}}\n *     &= \\left(\\lambda_{v^g}^-\n *              - \\frac{\\lambda_{v^+}^- + \\lambda_{v^-}^-}{2}\\right)\n *       \\gamma_2 \\Delta g_{ab}\n *     + \\frac{\\lambda_{v^+}^- + \\lambda_{v^-}^-}{2} \\Delta \\Pi_{ab}\n *     + \\frac{\\lambda_{v^+}^- - \\lambda_{v^-}^-}{2} n^j \\Delta \\Phi_{jab} \\\\\n *   D_{\\Phi_{iab}}\n *     &= \\lambda_{v^0}^- (\\delta_i^j - n_i n^j) \\Delta \\Phi_{jab}\n *     + \\frac{\\lambda_{v^+}^- - \\lambda_{v^-}^-}{2} n_i\n *       (\\Delta \\Pi_{ab} - \\gamma_2 \\Delta g_{ab})\n *     + \\frac{\\lambda_{v^+}^- + \\lambda_{v^-}^-}{2} n_i n^j \\Delta \\Phi_{jab}.\n * \\f}\n *\n * In the above expressions, the outgoing face normal $n_i$ is\n * normalized using the average metric from the two sides.  Quantities\n * such as $\\gamma_2$ that should analytically be the same on both\n * sides of the interface are also averaged, as they can differ\n * because of truncation errors during projection.\n *\n * The characteristic speeds are given by\n *\n * \\f{align}{\n *   \\lambda_{v^g} &= - (1 + \\gamma_1) (\\beta^i + v^i_g) n_i, \\\\\n *   \\lambda_{v^0} &= - (\\beta^i + v^i_g) n_i, \\\\\n *   \\lambda_{v^\\pm} &= \\pm \\alpha - (\\beta^i + v^i_g) n_i,\n * \\f}\n *\n * where \\f$v_g^i\\f$ is the mesh velocity, with the incoming speeds\n *\n * \\f{equation}{\n *   \\lambda^-_{\\hat{\\alpha}} =\n *     \\begin{cases}\n *       \\lambda_{\\hat{\\alpha}} & \\lambda_{\\hat{\\alpha}} < 0 \\\\\n *       0 & \\text{otherwise.}\n *     \\end{cases}\n * \\f}\n */\ntemplate <size_t Dim>\nclass AveragedUpwindPenalty final : public evolution::BoundaryCorrection {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the upwind penalty boundary correction term for the \"\n      \"generalized harmonic system using the averaged fields from the two \"\n      \"sides of the interface.\"};\n\n  AveragedUpwindPenalty() = default;\n  AveragedUpwindPenalty(const AveragedUpwindPenalty&) = default;\n  AveragedUpwindPenalty& operator=(const AveragedUpwindPenalty&) = default;\n  AveragedUpwindPenalty(AveragedUpwindPenalty&&) = default;\n  AveragedUpwindPenalty& operator=(AveragedUpwindPenalty&&) = default;\n  ~AveragedUpwindPenalty() override = default;\n\n  /// \\cond\n  explicit AveragedUpwindPenalty(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(AveragedUpwindPenalty);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  struct MeshVelocity : db::SimpleTag {\n    using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n  };\n\n  using dg_package_field_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                 gh::Tags::Pi<DataVector, Dim>, gh::Tags::Phi<DataVector, Dim>,\n                 gh::Tags::ConstraintGamma1, gh::Tags::ConstraintGamma2,\n                 domain::Tags::UnnormalizedFaceNormal<Dim>, MeshVelocity>;\n  // We don't need lapse and shift, but this list must be exactly this\n  // or the boundary condition code doesn't compile.\n  using dg_package_data_temporary_tags =\n      tmpl::list<gh::Tags::ConstraintGamma1, gh::Tags::ConstraintGamma2,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>>;\n  using dg_package_data_primitive_tags = tmpl::list<>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          packaged_spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*> packaged_pi,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*> packaged_phi,\n      gsl::not_null<Scalar<DataVector>*> packaged_constraint_gamma1,\n      gsl::not_null<Scalar<DataVector>*> packaged_constraint_gamma2,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> packaged_normal,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          packaged_mesh_velocity,\n\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n\n      const Scalar<DataVector>& constraint_gamma1,\n      const Scalar<DataVector>& constraint_gamma2,\n      const Scalar<DataVector>& lapse, const tnsr::I<DataVector, Dim>& shift,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const;\n\n  void dg_boundary_terms(\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_pi,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_phi,\n\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric_int,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi_int,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi_int,\n      const Scalar<DataVector>& constraint_gamma1_int,\n      const Scalar<DataVector>& constraint_gamma2_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_int,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& mesh_velocity_int,\n\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric_ext,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi_ext,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi_ext,\n      const Scalar<DataVector>& constraint_gamma1_ext,\n      const Scalar<DataVector>& constraint_gamma2_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_ext,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& mesh_velocity_ext,\n\n      dg::Formulation dg_formulation) const;\n};\n\ntemplate <size_t Dim>\nbool operator==(const AveragedUpwindPenalty<Dim>& lhs,\n                const AveragedUpwindPenalty<Dim>& rhs);\ntemplate <size_t Dim>\nbool operator!=(const AveragedUpwindPenalty<Dim>& lhs,\n                const AveragedUpwindPenalty<Dim>& rhs);\n}  // namespace gh::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AveragedUpwindPenalty.cpp\n  UpwindPenalty.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AveragedUpwindPenalty.hpp\n  Factory.hpp\n  NamespaceDocs.hpp\n  UpwindPenalty.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/AveragedUpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::BoundaryCorrections {\ntemplate <size_t Dim>\nusing standard_boundary_corrections =\n    tmpl::list<AveragedUpwindPenalty<Dim>, UpwindPenalty<Dim>>;\n}  // namespace gh::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace gh::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::BoundaryCorrections {\ntemplate <size_t Dim>\nUpwindPenalty<Dim>::UpwindPenalty(CkMigrateMessage* msg)\n    : BoundaryCorrection(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::BoundaryCorrection> UpwindPenalty<Dim>::get_clone()\n    const {\n  return std::make_unique<UpwindPenalty>(*this);\n}\n\ntemplate <size_t Dim>\nvoid UpwindPenalty<Dim>::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n}\n\ntemplate <size_t Dim>\ndouble UpwindPenalty<Dim>::dg_package_data(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_v_spacetime_metric,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_v_zero,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_v_plus,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_v_minus,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_n_times_v_plus,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_n_times_v_minus,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_gamma2_v_spacetime_metric,\n    const gsl::not_null<tnsr::a<DataVector, 3, Frame::Inertial>*>\n        packaged_char_speeds,\n\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n\n    const Scalar<DataVector>& constraint_gamma1,\n    const Scalar<DataVector>& constraint_gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n  // Compute the char speeds without the mesh movement, then add the mesh\n  // movement. We compute the zero-speed first since it is just the normal\n  // dotted into the shift.\n  {\n    Scalar<DataVector> shift_dot_normal{};\n    get(shift_dot_normal)\n        .set_data_ref(make_not_null(&get<1>(*packaged_char_speeds)));\n    dot_product(make_not_null(&shift_dot_normal), shift, normal_covector);\n    get(shift_dot_normal) *= -1.0;\n    // the metric mode speed is the zero speed times (1 + gamma_1)\n    get<0>(*packaged_char_speeds) =\n        (1.0 + get(constraint_gamma1)) * get(shift_dot_normal);\n    // 2 = plus, 3 = minus\n    get<2>(*packaged_char_speeds) = get(lapse) + get(shift_dot_normal);\n    get<3>(*packaged_char_speeds) = -get(lapse) + get(shift_dot_normal);\n  }\n\n  if (normal_dot_mesh_velocity.has_value()) {\n    get<0>(*packaged_char_speeds) -=\n        get(*normal_dot_mesh_velocity) * (1. + get(constraint_gamma1));\n    get<1>(*packaged_char_speeds) -= get(*normal_dot_mesh_velocity);\n    get<2>(*packaged_char_speeds) -= get(*normal_dot_mesh_velocity);\n    get<3>(*packaged_char_speeds) -= get(*normal_dot_mesh_velocity);\n  }\n\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = a; b < Dim + 1; ++b) {\n      packaged_char_speed_gamma2_v_spacetime_metric->get(a, b) =\n          get(constraint_gamma2) * spacetime_metric.get(a, b);\n    }\n  }\n\n  // Computes the contribution to the boundary correction from one side of the\n  // interface.\n  //\n  // Note: when UpwindPenalty::dg_boundary_terms() is called, an Element passes\n  // in its own packaged data to fill the interior fields, and its neighbor's\n  // packaged data to fill the exterior fields. This introduces a sign flip for\n  // each normal used in computing the exterior fields.\n  // Use v_psi allocation as n^i Phi_i\n  {\n    tnsr::aa<DataVector, Dim, Frame::Inertial>& normal_dot_phi =\n        *packaged_char_speed_v_spacetime_metric;\n    for (size_t a = 0; a < Dim + 1; ++a) {\n      for (size_t b = a; b < Dim + 1; ++b) {\n        normal_dot_phi.get(a, b) = get<0>(normal_vector) * phi.get(0, a, b);\n        for (size_t i = 1; i < Dim; ++i) {\n          normal_dot_phi.get(a, b) += normal_vector.get(i) * phi.get(i, a, b);\n        }\n      }\n    }\n\n    for (size_t a = 0; a < Dim + 1; ++a) {\n      for (size_t b = a; b < Dim + 1; ++b) {\n        packaged_char_speed_v_plus->get(a, b) =\n            get<2>(*packaged_char_speeds) *\n            (pi.get(a, b) + normal_dot_phi.get(a, b) -\n             packaged_char_speed_gamma2_v_spacetime_metric->get(a, b));\n        packaged_char_speed_v_minus->get(a, b) =\n            get<3>(*packaged_char_speeds) *\n            (pi.get(a, b) - normal_dot_phi.get(a, b) -\n             packaged_char_speed_gamma2_v_spacetime_metric->get(a, b));\n\n        for (size_t i = 0; i < Dim; ++i) {\n          packaged_char_speed_v_zero->get(i, a, b) =\n              get<1>(*packaged_char_speeds) *\n              (phi.get(i, a, b) -\n               normal_covector.get(i) * normal_dot_phi.get(a, b));\n        }\n      }\n    }\n  }\n\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = a; b < Dim + 1; ++b) {\n      for (size_t d = 0; d < Dim; ++d) {\n        packaged_char_speed_n_times_v_plus->get(d, a, b) =\n            packaged_char_speed_v_plus->get(a, b) * normal_covector.get(d);\n        packaged_char_speed_n_times_v_minus->get(d, a, b) =\n            packaged_char_speed_v_minus->get(a, b) * normal_covector.get(d);\n      }\n      packaged_char_speed_v_spacetime_metric->get(a, b) =\n          get<0>(*packaged_char_speeds) * spacetime_metric.get(a, b);\n      packaged_char_speed_gamma2_v_spacetime_metric->get(a, b) *=\n          get<0>(*packaged_char_speeds);\n    }\n  }\n\n  return max(max(get<0>(*packaged_char_speeds), get<1>(*packaged_char_speeds),\n                 get<2>(*packaged_char_speeds), get<3>(*packaged_char_speeds)));\n}\n\ntemplate <size_t Dim>\nvoid UpwindPenalty<Dim>::dg_boundary_terms(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_pi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_phi,\n\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n        char_speed_v_spacetime_metric_int,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& char_speed_v_zero_int,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& char_speed_v_plus_int,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& char_speed_v_minus_int,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>&\n        char_speed_normal_times_v_plus_int,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>&\n        char_speed_normal_times_v_minus_int,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n        char_speed_constraint_gamma2_v_spacetime_metric_int,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds_int,\n\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n        char_speed_v_spacetime_metric_ext,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& char_speed_v_zero_ext,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& char_speed_v_plus_ext,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& char_speed_v_minus_ext,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>&\n        char_speed_normal_times_v_plus_ext,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>&\n        char_speed_normal_times_v_minus_ext,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n        char_speed_constraint_gamma2_v_spacetime_metric_ext,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds_ext,\n    dg::Formulation /*dg_formulation*/) const {\n  const size_t num_pts = char_speeds_int[0].size();\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>, ::Tags::TempScalar<3>,\n                       ::Tags::TempScalar<4>, ::Tags::TempScalar<5>,\n                       ::Tags::TempScalar<6>, ::Tags::TempScalar<7>>>\n      buffer(num_pts);\n  DataVector& weighted_lambda_spacetime_metric_int =\n      get(get<::Tags::TempScalar<0>>(buffer));\n  weighted_lambda_spacetime_metric_int = step_function(-char_speeds_int[0]);\n  DataVector& weighted_lambda_spacetime_metric_ext =\n      get(get<::Tags::TempScalar<1>>(buffer));\n  weighted_lambda_spacetime_metric_ext = -step_function(char_speeds_ext[0]);\n\n  DataVector& weighted_lambda_zero_int =\n      get(get<::Tags::TempScalar<2>>(buffer));\n  weighted_lambda_zero_int = step_function(-char_speeds_int[1]);\n  DataVector& weighted_lambda_zero_ext =\n      get(get<::Tags::TempScalar<3>>(buffer));\n  weighted_lambda_zero_ext = -step_function(char_speeds_ext[1]);\n\n  DataVector& weighted_lambda_plus_int =\n      get(get<::Tags::TempScalar<4>>(buffer));\n  weighted_lambda_plus_int = step_function(-char_speeds_int[2]);\n  DataVector& weighted_lambda_plus_ext =\n      get(get<::Tags::TempScalar<5>>(buffer));\n  weighted_lambda_plus_ext = -step_function(char_speeds_ext[2]);\n\n  DataVector& weighted_lambda_minus_int =\n      get(get<::Tags::TempScalar<6>>(buffer));\n  weighted_lambda_minus_int = step_function(-char_speeds_int[3]);\n  DataVector& weighted_lambda_minus_ext =\n      get(get<::Tags::TempScalar<7>>(buffer));\n  weighted_lambda_minus_ext = -step_function(char_speeds_ext[3]);\n\n  // D_spacetime_metric = Theta(-lambda_spacetime_metric^{ext})\n  // lambda_spacetime_metric^{ext} v_spacetime_metric^{ext}\n  //       - Theta(-lambda_spacetime_metric^{int}) lambda_spacetime_metric^{int}\n  //       v_spacetime_metric^{int}\n  // where the unit normals on both sides point in the same direction, out\n  // of the current element. Since lambda_spacetime_metric from the neighbor is\n  // computing with the normal vector pointing into the current element in the\n  // code, we need to swap the sign of lambda_spacetime_metric^{ext}.\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = a; b < Dim + 1; ++b) {\n      boundary_correction_spacetime_metric->get(a, b) =\n          weighted_lambda_spacetime_metric_ext *\n              char_speed_v_spacetime_metric_ext.get(a, b) -\n          weighted_lambda_spacetime_metric_int *\n              char_speed_v_spacetime_metric_int.get(a, b);\n\n      boundary_correction_pi->get(a, b) =\n          0.5 * (weighted_lambda_plus_ext * char_speed_v_plus_ext.get(a, b) +\n                 weighted_lambda_minus_ext * char_speed_v_minus_ext.get(a, b)) +\n          weighted_lambda_spacetime_metric_ext *\n              char_speed_constraint_gamma2_v_spacetime_metric_ext.get(a, b)\n\n          -\n          0.5 * (weighted_lambda_plus_int * char_speed_v_plus_int.get(a, b) +\n                 weighted_lambda_minus_int * char_speed_v_minus_int.get(a, b)) -\n          weighted_lambda_spacetime_metric_int *\n              char_speed_constraint_gamma2_v_spacetime_metric_int.get(a, b);\n\n      for (size_t d = 0; d < Dim; ++d) {\n        // Overall minus sign on ext because of normal vector is opposite\n        // direction.\n        boundary_correction_phi->get(d, a, b) =\n            -0.5 * (weighted_lambda_minus_ext *\n                        char_speed_normal_times_v_minus_ext.get(d, a, b) -\n                    weighted_lambda_plus_ext *\n                        char_speed_normal_times_v_plus_ext.get(d, a, b)) +\n            weighted_lambda_zero_ext * char_speed_v_zero_ext.get(d, a, b)\n\n            - 0.5 * (weighted_lambda_plus_int *\n                         char_speed_normal_times_v_plus_int.get(d, a, b) -\n                     weighted_lambda_minus_int *\n                         char_speed_normal_times_v_minus_int.get(d, a, b)) -\n            weighted_lambda_zero_int * char_speed_v_zero_int.get(d, a, b);\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const UpwindPenalty<Dim>& /*lhs*/,\n                const UpwindPenalty<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const UpwindPenalty<Dim>& lhs, const UpwindPenalty<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID UpwindPenalty<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                                       \\\n  template class UpwindPenalty<DIM(data)>;                           \\\n  template bool operator==(const UpwindPenalty<DIM(data)>& /*lhs*/,  \\\n                           const UpwindPenalty<DIM(data)>& /*rhs*/); \\\n  template bool operator!=(const UpwindPenalty<DIM(data)>& /*lhs*/,  \\\n                           const UpwindPenalty<DIM(data)>& /*rhs*/);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace gh::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace gh::BoundaryCorrections {\n/*!\n * \\brief Computes the generalized harmonic upwind multipenalty boundary\n * correction.\n *\n * This implements the upwind multipenalty boundary correction term\n * \\f$D_\\beta\\f$. The general form is given by:\n *\n * \\f{align*}{\n *   D_\\beta =\n *   T_{\\beta\\hat{\\beta}}^{\\mathrm{ext}}\n *   \\Lambda^{\\mathrm{ext},-}_{\\hat{\\beta}\\hat{\\alpha}}\n *   v^{\\mathrm{ext}}_{\\hat{\\alpha}}\n *   -T_{\\beta\\hat{\\beta}}^{\\mathrm{int}}\n *   \\Lambda^{\\mathrm{int},-}_{\\hat{\\beta}\\hat{\\alpha}}\n *   v^{\\mathrm{int}}_{\\hat{\\alpha}}.\n * \\f}\n *\n * We denote the evolved fields by \\f$u_{\\alpha}\\f$, the characteristic fields\n * by \\f$v_{\\hat{\\alpha}}\\f$, and implicitly sum over reapeated indices.\n * \\f$T_{\\beta\\hat{\\beta}}\\f$ transforms characteristic fields to evolved\n * fields, while \\f$\\Lambda_{\\hat{\\beta}\\hat{\\alpha}}^-\\f$ is a diagonal matrix\n * with only the negative characteristic speeds, and has zeros on the diagonal\n * for all other entries. The int and ext superscripts denote quantities on the\n * internal and external side of the mortar. Note that Eq. (6.3) of\n * \\cite Teukolsky2015ega is not exactly what's implemented since that boundary\n * term does not consistently treat both sides of the interface on the same\n * footing.\n *\n * For the first-order generalized harmonic system the correction is:\n *\n * \\f{align*}{\n *   D_{g_{ab}} &= \\lambda_{v^{g}}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},g}_{ab}\n *                - \\lambda_{v^{g}}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},g}_{ab}, \\\\\n *   D_{\\Pi_{ab}}\n *              &= \\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},+}_{ab} +\n *                \\lambda_{v^-}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},-}_{ab}\\right)\n *                + \\lambda_{v^g}^{\\mathrm{ext},-}\\gamma_2\n *                v^{\\mathrm{ext},g}_{ab}\n *                \\notag \\\\\n *              &-\\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},+}_{ab} +\n *                \\lambda_{v^-}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},-}_{ab}\\right)\n *                - \\lambda_{v^g}^{\\mathrm{int},-}\\gamma_2\n *                v^{\\mathrm{int},g}_{ab} , \\\\\n *   D_{\\Phi_{iab}}\n *              &= \\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},+}_{ab}\n *                - \\lambda_{v^-}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},-}_{ab}\\right)n_i^{\\mathrm{ext}}\n *                + \\lambda_{v^0}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},0}_{iab}\n *                \\notag \\\\\n *              &-\n *                \\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},+}_{ab}\n *                - \\lambda_{v^-}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},-}_{ab}\\right)n_i^{\\mathrm{int}}\n *                - \\lambda_{v^0}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},0}_{iab},\n * \\f}\n *\n * with characteristic fields\n *\n * \\f{align*}{\n *   v^{g}_{ab} &= g_{ab}, \\\\\n *   v^{0}_{iab} &= (\\delta^k_i-n^k n_i)\\Phi_{kab}, \\\\\n *   v^{\\pm}_{ab} &= \\Pi_{ab}\\pm n^i\\Phi_{iab} -\\gamma_2 g_{ab},\n * \\f}\n *\n * and characteristic speeds\n *\n * \\f{align*}{\n *   \\lambda_{v^g} =& -(1+\\gamma_1) (\\beta^i n_i + v^i_g n_i), \\\\\n *   \\lambda_{v^0} =& -\\beta^i n_i -v^i_g n_i, \\\\\n *   \\lambda_{v^\\pm} =& \\pm \\alpha - \\beta^i n_i - v^i_g n_i,\n * \\f}\n *\n * where \\f$v_g^i\\f$ is the mesh velocity and \\f$n_i\\f$ is the outward directed\n * unit normal covector to the interface. We have also defined\n *\n * \\f{align}{\n *   \\lambda^{\\pm}_{\\hat{\\alpha}} =\n *    \\left\\{\n *        \\begin{array}{ll}\n *          \\lambda_{\\hat{\\alpha}} &\n *            \\mathrm{if}\\;\\pm\\lambda_{\\hat{\\alpha}}> 0 \\\\\n *          0 & \\mathrm{otherwise}\n *        \\end{array}\\right.\n * \\f}\n *\n * In the implementation we store the speeds in a rank-4 tensor with the zeroth\n * component being \\f$\\lambda_{v^\\Psi}\\f$, the first being \\f$\\lambda_{v^0}\\f$,\n * the second being \\f$\\lambda_{v^+}\\f$, and the third being\n * \\f$\\lambda_{v^-}\\f$.\n *\n * Note that we have assumed \\f$n_i^{\\mathrm{ext}}\\f$ points in the same\n * direction as \\f$n_i^{\\mathrm{int}}\\f$, but in the code they point in opposite\n * directions. If \\f$n_i^{\\mathrm{ext}}\\f$ points in the opposite direction the\n * external speeds have their sign flipped and the \\f$\\pm\\f$ fields and their\n * speeds reverse roles (i.e. the \\f$v^{\\mathrm{ext},+}\\f$ field is now flowing\n * into the element, while \\f$v^{\\mathrm{ext},-}\\f$ flows out). In our\n * implementation this reversal actually cancels out, and we have the following\n * equations:\n *\n * \\f{align*}{\n *   D_{g_{ab}} &= -\\lambda_{v^{g}}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},g}_{ab}\n *                - \\lambda_{v^{g}}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},g}_{ab}, \\\\\n *   D_{\\Pi_{ab}}\n *              &= \\frac{1}{2}\\left(-\\lambda_{v^+}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},+}_{ab} -\n *                \\lambda_{v^-}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},-}_{ab}\\right)\n *                - \\lambda_{v^g}^{\\mathrm{ext},+}\\gamma_2\n *                v^{\\mathrm{ext},g}_{ab}\n *                \\notag \\\\\n *              &-\\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},+}_{ab} +\n *                \\lambda_{v^-}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},-}_{ab}\\right)\n *                - \\lambda_{v^g}^{\\mathrm{int},-}\\gamma_2\n *                v^{\\mathrm{int},g}_{ab} , \\\\\n *   D_{\\Phi_{iab}}\n *              &= \\frac{1}{2}\\left(-\\lambda_{v^+}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},+}_{ab}\n *                + \\lambda_{v^-}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},-}_{ab}\\right)n_i^{\\mathrm{ext}}\n *                - \\lambda_{v^0}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},0}_{iab}\n *                \\\\\n *              &-\n *                \\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},+}_{ab}\n *                - \\lambda_{v^-}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},-}_{ab}\\right)n_i^{\\mathrm{int}}\n *                - \\lambda_{v^0}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},0}_{iab},\n * \\f}\n */\ntemplate <size_t Dim>\nclass UpwindPenalty final : public evolution::BoundaryCorrection {\n private:\n  struct NormalTimesVPlus : db::SimpleTag {\n    using type = tnsr::iaa<DataVector, Dim, Frame::Inertial>;\n  };\n  struct NormalTimesVMinus : db::SimpleTag {\n    using type = tnsr::iaa<DataVector, Dim, Frame::Inertial>;\n  };\n  struct Gamma2VSpacetimeMetric : db::SimpleTag {\n    using type = tnsr::aa<DataVector, Dim, Frame::Inertial>;\n  };\n  struct CharSpeedsTensor : db::SimpleTag {\n    using type = tnsr::a<DataVector, 3, Frame::Inertial>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the UpwindPenalty boundary correction term for the generalized \"\n      \"harmonic system.\"};\n\n  UpwindPenalty() = default;\n  UpwindPenalty(const UpwindPenalty&) = default;\n  UpwindPenalty& operator=(const UpwindPenalty&) = default;\n  UpwindPenalty(UpwindPenalty&&) = default;\n  UpwindPenalty& operator=(UpwindPenalty&&) = default;\n  ~UpwindPenalty() override = default;\n\n  /// \\cond\n  explicit UpwindPenalty(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(UpwindPenalty);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::VSpacetimeMetric<DataVector, Dim>,\n                 Tags::VZero<DataVector, Dim>, Tags::VPlus<DataVector, Dim>,\n                 Tags::VMinus<DataVector, Dim>, NormalTimesVPlus,\n                 NormalTimesVMinus, Gamma2VSpacetimeMetric, CharSpeedsTensor>;\n  using dg_package_data_temporary_tags =\n      tmpl::list<::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>>;\n  using dg_package_data_primitive_tags = tmpl::list<>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_v_spacetime_metric,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_v_zero,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_v_plus,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_v_minus,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_n_times_v_plus,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_n_times_v_minus,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_gamma2_v_spacetime_metric,\n      gsl::not_null<tnsr::a<DataVector, 3, Frame::Inertial>*>\n          packaged_char_speeds,\n\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n\n      const Scalar<DataVector>& constraint_gamma1,\n      const Scalar<DataVector>& constraint_gamma2,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const;\n\n  void dg_boundary_terms(\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_pi,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_phi,\n\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n          char_speed_v_spacetime_metric_int,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& char_speed_v_zero_int,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& char_speed_v_plus_int,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& char_speed_v_minus_int,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>&\n          char_speed_normal_times_v_plus_int,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>&\n          char_speed_normal_times_v_minus_int,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n          char_speed_constraint_gamma2_v_spacetime_metric_int,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds_int,\n\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n          char_speed_v_spacetime_metric_ext,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& char_speed_v_zero_ext,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& char_speed_v_plus_ext,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& char_speed_v_minus_ext,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>&\n          char_speed_normal_times_v_plus_ext,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>&\n          char_speed_normal_times_v_minus_ext,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>&\n          char_speed_constraint_gamma2_v_spacetime_metric_ext,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds_ext,\n      dg::Formulation /*dg_formulation*/) const;\n};\n\ntemplate <size_t Dim>\nbool operator==(const UpwindPenalty<Dim>& lhs, const UpwindPenalty<Dim>& rhs);\ntemplate <size_t Dim>\nbool operator!=(const UpwindPenalty<Dim>& lhs, const UpwindPenalty<Dim>& rhs);\n}  // namespace gh::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GeneralizedHarmonic)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ApplyTensorYlmFilter.cpp\n  Characteristics.cpp\n  Constraints.cpp\n  Equations.cpp\n  TimeDerivative.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApplyTensorYlmFilter.hpp\n  Characteristics.hpp\n  Constraints.hpp\n  DuDtTempTags.hpp\n  Equations.hpp\n  Initialize.hpp\n  System.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  TimeDerivative.hpp\n  TimeDerivative.tpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  CoordinateMaps\n  ConstraintDamping\n  DataStructures\n  Domain\n  DomainBoundaryConditions\n  ErrorHandling\n  Events\n  Evolution\n  FunctionsOfTime\n  GeneralRelativity\n  GeneralRelativitySolutions\n  GhGrMhdAnalyticData\n  GhGrMhdSolutions\n  GhRelativisticEulerSolutions\n  IO\n  Importers\n  Initialization\n  LinearOperators\n  Options\n  Serialization\n  Spectral\n  Utilities\n  INTERFACE\n  Initialization\n  Parallel\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Bbh)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(GaugeSourceFunctions)\nadd_subdirectory(Instantiations)\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nInverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>\nnormalize_inverse_jacobian(\n    const InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, TargetFrame>& inverse_spatial_metric) {\n  // Normalizing the first index wrt the spatial metric\n  InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>\n      scaled_inv_jacobian{get_size(get<0, 0>(inverse_jacobian))};\n  DataVector magnitude{get_size(get<0, 0>(inverse_jacobian))};\n  for (size_t i_hat = 0; i_hat < Dim; ++i_hat) {\n    magnitude = 0.;\n    for (size_t j = 0; j < Dim; ++j) {\n      for (size_t k = 0; k < Dim; ++k) {\n        magnitude += inverse_jacobian.get(i_hat, j) *\n                     inverse_spatial_metric.get(j, k) *\n                     inverse_jacobian.get(i_hat, k);\n      }\n    }\n    ASSERT(min(magnitude) > 0,\n           \"Trying to normalize inverse jacobian with a negative magnitude: \"\n               << magnitude);\n    magnitude = sqrt(magnitude);\n    for (size_t j = 0; j < Dim; ++j) {\n      scaled_inv_jacobian.get(i_hat, j) =\n          inverse_jacobian.get(i_hat, j) / magnitude;\n    }\n  }\n  return scaled_inv_jacobian;\n}\n\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid v_zero_plus_minus_speed_impl(\n    const gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity,\n    const int sign) {\n  const auto normalized_inverse_jacobian =\n      normalize_inverse_jacobian(inverse_jacobian, inverse_spatial_metric);\n  for (size_t i = 0; i < Dim; ++i) {\n    (*char_speed).get(i) = static_cast<double>(sign) * get(lapse) -\n                           normalized_inverse_jacobian.get(i, 0) * shift.get(0);\n    for (size_t j = 1; j < Dim; ++j) {\n      (*char_speed).get(i) +=\n          -normalized_inverse_jacobian.get(i, j) * shift.get(j);\n    }\n  }\n  if (mesh_velocity.has_value()) {\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        (*char_speed).get(i) -=\n            normalized_inverse_jacobian.get(i, j) * (*mesh_velocity).get(j);\n      }\n    }\n  }\n}\n}  // namespace\n\nnamespace gh {\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid vspacetimemetric_speed(\n    const gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& gamma_1,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  const auto normalized_inverse_jacobian =\n      normalize_inverse_jacobian(inverse_jacobian, inverse_spatial_metric);\n  for (size_t i = 0; i < Dim; ++i) {\n    (*char_speed).get(i) = -(1. + get(gamma_1)) *\n                           normalized_inverse_jacobian.get(i, 0) * shift.get(0);\n    for (size_t j = 1; j < Dim; ++j) {\n      (*char_speed).get(i) += -(1. + get(gamma_1)) *\n                              normalized_inverse_jacobian.get(i, j) *\n                              shift.get(j);\n    }\n  }\n  if (mesh_velocity.has_value()) {\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        (*char_speed).get(i) -= (1. + get(gamma_1)) *\n                                normalized_inverse_jacobian.get(i, j) *\n                                (*mesh_velocity).get(j);\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\ntnsr::I<DataVector, Dim, SourceFrame> vspacetimemetric_speed(\n    const Scalar<DataVector>& gamma_1,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  auto char_speed =\n      make_with_value<tnsr::I<DataVector, Dim, SourceFrame>>(get<0>(shift), 0.);\n  vspacetimemetric_speed(make_not_null(&char_speed), gamma_1, shift,\n                         inverse_jacobian, inverse_spatial_metric,\n                         mesh_velocity);\n  return char_speed;\n}\n\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid vzero_speed(\n    const gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  v_zero_plus_minus_speed_impl(char_speed, lapse, shift, inverse_jacobian,\n                               inverse_spatial_metric, mesh_velocity, 0);\n}\n\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\ntnsr::I<DataVector, Dim, SourceFrame> vzero_speed(\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  auto char_speed =\n      make_with_value<tnsr::I<DataVector, Dim, SourceFrame>>(get<0>(shift), 0.);\n  vzero_speed(make_not_null(&char_speed), lapse, shift, inverse_jacobian,\n              inverse_spatial_metric, mesh_velocity);\n  return char_speed;\n}\n\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid vplus_speed(\n    const gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  v_zero_plus_minus_speed_impl(char_speed, lapse, shift, inverse_jacobian,\n                               inverse_spatial_metric, mesh_velocity, 1);\n}\n\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\ntnsr::I<DataVector, Dim, SourceFrame> vplus_speed(\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  auto char_speed =\n      make_with_value<tnsr::I<DataVector, Dim, SourceFrame>>(get<0>(shift), 0.);\n  vplus_speed(make_not_null(&char_speed), lapse, shift, inverse_jacobian,\n              inverse_spatial_metric, mesh_velocity);\n  return char_speed;\n}\n\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid vminus_speed(\n    const gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n      v_zero_plus_minus_speed_impl(char_speed, lapse, shift, inverse_jacobian,\n                                   inverse_spatial_metric, mesh_velocity, -1);\n}\n\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\ntnsr::I<DataVector, Dim, SourceFrame> vminus_speed(\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  auto char_speed =\n      make_with_value<tnsr::I<DataVector, Dim, SourceFrame>>(get<0>(shift), 0.);\n  vminus_speed(make_not_null(&char_speed), lapse, shift, inverse_jacobian,\n               inverse_spatial_metric, mesh_velocity);\n  return char_speed;\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid characteristic_speeds(\n    const gsl::not_null<std::array<DataVector, 4>*> char_speeds,\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  auto shift_dot_normal = dot_product(shift, unit_normal_one_form);\n  (*char_speeds)[0] =\n      -(1. + get(gamma_1)) * get(shift_dot_normal);  // lambda(VSpacetimeMetric)\n  (*char_speeds)[1] = -get(shift_dot_normal);        // lambda(VZero)\n  (*char_speeds)[2] = -get(shift_dot_normal) + get(lapse);  // lambda(VPlus)\n  (*char_speeds)[3] = -get(shift_dot_normal) - get(lapse);  // lambda(VMinus)\n  if (mesh_velocity.has_value()) {\n    dot_product(make_not_null(&shift_dot_normal), *mesh_velocity,\n                unit_normal_one_form);\n    (*char_speeds)[0] -= (1. + get(gamma_1)) * get(shift_dot_normal);\n    (*char_speeds)[1] -= get(shift_dot_normal);\n    (*char_speeds)[2] -= get(shift_dot_normal);\n    (*char_speeds)[3] -= get(shift_dot_normal);\n  }\n}\n\ntemplate <size_t Dim, typename Frame>\nstd::array<DataVector, 4> characteristic_speeds(\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n  auto char_speeds = make_with_value<\n      typename Tags::CharacteristicSpeeds<DataVector, Dim, Frame>::type>(\n      get(lapse), 0.);\n  characteristic_speeds(make_not_null(&char_speeds), gamma_1, lapse, shift,\n                        unit_normal_one_form, mesh_velocity);\n  return char_speeds;\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid characteristic_fields(\n    const gsl::not_null<\n        typename Tags::CharacteristicFields<DataVector, Dim, Frame>::type*>\n        char_fields,\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame>& phi,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form) {\n  const auto number_of_grid_points = get(gamma_2).size();\n  if (UNLIKELY(number_of_grid_points != char_fields->number_of_grid_points())) {\n    char_fields->initialize(number_of_grid_points);\n  }\n  auto phi_dot_normal =\n      make_with_value<tnsr::aa<DataVector, Dim, Frame>>(pi, 0.);\n  auto unit_normal_vector =\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric);\n\n  // Compute phi_dot_normal_{ab} = n^i \\Phi_{iab}\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = 0; b < a + 1; ++b) {\n      for (size_t i = 0; i < Dim; ++i) {\n        phi_dot_normal.get(a, b) +=\n            unit_normal_vector.get(i) * phi.get(i, a, b);\n      }\n    }\n  }\n\n  // Eq.(34) of Lindblom+ (2005)\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t a = 0; a < Dim + 1; ++a) {\n      for (size_t b = 0; b < a + 1; ++b) {\n        get<Tags::VZero<DataVector, Dim, Frame>>(*char_fields).get(i, a, b) =\n            phi.get(i, a, b) -\n            unit_normal_one_form.get(i) * phi_dot_normal.get(a, b);\n      }\n    }\n  }\n\n  // Eq.(32) of Lindblom+ (2005)\n  get<Tags::VSpacetimeMetric<DataVector, Dim, Frame>>(*char_fields) =\n      spacetime_metric;\n\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = 0; b < a + 1; ++b) {\n      // Eq.(33) of Lindblom+ (2005)\n      get<Tags::VPlus<DataVector, Dim, Frame>>(*char_fields).get(a, b) =\n          pi.get(a, b) + phi_dot_normal.get(a, b) -\n          get(gamma_2) * spacetime_metric.get(a, b);\n      get<Tags::VMinus<DataVector, Dim, Frame>>(*char_fields).get(a, b) =\n          pi.get(a, b) - phi_dot_normal.get(a, b) -\n          get(gamma_2) * spacetime_metric.get(a, b);\n    }\n  }\n}\n\ntemplate <size_t Dim, typename Frame>\ntypename Tags::CharacteristicFields<DataVector, Dim, Frame>::type\ncharacteristic_fields(\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame>& phi,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form) {\n  auto char_fields = make_with_value<\n      typename Tags::CharacteristicFields<DataVector, Dim, Frame>::type>(\n      get(gamma_2), 0.);\n  characteristic_fields(make_not_null(&char_fields), gamma_2,\n                        inverse_spatial_metric, spacetime_metric, pi, phi,\n                        unit_normal_one_form);\n  return char_fields;\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid evolved_fields_from_characteristic_fields(\n    const gsl::not_null<typename Tags::EvolvedFieldsFromCharacteristicFields<\n        DataVector, Dim, Frame>::type*>\n        evolved_fields,\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::aa<DataVector, Dim, Frame>& u_psi,\n    const tnsr::iaa<DataVector, Dim, Frame>& u_zero,\n    const tnsr::aa<DataVector, Dim, Frame>& u_plus,\n    const tnsr::aa<DataVector, Dim, Frame>& u_minus,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form) {\n  const auto number_of_grid_points = get(gamma_2).size();\n  if (UNLIKELY(number_of_grid_points !=\n               evolved_fields->number_of_grid_points())) {\n    evolved_fields->initialize(number_of_grid_points);\n  }\n  // Invert Eq.(32) of Lindblom+ (2005) for Psi\n  get<::gr::Tags::SpacetimeMetric<DataVector, Dim, Frame>>(*evolved_fields) =\n      u_psi;\n\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = 0; b < a + 1; ++b) {\n      // Invert Eq.(32) - (34) of Lindblom+ (2005) for Pi and Phi\n      get<Tags::Pi<DataVector, Dim, Frame>>(*evolved_fields).get(a, b) =\n          0.5 * (u_plus.get(a, b) + u_minus.get(a, b)) +\n          get(gamma_2) * u_psi.get(a, b);\n      for (size_t i = 0; i < Dim; ++i) {\n        get<Tags::Phi<DataVector, Dim, Frame>>(*evolved_fields).get(i, a, b) =\n            0.5 * (u_plus.get(a, b) - u_minus.get(a, b)) *\n                unit_normal_one_form.get(i) +\n            u_zero.get(i, a, b);\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim, typename Frame>\ntypename Tags::EvolvedFieldsFromCharacteristicFields<DataVector, Dim,\n                                                     Frame>::type\nevolved_fields_from_characteristic_fields(\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::aa<DataVector, Dim, Frame>& u_psi,\n    const tnsr::iaa<DataVector, Dim, Frame>& u_zero,\n    const tnsr::aa<DataVector, Dim, Frame>& u_plus,\n    const tnsr::aa<DataVector, Dim, Frame>& u_minus,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form) {\n  auto evolved_fields =\n      make_with_value<typename Tags::EvolvedFieldsFromCharacteristicFields<\n          DataVector, Dim, Frame>::type>(get(gamma_2), 0.);\n  evolved_fields_from_characteristic_fields(make_not_null(&evolved_fields),\n                                            gamma_2, u_psi, u_zero, u_plus,\n                                            u_minus, unit_normal_one_form);\n  return evolved_fields;\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid Tags::ComputeLargestCharacteristicSpeed<Dim, Frame>::function(\n    const gsl::not_null<double*> speed, const Scalar<DataVector>& gamma_1,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const tnsr::ii<DataVector, Dim, Frame>& spatial_metric) {\n  const auto shift_magnitude = magnitude(shift, spatial_metric);\n  *speed = std::max(max(abs(1. + get(gamma_1)) * get(shift_magnitude)),\n                    max(get(shift_magnitude) + get(lapse)));\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(_, data)                                                 \\\n  template void gh::characteristic_speeds(                                     \\\n      const gsl::not_null<std::array<DataVector, 4>*> char_speeds,             \\\n      const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,      \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const tnsr::i<DataVector, DIM(data), FRAME(data)>& unit_normal_one_form, \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n  template std::array<DataVector, 4> gh::characteristic_speeds(                \\\n      const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,      \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const tnsr::i<DataVector, DIM(data), FRAME(data)>& unit_normal_one_form, \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n  template struct gh::CharacteristicSpeedsCompute<DIM(data), FRAME(data)>;     \\\n  template struct gh::CharacteristicSpeedsOnStrahlkorperCompute<DIM(data),     \\\n                                                                FRAME(data)>;  \\\n  template void gh::characteristic_fields(                                     \\\n      const gsl::not_null<typename gh::Tags::CharacteristicFields<             \\\n          DataVector, DIM(data), FRAME(data)>::type*>                          \\\n          char_fields,                                                         \\\n      const Scalar<DataVector>& gamma_2,                                       \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& spacetime_metric,    \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& pi,                  \\\n      const tnsr::iaa<DataVector, DIM(data), FRAME(data)>& phi,                \\\n      const tnsr::i<DataVector, DIM(data), FRAME(data)>&                       \\\n          unit_normal_one_form);                                               \\\n  template typename gh::Tags::CharacteristicFields<DataVector, DIM(data),      \\\n                                                   FRAME(data)>::type          \\\n  gh::characteristic_fields(                                                   \\\n      const Scalar<DataVector>& gamma_2,                                       \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& spacetime_metric,    \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& pi,                  \\\n      const tnsr::iaa<DataVector, DIM(data), FRAME(data)>& phi,                \\\n      const tnsr::i<DataVector, DIM(data), FRAME(data)>&                       \\\n          unit_normal_one_form);                                               \\\n  template struct gh::CharacteristicFieldsCompute<DIM(data), FRAME(data)>;     \\\n  template void gh::evolved_fields_from_characteristic_fields(                 \\\n      const gsl::not_null<                                                     \\\n          typename gh::Tags::EvolvedFieldsFromCharacteristicFields<            \\\n              DataVector, DIM(data), FRAME(data)>::type*>                      \\\n          evolved_fields,                                                      \\\n      const Scalar<DataVector>& gamma_2,                                       \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& u_psi,               \\\n      const tnsr::iaa<DataVector, DIM(data), FRAME(data)>& u_zero,             \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& u_plus,              \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& u_minus,             \\\n      const tnsr::i<DataVector, DIM(data), FRAME(data)>&                       \\\n          unit_normal_one_form);                                               \\\n  template typename gh::Tags::EvolvedFieldsFromCharacteristicFields<           \\\n      DataVector, DIM(data), FRAME(data)>::type                                \\\n  gh::evolved_fields_from_characteristic_fields(                               \\\n      const Scalar<DataVector>& gamma_2,                                       \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& u_psi,               \\\n      const tnsr::iaa<DataVector, DIM(data), FRAME(data)>& u_zero,             \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& u_plus,              \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& u_minus,             \\\n      const tnsr::i<DataVector, DIM(data), FRAME(data)>&                       \\\n          unit_normal_one_form);                                               \\\n  template struct gh::EvolvedFieldsFromCharacteristicFieldsCompute<            \\\n      DIM(data), FRAME(data)>;                                                 \\\n  template struct gh::Tags::ComputeLargestCharacteristicSpeed<DIM(data),       \\\n                                                              FRAME(data)>;    \\\n  template void gh::vspacetimemetric_speed(                                    \\\n      const gsl::not_null<                                                     \\\n          tnsr::I<DataVector, DIM(data), Frame::ElementLogical>*>              \\\n          char_speed,                                                          \\\n      const Scalar<DataVector>& gamma_1,                                       \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            FRAME(data)>& inverse_jacobian,                    \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n  template void gh::vzero_speed(                                               \\\n      const gsl::not_null<                                                     \\\n          tnsr::I<DataVector, DIM(data), Frame::ElementLogical>*>              \\\n          char_speed,                                                          \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            FRAME(data)>& inverse_jacobian,                    \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n  template void gh::vplus_speed(                                               \\\n      const gsl::not_null<                                                     \\\n          tnsr::I<DataVector, DIM(data), Frame::ElementLogical>*>              \\\n          char_speed,                                                          \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            FRAME(data)>& inverse_jacobian,                    \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n  template void gh::vminus_speed(                                              \\\n      const gsl::not_null<                                                     \\\n          tnsr::I<DataVector, DIM(data), Frame::ElementLogical>*>              \\\n          char_speed,                                                          \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            FRAME(data)>& inverse_jacobian,                    \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n                                                                               \\\n  template tnsr::I<DataVector, DIM(data), Frame::ElementLogical>               \\\n  gh::vspacetimemetric_speed(                                                  \\\n      const Scalar<DataVector>& gamma_1,                                       \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            FRAME(data)>& inverse_jacobian,                    \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n  template tnsr::I<DataVector, DIM(data), Frame::ElementLogical>               \\\n  gh::vzero_speed(                                                             \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            FRAME(data)>& inverse_jacobian,                    \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n  template tnsr::I<DataVector, DIM(data), Frame::ElementLogical>               \\\n  gh::vplus_speed(                                                             \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            FRAME(data)>& inverse_jacobian,                    \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);                                                      \\\n  template tnsr::I<DataVector, DIM(data), Frame::ElementLogical>               \\\n  gh::vminus_speed(                                                            \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            FRAME(data)>& inverse_jacobian,                    \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const std::optional<tnsr::I<DataVector, DIM(data), FRAME(data)>>&        \\\n          mesh_velocity);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3),\n                        (Frame::Inertial, Frame::Grid))\n\n#undef INSTANTIATION\n#undef DIM\n#undef FRAME\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\nnamespace Tags {\ntemplate <typename Tag>\nstruct Normalized;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gh {\n\n/// @{\n/*!\n * \\brief Computes the spacetime metric characteristic speed in `SourceFrame`.\n *\n * This function is meant to be used for observing, as it is not optimized and\n * computations that are redundant across other characteristic speed\n * functions.\n *\n * It computes\n * \\f[\n * v_g^{\\hat{\\imath}} = -(1 + \\gamma_1) \\beta^{\\hat{\\imath}}\n *                           -(1 + \\gamma_1) v^{\\hat{\\imath}},\n * \\f]\n * where \\f$\\hat{\\imath}\\f$ is in `SourceFrame`.\n *\n * \\see VSpacetimeMetricSpeed\n */\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid vspacetimemetric_speed(\n    gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& gamma_1,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\ntnsr::I<DataVector, Dim, SourceFrame> vspacetimemetric_speed(\n    const Scalar<DataVector>& gamma_1,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the zero characteristic speed in `SourceFrame`.\n *\n * This function is meant to be used for observing, as it is not optimized and\n * shares computations that are redundant across other characteristic speed\n * functions.\n *\n * It computes\n * \\f[\n * (v_0)^{\\hat{\\imath}} = -\\beta^{\\hat{\\imath}} - v^{\\hat{\\imath}},\n * \\f]\n * where \\f$\\hat{\\imath}\\f$ is in `SourceFrame`.\n *\n * \\see VZeroSpeed\n */\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid vzero_speed(\n    gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\ntnsr::I<DataVector, Dim, SourceFrame> vzero_speed(\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the plus characteristic speed in `SourceFrame`.\n *\n * This function is meant to be used for observing, as it is not optimized and\n * shares computations that are redundant across other characteristic speed\n * functions.\n *\n * It computes\n * \\f[\n * v_{+}^{\\hat{\\imath}} = \\alpha -\\beta^{\\hat{\\imath}} - v^{\\hat{\\imath}},\n * \\f]\n * where \\f$\\hat{\\imath}\\f$ is in `SourceFrame`.\n *\n * \\see VPlusSpeed\n*/\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid vplus_speed(\n    gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\ntnsr::I<DataVector, Dim, SourceFrame> vplus_speed(\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the minus characteristic speed in `SourceFrame`.\n *\n * This function is meant to be used for observing, as it is not optimized and\n * shares computations that are redundant across other characteristic speed\n * functions.\n *\n * It computes\n * \\f[\n * v_{-}^{\\hat{\\imath}} = -\\alpha -\\beta^{\\hat{\\imath}} - v^{\\hat{\\imath}},\n * \\f]\n * where \\f$\\hat{\\imath}\\f$ is in `SourceFrame`.\n *\n * \\see VMinusSpeed\n */\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nvoid vminus_speed(\n    gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*> char_speed,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\ntnsr::I<DataVector, Dim, SourceFrame> vminus_speed(\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&\n        inverse_jacobian,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Computes the spacetimemetric characteristic speed in `SourceFrame`.\n *\n * \\see vspacetimemetric_speed()\n */\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nstruct VSpacetimeMetricSpeedCompute\n    : VSpacetimeMetricSpeed<DataVector, Dim, SourceFrame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::gh::Tags::ConstraintGamma1,\n                 gr::Tags::Shift<DataVector, Dim, Frame>,\n                 domain::Tags::InverseJacobian<Dim, SourceFrame, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>,\n                 domain::Tags::MeshVelocity<Dim, Frame>>;\n\n  using return_type = tnsr::I<DataVector, Dim, SourceFrame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*>,\n      const Scalar<DataVector>&, const tnsr::I<DataVector, Dim, Frame>&,\n      const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&,\n      const tnsr::II<DataVector, Dim, Frame>&,\n      const std::optional<tnsr::I<DataVector, Dim, Frame>>&)>(\n      &vspacetimemetric_speed<Dim, Frame, SourceFrame>);\n\n  using base = VSpacetimeMetricSpeed<DataVector, Dim, SourceFrame>;\n};\n\n/*!\n * \\brief Computes the zero characteristic speed in `SourceFrame`.\n *\n * \\see vzero_speed()\n */\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nstruct VZeroSpeedCompute : VZeroSpeed<DataVector, Dim, SourceFrame>,\n                           db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim, Frame>,\n                 domain::Tags::InverseJacobian<Dim, SourceFrame, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>,\n                 domain::Tags::MeshVelocity<Dim, Frame>>;\n\n  using return_type = tnsr::I<DataVector, Dim, SourceFrame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*>,\n      const Scalar<DataVector>&, const tnsr::I<DataVector, Dim, Frame>&,\n      const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&,\n      const tnsr::II<DataVector, Dim, Frame>&,\n      const std::optional<tnsr::I<DataVector, Dim, Frame>>&)>(\n      &vzero_speed<Dim, Frame, SourceFrame>);\n\n  using base = VZeroSpeed<DataVector, Dim, SourceFrame>;\n};\n\n/*!\n * \\brief Computes the plus characteristic speed in `SourceFrame`.\n *\n * \\see vplus_speed()\n */\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nstruct VPlusSpeedCompute : VPlusSpeed<DataVector, Dim, SourceFrame>,\n                           db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim, Frame>,\n                 domain::Tags::InverseJacobian<Dim, SourceFrame, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>,\n                 domain::Tags::MeshVelocity<Dim, Frame>>;\n\n  using return_type = tnsr::I<DataVector, Dim, SourceFrame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*>,\n      const Scalar<DataVector>&, const tnsr::I<DataVector, Dim, Frame>&,\n      const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&,\n      const tnsr::II<DataVector, Dim, Frame>&,\n      const std::optional<tnsr::I<DataVector, Dim, Frame>>&)>(\n      &vplus_speed<Dim, Frame, SourceFrame>);\n\n  using base = VPlusSpeed<DataVector, Dim, SourceFrame>;\n};\n\n/*!\n * \\brief Computes the minus characteristic speed in `SourceFrame`.\n *\n * \\see vminus_speed()\n */\ntemplate <size_t Dim, typename Frame, typename SourceFrame>\nstruct VMinusSpeedCompute : VMinusSpeed<DataVector, Dim, SourceFrame>,\n                            db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim, Frame>,\n                 domain::Tags::InverseJacobian<Dim, SourceFrame, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>,\n                 domain::Tags::MeshVelocity<Dim, Frame>>;\n\n  using return_type = tnsr::I<DataVector, Dim, SourceFrame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::I<DataVector, Dim, SourceFrame>*>,\n      const Scalar<DataVector>&, const tnsr::I<DataVector, Dim, Frame>&,\n      const InverseJacobian<DataVector, Dim, SourceFrame, Frame>&,\n      const tnsr::II<DataVector, Dim, Frame>&,\n      const std::optional<tnsr::I<DataVector, Dim, Frame>>&)>(\n      &vminus_speed<Dim, Frame, SourceFrame>);\n\n  using base = VMinusSpeed<DataVector, Dim, SourceFrame>;\n};\n}  // namespace Tags\n\n/// @{\n/*!\n * \\brief Compute the characteristic speeds for the generalized harmonic system.\n *\n * Computes the speeds as described in \"A New Generalized Harmonic\n * Evolution System\" by Lindblom et. al \\cite Lindblom2005qh\n * [see text following Eq.(34)]. The characteristic fields' names used here\n * differ from this paper:\n *\n * \\f{align*}\n * \\mathrm{SpECTRE} && \\mathrm{Lindblom} \\\\\n * u^{g}_{ab} && u^\\hat{0}_{ab} \\\\\n * u^0_{iab} && u^\\hat{2}_{iab} \\\\\n * u^{\\pm}_{ab} && u^{\\hat{1}\\pm}_{ab}\n * \\f}\n *\n * The corresponding characteristic speeds \\f$v\\f$ are given in the text between\n * Eq.(34) and Eq.(35) of \\cite Lindblom2005qh, except for a constraint damping\n * term used in SpEC and SpECTRE but not published anywhere:\n *\n * \\f{align*}\n * v_{g} =& -(1 + \\gamma_1) n_k \\beta^k - (1 + \\gamma_1) n_k v^k_g \\\\\n * v_{0} =& -n_k \\beta^k - n_k v^k_g\\\\\n * v_{\\pm} =& -n_k \\beta^k \\pm \\alpha - n_k v^k_g\n * \\f}\n *\n * where \\f$\\alpha, \\beta^k\\f$ are the lapse and shift respectively,\n * \\f$\\gamma_1\\f$ is a constraint damping parameter, \\f$n_k\\f$ is the unit\n * normal to the surface, and $v^k_g$ is the (optional) mesh velocity.\n *\n * \\note The results of this function depend on the mesh velocity, but\n * do not include the system-independent grid advection terms.  The\n * included terms arise from explicit dependence on the mesh velocity\n * in the GH time derivative.\n */\ntemplate <size_t Dim, typename Frame>\nstd::array<DataVector, 4> characteristic_speeds(\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\n\ntemplate <size_t Dim, typename Frame>\nvoid characteristic_speeds(\n    gsl::not_null<std::array<DataVector, 4>*> char_speeds,\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form,\n    const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity);\n\ntemplate <size_t Dim, typename Frame>\nstruct CharacteristicSpeedsCompute\n    : Tags::CharacteristicSpeeds<DataVector, Dim, Frame>,\n      db::ComputeTag {\n  using base = Tags::CharacteristicSpeeds<DataVector, Dim, Frame>;\n  using type = typename base::type;\n  using argument_tags = tmpl::list<\n      ::gh::Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n      gr::Tags::Shift<DataVector, Dim, Frame>,\n      ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim, Frame>>,\n      domain::Tags::MeshVelocity<Dim, Frame>>;\n\n  using return_type = typename base::type;\n\n  static void function(\n      const gsl::not_null<return_type*> result,\n      const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame>& shift,\n      const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form,\n      const std::optional<tnsr::I<DataVector, Dim, Frame>>& mesh_velocity) {\n    characteristic_speeds(result, gamma_1, lapse, shift, unit_normal_one_form,\n                          mesh_velocity);\n  };\n};\n\n// Simple tag used when observing the characteristic speeds on a Strahlkorper\ntemplate <typename Frame>\nstruct CharacteristicSpeedsOnStrahlkorper : db::SimpleTag {\n  using type = tnsr::a<DataVector, 3, Frame>;\n};\n\n// Compute tag used when computing the characteristic speeds on a Strahlkorper\n// from gamma1, the lapse, shift, and the unit normal one form.\ntemplate <size_t Dim, typename Frame>\nstruct CharacteristicSpeedsOnStrahlkorperCompute\n    : CharacteristicSpeedsOnStrahlkorper<Frame>,\n      db::ComputeTag {\n  using base = CharacteristicSpeedsOnStrahlkorper<Frame>;\n  using type = typename base::type;\n  using argument_tags =\n      tmpl::list<::gh::Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim, Frame>,\n                 ::ylm::Tags::UnitNormalOneForm<Frame>>;\n\n  using return_type = typename base::type;\n\n  static void function(\n      const gsl::not_null<return_type*> result,\n      const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame>& shift,\n      const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form) {\n    auto array_char_speeds = make_array<4>(DataVector(get(lapse).size(), 0.0));\n    characteristic_speeds(make_not_null(&array_char_speeds), gamma_1, lapse,\n                          shift, unit_normal_one_form, {});\n    for (size_t i = 0; i < 4; ++i) {\n      (*result)[i] = array_char_speeds[i];\n    }\n  }\n};\n\n/// @}\n\n/// @{\n/*!\n * \\brief Computes characteristic fields from evolved fields\n *\n * \\ref CharacteristicFieldsCompute and\n * \\ref EvolvedFieldsFromCharacteristicFieldsCompute convert between\n * characteristic and evolved fields for the generalized harmonic system.\n *\n * \\ref CharacteristicFieldsCompute computes\n * characteristic fields as described in \"A New Generalized Harmonic\n * Evolution System\" by Lindblom et. al \\cite Lindblom2005qh .\n * Their names used here differ from this paper:\n *\n * \\f{align*}\n * \\mathrm{SpECTRE} && \\mathrm{Lindblom} \\\\\n * u^{g}_{ab} && u^\\hat{0}_{ab} \\\\\n * u^0_{iab} && u^\\hat{2}_{iab} \\\\\n * u^{\\pm}_{ab} && u^{\\hat{1}\\pm}_{ab}\n * \\f}\n *\n * The characteristic fields \\f$u\\f$ are given in terms of the evolved fields by\n * Eq.(32) - (34) of \\cite Lindblom2005qh, respectively:\n * \\f{align*}\n * u^{g}_{ab} =& g_{ab} \\\\\n * u^0_{iab} =& (\\delta^k_i - n_i n^k) \\Phi_{kab} := P^k_i \\Phi_{kab} \\\\\n * u^{\\pm}_{ab} =& \\Pi_{ab} \\pm n^i \\Phi_{iab} - \\gamma_2 g_{ab}\n * \\f}\n *\n * where \\f$g_{ab}\\f$ is the spacetime metric, \\f$\\Pi_{ab}\\f$ and\n * \\f$\\Phi_{iab}\\f$ are evolved generalized harmonic fields introduced by first\n * derivatives of \\f$g_{ab}\\f$, \\f$\\gamma_2\\f$ is a constraint damping\n * parameter, and \\f$n_k\\f$ is the unit normal to the surface.\n *\n * \\ref EvolvedFieldsFromCharacteristicFieldsCompute computes evolved fields\n * \\f$w\\f$ in terms of the characteristic fields. This uses the inverse of\n * above relations:\n *\n * \\f{align*}\n * g_{ab} =& u^{g}_{ab}, \\\\\n * \\Pi_{ab} =& \\frac{1}{2}(u^{+}_{ab} + u^{-}_{ab}) + \\gamma_2 u^{g}_{ab}, \\\\\n * \\Phi_{iab} =& \\frac{1}{2}(u^{+}_{ab} - u^{-}_{ab}) n_i + u^0_{iab}.\n * \\f}\n *\n * The corresponding characteristic speeds \\f$v\\f$ are computed by\n * \\ref CharacteristicSpeedsCompute .\n */\ntemplate <size_t Dim, typename Frame>\ntypename Tags::CharacteristicFields<DataVector, Dim, Frame>::type\ncharacteristic_fields(\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame>& phi,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form);\n\ntemplate <size_t Dim, typename Frame>\nvoid characteristic_fields(\n    gsl::not_null<\n        typename Tags::CharacteristicFields<DataVector, Dim, Frame>::type*>\n        char_fields,\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame>& phi,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form);\n\ntemplate <size_t Dim, typename Frame>\nstruct CharacteristicFieldsCompute\n    : Tags::CharacteristicFields<DataVector, Dim, Frame>,\n      db::ComputeTag {\n  using base = Tags::CharacteristicFields<DataVector, Dim, Frame>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      ::gh::Tags::ConstraintGamma2,\n      gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>,\n      gr::Tags::SpacetimeMetric<DataVector, Dim, Frame>,\n      Tags::Pi<DataVector, Dim, Frame>, Tags::Phi<DataVector, Dim, Frame>,\n      ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim, Frame>>>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<return_type*>, const Scalar<DataVector>&,\n      const tnsr::II<DataVector, Dim, Frame>&,\n      const tnsr::aa<DataVector, Dim, Frame>&,\n      const tnsr::aa<DataVector, Dim, Frame>&,\n      const tnsr::iaa<DataVector, Dim, Frame>&,\n      const tnsr::i<DataVector, Dim, Frame>&)>(&characteristic_fields);\n};\n/// @}\n\n/// @{\n/*!\n * \\brief For expressions used here to compute evolved fields from\n * characteristic ones, see \\ref CharacteristicFieldsCompute.\n */\ntemplate <size_t Dim, typename Frame>\ntypename Tags::EvolvedFieldsFromCharacteristicFields<DataVector, Dim,\n                                                     Frame>::type\nevolved_fields_from_characteristic_fields(\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::aa<DataVector, Dim, Frame>& u_psi,\n    const tnsr::iaa<DataVector, Dim, Frame>& u_zero,\n    const tnsr::aa<DataVector, Dim, Frame>& u_plus,\n    const tnsr::aa<DataVector, Dim, Frame>& u_minus,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form);\n\ntemplate <size_t Dim, typename Frame>\nvoid evolved_fields_from_characteristic_fields(\n    gsl::not_null<typename Tags::EvolvedFieldsFromCharacteristicFields<\n        DataVector, Dim, Frame>::type*>\n        evolved_fields,\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::aa<DataVector, Dim, Frame>& u_psi,\n    const tnsr::iaa<DataVector, Dim, Frame>& u_zero,\n    const tnsr::aa<DataVector, Dim, Frame>& u_plus,\n    const tnsr::aa<DataVector, Dim, Frame>& u_minus,\n    const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form);\n\ntemplate <size_t Dim, typename Frame>\nstruct EvolvedFieldsFromCharacteristicFieldsCompute\n    : Tags::EvolvedFieldsFromCharacteristicFields<DataVector, Dim, Frame>,\n      db::ComputeTag {\n  using base =\n      Tags::EvolvedFieldsFromCharacteristicFields<DataVector, Dim, Frame>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      gh::Tags::ConstraintGamma2,\n      Tags::VSpacetimeMetric<DataVector, Dim, Frame>,\n      Tags::VZero<DataVector, Dim, Frame>, Tags::VPlus<DataVector, Dim, Frame>,\n      Tags::VMinus<DataVector, Dim, Frame>,\n      ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim, Frame>>>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<return_type*>, const Scalar<DataVector>& gamma_2,\n      const tnsr::aa<DataVector, Dim, Frame>& u_psi,\n      const tnsr::iaa<DataVector, Dim, Frame>& u_zero,\n      const tnsr::aa<DataVector, Dim, Frame>& u_plus,\n      const tnsr::aa<DataVector, Dim, Frame>& u_minus,\n      const tnsr::i<DataVector, Dim, Frame>& unit_normal_one_form)>(\n      &evolved_fields_from_characteristic_fields);\n};\n/// @}\n\nnamespace Tags{\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n/*!\n * \\brief Computes the largest magnitude of the characteristic speeds.\n */\ntemplate <size_t Dim, typename Frame>\nstruct ComputeLargestCharacteristicSpeed : db::ComputeTag,\n                                           LargestCharacteristicSpeed {\n  using argument_tags =\n      tmpl::list<::gh::Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim, Frame>,\n                 gr::Tags::SpatialMetric<DataVector, Dim, Frame>>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(const gsl::not_null<double*> speed,\n                       const Scalar<DataVector>& gamma_1,\n                       const Scalar<DataVector>& lapse,\n                       const tnsr::I<DataVector, Dim, Frame>& shift,\n                       const tnsr::ii<DataVector, Dim, Frame>& spatial_metric);\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace {\n// Functions to compute generalized-harmonic 2-index constraint, where\n// function arguments are in the order that each quantity first\n// appears in the corresponding term in Eq. (44) of\n// https://arXiv.org/abs/gr-qc/0512093v3\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_1_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        for (size_t k = 0; k < SpatialDim; ++k) {\n          constraint->get(i, a) +=\n              inverse_spatial_metric.get(j, k) * d_phi.get(j, i, k + 1, a);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_2_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          if (a > 0) {\n            constraint->get(i, a) -= 0.5 * inverse_spacetime_metric.get(c, d) *\n                                     d_phi.get(a - 1, i, c, d);\n          }\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            constraint->get(i, a) -= 0.5 * spacetime_normal_vector.get(j + 1) *\n                                     spacetime_normal_one_form.get(a) *\n                                     inverse_spacetime_metric.get(c, d) *\n                                     d_phi.get(j, i, c, d);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_3_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t b = 0; b < SpatialDim + 1; ++b) {\n        constraint->get(i, a) +=\n            spacetime_normal_vector.get(b) * d_pi.get(i, b, a);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_4_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          constraint->get(i, a) -= 0.5 * spacetime_normal_one_form.get(a) *\n                                   inverse_spacetime_metric.get(c, d) *\n                                   d_pi.get(i, c, d);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_5_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      constraint->get(i, a) += spacetime_d_gauge_function.get(i + 1, a);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_6_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t e = 0; e < SpatialDim + 1; ++e) {\n            for (size_t f = 0; f < SpatialDim + 1; ++f) {\n              if (a > 0) {\n                constraint->get(i, a) += 0.5 * phi.get(a - 1, c, d) *\n                                         phi.get(i, e, f) *\n                                         inverse_spacetime_metric.get(c, e) *\n                                         inverse_spacetime_metric.get(d, f);\n              }\n              for (size_t j = 0; j < SpatialDim; ++j) {\n                constraint->get(i, a) +=\n                    0.5 * spacetime_normal_vector.get(j + 1) *\n                    spacetime_normal_one_form.get(a) * phi.get(j, c, d) *\n                    phi.get(i, e, f) * inverse_spacetime_metric.get(c, e) *\n                    inverse_spacetime_metric.get(d, f);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_7_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        for (size_t k = 0; k < SpatialDim; ++k) {\n          for (size_t c = 0; c < SpatialDim + 1; ++c) {\n            for (size_t d = 0; d < SpatialDim + 1; ++d) {\n              for (size_t e = 0; e < SpatialDim + 1; ++e) {\n                constraint->get(i, a) +=\n                    0.5 * inverse_spatial_metric.get(j, k) * phi.get(j, c, d) *\n                    phi.get(i, k + 1, e) * inverse_spacetime_metric.get(c, d) *\n                    spacetime_normal_vector.get(e) *\n                    spacetime_normal_one_form.get(a);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_8_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        for (size_t k = 0; k < SpatialDim; ++k) {\n          for (size_t m = 0; m < SpatialDim; ++m) {\n            for (size_t n = 0; n < SpatialDim; ++n) {\n              constraint->get(i, a) -= inverse_spatial_metric.get(j, k) *\n                                       inverse_spatial_metric.get(m, n) *\n                                       phi.get(j, m + 1, a) *\n                                       phi.get(i, k + 1, n + 1);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_9_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t b = 0; b < SpatialDim + 1; ++b) {\n        for (size_t c = 0; c < SpatialDim + 1; ++c) {\n          for (size_t d = 0; d < SpatialDim + 1; ++d) {\n            for (size_t e = 0; e < SpatialDim + 1; ++e) {\n              constraint->get(i, a) +=\n                  0.5 * phi.get(i, c, d) * pi.get(b, e) *\n                  spacetime_normal_one_form.get(a) *\n                  (inverse_spacetime_metric.get(c, b) *\n                       inverse_spacetime_metric.get(d, e) +\n                   0.5 * inverse_spacetime_metric.get(b, e) *\n                       spacetime_normal_vector.get(c) *\n                       spacetime_normal_vector.get(d));\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_10_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t b = 0; b < SpatialDim + 1; ++b) {\n        for (size_t c = 0; c < SpatialDim + 1; ++c) {\n          for (size_t d = 0; d < SpatialDim + 1; ++d) {\n            constraint->get(i, a) -= phi.get(i, c, d) * pi.get(b, a) *\n                                     spacetime_normal_vector.get(c) *\n                                     (inverse_spacetime_metric.get(b, d) +\n                                      0.5 * spacetime_normal_vector.get(b) *\n                                          spacetime_normal_vector.get(d));\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint_add_term_11_of_11(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const Scalar<DataType>& gamma2,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t d = 0; d < SpatialDim + 1; ++d) {\n        for (size_t c = 0; c < SpatialDim + 1; ++c) {\n          constraint->get(i, a) += 0.5 * get(gamma2) *\n                                   spacetime_normal_one_form.get(a) *\n                                   inverse_spacetime_metric.get(c, d) *\n                                   three_index_constraint.get(i, c, d);\n        }\n        constraint->get(i, a) -= get(gamma2) * spacetime_normal_vector.get(d) *\n                                 three_index_constraint.get(i, a, d);\n      }\n    }\n  }\n}\n\n// Functions to compute generalized-harmonic F constraint, where\n// function arguments are in the order that each quantity first\n// appears in the corresponding term in Eq. (43) of\n// https://arXiv.org/abs/gr-qc/0512093v3\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_1_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        if (a > 0) {\n          constraint->get(a) +=\n              0.5 * inverse_spacetime_metric.get(b, c) * d_pi.get(a - 1, b, c);\n        }\n        for (size_t i = 0; i < SpatialDim; ++i) {\n          constraint->get(a) += 0.5 * spacetime_normal_vector.get(i + 1) *\n                                spacetime_normal_one_form.get(a) *\n                                inverse_spacetime_metric.get(b, c) *\n                                d_pi.get(i, b, c);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_2_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        constraint->get(a) -=\n            inverse_spatial_metric.get(i, j) * d_pi.get(i, j + 1, a);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_3_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        for (size_t b = 0; b < SpatialDim + 1; ++b) {\n          constraint->get(a) -= inverse_spatial_metric.get(i, j) *\n                                spacetime_normal_vector.get(b) *\n                                d_phi.get(i, j, b, a);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_4_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t i = 0; i < SpatialDim; ++i) {\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            constraint->get(a) += 0.5 * spacetime_normal_one_form.get(a) *\n                                  inverse_spacetime_metric.get(b, c) *\n                                  inverse_spatial_metric.get(i, j) *\n                                  d_phi.get(i, j, b, c);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_5_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        constraint->get(a) += spacetime_normal_one_form.get(a) *\n                              inverse_spatial_metric.get(i, j) *\n                              spacetime_d_gauge_function.get(i + 1, j + 1);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_6_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            for (size_t k = 0; k < SpatialDim; ++k) {\n              if (a > 0) {\n                constraint->get(a) += phi.get(a - 1, j + 1, b) *\n                                      inverse_spatial_metric.get(j, k) *\n                                      phi.get(k, c, d) *\n                                      inverse_spacetime_metric.get(b, d) *\n                                      spacetime_normal_vector.get(c);\n              }\n              for (size_t i = 0; i < SpatialDim; ++i) {\n                constraint->get(a) +=\n                    spacetime_normal_one_form.get(a) *\n                    spacetime_normal_vector.get(i + 1) * phi.get(i, j + 1, b) *\n                    inverse_spatial_metric.get(j, k) * phi.get(k, c, d) *\n                    inverse_spacetime_metric.get(b, d) *\n                    spacetime_normal_vector.get(c);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_7_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            for (size_t k = 0; k < SpatialDim; ++k) {\n              if (a > 0) {\n                constraint->get(a) -= 0.5 * phi.get(a - 1, j + 1, b) *\n                                      inverse_spatial_metric.get(j, k) *\n                                      phi.get(k, c, d) *\n                                      inverse_spacetime_metric.get(c, d) *\n                                      spacetime_normal_vector.get(b);\n              }\n              for (size_t i = 0; i < SpatialDim; ++i) {\n                constraint->get(a) -=\n                    0.5 * spacetime_normal_one_form.get(a) *\n                    spacetime_normal_vector.get(i + 1) * phi.get(i, j + 1, b) *\n                    inverse_spatial_metric.get(j, k) * phi.get(k, c, d) *\n                    inverse_spacetime_metric.get(c, d) *\n                    spacetime_normal_vector.get(b);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_8_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      if (a > 0) {\n        constraint->get(a) -= spacetime_normal_vector.get(b) *\n                              spacetime_d_gauge_function.get(a, b);\n      }\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        constraint->get(a) -= spacetime_normal_one_form.get(a) *\n                              spacetime_normal_vector.get(i + 1) *\n                              spacetime_normal_vector.get(b) *\n                              spacetime_d_gauge_function.get(i + 1, b);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_9_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t i = 0; i < SpatialDim; ++i) {\n            for (size_t j = 0; j < SpatialDim; ++j) {\n              constraint->get(a) += inverse_spatial_metric.get(i, j) *\n                                    phi.get(i, c, d) * phi.get(j, b, a) *\n                                    inverse_spacetime_metric.get(b, c) *\n                                    spacetime_normal_vector.get(d);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_10_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t c = 0; c < SpatialDim + 1; ++c) {\n      for (size_t d = 0; d < SpatialDim + 1; ++d) {\n        for (size_t i = 0; i < SpatialDim; ++i) {\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            for (size_t m = 0; m < SpatialDim; ++m) {\n              for (size_t n = 0; n < SpatialDim; ++n) {\n                constraint->get(a) -=\n                    0.5 * spacetime_normal_one_form.get(a) *\n                    inverse_spatial_metric.get(i, j) *\n                    inverse_spatial_metric.get(m, n) * phi.get(i, m + 1, c) *\n                    phi.get(n, j + 1, d) * inverse_spacetime_metric.get(c, d);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_11_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t e = 0; e < SpatialDim + 1; ++e) {\n            for (size_t i = 0; i < SpatialDim; ++i) {\n              for (size_t j = 0; j < SpatialDim; ++j) {\n                constraint->get(a) -= 0.25 * spacetime_normal_one_form.get(a) *\n                                      inverse_spatial_metric.get(i, j) *\n                                      phi.get(i, c, d) * phi.get(j, b, e) *\n                                      inverse_spacetime_metric.get(c, b) *\n                                      inverse_spacetime_metric.get(d, e);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_12_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t e = 0; e < SpatialDim + 1; ++e) {\n            constraint->get(a) += 0.25 * spacetime_normal_one_form.get(a) *\n                                  pi.get(c, d) * pi.get(b, e) *\n                                  inverse_spacetime_metric.get(c, b) *\n                                  inverse_spacetime_metric.get(d, e);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_13_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        constraint->get(a) -= inverse_spatial_metric.get(i, j) *\n                              gauge_function.get(i + 1) * pi.get(j + 1, a);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_14_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        for (size_t j = 0; j < SpatialDim; ++j) {\n          constraint->get(a) -= spacetime_normal_vector.get(b) *\n                                inverse_spatial_metric.get(i, j) *\n                                pi.get(b, i + 1) * pi.get(j + 1, a);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_15_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t e = 0; e < SpatialDim + 1; ++e) {\n            if (a > 0) {\n              constraint->get(a) -=\n                  0.25 * phi.get(a - 1, c, d) * spacetime_normal_vector.get(c) *\n                  spacetime_normal_vector.get(d) * pi.get(b, e) *\n                  inverse_spacetime_metric.get(b, e);\n            }\n            for (size_t i = 0; i < SpatialDim; ++i) {\n              constraint->get(a) -=\n                  0.25 * spacetime_normal_one_form.get(a) *\n                  spacetime_normal_vector.get(i + 1) * phi.get(i, c, d) *\n                  spacetime_normal_vector.get(c) *\n                  spacetime_normal_vector.get(d) * pi.get(b, e) *\n                  inverse_spacetime_metric.get(b, e);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_16_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t e = 0; e < SpatialDim + 1; ++e) {\n            constraint->get(a) +=\n                0.5 * spacetime_normal_one_form.get(a) * pi.get(c, d) *\n                pi.get(b, e) * inverse_spacetime_metric.get(c, e) *\n                spacetime_normal_vector.get(d) * spacetime_normal_vector.get(b);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_17_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          for (size_t e = 0; e < SpatialDim + 1; ++e) {\n            if (a > 0) {\n              constraint->get(a) += phi.get(a - 1, c, d) * pi.get(b, e) *\n                                    spacetime_normal_vector.get(c) *\n                                    spacetime_normal_vector.get(b) *\n                                    inverse_spacetime_metric.get(d, e);\n            }\n            for (size_t i = 0; i < SpatialDim; ++i) {\n              constraint->get(a) += spacetime_normal_one_form.get(a) *\n                                    spacetime_normal_vector.get(i + 1) *\n                                    phi.get(i, c, d) * pi.get(b, e) *\n                                    spacetime_normal_vector.get(c) *\n                                    spacetime_normal_vector.get(b) *\n                                    inverse_spacetime_metric.get(d, e);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_18_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t e = 0; e < SpatialDim + 1; ++e) {\n        for (size_t i = 0; i < SpatialDim; ++i) {\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            constraint->get(a) -=\n                inverse_spatial_metric.get(i, j) * phi.get(i, b, a) *\n                spacetime_normal_vector.get(b) * pi.get(j + 1, e) *\n                spacetime_normal_vector.get(e);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_19_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t c = 0; c < SpatialDim + 1; ++c) {\n      for (size_t d = 0; d < SpatialDim + 1; ++d) {\n        for (size_t i = 0; i < SpatialDim; ++i) {\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            constraint->get(a) -=\n                0.5 * inverse_spatial_metric.get(i, j) * phi.get(i, c, d) *\n                spacetime_normal_vector.get(c) *\n                spacetime_normal_vector.get(d) * pi.get(j + 1, a);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_20_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        for (size_t j = 0; j < SpatialDim; ++j) {\n          constraint->get(a) -= inverse_spatial_metric.get(i, j) *\n                                gauge_function.get(i + 1) * phi.get(j, b, a) *\n                                spacetime_normal_vector.get(b);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_21_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          if (a > 0) {\n            constraint->get(a) += phi.get(a - 1, c, d) * gauge_function.get(b) *\n                                  inverse_spacetime_metric.get(b, c) *\n                                  spacetime_normal_vector.get(d);\n          }\n          for (size_t i = 0; i < SpatialDim; ++i) {\n            constraint->get(a) += spacetime_normal_one_form.get(a) *\n                                  spacetime_normal_vector.get(i + 1) *\n                                  phi.get(i, c, d) * gauge_function.get(b) *\n                                  inverse_spacetime_metric.get(b, c) *\n                                  spacetime_normal_vector.get(d);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_22_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const Scalar<DataType>& gamma2,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t d = 0; d < SpatialDim + 1; ++d) {\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        constraint->get(a) += get(gamma2) *\n                              inverse_spacetime_metric.get(i + 1, d) *\n                              three_index_constraint.get(i, d, a);\n        constraint->get(a) += get(gamma2) * spacetime_normal_vector.get(i + 1) *\n                              spacetime_normal_vector.get(d) *\n                              three_index_constraint.get(i, d, a);\n      }\n\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        if (a > 0) {\n          constraint->get(a) -= 0.5 * get(gamma2) *\n                                inverse_spacetime_metric.get(c, d) *\n                                three_index_constraint.get(a - 1, c, d);\n        }\n        for (size_t i = 0; i < SpatialDim; ++i) {\n          constraint->get(a) -= 0.5 * get(gamma2) *\n                                spacetime_normal_one_form.get(a) *\n                                spacetime_normal_vector.get(i + 1) *\n                                inverse_spacetime_metric.get(c, d) *\n                                three_index_constraint.get(i, c, d);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_23_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        for (size_t d = 0; d < SpatialDim + 1; ++d) {\n          constraint->get(a) +=\n              0.5 * spacetime_normal_one_form.get(a) * pi.get(c, d) *\n              inverse_spacetime_metric.get(c, d) * gauge_function.get(b) *\n              spacetime_normal_vector.get(b);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_24_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t c = 0; c < SpatialDim + 1; ++c) {\n      for (size_t d = 0; d < SpatialDim + 1; ++d) {\n        for (size_t i = 0; i < SpatialDim; ++i) {\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            constraint->get(a) -= spacetime_normal_one_form.get(a) *\n                                  inverse_spatial_metric.get(i, j) *\n                                  phi.get(i, j + 1, c) * gauge_function.get(d) *\n                                  inverse_spacetime_metric.get(c, d);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_term_25_of_25(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t c = 0; c < SpatialDim + 1; ++c) {\n      for (size_t d = 0; d < SpatialDim + 1; ++d) {\n        for (size_t i = 0; i < SpatialDim; ++i) {\n          for (size_t j = 0; j < SpatialDim; ++j) {\n            constraint->get(a) += 0.5 * spacetime_normal_one_form.get(a) *\n                                  inverse_spatial_metric.get(i, j) *\n                                  gauge_function.get(i + 1) * phi.get(j, c, d) *\n                                  inverse_spacetime_metric.get(c, d);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint_add_stress_energy_term(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::aa<DataType, SpatialDim, Frame>& trace_reversed_stress_energy) {\n  // This term, like many terms in the f constraint, may benefit from\n  // allocating a temporary for the trace. However, once we apply that\n  // optimization it should be applied to all terms that can benefit from\n  // temporary storage, and the allocation shared among all of the terms.\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      constraint->get(a) -= 16.0 * M_PI * spacetime_normal_vector.get(b) *\n                            trace_reversed_stress_energy.get(a, b);\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        constraint->get(a) += 8.0 * M_PI * spacetime_normal_one_form.get(a) *\n                              inverse_spacetime_metric.get(b, c) *\n                              trace_reversed_stress_energy.get(b, c);\n      }\n    }\n  }\n}\n}  // namespace\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::iaa<DataType, SpatialDim, Frame> three_index_constraint(\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  auto constraint =\n      make_with_value<tnsr::iaa<DataType, SpatialDim, Frame>>(phi, 0.0);\n  three_index_constraint<DataType, SpatialDim, Frame>(&constraint,\n                                                      d_spacetime_metric, phi);\n  return constraint;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid three_index_constraint(\n    const gsl::not_null<tnsr::iaa<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  // Declare iterators for d_spacetime_metric and phi outside the for loop,\n  // because they are const but constraint is not\n  // clang-tidy: llvm-qualified-auto\n  auto d_spacetime_metric_it = d_spacetime_metric.begin();  // NOLINT\n  auto phi_it = phi.begin();                                // NOLINT\n\n  for (auto constraint_it = (*constraint).begin();  // NOLINT\n       constraint_it != (*constraint).end();\n       ++constraint_it, (void)++d_spacetime_metric_it, (void)++phi_it) {\n    // clang-tidy: cppcoreguidelines-pro-bounds-pointer-arithmetic\n    *constraint_it = *d_spacetime_metric_it - *phi_it;  // NOLINT\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> gauge_constraint(\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  auto constraint =\n      make_with_value<tnsr::a<DataType, SpatialDim, Frame>>(pi, 0.0);\n  gauge_constraint<DataType, SpatialDim, Frame>(\n      &constraint, gauge_function, spacetime_normal_one_form,\n      spacetime_normal_vector, inverse_spatial_metric, inverse_spacetime_metric,\n      pi, phi);\n  return constraint;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid gauge_constraint(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  trace_christoffel(constraint, spacetime_normal_one_form,\n                    spacetime_normal_vector, inverse_spatial_metric,\n                    inverse_spacetime_metric, pi, phi);\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    (*constraint).get(a) += gauge_function.get(a);\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ia<DataType, SpatialDim, Frame> two_index_constraint(\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint) {\n  auto constraint =\n      make_with_value<tnsr::ia<DataType, SpatialDim, Frame>>(pi, 0.0);\n  two_index_constraint<DataType, SpatialDim, Frame>(\n      &constraint, spacetime_d_gauge_function, spacetime_normal_one_form,\n      spacetime_normal_vector, inverse_spatial_metric, inverse_spacetime_metric,\n      pi, phi, d_pi, d_phi, gamma2, three_index_constraint);\n  return constraint;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint(\n    const gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint) {\n  set_number_of_grid_points(constraint, gamma2);\n  for (auto& component : *constraint) {\n    component = 0.0;\n  }\n\n  two_index_constraint_add_term_1_of_11(constraint, inverse_spatial_metric,\n                                        d_phi);\n  two_index_constraint_add_term_2_of_11(constraint, spacetime_normal_vector,\n                                        spacetime_normal_one_form,\n                                        inverse_spacetime_metric, d_phi);\n  two_index_constraint_add_term_3_of_11(constraint, spacetime_normal_vector,\n                                        d_pi);\n  two_index_constraint_add_term_4_of_11(constraint, spacetime_normal_one_form,\n                                        inverse_spacetime_metric, d_pi);\n  two_index_constraint_add_term_5_of_11(constraint, spacetime_d_gauge_function);\n  two_index_constraint_add_term_6_of_11(constraint, spacetime_normal_vector,\n                                        spacetime_normal_one_form, phi,\n                                        inverse_spacetime_metric);\n  two_index_constraint_add_term_7_of_11(\n      constraint, inverse_spatial_metric, phi, inverse_spacetime_metric,\n      spacetime_normal_vector, spacetime_normal_one_form);\n  two_index_constraint_add_term_8_of_11(constraint, inverse_spatial_metric,\n                                        phi);\n  two_index_constraint_add_term_9_of_11(\n      constraint, phi, pi, spacetime_normal_one_form, inverse_spacetime_metric,\n      spacetime_normal_vector);\n  two_index_constraint_add_term_10_of_11(\n      constraint, phi, pi, spacetime_normal_vector, inverse_spacetime_metric);\n  two_index_constraint_add_term_11_of_11(\n      constraint, gamma2, spacetime_normal_one_form, inverse_spacetime_metric,\n      spacetime_normal_vector, three_index_constraint);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::iaa<DataType, SpatialDim, Frame> four_index_constraint(\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi) {\n  static_assert(\n      SpatialDim == 3,\n      \"four_index_constraint() currently only supports 3 spatial dimensions\");\n  auto constraint =\n      make_with_value<tnsr::iaa<DataType, SpatialDim, Frame>>(d_phi, 0.0);\n  four_index_constraint<DataType, SpatialDim, Frame>(&constraint, d_phi);\n  return constraint;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid four_index_constraint(\n    const gsl::not_null<tnsr::iaa<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi) {\n  static_assert(\n      SpatialDim == 3,\n      \"four_index_constraint() currently only supports 3 spatial dimensions\");\n  set_number_of_grid_points(constraint, d_phi);\n  std::fill(constraint->begin(), constraint->end(), 0.0);\n\n  for (LeviCivitaIterator<SpatialDim> it; it; ++it) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      // Constraint is symmetric on a,b. Avoid double-counting in summation\n      // by starting the inner loop at b = a.\n      for (size_t b = a; b < SpatialDim + 1; ++b) {\n        constraint->get(it[0], a, b) +=\n            it.sign() * d_phi.get(it[1], it[2], a, b);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> f_constraint(\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint) {\n  auto constraint =\n      make_with_value<tnsr::a<DataType, SpatialDim, Frame>>(pi, 0.0);\n  f_constraint<DataType, SpatialDim, Frame>(\n      &constraint, gauge_function, spacetime_d_gauge_function,\n      spacetime_normal_one_form, spacetime_normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi, d_pi, d_phi,\n      gamma2, three_index_constraint);\n  return constraint;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> f_constraint(\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::aa<DataType, SpatialDim, Frame>& trace_reversed_stress_energy) {\n  auto constraint =\n      make_with_value<tnsr::a<DataType, SpatialDim, Frame>>(pi, 0.0);\n  f_constraint<DataType, SpatialDim, Frame>(\n      &constraint, gauge_function, spacetime_d_gauge_function,\n      spacetime_normal_one_form, spacetime_normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi, d_pi, d_phi,\n      gamma2, three_index_constraint, trace_reversed_stress_energy);\n  return constraint;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::aa<DataType, SpatialDim, Frame>& trace_reversed_stress_energy) {\n  f_constraint<DataType, SpatialDim, Frame>(\n      constraint, gauge_function, spacetime_d_gauge_function,\n      spacetime_normal_one_form, spacetime_normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi, d_pi, d_phi,\n      gamma2, three_index_constraint);\n  f_constraint_add_stress_energy_term(\n      constraint, inverse_spacetime_metric, spacetime_normal_vector,\n      spacetime_normal_one_form, trace_reversed_stress_energy);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint) {\n  set_number_of_grid_points(constraint, pi);\n  std::fill(constraint->begin(), constraint->end(), 0.0);\n\n  f_constraint_add_term_1_of_25(constraint, spacetime_normal_one_form,\n                                spacetime_normal_vector,\n                                inverse_spacetime_metric, d_pi);\n  f_constraint_add_term_2_of_25(constraint, inverse_spatial_metric, d_pi);\n  f_constraint_add_term_3_of_25(constraint, inverse_spatial_metric,\n                                spacetime_normal_vector, d_phi);\n  f_constraint_add_term_4_of_25(constraint, spacetime_normal_one_form,\n                                inverse_spacetime_metric,\n                                inverse_spatial_metric, d_phi);\n  f_constraint_add_term_5_of_25(constraint, spacetime_normal_one_form,\n                                inverse_spatial_metric,\n                                spacetime_d_gauge_function);\n  f_constraint_add_term_6_of_25(\n      constraint, spacetime_normal_one_form, spacetime_normal_vector, phi,\n      inverse_spatial_metric, inverse_spacetime_metric);\n  f_constraint_add_term_7_of_25(\n      constraint, spacetime_normal_one_form, spacetime_normal_vector, phi,\n      inverse_spatial_metric, inverse_spacetime_metric);\n  f_constraint_add_term_8_of_25(constraint, spacetime_normal_one_form,\n                                spacetime_normal_vector,\n                                spacetime_d_gauge_function);\n  f_constraint_add_term_9_of_25(constraint, inverse_spatial_metric, phi,\n                                inverse_spacetime_metric,\n                                spacetime_normal_vector);\n  f_constraint_add_term_10_of_25(constraint, spacetime_normal_one_form,\n                                 inverse_spatial_metric, phi,\n                                 inverse_spacetime_metric);\n  f_constraint_add_term_11_of_25(constraint, spacetime_normal_one_form,\n                                 inverse_spatial_metric, phi,\n                                 inverse_spacetime_metric);\n  f_constraint_add_term_12_of_25(constraint, spacetime_normal_one_form, pi,\n                                 inverse_spacetime_metric);\n  f_constraint_add_term_13_of_25(constraint, inverse_spatial_metric,\n                                 gauge_function, pi);\n  f_constraint_add_term_14_of_25(constraint, spacetime_normal_vector,\n                                 inverse_spatial_metric, pi);\n  f_constraint_add_term_15_of_25(constraint, inverse_spacetime_metric,\n                                 spacetime_normal_one_form,\n                                 spacetime_normal_vector, phi, pi);\n  f_constraint_add_term_16_of_25(constraint, spacetime_normal_one_form, pi,\n                                 inverse_spacetime_metric,\n                                 spacetime_normal_vector);\n  f_constraint_add_term_17_of_25(constraint, inverse_spacetime_metric,\n                                 spacetime_normal_one_form,\n                                 spacetime_normal_vector, phi, pi);\n  f_constraint_add_term_18_of_25(constraint, inverse_spatial_metric, phi,\n                                 spacetime_normal_vector, pi);\n  f_constraint_add_term_19_of_25(constraint, inverse_spatial_metric, phi,\n                                 spacetime_normal_vector, pi);\n  f_constraint_add_term_20_of_25(constraint, inverse_spatial_metric,\n                                 gauge_function, phi, spacetime_normal_vector);\n  f_constraint_add_term_21_of_25(constraint, spacetime_normal_one_form,\n                                 spacetime_normal_vector, phi, gauge_function,\n                                 inverse_spacetime_metric);\n  f_constraint_add_term_22_of_25(\n      constraint, gamma2, inverse_spacetime_metric, spacetime_normal_vector,\n      three_index_constraint, spacetime_normal_one_form);\n  f_constraint_add_term_23_of_25(constraint, spacetime_normal_one_form, pi,\n                                 inverse_spacetime_metric, gauge_function,\n                                 spacetime_normal_vector);\n  f_constraint_add_term_24_of_25(constraint, spacetime_normal_one_form,\n                                 inverse_spatial_metric, phi, gauge_function,\n                                 inverse_spacetime_metric);\n  f_constraint_add_term_25_of_25(constraint, spacetime_normal_one_form,\n                                 inverse_spatial_metric, gauge_function, phi,\n                                 inverse_spacetime_metric);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> constraint_energy(\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& f_constraint,\n    const tnsr::ia<DataType, SpatialDim, Frame>& two_index_constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& four_index_constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const Scalar<DataType>& spatial_metric_determinant,\n    double gauge_constraint_multiplier, double two_index_constraint_multiplier,\n    double three_index_constraint_multiplier,\n    double four_index_constraint_multiplier) {\n  auto energy = make_with_value<Scalar<DataType>>(gauge_constraint, 0.0);\n  constraint_energy<DataType, SpatialDim, Frame>(\n      &energy, gauge_constraint, f_constraint, two_index_constraint,\n      three_index_constraint, four_index_constraint, inverse_spatial_metric,\n      spatial_metric_determinant, gauge_constraint_multiplier,\n      two_index_constraint_multiplier, three_index_constraint_multiplier,\n      four_index_constraint_multiplier);\n  return energy;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid constraint_energy(\n    const gsl::not_null<Scalar<DataType>*> energy,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& f_constraint,\n    const tnsr::ia<DataType, SpatialDim, Frame>& two_index_constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& four_index_constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const Scalar<DataType>& spatial_metric_determinant,\n    double gauge_constraint_multiplier, double two_index_constraint_multiplier,\n    double three_index_constraint_multiplier,\n    double four_index_constraint_multiplier) {\n  get(*energy) = gauge_constraint_multiplier * square(gauge_constraint.get(0)) +\n                 two_index_constraint_multiplier * square(f_constraint.get(0));\n  for (size_t a = 1; a < SpatialDim + 1; ++a) {\n    get(*energy) +=\n        gauge_constraint_multiplier * square(gauge_constraint.get(a)) +\n        two_index_constraint_multiplier * square(f_constraint.get(a));\n  }\n\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        get(*energy) +=\n            two_index_constraint_multiplier * two_index_constraint.get(i, a) *\n            two_index_constraint.get(j, a) * inverse_spatial_metric.get(i, j);\n\n        for (size_t b = 0; b < SpatialDim + 1; ++b) {\n          get(*energy) += three_index_constraint_multiplier *\n                              three_index_constraint.get(i, a, b) *\n                              three_index_constraint.get(j, a, b) *\n                              inverse_spatial_metric.get(i, j) +\n                          2.0 * four_index_constraint_multiplier *\n                              get(spatial_metric_determinant) *\n                              four_index_constraint.get(i, a, b) *\n                              four_index_constraint.get(j, a, b) *\n                              inverse_spatial_metric.get(i, j);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> constraint_energy_normalization(\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const Scalar<DataType>& sqrt_spatial_metric_determinant,\n    const double dimensional_constant) {\n  Scalar<DataType> energy_norm{get_size(get(sqrt_spatial_metric_determinant))};\n  constraint_energy_normalization<DataType, SpatialDim, Frame>(\n      make_not_null(&energy_norm), d_spacetime_metric, d_pi, d_phi,\n      inverse_spatial_metric, sqrt_spatial_metric_determinant,\n      dimensional_constant);\n  return energy_norm;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid constraint_energy_normalization(\n    const gsl::not_null<Scalar<DataType>*> energy_norm,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const Scalar<DataType>& sqrt_spatial_metric_determinant,\n    const double dimensional_constant) {\n  const double square_dimensional_constant = square(dimensional_constant);\n  get(*energy_norm) = 0.0;\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t c = 0; c < SpatialDim + 1; ++c) {\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        for (size_t j = 0; j < SpatialDim; ++j) {\n          get(*energy_norm) += inverse_spatial_metric.get(i, j) *\n                                   square_dimensional_constant *\n                                   d_spacetime_metric.get(i, a, c) *\n                                   d_spacetime_metric.get(j, a, c) +\n                               inverse_spatial_metric.get(i, j) *\n                                   d_pi.get(i, a, c) * d_pi.get(j, a, c);\n\n          for (size_t k = 0; k < SpatialDim; ++k) {\n            for (size_t l = 0; l < SpatialDim; ++l) {\n              get(*energy_norm) += inverse_spatial_metric.get(i, j) *\n                                   inverse_spatial_metric.get(k, l) *\n                                   d_phi.get(i, k, a, c) *\n                                   d_phi.get(j, l, a, c);\n            }\n          }\n        }\n      }\n    }\n  }\n  get(*energy_norm) *= get(sqrt_spatial_metric_determinant);\n}\n\n}  // namespace gh\n\n// Explicit Instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gh::three_index_constraint(                                                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          d_spacetime_metric,                                                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);             \\\n  template void gh::three_index_constraint(                                   \\\n      const gsl::not_null<tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          constraint,                                                         \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          d_spacetime_metric,                                                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);             \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)> gh::gauge_constraint( \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& gauge_function,     \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_one_form,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_vector,                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spacetime_metric,                                           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);             \\\n  template void gh::gauge_constraint(                                         \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*>      \\\n          constraint,                                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& gauge_function,     \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_one_form,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_vector,                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spacetime_metric,                                           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);             \\\n  template tnsr::ia<DTYPE(data), DIM(data), FRAME(data)>                      \\\n  gh::two_index_constraint(                                                   \\\n      const tnsr::ab<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_d_gauge_function,                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_one_form,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_vector,                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spacetime_metric,                                           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,              \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,             \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi,           \\\n      const Scalar<DTYPE(data)>& gamma2,                                      \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          three_index_constraint);                                            \\\n  template void gh::two_index_constraint(                                     \\\n      const gsl::not_null<tnsr::ia<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          constraint,                                                         \\\n      const tnsr::ab<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_d_gauge_function,                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_one_form,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_vector,                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spacetime_metric,                                           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,              \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,             \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi,           \\\n      const Scalar<DTYPE(data)>& gamma2,                                      \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          three_index_constraint);                                            \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)> gh::f_constraint(     \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& gauge_function,     \\\n      const tnsr::ab<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_d_gauge_function,                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_one_form,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_vector,                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spacetime_metric,                                           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,              \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,             \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi,           \\\n      const Scalar<DTYPE(data)>& gamma2,                                      \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          three_index_constraint);                                            \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)> gh::f_constraint(     \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& gauge_function,     \\\n      const tnsr::ab<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_d_gauge_function,                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_one_form,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_vector,                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spacetime_metric,                                           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,              \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,             \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi,           \\\n      const Scalar<DTYPE(data)>& gamma2,                                      \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          three_index_constraint,                                             \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          trace_reversed_stress_energy);                                      \\\n  template void gh::f_constraint(                                             \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*>      \\\n          constraint,                                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& gauge_function,     \\\n      const tnsr::ab<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_d_gauge_function,                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_one_form,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_vector,                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spacetime_metric,                                           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,              \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,             \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi,           \\\n      const Scalar<DTYPE(data)>& gamma2,                                      \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          three_index_constraint);                                            \\\n  template void gh::f_constraint(                                             \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*>      \\\n          constraint,                                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& gauge_function,     \\\n      const tnsr::ab<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_d_gauge_function,                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_one_form,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          spacetime_normal_vector,                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spacetime_metric,                                           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,              \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,             \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi,           \\\n      const Scalar<DTYPE(data)>& gamma2,                                      \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          three_index_constraint,                                             \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          trace_reversed_stress_energy);                                      \\\n  template Scalar<DTYPE(data)> gh::constraint_energy(                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& gauge_constraint,   \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& f_constraint,       \\\n      const tnsr::ia<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          two_index_constraint,                                               \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          three_index_constraint,                                             \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          four_index_constraint,                                              \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const Scalar<DTYPE(data)>& spatial_metric_determinant,                  \\\n      double gauge_constraint_multiplier,                                     \\\n      double two_index_constraint_multiplier,                                 \\\n      double three_index_constraint_multiplier,                               \\\n      double four_index_constraint_multiplier);                               \\\n  template void gh::constraint_energy(                                        \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> energy,                       \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& gauge_constraint,   \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& f_constraint,       \\\n      const tnsr::ia<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          two_index_constraint,                                               \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          three_index_constraint,                                             \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          four_index_constraint,                                              \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const Scalar<DTYPE(data)>& spatial_metric_determinant,                  \\\n      double gauge_constraint_multiplier,                                     \\\n      double two_index_constraint_multiplier,                                 \\\n      double three_index_constraint_multiplier,                               \\\n      double four_index_constraint_multiplier);                               \\\n  template Scalar<DTYPE(data)> gh::constraint_energy_normalization(           \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          d_spacetime_metric,                                                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,             \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi,           \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const Scalar<DTYPE(data)>& sqrt_spatial_metric_determinant,             \\\n      double dimensional_constant);                                           \\\n  template void gh::constraint_energy_normalization(                          \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> energy_norm,                  \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          d_spacetime_metric,                                                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,             \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi,           \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const Scalar<DTYPE(data)>& sqrt_spatial_metric_determinant,             \\\n      double dimensional_constant);\n\n#define INSTANTIATE_ONLY_3D(_, data)                                       \\\n  template tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::four_index_constraint(                                               \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi);       \\\n  template void gh::four_index_constraint(                                 \\\n      const gsl::not_null<tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          constraint,                                                      \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\nGENERATE_INSTANTIATIONS(INSTANTIATE_ONLY_3D, (3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Constraints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Defines functions to calculate the generalized harmonic constraints\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\brief Computes the generalized-harmonic 3-index constraint.\n *\n * \\details Computes the generalized-harmonic 3-index constraint,\n * \\f$C_{iab} = \\partial_i g_{ab} - \\Phi_{iab},\\f$ which is\n * given by Eq. (26) of \\cite Lindblom2005qh\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::iaa<DataType, SpatialDim, Frame> three_index_constraint(\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid three_index_constraint(\n    gsl::not_null<tnsr::iaa<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the generalized-harmonic gauge constraint.\n *\n * \\details Computes the generalized-harmonic gauge constraint\n * [Eq. (40) of \\cite Lindblom2005qh],\n * \\f[\n * C_a = H_a + \\gamma^{ij} \\Phi_{ija} + n^b \\Pi_{ba}\n * - \\frac{1}{2} \\gamma^i_a g^{bc} \\Phi_{ibc}\n * - \\frac{1}{2} n_a g^{bc} \\Pi_{bc},\n * \\f]\n * where \\f$H_a\\f$ is the gauge function,\n * \\f$g_{ab}\\f$ is the spacetime metric,\n * \\f$\\Pi_{ab}=-n^c\\partial_c g_{ab}\\f$, and\n * \\f$\\Phi_{iab} = \\partial_i g_{ab}\\f$; \\f$n^a\\f$ is the timelike unit\n * normal vector to the spatial slice, \\f$\\gamma^{ij}\\f$ is the inverse spatial\n * metric, and \\f$\\gamma^b_c = \\delta^b_c + n^b n_c\\f$.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> gauge_constraint(\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid gauge_constraint(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the generalized-harmonic 2-index constraint.\n *\n * \\details Computes the generalized-harmonic 2-index constraint\n * [Eq. (44) of \\cite Lindblom2005qh],\n * \\f{eqnarray}{\n * C_{ia} &\\equiv& \\gamma^{jk}\\partial_j \\Phi_{ika}\n * - \\frac{1}{2} \\gamma_a^j g^{cd}\\partial_j \\Phi_{icd}\n * + n^b \\partial_i \\Pi_{ba}\n * - \\frac{1}{2} n_a g^{cd}\\partial_i\\Pi_{cd}\n * \\nonumber\\\\&&\n * + \\partial_i H_a\n * + \\frac{1}{2} \\gamma_a^j \\Phi_{jcd} \\Phi_{ief}\n * g^{ce}g^{df}\n * + \\frac{1}{2} \\gamma^{jk} \\Phi_{jcd} \\Phi_{ike}\n * g^{cd}n^e n_a\n * \\nonumber\\\\&&\n * - \\gamma^{jk}\\gamma^{mn}\\Phi_{jma}\\Phi_{ikn}\n * + \\frac{1}{2} \\Phi_{icd} \\Pi_{be} n_a\n *                             \\left(g^{cb}g^{de}\n *                       +\\frac{1}{2}g^{be} n^c n^d\\right)\n * \\nonumber\\\\&&\n * - \\Phi_{icd} \\Pi_{ba} n^c \\left(g^{bd}\n *                             +\\frac{1}{2} n^b n^d\\right)\n * + \\frac{1}{2} \\gamma_2 \\left(n_a g^{cd}\n * - 2 \\delta^c_a n^d\\right) C_{icd}.\n * \\f}\n * where \\f$H_a\\f$ is the gauge function,\n * \\f$g_{ab}\\f$ is the spacetime metric,\n * \\f$\\Pi_{ab}=-n^c\\partial_c g_{ab}\\f$, and\n * \\f$\\Phi_{iab} = \\partial_i g_{ab}\\f$; \\f$n^a\\f$ is the timelike unit\n * normal vector to the spatial slice, \\f$\\gamma^{ij}\\f$ is the inverse spatial\n * metric, and \\f$\\gamma^b_c = \\delta^b_c + n^b n_c\\f$.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ia<DataType, SpatialDim, Frame> two_index_constraint(\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid two_index_constraint(\n    gsl::not_null<tnsr::ia<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the generalized-harmonic 4-index constraint.\n *\n * \\details Computes the independent components of the generalized-harmonic\n * 4-index constraint. The constraint itself is given by Eq. (45) of\n * \\cite Lindblom2005qh,\n * \\f{eqnarray}{\n * C_{ijab} = 2 \\partial_{[i}\\Phi_{j]ab},\n * \\f}\n * where \\f$\\Phi_{iab} = \\partial_i g_{ab}\\f$. Because the constraint is\n * antisymmetric on the two spatial indices, here we compute and store\n * only the independent components of \\f$C_{ijab}\\f$. Specifically, we\n * compute\n * \\f{eqnarray}{\n * D_{iab} \\equiv \\frac{1}{2} \\epsilon_{i}{}^{jk} C_{jkab}\n * = \\epsilon_{i}{}^{jk} \\partial_j \\Phi_{kab},\n * \\f}\n * where \\f$\\epsilon_{ijk}\\f$ is the flat-space Levi-Civita symbol,\n * which is raised and lowered with the Kronecker delta.\n * In terms\n * of \\f$D_{iab}\\f$, the full 4-index constraint is\n * \\f{eqnarray}{\n * C_{jkab} = \\epsilon^{i}{}_{jk} D_{iab}.\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::iaa<DataType, SpatialDim, Frame> four_index_constraint(\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid four_index_constraint(\n    gsl::not_null<tnsr::iaa<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the generalized-harmonic F constraint.\n *\n * \\details Computes the generalized-harmonic F constraint\n * [Eq. (43) of \\cite Lindblom2005qh],\n * \\f{eqnarray}{\n * {\\cal F}_a &\\equiv&\n * \\frac{1}{2} \\gamma_a^i g^{bc}\\partial_i \\Pi_{bc}\n * - \\gamma^{ij} \\partial_i \\Pi_{ja}\n * - \\gamma^{ij} n^b \\partial_i \\Phi_{jba}\n * + \\frac{1}{2} n_a g^{bc} \\gamma^{ij} \\partial_i \\Phi_{jbc}\n * \\nonumber \\\\ &&\n * + n_a \\gamma^{ij} \\partial_i H_j\n * + \\gamma_a^i \\Phi_{ijb} \\gamma^{jk}\\Phi_{kcd} g^{bd} n^c\n * - \\frac{1}{2} \\gamma_a^i \\Phi_{ijb} \\gamma^{jk}\n *   \\Phi_{kcd} g^{cd} n^b\n * \\nonumber \\\\ &&\n * - \\gamma_a^i n^b \\partial_i H_b\n * + \\gamma^{ij} \\Phi_{icd} \\Phi_{jba} g^{bc} n^d\n * - \\frac{1}{2} n_a \\gamma^{ij} \\gamma^{mn} \\Phi_{imc} \\Phi_{njd}g^{cd}\n * \\nonumber \\\\ &&\n * - \\frac{1}{4}  n_a \\gamma^{ij}\\Phi_{icd}\\Phi_{jbe}\n *    g^{cb}g^{de}\n * + \\frac{1}{4}  n_a \\Pi_{cd} \\Pi_{be}\n *    g^{cb}g^{de}\n * - \\gamma^{ij} H_i \\Pi_{ja}\n * \\nonumber \\\\ &&\n * - n^b \\gamma^{ij} \\Pi_{b i} \\Pi_{ja}\n * - \\frac{1}{4}  \\gamma_a^i \\Phi_{icd} n^c n^d \\Pi_{be}\n *   g^{be}\n * + \\frac{1}{2} n_a \\Pi_{cd} \\Pi_{be}g^{ce}\n *   n^d n^b\n * \\nonumber \\\\ &&\n * + \\gamma_a^i \\Phi_{icd} \\Pi_{be} n^c n^b g^{de}\n * - \\gamma^{ij}\\Phi_{iba} n^b \\Pi_{je} n^e\n * - \\frac{1}{2} \\gamma^{ij}\\Phi_{icd} n^c n^d \\Pi_{ja}\n * \\nonumber \\\\ &&\n * - \\gamma^{ij} H_i \\Phi_{jba} n^b\n * + \\gamma_{a}^i \\Phi_{icd} H_b g^{bc} n^d\n * +\\gamma_2\\bigl(\\gamma^{id}{\\cal C}_{ida}\n * -\\frac{1}{2}  \\gamma_a^i g^{cd}{\\cal C}_{icd}\\bigr)\n * \\nonumber \\\\ &&\n * + \\frac{1}{2} n_a \\Pi_{cd}g^{cd} H_b n^b\n * - n_a \\gamma^{ij} \\Phi_{ijc} H_d g^{cd}\n * +\\frac{1}{2}  n_a \\gamma^{ij} H_i \\Phi_{jcd}g^{cd}\n * \\nonumber \\\\ &&\n * - 16 \\pi n^a T_{a b}\n * \\f}\n * where \\f$H_a\\f$ is the gauge function,\n * \\f$g_{ab}\\f$ is the spacetime metric,\n * \\f$\\Pi_{ab}=-n^c\\partial_c g_{ab}\\f$, and\n * \\f$\\Phi_{iab} = \\partial_i g_{ab}\\f$; \\f$n^a\\f$ is the timelike unit\n * normal vector to the spatial slice, \\f$\\gamma^{ij}\\f$ is the inverse spatial\n * metric, \\f$\\gamma^b_c = \\delta^b_c + n^b n_c\\f$, and \\f$T_{a b}\\f$ is the\n * stress-energy tensor if nonzero (if using the overload with no stress-energy\n * tensor provided, the stress energy term is omitted).\n *\n * To justify the stress-energy contribution to the F constraint, note that\n * the stress-energy tensor appears in the dynamics of the Generalized\n * Harmonic system only through \\f$\\partial_t \\Pi_{a b}\\f$.\n * That dependence arises from (using \\f$\\dots\\f$ to indicate collections of\n * terms that are known to be independent of the stress-energy tensor):\n *\n * \\f[\n * {\\cal F}_a = \\dots \\alpha^{-1}(\\partial_t {\\cal C}_a),\n * \\f]\n *\n * where\n *\n * \\f[\n * {\\cal C}_a = H_a + \\gamma^{i j} \\Phi_{ij a} + n^b \\Pi_{ba}\n * - \\frac{1}{2} \\gamma_a{}^i g^{bc} \\Phi_{i b c}\n * - \\frac{1}{2} n_a g^{bc} \\Pi_{b c}.\n * \\f].\n *\n * Therefore, the Stress-energy contribution can be calculated from the\n * trace-reversed contribution appearing in\n * `grmhd::GhValenciaDivClean::add_stress_energy_term_to_dt_pi` -- the\n * trace reversal in that function and the trace-reversal that appears\n * explicitly in \\f${\\cal C}_a\\f$ cancel.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> f_constraint(\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> f_constraint(\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::aa<DataType, SpatialDim, Frame>& trace_reversed_stress_energy);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid f_constraint(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_function,\n    const tnsr::ab<DataType, SpatialDim, Frame>& spacetime_d_gauge_function,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const Scalar<DataType>& gamma2,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::aa<DataType, SpatialDim, Frame>& trace_reversed_stress_energy);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the generalized-harmonic (unnormalized) constraint energy.\n *\n * \\details Computes the generalized-harmonic unnormalized constraint energy\n * [Eq. (53) of \\cite Lindblom2005qh with \\f$m^{ab}=\\delta^{ab}\\f$ and with each\n * term in the sum scaled by an arbitrary coefficient],\n * \\f{eqnarray}{\n * E & = & K_1 C_a C_a + K_2\\left(F_a F_a\n     + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n     & + & K_3 C_{iab} C_{jab} \\gamma^{ij} + K_4 C_{ikab} C_{jlab}\\gamma^{ij}\n \\gamma^{kl}.\n * \\f}\n * Here \\f$C_a\\f$ is the gauge constraint, \\f$F_a\\f$ is the f constraint,\n * \\f$C_{ia}\\f$ is the two-index constraint, \\f$C_{iab}\\f$ is the\n * three-index constraint, \\f$C_{ikab}\\f$ is the four-index constraint,\n * \\f$\\gamma^{ij}\\f$ is the inverse spatial metric, and\n * \\f$K_1\\f$, \\f$K_2\\f$, \\f$K_3\\f$, and \\f$K_4\\f$ are constant multipliers\n * for each term that each default to a value of 1.0. Note that in this\n * equation, spacetime indices \\f$a,b\\f$ are raised and lowered with\n * the Kronecker delta.\n *\n * Also note that the argument `four_index_constraint` is a rank-3 tensor.\n * This is because `gh::four_index_constraint()` takes\n * advantage of the antisymmetry of the four-index constraint's first two\n * indices to only compute and return the independent\n * components of \\f$C_{ijab}\\f$, which can be written as\n * \\f{eqnarray}{\n * D_{iab} \\equiv \\frac{1}{2} \\epsilon_{i}{}^{jk} C_{jkab},\n * \\f} where \\f$\\epsilon_{ijk}\\f$ is the flat-space Levi-Civita tensor,\n * whose inidces are raised and lowered with the Kronecker delta.\n * The result is\n * \\f{eqnarray}{\n * E & = & K_1 C_a C_a + K_2\\left(F_a F_a\n *       + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *   & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n     & + & 2 K_4 \\gamma D_{iab} D_{jab} \\gamma^{ij},\n * \\f} where \\f$\\gamma\\f$ is the determinant of the spatial metric.\n *\n * To derive this expression for the constraint energy implemented here,\n * Eq.~(53) of \\cite Lindblom2005qh is\n * \\f{eqnarray}{\n * S_{AB} dc^A dc^B &=&\n *      m^{ab}\\Bigl[d F_ad F_b\n *      +\\gamma^{ij}\\bigl(d C_{ia}d C_{jb}\n *      +\\gamma^{kl}m^{cd}d C_{ikac}d C_{jlbd}\\bigr)\n * \\nonumber\\\\\n *      & + & \\Lambda^2\\bigl(d C_ad C_b\n *      +\\gamma^{ij}m^{cd}d C_{iac}d C_{jbd}\\bigr)\n * \\Bigr].\n * \\f} Replace \\f$dc^A\\rightarrow c^A\\f$ to get\n * \\f{eqnarray}{\n * E&=&\n *      m^{ab}\\Bigl[ F_a F_b\n *      +\\gamma^{ij}\\bigl( C_{ia} C_{jb}\n *      +\\gamma^{kl}m^{cd} C_{ikac} C_{jlbd}\\bigr)\n * \\nonumber\\\\\n *      & + & \\Lambda^2\\bigl( C_a C_b\n *      +\\gamma^{ij}m^{cd} C_{iac} C_{jbd}\\bigr)\n * \\Bigr]\\nonumber\\\\\n * &=&\n *      m^{ab} F_a F_b\n *      +m^{ab}\\gamma^{ij} C_{ia} C_{jb}\n *      +m^{ab}\\gamma^{ij} \\gamma^{kl}m^{cd} C_{ikac} C_{jlbd}\n * \\nonumber\\\\\n *      & + & m^{ab}\\Lambda^2 C_a C_b\n *      +m^{ab}\\Lambda^2 \\gamma^{ij}m^{cd} C_{iac} C_{jbd}.\n * \\f} Here \\f$m^{ab}\\f$ is an arbitrary positive-definite matrix, and\n * \\f$\\Lambda\\f$ is an arbitrary real scalar.\n * Choose \\f$m^{ab} = \\delta^{ab}\\f$ but allow an arbitrary coefficient to be\n * placed in front of each term. Then, absorb \\f$\\Lambda^2\\f$ into one of\n * these coefficients, to get\n * \\f{eqnarray}{ E &=& K_\n * F\\delta^{ab} F_a F_b +K_2\\delta^{ab}\\gamma^{ij} C_{ia} C_{jb}\n +K_4\\delta^{ab}\\gamma^{ij}\n * \\gamma^{kl}\\delta^{cd} C_{ikac} C_{jlbd}\n * \\nonumber\\\\\n *      & + & K_1\\delta^{ab} C_a C_b\n *      +K_3\\delta^{ab} \\gamma^{ij}\\delta^{cd} C_{iac} C_{jbd}.\n * \\f}\n * Adopting a Euclidean norm for the constraint space (i.e., choosing to raise\n and\n * lower spacetime indices with Kronecker deltas) gives\n * \\f{eqnarray}{ E &=& K_ F\n * F_a F_a +K_2\\gamma^{ij} C_{ia} C_{ja} +K_4 \\gamma^{ij} \\gamma^{kl} C_{ikac}\n C_{jlac}\n * \\nonumber\\\\\n *      & + & K_1 C_a C_a\n *      +K_3\\gamma^{ij} C_{iac} C_{jac}.\n * \\f} The two-index constraint and f constraint can be viewed as the\n * time and space components of a combined spacetime constraint. So next\n * choose\n * \\f$K_ F=K_2\\f$, giving \\f{eqnarray}{ E&=& K_1 C_a C_a + K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\n *      + K_4 C_{ikab} C_{jlab}\\gamma^{ij} \\gamma^{kl}.\n * \\f}\n *\n * Note that \\f$C_{ikab}\\f$ is antisymmetric on the first two indices. Next,\n * replace the four-index constraint \\f$C_{ijab}\\f$ with \\f$D_{iab}\\f$, which\n * contains the independent components of \\f$C_{ijab}\\f$. Specifically,\n * \\f{eqnarray}{\n * D_{iab} \\equiv \\frac{1}{2} \\epsilon_{i}{}^{jk} C_{jkab}.\n * \\f} The inverse relationship is\n * \\f{eqnarray}{\n * C_{jkab} = \\epsilon^{i}{}_{jk} D_{iab},\n * \\f} where \\f$\\epsilon_{ijk}\\f$ is the flat-space Levi-Civita tensor, whose\n * indices are raised and lowered with the Kronecker delta. Inserting this\n relation\n * to replace \\f$C_{jkab}\\f$ with \\f$D_{iab}\\f$ gives \\f{eqnarray}{ E &=& K_1\n C_a\n * C_a + K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n *      & + & K_4 D_{mab} D_{nab} \\epsilon^{m}{}_{ik}\n *      \\epsilon^{n}{}_{jl} \\gamma^{ij} \\gamma^{kl}. \\f}\n *\n * There's a subtle point here: \\f$\\gamma^{ij}\\f$ is the inverse spatial metric,\n which\n * is not necessarily flat. But \\f$\\epsilon^{i}{}_{jk}\\f$ is the flat space\n * Levi-Civita tensor. In order to raise and lower indices of the Levi-Civita\n * tensor with the inverse spatial metrics, put in the appropriate factors of\n * \\f$\\sqrt{\\gamma}\\f$, where \\f$\\gamma\\f$ is the metric determinant, to make\n the\n * curved-space Levi-Civita tensor compatible with \\f$\\gamma^{ij}\\f$. Let\n * \\f$\\varepsilon^{ijk}\\f$ represent the curved space Levi-Civita tensor\n compatible\n * with \\f$\\gamma^{ij}\\f$: \\f{eqnarray}{\n * \\varepsilon^{mik} = \\gamma^{-1/2} \\epsilon^{mik}\\\\\n * \\varepsilon_{mik} = \\gamma^{1/2} \\epsilon_{mik}.\n * \\f} Then we can write the constraint energy as\n * \\f{eqnarray}{\n * E &=& K_1 C_a C_a + K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n *      & + & K_4 D_{mab} D_{nab} \\gamma \\gamma^{-1/2}\\epsilon^{m}{}_{ik}\n * \\gamma^{-1/2}\\epsilon^{n}{}_{jl} \\gamma^{ij} \\gamma^{kl}. \\f} The factors of\n * \\f$\\gamma^{-1/2}\\f$ make the Levi-Civita tensor compatible with\n \\f$\\gamma^{ij}\\f$.\n * Swapping which summed indices are raised and which are lowered gives\n * \\f{eqnarray}{ E &=& K_1 C_a\n C_a +\n * K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n *      & + & K_4 D_{mab} D_{nab} \\gamma \\gamma^{-1/2}\\epsilon^{mik}\n \\gamma^{-1/2}\\epsilon^{njl}\n * \\gamma_{ij} \\gamma_{kl}, \\f} or \\f{eqnarray}{ E &=& K_1 C_a C_a +\n K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n *      & + & K_4 D_{mab} D_{nab} \\gamma \\varepsilon^{mik} \\varepsilon^{njl}\n \\gamma_{ij}\n * \\gamma_{kl}, \\f} or, reversing up and down repeated indices again,\n * \\f{eqnarray}{ E\n * &=& K_1 C_a C_a + K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n *      & + & K_4 D_{mab} D_{nab} \\gamma \\varepsilon^{m}{}_{ik}\n \\varepsilon^{n}{}_{jl}\n * \\gamma^{ij} \\gamma^{kl}. \\f}\n *\n * The metric raises and lowers the indices of \\f$\\varepsilon^{ijk}\\f$,\n * so this can\n * be written as \\f{eqnarray}{ E &=& K_1 C_a C_a + K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n *      & + & K_4 \\gamma D_{mab} D^{n}{}_{ab} \\varepsilon^{mjl}\n \\varepsilon_{njl}.\n * \\f}\n *\n * Now, in flat space (Eq. (1.23) of \\cite ThorneBlandford2017),\n * \\f{eqnarray}{\n * \\epsilon^{mjl} \\epsilon_{njl} = \\delta^{mj}_{nj} = \\delta^m_n \\delta^j_j -\n * \\delta^m_j \\delta^j_n = 2 \\delta^m_n. \\f} But this holds for curved space\n * as well: multiply the left hand side by \\f$1 = \\gamma^{1/2} \\gamma^{-1/2}\\f$\n to get\n * \\f{eqnarray}{\n * \\gamma^{-1/2}\\epsilon^{mjl} \\gamma^{1/2}\\epsilon_{njl} = \\varepsilon^{mjl}\n * \\varepsilon_{njl} = \\delta^{mj}_{nj} = \\delta^m_n \\delta^j_j - \\delta^m_j\n * \\delta^j_n = 2 \\delta^m_n. \\f} So the constraint energy is \\f{eqnarray}{ E\n &=&\n * K_1 C_a C_a + K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n *      & + & 2 K_4 D_{mab} D^{n}{}_{ab} \\delta^m_n.\n * \\f}\n * Simplifying gives the formula implemented here:\n * \\f{eqnarray}{\n * E &=& K_1 C_a C_a + K_2\\left(F_a F_a\n *      + C_{ia} C_{ja} \\gamma^{ij}\\right)\\nonumber\\\\\n *      & + & K_3 C_{iab} C_{jab} \\gamma^{ij}\\nonumber\\\\\n *      & + & 2 K_4 \\gamma D_{iab} D_{jab} \\gamma^{ij}.\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> constraint_energy(\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& f_constraint,\n    const tnsr::ia<DataType, SpatialDim, Frame>& two_index_constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& four_index_constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const Scalar<DataType>& spatial_metric_determinant,\n    double gauge_constraint_multiplier = 1.0,\n    double two_index_constraint_multiplier = 1.0,\n    double three_index_constraint_multiplier = 1.0,\n    double four_index_constraint_multiplier = 1.0);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid constraint_energy(\n    gsl::not_null<Scalar<DataType>*> energy,\n    const tnsr::a<DataType, SpatialDim, Frame>& gauge_constraint,\n    const tnsr::a<DataType, SpatialDim, Frame>& f_constraint,\n    const tnsr::ia<DataType, SpatialDim, Frame>& two_index_constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& three_index_constraint,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& four_index_constraint,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const Scalar<DataType>& spatial_metric_determinant,\n    double gauge_constraint_multiplier = 1.0,\n    double two_index_constraint_multiplier = 1.0,\n    double three_index_constraint_multiplier = 1.0,\n    double four_index_constraint_multiplier = 1.0);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes the generalized-harmonic normalized constraint energy.\n *\n * \\details Computes the generalized-harmonic normalized constraint energy\n * integrand [Eq. (70) of \\cite Lindblom2005qh with \\f$m^{ab}=\\delta^{ab}\\f$],\n * \\f{eqnarray}{\n * ||E||=\\gamma^{ij}\\delta^{ab}\\delta^{cd}\n * (\\Lambda^{2}\\partial_{i}g_{ac}\\partial_{j}g_{bd}\n * + \\partial_{i}\\Pi_{ac}\\partial_{j}\\Pi_{bd} +\n * \\gamma^{kl}\\partial_{i}\\Phi_{kac}\\partial_{j}\\Phi_{lbd})\\gamma^{1/2}\n * \\f}\n *\n * Here \\f$\\gamma^{ij}\\f$ is the inverse spatial metric, and \\f$\\Lambda^{2}\\f$\n * is the squared dimensional constant. Note\n * that in this equation, spacetime indices \\f$a,b\\f$ are raised and lowered\n * with the Kronecker delta.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> constraint_energy_normalization(\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const Scalar<DataType>& sqrt_spatial_metric_determinant,\n    double dimensional_constant);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid constraint_energy_normalization(\n    gsl::not_null<Scalar<DataType>*> energy_norm,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const Scalar<DataType>& sqrt_spatial_metric_determinant,\n    double dimensional_constant);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get the gauge constraint for the\n * generalized harmonic evolution system.\n *\n * \\details See `gauge_constraint()`. Can be retrieved using\n * `gh::Tags::GaugeConstraint`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct GaugeConstraintCompute : GaugeConstraint<DataVector, SpatialDim, Frame>,\n                                db::ComputeTag {\n  using argument_tags = tmpl::list<\n      GaugeH<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalOneForm<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, SpatialDim, Frame>,\n      Pi<DataVector, SpatialDim, Frame>, Phi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::a<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*>,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::II<DataVector, SpatialDim, Frame>&,\n      const tnsr::AA<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &gauge_constraint<DataVector, SpatialDim, Frame>);\n\n  using base = GaugeConstraint<DataVector, SpatialDim, Frame>;\n};\n\n/*!\n * \\brief Compute item to get the F-constraint for the\n * generalized harmonic evolution system.\n *\n * \\details See `f_constraint()`. Can be retrieved using\n * `gh::Tags::FConstraint`.\n *\n * \\note If the system contains matter, you will need to use a system-specific\n * version of this compute tag that passes the appropriate stress-energy tensor\n * to the F-constraint calculation.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct FConstraintCompute : FConstraint<DataVector, SpatialDim, Frame>,\n                            db::ComputeTag {\n  using argument_tags = tmpl::list<\n      GaugeH<DataVector, SpatialDim, Frame>,\n      SpacetimeDerivGaugeH<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalOneForm<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, SpatialDim, Frame>,\n      Pi<DataVector, SpatialDim, Frame>, Phi<DataVector, SpatialDim, Frame>,\n      ::Tags::deriv<Pi<DataVector, SpatialDim, Frame>, tmpl::size_t<SpatialDim>,\n                    Frame>,\n      ::Tags::deriv<Phi<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      ::gh::Tags::ConstraintGamma2,\n      ThreeIndexConstraint<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::a<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*>,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::ab<DataVector, SpatialDim, Frame>&,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::II<DataVector, SpatialDim, Frame>&,\n      const tnsr::AA<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::ijaa<DataVector, SpatialDim, Frame>&,\n      const Scalar<DataVector>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &f_constraint<DataVector, SpatialDim, Frame>);\n\n  using base = FConstraint<DataVector, SpatialDim, Frame>;\n};\n\n/*!\n * \\brief Compute item to get the two-index constraint for the\n * generalized harmonic evolution system.\n *\n * \\details See `two_index_constraint()`. Can be retrieved using\n * `gh::Tags::TwoIndexConstraint`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct TwoIndexConstraintCompute\n    : TwoIndexConstraint<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<\n      SpacetimeDerivGaugeH<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalOneForm<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, SpatialDim, Frame>,\n      Pi<DataVector, SpatialDim, Frame>, Phi<DataVector, SpatialDim, Frame>,\n      ::Tags::deriv<Pi<DataVector, SpatialDim, Frame>, tmpl::size_t<SpatialDim>,\n                    Frame>,\n      ::Tags::deriv<Phi<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      ::gh::Tags::ConstraintGamma2,\n      ThreeIndexConstraint<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::ia<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ia<DataVector, SpatialDim, Frame>*>,\n      const tnsr::ab<DataVector, SpatialDim, Frame>&,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::II<DataVector, SpatialDim, Frame>&,\n      const tnsr::AA<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::ijaa<DataVector, SpatialDim, Frame>&,\n      const Scalar<DataVector>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &two_index_constraint<DataVector, SpatialDim, Frame>);\n\n  using base = TwoIndexConstraint<DataVector, SpatialDim, Frame>;\n};\n\n/*!\n * \\brief Compute item to get the three-index constraint for the\n * generalized harmonic evolution system.\n *\n * \\details See `three_index_constraint()`. Can be retrieved using\n * `gh::Tags::ThreeIndexConstraint`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct ThreeIndexConstraintCompute\n    : ThreeIndexConstraint<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<\n      ::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      Phi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::iaa<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::iaa<DataVector, SpatialDim, Frame>*>,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &three_index_constraint<DataVector, SpatialDim, Frame>);\n\n  using base = ThreeIndexConstraint<DataVector, SpatialDim, Frame>;\n};\n\n/*!\n * \\brief Compute item to get the four-index constraint for the\n * generalized harmonic evolution system.\n *\n * \\details See `four_index_constraint()`. Can be retrieved using\n * `gh::Tags::FourIndexConstraint`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct FourIndexConstraintCompute\n    : FourIndexConstraint<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::Tags::deriv<Phi<DataVector, SpatialDim, Frame>,\n                               tmpl::size_t<SpatialDim>, Frame>>;\n\n  using return_type = tnsr::iaa<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::iaa<DataVector, SpatialDim, Frame>*>,\n      const tnsr::ijaa<DataVector, SpatialDim, Frame>&)>(\n      &four_index_constraint<DataVector, SpatialDim, Frame>);\n\n  using base = FourIndexConstraint<DataVector, SpatialDim, Frame>;\n};\n\n/*!\n * \\brief Compute item to get combined energy in all constraints for the\n * generalized harmonic evolution system.\n *\n * \\details See `constraint_energy()`. Can be retrieved using\n * `gh::Tags::ConstraintEnergy`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct ConstraintEnergyCompute\n    : ConstraintEnergy<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<GaugeConstraint<DataVector, SpatialDim, Frame>,\n                 FConstraint<DataVector, SpatialDim, Frame>,\n                 TwoIndexConstraint<DataVector, SpatialDim, Frame>,\n                 ThreeIndexConstraint<DataVector, SpatialDim, Frame>,\n                 FourIndexConstraint<DataVector, SpatialDim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, Frame>,\n                 gr::Tags::DetSpatialMetric<DataVector>>;\n\n  using return_type = Scalar<DataVector>;\n\n  static constexpr auto function(\n      const gsl::not_null<Scalar<DataVector>*> energy,\n      const tnsr::a<DataVector, SpatialDim, Frame>& gauge_constraint,\n      const tnsr::a<DataVector, SpatialDim, Frame>& f_constraint,\n      const tnsr::ia<DataVector, SpatialDim, Frame>& two_index_constraint,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>& three_index_constraint,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>& four_index_constraint,\n      const tnsr::II<DataVector, SpatialDim, Frame>& inverse_spatial_metric,\n      const Scalar<DataVector>& spatial_metric_determinant) {\n    set_number_of_grid_points(energy, spatial_metric_determinant);\n    constraint_energy<DataVector, SpatialDim, Frame>(\n        energy, gauge_constraint, f_constraint, two_index_constraint,\n        three_index_constraint, four_index_constraint, inverse_spatial_metric,\n        spatial_metric_determinant);\n  }\n\n  using base = ConstraintEnergy<DataVector, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/DuDtTempTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\nnamespace Tags {\n/// \\f$\\gamma_1 \\gamma_2\\f$ constraint damping product\nstruct Gamma1Gamma2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// \\f$0.5\\Pi_{ab}n^an^b\\f$\nstruct HalfPiTwoNormals : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// \\f$n^a \\mathcal{C}_a\\f$\nstruct NormalDotOneIndexConstraint : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// \\f$\\gamma_1 + 1\\f$\nstruct Gamma1Plus1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// \\f$\\Pi_{ab}n^a\\f$\ntemplate <size_t Dim>\nstruct PiOneNormal : db::SimpleTag {\n  using type = tnsr::a<DataVector, Dim, Frame::Inertial>;\n};\n\n/// \\f$0.5\\Phi_{iab}n^an^b\\f$\ntemplate <size_t Dim>\nstruct HalfPhiTwoNormals : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\n/// \\f$\\beta^i \\mathcal{C}_{iab}\\f$\ntemplate <size_t Dim>\nstruct ShiftDotThreeIndexConstraint : db::SimpleTag {\n  using type = tnsr::aa<DataVector, Dim, Frame::Inertial>;\n};\n\n/// \\f$\\v^i_g \\mathcal{C}_{iab}\\f$\ntemplate <size_t Dim>\nstruct MeshVelocityDotThreeIndexConstraint : db::SimpleTag {\n  using type = tnsr::aa<DataVector, Dim, Frame::Inertial>;\n};\n\n/// \\f$\\Phi_{iab}n^a\\f$\ntemplate <size_t Dim>\nstruct PhiOneNormal : db::SimpleTag {\n  using type = tnsr::ia<DataVector, Dim, Frame::Inertial>;\n};\n\n/// \\f$\\Pi_a{}^b\\f$\ntemplate <size_t Dim>\nstruct PiSecondIndexUp : db::SimpleTag {\n  using type = tnsr::aB<DataVector, Dim, Frame::Inertial>;\n};\n\n/// \\f$\\Phi^i{}_{ab}\\f$\ntemplate <size_t Dim>\nstruct PhiFirstIndexUp : db::SimpleTag {\n  using type = tnsr::Iaa<DataVector, Dim, Frame::Inertial>;\n};\n\n/// \\f$\\Phi_{ia}{}^b\\f$\ntemplate <size_t Dim>\nstruct PhiThirdIndexUp : db::SimpleTag {\n  using type = tnsr::iaB<DataVector, Dim, Frame::Inertial>;\n};\n\n/// \\f$\\Gamma_{ab}{}^c\\f$\ntemplate <size_t Dim>\nstruct SpacetimeChristoffelFirstKindThirdIndexUp : db::SimpleTag {\n  using type = tnsr::abC<DataVector, Dim, Frame::Inertial>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/Equations.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gh {\ntemplate <size_t Dim>\nvoid ComputeNormalDotFluxes<Dim>::apply(\n    const gsl::not_null<tnsr::aa<DataVector, Dim>*>\n        spacetime_metric_normal_dot_flux,\n    const gsl::not_null<tnsr::aa<DataVector, Dim>*> pi_normal_dot_flux,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim>*> phi_normal_dot_flux,\n    const tnsr::aa<DataVector, Dim>& spacetime_metric) {\n  set_number_of_grid_points(pi_normal_dot_flux, spacetime_metric);\n  set_number_of_grid_points(phi_normal_dot_flux, spacetime_metric);\n  set_number_of_grid_points(spacetime_metric_normal_dot_flux, spacetime_metric);\n  for (size_t storage_index = 0; storage_index < pi_normal_dot_flux->size();\n       ++storage_index) {\n    (*pi_normal_dot_flux)[storage_index] = 0.0;\n    (*spacetime_metric_normal_dot_flux)[storage_index] = 0.0;\n  }\n\n  for (size_t storage_index = 0; storage_index < phi_normal_dot_flux->size();\n       ++storage_index) {\n    (*phi_normal_dot_flux)[storage_index] = 0.0;\n  }\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data) \\\n  template struct gh::ComputeNormalDotFluxes<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class template GeneralizedHarmonicEquations.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gh {\n/*!\n * \\brief Set the normal dot the flux to zero since the generalized harmonic\n * system has no fluxes and they're currently still needed for the evolution\n * scheme.\n */\ntemplate <size_t Dim>\nstruct ComputeNormalDotFluxes {\n public:\n  using argument_tags = tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>>;\n\n  static void apply(\n      gsl::not_null<tnsr::aa<DataVector, Dim>*>\n          spacetime_metric_normal_dot_flux,\n      gsl::not_null<tnsr::aa<DataVector, Dim>*> pi_normal_dot_flux,\n      gsl::not_null<tnsr::iaa<DataVector, Dim>*> phi_normal_dot_flux,\n      const tnsr::aa<DataVector, Dim>& spacetime_metric);\n};\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::gauges {\nAnalyticChristoffel::AnalyticChristoffel(const AnalyticChristoffel& rhs)\n    : GaugeCondition{dynamic_cast<const GaugeCondition&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\nAnalyticChristoffel& AnalyticChristoffel::operator=(\n    const AnalyticChristoffel& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\nAnalyticChristoffel::AnalyticChristoffel(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\nAnalyticChristoffel::AnalyticChristoffel(CkMigrateMessage* const msg)\n    : GaugeCondition(msg) {}\n\nvoid AnalyticChristoffel::pup(PUP::er& p) {\n  GaugeCondition::pup(p);\n  p | analytic_prescription_;\n}\n\nstd::unique_ptr<GaugeCondition> AnalyticChristoffel::get_clone() const {\n  return std::make_unique<AnalyticChristoffel>(*this);\n}\n\ntemplate <size_t SpatialDim>\nvoid AnalyticChristoffel::gauge_and_spacetime_derivative_impl(\n    const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame::Inertial>*>\n        gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame::Inertial>*>\n        d4_gauge_h,\n    const Mesh<SpatialDim>& mesh,\n    const InverseJacobian<DataVector, SpatialDim, Frame::ElementLogical,\n                          Frame::Inertial>& inverse_jacobian,\n    const tuples::tagged_tuple_from_typelist<solution_tags<SpatialDim>>&\n        solution_vars) const {\n  const auto [pi, phi, spacetime_metric, lapse, shift, spatial_metric] =\n      solution_vars;\n  // Now compute Gamma_a\n  Variables<\n      tmpl::list<gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim>,\n                 gr::Tags::SpacetimeNormalOneForm<DataVector, SpatialDim>,\n                 gr::Tags::InverseSpatialMetric<DataVector, SpatialDim>,\n                 gr::Tags::InverseSpacetimeMetric<DataVector, SpatialDim>>>\n      temp_vars(mesh.number_of_grid_points());\n  auto& spacetime_normal_vector =\n      get<gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim>>(temp_vars);\n  auto& spacetime_normal_one_form =\n      get<gr::Tags::SpacetimeNormalOneForm<DataVector, SpatialDim>>(temp_vars);\n  auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim>>(temp_vars);\n  auto& inverse_spacetime_metric =\n      get<gr::Tags::InverseSpacetimeMetric<DataVector, SpatialDim>>(temp_vars);\n  {\n    Scalar<DataVector> det_buffer{};\n    get(det_buffer)\n        .set_data_ref(make_not_null(&get<0>(spacetime_normal_vector)));\n    determinant_and_inverse(make_not_null(&det_buffer),\n                            make_not_null(&inverse_spatial_metric),\n                            spatial_metric);\n    determinant_and_inverse(make_not_null(&det_buffer),\n                            make_not_null(&inverse_spacetime_metric),\n                            spacetime_metric);\n  }\n  gr::spacetime_normal_one_form(make_not_null(&spacetime_normal_one_form),\n                                lapse);\n  gr::spacetime_normal_vector(make_not_null(&spacetime_normal_vector), lapse,\n                              shift);\n  gh::trace_christoffel(gauge_h, spacetime_normal_one_form,\n                        spacetime_normal_vector, inverse_spatial_metric,\n                        inverse_spacetime_metric, pi, phi);\n  for (auto& component : *gauge_h) {\n    component *= -1.0;\n  }\n\n  tnsr::ia<DataVector, SpatialDim, Frame::Inertial> di_gauge_h{};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      di_gauge_h.get(i, a).set_data_ref(\n          make_not_null(&d4_gauge_h->get(i + 1, a)));\n    }\n  }\n  partial_derivative(make_not_null(&di_gauge_h), *gauge_h, mesh,\n                     inverse_jacobian);\n  // Set time derivative to zero. We are assuming a static solution.\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    d4_gauge_h->get(0, a) = 0.0;\n  }\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID AnalyticChristoffel::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void AnalyticChristoffel::gauge_and_spacetime_derivative_impl(    \\\n      const gsl::not_null<tnsr::a<DataVector, DIM(data), Frame::Inertial>*>  \\\n          gauge_h,                                                           \\\n      const gsl::not_null<tnsr::ab<DataVector, DIM(data), Frame::Inertial>*> \\\n          d4_gauge_h,                                                        \\\n      const Mesh<DIM(data)>& mesh,                                           \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,    \\\n                            Frame::Inertial>& inverse_jacobian,              \\\n      const tuples::tagged_tuple_from_typelist<solution_tags<DIM(data)>>&    \\\n          solution_vars) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace gh::gauges {\n/*!\n * \\brief Imposes the analytic gauge condition,\n * \\f$H_a=\\Gamma_a^{\\mathrm{analytic}}\\f$ from an analytic solution or analytic\n * data.\n *\n * \\warning Assumes \\f$\\partial_t \\Gamma_a=0\\f$ i.e. the solution is static or\n * in harmonic gauge.\n */\nclass AnalyticChristoffel final : public GaugeCondition {\n private:\n  template <size_t SpatialDim>\n  using solution_tags =\n      tmpl::list<gh::Tags::Pi<DataVector, SpatialDim>,\n                 gh::Tags::Phi<DataVector, SpatialDim>,\n                 gr::Tags::SpacetimeMetric<DataVector, SpatialDim>,\n                 gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, SpatialDim>,\n                 gr::Tags::SpatialMetric<DataVector, SpatialDim>>;\n\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n\n  using options = tmpl::list<AnalyticPrescription>;\n\n  static constexpr Options::String help{\n      \"Apply the analytic gauge condition H_a = Gamma_a, where Gamma_a comes \"\n      \"from the AnalyticPrescription.\"};\n\n  AnalyticChristoffel() = default;\n  AnalyticChristoffel(const AnalyticChristoffel&);\n  AnalyticChristoffel& operator=(const AnalyticChristoffel&);\n  AnalyticChristoffel(AnalyticChristoffel&&) = default;\n  AnalyticChristoffel& operator=(AnalyticChristoffel&&) = default;\n  ~AnalyticChristoffel() override = default;\n\n  explicit AnalyticChristoffel(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  /// \\cond\n  explicit AnalyticChristoffel(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(AnalyticChristoffel);  // NOLINT\n  /// \\endcond\n\n  template <size_t SpatialDim, class AllSolutionsForChristoffelAnalytic>\n  void gauge_and_spacetime_derivative(\n      const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame::Inertial>*>\n          gauge_h,\n      const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame::Inertial>*>\n          d4_gauge_h,\n      const Mesh<SpatialDim>& mesh, const double time,\n      const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& inertial_coords,\n      const InverseJacobian<DataVector, SpatialDim, Frame::ElementLogical,\n                            Frame::Inertial>& inverse_jacobian,\n      const AllSolutionsForChristoffelAnalytic /*meta*/) const {\n    // Use .get() to avoid Clang-13 bug: with -Og in C++20 mode, the compiler\n    // rewrites unique_ptr != nullptr via std::operator== but fails to emit the\n    // out-of-line instantiation, causing a linker error.\n    ASSERT(analytic_prescription_.get() != nullptr,\n           \"The analytic prescription cannot be nullptr.\");\n    const auto solution_vars = call_with_dynamic_type<\n        tuples::tagged_tuple_from_typelist<solution_tags<SpatialDim>>,\n        AllSolutionsForChristoffelAnalytic>(\n        analytic_prescription_.get(),\n        [&inertial_coords, &time](const auto* const analytic_solution_or_data) {\n          if constexpr (is_analytic_solution_v<std::decay_t<\n                            decltype(*analytic_solution_or_data)>>) {\n            return analytic_solution_or_data->variables(\n                inertial_coords, time, solution_tags<SpatialDim>{});\n\n          } else {\n            (void)time;\n            return analytic_solution_or_data->variables(\n                inertial_coords, solution_tags<SpatialDim>{});\n          }\n        });\n    gauge_and_spacetime_derivative_impl(gauge_h, d4_gauge_h, mesh,\n                                        inverse_jacobian, solution_vars);\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  std::unique_ptr<GaugeCondition> get_clone() const override;\n\n private:\n  template <size_t SpatialDim>\n  void gauge_and_spacetime_derivative_impl(\n      gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame::Inertial>*> gauge_h,\n      gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame::Inertial>*>\n          d4_gauge_h,\n      const Mesh<SpatialDim>& mesh,\n      const InverseJacobian<DataVector, SpatialDim, Frame::ElementLogical,\n                            Frame::Inertial>& inverse_jacobian,\n      const tuples::tagged_tuple_from_typelist<solution_tags<SpatialDim>>&\n          solution_vars) const;\n\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AnalyticChristoffel.cpp\n  DampedHarmonic.cpp\n  DampedWaveHelpers.cpp\n  DhGaugeParameters.cpp\n  Dispatch.cpp\n  Gauges.cpp\n  HalfPiPhiTwoNormals.cpp\n  Harmonic.cpp\n  RegisterDerived.cpp\n  SetPiAndPhiFromConstraints.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticChristoffel.hpp\n  DampedHarmonic.hpp\n  DampedWaveHelpers.hpp\n  DhGaugeParameters.hpp\n  Dispatch.hpp\n  Dispatch.tpp\n  Factory.hpp\n  Gauges.hpp\n  HalfPiPhiTwoNormals.hpp\n  Harmonic.hpp\n  RegisterDerived.hpp\n  SetPiAndPhiFromConstraints.hpp\n  SetPiAndPhiFromConstraints.tpp\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Blaze/IntegerPow.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedWaveHelpers.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/HalfPiPhiTwoNormals.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfDetSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::gauges {\nnamespace DampedHarmonicGauge_detail {\n// Roll-on function for the damped harmonic gauge.\n//\n// For times after \\f$t_0\\f$, compute the roll-on function\n// \\f$ R(t) = 1 - \\exp(-((t - t_0)/\\sigma_t)^4]) \\f$,\n// and return \\f$ R(t) = 0\\f$ at times before.\ndouble roll_on_function(const double time, const double t_start,\n                        const double sigma_t) {\n  if (time < t_start) {\n    return 0.;\n  }\n  return 1. - exp(-pow<4>((time - t_start) / sigma_t));\n}\n\n// Time derivative of the damped harmonic gauge roll-on function.\n//\n// \\details Compute the time derivative:\n// \\f{align*}\n// \\partial_0 R(t) = 4 exp[-((t - t0)/\\sigma_t)^4] (t - t0)^3 / sigma_t^4\ndouble time_deriv_of_roll_on_function(const double time, const double t_start,\n                                      const double sigma_t) {\n  if (time < t_start) {\n    return 0.;\n  }\n  const double time_since_start_over_sigma = (time - t_start) / sigma_t;\n  return exp(-pow<4>(time_since_start_over_sigma)) * 4. *\n         pow<3>(time_since_start_over_sigma) / sigma_t;\n}\n}  // namespace DampedHarmonicGauge_detail\n\nnamespace {\ntemplate <bool UseRollon, size_t SpatialDim, typename Frame>\nvoid damped_harmonic_impl(\n    const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*> gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame>*> d4_gauge_h,\n    const tnsr::a<DataVector, SpatialDim, Frame>* gauge_h_init,\n    const tnsr::ab<DataVector, SpatialDim, Frame>* dgauge_h_init,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::abb<DataVector, SpatialDim, Frame>& d4_spacetime_metric,\n    const Scalar<DataVector>& half_pi_two_normals,\n    const tnsr::i<DataVector, SpatialDim, Frame>& half_phi_two_normals,\n    const tnsr::aa<DataVector, SpatialDim, Frame>& spacetime_metric,\n    const tnsr::iaa<DataVector, SpatialDim, Frame>& phi, const double time,\n    const tnsr::I<DataVector, SpatialDim, Frame>& coords,\n    const double amp_coef_L1, const double amp_coef_L2, const double amp_coef_S,\n    const int exp_L1, const int exp_L2, const int exp_S,\n    const double rollon_start_time, const double rollon_width,\n    const double sigma_r) {\n  const size_t num_points = get(lapse).size();\n\n  if constexpr (UseRollon) {\n    ASSERT(gauge_h_init != nullptr,\n           \"Cannot call damped_harmonic_impl with UseRollon enabled and \"\n           \"gauge_h_init being nullptr\");\n    ASSERT(dgauge_h_init != nullptr,\n           \"Cannot call damped_harmonic_impl with UseRollon enabled and \"\n           \"dgauge_h_init being nullptr\");\n  } else {\n    ASSERT(gauge_h_init == nullptr,\n           \"Cannot call damped_harmonic_impl with UseRollon disabled and \"\n           \"gauge_h_init not being nullptr\");\n    ASSERT(dgauge_h_init == nullptr,\n           \"Cannot call damped_harmonic_impl with UseRollon disabled and \"\n           \"dgauge_h_init not being nullptr\");\n  }\n\n  // Use a TempBuffer to reduce total number of allocations. This is especially\n  // important in a multithreaded environment.\n  TempBuffer<tmpl::list<\n      ::Tags::Tempii<1, SpatialDim, Frame>,\n      ::Tags::Tempijj<4, SpatialDim, Frame>, ::Tags::TempScalar<5>,\n      ::Tags::TempScalar<6>, ::Tags::TempScalar<7>, ::Tags::TempScalar<8>,\n      ::Tags::TempScalar<9>, ::Tags::TempScalar<10>, ::Tags::TempScalar<11>,\n      ::Tags::TempScalar<12>, ::Tags::TempScalar<13>, ::Tags::TempScalar<14>,\n      ::Tags::Tempa<15, SpatialDim, Frame>,\n      ::Tags::Tempa<16, SpatialDim, Frame>,\n      ::Tags::Tempa<17, SpatialDim, Frame>,\n      ::Tags::Tempa<18, SpatialDim, Frame>,\n      ::Tags::Tempa<19, SpatialDim, Frame>,\n      ::Tags::Tempa<20, SpatialDim, Frame>,\n      ::Tags::Tempa<21, SpatialDim, Frame>,\n      ::Tags::Tempa<22, SpatialDim, Frame>,\n      ::Tags::Tempa<23, SpatialDim, Frame>,\n      ::Tags::Tempa<24, SpatialDim, Frame>,\n      ::Tags::Tempa<27, SpatialDim, Frame>,\n      ::Tags::TempaB<30, SpatialDim, Frame>,\n      ::Tags::Tempa<31, SpatialDim, Frame>, ::Tags::TempScalar<32>,\n      ::Tags::Tempa<33, SpatialDim, Frame>,\n      ::Tags::Tempab<35, SpatialDim, Frame>,\n      ::Tags::Tempa<36, SpatialDim, Frame>,\n      ::Tags::Tempab<37, SpatialDim, Frame>, ::Tags::TempScalar<38>,\n      ::Tags::Tempa<39, SpatialDim, Frame>, ::Tags::TempScalar<40>,\n      ::Tags::Tempa<41, SpatialDim, Frame>, ::Tags::TempScalar<42>,\n      ::Tags::Tempa<43, SpatialDim, Frame>, ::Tags::TempScalar<44>,\n      ::Tags::TempScalar<45>, ::Tags::TempScalar<46>, ::Tags::TempScalar<47>>>\n      buffer(num_points);\n  auto& one_over_lapse = get<::Tags::TempScalar<5>>(buffer);\n  auto& log_fac_1 = get<::Tags::TempScalar<6>>(buffer);\n  auto& log_fac_2 = get<::Tags::TempScalar<7>>(buffer);\n  auto& weight = get<::Tags::TempScalar<8>>(buffer);\n  auto& mu_L1 = get<::Tags::TempScalar<9>>(buffer);\n  auto& mu_S = get<::Tags::TempScalar<10>>(buffer);\n  auto& mu_L2 = get<::Tags::TempScalar<11>>(buffer);\n  auto& mu_S_over_lapse = get<::Tags::TempScalar<12>>(buffer);\n  auto& mu1 = get<::Tags::TempScalar<13>>(buffer);\n  auto& mu2 = get<::Tags::TempScalar<14>>(buffer);\n  auto& d4_weight = get<::Tags::Tempa<15, SpatialDim, Frame>>(buffer);\n  auto& d4_RW_L1 = get<::Tags::Tempa<16, SpatialDim, Frame>>(buffer);\n  auto& d4_RW_S = get<::Tags::Tempa<17, SpatialDim, Frame>>(buffer);\n  auto& d4_RW_L2 = get<::Tags::Tempa<18, SpatialDim, Frame>>(buffer);\n  auto& d4_mu_S = get<::Tags::Tempa<19, SpatialDim, Frame>>(buffer);\n  auto& d4_mu1 = get<::Tags::Tempa<20, SpatialDim, Frame>>(buffer);\n  auto& d4_mu2 = get<::Tags::Tempa<21, SpatialDim, Frame>>(buffer);\n  auto& d4_log_fac_mu1 = get<::Tags::Tempa<22, SpatialDim, Frame>>(buffer);\n  auto& d4_log_fac_muS = get<::Tags::Tempa<23, SpatialDim, Frame>>(buffer);\n  auto& d4_log_fac_mu2 = get<::Tags::Tempa<24, SpatialDim, Frame>>(buffer);\n\n  auto& spacetime_metric_dot_shift =\n      get<::Tags::Tempa<31, SpatialDim, Frame>>(buffer);\n  auto& prefac = get<::Tags::TempScalar<32>>(buffer);\n  auto& d4_muS_over_lapse = get<::Tags::Tempa<33, SpatialDim, Frame>>(buffer);\n  auto& dT1 = get<::Tags::Tempab<35, SpatialDim, Frame>>(buffer);\n  auto& dT2 = get<::Tags::Tempa<36, SpatialDim, Frame>>(buffer);\n  auto& dT3 = get<::Tags::Tempab<37, SpatialDim, Frame>>(buffer);\n\n  auto& dt_lapse = get<::Tags::TempScalar<38>>(buffer);\n  auto& d_lapse_by_lapse = get<::Tags::Tempa<39, SpatialDim, Frame>>(buffer);\n  auto& det_spatial_metric = get<::Tags::TempScalar<40>>(buffer);\n  auto& d_g_by_det = get<::Tags::Tempa<41, SpatialDim, Frame>>(buffer);\n  auto& logfac = get<::Tags::TempScalar<42>>(buffer);\n  auto& d_logfac = get<::Tags::Tempa<43, SpatialDim, Frame>>(buffer);\n  auto& prefac_log = get<::Tags::TempScalar<44>>(buffer);\n\n  auto& pow1 = get<::Tags::TempScalar<45>>(buffer);\n  auto& pow2 = get<::Tags::TempScalar<46>>(buffer);\n  auto& pow3 = get<::Tags::TempScalar<47>>(buffer);\n\n  // 3+1 quantities\n  const tnsr::ii<DataVector, SpatialDim, Frame> spatial_metric{};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      make_const_view(make_not_null(&spatial_metric.get(i, j)),\n                      spacetime_metric.get(i + 1, j + 1), 0, num_points);\n    }\n  }\n  // We need \\f$ \\partial_a \\gamma_{bi} = \\partial_a g_{bi} \\f$. Here we\n  // use `derivatives_of_spacetime_metric` to get \\f$ \\partial_a g_{bc}\\f$\n  // instead, and use only the derivatives of \\f$ \\gamma_{bi}\\f$.\n  const tnsr::ii<DataVector, SpatialDim, Frame> d0_spatial_metric{};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      make_const_view(make_not_null(&d0_spatial_metric.get(i, j)),\n                      d4_spacetime_metric.get(0, i + 1, j + 1), 0, num_points);\n    }\n  }\n\n  // commonly used terms\n  constexpr auto exp_fac_1 = 0.5;\n  constexpr auto exp_fac_2 = 0.;\n  get(one_over_lapse) = 1. / get(lapse);\n  DampedHarmonicGauge_detail::log_factor_metric_lapse<DataVector>(\n      make_not_null(&log_fac_1), lapse, sqrt_det_spatial_metric, exp_fac_1);\n  DampedHarmonicGauge_detail::log_factor_metric_lapse<DataVector>(\n      make_not_null(&log_fac_2), lapse, sqrt_det_spatial_metric, exp_fac_2);\n\n  // Tempering functions\n  const double roll_on = UseRollon\n                             ? DampedHarmonicGauge_detail::roll_on_function(\n                                   time, rollon_start_time, rollon_width)\n                             : 1.0;\n  DampedHarmonicGauge_detail::spatial_weight_function<DataVector, SpatialDim,\n                                                      Frame>(\n      make_not_null(&weight), coords, sigma_r);\n\n  get(pow1) = integer_pow(get(log_fac_1), exp_L1);\n  get(pow2) = integer_pow(get(log_fac_1), exp_S);\n  get(pow3) = integer_pow(get(log_fac_2), exp_L2);\n\n  // coeffs that enter gauge source function\n  get(mu_L1) = amp_coef_L1 * roll_on * get(weight) * get(pow1);\n  get(mu_S) = amp_coef_S * roll_on * get(weight) * get(pow2);\n  get(mu_L2) = amp_coef_L2 * roll_on * get(weight) * get(pow3);\n  get(mu_S_over_lapse) = get(mu_S) * get(one_over_lapse);\n\n  // Calc \\f$ \\mu_1 = \\mu_{L1} \\log(\\sqrt{\\gamma}/\\alpha) = R W\n  // \\log(\\sqrt{\\gamma}/\\alpha)^5\\f$\n  get(mu1) = get(mu_L1) * get(log_fac_1);\n\n  // Calc \\f$ \\mu_2 = \\mu_{L2} \\log(1/\\alpha) = R W \\log(1/\\alpha)^5\\f$\n  get(mu2) = get(mu_L2) * get(log_fac_2);\n\n  get(prefac) = get(mu_L1) * get(log_fac_1) + get(mu_L2) * get(log_fac_2);\n\n  // Compute g_{ai} shift^i\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    spacetime_metric_dot_shift.get(a) =\n        spacetime_metric.get(a, 1) * shift.get(0);\n    for (size_t i = 1; i < SpatialDim; ++i) {\n      spacetime_metric_dot_shift.get(a) +=\n          spacetime_metric.get(a, i + 1) * shift.get(i);\n    }\n  }\n\n  // Calculate H_a\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    gauge_h->get(a) = -get(mu_S_over_lapse) * spacetime_metric_dot_shift.get(a);\n    if constexpr (UseRollon) {\n      gauge_h->get(a) += (1. - roll_on) * gauge_h_init->get(a);\n    }\n  }\n  // Since n_i = 0 only do H_0 term with n_0 = -lapse\n  gauge_h->get(0) -= get(prefac) * get(lapse);\n\n  [[maybe_unused]] const double d0_roll_on =\n      UseRollon ? DampedHarmonicGauge_detail::time_deriv_of_roll_on_function(\n                      time, rollon_start_time, rollon_width)\n                : 0.0;\n\n  // Calc \\f$ \\partial_a [R W] \\f$\n  DampedHarmonicGauge_detail::spacetime_deriv_of_spatial_weight_function<\n      DataVector, SpatialDim, Frame>(make_not_null(&d4_weight), coords, sigma_r,\n                                     weight);\n  d4_RW_L1 = d4_weight;\n  d4_RW_S = d4_weight;\n  d4_RW_L2 = d4_weight;\n  if constexpr (UseRollon) {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      d4_RW_L1.get(a) *= roll_on;\n      d4_RW_S.get(a) *= roll_on;\n      d4_RW_L2.get(a) *= roll_on;\n    }\n    get<0>(d4_RW_L1) += get(weight) * d0_roll_on;\n    get<0>(d4_RW_S) += get(weight) * d0_roll_on;\n    get<0>(d4_RW_L2) += get(weight) * d0_roll_on;\n  }\n  tenex::evaluate(make_not_null(&dt_lapse),\n                  lapse() * (lapse() * half_pi_two_normals() -\n                             shift(ti::I) * half_phi_two_normals(ti::i)));\n  get<0>(d_lapse_by_lapse) = get(one_over_lapse) * get(dt_lapse);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    d_lapse_by_lapse.get(i + 1) = -half_phi_two_normals.get(i);\n  }\n  get(det_spatial_metric) = square(get(sqrt_det_spatial_metric));\n  spacetime_deriv_of_det_spatial_metric<DataVector, SpatialDim, Frame>(\n      make_not_null(&d_g_by_det), sqrt_det_spatial_metric,\n      inverse_spatial_metric, d0_spatial_metric, phi);\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    d_g_by_det.get(a) = d_g_by_det.get(a) / get(det_spatial_metric);\n  }\n  const auto spacetime_deriv_of_power_log_factor_metric_lapse =\n      [&d_lapse_by_lapse, &d_g_by_det, &lapse, &sqrt_det_spatial_metric,\n       &prefac_log, &d_logfac,\n       &logfac](gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*> result,\n                double g_exponent, int exponent) -> void {\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      d_logfac.get(a) =\n          g_exponent * d_g_by_det.get(a) - d_lapse_by_lapse.get(a);\n    }\n    DampedHarmonicGauge_detail::log_factor_metric_lapse(\n        make_not_null(&logfac), lapse, sqrt_det_spatial_metric, g_exponent);\n    get(prefac_log) =\n        static_cast<double>(exponent) * integer_pow(get(logfac), exponent - 1);\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      result->get(a) = get(prefac_log) * d_logfac.get(a);\n    }\n  };\n  // \\partial_a \\mu_{S} = \\partial_a(A_S R_S W\n  //                               \\log(\\sqrt{\\gamma}/\\alpha)^{c_{S}})\n  // \\partial_a \\mu_1 = \\partial_a(A_L1 R_L1 W\n  //                               \\log(\\sqrt{\\gamma}/\\alpha)^{1+c_{L1}})\n  // \\partial_a \\mu_2 = \\partial_a(A_L2 R_L2 W\n  //                               \\log(1/\\alpha)^{1+c_{L2}})\n  spacetime_deriv_of_power_log_factor_metric_lapse(\n      make_not_null(&d4_log_fac_mu1), exp_fac_1, exp_L1 + 1);\n  spacetime_deriv_of_power_log_factor_metric_lapse(\n      make_not_null(&d4_log_fac_muS), exp_fac_1, exp_S);\n  spacetime_deriv_of_power_log_factor_metric_lapse(\n      make_not_null(&d4_log_fac_mu2), exp_fac_2, exp_L2 + 1);\n\n  get(pow1) *= get(log_fac_1) * amp_coef_L1;\n  get(pow2) *= amp_coef_S;\n  get(pow3) *= get(log_fac_2) * amp_coef_L2;\n\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    // \\f$ \\partial_a \\mu_1 \\f$\n    d4_mu1.get(a) = get(pow1) * d4_RW_L1.get(a) +\n                    amp_coef_L1 * roll_on * get(weight) * d4_log_fac_mu1.get(a);\n    // \\f$ \\partial_a \\mu_{S} \\f$\n    d4_mu_S.get(a) = d4_RW_S.get(a) * get(pow2) +\n                     amp_coef_S * roll_on * get(weight) * d4_log_fac_muS.get(a);\n    // \\f$ \\partial_a \\mu_2 \\f$\n    d4_mu2.get(a) = get(pow3) * d4_RW_L2.get(a) +\n                    amp_coef_L2 * roll_on * get(weight) * d4_log_fac_mu2.get(a);\n  }\n\n  const tnsr::ijj<DataVector, SpatialDim, Frame> d3_spatial_metric{};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t k = 0; k <= j; ++k) {\n        make_const_view(make_not_null(&d3_spatial_metric.get(i, j, k)),\n                        phi.get(i, j + 1, k + 1), 0, num_points);\n      }\n    }\n  }\n\n  // Calc \\f$ \\partial_a T1 \\f$\n  if constexpr (UseRollon) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      dT1.get(0, b) = -gauge_h_init->get(b) * d0_roll_on;\n      for (size_t a = 0; a < SpatialDim + 1; ++a) {\n        if (a != 0) {\n          dT1.get(a, b) = (1. - roll_on) * dgauge_h_init->get(a, b);\n        } else {\n          dT1.get(a, b) += (1. - roll_on) * dgauge_h_init->get(a, b);\n        }\n      }\n    }\n  }\n\n  // d_t lapse = lapse * (0.5 * n^a n^b (lapse Pi_{ab} - shift^i Phi_{iab}))\n  get<0>(d4_muS_over_lapse) = -get<0>(shift) * get<0>(half_phi_two_normals);\n  for (size_t i = 1; i < SpatialDim; ++i) {\n    get<0>(d4_muS_over_lapse) -= shift.get(i) * half_phi_two_normals.get(i);\n  }\n  get<0>(d4_muS_over_lapse) += get(lapse) * get(half_pi_two_normals);\n  get<0>(d4_muS_over_lapse) *= get(lapse);\n\n  // Calc \\f$ \\partial_a T2 \\f$\n  get<0>(dT2) = -(get<0>(d4_mu1) + get<0>(d4_mu2)) * get(lapse)\n                // Note:  \\f$ \\partial_a n_b = {-\\partial_a lapse, 0, 0, 0} \\f$\n                - (get(mu1) + get(mu2)) * get<0>(d4_muS_over_lapse);\n\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    dT2.get(i + 1) =\n        -(d4_mu1.get(i + 1) + d4_mu2.get(i + 1)) * get(lapse)\n        // Note:  \\f$ \\partial_a n_b = {-\\partial_a lapse, 0, 0, 0} \\f$\n        + (get(mu1) + get(mu2)) * get(lapse) * half_phi_two_normals.get(i);\n  }\n\n  // \\f[ \\partial_a (\\mu_S/\\alpha) = (1/\\alpha) \\partial_a \\mu_{S}\n  //         - (\\mu_{S}/\\alpha^2) \\partial_a \\alpha\n  // \\f]\n  //\n  // Note that the d4_lapse terms are actually\n  // d_t lapse / lapse = 0.5 * n^a n^b (lapse Pi_{ab} - shift^i Phi_{iab})\n  // d_i lapse / lapse = -0.5 * n^a n^b Phi_{iab}\n  //\n  // The GH RHS computes:\n  //  0.5 * n^a n^b Pi_{ab}\n  //  0.5 * n^a n^b Phi_{iab}\n  // so we reuse that work by taking them as arguments.\n  get<0>(d4_muS_over_lapse) *= -get(mu_S) * get(one_over_lapse);\n  get<0>(d4_muS_over_lapse) += get<0>(d4_mu_S);\n  get<0>(d4_muS_over_lapse) *= get(one_over_lapse);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    d4_muS_over_lapse.get(i + 1) =\n        get(one_over_lapse) *\n        (d4_mu_S.get(i + 1) + get(mu_S) * half_phi_two_normals.get(i));\n  }\n\n  // Calc \\f$ \\partial_a T3 \\f$ (note minus sign)\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      dT3.get(a, j + 1) = d4_spacetime_metric.get(a, 0, j + 1);\n    }\n    dT3.get(a, 0) = d4_spacetime_metric.get(a, 0, 1) * get<0>(shift);\n    for (size_t j = 1; j < SpatialDim; ++j) {\n      dT3.get(a, 0) += d4_spacetime_metric.get(a, 0, j + 1) * shift.get(j);\n    }\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = i + 1; j < SpatialDim; ++j) {\n        dT3.get(a, 0) -= shift.get(i) * shift.get(j) *\n                         d4_spacetime_metric.get(a, i + 1, j + 1);\n      }\n    }\n    dT3.get(a, 0) *= 2.0;\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      dT3.get(a, 0) -= shift.get(i) * shift.get(i) *\n                       d4_spacetime_metric.get(a, i + 1, i + 1);\n    }\n\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      dT3.get(a, b) *= -get(mu_S_over_lapse);\n      dT3.get(a, b) -=\n          d4_muS_over_lapse.get(a) * spacetime_metric_dot_shift.get(b);\n    }\n  }\n\n  // Calc \\f$ \\partial_a H_b = dT1_{ab} + dT2_{ab} + dT3_{ab} \\f$\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      if constexpr (UseRollon) {\n        d4_gauge_h->get(a, b) = dT1.get(a, b) + dT3.get(a, b);\n      } else {\n        d4_gauge_h->get(a, b) = dT3.get(a, b);\n      }\n    }\n    d4_gauge_h->get(a, 0) += dT2.get(a);\n  }\n}\n}  // namespace\n\ntemplate <size_t SpatialDim, typename Frame>\nvoid damped_harmonic_rollon(\n    const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*> gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame>*> d4_gauge_h,\n    const tnsr::a<DataVector, SpatialDim, Frame>& gauge_h_init,\n    const tnsr::ab<DataVector, SpatialDim, Frame>& dgauge_h_init,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::abb<DataVector, SpatialDim, Frame>& d4_spacetime_metric,\n    const Scalar<DataVector>& half_pi_two_normals,\n    const tnsr::i<DataVector, SpatialDim, Frame>& half_phi_two_normals,\n    const tnsr::aa<DataVector, SpatialDim, Frame>& spacetime_metric,\n    const tnsr::iaa<DataVector, SpatialDim, Frame>& phi, const double time,\n    const tnsr::I<DataVector, SpatialDim, Frame>& coords,\n    const double amp_coef_L1, const double amp_coef_L2, const double amp_coef_S,\n    const int exp_L1, const int exp_L2, const int exp_S,\n    const double rollon_start_time, const double rollon_width,\n    const double sigma_r) {\n  damped_harmonic_impl<true>(\n      gauge_h, d4_gauge_h, &gauge_h_init, &dgauge_h_init, lapse, shift,\n      sqrt_det_spatial_metric, inverse_spatial_metric, d4_spacetime_metric,\n      half_pi_two_normals, half_phi_two_normals, spacetime_metric, phi, time,\n      coords, amp_coef_L1, amp_coef_L2, amp_coef_S, exp_L1, exp_L2, exp_S,\n      rollon_start_time, rollon_width, sigma_r);\n}\n\ntemplate <size_t SpatialDim, typename Frame>\nvoid damped_harmonic(\n    const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*> gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame>*> d4_gauge_h,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::abb<DataVector, SpatialDim, Frame>& d4_spacetime_metric,\n    const Scalar<DataVector>& half_pi_two_normals,\n    const tnsr::i<DataVector, SpatialDim, Frame>& half_phi_two_normals,\n    const tnsr::aa<DataVector, SpatialDim, Frame>& spacetime_metric,\n    const tnsr::iaa<DataVector, SpatialDim, Frame>& phi,\n    const tnsr::I<DataVector, SpatialDim, Frame>& coords,\n    const double amp_coef_L1, const double amp_coef_L2, const double amp_coef_S,\n    const int exp_L1, const int exp_L2, const int exp_S, const double sigma_r) {\n  damped_harmonic_impl<false, SpatialDim, Frame>(\n      gauge_h, d4_gauge_h, nullptr, nullptr, lapse, shift,\n      sqrt_det_spatial_metric, inverse_spatial_metric, d4_spacetime_metric,\n      half_pi_two_normals, half_phi_two_normals, spacetime_metric, phi,\n      std::numeric_limits<double>::signaling_NaN(), coords, amp_coef_L1,\n      amp_coef_L2, amp_coef_S, exp_L1, exp_L2, exp_S,\n      std::numeric_limits<double>::signaling_NaN(),\n      std::numeric_limits<double>::signaling_NaN(), sigma_r);\n}\n\nDampedHarmonic::DampedHarmonic(const double width,\n                               const std::array<double, 3>& amps,\n                               const std::array<int, 3>& exps)\n    : spatial_decay_width_(width), amplitudes_(amps), exponents_(exps) {}\n\nDampedHarmonic::DampedHarmonic(CkMigrateMessage* const msg)\n    : GaugeCondition(msg) {}\n\nvoid DampedHarmonic::pup(PUP::er& p) {\n  GaugeCondition::pup(p);\n  p | spatial_decay_width_;\n  p | amplitudes_;\n  p | exponents_;\n}\n\nstd::unique_ptr<GaugeCondition> DampedHarmonic::get_clone() const {\n  return std::make_unique<DampedHarmonic>(*this);\n}\n\ntemplate <size_t SpatialDim>\nvoid DampedHarmonic::gauge_and_spacetime_derivative(\n    const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame::Inertial>*>\n        gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame::Inertial>*>\n        d4_gauge_h,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, Frame::Inertial>&\n        inverse_spatial_metric,\n    const tnsr::abb<DataVector, SpatialDim, Frame::Inertial>&\n        d4_spacetime_metric,\n    const Scalar<DataVector>& half_pi_two_normals,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n        half_phi_two_normals,\n    const tnsr::aa<DataVector, SpatialDim, Frame::Inertial>& spacetime_metric,\n    const tnsr::iaa<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const double /*time*/,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& inertial_coords)\n    const {\n  damped_harmonic(gauge_h, d4_gauge_h, lapse, shift, sqrt_det_spatial_metric,\n                  inverse_spatial_metric, d4_spacetime_metric,\n                  half_pi_two_normals, half_phi_two_normals, spacetime_metric,\n                  phi, inertial_coords, amplitudes_[0], amplitudes_[1],\n                  amplitudes_[2], exponents_[0], exponents_[1], exponents_[2],\n                  spatial_decay_width_);\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DampedHarmonic::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void DampedHarmonic::gauge_and_spacetime_derivative(                \\\n      gsl::not_null<tnsr::a<DataVector, DIM(data), Frame::Inertial>*> gauge_h, \\\n      gsl::not_null<tnsr::ab<DataVector, DIM(data), Frame::Inertial>*>         \\\n          d4_gauge_h,                                                          \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift,            \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric,                       \\\n      const tnsr::II<DataVector, DIM(data), Frame::Inertial>&                  \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::abb<DataVector, DIM(data), Frame::Inertial>&                 \\\n          d4_spacetime_metric,                                                 \\\n      const Scalar<DataVector>& half_pi_two_normals,                           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                   \\\n          half_phi_two_normals,                                                \\\n      const tnsr::aa<DataVector, DIM(data), Frame::Inertial>&                  \\\n          spacetime_metric,                                                    \\\n      const tnsr::iaa<DataVector, DIM(data), Frame::Inertial>& phi,            \\\n      const double /*time*/,                                                   \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& inertial_coords)  \\\n      const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define DTYPE_SCAL(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE_DV_FUNC(_, data)                                           \\\n  template void damped_harmonic_rollon(                                        \\\n      gsl::not_null<tnsr::a<DataVector, DIM(data), FRAME(data)>*> gauge_h,     \\\n      gsl::not_null<tnsr::ab<DataVector, DIM(data), FRAME(data)>*> d4_gauge_h, \\\n      const tnsr::a<DataVector, DIM(data), FRAME(data)>& gauge_h_init,         \\\n      const tnsr::ab<DataVector, DIM(data), FRAME(data)>& dgauge_h_init,       \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric,                       \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::abb<DataVector, DIM(data), FRAME(data)>&                     \\\n          d4_spacetime_metric,                                                 \\\n      const Scalar<DataVector>& half_pi_two_normals,                           \\\n      const tnsr::i<DataVector, DIM(data), FRAME(data)>& half_phi_two_normals, \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& spacetime_metric,    \\\n      const tnsr::iaa<DataVector, DIM(data), FRAME(data)>& phi,                \\\n      const double time,                                                       \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& coords,               \\\n      const double amp_coef_L1, const double amp_coef_L2,                      \\\n      const double amp_coef_S, const int exp_L1, const int exp_L2,             \\\n      const int exp_S, const double rollon_start_time,                         \\\n      const double rollon_width, const double sigma_r);                        \\\n  template void damped_harmonic(                                               \\\n      gsl::not_null<tnsr::a<DataVector, DIM(data), FRAME(data)>*> gauge_h,     \\\n      gsl::not_null<tnsr::ab<DataVector, DIM(data), FRAME(data)>*> d4_gauge_h, \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& shift,                \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric,                       \\\n      const tnsr::II<DataVector, DIM(data), FRAME(data)>&                      \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::abb<DataVector, DIM(data), FRAME(data)>&                     \\\n          d4_spacetime_metric,                                                 \\\n      const Scalar<DataVector>& half_pi_two_normals,                           \\\n      const tnsr::i<DataVector, DIM(data), FRAME(data)>& half_phi_two_normals, \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& spacetime_metric,    \\\n      const tnsr::iaa<DataVector, DIM(data), FRAME(data)>& phi,                \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& coords,               \\\n      const double amp_coef_L1, const double amp_coef_L2,                      \\\n      const double amp_coef_S, const int exp_L1, const int exp_L2,             \\\n      const int exp_S, const double sigma_r);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_DV_FUNC, (1, 2, 3), (DataVector),\n                        (Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef DTYPE_SCAL\n#undef INSTANTIATE_DV_FUNC\n#undef INSTANTIATE_SCALAR_FUNC\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gh {\nnamespace gauges {\n/*!\n * \\brief Damped harmonic gauge source function and its spacetime derivative.\n *\n * \\details The gauge condition has been taken from \\cite Szilagyi2009qz and\n * \\cite Deppe2018uye. We provide both a \"rollon\" version\n * (`damped_harmonic_rollon`), and a \"non-rollon\" version (`damped_harmonic`).\n * In the non-rollon version the rollon function \\f$R(t)=1\\f$.\n *\n * \\warning Only the non-rollon version can be used with a moving mesh.\n *\n * The covariant form of the source function \\f$H_a\\f$ is written as:\n *\n * \\f{align*}\n * H_a :=  [1 - R(t)] H_a^\\mathrm{init} +\n *  [\\mu_{L1} \\mathrm{log}(\\sqrt{\\gamma}/\\alpha) + \\mu_{L2}\n * \\mathrm{log}(1/\\alpha)] n_a\n *   - \\mu_S \\gamma_{ai} \\beta^i / \\alpha\n * \\f}\n *\n * where \\f$\\alpha, \\beta^k\\f$ are the lapse and shift respectively, \\f$n_a\\f$\n * is the unit normal one-form to the spatial slice, and \\f$\\gamma_{ab}\\f$ is\n * the spatial metric (obtained by projecting the spacetime metric onto the\n * 3-slice, i.e. \\f$\\gamma_{ab} = g_{ab} + n_a n_b\\f$). The prefactors are:\n *\n * \\f{align*}\n *  \\mu_{L1} &= A_{L1} R(t) W(x^i) \\mathrm{log}(\\sqrt{\\gamma}/\\alpha)^{e_{L1}},\n * \\\\\n *  \\mu_{L2} &= A_{L2} R(t) W(x^i) \\mathrm{log}(1/\\alpha)^{e_{L2}}, \\\\\n *  \\mu_{S} &= A_{S} R(t) W(x^i) \\mathrm{log}(\\sqrt{\\gamma}/\\alpha)^{e_{S}},\n * \\f}\n *\n * temporal roll-on function \\f$ R(t)\\f$ is:\n *\n * \\f{align*}\n * \\begin{array}{ll}\n *     R(t) & = 0, & t< t_{0} \\\\\n *          & = 1 - \\exp[-((t - t_{0})/ \\sigma_t)^4], & t\\geq t_{0} \\\\\n * \\end{array}\n * \\f}\n *\n * and the spatial weight function is:\n *\n * \\f{align*}\n * W(x^i) = \\exp[-(r/\\sigma_r)^2].\n * \\f}\n *\n * This weight function can be written with multiple constant factors in the\n * exponent in literature \\cite Deppe2018uye, but we absorb them all into\n * \\f$ \\sigma_r\\f$ here. The coordinate \\f$ r\\f$ is the Euclidean radius\n * in Inertial coordinates.\n *\n * Note that for the last three terms in \\f$H_a\\f$ (with \\f$ X = \\{L1, L2, S\\}\n * \\f$):\n *   - Amplitude factors \\f$ A_{X} \\f$ are taken as input here as `amp_coef_X`\n *   - Exponents \\f$ e_X\\f$ are taken as input here as `exp_X`.\n *   - Spatial weight function \\f$W\\f$ is specified completely by\n *     \\f$\\sigma_r\\f$, which is taken as input here as `sigma_r`.\n *\n * Also computes spacetime derivatives, i.e. \\f$\\partial_a H_b\\f$, of the damped\n * harmonic source function H. Using notation from damped_harmonic_h(), we\n * rewrite the same as:\n *\n * \\f{align*}\n * \\partial_a H_b =& \\partial_a T_1 + \\partial_a T_2 + \\partial_a T_3, \\\\\n * H_a =& T_1 + T_2 + T_3,\n * \\f}\n *\n * where:\n *\n * \\f{align*}\n * T_1 =& [1 - R(t)] H_a^\\mathrm{init}, \\\\\n * T_2 =& [\\mu_{L1} \\mathrm{log}(\\sqrt{\\gamma}/\\alpha) + \\mu_{L2}\n * \\mathrm{log}(1/\\alpha)] n_a,\n * \\\\\n * T_3 =& - \\mu_S \\gamma_{ai} \\beta^i / \\alpha.\n * \\f}\n *\n * Derivation:\n *\n * \\f$\\blacksquare\\f$ For \\f$ T_1 \\f$, the derivatives are:\n * \\f{align*}\n * \\partial_a T_1 = (1 - R(t))\n * \\partial_a H_b^\\mathrm{init}\n *                - H_b^\\mathrm{init} \\partial_a R.\n * \\f}\n *\n * \\f$\\blacksquare\\f$ Write \\f$ T_2 \\equiv (\\mu_1 + \\mu_2) n_b \\f$. Then:\n *\n * \\f{align*}\n * \\partial_a T_2 =& (\\partial_a \\mu_1 + \\partial_a \\mu_2) n_b \\\\\n *               +& (\\mu_1 + \\mu_2) \\partial_a n_b,\n * \\f}\n *\n * where\n *\n * \\f{align*}\n * \\partial_a n_b =& \\left(-\\partial_a \\alpha, 0, 0, 0\\right) \\\\\n *\n * \\partial_a \\mu_1\n *  =& \\partial_a [A_{L1} R(t) W(x^i) \\mathrm{log}(\\sqrt{\\gamma}/\\alpha)^{e_{L1}\n * +\n * 1}], \\\\\n *  =& A_{L1} R(t) W(x^i) \\partial_a [\\mathrm{log}(\\sqrt{\\gamma}/\\alpha)^{e_{L1}\n * +\n * 1}] \\\\\n *   +& A_{L1} \\mathrm{log}(\\sqrt{\\gamma}/\\alpha)^{e_{L1} + 1} \\partial_a [R(t)\n * W(x^i)],\\\\\n *\n * \\partial_a \\mu_2\n *  =& \\partial_a [A_{L2} R(t) W(x^i) \\mathrm{log}(1/\\alpha)^{e_{L2} + 1}], \\\\\n *  =& A_{L2} R(t) W(x^i) \\partial_a [\\mathrm{log}(1/\\alpha)^{e_{L2} + 1}] \\\\\n *     +& A_{L2} \\mathrm{log}(1/\\alpha)^{e_{L2} + 1} \\partial_a [R(t) W(x^i)],\n * \\f}\n *\n * where \\f$\\partial_a [R W] = \\left(\\partial_0 R(t), \\partial_i\n * W(x^j)\\right)\\f$.\n *\n * \\f$\\blacksquare\\f$ Finally, the derivatives of \\f$ T_3 \\f$ are:\n *\n * \\f[\n * \\partial_a T_3 = -\\partial_a(\\mu_S/\\alpha) \\gamma_{bi} \\beta^i\n *                  -(\\mu_S/\\alpha) \\partial_a(\\gamma_{bi}) \\beta^i\n *                  -(\\mu_S/\\alpha) \\gamma_{bi}\\partial_a \\beta^i,\n * \\f]\n *\n * where\n *\n * \\f{align*}\n * \\partial_a(\\mu_S / \\alpha) =& (1/\\alpha)\\partial_a \\mu_S\n *                       - \\frac{\\mu_S}{\\alpha^2}\\partial_a \\alpha,\n * \\,\\,\\mathrm{and}\\\\ \\partial_a \\mu_S =& \\partial_a [A_S R(t) W(x^i)\n * \\mathrm{log}(\\sqrt{\\gamma}/\\alpha)^{e_S}], \\\\\n *                  =& A_S R(t) W(x^i) \\partial_a\n * [\\mathrm{log}(\\sqrt{\\gamma}/\\alpha)^{e_S}] \\\\\n *                  +& A_S \\mathrm{log}(\\sqrt{\\gamma} / \\alpha)^{e_S} \\partial_a\n * [R(t) W(x^i)]. \\f}\n */\ntemplate <size_t SpatialDim, typename Frame>\nvoid damped_harmonic_rollon(\n    gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*> gauge_h,\n    gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame>*> d4_gauge_h,\n    const tnsr::a<DataVector, SpatialDim, Frame>& gauge_h_init,\n    const tnsr::ab<DataVector, SpatialDim, Frame>& dgauge_h_init,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::abb<DataVector, SpatialDim, Frame>& d4_spacetime_metric,\n    const Scalar<DataVector>& half_pi_two_normals,\n    const tnsr::i<DataVector, SpatialDim, Frame>& half_phi_two_normals,\n    const tnsr::aa<DataVector, SpatialDim, Frame>& spacetime_metric,\n    const tnsr::iaa<DataVector, SpatialDim, Frame>& phi, double time,\n    const tnsr::I<DataVector, SpatialDim, Frame>& coords, double amp_coef_L1,\n    double amp_coef_L2, double amp_coef_S, int exp_L1, int exp_L2, int exp_S,\n    double rollon_start_time, double rollon_width, double sigma_r);\n\n/*!\n * \\copydoc damped_harmonic_rollon()\n */\ntemplate <size_t SpatialDim, typename Frame>\nvoid damped_harmonic(\n    gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*> gauge_h,\n    gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame>*> d4_gauge_h,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::abb<DataVector, SpatialDim, Frame>& d4_spacetime_metric,\n    const Scalar<DataVector>& half_pi_two_normals,\n    const tnsr::i<DataVector, SpatialDim, Frame>& half_phi_two_normals,\n    const tnsr::aa<DataVector, SpatialDim, Frame>& spacetime_metric,\n    const tnsr::iaa<DataVector, SpatialDim, Frame>& phi,\n    const tnsr::I<DataVector, SpatialDim, Frame>& coords, double amp_coef_L1,\n    double amp_coef_L2, double amp_coef_S, int exp_L1, int exp_L2, int exp_S,\n    double sigma_r);\n\n/*!\n * \\brief Impose damped harmonic gauge.\n *\n * \\see `damped_harmonic()`\n */\nclass DampedHarmonic final : public GaugeCondition {\n public:\n  /// The width of the Gaussian for the spatial decay of the damped harmonic\n  /// gauge.\n  struct SpatialDecayWidth {\n    using type = double;\n    static constexpr Options::String help{\n        \"Spatial width (sigma_r) of weight function (W(x^i)) used in the \"\n        \"damped harmonic gauge.\"};\n  };\n  /// The amplitudes for the L1, L2, and S terms, respectively, for the damped\n  /// harmonic gauge.\n  struct Amplitudes {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help{\n        \"Amplitudes [A_{L1}, A_{L2}, A_{S}] for the damped harmonic gauge.\"};\n  };\n  /// The exponents for the L1, L2, and S terms, respectively, for the damped\n  /// harmonic gauge.\n  struct Exponents {\n    using type = std::array<int, 3>;\n    static constexpr Options::String help{\n        \"Exponents [e_{L1}, e_{L2}, e_{S}] for the damped harmonic gauge.\"};\n  };\n\n  static constexpr Options::String help{\n      \"Apply damped harmonic/damped wave gauge.\"};\n\n  using options = tmpl::list<SpatialDecayWidth, Amplitudes, Exponents>;\n\n  DampedHarmonic(double width, const std::array<double, 3>& amps,\n                 const std::array<int, 3>& exps);\n\n  DampedHarmonic() = default;\n  DampedHarmonic(const DampedHarmonic&) = default;\n  DampedHarmonic& operator=(const DampedHarmonic&) = default;\n  DampedHarmonic(DampedHarmonic&&) = default;\n  DampedHarmonic& operator=(DampedHarmonic&&) = default;\n  ~DampedHarmonic() override = default;\n\n  /// \\cond\n  explicit DampedHarmonic(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(DampedHarmonic);  // NOLINT\n  /// \\endcond\n\n  std::unique_ptr<GaugeCondition> get_clone() const override;\n\n  template <size_t SpatialDim>\n  void gauge_and_spacetime_derivative(\n      gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame::Inertial>*> gauge_h,\n      gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame::Inertial>*>\n          d4_gauge_h,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::II<DataVector, SpatialDim, Frame::Inertial>&\n          inverse_spatial_metric,\n      const tnsr::abb<DataVector, SpatialDim, Frame::Inertial>&\n          d4_spacetime_metric,\n      const Scalar<DataVector>& half_pi_two_normals,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&\n          half_phi_two_normals,\n      const tnsr::aa<DataVector, SpatialDim, Frame::Inertial>& spacetime_metric,\n      const tnsr::iaa<DataVector, SpatialDim, Frame::Inertial>& phi,\n      double time,\n      const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& inertial_coords)\n      const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double spatial_decay_width_{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> amplitudes_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<int, 3> exponents_{{std::numeric_limits<int>::max(),\n                                 std::numeric_limits<int>::max(),\n                                 std::numeric_limits<int>::max()}};\n};\n\nnamespace DampedHarmonicGauge_detail {\n// Used in the test\ndouble roll_on_function(double time, double t_start, double sigma_t);\n\ndouble time_deriv_of_roll_on_function(double time, double t_start,\n                                      double sigma_t);\n}  // namespace DampedHarmonicGauge_detail\n}  // namespace gauges\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedWaveHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedWaveHelpers.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfDetSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::gauges::DampedHarmonicGauge_detail {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spatial_weight_function(const gsl::not_null<Scalar<DataType>*> weight,\n                             const tnsr::I<DataType, SpatialDim, Frame>& coords,\n                             const double sigma_r) {\n  const auto r_squared = dot_product(coords, coords);\n  get(*weight) = exp(-get(r_squared) / pow<2>(sigma_r));\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_deriv_of_spatial_weight_function(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> d4_weight,\n    const tnsr::I<DataType, SpatialDim, Frame>& coords, const double sigma_r,\n    const Scalar<DataType>& weight_function) {\n  set_number_of_grid_points(d4_weight, coords);\n  // use 0th component to avoid allocations\n  get<0>(*d4_weight) = get(weight_function) * (-2. / pow<2>(sigma_r));\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    d4_weight->get(1 + i) = get<0>(*d4_weight) * coords.get(i);\n  }\n  // time derivative of weight function is zero\n  get<0>(*d4_weight) = 0.;\n}\n\ntemplate <typename DataType>\nvoid log_factor_metric_lapse(const gsl::not_null<Scalar<DataType>*> logfac,\n                             const Scalar<DataType>& lapse,\n                             const Scalar<DataType>& sqrt_det_spatial_metric,\n                             const double exponent) {\n  // branching below is to avoid using pow for performance reasons\n  if (exponent == 0.) {\n    get(*logfac) = -log(get(lapse));\n  } else if (exponent == 0.5) {\n    get(*logfac) = log(get(sqrt_det_spatial_metric) / get(lapse));\n  } else {\n    get(*logfac) =\n        2. * exponent * log(get(sqrt_det_spatial_metric)) - log(get(lapse));\n  }\n}\n\ntemplate <typename DataType>\nScalar<DataType> log_factor_metric_lapse(\n    const Scalar<DataType>& lapse,\n    const Scalar<DataType>& sqrt_det_spatial_metric, const double exponent) {\n  Scalar<DataType> logfac{get_size(get(lapse))};\n  log_factor_metric_lapse(make_not_null(&logfac), lapse,\n                          sqrt_det_spatial_metric, exponent);\n  return logfac;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define DTYPE_SCAL(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template void spatial_weight_function(                                 \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> weight,                  \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& coords,        \\\n      const double sigma_r);                                             \\\n  template void spacetime_deriv_of_spatial_weight_function(              \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          d4_weight,                                                     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& coords,        \\\n      const double sigma_r, const Scalar<DTYPE(data)>& weight_function);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Inertial))\n\n#undef INSTANTIATE\n\n#define INSTANTIATE(_, data)                                   \\\n  template void log_factor_metric_lapse(                       \\\n      const gsl::not_null<Scalar<DTYPE_SCAL(data)>*> logfac,   \\\n      const Scalar<DTYPE_SCAL(data)>& lapse,                   \\\n      const Scalar<DTYPE_SCAL(data)>& sqrt_det_spatial_metric, \\\n      const double exponent);                                  \\\n  template Scalar<DTYPE_SCAL(data)> log_factor_metric_lapse(   \\\n      const Scalar<DTYPE_SCAL(data)>& lapse,                   \\\n      const Scalar<DTYPE_SCAL(data)>& sqrt_det_spatial_metric, \\\n      const double exponent);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef INSTANTIATE\n\n#undef DTYPE_SCAL\n#undef FRAME\n#undef DTYPE\n#undef DIM\n}  // namespace gh::gauges::DampedHarmonicGauge_detail\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedWaveHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\nnamespace gauges {\nnamespace DampedHarmonicGauge_detail {\n/*\n * Spatial weight function used in the damped harmonic gauge source\n * function.\n *\n * The spatial weight function is:\n * \\f{align*}{\n *   W(x^i) = \\exp(- (r / \\sigma_r)^2),\n * \\f}\n *\n * where \\f$r=\\sqrt{x^i\\delta_{ij}x^j}\\f$ is the coordinate radius, and\n * \\f$\\sigma_r\\f$ is the width of the Gaussian.\n *\n * This function can be written with an extra factor inside the exponent in\n * literature, e.g. \\cite Deppe2018uye. We absorb that in \\f$\\sigma_r\\f$.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spatial_weight_function(gsl::not_null<Scalar<DataType>*> weight,\n                             const tnsr::I<DataType, SpatialDim, Frame>& coords,\n                             double sigma_r);\n\n/*\n * Spacetime derivatives of the spatial weight function that enters the\n * damped harmonic gauge source function.\n *\n * Compute the derivatives:\n * \\f{align*}{\n * \\partial_a W(x^i)= \\partial_a \\exp(- (r/\\sigma_r)^2)\n *                  = (-2 * x^i / \\sigma_r^2) * exp(-(r/\\sigma_r)^2)\n * \\f}\n *\n * where \\f$r=\\sqrt{x^i\\delta_{ij}x^j}\\f$ is the coordinate radius, and\n * \\f$\\sigma_r\\f$ is the width of the Gaussian. Since the weight function is\n * spatial, the time derivative is always zero.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_deriv_of_spatial_weight_function(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> d4_weight,\n    const tnsr::I<DataType, SpatialDim, Frame>& coords, double sigma_r,\n    const Scalar<DataType>& weight_function);\n\n/*\n * The log factor that appears in damped harmonic gauge source function.\n *\n * Calculates:  \\f$ logF = \\mathrm{log}(\\gamma^p/\\alpha) \\f$.\n */\ntemplate <typename DataType>\nvoid log_factor_metric_lapse(gsl::not_null<Scalar<DataType>*> logfac,\n                             const Scalar<DataType>& lapse,\n                             const Scalar<DataType>& sqrt_det_spatial_metric,\n                             double exponent);\n\ntemplate <typename DataType>\nScalar<DataType> log_factor_metric_lapse(\n    const Scalar<DataType>& lapse,\n    const Scalar<DataType>& sqrt_det_spatial_metric, double exponent);\n\n}  // namespace DampedHarmonicGauge_detail\n}  // namespace gauges\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DhGaugeParameters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DhGaugeParameters.hpp\"\n\n#include <array>\n#include <pup.h>\n#include <pup_stl.h>\n\ngh::gauges::DhGaugeParameters<true>::DhGaugeParameters(\n    const double start, const double window, const double width,\n    const std::array<double, 3>& amps, const std::array<int, 3>& exps)\n    : rollon_start(start),\n      rollon_window(window),\n      spatial_decay_width(width),\n      amplitudes(amps),\n      exponents(exps) {}\n\ngh::gauges::DhGaugeParameters<false>::DhGaugeParameters(\n    const double width, const std::array<double, 3>& amps,\n    const std::array<int, 3>& exps)\n    : spatial_decay_width(width), amplitudes(amps), exponents(exps) {}\n\n// NOLINTNEXTLINE(google-runtime-references)\nvoid gh::gauges::DhGaugeParameters<true>::pup(PUP::er& p) {\n  p | rollon_start;\n  p | rollon_window;\n  p | spatial_decay_width;\n  p | amplitudes;\n  p | exponents;\n}\n\n// NOLINTNEXTLINE(google-runtime-references)\nvoid gh::gauges::DhGaugeParameters<false>::pup(PUP::er& p) {\n  p | spatial_decay_width;\n  p | amplitudes;\n  p | exponents;\n}\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DhGaugeParameters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <string>\n\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace gh::gauges {\n/*!\n * \\brief A struct holding the parameters for initializing damped harmonic gauge\n *\n * If UseRollon is true, the gauge transitions (\"rolls on\") to damped\n * harmonic from the initial data gauge; otherwise, the gauge begins\n * immediately in damped harmonic gauge.\n */\ntemplate <bool UseRollon>\nstruct DhGaugeParameters;\n\n/// \\cond\ntemplate <>\nstruct DhGaugeParameters<true> {\n  double rollon_start;\n  double rollon_window;\n  double spatial_decay_width;\n  std::array<double, 3> amplitudes;\n  std::array<int, 3> exponents;\n\n  static constexpr Options::String help{\n      \"A struct holding the parameters for initializing damped harmonic \"\n      \"gauge, including a roll-on from the initial gauge.\"};\n\n  /// The rollon start time\n  struct RollOnStartTime {\n    using type = double;\n    static constexpr Options::String help{\n        \"Simulation time to start rolling on the damped harmonic gauge\"};\n  };\n\n  /// The width of the Gaussian for the gauge rollon\n  struct RollOnTimeWindow {\n    using type = double;\n    static constexpr Options::String help{\n        \"The width of the Gaussian that controls how quickly the gauge is \"\n        \"rolled on.\"};\n  };\n\n  /// The width of the Gaussian for the spatial decay of the damped harmonic\n  /// gauge.\n  struct SpatialDecayWidth {\n    using type = double;\n    static constexpr Options::String help{\n        \"Spatial width of weight function used in the damped harmonic \"\n        \"gauge.\"};\n  };\n\n  /// The amplitudes for the L1, L2, and S terms, respectively, for the damped\n  /// harmonic gauge.\n  struct Amplitudes {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help{\n        \"Amplitudes [AL1, AL2, AS] for the damped harmonic gauge.\"};\n  };\n\n  /// The exponents for the L1, L2, and S terms, respectively, for the damped\n  /// harmonic gauge.\n  struct Exponents {\n    using type = std::array<int, 3>;\n    static constexpr Options::String help{\n        \"Exponents [eL1, eL2, eS] for the damped harmonic gauge.\"};\n  };\n\n  using options = tmpl::list<RollOnStartTime, RollOnTimeWindow,\n                             SpatialDecayWidth, Amplitudes, Exponents>;\n\n  DhGaugeParameters(double start, double window, double width,\n                    const std::array<double, 3>& amps,\n                    const std::array<int, 3>& exps);\n\n  DhGaugeParameters() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n\ntemplate <>\nstruct DhGaugeParameters<false> {\n  double spatial_decay_width;\n  std::array<double, 3> amplitudes;\n  std::array<int, 3> exponents;\n\n  static constexpr Options::String help{\n      \"A struct holding the parameters for initializing damped harmonic \"\n      \"gauge with no roll-on from the initial gauge.\"};\n\n  /// The width of the Gaussian for the spatial decay of the damped harmonic\n  /// gauge.\n  struct SpatialDecayWidth {\n    using type = double;\n    static constexpr Options::String help{\n        \"Spatial width of weight function used in the damped harmonic \"\n        \"gauge.\"};\n  };\n\n  /// The amplitudes for the L1, L2, and S terms, respectively, for the damped\n  /// harmonic gauge.\n  struct Amplitudes {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help{\n        \"Amplitudes [AL1, AL2, AS] for the damped harmonic gauge.\"};\n  };\n\n  /// The exponents for the L1, L2, and S terms, respectively, for the damped\n  /// harmonic gauge.\n  struct Exponents {\n    using type = std::array<int, 3>;\n    static constexpr Options::String help{\n        \"Exponents [eL1, eL2, eS] for the damped harmonic gauge.\"};\n  };\n\n  using options = tmpl::list<SpatialDecayWidth, Amplitudes, Exponents>;\n\n  DhGaugeParameters(double width, const std::array<double, 3>& amps,\n                    const std::array<int, 3>& exps);\n\n  DhGaugeParameters() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n/// \\endcond\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.tpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gh::gauges {\n#define GH_GAUGE_DISPATCH_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GH_GAUGE_DISPATCH_SOLUTION(data) \\\n  gh::Solutions::all_solutions<BOOST_PP_TUPLE_ELEM(0, data)>\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_GH_GAUGE_DISPATCH, (1, 2, 3))\n\n#undef GH_GAUGE_DISPATCH_SOLUTION\n#undef GH_GAUGE_DISPATCH_DIM\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace gh::gauges {\n/*!\n * \\brief Dispatch to the derived gauge condition.\n *\n * Which of the arguments to this function are used will depend on the gauge\n * condition, but since that is a runtime choice we need support for all gauge\n * conditions.\n */\ntemplate <class AllSolutionsForChristoffelAnalytic, size_t Dim>\nvoid dispatch(\n    gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*> gauge_h,\n    gsl::not_null<tnsr::ab<DataVector, Dim, Frame::Inertial>*> d4_gauge_h,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, Dim, Frame::Inertial>& inverse_spatial_metric,\n    const tnsr::abb<DataVector, Dim, Frame::Inertial>& d4_spacetime_metric,\n    const Scalar<DataVector>& half_pi_two_normals,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& half_phi_two_normals,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n    const Mesh<Dim>& mesh, double time,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& inverse_jacobian,\n    const GaugeCondition& gauge_condition);\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh::gauges {\ntemplate <class AllSolutionsForChristoffelAnalytic, size_t Dim>\nvoid dispatch(\n    const gsl::not_null<tnsr::a<DataVector, Dim, Frame::Inertial>*> gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, Dim, Frame::Inertial>*> d4_gauge_h,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, Dim, Frame::Inertial>& inverse_spatial_metric,\n    const tnsr::abb<DataVector, Dim, Frame::Inertial>& d4_spacetime_metric,\n    const Scalar<DataVector>& half_pi_two_normals,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& half_phi_two_normals,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n    const Mesh<Dim>& mesh, double time,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& inverse_jacobian,\n    const GaugeCondition& gauge_condition) {\n  if (const auto* harmonic_gauge =\n          dynamic_cast<const Harmonic*>(&gauge_condition);\n      harmonic_gauge != nullptr) {\n    harmonic_gauge->gauge_and_spacetime_derivative(gauge_h, d4_gauge_h, time,\n                                                   inertial_coords);\n  } else if (const auto* damped_harmonic_gauge =\n                 dynamic_cast<const DampedHarmonic*>(&gauge_condition);\n             damped_harmonic_gauge != nullptr) {\n    damped_harmonic_gauge->gauge_and_spacetime_derivative(\n        gauge_h, d4_gauge_h, lapse, shift, sqrt_det_spatial_metric,\n        inverse_spatial_metric, d4_spacetime_metric, half_pi_two_normals,\n        half_phi_two_normals, spacetime_metric, phi, time, inertial_coords);\n  } else if (const auto* analytic_gauge =\n                 dynamic_cast<const AnalyticChristoffel*>(&gauge_condition);\n             analytic_gauge != nullptr) {\n    analytic_gauge->gauge_and_spacetime_derivative(\n        gauge_h, d4_gauge_h, mesh, time, inertial_coords, inverse_jacobian,\n        AllSolutionsForChristoffelAnalytic{});\n  } else {\n    // LCOV_EXCL_START\n    ERROR(\n        \"Failed to dispatch to Harmonic, DampedHarmonic, or Analytic gauge \"\n        \"condition.\");\n    // LCOV_EXCL_STOP\n  }\n}\n\n#define INSTANTIATE_GH_GAUGE_DISPATCH(_, data)                                 \\\n  template void dispatch<GH_GAUGE_DISPATCH_SOLUTION(data)>(                    \\\n      gsl::not_null<                                                           \\\n          tnsr::a<DataVector, GH_GAUGE_DISPATCH_DIM(data), Frame::Inertial>*>  \\\n          gauge_h,                                                             \\\n      gsl::not_null<                                                           \\\n          tnsr::ab<DataVector, GH_GAUGE_DISPATCH_DIM(data), Frame::Inertial>*> \\\n          d4_gauge_h,                                                          \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, GH_GAUGE_DISPATCH_DIM(data), Frame::Inertial>& \\\n          shift,                                                               \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric,                       \\\n      const tnsr::II<DataVector, GH_GAUGE_DISPATCH_DIM(data),                  \\\n                     Frame::Inertial>& inverse_spatial_metric,                 \\\n      const tnsr::abb<DataVector, GH_GAUGE_DISPATCH_DIM(data),                 \\\n                      Frame::Inertial>& d4_spacetime_metric,                   \\\n      const Scalar<DataVector>& half_pi_two_normals,                           \\\n      const tnsr::i<DataVector, GH_GAUGE_DISPATCH_DIM(data), Frame::Inertial>& \\\n          half_phi_two_normals,                                                \\\n      const tnsr::aa<DataVector, GH_GAUGE_DISPATCH_DIM(data),                  \\\n                     Frame::Inertial>& spacetime_metric,                       \\\n      const tnsr::iaa<DataVector, GH_GAUGE_DISPATCH_DIM(data),                 \\\n                      Frame::Inertial>& phi,                                   \\\n      const Mesh<GH_GAUGE_DISPATCH_DIM(data)>& mesh, double time,              \\\n      const tnsr::I<DataVector, GH_GAUGE_DISPATCH_DIM(data), Frame::Inertial>& \\\n          inertial_coords,                                                     \\\n      const InverseJacobian<DataVector, GH_GAUGE_DISPATCH_DIM(data),           \\\n                            Frame::ElementLogical, Frame::Inertial>&           \\\n          inverse_jacobian,                                                    \\\n      const GaugeCondition& gauge_condition);\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.hpp\"\n\nnamespace gh::gauges {\n/// \\brief List of all gauge conditions\nusing all_gauges = tmpl::list<AnalyticChristoffel, DampedHarmonic, Harmonic>;\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n\nnamespace gh::gauges {\nGaugeCondition::GaugeCondition(CkMigrateMessage* msg) : PUP::able(msg) {}\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace gh {\n/// \\brief Gauge conditions for generalized harmonic evolution systems.\nnamespace gauges {\n/// \\brief Base class for GH gauge conditions.\n///\n/// Derived class must have a `void gauge_and_spacetime_derivative` function\n/// that takes as `not_null` arguments \\f$H_a\\f$ and \\f$\\partial_b H_a\\f$.\n/// Additional arguments can be added that are needed to compute the gauge\n/// condition. The `gh::gauges::dispatch()` function must also\n/// be updated to correctly detect and forward to the gauge condition. The\n/// header file must also be included in `Factory.hpp` and the gauge condition\n/// added to the `all_gauges` type alias in `Factory.hpp`.\nclass GaugeCondition : public PUP::able {\n public:\n  GaugeCondition() = default;\n  GaugeCondition(const GaugeCondition&) = default;\n  GaugeCondition& operator=(const GaugeCondition&) = default;\n  GaugeCondition(GaugeCondition&&) = default;\n  GaugeCondition& operator=(GaugeCondition&&) = default;\n  ~GaugeCondition() override = default;\n\n  explicit GaugeCondition(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_abstract(GaugeCondition);  // NOLINT\n\n  virtual std::unique_ptr<GaugeCondition> get_clone() const = 0;\n\n  virtual bool is_harmonic() const { return false; }\n};\n}  // namespace gauges\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/HalfPiPhiTwoNormals.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/HalfPiPhiTwoNormals.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh::gauges {\ntemplate <size_t Dim, typename Frame>\nvoid half_pi_and_phi_two_normals(\n    const gsl::not_null<Scalar<DataVector>*> half_pi_two_normals,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame>*> half_phi_two_normals,\n    const tnsr::A<DataVector, Dim, Frame>& spacetime_normal_vector,\n    const tnsr::aa<DataVector, Dim, Frame>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame>& phi) {\n  set_number_of_grid_points(half_pi_two_normals, pi);\n  set_number_of_grid_points(half_phi_two_normals, pi);\n  get(*half_pi_two_normals) = 0.0;\n  for (size_t i = 0; i < Dim; ++i) {\n    half_phi_two_normals->get(i) = 0.0;\n  }\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    get(*half_pi_two_normals) += spacetime_normal_vector.get(a) *\n                                 spacetime_normal_vector.get(a) * pi.get(a, a);\n    for (size_t i = 0; i < Dim; ++i) {\n      half_phi_two_normals->get(i) += 0.5 * spacetime_normal_vector.get(a) *\n                                      spacetime_normal_vector.get(a) *\n                                      phi.get(i, a, a);\n    }\n    for (size_t b = a + 1; b < Dim + 1; ++b) {\n      get(*half_pi_two_normals) += 2.0 * spacetime_normal_vector.get(a) *\n                                   spacetime_normal_vector.get(b) *\n                                   pi.get(a, b);\n      for (size_t i = 0; i < Dim; ++i) {\n        half_phi_two_normals->get(i) += spacetime_normal_vector.get(a) *\n                                        spacetime_normal_vector.get(b) *\n                                        phi.get(i, a, b);\n      }\n    }\n  }\n  get(*half_pi_two_normals) *= 0.5;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                            \\\n  template void half_pi_and_phi_two_normals(                            \\\n      const gsl::not_null<Scalar<DataVector>*> half_pi_two_normals,     \\\n      const gsl::not_null<tnsr::i<DataVector, DIM(data), FRAME(data)>*> \\\n          half_phi_two_normals,                                         \\\n      const tnsr::A<DataVector, DIM(data), FRAME(data)>&                \\\n          spacetime_normal_vector,                                      \\\n      const tnsr::aa<DataVector, DIM(data), FRAME(data)>& pi,           \\\n      const tnsr::iaa<DataVector, DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Inertial, Frame::Grid))\n\n#undef INSTANTIATE\n#undef FRAME\n#undef DIM\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/HalfPiPhiTwoNormals.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace gh::gauges {\n/*!\n * \\brief Compute \\f$0.5 n^a n^b \\Pi_{ab}\\f$ and \\f$0.5 n^a n^b \\Phi_{iab}\\f$\n */\ntemplate <size_t Dim, typename Frame>\nvoid half_pi_and_phi_two_normals(\n    gsl::not_null<Scalar<DataVector>*> half_pi_two_normals,\n    gsl::not_null<tnsr::i<DataVector, Dim, Frame>*> half_phi_two_normals,\n    const tnsr::A<DataVector, Dim, Frame>& spacetime_normal_vector,\n    const tnsr::aa<DataVector, Dim, Frame>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame>& phi);\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh::gauges {\nHarmonic::Harmonic(CkMigrateMessage* const msg) : GaugeCondition(msg) {}\n\nvoid Harmonic::pup(PUP::er& p) { GaugeCondition::pup(p); }\n\nstd::unique_ptr<GaugeCondition> Harmonic::get_clone() const {\n  return std::make_unique<Harmonic>(*this);\n}\n\ntemplate <size_t SpatialDim>\nvoid Harmonic::gauge_and_spacetime_derivative(\n    const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame::Inertial>*>\n        gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame::Inertial>*>\n        d4_gauge_h,\n    const double /*time*/,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& /*inertial_coords*/)\n    const {\n  for (auto& component : *gauge_h) {\n    component = 0.0;\n  }\n  for (auto& component : *d4_gauge_h) {\n    component = 0.0;\n  }\n}\n\nbool Harmonic::is_harmonic() const { return true; }\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Harmonic::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void Harmonic::gauge_and_spacetime_derivative(                    \\\n      const gsl::not_null<tnsr::a<DataVector, DIM(data), Frame::Inertial>*>  \\\n          gauge_h,                                                           \\\n      const gsl::not_null<tnsr::ab<DataVector, DIM(data), Frame::Inertial>*> \\\n          d4_gauge_h,                                                        \\\n      double /*time*/,                                                       \\\n      const tnsr::I<DataVector, DIM(data),                                   \\\n                    Frame::Inertial>& /*inertial_coords*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gh::gauges {\n/*!\n * \\brief Imposes the harmonic gauge condition, \\f$H_a=0\\f$.\n */\nclass Harmonic final : public GaugeCondition {\n public:\n  using options = tmpl::list<>;\n\n  static constexpr Options::String help{\n      \"Apply the Harmonic gauge condition H_a=0.\"};\n\n  Harmonic() = default;\n  Harmonic(const Harmonic&) = default;\n  Harmonic& operator=(const Harmonic&) = default;\n  Harmonic(Harmonic&&) = default;\n  Harmonic& operator=(Harmonic&&) = default;\n  ~Harmonic() override = default;\n\n  /// \\cond\n  explicit Harmonic(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Harmonic);  // NOLINT\n  /// \\endcond\n\n  template <size_t SpatialDim>\n  void gauge_and_spacetime_derivative(\n      gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame::Inertial>*> gauge_h,\n      gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame::Inertial>*>\n          d4_gauge_h,\n      double time,\n      const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& inertial_coords)\n      const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  std::unique_ptr<GaugeCondition> get_clone() const override;\n\n  bool is_harmonic() const override;\n};\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/RegisterDerived.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/RegisterDerived.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::gauges {\nnamespace {\ntemplate <size_t Dim>\nvoid impl() {\n  // The analytic gauge condition also can hold all the different solutions, so\n  // register those too.\n  using solutions = gh::Solutions::all_solutions<Dim>;\n  register_classes_with_charm(solutions{});\n}\n}  // namespace\n\nvoid register_derived_with_charm() {\n  impl<1>();\n  impl<2>();\n  impl<3>();\n  register_classes_with_charm(\n      tmpl::list<AnalyticChristoffel, DampedHarmonic, Harmonic>{});\n}\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/RegisterDerived.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace gh::gauges {\nvoid register_derived_with_charm();\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.tpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gh::gauges {\nvoid SetPiAndPhiFromConstraintsCacheMutator::apply(\n    const gsl::not_null<bool*> value, const bool new_value) {\n  *value = new_value;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                 \\\n  template class SetPiAndPhiFromConstraints< \\\n      gh::Solutions::all_solutions<DIM(data)>, DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gh {\nnamespace Tags {\n/// DataBox tag for holding whether or not to set GH variables $\\Pi$ and $\\Phi$\n/// from constraints.\nstruct SetPiAndPhiFromConstraints : db::SimpleTag {\n  using type = bool;\n\n  using option_tags = tmpl::list<>;\n  static constexpr bool pass_metavariables = false;\n\n  static bool create_from_options() { return true; }\n};\n}  // namespace Tags\n\nnamespace gauges {\n/*!\n * \\brief GlobalCache mutator to set the value of the\n * `gh::Tags::SetPiAndPhiFromConstraints` tag.\n */\nstruct SetPiAndPhiFromConstraintsCacheMutator {\n  static void apply(gsl::not_null<bool*> value, bool new_value);\n};\n\n/*!\n * \\brief Set \\f$\\Pi_{ab}\\f$ from the gauge source function (or 1-index\n * constraint) and \\f$\\Phi_{iab}\\f$ from the 3-index constraint.\n *\n * This is necessary to ensure the initial data is in the desired evolution\n * gauge and that the 1- and 3-index constraints are satisfied.\n */\ntemplate <class AllSolutionsForChristoffelAnalytic, size_t Dim>\nstruct SetPiAndPhiFromConstraints {\n  using const_global_cache_tags = tmpl::list<gh::gauges::Tags::GaugeCondition>;\n  using mutable_global_cache_tags =\n      tmpl::list<gh::Tags::SetPiAndPhiFromConstraints>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<\n        tmpl::list<gh::Tags::Pi<DataVector, Dim>,\n                   gh::Tags::Phi<DataVector, Dim>>,\n        tmpl::list<::Tags::Time, domain::Tags::Mesh<Dim>,\n                   domain::Tags::ElementMap<Dim, Frame::Grid>,\n                   domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                               Frame::Inertial>,\n                   domain::Tags::FunctionsOfTime,\n                   domain::Tags::Coordinates<Dim, Frame::ElementLogical>,\n                   gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                   gh::gauges::Tags::GaugeCondition>>(\n        [](const auto&... args) { impl(args...); }, make_not_null(&box),\n        Parallel::get<gh::Tags::SetPiAndPhiFromConstraints>(cache));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  static void impl(\n      gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*> phi,\n      double time, const Mesh<Dim>& mesh,\n      const ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&\n          grid_to_inertial_map,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n          logical_coordinates,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n      const gauges::GaugeCondition& gauge_condition,\n      bool set_pi_and_phi_from_constraints);\n};\n}  // namespace gauges\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace gh::gauges {\ntemplate <class AllSolutionsForChristoffelAnalytic, size_t Dim>\nvoid SetPiAndPhiFromConstraints<AllSolutionsForChristoffelAnalytic, Dim>::impl(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, Frame::Inertial>*> phi,\n    const double time, const Mesh<Dim>& mesh,\n    const ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&\n        grid_to_inertial_map,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coordinates,\n    const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n    const gauges::GaugeCondition& gauge_condition,\n    const bool set_pi_and_phi_from_constraints) {\n  if (not set_pi_and_phi_from_constraints) {\n    return;\n  }\n  const auto grid_coords = logical_to_grid_map(logical_coordinates);\n  const auto inv_jac_logical_to_grid =\n      logical_to_grid_map.inv_jacobian(logical_coordinates);\n  const auto [inertial_coords, inv_jac_grid_to_inertial, jac_grid_to_inertial,\n              mesh_velocity] =\n      grid_to_inertial_map.coords_frame_velocity_jacobians(grid_coords, time,\n                                                           functions_of_time);\n\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian{};\n\n  for (size_t logical_i = 0; logical_i < Dim; ++logical_i) {\n    for (size_t inertial_i = 0; inertial_i < Dim; ++inertial_i) {\n      inverse_jacobian.get(logical_i, inertial_i) =\n          inv_jac_logical_to_grid.get(logical_i, 0) *\n          inv_jac_grid_to_inertial.get(0, inertial_i);\n      for (size_t grid_i = 1; grid_i < Dim; ++grid_i) {\n        inverse_jacobian.get(logical_i, inertial_i) +=\n            inv_jac_logical_to_grid.get(logical_i, grid_i) *\n            inv_jac_grid_to_inertial.get(grid_i, inertial_i);\n      }\n    }\n  }\n\n  partial_derivative(phi, spacetime_metric, mesh, inverse_jacobian);\n\n  Variables<\n      tmpl::list<gr::Tags::SpatialMetric<DataVector, Dim>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n                 gr::Tags::InverseSpacetimeMetric<DataVector, Dim>,\n                 gr::Tags::SpacetimeNormalVector<DataVector, Dim>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<Dim>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, Dim>,\n                               tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, Dim>,\n                               tmpl::size_t<Dim>, Frame::Inertial>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, Dim>,\n                 gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                 gr::Tags::SpatialChristoffelFirstKind<DataVector, Dim>,\n                 gr::Tags::TraceSpatialChristoffelFirstKind<DataVector, Dim>,\n                 gh::Tags::GaugeH<DataVector, Dim>,\n                 gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>,\n                 ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n                 ::Tags::dt<gr::Tags::Shift<DataVector, Dim>>,\n                 ::Tags::dt<gr::Tags::SpatialMetric<DataVector, Dim>>>>\n      buffer(get<0, 0>(spacetime_metric).size());\n\n  auto& [spatial_metric, sqrt_det_spatial_metric, inverse_spatial_metric, lapse,\n         shift, inverse_spacetime_metric, spacetime_unit_normal_vector, d_lapse,\n         d_shift, d_spatial_metric, ex_curvature, trace_ex_curvature,\n         spatial_christoffel_first, trace_spatial_christoffel_first, gauge_h,\n         d4_gauge_h, dt_lapse, dt_shift, dt_spatial_metric] = buffer;\n\n  gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n  determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                          make_not_null(&inverse_spatial_metric),\n                          spatial_metric);\n  get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n  gr::shift(make_not_null(&shift), spacetime_metric, inverse_spatial_metric);\n  gr::lapse(make_not_null(&lapse), shift, spacetime_metric);\n  gr::inverse_spacetime_metric(make_not_null(&inverse_spacetime_metric), lapse,\n                               shift, inverse_spatial_metric);\n  gr::spacetime_normal_vector(make_not_null(&spacetime_unit_normal_vector),\n                              lapse, shift);\n\n  spatial_deriv_of_lapse(make_not_null(&d_lapse), lapse,\n                         spacetime_unit_normal_vector, *phi);\n  spatial_deriv_of_shift(make_not_null(&d_shift), lapse,\n                         inverse_spacetime_metric, spacetime_unit_normal_vector,\n                         *phi);\n  deriv_spatial_metric(make_not_null(&d_spatial_metric), *phi);\n\n  extrinsic_curvature(make_not_null(&ex_curvature),\n                      spacetime_unit_normal_vector, *pi, *phi);\n  trace(make_not_null(&trace_ex_curvature), ex_curvature,\n        inverse_spatial_metric);\n  gr::christoffel_first_kind(make_not_null(&spatial_christoffel_first),\n                             d_spatial_metric);\n  trace_last_indices(make_not_null(&trace_spatial_christoffel_first),\n                     spatial_christoffel_first, inverse_spatial_metric);\n\n  // Here we use `derivatives_of_spacetime_metric` to get \\f$ \\partial_a\n  // g_{bc}\\f$ instead, and use only the derivatives of \\f$ g_{bi}\\f$.\n  tnsr::abb<DataVector, Dim, Frame::Inertial> d4_spacetime_metric{};\n  gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&d4_spacetime_metric), lapse, shift, *pi, *phi);\n\n  Scalar<DataVector> half_pi_two_normals{get(lapse).size(), 0.0};\n  tnsr::i<DataVector, Dim, Frame::Inertial> half_phi_two_normals{\n      get(lapse).size(), 0.0};\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    get(half_pi_two_normals) += spacetime_unit_normal_vector.get(a) *\n                                spacetime_unit_normal_vector.get(a) *\n                                pi->get(a, a);\n    for (size_t i = 0; i < Dim; ++i) {\n      half_phi_two_normals.get(i) += 0.5 * spacetime_unit_normal_vector.get(a) *\n                                     spacetime_unit_normal_vector.get(a) *\n                                     phi->get(i, a, a);\n    }\n    for (size_t b = a + 1; b < Dim + 1; ++b) {\n      get(half_pi_two_normals) += 2.0 * spacetime_unit_normal_vector.get(a) *\n                                  spacetime_unit_normal_vector.get(b) *\n                                  pi->get(a, b);\n      for (size_t i = 0; i < Dim; ++i) {\n        half_phi_two_normals.get(i) += spacetime_unit_normal_vector.get(a) *\n                                       spacetime_unit_normal_vector.get(b) *\n                                       phi->get(i, a, b);\n      }\n    }\n  }\n  get(half_pi_two_normals) *= 0.5;\n\n  // Note: we pass in pi to compute d4_gauge_h, but we don't use d4_gauge_h. We\n  // actually reset pi from gauge_h below.\n  dispatch<AllSolutionsForChristoffelAnalytic>(\n      make_not_null(&gauge_h), make_not_null(&d4_gauge_h), lapse, shift,\n      sqrt_det_spatial_metric, inverse_spatial_metric, d4_spacetime_metric,\n      half_pi_two_normals, half_phi_two_normals, spacetime_metric, *phi, mesh,\n      time, inertial_coords, inverse_jacobian, gauge_condition);\n\n  // Compute lapse and shift time derivatives\n  get(dt_lapse) =\n      -get(lapse) * (get<0>(gauge_h) + get(lapse) * get(trace_ex_curvature));\n  for (size_t i = 0; i < Dim; ++i) {\n    get(dt_lapse) +=\n        shift.get(i) * (d_lapse.get(i) + get(lapse) * gauge_h.get(i + 1));\n  }\n\n  for (size_t i = 0; i < Dim; ++i) {\n    dt_shift.get(i) = get<0>(shift) * d_shift.get(0, i);\n    for (size_t k = 1; k < Dim; ++k) {\n      dt_shift.get(i) += shift.get(k) * d_shift.get(k, i);\n    }\n    for (size_t j = 0; j < Dim; ++j) {\n      dt_shift.get(i) +=\n          get(lapse) * inverse_spatial_metric.get(i, j) *\n          (get(lapse) *\n               (gauge_h.get(j + 1) + trace_spatial_christoffel_first.get(j)) -\n           d_lapse.get(j));\n    }\n  }\n\n  time_deriv_of_spatial_metric(make_not_null(&dt_spatial_metric), lapse, shift,\n                               *phi, *pi);\n  gh::pi(pi, lapse, dt_lapse, shift, dt_shift, spatial_metric,\n         dt_spatial_metric, *phi);\n}\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GaugeCondition.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  DhGaugeParameters.hpp\n  GaugeCondition.hpp\n  GaugeCondition.tpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/DhGaugeParameters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DhGaugeParameters.hpp\"\n#include \"Options/String.hpp\"\n\n/// \\cond\nnamespace gh::OptionTags {\nstruct Group;\n}  // namespace gh::OptionTags\n/// \\endcond\n\nnamespace gh::gauges {\nnamespace OptionTags {\ntemplate <bool UseRollon>\nstruct DhGaugeParameters {\n  using type = gh::gauges::DhGaugeParameters<UseRollon>;\n  static constexpr Options::String help{\n      \"Parameters for initializing damped harmonic gauge.\"};\n  using group = gh::OptionTags::Group;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\brief Input option tags for the generalized harmonic evolution system\ntemplate <bool UseRollon>\nstruct DhGaugeParameters : db::SimpleTag {\n  using ParametersType = gh::gauges::DhGaugeParameters<UseRollon>;\n  using type = ParametersType;\n  using option_tags =\n      tmpl::list<gh::gauges::OptionTags::DhGaugeParameters<UseRollon>>;\n\n  static constexpr bool pass_metavariables = false;\n  static ParametersType create_from_options(const ParametersType& parameters) {\n    return parameters;\n  }\n};\n}  // namespace Tags\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.tpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gh::gauges::Tags {\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)              \\\n  template class GaugeAndDerivativeCompute< \\\n      DIM(data), gh::Solutions::all_solutions<DIM(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace gh::gauges::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace gh::OptionTags {\nstruct Group;\n}  // namespace gh::OptionTags\n/// \\endcond\n\nnamespace gh::gauges {\nnamespace OptionTags {\nstruct GaugeCondition {\n  using type = std::unique_ptr<gauges::GaugeCondition>;\n  static constexpr Options::String help{\"The gauge condition to impose.\"};\n  using group = gh::OptionTags::Group;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\brief The gauge condition to impose.\nstruct GaugeCondition : db::SimpleTag {\n  using type = std::unique_ptr<gauges::GaugeCondition>;\n  using option_tags = tmpl::list<gh::gauges::OptionTags::GaugeCondition>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::unique_ptr<gauges::GaugeCondition> create_from_options(\n      const std::unique_ptr<gauges::GaugeCondition>& gauge_condition) {\n    return gauge_condition->get_clone();\n  }\n};\n\n/// \\brief Gauge condition \\f$H_a\\f$ and its spacetime derivative\n/// \\f$\\partial_b H_a\\f$\ntemplate <size_t Dim, class AllSolutions>\nstruct GaugeAndDerivativeCompute\n    : ::Tags::Variables<\n          tmpl::list<::gh::Tags::GaugeH<DataVector, Dim>,\n                     ::gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>>>,\n      db::ComputeTag {\n  using base = ::Tags::Variables<\n      tmpl::list<::gh::Tags::GaugeH<DataVector, Dim>,\n                 ::gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>>>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n      gr::Tags::SpacetimeNormalVector<DataVector, Dim>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n      gr::Tags::SpacetimeMetric<DataVector, Dim>, gh::Tags::Pi<DataVector, Dim>,\n      gh::Tags::Phi<DataVector, Dim>, ::Events::Tags::ObserverMesh<Dim>,\n      ::Tags::Time, ::Events::Tags::ObserverCoordinates<Dim, Frame::Inertial>,\n      ::Events::Tags::ObserverInverseJacobian<Dim, Frame::ElementLogical,\n                                              Frame::Inertial>,\n      Tags::GaugeCondition>;\n\n  static void function(\n      gsl::not_null<return_type*> gauge_and_deriv,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n      const tnsr::A<DataVector, Dim, Frame::Inertial>& spacetime_unit_normal,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::II<DataVector, Dim, Frame::Inertial>& inverse_spatial_metric,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n      const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n      const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n      const Mesh<Dim>& mesh, double time,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inverse_jacobian,\n      const gauges::GaugeCondition& gauge_condition);\n};\n}  // namespace Tags\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh::gauges::Tags {\ntemplate <size_t Dim, class AllSolutionsForChristoffelAnalytic>\nvoid GaugeAndDerivativeCompute<Dim, AllSolutionsForChristoffelAnalytic>::\n    function(\n        const gsl::not_null<return_type*> gauge_and_deriv,\n        const Scalar<DataVector>& lapse,\n        const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n        const tnsr::A<DataVector, Dim, Frame::Inertial>& spacetime_unit_normal,\n        const Scalar<DataVector>& sqrt_det_spatial_metric,\n        const tnsr::II<DataVector, Dim, Frame::Inertial>&\n            inverse_spatial_metric,\n        const tnsr::aa<DataVector, Dim, Frame::Inertial>& spacetime_metric,\n        const tnsr::aa<DataVector, Dim, Frame::Inertial>& pi,\n        const tnsr::iaa<DataVector, Dim, Frame::Inertial>& phi,\n        const Mesh<Dim>& mesh, const double time,\n        const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n        const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                              Frame::Inertial>& inverse_jacobian,\n        const gauges::GaugeCondition& gauge_condition) {\n  if (const size_t num_points = mesh.number_of_grid_points();\n      UNLIKELY(gauge_and_deriv->number_of_grid_points() != num_points)) {\n    gauge_and_deriv->initialize(num_points);\n  }\n  // Just compute d4_spacetime_metric internally for now. Since compute tags\n  // are only used for observing, this should be fine.\n  tnsr::abb<DataVector, Dim, Frame::Inertial> d4_spacetime_metric{\n      get(lapse).size()};\n  gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&d4_spacetime_metric), lapse, shift, pi, phi);\n  Scalar<DataVector> half_pi_two_normals{get(lapse).size(), 0.0};\n  tnsr::i<DataVector, Dim, Frame::Inertial> half_phi_two_normals{\n      get(lapse).size(), 0.0};\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    get(half_pi_two_normals) += spacetime_unit_normal.get(a) *\n                                spacetime_unit_normal.get(a) * pi.get(a, a);\n    for (size_t i = 0; i < Dim; ++i) {\n      half_phi_two_normals.get(i) += 0.5 * spacetime_unit_normal.get(a) *\n                                     spacetime_unit_normal.get(a) *\n                                     phi.get(i, a, a);\n    }\n    for (size_t b = a + 1; b < Dim + 1; ++b) {\n      get(half_pi_two_normals) += 2.0 * spacetime_unit_normal.get(a) *\n                                  spacetime_unit_normal.get(b) * pi.get(a, b);\n      for (size_t i = 0; i < Dim; ++i) {\n        half_phi_two_normals.get(i) += spacetime_unit_normal.get(a) *\n                                       spacetime_unit_normal.get(b) *\n                                       phi.get(i, a, b);\n      }\n    }\n  }\n  get(half_pi_two_normals) *= 0.5;\n\n  dispatch<AllSolutionsForChristoffelAnalytic>(\n      make_not_null(\n          &get<::gh::Tags::GaugeH<DataVector, Dim>>(*gauge_and_deriv)),\n      make_not_null(&get<::gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>>(\n          *gauge_and_deriv)),\n      lapse, shift, sqrt_det_spatial_metric, inverse_spatial_metric,\n      d4_spacetime_metric, half_pi_two_normals, half_phi_two_normals,\n      spacetime_metric, phi, mesh, time, inertial_coords, inverse_jacobian,\n      gauge_condition);\n}\n}  // namespace gh::gauges::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Norms.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace gh::Actions {\ntemplate <size_t Dim>\nstruct InitializeGhAnd3Plus1Variables {\n  using frame = Frame::Inertial;\n  using compute_tags = db::AddComputeTags<\n      // Needed to compute the characteristic speeds for the AH finder\n      gr::Tags::SpatialMetricCompute<DataVector, Dim, frame>,\n      gr::Tags::DetAndInverseSpatialMetricCompute<DataVector, Dim, frame>,\n      gr::Tags::ShiftCompute<DataVector, Dim, frame>,\n      gr::Tags::LapseCompute<DataVector, Dim, frame>,\n\n      // Compute constraint damping parameters.\n      gh::Tags::ConstraintGamma0Compute<Dim, Frame::Grid>,\n      gh::Tags::ConstraintGamma1Compute<Dim, Frame::Grid>,\n      gh::Tags::ConstraintGamma2Compute<Dim, Frame::Grid>>;\n\n  using const_global_cache_tags =\n      tmpl::list<gh::Tags::DampingFunctionGamma0<Dim, Frame::Grid>,\n                 gh::Tags::DampingFunctionGamma1<Dim, Frame::Grid>,\n                 gh::Tags::DampingFunctionGamma2<Dim, Frame::Grid>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace gh::Actions\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                   \\\n  template class Events::ObserveTimeStep<gh::System<DIM(data)>>; \\\n  template class dg::Events::ObserveTimeStepVolume<gh::System<DIM(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                  \\\n  template class ChangeTimeStepperOrder<gh::System<DIM(data)>>; \\\n  template class CleanHistory<gh::System<DIM(data)>>;           \\\n  template class RecordTimeStepperData<gh::System<DIM(data)>>;  \\\n  template class UpdateU<gh::System<DIM(data)>, false>;         \\\n  template class UpdateU<gh::System<DIM(data)>, true>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <optional>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void                                                                \\\n  evolution::dg::Actions::detail::volume_terms<::gh::TimeDerivative<           \\\n      gh::Solutions::all_solutions<DIM(data)>, DIM(data)>>(                    \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                          \\\n          ::Tags::dt,                                                          \\\n          typename ::gh::System<DIM(data)>::variables_tag::tags_list>>*>       \\\n          dt_vars_ptr,                                                         \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                          \\\n          ::Tags::Flux, typename ::gh::System<DIM(data)>::flux_variables,      \\\n          tmpl::size_t<DIM(data)>, Frame::Inertial>>*>                         \\\n          volume_fluxes,                                                       \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                          \\\n          ::Tags::deriv, typename ::gh::System<DIM(data)>::gradient_variables, \\\n          tmpl::size_t<DIM(data)>, Frame::Inertial>>*>                         \\\n          partial_derivs,                                                      \\\n      const gsl::not_null<Variables<typename ::gh::System<DIM(                 \\\n          data)>::compute_volume_time_derivative_terms::temporary_tags>*>      \\\n          temporaries,                                                         \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                          \\\n          ::Tags::div,                                                         \\\n          db::wrap_tags_in<::Tags::Flux,                                       \\\n                           typename ::gh::System<DIM(data)>::flux_variables,   \\\n                           tmpl::size_t<DIM(data)>, Frame::Inertial>>>*>       \\\n          div_fluxes,                                                          \\\n      const Variables<typename ::gh::System<DIM(                               \\\n          data)>::variables_tag::tags_list>& evolved_vars,                     \\\n      const ::dg::Formulation dg_formulation, const Mesh<DIM(data)>& mesh,     \\\n      [[maybe_unused]] const tnsr::I<DataVector, DIM(data), Frame::Inertial>&  \\\n          inertial_coordinates,                                                \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            Frame::Inertial>&                                  \\\n          logical_to_inertial_inverse_jacobian,                                \\\n      [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,   \\\n      const std::optional<tnsr::I<DataVector, DIM(data), Frame::Inertial>>&    \\\n          mesh_velocity,                                                       \\\n      const std::optional<Scalar<DataVector>>& div_mesh_velocity,              \\\n      const tnsr::aa<DataVector, DIM(data)>& spacetime_metric,                 \\\n      const tnsr::aa<DataVector, DIM(data)>& pi,                               \\\n      const tnsr::iaa<DataVector, DIM(data)>& phi,                             \\\n      const Scalar<DataVector>& gamma0, const Scalar<DataVector>& gamma1,      \\\n      const Scalar<DataVector>& gamma2,                                        \\\n      const ::gh::gauges::GaugeCondition& gauge_condition,                     \\\n      const Mesh<DIM(data)>& mesh_for_rhs, const double& time,                 \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& inertial_coords,  \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,      \\\n                            Frame::Inertial>& inverse_jacobian,                \\\n      const std::optional<tnsr::I<DataVector, DIM(data), Frame::Inertial>>&    \\\n          mesh_velocity_from_time_deriv_args);                                 \\\n  INSTANTIATE_PARTIAL_DERIVATIVES_WITH_SYSTEM(gh::System<DIM(data)>,           \\\n                                              DIM(data), Frame::Inertial)\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Equations.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\n/*!\n * \\ingroup EvolutionSystemsGroup\n * \\brief Items related to evolving the first-order generalized harmonic system.\n */\nnamespace gh {\ntemplate <size_t Dim>\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = false;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = Dim;\n  static constexpr bool is_euclidean = false;\n\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition<Dim>;\n\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                 Tags::Pi<DataVector, Dim>, Tags::Phi<DataVector, Dim>>>;\n  using flux_variables = tmpl::list<>;\n  using gradient_variables =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                 Tags::Pi<DataVector, Dim>, Tags::Phi<DataVector, Dim>>;\n  using gradients_tags = gradient_variables;\n\n  using compute_volume_time_derivative_terms =\n      TimeDerivative<gh::Solutions::all_solutions<Dim>, Dim>;\n  using normal_dot_fluxes = ComputeNormalDotFluxes<Dim>;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed<Dim, Frame::Inertial>;\n\n  using inverse_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, Dim>;\n};\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TagsDeclarations.hpp\"\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n\nnamespace gh {\nnamespace Tags {\n/*!\n * \\brief Conjugate momentum to the spacetime metric.\n *\n * \\details If \\f$ g_{ab} \\f$ is the spacetime metric, and \\f$ \\alpha \\f$ and\n * \\f$ \\beta^i \\f$ are the lapse and shift respectively, then we define\n * \\f$ \\Pi_{ab} = -\\frac{1}{\\alpha} ( \\partial_t g_{ab} + \\beta^{i} \\Phi_{iab} )\n * \\f$ where \\f$\\Phi_{iab}\\f$ is the variable defined by the tag Phi.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Pi : db::SimpleTag {\n  using type = tnsr::aa<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Auxiliary variable which is analytically the spatial derivative of the\n * spacetime metric\n * \\details If \\f$g_{ab}\\f$ is the spacetime metric then we define\n * \\f$\\Phi_{iab} = \\partial_i g_{ab}\\f$\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Phi : db::SimpleTag {\n  using type = tnsr::iaa<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Gauge source function for the generalized harmonic system.\n *\n * \\details In the generalized / damped harmonic gauge, unlike the simple\n * harmonic gauge, the right hand side of the gauge equation\n * \\f$ \\square x_a = H_a\\f$ is sourced by non-vanishing functions. This variable\n * stores those functions \\f$ H_a\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct GaugeH : db::SimpleTag {\n  using type = tnsr::a<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Spacetime derivatives of the gauge source function for the\n * generalized harmonic system.\n *\n * \\details In the generalized / damped harmonic gauge, the right hand side of\n * the gauge equation \\f$ \\square x_a = H_a\\f$ is sourced by non-vanishing\n * functions \\f$ H_a\\f$. This variable stores their spacetime derivatives\n * \\f$ \\partial_b H_a\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpacetimeDerivGaugeH : db::SimpleTag {\n  using type = tnsr::ab<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Initial value of the gauge source function for the generalized\n * harmonic system.\n *\n * \\details In the generalized / damped harmonic gauge, unlike the simple\n * harmonic gauge, the right hand side of the gauge equation\n * \\f$ \\square x_a = H_a\\f$ is sourced by non-vanishing functions. This variable\n * stores the initial or starting value of those functions \\f$ H_a\\f$, which\n * are set by the user (based on the choice of initial data) to begin evolution.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct InitialGaugeH : db::SimpleTag {\n  using type = tnsr::a<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Initial spacetime derivatives of the gauge source function\n * for the generalized harmonic system.\n *\n * \\details In the generalized / damped harmonic gauge, the right hand side of\n * the gauge equation \\f$ \\square x_a = H_a\\f$ is sourced by non-vanishing\n * functions \\f$ H_a\\f$. This variable stores the initial or starting value of\n * the spacetime derivatives of those functions \\f$ \\partial_b H_a\\f$, which\n * are set by the user (based on the choice of initial data) to begin evolution.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpacetimeDerivInitialGaugeH : db::SimpleTag {\n  using type = tnsr::ab<DataType, Dim, Frame>;\n};\n\n/// @{\n/// \\brief Tags corresponding to the characteristic fields of the generalized\n/// harmonic system.\n///\n/// \\details For details on how these are defined and computed, see\n/// CharacteristicSpeedsCompute\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct VSpacetimeMetric : db::SimpleTag {\n  using type = tnsr::aa<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct VZero : db::SimpleTag {\n  using type = tnsr::iaa<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct VPlus : db::SimpleTag {\n  using type = tnsr::aa<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct VMinus : db::SimpleTag {\n  using type = tnsr::aa<DataType, Dim, Frame>;\n};\n/// @}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct CharacteristicSpeeds : db::SimpleTag {\n  using type = std::array<DataType, 4>;\n};\n\n/*!\n * \\brief Characterisic speed as type `tnsr::I`, for use in observing in\n * `ElementLogical` frame in terms of logical directions.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct VSpacetimeMetricSpeed : db::SimpleTag {\n  using type = tnsr::I<DataType, SpatialDim, Frame>;\n};\n/// \\copydoc VSpacetimeMetricSpeed\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct VZeroSpeed : db::SimpleTag {\n  using type = tnsr::I<DataType, SpatialDim, Frame>;\n};\n/// \\copydoc VSpacetimeMetricSpeed\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct VPlusSpeed : db::SimpleTag {\n  using type = tnsr::I<DataType, SpatialDim, Frame>;\n};\n/// \\copydoc VSpacetimeMetricSpeed\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct VMinusSpeed : db::SimpleTag {\n  using type = tnsr::I<DataType, SpatialDim, Frame>;\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct CharacteristicFields : db::SimpleTag {\n  using type = Variables<tmpl::list<\n      VSpacetimeMetric<DataType, Dim, Frame>, VZero<DataType, Dim, Frame>,\n      VPlus<DataType, Dim, Frame>, VMinus<DataType, Dim, Frame>>>;\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct EvolvedFieldsFromCharacteristicFields : db::SimpleTag {\n  using type = Variables<\n      tmpl::list<gr::Tags::SpacetimeMetric<DataType, Dim, Frame>,\n                 Pi<DataType, Dim, Frame>, Phi<DataType, Dim, Frame>>>;\n};\n\n/*!\n * \\brief Tags corresponding to various constraints of the generalized\n * harmonic system, and their diagnostically useful combinations.\n * \\details For details on how these are defined and computed, see\n * `GaugeConstraintCompute`, `FConstraintCompute`, `TwoIndexConstraintCompute`,\n * `ThreeIndexConstraintCompute`, `FourIndexConstraintCompute`, and\n * `ConstraintEnergyCompute` respectively\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct GaugeConstraint : db::SimpleTag {\n  using type = tnsr::a<DataType, SpatialDim, Frame>;\n};\n/// \\copydoc GaugeConstraint\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct FConstraint : db::SimpleTag {\n  using type = tnsr::a<DataType, SpatialDim, Frame>;\n};\n/// \\copydoc GaugeConstraint\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct TwoIndexConstraint : db::SimpleTag {\n  using type = tnsr::ia<DataType, SpatialDim, Frame>;\n};\n/// \\copydoc GaugeConstraint\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct ThreeIndexConstraint : db::SimpleTag {\n  using type = tnsr::iaa<DataType, SpatialDim, Frame>;\n};\n/// \\copydoc GaugeConstraint\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct FourIndexConstraint : db::SimpleTag {\n  using type = tnsr::iaa<DataType, SpatialDim, Frame>;\n};\n/// \\copydoc GaugeConstraint\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct ConstraintEnergy : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n}  // namespace Tags\n\nnamespace OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * Groups option tags related to the GeneralizedHarmonic evolution system.\n */\nstruct Group {\n  static std::string name() { return \"GeneralizedHarmonic\"; }\n  static constexpr Options::String help{\"Options for the GH evolution system\"};\n  using group = evolution::OptionTags::SystemGroup;\n};\n}  // namespace OptionTags\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n\nnamespace gh {\n\n/// \\brief Tags for the generalized harmonic formulation of Einstein equations\nnamespace Tags {\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct Pi;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct Phi;\n\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct InitialGaugeH;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpacetimeDerivInitialGaugeH;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct GaugeH;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpacetimeDerivGaugeH;\n\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct VSpacetimeMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct VZero;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct VPlus;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct VMinus;\n\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::ElementLogical>\nstruct VSpacetimeMetricSpeed;\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::ElementLogical>\nstruct VZeroSpeed;\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::ElementLogical>\nstruct VPlusSpeed;\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::ElementLogical>\nstruct VMinusSpeed;\n\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct CharacteristicSpeeds;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct CharacteristicFields;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct EvolvedFieldsFromCharacteristicFields;\n\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::Inertial>\nstruct GaugeConstraint;\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::Inertial>\nstruct FConstraint;\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::Inertial>\nstruct TwoIndexConstraint;\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::Inertial>\nstruct ThreeIndexConstraint;\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::Inertial>\nstruct FourIndexConstraint;\ntemplate <typename DataType, size_t SpatialDim,\n          typename Frame = Frame::Inertial>\nstruct ConstraintEnergy;\n}  // namespace Tags\n\n/// \\brief Input option tags for the generalized harmonic evolution system\nnamespace OptionTags {\nstruct Group;\n}  // namespace OptionTags\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.tpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n// Explicit instantiations of structs defined in `Equations.cpp` as well as of\n// `partial_derivatives` function for use in the computation of spatial\n// derivatives of `gradients_tags`, and of the initial gauge source function\n// (needed in `Initialize.hpp`).\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n\nusing derivative_frame = Frame::Inertial;\n\ntemplate <size_t Dim>\nusing derivative_tags_initial_gauge =\n    tmpl::list<gh::Tags::InitialGaugeH<DataVector, Dim, derivative_frame>>;\n\ntemplate <size_t Dim>\nusing variables_tags_initial_gauge =\n    tmpl::list<gh::Tags::InitialGaugeH<DataVector, Dim, derivative_frame>>;\n\ntemplate <size_t Dim>\nusing derivative_tags = typename gh::System<Dim>::gradients_tags;\n\ntemplate <size_t Dim>\nusing variables_tags = typename gh::System<Dim>::variables_tag::tags_list;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                                  \\\n  template struct gh::TimeDerivative<gh::Solutions::all_solutions<DIM(data)>, \\\n                                     DIM(data)>;                              \\\n  template Variables<                                                         \\\n      db::wrap_tags_in<::Tags::deriv, derivative_tags<DIM(data)>,             \\\n                       tmpl::size_t<DIM(data)>, derivative_frame>>            \\\n  partial_derivatives<derivative_tags<DIM(data)>, variables_tags<DIM(data)>,  \\\n                      DIM(data), derivative_frame>(                           \\\n      const Variables<variables_tags<DIM(data)>>& u,                          \\\n      const Mesh<DIM(data)>& mesh,                                            \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,     \\\n                            derivative_frame>& inverse_jacobian);             \\\n  template Variables<db::wrap_tags_in<                                        \\\n      ::Tags::deriv, derivative_tags_initial_gauge<DIM(data)>,                \\\n      tmpl::size_t<DIM(data)>, derivative_frame>>                             \\\n  partial_derivatives<derivative_tags_initial_gauge<DIM(data)>,               \\\n                      variables_tags_initial_gauge<DIM(data)>, DIM(data),     \\\n                      derivative_frame>(                                      \\\n      const Variables<variables_tags_initial_gauge<DIM(data)>>& u,            \\\n      const Mesh<DIM(data)>& mesh,                                            \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,     \\\n                            derivative_frame>& inverse_jacobian);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/DuDtTempTags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n\ntemplate <typename, typename, typename>\nclass Tensor;\n/// \\endcond\n\nnamespace gh {\n/*!\n * \\brief Compute the RHS of the Generalized Harmonic formulation of\n * Einstein's equations.\n *\n * The evolved variables are the spacetime metric \\f$g_{ab}\\f$, its spatial\n * derivative \\f$\\Phi_{iab}=\\partial_i g_{ab}\\f$, and conjugate momentum\n * \\f$\\Pi_{ab}=n^c\\partial_c g_{ab}\\f$, where \\f$n^a\\f$ is the spacetime\n * unit normal vector. The evolution equations are (Eqs. 35-57 of\n * \\cite Lindblom2005qh)\n *\n * \\f{align}{\n *   \\partial_t g_{ab}-\n *   &\\left(1+\\gamma_1\\right)\\beta^k\\partial_k g_{ab} - \\gamma_1 v^i_g\n * \\mathcal{C}_{iab} =\n *     -\\alpha \\Pi_{ab}-\\gamma_1\\beta^i\\Phi_{iab}, \\\\\n *\n *   \\partial_t\\Pi_{ab}-\n *   &\\beta^k\\partial_k\\Pi_{ab} + \\alpha \\gamma^{ki}\\partial_k\\Phi_{iab}\n *     - \\gamma_1\\gamma_2\\beta^k\\partial_kg_{ab} - \\gamma_1 \\gamma_2 v^i_g\n * \\mathcal{C}_{iab} \\notag\\\\\n *   =&2\\alpha g^{cd}\\left(\\gamma^{ij}\\Phi_{ica}\\Phi_{jdb}\n *      - \\Pi_{ca}\\Pi_{db} - g^{ef}\\Gamma_{ace}\\Gamma_{bdf}\\right) \\notag \\\\\n *   &-2\\alpha \\nabla_{(a}H_{b)}\n *     - \\frac{1}{2}\\alpha n^c n^d\\Pi_{cd}\\Pi_{ab}\n *     - \\alpha n^c \\Pi_{ci}\\gamma^{ij}\\Phi_{jab} \\notag \\\\\n *   &+\\alpha \\gamma_0\\left(2\\delta^c{}_{(a} n_{b)}\n *     - (1 + \\gamma_3)g_{ab}n^c\\right)\\mathcal{C}_c \\notag \\\\\n *   &+ 2 \\gamma_4 \\alpha \\Pi_{ab} n^c \\mathcal{C}_c \\notag \\\\\n *   &- \\gamma_5\\alpha n^c\\mathcal{C}_c \\left(\\frac{\\mathcal{C}_a\\mathcal{C}_b\n *     - \\frac{1}{2} g_{ab} \\mathcal{C}_d \\mathcal{C}^d}\n *     {\\epsilon_{5} + 2 n^d \\mathcal{C}_d n^e \\mathcal{C}_e\n *     + \\mathcal{C}_d \\mathcal{C}^d} \\right) \\notag \\\\\n *   &-\\gamma_1\\gamma_2 \\beta^i\\Phi_{iab} \\notag \\\\\n *   &-16\\pi \\alpha \\left(T_{ab} - \\frac{1}{2}g_{ab}T^c{}_c\\right),\\\\\n *\n *   \\partial_t\\Phi_{iab}-\n *   &\\beta^k\\partial_k\\Phi_{iab} + \\alpha \\partial_i\\Pi_{ab}\n *     - \\alpha \\gamma_2\\partial_ig_{ab} \\notag \\\\\n *   =&\\frac{1}{2}\\alpha n^c n^d\\Phi_{icd}\\Pi_{ab}\n *      + \\alpha \\gamma^{jk}n^c\\Phi_{ijc}\\Phi_{kab} \\notag \\\\\n *   &-\\alpha \\gamma_2\\Phi_{iab},\n * \\f}\n *\n * where \\f$H_a\\f$ is the gauge source function,\n * \\f$\\mathcal{C}_a=H_a+\\Gamma_a\\f$ is the gauge constraint,\n * \\f$\\mathcal{C}_{iab}\\f$ is the 3-index constraint, and\n * \\f$v^i_g\\f$ is the mesh velocity. The constraint\n * damping parameters \\f$\\gamma_0\\f$ \\f$\\gamma_1\\f$, \\f$\\gamma_2\\f$,\n * \\f$\\gamma_3\\f$, \\f$\\gamma_4\\f$, and \\f$\\gamma_5\\f$ have units of inverse time\n * and control the time scales on which the constraints are damped to zero.\n *\n * \\note We have not coded up the constraint damping terms for \\f$\\gamma_3\\f$,\n * \\f$\\gamma_4\\f$, and \\f$\\gamma_5\\f$. \\f$\\gamma_3\\f$ was found to be essential\n * for evolutions of black strings by Pretorius and Lehner \\cite Lehner2010pn.\n *\n * \\note Here the only terms dependent on mesh velocity are constraint damping\n * terms added to this implementation of the generalized harmonic system.\n * Mesh-velocity corrections that are applicable to all systems are made in\n * `evolution::dg::Actions::detail::volume_terms()`.\n *\n * \\warning When using harmonic gauge,\n * gr::Tags::SqrtDetSpatialMetric<DataVector> and\n * gr::Tags::SpacetimeChristoffelSecondKind<Dim, Frame::Inertial, DataVector>\n * are not computed. In Debug mode, they are filled with with signaling NaNs.\n */\ntemplate <class AllSolutionsForChristoffelAnalytic, size_t Dim>\nstruct TimeDerivative {\n public:\n  using temporary_tags = tmpl::list<\n      ::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2,\n      Tags::GaugeH<DataVector, Dim>,\n      Tags::SpacetimeDerivGaugeH<DataVector, Dim>, Tags::Gamma1Gamma2,\n      Tags::HalfPiTwoNormals, Tags::NormalDotOneIndexConstraint,\n      Tags::Gamma1Plus1, Tags::PiOneNormal<Dim>,\n      Tags::GaugeConstraint<DataVector, Dim>, Tags::HalfPhiTwoNormals<Dim>,\n      Tags::ShiftDotThreeIndexConstraint<Dim>,\n      Tags::MeshVelocityDotThreeIndexConstraint<Dim>, Tags::PhiOneNormal<Dim>,\n      Tags::PiSecondIndexUp<Dim>, Tags::ThreeIndexConstraint<DataVector, Dim>,\n      Tags::PhiFirstIndexUp<Dim>, Tags::PhiThirdIndexUp<Dim>,\n      Tags::SpacetimeChristoffelFirstKindThirdIndexUp<Dim>,\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n      gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n      gr::Tags::DetSpatialMetric<DataVector>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, Dim>,\n      gr::Tags::SpacetimeChristoffelFirstKind<DataVector, Dim>,\n      gr::Tags::SpacetimeChristoffelSecondKind<DataVector, Dim>,\n      gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, Dim>,\n      gr::Tags::SpacetimeNormalVector<DataVector, Dim>>;\n  using argument_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                 Tags::Pi<DataVector, Dim>, Tags::Phi<DataVector, Dim>,\n                 ::gh::Tags::ConstraintGamma0, ::gh::Tags::ConstraintGamma1,\n                 ::gh::Tags::ConstraintGamma2, gauges::Tags::GaugeCondition,\n                 domain::Tags::Mesh<Dim>, ::Tags::Time,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>,\n                 domain::Tags::MeshVelocity<Dim, Frame::Inertial>>;\n\n  static evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      gsl::not_null<tnsr::aa<DataVector, Dim>*> dt_spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, Dim>*> dt_pi,\n      gsl::not_null<tnsr::iaa<DataVector, Dim>*> dt_phi,\n      gsl::not_null<Scalar<DataVector>*> temp_gamma1,\n      gsl::not_null<Scalar<DataVector>*> temp_gamma2,\n      gsl::not_null<tnsr::a<DataVector, Dim>*> temp_gauge_function,\n      gsl::not_null<tnsr::ab<DataVector, Dim>*>\n          temp_spacetime_deriv_gauge_function,\n      gsl::not_null<Scalar<DataVector>*> gamma1gamma2,\n      gsl::not_null<Scalar<DataVector>*> half_half_pi_two_normals,\n      gsl::not_null<Scalar<DataVector>*> normal_dot_gauge_constraint,\n      gsl::not_null<Scalar<DataVector>*> gamma1_plus_1,\n      gsl::not_null<tnsr::a<DataVector, Dim>*> pi_one_normal,\n      gsl::not_null<tnsr::a<DataVector, Dim>*> gauge_constraint,\n      gsl::not_null<tnsr::i<DataVector, Dim>*> half_phi_two_normals,\n      gsl::not_null<tnsr::aa<DataVector, Dim>*>\n          shift_dot_three_index_constraint,\n      gsl::not_null<tnsr::aa<DataVector, Dim>*>\n          mesh_velocity_dot_three_index_constraint,\n      gsl::not_null<tnsr::ia<DataVector, Dim>*> phi_one_normal,\n      gsl::not_null<tnsr::aB<DataVector, Dim>*> pi_2_up,\n      gsl::not_null<tnsr::iaa<DataVector, Dim>*> three_index_constraint,\n      gsl::not_null<tnsr::Iaa<DataVector, Dim>*> phi_1_up,\n      gsl::not_null<tnsr::iaB<DataVector, Dim>*> phi_3_up,\n      gsl::not_null<tnsr::abC<DataVector, Dim>*> christoffel_first_kind_3_up,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, Dim>*> shift,\n      gsl::not_null<tnsr::II<DataVector, Dim>*> inverse_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> det_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> sqrt_det_spatial_metric,\n      gsl::not_null<tnsr::AA<DataVector, Dim>*> inverse_spacetime_metric,\n      gsl::not_null<tnsr::abb<DataVector, Dim>*> christoffel_first_kind,\n      gsl::not_null<tnsr::Abb<DataVector, Dim>*> christoffel_second_kind,\n      gsl::not_null<tnsr::a<DataVector, Dim>*> trace_christoffel,\n      gsl::not_null<tnsr::A<DataVector, Dim>*> normal_spacetime_vector,\n      const tnsr::iaa<DataVector, Dim>& d_spacetime_metric,\n      const tnsr::iaa<DataVector, Dim>& d_pi,\n      const tnsr::ijaa<DataVector, Dim>& d_phi,\n      const tnsr::aa<DataVector, Dim>& spacetime_metric,\n      const tnsr::aa<DataVector, Dim>& pi,\n      const tnsr::iaa<DataVector, Dim>& phi, const Scalar<DataVector>& gamma0,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n      const gauges::GaugeCondition& gauge_condition, const Mesh<Dim>& mesh,\n      double time,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inverse_jacobian,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity);\n};\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GeneralizedHarmonic/TimeDerivative.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/DuDtTempTags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <class AllSolutionsForChristoffelAnalytic, size_t Dim>\nevolution::dg::TimeDerivativeDecisions<Dim>\nTimeDerivative<AllSolutionsForChristoffelAnalytic, Dim>::apply(\n    const gsl::not_null<tnsr::aa<DataVector, Dim>*> dt_spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, Dim>*> dt_pi,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim>*> dt_phi,\n    const gsl::not_null<Scalar<DataVector>*> temp_gamma1,\n    const gsl::not_null<Scalar<DataVector>*> temp_gamma2,\n    const gsl::not_null<tnsr::a<DataVector, Dim>*> gauge_function,\n    const gsl::not_null<tnsr::ab<DataVector, Dim>*>\n        spacetime_deriv_gauge_function,\n    const gsl::not_null<Scalar<DataVector>*> gamma1gamma2,\n    const gsl::not_null<Scalar<DataVector>*> half_pi_two_normals,\n    const gsl::not_null<Scalar<DataVector>*> normal_dot_gauge_constraint,\n    const gsl::not_null<Scalar<DataVector>*> gamma1_plus_1,\n    const gsl::not_null<tnsr::a<DataVector, Dim>*> pi_one_normal,\n    const gsl::not_null<tnsr::a<DataVector, Dim>*> gauge_constraint,\n    const gsl::not_null<tnsr::i<DataVector, Dim>*> half_phi_two_normals,\n    const gsl::not_null<tnsr::aa<DataVector, Dim>*>\n        shift_dot_three_index_constraint,\n    const gsl::not_null<tnsr::aa<DataVector, Dim>*>\n        mesh_velocity_dot_three_index_constraint,\n    const gsl::not_null<tnsr::ia<DataVector, Dim>*> phi_one_normal,\n    const gsl::not_null<tnsr::aB<DataVector, Dim>*> pi_2_up,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim>*> three_index_constraint,\n    const gsl::not_null<tnsr::Iaa<DataVector, Dim>*> phi_1_up,\n    const gsl::not_null<tnsr::iaB<DataVector, Dim>*> phi_3_up,\n    const gsl::not_null<tnsr::abC<DataVector, Dim>*>\n        christoffel_first_kind_3_up,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> shift,\n    const gsl::not_null<tnsr::II<DataVector, Dim>*> inverse_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> det_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> sqrt_det_spatial_metric,\n    const gsl::not_null<tnsr::AA<DataVector, Dim>*> inverse_spacetime_metric,\n    const gsl::not_null<tnsr::abb<DataVector, Dim>*> christoffel_first_kind,\n    const gsl::not_null<tnsr::Abb<DataVector, Dim>*> christoffel_second_kind,\n    const gsl::not_null<tnsr::a<DataVector, Dim>*> trace_christoffel,\n    const gsl::not_null<tnsr::A<DataVector, Dim>*> normal_spacetime_vector,\n    const tnsr::iaa<DataVector, Dim>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, Dim>& d_pi,\n    const tnsr::ijaa<DataVector, Dim>& d_phi,\n    const tnsr::aa<DataVector, Dim>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim>& pi, const tnsr::iaa<DataVector, Dim>& phi,\n    const Scalar<DataVector>& gamma0, const Scalar<DataVector>& gamma1,\n    const Scalar<DataVector>& gamma2,\n    const gauges::GaugeCondition& gauge_condition, const Mesh<Dim>& mesh,\n    double time,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity) {\n  const size_t number_of_points = get<0, 0>(*dt_spacetime_metric).size();\n  // Need constraint damping on interfaces in DG schemes\n  *temp_gamma1 = gamma1;\n  *temp_gamma2 = gamma2;\n\n  const tnsr::ii<DataVector, Dim> spatial_metric{};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      make_const_view(make_not_null(&spatial_metric.get(i, j)),\n                      spacetime_metric.get(i + 1, j + 1), 0, number_of_points);\n    }\n  }\n  determinant_and_inverse(det_spatial_metric, inverse_spatial_metric,\n                          spatial_metric);\n  gr::shift(shift, spacetime_metric, *inverse_spatial_metric);\n  gr::lapse(lapse, *shift, spacetime_metric);\n  gr::inverse_spacetime_metric(inverse_spacetime_metric, *lapse, *shift,\n                               *inverse_spatial_metric);\n  // Compute the part of the dt_spacetime_metric equation that doesn't involve\n  // constraints so we can use it for da_spacetime_metric to compute Christoffel\n  // symbols.\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    for (size_t nu = mu; nu < Dim + 1; ++nu) {\n      dt_spacetime_metric->get(mu, nu) = -get(*lapse) * pi.get(mu, nu);\n      for (size_t m = 0; m < Dim; ++m) {\n        dt_spacetime_metric->get(mu, nu) += shift->get(m) * phi.get(m, mu, nu);\n      }\n    }\n  }\n\n  const std::optional da_spacetime_metric{tnsr::abb<DataVector, Dim>{}};\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = a; b < Dim + 1; ++b) {\n      make_const_view(make_not_null(&da_spacetime_metric.value().get(0, a, b)),\n                      dt_spacetime_metric->get(a, b), 0, number_of_points);\n      for (size_t i = 0; i < Dim; ++i) {\n        make_const_view(\n            make_not_null(&da_spacetime_metric.value().get(i + 1, a, b)),\n            phi.get(i, a, b), 0, number_of_points);\n      }\n    }\n  }\n\n  gr::christoffel_first_kind(christoffel_first_kind,\n                             da_spacetime_metric.value());\n  trace_last_indices(trace_christoffel, *christoffel_first_kind,\n                     *inverse_spacetime_metric);\n  gr::spacetime_normal_vector(normal_spacetime_vector, *lapse, *shift);\n\n  get(*gamma1gamma2) = get(gamma1) * get(gamma2);\n  const DataVector& gamma12 = get(*gamma1gamma2);\n\n  for (size_t m = 0; m < Dim; ++m) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      for (size_t nu = mu; nu < Dim + 1; ++nu) {\n        phi_1_up->get(m, mu, nu) =\n            inverse_spatial_metric->get(m, 0) * phi.get(0, mu, nu);\n        for (size_t n = 1; n < Dim; ++n) {\n          phi_1_up->get(m, mu, nu) +=\n              inverse_spatial_metric->get(m, n) * phi.get(n, mu, nu);\n        }\n      }\n    }\n  }\n\n  for (size_t m = 0; m < Dim; ++m) {\n    for (size_t nu = 0; nu < Dim + 1; ++nu) {\n      for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n        phi_3_up->get(m, nu, alpha) =\n            inverse_spacetime_metric->get(alpha, 0) * phi.get(m, nu, 0);\n        for (size_t beta = 1; beta < Dim + 1; ++beta) {\n          phi_3_up->get(m, nu, alpha) +=\n              inverse_spacetime_metric->get(alpha, beta) * phi.get(m, nu, beta);\n        }\n      }\n    }\n  }\n\n  for (size_t nu = 0; nu < Dim + 1; ++nu) {\n    for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n      pi_2_up->get(nu, alpha) =\n          inverse_spacetime_metric->get(alpha, 0) * pi.get(nu, 0);\n      for (size_t beta = 1; beta < Dim + 1; ++beta) {\n        pi_2_up->get(nu, alpha) +=\n            inverse_spacetime_metric->get(alpha, beta) * pi.get(nu, beta);\n      }\n    }\n  }\n\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    for (size_t nu = 0; nu < Dim + 1; ++nu) {\n      for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n        christoffel_first_kind_3_up->get(mu, nu, alpha) =\n            inverse_spacetime_metric->get(alpha, 0) *\n            christoffel_first_kind->get(mu, nu, 0);\n        for (size_t beta = 1; beta < Dim + 1; ++beta) {\n          christoffel_first_kind_3_up->get(mu, nu, alpha) +=\n              inverse_spacetime_metric->get(alpha, beta) *\n              christoffel_first_kind->get(mu, nu, beta);\n        }\n      }\n    }\n  }\n\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    pi_one_normal->get(mu) = get<0>(*normal_spacetime_vector) * pi.get(0, mu);\n    for (size_t nu = 1; nu < Dim + 1; ++nu) {\n      pi_one_normal->get(mu) +=\n          normal_spacetime_vector->get(nu) * pi.get(nu, mu);\n    }\n  }\n\n  get(*half_pi_two_normals) =\n      get<0>(*normal_spacetime_vector) * get<0>(*pi_one_normal);\n  for (size_t mu = 1; mu < Dim + 1; ++mu) {\n    get(*half_pi_two_normals) +=\n        normal_spacetime_vector->get(mu) * pi_one_normal->get(mu);\n  }\n  get(*half_pi_two_normals) *= 0.5;\n\n  for (size_t n = 0; n < Dim; ++n) {\n    for (size_t nu = 0; nu < Dim + 1; ++nu) {\n      phi_one_normal->get(n, nu) =\n          get<0>(*normal_spacetime_vector) * phi.get(n, 0, nu);\n      for (size_t mu = 1; mu < Dim + 1; ++mu) {\n        phi_one_normal->get(n, nu) +=\n            normal_spacetime_vector->get(mu) * phi.get(n, mu, nu);\n      }\n    }\n  }\n\n  for (size_t n = 0; n < Dim; ++n) {\n    half_phi_two_normals->get(n) =\n        get<0>(*normal_spacetime_vector) * phi_one_normal->get(n, 0);\n    for (size_t mu = 1; mu < Dim + 1; ++mu) {\n      half_phi_two_normals->get(n) +=\n          normal_spacetime_vector->get(mu) * phi_one_normal->get(n, mu);\n    }\n    half_phi_two_normals->get(n) *= 0.5;\n  }\n\n  for (size_t n = 0; n < Dim; ++n) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      for (size_t nu = mu; nu < Dim + 1; ++nu) {\n        three_index_constraint->get(n, mu, nu) =\n            d_spacetime_metric.get(n, mu, nu) - phi.get(n, mu, nu);\n      }\n    }\n  }\n\n  get(*gamma1_plus_1) = 1.0 + gamma1.get();\n  const DataVector& gamma1p1 = get(*gamma1_plus_1);\n\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    gauge_constraint->get(mu) = trace_christoffel->get(mu);\n    for (size_t nu = mu; nu < Dim + 1; ++nu) {\n      shift_dot_three_index_constraint->get(mu, nu) =\n          get<0>(*shift) * three_index_constraint->get(0, mu, nu);\n      if (mesh_velocity.has_value()) {\n        mesh_velocity_dot_three_index_constraint->get(mu, nu) =\n            get<0>(*mesh_velocity) * three_index_constraint->get(0, mu, nu);\n      }\n      for (size_t m = 1; m < Dim; ++m) {\n        shift_dot_three_index_constraint->get(mu, nu) +=\n            shift->get(m) * three_index_constraint->get(m, mu, nu);\n        if (mesh_velocity.has_value()) {\n          mesh_velocity_dot_three_index_constraint->get(mu, nu) +=\n              mesh_velocity->get(m) * three_index_constraint->get(m, mu, nu);\n        }\n      }\n    }\n  }\n\n  const bool using_harmonic_gauge = gauge_condition.is_harmonic();\n  if (not using_harmonic_gauge) {\n    // Compute gauge condition.\n    get(*sqrt_det_spatial_metric) = sqrt(get(*det_spatial_metric));\n    raise_or_lower_first_index(christoffel_second_kind, *christoffel_first_kind,\n                               *inverse_spacetime_metric);\n  }\n  gauges::dispatch<AllSolutionsForChristoffelAnalytic, Dim>(\n      gauge_function, spacetime_deriv_gauge_function, *lapse, *shift,\n      *sqrt_det_spatial_metric, *inverse_spatial_metric, *da_spacetime_metric,\n      *half_pi_two_normals, *half_phi_two_normals, spacetime_metric, phi, mesh,\n      time, inertial_coords, inverse_jacobian, gauge_condition);\n  if (not using_harmonic_gauge) {\n    // Compute source function last so that we don't need to recompute any of\n    // the other temporary tags.\n    for (size_t nu = 0; nu < Dim + 1; ++nu) {\n      gauge_constraint->get(nu) += gauge_function->get(nu);\n    }\n  }\n\n  get(*normal_dot_gauge_constraint) =\n      get<0>(*normal_spacetime_vector) * get<0>(*gauge_constraint);\n  for (size_t mu = 1; mu < Dim + 1; ++mu) {\n    get(*normal_dot_gauge_constraint) +=\n        normal_spacetime_vector->get(mu) * gauge_constraint->get(mu);\n  }\n\n  // Invalidate da_spacetime_metric since we will be modifying some of the\n  // data it points to.\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n  const_cast<std::optional<tnsr::abb<DataVector, Dim>>&>(da_spacetime_metric) =\n      std::nullopt;\n\n  // Here are the actual equations\n\n  // Equation for dt_spacetime_metric\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    for (size_t nu = mu; nu < Dim + 1; ++nu) {\n      dt_spacetime_metric->get(mu, nu) +=\n          gamma1p1 * shift_dot_three_index_constraint->get(mu, nu);\n      if (mesh_velocity.has_value()) {\n        dt_spacetime_metric->get(mu, nu) +=\n            get(gamma1) * mesh_velocity_dot_three_index_constraint->get(mu, nu);\n      }\n    }\n  }\n\n  // Equation for dt_pi\n\n  // We first compute the n_a contributions but only for a=0 since n_i=0\n  // identically. We also use dt_Pi_{00} as temporary storage to avoid any extra\n  // allocations and multiply normal_dot_gauge_constraint=(n^a C_a) by gamma0\n  // since it always shows up multiplied by gamma0 in the equations. This\n  // reduces the number of multiplications that are needed for the RHS\n  // evaluation.\n  //\n  // WARNING: normal_dot_gauge_constraint is rescaled by gamma0!\n  get(*normal_dot_gauge_constraint) *= get(gamma0);\n\n  // Use dt_pi_{00} as temporary storage.\n  get<0, 0>(*dt_pi) = -get(gamma0) * get(*lapse);\n  for (size_t i = 1; i < Dim + 1; ++i) {\n    dt_pi->get(0, i) =\n        get<0, 0>(*dt_pi) * gauge_constraint->get(i) -\n        get(*normal_dot_gauge_constraint) * spacetime_metric.get(0, i);\n  }\n  get<0, 0>(*dt_pi) =\n      2.0 * get<0, 0>(*dt_pi) * get<0>(*gauge_constraint) -\n      get(*normal_dot_gauge_constraint) * get<0, 0>(spacetime_metric);\n\n  // Set space-space components\n  for (size_t mu = 1; mu < Dim + 1; ++mu) {\n    for (size_t nu = mu; nu < Dim + 1; ++nu) {\n      dt_pi->get(mu, nu) =\n          -get(*normal_dot_gauge_constraint) * spacetime_metric.get(mu, nu);\n    }\n  }\n\n  // Add additional pieces to dt_pi that aren't just n_a*(stuff)\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    for (size_t nu = mu; nu < Dim + 1; ++nu) {\n      dt_pi->get(mu, nu) -= get(*half_pi_two_normals) * pi.get(mu, nu);\n\n      if (not using_harmonic_gauge) {\n        dt_pi->get(mu, nu) -= spacetime_deriv_gauge_function->get(mu, nu) +\n                              spacetime_deriv_gauge_function->get(nu, mu);\n      }\n      for (size_t delta = 0; delta < Dim + 1; ++delta) {\n        dt_pi->get(mu, nu) -= 2 * pi.get(mu, delta) * pi_2_up->get(nu, delta);\n        if (not using_harmonic_gauge) {\n          dt_pi->get(mu, nu) += 2 *\n                                christoffel_second_kind->get(delta, mu, nu) *\n                                gauge_function->get(delta);\n        }\n        for (size_t n = 0; n < Dim; ++n) {\n          dt_pi->get(mu, nu) +=\n              2 * phi_1_up->get(n, mu, delta) * phi_3_up->get(n, nu, delta);\n        }\n\n        for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n          dt_pi->get(mu, nu) -=\n              2. * christoffel_first_kind_3_up->get(mu, alpha, delta) *\n              christoffel_first_kind_3_up->get(nu, delta, alpha);\n        }\n      }\n\n      for (size_t m = 0; m < Dim; ++m) {\n        dt_pi->get(mu, nu) -=\n            pi_one_normal->get(m + 1) * phi_1_up->get(m, mu, nu);\n\n        for (size_t n = 0; n < Dim; ++n) {\n          dt_pi->get(mu, nu) -=\n              inverse_spatial_metric->get(m, n) * d_phi.get(m, n, mu, nu);\n        }\n      }\n\n      dt_pi->get(mu, nu) *= get(*lapse);\n\n      dt_pi->get(mu, nu) +=\n          gamma12 * shift_dot_three_index_constraint->get(mu, nu);\n      if (mesh_velocity.has_value()) {\n        dt_pi->get(mu, nu) +=\n            gamma12 * mesh_velocity_dot_three_index_constraint->get(mu, nu);\n      }\n\n      for (size_t m = 0; m < Dim; ++m) {\n        // DualFrame term\n        dt_pi->get(mu, nu) += shift->get(m) * d_pi.get(m, mu, nu);\n      }\n    }\n  }\n\n  // Equation for dt_phi\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      for (size_t nu = mu; nu < Dim + 1; ++nu) {\n        dt_phi->get(i, mu, nu) =\n            pi.get(mu, nu) * half_phi_two_normals->get(i) -\n            d_pi.get(i, mu, nu) +\n            get(gamma2) * three_index_constraint->get(i, mu, nu);\n        for (size_t n = 0; n < Dim; ++n) {\n          dt_phi->get(i, mu, nu) +=\n              phi_one_normal->get(i, n + 1) * phi_1_up->get(n, mu, nu);\n        }\n\n        dt_phi->get(i, mu, nu) *= get(*lapse);\n        for (size_t m = 0; m < Dim; ++m) {\n          dt_phi->get(i, mu, nu) += shift->get(m) * d_phi.get(m, i, mu, nu);\n        }\n      }\n    }\n  }\n  return {true};\n}\n}  // namespace gh\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(GhValenciaDivClean)\nadd_subdirectory(ValenciaDivClean)\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  SetInitialData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  SetInitialData.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Actions/SetInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Actions/SetInitialData.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <optional>\n#include <string>\n#include <utility>\n#include <variant>\n\nnamespace grmhd::GhValenciaDivClean {\n\nNumericInitialData::NumericInitialData(\n    std::string file_glob, std::string subfile_name,\n    std::variant<double, importers::ObservationSelector> observation_value,\n    std::optional<double> observation_value_epsilon,\n    const bool enable_interpolation,\n    typename GhNumericId::Variables::type gh_selected_variables,\n    typename HydroNumericId::Variables::type hydro_selected_variables,\n    const double density_cutoff)\n    : gh_numeric_id_(file_glob, subfile_name, observation_value,\n                     observation_value_epsilon.value_or(1.0e-12),\n                     enable_interpolation, std::move(gh_selected_variables)),\n      hydro_numeric_id_(\n          std::move(file_glob), std::move(subfile_name), observation_value,\n          observation_value_epsilon.value_or(1.0e-12), enable_interpolation,\n          std::move(hydro_selected_variables), density_cutoff) {}\n\nNumericInitialData::NumericInitialData(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nPUP::able::PUP_ID NumericInitialData::my_PUP_ID = 0;\n\nsize_t NumericInitialData::volume_data_id() const {\n  size_t hash = 0;\n  boost::hash_combine(hash, gh_numeric_id_.volume_data_id());\n  boost::hash_combine(hash, hydro_numeric_id_.volume_data_id());\n  return hash;\n}\n\nvoid NumericInitialData::pup(PUP::er& p) {\n  p | gh_numeric_id_;\n  p | hydro_numeric_id_;\n}\n\nbool operator==(const NumericInitialData& lhs, const NumericInitialData& rhs) {\n  return lhs.gh_numeric_id_ == rhs.gh_numeric_id_ and\n         lhs.hydro_numeric_id_ == rhs.hydro_numeric_id_;\n}\n\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Actions/SetInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/NumericInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Actions/NumericInitialData.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean {\n\n/*!\n * \\brief Numeric initial data loaded from volume data files\n *\n * This class can be factory-created in the input file to start an evolution\n * from numeric initial data. It selects the set of GH variables to load from\n * the volume data file (ADM or GH variables), the set of hydro variables, and\n * allows to set constant values for some of the hydro variables. This class\n * mostly combines the `gh::NumericInitialData` and\n * `grmhd::ValenciaDivClean::NumericInitialData` classes.\n */\nclass NumericInitialData : public evolution::initial_data::InitialData,\n                           public evolution::NumericInitialData {\n private:\n  using GhNumericId = gh::NumericInitialData;\n  using HydroNumericId = grmhd::ValenciaDivClean::NumericInitialData;\n\n public:\n  using all_vars =\n      tmpl::append<GhNumericId::all_vars, HydroNumericId::all_vars>;\n\n  struct GhVariables : GhNumericId::Variables {};\n  struct HydroVariables : HydroNumericId::Variables {};\n\n  using options =\n      tmpl::push_back<importers::ImporterOptions::tags_list, GhVariables,\n                      HydroVariables, HydroNumericId::DensityCutoff>;\n\n  static constexpr Options::String help =\n      \"Numeric initial data loaded from volume data files\";\n\n  NumericInitialData() = default;\n  NumericInitialData(const NumericInitialData& rhs) = default;\n  NumericInitialData& operator=(const NumericInitialData& rhs) = default;\n  NumericInitialData(NumericInitialData&& /*rhs*/) = default;\n  NumericInitialData& operator=(NumericInitialData&& /*rhs*/) = default;\n  ~NumericInitialData() = default;\n\n  /// \\cond\n  explicit NumericInitialData(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NumericInitialData);\n  /// \\endcond\n\n  std::unique_ptr<evolution::initial_data::InitialData> get_clone()\n      const override {\n    return std::make_unique<NumericInitialData>(*this);\n  }\n\n  NumericInitialData(\n      std::string file_glob, std::string subfile_name,\n      std::variant<double, importers::ObservationSelector> observation_value,\n      std::optional<double> observation_value_epsilon,\n      bool enable_interpolation,\n      typename GhNumericId::Variables::type gh_selected_variables,\n      typename HydroNumericId::Variables::type hydro_selected_variables,\n      double density_cutoff);\n\n  const importers::ImporterOptions& importer_options() const {\n    return gh_numeric_id_.importer_options();\n  }\n\n  const GhNumericId& gh_numeric_id() const { return gh_numeric_id_; }\n\n  const HydroNumericId& hydro_numeric_id() const { return hydro_numeric_id_; }\n\n  size_t volume_data_id() const;\n\n  template <typename... AllTags>\n  void select_for_import(\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> fields) const {\n    gh_numeric_id_.select_for_import(fields);\n    hydro_numeric_id_.select_for_import(fields);\n  }\n\n  template <typename... AllTags, size_t ThermodynamicDim>\n  void set_initial_data(\n      const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n      const gsl::not_null<tnsr::aa<DataVector, 3>*> pi,\n      const gsl::not_null<tnsr::iaa<DataVector, 3>*> phi,\n      const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      const gsl::not_null<tnsr::I<DataVector, 3>*> spatial_velocity,\n      const gsl::not_null<tnsr::I<DataVector, 3>*> magnetic_field,\n      const gsl::not_null<Scalar<DataVector>*> div_cleaning_field,\n      const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      const gsl::not_null<Scalar<DataVector>*> pressure,\n      const gsl::not_null<Scalar<DataVector>*> temperature,\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> numeric_data,\n      const Mesh<3>& mesh,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n          equation_of_state) const {\n    gh_numeric_id_.set_initial_data(spacetime_metric, pi, phi, numeric_data,\n                                    mesh, inv_jacobian);\n    const auto spatial_metric = gr::spatial_metric(*spacetime_metric);\n    const auto inv_spatial_metric =\n        determinant_and_inverse(spatial_metric).second;\n    hydro_numeric_id_.set_initial_data(\n        rest_mass_density, electron_fraction, specific_internal_energy,\n        spatial_velocity, magnetic_field, div_cleaning_field, lorentz_factor,\n        pressure, temperature, numeric_data, inv_spatial_metric,\n        equation_of_state);\n  }\n\n  void pup(PUP::er& p) override;\n\n  friend bool operator==(const NumericInitialData& lhs,\n                         const NumericInitialData& rhs);\n\n private:\n  GhNumericId gh_numeric_id_{};\n  HydroNumericId hydro_numeric_id_{};\n};\n\nnamespace Actions {\n\n/*!\n * \\brief Dispatch loading numeric initial data from files.\n *\n * Place this action before\n * grmhd::GhValenciaDivClean::Actions::SetNumericInitialData in the action list.\n * See importers::Actions::ReadAllVolumeDataAndDistribute for details, which is\n * invoked by this action.\n */\nstruct SetInitialData {\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const parallel_component) {\n    // Dispatch to the correct `apply` overload based on type of initial data\n    using initial_data_classes =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 evolution::initial_data::InitialData>;\n    return call_with_dynamic_type<Parallel::iterable_action_return_t,\n                                  initial_data_classes>(\n        &db::get<evolution::initial_data::Tags::InitialData>(box),\n        [&box, &cache, &array_index,\n         &parallel_component](const auto* const initial_data) {\n          return apply(make_not_null(&box), *initial_data, cache, array_index,\n                       parallel_component);\n        });\n  }\n\n private:\n  static constexpr size_t Dim = 3;\n\n  // Numeric initial data\n  template <typename DbTagsList, typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,\n      const NumericInitialData& initial_data,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ParallelComponent* const /*meta*/) {\n    // If we are using GH Numeric ID, then we don't have to set Pi and Phi since\n    // we are reading them in. Also we only need to mutate this tag once so do\n    // it on the first element.\n    if (is_zeroth_element(array_index) and\n        std::holds_alternative<gh::NumericInitialData::GhVars>(\n            initial_data.gh_numeric_id().selected_variables())) {\n      Parallel::mutate<gh::Tags::SetPiAndPhiFromConstraints,\n                       gh::gauges::SetPiAndPhiFromConstraintsCacheMutator>(\n          cache, false);\n    }\n    // Select the subset of the available variables that we want to read from\n    // the volume data file\n    tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n        importers::Tags::Selected, NumericInitialData::all_vars>>\n        selected_fields{};\n    initial_data.select_for_import(make_not_null(&selected_fields));\n    // Dispatch loading the variables from the volume data file\n    // - Not using `ckLocalBranch` here to make sure the simple action\n    //   invocation is asynchronous.\n    auto& reader_component = Parallel::get_parallel_component<\n        importers::ElementDataReader<Metavariables>>(cache);\n    Parallel::simple_action<importers::Actions::ReadAllVolumeDataAndDistribute<\n        3, NumericInitialData::all_vars, ParallelComponent>>(\n        reader_component, initial_data.importer_options(),\n        initial_data.volume_data_id(), std::move(selected_fields));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  // \"AnalyticData\"-type initial data\n  template <typename DbTagsList, typename InitialData, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const InitialData& initial_data,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*meta*/) {\n    // Get ADM + hydro variables from analytic data / solution\n    const auto& [coords, mesh, inv_jacobian] = [&box]() {\n      if constexpr (db::tag_is_retrievable_v<\n                        evolution::dg::subcell::Tags::ActiveGrid,\n                        db::DataBox<DbTagsList>>) {\n        const bool on_subcell =\n            db::get<evolution::dg::subcell::Tags::ActiveGrid>(*box) ==\n            evolution::dg::subcell::ActiveGrid::Subcell;\n        if (on_subcell) {\n          return std::forward_as_tuple(\n              db::get<evolution::dg::subcell::Tags::Coordinates<\n                  Dim, Frame::Inertial>>(*box),\n              db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(*box),\n              db::get<evolution::dg::subcell::fd::Tags::\n                          InverseJacobianLogicalToInertial<Dim>>(*box));\n        }\n      }\n      return std::forward_as_tuple(\n          db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(*box),\n          db::get<domain::Tags::Mesh<Dim>>(*box),\n          db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                Frame::Inertial>>(*box));\n    }();\n    auto vars = evolution::Initialization::initial_data(\n        initial_data, coords, db::get<::Tags::Time>(*box),\n        tmpl::append<tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                                gr::Tags::Lapse<DataVector>,\n                                gr::Tags::Shift<DataVector, 3>,\n                                gr::Tags::ExtrinsicCurvature<DataVector, 3>>,\n                     hydro::grmhd_tags<DataVector>>{});\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n    const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n    const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n    const auto& extrinsic_curvature =\n        get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(vars);\n\n    // Compute GH vars from ADM vars\n    db::mutate<gr::Tags::SpacetimeMetric<DataVector, 3>,\n               gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>(\n        &gh::initial_gh_variables_from_adm<3>, box, spatial_metric, lapse,\n        shift, extrinsic_curvature, mesh, inv_jacobian);\n\n    // Move hydro vars directly into the DataBox\n    tmpl::for_each<hydro::grmhd_tags<DataVector>>(\n        [&box, &vars](const auto tag_v) {\n          using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n          Initialization::mutate_assign<tmpl::list<tag>>(\n              box, std::move(get<tag>(vars)));\n        });\n\n    // No need to import numeric initial data, so we terminate the phase by\n    // pausing the algorithm on this element\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Receive numeric initial data loaded by\n * grmhd::GhValenciaDivClean::Actions::SetInitialData.\n *\n * Place this action in the action list after\n * grmhd::GhValenciaDivClean::Actions::SetInitialData to wait until the\n * data for this element has arrived, and then compute the GH variables and the\n * remaining primitive variables and store them in the DataBox to be used as\n * initial data.\n *\n * This action modifies the GH system tags (spacetime metric, pi, phi) and the\n * tags listed in `hydro::grmhd_tags` in the DataBox (i.e., the hydro\n * primitives). It does not modify conservative variables, so it relies on a\n * primitive-to-conservative update in the action list before the evolution can\n * start.\n *\n * \\requires This action requires an equation of state, which is retrieved from\n * the DataBox as `hydro::Tags::GrmhdEquationOfState`.\n */\nstruct ReceiveNumericInitialData {\n  static constexpr size_t Dim = 3;\n  using inbox_tags =\n      tmpl::list<importers::Tags::VolumeData<NumericInitialData::all_vars>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox =\n        tuples::get<importers::Tags::VolumeData<NumericInitialData::all_vars>>(\n            inboxes);\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        db::get<evolution::initial_data::Tags::InitialData>(box));\n    const size_t volume_data_id = initial_data.volume_data_id();\n    if (inbox.find(volume_data_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    auto numeric_data = std::move(inbox.extract(volume_data_id).mapped());\n\n    const auto& [mesh, inv_jacobian] = [&box]() {\n      if constexpr (db::tag_is_retrievable_v<\n                        evolution::dg::subcell::Tags::ActiveGrid,\n                        db::DataBox<DbTagsList>>) {\n        const bool on_subcell =\n            db::get<evolution::dg::subcell::Tags::ActiveGrid>(box) ==\n            evolution::dg::subcell::ActiveGrid::Subcell;\n        if (on_subcell) {\n          return std::forward_as_tuple(\n              db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(box),\n              db::get<evolution::dg::subcell::fd::Tags::\n                          InverseJacobianLogicalToInertial<Dim>>(box));\n        }\n      }\n      return std::forward_as_tuple(\n          db::get<domain::Tags::Mesh<Dim>>(box),\n          db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                Frame::Inertial>>(box));\n    }();\n    const auto& equation_of_state =\n        db::get<hydro::Tags::GrmhdEquationOfState>(box);\n\n    db::mutate<gr::Tags::SpacetimeMetric<DataVector, 3>,\n               gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n               hydro::Tags::RestMassDensity<DataVector>,\n               hydro::Tags::ElectronFraction<DataVector>,\n               hydro::Tags::SpecificInternalEnergy<DataVector>,\n               hydro::Tags::SpatialVelocity<DataVector, 3>,\n               hydro::Tags::MagneticField<DataVector, 3>,\n               hydro::Tags::DivergenceCleaningField<DataVector>,\n               hydro::Tags::LorentzFactor<DataVector>,\n               hydro::Tags::Pressure<DataVector>,\n               hydro::Tags::Temperature<DataVector>>(\n        [&initial_data, &numeric_data, &equation_of_state](\n            const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n            const gsl::not_null<tnsr::aa<DataVector, 3>*> pi,\n            const gsl::not_null<tnsr::iaa<DataVector, 3>*> phi,\n            const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n            const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n            const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n            const gsl::not_null<tnsr::I<DataVector, 3>*> spatial_velocity,\n            const gsl::not_null<tnsr::I<DataVector, 3>*> magnetic_field,\n            const gsl::not_null<Scalar<DataVector>*> div_cleaning_field,\n            const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n            const gsl::not_null<Scalar<DataVector>*> pressure,\n            const gsl::not_null<Scalar<DataVector>*> temperature,\n            const auto& local_mesh, const auto& local_inv_jacobian) {\n          initial_data.set_initial_data(\n              spacetime_metric, pi, phi, rest_mass_density, electron_fraction,\n              specific_internal_energy, spatial_velocity, magnetic_field,\n              div_cleaning_field, lorentz_factor, pressure, temperature,\n              make_not_null(&numeric_data), local_mesh, local_inv_jacobian,\n              equation_of_state);\n        },\n        make_not_null(&box), mesh, inv_jacobian);\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Actions/SetInitialData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GhGrMhd/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GhGrMhd/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/Factory.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// Check if SpEC is linked and therefore we can load SpEC initial data\n#ifdef HAS_SPEC_EXPORTER\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SpecInitialData.hpp\"\nusing SpecInitialDataList = tmpl::list<grmhd::AnalyticData::SpecInitialData<1>,\n                                       grmhd::AnalyticData::SpecInitialData<2>,\n                                       grmhd::AnalyticData::SpecInitialData<3>>;\n#else\nusing SpecInitialDataList = NoSuchType;\n#endif\n\n// Check if FUKA is linked and therefore we can load FUKA initial data\n#ifdef HAS_FUKA_EXPORTER\n#include \"PointwiseFunctions/AnalyticData/GrMhd/FukaInitialData.hpp\"\nusing FukaInitialDataList = tmpl::list<grmhd::AnalyticData::FukaInitialData>;\n#else\nusing FukaInitialDataList = NoSuchType;\n#endif\n\nnamespace ghmhd::GhValenciaDivClean::InitialData {\n// These are solutions that can be used for analytic prescriptions\nusing analytic_solutions_and_data_list =\n    tmpl::append<gh::RelativisticEuler::Solutions::all_solutions,\n                 gh::grmhd::Solutions::all_solutions,\n                 RadiationTransport::MonteCarlo::Solutions::all_solutions,\n                 gh::grmhd::AnalyticData::all_analytic_data>;\nusing initial_data_list = tmpl::flatten<tmpl::list<\n    analytic_solutions_and_data_list,\n    tmpl::flatten<tmpl::list<\n        grmhd::GhValenciaDivClean::NumericInitialData,\n        tmpl::conditional_t<std::is_same_v<SpecInitialDataList, NoSuchType>,\n                            tmpl::list<>, SpecInitialDataList>,\n        tmpl::conditional_t<std::is_same_v<FukaInitialDataList, NoSuchType>,\n                            tmpl::list<>, FukaInitialDataList>>>>>;\n}  // namespace ghmhd::GhValenciaDivClean::InitialData\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::BoundaryConditions {\nBoundaryCondition::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\nvoid BoundaryCondition::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n}  // namespace grmhd::GhValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\n/// \\brief Boundary conditions for the combined Generalized Harmonic and\n/// Valencia GRMHD systems\nnamespace BoundaryConditions {\n\n/// \\brief The base class for Generalized Harmonic and Valencia combined\n/// boundary conditions; all boundary conditions for this system must inherit\n/// from this base class.\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace BoundaryConditions\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  ConstraintPreservingFreeOutflow.cpp\n  DirichletAnalytic.cpp\n  DirichletFreeOutflow.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  ConstraintPreservingFreeOutflow.hpp\n  DirichletAnalytic.hpp\n  DirichletFreeOutflow.hpp\n  Factory.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/ConstraintPreservingFreeOutflow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/ConstraintPreservingFreeOutflow.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\nnamespace grmhd::GhValenciaDivClean::BoundaryConditions {\nConstraintPreservingFreeOutflow::ConstraintPreservingFreeOutflow(\n    gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType type,\n    std::optional<std::unique_ptr<::MathFunction<1, Frame::Inertial>>>\n        incoming_wave_profile)\n    : constraint_preserving_(type, std::move(incoming_wave_profile)) {}\n\n// LCOV_EXCL_START\nConstraintPreservingFreeOutflow::ConstraintPreservingFreeOutflow(\n    CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n// LCOV_EXCL_STOP\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nConstraintPreservingFreeOutflow::get_clone() const {\n  return std::make_unique<ConstraintPreservingFreeOutflow>(*this);\n}\n\nvoid ConstraintPreservingFreeOutflow::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | constraint_preserving_;\n}\n\nstd::optional<std::string> ConstraintPreservingFreeOutflow::dg_ghost(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity_one_form,\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& interior_spacetime_metric,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& interior_pi,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& interior_phi,\n\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_temperature,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*coords*/,\n    const Scalar<DataVector>& interior_gamma1,\n    const Scalar<DataVector>& interior_gamma2,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::I<DataVector, 3>& interior_shift,\n    const tnsr::II<DataVector, 3>& interior_inv_spatial_metric,\n    const tnsr::AA<DataVector, 3,\n                   Frame::Inertial>& /*inverse_spacetime_metric*/,\n    const tnsr::A<DataVector, 3, Frame::Inertial>&\n    /*spacetime_unit_normal_vector*/,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*three_index_constraint*/,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& /*gauge_source*/,\n    const tnsr::ab<DataVector, 3, Frame::Inertial>&\n    /*spacetime_deriv_gauge_source*/,\n\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, 3, Frame::Inertial>&\n    /*logical_dt_spacetime_metric*/,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& /*logical_dt_pi*/,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*logical_dt_phi*/,\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*d_spacetime_metric*/,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*d_pi*/,\n    const tnsr::ijaa<DataVector, 3, Frame::Inertial>& /*d_phi*/) {\n  *gamma1 = interior_gamma1;\n  *gamma2 = interior_gamma2;\n  *spacetime_metric = interior_spacetime_metric;\n  *pi = interior_pi;\n  *phi = interior_phi;\n  *lapse = interior_lapse;\n  *shift = interior_shift;\n  *inv_spatial_metric = interior_inv_spatial_metric;\n\n  return grmhd::ValenciaDivClean::BoundaryConditions::HydroFreeOutflow::\n      dg_ghost(tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n\n               tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n               tilde_b_flux, tilde_phi_flux,\n\n               lapse, shift, spatial_velocity_one_form, rest_mass_density,\n               electron_fraction, temperature, spatial_velocity,\n               inv_spatial_metric,\n\n               face_mesh_velocity, normal_covector, normal_vector,\n\n               interior_rest_mass_density, interior_electron_fraction,\n               interior_specific_internal_energy, interior_spatial_velocity,\n               interior_magnetic_field, interior_lorentz_factor,\n               interior_pressure, interior_temperature,\n\n               *shift, *lapse, *inv_spatial_metric);\n}\n\nstd::optional<std::string> ConstraintPreservingFreeOutflow::dg_time_derivative(\n    gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        dt_spacetime_metric_correction,\n    gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> dt_pi_correction,\n    gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> dt_phi_correction,\n    const gsl::not_null<Scalar<DataVector>*> dt_tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> dt_tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> dt_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> dt_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> dt_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> dt_tilde_phi,\n\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n    // c.f. dg_interior_evolved_variables_tags\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& pi,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& phi,\n    // c.f. dg_interior_primitive_variables_tags\n    const Scalar<DataVector>& /*interior_rest_mass_density*/,\n    const Scalar<DataVector>& /*interior_electron_fraction*/,\n    const Scalar<DataVector>& /*interior_specific_internal_energy*/,\n    const tnsr::I<DataVector, 3,\n                  Frame::Inertial>& /*interior_spatial_velocity*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*interior_magnetic_field*/,\n    const Scalar<DataVector>& /*interior_lorentz_factor*/,\n    const Scalar<DataVector>& /*interior_pressure*/,\n    const Scalar<DataVector>& /*interior_temperature*/,\n\n    // c.f. dg_interior_temporary_tags\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3>& /*interior_inv_spatial_metric*/,\n    const tnsr::AA<DataVector, 3, Frame::Inertial>& inverse_spacetime_metric,\n    const tnsr::A<DataVector, 3, Frame::Inertial>& spacetime_unit_normal_vector,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& three_index_constraint,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& gauge_source,\n    const tnsr::ab<DataVector, 3, Frame::Inertial>&\n        spacetime_deriv_gauge_source,\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& logical_dt_spacetime_metric,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& logical_dt_pi,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& logical_dt_phi,\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& d_pi,\n    const tnsr::ijaa<DataVector, 3, Frame::Inertial>& d_phi) const {\n  get(*dt_tilde_d) = 0.0;\n  get(*dt_tilde_ye) = 0.0;\n  get(*dt_tilde_tau) = 0.0;\n  for (size_t i = 0; i < 3; ++i) {\n    dt_tilde_s->get(i) = 0.0;\n    dt_tilde_b->get(i) = 0.0;\n  }\n  get(*dt_tilde_phi) = 0.0;\n  return constraint_preserving_.dg_time_derivative(\n      dt_spacetime_metric_correction, dt_pi_correction, dt_phi_correction,\n      face_mesh_velocity, normal_covector, normal_vector, spacetime_metric, pi,\n      phi, coords, gamma1, gamma2, lapse, shift, inverse_spacetime_metric,\n      spacetime_unit_normal_vector, three_index_constraint, gauge_source,\n      spacetime_deriv_gauge_source, logical_dt_spacetime_metric, logical_dt_pi,\n      logical_dt_phi, d_spacetime_metric, d_pi, d_phi);\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID ConstraintPreservingFreeOutflow::my_PUP_ID = 0;\n}  // namespace grmhd::GhValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/ConstraintPreservingFreeOutflow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::BoundaryConditions {\n/*!\n * \\brief Sets constraint-preserving boundary conditions on the spacetime\n * variables and hydro free outflow on the GRMHD variables.\n *\n * \\warning This is only implemented for DG, on FD you get an error. The\n * reason is that some care and experimentation is necessary to impose the\n * boundary condition correctly on FD.\n */\nclass ConstraintPreservingFreeOutflow final : public BoundaryCondition {\n public:\n  using options =\n      typename gh::BoundaryConditions::ConstraintPreservingBjorhus<3>::options;\n  static constexpr Options::String help{\n      \"ConstraintPreservingAnalytic boundary conditions  for GH variables and \"\n      \"hydro free outflow for GRMHD.\"};\n\n  ConstraintPreservingFreeOutflow() = default;\n  explicit ConstraintPreservingFreeOutflow(\n      gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType type,\n      std::optional<std::unique_ptr<::MathFunction<1, Frame::Inertial>>>\n          incoming_wave_profile = std::nullopt);\n\n  ConstraintPreservingFreeOutflow(ConstraintPreservingFreeOutflow&&) = default;\n  ConstraintPreservingFreeOutflow& operator=(\n      ConstraintPreservingFreeOutflow&&) = default;\n  ConstraintPreservingFreeOutflow(const ConstraintPreservingFreeOutflow&) =\n      default;\n  ConstraintPreservingFreeOutflow& operator=(\n      const ConstraintPreservingFreeOutflow&) = default;\n  ~ConstraintPreservingFreeOutflow() override = default;\n\n  explicit ConstraintPreservingFreeOutflow(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition,\n      ConstraintPreservingFreeOutflow);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::GhostAndTimeDerivative;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>,\n                 ::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpacetimeMetric<DataVector, 3>,\n                 gr::Tags::SpacetimeNormalVector<DataVector, 3>,\n                 gh::Tags::ThreeIndexConstraint<DataVector, 3>,\n                 gh::Tags::GaugeH<DataVector, 3>,\n                 gh::Tags::SpacetimeDerivGaugeH<DataVector, 3>>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_ghost(\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n      gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n\n      gsl::not_null<Scalar<DataVector>*> gamma1,\n      gsl::not_null<Scalar<DataVector>*> gamma2,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          spatial_velocity_one_form,\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& interior_spacetime_metric,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& interior_pi,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& interior_phi,\n\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_temperature,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& /*coords*/,\n      const Scalar<DataVector>& interior_gamma1,\n      const Scalar<DataVector>& interior_gamma2,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::I<DataVector, 3>& interior_shift,\n      const tnsr::II<DataVector, 3>& interior_inv_spatial_metric,\n      const tnsr::AA<DataVector, 3,\n                     Frame::Inertial>& /*inverse_spacetime_metric*/,\n      const tnsr::A<DataVector, 3, Frame::Inertial>&\n      /*spacetime_unit_normal_vector*/,\n      const tnsr::iaa<DataVector, 3,\n                      Frame::Inertial>& /*three_index_constraint*/,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& /*gauge_source*/,\n      const tnsr::ab<DataVector, 3, Frame::Inertial>&\n      /*spacetime_deriv_gauge_source*/,\n\n      // c.f. dg_interior_dt_vars_tags\n      const tnsr::aa<DataVector, 3, Frame::Inertial>&\n      /*logical_dt_spacetime_metric*/,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& /*logical_dt_pi*/,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*logical_dt_phi*/,\n      // c.f. dg_interior_deriv_vars_tags\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*d_spacetime_metric*/,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*d_pi*/,\n      const tnsr::ijaa<DataVector, 3, Frame::Inertial>& /*d_phi*/);\n\n  using dg_interior_dt_vars_tags =\n      tmpl::list<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                 ::Tags::dt<gh::Tags::Pi<DataVector, 3>>,\n                 ::Tags::dt<gh::Tags::Phi<DataVector, 3>>>;\n  using dg_interior_deriv_vars_tags =\n      tmpl::list<::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::deriv<gh::Tags::Pi<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>>;\n\n  std::optional<std::string> dg_time_derivative(\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n          dt_spacetime_metric_correction,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> dt_pi_correction,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*>\n          dt_phi_correction,\n      gsl::not_null<Scalar<DataVector>*> dt_tilde_d,\n      gsl::not_null<Scalar<DataVector>*> dt_tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> dt_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> dt_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> dt_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> dt_tilde_phi,\n\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      // c.f. dg_interior_evolved_variables_tags\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& pi,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& phi,\n      // c.f. dg_interior_primitive_variables_tags\n      const Scalar<DataVector>& /*interior_rest_mass_density*/,\n      const Scalar<DataVector>& /*interior_electron_fraction*/,\n      const Scalar<DataVector>& /*interior_specific_internal_energy*/,\n      const tnsr::I<DataVector, 3,\n                    Frame::Inertial>& /*interior_spatial_velocity*/,\n      const tnsr::I<DataVector, 3,\n                    Frame::Inertial>& /*interior_magnetic_field*/,\n      const Scalar<DataVector>& /*interior_lorentz_factor*/,\n      const Scalar<DataVector>& /*interior_pressure*/,\n      const Scalar<DataVector>& /*interior_temperature*/,\n\n      // c.f. dg_interior_temporary_tags\n      const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const tnsr::II<DataVector, 3>& /*interior_inv_spatial_metric*/,\n      const tnsr::AA<DataVector, 3, Frame::Inertial>& inverse_spacetime_metric,\n      const tnsr::A<DataVector, 3, Frame::Inertial>&\n          spacetime_unit_normal_vector,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& three_index_constraint,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& gauge_source,\n      const tnsr::ab<DataVector, 3, Frame::Inertial>&\n          spacetime_deriv_gauge_source,\n      // c.f. dg_interior_dt_vars_tags\n      const tnsr::aa<DataVector, 3, Frame::Inertial>&\n          logical_dt_spacetime_metric,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& logical_dt_pi,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& logical_dt_phi,\n      // c.f. dg_interior_deriv_vars_tags\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& d_spacetime_metric,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& d_pi,\n      const tnsr::ijaa<DataVector, 3, Frame::Inertial>& d_phi) const;\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags = tmpl::list<>;\n  using fd_interior_primitive_variables_tags = tmpl::list<>;\n  using fd_gridless_tags = tmpl::list<>;\n\n  [[noreturn]] static void fd_ghost(\n      const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n      /*spacetime_metric*/,\n      const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> /*pi*/,\n      const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> /*phi*/,\n      const gsl::not_null<Scalar<DataVector>*> /*rest_mass_density*/,\n      const gsl::not_null<Scalar<DataVector>*> /*electron_fraction*/,\n      const gsl::not_null<Scalar<DataVector>*> /*pressure*/,\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n      /*lorentz_factor_times_spatial_velocity*/,\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n      /*magnetic_field*/,\n      const gsl::not_null<Scalar<DataVector>*> /*divergence_cleaning_field*/,\n      const Direction<3>& /*direction*/) {\n    ERROR(\n        \"Not implemented because it's not trivial to figure out what the right \"\n        \"way of handling this case is.\");\n  }\n\n private:\n  gh::BoundaryConditions::ConstraintPreservingBjorhus<3>\n      constraint_preserving_{};\n};\n}  // namespace grmhd::GhValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::BoundaryConditions {\n\n// LCOV_EXCL_START\ntemplate <typename System>\nDirichletAnalytic<System>::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n// LCOV_EXCL_STOP\ntemplate <typename System>\nDirichletAnalytic<System>::DirichletAnalytic(\n    const DirichletAnalytic<System>& rhs)\n    : BoundaryCondition{dynamic_cast<const BoundaryCondition&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\ntemplate <typename System>\nDirichletAnalytic<System>& DirichletAnalytic<System>::operator=(\n    const DirichletAnalytic<System>& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\ntemplate <typename System>\nDirichletAnalytic<System>::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\ntemplate <typename System>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic<System>::get_clone() const {\n  return std::make_unique<DirichletAnalytic<System>>(*this);\n}\n\ntemplate <typename System>\nvoid DirichletAnalytic<System>::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | analytic_prescription_;\n}\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic<System>::my_PUP_ID = 0;\n\ntemplate <typename System>\nstd::optional<std::string> DirichletAnalytic<System>::dg_ghost(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity_one_form,\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<\n        tnsr::I<DataVector, 3, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    const Scalar<DataVector>& interior_gamma1,\n    const Scalar<DataVector>& interior_gamma2,\n    [[maybe_unused]] const double time) const {\n  *gamma1 = interior_gamma1;\n  *gamma2 = interior_gamma2;\n\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<\n          hydro::Tags::RestMassDensity<DataVector>,\n          hydro::Tags::ElectronFraction<DataVector>,\n          hydro::Tags::SpecificInternalEnergy<DataVector>,\n          hydro::Tags::Pressure<DataVector>,\n          hydro::Tags::Temperature<DataVector>,\n          hydro::Tags::SpatialVelocity<DataVector, 3>,\n          hydro::Tags::LorentzFactor<DataVector>,\n          hydro::Tags::MagneticField<DataVector, 3>,\n          hydro::Tags::DivergenceCleaningField<DataVector>,\n          gr::Tags::SpatialMetric<DataVector, 3>,\n          gr::Tags::InverseSpatialMetric<DataVector, 3>,\n          gr::Tags::SqrtDetSpatialMetric<DataVector>,\n          gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n          gr::Tags::SpacetimeMetric<DataVector, 3>,\n          ::gh::Tags::Pi<DataVector, 3>, ::gh::Tags::Phi<DataVector, 3>>,\n      ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const initial_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(\n              coords, time,\n              tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                         hydro::Tags::ElectronFraction<DataVector>,\n                         hydro::Tags::SpecificInternalEnergy<DataVector>,\n                         hydro::Tags::Pressure<DataVector>,\n                         hydro::Tags::Temperature<DataVector>,\n                         hydro::Tags::SpatialVelocity<DataVector, 3>,\n                         hydro::Tags::LorentzFactor<DataVector>,\n                         hydro::Tags::MagneticField<DataVector, 3>,\n                         hydro::Tags::DivergenceCleaningField<DataVector>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                         gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>,\n                         gr::Tags::SpacetimeMetric<DataVector, 3>,\n                         ::gh::Tags::Pi<DataVector, 3>,\n                         ::gh::Tags::Phi<DataVector, 3>>{});\n\n        } else if constexpr (evolution::is_numeric_initial_data_v<\n                                 std::decay_t<decltype(*initial_data)>>) {\n          ERROR(\n              \"Cannot currently use numeric initial data as an analytic \"\n              \"prescription in boundary conditions.\");\n        } else {\n          (void)time;\n          return initial_data->variables(\n              coords,\n              tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                         hydro::Tags::ElectronFraction<DataVector>,\n                         hydro::Tags::SpecificInternalEnergy<DataVector>,\n                         hydro::Tags::Pressure<DataVector>,\n                         hydro::Tags::Temperature<DataVector>,\n                         hydro::Tags::SpatialVelocity<DataVector, 3>,\n                         hydro::Tags::LorentzFactor<DataVector>,\n                         hydro::Tags::MagneticField<DataVector, 3>,\n                         hydro::Tags::DivergenceCleaningField<DataVector>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                         gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>,\n                         gr::Tags::SpacetimeMetric<DataVector, 3>,\n                         ::gh::Tags::Pi<DataVector, 3>,\n                         ::gh::Tags::Phi<DataVector, 3>>{});\n        }\n      });\n\n  *spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_values);\n  *pi = get<::gh::Tags::Pi<DataVector, 3>>(boundary_values);\n  *phi = get<::gh::Tags::Phi<DataVector, 3>>(boundary_values);\n  *lapse = get<gr::Tags::Lapse<DataVector>>(boundary_values);\n  *shift = get<gr::Tags::Shift<DataVector, 3>>(boundary_values);\n  *rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(boundary_values);\n  *electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataVector>>(boundary_values);\n  *temperature = get<hydro::Tags::Temperature<DataVector>>(boundary_values);\n  *spatial_velocity =\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values);\n  tenex::evaluate<ti::i>(\n      spatial_velocity_one_form,\n      (*spatial_velocity)(ti::J) * (get<gr::Tags::SpatialMetric<DataVector, 3>>(\n                                       boundary_values)(ti::i, ti::j)));\n  *inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_values);\n\n  grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n      get<hydro::Tags::RestMassDensity<DataVector>>(boundary_values),\n      get<hydro::Tags::ElectronFraction<DataVector>>(boundary_values),\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(boundary_values),\n      get<hydro::Tags::Pressure<DataVector>>(boundary_values),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values),\n      get<hydro::Tags::LorentzFactor<DataVector>>(boundary_values),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(boundary_values),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(boundary_values),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_values),\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(boundary_values));\n\n  grmhd::ValenciaDivClean::ComputeFluxes::apply(\n      tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux, tilde_b_flux,\n      tilde_phi_flux, *tilde_d, *tilde_ye, *tilde_tau, *tilde_s, *tilde_b,\n      *tilde_phi, *lapse, *shift,\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(boundary_values),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_values),\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_values),\n      get<hydro::Tags::Pressure<DataVector>>(boundary_values),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values),\n      get<hydro::Tags::LorentzFactor<DataVector>>(boundary_values),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(boundary_values));\n\n  return {};\n}\n\ntemplate <typename System>\nvoid DirichletAnalytic<System>::fd_ghost(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        magnetic_field,\n    const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n    const Direction<3>& direction,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n\n    // fd_gridless_tags\n    const double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n        grid_to_inertial_map,\n    const fd::Reconstructor<System>& reconstructor) const {\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n\n  const auto ghost_logical_coords =\n      evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n          subcell_mesh, ghost_zone_size, direction);\n\n  const auto ghost_inertial_coords = grid_to_inertial_map(\n      logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n  // Compute FD ghost data with the analytic data or solution\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataVector>,\n                          hydro::Tags::ElectronFraction<DataVector>,\n                          hydro::Tags::SpecificInternalEnergy<DataVector>,\n                          hydro::Tags::Temperature<DataVector>,\n                          hydro::Tags::SpatialVelocity<DataVector, 3>,\n                          hydro::Tags::LorentzFactor<DataVector>,\n                          hydro::Tags::MagneticField<DataVector, 3>,\n                          hydro::Tags::DivergenceCleaningField<DataVector>,\n                          gr::Tags::SpacetimeMetric<DataVector, 3>,\n                          ::gh::Tags::Pi<DataVector, 3>,\n                          ::gh::Tags::Phi<DataVector, 3>>,\n      ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list>(\n      analytic_prescription_.get(),\n      [&ghost_inertial_coords, &time](const auto* const initial_data) {\n        using hydro_and_spacetime_tags =\n            tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                       hydro::Tags::ElectronFraction<DataVector>,\n                       hydro::Tags::SpecificInternalEnergy<DataVector>,\n                       hydro::Tags::Temperature<DataVector>,\n                       hydro::Tags::SpatialVelocity<DataVector, 3>,\n                       hydro::Tags::LorentzFactor<DataVector>,\n                       hydro::Tags::MagneticField<DataVector, 3>,\n                       hydro::Tags::DivergenceCleaningField<DataVector>,\n                       gr::Tags::SpacetimeMetric<DataVector, 3>,\n                       ::gh::Tags::Pi<DataVector, 3>,\n                       ::gh::Tags::Phi<DataVector, 3>>;\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(ghost_inertial_coords, time,\n                                         hydro_and_spacetime_tags{});\n        } else if constexpr (evolution::is_numeric_initial_data_v<\n                                 std::decay_t<decltype(*initial_data)>>) {\n          ERROR(\n              \"Cannot currently use numeric initial data as an analytic \"\n              \"prescription for boundary conditions.\");\n        } else {\n          (void)time;\n          return initial_data->variables(ghost_inertial_coords,\n                                         hydro_and_spacetime_tags{});\n        }\n      });\n  *spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_values);\n  *pi = get<::gh::Tags::Pi<DataVector, 3>>(boundary_values);\n  *phi = get<::gh::Tags::Phi<DataVector, 3>>(boundary_values);\n  *rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(boundary_values);\n  *electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataVector>>(boundary_values);\n  *temperature = get<hydro::Tags::Temperature<DataVector>>(boundary_values);\n\n  for (size_t i = 0; i < 3; ++i) {\n    auto& lorentz_factor =\n        get<hydro::Tags::LorentzFactor<DataVector>>(boundary_values);\n    auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values);\n    (*lorentz_factor_times_spatial_velocity).get(i) =\n        get(lorentz_factor) * spatial_velocity.get(i);\n  }\n\n  *magnetic_field =\n      get<hydro::Tags::MagneticField<DataVector, 3>>(boundary_values);\n  *divergence_cleaning_field =\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(boundary_values);\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class DirichletAnalytic<GhValenciaDivClean::System<NEUTRINO(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, GHMHD_NEUTRINOS)\n\n#undef INSTANTIATION\n#undef NEUTRINO\n\n}  // namespace grmhd::GhValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\n// template on System instead\ntemplate <typename System>\nclass DirichletAnalytic final : public BoundaryCondition {\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n  using options = tmpl::list<AnalyticPrescription>;\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions using either analytic solution or \"\n      \"analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>,\n                 ::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n      gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n\n      gsl::not_null<Scalar<DataVector>*> gamma1,\n      gsl::not_null<Scalar<DataVector>*> gamma2,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          spatial_velocity_one_form,\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<\n          tnsr::I<DataVector, 3, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n      const Scalar<DataVector>& interior_gamma1,\n      const Scalar<DataVector>& interior_gamma2,\n      [[maybe_unused]] double time) const;\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>>;\n  using fd_interior_primitive_variables_tags = tmpl::list<>;\n  using fd_gridless_tags =\n      tmpl::list<::Tags::Time, ::domain::Tags::FunctionsOfTime,\n                 domain::Tags::ElementMap<3, Frame::Grid>,\n                 domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                             Frame::Inertial>,\n                 fd::Tags::Reconstructor<System>>;\n  void fd_ghost(\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          lorentz_factor_times_spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n      const Direction<3>& direction,\n\n      // fd_interior_temporary_tags\n      const Mesh<3>& subcell_mesh,\n\n      // fd_gridless_tags\n      double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n          grid_to_inertial_map,\n      const fd::Reconstructor<System>& reconstructor) const;\n\n private:\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace grmhd::GhValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletFreeOutflow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletFreeOutflow.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::BoundaryConditions {\ntemplate <typename System>\nDirichletFreeOutflow<System>::DirichletFreeOutflow(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n// LCOV_EXCL_STOP\ntemplate <typename System>\nDirichletFreeOutflow<System>::DirichletFreeOutflow(\n    const DirichletFreeOutflow<System>& rhs)\n    : BoundaryCondition{dynamic_cast<const BoundaryCondition&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\ntemplate <typename System>\nDirichletFreeOutflow<System>& DirichletFreeOutflow<System>::operator=(\n    const DirichletFreeOutflow<System>& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\ntemplate <typename System>\nDirichletFreeOutflow<System>::DirichletFreeOutflow(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\ntemplate <typename System>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletFreeOutflow<System>::get_clone() const {\n  return std::make_unique<DirichletFreeOutflow>(*this);\n}\n\ntemplate <typename System>\nvoid DirichletFreeOutflow<System>::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | analytic_prescription_;\n}\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletFreeOutflow<System>::my_PUP_ID = 0;\n\ntemplate <typename System>\nstd::optional<std::string> DirichletFreeOutflow<System>::dg_ghost(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity_one_form,\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_temperature,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    const Scalar<DataVector>& interior_gamma1,\n    const Scalar<DataVector>& interior_gamma2, const double time) const {\n  *gamma1 = interior_gamma1;\n  *gamma2 = interior_gamma2;\n\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<\n          gr::Tags::SpatialMetric<DataVector, 3>,\n          gr::Tags::InverseSpatialMetric<DataVector, 3>,\n          gr::Tags::SqrtDetSpatialMetric<DataVector>,\n          gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n          gr::Tags::SpacetimeMetric<DataVector, 3>,\n          ::gh::Tags::Pi<DataVector, 3>, ::gh::Tags::Phi<DataVector, 3>>,\n      ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const initial_data) {\n        using spacetime_tags = tmpl::list<\n            gr::Tags::SpatialMetric<DataVector, 3>,\n            gr::Tags::InverseSpatialMetric<DataVector, 3>,\n            gr::Tags::SqrtDetSpatialMetric<DataVector>,\n            gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n            gr::Tags::SpacetimeMetric<DataVector, 3>,\n            ::gh::Tags::Pi<DataVector, 3>, ::gh::Tags::Phi<DataVector, 3>>;\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(coords, time, spacetime_tags{});\n        } else if constexpr (evolution::is_numeric_initial_data_v<\n                                 std::decay_t<decltype(*initial_data)>>) {\n          ERROR(\n              \"Cannot currently use numeric initial data as an analytic \"\n              \"prescription for boundary conditions.\");\n        } else {\n          (void)time;\n          return initial_data->variables(coords, spacetime_tags{});\n        }\n      });\n\n  *spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_values);\n  *pi = get<::gh::Tags::Pi<DataVector, 3>>(boundary_values);\n  *phi = get<::gh::Tags::Phi<DataVector, 3>>(boundary_values);\n  *lapse = get<gr::Tags::Lapse<DataVector>>(boundary_values);\n  *shift = get<gr::Tags::Shift<DataVector, 3>>(boundary_values);\n  *inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_values);\n\n  return grmhd::ValenciaDivClean::BoundaryConditions::HydroFreeOutflow::\n      dg_ghost(tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n\n               tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n               tilde_b_flux, tilde_phi_flux,\n\n               lapse, shift, spatial_velocity_one_form, rest_mass_density,\n               electron_fraction, temperature, spatial_velocity,\n               inv_spatial_metric,\n\n               face_mesh_velocity, normal_covector, normal_vector,\n\n               interior_rest_mass_density, interior_electron_fraction,\n               interior_specific_internal_energy, interior_spatial_velocity,\n               interior_magnetic_field, interior_lorentz_factor,\n               interior_pressure, interior_temperature,\n\n               *shift, *lapse, *inv_spatial_metric);\n}\n\ntemplate <typename System>\nvoid DirichletFreeOutflow<System>::fd_ghost(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        magnetic_field,\n    const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n    const Direction<3>& direction,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n\n    // interior prim vars tags\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_temperature,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n\n    // fd_gridless_tags\n    const double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n        grid_to_inertial_map,\n    const fd::Reconstructor<System>& reconstructor) const {\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n\n  const auto ghost_logical_coords =\n      evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n          subcell_mesh, ghost_zone_size, direction);\n\n  const auto ghost_inertial_coords = grid_to_inertial_map(\n      logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n  // Compute FD ghost data with the analytic data or solution\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                          ::gh::Tags::Pi<DataVector, 3>,\n                          ::gh::Tags::Phi<DataVector, 3>>,\n      ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list>(\n      analytic_prescription_.get(),\n      [&ghost_inertial_coords, &time](const auto* const initial_data) {\n        using spacetime_tags =\n            tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                       ::gh::Tags::Pi<DataVector, 3>,\n                       ::gh::Tags::Phi<DataVector, 3>>;\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(ghost_inertial_coords, time,\n                                         spacetime_tags{});\n        } else if constexpr (evolution::is_numeric_initial_data_v<\n                                 std::decay_t<decltype(*initial_data)>>) {\n          ERROR(\n              \"Cannot currently use numeric initial data as an analytic \"\n              \"prescription for boundary conditions.\");\n        } else {\n          (void)time;\n          return initial_data->variables(ghost_inertial_coords,\n                                         spacetime_tags{});\n        }\n      });\n\n  *spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_values);\n  *pi = get<::gh::Tags::Pi<DataVector, 3>>(boundary_values);\n  *phi = get<::gh::Tags::Phi<DataVector, 3>>(boundary_values);\n\n  // Note: Once we support high-order fluxes with GHMHD we will need to\n  // handle this correctly.\n  std::optional<Variables<db::wrap_tags_in<\n      Flux, typename grmhd::ValenciaDivClean::System::flux_variables>>>\n      cell_centered_ghost_fluxes{std::nullopt};\n  // Dummy placeholders to call `fd_ghost_impl` but are not actually returned\n  Scalar<DataVector> pressure{};\n  Scalar<DataVector> specific_internal_energy{};\n  tnsr::I<DataVector, 3> spatial_velocity{};\n  Scalar<DataVector> lorentz_factor{};\n  const tnsr::I<DataVector, 3> interior_shift{};\n  const Scalar<DataVector> interior_lapse{};\n  const tnsr::ii<DataVector, 3> interior_spatial_metric{};\n  tnsr::ii<DataVector, 3> spatial_metric{};\n  tnsr::II<DataVector, 3> inv_spatial_metric{};\n  Scalar<DataVector> sqrt_det_spatial_metric{};\n  Scalar<DataVector> lapse{};\n  tnsr::I<DataVector, 3> shift{};\n\n  grmhd::ValenciaDivClean::BoundaryConditions::HydroFreeOutflow::fd_ghost_impl(\n      rest_mass_density, electron_fraction, temperature,\n      make_not_null(&pressure), make_not_null(&specific_internal_energy),\n      lorentz_factor_times_spatial_velocity, make_not_null(&spatial_velocity),\n      make_not_null(&lorentz_factor), magnetic_field, divergence_cleaning_field,\n\n      make_not_null(&spatial_metric), make_not_null(&inv_spatial_metric),\n      make_not_null(&sqrt_det_spatial_metric), make_not_null(&lapse),\n      make_not_null(&shift),\n\n      direction,\n\n      // fd_interior_temporary_tags\n      subcell_mesh,\n\n      // fd_interior_primitive_variables_tags\n      interior_rest_mass_density, interior_electron_fraction,\n      interior_temperature, interior_pressure,\n      interior_specific_internal_energy, interior_lorentz_factor,\n      interior_spatial_velocity, interior_magnetic_field,\n      // Note: metric vars are empty because they shouldn't be used\n      interior_spatial_metric, interior_lapse, interior_shift,\n\n      // fd_gridless_tags\n      reconstructor.ghost_zone_size(), cell_centered_ghost_fluxes.has_value());\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)         \\\n  template class DirichletFreeOutflow< \\\n      GhValenciaDivClean::System<NEUTRINO(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, GHMHD_NEUTRINOS)\n\n#undef INSTANTIATION\n#undef NEUTRINO\n\n}  // namespace grmhd::GhValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletFreeOutflow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data on the spacetime variables and hydro free outflow on the GRMHD\n * variables.\n */\ntemplate <typename System>\nclass DirichletFreeOutflow final : public BoundaryCondition {\n private:\n  template <typename T>\n  using Flux = ::Tags::Flux<T, tmpl::size_t<3>, Frame::Inertial>;\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n  using options = tmpl::list<AnalyticPrescription>;\n  static constexpr Options::String help{\n      \"DirichletFreeOutflow boundary conditions using either analytic solution \"\n      \"or analytic data for GH variables and hydro free outflow for GRMHD\"};\n\n  DirichletFreeOutflow() = default;\n  DirichletFreeOutflow(DirichletFreeOutflow&&) = default;\n  DirichletFreeOutflow& operator=(DirichletFreeOutflow&&) = default;\n  DirichletFreeOutflow(const DirichletFreeOutflow&);\n  DirichletFreeOutflow& operator=(const DirichletFreeOutflow&);\n  ~DirichletFreeOutflow() override = default;\n\n  explicit DirichletFreeOutflow(CkMigrateMessage* msg);\n\n  explicit DirichletFreeOutflow(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletFreeOutflow);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>,\n                 ::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n      gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n\n      gsl::not_null<Scalar<DataVector>*> gamma1,\n      gsl::not_null<Scalar<DataVector>*> gamma2,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          spatial_velocity_one_form,\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_temperature,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n      const Scalar<DataVector>& interior_gamma1,\n      const Scalar<DataVector>& interior_gamma2, double time) const;\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>>;\n  using fd_interior_primitive_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>>;\n  using fd_gridless_tags =\n      tmpl::list<::Tags::Time, ::domain::Tags::FunctionsOfTime,\n                 domain::Tags::ElementMap<3, Frame::Grid>,\n                 domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                             Frame::Inertial>,\n                 fd::Tags::Reconstructor<System>>;\n  void fd_ghost(\n\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          lorentz_factor_times_spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n      const Direction<3>& direction,\n\n      // fd_interior_temporary_tags\n      const Mesh<3>& subcell_mesh,\n\n      // interior prim vars tags\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_temperature,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n\n      // fd_gridless_tags\n      double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n          grid_to_inertial_map,\n      const fd::Reconstructor<System>& reconstructor) const;\n};\n}  // namespace grmhd::GhValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/ConstraintPreservingFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::BoundaryConditions {\n/// Boundary conditions that work with finite difference.\ntemplate <typename System>\nusing standard_boundary_conditions = tmpl::list<\n    ConstraintPreservingFreeOutflow,\n    domain::BoundaryConditions::Periodic<BoundaryCondition>,\n    grmhd::GhValenciaDivClean::BoundaryConditions::DirichletAnalytic<System>,\n    grmhd::GhValenciaDivClean::BoundaryConditions::DirichletFreeOutflow<\n        System>>;\n}  // namespace grmhd::GhValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  NamespaceDocs.hpp\n  ProductOfCorrections.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::BoundaryCorrections {\nnamespace detail {\ntemplate <typename GhList, typename ValenciaList>\nstruct AllProductCorrections;\n\ntemplate <typename GhList, typename... ValenciaCorrections>\nstruct AllProductCorrections<GhList, tmpl::list<ValenciaCorrections...>> {\n  using type = tmpl::flatten<tmpl::list<\n      tmpl::transform<GhList, tmpl::bind<ProductOfCorrections, tmpl::_1,\n                                         tmpl::pin<ValenciaCorrections>>>...>>;\n};\n}  // namespace detail\n\nusing standard_boundary_corrections = typename detail::AllProductCorrections<\n    typename gh::BoundaryCorrections::standard_boundary_corrections<3_st>,\n    typename grmhd::ValenciaDivClean::BoundaryCorrections::\n        standard_boundary_corrections>::type;\n}  // namespace grmhd::GhValenciaDivClean::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace grmhd::GhValenciaDivClean::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/ProductOfCorrections.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/StdHelpers/RetrieveUniquePtr.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::BoundaryCorrections {\nnamespace detail {\ntemplate <typename DerivedGhCorrection, typename DerivedValenciaCorrection,\n          typename GhPackageFieldTagList, typename ValenciaPackageFieldTagList,\n          typename GhEvolvedTagList, typename ValenciaEvolvedTags,\n          typename GhFluxTagList, typename ValenciaFluxTagList,\n          typename GhTempTagList, typename ValenciaTempTagList,\n          typename DeduplicatedTempTags, typename GhPrimTagList,\n          typename ValenicaPrimTagList, typename GhVolumeTagList,\n          typename ValenciaVolumeTagList>\nstruct ProductOfCorrectionsImpl;\n\ntemplate <typename DerivedGhCorrection, typename DerivedValenciaCorrection,\n          typename... GhPackageFieldTags, typename... ValenciaPackageFieldTags,\n          typename... GhEvolvedTags, typename... ValenciaEvolvedTags,\n          typename... GhFluxTags, typename... ValenciaFluxTags,\n          typename... GhTempTags, typename... ValenciaTempTags,\n          typename... DeduplicatedTempTags, typename... GhPrimTags,\n          typename... ValenciaPrimTags, typename... GhVolumeTags,\n          typename... ValenciaVolumeTags>\nstruct ProductOfCorrectionsImpl<\n    DerivedGhCorrection, DerivedValenciaCorrection,\n    tmpl::list<GhPackageFieldTags...>, tmpl::list<ValenciaPackageFieldTags...>,\n    tmpl::list<GhEvolvedTags...>, tmpl::list<ValenciaEvolvedTags...>,\n    tmpl::list<GhFluxTags...>, tmpl::list<ValenciaFluxTags...>,\n    tmpl::list<GhTempTags...>, tmpl::list<ValenciaTempTags...>,\n    tmpl::list<DeduplicatedTempTags...>, tmpl::list<GhPrimTags...>,\n    tmpl::list<ValenciaPrimTags...>, tmpl::list<GhVolumeTags...>,\n    tmpl::list<ValenciaVolumeTags...>> {\n  static double dg_package_data(\n      const gsl::not_null<\n          typename GhPackageFieldTags::type*>... gh_packaged_fields,\n      const gsl::not_null<\n          typename ValenciaPackageFieldTags::type*>... valencia_packaged_fields,\n\n      const typename GhEvolvedTags::type&... gh_variables,\n      const typename ValenciaEvolvedTags::type&... valencia_variables,\n\n      const typename GhFluxTags::type&... gh_fluxes,\n      const typename ValenciaFluxTags::type&... valencia_fluxes,\n\n      const typename DeduplicatedTempTags::type&... temporaries,\n\n      const typename GhPrimTags::type&... gh_primitives,\n      const typename ValenciaPrimTags::type&... valencia_primitives,\n\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n\n      const typename GhVolumeTags::type&... gh_volume_quantities,\n      decltype(StdHelpers::retrieve(\n          std::declval<typename ValenciaVolumeTags::\n                           type>()))... valencia_volume_quantities,\n\n      const DerivedGhCorrection& gh_correction,\n      const DerivedValenciaCorrection& valencia_correction) {\n    tuples::TaggedTuple<\n        Tags::detail::TemporaryReference<DeduplicatedTempTags>...>\n        shuffle_refs{temporaries...};\n    return std::max(\n        gh_correction.dg_package_data(\n            gh_packaged_fields..., gh_variables..., gh_fluxes...,\n            tuples::get<Tags::detail::TemporaryReference<GhTempTags>>(\n                shuffle_refs)...,\n            gh_primitives..., normal_covector, normal_vector, mesh_velocity,\n            normal_dot_mesh_velocity, gh_volume_quantities...),\n        valencia_correction.dg_package_data(\n            valencia_packaged_fields..., valencia_variables...,\n            valencia_fluxes...,\n            tuples::get<Tags::detail::TemporaryReference<ValenciaTempTags>>(\n                shuffle_refs)...,\n            valencia_primitives..., normal_covector, normal_vector,\n            mesh_velocity, normal_dot_mesh_velocity,\n            StdHelpers::retrieve(valencia_volume_quantities)...));\n  }\n\n  static void dg_boundary_terms(\n      const gsl::not_null<\n          typename GhEvolvedTags::type*>... gh_boundary_corrections,\n      const gsl::not_null<\n          typename ValenciaEvolvedTags::type*>... valencia_boundary_corrections,\n\n      const typename GhPackageFieldTags::type&... gh_internal_packaged_fields,\n      const typename ValenciaPackageFieldTags::\n          type&... valencia_internal_packaged_fields,\n\n      const typename GhPackageFieldTags::type&... gh_external_packaged_fields,\n      const typename ValenciaPackageFieldTags::\n          type&... valencia_external_packaged_fields,\n      const dg::Formulation dg_formulation,\n\n      const DerivedGhCorrection& gh_correction,\n      const DerivedValenciaCorrection& valencia_correction) {\n    gh_correction.dg_boundary_terms(\n        gh_boundary_corrections..., gh_internal_packaged_fields...,\n        gh_external_packaged_fields..., dg_formulation);\n    valencia_correction.dg_boundary_terms(\n        valencia_boundary_corrections..., valencia_internal_packaged_fields...,\n        valencia_external_packaged_fields..., dg_formulation);\n  }\n};\n}  // namespace detail\n\n/*!\n * \\brief Apply a boundary condition to the combined Generalized Harmonic (GH)\n * and Valencia GRMHD system using boundary corrections defined separately for\n * the GH and Valencia systems.\n *\n * \\details The implementation of this boundary correction applies the\n * `DerivedGhCorrection` followed by the `DerivedValenciaCorrection`. It is\n * anticipated that the systems are sufficiently independent that the order of\n * application is inconsequential.\n */\ntemplate <typename DerivedGhCorrection, typename DerivedValenciaCorrection>\nclass ProductOfCorrections final : public evolution::BoundaryCorrection {\n public:\n  using dg_package_field_tags =\n      tmpl::append<typename DerivedGhCorrection::dg_package_field_tags,\n                   typename DerivedValenciaCorrection::dg_package_field_tags>;\n\n  using dg_package_data_temporary_tags = tmpl::remove_duplicates<tmpl::append<\n      typename DerivedGhCorrection::dg_package_data_temporary_tags,\n      typename DerivedValenciaCorrection::dg_package_data_temporary_tags>>;\n\n  using dg_package_data_primitive_tags =\n      typename DerivedValenciaCorrection::dg_package_data_primitive_tags;\n\n  using dg_package_data_volume_tags = tmpl::append<\n      typename DerivedGhCorrection::dg_package_data_volume_tags,\n      typename DerivedValenciaCorrection::dg_package_data_volume_tags>;\n\n  using dg_boundary_terms_volume_tags = tmpl::append<\n      typename DerivedGhCorrection::dg_boundary_terms_volume_tags,\n      typename DerivedValenciaCorrection::dg_boundary_terms_volume_tags>;\n\n  using derived_product_correction_impl = detail::ProductOfCorrectionsImpl<\n      DerivedGhCorrection, DerivedValenciaCorrection,\n      typename DerivedGhCorrection::dg_package_field_tags,\n      typename DerivedValenciaCorrection::dg_package_field_tags,\n      typename gh::System<3_st>::variables_tag::tags_list,\n      typename grmhd::ValenciaDivClean::System::variables_tag::tags_list,\n      db::wrap_tags_in<::Tags::Flux, typename gh::System<3_st>::flux_variables,\n                       tmpl::size_t<3_st>, Frame::Inertial>,\n      db::wrap_tags_in<::Tags::Flux,\n                       typename grmhd::ValenciaDivClean::System::flux_variables,\n                       tmpl::size_t<3_st>, Frame::Inertial>,\n      typename DerivedGhCorrection::dg_package_data_temporary_tags,\n      typename DerivedValenciaCorrection::dg_package_data_temporary_tags,\n      dg_package_data_temporary_tags, tmpl::list<>,\n      typename DerivedValenciaCorrection::dg_package_data_primitive_tags,\n      typename DerivedGhCorrection::dg_package_data_volume_tags,\n      typename DerivedValenciaCorrection::dg_package_data_volume_tags>;\n\n  static std::string name() {\n    return \"Product\" + pretty_type::name<DerivedGhCorrection>() + \"And\" +\n           pretty_type::name<DerivedValenciaCorrection>();\n  }\n\n  struct GhCorrection {\n    using type = DerivedGhCorrection;\n    static std::string name() {\n      return pretty_type::name<DerivedGhCorrection>();\n    }\n    static constexpr Options::String help{\n        \"The Generalized Harmonic part of the product boundary condition\"};\n  };\n  struct ValenciaCorrection {\n    using type = DerivedValenciaCorrection;\n    static std::string name() {\n      return pretty_type::name<DerivedValenciaCorrection>();\n    }\n    static constexpr Options::String help{\n        \"The Valencia part of the product boundary condition\"};\n  };\n\n  using options = tmpl::list<GhCorrection, ValenciaCorrection>;\n\n  static constexpr Options::String help = {\n      \"Direct product of a GH and ValenciaDivClean GRMHD boundary correction. \"\n      \"See the documentation for the two individual boundary corrections for \"\n      \"further details.\"};\n\n  ProductOfCorrections() = default;\n  ProductOfCorrections(DerivedGhCorrection gh_correction,\n                       DerivedValenciaCorrection valencia_correction)\n      : derived_gh_correction_{gh_correction},\n        derived_valencia_correction_{valencia_correction} {}\n  ProductOfCorrections(const ProductOfCorrections&) = default;\n  ProductOfCorrections& operator=(const ProductOfCorrections&) = default;\n  ProductOfCorrections(ProductOfCorrections&&) = default;\n  ProductOfCorrections& operator=(ProductOfCorrections&&) = default;\n  ~ProductOfCorrections() override = default;\n\n  /// \\cond\n  explicit ProductOfCorrections(CkMigrateMessage* msg)\n      : BoundaryCorrection(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ProductOfCorrections);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override {\n    BoundaryCorrection::pup(p);\n    p | derived_gh_correction_;\n    p | derived_valencia_correction_;\n  }\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override {\n    return std::make_unique<ProductOfCorrections>(*this);\n  }\n\n  template <typename... Args>\n  double dg_package_data(Args&&... args) const {\n    return derived_product_correction_impl::dg_package_data(\n        std::forward<Args>(args)..., derived_gh_correction_,\n        derived_valencia_correction_);\n  }\n\n  template <typename... Args>\n  void dg_boundary_terms(Args&&... args) const {\n    derived_product_correction_impl::dg_boundary_terms(\n        std::forward<Args>(args)..., derived_gh_correction_,\n        derived_valencia_correction_);\n  }\n\n  const DerivedGhCorrection& gh_correction() const {\n    return derived_gh_correction_;\n  }\n\n  const DerivedValenciaCorrection& valencia_correction() const {\n    return derived_valencia_correction_;\n  }\n\n private:\n  DerivedGhCorrection derived_gh_correction_;\n  DerivedValenciaCorrection derived_valencia_correction_;\n};\n\n/// \\cond\ntemplate <typename DerivedGhCorrection, typename DerivedValenciaCorrection>\nPUP::able::PUP_ID ProductOfCorrections<DerivedGhCorrection,\n                                       DerivedValenciaCorrection>::my_PUP_ID =\n    0;  // NOLINT\n/// \\endcond\n}  // namespace grmhd::GhValenciaDivClean::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GhValenciaDivClean)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  StressEnergy.cpp\n  TimeDerivativeTerms.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AllSolutions.hpp\n  Characteristics.hpp\n  Constraints.hpp\n  NeutrinoSystems.hpp\n  SetPiAndPhiFromConstraints.hpp\n  System.hpp\n  SystemM1.hpp\n  StressEnergy.hpp\n  Tags.hpp\n  TimeDerivativeTerms.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DiscontinuousGalerkin\n  DomainBoundaryConditions\n  FiniteDifference\n  GeneralRelativity\n  GeneralizedHarmonic\n  GhGrMhdAnalyticData\n  GhGrMhdSolutions\n  GhRelativisticEulerSolutions\n  Hydro\n  LinearOperators\n  MonteCarloSolutions\n  Serialization\n  Utilities\n  ValenciaDivClean\n  INTERFACE\n  Options\n  Parallel\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Instantiations)\nadd_subdirectory(Subcell)\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::Tags {\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\n/*!\n * \\brief Computes the largest magnitude of the characteristic speeds.\n *\n * \\warning Assumes \\f$-1\\le\\gamma_1\\le0\\f$.\n */\ntemplate <typename Frame = Frame::Inertial>\nstruct ComputeLargestCharacteristicSpeed : db::ComputeTag,\n                                           LargestCharacteristicSpeed {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, 3, Frame>,\n                 gr::Tags::SpatialMetric<DataVector, 3, Frame>>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(const gsl::not_null<double*> speed,\n                       const Scalar<DataVector>& lapse,\n                       const tnsr::I<DataVector, 3, Frame>& shift,\n                       const tnsr::ii<DataVector, 3, Frame>& spatial_metric) {\n    const auto shift_magnitude = magnitude(shift, spatial_metric);\n    *speed = max(get(shift_magnitude) + get(lapse));\n  }\n};\n}  // namespace grmhd::GhValenciaDivClean::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Constraints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::Tags {\n/*!\n * \\brief Compute item to get the F-constraint for the generalized harmonic\n * evolution system with an MHD stress-energy source.\n *\n * \\details See `gh::f_constraint()`. Can be retrieved using\n * `gh::Tags::FConstraint`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct FConstraintCompute\n    : gh::Tags::FConstraint<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gh::Tags::GaugeH<DataVector, SpatialDim, Frame>,\n      gh::Tags::SpacetimeDerivGaugeH<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalOneForm<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, SpatialDim, Frame>,\n      gh::Tags::Pi<DataVector, SpatialDim, Frame>,\n      gh::Tags::Phi<DataVector, SpatialDim, Frame>,\n      ::Tags::deriv<gh::Tags::Pi<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      ::Tags::deriv<gh::Tags::Phi<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      ::gh::Tags::ConstraintGamma2,\n      gh::Tags::ThreeIndexConstraint<DataVector, SpatialDim, Frame>,\n      Tags::TraceReversedStressEnergy>;\n\n  using return_type = tnsr::a<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*>,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::ab<DataVector, SpatialDim, Frame>&,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::II<DataVector, SpatialDim, Frame>&,\n      const tnsr::AA<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::ijaa<DataVector, SpatialDim, Frame>&,\n      const Scalar<DataVector>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&)>(\n      &gh::f_constraint<DataVector, SpatialDim, Frame>);\n\n  using base = gh::Tags::FConstraint<DataVector, SpatialDim, Frame>;\n};\n}  // namespace grmhd::GhValenciaDivClean::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/BoundaryConditionGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/*!\n * \\brief Computes finite difference ghost data for external boundary\n * conditions.\n *\n * If the element is at the external boundary, computes FD ghost data with a\n * given boundary condition and stores it into neighbor data with {direction,\n * ElementId::external_boundary_id()} as the mortar_id key.\n *\n * \\note Subcell needs to be enabled for boundary elements. Otherwise this\n * function would be never called.\n *\n */\ntemplate <typename System>\nstruct BoundaryConditionGhostData {\n  template <typename DbTagsList>\n  static void apply(\n      gsl::not_null<db::DataBox<DbTagsList>*> box, const Element<3>& element,\n      const Reconstructor<System>& reconstructor);\n\n private:\n  template <typename FdBoundaryConditionHelper, typename DbTagsList,\n            typename... FdBoundaryConditionArgsTags>\n  // A helper function for calling fd_ghost() of BoundaryCondition subclasses\n  static void apply_subcell_boundary_condition_impl(\n      FdBoundaryConditionHelper& fd_boundary_condition_helper,\n      const gsl::not_null<db::DataBox<DbTagsList>*>& box,\n      tmpl::list<FdBoundaryConditionArgsTags...>) {\n    return fd_boundary_condition_helper(\n        db::get<FdBoundaryConditionArgsTags>(*box)...);\n  }\n};\n\ntemplate <typename System>\ntemplate <typename DbTagsList>\nvoid BoundaryConditionGhostData<System>::apply(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    const Element<3>& element,\n    const Reconstructor<System>& reconstructor) {\n  const auto& external_boundary_condition =\n      db::get<domain::Tags::ExternalBoundaryConditions<3>>(*box).at(\n          element.id().block_id());\n\n  // Check if the element is on the external boundary. If not, the caller is\n  // doing something wrong (e.g. trying to compute FD ghost data with boundary\n  // conditions at an element which is not on the external boundary).\n  ASSERT(not element.external_boundaries().empty(),\n         \"The element (ID : \" << element.id()\n                              << \") is not on external boundaries\");\n\n  const Mesh<3> subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<3>>(*box);\n\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n\n  // Tags and tags list for FD reconstruction\n  using RestMassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using LorentzFactorTimesSpatialVelocity =\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n  using MagneticField = hydro::Tags::MagneticField<DataVector, 3>;\n  using DivergenceCleaningField =\n      hydro::Tags::DivergenceCleaningField<DataVector>;\n  using SpacetimeMetric = gr::Tags::SpacetimeMetric<DataVector, 3>;\n  using Pi = gh::Tags::Pi<DataVector, 3>;\n  using Phi = gh::Tags::Phi<DataVector, 3>;\n\n  using reconstruction_tags = GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n  using NeighborVariables = Variables<reconstruction_tags>;\n  constexpr size_t number_of_tensor_components =\n      NeighborVariables::number_of_independent_components;\n\n  for (const auto& direction : element.external_boundaries()) {\n    const auto& boundary_condition_at_direction =\n        *external_boundary_condition.at(direction);\n\n    const size_t num_face_pts{\n        subcell_mesh.extents().slice_away(direction.dimension()).product()};\n\n    // Allocate a vector to store the computed FD ghost data and assign a\n    // non-owning Variables on it.\n    auto& all_ghost_data = db::get_mutable_reference<\n        evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box);\n    // Put the computed ghost data into neighbor data with {direction,\n    // ElementId::external_boundary_id()} as the mortar_id key\n    const DirectionalId<3> mortar_id{direction,\n                                     ElementId<3>::external_boundary_id()};\n    all_ghost_data[mortar_id] = evolution::dg::subcell::GhostData{1};\n    DataVector& boundary_ghost_data =\n        all_ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    boundary_ghost_data.destructive_resize(number_of_tensor_components *\n                                           ghost_zone_size * num_face_pts);\n    Variables<reconstruction_tags> ghost_data_vars{boundary_ghost_data.data(),\n                                                   boundary_ghost_data.size()};\n\n    // We don't need to care about boundary ghost data when using the periodic\n    // condition, so exclude it from the type list\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>::factory_creation::factory_classes;\n    using derived_boundary_conditions_for_subcell = tmpl::remove_if<\n        tmpl::at<factory_classes, typename System::\n                                      boundary_conditions_base>,\n        tmpl::or_<\n            std::is_base_of<domain::BoundaryConditions::MarkAsPeriodic,\n                            tmpl::_1>,\n            std::is_base_of<domain::BoundaryConditions::MarkAsNone, tmpl::_1>>>;\n\n    // Now apply subcell boundary conditions\n    call_with_dynamic_type<void, derived_boundary_conditions_for_subcell>(\n        &boundary_condition_at_direction,\n        [&box, &direction, &ghost_data_vars](const auto* boundary_condition) {\n          using BoundaryCondition = std::decay_t<decltype(*boundary_condition)>;\n          using bcondition_interior_evolved_vars_tags =\n              typename BoundaryCondition::fd_interior_evolved_variables_tags;\n          using bcondition_interior_temporary_tags =\n              typename BoundaryCondition::fd_interior_temporary_tags;\n          using bcondition_interior_primitive_vars_tags =\n              typename BoundaryCondition::fd_interior_primitive_variables_tags;\n          using bcondition_gridless_tags =\n              typename BoundaryCondition::fd_gridless_tags;\n\n          using bcondition_interior_tags =\n              tmpl::append<bcondition_interior_evolved_vars_tags,\n                           bcondition_interior_temporary_tags,\n                           bcondition_interior_primitive_vars_tags,\n                           bcondition_gridless_tags>;\n\n          if constexpr (BoundaryCondition::bc_type ==\n                            evolution::BoundaryConditions::Type::Ghost or\n                        BoundaryCondition::bc_type ==\n                            evolution::BoundaryConditions::Type::\n                                GhostAndTimeDerivative) {\n            const auto apply_fd_ghost =\n                [&boundary_condition, &direction,\n                 &ghost_data_vars](const auto&... boundary_ghost_data_args) {\n                  (*boundary_condition)\n                      .fd_ghost(\n                          make_not_null(&get<SpacetimeMetric>(ghost_data_vars)),\n                          make_not_null(&get<Pi>(ghost_data_vars)),\n                          make_not_null(&get<Phi>(ghost_data_vars)),\n                          make_not_null(&get<RestMassDensity>(ghost_data_vars)),\n                          make_not_null(\n                              &get<ElectronFraction>(ghost_data_vars)),\n                          make_not_null(&get<Temperature>(ghost_data_vars)),\n                          make_not_null(&get<LorentzFactorTimesSpatialVelocity>(\n                              ghost_data_vars)),\n                          make_not_null(&get<MagneticField>(ghost_data_vars)),\n                          make_not_null(\n                              &get<DivergenceCleaningField>(ghost_data_vars)),\n                          direction, boundary_ghost_data_args...);\n                };\n            apply_subcell_boundary_condition_impl(apply_fd_ghost, box,\n                                                  bcondition_interior_tags{});\n          } else {\n            ERROR(\"Unsupported boundary condition \"\n                  << pretty_type::short_name<BoundaryCondition>()\n                  << \" when using finite-difference\");\n          }\n        });\n    if (dynamic_cast<const BoundaryConditions::DirichletAnalytic<System>*>(\n            &boundary_condition_at_direction) != nullptr and\n        reconstructor.reconstruct_rho_times_temperature()) {\n      // If we reconstruct rho*T we end up having to divide by rho to compute\n      // T. In some cases, like when evolving a TOV star with an analytic\n      // boundary condition, the boundary condition sets rho=0. While\n      // unphysical in general, this is how we have implemented the\n      // solutions. We deal with this by applying our atmosphere treatment to\n      // the reconstructed stated.\n\n      const auto& atmosphere_fixer =\n          db::get<::Tags::VariableFixer<VariableFixing::FixToAtmosphere<3>>>(\n              *box);\n      const auto& equation_of_state =\n          db::get<hydro::Tags::GrmhdEquationOfState>(*box);\n\n      tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{};\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = i; j < 3; ++j) {\n          spatial_metric.get(i, j).set_data_ref(\n              &get<SpacetimeMetric>(ghost_data_vars).get(i + 1, j + 1));\n        }\n      }\n\n      Variables<tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3>,\n                           hydro::Tags::LorentzFactor<DataVector>,\n                           hydro::Tags::SpecificInternalEnergy<DataVector>,\n                           hydro::Tags::Pressure<DataVector>>>\n          temp_hydro_vars{ghost_data_vars.number_of_grid_points()};\n\n      const auto& lorentz_factor_times_spatial_velocity =\n          get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n              ghost_data_vars);\n      auto& lorentz_factor =\n          get<hydro::Tags::LorentzFactor<DataVector>>(temp_hydro_vars);\n      get(lorentz_factor) = 0.0;\n      for (size_t i = 0; i < 3; ++i) {\n        get(lorentz_factor) +=\n            spatial_metric.get(i, i) *\n            square(lorentz_factor_times_spatial_velocity.get(i));\n        for (size_t j = i + 1; j < 3; ++j) {\n          get(lorentz_factor) += 2.0 * spatial_metric.get(i, j) *\n                                 lorentz_factor_times_spatial_velocity.get(i) *\n                                 lorentz_factor_times_spatial_velocity.get(j);\n        }\n      }\n      get(lorentz_factor) = sqrt(1.0 + get(lorentz_factor));\n      auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(temp_hydro_vars) =\n              lorentz_factor_times_spatial_velocity;\n      for (size_t i = 0; i < 3; ++i) {\n        spatial_velocity.get(i) /= get(lorentz_factor);\n      }\n\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(temp_hydro_vars) =\n          equation_of_state\n              .specific_internal_energy_from_density_and_temperature(\n                  get<RestMassDensity>(ghost_data_vars),\n                  get<Temperature>(ghost_data_vars),\n                  get<ElectronFraction>(ghost_data_vars));\n      get<hydro::Tags::Pressure<DataVector>>(temp_hydro_vars) =\n          equation_of_state.pressure_from_density_and_temperature(\n              get<RestMassDensity>(ghost_data_vars),\n              get<Temperature>(ghost_data_vars),\n              get<ElectronFraction>(ghost_data_vars));\n\n      atmosphere_fixer(\n          make_not_null(&get<RestMassDensity>(ghost_data_vars)),\n          make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n              temp_hydro_vars)),\n          make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n              temp_hydro_vars)),\n          make_not_null(\n              &get<hydro::Tags::LorentzFactor<DataVector>>(temp_hydro_vars)),\n          make_not_null(\n              &get<hydro::Tags::Pressure<DataVector>>(temp_hydro_vars)),\n          make_not_null(&get<Temperature>(ghost_data_vars)),\n          get<ElectronFraction>(ghost_data_vars), spatial_metric,\n          equation_of_state);\n    }\n  }  // for (direction : external boundaries)\n}\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Derivatives.cpp\n  FilterOptions.cpp\n  Filters.cpp\n  MonotonisedCentral.cpp\n  PositivityPreservingAdaptiveOrder.cpp\n  Reconstructor.cpp\n  RegisterDerivedWithCharm.cpp\n  Wcns5z.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryConditionGhostData.hpp\n  Derivatives.hpp\n  Factory.hpp\n  FillNeighborSpacetimeVariables.hpp\n  FilterOptions.hpp\n  Filters.hpp\n  FiniteDifference.hpp\n  MonotonisedCentral.hpp\n  PositivityPreservingAdaptiveOrder.hpp\n  ReconstructWork.hpp\n  ReconstructWork.tpp\n  Reconstructor.hpp\n  RegisterDerivedWithCharm.hpp\n  Tag.hpp\n  Wcns5z.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Derivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Derivatives.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FillNeighborSpacetimeVariables.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\ntemplate <typename System>\nvoid spacetime_derivatives(\n    const gsl::not_null<Variables<\n        db::wrap_tags_in<::Tags::deriv, typename System::gradients_tags,\n                         tmpl::size_t<3>, Frame::Inertial>>*>\n        result,\n    const Variables<typename System::variables_tag::tags_list>&\n        volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const size_t& deriv_order, const Mesh<3>& volume_mesh,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        cell_centered_logical_to_inertial_inv_jacobian) {\n  using gradients_tags = typename System::gradients_tags;\n  if (UNLIKELY(result->number_of_grid_points() !=\n               volume_evolved_variables.number_of_grid_points())) {\n    result->initialize(volume_evolved_variables.number_of_grid_points());\n  }\n\n  constexpr size_t number_of_gh_components = Variables<\n      grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>::\n      number_of_independent_components;\n\n  DirectionMap<3, gsl::span<const double>> ghost_cell_spacetime_vars{};\n  using NeighborVariables =\n      Variables<grmhd::GhValenciaDivClean::Tags::\n                    primitive_grmhd_and_spacetime_reconstruction_tags>;\n  using FirstGhTag = tmpl::front<\n      grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>;\n\n  fill_neighbor_spacetime_variables<NeighborVariables, FirstGhTag>(\n      make_not_null(&ghost_cell_spacetime_vars), all_ghost_data,\n      number_of_gh_components);\n\n  const auto volume_gh_vars =\n      gsl::make_span(get<FirstGhTag>(volume_evolved_variables)[0].data(),\n                     number_of_gh_components *\n                         volume_evolved_variables.number_of_grid_points());\n\n  ::fd::partial_derivatives<gradients_tags>(\n      result, volume_gh_vars, ghost_cell_spacetime_vars, volume_mesh,\n      number_of_gh_components, deriv_order,\n      cell_centered_logical_to_inertial_inv_jacobian);\n}\n\n// Instantiate here\n// spacetime_derivatives()\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template void                                                             \\\n  spacetime_derivatives<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>( \\\n      const gsl::not_null<Variables<                                        \\\n          db::wrap_tags_in<::Tags::deriv,                                   \\\n                           typename grmhd::GhValenciaDivClean::System<      \\\n                               NEUTRINO(data)>::gradients_tags,             \\\n                           tmpl::size_t<3>, Frame::Inertial>>*>             \\\n          result,                                                           \\\n      const Variables<typename grmhd::GhValenciaDivClean::System<NEUTRINO(  \\\n          data)>::variables_tag::tags_list>& volume_evolved_variables,      \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          all_ghost_data,                                                   \\\n      const size_t& deriv_order, const Mesh<3>& volume_mesh,                \\\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,           \\\n                            Frame::Inertial>&                               \\\n          cell_centered_logical_to_inertial_inv_jacobian);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (RadiationTransport::NoNeutrinos::System))\n#undef INSTANTIATION\n#undef NEUTRINO\n\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Derivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/*!\n * \\brief Compute partial derivatives of the spacetime variables \\f$g_{ab}\\f$,\n * \\f$\\Phi_{iab}\\f$, and \\f$\\Pi_{ab}\\f$.\n *\n * The derivatives are computed using FD of order deriv_order\n */\ntemplate <typename System>\nvoid spacetime_derivatives(\n    gsl::not_null<\n        Variables<db::wrap_tags_in<::Tags::deriv,\n                                   typename System::gradients_tags,\n                                   tmpl::size_t<3>, Frame::Inertial>>*>\n        result,\n    const Variables<typename System::variables_tag::tags_list>&\n        volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const size_t& deriv_order, const Mesh<3>& volume_mesh,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        cell_centered_logical_to_inertial_inv_jacobian);\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Wcns5z.hpp\"\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FillNeighborSpacetimeVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/*!\n * \\brief Helper function that takes spacetime variable data from a\n * `DirectionalIdMap` containing all neighbor data and copies them to\n * `ghost_cell_spacetime_vars`, a separate `DirectionMap`.\n *\n * \\tparam NeighborVariables a `Variables` type containing all tags stored in\n * the neighbor data\n * \\tparam FirstGhTag the first spacetime variables tag in\n * `NeighborVariables`. Note: it is assumed that the spacetime variables are\n * consecutively stored in the `Variables`\n * \\param ghost_cell_spacetime_vars a `DirectionMap` to be filled with spans\n * storing the spacetime data\n * \\param all_ghost_data a `DirectionalIdMap` containing all neighbor data\n * \\param number_of_gh_components the number of independent components for all\n * spacetime variables in `NeighborVariables`\n */\ntemplate <typename NeighborVariables, typename FirstGhTag>\nvoid fill_neighbor_spacetime_variables(\n    const gsl::not_null<DirectionMap<3, gsl::span<const double>>*>\n        ghost_cell_spacetime_vars,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const size_t number_of_gh_components) {\n  for (const auto& [directional_element_id, ghost_data] : all_ghost_data) {\n    const DataVector& neighbor_data =\n        ghost_data.neighbor_ghost_data_for_reconstruction();\n    const size_t neighbor_number_of_points =\n        neighbor_data.size() /\n        NeighborVariables::number_of_independent_components;\n    ASSERT(\n        neighbor_data.size() %\n                NeighborVariables::number_of_independent_components ==\n            0,\n        \"Amount of reconstruction data sent (\"\n            << neighbor_data.size() << \") from \" << directional_element_id\n            << \" is not a multiple of the number of reconstruction variables \"\n            << NeighborVariables::number_of_independent_components);\n    // Use a Variables view to get offset into spacetime variables\n    // without having to do pointer math.\n    const NeighborVariables\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        view{const_cast<double*>(neighbor_data.data()),\n             neighbor_number_of_points *\n                 NeighborVariables::number_of_independent_components};\n    // Note: assumes that the spacetime tags are consecutive in\n    // `NeighborVariables`\n    ghost_cell_spacetime_vars->insert(std::pair{\n        directional_element_id.direction(),\n        gsl::make_span(get<FirstGhTag>(view)[0].data(),\n                       number_of_gh_components * neighbor_number_of_points)});\n  }\n}\n\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FilterOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FilterOptions.hpp\"\n\n#include <pup.h>\n\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\nFilterOptions::FilterOptions(\n    const std::optional<double> in_spacetime_dissipation,\n    const Options::Context& context)\n    : spacetime_dissipation(in_spacetime_dissipation) {\n  if (spacetime_dissipation.has_value() and\n      (spacetime_dissipation.value() <= 0.0 or\n       spacetime_dissipation.value() >= 1.0)) {\n    PARSE_ERROR(context,\n                \"Spacetime dissipation must be between 0 and 1, but got \"\n                    << spacetime_dissipation.value());\n  }\n}\n\nvoid FilterOptions::pup(PUP::er& p) { p | spacetime_dissipation; }\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FilterOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/*!\n * \\brief Filtering/dissipation options\n */\nstruct FilterOptions {\n  /// Kreiss-Oliger dissipation applied to metric variables\n  ///\n  /// Must be positive and less than 1.\n  struct SpacetimeDissipation {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help{\n        \"The amount of Kreiss-Oliger filter dissipation to apply. Must be \"\n        \"positive and less than 1. If 'None' then no dissipation is applied.\"};\n  };\n  using options = tmpl::list<SpacetimeDissipation>;\n\n  static constexpr Options::String help{\n      \"Parameters for controlling filter on the FD grid.\"};\n\n  FilterOptions() = default;\n  explicit FilterOptions(std::optional<double> in_spacetime_dissipation,\n                         const Options::Context& context = {});\n  void pup(PUP::er& p);\n\n  std::optional<double> spacetime_dissipation = std::nullopt;\n};\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Filters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Filters.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FillNeighborSpacetimeVariables.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Filter.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\ntemplate <typename VariableTags>\nvoid spacetime_kreiss_oliger_filter(\n    const gsl::not_null<Variables<VariableTags>*> result,\n    const Variables<VariableTags>& volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const Mesh<3>& volume_mesh, const size_t order, const double epsilon) {\n  if (UNLIKELY(result->number_of_grid_points() !=\n               volume_evolved_variables.number_of_grid_points())) {\n    result->initialize(volume_evolved_variables.number_of_grid_points());\n  }\n\n  constexpr size_t number_of_gh_components = Variables<\n      grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>::\n      number_of_independent_components;\n\n  DirectionMap<3, gsl::span<const double>> ghost_cell_spacetime_vars{};\n  using NeighborVariables =\n      Variables<grmhd::GhValenciaDivClean::Tags::\n                    primitive_grmhd_and_spacetime_reconstruction_tags>;\n  using FirstGhTag = tmpl::front<\n      grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>;\n\n  fill_neighbor_spacetime_variables<NeighborVariables, FirstGhTag>(\n      make_not_null(&ghost_cell_spacetime_vars), all_ghost_data,\n      number_of_gh_components);\n\n  const auto volume_gh_vars =\n      gsl::make_span(get<FirstGhTag>(volume_evolved_variables)[0].data(),\n                     number_of_gh_components *\n                         volume_evolved_variables.number_of_grid_points());\n\n  auto filtered_gh_vars =\n      gsl::make_span(get<FirstGhTag>(*result)[0].data(),\n                     number_of_gh_components *\n                         volume_evolved_variables.number_of_grid_points());\n  ::fd::kreiss_oliger_filter(make_not_null(&filtered_gh_vars), volume_gh_vars,\n                             ghost_cell_spacetime_vars, volume_mesh,\n                             number_of_gh_components, order, epsilon);\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                             \\\n  template void spacetime_kreiss_oliger_filter(                            \\\n      const gsl::not_null<                                                 \\\n          Variables<typename grmhd::GhValenciaDivClean::System<NEUTRINO(   \\\n              data)>::variables_tag::tags_list>*>                          \\\n          result,                                                          \\\n      const Variables<typename grmhd::GhValenciaDivClean::System<NEUTRINO( \\\n          data)>::variables_tag::tags_list>& volume_evolved_variables,     \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&        \\\n          all_ghost_data,                                                  \\\n      const Mesh<3>& volume_mesh, const size_t order, const double epsilon);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (RadiationTransport::NoNeutrinos::System))\n\n#undef INSTANTIATION\n#undef NEUTRINO\n\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Filters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/*!\n * \\brief Apply a Kreiss-Oliger filter to \\f$g_{ab}\\f$, \\f$\\Phi_{iab}\\f$, and\n * \\f$\\Pi_{ab}\\f$.\n */\ntemplate <typename VariableTags>\nvoid spacetime_kreiss_oliger_filter(\n    gsl::not_null<Variables<VariableTags>*> result,\n    const Variables<VariableTags>& volume_evolved_variables,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const Mesh<3>& volume_mesh, size_t order, double epsilon);\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FiniteDifference.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace grmhd::GhValenciaDivClean {\n/// Finite difference functionality for the coupled\n/// Generalized Harmonic and ValenciaDivClean equations\nnamespace fd {}\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Unlimited.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Wcns5z.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\n\ntemplate <typename System>\nMonotonisedCentralPrim<System>::MonotonisedCentralPrim(\n    const ::VariableFixing::FixReconstructedStateToAtmosphere\n        fix_reconstructed_state_to_atmosphere,\n    const bool reconstruct_rho_times_temperature)\n    : fix_reconstructed_state_to_atmosphere_(\n          fix_reconstructed_state_to_atmosphere),\n      reconstruct_rho_times_temperature_(reconstruct_rho_times_temperature) {}\n\ntemplate <typename System>\nMonotonisedCentralPrim<System>::MonotonisedCentralPrim(\n    CkMigrateMessage* const msg)\n    : Reconstructor<System>(msg) {}\n\ntemplate <typename System>\nstd::unique_ptr<Reconstructor<System>>\nMonotonisedCentralPrim<System>::get_clone() const {\n  return std::make_unique<MonotonisedCentralPrim>(*this);\n}\n\ntemplate <typename System>\nvoid MonotonisedCentralPrim<System>::pup(PUP::er& p) {\n  Reconstructor<System>::pup(p);\n  p | fix_reconstructed_state_to_atmosphere_;\n  p | reconstruct_rho_times_temperature_;\n}\n\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID MonotonisedCentralPrim<System>::my_PUP_ID = 0;\n\ntemplate <typename System>\ntemplate <size_t ThermodynamicDim, typename TagsList>\nvoid MonotonisedCentralPrim<System>::reconstruct(\n    const gsl::not_null<std::array<Variables<TagsList>, dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, dim>*>\n        vars_on_upper_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n    const Variables<typename System::variables_tag::type::tags_list>&\n        volume_spacetime_and_cons_vars,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere) const {\n  using ::VariableFixing::FixReconstructedStateToAtmosphere;\n  using prim_tags_for_reconstruction =\n      grmhd::GhValenciaDivClean::Tags::primitive_grmhd_reconstruction_tags;\n  using all_tags_for_reconstruction = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n\n  DirectionalIdMap<dim, Variables<all_tags_for_reconstruction>>\n      neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_prims_work<tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                         prim_tags_for_reconstruction>(\n      vars_on_lower_face, vars_on_upper_face,\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_vars, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::monotonised_central(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_vars, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::unlimited<2>(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      [](const auto vars_on_face_ptr) {\n        const auto& spacetime_metric =\n            get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*vars_on_face_ptr);\n        auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face_ptr);\n        gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n        auto& inverse_spatial_metric =\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                *vars_on_face_ptr);\n        auto& sqrt_det_spatial_metric =\n            get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*vars_on_face_ptr);\n\n        determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                                make_not_null(&inverse_spatial_metric),\n                                spatial_metric);\n        get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n        auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*vars_on_face_ptr);\n        gr::shift(make_not_null(&shift), spacetime_metric,\n                  inverse_spatial_metric);\n        gr::lapse(\n            make_not_null(&get<gr::Tags::Lapse<DataVector>>(*vars_on_face_ptr)),\n            shift, spacetime_metric);\n      },\n      volume_prims, volume_spacetime_and_cons_vars, eos, element,\n      neighbor_variables_data, subcell_mesh, ghost_zone_size(), true,\n      reconstruct_rho_times_temperature(),\n      (fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::Always or\n               fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::OnFdOnly\n           ? &fix_to_atmosphere\n           : nullptr));\n}\n\ntemplate <typename System>\ntemplate <size_t ThermodynamicDim, typename TagsList>\nvoid MonotonisedCentralPrim<System>::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n    const Variables<\n        grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>&\n        subcell_volume_spacetime_metric,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,\n    const Direction<dim> direction_to_reconstruct) const {\n  using ::VariableFixing::FixReconstructedStateToAtmosphere;\n  using prim_tags_for_reconstruction =\n      grmhd::GhValenciaDivClean::Tags::primitive_grmhd_reconstruction_tags;\n  using all_tags_for_reconstruction = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n  reconstruct_fd_neighbor_work<Tags::spacetime_reconstruction_tags,\n                               prim_tags_for_reconstruction,\n                               all_tags_for_reconstruction>(\n      vars_on_face,\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::UnlimitedReconstructor<2>>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::UnlimitedReconstructor<2>>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto vars_on_face_ptr) {\n        const auto& spacetime_metric =\n            get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*vars_on_face_ptr);\n        auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face_ptr);\n        gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n        auto& inverse_spatial_metric =\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                *vars_on_face_ptr);\n        auto& sqrt_det_spatial_metric =\n            get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*vars_on_face_ptr);\n\n        determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                                make_not_null(&inverse_spatial_metric),\n                                spatial_metric);\n        get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n        auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*vars_on_face_ptr);\n        gr::shift(make_not_null(&shift), spacetime_metric,\n                  inverse_spatial_metric);\n        gr::lapse(\n            make_not_null(&get<gr::Tags::Lapse<DataVector>>(*vars_on_face_ptr)),\n            shift, spacetime_metric);\n      },\n      subcell_volume_prims, subcell_volume_spacetime_metric, eos, element,\n      ghost_data, subcell_mesh, direction_to_reconstruct, ghost_zone_size(),\n      true, reconstruct_rho_times_temperature(),\n      (fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::Always or\n               fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::AtDgFdInterfaceOnly\n           ? &fix_to_atmosphere\n           : nullptr));\n}\n\ntemplate <typename System>\nbool MonotonisedCentralPrim<System>::reconstruct_rho_times_temperature() const {\n  return reconstruct_rho_times_temperature_;\n}\n\ntemplate <typename System>\nbool operator==(const MonotonisedCentralPrim<System>& lhs,\n                const MonotonisedCentralPrim<System>& rhs) {\n  return lhs.fix_reconstructed_state_to_atmosphere_ ==\n             rhs.fix_reconstructed_state_to_atmosphere_ and\n         lhs.reconstruct_rho_times_temperature() ==\n             rhs.reconstruct_rho_times_temperature();\n}\n\ntemplate <typename System>\nbool operator!=(const MonotonisedCentralPrim<System>& lhs,\n                const MonotonisedCentralPrim<System>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                               \\\n  template class MonotonisedCentralPrim<                     \\\n      GhValenciaDivClean::System<NEUTRINO(data)>>;           \\\n  template bool operator==(                                  \\\n      const MonotonisedCentralPrim<                          \\\n          GhValenciaDivClean::System<NEUTRINO(data)>>& lhs,  \\\n      const MonotonisedCentralPrim<                          \\\n          GhValenciaDivClean::System<NEUTRINO(data)>>& rhs); \\\n  template bool operator!=(                                  \\\n      const MonotonisedCentralPrim<                          \\\n          GhValenciaDivClean::System<NEUTRINO(data)>>& lhs,  \\\n      const MonotonisedCentralPrim<                          \\\n          GhValenciaDivClean::System<NEUTRINO(data)>>& rhs);\nGENERATE_INSTANTIATIONS(INSTANTIATION, GHMHD_NEUTRINOS)\n#undef INSTANTIATION\n#undef NEUTRINO\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void                                                                \\\n  MonotonisedCentralPrim<GhValenciaDivClean::System<NEUTRINO(data)>>::         \\\n      reconstruct(                                                             \\\n          gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>  \\\n              vars_on_lower_face,                                              \\\n          gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>  \\\n              vars_on_upper_face,                                              \\\n          const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,        \\\n          const Variables<typename GhValenciaDivClean::System<NEUTRINO(        \\\n              data)>::variables_tag::type::tags_list>&                         \\\n              volume_spacetime_and_cons_vars,                                  \\\n          const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>&     \\\n              eos,                                                             \\\n          const Element<3>& element,                                           \\\n          const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&        \\\n              ghost_data,                                                      \\\n          const Mesh<3>& subcell_mesh,                                         \\\n          const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere)       \\\n          const;                                                               \\\n  template void                                                                \\\n  MonotonisedCentralPrim<GhValenciaDivClean::System<NEUTRINO(data)>>::         \\\n      reconstruct_fd_neighbor(                                                 \\\n          gsl::not_null<Variables<tags_list_for_reconstruct_fd_neighbor>*>     \\\n              vars_on_face,                                                    \\\n          const Variables<hydro::grmhd_tags<DataVector>>&                      \\\n              subcell_volume_prims,                                            \\\n          const Variables<                                                     \\\n              grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>& \\\n              subcell_volume_spacetime_metric,                                 \\\n          const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>&     \\\n              eos,                                                             \\\n          const Element<3>& element,                                           \\\n          const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&        \\\n              ghost_data,                                                      \\\n          const Mesh<3>& subcell_mesh,                                         \\\n          const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,       \\\n          const Direction<3> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), GHMHD_NEUTRINOS)\n\n#undef INSTANTIATION\n#undef TAGS_LIST\n#undef THERMO_DIM\n#undef NEUTRINO\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/*!\n * \\brief Monotonised central reconstruction on the GRMHD primitive variables\n * (see\n * ::fd::reconstruction::monotonised_central() for details) and unlimited 3rd\n * order (degree 2 polynomial) reconstruction on the metric variables.\n *\n * Only the spacetime metric is reconstructed when we and the neighboring\n * element in the direction are doing FD. If we are doing DG and a neighboring\n * element is doing FD, then the spacetime metric, \\f$\\Phi_{iab}\\f$, and\n * \\f$\\Pi_{ab}\\f$ are all reconstructed since the Riemann solver on the DG\n * element also needs to solve for the metric variables.\n */\ntemplate <typename System>\nclass MonotonisedCentralPrim : public Reconstructor<System> {\n public:\n  static constexpr size_t dim = 3;\n\n  struct AtmosphereTreatment {\n    using type = ::VariableFixing::FixReconstructedStateToAtmosphere;\n    static constexpr Options::String help = {\n        \"What reconstructed states to fix to their atmosphere values.\"};\n  };\n  struct ReconstructRhoTimesTemperature {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If 'true' then we reconstruct the rho*T, if 'false' we reconstruct \"\n        \"T.\"};\n  };\n\n  using options =\n      tmpl::list<AtmosphereTreatment, ReconstructRhoTimesTemperature>;\n  static constexpr Options::String help{\n      \"Monotonised central reconstruction scheme using primitive variables and \"\n      \"the metric variables.\"};\n\n  MonotonisedCentralPrim(::VariableFixing::FixReconstructedStateToAtmosphere\n                             fix_reconstructed_state_to_atmosphere,\n                         bool reconstruct_rho_times_temperature);\n\n  MonotonisedCentralPrim() = default;\n  MonotonisedCentralPrim(MonotonisedCentralPrim&&) = default;\n  MonotonisedCentralPrim& operator=(MonotonisedCentralPrim&&) = default;\n  MonotonisedCentralPrim(const MonotonisedCentralPrim&) = default;\n  MonotonisedCentralPrim& operator=(const MonotonisedCentralPrim&) = default;\n  ~MonotonisedCentralPrim() override = default;\n\n  explicit MonotonisedCentralPrim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor<System>,\n                                     MonotonisedCentralPrim);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor<System>> override;\n\n  static constexpr bool use_adaptive_order = false;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 2; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                 typename System::variables_tag,\n                 hydro::Tags::GrmhdEquationOfState, domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>,\n                 ::Tags::VariableFixer<VariableFixing::FixToAtmosphere<dim>>>;\n\n  template <size_t ThermodynamicDim, typename TagsList>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<TagsList>, dim>*> vars_on_lower_face,\n      gsl::not_null<std::array<Variables<TagsList>, dim>*> vars_on_upper_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n      const Variables<typename System::variables_tag::type::tags_list>&\n          volume_spacetime_and_cons_vars,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere) const;\n\n  /// Called by an element doing DG when the neighbor is doing subcell.\n  template <size_t ThermodynamicDim, typename TagsList>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<TagsList>*> vars_on_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n      const Variables<\n          grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>&\n          subcell_volume_spacetime_metric,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,\n      Direction<dim> direction_to_reconstruct) const;\n\n  bool reconstruct_rho_times_temperature() const override;\n\n private:\n  template <typename LocalSystem>\n  friend bool operator==(const MonotonisedCentralPrim<LocalSystem>& lhs,\n                         const MonotonisedCentralPrim<LocalSystem>& rhs);\n  template <typename LocalSystem>\n  friend bool operator!=(const MonotonisedCentralPrim<LocalSystem>& lhs,\n                         const MonotonisedCentralPrim<LocalSystem>& rhs);\n\n  ::VariableFixing::FixReconstructedStateToAtmosphere\n      fix_reconstructed_state_to_atmosphere_{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never};\n  bool reconstruct_rho_times_temperature_{false};\n};\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n\n#include <pup.h>\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonicityPreserving5.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Unlimited.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\ntemplate <typename System>\nPositivityPreservingAdaptiveOrderPrim<\n    System>::PositivityPreservingAdaptiveOrderPrim(CkMigrateMessage* const msg)\n    : Reconstructor<System>(msg) {}\n\ntemplate <typename System>\nPositivityPreservingAdaptiveOrderPrim<System>::\n    PositivityPreservingAdaptiveOrderPrim(\n        const double alpha_5, const std::optional<double> alpha_7,\n        const std::optional<double> alpha_9,\n        const ::fd::reconstruction::FallbackReconstructorType\n            low_order_reconstructor,\n        const ::VariableFixing::FixReconstructedStateToAtmosphere\n            fix_reconstructed_state_to_atmosphere,\n        const bool reconstruct_rho_times_temperature,\n        const Options::Context& context)\n    : four_to_the_alpha_5_(pow(4.0, alpha_5)),\n      low_order_reconstructor_(low_order_reconstructor),\n      fix_reconstructed_state_to_atmosphere_(\n          fix_reconstructed_state_to_atmosphere),\n      reconstruct_rho_times_temperature_(reconstruct_rho_times_temperature) {\n  if (low_order_reconstructor_ ==\n      ::fd::reconstruction::FallbackReconstructorType::None) {\n    PARSE_ERROR(context, \"None is not an allowed low-order reconstructor.\");\n  }\n  if (alpha_7.has_value()) {\n    PARSE_ERROR(context, \"Alpha7 hasn't been tested.\");\n    six_to_the_alpha_7_ = pow(6.0, alpha_7.value());\n  }\n  if (alpha_9.has_value()) {\n    PARSE_ERROR(context, \"Alpha9 hasn't been tested.\");\n    eight_to_the_alpha_9_ = pow(8.0, alpha_9.value());\n  }\n  set_function_pointers();\n}\n\ntemplate <typename System>\nvoid PositivityPreservingAdaptiveOrderPrim<System>::set_function_pointers() {\n  std::tie(reconstruct_, reconstruct_lower_neighbor_,\n           reconstruct_upper_neighbor_) = ::fd::reconstruction::\n      positivity_preserving_adaptive_order_function_pointers<3, false>(\n          false, eight_to_the_alpha_9_.has_value(),\n          six_to_the_alpha_7_.has_value(), low_order_reconstructor_);\n  std::tie(pp_reconstruct_, pp_reconstruct_lower_neighbor_,\n           pp_reconstruct_upper_neighbor_) = ::fd::reconstruction::\n      positivity_preserving_adaptive_order_function_pointers<3, true>(\n          true, eight_to_the_alpha_9_.has_value(),\n          six_to_the_alpha_7_.has_value(), low_order_reconstructor_);\n}\n\ntemplate <typename System>\nstd::unique_ptr<Reconstructor<System>>\nPositivityPreservingAdaptiveOrderPrim<System>::get_clone() const {\n  return std::make_unique<PositivityPreservingAdaptiveOrderPrim>(*this);\n}\n\ntemplate <typename System>\nvoid PositivityPreservingAdaptiveOrderPrim<System>::pup(PUP::er& p) {\n  Reconstructor<System>::pup(p);\n  p | four_to_the_alpha_5_;\n  p | six_to_the_alpha_7_;\n  p | eight_to_the_alpha_9_;\n  p | low_order_reconstructor_;\n  p | fix_reconstructed_state_to_atmosphere_;\n  p | reconstruct_rho_times_temperature_;\n  if (p.isUnpacking()) {\n    set_function_pointers();\n  }\n}\n\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID PositivityPreservingAdaptiveOrderPrim<System>::my_PUP_ID = 0;\n\ntemplate <typename System>\ntemplate <size_t ThermodynamicDim, typename TagsList>\nvoid PositivityPreservingAdaptiveOrderPrim<System>::reconstruct(\n    const gsl::not_null<std::array<Variables<TagsList>, dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, dim>*>\n        vars_on_upper_face,\n    const gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, 3>>*>\n        reconstruction_order,\n    const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n    const Variables<typename System::variables_tag::type::tags_list>&\n        volume_spacetime_and_cons_vars,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere) const {\n  using ::VariableFixing::FixReconstructedStateToAtmosphere;\n  using all_tags_for_reconstruction = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n\n  DirectionalIdMap<dim, Variables<all_tags_for_reconstruction>>\n      neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_prims_work<tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                         positivity_preserving_tags>(\n      vars_on_lower_face, vars_on_upper_face,\n      [this, &reconstruction_order](\n          auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n          const auto& volume_vars, const auto& ghost_cell_vars,\n          const auto& subcell_extents, const size_t number_of_variables) {\n        pp_reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr,\n                        reconstruction_order, volume_vars, ghost_cell_vars,\n                        subcell_extents, number_of_variables,\n                        four_to_the_alpha_5_,\n                        six_to_the_alpha_7_.value_or(\n                            std::numeric_limits<double>::signaling_NaN()),\n                        eight_to_the_alpha_9_.value_or(\n                            std::numeric_limits<double>::signaling_NaN()));\n      },\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_vars, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::unlimited<4>(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      [](const auto vars_on_face_ptr) {\n        const auto& spacetime_metric =\n            get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*vars_on_face_ptr);\n        auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face_ptr);\n        gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n        auto& inverse_spatial_metric =\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                *vars_on_face_ptr);\n        auto& sqrt_det_spatial_metric =\n            get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*vars_on_face_ptr);\n\n        determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                                make_not_null(&inverse_spatial_metric),\n                                spatial_metric);\n        get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n        auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*vars_on_face_ptr);\n        gr::shift(make_not_null(&shift), spacetime_metric,\n                  inverse_spatial_metric);\n        gr::lapse(\n            make_not_null(&get<gr::Tags::Lapse<DataVector>>(*vars_on_face_ptr)),\n            shift, spacetime_metric);\n      },\n      volume_prims, volume_spacetime_and_cons_vars, eos, element,\n      neighbor_variables_data, subcell_mesh, ghost_zone_size(), false,\n      reconstruct_rho_times_temperature(), nullptr);\n\n  reconstruct_prims_work<tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                         non_positive_tags>(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_vars, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n                     ghost_cell_vars, subcell_extents, number_of_variables,\n                     four_to_the_alpha_5_,\n                     six_to_the_alpha_7_.value_or(\n                         std::numeric_limits<double>::signaling_NaN()),\n                     eight_to_the_alpha_9_.value_or(\n                         std::numeric_limits<double>::signaling_NaN()));\n      },\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_vars, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::unlimited<4>(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      [](const auto vars_on_face_ptr) {\n        const auto& spacetime_metric =\n            get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*vars_on_face_ptr);\n        auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face_ptr);\n        gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n        auto& inverse_spatial_metric =\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                *vars_on_face_ptr);\n        auto& sqrt_det_spatial_metric =\n            get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*vars_on_face_ptr);\n\n        determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                                make_not_null(&inverse_spatial_metric),\n                                spatial_metric);\n        get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n        auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*vars_on_face_ptr);\n        gr::shift(make_not_null(&shift), spacetime_metric,\n                  inverse_spatial_metric);\n        gr::lapse(\n            make_not_null(&get<gr::Tags::Lapse<DataVector>>(*vars_on_face_ptr)),\n            shift, spacetime_metric);\n      },\n      volume_prims, volume_spacetime_and_cons_vars, eos, element,\n      neighbor_variables_data, subcell_mesh, ghost_zone_size(), true,\n      reconstruct_rho_times_temperature(),\n      (fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::Always or\n               fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::OnFdOnly\n           ? &fix_to_atmosphere\n           : nullptr));\n}\n\ntemplate <typename System>\ntemplate <size_t ThermodynamicDim, typename TagsList>\nvoid PositivityPreservingAdaptiveOrderPrim<System>::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n    const Variables<\n        grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>&\n        subcell_volume_spacetime_metric,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,\n    const Direction<dim> direction_to_reconstruct) const {\n  using ::VariableFixing::FixReconstructedStateToAtmosphere;\n  using all_tags_for_reconstruction = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n  reconstruct_fd_neighbor_work<tmpl::list<>, positivity_preserving_tags,\n                               all_tags_for_reconstruction>(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<dim>& subcell_extents,\n             const Index<dim>& ghost_data_extents,\n             const Direction<dim>& local_direction_to_reconstruct) {\n        pp_reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      [](auto&&...) {\n        ERROR(\"Spacetime reconstruction should not happen here\");\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<dim>& subcell_extents,\n             const Index<dim>& ghost_data_extents,\n             const Direction<dim>& local_direction_to_reconstruct) {\n        pp_reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      [](auto&&...) {\n        ERROR(\"Spacetime reconstruction should not happen here\");\n      },\n      [](auto&&...) {\n        ERROR(\"Spacetime reconstruction should not happen here\");\n      },\n      subcell_volume_prims, subcell_volume_spacetime_metric, eos, element,\n      ghost_data, subcell_mesh, direction_to_reconstruct, ghost_zone_size(),\n      false, reconstruct_rho_times_temperature(), nullptr);\n  reconstruct_fd_neighbor_work<Tags::spacetime_reconstruction_tags,\n                               non_positive_tags, all_tags_for_reconstruction>(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<dim>& subcell_extents,\n             const Index<dim>& ghost_data_extents,\n             const Direction<dim>& local_direction_to_reconstruct) {\n        reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::UnlimitedReconstructor<4>>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<dim>& subcell_extents,\n             const Index<dim>& ghost_data_extents,\n             const Direction<dim>& local_direction_to_reconstruct) {\n        reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::UnlimitedReconstructor<4>>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto vars_on_face_ptr) {\n        const auto& spacetime_metric =\n            get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*vars_on_face_ptr);\n        auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face_ptr);\n        gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n        auto& inverse_spatial_metric =\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                *vars_on_face_ptr);\n        auto& sqrt_det_spatial_metric =\n            get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*vars_on_face_ptr);\n\n        determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                                make_not_null(&inverse_spatial_metric),\n                                spatial_metric);\n        get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n        auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*vars_on_face_ptr);\n        gr::shift(make_not_null(&shift), spacetime_metric,\n                  inverse_spatial_metric);\n        gr::lapse(\n            make_not_null(&get<gr::Tags::Lapse<DataVector>>(*vars_on_face_ptr)),\n            shift, spacetime_metric);\n      },\n      subcell_volume_prims, subcell_volume_spacetime_metric, eos, element,\n      ghost_data, subcell_mesh, direction_to_reconstruct, ghost_zone_size(),\n      true, reconstruct_rho_times_temperature(),\n      (fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::Always or\n               fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::AtDgFdInterfaceOnly\n           ? &fix_to_atmosphere\n           : nullptr));\n}\n\ntemplate <typename System>\nbool PositivityPreservingAdaptiveOrderPrim<\n    System>::reconstruct_rho_times_temperature() const {\n  return reconstruct_rho_times_temperature_;\n}\n\ntemplate <typename System>\nbool operator==(const PositivityPreservingAdaptiveOrderPrim<System>& lhs,\n                const PositivityPreservingAdaptiveOrderPrim<System>& rhs) {\n  // Don't check function pointers since they are set from\n  // low_order_reconstructor_\n  return lhs.four_to_the_alpha_5_ == rhs.four_to_the_alpha_5_ and\n         lhs.six_to_the_alpha_7_ == rhs.six_to_the_alpha_7_ and\n         lhs.eight_to_the_alpha_9_ == rhs.eight_to_the_alpha_9_ and\n         lhs.low_order_reconstructor_ == rhs.low_order_reconstructor_ and\n         lhs.fix_reconstructed_state_to_atmosphere_ ==\n             rhs.fix_reconstructed_state_to_atmosphere_ and\n         lhs.reconstruct_rho_times_temperature() ==\n             rhs.reconstruct_rho_times_temperature();\n}\n\ntemplate <typename System>\nbool operator!=(const PositivityPreservingAdaptiveOrderPrim<System>& lhs,\n                const PositivityPreservingAdaptiveOrderPrim<System>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                               \\\n  template class PositivityPreservingAdaptiveOrderPrim<      \\\n      GhValenciaDivClean::System<NEUTRINO(data)>>;           \\\n  template bool operator==(                                  \\\n      const PositivityPreservingAdaptiveOrderPrim<           \\\n          GhValenciaDivClean::System<NEUTRINO(data)>>& lhs,  \\\n      const PositivityPreservingAdaptiveOrderPrim<           \\\n          GhValenciaDivClean::System<NEUTRINO(data)>>& rhs); \\\n  template bool operator!=(                                  \\\n      const PositivityPreservingAdaptiveOrderPrim<           \\\n          GhValenciaDivClean::System<NEUTRINO(data)>>& lhs,  \\\n      const PositivityPreservingAdaptiveOrderPrim<           \\\n          GhValenciaDivClean::System<NEUTRINO(data)>>& rhs);\nGENERATE_INSTANTIATIONS(INSTANTIATION, GHMHD_NEUTRINOS)\n#undef INSTANTIATION\n#undef NEUTRINO\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void PositivityPreservingAdaptiveOrderPrim<                         \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>::                     \\\n      reconstruct(                                                             \\\n          gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>  \\\n              vars_on_lower_face,                                              \\\n          gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>  \\\n              vars_on_upper_face,                                              \\\n          const gsl::not_null<                                                 \\\n              std::optional<std::array<gsl::span<std::uint8_t>, 3>>*>          \\\n              reconstruction_order,                                            \\\n          const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,        \\\n          const Variables<typename grmhd::GhValenciaDivClean::System<NEUTRINO( \\\n              data)>::variables_tag::type::tags_list>&                         \\\n              volume_spacetime_and_cons_vars,                                  \\\n          const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>&     \\\n              eos,                                                             \\\n          const Element<3>& element,                                           \\\n          const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&        \\\n              ghost_data,                                                      \\\n          const Mesh<3>& subcell_mesh,                                         \\\n          const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere)       \\\n          const;                                                               \\\n  template void PositivityPreservingAdaptiveOrderPrim<                         \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>::                     \\\n      reconstruct_fd_neighbor(                                                 \\\n          gsl::not_null<Variables<tags_list_for_reconstruct_fd_neighbor>*>     \\\n              vars_on_face,                                                    \\\n          const Variables<hydro::grmhd_tags<DataVector>>&                      \\\n              subcell_volume_prims,                                            \\\n          const Variables<                                                     \\\n              grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>& \\\n              subcell_volume_spacetime_metric,                                 \\\n          const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>&     \\\n              eos,                                                             \\\n          const Element<3>& element,                                           \\\n          const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&        \\\n              ghost_data,                                                      \\\n          const Mesh<3>& subcell_mesh,                                         \\\n          const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,       \\\n          const Direction<3> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), GHMHD_NEUTRINOS)\n\n#undef INSTANTIATION\n#undef TAGS_LIST\n#undef THERMO_DIM\n#undef NEUTRINO\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/*!\n * \\brief Positivity-preserving adaptive order reconstruction. See\n * ::fd::reconstruction::positivity_preserving_adaptive_order() for details.\n * The rest mass density, electron fraction, and the pressure are kept positive.\n * Use unlimited 5th order (degree 4 polynomial) reconstruction on the\n * metric variables.\n *\n * Only the spacetime metric is reconstructed when we and the neighboring\n * element in the direction are doing FD. If we are doing DG and a neighboring\n * element is doing FD, then the spacetime metric, \\f$\\Phi_{iab}\\f$, and\n * \\f$\\Pi_{ab}\\f$ are all reconstructed since the Riemann solver on the DG\n * element also needs to solve for the metric variables.\n */\ntemplate <typename System>\nclass PositivityPreservingAdaptiveOrderPrim : public Reconstructor<System> {\n private:\n  using positivity_preserving_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n  using non_positive_tags =\n      tmpl::list<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n  using FallbackReconstructorType =\n      ::fd::reconstruction::FallbackReconstructorType;\n\n public:\n  static constexpr size_t dim = 3;\n\n  struct Alpha5 {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations.\"};\n  };\n  struct Alpha7 {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations. If not specified then \"\n        \"7th-order reconstruction is not used.\"};\n  };\n  struct Alpha9 {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations. If not specified then \"\n        \"9th-order reconstruction is not used.\"};\n  };\n  struct LowOrderReconstructor {\n    using type = FallbackReconstructorType;\n    static constexpr Options::String help = {\n        \"The 2nd/3rd-order reconstruction scheme to use if unlimited 5th-order \"\n        \"isn't okay.\"};\n  };\n  struct AtmosphereTreatment {\n    using type = ::VariableFixing::FixReconstructedStateToAtmosphere;\n    static constexpr Options::String help = {\n        \"What reconstructed states to fix to their atmosphere values.\"};\n  };\n  struct ReconstructRhoTimesTemperature {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If 'true' then we reconstruct the rho*T, if 'false' we reconstruct \"\n        \"T.\"};\n  };\n\n  using options =\n      tmpl::list<Alpha5, Alpha7, Alpha9, LowOrderReconstructor,\n                 AtmosphereTreatment, ReconstructRhoTimesTemperature>;\n\n  static constexpr Options::String help{\n      \"Positivity-preserving adaptive-order reconstruction.\"};\n\n  PositivityPreservingAdaptiveOrderPrim() = default;\n  PositivityPreservingAdaptiveOrderPrim(\n      PositivityPreservingAdaptiveOrderPrim&&) = default;\n  PositivityPreservingAdaptiveOrderPrim& operator=(\n      PositivityPreservingAdaptiveOrderPrim&&) = default;\n  PositivityPreservingAdaptiveOrderPrim(\n      const PositivityPreservingAdaptiveOrderPrim&) = default;\n  PositivityPreservingAdaptiveOrderPrim& operator=(\n      const PositivityPreservingAdaptiveOrderPrim&) = default;\n  ~PositivityPreservingAdaptiveOrderPrim() override = default;\n\n  PositivityPreservingAdaptiveOrderPrim(\n      double alpha_5, std::optional<double> alpha_7,\n      std::optional<double> alpha_9,\n      FallbackReconstructorType low_order_reconstructor,\n      ::VariableFixing::FixReconstructedStateToAtmosphere\n          fix_reconstructed_state_to_atmosphere,\n      bool reconstruct_rho_times_temperature,\n      const Options::Context& context = {});\n\n  explicit PositivityPreservingAdaptiveOrderPrim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      Reconstructor<System>, PositivityPreservingAdaptiveOrderPrim<System>);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor<System>> override;\n\n  static constexpr bool use_adaptive_order = true;\n  bool supports_adaptive_order() const override { return use_adaptive_order; }\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override {\n    return eight_to_the_alpha_9_.has_value()\n               ? 5\n               : (six_to_the_alpha_7_.has_value() ? 4 : 3);\n  }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                 typename System::variables_tag,\n                 hydro::Tags::GrmhdEquationOfState, domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>,\n                 ::Tags::VariableFixer<VariableFixing::FixToAtmosphere<dim>>>;\n\n  template <size_t ThermodynamicDim, typename TagsList>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<TagsList>, dim>*> vars_on_lower_face,\n      gsl::not_null<std::array<Variables<TagsList>, dim>*> vars_on_upper_face,\n      gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, dim>>*>\n          reconstruction_order,\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n      const Variables<typename System::variables_tag::type::tags_list>&\n          volume_spacetime_and_cons_vars,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere) const;\n\n  /// Called by an element doing DG when the neighbor is doing subcell.\n  template <size_t ThermodynamicDim, typename TagsList>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<TagsList>*> vars_on_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n      const Variables<\n          grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>&\n          subcell_volume_spacetime_metric,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,\n      Direction<dim> direction_to_reconstruct) const;\n\n  bool reconstruct_rho_times_temperature() const override;\n\n private:\n  template <typename LocalSystem>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(\n      const PositivityPreservingAdaptiveOrderPrim<LocalSystem>& lhs,\n      const PositivityPreservingAdaptiveOrderPrim<LocalSystem>& rhs);\n\n  template <typename LocalSystem>\n  friend bool operator!=(\n      const PositivityPreservingAdaptiveOrderPrim<LocalSystem>& lhs,\n      const PositivityPreservingAdaptiveOrderPrim<LocalSystem>& rhs);\n\n  void set_function_pointers();\n\n  double four_to_the_alpha_5_ = std::numeric_limits<double>::signaling_NaN();\n  std::optional<double> six_to_the_alpha_7_{};\n  std::optional<double> eight_to_the_alpha_9_{};\n  FallbackReconstructorType low_order_reconstructor_ =\n      FallbackReconstructorType::None;\n  ::VariableFixing::FixReconstructedStateToAtmosphere\n      fix_reconstructed_state_to_atmosphere_{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never};\n  bool reconstruct_rho_times_temperature_{false};\n\n  using PointerReconsOrder = void (*)(\n      gsl::not_null<std::array<gsl::span<double>, dim>*>,\n      gsl::not_null<std::array<gsl::span<double>, dim>*>,\n      gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, dim>>*>,\n      const gsl::span<const double>&,\n      const DirectionMap<dim, gsl::span<const double>>&, const Index<dim>&,\n      size_t, double, double, double);\n  using PointerRecons =\n      void (*)(gsl::not_null<std::array<gsl::span<double>, dim>*>,\n               gsl::not_null<std::array<gsl::span<double>, dim>*>,\n               const gsl::span<const double>&,\n               const DirectionMap<dim, gsl::span<const double>>&,\n               const Index<dim>&, size_t, double, double, double);\n  PointerRecons reconstruct_ = nullptr;\n  PointerReconsOrder pp_reconstruct_ = nullptr;\n\n  using PointerNeighbor = void (*)(gsl::not_null<DataVector*>,\n                                   const DataVector&, const DataVector&,\n                                   const Index<dim>&, const Index<dim>&,\n                                   const Direction<dim>&, const double&,\n                                   const double&, const double&);\n  PointerNeighbor reconstruct_lower_neighbor_ = nullptr;\n  PointerNeighbor reconstruct_upper_neighbor_ = nullptr;\n  PointerNeighbor pp_reconstruct_lower_neighbor_ = nullptr;\n  PointerNeighbor pp_reconstruct_upper_neighbor_ = nullptr;\n};\n\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\nnamespace evolution::dg::Actions::detail {\ntemplate <size_t Dim>\nstruct NormalVector;\n}  // namespace evolution::dg::Actions::detail\nnamespace gh::Tags {\nstruct ConstraintGamma1;\nstruct ConstraintGamma2;\n}  // namespace gh::Tags\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::fd {\nusing tags_list_for_reconstruct =\n    tmpl::push_front<grmhd::ValenciaDivClean::fd::tags_list_for_reconstruct,\n                     gr::Tags::SpacetimeMetric<DataVector, 3>,\n                     gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>;\n\nnamespace detail {\nusing tags_list_for_reconstruct_split_lapse =\n    tmpl::split<tags_list_for_reconstruct, gr::Tags::Lapse<DataVector>>;\n}  // namespace detail\n\nusing tags_list_for_reconstruct_fd_neighbor = tmpl::append<\n    tmpl::front<detail::tags_list_for_reconstruct_split_lapse>,\n    tmpl::push_front<tmpl::back<detail::tags_list_for_reconstruct_split_lapse>,\n                     gh::Tags::ConstraintGamma1, gh::Tags::ConstraintGamma2,\n                     gr::Tags::Lapse<DataVector>>>;\n\n/*!\n * \\brief Reconstructs \\f$\\rho, p, Wv^i, B^i\\f$, \\f$\\Phi\\f$, and the spacetime\n * metric, then computes the Lorentz factor, upper spatial velocity, specific\n * internal energy, and the conserved variables. All results are written into\n * `vars_on_lower_face` and `vars_on_upper_face`.\n */\ntemplate <typename SpacetimeTagsToReconstruct,\n          typename PrimTagsForReconstruction, typename PrimsTags,\n          typename SpacetimeAndConsTags, size_t ThermodynamicDim,\n          typename HydroReconstructor, typename SpacetimeReconstructor,\n          typename ComputeGrmhdSpacetimeVarsFromReconstructedSpacetimeTags,\n          typename PrimsTagsSentByNeighbor>\nvoid reconstruct_prims_work(\n    gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_lower_face,\n    gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_upper_face,\n    const HydroReconstructor& hydro_reconstructor,\n    const SpacetimeReconstructor& spacetime_reconstructor,\n    const ComputeGrmhdSpacetimeVarsFromReconstructedSpacetimeTags&\n        spacetime_vars_for_grmhd,\n    const Variables<PrimsTags>& volume_prims,\n    const Variables<SpacetimeAndConsTags>& volume_spacetime_and_cons_vars,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, Variables<PrimsTagsSentByNeighbor>>&\n        neighbor_data,\n    const Mesh<3>& subcell_mesh, size_t ghost_zone_size,\n    bool compute_conservatives, bool reconstruct_density_times_temperature,\n    const VariableFixing::FixToAtmosphere<3>* fix_to_atmosphere);\n\n/*!\n * \\brief Reconstructs \\f$\\rho, p, Wv^i, B^i\\f$, \\f$\\Phi\\f$, the spacetime\n * metric, \\f$\\Phi_{iab}\\f$, and \\f$\\Pi_{ab}\\f$, then computes the Lorentz\n * factor, upper spatial velocity, specific internal energy, and the conserved\n * variables. All results are written into `vars_on_face`.\n *\n * This is used on DG elements to reconstruct their subcell neighbors' solution\n * on the shared faces.\n */\ntemplate <\n    typename SpacetimeTagsToReconstruct, typename PrimTagsForReconstruction,\n    typename PrimsTagsSentByNeighbor, typename PrimsTags,\n    size_t ThermodynamicDim, typename LowerHydroReconstructor,\n    typename LowerSpacetimeReconstructor, typename UpperHydroReconstructor,\n    typename UpperSpacetimeReconstructor,\n    typename ComputeGrmhdSpacetimeVarsFromReconstructedSpacetimeTags>\nvoid reconstruct_fd_neighbor_work(\n    gsl::not_null<Variables<tags_list_for_reconstruct_fd_neighbor>*>\n        vars_on_face,\n    const LowerHydroReconstructor& reconstruct_lower_neighbor_hydro,\n    const LowerSpacetimeReconstructor& reconstruct_lower_neighbor_spacetime,\n    const UpperHydroReconstructor& reconstruct_upper_neighbor_hydro,\n    const UpperSpacetimeReconstructor& reconstruct_upper_neighbor_spacetime,\n    const ComputeGrmhdSpacetimeVarsFromReconstructedSpacetimeTags&\n        spacetime_vars_for_grmhd,\n    const Variables<PrimsTags>& subcell_volume_prims,\n    const Variables<\n        grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>&\n        subcell_volume_spacetime_vars,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh, const Direction<3>& direction_to_reconstruct,\n    size_t ghost_zone_size, bool compute_conservatives,\n    bool reconstruct_density_times_temperature,\n    const VariableFixing::FixToAtmosphere<3>* fix_to_atmosphere);\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\ntemplate <typename SpacetimeTagsToReconstruct,\n          typename PrimTagsForReconstruction, typename PrimsTags,\n          typename SpacetimeAndConsTags, size_t ThermodynamicDim,\n          typename HydroReconstructor, typename SpacetimeReconstructor,\n          typename ComputeGrmhdSpacetimeVarsFromReconstructedSpacetimeTags,\n          typename PrimsTagsSentByNeighbor>\nvoid reconstruct_prims_work(\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_upper_face,\n    const HydroReconstructor& hydro_reconstructor,\n    const SpacetimeReconstructor& spacetime_reconstructor,\n    const ComputeGrmhdSpacetimeVarsFromReconstructedSpacetimeTags&\n        spacetime_vars_for_grmhd,\n    const Variables<PrimsTags>& volume_prims,\n    const Variables<SpacetimeAndConsTags>& volume_spacetime_and_cons_vars,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, Variables<PrimsTagsSentByNeighbor>>&\n        neighbor_data,\n    const Mesh<3>& subcell_mesh, const size_t ghost_zone_size,\n    const bool compute_conservatives,\n    const bool reconstruct_density_times_temperature,\n    const VariableFixing::FixToAtmosphere<3>* fix_to_atmosphere) {\n  ASSERT(Mesh<3>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                 subcell_mesh.quadrature(0)) == subcell_mesh,\n         \"The subcell mesh should be isotropic but got \" << subcell_mesh);\n  const size_t volume_num_pts = subcell_mesh.number_of_grid_points();\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n  const size_t neighbor_num_pts =\n      ghost_zone_size * subcell_mesh.extents().slice_away(0).product();\n  size_t vars_in_neighbor_count = 0;\n  const size_t number_of_pts_for_thermodynamic_var =\n      6 * neighbor_num_pts + volume_num_pts;\n  DataVector buffer_for_recons_vars{\n      std::max(number_of_pts_for_thermodynamic_var, 3 * volume_num_pts)};\n  tmpl::for_each<\n      PrimTagsForReconstruction>([&buffer_for_recons_vars, &element,\n                                  &neighbor_data, neighbor_num_pts,\n                                  &hydro_reconstructor,\n                                  reconstruct_density_times_temperature,\n                                  reconstructed_num_pts, volume_num_pts,\n                                  &volume_prims, &vars_in_neighbor_count,\n                                  &vars_on_lower_face, &vars_on_upper_face,\n                                  &subcell_mesh](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const typename tag::type* volume_tensor_ptr = nullptr;\n    Variables<tmpl::list<\n        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>>\n        lorentz_factor_times_v_I{};\n    Scalar<DataVector> thermo_volume_var{};\n    if constexpr (std::is_same_v<tag,\n                                 hydro::Tags::LorentzFactorTimesSpatialVelocity<\n                                     DataVector, 3>>) {\n      // we need to handle the Wv^i reconstruction separately since we need to\n      // first compute Wv^i in the volume (it's not one of our primitives from\n      // the recovery). The components need to be stored contiguously, which is\n      // why we have the Variables `lorentz_factor_times_v_I`\n      const auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(volume_prims);\n      const auto& lorentz_factor =\n          get<hydro::Tags::LorentzFactor<DataVector>>(volume_prims);\n      lorentz_factor_times_v_I.set_data_ref(buffer_for_recons_vars.data(),\n                                            3 * volume_num_pts);\n      auto& volume_tensor =\n          get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n              lorentz_factor_times_v_I) = spatial_velocity;\n      for (size_t i = 0; i < 3; ++i) {\n        volume_tensor.get(i) *= get(lorentz_factor);\n      }\n      volume_tensor_ptr = &volume_tensor;\n    } else if constexpr (std::is_same_v<tag,\n                                        hydro::Tags::Temperature<DataVector>>) {\n      if (reconstruct_density_times_temperature) {\n        get(thermo_volume_var)\n            .set_data_ref(buffer_for_recons_vars.data(), volume_num_pts);\n        get(thermo_volume_var) =\n            get(get<tag>(volume_prims)) *\n            get(get<hydro::Tags::RestMassDensity<DataVector>>(volume_prims));\n        volume_tensor_ptr = &thermo_volume_var;\n      } else {\n        volume_tensor_ptr = &get<tag>(volume_prims);\n      }\n    } else {\n      volume_tensor_ptr = &get<tag>(volume_prims);\n    }\n\n    const size_t number_of_variables = volume_tensor_ptr->size();\n    const gsl::span<const double> volume_vars = gsl::make_span(\n        (*volume_tensor_ptr)[0].data(), number_of_variables * volume_num_pts);\n    std::array<gsl::span<double>, 3> upper_face_vars{};\n    std::array<gsl::span<double>, 3> lower_face_vars{};\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(upper_face_vars, i) =\n          gsl::make_span(get<tag>(gsl::at(*vars_on_upper_face, i))[0].data(),\n                         number_of_variables * reconstructed_num_pts);\n      gsl::at(lower_face_vars, i) =\n          gsl::make_span(get<tag>(gsl::at(*vars_on_lower_face, i))[0].data(),\n                         number_of_variables * reconstructed_num_pts);\n    }\n\n    DirectionMap<3, gsl::span<const double>> ghost_cell_vars{};\n    for (const auto& direction : Direction<3>::all_directions()) {\n      DirectionalId<3> id{};\n      if (element.neighbors().contains(direction)) {\n        const auto& neighbors_in_direction = element.neighbors().at(direction);\n        ASSERT(neighbors_in_direction.size() == 1,\n               \"Currently only support one neighbor in each direction, but \"\n               \"got \"\n                   << neighbors_in_direction.size() << \" in direction \"\n                   << direction);\n        id = DirectionalId<3>{direction, *neighbors_in_direction.begin()};\n      } else {\n        // retrieve boundary ghost data from neighbor_data\n        ASSERT(\n            element.external_boundaries().count(direction) == 1,\n            \"Element has neither neighbor nor external boundary to direction: \"\n                << direction);\n        id = DirectionalId<3>{direction, ElementId<3>::external_boundary_id()};\n      }\n      if constexpr (std::is_same_v<tag, hydro::Tags::Temperature<DataVector>>) {\n        ASSERT(number_of_variables == 1,\n               \"Should only have one tensor component for a Scalar\");\n        if (reconstruct_density_times_temperature) {\n          DataVector view{\n              &buffer_for_recons_vars[volume_num_pts +\n                                      (2 * direction.dimension() +\n                                       (direction.side() == Side::Upper ? 1\n                                                                        : 0)) *\n                                          neighbor_num_pts],\n              number_of_variables * neighbor_num_pts};\n          const auto& data_in_dir = neighbor_data.at(id);\n          view =\n              get(get<hydro::Tags::RestMassDensity<DataVector>>(data_in_dir)) *\n              get(get<tag>(data_in_dir));\n\n          ghost_cell_vars[direction] = gsl::make_span(view.data(), view.size());\n          continue;\n        }\n      }\n      ghost_cell_vars[direction] =\n          gsl::make_span(get<tag>(neighbor_data.at(id))[0].data(),\n                         number_of_variables * neighbor_num_pts);\n    }\n\n    hydro_reconstructor(make_not_null(&upper_face_vars),\n                        make_not_null(&lower_face_vars), volume_vars,\n                        ghost_cell_vars, subcell_mesh.extents(),\n                        number_of_variables);\n\n    if constexpr (std::is_same_v<tag, hydro::Tags::Temperature<DataVector>>) {\n      if (reconstruct_density_times_temperature) {\n        for (size_t i = 0; i < 3; ++i) {\n          get(get<tag>(gsl::at(*vars_on_upper_face, i))) /=\n              get(get<hydro::Tags::RestMassDensity<DataVector>>(\n                  gsl::at(*vars_on_upper_face, i)));\n          get(get<tag>(gsl::at(*vars_on_lower_face, i))) /=\n              get(get<hydro::Tags::RestMassDensity<DataVector>>(\n                  gsl::at(*vars_on_lower_face, i)));\n        }\n      }\n    }\n\n    vars_in_neighbor_count += number_of_variables;\n  });\n  tmpl::for_each<SpacetimeTagsToReconstruct>(\n      [&element, &neighbor_data, neighbor_num_pts, &spacetime_reconstructor,\n       reconstructed_num_pts, volume_num_pts, &volume_spacetime_and_cons_vars,\n       &vars_in_neighbor_count, &vars_on_lower_face, &vars_on_upper_face,\n       &subcell_mesh](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const typename tag::type& volume_tensor =\n            get<tag>(volume_spacetime_and_cons_vars);\n\n        const size_t number_of_variables = volume_tensor.size();\n        const gsl::span<const double> volume_vars = gsl::make_span(\n            (volume_tensor)[0].data(), number_of_variables * volume_num_pts);\n        std::array<gsl::span<double>, 3> upper_face_vars{};\n        std::array<gsl::span<double>, 3> lower_face_vars{};\n        for (size_t i = 0; i < 3; ++i) {\n          gsl::at(upper_face_vars, i) = gsl::make_span(\n              get<tag>(gsl::at(*vars_on_upper_face, i))[0].data(),\n              number_of_variables * reconstructed_num_pts);\n          gsl::at(lower_face_vars, i) = gsl::make_span(\n              get<tag>(gsl::at(*vars_on_lower_face, i))[0].data(),\n              number_of_variables * reconstructed_num_pts);\n        }\n\n        DirectionMap<3, gsl::span<const double>> ghost_cell_vars{};\n        for (const auto& direction : Direction<3>::all_directions()) {\n          if (element.neighbors().contains(direction)) {\n            const auto& neighbors_in_direction =\n                element.neighbors().at(direction);\n            ASSERT(neighbors_in_direction.size() == 1,\n                   \"Currently only support one neighbor in each direction, but \"\n                   \"got \"\n                       << neighbors_in_direction.size() << \" in direction \"\n                       << direction);\n            ghost_cell_vars[direction] = gsl::make_span(\n                get<tag>(neighbor_data.at(DirectionalId<3>{\n                    direction, *neighbors_in_direction.begin()}))[0]\n                    .data(),\n                number_of_variables * neighbor_num_pts);\n          } else {\n            // retrieve boundary ghost data from neighbor_data\n            ASSERT(element.external_boundaries().count(direction) == 1,\n                   \"Element has neither neighbor nor external boundary to \"\n                   \"direction : \"\n                       << direction);\n            ghost_cell_vars[direction] = gsl::make_span(\n                get<tag>(neighbor_data.at(DirectionalId<3>{\n                    direction, ElementId<3>::external_boundary_id()}))[0]\n                    .data(),\n                number_of_variables * neighbor_num_pts);\n          }\n        }\n\n        spacetime_reconstructor(make_not_null(&upper_face_vars),\n                                make_not_null(&lower_face_vars), volume_vars,\n                                ghost_cell_vars, subcell_mesh.extents(),\n                                number_of_variables);\n\n        vars_in_neighbor_count += number_of_variables;\n      });\n\n  for (size_t i = 0; i < 3; ++i) {\n    if constexpr (tmpl::size<SpacetimeTagsToReconstruct>::value != 0) {\n      spacetime_vars_for_grmhd(make_not_null(&gsl::at(*vars_on_lower_face, i)));\n      spacetime_vars_for_grmhd(make_not_null(&gsl::at(*vars_on_upper_face, i)));\n    }\n\n    if (compute_conservatives) {\n      ValenciaDivClean::fd::compute_conservatives_for_reconstruction(\n          make_not_null(&gsl::at(*vars_on_lower_face, i)), eos,\n          fix_to_atmosphere);\n      ValenciaDivClean::fd::compute_conservatives_for_reconstruction(\n          make_not_null(&gsl::at(*vars_on_upper_face, i)), eos,\n          fix_to_atmosphere);\n    }\n  }\n}\n\ntemplate <\n    typename SpacetimeTagsToReconstruct, typename PrimTagsForReconstruction,\n    typename PrimsTagsSentByNeighbor, typename PrimsTags,\n    size_t ThermodynamicDim, typename LowerHydroReconstructor,\n    typename LowerSpacetimeReconstructor, typename UpperHydroReconstructor,\n    typename UpperSpacetimeReconstructor,\n    typename ComputeGrmhdSpacetimeVarsFromReconstructedSpacetimeTags>\nvoid reconstruct_fd_neighbor_work(\n    const gsl::not_null<Variables<tags_list_for_reconstruct_fd_neighbor>*>\n        vars_on_face,\n    const LowerHydroReconstructor& reconstruct_lower_neighbor_hydro,\n    const LowerSpacetimeReconstructor& reconstruct_lower_neighbor_spacetime,\n    const UpperHydroReconstructor& reconstruct_upper_neighbor_hydro,\n    const UpperSpacetimeReconstructor& reconstruct_upper_neighbor_spacetime,\n    const ComputeGrmhdSpacetimeVarsFromReconstructedSpacetimeTags&\n        spacetime_vars_for_grmhd,\n    const Variables<PrimsTags>& subcell_volume_prims,\n    const Variables<\n        grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>&\n        subcell_volume_spacetime_vars,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh, const Direction<3>& direction_to_reconstruct,\n    const size_t ghost_zone_size, const bool compute_conservatives,\n    const bool reconstruct_density_times_temperature,\n    const VariableFixing::FixToAtmosphere<3>* const fix_to_atmosphere) {\n  const DirectionalId<3> mortar_id{\n      direction_to_reconstruct,\n      *element.neighbors().at(direction_to_reconstruct).begin()};\n  Index<3> ghost_data_extents = subcell_mesh.extents();\n  ghost_data_extents[direction_to_reconstruct.dimension()] = ghost_zone_size;\n  Variables<PrimsTagsSentByNeighbor> neighbor_prims{\n      ghost_data_extents.product()};\n  {\n    ASSERT(ghost_data.contains(mortar_id),\n           \"The neighbor data does not contain the mortar: \" << mortar_id);\n    const DataVector& neighbor_data_on_mortar =\n        ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    std::copy(neighbor_data_on_mortar.begin(),\n              std::next(neighbor_data_on_mortar.begin(),\n                        static_cast<std::ptrdiff_t>(\n                            neighbor_prims.number_of_independent_components *\n                            ghost_data_extents.product())),\n              neighbor_prims.data());\n  }\n\n  DataVector buffer{3 * subcell_volume_prims.number_of_grid_points() +\n                    ghost_data_extents.product()};\n  Scalar<DataVector> rho_times_temperature_neighbor{};\n  tmpl::for_each<PrimTagsForReconstruction>(\n      [&buffer, &direction_to_reconstruct, &ghost_data_extents, &neighbor_prims,\n       reconstruct_density_times_temperature, &reconstruct_lower_neighbor_hydro,\n       &reconstruct_upper_neighbor_hydro, &rho_times_temperature_neighbor,\n       &subcell_mesh, &subcell_volume_prims, &vars_on_face](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const typename tag::type* volume_tensor_ptr = nullptr;\n        typename tag::type volume_tensor{};\n        if constexpr (std::is_same_v<\n                          tag, hydro::Tags::LorentzFactorTimesSpatialVelocity<\n                                   DataVector, 3>>) {\n          // we need to handle the Wv^i reconstruction separately since we need\n          // to first compute Wv^i in the volume (it's not one of our primitives\n          // from the recovery). The components need to be stored contiguously,\n          // which is why we have the Variables `lorentz_factor_times_v_I`\n          const auto& spatial_velocity =\n              get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n                  subcell_volume_prims);\n          const auto& lorentz_factor =\n              get<hydro::Tags::LorentzFactor<DataVector>>(subcell_volume_prims);\n          for (size_t i = 0; i < 3; ++i) {\n            volume_tensor.get(i).set_data_ref(\n                &buffer[i * subcell_volume_prims.number_of_grid_points()],\n                subcell_volume_prims.number_of_grid_points());\n          }\n          volume_tensor = spatial_velocity;\n          for (size_t i = 0; i < 3; ++i) {\n            volume_tensor.get(i) *= get(lorentz_factor);\n          }\n          volume_tensor_ptr = &volume_tensor;\n        } else {\n          if constexpr (std::is_same_v<tag,\n                                       hydro::Tags::Temperature<DataVector>>) {\n            if (reconstruct_density_times_temperature) {\n              get(volume_tensor)\n                  .set_data_ref(buffer.data(),\n                                subcell_volume_prims.number_of_grid_points());\n              get(volume_tensor) =\n                  get(get<hydro::Tags::RestMassDensity<DataVector>>(\n                      subcell_volume_prims)) *\n                  get(get<tag>(subcell_volume_prims));\n              volume_tensor_ptr = &volume_tensor;\n            } else {\n              volume_tensor_ptr = &get<tag>(subcell_volume_prims);\n            }\n          } else {\n            volume_tensor_ptr = &get<tag>(subcell_volume_prims);\n          }\n        }\n\n        const auto& tensor_neighbor =\n            [&buffer, &neighbor_prims, reconstruct_density_times_temperature,\n             &rho_times_temperature_neighbor,\n             &subcell_volume_prims]() -> typename tag::type& {\n          if constexpr (std::is_same_v<tag,\n                                       hydro::Tags::Temperature<DataVector>>) {\n            if (reconstruct_density_times_temperature) {\n              get(rho_times_temperature_neighbor)\n                  .set_data_ref(\n                      &buffer[subcell_volume_prims.number_of_grid_points()],\n                      get(get<tag>(neighbor_prims)).size());\n              get(rho_times_temperature_neighbor) =\n                  get(get<hydro::Tags::RestMassDensity<DataVector>>(\n                      neighbor_prims)) *\n                  get(get<tag>(neighbor_prims));\n              return rho_times_temperature_neighbor;\n            } else {\n              (void)buffer, (void)reconstruct_density_times_temperature;\n              (void)rho_times_temperature_neighbor, (void)subcell_volume_prims;\n              return get<tag>(neighbor_prims);\n            }\n          } else {\n            (void)buffer, (void)reconstruct_density_times_temperature;\n            (void)rho_times_temperature_neighbor, (void)subcell_volume_prims;\n            return get<tag>(neighbor_prims);\n          }\n        }();\n        auto& tensor_on_face = get<tag>(*vars_on_face);\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_upper_neighbor_hydro(\n                make_not_null(&tensor_on_face[tensor_index]),\n                (*volume_tensor_ptr)[tensor_index],\n                tensor_neighbor[tensor_index], subcell_mesh.extents(),\n                ghost_data_extents, direction_to_reconstruct);\n          }\n        } else {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_lower_neighbor_hydro(\n                make_not_null(&tensor_on_face[tensor_index]),\n                (*volume_tensor_ptr)[tensor_index],\n                tensor_neighbor[tensor_index], subcell_mesh.extents(),\n                ghost_data_extents, direction_to_reconstruct);\n          }\n        }\n        if constexpr (std::is_same_v<tag,\n                                     hydro::Tags::Temperature<DataVector>>) {\n          if (reconstruct_density_times_temperature) {\n            get(tensor_on_face) /= get(\n                get<hydro::Tags::RestMassDensity<DataVector>>(*vars_on_face));\n          }\n        }\n      });\n\n  tmpl::for_each<SpacetimeTagsToReconstruct>(\n      [&direction_to_reconstruct, &ghost_data_extents, &neighbor_prims,\n       &reconstruct_lower_neighbor_spacetime,\n       &reconstruct_upper_neighbor_spacetime, &subcell_mesh,\n       &subcell_volume_spacetime_vars, &vars_on_face](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const typename tag::type volume_tensor =\n            get<tag>(subcell_volume_spacetime_vars);\n\n        const auto& tensor_neighbor = get<tag>(neighbor_prims);\n        auto& tensor_on_face = get<tag>(*vars_on_face);\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_upper_neighbor_spacetime(\n                make_not_null(&tensor_on_face[tensor_index]),\n                volume_tensor[tensor_index], tensor_neighbor[tensor_index],\n                subcell_mesh.extents(), ghost_data_extents,\n                direction_to_reconstruct);\n          }\n        } else {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_lower_neighbor_spacetime(\n                make_not_null(&tensor_on_face[tensor_index]),\n                volume_tensor[tensor_index], tensor_neighbor[tensor_index],\n                subcell_mesh.extents(), ghost_data_extents,\n                direction_to_reconstruct);\n          }\n        }\n      });\n\n  if constexpr (tmpl::size<SpacetimeTagsToReconstruct>::value != 0) {\n    spacetime_vars_for_grmhd(vars_on_face);\n  }\n  if (compute_conservatives) {\n    ValenciaDivClean::fd::compute_conservatives_for_reconstruction(\n        vars_on_face, eos, fix_to_atmosphere);\n  }\n}\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n\n#include <pup.h>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\nnamespace grmhd::GhValenciaDivClean::fd {\ntemplate <typename System>\nReconstructor<System>::Reconstructor(CkMigrateMessage* const msg)\n    : PUP::able(msg) {}\n\ntemplate <typename System>\nvoid Reconstructor<System>::pup(PUP::er& p) {\n  PUP::able::pup(p);\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)  \\\n  template class Reconstructor< \\\n      typename grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, GHMHD_NEUTRINOS)\n\n#undef INSTANTIATION\n#undef NEUTRINO\n\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/// \\cond\ntemplate <typename System>\nclass MonotonisedCentralPrim;\ntemplate <typename System>\nclass PositivityPreservingAdaptiveOrderPrim;\ntemplate <typename System>\nclass Wcns5zPrim;\n/// \\endcond\n\n/*!\n * \\brief The base class from which all reconstruction schemes must inherit\n */\n\n// template on System instead\ntemplate <typename System>\nclass Reconstructor : public PUP::able {\n public:\n  Reconstructor() = default;\n  Reconstructor(const Reconstructor&) = default;\n  Reconstructor& operator=(const Reconstructor&) = default;\n  Reconstructor(Reconstructor&&) = default;\n  Reconstructor& operator=(Reconstructor&&) = default;\n  ~Reconstructor() override = default;\n\n  /// \\cond\n  explicit Reconstructor(CkMigrateMessage* msg);\n  WRAPPED_PUPable_abstract(Reconstructor<System>);  // NOLINT\n  /// \\endcond\n\n  using system = System;\n  using creatable_classes =\n      tmpl::list<MonotonisedCentralPrim<System>,\n                 PositivityPreservingAdaptiveOrderPrim<System>,\n                 Wcns5zPrim<System>>;\n\n  virtual std::unique_ptr<Reconstructor<System>> get_clone() const = 0;\n\n  virtual size_t ghost_zone_size() const = 0;\n\n  virtual bool supports_adaptive_order() const { return false; }\n\n  virtual bool reconstruct_rho_times_temperature() const = 0;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n};\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\n\ntemplate <typename System>\nvoid register_derived_with_charm() {\n  register_classes_with_charm(\n      typename Reconstructor<System>::creatable_classes{});\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template void                \\\n  register_derived_with_charm<GhValenciaDivClean::System<NEUTRINO(data)>>();\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, GHMHD_NEUTRINOS)\n\n#undef INSTANTIATION\n#undef NEUTRINO\n\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace grmhd::GhValenciaDivClean::fd {\n\ntemplate <typename System>\nvoid register_derived_with_charm();\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellSolver.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FilterOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/// Option tags for finite difference solver\nnamespace OptionTags {\n/// \\brief Option tag for the reconstructor\ntemplate <typename System>\nstruct Reconstructor {\n  using type = std::unique_ptr<fd::Reconstructor<System>>;\n\n  static constexpr Options::String help = {\"The reconstruction scheme to use.\"};\n  using group = evolution::dg::subcell::OptionTags::SubcellSolverGroup;\n};\n\n/// \\brief Option tag for the filter/dissipation options.\nstruct FilterOptions {\n  using type = fd::FilterOptions;\n\n  static constexpr Options::String help = type::help;\n  using group = evolution::dg::subcell::OptionTags::SubcellSolverGroup;\n};\n}  // namespace OptionTags\n\n/// %Tags for finite difference solver\nnamespace Tags {\n/// \\brief Tag for the reconstructor\ntemplate <typename System>\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<fd::Reconstructor<System>>;\n  using option_tags =\n      tmpl::list<OptionTags::Reconstructor<System>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& reconstructor) {\n    return reconstructor->get_clone();\n  }\n};\n\n/// \\brief Tag for filter/dissipation options.\nstruct FilterOptions : db::SimpleTag {\n  using type = fd::FilterOptions;\n  using option_tags = tmpl::list<OptionTags::FilterOptions>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& filter_options) {\n    return filter_options;\n  }\n};\n}  // namespace Tags\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Wcns5z.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Wcns5z.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Unlimited.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Wcns5z.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::fd {\n\ntemplate <typename System>\nWcns5zPrim<System>::Wcns5zPrim(\n    const size_t nonlinear_weight_exponent, const double epsilon,\n    const ::fd::reconstruction::FallbackReconstructorType\n        fallback_reconstructor,\n    const size_t max_number_of_extrema,\n    const ::VariableFixing::FixReconstructedStateToAtmosphere\n        fix_reconstructed_state_to_atmosphere,\n    const bool reconstruct_rho_times_temperature)\n    : nonlinear_weight_exponent_(nonlinear_weight_exponent),\n      epsilon_(epsilon),\n      fallback_reconstructor_(fallback_reconstructor),\n      max_number_of_extrema_(max_number_of_extrema),\n      fix_reconstructed_state_to_atmosphere_(\n          fix_reconstructed_state_to_atmosphere),\n      reconstruct_rho_times_temperature_(reconstruct_rho_times_temperature) {\n  std::tie(reconstruct_, reconstruct_lower_neighbor_,\n           reconstruct_upper_neighbor_) =\n      ::fd::reconstruction::wcns5z_function_pointers<3>(\n          nonlinear_weight_exponent_, fallback_reconstructor_);\n}\n\ntemplate <typename System>\nWcns5zPrim<System>::Wcns5zPrim(CkMigrateMessage* const msg)\n    : Reconstructor<System>(msg) {}\n\ntemplate <typename System>\nstd::unique_ptr<Reconstructor<System>> Wcns5zPrim<System>::get_clone() const {\n  return std::make_unique<Wcns5zPrim<System>>(*this);\n}\n\ntemplate <typename System>\nvoid Wcns5zPrim<System>::pup(PUP::er& p) {\n  Reconstructor<System>::pup(p);\n  p | nonlinear_weight_exponent_;\n  p | epsilon_;\n  p | fallback_reconstructor_;\n  p | max_number_of_extrema_;\n  p | fix_reconstructed_state_to_atmosphere_;\n  p | reconstruct_rho_times_temperature_;\n  if (p.isUnpacking()) {\n    std::tie(reconstruct_, reconstruct_lower_neighbor_,\n             reconstruct_upper_neighbor_) =\n        ::fd::reconstruction::wcns5z_function_pointers<3>(\n            nonlinear_weight_exponent_, fallback_reconstructor_);\n  }\n}\n\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Wcns5zPrim<System>::my_PUP_ID = 0;\n\ntemplate <typename System>\ntemplate <size_t ThermodynamicDim, typename TagsList>\nvoid Wcns5zPrim<System>::reconstruct(\n    const gsl::not_null<std::array<Variables<TagsList>, dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, dim>*>\n        vars_on_upper_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n    const Variables<typename System::variables_tag::type::tags_list>&\n        volume_spacetime_and_cons_vars,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere) const {\n  using ::VariableFixing::FixReconstructedStateToAtmosphere;\n  using all_tags_for_reconstruction = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n\n  DirectionalIdMap<dim, Variables<all_tags_for_reconstruction>>\n      neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_prims_work<tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                         prims_to_reconstruct_tags>(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_vars, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n                     ghost_cell_vars, subcell_extents, number_of_variables,\n                     epsilon_, max_number_of_extrema_);\n      },\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_vars, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::unlimited<4>(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      [](const auto vars_on_face_ptr) {\n        const auto& spacetime_metric =\n            get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*vars_on_face_ptr);\n        auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face_ptr);\n        gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n        auto& inverse_spatial_metric =\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                *vars_on_face_ptr);\n        auto& sqrt_det_spatial_metric =\n            get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*vars_on_face_ptr);\n\n        determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                                make_not_null(&inverse_spatial_metric),\n                                spatial_metric);\n        get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n        auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*vars_on_face_ptr);\n        gr::shift(make_not_null(&shift), spacetime_metric,\n                  inverse_spatial_metric);\n        gr::lapse(\n            make_not_null(&get<gr::Tags::Lapse<DataVector>>(*vars_on_face_ptr)),\n            shift, spacetime_metric);\n      },\n      volume_prims, volume_spacetime_and_cons_vars, eos, element,\n      neighbor_variables_data, subcell_mesh, ghost_zone_size(), true,\n      reconstruct_rho_times_temperature(),\n      (fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::Always or\n               fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::OnFdOnly\n           ? &fix_to_atmosphere\n           : nullptr));\n}\n\ntemplate <typename System>\ntemplate <size_t ThermodynamicDim, typename TagsList>\nvoid Wcns5zPrim<System>::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n    const Variables<\n        grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>&\n        subcell_volume_spacetime_metric,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh,\n    const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,\n    const Direction<3>& direction_to_reconstruct) const {\n  using ::VariableFixing::FixReconstructedStateToAtmosphere;\n  using prim_tags_for_reconstruction =\n      grmhd::GhValenciaDivClean::Tags::primitive_grmhd_reconstruction_tags;\n  using all_tags_for_reconstruction = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n\n  reconstruct_fd_neighbor_work<Tags::spacetime_reconstruction_tags,\n                               prim_tags_for_reconstruction,\n                               all_tags_for_reconstruction>(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<dim>& subcell_extents,\n             const Index<dim>& ghost_data_extents,\n             const Direction<dim>& local_direction_to_reconstruct) {\n        reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, epsilon_, max_number_of_extrema_);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::UnlimitedReconstructor<4>>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<dim>& subcell_extents,\n             const Index<dim>& ghost_data_extents,\n             const Direction<dim>& local_direction_to_reconstruct) {\n        reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, epsilon_, max_number_of_extrema_);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::UnlimitedReconstructor<4>>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto vars_on_face_ptr) {\n        const auto& spacetime_metric =\n            get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*vars_on_face_ptr);\n        auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face_ptr);\n        gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n        auto& inverse_spatial_metric =\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                *vars_on_face_ptr);\n        auto& sqrt_det_spatial_metric =\n            get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*vars_on_face_ptr);\n\n        determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                                make_not_null(&inverse_spatial_metric),\n                                spatial_metric);\n        get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n        auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*vars_on_face_ptr);\n        gr::shift(make_not_null(&shift), spacetime_metric,\n                  inverse_spatial_metric);\n        gr::lapse(\n            make_not_null(&get<gr::Tags::Lapse<DataVector>>(*vars_on_face_ptr)),\n            shift, spacetime_metric);\n      },\n      subcell_volume_prims, subcell_volume_spacetime_metric, eos, element,\n      ghost_data, subcell_mesh, direction_to_reconstruct, ghost_zone_size(),\n      true, reconstruct_rho_times_temperature(),\n      (fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::Always or\n               fix_reconstructed_state_to_atmosphere_ ==\n                   FixReconstructedStateToAtmosphere::AtDgFdInterfaceOnly\n           ? &fix_to_atmosphere\n           : nullptr));\n}\n\ntemplate <typename System>\nbool Wcns5zPrim<System>::reconstruct_rho_times_temperature() const {\n  return reconstruct_rho_times_temperature_;\n}\n\ntemplate <typename System>\nbool operator==(const Wcns5zPrim<System>& lhs, const Wcns5zPrim<System>& rhs) {\n  // Don't check function pointers since they are set from\n  // nonlinear_weight_exponent_ and fallback_reconstructor_\n  return lhs.nonlinear_weight_exponent_ == rhs.nonlinear_weight_exponent_ and\n         lhs.epsilon_ == rhs.epsilon_ and\n         lhs.fallback_reconstructor_ == rhs.fallback_reconstructor_ and\n         lhs.max_number_of_extrema_ == rhs.max_number_of_extrema_ and\n         lhs.fix_reconstructed_state_to_atmosphere_ ==\n             rhs.fix_reconstructed_state_to_atmosphere_ and\n         lhs.reconstruct_rho_times_temperature() ==\n             rhs.reconstruct_rho_times_temperature();\n}\n\ntemplate <typename System>\nbool operator!=(const Wcns5zPrim<System>& lhs, const Wcns5zPrim<System>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                             \\\n  template class Wcns5zPrim<                                               \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>;                  \\\n  template bool operator==(                                                \\\n      const Wcns5zPrim<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>& \\\n          lhs,                                                             \\\n      const Wcns5zPrim<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>& \\\n          rhs);                                                            \\\n  template bool operator!=(                                                \\\n      const Wcns5zPrim<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>& \\\n          lhs,                                                             \\\n      const Wcns5zPrim<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>& \\\n          rhs);\nGENERATE_INSTANTIATIONS(INSTANTIATION, GHMHD_NEUTRINOS)\n#undef INSTANTIATION\n#undef NEUTRINO\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n// If templated on System, need to template  Wcns5zPrim<typename\n// System<NEUTRINO(data)>>\n#define INSTANTIATION(r, data)                                                 \\\n  template void                                                                \\\n  Wcns5zPrim<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>::reconstruct(  \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>      \\\n          vars_on_lower_face,                                                  \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>      \\\n          vars_on_upper_face,                                                  \\\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,            \\\n      const Variables<typename grmhd::GhValenciaDivClean::System<NEUTRINO(     \\\n          data)>::variables_tag::type::tags_list>&                             \\\n          volume_spacetime_and_cons_vars,                                      \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos,    \\\n      const Element<3>& element,                                               \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&            \\\n          ghost_data,                                                          \\\n      const Mesh<3>& subcell_mesh,                                             \\\n      const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere) const;    \\\n  template void                                                                \\\n  Wcns5zPrim<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>::              \\\n      reconstruct_fd_neighbor(                                                 \\\n          gsl::not_null<Variables<tags_list_for_reconstruct_fd_neighbor>*>     \\\n              vars_on_face,                                                    \\\n          const Variables<hydro::grmhd_tags<DataVector>>&                      \\\n              subcell_volume_prims,                                            \\\n          const Variables<                                                     \\\n              grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>& \\\n              subcell_volume_spacetime_metric,                                 \\\n          const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>&     \\\n              eos,                                                             \\\n          const Element<3>& element,                                           \\\n          const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&        \\\n              ghost_data,                                                      \\\n          const Mesh<3>& subcell_mesh,                                         \\\n          const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,       \\\n          const Direction<3>& direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), GHMHD_NEUTRINOS)\n\n#undef INSTANTIATION\n#undef TAGS_LIST\n#undef THERMO_DIM\n#undef NEUTRINO\n\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Wcns5z.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::fd {\n/*!\n * \\brief Fifth order weighted nonlinear compact scheme reconstruction using the\n * Z oscillation indicator. See ::fd::reconstruction::wcns5z() for details.\n *\n */\n// template on System instead\ntemplate <typename System>\nclass Wcns5zPrim : public Reconstructor<System> {\n private:\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n  using FallbackReconstructorType =\n      ::fd::reconstruction::FallbackReconstructorType;\n\n public:\n  static constexpr size_t dim = 3;\n\n  struct NonlinearWeightExponent {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The exponent q to which the oscillation indicator term is raised\"};\n  };\n  struct Epsilon {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The parameter added to the oscillation indicators to avoid division \"\n        \"by zero\"};\n  };\n  struct FallbackReconstructor {\n    using type = FallbackReconstructorType;\n    static constexpr Options::String help = {\n        \"A reconstruction scheme to fallback to adaptively. Finite difference \"\n        \"will switch to this reconstruction scheme if there are more extrema \"\n        \"in a FD stencil than a specified number. See also the option \"\n        \"'MaxNumberOfExtrema' below. Adaptive fallback is disabled if 'None'.\"};\n  };\n  struct MaxNumberOfExtrema {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The maximum allowed number of extrema in FD stencil for using Wcns5z \"\n        \"reconstruction before switching to a low-order reconstruction. If \"\n        \"FallbackReconstructor=None, this option is ignored\"};\n  };\n\n  struct AtmosphereTreatment {\n    using type = ::VariableFixing::FixReconstructedStateToAtmosphere;\n    static constexpr Options::String help = {\n        \"What reconstructed states to fix to their atmosphere values.\"};\n  };\n\n  struct ReconstructRhoTimesTemperature {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If 'true' then we reconstruct the rho*T, if 'false' we reconstruct \"\n        \"T.\"};\n  };\n\n  using options =\n      tmpl::list<NonlinearWeightExponent, Epsilon, FallbackReconstructor,\n                 MaxNumberOfExtrema, AtmosphereTreatment,\n                 ReconstructRhoTimesTemperature>;\n\n  static constexpr Options::String help{\n      \"WCNS 5Z reconstruction scheme using primitive variables.\"};\n\n  Wcns5zPrim() = default;\n  Wcns5zPrim(Wcns5zPrim&&) = default;\n  Wcns5zPrim& operator=(Wcns5zPrim&&) = default;\n  Wcns5zPrim(const Wcns5zPrim&) = default;\n  Wcns5zPrim& operator=(const Wcns5zPrim&) = default;\n  ~Wcns5zPrim() override = default;\n\n  Wcns5zPrim(size_t nonlinear_weight_exponent, double epsilon,\n             FallbackReconstructorType fallback_reconstructor,\n             size_t max_number_of_extrema,\n             ::VariableFixing::FixReconstructedStateToAtmosphere\n                 fix_reconstructed_state_to_atmosphere,\n             bool reconstruct_rho_times_temperature);\n\n  explicit Wcns5zPrim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor<System>, Wcns5zPrim<System>);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor<System>> override;\n\n  static constexpr bool use_adaptive_order = false;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 3; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                 typename System::variables_tag,\n                 hydro::Tags::GrmhdEquationOfState, domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>,\n                 ::Tags::VariableFixer<VariableFixing::FixToAtmosphere<dim>>>;\n\n  template <size_t ThermodynamicDim, typename TagsList>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<TagsList>, dim>*> vars_on_lower_face,\n      gsl::not_null<std::array<Variables<TagsList>, dim>*> vars_on_upper_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n      const Variables<typename System::variables_tag::type::tags_list>&\n          volume_spacetime_and_cons_vars,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere) const;\n\n  template <size_t ThermodynamicDim, typename TagsList>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<TagsList>*> vars_on_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n      const Variables<\n          grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>&\n          subcell_volume_spacetime_metric,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      const VariableFixing::FixToAtmosphere<dim>& fix_to_atmosphere,\n      const Direction<dim>& direction_to_reconstruct) const;\n\n  bool reconstruct_rho_times_temperature() const override;\n\n private:\n  template <typename LocalSystem>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Wcns5zPrim<LocalSystem>& lhs,\n                         const Wcns5zPrim<LocalSystem>& rhs);\n\n  template <typename LocalSystem>\n  friend bool operator!=(const Wcns5zPrim<LocalSystem>& lhs,\n                         const Wcns5zPrim<LocalSystem>& rhs);\n\n  size_t nonlinear_weight_exponent_ = 0;\n  double epsilon_ = std::numeric_limits<double>::signaling_NaN();\n  FallbackReconstructorType fallback_reconstructor_ =\n      FallbackReconstructorType::None;\n  size_t max_number_of_extrema_ = 0;\n  ::VariableFixing::FixReconstructedStateToAtmosphere\n      fix_reconstructed_state_to_atmosphere_{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never};\n  bool reconstruct_rho_times_temperature_{false};\n\n  void (*reconstruct_)(gsl::not_null<std::array<gsl::span<double>, dim>*>,\n                       gsl::not_null<std::array<gsl::span<double>, dim>*>,\n                       const gsl::span<const double>&,\n                       const DirectionMap<dim, gsl::span<const double>>&,\n                       const Index<dim>&, size_t, double, size_t) = nullptr;\n  void (*reconstruct_lower_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<dim>&, const Index<dim>&,\n                                      const Direction<dim>&, const double&,\n                                      const size_t&) = nullptr;\n  void (*reconstruct_upper_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<dim>&, const Index<dim>&,\n                                      const Direction<dim>&, const double&,\n                                      const size_t&) = nullptr;\n};\n}  // namespace grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                            \\\n  template class Events::ObserveTimeStep<                 \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>; \\\n  template class dg::Events::ObserveTimeStepVolume<       \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (RadiationTransport::NoNeutrinos::System))\n\n#undef INSTANTIATION\n#undef NEUTRINO\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                                              \\\n  template class ChangeTimeStepperOrder<                                    \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>;                   \\\n  template class CleanHistory<                                              \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>;                   \\\n  template class RecordTimeStepperData<                                     \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>;                   \\\n  template class UpdateU<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>, \\\n                         false>;                                            \\\n  template class UpdateU<grmhd::GhValenciaDivClean::System<NEUTRINO(data)>, \\\n                         true>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (RadiationTransport::NoNeutrinos::System))\n\n#undef INSTANTIATION\n#undef NEUTRINO\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <optional>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void evolution::dg::Actions::detail::volume_terms<                  \\\n      ::grmhd::GhValenciaDivClean::TimeDerivativeTerms>(                       \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                          \\\n          ::Tags::dt, typename ::grmhd::GhValenciaDivClean::System<NEUTRINO(   \\\n                          data)>::variables_tag::tags_list>>*>                 \\\n          dt_vars_ptr,                                                         \\\n      const gsl::not_null<Variables<                                           \\\n          db::wrap_tags_in<::Tags::Flux,                                       \\\n                           typename ::grmhd::GhValenciaDivClean::System<       \\\n                               NEUTRINO(data)>::flux_variables,                \\\n                           tmpl::size_t<3>, Frame::Inertial>>*>                \\\n          volume_fluxes,                                                       \\\n      const gsl::not_null<Variables<                                           \\\n          db::wrap_tags_in<::Tags::deriv,                                      \\\n                           typename ::grmhd::GhValenciaDivClean::System<       \\\n                               NEUTRINO(data)>::gradient_variables,            \\\n                           tmpl::size_t<3>, Frame::Inertial>>*>                \\\n          partial_derivs,                                                      \\\n      const gsl::not_null<                                                     \\\n          Variables<typename ::grmhd::GhValenciaDivClean::System<NEUTRINO(     \\\n              data)>::compute_volume_time_derivative_terms::temporary_tags>*>  \\\n          temporaries,                                                         \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                          \\\n          ::Tags::div,                                                         \\\n          db::wrap_tags_in<::Tags::Flux,                                       \\\n                           typename ::grmhd::GhValenciaDivClean::System<       \\\n                               NEUTRINO(data)>::flux_variables,                \\\n                           tmpl::size_t<3>, Frame::Inertial>>>*>               \\\n          div_fluxes,                                                          \\\n      const Variables<typename ::grmhd::GhValenciaDivClean::System<NEUTRINO(   \\\n          data)>::variables_tag::tags_list>& evolved_vars,                     \\\n      const ::dg::Formulation dg_formulation, const Mesh<3>& mesh,             \\\n      [[maybe_unused]] const tnsr::I<DataVector, 3, Frame::Inertial>&          \\\n          inertial_coordinates,                                                \\\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,              \\\n                            Frame::Inertial>&                                  \\\n          logical_to_inertial_inverse_jacobian,                                \\\n      [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,   \\\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&            \\\n          mesh_velocity,                                                       \\\n      const std::optional<Scalar<DataVector>>& div_mesh_velocity,              \\\n      const tnsr::aa<DataVector, 3>& spacetime_metric,                         \\\n      const tnsr::aa<DataVector, 3>& pi, const tnsr::iaa<DataVector, 3>& phi,  \\\n      const Scalar<DataVector>& gamma0, const Scalar<DataVector>& gamma1,      \\\n      const Scalar<DataVector>& gamma2,                                        \\\n      const ::gh::gauges::GaugeCondition& gauge_condition,                     \\\n      const Mesh<3>& mesh_for_rhs, const double& time,                         \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,          \\\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,              \\\n                            Frame::Inertial>& inverse_jacobian,                \\\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&            \\\n          mesh_velocity_gh,                                                    \\\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,   \\\n      const Scalar<DataVector>& tilde_tau,                                     \\\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,                  \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,                  \\\n      const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& pressure, \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,         \\\n      const Scalar<DataVector>& lorentz_factor,                                \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,           \\\n      const Scalar<DataVector>& rest_mass_density,                             \\\n      const Scalar<DataVector>& electron_fraction,                             \\\n      const Scalar<DataVector>& specific_internal_energy,                      \\\n      const double& constraint_damping_parameter,                              \\\n      const ::VariableFixing::FixToAtmosphere<3>& fix_to_atmosphere);          \\\n  INSTANTIATE_PARTIAL_DERIVATIVES_WITH_SYSTEM(                                 \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>, 3, Frame::Inertial)\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (RadiationTransport::NoNeutrinos::System))\n\n#undef INSTANTIATION\n#undef NEUTRINO\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Particles/MonteCarlo/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n\nusing neutrino_species_elec = tmpl::list<neutrinos::ElectronNeutrinos<1>>;\nusing neutrino_species_elec_anti =\n    tmpl::list<neutrinos::ElectronNeutrinos<1>,\n               neutrinos::ElectronAntiNeutrinos<1>>;\n\n#define GHMHD_NEUTRINOS                                                    \\\n  (RadiationTransport::NoNeutrinos::System, Particles::MonteCarlo::System, \\\n   RadiationTransport::M1Grey::System<neutrino_species_elec>,              \\\n   RadiationTransport::M1Grey::System<neutrino_species_elec_anti>)\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/SetPiAndPhiFromConstraints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean {\n/*!\n * \\brief Set \\f$\\Pi_{ab}\\f$ from the gauge source function.\n *\n * This is necessary to ensure the initial data is in the desired evolution\n * gauge.\n */\nstruct SetPiAndPhiFromConstraints {\n  using const_global_cache_tags =\n      typename gh::gauges::SetPiAndPhiFromConstraints<\n          ghmhd::GhValenciaDivClean::InitialData::\n              analytic_solutions_and_data_list,\n          3>::const_global_cache_tags;\n  using mutable_global_cache_tags =\n      typename gh::gauges::SetPiAndPhiFromConstraints<\n          ghmhd::GhValenciaDivClean::InitialData::\n              analytic_solutions_and_data_list,\n          3>::mutable_global_cache_tags;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<\n        tmpl::list<gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>,\n        tmpl::list<\n            ::Tags::Time, domain::Tags::Mesh<3>,\n            domain::Tags::ElementMap<3, Frame::Grid>,\n            domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                        Frame::Inertial>,\n            domain::Tags::FunctionsOfTime,\n            domain::Tags::Coordinates<3, Frame::ElementLogical>,\n            gr::Tags::SpacetimeMetric<DataVector, 3>,\n            gh::gauges::Tags::GaugeCondition,\n            evolution::dg::subcell::Tags::Mesh<3>,\n            evolution::dg::subcell::Tags::Coordinates<3, Frame::ElementLogical>,\n            evolution::dg::subcell::Tags::ActiveGrid>>(\n        [](const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n           const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n           const double initial_time, const Mesh<3>& dg_mesh,\n           const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n           const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n               grid_to_inertial_map,\n           const std::unordered_map<\n               std::string,\n               std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n               functions_of_time,\n           const tnsr::I<DataVector, 3, Frame::ElementLogical>&\n               dg_logical_coordinates,\n           const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n           const gh::gauges::GaugeCondition& gauge_condition,\n           const Mesh<3>& subcell_mesh,\n           const tnsr::I<DataVector, 3, Frame::ElementLogical>&\n               subcell_logical_coordinates,\n           const evolution::dg::subcell::ActiveGrid active_grid,\n           const bool set_pi_and_phi_from_constraints) {\n          if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n            gh::gauges::SetPiAndPhiFromConstraints<\n                ghmhd::GhValenciaDivClean::InitialData::\n                    analytic_solutions_and_data_list,\n                3>::impl(pi, phi, initial_time, dg_mesh, logical_to_grid_map,\n                         grid_to_inertial_map, functions_of_time,\n                         dg_logical_coordinates, spacetime_metric,\n                         gauge_condition, set_pi_and_phi_from_constraints);\n          } else {\n            gh::gauges::SetPiAndPhiFromConstraints<\n                ghmhd::GhValenciaDivClean::InitialData::\n                    analytic_solutions_and_data_list,\n                3>::impl(pi, phi, initial_time, subcell_mesh,\n                         logical_to_grid_map, grid_to_inertial_map,\n                         functions_of_time, subcell_logical_coordinates,\n                         spacetime_metric, gauge_condition,\n                         set_pi_and_phi_from_constraints);\n          }\n        },\n        make_not_null(&box),\n        Parallel::get<gh::Tags::SetPiAndPhiFromConstraints>(cache));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/ComovingMagneticField.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\n\nnamespace {\n// spatial components of the four-velocity are:\n// u^i = W(v^i - \\beta^i / \\alpha)\n// so the down-index spatial components are\n// u_i = W v_i\n//\n// u_0 = - \\alpha W + \\beta^i u_i\nvoid four_velocity_one_form(\n    const gsl::not_null<tnsr::a<DataVector, 3>*> four_velocity_one_form_result,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& spatial_velocity_one_form,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& lapse) {\n  get<0>(*four_velocity_one_form_result) = -get(lapse) * get(lorentz_factor);\n  for (size_t i = 0; i < 3; ++i) {\n    four_velocity_one_form_result->get(i + 1) =\n        get(lorentz_factor) * spatial_velocity_one_form.get(i);\n    get<0>(*four_velocity_one_form_result) +=\n        shift.get(i) * four_velocity_one_form_result->get(i + 1);\n  }\n}\n}  // namespace\n\nvoid add_stress_energy_term_to_dt_pi(\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> dt_pi,\n    const tnsr::aa<DataVector, 3>& trace_reversed_stress_energy,\n    const Scalar<DataVector>& lapse) {\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      dt_pi->get(a, b) -=\n          16.0 * M_PI * get(lapse) * trace_reversed_stress_energy.get(a, b);\n    }\n  }\n}\n\nvoid trace_reversed_stress_energy(\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> stress_energy,\n    const gsl::not_null<tnsr::a<DataVector, 3>*> four_velocity_one_form_buffer,\n    const gsl::not_null<tnsr::a<DataVector, 3>*>\n        comoving_magnetic_field_one_form_buffer,\n    const Scalar<DataVector>& rest_mass_density,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& spatial_velocity_one_form,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& magnetic_field_one_form,\n    const Scalar<DataVector>& magnetic_field_squared,\n    const Scalar<DataVector>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const Scalar<DataVector>& one_over_w_squared,\n    const Scalar<DataVector>& pressure,\n    const Scalar<DataVector>& specific_internal_energy,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& lapse) {\n  four_velocity_one_form(four_velocity_one_form_buffer,\n                         spatial_velocity_one_form, lorentz_factor, shift,\n                         lapse);\n\n  // 3,3 component will be the last component assigned, so we can use it as a\n  // temporary buffer\n  auto& modified_enthalpy_times_rest_mass = get<3, 3>(*stress_energy);\n  modified_enthalpy_times_rest_mass =\n      get(rest_mass_density) * (1.0 + get(specific_internal_energy)) +\n      get(pressure) + square(get(magnetic_field_dot_spatial_velocity)) +\n      get(magnetic_field_squared) * get(one_over_w_squared);\n\n  hydro::comoving_magnetic_field_one_form(\n      comoving_magnetic_field_one_form_buffer, spatial_velocity_one_form,\n      magnetic_field_one_form, magnetic_field_dot_spatial_velocity,\n      lorentz_factor, shift, lapse);\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      stress_energy->get(a, b) =\n          modified_enthalpy_times_rest_mass *\n              four_velocity_one_form_buffer->get(a) *\n              four_velocity_one_form_buffer->get(b) +\n          (0.5 * modified_enthalpy_times_rest_mass - get(pressure)) *\n              spacetime_metric.get(a, b) -\n          comoving_magnetic_field_one_form_buffer->get(a) *\n              comoving_magnetic_field_one_form_buffer->get(b);\n    }\n  }\n}\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\n\n/*!\n * \\brief Add in the trace-reversed stress-energy source term to the \\f$\\Pi\\f$\n * evolved variable of the Generalized Harmonic system.\n *\n * \\details The only stress energy source term in the Generalized Harmonic\n * evolution equations is in the equation for \\f$\\Pi_{a b}\\f$:\n *\n * \\f[\n * \\partial_t \\Pi_{ab} + \\text{(spatial derivative terms)} =\n * \\text{(GH source terms)}\n * - 16 \\pi \\alpha (T_{ab} - \\frac{1}{2} g_{a b} T^c{}_c)\n * \\f]\n *\n * (note that this function takes as argument the trace-reversed stress energy\n * tensor)\n *\n * This function adds that contribution to the existing value of `dt_pi`. The\n * spacetime terms in the GH equation should be computed before passing the\n * `dt_pi` to this function for updating.\n *\n * \\see `gh::TimeDerivative` for details about the spacetime\n * part of the time derivative calculation.\n */\nvoid add_stress_energy_term_to_dt_pi(\n    gsl::not_null<tnsr::aa<DataVector, 3>*> dt_pi,\n    const tnsr::aa<DataVector, 3>& trace_reversed_stress_energy,\n    const Scalar<DataVector>& lapse);\n\n/*!\n * \\brief Calculate the trace-reversed stress-energy tensor \\f$(T_{\\mu \\nu} -\n * 1/2 g_{\\mu \\nu} g^{\\lambda \\sigma} T_{\\lambda \\sigma}) \\f$ associated with\n * the matter part of the GRMHD system.\n *\n * \\details The stress energy tensor is needed to compute the backreaction of\n * the matter to the spacetime degrees of freedom. The stress energy is\n * calculated as described in \\cite Moesta2013dna :\n *\n * \\f[\n * T_{\\mu \\nu} = \\rho h^* u_\\mu u_\\nu + p^* g_{\\mu \\nu} - b_\\mu b_\\nu,\n * \\f]\n *\n * where \\f$u_\\mu\\f$ is the four-velocity, \\f$\\rho\\f$ is the rest mass density,\n * \\f$h^*\\f$ is the magnetically modified enthalpy, \\f$p^*\\f$ is the\n * magnetically modified pressure, and \\f$b_{\\mu}\\f$ is the comoving magnetic\n * field (note that we deviate from the notation of \\cite Moesta2013dna by\n * denoting the pressure with a lower-case \\f$p\\f$ instead of an upper-case\n * \\f$P\\f$).\n *\n * The spatial components of the four velocity \\f$u_\\mu\\f$ are\n *\n * \\f[\n * u_i = W v_i,\n * \\f]\n *\n * and the time component is\n *\n * \\f[\n * u_0 = - \\alpha W + \\beta^i u_i.\n * \\f].\n *\n * The magnetically modified enthalpy is\n *\n * \\f[\n * h^* = 1 + \\epsilon + (p + b^2) / \\rho.\n * \\f]\n *\n * The magnetically modified pressure is\n *\n * \\f[\n * p^* = p + b^2 / 2.\n * \\f]\n *\n *\n * The comoving magnetic field is computed via\n *\n * \\f{align}{\n * b_i &= B_i / W + v_i W v^k B_k\\\\\n * b_0 &= - \\alpha W v^i B_i + \\beta^i b_i\n * \\f}\n *\n * Therefore, the trace-reversed stress energy simplifies to\n *\n * \\f[\n * (T_{\\mu \\nu} - \\frac{1}{2} g_{\\mu \\nu} g^{\\lambda \\sigma} T_{\\lambda \\sigma})\n * = \\rho h^* u_\\mu u_\\nu  + \\left(\\frac{1}{2} \\rho h^* - p\\right) g_{\\mu \\nu}\n *  - b_\\mu b_\\nu\n * \\f]\n */\nvoid trace_reversed_stress_energy(\n    gsl::not_null<tnsr::aa<DataVector, 3>*> stress_energy,\n    gsl::not_null<tnsr::a<DataVector, 3>*> four_velocity_one_form_buffer,\n    gsl::not_null<tnsr::a<DataVector, 3>*>\n        comoving_magnetic_field_one_form_buffer,\n    const Scalar<DataVector>& rest_mass_density,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& spatial_velocity_one_form,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& magnetic_field_one_form,\n    const Scalar<DataVector>& magnetic_field_squared,\n    const Scalar<DataVector>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const Scalar<DataVector>& one_over_w_squared,\n    const Scalar<DataVector>& pressure,\n    const Scalar<DataVector>& specific_internal_energy,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& lapse);\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FixConservativesAndComputePrims.cpp\n  NeighborPackagedData.cpp\n  PrimitiveGhostData.cpp\n  PrimsAfterRollback.cpp\n  ResizeAndComputePrimitives.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FixConservativesAndComputePrims.hpp\n  NeighborPackagedData.hpp\n  PrimitiveGhostData.hpp\n  PrimsAfterRollback.hpp\n  ResizeAndComputePrimitives.hpp\n  Subcell.hpp\n  TciOnDgGrid.hpp\n  TciOnFdGrid.hpp\n  TimeDerivative.hpp\n  ZeroTimeDerivatives.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/FixConservativesAndComputePrims.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/FixConservativesAndComputePrims.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::subcell {\ntemplate <typename OrderedListOfRecoverySchemes, typename System>\nvoid FixConservativesAndComputePrims<OrderedListOfRecoverySchemes, System>::\n    apply(const gsl::not_null<bool*> needed_fixing,\n          const gsl::not_null<typename System::variables_tag::type*>\n              conserved_vars_ptr,\n          const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*>\n              primitive_vars_ptr,\n          const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_coords,\n          const grmhd::ValenciaDivClean::FixConservatives& fix_conservatives,\n          const EquationsOfState::EquationOfState<true, 3>& eos,\n          const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n              primitive_from_conservative_options) {\n  CAPTURE_FOR_ERROR(subcell_coords);\n  const auto& cons_vars = *conserved_vars_ptr;\n  CAPTURE_FOR_ERROR(cons_vars);\n  // Compute the spatial metric, inverse spatial metric, and sqrt{det{spatial\n  // metric}}. Storing the allocation or the result in the DataBox might\n  // actually be useful here, but unclear. We will need to profile.\n  Variables<tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                       gr::Tags::DetSpatialMetric<DataVector>,\n                       gr::Tags::SqrtDetSpatialMetric<DataVector>>>\n      temp_buffer{conserved_vars_ptr->number_of_grid_points()};\n  const tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      make_const_view(\n          make_not_null(&spatial_metric.get(i, j)),\n          get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*conserved_vars_ptr)\n              .get(i + 1, j + 1),\n          0, temp_buffer.number_of_grid_points());\n    }\n  }\n  auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp_buffer);\n  auto& det_spatial_metric =\n      get<gr::Tags::DetSpatialMetric<DataVector>>(temp_buffer);\n  auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(temp_buffer);\n  determinant_and_inverse(make_not_null(&det_spatial_metric),\n                          make_not_null(&inverse_spatial_metric),\n                          spatial_metric);\n  CAPTURE_FOR_ERROR(det_spatial_metric);\n  get(sqrt_det_spatial_metric) = sqrt(get(det_spatial_metric));\n\n  *needed_fixing = fix_conservatives(\n      make_not_null(&get<ValenciaDivClean::Tags::TildeD>(*conserved_vars_ptr)),\n      make_not_null(&get<ValenciaDivClean::Tags::TildeYe>(*conserved_vars_ptr)),\n      make_not_null(\n          &get<ValenciaDivClean::Tags::TildeTau>(*conserved_vars_ptr)),\n      make_not_null(&get<ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(\n          *conserved_vars_ptr)),\n      get<ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(*conserved_vars_ptr),\n      spatial_metric, inverse_spatial_metric, sqrt_det_spatial_metric);\n  grmhd::ValenciaDivClean::\n      PrimitiveFromConservative<OrderedListOfRecoverySchemes, true>::apply(\n          make_not_null(&get<hydro::Tags::RestMassDensity<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::ElectronFraction<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::MagneticField<DataVector, 3>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::LorentzFactor<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(\n              &get<hydro::Tags::Pressure<DataVector>>(*primitive_vars_ptr)),\n          make_not_null(\n              &get<hydro::Tags::Temperature<DataVector>>(*primitive_vars_ptr)),\n          get<ValenciaDivClean::Tags::TildeD>(*conserved_vars_ptr),\n          get<ValenciaDivClean::Tags::TildeYe>(*conserved_vars_ptr),\n          get<ValenciaDivClean::Tags::TildeTau>(*conserved_vars_ptr),\n          get<ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(\n              *conserved_vars_ptr),\n          get<ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(\n              *conserved_vars_ptr),\n          get<ValenciaDivClean::Tags::TildePhi>(*conserved_vars_ptr),\n          spatial_metric, inverse_spatial_metric, sqrt_det_spatial_metric, eos,\n          primitive_from_conservative_options);\n}\n\nnamespace {\nusing NewmanThenPalenzuela =\n    tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n               ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\nusing KastaunThenNewmanThenPalenzuela =\n    tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n               ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n               ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n}  // namespace\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                     \\\n  template struct FixConservativesAndComputePrims< \\\n      RECOVERY(data), GhValenciaDivClean::System<NEUTRINO(data)>>;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION,\n    (tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>,\n     tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>,\n     tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n     NewmanThenPalenzuela, KastaunThenNewmanThenPalenzuela),\n    GHMHD_NEUTRINOS)\n\n#undef INSTANTIATION\n#undef RECOVERY\n#undef NEUTRINO\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/FixConservativesAndComputePrims.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::subcell {\n/*!\n * \\brief Fix the conservative variables and compute the primitive variables.\n *\n * Sets `ValenciaDivClean::Tags::VariablesNeededFixing` to `true` if the\n * conservative variables needed fixing, otherwise sets the tag to `false`.\n */\ntemplate <typename OrderedListOfRecoverySchemes,\n          typename System>\nstruct FixConservativesAndComputePrims {\n  using return_tags = tmpl::list<\n      ValenciaDivClean::Tags::VariablesNeededFixing,\n      typename System::variables_tag,\n      typename System::primitive_variables_tag>;\n  using argument_tags = tmpl::list<\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n      ::Tags::VariableFixer<grmhd::ValenciaDivClean::FixConservatives>,\n      hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  static void apply(\n      gsl::not_null<bool*> needed_fixing,\n      gsl::not_null<\n          typename System::variables_tag::type*>\n          conserved_vars_ptr,\n      gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*>\n          primitive_vars_ptr,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_coords,\n      const grmhd::ValenciaDivClean::FixConservatives& fix_conservatives,\n      const EquationsOfState::EquationOfState<true, 3>& eos,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/NeighborPackagedData.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.tpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/NeutrinoSystems.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::subcell {\ntemplate <typename System>\nDirectionalIdMap<3, DataVector> NeighborPackagedData<System>::apply(\n    const db::Access& box,\n    const std::vector<DirectionalId<3>>& mortars_to_reconstruct_to) {\n  using evolved_vars_tag = typename System::variables_tag;\n  using evolved_vars_tags = typename evolved_vars_tag::tags_list;\n  using prim_tags = typename System::primitive_variables_tag::tags_list;\n  using recons_prim_tags = tmpl::push_front<tmpl::push_back<\n      prim_tags,\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>>;\n  using fluxes_tags =\n      db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,\n                       tmpl::size_t<3>, Frame::Inertial>;\n  using grmhd_evolved_vars_tag =\n      typename grmhd::ValenciaDivClean::System::variables_tag;\n  using grmhd_evolved_vars_tags = typename grmhd_evolved_vars_tag::tags_list;\n\n  DirectionalIdMap<3, DataVector> neighbor_package_data{};\n  if (mortars_to_reconstruct_to.empty()) {\n    return neighbor_package_data;\n  }\n\n  const auto& ghost_subcell_data =\n      db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box);\n  const Mesh<3>& subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<3>>(box);\n  const Mesh<3>& dg_mesh = db::get<domain::Tags::Mesh<3>>(box);\n  const auto& subcell_options =\n      db::get<evolution::dg::subcell::Tags::SubcellOptions<3>>(box);\n  const auto& evolved_vars = db::get<evolved_vars_tag>(box);\n\n  const Variables<Tags::spacetime_reconstruction_tags> volume_spacetime_vars =\n      evolution::dg::subcell::fd::project(\n          Variables<Tags::spacetime_reconstruction_tags>{\n              // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n              const_cast<double*>(\n                  get<tmpl::front<Tags::spacetime_reconstruction_tags>>(\n                      evolved_vars)[0]\n                      .data()),\n              Variables<Tags::spacetime_reconstruction_tags>::\n                      number_of_independent_components *\n                  evolved_vars.number_of_grid_points()},\n          dg_mesh, subcell_mesh.extents());\n\n  // Note: we need to compare if projecting the entire mesh or only ghost\n  // zones needed is faster. This probably depends on the number of neighbors\n  // we have doing FD.\n  const auto volume_prims = evolution::dg::subcell::fd::project(\n      db::get<typename System::primitive_variables_tag>(box), dg_mesh,\n      subcell_mesh.extents());\n\n  const auto& recons =\n      db::get<grmhd::GhValenciaDivClean::fd::Tags::Reconstructor<System>>(box);\n  const auto& base_boundary_correction =\n      db::get<evolution::Tags::BoundaryCorrection>(box);\n  const auto& fix_to_atmosphere =\n      db::get<::Tags::VariableFixer<::VariableFixing::FixToAtmosphere<3>>>(box);\n  using derived_boundary_corrections = grmhd::GhValenciaDivClean::\n      BoundaryCorrections::standard_boundary_corrections;\n  call_with_dynamic_type<void, derived_boundary_corrections>(\n      &base_boundary_correction,\n      [&box, &dg_mesh, &fix_to_atmosphere, &mortars_to_reconstruct_to,\n       &neighbor_package_data, &ghost_subcell_data, &recons, &subcell_mesh,\n       &subcell_options, &volume_prims,\n       &volume_spacetime_vars](const auto* gh_grmhd_correction) {\n        using DerivedCorrection = std::decay_t<decltype(*gh_grmhd_correction)>;\n        const auto& boundary_correction =\n            dynamic_cast<const DerivedCorrection&>(*gh_grmhd_correction);\n\n        using dg_package_data_temporary_tags =\n            typename DerivedCorrection::dg_package_data_temporary_tags;\n        using dg_package_data_argument_tags =\n            tmpl::append<evolved_vars_tags, recons_prim_tags, fluxes_tags,\n                         tmpl::remove_duplicates<tmpl::push_back<\n                             dg_package_data_temporary_tags,\n                             gr::Tags::SpatialMetric<DataVector, 3>,\n                             gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                             gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                             evolution::dg::Actions::detail::NormalVector<3>>>>;\n\n        const auto& element = db::get<domain::Tags::Element<3>>(box);\n        const auto& eos = get<hydro::Tags::GrmhdEquationOfState>(box);\n\n        using dg_package_field_tags =\n            typename DerivedCorrection::dg_package_field_tags;\n        Variables<dg_package_data_argument_tags> vars_on_face{0};\n        Variables<dg_package_field_tags> packaged_data{0};\n        for (const auto& mortar_id : mortars_to_reconstruct_to) {\n          const Direction<3>& direction = mortar_id.direction();\n\n          const Mesh<2> dg_face_mesh =\n              dg_mesh.slice_away(direction.dimension());\n          Index<3> extents = subcell_mesh.extents();\n          // Switch to face-centered instead of cell-centered points on the\n          // FD. There are num_cell_centered+1 face-centered points.\n          ++extents[direction.dimension()];\n          const Index<2> subcell_face_extents =\n              extents.slice_away(direction.dimension());\n\n          // Computed prims and cons on face via reconstruction\n          const size_t num_face_pts = subcell_mesh.extents()\n                                          .slice_away(direction.dimension())\n                                          .product();\n          vars_on_face.initialize(num_face_pts);\n\n          call_with_dynamic_type<\n              void, typename grmhd::GhValenciaDivClean::fd::Reconstructor<\n                        System>::creatable_classes>(\n              &recons,\n              [&element, &eos, &fix_to_atmosphere, &mortar_id,\n               &ghost_subcell_data, &subcell_mesh, &vars_on_face, &volume_prims,\n               &volume_spacetime_vars](const auto& reconstructor) {\n                reconstructor->reconstruct_fd_neighbor(\n                    make_not_null(&vars_on_face), volume_prims,\n                    volume_spacetime_vars, eos, element, ghost_subcell_data,\n                    subcell_mesh, fix_to_atmosphere, mortar_id.direction());\n              });\n\n          // Get the mesh velocity if needed\n          const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>\n              mesh_velocity_dg = db::get<domain::Tags::MeshVelocity<3>>(box);\n          std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>\n              mesh_velocity_on_subcell_face = {};\n          if (mesh_velocity_dg.has_value()) {\n            // Slice data on current face\n            tnsr::I<DataVector, 3, Frame::Inertial> mesh_velocity_on_dg_face =\n                data_on_slice(\n                    mesh_velocity_dg.value(), dg_mesh.extents(),\n                    direction.dimension(),\n                    direction.side() == Side::Lower\n                        ? 0\n                        : (dg_mesh.extents(direction.dimension()) - 1));\n\n            mesh_velocity_on_subcell_face =\n                tnsr::I<DataVector, 3, Frame::Inertial>{num_face_pts};\n\n            for (size_t i = 0; i < 3; i++) {\n              evolution::dg::subcell::fd::project(\n                  make_not_null(&mesh_velocity_on_subcell_face.value().get(i)),\n                  mesh_velocity_on_dg_face.get(i), dg_face_mesh,\n                  subcell_face_extents);\n            }\n          }\n\n          grmhd::ValenciaDivClean::subcell::compute_fluxes(\n              make_not_null(&vars_on_face));\n\n          if (mesh_velocity_on_subcell_face.has_value()) {\n            tmpl::for_each<grmhd_evolved_vars_tags>(\n                [&vars_on_face, &mesh_velocity_on_subcell_face](auto tag_v) {\n                  using tag = tmpl::type_from<decltype(tag_v)>;\n                  using flux_tag =\n                      ::Tags::Flux<tag, tmpl::size_t<3>, Frame::Inertial>;\n                  using FluxTensor = typename flux_tag::type;\n                  const auto& var = get<tag>(vars_on_face);\n                  auto& flux = get<flux_tag>(vars_on_face);\n                  for (size_t storage_index = 0; storage_index < var.size();\n                       ++storage_index) {\n                    const auto tensor_index =\n                        var.get_tensor_index(storage_index);\n                    for (size_t j = 0; j < 3; j++) {\n                      const auto flux_storage_index =\n                          FluxTensor::get_storage_index(\n                              prepend(tensor_index, j));\n                      flux[flux_storage_index] -=\n                          mesh_velocity_on_subcell_face.value().get(j) *\n                          var[storage_index];\n                    }\n                  }\n                });\n          }\n\n          // Copy over Gamma1 and Gamma2. Future considerations:\n          // - For LTS do we instead just recompute it since the times might\n          //   not be aligned?\n          // - Instead of slicing, can we copy the data from the local\n          //   packaged data?\n          {\n            Scalar<DataVector> gamma_on_dg_face = data_on_slice(\n                get<gh::Tags::ConstraintGamma1>(box), dg_mesh.extents(),\n                direction.dimension(),\n                direction.side() == Side::Lower\n                    ? 0\n                    : (dg_mesh.extents(direction.dimension()) - 1));\n            evolution::dg::subcell::fd::project(\n                make_not_null(\n                    &get(get<gh::Tags::ConstraintGamma1>(vars_on_face))),\n                get(gamma_on_dg_face), dg_face_mesh, subcell_face_extents);\n            data_on_slice(make_not_null(&gamma_on_dg_face),\n                          get<gh::Tags::ConstraintGamma2>(box),\n                          dg_mesh.extents(), direction.dimension(),\n                          direction.side() == Side::Lower\n                              ? 0\n                              : (dg_mesh.extents(direction.dimension()) - 1));\n            evolution::dg::subcell::fd::project(\n                make_not_null(\n                    &get(get<gh::Tags::ConstraintGamma2>(vars_on_face))),\n                get(gamma_on_dg_face), dg_face_mesh, subcell_face_extents);\n          }\n\n          // Compute the neighbor's normal covector and normalize it.\n          tnsr::i<DataVector, 3, Frame::Inertial> normal_covector =\n              get<evolution::dg::Tags::NormalCovector<3>>(\n                  *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<3>>(\n                       box)\n                       .at(mortar_id.direction()));\n          for (auto& t : normal_covector) {\n            t *= -1.0;\n          }\n          // Note: Only need to do the projection in 2d and 3d, but GRMHD is\n          // always 3d currently.\n          for (size_t i = 0; i < 3; ++i) {\n            normal_covector.get(i) = evolution::dg::subcell::fd::project(\n                normal_covector.get(i),\n                dg_mesh.slice_away(mortar_id.direction().dimension()),\n                subcell_mesh.extents().slice_away(\n                    mortar_id.direction().dimension()));\n          }\n          // Need to renormalize the normal vector with the neighbor's\n          // inverse spatial metric.\n          const auto& inverse_spatial_metric =\n              get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(vars_on_face);\n          const auto normal_magnitude =\n              magnitude(normal_covector, inverse_spatial_metric);\n          for (size_t i = 0; i < 3; ++i) {\n            normal_covector.get(i) /= get(normal_magnitude);\n          }\n\n          auto& normal_vector =\n              get<evolution::dg::Actions::detail::NormalVector<3>>(\n                  vars_on_face);\n          for (size_t i = 0; i < 3; ++i) {\n            normal_vector.get(i) =\n                normal_covector.get(0) * inverse_spatial_metric.get(i, 0) +\n                normal_covector.get(1) * inverse_spatial_metric.get(i, 1) +\n                normal_covector.get(2) * inverse_spatial_metric.get(i, 2);\n          }\n\n          // Compute the packaged data\n          packaged_data.initialize(num_face_pts);\n          using dg_package_data_projected_tags = tmpl::append<\n              evolved_vars_tags, fluxes_tags, dg_package_data_temporary_tags,\n              typename DerivedCorrection::dg_package_data_primitive_tags>;\n          evolution::dg::Actions::detail::dg_package_data<System>(\n              make_not_null(&packaged_data),\n              dynamic_cast<const DerivedCorrection&>(boundary_correction),\n              vars_on_face, normal_covector, mesh_velocity_on_subcell_face, box,\n              typename DerivedCorrection::dg_package_data_volume_tags{},\n              dg_package_data_projected_tags{});\n\n          // Reconstruct the DG solution.\n          // Really we should be solving the boundary correction and\n          // then reconstructing, but away from a shock this doesn't\n          // matter.\n          DataVector dg_data{\n              Variables<\n                  dg_package_field_tags>::number_of_independent_components *\n              dg_face_mesh.number_of_grid_points()};\n          Variables<dg_package_field_tags> dg_packaged_data{dg_data.data(),\n                                                            dg_data.size()};\n          evolution::dg::subcell::fd::reconstruct(\n              make_not_null(&dg_packaged_data), packaged_data, dg_face_mesh,\n              subcell_mesh.extents().slice_away(\n                  mortar_id.direction().dimension()),\n              subcell_options.reconstruction_method());\n\n          neighbor_package_data[mortar_id] = std::move(dg_data);\n        }\n      });\n\n  return neighbor_package_data;\n}\n\n#define NEUTRINO(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)          \\\n  template struct NeighborPackagedData< \\\n      grmhd::GhValenciaDivClean::System<NEUTRINO(data)>>;\nGENERATE_INSTANTIATIONS(INSTANTIATION, GHMHD_NEUTRINOS)\n#undef INSTANTIATION\n#undef NEUTRINO\n}  // namespace grmhd::GhValenciaDivClean::subcell\n\ntemplate void evolution::dg::subcell::neighbor_reconstructed_face_solution<\n    3, grmhd::GhValenciaDivClean::subcell::NeighborPackagedData<\n           grmhd::GhValenciaDivClean::System<\n               RadiationTransport::NoNeutrinos::System>>>(\n    gsl::not_null<db::Access*> box);\n\ntemplate void evolution::dg::subcell::neighbor_reconstructed_face_solution<\n    3, grmhd::GhValenciaDivClean::subcell::NeighborPackagedData<\n           grmhd::GhValenciaDivClean::System<Particles::MonteCarlo::System>>>(\n    gsl::not_null<db::Access*> box);\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/NeighborPackagedData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n/// \\cond\nnamespace db {\nclass Access;\n}  // namespace db\nclass DataVector;\ntemplate <size_t VolumeDim>\nstruct DirectionalId;\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::subcell {\n/*!\n * \\brief On elements using DG, reconstructs the interface data from a\n * neighboring element doing subcell.\n *\n * The neighbor's packaged data needed by the boundary correction is computed\n * and returned so that it can be used for solving the Riemann problem on the\n * interfaces.\n *\n * Note that for strict conservation the Riemann solve should be done on the\n * subcells, with the correction being projected back to the DG interface.\n * However, in practice such strict conservation doesn't seem to be necessary\n * and can be explained by that we only need strict conservation at shocks, and\n * if one element is doing DG, then we aren't at a shock.\n *\n * Developer note: For performance reasons We should consider storing the mesh\n * velocity on the faces instead of re-slicing/projecting.\n */\ntemplate <typename System>\nstruct NeighborPackagedData {\n  static DirectionalIdMap<3, DataVector> apply(\n      const db::Access& box,\n      const std::vector<DirectionalId<3>>& mortars_to_reconstruct_to);\n};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimitiveGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimitiveGhostData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::subcell {\nDataVector PrimitiveGhostVariables::apply(\n    const Variables<hydro::grmhd_tags<DataVector>>& prims,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& phi,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& pi,\n    const size_t rdmp_size) {\n  DataVector buffer{\n      prims.number_of_grid_points() *\n          Variables<tags_for_reconstruction>::number_of_independent_components +\n      rdmp_size};\n  Variables<tags_for_reconstruction> vars_to_reconstruct(\n      buffer.data(), buffer.size() - rdmp_size);\n  get<hydro::Tags::RestMassDensity<DataVector>>(vars_to_reconstruct) =\n      get<hydro::Tags::RestMassDensity<DataVector>>(prims);\n  get<hydro::Tags::ElectronFraction<DataVector>>(vars_to_reconstruct) =\n      get<hydro::Tags::ElectronFraction<DataVector>>(prims);\n  get<hydro::Tags::Temperature<DataVector>>(vars_to_reconstruct) =\n      get<hydro::Tags::Temperature<DataVector>>(prims);\n  get<hydro::Tags::MagneticField<DataVector, 3>>(vars_to_reconstruct) =\n      get<hydro::Tags::MagneticField<DataVector, 3>>(prims);\n  get<hydro::Tags::DivergenceCleaningField<DataVector>>(vars_to_reconstruct) =\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(prims);\n\n  auto& lorentz_factor_time_spatial_velocity =\n      get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n          vars_to_reconstruct) =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prims);\n  for (size_t i = 0; i < 3; ++i) {\n    lorentz_factor_time_spatial_velocity.get(i) *=\n        get(get<hydro::Tags::LorentzFactor<DataVector>>(prims));\n  }\n  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(vars_to_reconstruct) =\n      spacetime_metric;\n  get<gh::Tags::Phi<DataVector, 3>>(vars_to_reconstruct) = phi;\n  get<gh::Tags::Pi<DataVector, 3>>(vars_to_reconstruct) = pi;\n  return buffer;\n}\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimitiveGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename T>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::subcell {\n/*!\n * \\brief Computes the rest mass density \\f$\\rho\\f$, electron fraction\n * \\f$Y_e\\f$, pressure \\f$p\\f$, Lorentz factor times the spatial velocity \\f$W\n * v^i\\f$, magnetic field \\f$B^i\\f$, the divergence cleaning field \\f$\\Phi\\f$,\n * and the generalized harmonic evolved variables \\f$g_{ab}\\f$, \\f$\\Phi_{iab}\\f$\n * and \\f$\\Pi_{ab}\\f$ on the subcells so they can be used for reconstruction.\n *\n * The computation copies the data from the primitive variables to a new\n * Variables and computes \\f$W v^i\\f$. In the future we will likely want to\n * elide this copy but that requires support from the actions.\n *\n * This mutator is passed to\n * `evolution::dg::subcell::Actions::SendDataForReconstruction`.\n *\n * \\note Only called on elements using FD.\n */\nclass PrimitiveGhostVariables {\n private:\n  using tags_for_reconstruction = GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                 gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Phi<DataVector, 3>, gh::Tags::Pi<DataVector, 3>>;\n\n  static DataVector apply(\n      const Variables<hydro::grmhd_tags<DataVector>>& prims,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& phi,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& pi, size_t rdmp_size);\n};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimsAfterRollback.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimsAfterRollback.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::subcell {\ntemplate <typename OrderedListOfRecoverySchemes>\nvoid PrimsAfterRollback<OrderedListOfRecoverySchemes>::apply(\n    const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> prim_vars,\n    const bool did_rollback, const Mesh<3>& dg_mesh,\n    const Mesh<3>& subcell_mesh, const Scalar<DataVector>& tilde_d,\n    const Scalar<DataVector>& tilde_ye, const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n    const EquationsOfState::EquationOfState<true, 3>& eos,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options) {\n  if (did_rollback) {\n    const size_t num_grid_points = subcell_mesh.number_of_grid_points();\n    ASSERT(prim_vars->number_of_grid_points() != num_grid_points,\n           \"Doing rollback and the prim_vars should not be the size of the \"\n           \"subcell mesh\");\n    // Project a copy of the pressure from the DG grid to the FD grid to give\n    // high-order initial guess for prim recovery.\n    const Scalar<DataVector> dg_pressure =\n        get<hydro::Tags::Pressure<DataVector>>(*prim_vars);\n    prim_vars->initialize(num_grid_points);\n    evolution::dg::subcell::fd::project(\n        make_not_null(&get(get<hydro::Tags::Pressure<DataVector>>(*prim_vars))),\n        get(dg_pressure), dg_mesh, subcell_mesh.extents());\n\n    // Compute the spatial metric, inverse spatial metric, and sqrt{det{spatial\n    // metric}} on the subcells since we need these for the prim recovery.\n    //\n    // Note: This same computation needs to be done in the time derivative\n    // computation, so it is more efficient to move the code there in the long\n    // term, but that likely requires more refactoring anyway.\n    Variables<tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>>>\n        temp_buffer{num_grid_points};\n    auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(temp_buffer);\n    gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n    auto& inverse_spatial_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp_buffer);\n    auto& sqrt_det_spatial_metric =\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(temp_buffer);\n    determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                            make_not_null(&inverse_spatial_metric),\n                            spatial_metric);\n    get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n    grmhd::ValenciaDivClean::\n        PrimitiveFromConservative<OrderedListOfRecoverySchemes, true>::apply(\n            make_not_null(\n                &get<hydro::Tags::RestMassDensity<DataVector>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::ElectronFraction<DataVector>>(*prim_vars)),\n            make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                *prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::SpatialVelocity<DataVector, 3>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::MagneticField<DataVector, 3>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n                    *prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::LorentzFactor<DataVector>>(*prim_vars)),\n            make_not_null(&get<hydro::Tags::Pressure<DataVector>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::Temperature<DataVector>>(*prim_vars)),\n            tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n            spatial_metric, inverse_spatial_metric, sqrt_det_spatial_metric,\n            eos, primitive_from_conservative_options);\n  }\n}\n\nnamespace {\nusing NewmanThenPalenzuela =\n    tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n               ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\nusing KastaunThenNewmanThenPalenzuela =\n    tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n               ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n               ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n}  // namespace\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                                \\\n  template void PrimsAfterRollback<RECOVERY(data)>::apply(                    \\\n      const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*>          \\\n          prim_vars,                                                          \\\n      bool did_rollback, const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh, \\\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,  \\\n      const Scalar<DataVector>& tilde_tau,                                    \\\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,                 \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,                 \\\n      const Scalar<DataVector>& tilde_phi,                                    \\\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,       \\\n      const EquationsOfState::EquationOfState<true, 3>& eos,                  \\\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&        \\\n          primitive_from_conservative_options);\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION,\n    (tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>,\n     tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>,\n     tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n     NewmanThenPalenzuela, KastaunThenNewmanThenPalenzuela))\n#undef INSTANTIATION\n#undef THERMO_DIM\n#undef RECOVERY\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimsAfterRollback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::subcell {\n/*!\n * \\brief Mutator that resizes the primitive variables to the subcell mesh and\n * computes the primitives, but only if\n * `evolution::dg::subcell::Tags::DidRollback` is `true`.\n *\n * In the DG-subcell `step_actions` list this will normally be called using the\n * `::Actions::MutateApply` action in the following way in the action list:\n * - `Actions::Label<Labels::BeginSubcellAfterDgRollback>`\n * - `Actions::MutateApply<PrimsAfterRollback<primitive_recovery_schemes>>`\n */\ntemplate <typename OrderedListOfRecoverySchemes>\nstruct PrimsAfterRollback {\n  using return_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>>;\n  using argument_tags = tmpl::list<\n      evolution::dg::subcell::Tags::DidRollback, domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      grmhd::ValenciaDivClean::Tags::TildeD,\n      grmhd::ValenciaDivClean::Tags::TildeYe,\n      grmhd::ValenciaDivClean::Tags::TildeTau,\n      grmhd::ValenciaDivClean::Tags::TildeS<>,\n      grmhd::ValenciaDivClean::Tags::TildeB<>,\n      grmhd::ValenciaDivClean::Tags::TildePhi,\n      gr::Tags::SpacetimeMetric<DataVector, 3>,\n      hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  static void apply(\n      gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> prim_vars,\n      bool did_rollback, const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n      const EquationsOfState::EquationOfState<true, 3>& eos,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/ResizeAndComputePrimitives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/ResizeAndComputePrimitives.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::subcell {\ntemplate <typename OrderedListOfRecoverySchemes>\nvoid ResizeAndComputePrims<OrderedListOfRecoverySchemes>::apply(\n    const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> prim_vars,\n    const evolution::dg::subcell::ActiveGrid active_grid,\n    const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n    const EquationsOfState::EquationOfState<true, 3>& eos,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    const size_t num_grid_points = dg_mesh.number_of_grid_points();\n    // Reconstruct a copy of the pressure from the FD grid to the DG grid to\n    // provide a high-order initial guess.\n    const Scalar<DataVector> fd_pressure =\n        get<hydro::Tags::Pressure<DataVector>>(*prim_vars);\n    prim_vars->initialize(num_grid_points);\n    if (get(fd_pressure).size() == subcell_mesh.number_of_grid_points()) {\n      evolution::dg::subcell::fd::reconstruct(\n          make_not_null(\n              &get(get<hydro::Tags::Pressure<DataVector>>(*prim_vars))),\n          get(fd_pressure), dg_mesh, subcell_mesh.extents(),\n          // Always do dim-by-dim reconstruction because it's fast\n          evolution::dg::subcell::fd ::ReconstructionMethod::DimByDim);\n    }\n\n    // Compute the spatial metric, inverse spatial metric, and sqrt{det{spatial\n    // metric}} on the DG grid since we need these for the prim recovery.\n    Variables<tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>>>\n        temp_buffer{num_grid_points};\n    auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(temp_buffer);\n    gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n    auto& inverse_spatial_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp_buffer);\n    auto& sqrt_det_spatial_metric =\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(temp_buffer);\n    determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                            make_not_null(&inverse_spatial_metric),\n                            spatial_metric);\n    get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n    // We only need to compute the prims if we switched to the DG grid because\n    // otherwise we computed the prims during the FD TCI.\n    grmhd::ValenciaDivClean::\n        PrimitiveFromConservative<OrderedListOfRecoverySchemes, true>::apply(\n            make_not_null(\n                &get<hydro::Tags::RestMassDensity<DataVector>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::ElectronFraction<DataVector>>(*prim_vars)),\n            make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                *prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::SpatialVelocity<DataVector, 3>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::MagneticField<DataVector, 3>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n                    *prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::LorentzFactor<DataVector>>(*prim_vars)),\n            make_not_null(&get<hydro::Tags::Pressure<DataVector>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::Temperature<DataVector>>(*prim_vars)),\n            tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n            spatial_metric, inverse_spatial_metric, sqrt_det_spatial_metric,\n            eos, primitive_from_conservative_options);\n  }\n}\n\nnamespace {\nusing NewmanThenPalenzuela =\n    tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n               ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\nusing KastaunThenNewmanThenPalenzuela =\n    tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n               ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n               ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n}  // namespace\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) \\\n  template struct ResizeAndComputePrims<RECOVERY(data)>;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION,\n    (tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>,\n     tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>,\n     tmpl::list<ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n     NewmanThenPalenzuela, KastaunThenNewmanThenPalenzuela))\n\n#undef INSTANTIATION\n#undef RECOVERY\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/ResizeAndComputePrimitives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::subcell {\n/*!\n * \\brief If the grid is switched from subcell to DG, then this mutator resizes\n * the primitive variables to the DG grid and computes the primitive variables\n * on the DG grid.\n *\n * In the DG-subcell `step_actions` list this will normally be called using the\n * `::Actions::MutateApply` action in the following way in the action list:\n * - `TciAndSwitchToDg<TciOnFdGrid>`\n * - `Actions::MutateApply<ResizeAndComputePrims<primitive_recovery_schemes>>`\n *\n * If the active grid is DG (we are switching from subcell back to DG) then this\n * mutator computes the primitive variables on the active grid. We reconstruct\n * the pressure to the DG grid to give a high-order initial guess for the\n * primitive recovery. A possible future optimization would be to avoid this\n * reconstruction when all recovery schemes don't need an initial guess.\n * Finally, we perform the primitive recovery on the DG grid.\n *\n * If the active grid is Subcell then this mutator does nothing.\n *\n * \\note All evolved variables are on the DG grid when this mutator is called\n * and the active grid is DG.\n */\ntemplate <typename OrderedListOfRecoverySchemes>\nstruct ResizeAndComputePrims {\n  using return_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>>;\n  using argument_tags = tmpl::list<\n      evolution::dg::subcell::Tags::ActiveGrid, domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      grmhd::ValenciaDivClean::Tags::TildeD,\n      grmhd::ValenciaDivClean::Tags::TildeYe,\n      grmhd::ValenciaDivClean::Tags::TildeTau,\n      grmhd::ValenciaDivClean::Tags::TildeS<>,\n      grmhd::ValenciaDivClean::Tags::TildeB<>,\n      grmhd::ValenciaDivClean::Tags::TildePhi,\n      gr::Tags::SpacetimeMetric<DataVector, 3>,\n      hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  static void apply(\n      gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> prim_vars,\n      evolution::dg::subcell::ActiveGrid active_grid, const Mesh<3>& dg_mesh,\n      const Mesh<3>& subcell_mesh, const Scalar<DataVector>& tilde_d,\n      const Scalar<DataVector>& tilde_ye, const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n      const EquationsOfState::EquationOfState<true, 3>& eos,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/Subcell.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace grmhd::GhValenciaDivClean {\n/// \\brief Code required by the DG-subcell/FD hybrid solver.\nnamespace subcell {}\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/TciOnDgGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnDgGrid.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::subcell {\n/*!\n * \\brief The troubled-cell indicator run on the DG grid to check if the\n * solution is admissible.\n *\n * See `grmhd::ValenciaDivClean::subcell::TciOnDgGrid` for details.\n */\ntemplate <typename RecoveryScheme>\nstruct TciOnDgGrid\n    : grmhd::ValenciaDivClean::subcell::TciOnDgGrid<RecoveryScheme> {};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/TciOnFdGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnFdGrid.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::subcell {\n/*!\n * \\brief The troubled-cell indicator run on the FD grid to check if the\n * corresponding DG solution is admissible.\n *\n * See `grmhd::ValenciaDivClean::subcell::TciOnFdGrid` for details.\n */\nstruct TciOnFdGrid : grmhd::ValenciaDivClean::subcell::TciOnFdGrid {};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/ComputeBoundaryTerms.hpp\"\n#include \"Evolution/DgSubcell/CorrectPackagedData.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/BoundaryConditionGhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Derivatives.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FilterOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Filters.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Sources.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace grmhd::GhValenciaDivClean::subcell {\nnamespace detail {\ntemplate <class GhDtTagsList, class GhTemporariesList, class GhGradientTagsList,\n          class GhExtraTagsList, class GrmhdDtTagsList,\n          class GrmhdSourceTagsList, class GrmhdArgumentSourceTagsList,\n          typename System>\nstruct ComputeTimeDerivImpl;\n\ntemplate <class... GhDtTags, class... GhTemporaries, class... GhGradientTags,\n          class... GhExtraTags, class... GrmhdDtTags, class... GrmhdSourceTags,\n          class... GrmhdArgumentSourceTags, typename System>\nstruct ComputeTimeDerivImpl<\n    tmpl::list<GhDtTags...>, tmpl::list<GhTemporaries...>,\n    tmpl::list<GhGradientTags...>, tmpl::list<GhExtraTags...>,\n    tmpl::list<GrmhdDtTags...>, tmpl::list<GrmhdSourceTags...>,\n    tmpl::list<GrmhdArgumentSourceTags...>, System> {\n  template <class DbTagsList>\n  static void apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n      const Scalar<DataVector>& cell_centered_det_inv_jacobian,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>&\n          cell_centered_logical_to_inertial_inv_jacobian,\n      const std::array<double, 3>& one_over_delta_xi,\n      const std::array<Variables<tmpl::list<GrmhdDtTags...>>, 3>&\n          boundary_corrections,\n      const Variables<\n          db::wrap_tags_in<::Tags::deriv, typename System::gradients_tags,\n                           tmpl::size_t<3>, Frame::Inertial>>& gh_derivs) {\n    const Mesh<3>& subcell_mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<3>>(*box);\n    const size_t number_of_points = subcell_mesh.number_of_grid_points();\n    // Note: GH+GRMHD tags are always GH,GRMHD\n    using deriv_lapse = ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                      tmpl::size_t<3>, Frame::Inertial>;\n    using deriv_shift = ::Tags::deriv<gr::Tags::Shift<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>;\n    using deriv_spatial_metric =\n        ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>;\n    using extra_tags_for_grmhd =\n        tmpl::list<deriv_lapse, deriv_shift, deriv_spatial_metric,\n                   gr::Tags::ExtrinsicCurvature<DataVector, 3>>;\n    using temporary_tags = tmpl::remove_duplicates<tmpl::append<\n        typename gh::TimeDerivative<ghmhd::GhValenciaDivClean::InitialData::\n                                        analytic_solutions_and_data_list,\n                                    3_st>::temporary_tags,\n        tmpl::push_front<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::\n                             temporary_tags,\n                         ::gh::Tags::ConstraintGamma0>,\n        extra_tags_for_grmhd,\n        tmpl::list<Tags::TraceReversedStressEnergy, Tags::FourVelocityOneForm,\n                   Tags::ComovingMagneticFieldOneForm>>>;\n    Variables<temporary_tags> temp_tags{subcell_mesh.number_of_grid_points()};\n    const auto temp_tags_ptr = make_not_null(&temp_tags);\n\n    // Compute constraint damping terms.\n    const double time = db::get<::Tags::Time>(*box);\n    const auto& functions_of_time =\n        db::get<::domain::Tags::FunctionsOfTime>(*box);\n    const auto& grid_coords =\n        db::get<evolution::dg::subcell::Tags::Coordinates<3, Frame::Grid>>(\n            *box);\n    db::get<gh::Tags::DampingFunctionGamma0<3, Frame::Grid>> (*box)(\n        get<gh::Tags::ConstraintGamma0>(temp_tags_ptr), grid_coords, time,\n        functions_of_time);\n    db::get<gh::Tags::DampingFunctionGamma1<3, Frame::Grid>> (*box)(\n        get<gh::Tags::ConstraintGamma1>(temp_tags_ptr), grid_coords, time,\n        functions_of_time);\n    db::get<gh::Tags::DampingFunctionGamma2<3, Frame::Grid>> (*box)(\n        get<gh::Tags::ConstraintGamma2>(temp_tags_ptr), grid_coords, time,\n        functions_of_time);\n\n    using variables_tag = typename System::variables_tag;\n    using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n    const gsl::not_null<typename dt_variables_tag::type*> dt_vars_ptr =\n        db::mutate<dt_variables_tag>(\n            [](const auto local_dt_vars_ptr) { return local_dt_vars_ptr; },\n            box);\n    dt_vars_ptr->initialize(subcell_mesh.number_of_grid_points());\n\n    using primitives_tag = typename System::primitive_variables_tag;\n    using evolved_vars_tag = typename System::variables_tag;\n\n    const auto& primitive_vars = db::get<primitives_tag>(*box);\n    const auto& evolved_vars = db::get<evolved_vars_tag>(*box);\n\n    // Velocity of the moving mesh, if applicable. We project the value\n    // stored on the DG grid onto the subcell grid.\n    const Mesh<3>& dg_mesh = db::get<domain::Tags::Mesh<3>>(*box);\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        mesh_velocity_dg = db::get<domain::Tags::MeshVelocity<3>>(*box);\n    const std::optional<Scalar<DataVector>>& div_mesh_velocity_dg =\n        db::get<domain::Tags::DivMeshVelocity>(*box);\n    std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>\n        mesh_velocity_subcell = {};\n    if (mesh_velocity_dg.has_value()) {\n      mesh_velocity_subcell = tnsr::I<DataVector, 3, Frame::Inertial>{\n          subcell_mesh.number_of_grid_points()};\n      for (size_t i = 0; i < 3; i++) {\n        mesh_velocity_subcell.value().get(i) =\n            evolution::dg::subcell::fd::project(mesh_velocity_dg.value().get(i),\n                                                dg_mesh,\n                                                subcell_mesh.extents());\n      }\n    }\n\n    gh::TimeDerivative<\n        ghmhd::GhValenciaDivClean::InitialData::\n            analytic_solutions_and_data_list,\n        3_st>::apply(get<::Tags::dt<GhDtTags>>(dt_vars_ptr)...,\n                     get<GhTemporaries>(temp_tags_ptr)...,\n                     get<::Tags::deriv<GhGradientTags, tmpl::size_t<3>,\n                                       Frame::Inertial>>(gh_derivs)...,\n                     get<GhExtraTags>(evolved_vars, temp_tags)...,\n\n                     db::get<::gh::gauges::Tags::GaugeCondition>(*box),\n                     db::get<evolution::dg::subcell::Tags::Mesh<3>>(*box), time,\n                     inertial_coords,\n                     cell_centered_logical_to_inertial_inv_jacobian,\n                     mesh_velocity_subcell);\n    if (get<gh::gauges::Tags::GaugeCondition>(*box).is_harmonic()) {\n      get(get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*temp_tags_ptr)) =\n          sqrt(\n              get(get<gr::Tags::DetSpatialMetric<DataVector>>(*temp_tags_ptr)));\n    }\n\n    // Add source terms from moving mesh\n    if (mesh_velocity_dg.has_value()) {\n      tmpl::for_each<tmpl::list<GhDtTags...>>([&dt_vars_ptr,\n                                               &mesh_velocity_subcell,\n                                               &gh_derivs](\n                                                  auto evolved_var_tag_v) {\n        using evolved_var_tag = tmpl::type_from<decltype(evolved_var_tag_v)>;\n        using dt_tag = ::Tags::dt<evolved_var_tag>;\n        using grad_tag =\n            ::Tags::deriv<evolved_var_tag, tmpl::size_t<3>, Frame::Inertial>;\n        // Flux and gradients use the same indexing conventions,\n        // replacing the direction of the face with the direction\n        // of the derivative.\n        using FluxTensor = typename grad_tag::type;\n        auto& dt_var = get<dt_tag>(*dt_vars_ptr);\n        const auto& grad_var = get<grad_tag>(gh_derivs);\n        for (size_t i = 0; i < dt_var.size(); ++i) {\n          const auto tensor_index = dt_var.get_tensor_index(i);\n          for (size_t j = 0; j < 3; j++) {\n            const auto grad_index =\n                FluxTensor::get_storage_index(prepend(tensor_index, j));\n            // Add (mesh_velocity)^j grad_j (var[i])\n            dt_var[i] +=\n                mesh_velocity_subcell.value().get(j) * grad_var[grad_index];\n          }\n        }\n      });\n    }\n\n    {\n      // Set extra tags needed for GRMHD source terms. We compute these from\n      // quantities already computed inside the GH RHS computation to minimize\n      // FLOPs.\n      const auto& lapse = get<gr::Tags::Lapse<DataVector>>(temp_tags);\n      const auto& half_phi_two_normals =\n          get<gh::Tags::HalfPhiTwoNormals<3>>(temp_tags);\n      const auto& phi = get<gh::Tags::Phi<DataVector, 3>>(evolved_vars);\n      const auto& phi_one_normal = get<gh::Tags::PhiOneNormal<3>>(temp_tags);\n      const auto& spacetime_normal_vector =\n          get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(temp_tags);\n      const auto& inverse_spacetime_metric =\n          get<gr::Tags::InverseSpacetimeMetric<DataVector, 3>>(temp_tags);\n\n      auto& spatial_deriv_lapse = get<deriv_lapse>(temp_tags);\n      auto& spatial_deriv_shift = get<deriv_shift>(temp_tags);\n      // Compute d_i beta^i\n      for (size_t i = 0; i < 3; ++i) {\n        // Use spatial_deriv_lapse as temp buffer to reduce number of 2*\n        // operations.\n        const auto& phi_two_normals_i = spatial_deriv_lapse.get(i) =\n            2.0 * half_phi_two_normals.get(i);\n        for (size_t j = 0; j < 3; ++j) {\n          spatial_deriv_shift.get(i, j) =\n              spacetime_normal_vector.get(j + 1) * phi_two_normals_i;\n          for (size_t a = 0; a < 4; ++a) {\n            spatial_deriv_shift.get(i, j) +=\n                inverse_spacetime_metric.get(j + 1, a) *\n                phi_one_normal.get(i, a);\n          }\n          spatial_deriv_shift.get(i, j) *= get(lapse);\n        }\n      }\n\n      // Compute d_i lapse\n      for (size_t i = 0; i < 3; ++i) {\n        spatial_deriv_lapse.get(i) = -get(lapse) * half_phi_two_normals.get(i);\n      }\n      // Extract d_i \\gamma_{ij}\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t i = 0; i < 3; ++i) {\n          for (size_t j = i; j < 3; ++j) {\n            get<deriv_spatial_metric>(temp_tags).get(k, i, j) =\n                phi.get(k, i + 1, j + 1);\n          }\n        }\n      }\n\n      // Compute extrinsic curvature\n      const auto& pi = get<gh::Tags::Pi<DataVector, 3>>(evolved_vars);\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = i; j < 3; ++j) {\n          get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(temp_tags).get(i,\n                                                                          j) =\n              0.5 * (pi.get(i + 1, j + 1) + phi_one_normal.get(i, j + 1) +\n                     phi_one_normal.get(j, i + 1));\n        }\n      }\n    }  // End scope for computing metric terms in GRMHD source terms.\n\n    grmhd::ValenciaDivClean::ComputeSources::apply(\n        get<::Tags::dt<GrmhdSourceTags>>(dt_vars_ptr)...,\n        get<GrmhdArgumentSourceTags>(temp_tags, primitive_vars, evolved_vars,\n                                     *box)...);\n\n    // Zero GRMHD tags that don't have sources.\n    tmpl::for_each<tmpl::list<GrmhdDtTags...>>([&dt_vars_ptr](\n                                                   auto evolved_var_tag_v) {\n      using evolved_var_tag = tmpl::type_from<decltype(evolved_var_tag_v)>;\n      using dt_tag = ::Tags::dt<evolved_var_tag>;\n      auto& dt_var = get<dt_tag>(*dt_vars_ptr);\n      for (size_t i = 0; i < dt_var.size(); ++i) {\n        if constexpr (not tmpl::list_contains_v<tmpl::list<GrmhdSourceTags...>,\n                                                evolved_var_tag>) {\n          // Zero the GRMHD dt(u) for variables that do not have a source term .\n          // This is necessary to avoid `+=` to a `NaN` (debug mode) or random\n          // garbage (release mode) when adding to dt_var below.\n          dt_var[i] = 0.0;\n        }\n      }\n    });\n    // Correction to source terms due to moving mesh\n    if (div_mesh_velocity_dg.has_value()) {\n      const DataVector div_mesh_velocity_subcell =\n          evolution::dg::subcell::fd::project(\n              div_mesh_velocity_dg.value().get(), dg_mesh,\n              subcell_mesh.extents());\n      tmpl::for_each<tmpl::list<GrmhdDtTags...>>(\n          [&dt_vars_ptr, &div_mesh_velocity_subcell,\n           &evolved_vars](auto evolved_var_tag_v) {\n            using evolved_var_tag =\n                tmpl::type_from<decltype(evolved_var_tag_v)>;\n            using dt_tag = ::Tags::dt<evolved_var_tag>;\n            auto& dt_var = get<dt_tag>(*dt_vars_ptr);\n            const auto& evolved_var = get<evolved_var_tag>(evolved_vars);\n            for (size_t i = 0; i < dt_var.size(); ++i) {\n              dt_var[i] -= div_mesh_velocity_subcell * evolved_var[i];\n            }\n          });\n    }\n\n    const tnsr::ii<DataVector, 3> spatial_metric{};\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        make_const_view(\n            make_not_null(&spatial_metric.get(i, j)),\n            get<gr::Tags::SpacetimeMetric<DataVector, 3>>(evolved_vars)\n                .get(i + 1, j + 1),\n            0, number_of_points);\n      }\n    }\n\n    tenex::evaluate<ti::i>(get<hydro::Tags::SpatialVelocityOneForm<\n                               DataVector, 3, Frame::Inertial>>(temp_tags_ptr),\n                           get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n                               primitive_vars)(ti::J) *\n                               spatial_metric(ti::i, ti::j));\n\n    tenex::evaluate<ti::i>(\n        get<hydro::Tags::MagneticFieldOneForm<DataVector, 3, Frame::Inertial>>(\n            temp_tags_ptr),\n        get<hydro::Tags::MagneticField<DataVector, 3>>(primitive_vars)(ti::J) *\n            spatial_metric(ti::i, ti::j));\n\n    tenex::evaluate(\n        get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tags_ptr),\n        get<hydro::Tags::MagneticField<DataVector, 3>>(primitive_vars)(ti::J) *\n            get<hydro::Tags::MagneticFieldOneForm<DataVector, 3>>(temp_tags)(\n                ti::j));\n\n    tenex::evaluate(\n        get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n            temp_tags_ptr),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(primitive_vars)(\n            ti::J) *\n            get<hydro::Tags::MagneticFieldOneForm<DataVector, 3>>(temp_tags)(\n                ti::j));\n\n    tenex::evaluate(get<typename ValenciaDivClean::TimeDerivativeTerms::\n                            OneOverLorentzFactorSquared>(temp_tags_ptr),\n                    1.0 / (square(get<hydro::Tags::LorentzFactor<DataVector>>(\n                              primitive_vars)())));\n\n    trace_reversed_stress_energy(\n        get<Tags::TraceReversedStressEnergy>(temp_tags_ptr),\n        get<Tags::FourVelocityOneForm>(temp_tags_ptr),\n        get<Tags::ComovingMagneticFieldOneForm>(temp_tags_ptr),\n\n        get<hydro::Tags::RestMassDensity<DataVector>>(evolved_vars, temp_tags,\n                                                      primitive_vars),\n        get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3,\n                                                Frame::Inertial>>(\n            evolved_vars, temp_tags, primitive_vars),\n\n        get<hydro::Tags::MagneticFieldOneForm<DataVector, 3, Frame::Inertial>>(\n            evolved_vars, temp_tags, primitive_vars),\n\n        get<hydro::Tags::MagneticFieldSquared<DataVector>>(\n            evolved_vars, temp_tags, primitive_vars),\n\n        get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n            evolved_vars, temp_tags, primitive_vars),\n        get<hydro::Tags::LorentzFactor<DataVector>>(evolved_vars, temp_tags,\n                                                    primitive_vars),\n        get<typename ValenciaDivClean::TimeDerivativeTerms::\n                OneOverLorentzFactorSquared>(evolved_vars, temp_tags,\n                                             primitive_vars),\n        get<hydro::Tags::Pressure<DataVector>>(evolved_vars, temp_tags,\n                                               primitive_vars),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n            evolved_vars, temp_tags, primitive_vars),\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(evolved_vars, temp_tags,\n                                                      primitive_vars),\n        get<gr::Tags::Shift<DataVector, 3>>(evolved_vars, temp_tags,\n                                            primitive_vars),\n        get<gr::Tags::Lapse<DataVector>>(evolved_vars, temp_tags,\n                                         primitive_vars));\n\n    add_stress_energy_term_to_dt_pi(\n        get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(dt_vars_ptr),\n        get<Tags::TraceReversedStressEnergy>(temp_tags),\n        get<gr::Tags::Lapse<DataVector>>(temp_tags));\n\n    for (size_t dim = 0; dim < 3; ++dim) {\n      const auto& boundary_correction_in_axis =\n          gsl::at(boundary_corrections, dim);\n      const double inverse_delta = gsl::at(one_over_delta_xi, dim);\n      EXPAND_PACK_LEFT_TO_RIGHT([&dt_vars_ptr, &boundary_correction_in_axis,\n                                 &cell_centered_det_inv_jacobian, dim,\n                                 inverse_delta, &subcell_mesh]() {\n        auto& dt_var = *get<::Tags::dt<GrmhdDtTags>>(dt_vars_ptr);\n        const auto& var_correction =\n            get<GrmhdDtTags>(boundary_correction_in_axis);\n        for (size_t i = 0; i < dt_var.size(); ++i) {\n          evolution::dg::subcell::add_cartesian_flux_divergence(\n              make_not_null(&dt_var[i]), inverse_delta,\n              get(cell_centered_det_inv_jacobian), var_correction[i],\n              subcell_mesh.extents(), dim);\n        }\n      }());\n    }\n  }\n};\n}  // namespace detail\n\n/*!\n * \\brief Compute the time derivative on the subcell grid using FD\n * reconstruction.\n *\n * The code makes the following unchecked assumptions:\n * - Assumes Cartesian coordinates with a diagonal Jacobian matrix\n * from the logical to the inertial frame\n */\ntemplate <typename System>\nstruct TimeDerivative {\n  template <typename DbTagsList>\n  static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n    using metavariables =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>;\n    using evolved_vars_tag = typename System::variables_tag;\n    using evolved_vars_tags = typename evolved_vars_tag::tags_list;\n    using grmhd_evolved_vars_tag =\n        typename grmhd::ValenciaDivClean::System::variables_tag;\n    using grmhd_evolved_vars_tags = typename grmhd_evolved_vars_tag::tags_list;\n    using fluxes_tags =\n        db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,\n                         tmpl::size_t<3>, Frame::Inertial>;\n    using prim_tag = typename System::primitive_variables_tag;\n    using prim_tags = typename prim_tag::tags_list;\n    using recons_prim_tags = tmpl::push_front<tmpl::push_back<\n        prim_tags,\n        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>>;\n    using gradients_tags = typename System::gradients_tags;\n\n    const Mesh<3>& dg_mesh = db::get<domain::Tags::Mesh<3>>(*box);\n    const Mesh<3>& subcell_mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<3>>(*box);\n    ASSERT(\n        subcell_mesh == Mesh<3>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                                subcell_mesh.quadrature(0)),\n        \"The subcell/FD mesh must be isotropic for the FD time derivative but \"\n        \"got \"\n            << subcell_mesh);\n    const size_t num_pts = subcell_mesh.number_of_grid_points();\n    const size_t reconstructed_num_pts =\n        (subcell_mesh.extents(0) + 1) *\n        subcell_mesh.extents().slice_away(0).product();\n\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>&\n        cell_centered_logical_coords =\n            db::get<evolution::dg::subcell::Tags::Coordinates<\n                3, Frame::ElementLogical>>(*box);\n    std::array<double, 3> one_over_delta_xi{};\n    for (size_t i = 0; i < 3; ++i) {\n      // Note: assumes isotropic extents\n      gsl::at(one_over_delta_xi, i) =\n          1.0 / (get<0>(cell_centered_logical_coords)[1] -\n                 get<0>(cell_centered_logical_coords)[0]);\n    }\n    const auto& cell_centered_logical_to_inertial_inv_jacobian = db::get<\n        evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<3>>(\n        *box);\n    const auto& inertial_coords =\n        db::get<evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>>(\n            *box);\n\n    const Element<3>& element = db::get<domain::Tags::Element<3>>(*box);\n    const bool element_is_interior = element.external_boundaries().empty();\n    constexpr bool subcell_enabled_at_external_boundary =\n        metavariables::SubcellOptions::subcell_enabled_at_external_boundary;\n\n    ASSERT(element_is_interior or subcell_enabled_at_external_boundary,\n           \"Subcell time derivative is called at a boundary element while \"\n           \"using subcell is disabled at external boundaries.\"\n           \"ElementID \"\n               << element.id());\n\n    const fd::Reconstructor<System>& recons =\n        db::get<fd::Tags::Reconstructor<System>>(*box);\n    // If the element has external boundaries and subcell is enabled for\n    // boundary elements, compute FD ghost data with a given boundary condition.\n    if constexpr (subcell_enabled_at_external_boundary) {\n      if (not element_is_interior) {\n        fd::BoundaryConditionGhostData<System>::apply(box, element, recons);\n      }\n    }\n    std::optional<std::array<gsl::span<std::uint8_t>, 3>>\n        reconstruction_order{};\n\n    if (const auto& filter_options =\n            db::get<grmhd::GhValenciaDivClean::fd::Tags::FilterOptions>(*box);\n        filter_options.spacetime_dissipation.has_value()) {\n      db::mutate<evolved_vars_tag>(\n          [&filter_options, &recons, &subcell_mesh](const auto evolved_vars_ptr,\n                                                    const auto& ghost_data) {\n            typename evolved_vars_tag::type filtered_vars = *evolved_vars_ptr;\n            // $(recons.ghost_zone_size() - 1) * 2 + 1$ => always use highest\n            // order dissipation filter possible.\n            grmhd::GhValenciaDivClean::fd::spacetime_kreiss_oliger_filter(\n                make_not_null(&filtered_vars), *evolved_vars_ptr, ghost_data,\n                subcell_mesh, 2 * recons.ghost_zone_size(),\n                filter_options.spacetime_dissipation.value());\n            *evolved_vars_ptr = filtered_vars;\n          },\n          box,\n          db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(\n              *box));\n    }\n\n    // Velocity of the moving mesh on the dg grid, if applicable.\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        mesh_velocity_dg = db::get<domain::Tags::MeshVelocity<3>>(*box);\n    // Inverse jacobian, to be projected on faces\n    const auto& inv_jacobian_dg =\n        db::get<domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                              Frame::Inertial>>(*box);\n    const auto& det_inv_jacobian_dg = db::get<\n        domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>>(\n        *box);\n\n    // GH+GRMHD is a bit different.\n    // 1. Compute GH time derivative, since this will also give us lapse, shift,\n    //    etc. that we need to reconstruct.\n    // 2. Compute d_t Pi_{ab} source terms from MHD (or do we wait until post\n    //    MHD source terms?)\n    // 3. Reconstruct MHD+spacetime vars to interfaces\n    // 4. Compute MHD time derivatives.\n    //\n    // Compute FD GH derivatives with neighbor data\n    // Use highest possible FD order for number of GZ, 2 * (ghost_zone_size)\n    const auto& evolved_vars = db::get<evolved_vars_tag>(*box);\n    Variables<db::wrap_tags_in<::Tags::deriv, gradients_tags, tmpl::size_t<3>,\n                               Frame::Inertial>>\n        cell_centered_gh_derivs{num_pts};\n    grmhd::GhValenciaDivClean::fd::spacetime_derivatives<System>(\n        make_not_null(&cell_centered_gh_derivs), evolved_vars,\n        db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(\n            *box),\n        recons.ghost_zone_size() * 2, subcell_mesh,\n        cell_centered_logical_to_inertial_inv_jacobian);\n\n    // Now package the data and compute the correction\n    //\n    // Note: Assumes a the GH and GRMHD corrections can be invoked separately.\n    // This is reasonable since the systems are a tensor product system.\n    const auto& base_boundary_correction =\n        db::get<evolution::Tags::BoundaryCorrection>(*box);\n    using derived_boundary_corrections =\n        tmpl::at<typename metavariables::factory_creation::factory_classes,\n                 evolution::BoundaryCorrection>;\n    std::array<Variables<grmhd_evolved_vars_tags>, 3> boundary_corrections{};\n    call_with_dynamic_type<void, derived_boundary_corrections>(\n        &base_boundary_correction, [&](const auto* gh_grmhd_correction) {\n          // Need the GH packaged tags to avoid projecting them.\n          using gh_dg_package_field_tags = typename std::decay_t<\n              decltype(gh_grmhd_correction\n                           ->gh_correction())>::dg_package_field_tags;\n          // Only apply correction to GRMHD variables.\n          const auto& boundary_correction =\n              gh_grmhd_correction->valencia_correction();\n          using DerivedCorrection = std::decay_t<decltype(boundary_correction)>;\n          using dg_package_data_temporary_tags =\n              typename DerivedCorrection::dg_package_data_temporary_tags;\n\n          using dg_package_data_argument_tags = tmpl::append<\n              evolved_vars_tags, recons_prim_tags, fluxes_tags,\n              tmpl::remove_duplicates<tmpl::push_back<\n                  dg_package_data_temporary_tags,\n                  gr::Tags::SpatialMetric<DataVector, 3>,\n                  gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                  gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                  evolution::dg::Actions::detail::NormalVector<3>>>>;\n\n          // Computed prims and cons on face via reconstruction\n          auto package_data_argvars_lower_face = make_array<3>(\n              Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n          auto package_data_argvars_upper_face = make_array<3>(\n              Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n\n          // Reconstruct data to the face\n          call_with_dynamic_type<\n              void, typename grmhd::GhValenciaDivClean::fd::Reconstructor<\n                        System>::creatable_classes>(\n              &recons, [&box, &package_data_argvars_lower_face,\n                        &package_data_argvars_upper_face,\n                        &reconstruction_order](const auto& reconstructor) {\n                using ReconstructorType =\n                    std::decay_t<decltype(*reconstructor)>;\n                db::apply<\n                    typename ReconstructorType::reconstruction_argument_tags>(\n                    [&package_data_argvars_lower_face,\n                     &package_data_argvars_upper_face, &reconstructor,\n                     &reconstruction_order](const auto&... args) {\n                      if constexpr (ReconstructorType::use_adaptive_order) {\n                        reconstructor->reconstruct(\n                            make_not_null(&package_data_argvars_lower_face),\n                            make_not_null(&package_data_argvars_upper_face),\n                            make_not_null(&reconstruction_order), args...);\n                      } else {\n                        (void)reconstruction_order;\n                        reconstructor->reconstruct(\n                            make_not_null(&package_data_argvars_lower_face),\n                            make_not_null(&package_data_argvars_upper_face),\n                            args...);\n                      }\n                    },\n                    *box);\n              });\n\n          using dg_package_field_tags =\n              typename DerivedCorrection::dg_package_field_tags;\n          // Allocated outside for loop to reduce allocations\n          Variables<dg_package_field_tags> upper_packaged_data{\n              reconstructed_num_pts};\n          Variables<dg_package_field_tags> lower_packaged_data{\n              reconstructed_num_pts};\n\n          // Compute fluxes on faces\n          for (size_t i = 0; i < 3; ++i) {\n            auto& vars_upper_face = gsl::at(package_data_argvars_upper_face, i);\n            auto& vars_lower_face = gsl::at(package_data_argvars_lower_face, i);\n            grmhd::ValenciaDivClean::subcell::compute_fluxes(\n                make_not_null(&vars_upper_face));\n            grmhd::ValenciaDivClean::subcell::compute_fluxes(\n                make_not_null(&vars_lower_face));\n\n            // Build extents of mesh shifted by half a grid cell in direction i\n            const unsigned long& num_subcells_1d = subcell_mesh.extents(0);\n            Index<3> face_mesh_extents(std::array<size_t, 3>{\n                num_subcells_1d, num_subcells_1d, num_subcells_1d});\n            face_mesh_extents[i] = num_subcells_1d + 1;\n            // Add moving mesh corrections to the fluxes, if needed\n            std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>\n                mesh_velocity_on_face = {};\n            if (mesh_velocity_dg.has_value()) {\n              // Project mesh velocity on face mesh.\n              // Can we get away with only doing the normal component? It\n              // is also used in the packaged data...\n              mesh_velocity_on_face = tnsr::I<DataVector, 3, Frame::Inertial>{\n                  reconstructed_num_pts};\n              for (size_t j = 0; j < 3; j++) {\n                // j^th component of the velocity on the i^th directed face\n                mesh_velocity_on_face.value().get(j) =\n                    evolution::dg::subcell::fd::project_to_faces(\n                        mesh_velocity_dg.value().get(j), dg_mesh,\n                        face_mesh_extents, i);\n              }\n\n              tmpl::for_each<grmhd_evolved_vars_tags>(\n                  [&vars_upper_face, &vars_lower_face,\n                   &mesh_velocity_on_face](auto tag_v) {\n                    using tag = tmpl::type_from<decltype(tag_v)>;\n                    using flux_tag =\n                        ::Tags::Flux<tag, tmpl::size_t<3>, Frame::Inertial>;\n                    using FluxTensor = typename flux_tag::type;\n                    const auto& var_upper = get<tag>(vars_upper_face);\n                    const auto& var_lower = get<tag>(vars_lower_face);\n                    auto& flux_upper = get<flux_tag>(vars_upper_face);\n                    auto& flux_lower = get<flux_tag>(vars_lower_face);\n                    for (size_t storage_index = 0;\n                         storage_index < var_upper.size(); ++storage_index) {\n                      const auto tensor_index =\n                          var_upper.get_tensor_index(storage_index);\n                      for (size_t j = 0; j < 3; j++) {\n                        const auto flux_storage_index =\n                            FluxTensor::get_storage_index(\n                                prepend(tensor_index, j));\n                        flux_upper[flux_storage_index] -=\n                            mesh_velocity_on_face.value().get(j) *\n                            var_upper[storage_index];\n                        flux_lower[flux_storage_index] -=\n                            mesh_velocity_on_face.value().get(j) *\n                            var_lower[storage_index];\n                      }\n                    }\n                  });\n            }\n\n            // Normal vectors in curved spacetime normalized by inverse\n            // spatial metric. Since we assume a Cartesian grid, this is\n            // relatively easy. Note that we use the sign convention on\n            // the normal vectors to be compatible with DG.\n            //\n            // Note that these normal vectors are on all faces inside the DG\n            // element since there are a bunch of subcells. We don't use the\n            // NormalCovectorAndMagnitude tag in the DataBox right now to avoid\n            // conflicts with the DG solver. We can explore in the future if\n            // it's possible to reuse that allocation.\n            //\n            // The unnormalized normal vector is\n            // n_j = d \\xi^{\\hat i}/dx^j\n            // with \"i\" the current face.\n            tnsr::i<DataVector, 3, Frame::Inertial> lower_outward_conormal{\n                reconstructed_num_pts, 0.0};\n            for (size_t j = 0; j < 3; j++) {\n              lower_outward_conormal.get(j) =\n                  evolution::dg::subcell::fd::project_to_faces(\n                      inv_jacobian_dg.get(i, j), dg_mesh, face_mesh_extents, i);\n            }\n            const auto det_inv_jacobian_face =\n                evolution::dg::subcell::fd::project_to_faces(\n                    get(det_inv_jacobian_dg), dg_mesh, face_mesh_extents, i);\n\n            const Scalar<DataVector> normalization{sqrt(get(\n                dot_product(lower_outward_conormal, lower_outward_conormal,\n                            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                                vars_upper_face))))};\n            for (size_t j = 0; j < 3; j++) {\n              lower_outward_conormal.get(j) =\n                  lower_outward_conormal.get(j) / get(normalization);\n            }\n\n            tnsr::i<DataVector, 3, Frame::Inertial> upper_outward_conormal{\n                reconstructed_num_pts, 0.0};\n            for (size_t j = 0; j < 3; j++) {\n              upper_outward_conormal.get(j) = -lower_outward_conormal.get(j);\n            }\n            // Note: we probably should compute the normal vector in addition to\n            // the co-vector. Not a huge issue since we'll get an FPE right now\n            // if it's used by a Riemann solver.\n\n            // Compute the packaged data\n            using dg_package_data_projected_tags = tmpl::append<\n                grmhd_evolved_vars_tags, fluxes_tags,\n                dg_package_data_temporary_tags,\n                typename DerivedCorrection::dg_package_data_primitive_tags>;\n            evolution::dg::Actions::detail::dg_package_data<System>(\n                make_not_null(&upper_packaged_data),\n                dynamic_cast<const DerivedCorrection&>(boundary_correction),\n                vars_upper_face, upper_outward_conormal, mesh_velocity_on_face,\n                *box, typename DerivedCorrection::dg_package_data_volume_tags{},\n                dg_package_data_projected_tags{});\n\n            evolution::dg::Actions::detail::dg_package_data<System>(\n                make_not_null(&lower_packaged_data),\n                dynamic_cast<const DerivedCorrection&>(boundary_correction),\n                vars_lower_face, lower_outward_conormal, mesh_velocity_on_face,\n                *box, typename DerivedCorrection::dg_package_data_volume_tags{},\n                dg_package_data_projected_tags{});\n\n            // Now need to check if any of our neighbors are doing DG,\n            // because if so then we need to use whatever boundary data\n            // they sent instead of what we computed locally.\n            //\n            // Note: We could check this beforehand to avoid the extra\n            // work of reconstruction and flux computations at the\n            // boundaries.\n            evolution::dg::subcell::correct_package_data<true>(\n                make_not_null(&lower_packaged_data),\n                make_not_null(&upper_packaged_data), i, element, subcell_mesh,\n                db::get<evolution::dg::Tags::MortarData<3>>(*box),\n                Variables<gh_dg_package_field_tags>::\n                    number_of_independent_components);\n\n            // Compute the corrections on the faces. We only need to\n            // compute this once because we can just flip the normal\n            // vectors then\n            gsl::at(boundary_corrections, i).initialize(reconstructed_num_pts);\n            evolution::dg::subcell::compute_boundary_terms(\n                make_not_null(&gsl::at(boundary_corrections, i)),\n                dynamic_cast<const DerivedCorrection&>(boundary_correction),\n                upper_packaged_data, lower_packaged_data, db::as_access(*box),\n                typename DerivedCorrection::dg_boundary_terms_volume_tags{});\n            // We need to multiply by the normal vector normalization\n            gsl::at(boundary_corrections, i) *= get(normalization);\n            // Also multiply by determinant of Jacobian, following Eq.(34)\n            // of 2109.11645\n            gsl::at(boundary_corrections, i) *= 1.0 / det_inv_jacobian_face;\n          }\n        });\n\n    // Now compute the actual time derivatives.\n    using gh_variables_tags =\n        typename System::gh_system::variables_tag::tags_list;\n    using gh_gradient_tags = typename TimeDerivativeTerms::gh_gradient_tags;\n    using gh_temporary_tags = typename TimeDerivativeTerms::gh_temp_tags;\n    using gh_extra_tags =\n        tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                   gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                   ::gh::Tags::ConstraintGamma0, ::gh::Tags::ConstraintGamma1,\n                   ::gh::Tags::ConstraintGamma2>;\n    using grmhd_source_tags =\n        tmpl::transform<ValenciaDivClean::ComputeSources::return_tags,\n                        tmpl::bind<db::remove_tag_prefix, tmpl::_1>>;\n    using grmhd_source_argument_tags =\n        ValenciaDivClean::ComputeSources::argument_tags;\n    detail::ComputeTimeDerivImpl<\n        gh_variables_tags, gh_temporary_tags, gh_gradient_tags, gh_extra_tags,\n        grmhd_evolved_vars_tags, grmhd_source_tags, grmhd_source_argument_tags,\n        System>::apply(box, inertial_coords,\n                       db::get<evolution::dg::subcell::fd::Tags::\n                                   DetInverseJacobianLogicalToInertial>(*box),\n                       cell_centered_logical_to_inertial_inv_jacobian,\n                       one_over_delta_xi, boundary_corrections,\n                       cell_centered_gh_derivs);\n    evolution::dg::subcell::store_reconstruction_order_in_databox(\n        box, reconstruction_order);\n  }\n};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/ZeroTimeDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::subcell {\n/*!\n * \\brief Zeros out the MHD time derivatives in the elements next to a DG-only\n * block that themselves are not DG-only elements.\n */\ntemplate <typename System>\nstruct ZeroMhdTimeDerivatives {\n  using return_tags = tmpl::list<::Tags::Variables<\n      db::wrap_tags_in<::Tags::dt, typename System::variables_tag::tags_list>>>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Element<3>,\n                 evolution::dg::subcell::Tags::SubcellOptions<3>>;\n\n  template <class DtTagsList>\n  static void apply(\n      const gsl::not_null<Variables<DtTagsList>*> dt_variables,\n      const Element<3>& element,\n      const evolution::dg::subcell::SubcellOptions& subcell_options) {\n    const bool bordering_dg_block = alg::any_of(\n        element.neighbors(),\n        [&subcell_options](const auto& direction_and_neighbor) {\n          const size_t first_block_id =\n              direction_and_neighbor.second.ids().begin()->block_id();\n          return alg::found(subcell_options.only_dg_block_ids(),\n                            first_block_id);\n        });\n    const bool in_dg_only_zone = alg::found(subcell_options.only_dg_block_ids(),\n                                            element.id().block_id());\n    if (bordering_dg_block and not in_dg_only_zone) {\n      tmpl::for_each<\n          typename grmhd::ValenciaDivClean::System::variables_tag::tags_list>(\n          [&dt_variables]<class Tag>(tmpl::type_<Tag> /*meta*/) {\n            auto& var = get<::Tags::dt<Tag>>(*dt_variables);\n            for (size_t i = 0; i < var.size(); ++i) {\n              var[i] = 0.0;\n            }\n          });\n    }\n  }\n};\n}  // namespace grmhd::GhValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Characteristics.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd {\n\n/// Namespace associated with utilities for the combined Generalized Harmonic\n/// and Valencia formulation of ideal GRMHD with divergence cleaning systems.\nnamespace GhValenciaDivClean {\n/// \\cond\nstruct TimeDerivativeTerms;\n/// \\endcond\n\ntemplate <typename NeutrinoTransportSystem>\nstruct System;\n\ntemplate <typename NeutrinoTransportSystem>\nstruct System {\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition;\n  static constexpr bool has_primitive_and_conservative_vars = true;\n  static constexpr size_t volume_dim = 3;\n  using grmhd_system = grmhd::ValenciaDivClean::System;\n  using gh_system = gh::System<3_st>;\n  using neutrino_transport_system = NeutrinoTransportSystem;\n\n  static_assert(std::is_same_v<Tags::spacetime_reconstruction_tags,\n                               typename gh_system::variables_tag::tags_list>);\n\n  using variables_tag = ::Tags::Variables<\n      tmpl::append<typename gh_system::variables_tag::tags_list,\n                   typename grmhd_system::variables_tag::tags_list>>;\n  using non_conservative_variables =\n      typename gh_system::variables_tag::tags_list;\n  using flux_variables = tmpl::append<typename gh_system::flux_variables,\n                                      typename grmhd_system::flux_variables>;\n  using gradient_variables =\n      tmpl::append<typename gh_system::gradient_variables,\n                   typename grmhd_system::gradient_variables>;\n  using gradients_tags = gradient_variables;\n  static constexpr bool is_in_flux_conservative_form = false;\n\n  using primitive_variables_tag =\n      typename grmhd_system::primitive_variables_tag;\n  using spacetime_variables_tag = ::Tags::Variables<\n      tmpl::list<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>>>;\n\n  using compute_volume_time_derivative_terms = TimeDerivativeTerms;\n\n  using conservative_from_primitive =\n      typename grmhd_system::conservative_from_primitive;\n  template <typename OrderedListOfPrimitiveRecoverySchemes>\n  using primitive_from_conservative =\n      typename grmhd_system::template primitive_from_conservative<\n          OrderedListOfPrimitiveRecoverySchemes>;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed<>;\n\n  using inverse_spatial_metric_tag =\n      typename gh_system::inverse_spatial_metric_tag;\n};\n}  // namespace GhValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/SystemM1.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Characteristics.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Namespace associated with utilities for the combined Generalized Harmonic,\n/// Valencia formulation of ideal GRMHD with divergence cleaning, and M1Grey\n/// neutrino systems.\nnamespace grmhd::GhValenciaDivClean {\n/// \\cond\nstruct TimeDerivativeTerms;\n/// \\endcond\n\ntemplate <typename... NeutrinoSpecies>\nstruct System<\n    RadiationTransport::M1Grey::System<tmpl::list<NeutrinoSpecies...>>> {\n  // Will need M1 BCs on top of existing\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition;\n  static constexpr bool has_primitive_and_conservative_vars = true;\n  static constexpr size_t volume_dim = 3;\n  using grmhd_system = grmhd::ValenciaDivClean::System;\n  using gh_system = gh::System<3_st>;\n  using neutrino_transport_system =\n      RadiationTransport::M1Grey::System<tmpl::list<NeutrinoSpecies...>>;\n\n  static_assert(std::is_same_v<Tags::spacetime_reconstruction_tags,\n                               typename gh_system::variables_tag::tags_list>);\n\n  using variables_tag = ::Tags::Variables<tmpl::append<\n      typename gh_system::variables_tag::tags_list,\n      typename grmhd_system::variables_tag::tags_list,\n      typename neutrino_transport_system::variables_tag::tags_list>>;\n  using non_conservative_variables =\n      typename gh_system::variables_tag::tags_list;\n  using flux_variables =\n      tmpl::append<typename gh_system::flux_variables,\n                   typename grmhd_system::flux_variables,\n                   typename neutrino_transport_system::flux_variables>;\n  using gradient_variables =\n      tmpl::append<typename gh_system::gradient_variables,\n                   typename grmhd_system::gradient_variables,\n                   typename neutrino_transport_system::gradient_variables>;\n  using gradients_tags = gradient_variables;\n  static constexpr bool is_in_flux_conservative_form = false;\n\n  // might need to filter duplicate tags\n  using primitive_variables_tag =\n      tmpl::append<typename grmhd_system::primitive_variables_tag,\n                   typename neutrino_transport_system::primitive_variables_tag>;\n\n  // contains required metric and metric derivatives for Gh system as well\n  using spacetime_variables_tags =\n      typename neutrino_transport_system::spacetime_variables_tag;\n\n  using compute_volume_time_derivative_terms = TimeDerivativeTerms;\n\n  using conservative_from_primitive =\n      typename grmhd_system::conservative_from_primitive;\n  template <typename OrderedListOfPrimitiveRecoverySchemes>\n  using primitive_from_conservative =\n      typename grmhd_system::template primitive_from_conservative<\n          OrderedListOfPrimitiveRecoverySchemes>;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed<>;\n\n  using inverse_spatial_metric_tag =\n      typename gh_system::inverse_spatial_metric_tag;\n};\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\n/// %Tags for the combined system of the Generalized Harmonic formulation for\n/// the Einstein field equations and the Valencia GRMHD formulation.\nnamespace Tags {\nnamespace detail {\n// A const reference to another tag, used for rerouting arguments in the\n// combined system utilities\ntemplate <typename Tag, typename Type = db::const_item_type<Tag, tmpl::list<>>>\nstruct TemporaryReference {\n  using tag = Tag;\n  using type = const Type&;\n};\n}  // namespace detail\n\n/// Represents the trace reversed stress-energy tensor of the matter in the MHD\n/// sector of the GRMHD system\nstruct TraceReversedStressEnergy : db::SimpleTag {\n  using type = tnsr::aa<DataVector, 3>;\n};\n\n/// Represents the stress-energy tensor of the matter in the MHD sector of the\n/// GRMHD system\nstruct StressEnergy : db::SimpleTag {\n  using type = tnsr::aa<DataVector, 3>;\n};\n\n/// The comoving magnetic field \\f$b^\\mu\\f$\nstruct ComovingMagneticField : db::SimpleTag {\n  using type = tnsr::A<DataVector, 3>;\n};\n\n/// The fluid four-velocity \\f$u^\\mu\\f$\nstruct FourVelocity : db::SimpleTag {\n  using type = tnsr::A<DataVector, 3>;\n};\n\n/// The down-index comoving magnetic field \\f$b_\\mu\\f$\nstruct ComovingMagneticFieldOneForm : db::SimpleTag {\n  using type = tnsr::a<DataVector, 3>;\n};\n\n/// The down-index four-velocity \\f$u_\\mu\\f$\nstruct FourVelocityOneForm : db::SimpleTag {\n  using type = tnsr::a<DataVector, 3>;\n};\n\n/// \\brief Tags sent for GRMHD primitive variable reconstruction. All tags sent\n/// are the reconstruciton+spacetime tags\nusing primitive_grmhd_reconstruction_tags =\n    tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n               hydro::Tags::ElectronFraction<DataVector>,\n               hydro::Tags::Temperature<DataVector>,\n               hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n               hydro::Tags::MagneticField<DataVector, 3>,\n               hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n/// \\brief Tags sent for spacetime evolution. All tags sent are the\n/// reconstruciton+spacetime tags\nusing spacetime_reconstruction_tags =\n    tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n               gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>;\n\n/// \\brief All tags sent for primitive reconstruction, both GRMHD and spacetime\n/// evolution tags.\nusing primitive_grmhd_and_spacetime_reconstruction_tags =\n    tmpl::append<primitive_grmhd_reconstruction_tags,\n                 spacetime_reconstruction_tags>;\n}  // namespace Tags\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.tpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\ntemplate struct gh::TimeDerivative<\n    ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list,\n    3>;\n\ntemplate class gh::gauges::Tags::GaugeAndDerivativeCompute<\n    3,\n    ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list>;\n\ntemplate class gh::gauges::SetPiAndPhiFromConstraints<\n    ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list,\n    3>;\n\nnamespace gh::gauges {\n#define GH_GAUGE_DISPATCH_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GH_GAUGE_DISPATCH_SOLUTION(data) \\\n  ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_GH_GAUGE_DISPATCH, (3))\n\n#undef GH_GAUGE_DISPATCH_SOLUTION\n#undef GH_GAUGE_DISPATCH_DIM\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/GhValenciaDivClean/TimeDerivativeTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/PassVariables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\" // For reference tag\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\nnamespace detail {\n// Some temporary tags appear both in the GRMHD temporary list and the\n// GeneralizedHarmonic temporary list, so we wrap the GRMHD temporaries in this\n// prefix tag to avoid collisions in data structures used to store the\n// temporaries.\ntemplate <typename Tag>\nstruct ValenciaTempTag : db::SimpleTag, db::PrefixTag {\n  using tag = Tag;\n  using type = typename Tag::type;\n};\n\nnamespace dt_type_aliases {\nusing gh_dt_tags =\n    db::wrap_tags_in<::Tags::dt,\n                     typename gh::System<3_st>::variables_tag::tags_list>;\nusing valencia_dt_tags = db::wrap_tags_in<\n    ::Tags::dt,\n    typename grmhd::ValenciaDivClean::System::variables_tag::tags_list>;\n\nusing dt_tags = tmpl::append<gh_dt_tags, valencia_dt_tags>;\n}  // namespace dt_type_aliases\n\nstruct TimeDerivativeTermsImpl;\nstruct TimeDerivativeTermsImpl {\n  // Tags related to GeneralizedHarmonic system\n  using gh_arg_tags = typename gh::TimeDerivative<\n      ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list,\n      3_st>::argument_tags;\n\n  using gh_temp_tags = typename gh::TimeDerivative<\n      ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list,\n      3_st>::temporary_tags;\n\n  using valencia_flux_tags = db::wrap_tags_in<\n      ::Tags::Flux,\n      typename grmhd::ValenciaDivClean::System::variables_tag::tags_list,\n      tmpl::size_t<3>, Frame::Inertial>;\n\n  using valencia_temp_tags =\n      typename grmhd::ValenciaDivClean::TimeDerivativeTerms::temporary_tags;\n\n  using valencia_time_derivative_arg_tags =\n      typename grmhd::ValenciaDivClean::TimeDerivativeTerms::argument_tags;\n\n  using trace_reversed_stress_result_tags =\n      tmpl::list<Tags::TraceReversedStressEnergy, Tags::FourVelocityOneForm,\n                 Tags::ComovingMagneticFieldOneForm>;\n  using trace_reversed_stress_argument_tags = tmpl::list<\n      hydro::Tags::RestMassDensity<DataVector>,\n      hydro::Tags::SpatialVelocityOneForm<DataVector, 3_st, Frame::Inertial>,\n      hydro::Tags::MagneticFieldOneForm<DataVector, 3_st, Frame::Inertial>,\n      hydro::Tags::MagneticFieldSquared<DataVector>,\n      hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>,\n      hydro::Tags::LorentzFactor<DataVector>,\n      grmhd::ValenciaDivClean::TimeDerivativeTerms::OneOverLorentzFactorSquared,\n      hydro::Tags::Pressure<DataVector>,\n      hydro::Tags::SpecificInternalEnergy<DataVector>,\n      gr::Tags::SpacetimeMetric<DataVector, 3>,\n      gr::Tags::Shift<DataVector, 3_st>, gr::Tags::Lapse<DataVector>>;\n\n  template <typename TemporaryTagsList, typename... ExtraTags>\n  static evolution::dg::TimeDerivativeDecisions<3> apply(\n      const gsl::not_null<Variables<dt_type_aliases::dt_tags>*> dt_vars_ptr,\n      const gsl::not_null<Variables<db::wrap_tags_in<\n          ::Tags::Flux, typename ValenciaDivClean::System::flux_variables,\n          tmpl::size_t<3>, Frame::Inertial>>*>\n          fluxes_ptr,\n      const gsl::not_null<Variables<TemporaryTagsList>*> temps_ptr,\n\n      const tnsr::iaa<DataVector, 3>& d_spacetime_metric,\n      const tnsr::iaa<DataVector, 3>& d_pi,\n      const tnsr::ijaa<DataVector, 3>& d_phi,\n\n      const tuples::TaggedTuple<ExtraTags...>& arguments) {\n    generalized_harmonic_time_derivative(\n        dt_vars_ptr, temps_ptr, d_spacetime_metric, d_pi, d_phi, arguments,\n        dt_type_aliases::gh_dt_tags{}, gh_temp_tags{}, gh_arg_tags{});\n\n    // Track whether or not we extracted the 3+1 quantities\n    bool three_plus_one_extracted = false;\n\n    // If we are in the atmosphere, then we can skip the evolution\n    // of the GRMHD system completely. This inevitably depends on parameters\n    // of the FixToAtmosphere code, so we grab those directly.\n    if (const auto& fix_to_atmosphere = get<Tags::detail::TemporaryReference<\n            ::Tags::VariableFixer<::VariableFixing::FixToAtmosphere<3>>>>(\n            arguments);\n        max(get(get<Tags::detail::TemporaryReference<\n                    hydro::Tags::RestMassDensity<DataVector>>>(arguments))) <=\n        std::min({fix_to_atmosphere.density_of_atmosphere(),\n                  (fix_to_atmosphere.velocity_limiting().has_value()\n                       ? fix_to_atmosphere.velocity_limiting()\n                             ->atmosphere_density_cutoff\n                       : std::numeric_limits<double>::infinity()),\n                  (fix_to_atmosphere.kappa_limiting().has_value()\n                       ? fix_to_atmosphere.kappa_limiting()->density_lower_bound\n                       : std::numeric_limits<double>::infinity())}) *\n            (1.0 + 10.0 * std::numeric_limits<double>::epsilon())) {\n      // Point into the right memory, then set it to zero.\n\n      ASSERT(\n          max(get(get<tmpl::front<dt_type_aliases::valencia_dt_tags>>(\n              *dt_vars_ptr))) == 0.0,\n          \"GH+GRMHD assumes the time derivatives are set to zero in general.\"\n          \" If this is no longer the case, please set them to zero in \"\n          \"atmosphere by changing the code where this ASSERT was triggered.\");\n      // Code that we could use to set the sources to zero if needed.\n      // Variables<tmpl::list<ValenciaDtTags...>> dt_div_clean(\n      //     get<tmpl::front<tmpl::list<ValenciaDtTags...>>>(*dt_vars_ptr)[0]\n      //         .data(),\n      //     0.0);\n      fluxes_ptr->initialize(fluxes_ptr->number_of_grid_points(), 0.0);\n      return evolution::dg::TimeDerivativeDecisions<3>{false};\n    } else {\n      // MHD calls\n\n      // extracting 3+1 quantities to assign spacetime spatial derivatives\n      extract_three_plus_one_quantities(temps_ptr, arguments);\n      three_plus_one_extracted = true;\n\n      // MHD computation\n      aggregate_time_derivative_terms(\n          dt_vars_ptr, fluxes_ptr, temps_ptr, arguments,\n          dt_type_aliases::valencia_dt_tags{}, valencia_flux_tags{},\n          valencia_temp_tags{}, valencia_time_derivative_arg_tags{},\n          trace_reversed_stress_result_tags{},\n          trace_reversed_stress_argument_tags{});\n    }\n\n    if (!three_plus_one_extracted) {\n      // If in atmosphere, extract 3+1 quantities for neutrino\n      // evolution\n      extract_three_plus_one_quantities(temps_ptr, arguments);\n      three_plus_one_extracted = true;\n      // Neutrino evolution will be called below\n    }\n\n    return evolution::dg::TimeDerivativeDecisions<3>{true};\n  }\n\n  template <typename OutputTags, typename TemporaryTagsList,\n            typename... ExtraTags, typename... GhDtTags, typename... GhTempTags,\n            typename... GhArgTags>\n  static void generalized_harmonic_time_derivative(\n      const gsl::not_null<Variables<OutputTags>*> dt_vars_ptr,\n      const gsl::not_null<Variables<TemporaryTagsList>*> temps_ptr,\n      const tnsr::iaa<DataVector, 3>& d_spacetime_metric,\n      const tnsr::iaa<DataVector, 3>& d_pi,\n      const tnsr::ijaa<DataVector, 3>& d_phi,\n      const tuples::TaggedTuple<ExtraTags...>& arguments,\n      tmpl::list<GhDtTags...> /*meta*/, tmpl::list<GhTempTags...> /*meta*/,\n      tmpl::list<GhArgTags...> /*meta*/) {\n    gh::TimeDerivative<\n        ghmhd::GhValenciaDivClean::InitialData::\n            analytic_solutions_and_data_list,\n        3_st>::apply(get<GhDtTags>(dt_vars_ptr)...,\n                     get<GhTempTags>(temps_ptr)..., d_spacetime_metric, d_pi,\n                     d_phi,\n                     get<Tags::detail::TemporaryReference<GhArgTags>>(\n                         arguments)...);\n  }\n\n  template <typename TemporaryTagsList, typename... ExtraTags>\n  static void extract_three_plus_one_quantities(\n      const gsl::not_null<Variables<TemporaryTagsList>*> temps_ptr,\n      const tuples::TaggedTuple<ExtraTags...>& arguments) {\n    // Extract the 3+1 quantities from the spacetime metric\n\n    // Check whether or not sqrt(det(g)) exists based on gauge condition\n    if (get<Tags::detail::TemporaryReference<gh::gauges::Tags::GaugeCondition>>(\n            arguments)\n            .is_harmonic()) {\n      get(get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*temps_ptr)) =\n          sqrt(get(get<gr::Tags::DetSpatialMetric<DataVector>>(*temps_ptr)));\n    }\n\n    // extract spatial derivative of lapse\n    for (size_t i = 0; i < 3; ++i) {\n      get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        Frame::Inertial>>(*temps_ptr)\n          .get(i) = -get(get<gr::Tags::Lapse<DataVector>>(*temps_ptr)) *\n                    get<gh::Tags::HalfPhiTwoNormals<3>>(*temps_ptr).get(i);\n    }\n\n    const auto& phi =\n        get<Tags::detail::TemporaryReference<gh::Tags::Phi<DataVector, 3>>>(\n            arguments);\n    const auto& inv_spatial_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(*temps_ptr);\n    const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(*temps_ptr);\n\n    // extract spatial derivative of shift\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                          Frame::Inertial>>(*temps_ptr)\n            .get(i, j) = inv_spatial_metric.get(j, 0) * phi.get(i, 0, 1);\n        for (size_t k = 1; k < 3; ++k) {\n          get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                            Frame::Inertial>>(*temps_ptr)\n              .get(i, j) += inv_spatial_metric.get(j, k) * phi.get(i, 0, k + 1);\n        }\n        for (size_t k = 0; k < 3; ++k) {\n          for (size_t l = 0; l < 3; ++l) {\n            get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                              Frame::Inertial>>(*temps_ptr)\n                .get(i, j) -= shift.get(k) * inv_spatial_metric.get(j, l) *\n                              phi.get(i, l + 1, k + 1);\n          }\n        }\n      }\n    }\n\n    const auto& pi =\n        get<Tags::detail::TemporaryReference<gh::Tags::Pi<DataVector, 3>>>(\n            arguments);\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(*temps_ptr).get(i, j) =\n            0.5 * (pi.get(i + 1, j + 1) +\n                   get<gh::Tags::PhiOneNormal<3>>(*temps_ptr).get(i, j + 1) +\n                   get<gh::Tags::PhiOneNormal<3>>(*temps_ptr).get(j, i + 1));\n      }\n    }\n\n  }  // end extract3plus1\n\n  template <typename OutputTags, typename TemporaryTagsList,\n            typename... ExtraTags, typename... ValenciaDtTags,\n            typename... ValenciaFluxTags, typename... ValenciaTempTags,\n            typename... ValenciaTimeDerivativeArgTags,\n            typename... TraceReversedStressResultTags,\n            typename... TraceReversedStressArgumentTags>\n  static void aggregate_time_derivative_terms(\n      const gsl::not_null<Variables<OutputTags>*> dt_vars_ptr,\n      const gsl::not_null<Variables<db::wrap_tags_in<\n          ::Tags::Flux, typename ValenciaDivClean::System::flux_variables,\n          tmpl::size_t<3>, Frame::Inertial>>*>\n          fluxes_ptr,\n      const gsl::not_null<Variables<TemporaryTagsList>*> temps_ptr,\n      const tuples::TaggedTuple<ExtraTags...>& arguments,\n      tmpl::list<ValenciaDtTags...> /*meta*/,\n      tmpl::list<ValenciaFluxTags...> /*meta*/,\n      tmpl::list<ValenciaTempTags...> /*meta*/,\n      tmpl::list<ValenciaTimeDerivativeArgTags...> /*meta*/,\n      tmpl::list<TraceReversedStressResultTags...> /*meta*/,\n      tmpl::list<TraceReversedStressArgumentTags...> /*meta*/) {\n    using extra_tags_list = tmpl::list<ExtraTags...>;\n\n    grmhd::ValenciaDivClean::TimeDerivativeTerms::apply(\n        get<ValenciaDtTags>(dt_vars_ptr)...,\n        get<ValenciaFluxTags>(fluxes_ptr)...,\n        get<ValenciaTempTags>(temps_ptr)...,\n\n        get<tmpl::conditional_t<\n            tmpl::list_contains_v<extra_tags_list,\n                                  Tags::detail::TemporaryReference<\n                                      ValenciaTimeDerivativeArgTags>>,\n            Tags::detail::TemporaryReference<ValenciaTimeDerivativeArgTags>,\n            ValenciaTimeDerivativeArgTags>>(arguments, *temps_ptr)...);\n\n    trace_reversed_stress_energy(\n        get<TraceReversedStressResultTags>(temps_ptr)...,\n        get<tmpl::conditional_t<\n            tmpl::list_contains_v<extra_tags_list,\n                                  Tags::detail::TemporaryReference<\n                                      TraceReversedStressArgumentTags>>,\n            Tags::detail::TemporaryReference<TraceReversedStressArgumentTags>,\n            TraceReversedStressArgumentTags>>(*temps_ptr, arguments)...);\n\n    add_stress_energy_term_to_dt_pi(\n        get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(dt_vars_ptr),\n        get<grmhd::GhValenciaDivClean::Tags::TraceReversedStressEnergy>(\n            *temps_ptr),\n        get<gr::Tags::Lapse<DataVector>>(*temps_ptr));\n  }\n};  // namespace detail\n}  // namespace detail\n\n/*!\n * \\brief Compute the RHS terms and flux values for both the Generalized\n * Harmonic formulation of Einstein's equations and the Valencia formulation of\n * the GRMHD equations with divergence cleaning.\n *\n * \\details The bulk of the computations in this class dispatch to\n * `gh::TimeDerivative` and\n * `grmhd::ValenciaDivClean::TimeDerivativeTerms` as a 'product system' -- each\n * independently operating on its own subset of the supplied variable\n * collections.\n * The additional step is taken to compute the trace-reversed stress energy\n * tensor associated with the GRMHD part of the system and add its contribution\n * to the \\f$\\partial_t \\Pi_{a b}\\f$ variable in the Generalized Harmonic\n * system, which is the only explicit coupling required to back-react the effect\n * of matter on the spacetime solution.\n *\n * \\note The MHD calculation reuses any spacetime quantities in its\n * argument_tags that are computed by the GH time derivative. However, other\n * quantities that aren't computed by the GH time derivative like the extrinsic\n * curvature are currently still retrieved from the DataBox. Those calculations\n * can be explicitly inlined here to reduce memory pressure and the number of\n * compute tags.\n */\nstruct TimeDerivativeTerms : evolution::PassVariables {\n  using gh_dt_tags =\n      db::wrap_tags_in<::Tags::dt,\n                       typename gh::System<3_st>::variables_tag::tags_list>;\n  using valencia_dt_tags = db::wrap_tags_in<\n      ::Tags::dt,\n      typename grmhd::ValenciaDivClean::System::variables_tag::tags_list>;\n\n  using dt_tags = tmpl::append<gh_dt_tags, valencia_dt_tags>;\n\n  using d_spatial_metric = ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                         tmpl::size_t<3>, Frame::Inertial>;\n\n  using gh_temp_tags = typename gh::TimeDerivative<\n      ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list,\n      3_st>::temporary_tags;\n  using gh_gradient_tags = typename gh::System<3_st>::gradients_tags;\n  using gh_arg_tags = typename gh::TimeDerivative<\n      ghmhd::GhValenciaDivClean::InitialData::analytic_solutions_and_data_list,\n      3_st>::argument_tags;\n\n  using valencia_temp_tags =\n      typename grmhd::ValenciaDivClean::TimeDerivativeTerms::temporary_tags;\n  // Additional temp tags are the derivatives of the metric since GH doesn't\n  // explicitly calculate those.\n  using valencia_extra_temp_tags =\n      tmpl::list<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>>;\n  using valencia_arg_tags = tmpl::list_difference<\n      typename grmhd::ValenciaDivClean::TimeDerivativeTerms::argument_tags,\n      tmpl::append<gh_temp_tags, valencia_extra_temp_tags>>;\n\n  using trace_reversed_stress_result_tags =\n      tmpl::list<Tags::TraceReversedStressEnergy, Tags::FourVelocityOneForm,\n                 Tags::ComovingMagneticFieldOneForm>;\n  using extra_temp_tags = tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>>;\n\n  using temporary_tags = tmpl::remove<\n      tmpl::remove_duplicates<tmpl::append<\n          gh_temp_tags, valencia_temp_tags, valencia_extra_temp_tags,\n          trace_reversed_stress_result_tags, extra_temp_tags>>,\n      gr::Tags::SpatialMetric<DataVector, 3>>;\n  using argument_tags = tmpl::remove<\n      tmpl::remove<tmpl::append<gh_arg_tags,\n\n                                valencia_arg_tags,\n\n                                tmpl::list<::Tags::VariableFixer<\n                                    ::VariableFixing::FixToAtmosphere<3>>>>,\n                   gr::Tags::SpatialMetric<DataVector, 3>>,\n      d_spatial_metric>;\n\n  template <typename... Args>\n  static evolution::dg::TimeDerivativeDecisions<3> apply(\n      const gsl::not_null<Variables<dt_tags>*> dt_vars_ptr,\n      const gsl::not_null<Variables<db::wrap_tags_in<\n          ::Tags::Flux, typename ValenciaDivClean::System::flux_variables,\n          tmpl::size_t<3>, Frame::Inertial>>*>\n          fluxes_ptr,\n      const gsl::not_null<Variables<temporary_tags>*> temps_ptr,\n      const tnsr::iaa<DataVector, 3>& d_spacetime_metric,\n      const tnsr::iaa<DataVector, 3>& d_pi,\n      const tnsr::ijaa<DataVector, 3>& d_phi, const Args&... args) {\n    using args_list = tmpl::push_back<\n        db::wrap_tags_in<Tags::detail::TemporaryReference, argument_tags>,\n        gr::Tags::SpatialMetric<DataVector, 3>, d_spatial_metric>;\n    tuples::tagged_tuple_from_typelist<args_list> arguments{\n        args..., typename gr::Tags::SpatialMetric<DataVector, 3>::type{},\n        typename d_spatial_metric::type{}};\n    const size_t number_of_points = get<Tags::detail::TemporaryReference<\n        gr::Tags::SpacetimeMetric<DataVector, 3>>>(arguments)[0]\n                                        .size();\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        make_const_view(\n            make_not_null(\n                &std::as_const(\n                     get<gr::Tags::SpatialMetric<DataVector, 3>>(arguments))\n                     .get(i, j)),\n            get<Tags::detail::TemporaryReference<\n                gr::Tags::SpacetimeMetric<DataVector, 3>>>(arguments)\n                .get(i + 1, j + 1),\n            0, number_of_points);\n      }\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        for (size_t k = j; k < 3; ++k) {\n          make_const_view(\n              make_not_null(&std::as_const(get<d_spatial_metric>(arguments))\n                                 .get(i, j, k)),\n              get<Tags::detail::TemporaryReference<\n                  gh::Tags::Phi<DataVector, 3>>>(arguments)\n                  .get(i, j + 1, k + 1),\n              0, number_of_points);\n        }\n      }\n    }\n\n    return detail::TimeDerivativeTermsImpl::apply(dt_vars_ptr, fluxes_ptr,\n                                                  temps_ptr, d_spacetime_metric,\n                                                  d_pi, d_phi, arguments);\n  }\n};\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  NumericInitialData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  NumericInitialData.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Actions/NumericInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Actions/NumericInitialData.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <optional>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\n\nNumericInitialData::NumericInitialData(\n    std::string file_glob, std::string subfile_name,\n    std::variant<double, importers::ObservationSelector> observation_value,\n    std::optional<double> observation_value_epsilon,\n    const bool enable_interpolation, PrimitiveVars selected_variables,\n    const double density_cutoff)\n    : importer_options_(\n          std::move(file_glob), std::move(subfile_name), observation_value,\n          observation_value_epsilon.value_or(1.0e-12), enable_interpolation),\n      selected_variables_(std::move(selected_variables)),\n      density_cutoff_(density_cutoff) {}\n\nNumericInitialData::NumericInitialData(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nPUP::able::PUP_ID NumericInitialData::my_PUP_ID = 0;\n\nsize_t NumericInitialData::volume_data_id() const {\n  size_t hash = 0;\n  boost::hash_combine(hash, pretty_type::get_name<NumericInitialData>());\n  boost::hash_combine(hash,\n                      get<importers::OptionTags::FileGlob>(importer_options_));\n  boost::hash_combine(hash,\n                      get<importers::OptionTags::Subgroup>(importer_options_));\n  return hash;\n}\n\nvoid NumericInitialData::pup(PUP::er& p) {\n  p | importer_options_;\n  p | selected_variables_;\n  p | density_cutoff_;\n}\n\nbool operator==(const NumericInitialData& lhs, const NumericInitialData& rhs) {\n  return lhs.importer_options_ == rhs.importer_options_ and\n         lhs.selected_variables_ == rhs.selected_variables_ and\n         lhs.density_cutoff_ == rhs.density_cutoff_;\n}\n\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Actions/NumericInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\n\n/*!\n * \\brief Numeric initial data loaded from volume data files\n *\n * This class can be factory-created in the input file to start an evolution\n * from numeric initial data. It selects the hydro variables to load from the\n * volume data files and allows to choose constant values for some of them.\n *\n * Where the density is below the `DensityCutoff` the fluid variables are set to\n * vacuum (zero density, pressure, energy and velocity, and unit Lorentz\n * factor). To evolve the initial data, an atmosphere treatment is likely\n * required to fix the value of the fluid variables in these regions.\n */\nclass NumericInitialData : public evolution::initial_data::InitialData {\n public:\n  /// Name of a variable in the volume data file. Can be optional, in which case\n  /// a constant value can be supplied instead of a dataset name.\n  template <typename Tag, typename IsRequired>\n  struct VarName {\n    using tag = Tag;\n    static constexpr bool is_required = IsRequired::value;\n    static std::string name() { return db::tag_name<Tag>(); }\n    using type = std::conditional_t<is_required, std::string,\n                                    std::variant<double, std::string>>;\n    static constexpr Options::String help =\n        \"Name of the variable in the volume data file. For optional variables \"\n        \"you may instead specify a double that is used as a constant value \"\n        \"on the entire grid.\";\n  };\n\n  // These are the hydro variables that we support loading from volume\n  // data files\n  using required_primitive_vars =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::LowerSpatialFourVelocity<DataVector, 3>>;\n  using optional_primitive_vars =\n      tmpl::list<hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>>;\n  using primitive_vars_option_tags =\n      tmpl::append<db::wrap_tags_in<VarName, required_primitive_vars,\n                                    std::bool_constant<true>>,\n                   db::wrap_tags_in<VarName, optional_primitive_vars,\n                                    std::bool_constant<false>>>;\n  struct PrimitiveVars\n      : tuples::tagged_tuple_from_typelist<primitive_vars_option_tags> {\n    static constexpr Options::String help =\n        \"Primitive hydro variables: 'RestMassDensity' and \"\n        \"'LowerSpatialFourVelocity' (which is u_i = W * gamma_ij v^j). \";\n    using options = tags_list;\n    using TaggedTuple::TaggedTuple;\n  };\n\n  using all_vars =\n      tmpl::append<required_primitive_vars, optional_primitive_vars>;\n\n  // Input-file options\n  struct Variables {\n    using type = PrimitiveVars;\n    static constexpr Options::String help =\n        \"Set of initial data variables from which the Valencia evolution \"\n        \"variables are computed.\";\n  };\n\n  struct DensityCutoff {\n    using type = double;\n    static constexpr Options::String help =\n        \"Where the density is below this cutoff the fluid variables are set to \"\n        \"vacuum (zero density, pressure, energy and velocity, and unit Lorentz \"\n        \"factor). \"\n        \"During the evolution, atmosphere treatment will typically kick in and \"\n        \"fix the value of the fluid variables in these regions. Therefore, \"\n        \"it makes sense to set this density cutoff to the same value as the \"\n        \"atmosphere density cutoff.\";\n    static constexpr double lower_bound() { return 0.; }\n  };\n\n  using options = tmpl::push_back<importers::ImporterOptions::tags_list,\n                                  Variables, DensityCutoff>;\n\n  static constexpr Options::String help =\n      \"Numeric initial data loaded from volume data files\";\n\n  NumericInitialData() = default;\n  NumericInitialData(const NumericInitialData& rhs) = default;\n  NumericInitialData& operator=(const NumericInitialData& rhs) = default;\n  NumericInitialData(NumericInitialData&& /*rhs*/) = default;\n  NumericInitialData& operator=(NumericInitialData&& /*rhs*/) = default;\n  ~NumericInitialData() = default;\n\n  /// \\cond\n  explicit NumericInitialData(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NumericInitialData);\n  /// \\endcond\n\n  std::unique_ptr<evolution::initial_data::InitialData> get_clone()\n      const override {\n    return std::make_unique<NumericInitialData>(*this);\n  }\n\n  NumericInitialData(\n      std::string file_glob, std::string subfile_name,\n      std::variant<double, importers::ObservationSelector> observation_value,\n      std::optional<double> observation_value_epsilon,\n      bool enable_interpolation, PrimitiveVars selected_variables,\n      double density_cutoff);\n\n  const importers::ImporterOptions& importer_options() const {\n    return importer_options_;\n  }\n\n  const PrimitiveVars& selected_variables() const {\n    return selected_variables_;\n  }\n\n  double density_cutoff() const { return density_cutoff_; }\n\n  size_t volume_data_id() const;\n\n  template <typename... AllTags>\n  void select_for_import(\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> all_fields) const {\n    // Select the subset of the available variables that we want to read from\n    // the volume data file\n    tmpl::for_each<primitive_vars_option_tags>([&all_fields,\n                                                this](const auto option_tag_v) {\n      using option_tag = tmpl::type_from<std::decay_t<decltype(option_tag_v)>>;\n      using tag = typename option_tag::tag;\n      static constexpr bool is_required = option_tag::is_required;\n      const auto& selected_dataset_name = get<option_tag>(selected_variables_);\n      if constexpr (is_required) {\n        // Always select required tags for import\n        get<importers::Tags::Selected<tag>>(*all_fields) =\n            selected_dataset_name;\n      } else {\n        // Only select optional tags for import if a dataset name was\n        // specified\n        if (std::holds_alternative<std::string>(selected_dataset_name)) {\n          get<importers::Tags::Selected<tag>>(*all_fields) =\n              std::get<std::string>(selected_dataset_name);\n        }\n      }\n    });\n  }\n\n  template <typename... AllTags, size_t ThermodynamicDim>\n  void set_initial_data(\n      const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      const gsl::not_null<tnsr::I<DataVector, 3>*> spatial_velocity,\n      const gsl::not_null<tnsr::I<DataVector, 3>*> magnetic_field,\n      const gsl::not_null<Scalar<DataVector>*> div_cleaning_field,\n      const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      const gsl::not_null<Scalar<DataVector>*> pressure,\n      const gsl::not_null<Scalar<DataVector>*> temperature,\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> numeric_data,\n      const tnsr::II<DataVector, 3>& inv_spatial_metric,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n          equation_of_state) const {\n    // Rest mass density from dataset\n    *rest_mass_density =\n        std::move(get<hydro::Tags::RestMassDensity<DataVector>>(*numeric_data));\n    const size_t num_points = get(*rest_mass_density).size();\n    // Electron fraction from dataset or constant value\n    const std::variant<double, std::string>& electron_fraction_selection =\n        get<VarName<hydro::Tags::ElectronFraction<DataVector>,\n                    std::bool_constant<false>>>(selected_variables_);\n    if (std::holds_alternative<std::string>(electron_fraction_selection)) {\n      *electron_fraction = std::move(\n          get<hydro::Tags::ElectronFraction<DataVector>>(*numeric_data));\n    } else {\n      const double constant_electron_fraction =\n          std::get<double>(electron_fraction_selection);\n      set_number_of_grid_points(electron_fraction, num_points);\n      get(*electron_fraction) = constant_electron_fraction;\n    }\n    // Velocity and Lorentz factor from u_i dataset\n    // W = 1 + W^2 v_i v^i\n    // where W v_i = u_i, so we first raise the index on W v_i with the\n    // spatial metric and then compute its magnitude. We use\n    // `spatial_velocity` as intermediate memory buffer for W v^i.\n    const auto& u_i = get<hydro::Tags::LowerSpatialFourVelocity<DataVector, 3>>(\n        *numeric_data);\n    raise_or_lower_index(spatial_velocity, u_i, inv_spatial_metric);\n    dot_product(lorentz_factor, u_i, *spatial_velocity);\n    get(*lorentz_factor) += 1.;\n    for (size_t d = 0; d < 3; ++d) {\n      spatial_velocity->get(d) /= get(*lorentz_factor);\n    }\n    // Specific internal energy, and pressure from EOS\n    set_number_of_grid_points(specific_internal_energy, num_points);\n    set_number_of_grid_points(pressure, num_points);\n    for (size_t i = 0; i < num_points; ++i) {\n      double& local_rest_mass_density = get(*rest_mass_density)[i];\n      // Apply the EOS only where the density is above the cutoff, because the\n      // fluid model breaks down in the zero-density limit\n      if (local_rest_mass_density <= density_cutoff_) {\n        local_rest_mass_density = 0.;\n        get(*specific_internal_energy)[i] = 0.;\n        get(*pressure)[i] = 0.;\n        get(*temperature)[i] = 0.;\n        // Also reset velocity and Lorentz factor below cutoff to be safe\n        for (size_t d = 0; d < 3; ++d) {\n          spatial_velocity->get(d)[i] = 0.;\n        }\n        get(*lorentz_factor)[i] = 1.;\n      } else {\n        if constexpr (ThermodynamicDim == 1) {\n          get(*specific_internal_energy)[i] =\n              get(equation_of_state.specific_internal_energy_from_density(\n                  Scalar<double>(local_rest_mass_density)));\n          get(*pressure)[i] = get(equation_of_state.pressure_from_density(\n              Scalar<double>(local_rest_mass_density)));\n          get(*temperature)[i] = get(equation_of_state.temperature_from_density(\n              Scalar<double>(local_rest_mass_density)));\n        } else if constexpr (ThermodynamicDim == 2) {\n          get(*specific_internal_energy)[i] =\n              get(equation_of_state\n                      .specific_internal_energy_from_density_and_temperature(\n                          Scalar<double>(local_rest_mass_density),\n                          Scalar<double>(0.)));\n          get(*pressure)[i] =\n              get(equation_of_state.pressure_from_density_and_energy(\n                  Scalar<double>(local_rest_mass_density),\n                  Scalar<double>(get(*specific_internal_energy)[i])));\n          get(*temperature)[i] =\n              get(equation_of_state.temperature_from_density_and_energy(\n                  Scalar<double>(local_rest_mass_density),\n                  Scalar<double>(get(*specific_internal_energy)[i])));\n        } else {\n          // Loaded the electron fraction previously.\n          get(*specific_internal_energy)[i] =\n              get(equation_of_state\n                      .specific_internal_energy_from_density_and_temperature(\n                          Scalar<double>(local_rest_mass_density),\n                          Scalar<double>(0.),\n                          Scalar<double>(get(*electron_fraction)[i])));\n          get(*pressure)[i] =\n              get(equation_of_state.pressure_from_density_and_energy(\n                  Scalar<double>(local_rest_mass_density),\n                  Scalar<double>(get(*specific_internal_energy)[i]),\n                  Scalar<double>(get(*electron_fraction)[i])));\n          get(*temperature)[i] =\n              get(equation_of_state.temperature_from_density_and_energy(\n                  Scalar<double>(local_rest_mass_density),\n                  Scalar<double>(get(*specific_internal_energy)[i]),\n                  Scalar<double>(get(*electron_fraction)[i])));\n        }\n      }\n    }\n    // Magnetic field from dataset or constant value\n    const std::variant<double, std::string>& magnetic_field_selection =\n        get<VarName<hydro::Tags::MagneticField<DataVector, 3>,\n                    std::bool_constant<false>>>(selected_variables_);\n    if (std::holds_alternative<std::string>(magnetic_field_selection)) {\n      *magnetic_field = std::move(\n          get<hydro::Tags::MagneticField<DataVector, 3>>(*numeric_data));\n    } else {\n      const double constant_magnetic_field =\n          std::get<double>(magnetic_field_selection);\n      if (constant_magnetic_field != 0.) {\n        ERROR(\n            \"Choose a magnetic field dataset or set it to zero. \"\n            \"Nonzero uniform magnetic fields cannot currently be chosen \"\n            \"in the input file. Generate a dataset for the nonzero \"\n            \"uniform magnetic field if you need to.\");\n      }\n      set_number_of_grid_points(magnetic_field, num_points);\n      std::fill(magnetic_field->begin(), magnetic_field->end(),\n                constant_magnetic_field);\n    }\n    // Divergence cleaning field\n    set_number_of_grid_points(div_cleaning_field, num_points);\n    get(*div_cleaning_field) = 0.;\n  }\n\n  void pup(PUP::er& p) override;\n\n  friend bool operator==(const NumericInitialData& lhs,\n                         const NumericInitialData& rhs);\n\n private:\n  importers::ImporterOptions importer_options_{};\n  PrimitiveVars selected_variables_{};\n  double density_cutoff_{};\n};\n\nnamespace Actions {\n\n/*!\n * \\brief Dispatch loading numeric initial data from files.\n *\n * Place this action before\n * grmhd::ValenciaDivClean::Actions::SetNumericInitialData in the action list.\n * See importers::Actions::ReadAllVolumeDataAndDistribute for details, which is\n * invoked by this action.\n */\nstruct ReadNumericInitialData {\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Select the subset of the available variables that we want to read from\n    // the volume data file\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        db::get<evolution::initial_data::Tags::InitialData>(box));\n    tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n        importers::Tags::Selected, NumericInitialData::all_vars>>\n        selected_fields{};\n    initial_data.select_for_import(make_not_null(&selected_fields));\n    // Dispatch loading the variables from the volume data file\n    // - Not using `ckLocalBranch` here to make sure the simple action\n    //   invocation is asynchronous.\n    auto& reader_component = Parallel::get_parallel_component<\n        importers::ElementDataReader<Metavariables>>(cache);\n    Parallel::simple_action<importers::Actions::ReadAllVolumeDataAndDistribute<\n        3, NumericInitialData::all_vars, ParallelComponent>>(\n        reader_component, initial_data.importer_options(),\n        initial_data.volume_data_id(), std::move(selected_fields));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Receive numeric initial data loaded by\n * grmhd::ValenciaDivClean::Actions::ReadNumericInitialData.\n *\n * Place this action in the action list after\n * grmhd::ValenciaDivClean::Actions::ReadNumericInitialData to wait until the\n * data for this element has arrived, and then compute the remaining primitive\n * variables and store them in the DataBox to be used as initial data.\n *\n * This action modifies the tags listed in `hydro::grmhd_tags` in the DataBox\n * (i.e., the hydro primitives). It does not modify conservative variables, so\n * it relies on a primitive-to-conservative update in the action list before\n * the evolution can start.\n *\n * \\requires This action requires that the (inverse) spatial metric is available\n * through the DataBox, so it should run after GR initial data has been loaded.\n *\n * \\requires This action also requires an equation of state, which is retrieved\n * from the DataBox as `hydro::Tags::GrmhdEquationOfState`.\n */\nstruct SetNumericInitialData {\n  static constexpr size_t Dim = 3;\n  using inbox_tags =\n      tmpl::list<importers::Tags::VolumeData<NumericInitialData::all_vars>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox =\n        tuples::get<importers::Tags::VolumeData<NumericInitialData::all_vars>>(\n            inboxes);\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        db::get<evolution::initial_data::Tags::InitialData>(box));\n    const size_t volume_data_id = initial_data.volume_data_id();\n    if (inbox.find(volume_data_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    auto numeric_data = std::move(inbox.extract(volume_data_id).mapped());\n\n    const auto& inv_spatial_metric =\n        db::get<gr::Tags::InverseSpatialMetric<DataVector, Dim>>(box);\n    const auto& equation_of_state =\n        db::get<hydro::Tags::GrmhdEquationOfState>(box);\n\n    db::mutate<hydro::Tags::RestMassDensity<DataVector>,\n               hydro::Tags::ElectronFraction<DataVector>,\n               hydro::Tags::SpecificInternalEnergy<DataVector>,\n               hydro::Tags::SpatialVelocity<DataVector, 3>,\n               hydro::Tags::MagneticField<DataVector, 3>,\n               hydro::Tags::DivergenceCleaningField<DataVector>,\n               hydro::Tags::LorentzFactor<DataVector>,\n               hydro::Tags::Pressure<DataVector>,\n               hydro::Tags::Temperature<DataVector>>(\n        [&initial_data, &numeric_data, &inv_spatial_metric, &equation_of_state](\n            const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n            const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n            const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n            const gsl::not_null<tnsr::I<DataVector, 3>*> spatial_velocity,\n            const gsl::not_null<tnsr::I<DataVector, 3>*> magnetic_field,\n            const gsl::not_null<Scalar<DataVector>*> div_cleaning_field,\n            const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n            const gsl::not_null<Scalar<DataVector>*> pressure,\n            const gsl::not_null<Scalar<DataVector>*> temperature) {\n          initial_data.set_initial_data(\n              rest_mass_density, electron_fraction, specific_internal_energy,\n              spatial_velocity, magnetic_field, div_cleaning_field,\n              lorentz_factor, pressure, temperature,\n              make_not_null(&numeric_data), inv_spatial_metric,\n              equation_of_state);\n        },\n        make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/AllSolutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BlastWave.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/KhInstability.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticFieldLoop.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/OrszagTangVortex.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/PolarMagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/RiemannProblem.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SlabJet.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/Solutions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n\nnamespace grmhd::ValenciaDivClean::InitialData {\nusing initial_data_list = tmpl::list<\n    AnalyticData::BlastWave, AnalyticData::BondiHoyleAccretion,\n    AnalyticData::CcsnCollapse, AnalyticData::KhInstability,\n    AnalyticData::MagneticFieldLoop, AnalyticData::MagneticRotor,\n    AnalyticData::MagnetizedFmDisk, AnalyticData::MagnetizedTovStar,\n    AnalyticData::OrszagTangVortex, AnalyticData::PolarMagnetizedFmDisk,\n    AnalyticData::RiemannProblem, AnalyticData::SlabJet, Solutions::AlfvenWave,\n    grmhd::Solutions::BondiMichel, Solutions::KomissarovShock,\n    Solutions::SmoothFlow, RelativisticEuler::Solutions::FishboneMoncriefDisk,\n    RelativisticEuler::Solutions::RotatingStar,\n    RelativisticEuler::Solutions::TovStar>;\n}  // namespace grmhd::ValenciaDivClean::InitialData\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\nBoundaryCondition::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\nvoid BoundaryCondition::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\n/// \\brief Boundary conditions for the GRMHD Valencia Divergence Cleaning system\nnamespace BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace BoundaryConditions\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  DemandOutgoingCharSpeeds.cpp\n  DirichletAnalytic.cpp\n  HydroFreeOutflow.cpp\n  Reflective.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  DemandOutgoingCharSpeeds.hpp\n  DirichletAnalytic.hpp\n  Factory.hpp\n  HydroFreeOutflow.hpp\n  Reflective.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComputeFluxesFromPrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\nDemandOutgoingCharSpeeds::DemandOutgoingCharSpeeds(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDemandOutgoingCharSpeeds::get_clone() const {\n  return std::make_unique<DemandOutgoingCharSpeeds>(*this);\n}\n\nvoid DemandOutgoingCharSpeeds::pup(PUP::er& p) { BoundaryCondition::pup(p); }\n\nstd::optional<std::string>\nDemandOutgoingCharSpeeds::dg_demand_outgoing_char_speeds(\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        outward_directed_normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>&\n    /*outward_directed_normal_vector*/,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& lapse) {\n  double min_speed = std::numeric_limits<double>::signaling_NaN();\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>>> buffer{\n      get(lapse).size()};\n  auto& normal_dot_shift = get<::Tags::TempScalar<0>>(buffer);\n  dot_product(make_not_null(&normal_dot_shift),\n              outward_directed_normal_covector, shift);\n  if (face_mesh_velocity.has_value()) {\n    auto& normal_dot_mesh_velocity = get<::Tags::TempScalar<1>>(buffer);\n    dot_product(make_not_null(&normal_dot_mesh_velocity),\n                outward_directed_normal_covector, face_mesh_velocity.value());\n    get(normal_dot_shift) += get(normal_dot_mesh_velocity);\n  }\n  // The characteristic speeds are bounded by \\pm \\alpha - \\beta_n, and\n  // saturate that bound, so there is no need to check the hydro-dependent\n  // characteristic speeds.\n  min_speed = std::min(min(-get(lapse) - get(normal_dot_shift)),\n                       min(get(lapse) - get(normal_dot_shift)));\n  if (min_speed < 0.0) {\n    return {MakeString{}\n            << \"DemandOutgoingCharSpeeds boundary condition violated. Speed: \"\n            << min_speed << \"\\nn_i: \" << outward_directed_normal_covector\n            << \"\\n\"};\n  }\n  return std::nullopt;\n}\n\nvoid DemandOutgoingCharSpeeds::fd_demand_outgoing_char_speeds(\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> pressure,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        magnetic_field,\n    const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n\n    const gsl::not_null<std::optional<Variables<db::wrap_tags_in<\n        Flux, typename grmhd::ValenciaDivClean::System::flux_variables>>>*>\n        cell_centered_ghost_fluxes,\n\n    const Direction<3>& direction,\n\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        outward_directed_normal_covector,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n\n    // fd_interior_primitive_variables_tags\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n    const Scalar<DataVector>& interior_divergence_cleaning_field,\n\n    // fd_gridless_tags\n    const fd::Reconstructor& reconstructor) {\n  double min_char_speed = std::numeric_limits<double>::signaling_NaN();\n\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n  const size_t dim_direction{direction.dimension()};\n\n  const auto subcell_extents{subcell_mesh.extents()};\n  const size_t num_face_pts{\n      subcell_extents.slice_away(dim_direction).product()};\n\n  // The boundary condition below simply uses the outermost values on\n  // cell-centered FD grid points to compute face values on the external\n  // boundary. This is equivalent to adopting the piecewise constant (lowest\n  // order) FD reconstruction for FD cells at the external boundaries.\n  //\n  // In the future we may want to use more accurate methods (for instance,\n  // one-sided characteristic reconstruction using WENO) for imposing\n  // higher-order DemandOutgoingCharSpeeds boundary condition.\n\n  auto lapse_at_boundary = evolution::dg::subcell::slice_tensor_for_subcell(\n      interior_lapse, subcell_extents, 1, direction, {});\n  auto shift_at_boundary = evolution::dg::subcell::slice_tensor_for_subcell(\n      interior_shift, subcell_extents, 1, direction, {});\n  auto spatial_metric_at_boundary =\n      evolution::dg::subcell::slice_tensor_for_subcell(\n          interior_spatial_metric, subcell_extents, 1, direction, {});\n\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>>> buffer{\n      num_face_pts};\n\n  auto& normal_dot_shift = get<::Tags::TempScalar<0>>(buffer);\n  dot_product(make_not_null(&normal_dot_shift),\n              outward_directed_normal_covector, shift_at_boundary);\n\n  if (face_mesh_velocity.has_value()) {\n    auto& normal_dot_mesh_velocity = get<::Tags::TempScalar<1>>(buffer);\n    dot_product(make_not_null(&normal_dot_mesh_velocity),\n                outward_directed_normal_covector, face_mesh_velocity.value());\n    get(normal_dot_shift) += get(normal_dot_mesh_velocity);\n  }\n  // The characteristic speeds are bounded by \\pm \\alpha - \\beta_n, and\n  // saturate that bound, so there is no need to check the hydro-dependent\n  // characteristic speeds.\n  min_char_speed =\n      std::min(min(-get(lapse_at_boundary) - get(normal_dot_shift)),\n               min(get(lapse_at_boundary) - get(normal_dot_shift)));\n\n  if (min_char_speed < 0.0) {\n    ERROR(\n        \"Subcell DemandOutgoingCharSpeeds boundary condition violated. Speed: \"\n        << min_char_speed << \"\\nn_i: \" << outward_directed_normal_covector\n        << \"\\n\");\n  } else {\n    // Once the DemandOutgoingCharSpeeds condition has been checked, we fill\n    // each slices of the ghost data with the boundary values. The reason that\n    // we need this step is to prevent floating point exceptions being raised\n    // while computing the subcell time derivative because of NaN or\n    // uninitialized values in ghost data.\n\n    using RestMassDensity = hydro::Tags::RestMassDensity<DataVector>;\n    using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n    using Pressure = hydro::Tags::Pressure<DataVector>;\n    using SpecificInternalEnergy =\n        hydro::Tags::SpecificInternalEnergy<DataVector>;\n    using LorentzFactorTimesSpatialVelocity =\n        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n    using MagneticField = hydro::Tags::MagneticField<DataVector, 3>;\n    using DivergenceCleaningField =\n        hydro::Tags::DivergenceCleaningField<DataVector>;\n    using SpatialVelocity = hydro::Tags::SpatialVelocity<DataVector, 3>;\n    using LorentzFactor = hydro::Tags::LorentzFactor<DataVector>;\n    using SqrtDetSpatialMetric = gr::Tags::SqrtDetSpatialMetric<DataVector>;\n    using SpatialMetric = gr::Tags::SpatialMetric<DataVector, 3>;\n    using InvSpatialMetric = gr::Tags::InverseSpatialMetric<DataVector, 3>;\n    using Lapse = gr::Tags::Lapse<DataVector>;\n    using Shift = gr::Tags::Shift<DataVector, 3>;\n\n    using prim_tags_for_reconstruction =\n        tmpl::list<RestMassDensity, ElectronFraction, Pressure,\n                   LorentzFactorTimesSpatialVelocity, MagneticField,\n                   DivergenceCleaningField>;\n    using fluxes_tags =\n        tmpl::push_back<typename System::primitive_variables_tag::tags_list,\n                        LorentzFactorTimesSpatialVelocity, SpatialMetric, Lapse,\n                        Shift, SqrtDetSpatialMetric, InvSpatialMetric>;\n\n    const bool need_tags_for_fluxes = cell_centered_ghost_fluxes->has_value();\n    // Create a single large DV to reduce the number of Variables allocations\n    const size_t buffer_size_for_fluxes =\n        need_tags_for_fluxes\n            ? Variables<fluxes_tags>::number_of_independent_components\n            : 0;\n    const size_t buffer_size_per_grid_pts = Variables<\n        prim_tags_for_reconstruction>::number_of_independent_components;\n    DataVector buffer_for_boundary_and_ghost_vars{\n        (buffer_size_per_grid_pts + buffer_size_for_fluxes) * num_face_pts *\n            (1 + ghost_zone_size),\n        0.0};\n\n    // a Variables object to store prim variables on outermost layer of FD grid\n    // points\n    Variables<prim_tags_for_reconstruction> boundary_vars{\n        buffer_for_boundary_and_ghost_vars.data(),\n        buffer_size_per_grid_pts * num_face_pts};\n    // a Variables object to store prim variables on ghost zone\n    Variables<prim_tags_for_reconstruction> ghost_vars{\n        std::next(buffer_for_boundary_and_ghost_vars.data(),\n                  static_cast<std::ptrdiff_t>(boundary_vars.size())),\n        buffer_size_per_grid_pts * num_face_pts * ghost_zone_size};\n\n    auto get_boundary_val = [&direction, &subcell_extents](auto volume_tensor) {\n      return evolution::dg::subcell::slice_tensor_for_subcell(\n          volume_tensor, subcell_extents, 1, direction, {});\n    };\n\n    get<RestMassDensity>(boundary_vars) =\n        get_boundary_val(interior_rest_mass_density);\n    get<ElectronFraction>(boundary_vars) =\n        get_boundary_val(interior_electron_fraction);\n    get<Pressure>(boundary_vars) = get_boundary_val(interior_pressure);\n    // Note : 'lorentz factor times spatial velocity' needs to be in the FD\n    // ghost data for reconstruction, instead of lorentz factor and spatial\n    // velocity separately.\n    for (size_t i = 0; i < 3; ++i) {\n      get<LorentzFactorTimesSpatialVelocity>(boundary_vars).get(i) =\n          get(get_boundary_val(interior_lorentz_factor)) *\n          get_boundary_val(interior_spatial_velocity).get(i);\n    }\n    get<MagneticField>(boundary_vars) =\n        get_boundary_val(interior_magnetic_field);\n    get<DivergenceCleaningField>(boundary_vars) =\n        get_boundary_val(interior_divergence_cleaning_field);\n\n    // Next, copy boundary values into each slices of ghost zone vars\n    Index<3> ghost_data_extents = subcell_extents;\n    ghost_data_extents[dim_direction] = ghost_zone_size;\n\n    for (size_t i_ghost = 0; i_ghost < ghost_zone_size; ++i_ghost) {\n      add_slice_to_data(make_not_null(&ghost_vars), boundary_vars,\n                        ghost_data_extents, dim_direction, i_ghost);\n    }\n\n    *rest_mass_density = get<RestMassDensity>(ghost_vars);\n    *electron_fraction = get<ElectronFraction>(ghost_vars);\n    *pressure = get<Pressure>(ghost_vars);\n    *lorentz_factor_times_spatial_velocity =\n        get<LorentzFactorTimesSpatialVelocity>(ghost_vars);\n    *magnetic_field = get<MagneticField>(ghost_vars);\n    *divergence_cleaning_field = get<DivergenceCleaningField>(ghost_vars);\n\n    if (need_tags_for_fluxes) {\n      Variables<fluxes_tags> outermost_fluxes_vars{\n          std::next(ghost_vars.data(),\n                    static_cast<std::ptrdiff_t>(ghost_vars.size())),\n          num_face_pts * buffer_size_for_fluxes};\n      Variables<fluxes_tags> ghost_fluxes_vars{\n          std::next(outermost_fluxes_vars.data(),\n                    static_cast<std::ptrdiff_t>(outermost_fluxes_vars.size())),\n          num_face_pts * buffer_size_for_fluxes * ghost_zone_size};\n\n      get<RestMassDensity>(ghost_fluxes_vars) = *rest_mass_density;\n      get<ElectronFraction>(ghost_fluxes_vars) = *electron_fraction;\n      get<Pressure>(ghost_fluxes_vars) = *pressure;\n      get<MagneticField>(ghost_fluxes_vars) = *magnetic_field;\n      get<DivergenceCleaningField>(ghost_fluxes_vars) =\n          *divergence_cleaning_field;\n\n      get<SpecificInternalEnergy>(outermost_fluxes_vars) =\n          get_boundary_val(interior_specific_internal_energy);\n      get<SpatialMetric>(outermost_fluxes_vars) =\n          get_boundary_val(interior_spatial_metric);\n      get<Lapse>(outermost_fluxes_vars) = get_boundary_val(interior_lapse);\n      get<Shift>(outermost_fluxes_vars) = get_boundary_val(interior_shift);\n\n      for (size_t i_ghost = 0; i_ghost < ghost_zone_size; ++i_ghost) {\n        add_slice_to_data(make_not_null(&ghost_fluxes_vars),\n                          outermost_fluxes_vars, ghost_data_extents,\n                          dim_direction, i_ghost);\n      }\n\n      determinant_and_inverse(\n          make_not_null(&get<SqrtDetSpatialMetric>(ghost_fluxes_vars)),\n          make_not_null(&get<InvSpatialMetric>(ghost_fluxes_vars)),\n          get<SpatialMetric>(ghost_fluxes_vars));\n      get(get<SqrtDetSpatialMetric>(ghost_fluxes_vars)) =\n          sqrt(get(get<SqrtDetSpatialMetric>(ghost_fluxes_vars)));\n      tenex::evaluate(\n          make_not_null(&get<LorentzFactor>(ghost_fluxes_vars)),\n          sqrt(1.0 + get<SpatialMetric>(ghost_fluxes_vars)(ti::i, ti::j) *\n                         (*lorentz_factor_times_spatial_velocity)(ti::I) *\n                         (*lorentz_factor_times_spatial_velocity)(ti::J)));\n      tenex::evaluate<ti::I>(\n          make_not_null(&get<SpatialVelocity>(ghost_fluxes_vars)),\n          (*lorentz_factor_times_spatial_velocity)(ti::I) /\n              get<LorentzFactor>(ghost_fluxes_vars)());\n\n      Variables<typename System::variables_tag::tags_list> temp_vars(\n          get(*rest_mass_density).size());\n\n      compute_fluxes_from_primitives(\n          make_not_null(&cell_centered_ghost_fluxes->value()),\n          ghost_fluxes_vars);\n    }\n  }\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DemandOutgoingCharSpeeds::my_PUP_ID = 0;\n\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DemandOutgoingCharSpeeds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\n/// A `BoundaryCondition` that only verifies that all characteristic speeds are\n/// directed out of the domain; no boundary data is altered by this boundary\n/// condition.\nclass DemandOutgoingCharSpeeds final : public BoundaryCondition {\n private:\n  template <typename T>\n  using Flux = ::Tags::Flux<T, tmpl::size_t<3>, Frame::Inertial>;\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"DemandOutgoingCharSpeeds boundary condition that only verifies the \"\n      \"characteristic speeds \"\n      \"are all directed out of the domain.\"};\n\n  DemandOutgoingCharSpeeds() = default;\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<gr::Tags::Shift<DataVector, 3>, gr::Tags::Lapse<DataVector>>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n      /*outward_directed_normal_vector*/,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const Scalar<DataVector>& lapse);\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>,\n                 gr::Tags::Shift<DataVector, 3>, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>>;\n  using fd_interior_primitive_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n  using fd_gridless_tags = tmpl::list<fd::Tags::Reconstructor>;\n\n  static void fd_demand_outgoing_char_speeds(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          lorentz_factor_times_spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n\n      gsl::not_null<std::optional<Variables<db::wrap_tags_in<\n          Flux, typename grmhd::ValenciaDivClean::System::flux_variables>>>*>\n          cell_centered_ghost_fluxes,\n\n      const Direction<3>& direction,\n\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          outward_directed_normal_covector,\n\n      // fd_interior_temporary_tags\n      const Mesh<3>& subcell_mesh,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n\n      // fd_interior_primitive_variables_tags\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n      const Scalar<DataVector>& interior_divergence_cleaning_field,\n\n      // fd_gridless_tags\n      const fd::Reconstructor& reconstructor);\n};\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComputeFluxesFromPrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\nDirichletAnalytic::DirichletAnalytic(const DirichletAnalytic& rhs)\n    : BoundaryCondition{dynamic_cast<const BoundaryCondition&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\nDirichletAnalytic& DirichletAnalytic::operator=(const DirichletAnalytic& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\nDirichletAnalytic::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\nDirichletAnalytic::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic::get_clone() const {\n  return std::make_unique<DirichletAnalytic>(*this);\n}\n\nvoid DirichletAnalytic::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | analytic_prescription_;\n}\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic::my_PUP_ID = 0;\n\nstd::optional<std::string> DirichletAnalytic::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity_one_form,\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<\n        tnsr::I<DataVector, 3, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    [[maybe_unused]] const double time) const {\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataVector>,\n                          hydro::Tags::ElectronFraction<DataVector>,\n                          hydro::Tags::SpecificInternalEnergy<DataVector>,\n                          hydro::Tags::Pressure<DataVector>,\n                          hydro::Tags::Temperature<DataVector>,\n                          hydro::Tags::SpatialVelocity<DataVector, 3>,\n                          hydro::Tags::LorentzFactor<DataVector>,\n                          hydro::Tags::MagneticField<DataVector, 3>,\n                          hydro::Tags::DivergenceCleaningField<DataVector>,\n                          gr::Tags::SpatialMetric<DataVector, 3>,\n                          gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                          gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                          gr::Tags::Lapse<DataVector>,\n                          gr::Tags::Shift<DataVector, 3>>,\n      grmhd::ValenciaDivClean::InitialData::initial_data_list>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const initial_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(\n              coords, time,\n              tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                         hydro::Tags::ElectronFraction<DataVector>,\n                         hydro::Tags::SpecificInternalEnergy<DataVector>,\n                         hydro::Tags::Pressure<DataVector>,\n                         hydro::Tags::Temperature<DataVector>,\n                         hydro::Tags::SpatialVelocity<DataVector, 3>,\n                         hydro::Tags::LorentzFactor<DataVector>,\n                         hydro::Tags::MagneticField<DataVector, 3>,\n                         hydro::Tags::DivergenceCleaningField<DataVector>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                         gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>>{});\n\n        } else {\n          (void)time;\n          return initial_data->variables(\n              coords,\n              tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                         hydro::Tags::ElectronFraction<DataVector>,\n                         hydro::Tags::SpecificInternalEnergy<DataVector>,\n                         hydro::Tags::Pressure<DataVector>,\n                         hydro::Tags::Temperature<DataVector>,\n                         hydro::Tags::SpatialVelocity<DataVector, 3>,\n                         hydro::Tags::LorentzFactor<DataVector>,\n                         hydro::Tags::MagneticField<DataVector, 3>,\n                         hydro::Tags::DivergenceCleaningField<DataVector>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                         gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>>{});\n        }\n      });\n  // Recover values from analytic solution/ analytic data calls\n  *lapse = get<gr::Tags::Lapse<DataVector>>(boundary_values);\n  *shift = get<gr::Tags::Shift<DataVector, 3>>(boundary_values);\n  *inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_values);\n  *rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(boundary_values);\n  *electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataVector>>(boundary_values);\n  *temperature = get<hydro::Tags::Temperature<DataVector>>(boundary_values);\n  *spatial_velocity =\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values);\n  tenex::evaluate<ti::i>(\n      spatial_velocity_one_form,\n      (*spatial_velocity)(ti::J) * (get<gr::Tags::SpatialMetric<DataVector, 3>>(\n                                       boundary_values)(ti::i, ti::j)));\n  // Recover the conservative variables from the primitives\n  ConservativeFromPrimitive::apply(\n      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n      get<hydro::Tags::RestMassDensity<DataVector>>(boundary_values),\n      get<hydro::Tags::ElectronFraction<DataVector>>(boundary_values),\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(boundary_values),\n      get<hydro::Tags::Pressure<DataVector>>(boundary_values),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values),\n      get<hydro::Tags::LorentzFactor<DataVector>>(boundary_values),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(boundary_values),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(boundary_values),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_values),\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(boundary_values));\n\n  ComputeFluxes::apply(\n      tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux, tilde_b_flux,\n      tilde_phi_flux, *tilde_d, *tilde_ye, *tilde_tau, *tilde_s, *tilde_b,\n      *tilde_phi, *lapse, *shift,\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(boundary_values),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_values),\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_values),\n      get<hydro::Tags::Pressure<DataVector>>(boundary_values),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values),\n      get<hydro::Tags::LorentzFactor<DataVector>>(boundary_values),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(boundary_values));\n\n  return {};\n}\n\nvoid DirichletAnalytic::fd_ghost(\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        magnetic_field,\n    const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n\n    const gsl::not_null<std::optional<Variables<db::wrap_tags_in<\n        Flux, typename grmhd::ValenciaDivClean::System::flux_variables>>>*>\n        cell_centered_ghost_fluxes,\n\n    const Direction<3>& direction,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n\n    // fd_gridless_tags\n    [[maybe_unused]] const double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n        grid_to_inertial_map,\n    const fd::Reconstructor& reconstructor) const {\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n\n  const auto ghost_logical_coords =\n      evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n          subcell_mesh, ghost_zone_size, direction);\n\n  const auto ghost_inertial_coords = grid_to_inertial_map(\n      logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n  // Compute FD ghost data with the analytic data or solution\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataVector>,\n                          hydro::Tags::ElectronFraction<DataVector>,\n                          hydro::Tags::Pressure<DataVector>,\n                          hydro::Tags::Temperature<DataVector>,\n                          hydro::Tags::SpatialVelocity<DataVector, 3>,\n                          hydro::Tags::LorentzFactor<DataVector>,\n                          hydro::Tags::MagneticField<DataVector, 3>,\n                          hydro::Tags::DivergenceCleaningField<DataVector>,\n                          hydro::Tags::SpecificInternalEnergy<DataVector>>,\n      grmhd::ValenciaDivClean::InitialData::initial_data_list>(\n      analytic_prescription_.get(),\n      [&ghost_inertial_coords, &time](const auto* const initial_data) {\n        using hydro_tags =\n            tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                       hydro::Tags::ElectronFraction<DataVector>,\n                       hydro::Tags::Pressure<DataVector>,\n                       hydro::Tags::Temperature<DataVector>,\n                       hydro::Tags::SpatialVelocity<DataVector, 3>,\n                       hydro::Tags::LorentzFactor<DataVector>,\n                       hydro::Tags::MagneticField<DataVector, 3>,\n                       hydro::Tags::DivergenceCleaningField<DataVector>,\n                       hydro::Tags::SpecificInternalEnergy<DataVector>>;\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(ghost_inertial_coords, time,\n                                         hydro_tags{});\n        } else {\n          (void)time;\n          return initial_data->variables(ghost_inertial_coords, hydro_tags{});\n        }\n      });\n\n  *rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(boundary_values);\n  *electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataVector>>(boundary_values);\n  *temperature = get<hydro::Tags::Temperature<DataVector>>(boundary_values);\n\n  for (size_t i = 0; i < 3; ++i) {\n    auto& lorentz_factor =\n        get<hydro::Tags::LorentzFactor<DataVector>>(boundary_values);\n    auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values);\n    (*lorentz_factor_times_spatial_velocity).get(i) =\n        get(lorentz_factor) * spatial_velocity.get(i);\n  }\n\n  *magnetic_field =\n      get<hydro::Tags::MagneticField<DataVector, 3>>(boundary_values);\n  *divergence_cleaning_field =\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(boundary_values);\n\n  if (cell_centered_ghost_fluxes->has_value()) {\n    auto metric_boundary_values = call_with_dynamic_type<\n        tuples::TaggedTuple<gr::Tags::Lapse<DataVector>,\n                            gr::Tags::Shift<DataVector, 3>,\n                            gr::Tags::SpatialMetric<DataVector, 3>,\n                            gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                            gr::Tags::InverseSpatialMetric<DataVector, 3>>,\n        grmhd::ValenciaDivClean::InitialData::initial_data_list>(\n        analytic_prescription_.get(),\n        [&ghost_inertial_coords, &time](const auto* const initial_data) {\n          using gr_tags =\n              tmpl::list<gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n          if constexpr (is_analytic_solution_v<\n                            std::decay_t<decltype(*initial_data)>>) {\n            return initial_data->variables(ghost_inertial_coords, time,\n                                           gr_tags{});\n          } else {\n            (void)time;\n            return initial_data->variables(ghost_inertial_coords, gr_tags{});\n          }\n        });\n\n    compute_fluxes_from_primitives(\n        make_not_null(&cell_centered_ghost_fluxes->value()),\n        tagged_tuple_cat(std::move(boundary_values),\n                         std::move(metric_boundary_values)));\n  }\n}\n\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\nclass DirichletAnalytic final : public BoundaryCondition {\n private:\n  template <typename T>\n  using Flux = ::Tags::Flux<T, tmpl::size_t<3>, Frame::Inertial>;\n\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n  using options = tmpl::list<AnalyticPrescription>;\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions using either analytic solution or \"\n      \"analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n      gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<tnsr::i<DataVector, 3,\n                            Frame::Inertial>*> /*spatial_velocity_one_form*/,\n      gsl::not_null<Scalar<DataVector>*> /*rest_mass_density*/,\n      gsl::not_null<Scalar<DataVector>*> /*electron_fraction*/,\n      gsl::not_null<Scalar<DataVector>*> /*temperature*/,\n      gsl::not_null<\n          tnsr::I<DataVector, 3, Frame::Inertial>*> /*spatial_velocity*/,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<\n          tnsr::I<DataVector, 3, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n      [[maybe_unused]] double time) const;\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>>;\n  using fd_interior_primitive_variables_tags = tmpl::list<>;\n  using fd_gridless_tags =\n      tmpl::list<::Tags::Time, ::domain::Tags::FunctionsOfTime,\n                 domain::Tags::ElementMap<3, Frame::Grid>,\n                 domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                             Frame::Inertial>,\n                 fd::Tags::Reconstructor>;\n  void fd_ghost(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          lorentz_factor_times_spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n\n      gsl::not_null<std::optional<Variables<db::wrap_tags_in<\n          Flux, typename grmhd::ValenciaDivClean::System::flux_variables>>>*>\n          cell_centered_ghost_fluxes,\n\n      const Direction<3>& direction,\n\n      // fd_interior_temporary_tags\n      const Mesh<3>& subcell_mesh,\n\n      // fd_gridless_tags\n      [[maybe_unused]] double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const ElementMap<3, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n          grid_to_inertial_map,\n      const fd::Reconstructor& reconstructor) const;\n\n private:\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Reflective.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\nusing standard_boundary_conditions =\n    tmpl::list<DemandOutgoingCharSpeeds, DirichletAnalytic, HydroFreeOutflow,\n               Reflective,\n               domain::BoundaryConditions::Periodic<BoundaryCondition>>;\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/Evaluate.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComputeFluxesFromPrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\nHydroFreeOutflow::HydroFreeOutflow(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nHydroFreeOutflow::get_clone() const {\n  return std::make_unique<HydroFreeOutflow>(*this);\n}\n\nvoid HydroFreeOutflow::pup(PUP::er& p) { BoundaryCondition::pup(p); }\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID HydroFreeOutflow::my_PUP_ID = 0;\n\nstd::optional<std::string> HydroFreeOutflow::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity_one_form,\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n    /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        outward_directed_normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>&\n        outward_directed_normal_vector,\n\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_temperature,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::II<DataVector, 3, Frame::Inertial>&\n        interior_inv_spatial_metric) {\n  const size_t number_of_grid_points = get(interior_rest_mass_density).size();\n\n  // temp buffer to store\n  //  * n_i v^i\n  //  * v_{ghost}^i\n  //  * divergence cleaning field on ghost zone\n  //  * spatial metric\n  //  * sqrt determinant of spatial metric\n  Variables<tmpl::list<\n      ::Tags::TempScalar<0>, hydro::Tags::SpatialVelocity<DataVector, 3>,\n      ::Tags::TempScalar<1>, gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>>>\n      temp_buffer{number_of_grid_points};\n  auto& normal_dot_interior_spatial_velocity =\n      get<::Tags::TempScalar<0>>(temp_buffer);\n  *spatial_velocity =\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(temp_buffer);\n  auto& exterior_divergence_cleaning_field =\n      get<::Tags::TempScalar<1>>(temp_buffer);\n  auto& interior_spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(temp_buffer);\n  auto& interior_sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(temp_buffer);\n\n  get(*lapse) = get(interior_lapse);\n  for (size_t i = 0; i < 3; ++i) {\n    (*shift).get(i) = interior_shift.get(i);\n    for (size_t j = 0; j < 3; ++j) {\n      (*inv_spatial_metric).get(i, j) = interior_inv_spatial_metric.get(i, j);\n    }\n  }\n\n  // spatial metric and sqrt determinant of spatial metric can be retrived from\n  // Databox but only as gridless_tags with whole volume data (unlike all the\n  // other arguments which are face tensors). Rather than doing expensive tensor\n  // slicing operations on those, we just compute those two quantities from\n  // inverse spatial metric as below.\n  determinant_and_inverse(make_not_null(&interior_sqrt_det_spatial_metric),\n                          make_not_null(&interior_spatial_metric),\n                          interior_inv_spatial_metric);\n  get(interior_sqrt_det_spatial_metric) =\n      1.0 / sqrt(get(interior_sqrt_det_spatial_metric));\n\n  // copy-paste interior spatial velocity to exterior spatial velocity, but\n  // kill ingoing normal component to zero\n  dot_product(make_not_null(&normal_dot_interior_spatial_velocity),\n              outward_directed_normal_covector, interior_spatial_velocity);\n  for (size_t i = 0; i < number_of_grid_points; ++i) {\n    if (get(normal_dot_interior_spatial_velocity)[i] >= 0.0) {\n      for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n        spatial_velocity->get(spatial_index)[i] =\n            interior_spatial_velocity.get(spatial_index)[i];\n      }\n    } else {\n      for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n        spatial_velocity->get(spatial_index)[i] =\n            interior_spatial_velocity.get(spatial_index)[i] -\n            get(normal_dot_interior_spatial_velocity)[i] *\n                outward_directed_normal_vector.get(spatial_index)[i];\n      }\n    }\n  }\n\n  get(exterior_divergence_cleaning_field) = 0.0;\n\n  *rest_mass_density = interior_rest_mass_density;\n  *electron_fraction = interior_electron_fraction;\n  *temperature = interior_temperature;\n  tenex::evaluate<ti::i>(\n      spatial_velocity_one_form,\n      (*spatial_velocity)(ti::J) * (interior_spatial_metric(ti::i, ti::j)));\n\n  ConservativeFromPrimitive::apply(\n      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n      interior_rest_mass_density, interior_electron_fraction,\n      interior_specific_internal_energy, interior_pressure, *spatial_velocity,\n      interior_lorentz_factor, interior_magnetic_field,\n      interior_sqrt_det_spatial_metric, interior_spatial_metric,\n      exterior_divergence_cleaning_field);\n\n  ComputeFluxes::apply(\n      tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux, tilde_b_flux,\n      tilde_phi_flux, *tilde_d, *tilde_ye, *tilde_tau, *tilde_s, *tilde_b,\n      *tilde_phi, *lapse, *shift, interior_sqrt_det_spatial_metric,\n      interior_spatial_metric, *inv_spatial_metric, interior_pressure,\n      *spatial_velocity, interior_lorentz_factor, interior_magnetic_field);\n\n  return {};\n}\n\nnamespace {\ntemplate <typename T>\nusing Flux = ::Tags::Flux<T, tmpl::size_t<3>, Frame::Inertial>;\n}  // namespace\n\nvoid HydroFreeOutflow::fd_ghost(\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        magnetic_field,\n    const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n\n    const gsl::not_null<std::optional<Variables<db::wrap_tags_in<\n        ::Tags::Flux, typename grmhd::ValenciaDivClean::System::flux_variables,\n        tmpl::size_t<3>, Frame::Inertial>>>*>\n        cell_centered_ghost_fluxes,\n\n    const Direction<3>& direction,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n\n    // fd_interior_primitive_variables_tags\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_temperature,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n\n    // fd_gridless_tags\n    const fd::Reconstructor& reconstructor) {\n  Variables<tmpl::push_back<\n      tmpl::append<typename System::variables_tag::tags_list,\n                   typename System::primitive_variables_tag::tags_list>,\n      SqrtDetSpatialMetric, SpatialMetric, InvSpatialMetric, Lapse, Shift>>\n      temp_vars{get(*rest_mass_density).size()};\n\n  fd_ghost_impl(make_not_null(&get<RestMassDensity>(temp_vars)),\n                make_not_null(&get<ElectronFraction>(temp_vars)),\n                make_not_null(&get<Temperature>(temp_vars)),\n                make_not_null(&get<Pressure>(temp_vars)),\n                make_not_null(&get<SpecificInternalEnergy>(temp_vars)),\n                lorentz_factor_times_spatial_velocity,\n                make_not_null(&get<SpatialVelocity>(temp_vars)),\n                make_not_null(&get<LorentzFactor>(temp_vars)),\n                make_not_null(&get<MagneticField>(temp_vars)),\n                make_not_null(&get<DivergenceCleaningField>(temp_vars)),\n                make_not_null(&get<SpatialMetric>(temp_vars)),\n                make_not_null(&get<InvSpatialMetric>(temp_vars)),\n                make_not_null(&get<SqrtDetSpatialMetric>(temp_vars)),\n                make_not_null(&get<Lapse>(temp_vars)),\n                make_not_null(&get<Shift>(temp_vars)),\n\n                direction,\n\n                // fd_interior_temporary_tags\n                subcell_mesh,\n\n                // fd_interior_primitive_variables_tags\n                interior_rest_mass_density, interior_electron_fraction,\n                interior_temperature, interior_pressure,\n                interior_specific_internal_energy, interior_lorentz_factor,\n                interior_spatial_velocity, interior_magnetic_field,\n                interior_spatial_metric, interior_lapse, interior_shift,\n\n                reconstructor.ghost_zone_size(),\n\n                cell_centered_ghost_fluxes->has_value());\n\n  if (cell_centered_ghost_fluxes->has_value()) {\n    compute_fluxes_from_primitives(\n        make_not_null(&cell_centered_ghost_fluxes->value()), temp_vars);\n  }\n\n  *rest_mass_density = get<hydro::Tags::RestMassDensity<DataVector>>(temp_vars);\n  *electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataVector>>(temp_vars);\n  *temperature = get<hydro::Tags::Temperature<DataVector>>(temp_vars);\n  *magnetic_field = get<hydro::Tags::MagneticField<DataVector, 3>>(temp_vars);\n  *divergence_cleaning_field =\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(temp_vars);\n}\n\nvoid HydroFreeOutflow::fd_ghost_impl(\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<Scalar<DataVector>*> pressure,\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        magnetic_field,\n    const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n    const gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*>\n        spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> sqrt_det_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n\n    const Direction<3>& direction,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n\n    // fd_interior_primitive_variables_tags\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_temperature,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n\n    const size_t ghost_zone_size, const bool need_tags_for_fluxes) {\n  const size_t dim_direction{direction.dimension()};\n\n  const auto subcell_extents{subcell_mesh.extents()};\n  const size_t num_face_pts{\n      subcell_extents.slice_away(dim_direction).product()};\n\n  using prim_tags_without_cleaning_field =\n      tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                 LorentzFactorTimesSpatialVelocity, MagneticField>;\n\n  // Create a single large DV to reduce the number of Variables allocations\n  using fluxes_tags =\n      tmpl::list<Pressure, SpecificInternalEnergy, SpatialMetric, Lapse, Shift>;\n  const size_t buffer_size_for_fluxes =\n      need_tags_for_fluxes\n          ? Variables<fluxes_tags>::number_of_independent_components\n          : 0;\n  const size_t buffer_size_per_grid_pts = Variables<\n      prim_tags_without_cleaning_field>::number_of_independent_components;\n  DataVector buffer_for_vars{\n      num_face_pts * ((1 + ghost_zone_size) *\n                      (buffer_size_per_grid_pts + buffer_size_for_fluxes)),\n      0.0};\n\n  // Assign two Variables object to the buffer\n  Variables<prim_tags_without_cleaning_field> outermost_prim_vars{\n      buffer_for_vars.data(), num_face_pts * buffer_size_per_grid_pts};\n  Variables<prim_tags_without_cleaning_field> ghost_prim_vars{\n      outermost_prim_vars.data() + outermost_prim_vars.size(),\n      num_face_pts * buffer_size_per_grid_pts * ghost_zone_size};\n\n  // Slice values on the outermost grid points and store them to\n  // `outermost_prim_vars`\n  auto get_boundary_val = [&direction, &subcell_extents](auto volume_tensor) {\n    return evolution::dg::subcell::slice_tensor_for_subcell(\n        volume_tensor, subcell_extents, 1, direction, {});\n  };\n\n  get<RestMassDensity>(outermost_prim_vars) =\n      get_boundary_val(interior_rest_mass_density);\n  get<ElectronFraction>(outermost_prim_vars) =\n      get_boundary_val(interior_electron_fraction);\n  get<Temperature>(outermost_prim_vars) =\n      get_boundary_val(interior_temperature);\n\n  {\n    // Kill ingoing components of spatial velocity and compute Wv^i\n    //\n    // Note : Here we require the grid to be Cartesian, therefore we will need\n    // to change the implementation below once subcell supports curved mesh.\n    const auto normal_spatial_velocity_at_boundary =\n        get_boundary_val(interior_spatial_velocity).get(dim_direction);\n    for (size_t i = 0; i < 3; ++i) {\n      if (i == dim_direction) {\n        if (direction.sign() > 0.0) {\n          get<LorentzFactorTimesSpatialVelocity>(outermost_prim_vars).get(i) =\n              get(get_boundary_val(interior_lorentz_factor)) *\n              max(normal_spatial_velocity_at_boundary,\n                  normal_spatial_velocity_at_boundary * 0.0);\n        } else {\n          get<LorentzFactorTimesSpatialVelocity>(outermost_prim_vars).get(i) =\n              get(get_boundary_val(interior_lorentz_factor)) *\n              min(normal_spatial_velocity_at_boundary,\n                  normal_spatial_velocity_at_boundary * 0.0);\n        }\n      } else {\n        get<LorentzFactorTimesSpatialVelocity>(outermost_prim_vars).get(i) =\n            get(get_boundary_val(interior_lorentz_factor)) *\n            get_boundary_val(interior_spatial_velocity).get(i);\n      }\n\n      get<MagneticField>(outermost_prim_vars).get(i) =\n          get_boundary_val(interior_magnetic_field).get(i);\n    }\n  }\n\n  // Now copy `outermost_prim_vars` into each slices of `ghost_prim_vars`.\n  Index<3> ghost_data_extents = subcell_extents;\n  ghost_data_extents[dim_direction] = ghost_zone_size;\n\n  for (size_t i_ghost = 0; i_ghost < ghost_zone_size; ++i_ghost) {\n    add_slice_to_data(make_not_null(&ghost_prim_vars), outermost_prim_vars,\n                      ghost_data_extents, dim_direction, i_ghost);\n  }\n\n  *rest_mass_density = get<RestMassDensity>(ghost_prim_vars);\n  *electron_fraction = get<ElectronFraction>(ghost_prim_vars);\n  *temperature = get<Temperature>(ghost_prim_vars);\n  *lorentz_factor_times_spatial_velocity =\n      get<LorentzFactorTimesSpatialVelocity>(ghost_prim_vars);\n  *magnetic_field = get<MagneticField>(ghost_prim_vars);\n\n  // divergence cleaning scalar field is just set to zero in the ghost zone.\n  get(*divergence_cleaning_field) = 0.0;\n\n  if (need_tags_for_fluxes) {\n    Variables<fluxes_tags> outermost_fluxes_vars{\n        std::next(ghost_prim_vars.data(),\n                  static_cast<std::ptrdiff_t>(ghost_prim_vars.size())),\n        num_face_pts * buffer_size_for_fluxes};\n    Variables<fluxes_tags> ghost_fluxes_vars{\n        std::next(outermost_fluxes_vars.data(),\n                  static_cast<std::ptrdiff_t>(outermost_fluxes_vars.size())),\n        num_face_pts * buffer_size_for_fluxes * ghost_zone_size};\n\n    get<Pressure>(outermost_fluxes_vars) = get_boundary_val(interior_pressure);\n    get<SpecificInternalEnergy>(outermost_fluxes_vars) =\n        get_boundary_val(interior_specific_internal_energy);\n    get<SpatialMetric>(outermost_fluxes_vars) =\n        get_boundary_val(interior_spatial_metric);\n    get<Lapse>(outermost_fluxes_vars) = get_boundary_val(interior_lapse);\n    get<Shift>(outermost_fluxes_vars) = get_boundary_val(interior_shift);\n\n    for (size_t i_ghost = 0; i_ghost < ghost_zone_size; ++i_ghost) {\n      add_slice_to_data(make_not_null(&ghost_fluxes_vars),\n                        outermost_fluxes_vars, ghost_data_extents,\n                        dim_direction, i_ghost);\n    }\n    // Need pressure for high-order finite difference\n    *pressure = get<Pressure>(ghost_fluxes_vars);\n    *specific_internal_energy = get<SpecificInternalEnergy>(ghost_fluxes_vars);\n    *spatial_metric = get<SpatialMetric>(ghost_fluxes_vars);\n    *lapse = get<Lapse>(ghost_fluxes_vars);\n    *shift = get<Shift>(ghost_fluxes_vars);\n\n    determinant_and_inverse(sqrt_det_spatial_metric, inv_spatial_metric,\n                            *spatial_metric);\n    get(*sqrt_det_spatial_metric) = sqrt(get(*sqrt_det_spatial_metric));\n    tenex::evaluate(\n        lorentz_factor,\n        sqrt(1.0 + (*spatial_metric)(ti::i, ti::j) *\n                       (*lorentz_factor_times_spatial_velocity)(ti::I) *\n                       (*lorentz_factor_times_spatial_velocity)(ti::J)));\n    tenex::evaluate<ti::I>(\n        spatial_velocity,\n        (*lorentz_factor_times_spatial_velocity)(ti::I) / (*lorentz_factor)());\n  }\n}\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Direction;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\n/*!\n * \\brief Apply hydrodynamic free outflow and no inflow boundary condition to\n * GRMHD primitive variables.\n *\n * All primitive variables at the boundary are copied into ghost zone except :\n *\n *  - If \\f$n_iv^i \\geq 0\\f$ where \\f$v^i\\f$ is spatial velocity and \\f$n_i\\f$\n * is outward directed normal covector, copy the values of \\f$v^i\\f$ at the\n * boundary to ghost zone. If \\f$n_iv^i<0\\f$, spatial velocity in the ghost zone\n * is modified such that the normal component is zero at the interface i.e.\n * \\f$v_\\text{ghost}^i = v^i - (n_jv^j)n^i\\f$.\n *\n *  - However, regardless of whether the normal component of the spatial\n * velocity $n_iv^i$ is pointing outward or inward, the lorentz factor \\f$W\\f$\n * is copied into ghost zone without any modification.\n *\n * \\note In principle we need to recompute the Lorentz factor\n * \\f$W_\\text{ghost}\\f$ using $v_\\text{ghost}^i$. However, the case in which\n * skipping that procedure becomes problematic is only when the fluid has\n * relativistic incoming normal speed on the external boundary, which already\n * implies the failure of adopting outflow type boundary conditions.\n *\n *  - Divergence cleaning scalar field \\f$\\Phi\\f$ is set to zero in ghost zone.\n *\n */\nclass HydroFreeOutflow final : public BoundaryCondition {\n private:\n  using RestMassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n  using LorentzFactorTimesSpatialVelocity =\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n  using MagneticField = hydro::Tags::MagneticField<DataVector, 3>;\n  using DivergenceCleaningField =\n      hydro::Tags::DivergenceCleaningField<DataVector>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using SpatialVelocity = hydro::Tags::SpatialVelocity<DataVector, 3>;\n  using LorentzFactor = hydro::Tags::LorentzFactor<DataVector>;\n  using SqrtDetSpatialMetric = gr::Tags::SqrtDetSpatialMetric<DataVector>;\n  using SpatialMetric = gr::Tags::SpatialMetric<DataVector, 3>;\n  using InvSpatialMetric = gr::Tags::InverseSpatialMetric<DataVector, 3>;\n  using Lapse = gr::Tags::Lapse<DataVector>;\n  using Shift = gr::Tags::Shift<DataVector, 3>;\n\n  using prim_tags_for_reconstruction =\n      tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                 LorentzFactorTimesSpatialVelocity, MagneticField,\n                 DivergenceCleaningField>;\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Free outflow & no inflow boundary condition on GRMHD primitive \"\n      \"variables\"};\n\n  HydroFreeOutflow() = default;\n  HydroFreeOutflow(HydroFreeOutflow&&) = default;\n  HydroFreeOutflow& operator=(HydroFreeOutflow&&) = default;\n  HydroFreeOutflow(const HydroFreeOutflow&) = default;\n  HydroFreeOutflow& operator=(const HydroFreeOutflow&) = default;\n  ~HydroFreeOutflow() override = default;\n\n  explicit HydroFreeOutflow(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, HydroFreeOutflow);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n  using dg_interior_temporary_tags = tmpl::list<Shift, Lapse, InvSpatialMetric>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_ghost(\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n      gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          spatial_velocity_one_form,\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n      /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          outward_directed_normal_vector,\n\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_temperature,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::II<DataVector, 3, Frame::Inertial>&\n          interior_inv_spatial_metric);\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>, Shift, Lapse,\n                 SpatialMetric>;\n  using fd_interior_primitive_variables_tags =\n      tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>, MagneticField>;\n\n  using fd_gridless_tags = tmpl::list<fd::Tags::Reconstructor>;\n\n  static void fd_ghost(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          lorentz_factor_times_spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n\n      gsl::not_null<std::optional<Variables<db::wrap_tags_in<\n          ::Tags::Flux,\n          typename grmhd::ValenciaDivClean::System::flux_variables,\n          tmpl::size_t<3>, Frame::Inertial>>>*>\n          cell_centered_ghost_fluxes,\n\n      const Direction<3>& direction,\n\n      // interior temporary tags\n      const Mesh<3>& subcell_mesh,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n\n      // interior prim vars tags\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_temperature,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n\n      // gridless tags\n      const fd::Reconstructor& reconstructor);\n\n  // have an impl to make sharing code with GH+GRMHD easy\n  static void fd_ghost_impl(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          lorentz_factor_times_spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n      gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*> spatial_metric,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> sqrt_det_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n\n      const Direction<3>& direction,\n\n      // fd_interior_temporary_tags\n      const Mesh<3>& subcell_mesh,\n\n      // fd_interior_primitive_variables_tags\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_temperature,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n\n      size_t ghost_zone_size, bool need_tags_for_fluxes);\n};\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Reflective.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Reflective.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/Evaluate.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComputeFluxesFromPrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\n\nReflective::Reflective(bool reflect_both) : reflect_both_(reflect_both) {}\n\nReflective::Reflective(CkMigrateMessage* const msg) : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nReflective::get_clone() const {\n  return std::make_unique<Reflective>(*this);\n}\n\nvoid Reflective::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | reflect_both_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Reflective::my_PUP_ID = 0;\n\nstd::optional<std::string> Reflective::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity_one_form,\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n    /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>&\n        outward_directed_normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>&\n        outward_directed_normal_vector,\n\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_temperature,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& interior_inv_spatial_metric)\n    const {\n  const size_t number_of_grid_points = get(interior_rest_mass_density).size();\n\n  // temp buffer to store\n  //  * n_i v^i\n  //  * v_{ghost}^i\n  //  * n_i B^i\n  //  * B_{ghost}^i\n  //  * divergence cleaning field on ghost zone\n  //  * spatial metric\n  //  * sqrt determinant of spatial metric\n  Variables<\n      tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 ::Tags::TempScalar<2>, gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>>\n      temp_buffer{number_of_grid_points};\n  auto& normal_dot_interior_spatial_velocity =\n      get<::Tags::TempScalar<0>>(temp_buffer);\n  auto& exterior_spatial_velocity = *spatial_velocity;\n  auto& normal_dot_interior_magnetic_field =\n      get<::Tags::TempScalar<1>>(temp_buffer);\n  auto& exterior_magnetic_field =\n      get<hydro::Tags::MagneticField<DataVector, 3>>(temp_buffer);\n  auto& exterior_divergence_cleaning_field =\n      get<::Tags::TempScalar<2>>(temp_buffer);\n  auto& interior_spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(temp_buffer);\n  auto& interior_sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(temp_buffer);\n\n  get(*lapse) = get(interior_lapse);\n  for (size_t i = 0; i < 3; ++i) {\n    (*shift).get(i) = interior_shift.get(i);\n    for (size_t j = 0; j < 3; ++j) {\n      (*inv_spatial_metric).get(i, j) = interior_inv_spatial_metric.get(i, j);\n    }\n  }\n\n  // spatial metric and sqrt determinant of spatial metric can be retrived from\n  // Databox but only as gridless_tags with whole volume data (unlike all the\n  // other arguments which are face tensors). Rather than doing expensive tensor\n  // slicing operations on those, we just compute those two quantities from\n  // inverse spatial metric as below.\n  determinant_and_inverse(make_not_null(&interior_sqrt_det_spatial_metric),\n                          make_not_null(&interior_spatial_metric),\n                          interior_inv_spatial_metric);\n  get(interior_sqrt_det_spatial_metric) =\n      1.0 / sqrt(get(interior_sqrt_det_spatial_metric));\n\n  // copy-paste interior spatial velocity to exterior spatial velocity, but\n  // kill ingoing normal component to zero\n  dot_product(make_not_null(&normal_dot_interior_spatial_velocity),\n              outward_directed_normal_covector, interior_spatial_velocity);\n  dot_product(make_not_null(&normal_dot_interior_magnetic_field),\n              outward_directed_normal_covector, interior_magnetic_field);\n\n  // reflect both outward and inward normal components of\n  // spatial velocity and magnetic field.\n  if (reflect_both_) {\n    for (size_t i = 0; i < number_of_grid_points; ++i) {\n      if (get(normal_dot_interior_spatial_velocity)[i] > 0.0) {\n        for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n          exterior_spatial_velocity.get(spatial_index)[i] =\n              interior_spatial_velocity.get(spatial_index)[i] -\n              2.0 * get(normal_dot_interior_spatial_velocity)[i] *\n                  outward_directed_normal_vector.get(spatial_index)[i];\n        }\n      } else {\n        for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n          exterior_spatial_velocity.get(spatial_index)[i] =\n              interior_spatial_velocity.get(spatial_index)[i] +\n              2.0 * get(normal_dot_interior_spatial_velocity)[i] *\n                  outward_directed_normal_vector.get(spatial_index)[i];\n        }\n      }\n      if (get(normal_dot_interior_magnetic_field)[i] > 0.0) {\n        for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n          exterior_magnetic_field.get(spatial_index)[i] =\n              interior_magnetic_field.get(spatial_index)[i] -\n              2.0 * get(normal_dot_interior_magnetic_field)[i] *\n                  outward_directed_normal_vector.get(spatial_index)[i];\n        }\n      } else {\n        for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n          exterior_magnetic_field.get(spatial_index)[i] =\n              interior_magnetic_field.get(spatial_index)[i] +\n              2.0 * get(normal_dot_interior_magnetic_field)[i] *\n                  outward_directed_normal_vector.get(spatial_index)[i];\n        }\n      }\n    }\n  }\n\n  // reflect only the outward normal component of\n  // spatial velocity and magnetic field.\n  else {\n    for (size_t i = 0; i < number_of_grid_points; ++i) {\n      if (get(normal_dot_interior_spatial_velocity)[i] > 0.0) {\n        for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n          exterior_spatial_velocity.get(spatial_index)[i] =\n              interior_spatial_velocity.get(spatial_index)[i] -\n              2.0 * get(normal_dot_interior_spatial_velocity)[i] *\n                  outward_directed_normal_vector.get(spatial_index)[i];\n        }\n      } else {\n        for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n          exterior_spatial_velocity.get(spatial_index)[i] =\n              interior_spatial_velocity.get(spatial_index)[i];\n        }\n      }\n      if (get(normal_dot_interior_magnetic_field)[i] > 0.0) {\n        for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n          exterior_magnetic_field.get(spatial_index)[i] =\n              interior_magnetic_field.get(spatial_index)[i] -\n              2.0 * get(normal_dot_interior_magnetic_field)[i] *\n                  outward_directed_normal_vector.get(spatial_index)[i];\n        }\n      } else {\n        for (size_t spatial_index = 0; spatial_index < 3; ++spatial_index) {\n          exterior_magnetic_field.get(spatial_index)[i] =\n              interior_magnetic_field.get(spatial_index)[i];\n        }\n      }\n    }\n  }\n  get(exterior_divergence_cleaning_field) = 0.0;\n\n  ConservativeFromPrimitive::apply(\n      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n      interior_rest_mass_density, interior_electron_fraction,\n      interior_specific_internal_energy, interior_pressure,\n      exterior_spatial_velocity, interior_lorentz_factor,\n      exterior_magnetic_field, interior_sqrt_det_spatial_metric,\n      interior_spatial_metric, exterior_divergence_cleaning_field);\n\n  ComputeFluxes::apply(tilde_d_flux, tilde_ye_flux, tilde_tau_flux,\n                       tilde_s_flux, tilde_b_flux, tilde_phi_flux, *tilde_d,\n                       *tilde_ye, *tilde_tau, *tilde_s, *tilde_b, *tilde_phi,\n                       *lapse, *shift, interior_sqrt_det_spatial_metric,\n                       interior_spatial_metric, *inv_spatial_metric,\n                       interior_pressure, exterior_spatial_velocity,\n                       interior_lorentz_factor, exterior_magnetic_field);\n\n  *rest_mass_density = interior_rest_mass_density;\n  *electron_fraction = interior_electron_fraction;\n  *temperature = interior_temperature;\n  raise_or_lower_index(spatial_velocity_one_form, exterior_spatial_velocity,\n                       interior_spatial_metric);\n\n  return {};\n}\n\nvoid Reflective::fd_ghost(\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        magnetic_field,\n    const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n\n    const gsl::not_null<std::optional<Variables<db::wrap_tags_in<\n        Flux, typename grmhd::ValenciaDivClean::System::flux_variables>>>*>\n        cell_centered_ghost_fluxes,\n\n    const Direction<3>& direction,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n\n    // fd_interior_primitive_variables_tags\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_temperature,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n\n    // fd_gridless_tags\n    const fd::Reconstructor& reconstructor) const {\n  Variables<tmpl::push_back<\n      tmpl::append<typename System::variables_tag::tags_list,\n                   typename System::primitive_variables_tag::tags_list>,\n      SqrtDetSpatialMetric, SpatialMetric, InvSpatialMetric, Lapse, Shift>>\n      temp_vars{get(*rest_mass_density).size()};\n\n  fd_ghost_impl(make_not_null(&get<RestMassDensity>(temp_vars)),\n                make_not_null(&get<ElectronFraction>(temp_vars)),\n                make_not_null(&get<Temperature>(temp_vars)),\n                make_not_null(&get<Pressure>(temp_vars)),\n                make_not_null(&get<SpecificInternalEnergy>(temp_vars)),\n                lorentz_factor_times_spatial_velocity,\n                make_not_null(&get<SpatialVelocity>(temp_vars)),\n                make_not_null(&get<LorentzFactor>(temp_vars)),\n                make_not_null(&get<MagneticField>(temp_vars)),\n                make_not_null(&get<DivergenceCleaningField>(temp_vars)),\n                make_not_null(&get<SpatialMetric>(temp_vars)),\n                make_not_null(&get<InvSpatialMetric>(temp_vars)),\n                make_not_null(&get<SqrtDetSpatialMetric>(temp_vars)),\n                make_not_null(&get<Lapse>(temp_vars)),\n                make_not_null(&get<Shift>(temp_vars)),\n\n                direction,\n\n                // fd_interior_temporary_tags\n                subcell_mesh,\n\n                // fd_interior_primitive_variables_tags\n                interior_rest_mass_density, interior_electron_fraction,\n                interior_temperature, interior_pressure,\n                interior_specific_internal_energy, interior_lorentz_factor,\n                interior_spatial_velocity, interior_magnetic_field,\n                interior_spatial_metric, interior_lapse, interior_shift,\n\n                reconstructor.ghost_zone_size(),\n\n                cell_centered_ghost_fluxes->has_value());\n\n  if (cell_centered_ghost_fluxes->has_value()) {\n    compute_fluxes_from_primitives(\n        make_not_null(&cell_centered_ghost_fluxes->value()), temp_vars);\n  }\n\n  *rest_mass_density = get<hydro::Tags::RestMassDensity<DataVector>>(temp_vars);\n  *electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataVector>>(temp_vars);\n  *temperature = get<hydro::Tags::Temperature<DataVector>>(temp_vars);\n  *magnetic_field = get<hydro::Tags::MagneticField<DataVector, 3>>(temp_vars);\n  *divergence_cleaning_field =\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(temp_vars);\n}\n\nvoid Reflective::fd_ghost_impl(\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<Scalar<DataVector>*> pressure,\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        magnetic_field,\n    const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n    const gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*>\n        spatial_metric,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> sqrt_det_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n\n    const Direction<3>& direction,\n\n    // fd_interior_temporary_tags\n    const Mesh<3>& subcell_mesh,\n\n    // fd_interior_primitive_variables_tags\n    const Scalar<DataVector>& interior_rest_mass_density,\n    const Scalar<DataVector>& interior_electron_fraction,\n    const Scalar<DataVector>& interior_temperature,\n    const Scalar<DataVector>& interior_pressure,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const Scalar<DataVector>& interior_lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n\n    const size_t ghost_zone_size, const bool need_tags_for_fluxes) const {\n  const size_t dim_direction{direction.dimension()};\n\n  const auto subcell_extents{subcell_mesh.extents()};\n  const size_t num_face_pts{\n      subcell_extents.slice_away(dim_direction).product()};\n\n  using prim_tags_without_cleaning_field =\n      tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                 LorentzFactorTimesSpatialVelocity, MagneticField>;\n\n  // Create a single large DV to reduce the number of Variables allocations\n  using fluxes_tags =\n      tmpl::list<Pressure, SpecificInternalEnergy, SpatialMetric, Lapse, Shift>;\n  const size_t buffer_size_for_fluxes =\n      need_tags_for_fluxes\n          ? Variables<fluxes_tags>::number_of_independent_components\n          : 0;\n  const size_t buffer_size_per_grid_pts = Variables<\n      prim_tags_without_cleaning_field>::number_of_independent_components;\n  DataVector buffer_for_vars{\n      num_face_pts * ((1 + ghost_zone_size) *\n                      (buffer_size_per_grid_pts + buffer_size_for_fluxes)),\n      0.0};\n\n  // Assign two Variables object to the buffer\n  Variables<prim_tags_without_cleaning_field> outermost_prim_vars{\n      buffer_for_vars.data(), num_face_pts * buffer_size_per_grid_pts};\n  Variables<prim_tags_without_cleaning_field> ghost_prim_vars{\n      outermost_prim_vars.data() + outermost_prim_vars.size(),\n      num_face_pts * buffer_size_per_grid_pts * ghost_zone_size};\n\n  // Slice values on the outermost grid points and store them to\n  // `outermost_prim_vars`\n  auto get_boundary_val = [&direction, &subcell_extents](auto volume_tensor) {\n    return evolution::dg::subcell::slice_tensor_for_subcell(\n        volume_tensor, subcell_extents, 1, direction, {});\n  };\n\n  get<RestMassDensity>(outermost_prim_vars) =\n      get_boundary_val(interior_rest_mass_density);\n  get<ElectronFraction>(outermost_prim_vars) =\n      get_boundary_val(interior_electron_fraction);\n  get<Temperature>(outermost_prim_vars) =\n      get_boundary_val(interior_temperature);\n\n  {\n    const auto normal_spatial_velocity_at_boundary =\n        get_boundary_val(interior_spatial_velocity).get(dim_direction);\n    // reflect both the outgoing and ingoing normal components of\n    // spatial velocity and the magnetic field.\n    if (reflect_both_) {\n      for (size_t i = 0; i < 3; ++i) {\n        if (i == dim_direction) {\n          get<LorentzFactorTimesSpatialVelocity>(outermost_prim_vars).get(i) =\n              -get(get_boundary_val(interior_lorentz_factor)) *\n              get_boundary_val(interior_spatial_velocity).get(i);\n          get<MagneticField>(outermost_prim_vars).get(i) =\n              -1.0 * get_boundary_val(interior_magnetic_field).get(i);\n        } else {\n          get<LorentzFactorTimesSpatialVelocity>(outermost_prim_vars).get(i) =\n              get(get_boundary_val(interior_lorentz_factor)) *\n              get_boundary_val(interior_spatial_velocity).get(i);\n          get<MagneticField>(outermost_prim_vars).get(i) =\n              get_boundary_val(interior_magnetic_field).get(i);\n        }\n      }\n    }\n    // reflect only the outgoing normal component of spatial\n    // velocity and the magnetic field.\n    else {\n      for (size_t i = 0; i < 3; ++i) {\n        if (i == dim_direction) {\n          if (direction.sign() > 0.0) {\n            get<LorentzFactorTimesSpatialVelocity>(outermost_prim_vars).get(i) =\n                get(get_boundary_val(interior_lorentz_factor)) *\n                min(normal_spatial_velocity_at_boundary,\n                    -normal_spatial_velocity_at_boundary);\n            get<MagneticField>(outermost_prim_vars).get(i) =\n                min(get_boundary_val(interior_magnetic_field).get(i),\n                    -1.0 * get_boundary_val(interior_magnetic_field).get(i));\n          } else {\n            get<LorentzFactorTimesSpatialVelocity>(outermost_prim_vars).get(i) =\n                get(get_boundary_val(interior_lorentz_factor)) *\n                max(normal_spatial_velocity_at_boundary,\n                    -normal_spatial_velocity_at_boundary);\n            get<MagneticField>(outermost_prim_vars).get(i) =\n                max(get_boundary_val(interior_magnetic_field).get(i),\n                    -1.0 * get_boundary_val(interior_magnetic_field).get(i));\n          }\n        } else {\n          get<LorentzFactorTimesSpatialVelocity>(outermost_prim_vars).get(i) =\n              get(get_boundary_val(interior_lorentz_factor)) *\n              get_boundary_val(interior_spatial_velocity).get(i);\n          get<MagneticField>(outermost_prim_vars).get(i) =\n              get_boundary_val(interior_magnetic_field).get(i);\n        }\n      }\n    }\n  }\n\n  // Now copy `outermost_prim_vars` into each slices of `ghost_prim_vars`.\n  Index<3> ghost_data_extents = subcell_extents;\n  ghost_data_extents[dim_direction] = ghost_zone_size;\n\n  for (size_t i_ghost = 0; i_ghost < ghost_zone_size; ++i_ghost) {\n    add_slice_to_data(make_not_null(&ghost_prim_vars), outermost_prim_vars,\n                      ghost_data_extents, dim_direction, i_ghost);\n  }\n\n  *rest_mass_density = get<RestMassDensity>(ghost_prim_vars);\n  *electron_fraction = get<ElectronFraction>(ghost_prim_vars);\n  *temperature = get<Temperature>(ghost_prim_vars);\n  *lorentz_factor_times_spatial_velocity =\n      get<LorentzFactorTimesSpatialVelocity>(ghost_prim_vars);\n  *magnetic_field = get<MagneticField>(ghost_prim_vars);\n\n  // divergence cleaning scalar field is just set to zero in the ghost zone.\n  get(*divergence_cleaning_field) = 0.0;\n\n  if (need_tags_for_fluxes) {\n    Variables<fluxes_tags> outermost_fluxes_vars{\n        std::next(ghost_prim_vars.data(),\n                  static_cast<std::ptrdiff_t>(ghost_prim_vars.size())),\n        num_face_pts * buffer_size_for_fluxes};\n    Variables<fluxes_tags> ghost_fluxes_vars{\n        std::next(outermost_fluxes_vars.data(),\n                  static_cast<std::ptrdiff_t>(outermost_fluxes_vars.size())),\n        num_face_pts * buffer_size_for_fluxes * ghost_zone_size};\n\n    get<Pressure>(outermost_fluxes_vars) = get_boundary_val(interior_pressure);\n    get<SpecificInternalEnergy>(outermost_fluxes_vars) =\n        get_boundary_val(interior_specific_internal_energy);\n    get<SpatialMetric>(outermost_fluxes_vars) =\n        get_boundary_val(interior_spatial_metric);\n    get<Lapse>(outermost_fluxes_vars) = get_boundary_val(interior_lapse);\n    get<Shift>(outermost_fluxes_vars) = get_boundary_val(interior_shift);\n\n    for (size_t i_ghost = 0; i_ghost < ghost_zone_size; ++i_ghost) {\n      add_slice_to_data(make_not_null(&ghost_fluxes_vars),\n                        outermost_fluxes_vars, ghost_data_extents,\n                        dim_direction, i_ghost);\n    }\n    // Need pressure for high-order finite difference\n    *pressure = get<Pressure>(ghost_fluxes_vars);\n    *specific_internal_energy = get<SpecificInternalEnergy>(ghost_fluxes_vars);\n    *spatial_metric = get<SpatialMetric>(ghost_fluxes_vars);\n    *lapse = get<Lapse>(ghost_fluxes_vars);\n    *shift = get<Shift>(ghost_fluxes_vars);\n\n    determinant_and_inverse(sqrt_det_spatial_metric, inv_spatial_metric,\n                            *spatial_metric);\n    get(*sqrt_det_spatial_metric) = sqrt(get(*sqrt_det_spatial_metric));\n    tenex::evaluate(\n        lorentz_factor,\n        sqrt(1.0 + (*spatial_metric)(ti::i, ti::j) *\n                       (*lorentz_factor_times_spatial_velocity)(ti::I) *\n                       (*lorentz_factor_times_spatial_velocity)(ti::J)));\n    tenex::evaluate<ti::I>(\n        spatial_velocity,\n        (*lorentz_factor_times_spatial_velocity)(ti::I) / (*lorentz_factor)());\n  }\n}\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Reflective.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Direction;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::BoundaryConditions {\n/*!\n * \\brief Apply \"soft\" reflective boundary condition as described in\n * \\cite Shiokawa2011ih.\n *\n * All primitive variables at the boundary are copied into ghost zone except :\n *\n *  - If \\f$n_iv^i \\leq 0\\f$ where \\f$v^i\\f$ is spatial velocity and \\f$n_i\\f$\n * is outward directed normal covector, copy the values of \\f$v^i\\f$ at the\n * boundary to ghost zone. If \\f$n_iv^i>0\\f$, spatial velocity in the ghost zone\n * is modified such that the sign of normal component is inverted at the\n * interface i.e. \\f$v_\\text{ghost}^i = v^i - 2*(n_jv^j)n^i\\f$.\n *\n *  - If \\f$n_iB^i \\leq 0\\f$ where \\f$B^i\\f$ is magnetic field and \\f$n_i\\f$\n * is outward directed normal covector, copy the values of \\f$B^i\\f$ at the\n * boundary to ghost zone. If \\f$n_iB^i>0\\f$, magnetic field in the ghost zone\n * is modified such that the sign of normal component is inverted at the\n * interface i.e. \\f$B_\\text{ghost}^i = B^i - 2*(n_jB^j)n^i\\f$.\n *\n *  - If reflect_both is true, then spatial velocity and magnetic field are\n * are inverted regardless of whether the normal component is pointing outward\n * or inward.\n *\n *  - However, regardless of whether the normal component of the spatial\n * velocity $n_iv^i$ is pointing outward or inward, the lorentz factor \\f$W\\f$\n * is copied into ghost zone without any modification.\n *\n *  - Divergence cleaning scalar field \\f$\\Phi\\f$ is set to zero in ghost zone.\n *\n */\nclass Reflective final : public BoundaryCondition {\n private:\n  bool reflect_both_{false};\n\n  using RestMassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n  using LorentzFactorTimesSpatialVelocity =\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n  using MagneticField = hydro::Tags::MagneticField<DataVector, 3>;\n  using DivergenceCleaningField =\n      hydro::Tags::DivergenceCleaningField<DataVector>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using SpatialVelocity = hydro::Tags::SpatialVelocity<DataVector, 3>;\n  using LorentzFactor = hydro::Tags::LorentzFactor<DataVector>;\n  using SqrtDetSpatialMetric = gr::Tags::SqrtDetSpatialMetric<DataVector>;\n  using SpatialMetric = gr::Tags::SpatialMetric<DataVector, 3>;\n  using InvSpatialMetric = gr::Tags::InverseSpatialMetric<DataVector, 3>;\n  using Lapse = gr::Tags::Lapse<DataVector>;\n  using Shift = gr::Tags::Shift<DataVector, 3>;\n\n  using prim_tags_for_reconstruction =\n      tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                 LorentzFactorTimesSpatialVelocity, MagneticField,\n                 DivergenceCleaningField>;\n\n  template <typename T>\n  using Flux = ::Tags::Flux<T, tmpl::size_t<3>, Frame::Inertial>;\n\n public:\n  struct ReflectBoth {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Reflect both outgoing and incoming normal component of \"\n        \"spatial velocity and magnetic field.\"};\n  };\n  using options = tmpl::list<ReflectBoth>;\n  static constexpr Options::String help{\n      \"Reflective boundary conditions, inverting the sign \"\n      \"of outgoing normal component of spatial velocity \"\n      \"and magnetic field.\"};\n\n  Reflective() = default;\n  Reflective(Reflective&&) = default;\n  Reflective& operator=(Reflective&&) = default;\n  Reflective(const Reflective&) = default;\n  Reflective& operator=(const Reflective&) = default;\n  ~Reflective() override = default;\n\n  explicit Reflective(bool reflect_both);\n\n  explicit Reflective(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, Reflective);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n  using dg_interior_temporary_tags = tmpl::list<Shift, Lapse, InvSpatialMetric>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n      gsl::not_null<Scalar<DataVector>*> tilde_phi,\n\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          spatial_velocity_one_form,\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n      /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          outward_directed_normal_vector,\n\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_temperature,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::II<DataVector, 3, Frame::Inertial>&\n          interior_inv_spatial_metric) const;\n\n  using fd_interior_evolved_variables_tags = tmpl::list<>;\n  using fd_interior_temporary_tags =\n      tmpl::list<evolution::dg::subcell::Tags::Mesh<3>, Shift, Lapse,\n                 SpatialMetric>;\n  using fd_interior_primitive_variables_tags =\n      tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>, MagneticField>;\n\n  using fd_gridless_tags = tmpl::list<fd::Tags::Reconstructor>;\n\n  void fd_ghost(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          lorentz_factor_times_spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n\n      gsl::not_null<std::optional<Variables<db::wrap_tags_in<\n          Flux, typename grmhd::ValenciaDivClean::System::flux_variables>>>*>\n          cell_centered_ghost_fluxes,\n\n      const Direction<3>& direction,\n\n      // interior temporary tags\n      const Mesh<3>& subcell_mesh,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n\n      // interior prim vars tags\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_temperature,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n\n      // gridless tags\n      const fd::Reconstructor& reconstructor) const;\n\n  // have an impl to make sharing code with GH+GRMHD easy\n  void fd_ghost_impl(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          lorentz_factor_times_spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n      gsl::not_null<tnsr::ii<DataVector, 3, Frame::Inertial>*> spatial_metric,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> sqrt_det_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n\n      const Direction<3>& direction,\n\n      // fd_interior_temporary_tags\n      const Mesh<3>& subcell_mesh,\n\n      // fd_interior_primitive_variables_tags\n      const Scalar<DataVector>& interior_rest_mass_density,\n      const Scalar<DataVector>& interior_electron_fraction,\n      const Scalar<DataVector>& interior_temperature,\n      const Scalar<DataVector>& interior_pressure,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const Scalar<DataVector>& interior_lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_magnetic_field,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& interior_spatial_metric,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& interior_shift,\n\n      size_t ghost_zone_size, bool need_tags_for_fluxes) const;\n  };\n}  // namespace grmhd::ValenciaDivClean::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/CMakeLists.txt",
    "content": "#\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Hll.cpp\n  Rusanov.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Hll.hpp\n  NamespaceDocs.hpp\n  Rusanov.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryCorrections {\nusing standard_boundary_corrections = tmpl::list<Hll, Rusanov>;\n}  // namespace grmhd::ValenciaDivClean::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Hll.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Hll.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"PointwiseFunctions/Hydro/SoundSpeedSquared.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryCorrections {\nHll::Hll(const double magnetic_field_magnitude_for_hydro,\n         const double light_speed_density_cutoff)\n    : magnetic_field_magnitude_for_hydro_(magnetic_field_magnitude_for_hydro),\n      light_speed_density_cutoff_(light_speed_density_cutoff) {}\n\nHll::Hll(CkMigrateMessage* /*unused*/) {}\n\nstd::unique_ptr<evolution::BoundaryCorrection> Hll::get_clone() const {\n  return std::make_unique<Hll>(*this);\n}\n\nvoid Hll::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n  p | magnetic_field_magnitude_for_hydro_;\n  p | light_speed_density_cutoff_;\n}\n\ndouble Hll::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        packaged_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        packaged_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        packaged_normal_dot_flux_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        packaged_normal_dot_flux_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_largest_outgoing_char_speed,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_largest_ingoing_char_speed,\n\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_d,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_ye,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_tau,\n    const tnsr::Ij<DataVector, 3, Frame::Inertial>& flux_tilde_s,\n    const tnsr::IJ<DataVector, 3, Frame::Inertial>& flux_tilde_b,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_phi,\n\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& spatial_velocity_one_form,\n\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& temperature,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n    const EquationsOfState::EquationOfState<true, 3>& equation_of_state) const {\n  {\n    Scalar<DataVector> shift_dot_normal = tilde_d;\n    dot_product(make_not_null(&shift_dot_normal), shift, normal_covector);\n\n    // Initialize characteristic speeds to light speed\n    // (default choice)\n    get(*packaged_largest_outgoing_char_speed) =\n        get(lapse) - get(shift_dot_normal);\n    get(*packaged_largest_ingoing_char_speed) =\n        -get(lapse) - get(shift_dot_normal);\n\n    if (const bool has_b_field =\n            max(get(magnitude(tilde_b))) > magnetic_field_magnitude_for_hydro_;\n        not has_b_field and\n        (max(get(rest_mass_density)) > light_speed_density_cutoff_)) {\n      // Since we have no magnetic field (and we have grid points not in the\n      // atmosphere), we can reduce the char speeds to the hydro speeds only.\n      // This makes the scheme less dissipative.\n      //\n      // Calculate sound speed squared\n      const size_t num_points = get(rest_mass_density).size();\n      Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                           ::Tags::TempScalar<2>, ::Tags::TempScalar<3>,\n                           ::Tags::TempScalar<4>, ::Tags::TempScalar<5>,\n                           ::Tags::TempScalar<6>>>\n          temp_buffer{num_points};\n      auto& v_dot_normal = get<::Tags::TempScalar<0>>(temp_buffer);\n      auto& v_squared = get<::Tags::TempScalar<1>>(temp_buffer);\n      auto& discriminant = get(get<::Tags::TempScalar<2>>(temp_buffer));\n      auto& one_minus_v2_cs2 = get<::Tags::TempScalar<3>>(temp_buffer);\n      auto& one_minus_cs2 = get<::Tags::TempScalar<4>>(temp_buffer);\n      auto& lapse_over_one_minus_v2_cs2 =\n          get<::Tags::TempScalar<5>>(temp_buffer);\n      auto& v_dot_normal_times_one_minus_cs2 =\n          get<::Tags::TempScalar<6>>(temp_buffer);\n\n      const Scalar<DataVector> specific_internal_energy =\n          equation_of_state\n              .specific_internal_energy_from_density_and_temperature(\n                  rest_mass_density, temperature, electron_fraction);\n      const Scalar<DataVector> pressure =\n          equation_of_state.pressure_from_density_and_energy(\n              rest_mass_density, specific_internal_energy, electron_fraction);\n      const Scalar<DataVector> specific_enthalpy =\n          hydro::relativistic_specific_enthalpy(\n              rest_mass_density, specific_internal_energy, pressure);\n      const Scalar<DataVector> sound_speed_squared{\n          clamp(get(equation_of_state\n                        .sound_speed_squared_from_density_and_temperature(\n                            rest_mass_density, temperature, electron_fraction)),\n                0.0, 1.0)};\n\n      // Compute v_dot_normal, v^i n_i\n      dot_product(make_not_null(&v_dot_normal), spatial_velocity,\n                  normal_covector);\n\n      // Compute v^2=v^i v_i\n      dot_product(make_not_null(&v_squared), spatial_velocity,\n                  spatial_velocity_one_form);\n      get(v_squared) = clamp(get(v_squared), 0.0, 1.0 - 1.0e-8);\n\n      // Calculate characteristic speeds in inertial frame\n      //\n      // Ideally we'd use the Lorentz factor instead of 1-v^2, but I (Nils\n      // Deppe) don't have the bandwidth to change this right now.\n      get(one_minus_v2_cs2) = 1.0 - get(v_squared) * get(sound_speed_squared);\n      get(one_minus_cs2) = 1.0 - get(sound_speed_squared);\n      discriminant =\n          get(sound_speed_squared) * (1.0 - get(v_squared)) *\n          (get(one_minus_v2_cs2) -\n           get(v_dot_normal) * get(v_dot_normal) * get(one_minus_cs2));\n      discriminant = max(discriminant, 0.0);\n      discriminant = sqrt(discriminant);\n\n      get(lapse_over_one_minus_v2_cs2) = get(lapse) / get(one_minus_v2_cs2);\n      get(v_dot_normal_times_one_minus_cs2) =\n          get(v_dot_normal) * get(one_minus_cs2);\n\n      for (size_t i = 0; i < num_points; ++i) {\n        if (get(rest_mass_density)[i] > light_speed_density_cutoff_) {\n          get(*packaged_largest_outgoing_char_speed)[i] =\n              get(lapse_over_one_minus_v2_cs2)[i] *\n                  (get(v_dot_normal_times_one_minus_cs2)[i] + discriminant[i]) -\n              get(shift_dot_normal)[i];\n          get(*packaged_largest_ingoing_char_speed)[i] =\n              get(lapse_over_one_minus_v2_cs2)[i] *\n                  (get(v_dot_normal_times_one_minus_cs2)[i] - discriminant[i]) -\n              get(shift_dot_normal)[i];\n        }\n      }\n    }\n\n    // Correct for mesh velocity\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*packaged_largest_outgoing_char_speed) -=\n          get(*normal_dot_mesh_velocity);\n      get(*packaged_largest_ingoing_char_speed) -=\n          get(*normal_dot_mesh_velocity);\n    }\n  }\n\n  *packaged_tilde_d = tilde_d;\n  *packaged_tilde_ye = tilde_ye;\n  *packaged_tilde_tau = tilde_tau;\n  *packaged_tilde_s = tilde_s;\n  *packaged_tilde_b = tilde_b;\n  *packaged_tilde_phi = tilde_phi;\n\n  normal_dot_flux(packaged_normal_dot_flux_tilde_d, normal_covector,\n                  flux_tilde_d);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_ye, normal_covector,\n                  flux_tilde_ye);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_tau, normal_covector,\n                  flux_tilde_tau);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_s, normal_covector,\n                  flux_tilde_s);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_b, normal_covector,\n                  flux_tilde_b);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_phi, normal_covector,\n                  flux_tilde_phi);\n\n  using std::max;\n  return max(max(abs(get(*packaged_largest_outgoing_char_speed))),\n             max(abs(get(*packaged_largest_ingoing_char_speed))));\n}\n\nvoid Hll::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        boundary_correction_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        boundary_correction_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_phi,\n    const Scalar<DataVector>& tilde_d_int,\n    const Scalar<DataVector>& tilde_ye_int,\n    const Scalar<DataVector>& tilde_tau_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_int,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_int,\n    const Scalar<DataVector>& tilde_phi_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_d_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_ye_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_tau_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_s_int,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_b_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_phi_int,\n    const Scalar<DataVector>& largest_outgoing_char_speed_int,\n    const Scalar<DataVector>& largest_ingoing_char_speed_int,\n    const Scalar<DataVector>& tilde_d_ext,\n    const Scalar<DataVector>& tilde_ye_ext,\n    const Scalar<DataVector>& tilde_tau_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_ext,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_ext,\n    const Scalar<DataVector>& tilde_phi_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_d_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_ye_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_tau_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_s_ext,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_b_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_phi_ext,\n    const Scalar<DataVector>& largest_outgoing_char_speed_ext,\n    const Scalar<DataVector>& largest_ingoing_char_speed_ext,\n    const dg::Formulation dg_formulation) {\n  // Allocate a temp buffer with four tags.\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>, ::Tags::TempScalar<3>>>\n      temps{get(tilde_d_int).size()};\n\n  // Determine lambda_max and lambda_min from the characteristic speeds info\n  // from interior and exterior\n  get(get<::Tags::TempScalar<0>>(temps)) =\n      max(0., get(largest_outgoing_char_speed_int),\n          -get(largest_ingoing_char_speed_ext));\n  const DataVector& lambda_max = get(get<::Tags::TempScalar<0>>(temps));\n  get(get<::Tags::TempScalar<1>>(temps)) =\n      min(0., get(largest_ingoing_char_speed_int),\n          -get(largest_outgoing_char_speed_ext));\n  const DataVector& lambda_min = get(get<::Tags::TempScalar<1>>(temps));\n\n  // Pre-compute two expressions made out of lambda_max and lambda_min\n  get(get<::Tags::TempScalar<2>>(temps)) = lambda_max * lambda_min;\n  const DataVector& lambdas_product = get(get<::Tags::TempScalar<2>>(temps));\n  get(get<::Tags::TempScalar<3>>(temps)) = 1. / (lambda_max - lambda_min);\n  const DataVector& one_over_lambda_max_minus_min =\n      get(get<::Tags::TempScalar<3>>(temps));\n\n  if (dg_formulation == dg::Formulation::WeakInertial) {\n    get(*boundary_correction_tilde_d) =\n        ((lambda_max * get(normal_dot_flux_tilde_d_int) +\n          lambda_min * get(normal_dot_flux_tilde_d_ext)) +\n         lambdas_product * (get(tilde_d_ext) - get(tilde_d_int))) *\n        one_over_lambda_max_minus_min;\n    get(*boundary_correction_tilde_ye) =\n        ((lambda_max * get(normal_dot_flux_tilde_ye_int) +\n          lambda_min * get(normal_dot_flux_tilde_ye_ext)) +\n         lambdas_product * (get(tilde_ye_ext) - get(tilde_ye_int))) *\n        one_over_lambda_max_minus_min;\n    get(*boundary_correction_tilde_tau) =\n        ((lambda_max * get(normal_dot_flux_tilde_tau_int) +\n          lambda_min * get(normal_dot_flux_tilde_tau_ext)) +\n         lambdas_product * (get(tilde_tau_ext) - get(tilde_tau_int))) *\n        one_over_lambda_max_minus_min;\n    get(*boundary_correction_tilde_phi) =\n        ((lambda_max * get(normal_dot_flux_tilde_phi_int) +\n          lambda_min * get(normal_dot_flux_tilde_phi_ext)) +\n         lambdas_product * (get(tilde_phi_ext) - get(tilde_phi_int))) *\n        one_over_lambda_max_minus_min;\n\n    for (size_t i = 0; i < 3; ++i) {\n      boundary_correction_tilde_s->get(i) =\n          ((lambda_max * normal_dot_flux_tilde_s_int.get(i) +\n            lambda_min * normal_dot_flux_tilde_s_ext.get(i)) +\n           lambdas_product * (tilde_s_ext.get(i) - tilde_s_int.get(i))) *\n          one_over_lambda_max_minus_min;\n      boundary_correction_tilde_b->get(i) =\n          ((lambda_max * normal_dot_flux_tilde_b_int.get(i) +\n            lambda_min * normal_dot_flux_tilde_b_ext.get(i)) +\n           lambdas_product * (tilde_b_ext.get(i) - tilde_b_int.get(i))) *\n          one_over_lambda_max_minus_min;\n    }\n  } else {\n    get(*boundary_correction_tilde_d) =\n        (lambda_min * (get(normal_dot_flux_tilde_d_int) +\n                       get(normal_dot_flux_tilde_d_ext)) +\n         lambdas_product * (get(tilde_d_ext) - get(tilde_d_int))) *\n        one_over_lambda_max_minus_min;\n    get(*boundary_correction_tilde_ye) =\n        (lambda_min * (get(normal_dot_flux_tilde_ye_int) +\n                       get(normal_dot_flux_tilde_ye_ext)) +\n         lambdas_product * (get(tilde_ye_ext) - get(tilde_ye_int))) *\n        one_over_lambda_max_minus_min;\n    get(*boundary_correction_tilde_tau) =\n        (lambda_min * (get(normal_dot_flux_tilde_tau_int) +\n                       get(normal_dot_flux_tilde_tau_ext)) +\n         lambdas_product * (get(tilde_tau_ext) - get(tilde_tau_int))) *\n        one_over_lambda_max_minus_min;\n    get(*boundary_correction_tilde_phi) =\n        (lambda_min * (get(normal_dot_flux_tilde_phi_int) +\n                       get(normal_dot_flux_tilde_phi_ext)) +\n         lambdas_product * (get(tilde_phi_ext) - get(tilde_phi_int))) *\n        one_over_lambda_max_minus_min;\n\n    for (size_t i = 0; i < 3; ++i) {\n      boundary_correction_tilde_s->get(i) =\n          (lambda_min * (normal_dot_flux_tilde_s_int.get(i) +\n                         normal_dot_flux_tilde_s_ext.get(i)) +\n           lambdas_product * (tilde_s_ext.get(i) - tilde_s_int.get(i))) *\n          one_over_lambda_max_minus_min;\n      boundary_correction_tilde_b->get(i) =\n          (lambda_min * (normal_dot_flux_tilde_b_int.get(i) +\n                         normal_dot_flux_tilde_b_ext.get(i)) +\n           lambdas_product * (tilde_b_ext.get(i) - tilde_b_int.get(i))) *\n          one_over_lambda_max_minus_min;\n    }\n  }\n}\n\nbool operator==(const Hll& lhs, const Hll& rhs) {\n  return lhs.magnetic_field_magnitude_for_hydro_ ==\n             rhs.magnetic_field_magnitude_for_hydro_ and\n         lhs.light_speed_density_cutoff_ == rhs.light_speed_density_cutoff_;\n}\nbool operator!=(const Hll& lhs, const Hll& rhs) { return not(lhs == rhs); }\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Hll::my_PUP_ID = 0;\n}  // namespace grmhd::ValenciaDivClean::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Hll.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::BoundaryCorrections {\n/*!\n * \\brief An HLL Riemann solver\n *\n * Let \\f$U\\f$ be the evolved variable, \\f$F^i\\f$ the flux, and \\f$n_i\\f$ be the\n * outward directed unit normal to the interface. Denoting \\f$F := n_i F^i\\f$,\n * the HLL boundary correction is \\cite Harten1983\n *\n * \\f{align*}\n * G_\\text{HLL} = \\frac{\\lambda_\\text{max} F_\\text{int} +\n * \\lambda_\\text{min} F_\\text{ext}}{\\lambda_\\text{max} - \\lambda_\\text{min}}\n * - \\frac{\\lambda_\\text{min}\\lambda_\\text{max}}{\\lambda_\\text{max} -\n *   \\lambda_\\text{min}} \\left(U_\\text{int} - U_\\text{ext}\\right)\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior.\n * \\f$\\lambda_\\text{min}\\f$ and \\f$\\lambda_\\text{max}\\f$ are defined as\n *\n * \\f{align*}\n * \\lambda_\\text{min} &=\n * \\text{min}\\left(\\lambda^{-}_\\text{int},-\\lambda^{+}_\\text{ext}, 0\\right) \\\\\n * \\lambda_\\text{max} &=\n * \\text{max}\\left(\\lambda^{+}_\\text{int},-\\lambda^{-}_\\text{ext}, 0\\right)\n * \\f}\n *\n * where \\f$\\lambda^{+}\\f$ (\\f$\\lambda^{-}\\f$) is the largest characteristic\n * speed in the outgoing (ingoing) direction. Note the minus signs in front of\n * \\f$\\lambda^{\\pm}_\\text{ext}\\f$, which is because an outgoing speed w.r.t. the\n * neighboring element is an ingoing speed w.r.t. the local element, and vice\n * versa. Similarly, the \\f$F_{\\text{ext}}\\f$ term in \\f$G_\\text{HLL}\\f$ has a\n * positive sign because the outward directed normal of the neighboring element\n * has the opposite sign, i.e. \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$.\n *\n * The characteristic/signal speeds are given in the documentation for\n * `grmhd::ValenciaDivClean::characteristic_speeds()`. Since the fluid is\n * travelling slower than the speed of light, the speeds we are interested in\n * are\n *\n * \\f{align*}{\n *   \\lambda^{\\pm}&=\\pm\\alpha-\\beta^i n_i,\n * \\f}\n *\n * which correspond to the divergence cleaning field.\n *\n * \\note\n * - In the strong form the `dg_boundary_terms` function returns\n *   \\f$G - F_\\text{int}\\f$\n * - For either \\f$\\lambda_\\text{min} = 0\\f$ or \\f$\\lambda_\\text{max} = 0\\f$\n *   (i.e. all characteristics move in the same direction) the HLL boundary\n *   correction reduces to pure upwinding.\n * - Some references use \\f$S\\f$ instead of \\f$\\lambda\\f$ for the\n *   signal/characteristic speeds\n * - It may be possible to use the slower speeds for the magnetic field and\n *   fluid part of the system in order to make the flux less dissipative for\n *   those variables.\n */\nclass Hll final : public evolution::BoundaryCorrection {\n public:\n  struct LargestOutgoingCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n  struct LargestIngoingCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n  struct MagneticFieldMagnitudeForHydro {\n    static constexpr Options::String help = {\n        \"When the magnetic field is below this value we use the hydro \"\n        \"characteristic speeds.\"};\n    using type = double;\n  };\n  struct LightSpeedDensityCutoff {\n    static constexpr Options::String help = {\n        \"When the density is below this value we just use the light speed for \"\n        \"the characteristic speeds.\"};\n    using type = double;\n  };\n  using options =\n      tmpl::list<MagneticFieldMagnitudeForHydro, LightSpeedDensityCutoff>;\n  static constexpr Options::String help = {\n      \"Computes the HLL boundary correction term for the GRMHD system.\"};\n\n  Hll() = default;\n  Hll(const Hll&) = default;\n  Hll& operator=(const Hll&) = default;\n  Hll(Hll&&) = default;\n  Hll& operator=(Hll&&) = default;\n  ~Hll() override = default;\n\n  Hll(double magnetic_field_magnitude_for_hydro,\n      double light_speed_density_cutoff);\n\n  /// \\cond\n  explicit Hll(CkMigrateMessage* /*unused*/);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Hll);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::TildeD, Tags::TildeYe, Tags::TildeTau,\n                 Tags::TildeS<Frame::Inertial>, Tags::TildeB<Frame::Inertial>,\n                 Tags::TildePhi, ::Tags::NormalDotFlux<Tags::TildeD>,\n                 ::Tags::NormalDotFlux<Tags::TildeYe>,\n                 ::Tags::NormalDotFlux<Tags::TildeTau>,\n                 ::Tags::NormalDotFlux<Tags::TildeS<Frame::Inertial>>,\n                 ::Tags::NormalDotFlux<Tags::TildeB<Frame::Inertial>>,\n                 ::Tags::NormalDotFlux<Tags::TildePhi>,\n                 LargestOutgoingCharSpeed, LargestIngoingCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n      hydro::Tags::SpatialVelocityOneForm<DataVector, 3, Frame::Inertial>>;\n  using dg_package_data_primitive_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>>;\n  using dg_package_data_volume_tags =\n      tmpl::list<hydro::Tags::GrmhdEquationOfState>;\n    using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_d,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> packaged_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> packaged_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_phi,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_d,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          packaged_normal_dot_flux_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          packaged_normal_dot_flux_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_phi,\n      gsl::not_null<Scalar<DataVector>*> packaged_largest_outgoing_char_speed,\n      gsl::not_null<Scalar<DataVector>*> packaged_largest_ingoing_char_speed,\n\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_d,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_ye,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_tau,\n      const tnsr::Ij<DataVector, 3, Frame::Inertial>& flux_tilde_s,\n      const tnsr::IJ<DataVector, 3, Frame::Inertial>& flux_tilde_b,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_phi,\n\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& spatial_velocity_one_form,\n\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& electron_fraction,\n      const Scalar<DataVector>& temperature,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const EquationsOfState::EquationOfState<true, 3>& equation_of_state)\n      const;\n\n  static void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_d,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          boundary_correction_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          boundary_correction_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_phi,\n      const Scalar<DataVector>& tilde_d_int,\n      const Scalar<DataVector>& tilde_ye_int,\n      const Scalar<DataVector>& tilde_tau_int,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_int,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_int,\n      const Scalar<DataVector>& tilde_phi_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_d_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_ye_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_tau_int,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_s_int,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_b_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_phi_int,\n      const Scalar<DataVector>& largest_outgoing_char_speed_int,\n      const Scalar<DataVector>& largest_ingoing_char_speed_int,\n      const Scalar<DataVector>& tilde_d_ext,\n      const Scalar<DataVector>& tilde_ye_ext,\n      const Scalar<DataVector>& tilde_tau_ext,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_ext,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_ext,\n      const Scalar<DataVector>& tilde_phi_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_d_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_ye_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_tau_ext,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_s_ext,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_b_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_phi_ext,\n      const Scalar<DataVector>& largest_outgoing_char_speed_ext,\n      const Scalar<DataVector>& largest_ingoing_char_speed_ext,\n      dg::Formulation dg_formulation);\n\n private:\n  friend bool operator==(const Hll& lhs, const Hll& rhs);\n\n  double magnetic_field_magnitude_for_hydro_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double light_speed_density_cutoff_{\n      std::numeric_limits<double>::signaling_NaN()};\n};\nbool operator!=(const Hll& lhs, const Hll& rhs);\n}  // namespace grmhd::ValenciaDivClean::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace grmhd::ValenciaDivClean::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp\"\n\n#include <pup.h>\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::BoundaryCorrections {\nRusanov::Rusanov(CkMigrateMessage* /*unused*/) {}\n\nstd::unique_ptr<evolution::BoundaryCorrection> Rusanov::get_clone() const {\n  return std::make_unique<Rusanov>(*this);\n}\n\nvoid Rusanov::pup(PUP::er& p) { BoundaryCorrection::pup(p); }\n\ndouble Rusanov::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        packaged_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        packaged_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        packaged_normal_dot_flux_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        packaged_normal_dot_flux_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_d,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_ye,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_tau,\n    const tnsr::Ij<DataVector, 3, Frame::Inertial>& flux_tilde_s,\n    const tnsr::IJ<DataVector, 3, Frame::Inertial>& flux_tilde_b,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_phi,\n\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, 3,\n                  Frame::Inertial>& /*spatial_velocity_one_form*/,\n\n    const Scalar<DataVector>& /*rest_mass_density*/,\n    const Scalar<DataVector>& /*electron_fraction*/,\n    const Scalar<DataVector>& /*temperature*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*spatial_velocity*/,\n\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n    const EquationsOfState::EquationOfState<true, 3>&\n    /*equation_of_state*/) {\n  {\n    // Compute max abs char speed\n    Scalar<DataVector>& shift_dot_normal = *packaged_tilde_d;\n    dot_product(make_not_null(&shift_dot_normal), shift, normal_covector);\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*packaged_abs_char_speed) =\n          max(abs(-get(lapse) - get(shift_dot_normal) -\n                  get(*normal_dot_mesh_velocity)),\n              abs(get(lapse) - get(shift_dot_normal) -\n                  get(*normal_dot_mesh_velocity)));\n    } else {\n      get(*packaged_abs_char_speed) =\n          max(abs(-get(lapse) - get(shift_dot_normal)),\n              abs(get(lapse) - get(shift_dot_normal)));\n    }\n  }\n\n  *packaged_tilde_d = tilde_d;\n  *packaged_tilde_ye = tilde_ye;\n  *packaged_tilde_tau = tilde_tau;\n  *packaged_tilde_s = tilde_s;\n  *packaged_tilde_b = tilde_b;\n  *packaged_tilde_phi = tilde_phi;\n\n  normal_dot_flux(packaged_normal_dot_flux_tilde_d, normal_covector,\n                  flux_tilde_d);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_ye, normal_covector,\n                  flux_tilde_ye);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_tau, normal_covector,\n                  flux_tilde_tau);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_s, normal_covector,\n                  flux_tilde_s);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_b, normal_covector,\n                  flux_tilde_b);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_phi, normal_covector,\n                  flux_tilde_phi);\n\n  return max(get(*packaged_abs_char_speed));\n}\n\nvoid Rusanov::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        boundary_correction_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        boundary_correction_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_phi,\n    const Scalar<DataVector>& tilde_d_int,\n    const Scalar<DataVector>& tilde_ye_int,\n    const Scalar<DataVector>& tilde_tau_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_int,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_int,\n    const Scalar<DataVector>& tilde_phi_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_d_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_ye_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_tau_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_s_int,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_b_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_phi_int,\n    const Scalar<DataVector>& abs_char_speed_int,\n    const Scalar<DataVector>& tilde_d_ext,\n    const Scalar<DataVector>& tilde_ye_ext,\n    const Scalar<DataVector>& tilde_tau_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_ext,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_ext,\n    const Scalar<DataVector>& tilde_phi_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_d_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_ye_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_tau_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_s_ext,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_b_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_phi_ext,\n    const Scalar<DataVector>& abs_char_speed_ext,\n    const dg::Formulation dg_formulation) {\n  if (dg_formulation == dg::Formulation::WeakInertial) {\n    get(*boundary_correction_tilde_d) =\n        0.5 * (get(normal_dot_flux_tilde_d_int) -\n               get(normal_dot_flux_tilde_d_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_d_ext) - get(tilde_d_int));\n    get(*boundary_correction_tilde_ye) =\n        0.5 * (get(normal_dot_flux_tilde_ye_int) -\n               get(normal_dot_flux_tilde_ye_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_ye_ext) - get(tilde_ye_int));\n    get(*boundary_correction_tilde_tau) =\n        0.5 * (get(normal_dot_flux_tilde_tau_int) -\n               get(normal_dot_flux_tilde_tau_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_tau_ext) - get(tilde_tau_int));\n    get(*boundary_correction_tilde_phi) =\n        0.5 * (get(normal_dot_flux_tilde_phi_int) -\n               get(normal_dot_flux_tilde_phi_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_phi_ext) - get(tilde_phi_int));\n\n    for (size_t i = 0; i < 3; ++i) {\n      boundary_correction_tilde_s->get(i) =\n          0.5 * (normal_dot_flux_tilde_s_int.get(i) -\n                 normal_dot_flux_tilde_s_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (tilde_s_ext.get(i) - tilde_s_int.get(i));\n      boundary_correction_tilde_b->get(i) =\n          0.5 * (normal_dot_flux_tilde_b_int.get(i) -\n                 normal_dot_flux_tilde_b_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (tilde_b_ext.get(i) - tilde_b_int.get(i));\n    }\n  } else {\n    get(*boundary_correction_tilde_d) =\n        -0.5 * (get(normal_dot_flux_tilde_d_int) +\n                get(normal_dot_flux_tilde_d_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_d_ext) - get(tilde_d_int));\n    get(*boundary_correction_tilde_ye) =\n        -0.5 * (get(normal_dot_flux_tilde_ye_int) +\n                get(normal_dot_flux_tilde_ye_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_ye_ext) - get(tilde_ye_int));\n    get(*boundary_correction_tilde_tau) =\n        -0.5 * (get(normal_dot_flux_tilde_tau_int) +\n                get(normal_dot_flux_tilde_tau_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_tau_ext) - get(tilde_tau_int));\n    get(*boundary_correction_tilde_phi) =\n        -0.5 * (get(normal_dot_flux_tilde_phi_int) +\n                get(normal_dot_flux_tilde_phi_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(tilde_phi_ext) - get(tilde_phi_int));\n\n    for (size_t i = 0; i < 3; ++i) {\n      boundary_correction_tilde_s->get(i) =\n          -0.5 * (normal_dot_flux_tilde_s_int.get(i) +\n                  normal_dot_flux_tilde_s_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (tilde_s_ext.get(i) - tilde_s_int.get(i));\n      boundary_correction_tilde_b->get(i) =\n          -0.5 * (normal_dot_flux_tilde_b_int.get(i) +\n                  normal_dot_flux_tilde_b_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (tilde_b_ext.get(i) - tilde_b_int.get(i));\n    }\n  }\n}\n\nbool operator==(const Rusanov& /*lhs*/, const Rusanov& /*rhs*/) { return true; }\n\nbool operator!=(const Rusanov& lhs, const Rusanov& rhs) {\n  return not(lhs == rhs);\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Rusanov::my_PUP_ID = 0;\n}  // namespace grmhd::ValenciaDivClean::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::BoundaryCorrections {\n/*!\n * \\brief A Rusanov/local Lax-Friedrichs Riemann solver\n *\n * Let \\f$U\\f$ be the state vector of evolved variables, \\f$F^i\\f$ the\n * corresponding fluxes, and \\f$n_i\\f$ be the outward directed unit normal to\n * the interface. Denoting \\f$F := n_i F^i\\f$, the %Rusanov boundary correction\n * is\n *\n * \\f{align*}\n * G_\\text{Rusanov} = \\frac{F_\\text{int} - F_\\text{ext}}{2} -\n * \\frac{\\text{max}\\left(\\{|\\lambda_\\text{int}|\\},\n * \\{|\\lambda_\\text{ext}|\\}\\right)}{2} \\left(U_\\text{ext} - U_\\text{int}\\right),\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior, and\n * \\f$\\{|\\lambda|\\}\\f$ is the set of characteristic/signal speeds. The minus\n * sign in front of the \\f$F_{\\text{ext}}\\f$ is necessary because the outward\n * directed normal of the neighboring element has the opposite sign, i.e.\n * \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$. The characteristic/signal speeds\n * are given in the documentation for\n * grmhd::ValenciaDivClean::characteristic_speeds(). Since the fluid is\n * travelling slower than the speed of light, the speeds we are interested in\n * are\n *\n * \\f{align*}{\n *   \\lambda_{\\pm}&=\\pm\\alpha-\\beta^i n_i,\n * \\f}\n *\n * which correspond to the divergence cleaning field.\n *\n * \\note\n * - In the strong form the `dg_boundary_terms` function returns\n * \\f$G - F_\\text{int}\\f$\n * - It may be possible to use the slower speeds for the magnetic field and\n * fluid part of the system in order to make the flux less dissipative for\n * those variables.\n */\nclass Rusanov final : public evolution::BoundaryCorrection {\n private:\n  struct AbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the Rusanov or local Lax-Friedrichs boundary correction term \"\n      \"for the GRMHD system.\"};\n\n  Rusanov() = default;\n  Rusanov(const Rusanov&) = default;\n  Rusanov& operator=(const Rusanov&) = default;\n  Rusanov(Rusanov&&) = default;\n  Rusanov& operator=(Rusanov&&) = default;\n  ~Rusanov() override = default;\n\n  /// \\cond\n  explicit Rusanov(CkMigrateMessage* /*unused*/);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Rusanov);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::TildeD, Tags::TildeYe, Tags::TildeTau,\n                 Tags::TildeS<Frame::Inertial>, Tags::TildeB<Frame::Inertial>,\n                 Tags::TildePhi, ::Tags::NormalDotFlux<Tags::TildeD>,\n                 ::Tags::NormalDotFlux<Tags::TildeYe>,\n                 ::Tags::NormalDotFlux<Tags::TildeTau>,\n                 ::Tags::NormalDotFlux<Tags::TildeS<Frame::Inertial>>,\n                 ::Tags::NormalDotFlux<Tags::TildeB<Frame::Inertial>>,\n                 ::Tags::NormalDotFlux<Tags::TildePhi>, AbsCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n      hydro::Tags::SpatialVelocityOneForm<DataVector, 3, Frame::Inertial>>;\n  using dg_package_data_primitive_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>>;\n  using dg_package_data_volume_tags =\n      tmpl::list<hydro::Tags::GrmhdEquationOfState>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  static double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_d,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> packaged_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> packaged_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> packaged_tilde_phi,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_d,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          packaged_normal_dot_flux_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          packaged_normal_dot_flux_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_phi,\n      gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_d,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_ye,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_tau,\n      const tnsr::Ij<DataVector, 3, Frame::Inertial>& flux_tilde_s,\n      const tnsr::IJ<DataVector, 3, Frame::Inertial>& flux_tilde_b,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_phi,\n\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const tnsr::i<DataVector, 3,\n                    Frame::Inertial>& /*spatial_velocity_one_form*/,\n\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*electron_fraction*/,\n      const Scalar<DataVector>& /*temperature*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& /*spatial_velocity*/,\n\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const EquationsOfState::EquationOfState<true, 3>&\n      /*equation_of_state*/);\n\n  static void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_d,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          boundary_correction_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          boundary_correction_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_phi,\n      const Scalar<DataVector>& tilde_d_int,\n      const Scalar<DataVector>& tilde_ye_int,\n      const Scalar<DataVector>& tilde_tau_int,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_int,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_int,\n      const Scalar<DataVector>& tilde_phi_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_d_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_ye_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_tau_int,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_s_int,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_b_int,\n      const Scalar<DataVector>& normal_dot_flux_tilde_phi_int,\n      const Scalar<DataVector>& abs_char_speed_int,\n      const Scalar<DataVector>& tilde_d_ext,\n      const Scalar<DataVector>& tilde_ye_ext,\n      const Scalar<DataVector>& tilde_tau_ext,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_ext,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b_ext,\n      const Scalar<DataVector>& tilde_phi_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_d_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_ye_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_tau_ext,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_s_ext,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n          normal_dot_flux_tilde_b_ext,\n      const Scalar<DataVector>& normal_dot_flux_tilde_phi_ext,\n      const Scalar<DataVector>& abs_char_speed_ext,\n      dg::Formulation dg_formulation);\n};\n\nbool operator==(const Rusanov& lhs, const Rusanov& rhs);\nbool operator!=(const Rusanov& lhs, const Rusanov& rhs);\n}  // namespace grmhd::ValenciaDivClean::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ValenciaDivClean)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Characteristics.cpp\n  ComovingMagneticFieldMagnitude.cpp\n  ConservativeFromPrimitive.cpp\n  FixConservatives.cpp\n  Flattener.cpp\n  Fluxes.cpp\n  PrimitiveFromConservative.cpp\n  PrimitiveFromConservativeOptions.cpp\n  SetVariablesNeededFixingToFalse.cpp\n  Sources.cpp\n  TimeDerivativeTerms.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AllSolutions.hpp\n  Characteristics.hpp\n  ComovingMagneticFieldMagnitude.hpp\n  ComputeFluxesFromPrimitives.hpp\n  ConservativeFromPrimitive.hpp\n  FixConservatives.hpp\n  Flattener.hpp\n  Fluxes.hpp\n  KastaunEtAl.hpp\n  KastaunEtAl.tpp\n  KastaunEtAlHydro.hpp\n  KastaunEtAlHydro.tpp\n  NewmanHamlin.hpp\n  NewmanHamlin.tpp\n  PalenzuelaEtAl.hpp\n  PalenzuelaEtAl.tpp\n  PrimitiveFromConservative.hpp\n  PrimitiveFromConservativeOptions.hpp\n  PrimitiveRecoveryData.hpp\n  QuadrupoleFormula.hpp\n  SetVariablesNeededFixingToFalse.hpp\n  Sources.hpp\n  System.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  TimeDerivativeTerms.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  Domain\n  DgSubcell\n  DiscontinuousGalerkin\n  ErrorHandling\n  Events\n  Evolution\n  FiniteDifference\n  GeneralRelativity\n  GrMhdAnalyticData\n  GrMhdSolutions\n  H5\n  Hydro\n  Options\n  RelativisticEulerSolutions\n  Utilities\n  VariableFixing\n  PRIVATE\n  RootFinding\n  Simd\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Instantiations)\nadd_subdirectory(Subcell)\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Characteristics.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid compute_characteristic_speeds_approximate_mhd(\n    const gsl::not_null<std::array<DataVector, 9>*> pchar_speeds,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift,\n    const tnsr::I<DataVector, 3>& spatial_velocity,\n    const Scalar<DataVector>& spatial_velocity_squared,\n    const Scalar<DataVector>& sound_speed_squared,\n    const Scalar<DataVector>& alfven_speed_squared,\n    const tnsr::i<DataVector, 3>& normal) {\n  const size_t num_grid_points = get(lapse).size();\n  auto& char_speeds = *pchar_speeds;\n  if (char_speeds[0].size() != num_grid_points) {\n    char_speeds[0] = DataVector(num_grid_points);\n  }\n  // Mapping of indices between GRMHD char speeds and relativistic Euler char\n  // speeds arrays.\n  //\n  // GRMHD     Rel Euler\n  //   1           0\n  //   2           1\n  //   3           2\n  //   4           3\n  //   5           1\n  //   6           1\n  //   7           4\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>, ::Tags::TempScalar<3>,\n                       ::Tags::TempScalar<4>, ::Tags::TempScalar<5>>>\n      temp_tensors{num_grid_points};\n\n  // Because we don't require char_speeds to be of the correct size we use a\n  // temp buffer for the dot product, then multiply by -1 assigning the result\n  // to char_speeds.\n  {\n    Scalar<DataVector>& normal_shift = get<::Tags::TempScalar<0>>(temp_tensors);\n    dot_product(make_not_null(&normal_shift), normal, shift);\n    char_speeds[0] = -1.0 * get(normal_shift);\n    char_speeds[1] = char_speeds[0];\n  }\n  Scalar<DataVector>& scaled_sound_speed_squared =\n      get<::Tags::TempScalar<5>>(temp_tensors);\n  get(scaled_sound_speed_squared) =\n      get(sound_speed_squared) +\n      get(alfven_speed_squared) * (1.0 - get(sound_speed_squared));\n  // Dim-fold degenerate eigenvalue, reuse normal_shift allocation\n  Scalar<DataVector>& normal_velocity =\n      get<::Tags::TempScalar<0>>(temp_tensors);\n  dot_product(make_not_null(&normal_velocity), normal, spatial_velocity);\n  char_speeds[2] = char_speeds[1] + get(lapse) * get(normal_velocity);\n  char_speeds[3] = char_speeds[2];\n  char_speeds[4] = char_speeds[3];\n  char_speeds[5] = char_speeds[2];\n  char_speeds[6] = char_speeds[2];\n\n  Scalar<DataVector>& one_minus_v_sqrd_cs_sqrd =\n      get<::Tags::TempScalar<1>>(temp_tensors);\n  get(one_minus_v_sqrd_cs_sqrd) =\n      1.0 - get(spatial_velocity_squared) * get(scaled_sound_speed_squared);\n  Scalar<DataVector>& vn_times_one_minus_cs_sqrd =\n      get<::Tags::TempScalar<2>>(temp_tensors);\n  get(vn_times_one_minus_cs_sqrd) =\n      get(normal_velocity) * (1.0 - get(scaled_sound_speed_squared));\n\n  Scalar<DataVector>& first_term = get<::Tags::TempScalar<3>>(temp_tensors);\n  get(first_term) = get(lapse) / get(one_minus_v_sqrd_cs_sqrd);\n  Scalar<DataVector>& second_term = get<::Tags::TempScalar<4>>(temp_tensors);\n  get(second_term) =\n      get(first_term) * sqrt(get(scaled_sound_speed_squared)) *\n      sqrt((1.0 - get(spatial_velocity_squared)) *\n           (get(one_minus_v_sqrd_cs_sqrd) -\n            get(normal_velocity) * get(vn_times_one_minus_cs_sqrd)));\n  get(first_term) *= get(vn_times_one_minus_cs_sqrd);\n\n  char_speeds[7] = char_speeds[1] + get(first_term) + get(second_term);\n  char_speeds[1] += get(first_term) - get(second_term);\n\n  char_speeds[8] = char_speeds[0] + get(lapse);\n  char_speeds[0] -= get(lapse);\n}\n}  // namespace\n\nnamespace grmhd::ValenciaDivClean {\ntemplate <size_t ThermodynamicDim>\nvoid characteristic_speeds_approximate_mhd(\n    const gsl::not_null<std::array<DataVector, 9>*> char_speeds,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) {\n  // Use a Variables to reduce total number of allocations. This is especially\n  // important in a multithreaded environment.\n  // Remaining places to reduce allocations:\n  // - EoS calls: 2 allocations\n  // - Pass temp pointer to Rel Euler: 1 allocation\n  // - Return a DataVectorArray (not yet implemented): 9 allocations\n  Variables<tmpl::list<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>,\n                       hydro::Tags::SpatialVelocitySquared<DataVector>,\n                       hydro::Tags::MagneticFieldOneForm<DataVector, 3>,\n                       hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>,\n                       hydro::Tags::MagneticFieldSquared<DataVector>,\n                       hydro::Tags::ComovingMagneticFieldSquared<DataVector>,\n                       hydro::Tags::SoundSpeedSquared<DataVector>>>\n      temp_tensors{get<0>(shift).size()};\n\n  const auto& spatial_velocity_one_form =\n      get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(temp_tensors);\n  raise_or_lower_index(\n      make_not_null(&get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(\n          temp_tensors)),\n      spatial_velocity, spatial_metric);\n  const auto& magnetic_field_one_form =\n      get<hydro::Tags::MagneticFieldOneForm<DataVector, 3>>(temp_tensors);\n  raise_or_lower_index(\n      make_not_null(\n          &get<hydro::Tags::MagneticFieldOneForm<DataVector, 3>>(temp_tensors)),\n      magnetic_field, spatial_metric);\n  const auto& magnetic_field_dot_spatial_velocity =\n      get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n          temp_tensors);\n  dot_product(\n      make_not_null(\n          &get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n              temp_tensors)),\n      magnetic_field, spatial_velocity_one_form);\n  const auto& spatial_velocity_squared =\n      get<hydro::Tags::SpatialVelocitySquared<DataVector>>(temp_tensors);\n  dot_product(\n      make_not_null(\n          &get<hydro::Tags::SpatialVelocitySquared<DataVector>>(temp_tensors)),\n      spatial_velocity, spatial_velocity_one_form);\n\n  const auto& magnetic_field_squared =\n      get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tensors);\n  dot_product(make_not_null(&get<hydro::Tags::MagneticFieldSquared<DataVector>>(\n                  temp_tensors)),\n              magnetic_field, magnetic_field_one_form);\n  const auto& comoving_magnetic_field_squared =\n      get<hydro::Tags::ComovingMagneticFieldSquared<DataVector>>(temp_tensors);\n  get(get<hydro::Tags::ComovingMagneticFieldSquared<DataVector>>(\n      temp_tensors)) =\n      get(magnetic_field_squared) / square(get(lorentz_factor)) +\n      square(get(magnetic_field_dot_spatial_velocity));\n\n  // reuse magnetic_field_squared allocation for Alfven speed squared\n  const auto& alfven_speed_squared =\n      get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tensors);\n  get(get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tensors)) =\n      get(comoving_magnetic_field_squared) /\n      (get(comoving_magnetic_field_squared) +\n       get(rest_mass_density) * get(specific_enthalpy));\n\n  Scalar<DataVector>& sound_speed_squared =\n      get<hydro::Tags::SoundSpeedSquared<DataVector>>(temp_tensors);\n  if constexpr (ThermodynamicDim == 1) {\n    get(sound_speed_squared) =\n        get(equation_of_state.chi_from_density(rest_mass_density)) +\n        get(equation_of_state.kappa_times_p_over_rho_squared_from_density(\n            rest_mass_density));\n    get(sound_speed_squared) /= get(specific_enthalpy);\n  } else if constexpr (ThermodynamicDim == 2) {\n    get(sound_speed_squared) =\n        get(equation_of_state.chi_from_density_and_energy(\n            rest_mass_density, specific_internal_energy)) +\n        get(equation_of_state\n                .kappa_times_p_over_rho_squared_from_density_and_energy(\n                    rest_mass_density, specific_internal_energy));\n    get(sound_speed_squared) /= get(specific_enthalpy);\n  } else if constexpr (ThermodynamicDim == 3) {\n    const auto temperature =\n        equation_of_state.temperature_from_density_and_energy(\n            rest_mass_density, specific_internal_energy, electron_fraction);\n    get(sound_speed_squared) =\n        get(equation_of_state.sound_speed_squared_from_density_and_temperature(\n            rest_mass_density, temperature, electron_fraction));\n  }\n\n  compute_characteristic_speeds_approximate_mhd(\n      char_speeds, lapse, shift, spatial_velocity, spatial_velocity_squared,\n      sound_speed_squared, alfven_speed_squared, unit_normal);\n}\n\ntemplate <size_t ThermodynamicDim>\nstd::array<DataVector, 9> characteristic_speeds_approximate_mhd(\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) {\n  std::array<DataVector, 9> char_speeds{};\n  characteristic_speeds_approximate_mhd(\n      make_not_null(&char_speeds), rest_mass_density, electron_fraction,\n      specific_internal_energy, specific_enthalpy, spatial_velocity,\n      lorentz_factor, magnetic_field, lapse, shift, spatial_metric, unit_normal,\n      equation_of_state);\n  return char_speeds;\n}\n\ntemplate <size_t ThermodynamicDim>\nvoid characteristic_speeds_hydro(\n    const gsl::not_null<std::array<DataVector, 3>*> char_speeds,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) {\n  const size_t num_grid_points = get(lorentz_factor).size();\n  if ((*char_speeds)[0].size() != num_grid_points) {\n    for (auto& cs : (*char_speeds)) {\n      cs = DataVector(num_grid_points, 0.0);\n    }\n  }\n\n  Variables<tmpl::list<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>,\n                       hydro::Tags::SpatialVelocitySquared<DataVector>,\n                       hydro::Tags::SoundSpeedSquared<DataVector>,\n                       ::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>, ::Tags::TempScalar<3>>>\n      temp_tensors{num_grid_points};\n\n  Scalar<DataVector>& normal_velocity =\n      get<::Tags::TempScalar<0>>(temp_tensors);\n  dot_product(make_not_null(&normal_velocity), unit_normal, spatial_velocity);\n\n  const auto& spatial_velocity_one_form =\n      get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(temp_tensors);\n  raise_or_lower_index(\n      make_not_null(&get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(\n          temp_tensors)),\n      spatial_velocity, spatial_metric);\n\n  const auto& spatial_velocity_squared =\n      get<hydro::Tags::SpatialVelocitySquared<DataVector>>(temp_tensors);\n  dot_product(\n      make_not_null(\n          &get<hydro::Tags::SpatialVelocitySquared<DataVector>>(temp_tensors)),\n      spatial_velocity, spatial_velocity_one_form);\n\n  Scalar<DataVector>& sound_speed_squared =\n      get<hydro::Tags::SoundSpeedSquared<DataVector>>(temp_tensors);\n  if constexpr (ThermodynamicDim == 1) {\n    get(sound_speed_squared) =\n        get(equation_of_state.chi_from_density(rest_mass_density)) +\n        get(equation_of_state.kappa_times_p_over_rho_squared_from_density(\n            rest_mass_density));\n    get(sound_speed_squared) /= get(specific_enthalpy);\n  } else if constexpr (ThermodynamicDim == 2) {\n    get(sound_speed_squared) =\n        get(equation_of_state.chi_from_density_and_energy(\n            rest_mass_density, specific_internal_energy)) +\n        get(equation_of_state\n                .kappa_times_p_over_rho_squared_from_density_and_energy(\n                    rest_mass_density, specific_internal_energy));\n    get(sound_speed_squared) /= get(specific_enthalpy);\n  } else if constexpr (ThermodynamicDim == 3) {\n    const auto temperature =\n        equation_of_state.temperature_from_density_and_energy(\n            rest_mass_density, specific_internal_energy, electron_fraction);\n    get(sound_speed_squared) =\n        get(equation_of_state.sound_speed_squared_from_density_and_temperature(\n            rest_mass_density, temperature, electron_fraction));\n  }\n\n  // Calculate the characteristic speed for non-degenerate ones\n  Scalar<DataVector>& denom = get<::Tags::TempScalar<1>>(temp_tensors);\n  get(denom) = 1.0 - get(spatial_velocity_squared) * get(sound_speed_squared);\n\n  Scalar<DataVector>& first_term = get<::Tags::TempScalar<2>>(temp_tensors);\n  get(first_term) =\n      (1.0 - get(sound_speed_squared)) * get(normal_velocity) / get(denom);\n\n  Scalar<DataVector>& second_term = get<::Tags::TempScalar<3>>(temp_tensors);\n  get(second_term) =\n      sqrt(get(sound_speed_squared)) *\n      sqrt(get(denom) - get(normal_velocity) * get(normal_velocity) *\n                            (1 - get(sound_speed_squared))) /\n      (get(lorentz_factor) * get(denom));\n\n  enum HydroSpeed : uint32_t {\n    NormalDotVelocity = 0,\n    LambdaPlus = 1,\n    LambdaMinus = 2\n  };\n  // Degenerate eigenvalue (normal dot velocity)\n  (*char_speeds)[HydroSpeed::NormalDotVelocity] = get(normal_velocity);\n  (*char_speeds)[HydroSpeed::LambdaPlus] = get(first_term) + get(second_term);\n  (*char_speeds)[HydroSpeed::LambdaMinus] = get(first_term) - get(second_term);\n}\n\n\ntemplate <size_t ThermodynamicDim>\nstd::array<DataVector, 3> characteristic_speeds_hydro(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) {\n  std::array<DataVector, 3> char_speeds{};\n  characteristic_speeds_hydro(\n      make_not_null(&char_speeds), spatial_velocity, rest_mass_density,\n      specific_internal_energy, specific_enthalpy, electron_fraction,\n      lorentz_factor, unit_normal, spatial_metric, equation_of_state);\n  return char_speeds;\n}\n\nnamespace Tags {\n\ntemplate <size_t ThermodynamicDim>\nvoid CharacteristicSpeedsCompute::function(\n    const gsl::not_null<return_type*> result,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& /* electron_fraction */,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) {\n  characteristic_speeds_approximate_mhd<ThermodynamicDim>(\n      result, rest_mass_density, /*electron_fraction*/ {},\n      specific_internal_energy, specific_enthalpy, spatial_velocity,\n      lorentz_factor, magnetic_field, lapse, shift, spatial_metric, unit_normal,\n      equation_of_state);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FUNCTION_INSTANTIATION(r, data)                                     \\\n  template void CharacteristicSpeedsCompute::function<DIM(data)>(           \\\n      const gsl::not_null<return_type*> result,                             \\\n      const Scalar<DataVector>& rest_mass_density,                          \\\n      const Scalar<DataVector>& electron_fraction,                          \\\n      const Scalar<DataVector>& specific_internal_energy,                   \\\n      const Scalar<DataVector>& specific_enthalpy,                          \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,      \\\n      const Scalar<DataVector>& lorentz_factor,                             \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,        \\\n      const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift, \\\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,       \\\n      const tnsr::i<DataVector, 3>& unit_normal,                            \\\n      const EquationsOfState::EquationOfState<true, DIM(data)>&             \\\n          equation_of_state);\n\nGENERATE_INSTANTIATIONS(FUNCTION_INSTANTIATION, (1, 2, 3))\n#undef DIM\n#undef FUNCTION_INSTANTIATION\n\n}  // namespace Tags\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template std::array<DataVector, 9>                                        \\\n  characteristic_speeds_approximate_mhd<GET_DIM(data)>(                     \\\n      const Scalar<DataVector>& rest_mass_density,                          \\\n      const Scalar<DataVector>& electron_fraction,                          \\\n      const Scalar<DataVector>& specific_internal_energy,                   \\\n      const Scalar<DataVector>& specific_enthalpy,                          \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,      \\\n      const Scalar<DataVector>& lorentz_factor,                             \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,        \\\n      const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift, \\\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,       \\\n      const tnsr::i<DataVector, 3>& unit_normal,                            \\\n      const EquationsOfState::EquationOfState<true, GET_DIM(data)>&         \\\n          equation_of_state);                                               \\\n  template void characteristic_speeds_approximate_mhd<GET_DIM(data)>(       \\\n      const gsl::not_null<std::array<DataVector, 9>*> char_speeds,          \\\n      const Scalar<DataVector>& rest_mass_density,                          \\\n      const Scalar<DataVector>& electron_fraction,                          \\\n      const Scalar<DataVector>& specific_internal_energy,                   \\\n      const Scalar<DataVector>& specific_enthalpy,                          \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,      \\\n      const Scalar<DataVector>& lorentz_factor,                             \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,        \\\n      const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift, \\\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,       \\\n      const tnsr::i<DataVector, 3>& unit_normal,                            \\\n      const EquationsOfState::EquationOfState<true, GET_DIM(data)>&         \\\n          equation_of_state);                                               \\\n  template std::array<DataVector, 3>                                        \\\n  characteristic_speeds_hydro<GET_DIM(data)>(                               \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,      \\\n      const Scalar<DataVector>& rest_mass_density,                          \\\n      const Scalar<DataVector>& specific_internal_energy,                   \\\n      const Scalar<DataVector>& specific_enthalpy,                          \\\n      const Scalar<DataVector>& electron_fraction,                          \\\n      const Scalar<DataVector>& lorentz_factor,                             \\\n      const tnsr::i<DataVector, 3>& unit_normal,                            \\\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,       \\\n      const EquationsOfState::EquationOfState<true, GET_DIM(data)>&         \\\n          equation_of_state);                                               \\\n  template void characteristic_speeds_hydro<GET_DIM(data)>(                 \\\n      const gsl::not_null<std::array<DataVector, 3>*> char_speeds,          \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,      \\\n      const Scalar<DataVector>& rest_mass_density,                          \\\n      const Scalar<DataVector>& specific_internal_energy,                   \\\n      const Scalar<DataVector>& specific_enthalpy,                          \\\n      const Scalar<DataVector>& electron_fraction,                          \\\n      const Scalar<DataVector>& lorentz_factor,                             \\\n      const tnsr::i<DataVector, 3>& unit_normal,                            \\\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,       \\\n      const EquationsOfState::EquationOfState<true, GET_DIM(data)>&         \\\n          equation_of_state);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace Tags {\ntemplate <typename Tag>\nstruct Normalized;\n}  // namespace Tags\n/// \\endcond\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\n/// @{\n/*!\n * \\brief Compute the characteristic speeds for the Valencia formulation of\n * GRMHD with divergence cleaning.\n *\n * Obtaining the exact form of the characteristic speeds involves the solution\n * of a nontrivial quartic equation for the fast and slow modes. Here we make\n * use of a common approximation in the literature (e.g. \\cite Gammie2003)\n * where the resulting characteristic speeds are analogous to those of the\n * Valencia formulation of the 3-D relativistic Euler system\n * (see RelativisticEuler::Valencia::characteristic_speeds),\n *\n * \\f{align*}\n * \\lambda_2 &= \\alpha \\Lambda^- - \\beta_n,\\\\\n * \\lambda_{3, 4, 5, 6, 7} &= \\alpha v_n - \\beta_n,\\\\\n * \\lambda_{8} &= \\alpha \\Lambda^+ - \\beta_n,\n * \\f}\n *\n * with the substitution\n *\n * \\f{align*}\n * c_s^2 \\longrightarrow c_s^2 + v_A^2(1 - c_s^2)\n * \\f}\n *\n * in the definition of \\f$\\Lambda^\\pm\\f$. Here \\f$v_A\\f$ is the Alfvén\n * speed. In addition, two more speeds corresponding to the divergence cleaning\n * mode and the longitudinal magnetic field are added,\n *\n * \\f{align*}\n * \\lambda_1 = -\\alpha - \\beta_n,\\\\\n * \\lambda_9 = \\alpha - \\beta_n.\n * \\f}\n *\n * \\note The ordering assumed here is such that, in the Newtonian limit,\n * the exact expressions for \\f$\\lambda_{2, 8}\\f$, \\f$\\lambda_{3, 7}\\f$,\n * and \\f$\\lambda_{4, 6}\\f$ should reduce to the\n * corresponding fast modes, Alfvén modes, and slow modes, respectively.\n * See \\cite Dedner2002 for a detailed description of the hyperbolic\n * characterization of Newtonian MHD.  In terms of the primitive variables:\n *\n * \\f{align*}\n * v^2 &= \\gamma_{mn} v^m v^n \\\\\n * c_s^2 &= \\frac{1}{h} \\left[ \\left( \\frac{\\partial p}{\\partial \\rho}\n * \\right)_\\epsilon +\n * \\frac{p}{\\rho^2} \\left(\\frac{\\partial p}{\\partial \\epsilon}\n * \\right)_\\rho \\right] \\\\\n * v_A^2 &= \\frac{b^2}{b^2 + \\rho h} \\\\\n * b^2 &= \\frac{1}{W^2} \\gamma_{mn} B^m B^n + \\left( \\gamma_{mn} B^m v^n\n * \\right)^2\n * \\f}\n *\n * where \\f$\\gamma_{mn}\\f$ is the spatial metric, \\f$\\rho\\f$ is the rest\n * mass density, \\f$W = 1/\\sqrt{1-v_i v^i}\\f$ is the Lorentz factor, \\f$h = 1 +\n * \\epsilon + \\frac{p}{\\rho}\\f$ is the specific enthalpy, \\f$v^i\\f$ is the\n * spatial velocity, \\f$\\epsilon\\f$ is the specific internal energy, \\f$p\\f$ is\n * the pressure, and \\f$B^i\\f$ is the spatial magnetic field measured by an\n * Eulerian observer.\n */\n\ntemplate <size_t ThermodynamicDim>\nstd::array<DataVector, 9> characteristic_speeds_approximate_mhd(\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state);\n\ntemplate <size_t ThermodynamicDim>\nvoid characteristic_speeds_approximate_mhd(\n    gsl::not_null<std::array<DataVector, 9>*> char_speeds,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state);\n/// @}\n\n/// @{\n/*!\n * \\brief Compute the characteristic speeds for the relativistic hydrodynamics\n * system in the Eulerian frame.\n *\n * These are the eigenvalues of the flux Jacobian projected along a spatial\n * direction with unit normal \\f$ s_i\\f$, measured by an Eulerian observer.\n * They consist of four degenerate modes and two non-degenerate modes:\n *\n * \\f{align*}\n * \\lambda_\\mathrm{deg} &= v_n ,\\\\\n * \\lambda_{\\pm} &=\n *   \\frac{ (1 - c_s^2)\\,v_n \\pm c_s\\,d / W^2 }\n *        { 1 - v^2 c_s^2 }.\n * \\f}\n *\n * where\n *\n * \\f{align*}\n * d &\\equiv\n *   W\\,\\sqrt{ 1 - v^2 c_s^2 - v_n^2(1 - c_s^2) } .\n * \\f}\n *\n * The variables are defined as:\n *\n * \\f{align*}\n * v^2 &= \\gamma_{mn} v^m v^n ,\\\\\n * v_n &= v^a s_a ,\\\\\n * W &= \\frac{1}{\\sqrt{1 - v^a v_a}} ,\\\\\n * h &= 1 + \\epsilon + \\frac{p}{\\rho} ,\\\\\n * c_s^2 &= \\frac{1}{h}\\left(\\chi + \\kappa \\frac{p}{\\rho^2}\\right) ,\n * \\f}\n *\n * The returned array has size 3 and contains the unique characteristic speeds:\n *  - `char_speeds[0]`: \\f$v_n\\f$ (degenerate eigenvalue, multiplicity 4 if the\n * electron fraction is included),\n *  - `char_speeds[1]`: \\f$\\lambda_+\\f$ (right-propagating acoustic mode),\n *  - `char_speeds[2]`: \\f$\\lambda_-\\f$ (left-propagating acoustic mode).\n */\n\ntemplate <size_t ThermodynamicDim>\nvoid characteristic_speeds_hydro(\n    gsl::not_null<std::array<DataVector, 3>*> pchar_speeds,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state);\n\ntemplate <size_t ThermodynamicDim>\nstd::array<DataVector, 3> characteristic_speeds_hydro(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& specific_enthalpy,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::i<DataVector, 3>& unit_normal,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state);\n/// @}\n\nnamespace Tags {\n/// \\brief Compute the characteristic speeds for the Valencia formulation of\n/// GRMHD with divergence cleaning.\n///\n/// \\details see grmhd::ValenciaDivClean::characteristic_speeds\nstruct CharacteristicSpeedsCompute : Tags::CharacteristicSpeeds,\n                                     db::ComputeTag {\n  using base = Tags::CharacteristicSpeeds;\n  using argument_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpecificEnthalpy<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<3>>,\n                 hydro::Tags::GrmhdEquationOfState>;\n\n  using volume_tags = tmpl::list<hydro::Tags::GrmhdEquationOfState>;\n\n  using return_type = std::array<DataVector, 9>;\n\n  template <size_t ThermodynamicDim>\n  void function(gsl::not_null<return_type*> result,\n                const Scalar<DataVector>& rest_mass_density,\n                const Scalar<DataVector>& /* electron_fraction */,\n                const Scalar<DataVector>& specific_internal_energy,\n                const Scalar<DataVector>& specific_enthalpy,\n                const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n                const Scalar<DataVector>& lorentz_factor,\n                const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n                const Scalar<DataVector>& lapse,\n                const tnsr::I<DataVector, 3>& shift,\n                const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n                const tnsr::i<DataVector, 3>& unit_normal,\n                const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n                    equation_of_state);\n};\n\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\nstruct ComputeLargestCharacteristicSpeed : db::ComputeTag,\n                                           LargestCharacteristicSpeed {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(gsl::not_null<double*> speed,\n                       const Scalar<DataVector>& lapse,\n                       const tnsr::I<DataVector, 3>& shift,\n                       const tnsr::ii<DataVector, 3>& spatial_metric) {\n    const auto shift_magnitude = magnitude(shift, spatial_metric);\n    *speed = max(get(shift_magnitude) + get(lapse));\n  }\n};\n}  // namespace Tags\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/ComovingMagneticFieldMagnitude.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComovingMagneticFieldMagnitude.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::Tags {\n\nvoid ComovingMagneticFieldMagnitudeCompute::function(\n    const gsl::not_null<Scalar<DataVector>*> comoving_magnetic_field_magnitude,\n    const tnsr::I<DataVector, 3>& magnetic_field,\n    const tnsr::I<DataVector, 3>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric) {\n  Variables<tmpl::list<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>,\n                       hydro::Tags::MagneticFieldSquared<DataVector>>>\n      temp_tensors{get(lorentz_factor).size()};\n\n  const auto& magnetic_field_dot_spatial_velocity =\n      get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n          temp_tensors);\n  dot_product(\n      make_not_null(\n          &get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n              temp_tensors)),\n      magnetic_field, spatial_velocity, spatial_metric);\n\n  const auto& magnetic_field_squared =\n      get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tensors);\n  dot_product(make_not_null(&get<hydro::Tags::MagneticFieldSquared<DataVector>>(\n                  temp_tensors)),\n              magnetic_field, magnetic_field, spatial_metric);\n\n  get(*comoving_magnetic_field_magnitude) =\n      sqrt(get(magnetic_field_squared) / square(get(lorentz_factor)) +\n           square(get(magnetic_field_dot_spatial_velocity)));\n}\n\n}  // namespace grmhd::ValenciaDivClean::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/ComovingMagneticFieldMagnitude.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::Tags {\n/*!\n * \\brief Compute the magnitude of the comoving magnetic field.\n *\n * \\f{align}\n *  \\sqrt{b^2} = \\left( \\frac{B^2}{W^2} + (B^iv_i)^2 \\right)^{1/2}\n * \\f}\n *\n * \\note This ComputeTag is for observation and monitoring purpose, not related\n * to the actual time evolution.\n *\n */\nstruct ComovingMagneticFieldMagnitudeCompute\n    : hydro::Tags::ComovingMagneticFieldMagnitude<DataVector>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<hydro::Tags::MagneticField<DataVector, 3>,\n                                   hydro::Tags::SpatialVelocity<DataVector, 3>,\n                                   hydro::Tags::LorentzFactor<DataVector>,\n                                   gr::Tags::SpatialMetric<DataVector, 3>>;\n  using return_type = Scalar<DataVector>;\n  using base = hydro::Tags::ComovingMagneticFieldMagnitude<DataVector>;\n\n  static void function(\n      gsl::not_null<Scalar<DataVector>*> comoving_magnetic_field_magnitude,\n      const tnsr::I<DataVector, 3>& magnetic_field,\n      const tnsr::I<DataVector, 3>& spatial_velocity,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric);\n};\n}  // namespace grmhd::ValenciaDivClean::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/ComputeFluxesFromPrimitives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace detail {\ntemplate <typename FluxTags, typename BoundaryVarsType, typename... ReturnTags,\n          typename... ConservativeTags, typename... ArgumentTags,\n          typename... FluxArgumentTags>\nvoid compute_fluxes_from_primitives_impl(\n    const gsl::not_null<Variables<FluxTags>*> flux_vars,\n    const BoundaryVarsType& boundary_vars, tmpl::list<ReturnTags...> /*meta*/,\n    tmpl::list<ConservativeTags...> /*meta*/,\n    tmpl::list<ArgumentTags...> /*meta*/,\n    tmpl::list<FluxArgumentTags...> /*meta*/) {\n  Variables<typename tmpl::list<ConservativeTags...>> conserved_vars{\n      flux_vars->number_of_grid_points()};\n  grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n      make_not_null(&get<ConservativeTags>(conserved_vars))...,\n      get<ArgumentTags>(boundary_vars)...);\n  grmhd::ValenciaDivClean::ComputeFluxes::apply(\n      make_not_null(&get<ReturnTags>(*flux_vars))...,\n      get<ConservativeTags>(conserved_vars)...,\n      get<FluxArgumentTags>(boundary_vars)...);\n}\n}  // namespace detail\n\n/*!\n * \\brief Helper function that computes fluxes from a given set of primitive\n * variables. Primarily used for filling neighbor data when flux information is\n * needed for boundaries.\n *\n * \\warning This computes the conservative variables internally. If you also\n * need those, you shouldn't call this function.\n */\ntemplate <typename FluxTags, typename BoundaryVarsType>\nvoid compute_fluxes_from_primitives(\n    const gsl::not_null<Variables<FluxTags>*> flux_vars,\n    const BoundaryVarsType& boundary_vars) {\n  using ConservativeTags =\n      typename grmhd::ValenciaDivClean::ConservativeFromPrimitive::return_tags;\n  using FluxArgumentTags =\n      typename grmhd::ValenciaDivClean::ComputeFluxes::argument_tags;\n  using flux_arg_tags = tmpl::back<tmpl::split_at<\n      FluxArgumentTags, tmpl::next<tmpl::index_of<\n                            FluxArgumentTags, tmpl::back<ConservativeTags>>>>>;\n  detail::compute_fluxes_from_primitives_impl(\n      flux_vars, boundary_vars,\n      typename grmhd::ValenciaDivClean::ComputeFluxes::return_tags{},\n      ConservativeTags{},\n      typename grmhd::ValenciaDivClean::ConservativeFromPrimitive::\n          argument_tags{},\n      flux_arg_tags{});\n}\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\n\nvoid ConservativeFromPrimitive::apply(\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> tilde_phi,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& pressure,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const Scalar<DataVector>& divergence_cleaning_field) {\n  Variables<tmpl::list<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>,\n                       hydro::Tags::SpatialVelocitySquared<DataVector>,\n                       hydro::Tags::MagneticFieldOneForm<DataVector, 3>,\n                       hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>,\n                       hydro::Tags::MagneticFieldSquared<DataVector>>>\n      temp_tensors{get(rest_mass_density).size()};\n  auto& spatial_velocity_one_form =\n      get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(temp_tensors);\n  raise_or_lower_index(make_not_null(&spatial_velocity_one_form),\n                       spatial_velocity, spatial_metric);\n  auto& magnetic_field_one_form =\n      get<hydro::Tags::MagneticFieldOneForm<DataVector, 3>>(temp_tensors);\n  raise_or_lower_index(make_not_null(&magnetic_field_one_form), magnetic_field,\n                       spatial_metric);\n  auto& magnetic_field_dot_spatial_velocity =\n      get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n          temp_tensors);\n  dot_product(make_not_null(&magnetic_field_dot_spatial_velocity),\n              magnetic_field, spatial_velocity_one_form);\n  auto& spatial_velocity_squared =\n      get<hydro::Tags::SpatialVelocitySquared<DataVector>>(temp_tensors);\n  dot_product(make_not_null(&spatial_velocity_squared), spatial_velocity,\n              spatial_velocity_one_form);\n\n  auto& magnetic_field_squared =\n      get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tensors);\n  dot_product(make_not_null(&magnetic_field_squared), magnetic_field,\n              magnetic_field_one_form);\n\n  get(*tilde_d) = get(sqrt_det_spatial_metric) * get(rest_mass_density) *\n                  get(lorentz_factor);\n\n  get(*tilde_ye) = get(*tilde_d) * get(electron_fraction);\n\n  get(*tilde_tau) = get(sqrt_det_spatial_metric) *\n                      (square(get(lorentz_factor)) *\n                        (get(rest_mass_density) *\n                          (get(specific_internal_energy) +\n                           get(spatial_velocity_squared) * get(lorentz_factor) /\n                              (get(lorentz_factor) + 1.)) +\n                         get(pressure) * get(spatial_velocity_squared)) +\n                       0.5 * get(magnetic_field_squared) *\n                        (1.0 + get(spatial_velocity_squared)) -\n                       0.5 * square(get(magnetic_field_dot_spatial_velocity)));\n\n  // Reuse allocation\n  Scalar<DataVector>& common_factor =\n      get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tensors);\n  get(common_factor) +=\n      (get(rest_mass_density) * (1.0 + get(specific_internal_energy)) +\n       get(pressure)) *\n      square(get(lorentz_factor));\n  get(common_factor) *= get(sqrt_det_spatial_metric);\n\n  get(magnetic_field_dot_spatial_velocity) *= get(sqrt_det_spatial_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    tilde_s->get(i) = get(common_factor) * spatial_velocity_one_form.get(i) -\n                      get(magnetic_field_dot_spatial_velocity) *\n                          magnetic_field_one_form.get(i);\n  }\n  for (size_t i = 0; i < 3; ++i) {\n    tilde_b->get(i) = get(sqrt_det_spatial_metric) * magnetic_field.get(i);\n  }\n  get(*tilde_phi) =\n      get(sqrt_det_spatial_metric) * get(divergence_cleaning_field);\n}\n\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\n\n/*!\n * \\brief Compute the conservative variables from primitive variables\n *\n * \\f{align*}\n * {\\tilde D} = & \\sqrt{\\gamma} \\rho W \\\\\n * {\\tilde Y}_e = & \\sqrt{\\gamma} \\rho Y_e W \\\\\n * {\\tilde S}_i = & \\sqrt{\\gamma} \\left( \\rho h W^2 v_i + B^m B_m v_i - B^m v_m\n * B_i \\right) \\\\\n * {\\tilde \\tau} = & \\sqrt{\\gamma} \\left[ \\rho h W^2 - p - \\rho W - \\frac{1}{2}\n * (B^m v_m)^2 + \\frac{1}{2} B^m B_m \\left( 1 + v^m v_m \\right) \\right] \\\\\n * {\\tilde B}^i = & \\sqrt{\\gamma} B^i \\\\\n * {\\tilde \\Phi} = & \\sqrt{\\gamma} \\Phi\n * \\f}\n *\n * where the conserved variables \\f${\\tilde D}\\f$, \\f$\\tilde{Y}_e\\f$, \\f${\\tilde\n * S}_i\\f$, \\f${\\tilde \\tau}\\f$, \\f${\\tilde B}^i\\f$, and \\f${\\tilde \\Phi}\\f$ are\n * a generalized mass-energy density, electron fraction, momentum density,\n * specific internal energy density, magnetic field, and divergence cleaning\n * field.  Furthermore \\f$\\gamma\\f$ is the determinant of the spatial metric,\n * \\f$\\rho\\f$ is the rest mass density, \\f$Y_e\\f$ is the electron fraction,\n * \\f$W = 1/\\sqrt{1-v_i v^i}\\f$ is the Lorentz factor, \\f$h = 1 + \\epsilon +\n * \\frac{p}{\\rho}\\f$ is the specific enthalpy, \\f$v^i\\f$ is the spatial\n * velocity, \\f$\\epsilon\\f$ is the specific internal energy, \\f$p\\f$ is the\n * pressure, \\f$B^i\\f$ is the spatial magnetic field measured by an Eulerian\n * observer, and \\f$\\Phi\\f$ is a divergence cleaning field.\n *\n * The quantity \\f${\\tilde \\tau}\\f$ is rewritten as in `RelativisticEuler`\n * to avoid cancellation error in the non-relativistic limit:\n * \\f[\n * \\left( \\rho h W^2 - p - \\rho W \\right) \\longrightarrow\n *  W^2 \\left[ \\rho \\left( \\epsilon + v^2\n * \\frac{W}{W + 1} \\right) + p v^2 \\right] .\\f]\n */\nstruct ConservativeFromPrimitive {\n  using return_tags = tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                                 grmhd::ValenciaDivClean::Tags::TildeYe,\n                                 grmhd::ValenciaDivClean::Tags::TildeTau,\n                                 grmhd::ValenciaDivClean::Tags::TildeS<>,\n                                 grmhd::ValenciaDivClean::Tags::TildeB<>,\n                                 grmhd::ValenciaDivClean::Tags::TildePhi>;\n\n  using argument_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_b,\n      gsl::not_null<Scalar<DataVector>*> tilde_phi,\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& electron_fraction,\n      const Scalar<DataVector>& specific_internal_energy,\n      const Scalar<DataVector>& pressure,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const Scalar<DataVector>& divergence_cleaning_field);\n};\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/BoundaryConditionGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\n/*!\n * \\brief Computes finite difference ghost data for external boundary\n * conditions.\n *\n * If the element is at the external boundary, computes FD ghost data with a\n * given boundary condition and stores it into neighbor data with {direction,\n * ElementId::external_boundary_id()} as the mortar_id key.\n *\n * \\note Subcell needs to be enabled for boundary elements. Otherwise this\n * function would be never called.\n *\n */\nstruct BoundaryConditionGhostData {\n  template <typename DbTagsList>\n  static void apply(gsl::not_null<db::DataBox<DbTagsList>*> box,\n                    const Element<3>& element,\n                    const Reconstructor& reconstructor);\n\n private:\n  template <typename FdBoundaryConditionHelper, typename DbTagsList,\n            typename... FdBoundaryConditionArgsTags>\n  // A helper function for calling fd_ghost() of BoundaryCondition subclasses\n  static void apply_subcell_boundary_condition_impl(\n      FdBoundaryConditionHelper& fd_boundary_condition_helper,\n      const gsl::not_null<db::DataBox<DbTagsList>*>& box,\n      tmpl::list<FdBoundaryConditionArgsTags...>) {\n    return fd_boundary_condition_helper(\n        db::get<FdBoundaryConditionArgsTags>(*box)...);\n  }\n};\n\ntemplate <typename DbTagsList>\nvoid BoundaryConditionGhostData::apply(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box,\n    const Element<3>& element, const Reconstructor& reconstructor) {\n  const auto& external_boundary_condition =\n      db::get<domain::Tags::ExternalBoundaryConditions<3>>(*box).at(\n          element.id().block_id());\n\n  // Check if the element is on the external boundary. If not, the caller is\n  // doing something wrong (e.g. trying to compute FD ghost data with boundary\n  // conditions at an element which is not on the external boundary).\n  ASSERT(not element.external_boundaries().empty(),\n         \"The element (ID : \" << element.id()\n                              << \") is not on external boundaries\");\n\n  const Mesh<3> subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<3>>(*box);\n\n  const size_t ghost_zone_size{reconstructor.ghost_zone_size()};\n\n  // Tags and tags list for FD reconstruction\n  using RestMassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using LorentzFactorTimesSpatialVelocity =\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n  using MagneticField = hydro::Tags::MagneticField<DataVector, 3>;\n  using DivergenceCleaningField =\n      hydro::Tags::DivergenceCleaningField<DataVector>;\n\n  using prims_for_reconstruction =\n      tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                 LorentzFactorTimesSpatialVelocity, MagneticField,\n                 DivergenceCleaningField>;\n\n  size_t num_prims_tensor_components = 0;\n  tmpl::for_each<prims_for_reconstruction>([&num_prims_tensor_components](\n                                               auto tag) {\n    num_prims_tensor_components += tmpl::type_from<decltype(tag)>::type::size();\n  });\n\n  using flux_variables =\n      typename grmhd::ValenciaDivClean::System::flux_variables;\n  const bool compute_cell_centered_flux =\n      db::get<\n          evolution::dg::subcell::Tags::CellCenteredFlux<flux_variables, 3>>(\n          *box)\n          .has_value();\n\n  for (const auto& direction : element.external_boundaries()) {\n    const auto& boundary_condition_at_direction =\n        *external_boundary_condition.at(direction);\n\n    const size_t num_face_pts{\n        subcell_mesh.extents().slice_away(direction.dimension()).product()};\n\n    // Allocate a vector to store the computed FD ghost data and assign a\n    // non-owning Variables on it.\n    using FluxVars =\n        Variables<db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                    tmpl::size_t<3>, Frame::Inertial>>;\n    const size_t prims_size =\n        num_prims_tensor_components * ghost_zone_size * num_face_pts;\n    const size_t fluxes_size =\n        (compute_cell_centered_flux ? FluxVars::number_of_independent_components\n         : 0) *\n        ghost_zone_size * num_face_pts;\n\n    auto& all_ghost_data = db::get_mutable_reference<\n        evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box);\n    // Put the computed ghost data into neighbor data with {direction,\n    // ElementId::external_boundary_id()} as the mortar_id key\n    const DirectionalId<3> mortar_id{direction,\n                                     ElementId<3>::external_boundary_id()};\n\n    all_ghost_data[mortar_id] = evolution::dg::subcell::GhostData{1};\n    DataVector& boundary_ghost_data =\n        all_ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    boundary_ghost_data.destructive_resize(prims_size + fluxes_size);\n    Variables<prims_for_reconstruction> ghost_data_vars{\n        boundary_ghost_data.data(), prims_size};\n    std::optional<FluxVars> cell_centered_ghost_fluxes{};\n    if (compute_cell_centered_flux) {\n      cell_centered_ghost_fluxes = FluxVars{};\n      cell_centered_ghost_fluxes.value().set_data_ref(\n          std::next(boundary_ghost_data.data(),\n                    static_cast<std::ptrdiff_t>(prims_size)),\n          fluxes_size);\n    }\n\n    // We don't need to care about boundary ghost data when using the periodic\n    // condition, so exclude it from the type list\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>::factory_creation::factory_classes;\n    using derived_boundary_conditions_for_subcell = tmpl::remove_if<\n        tmpl::at<factory_classes, typename System::boundary_conditions_base>,\n        tmpl::or_<\n            std::is_base_of<domain::BoundaryConditions::MarkAsPeriodic,\n                            tmpl::_1>,\n            std::is_base_of<domain::BoundaryConditions::MarkAsNone, tmpl::_1>>>;\n\n    // Now apply subcell boundary conditions\n    call_with_dynamic_type<void, derived_boundary_conditions_for_subcell>(\n        &boundary_condition_at_direction,\n        [&box, &cell_centered_ghost_fluxes, &direction,\n         &ghost_data_vars](const auto* boundary_condition) {\n          using BoundaryCondition = std::decay_t<decltype(*boundary_condition)>;\n          using bcondition_interior_evolved_vars_tags =\n              typename BoundaryCondition::fd_interior_evolved_variables_tags;\n          using bcondition_interior_temporary_tags =\n              typename BoundaryCondition::fd_interior_temporary_tags;\n          using bcondition_interior_primitive_vars_tags =\n              typename BoundaryCondition::fd_interior_primitive_variables_tags;\n          using bcondition_gridless_tags =\n              typename BoundaryCondition::fd_gridless_tags;\n\n          using bcondition_interior_tags =\n              tmpl::append<bcondition_interior_evolved_vars_tags,\n                           bcondition_interior_temporary_tags,\n                           bcondition_interior_primitive_vars_tags,\n                           bcondition_gridless_tags>;\n\n          if constexpr (BoundaryCondition::bc_type ==\n                        evolution::BoundaryConditions::Type::Ghost) {\n            const auto apply_fd_ghost =\n                [&boundary_condition, &cell_centered_ghost_fluxes, &direction,\n                 &ghost_data_vars](const auto&... boundary_ghost_data_args) {\n                  (*boundary_condition)\n                      .fd_ghost(\n                          make_not_null(&get<RestMassDensity>(ghost_data_vars)),\n                          make_not_null(\n                              &get<ElectronFraction>(ghost_data_vars)),\n                          make_not_null(&get<Temperature>(ghost_data_vars)),\n                          make_not_null(&get<LorentzFactorTimesSpatialVelocity>(\n                              ghost_data_vars)),\n                          make_not_null(&get<MagneticField>(ghost_data_vars)),\n                          make_not_null(\n                              &get<DivergenceCleaningField>(ghost_data_vars)),\n                          make_not_null(&cell_centered_ghost_fluxes), direction,\n                          boundary_ghost_data_args...);\n                };\n            apply_subcell_boundary_condition_impl(apply_fd_ghost, box,\n                                                  bcondition_interior_tags{});\n          } else if constexpr (BoundaryCondition::bc_type ==\n                               evolution::BoundaryConditions::Type::\n                                   DemandOutgoingCharSpeeds) {\n            // This boundary condition only checks if all the characteristic\n            // speeds are directed outward.\n            const auto& volume_mesh_velocity =\n                db::get<domain::Tags::MeshVelocity<3, Frame::Inertial>>(*box);\n            if (volume_mesh_velocity.has_value()) {\n              ERROR(\"Subcell currently does not support moving mesh\");\n            }\n\n            std::optional<tnsr::I<DataVector, 3>> face_mesh_velocity{};\n\n            const auto& normal_covector_and_magnitude =\n                db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<3>>(\n                    *box);\n            const auto outward_directed_normal_covector =\n                get<evolution::dg::Tags::NormalCovector<3>>(\n                    normal_covector_and_magnitude.at(direction).value());\n\n            const auto apply_fd_demand_outgoing_char_speeds =\n                [&boundary_condition, &cell_centered_ghost_fluxes, &direction,\n                 &face_mesh_velocity, &ghost_data_vars,\n                 &outward_directed_normal_covector](\n                    const auto&... boundary_ghost_data_args) {\n                  return (*boundary_condition)\n                      .fd_demand_outgoing_char_speeds(\n                          make_not_null(&get<RestMassDensity>(ghost_data_vars)),\n                          make_not_null(\n                              &get<ElectronFraction>(ghost_data_vars)),\n                          make_not_null(&get<Temperature>(ghost_data_vars)),\n                          make_not_null(&get<LorentzFactorTimesSpatialVelocity>(\n                              ghost_data_vars)),\n                          make_not_null(&get<MagneticField>(ghost_data_vars)),\n                          make_not_null(\n                              &get<DivergenceCleaningField>(ghost_data_vars)),\n                          make_not_null(&cell_centered_ghost_fluxes), direction,\n                          face_mesh_velocity, outward_directed_normal_covector,\n                          boundary_ghost_data_args...);\n                };\n            apply_subcell_boundary_condition_impl(\n                apply_fd_demand_outgoing_char_speeds, box,\n                bcondition_interior_tags{});\n\n            return;\n          } else {\n            ERROR(\"Unsupported boundary condition \"\n                  << pretty_type::short_name<BoundaryCondition>()\n                  << \" when using finite-difference\");\n          }\n        });\n    if (dynamic_cast<const BoundaryConditions::DirichletAnalytic*>(\n            &boundary_condition_at_direction) != nullptr and\n        reconstructor.reconstruct_rho_times_temperature()) {\n      // If we reconstruct rho*T we end up having to divide by rho to compute\n      // T. In some cases, like when evolving a TOV star with an analytic\n      // boundary condition, the boundary condition sets rho=0. While\n      // unphysical in general, this is how we have implemented the\n      // solutions. We deal with this by applying our atmosphere treatment to\n      // the reconstructed state.\n\n      const auto& atmosphere_fixer =\n          db::get<::Tags::VariableFixer<VariableFixing::FixToAtmosphere<3>>>(\n              *box);\n      const auto& equation_of_state =\n          db::get<hydro::Tags::GrmhdEquationOfState>(*box);\n\n      Variables<tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                           hydro::Tags::SpatialVelocity<DataVector, 3>,\n                           hydro::Tags::LorentzFactor<DataVector>,\n                           hydro::Tags::SpecificInternalEnergy<DataVector>,\n                           hydro::Tags::Pressure<DataVector>>>\n          temp_hydro_vars{ghost_data_vars.number_of_grid_points()};\n\n      tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric =\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(temp_hydro_vars);\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = i; j < 3; ++j) {\n          spatial_metric.get(i, j) = (i == j ? 1.0 : 0.0);\n        }\n      }\n\n      const auto& lorentz_factor_times_spatial_velocity =\n          get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n              ghost_data_vars);\n      auto& lorentz_factor =\n          get<hydro::Tags::LorentzFactor<DataVector>>(temp_hydro_vars);\n      get(lorentz_factor) = 0.0;\n      for (size_t i = 0; i < 3; ++i) {\n        get(lorentz_factor) +=\n            spatial_metric.get(i, i) *\n            square(lorentz_factor_times_spatial_velocity.get(i));\n        for (size_t j = i + 1; j < 3; ++j) {\n          get(lorentz_factor) += 2.0 * spatial_metric.get(i, j) *\n                                 lorentz_factor_times_spatial_velocity.get(i) *\n                                 lorentz_factor_times_spatial_velocity.get(j);\n        }\n      }\n      get(lorentz_factor) = sqrt(1.0 + get(lorentz_factor));\n      auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(temp_hydro_vars) =\n              lorentz_factor_times_spatial_velocity;\n      for (size_t i = 0; i < 3; ++i) {\n        spatial_velocity.get(i) /= get(lorentz_factor);\n      }\n\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(temp_hydro_vars) =\n          equation_of_state\n              .specific_internal_energy_from_density_and_temperature(\n                  get<RestMassDensity>(ghost_data_vars),\n                  get<Temperature>(ghost_data_vars),\n                  get<ElectronFraction>(ghost_data_vars));\n      get<hydro::Tags::Pressure<DataVector>>(temp_hydro_vars) =\n          equation_of_state.pressure_from_density_and_temperature(\n              get<RestMassDensity>(ghost_data_vars),\n              get<Temperature>(ghost_data_vars),\n              get<ElectronFraction>(ghost_data_vars));\n\n      atmosphere_fixer(\n          make_not_null(&get<RestMassDensity>(ghost_data_vars)),\n          make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n              temp_hydro_vars)),\n          make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n              temp_hydro_vars)),\n          make_not_null(\n              &get<hydro::Tags::LorentzFactor<DataVector>>(temp_hydro_vars)),\n          make_not_null(\n              &get<hydro::Tags::Pressure<DataVector>>(temp_hydro_vars)),\n          make_not_null(&get<Temperature>(ghost_data_vars)),\n          get<ElectronFraction>(ghost_data_vars), spatial_metric,\n          equation_of_state);\n    }\n  }\n}\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  MonotonicityPreserving5.cpp\n  MonotonisedCentral.cpp\n  PositivityPreservingAdaptiveOrder.cpp\n  Reconstructor.cpp\n  RegisterDerivedWithCharm.cpp\n  Wcns5z.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryConditionGhostData.hpp\n  Factory.hpp\n  FiniteDifference.hpp\n  MonotonicityPreserving5.hpp\n  MonotonisedCentral.hpp\n  PositivityPreservingAdaptiveOrder.hpp\n  Reconstructor.hpp\n  ReconstructWork.hpp\n  ReconstructWork.tpp\n  RegisterDerivedWithCharm.hpp\n  Tag.hpp\n  Wcns5z.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonicityPreserving5.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Wcns5z.hpp\"\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/FiniteDifference.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace grmhd::ValenciaDivClean {\n/// Finite difference functionality for the ValenciaDivClean form of the GRMHD\n/// equations\nnamespace fd {}\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonicityPreserving5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonicityPreserving5.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonicityPreserving5.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\n\nMonotonicityPreserving5Prim::MonotonicityPreserving5Prim(\n    const double alpha, const double epsilon,\n    const bool reconstruct_rho_times_temperature)\n    : alpha_(alpha),\n      epsilon_(epsilon),\n      reconstruct_rho_times_temperature_(reconstruct_rho_times_temperature) {}\n\nMonotonicityPreserving5Prim::MonotonicityPreserving5Prim(\n    CkMigrateMessage* const msg)\n    : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor> MonotonicityPreserving5Prim::get_clone() const {\n  return std::make_unique<MonotonicityPreserving5Prim>(*this);\n}\n\nvoid MonotonicityPreserving5Prim::pup(PUP::er& p) {\n  Reconstructor::pup(p);\n  p | alpha_;\n  p | epsilon_;\n  p | reconstruct_rho_times_temperature_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID MonotonicityPreserving5Prim::my_PUP_ID = 0;\n\ntemplate <size_t ThermodynamicDim>\nvoid MonotonicityPreserving5Prim::reconstruct(\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n        vars_on_upper_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh) const {\n  DirectionalIdMap<dim, Variables<prims_to_reconstruct_tags>>\n      neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_prims_work<prims_to_reconstruct_tags>(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_vars, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::monotonicity_preserving_5(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n            ghost_cell_vars, subcell_extents, number_of_variables, alpha_,\n            epsilon_);\n      },\n      volume_prims, eos, element, neighbor_variables_data, subcell_mesh,\n      ghost_zone_size(), true, reconstruct_rho_times_temperature());\n}\n\ntemplate <size_t ThermodynamicDim>\nvoid MonotonicityPreserving5Prim::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const Direction<dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work<prims_to_reconstruct_tags,\n                               prims_to_reconstruct_tags>(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<dim>& subcell_extents,\n             const Index<dim>& ghost_data_extents,\n             const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::MonotonicityPreserving5Reconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, alpha_, epsilon_);\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<dim>& subcell_extents,\n             const Index<dim>& ghost_data_extents,\n             const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::MonotonicityPreserving5Reconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, alpha_, epsilon_);\n      },\n      subcell_volume_prims, eos, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size(), true,\n      reconstruct_rho_times_temperature());\n}\n\nbool MonotonicityPreserving5Prim::reconstruct_rho_times_temperature() const {\n  return reconstruct_rho_times_temperature_;\n}\n\nbool operator==(const MonotonicityPreserving5Prim& lhs,\n                const MonotonicityPreserving5Prim& rhs) {\n  return lhs.alpha_ == rhs.alpha_ and lhs.epsilon_ == rhs.epsilon_ and\n         lhs.reconstruct_rho_times_temperature() ==\n             rhs.reconstruct_rho_times_temperature();\n}\n\nbool operator!=(const MonotonicityPreserving5Prim& lhs,\n                const MonotonicityPreserving5Prim& rhs) {\n  return not(lhs == rhs);\n}\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template void MonotonicityPreserving5Prim::reconstruct(                   \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>   \\\n          vars_on_lower_face,                                               \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>   \\\n          vars_on_upper_face,                                               \\\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,         \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos, \\\n      const Element<3>& element,                                            \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          ghost_data,                                                       \\\n      const Mesh<3>& subcell_mesh) const;                                   \\\n  template void MonotonicityPreserving5Prim::reconstruct_fd_neighbor(       \\\n      gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,    \\\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims, \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos, \\\n      const Element<3>& element,                                            \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          ghost_data,                                                       \\\n      const Mesh<3>& subcell_mesh,                                          \\\n      const Direction<3> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef THERMO_DIM\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonicityPreserving5.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::fd {\n/*!\n * \\brief Fifth order monotonicity-preserving (MP5) reconstruction. See\n * ::fd::reconstruction::monotonicity_preserving_5() for details.\n *\n */\nclass MonotonicityPreserving5Prim : public Reconstructor {\n private:\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n public:\n  static constexpr size_t dim = 3;\n\n  struct Alpha {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The parameter used in an intermediate reconstruction step to impose \"\n        \"monotonicity; typically Alpha=4.0 is used. Note that in principle the \"\n        \"CFL number must be not bigger than 1/(1+Alpha). See the original text \"\n        \"Suresh & Huynh (1997) for the details\"};\n  };\n  struct Epsilon {\n    using type = double;\n    static constexpr Options::String help = {\n        \"A small tolerance value by which limiting process is turned on and \"\n        \"off. Suresh & Huynh (1997) suggests 1e-10, but for hydro simulations \"\n        \"with atmosphere treatment setting Epsilon=0.0 would be safe.\"};\n  };\n  struct ReconstructRhoTimesTemperature {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If 'true' then we reconstruct the rho*T, if 'false' we reconstruct \"\n        \"T.\"};\n  };\n\n  using options = tmpl::list<Alpha, Epsilon, ReconstructRhoTimesTemperature>;\n  static constexpr Options::String help{\n      \"MP5 reconstruction scheme using primitive variables.\"};\n\n  MonotonicityPreserving5Prim() = default;\n  MonotonicityPreserving5Prim(MonotonicityPreserving5Prim&&) = default;\n  MonotonicityPreserving5Prim& operator=(MonotonicityPreserving5Prim&&) =\n      default;\n  MonotonicityPreserving5Prim(const MonotonicityPreserving5Prim&) = default;\n  MonotonicityPreserving5Prim& operator=(const MonotonicityPreserving5Prim&) =\n      default;\n  ~MonotonicityPreserving5Prim() override = default;\n\n  MonotonicityPreserving5Prim(double alpha, double epsilon,\n                              bool reconstruct_rho_times_temperature);\n\n  explicit MonotonicityPreserving5Prim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor,\n                                     MonotonicityPreserving5Prim);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  static constexpr bool use_adaptive_order = false;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 3; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                 hydro::Tags::GrmhdEquationOfState, domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>>;\n\n  template <size_t ThermodynamicDim>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n          vars_on_lower_face,\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n          vars_on_upper_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh) const;\n\n  template <size_t ThermodynamicDim>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      Direction<dim> direction_to_reconstruct) const;\n\n  bool reconstruct_rho_times_temperature() const override;\n\n private:\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const MonotonicityPreserving5Prim& lhs,\n                         const MonotonicityPreserving5Prim& rhs);\n  friend bool operator!=(const MonotonicityPreserving5Prim& lhs,\n                         const MonotonicityPreserving5Prim& rhs);\n\n  double alpha_ = std::numeric_limits<double>::signaling_NaN();\n  double epsilon_ = std::numeric_limits<double>::signaling_NaN();\n  bool reconstruct_rho_times_temperature_{false};\n};\n\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\nMonotonisedCentralPrim::MonotonisedCentralPrim(\n    const bool reconstruct_rho_times_temperature)\n    : reconstruct_rho_times_temperature_(reconstruct_rho_times_temperature) {}\n\nMonotonisedCentralPrim::MonotonisedCentralPrim(CkMigrateMessage* const msg)\n    : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor> MonotonisedCentralPrim::get_clone() const {\n  return std::make_unique<MonotonisedCentralPrim>(*this);\n}\n\nvoid MonotonisedCentralPrim::pup(PUP::er& p) {\n  Reconstructor::pup(p);\n  p | reconstruct_rho_times_temperature_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID MonotonisedCentralPrim::my_PUP_ID = 0;\n\ntemplate <size_t ThermodynamicDim>\nvoid MonotonisedCentralPrim::reconstruct(\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n        vars_on_upper_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh) const {\n  DirectionalIdMap<dim, Variables<prims_to_reconstruct_tags>>\n      neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n  reconstruct_prims_work<prims_to_reconstruct_tags>(\n      vars_on_lower_face, vars_on_upper_face,\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_vars, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::monotonised_central(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      volume_prims, eos, element, neighbor_variables_data, subcell_mesh,\n      ghost_zone_size(), true, reconstruct_rho_times_temperature());\n}\n\ntemplate <size_t ThermodynamicDim>\nvoid MonotonisedCentralPrim::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<dim>& element,\n    const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<dim>& subcell_mesh,\n    const Direction<dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work<prims_to_reconstruct_tags,\n                               prims_to_reconstruct_tags>(\n      vars_on_face,\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<dim>& subcell_extents,\n         const Index<dim>& ghost_data_extents,\n         const Direction<dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      subcell_volume_prims, eos, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size(), true,\n      reconstruct_rho_times_temperature());\n}\n\nbool MonotonisedCentralPrim::reconstruct_rho_times_temperature() const {\n  return reconstruct_rho_times_temperature_;\n}\n\nbool operator==(const MonotonisedCentralPrim& lhs,\n                const MonotonisedCentralPrim& rhs) {\n  return lhs.reconstruct_rho_times_temperature() ==\n         rhs.reconstruct_rho_times_temperature();\n}\n\nbool operator!=(const MonotonisedCentralPrim& lhs,\n                const MonotonisedCentralPrim& rhs) {\n  return not(lhs == rhs);\n}\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template void MonotonisedCentralPrim::reconstruct(                        \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>   \\\n          vars_on_lower_face,                                               \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>   \\\n          vars_on_upper_face,                                               \\\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,         \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos, \\\n      const Element<3>& element,                                            \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          ghost_data,                                                       \\\n      const Mesh<3>& subcell_mesh) const;                                   \\\n  template void MonotonisedCentralPrim::reconstruct_fd_neighbor(            \\\n      gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,    \\\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims, \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos, \\\n      const Element<3>& element,                                            \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          ghost_data,                                                       \\\n      const Mesh<3>& subcell_mesh,                                          \\\n      const Direction<3> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef THERMO_DIM\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::fd {\n/*!\n * \\brief Monotonised central reconstruction. See\n * ::fd::reconstruction::monotonised_central() for details.\n */\nclass MonotonisedCentralPrim : public Reconstructor {\n private:\n  // pressure -> temperature\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n public:\n  static constexpr size_t dim = 3;\n\n  struct ReconstructRhoTimesTemperature {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If 'true' then we reconstruct the rho*T, if 'false' we reconstruct \"\n        \"T.\"};\n  };\n\n  using options = tmpl::list<ReconstructRhoTimesTemperature>;\n  static constexpr Options::String help{\n      \"Monotonised central reconstruction scheme using primitive variables.\"};\n\n  MonotonisedCentralPrim() = default;\n  MonotonisedCentralPrim(MonotonisedCentralPrim&&) = default;\n  MonotonisedCentralPrim& operator=(MonotonisedCentralPrim&&) = default;\n  MonotonisedCentralPrim(const MonotonisedCentralPrim&) = default;\n  MonotonisedCentralPrim& operator=(const MonotonisedCentralPrim&) = default;\n  ~MonotonisedCentralPrim() override = default;\n\n  explicit MonotonisedCentralPrim(bool reconstruct_rho_times_temperature);\n\n  explicit MonotonisedCentralPrim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor, MonotonisedCentralPrim);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  static constexpr bool use_adaptive_order = false;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 2; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                 hydro::Tags::GrmhdEquationOfState, domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>>;\n\n  template <size_t ThermodynamicDim>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n          vars_on_lower_face,\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n          vars_on_upper_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh) const;\n\n  /// Called by an element doing DG when the neighbor is doing subcell.\n  template <size_t ThermodynamicDim>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      Direction<dim> direction_to_reconstruct) const;\n\n  bool reconstruct_rho_times_temperature() const override;\n\n private:\n  bool reconstruct_rho_times_temperature_{false};\n};\n\nbool operator==(const MonotonisedCentralPrim& lhs,\n                const MonotonisedCentralPrim& rhs);\n\nbool operator!=(const MonotonisedCentralPrim& lhs,\n                const MonotonisedCentralPrim& rhs);\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\n\nPositivityPreservingAdaptiveOrderPrim::PositivityPreservingAdaptiveOrderPrim(\n    const double alpha_5, const std::optional<double> alpha_7,\n    const std::optional<double> alpha_9,\n    const ::fd::reconstruction::FallbackReconstructorType\n        low_order_reconstructor,\n    const bool reconstruct_rho_times_temperature,\n    const Options::Context& context)\n    : four_to_the_alpha_5_(pow(4.0, alpha_5)),\n      low_order_reconstructor_(low_order_reconstructor),\n      reconstruct_rho_times_temperature_(reconstruct_rho_times_temperature) {\n  if (low_order_reconstructor_ ==\n      ::fd::reconstruction::FallbackReconstructorType::None) {\n    PARSE_ERROR(context, \"None is not an allowed low-order reconstructor.\");\n  }\n  if (alpha_7.has_value()) {\n    six_to_the_alpha_7_ = pow(6.0, alpha_7.value());\n  }\n  if (alpha_9.has_value()) {\n    eight_to_the_alpha_9_ = pow(8.0, alpha_9.value());\n  }\n  set_function_pointers();\n}\n\nPositivityPreservingAdaptiveOrderPrim::PositivityPreservingAdaptiveOrderPrim(\n    CkMigrateMessage* const msg)\n    : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor>\nPositivityPreservingAdaptiveOrderPrim::get_clone() const {\n  return std::make_unique<PositivityPreservingAdaptiveOrderPrim>(*this);\n}\n\nvoid PositivityPreservingAdaptiveOrderPrim::set_function_pointers() {\n  std::tie(reconstruct_, reconstruct_lower_neighbor_,\n           reconstruct_upper_neighbor_) = ::fd::reconstruction::\n      positivity_preserving_adaptive_order_function_pointers<3, false>(\n          false, eight_to_the_alpha_9_.has_value(),\n          six_to_the_alpha_7_.has_value(), low_order_reconstructor_);\n  std::tie(pp_reconstruct_, pp_reconstruct_lower_neighbor_,\n           pp_reconstruct_upper_neighbor_) = ::fd::reconstruction::\n      positivity_preserving_adaptive_order_function_pointers<3, true>(\n          true, eight_to_the_alpha_9_.has_value(),\n          six_to_the_alpha_7_.has_value(), low_order_reconstructor_);\n}\n\nvoid PositivityPreservingAdaptiveOrderPrim::pup(PUP::er& p) {\n  Reconstructor::pup(p);\n  p | four_to_the_alpha_5_;\n  p | six_to_the_alpha_7_;\n  p | eight_to_the_alpha_9_;\n  p | low_order_reconstructor_;\n  p | reconstruct_rho_times_temperature_;\n  if (p.isUnpacking()) {\n    set_function_pointers();\n  }\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID PositivityPreservingAdaptiveOrderPrim::my_PUP_ID = 0;\n\ntemplate <size_t ThermodynamicDim>\nvoid PositivityPreservingAdaptiveOrderPrim::reconstruct(\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_upper_face,\n    const gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, 3>>*>\n        reconstruction_order,\n    const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh) const {\n  DirectionalIdMap<dim, Variables<prims_to_reconstruct_tags>>\n      neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_prims_work<positivity_preserving_tags>(\n      vars_on_lower_face, vars_on_upper_face,\n      [this, &reconstruction_order](\n          auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n          const auto& volume_vars, const auto& ghost_cell_vars,\n          const auto& subcell_extents, const size_t number_of_variables) {\n        pp_reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr,\n                        reconstruction_order, volume_vars, ghost_cell_vars,\n                        subcell_extents, number_of_variables,\n                        four_to_the_alpha_5_,\n                        six_to_the_alpha_7_.value_or(\n                            std::numeric_limits<double>::signaling_NaN()),\n                        eight_to_the_alpha_9_.value_or(\n                            std::numeric_limits<double>::signaling_NaN()));\n      },\n      volume_prims, eos, element, neighbor_variables_data, subcell_mesh,\n      ghost_zone_size(), false, reconstruct_rho_times_temperature());\n  reconstruct_prims_work<non_positive_tags>(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_vars, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n                     ghost_cell_vars, subcell_extents, number_of_variables,\n                     four_to_the_alpha_5_,\n                     six_to_the_alpha_7_.value_or(\n                         std::numeric_limits<double>::signaling_NaN()),\n                     eight_to_the_alpha_9_.value_or(\n                         std::numeric_limits<double>::signaling_NaN()));\n      },\n      volume_prims, eos, element, neighbor_variables_data, subcell_mesh,\n      ghost_zone_size(), true, reconstruct_rho_times_temperature());\n}\n\ntemplate <size_t ThermodynamicDim>\nvoid PositivityPreservingAdaptiveOrderPrim::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh,\n    const Direction<3> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work<positivity_preserving_tags,\n                               prims_to_reconstruct_tags>(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<3>& subcell_extents,\n             const Index<3>& ghost_data_extents,\n             const Direction<3>& local_direction_to_reconstruct) {\n        pp_reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<3>& subcell_extents,\n             const Index<3>& ghost_data_extents,\n             const Direction<3>& local_direction_to_reconstruct) {\n        pp_reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      subcell_volume_prims, eos, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size(), false,\n      reconstruct_rho_times_temperature());\n  reconstruct_fd_neighbor_work<non_positive_tags, prims_to_reconstruct_tags>(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<3>& subcell_extents,\n             const Index<3>& ghost_data_extents,\n             const Direction<3>& local_direction_to_reconstruct) {\n        reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<3>& subcell_extents,\n             const Index<3>& ghost_data_extents,\n             const Direction<3>& local_direction_to_reconstruct) {\n        reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, four_to_the_alpha_5_,\n            six_to_the_alpha_7_.value_or(\n                std::numeric_limits<double>::signaling_NaN()),\n            eight_to_the_alpha_9_.value_or(\n                std::numeric_limits<double>::signaling_NaN()));\n      },\n      subcell_volume_prims, eos, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size(), true,\n      reconstruct_rho_times_temperature());\n}\n\nbool PositivityPreservingAdaptiveOrderPrim::reconstruct_rho_times_temperature()\n    const {\n  return reconstruct_rho_times_temperature_;\n}\n\nbool operator==(const PositivityPreservingAdaptiveOrderPrim& lhs,\n                const PositivityPreservingAdaptiveOrderPrim& rhs) {\n  // Don't check function pointers since they are set from\n  // low_order_reconstructor_\n  return lhs.four_to_the_alpha_5_ == rhs.four_to_the_alpha_5_ and\n         lhs.six_to_the_alpha_7_ == rhs.six_to_the_alpha_7_ and\n         lhs.eight_to_the_alpha_9_ == rhs.eight_to_the_alpha_9_ and\n         lhs.low_order_reconstructor_ == rhs.low_order_reconstructor_ and\n         lhs.reconstruct_rho_times_temperature() ==\n             rhs.reconstruct_rho_times_temperature();\n}\n\nbool operator!=(const PositivityPreservingAdaptiveOrderPrim& lhs,\n                const PositivityPreservingAdaptiveOrderPrim& rhs) {\n  return not(lhs == rhs);\n}\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template void PositivityPreservingAdaptiveOrderPrim::reconstruct(         \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>   \\\n          vars_on_lower_face,                                               \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>   \\\n          vars_on_upper_face,                                               \\\n      const gsl::not_null<                                                  \\\n          std::optional<std::array<gsl::span<std::uint8_t>, 3>>*>           \\\n          reconstruction_order,                                             \\\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,         \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos, \\\n      const Element<3>& element,                                            \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          ghost_data,                                                       \\\n      const Mesh<3>& subcell_mesh) const;                                   \\\n  template void                                                             \\\n  PositivityPreservingAdaptiveOrderPrim::reconstruct_fd_neighbor(           \\\n      gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,    \\\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims, \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos, \\\n      const Element<3>& element,                                            \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          ghost_data,                                                       \\\n      const Mesh<3>& subcell_mesh,                                          \\\n      const Direction<3> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef THERMO_DIM\n\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::fd {\n/*!\n * \\brief Positivity-preserving adaptive order reconstruction. See\n * ::fd::reconstruction::positivity_preserving_adaptive_order() for details.\n *\n * The rest mass density, electron fraction, and the pressure are kept positive.\n */\nclass PositivityPreservingAdaptiveOrderPrim : public Reconstructor {\n private:\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n  using positivity_preserving_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n  using non_positive_tags =\n      tmpl::list<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n  using FallbackReconstructorType =\n      ::fd::reconstruction::FallbackReconstructorType;\n\n public:\n  static constexpr size_t dim = 3;\n\n  struct Alpha5 {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations.\"};\n  };\n  struct Alpha7 {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations. If specified to None, \"\n        \"then 7th-order reconstruction is not used.\"};\n  };\n  struct Alpha9 {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"The alpha parameter in the Persson convergence measurement. 4 is the \"\n        \"right value, but anything in the range of 3-5 is 'reasonable'. \"\n        \"Smaller values allow for more oscillations. If specified to None, \"\n        \"then 9th-order reconstruction is not used.\"};\n  };\n  struct LowOrderReconstructor {\n    using type = FallbackReconstructorType;\n    static constexpr Options::String help = {\n        \"The 2nd/3rd-order reconstruction scheme to use if unlimited 5th-order \"\n        \"isn't okay.\"};\n  };\n  struct ReconstructRhoTimesTemperature {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If 'true' then we reconstruct the rho*T, if 'false' we reconstruct \"\n        \"T.\"};\n  };\n\n  using options = tmpl::list<Alpha5, Alpha7, Alpha9, LowOrderReconstructor,\n                             ReconstructRhoTimesTemperature>;\n\n  static constexpr Options::String help{\n      \"Positivity-preserving adaptive-order reconstruction.\"};\n\n  PositivityPreservingAdaptiveOrderPrim() = default;\n  PositivityPreservingAdaptiveOrderPrim(\n      PositivityPreservingAdaptiveOrderPrim&&) = default;\n  PositivityPreservingAdaptiveOrderPrim& operator=(\n      PositivityPreservingAdaptiveOrderPrim&&) = default;\n  PositivityPreservingAdaptiveOrderPrim(\n      const PositivityPreservingAdaptiveOrderPrim&) = default;\n  PositivityPreservingAdaptiveOrderPrim& operator=(\n      const PositivityPreservingAdaptiveOrderPrim&) = default;\n  ~PositivityPreservingAdaptiveOrderPrim() override = default;\n\n  PositivityPreservingAdaptiveOrderPrim(\n      double alpha_5, std::optional<double> alpha_7,\n      std::optional<double> alpha_9,\n      FallbackReconstructorType low_order_reconstructor,\n      bool reconstruct_rho_times_temperature,\n      const Options::Context& context = {});\n\n  explicit PositivityPreservingAdaptiveOrderPrim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor,\n                                     PositivityPreservingAdaptiveOrderPrim);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  static constexpr bool use_adaptive_order = true;\n  bool supports_adaptive_order() const override { return use_adaptive_order; }\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override {\n    return eight_to_the_alpha_9_.has_value()\n               ? 5\n               : (six_to_the_alpha_7_.has_value() ? 4 : 3);\n  }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                 hydro::Tags::GrmhdEquationOfState, domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>>;\n\n  template <size_t ThermodynamicDim>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n          vars_on_lower_face,\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n          vars_on_upper_face,\n      gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, dim>>*>\n          reconstruction_order,\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh) const;\n\n  template <size_t ThermodynamicDim>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      Direction<dim> direction_to_reconstruct) const;\n\n  bool reconstruct_rho_times_temperature() const override;\n\n private:\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const PositivityPreservingAdaptiveOrderPrim& lhs,\n                         const PositivityPreservingAdaptiveOrderPrim& rhs);\n  friend bool operator!=(const PositivityPreservingAdaptiveOrderPrim& lhs,\n                         const PositivityPreservingAdaptiveOrderPrim& rhs);\n  void set_function_pointers();\n\n  double four_to_the_alpha_5_ = std::numeric_limits<double>::signaling_NaN();\n  std::optional<double> six_to_the_alpha_7_{};\n  std::optional<double> eight_to_the_alpha_9_{};\n  FallbackReconstructorType low_order_reconstructor_ =\n      FallbackReconstructorType::None;\n  bool reconstruct_rho_times_temperature_{false};\n\n  using PointerReconsOrder = void (*)(\n      gsl::not_null<std::array<gsl::span<double>, dim>*>,\n      gsl::not_null<std::array<gsl::span<double>, dim>*>,\n      gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, dim>>*>,\n      const gsl::span<const double>&,\n      const DirectionMap<dim, gsl::span<const double>>&, const Index<dim>&,\n      size_t, double, double, double);\n  using PointerRecons =\n      void (*)(gsl::not_null<std::array<gsl::span<double>, dim>*>,\n               gsl::not_null<std::array<gsl::span<double>, dim>*>,\n               const gsl::span<const double>&,\n               const DirectionMap<dim, gsl::span<const double>>&,\n               const Index<dim>&, size_t, double, double, double);\n  PointerRecons reconstruct_ = nullptr;\n  PointerReconsOrder pp_reconstruct_ = nullptr;\n\n  using PointerNeighbor = void (*)(gsl::not_null<DataVector*>,\n                                   const DataVector&, const DataVector&,\n                                   const Index<dim>&, const Index<dim>&,\n                                   const Direction<dim>&, const double&,\n                                   const double&, const double&);\n  PointerNeighbor reconstruct_lower_neighbor_ = nullptr;\n  PointerNeighbor reconstruct_upper_neighbor_ = nullptr;\n  PointerNeighbor pp_reconstruct_lower_neighbor_ = nullptr;\n  PointerNeighbor pp_reconstruct_upper_neighbor_ = nullptr;\n};\n\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\nnamespace evolution::dg::Actions::detail {\ntemplate <size_t Dim>\nstruct NormalVector;\n}  // namespace evolution::dg::Actions::detail\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::fd {\n/// \\brief The list of tags used by `reconstruct_prims_work` and\n/// `reconstruct_fd_neighbor_work`\nusing tags_list_for_reconstruct = tmpl::list<\n    grmhd::ValenciaDivClean::Tags::TildeD,\n    grmhd::ValenciaDivClean::Tags::TildeYe,\n    grmhd::ValenciaDivClean::Tags::TildeTau,\n    grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>,\n    grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>,\n    grmhd::ValenciaDivClean::Tags::TildePhi,\n    hydro::Tags::RestMassDensity<DataVector>,\n    hydro::Tags::ElectronFraction<DataVector>,\n    hydro::Tags::SpecificInternalEnergy<DataVector>,\n    hydro::Tags::SpatialVelocity<DataVector, 3>,\n    hydro::Tags::MagneticField<DataVector, 3>,\n    hydro::Tags::DivergenceCleaningField<DataVector>,\n    hydro::Tags::LorentzFactor<DataVector>, hydro::Tags::Pressure<DataVector>,\n    hydro::Tags::Temperature<DataVector>,\n    hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n    ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD, tmpl::size_t<3>,\n                 Frame::Inertial>,\n    ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe, tmpl::size_t<3>,\n                 Frame::Inertial>,\n    ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau, tmpl::size_t<3>,\n                 Frame::Inertial>,\n    ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>,\n                 tmpl::size_t<3>, Frame::Inertial>,\n    ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>,\n                 tmpl::size_t<3>, Frame::Inertial>,\n    ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi, tmpl::size_t<3>,\n                 Frame::Inertial>,\n    gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n    hydro::Tags::SpatialVelocityOneForm<DataVector, 3, Frame::Inertial>,\n    gr::Tags::SpatialMetric<DataVector, 3>,\n    gr::Tags::SqrtDetSpatialMetric<DataVector>,\n    gr::Tags::InverseSpatialMetric<DataVector, 3>,\n    evolution::dg::Actions::detail::NormalVector<3>>;\n\n/*!\n * \\brief Reconstructs the `PrimTagsForReconstruction` (usually\n * \\f$\\rho, p, Wv^i, B^i\\f$, and \\f$\\Phi\\f$), and if `compute_conservatives` is\n * true computes the Lorentz factor, upper spatial velocity, specific internal\n * energy, and the conserved variables.\n *\n * All results are written into `vars_on_lower_face` and `vars_on_upper_face`.\n *\n * The reason the `PrimTagsForReconstruction` can be specified separately is\n * because some variables might need separate reconstruction methods from\n * others, e.g. to guarantee the reconstructed solution is positive.\n */\ntemplate <typename PrimTagsForReconstruction, typename PrimsTagsVolume,\n          size_t ThermodynamicDim, typename F, typename PrimsTagsSentByNeighbor>\nvoid reconstruct_prims_work(\n    gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_lower_face,\n    gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_upper_face,\n    const F& reconstruct, const Variables<PrimsTagsVolume>& volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, Variables<PrimsTagsSentByNeighbor>>&\n        neighbor_data,\n    const Mesh<3>& subcell_mesh, size_t ghost_zone_size,\n    bool compute_conservatives, bool reconstruct_density_times_temperature);\n\n/*!\n * \\brief Reconstructs the `PrimTagsForReconstruction` and if\n * `compute_conservatives` is `true`  computes the Lorentz factor, upper spatial\n * velocity, specific internal energy, and the conserved variables.\n *\n * All results are written into `vars_on_face`.\n *\n * This is used on DG elements to reconstruct their subcell neighbors' solution\n * on the shared faces.\n *\n * The reason the `PrimTagsForReconstruction` can be specified separately is\n * because some variables might need separate reconstruction methods from\n * others, e.g. to guarantee the reconstructed solution is positiv\n */\ntemplate <typename PrimTagsForReconstruction, typename PrimsTagsSentByNeighbor,\n          typename TagsList, typename PrimsTags, size_t ThermodynamicDim,\n          typename F0, typename F1>\nvoid reconstruct_fd_neighbor_work(\n    gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n    const F0& reconstruct_lower_neighbor, const F1& reconstruct_upper_neighbor,\n    const Variables<PrimsTags>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh, const Direction<3>& direction_to_reconstruct,\n    size_t ghost_zone_size, bool compute_conservatives,\n    bool reconstruct_density_times_temperature);\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\ntemplate <typename TagsList, size_t ThermodynamicDim>\nvoid compute_conservatives_for_reconstruction(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const VariableFixing::FixToAtmosphere<3>* const fix_to_atmosphere) {\n  // Computes:\n  // 1. W v^i\n  // 2. Lorentz factor as sqrt(1 + Wv^i Wv^j\\gamma_{ij})\n  // 3. v^i = Wv^i / W\n  // 4. specific internal energy\n  // 5. conserved variables\n  // - note: spatial metric, inv spatial metric, lapse, and shift are\n  //         all already in vars_on_face\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face);\n  const auto& lorentz_factor_times_spatial_velocity =\n      get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n          *vars_on_face);\n  auto& lorentz_factor =\n      get<hydro::Tags::LorentzFactor<DataVector>>(*vars_on_face);\n  get(lorentz_factor) = 0.0;\n  for (size_t i = 0; i < 3; ++i) {\n    get(lorentz_factor) += spatial_metric.get(i, i) *\n                           square(lorentz_factor_times_spatial_velocity.get(i));\n    for (size_t j = i + 1; j < 3; ++j) {\n      get(lorentz_factor) += 2.0 * spatial_metric.get(i, j) *\n                             lorentz_factor_times_spatial_velocity.get(i) *\n                             lorentz_factor_times_spatial_velocity.get(j);\n    }\n  }\n  get(lorentz_factor) = sqrt(1.0 + get(lorentz_factor));\n  auto& spatial_velocity =\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(*vars_on_face) =\n          lorentz_factor_times_spatial_velocity;\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_velocity.get(i) /= get(lorentz_factor);\n  }\n\n  if (fix_to_atmosphere != nullptr) {\n    fix_to_atmosphere->operator()(\n        get<hydro::Tags::RestMassDensity<DataVector>>(vars_on_face),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(vars_on_face),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(vars_on_face),\n        get<hydro::Tags::LorentzFactor<DataVector>>(vars_on_face),\n        get<hydro::Tags::Pressure<DataVector>>(vars_on_face),\n        get<hydro::Tags::Temperature<DataVector>>(vars_on_face),\n\n        get<hydro::Tags::ElectronFraction<DataVector>>(*vars_on_face),\n        spatial_metric, eos);\n  }\n\n  // pointers to primitive variables\n  auto& spatial_velocity_one_form =\n      get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3, Frame::Inertial>>(\n          *vars_on_face);\n  raise_or_lower_index(make_not_null(&spatial_velocity_one_form),\n                       spatial_velocity, spatial_metric);\n  const auto& rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(*vars_on_face);\n  const auto& electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataVector>>(*vars_on_face);\n  auto& pressure = get<hydro::Tags::Pressure<DataVector>>(*vars_on_face);\n  auto& specific_internal_energy =\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(*vars_on_face);\n  const auto& temperature =\n      get<hydro::Tags::Temperature<DataVector>>(*vars_on_face);\n\n  // EoS calls based on reconstructed primitives\n  if constexpr (ThermodynamicDim == 1) {\n    specific_internal_energy =\n        eos.specific_internal_energy_from_density(\n            rest_mass_density);\n    pressure = eos.pressure_from_density(rest_mass_density);\n  } else if constexpr (ThermodynamicDim == 2) {\n    specific_internal_energy =\n        eos.specific_internal_energy_from_density_and_temperature(\n            rest_mass_density, temperature);\n    pressure = eos.pressure_from_density_and_energy(rest_mass_density,\n                                                    specific_internal_energy);\n  } else if constexpr (ThermodynamicDim == 3) {\n    specific_internal_energy =\n        eos.specific_internal_energy_from_density_and_temperature(\n            rest_mass_density, temperature, electron_fraction);\n    pressure = eos.pressure_from_density_and_temperature(\n        rest_mass_density, temperature, electron_fraction);\n  } else {\n    ERROR(\"EOS Must be 1, 2, or 3d\");\n  }\n\n  ConservativeFromPrimitive::apply(\n      make_not_null(&get<ValenciaDivClean::Tags::TildeD>(*vars_on_face)),\n      make_not_null(&get<ValenciaDivClean::Tags::TildeYe>(*vars_on_face)),\n      make_not_null(&get<ValenciaDivClean::Tags::TildeTau>(*vars_on_face)),\n      make_not_null(\n          &get<ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(*vars_on_face)),\n      make_not_null(\n          &get<ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(*vars_on_face)),\n      make_not_null(&get<ValenciaDivClean::Tags::TildePhi>(*vars_on_face)),\n      rest_mass_density, electron_fraction, specific_internal_energy, pressure,\n      spatial_velocity, lorentz_factor,\n      get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(\n          *vars_on_face),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(*vars_on_face),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(*vars_on_face),\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(*vars_on_face));\n}\n\ntemplate <typename PrimTagsForReconstruction, typename PrimsTags,\n          size_t ThermodynamicDim, typename F, typename PrimsTagsSentByNeighbor>\nvoid reconstruct_prims_work(\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_upper_face,\n    const F& reconstruct, const Variables<PrimsTags>& volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, Variables<PrimsTagsSentByNeighbor>>&\n        neighbor_data,\n    const Mesh<3>& subcell_mesh, const size_t ghost_zone_size,\n    const bool compute_conservatives,\n    const bool reconstruct_density_times_temperature) {\n  ASSERT(Mesh<3>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                 subcell_mesh.quadrature(0)) == subcell_mesh,\n         \"The subcell mesh should be isotropic but got \" << subcell_mesh);\n  const size_t volume_num_pts = subcell_mesh.number_of_grid_points();\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n  const size_t neighbor_num_pts =\n      ghost_zone_size * subcell_mesh.extents().slice_away(0).product();\n  const size_t number_of_pts_for_thermodynamic_var =\n      6 * neighbor_num_pts + volume_num_pts;\n  DataVector buffer_for_recons_vars{\n      std::max(number_of_pts_for_thermodynamic_var, 3 * volume_num_pts)};\n  tmpl::for_each<\n      PrimTagsForReconstruction>([&buffer_for_recons_vars, &element,\n                                  &neighbor_data, neighbor_num_pts,\n                                  &reconstruct,\n                                  reconstruct_density_times_temperature,\n                                  reconstructed_num_pts, volume_num_pts,\n                                  &volume_prims, &vars_on_lower_face,\n                                  &vars_on_upper_face,\n                                  &subcell_mesh](auto tag_v) {\n    (void)reconstruct_density_times_temperature;\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const typename tag::type* volume_tensor_ptr = nullptr;\n    Variables<tmpl::list<\n        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>>\n        lorentz_factor_times_v_I{};\n    Scalar<DataVector> thermo_volume_var{};\n    if constexpr (std::is_same_v<tag,\n                                 hydro::Tags::LorentzFactorTimesSpatialVelocity<\n                                     DataVector, 3>>) {\n      // we need to handle the Wv^i reconstruction separately since we need to\n      // first compute Wv^i in the volume (it's not one of our primitives from\n      // the recovery). The components need to be stored contiguously, which is\n      // why we have the Variables `lorentz_factor_times_v_I`\n      const auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(volume_prims);\n      const auto& lorentz_factor =\n          get<hydro::Tags::LorentzFactor<DataVector>>(volume_prims);\n      lorentz_factor_times_v_I.set_data_ref(buffer_for_recons_vars.data(),\n                                            3 * volume_num_pts);\n      auto& volume_tensor =\n          get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n              lorentz_factor_times_v_I) = spatial_velocity;\n      for (size_t i = 0; i < 3; ++i) {\n        volume_tensor.get(i) *= get(lorentz_factor);\n      }\n      volume_tensor_ptr = &volume_tensor;\n    } else if constexpr (std::is_same_v<tag,\n                                        hydro::Tags::Temperature<DataVector>>) {\n      if (reconstruct_density_times_temperature) {\n        get(thermo_volume_var)\n            .set_data_ref(buffer_for_recons_vars.data(), volume_num_pts);\n        get(thermo_volume_var) =\n            get(get<tag>(volume_prims)) *\n            get(get<hydro::Tags::RestMassDensity<DataVector>>(volume_prims));\n        volume_tensor_ptr = &thermo_volume_var;\n      } else {\n        volume_tensor_ptr = &get<tag>(volume_prims);\n      }\n    } else {\n      volume_tensor_ptr = &get<tag>(volume_prims);\n    }\n\n    const size_t number_of_variables = volume_tensor_ptr->size();\n    const gsl::span<const double> volume_vars = gsl::make_span(\n        (*volume_tensor_ptr)[0].data(), number_of_variables * volume_num_pts);\n    std::array<gsl::span<double>, 3> upper_face_vars{};\n    std::array<gsl::span<double>, 3> lower_face_vars{};\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(upper_face_vars, i) =\n          gsl::make_span(get<tag>(gsl::at(*vars_on_upper_face, i))[0].data(),\n                         number_of_variables * reconstructed_num_pts);\n      gsl::at(lower_face_vars, i) =\n          gsl::make_span(get<tag>(gsl::at(*vars_on_lower_face, i))[0].data(),\n                         number_of_variables * reconstructed_num_pts);\n    }\n\n    DirectionMap<3, gsl::span<const double>> ghost_cell_vars{};\n\n    for (const auto& direction : Direction<3>::all_directions()) {\n      DirectionalId<3> id{};\n      if (element.neighbors().contains(direction)) {\n        const auto& neighbors_in_direction = element.neighbors().at(direction);\n        ASSERT(neighbors_in_direction.size() == 1,\n               \"Currently only support one neighbor in each direction, but \"\n               \"got \"\n                   << neighbors_in_direction.size() << \" in direction \"\n                   << direction);\n        id = DirectionalId<3>{direction, *neighbors_in_direction.begin()};\n      } else {\n        // retrieve boundary ghost data from neighbor_data\n        ASSERT(\n            element.external_boundaries().count(direction) == 1,\n            \"Element has neither neighbor nor external boundary to direction: \"\n                << direction);\n        id = DirectionalId<3>{direction, ElementId<3>::external_boundary_id()};\n      }\n      if constexpr (std::is_same_v<tag, hydro::Tags::Temperature<DataVector>>) {\n        ASSERT(number_of_variables == 1,\n               \"Should only have one tensor component for a Scalar\");\n        if (reconstruct_density_times_temperature) {\n          DataVector view{\n              &buffer_for_recons_vars[volume_num_pts +\n                                      (2 * direction.dimension() +\n                                       (direction.side() == Side::Upper ? 1\n                                                                        : 0)) *\n                                          neighbor_num_pts],\n              number_of_variables * neighbor_num_pts};\n          const auto& data_in_dir = neighbor_data.at(id);\n          view =\n              get(get<hydro::Tags::RestMassDensity<DataVector>>(data_in_dir)) *\n              get(get<tag>(data_in_dir));\n          ghost_cell_vars[direction] = gsl::make_span(view.data(), view.size());\n          continue;\n        }\n      }\n      ghost_cell_vars[direction] =\n          gsl::make_span(get<tag>(neighbor_data.at(id))[0].data(),\n                         number_of_variables * neighbor_num_pts);\n    }\n\n    reconstruct(make_not_null(&upper_face_vars),\n                make_not_null(&lower_face_vars), volume_vars, ghost_cell_vars,\n                subcell_mesh.extents(), number_of_variables);\n    if constexpr (std::is_same_v<tag, hydro::Tags::Temperature<DataVector>>) {\n      if (reconstruct_density_times_temperature) {\n        for (size_t i = 0; i < 3; ++i) {\n          get(get<tag>(gsl::at(*vars_on_upper_face, i))) /=\n              get(get<hydro::Tags::RestMassDensity<DataVector>>(\n                  gsl::at(*vars_on_upper_face, i)));\n          get(get<tag>(gsl::at(*vars_on_lower_face, i))) /=\n              get(get<hydro::Tags::RestMassDensity<DataVector>>(\n                  gsl::at(*vars_on_lower_face, i)));\n        }\n      }\n    }\n  });\n\n  for (size_t i = 0; compute_conservatives and i < 3; ++i) {\n    compute_conservatives_for_reconstruction(\n        make_not_null(&gsl::at(*vars_on_lower_face, i)), eos, nullptr);\n    compute_conservatives_for_reconstruction(\n        make_not_null(&gsl::at(*vars_on_upper_face, i)), eos, nullptr);\n  }\n}\n\ntemplate <typename PrimTagsForReconstruction, typename PrimsTagsSentByNeighbor,\n          typename PrimsTags, size_t ThermodynamicDim, typename F0, typename F1>\nvoid reconstruct_fd_neighbor_work(\n    const gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n    const F0& reconstruct_lower_neighbor, const F1& reconstruct_upper_neighbor,\n    const Variables<PrimsTags>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh, const Direction<3>& direction_to_reconstruct,\n    const size_t ghost_zone_size, const bool compute_conservatives,\n    const bool reconstruct_density_times_temperature) {\n  const DirectionalId<3> mortar_id{\n      direction_to_reconstruct,\n      *element.neighbors().at(direction_to_reconstruct).begin()};\n  Index<3> ghost_data_extents = subcell_mesh.extents();\n  ghost_data_extents[direction_to_reconstruct.dimension()] = ghost_zone_size;\n  Variables<PrimsTagsSentByNeighbor> neighbor_prims{};\n  {\n    ASSERT(ghost_data.contains(mortar_id),\n           \"The neighbor data does not contain the mortar: \" << mortar_id);\n    const DataVector& neighbor_data_on_mortar =\n        ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    neighbor_prims.set_data_ref(\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        const_cast<double*>(neighbor_data_on_mortar.data()),\n        neighbor_prims.number_of_independent_components *\n            ghost_data_extents.product());\n  }\n\n  DataVector buffer{3 * subcell_volume_prims.number_of_grid_points() +\n                    ghost_data_extents.product()};\n  Scalar<DataVector> rho_times_temperature_neighbor{};\n  tmpl::for_each<PrimTagsForReconstruction>(\n      [&buffer, &direction_to_reconstruct, &ghost_data_extents, &neighbor_prims,\n       reconstruct_density_times_temperature, &reconstruct_lower_neighbor,\n       &reconstruct_upper_neighbor, &rho_times_temperature_neighbor,\n       &subcell_mesh, &subcell_volume_prims, &vars_on_face](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const typename tag::type* volume_tensor_ptr = nullptr;\n        typename tag::type volume_tensor{};\n        if constexpr (std::is_same_v<\n                          tag, hydro::Tags::LorentzFactorTimesSpatialVelocity<\n                                   DataVector, 3>>) {\n          // we need to handle the Wv^i reconstruction separately since we need\n          // to first compute Wv^i in the volume (it's not one of our primitives\n          // from the recovery). The components need to be stored contiguously,\n          // which is why we have the Variables `lorentz_factor_times_v_I`\n          const auto& spatial_velocity =\n              get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n                  subcell_volume_prims);\n          const auto& lorentz_factor =\n              get<hydro::Tags::LorentzFactor<DataVector>>(subcell_volume_prims);\n          for (size_t i = 0; i < 3; ++i) {\n            volume_tensor.get(i).set_data_ref(\n                &buffer[i * subcell_volume_prims.number_of_grid_points()],\n                subcell_volume_prims.number_of_grid_points());\n          }\n          volume_tensor = spatial_velocity;\n          for (size_t i = 0; i < 3; ++i) {\n            volume_tensor.get(i) *= get(lorentz_factor);\n          }\n          volume_tensor_ptr = &volume_tensor;\n        } else {\n          if constexpr (std::is_same_v<tag,\n                                       hydro::Tags::Temperature<DataVector>>) {\n            if (reconstruct_density_times_temperature) {\n              get(volume_tensor)\n                  .set_data_ref(buffer.data(),\n                                subcell_volume_prims.number_of_grid_points());\n              get(volume_tensor) =\n                  get(get<hydro::Tags::RestMassDensity<DataVector>>(\n                      subcell_volume_prims)) *\n                  get(get<tag>(subcell_volume_prims));\n              volume_tensor_ptr = &volume_tensor;\n            } else {\n              volume_tensor_ptr = &get<tag>(subcell_volume_prims);\n            }\n          } else {\n            volume_tensor_ptr = &get<tag>(subcell_volume_prims);\n          }\n        }\n\n        const auto& tensor_neighbor =\n            [&buffer, &neighbor_prims, reconstruct_density_times_temperature,\n             &rho_times_temperature_neighbor,\n             &subcell_volume_prims]() -> typename tag::type& {\n          if constexpr (std::is_same_v<tag,\n                                       hydro::Tags::Temperature<DataVector>>) {\n            if (reconstruct_density_times_temperature) {\n              get(rho_times_temperature_neighbor)\n                  .set_data_ref(\n                      &buffer[subcell_volume_prims.number_of_grid_points()],\n                      get(get<tag>(neighbor_prims)).size());\n              get(rho_times_temperature_neighbor) =\n                  get(get<hydro::Tags::RestMassDensity<DataVector>>(\n                      neighbor_prims)) *\n                  get(get<tag>(neighbor_prims));\n              return rho_times_temperature_neighbor;\n            } else {\n              (void)buffer, (void)reconstruct_density_times_temperature;\n              (void)rho_times_temperature_neighbor, (void)subcell_volume_prims;\n              return get<tag>(neighbor_prims);\n            }\n          } else {\n            (void)buffer, (void)reconstruct_density_times_temperature;\n            (void)rho_times_temperature_neighbor, (void)subcell_volume_prims;\n            return get<tag>(neighbor_prims);\n          }\n        }();\n        auto& tensor_on_face = get<tag>(*vars_on_face);\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_upper_neighbor(\n                make_not_null(&tensor_on_face[tensor_index]),\n                (*volume_tensor_ptr)[tensor_index],\n                tensor_neighbor[tensor_index], subcell_mesh.extents(),\n                ghost_data_extents, direction_to_reconstruct);\n          }\n        } else {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_lower_neighbor(\n                make_not_null(&tensor_on_face[tensor_index]),\n                (*volume_tensor_ptr)[tensor_index],\n                tensor_neighbor[tensor_index], subcell_mesh.extents(),\n                ghost_data_extents, direction_to_reconstruct);\n          }\n        }\n        if constexpr (std::is_same_v<tag,\n                                     hydro::Tags::Temperature<DataVector>>) {\n          if (reconstruct_density_times_temperature) {\n            get(tensor_on_face) /= get(\n                get<hydro::Tags::RestMassDensity<DataVector>>(*vars_on_face));\n          }\n        }\n      });\n\n  if (compute_conservatives) {\n    compute_conservatives_for_reconstruction(vars_on_face, eos, nullptr);\n  }\n}\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n\n#include <pup.h>\n\nnamespace grmhd::ValenciaDivClean::fd {\nReconstructor::Reconstructor(CkMigrateMessage* const msg) : PUP::able(msg) {}\n\nvoid Reconstructor::pup(PUP::er& p) { PUP::able::pup(p); }\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\n/// \\cond\nclass MonotonicityPreserving5Prim;\nclass MonotonisedCentralPrim;\nclass PositivityPreservingAdaptiveOrderPrim;\nclass Wcns5zPrim;\n/// \\endcond\n\n/*!\n * \\brief The base class from which all reconstruction schemes must inherit\n */\nclass Reconstructor : public PUP::able {\n public:\n  Reconstructor() = default;\n  Reconstructor(const Reconstructor&) = default;\n  Reconstructor& operator=(const Reconstructor&) = default;\n  Reconstructor(Reconstructor&&) = default;\n  Reconstructor& operator=(Reconstructor&&) = default;\n  ~Reconstructor() override = default;\n\n  /// \\cond\n  explicit Reconstructor(CkMigrateMessage* msg);\n  WRAPPED_PUPable_abstract(Reconstructor);  // NOLINT\n  /// \\endcond\n\n  using creatable_classes =\n      tmpl::list<MonotonicityPreserving5Prim, MonotonisedCentralPrim,\n                 PositivityPreservingAdaptiveOrderPrim, Wcns5zPrim>;\n\n  virtual std::unique_ptr<Reconstructor> get_clone() const = 0;\n\n  virtual size_t ghost_zone_size() const = 0;\n\n  virtual bool supports_adaptive_order() const { return false; }\n\n  virtual bool reconstruct_rho_times_temperature() const = 0;\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\nvoid register_derived_with_charm() {\n  register_classes_with_charm(typename Reconstructor::creatable_classes{});\n}\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace grmhd::ValenciaDivClean::fd {\nvoid register_derived_with_charm();\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellSolver.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\n/// Option tags for reconstruction\nnamespace OptionTags {\n/// \\brief Option tag for the reconstructor\nstruct Reconstructor {\n  using type = std::unique_ptr<fd::Reconstructor>;\n\n  static constexpr Options::String help = {\"The reconstruction scheme to use.\"};\n  using group = evolution::dg::subcell::OptionTags::SubcellSolverGroup;\n};\n}  // namespace OptionTags\n\n/// %Tags for reconstruction\nnamespace Tags {\n/// \\brief Tag for the reconstructor\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<fd::Reconstructor>;\n  using option_tags =\n      tmpl::list<OptionTags::Reconstructor,\n                 ::evolution::dg::subcell::OptionTags::SubcellOptions>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const type& reconstructor,\n      const ::evolution::dg::subcell::SubcellOptions& subcell_options) {\n    if (static_cast<int>(subcell_options.finite_difference_derivative_order()) <\n            0 and\n        not reconstructor->supports_adaptive_order()) {\n      ERROR_NO_TRACE(\n          \"Cannot use adaptive finite difference derivative order with \"\n          \"specified reconstructor.\");\n    }\n    if ((static_cast<int>(\n             subcell_options.finite_difference_derivative_order()) /\n             2 -\n         1) > static_cast<int>(reconstructor->ghost_zone_size())) {\n      ERROR_NO_TRACE(\n          \"The derivative order chosen (\"\n          << subcell_options.finite_difference_derivative_order()\n          << \") requires more ghost zones (\"\n          << (static_cast<int>(\n                  subcell_options.finite_difference_derivative_order()) /\n                  2 -\n              1)\n          << \") than the reconstructor sends, \"\n          << reconstructor->ghost_zone_size());\n    }\n    return reconstructor->get_clone();\n  }\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Wcns5z.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Wcns5z.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Wcns5z.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::fd {\n\nWcns5zPrim::Wcns5zPrim(const size_t nonlinear_weight_exponent,\n                       const double epsilon,\n                       const ::fd::reconstruction::FallbackReconstructorType\n                           fallback_reconstructor,\n                       const size_t max_number_of_extrema,\n                       const bool reconstruct_rho_times_temperature)\n    : nonlinear_weight_exponent_(nonlinear_weight_exponent),\n      epsilon_(epsilon),\n      fallback_reconstructor_(fallback_reconstructor),\n      max_number_of_extrema_(max_number_of_extrema),\n      reconstruct_rho_times_temperature_(reconstruct_rho_times_temperature) {\n  std::tie(reconstruct_, reconstruct_lower_neighbor_,\n           reconstruct_upper_neighbor_) =\n      ::fd::reconstruction::wcns5z_function_pointers<3>(\n          nonlinear_weight_exponent_, fallback_reconstructor_);\n}\n\nWcns5zPrim::Wcns5zPrim(CkMigrateMessage* const msg) : Reconstructor(msg) {}\n\nstd::unique_ptr<Reconstructor> Wcns5zPrim::get_clone() const {\n  return std::make_unique<Wcns5zPrim>(*this);\n}\n\nvoid Wcns5zPrim::pup(PUP::er& p) {\n  Reconstructor::pup(p);\n  p | nonlinear_weight_exponent_;\n  p | epsilon_;\n  p | fallback_reconstructor_;\n  p | max_number_of_extrema_;\n  p | reconstruct_rho_times_temperature_;\n  if (p.isUnpacking()) {\n    std::tie(reconstruct_, reconstruct_lower_neighbor_,\n             reconstruct_upper_neighbor_) =\n        ::fd::reconstruction::wcns5z_function_pointers<3>(\n            nonlinear_weight_exponent_, fallback_reconstructor_);\n  }\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Wcns5zPrim::my_PUP_ID = 0;\n\ntemplate <size_t ThermodynamicDim>\nvoid Wcns5zPrim::reconstruct(\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>\n        vars_on_upper_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh) const {\n  DirectionalIdMap<dim, Variables<prims_to_reconstruct_tags>>\n      neighbor_variables_data{};\n  ::fd::neighbor_data_as_variables<dim>(make_not_null(&neighbor_variables_data),\n                                        ghost_data, ghost_zone_size(),\n                                        subcell_mesh);\n\n  reconstruct_prims_work<prims_to_reconstruct_tags>(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_vars, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n                     ghost_cell_vars, subcell_extents, number_of_variables,\n                     epsilon_, max_number_of_extrema_);\n      },\n      volume_prims, eos, element, neighbor_variables_data, subcell_mesh,\n      ghost_zone_size(), true, reconstruct_rho_times_temperature());\n}\n\ntemplate <size_t ThermodynamicDim>\nvoid Wcns5zPrim::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n    const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const Element<3>& element,\n    const DirectionalIdMap<3, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<3>& subcell_mesh,\n    const Direction<3> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work<prims_to_reconstruct_tags,\n                               prims_to_reconstruct_tags>(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<3>& subcell_extents,\n             const Index<3>& ghost_data_extents,\n             const Direction<3>& local_direction_to_reconstruct) {\n        reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, epsilon_, max_number_of_extrema_);\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<3>& subcell_extents,\n             const Index<3>& ghost_data_extents,\n             const Direction<3>& local_direction_to_reconstruct) {\n        reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, epsilon_, max_number_of_extrema_);\n      },\n      subcell_volume_prims, eos, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size(), true,\n      reconstruct_rho_times_temperature());\n}\n\nbool Wcns5zPrim::reconstruct_rho_times_temperature() const {\n  return reconstruct_rho_times_temperature_;\n}\n\nbool operator==(const Wcns5zPrim& lhs, const Wcns5zPrim& rhs) {\n  // Don't check function pointers since they are set from\n  // nonlinear_weight_exponent_ and fallback_reconstructor_\n  return lhs.nonlinear_weight_exponent_ == rhs.nonlinear_weight_exponent_ and\n         lhs.epsilon_ == rhs.epsilon_ and\n         lhs.fallback_reconstructor_ == rhs.fallback_reconstructor_ and\n         lhs.max_number_of_extrema_ == rhs.max_number_of_extrema_ and\n         lhs.reconstruct_rho_times_temperature() ==\n             rhs.reconstruct_rho_times_temperature();\n}\n\nbool operator!=(const Wcns5zPrim& lhs, const Wcns5zPrim& rhs) {\n  return not(lhs == rhs);\n}\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template void Wcns5zPrim::reconstruct(                                    \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>   \\\n          vars_on_lower_face,                                               \\\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, 3>*>   \\\n          vars_on_upper_face,                                               \\\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,         \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos, \\\n      const Element<3>& element,                                            \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          ghost_data,                                                       \\\n      const Mesh<3>& subcell_mesh) const;                                   \\\n  template void Wcns5zPrim::reconstruct_fd_neighbor(                        \\\n      gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,    \\\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims, \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& eos, \\\n      const Element<3>& element,                                            \\\n      const DirectionalIdMap<3, evolution::dg::subcell::GhostData>&         \\\n          ghost_data,                                                       \\\n      const Mesh<3>& subcell_mesh,                                          \\\n      const Direction<3> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef THERMO_DIM\n\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Wcns5z.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::fd {\n/*!\n * \\brief Fifth order weighted nonlinear compact scheme reconstruction using the\n * Z oscillation indicator. See ::fd::reconstruction::wcns5z() for details.\n *\n */\nclass Wcns5zPrim : public Reconstructor {\n private:\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n  using FallbackReconstructorType =\n      ::fd::reconstruction::FallbackReconstructorType;\n\n public:\n  static constexpr size_t dim = 3;\n\n  struct NonlinearWeightExponent {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The exponent q to which the oscillation indicator term is raised\"};\n  };\n  struct Epsilon {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The parameter added to the oscillation indicators to avoid division \"\n        \"by zero\"};\n  };\n  struct FallbackReconstructor {\n    using type = FallbackReconstructorType;\n    static constexpr Options::String help = {\n        \"A reconstruction scheme to fallback to adaptively. Finite difference \"\n        \"will switch to this reconstruction scheme if there are more extrema \"\n        \"in a FD stencil than a specified number. See also the option \"\n        \"'MaxNumberOfExtrema' below. Adaptive fallback is disabled if 'None'.\"};\n  };\n  struct MaxNumberOfExtrema {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The maximum allowed number of extrema in FD stencil for using Wcns5z \"\n        \"reconstruction before switching to a low-order reconstruction. If \"\n        \"FallbackReconstructor=None, this option is ignored\"};\n  };\n  struct ReconstructRhoTimesTemperature {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If 'true' then we reconstruct the rho*T, if 'false' we reconstruct \"\n        \"T.\"};\n  };\n\n  using options =\n      tmpl::list<NonlinearWeightExponent, Epsilon, FallbackReconstructor,\n                 MaxNumberOfExtrema, ReconstructRhoTimesTemperature>;\n\n  static constexpr Options::String help{\n      \"WCNS 5Z reconstruction scheme using primitive variables.\"};\n\n  Wcns5zPrim() = default;\n  Wcns5zPrim(Wcns5zPrim&&) = default;\n  Wcns5zPrim& operator=(Wcns5zPrim&&) = default;\n  Wcns5zPrim(const Wcns5zPrim&) = default;\n  Wcns5zPrim& operator=(const Wcns5zPrim&) = default;\n  ~Wcns5zPrim() override = default;\n\n  Wcns5zPrim(size_t nonlinear_weight_exponent, double epsilon,\n             FallbackReconstructorType fallback_reconstructor,\n             size_t max_number_of_extrema,\n             bool reconstruct_rho_times_temperature);\n\n  explicit Wcns5zPrim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor, Wcns5zPrim);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor> override;\n\n  static constexpr bool use_adaptive_order = false;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 3; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                 hydro::Tags::GrmhdEquationOfState, domain::Tags::Element<dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n                 evolution::dg::subcell::Tags::Mesh<dim>>;\n\n  template <size_t ThermodynamicDim>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n          vars_on_lower_face,\n      gsl::not_null<std::array<Variables<tags_list_for_reconstruct>, dim>*>\n          vars_on_upper_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& volume_prims,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh) const;\n\n  template <size_t ThermodynamicDim>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<tags_list_for_reconstruct>*> vars_on_face,\n      const Variables<hydro::grmhd_tags<DataVector>>& subcell_volume_prims,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n      const Element<dim>& element,\n      const DirectionalIdMap<dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<dim>& subcell_mesh,\n      Direction<dim> direction_to_reconstruct) const;\n\n  bool reconstruct_rho_times_temperature() const override;\n\n private:\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Wcns5zPrim& lhs, const Wcns5zPrim& rhs);\n  friend bool operator!=(const Wcns5zPrim& lhs, const Wcns5zPrim& rhs);\n\n  size_t nonlinear_weight_exponent_ = 0;\n  double epsilon_ = std::numeric_limits<double>::signaling_NaN();\n  FallbackReconstructorType fallback_reconstructor_ =\n      FallbackReconstructorType::None;\n  size_t max_number_of_extrema_ = 0;\n  bool reconstruct_rho_times_temperature_{false};\n\n  void (*reconstruct_)(gsl::not_null<std::array<gsl::span<double>, dim>*>,\n                       gsl::not_null<std::array<gsl::span<double>, dim>*>,\n                       const gsl::span<const double>&,\n                       const DirectionMap<dim, gsl::span<const double>>&,\n                       const Index<dim>&, size_t, double, size_t) = nullptr;\n  void (*reconstruct_lower_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<dim>&, const Index<dim>&,\n                                      const Direction<dim>&, const double&,\n                                      const size_t&) = nullptr;\n  void (*reconstruct_upper_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<dim>&, const Index<dim>&,\n                                      const Direction<dim>&, const double&,\n                                      const size_t&) = nullptr;\n};\n\n}  // namespace grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ExtractPoint.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/Hydro/MagneticFieldTreatment.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Simd/Simd.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\n// This class codes Eq. (B.34), rewritten as a standard-form\n// polynomial in (W - 1) or (W - ((tau/D) - (B^2/D) + 1)) for better\n// numerical behavior.\n//\n// If lower_bound is 0, the implemented function is\n// ((B^2/D) / 2 - (tau/D)) (1 + 2 (B^2/D) mu^2 + (B^2/D)^2 mu^2)\n// + (W-1) (2 ((B^2/D) - (tau/D)) (1 + (B^2/D) mu^2) + (B^2/D) mu^2 + 1)\n// + (W-1)^2 ((B^2/D) - (tau/D) + 3/2 (B^2/D) mu^2 + 2)\n// + (W-1)^3\n// where mu^2 = (B.S)^2 / (B^2 S^2).  A nice property of this form is\n// that, because we've already modified tau to satisfy B.39, its value\n// at W-1 = 0 is guaranteed to be negative, even in the presence of\n// roundoff error.\n//\n// If lower_bound is LB = ((tau/D) - (B^2/D)), the implemented function is\n// - 1/2 (B^2/D) (1 + mu^2 (tau/D) ((tau/D) + 2))\n// + (W-[LB+1]) (1 + (B^2/D) mu^2 + LB ((B^2/D) mu^2 + LB + 2))\n// + (W-[LB+1])^2 (2 LB + 3/2 (B^2/D) mu^2 + 2)\n// + (W-[LB+1])^3\n// This form is only used if LB > 0, so, as in the previous case, it\n// is guaranteed to be negative at W-[LB+1] = 0.\ntemplate <bool AssumeNonZeroMagneticField, typename T>\nclass FunctionOfLorentzFactor {\n public:\n  FunctionOfLorentzFactor(const T b_squared_over_d, const T tau_over_d,\n                          [[maybe_unused]] const T normalized_s_dot_b,\n                          const T lower_bound) {\n    const auto mask = lower_bound == 0.0;\n    T sqr_normalized_s_dot_b_times_b_squared_over_d{};\n    if constexpr (AssumeNonZeroMagneticField) {\n      sqr_normalized_s_dot_b_times_b_squared_over_d =\n          square(normalized_s_dot_b) * b_squared_over_d;\n    }\n    const auto zero_bound_values =\n        zero_bound(b_squared_over_d, tau_over_d,\n                   sqr_normalized_s_dot_b_times_b_squared_over_d);\n    const auto nonzero_bound_values =\n        nonzero_bound(b_squared_over_d, tau_over_d, lower_bound,\n                      sqr_normalized_s_dot_b_times_b_squared_over_d);\n    for (size_t i = 0; i < 4; ++i) {\n      gsl::at(coefficients_, i) =\n          simd::select(mask, gsl::at(zero_bound_values, i),\n                       gsl::at(nonzero_bound_values, i));\n    }\n  }\n\n  T operator()(const T excess_lorentz_factor) const {\n    return evaluate_polynomial(coefficients_, excess_lorentz_factor);\n  }\n\n private:\n  static std::array<T, 4> zero_bound(\n      const T& b_squared_over_d, const T& tau_over_d,\n      const T& sqr_normalized_s_dot_b_times_b_squared_over_d) {\n    if constexpr (AssumeNonZeroMagneticField) {\n      const T temp0 = b_squared_over_d - tau_over_d;\n      return std::array{\n          (0.5 * b_squared_over_d - tau_over_d) *\n              (sqr_normalized_s_dot_b_times_b_squared_over_d *\n                   (b_squared_over_d + 2.0) +\n               1.0),\n          2.0 * (sqr_normalized_s_dot_b_times_b_squared_over_d + 1.0) * temp0 +\n              sqr_normalized_s_dot_b_times_b_squared_over_d + 1.0,\n          temp0 + 1.5 * sqr_normalized_s_dot_b_times_b_squared_over_d + 2.0,\n          static_cast<T>(1.0)};\n    } else {\n      return std::array{-tau_over_d, -2.0 * tau_over_d + 1.0, -tau_over_d + 2.0,\n                        static_cast<T>(1.0)};\n    }\n  }\n\n  static std::array<T, 4> nonzero_bound(\n      const T& b_squared_over_d, const T& tau_over_d, const T& lower_bound,\n      const T& sqr_normalized_s_dot_b_times_b_squared_over_d) {\n    if constexpr (AssumeNonZeroMagneticField) {\n      return std::array{\n          -0.5 * (b_squared_over_d +\n                  sqr_normalized_s_dot_b_times_b_squared_over_d * tau_over_d *\n                      (tau_over_d + 2.0)),\n          1.0 + sqr_normalized_s_dot_b_times_b_squared_over_d +\n              lower_bound *\n                  (2.0 + sqr_normalized_s_dot_b_times_b_squared_over_d +\n                   lower_bound),\n          2.0 + 1.5 * sqr_normalized_s_dot_b_times_b_squared_over_d +\n              2.0 * lower_bound,\n          static_cast<T>(1.0)};\n    } else {\n      return std::array{static_cast<T>(0),\n                        1.0 + lower_bound * (2.0 + lower_bound),\n                        2.0 + 2.0 * lower_bound, static_cast<T>(1.0)};\n    }\n  }\n\n  std::array<T, 4> coefficients_;\n};\ntemplate <bool AssumeNonZeroMagneticField, typename T>\nFunctionOfLorentzFactor<AssumeNonZeroMagneticField, T>\nmake_function_of_lorentz_factor(T b_squared_over_d, T tau_over_d,\n                                T normalized_s_dot_b, T lower_bound) {\n  return {b_squared_over_d, tau_over_d, normalized_s_dot_b, lower_bound};\n}\n}  // namespace\n\nnamespace grmhd::ValenciaDivClean {\nFixConservatives::FixConservatives(\n    const double minimum_rest_mass_density_times_lorentz_factor,\n    const double rest_mass_density_times_lorentz_factor_cutoff,\n    double minimum_electron_fraction, double electron_fraction_cutoff,\n    const double safety_factor_for_magnetic_field,\n    const double safety_factor_for_momentum_density,\n    const double safety_factor_for_momentum_density_cutoff_d,\n    const double safety_factor_for_momentum_density_slope, const bool enable,\n    const hydro::MagneticFieldTreatment magnetic_field_treatment,\n    const Options::Context& context)\n    : minimum_rest_mass_density_times_lorentz_factor_(\n          minimum_rest_mass_density_times_lorentz_factor),\n      rest_mass_density_times_lorentz_factor_cutoff_(\n          rest_mass_density_times_lorentz_factor_cutoff),\n      minimum_electron_fraction_(minimum_electron_fraction),\n      electron_fraction_cutoff_(electron_fraction_cutoff),\n      one_minus_safety_factor_for_magnetic_field_(\n          1.0 - safety_factor_for_magnetic_field),\n      one_minus_safety_factor_for_momentum_density_(\n          1.0 - safety_factor_for_momentum_density),\n      safety_factor_for_momentum_density_cutoff_d_(\n          safety_factor_for_momentum_density_cutoff_d),\n      safety_factor_for_momentum_density_slope_(\n          safety_factor_for_momentum_density_slope),\n      enable_(enable),\n      magnetic_field_treatment_(magnetic_field_treatment) {\n  if (minimum_rest_mass_density_times_lorentz_factor_ >\n      rest_mass_density_times_lorentz_factor_cutoff_) {\n    PARSE_ERROR(context,\n                \"The minimum value of D (a.k.a. rest mass density times \"\n                \"Lorentz factor) (\"\n                    << minimum_rest_mass_density_times_lorentz_factor_\n                    << \") must be less than or equal to the cutoff value of D (\"\n                    << rest_mass_density_times_lorentz_factor_cutoff_ << ')');\n  }\n  if (minimum_electron_fraction_ > electron_fraction_cutoff_) {\n    PARSE_ERROR(context,\n                \"The minimum value of electron fraction Y_e (\"\n                    << minimum_electron_fraction_\n                    << \") must be less than or equal to the cutoff value (\"\n                    << electron_fraction_cutoff_ << ')');\n  }\n  if (safety_factor_for_momentum_density_slope_ < 0.0) {\n    PARSE_ERROR(context,\n                \"The option SafetyFactorForSSlope cannot be negative.\");\n  }\n  if (safety_factor_for_momentum_density +\n          safety_factor_for_momentum_density_slope_ *\n              log10(safety_factor_for_momentum_density_cutoff_d /\n                    minimum_rest_mass_density_times_lorentz_factor_) >=\n      1.0) {\n    PARSE_ERROR(context,\n                \"SafetyFactorForSSlope too large, will lead to unphysical \"\n                \"cutoff at low density\");\n  }\n}\n\n// NOLINTNEXTLINE(google-runtime-references)\nvoid FixConservatives::pup(PUP::er& p) {\n  p | minimum_rest_mass_density_times_lorentz_factor_;\n  p | rest_mass_density_times_lorentz_factor_cutoff_;\n  p | minimum_electron_fraction_;\n  p | electron_fraction_cutoff_;\n  p | one_minus_safety_factor_for_magnetic_field_;\n  p | one_minus_safety_factor_for_momentum_density_;\n  p | safety_factor_for_momentum_density_cutoff_d_;\n  p | safety_factor_for_momentum_density_slope_;\n  p | enable_;\n  p | magnetic_field_treatment_;\n}\n\n// WARNING!\n// Notation of Foucart is not that of SpECTRE\n// SpECTRE           Foucart\n// {\\tilde D}        \\rho_*\n// {\\tilde \\tau}     \\tau\n// {\\tilde S}_k      S_k\n// {\\tilde B}^k      B^k \\sqrt{g}\n// \\rho              \\rho_0\n// \\gamma_{mn}       g_{mn}\nbool FixConservatives::operator()(\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) const {\n  bool needed_fixing = false;\n  if (not enable_) {\n    return needed_fixing;\n  }\n  const size_t size = get<0>(tilde_b).size();\n  Variables<tmpl::list<::Tags::TempScalar<1>, ::Tags::TempScalar<2>,\n                       ::Tags::TempScalar<3>>>\n      temp_buffer(size);\n\n  Scalar<DataVector>& tilde_s_squared = get<::Tags::TempScalar<2>>(temp_buffer);\n  dot_product(make_not_null(&tilde_s_squared), *tilde_s, *tilde_s,\n              inv_spatial_metric);\n\n  const bool non_zero_mag =\n      magnetic_field_treatment_ == hydro::MagneticFieldTreatment::AssumeNonZero;\n  if (magnetic_field_treatment_ == hydro::MagneticFieldTreatment::CheckIfZero) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n    const_cast<bool&>(non_zero_mag) =\n        (max(max(abs(get<0>(tilde_b)), abs(get<1>(tilde_b)),\n                 abs(get<2>(tilde_b)))) > 0.0);\n  }\n\n  Scalar<DataVector>& tilde_b_squared = get<::Tags::TempScalar<1>>(temp_buffer);\n  Scalar<DataVector>& tilde_s_dot_tilde_b =\n      get<::Tags::TempScalar<3>>(temp_buffer);\n\n  if (non_zero_mag) {\n    dot_product(make_not_null(&tilde_b_squared), tilde_b, tilde_b,\n                spatial_metric);\n    dot_product(make_not_null(&tilde_s_dot_tilde_b), *tilde_s, tilde_b);\n  }\n\n  const double one_over_one_minus_safety_factor_for_magnetic_field =\n      1.0 / one_minus_safety_factor_for_magnetic_field_;\n  const double one_over_safety_factor_for_momentum_density_cutoff_d =\n      1.0 / safety_factor_for_momentum_density_cutoff_d_;\n\n  const auto fix_impl = [&](const size_t grid_index, auto use_simd,\n                            const auto completion_mask,\n                            auto assume_non_zero_magnetic_field_v) {\n    constexpr bool assume_non_zero_magnetic_field =\n        decltype(assume_non_zero_magnetic_field_v)::value;\n    using SimdType =\n        tmpl::conditional_t<std::decay_t<decltype(use_simd)>::value,\n                            simd::batch<double>, double>;\n    const auto load = [&grid_index, &use_simd](const auto& data_vector) {\n      (void)use_simd;\n      if constexpr (std::decay_t<decltype(use_simd)>::value) {\n        return simd::load_unaligned(&data_vector[grid_index]);\n      } else {\n        return data_vector[grid_index];\n      }\n    };\n\n    auto d_tilde = load(get(*tilde_d));\n    const auto sqrt_det_g = load(get(sqrt_det_spatial_metric));\n    const SimdType one_over_sqrt_det_g = 1.0 / sqrt_det_g;\n    SimdType one_over_d_tilde = 1.0 / d_tilde;\n    SimdType rest_mass_density_times_lorentz_factor =\n        d_tilde * one_over_sqrt_det_g;\n\n    // Increase electron fraction if necessary\n    auto ye_tilde = load(get(*tilde_ye));\n    if (const auto ye_mask = ye_tilde < electron_fraction_cutoff_ * d_tilde;\n        simd::any(ye_mask)) {\n      needed_fixing = true;\n      ye_tilde =\n          simd::select(ye_mask, minimum_electron_fraction_ * d_tilde, ye_tilde);\n    }\n\n    // Increase mass density if necessary\n    if (const auto tilde_d_mask =\n            rest_mass_density_times_lorentz_factor <\n            rest_mass_density_times_lorentz_factor_cutoff_;\n        simd::any(tilde_d_mask)) {\n      needed_fixing = true;\n      d_tilde = simd::select(\n          tilde_d_mask,\n          minimum_rest_mass_density_times_lorentz_factor_ * sqrt_det_g,\n          d_tilde);\n      ye_tilde = d_tilde * one_over_d_tilde * ye_tilde;\n      one_over_d_tilde = 1.0 / d_tilde;\n      rest_mass_density_times_lorentz_factor =\n          simd::select(tilde_d_mask,\n                       static_cast<SimdType>(\n                           minimum_rest_mass_density_times_lorentz_factor_),\n                       rest_mass_density_times_lorentz_factor);\n    }\n\n    // Increase internal energy if necessary\n    auto tau_tilde = load(get(*tilde_tau));\n    auto b_tilde_squared = static_cast<SimdType>(0);\n    if constexpr (assume_non_zero_magnetic_field) {\n      b_tilde_squared = load(get(tilde_b_squared));\n      // Equation B.39 of Foucart\n      if (const auto tilde_tau_mask =\n              b_tilde_squared > one_minus_safety_factor_for_magnetic_field_ *\n                                    2. * tau_tilde * sqrt_det_g;\n          simd::any(tilde_tau_mask)) {\n        needed_fixing = true;\n        tau_tilde = simd::select(\n            tilde_tau_mask,\n            0.5 * b_tilde_squared *\n                one_over_one_minus_safety_factor_for_magnetic_field *\n                one_over_sqrt_det_g,\n            tau_tilde);\n      }\n    }\n\n    else {\n      auto zeroes = static_cast<SimdType>(0);\n      if (const auto tilde_tau_mask = tau_tilde < zeroes;\n          simd::any(tilde_tau_mask)) {\n        needed_fixing = true;\n        tau_tilde = simd::select(tilde_tau_mask, zeroes, tau_tilde);\n      }\n    }\n\n    // Decrease momentum density if necessary\n    auto s_tilde_squared = load(get(tilde_s_squared));\n    // Equation B.24 of Foucart\n    const SimdType tau_over_d = tau_tilde * one_over_d_tilde;\n    // Equation B.23 of Foucart\n    const SimdType b_squared_over_d =\n        assume_non_zero_magnetic_field\n            ? b_tilde_squared * one_over_sqrt_det_g * one_over_d_tilde\n            : static_cast<SimdType>(0);\n    const auto s_tilde_dot_b_tilde = assume_non_zero_magnetic_field\n                                         ? load(get(tilde_s_dot_tilde_b))\n                                         : static_cast<SimdType>(0);\n\n    // Equation B.27 of Foucart\n    //\n    // We avoid division by zero in the SIMD case using a mask and then set the\n    // result to zero using the same mask.\n    const auto normaled_s_dot_b_mask =\n        (b_tilde_squared > 1.e-16 * d_tilde and\n         s_tilde_squared > 1.e-16 * square(d_tilde));\n    const SimdType normalized_s_dot_b =\n        assume_non_zero_magnetic_field\n            ? simd::select(\n                  normaled_s_dot_b_mask,\n                  s_tilde_dot_b_tilde /\n                      simd::select(normaled_s_dot_b_mask,\n                                   sqrt(b_tilde_squared * s_tilde_squared),\n                                   SimdType{1.0}),\n                  SimdType{0.0})\n            : static_cast<SimdType>(0);\n\n    // Equation B.40 of Foucart\n    const auto lower_bound_of_lorentz_factor_minus_one =\n        simd::max(tau_over_d - b_squared_over_d, SimdType{0.});\n    // Equation B.31 of Foucart\n    const auto upper_bound_for_s_tilde_squared =\n        [&b_squared_over_d, &d_tilde, &lower_bound_of_lorentz_factor_minus_one,\n         &normalized_s_dot_b](const auto local_excess_lorentz_factor) {\n          const auto local_lorentz_factor_minus_one =\n              lower_bound_of_lorentz_factor_minus_one +\n              local_excess_lorentz_factor;\n          if constexpr (assume_non_zero_magnetic_field) {\n            return square(1.0 + local_lorentz_factor_minus_one +\n                          b_squared_over_d) *\n                   local_lorentz_factor_minus_one *\n                   (2.0 + local_lorentz_factor_minus_one) /\n                   (square(1.0 + local_lorentz_factor_minus_one) +\n                    square(normalized_s_dot_b) * b_squared_over_d *\n                        (b_squared_over_d +\n                         2.0 * (1.0 + local_lorentz_factor_minus_one))) *\n                   square(d_tilde);\n          } else {\n            (void)b_squared_over_d;\n            (void)normalized_s_dot_b;\n            return local_lorentz_factor_minus_one *\n                   (2.0 + local_lorentz_factor_minus_one) * square(d_tilde);\n          }\n        };\n    const SimdType simple_upper_bound_for_s_tilde_squared =\n        upper_bound_for_s_tilde_squared(SimdType{0.});\n\n    // If s_tilde_squared is small enough, no fix is needed. Otherwise, we need\n    // to do some real work.\n    const auto one_minus_safety_factor_for_momentum_density_at_density =\n        simd::select(\n            rest_mass_density_times_lorentz_factor >\n                safety_factor_for_momentum_density_cutoff_d_,\n            SimdType{one_minus_safety_factor_for_momentum_density_},\n            one_minus_safety_factor_for_momentum_density_ +\n                safety_factor_for_momentum_density_slope_ *\n                    log10(\n                        rest_mass_density_times_lorentz_factor *\n                        one_over_safety_factor_for_momentum_density_cutoff_d));\n    if (const auto tilde_s_mask =\n            s_tilde_squared >\n                one_minus_safety_factor_for_momentum_density_at_density *\n                    simple_upper_bound_for_s_tilde_squared and\n            not completion_mask;\n        simd::any(tilde_s_mask)) {\n      // Find root of Equation B.34 of Foucart\n      // NOTE:\n      // - This assumes minimum specific enthalpy is 1.\n      // - SpEC implements a more complicated formula (B.32) which is equivalent\n      // - Bounds on root are given by Equation  B.40 of Foucart\n      // - In regions where the solution is just above atmosphere we sometimes\n      //   obtain an upper bound on the Lorentz factor somewhere around ~1e5,\n      //   while the actual Lorentz factor is only 1+1e-6. This leads to\n      //   situations where the solver must perform many (over 50) iterations to\n      //   converge. A simple way of avoiding this is to check that\n      //   [W_{lower_bound}, 10 * W_{lower_bound}] brackets the root and then\n      //   use 10 * W_{lower_bound} as the upper bound. This reduces the number\n      //   of iterations for the TOMS748 algorithm to converge to less than 10.\n      //   Note that the factor 10 is chosen arbitrarily and could probably be\n      //   reduced if required. The reasoning behind 10 is that it is unlikely\n      //   the Lorentz factor will increase by a factor of 10 from one time step\n      //   to the next in a physically meaning situation, and so 10 provides a\n      //   reasonable bound.\n      const auto f_of_lorentz_factor =\n          make_function_of_lorentz_factor<assume_non_zero_magnetic_field>(\n              b_squared_over_d, tau_over_d, normalized_s_dot_b,\n              lower_bound_of_lorentz_factor_minus_one);\n      SimdType upper_bound =\n          simd::select(lower_bound_of_lorentz_factor_minus_one == 0.0,\n                       tau_over_d, b_squared_over_d);\n\n      SimdType excess_lorentz_factor = 0.0;\n      if (const auto upper_not_zero_mask = upper_bound != 0.0;\n          simd::any(upper_not_zero_mask)) {\n        const SimdType f_at_lower = f_of_lorentz_factor(SimdType{0.0});\n        const SimdType candidate_upper_bound =\n            9.0 * (lower_bound_of_lorentz_factor_minus_one + 1.0);\n        // The if-based implementation is here as a reference since it's\n        // likely easier to understand.\n        //\n        // auto f_at_upper = std::numeric_limits<SimdType>::signaling_NaN();\n        // if (upper_bound < candidate_upper_bound) {\n        //   f_at_upper = f_of_lorentz_factor(upper_bound);\n        // } else {\n        //   f_at_upper = f_of_lorentz_factor(candidate_upper_bound);\n        //   if (f_at_upper > 0.0) {\n        //     upper_bound = candidate_upper_bound;\n        //   } else {\n        //     f_at_upper = f_of_lorentz_factor(upper_bound);\n        //   }\n        // }\n        auto f_at_upper = f_of_lorentz_factor(upper_bound);\n        if (const auto not_upper_less_candidate_bound =\n                not(upper_bound < candidate_upper_bound);\n            simd::any(not_upper_less_candidate_bound)) {\n          const SimdType f_at_candidate_upper_bound =\n              f_of_lorentz_factor(candidate_upper_bound);\n          upper_bound = simd::select((f_at_candidate_upper_bound > 0.0) and\n                                         not_upper_less_candidate_bound,\n                                     candidate_upper_bound, upper_bound);\n          f_at_upper = simd::select((f_at_candidate_upper_bound > 0.0) and\n                                        not_upper_less_candidate_bound,\n                                    f_at_candidate_upper_bound, f_at_upper);\n        }\n\n        try {\n          excess_lorentz_factor = RootFinder::toms748<true>(\n              f_of_lorentz_factor, SimdType{0.0}, upper_bound, f_at_lower,\n              f_at_upper, 1.e-14, 1.e-14, 100, not tilde_s_mask);\n        } catch (std::exception& exception) {\n          // clang-format makes the streamed text hard to read in code...\n          // clang-format off\n        ERROR(\n            \"Failed to fix conserved variables because the root finder failed \"\n            \"to find the lorentz factor.\\n\"\n            \"  upper_bound = \"\n            << std::scientific << std::setprecision(18)\n            << upper_bound\n            << \"\\n  lower_bound_of_lorentz_factor_minus_one = \"\n            << lower_bound_of_lorentz_factor_minus_one\n            << \"\\n  s_tilde_squared = \" << s_tilde_squared\n            << \"\\n  d_tilde = \" << d_tilde\n            << \"\\n  sqrt_det_g = \" << sqrt_det_g\n            << \"\\n  tau_tilde = \" << tau_tilde\n            << \"\\n  b_tilde_squared = \" << b_tilde_squared\n            << \"\\n  b_squared_over_d = \" << b_squared_over_d\n            << \"\\n  tau_over_d = \" << tau_over_d\n            << \"\\n  normalized_s_dot_b = \" << normalized_s_dot_b\n            << \"\\n  tilde_s =\\n\" << extract_point(*tilde_s, grid_index)\n            << \"\\n  tilde_b =\\n\" << extract_point(tilde_b, grid_index)\n            << \"\\n  spatial_metric =\\n\"\n            << extract_point(spatial_metric, grid_index)\n            << \"\\n  inv_spatial_metric =\\n\"\n            << extract_point(inv_spatial_metric, grid_index) << \"\\n\"\n            << \"The message of the exception thrown by the root finder \"\n               \"is:\\n\"\n            << exception.what());\n          // clang-format on\n        }\n      }\n\n      const auto rescaling_factor = simd::select(\n          tilde_s_mask,\n          simd::min(\n              sqrt(one_minus_safety_factor_for_momentum_density_ *\n                   upper_bound_for_s_tilde_squared(excess_lorentz_factor) /\n                   (s_tilde_squared + 1.e-16 * square(d_tilde))),\n              SimdType{1.}),\n          SimdType{1.});\n      if (UNLIKELY(simd::any(rescaling_factor < 1.))) {\n        needed_fixing = true;\n        for (size_t i = 0; i < 3; i++) {\n          if constexpr (std::decay_t<decltype(use_simd)>::value) {\n            SimdType s_tilde = load(tilde_s->get(i));\n            s_tilde *= rescaling_factor;\n            simd::store_unaligned(&tilde_s->get(i)[grid_index], s_tilde);\n          } else {\n            tilde_s->get(i)[grid_index] *= rescaling_factor;\n          }\n        }\n      }\n    }\n\n    if constexpr (std::decay_t<decltype(use_simd)>::value) {\n      simd::store_unaligned(&get(*tilde_d)[grid_index], d_tilde);\n      simd::store_unaligned(&get(*tilde_ye)[grid_index], ye_tilde);\n      simd::store_unaligned(&get(*tilde_tau)[grid_index], tau_tilde);\n    } else {\n      get(*tilde_d)[grid_index] = d_tilde;\n      get(*tilde_ye)[grid_index] = ye_tilde;\n      get(*tilde_tau)[grid_index] = tau_tilde;\n    }\n  };\n\n#ifdef SPECTRE_USE_XSIMD\n  constexpr size_t simd_width = simd::size<simd::batch<double>>();\n  if (size < simd_width) {\n    if (non_zero_mag) {\n      for (size_t s = 0; s < size; s++) {\n        fix_impl(s, std::false_type{}, false, std::true_type{});\n      }\n    } else {\n      for (size_t s = 0; s < size; s++) {\n        fix_impl(s, std::false_type{}, false, std::false_type{});\n      }\n    }\n  } else {\n    const size_t vectorized_size = size - size % simd_width;\n    if (non_zero_mag) {\n      for (size_t s = 0; s < vectorized_size; s += simd_width) {\n        fix_impl(s, std::true_type{},\n                 simd::mask_type_t<simd::batch<double>>{false},\n                 std::true_type{});\n      }\n      if (const size_t remainder = size - vectorized_size; remainder > 0) {\n        const auto complete_mask =\n            simd::make_sequence<simd::batch<double>>() <\n            (simd_width - static_cast<double>(remainder));\n        fix_impl(size - simd_width, std::true_type{}, complete_mask,\n                 std::true_type{});\n      }\n    } else {\n      for (size_t s = 0; s < vectorized_size; s += simd_width) {\n        fix_impl(s, std::true_type{},\n                 simd::mask_type_t<simd::batch<double>>{false},\n                 std::false_type{});\n      }\n      if (const size_t remainder = size - vectorized_size; remainder > 0) {\n        const auto complete_mask =\n            simd::make_sequence<simd::batch<double>>() <\n            (simd_width - static_cast<double>(remainder));\n        fix_impl(size - simd_width, std::true_type{}, complete_mask,\n                 std::false_type{});\n      }\n    }\n  }\n#else\n  if (non_zero_mag) {\n    for (size_t s = 0; s < size; s++) {\n      fix_impl(s, std::false_type{}, false, std::true_type{});\n    }\n  } else {\n    for (size_t s = 0; s < size; s++) {\n      fix_impl(s, std::false_type{}, false, std::false_type{});\n    }\n  }\n#endif\n  return needed_fixing;\n}\n\nbool operator==(const FixConservatives& lhs, const FixConservatives& rhs) {\n  return lhs.minimum_rest_mass_density_times_lorentz_factor_ ==\n             rhs.minimum_rest_mass_density_times_lorentz_factor_ and\n         lhs.rest_mass_density_times_lorentz_factor_cutoff_ ==\n             rhs.rest_mass_density_times_lorentz_factor_cutoff_ and\n         lhs.one_minus_safety_factor_for_magnetic_field_ ==\n             rhs.one_minus_safety_factor_for_magnetic_field_ and\n         lhs.one_minus_safety_factor_for_momentum_density_ ==\n             rhs.one_minus_safety_factor_for_momentum_density_ and\n         lhs.safety_factor_for_momentum_density_cutoff_d_ ==\n             rhs.safety_factor_for_momentum_density_cutoff_d_ and\n         lhs.safety_factor_for_momentum_density_slope_ ==\n             rhs.safety_factor_for_momentum_density_slope_ and\n         lhs.enable_ == rhs.enable_;\n}\n\nbool operator!=(const FixConservatives& lhs, const FixConservatives& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/MagneticFieldTreatment.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nclass DataVector;\n/// \\endcond\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\n\n/*!\n * \\ingroup VariableFixingGroup\n * \\brief Fix conservative variables using method developed by Foucart.\n *\n * Adjusts the conservative variables as follows:\n * - If the electron fraction \\f$Y_e\\f$ is below the value of the option\n *   `CutoffYe`, change \\f$\\tilde{Y}_e\\f$ to \\f$\\tilde{D}\\f$ times the value of\n *   the option `MinimumValueOfYe`.\n * - Changes \\f${\\tilde D}\\f$, the generalized mass-energy density, such\n *   that \\f$D\\f$, the product of the rest mass density \\f$\\rho\\f$ and the\n *   Lorentz factor \\f$W\\f$, is set to value of the option `MinimumValueOfD`,\n *   whenever \\f$D\\f$ is below the value of the option `CutoffD`.\n * - Increases \\f${\\tilde \\tau}\\f$, the generalized internal energy density,\n *   such that\n *   \\f${\\tilde B}^2 \\leq 2 \\sqrt{\\gamma} (1 - \\epsilon_B) {\\tilde \\tau}\\f$,\n *   where \\f${\\tilde B}^i\\f$ is the generalized magnetic field,\n *   \\f$\\gamma\\f$ is the determinant of the spatial metric, and\n *   \\f$\\epsilon_B\\f$ is the option `SafetyFactorForB`.\n * - Decreases \\f${\\tilde S}_i\\f$, the generalized momentum density, such that\n *   \\f${\\tilde S}^2 \\leq (1 - \\epsilon_S) {\\tilde S}^2_{max}\\f$, where\n *   \\f$\\epsilon_S\\f$ is the option `SafetyFactorForS`, and\n *   \\f${\\tilde S}^2_{max}\\f$ is a complicated function of the conservative\n *   variables which can only be found through root finding. There are\n *   sufficient conditions for a set of conservative variables to satisfy the\n *   inequality, which can be used to avoid root finding at most points.\n *\n * \\note The routine currently assumes the minimum specific enthalpy is one.\n *\n * For more details see Appendix B from the [thesis of Francois\n * Foucart](https://ecommons.cornell.edu/handle/1813/30652)\n *\n * You can plot the function whose root we are finding using:\n *\n * \\code{.py}\n *\n * import numpy as np\n * import matplotlib.pyplot as plt\n *\n * upper_bound = 1.000119047987896748e+00\n * lower_bound = 1.000000000000000000e+00\n * s_tilde_squared = 5.513009056734747750e-30\n * d_tilde = 1.131468709980503465e-12\n * sqrt_det_g: 1.131468709980503418e+00\n * tau_tilde = 1.346990732914080573e-16\n * b_tilde_squared = 3.048155733848927391e-16\n * b_squared_over_d = 2.380959757934347320e-04\n * tau_over_d = 1.190479878968363843e-04\n * normalized_s_dot_b = -9.999999082462245337e-01\n *\n *\n * def function_of_w(lorentz_factor):\n *     return ((lorentz_factor + b_squared_over_d - tau_over_d - 1.0) *\n *             (lorentz_factor**2 + b_squared_over_d * normalized_s_dot_b**2 *\n *              (b_squared_over_d + 2.0 * lorentz_factor)) -\n *             0.5 * b_squared_over_d -\n *             0.5 * b_squared_over_d * normalized_s_dot_b**2 *\n *             (lorentz_factor**2 - 1.0 +\n *              2.0 * lorentz_factor * b_squared_over_d + b_squared_over_d**2))\n *\n *\n * lorentz_factor = np.linspace(lower_bound, upper_bound, num=10000)\n *\n * plt.plot(lorentz_factor, function_of_w(lorentz_factor))\n * plt.show()\n *\n * \\endcode\n */\nclass FixConservatives {\n public:\n  /// \\brief Minimum value of rest-mass density times lorentz factor\n  struct MinimumValueOfD {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Minimum value of rest-mass density times lorentz factor\"};\n  };\n  /// \\brief Cutoff below which \\f$D = \\rho W\\f$ is set to MinimumValueOfD\n  struct CutoffD {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Cutoff below which D is set to MinimumValueOfD\"};\n  };\n  /// \\brief Minimum value of electron fraction \\f$Y_e\\f$\n  struct MinimumValueOfYe {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Minimum value of electron fraction\"};\n  };\n  /// \\brief Cutoff below which \\f$Y_e\\f$ is set to MinimumValueOfYe\n  struct CutoffYe {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Cutoff below which Y_e is set to MinimumValueOfYe\"};\n  };\n  /// \\brief Safety factor \\f$\\epsilon_B\\f$.\n  struct SafetyFactorForB {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Safety factor for magnetic field bound.\"};\n  };\n  /// \\brief Safety factor \\f$\\epsilon_S\\f$.\n  struct SafetyFactorForS {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Safety factor for momentum density bound above density cutoff.\"};\n  };\n  /// \\brief Cutoff in \\f$\\rho_0 W\\f$ below which we use a stricter safety\n  /// factor for the magnitude of S.\n  struct SafetyFactorForSCutoffD {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Below this value of rest mass density time Lorentz factor, limit S \"\n        \"more agressively.\"};\n  };\n\n  /// \\brief Below SafetyFactorForSCutoffD, reduce \\f$\\epsilon_S\\f$ by\n  /// SafetyFactorForSSlope times\n  /// \\f$\\log_{10}(\\rho_0 W / SafetyFactorForSCutoffD)\\f$\n  struct SafetyFactorForSSlope {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Slope of safety factor for momentum density bound below \"\n        \"SafetyFactorForSCutoffD, express as a function of log10(rho*W).\"};\n  };\n  /// Whether or not the limiting is enabled\n  struct Enable {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If true then the limiting is applied.\"};\n  };\n  /// How to treat the magnetic field\n  struct MagneticField {\n    using type = hydro::MagneticFieldTreatment;\n    static constexpr Options::String help = {\n        \"How to treat the magnetic field.\"};\n  };\n\n  using options =\n      tmpl::list<MinimumValueOfD, CutoffD, MinimumValueOfYe, CutoffYe,\n                 SafetyFactorForB, SafetyFactorForS, SafetyFactorForSCutoffD,\n                 SafetyFactorForSSlope, Enable, MagneticField>;\n  static constexpr Options::String help = {\n      \"Variable fixing used in Foucart's thesis.\\n\"};\n\n  FixConservatives(double minimum_rest_mass_density_times_lorentz_factor,\n                   double rest_mass_density_times_lorentz_factor_cutoff,\n                   double minimum_electron_fraction,\n                   double electron_fraction_cutoff,\n                   double safety_factor_for_magnetic_field,\n                   double safety_factor_for_momentum_density,\n                   double safety_factor_for_momentum_density_cutoff_d,\n                   double safety_factor_for_momentum_density_slope, bool enable,\n                   hydro::MagneticFieldTreatment magnetic_field_treatment,\n                   const Options::Context& context = {});\n\n  FixConservatives() = default;\n  FixConservatives(const FixConservatives& /*rhs*/) = default;\n  FixConservatives& operator=(const FixConservatives& /*rhs*/) = default;\n  FixConservatives(FixConservatives&& /*rhs*/) = default;\n  FixConservatives& operator=(FixConservatives&& /*rhs*/) = default;\n  ~FixConservatives() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  using return_tags = tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                                 grmhd::ValenciaDivClean::Tags::TildeYe,\n                                 grmhd::ValenciaDivClean::Tags::TildeTau,\n                                 grmhd::ValenciaDivClean::Tags::TildeS<>>;\n  using argument_tags =\n      tmpl::list<grmhd::ValenciaDivClean::Tags::TildeB<>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n\n  /// Returns `true` if any variables were fixed.\n  bool operator()(\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric) const;\n\n private:\n  friend bool operator==(const FixConservatives& lhs,\n                         const FixConservatives& rhs);\n\n  double minimum_rest_mass_density_times_lorentz_factor_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double rest_mass_density_times_lorentz_factor_cutoff_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double minimum_electron_fraction_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double electron_fraction_cutoff_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double one_minus_safety_factor_for_magnetic_field_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double one_minus_safety_factor_for_momentum_density_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double safety_factor_for_momentum_density_cutoff_d_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double safety_factor_for_momentum_density_slope_{\n      std::numeric_limits<double>::signaling_NaN()};\n  bool enable_{true};\n  hydro::MagneticFieldTreatment magnetic_field_treatment_{\n      hydro::MagneticFieldTreatment::AssumeNonZero};\n};\n\nbool operator!=(const FixConservatives& lhs, const FixConservatives& rhs);\n\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Flattener.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Flattener.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\ntemplate <typename RecoverySchemesList>\nFlattener<RecoverySchemesList>::Flattener(\n    const bool require_positive_mean_tilde_d,\n    const bool require_positive_mean_tilde_ye,\n    const bool require_physical_mean_tilde_tau, const bool recover_primitives)\n    : require_positive_mean_tilde_d_(require_positive_mean_tilde_d),\n      require_positive_mean_tilde_ye_(require_positive_mean_tilde_ye),\n      require_physical_mean_tilde_tau_(require_physical_mean_tilde_tau),\n      recover_primitives_(recover_primitives) {}\n\ntemplate <typename RecoverySchemesList>\nvoid Flattener<RecoverySchemesList>::pup(PUP::er& p) {\n  p | require_positive_mean_tilde_d_;\n  p | require_positive_mean_tilde_ye_;\n  p | require_physical_mean_tilde_tau_;\n  p | recover_primitives_;\n}\n\ntemplate <typename RecoverySchemesList>\nvoid Flattener<RecoverySchemesList>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> tilde_s,\n    const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> primitives,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Mesh<3>& mesh,\n    const Scalar<DataVector>& det_logical_to_inertial_inv_jacobian,\n    const EquationsOfState::EquationOfState<true, 3>& eos,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options) const {\n  // Create a temporary variable for each field's cell average.\n  // These temporaries live on the stack and should have minimal cost.\n  double mean_tilde_d = std::numeric_limits<double>::signaling_NaN();\n  double mean_tilde_ye = std::numeric_limits<double>::signaling_NaN();\n  double mean_tilde_tau = std::numeric_limits<double>::signaling_NaN();\n  auto mean_tilde_s =\n      make_array<3>(std::numeric_limits<double>::signaling_NaN());\n  const Scalar<DataVector> det_logical_to_inertial_jacobian{\n      DataVector{get(det_logical_to_inertial_inv_jacobian)}};\n  bool already_computed_means = false;\n\n  const auto compute_means = [\n    &already_computed_means, &mean_tilde_d, &mean_tilde_ye, &mean_tilde_tau,\n    &mean_tilde_s, &tilde_d, &tilde_ye, &tilde_tau, &tilde_s, &mesh,\n    &det_logical_to_inertial_jacobian,\n    require_positive_mean_tilde_d = require_positive_mean_tilde_d_,\n    require_positive_mean_tilde_ye = require_positive_mean_tilde_ye_\n  ]() {\n    if (already_computed_means) {\n      return;\n    }\n    already_computed_means = true;\n    // Compute the means w.r.t. the inertial coords\n    // (Note that several other parts of the limiter code take means w.r.t.\n    // the logical coords, and therefore might not be conservative on curved\n    // grids)\n    const double volume_of_cell =\n        definite_integral(get(det_logical_to_inertial_jacobian), mesh);\n    const auto inertial_coord_mean = [&mesh, &det_logical_to_inertial_jacobian,\n                                      &volume_of_cell](const DataVector& u) {\n      // Note that the term `det_jac * u` below results in an\n      // allocation. If this function needs to be optimized, a buffer\n      // for the product could be allocated outside the lambda, and\n      // updated in the lambda.\n      return definite_integral(get(det_logical_to_inertial_jacobian) * u,\n                               mesh) /\n             volume_of_cell;\n    };\n    mean_tilde_d = inertial_coord_mean(get(*tilde_d));\n    mean_tilde_ye = inertial_coord_mean(get(*tilde_ye));\n    mean_tilde_tau = inertial_coord_mean(get(*tilde_tau));\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(mean_tilde_s, i) = inertial_coord_mean(tilde_s->get(i));\n    }\n\n    if (require_positive_mean_tilde_d and mean_tilde_d < 0.) {\n      ERROR(\"We require TildeD to have positive mean, but got \"\n            << *tilde_d << \" with mean value \" << mean_tilde_d);\n    }\n\n    if (require_positive_mean_tilde_ye and mean_tilde_ye < 0.) {\n      ERROR(\"We require TildeYe to have positive mean, but got \"\n            << *tilde_ye << \" with mean value \" << mean_tilde_ye);\n    }\n  };\n\n  // If min(tilde_d) is negative, then flatten.\n  if (const double min_tilde_d = min(get(*tilde_d)),\n      min_tilde_ye = min(get(*tilde_ye));\n      min_tilde_d < 0. or min_tilde_ye < 0.) {\n    compute_means();\n\n    // Note: the current algorithm flattens all fields by the same factor,\n    // though in principle a different factor could be applied to each field.\n    //\n    // Experiments with cylindrical blast wave simulations showed that applying\n    // the same factor to all fields here dramatically improves results for the\n    // cylindrical blast wave. Of course, this might differ depending on the\n    // test problem.\n    constexpr double safety = 0.95;\n    const double factor = safety * mean_tilde_d / (mean_tilde_d - min_tilde_d);\n\n    get(*tilde_d) = mean_tilde_d + factor * (get(*tilde_d) - mean_tilde_d);\n    get(*tilde_ye) = mean_tilde_ye + factor * (get(*tilde_ye) - mean_tilde_ye);\n    get(*tilde_tau) =\n        mean_tilde_tau + factor * (get(*tilde_tau) - mean_tilde_tau);\n    for (size_t i = 0; i < 3; ++i) {\n      tilde_s->get(i) = gsl::at(mean_tilde_s, i) +\n                        factor * (tilde_s->get(i) - gsl::at(mean_tilde_s, i));\n    }\n  }\n\n  const Scalar<DataVector> tilde_b_squared =\n      dot_product(tilde_b, tilde_b, spatial_metric);\n\n  if (require_physical_mean_tilde_tau_) {\n    compute_means();\n    if (UNLIKELY(max((0.5 / mean_tilde_tau) * get(tilde_b_squared) /\n                     get(sqrt_det_spatial_metric)) > 1.0)) {\n      ERROR(\n          \"Unable to rescale TildeTau to the required range in a \"\n          \"conservative manner:\\n\"\n          \"mean_tilde_tau: \"\n          << mean_tilde_tau << \"\\ntilde_b_squared: \" << tilde_b_squared\n          << \"\\nsqrt_det_spatial_metric: \" << sqrt_det_spatial_metric);\n    }\n  }\n\n  // Check TildeTau with the condition from Foucart's thesis\n  //\n  // Increase tilde_tau if necessary. We do this by _decreasing_ the amount of\n  // oscillation around the mean.\n  compute_means();\n  const size_t num_pts = get(*tilde_tau).size();\n  double tilde_tau_scale_factor = 1.;\n  for (size_t i = 0; i < num_pts; ++i) {\n    if (0.5 * get(tilde_b_squared)[i] / get(sqrt_det_spatial_metric)[i] /\n            get(*tilde_tau)[i] >\n        1.) {\n      tilde_tau_scale_factor =\n          std::min(tilde_tau_scale_factor,\n                   std::abs((0.5 * get(tilde_b_squared)[i] /\n                                 get(sqrt_det_spatial_metric)[i] -\n                             mean_tilde_tau) /\n                            (get(*tilde_tau)[i] - mean_tilde_tau)));\n    }\n  }\n  if (tilde_tau_scale_factor < 1.) {\n    constexpr double safety = 0.99;\n    get(*tilde_tau) = mean_tilde_tau + (safety * tilde_tau_scale_factor) *\n                                           (get(*tilde_tau) - mean_tilde_tau);\n  }\n\n  // Check if we can recover the primitive variables. If not, then we need to\n  // flatten.\n  const size_t number_of_points = mesh.number_of_grid_points();\n  Variables<hydro::grmhd_tags<DataVector>> temp_prims(number_of_points);\n  get<hydro::Tags::Pressure<DataVector>>(temp_prims) =\n      get<hydro::Tags::Pressure<DataVector>>(*primitives);\n  if (not grmhd::ValenciaDivClean::\n          PrimitiveFromConservative<RecoverySchemesList, false>::apply(\n              make_not_null(\n                  &get<hydro::Tags::RestMassDensity<DataVector>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::ElectronFraction<DataVector>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                      temp_prims)),\n              make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n                  temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::MagneticField<DataVector, 3>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n                      temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::LorentzFactor<DataVector>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::Pressure<DataVector>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::Temperature<DataVector>>(temp_prims)),\n              *tilde_d, *tilde_ye, *tilde_tau, *tilde_s, tilde_b, tilde_phi,\n              spatial_metric, inv_spatial_metric, sqrt_det_spatial_metric, eos,\n              primitive_from_conservative_options)) {\n    compute_means();\n\n    get(*tilde_d) = mean_tilde_d;\n    get(*tilde_ye) = mean_tilde_ye;\n    get(*tilde_tau) = mean_tilde_tau;\n    for (size_t i = 0; i < 3; ++i) {\n      tilde_s->get(i) = gsl::at(mean_tilde_s, i);\n    }\n\n    if (recover_primitives_) {\n      grmhd::ValenciaDivClean::\n          PrimitiveFromConservative<RecoverySchemesList, true>::apply(\n              make_not_null(\n                  &get<hydro::Tags::RestMassDensity<DataVector>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::ElectronFraction<DataVector>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                      temp_prims)),\n              make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n                  temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::MagneticField<DataVector, 3>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n                      temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::LorentzFactor<DataVector>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::Pressure<DataVector>>(temp_prims)),\n              make_not_null(\n                  &get<hydro::Tags::Temperature<DataVector>>(temp_prims)),\n              *tilde_d, *tilde_ye, *tilde_tau, *tilde_s, tilde_b, tilde_phi,\n              spatial_metric, inv_spatial_metric, sqrt_det_spatial_metric, eos,\n              primitive_from_conservative_options);\n    }\n  }\n  if (recover_primitives_) {\n    using std::swap;\n    swap(*primitives, temp_prims);\n  }\n}\n\ntemplate <typename RecoverySchemesList>\nbool operator==(const Flattener<RecoverySchemesList>& lhs,\n                const Flattener<RecoverySchemesList>& rhs) {\n  return lhs.require_positive_mean_tilde_d_ ==\n             rhs.require_positive_mean_tilde_d_ and\n         lhs.require_positive_mean_tilde_ye_ ==\n             rhs.require_positive_mean_tilde_ye_ and\n         lhs.require_physical_mean_tilde_tau_ ==\n             rhs.require_physical_mean_tilde_tau_ and\n         lhs.recover_primitives_ == rhs.recover_primitives_;\n}\n\ntemplate <typename RecoverySchemesList>\nbool operator!=(const Flattener<RecoverySchemesList>& lhs,\n                const Flattener<RecoverySchemesList>& rhs) {\n  return not(lhs == rhs);\n}\n\nusing NewmanHamlinThenPalenzuelaEtAl =\n    tmpl::list<PrimitiveRecoverySchemes::NewmanHamlin,\n               PrimitiveRecoverySchemes::PalenzuelaEtAl>;\nusing KastaunThenNewmanThenPalenzuela =\n    tmpl::list<PrimitiveRecoverySchemes::KastaunEtAl,\n               PrimitiveRecoverySchemes::NewmanHamlin,\n               PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n\n#define ORDERED_RECOVERY_LIST                            \\\n  (tmpl::list<PrimitiveRecoverySchemes::KastaunEtAl>,    \\\n   tmpl::list<PrimitiveRecoverySchemes::NewmanHamlin>,   \\\n   tmpl::list<PrimitiveRecoverySchemes::PalenzuelaEtAl>, \\\n   NewmanHamlinThenPalenzuelaEtAl, KastaunThenNewmanThenPalenzuela)\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                    \\\n  template class Flattener<RECOVERY(data)>;                       \\\n  template bool operator==(const Flattener<RECOVERY(data)>& lhs,  \\\n                           const Flattener<RECOVERY(data)>& rhs); \\\n  template bool operator!=(const Flattener<RECOVERY(data)>& lhs,  \\\n                           const Flattener<RECOVERY(data)>& rhs);\nGENERATE_INSTANTIATIONS(INSTANTIATION, ORDERED_RECOVERY_LIST)\n#undef INSTANTIATION\n\n#undef THERMO_DIM\n#undef RECOVERY\n#undef ORDERED_RECOVERY_LIST\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Flattener.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean {\n/*!\n * \\brief Reduces oscillations inside an element in an attempt to guarantee a\n * physical solution of the conserved variables for which the primitive\n * variables can be recovered.\n *\n * The algorithm uses the conditions of FixConservatives on \\f$\\tilde{D}\\f$ and\n * \\f$\\tilde{\\tau}\\f$ to reduce oscillations inside an element. Oscillations are\n * reduced by rescaling the conserved variables about the mean to bring them\n * into the required range. When rescaling \\f$\\tilde{D}\\f$ because it is\n * negative, it is important to also rescale \\f$\\tilde{\\tau}\\f$ and\n * \\f$\\tilde{S}_i\\f$ by the same amount. At least, this is what is observed in\n * the cylindrical blast wave test problem.\n *\n * This currently doesn't use the check on \\f$\\tilde{S}^2\\f$, but instead checks\n * that the primitive variables can be recovered. If the primitives cannot be\n * recovered then we flatten to the mean values in the element.\n */\ntemplate <typename RecoverySchemesList>\nclass Flattener {\n public:\n  /// \\brief Require that the mean of TildeD is positive, otherwise terminate\n  /// the simulation.\n  struct RequirePositiveMeanTildeD {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Require that the mean of TildeD is positive, otherwise terminate the \"\n        \"simulation.\"};\n  };\n\n  /// \\brief Require that the mean of TildeYe is positive, otherwise terminate\n  /// the simulation.\n  struct RequirePositiveMeanTildeYe {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Require that the mean of TildeYe is positive, otherwise terminate the \"\n        \"simulation.\"};\n  };\n\n  /// \\brief Require that the mean of TildeTau is physical, otherwise terminate\n  /// the simulation.\n  struct RequirePhysicalMeanTildeTau {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Require that the mean of TildeTau is physical, otherwise terminate \"\n        \"the simulation.\"};\n  };\n\n  /// \\brief If true, then the primitive variables are updated at the end of the\n  /// function.\n  ///\n  /// This is useful when not using any pointwise conserved variable fixing,\n  /// treating the case that the means do not satisfy the bounds as an error.\n  struct RecoverPrimitives {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If true, then the primitive variables are updated at the end of the \"\n        \"function.\"};\n  };\n\n  using options =\n      tmpl::list<RequirePositiveMeanTildeD, RequirePositiveMeanTildeYe,\n                 RequirePhysicalMeanTildeTau, RecoverPrimitives>;\n  static constexpr Options::String help = {\n      \"Reduces oscillations (flattens) the conserved variables according to \"\n      \"the variable fixing procedure described in Foucart's thesis.\\n\"};\n\n  Flattener(bool require_positive_mean_tilde_d,\n            bool require_positive_mean_tilde_ye,\n            bool require_physical_mean_tilde_tau, bool recover_primitives);\n\n  Flattener() = default;\n  Flattener(const Flattener& /*rhs*/) = default;\n  Flattener& operator=(const Flattener& /*rhs*/) = default;\n  Flattener(Flattener&& /*rhs*/) = default;\n  Flattener& operator=(Flattener&& /*rhs*/) = default;\n  ~Flattener() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  using return_tags =\n      tmpl::list<Tags::TildeD, Tags::TildeYe, Tags::TildeTau,\n                 Tags::TildeS<Frame::Inertial>,\n                 ::Tags::Variables<hydro::grmhd_tags<DataVector>>>;\n  using argument_tags = tmpl::list<\n      Tags::TildeB<>, Tags::TildePhi,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>, domain::Tags::Mesh<3>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> tilde_d,\n      gsl::not_null<Scalar<DataVector>*> tilde_ye,\n      gsl::not_null<Scalar<DataVector>*> tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3>*> tilde_s,\n      gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> primitives,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Mesh<3>& mesh,\n      const Scalar<DataVector>& det_logical_to_inertial_inv_jacobian,\n      const EquationsOfState::EquationOfState<true, 3>& eos,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options) const;\n\n private:\n  template <typename LocalRecoverySchemesList>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Flattener<LocalRecoverySchemesList>& lhs,\n                         const Flattener<LocalRecoverySchemesList>& rhs);\n\n  bool require_positive_mean_tilde_d_ = false;\n  bool require_positive_mean_tilde_ye_ = false;\n  bool require_physical_mean_tilde_tau_ = false;\n  bool recover_primitives_ = false;\n};\n\ntemplate <typename RecoverySchemesList>\nbool operator!=(const Flattener<RecoverySchemesList>& lhs,\n                const Flattener<RecoverySchemesList>& rhs);\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TransportVelocity.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace detail {\nvoid fluxes_impl(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    // Temporaries\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        transport_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lapse_b_over_w,\n    const Scalar<DataVector>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataVector>& pressure_star_lapse_sqrt_det_spatial_metric,\n\n    // Extra args\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity) {\n  hydro::transport_velocity(transport_velocity, spatial_velocity, lapse, shift);\n  for (size_t i = 0; i < 3; ++i) {\n    tilde_d_flux->get(i) = get(tilde_d) * transport_velocity->get(i);\n    tilde_ye_flux->get(i) = get(tilde_ye) * transport_velocity->get(i);\n    tilde_tau_flux->get(i) =\n        get(tilde_tau) * transport_velocity->get(i) +\n        get(pressure_star_lapse_sqrt_det_spatial_metric) *\n            spatial_velocity.get(i) -\n        get(lapse) * get(magnetic_field_dot_spatial_velocity) * tilde_b.get(i);\n    tilde_phi_flux->get(i) =\n        get(lapse) * tilde_b.get(i) - get(tilde_phi) * shift.get(i);\n    for (size_t j = 0; j < 3; ++j) {\n      tilde_s_flux->get(i, j) = tilde_s.get(j) * transport_velocity->get(i) -\n                                lapse_b_over_w.get(j) * tilde_b.get(i);\n      tilde_b_flux->get(i, j) =\n        tilde_b.get(j) * transport_velocity->get(i) +\n        get(lapse) * (get(tilde_phi) * inv_spatial_metric.get(i, j) -\n        spatial_velocity.get(j) * tilde_b.get(i));\n    }\n    tilde_s_flux->get(i, i) += get(pressure_star_lapse_sqrt_det_spatial_metric);\n  }\n}\n}  // namespace detail\n\nvoid ComputeFluxes::apply(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& pressure,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field) {\n  Variables<tmpl::list<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>,\n                       hydro::Tags::MagneticFieldOneForm<DataVector, 3>,\n                       hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>,\n                       hydro::Tags::MagneticFieldSquared<DataVector>,\n                       ::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempI<2, 3, Frame::Inertial>>>\n      temp_tensors{get<0>(shift).size()};\n\n  auto& spatial_velocity_one_form =\n      get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(temp_tensors);\n  raise_or_lower_index(make_not_null(&spatial_velocity_one_form),\n                       spatial_velocity, spatial_metric);\n  auto& magnetic_field_one_form =\n      get<hydro::Tags::MagneticFieldOneForm<DataVector, 3>>(temp_tensors);\n  raise_or_lower_index(make_not_null(&magnetic_field_one_form), magnetic_field,\n                       spatial_metric);\n  auto& magnetic_field_dot_spatial_velocity =\n      get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n          temp_tensors);\n  dot_product(make_not_null(&magnetic_field_dot_spatial_velocity),\n              magnetic_field, spatial_velocity_one_form);\n  auto& magnetic_field_squared =\n      get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tensors);\n  dot_product(make_not_null(&magnetic_field_squared), magnetic_field,\n              magnetic_field_one_form);\n\n  DataVector& one_over_w_squared =\n      get(get<::Tags::TempScalar<0>>(temp_tensors));\n  one_over_w_squared = 1.0 / square(get(lorentz_factor));\n  // p_star = p + p_m = p + b^2/2 = p + ((B^m v_m)^2 + (B^m B_m)/W^2)/2\n  Scalar<DataVector>& pressure_star_lapse_sqrt_det_spatial_metric =\n      get<::Tags::TempScalar<1>>(temp_tensors);\n  get(pressure_star_lapse_sqrt_det_spatial_metric) =\n      get(sqrt_det_spatial_metric) * get(lapse) *\n      (get(pressure) + 0.5 * square(get(magnetic_field_dot_spatial_velocity)) +\n       0.5 * get(magnetic_field_squared) * one_over_w_squared);\n\n  // lapse b_i / W = lapse (B_i / W^2 + v_i (B^m v_m)\n  tnsr::i<DataVector, 3, Frame::Inertial>& lapse_b_over_w =\n      get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(temp_tensors);\n  for (size_t i = 0; i < 3; ++i) {\n    lapse_b_over_w.get(i) *= get(magnetic_field_dot_spatial_velocity);\n    lapse_b_over_w.get(i) +=\n        one_over_w_squared * magnetic_field_one_form.get(i);\n    lapse_b_over_w.get(i) *= get(lapse);\n  }\n\n  // Outside the loop to save allocations\n  detail::fluxes_impl(\n      tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux, tilde_b_flux,\n      tilde_phi_flux,\n      // Temporaries\n      make_not_null(&get<::Tags::TempI<2, 3, Frame::Inertial>>(temp_tensors)),\n      lapse_b_over_w, magnetic_field_dot_spatial_velocity,\n      pressure_star_lapse_sqrt_det_spatial_metric,\n      // Extra args\n      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, lapse, shift,\n      inv_spatial_metric, spatial_velocity);\n}\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\nnamespace detail {\nvoid fluxes_impl(\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n    gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n\n    // Temporaries\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> transport_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& lapse_b_over_w,\n    const Scalar<DataVector>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataVector>& pressure_star_lapse_sqrt_det_spatial_metric,\n\n    // Extra args\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity);\n}  // namespace detail\n\n/*!\n * \\brief The fluxes of the conservative variables\n *\n * \\f{align*}\n * F^i({\\tilde D}) = &~ {\\tilde D} v^i_{tr} \\\\\n * F^i({\\tilde Y}_e) = &~ {\\tilde Y}_e v^i_{tr} \\\\\n * F^i({\\tilde S}_j) = &~  {\\tilde S}_j v^i_{tr} + \\sqrt{\\gamma} \\alpha \\left( p\n * + p_m \\right) \\delta^i_j - \\frac{B_j {\\tilde B}^i}{W^2} - v_j {\\tilde B}^i\n * B^m v_m\\\\\n * F^i({\\tilde \\tau}) = &~  {\\tilde \\tau} v^i_{tr} + \\sqrt{\\gamma} \\alpha \\left(\n * p + p_m \\right) v^i - \\alpha {\\tilde B}^i B^m v_m \\\\\n * F^i({\\tilde B}^j) = &~  {\\tilde B}^j v^i_{tr} - \\alpha v^j {\\tilde B}^i +\n * \\alpha \\gamma^{ij} {\\tilde \\Phi} \\\\\n * F^i({\\tilde \\Phi}) = &~ \\alpha {\\tilde B^i} - \\beta^i {\\tilde \\Phi}\n * \\f}\n *\n * where the conserved variables \\f${\\tilde D}\\f$, \\f${\\tilde Y}_e\\f$,\n * \\f${\\tilde S}_i\\f$, \\f${\\tilde \\tau}\\f$, \\f${\\tilde B}^i\\f$, and \\f${\\tilde\n * \\Phi}\\f$ are a generalized mass-energy density, electron fraction, momentum\n * density, specific internal energy density, magnetic field, and divergence\n * cleaning field. Furthermore, \\f$v^i_{tr} = \\alpha v^i - \\beta^i\\f$ is the\n * transport velocity, \\f$\\alpha\\f$ is the lapse, \\f$\\beta^i\\f$ is the shift,\n * \\f$\\gamma\\f$ is the determinant of the spatial metric \\f$\\gamma_{ij}\\f$,\n * \\f$Y_e\\f$ is the electron fraction, \\f$v^i\\f$ is the spatial velocity,\n * \\f$B^i\\f$ is the spatial magnetic field measured by an Eulerian observer,\n * \\f$p\\f$ is the fluid pressure, and \\f$p_m = \\frac{1}{2} \\left[ \\left( B^n v_n\n * \\right)^2 + B^n B_n / W^2 \\right]\\f$ is the magnetic pressure.\n */\nstruct ComputeFluxes {\n  using return_tags =\n      tmpl::list<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                              tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                              tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                              tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeS<>,\n                              tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeB<>,\n                              tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                              tmpl::size_t<3>, Frame::Inertial>>;\n\n  using argument_tags =\n      tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                 grmhd::ValenciaDivClean::Tags::TildeYe,\n                 grmhd::ValenciaDivClean::Tags::TildeTau,\n                 grmhd::ValenciaDivClean::Tags::TildeS<>,\n                 grmhd::ValenciaDivClean::Tags::TildeB<>,\n                 grmhd::ValenciaDivClean::Tags::TildePhi,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>>;\n\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& pressure,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field);\n};\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n\ntemplate class Events::ObserveTimeStep<grmhd::ValenciaDivClean::System>;\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n\ntemplate class ChangeTimeStepperOrder<grmhd::ValenciaDivClean::System>;\ntemplate class CleanHistory<grmhd::ValenciaDivClean::System>;\ntemplate class RecordTimeStepperData<grmhd::ValenciaDivClean::System>;\ntemplate class UpdateU<grmhd::ValenciaDivClean::System, false>;\ntemplate class UpdateU<grmhd::ValenciaDivClean::System, true>;\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n\nnamespace evolution::dg::Actions::detail {\ntemplate void volume_terms<::grmhd::ValenciaDivClean::TimeDerivativeTerms>(\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::dt,\n        typename ::grmhd::ValenciaDivClean::System::variables_tag::tags_list>>*>\n        dt_vars_ptr,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::Flux,\n        typename ::grmhd::ValenciaDivClean::System::flux_variables,\n        tmpl::size_t<3>, Frame::Inertial>>*>\n        volume_fluxes,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::deriv,\n        typename ::grmhd::ValenciaDivClean::System::gradient_variables,\n        tmpl::size_t<3>, Frame::Inertial>>*>\n        partial_derivs,\n    const gsl::not_null<\n        Variables<typename ::grmhd::ValenciaDivClean::System::\n                      compute_volume_time_derivative_terms::temporary_tags>*>\n        temporaries,\n    [[maybe_unused]] const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::div, db::wrap_tags_in<::Tags::Flux,\n                                      typename ::grmhd::ValenciaDivClean::\n                                          System::flux_variables,\n                                      tmpl::size_t<3>, Frame::Inertial>>>*>\n        div_fluxes,\n    const Variables<\n        typename ::grmhd::ValenciaDivClean::System::variables_tag::tags_list>&\n        evolved_vars,\n    const ::dg::Formulation dg_formulation, const Mesh<3>& mesh,\n    [[maybe_unused]] const tnsr::I<DataVector, 3, Frame::Inertial>&\n        inertial_coordinates,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        logical_to_inertial_inverse_jacobian,\n    [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const std::optional<Scalar<DataVector>>& div_mesh_velocity,\n\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n    const Scalar<DataVector>& pressure,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_internal_energy,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    const double& constraint_damping_parameter);\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp\"\n\n/// \\cond\nnamespace EquationsOfState {\ntemplate <bool, size_t>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace grmhd::ValenciaDivClean {\nclass PrimitiveFromConservativeOptions;\n}  // namespace grmhd::ValenciaDivClean\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\n\n/*!\n * \\brief Compute the primitive variables from the conservative variables using\n * the scheme of \\cite Kastaun2020uxr.\n *\n * In the notation of the Kastaun paper, `tau` is \\f$D q)\\f$,\n * `momentum_density_squared` is \\f$r^2 D^2\\f$,\n * `momentum_density_dot_magnetic_field` is \\f$t D^{\\frac{3}{2}}\\f$,\n * `magnetic_field_squared` is \\f$s D\\f$, and\n * `rest_mass_density_times_lorentz_factor` is \\f$D\\f$.\n * Furthermore, the returned `PrimitiveRecoveryData.rho_h_w_squared` is \\f$x\n * D\\f$.\n *\n * In terms of the conservative variables (in our notation):\n * \\f{align*}\n * q = & \\frac{{\\tilde \\tau}}{{\\tilde D}} \\\\\n * r = & \\frac{\\gamma^{kl} {\\tilde S}_k {\\tilde S}_l}{{\\tilde D}^2} \\\\\n * t^2 = & \\frac{({\\tilde B}^k {\\tilde S}_k)^2}{{\\tilde D}^3 \\sqrt{\\gamma}} \\\\\n * s = & \\frac{\\gamma_{kl} {\\tilde B}^k {\\tilde B}^l}{{\\tilde D}\\sqrt{\\gamma}}\n * \\f}\n *\n * where the conserved variables \\f${\\tilde D}\\f$, \\f${\\tilde S}_i\\f$,\n * \\f${\\tilde \\tau}\\f$, and \\f${\\tilde B}^i\\f$ are a generalized mass-energy\n * density, momentum density, specific internal energy density, and magnetic\n * field, and \\f$\\gamma\\f$ and \\f$\\gamma^{kl}\\f$ are the determinant and inverse\n * of the spatial metric \\f$\\gamma_{kl}\\f$.\n *\n * \\note This scheme does not use the initial guess for the pressure.\n */\nclass KastaunEtAl {\n public:\n  template <bool EnforcePhysicality, typename EosType>\n  static std::optional<PrimitiveRecoveryData> apply(\n      double initial_guess_pressure, double tau,\n      double momentum_density_squared,\n      double momentum_density_dot_magnetic_field, double magnetic_field_squared,\n      double rest_mass_density_times_lorentz_factor, double electron_fraction,\n      const EosType& equation_of_state,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n\n  static const std::string name() { return \"KastaunEtAl\"; }\n\n private:\n  static constexpr size_t max_iterations_ = 100;\n  static constexpr double absolute_tolerance_ =\n      10.0 * std::numeric_limits<double>::epsilon();\n  static constexpr double relative_tolerance_ =\n      10.0 * std::numeric_limits<double>::epsilon();\n};\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n\n#include <cmath>\n#include <exception>\n#include <limits>\n#include <optional>\n#include <stdexcept>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\n\nnamespace KastaunEtAl_detail {\n// GCC warns without forward decls\ndouble compute_x(double mu, double b_squared);\ndouble compute_r_bar_squared(double mu, double x, double r_squared,\n                             double r_dot_b_squared);\ndouble compute_v_0_squared(double r_squared, double h_0, double lorentz_max);\n\n// Equation (26)\ndouble compute_x(const double mu, const double b_squared) {\n  return 1.0 / (1.0 + mu * b_squared);\n}\n\n// Equation (38)\ndouble compute_r_bar_squared(const double mu, const double x,\n                             const double r_squared,\n                             const double r_dot_b_squared) {\n  return x * (r_squared * x + mu * (1.0 + x) * r_dot_b_squared);\n}\n\n// Equations (33) and (32)\ndouble compute_v_0_squared(const double r_squared, const double h_0,\n                           const double lorentz_max) {\n  const double z_0_squared = r_squared / square(h_0);\n  const double velocity_squared_upper_bound =\n      1.0 - 1.0 / (lorentz_max * lorentz_max);\n  return std::min(z_0_squared / (1.0 + z_0_squared),\n                  velocity_squared_upper_bound);\n}\n\nstruct Primitives {\n  const double rest_mass_density;\n  const double lorentz_factor;\n  const double pressure;\n  const double specific_internal_energy;\n  const double q_bar;\n  const double r_bar_squared;\n};\n\n// Function to tighten master function bracket in corner case discussed in\n// Appendix A when Equation (41) produces a rest mass density outside the\n// valid range of the EOS\nclass CornerCaseFunction {\n public:\n  CornerCaseFunction(const double w_target, const double r_squared,\n                     const double b_squared, const double r_dot_b_squared)\n      : v_squared_target_(1.0 - 1.0 / square(w_target)),\n        r_squared_(r_squared),\n        b_squared_(b_squared),\n        r_dot_b_squared_(r_dot_b_squared) {}\n\n  double operator()(const double mu) const {\n    const double x = compute_x(mu, b_squared_);\n    const double r_bar_squared =\n        compute_r_bar_squared(mu, x, r_squared_, r_dot_b_squared_);\n    // v = mu*r_bar, see text after Equation (31)\n    // target is v satisfying W(mu) = D/ rho_{min/max}\n    return square(mu) * r_bar_squared - v_squared_target_;\n  }\n\n private:\n  const double v_squared_target_;\n  const double r_squared_;\n  const double b_squared_;\n  const double r_dot_b_squared_;\n};\n\n// Function whose root is upper bracket of master function, see Sec. II.F\nclass AuxiliaryFunction {\n public:\n  AuxiliaryFunction(const double h_0, const double r_squared,\n                    const double b_squared, const double r_dot_b_squared)\n      : h_0_(h_0),\n        r_squared_(r_squared),\n        b_squared_(b_squared),\n        r_dot_b_squared_(r_dot_b_squared) {}\n\n  double operator()(double mu) const {\n    const double x = compute_x(mu, b_squared_);\n    const double r_bar_squared =\n        compute_r_bar_squared(mu, x, r_squared_, r_dot_b_squared_);\n    // Equation (49)\n    return mu * sqrt(square(h_0_) + r_bar_squared) - 1.0;\n  }\n\n private:\n  const double h_0_;\n  const double r_squared_;\n  const double b_squared_;\n  const double r_dot_b_squared_;\n};\n\n// Master function, see Equation (44) in Sec. II.E\ntemplate <bool EnforcePhysicality, typename EosType>\nclass FunctionOfMu {\n public:\n  FunctionOfMu(const double tau, const double momentum_density_squared,\n               const double momentum_density_dot_magnetic_field,\n               const double magnetic_field_squared,\n               const double rest_mass_density_times_lorentz_factor,\n               const double electron_fraction, const EosType& equation_of_state,\n               const double lorentz_max)\n      : r_squared_(momentum_density_squared /\n                   square(rest_mass_density_times_lorentz_factor)),\n        b_squared_(magnetic_field_squared /\n                   rest_mass_density_times_lorentz_factor),\n        r_dot_b_squared_(square(momentum_density_dot_magnetic_field) /\n                         cube(rest_mass_density_times_lorentz_factor)),\n        rest_mass_density_times_lorentz_factor_(\n            rest_mass_density_times_lorentz_factor),\n        electron_fraction_(electron_fraction),\n        equation_of_state_(equation_of_state),\n        h_0_(equation_of_state_.specific_enthalpy_lower_bound()),\n        v_0_squared_(compute_v_0_squared(r_squared_, h_0_, lorentz_max)) {\n    double eps_min{};\n    if constexpr (EosType::thermodynamic_dim == 3) {\n      eps_min = equation_of_state_.specific_internal_energy_lower_bound(\n          rest_mass_density_times_lorentz_factor_ / lorentz_max,\n          electron_fraction_);\n    } else if constexpr (EosType::thermodynamic_dim == 2) {\n      eps_min = equation_of_state_.specific_internal_energy_lower_bound(\n          rest_mass_density_times_lorentz_factor_ / lorentz_max);\n    } else {\n      eps_min = equation_of_state_.specific_internal_energy_lower_bound();\n    }\n    q_ = tau / rest_mass_density_times_lorentz_factor;\n    if constexpr (EnforcePhysicality) {\n      q_ = std::max(q_, eps_min);\n      const double r_squared_bound =\n          4.0 * v_0_squared_ * square(q_ + 1.0) / square(1.0 + v_0_squared_);\n      if (r_squared_bound < r_squared_) {\n        r_squared_ = r_squared_bound;\n        r_dot_b_squared_ *= r_squared_bound / r_squared_;\n      }\n\n    } else {\n      const double r_squared_bound =\n          4.0 * v_0_squared_ * square(q_ + 1.0) / square(1.0 + v_0_squared_);\n      if (q_ < eps_min or r_squared_ > r_squared_bound) {\n        state_is_unphysical_ = true;\n      }\n    }\n  }\n\n  std::pair<double, double> root_bracket(\n      double rest_mass_density_times_lorentz_factor, double absolute_tolerance,\n      double relative_tolerance, size_t max_iterations) const;\n\n  Primitives primitives(double mu) const;\n\n  double operator()(double mu) const;\n\n  bool state_is_unphysical() const { return state_is_unphysical_; }\n\n private:\n  double q_;\n  double r_squared_;\n  const double b_squared_;\n  double r_dot_b_squared_;\n  const double rest_mass_density_times_lorentz_factor_;\n  const double electron_fraction_;\n  const EosType& equation_of_state_;\n  const double h_0_;\n  const double v_0_squared_;\n  bool state_is_unphysical_ = false;\n};\n\ntemplate <bool EnforcePhysicality, typename EosType>\nstd::pair<double, double>\nFunctionOfMu<EnforcePhysicality, EosType>::root_bracket(\n    const double rest_mass_density_times_lorentz_factor,\n    const double absolute_tolerance, const double relative_tolerance,\n    const size_t max_iterations) const {\n  // see text between Equations (49) and (50) and after Equation (54)\n  double lower_bound = 0.0;\n  // We use `1 / (h_0_ + numeric_limits<double>::min())` to avoid division by\n  // zero in a way that avoids conditionals.\n  double upper_bound = 1.0 / (h_0_ + std::numeric_limits<double>::min());\n  if (r_squared_ < square(h_0_)) {\n    // need to solve auxiliary function to determine mu_+ which will\n    // be the upper bound for the master function bracket\n    const auto auxiliary_function =\n        AuxiliaryFunction{h_0_, r_squared_, b_squared_, r_dot_b_squared_};\n    upper_bound =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(auxiliary_function, lower_bound, upper_bound,\n                            absolute_tolerance, relative_tolerance,\n                            max_iterations);\n  }\n\n  // Determine if the corner case discussed in Appendix A occurs where the\n  // mass density is outside the valid range of the EOS\n  const double rho_min = equation_of_state_.rest_mass_density_lower_bound();\n  const double rho_max = equation_of_state_.rest_mass_density_upper_bound();\n\n  // If this is triggering, the most likely cause is that the density cutoff\n  // for atmosphere is smaller than the minimum density of the EOS, i.e. this\n  // point should have been flagged as atmosphere\n  if (rest_mass_density_times_lorentz_factor < rho_min) {\n    throw std::runtime_error(\"Density too small for EOS\");\n  }\n\n  const double mu_b = upper_bound;\n  const double x = compute_x(mu_b, b_squared_);\n  const double r_bar_squared =\n      compute_r_bar_squared(mu_b, x, r_squared_, r_dot_b_squared_);\n  // Equation (40)\n  const double v_hat_squared =\n      std::min(square(mu_b) * r_bar_squared, v_0_squared_);\n  const double w_hat = 1.0 / sqrt(1.0 - v_hat_squared);\n\n  // If this is being triggered, the only possible recourse would be\n  // to limit the rest mass density to the maximum value allowed by the\n  // EOS.  This seems questionable, so it is treated as an inversion failure.\n  // The exception should be caught by the try-catch in apply() which will\n  // return std::nullopt\n  if (rest_mass_density_times_lorentz_factor / w_hat > rho_max) {\n    throw std::runtime_error(\"Density too big for EOS\");\n  }\n\n  if (rest_mass_density_times_lorentz_factor / w_hat < rho_min) {\n    // adjust lower bound\n    const auto corner_case_function =\n        CornerCaseFunction{rest_mass_density_times_lorentz_factor / rho_max,\n                           r_squared_, b_squared_, r_dot_b_squared_};\n    lower_bound = std::max(\n        lower_bound, RootFinder::toms748(corner_case_function, lower_bound,\n                                         upper_bound, absolute_tolerance,\n                                         relative_tolerance, max_iterations));\n  }\n\n  if (rho_max < rest_mass_density_times_lorentz_factor) {\n    // adjust upper bound\n    const auto corner_case_function =\n        CornerCaseFunction{rest_mass_density_times_lorentz_factor / rho_min,\n                           r_squared_, b_squared_, r_dot_b_squared_};\n    upper_bound = std::min(\n        upper_bound, RootFinder::toms748(corner_case_function, lower_bound,\n                                         upper_bound, absolute_tolerance,\n                                         relative_tolerance, max_iterations));\n  }\n\n  return {lower_bound, upper_bound};\n}\n\ntemplate <bool EnforcePhysicality, typename EosType>\nPrimitives FunctionOfMu<EnforcePhysicality, EosType>::primitives(\n    const double mu) const {\n  // Equation (26)\n  const double x = compute_x(mu, b_squared_);\n  // Equations(38)\n  const double r_bar_squared =\n      compute_r_bar_squared(mu, x, r_squared_, r_dot_b_squared_);\n  // Equation (40)\n  const double v_hat_squared =\n      std::min(square(mu) * r_bar_squared, v_0_squared_);\n  const double w_hat = 1.0 / sqrt(1.0 - v_hat_squared);\n  // Equation (41) with bounds from Equation (5)\n  const double rho_hat =\n      std::clamp(rest_mass_density_times_lorentz_factor_ / w_hat,\n                 equation_of_state_.rest_mass_density_lower_bound(),\n                 equation_of_state_.rest_mass_density_upper_bound());\n  // Equations (39) and (25)\n  const double q_bar =\n      q_ - 0.5 * b_squared_ -\n      0.5 * square(mu * x) * (r_squared_ * b_squared_ - r_dot_b_squared_);\n  // Equation (42) with bounds from Equation (6)\n  double epsilon_hat = w_hat * (q_bar - mu * r_bar_squared) +\n                       v_hat_squared * square(w_hat) / (1.0 + w_hat);\n  if constexpr (EosType::thermodynamic_dim == 3) {\n    epsilon_hat =\n        std::clamp(epsilon_hat,\n                   equation_of_state_.specific_internal_energy_lower_bound(\n                       rho_hat, electron_fraction_),\n                   equation_of_state_.specific_internal_energy_upper_bound(\n                       rho_hat, electron_fraction_));\n  } else if constexpr (EosType::thermodynamic_dim == 2) {\n    epsilon_hat = std::clamp(\n        epsilon_hat,\n        equation_of_state_.specific_internal_energy_lower_bound(rho_hat),\n        equation_of_state_.specific_internal_energy_upper_bound(rho_hat));\n  } else {\n    epsilon_hat = std::clamp(\n        epsilon_hat,\n        equation_of_state_.specific_internal_energy_lower_bound(),\n        equation_of_state_.specific_internal_energy_upper_bound());\n  }\n  // Pressure from EOS\n  double p_hat = std::numeric_limits<double>::signaling_NaN();\n  if constexpr (EosType::thermodynamic_dim == 1) {\n    // Note: we do not reset epsilon to satisfy the EOS because in that case\n    // we are not guaranteed to be able to find a solution. That's because the\n    // conserved-variable solution is inconsistent with the restrictive\n    // 1d-EOS. Instead, we recover the primitives and then reset the specific\n    // internal energy and specific enthalpy using the EOS.\n    p_hat =\n        get(equation_of_state_.pressure_from_density(Scalar<double>(rho_hat)));\n  } else if constexpr (EosType::thermodynamic_dim == 2) {\n    p_hat = get(equation_of_state_.pressure_from_density_and_energy(\n        Scalar<double>(rho_hat), Scalar<double>(epsilon_hat)));\n  } else if constexpr (EosType::thermodynamic_dim == 3) {\n    p_hat = get(equation_of_state_.pressure_from_density_and_energy(\n        Scalar<double>(rho_hat), Scalar<double>(epsilon_hat),\n        Scalar<double>(electron_fraction_)));\n  }\n  return Primitives{rho_hat, w_hat, p_hat, epsilon_hat, q_bar, r_bar_squared};\n}\n\ntemplate <bool EnforcePhysicality, typename EosType>\ndouble FunctionOfMu<EnforcePhysicality, EosType>::operator()(\n    const double mu) const {\n  const auto [rho_hat, w_hat, p_hat, epsilon_hat, q_bar, r_bar_squared] =\n      primitives(mu);\n  // Equation (43)\n  const double a_hat = p_hat / (rho_hat * (1.0 + epsilon_hat));\n  const double h_hat = (1.0 + epsilon_hat) * (1.0 + a_hat);\n  // Equations (46) - (48)\n  const double nu_hat = std::max(\n      h_hat / w_hat, (1.0 + a_hat) * (1.0 + q_bar - mu * r_bar_squared));\n  // Equations (44) - (45)\n  return mu - 1.0 / (nu_hat + mu * r_bar_squared);\n}\n}  // namespace KastaunEtAl_detail\n\ntemplate <bool EnforcePhysicality, typename EosType>\nstd::optional<PrimitiveRecoveryData> KastaunEtAl::apply(\n    const double /*initial_guess_pressure*/, const double tau,\n    const double momentum_density_squared,\n    const double momentum_density_dot_magnetic_field,\n    const double magnetic_field_squared,\n    const double rest_mass_density_times_lorentz_factor,\n    const double electron_fraction, const EosType& equation_of_state,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options) {\n  // Master function see Equation (44)\n  const auto f_of_mu =\n      KastaunEtAl_detail::FunctionOfMu<EnforcePhysicality, EosType>{\n          tau,\n          momentum_density_squared,\n          momentum_density_dot_magnetic_field,\n          magnetic_field_squared,\n          rest_mass_density_times_lorentz_factor,\n          electron_fraction,\n          equation_of_state,\n          primitive_from_conservative_options.kastaun_max_lorentz_factor()};\n  if (f_of_mu.state_is_unphysical()) {\n    return std::nullopt;\n  }\n\n  // mu is 1 / (h W) see Equation (26)\n  double one_over_specific_enthalpy_times_lorentz_factor =\n      std::numeric_limits<double>::signaling_NaN();\n  try {\n    // Bracket for master function, see Sec. II.F\n    const auto [lower_bound, upper_bound] = f_of_mu.root_bracket(\n        rest_mass_density_times_lorentz_factor, absolute_tolerance_,\n        relative_tolerance_, max_iterations_);\n\n    // Try to recover primitves\n    one_over_specific_enthalpy_times_lorentz_factor =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(f_of_mu, lower_bound, upper_bound,\n                            absolute_tolerance_, relative_tolerance_,\n                            max_iterations_);\n  } catch (std::exception& exception) {\n    return std::nullopt;\n  }\n\n  const auto [rest_mass_density, lorentz_factor, pressure,\n              specific_internal_energy, q_bar, r_bar_squared] =\n      f_of_mu.primitives(one_over_specific_enthalpy_times_lorentz_factor);\n\n  (void)(q_bar);\n  (void)(r_bar_squared);\n\n  return PrimitiveRecoveryData{\n      rest_mass_density,\n      lorentz_factor,\n      pressure,\n      specific_internal_energy,\n      rest_mass_density_times_lorentz_factor /\n          one_over_specific_enthalpy_times_lorentz_factor,\n      electron_fraction};\n}\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAlHydro.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n\n\n/// \\cond\nnamespace EquationsOfState {\ntemplate <bool, size_t>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace grmhd::ValenciaDivClean {\nclass PrimitiveFromConservativeOptions;\n}  // namespace grmhd::ValenciaDivClean\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\n\nstruct PrimitiveRecoveryData;\n\n/*!\n * \\brief Compute the primitive variables from the conservative variables using\n * the scheme of \\cite Galeazzi2013mia.\n *\n * In the notation of the Kastaun paper, `tau` is \\f$D q\\f$,\n * `momentum_density_squared` is \\f$r^2 D^2\\f$,\n * `rest_mass_density_times_lorentz_factor` is \\f$D\\f$.\n * Furthermore, the algorithm iterates over \\f$z\\f$, which is the Lorentz factor\n * times the absolute magnetitude of the velocity.\n *\n * In terms of the conservative variables (in our notation):\n * \\f{align*}\n * q = & \\frac{{\\tilde \\tau}}{{\\tilde D}} \\\\\n * r^2 = & \\frac{\\gamma^{kl} {\\tilde S}_k {\\tilde S}_l}{{\\tilde D}^2} \\\\\n * \\f}\n *\n * where the conserved variables \\f${\\tilde D}\\f$, \\f${\\tilde S}_i\\f$ and\n * \\f${\\tilde \\tau}\\f$ are a generalized mass-energy\n * density, momentum density and specific internal energy density,\n * and \\f$\\gamma\\f$ and \\f$\\gamma^{kl}\\f$ are the determinant and inverse\n * of the spatial metric \\f$\\gamma_{kl}\\f$.\n *\n * \\note This scheme does not use the initial guess for the pressure.\n */\nclass KastaunEtAlHydro {\n public:\n  template <bool EnforcePhysicality, typename EosType>\n  static std::optional<PrimitiveRecoveryData> apply(\n      double initial_guess_pressure, double tau,\n      double momentum_density_squared,\n      double momentum_density_dot_magnetic_field, double magnetic_field_squared,\n      double rest_mass_density_times_lorentz_factor, double electron_fraction,\n      const EosType& equation_of_state,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n\n  static const std::string name() { return \"KastaunEtAlHydro\"; }\n\n private:\n  static constexpr size_t max_iterations_ = 100;\n  static constexpr double absolute_tolerance_ =\n      10.0 * std::numeric_limits<double>::epsilon();\n  static constexpr double relative_tolerance_ =\n      10.0 * std::numeric_limits<double>::epsilon();\n};\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAlHydro.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAlHydro.hpp\"\n\n#include <cmath>\n#include <exception>\n#include <limits>\n#include <optional>\n#include <stdexcept>\n#include <utility>\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\n\nnamespace KastaunEtAlHydro_detail {\n\nstruct Primitives {\n  const double rest_mass_density;\n  const double lorentz_factor;\n  const double pressure;\n  const double specific_internal_energy;\n};\n\ntemplate <typename EosType, bool EnforcePhysicality>\nclass FunctionOfZ {\n public:\n  FunctionOfZ(const double tau, const double momentum_density_squared,\n              const double /* momentum_density_dot_magnetic_field */,\n              const double /* magnetic_field_squared */,\n              const double rest_mass_density_times_lorentz_factor,\n              const double electron_fraction, const EosType& equation_of_state,\n              const double lorentz_max)\n      : q_(tau / rest_mass_density_times_lorentz_factor),\n        r_(std::sqrt(momentum_density_squared /\n                     square(rest_mass_density_times_lorentz_factor))),\n        rest_mass_density_times_lorentz_factor_(\n            rest_mass_density_times_lorentz_factor),\n        electron_fraction_(electron_fraction),\n        equation_of_state_(equation_of_state) {\n    // Internal consistency check\n    //\n    const double rho_min =\n        rest_mass_density_times_lorentz_factor_ / lorentz_max;\n    const double v_0_squared = 1. - 1. / (lorentz_max * lorentz_max);\n    const double kmax = 2. * std::sqrt(v_0_squared) / (1. + v_0_squared);\n\n    double eps_min{};\n    if constexpr (EosType::thermodynamic_dim == 3) {\n      eps_min = equation_of_state_.specific_internal_energy_lower_bound(\n          rho_min, electron_fraction);\n    } else if constexpr (EosType::thermodynamic_dim == 2) {\n      eps_min =\n          equation_of_state_.specific_internal_energy_lower_bound(rho_min);\n    } else {\n      eps_min =\n          equation_of_state_.specific_internal_energy_lower_bound();\n    }\n\n    if constexpr (EnforcePhysicality) {\n      q_ = std::max(q_, eps_min);\n      r_ = std::min(r_, kmax * (q_ + 1.));\n    } else {\n      if ((q_ < eps_min) or (r_ > kmax * (q_ + 1.))) {\n        state_is_unphysical_ = true;\n      }\n    }\n  }\n\n  std::pair<double, double> root_bracket() const;\n\n  Primitives primitives(double z) const;\n\n  double operator()(double z) const;\n\n  bool has_no_root() const { return state_is_unphysical_; }\n\n private:\n  double q_;\n  double r_;\n  bool state_is_unphysical_ = false;\n  const double rest_mass_density_times_lorentz_factor_;\n  const double electron_fraction_;\n  const EosType& equation_of_state_;\n};\n\ntemplate <typename EosType, bool EnforcePhysicality>\nstd::pair<double, double>\nFunctionOfZ<EosType, EnforcePhysicality>::root_bracket() const {\n  const auto k = r_ / (q_ + 1.);\n\n  const double rho_min = equation_of_state_.rest_mass_density_lower_bound();\n\n  // If this is triggering, the most likely cause is that the density cutoff\n  // for atmosphere is smaller than the minizm density of the EOS, i.e. this\n  // point should have been flagged as atmosphere\n  if (rest_mass_density_times_lorentz_factor_ < rho_min) {\n    throw std::runtime_error(\"Density too small for EOS\");\n  }\n\n  // Compute bounds (C23)\n  const double lower_bound = 0.5 * k / std::sqrt(std::abs(1. - 0.25 * k * k));\n  // Ensure that upper_bound does not become degenerate when k ~ 0\n  // Empirically, an offset of 1.e-8 has worked well.\n  const double upper_bound = 1.e-8 + k / std::sqrt(std::abs(1. - k * k));\n\n  return {lower_bound, upper_bound};\n}\n\ntemplate <typename EosType, bool EnforcePhysicality>\nPrimitives FunctionOfZ<EosType, EnforcePhysicality>::primitives(\n    const double z) const {\n  // Compute Lorentz factor, note that z = lorentz * v\n  const double w_hat = std::sqrt(1. + z * z);\n\n  // Compute rest mass density, D/w_hat\n  const double rho_hat =\n      std::clamp(rest_mass_density_times_lorentz_factor_ / w_hat,\n                 equation_of_state_.rest_mass_density_lower_bound(),\n                 equation_of_state_.rest_mass_density_upper_bound());\n\n  // Equation (C14) and (C16)\n  double epsilon_hat = w_hat * q_ - z * (r_ - z / (1. + w_hat));\n  if constexpr (EosType::thermodynamic_dim == 3) {\n    epsilon_hat =\n        std::clamp(epsilon_hat,\n                   equation_of_state_.specific_internal_energy_lower_bound(\n                       rho_hat, electron_fraction_),\n                   equation_of_state_.specific_internal_energy_upper_bound(\n                       rho_hat, electron_fraction_));\n  } else if constexpr (EosType::thermodynamic_dim == 2) {\n    epsilon_hat = std::clamp(\n        epsilon_hat,\n        equation_of_state_.specific_internal_energy_lower_bound(rho_hat),\n        equation_of_state_.specific_internal_energy_upper_bound(rho_hat));\n  } else {\n    epsilon_hat = std::clamp(\n        epsilon_hat,\n        equation_of_state_.specific_internal_energy_lower_bound(),\n        equation_of_state_.specific_internal_energy_upper_bound());\n  }\n\n  // Pressure from EOS\n  double p_hat = std::numeric_limits<double>::signaling_NaN();\n  if constexpr (EosType::thermodynamic_dim == 1) {\n    // Note: we do not reset epsilon to satisfy the EOS because in that case\n    // we are not guaranteed to be able to find a solution. That's because\n    // the conserved-variable solution is inconsistent with the restrictive\n    // 1d-EOS. Instead, we recover the primitives and then reset the\n    // specific internal energy and specific enthalpy using the EOS.\n    p_hat =\n        get(equation_of_state_.pressure_from_density(Scalar<double>{rho_hat}));\n  } else if constexpr (EosType::thermodynamic_dim == 2) {\n    p_hat = get(equation_of_state_.pressure_from_density_and_energy(\n        Scalar<double>{rho_hat}, Scalar<double>{epsilon_hat}));\n  } else if constexpr (EosType::thermodynamic_dim == 3) {\n    p_hat = get(equation_of_state_.pressure_from_density_and_energy(\n        Scalar<double>{rho_hat}, Scalar<double>{epsilon_hat},\n        Scalar<double>{electron_fraction_}));\n  }\n  return Primitives{rho_hat, w_hat, p_hat, epsilon_hat};\n}\n\ntemplate <typename EosType, bool EnforcePhysicality>\ndouble FunctionOfZ<EosType, EnforcePhysicality>::operator()(\n    const double z) const {\n  const auto [rho_hat, w_hat, p_hat, epsilon_hat] = primitives(z);\n  // Equation (C5)\n  const double a_hat = p_hat / (rho_hat * (1.0 + epsilon_hat));\n  const double h_hat = (1.0 + epsilon_hat) * (1.0 + a_hat);\n\n  // Equations (C22)\n  return z - r_ / h_hat;\n}\n}  // namespace KastaunEtAlHydro_detail\n\ntemplate <bool EnforcePhysicality, typename EosType>\nstd::optional<PrimitiveRecoveryData> KastaunEtAlHydro::apply(\n    const double /*initial_guess_pressure*/, const double tau,\n    const double momentum_density_squared,\n    const double momentum_density_dot_magnetic_field,\n    const double magnetic_field_squared,\n    const double rest_mass_density_times_lorentz_factor,\n    const double electron_fraction, const EosType& equation_of_state,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options) {\n  // Master function see Equation (44)\n  const auto f_of_z =\n      KastaunEtAlHydro_detail::FunctionOfZ<EosType, EnforcePhysicality>{\n          tau,\n          momentum_density_squared,\n          momentum_density_dot_magnetic_field,\n          magnetic_field_squared,\n          rest_mass_density_times_lorentz_factor,\n          electron_fraction,\n          equation_of_state,\n          primitive_from_conservative_options.kastaun_max_lorentz_factor()};\n\n  if (f_of_z.has_no_root()) {\n    return std::nullopt;\n  };\n\n  // z is W * v  (Lorentz factor * velocity)\n  double z = std::numeric_limits<double>::signaling_NaN();\n  try {\n    // Bracket for master function\n    const auto [lower_bound, upper_bound] = f_of_z.root_bracket();\n\n    // Try to recover primitves\n    z =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(f_of_z, lower_bound, upper_bound,\n                            absolute_tolerance_, relative_tolerance_,\n                            max_iterations_);\n  } catch (std::exception& exception) {\n    return std::nullopt;\n  }\n\n  const auto [rest_mass_density, lorentz_factor, pressure,\n              specific_internal_energy] = f_of_z.primitives(z);\n\n  return PrimitiveRecoveryData{\n      rest_mass_density,\n      lorentz_factor,\n      pressure,\n      specific_internal_energy,\n      (rest_mass_density * (1. + specific_internal_energy) + pressure) *\n          square(lorentz_factor),\n      electron_fraction};\n}\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp\"\n\n/// \\cond\nnamespace EquationsOfState {\ntemplate <bool, size_t>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace grmhd::ValenciaDivClean {\nclass PrimitiveFromConservativeOptions;\n}  // namespace grmhd::ValenciaDivClean\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\n\n/*!\n * \\brief Compute the primitive variables from the conservative variables using\n * the scheme of [Newman and Hamlin, SIAM J. Sci. Comput., 36(4)\n * B661-B683 (2014)](https://epubs.siam.org/doi/10.1137/140956749).\n *\n * In the Newman and Hamlin paper, `tau` is \\f$e - \\rho W\\f$,\n * `momentum_density_squared` is\\f${\\cal M}^2\\f$,\n * `momentum_density_dot_magnetic_field` is \\f${\\cal T}\\f$,\n * `magnetic_field_squared` is \\f${\\cal B}^2\\f$, and\n * `rest_mass_density_times_lorentz_factor` is \\f${\\tilde \\rho}\\f$.\n * Furthermore, the returned `PrimitiveRecoveryData.rho_h_w_squared` is \\f${\\cal\n * L}\\f$.\n *\n * In terms of the conservative variables (in our notation):\n * \\f{align}\n * e = & \\frac{{\\tilde D} + {\\tilde \\tau}}{\\sqrt{\\gamma}} \\\\\n * {\\cal M}^2 = & \\frac{\\gamma^{mn} {\\tilde S}_m {\\tilde S}_n}{\\gamma} \\\\\n * {\\cal T} = & \\frac{{\\tilde B}^m {\\tilde S}_m}{\\gamma} \\\\\n * {\\cal B}^2 = & \\frac{\\gamma_{mn} {\\tilde B}^m {\\tilde B}^n}{\\gamma} \\\\\n * {\\tilde \\rho} = & \\frac{\\tilde D}{\\sqrt{\\gamma}}\n * \\f}\n *\n * where the conserved variables \\f${\\tilde D}\\f$, \\f${\\tilde S}_i\\f$,\n * \\f${\\tilde \\tau}\\f$, and \\f${\\tilde B}^i\\f$ are a generalized mass-energy\n * density, momentum density, specific internal energy density, and magnetic\n * field, and \\f$\\gamma\\f$ and \\f$\\gamma^{mn}\\f$ are the determinant and inverse\n * of the spatial metric \\f$\\gamma_{mn}\\f$.\n */\nclass NewmanHamlin {\n public:\n  template <bool EnforcePhysicality, typename EosType>\n  static std::optional<PrimitiveRecoveryData> apply(\n      double initial_guess_for_pressure, double tau,\n      double momentum_density_squared,\n      double momentum_density_dot_magnetic_field, double magnetic_field_squared,\n      double rest_mass_density_times_lorentz_factor, double electron_fraction,\n      const EosType& equation_of_state,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n\n  static const std::string name() { return \"Newman Hamlin\"; }\n\n private:\n  static constexpr size_t max_iterations_ = 50;\n  static constexpr double relative_tolerance_ = 1.e-10;\n};\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <limits>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\n\ntemplate <bool EnforcePhysicality, typename EosType>\nstd::optional<PrimitiveRecoveryData> NewmanHamlin::apply(\n    const double initial_guess_for_pressure, const double tau,\n    const double momentum_density_squared,\n    const double momentum_density_dot_magnetic_field,\n    const double magnetic_field_squared,\n    const double rest_mass_density_times_lorentz_factor,\n    const double electron_fraction, const EosType& equation_of_state,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n    /*primitive_from_conservative_options*/) {\n  const double total_energy_density =\n      tau + rest_mass_density_times_lorentz_factor;\n  // constant in cubic equation  f(eps) = eps^3 - a eps^2 + d\n  // whose root is being found at each point in the iteration below\n  const double d_in_cubic = [momentum_density_squared, magnetic_field_squared,\n                             momentum_density_dot_magnetic_field]() {\n    const double local_d_in_cubic =\n        0.5 * (momentum_density_squared * magnetic_field_squared -\n               square(momentum_density_dot_magnetic_field));\n    if (UNLIKELY(-1e-12 * square(momentum_density_dot_magnetic_field) >\n                 local_d_in_cubic)) {\n      return local_d_in_cubic;  // will fail returning std::nullopt\n    }\n    return std::max(0.0, local_d_in_cubic);\n  }();\n  if (UNLIKELY(0.0 > d_in_cubic)) {\n    return std::nullopt;\n  }\n\n  // bound needed so cubic equation has a positive root\n  const double minimum_pressure =\n      std::max(0.0, cbrt(6.75 * d_in_cubic) - total_energy_density -\n                        0.5 * magnetic_field_squared);\n  double current_pressure =\n      std::max(minimum_pressure, initial_guess_for_pressure);\n  double previous_pressure{std::numeric_limits<double>::signaling_NaN()};\n  size_t iteration_step{0};\n  std::array<double, 3> aitken_pressure{\n      {current_pressure, std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  size_t valid_entries_in_aitken_pressure = 1;\n  bool converged = false;\n\n  while (true) {  // will break when relative pressure change is <\n                  // relative_tolernance_\n    if (UNLIKELY(max_iterations_ == iteration_step and not converged)) {\n      return std::nullopt;\n    }\n\n    ++iteration_step;\n    previous_pressure = current_pressure;\n    // enforces NH Eq.(5.9): d <= (4/27) a^3 so cubic has positive root\n    current_pressure = std::max(current_pressure, minimum_pressure);\n    const double a_in_cubic =\n        total_energy_density + current_pressure + 0.5 * magnetic_field_squared;\n\n    if (UNLIKELY(a_in_cubic <= 0.0)) {\n      return std::nullopt;\n    }\n\n    // NH Eq. (5.10): d = (4/27) a^3 cos^2(phi)\n    const double phi = acos(sqrt(6.75 * d_in_cubic / cube(a_in_cubic)));\n    // NH Eq. (5.11) with l=1 is desired positive root\n    const double root_of_cubic =\n        (a_in_cubic / 3.0) * (1.0 - 2.0 * cos((2.0 / 3.0) * (M_PI + phi)));\n    // NH Eq. (5.5) with their script L being rho_h_w_squared\n    // where rho is rest_mass_density, h is specific_enthalpy,\n    // and w is the lorentz factor\n    const double rho_h_w_squared = root_of_cubic - magnetic_field_squared;\n\n    if (UNLIKELY(rho_h_w_squared <= 0.0)) {\n      return std::nullopt;\n    }\n\n    // NH Eq. (5.2) with (5.5) substituted in denominator\n    const double v_squared =\n        (momentum_density_squared * square(rho_h_w_squared) +\n         square(momentum_density_dot_magnetic_field) *\n             (magnetic_field_squared + 2.0 * rho_h_w_squared)) /\n        square(rho_h_w_squared * root_of_cubic);\n\n    // If this fails, there was code in the Bitbucket version that adjusted\n    // the pressure to get the maximum allowed velocity in atmosphere.\n    // Instead, we could return std::nullopt and try the next inversion method.\n    if (UNLIKELY(v_squared < 0.0 or v_squared >= 1.0)) {\n      return std::nullopt;\n    }\n\n    const double current_lorentz_factor = sqrt(1.0 / (1.0 - v_squared));\n    const double current_rest_mass_density =\n        rest_mass_density_times_lorentz_factor / current_lorentz_factor;\n\n    if (converged) {\n      double current_specific_internal_energy{};\n      if constexpr (EosType::thermodynamic_dim == 1) {\n        current_specific_internal_energy =\n            get(equation_of_state.specific_internal_energy_from_density(\n                Scalar<double>(current_rest_mass_density)));\n      } else if constexpr (EosType::thermodynamic_dim == 2) {\n        current_specific_internal_energy =\n            get(equation_of_state\n                    .specific_internal_energy_from_density_and_pressure(\n                        Scalar<double>(current_rest_mass_density),\n                        Scalar<double>(current_pressure)));\n      } else if constexpr (EosType::thermodynamic_dim == 3) {\n        current_specific_internal_energy =\n            (rho_h_w_squared / square(current_lorentz_factor) -\n             current_pressure) /\n                current_rest_mass_density -\n            1.0;\n      }\n      return PrimitiveRecoveryData{\n          current_rest_mass_density, current_lorentz_factor,\n          current_pressure,          current_specific_internal_energy,\n          rho_h_w_squared,           electron_fraction};\n    }\n\n    const double current_specific_enthalpy =\n        [rho_h_w_squared, current_rest_mass_density, current_lorentz_factor]() {\n          const double specific_enthalpy =\n              rho_h_w_squared /\n              (current_rest_mass_density * square(current_lorentz_factor));\n          if (UNLIKELY(1.0 - 1.0e-12 > specific_enthalpy)) {\n            return specific_enthalpy;  // will fail returning std::nullopt\n          }\n          return std::max(1.0, specific_enthalpy);\n        }();\n    if (UNLIKELY(1.0 > current_specific_enthalpy)) {\n      return std::nullopt;\n    }\n\n    if constexpr (EosType::thermodynamic_dim == 1) {\n      current_pressure = get(equation_of_state.pressure_from_density(\n          Scalar<double>(current_rest_mass_density)));\n    } else if constexpr (EosType::thermodynamic_dim == 2) {\n      current_pressure =\n          get(equation_of_state.pressure_from_density_and_enthalpy(\n              Scalar<double>(current_rest_mass_density),\n              Scalar<double>(current_specific_enthalpy)));\n      if (UNLIKELY(current_pressure <= 0.0)) {\n        return std::nullopt;\n      }\n    } else if constexpr (EosType::thermodynamic_dim == 3) {\n      const Scalar<double> local_internal_energy(\n          current_specific_enthalpy -\n          current_pressure / current_rest_mass_density - 1.0);\n      current_pressure = get(equation_of_state.pressure_from_density_and_energy(\n          Scalar<double>(current_rest_mass_density), local_internal_energy,\n          Scalar<double>(electron_fraction)));\n    }\n\n    gsl::at(aitken_pressure, valid_entries_in_aitken_pressure++) =\n        current_pressure;\n    if (3 == valid_entries_in_aitken_pressure) {\n      const double aitken_residual = (aitken_pressure[2] - aitken_pressure[1]) /\n                                     (aitken_pressure[1] - aitken_pressure[0]);\n      if (0.0 <= aitken_residual and aitken_residual < 1.0) {\n        previous_pressure = current_pressure;\n        current_pressure =\n            aitken_pressure[1] +\n            (aitken_pressure[2] - aitken_pressure[1]) / (1.0 - aitken_residual);\n        aitken_pressure = {{current_pressure,\n                            std::numeric_limits<double>::signaling_NaN(),\n                            std::numeric_limits<double>::signaling_NaN()}};\n        valid_entries_in_aitken_pressure = 1;\n      } else {\n        // Aitken extrapolation failed, retain latest 2 values for next attempt\n        aitken_pressure[0] = aitken_pressure[1];\n        aitken_pressure[1] = aitken_pressure[2];\n        valid_entries_in_aitken_pressure = 2;\n      }\n    }\n    // note primitives are recomputed above before being returned\n    converged = fabs(current_pressure - previous_pressure) <=\n                relative_tolerance_ * (current_pressure + previous_pressure);\n  }  // while loop\n}\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp\"\n\n/// \\cond\nnamespace EquationsOfState {\ntemplate <bool, size_t>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace grmhd::ValenciaDivClean {\nclass PrimitiveFromConservativeOptions;\n}  // namespace grmhd::ValenciaDivClean\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\n\n/*!\n * \\brief Compute the primitive variables from the conservative variables using\n * the scheme of [Palenzuela et al, Phys. Rev. D 92, 044045\n * (2015)](https://doi.org/10.1103/PhysRevD.92.044045).\n *\n * In the notation of the Palenzuela paper, `tau` is \\f$D q\\f$,\n * `momentum_density_squared` is \\f$r^2 D^2\\f$,\n * `momentum_density_dot_magnetic_field` is \\f$t D^{\\frac{3}{2}}\\f$,\n * `magnetic_field_squared` is \\f$s D\\f$, and\n * `rest_mass_density_times_lorentz_factor` is \\f$D\\f$.\n * Furthermore, the returned `PrimitiveRecoveryData.rho_h_w_squared` is \\f$x\n * D\\f$.  Note also that \\f$h\\f$ in the Palenzuela paper is the specific\n * enthalpy times the rest mass density.\n *\n * In terms of the conservative variables (in our notation):\n * \\f{align*}\n * q = & \\frac{{\\tilde \\tau}}{{\\tilde D}} \\\\\n * r = & \\frac{\\gamma^{mn} {\\tilde S}_m {\\tilde S}_n}{{\\tilde D}^2} \\\\\n * t^2 = & \\frac{({\\tilde B}^m {\\tilde S}_m)^2}{{\\tilde D}^3 \\sqrt{\\gamma}} \\\\\n * s = & \\frac{\\gamma_{mn} {\\tilde B}^m {\\tilde B}^n}{{\\tilde D}\\sqrt{\\gamma}}\n * \\f}\n *\n * where the conserved variables \\f${\\tilde D}\\f$, \\f${\\tilde S}_i\\f$,\n * \\f${\\tilde \\tau}\\f$, and \\f${\\tilde B}^i\\f$ are a generalized mass-energy\n * density, momentum density, specific internal energy density, and magnetic\n * field, and \\f$\\gamma\\f$ and \\f$\\gamma^{mn}\\f$ are the determinant and inverse\n * of the spatial metric \\f$\\gamma_{mn}\\f$.\n */\nclass PalenzuelaEtAl {\n public:\n  template <bool EnforcePhysicality, typename EosType>\n  static std::optional<PrimitiveRecoveryData> apply(\n      double /*initial_guess_pressure*/, double tau,\n      double momentum_density_squared,\n      double momentum_density_dot_magnetic_field, double magnetic_field_squared,\n      double rest_mass_density_times_lorentz_factor, double electron_fraction,\n      const EosType& equation_of_state,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n\n  static const std::string name() { return \"PalenzuelaEtAl\"; }\n\n private:\n  static constexpr size_t max_iterations_ = 100;\n  static constexpr double absolute_tolerance_ =\n      10.0 * std::numeric_limits<double>::epsilon();\n  static constexpr double relative_tolerance_ =\n      10.0 * std::numeric_limits<double>::epsilon();\n};\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n\n#include <cmath>\n#include <exception>\n#include <limits>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\n\nnamespace PalenzuelaEtAl_detail {\n\n// note q,r,s,t,x are defined in the documentation\ntemplate <typename EosType>\nclass FunctionOfX {\n public:\n  FunctionOfX(const double tau, const double momentum_density_squared,\n              const double momentum_density_dot_magnetic_field,\n              const double magnetic_field_squared,\n              const double rest_mass_density_times_lorentz_factor,\n              const double electron_fraction, const EosType& equation_of_state)\n      : q_(tau / rest_mass_density_times_lorentz_factor),\n        r_(momentum_density_squared /\n           square(rest_mass_density_times_lorentz_factor)),\n        s_(magnetic_field_squared / rest_mass_density_times_lorentz_factor),\n        t_squared_(square(momentum_density_dot_magnetic_field) /\n                   cube(rest_mass_density_times_lorentz_factor)),\n        rest_mass_density_times_lorentz_factor_(\n            rest_mass_density_times_lorentz_factor),\n        electron_fraction_(electron_fraction),\n        equation_of_state_(equation_of_state) {}\n\n  double lorentz_factor(const double x) const {\n    static constexpr double v_maximum = 1.0 - 1.e-12;\n    // Clamp v^2 to physical values.  This is needed because the bounds on\n    // x used for the root solve do not guarantee a physical velocity.  Some\n    // work would be needed to investigate whether better bounds could guarantee\n    // a physical velocity.\n    const double v_squared = std::clamp(\n        (square(x) * r_ + (2 * x + s_) * t_squared_) / square(x * (x + s_)),\n        0.0, square(v_maximum));\n    return 1.0 / sqrt(1.0 - v_squared);\n  }\n\n  double specific_internal_energy(const double x,\n                                  const double lorentz_factor) const {\n    return lorentz_factor - 1.0 +\n           x * (1.0 - square(lorentz_factor)) / lorentz_factor +\n           lorentz_factor * (q_ - s_ + 0.5 * t_squared_ / square(x) +\n                             0.5 * s_ / square(lorentz_factor));\n  }\n\n  double operator()(const double x) const {\n    const double current_lorentz_factor = lorentz_factor(x);\n    const double current_rest_mass_density =\n        rest_mass_density_times_lorentz_factor_ / current_lorentz_factor;\n    const double current_specific_internal_energy =\n        specific_internal_energy(x, current_lorentz_factor);\n    double current_pressure = std::numeric_limits<double>::signaling_NaN();\n    if constexpr (EosType::thermodynamic_dim == 1) {\n      current_pressure = get(equation_of_state_.pressure_from_density(\n          Scalar<double>(current_rest_mass_density)));\n    } else if constexpr (EosType::thermodynamic_dim == 2) {\n      current_pressure =\n          get(equation_of_state_.pressure_from_density_and_energy(\n              Scalar<double>(current_rest_mass_density),\n              Scalar<double>(current_specific_internal_energy)));\n    } else if constexpr (EosType::thermodynamic_dim == 3) {\n      current_pressure =\n          get(equation_of_state_.pressure_from_density_and_energy(\n              Scalar<double>(current_rest_mass_density),\n              Scalar<double>(current_specific_internal_energy),\n              Scalar<double>(electron_fraction_)));\n    }\n\n    return x - (1.0 + current_specific_internal_energy +\n                current_pressure / current_rest_mass_density) *\n                   current_lorentz_factor;\n  }\n\n private:\n  const double q_;\n  const double r_;\n  const double s_;\n  const double t_squared_;\n  const double rest_mass_density_times_lorentz_factor_;\n  const double electron_fraction_;\n  const EosType& equation_of_state_;\n};\n}  // namespace PalenzuelaEtAl_detail\n\ntemplate <bool EnforcePhysicality, typename EosType>\nstd::optional<PrimitiveRecoveryData> PalenzuelaEtAl::apply(\n    const double /*initial_guess_pressure*/, const double tau,\n    const double momentum_density_squared,\n    const double momentum_density_dot_magnetic_field,\n    const double magnetic_field_squared,\n    const double rest_mass_density_times_lorentz_factor,\n    const double electron_fraction, const EosType& equation_of_state,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n    /*primitive_from_conservative_options*/) {\n  const double lower_bound =\n      (tau - magnetic_field_squared) / rest_mass_density_times_lorentz_factor +\n      1.0;\n  const double upper_bound = (2.0 * tau - magnetic_field_squared) /\n                                 rest_mass_density_times_lorentz_factor +\n                             2.0;\n  const auto f_of_x = PalenzuelaEtAl_detail::FunctionOfX<EosType>{\n      tau,\n      momentum_density_squared,\n      momentum_density_dot_magnetic_field,\n      magnetic_field_squared,\n      rest_mass_density_times_lorentz_factor,\n      electron_fraction,\n      equation_of_state};\n  double specific_enthalpy_times_lorentz_factor =\n      std::numeric_limits<double>::signaling_NaN();\n  try {\n    specific_enthalpy_times_lorentz_factor =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(f_of_x, lower_bound, upper_bound,\n                            absolute_tolerance_, relative_tolerance_,\n                            max_iterations_);\n  } catch (std::exception& exception) {\n    return std::nullopt;\n  }\n  const double lorentz_factor =\n      f_of_x.lorentz_factor(specific_enthalpy_times_lorentz_factor);\n  const double rest_mass_density =\n      rest_mass_density_times_lorentz_factor / lorentz_factor;\n  double pressure = std::numeric_limits<double>::signaling_NaN();\n  double specific_internal_energy = f_of_x.specific_internal_energy(\n      specific_enthalpy_times_lorentz_factor, lorentz_factor);\n  if constexpr (EosType::thermodynamic_dim == 1) {\n    pressure = get(equation_of_state.pressure_from_density(\n        Scalar<double>(rest_mass_density)));\n    specific_internal_energy =\n        get(equation_of_state.specific_internal_energy_from_density(\n            Scalar<double>(rest_mass_density)));\n  } else if constexpr (EosType::thermodynamic_dim == 2) {\n    pressure = get(equation_of_state.pressure_from_density_and_energy(\n        Scalar<double>(rest_mass_density),\n        Scalar<double>(specific_internal_energy)));\n  } else if constexpr (EosType::thermodynamic_dim == 3) {\n    pressure = get(equation_of_state.pressure_from_density_and_energy(\n        Scalar<double>(rest_mass_density),\n        Scalar<double>(specific_internal_energy),\n        Scalar<double>(electron_fraction)));\n  }\n\n  return PrimitiveRecoveryData{rest_mass_density,\n                               lorentz_factor,\n                               pressure,\n                               specific_internal_energy,\n                               specific_enthalpy_times_lorentz_factor *\n                                   rest_mass_density_times_lorentz_factor,\n                               electron_fraction};\n}\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n\n#include <iomanip>\n#include <limits>\n#include <optional>\n#include <ostream>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.tpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAlHydro.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAlHydro.tpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.tpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.tpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\ntemplate <typename OrderedListOfPrimitiveRecoverySchemes, bool ErrorOnFailure>\ntemplate <bool EnforcePhysicality>\nbool PrimitiveFromConservative<OrderedListOfPrimitiveRecoverySchemes,\n                               ErrorOnFailure>::\n    apply(const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n          const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n          const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n          const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n              spatial_velocity,\n          const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n              magnetic_field,\n          const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n          const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n          const gsl::not_null<Scalar<DataVector>*> pressure,\n          const gsl::not_null<Scalar<DataVector>*> temperature,\n          const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n          const Scalar<DataVector>& tilde_tau,\n          const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n          const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n          const Scalar<DataVector>& tilde_phi,\n          const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n          const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n          const Scalar<DataVector>& sqrt_det_spatial_metric,\n          const EquationsOfState::EquationOfState<true, 3>& equation_of_state,\n          const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n              primitive_from_conservative_options) {\n  return call_with_dynamic_type<\n      bool, typename EquationsOfState::detail::DerivedClasses<true, 3>::type>(\n      &equation_of_state, [&](const auto* const derived_eos) {\n        return impl<EnforcePhysicality>(\n            rest_mass_density, electron_fraction, specific_internal_energy,\n            spatial_velocity, magnetic_field, divergence_cleaning_field,\n            lorentz_factor, pressure, temperature, tilde_d, tilde_ye, tilde_tau,\n            tilde_s, tilde_b, tilde_phi, spatial_metric, inv_spatial_metric,\n            sqrt_det_spatial_metric, *derived_eos,\n            primitive_from_conservative_options);\n      });\n}\n\n// If EnforceBarotropic then we assume the EOS is barotropic and enforce that\n// condition.\ntemplate <typename OrderedListOfPrimitiveRecoverySchemes, bool ErrorOnFailure>\ntemplate <bool EnforcePhysicality, typename EosType>\nbool PrimitiveFromConservative<OrderedListOfPrimitiveRecoverySchemes,\n                               ErrorOnFailure>::\n    impl(const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n         const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n         const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n         const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n             spatial_velocity,\n         const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n             magnetic_field,\n         const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n         const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n         const gsl::not_null<Scalar<DataVector>*> pressure,\n         const gsl::not_null<Scalar<DataVector>*> temperature,\n         const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n         const Scalar<DataVector>& tilde_tau,\n         const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n         const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n         const Scalar<DataVector>& tilde_phi,\n         const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n         const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n         const Scalar<DataVector>& sqrt_det_spatial_metric,\n         const EosType& equation_of_state,\n         const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n             primitive_from_conservative_options) {\n  static_assert(EosType::thermodynamic_dim == 3);\n  static_assert(EosType::is_relativistic);\n  constexpr bool eos_is_barotropic =\n      tt::is_a_v<EquationsOfState::Barotropic3D, EosType>;\n\n  get(*divergence_cleaning_field) =\n      get(tilde_phi) / get(sqrt_det_spatial_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    magnetic_field->get(i) = tilde_b.get(i) / get(sqrt_det_spatial_metric);\n  }\n\n  // Parameters for quick exit from inversion\n  const double cutoffD =\n      primitive_from_conservative_options.cutoff_d_for_inversion();\n  const double floorD =\n      primitive_from_conservative_options.density_when_skipping_inversion();\n\n  // If the max over the grid is below the cutoff, then just don't do any\n  // work because everything will get reset to atmosphere.\n  if (max(get(tilde_d)) < cutoffD) {\n    get(*rest_mass_density) = floorD;\n    get(*electron_fraction) =\n        min(equation_of_state.electron_fraction_upper_bound(),\n            max(get(tilde_ye) / get(tilde_d),\n                equation_of_state.electron_fraction_lower_bound()));\n    if constexpr (eos_is_barotropic) {\n      // Note: default construction for T and Y_e must be okay since the EOS is\n      // barotropic.\n      *specific_internal_energy =\n          equation_of_state\n              .specific_internal_energy_from_density_and_temperature(\n                  *rest_mass_density, Scalar<DataVector>{},\n                  Scalar<DataVector>{});\n    } else {\n      const auto num_points = get(*rest_mass_density).size();\n      for (size_t i = 0; i < num_points; ++i) {\n        get(*specific_internal_energy)[i] =\n            equation_of_state.specific_internal_energy_lower_bound(\n                get(*rest_mass_density)[i], get(*electron_fraction)[i]);\n      }\n    }\n    *temperature = equation_of_state.temperature_from_density_and_energy(\n        *rest_mass_density, *specific_internal_energy, *electron_fraction);\n    *pressure = equation_of_state.pressure_from_density_and_temperature(\n        *rest_mass_density, *temperature, *electron_fraction);\n    get(*lorentz_factor) = 1.0;\n    for (size_t i = 0; i < 3; ++i) {\n      spatial_velocity->get(i) = 0.0;\n    }\n  }\n\n  const size_t number_of_points = get<0>(tilde_b).size();\n  Variables<\n      tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                 ::Tags::TempScalar<2>, ::Tags::TempScalar<3>,\n                 ::Tags::TempScalar<4>, ::Tags::TempI<5, 3, Frame::Inertial>>>\n      temp_buffer(number_of_points);\n\n  DataVector& tau = get(get<::Tags::TempScalar<0>>(temp_buffer));\n  tau = get(tilde_tau) / get(sqrt_det_spatial_metric);\n\n  tnsr::I<DataVector, 3, Frame::Inertial>& tilde_s_upper =\n      get<::Tags::TempI<5, 3, Frame::Inertial>>(temp_buffer);\n  raise_or_lower_index(make_not_null(&tilde_s_upper), tilde_s,\n                       inv_spatial_metric);\n\n  Scalar<DataVector>& momentum_density_squared =\n      get<::Tags::TempScalar<1>>(temp_buffer);\n  dot_product(make_not_null(&momentum_density_squared), tilde_s, tilde_s_upper);\n  get(momentum_density_squared) /= square(get(sqrt_det_spatial_metric));\n\n  Scalar<DataVector>& momentum_density_dot_magnetic_field =\n      get<::Tags::TempScalar<2>>(temp_buffer);\n  dot_product(make_not_null(&momentum_density_dot_magnetic_field), tilde_s,\n              *magnetic_field);\n  get(momentum_density_dot_magnetic_field) /= get(sqrt_det_spatial_metric);\n\n  Scalar<DataVector>& magnetic_field_squared =\n      get<::Tags::TempScalar<3>>(temp_buffer);\n  dot_product(make_not_null(&magnetic_field_squared), *magnetic_field,\n              *magnetic_field, spatial_metric);\n\n  DataVector& rest_mass_density_times_lorentz_factor =\n      get(get<::Tags::TempScalar<4>>(temp_buffer));\n  rest_mass_density_times_lorentz_factor =\n      get(tilde_d) / get(sqrt_det_spatial_metric);\n\n  for (size_t s = 0; s < number_of_points; ++s) {\n    get(*electron_fraction)[s] =\n        std::min(equation_of_state.electron_fraction_upper_bound(),\n            std::max(get(tilde_ye)[s] / get(tilde_d)[s],\n                equation_of_state.electron_fraction_lower_bound()));\n\n    std::optional<PrimitiveRecoverySchemes::PrimitiveRecoveryData>\n        primitive_data = std::nullopt;\n    // Quick exit from inversion in low-density regions where we will\n    // apply atmosphere corrections anyways.\n    if (rest_mass_density_times_lorentz_factor[s] < cutoffD) {\n      double specific_energy_at_point =\n          equation_of_state.specific_internal_energy_lower_bound(\n              floorD, get(*electron_fraction)[s]);\n      if constexpr (eos_is_barotropic) {\n        specific_energy_at_point =\n            get(equation_of_state\n                    .specific_internal_energy_from_density_and_temperature(\n                        Scalar<double>{floorD}, Scalar<double>{0.0},\n                        Scalar<double>{0.1}));\n      }\n      const double pressure_at_point =\n          get(equation_of_state.pressure_from_density_and_energy(\n              Scalar<double>{floorD}, Scalar<double>{specific_energy_at_point},\n              Scalar<double>{get(*electron_fraction)[s]}));\n      const double enthalpy_density_at_point =\n          floorD + specific_energy_at_point * floorD + pressure_at_point;\n      primitive_data = PrimitiveRecoverySchemes::PrimitiveRecoveryData{\n          floorD,\n          1.0,\n          pressure_at_point,\n          specific_energy_at_point,\n          enthalpy_density_at_point,\n          get(*electron_fraction)[s]};\n    } else {\n      // not in atmosphere.\n      auto apply_scheme = [&pressure, &primitive_data, &tau,\n                           &momentum_density_squared,\n                           &momentum_density_dot_magnetic_field,\n                           &magnetic_field_squared,\n                           &rest_mass_density_times_lorentz_factor,\n                           &equation_of_state, &s, &electron_fraction,\n                           &primitive_from_conservative_options](auto scheme) {\n        using primitive_recovery_scheme = tmpl::type_from<decltype(scheme)>;\n        if (not primitive_data.has_value()) {\n          primitive_data =\n              primitive_recovery_scheme::template apply<EnforcePhysicality>(\n                  get(*pressure)[s], tau[s], get(momentum_density_squared)[s],\n                  get(momentum_density_dot_magnetic_field)[s],\n                  get(magnetic_field_squared)[s],\n                  rest_mass_density_times_lorentz_factor[s],\n                  get(*electron_fraction)[s], equation_of_state,\n                  primitive_from_conservative_options);\n        }\n      };\n      // Check consistency\n      if (use_hydro_optimization and\n          (get(magnetic_field_squared)[s] <\n           100.0 * std::numeric_limits<double>::epsilon() * tau[s])) {\n        tmpl::for_each<\n            tmpl::list<grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::\n                           KastaunEtAlHydro>>(apply_scheme);\n      } else {\n        tmpl::for_each<OrderedListOfPrimitiveRecoverySchemes>(apply_scheme);\n      }\n    }\n\n    if (primitive_data.has_value()) {\n      get(*rest_mass_density)[s] = primitive_data.value().rest_mass_density;\n      // if we set the Lorentz factor to 1.0 in atmosphere, then\n      // we also need to set the spatial_velocity accordingly.\n      if (primitive_data.value().lorentz_factor == 1.0) {\n        for (size_t i = 0; i < 3; ++i) {\n          spatial_velocity->get(i)[s] = 0.0;\n        }\n      } else {\n        const double coefficient_of_b =\n            get(momentum_density_dot_magnetic_field)[s] /\n            (primitive_data.value().rho_h_w_squared *\n             (primitive_data.value().rho_h_w_squared +\n              get(magnetic_field_squared)[s]));\n        const double coefficient_of_s =\n            1.0 / (get(sqrt_det_spatial_metric)[s] *\n                   (primitive_data.value().rho_h_w_squared +\n                    get(magnetic_field_squared)[s]));\n        for (size_t i = 0; i < 3; ++i) {\n          spatial_velocity->get(i)[s] =\n              coefficient_of_b * magnetic_field->get(i)[s] +\n              coefficient_of_s * tilde_s_upper.get(i)[s];\n        }\n      }\n      get(*lorentz_factor)[s] = primitive_data.value().lorentz_factor;\n      get(*pressure)[s] = primitive_data.value().pressure;\n      if constexpr (not eos_is_barotropic) {\n        get(*specific_internal_energy)[s] =\n            primitive_data.value().specific_internal_energy;\n      }\n    } else {\n      if constexpr (ErrorOnFailure) {\n        ERROR(\"All primitive inversion schemes failed at s = \"\n              << s << \".\\n\"\n              << std::setprecision(17) << \"tau = \" << tau[s] << \"\\n\"\n              << \"rest_mass_density_times_lorentz_factor = \"\n              << rest_mass_density_times_lorentz_factor[s] << \"\\n\"\n              << \"momentum_density_squared = \"\n              << get(momentum_density_squared)[s] << \"\\n\"\n              << \"momentum_density_dot_magnetic_field = \"\n              << get(momentum_density_dot_magnetic_field)[s] << \"\\n\"\n              << \"magnetic_field_squared = \" << get(magnetic_field_squared)[s]\n              << \"\\n\"\n              << \"rest_mass_density_times_lorentz_factor = \"\n              << rest_mass_density_times_lorentz_factor[s] << \"\\n\"\n              << \"previous_rest_mass_density = \" << get(*rest_mass_density)[s]\n              << \"\\n\"\n              << \"previous_pressure = \" << get(*pressure)[s] << \"\\n\"\n              << \"previous_lorentz_factor = \" << get(*lorentz_factor)[s]\n              << \"\\n\");\n      } else {\n        return false;\n      }\n    }\n  }\n  if constexpr (eos_is_barotropic) {\n    // Since the primitive recovery scheme is not restricted to lie on the\n    // EOS-satisfying sub-manifold, we project back to the sub-manifold by\n    // recomputing the specific internal energy from the EOS.\n    //\n    // Note: default construction for T and Y_e must be okay since the EOS is\n    // barotropic.\n    *specific_internal_energy =\n        equation_of_state.specific_internal_energy_from_density_and_temperature(\n            *rest_mass_density, Scalar<DataVector>{}, Scalar<DataVector>{});\n  }\n  *temperature = equation_of_state.temperature_from_density_and_energy(\n      *rest_mass_density, *specific_internal_energy, *electron_fraction);\n  return true;\n}\n}  // namespace grmhd::ValenciaDivClean\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define ERROR_ON_FAILURE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(_, data)                                        \\\n  template struct grmhd::ValenciaDivClean::PrimitiveFromConservative< \\\n      RECOVERY(data), ERROR_ON_FAILURE(data)>;\n\nusing NewmanHamlinThenPalenzuelaEtAl = tmpl::list<\n    grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n    grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\nusing KastaunThenNewmanThenPalenzuela = tmpl::list<\n    grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n    grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n    grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION,\n    (tmpl::list<\n         grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>,\n     tmpl::list<\n         grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n     NewmanHamlinThenPalenzuelaEtAl),\n    (true, false))\n\n#undef INSTANTIATION\n\n#define PHYSICALITY(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATION(_, data)                                                 \\\n  template bool grmhd::ValenciaDivClean::PrimitiveFromConservative<            \\\n      RECOVERY(data), ERROR_ON_FAILURE(data)>::                                \\\n      apply<PHYSICALITY(data)>(                                                \\\n          const gsl::not_null<Scalar<DataVector>*> rest_mass_density,          \\\n          const gsl::not_null<Scalar<DataVector>*> electron_fraction,          \\\n          const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,   \\\n          const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>        \\\n              spatial_velocity,                                                \\\n          const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>        \\\n              magnetic_field,                                                  \\\n          const gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,  \\\n          const gsl::not_null<Scalar<DataVector>*> lorentz_factor,             \\\n          const gsl::not_null<Scalar<DataVector>*> pressure,                   \\\n          const gsl::not_null<Scalar<DataVector>*> temperature,                \\\n          const Scalar<DataVector>& tilde_d,                                   \\\n          const Scalar<DataVector>& tilde_ye,                                  \\\n          const Scalar<DataVector>& tilde_tau,                                 \\\n          const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,              \\\n          const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,              \\\n          const Scalar<DataVector>& tilde_phi,                                 \\\n          const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,      \\\n          const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,  \\\n          const Scalar<DataVector>& sqrt_det_spatial_metric,                   \\\n          const EquationsOfState::EquationOfState<true, 3>& equation_of_state, \\\n          const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&     \\\n              primitive_from_conservative_options);\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION,\n    (tmpl::list<grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>,\n     tmpl::list<\n         grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAlHydro>,\n     tmpl::list<\n         grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>,\n     tmpl::list<\n         grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n     NewmanHamlinThenPalenzuelaEtAl, KastaunThenNewmanThenPalenzuela),\n    (true, false), (true, false))\n\n#undef INSTANTIATION\n#undef THERMODIM\n#undef PHYSICALITY\n#undef RECOVERY\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\n/*!\n * \\brief Compute the primitive variables from the conservative variables\n *\n * For the Valencia formulation of the GRMHD system with divergence cleaning,\n * the conversion of the evolved conserved variables to the primitive variables\n * cannot be expressed in closed analytic form and requires a root find.\n *\n * [Siegel {\\em et al}, The Astrophysical Journal 859:71(2018)]\n * (http://iopscience.iop.org/article/10.3847/1538-4357/aabcc5/meta)\n * compares several inversion methods.\n *\n * If `ErrorOnFailure` is `false` then the returned `bool` will be `false` if\n * recovery failed and `true` if it succeeded.\n *\n * If `EnforcePhysicality` is `false` then the hydrodynamic inversion will\n * return with an error if the input conservatives are unphysical, i.e., if no\n * solution exits. The exact behavior is governed by `ErrorOnFailure`.\n *\n * \\note\n * Here, we outline the algebra for computing the spatial velocity\n * \\f$ v^i \\f$:\n *\n * We start from definition of the momentum density \\f$ S_i \\f$:\n * \\f[\n * \\begin{aligned}\n * S_i &= (\\rho h)^* W^2 v_i - \\alpha b^0 b_i \\\\\n *     &= \\left(\\rho h + \\frac{B^2}{W^2} + (v \\cdot B)^2\\right) W^2 v_i\n *        - \\alpha \\left(\\frac{W}{\\alpha}(v \\cdot B)\\right)\n *        \\left(\\frac{B_i}{W} + (v \\cdot B) W v_i\\right) \\\\\n *     &= \\left(\\rho h + \\frac{B^2}{W^2}\\right) W^2 v_i - (v \\cdot B) B_i\n * \\end{aligned}\n * \\f]\n *\n * From the above, we obtain:\n * \\f[\n * \\begin{aligned}\n * S^i &= \\left(\\rho h + \\frac{B^2}{W^2}\\right) W^2 v^i - (v \\cdot B) B^i \\\\\n * S \\cdot B &= \\left(\\rho h + \\frac{B^2}{W^2}\\right) W^2 (v \\cdot B)\n *              - (v \\cdot B) B^2 \\\\\n *          &= \\rho h W^2 (v \\cdot B) \\\\\n * v^i &= \\frac{1}{\\sqrt{\\gamma} (\\rho h W^2 + B^2)} \\tilde{S}^i\n *       + \\frac{S \\cdot B}{\\rho h W^2 (\\rho h W^2 + B^2)} B^i\n * \\end{aligned}\n * \\f]\n */\ntemplate <typename OrderedListOfPrimitiveRecoverySchemes,\n          bool ErrorOnFailure = true>\nstruct PrimitiveFromConservative {\n  using return_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n\n  using argument_tags = tmpl::list<\n      grmhd::ValenciaDivClean::Tags::TildeD,\n      grmhd::ValenciaDivClean::Tags::TildeYe,\n      grmhd::ValenciaDivClean::Tags::TildeTau,\n      grmhd::ValenciaDivClean::Tags::TildeS<>,\n      grmhd::ValenciaDivClean::Tags::TildeB<>,\n      grmhd::ValenciaDivClean::Tags::TildePhi,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  template <bool EnforcePhysicality = true>\n  static bool apply(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n      gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const EquationsOfState::EquationOfState<true, 3>& equation_of_state,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n\n private:\n  template <bool EnforcePhysicality, typename EosType>\n  static bool impl(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> magnetic_field,\n      gsl::not_null<Scalar<DataVector>*> divergence_cleaning_field,\n      gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const EosType& equation_of_state,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n\n  // Use Kastaun hydro inversion if B is dynamically unimportant\n  static constexpr bool use_hydro_optimization = true;\n};\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\n\nPrimitiveFromConservativeOptions::PrimitiveFromConservativeOptions(\n    const double cutoff_d_for_inversion,\n    const double density_when_skipping_inversion,\n    const double kastaun_max_lorentz_factor, const Options::Context& context)\n    : cutoff_d_for_inversion_(cutoff_d_for_inversion),\n      density_when_skipping_inversion_(density_when_skipping_inversion),\n      kastaun_max_lorentz_factor_(kastaun_max_lorentz_factor) {\n  using std::sqrt;\n  if (kastaun_max_lorentz_factor_ > sqrt(std::numeric_limits<double>::max())) {\n    PARSE_ERROR(context, \"The Kastaun max lorentz factor must be smaller than \"\n                             << sqrt(std::numeric_limits<double>::max())\n                             << \" but is \" << kastaun_max_lorentz_factor_);\n  }\n}\n\nvoid PrimitiveFromConservativeOptions::pup(PUP::er& p) {\n  p | cutoff_d_for_inversion_;\n  p | density_when_skipping_inversion_;\n  p | kastaun_max_lorentz_factor_;\n}\n\nbool operator==(const PrimitiveFromConservativeOptions& lhs,\n                const PrimitiveFromConservativeOptions& rhs) {\n  return (lhs.cutoff_d_for_inversion_ == rhs.cutoff_d_for_inversion_) and\n         (lhs.density_when_skipping_inversion_ ==\n          rhs.density_when_skipping_inversion_) and\n         lhs.kastaun_max_lorentz_factor_ == rhs.kastaun_max_lorentz_factor_;\n}\n\nbool operator!=(const PrimitiveFromConservativeOptions& lhs,\n                const PrimitiveFromConservativeOptions& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <vector>\n\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean {\n\n/// Options to be passed to the Con2Prim algorithm.\n/// Currently, we simply set a threshold for tildeD\n/// below which the inversion is not performed and\n/// the density is set to atmosphere values.\nclass PrimitiveFromConservativeOptions {\n public:\n  struct CutoffDForInversion {\n    static std::string name() { return \"CutoffDForInversion\"; }\n    static constexpr Options::String help{\n        \"Value of density times Lorentz factor below which we skip \"\n        \"conservative to primitive inversion.\"};\n    using type = double;\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct DensityWhenSkippingInversion {\n    static std::string name() { return \"DensityWhenSkippingInversion\"; }\n    static constexpr Options::String help{\n        \"Value of density when we skip conservative to primitive inversion.\"};\n    using type = double;\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct KastaunMaxLorentzFactor {\n    static constexpr Options::String help{\n        \"The maximum Lorentz allowed during primitive recovery when using the \"\n        \"Kastaun schemes.\"};\n    using type = double;\n    static type lower_bound() { return 1.0; }\n  };\n\n  using options = tmpl::list<CutoffDForInversion, DensityWhenSkippingInversion,\n                             KastaunMaxLorentzFactor>;\n\n  static constexpr Options::String help{\n      \"Options given to conservative to primitive inversion.\"};\n\n  PrimitiveFromConservativeOptions() = default;\n\n  PrimitiveFromConservativeOptions(double cutoff_d_for_inversion,\n                                   double density_when_skipping_inversion,\n                                   double kastaun_max_lorentz_factor,\n                                   const Options::Context& context = {});\n\n  void pup(PUP::er& p);\n\n  double cutoff_d_for_inversion() const { return cutoff_d_for_inversion_; }\n  double density_when_skipping_inversion() const {\n    return density_when_skipping_inversion_;\n  }\n  double kastaun_max_lorentz_factor() const {\n    return kastaun_max_lorentz_factor_;\n  }\n\n private:\n  friend bool operator==(const PrimitiveFromConservativeOptions& lhs,\n                         const PrimitiveFromConservativeOptions& rhs);\n\n  double cutoff_d_for_inversion_ = std::numeric_limits<double>::signaling_NaN();\n  double density_when_skipping_inversion_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double kastaun_max_lorentz_factor_ =\n      std::numeric_limits<double>::signaling_NaN();\n};\n\nbool operator!=(const PrimitiveFromConservativeOptions& lhs,\n                const PrimitiveFromConservativeOptions& rhs);\n\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveRecoveryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\n\n/// \\brief Schemes for recovering primitive variables from conservative\n/// variables.\nnamespace PrimitiveRecoverySchemes {\n\n/*!\n * \\brief Data determined by PrimitiveRecoverySchemes at a single grid point.\n *\n * `rho_h_w_squared` is \\f$\\rho h W^2\\f$ where \\f$\\rho\\f$ is the rest mass\n * density, \\f$h\\f$ is the specific enthalpy, and \\f$W\\f$ is the Lorentz factor.\n */\nstruct PrimitiveRecoveryData {\n  double rest_mass_density;\n  double lorentz_factor;\n  double pressure;\n  double specific_internal_energy;\n  double rho_h_w_squared;\n  double electron_fraction;\n};\n}  // namespace PrimitiveRecoverySchemes\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/QuadrupoleFormula.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/QuadrupoleFormula.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::Tags {\n/// \\brief Compute tag for the quadrupole moment.\n///\n/// Compute item for the quadrupole moment, using\n/// \\f$\\tilde{D} x^i x^j\\f$ (equation 21 of \\cite Shibata2003),\n/// with \\f$\\tilde{D}=\\sqrt{\\gamma}\\rho W\\f$, $W$ being the Lorentz factor,\n/// $\\gamma$ being the determinant of the spatial metric, and\n/// \\f$x\\f$ the coordinates in Frame Fr.\ntemplate <typename DataType, size_t Dim, typename OutputCoordsTag,\n          typename Fr = Frame::Inertial>\nstruct QuadrupoleMomentCompute\n    : hydro::Tags::QuadrupoleMoment<DataType, Dim, Fr>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<TildeD, OutputCoordsTag>;\n\n  using base = hydro::Tags::QuadrupoleMoment<DataType, Dim, Fr>;\n  using return_type = typename base::type;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::ii<DataType, Dim, Fr>*> result,\n      const Scalar<DataType>& tilde_d,\n      const tnsr::I<DataType, Dim, Fr>& coordinates)>(\n      &hydro::quadrupole_moment<DataType, Dim, Fr>);\n};\n\n/// \\brief Compute tag for the first time derivative of the quadrupole moment.\n///\n/// Compute item for the first time derivative of the quadrupole moment, using\n/// \\f$\\tilde{D} (v^i x^j + x^i v^j)\\f$ (equation 23 of \\cite Shibata2003),\n/// with \\f$\\tilde{D}=\\sqrt{\\gamma}\\rho W\\f$, $W$ being the Lorentz factor,\n/// $\\gamma$ being the determinant of the spatial metric, \\f$x\\f$ the\n/// coordinates in Frame Fr, and \\f$v\\f$ the corresponding spatial velocity.\ntemplate <typename DataType, size_t Dim, typename OutputCoordsTag,\n          typename VelocityTag, typename Fr = Frame::Inertial>\nstruct QuadrupoleMomentDerivativeCompute\n    : hydro::Tags::QuadrupoleMomentDerivative<DataType, Dim, Fr>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<TildeD, OutputCoordsTag, VelocityTag>;\n\n  using base = hydro::Tags::QuadrupoleMomentDerivative<DataType, Dim, Fr>;\n  using return_type = typename base::type;\n\n  static std::string name() {\n    return \"QuadrupoleMomentDerivative(\" + db::tag_name<VelocityTag>() + \")\";\n  }\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::ii<DataType, Dim, Fr>*> result,\n      const Scalar<DataType>& tilde_d,\n      const tnsr::I<DataType, Dim, Fr>& coordinates,\n      const tnsr::I<DataType, Dim, Fr>& spatial_velocity)>(\n      &hydro::quadrupole_moment_derivative<DataType, Dim, Fr>);\n};\n}  // namespace grmhd::ValenciaDivClean::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/SetVariablesNeededFixingToFalse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/SetVariablesNeededFixingToFalse.hpp\"\n\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nvoid SetVariablesNeededFixingToFalse::apply(\n    const gsl::not_null<bool*> variables_needed_fixing) {\n  *variables_needed_fixing = false;\n}\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/SetVariablesNeededFixingToFalse.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean {\n/*!\n * \\brief Mutator used with `Initialization::Actions::AddSimpleTags` to\n * initialize the `VariablesNeededFixing` to `false`\n */\nstruct SetVariablesNeededFixingToFalse\n    : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<Tags::VariablesNeededFixing>;\n  using argument_tags = tmpl::list<>;\n\n  static void apply(gsl::not_null<bool*> variables_needed_fixing);\n};\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Sources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Sources.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid densitized_stress(\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*> result,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataVector>& one_over_w_squared,\n    const Scalar<DataVector>& pressure_star,\n    const Scalar<DataVector>& h_rho_w_squared_plus_b_squared,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  *result = inv_spatial_metric;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      result->get(i, j) *= get(pressure_star);\n      result->get(i, j) +=\n          get(h_rho_w_squared_plus_b_squared) * spatial_velocity.get(i) *\n              spatial_velocity.get(j) -\n          get(magnetic_field_dot_spatial_velocity) *\n              (magnetic_field.get(i) * spatial_velocity.get(j) +\n               magnetic_field.get(j) * spatial_velocity.get(i)) -\n          magnetic_field.get(i) * magnetic_field.get(j) *\n              get(one_over_w_squared);\n      result->get(i, j) *= get(sqrt_det_spatial_metric);\n    }\n  }\n}\n\nstruct MagneticFieldOneForm : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Frame::Inertial>;\n};\nstruct TildeSUp : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n};\nstruct DensitizedStress : db::SimpleTag {\n  using type = tnsr::II<DataVector, 3, Frame::Inertial>;\n};\nstruct OneOverLorentzFactorSquared : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct PressureStar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct EnthalpyTimesDensityWSquaredPlusBSquared : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace\n\nnamespace grmhd::ValenciaDivClean {\nnamespace detail {\nvoid sources_impl(\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        source_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        source_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_s_up,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        densitized_stress,\n    const gsl::not_null<Scalar<DataVector>*> h_rho_w_squared_plus_b_squared,\n\n    const Scalar<DataVector>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataVector>& magnetic_field_squared,\n    const Scalar<DataVector>& one_over_w_squared,\n    const Scalar<DataVector>& pressure_star,\n    const tnsr::I<DataVector, 3, Frame::Inertial>&\n        trace_spatial_christoffel_second,\n\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& /* tilde_ye */,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& /* electron_fraction */,\n    const Scalar<DataVector>& pressure,\n    const Scalar<DataVector>& specific_internal_energy,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    const double constraint_damping_parameter) {\n  get(*h_rho_w_squared_plus_b_squared) =\n      get(magnetic_field_squared) +\n      (get(rest_mass_density) * (1.0 + get(specific_internal_energy)) +\n       get(pressure)) *\n          square(get(lorentz_factor));\n  ::densitized_stress(densitized_stress, inv_spatial_metric,\n                      magnetic_field_dot_spatial_velocity, one_over_w_squared,\n                      pressure_star, *h_rho_w_squared_plus_b_squared,\n                      spatial_velocity, magnetic_field,\n                      sqrt_det_spatial_metric);\n  raise_or_lower_index(tilde_s_up, tilde_s, inv_spatial_metric);\n\n  get(*source_tilde_tau) = get(lapse) * get<0, 0>(extrinsic_curvature) *\n                               get<0, 0>(*densitized_stress) -\n                           get<0>(*tilde_s_up) * get<0>(d_lapse);\n  for (size_t m = 1; m < 3; ++m) {\n    get(*source_tilde_tau) +=\n        get(lapse) *\n            (extrinsic_curvature.get(m, 0) * densitized_stress->get(m, 0) +\n             extrinsic_curvature.get(0, m) * densitized_stress->get(0, m)) -\n        tilde_s_up->get(m) * d_lapse.get(m);\n    for (size_t n = 1; n < 3; ++n) {\n      get(*source_tilde_tau) += get(lapse) * extrinsic_curvature.get(m, n) *\n                                densitized_stress->get(m, n);\n    }\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    source_tilde_s->get(i) = -(get(tilde_d) + get(tilde_tau)) * d_lapse.get(i);\n    for (size_t m = 0; m < 3; ++m) {\n      source_tilde_s->get(i) += tilde_s.get(m) * d_shift.get(i, m);\n      for (size_t n = 0; n < 3; ++n) {\n        source_tilde_s->get(i) += 0.5 * get(lapse) *\n                                  d_spatial_metric.get(i, m, n) *\n                                  densitized_stress->get(m, n);\n      }\n    }\n  }\n\n  raise_or_lower_index(source_tilde_b, d_lapse, inv_spatial_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    source_tilde_b->get(i) *= get(tilde_phi);\n    source_tilde_b->get(i) -=\n        get(lapse) * get(tilde_phi) * trace_spatial_christoffel_second.get(i);\n    for (size_t m = 0; m < 3; ++m) {\n      source_tilde_b->get(i) -= tilde_b.get(m) * d_shift.get(m, i);\n    }\n  }\n\n  trace(source_tilde_phi, extrinsic_curvature, inv_spatial_metric);\n  get(*source_tilde_phi) += constraint_damping_parameter;\n  get(*source_tilde_phi) *= -1.0 * get(lapse) * get(tilde_phi);\n  for (size_t m = 0; m < 3; ++m) {\n    get(*source_tilde_phi) += tilde_b.get(m) * d_lapse.get(m);\n  }\n}\n}  // namespace detail\n\nvoid ComputeSources::apply(\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        source_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        source_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_phi,\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_internal_energy,\n    const Scalar<DataVector>& lorentz_factor,\n    const Scalar<DataVector>& pressure, const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    const double constraint_damping_parameter) {\n  Variables<\n      tmpl::list<TildeSUp, DensitizedStress, MagneticFieldOneForm,\n                 hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>,\n                 hydro::Tags::MagneticFieldSquared<DataVector>,\n                 OneOverLorentzFactorSquared, PressureStar,\n                 EnthalpyTimesDensityWSquaredPlusBSquared,\n                 gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>,\n                 gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>,\n                 gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>>\n      temp_tensors(get<0>(tilde_s).size());\n\n  auto& magnetic_field_oneform = get<MagneticFieldOneForm>(temp_tensors);\n  raise_or_lower_index(make_not_null(&magnetic_field_oneform), magnetic_field,\n                       spatial_metric);\n\n  auto& magnetic_field_squared =\n      get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp_tensors);\n  dot_product(make_not_null(&magnetic_field_squared), magnetic_field,\n              magnetic_field_oneform);\n\n  auto& magnetic_field_dot_spatial_velocity =\n      get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n          temp_tensors);\n  dot_product(make_not_null(&magnetic_field_dot_spatial_velocity),\n              magnetic_field_oneform, spatial_velocity);\n\n  auto& one_over_w_squared = get<OneOverLorentzFactorSquared>(temp_tensors);\n  get(one_over_w_squared) = 1.0 / square(get(lorentz_factor));\n\n  auto& pressure_star = get<PressureStar>(temp_tensors);\n  get(pressure_star) =\n      get(pressure) + 0.5 * square(get(magnetic_field_dot_spatial_velocity)) +\n      0.5 * get(magnetic_field_squared) * get(one_over_w_squared);\n\n  auto& spatial_christoffel_first_kind =\n      get<gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>>(temp_tensors);\n  gr::christoffel_first_kind(make_not_null(&spatial_christoffel_first_kind),\n                             d_spatial_metric);\n  auto& spatial_christoffel_second_kind =\n      get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>>(temp_tensors);\n  raise_or_lower_first_index(make_not_null(&spatial_christoffel_second_kind),\n                             spatial_christoffel_first_kind,\n                             inv_spatial_metric);\n  auto& trace_spatial_christoffel_second =\n      get<gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>(\n          temp_tensors);\n  trace_last_indices(make_not_null(&trace_spatial_christoffel_second),\n                     spatial_christoffel_second_kind, inv_spatial_metric);\n\n  detail::sources_impl(\n      source_tilde_tau, source_tilde_s, source_tilde_b, source_tilde_phi,\n\n      make_not_null(&get<TildeSUp>(temp_tensors)),\n      make_not_null(&get<DensitizedStress>(temp_tensors)),\n      make_not_null(\n          &get<EnthalpyTimesDensityWSquaredPlusBSquared>(temp_tensors)),\n\n      magnetic_field_dot_spatial_velocity, magnetic_field_squared,\n      one_over_w_squared, pressure_star, trace_spatial_christoffel_second,\n\n      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, lapse,\n      sqrt_det_spatial_metric, inv_spatial_metric, d_lapse, d_shift,\n      d_spatial_metric, spatial_velocity, lorentz_factor, magnetic_field,\n\n      rest_mass_density, electron_fraction, pressure, specific_internal_energy,\n      extrinsic_curvature, constraint_damping_parameter);\n}\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Sources.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace Tags {\ntemplate <typename>\nstruct Source;\n}  // namespace Tags\n/// \\endcond\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\nnamespace detail {\nvoid sources_impl(\n    gsl::not_null<Scalar<DataVector>*> source_tilde_tau,\n    gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> source_tilde_s,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> source_tilde_b,\n    gsl::not_null<Scalar<DataVector>*> source_tilde_phi,\n\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_s_up,\n    gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*> densitized_stress,\n    gsl::not_null<Scalar<DataVector>*> h_rho_w_squared_plus_b_squared,\n\n    const Scalar<DataVector>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataVector>& magnetic_field_squared,\n    const Scalar<DataVector>& one_over_w_squared,\n    const Scalar<DataVector>& pressure_star,\n    const tnsr::I<DataVector, 3, Frame::Inertial>&\n        trace_spatial_christoffel_second,\n\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& pressure,\n    const Scalar<DataVector>& specific_internal_energy,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    double constraint_damping_parameter);\n}  // namespace detail\n\n/*!\n * \\brief Compute the source terms for the flux-conservative Valencia\n * formulation of GRMHD with divergence cleaning, coupled with electron\n * fraction.\n *\n * A flux-conservative system has the generic form:\n * \\f[\n * \\partial_t U_i + \\partial_m F^m(U_i) = S(U_i)\n * \\f]\n *\n * where \\f$F^a()\\f$ denotes the flux of a conserved variable \\f$U_i\\f$ and\n * \\f$S()\\f$ denotes the source term for the conserved variable.\n *\n * For the Valencia formulation:\n * \\f{align*}\n * S({\\tilde D}) = & 0\\\\\n * S({\\tilde S}_i) = & \\frac{1}{2} \\alpha {\\tilde S}^{mn} \\partial_i \\gamma_{mn}\n * + {\\tilde S}_m \\partial_i \\beta^m - ({\\tilde D} + {\\tilde \\tau}) \\partial_i\n * \\alpha \\\\\n * S({\\tilde \\tau}) = & \\alpha {\\tilde S}^{mn} K_{mn} - {\\tilde S}^m \\partial_m\n * \\alpha \\\\\n * S({\\tilde B}^i) = & -{\\tilde B}^m \\partial_m \\beta^i + {\\tilde \\Phi}\n * \\gamma^{im} \\partial_m \\alpha + \\alpha {\\tilde \\Phi} \\left( \\frac{1}{2}\n * \\gamma^{il} \\gamma^{jk} - \\gamma^{ij} \\gamma^{lk} \\right) \\partial_l\n * \\gamma_{jk} \\\\ S({\\tilde \\Phi}) = & {\\tilde B}^k \\partial_k \\alpha - \\alpha K\n * {\\tilde \\Phi}\n * - \\alpha \\kappa {\\tilde \\Phi}\n * \\f}\n *\n * where\n * \\f{align*}\n * {\\tilde S}^i = & {\\tilde S}_m \\gamma^{im} \\\\\n * {\\tilde S}^{ij} = & \\sqrt{\\gamma} \\left[ \\left(h \\rho W^2 + B^n B_n \\right)\n * v^i v^j + \\left(p + p_m \\right) \\gamma^{ij} - B^n v_n \\left( B^i v^j + B^j\n * v^i \\right) - \\frac{B^i B^j}{W^2} \\right]\n * \\f}\n *\n * where \\f${\\tilde D}\\f$, \\f${\\tilde S}_i\\f$, \\f${\\tilde \\tau}\\f$, \\f${\\tilde\n * B}^i\\f$, and \\f${\\tilde \\Phi}\\f$ are a generalized mass-energy density,\n * momentum density, specific internal energy density, magnetic field, and\n * divergence cleaning field.  Furthermore, \\f$\\gamma\\f$ is the determinant of\n * the spatial metric \\f$\\gamma_{ij}\\f$, \\f$\\rho\\f$ is the rest mass density,\n * \\f$W\\f$ is the Lorentz factor, \\f$h\\f$ is the specific enthalpy, \\f$v^i\\f$ is\n * the spatial velocity, \\f$B^k\\f$ is the magnetic field, \\f$p\\f$ is the\n * pressure, \\f$p_m = \\frac{1}{2} \\left[ \\left( B^n v_n \\right)^2 + B^n B_n /\n * W^2 \\right]\\f$ is the magnetic pressure, \\f$\\alpha\\f$ is the lapse,\n * \\f$\\beta^i\\f$ is the shift, \\f$K\\f$ is the trace of the extrinsic curvature\n * \\f$K_{ij}\\f$, and \\f$\\kappa\\f$ is a damping parameter that damps violations\n * of the divergence-free (no-monopole) condition \\f$\\Phi = \\partial_i {\\tilde\n * B}^i = 0\\f$ .\n *\n * \\note For the electron fraction side, the source term is currently set to\n * \\f$S(\\tilde{Y}_e) = 0\\f$ where the conserved variable \\f$\\tilde{Y}_e\\f$ is a\n * generalized electron fraction. Implementing the source term using neutrino\n * scheme is in progress (Last update : Oct 2022).\n *\n */\nstruct ComputeSources {\n  using return_tags =\n      tmpl::list<::Tags::Source<grmhd::ValenciaDivClean::Tags::TildeTau>,\n                 ::Tags::Source<grmhd::ValenciaDivClean::Tags::TildeS<>>,\n                 ::Tags::Source<grmhd::ValenciaDivClean::Tags::TildeB<>>,\n                 ::Tags::Source<grmhd::ValenciaDivClean::Tags::TildePhi>>;\n\n  using argument_tags =\n      tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                 grmhd::ValenciaDivClean::Tags::TildeYe,\n                 grmhd::ValenciaDivClean::Tags::TildeTau,\n                 grmhd::ValenciaDivClean::Tags::TildeS<>,\n                 grmhd::ValenciaDivClean::Tags::TildeB<>,\n                 grmhd::ValenciaDivClean::Tags::TildePhi,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::Pressure<DataVector>, gr::Tags::Lapse<DataVector>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                 grmhd::ValenciaDivClean::Tags::ConstraintDampingParameter>;\n\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> source_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> source_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> source_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> source_tilde_phi,\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& electron_fraction,\n      const Scalar<DataVector>& specific_internal_energy,\n      const Scalar<DataVector>& lorentz_factor,\n      const Scalar<DataVector>& pressure, const Scalar<DataVector>& lapse,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n      const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n      double constraint_damping_parameter);\n};\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FixConservativesAndComputePrims.cpp\n  NeighborPackagedData.cpp\n  PrimitiveGhostData.cpp\n  PrimsAfterRollback.cpp\n  ResizeAndComputePrimitives.cpp\n  SetInitialRdmpData.cpp\n  SwapGrTags.cpp\n  TciOnDgGrid.cpp\n  TciOnFdGrid.cpp\n  TciOptions.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComputeFluxes.hpp\n  FixConservativesAndComputePrims.hpp\n  NeighborPackagedData.hpp\n  PrimitiveGhostData.hpp\n  PrimsAfterRollback.hpp\n  ResizeAndComputePrimitives.hpp\n  SetInitialRdmpData.hpp\n  Subcell.hpp\n  SwapGrTags.hpp\n  TciOnDgGrid.hpp\n  TciOnFdGrid.hpp\n  TciOptions.hpp\n  TimeDerivative.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ComputeFluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\nnamespace detail {\ntemplate <typename TagsList, typename... ReturnTags, typename... ArgumentTags>\nvoid compute_fluxes_impl(const gsl::not_null<Variables<TagsList>*> vars,\n                         tmpl::list<ReturnTags...> /*meta*/,\n                         tmpl::list<ArgumentTags...> /*meta*/) {\n  grmhd::ValenciaDivClean::ComputeFluxes::apply(\n      make_not_null(&get<ReturnTags>(*vars))..., get<ArgumentTags>(*vars)...);\n}\n}  // namespace detail\n\n/*!\n * \\brief Helper function that calls `ComputeFluxes` by retrieving the return\n * and argument tags from `vars`.\n */\ntemplate <typename TagsList>\nvoid compute_fluxes(const gsl::not_null<Variables<TagsList>*> vars) {\n  detail::compute_fluxes_impl(\n      vars, typename grmhd::ValenciaDivClean::ComputeFluxes::return_tags{},\n      typename grmhd::ValenciaDivClean::ComputeFluxes::argument_tags{});\n}\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/FixConservativesAndComputePrims.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/FixConservativesAndComputePrims.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\ntemplate <typename OrderedListOfRecoverySchemes>\nvoid FixConservativesAndComputePrims<OrderedListOfRecoverySchemes>::apply(\n    const gsl::not_null<bool*> needed_fixing,\n    const gsl::not_null<typename System::variables_tag::type*>\n        conserved_vars_ptr,\n    const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*>\n        primitive_vars_ptr,\n    const grmhd::ValenciaDivClean::FixConservatives& fix_conservatives,\n    const EquationsOfState::EquationOfState<true, 3>& eos,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options) {\n  *needed_fixing = fix_conservatives(\n      make_not_null(&get<Tags::TildeD>(*conserved_vars_ptr)),\n      make_not_null(&get<Tags::TildeYe>(*conserved_vars_ptr)),\n      make_not_null(&get<Tags::TildeTau>(*conserved_vars_ptr)),\n      make_not_null(&get<Tags::TildeS<Frame::Inertial>>(*conserved_vars_ptr)),\n      get<Tags::TildeB<Frame::Inertial>>(*conserved_vars_ptr), spatial_metric,\n      inv_spatial_metric, sqrt_det_spatial_metric);\n  grmhd::ValenciaDivClean::\n      PrimitiveFromConservative<OrderedListOfRecoverySchemes, true>::apply(\n          make_not_null(&get<hydro::Tags::RestMassDensity<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::ElectronFraction<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::MagneticField<DataVector, 3>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(&get<hydro::Tags::LorentzFactor<DataVector>>(\n              *primitive_vars_ptr)),\n          make_not_null(\n              &get<hydro::Tags::Pressure<DataVector>>(*primitive_vars_ptr)),\n          make_not_null(\n              &get<hydro::Tags::Temperature<DataVector>>(*primitive_vars_ptr)),\n          get<Tags::TildeD>(*conserved_vars_ptr),\n          get<Tags::TildeYe>(*conserved_vars_ptr),\n          get<Tags::TildeTau>(*conserved_vars_ptr),\n          get<Tags::TildeS<Frame::Inertial>>(*conserved_vars_ptr),\n          get<Tags::TildeB<Frame::Inertial>>(*conserved_vars_ptr),\n          get<Tags::TildePhi>(*conserved_vars_ptr), spatial_metric,\n          inv_spatial_metric, sqrt_det_spatial_metric, eos,\n          primitive_from_conservative_options);\n}\n\nnamespace {\nusing NewmanThenPalenzuela =\n    tmpl::list<PrimitiveRecoverySchemes::NewmanHamlin,\n               PrimitiveRecoverySchemes::PalenzuelaEtAl>;\nusing KastaunThenNewmanThenPalenzuela =\n    tmpl::list<PrimitiveRecoverySchemes::KastaunEtAl,\n               PrimitiveRecoverySchemes::NewmanHamlin,\n               PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n}  // namespace\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template struct FixConservativesAndComputePrims<RECOVERY(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (tmpl::list<PrimitiveRecoverySchemes::KastaunEtAl>,\n                         tmpl::list<PrimitiveRecoverySchemes::NewmanHamlin>,\n                         tmpl::list<PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n                         NewmanThenPalenzuela, KastaunThenNewmanThenPalenzuela))\n\n#undef INSTANTIATION\n#undef THERMO_DIM\n#undef RECOVERY\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/FixConservativesAndComputePrims.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief Fix the conservative variables and compute the primitive variables.\n *\n * Sets `ValenciaDivClean::Tags::VariablesNeededFixing` to `true` if the\n * conservative variables needed fixing, otherwise sets the tag to `false`.\n */\ntemplate <typename OrderedListOfRecoverySchemes>\nstruct FixConservativesAndComputePrims {\n  using return_tags = tmpl::list<ValenciaDivClean::Tags::VariablesNeededFixing,\n                                 typename System::variables_tag,\n                                 typename System::primitive_variables_tag>;\n  using argument_tags = tmpl::list<\n      ::Tags::VariableFixer<grmhd::ValenciaDivClean::FixConservatives>,\n      hydro::Tags::GrmhdEquationOfState, gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  static void apply(\n      gsl::not_null<bool*> needed_fixing,\n      gsl::not_null<typename System::variables_tag::type*> conserved_vars_ptr,\n      gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*>\n          primitive_vars_ptr,\n      const grmhd::ValenciaDivClean::FixConservatives& fix_conservatives,\n      const EquationsOfState::EquationOfState<true, 3>& eos,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/NeighborPackagedData.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.tpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\nDirectionalIdMap<3, DataVector> NeighborPackagedData::apply(\n    const db::Access& box,\n    const std::vector<DirectionalId<3>>& mortars_to_reconstruct_to) {\n  using evolved_vars_tag = typename System::variables_tag;\n  using evolved_vars_tags = typename evolved_vars_tag::tags_list;\n  using fluxes_tags = db::wrap_tags_in<::Tags::Flux, evolved_vars_tags,\n                                       tmpl::size_t<3>, Frame::Inertial>;\n\n  ASSERT(not db::get<domain::Tags::MeshVelocity<3>>(box).has_value(),\n         \"Haven't yet added support for moving mesh to DG-subcell. This \"\n         \"should be easy to generalize, but we will want to consider \"\n         \"storing the mesh velocity on the faces instead of \"\n         \"re-slicing/projecting.\");\n\n  DirectionalIdMap<3, DataVector> neighbor_package_data{};\n  if (mortars_to_reconstruct_to.empty()) {\n    return neighbor_package_data;\n  }\n\n  const auto& ghost_subcell_data =\n      db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box);\n  const Mesh<3>& subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<3>>(box);\n  const Mesh<3>& dg_mesh = db::get<domain::Tags::Mesh<3>>(box);\n  const auto& subcell_options =\n      db::get<evolution::dg::subcell::Tags::SubcellOptions<3>>(box);\n\n  // Note: we need to compare if projecting the entire mesh or only ghost\n  // zones needed is faster. This probably depends on the number of neighbors\n  // we have doing FD.\n  const auto volume_prims = evolution::dg::subcell::fd::project(\n      db::get<typename System::primitive_variables_tag>(box), dg_mesh,\n      subcell_mesh.extents());\n\n  const auto& recons =\n      db::get<grmhd::ValenciaDivClean::fd::Tags::Reconstructor>(box);\n  const auto& boundary_correction =\n      db::get<evolution::Tags::BoundaryCorrection>(box);\n  using derived_boundary_corrections = grmhd::ValenciaDivClean::\n      BoundaryCorrections::standard_boundary_corrections;\n  tmpl::for_each<derived_boundary_corrections>([&box, &boundary_correction,\n                                                &dg_mesh,\n                                                &mortars_to_reconstruct_to,\n                                                &neighbor_package_data,\n                                                &ghost_subcell_data, &recons,\n                                                &subcell_mesh, &subcell_options,\n                                                &volume_prims](\n                                                   auto derived_correction_v) {\n    using DerivedCorrection = tmpl::type_from<decltype(derived_correction_v)>;\n    if (typeid(boundary_correction) == typeid(DerivedCorrection)) {\n      using dg_package_data_temporary_tags =\n          typename DerivedCorrection::dg_package_data_temporary_tags;\n      using dg_package_data_argument_tags = fd::tags_list_for_reconstruct;\n\n      const auto& element = db::get<domain::Tags::Element<3>>(box);\n      const auto& eos = get<hydro::Tags::GrmhdEquationOfState>(box);\n\n      using dg_package_field_tags =\n          typename DerivedCorrection::dg_package_field_tags;\n      Variables<dg_package_data_argument_tags> vars_on_face{0};\n      Variables<dg_package_field_tags> packaged_data{0};\n      for (const auto& mortar_id : mortars_to_reconstruct_to) {\n        const Direction<3>& direction = mortar_id.direction();\n\n        const Mesh<2> dg_face_mesh = dg_mesh.slice_away(direction.dimension());\n        Index<3> extents = subcell_mesh.extents();\n        // Switch to face-centered instead of cell-centered points on the FD.\n        // There are num_cell_centered+1 face-centered points.\n        ++extents[direction.dimension()];\n        const Index<2> subcell_face_extents =\n            extents.slice_away(direction.dimension());\n\n        // Computed prims and cons on face via reconstruction\n        const size_t num_face_pts =\n            subcell_mesh.extents().slice_away(direction.dimension()).product();\n        vars_on_face.initialize(num_face_pts);\n        // Copy spacetime vars over from volume.\n        using spacetime_vars_to_copy =\n            tmpl::list<gr::Tags::Lapse<DataVector>,\n                       gr::Tags::Shift<DataVector, 3>,\n                       gr::Tags::SpatialMetric<DataVector, 3>,\n                       gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                       gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n        tmpl::for_each<spacetime_vars_to_copy>(\n            [&direction, &extents, &vars_on_face,\n             &spacetime_vars_on_faces =\n                 db::get<evolution::dg::subcell::Tags::OnSubcellFaces<\n                     typename System::flux_spacetime_variables_tag, 3>>(box)](\n                auto tag_v) {\n              using tag = tmpl::type_from<decltype(tag_v)>;\n              data_on_slice(make_not_null(&get<tag>(vars_on_face)),\n                            get<tag>(gsl::at(spacetime_vars_on_faces,\n                                             direction.dimension())),\n                            extents, direction.dimension(),\n                            direction.side() == Side::Lower\n                                ? 0\n                                : extents[direction.dimension()] - 1);\n            });\n\n        call_with_dynamic_type<void, typename grmhd::ValenciaDivClean::fd::\n                                         Reconstructor::creatable_classes>(\n            &recons,\n            [&element, &eos, &mortar_id, &ghost_subcell_data, &subcell_mesh,\n             &vars_on_face, &volume_prims](const auto& reconstructor) {\n              reconstructor->reconstruct_fd_neighbor(\n                  make_not_null(&vars_on_face), volume_prims, eos, element,\n                  ghost_subcell_data, subcell_mesh, mortar_id.direction());\n            });\n\n        // Get the mesh velocity if needed\n        const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>\n            mesh_velocity_dg = db::get<domain::Tags::MeshVelocity<3>>(box);\n        std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>\n            mesh_velocity_on_subcell_face = {};\n        if (mesh_velocity_dg.has_value()) {\n          // Slice data on current face\n          tnsr::I<DataVector, 3, Frame::Inertial> mesh_velocity_on_dg_face =\n              data_on_slice(mesh_velocity_dg.value(), dg_mesh.extents(),\n                            direction.dimension(),\n                            direction.side() == Side::Lower\n                                ? 0\n                                : (dg_mesh.extents(direction.dimension()) - 1));\n\n          mesh_velocity_on_subcell_face =\n              tnsr::I<DataVector, 3, Frame::Inertial>{num_face_pts};\n\n          for (size_t i = 0; i < 3; i++) {\n            evolution::dg::subcell::fd::project(\n                make_not_null(&mesh_velocity_on_subcell_face.value().get(i)),\n                mesh_velocity_on_dg_face.get(i), dg_face_mesh,\n                subcell_face_extents);\n          }\n        }\n\n        grmhd::ValenciaDivClean::subcell::compute_fluxes(\n            make_not_null(&vars_on_face));\n\n        if (mesh_velocity_on_subcell_face.has_value()) {\n          tmpl::for_each<evolved_vars_tags>(\n              [&vars_on_face, &mesh_velocity_on_subcell_face](auto tag_v) {\n                using tag = tmpl::type_from<decltype(tag_v)>;\n                using flux_tag =\n                    ::Tags::Flux<tag, tmpl::size_t<3>, Frame::Inertial>;\n                using FluxTensor = typename flux_tag::type;\n                const auto& var = get<tag>(vars_on_face);\n                auto& flux = get<flux_tag>(vars_on_face);\n                for (size_t storage_index = 0; storage_index < var.size();\n                     ++storage_index) {\n                  const auto tensor_index = var.get_tensor_index(storage_index);\n                  for (size_t j = 0; j < 3; j++) {\n                    const auto flux_storage_index =\n                        FluxTensor::get_storage_index(prepend(tensor_index, j));\n                    flux[flux_storage_index] -=\n                        mesh_velocity_on_subcell_face.value().get(j) *\n                        var[storage_index];\n                  }\n                }\n              });\n        }\n\n        // Note: since the spacetime isn't dynamical we don't need to\n        // worry about different normal vectors on the different sides\n        // of the element. If the spacetime is dynamical, then the normal\n        // covector needs to be sent, or at least the inverse spatial metric\n        // from the neighbor so that the normal covector can be computed\n        // correctly.\n        tnsr::i<DataVector, 3, Frame::Inertial> normal_covector =\n            get<evolution::dg::Tags::NormalCovector<3>>(\n                *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<3>>(\n                     box)\n                     .at(mortar_id.direction()));\n        for (auto& t : normal_covector) {\n          t *= -1.0;\n        }\n        // Note: Only need to do the projection in 2d and 3d, but GRMHD is\n        // always 3d currently.\n        const auto dg_normal_covector = normal_covector;\n        for (size_t i = 0; i < 3; ++i) {\n          normal_covector.get(i) = evolution::dg::subcell::fd::project(\n              dg_normal_covector.get(i),\n              dg_mesh.slice_away(mortar_id.direction().dimension()),\n              subcell_mesh.extents().slice_away(\n                  mortar_id.direction().dimension()));\n        }\n\n        // Compute the packaged data\n        packaged_data.initialize(num_face_pts);\n        using dg_package_data_projected_tags = tmpl::append<\n            evolved_vars_tags, fluxes_tags, dg_package_data_temporary_tags,\n            typename DerivedCorrection::dg_package_data_primitive_tags>;\n        evolution::dg::Actions::detail::dg_package_data<System>(\n            make_not_null(&packaged_data),\n            dynamic_cast<const DerivedCorrection&>(boundary_correction),\n            vars_on_face, normal_covector, mesh_velocity_on_subcell_face, box,\n            typename DerivedCorrection::dg_package_data_volume_tags{},\n            dg_package_data_projected_tags{});\n\n        // Reconstruct the DG solution.\n        // Really we should be solving the boundary correction and\n        // then reconstructing, but away from a shock this doesn't\n        // matter.\n        auto dg_packaged_data = evolution::dg::subcell::fd::reconstruct(\n            packaged_data,\n            dg_mesh.slice_away(mortar_id.direction().dimension()),\n            subcell_mesh.extents().slice_away(\n                mortar_id.direction().dimension()),\n            subcell_options.reconstruction_method());\n        // Make a view so we can use iterators with std::copy\n        DataVector dg_packaged_data_view{dg_packaged_data.data(),\n                                         dg_packaged_data.size()};\n        neighbor_package_data[mortar_id] = DataVector{dg_packaged_data.size()};\n        std::copy(dg_packaged_data_view.begin(), dg_packaged_data_view.end(),\n                  neighbor_package_data[mortar_id].begin());\n      }\n    }\n  });\n\n  return neighbor_package_data;\n}\n}  // namespace grmhd::ValenciaDivClean::subcell\n\ntemplate void evolution::dg::subcell::neighbor_reconstructed_face_solution<\n    3, grmhd::ValenciaDivClean::subcell::NeighborPackagedData>(\n    gsl::not_null<db::Access*> box);\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/NeighborPackagedData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n/// \\cond\nnamespace db {\nclass Access;\n}  // namespace db\nclass DataVector;\ntemplate <size_t VolumeDim>\nstruct DirectionalId;\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief On elements using DG, reconstructs the interface data from a\n * neighboring element doing subcell.\n *\n * The neighbor's packaged data needed by the boundary correction is computed\n * and returned so that it can be used for solving the Riemann problem on the\n * interfaces.\n *\n * Note that for strict conservation the Riemann solve should be done on the\n * subcells, with the correction being projected back to the DG interface.\n * However, in practice such strict conservation doesn't seem to be necessary\n * and can be explained by that we only need strict conservation at shocks, and\n * if one element is doing DG, then we aren't at a shock.\n */\nstruct NeighborPackagedData {\n  static DirectionalIdMap<3, DataVector> apply(\n      const db::Access& box,\n      const std::vector<DirectionalId<3>>& mortars_to_reconstruct_to);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimitiveGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimitiveGhostData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\nDataVector PrimitiveGhostVariables::apply(\n    const Variables<hydro::grmhd_tags<DataVector>>& prims,\n    const size_t rdmp_size) {\n  DataVector buffer{\n      prims.number_of_grid_points() *\n          Variables<\n              prims_to_reconstruct_tags>::number_of_independent_components +\n      rdmp_size};\n  Variables<prims_to_reconstruct_tags> vars_to_reconstruct(\n      buffer.data(), buffer.size() - rdmp_size);\n  get<hydro::Tags::RestMassDensity<DataVector>>(vars_to_reconstruct) =\n      get<hydro::Tags::RestMassDensity<DataVector>>(prims);\n  get<hydro::Tags::ElectronFraction<DataVector>>(vars_to_reconstruct) =\n      get<hydro::Tags::ElectronFraction<DataVector>>(prims);\n  get<hydro::Tags::Temperature<DataVector>>(vars_to_reconstruct) =\n      get<hydro::Tags::Temperature<DataVector>>(prims);\n  get<hydro::Tags::MagneticField<DataVector, 3>>(vars_to_reconstruct) =\n      get<hydro::Tags::MagneticField<DataVector, 3>>(prims);\n  get<hydro::Tags::DivergenceCleaningField<DataVector>>(vars_to_reconstruct) =\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(prims);\n\n  auto& lorentz_factor_time_spatial_velocity =\n      get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n          vars_to_reconstruct) =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prims);\n  for (size_t i = 0; i < 3; ++i) {\n    lorentz_factor_time_spatial_velocity.get(i) *=\n        get(get<hydro::Tags::LorentzFactor<DataVector>>(prims));\n  }\n  return buffer;\n}\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimitiveGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename T>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief Computes the rest mass density \\f$\\rho\\f$, electron fraction\n * \\f$Y_e\\f$, temperature \\f$T\\f$, Lorentz factor times the spatial velocity\n * \\f$W v^i\\f$, magnetic field \\f$B^i\\f$, and the divergence cleaning field\n * \\f$\\Phi\\f$ so they can be used for reconstruction.\n *\n * The computation copies the data from the primitive variables to a new\n * Variables and computes \\f$W v^i\\f$. In the future we will likely want to\n * elide this copy but that requires support from the actions.\n *\n * This mutator is passed to\n * `evolution::dg::subcell::Actions::SendDataForReconstruction`.\n */\nclass PrimitiveGhostVariables {\n private:\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>>;\n\n  static DataVector apply(const Variables<hydro::grmhd_tags<DataVector>>& prims,\n                          size_t rdmp_size);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimsAfterRollback.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimsAfterRollback.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\ntemplate <typename OrderedListOfRecoverySchemes>\nvoid PrimsAfterRollback<OrderedListOfRecoverySchemes>::apply(\n    const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> prim_vars,\n    const bool did_rollback, const Mesh<3>& dg_mesh,\n    const Mesh<3>& subcell_mesh, const Scalar<DataVector>& tilde_d,\n    const Scalar<DataVector>& tilde_ye, const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const EquationsOfState::EquationOfState<true, 3>& eos,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options) {\n  if (did_rollback) {\n    const size_t num_grid_points = subcell_mesh.number_of_grid_points();\n    ASSERT(prim_vars->number_of_grid_points() != num_grid_points,\n           \"Doing rollback and the prim_vars should not be the size of the \"\n           \"subcell mesh\");\n    // Project a copy of the pressure from the DG grid to the FD grid to give\n    // high-order initial guess for prim recovery.\n    const Scalar<DataVector> dg_pressure =\n        get<hydro::Tags::Pressure<DataVector>>(*prim_vars);\n    prim_vars->initialize(num_grid_points);\n    evolution::dg::subcell::fd::project(\n        make_not_null(&get(get<hydro::Tags::Pressure<DataVector>>(*prim_vars))),\n        get(dg_pressure), dg_mesh, subcell_mesh.extents());\n\n    grmhd::ValenciaDivClean::\n        PrimitiveFromConservative<OrderedListOfRecoverySchemes, true>::apply(\n            make_not_null(\n                &get<hydro::Tags::RestMassDensity<DataVector>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::ElectronFraction<DataVector>>(*prim_vars)),\n            make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                *prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::SpatialVelocity<DataVector, 3>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::MagneticField<DataVector, 3>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n                    *prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::LorentzFactor<DataVector>>(*prim_vars)),\n            make_not_null(&get<hydro::Tags::Pressure<DataVector>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::Temperature<DataVector>>(*prim_vars)),\n            tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n            spatial_metric, inv_spatial_metric, sqrt_det_spatial_metric, eos,\n            primitive_from_conservative_options);\n  }\n}\n\nnamespace {\nusing NewmanThenPalenzuela =\n    tmpl::list<PrimitiveRecoverySchemes::NewmanHamlin,\n               PrimitiveRecoverySchemes::PalenzuelaEtAl>;\nusing KastaunThenNewmanThenPalenzuela =\n    tmpl::list<PrimitiveRecoverySchemes::KastaunEtAl,\n               PrimitiveRecoverySchemes::NewmanHamlin,\n               PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n}  // namespace\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                                \\\n  template void PrimsAfterRollback<RECOVERY(data)>::apply(                    \\\n      const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*>          \\\n          prim_vars,                                                          \\\n      bool did_rollback, const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh, \\\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,  \\\n      const Scalar<DataVector>& tilde_tau,                                    \\\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,                 \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,                 \\\n      const Scalar<DataVector>& tilde_phi,                                    \\\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,         \\\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,     \\\n      const Scalar<DataVector>& sqrt_det_spatial_metric,                      \\\n      const EquationsOfState::EquationOfState<true, 3>& eos,                  \\\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&        \\\n          primitive_from_conservative_options);\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (tmpl::list<PrimitiveRecoverySchemes::KastaunEtAl>,\n                         tmpl::list<PrimitiveRecoverySchemes::NewmanHamlin>,\n                         tmpl::list<PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n                         NewmanThenPalenzuela, KastaunThenNewmanThenPalenzuela))\n#undef INSTANTIATION\n#undef THERMO_DIM\n#undef RECOVERY\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimsAfterRollback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief Mutator that resizes the primitive variables to the subcell mesh and\n * computes the primitives, but only if\n * `evolution::dg::subcell::Tags::DidRollback` is `true`.\n *\n * In the DG-subcell `step_actions` list this will normally be called using the\n * `::Actions::MutateApply` action in the following way in the action list:\n * - `Actions::Label<Labels::BeginSubcellAfterDgRollback>`\n * - `Actions::MutateApply<grmhd::ValenciaDivClean::subcell::SwapGrTags>`\n * - `Actions::MutateApply<PrimsAfterRollback<primitive_recovery_schemes>>`\n * Note that the GR tags are swapped to the subcell grid before this action is\n * called.\n */\ntemplate <typename OrderedListOfRecoverySchemes>\nstruct PrimsAfterRollback {\n  using return_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>>;\n  using argument_tags = tmpl::list<\n      evolution::dg::subcell::Tags::DidRollback, domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      grmhd::ValenciaDivClean::Tags::TildeD,\n      grmhd::ValenciaDivClean::Tags::TildeYe,\n      grmhd::ValenciaDivClean::Tags::TildeTau,\n      grmhd::ValenciaDivClean::Tags::TildeS<>,\n      grmhd::ValenciaDivClean::Tags::TildeB<>,\n      grmhd::ValenciaDivClean::Tags::TildePhi,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  static void apply(\n      gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> prim_vars,\n      bool did_rollback, const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const EquationsOfState::EquationOfState<true, 3>& eos,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ResizeAndComputePrimitives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ResizeAndComputePrimitives.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\ntemplate <typename OrderedListOfRecoverySchemes>\nvoid ResizeAndComputePrims<OrderedListOfRecoverySchemes>::apply(\n    const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> prim_vars,\n    const evolution::dg::subcell::ActiveGrid active_grid,\n    const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const EquationsOfState::EquationOfState<true, 3>& eos,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    const size_t num_grid_points = dg_mesh.number_of_grid_points();\n    // Reconstruct a copy of the pressure from the FD grid to the DG grid to\n    // provide a high-order initial guess.\n    const Scalar<DataVector> fd_pressure =\n        get<hydro::Tags::Pressure<DataVector>>(*prim_vars);\n    prim_vars->initialize(num_grid_points);\n    if (get(fd_pressure).size() == subcell_mesh.number_of_grid_points()) {\n      evolution::dg::subcell::fd::reconstruct(\n          make_not_null(\n              &get(get<hydro::Tags::Pressure<DataVector>>(*prim_vars))),\n          get(fd_pressure), dg_mesh, subcell_mesh.extents(),\n          // Always do dim-by-dim reconstruction because it's fast\n          evolution::dg::subcell::fd ::ReconstructionMethod::DimByDim);\n    }\n\n    // We only need to compute the prims if we switched to the DG grid because\n    // otherwise we computed the prims during the FD TCI.\n    grmhd::ValenciaDivClean::\n        PrimitiveFromConservative<OrderedListOfRecoverySchemes, true>::apply(\n            make_not_null(\n                &get<hydro::Tags::RestMassDensity<DataVector>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::ElectronFraction<DataVector>>(*prim_vars)),\n            make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                *prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::SpatialVelocity<DataVector, 3>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::MagneticField<DataVector, 3>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n                    *prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::LorentzFactor<DataVector>>(*prim_vars)),\n            make_not_null(&get<hydro::Tags::Pressure<DataVector>>(*prim_vars)),\n            make_not_null(\n                &get<hydro::Tags::Temperature<DataVector>>(*prim_vars)),\n            tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n            spatial_metric, inv_spatial_metric, sqrt_det_spatial_metric, eos,\n            primitive_from_conservative_options);\n  }\n}\n\nnamespace {\nusing NewmanThenPalenzuela =\n    tmpl::list<PrimitiveRecoverySchemes::NewmanHamlin,\n               PrimitiveRecoverySchemes::PalenzuelaEtAl>;\nusing KastaunThenNewmanThenPalenzuela =\n    tmpl::list<PrimitiveRecoverySchemes::KastaunEtAl,\n               PrimitiveRecoverySchemes::NewmanHamlin,\n               PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n}  // namespace\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) \\\n  template struct ResizeAndComputePrims<RECOVERY(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (tmpl::list<PrimitiveRecoverySchemes::KastaunEtAl>,\n                         tmpl::list<PrimitiveRecoverySchemes::NewmanHamlin>,\n                         tmpl::list<PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n                         NewmanThenPalenzuela, KastaunThenNewmanThenPalenzuela))\n#undef INSTANTIATION\n#undef THERMO_DIM\n#undef RECOVERY\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ResizeAndComputePrimitives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief Mutator that, if the active mesh is subcell, resizes the primitive\n * variables to have the size of the subcell and then computes the primitive\n * variables.\n *\n * In the DG-subcell `step_actions` list this will normally be called using the\n * `::Actions::MutateApply` action in the following way in the action list:\n * - `TciAndSwitchToDg<TciOnFdGrid>`\n * - `Actions::MutateApply<grmhd::ValenciaDivClean::subcell::SwapGrTags>`\n * - `Actions::MutateApply<ResizeAndComputePrims<primitive_recovery_schemes>>`\n * Note that the GR tags are swapped before this action is called.\n *\n * If the active grid is DG (we are switching from subcell back to DG) then this\n * mutator computes the primitive variables on the active grid. We reconstruct\n * the pressure to the DG grid to give a high-order initial guess for the\n * primitive recovery. A possible future optimization would be to avoid this\n * reconstruction when all recovery schemes don't need an initial guess.\n * Finally, we perform the primitive recovery on the DG grid.\n */\ntemplate <typename OrderedListOfRecoverySchemes>\nstruct ResizeAndComputePrims {\n  using return_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>>;\n  using argument_tags = tmpl::list<\n      evolution::dg::subcell::Tags::ActiveGrid, domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      grmhd::ValenciaDivClean::Tags::TildeD,\n      grmhd::ValenciaDivClean::Tags::TildeYe,\n      grmhd::ValenciaDivClean::Tags::TildeTau,\n      grmhd::ValenciaDivClean::Tags::TildeS<>,\n      grmhd::ValenciaDivClean::Tags::TildeB<>,\n      grmhd::ValenciaDivClean::Tags::TildePhi,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  static void apply(\n      gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> prim_vars,\n      evolution::dg::subcell::ActiveGrid active_grid, const Mesh<3>& dg_mesh,\n      const Mesh<3>& subcell_mesh, const Scalar<DataVector>& tilde_d,\n      const Scalar<DataVector>& tilde_ye, const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const EquationsOfState::EquationOfState<true, 3>& eos,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SetInitialRdmpData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/TwoMeshRdmpTci.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\nvoid SetInitialRdmpData::apply(\n    const gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const evolution::dg::subcell::ActiveGrid active_grid,\n    const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Subcell) {\n    const Scalar<DataVector> tilde_b_magnitude = magnitude(tilde_b);\n\n    rdmp_tci_data->max_variables_values =\n        DataVector{max(get(tilde_d)), max(get(tilde_ye)), max(get(tilde_tau)),\n                   max(get(tilde_b_magnitude))};\n    rdmp_tci_data->min_variables_values =\n        DataVector{min(get(tilde_d)), min(get(tilde_ye)), min(get(tilde_tau)),\n                   min(get(tilde_b_magnitude))};\n  } else {\n    const Scalar<DataVector> tilde_b_magnitude = magnitude(tilde_b);\n    const auto subcell_tilde_b_mag = evolution::dg::subcell::fd::project(\n        get(tilde_b_magnitude), dg_mesh, subcell_mesh.extents());\n    const auto subcell_tilde_d = evolution::dg::subcell::fd::project(\n        get(tilde_d), dg_mesh, subcell_mesh.extents());\n    const auto subcell_tilde_ye = evolution::dg::subcell::fd::project(\n        get(tilde_ye), dg_mesh, subcell_mesh.extents());\n    const auto subcell_tilde_tau = evolution::dg::subcell::fd::project(\n        get(tilde_tau), dg_mesh, subcell_mesh.extents());\n\n    using std::max;\n    using std::min;\n    rdmp_tci_data->max_variables_values =\n        DataVector{max(max(subcell_tilde_d), max(get(tilde_d))),\n                   max(max(subcell_tilde_ye), max(get(tilde_ye))),\n                   max(max(subcell_tilde_tau), max(get(tilde_tau))),\n                   max(max(subcell_tilde_b_mag), max(get(tilde_b_magnitude)))};\n    rdmp_tci_data->min_variables_values =\n        DataVector{min(min(subcell_tilde_d), min(get(tilde_d))),\n                   min(min(subcell_tilde_ye), min(get(tilde_ye))),\n                   min(min(subcell_tilde_tau), min(get(tilde_tau))),\n                   min(min(subcell_tilde_b_mag), min(get(tilde_b_magnitude)))};\n  }\n}\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SetInitialRdmpData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/// \\brief Sets the initial RDMP data.\n///\n/// Used on the subcells after the TCI marked the DG solution as inadmissible.\nstruct SetInitialRdmpData {\n  using argument_tags = tmpl::list<\n      ValenciaDivClean::Tags::TildeD, ValenciaDivClean::Tags::TildeYe,\n      ValenciaDivClean::Tags::TildeTau, ValenciaDivClean::Tags::TildeB<>,\n      evolution::dg::subcell::Tags::ActiveGrid, ::domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>>;\n  using return_tags = tmpl::list<evolution::dg::subcell::Tags::DataForRdmpTci>;\n\n  static void apply(\n      gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n      const Scalar<DataVector>& subcell_tilde_d,\n      const Scalar<DataVector>& subcell_tilde_ye,\n      const Scalar<DataVector>& subcell_tilde_tau,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_b,\n      evolution::dg::subcell::ActiveGrid active_grid, const Mesh<3>& dg_mesh,\n      const Mesh<3>& subcell_mesh);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Subcell.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace grmhd::ValenciaDivClean {\n/// \\brief Code required by the DG-subcell/FD hybrid solver.\nnamespace subcell {}\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SwapGrTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SwapGrTags.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\nvoid SwapGrTags::apply(\n    const gsl::not_null<\n        Variables<typename System::spacetime_variables_tag::tags_list>*>\n        active_gr_vars,\n    const gsl::not_null<typename evolution::dg::subcell::Tags::Inactive<\n        typename System::spacetime_variables_tag>::type*>\n        inactive_gr_vars,\n    const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n    const evolution::dg::subcell::ActiveGrid active_grid) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    // We might request a switch to the DG grid even if we are already on the DG\n    // grid, and in this case we do nothing. This can occur when applying\n    // SwapGrTags to a collection of elements that may have different TCI\n    // results.\n    if (active_gr_vars->number_of_grid_points() !=\n        dg_mesh.number_of_grid_points()) {\n      ASSERT(\n          active_gr_vars->number_of_grid_points() ==\n              subcell_mesh.number_of_grid_points(),\n          \"When swapping the GR variables from subcell to DG, the active \"\n          \"GR variables should be holding the subcell variables and be of size \"\n              << subcell_mesh.number_of_grid_points()\n              << \" but they are of size \"\n              << active_gr_vars->number_of_grid_points());\n      using std::swap;\n      swap(*active_gr_vars, *inactive_gr_vars);\n    }\n  } else {\n    if (active_gr_vars->number_of_grid_points() !=\n        subcell_mesh.number_of_grid_points()) {\n      ASSERT(active_gr_vars->number_of_grid_points() ==\n                 dg_mesh.number_of_grid_points(),\n             \"When swapping the GR variables from DG to subcell, the active \"\n             \"GR variables should be holding the DG variables and be of size \"\n                 << dg_mesh.number_of_grid_points() << \" but they are of size \"\n                 << active_gr_vars->number_of_grid_points());\n      using std::swap;\n      swap(*active_gr_vars, *inactive_gr_vars);\n    }\n  }\n}\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SwapGrTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief Swaps the inactive and active GR variables.\n *\n * The values on the subcells are at the cell-centers.\n *\n * It should be possible to reduce memory usage by deallocating the GR variables\n * on the DG grid when switching to subcell. However, the opposite case is not\n * true since the GR variables are needed on the subcells if a neighbor is using\n * subcell in order to compute the neighbor's fluxes.\n *\n * \\note The `active_grid` is the grid we are swapping to, which may be the same\n * as the current grid. On output the `active_gr_vars` will match the grid that\n * `active_grid` is. This mutator is a no-op if they matched on input.\n */\nstruct SwapGrTags {\n  using return_tags = tmpl::list<typename System::spacetime_variables_tag,\n                                 evolution::dg::subcell::Tags::Inactive<\n                                     typename System::spacetime_variables_tag>>;\n  using argument_tags =\n      tmpl::list<::domain::Tags::Mesh<3>, evolution::dg::subcell::Tags::Mesh<3>,\n                 evolution::dg::subcell::Tags::ActiveGrid>;\n\n  static void apply(\n      gsl::not_null<\n          Variables<typename System::spacetime_variables_tag::tags_list>*>\n          active_gr_vars,\n      gsl::not_null<typename evolution::dg::subcell::Tags::Inactive<\n          typename System::spacetime_variables_tag>::type*>\n          inactive_gr_vars,\n      const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n      evolution::dg::subcell::ActiveGrid active_grid);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnDgGrid.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\ntemplate <typename RecoveryScheme>\nstd::tuple<int, evolution::dg::subcell::RdmpTciData>\nTciOnDgGrid<RecoveryScheme>::apply(\n    const gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> dg_prim_vars,\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const EquationsOfState::EquationOfState<true, 3>& eos,\n    const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const TciOptions& tci_options,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n        primitive_from_conservative_options,\n    const double persson_exponent, const bool element_stays_on_dg) {\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{};\n\n  using std::max;\n  using std::min;\n  const size_t num_dg_pts = dg_mesh.number_of_grid_points();\n  const size_t num_subcell_pts = subcell_mesh.number_of_grid_points();\n  DataVector temp_buffer{4 * num_subcell_pts + num_dg_pts};\n  size_t offset_into_temp_buffer = 0;\n  const auto assign_data =\n      [&temp_buffer, &offset_into_temp_buffer](\n          const gsl::not_null<Scalar<DataVector>*> to_assign,\n          const size_t size) {\n        ASSERT(offset_into_temp_buffer + size <= temp_buffer.size(),\n               \"Trying to assign data out of allocated memory size\");\n        get(*to_assign)\n            .set_data_ref(temp_buffer.data() + offset_into_temp_buffer, size);\n        offset_into_temp_buffer += size;\n      };\n  Scalar<DataVector> subcell_tilde_d{};\n  assign_data(make_not_null(&subcell_tilde_d), num_subcell_pts);\n  evolution::dg::subcell::fd::project(make_not_null(&get(subcell_tilde_d)),\n                                      get(tilde_d), dg_mesh,\n                                      subcell_mesh.extents());\n\n  Scalar<DataVector> subcell_tilde_ye{};\n  assign_data(make_not_null(&subcell_tilde_ye), num_subcell_pts);\n  evolution::dg::subcell::fd::project(make_not_null(&get(subcell_tilde_ye)),\n                                      get(tilde_ye), dg_mesh,\n                                      subcell_mesh.extents());\n\n  Scalar<DataVector> subcell_tilde_tau{};\n  assign_data(make_not_null(&subcell_tilde_tau), num_subcell_pts);\n  evolution::dg::subcell::fd::project(make_not_null(&get(subcell_tilde_tau)),\n                                      get(tilde_tau), dg_mesh,\n                                      subcell_mesh.extents());\n\n  Scalar<DataVector> mag_tilde_b{};\n  assign_data(make_not_null(&mag_tilde_b), num_dg_pts);\n  magnitude(make_not_null(&mag_tilde_b), tilde_b);\n  Scalar<DataVector> subcell_mag_tilde_b{};\n  assign_data(make_not_null(&subcell_mag_tilde_b), num_subcell_pts);\n  evolution::dg::subcell::fd::project(make_not_null(&get(subcell_mag_tilde_b)),\n                                      get(mag_tilde_b), dg_mesh,\n                                      subcell_mesh.extents());\n  const double max_mag_tilde_b = max(get(mag_tilde_b));\n\n  rdmp_tci_data.max_variables_values =\n      DataVector{max(max(get(subcell_tilde_d)), max(get(tilde_d))),\n                 max(max(get(subcell_tilde_ye)), max(get(tilde_ye))),\n                 max(max(get(subcell_tilde_tau)), max(get(tilde_tau))),\n                 max(max(get(subcell_mag_tilde_b)), max_mag_tilde_b)};\n  rdmp_tci_data.min_variables_values =\n      DataVector{min(min(get(subcell_tilde_d)), min(get(tilde_d))),\n                 min(min(get(subcell_tilde_ye)), min(get(tilde_ye))),\n                 min(min(get(subcell_tilde_tau)), min(get(tilde_tau))),\n                 min(min(get(subcell_mag_tilde_b)), min(get(mag_tilde_b)))};\n\n  const double average_sqrt_det_spatial_metric =\n      l1Norm(get(sqrt_det_spatial_metric));\n\n  Variables<hydro::grmhd_tags<DataVector>> pre_tci_prims(num_dg_pts);\n\n  // Get a pressure guess that may be needed for con2prim\n  get<hydro::Tags::Pressure<DataVector>>(pre_tci_prims) =\n      get<hydro::Tags::Pressure<DataVector>>(*dg_prim_vars);\n\n  // require: tilde_d/avg(sqrt{gamma}) >= 0.0 (or some positive user-specified\n  // value)\n  if (min(get(tilde_d)) / average_sqrt_det_spatial_metric <\n          tci_options.minimum_rest_mass_density_times_lorentz_factor or\n      min(get(subcell_tilde_d)) / average_sqrt_det_spatial_metric <\n          tci_options.minimum_rest_mass_density_times_lorentz_factor or\n      min(get(tilde_ye)) / average_sqrt_det_spatial_metric <\n          tci_options.minimum_rest_mass_density_times_lorentz_factor *\n              tci_options.minimum_ye or\n      min(get(subcell_tilde_ye)) / average_sqrt_det_spatial_metric <\n          tci_options.minimum_rest_mass_density_times_lorentz_factor *\n              tci_options.minimum_ye) {\n    return {-1, std::move(rdmp_tci_data)};\n  }\n\n  // require: tilde_tau >= 0.0 (or some positive user-specified value)\n  if (min(get(tilde_tau)) < tci_options.minimum_tilde_tau or\n      min(get(subcell_tilde_tau)) < tci_options.minimum_tilde_tau) {\n    return {-2, std::move(rdmp_tci_data)};\n  }\n\n  // Calculate con2prim up front for primitive variable use later.\n  // Determine if con2prim is successful\n  const bool successful_con2prim_transformation =\n      grmhd::ValenciaDivClean::PrimitiveFromConservative<\n          tmpl::list<RecoveryScheme>, false>::\n          template apply<false>(\n              make_not_null(&get<hydro::Tags::RestMassDensity<DataVector>>(\n                  pre_tci_prims)),\n              make_not_null(&get<hydro::Tags::ElectronFraction<DataVector>>(\n                  pre_tci_prims)),\n              make_not_null(\n                  &get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                      pre_tci_prims)),\n              make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n                  pre_tci_prims)),\n              make_not_null(&get<hydro::Tags::MagneticField<DataVector, 3>>(\n                  pre_tci_prims)),\n              make_not_null(\n                  &get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n                      pre_tci_prims)),\n              make_not_null(\n                  &get<hydro::Tags::LorentzFactor<DataVector>>(pre_tci_prims)),\n              make_not_null(\n                  &get<hydro::Tags::Pressure<DataVector>>(pre_tci_prims)),\n              make_not_null(\n                  &get<hydro::Tags::Temperature<DataVector>>(pre_tci_prims)),\n              tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n              spatial_metric, inv_spatial_metric, sqrt_det_spatial_metric, eos,\n              primitive_from_conservative_options);\n\n  // This lambda is called before every TCI failure\n  // in order to allow primitives to be updated, rather\n  // than defaulting to primitive variables from\n  // the previous timestep\n  const auto& equate_pre_tci_prims = [&dg_prim_vars, &pre_tci_prims,\n                                      &element_stays_on_dg]() {\n    if (element_stays_on_dg) {\n      *dg_prim_vars = std::move(pre_tci_prims);\n    }\n  };\n\n  // Check if we are in atmosphere (but not negative tildeD), and if so, then\n  // we continue using DG on this element.\n  if (max(get(tilde_d) /\n          (get(sqrt_det_spatial_metric) *\n           get(get<hydro::Tags::LorentzFactor<DataVector>>(*dg_prim_vars)))) <\n          tci_options.atmosphere_density and\n      max(get(get<hydro::Tags::RestMassDensity<DataVector>>(*dg_prim_vars))) <\n          tci_options.atmosphere_density) {\n    // In atmosphere, we only need to recover the primitive variables for the\n    // magnetic field and divergence cleaning field\n    for (size_t i = 0; i < 3; ++i) {\n      get<hydro::Tags::MagneticField<DataVector, 3>>(*dg_prim_vars).get(i) =\n          tilde_b.get(i) / get(sqrt_det_spatial_metric);\n    }\n    get(get<hydro::Tags::DivergenceCleaningField<DataVector>>(*dg_prim_vars)) =\n        get(tilde_phi) / get(sqrt_det_spatial_metric);\n\n    return {+0, std::move(rdmp_tci_data)};\n  }\n\n  {\n    // require: tilde{B}^2 <= 2sqrt{gamma}(1-epsilon_B)\\tilde{tau}\n    Scalar<DataVector>& tilde_b_squared =\n        get<hydro::Tags::RestMassDensity<DataVector>>(*dg_prim_vars);\n    dot_product(make_not_null(&tilde_b_squared), tilde_b, tilde_b,\n                spatial_metric);\n    for (size_t i = 0; i < num_dg_pts; ++i) {\n      if (get(tilde_b_squared)[i] >\n          (1.0 - tci_options.safety_factor_for_magnetic_field) * 2.0 *\n              get(tilde_tau)[i] * get(sqrt_det_spatial_metric)[i]) {\n        equate_pre_tci_prims();\n        return {-3, std::move(rdmp_tci_data)};\n      }\n    }\n  }\n\n  // Try to recover the primitive variables.\n  // We assign them to a temporary so that if recovery fails at any of the\n  // points we can use the valid primitives at the current time to provide a\n  // high-order initial guess for the recovery on the subcells.\n\n  if (not successful_con2prim_transformation) {\n    equate_pre_tci_prims();\n    return {-4, std::move(rdmp_tci_data)};\n  }\n\n  // Check if we are in atmosphere after recovery. Unlikely we'd hit this and\n  // not the check before the recovery, but just in case.\n  if (max(get(get<hydro::Tags::RestMassDensity<DataVector>>(pre_tci_prims))) <\n      tci_options.atmosphere_density) {\n    equate_pre_tci_prims();\n    return {+0, std::move(rdmp_tci_data)};\n  }\n\n  // Check that tilde_d, tilde_ye, and pressure satisfy the Persson TCI\n  if (evolution::dg::subcell::persson_tci(\n          tilde_d, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    equate_pre_tci_prims();\n    return {-5, std::move(rdmp_tci_data)};\n  }\n  if (evolution::dg::subcell::persson_tci(\n          tilde_ye, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    equate_pre_tci_prims();\n    return {-6, std::move(rdmp_tci_data)};\n  }\n  if (evolution::dg::subcell::persson_tci(\n          get<hydro::Tags::Pressure<DataVector>>(*dg_prim_vars), dg_mesh,\n          persson_exponent, subcell_options.persson_num_highest_modes())) {\n    equate_pre_tci_prims();\n    return {-7, std::move(rdmp_tci_data)};\n  }\n  // Check Cartesian magnitude of magnetic field satisfies the Persson TCI\n  if (tci_options.magnetic_field_cutoff.has_value() and\n      max_mag_tilde_b > tci_options.magnetic_field_cutoff.value() and\n      evolution::dg::subcell::persson_tci(\n          mag_tilde_b, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes())) {\n    equate_pre_tci_prims();\n    return {-8, std::move(rdmp_tci_data)};\n  }\n\n  if (const int rdmp_tci_status = evolution::dg::subcell::rdmp_tci(\n          rdmp_tci_data.max_variables_values,\n          rdmp_tci_data.min_variables_values,\n          past_rdmp_tci_data.max_variables_values,\n          past_rdmp_tci_data.min_variables_values,\n          subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon())) {\n    equate_pre_tci_prims();\n    return {-(8 + rdmp_tci_status), std::move(rdmp_tci_data)};\n  }\n\n  // If no TCI failures, assign proper primitives variables\n  *dg_prim_vars = std::move(pre_tci_prims);\n  return {+0, std::move(rdmp_tci_data)};\n}\n\n#define RECOVERY(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) template class TciOnDgGrid<RECOVERY(data)>;\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION,\n    (grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n     grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n     grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl))\n#undef INSTANTIATION\n#undef RECOVERY\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnDgGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace evolution::dg::subcell {\nstruct RdmpTciData;\nclass SubcellOptions;\n}  // namespace evolution::dg::subcell\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief The troubled-cell indicator run on the DG grid to check if the\n * solution is admissible.\n *\n * We denote variables at the candidate solution's time level by a superscript\n * \\f$n+1\\f$ and at the time level where the solution is known to be admissible\n * by a superscript \\f$n\\f$.\n *\n * The following checks are done in the order they are listed:\n *\n * <table>\n * <caption>List of checks</caption>\n * <tr><th> Description <th> TCI status\n *\n * <tr><td> if \\f$\\min(\\tilde{D}^{n+1}/\\textrm{avg}(\\sqrt{\\gamma^{n}}))\\f$\n * is less than `tci_options.minimum_rest_mass_density_times_lorentz_factor`\n * or if \\f$\\min(\\tilde{Y}_e^{n+1}/\\textrm{avg}(\\sqrt{\\gamma^{n}}))\\f$ is less\n * than `tci_options.minimum_rest_mass_density_times_lorentz_factor` times\n * `tci_options.minimum_ye`, we have a negative (or extremely small) density or\n * electron fraction and the cell is troubled. Note that if this `tci_option` is\n * approximately equal to or larger than the `atmosphere_density`, the\n * atmosphere will be flagged as troubled.\n * <td> `-1`\n *\n * <tr><td> if \\f$\\tilde{\\tau}\\f$ is less than `tci_options.minimum_tilde_tau`\n * then we have a negative (or extremely small) energy and the cell is troubled.\n * <td> `-2`\n *\n * <tr><td> if \\f$\\max(\\tilde{D}^{n+1}/(\\sqrt{\\gamma^n}W^n))\\f$ and\n * \\f$\\max(\\rho^n)\\f$ are less than `tci_options.atmosphere_density` then the\n * entire DG element is in atmosphere and it is _not_ troubled.\n * <td> `0`\n *\n * <tr><td> if\n * \\f$(\\tilde{B}^{n+1})^2>2\\sqrt{\\gamma^n}(1-\\epsilon_B)\\tilde{\\tau}^{n+1}\\f$ at\n * any grid point, then the cell is troubled.\n * <td> `-3`\n *\n * <tr><td> attempt a primitive recovery using the `RecoveryScheme` from the\n * template parameter. The cell is marked as troubled if the primitive recovery\n * fails at any grid point.\n * <td> `-4`\n *\n * <tr><td> if \\f$\\max(\\rho^{n+1})\\f$ is below `tci_options.atmosphere_density`\n * then the cell is in atmosphere and not marked as troubled. Note that the\n * magnetic field is still freely evolved.\n * <td> `0`\n *\n * <tr><td> apply the Persson TCI to \\f$\\tilde{D}^{n+1}\\f$\n * <td> `-5`\n *\n * <tr><td> apply the Persson TCI to \\f$\\tilde{Y}_e^{n+1}\\f$\n * <td> `-6`\n *\n * <tr><td> apply the Persson TCI to pressure \\f$p^{n+1}\\f$\n * <td> `-7`\n *\n * <tr><td> apply the Persson TCI to the magnitude of \\f$\\tilde{B}^{n+1}\\f$ if\n * its magnitude is greater than `tci_options.magnetic_field_cutoff`\n * <td> `-8`\n *\n * <tr><td> apply the RDMP TCI to `TildeD`\n * <td> `-9`\n *\n * <tr><td> apply the RDMP TCI to `TildeTau`\n * <td> `-10`\n *\n * <tr><td> apply the RDMP TCI to `TildeB`\n * <td> `-11`\n *\n * </table>\n *\n * If the cell is not flagged as troubled then the primitives are computed at\n * time level `n+1`.\n *\n * The second column of the table above denotes the value of an integer stored\n * as the first element of the returned `std::tuple`, which indicates the\n * particular kind of check that failed. For example, if the fifth check\n * (primitive recovery) fails and cell is marked as troubled, an integer with\n * value `-4` is stored in the first slot of the returned tuple. Note that this\n * integer is marking only the _first_ check to fail, since checks are done in a\n * particular sequence as listed above. If all checks are passed and cell is not\n * troubled, it is returned with the value `0`.\n *\n * \\note We adopt negative integers to mark TCI status from DG grid returned by\n * TciOnDgGrid class. Positive integers are used for TCIs on FD grid; see\n * TciOnFdGrid and its documentation.\n *\n */\ntemplate <typename RecoveryScheme>\nclass TciOnDgGrid {\n public:\n  using return_tags =\n      tmpl::list<::Tags::Variables<hydro::grmhd_tags<DataVector>>>;\n  using argument_tags = tmpl::list<\n      grmhd::ValenciaDivClean::Tags::TildeD,\n      grmhd::ValenciaDivClean::Tags::TildeYe,\n      grmhd::ValenciaDivClean::Tags::TildeTau,\n      grmhd::ValenciaDivClean::Tags::TildeS<>,\n      grmhd::ValenciaDivClean::Tags::TildeB<>,\n      grmhd::ValenciaDivClean::Tags::TildePhi,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      hydro::Tags::GrmhdEquationOfState, domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::DataForRdmpTci, Tags::TciOptions,\n      evolution::dg::subcell::Tags::SubcellOptions<3>,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>;\n\n  static std::tuple<int, evolution::dg::subcell::RdmpTciData> apply(\n      gsl::not_null<Variables<hydro::grmhd_tags<DataVector>>*> dg_prim_vars,\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const EquationsOfState::EquationOfState<true, 3>& eos,\n      const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const TciOptions& tci_options,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions&\n          primitive_from_conservative_options,\n      double persson_exponent, bool element_stays_on_dg);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnFdGrid.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\nstd::tuple<int, evolution::dg::subcell::RdmpTciData> TciOnFdGrid::apply(\n    const Scalar<DataVector>& subcell_tilde_d,\n    const Scalar<DataVector>& subcell_tilde_ye,\n    const Scalar<DataVector>& subcell_tilde_tau,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_b,\n    const Scalar<DataVector>& subcell_rest_mass_density,\n    const Scalar<DataVector>& subcell_pressure, const bool vars_needed_fixing,\n    const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const TciOptions& tci_options,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const double persson_exponent, const bool need_rdmp_data_only) {\n  const size_t num_dg_pts = dg_mesh.number_of_grid_points();\n  const size_t num_subcell_pts = subcell_mesh.number_of_grid_points();\n  DataVector temp_buffer{num_subcell_pts + 5 * num_dg_pts};\n  size_t offset_into_temp_buffer = 0;\n  const auto assign_data =\n      [&temp_buffer, &offset_into_temp_buffer](\n          const gsl::not_null<Scalar<DataVector>*> to_assign,\n          const size_t size) {\n        ASSERT(offset_into_temp_buffer + size <= temp_buffer.size(),\n               \"Trying to assign data out of allocated memory size\");\n        get(*to_assign)\n            .set_data_ref(temp_buffer.data() + offset_into_temp_buffer, size);\n        offset_into_temp_buffer += size;\n      };\n\n  Scalar<DataVector> subcell_mag_tilde_b{};\n  assign_data(make_not_null(&subcell_mag_tilde_b), num_subcell_pts);\n  magnitude(make_not_null(&subcell_mag_tilde_b), subcell_tilde_b);\n\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{};\n  rdmp_tci_data.max_variables_values =\n      DataVector{max(get(subcell_tilde_d)), max(get(subcell_tilde_ye)),\n                 max(get(subcell_tilde_tau)), max(get(subcell_mag_tilde_b))};\n  rdmp_tci_data.min_variables_values =\n      DataVector{min(get(subcell_tilde_d)), min(get(subcell_tilde_ye)),\n                 min(get(subcell_tilde_tau)), min(get(subcell_mag_tilde_b))};\n\n  if (need_rdmp_data_only) {\n    return {+0, rdmp_tci_data};\n  }\n\n  Scalar<DataVector> dg_tilde_d{};\n  assign_data(make_not_null(&dg_tilde_d), num_dg_pts);\n  evolution::dg::subcell::fd::reconstruct(\n      make_not_null(&get(dg_tilde_d)), get(subcell_tilde_d), dg_mesh,\n      subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n\n  Scalar<DataVector> dg_tilde_ye{};\n  assign_data(make_not_null(&dg_tilde_ye), num_dg_pts);\n  evolution::dg::subcell::fd::reconstruct(\n      make_not_null(&get(dg_tilde_ye)), get(subcell_tilde_ye), dg_mesh,\n      subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n  Scalar<DataVector> dg_tilde_tau{};\n  assign_data(make_not_null(&dg_tilde_tau), num_dg_pts);\n  evolution::dg::subcell::fd::reconstruct(\n      make_not_null(&get(dg_tilde_tau)), get(subcell_tilde_tau), dg_mesh,\n      subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n\n  if (min(get(dg_tilde_d)) <\n          tci_options.minimum_rest_mass_density_times_lorentz_factor) {\n    return {+1, rdmp_tci_data};\n  }\n  if (min(get(dg_tilde_ye)) <\n          tci_options.minimum_rest_mass_density_times_lorentz_factor *\n              tci_options.minimum_ye) {\n    return {+2, rdmp_tci_data};\n  }\n  if (min(get(dg_tilde_tau)) < tci_options.minimum_tilde_tau) {\n    return {+3, rdmp_tci_data};\n  }\n\n  const bool in_atmosphere =\n      max(get(subcell_rest_mass_density)) < tci_options.atmosphere_density;\n\n  if (not(in_atmosphere) and vars_needed_fixing) {\n    return {+4, rdmp_tci_data};\n  }\n\n  if (not in_atmosphere) {\n    Scalar<DataVector> dg_pressure{};\n    assign_data(make_not_null(&dg_pressure), num_dg_pts);\n    evolution::dg::subcell::fd::reconstruct(\n        make_not_null(&get(dg_pressure)), get(subcell_pressure), dg_mesh,\n        subcell_mesh.extents(),\n        evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n\n    if (evolution::dg::subcell::persson_tci(\n            dg_tilde_d, dg_mesh, persson_exponent,\n            subcell_options.persson_num_highest_modes())) {\n      return {+5, rdmp_tci_data};\n    }\n    if (evolution::dg::subcell::persson_tci(\n            dg_tilde_ye, dg_mesh, persson_exponent,\n            subcell_options.persson_num_highest_modes())) {\n      return {+6, rdmp_tci_data};\n    }\n    if (evolution::dg::subcell::persson_tci(\n            dg_pressure, dg_mesh, persson_exponent,\n            subcell_options.persson_num_highest_modes())) {\n      return {+7, rdmp_tci_data};\n    }\n  }\n\n  Scalar<DataVector> dg_mag_tilde_b{};\n  assign_data(make_not_null(&dg_mag_tilde_b), num_dg_pts);\n  evolution::dg::subcell::fd::reconstruct(\n      make_not_null(&get(dg_mag_tilde_b)), get(subcell_mag_tilde_b), dg_mesh,\n      subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n  // Add the reconstructed DG solution to the check. This is done so that we\n  // wouldn't violate RDMP if we switch back. However, we don't want to return\n  // max/mins from a bad DG solution.\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data_for_check{};\n  rdmp_tci_data_for_check.max_variables_values = DataVector{\n      max(max(get(dg_tilde_d)), rdmp_tci_data.max_variables_values[0]),\n      max(max(get(dg_tilde_ye)), rdmp_tci_data.max_variables_values[1]),\n      max(max(get(dg_tilde_tau)), rdmp_tci_data.max_variables_values[2]),\n      max(max(get(dg_mag_tilde_b)), rdmp_tci_data.max_variables_values[3])};\n  rdmp_tci_data_for_check.min_variables_values = DataVector{\n      min(min(get(dg_tilde_d)), rdmp_tci_data.min_variables_values[0]),\n      min(min(get(dg_tilde_ye)), rdmp_tci_data.min_variables_values[1]),\n      min(min(get(dg_tilde_tau)), rdmp_tci_data.min_variables_values[2]),\n      min(min(get(dg_mag_tilde_b)), rdmp_tci_data.min_variables_values[3])};\n\n  if (const int rdmp_tci_status = evolution::dg::subcell::rdmp_tci(\n          rdmp_tci_data_for_check.max_variables_values,\n          rdmp_tci_data_for_check.min_variables_values,\n          past_rdmp_tci_data.max_variables_values,\n          past_rdmp_tci_data.min_variables_values,\n          subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon())) {\n    return {+7 + rdmp_tci_status, rdmp_tci_data};\n  }\n\n  if (tci_options.magnetic_field_cutoff.has_value() and\n      (max(get(dg_mag_tilde_b)) > tci_options.magnetic_field_cutoff.value() and\n       evolution::dg::subcell::persson_tci(\n           dg_mag_tilde_b, dg_mesh, persson_exponent,\n           subcell_options.persson_num_highest_modes()))) {\n    return {+12, rdmp_tci_data};\n  }\n\n  return {+0, rdmp_tci_data};\n}\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnFdGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace evolution::dg::subcell {\nclass SubcellOptions;\n}  // namespace evolution::dg::subcell\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief The troubled-cell indicator run on the FD grid to check if the\n * corresponding DG solution is admissible.\n *\n * The following checks are done in the order they are listed:\n *\n * <table>\n * <caption>List of checks</caption>\n * <tr><th> Description <th> TCI status\n *\n * <tr><td> the element is not troubled.\n * <td> `+0`\n *\n * <tr><td> if `min(tilde_d)` is less than\n *  `tci_options.minimum_rest_mass_density_times_lorentz_factor`, then we\n *  remain on FD.\n * <td> `+1`\n *\n * <tr><td> if `min(tilde_ye)` is less than\n *  `tci_options.minimum_rest_mass_density_times_lorentz_factor` times\n *  `tci_options.minimum_ye`, then we remain on FD.\n * <td> `+2`\n *\n * <tr><td> if `min(tilde_tau)` is less than\n *  `tci_options.minimum_tilde_tau`, then we remain on FD.\n * <td> `+3`\n *\n * <tr><td> if `grmhd::ValenciaDivClean::Tags::VariablesNeededFixing` is `true`\n *  and the maximum of rest mass density on FD grid is greater than\n * `tci_options.atmosphere_density`, then we remain on FD.\n * <td> `+4`\n *\n * <tr><td> apply the Persson TCI to \\f$\\tilde{D}\\f$ if the maximum of rest\n * mass density on FD grid is greater than `tci_options.atmosphere_density`.\n * <td> `+5`\n *\n * <tr><td> apply the Persson TCI to \\f$\\tilde{Y}_e\\f$ if the maximum of rest\n * mass density on FD grid is greater than `tci_options.atmosphere_density`.\n * <td> `+6`\n *\n * <tr><td> apply the Persson TCI to pressure if the maximum of rest mass\n * density on FD grid is greater than `tci_options.atmosphere_density`. <td>\n * `+7`\n *\n * <tr><td> apply the RDMP TCI to `TildeD`\n * <td> `+8`\n *\n * <tr><td> apply the RDMP TCI to `TildeYe`\n * <td> `+9`\n *\n * <tr><td> apply the RDMP TCI to `TildeTau`\n * <td> `+10`\n *\n * <tr><td> apply the RDMP TCI to `TildeB`\n * <td> `+11`\n *\n * <tr><td> apply the Persson TCI to the magnitude of \\f$\\tilde{B}^{n+1}\\f$ if\n * its magnitude is greater than `tci_options.magnetic_field_cutoff`.\n * <td> `+12`\n *\n * </table>\n *\n * The second column of the table above denotes the value of an integer stored\n * as the first element of the returned `std::tuple`, which indicates the\n * particular kind of check that failed. For example, if the tenth check\n * (RDMP TCI to TildeTau) fails and cell is marked as troubled, an integer with\n * value `+10` is stored in the first slot of the returned tuple. Note that this\n * integer is marking only the _first_ check to fail, since checks are done in a\n * particular sequence as listed above. If all checks are passed and cell is not\n * troubled, it is returned with the value `0`.\n *\n * \\note We adopt positive integers to mark TCI status from FD grid returned by\n * TciOnFdGrid class. Negative integers are reserved for TCIs on DG grid; see\n * TciOnDgGrid and its documentation.\n *\n */\nstruct TciOnFdGrid {\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                 grmhd::ValenciaDivClean::Tags::TildeYe,\n                 grmhd::ValenciaDivClean::Tags::TildeTau,\n                 grmhd::ValenciaDivClean::Tags::TildeB<>,\n                 hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 grmhd::ValenciaDivClean::Tags::VariablesNeededFixing,\n                 domain::Tags::Mesh<3>, evolution::dg::subcell::Tags::Mesh<3>,\n                 evolution::dg::subcell::Tags::DataForRdmpTci, Tags::TciOptions,\n                 evolution::dg::subcell::Tags::SubcellOptions<3>>;\n  static std::tuple<int, evolution::dg::subcell::RdmpTciData> apply(\n      const Scalar<DataVector>& subcell_tilde_d,\n      const Scalar<DataVector>& subcell_tilde_ye,\n      const Scalar<DataVector>& subcell_tilde_tau,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& subcell_tilde_b,\n      const Scalar<DataVector>& subcell_rest_mass_density,\n      const Scalar<DataVector>& subcell_pressure, bool vars_needed_fixing,\n      const Mesh<3>& dg_mesh, const Mesh<3>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const TciOptions& tci_options,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      double persson_exponent, bool need_rdmp_data_only);\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp\"\n\n#include <pup.h>\n\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\nTciOptions::TciOptions() = default;\nTciOptions::TciOptions(\n    const double minimum_rest_mass_density_times_lorentz_factor_in,\n    const double minimum_ye_in, const double minimum_tilde_tau_in,\n    const double atmosphere_density_in,\n    const double safety_factor_for_magnetic_field_in,\n    const std::optional<double> magnetic_field_cutoff_in)\n    : minimum_rest_mass_density_times_lorentz_factor(\n          minimum_rest_mass_density_times_lorentz_factor_in),\n      minimum_ye(minimum_ye_in),\n      minimum_tilde_tau(minimum_tilde_tau_in),\n      atmosphere_density(atmosphere_density_in),\n      safety_factor_for_magnetic_field(safety_factor_for_magnetic_field_in),\n      magnetic_field_cutoff(magnetic_field_cutoff_in) {}\n\nvoid TciOptions::pup(PUP::er& p) {\n  p | minimum_rest_mass_density_times_lorentz_factor;\n  p | minimum_ye;\n  p | minimum_tilde_tau;\n  p | atmosphere_density;\n  p | safety_factor_for_magnetic_field;\n  p | magnetic_field_cutoff;\n}\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/OptionsGroup.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief Class holding options using by the GRMHD-specific parts of the\n * troubled-cell indicator.\n */\nstruct TciOptions {\n private:\n  struct DoNotCheckMagneticField {};\n\n public:\n  /// \\brief Minimum value of rest-mass density times Lorentz factor before we\n  /// switch to subcell. Used to identify places where the density has suddenly\n  /// become negative\n  struct MinimumValueOfD {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Minimum value of rest-mass density times Lorentz factor before we \"\n        \"switch to subcell.\"};\n  };\n  /// \\brief Minimum value of \\f$Y_e\\f$ before we switch to subcell.\n  /// Used to identify places where the electron fraction has suddenly become\n  //  negative\n  struct MinimumValueOfYe {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Minimum value of Y_e before we switch to subcell.\"};\n  };\n  /// \\brief Minimum value of \\f$\\tilde{\\tau}\\f$ before we switch to subcell.\n  /// Used to identify places where the energy has suddenly become negative\n  struct MinimumValueOfTildeTau {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Minimum value of tilde tau before we switch to subcell.\"};\n  };\n  /// \\brief The density cutoff where if the maximum value of the density in the\n  /// DG element is below this value we skip primitive recovery and treat the\n  /// cell as atmosphere.\n  struct AtmosphereDensity {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The density cutoff where if the maximum value of the density in the \"\n        \"DG element is below this value we skip primitive recovery and treat \"\n        \"the cell as atmosphere.\"};\n  };\n  /// \\brief Safety factor \\f$\\epsilon_B\\f$.\n  ///\n  /// See the documentation for TciOnDgGrid for details on what this parameter\n  /// controls.\n  struct SafetyFactorForB {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Safety factor for magnetic field bound.\"};\n  };\n  /// \\brief The cutoff where if the maximum of the magnetic field in an element\n  /// is below this value we do not apply the Persson TCI to the magnetic field.\n  struct MagneticFieldCutoff {\n    using type = Options::Auto<double, DoNotCheckMagneticField>;\n    static constexpr Options::String help = {\n        \"The cutoff where if the maximum of the magnetic field in an element \"\n        \"is below this value we do not apply the Persson TCI to the magnetic \"\n        \"field. This is to avoid switching to subcell in regions where there's \"\n        \"no magnetic field.\\n\"\n        \"To disable the magnetic field check, set to \"\n        \"'DoNotCheckMagneticField'.\"};\n  };\n\n  using options =\n      tmpl::list<MinimumValueOfD, MinimumValueOfYe, MinimumValueOfTildeTau,\n                 AtmosphereDensity, SafetyFactorForB, MagneticFieldCutoff>;\n  static constexpr Options::String help = {\n      \"Options for the troubled-cell indicator.\"};\n\n  TciOptions();\n  TciOptions(double minimum_rest_mass_density_times_lorentz_factor_in,\n             double minimum_ye_in, double minimum_tilde_tau_in,\n             double atmosphere_density_in,\n             double safety_factor_for_magnetic_field_in,\n             std::optional<double> magnetic_field_cutoff_in);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  double minimum_rest_mass_density_times_lorentz_factor{\n      std::numeric_limits<double>::signaling_NaN()};\n  double minimum_ye{std::numeric_limits<double>::signaling_NaN()};\n  double minimum_tilde_tau{std::numeric_limits<double>::signaling_NaN()};\n  double atmosphere_density{std::numeric_limits<double>::signaling_NaN()};\n  double safety_factor_for_magnetic_field{\n      std::numeric_limits<double>::signaling_NaN()};\n  // The signaling_NaN default is chosen so that users hit an error/FPE if the\n  // cutoff is not specified, rather than silently defaulting to ignoring the\n  // magnetic field.\n  std::optional<double> magnetic_field_cutoff{\n      std::numeric_limits<double>::signaling_NaN()};\n};\n\nnamespace OptionTags {\nstruct TciOptions {\n  using type = subcell::TciOptions;\n  static constexpr Options::String help = \"GRMHD-specific options for the TCI.\";\n  using group = ::dg::OptionTags::DiscontinuousGalerkinGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\nstruct TciOptions : db::SimpleTag {\n  using type = subcell::TciOptions;\n\n  using option_tags = tmpl::list<OptionTags::TciOptions>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& tci_options) {\n    return tci_options;\n  }\n};\n}  // namespace Tags\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/ComputeBoundaryTerms.hpp\"\n#include \"Evolution/DgSubcell/CorrectPackagedData.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionOrder.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/BoundaryConditionGhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Sources.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/HighOrderFluxCorrection.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::subcell {\n/*!\n * \\brief Compute the time derivative on the subcell grid using FD\n * reconstruction.\n */\nstruct TimeDerivative {\n  template <typename DbTagsList>\n  static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n    using metavariables =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>;\n    using evolved_vars_tag = typename System::variables_tag;\n    using evolved_vars_tags = typename evolved_vars_tag::tags_list;\n    using prim_tags = typename System::primitive_variables_tag::tags_list;\n    using recons_prim_tags = tmpl::push_back<\n        prim_tags,\n        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>;\n    using fluxes_tags = db::wrap_tags_in<::Tags::Flux, evolved_vars_tags,\n                                         tmpl::size_t<3>, Frame::Inertial>;\n\n    ASSERT(\n        (db::get<::domain::CoordinateMaps::Tags::CoordinateMap<\n             3, Frame::Grid, Frame::Inertial>>(*box))\n            .is_identity(),\n        \"Moving mesh is only partly implemented in ValenciaDivClean. If you \"\n        \"need this look at the complete implementation in GhValenciaDivClean. \"\n        \"You will at least need to update the high-order boundary correction \"\n        \"code to include the right normal vectors/Jacobians.\");\n\n    const Mesh<3>& subcell_mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<3>>(*box);\n    const Mesh<3>& dg_mesh = db::get<domain::Tags::Mesh<3>>(*box);\n    ASSERT(\n        is_isotropic(subcell_mesh),\n        \"The subcell/FD mesh must be isotropic for the FD time derivative but \"\n        \"got \"\n            << subcell_mesh);\n    const size_t reconstructed_num_pts =\n        (subcell_mesh.extents(0) + 1) *\n        subcell_mesh.extents().slice_away(0).product();\n\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>&\n        cell_centered_logical_coords =\n            db::get<evolution::dg::subcell::Tags::Coordinates<\n                3, Frame::ElementLogical>>(*box);\n    std::array<double, 3> one_over_delta_xi{};\n    for (size_t i = 0; i < 3; ++i) {\n      // Note: assumes isotropic extents\n      gsl::at(one_over_delta_xi, i) =\n          1.0 / (get<0>(cell_centered_logical_coords)[1] -\n                 get<0>(cell_centered_logical_coords)[0]);\n    }\n\n    // Inverse jacobian, to be projected on faces\n    const auto& inv_jacobian_dg =\n        db::get<domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                              Frame::Inertial>>(*box);\n    const auto& det_inv_jacobian_dg = db::get<\n        domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>>(\n        *box);\n\n    // Velocity of the moving mesh on the DG grid, if applicable.\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        mesh_velocity_dg = db::get<domain::Tags::MeshVelocity<3>>(*box);\n    const std::optional<Scalar<DataVector>>& div_mesh_velocity =\n        db::get<domain::Tags::DivMeshVelocity>(*box);\n\n    const grmhd::ValenciaDivClean::fd::Reconstructor& recons =\n        db::get<grmhd::ValenciaDivClean::fd::Tags::Reconstructor>(*box);\n\n    const Element<3>& element = db::get<domain::Tags::Element<3>>(*box);\n    const auto fd_derivative_order =\n        db::get<evolution::dg::subcell::Tags::SubcellOptions<3>>(*box)\n            .finite_difference_derivative_order();\n    std::optional<std::array<std::vector<std::uint8_t>, 3>>\n        reconstruction_order_data{};\n    std::optional<std::array<gsl::span<std::uint8_t>, 3>>\n        reconstruction_order{};\n    if (static_cast<int>(fd_derivative_order) < 0) {\n      reconstruction_order_data = make_array<3>(std::vector<std::uint8_t>(\n          (subcell_mesh.extents(0) + 2) * subcell_mesh.extents(1) *\n              subcell_mesh.extents(2),\n          std::numeric_limits<std::uint8_t>::max()));\n      reconstruction_order = std::array<gsl::span<std::uint8_t>, 3>{};\n      for (size_t i = 0; i < 3; ++i) {\n        gsl::at(reconstruction_order.value(), i) = gsl::make_span(\n            gsl::at(reconstruction_order_data.value(), i).data(),\n            gsl::at(reconstruction_order_data.value(), i).size());\n      }\n    }\n\n    const bool element_is_interior = element.external_boundaries().empty();\n    constexpr bool subcell_enabled_at_external_boundary =\n        metavariables::SubcellOptions::subcell_enabled_at_external_boundary;\n\n    ASSERT(element_is_interior or subcell_enabled_at_external_boundary,\n           \"Subcell time derivative is called at a boundary element while \"\n           \"using subcell is disabled at external boundaries.\"\n           \"ElementID \"\n               << element.id());\n\n    // Now package the data and compute the correction\n    const auto& boundary_correction =\n        db::get<evolution::Tags::BoundaryCorrection>(*box);\n    using derived_boundary_corrections =\n        tmpl::at<typename metavariables::factory_creation::factory_classes,\n                 evolution::BoundaryCorrection>;\n    std::array<Variables<evolved_vars_tags>, 3> boundary_corrections{};\n\n    // If the element has external boundaries and subcell is enabled for\n    // boundary elements, compute FD ghost data with a given boundary condition.\n    if constexpr (subcell_enabled_at_external_boundary) {\n      if (not element.external_boundaries().empty()) {\n        fd::BoundaryConditionGhostData::apply(box, element, recons);\n      }\n    }\n\n    call_with_dynamic_type<void, derived_boundary_corrections>(\n        &boundary_correction, [&](const auto* derived_correction) {\n          using DerivedCorrection = std::decay_t<decltype(*derived_correction)>;\n          using dg_package_data_temporary_tags =\n              typename DerivedCorrection::dg_package_data_temporary_tags;\n          using dg_package_data_argument_tags = tmpl::append<\n              evolved_vars_tags, recons_prim_tags, fluxes_tags,\n              tmpl::remove_duplicates<tmpl::push_back<\n                  dg_package_data_temporary_tags,\n                  gr::Tags::SpatialMetric<DataVector, 3>,\n                  gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                  gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                  evolution::dg::Actions::detail::NormalVector<3>>>>;\n          // Computed prims and cons on face via reconstruction\n          auto package_data_argvars_lower_face = make_array<3>(\n              Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n          auto package_data_argvars_upper_face = make_array<3>(\n              Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n          // Copy over the face values of the metric quantities.\n          using spacetime_vars_to_copy =\n              tmpl::list<gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n          tmpl::for_each<spacetime_vars_to_copy>(\n              [&package_data_argvars_lower_face,\n               &package_data_argvars_upper_face,\n               &spacetime_vars_on_face =\n                   db::get<evolution::dg::subcell::Tags::OnSubcellFaces<\n                       typename System::flux_spacetime_variables_tag, 3>>(\n                       *box)](auto tag_v) {\n                using tag = tmpl::type_from<decltype(tag_v)>;\n                for (size_t d = 0; d < 3; ++d) {\n                  get<tag>(gsl::at(package_data_argvars_lower_face, d)) =\n                      get<tag>(gsl::at(spacetime_vars_on_face, d));\n                  get<tag>(gsl::at(package_data_argvars_upper_face, d)) =\n                      get<tag>(gsl::at(spacetime_vars_on_face, d));\n                }\n              });\n\n          // Reconstruct data to the face\n          call_with_dynamic_type<void, typename grmhd::ValenciaDivClean::fd::\n                                           Reconstructor::creatable_classes>(\n              &recons, [&box, &package_data_argvars_lower_face,\n                        &package_data_argvars_upper_face,\n                        &reconstruction_order](const auto& reconstructor) {\n                using ReconstructorType =\n                    std::decay_t<decltype(*reconstructor)>;\n                db::apply<\n                    typename ReconstructorType::reconstruction_argument_tags>(\n                    [&package_data_argvars_lower_face,\n                     &package_data_argvars_upper_face, &reconstructor,\n                     &reconstruction_order](const auto&... args) {\n                      if constexpr (ReconstructorType::use_adaptive_order) {\n                        reconstructor->reconstruct(\n                            make_not_null(&package_data_argvars_lower_face),\n                            make_not_null(&package_data_argvars_upper_face),\n                            make_not_null(&reconstruction_order), args...);\n                      } else {\n                        (void)reconstruction_order;\n                        reconstructor->reconstruct(\n                            make_not_null(&package_data_argvars_lower_face),\n                            make_not_null(&package_data_argvars_upper_face),\n                            args...);\n                      }\n                    },\n                    *box);\n              });\n\n          using dg_package_field_tags =\n              typename DerivedCorrection::dg_package_field_tags;\n          // Allocated outside for loop to reduce allocations\n          Variables<dg_package_field_tags> upper_packaged_data{\n              reconstructed_num_pts};\n          Variables<dg_package_field_tags> lower_packaged_data{\n              reconstructed_num_pts};\n\n          // Compute fluxes on faces\n          for (size_t i = 0; i < 3; ++i) {\n            // Build extents of mesh shifted by half a grid cell in direction i\n            const unsigned long& num_subcells_1d = subcell_mesh.extents(0);\n            Index<3> face_mesh_extents(std::array<size_t, 3>{\n                num_subcells_1d, num_subcells_1d, num_subcells_1d});\n            face_mesh_extents[i] = num_subcells_1d + 1;\n\n            auto& vars_upper_face = gsl::at(package_data_argvars_upper_face, i);\n            auto& vars_lower_face = gsl::at(package_data_argvars_lower_face, i);\n            grmhd::ValenciaDivClean::subcell::compute_fluxes(\n                make_not_null(&vars_upper_face));\n            grmhd::ValenciaDivClean::subcell::compute_fluxes(\n                make_not_null(&vars_lower_face));\n\n            // Add moving mesh corrections to the fluxes, if needed\n            std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>\n                mesh_velocity_on_face = {};\n            if (mesh_velocity_dg.has_value()) {\n              // Project mesh velocity on face mesh.\n              // Can we get away with only doing the normal component? It\n              // is also used in the packaged data...\n              mesh_velocity_on_face = tnsr::I<DataVector, 3, Frame::Inertial>{\n                  reconstructed_num_pts};\n              for (size_t j = 0; j < 3; j++) {\n                // j^th component of the velocity on the i^th directed face\n                mesh_velocity_on_face.value().get(j) =\n                    evolution::dg::subcell::fd::project_to_faces(\n                        mesh_velocity_dg.value().get(j), dg_mesh,\n                        face_mesh_extents, i);\n              }\n              tmpl::for_each<evolved_vars_tags>([&vars_upper_face,\n                                                 &vars_lower_face,\n                                                 &mesh_velocity_on_face](\n                                                    auto tag_v) {\n                using tag = tmpl::type_from<decltype(tag_v)>;\n                using flux_tag =\n                    ::Tags::Flux<tag, tmpl::size_t<3>, Frame::Inertial>;\n                using FluxTensor = typename flux_tag::type;\n                const auto& var_upper = get<tag>(vars_upper_face);\n                const auto& var_lower = get<tag>(vars_lower_face);\n                auto& flux_upper = get<flux_tag>(vars_upper_face);\n                auto& flux_lower = get<flux_tag>(vars_lower_face);\n                for (size_t storage_index = 0; storage_index < var_upper.size();\n                     ++storage_index) {\n                  const auto tensor_index =\n                      var_upper.get_tensor_index(storage_index);\n                  for (size_t j = 0; j < 3; j++) {\n                    const auto flux_storage_index =\n                        FluxTensor::get_storage_index(prepend(tensor_index, j));\n                    flux_upper[flux_storage_index] -=\n                        mesh_velocity_on_face.value().get(j) *\n                        var_upper[storage_index];\n                    flux_lower[flux_storage_index] -=\n                        mesh_velocity_on_face.value().get(j) *\n                        var_lower[storage_index];\n                  }\n                }\n              });\n            }\n\n            // Normal vectors in curved spacetime normalized by inverse\n            // spatial metric. Note that we use the sign convention on\n            // the normal vectors to be compatible with DG.\n            //\n            // Note that these normal vectors are on all faces inside the DG\n            // element since there are a bunch of subcells. We don't use the\n            // NormalCovectorAndMagnitude tag in the DataBox right now to avoid\n            // conflicts with the DG solver. We can explore in the future if\n            // it's possible to reuse that allocation.\n            //\n            // The unnormalized normal vector is\n            // n_j = d \\xi^{\\hat i}/dx^j\n            // with \"i\" the current face.\n            tnsr::i<DataVector, 3, Frame::Inertial> lower_outward_conormal{\n                reconstructed_num_pts, 0.0};\n            for (size_t j = 0; j < 3; j++) {\n              lower_outward_conormal.get(j) =\n                  evolution::dg::subcell::fd::project_to_faces(\n                      inv_jacobian_dg.get(i, j), dg_mesh, face_mesh_extents, i);\n            }\n            const auto det_inv_jacobian_face =\n                evolution::dg::subcell::fd::project_to_faces(\n                    get(det_inv_jacobian_dg), dg_mesh, face_mesh_extents, i);\n\n            const Scalar<DataVector> normalization{sqrt(get(\n                dot_product(lower_outward_conormal, lower_outward_conormal,\n                            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                                vars_upper_face))))};\n            for (size_t j = 0; j < 3; j++) {\n              lower_outward_conormal.get(j) =\n                  lower_outward_conormal.get(j) / get(normalization);\n            }\n\n            tnsr::i<DataVector, 3, Frame::Inertial> upper_outward_conormal{\n                reconstructed_num_pts, 0.0};\n            for (size_t j = 0; j < 3; j++) {\n              upper_outward_conormal.get(j) = -lower_outward_conormal.get(j);\n            }\n            // Note: we probably should compute the normal vector in addition to\n            // the co-vector. Not a huge issue since we'll get an FPE right now\n            // if it's used by a Riemann solver.\n\n            // Compute the packaged data\n            using dg_package_data_projected_tags = tmpl::append<\n                evolved_vars_tags, fluxes_tags, dg_package_data_temporary_tags,\n                typename DerivedCorrection::dg_package_data_primitive_tags>;\n            evolution::dg::Actions::detail::dg_package_data<System>(\n                make_not_null(&upper_packaged_data), *derived_correction,\n                vars_upper_face, upper_outward_conormal, mesh_velocity_on_face,\n                *box, typename DerivedCorrection::dg_package_data_volume_tags{},\n                dg_package_data_projected_tags{});\n\n            evolution::dg::Actions::detail::dg_package_data<System>(\n                make_not_null(&lower_packaged_data), *derived_correction,\n                vars_lower_face, lower_outward_conormal, mesh_velocity_on_face,\n                *box, typename DerivedCorrection::dg_package_data_volume_tags{},\n                dg_package_data_projected_tags{});\n\n            // Now need to check if any of our neighbors are doing DG,\n            // because if so then we need to use whatever boundary data\n            // they sent instead of what we computed locally.\n            //\n            // Note: We could check this beforehand to avoid the extra\n            // work of reconstruction and flux computations at the\n            // boundaries.\n            evolution::dg::subcell::correct_package_data<true>(\n                make_not_null(&lower_packaged_data),\n                make_not_null(&upper_packaged_data), i, element, subcell_mesh,\n                db::get<evolution::dg::Tags::MortarData<3>>(*box), 0);\n\n            // Compute the corrections on the faces. We only need to\n            // compute this once because we can just flip the normal\n            // vectors then\n            gsl::at(boundary_corrections, i).initialize(reconstructed_num_pts);\n            evolution::dg::subcell::compute_boundary_terms(\n                make_not_null(&gsl::at(boundary_corrections, i)),\n                *derived_correction, upper_packaged_data, lower_packaged_data,\n                db::as_access(*box),\n                typename DerivedCorrection::dg_boundary_terms_volume_tags{});\n            // We need to multiply by the normal vector normalization\n            gsl::at(boundary_corrections, i) *= get(normalization);\n            // Also multiply by determinant of Jacobian, following Eq.(34)\n            // of 2109.11645\n            gsl::at(boundary_corrections, i) *= 1.0 / det_inv_jacobian_face;\n          }\n        });\n\n    // Now compute the actual time derivatives.\n    using variables_tag = typename System::variables_tag;\n    using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n    const gsl::not_null<typename dt_variables_tag::type*> dt_vars_ptr =\n        db::mutate<dt_variables_tag>(\n            [](const auto local_dt_vars_ptr) { return local_dt_vars_ptr; },\n            box);\n    dt_vars_ptr->initialize(subcell_mesh.number_of_grid_points());\n\n    using grmhd_source_tags =\n        tmpl::transform<ValenciaDivClean::ComputeSources::return_tags,\n                        tmpl::bind<db::remove_tag_prefix, tmpl::_1>>;\n    sources_impl(\n        dt_vars_ptr, *box, grmhd_source_tags{},\n        typename grmhd::ValenciaDivClean::ComputeSources::argument_tags{});\n\n    // Zero GRMHD tags that don't have sources.\n    tmpl::for_each<typename variables_tag::tags_list>(\n        [&dt_vars_ptr](auto evolved_var_tag_v) {\n          using evolved_var_tag = tmpl::type_from<decltype(evolved_var_tag_v)>;\n          using dt_tag = ::Tags::dt<evolved_var_tag>;\n          auto& dt_var = get<dt_tag>(*dt_vars_ptr);\n          for (size_t i = 0; i < dt_var.size(); ++i) {\n            if constexpr (not tmpl::list_contains_v<grmhd_source_tags,\n                                                    evolved_var_tag>) {\n              dt_var[i] = 0.0;\n            }\n          }\n        });\n\n    // Correction to source terms due to moving mesh\n    if (div_mesh_velocity.has_value()) {\n      const DataVector div_mesh_velocity_subcell =\n          evolution::dg::subcell::fd::project(div_mesh_velocity.value().get(),\n                                              dg_mesh, subcell_mesh.extents());\n      const auto& evolved_vars = db::get<evolved_vars_tag>(*box);\n\n      tmpl::for_each<typename variables_tag::tags_list>(\n          [&dt_vars_ptr, &div_mesh_velocity_subcell,\n           &evolved_vars](auto evolved_var_tag_v) {\n            using evolved_var_tag =\n                tmpl::type_from<decltype(evolved_var_tag_v)>;\n            using dt_tag = ::Tags::dt<evolved_var_tag>;\n            auto& dt_var = get<dt_tag>(*dt_vars_ptr);\n            const auto& evolved_var = get<evolved_var_tag>(evolved_vars);\n            for (size_t i = 0; i < dt_var.size(); ++i) {\n              dt_var[i] -= div_mesh_velocity_subcell * evolved_var[i];\n            }\n          });\n    }\n\n    if (UNLIKELY(fd_derivative_order != ::fd::DerivativeOrder::Two)) {\n      ERROR(\n          \"We don't yet have high-order flux corrections for curved/moving \"\n          \"meshes and the implementation assumes curved/moving meshes. We need \"\n          \"to dot the Cartesian fluxes into the cell-centered \"\n          \"J inv(J)^{hat{i}}_j to get JF^{hat{i}} = J inv(J)^{hat{i}}_j F^j.\"\n          \" Some care needs to be taken since we also get F^j from our \"\n          \"neighbors, which leaves the question as to whether to interpolate \"\n          \"the _inertial fluxes_ and then transform or whether to transform \"\n          \"and then interpolate the _densitized logical fluxes_.\");\n    }\n    std::optional<std::array<Variables<evolved_vars_tags>, 3>>\n        high_order_corrections{};\n    ::fd::cartesian_high_order_flux_corrections(\n        make_not_null(&high_order_corrections),\n\n        db::get<evolution::dg::subcell::Tags::CellCenteredFlux<\n            evolved_vars_tags, 3>>(*box),\n        boundary_corrections, fd_derivative_order,\n        db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(\n            *box),\n        subcell_mesh, recons.ghost_zone_size(),\n        reconstruction_order.value_or(\n            std::array<gsl::span<std::uint8_t>, 3>{}));\n\n    const auto& cell_centered_det_inv_jacobian = db::get<\n        evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToInertial>(\n        *box);\n    for (size_t dim = 0; dim < 3; ++dim) {\n      const auto& boundary_correction_in_axis =\n          high_order_corrections.has_value()\n              ? gsl::at(high_order_corrections.value(), dim)\n              : gsl::at(boundary_corrections, dim);\n      const double inverse_delta = gsl::at(one_over_delta_xi, dim);\n      tmpl::for_each<typename variables_tag::tags_list>(\n          [&dt_vars_ptr, &boundary_correction_in_axis,\n           &cell_centered_det_inv_jacobian, dim, inverse_delta,\n           &subcell_mesh](auto evolved_var_tag_v) {\n            using evolved_var_tag =\n                tmpl::type_from<decltype(evolved_var_tag_v)>;\n            using dt_tag = ::Tags::dt<evolved_var_tag>;\n            auto& dt_var = get<dt_tag>(*dt_vars_ptr);\n            const auto& var_correction =\n                get<evolved_var_tag>(boundary_correction_in_axis);\n            for (size_t i = 0; i < dt_var.size(); ++i) {\n              evolution::dg::subcell::add_cartesian_flux_divergence(\n                  make_not_null(&dt_var[i]), inverse_delta,\n                  get(cell_centered_det_inv_jacobian), var_correction[i],\n                  subcell_mesh.extents(), dim);\n            }\n          });\n    }\n\n    evolution::dg::subcell::store_reconstruction_order_in_databox(\n        box, reconstruction_order);\n  }\n\n private:\n  template <typename DtVarsList, typename DbTagsList, typename... SourcedTags,\n            typename... ArgsTags>\n  static void sources_impl(\n      const gsl::not_null<Variables<DtVarsList>*> dt_vars_ptr,\n      const db::DataBox<DbTagsList>& box, tmpl::list<SourcedTags...> /*meta*/,\n      tmpl::list<ArgsTags...> /*meta*/) {\n    grmhd::ValenciaDivClean::ComputeSources::apply(\n        get<::Tags::dt<SourcedTags>>(dt_vars_ptr)..., get<ArgsTags>(box)...);\n  }\n};\n}  // namespace grmhd::ValenciaDivClean::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Characteristics.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup EvolutionSystemsGroup\n/// \\brief Items related to general relativistic magnetohydrodynamics (GRMHD)\nnamespace grmhd {\n/// Items related to the Valencia formulation of ideal GRMHD with divergence\n/// cleaning coupled with electron fraction.\n///\n/// References:\n/// - Numerical 3+1 General Relativistic Magnetohydrodynamics: A Local\n/// Characteristic Approach \\cite Anton2006\n/// - GRHydro: a new open-source general-relativistic magnetohydrodynamics code\n/// for the Einstein toolkit \\cite Moesta2014\n/// - Black hole-neutron star mergers with a hot nuclear equation of state:\n/// outflow and neutrino-cooled disk for a low-mass, high-spin case\n/// \\cite Deaton2013\n///\nnamespace ValenciaDivClean {\n\n/*!\n * \\brief Ideal general relativistic magnetohydrodynamics (GRMHD) system with\n * divergence cleaning coupled with electron fraction.\n *\n * We adopt the standard 3+1 form of metric\n * \\f{align*}\n *  ds^2 = -\\alpha^2 dt^2 + \\gamma_{ij}(dx^i + \\beta^i dt)(dx^j + \\beta^j dt)\n * \\f}\n * where \\f$\\alpha\\f$ is the lapse, \\f$\\beta^i\\f$ is the shift vector, and\n * \\f$\\gamma_{ij}\\f$ is the spatial metric.\n *\n * Primitive variables of the system are\n *  - rest mass density \\f$\\rho\\f$\n *  - electron fraction \\f$Y_e\\f$\n *  - the spatial velocity \\f{align*} v^i =\n * \\frac{1}{\\alpha}\\left(\\frac{u^i}{u^0} + \\beta^i\\right) \\f} measured by the\n * Eulerian observer\n *  - specific internal energy density \\f$\\epsilon\\f$\n *  - magnetic field \\f$B^i = -^*F^{ia}n_a = -\\alpha (^*F^{0i})\\f$\n *  - the divergence cleaning field \\f$\\Phi\\f$\n *\n * with corresponding derived physical quantities which frequently appear in\n * equations:\n * - The transport velocity \\f$v^i_{tr} = \\alpha v^i - \\beta^i\\f$\n * - The Lorentz factor \\f{align*} W = -u^an_a = \\alpha u^0 =\n * \\frac{1}{\\sqrt{1-\\gamma_{ij}v^iv^j}} = \\sqrt{1+\\gamma^{ij}u_iu_j} =\n * \\sqrt{1+\\gamma^{ij}W^2v_iv_j} \\f}\n * - The specific enthalpy \\f$h = 1 + \\epsilon + p/\\rho\\f$ where \\f$p\\f$ is the\n * pressure specified by a particular equation of state (EoS)\n * - The comoving magnetic field \\f$b^b = -^*F^{ba} u_a\\f$ in the ideal MHD\n * limit\n * \\f{align*}\n *  b^0 & = W B^i v_i / \\alpha \\\\\n *  b^i & = (B^i + \\alpha b^0 u^i) / W \\\\\n *  b^2 & = b^ab_a = B^2/W^2 + (B^iv_i)^2\n * \\f}\n * - Augmented enthalpy density \\f$(\\rho h)^* = \\rho h + b^2\\f$ and augmented\n * pressure \\f$p^* = p + b^2/2\\f$ which include contributions from magnetic\n * field\n *\n * \\note We are using the electromagnetic variables with the scaling convention\n * that the factor \\f$4\\pi\\f$ does not appear in Maxwell equations and the\n * stress-energy tensor of the EM fields (geometrized Heaviside-Lorentz units).\n * To recover the physical value of magnetic field in the usual CGS Gaussian\n * unit, the conversion factor is\n * \\f{align*}\n *  \\sqrt{4\\pi}\\frac{c^4}{G^{3/2}M_\\odot} \\approx 8.35 \\times 10^{19}\n *  \\,\\text{Gauss}\n * \\f}\n * For example, magnetic field $10^{-5}$ with the code unit corresponds to the\n * $8.35\\times 10^{14}\\,\\text{G}$ in the CGS Gaussian unit. See also\n * documentation of hydro::units::cgs::gauss_unit for details.\n *\n * The GRMHD equations can be written in a flux-balanced form\n * \\f[\n *  \\partial_t U+ \\partial_i F^i(U) = S(U).\n * \\f]\n *\n * Evolved (conserved) variables \\f$U\\f$ are\n * \\f{align*}{\n * U = \\sqrt{\\gamma}\\left[\\,\\begin{matrix}\n *      D \\\\\n *      D Y_e \\\\\n *      S_j \\\\\n *      \\tau \\\\\n *      B^j \\\\\n *      \\Phi \\\\\n * \\end{matrix}\\,\\right] \\equiv \\left[\\,\\,\\begin{matrix}\n *      \\tilde{D} \\\\\n *      \\tilde{Y_e} \\\\\n *      \\tilde{S}_j \\\\\n *      \\tilde{\\tau} \\\\\n *      \\tilde{B}^j \\\\\n *      \\tilde{\\Phi} \\\\\n * \\end{matrix}\\,\\,\\right] = \\sqrt{\\gamma} \\left[\\,\\,\\begin{matrix}\n *      \\rho W \\\\\n *      \\rho W Y_e \\\\\n *      (\\rho h)^* W^2 v_j - \\alpha b^0 b_j \\\\\n *      (\\rho h)^* W^2 - p^* - (\\alpha b^0)^2 - \\rho W \\\\\n *      B^j \\\\\n *      \\Phi \\\\\n * \\end{matrix}\\,\\,\\right]\n * \\f}\n *\n * where \\f${\\tilde D}\\f$, \\f${\\tilde Y}_e\\f$,\\f${\\tilde S}_i\\f$, \\f${\\tilde\n * \\tau}\\f$, \\f${\\tilde B}^i\\f$, and \\f${\\tilde \\Phi}\\f$ are a generalized\n * mass-energy density, electron fraction, momentum density, specific internal\n * energy density, magnetic field, and divergence cleaning field. Also,\n * \\f$\\gamma\\f$ is the determinant of the spatial metric \\f$\\gamma_{ij}\\f$.\n *\n * Corresponding fluxes \\f$F^i(U)\\f$ are\n *\n * \\f{align*}\n * F^i({\\tilde D}) &= {\\tilde D} v^i_{tr} \\\\\n * F^i({\\tilde Y}_e) &= {\\tilde Y}_e v^i_{tr} \\\\\n * F^i({\\tilde S}_j) &= {\\tilde S}_j v^i_{tr} + \\alpha \\sqrt{\\gamma} p^*\n *      \\delta^i_j - \\alpha b_j \\tilde{B}^i / W \\\\\n * F^i({\\tilde \\tau}) &= {\\tilde \\tau} v^i_{tr} + \\alpha \\sqrt{\\gamma} p^* v^i\n *      - \\alpha^2 b^0 \\tilde{B}^i / W \\\\\n * F^i({\\tilde B}^j) &= {\\tilde B}^j v^i_{tr} - \\alpha v^j {\\tilde B}^i\n *      + \\alpha \\gamma^{ij} {\\tilde \\Phi} \\\\\n * F^i({\\tilde \\Phi}) &= \\alpha {\\tilde B^i} - {\\tilde \\Phi} \\beta^i\n * \\f}\n *\n * and source terms \\f$S(U)\\f$ are\n *\n * \\f{align*}\n * S({\\tilde D}) &= 0 \\\\\n * S({\\tilde Y}_e) &= 0 \\\\\n * S({\\tilde S}_j) &= \\frac{\\alpha}{2} {\\tilde S}^{mn} \\partial_j \\gamma_{mn}\n *      + {\\tilde S}_k \\partial_j \\beta^k - ({\\tilde D}\n *      + {\\tilde \\tau}) \\partial_j \\alpha \\\\\n * S({\\tilde \\tau}) &= \\alpha {\\tilde S}^{mn} K_{mn}\n *      - {\\tilde S}^m \\partial_m \\alpha \\\\\n * S({\\tilde B}^j) &= -{\\tilde B}^m \\partial_m \\beta^j\n *      + \\Phi \\partial_k (\\alpha \\sqrt{\\gamma}\\gamma^{jk}) \\\\\n * S({\\tilde \\Phi}) &= {\\tilde B}^k \\partial_k \\alpha - \\alpha K\n *      {\\tilde \\Phi} - \\alpha \\kappa {\\tilde \\Phi}\n * \\f}\n *\n * with\n * \\f{align*}\n * {\\tilde S}^{ij} = & \\sqrt{\\gamma} \\left[ (\\rho h)^* W^2 v^i v^j + p^*\n *      \\gamma^{ij} - \\gamma^{ik}\\gamma^{jl}b_kb_l \\right]\n * \\f}\n *\n * where \\f$K\\f$ is the trace of the extrinsic curvature \\f$K_{ij}\\f$, and\n * \\f$\\kappa\\f$ is a damping parameter that damps violations of the\n * divergence-free (no-monopole) condition \\f$\\Phi = \\partial_i {\\tilde B}^i =\n * 0\\f$.\n *\n * \\note On the electron fraction side, the source term is currently set to\n * \\f$S(\\tilde{Y}_e) = 0\\f$. Implementing the source term using neutrino scheme\n * is in progress (Last update : Oct 2022).\n *\n */\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = true;\n  static constexpr bool has_primitive_and_conservative_vars = true;\n  static constexpr size_t volume_dim = 3;\n\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition;\n\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::TildeD, Tags::TildeYe, Tags::TildeTau, Tags::TildeS<>,\n                 Tags::TildeB<>, Tags::TildePhi>>;\n  using flux_variables =\n      tmpl::list<Tags::TildeD, Tags::TildeYe, Tags::TildeTau, Tags::TildeS<>,\n                 Tags::TildeB<>, Tags::TildePhi>;\n  using non_conservative_variables = tmpl::list<>;\n  using gradient_variables = tmpl::list<>;\n  using primitive_variables_tag =\n      ::Tags::Variables<hydro::grmhd_tags<DataVector>>;\n  using spacetime_variables_tag =\n      ::Tags::Variables<gr::tags_for_hydro<volume_dim, DataVector>>;\n  using flux_spacetime_variables_tag = ::Tags::Variables<\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>>>;\n\n  using compute_volume_time_derivative_terms = TimeDerivativeTerms;\n\n  using conservative_from_primitive = ConservativeFromPrimitive;\n  template <typename OrderedListOfPrimitiveRecoverySchemes>\n  using primitive_from_conservative =\n      PrimitiveFromConservative<OrderedListOfPrimitiveRecoverySchemes>;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed;\n\n  using inverse_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, volume_dim>;\n};\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\n/// %Tags for the Valencia formulation of the ideal GRMHD equations\n/// with divergence cleaning.\nnamespace Tags {\n\n/// The characteristic speeds\nstruct CharacteristicSpeeds : db::SimpleTag {\n  using type = std::array<DataVector, 9>;\n};\n\n/// The densitized rest-mass density \\f${\\tilde D}\\f$\nstruct TildeD : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The densitized electron number density times the baryon mass\n/// \\f$\\tilde{Y}_e = {\\tilde D} Y_e\\f$\nstruct TildeYe : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The densitized energy density \\f${\\tilde \\tau}\\f$\nstruct TildeTau : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The densitized momentum density \\f${\\tilde S_i}\\f$\ntemplate <typename Fr>\nstruct TildeS : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Fr>;\n  static std::string name() { return Frame::prefix<Fr>() + \"TildeS\"; }\n};\n\n/// The densitized magnetic field \\f${\\tilde B^i}\\f$\ntemplate <typename Fr>\nstruct TildeB : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Fr>;\n  static std::string name() { return Frame::prefix<Fr>() + \"TildeB\"; }\n};\n\n/// The densitized divergence-cleaning field \\f${\\tilde \\Phi}\\f$\nstruct TildePhi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// \\brief Set to `true` if the variables needed fixing.\n///\n/// Used in DG-subcell hybrid scheme evolutions.\nstruct VariablesNeededFixing : db::SimpleTag {\n  using type = bool;\n};\n}  // namespace Tags\n\nnamespace OptionTags {\n/// \\ingroup OptionGroupsGroup\n/// Groups option tags related to the ValenciaDivClean evolution system.\nstruct ValenciaDivCleanGroup {\n  static std::string name() { return \"ValenciaDivClean\"; }\n  static constexpr Options::String help{\"Options for the evolution system\"};\n  using group = evolution::OptionTags::SystemGroup;\n};\n\n/// \\brief The constraint damping parameter\nstruct DampingParameter {\n  static std::string name() { return \"DampingParameter\"; }\n  using type = double;\n  static constexpr Options::String help{\n      \"Constraint damping parameter for divergence cleaning\"};\n  using group = ValenciaDivCleanGroup;\n};\n\nstruct PrimitiveFromConservativeOptions {\n  static std::string name() { return \"PrimitiveFromConservative\"; }\n  using type = grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions;\n  static constexpr Options::String help{\n      \"Value of density times Lorentz factor below which we skip conservative \"\n      \"to primitive inversion.\"};\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n/// The constraint damping parameter for divergence cleaning\nstruct ConstraintDampingParameter : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::DampingParameter>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double constraint_damping_parameter) {\n    return constraint_damping_parameter;\n  }\n};\n\nstruct PrimitiveFromConservativeOptions : db::SimpleTag {\n  using type = grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions;\n  using option_tags = tmpl::list<OptionTags::PrimitiveFromConservativeOptions>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& options) { return options; }\n};\n\n}  // namespace Tags\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace grmhd {\nnamespace ValenciaDivClean {\nnamespace Tags {\nstruct CharacteristicSpeeds;\nstruct ConstraintDampingParameter;\nstruct PrimitiveFromConservativeOptions;\nstruct TildeD;\nstruct TildeYe;\nstruct TildeTau;\ntemplate <typename Fr = Frame::Inertial>\nstruct TildeS;\ntemplate <typename Fr = Frame::Inertial>\nstruct TildeB;\nstruct TildePhi;\n}  // namespace Tags\n}  // namespace ValenciaDivClean\n}  // namespace grmhd\n/// \\endcond\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Sources.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nevolution::dg::TimeDerivativeDecisions<3> TimeDerivativeTerms::apply(\n    const gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_tilde_d*/,\n    const gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_tilde_ye*/,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity_one_form,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        magnetic_field_one_form,\n    const gsl::not_null<Scalar<DataVector>*>\n        magnetic_field_dot_spatial_velocity,\n    const gsl::not_null<Scalar<DataVector>*> magnetic_field_squared,\n    const gsl::not_null<Scalar<DataVector>*> one_over_w_squared,\n    const gsl::not_null<Scalar<DataVector>*> pressure_star,\n    const gsl::not_null<Scalar<DataVector>*>\n        pressure_star_lapse_sqrt_det_spatial_metric,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        transport_velocity,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        lapse_b_over_w,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_s_up,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        densitized_stress,\n    const gsl::not_null<tnsr::ijj<DataVector, 3, Frame::Inertial>*>\n        spatial_christoffel_first_kind,\n    const gsl::not_null<tnsr::Ijj<DataVector, 3, Frame::Inertial>*>\n        spatial_christoffel_second_kind,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        trace_spatial_christoffel_second,\n    const gsl::not_null<Scalar<DataVector>*> h_rho_w_squared_plus_b_squared,\n\n    const gsl::not_null<Scalar<DataVector>*> temp_lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> temp_shift,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        temp_inverse_spatial_metric,\n\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n    const Scalar<DataVector>& pressure,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_internal_energy,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    const double constraint_damping_parameter) {\n  // Note that if the temp_lapse and lapse arguments point to the same object\n  // then the copy is elided internally.\n  *temp_lapse = lapse;\n  *temp_shift = shift;\n  *temp_inverse_spatial_metric = inv_spatial_metric;\n\n  raise_or_lower_index(spatial_velocity_one_form, spatial_velocity,\n                       spatial_metric);\n  raise_or_lower_index(magnetic_field_one_form, magnetic_field, spatial_metric);\n  dot_product(magnetic_field_dot_spatial_velocity, magnetic_field,\n              *spatial_velocity_one_form);\n  dot_product(magnetic_field_squared, magnetic_field, *magnetic_field_one_form);\n  get(*one_over_w_squared) = 1.0 / square(get(lorentz_factor));\n  get(*pressure_star) =\n      get(pressure) + 0.5 * square(get(*magnetic_field_dot_spatial_velocity)) +\n      0.5 * get(*magnetic_field_squared) * get(*one_over_w_squared);\n  get(*pressure_star_lapse_sqrt_det_spatial_metric) =\n      get(sqrt_det_spatial_metric) * get(lapse) * get(*pressure_star);\n\n  // lapse b_i / W = lapse (B_i / W^2 + v_i (B^m v_m)\n  *lapse_b_over_w = *spatial_velocity_one_form;\n  for (size_t i = 0; i < 3; ++i) {\n    lapse_b_over_w->get(i) *= get(*magnetic_field_dot_spatial_velocity);\n    lapse_b_over_w->get(i) +=\n        get(*one_over_w_squared) * magnetic_field_one_form->get(i);\n    lapse_b_over_w->get(i) *= get(lapse);\n  }\n\n  detail::fluxes_impl(tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n                      tilde_b_flux, tilde_phi_flux,\n                      // Temporaries\n                      transport_velocity, *lapse_b_over_w,\n                      *magnetic_field_dot_spatial_velocity,\n                      *pressure_star_lapse_sqrt_det_spatial_metric,\n                      // Extra args\n                      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi,\n                      lapse, shift, inv_spatial_metric, spatial_velocity);\n\n  // Compute source terms\n  gr::christoffel_first_kind(spatial_christoffel_first_kind, d_spatial_metric);\n  raise_or_lower_first_index(spatial_christoffel_second_kind,\n                             *spatial_christoffel_first_kind,\n                             inv_spatial_metric);\n  trace_last_indices(trace_spatial_christoffel_second,\n                     *spatial_christoffel_second_kind, inv_spatial_metric);\n\n  detail::sources_impl(\n      non_flux_terms_dt_tilde_tau, non_flux_terms_dt_tilde_s,\n      non_flux_terms_dt_tilde_b, non_flux_terms_dt_tilde_phi,\n\n      tilde_s_up, densitized_stress, h_rho_w_squared_plus_b_squared,\n\n      *magnetic_field_dot_spatial_velocity, *magnetic_field_squared,\n      *one_over_w_squared, *pressure_star, *trace_spatial_christoffel_second,\n\n      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, lapse,\n      sqrt_det_spatial_metric, inv_spatial_metric, d_lapse, d_shift,\n      d_spatial_metric, spatial_velocity, lorentz_factor, magnetic_field,\n\n      rest_mass_density, electron_fraction, pressure, specific_internal_energy,\n      extrinsic_curvature, constraint_damping_parameter);\n  return {true};\n}\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace grmhd::ValenciaDivClean {\n/*!\n * \\brief Compute the time derivative of the conserved variables for the\n * Valencia formulation of the GRMHD equations with divergence cleaning.\n */\nstruct TimeDerivativeTerms {\n  struct MagneticFieldOneForm : db::SimpleTag {\n    using type = tnsr::i<DataVector, 3, Frame::Inertial>;\n  };\n  struct TildeSUp : db::SimpleTag {\n    using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n  };\n  struct DensitizedStress : db::SimpleTag {\n    using type = tnsr::II<DataVector, 3, Frame::Inertial>;\n  };\n  struct OneOverLorentzFactorSquared : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n  struct PressureStarLapseSqrtDetSpatialMetric : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n  struct PressureStar : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n  struct EnthalpyTimesDensityWSquaredPlusBSquared : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n  struct LapseTimesbOverW : db::SimpleTag {\n    using type = tnsr::i<DataVector, 3, Frame::Inertial>;\n  };\n\n  using temporary_tags = tmpl::list<\n      // Flux terms\n      hydro::Tags::SpatialVelocityOneForm<DataVector, 3, Frame::Inertial>,\n      hydro::Tags::MagneticFieldOneForm<DataVector, 3, Frame::Inertial>,\n      hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>,\n      hydro::Tags::MagneticFieldSquared<DataVector>,\n      OneOverLorentzFactorSquared, PressureStar,\n      PressureStarLapseSqrtDetSpatialMetric,\n      hydro::Tags::TransportVelocity<DataVector, 3, Frame::Inertial>,\n      LapseTimesbOverW,\n\n      // Source terms\n      TildeSUp, DensitizedStress,\n      gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>,\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>,\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>,\n      EnthalpyTimesDensityWSquaredPlusBSquared,\n\n      // Need lapse, shift, and inverse spatial metric to be projected to the\n      // boundary for Riemann solvers.\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n  using argument_tags =\n      tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                 grmhd::ValenciaDivClean::Tags::TildeYe,\n                 grmhd::ValenciaDivClean::Tags::TildeTau,\n                 grmhd::ValenciaDivClean::Tags::TildeS<>,\n                 grmhd::ValenciaDivClean::Tags::TildeB<>,\n                 grmhd::ValenciaDivClean::Tags::TildePhi,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                 grmhd::ValenciaDivClean::Tags::ConstraintDampingParameter>;\n\n  static evolution::dg::TimeDerivativeDecisions<3> apply(\n      gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_tilde_d*/,\n      gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_tilde_ye*/,\n      gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_tau,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          non_flux_terms_dt_tilde_s,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          non_flux_terms_dt_tilde_b,\n      gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_phi,\n\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_tau_flux,\n      gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n      gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_phi_flux,\n\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          spatial_velocity_one_form,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          magnetic_field_one_form,\n      gsl::not_null<Scalar<DataVector>*> magnetic_field_dot_spatial_velocity,\n      gsl::not_null<Scalar<DataVector>*> magnetic_field_squared,\n      gsl::not_null<Scalar<DataVector>*> one_over_w_squared,\n      gsl::not_null<Scalar<DataVector>*> pressure_star,\n      gsl::not_null<Scalar<DataVector>*>\n          pressure_star_lapse_sqrt_det_spatial_metric,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          transport_velocity,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> lapse_b_over_w,\n\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_s_up,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          densitized_stress,\n      gsl::not_null<tnsr::ijj<DataVector, 3, Frame::Inertial>*>\n          spatial_christoffel_first_kind,\n      gsl::not_null<tnsr::Ijj<DataVector, 3, Frame::Inertial>*>\n          spatial_christoffel_second_kind,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n          trace_spatial_christoffel_second,\n      gsl::not_null<Scalar<DataVector>*> h_rho_w_squared_plus_b_squared,\n\n      gsl::not_null<Scalar<DataVector>*> temp_lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> temp_shift,\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          temp_inverse_spatial_metric,\n\n      const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n      const Scalar<DataVector>& tilde_tau,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n      const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n      const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n      const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n      const Scalar<DataVector>& pressure,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& electron_fraction,\n      const Scalar<DataVector>& specific_internal_energy,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n      double constraint_damping_parameter);\n};\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/AllSolutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/KhInstability.hpp\"\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/ShuOsherTube.hpp\"\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/SodExplosion.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/IsentropicVortex.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/RiemannProblem.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp\"\n\nnamespace NewtonianEuler::InitialData {\n/// The initial data that can be used depending on the spatial and thermodynamic\n/// dimension.\ntemplate <size_t Dim>\nusing initial_data_list = tmpl::conditional_t<\n    Dim == 3,\n    tmpl::list<  // Polytropic EOS\n        NewtonianEuler::Solutions::LaneEmdenStar,\n        NewtonianEuler::Solutions::IsentropicVortex<3>,\n\n        // Ideal fluid EOS\n        NewtonianEuler::Solutions::RiemannProblem<3>,\n        NewtonianEuler::Solutions::SmoothFlow<3>,\n        AnalyticData::KhInstability<3>, AnalyticData::SodExplosion<3>>,\n    tmpl::conditional_t<\n        Dim == 2,\n        tmpl::list<  // Polytropic EOS\n            NewtonianEuler::Solutions::IsentropicVortex<2>,\n\n            // Ideal fluid EOS\n            NewtonianEuler::Solutions::RiemannProblem<2>,\n            NewtonianEuler::Solutions::SmoothFlow<2>,\n            AnalyticData::KhInstability<2>, AnalyticData::SodExplosion<2>>,\n        tmpl::list<NewtonianEuler::Solutions::RiemannProblem<1>,\n                   NewtonianEuler::Solutions::SmoothFlow<1>,\n                   AnalyticData::ShuOsherTube>>>;\n}  // namespace NewtonianEuler::InitialData\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace NewtonianEuler::BoundaryConditions {\ntemplate <size_t Dim>\nBoundaryCondition<Dim>::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\ntemplate <size_t Dim>\nvoid BoundaryCondition<Dim>::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class BoundaryCondition<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace NewtonianEuler {\n/// \\brief Boundary conditions for the Newtonian Euler hydrodynamics system\nnamespace BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\ntemplate <size_t Dim>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace BoundaryConditions\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  DemandOutgoingCharSpeeds.cpp\n  DirichletAnalytic.cpp\n  Reflection.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  DemandOutgoingCharSpeeds.hpp\n  DirichletAnalytic.hpp\n  Factory.hpp\n  Reflection.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::BoundaryConditions {\ntemplate <size_t Dim>\nDemandOutgoingCharSpeeds<Dim>::DemandOutgoingCharSpeeds(\n    CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDemandOutgoingCharSpeeds<Dim>::get_clone() const {\n  return std::make_unique<DemandOutgoingCharSpeeds>(*this);\n}\n\ntemplate <size_t Dim>\nvoid DemandOutgoingCharSpeeds<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DemandOutgoingCharSpeeds<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\ntemplate <size_t ThermodynamicDim>\nstd::optional<std::string>\nDemandOutgoingCharSpeeds<Dim>::dg_demand_outgoing_char_speeds(\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>&\n        outward_directed_normal_covector,\n\n    const Scalar<DataVector>& mass_density,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity,\n    const Scalar<DataVector>& specific_internal_energy,\n    const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n        equation_of_state) {\n  double min_char_speed = std::numeric_limits<double>::signaling_NaN();\n\n  // Temp buffer for sound speed, normal dot velocity, and (optional) normal\n  // dot mesh velocity\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>>>\n      buffer{get(mass_density).size()};\n\n  auto& sound_speed = get<::Tags::TempScalar<0>>(buffer);\n  sound_speed_squared(make_not_null(&sound_speed), mass_density,\n                      specific_internal_energy, equation_of_state);\n  get(sound_speed) = sqrt(get(sound_speed));\n\n  auto& normal_dot_velocity = get<::Tags::TempScalar<1>>(buffer);\n  dot_product(make_not_null(&normal_dot_velocity),\n              outward_directed_normal_covector, velocity);\n\n  if (face_mesh_velocity.has_value()) {\n    auto& normal_dot_mesh_velocity = get<::Tags::TempScalar<2>>(buffer);\n    dot_product(make_not_null(&normal_dot_mesh_velocity),\n                outward_directed_normal_covector, face_mesh_velocity.value());\n\n    min_char_speed = min(get(normal_dot_velocity) - get(sound_speed) -\n                         get(normal_dot_mesh_velocity));\n  } else {\n    min_char_speed = min(get(normal_dot_velocity) - get(sound_speed));\n  }\n\n  if (min_char_speed < 0.0) {\n    return {MakeString{}\n            << \"DemandOutgoingCharSpeeds boundary condition violated with the \"\n               \"characteristic speed : \"\n            << min_char_speed << \"\\n\"};\n  }\n\n  return std::nullopt;\n}  // namespace NewtonianEuler::BoundaryConditions\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) \\\n  template class DemandOutgoingCharSpeeds<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define THERMODIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(_, data)                                              \\\n  template std::optional<std::string>                                       \\\n  DemandOutgoingCharSpeeds<DIM(data)>::dg_demand_outgoing_char_speeds<      \\\n      THERMODIM(data)>(                                                     \\\n      const std::optional<tnsr::I<DataVector, DIM(data), Frame::Inertial>>& \\\n          face_mesh_velocity,                                               \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                \\\n          outward_directed_normal_covector,                                 \\\n      const Scalar<DataVector>& mass_density,                               \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& velocity,      \\\n      const Scalar<DataVector>& specific_internal_energy,                   \\\n      const EquationsOfState::EquationOfState<false, THERMODIM(data)>&      \\\n          equation_of_state);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (1, 2))\n\n#undef INSTANTIATION\n\n#undef THERMODIM\n\n#undef DIM\n}  // namespace NewtonianEuler::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/DemandOutgoingCharSpeeds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace NewtonianEuler::BoundaryConditions {\n/*!\n * \\brief A boundary condition that only verifies that all characteristic speeds\n * are directed out of the domain; no boundary data is altered by this boundary\n * condition.\n */\ntemplate <size_t Dim>\nclass DemandOutgoingCharSpeeds final : public BoundaryCondition<Dim> {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A boundary condition that only verifies the characteristic speeds are \"\n      \"all directed out of the domain.\"};\n\n  DemandOutgoingCharSpeeds() = default;\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags = tmpl::list<>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim, Frame::Inertial>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>>;\n  using dg_gridless_tags = tmpl::list<hydro::Tags::EquationOfState<false, 2>>;\n\n  template <size_t ThermodynamicDim>\n  static std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n\n      const Scalar<DataVector>& mass_density,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity,\n      const Scalar<DataVector>& specific_internal_energy,\n      const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n          equation_of_state);\n};\n}  // namespace NewtonianEuler::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/AllSolutions.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace NewtonianEuler::BoundaryConditions {\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(const DirichletAnalytic<Dim>& rhs)\n    : BoundaryCondition<Dim>{dynamic_cast<const BoundaryCondition<Dim>&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>& DirichletAnalytic<Dim>::operator=(\n    const DirichletAnalytic<Dim>& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic<Dim>::get_clone() const {\n  return std::make_unique<DirichletAnalytic>(*this);\n}\n\ntemplate <size_t Dim>\nvoid DirichletAnalytic<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n  p | analytic_prescription_;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string> DirichletAnalytic<Dim>::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        flux_mass_density,\n    const gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>\n        flux_momentum_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        flux_energy_density,\n\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> velocity,\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n\n    const std::optional<\n        tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const double time) const {\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataVector>,\n                          hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                          hydro::Tags::Pressure<DataVector>,\n                          hydro::Tags::SpecificInternalEnergy<DataVector>>,\n      NewtonianEuler::InitialData::initial_data_list<Dim>>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const initial_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(\n              coords, time,\n              tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                         hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                         hydro::Tags::Pressure<DataVector>,\n                         hydro::Tags::SpecificInternalEnergy<DataVector>>{});\n\n        } else {\n          (void)time;\n          return initial_data->variables(\n              coords,\n              tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                         hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                         hydro::Tags::Pressure<DataVector>,\n                         hydro::Tags::SpecificInternalEnergy<DataVector>>{});\n        }\n      });\n\n  *mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(boundary_values);\n  *velocity =\n      get<hydro::Tags::SpatialVelocity<DataVector, Dim>>(boundary_values);\n  *specific_internal_energy =\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(boundary_values);\n\n  ConservativeFromPrimitive<Dim>::apply(mass_density, momentum_density,\n                                        energy_density, *mass_density,\n                                        *velocity, *specific_internal_energy);\n  ComputeFluxes<Dim>::apply(\n      flux_mass_density, flux_momentum_density, flux_energy_density,\n      *momentum_density, *energy_density, *velocity,\n      get<hydro::Tags::Pressure<DataVector>>(boundary_values));\n\n  return {};\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class DirichletAnalytic<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Fluxes.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace NewtonianEuler::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\ntemplate <size_t Dim>\nclass DirichletAnalytic final : public BoundaryCondition<Dim> {\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n  using options = tmpl::list<AnalyticPrescription>;\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions using either analytic solution or \"\n      \"analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<Scalar<DataVector>*> mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          momentum_density,\n      gsl::not_null<Scalar<DataVector>*> energy_density,\n\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          flux_mass_density,\n      gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>\n          flux_momentum_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          flux_energy_density,\n\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> velocity,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n\n      const std::optional<\n          tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      double time) const;\n\n private:\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace NewtonianEuler::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/Reflection.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\ntemplate <size_t Dim>\nusing standard_boundary_conditions =\n    tmpl::list<domain::BoundaryConditions::Periodic<BoundaryCondition<Dim>>,\n               DemandOutgoingCharSpeeds<Dim>, DirichletAnalytic<Dim>,\n               Reflection<Dim>>;\n}  // namespace NewtonianEuler::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/Reflection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/Reflection.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Fluxes.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::BoundaryConditions {\ntemplate <size_t Dim>\nReflection<Dim>::Reflection(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nReflection<Dim>::get_clone() const {\n  return std::make_unique<Reflection>(*this);\n}\n\ntemplate <size_t Dim>\nvoid Reflection<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string> Reflection<Dim>::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        flux_mass_density,\n    const gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>\n        flux_momentum_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        flux_energy_density,\n\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> velocity,\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>&\n        outward_directed_normal_covector,\n\n    const Scalar<DataVector>& interior_mass_desity,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& interior_velocity,\n    const Scalar<DataVector>& interior_specific_internal_energy,\n    const Scalar<DataVector>& interior_pressure) const {\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>>> buffer{\n      get(interior_mass_desity).size()};\n  auto& normal_dot_velocity = get<::Tags::TempScalar<0>>(buffer);\n  dot_product(make_not_null(&normal_dot_velocity),\n              outward_directed_normal_covector, interior_velocity);\n  for (size_t i = 0; i < Dim; i++) {\n    (*velocity).get(i) =\n        interior_velocity.get(i) - 2.0 * get(normal_dot_velocity) *\n                                       outward_directed_normal_covector.get(i);\n  }\n  if (face_mesh_velocity.has_value()) {\n    auto& normal_dot_mesh_velocity = get<::Tags::TempScalar<1>>(buffer);\n    dot_product(make_not_null(&normal_dot_mesh_velocity),\n                outward_directed_normal_covector, face_mesh_velocity.value());\n    for (size_t i = 0; i < Dim; i++) {\n      (*velocity).get(i) += 2.0 * get(normal_dot_mesh_velocity) *\n                            outward_directed_normal_covector.get(i);\n    }\n  }\n\n  *specific_internal_energy = interior_specific_internal_energy;\n\n  ConservativeFromPrimitive<Dim>::apply(\n      mass_density, momentum_density, energy_density, interior_mass_desity,\n      *velocity, interior_specific_internal_energy);\n  ComputeFluxes<Dim>::apply(flux_mass_density, flux_momentum_density,\n                            flux_energy_density, *momentum_density,\n                            *energy_density, *velocity, interior_pressure);\n\n  return {};\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Reflection<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) template class Reflection<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#undef DIM\n}  // namespace NewtonianEuler::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryConditions/Reflection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace NewtonianEuler::BoundaryConditions {\n/*!\n * \\brief Reflecting boundary conditions for Newtonian hydrodynamics.\n *\n * Ghost (exterior) data 'mirrors' interior volume data with respect to the\n * boundary interface. i.e. reverses the normal component of velocity while\n * using same values for other scalar quantities.\n *\n * In the frame instantaneously moving with the same velocity as face mesh, the\n * reflection condition reads\n *\n * \\f{align*}\n * \\vec{u}_\\text{ghost} = \\vec{u}_\\text{int} - 2 (\\vec{u}_\\text{int} \\cdot\n * \\hat{n}) \\hat{n}\n * \\f}\n *\n * where \\f$\\vec{u}\\f$ is the fluid velocity in the moving frame, \"int\" stands\n * for interior, and \\f$\\hat{n}\\f$ is the outward normal vector on the boundary\n * interface.\n *\n * Substituting \\f$\\vec{u} = \\vec{v} - \\vec{v}_m\\f$, we get\n *\n * \\f{align*}\n * v_\\text{ghost}^i &= v_\\text{int}^i - 2[(v_\\text{int}^j-v_m^j)n_j]n^i\n * \\f}\n *\n * where \\f$v\\f$ is the fluid velocity and \\f$v_m\\f$ is face mesh velocity.\n *\n */\ntemplate <size_t Dim>\nclass Reflection final : public BoundaryCondition<Dim> {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Reflecting boundary conditions for Newtonian hydrodynamics.\"};\n\n  Reflection() = default;\n  Reflection(Reflection&&) = default;\n  Reflection& operator=(Reflection&&) = default;\n  Reflection(const Reflection&) = default;\n  Reflection& operator=(const Reflection&) = default;\n  ~Reflection() override = default;\n\n  explicit Reflection(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, Reflection);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags = tmpl::list<>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<Scalar<DataVector>*> mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          momentum_density,\n      gsl::not_null<Scalar<DataVector>*> energy_density,\n\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          flux_mass_density,\n      gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>\n          flux_momentum_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          flux_energy_density,\n\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> velocity,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n\n      const Scalar<DataVector>& interior_mass_desity,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& interior_velocity,\n      const Scalar<DataVector>& interior_specific_internal_energy,\n      const Scalar<DataVector>& interior_pressure) const;\n};\n\n}  // namespace NewtonianEuler::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Hll.cpp\n  Hllc.cpp\n  Rusanov.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Hll.hpp\n  Hllc.hpp\n  NamespaceDocs.hpp\n  Rusanov.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hllc.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::BoundaryCorrections {\ntemplate <size_t Dim>\nusing standard_boundary_corrections =\n    tmpl::list<Hll<Dim>, Hllc<Dim>, Rusanov<Dim>>;\n}  // namespace NewtonianEuler::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hll.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hll.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler::BoundaryCorrections {\ntemplate <size_t Dim>\nHll<Dim>::Hll(CkMigrateMessage* msg) : BoundaryCorrection(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::BoundaryCorrection> Hll<Dim>::get_clone() const {\n  return std::make_unique<Hll>(*this);\n}\n\ntemplate <size_t Dim>\nvoid Hll<Dim>::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n}\n\ntemplate <size_t Dim>\ndouble Hll<Dim>::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        packaged_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> packaged_energy_density,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_normal_dot_flux_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        packaged_normal_dot_flux_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_normal_dot_flux_energy_density,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_largest_outgoing_char_speed,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_largest_ingoing_char_speed,\n\n    const Scalar<DataVector>& mass_density,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_mass_density,\n    const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_momentum_density,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_energy_density,\n\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity,\n    const Scalar<DataVector>& specific_internal_energy,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state)\n    const {\n  {\n    // Compute sound speed\n    Scalar<DataVector>& sound_speed = *packaged_mass_density;\n    Scalar<DataVector>& normal_dot_velocity = *packaged_energy_density;\n    sound_speed_squared(make_not_null(&sound_speed), mass_density,\n                        specific_internal_energy, equation_of_state);\n    get(sound_speed) = sqrt(get(sound_speed));\n    dot_product(make_not_null(&normal_dot_velocity), velocity, normal_covector);\n    // Compute the largest outgoing / ingoing characteristic speeds. Note that\n    // the 'largest ingoing' speed has the most negative value, because the\n    // positive direction is along the outward-directed normal\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*packaged_largest_outgoing_char_speed) =\n          get(normal_dot_velocity) + get(sound_speed) -\n          get(*normal_dot_mesh_velocity);\n      get(*packaged_largest_ingoing_char_speed) =\n          get(normal_dot_velocity) - get(sound_speed) -\n          get(*normal_dot_mesh_velocity);\n    } else {\n      get(*packaged_largest_outgoing_char_speed) =\n          get(normal_dot_velocity) + get(sound_speed);\n      get(*packaged_largest_ingoing_char_speed) =\n          get(normal_dot_velocity) - get(sound_speed);\n    }\n  }\n\n  *packaged_mass_density = mass_density;\n  *packaged_momentum_density = momentum_density;\n  *packaged_energy_density = energy_density;\n\n  normal_dot_flux(packaged_normal_dot_flux_mass_density, normal_covector,\n                  flux_mass_density);\n  normal_dot_flux(packaged_normal_dot_flux_momentum_density, normal_covector,\n                  flux_momentum_density);\n  normal_dot_flux(packaged_normal_dot_flux_energy_density, normal_covector,\n                  flux_energy_density);\n\n  return fmax(max(get(*packaged_largest_outgoing_char_speed)),\n              -min(get(*packaged_largest_ingoing_char_speed)));\n}\n\ntemplate <size_t Dim>\nvoid Hll<Dim>::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_energy_density,\n    const Scalar<DataVector>& mass_density_int,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_int,\n    const Scalar<DataVector>& energy_density_int,\n    const Scalar<DataVector>& normal_dot_flux_mass_density_int,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        normal_dot_flux_momentum_density_int,\n    const Scalar<DataVector>& normal_dot_flux_energy_density_int,\n    const Scalar<DataVector>& largest_outgoing_char_speed_int,\n    const Scalar<DataVector>& largest_ingoing_char_speed_int,\n    const Scalar<DataVector>& mass_density_ext,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_ext,\n    const Scalar<DataVector>& energy_density_ext,\n    const Scalar<DataVector>& normal_dot_flux_mass_density_ext,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        normal_dot_flux_momentum_density_ext,\n    const Scalar<DataVector>& normal_dot_flux_energy_density_ext,\n    const Scalar<DataVector>& largest_outgoing_char_speed_ext,\n    const Scalar<DataVector>& largest_ingoing_char_speed_ext,\n    const dg::Formulation dg_formulation) const {\n  // Allocate a temp buffer with four tags.\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>, ::Tags::TempScalar<3>>>\n      temps{get(mass_density_int).size()};\n\n  // Determine lambda_max and lambda_min from the characteristic speeds info\n  // from interior and exterior\n  get(get<::Tags::TempScalar<0>>(temps)) =\n      max(0., get(largest_outgoing_char_speed_int),\n          -get(largest_ingoing_char_speed_ext));\n  const DataVector& lambda_max = get(get<::Tags::TempScalar<0>>(temps));\n  get(get<::Tags::TempScalar<1>>(temps)) =\n      min(0., get(largest_ingoing_char_speed_int),\n          -get(largest_outgoing_char_speed_ext));\n  const DataVector& lambda_min = get(get<::Tags::TempScalar<1>>(temps));\n\n  // Pre-compute two expressions made out of lambda_max and lambda_min\n  get(get<::Tags::TempScalar<2>>(temps)) = lambda_max * lambda_min;\n  const DataVector& lambdas_product = get(get<::Tags::TempScalar<2>>(temps));\n  get(get<::Tags::TempScalar<3>>(temps)) = 1. / (lambda_max - lambda_min);\n  const DataVector& one_over_lambda_max_minus_min =\n      get(get<::Tags::TempScalar<3>>(temps));\n\n  if (dg_formulation == dg::Formulation::WeakInertial) {\n    get(*boundary_correction_mass_density) =\n        ((lambda_max * get(normal_dot_flux_mass_density_int) +\n          lambda_min * get(normal_dot_flux_mass_density_ext)) +\n         lambdas_product * (get(mass_density_ext) - get(mass_density_int))) *\n        one_over_lambda_max_minus_min;\n    for (size_t i = 0; i < Dim; ++i) {\n      boundary_correction_momentum_density->get(i) =\n          ((lambda_max * normal_dot_flux_momentum_density_int.get(i) +\n            lambda_min * normal_dot_flux_momentum_density_ext.get(i)) +\n           lambdas_product *\n               (momentum_density_ext.get(i) - momentum_density_int.get(i))) *\n          one_over_lambda_max_minus_min;\n    }\n    get(*boundary_correction_energy_density) =\n        ((lambda_max * get(normal_dot_flux_energy_density_int) +\n          lambda_min * get(normal_dot_flux_energy_density_ext)) +\n         lambdas_product *\n             (get(energy_density_ext) - get(energy_density_int))) *\n        one_over_lambda_max_minus_min;\n  } else {\n    // Compute boundary correction for strong formulation\n    get(*boundary_correction_mass_density) =\n        (lambda_min * (get(normal_dot_flux_mass_density_int) +\n                       get(normal_dot_flux_mass_density_ext)) +\n         lambdas_product * (get(mass_density_ext) - get(mass_density_int))) *\n        one_over_lambda_max_minus_min;\n    for (size_t i = 0; i < Dim; ++i) {\n      boundary_correction_momentum_density->get(i) =\n          (lambda_min * (normal_dot_flux_momentum_density_int.get(i) +\n                         normal_dot_flux_momentum_density_ext.get(i)) +\n           lambdas_product *\n               (momentum_density_ext.get(i) - momentum_density_int.get(i))) *\n          one_over_lambda_max_minus_min;\n    }\n    get(*boundary_correction_energy_density) =\n        (lambda_min * (get(normal_dot_flux_energy_density_int) +\n                       get(normal_dot_flux_energy_density_ext)) +\n         lambdas_product *\n             (get(energy_density_ext) - get(energy_density_int))) *\n        one_over_lambda_max_minus_min;\n  }\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Hll<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) template class Hll<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hll.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::BoundaryCorrections {\n/*!\n * \\brief An HLL (Harten-Lax-van Leer) Riemann solver for NewtonianEuler system\n *\n * Let \\f$U\\f$ be the evolved variable, \\f$F^i\\f$ the flux, and \\f$n_i\\f$ be the\n * outward directed unit normal to the interface. Denoting \\f$F := n_i F^i\\f$,\n * the HLL boundary correction is \\cite Harten1983\n *\n * \\f{align*}\n * G_\\text{HLL} = \\frac{\\lambda_\\text{max} F_\\text{int} +\n * \\lambda_\\text{min} F_\\text{ext}}{\\lambda_\\text{max} - \\lambda_\\text{min}}\n * - \\frac{\\lambda_\\text{min}\\lambda_\\text{max}}{\\lambda_\\text{max} -\n *   \\lambda_\\text{min}} \\left(U_\\text{int} - U_\\text{ext}\\right)\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior.\n * \\f$\\lambda_\\text{min}\\f$ and \\f$\\lambda_\\text{max}\\f$ are defined as\n *\n * \\f{align*}\n * \\lambda_\\text{min} &=\n * \\text{min}\\left(\\lambda^{-}_\\text{int},-\\lambda^{+}_\\text{ext}, 0\\right) \\\\\n * \\lambda_\\text{max} &=\n * \\text{max}\\left(\\lambda^{+}_\\text{int},-\\lambda^{-}_\\text{ext}, 0\\right)\n * \\f}\n *\n * where \\f$\\lambda^{+}\\f$ (\\f$\\lambda^{-}\\f$) is the largest characteristic\n * speed in the outgoing (ingoing) direction. Note the minus signs in front of\n * \\f$\\lambda^{\\pm}_\\text{ext}\\f$, which is because an outgoing speed w.r.t. the\n * neighboring element is an ingoing speed w.r.t. the local element, and vice\n * versa. Similarly, the \\f$F_{\\text{ext}}\\f$ term in \\f$G_\\text{HLL}\\f$ has a\n * positive sign because the outward directed normal of the neighboring element\n * has the opposite sign, i.e. \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$.\n *\n * For the NewtonianEuler system, \\f$\\lambda^\\pm\\f$ are given as\n *\n * \\f{align*}\n * \\lambda^\\pm = v^in_i \\pm c_s\n * \\f}\n *\n * where \\f$v^i\\f$ is the spatial velocity and \\f$c_s\\f$ the sound speed.\n *\n * \\note\n * - In the strong form the `dg_boundary_terms` function returns \\f$G -\n *   F_\\text{int}\\f$\n * - For either \\f$\\lambda_\\text{min} = 0\\f$ or \\f$\\lambda_\\text{max} = 0\\f$\n *   (i.e. all characteristics move in the same direction) the HLL boundary\n *   correction reduces to pure upwinding.\n * - Some references use \\f$S\\f$ instead of \\f$\\lambda\\f$ for the\n *   signal/characteristic speeds\n */\ntemplate <size_t Dim>\nclass Hll final : public evolution::BoundaryCorrection {\n public:\n  struct LargestOutgoingCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n  struct LargestIngoingCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the HLL boundary correction term for the \"\n      \"Newtonian Euler/hydrodynamics system.\"};\n\n  Hll() = default;\n  Hll(const Hll&) = default;\n  Hll& operator=(const Hll&) = default;\n  Hll(Hll&&) = default;\n  Hll& operator=(Hll&&) = default;\n  ~Hll() override = default;\n\n  /// \\cond\n  explicit Hll(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Hll);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<Dim>,\n                 Tags::EnergyDensity,\n                 ::Tags::NormalDotFlux<Tags::MassDensityCons>,\n                 ::Tags::NormalDotFlux<Tags::MomentumDensity<Dim>>,\n                 ::Tags::NormalDotFlux<Tags::EnergyDensity>,\n                 LargestOutgoingCharSpeed, LargestIngoingCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<>;\n  using dg_package_data_primitive_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>>;\n  using dg_package_data_volume_tags =\n      tmpl::list<hydro::Tags::EquationOfState<false, 2>>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          packaged_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> packaged_energy_density,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          packaged_normal_dot_flux_momentum_density,\n      gsl::not_null<Scalar<DataVector>*>\n          packaged_normal_dot_flux_energy_density,\n      gsl::not_null<Scalar<DataVector>*> packaged_largest_outgoing_char_speed,\n      gsl::not_null<Scalar<DataVector>*> packaged_largest_ingoing_char_speed,\n\n      const Scalar<DataVector>& mass_density,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_mass_density,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_momentum_density,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_energy_density,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity,\n      const Scalar<DataVector>& specific_internal_energy,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const EquationsOfState::EquationOfState<false, 2>& equation_of_state)\n      const;\n\n  void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_energy_density,\n      const Scalar<DataVector>& mass_density_int,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_int,\n      const Scalar<DataVector>& energy_density_int,\n      const Scalar<DataVector>& normal_dot_flux_mass_density_int,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          normal_dot_flux_momentum_density_int,\n      const Scalar<DataVector>& normal_dot_flux_energy_density_int,\n      const Scalar<DataVector>& largest_outgoing_char_speed_int,\n      const Scalar<DataVector>& largest_ingoing_char_speed_int,\n      const Scalar<DataVector>& mass_density_ext,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_ext,\n      const Scalar<DataVector>& energy_density_ext,\n      const Scalar<DataVector>& normal_dot_flux_mass_density_ext,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          normal_dot_flux_momentum_density_ext,\n      const Scalar<DataVector>& normal_dot_flux_energy_density_ext,\n      const Scalar<DataVector>& largest_outgoing_char_speed_ext,\n      const Scalar<DataVector>& largest_ingoing_char_speed_ext,\n      dg::Formulation dg_formulation) const;\n};\n}  // namespace NewtonianEuler::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hllc.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hllc.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler::BoundaryCorrections {\ntemplate <size_t Dim>\nHllc<Dim>::Hllc(CkMigrateMessage* msg) : BoundaryCorrection(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::BoundaryCorrection> Hllc<Dim>::get_clone() const {\n  return std::make_unique<Hllc>(*this);\n}\n\ntemplate <size_t Dim>\nvoid Hllc<Dim>::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n}\n\ntemplate <size_t Dim>\ndouble Hllc<Dim>::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        packaged_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> packaged_energy_density,\n    const gsl::not_null<Scalar<DataVector>*> packaged_pressure,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_normal_dot_flux_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        packaged_normal_dot_flux_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_normal_dot_flux_energy_density,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        packaged_interface_unit_normal,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_velocity,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_largest_outgoing_char_speed,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_largest_ingoing_char_speed,\n\n    const Scalar<DataVector>& mass_density,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_mass_density,\n    const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_momentum_density,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_energy_density,\n\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity,\n    const Scalar<DataVector>& specific_internal_energy,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state)\n    const {\n  {\n    // Compute pressure\n    *packaged_pressure = equation_of_state.pressure_from_density_and_energy(\n        mass_density, specific_internal_energy);\n\n    // Compute sound speed\n    Scalar<DataVector>& sound_speed = *packaged_mass_density;\n    sound_speed_squared(make_not_null(&sound_speed), mass_density,\n                        specific_internal_energy, equation_of_state);\n    get(sound_speed) = sqrt(get(sound_speed));\n\n    // Compute normal velocity of fluid w.r.t. mesh\n    Scalar<DataVector>& normal_dot_velocity = *packaged_energy_density;\n    dot_product(make_not_null(&normal_dot_velocity), velocity, normal_covector);\n\n    // Compute the normal velocity and largest outgoing / ingoing characteristic\n    // speeds. Note that the notion of being 'largest ingoing' means taking the\n    // most negative value given that the positive direction is outward normal.\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*packaged_largest_outgoing_char_speed) =\n          get(normal_dot_velocity) + get(sound_speed) -\n          get(*normal_dot_mesh_velocity);\n      get(*packaged_largest_ingoing_char_speed) =\n          get(normal_dot_velocity) - get(sound_speed) -\n          get(*normal_dot_mesh_velocity);\n      get(*packaged_normal_dot_velocity) =\n          get(normal_dot_velocity) - get(*normal_dot_mesh_velocity);\n    } else {\n      get(*packaged_largest_outgoing_char_speed) =\n          get(normal_dot_velocity) + get(sound_speed);\n      get(*packaged_largest_ingoing_char_speed) =\n          get(normal_dot_velocity) - get(sound_speed);\n      get(*packaged_normal_dot_velocity) = get(normal_dot_velocity);\n    }\n  }\n\n  *packaged_mass_density = mass_density;\n  *packaged_momentum_density = momentum_density;\n  *packaged_energy_density = energy_density;\n  *packaged_interface_unit_normal = normal_covector;\n\n  normal_dot_flux(packaged_normal_dot_flux_mass_density, normal_covector,\n                  flux_mass_density);\n  normal_dot_flux(packaged_normal_dot_flux_momentum_density, normal_covector,\n                  flux_momentum_density);\n  normal_dot_flux(packaged_normal_dot_flux_energy_density, normal_covector,\n                  flux_energy_density);\n\n  return fmax(max(get(*packaged_largest_outgoing_char_speed)),\n              -min(get(*packaged_largest_ingoing_char_speed)));\n}\n\ntemplate <size_t Dim>\nvoid Hllc<Dim>::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_energy_density,\n    const Scalar<DataVector>& mass_density_int,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_int,\n    const Scalar<DataVector>& energy_density_int,\n    const Scalar<DataVector>& pressure_int,\n    const Scalar<DataVector>& normal_dot_flux_mass_density_int,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        normal_dot_flux_momentum_density_int,\n    const Scalar<DataVector>& normal_dot_flux_energy_density_int,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& interface_unit_normal_int,\n    const Scalar<DataVector>& normal_dot_velocity_int,\n    const Scalar<DataVector>& largest_outgoing_char_speed_int,\n    const Scalar<DataVector>& largest_ingoing_char_speed_int,\n    const Scalar<DataVector>& mass_density_ext,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_ext,\n    const Scalar<DataVector>& energy_density_ext,\n    const Scalar<DataVector>& pressure_ext,\n    const Scalar<DataVector>& normal_dot_flux_mass_density_ext,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        normal_dot_flux_momentum_density_ext,\n    const Scalar<DataVector>& normal_dot_flux_energy_density_ext,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& interface_unit_normal_ext,\n    const Scalar<DataVector>& normal_dot_velocity_ext,\n    const Scalar<DataVector>& largest_outgoing_char_speed_ext,\n    const Scalar<DataVector>& largest_ingoing_char_speed_ext,\n    const dg::Formulation dg_formulation) const {\n  // Allocate a temp buffer\n  const size_t vector_size = get(mass_density_int).size();\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>, ::Tags::TempScalar<3>,\n                       ::Tags::TempScalar<4>>>\n      temps{vector_size};\n\n  // Determine lambda_max and lambda_min from the characteristic speeds from\n  // interior and exterior\n  get(get<::Tags::TempScalar<0>>(temps)) =\n      min(0., get(largest_ingoing_char_speed_int),\n          -get(largest_outgoing_char_speed_ext));\n  const DataVector& lambda_min = get(get<::Tags::TempScalar<0>>(temps));\n  get(get<::Tags::TempScalar<1>>(temps)) =\n      max(0., get(largest_outgoing_char_speed_int),\n          -get(largest_ingoing_char_speed_ext));\n  const DataVector& lambda_max = get(get<::Tags::TempScalar<1>>(temps));\n\n  // Compute lambda_star, the contact wave speed. (cf. Eq 10.37 of Toro2009,\n  // note that here lambda instead of S was used to denote characteristic\n  // speeds)\n  get(get<::Tags::TempScalar<2>>(temps)) =\n      (get(pressure_ext) - get(pressure_int) +\n       get(mass_density_int) * get(normal_dot_velocity_int) *\n           (lambda_min - get(normal_dot_velocity_int)) +\n       get(mass_density_ext) * get(normal_dot_velocity_ext) *\n           (lambda_max + get(normal_dot_velocity_ext))) /\n      (get(mass_density_int) * (lambda_min - get(normal_dot_velocity_int)) -\n       get(mass_density_ext) * (lambda_max + get(normal_dot_velocity_ext)));\n  const DataVector& lambda_star = get(get<::Tags::TempScalar<2>>(temps));\n\n  // Precompute common numerical factors for state variables in star region\n  // (cf. Eq 10.39 of Toro2009)\n  get(get<::Tags::TempScalar<3>>(temps)) =\n      (lambda_min - get(normal_dot_velocity_int)) / (lambda_min - lambda_star);\n  const DataVector& prefactor_int = get(get<::Tags::TempScalar<3>>(temps));\n  get(get<::Tags::TempScalar<4>>(temps)) =\n      (lambda_max + get(normal_dot_velocity_ext)) / (lambda_max - lambda_star);\n  const DataVector& prefactor_ext = get(get<::Tags::TempScalar<4>>(temps));\n\n  for (size_t i = 0; i < vector_size; ++i) {\n    // check if lambda_star falls in the correct range [lambda_min,lambda_max]\n    ASSERT(\n        (lambda_star[i] <= lambda_max[i]) and (lambda_star[i] >= lambda_min[i]),\n        \"lambda_star in HLLC boundary correction is not consistent : \"\n            << \"\\n lambda_min  = \" << lambda_min[i] << \"\\n lambda_*    = \"\n            << lambda_star[i] << \"\\n lambda_max  = \" << lambda_max[i]);\n\n    if (dg_formulation == dg::Formulation::WeakInertial) {\n      // Compute intermediate flux F_star (cf. Eq 10.71 - 10.73 of Toro2009)\n      if (lambda_star[i] >= 0.0) {\n        get(*boundary_correction_mass_density)[i] =\n            get(normal_dot_flux_mass_density_int)[i] +\n            lambda_min[i] * (prefactor_int[i] - 1.0) * get(mass_density_int)[i];\n        for (size_t spatial_index = 0; spatial_index < Dim; ++spatial_index) {\n          boundary_correction_momentum_density->get(spatial_index)[i] =\n              normal_dot_flux_momentum_density_int.get(spatial_index)[i] +\n              lambda_min[i] *\n                  (get(mass_density_int)[i] * prefactor_int[i] *\n                       (lambda_star[i] - get(normal_dot_velocity_int)[i]) *\n                       interface_unit_normal_int.get(spatial_index)[i] +\n                   momentum_density_int.get(spatial_index)[i] *\n                       (prefactor_int[i] - 1.0));\n        }\n        get(*boundary_correction_energy_density)[i] =\n            get(normal_dot_flux_energy_density_int)[i] +\n            lambda_min[i] *\n                ((prefactor_int[i] - 1.0) *\n                     (get(energy_density_int)[i] + get(pressure_int)[i]) +\n                 prefactor_int[i] * get(mass_density_int)[i] * lambda_star[i] *\n                     (lambda_star[i] - get(normal_dot_velocity_int)[i]));\n      } else {\n        get(*boundary_correction_mass_density)[i] =\n            -get(normal_dot_flux_mass_density_ext)[i] +\n            lambda_max[i] * (prefactor_ext[i] - 1.0) * get(mass_density_ext)[i];\n        for (size_t spatial_index = 0; spatial_index < Dim; ++spatial_index) {\n          boundary_correction_momentum_density->get(spatial_index)[i] =\n              -normal_dot_flux_momentum_density_ext.get(spatial_index)[i] +\n              lambda_max[i] *\n                  (get(mass_density_ext)[i] * prefactor_ext[i] *\n                       (lambda_star[i] + get(normal_dot_velocity_ext)[i]) *\n                       (-interface_unit_normal_ext.get(spatial_index)[i]) +\n                   momentum_density_ext.get(spatial_index)[i] *\n                       (prefactor_ext[i] - 1.0));\n        }\n        get(*boundary_correction_energy_density)[i] =\n            -get(normal_dot_flux_energy_density_ext)[i] +\n            lambda_max[i] *\n                ((prefactor_ext[i] - 1.0) *\n                     (get(energy_density_ext)[i] + get(pressure_ext)[i]) +\n                 prefactor_ext[i] * get(mass_density_ext)[i] * lambda_star[i] *\n                     (lambda_star[i] + get(normal_dot_velocity_ext)[i]));\n      }\n    } else {\n      // Compute boundary correction for strong formulation\n      if (lambda_star[i] >= 0.0) {\n        get(*boundary_correction_mass_density)[i] =\n            lambda_min[i] * (prefactor_int[i] - 1.0) * get(mass_density_int)[i];\n        for (size_t spatial_index = 0; spatial_index < Dim; ++spatial_index) {\n          boundary_correction_momentum_density->get(spatial_index)[i] =\n              lambda_min[i] *\n              (get(mass_density_int)[i] * prefactor_int[i] *\n                   (lambda_star[i] - get(normal_dot_velocity_int)[i]) *\n                   interface_unit_normal_int.get(spatial_index)[i] +\n               momentum_density_int.get(spatial_index)[i] *\n                   (prefactor_int[i] - 1.0));\n        }\n        get(*boundary_correction_energy_density)[i] =\n            lambda_min[i] *\n            ((prefactor_int[i] - 1.0) *\n                 (get(energy_density_int)[i] + get(pressure_int)[i]) +\n             prefactor_int[i] * get(mass_density_int)[i] * lambda_star[i] *\n                 (lambda_star[i] - get(normal_dot_velocity_int)[i]));\n      } else {\n        get(*boundary_correction_mass_density)[i] =\n            -get(normal_dot_flux_mass_density_int)[i] -\n            get(normal_dot_flux_mass_density_ext)[i] +\n            lambda_max[i] * (prefactor_ext[i] - 1.0) * get(mass_density_ext)[i];\n        for (size_t spatial_index = 0; spatial_index < Dim; ++spatial_index) {\n          boundary_correction_momentum_density->get(spatial_index)[i] =\n              -normal_dot_flux_momentum_density_int.get(spatial_index)[i] -\n              normal_dot_flux_momentum_density_ext.get(spatial_index)[i] +\n              lambda_max[i] *\n                  (get(mass_density_ext)[i] * prefactor_ext[i] *\n                       (lambda_star[i] + get(normal_dot_velocity_ext)[i]) *\n                       (-interface_unit_normal_ext.get(spatial_index)[i]) +\n                   momentum_density_ext.get(spatial_index)[i] *\n                       (prefactor_ext[i] - 1.0));\n        }\n        get(*boundary_correction_energy_density)[i] =\n            -get(normal_dot_flux_energy_density_int)[i] -\n            get(normal_dot_flux_energy_density_ext)[i] +\n            lambda_max[i] *\n                ((prefactor_ext[i] - 1.0) *\n                     (get(energy_density_ext)[i] + get(pressure_ext)[i]) +\n                 prefactor_ext[i] * get(mass_density_ext)[i] * lambda_star[i] *\n                     (lambda_star[i] + get(normal_dot_velocity_ext)[i]));\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Hllc<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) template class Hllc<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hllc.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::BoundaryCorrections {\n/*!\n * \\brief The HLLC (Harten-Lax-van Leer-Contact) Riemann solver for the\n * NewtonianEuler system\n *\n * Let \\f$U\\f$ be the evolved variable, \\f$F^i\\f$ the flux, \\f$v^i\\f$ the\n * spatial velocity, and \\f$n_i\\f$ be the outward directed unit normal to the\n * interface. Denoting \\f$F := n_i F^i\\f$ and \\f$v:=n_iv^i\\f$, the HLLC boundary\n * correction is \\cite Toro1994\n *\n * \\f{align*}\n * G_\\text{HLLC} = \\left\\{\\begin{array}{lcl}\n * F_\\text{int} & \\text{if} & 0 \\leq \\lambda_\\text{min} \\\\\n * F_\\text{int} + \\lambda_\\text{min}(U_\\text{*int} - U_\\text{int}) & \\text{if} &\n * \\lambda_\\text{min} \\leq 0 \\leq \\lambda_\\text{*} \\\\\n * -F_\\text{ext} + \\lambda_\\text{max}(U_\\text{*ext} - U_\\text{ext}) & \\text{if}\n * & \\lambda_\\text{*} \\leq 0 \\leq \\lambda_\\text{max} \\\\\n * -F_\\text{ext} & \\text{if} &  \\lambda_\\text{max} \\leq 0\n * \\end{array}\\right\\}\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior.\n *\n * Intermediate ('star') states are given by\n *\n * \\f{align*}\n * U_\\text{*int} = \\left(\\frac{\\lambda_\\text{min} - v_\\text{int}}\n * {\\lambda_\\text{min} - \\lambda_*}\\right)\n * \\left[\\begin{array}{c}\n * \\displaystyle \\rho_\\text{int} \\\\\n * \\displaystyle \\rho_\\text{int}[v_\\text{int}^x + (\\lambda_* - v_\\text{int})\n * n_x^\\text{int}] \\\\\n * \\displaystyle \\rho_\\text{int}[v_\\text{int}^y + (\\lambda_* - v_\\text{int})\n * n_y^\\text{int}] \\\\\n * \\displaystyle \\rho_\\text{int}[v_\\text{int}^z + (\\lambda_* - v_\\text{int})\n * n_z^\\text{int}] \\\\\n * \\displaystyle E_\\text{int} + p_\\text{int} \\frac{\\lambda_* - v_\\text{int}}\n * {\\lambda_\\text{min} - v_\\text{int}} + \\rho_\\text{int}\\lambda_*(\\lambda_* -\n * v_\\text{int}) \\end{array}\\right]\n * \\f}\n *\n * and\n *\n * \\f{align*}\n * U_\\text{*ext} = \\left(\\frac{\\lambda_\\text{max} + v_\\text{ext}}\n * {\\lambda_\\text{max} - \\lambda_*}\\right)\n * \\left[\\begin{array}{c}\n * \\displaystyle \\rho_\\text{ext} \\\\\n * \\displaystyle \\rho_\\text{ext}[-v_\\text{ext}^x - (\\lambda_* + v_\\text{ext})\n * n_x^\\text{ext}] \\\\\n * \\displaystyle \\rho_\\text{ext}[-v_\\text{ext}^y - (\\lambda_* + v_\\text{ext})\n * n_y^\\text{ext}] \\\\\n * \\displaystyle \\rho_\\text{ext}[-v_\\text{ext}^z - (\\lambda_* + v_\\text{ext})\n * n_z^\\text{ext}] \\\\\n * \\displaystyle E_\\text{ext} + p_\\text{ext} \\frac{\\lambda_* + v_\\text{ext}}\n * {\\lambda_\\text{max} + v_\\text{ext}} + \\rho_\\text{ext}\\lambda_*(\\lambda_* +\n * v_\\text{ext}) \\end{array}\\right].\n * \\f}\n *\n * The contact wave speed \\f$\\lambda_*\\f$ is \\cite Toro2009\n *\n * \\f{align*}\n * \\lambda_* = \\frac\n * { p_\\text{ext} - p_\\text{int} +\n * \\rho_\\text{int}v_\\text{int}(\\lambda_\\text{min}-v_\\text{int})\n * + \\rho_\\text{ext}v_\\text{ext}(\\lambda_\\text{max}+v_\\text{ext})}\n * { \\rho_\\text{int}(\\lambda_\\text{min}-v_\\text{int}) -\n * \\rho_\\text{ext}(\\lambda_\\text{max} + v_\\text{ext})}.\n * \\f}\n *\n * \\f$\\lambda_\\text{min}\\f$ and \\f$\\lambda_\\text{max}\\f$ are estimated by\n * \\cite Davis1988\n *\n * \\f{align*}\n * \\lambda_\\text{min} &=\n * \\text{min}\\left(\\lambda^{-}_\\text{int},-\\lambda^{+}_\\text{ext}\\right) \\\\\n * \\lambda_\\text{max} &=\n * \\text{max}\\left(\\lambda^{+}_\\text{int},-\\lambda^{-}_\\text{ext}\\right)\n * \\f}\n *\n * where \\f$\\lambda^{+}\\f$ (\\f$\\lambda^{-}\\f$) is the largest characteristic\n * speed in the outgoing (ingoing) direction for each domain.\n *\n * Note the minus signs in front of \\f$\\lambda^{\\pm}_\\text{ext}\\f$, which is\n * because an outgoing speed w.r.t. the neighboring element is an ingoing speed\n * w.r.t. the local element, and vice versa. Similarly, the \\f$F_{\\text{ext}}\\f$\n * term in \\f$G_\\text{HLLC}\\f$ and the \\f$v_\\text{ext}\\f$ term in\n * \\f$U_\\text{*ext}\\f$ have a positive sign because the outward directed normal\n * of the neighboring element has the opposite sign, i.e.\n * \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$.\n *\n * For the NewtonianEuler system, \\f$\\lambda^\\pm\\f$ are given as\n *\n * \\f{align*}\n * \\lambda^\\pm = v \\pm c_s\n * \\f}\n *\n * where \\f$c_s\\f$ is the sound speed.\n *\n * \\note\n * - In the strong form the `dg_boundary_terms` function returns \\f$G -\n *   F_\\text{int}\\f$\n * - In the implementation, we use\n *\n *   \\f{align*}\n *   G_\\text{HLLC} = \\left\\{\\begin{array}{lcl}\n *   F_\\text{int} + \\lambda_\\text{min}(U_\\text{*int} - U_\\text{int}) & \\text{if}\n *   & 0 \\leq \\lambda_\\text{*} \\\\\n *   -F_\\text{ext} + \\lambda_\\text{max}(U_\\text{*ext} - U_\\text{ext}) &\n *   \\text{if} & \\lambda_\\text{*} \\leq 0 \\\\\n *   \\end{array}\\right\\},\n *   \\f}\n *\n *   with\n *\n *   \\f{align*}\n *   \\lambda_\\text{min} &=\n *   \\text{min}\\left(\\lambda^{-}_\\text{int},-\\lambda^{+}_\\text{ext}, 0\\right) \\\\\n *   \\lambda_\\text{max} &=\n *   \\text{max}\\left(\\lambda^{+}_\\text{int},-\\lambda^{-}_\\text{ext}, 0\\right).\n *   \\f}\n *\n *   Provided that \\f$\\lambda_*\\f$ falls in the correct range i.e\n *\n *   \\f{align*}\n *   \\text{min}\\left(\\lambda^{-}_\\text{int},-\\lambda^{+}_\\text{ext}\\right)\n *   < \\lambda_* <\n *   \\text{max}\\left(\\lambda^{+}_\\text{int},-\\lambda^{-}_\\text{ext}\\right),\n *   \\f}\n *\n *   this prescription recovers the original HLLC boundary correction for all\n *   four cases. For either \\f$\\lambda_\\text{min} = 0\\f$ or\n *   \\f$\\lambda_\\text{max} = 0\\f$ (i.e. all characteristics move in the same\n *   direction), boundary correction reduces to pure upwinding.\n * - Some references use \\f$S\\f$ instead of \\f$\\lambda\\f$ for the\n *   signal/characteristic speeds.\n */\ntemplate <size_t Dim>\nclass Hllc final : public evolution::BoundaryCorrection {\n private:\n  struct InterfaceUnitNormal : db::SimpleTag {\n    using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n  };\n  struct NormalDotVelocity : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  struct LargestOutgoingCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n  struct LargestIngoingCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the HLLC boundary correction term for the \"\n      \"Newtonian Euler/hydrodynamics system.\"};\n\n  Hllc() = default;\n  Hllc(const Hllc&) = default;\n  Hllc& operator=(const Hllc&) = default;\n  Hllc(Hllc&&) = default;\n  Hllc& operator=(Hllc&&) = default;\n  ~Hllc() override = default;\n\n  /// \\cond\n  explicit Hllc(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Hllc);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<Dim>,\n                 Tags::EnergyDensity, hydro::Tags::Pressure<DataVector>,\n                 ::Tags::NormalDotFlux<Tags::MassDensityCons>,\n                 ::Tags::NormalDotFlux<Tags::MomentumDensity<Dim>>,\n                 ::Tags::NormalDotFlux<Tags::EnergyDensity>,\n                 InterfaceUnitNormal, NormalDotVelocity,\n                 LargestOutgoingCharSpeed, LargestIngoingCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<>;\n  using dg_package_data_primitive_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>>;\n  using dg_package_data_volume_tags =\n      tmpl::list<hydro::Tags::EquationOfState<false, 2>>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          packaged_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> packaged_energy_density,\n      gsl::not_null<Scalar<DataVector>*> packaged_pressure,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          packaged_normal_dot_flux_momentum_density,\n      gsl::not_null<Scalar<DataVector>*>\n          packaged_normal_dot_flux_energy_density,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_interface_unit_normal,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_velocity,\n      gsl::not_null<Scalar<DataVector>*> packaged_largest_outgoing_char_speed,\n      gsl::not_null<Scalar<DataVector>*> packaged_largest_ingoing_char_speed,\n\n      const Scalar<DataVector>& mass_density,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_mass_density,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_momentum_density,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_energy_density,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity,\n      const Scalar<DataVector>& specific_internal_energy,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const EquationsOfState::EquationOfState<false, 2>& equation_of_state)\n      const;\n\n  void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_energy_density,\n      const Scalar<DataVector>& mass_density_int,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_int,\n      const Scalar<DataVector>& energy_density_int,\n      const Scalar<DataVector>& pressure_int,\n      const Scalar<DataVector>& normal_dot_flux_mass_density_int,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          normal_dot_flux_momentum_density_int,\n      const Scalar<DataVector>& normal_dot_flux_energy_density_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          interface_unit_normal_int,\n      const Scalar<DataVector>& normal_dot_velocity_int,\n      const Scalar<DataVector>& largest_outgoing_char_speed_int,\n      const Scalar<DataVector>& largest_ingoing_char_speed_int,\n      const Scalar<DataVector>& mass_density_ext,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_ext,\n      const Scalar<DataVector>& energy_density_ext,\n      const Scalar<DataVector>& pressure_ext,\n      const Scalar<DataVector>& normal_dot_flux_mass_density_ext,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          normal_dot_flux_momentum_density_ext,\n      const Scalar<DataVector>& normal_dot_flux_energy_density_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          interface_unit_normal_ext,\n      const Scalar<DataVector>& normal_dot_velocity_ext,\n      const Scalar<DataVector>& largest_outgoing_char_speed_ext,\n      const Scalar<DataVector>& largest_ingoing_char_speed_ext,\n      dg::Formulation dg_formulation) const;\n};\n}  // namespace NewtonianEuler::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Boundary corrections/numerical fluxes\nnamespace NewtonianEuler::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler::BoundaryCorrections {\ntemplate <size_t Dim>\nRusanov<Dim>::Rusanov(CkMigrateMessage* msg) : BoundaryCorrection(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::BoundaryCorrection> Rusanov<Dim>::get_clone() const {\n  return std::make_unique<Rusanov>(*this);\n}\n\ntemplate <size_t Dim>\nvoid Rusanov<Dim>::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n}\n\ntemplate <size_t Dim>\ndouble Rusanov<Dim>::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        packaged_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> packaged_energy_density,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_normal_dot_flux_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        packaged_normal_dot_flux_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*>\n        packaged_normal_dot_flux_energy_density,\n    const gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n\n    const Scalar<DataVector>& mass_density,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_mass_density,\n    const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_momentum_density,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_energy_density,\n\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity,\n    const Scalar<DataVector>& specific_internal_energy,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state)\n    const {\n  {\n    // Compute sound speed\n    Scalar<DataVector>& sound_speed = *packaged_mass_density;\n    Scalar<DataVector>& normal_dot_velocity = *packaged_energy_density;\n    sound_speed_squared(make_not_null(&sound_speed), mass_density,\n                        specific_internal_energy, equation_of_state);\n    get(sound_speed) = sqrt(get(sound_speed));\n    dot_product(make_not_null(&normal_dot_velocity), velocity, normal_covector);\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*packaged_abs_char_speed) =\n          max(abs(get(normal_dot_velocity) + get(sound_speed) -\n                  get(*normal_dot_mesh_velocity)),\n              abs(get(normal_dot_velocity) - get(sound_speed) -\n                  get(*normal_dot_mesh_velocity)));\n    } else {\n      get(*packaged_abs_char_speed) =\n          max(abs(get(normal_dot_velocity) + get(sound_speed)),\n              abs(get(normal_dot_velocity) - get(sound_speed)));\n    }\n  }\n\n  *packaged_mass_density = mass_density;\n  *packaged_momentum_density = momentum_density;\n  *packaged_energy_density = energy_density;\n\n  normal_dot_flux(packaged_normal_dot_flux_mass_density, normal_covector,\n                  flux_mass_density);\n  normal_dot_flux(packaged_normal_dot_flux_momentum_density, normal_covector,\n                  flux_momentum_density);\n  normal_dot_flux(packaged_normal_dot_flux_energy_density, normal_covector,\n                  flux_energy_density);\n\n  return max(get(*packaged_abs_char_speed));\n}\n\ntemplate <size_t Dim>\nvoid Rusanov<Dim>::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        boundary_correction_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_energy_density,\n    const Scalar<DataVector>& mass_density_int,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_int,\n    const Scalar<DataVector>& energy_density_int,\n    const Scalar<DataVector>& normal_dot_flux_mass_density_int,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        normal_dot_flux_momentum_density_int,\n    const Scalar<DataVector>& normal_dot_flux_energy_density_int,\n    const Scalar<DataVector>& abs_char_speed_int,\n    const Scalar<DataVector>& mass_density_ext,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_ext,\n    const Scalar<DataVector>& energy_density_ext,\n    const Scalar<DataVector>& normal_dot_flux_mass_density_ext,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>&\n        normal_dot_flux_momentum_density_ext,\n    const Scalar<DataVector>& normal_dot_flux_energy_density_ext,\n    const Scalar<DataVector>& abs_char_speed_ext,\n    const dg::Formulation dg_formulation) const {\n  if (dg_formulation == dg::Formulation::WeakInertial) {\n    get(*boundary_correction_mass_density) =\n        0.5 * (get(normal_dot_flux_mass_density_int) -\n               get(normal_dot_flux_mass_density_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(mass_density_ext) - get(mass_density_int));\n    for (size_t i = 0; i < Dim; ++i) {\n      boundary_correction_momentum_density->get(i) =\n          0.5 * (normal_dot_flux_momentum_density_int.get(i) -\n                 normal_dot_flux_momentum_density_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (momentum_density_ext.get(i) - momentum_density_int.get(i));\n    }\n    get(*boundary_correction_energy_density) =\n        0.5 * (get(normal_dot_flux_energy_density_int) -\n               get(normal_dot_flux_energy_density_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(energy_density_ext) - get(energy_density_int));\n  } else {\n    get(*boundary_correction_mass_density) =\n        -0.5 * (get(normal_dot_flux_mass_density_int) +\n                get(normal_dot_flux_mass_density_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(mass_density_ext) - get(mass_density_int));\n    for (size_t i = 0; i < Dim; ++i) {\n      boundary_correction_momentum_density->get(i) =\n          -0.5 * (normal_dot_flux_momentum_density_int.get(i) +\n                  normal_dot_flux_momentum_density_ext.get(i)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (momentum_density_ext.get(i) - momentum_density_int.get(i));\n    }\n    get(*boundary_correction_energy_density) =\n        -0.5 * (get(normal_dot_flux_energy_density_int) +\n                get(normal_dot_flux_energy_density_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(energy_density_ext) - get(energy_density_int));\n  }\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Rusanov<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) template class Rusanov<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::BoundaryCorrections {\n/*!\n * \\brief A Rusanov/local Lax-Friedrichs Riemann solver\n *\n * Let \\f$U\\f$ be the state vector of evolved variables, \\f$F^i\\f$ the\n * corresponding fluxes, and \\f$n_i\\f$ be the outward directed unit normal to\n * the interface. Denoting \\f$F := n_i F^i\\f$, the %Rusanov boundary correction\n * is\n *\n * \\f{align*}\n * G_\\text{Rusanov} = \\frac{F_\\text{int} - F_\\text{ext}}{2} -\n * \\frac{\\text{max}\\left(\\{|\\lambda_\\text{int}|\\},\n * \\{|\\lambda_\\text{ext}|\\}\\right)}{2} \\left(U_\\text{ext} - U_\\text{int}\\right),\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior, and\n * \\f$\\{|\\lambda|\\}\\f$ is the set of characteristic/signal speeds. The minus\n * sign in front of the \\f$F_{\\text{ext}}\\f$ is necessary because the outward\n * directed normal of the neighboring element has the opposite sign, i.e.\n * \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$. The characteristic/signal speeds\n * are given by:\n *\n * \\f{align*}\n * \\lambda_{\\pm}&=v^i n_i \\pm c_s, \\\\\n * \\lambda_v&=v^i n_i\n * \\f}\n *\n * where \\f$v^i\\f$ is the spatial velocity and \\f$c_s\\f$ the sound speed.\n *\n * \\note In the strong form the `dg_boundary_terms` function returns\n * \\f$G - F_\\text{int}\\f$\n */\ntemplate <size_t Dim>\nclass Rusanov final : public evolution::BoundaryCorrection {\n private:\n  struct AbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the Rusanov or local Lax-Friedrichs boundary correction term \"\n      \"for the Newtonian Euler/hydrodynamics system.\"};\n\n  Rusanov() = default;\n  Rusanov(const Rusanov&) = default;\n  Rusanov& operator=(const Rusanov&) = default;\n  Rusanov(Rusanov&&) = default;\n  Rusanov& operator=(Rusanov&&) = default;\n  ~Rusanov() override = default;\n\n  /// \\cond\n  explicit Rusanov(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Rusanov);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<Dim>,\n                 Tags::EnergyDensity,\n                 ::Tags::NormalDotFlux<Tags::MassDensityCons>,\n                 ::Tags::NormalDotFlux<Tags::MomentumDensity<Dim>>,\n                 ::Tags::NormalDotFlux<Tags::EnergyDensity>, AbsCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<>;\n  using dg_package_data_primitive_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>>;\n  using dg_package_data_volume_tags =\n      tmpl::list<hydro::Tags::EquationOfState<false, 2>>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          packaged_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> packaged_energy_density,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          packaged_normal_dot_flux_momentum_density,\n      gsl::not_null<Scalar<DataVector>*>\n          packaged_normal_dot_flux_energy_density,\n      gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n\n      const Scalar<DataVector>& mass_density,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_mass_density,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_momentum_density,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_energy_density,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity,\n      const Scalar<DataVector>& specific_internal_energy,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const EquationsOfState::EquationOfState<false, 2>& equation_of_state)\n      const;\n\n  void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_energy_density,\n      const Scalar<DataVector>& mass_density_int,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_int,\n      const Scalar<DataVector>& energy_density_int,\n      const Scalar<DataVector>& normal_dot_flux_mass_density_int,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          normal_dot_flux_momentum_density_int,\n      const Scalar<DataVector>& normal_dot_flux_energy_density_int,\n      const Scalar<DataVector>& abs_char_speed_int,\n      const Scalar<DataVector>& mass_density_ext,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density_ext,\n      const Scalar<DataVector>& energy_density_ext,\n      const Scalar<DataVector>& normal_dot_flux_mass_density_ext,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          normal_dot_flux_momentum_density_ext,\n      const Scalar<DataVector>& normal_dot_flux_energy_density_ext,\n      const Scalar<DataVector>& abs_char_speed_ext,\n      dg::Formulation dg_formulation) const;\n};\n}  // namespace NewtonianEuler::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY NewtonianEuler)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Characteristics.cpp\n  ConservativeFromPrimitive.cpp\n  Fluxes.cpp\n  InternalEnergyDensity.cpp\n  KineticEnergyDensity.cpp\n  MachNumber.cpp\n  PrimitiveFromConservative.cpp\n  RamPressure.cpp\n  SoundSpeedSquared.cpp\n  SpecificKineticEnergy.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AllSolutions.hpp\n  Characteristics.hpp\n  ConservativeFromPrimitive.hpp\n  Fluxes.hpp\n  InternalEnergyDensity.hpp\n  KineticEnergyDensity.hpp\n  MachNumber.hpp\n  PrimitiveFromConservative.hpp\n  RamPressure.hpp\n  SoundSpeedSquared.hpp\n  SpecificKineticEnergy.hpp\n  System.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  TimeDerivativeTerms.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DgSubcell\n  Domain\n  ErrorHandling\n  Events\n  Evolution\n  Hydro\n  Options\n  Serialization\n  PRIVATE\n  FiniteDifference\n  NewtonianEulerAnalyticData\n  NewtonianEulerSolutions\n  )\n\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Instantiations)\nadd_subdirectory(Limiters)\nadd_subdirectory(Sources)\nadd_subdirectory(Subcell)\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Characteristics.hpp\"\n\n#include <cmath>\n#include <iterator>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace NewtonianEuler {\nnamespace detail {\ntemplate <>\nMatrix flux_jacobian<1>(const tnsr::I<double, 1>& velocity,\n                        const double kappa_over_density,\n                        const double b_times_theta,\n                        const double specific_enthalpy,\n                        const tnsr::i<double, 1>& unit_normal) {\n  const double n_x = get<0>(unit_normal);\n  const double u = get<0>(velocity);\n  const Matrix a_x = blaze::DynamicMatrix<double>{\n      {0., 1., 0.},\n      {b_times_theta - square(u), u * (2. - kappa_over_density),\n       kappa_over_density},\n      {u * (b_times_theta - specific_enthalpy),\n       specific_enthalpy - kappa_over_density * square(u),\n       u * (kappa_over_density + 1.)}};\n  return n_x * a_x;\n}\n\ntemplate <>\nMatrix flux_jacobian<2>(const tnsr::I<double, 2>& velocity,\n                        const double kappa_over_density,\n                        const double b_times_theta,\n                        const double specific_enthalpy,\n                        const tnsr::i<double, 2>& unit_normal) {\n  const double n_x = get<0>(unit_normal);\n  const double n_y = get<1>(unit_normal);\n  const double u = get<0>(velocity);\n  const double v = get<1>(velocity);\n  const Matrix a_x = blaze::DynamicMatrix<double>{\n      {0., 1., 0., 0.},\n      {b_times_theta - square(u), u * (2. - kappa_over_density),\n       -v * kappa_over_density, kappa_over_density},\n      {-u * v, v, u, 0.},\n      {u * (b_times_theta - specific_enthalpy),\n       specific_enthalpy - kappa_over_density * square(u),\n       -u * v * kappa_over_density, u * (kappa_over_density + 1.)}};\n  const Matrix a_y = blaze::DynamicMatrix<double>{\n      {0., 0., 1., 0.},\n      {-u * v, v, u, 0.},\n      {b_times_theta - square(v), -u * kappa_over_density,\n       v * (2. - kappa_over_density), kappa_over_density},\n      {v * (b_times_theta - specific_enthalpy), -u * v * kappa_over_density,\n       specific_enthalpy - kappa_over_density * square(v),\n       v * (kappa_over_density + 1.)}};\n  return n_x * a_x + n_y * a_y;\n}\n\ntemplate <>\nMatrix flux_jacobian<3>(const tnsr::I<double, 3>& velocity,\n                        const double kappa_over_density,\n                        const double b_times_theta,\n                        const double specific_enthalpy,\n                        const tnsr::i<double, 3>& unit_normal) {\n  const double n_x = get<0>(unit_normal);\n  const double n_y = get<1>(unit_normal);\n  const double n_z = get<2>(unit_normal);\n  const double u = get<0>(velocity);\n  const double v = get<1>(velocity);\n  const double w = get<2>(velocity);\n  const Matrix a_x = blaze::DynamicMatrix<double>{\n      {0., 1., 0., 0., 0.},\n      {b_times_theta - square(u), u * (2. - kappa_over_density),\n       -v * kappa_over_density, -w * kappa_over_density, kappa_over_density},\n      {-u * v, v, u, 0., 0.},\n      {-u * w, w, 0., u, 0.},\n      {u * (b_times_theta - specific_enthalpy),\n       specific_enthalpy - kappa_over_density * square(u),\n       -u * v * kappa_over_density, -u * w * kappa_over_density,\n       u * (kappa_over_density + 1.)}};\n  const Matrix a_y = blaze::DynamicMatrix<double>{\n      {0., 0., 1., 0., 0.},\n      {-u * v, v, u, 0., 0.},\n      {b_times_theta - square(v), -u * kappa_over_density,\n       v * (2. - kappa_over_density), -w * kappa_over_density,\n       kappa_over_density},\n      {-v * w, 0., w, v, 0.},\n      {v * (b_times_theta - specific_enthalpy), -u * v * kappa_over_density,\n       specific_enthalpy - kappa_over_density * square(v),\n       -v * w * kappa_over_density, v * (kappa_over_density + 1.)}};\n  const Matrix a_z = blaze::DynamicMatrix<double>{\n      {0., 0., 0., 1., 0.},\n      {-u * w, w, 0., u, 0.},\n      {-v * w, 0., w, v, 0.},\n      {b_times_theta - square(w), -u * kappa_over_density,\n       -v * kappa_over_density, w * (2. - kappa_over_density),\n       kappa_over_density},\n      {w * (b_times_theta - specific_enthalpy), -u * w * kappa_over_density,\n       -v * w * kappa_over_density,\n       specific_enthalpy - kappa_over_density * square(w),\n       w * (kappa_over_density + 1.)}};\n  return n_x * a_x + n_y * a_y + n_z * a_z;\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nvoid characteristic_speeds(\n    const gsl::not_null<std::array<DataVector, Dim + 2>*> char_speeds,\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& sound_speed,\n    const tnsr::i<DataVector, Dim>& normal) {\n  auto& characteristic_speeds = *char_speeds;\n  characteristic_speeds =\n      make_array<Dim + 2>(DataVector(get(dot_product(velocity, normal))));\n\n  characteristic_speeds[0] -= get(sound_speed);\n  characteristic_speeds[Dim + 1] += get(sound_speed);\n}\n\ntemplate <size_t Dim>\nstd::array<DataVector, Dim + 2> characteristic_speeds(\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& sound_speed,\n    const tnsr::i<DataVector, Dim>& normal) {\n  std::array<DataVector, Dim + 2> char_speeds{};\n  characteristic_speeds(make_not_null(&char_speeds), velocity, sound_speed,\n                        normal);\n  return char_speeds;\n}\n\ntemplate <>\nMatrix right_eigenvectors<1>(const tnsr::I<double, 1>& velocity,\n                             const Scalar<double>& sound_speed_squared,\n                             const Scalar<double>& specific_enthalpy,\n                             const Scalar<double>& kappa_over_density,\n                             const tnsr::i<double, 1>& unit_normal) {\n  ASSERT(equal_within_roundoff(get(magnitude(unit_normal)), 1.),\n         \"Expected unit normal, but got normal with magnitude \"\n             << get(magnitude(unit_normal)));\n\n  const double u = get<0>(velocity);\n  const double n_x = get<0>(unit_normal);\n  const double c = sqrt(get(sound_speed_squared));\n  const double v_n = get(dot_product(velocity, unit_normal));\n\n  Matrix result(3, 3);\n  result(0, 0) = 1.;\n  result(0, 1) = get(kappa_over_density);\n  result(0, 2) = 1.;\n  result(1, 0) = u - n_x * c;\n  result(1, 1) = get(kappa_over_density) * u;\n  result(1, 2) = u + n_x * c;\n  result(2, 0) = get(specific_enthalpy) - c * v_n;\n  result(2, 1) = get(kappa_over_density) * get(specific_enthalpy) -\n                 get(sound_speed_squared);\n  result(2, 2) = get(specific_enthalpy) + c * v_n;\n  return result;\n}\n\ntemplate <>\nMatrix right_eigenvectors<2>(const tnsr::I<double, 2>& velocity,\n                             const Scalar<double>& sound_speed_squared,\n                             const Scalar<double>& specific_enthalpy,\n                             const Scalar<double>& kappa_over_density,\n                             const tnsr::i<double, 2>& unit_normal) {\n  ASSERT(equal_within_roundoff(get(magnitude(unit_normal)), 1.),\n         \"Expected unit normal, but got normal with magnitude \"\n             << get(magnitude(unit_normal)));\n\n  const double u = get<0>(velocity);\n  const double v = get<1>(velocity);\n  const double n_x = get<0>(unit_normal);\n  const double n_y = get<1>(unit_normal);\n  const double c = sqrt(get(sound_speed_squared));\n  const double v_n = get(dot_product(velocity, unit_normal));\n\n  Matrix result(4, 4);\n  result(0, 0) = 1.;\n  result(0, 1) = get(kappa_over_density);\n  result(0, 2) = 0.;\n  result(0, 3) = 1.;\n  result(1, 0) = u - n_x * c;\n  result(1, 1) = get(kappa_over_density) * u;\n  result(1, 2) = -n_y;\n  result(1, 3) = u + n_x * c;\n  result(2, 0) = v - n_y * c;\n  result(2, 1) = get(kappa_over_density) * v;\n  result(2, 2) = n_x;\n  result(2, 3) = v + n_y * c;\n  result(3, 0) = get(specific_enthalpy) - c * v_n;\n  result(3, 1) = get(kappa_over_density) * get(specific_enthalpy) -\n                 get(sound_speed_squared);\n  result(3, 2) = -n_y * u + n_x * v;\n  result(3, 3) = get(specific_enthalpy) + c * v_n;\n  return result;\n}\n\ntemplate <>\nMatrix right_eigenvectors<3>(const tnsr::I<double, 3>& velocity,\n                             const Scalar<double>& sound_speed_squared,\n                             const Scalar<double>& specific_enthalpy,\n                             const Scalar<double>& kappa_over_density,\n                             const tnsr::i<double, 3>& unit_normal) {\n  ASSERT(equal_within_roundoff(get(magnitude(unit_normal)), 1.),\n         \"Expected unit normal, but got normal with magnitude \"\n             << get(magnitude(unit_normal)));\n\n  const double u = get<0>(velocity);\n  const double v = get<1>(velocity);\n  const double w = get<2>(velocity);\n  const double n_x = get<0>(unit_normal);\n  const double n_y = get<1>(unit_normal);\n  const double n_z = get<2>(unit_normal);\n  const double c = sqrt(get(sound_speed_squared));\n  const double v_n = get(dot_product(velocity, unit_normal));\n\n  Matrix result(5, 5);\n  result(0, 0) = 1.;\n  result(1, 0) = u - n_x * c;\n  result(2, 0) = v - n_y * c;\n  result(3, 0) = w - n_z * c;\n  result(4, 0) = get(specific_enthalpy) - v_n * c;\n  result(0, 1) = get(kappa_over_density);\n  result(1, 1) = get(kappa_over_density) * u;\n  result(2, 1) = get(kappa_over_density) * v;\n  result(3, 1) = get(kappa_over_density) * w;\n  result(4, 1) = get(kappa_over_density) * get(specific_enthalpy) -\n                 get(sound_speed_squared);\n  result(0, 4) = 1.;\n  result(1, 4) = u + n_x * c;\n  result(2, 4) = v + n_y * c;\n  result(3, 4) = w + n_z * c;\n  result(4, 4) = get(specific_enthalpy) + v_n * c;\n\n  // There is some degeneracy in the choice of the column eigenvectors. The row\n  // eigenvectors are chosen so that the largest of (n_x, n_y, n_z) appears in\n  // the denominator, because this avoids division by zero. Here we make the\n  // consistent choice of right eigenvectors.\n  const auto index_of_largest = std::distance(\n      unit_normal.begin(),\n      alg::max_element(unit_normal, [](const double n1, const double n2) {\n        return fabs(n1) < fabs(n2);\n      }));\n  if (index_of_largest == 0) {\n    // right eigenvectors corresponding to left eigenvectors with 1/n_x terms\n    result(0, 2) = 0.;\n    result(1, 2) = -n_y;\n    result(2, 2) = n_x;\n    result(3, 2) = 0.;\n    result(4, 2) = -n_y * u + n_x * v;\n    result(0, 3) = 0.;\n    result(1, 3) = -n_z;\n    result(2, 3) = 0.;\n    result(3, 3) = n_x;\n    result(4, 3) = -n_z * u + n_x * w;\n  } else if (index_of_largest == 1) {\n    // right eigenvectors corresponding to left eigenvectors with 1/n_y terms\n    result(0, 2) = 0.;\n    result(1, 2) = -n_y;\n    result(2, 2) = n_x;\n    result(3, 2) = 0.;\n    result(4, 2) = -n_y * u + n_x * v;\n    result(0, 3) = 0.;\n    result(1, 3) = 0.;\n    result(2, 3) = -n_z;\n    result(3, 3) = n_y;\n    result(4, 3) = -n_z * v + n_y * w;\n  } else {\n    // right eigenvectors corresponding to left eigenvectors with 1/n_z terms\n    result(0, 2) = 0.;\n    result(1, 2) = -n_z;\n    result(2, 2) = 0.;\n    result(3, 2) = n_x;\n    result(4, 2) = -n_z * u + n_x * w;\n    result(0, 3) = 0.;\n    result(1, 3) = 0.;\n    result(2, 3) = -n_z;\n    result(3, 3) = n_y;\n    result(4, 3) = -n_z * v + n_y * w;\n  }\n  return result;\n}\n\ntemplate <>\nMatrix left_eigenvectors<1>(const tnsr::I<double, 1>& velocity,\n                            const Scalar<double>& sound_speed_squared,\n                            const Scalar<double>& specific_enthalpy,\n                            const Scalar<double>& kappa_over_density,\n                            const tnsr::i<double, 1>& unit_normal) {\n  ASSERT(equal_within_roundoff(get(magnitude(unit_normal)), 1.),\n         \"Expected unit normal, but got normal with magnitude \"\n             << get(magnitude(unit_normal)));\n\n  const double velocity_squared = get(dot_product(velocity, velocity));\n  const double u = get<0>(velocity);\n  const double n_x = get<0>(unit_normal);\n  const double c = sqrt(get(sound_speed_squared));\n  const double v_n = get(dot_product(velocity, unit_normal));\n\n  // Temporary with a useful combination, as per Kulikovskii Ch3\n  const double b_times_theta =\n      get(kappa_over_density) * (velocity_squared - get(specific_enthalpy)) +\n      get(sound_speed_squared);\n\n  Matrix result(3, 3);\n  result(0, 0) = 0.5 * (b_times_theta + c * v_n) / get(sound_speed_squared);\n  result(0, 1) =\n      -0.5 * (get(kappa_over_density) * u + n_x * c) / get(sound_speed_squared);\n  result(0, 2) = 0.5 * get(kappa_over_density) / get(sound_speed_squared);\n  result(1, 0) =\n      (get(specific_enthalpy) - velocity_squared) / get(sound_speed_squared);\n  result(1, 1) = u / get(sound_speed_squared);\n  result(1, 2) = -1. / get(sound_speed_squared);\n  result(2, 0) = 0.5 * (b_times_theta - c * v_n) / get(sound_speed_squared);\n  result(2, 1) =\n      -0.5 * (get(kappa_over_density) * u - n_x * c) / get(sound_speed_squared);\n  result(2, 2) = 0.5 * get(kappa_over_density) / get(sound_speed_squared);\n  return result;\n}\n\ntemplate <>\nMatrix left_eigenvectors<2>(const tnsr::I<double, 2>& velocity,\n                            const Scalar<double>& sound_speed_squared,\n                            const Scalar<double>& specific_enthalpy,\n                            const Scalar<double>& kappa_over_density,\n                            const tnsr::i<double, 2>& unit_normal) {\n  ASSERT(equal_within_roundoff(get(magnitude(unit_normal)), 1.),\n         \"Expected unit normal, but got normal with magnitude \"\n             << get(magnitude(unit_normal)));\n\n  const double velocity_squared = get(dot_product(velocity, velocity));\n  const double u = get<0>(velocity);\n  const double v = get<1>(velocity);\n  const double n_x = get<0>(unit_normal);\n  const double n_y = get<1>(unit_normal);\n  const double c = sqrt(get(sound_speed_squared));\n  const double v_n = get(dot_product(velocity, unit_normal));\n\n  // Temporary with a useful combination, as per Kulikovskii Ch3\n  const double b_times_theta =\n      get(kappa_over_density) * (velocity_squared - get(specific_enthalpy)) +\n      get(sound_speed_squared);\n\n  Matrix result(4, 4);\n  result(0, 0) = 0.5 * (b_times_theta + c * v_n) / get(sound_speed_squared);\n  result(0, 1) =\n      -0.5 * (get(kappa_over_density) * u + n_x * c) / get(sound_speed_squared);\n  result(0, 2) =\n      -0.5 * (get(kappa_over_density) * v + n_y * c) / get(sound_speed_squared);\n  result(0, 3) = 0.5 * get(kappa_over_density) / get(sound_speed_squared);\n  result(1, 0) =\n      (get(specific_enthalpy) - velocity_squared) / get(sound_speed_squared);\n  result(1, 1) = u / get(sound_speed_squared);\n  result(1, 2) = v / get(sound_speed_squared);\n  result(1, 3) = -1. / get(sound_speed_squared);\n  result(2, 0) = n_y * u - n_x * v;\n  result(2, 1) = -n_y;\n  result(2, 2) = n_x;\n  result(2, 3) = 0.;\n  result(3, 0) = 0.5 * (b_times_theta - c * v_n) / get(sound_speed_squared);\n  result(3, 1) =\n      -0.5 * (get(kappa_over_density) * u - n_x * c) / get(sound_speed_squared);\n  result(3, 2) =\n      -0.5 * (get(kappa_over_density) * v - n_y * c) / get(sound_speed_squared);\n  result(3, 3) = 0.5 * get(kappa_over_density) / get(sound_speed_squared);\n  return result;\n}\n\ntemplate <>\nMatrix left_eigenvectors<3>(const tnsr::I<double, 3>& velocity,\n                            const Scalar<double>& sound_speed_squared,\n                            const Scalar<double>& specific_enthalpy,\n                            const Scalar<double>& kappa_over_density,\n                            const tnsr::i<double, 3>& unit_normal) {\n  ASSERT(equal_within_roundoff(get(magnitude(unit_normal)), 1.),\n         \"Expected unit normal, but got normal with magnitude \"\n             << get(magnitude(unit_normal)));\n\n  const double velocity_squared = get(dot_product(velocity, velocity));\n  const double u = get<0>(velocity);\n  const double v = get<1>(velocity);\n  const double w = get<2>(velocity);\n  const double n_x = get<0>(unit_normal);\n  const double n_y = get<1>(unit_normal);\n  const double n_z = get<2>(unit_normal);\n  const double c = sqrt(get(sound_speed_squared));\n  const double v_n = get(dot_product(velocity, unit_normal));\n\n  // Temporary with a useful combination, as per Kulikovskii Ch3\n  const double b_times_theta =\n      get(kappa_over_density) * (velocity_squared - get(specific_enthalpy)) +\n      get(sound_speed_squared);\n\n  Matrix result(5, 5);\n  result(0, 0) = 0.5 * (b_times_theta + c * v_n) / get(sound_speed_squared);\n  result(0, 1) =\n      -0.5 * (get(kappa_over_density) * u + n_x * c) / get(sound_speed_squared);\n  result(0, 2) =\n      -0.5 * (get(kappa_over_density) * v + n_y * c) / get(sound_speed_squared);\n  result(0, 3) =\n      -0.5 * (get(kappa_over_density) * w + n_z * c) / get(sound_speed_squared);\n  result(0, 4) = 0.5 * get(kappa_over_density) / get(sound_speed_squared);\n  result(1, 0) =\n      (get(specific_enthalpy) - velocity_squared) / get(sound_speed_squared);\n  result(1, 1) = u / get(sound_speed_squared);\n  result(1, 2) = v / get(sound_speed_squared);\n  result(1, 3) = w / get(sound_speed_squared);\n  result(1, 4) = -1. / get(sound_speed_squared);\n  result(4, 0) = 0.5 * (b_times_theta - c * v_n) / get(sound_speed_squared);\n  result(4, 1) =\n      -0.5 * (get(kappa_over_density) * u - n_x * c) / get(sound_speed_squared);\n  result(4, 2) =\n      -0.5 * (get(kappa_over_density) * v - n_y * c) / get(sound_speed_squared);\n  result(4, 3) =\n      -0.5 * (get(kappa_over_density) * w - n_z * c) / get(sound_speed_squared);\n  result(4, 4) = 0.5 * get(kappa_over_density) / get(sound_speed_squared);\n\n  // There is some degeneracy in the choice of the row eigenvectors. Here, we\n  // use rows where the largest of (n_x, n_y, n_z) appears in the denominator,\n  // because this avoids division by zero. A consistent choice of right\n  // eigenvectors must be made.\n  const auto index_of_largest = std::distance(\n      unit_normal.begin(),\n      alg::max_element(unit_normal, [](const double n1, const double n2) {\n        return fabs(n1) < fabs(n2);\n      }));\n  if (index_of_largest == 0) {\n    // left eigenvectors with 1/n_x terms\n    result(2, 0) = (n_y * v_n - v) / n_x;\n    result(2, 1) = -n_y;\n    result(2, 2) = (1. - square(n_y)) / n_x;\n    result(2, 3) = -n_y * n_z / n_x;\n    result(2, 4) = 0.;\n    result(3, 0) = (n_z * v_n - w) / n_x;\n    result(3, 1) = -n_z;\n    result(3, 2) = -n_y * n_z / n_x;\n    result(3, 3) = n_x + square(n_y) / n_x;\n    result(3, 4) = 0.;\n  } else if (index_of_largest == 1) {\n    // left eigenvectors with 1/n_y terms\n    result(2, 0) = (u - n_x * v_n) / n_y;\n    result(2, 1) = (-1. + square(n_x)) / n_y;\n    result(2, 2) = n_x;\n    result(2, 3) = n_x * n_z / n_y;\n    result(2, 4) = 0.;\n    result(3, 0) = (n_z * v_n - w) / n_y;\n    result(3, 1) = -n_x * n_z / n_y;\n    result(3, 2) = -n_z;\n    result(3, 3) = n_y + square(n_x) / n_y;\n    result(3, 4) = 0.;\n  } else {\n    // left eigenvectors with 1/n_z terms\n    result(2, 0) = (u - n_x * v_n) / n_z;\n    result(2, 1) = (-1. + square(n_x)) / n_z;\n    result(2, 2) = n_x * n_y / n_z;\n    result(2, 3) = n_x;\n    result(2, 4) = 0.;\n    result(3, 0) = (v - n_y * v_n) / n_z;\n    result(3, 1) = n_x * n_y / n_z;\n    result(3, 2) = (-1. + square(n_y)) / n_z;\n    result(3, 3) = n_y;\n    result(3, 4) = 0.;\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::pair<DataVector, std::pair<Matrix, Matrix>> numerical_eigensystem(\n    const tnsr::I<double, Dim>& velocity,\n    const Scalar<double>& sound_speed_squared,\n    const Scalar<double>& specific_enthalpy,\n    const Scalar<double>& kappa_over_density,\n    const tnsr::i<double, Dim>& unit_normal) {\n  ASSERT(equal_within_roundoff(get(magnitude(unit_normal)), 1.),\n         \"Expected unit normal, but got normal with magnitude \"\n             << get(magnitude(unit_normal)));\n\n  const double b_times_theta =\n      get(kappa_over_density) *\n          (get(dot_product(velocity, velocity)) - get(specific_enthalpy)) +\n      get(sound_speed_squared);\n\n  const Matrix a = detail::flux_jacobian<Dim>(\n      velocity, get(kappa_over_density), b_times_theta, get(specific_enthalpy),\n      unit_normal);\n\n  const double vn = get(dot_product(velocity, unit_normal));\n  const double cs = sqrt(get(sound_speed_squared));\n  DataVector eigenvalues(Dim + 2, vn);\n  eigenvalues[0] -= cs;\n  eigenvalues[Dim + 1] += cs;\n\n  Matrix right(Dim + 2, Dim + 2);\n\n  // We'd like to use `blaze::eigen` to get the eigenvalues and eigenvectors\n  // of the flux Jacobian matrix `a`... but because `a` is not symmetric,\n  // blaze generically produces complex eigenvectors. So instead we find the\n  // nullspace of `a - \\lambda I` using `blaze::svd`.\n  blaze::DynamicMatrix<double, blaze::rowMajor> a_minus_lambda;\n  blaze::DynamicMatrix<double, blaze::rowMajor> U;      // left singular vectors\n  blaze::DynamicVector<double, blaze::columnVector> s;  // singular values\n  blaze::DynamicMatrix<double, blaze::rowMajor> V;  // right singular vectors\n\n  const auto find_group_of_eigenvectors =\n      [&a_minus_lambda, &a, &U, &s, &V, &eigenvalues, &right](\n          const size_t index, const size_t degeneracy) {\n        a_minus_lambda = a;\n        for (size_t i = 0; i < Dim + 2; ++i) {\n          a_minus_lambda(i, i) -= eigenvalues[index];\n        }\n        blaze::svd(a_minus_lambda, U, s, V);\n\n    // Check the null space has the expected size: the last degeneracy\n    // singular values should vanish\n#ifdef SPECTRE_DEBUG\n        for (size_t i = 0; i < Dim + 2 - degeneracy; ++i) {\n          ASSERT(fabs(s[i]) > 1e-14, \"Bad SVD\");\n        }\n        for (size_t i = Dim + 2 - degeneracy; i < Dim + 2; ++i) {\n          ASSERT(fabs(s[i]) < 1e-14, \"Bad SVD\");\n        }\n#endif  // ifdef SPECTRE_DEBUG\n\n        // Copy the last degeneracy rows of V into the\n        // (index, index+degeneracy) columns of right\n        for (size_t i = 0; i < Dim + 2; ++i) {\n          for (size_t j = 0; j < degeneracy; ++j) {\n            right(i, index + j) = V(Dim + 2 - degeneracy + j, i);\n          }\n        }\n      };\n\n  // lambda = vn - cs\n  find_group_of_eigenvectors(0, 1);\n  // Dim-degenerate eigenvalues, lambda = vn\n  find_group_of_eigenvectors(1, Dim);\n  // lambda = vn + cs\n  find_group_of_eigenvectors(Dim + 1, 1);\n\n  Matrix left = right;\n  blaze::invert<blaze::asGeneral>(left);\n\n  return std::make_pair(eigenvalues, std::make_pair(right, left));\n}\n\n}  // namespace NewtonianEuler\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void NewtonianEuler::characteristic_speeds(                         \\\n      const gsl::not_null<std::array<DataVector, DIM(data) + 2>*> char_speeds, \\\n      const tnsr::I<DataVector, DIM(data)>& velocity,                          \\\n      const Scalar<DataVector>& sound_speed,                                   \\\n      const tnsr::i<DataVector, DIM(data)>& normal);                           \\\n  template std::array<DataVector, DIM(data) + 2>                               \\\n  NewtonianEuler::characteristic_speeds(                                       \\\n      const tnsr::I<DataVector, DIM(data)>& velocity,                          \\\n      const Scalar<DataVector>& sound_speed,                                   \\\n      const tnsr::i<DataVector, DIM(data)>& normal);                           \\\n  template struct NewtonianEuler::Tags::ComputeLargestCharacteristicSpeed<DIM( \\\n      data)>;                                                                  \\\n  template std::pair<DataVector, std::pair<Matrix, Matrix>>                    \\\n  NewtonianEuler::numerical_eigensystem(                                       \\\n      const tnsr::I<double, DIM(data)>&, const Scalar<double>&,                \\\n      const Scalar<double>&, const Scalar<double>&,                            \\\n      const tnsr::i<double, DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace NewtonianEuler {\n\nnamespace detail {\n// Compute the flux Jacobian for the NewtonianEuler system\n//\n// The flux Jacobian is \\f$n_i A^i = n_i \\partial F^i / \\partial U\\f$.\n// The input `b_times_theta` is \\f$b\\theta = \\kappa/\\rho (v^2 - h) + c_s^2\\f$.\n//\n// This is used for:\n// - testing the analytic characteristic transformation\n// - as input for the numerical characteristic transformation\ntemplate <size_t Dim>\nMatrix flux_jacobian(const tnsr::I<double, Dim>& velocity,\n                     double kappa_over_density, double b_times_theta,\n                     double specific_enthalpy,\n                     const tnsr::i<double, Dim>& unit_normal);\n}  // namespace detail\n\n/// @{\n/*!\n * \\brief Compute the characteristic speeds of NewtonianEuler system\n *\n * The principal symbol of the system is diagonalized so that the elements of\n * the diagonal matrix are the characteristic speeds\n *\n * \\f{align*}\n * \\lambda_1 &= v_n - c_s,\\\\\n * \\lambda_{i + 1} &= v_n,\\\\\n * \\lambda_{\\text{Dim} + 2} &= v_n + c_s,\n * \\f}\n *\n * where \\f$i = 1,...,\\text{Dim}\\f$,\n * \\f$v_n = n_i v^i\\f$ is the velocity projected onto the normal,\n * and \\f$c_s\\f$ is the sound speed.\n */\ntemplate <size_t Dim>\nvoid characteristic_speeds(\n    gsl::not_null<std::array<DataVector, Dim + 2>*> char_speeds,\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& sound_speed,\n    const tnsr::i<DataVector, Dim>& normal);\n\ntemplate <size_t Dim>\nstd::array<DataVector, Dim + 2> characteristic_speeds(\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& sound_speed,\n    const tnsr::i<DataVector, Dim>& normal);\n/// @}\n\n/// @{\n/*!\n * \\brief Compute the transform matrices between the conserved variables and\n * the characteristic variables of the NewtonianEuler system.\n *\n * Let \\f$u\\f$ be the conserved (i.e., evolved) variables of the Newtonian Euler\n * system, and \\f$w\\f$ the characteristic variables of this system with respect\n * to a unit normal one form \\f$n_i\\f$.  The function `left_eigenvectors`\n * computes the matrix \\f$\\Omega_{L}\\f$ corresponding to the transform\n * \\f$w = \\Omega_{L} u\\f$. The function `right_eigenvectors` computes the matrix\n * \\f$\\Omega_{R}\\f$ corresponding to the inverse transform\n * \\f$u = \\Omega_{R} w\\f$. Here the components of \\f$u\\f$ are ordered as\n * \\f$u = \\{\\rho, \\rho v_x, \\rho v_y, \\rho v_z, e\\}\\f$ in 3D, and the components\n * of \\f$w\\f$ are ordered by their corresponding eigenvalues\n * (i.e., characteristic speeds)\n * \\f$\\lambda = \\{v_n - c_s, v_n, v_n, v_n, v_n + c_s\\}\\f$. In these\n * expressions, \\f$\\rho\\f$ is the fluid mass density, \\f$v_{x,y,z}\\f$ are the\n * components of the fluid velocity, \\f$e\\f$ is the total energy density,\n * \\f$v_n\\f$ is the component of the velocity along the unit normal \\f$n_i\\f$,\n * and \\f$c_s\\f$ is the sound speed.\n *\n * For a short discussion of the characteristic transformation and the matrices\n * \\f$\\Omega_{L}\\f$ and \\f$\\Omega_{R}\\f$, see \\cite Kulikovskii2000 Chapter 3.\n *\n * Here we briefly summarize the procedure. With \\f$F^x(u)\\f$ the Newtonian\n * Euler flux in direction \\f$x\\f$, then the flux Jacobian along \\f$x\\f$ is the\n * matrix \\f$A_x = \\partial F^x_{\\beta}(u) / \\partial u_{\\alpha}\\f$. The indices\n * \\f$\\alpha, \\beta\\f$ range over the different evolved fields. In higher\n * dimensions, the flux Jacobian along the unit normal \\f$n_i\\f$ is\n * \\f$A = n_x A_x + n_y A_y + n_z A_z\\f$.\n * This matrix can be diagonalized as \\f$A = \\Omega_{R} \\Lambda \\Omega_{L}\\f$.\n * Here \\f$\\Lambda = \\mathrm{diag}(v_n - c_s, v_n, v_n, v_n, v_n + c_s)\\f$\n * is a diagonal matrix containing the characteristic speeds; \\f$\\Omega_{R}\\f$\n * is a matrix whose columns are the right eigenvectors of \\f$A\\f$;\n * \\f$\\Omega_{L}\\f$ is the inverse of \\f$R\\f$.\n */\ntemplate <size_t Dim>\nMatrix right_eigenvectors(const tnsr::I<double, Dim>& velocity,\n                          const Scalar<double>& sound_speed_squared,\n                          const Scalar<double>& specific_enthalpy,\n                          const Scalar<double>& kappa_over_density,\n                          const tnsr::i<double, Dim>& unit_normal);\n\ntemplate <size_t Dim>\nMatrix left_eigenvectors(const tnsr::I<double, Dim>& velocity,\n                         const Scalar<double>& sound_speed_squared,\n                         const Scalar<double>& specific_enthalpy,\n                         const Scalar<double>& kappa_over_density,\n                         const tnsr::i<double, Dim>& unit_normal);\n/// @}\n\n/*!\n * \\brief Compute the transform matrices between the conserved variables and\n * the characteristic variables of the NewtonianEuler system.\n *\n * See `right_eigenvectors` and `left_eigenvectors` for more details.\n *\n * However, note that this function computes the transformation (i.e., the\n * eigenvectors of the flux Jacobian) numerically, instead of using the analytic\n * expressions. This is useful as a proof-of-concept for more complicated\n * systems where the analytic expressions may not be known.\n */\ntemplate <size_t Dim>\nstd::pair<DataVector, std::pair<Matrix, Matrix>> numerical_eigensystem(\n    const tnsr::I<double, Dim>& velocity,\n    const Scalar<double>& sound_speed_squared,\n    const Scalar<double>& specific_enthalpy,\n    const Scalar<double>& kappa_over_density,\n    const tnsr::i<double, Dim>& unit_normal);\n\nnamespace Tags {\n\ntemplate <size_t Dim>\nstruct CharacteristicSpeedsCompute : CharacteristicSpeeds<Dim>, db::ComputeTag {\n  using base = CharacteristicSpeeds<Dim>;\n  using argument_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 SoundSpeed<DataVector>,\n                 ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim>>>;\n\n  using return_type = std::array<DataVector, Dim + 2>;\n\n  static constexpr void function(const gsl::not_null<return_type*> result,\n                                 const tnsr::I<DataVector, Dim>& velocity,\n                                 const Scalar<DataVector>& sound_speed,\n                                 const tnsr::i<DataVector, Dim>& normal) {\n    characteristic_speeds<Dim>(result, velocity, sound_speed, normal);\n  }\n};\n\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <size_t Dim>\nstruct ComputeLargestCharacteristicSpeed : LargestCharacteristicSpeed,\n                                           db::ComputeTag {\n  using argument_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 Tags::SoundSpeed<DataVector>>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(const gsl::not_null<double*> speed,\n                       const tnsr::I<DataVector, Dim>& velocity,\n                       const Scalar<DataVector>& sound_speed) {\n    *speed = max(get(magnitude(velocity)) + get(sound_speed));\n  }\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler {\n\ntemplate <size_t Dim>\nvoid ConservativeFromPrimitive<Dim>::apply(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const Scalar<DataVector>& mass_density,\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& specific_internal_energy) {\n  get(*mass_density_cons) = get(mass_density);\n\n  for (size_t i = 0; i < Dim; ++i) {\n    momentum_density->get(i) = get(mass_density) * velocity.get(i);\n  }\n\n  get(*energy_density) =\n      get(mass_density) * (0.5 * get(dot_product(velocity, velocity)) +\n                           get(specific_internal_energy));\n}\n\n}  // namespace NewtonianEuler\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) \\\n  template struct NewtonianEuler::ConservativeFromPrimitive<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace NewtonianEuler {\n\n/*!\n * \\brief Compute the conservative variables from the primitive variables.\n *\n * \\f{align*}\n * S^i &= \\rho v^i \\\\\n * e &= \\dfrac{1}{2}\\rho v^2 + \\rho\\epsilon\n * \\f}\n *\n * where \\f$S^i\\f$ is the momentum density, \\f$e\\f$ is the energy density,\n * \\f$\\rho\\f$ is the mass density, \\f$v^i\\f$ is the velocity, \\f$v^2\\f$ is its\n * magnitude squared, and \\f$\\epsilon\\f$ is the specific internal energy.\n * In addition, this method returns the mass density as a conservative.\n */\ntemplate <size_t Dim>\nstruct ConservativeFromPrimitive {\n  using return_tags =\n      tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<Dim>,\n                 Tags::EnergyDensity>;\n\n  using argument_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>>;\n\n  static void apply(gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n                    gsl::not_null<tnsr::I<DataVector, Dim>*> momentum_density,\n                    gsl::not_null<Scalar<DataVector>*> energy_density,\n                    const Scalar<DataVector>& mass_density,\n                    const tnsr::I<DataVector, Dim>& velocity,\n                    const Scalar<DataVector>& specific_internal_energy);\n};\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/AoWeno.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/AoWeno.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/ReconstructWork.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/AoWeno.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::fd {\ntemplate <size_t Dim>\nAoWeno53Prim<Dim>::AoWeno53Prim(const double gamma_hi, const double gamma_lo,\n                                const double epsilon,\n                                const size_t nonlinear_weight_exponent)\n    : gamma_hi_(gamma_hi),\n      gamma_lo_(gamma_lo),\n      epsilon_(epsilon),\n      nonlinear_weight_exponent_(nonlinear_weight_exponent) {\n  std::tie(reconstruct_, reconstruct_lower_neighbor_,\n           reconstruct_upper_neighbor_) =\n      ::fd::reconstruction::aoweno_53_function_pointers<Dim>(\n          nonlinear_weight_exponent_);\n}\n\ntemplate <size_t Dim>\nAoWeno53Prim<Dim>::AoWeno53Prim(CkMigrateMessage* const msg)\n    : Reconstructor<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<Reconstructor<Dim>> AoWeno53Prim<Dim>::get_clone() const {\n  return std::make_unique<AoWeno53Prim>(*this);\n}\n\ntemplate <size_t Dim>\nvoid AoWeno53Prim<Dim>::pup(PUP::er& p) {\n  Reconstructor<Dim>::pup(p);\n  p | gamma_hi_;\n  p | gamma_lo_;\n  p | epsilon_;\n  p | nonlinear_weight_exponent_;\n  if (p.isUnpacking()) {\n    std::tie(reconstruct_, reconstruct_lower_neighbor_,\n             reconstruct_upper_neighbor_) =\n        ::fd::reconstruction::aoweno_53_function_pointers<Dim>(\n            nonlinear_weight_exponent_);\n  }\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID AoWeno53Prim<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid AoWeno53Prim<Dim>::reconstruct(\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_upper_face,\n    const Variables<prims_tags>& volume_prims,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh) const {\n  reconstruct_prims_work(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_vars, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n                     ghost_cell_vars, subcell_extents, number_of_variables,\n                     gamma_hi_, gamma_lo_, epsilon_);\n      },\n      volume_prims, eos, element, ghost_data, subcell_mesh, ghost_zone_size());\n}\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid AoWeno53Prim<Dim>::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const Variables<prims_tags>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh,\n    const Direction<Dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<Dim>& subcell_extents,\n             const Index<Dim>& ghost_data_extents,\n             const Direction<Dim>& local_direction_to_reconstruct) {\n        reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, gamma_hi_, gamma_lo_, epsilon_);\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<Dim>& subcell_extents,\n             const Index<Dim>& ghost_data_extents,\n             const Direction<Dim>& local_direction_to_reconstruct) {\n        reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, gamma_hi_, gamma_lo_, epsilon_);\n      },\n      subcell_volume_prims, eos, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size());\n}\n\ntemplate <size_t Dim>\nbool operator==(const AoWeno53Prim<Dim>& lhs, const AoWeno53Prim<Dim>& rhs) {\n  // Don't check function pointers since they are set from\n  // nonlinear_weight_exponent_\n  return lhs.gamma_hi_ == rhs.gamma_hi_ and lhs.gamma_lo_ == rhs.gamma_lo_ and\n         lhs.epsilon_ == rhs.epsilon_ and\n         lhs.nonlinear_weight_exponent_ == rhs.nonlinear_weight_exponent_;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAGS_LIST(data)                                                     \\\n  tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<DIM(data)>,       \\\n             Tags::EnergyDensity, hydro::Tags::RestMassDensity<DataVector>, \\\n             hydro::Tags::SpatialVelocity<DataVector, DIM(data)>,           \\\n             hydro::Tags::SpecificInternalEnergy<DataVector>,               \\\n             hydro::Tags::Pressure<DataVector>,                             \\\n             ::Tags::Flux<Tags::MassDensityCons, tmpl::size_t<DIM(data)>,   \\\n                          Frame::Inertial>,                                 \\\n             ::Tags::Flux<Tags::MomentumDensity<DIM(data)>,                 \\\n                          tmpl::size_t<DIM(data)>, Frame::Inertial>,        \\\n             ::Tags::Flux<Tags::EnergyDensity, tmpl::size_t<DIM(data)>,     \\\n                          Frame::Inertial>>\n\n#define INSTANTIATION(r, data)                                 \\\n  template class AoWeno53Prim<DIM(data)>;                      \\\n  template bool operator==(const AoWeno53Prim<DIM(data)>& lhs, \\\n                           const AoWeno53Prim<DIM(data)>& rhs);\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n\n#define INSTANTIATION(r, data)                                              \\\n  template void AoWeno53Prim<DIM(data)>::reconstruct(                       \\\n      gsl::not_null<std::array<Variables<TAGS_LIST(data)>, DIM(data)>*>     \\\n          vars_on_lower_face,                                               \\\n      gsl::not_null<std::array<Variables<TAGS_LIST(data)>, DIM(data)>*>     \\\n          vars_on_upper_face,                                               \\\n      const Variables<prims_tags>& volume_prims,                            \\\n      const EquationsOfState::EquationOfState<false, 2>& eos,               \\\n      const Element<DIM(data)>& element,                                    \\\n      const DirectionalIdMap<DIM(data), evolution::dg::subcell::GhostData>& \\\n          ghost_data,                                                       \\\n      const Mesh<DIM(data)>& subcell_mesh) const;                           \\\n  template void AoWeno53Prim<DIM(data)>::reconstruct_fd_neighbor(           \\\n      gsl::not_null<Variables<TAGS_LIST(data)>*> vars_on_face,              \\\n      const Variables<prims_tags>& subcell_volume_prims,                    \\\n      const EquationsOfState::EquationOfState<false, 2>& eos,               \\\n      const Element<DIM(data)>& element,                                    \\\n      const DirectionalIdMap<DIM(data), evolution::dg::subcell::GhostData>& \\\n          ghost_data,                                                       \\\n      const Mesh<DIM(data)>& subcell_mesh,                                  \\\n      const Direction<DIM(data)> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef TAGS_LIST\n#undef DIM\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/AoWeno.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace NewtonianEuler::fd {\n/*!\n * \\brief Adaptive-order WENO reconstruction hybridizing orders 5 and 3. See\n * ::fd::reconstruction::aoweno_53() for details.\n */\ntemplate <size_t Dim>\nclass AoWeno53Prim : public Reconstructor<Dim> {\n private:\n  // Conservative vars tags\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n  // Primitive vars tags\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  using prims_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using cons_tags = tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>;\n  using flux_tags = db::wrap_tags_in<::Tags::Flux, cons_tags, tmpl::size_t<Dim>,\n                                     Frame::Inertial>;\n  using prim_tags_for_reconstruction =\n      tmpl::list<MassDensity, Velocity, Pressure>;\n\n public:\n  struct GammaHi {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The linear weight for the 5th-order stencil.\"};\n  };\n  struct GammaLo {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The linear weight for the central 3rd-order stencil.\"};\n  };\n  struct Epsilon {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The parameter added to the oscillation indicators to avoid division \"\n        \"by zero\"};\n  };\n  struct NonlinearWeightExponent {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The exponent q to which the oscillation indicators are raised\"};\n  };\n\n  using options =\n      tmpl::list<GammaHi, GammaLo, Epsilon, NonlinearWeightExponent>;\n  static constexpr Options::String help{\n      \"Adaptive-order WENO reconstruction hybridizing orders 5 and 3 using \"\n      \"primitive variables.\"};\n\n  AoWeno53Prim() = default;\n  AoWeno53Prim(AoWeno53Prim&&) = default;\n  AoWeno53Prim& operator=(AoWeno53Prim&&) = default;\n  AoWeno53Prim(const AoWeno53Prim&) = default;\n  AoWeno53Prim& operator=(const AoWeno53Prim&) = default;\n  ~AoWeno53Prim() override = default;\n\n  AoWeno53Prim(double gamma_hi, double gamma_lo, double epsilon,\n               size_t nonlinear_weight_exponent);\n\n  explicit AoWeno53Prim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor<Dim>, AoWeno53Prim);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor<Dim>> override;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 3; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<prims_tags>,\n                 hydro::Tags::EquationOfState<false, 2>,\n                 domain::Tags::Element<Dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>>;\n\n  template <typename TagsList>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_lower_face,\n      gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_upper_face,\n      const Variables<prims_tags>& volume_prims,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const Element<Dim>& element,\n      const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<Dim>& subcell_mesh) const;\n\n  /// Called by an element doing DG when the neighbor is doing subcell.\n  template <typename TagsList>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<TagsList>*> vars_on_face,\n      const Variables<prims_tags>& subcell_volume_prims,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const Element<Dim>& element,\n      const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<Dim>& subcell_mesh,\n      const Direction<Dim> direction_to_reconstruct) const;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const AoWeno53Prim<LocalDim>& lhs,\n                         const AoWeno53Prim<LocalDim>& rhs);\n\n  double gamma_hi_ = std::numeric_limits<double>::signaling_NaN();\n  double gamma_lo_ = std::numeric_limits<double>::signaling_NaN();\n  double epsilon_ = std::numeric_limits<double>::signaling_NaN();\n  size_t nonlinear_weight_exponent_ = 0;\n\n  void (*reconstruct_)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                       gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                       const gsl::span<const double>&,\n                       const DirectionMap<Dim, gsl::span<const double>>&,\n                       const Index<Dim>&, size_t, double, double, double);\n  void (*reconstruct_lower_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<Dim>&, const Index<Dim>&,\n                                      const Direction<Dim>&, const double&,\n                                      const double&, const double&);\n  void (*reconstruct_upper_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<Dim>&, const Index<Dim>&,\n                                      const Direction<Dim>&, const double&,\n                                      const double&, const double&);\n};\n\ntemplate <size_t Dim>\nbool operator!=(const AoWeno53Prim<Dim>& lhs, const AoWeno53Prim<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AoWeno.cpp\n  MonotonisedCentral.cpp\n  Reconstructor.cpp\n  RegisterDerivedWithCharm.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AoWeno.hpp\n  Factory.hpp\n  FiniteDifference.hpp\n  MonotonisedCentral.hpp\n  ReconstructWork.hpp\n  ReconstructWork.tpp\n  Reconstructor.hpp\n  RegisterDerivedWithCharm.hpp\n  Tag.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/AoWeno.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/FiniteDifference.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace NewtonianEuler {\n/// Finite difference functionality for Newtonian Euler\nnamespace fd {}\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/MonotonisedCentral.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/ReconstructWork.tpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::fd {\ntemplate <size_t Dim>\nMonotonisedCentralPrim<Dim>::MonotonisedCentralPrim(CkMigrateMessage* const msg)\n    : Reconstructor<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<Reconstructor<Dim>> MonotonisedCentralPrim<Dim>::get_clone()\n    const {\n  return std::make_unique<MonotonisedCentralPrim>(*this);\n}\n\ntemplate <size_t Dim>\nvoid MonotonisedCentralPrim<Dim>::pup(PUP::er& p) {\n  Reconstructor<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID MonotonisedCentralPrim<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid MonotonisedCentralPrim<Dim>::reconstruct(\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_upper_face,\n    const Variables<prims_tags>& volume_prims,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh) const {\n  reconstruct_prims_work(\n      vars_on_lower_face, vars_on_upper_face,\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_vars, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::monotonised_central(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_vars,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      volume_prims, eos, element, ghost_data, subcell_mesh, ghost_zone_size());\n}\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid MonotonisedCentralPrim<Dim>::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const Variables<prims_tags>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh,\n    const Direction<Dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work(\n      vars_on_face,\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<Dim>& subcell_extents,\n         const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor,\n         const Index<Dim>& subcell_extents,\n         const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      subcell_volume_prims, eos, element, ghost_data, subcell_mesh,\n      direction_to_reconstruct, ghost_zone_size());\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAGS_LIST(data)                                                     \\\n  tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<DIM(data)>,       \\\n             Tags::EnergyDensity, hydro::Tags::RestMassDensity<DataVector>, \\\n             hydro::Tags::SpatialVelocity<DataVector, DIM(data)>,           \\\n             hydro::Tags::SpecificInternalEnergy<DataVector>,               \\\n             hydro::Tags::Pressure<DataVector>,                             \\\n             ::Tags::Flux<Tags::MassDensityCons, tmpl::size_t<DIM(data)>,   \\\n                          Frame::Inertial>,                                 \\\n             ::Tags::Flux<Tags::MomentumDensity<DIM(data)>,                 \\\n                          tmpl::size_t<DIM(data)>, Frame::Inertial>,        \\\n             ::Tags::Flux<Tags::EnergyDensity, tmpl::size_t<DIM(data)>,     \\\n                          Frame::Inertial>>\n\n#define INSTANTIATION(r, data) template class MonotonisedCentralPrim<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n\n#define INSTANTIATION(r, data)                                              \\\n  template void MonotonisedCentralPrim<DIM(data)>::reconstruct(             \\\n      gsl::not_null<std::array<Variables<TAGS_LIST(data)>, DIM(data)>*>     \\\n          vars_on_lower_face,                                               \\\n      gsl::not_null<std::array<Variables<TAGS_LIST(data)>, DIM(data)>*>     \\\n          vars_on_upper_face,                                               \\\n      const Variables<prims_tags>& volume_prims,                            \\\n      const EquationsOfState::EquationOfState<false, 2>& eos,               \\\n      const Element<DIM(data)>& element,                                    \\\n      const DirectionalIdMap<DIM(data), evolution::dg::subcell::GhostData>& \\\n          ghost_data,                                                       \\\n      const Mesh<DIM(data)>& subcell_mesh) const;                           \\\n  template void MonotonisedCentralPrim<DIM(data)>::reconstruct_fd_neighbor( \\\n      gsl::not_null<Variables<TAGS_LIST(data)>*> vars_on_face,              \\\n      const Variables<prims_tags>& subcell_volume_prims,                    \\\n      const EquationsOfState::EquationOfState<false, 2>& eos,               \\\n      const Element<DIM(data)>& element,                                    \\\n      const DirectionalIdMap<DIM(data), evolution::dg::subcell::GhostData>& \\\n          ghost_data,                                                       \\\n      const Mesh<DIM(data)>& subcell_mesh,                                  \\\n      const Direction<DIM(data)> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef TAGS_LIST\n#undef DIM\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/MonotonisedCentral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace NewtonianEuler::fd {\n/*!\n * \\brief Monotonised central reconstruction. See\n * `::fd::reconstruction::monotonised_central()` for details.\n */\ntemplate <size_t Dim>\nclass MonotonisedCentralPrim : public Reconstructor<Dim> {\n private:\n  // Conservative vars tags\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n  // Primitive vars tags\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  using prims_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using cons_tags = tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>;\n  using flux_tags = db::wrap_tags_in<::Tags::Flux, cons_tags, tmpl::size_t<Dim>,\n                                     Frame::Inertial>;\n  using prim_tags_for_reconstruction =\n      tmpl::list<MassDensity, Velocity, Pressure>;\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Monotonised central reconstruction scheme using primitive variables.\"};\n\n  MonotonisedCentralPrim() = default;\n  MonotonisedCentralPrim(MonotonisedCentralPrim&&) = default;\n  MonotonisedCentralPrim& operator=(MonotonisedCentralPrim&&) = default;\n  MonotonisedCentralPrim(const MonotonisedCentralPrim&) = default;\n  MonotonisedCentralPrim& operator=(const MonotonisedCentralPrim&) = default;\n  ~MonotonisedCentralPrim() override = default;\n\n  explicit MonotonisedCentralPrim(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor<Dim>,\n                                     MonotonisedCentralPrim);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor<Dim>> override;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 2; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<prims_tags>,\n                 hydro::Tags::EquationOfState<false, 2>,\n                 domain::Tags::Element<Dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>>;\n\n  template <typename TagsList>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_lower_face,\n      gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_upper_face,\n      const Variables<prims_tags>& volume_prims,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const Element<Dim>& element,\n      const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<Dim>& subcell_mesh) const;\n\n  /// Called by an element doing DG when the neighbor is doing subcell.\n  ///\n  /// This is used to reconstruct the fluxes on the mortar that the subcell\n  /// neighbor would have sent had we instead used a two a two-communication\n  /// subcell solver (first communication for reconstruction, second for\n  /// fluxes).\n  template <typename TagsList>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<TagsList>*> vars_on_face,\n      const Variables<prims_tags>& subcell_volume_prims,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const Element<Dim>& element,\n      const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<Dim>& subcell_mesh,\n      const Direction<Dim> direction_to_reconstruct) const;\n};\n\ntemplate <size_t Dim>\nbool operator==(const MonotonisedCentralPrim<Dim>& /*lhs*/,\n                const MonotonisedCentralPrim<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const MonotonisedCentralPrim<Dim>& lhs,\n                const MonotonisedCentralPrim<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/ReconstructWork.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace NewtonianEuler::fd {\n/*!\n * \\brief Reconstructs the mass density, velocity, and pressure, then computes\n * the specific internal energy and conserved variables. All results are written\n * into `vars_on_lower_face` and `vars_on_upper_face`.\n */\ntemplate <typename PrimsTags, typename TagsList, size_t Dim, typename F>\nvoid reconstruct_prims_work(\n    gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_lower_face,\n    gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_upper_face,\n    const F& reconstruct, const Variables<PrimsTags>& volume_prims,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh, size_t ghost_zone_size);\n\n/*!\n * \\brief Reconstructs the mass density, velocity, and pressure, then computes\n * the specific internal energy and conserved variables. All results are written\n * into `vars_on_face`.\n *\n * This is used on DG elements to reconstruct their subcell neighbors' solution\n * on the shared faces.\n */\ntemplate <typename TagsList, typename PrimsTags, size_t Dim, typename F0,\n          typename F1>\nvoid reconstruct_fd_neighbor_work(\n    gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const F0& reconstruct_lower_neighbor, const F1& reconstruct_upper_neighbor,\n    const Variables<PrimsTags>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh,\n    const Direction<Dim>& direction_to_reconstruct, size_t ghost_zone_size);\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/ReconstructWork.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler::fd {\ntemplate <typename PrimsTags, typename TagsList, size_t Dim, typename F>\nvoid reconstruct_prims_work(\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_upper_face,\n    const F& reconstruct, const Variables<PrimsTags>& volume_prims,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size) {\n  // Conservative vars tags\n  using MassDensityCons = Tags::MassDensityCons;\n  using EnergyDensity = Tags::EnergyDensity;\n  using MomentumDensity = Tags::MomentumDensity<Dim>;\n\n  // Primitive vars tags\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  using prim_tags_for_reconstruction =\n      tmpl::list<MassDensity, Velocity, Pressure>;\n\n  ASSERT(Mesh<Dim>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                   subcell_mesh.quadrature(0)) == subcell_mesh,\n         \"The subcell mesh should be isotropic but got \" << subcell_mesh);\n  const size_t volume_num_pts = subcell_mesh.number_of_grid_points();\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n  const size_t neighbor_num_pts =\n      ghost_zone_size * subcell_mesh.extents().slice_away(0).product();\n  size_t vars_in_neighbor_count = 0;\n  tmpl::for_each<prim_tags_for_reconstruction>([&element, &ghost_data,\n                                                neighbor_num_pts, &reconstruct,\n                                                reconstructed_num_pts,\n                                                volume_num_pts, &volume_prims,\n                                                &vars_in_neighbor_count,\n                                                &vars_on_lower_face,\n                                                &vars_on_upper_face,\n                                                &subcell_mesh](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    auto& volume_tensor = get<tag>(volume_prims);\n\n    const size_t number_of_components = volume_tensor.size();\n    const gsl::span<const double> volume_vars = gsl::make_span(\n        volume_tensor[0].data(), number_of_components * volume_num_pts);\n    std::array<gsl::span<double>, Dim> upper_face_vars{};\n    std::array<gsl::span<double>, Dim> lower_face_vars{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(upper_face_vars, i) =\n          gsl::make_span(get<tag>(gsl::at(*vars_on_upper_face, i))[0].data(),\n                         number_of_components * reconstructed_num_pts);\n      gsl::at(lower_face_vars, i) =\n          gsl::make_span(get<tag>(gsl::at(*vars_on_lower_face, i))[0].data(),\n                         number_of_components * reconstructed_num_pts);\n    }\n\n    DirectionMap<Dim, gsl::span<const double>> ghost_cell_vars{};\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      const auto& neighbors_in_direction = element.neighbors().at(direction);\n      ASSERT(neighbors_in_direction.size() == 1,\n             \"Currently only support one neighbor in each direction, but \"\n             \"got \"\n                 << neighbors_in_direction.size() << \" in direction \"\n                 << direction);\n\n      const DataVector& neighbor_data =\n          ghost_data\n              .at(DirectionalId<Dim>{direction,\n                                     *neighbors_in_direction.begin()})\n              .neighbor_ghost_data_for_reconstruction();\n\n      ASSERT(neighbor_data.size() != 0,\n             \"The neighber data is empty in direction \"\n                 << direction << \" on element id \" << element.id());\n      ghost_cell_vars[direction] = gsl::make_span(\n          &neighbor_data[vars_in_neighbor_count * neighbor_num_pts],\n          number_of_components * neighbor_num_pts);\n    }\n\n    reconstruct(make_not_null(&upper_face_vars),\n                make_not_null(&lower_face_vars), volume_vars, ghost_cell_vars,\n                subcell_mesh.extents(), number_of_components);\n\n    vars_in_neighbor_count += number_of_components;\n  });\n\n  for (size_t i = 0; i < Dim; ++i) {\n    auto& vars_upper_face = gsl::at(*vars_on_upper_face, i);\n    auto& vars_lower_face = gsl::at(*vars_on_lower_face, i);\n\n    get<SpecificInternalEnergy>(vars_upper_face) =\n        eos.specific_internal_energy_from_density_and_pressure(\n            get<MassDensity>(vars_upper_face), get<Pressure>(vars_upper_face));\n    get<SpecificInternalEnergy>(vars_lower_face) =\n        eos.specific_internal_energy_from_density_and_pressure(\n            get<MassDensity>(vars_lower_face), get<Pressure>(vars_lower_face));\n\n    // Compute conserved variables on faces\n    NewtonianEuler::ConservativeFromPrimitive<Dim>::apply(\n        make_not_null(&get<MassDensityCons>(vars_upper_face)),\n        make_not_null(&get<MomentumDensity>(vars_upper_face)),\n        make_not_null(&get<EnergyDensity>(vars_upper_face)),\n        get<MassDensity>(vars_upper_face), get<Velocity>(vars_upper_face),\n        get<SpecificInternalEnergy>(vars_upper_face));\n    NewtonianEuler::ConservativeFromPrimitive<Dim>::apply(\n        make_not_null(&get<MassDensityCons>(vars_lower_face)),\n        make_not_null(&get<MomentumDensity>(vars_lower_face)),\n        make_not_null(&get<EnergyDensity>(vars_lower_face)),\n        get<MassDensity>(vars_lower_face), get<Velocity>(vars_lower_face),\n        get<SpecificInternalEnergy>(vars_lower_face));\n  }\n}\n\ntemplate <typename TagsList, typename PrimsTags, size_t Dim, typename F0,\n          typename F1>\nvoid reconstruct_fd_neighbor_work(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const F0& reconstruct_lower_neighbor, const F1& reconstruct_upper_neighbor,\n    const Variables<PrimsTags>& subcell_volume_prims,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh,\n    const Direction<Dim>& direction_to_reconstruct,\n    const size_t ghost_zone_size) {\n  // Conservative vars tags\n  using MassDensityCons = Tags::MassDensityCons;\n  using EnergyDensity = Tags::EnergyDensity;\n  using MomentumDensity = Tags::MomentumDensity<Dim>;\n\n  // Primitive vars tags\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  using prim_tags_for_reconstruction =\n      tmpl::list<MassDensity, Velocity, Pressure>;\n\n  const DirectionalId<Dim> mortar_id{\n      direction_to_reconstruct,\n      *element.neighbors().at(direction_to_reconstruct).begin()};\n  Index<Dim> ghost_data_extents = subcell_mesh.extents();\n  ghost_data_extents[direction_to_reconstruct.dimension()] = ghost_zone_size;\n  Variables<prim_tags_for_reconstruction> neighbor_prims{\n      ghost_data_extents.product()};\n  {\n    ASSERT(ghost_data.contains(mortar_id),\n           \"The neighbor data does not contain the mortar: \" << mortar_id);\n    const DataVector& neighbor_data_in_direction =\n        ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    std::copy(neighbor_data_in_direction.begin(),\n              std::next(neighbor_data_in_direction.begin(),\n                        static_cast<std::ptrdiff_t>(\n                            neighbor_prims.number_of_independent_components *\n                            ghost_data_extents.product())),\n              neighbor_prims.data());\n  }\n\n  tmpl::for_each<prim_tags_for_reconstruction>(\n      [&direction_to_reconstruct, &ghost_data_extents, &neighbor_prims,\n       &reconstruct_lower_neighbor, &reconstruct_upper_neighbor, &subcell_mesh,\n       &subcell_volume_prims, &vars_on_face](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const auto& tensor_volume = get<tag>(subcell_volume_prims);\n        const auto& tensor_neighbor = get<tag>(neighbor_prims);\n        auto& tensor_on_face = get<tag>(*vars_on_face);\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_upper_neighbor(\n                make_not_null(&tensor_on_face[tensor_index]),\n                tensor_volume[tensor_index], tensor_neighbor[tensor_index],\n                subcell_mesh.extents(), ghost_data_extents,\n                direction_to_reconstruct);\n          }\n        } else {\n          for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n               ++tensor_index) {\n            reconstruct_lower_neighbor(\n                make_not_null(&tensor_on_face[tensor_index]),\n                tensor_volume[tensor_index], tensor_neighbor[tensor_index],\n                subcell_mesh.extents(), ghost_data_extents,\n                direction_to_reconstruct);\n          }\n        }\n      });\n\n  get<SpecificInternalEnergy>(*vars_on_face) =\n      eos.specific_internal_energy_from_density_and_pressure(\n          get<MassDensity>(*vars_on_face), get<Pressure>(*vars_on_face));\n  NewtonianEuler::ConservativeFromPrimitive<Dim>::apply(\n      make_not_null(&get<MassDensityCons>(*vars_on_face)),\n      make_not_null(&get<MomentumDensity>(*vars_on_face)),\n      make_not_null(&get<EnergyDensity>(*vars_on_face)),\n      get<MassDensity>(*vars_on_face), get<Velocity>(*vars_on_face),\n      get<SpecificInternalEnergy>(*vars_on_face));\n}\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n\n#include <pup.h>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace NewtonianEuler::fd {\ntemplate <size_t Dim>\nReconstructor<Dim>::Reconstructor(CkMigrateMessage* const msg)\n    : PUP::able(msg) {}\n\ntemplate <size_t Dim>\nvoid Reconstructor<Dim>::pup(PUP::er& p) {\n  PUP::able::pup(p);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class Reconstructor<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::fd {\n/// \\cond\ntemplate <size_t Dim>\nclass AoWeno53Prim;\ntemplate <size_t Dim>\nclass MonotonisedCentralPrim;\n/// \\endcond\n\n/*!\n * \\brief The base class from which all reconstruction schemes must inherit\n *\n * Currently we have hard-coded reconstructing \\f$\\rho, p, v^i\\f$. However, the\n * DG-subcell solver is coded generally enough that an efficient implementation\n * of reconstructing the conserved or characteristic variables is also possible.\n * It is not yet clear how much info about what is being reconstructed is needed\n * at compile time and so we currently append `Prim` to the end of the\n * reconstruction schemes to clarify that they are reconstructing the primitive\n * variables. Ideally the choice of what variables to reconstruct can be made by\n * a runtime argument to the individual reconstruction schemes.\n */\ntemplate <size_t Dim>\nclass Reconstructor : public PUP::able {\n public:\n  Reconstructor() = default;\n  Reconstructor(const Reconstructor&) = default;\n  Reconstructor& operator=(const Reconstructor&) = default;\n  Reconstructor(Reconstructor&&) = default;\n  Reconstructor& operator=(Reconstructor&&) = default;\n  ~Reconstructor() override = default;\n\n  /// \\cond\n  explicit Reconstructor(CkMigrateMessage* msg);\n  WRAPPED_PUPable_abstract(Reconstructor<Dim>);  // NOLINT\n  /// \\endcond\n\n  using creatable_classes =\n      tmpl::list<AoWeno53Prim<Dim>, MonotonisedCentralPrim<Dim>>;\n\n  virtual std::unique_ptr<Reconstructor<Dim>> get_clone() const = 0;\n\n  virtual size_t ghost_zone_size() const = 0;\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace NewtonianEuler::fd {\nvoid register_derived_with_charm() {\n  register_classes_with_charm(typename Reconstructor<1>::creatable_classes{});\n  register_classes_with_charm(typename Reconstructor<2>::creatable_classes{});\n  register_classes_with_charm(typename Reconstructor<3>::creatable_classes{});\n}\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace NewtonianEuler::fd {\nvoid register_derived_with_charm();\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellSolver.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace NewtonianEuler::fd {\n/// Option tags for reconstruction\nnamespace OptionTags {\n/// \\brief Option tag for the reconstructor\ntemplate <size_t Dim>\nstruct Reconstructor {\n  using type = std::unique_ptr<fd::Reconstructor<Dim>>;\n\n  static constexpr Options::String help = {\"The reconstruction scheme to use.\"};\n  using group = evolution::dg::subcell::OptionTags::SubcellSolverGroup;\n};\n}  // namespace OptionTags\n\n/// %Tags for reconstruction\nnamespace Tags {\n/// \\brief Tag for the reconstructor\ntemplate <size_t Dim>\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<fd::Reconstructor<Dim>>;\n  using option_tags = tmpl::list<OptionTags::Reconstructor<Dim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& reconstructor) {\n    return reconstructor->get_clone();\n  }\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler::fd\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Fluxes.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler {\nnamespace detail {\ntemplate <size_t Dim>\nvoid fluxes_impl(\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> mass_density_cons_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, Dim>*> momentum_density_flux,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> energy_density_flux,\n    const gsl::not_null<Scalar<DataVector>*> enthalpy_density,\n    const tnsr::I<DataVector, Dim>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& pressure) {\n  get(*enthalpy_density) = get(energy_density) + get(pressure);\n\n  for (size_t i = 0; i < Dim; ++i) {\n    mass_density_cons_flux->get(i) = momentum_density.get(i);\n    for (size_t j = 0; j < Dim; ++j) {\n      momentum_density_flux->get(i, j) =\n          momentum_density.get(i) * velocity.get(j);\n    }\n    momentum_density_flux->get(i, i) += get(pressure);\n    energy_density_flux->get(i) = get(*enthalpy_density) * velocity.get(i);\n  }\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nvoid ComputeFluxes<Dim>::apply(\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> mass_density_cons_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, Dim>*> momentum_density_flux,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> energy_density_flux,\n    const tnsr::I<DataVector, Dim>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& pressure) {\n  Scalar<DataVector> enthalpy_density{get<0>(momentum_density).size()};\n  detail::fluxes_impl(mass_density_cons_flux, momentum_density_flux,\n                      energy_density_flux, make_not_null(&enthalpy_density),\n                      momentum_density, energy_density, velocity, pressure);\n}\n\n}  // namespace NewtonianEuler\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void NewtonianEuler::detail::fluxes_impl<DIM(data)>(              \\\n      gsl::not_null<tnsr::I<DataVector, DIM(data)>*> mass_density_cons_flux, \\\n      gsl::not_null<tnsr::IJ<DataVector, DIM(data)>*> momentum_density_flux, \\\n      gsl::not_null<tnsr::I<DataVector, DIM(data)>*> energy_density_flux,    \\\n      gsl::not_null<Scalar<DataVector>*> enthalpy_density,                   \\\n      const tnsr::I<DataVector, DIM(data)>& momentum_density,                \\\n      const Scalar<DataVector>& energy_density,                              \\\n      const tnsr::I<DataVector, DIM(data)>& velocity,                        \\\n      const Scalar<DataVector>& pressure);                                   \\\n  template class NewtonianEuler::ComputeFluxes<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Fluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace NewtonianEuler {\n\n/*!\n * \\brief Compute the fluxes of the conservative variables of the\n * Newtonian Euler system\n *\n * The fluxes are \\f$(\\text{Dim} + 2)\\f$ vectors of\n * dimension \\f$\\text{Dim}\\f$. Denoting the flux of the conservative\n * variable \\f$u\\f$ as \\f$F(u)\\f$, one has\n *\n * \\f{align*}\n * F^i(\\rho) &= S^i\\\\\n * F^i(S^j) &= S^i v^j + \\delta^{ij}p\\\\\n * F^i(e) &= (e + p)v^i\n * \\f}\n *\n * where \\f$S^i\\f$ is the momentum density, \\f$e\\f$ is the energy density,\n * \\f$v^i\\f$ is the velocity, \\f$p\\f$ is the pressure, and \\f$\\delta^{ij}\\f$\n * is the Kronecker delta. This form of the fluxes combines conservative and\n * primitive variables (while the velocity appears explicitly, the pressure\n * implicitly depends, for instance, on the mass density and the specific\n * internal energy), so the conversion from one variable set to the other\n * must be known prior to calling this function.\n */\ntemplate <size_t Dim>\nstruct ComputeFluxes {\n  using return_tags = tmpl::list<\n      ::Tags::Flux<Tags::MassDensityCons, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::Flux<Tags::MomentumDensity<Dim>, tmpl::size_t<Dim>,\n                   Frame::Inertial>,\n      ::Tags::Flux<Tags::EnergyDensity, tmpl::size_t<Dim>, Frame::Inertial>>;\n\n  using argument_tags =\n      tmpl::list<Tags::MomentumDensity<Dim>, Tags::EnergyDensity,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::Pressure<DataVector>>;\n\n  static void apply(\n      gsl::not_null<tnsr::I<DataVector, Dim>*> mass_density_cons_flux,\n      gsl::not_null<tnsr::IJ<DataVector, Dim>*> momentum_density_flux,\n      gsl::not_null<tnsr::I<DataVector, Dim>*> energy_density_flux,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const tnsr::I<DataVector, Dim>& velocity,\n      const Scalar<DataVector>& pressure);\n};\n\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class Events::ObserveTimeStep<NewtonianEuler::System<DIM(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template class ChangeTimeStepperOrder<NewtonianEuler::System<DIM(data)>>; \\\n  template class CleanHistory<NewtonianEuler::System<DIM(data)>>;           \\\n  template class RecordTimeStepperData<NewtonianEuler::System<DIM(data)>>;  \\\n  template class UpdateU<NewtonianEuler::System<DIM(data)>, false>;         \\\n  template class UpdateU<NewtonianEuler::System<DIM(data)>, true>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/InternalEnergyDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/InternalEnergyDensity.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler {\ntemplate <typename DataType>\nvoid internal_energy_density(const gsl::not_null<Scalar<DataType>*> result,\n                             const Scalar<DataType>& mass_density,\n                             const Scalar<DataType>& specific_internal_energy) {\n  get(*result) = get(mass_density) * get(specific_internal_energy);\n}\n\ntemplate <typename DataType>\nScalar<DataType> internal_energy_density(\n    const Scalar<DataType>& mass_density,\n    const Scalar<DataType>& specific_internal_energy) {\n  Scalar<DataType> result{};\n  internal_energy_density(make_not_null(&result), mass_density,\n                          specific_internal_energy);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                \\\n  template void internal_energy_density(                    \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result,     \\\n      const Scalar<DTYPE(data)>& mass_density,              \\\n      const Scalar<DTYPE(data)>& specific_internal_energy); \\\n  template Scalar<DTYPE(data)> internal_energy_density(     \\\n      const Scalar<DTYPE(data)>& mass_density,              \\\n      const Scalar<DTYPE(data)>& specific_internal_energy);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/InternalEnergyDensity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler {\n/// @{\n/*!\n * Compute the internal energy density, \\f$\\rho \\epsilon\\f$,\n * where \\f$\\rho\\f$ is the mass density, and \\f$\\epsilon\\f$ is the\n * specific internal energy.\n */\ntemplate <typename DataType>\nvoid internal_energy_density(gsl::not_null<Scalar<DataType>*> result,\n                             const Scalar<DataType>& mass_density,\n                             const Scalar<DataType>& specific_internal_energy);\n\ntemplate <typename DataType>\nScalar<DataType> internal_energy_density(\n    const Scalar<DataType>& mass_density,\n    const Scalar<DataType>& specific_internal_energy);\n/// @}\n\nnamespace Tags {\n/// Compute item for the internal energy density, \\f$\\rho \\epsilon\\f$.\n/// \\see NewtonianEuler::internal_energy_density\n///\n/// Can be retrieved using `NewtonianEuler::Tags::InternalEnergyDensity`\ntemplate <typename DataType>\nstruct InternalEnergyDensityCompute : InternalEnergyDensity<DataType>,\n                                      db::ComputeTag {\n  using base = InternalEnergyDensity<DataType>;\n\n  using argument_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function =\n      static_cast<void (*)(const gsl::not_null<Scalar<DataType>*>,\n                           const Scalar<DataType>&, const Scalar<DataType>&)>(\n          &internal_energy_density<DataType>);\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/KineticEnergyDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/KineticEnergyDensity.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler {\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid kinetic_energy_density(const gsl::not_null<Scalar<DataType>*> result,\n                            const Scalar<DataType>& mass_density,\n                            const tnsr::I<DataType, Dim, Fr>& velocity) {\n  get(*result) = 0.5 * get(mass_density) * get(dot_product(velocity, velocity));\n}\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> kinetic_energy_density(\n    const Scalar<DataType>& mass_density,\n    const tnsr::I<DataType, Dim, Fr>& velocity) {\n  Scalar<DataType> result{};\n  kinetic_energy_density(make_not_null(&result), mass_density, velocity);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                            \\\n  template void kinetic_energy_density(                 \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result, \\\n      const Scalar<DTYPE(data)>& mass_density,          \\\n      const tnsr::I<DTYPE(data), DIM(data)>& velocity); \\\n  template Scalar<DTYPE(data)> kinetic_energy_density(  \\\n      const Scalar<DTYPE(data)>& mass_density,          \\\n      const tnsr::I<DTYPE(data), DIM(data)>& velocity);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector), (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n#undef DTYPE\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/KineticEnergyDensity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler {\n/// @{\n/*!\n * Compute the kinetic energy density, \\f$\\rho v^2/2\\f$,\n * where \\f$\\rho\\f$ is the mass density, and \\f$v\\f$ is the\n * magnitude of the velocity.\n */\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid kinetic_energy_density(gsl::not_null<Scalar<DataType>*> result,\n                            const Scalar<DataType>& mass_density,\n                            const tnsr::I<DataType, Dim, Fr>& velocity);\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> kinetic_energy_density(\n    const Scalar<DataType>& mass_density,\n    const tnsr::I<DataType, Dim, Fr>& velocity);\n/// @}\n\nnamespace Tags {\n/// Compute item for the kinetic energy density, \\f$\\rho v^2/2\\f$.\n/// \\see NewtonianEuler::kinetic_energy_density\n///\n/// Can be retrieved using `NewtonianEuler::Tags::KineticEnergyDensity`\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct KineticEnergyDensityCompute : KineticEnergyDensity<DataType>,\n                                     db::ComputeTag {\n  using base = KineticEnergyDensity<DataType>;\n\n  using argument_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim, Fr>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<Scalar<DataType>*>, const Scalar<DataType>&,\n      const tnsr::I<DataType, Dim, Fr>&)>(\n      &kinetic_energy_density<DataType, Dim, Fr>);\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY NewtonianEulerLimiters)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CharacteristicHelpers.cpp\n  Flattener.cpp\n  Minmod.cpp\n  VariablesToLimit.cpp\n  Weno.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CharacteristicHelpers.hpp\n  Flattener.hpp\n  KxrcfTci.hpp\n  Minmod.hpp\n  VariablesToLimit.hpp\n  Weno.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  Domain\n  ErrorHandling\n  Limiters\n  NewtonianEuler\n  Utilities\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/CharacteristicHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Limiters/CharacteristicHelpers.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Characteristics.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::Limiters {\n\ntemplate <size_t VolumeDim>\nstd::pair<Matrix, Matrix> right_and_left_eigenvectors(\n    const Scalar<double>& mean_density,\n    const tnsr::I<double, VolumeDim>& mean_momentum,\n    const Scalar<double>& mean_energy,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n    const tnsr::i<double, VolumeDim>& unit_vector,\n    const bool compute_char_transformation_numerically) {\n  // Compute fluid primitives from mean conserved state\n  const auto velocity = [&mean_density, &mean_momentum]() {\n    auto result = mean_momentum;\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      result.get(i) /= get(mean_density);\n    }\n    return result;\n  }();\n  const auto specific_internal_energy = [&mean_density, &mean_energy,\n                                         &mean_momentum]() {\n    auto result = mean_energy;\n    get(result) /= get(mean_density);\n    get(result) -= 0.5 * get(dot_product(mean_momentum, mean_momentum)) /\n                   square(get(mean_density));\n    return result;\n  }();\n\n  Scalar<double> pressure{};\n  Scalar<double> kappa_over_density{};\n  pressure = equation_of_state.pressure_from_density_and_energy(\n      mean_density, specific_internal_energy);\n  get(kappa_over_density) =\n      get(equation_of_state\n              .kappa_times_p_over_rho_squared_from_density_and_energy(\n                  mean_density, specific_internal_energy)) *\n      get(mean_density) / get(pressure);\n\n  const Scalar<double> specific_enthalpy{\n      {{(get(mean_energy) + get(pressure)) / get(mean_density)}}};\n  const Scalar<double> sound_speed_squared =\n      NewtonianEuler::sound_speed_squared(\n          mean_density, specific_internal_energy, equation_of_state);\n\n  if (compute_char_transformation_numerically) {\n    return numerical_eigensystem(velocity, sound_speed_squared,\n                                 specific_enthalpy, kappa_over_density,\n                                 unit_vector)\n        .second;\n  } else {\n    return std::make_pair(right_eigenvectors<VolumeDim>(\n                              velocity, sound_speed_squared, specific_enthalpy,\n                              kappa_over_density, unit_vector),\n                          left_eigenvectors<VolumeDim>(\n                              velocity, sound_speed_squared, specific_enthalpy,\n                              kappa_over_density, unit_vector));\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid characteristic_fields(\n    const gsl::not_null<tuples::TaggedTuple<\n        ::Tags::Mean<NewtonianEuler::Tags::VMinus>,\n        ::Tags::Mean<NewtonianEuler::Tags::VMomentum<VolumeDim>>,\n        ::Tags::Mean<NewtonianEuler::Tags::VPlus>>*>\n        char_means,\n    const tuples::TaggedTuple<\n        ::Tags::Mean<NewtonianEuler::Tags::MassDensityCons>,\n        ::Tags::Mean<NewtonianEuler::Tags::MomentumDensity<VolumeDim>>,\n        ::Tags::Mean<NewtonianEuler::Tags::EnergyDensity>>& cons_means,\n    const Matrix& left) {\n  auto& char_v_minus =\n      get<::Tags::Mean<NewtonianEuler::Tags::VMinus>>(*char_means);\n  auto& char_v_momentum =\n      get<::Tags::Mean<NewtonianEuler::Tags::VMomentum<VolumeDim>>>(\n          *char_means);\n  auto& char_v_plus =\n      get<::Tags::Mean<NewtonianEuler::Tags::VPlus>>(*char_means);\n\n  const auto& cons_mass_density =\n      get<::Tags::Mean<NewtonianEuler::Tags::MassDensityCons>>(cons_means);\n  const auto& cons_momentum_density =\n      get<::Tags::Mean<NewtonianEuler::Tags::MomentumDensity<VolumeDim>>>(\n          cons_means);\n  const auto& cons_energy_density =\n      get<::Tags::Mean<NewtonianEuler::Tags::EnergyDensity>>(cons_means);\n\n  get(char_v_minus) = left(0, 0) * get(cons_mass_density);\n  for (size_t j = 0; j < VolumeDim; ++j) {\n    char_v_momentum.get(j) = left(j + 1, 0) * get(cons_mass_density);\n  }\n  get(char_v_plus) = left(VolumeDim + 1, 0) * get(cons_mass_density);\n\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    get(char_v_minus) += left(0, i + 1) * cons_momentum_density.get(i);\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      char_v_momentum.get(j) +=\n          left(j + 1, i + 1) * cons_momentum_density.get(i);\n    }\n    get(char_v_plus) +=\n        left(VolumeDim + 1, i + 1) * cons_momentum_density.get(i);\n  }\n\n  get(char_v_minus) += left(0, VolumeDim + 1) * get(cons_energy_density);\n  for (size_t j = 0; j < VolumeDim; ++j) {\n    char_v_momentum.get(j) +=\n        left(j + 1, VolumeDim + 1) * get(cons_energy_density);\n  }\n  get(char_v_plus) +=\n      left(VolumeDim + 1, VolumeDim + 1) * get(cons_energy_density);\n}\n\ntemplate <size_t VolumeDim>\nvoid characteristic_fields(\n    const gsl::not_null<Scalar<DataVector>*> char_v_minus,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> char_v_momentum,\n    const gsl::not_null<Scalar<DataVector>*> char_v_plus,\n    const Scalar<DataVector>& cons_mass_density,\n    const tnsr::I<DataVector, VolumeDim>& cons_momentum_density,\n    const Scalar<DataVector>& cons_energy_density, const Matrix& left) {\n  get(*char_v_minus) = left(0, 0) * get(cons_mass_density);\n  for (size_t j = 0; j < VolumeDim; ++j) {\n    char_v_momentum->get(j) = left(j + 1, 0) * get(cons_mass_density);\n  }\n  get(*char_v_plus) = left(VolumeDim + 1, 0) * get(cons_mass_density);\n\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    get(*char_v_minus) += left(0, i + 1) * cons_momentum_density.get(i);\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      char_v_momentum->get(j) +=\n          left(j + 1, i + 1) * cons_momentum_density.get(i);\n    }\n    get(*char_v_plus) +=\n        left(VolumeDim + 1, i + 1) * cons_momentum_density.get(i);\n  }\n\n  get(*char_v_minus) += left(0, VolumeDim + 1) * get(cons_energy_density);\n  for (size_t j = 0; j < VolumeDim; ++j) {\n    char_v_momentum->get(j) +=\n        left(j + 1, VolumeDim + 1) * get(cons_energy_density);\n  }\n  get(*char_v_plus) +=\n      left(VolumeDim + 1, VolumeDim + 1) * get(cons_energy_density);\n}\n\ntemplate <size_t VolumeDim>\nvoid characteristic_fields(\n    const gsl::not_null<\n        Variables<tmpl::list<NewtonianEuler::Tags::VMinus,\n                             NewtonianEuler::Tags::VMomentum<VolumeDim>,\n                             NewtonianEuler::Tags::VPlus>>*>\n        char_vars,\n    const Variables<tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                               NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                               NewtonianEuler::Tags::EnergyDensity>>& cons_vars,\n    const Matrix& left) {\n  characteristic_fields(\n      make_not_null(&get<NewtonianEuler::Tags::VMinus>(*char_vars)),\n      make_not_null(\n          &get<NewtonianEuler::Tags::VMomentum<VolumeDim>>(*char_vars)),\n      make_not_null(&get<NewtonianEuler::Tags::VPlus>(*char_vars)),\n      get<NewtonianEuler::Tags::MassDensityCons>(cons_vars),\n      get<NewtonianEuler::Tags::MomentumDensity<VolumeDim>>(cons_vars),\n      get<NewtonianEuler::Tags::EnergyDensity>(cons_vars), left);\n}\n\ntemplate <size_t VolumeDim>\nvoid conserved_fields_from_characteristic_fields(\n    const gsl::not_null<Scalar<DataVector>*> cons_mass_density,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> cons_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> cons_energy_density,\n    const Scalar<DataVector>& char_v_minus,\n    const tnsr::I<DataVector, VolumeDim>& char_v_momentum,\n    const Scalar<DataVector>& char_v_plus, const Matrix& right) {\n  get(*cons_mass_density) = right(0, 0) * get(char_v_minus);\n  for (size_t j = 0; j < VolumeDim; ++j) {\n    cons_momentum_density->get(j) = right(j + 1, 0) * get(char_v_minus);\n  }\n  get(*cons_energy_density) = right(VolumeDim + 1, 0) * get(char_v_minus);\n\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    get(*cons_mass_density) += right(0, i + 1) * char_v_momentum.get(i);\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      cons_momentum_density->get(j) +=\n          right(j + 1, i + 1) * char_v_momentum.get(i);\n    }\n    get(*cons_energy_density) +=\n        right(VolumeDim + 1, i + 1) * char_v_momentum.get(i);\n  }\n\n  get(*cons_mass_density) += right(0, VolumeDim + 1) * get(char_v_plus);\n  for (size_t j = 0; j < VolumeDim; ++j) {\n    cons_momentum_density->get(j) +=\n        right(j + 1, VolumeDim + 1) * get(char_v_plus);\n  }\n  get(*cons_energy_density) +=\n      right(VolumeDim + 1, VolumeDim + 1) * get(char_v_plus);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                      \\\n  template std::pair<Matrix, Matrix> right_and_left_eigenvectors( \\\n      const Scalar<double>&, const tnsr::I<double, DIM(data)>&,   \\\n      const Scalar<double>&,                                      \\\n      const EquationsOfState::EquationOfState<false, 2>&,         \\\n      const tnsr::i<double, DIM(data)>&, const bool);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n\n#define INSTANTIATE(_, data)                                               \\\n  template void characteristic_fields(                                     \\\n      const gsl::not_null<tuples::TaggedTuple<                             \\\n          ::Tags::Mean<NewtonianEuler::Tags::VMinus>,                      \\\n          ::Tags::Mean<NewtonianEuler::Tags::VMomentum<DIM(data)>>,        \\\n          ::Tags::Mean<NewtonianEuler::Tags::VPlus>>*>,                    \\\n      const tuples::TaggedTuple<                                           \\\n          ::Tags::Mean<NewtonianEuler::Tags::MassDensityCons>,             \\\n          ::Tags::Mean<NewtonianEuler::Tags::MomentumDensity<DIM(data)>>,  \\\n          ::Tags::Mean<NewtonianEuler::Tags::EnergyDensity>>&,             \\\n      const Matrix&);                                                      \\\n  template void characteristic_fields(                                     \\\n      const gsl::not_null<Scalar<DataVector>*>,                            \\\n      const gsl::not_null<tnsr::I<DataVector, DIM(data)>*>,                \\\n      const gsl::not_null<Scalar<DataVector>*>, const Scalar<DataVector>&, \\\n      const tnsr::I<DataVector, DIM(data)>&, const Scalar<DataVector>&,    \\\n      const Matrix&);                                                      \\\n  template void characteristic_fields(                                     \\\n      const gsl::not_null<                                                 \\\n          Variables<tmpl::list<NewtonianEuler::Tags::VMinus,               \\\n                               NewtonianEuler::Tags::VMomentum<DIM(data)>, \\\n                               NewtonianEuler::Tags::VPlus>>*>,            \\\n      const Variables<                                                     \\\n          tmpl::list<NewtonianEuler::Tags::MassDensityCons,                \\\n                     NewtonianEuler::Tags::MomentumDensity<DIM(data)>,     \\\n                     NewtonianEuler::Tags::EnergyDensity>>&,               \\\n      const Matrix&);                                                      \\\n  template void conserved_fields_from_characteristic_fields(               \\\n      const gsl::not_null<Scalar<DataVector>*>,                            \\\n      const gsl::not_null<tnsr::I<DataVector, DIM(data)>*>,                \\\n      const gsl::not_null<Scalar<DataVector>*>, const Scalar<DataVector>&, \\\n      const tnsr::I<DataVector, DIM(data)>&, const Scalar<DataVector>&,    \\\n      const Matrix&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace NewtonianEuler::Limiters\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/CharacteristicHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Characteristics.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::Limiters {\n\n/*!\n * \\brief Compute the transform matrices between the conserved variables and\n * the characteristic variables of the NewtonianEuler system.\n *\n * Wraps calls to `NewtonianEuler::right_eigenvectors` and\n * `NewtonianEuler::left_eigenvectors`.\n */\ntemplate <size_t VolumeDim>\nstd::pair<Matrix, Matrix> right_and_left_eigenvectors(\n    const Scalar<double>& mean_density,\n    const tnsr::I<double, VolumeDim>& mean_momentum,\n    const Scalar<double>& mean_energy,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n    const tnsr::i<double, VolumeDim>& unit_vector,\n    bool compute_char_transformation_numerically = false);\n\n/// @{\n/// \\brief Compute characteristic fields from conserved fields\n///\n/// Note that these functions apply the same transformation to every grid point\n/// in the element, using the same matrix from `left_eigenvectors`.\n/// This is in contrast to the characteristic transformation used in the\n/// GeneralizedHarmonic upwind flux, which is computed pointwise.\n///\n/// By using a fixed matrix of eigenvectors computed from the cell-averaged\n/// fields, we ensure we can consistently transform back from the characteristic\n/// variables even after applying the limiter (the limiter changes the pointwise\n/// values but preserves the cell averages).\ntemplate <size_t VolumeDim>\nvoid characteristic_fields(\n    gsl::not_null<tuples::TaggedTuple<\n        ::Tags::Mean<NewtonianEuler::Tags::VMinus>,\n        ::Tags::Mean<NewtonianEuler::Tags::VMomentum<VolumeDim>>,\n        ::Tags::Mean<NewtonianEuler::Tags::VPlus>>*>\n        char_means,\n    const tuples::TaggedTuple<\n        ::Tags::Mean<NewtonianEuler::Tags::MassDensityCons>,\n        ::Tags::Mean<NewtonianEuler::Tags::MomentumDensity<VolumeDim>>,\n        ::Tags::Mean<NewtonianEuler::Tags::EnergyDensity>>& cons_means,\n    const Matrix& left);\n\ntemplate <size_t VolumeDim>\nvoid characteristic_fields(\n    gsl::not_null<Scalar<DataVector>*> char_v_minus,\n    gsl::not_null<tnsr::I<DataVector, VolumeDim>*> char_v_momentum,\n    gsl::not_null<Scalar<DataVector>*> char_v_plus,\n    const Scalar<DataVector>& cons_mass_density,\n    const tnsr::I<DataVector, VolumeDim>& cons_momentum_density,\n    const Scalar<DataVector>& cons_energy_density, const Matrix& left);\n\ntemplate <size_t VolumeDim>\nvoid characteristic_fields(\n    gsl::not_null<\n        Variables<tmpl::list<NewtonianEuler::Tags::VMinus,\n                             NewtonianEuler::Tags::VMomentum<VolumeDim>,\n                             NewtonianEuler::Tags::VPlus>>*>\n        char_vars,\n    const Variables<tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                               NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                               NewtonianEuler::Tags::EnergyDensity>>& cons_vars,\n    const Matrix& left);\n/// @}\n\n/// \\brief Compute conserved fields from characteristic fields\n///\n/// Note that this function applies the same transformation to every grid point\n/// in the element, using the same matrix from `right_eigenvectors`.\n/// This is in contrast to the characteristic transformation used in the\n/// GeneralizedHarmonic upwind flux, which is computed pointwise.\n///\n/// By using a fixed matrix of eigenvectors computed from the cell-averaged\n/// fields, we ensure we can consistently transform back from the characteristic\n/// variables even after applying the limiter (the limiter changes the pointwise\n/// values but preserves the cell averages).\ntemplate <size_t VolumeDim>\nvoid conserved_fields_from_characteristic_fields(\n    gsl::not_null<Scalar<DataVector>*> cons_mass_density,\n    gsl::not_null<tnsr::I<DataVector, VolumeDim>*> cons_momentum_density,\n    gsl::not_null<Scalar<DataVector>*> cons_energy_density,\n    const Scalar<DataVector>& char_v_minus,\n    const tnsr::I<DataVector, VolumeDim>& char_v_momentum,\n    const Scalar<DataVector>& char_v_plus, const Matrix& right);\n\n/// \\brief Apply a limiter to the characteristic fields computed with respect\n/// to each direction in the volume, then take average of results\n///\n/// When computing the characteristic fields in the volume to pass as inputs to\n/// the limiter, it is not necessarily clear (in more than one dimension) which\n/// unit vector should be used in the characteristic decomposition. This is in\n/// contrast to uses of characteristic fields for, e.g., computing boundary\n/// conditions where the boundary normal provides a clear choice of unit vector.\n//\n/// The common solution to this challenge is to average the results of applying\n/// the limiter with different choices of characteristic decomposition:\n/// - For each direction (e.g. in 3D for each of\n///   \\f$(\\hat{x}, \\hat{y}, \\hat{z})\\f$), compute the characteristic fields with\n///   respect to this direction. This gives `VolumeDim` sets of characteristic\n///   fields.\n/// - Apply the limiter to each set of characteristic fields, then convert back\n///   to conserved fields. This gives `VolumeDim` different sets of limited\n///   conserved fields.\n/// - Average the new conserved fields; this is the result of the limiting\n///   process.\n///\n/// This function handles the logic of computing the different characteristic\n/// fields, limiting them, converting back to conserved fields, and averaging.\n///\n/// The limiter to apply is passed in via a lambda which is responsible for\n/// converting any neighbor data to the characteristic representation, and then\n/// applying the limiter to all NewtonianEuler characteristic fields.\ntemplate <size_t VolumeDim, typename LimiterLambda>\nbool apply_limiter_to_characteristic_fields_in_all_directions(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const Mesh<VolumeDim>& mesh,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n    const LimiterLambda& prepare_and_apply_limiter,\n    const bool compute_char_transformation_numerically = false) {\n  // Temp variables for calculations\n  // There are quite a few tensors in this allocation because in general we need\n  // to preserve the input (cons field) tensors until they are overwritten at\n  // the very end of the computation... then we need additional buffers for the\n  // char fields, the limited cons fields, and the accumulated cons fields for\n  // averaging.\n  //\n  // Possible optimization: specialize the 1D case which doesn't do average so\n  // doesn't need as many buffers\n  Variables<tmpl::list<\n      NewtonianEuler::Tags::VMinus, NewtonianEuler::Tags::VMomentum<VolumeDim>,\n      NewtonianEuler::Tags::VPlus, ::Tags::TempScalar<0>,\n      ::Tags::TempI<0, VolumeDim>, ::Tags::TempScalar<1>, ::Tags::TempScalar<2>,\n      ::Tags::TempI<1, VolumeDim>, ::Tags::TempScalar<3>>>\n      temp_buffer(mesh.number_of_grid_points());\n  auto& char_v_minus = get<NewtonianEuler::Tags::VMinus>(temp_buffer);\n  auto& char_v_momentum =\n      get<NewtonianEuler::Tags::VMomentum<VolumeDim>>(temp_buffer);\n  auto& char_v_plus = get<NewtonianEuler::Tags::VPlus>(temp_buffer);\n  auto& temp_mass_density_cons = get<::Tags::TempScalar<0>>(temp_buffer);\n  auto& temp_momentum_density = get<::Tags::TempI<0, VolumeDim>>(temp_buffer);\n  auto& temp_energy_density = get<::Tags::TempScalar<1>>(temp_buffer);\n  auto& accumulate_mass_density_cons = get<::Tags::TempScalar<2>>(temp_buffer);\n  auto& accumulate_momentum_density =\n      get<::Tags::TempI<1, VolumeDim>>(temp_buffer);\n  auto& accumulate_energy_density = get<::Tags::TempScalar<3>>(temp_buffer);\n\n  // Initialize the accumulating tensors\n  get(accumulate_mass_density_cons) = 0.;\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    accumulate_momentum_density.get(i) = 0.;\n  }\n  get(accumulate_energy_density) = 0.;\n\n  // Cellwise means, used in computing the cons/char transformations\n  const auto mean_density =\n      Scalar<double>{mean_value(get(*mass_density_cons), mesh)};\n  const auto mean_momentum = [&momentum_density, &mesh]() {\n    tnsr::I<double, VolumeDim> result{};\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      result.get(i) = mean_value(momentum_density->get(i), mesh);\n    }\n    return result;\n  }();\n  const auto mean_energy =\n      Scalar<double>{mean_value(get(*energy_density), mesh)};\n\n  bool some_component_was_limited = false;\n\n  // Loop over directions, then compute chars w.r.t. this direction and limit\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const auto unit_vector = [&d]() {\n      auto components = make_array<VolumeDim>(0.);\n      components[d] = 1.;\n      return tnsr::i<double, VolumeDim>(components);\n    }();\n    const auto right_and_left =\n        NewtonianEuler::Limiters::right_and_left_eigenvectors(\n            mean_density, mean_momentum, mean_energy, equation_of_state,\n            unit_vector, compute_char_transformation_numerically);\n    const auto& right = right_and_left.first;\n    const auto& left = right_and_left.second;\n\n    // Transform tensors to characteristics\n    NewtonianEuler::Limiters::characteristic_fields(\n        make_not_null(&char_v_minus), make_not_null(&char_v_momentum),\n        make_not_null(&char_v_plus), *mass_density_cons, *momentum_density,\n        *energy_density, left);\n\n    // Transform neighbor data and apply limiter\n    const bool some_component_was_limited_with_this_unit_vector =\n        prepare_and_apply_limiter(make_not_null(&char_v_minus),\n                                  make_not_null(&char_v_momentum),\n                                  make_not_null(&char_v_plus), left);\n\n    some_component_was_limited =\n        some_component_was_limited_with_this_unit_vector or\n        some_component_was_limited;\n\n    // Transform back to conserved variables. But skip the transformation if no\n    // limiting occured with this unit vector.\n    if (some_component_was_limited_with_this_unit_vector) {\n      NewtonianEuler::Limiters::conserved_fields_from_characteristic_fields(\n          make_not_null(&temp_mass_density_cons),\n          make_not_null(&temp_momentum_density),\n          make_not_null(&temp_energy_density), char_v_minus, char_v_momentum,\n          char_v_plus, right);\n    } else {\n      temp_mass_density_cons = *mass_density_cons;\n      temp_momentum_density = *momentum_density;\n      temp_energy_density = *energy_density;\n    }\n\n    // Add to running sum for averaging\n    const double one_over_dim = 1.0 / static_cast<double>(VolumeDim);\n    get(accumulate_mass_density_cons) +=\n        one_over_dim * get(temp_mass_density_cons);\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      accumulate_momentum_density.get(i) +=\n          one_over_dim * temp_momentum_density.get(i);\n    }\n    get(accumulate_energy_density) += one_over_dim * get(temp_energy_density);\n  }  // for loop over dimensions\n\n  *mass_density_cons = accumulate_mass_density_cons;\n  *momentum_density = accumulate_momentum_density;\n  *energy_density = accumulate_energy_density;\n  return some_component_was_limited;\n}\n\n}  // namespace NewtonianEuler::Limiters\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/Flattener.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Flattener.hpp\"\n\n#include <array>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace NewtonianEuler::Limiters {\n\ntemplate <size_t VolumeDim>\nFlattenerAction flatten_solution(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const Mesh<VolumeDim>& mesh,\n    const Scalar<DataVector>& det_logical_to_inertial_jacobian,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state) {\n  // A note on the design behind the handling of the cell-averaged fields:\n  //\n  // The cell averages are needed for\n  // - sanity-checking the inputs for validity\n  // - flattening in case of negative density\n  // - flattening in case of negative pressure\n  //\n  // In addition, we have the following optimization goals:\n  // - cell averages are only computed once\n  // - cell averages are only computed when needed\n  //\n  // To meet these goals, we create a temporary variable for each field's cell\n  // average. These temporaries live on the stack and should have minimal cost.\n  double mean_density = std::numeric_limits<double>::signaling_NaN();\n  auto mean_momentum =\n      make_array<VolumeDim>(std::numeric_limits<double>::signaling_NaN());\n  double mean_energy = std::numeric_limits<double>::signaling_NaN();\n\n  const auto compute_means = [&mean_density, &mean_momentum, &mean_energy,\n                              &mass_density_cons, &momentum_density,\n                              &energy_density, &mesh,\n                              &det_logical_to_inertial_jacobian]() {\n    // Compute the means w.r.t. the inertial coords\n    // (Note that several other parts of the limiter code take means w.r.t. the\n    // logical coords, and therefore might not be conservative on curved grids)\n    const double volume_of_cell =\n        definite_integral(get(det_logical_to_inertial_jacobian), mesh);\n    const auto inertial_coord_mean = [&mesh, &det_logical_to_inertial_jacobian,\n                                      &volume_of_cell](const DataVector& u) {\n      // Note that the term `det_jac * u` below results in an allocation.\n      // If this function needs to be optimized, a buffer for the product\n      // could be allocated outside the lambda, and updated in the lambda.\n      return definite_integral(get(det_logical_to_inertial_jacobian) * u,\n                               mesh) /\n             volume_of_cell;\n    };\n    mean_density = inertial_coord_mean(get(*mass_density_cons));\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      gsl::at(mean_momentum, i) = inertial_coord_mean(momentum_density->get(i));\n    }\n    mean_energy = inertial_coord_mean(get(*energy_density));\n\n    // sanity check the means\n    ASSERT(mean_density > 0., \"Invalid mass density input to flattener\");\n    ASSERT(mean_energy > 0., \"Invalid energy density input to flattener\");\n  };\n\n  FlattenerAction flattener_action = FlattenerAction::NoOp;\n\n  // If min(density) is negative, then flatten.\n  const double min_density = min(get(*mass_density_cons));\n  if (min_density < 0.) {\n    compute_means();\n\n    // Note: the current algorithm flattens all fields by the same factor,\n    // though in principle a different factor could be applied to each field.\n    constexpr double safety = 0.95;\n    const double factor = safety * mean_density / (mean_density - min_density);\n\n    get(*mass_density_cons) =\n        mean_density + factor * (get(*mass_density_cons) - mean_density);\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      momentum_density->get(i) =\n          gsl::at(mean_momentum, i) +\n          factor * (momentum_density->get(i) - gsl::at(mean_momentum, i));\n    }\n    get(*energy_density) =\n        mean_energy + factor * (get(*energy_density) - mean_energy);\n\n    flattener_action = FlattenerAction::ScaledSolution;\n  }\n\n  // Check for negative pressures\n  const auto specific_internal_energy = Scalar<DataVector>{\n      get(*energy_density) / get(*mass_density_cons) -\n      0.5 * get(dot_product(*momentum_density, *momentum_density)) /\n          square(get(*mass_density_cons))};\n  const auto pressure = equation_of_state.pressure_from_density_and_energy(\n      *mass_density_cons, specific_internal_energy);\n\n  // If min(pressure) is negative, set solution to cell averages\n  const double min_pressure = min(get(pressure));\n  if (min_pressure < 0.) {\n    if (flattener_action == FlattenerAction::NoOp) {\n      // We didn't previously correct for negative densities, therefore the\n      // means have not yet been computed\n      compute_means();\n    }\n\n    get(*mass_density_cons) = mean_density;\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      momentum_density->get(i) = gsl::at(mean_momentum, i);\n    }\n    get(*energy_density) = mean_energy;\n\n    flattener_action = FlattenerAction::SetSolutionToMean;\n  }\n\n  return flattener_action;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                      \\\n  template FlattenerAction flatten_solution(                      \\\n      gsl::not_null<Scalar<DataVector>*>,                         \\\n      gsl::not_null<tnsr::I<DataVector, DIM(data)>*>,             \\\n      gsl::not_null<Scalar<DataVector>*>, const Mesh<DIM(data)>&, \\\n      const Scalar<DataVector>&,                                  \\\n      const EquationsOfState::EquationOfState<false, 2>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace NewtonianEuler::Limiters\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/Flattener.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\n\nnamespace EquationsOfState {\ntemplate <bool, size_t>\nclass EquationOfState;\n}  // namespace EquationsOfState\n/// \\endcond\n\nnamespace NewtonianEuler::Limiters {\n\n/// \\ingroup LimitersGroup\n/// \\brief Encodes the action taken by `flatten_solution`\nenum class FlattenerAction {\n  NoOp = 0,\n  ScaledSolution = 1,\n  SetSolutionToMean = 2,\n};\n\n/// \\ingroup LimitersGroup\n/// \\brief Scale a NewtonianEuler solution around its mean to remove pointwise\n/// positivity violations.\n///\n/// If the solution has points with negative density, scales the solution\n/// to make these points positive again. For each component \\f$u\\f$ of the\n/// solution, the scaling takes the form\n/// \\f$u \\to \\bar{u} + \\theta (u - \\bar{u})\\f$,\n/// where \\f$\\bar{u}\\f$ is the cell-average value of \\f$u\\f$, and \\f$\\theta\\f$\n/// is a factor less than 1, chosen to restore positive density.\n/// The cell averages in this implementation are computed in inertial\n/// coordinates, so the flattener is conservative even on deformed grids.\n///\n/// A scaling of this form used to restore positivity is usually called a\n/// flattener (we use this name) or a bounds-preserving filter. Note that the\n/// scaling approach only works if the cell-averaged solution is itself\n/// physical, in other words, if the cell-averaged density is positive.\n///\n/// After checking for (and correcting) negative densities, if the equation of\n/// state is two-dimensional, then the pressure is also checked for positivity.\n/// If negative pressures are found, each solution component is set to its mean\n/// (this is equivalent to \\f$\\theta = 0\\f$ in the scaling form above).\n/// In principle, a less aggressive scaling could be used, but solving for the\n/// correct \\f$\\theta\\f$ in this case is more involved.\ntemplate <size_t VolumeDim>\nFlattenerAction flatten_solution(\n    gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    gsl::not_null<Scalar<DataVector>*> energy_density,\n    const Mesh<VolumeDim>& mesh,\n    const Scalar<DataVector>& det_logical_to_inertial_jacobian,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state);\n\n}  // namespace NewtonianEuler::Limiters\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/KxrcfTci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\nnamespace NewtonianEuler {\nnamespace Limiters {\nnamespace Tci {\n\n/// \\ingroup LimitersGroup\n/// \\brief Implements the troubled-cell indicator from Krivodonova et al, 2004.\n///\n/// The KXRCF (these are the author initials) TCI is described in\n/// \\cite Krivodonova2004.\n///\n/// In summary, this TCI uses the size of discontinuities between neighboring DG\n/// elements to determine the smoothness of the solution. This works because the\n/// discontinuities converge rapidly for smooth solutions, therefore a large\n/// discontinuity suggests a lack of smoothness and the need to apply a limiter.\n///\n/// The reference sets the constant we call `kxrcf_constant` to 1. This should\n/// generally be a good threshold to use, though it might not be the optimal\n/// value (in balancing robustness vs accuracy) for any particular problem.\n///\n/// This implementation\n/// - does not support h- or p-refinement; this is checked by assertion.\n/// - chooses not to check external boundaries, because this adds complexity.\n///   However, by not checking external boundaries, the implementation may not\n///   be robust for problems that feed in shocks through boundary conditions.\ntemplate <size_t VolumeDim, typename PackagedData>\nbool kxrcf_indicator(\n    const double kxrcf_constant, const Scalar<DataVector>& cons_mass_density,\n    const tnsr::I<DataVector, VolumeDim>& cons_momentum_density,\n    const Scalar<DataVector>& cons_energy_density, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const Scalar<DataVector>& det_logical_to_inertial_jacobian,\n    const typename evolution::dg::Tags::NormalCovectorAndMagnitude<\n        VolumeDim>::type& normals_and_magnitudes,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) {\n  // Enforce restrictions on h-refinement, p-refinement\n  if (UNLIKELY(\n          alg::any_of(element.neighbors(), [](const auto& direction_neighbors) {\n            return direction_neighbors.second.size() != 1;\n          }))) {\n    ERROR(\"The Kxrcf TCI does not yet support h-refinement\");\n    // Removing this limitation will require adapting the surface integrals to\n    // correctly acount for,\n    // - multiple (smaller) neighbors contributing to the integral\n    // - only a portion of a (larger) neighbor contributing to the integral\n  }\n  alg::for_each(neighbor_data, [&mesh](const auto& neighbor_and_data) {\n    if (UNLIKELY(neighbor_and_data.second.mesh != mesh)) {\n      ERROR(\"The Kxrcf TCI does not yet support p-refinement\");\n      // Removing this limitation will require generalizing the surface\n      // integrals to make sure the meshes are consistent.\n    }\n  });\n  // Check the mesh matches expectations:\n  // - the extents must be uniform, because the TCI expects a unique \"order\" for\n  //   the DG scheme. This shows up when computing the threshold parameter,\n  //   h^(degree+1)/2\n  // - the quadrature must be GL, because the current implementation doesn't\n  //   handle extrapolation to the boundary, though this could be changed.\n  // - the basis in principle could be changed, but until we need to change it\n  //   we just check it's the expected Legendre for simplicity\n  ASSERT(mesh == Mesh<VolumeDim>(mesh.extents(0), Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto),\n         \"The Kxrcf TCI expects a uniform LGL mesh, but got mesh = \" << mesh);\n\n  bool inflow_boundaries_present = false;\n  double inflow_area = 0.;\n  double inflow_delta_density = 0.;\n  double inflow_delta_energy = 0.;\n\n  // Skip boundary integrations on external boundaries. This choice might be\n  // problematic for evolutions (likely only simple test cases) that feed in\n  // shocks through the boundary condition: the limiter might fail to activate\n  // in the cell that touches the boundary.\n  //\n  // To properly compute the limiter at external boundaries we would need the\n  // limiter to know about the boundary condition, which may be difficult to\n  // do in a general way.\n  for (const auto& [neighbor, data] : neighbor_data) {\n    const auto& dir = neighbor.direction();\n\n    // Check consistency of neighbor_data with element and normals\n    ASSERT(element.neighbors().contains(dir),\n           \"Received neighbor data from dir = \"\n               << dir << \", but element has no neighbor in this dir\");\n    ASSERT(normals_and_magnitudes.contains(dir),\n           \"Received neighbor data from dir = \"\n               << dir\n               << \", but normals_and_magnitudes has no normal in this dir\");\n    ASSERT(normals_and_magnitudes.at(dir).has_value(),\n           \"The normals_and_magnitudes are not up-to-date in dir = \" << dir);\n    const auto& normal = get<evolution::dg::Tags::NormalCovector<VolumeDim>>(\n        normals_and_magnitudes.at(dir).value());\n    const auto& magnitude_of_normal =\n        get<evolution::dg::Tags::MagnitudeOfNormal>(\n            normals_and_magnitudes.at(dir).value());\n\n    const size_t sliced_dim = dir.dimension();\n    const size_t index_of_slice =\n        (dir.side() == Side::Lower ? 0 : mesh.extents()[sliced_dim] - 1);\n    const auto momentum_on_slice = data_on_slice(\n        cons_momentum_density, mesh.extents(), sliced_dim, index_of_slice);\n    const auto momentum_dot_normal = dot_product(momentum_on_slice, normal);\n\n    // Skip boundaries with no significant inflow\n    // Note: the cutoff value here is small but arbitrarily chosen.\n    if (min(get(momentum_dot_normal)) > -1e-12) {\n      continue;\n    }\n    inflow_boundaries_present = true;\n\n    // This mask has value 1. for momentum_dot_normal < 0.\n    //                     0. for momentum_dot_normal >= 0.\n    const DataVector inflow_mask = 1. - step_function(get(momentum_dot_normal));\n    // Mask is then weighted pointwise by the Jacobian determinant giving\n    // surface integrals in inertial coordinates. This Jacobian determinant is\n    // given by the product of the volume Jacobian determinant with the\n    // magnitude of the unnormalized normal covectors.\n    const DataVector weighted_inflow_mask =\n        inflow_mask *\n        get(data_on_slice(det_logical_to_inertial_jacobian, mesh.extents(),\n                          sliced_dim, index_of_slice)) *\n        get(magnitude_of_normal);\n\n    inflow_area +=\n        definite_integral(weighted_inflow_mask, mesh.slice_away(sliced_dim));\n\n    // This is the step that is incompatible with h/p refinement. For use with\n    // h/p refinement, would need to correctly obtain the neighbor solution on\n    // the local grid points.\n    const auto neighbor_vars_on_slice = data_on_slice(\n        data.volume_data, mesh.extents(), sliced_dim, index_of_slice);\n\n    const auto density_on_slice = data_on_slice(\n        cons_mass_density, mesh.extents(), sliced_dim, index_of_slice);\n    const auto& neighbor_density_on_slice =\n        get<NewtonianEuler::Tags::MassDensityCons>(neighbor_vars_on_slice);\n    inflow_delta_density += definite_integral(\n        (get(density_on_slice) - get(neighbor_density_on_slice)) *\n            weighted_inflow_mask,\n        mesh.slice_away(sliced_dim));\n\n    const auto energy_on_slice = data_on_slice(\n        cons_energy_density, mesh.extents(), sliced_dim, index_of_slice);\n    const auto& neighbor_energy_on_slice =\n        get<NewtonianEuler::Tags::EnergyDensity>(neighbor_vars_on_slice);\n    inflow_delta_energy += definite_integral(\n        (get(energy_on_slice) - get(neighbor_energy_on_slice)) *\n            weighted_inflow_mask,\n        mesh.slice_away(sliced_dim));\n  }\n\n  if (not inflow_boundaries_present) {\n    // No boundaries had inflow, so not a troubled cell\n    return false;\n  }\n\n  // KXRCF take h to be the radius of the circumscribed circle\n  const double h = 0.5 * magnitude(element_size);\n  const double h_pow = pow(h, 0.5 * mesh.extents(0));\n\n  ASSERT(inflow_area > 0.,\n         \"Sanity check failed: negative area of inflow boundaries\");\n\n  const double norm_squared_density = mean_value(\n      get(det_logical_to_inertial_jacobian) * square(get(cons_mass_density)),\n      mesh);\n  ASSERT(norm_squared_density > 0.,\n         \"Sanity check failed: negative density norm over element\");\n  const double norm_density = sqrt(norm_squared_density);\n  const double ratio_for_density =\n      abs(inflow_delta_density) / (h_pow * inflow_area * norm_density);\n\n  const double norm_squared_energy = mean_value(\n      get(det_logical_to_inertial_jacobian) * square(get(cons_energy_density)),\n      mesh);\n  ASSERT(norm_squared_energy > 0.,\n         \"Sanity check failed: negative energy norm over element\");\n  const double norm_energy = sqrt(norm_squared_energy);\n  const double ratio_for_energy =\n      abs(inflow_delta_energy) / (h_pow * inflow_area * norm_energy);\n\n  return (ratio_for_density > kxrcf_constant or\n          ratio_for_energy > kxrcf_constant);\n}\n\n}  // namespace Tci\n}  // namespace Limiters\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/Minmod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Minmod.hpp\"\n\n#include <array>\n#include <cstdlib>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/CharacteristicHelpers.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Flattener.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <size_t VolumeDim>\nbool characteristic_minmod_impl(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const Limiters::MinmodType minmod_type, const double tvb_constant,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, VolumeDim>& element_size,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename NewtonianEuler::Limiters::Minmod<VolumeDim>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data,\n    const bool compute_char_transformation_numerically) {\n  // Storage for transforming neighbor_data into char variables\n  using CharacteristicVarsMinmod =\n      Limiters::Minmod<VolumeDim,\n                       tmpl::list<NewtonianEuler::Tags::VMinus,\n                                  NewtonianEuler::Tags::VMomentum<VolumeDim>,\n                                  NewtonianEuler::Tags::VPlus>>;\n  std::unordered_map<DirectionalId<VolumeDim>,\n                     typename CharacteristicVarsMinmod::PackagedData,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_char_data{};\n  for (const auto& [key, data] : neighbor_data) {\n    neighbor_char_data[key].element_size = data.element_size;\n  }\n\n  // Buffers for minmod limiter and TCI\n  DataVector u_lin_buffer(mesh.number_of_grid_points());\n  Limiters::Minmod_detail::BufferWrapper<VolumeDim> tci_buffer(mesh);\n\n  // Outer lambda: wraps applying minmod to the NewtonianEuler characteristics\n  // for one particular choice of characteristic decomposition\n  const auto minmod_convert_neighbor_data_then_limit =\n      [&u_lin_buffer, &tci_buffer, &minmod_type, &tvb_constant, &mesh, &element,\n       &logical_coords, &element_size, &neighbor_data, &neighbor_char_data](\n          const gsl::not_null<Scalar<DataVector>*> char_v_minus,\n          const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> char_v_momentum,\n          const gsl::not_null<Scalar<DataVector>*> char_v_plus,\n          const Matrix& left_eigenvectors) -> bool {\n    // Convert neighbor data to characteristics\n    for (const auto& [key, data] : neighbor_data) {\n      NewtonianEuler::Limiters::characteristic_fields(\n          make_not_null(&(neighbor_char_data[key].means)), data.means,\n          left_eigenvectors);\n    }\n\n    bool some_component_was_limited_with_this_normal = false;\n\n    // Inner lambda: apply minmod to one particular tensor\n    const auto wrap_minmod = [&some_component_was_limited_with_this_normal,\n                              &u_lin_buffer, &tci_buffer, &minmod_type,\n                              &tvb_constant, &mesh, &element, &logical_coords,\n                              &element_size, &neighbor_char_data](\n                                 auto tag, const auto tensor) {\n      const bool result =\n          Limiters::Minmod_detail::minmod_impl<VolumeDim, decltype(tag)>(\n              &u_lin_buffer, &tci_buffer, tensor, minmod_type, tvb_constant,\n              mesh, element, logical_coords, element_size, neighbor_char_data);\n      some_component_was_limited_with_this_normal =\n          result or some_component_was_limited_with_this_normal;\n    };\n    wrap_minmod(NewtonianEuler::Tags::VMinus{}, char_v_minus);\n    wrap_minmod(NewtonianEuler::Tags::VMomentum<VolumeDim>{}, char_v_momentum);\n    wrap_minmod(NewtonianEuler::Tags::VPlus{}, char_v_plus);\n    return some_component_was_limited_with_this_normal;\n  };\n\n  return NewtonianEuler::Limiters::\n      apply_limiter_to_characteristic_fields_in_all_directions(\n          mass_density_cons, momentum_density, energy_density, mesh,\n          equation_of_state, minmod_convert_neighbor_data_then_limit,\n          compute_char_transformation_numerically);\n}\n\n}  // namespace\n\nnamespace NewtonianEuler::Limiters {\n\ntemplate <size_t VolumeDim>\nMinmod<VolumeDim>::Minmod(\n    const ::Limiters::MinmodType minmod_type,\n    const NewtonianEuler::Limiters::VariablesToLimit vars_to_limit,\n    const double tvb_constant, const bool apply_flattener,\n    const bool disable_for_debugging)\n    : minmod_type_(minmod_type),\n      vars_to_limit_(vars_to_limit),\n      tvb_constant_(tvb_constant),\n      apply_flattener_(apply_flattener),\n      disable_for_debugging_(disable_for_debugging),\n      conservative_vars_minmod_(minmod_type_, tvb_constant_,\n                                disable_for_debugging_) {\n  ASSERT(tvb_constant >= 0.0, \"The TVB constant must be non-negative.\");\n}\n\ntemplate <size_t VolumeDim>\nvoid Minmod<VolumeDim>::pup(PUP::er& p) {\n  p | minmod_type_;\n  p | vars_to_limit_;\n  p | tvb_constant_;\n  p | apply_flattener_;\n  p | disable_for_debugging_;\n  p | conservative_vars_minmod_;\n}\n\ntemplate <size_t VolumeDim>\nvoid Minmod<VolumeDim>::package_data(\n    const gsl::not_null<PackagedData*> packaged_data,\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, VolumeDim>& momentum_density,\n    const Scalar<DataVector>& energy_density, const Mesh<VolumeDim>& mesh,\n    const std::array<double, VolumeDim>& element_size,\n    const OrientationMap<VolumeDim>& orientation_map) const {\n  conservative_vars_minmod_.package_data(packaged_data, mass_density_cons,\n                                         momentum_density, energy_density, mesh,\n                                         element_size, orientation_map);\n}\n\ntemplate <size_t VolumeDim>\nbool Minmod<VolumeDim>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, VolumeDim>& element_size,\n    const Scalar<DataVector>& det_inv_logical_to_inertial_jacobian,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) const {\n  if (UNLIKELY(disable_for_debugging_)) {\n    // Do not modify input tensors\n    return false;\n  }\n\n  // Checks for the post-timestep, pre-limiter NewtonianEuler state:\n#ifdef SPECTRE_DEBUG\n  const double mean_density = mean_value(get(*mass_density_cons), mesh);\n  ASSERT(mean_density > 0.0,\n         \"Positivity was violated on a cell-average level.\");\n  const double mean_energy = mean_value(get(*energy_density), mesh);\n  ASSERT(mean_energy > 0.0, \"Positivity was violated on a cell-average level.\");\n#endif  // SPECTRE_DEBUG\n\n  bool limiter_activated = false;\n\n  if (vars_to_limit_ == NewtonianEuler::Limiters::VariablesToLimit::Conserved) {\n    limiter_activated = conservative_vars_minmod_(\n        mass_density_cons, momentum_density, energy_density, mesh, element,\n        logical_coords, element_size, neighbor_data);\n  } else if (vars_to_limit_ ==\n                 NewtonianEuler::Limiters::VariablesToLimit::Characteristic or\n             vars_to_limit_ == NewtonianEuler::Limiters::VariablesToLimit::\n                                   NumericalCharacteristic) {\n    const bool compute_char_transformation_numerically =\n        (vars_to_limit_ ==\n         NewtonianEuler::Limiters::VariablesToLimit::NumericalCharacteristic);\n    limiter_activated = characteristic_minmod_impl(\n        mass_density_cons, momentum_density, energy_density, minmod_type_,\n        tvb_constant_, mesh, element, logical_coords, element_size,\n        equation_of_state, neighbor_data,\n        compute_char_transformation_numerically);\n  } else {\n    ERROR(\n        \"No implementation of NewtonianEuler::Limiters::Minmod for variables: \"\n        << vars_to_limit_);\n  }\n\n  if (apply_flattener_) {\n    const Scalar<DataVector> det_logical_to_inertial_jacobian{\n        1.0 / get(det_inv_logical_to_inertial_jacobian)};\n    const auto flattener_action = flatten_solution(\n        mass_density_cons, momentum_density, energy_density, mesh,\n        det_logical_to_inertial_jacobian, equation_of_state);\n    if (flattener_action != FlattenerAction::NoOp) {\n      limiter_activated = true;\n    }\n  }\n\n  // Checks for the post-limiter NewtonianEuler state:\n#ifdef SPECTRE_DEBUG\n  ASSERT(min(get(*mass_density_cons)) > 0.0, \"Bad density after limiting.\");\n  const auto specific_internal_energy = Scalar<DataVector>{\n      get(*energy_density) / get(*mass_density_cons) -\n      0.5 * get(dot_product(*momentum_density, *momentum_density)) /\n          square(get(*mass_density_cons))};\n  const auto pressure = equation_of_state.pressure_from_density_and_energy(\n      *mass_density_cons, specific_internal_energy);\n  ASSERT(min(get(pressure)) > 0.0, \"Bad pressure after limiting.\");\n#endif  // SPECTRE_DEBUG\n\n  return limiter_activated;\n}\n\ntemplate <size_t LocalDim>\nbool operator==(const Minmod<LocalDim>& lhs, const Minmod<LocalDim>& rhs) {\n  // No need to compare the conservative_vars_minmod_ member variable because\n  // it is constructed from the other member variables.\n  return lhs.minmod_type_ == rhs.minmod_type_ and\n         lhs.vars_to_limit_ == rhs.vars_to_limit_ and\n         lhs.tvb_constant_ == rhs.tvb_constant_ and\n         lhs.apply_flattener_ == rhs.apply_flattener_ and\n         lhs.disable_for_debugging_ == rhs.disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Minmod<VolumeDim>& lhs, const Minmod<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                          \\\n  template class Minmod<DIM(data)>;                   \\\n  template bool operator==(const Minmod<DIM(data)>&,  \\\n                           const Minmod<DIM(data)>&); \\\n  template bool operator!=(const Minmod<DIM(data)>&, const Minmod<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace NewtonianEuler::Limiters\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/Minmod.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstdlib>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Minmod.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Tags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/VariablesToLimit.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Direction;\ntemplate <size_t VolumeDim>\nclass Element;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass Mesh;\ntemplate <size_t VolumeDim>\nclass OrientationMap;\n\nnamespace boost {\ntemplate <class T>\nstruct hash;\n}  // namespace boost\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\ntemplate <size_t VolumeDim>\nstruct Element;\ntemplate <size_t VolumeDim>\nstruct Mesh;\ntemplate <size_t VolumeDim>\nstruct SizeOfElement;\n}  // namespace Tags\n}  // namespace domain\n/// \\endcond\n\nnamespace NewtonianEuler {\nnamespace Limiters {\n\n/// \\ingroup LimitersGroup\n/// \\brief A minmod-based generalized slope limiter for the NewtonianEuler\n/// system.\n///\n/// Implements the three minmod-based generalized slope limiters from\n/// \\cite Cockburn1999 Sec. 2.4: \\f$\\Lambda\\Pi^1\\f$, \\f$\\Lambda\\Pi^N\\f$, and\n/// MUSCL. See the documentation of the system-agnostic ::Limiters::Minmod\n/// limiter for a general discussion of the algorithm and the various options\n/// that control the action of the limiter.\n///\n/// This implemention is specialized to the NewtonianEuler evolution system.\n/// By specializing the limiter to the system, we can add two features that\n/// improve its robustness:\n/// - the limiter can be applied to the system's characteristic variables. This\n///   is the recommendation of the reference, because it reduces spurious\n///   oscillations in the post-limiter solution.\n/// - after limiting, the solution can be processed to remove any remaining\n///   unphysical values like negative densities and pressures. We do this by\n///   scaling the solution around its mean (a \"flattener\" or \"bounds-preserving\"\n///   filter). Note: the flattener is applied to all elements, including those\n///   where the limiter did not act to reduce the solution's slopes.\ntemplate <size_t VolumeDim>\nclass Minmod {\n public:\n  using ConservativeVarsMinmod = ::Limiters::Minmod<\n      VolumeDim, tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                            NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                            NewtonianEuler::Tags::EnergyDensity>>;\n\n  struct VariablesToLimit {\n    using type = NewtonianEuler::Limiters::VariablesToLimit;\n    static type suggested_value() {\n      return NewtonianEuler::Limiters::VariablesToLimit::Characteristic;\n    }\n    static constexpr Options::String help = {\n        \"Variable representation on which to apply the limiter\"};\n  };\n  struct ApplyFlattener {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Flatten after limiting to restore pointwise positivity\"};\n  };\n  using options =\n      tmpl::list<typename ConservativeVarsMinmod::Type, VariablesToLimit,\n                 typename ConservativeVarsMinmod::TvbConstant, ApplyFlattener,\n                 typename ConservativeVarsMinmod::DisableForDebugging>;\n  static constexpr Options::String help = {\n      \"A Minmod limiter specialized to the NewtonianEuler system\"};\n  static std::string name() { return \"NewtonianEulerMinmod\"; };\n\n  explicit Minmod(::Limiters::MinmodType minmod_type,\n                  NewtonianEuler::Limiters::VariablesToLimit vars_to_limit,\n                  double tvb_constant, bool apply_flattener,\n                  bool disable_for_debugging = false);\n\n  Minmod() = default;\n  Minmod(const Minmod& /*rhs*/) = default;\n  Minmod& operator=(const Minmod& /*rhs*/) = default;\n  Minmod(Minmod&& /*rhs*/) = default;\n  Minmod& operator=(Minmod&& /*rhs*/) = default;\n  ~Minmod() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  using PackagedData = typename ConservativeVarsMinmod::PackagedData;\n  using package_argument_tags =\n      typename ConservativeVarsMinmod::package_argument_tags;\n\n  /// \\brief Package data for sending to neighbor elements.\n  void package_data(gsl::not_null<PackagedData*> packaged_data,\n                    const Scalar<DataVector>& mass_density_cons,\n                    const tnsr::I<DataVector, VolumeDim>& momentum_density,\n                    const Scalar<DataVector>& energy_density,\n                    const Mesh<VolumeDim>& mesh,\n                    const std::array<double, VolumeDim>& element_size,\n                    const OrientationMap<VolumeDim>& orientation_map) const;\n\n  using limit_tags =\n      tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                 NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                 NewtonianEuler::Tags::EnergyDensity>;\n  using limit_argument_tags = tmpl::list<\n      domain::Tags::Mesh<VolumeDim>, domain::Tags::Element<VolumeDim>,\n      domain::Tags::Coordinates<VolumeDim, Frame::ElementLogical>,\n      domain::Tags::SizeOfElement<VolumeDim>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      ::hydro::Tags::EquationOfState<false, 2>>;\n\n  /// \\brief Limits the solution on the element.\n  bool operator()(\n      gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n      gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n      gsl::not_null<Scalar<DataVector>*> energy_density,\n      const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n      const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>&\n          logical_coords,\n      const std::array<double, VolumeDim>& element_size,\n      const Scalar<DataVector>& det_inv_logical_to_inertial_jacobian,\n      const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n      const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                               boost::hash<DirectionalId<VolumeDim>>>&\n          neighbor_data) const;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration) false positive\n  friend bool operator==(const Minmod<LocalDim>& lhs,\n                         const Minmod<LocalDim>& rhs);\n\n  ::Limiters::MinmodType minmod_type_;\n  NewtonianEuler::Limiters::VariablesToLimit vars_to_limit_;\n  double tvb_constant_;\n  bool apply_flattener_;\n  bool disable_for_debugging_;\n  ConservativeVarsMinmod conservative_vars_minmod_;\n};\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Minmod<VolumeDim>& lhs, const Minmod<VolumeDim>& rhs);\n\n}  // namespace Limiters\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/VariablesToLimit.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Limiters/VariablesToLimit.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nstd::ostream& NewtonianEuler::Limiters::operator<<(\n    std::ostream& os,\n    const NewtonianEuler::Limiters::VariablesToLimit vars_to_limit) {\n  switch (vars_to_limit) {\n    case NewtonianEuler::Limiters::VariablesToLimit::Conserved:\n      return os << \"Conserved\";\n    case NewtonianEuler::Limiters::VariablesToLimit::Characteristic:\n      return os << \"Characteristic\";\n    case NewtonianEuler::Limiters::VariablesToLimit::NumericalCharacteristic:\n      return os << \"NumericalCharacteristic\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Missing a case for operator<<(VariablesToLimit)\");\n      // LCOV_EXCL_STOP\n  }\n}\n\ntemplate <>\nNewtonianEuler::Limiters::VariablesToLimit\nOptions::create_from_yaml<NewtonianEuler::Limiters::VariablesToLimit>::create<\n    void>(const Options::Option& options) {\n  const auto vars_to_limit_read_type = options.parse_as<std::string>();\n  if (vars_to_limit_read_type == \"Conserved\") {\n    return NewtonianEuler::Limiters::VariablesToLimit::Conserved;\n  } else if (vars_to_limit_read_type == \"Characteristic\") {\n    return NewtonianEuler::Limiters::VariablesToLimit::Characteristic;\n  } else if (vars_to_limit_read_type == \"NumericalCharacteristic\") {\n    return NewtonianEuler::Limiters::VariablesToLimit::NumericalCharacteristic;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << vars_to_limit_read_type\n                  << \"\\\" to VariablesToLimit. Expected one of: \"\n                     \"{Conserved, Characteristic, NumericalCharacteristic}.\");\n}\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/VariablesToLimit.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace NewtonianEuler {\nnamespace Limiters {\n/// \\ingroup LimitersGroup\n/// \\brief Type of NewtonianEuler variables to apply limiter to\n///\n/// \\note The option `Characteristic` denotes the characteristic fields computed\n/// from the analytic expression, whereas the option `NumericalCharacteristic`\n/// denotes the fields as computed numerically by solving for the eigenvectors\n/// of the flux Jacobian.\n///\n/// Initial experiments with limiting in a Reimann problem by FH suggest the\n/// numerical eigenvectors can sometimes produce more accurate results than the\n/// analytic ones (does the numerical solution give a better linear combination\n/// of the degenerate eigenvectors?), and is not too much more expensive\n/// (probably the expense of the limiter as a whole dominates). More testing is\n/// needed to verify and understand this...\nenum class VariablesToLimit {\n  Conserved,\n  Characteristic,\n  NumericalCharacteristic\n};\n\nstd::ostream& operator<<(std::ostream& os, VariablesToLimit vars_to_limit);\n}  // namespace Limiters\n}  // namespace NewtonianEuler\n\ntemplate <>\nstruct Options::create_from_yaml<NewtonianEuler::Limiters::VariablesToLimit> {\n  template <typename Metavariables>\n  static NewtonianEuler::Limiters::VariablesToLimit create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nNewtonianEuler::Limiters::VariablesToLimit\nOptions::create_from_yaml<NewtonianEuler::Limiters::VariablesToLimit>::create<\n    void>(const Options::Option& options);\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/Weno.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Weno.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/SimpleWenoImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Weno.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/CharacteristicHelpers.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Flattener.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/KxrcfTci.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/VariablesToLimit.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Weno.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nbool characteristic_simple_weno_impl(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const double tvb_constant, const double neighbor_linear_weight,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename NewtonianEuler::Limiters::Weno<VolumeDim>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data,\n    const bool compute_char_transformation_numerically) {\n  // Storage for transforming neighbor_data into char variables\n  using CharacteristicVarsWeno =\n      Limiters::Weno<VolumeDim,\n                     tmpl::list<NewtonianEuler::Tags::VMinus,\n                                NewtonianEuler::Tags::VMomentum<VolumeDim>,\n                                NewtonianEuler::Tags::VPlus>>;\n  std::unordered_map<DirectionalId<VolumeDim>,\n                     typename CharacteristicVarsWeno::PackagedData,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_char_data{};\n  for (const auto& [key, data] : neighbor_data) {\n    neighbor_char_data[key].volume_data.initialize(\n        mesh.number_of_grid_points());\n    neighbor_char_data[key].mesh = data.mesh;\n    neighbor_char_data[key].element_size = data.element_size;\n  }\n\n  // Buffers for TCI\n  Limiters::Minmod_detail::BufferWrapper<VolumeDim> tci_buffer(mesh);\n  const auto effective_neighbor_sizes =\n      Limiters::Minmod_detail::compute_effective_neighbor_sizes(element,\n                                                                neighbor_data);\n\n  // Buffers for SimpleWeno extrapolated poly\n  std::unordered_map<DirectionalId<VolumeDim>, intrp::RegularGrid<VolumeDim>,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      interpolator_buffer{};\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      modified_neighbor_solution_buffer{};\n\n  // Outer lambda: wraps applying SimpleWeno to the NewtonianEuler\n  // characteristics for one particular choice of characteristic decomposition\n  const auto simple_weno_convert_neighbor_data_then_limit =\n      [&tci_buffer, &interpolator_buffer, &modified_neighbor_solution_buffer,\n       &tvb_constant, &neighbor_linear_weight, &mesh, &element, &element_size,\n       &neighbor_data, &neighbor_char_data, &effective_neighbor_sizes](\n          const gsl::not_null<Scalar<DataVector>*> char_v_minus,\n          const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> char_v_momentum,\n          const gsl::not_null<Scalar<DataVector>*> char_v_plus,\n          const Matrix& left_eigenvectors) -> bool {\n    // Convert neighbor data to characteristics\n    for (const auto& [key, data] : neighbor_data) {\n      NewtonianEuler::Limiters::characteristic_fields(\n          make_not_null(&(neighbor_char_data[key].volume_data)),\n          data.volume_data, left_eigenvectors);\n      NewtonianEuler::Limiters::characteristic_fields(\n          make_not_null(&(neighbor_char_data[key].means)), data.means,\n          left_eigenvectors);\n    }\n\n    bool some_component_was_limited_with_this_normal = false;\n\n    // Inner lambda: apply SimpleWeno to one particular tensor\n    const auto wrap_minmod_tci_and_simple_weno =\n        [&some_component_was_limited_with_this_normal, &tci_buffer,\n         &interpolator_buffer, &modified_neighbor_solution_buffer,\n         &tvb_constant, &neighbor_linear_weight, &mesh, &element, &element_size,\n         &neighbor_char_data,\n         &effective_neighbor_sizes](auto tag, const auto tensor) {\n          for (size_t tensor_storage_index = 0;\n               tensor_storage_index < tensor->size(); ++tensor_storage_index) {\n            // Check TCI\n            const auto effective_neighbor_means =\n                Limiters::Minmod_detail::compute_effective_neighbor_means<\n                    decltype(tag)>(tensor_storage_index, element,\n                                   neighbor_char_data);\n            const bool component_needs_limiting =\n                Limiters::Tci::tvb_minmod_indicator(\n                    make_not_null(&tci_buffer), tvb_constant,\n                    (*tensor)[tensor_storage_index], mesh, element,\n                    element_size, effective_neighbor_means,\n                    effective_neighbor_sizes);\n\n            if (component_needs_limiting) {\n              if (modified_neighbor_solution_buffer.empty()) {\n                // Allocate the neighbor solution buffers only if the limiter is\n                // triggered. This reduces allocation when no limiting occurs.\n                for (const auto& [neighbor, data] : neighbor_char_data) {\n                  (void)data;\n                  modified_neighbor_solution_buffer.insert(std::make_pair(\n                      neighbor, DataVector(mesh.number_of_grid_points())));\n                }\n              }\n              Limiters::Weno_detail::simple_weno_impl<decltype(tag)>(\n                  make_not_null(&interpolator_buffer),\n                  make_not_null(&modified_neighbor_solution_buffer), tensor,\n                  neighbor_linear_weight, tensor_storage_index, mesh, element,\n                  neighbor_char_data);\n              some_component_was_limited_with_this_normal = true;\n            }\n          }\n        };\n    wrap_minmod_tci_and_simple_weno(NewtonianEuler::Tags::VMinus{},\n                                    char_v_minus);\n    wrap_minmod_tci_and_simple_weno(\n        NewtonianEuler::Tags::VMomentum<VolumeDim>{}, char_v_momentum);\n    wrap_minmod_tci_and_simple_weno(NewtonianEuler::Tags::VPlus{}, char_v_plus);\n    return some_component_was_limited_with_this_normal;\n  };\n\n  return NewtonianEuler::Limiters::\n      apply_limiter_to_characteristic_fields_in_all_directions(\n          mass_density_cons, momentum_density, energy_density, mesh,\n          equation_of_state, simple_weno_convert_neighbor_data_then_limit,\n          compute_char_transformation_numerically);\n}\n\ntemplate <size_t VolumeDim>\nbool characteristic_hweno_impl(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const double kxrcf_constant, const double neighbor_linear_weight,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const Scalar<DataVector>& det_logical_to_inertial_jacobian,\n    const typename evolution::dg::Tags::NormalCovectorAndMagnitude<\n        VolumeDim>::type& normals_and_magnitudes,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename NewtonianEuler::Limiters::Weno<VolumeDim>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data,\n    const bool compute_char_transformation_numerically) {\n  // Hweno checks TCI before limiting any tensors\n  const bool cell_is_troubled = NewtonianEuler::Limiters::Tci::kxrcf_indicator(\n      kxrcf_constant, *mass_density_cons, *momentum_density, *energy_density,\n      mesh, element, element_size, det_logical_to_inertial_jacobian,\n      normals_and_magnitudes, neighbor_data);\n  if (not cell_is_troubled) {\n    // No limiting is needed\n    return false;\n  }\n\n  // Storage for transforming neighbor_data into char variables\n  using CharacteristicVarsWeno =\n      Limiters::Weno<VolumeDim,\n                     tmpl::list<NewtonianEuler::Tags::VMinus,\n                                NewtonianEuler::Tags::VMomentum<VolumeDim>,\n                                NewtonianEuler::Tags::VPlus>>;\n  std::unordered_map<DirectionalId<VolumeDim>,\n                     typename CharacteristicVarsWeno::PackagedData,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_char_data{};\n  for (const auto& [key, data] : neighbor_data) {\n    neighbor_char_data[key].volume_data.initialize(\n        mesh.number_of_grid_points());\n    neighbor_char_data[key].mesh = data.mesh;\n    neighbor_char_data[key].element_size = data.element_size;\n  }\n\n  // Buffers for Hweno extrapolated poly\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      modified_neighbor_solution_buffer{};\n  for (const auto& [neighbor, data] : neighbor_char_data) {\n    (void)data;\n    modified_neighbor_solution_buffer.insert(\n        std::make_pair(neighbor, DataVector(mesh.number_of_grid_points())));\n  }\n\n  // Lambda wraps applying Hweno to the NewtonianEuler characteristics for one\n  // particular choice of characteristic decomposition\n  const auto hweno_convert_neighbor_data_then_limit =\n      [&modified_neighbor_solution_buffer, &neighbor_linear_weight, &mesh,\n       &element, &neighbor_data, &neighbor_char_data](\n          const gsl::not_null<Scalar<DataVector>*> char_v_minus,\n          const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> char_v_momentum,\n          const gsl::not_null<Scalar<DataVector>*> char_v_plus,\n          const Matrix& left_eigenvectors) -> bool {\n    // Convert neighbor data to characteristics\n    for (const auto& [key, data] : neighbor_data) {\n      NewtonianEuler::Limiters::characteristic_fields(\n          make_not_null(&(neighbor_char_data[key].volume_data)),\n          data.volume_data, left_eigenvectors);\n      NewtonianEuler::Limiters::characteristic_fields(\n          make_not_null(&(neighbor_char_data[key].means)), data.means,\n          left_eigenvectors);\n    }\n\n    ::Limiters::Weno_detail::hweno_impl<NewtonianEuler::Tags::VMinus>(\n        make_not_null(&modified_neighbor_solution_buffer), char_v_minus,\n        neighbor_linear_weight, mesh, element, neighbor_char_data);\n    ::Limiters::Weno_detail::hweno_impl<\n        NewtonianEuler::Tags::VMomentum<VolumeDim>>(\n        make_not_null(&modified_neighbor_solution_buffer), char_v_momentum,\n        neighbor_linear_weight, mesh, element, neighbor_char_data);\n    ::Limiters::Weno_detail::hweno_impl<NewtonianEuler::Tags::VPlus>(\n        make_not_null(&modified_neighbor_solution_buffer), char_v_plus,\n        neighbor_linear_weight, mesh, element, neighbor_char_data);\n    return true;  // all components were limited\n  };\n\n  NewtonianEuler::Limiters::\n      apply_limiter_to_characteristic_fields_in_all_directions(\n          mass_density_cons, momentum_density, energy_density, mesh,\n          equation_of_state, hweno_convert_neighbor_data_then_limit,\n          compute_char_transformation_numerically);\n  return true;  // all components were limited\n}\n\ntemplate <size_t VolumeDim>\nbool conservative_hweno_impl(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const double kxrcf_constant, const double neighbor_linear_weight,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const Scalar<DataVector>& det_logical_to_inertial_jacobian,\n    const typename evolution::dg::Tags::NormalCovectorAndMagnitude<\n        VolumeDim>::type& normals_and_magnitudes,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename NewtonianEuler::Limiters::Weno<VolumeDim>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data) {\n  // Hweno checks TCI before limiting any tensors\n  const bool cell_is_troubled = NewtonianEuler::Limiters::Tci::kxrcf_indicator(\n      kxrcf_constant, *mass_density_cons, *momentum_density, *energy_density,\n      mesh, element, element_size, det_logical_to_inertial_jacobian,\n      normals_and_magnitudes, neighbor_data);\n  if (not cell_is_troubled) {\n    // No limiting is needed\n    return false;\n  }\n\n  // Buffers for Hweno extrapolated poly\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      modified_neighbor_solution_buffer{};\n  for (const auto& [neighbor, data] : neighbor_data) {\n    (void)data;\n    modified_neighbor_solution_buffer.insert(\n        std::make_pair(neighbor, DataVector(mesh.number_of_grid_points())));\n  }\n\n  ::Limiters::Weno_detail::hweno_impl<NewtonianEuler::Tags::MassDensityCons>(\n      make_not_null(&modified_neighbor_solution_buffer), mass_density_cons,\n      neighbor_linear_weight, mesh, element, neighbor_data);\n  ::Limiters::Weno_detail::hweno_impl<\n      NewtonianEuler::Tags::MomentumDensity<VolumeDim>>(\n      make_not_null(&modified_neighbor_solution_buffer), momentum_density,\n      neighbor_linear_weight, mesh, element, neighbor_data);\n  ::Limiters::Weno_detail::hweno_impl<NewtonianEuler::Tags::EnergyDensity>(\n      make_not_null(&modified_neighbor_solution_buffer), energy_density,\n      neighbor_linear_weight, mesh, element, neighbor_data);\n  return true;  // all components were limited\n}\n\n}  // namespace\n\nnamespace NewtonianEuler::Limiters {\n\ntemplate <size_t VolumeDim>\nWeno<VolumeDim>::Weno(\n    const ::Limiters::WenoType weno_type,\n    const NewtonianEuler::Limiters::VariablesToLimit vars_to_limit,\n    const double neighbor_linear_weight,\n    const std::optional<double> tvb_constant,\n    const std::optional<double> kxrcf_constant, const bool apply_flattener,\n    const bool disable_for_debugging, const Options::Context& context)\n    : weno_type_(weno_type),\n      vars_to_limit_(vars_to_limit),\n      neighbor_linear_weight_(neighbor_linear_weight),\n      tvb_constant_(tvb_constant),\n      kxrcf_constant_(kxrcf_constant),\n      apply_flattener_(apply_flattener),\n      disable_for_debugging_(disable_for_debugging),\n      conservative_vars_weno_(\n          weno_type_, neighbor_linear_weight_,\n          tvb_constant_.value_or(std::numeric_limits<double>::signaling_NaN()),\n          disable_for_debugging_) {\n  if (weno_type == ::Limiters::WenoType::Hweno) {\n    if (tvb_constant.has_value() or not kxrcf_constant.has_value()) {\n      PARSE_ERROR(context,\n                  \"The Hweno limiter uses the KXRCF TCI. The TvbConstant must \"\n                  \"be set to 'None', and the KxrcfConstant must be set to a \"\n                  \"non-negative value.\");\n    }\n    if (kxrcf_constant.value() < 0.0) {\n      PARSE_ERROR(context, \"The KXRCF constant must be non-negative, but got: \"\n                               << kxrcf_constant.value());\n    }\n  } else {  // SimpleWeno\n    if (not tvb_constant.has_value() or kxrcf_constant.has_value()) {\n      PARSE_ERROR(context,\n                  \"The SimpleWeno limiter uses the TVB minmod TCI. The \"\n                  \"TvbConstant must be set to a non-negative value, and the \"\n                  \"KxrcfConstant must be set to 'None'.\");\n    }\n    if (tvb_constant.value() < 0.0) {\n      PARSE_ERROR(context, \"The TVB constant must be non-negative, but got: \"\n                               << tvb_constant.value());\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid Weno<VolumeDim>::pup(PUP::er& p) {\n  p | weno_type_;\n  p | vars_to_limit_;\n  p | neighbor_linear_weight_;\n  p | tvb_constant_;\n  p | kxrcf_constant_;\n  p | apply_flattener_;\n  p | disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim>\nvoid Weno<VolumeDim>::package_data(\n    const gsl::not_null<PackagedData*> packaged_data,\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, VolumeDim>& momentum_density,\n    const Scalar<DataVector>& energy_density, const Mesh<VolumeDim>& mesh,\n    const std::array<double, VolumeDim>& element_size,\n    const OrientationMap<VolumeDim>& orientation_map) const {\n  conservative_vars_weno_.package_data(packaged_data, mass_density_cons,\n                                       momentum_density, energy_density, mesh,\n                                       element_size, orientation_map);\n}\n\ntemplate <size_t VolumeDim>\nbool Weno<VolumeDim>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> energy_density,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const Scalar<DataVector>& det_inv_logical_to_inertial_jacobian,\n    const typename evolution::dg::Tags::NormalCovectorAndMagnitude<\n        VolumeDim>::type& normals_and_magnitudes,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n    const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data) const {\n  if (UNLIKELY(disable_for_debugging_)) {\n    // Do not modify input tensors\n    return false;\n  }\n\n  // Enforce restrictions on h-refinement, p-refinement\n  if (UNLIKELY(\n          alg::any_of(element.neighbors(), [](const auto& direction_neighbors) {\n            return direction_neighbors.second.size() != 1;\n          }))) {\n    ERROR(\"The Weno limiter does not yet support h-refinement\");\n    // Removing this limitation will require:\n    // - Generalizing the computation of the modified neighbor solutions.\n    // - Generalizing the WENO weighted sum for multiple neighbors in each\n    //   direction.\n  }\n  alg::for_each(neighbor_data, [&mesh](const auto& neighbor_and_data) {\n    if (UNLIKELY(neighbor_and_data.second.mesh != mesh)) {\n      ERROR(\"The Weno limiter does not yet support p-refinement\");\n      // Removing this limitation will require generalizing the\n      // computation of the modified neighbor solutions.\n    }\n  });\n\n  // Checks for the post-timestep, pre-limiter NewtonianEuler state:\n#ifdef SPECTRE_DEBUG\n  const double mean_density = mean_value(get(*mass_density_cons), mesh);\n  ASSERT(mean_density > 0.0,\n         \"Positivity was violated on a cell-average level.\");\n  const double mean_energy = mean_value(get(*energy_density), mesh);\n  ASSERT(mean_energy > 0.0, \"Positivity was violated on a cell-average level.\");\n#endif  // SPECTRE_DEBUG\n\n  bool limiter_activated = false;\n\n  // Small possible optimization: only compute this if needed\n  const Scalar<DataVector> det_logical_to_inertial_jacobian{\n      1. / get(det_inv_logical_to_inertial_jacobian)};\n\n  if (weno_type_ == ::Limiters::WenoType::Hweno) {\n    if (vars_to_limit_ ==\n            NewtonianEuler::Limiters::VariablesToLimit::Characteristic or\n        vars_to_limit_ == NewtonianEuler::Limiters::VariablesToLimit::\n                              NumericalCharacteristic) {\n      const bool compute_char_transformation_numerically =\n          (vars_to_limit_ ==\n           NewtonianEuler::Limiters::VariablesToLimit::NumericalCharacteristic);\n      // impl function handles specialized TCI + char transform\n      limiter_activated = characteristic_hweno_impl(\n          mass_density_cons, momentum_density, energy_density,\n          kxrcf_constant_.value(), neighbor_linear_weight_, mesh, element,\n          element_size, det_logical_to_inertial_jacobian,\n          normals_and_magnitudes, equation_of_state, neighbor_data,\n          compute_char_transformation_numerically);\n    } else {\n      // impl function handles specialized TCI\n      limiter_activated = conservative_hweno_impl(\n          mass_density_cons, momentum_density, energy_density,\n          kxrcf_constant_.value(), neighbor_linear_weight_, mesh, element,\n          element_size, det_logical_to_inertial_jacobian,\n          normals_and_magnitudes, neighbor_data);\n    }\n  } else if (weno_type_ == ::Limiters::WenoType::SimpleWeno) {\n    if (vars_to_limit_ ==\n            NewtonianEuler::Limiters::VariablesToLimit::Characteristic or\n        vars_to_limit_ == NewtonianEuler::Limiters::VariablesToLimit::\n                              NumericalCharacteristic) {\n      const bool compute_char_transformation_numerically =\n          (vars_to_limit_ ==\n           NewtonianEuler::Limiters::VariablesToLimit::NumericalCharacteristic);\n      // impl function handles char transform\n      limiter_activated = characteristic_simple_weno_impl(\n          mass_density_cons, momentum_density, energy_density,\n          tvb_constant_.value(), neighbor_linear_weight_, mesh, element,\n          element_size, equation_of_state, neighbor_data,\n          compute_char_transformation_numerically);\n    } else {\n      // Fall back to generic SimpleWeno\n      limiter_activated = conservative_vars_weno_(\n          mass_density_cons, momentum_density, energy_density, mesh, element,\n          element_size, neighbor_data);\n    }\n  }\n\n  if (apply_flattener_) {\n    const auto flattener_action = flatten_solution(\n        mass_density_cons, momentum_density, energy_density, mesh,\n        det_logical_to_inertial_jacobian, equation_of_state);\n    if (flattener_action != FlattenerAction::NoOp) {\n      limiter_activated = true;\n    }\n  }\n\n  // Checks for the post-limiter NewtonianEuler state:\n#ifdef SPECTRE_DEBUG\n  ASSERT(min(get(*mass_density_cons)) > 0.0, \"Bad density after limiting.\");\n  const auto specific_internal_energy = Scalar<DataVector>{\n      get(*energy_density) / get(*mass_density_cons) -\n      0.5 * get(dot_product(*momentum_density, *momentum_density)) /\n          square(get(*mass_density_cons))};\n  const auto pressure = equation_of_state.pressure_from_density_and_energy(\n      *mass_density_cons, specific_internal_energy);\n  ASSERT(min(get(pressure)) > 0.0, \"Bad energy after limiting.\");\n#endif  // SPECTRE_DEBUG\n\n  return limiter_activated;\n}\n\ntemplate <size_t LocalDim>\nbool operator==(const Weno<LocalDim>& lhs, const Weno<LocalDim>& rhs) {\n  // No need to compare the conservative_vars_weno_ member variable because\n  // it is constructed from the other member variables.\n  return lhs.weno_type_ == rhs.weno_type_ and\n         lhs.vars_to_limit_ == rhs.vars_to_limit_ and\n         lhs.neighbor_linear_weight_ == rhs.neighbor_linear_weight_ and\n         lhs.tvb_constant_ == rhs.tvb_constant_ and\n         lhs.kxrcf_constant_ == rhs.kxrcf_constant_ and\n         lhs.apply_flattener_ == rhs.apply_flattener_ and\n         lhs.disable_for_debugging_ == rhs.disable_for_debugging_;\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Weno<VolumeDim>& lhs, const Weno<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template class Weno<DIM(data)>;                                           \\\n  template bool operator==(const Weno<DIM(data)>&, const Weno<DIM(data)>&); \\\n  template bool operator!=(const Weno<DIM(data)>&, const Weno<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace NewtonianEuler::Limiters\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Limiters/Weno.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Weno.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/VariablesToLimit.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Direction;\ntemplate <size_t VolumeDim>\nclass Element;\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass Mesh;\ntemplate <size_t VolumeDim>\nclass OrientationMap;\n\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\n\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler {\nnamespace Limiters {\n\n/// \\ingroup LimitersGroup\n/// \\brief A compact-stencil WENO limiter for the NewtonianEuler system.\n///\n/// Implements the simple WENO limiter of \\cite Zhong2013 and the Hermite WENO\n/// (HWENO) limiter of \\cite Zhu2016. See the documentation of the\n/// system-agnostic ::Limiters::Weno limiter for a general discussion of the\n/// algorithm and the various options that control the action of the limiter.\n//\n/// This implemention is specialized to the NewtonianEuler evolution system.\n/// By specializing the limiter to the system, we can add a few features that\n/// improve its robustness:\n/// - the troubled-cell indicator (TCI) can be specialized to the features of\n///   the evolution system.\n/// - the limiter can be applied to the system's characteristic variables. This\n///   is the recommendation of the reference, because it reduces spurious\n///   oscillations in the post-limiter solution.\n/// - after limiting, the solution can be processed to remove any remaining\n///   unphysical values like negative densities and pressures. We do this by\n///   scaling the solution around its mean (a \"flattener\" or \"bounds-preserving\"\n///   filter). Note: the flattener is applied to all elements, including those\n///   where the limiter did not act to reduce the solution's slopes.\n///\n/// The matrix of TCI, variables to limit, post-processing, etc. choices can\n/// rapidly grow large. Here we reduce the possibilities by tying the TCI to the\n/// limiter in keeping with each limiter's main reference: HWENO uses the KXRCF\n/// TCI and simple WENO uses the TVB TCI. To fully explore the matrix of\n/// possibilities, the source code could be generalized --- however, experience\n/// suggests it is unlikely that there exists one combination that will perform\n/// remarkably better than the others.\ntemplate <size_t VolumeDim>\nclass Weno {\n public:\n  using ConservativeVarsWeno = ::Limiters::Weno<\n      VolumeDim, tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                            NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                            NewtonianEuler::Tags::EnergyDensity>>;\n\n  struct VariablesToLimit {\n    using type = NewtonianEuler::Limiters::VariablesToLimit;\n    static type suggested_value() {\n      return NewtonianEuler::Limiters::VariablesToLimit::Characteristic;\n    }\n    static constexpr Options::String help = {\n        \"Variable representation on which to apply the limiter\"};\n  };\n  // Future design improvement: attach the TvbConstant/KxrcfConstant to the\n  // limiter type, so that it isn't necessary to specify both (but with one\n  // required to be 'None') in each input file.\n  struct TvbConstant {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"Constant in RHS of the TVB minmod TCI, used when Type = SimpleWeno\"};\n  };\n  struct KxrcfConstant {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"Constant in RHS of KXRCF TCI, used when Type = Hweno\"};\n  };\n  struct ApplyFlattener {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Flatten after limiting to restore pointwise positivity\"};\n  };\n  using options =\n      tmpl::list<typename ConservativeVarsWeno::Type, VariablesToLimit,\n                 typename ConservativeVarsWeno::NeighborWeight, TvbConstant,\n                 KxrcfConstant, ApplyFlattener,\n                 typename ConservativeVarsWeno::DisableForDebugging>;\n  static constexpr Options::String help = {\n      \"A WENO limiter specialized to the NewtonianEuler system\"};\n  static std::string name() { return \"NewtonianEulerWeno\"; };\n\n  Weno(::Limiters::WenoType weno_type,\n       NewtonianEuler::Limiters::VariablesToLimit vars_to_limit,\n       double neighbor_linear_weight, std::optional<double> tvb_constant,\n       std::optional<double> kxrcf_constant, bool apply_flattener,\n       bool disable_for_debugging = false,\n       const Options::Context& context = {});\n\n  Weno() = default;\n  Weno(const Weno& /*rhs*/) = default;\n  Weno& operator=(const Weno& /*rhs*/) = default;\n  Weno(Weno&& /*rhs*/) = default;\n  Weno& operator=(Weno&& /*rhs*/) = default;\n  ~Weno() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  using PackagedData = typename ConservativeVarsWeno::PackagedData;\n  using package_argument_tags =\n      typename ConservativeVarsWeno::package_argument_tags;\n\n  /// \\brief Package data for sending to neighbor elements\n  void package_data(gsl::not_null<PackagedData*> packaged_data,\n                    const Scalar<DataVector>& mass_density_cons,\n                    const tnsr::I<DataVector, VolumeDim>& momentum_density,\n                    const Scalar<DataVector>& energy_density,\n                    const Mesh<VolumeDim>& mesh,\n                    const std::array<double, VolumeDim>& element_size,\n                    const OrientationMap<VolumeDim>& orientation_map) const;\n\n  using limit_tags =\n      tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                 NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                 NewtonianEuler::Tags::EnergyDensity>;\n  using limit_argument_tags = tmpl::list<\n      domain::Tags::Mesh<VolumeDim>, domain::Tags::Element<VolumeDim>,\n      domain::Tags::SizeOfElement<VolumeDim>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<VolumeDim>,\n      ::hydro::Tags::EquationOfState<false, 2>>;\n\n  /// \\brief Limit the solution on the element\n  bool operator()(\n      gsl::not_null<Scalar<DataVector>*> mass_density_cons,\n      gsl::not_null<tnsr::I<DataVector, VolumeDim>*> momentum_density,\n      gsl::not_null<Scalar<DataVector>*> energy_density,\n      const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n      const std::array<double, VolumeDim>& element_size,\n      const Scalar<DataVector>& det_inv_logical_to_inertial_jacobian,\n      const typename evolution::dg::Tags::NormalCovectorAndMagnitude<\n          VolumeDim>::type& normals_and_magnitudes,\n      const EquationsOfState::EquationOfState<false, 2>& equation_of_state,\n      const std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                               boost::hash<DirectionalId<VolumeDim>>>&\n          neighbor_data) const;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration) false positive\n  friend bool operator==(const Weno<LocalDim>& lhs, const Weno<LocalDim>& rhs);\n\n  ::Limiters::WenoType weno_type_;\n  NewtonianEuler::Limiters::VariablesToLimit vars_to_limit_;\n  double neighbor_linear_weight_;\n  std::optional<double> tvb_constant_;\n  std::optional<double> kxrcf_constant_;\n  bool apply_flattener_;\n  bool disable_for_debugging_;\n  // Note: conservative_vars_weno_ is always used for calls to package_data, and\n  // is also used when limiting cons vars with the simple WENO algorithm. So we\n  // construct conservative_vars_weno_ with the correct TVB constant when it is\n  // used for limiting (precisely, when weno_type_ == SimpleWeno), and with a\n  // dummy TVB constant value of NaN otherwise (weno_type_ == Hweno). This lets\n  // us construct the conservative_vars_weno_ variable and use it for delegating\n  // the package_data work even when the specialized limiter has no TVB constant\n  // and is possible because package_data doesn't depend on the TCI.\n  ConservativeVarsWeno conservative_vars_weno_;\n};\n\ntemplate <size_t VolumeDim>\nbool operator!=(const Weno<VolumeDim>& lhs, const Weno<VolumeDim>& rhs);\n\n}  // namespace Limiters\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/MachNumber.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/MachNumber.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace NewtonianEuler {\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid mach_number(const gsl::not_null<Scalar<DataType>*> result,\n                 const tnsr::I<DataType, Dim, Fr>& velocity,\n                 const Scalar<DataType>& sound_speed) {\n  get(*result) = get(magnitude(velocity)) / get(sound_speed);\n}\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> mach_number(const tnsr::I<DataType, Dim, Fr>& velocity,\n                             const Scalar<DataType>& sound_speed) {\n  Scalar<DataType> result{};\n  mach_number(make_not_null(&result), velocity, sound_speed);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void mach_number(const gsl::not_null<Scalar<DTYPE(data)>*> result, \\\n                            const tnsr::I<DTYPE(data), DIM(data)>& velocity,  \\\n                            const Scalar<DTYPE(data)>& sound_speed);          \\\n  template Scalar<DTYPE(data)> mach_number(                                   \\\n      const tnsr::I<DTYPE(data), DIM(data)>& velocity,                        \\\n      const Scalar<DTYPE(data)>& sound_speed);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector), (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n#undef DTYPE\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/MachNumber.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler {\n/// @{\n/*!\n * Compute the local Mach number, \\f$\\text{Ma} = v/c_s\\f$,\n * where \\f$v\\f$ is the magnitude of the velocity, and\n * \\f$c_s\\f$ is the sound speed.\n */\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid mach_number(gsl::not_null<Scalar<DataType>*> result,\n                 const tnsr::I<DataType, Dim, Fr>& velocity,\n                 const Scalar<DataType>& sound_speed);\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> mach_number(const tnsr::I<DataType, Dim, Fr>& velocity,\n                             const Scalar<DataType>& sound_speed);\n/// @}\n\nnamespace Tags {\n/// Compute item for the local Mach number, \\f$\\text{Ma}\\f$.\n/// \\see NewtonianEuler::mach_number\n///\n/// Can be retrieved using `NewtonianEuler::Tags::MachNumber`\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct MachNumberCompute : MachNumber<DataType>, db::ComputeTag {\n  using base = MachNumber<DataType>;\n\n  using argument_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim, Fr>,\n                 SoundSpeed<DataType>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<Scalar<DataType>*>, const tnsr::I<DataType, Dim, Fr>&,\n      const Scalar<DataType>&)>(&mach_number<DataType, Dim, Fr>);\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler {\n\ntemplate <size_t Dim>\ntemplate <size_t ThermodynamicDim>\nvoid PrimitiveFromConservative<Dim>::apply(\n    const gsl::not_null<Scalar<DataVector>*> mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> velocity,\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n    const gsl::not_null<Scalar<DataVector>*> pressure,\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, Dim>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n    const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n        equation_of_state) {\n  get(*mass_density) = get(mass_density_cons);\n\n  // store inverse mass density here in order to save allocation\n  get(*specific_internal_energy) = 1.0 / get(mass_density_cons);\n  for (size_t i = 0; i < Dim; ++i) {\n    velocity->get(i) = momentum_density.get(i) * get(*specific_internal_energy);\n  }\n\n  get(*specific_internal_energy) *= get(energy_density);\n  get(*specific_internal_energy) -=\n      (0.5 * get(dot_product(*velocity, *velocity)));\n\n  if constexpr (ThermodynamicDim == 1) {\n    *pressure = equation_of_state.pressure_from_density(mass_density_cons);\n  } else if constexpr (ThermodynamicDim == 2) {\n    *pressure = equation_of_state.pressure_from_density_and_energy(\n        mass_density_cons, *specific_internal_energy);\n  }\n}\n\n}  // namespace NewtonianEuler\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) \\\n  template struct NewtonianEuler::PrimitiveFromConservative<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(_, data)                                               \\\n  template void NewtonianEuler::PrimitiveFromConservative<DIM(data)>::apply< \\\n      THERMO_DIM(data)>(                                                     \\\n      const gsl::not_null<Scalar<DataVector>*> mass_density,                 \\\n      const gsl::not_null<tnsr::I<DataVector, DIM(data)>*> velocity,         \\\n      const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,     \\\n      const gsl::not_null<Scalar<DataVector>*> pressure,                     \\\n      const Scalar<DataVector>& mass_density_cons,                           \\\n      const tnsr::I<DataVector, DIM(data)>& momentum_density,                \\\n      const Scalar<DataVector>& energy_density,                              \\\n      const EquationsOfState::EquationOfState<false, THERMO_DIM(data)>&      \\\n          equation_of_state);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (1, 2))\n\n#undef INSTANTIATION\n#undef THERMO_DIM\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace NewtonianEuler {\n\n/*!\n * \\brief Compute the primitive variables from the conservative variables.\n *\n * \\f{align*}\n * v^i &= \\frac{S^i}{\\rho} \\\\\n * \\epsilon &= \\frac{e}{\\rho} - \\frac{1}{2}\\frac{S^2}{\\rho^2}\n * \\f}\n *\n * where \\f$v^i\\f$ is the velocity, \\f$\\epsilon\\f$ is the specific\n * internal energy, \\f$e\\f$ is the energy density, \\f$\\rho\\f$\n * is the mass density, \\f$S^i\\f$ is the momentum density, and\n * \\f$S^2\\f$ is the momentum density squared.\n *\n * This routine also returns the mass density as a primitive, and the pressure\n * from a generic equation of state \\f$p = p(\\rho, \\epsilon)\\f$.\n */\ntemplate <size_t Dim>\nstruct PrimitiveFromConservative {\n  using return_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>>;\n\n  using argument_tags =\n      tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<Dim>,\n                 Tags::EnergyDensity, hydro::Tags::EquationOfState<false, 2>>;\n\n  template <size_t ThermodynamicDim>\n  static void apply(\n      gsl::not_null<Scalar<DataVector>*> mass_density,\n      gsl::not_null<tnsr::I<DataVector, Dim>*> velocity,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n          equation_of_state);\n};\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/RamPressure.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/RamPressure.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler {\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid ram_pressure(const gsl::not_null<tnsr::II<DataType, Dim, Fr>*> result,\n                  const Scalar<DataType>& mass_density,\n                  const tnsr::I<DataType, Dim, Fr>& velocity) {\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      result->get(i, j) = get(mass_density) * velocity.get(i) * velocity.get(j);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename Fr>\ntnsr::II<DataType, Dim, Fr> ram_pressure(\n    const Scalar<DataType>& mass_density,\n    const tnsr::I<DataType, Dim, Fr>& velocity) {\n  tnsr::II<DataType, Dim, Fr> result{};\n  ram_pressure(make_not_null(&result), mass_density, velocity);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template void ram_pressure(                                        \\\n      const gsl::not_null<tnsr::II<DTYPE(data), DIM(data)>*> result, \\\n      const Scalar<DTYPE(data)>& mass_density,                       \\\n      const tnsr::I<DTYPE(data), DIM(data)>& velocity);              \\\n  template tnsr::II<DTYPE(data), DIM(data)> ram_pressure(            \\\n      const Scalar<DTYPE(data)>& mass_density,                       \\\n      const tnsr::I<DTYPE(data), DIM(data)>& velocity);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector), (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n#undef DTYPE\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/RamPressure.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler {\n/// @{\n/*!\n * Compute the ram pressure, \\f$\\rho v^i v^j\\f$, where \\f$\\rho\\f$ is the\n * mass density, and \\f$v^i\\f$ is the velocity.\n */\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid ram_pressure(gsl::not_null<tnsr::II<DataType, Dim, Fr>*> result,\n                  const Scalar<DataType>& mass_density,\n                  const tnsr::I<DataType, Dim, Fr>& velocity);\n\ntemplate <typename DataType, size_t Dim, typename Fr>\ntnsr::II<DataType, Dim, Fr> ram_pressure(\n    const Scalar<DataType>& mass_density,\n    const tnsr::I<DataType, Dim, Fr>& velocity);\n/// @}\n\nnamespace Tags {\n/// Compute item for the ram pressure, \\f$\\rho v^i v^j\\f$.\n/// \\see NewtonianEuler::ram_pressure\n///\n/// Can be retrieved using `NewtonianEuler::Tags::RamPressure`\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct RamPressureCompute : RamPressure<DataType, Dim, Fr>, db::ComputeTag {\n  using base = RamPressure<DataType, Dim, Fr>;\n\n  using argument_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim, Fr>>;\n\n  using return_type = tnsr::II<DataType, Dim, Fr>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::II<DataType, Dim, Fr>*>,\n      const Scalar<DataType>&, const tnsr::I<DataType, Dim, Fr>&)>(\n      &ram_pressure<DataType, Dim, Fr>);\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/SoundSpeedSquared.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler {\ntemplate <typename DataType, size_t ThermodynamicDim>\nvoid sound_speed_squared(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n        equation_of_state) {\n  if constexpr (ThermodynamicDim == 1) {\n    get(*result) =\n        get(equation_of_state.chi_from_density(mass_density)) +\n        get(equation_of_state.kappa_times_p_over_rho_squared_from_density(\n            mass_density));\n  } else if constexpr (ThermodynamicDim == 2) {\n    get(*result) =\n        get(equation_of_state.chi_from_density_and_energy(\n            mass_density, specific_internal_energy)) +\n        get(equation_of_state\n                .kappa_times_p_over_rho_squared_from_density_and_energy(\n                    mass_density, specific_internal_energy));\n  }\n}\n\ntemplate <typename DataType, size_t ThermodynamicDim>\nScalar<DataType> sound_speed_squared(\n    const Scalar<DataType>& mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n        equation_of_state) {\n  Scalar<DataType> result{};\n  sound_speed_squared(make_not_null(&result), mass_density,\n                      specific_internal_energy, equation_of_state);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                            \\\n  template void sound_speed_squared(                                    \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result,                 \\\n      const Scalar<DTYPE(data)>& mass_density,                          \\\n      const Scalar<DTYPE(data)>& specific_internal_energy,              \\\n      const EquationsOfState::EquationOfState<false, THERMO_DIM(data)>& \\\n          equation_of_state);                                           \\\n  template Scalar<DTYPE(data)> sound_speed_squared(                     \\\n      const Scalar<DTYPE(data)>& mass_density,                          \\\n      const Scalar<DTYPE(data)>& specific_internal_energy,              \\\n      const EquationsOfState::EquationOfState<false, THERMO_DIM(data)>& \\\n          equation_of_state);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector), (1, 2))\n\n#undef INSTANTIATE\n#undef THERMO_DIM\n#undef DTYPE\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler {\n/// @{\n/*!\n * Compute the Newtonian sound speed squared\n * \\f$c_s^2 = \\chi + p\\kappa / \\rho^2\\f$, where\n * \\f$p\\f$ is the fluid pressure, \\f$\\rho\\f$ is the mass density,\n * \\f$\\chi = (\\partial p/\\partial\\rho)_\\epsilon\\f$ and\n * \\f$\\kappa = (\\partial p/ \\partial \\epsilon)_\\rho\\f$, where\n * \\f$\\epsilon\\f$ is the specific internal energy.\n */\ntemplate <typename DataType, size_t ThermodynamicDim>\nvoid sound_speed_squared(\n    gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n        equation_of_state);\n\ntemplate <typename DataType, size_t ThermodynamicDim>\nScalar<DataType> sound_speed_squared(\n    const Scalar<DataType>& mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n        equation_of_state);\n/// @}\n\nnamespace Tags {\n/// Compute item for the sound speed \\f$c_s\\f$.\n///\n/// Can be retrieved using `NewtonianEuler::Tags::SoundSpeed`\ntemplate <typename DataType>\nstruct SoundSpeedCompute : SoundSpeed<DataType>, db::ComputeTag {\n  using argument_tags = tmpl::list<hydro::Tags::SoundSpeedSquared<DataType>>;\n\n  using return_type = Scalar<DataType>;\n\n  static void function(const gsl::not_null<Scalar<DataType>*> result,\n                       const Scalar<DataType>& sound_speed_squared) {\n    get(*result) = sqrt(get(sound_speed_squared));\n  }\n\n  using base = SoundSpeed<DataType>;\n};\n\n/// Compute item for the sound speed squared \\f$c_s^2\\f$.\n/// \\see NewtonianEuler::sound_speed_squared\n///\n/// Can be retrieved using `NewtonianEuler::Tags::SoundSpeedSquared`\ntemplate <typename DataType>\nstruct SoundSpeedSquaredCompute : hydro::Tags::SoundSpeedSquared<DataType>,\n                                  db::ComputeTag {\n  using argument_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::EquationOfState<false, 2>>;\n\n  using return_type = Scalar<DataType>;\n\n  template <typename EquationOfStateType>\n  static void function(const gsl::not_null<Scalar<DataType>*> result,\n                       const Scalar<DataType>& mass_density,\n                       const Scalar<DataType>& specific_internal_energy,\n                       const EquationOfStateType& equation_of_state) {\n    sound_speed_squared(result, mass_density, specific_internal_energy,\n                        equation_of_state);\n  }\n\n  using base = hydro::Tags::SoundSpeedSquared<DataType>;\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY NewtonianEulerSources)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  LaneEmdenGravitationalField.cpp\n  NoSource.cpp\n  UniformAcceleration.cpp\n  VortexPerturbation.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  LaneEmdenGravitationalField.hpp\n  NoSource.hpp\n  Source.hpp\n  UniformAcceleration.hpp\n  VortexPerturbation.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  NewtonianEulerSolutions\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/NewtonianEuler/Sources/LaneEmdenGravitationalField.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/NoSource.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Source.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/UniformAcceleration.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/VortexPerturbation.hpp\"\n\nnamespace NewtonianEuler::Sources {\n/// All the available source terms.\ntemplate <size_t Dim>\nusing all_sources = tmpl::append<\n    tmpl::conditional_t<\n        Dim == 3, tmpl::list<LaneEmdenGravitationalField, VortexPerturbation>,\n        tmpl::list<>>,\n    tmpl::list<NoSource<Dim>, UniformAcceleration<Dim>>>;\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/LaneEmdenGravitationalField.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Sources/LaneEmdenGravitationalField.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler::Sources {\n\nLaneEmdenGravitationalField::LaneEmdenGravitationalField(\n    const double central_mass_density, const double polytropic_constant)\n    : central_mass_density_(central_mass_density),\n      polytropic_constant_(polytropic_constant) {}\n\nLaneEmdenGravitationalField::LaneEmdenGravitationalField(CkMigrateMessage* msg)\n    : Source{msg} {}\n\nvoid LaneEmdenGravitationalField::pup(PUP::er& p) { Source::pup(p); }\n\nauto LaneEmdenGravitationalField::get_clone() const\n    -> std::unique_ptr<Source<3>> {\n  return std::make_unique<LaneEmdenGravitationalField>(*this);\n}\n\nvoid LaneEmdenGravitationalField::operator()(\n    const gsl::not_null<Scalar<DataVector>*> /*source_mass_density_cons*/,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> source_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> source_energy_density,\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, 3>& momentum_density,\n    const Scalar<DataVector>& /*energy_density*/,\n    const tnsr::I<DataVector, 3>& /*velocity*/,\n    const Scalar<DataVector>& /*pressure*/,\n    const Scalar<DataVector>& /*specific_internal_energy*/,\n    const EquationsOfState::EquationOfState<false, 2>& /*eos*/,\n    const tnsr::I<DataVector, 3>& coords, const double /*time*/) const {\n  const auto gravitational_field = this->gravitational_field(coords);\n  for (size_t i = 0; i < 3; ++i) {\n    source_momentum_density->get(i) +=\n        get(mass_density_cons) * gravitational_field.get(i);\n  }\n  get(*source_energy_density) +=\n      get(dot_product(momentum_density, gravitational_field));\n}\n\ntnsr::I<DataVector, 3> LaneEmdenGravitationalField::gravitational_field(\n    const tnsr::I<DataVector, 3>& x) const {\n  // Compute alpha for polytrope n==1, units G==1\n  const double alpha = sqrt(0.5 * polytropic_constant_ / M_PI);\n  const double outer_radius = alpha * M_PI;\n  const double mass_scale = 4.0 * M_PI * cube(alpha) * central_mass_density_;\n  // Add tiny offset to avoid divisons by zero\n  const DataVector radius = get(magnitude(x)) + 1.e-30 * outer_radius;\n\n  auto enclosed_mass =\n      make_with_value<DataVector>(get_size(radius), mass_scale);\n  for (size_t s = 0; s < get_size(radius); ++s) {\n    if (get_element(radius, s) < outer_radius) {\n      const double xi = get_element(radius, s) / alpha;\n      get_element(enclosed_mass, s) *= sin(xi) - xi * cos(xi);\n    } else {\n      get_element(enclosed_mass, s) *= M_PI;\n    }\n  }\n\n  auto gravitational_field_result = x;\n  for (size_t i = 0; i < 3; ++i) {\n    gravitational_field_result.get(i) *= -enclosed_mass / cube(radius);\n  }\n  return gravitational_field_result;\n}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID LaneEmdenGravitationalField::my_PUP_ID = 0;\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/LaneEmdenGravitationalField.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Source.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TagsDeclarations.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace NewtonianEuler {\nnamespace Solutions {\nstruct LaneEmdenStar;\n}  // namespace Solutions\n}  // namespace NewtonianEuler\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace Tags {\ntemplate <typename SolutionType>\nstruct AnalyticSolution;\n}  // namespace Tags\n\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace NewtonianEuler::Sources {\n\n/*!\n * \\brief Source giving the acceleration due to gravity in the spherical,\n * Newtonian Lane-Emden star solution.\n *\n * The gravitational field \\f$g^i\\f$ enters the NewtonianEuler system as source\n * terms for the conserved momentum and energy:\n *\n * \\f{align*}\n * \\partial_t S^i + \\partial_j F^{j}(S^i) &= S(S^i) = \\rho g^i\n * \\partial_t e + \\partial_j F^{j}(e) &= S(e) = S_i g^i,\n * \\f}\n *\n * where \\f$S^i\\f$ is the conserved momentum density, \\f$e\\f$ is the conserved\n * energy, \\f$F^{j}(u)\\f$ is the flux of the conserved quantity \\f$u\\f$, and\n * \\f$\\rho\\f$ is the fluid mass density.\n *\n * \\note This source is specialized to the Lane-Emden solution because it\n * queries a LaneEmdenStar analytic solution for the gravitational field that\n * generates the fluid acceleration. This source does *not* integrate the fluid\n * density to compute a self-consistent gravitational field (i.e., as if one\n * were solving a coupled Euler + Poisson system).\n */\nclass LaneEmdenGravitationalField : public Source<3> {\n public:\n  /// The central mass density of the star.\n  struct CentralMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The central mass density of the star.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  /// The polytropic constant of the polytropic fluid.\n  struct PolytropicConstant {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The polytropic constant of the fluid.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  using options = tmpl::list<CentralMassDensity, PolytropicConstant>;\n\n  static constexpr Options::String help = {\n      \"The gravitational field corresponding to a static, \"\n      \"spherically-symmetric star in Newtonian gravity, found by \"\n      \"solving the Lane-Emden equations, with a given central density and \"\n      \"polytropic fluid. The fluid has polytropic index 1, but the polytropic \"\n      \"constant is specifiable\"};\n\n  LaneEmdenGravitationalField(double central_mass_density,\n                              double polytropic_constant);\n\n  LaneEmdenGravitationalField() = default;\n  LaneEmdenGravitationalField(const LaneEmdenGravitationalField& /*rhs*/) =\n      default;\n  LaneEmdenGravitationalField& operator=(\n      const LaneEmdenGravitationalField& /*rhs*/) = default;\n  LaneEmdenGravitationalField(LaneEmdenGravitationalField&& /*rhs*/) = default;\n  LaneEmdenGravitationalField& operator=(\n      LaneEmdenGravitationalField&& /*rhs*/) = default;\n  ~LaneEmdenGravitationalField() override = default;\n\n  /// \\cond\n  explicit LaneEmdenGravitationalField(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(LaneEmdenGravitationalField);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  auto get_clone() const -> std::unique_ptr<Source> override;\n\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> source_mass_density_cons,\n      gsl::not_null<tnsr::I<DataVector, 3>*> source_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, 3>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const tnsr::I<DataVector, 3>& velocity,\n      const Scalar<DataVector>& pressure,\n      const Scalar<DataVector>& specific_internal_energy,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const tnsr::I<DataVector, 3>& coords, double time) const override;\n\n private:\n  tnsr::I<DataVector, 3> gravitational_field(\n      const tnsr::I<DataVector, 3>& x) const;\n\n  double central_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_constant_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/NoSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Sources/NoSource.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler::Sources {\ntemplate <size_t Dim>\nNoSource<Dim>::NoSource(CkMigrateMessage* msg) : Source<Dim>{msg} {}\n\ntemplate <size_t Dim>\nvoid NoSource<Dim>::pup(PUP::er& p) {\n  Source<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\nauto NoSource<Dim>::get_clone() const -> std::unique_ptr<Source<Dim>> {\n  return std::make_unique<NoSource<Dim>>(*this);\n}\n\ntemplate <size_t Dim>\nvoid NoSource<Dim>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> /*source_mass_density_cons*/,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> /*source_momentum_density*/,\n    const gsl::not_null<Scalar<DataVector>*> /*source_energy_density*/,\n    const Scalar<DataVector>& /*mass_density_cons*/,\n    const tnsr::I<DataVector, Dim>& /*momentum_density*/,\n    const Scalar<DataVector>& /*energy_density*/,\n    const tnsr::I<DataVector, Dim>& /*velocity*/,\n    const Scalar<DataVector>& /*pressure*/,\n    const Scalar<DataVector>& /*specific_internal_energy*/,\n    const EquationsOfState::EquationOfState<false, 2>& /*eos*/,\n    const tnsr::I<DataVector, Dim>& /*coords*/, const double /*time*/) const {}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID NoSource<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class NoSource<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/NoSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Source.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::Sources {\n/*!\n * \\brief Used to mark that the initial data do not require source\n * terms in the evolution equations.\n */\ntemplate <size_t Dim>\nclass NoSource : public Source<Dim> {\n public:\n  using options = tmpl::list<>;\n\n  static constexpr Options::String help = {\"No source terms added.\"};\n\n  NoSource() = default;\n  NoSource(const NoSource& /*rhs*/) = default;\n  NoSource& operator=(const NoSource& /*rhs*/) = default;\n  NoSource(NoSource&& /*rhs*/) = default;\n  NoSource& operator=(NoSource&& /*rhs*/) = default;\n  ~NoSource() override = default;\n\n  /// \\cond\n  explicit NoSource(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NoSource);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  auto get_clone() const -> std::unique_ptr<Source<Dim>> override;\n\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> source_mass_density_cons,\n      gsl::not_null<tnsr::I<DataVector, Dim>*> source_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const tnsr::I<DataVector, Dim>& velocity,\n      const Scalar<DataVector>& pressure,\n      const Scalar<DataVector>& specific_internal_energy,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const tnsr::I<DataVector, Dim>& coords, double time) const override;\n\n  using sourced_variables = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n};\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/Source.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\n/// \\endcond\n\nnamespace NewtonianEuler {\n/*!\n * Holds classes implementing sources for the Newtonian Euler system.\n */\nnamespace Sources {\n/*!\n * \\brief Source terms base class.\n */\ntemplate <size_t Dim>\nclass Source : public PUP::able {\n protected:\n  Source() = default;\n\n public:\n  ~Source() override = default;\n\n  /// \\cond\n  explicit Source(CkMigrateMessage* msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(Source);\n  /// \\endcond\n\n  virtual auto get_clone() const -> std::unique_ptr<Source> = 0;\n\n  virtual void operator()(\n      gsl::not_null<Scalar<DataVector>*> source_mass_density_cons,\n      gsl::not_null<tnsr::I<DataVector, Dim>*> source_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const tnsr::I<DataVector, Dim>& velocity,\n      const Scalar<DataVector>& pressure,\n      const Scalar<DataVector>& specific_internal_energy,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const tnsr::I<DataVector, Dim>& coords, double time) const = 0;\n};\n}  // namespace Sources\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/UniformAcceleration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Sources/UniformAcceleration.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace NewtonianEuler::Sources {\ntemplate <size_t Dim>\nUniformAcceleration<Dim>::UniformAcceleration(\n    const std::array<double, Dim>& acceleration_field)\n    : acceleration_field_(acceleration_field) {}\n\ntemplate <size_t Dim>\nUniformAcceleration<Dim>::UniformAcceleration(CkMigrateMessage* msg)\n    : Source<Dim>{msg} {}\n\ntemplate <size_t Dim>\nvoid UniformAcceleration<Dim>::pup(PUP::er& p) {\n  Source<Dim>::pup(p);\n  p | acceleration_field_;\n}\n\ntemplate <size_t Dim>\nauto UniformAcceleration<Dim>::get_clone() const\n    -> std::unique_ptr<Source<Dim>> {\n  return std::make_unique<UniformAcceleration>(*this);\n}\n\ntemplate <size_t Dim>\nvoid UniformAcceleration<Dim>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> /*source_mass_density_cons*/,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> source_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> source_energy_density,\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, Dim>& momentum_density,\n    const Scalar<DataVector>& /*energy_density*/,\n    const tnsr::I<DataVector, Dim>& /*velocity*/,\n    const Scalar<DataVector>& /*pressure*/,\n    const Scalar<DataVector>& /*specific_internal_energy*/,\n    const EquationsOfState::EquationOfState<false, 2>& /*eos*/,\n    const tnsr::I<DataVector, Dim>& /*coords*/, const double /*time*/) const {\n  for (size_t i = 0; i < Dim; ++i) {\n    source_momentum_density->get(i) +=\n        get(mass_density_cons) * gsl::at(acceleration_field_, i);\n    get(*source_energy_density) +=\n        gsl::at(acceleration_field_, i) * momentum_density.get(i);\n  }\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID UniformAcceleration<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nbool operator==(const UniformAcceleration<Dim>& lhs,\n                const UniformAcceleration<Dim>& rhs) {\n  return lhs.acceleration_field_ == rhs.acceleration_field_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const UniformAcceleration<Dim>& lhs,\n                const UniformAcceleration<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template struct UniformAcceleration<DIM(data)>;                  \\\n  template bool operator==(const UniformAcceleration<DIM(data)>&,  \\\n                           const UniformAcceleration<DIM(data)>&); \\\n  template bool operator!=(const UniformAcceleration<DIM(data)>&,  \\\n                           const UniformAcceleration<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/UniformAcceleration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Source.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TagsDeclarations.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace NewtonianEuler::Sources {\n\n/*!\n * \\brief Source generated from an external uniform acceleration.\n *\n * The NewtonianEuler system with source terms is written as\n *\n * \\f{align*}\n * \\partial_t\\rho + \\partial_i F^i(\\rho) &= S(\\rho)\\\\\n * \\partial_t S^i + \\partial_j F^{j}(S^i) &= S(S^i)\\\\\n * \\partial_t e + \\partial_i F^i(e) &= S(e),\n * \\f}\n *\n * where \\f$F^i(u)\\f$ is the volume flux of the conserved quantity \\f$u\\f$\n * (see ComputeFluxes). For an external acceleration \\f$a^i\\f$, one has\n *\n * \\f{align*}\n * S(\\rho) &= 0\\\\\n * S(S^i) &= \\rho a^i\\\\\n * S(e) &= S_ia^i,\n * \\f}\n *\n * where \\f$\\rho\\f$ is the mass density, \\f$S^i\\f$ is the momentum density,\n * and \\f$e\\f$ is the energy density.\n */\ntemplate <size_t Dim>\nclass UniformAcceleration : public Source<Dim> {\n public:\n  /// The applied acceleration\n  struct Acceleration {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\"The applied accerelation.\"};\n  };\n\n  using options = tmpl::list<Acceleration>;\n\n  static constexpr Options::String help = {\n      \"Source terms corresponding to a uniform acceleration.\"};\n\n  UniformAcceleration() = default;\n  UniformAcceleration(const UniformAcceleration& /*rhs*/) = default;\n  UniformAcceleration& operator=(const UniformAcceleration& /*rhs*/) = default;\n  UniformAcceleration(UniformAcceleration&& /*rhs*/) = default;\n  UniformAcceleration& operator=(UniformAcceleration&& /*rhs*/) = default;\n  ~UniformAcceleration() override = default;\n\n  explicit UniformAcceleration(\n      const std::array<double, Dim>& acceleration_field);\n\n  /// \\cond\n  explicit UniformAcceleration(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(UniformAcceleration);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  auto get_clone() const -> std::unique_ptr<Source<Dim>> override;\n\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> source_mass_density_cons,\n      gsl::not_null<tnsr::I<DataVector, Dim>*> source_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const tnsr::I<DataVector, Dim>& velocity,\n      const Scalar<DataVector>& pressure,\n      const Scalar<DataVector>& specific_internal_energy,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const tnsr::I<DataVector, Dim>& coords, double time) const override;\n\n private:\n  template <size_t SpatialDim>\n  friend bool operator==(  // NOLINT(readability-redundant-declaration)\n      const UniformAcceleration<SpatialDim>& lhs,\n      const UniformAcceleration<SpatialDim>& rhs);\n\n  std::array<double, Dim> acceleration_field_ =\n      make_array<Dim>(std::numeric_limits<double>::signaling_NaN());\n};\n\ntemplate <size_t Dim>\nbool operator!=(const UniformAcceleration<Dim>& lhs,\n                const UniformAcceleration<Dim>& rhs);\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/VortexPerturbation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Sources/VortexPerturbation.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/IsentropicVortex.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::Sources {\nVortexPerturbation::VortexPerturbation(const double perturbation_amplitude)\n    : perturbation_amplitude_(perturbation_amplitude) {}\n\nVortexPerturbation::VortexPerturbation(CkMigrateMessage* msg) : Source{msg} {}\n\nvoid VortexPerturbation::pup(PUP::er& p) {\n  Source::pup(p);\n  p | perturbation_amplitude_;\n}\n\nauto VortexPerturbation::get_clone() const -> std::unique_ptr<Source<3>> {\n  return std::make_unique<VortexPerturbation>(*this);\n}\n\nvoid VortexPerturbation::operator()(\n    const gsl::not_null<Scalar<DataVector>*> source_mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> source_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> source_energy_density,\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, 3>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::I<DataVector, 3>& velocity, const Scalar<DataVector>& pressure,\n    const Scalar<DataVector>& /*specific_internal_energy*/,\n    const EquationsOfState::EquationOfState<false, 2>& /*eos*/,\n    const tnsr::I<DataVector, 3>& coords, const double /*time*/) const {\n  const Scalar<DataVector> dvz_by_dz{perturbation_amplitude_ *\n                                     cos(get<2>(coords))};\n\n  get(*source_mass_density_cons) += get(mass_density_cons) * get(dvz_by_dz);\n  for (size_t i = 0; i < 3; ++i) {\n    source_momentum_density->get(i) += momentum_density.get(i) * get(dvz_by_dz);\n  }\n  get(*source_energy_density) += (get(energy_density) + get(pressure) +\n                                  get<2>(velocity) * get<2>(momentum_density)) *\n                                 get(dvz_by_dz);\n}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID VortexPerturbation::my_PUP_ID = 0;\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Sources/VortexPerturbation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Source.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TagsDeclarations.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace NewtonianEuler {\nnamespace Solutions {\ntemplate <size_t Dim>\nstruct IsentropicVortex;\n}  // namespace Solutions\n}  // namespace NewtonianEuler\n\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace NewtonianEuler::Sources {\n\n/*!\n * \\brief Source generating a modified isentropic vortex.\n *\n * If Solutions::IsentropicVortex is modifed so that the flow velocity along\n * the \\f$z-\\f$axis is not a constant but a function of \\f$z\\f$, the new vortex\n * will be a solution to the 3-D Newtonian Euler equations with a source term,\n *\n * \\f{align*}\n * \\partial_t\\rho + \\partial_i F^i(\\rho) &= S(\\rho)\\\\\n * \\partial_t S^i + \\partial_j F^{j}(S^i) &= S(S^i)\\\\\n * \\partial_t e + \\partial_i F^i(e) &= S(e),\n * \\f}\n *\n * where \\f$F^i(u)\\f$ is the volume flux of the conserved quantity \\f$u\\f$\n * (see ComputeFluxes), and\n *\n * \\f{align*}\n * S(\\rho) &= \\rho \\dfrac{dv_z}{dz}\\\\\n * S(S_x) &= S_x \\dfrac{dv_z}{dz}\\\\\n * S(S_y) &= S_y \\dfrac{dv_z}{dz}\\\\\n * S(S_z) &= 2S_z \\dfrac{dv_z}{dz}\\\\\n * S(e) &= \\left(e + p + v_z S_z\\right)\\dfrac{dv_z}{dz},\n * \\f}\n *\n * where \\f$\\rho\\f$ is the mass density of the vortex, \\f$S_i\\f$ is\n * its momentum density, \\f$e\\f$ is its energy density,\n * \\f$v_z = v_z(z)\\f$ is the \\f$z-\\f$component of its velocity,\n * and \\f$p\\f$ is its pressure. These quantities are readily obtained\n * from the primitive variables, whose expressions are those in\n * Solutions::IsentropicVortex\n *\n * Currently \\f$v_z(z)=sin(z)\\f$ is hard-coded.\n */\nclass VortexPerturbation : public Source<3> {\n public:\n  /// The perturbation amplitude\n  struct PerturbationAmplitude {\n    using type = double;\n    static constexpr Options::String help = {\"The perturbation amplitude.\"};\n  };\n\n  using options = tmpl::list<PerturbationAmplitude>;\n\n  static constexpr Options::String help = {\n      \"Source terms corresponding to a vortex perturbation. Should be used \"\n      \"with the IsentropicVortex solution.\"};\n\n  explicit VortexPerturbation(double perturbation_amplitude);\n\n  VortexPerturbation() = default;\n  VortexPerturbation(const VortexPerturbation& /*rhs*/) = default;\n  VortexPerturbation& operator=(const VortexPerturbation& /*rhs*/) = default;\n  VortexPerturbation(VortexPerturbation&& /*rhs*/) = default;\n  VortexPerturbation& operator=(VortexPerturbation&& /*rhs*/) = default;\n  ~VortexPerturbation() override = default;\n\n  /// \\cond\n  explicit VortexPerturbation(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(VortexPerturbation);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  auto get_clone() const -> std::unique_ptr<Source<3>> override;\n\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> source_mass_density_cons,\n      gsl::not_null<tnsr::I<DataVector, 3>*> source_momentum_density,\n      gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, 3>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const tnsr::I<DataVector, 3>& velocity,\n      const Scalar<DataVector>& pressure,\n      const Scalar<DataVector>& specific_internal_energy,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const tnsr::I<DataVector, 3>& coords, double time) const override;\n\n private:\n  double perturbation_amplitude_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace NewtonianEuler::Sources\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/SpecificKineticEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/SpecificKineticEnergy.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace NewtonianEuler {\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid specific_kinetic_energy(const gsl::not_null<Scalar<DataType>*> result,\n                             const tnsr::I<DataType, Dim, Fr>& velocity) {\n  get(*result) = 0.5 * get(dot_product(velocity, velocity));\n}\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> specific_kinetic_energy(\n    const tnsr::I<DataType, Dim, Fr>& velocity) {\n  Scalar<DataType> result{};\n  specific_kinetic_energy(make_not_null(&result), velocity);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                            \\\n  template void specific_kinetic_energy(                \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result, \\\n      const tnsr::I<DTYPE(data), DIM(data)>& velocity); \\\n  template Scalar<DTYPE(data)> specific_kinetic_energy( \\\n      const tnsr::I<DTYPE(data), DIM(data)>& velocity);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector), (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n#undef DTYPE\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/SpecificKineticEnergy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler {\n/// @{\n/*!\n * Compute the specific kinetic energy, \\f$v^2/2\\f$,\n * where \\f$v\\f$ is the magnitude of the velocity.\n */\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid specific_kinetic_energy(gsl::not_null<Scalar<DataType>*> result,\n                             const tnsr::I<DataType, Dim, Fr>& velocity);\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> specific_kinetic_energy(\n    const tnsr::I<DataType, Dim, Fr>& velocity);\n/// @}\n\nnamespace Tags {\n/// Compute item for the specific kinetic energy, \\f$v^2/2\\f$.\n/// \\see NewtonianEuler::specific_kinetic_energy\n///\n/// Can be retrieved using `NewtonianEuler::Tags::SpecificKineticEnergy`\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct SpecificKineticEnergyCompute : SpecificKineticEnergy<DataType>,\n                                      db::ComputeTag {\n  using base = SpecificKineticEnergy<DataType>;\n\n  using argument_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim, Fr>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function =\n      static_cast<void (*)(const gsl::not_null<Scalar<DataType>*>,\n                           const tnsr::I<DataType, Dim, Fr>&)>(\n          &specific_kinetic_energy<DataType, Dim, Fr>);\n};\n}  // namespace Tags\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  NeighborPackagedData.cpp\n  PrimitiveGhostData.cpp\n  PrimsAfterRollback.cpp\n  ResizeAndComputePrimitives.cpp\n  SetInitialRdmpData.cpp\n  TciOnDgGrid.cpp\n  TciOnFdGrid.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComputeFluxes.hpp\n  NeighborPackagedData.hpp\n  PrimitiveGhostData.hpp\n  PrimsAfterRollback.hpp\n  ResizeAndComputePrimitives.hpp\n  SetInitialRdmpData.hpp\n  Subcell.hpp\n  TciOnDgGrid.hpp\n  TciOnFdGrid.hpp\n  TimeDerivative.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/ComputeFluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Fluxes.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::subcell {\nnamespace detail {\ntemplate <size_t Dim, typename TagsList, typename... ReturnTags,\n          typename... ArgumentTags>\nvoid compute_fluxes_impl(const gsl::not_null<Variables<TagsList>*> vars,\n                         tmpl::list<ReturnTags...> /*meta*/,\n                         tmpl::list<ArgumentTags...> /*meta*/) {\n  NewtonianEuler::ComputeFluxes<Dim>::apply(\n      make_not_null(&get<ReturnTags>(*vars))..., get<ArgumentTags>(*vars)...);\n}\n}  // namespace detail\n\n/*!\n * \\brief Helper function that calls `ComputeFluxes` by retrieving the return\n * and argument tags from `vars`.\n */\ntemplate <size_t Dim, typename TagsList>\nvoid compute_fluxes(const gsl::not_null<Variables<TagsList>*> vars) {\n  detail::compute_fluxes_impl<Dim>(\n      vars, typename NewtonianEuler::ComputeFluxes<Dim>::return_tags{},\n      typename NewtonianEuler::ComputeFluxes<Dim>::argument_tags{});\n}\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Subcell/NeighborPackagedData.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.tpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::subcell {\ntemplate <size_t Dim>\nDirectionalIdMap<Dim, DataVector> NeighborPackagedData::apply(\n    const db::Access& box,\n    const std::vector<DirectionalId<Dim>>& mortars_to_reconstruct_to) {\n  using system = NewtonianEuler::System<Dim>;\n  using evolved_vars_tag = typename system::variables_tag;\n  using evolved_vars_tags = typename evolved_vars_tag::tags_list;\n  using prim_tags = typename system::primitive_variables_tag::tags_list;\n  using fluxes_tags = db::wrap_tags_in<::Tags::Flux, evolved_vars_tags,\n                                       tmpl::size_t<Dim>, Frame::Inertial>;\n\n  ASSERT(not db::get<domain::Tags::MeshVelocity<Dim>>(box).has_value(),\n         \"Haven't yet added support for moving mesh to DG-subcell. This \"\n         \"should be easy to generalize, but we will want to consider \"\n         \"storing the mesh velocity on the faces instead of \"\n         \"re-slicing/projecting.\");\n\n  DirectionalIdMap<Dim, DataVector> neighbor_package_data{};\n  if (mortars_to_reconstruct_to.empty()) {\n    return neighbor_package_data;\n  }\n\n  const auto& ghost_subcell_data =\n      db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(\n          box);\n  const Mesh<Dim>& subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(box);\n  const Mesh<Dim>& dg_mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n  const auto& subcell_options =\n      db::get<evolution::dg::subcell::Tags::SubcellOptions<Dim>>(box);\n\n  // Note: we need to compare if projecting the entire mesh or only ghost\n  // zones needed is faster. This probably depends on the number of neighbors\n  // we have doing FD.\n  const auto volume_prims = evolution::dg::subcell::fd::project(\n      db::get<typename system::primitive_variables_tag>(box), dg_mesh,\n      subcell_mesh.extents());\n\n  const auto& recons =\n      db::get<NewtonianEuler::fd::Tags::Reconstructor<Dim>>(box);\n  const auto& boundary_correction =\n      db::get<evolution::Tags::BoundaryCorrection>(box);\n  using derived_boundary_corrections =\n      NewtonianEuler::BoundaryCorrections::standard_boundary_corrections<Dim>;\n  tmpl::for_each<derived_boundary_corrections>([&box, &boundary_correction,\n                                                &dg_mesh,\n                                                &mortars_to_reconstruct_to,\n                                                &neighbor_package_data,\n                                                &ghost_subcell_data, &recons,\n                                                &subcell_mesh, &subcell_options,\n                                                &volume_prims](\n                                                   auto derived_correction_v) {\n    using DerivedCorrection = tmpl::type_from<decltype(derived_correction_v)>;\n    if (typeid(boundary_correction) == typeid(DerivedCorrection)) {\n      using dg_package_data_temporary_tags =\n          typename DerivedCorrection::dg_package_data_temporary_tags;\n      using dg_package_data_argument_tags =\n          tmpl::append<evolved_vars_tags, prim_tags, fluxes_tags,\n                       dg_package_data_temporary_tags>;\n\n      const auto& element = db::get<domain::Tags::Element<Dim>>(box);\n      const auto& eos = get<hydro::Tags::EquationOfState<false, 2>>(box);\n\n      using dg_package_field_tags =\n          typename DerivedCorrection::dg_package_field_tags;\n      Variables<dg_package_data_argument_tags> vars_on_face;\n      Variables<dg_package_field_tags> packaged_data;\n      for (const auto& mortar_id : mortars_to_reconstruct_to) {\n        const Direction<Dim>& direction = mortar_id.direction();\n\n        Index<Dim> extents = subcell_mesh.extents();\n        // Switch to face-centered instead of cell-centered points on the FD.\n        // There are num_cell_centered+1 face-centered points.\n        ++extents[direction.dimension()];\n\n        // Computed prims and cons on face via reconstruction\n        const size_t num_face_pts =\n            subcell_mesh.extents().slice_away(direction.dimension()).product();\n        vars_on_face.initialize(num_face_pts);\n\n        call_with_dynamic_type<void, typename NewtonianEuler::fd::Reconstructor<\n                                         Dim>::creatable_classes>(\n            &recons,\n            [&element, &eos, &mortar_id, &ghost_subcell_data, &subcell_mesh,\n             &vars_on_face, &volume_prims](const auto& reconstructor) {\n              reconstructor->reconstruct_fd_neighbor(\n                  make_not_null(&vars_on_face), volume_prims, eos, element,\n                  ghost_subcell_data, subcell_mesh, mortar_id.direction());\n            });\n\n        NewtonianEuler::subcell::compute_fluxes<Dim>(\n            make_not_null(&vars_on_face));\n\n        tnsr::i<DataVector, Dim, Frame::Inertial> normal_covector =\n            get<evolution::dg::Tags::NormalCovector<Dim>>(\n                *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(\n                     box)\n                     .at(mortar_id.direction()));\n        for (auto& t : normal_covector) {\n          t *= -1.0;\n        }\n        if constexpr (Dim > 1) {\n          const auto dg_normal_covector = normal_covector;\n          for (size_t i = 0; i < Dim; ++i) {\n            normal_covector.get(i) = evolution::dg::subcell::fd::project(\n                dg_normal_covector.get(i),\n                dg_mesh.slice_away(mortar_id.direction().dimension()),\n                subcell_mesh.extents().slice_away(\n                    mortar_id.direction().dimension()));\n          }\n        }\n\n        // Compute the packaged data\n        packaged_data.initialize(num_face_pts);\n        using dg_package_data_projected_tags = tmpl::append<\n            evolved_vars_tags, fluxes_tags, dg_package_data_temporary_tags,\n            typename DerivedCorrection::dg_package_data_primitive_tags>;\n        evolution::dg::Actions::detail::dg_package_data<system>(\n            make_not_null(&packaged_data),\n            dynamic_cast<const DerivedCorrection&>(boundary_correction),\n            vars_on_face, normal_covector, {std::nullopt}, box,\n            typename DerivedCorrection::dg_package_data_volume_tags{},\n            dg_package_data_projected_tags{});\n\n        if constexpr (Dim == 1) {\n          (void)dg_mesh;\n          (void)subcell_options;\n          // Make a view so we can use iterators with std::copy\n          DataVector packaged_data_view{packaged_data.data(),\n                                        packaged_data.size()};\n          neighbor_package_data[mortar_id] = DataVector{packaged_data.size()};\n          std::copy(packaged_data_view.begin(), packaged_data_view.end(),\n                    neighbor_package_data[mortar_id].begin());\n        } else {\n          // Reconstruct the DG solution.\n          // Really we should be solving the boundary correction and\n          // then reconstructing, but away from a shock this doesn't\n          // matter.\n          auto dg_packaged_data = evolution::dg::subcell::fd::reconstruct(\n              packaged_data,\n              dg_mesh.slice_away(mortar_id.direction().dimension()),\n              subcell_mesh.extents().slice_away(\n                  mortar_id.direction().dimension()),\n              subcell_options.reconstruction_method());\n          // Make a view so we can use iterators with std::copy\n          DataVector dg_packaged_data_view{dg_packaged_data.data(),\n                                           dg_packaged_data.size()};\n          neighbor_package_data[mortar_id] =\n              DataVector{dg_packaged_data.size()};\n          std::copy(dg_packaged_data_view.begin(), dg_packaged_data_view.end(),\n                    neighbor_package_data[mortar_id].begin());\n        }\n      }\n    }\n  });\n\n  return neighbor_package_data;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                     \\\n  template DirectionalIdMap<DIM(data), DataVector> \\\n  NeighborPackagedData::apply<DIM(data)>(          \\\n      const db::Access& box,                       \\\n      const std::vector<DirectionalId<DIM(data)>>& mortars_to_reconstruct_to);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n}  // namespace NewtonianEuler::subcell\n\n#define INSTANTIATION(r, data)                                                \\\n  template void evolution::dg::subcell::neighbor_reconstructed_face_solution< \\\n      DIM(data), NewtonianEuler::subcell::NeighborPackagedData>(              \\\n      gsl::not_null<db::Access*> box);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/NeighborPackagedData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n/// \\cond\nnamespace db {\nclass Access;\n}  // namespace db\nclass DataVector;\ntemplate <size_t VolumeDim>\nstruct DirectionalId;\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\n/// \\endcond\n\nnamespace NewtonianEuler::subcell {\n/*!\n * \\brief On elements using DG, reconstructs the interface data from a\n * neighboring element doing subcell.\n *\n * The neighbor's packaged data needed by the boundary correction is computed\n * and returned so that it can be used for solving the Riemann problem on the\n * interfaces.\n *\n * Note that for strict conservation the Riemann solve should be done on the\n * subcells, with the correction being projected back to the DG interface.\n * However, in practice such strict conservation doesn't seem to be necessary\n * and can be explained by that we only need strict conservation at shocks, and\n * if one element is doing DG, then we aren't at a shock.\n */\nstruct NeighborPackagedData {\n  template <size_t Dim>\n  static DirectionalIdMap<Dim, DataVector> apply(\n      const db::Access& box,\n      const std::vector<DirectionalId<Dim>>& mortars_to_reconstruct_to);\n};\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/PrimitiveGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Subcell/PrimitiveGhostData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::subcell {\ntemplate <size_t Dim>\nDataVector PrimitiveGhostVariables<Dim>::apply(\n    const Variables<prim_tags>& prims, const size_t rdmp_size) {\n  DataVector buffer{\n      prims.number_of_grid_points() *\n          Variables<\n              prims_to_reconstruct_tags>::number_of_independent_components +\n      rdmp_size};\n  Variables<prims_to_reconstruct_tags> prims_for_reconstruction(\n      buffer.data(), buffer.size() - rdmp_size);\n  tmpl::for_each<prims_to_reconstruct_tags>(\n      [&prims, &prims_for_reconstruction](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        get<tag>(prims_for_reconstruction) = get<tag>(prims);\n      });\n  return buffer;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) \\\n  template class PrimitiveGhostVariables<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/PrimitiveGhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace NewtonianEuler::subcell {\n/*!\n * \\brief Computes the mass density, velocity, and pressure on the subcells so\n * they can be sent to the neighbors for their reconstructions.\n *\n * The computation just copies the data from the primitive variables tag to a\n * new Variables (the copy is subcell grid to subcell grid). In the future we\n * will likely want to elide this copy but that requires support from the\n * actions.\n *\n * This mutator is passed to\n * `evolution::dg::subcell::Actions::SendDataForReconstruction`.\n */\ntemplate <size_t Dim>\nclass PrimitiveGhostVariables {\n private:\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  using prim_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using prims_to_reconstruct_tags = tmpl::list<MassDensity, Velocity, Pressure>;\n\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::Variables<prim_tags>>;\n\n  static DataVector apply(const Variables<prim_tags>& prims, size_t rdmp_size);\n};\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/PrimsAfterRollback.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Subcell/PrimsAfterRollback.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::subcell {\ntemplate <size_t Dim>\nvoid PrimsAfterRollback<Dim>::apply(\n    const gsl::not_null<Variables<\n        tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>*>\n        prim_vars,\n    const bool did_rollback, const Mesh<Dim>& subcell_mesh,\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, Dim>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state) {\n  if (did_rollback) {\n    const size_t num_grid_points = subcell_mesh.number_of_grid_points();\n    if (prim_vars->number_of_grid_points() != num_grid_points) {\n      prim_vars->initialize(num_grid_points);\n    }\n    NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n        make_not_null(&get<MassDensity>(*prim_vars)),\n        make_not_null(&get<Velocity>(*prim_vars)),\n        make_not_null(&get<SpecificInternalEnergy>(*prim_vars)),\n        make_not_null(&get<Pressure>(*prim_vars)), mass_density_cons,\n        momentum_density, energy_density, equation_of_state);\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) template class PrimsAfterRollback<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/PrimsAfterRollback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace NewtonianEuler::subcell {\n/*!\n * \\brief Mutator that resizes the primitive variables to the subcell mesh and\n * computes the primitives, but only if\n * `evolution::dg::subcell::Tags::DidRollback` is `true`.\n *\n * In the DG-subcell `step_actions` list this will normally be called using the\n * `::Actions::MutateApply` action right after the\n * `evolution::dg::subcell::Actions::Labels::BeginSubcellAfterDgRollback` label.\n */\ntemplate <size_t Dim>\nstruct PrimsAfterRollback {\n private:\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n public:\n  using return_tags = tmpl::list<::Tags::Variables<\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>>;\n  using argument_tags =\n      tmpl::list<evolution::dg::subcell::Tags::DidRollback,\n                 evolution::dg::subcell::Tags::Mesh<Dim>, Tags::MassDensityCons,\n                 Tags::MomentumDensity<Dim>, Tags::EnergyDensity,\n                 hydro::Tags::EquationOfState<false, 2>>;\n\n  static void apply(\n      gsl::not_null<Variables<\n          tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>*>\n          prim_vars,\n      bool did_rollback, const Mesh<Dim>& subcell_mesh,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const EquationsOfState::EquationOfState<false, 2>& equation_of_state);\n};\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/ResizeAndComputePrimitives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Subcell/ResizeAndComputePrimitives.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::subcell {\ntemplate <size_t Dim>\nvoid ResizeAndComputePrims<Dim>::apply(\n    const gsl::not_null<Variables<\n        tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>*>\n        prim_vars,\n    const evolution::dg::subcell::ActiveGrid active_grid,\n    const Mesh<Dim>& dg_mesh, const Mesh<Dim>& subcell_mesh,\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, Dim>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n    const EquationsOfState::EquationOfState<false, 2>& equation_of_state) {\n  const size_t num_grid_points =\n      (active_grid == evolution::dg::subcell::ActiveGrid::Dg ? dg_mesh\n                                                             : subcell_mesh)\n          .number_of_grid_points();\n  if (prim_vars->number_of_grid_points() != num_grid_points) {\n    prim_vars->initialize(num_grid_points);\n    NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n        make_not_null(&get<MassDensity>(*prim_vars)),\n        make_not_null(&get<Velocity>(*prim_vars)),\n        make_not_null(&get<SpecificInternalEnergy>(*prim_vars)),\n        make_not_null(&get<Pressure>(*prim_vars)), mass_density_cons,\n        momentum_density, energy_density, equation_of_state);\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) template class ResizeAndComputePrims<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/ResizeAndComputePrimitives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace NewtonianEuler::subcell {\n/*!\n * \\brief Mutator that resizes the primitive variables to have the size of the\n * active mesh and then computes the primitive variables on the active mesh.\n *\n * In the DG-subcell `step_actions` list this will normally be called using the\n * `::Actions::MutateApply` action right after the\n * `evolution::dg::subcell::Actions::TciAndSwitchToDg` action. We only need to\n * compute the primitives if we switched to the DG grid because otherwise we\n * computed the primitives during the FD TCI. After the primitive variables tag\n * is resized for the DG grid, the primitives are computed directly on the DG\n * grid from the reconstructed conserved variables, not via a reconstruction\n * operation applied to the primitives.\n */\ntemplate <size_t Dim>\nstruct ResizeAndComputePrims {\n private:\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n public:\n  using return_tags = tmpl::list<::Tags::Variables<\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>>;\n  using argument_tags =\n      tmpl::list<evolution::dg::subcell::Tags::ActiveGrid,\n                 domain::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>, Tags::MassDensityCons,\n                 Tags::MomentumDensity<Dim>, Tags::EnergyDensity,\n                 hydro::Tags::EquationOfState<false, 2>>;\n\n  static void apply(\n      gsl::not_null<Variables<\n          tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>*>\n          prim_vars,\n      evolution::dg::subcell::ActiveGrid active_grid, const Mesh<Dim>& dg_mesh,\n      const Mesh<Dim>& subcell_mesh,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const EquationsOfState::EquationOfState<false, 2>& equation_of_state);\n};\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Subcell/SetInitialRdmpData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/TwoMeshRdmpTci.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace NewtonianEuler::subcell {\ntemplate <size_t Dim>\nvoid SetInitialRdmpData<Dim>::apply(\n    const gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n    const Variables<\n        tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>>& vars,\n    const evolution::dg::subcell::ActiveGrid active_grid,\n    const Mesh<Dim>& dg_mesh, const Mesh<Dim>& subcell_mesh) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Subcell) {\n    const Scalar<DataVector>& mass_density = get<MassDensityCons>(vars);\n    const Scalar<DataVector>& energy_density = get<EnergyDensity>(vars);\n    *rdmp_tci_data = {{max(get(mass_density)), max(get(energy_density))},\n                      {min(get(mass_density)), min(get(energy_density))}};\n  } else {\n    const Scalar<DataVector>& dg_mass_density = get<MassDensityCons>(vars);\n    const Scalar<DataVector>& dg_energy_density = get<EnergyDensity>(vars);\n    const auto subcell_mass_density = evolution::dg::subcell::fd::project(\n        get(dg_mass_density), dg_mesh, subcell_mesh.extents());\n    const auto subcell_energy_density = evolution::dg::subcell::fd::project(\n        get(dg_energy_density), dg_mesh, subcell_mesh.extents());\n\n    using std::max;\n    using std::min;\n    *rdmp_tci_data = {\n        {max(max(get(dg_mass_density)), max(subcell_mass_density)),\n         max(max(get(dg_energy_density)), max(subcell_energy_density))},\n        {min(min(get(dg_mass_density)), min(subcell_mass_density)),\n         min(min(get(dg_energy_density)), min(subcell_energy_density))}};\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) template struct SetInitialRdmpData<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/SetInitialRdmpData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace NewtonianEuler::subcell {\n/// \\brief Sets the initial RDMP data.\n///\n/// Used on the subcells after the TCI marked the DG solution as inadmissible.\ntemplate <size_t Dim>\nstruct SetInitialRdmpData {\n private:\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n public:\n  using argument_tags = tmpl::list<\n      ::Tags::Variables<\n          tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>>,\n      evolution::dg::subcell::Tags::ActiveGrid, ::domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>>;\n  using return_tags = tmpl::list<evolution::dg::subcell::Tags::DataForRdmpTci>;\n\n  static void apply(\n      gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n      const Variables<tmpl::list<MassDensityCons, MomentumDensity,\n                                 EnergyDensity>>& subcell_vars,\n      evolution::dg::subcell::ActiveGrid active_grid, const Mesh<Dim>& dg_mesh,\n      const Mesh<Dim>& subcell_mesh);\n};\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/Subcell.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace NewtonianEuler {\n/// \\brief Code required by the DG-subcell/FD hybrid solver.\nnamespace subcell {}\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Subcell/TciOnDgGrid.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::subcell {\ntemplate <size_t Dim>\nstd::tuple<bool, evolution::dg::subcell::RdmpTciData> TciOnDgGrid<Dim>::apply(\n    const gsl::not_null<Variables<\n        tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>*>\n        dg_prim_vars,\n    const Variables<\n        tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>>& dg_vars,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Mesh<Dim>& dg_mesh, const Mesh<Dim>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const double persson_exponent,\n    [[maybe_unused]] const bool element_stays_on_dg) {\n  const Variables<tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>>\n      subcell_vars = evolution::dg::subcell::fd::project(\n          dg_vars, dg_mesh, subcell_mesh.extents());\n  const Scalar<DataVector>& mass_density = get<MassDensityCons>(dg_vars);\n  const tnsr::I<DataVector, Dim, Frame::Inertial>& momentum_density =\n      get<MomentumDensity>(dg_vars);\n  const Scalar<DataVector>& energy_density = get<EnergyDensity>(dg_vars);\n\n  const Scalar<DataVector>& subcell_mass_density =\n      get<MassDensityCons>(subcell_vars);\n  const Scalar<DataVector>& subcell_energy_density =\n      get<EnergyDensity>(subcell_vars);\n\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{\n      {max(max(get(mass_density)), max(get(subcell_mass_density))),\n       max(max(get(energy_density)), max(get(subcell_energy_density)))},\n      {min(min(get(mass_density)), min(get(subcell_mass_density))),\n       min(min(get(energy_density)), min(get(subcell_energy_density)))}};\n\n  NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n      make_not_null(&get<MassDensity>(*dg_prim_vars)),\n      make_not_null(&get<Velocity>(*dg_prim_vars)),\n      make_not_null(&get<SpecificInternalEnergy>(*dg_prim_vars)),\n      make_not_null(&get<Pressure>(*dg_prim_vars)), mass_density,\n      momentum_density, energy_density, eos);\n\n  const bool cell_is_troubled =\n      evolution::dg::subcell::rdmp_tci(rdmp_tci_data.max_variables_values,\n                                       rdmp_tci_data.min_variables_values,\n                                       past_rdmp_tci_data.max_variables_values,\n                                       past_rdmp_tci_data.min_variables_values,\n                                       subcell_options.rdmp_delta0(),\n                                       subcell_options.rdmp_epsilon()) or\n      min(get(mass_density)) < min_density_allowed or\n      min(get(get<Pressure>(*dg_prim_vars))) < min_pressure_allowed or\n      evolution::dg::subcell::persson_tci(\n          mass_density, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes()) or\n      evolution::dg::subcell::persson_tci(\n          energy_density, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes());\n  return {cell_is_troubled, std::move(rdmp_tci_data)};\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) template class TciOnDgGrid<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/TciOnDgGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename T>\nclass Variables;\n/// \\endcond\n\nnamespace NewtonianEuler::subcell {\n/*!\n * \\brief Troubled-cell indicator applied to the DG solution.\n *\n * Computes the primitive variables on the DG grid, mutating them in the\n * DataBox. Then,\n * - apply RDMP TCI to the mass and energy density\n * - if the minimum density or pressure are below \\f$10^{-18}\\f$ (the arbitrary\n *   threshold used to signal \"negative\" density and pressure), marks the\n *   element as troubled and returns\n * - runs the Persson TCI on the mass and energy density. The reason for\n *   applying the Persson TCI to both the mass and energy density is to flag\n *   cells at contact discontinuities.\n */\ntemplate <size_t Dim>\nclass TciOnDgGrid {\n private:\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  static constexpr double min_density_allowed = 1.0e-18;\n  static constexpr double min_pressure_allowed = 1.0e-18;\n\n public:\n  using return_tags = tmpl::list<::Tags::Variables<\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>>;\n  using argument_tags = tmpl::list<\n      ::Tags::Variables<\n          tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>>,\n      hydro::Tags::EquationOfState<false, 2>, domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::DataForRdmpTci,\n      evolution::dg::subcell::Tags::SubcellOptions<Dim>>;\n\n  static std::tuple<bool, evolution::dg::subcell::RdmpTciData> apply(\n      gsl::not_null<Variables<\n          tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>*>\n          dg_prim_vars,\n      const Variables<\n          tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>>& dg_vars,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const Mesh<Dim>& dg_mesh, const Mesh<Dim>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      double persson_exponent, bool element_stays_on_dg);\n};\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Subcell/TciOnFdGrid.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::subcell {\ntemplate <size_t Dim>\nstd::tuple<bool, evolution::dg::subcell::RdmpTciData> TciOnFdGrid<Dim>::apply(\n    const gsl::not_null<Variables<\n        tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>*>\n        subcell_grid_prim_vars,\n    const Variables<tmpl::list<MassDensityCons, MomentumDensity,\n                               EnergyDensity>>& subcell_vars,\n    const EquationsOfState::EquationOfState<false, 2>& eos,\n    const Mesh<Dim>& dg_mesh, const Mesh<Dim>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const double persson_exponent, const bool need_rdmp_data_only) {\n  const Scalar<DataVector>& subcell_mass_density =\n      get<MassDensityCons>(subcell_vars);\n  const tnsr::I<DataVector, Dim, Frame::Inertial>& subcell_momentum_density =\n      get<MomentumDensity>(subcell_vars);\n  const Scalar<DataVector>& subcell_energy_density =\n      get<EnergyDensity>(subcell_vars);\n  const auto dg_vars = evolution::dg::subcell::fd::reconstruct(\n      subcell_vars, dg_mesh, subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n  const Scalar<DataVector>& dg_mass_density = get<MassDensityCons>(dg_vars);\n  const tnsr::I<DataVector, Dim, Frame::Inertial>& dg_momentum_density =\n      get<MomentumDensity>(dg_vars);\n  const Scalar<DataVector>& dg_energy_density = get<EnergyDensity>(dg_vars);\n\n  NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n      make_not_null(&get<MassDensity>(*subcell_grid_prim_vars)),\n      make_not_null(&get<Velocity>(*subcell_grid_prim_vars)),\n      make_not_null(&get<SpecificInternalEnergy>(*subcell_grid_prim_vars)),\n      make_not_null(&get<Pressure>(*subcell_grid_prim_vars)),\n      subcell_mass_density, subcell_momentum_density, subcell_energy_density,\n      eos);\n  Variables<tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>\n      dg_grid_prim_vars{get(dg_energy_density).size()};\n  NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n      make_not_null(&get<MassDensity>(dg_grid_prim_vars)),\n      make_not_null(&get<Velocity>(dg_grid_prim_vars)),\n      make_not_null(&get<SpecificInternalEnergy>(dg_grid_prim_vars)),\n      make_not_null(&get<Pressure>(dg_grid_prim_vars)), dg_mass_density,\n      dg_momentum_density, dg_energy_density, eos);\n\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{\n      {max(get(subcell_mass_density)), max(get(subcell_energy_density))},\n      {min(get(subcell_mass_density)), min(get(subcell_energy_density))}};\n\n  const evolution::dg::subcell::RdmpTciData rdmp_tci_data_for_check{\n      {max(rdmp_tci_data.max_variables_values[0], max(get(dg_mass_density))),\n       max(rdmp_tci_data.max_variables_values[1], max(get(dg_energy_density)))},\n      {min(rdmp_tci_data.min_variables_values[0], min(get(dg_mass_density))),\n       min(rdmp_tci_data.min_variables_values[1],\n           min(get(dg_energy_density)))}};\n\n  if (need_rdmp_data_only) {\n    return {false, rdmp_tci_data};\n  }\n\n  const bool cell_is_troubled =\n      evolution::dg::subcell::rdmp_tci(\n          rdmp_tci_data_for_check.max_variables_values,\n          rdmp_tci_data_for_check.min_variables_values,\n          past_rdmp_tci_data.max_variables_values,\n          past_rdmp_tci_data.min_variables_values,\n          subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon()) or\n      min(min(get(subcell_mass_density)), min(get(dg_mass_density))) <\n          min_density_allowed or\n      min(min(get(get<Pressure>(*subcell_grid_prim_vars))),\n          min(get(get<Pressure>(dg_grid_prim_vars)))) < min_pressure_allowed or\n      evolution::dg::subcell::persson_tci(\n          dg_mass_density, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes()) or\n      evolution::dg::subcell::persson_tci(\n          dg_energy_density, dg_mesh, persson_exponent,\n          subcell_options.persson_num_highest_modes());\n\n  return {cell_is_troubled, std::move(rdmp_tci_data)};\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) template class TciOnFdGrid<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n#undef DIM\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/TciOnFdGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace NewtonianEuler::subcell {\n/*!\n * \\brief Troubled-cell indicator applied to the finite difference subcell\n * solution to check if the corresponding DG solution is admissible.\n *\n * Computes the primitive variables on the DG and subcell grids, mutating the\n * subcell/active primitive variables in the DataBox. Then,\n * - apply RDMP TCI to the mass and energy density\n * - if the minimum density or pressure on either the DG or subcell mesh are\n *   below \\f$10^{-18}\\f$, marks the element as troubled and returns. We check\n *   both the FD and DG grids since when a discontinuity is inside the element\n *   oscillations in the DG solution can result in negative values that aren't\n *   present in the FD solution.\n * - runs the Persson TCI on the mass and energy density on the DG grid. The\n *   reason for applying the Persson TCI to both the mass and energy density is\n *   to flag cells at contact discontinuities. The Persson TCI only works with\n *   spectral-type methods and is a direct check of whether or not the DG\n *   solution is a good representation of the underlying data.\n *\n * Please note that the TCI is run after the subcell solution has been\n * reconstructed to the DG grid, and so `Inactive<Tag>` is the updated DG\n * solution.\n */\ntemplate <size_t Dim>\nclass TciOnFdGrid {\n private:\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  static constexpr double min_density_allowed = 1.0e-18;\n  static constexpr double min_pressure_allowed = 1.0e-18;\n\n public:\n  using return_tags = tmpl::list<::Tags::Variables<\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>>;\n  using argument_tags = tmpl::list<\n      ::Tags::Variables<\n          tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>>,\n      hydro::Tags::EquationOfState<false, 2>, domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::DataForRdmpTci,\n      evolution::dg::subcell::Tags::SubcellOptions<Dim>>;\n\n  static std::tuple<bool, evolution::dg::subcell::RdmpTciData> apply(\n      gsl::not_null<Variables<\n          tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>>*>\n          subcell_grid_prim_vars,\n      const Variables<tmpl::list<MassDensityCons, MomentumDensity,\n                                 EnergyDensity>>& subcell_vars,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const Mesh<Dim>& dg_mesh, const Mesh<Dim>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      double persson_exponentconst, bool need_rdmp_data_only);\n};\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Subcell/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/ComputeBoundaryTerms.hpp\"\n#include \"Evolution/DgSubcell/CorrectPackagedData.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Fluxes.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::subcell {\n/*!\n * \\brief Compute the time derivative on the subcell grid using FD\n * reconstruction.\n *\n * The code makes the following unchecked assumptions:\n * - Assumes Cartesian coordinates with a diagonal Jacobian matrix\n * from the logical to the inertial frame\n * - Assumes the mesh is not moving (grid and inertial frame are the same)\n */\ntemplate <size_t Dim>\nstruct TimeDerivative {\n  template <typename DbTagsList>\n  static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n    using metavariables = typename std::decay_t<decltype(\n        db::get<Parallel::Tags::Metavariables>(*box))>;\n    using system = typename metavariables::system;\n    using evolved_vars_tag = typename system::variables_tag;\n    using evolved_vars_tags = typename evolved_vars_tag::tags_list;\n    using prim_tags = typename system::primitive_variables_tag::tags_list;\n    using fluxes_tags = db::wrap_tags_in<::Tags::Flux, evolved_vars_tags,\n                                         tmpl::size_t<Dim>, Frame::Inertial>;\n\n    ASSERT((db::get<::domain::CoordinateMaps::Tags::CoordinateMap<\n                Dim, Frame::Grid, Frame::Inertial>>(*box))\n               .is_identity(),\n           \"Do not yet support moving mesh with DG-subcell.\");\n\n    // The copy of Mesh is intentional to avoid a GCC-7 internal compiler error.\n    const Mesh<Dim> subcell_mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(*box);\n    ASSERT(\n        subcell_mesh == Mesh<Dim>(subcell_mesh.extents(0),\n                                  subcell_mesh.basis(0),\n                                  subcell_mesh.quadrature(0)),\n        \"The subcell/FD mesh must be isotropic for the FD time derivative but \"\n        \"got \"\n            << subcell_mesh);\n    const size_t num_pts = subcell_mesh.number_of_grid_points();\n    const size_t reconstructed_num_pts =\n        (subcell_mesh.extents(0) + 1) *\n        subcell_mesh.extents().slice_away(0).product();\n\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n        cell_centered_logical_coords =\n            db::get<evolution::dg::subcell::Tags::Coordinates<\n                Dim, Frame::ElementLogical>>(*box);\n    std::array<double, Dim> one_over_delta_xi{};\n    for (size_t i = 0; i < Dim; ++i) {\n      // Note: assumes isotropic extents\n      gsl::at(one_over_delta_xi, i) =\n          1.0 / (get<0>(cell_centered_logical_coords)[1] -\n                 get<0>(cell_centered_logical_coords)[0]);\n    }\n\n    const NewtonianEuler::fd::Reconstructor<Dim>& recons =\n        db::get<NewtonianEuler::fd::Tags::Reconstructor<Dim>>(*box);\n\n    const Element<Dim>& element = db::get<domain::Tags::Element<Dim>>(*box);\n    ASSERT(element.external_boundaries().size() == 0,\n           \"Can't have external boundaries right now with subcell. ElementID \"\n               << element.id());\n\n    // Now package the data and compute the correction\n    const auto& boundary_correction =\n        db::get<evolution::Tags::BoundaryCorrection>(*box);\n    using derived_boundary_corrections =\n        tmpl::at<typename metavariables::factory_creation::factory_classes,\n                 evolution::BoundaryCorrection>;\n    std::array<Variables<evolved_vars_tags>, Dim> boundary_corrections{};\n    tmpl::for_each<derived_boundary_corrections>([&boundary_correction,\n                                                  &reconstructed_num_pts,\n                                                  &recons, &box, &element,\n                                                  &subcell_mesh,\n                                                  &boundary_corrections](\n                                                     auto\n                                                         derived_correction_v) {\n      using DerivedCorrection = tmpl::type_from<decltype(derived_correction_v)>;\n      if (typeid(boundary_correction) == typeid(DerivedCorrection)) {\n        using dg_package_data_temporary_tags =\n            typename DerivedCorrection::dg_package_data_temporary_tags;\n        using dg_package_data_argument_tags =\n            tmpl::append<evolved_vars_tags, prim_tags, fluxes_tags,\n                         dg_package_data_temporary_tags>;\n        // Computed prims and cons on face via reconstruction\n        auto package_data_argvars_lower_face = make_array<Dim>(\n            Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n        auto package_data_argvars_upper_face = make_array<Dim>(\n            Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n\n        // Reconstruct data to the face\n        call_with_dynamic_type<void, typename NewtonianEuler::fd::Reconstructor<\n                                         Dim>::creatable_classes>(\n            &recons,\n            [&box, &package_data_argvars_lower_face,\n             &package_data_argvars_upper_face](const auto& reconstructor) {\n              db::apply<typename std::decay_t<decltype(\n                  *reconstructor)>::reconstruction_argument_tags>(\n                  [&package_data_argvars_lower_face,\n                   &package_data_argvars_upper_face,\n                   &reconstructor](const auto&... args) {\n                    reconstructor->reconstruct(\n                        make_not_null(&package_data_argvars_lower_face),\n                        make_not_null(&package_data_argvars_upper_face),\n                        args...);\n                  },\n                  *box);\n            });\n\n        using dg_package_field_tags =\n            typename DerivedCorrection::dg_package_field_tags;\n        // Allocated outside for loop to reduce allocations\n        Variables<dg_package_field_tags> upper_packaged_data{\n            reconstructed_num_pts};\n        Variables<dg_package_field_tags> lower_packaged_data{\n            reconstructed_num_pts};\n\n        // Compute fluxes on faces\n        for (size_t i = 0; i < Dim; ++i) {\n          auto& vars_upper_face = gsl::at(package_data_argvars_upper_face, i);\n          auto& vars_lower_face = gsl::at(package_data_argvars_lower_face, i);\n          NewtonianEuler::subcell::compute_fluxes<Dim>(\n              make_not_null(&vars_upper_face));\n          NewtonianEuler::subcell::compute_fluxes<Dim>(\n              make_not_null(&vars_lower_face));\n\n          tnsr::i<DataVector, Dim, Frame::Inertial> lower_outward_conormal{\n              reconstructed_num_pts, 0.0};\n          lower_outward_conormal.get(i) = 1.0;\n\n          tnsr::i<DataVector, Dim, Frame::Inertial> upper_outward_conormal{\n              reconstructed_num_pts, 0.0};\n          upper_outward_conormal.get(i) = -1.0;\n\n          // Compute the packaged data\n          using dg_package_data_projected_tags = tmpl::append<\n              evolved_vars_tags, fluxes_tags, dg_package_data_temporary_tags,\n              typename DerivedCorrection::dg_package_data_primitive_tags>;\n          evolution::dg::Actions::detail::dg_package_data<system>(\n              make_not_null(&upper_packaged_data),\n              dynamic_cast<const DerivedCorrection&>(boundary_correction),\n              vars_upper_face, upper_outward_conormal, {std::nullopt}, *box,\n              typename DerivedCorrection::dg_package_data_volume_tags{},\n              dg_package_data_projected_tags{});\n\n          evolution::dg::Actions::detail::dg_package_data<system>(\n              make_not_null(&lower_packaged_data),\n              dynamic_cast<const DerivedCorrection&>(boundary_correction),\n              vars_lower_face, lower_outward_conormal, {std::nullopt}, *box,\n              typename DerivedCorrection::dg_package_data_volume_tags{},\n              dg_package_data_projected_tags{});\n\n          // Now need to check if any of our neighbors are doing DG,\n          // because if so then we need to use whatever boundary data\n          // they sent instead of what we computed locally.\n          //\n          // Note: We could check this beforehand to avoid the extra\n          // work of reconstruction and flux computations at the\n          // boundaries.\n          evolution::dg::subcell::correct_package_data<true>(\n              make_not_null(&lower_packaged_data),\n              make_not_null(&upper_packaged_data), i, element, subcell_mesh,\n              db::get<evolution::dg::Tags::MortarData<Dim>>(*box), 0);\n\n          // Compute the corrections on the faces. We only need to\n          // compute this once because we can just flip the normal\n          // vectors then\n          gsl::at(boundary_corrections, i).initialize(reconstructed_num_pts);\n          evolution::dg::subcell::compute_boundary_terms(\n              make_not_null(&gsl::at(boundary_corrections, i)),\n              dynamic_cast<const DerivedCorrection&>(boundary_correction),\n              upper_packaged_data, lower_packaged_data, db::as_access(*box),\n              typename DerivedCorrection::dg_boundary_terms_volume_tags{});\n        }\n      }\n    });\n\n    // Now compute the actual time derivatives.\n    using dt_variables_tag = db::add_tag_prefix<::Tags::dt, evolved_vars_tag>;\n    using source_argument_tags = tmpl::list<\n        Tags::MassDensityCons, Tags::MomentumDensity<Dim>, Tags::EnergyDensity,\n        hydro::Tags::SpatialVelocity<DataVector, Dim>,\n        hydro::Tags::Pressure<DataVector>,\n        hydro::Tags::SpecificInternalEnergy<DataVector>,\n        hydro::Tags::EquationOfState<false, 2>,\n        evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Inertial>,\n        ::Tags::Time, NewtonianEuler::Tags::SourceTerm<Dim>>;\n    db::mutate_apply<tmpl::list<dt_variables_tag>, source_argument_tags>(\n        [&num_pts, &boundary_corrections, &subcell_mesh, &one_over_delta_xi,\n         &cell_centered_logical_to_grid_inv_jacobian =\n             db::get<evolution::dg::subcell::fd::Tags::\n                         InverseJacobianLogicalToGrid<Dim>>(*box)](\n            const auto dt_vars_ptr, const Scalar<DataVector>& mass_density_cons,\n            const tnsr::I<DataVector, Dim>& momentum_density,\n            const Scalar<DataVector>& energy_density,\n            const tnsr::I<DataVector, Dim>& velocity,\n            const Scalar<DataVector>& pressure,\n            const Scalar<DataVector>& specific_internal_energy,\n            const EquationsOfState::EquationOfState<false, 2>& eos,\n            const tnsr::I<DataVector, Dim>& coords, const double time,\n            const Sources::Source<Dim>& source) {\n          dt_vars_ptr->initialize(num_pts, 0.0);\n          using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n          using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n          using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n\n          auto& dt_mass = get<::Tags::dt<MassDensityCons>>(*dt_vars_ptr);\n          auto& dt_momentum = get<::Tags::dt<MomentumDensity>>(*dt_vars_ptr);\n          auto& dt_energy = get<::Tags::dt<EnergyDensity>>(*dt_vars_ptr);\n\n          const auto eos_2d = eos.promote_to_2d_eos();\n          source(make_not_null(&dt_mass), make_not_null(&dt_momentum),\n                 make_not_null(&dt_energy), mass_density_cons, momentum_density,\n                 energy_density, velocity, pressure, specific_internal_energy,\n                 *eos_2d, coords, time);\n\n          for (size_t dim = 0; dim < Dim; ++dim) {\n            Scalar<DataVector>& mass_density_correction =\n                get<MassDensityCons>(gsl::at(boundary_corrections, dim));\n            Scalar<DataVector>& energy_density_correction =\n                get<EnergyDensity>(gsl::at(boundary_corrections, dim));\n            tnsr::I<DataVector, Dim, Frame::Inertial>&\n                momentum_density_correction =\n                    get<MomentumDensity>(gsl::at(boundary_corrections, dim));\n            evolution::dg::subcell::add_cartesian_flux_divergence(\n                make_not_null(&get(dt_mass)), gsl::at(one_over_delta_xi, dim),\n                cell_centered_logical_to_grid_inv_jacobian.get(dim, dim),\n                get(mass_density_correction), subcell_mesh.extents(), dim);\n            evolution::dg::subcell::add_cartesian_flux_divergence(\n                make_not_null(&get(dt_energy)), gsl::at(one_over_delta_xi, dim),\n                cell_centered_logical_to_grid_inv_jacobian.get(dim, dim),\n                get(energy_density_correction), subcell_mesh.extents(), dim);\n            for (size_t d = 0; d < Dim; ++d) {\n              evolution::dg::subcell::add_cartesian_flux_divergence(\n                  make_not_null(&dt_momentum.get(d)),\n                  gsl::at(one_over_delta_xi, dim),\n                  cell_centered_logical_to_grid_inv_jacobian.get(dim, dim),\n                  momentum_density_correction.get(d), subcell_mesh.extents(),\n                  dim);\n            }\n          }\n        },\n        box);\n  }\n\n private:\n  template <typename SourceTerm, typename... SourceTermArgs,\n            typename... SourcedVars>\n  static void sources_impl(std::tuple<gsl::not_null<Scalar<DataVector>*>,\n                                      gsl::not_null<tnsr::I<DataVector, Dim>*>,\n                                      gsl::not_null<Scalar<DataVector>*>>\n                               dt_vars,\n                           tmpl::list<SourcedVars...> /*meta*/,\n                           const SourceTerm& source,\n                           const SourceTermArgs&... source_term_args) {\n    using dt_vars_list =\n        tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<Dim>,\n                   Tags::EnergyDensity>;\n\n    source.apply(\n        std::get<tmpl::index_of<dt_vars_list, SourcedVars>::value>(dt_vars)...,\n        source_term_args...);\n  }\n};\n}  // namespace NewtonianEuler::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Characteristics.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TimeDerivativeTerms.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup EvolutionSystemsGroup\n/// \\brief Items related to evolving the Newtonian Euler system\nnamespace NewtonianEuler {\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = true;\n  static constexpr bool has_primitive_and_conservative_vars = true;\n  static constexpr size_t volume_dim = Dim;\n\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition<Dim>;\n\n  using variables_tag = ::Tags::Variables<tmpl::list<\n      Tags::MassDensityCons, Tags::MomentumDensity<Dim>, Tags::EnergyDensity>>;\n  using flux_variables =\n      tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<Dim>,\n                 Tags::EnergyDensity>;\n  using non_conservative_variables = tmpl::list<>;\n  using gradient_variables = tmpl::list<>;\n  // Compute item for pressure not currently implemented in SpECTRE,\n  // so its simple tag is passed along with the primitive variables.\n  using primitive_variables_tag = ::Tags::Variables<\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>>>;\n\n  using compute_volume_time_derivative_terms = TimeDerivativeTerms<Dim>;\n\n  using conservative_from_primitive = ConservativeFromPrimitive<Dim>;\n  using primitive_from_conservative = PrimitiveFromConservative<Dim>;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed<Dim>;\n};\n\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Source.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TagsDeclarations.hpp\"\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace NewtonianEuler {\n/// %OptionTags for the conservative formulation of the Newtonian Euler system\nnamespace OptionTags {\ntemplate <size_t Dim>\nstruct SourceTerm {\n  using type = std::unique_ptr<NewtonianEuler::Sources::Source<Dim>>;\n  static constexpr Options::String help = \"The source term to be used.\";\n  using group = ::evolution::OptionTags::SystemGroup;\n};\n}  // namespace OptionTags\n\n/// %Tags for the conservative formulation of the Newtonian Euler system\nnamespace Tags {\n/// The mass density of the fluid (as a conservative variable).\nstruct MassDensityCons : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The momentum density of the fluid.\ntemplate <size_t Dim, typename Fr>\nstruct MomentumDensity : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Fr>;\n  static std::string name() { return Frame::prefix<Fr>() + \"MomentumDensity\"; }\n};\n\n/// The energy density of the fluid.\nstruct EnergyDensity : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The sound speed.\ntemplate <typename DataType>\nstruct SoundSpeed : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The characteristic speeds.\ntemplate <size_t Dim>\nstruct CharacteristicSpeeds : db::SimpleTag {\n  using type = std::array<DataVector, Dim + 2>;\n};\n\n/// @{\n/// The characteristic fields of the NewtonianEuler system.\nstruct VMinus : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\ntemplate <size_t Dim>\nstruct VMomentum : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\nstruct VPlus : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n/// @}\n\n/// The internal energy density.\ntemplate <typename DataType>\nstruct InternalEnergyDensity : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The kinetic energy density.\ntemplate <typename DataType>\nstruct KineticEnergyDensity : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The local Mach number of the flow\ntemplate <typename DataType>\nstruct MachNumber : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The ram pressure of the fluid.\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct RamPressure : db::SimpleTag {\n  using type = tnsr::II<DataType, Dim, Fr>;\n  static std::string name() { return Frame::prefix<Fr>() + \"RamPressure\"; }\n};\n\n/// The specific kinetic energy.\ntemplate <typename DataType>\nstruct SpecificKineticEnergy : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The source term in the evolution equations\ntemplate <size_t Dim>\nstruct SourceTerm : db::SimpleTag {\n  using type = std::unique_ptr<NewtonianEuler::Sources::Source<Dim>>;\n\n  using option_tags = tmpl::list<OptionTags::SourceTerm<Dim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& source_term) {\n    return source_term->get_clone();\n  }\n};\n\n}  // namespace Tags\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n\n/// \\cond\nnamespace NewtonianEuler {\nnamespace Tags {\n\nstruct MassDensityCons;\ntemplate <size_t Dim, typename Fr = Frame::Inertial>\nstruct MomentumDensity;\nstruct EnergyDensity;\ntemplate <typename DataType>\nstruct SoundSpeed;\n\ntemplate <size_t Dim>\nstruct CharacteristicSpeeds;\nstruct VMinus;\ntemplate <size_t Dim>\nstruct VMomentum;\nstruct VPlus;\n\ntemplate <typename DataType>\nstruct InternalEnergyDensity;\ntemplate <typename DataType>\nstruct KineticEnergyDensity;\ntemplate <typename DataType>\nstruct MachNumber;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct RamPressure;\ntemplate <typename DataType>\nstruct SpecificKineticEnergy;\n\ntemplate <size_t Dim>\nstruct SourceTerm;\n}  // namespace Tags\n}  // namespace NewtonianEuler\n/// \\endcond\n"
  },
  {
    "path": "src/Evolution/Systems/NewtonianEuler/TimeDerivativeTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Source.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nclass DataVector;\n/// \\endcond\n\nnamespace NewtonianEuler {\nnamespace detail {\ntemplate <size_t Dim>\nvoid fluxes_impl(\n    gsl::not_null<tnsr::I<DataVector, Dim>*> mass_density_cons_flux,\n    gsl::not_null<tnsr::IJ<DataVector, Dim>*> momentum_density_flux,\n    gsl::not_null<tnsr::I<DataVector, Dim>*> energy_density_flux,\n    gsl::not_null<Scalar<DataVector>*> enthalpy_density,\n    const tnsr::I<DataVector, Dim>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& pressure);\n}  // namespace detail\n\n/*!\n * \\brief Compute the time derivative of the conserved variables for the\n * Newtonian Euler system\n */\ntemplate <size_t Dim>\nstruct TimeDerivativeTerms {\n private:\n  struct EnthalpyDensity : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  using temporary_tags = tmpl::list<EnthalpyDensity>;\n  using argument_tags =\n      tmpl::list<Tags::MassDensityCons, Tags::MomentumDensity<Dim>,\n                 Tags::EnergyDensity,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::EquationOfState<false, 2>,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>, ::Tags::Time,\n                 NewtonianEuler::Tags::SourceTerm<Dim>>;\n\n  static evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      // Time derivatives returned by reference. All the tags in the\n      // variables_tag in the system struct.\n      const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_mass_density,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*>\n          non_flux_terms_dt_momentum_density,\n      const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_energy_density,\n\n      // Fluxes returned by reference. Listed in the system struct as\n      // flux_variables.\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> mass_density_cons_flux,\n      const gsl::not_null<tnsr::IJ<DataVector, Dim>*> momentum_density_flux,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> energy_density_flux,\n\n      // Temporaries returned by reference. Listed in temporary_tags above.\n      const gsl::not_null<Scalar<DataVector>*> enthalpy_density,\n\n      // Arguments listed in argument_tags above\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density,\n      const tnsr::I<DataVector, Dim>& velocity,\n      const Scalar<DataVector>& pressure,\n      const Scalar<DataVector>& specific_internal_energy,\n      const EquationsOfState::EquationOfState<false, 2>& eos,\n      const tnsr::I<DataVector, Dim>& coords, const double time,\n      const Sources::Source<Dim>& source) {\n    detail::fluxes_impl(mass_density_cons_flux, momentum_density_flux,\n                        energy_density_flux, enthalpy_density, momentum_density,\n                        energy_density, velocity, pressure);\n\n    get(*non_flux_terms_dt_mass_density) = 0.0;\n    for (size_t i = 0; i < Dim; ++i) {\n      non_flux_terms_dt_momentum_density->get(i) = 0.0;\n    }\n    get(*non_flux_terms_dt_energy_density) = 0.0;\n    const auto eos_2d = eos.promote_to_2d_eos();\n    source(non_flux_terms_dt_mass_density, non_flux_terms_dt_momentum_density,\n           non_flux_terms_dt_energy_density, mass_density_cons,\n           momentum_density, energy_density, velocity, pressure,\n           specific_internal_energy, *eos_2d, coords, time);\n    return {true};\n  }\n};\n\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY RadiationTransport)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Tags.hpp\n  )\n\nadd_subdirectory(M1Grey)\nadd_subdirectory(NoNeutrinos)\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace RadiationTransport::M1Grey {\n/// \\brief Boundary conditions for the M1Grey radiation transport system\nnamespace BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\ntemplate <typename NeutrinoSpeciesList>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n\n  explicit BoundaryCondition(CkMigrateMessage* const msg)\n      : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\n  void pup(PUP::er& p) override {\n    domain::BoundaryConditions::BoundaryCondition::pup(p);\n  }\n};\n}  // namespace BoundaryConditions\n}  // namespace RadiationTransport::M1Grey\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  DirichletAnalytic.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  DirichletAnalytic.hpp\n  Factory.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace RadiationTransport::M1Grey::BoundaryConditions {\n\ntemplate <typename... NeutrinoSpecies>\nDirichletAnalytic<tmpl::list<NeutrinoSpecies...>>::DirichletAnalytic(\n    const DirichletAnalytic& rhs)\n    : BoundaryCondition<tmpl::list<NeutrinoSpecies...>>{dynamic_cast<\n          const BoundaryCondition<tmpl::list<NeutrinoSpecies...>>&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\ntemplate <typename... NeutrinoSpecies>\nDirichletAnalytic<tmpl::list<NeutrinoSpecies...>>&\nDirichletAnalytic<tmpl::list<NeutrinoSpecies...>>::operator=(\n    const DirichletAnalytic<tmpl::list<NeutrinoSpecies...>>& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\ntemplate <typename... NeutrinoSpecies>\nDirichletAnalytic<tmpl::list<NeutrinoSpecies...>>::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\ntemplate <typename... NeutrinoSpecies>\nvoid DirichletAnalytic<tmpl::list<NeutrinoSpecies...>>::pup(PUP::er& p) {\n  BoundaryCondition<tmpl::list<NeutrinoSpecies...>>::pup(p);\n  p | analytic_prescription_;\n}\n\ntemplate <typename... NeutrinoSpecies>\nstd::optional<std::string>\nDirichletAnalytic<tmpl::list<NeutrinoSpecies...>>::dg_ghost(\n    const gsl::not_null<typename Tags::TildeE<\n        Frame::Inertial, NeutrinoSpecies>::type*>... tilde_e,\n    const gsl::not_null<typename Tags::TildeS<\n        Frame::Inertial, NeutrinoSpecies>::type*>... tilde_s,\n\n    const gsl::not_null<typename ::Tags::Flux<\n        Tags::TildeE<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n        Frame::Inertial>::type*>... flux_tilde_e,\n    const gsl::not_null<typename ::Tags::Flux<\n        Tags::TildeS<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n        Frame::Inertial>::type*>... flux_tilde_s,\n\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<\n        tnsr::I<DataVector, 3, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    const double time) const {\n  using initial_data_list =\n      tmpl::append<RadiationTransport::M1Grey::AnalyticData::all_data,\n                   RadiationTransport::M1Grey::Solutions::all_solutions>;\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataVector>,\n                          hydro::Tags::SpatialVelocity<DataVector, 3>,\n                          Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                          Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...,\n                          gr::Tags::Lapse<DataVector>,\n                          gr::Tags::Shift<DataVector, 3>,\n                          gr::Tags::SpatialMetric<DataVector, 3>,\n                          gr::Tags::InverseSpatialMetric<DataVector, 3>>,\n      initial_data_list>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const initial_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*initial_data)>>) {\n          return initial_data->variables(\n              coords, time,\n              tmpl::list<hydro::Tags::LorentzFactor<DataVector>,\n                         hydro::Tags::SpatialVelocity<DataVector, 3>,\n                         Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                         Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...,\n                         gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>>{});\n\n        } else {\n          (void)time;\n          return initial_data->variables(\n              coords,\n              tmpl::list<hydro::Tags::LorentzFactor<DataVector>,\n                         hydro::Tags::SpatialVelocity<DataVector, 3>,\n                         Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                         Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...,\n                         gr::Tags::Lapse<DataVector>,\n                         gr::Tags::Shift<DataVector, 3>,\n                         gr::Tags::SpatialMetric<DataVector, 3>,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>>{});\n        }\n      });\n\n  *inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_values);\n\n  // Allocate the temporary tensors outside the loop over species\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempII<0, 3>,\n                       ::Tags::TempScalar<1>, ::Tags::TempScalar<2>,\n                       ::Tags::Tempi<0, 3>, ::Tags::TempI<0, 3>>>\n      buffer((*inv_spatial_metric)[0].size());\n\n  const auto assign_boundary_values_for_neutrino_species =\n      [&boundary_values, &inv_spatial_metric, &buffer](\n          const gsl::not_null<Scalar<DataVector>*> local_tilde_e,\n          const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n              local_tilde_s,\n          const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n              local_flux_tilde_e,\n          const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*>\n              local_flux_tilde_s,\n          auto species_v) {\n        using species = decltype(species_v);\n        using tilde_e_tag = Tags::TildeE<Frame::Inertial, species>;\n        using tilde_s_tag = Tags::TildeS<Frame::Inertial, species>;\n        *local_tilde_e = get<tilde_e_tag>(boundary_values);\n        *local_tilde_s = get<tilde_s_tag>(boundary_values);\n\n        // Compute pressure tensor tilde_p from the M1Closure\n        const auto& fluid_velocity =\n            get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_values);\n        const auto& fluid_lorentz_factor =\n            get<hydro::Tags::LorentzFactor<DataVector>>(boundary_values);\n        const auto& lapse = get<gr::Tags::Lapse<DataVector>>(boundary_values);\n        const auto& shift =\n            get<gr::Tags::Shift<DataVector, 3>>(boundary_values);\n        const auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_values);\n        auto& closure_factor = get<::Tags::TempScalar<0>>(buffer);\n        // The M1Closure reads in values from `closure_factor` as an\n        //   initial\n        // guess. We need to specify some value (else code fails in DEBUG\n        // mode when the temp Variables are initialized with NaNs)... for\n        //   now\n        // we use 0 because it's easy, but improvements may be possible.\n        get(closure_factor) = 0.;\n        auto& pressure_tensor = get<::Tags::TempII<0, 3>>(buffer);\n        auto& comoving_energy_density = get<::Tags::TempScalar<1>>(buffer);\n        auto& comoving_momentum_density_normal =\n            get<::Tags::TempScalar<2>>(buffer);\n        auto& comoving_momentum_density_spatial =\n            get<::Tags::Tempi<0, 3>>(buffer);\n\n        detail::compute_closure_impl(\n            make_not_null(&closure_factor), make_not_null(&pressure_tensor),\n            make_not_null(&comoving_energy_density),\n            make_not_null(&comoving_momentum_density_normal),\n            make_not_null(&comoving_momentum_density_spatial), *local_tilde_e,\n            *local_tilde_s, fluid_velocity, fluid_lorentz_factor,\n            spatial_metric, *inv_spatial_metric);\n        // Store det of metric in (otherwise unused) comoving_energy_density\n        get(comoving_energy_density) = get(determinant(spatial_metric));\n        for (auto& component : pressure_tensor) {\n          component *= get(comoving_energy_density);\n        }\n        const auto& tilde_p = pressure_tensor;\n\n        auto& tilde_s_M = get<::Tags::TempI<0, 3>>(buffer);\n        detail::compute_fluxes_impl(local_flux_tilde_e, local_flux_tilde_s,\n                                    &tilde_s_M, *local_tilde_e, *local_tilde_s,\n                                    tilde_p, lapse, shift, spatial_metric,\n                                    *inv_spatial_metric);\n        return '0';\n      };\n\n  expand_pack(assign_boundary_values_for_neutrino_species(\n      tilde_e, tilde_s, flux_tilde_e, flux_tilde_s, NeutrinoSpecies{})...);\n\n  return {};\n}\n\ntemplate <typename... NeutrinoSpecies>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic<tmpl::list<NeutrinoSpecies...>>::my_PUP_ID =\n    0;\n\ntemplate class DirichletAnalytic<tmpl::list<neutrinos::ElectronNeutrinos<1>>>;\n\ntemplate class DirichletAnalytic<tmpl::list<\n    neutrinos::ElectronNeutrinos<1>, neutrinos::ElectronAntiNeutrinos<1>>>;\n\n}  // namespace RadiationTransport::M1Grey::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Fluxes.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1Closure.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace RadiationTransport::M1Grey::BoundaryConditions {\n\n/// \\cond\ntemplate <typename NeutrinoSpeciesList>\nclass DirichletAnalytic;\n/// \\endcond\n\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\ntemplate <typename... NeutrinoSpecies>\nclass DirichletAnalytic<tmpl::list<NeutrinoSpecies...>> final\n    : public BoundaryCondition<tmpl::list<NeutrinoSpecies...>> {\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n\n  using options = tmpl::list<AnalyticPrescription>;\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions using either analytic solution or \"\n      \"analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg)\n      : BoundaryCondition<tmpl::list<NeutrinoSpecies...>>(msg) {}\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override {\n    return std::make_unique<DirichletAnalytic>(*this);\n  }\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<typename Tags::TildeE<\n          Frame::Inertial, NeutrinoSpecies>::type*>... tilde_e,\n      const gsl::not_null<typename Tags::TildeS<\n          Frame::Inertial, NeutrinoSpecies>::type*>... tilde_s,\n\n      const gsl::not_null<typename ::Tags::Flux<\n          Tags::TildeE<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n          Frame::Inertial>::type*>... flux_tilde_e,\n      const gsl::not_null<typename ::Tags::Flux<\n          Tags::TildeS<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n          Frame::Inertial>::type*>... flux_tilde_s,\n\n      const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<\n          tnsr::I<DataVector, 3, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& coords, double time) const;\n\n private:\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace RadiationTransport::M1Grey::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RadiationTransport::M1Grey::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\ntemplate <typename NeutrinoSpeciesList>\nusing standard_boundary_conditions =\n    tmpl::list<DirichletAnalytic<NeutrinoSpeciesList>,\n               domain::BoundaryConditions::Periodic<\n                   BoundaryCondition<NeutrinoSpeciesList>>>;\n}  // namespace RadiationTransport::M1Grey::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Rusanov.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  NamespaceDocs.hpp\n  Rusanov.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Rusanov.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RadiationTransport::M1Grey::BoundaryCorrections {\ntemplate <typename NeutrinoSpeciesList>\nusing standard_boundary_corrections = tmpl::list<Rusanov<NeutrinoSpeciesList>>;\n}  // namespace RadiationTransport::M1Grey::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace RadiationTransport::M1Grey::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Rusanov.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace RadiationTransport::M1Grey::BoundaryCorrections::Rusanov_detail {\n\nvoid dg_package_data_impl(\n    const gsl::not_null<Scalar<DataVector>*> packaged_tilde_e,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        packaged_tilde_s,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_e,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        packaged_normal_dot_flux_tilde_s,\n    const Scalar<DataVector>& tilde_e,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_e,\n    const tnsr::Ij<DataVector, 3, Frame::Inertial>& flux_tilde_s,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n    const std::optional<\n        tnsr::I<DataVector, 3, Frame::Inertial>>& /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& /*normal_dot_mesh_velocity*/) {\n  *packaged_tilde_e = tilde_e;\n  *packaged_tilde_s = tilde_s;\n\n  normal_dot_flux(packaged_normal_dot_flux_tilde_e, normal_covector,\n                  flux_tilde_e);\n  normal_dot_flux(packaged_normal_dot_flux_tilde_s, normal_covector,\n                  flux_tilde_s);\n}\n\nvoid dg_boundary_terms_impl(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_e,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        boundary_correction_tilde_s,\n    const Scalar<DataVector>& tilde_e_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_e_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_s_int,\n    const Scalar<DataVector>& tilde_e_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_e_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_s_ext,\n    dg::Formulation dg_formulation) {\n  constexpr double max_abs_char_speed = 1.0;\n  if (dg_formulation == dg::Formulation::WeakInertial) {\n    get(*boundary_correction_tilde_e) =\n        0.5 * (get(normal_dot_flux_tilde_e_int) -\n               get(normal_dot_flux_tilde_e_ext)) -\n        0.5 * max_abs_char_speed * (get(tilde_e_ext) - get(tilde_e_int));\n    for (size_t i = 0; i < 3; ++i) {\n      boundary_correction_tilde_s->get(i) =\n          0.5 * (normal_dot_flux_tilde_s_int.get(i) -\n                 normal_dot_flux_tilde_s_ext.get(i)) -\n          0.5 * max_abs_char_speed * (tilde_s_ext.get(i) - tilde_s_int.get(i));\n    }\n  } else {\n    get(*boundary_correction_tilde_e) =\n        -0.5 * (get(normal_dot_flux_tilde_e_int) +\n                get(normal_dot_flux_tilde_e_ext)) -\n        0.5 * max_abs_char_speed * (get(tilde_e_ext) - get(tilde_e_int));\n    for (size_t i = 0; i < 3; ++i) {\n      boundary_correction_tilde_s->get(i) =\n          -0.5 * (normal_dot_flux_tilde_s_int.get(i) +\n                  normal_dot_flux_tilde_s_ext.get(i)) -\n          0.5 * max_abs_char_speed * (tilde_s_ext.get(i) - tilde_s_int.get(i));\n    }\n  }\n}\n\n}  // namespace RadiationTransport::M1Grey::BoundaryCorrections::Rusanov_detail\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Rusanov.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace RadiationTransport::M1Grey::BoundaryCorrections {\n\nnamespace Rusanov_detail {\nvoid dg_package_data_impl(\n    gsl::not_null<Scalar<DataVector>*> packaged_tilde_e,\n    gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> packaged_tilde_s,\n    gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_tilde_e,\n    gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        packaged_normal_dot_flux_tilde_s,\n    const Scalar<DataVector>& tilde_e,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& flux_tilde_e,\n    const tnsr::Ij<DataVector, 3, Frame::Inertial>& flux_tilde_s,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity);\n\nvoid dg_boundary_terms_impl(\n    gsl::not_null<Scalar<DataVector>*> boundary_correction_tilde_e,\n    gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        boundary_correction_tilde_s,\n    const Scalar<DataVector>& tilde_e_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_int,\n    const Scalar<DataVector>& normal_dot_flux_tilde_e_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_s_int,\n    const Scalar<DataVector>& tilde_e_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s_ext,\n    const Scalar<DataVector>& normal_dot_flux_tilde_e_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_dot_flux_tilde_s_ext,\n    dg::Formulation dg_formulation);\n}  // namespace Rusanov_detail\n\n/// \\cond\ntemplate <typename NeutrinoSpeciesList>\nclass Rusanov;\n/// \\endcond\n\n/*!\n * \\brief A Rusanov/local Lax-Friedrichs Riemann solver\n *\n * Let \\f$U\\f$ be the state vector of evolved variables, \\f$F^i\\f$ the\n * corresponding fluxes, and \\f$n_i\\f$ be the outward directed unit normal to\n * the interface. Denoting \\f$F := n_i F^i\\f$, the %Rusanov boundary correction\n * is\n *\n * \\f{align*}\n * G_\\text{Rusanov} = \\frac{F_\\text{int} - F_\\text{ext}}{2} -\n * \\frac{\\text{max}\\left(\\{|\\lambda_\\text{int}|\\},\n * \\{|\\lambda_\\text{ext}|\\}\\right)}{2} \\left(U_\\text{ext} - U_\\text{int}\\right),\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior, and\n * \\f$\\{|\\lambda|\\}\\f$ is the set of characteristic/signal speeds. The minus\n * sign in front of the \\f$F_{\\text{ext}}\\f$ is necessary because the outward\n * directed normal of the neighboring element has the opposite sign, i.e.\n * \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$.\n *\n * For radiation, the max characteristic/signal speed is 1 (the speed of light).\n * Note that the characteristic speeds of this system are *not* yet fully\n * implemented, see the warning in the documentation for the characteristics.\n *\n * \\note In the strong form the `dg_boundary_terms` function returns\n * \\f$G - F_\\text{int}\\f$\n */\ntemplate <typename... NeutrinoSpecies>\nclass Rusanov<tmpl::list<NeutrinoSpecies...>> final\n    : public evolution::BoundaryCorrection {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the Rusanov or local Lax-Friedrichs boundary correction term \"\n      \"for the M1Grey radiation transport system.\"};\n\n  Rusanov() = default;\n  Rusanov(const Rusanov&) = default;\n  Rusanov& operator=(const Rusanov&) = default;\n  Rusanov(Rusanov&&) = default;\n  Rusanov& operator=(Rusanov&&) = default;\n  ~Rusanov() override = default;\n\n  /// \\cond\n  explicit Rusanov(CkMigrateMessage* msg) : BoundaryCorrection(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Rusanov);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override {  // NOLINT\n    BoundaryCorrection::pup(p);\n  }\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override {\n    return std::make_unique<Rusanov>(*this);\n  }\n\n  using dg_package_field_tags = tmpl::list<\n      Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n      Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...,\n      ::Tags::NormalDotFlux<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>>...,\n      ::Tags::NormalDotFlux<Tags::TildeS<Frame::Inertial, NeutrinoSpecies>>...>;\n  using dg_package_data_temporary_tags = tmpl::list<>;\n  using dg_package_data_primitive_tags = tmpl::list<>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      const gsl::not_null<typename Tags::TildeE<\n          Frame::Inertial, NeutrinoSpecies>::type*>... packaged_tilde_e,\n      const gsl::not_null<typename Tags::TildeS<\n          Frame::Inertial, NeutrinoSpecies>::type*>... packaged_tilde_s,\n      const gsl::not_null<typename Tags::TildeE<\n          Frame::Inertial,\n          NeutrinoSpecies>::type*>... packaged_normal_dot_flux_tilde_e,\n      const gsl::not_null<typename Tags::TildeS<\n          Frame::Inertial,\n          NeutrinoSpecies>::type*>... packaged_normal_dot_flux_tilde_s,\n\n      const typename Tags::TildeE<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_e,\n      const typename Tags::TildeS<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_s,\n      const typename ::Tags::Flux<\n          Tags::TildeE<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n          Frame::Inertial>::type&... flux_tilde_e,\n      const typename ::Tags::Flux<\n          Tags::TildeS<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n          Frame::Inertial>::type&... flux_tilde_s,\n\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    EXPAND_PACK_LEFT_TO_RIGHT(Rusanov_detail::dg_package_data_impl(\n        packaged_tilde_e, packaged_tilde_s, packaged_normal_dot_flux_tilde_e,\n        packaged_normal_dot_flux_tilde_s, tilde_e, tilde_s, flux_tilde_e,\n        flux_tilde_s, normal_covector, normal_vector, mesh_velocity,\n        normal_dot_mesh_velocity));\n    // max speed = speed of light\n    return 1.0;\n  }\n\n  void dg_boundary_terms(\n      const gsl::not_null<typename Tags::TildeE<\n          Frame::Inertial,\n          NeutrinoSpecies>::type*>... boundary_correction_tilde_e,\n      const gsl::not_null<typename Tags::TildeS<\n          Frame::Inertial,\n          NeutrinoSpecies>::type*>... boundary_correction_tilde_s,\n      const typename Tags::TildeE<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_e_int,\n      const typename Tags::TildeS<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_s_int,\n      const typename Tags::TildeE<Frame::Inertial, NeutrinoSpecies>::\n          type&... normal_dot_flux_tilde_e_int,\n      const typename Tags::TildeS<Frame::Inertial, NeutrinoSpecies>::\n          type&... normal_dot_flux_tilde_s_int,\n      const typename Tags::TildeE<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_e_ext,\n      const typename Tags::TildeS<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_s_ext,\n      const typename Tags::TildeE<Frame::Inertial, NeutrinoSpecies>::\n          type&... normal_dot_flux_tilde_e_ext,\n      const typename Tags::TildeS<Frame::Inertial, NeutrinoSpecies>::\n          type&... normal_dot_flux_tilde_s_ext,\n      dg::Formulation dg_formulation) const {\n    EXPAND_PACK_LEFT_TO_RIGHT(Rusanov_detail::dg_boundary_terms_impl(\n        boundary_correction_tilde_e, boundary_correction_tilde_s, tilde_e_int,\n        tilde_s_int, normal_dot_flux_tilde_e_int, normal_dot_flux_tilde_s_int,\n        tilde_e_ext, tilde_s_ext, normal_dot_flux_tilde_e_ext,\n        normal_dot_flux_tilde_s_ext, dg_formulation));\n  }\n};\n\n/// \\cond\ntemplate <typename... NeutrinoSpecies>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Rusanov<tmpl::list<NeutrinoSpecies...>>::my_PUP_ID = 0;\n/// \\endcond\n\n}  // namespace RadiationTransport::M1Grey::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY M1Grey)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Characteristics.cpp\n  Fluxes.cpp\n  M1Closure.cpp\n  M1HydroCoupling.cpp\n  Sources.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Characteristics.hpp\n  Fluxes.hpp\n  Initialize.hpp\n  M1Closure.hpp\n  M1HydroCoupling.hpp\n  Sources.hpp\n  System.hpp\n  Tags.hpp\n  TimeDerivativeTerms.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Events\n  Evolution\n  GeneralRelativity\n  Hydro\n  Imex\n  LinearOperators\n  M1GreyAnalyticData\n  M1GreySolutions\n  Serialization\n  Utilities\n  INTERFACE\n  Parallel\n  Initialization\n  PRIVATE\n  RootFinding\n  )\n\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(Instantiations)\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Characteristics.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace RadiationTransport {\nnamespace M1Grey {\n\nvoid characteristic_speeds(\n    const gsl::not_null<std::array<DataVector, 4>*> pchar_speeds,\n    const Scalar<DataVector>& lapse) {\n  const size_t num_grid_points = get(lapse).size();\n  auto& char_speeds = *pchar_speeds;\n  if (char_speeds[0].size() != num_grid_points) {\n    char_speeds[0] = DataVector(num_grid_points);\n  }\n  char_speeds[0] = 1.;\n  if (char_speeds[1].size() != num_grid_points) {\n    char_speeds[1] = DataVector(num_grid_points);\n  }\n  char_speeds[1] = -1.;\n  for (size_t i = 2; i < 4; i++) {\n    gsl::at(char_speeds, i) = char_speeds[0];\n  }\n}\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\nclass DataVector;\n/// \\endcond\n\nnamespace RadiationTransport {\nnamespace M1Grey {\n\n/*!\n * \\brief Compute the characteristic speeds for the M1 system\n *\n * At this point, for testing purposes, we just set all\n * speeds to 1... this needs to be fixed in the future to\n * use the correct speed values.\n */\nvoid characteristic_speeds(\n    gsl::not_null<std::array<DataVector, 4>*> pchar_speeds,\n    const Scalar<DataVector>& lapse);\n\nnamespace Tags {\n/// \\brief Compute the characteristic speeds for the M1 system\n///\nstruct CharacteristicSpeedsCompute : Tags::CharacteristicSpeeds,\n                                     db::ComputeTag {\n  using base = Tags::CharacteristicSpeeds;\n  using argument_tags = tmpl::list<gr::Tags::Lapse<DataVector>>;\n\n  using return_type = std::array<DataVector, 4>;\n\n  static constexpr auto function = characteristic_speeds;\n};\n}  // namespace Tags\n\nstruct ComputeLargestCharacteristicSpeed {\n  using argument_tags = tmpl::list<>;\n  static double apply() { return 1.0; }\n};\n\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Fluxes.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace RadiationTransport {\nnamespace M1Grey {\n\nnamespace detail {\nvoid compute_fluxes_impl(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_s_M,\n    const Scalar<DataVector>& tilde_e,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& tilde_p,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric) {\n  constexpr size_t spatial_dim = 3;\n\n  raise_or_lower_index(tilde_s_M, tilde_s, inv_spatial_metric);\n\n  for (size_t i = 0; i < spatial_dim; ++i) {\n    tilde_e_flux->get(i) =\n        get(lapse) * tilde_s_M->get(i) - shift.get(i) * get(tilde_e);\n    for (size_t j = 0; j < spatial_dim; ++j) {\n      tilde_s_flux->get(i, j) = -shift.get(i) * tilde_s.get(j);\n      for (size_t k = 0; k < spatial_dim; ++k) {\n        tilde_s_flux->get(i, j) +=\n            get(lapse) * tilde_p.get(i, k) * spatial_metric.get(j, k);\n      }\n    }\n  }\n}\n\n}  // namespace detail\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Fluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RadiationTransport {\nnamespace M1Grey {\n\n// Implementation of the M1 fluxes for individual neutrino species\nnamespace detail {\nvoid compute_fluxes_impl(\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_s_M,\n    const Scalar<DataVector>& tilde_e,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& tilde_p,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric);\n}  // namespace detail\n\n/*!\n * \\brief The fluxes of the conservative variables in the M1 scheme\n *\n * \\f{align*}\n * F^i({\\tilde E}) = &~ \\alpha \\gamma^{ij} {\\tilde S}_j - \\beta^j {\\tilde E} \\\\\n * F^i({\\tilde S}_j) = &~ \\alpha {\\tilde P}^{ik} \\gamma_{kj} - \\beta^i {\\tilde\n * S}_j \\f}\n *\n * where the conserved variables \\f${\\tilde E}\\f$, \\f${\\tilde S}_i\\f$,\n * are a generalized mass-energy density and momentum density.\n * Furthermore, \\f${\\tilde P^{ij}}\\f$ is the pressure tensor density of the\n * radiation field, \\f$\\alpha\\f$ is the lapse, \\f$\\beta^i\\f$ is the shift,\n * \\f$\\gamma_{ij}\\f$ the 3-metric, and \\f$\\gamma^{ij}\\f$ its inverse.\n *\n * In the main function, we loop over all neutrino species, and then call\n * the actual implementation of the fluxes.\n */\ntemplate <typename... NeutrinoSpecies>\nstruct ComputeFluxes {\n  using return_tags =\n      tmpl::list<::Tags::Flux<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>,\n                              tmpl::size_t<3>, Frame::Inertial>...,\n                 ::Tags::Flux<Tags::TildeS<Frame::Inertial, NeutrinoSpecies>,\n                              tmpl::size_t<3>, Frame::Inertial>...>;\n\n  using argument_tags =\n      tmpl::list<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeP<Frame::Inertial, NeutrinoSpecies>...,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n\n  static void apply(\n      const gsl::not_null<typename ::Tags::Flux<\n          Tags::TildeE<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n          Frame::Inertial>::type*>... tilde_e_flux,\n      const gsl::not_null<typename ::Tags::Flux<\n          Tags::TildeS<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n          Frame::Inertial>::type*>... tilde_s_flux,\n      const typename Tags::TildeE<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_e,\n      const typename Tags::TildeS<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_s,\n      const typename Tags::TildeP<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_p,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric) {\n    // Allocate memory for tildeS^i\n    tnsr::I<DataVector, 3, Frame::Inertial> tilde_s_M(get(lapse).size());\n    EXPAND_PACK_LEFT_TO_RIGHT(detail::compute_fluxes_impl(\n        tilde_e_flux, tilde_s_flux, &tilde_s_M, tilde_e, tilde_s, tilde_p,\n        lapse, shift, spatial_metric, inv_spatial_metric));\n  }\n};\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/Factory.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\ntemplate <size_t VolumeDim>\nstruct Mesh;\n}  // namespace Tags\n}  // namespace domain\n\n/// \\endcond\n\nnamespace RadiationTransport {\nnamespace M1Grey {\nnamespace Actions {\n\ntemplate <typename System>\nstruct InitializeM1Tags {\n  using evolved_variables_tag = typename System::variables_tag;\n  using hydro_variables_tag = typename System::hydro_variables_tag;\n  using m1_variables_tag = typename System::primitive_variables_tag;\n  // List of variables to be created... does NOT include\n  // evolved_variables_tag because the evolved variables\n  // are created by the ConservativeSystem initialization.\n  using simple_tags = tmpl::list<hydro_variables_tag, m1_variables_tag>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    using EvolvedVars = typename evolved_variables_tag::type;\n    using HydroVars = typename hydro_variables_tag::type;\n    using M1Vars = typename m1_variables_tag::type;\n\n    static constexpr size_t dim = System::volume_dim;\n    const double initial_time = db::get<::Tags::Time>(box);\n    const size_t num_grid_points =\n        db::get<domain::Tags::Mesh<dim>>(box).number_of_grid_points();\n    const auto& inertial_coords =\n        db::get<domain::Tags::Coordinates<dim, Frame::Inertial>>(box);\n\n    using initial_data_evo_tags = typename evolved_variables_tag::tags_list;\n    using initial_data_hydro_tags = typename hydro_variables_tag::tags_list;\n\n    using initial_data_list =\n        tmpl::append<RadiationTransport::M1Grey::AnalyticData::all_data,\n                     RadiationTransport::M1Grey::Solutions::all_solutions>;\n\n    const auto initial_data_evo_and_hydro_vars = call_with_dynamic_type<\n        tuples::tagged_tuple_from_typelist<\n            tmpl ::append<initial_data_evo_tags, initial_data_hydro_tags>>,\n        initial_data_list>(\n        &Parallel::get<evolution::initial_data::Tags::InitialData>(cache),\n        [&inertial_coords, &initial_time](const auto* const initial_data) {\n          return evolution::Initialization::initial_data(\n              *initial_data, inertial_coords, initial_time,\n              tmpl::append<initial_data_evo_tags, initial_data_hydro_tags>{});\n        });\n\n    db::mutate<evolved_variables_tag>(\n        [&initial_data_evo_and_hydro_vars](\n            const gsl::not_null<EvolvedVars*> evolved_vars) {\n          evolved_vars->assign_subset(initial_data_evo_and_hydro_vars);\n        },\n        make_not_null(&box));\n\n    // Get hydro variables\n    HydroVars hydro_variables{num_grid_points};\n\n    hydro_variables.assign_subset(initial_data_evo_and_hydro_vars);\n\n    M1Vars m1_variables{num_grid_points, -1.};\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box),\n                                               std::move(hydro_variables),\n                                               std::move(m1_variables));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  Imex.cpp\n  TimeStepping.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate class Events::ObserveTimeStep<RadiationTransport::M1Grey::System<\n    tmpl::list<neutrinos::ElectronNeutrinos<1>>>>;\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Instantiations/Imex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Imex/CleanHistory.hpp\"\n#include \"Evolution/Imex/CleanHistory.tpp\"\n#include \"Evolution/Imex/SolveImplicitSector.hpp\"\n#include \"Evolution/Imex/SolveImplicitSector.tpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nusing m1system = RadiationTransport::M1Grey::System<\n    tmpl::list<neutrinos::ElectronNeutrinos<1>>>;\n\ntemplate struct imex::CleanHistory<m1system>;\ntemplate struct imex::SolveImplicitSector<\n    m1system::variables_tag,\n    m1system::ImplicitSector<neutrinos::ElectronNeutrinos<1>>>;\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate class ChangeTimeStepperOrder<RadiationTransport::M1Grey::System<\n    tmpl::list<neutrinos::ElectronNeutrinos<1>>>>;\ntemplate class CleanHistory<RadiationTransport::M1Grey::System<\n    tmpl::list<neutrinos::ElectronNeutrinos<1>>>>;\ntemplate class RecordTimeStepperData<RadiationTransport::M1Grey::System<\n    tmpl::list<neutrinos::ElectronNeutrinos<1>>>>;\ntemplate class UpdateU<RadiationTransport::M1Grey::System<\n                           tmpl::list<neutrinos::ElectronNeutrinos<1>>>,\n                       false>;\ntemplate class UpdateU<RadiationTransport::M1Grey::System<\n                           tmpl::list<neutrinos::ElectronNeutrinos<1>>>,\n                       true>;\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/M1Closure.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1Closure.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct MomentumUp : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n};\nstruct MomentumSquared : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n// Minerbo (maximum entropy) closure for the M1 scheme\ndouble minerbo_closure_function(const double zeta) {\n  return 1.0 / 3.0 +\n         square(zeta) * (0.4 - 2.0 / 15.0 * zeta + 0.4 * square(zeta));\n}\n}  // namespace\n\nnamespace RadiationTransport::M1Grey::detail {\n\nvoid compute_closure_impl(\n    const gsl::not_null<Scalar<DataVector>*> closure_factor,\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        pressure_tensor,\n    const gsl::not_null<Scalar<DataVector>*> comoving_energy_density,\n    const gsl::not_null<Scalar<DataVector>*> comoving_momentum_density_normal,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        comoving_momentum_density_spatial,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& momentum_density,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& fluid_velocity,\n    const Scalar<DataVector>& fluid_lorentz_factor,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric) {\n  // Small number used to avoid divisions by zero\n  static constexpr double avoid_divisions_by_zero = 1.e-150;\n  // Below small_velocity, we use the v=0 closure,\n  // and we do not differentiate between fluid/inertial frames\n  static constexpr double small_velocity = 1.e-15;\n  // Dimension of spatial tensors\n  constexpr size_t spatial_dim = 3;\n  // Tolerance used in the rootfinding used to find the closure factor\n  constexpr double root_find_tolerance = 1.e-14;\n  Variables<\n      tmpl::list<hydro::Tags::LorentzFactorSquared<DataVector>, MomentumSquared,\n                 MomentumUp, hydro::Tags::SpatialVelocityOneForm<DataVector, 3>,\n                 hydro::Tags::SpatialVelocitySquared<DataVector>>>\n      temp_closure_tensors(get(energy_density).size());\n\n  // The main calculation needed for the M1 closure is to find the\n  // roots of J^2 zeta^2 = H^a H_a, with J the fluid-frame energy density\n  // and H^a the fluid-frame momentum density (0th and 1st moments).\n\n  // We first compute various fluid quantities and contractions\n  // with inertial moments needed even if v^2 is small\n  auto& w_sqr =\n      get<hydro::Tags::LorentzFactorSquared<DataVector>>(temp_closure_tensors);\n  get(w_sqr) = square(get(fluid_lorentz_factor));\n  auto& v_sqr = get<hydro::Tags::SpatialVelocitySquared<DataVector>>(\n      temp_closure_tensors);\n  get(v_sqr) = 1. - 1. / get(w_sqr);\n  // S^i, the neutrino momentum tensor\n  auto& s_M = get<MomentumUp>(temp_closure_tensors);\n  raise_or_lower_index(make_not_null(&s_M), momentum_density,\n                       inv_spatial_metric);\n  // S^i S_i\n  auto& s_sqr = get<MomentumSquared>(temp_closure_tensors);\n  dot_product(make_not_null(&s_sqr), s_M, momentum_density);\n  // v_i, the spatial velocity one-form of the fluid\n  auto& v_m = get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(\n      temp_closure_tensors);\n  raise_or_lower_index(make_not_null(&v_m), fluid_velocity, spatial_metric);\n\n  // Allocate memory for H^i\n  tnsr::I<double, 3, Frame::Inertial> H_M(0.);\n  // Loop over points\n  for (size_t s = 0; s < get(v_sqr).size(); ++s) {\n    const double& v_sqr_pt = get(v_sqr)[s];\n    const double& w_sqr_pt = get(w_sqr)[s];\n    const double& e_pt = get(energy_density)[s];\n    const double& s_sqr_pt = std::max(get(s_sqr)[s], avoid_divisions_by_zero);\n    // Ignore complicated closure calculations\n    // if the fluid velocity is very small\n    if (v_sqr_pt < small_velocity) {\n      // Minerbo closure assuming v=0 (see definition of\n      // minerbo_closure_function)\n      const double zeta = sqrt(s_sqr_pt) / e_pt;\n      const double chi = minerbo_closure_function(zeta);\n      const double d_thin_e_pt_over_s_sqr = (1.5 * chi - 0.5) * e_pt / s_sqr_pt;\n      const double d_thick_e_pt_over_3 = (0.5 - 0.5 * chi) * e_pt;\n\n      get(*closure_factor)[s] = zeta;\n      get(*comoving_energy_density)[s] = e_pt;\n      get(*comoving_momentum_density_normal)[s] = 0.;\n      for (size_t i = 0; i < spatial_dim; i++) {\n        comoving_momentum_density_spatial->get(i)[s] =\n            momentum_density.get(i)[s];\n        // Closure assuming that fluid-frame = inertial frame\n        for (size_t j = i; j < spatial_dim; j++) {\n          pressure_tensor->get(i, j)[s] =\n              d_thick_e_pt_over_3 * inv_spatial_metric.get(i, j)[s] +\n              d_thin_e_pt_over_s_sqr * s_M.get(i)[s] * s_M.get(j)[s];\n        }\n      }\n    }\n    // If the fluid velocity cannot be ignored, we need to\n    // go through a more expensive closure calculation\n    else {\n      // Compute useful intermediate quantities from v^i, F_i\n      double v_dot_f_pt = 0.;\n      for (size_t m = 0; m < spatial_dim; m++) {\n        v_dot_f_pt += fluid_velocity.get(m)[s] * momentum_density.get(m)[s];\n      }\n      // Decomposition of the fluid-frame energy density:\n      // J = J0 + d_thin * JThin + d_thick * JThick\n      // with d_thin, d_thick=1-d_thin coefficients\n      // obtained from the M1 closure.\n      const double j_0 = w_sqr_pt * (e_pt - 2. * v_dot_f_pt);\n      const double j_thin = w_sqr_pt * e_pt * square(v_dot_f_pt) / s_sqr_pt;\n      const double j_thick =\n          (w_sqr_pt - 1.) / (1. + 2. * w_sqr_pt) *\n          (4. * w_sqr_pt * v_dot_f_pt + e_pt * (3. - 2. * w_sqr_pt));\n      // Decomposition of the fluid-frame momentum density:\n      // H_a = -( h0T + d_thick hThickT + d_thin hThinT) t_a\n      //  - ( h0V + d_thick hThickV + d_thin hThinV) v_a\n      //  - ( h0F + d_thick hThickF + d_thin hThinF) F_a\n      // with t_a the unit normal, v_a the 3-velocity, and F_a the\n      // inertial frame momentum density. This is a decomposition of\n      // convenience, which is not unique: F_a and v_a are not\n      // orthogonal vectors, but both are normal to t_a.\n      const double& w_pt = get(fluid_lorentz_factor)[s];\n      const double h_0_t = w_pt * (j_0 + v_dot_f_pt - e_pt);\n      const double h_0_v = w_pt * j_0;\n      const double h_0_f = -w_pt;\n      const double h_thin_t = w_pt * j_thin;\n      const double h_thin_v = h_thin_t;\n      const double h_thin_f = w_pt * e_pt * v_dot_f_pt / s_sqr_pt;\n      const double h_thick_t = w_pt * j_thick;\n      const double h_thick_v =\n          h_thick_t +\n          w_pt / (2. * w_sqr_pt + 1.) *\n              ((3. - 2. * w_sqr_pt) * e_pt + (2. * w_sqr_pt - 1.) * v_dot_f_pt);\n      const double h_thick_f = w_pt * v_sqr_pt;\n      // Quantities needed for the computation of H^2 = H^a H_a,\n      // independent of zeta. We write:\n      // H^2 = h_sqr_0 + h_sqr_thin * d_thin + h_sqr_thick*d_thick\n      // + h_sqr_thin_thin * d_thin^2 + h_sqr_thick_thick * d_thick^2\n      // + h_sqr_thin_thick * d_thin * d_thick;\n      const double h_sqr_0 = -square(h_0_t) + square(h_0_v) * v_sqr_pt +\n                             square(h_0_f) * s_sqr_pt +\n                             2. * h_0_v * h_0_f * v_dot_f_pt;\n      const double h_sqr_thin =\n          2. * (h_0_v * h_thin_v * v_sqr_pt + h_0_f * h_thin_f * s_sqr_pt +\n                h_0_v * h_thin_f * v_dot_f_pt + h_0_f * h_thin_v * v_dot_f_pt -\n                h_0_t * h_thin_t);\n      const double h_sqr_thick =\n          2. * (h_0_v * h_thick_v * v_sqr_pt + h_0_f * h_thick_f * s_sqr_pt +\n                h_0_v * h_thick_f * v_dot_f_pt +\n                h_0_f * h_thick_v * v_dot_f_pt - h_0_t * h_thick_t);\n      const double h_sqr_thin_thick =\n          2. *\n          (h_thin_v * h_thick_v * v_sqr_pt + h_thin_f * h_thick_f * s_sqr_pt +\n           h_thin_v * h_thick_f * v_dot_f_pt +\n           h_thin_f * h_thick_v * v_dot_f_pt - h_thin_t * h_thick_t);\n      const double h_sqr_thick_thick =\n          square(h_thick_v) * v_sqr_pt + square(h_thick_f) * s_sqr_pt +\n          2. * h_thick_v * h_thick_f * v_dot_f_pt - square(h_thick_t);\n      const double h_sqr_thin_thin =\n          square(h_thin_v) * v_sqr_pt + square(h_thin_f) * s_sqr_pt +\n          2. * h_thin_v * h_thin_f * v_dot_f_pt - square(h_thin_t);\n\n      // Root finding function\n      const auto zeta_j_sqr_minus_h_sqr =\n          [&e_pt, &j_0, &j_thin, &j_thick, &h_sqr_0, &h_sqr_thick, &h_sqr_thin,\n           &h_sqr_thin_thin, &h_sqr_thick_thick,\n           &h_sqr_thin_thick](const double local_zeta) {\n            const double chi = minerbo_closure_function(local_zeta);\n            const double d_thin = 1.5 * chi - 0.5;\n            const double d_thick = 1. - d_thin;\n\n            const double e_fluid = j_0 + j_thin * d_thin + j_thick * d_thick;\n            const double h_sqr = h_sqr_0 + h_sqr_thick * d_thick +\n                                 h_sqr_thin * d_thin +\n                                 h_sqr_thin_thin * square(d_thin) +\n                                 h_sqr_thick_thick * square(d_thick) +\n                                 h_sqr_thin_thick * d_thin * d_thick;\n            return (square(e_fluid * local_zeta) - h_sqr) / square(e_pt);\n          };\n      // To avoid failures in the root find at the boundary of\n      // the allowed domain for zeta, test the edge values first.\n      if (fabs(zeta_j_sqr_minus_h_sqr(0.)) < root_find_tolerance) {\n        get(*closure_factor)[s] = 0.;\n      } else if (fabs(zeta_j_sqr_minus_h_sqr(1.)) < root_find_tolerance) {\n        get(*closure_factor)[s] = 1.;\n      } else {\n        get(*closure_factor)[s] = RootFinder::toms748(\n            zeta_j_sqr_minus_h_sqr, 1.e-15, 1., root_find_tolerance, 1.0e-15);\n      }\n      const double& zeta = get(*closure_factor)[s];\n\n      // Assemble output quantities:\n      const double chi = minerbo_closure_function(zeta);\n      const double d_thin = 1.5 * chi - 0.5;\n      const double d_thick = 1. - d_thin;\n      get(*comoving_energy_density)[s] =\n          j_0 + j_thin * d_thin + j_thick * d_thick;\n      get(*comoving_momentum_density_normal)[s] =\n          h_0_t + h_thin_t * d_thin + h_thick_t * d_thick;\n      for (size_t i = 0; i < spatial_dim; i++) {\n        comoving_momentum_density_spatial->get(i)[s] =\n            -(h_0_v + h_thin_v * d_thin + h_thick_v * d_thick) * v_m.get(i)[s] -\n            (h_0_f + h_thin_f * d_thin + h_thick_f * d_thick) *\n                momentum_density.get(i)[s];\n        for (size_t j = i; j < spatial_dim; j++) {\n          // Optically thin part of pressure tensor\n          pressure_tensor->get(i, j)[s] =\n              d_thin * e_pt * s_M.get(i)[s] * s_M.get(j)[s] / s_sqr_pt;\n        }\n      }\n      // Optically thick limit\n      for (size_t i = 0; i < spatial_dim; i++) {\n        H_M.get(i) =\n            s_M.get(i)[s] / w_pt +\n            fluid_velocity.get(i)[s] * w_pt / (2. * w_sqr_pt + 1.) *\n                ((4. * w_sqr_pt + 1.) * v_dot_f_pt - 4. * w_sqr_pt * e_pt);\n      }\n      const double J_over_3 =\n          1. / (2. * w_sqr_pt + 1.) *\n          ((2. * w_sqr_pt - 1.) * e_pt - 2. * w_sqr_pt * v_dot_f_pt);\n      for (size_t i = 0; i < spatial_dim; i++) {\n        for (size_t j = i; j < spatial_dim; j++) {\n          pressure_tensor->get(i, j)[s] +=\n              d_thick * (J_over_3 * (4. * w_sqr_pt * fluid_velocity.get(i)[s] *\n                                         fluid_velocity.get(j)[s] +\n                                     inv_spatial_metric.get(i, j)[s]) +\n                         w_pt * (H_M.get(i) * fluid_velocity.get(j)[s] +\n                                 H_M.get(j) * fluid_velocity.get(i)[s]));\n        }\n      }\n    }\n  }\n}\n\n}  // namespace RadiationTransport::M1Grey::detail\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/M1Closure.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Defines functions to compute the M1 closure\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace RadiationTransport {\nnamespace M1Grey {\n\n// Implementation of the M1 closure for an\n// individual species\nnamespace detail {\nvoid compute_closure_impl(\n    gsl::not_null<Scalar<DataVector>*> closure_factor,\n    gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*> pressure_tensor,\n    gsl::not_null<Scalar<DataVector>*> comoving_energy_density,\n    gsl::not_null<Scalar<DataVector>*> comoving_momentum_density_normal,\n    gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        comoving_momentum_density_spatial,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& momentum_density,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& fluid_velocity,\n    const Scalar<DataVector>& fluid_lorentz_factor,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric);\n}  // namespace detail\n\ntemplate <typename NeutrinoSpeciesList>\nstruct ComputeM1Closure;\n/*!\n * Compute the 2nd moment of the neutrino distribution function\n * in the inertial frame (pressure tensor) from the 0th (energy density)\n * and 1st (momentum density) moments. The M1 closure sets\n * \\f{align}{\n * P_{ij} = d_{\\rm thin} P_{ij,\\rm thin} + d_{\\rm thick} P_{ij,\\rm thick}\n * \\f}\n * with \\f$P_{ij}\\f$ the pressure tensor in the inertial frame, and\n * \\f$P_{ij,thin/thick}\\f$ its value assuming an optically thick / optically\n * thin medium.\n *\n * Following the algorithm described in Appendix A of \\cite Foucart2015vpa,\n * we choose the Minerbo closure (\\cite Minerbo1978) which sets\n * \\f{align}{\n * d_{\\rm thin} = & 1.5\\chi-0.5\\\\\n * d_{\\rm thick} = & 1.5-1.5\\chi \\\\\n * \\chi = & \\frac{1}{3} + \\xi^2 \\frac{6-2\\xi+6\\xi^2}{15} \\\\\n * \\xi = & H^aH_a/J^2\n * \\f}\n * with \\f$J\\f$ the energy density in the\n * frame comoving with the fluid, and \\f$H^a\\f$ the momentum density\n * in that frame.\n * The optically thick closure is\n * \\f{align}{\n * K^{ab,\\rm thick} = \\frac{J_{\\rm thick}}{3} (g^{ab}+u^a u^b)\n * \\f}\n * with \\f$ K^{ab,\\rm thick} \\f$ the pressure tensor in the frame comoving with\n * the fluid, and \\f$ u^a \\f$ the fluid 4-velocity, while the optically thin\n * closure is\n * \\f{align}{\n * P^{ij,\\rm thin} = \\frac{S^i S^j}{S^k S_k} E\n * \\f}\n * with \\f$ E \\f$, \\f$ S^{i}\n * \\f$ the 0th and 1st moments in the inertial frame (see paper for the\n * computation of \\f$P_{ij,thick}\\f$ from the other moments).\n *\n * The main step of this function is to solve the equation\n * \\f{align}{\n *  \\frac{\\xi^2 J^2 - H^aH_a}{E^2} = 0\n * \\f}\n * for \\f$\\xi\\f$, with \\f$J\\f$ and \\f$H^a\\f$ being functions\n * of \\f$\\xi\\f$ and of the input variables (the 0th and 1st moments\n * in the inertial frame). This is done by separating \\f$H^a\\f$ as\n * \\f{align}{\n * H_a = - H_t t_a - H_v v_a - H_f F_a\n * \\f}\n * (with \\f$v^a\\f$ the fluid 3-velocity and \\f$t^a\\f$ the unit normal\n * to the slice) and then writing each of the\n * variables \\f$J\\f$, \\f$H_n\\f$, \\f$H_v\\f$, \\f$H_f\\f$ as\n * \\f{align}{\n * X = X_0 + d_{\\rm thin} X_{\\rm thin} + d_{\\rm thick} X_{\\rm thick}\n * \\f}\n * so that evaluating\n * \\f{align}{\n * \\frac{\\xi^2 J^2 - H^aH_a}{E^2} = 0\n * \\f}\n * for a given \\f$\\xi\\f$ only requires recomputing \\f$d_{\\rm thin,thick}\\f$\n * and their derivatives with respect to \\f$\\xi\\f$.\n * We perform the root-finding using a Newton-Raphson algorithm, with the\n * accuracy set by the variable root_find_number_of_digits (6 significant digits\n * at the moment).\n *\n * The function returns the closure factors \\f$\\xi\\f$ (to be used as initial\n * guess for this function at the next step), the pressure tensor \\f$P_{ij}\\f$,\n * and the neutrino moments in the frame comoving with the fluid.\n * The momentum density in the frame comoving with the fluid\n * is decomposed into its normal component \\f$ H^a t_a\\f$, and its spatial\n * components \\f$ \\gamma_{ia} H^a\\f$.\n */\ntemplate <typename... NeutrinoSpecies>\nstruct ComputeM1Closure<tmpl::list<NeutrinoSpecies...>> {\n  using return_tags =\n      tmpl::list<Tags::ClosureFactor<NeutrinoSpecies>...,\n                 Tags::TildeP<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeJ<NeutrinoSpecies>...,\n                 Tags::TildeHNormal<NeutrinoSpecies>...,\n                 Tags::TildeHSpatial<Frame::Inertial, NeutrinoSpecies>...>;\n\n  using argument_tags =\n      tmpl::list<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n\n  static void apply(\n      const gsl::not_null<typename Tags::ClosureFactor<\n          NeutrinoSpecies>::type*>... closure_factor,\n      const gsl::not_null<typename Tags::TildeP<\n          Frame::Inertial, NeutrinoSpecies>::type*>... tilde_p,\n      const gsl::not_null<\n          typename Tags::TildeJ<NeutrinoSpecies>::type*>... tilde_j,\n      const gsl::not_null<\n          typename Tags::TildeHNormal<NeutrinoSpecies>::type*>... tilde_hn,\n      const gsl::not_null<typename Tags::TildeHSpatial<\n          Frame::Inertial, NeutrinoSpecies>::type*>... tilde_hi,\n      const typename Tags::TildeE<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_e,\n      const typename Tags::TildeS<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_s,\n      const tnsr::I<DataVector, 3>& spatial_velocity,\n      const Scalar<DataVector>& lorentz_factor,\n      const tnsr::ii<DataVector, 3>& spatial_metric,\n      const tnsr::II<DataVector, 3>& inv_spatial_metric) {\n    EXPAND_PACK_LEFT_TO_RIGHT(detail::compute_closure_impl(\n        closure_factor, tilde_p, tilde_j, tilde_hn, tilde_hi, tilde_e, tilde_s,\n        spatial_velocity, lorentz_factor, spatial_metric, inv_spatial_metric));\n  }\n};\n\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/M1HydroCoupling.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1HydroCoupling.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct densitized_eta_minus_kappaJ : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct kappaT_lapse : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace\n\nnamespace RadiationTransport::M1Grey::detail {\n\nvoid compute_m1_hydro_coupling_impl(\n    const gsl::not_null<Scalar<DataVector>*> source_n,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> source_i,\n    const Scalar<DataVector>& emissivity,\n    const Scalar<DataVector>& absorption_opacity,\n    const Scalar<DataVector>& scattering_opacity,\n    const Scalar<DataVector>& comoving_energy_density,\n    const Scalar<DataVector>& comoving_momentum_density_normal,\n    const tnsr::i<DataVector, 3>& comoving_momentum_density_spatial,\n    const tnsr::I<DataVector, 3>& fluid_velocity,\n    const Scalar<DataVector>& fluid_lorentz_factor,\n    const Scalar<DataVector>& lapse,\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  Variables<tmpl::list<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>,\n                       densitized_eta_minus_kappaJ, kappaT_lapse>>\n      temp_tensors(get(lapse).size());\n  // Dimension of spatial tensors\n  constexpr size_t spatial_dim = 3;\n\n  auto& dens_e_minus_kJ = get<densitized_eta_minus_kappaJ>(temp_tensors);\n  get(dens_e_minus_kJ) =\n      get(lapse) * get(fluid_lorentz_factor) *\n      (get(sqrt_det_spatial_metric) * get(emissivity) -\n       get(absorption_opacity) * get(comoving_energy_density));\n  auto& kT_lapse = get<kappaT_lapse>(temp_tensors);\n  get(kT_lapse) =\n      get(lapse) * (get(absorption_opacity) + get(scattering_opacity));\n  auto& fluid_velocity_i =\n      get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3>>(temp_tensors);\n  raise_or_lower_index(make_not_null(&fluid_velocity_i), fluid_velocity,\n                       spatial_metric);\n\n  get(*source_n) = get(dens_e_minus_kJ) +\n                   get(kT_lapse) * get(comoving_momentum_density_normal);\n  for (size_t i = 0; i < spatial_dim; i++) {\n    source_i->get(i) = fluid_velocity_i.get(i) * get(dens_e_minus_kJ) -\n                       get(kT_lapse) * comoving_momentum_density_spatial.get(i);\n  }\n}\n\nnamespace {\nnamespace LocalTags {\nstruct DummySpecies;\nusing TildeE = Tags::TildeE<Frame::Inertial, DummySpecies>;\nusing TildeS = Tags::TildeS<Frame::Inertial, DummySpecies>;\nusing TildeJ = Tags::TildeJ<DummySpecies>;\nusing TildeHSpatial = Tags::TildeHSpatial<Frame::Inertial, DummySpecies>;\n}  // namespace LocalTags\n}  // namespace\n\nvoid compute_m1_hydro_coupling_jacobian_impl(\n    const gsl::not_null<Scalar<DataVector>*> deriv_e_source_e,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> deriv_e_source_s,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> deriv_s_source_e,\n    const gsl::not_null<tnsr::Ij<DataVector, 3>*> deriv_s_source_s,\n    const tnsr::i<DataVector, 3>& tilde_s, const Scalar<DataVector>& tilde_e,\n    const Scalar<DataVector>& emissivity,\n    const Scalar<DataVector>& absorption_opacity,\n    const Scalar<DataVector>& scattering_opacity,\n    const tnsr::I<DataVector, 3>& fluid_velocity,\n    const Scalar<DataVector>& fluid_lorentz_factor,\n    const Scalar<DataVector>& closure_factor,\n    const Scalar<DataVector>& comoving_energy_density,\n    const tnsr::i<DataVector, 3>& comoving_momentum_density_spatial,\n    const Scalar<DataVector>& comoving_momentum_density_normal,\n    const Scalar<DataVector>& lapse,\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const tnsr::II<DataVector, 3>& inverse_spatial_metric) {\n  const double s_squared_floor = 1.0e-150;\n\n  Variables<tmpl::list<\n      ::Tags::TempScalar<0>, ::Tags::TempScalar<1>, ::Tags::Tempi<2, 3>,\n      Tags::TildeSVector<Frame::Inertial>, ::Tags::TempI<3, 3>,\n      ::Tags::TempScalar<4>, ::Tags::TempScalar<5>, ::Tags::TempScalar<6>,\n      ::Tags::TempScalar<7>, ::Tags::TempScalar<8>, ::Tags::TempScalar<9>,\n      ::Tags::TempScalar<10>, ::Tags::TempScalar<11>, ::Tags::TempScalar<12>,\n      ::Tags::TempScalar<13>, ::Tags::TempScalar<14>, ::Tags::Tempi<15, 3>,\n      ::Tags::TempScalar<16>, ::Tags::TempScalar<17>, ::Tags::TempScalar<18>,\n      ::Tags::TempScalar<19>, ::Tags::TempScalar<20>, ::Tags::TempScalar<21>,\n      ::Tags::TempScalar<22>, ::Tags::TempScalar<23>, ::Tags::TempScalar<24>,\n      ::Tags::TempScalar<25>, ::Tags::TempI<26, 3>, ::Tags::Tempi<27, 3>,\n      ::Tags::TempIj<28, 3>, ::Tags::TempScalar<29>, ::Tags::TempScalar<30>,\n      ::Tags::TempI<31, 3>,\n      ::imex::Tags::Jacobian<LocalTags::TildeE, LocalTags::TildeJ>,\n      ::imex::Tags::Jacobian<LocalTags::TildeS, LocalTags::TildeJ>,\n      ::imex::Tags::Jacobian<LocalTags::TildeE, LocalTags::TildeHSpatial>,\n      ::imex::Tags::Jacobian<LocalTags::TildeS, LocalTags::TildeHSpatial>,\n      ::Tags::TempScalar<32>, ::Tags::TempScalar<33>, ::Tags::TempScalar<34>,\n      ::Tags::TempScalar<35>>>\n      temporaries(get(emissivity).size());\n\n  auto& d_thin = get<::Tags::TempScalar<0>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&d_thin),\n      0.2 * square(closure_factor()) *\n          (3.0 + closure_factor() * (-1.0 + 3.0 * closure_factor())));\n\n  auto& d_thick = get<::Tags::TempScalar<1>>(temporaries);\n  tenex::evaluate(make_not_null(&d_thick), 1.0 - d_thin());\n\n  auto& fluid_velocity_lower = get<::Tags::Tempi<2, 3>>(temporaries);\n  tenex::evaluate<ti::i>(make_not_null(&fluid_velocity_lower),\n                         spatial_metric(ti::i, ti::j) * fluid_velocity(ti::J));\n\n  auto& s_upper = get<Tags::TildeSVector<Frame::Inertial>>(temporaries);\n  tenex::evaluate<ti::I>(make_not_null(&s_upper),\n                         inverse_spatial_metric(ti::I, ti::J) * tilde_s(ti::j));\n\n  auto& comoving_four_momentum_density_upper =\n      get<::Tags::TempI<3, 3>>(temporaries);\n  tenex::evaluate<ti::I>(\n      make_not_null(&comoving_four_momentum_density_upper),\n      comoving_momentum_density_spatial(ti::j) *\n              inverse_spatial_metric(ti::I, ti::J) +\n          comoving_momentum_density_normal() * fluid_velocity(ti::I));\n\n  auto& inverse_s_norm = get<::Tags::TempScalar<4>>(temporaries);\n  tenex::evaluate(make_not_null(&inverse_s_norm),\n                  1.0 / (s_upper(ti::I) * tilde_s(ti::i) + s_squared_floor));\n\n  auto& fluid_velocity_norm = get<::Tags::TempScalar<5>>(temporaries);\n  tenex::evaluate(make_not_null(&fluid_velocity_norm),\n                  fluid_velocity(ti::I) * fluid_velocity_lower(ti::i));\n\n  auto& s_dot_fluid_velocity = get<::Tags::TempScalar<6>>(temporaries);\n  tenex::evaluate(make_not_null(&s_dot_fluid_velocity),\n                  tilde_s(ti::i) * fluid_velocity(ti::I));\n\n  auto& s_dot_fluid_velocity_normalized =\n      get<::Tags::TempScalar<7>>(temporaries);\n  tenex::evaluate(make_not_null(&s_dot_fluid_velocity_normalized),\n                  s_dot_fluid_velocity() * inverse_s_norm());\n\n  auto& s_dot_fluid_velocity_squared_normalized =\n      get<::Tags::TempScalar<8>>(temporaries);\n  tenex::evaluate(make_not_null(&s_dot_fluid_velocity_squared_normalized),\n                  s_dot_fluid_velocity() * s_dot_fluid_velocity_normalized());\n\n  auto& denom = get<::Tags::TempScalar<9>>(temporaries);\n  tenex::evaluate(make_not_null(&denom),\n                  1.0 / (1.0 + 2.0 * square(fluid_lorentz_factor())));\n\n  auto& scaled_comoving_energy_density =\n      get<::Tags::TempScalar<10>>(temporaries);\n  tenex::evaluate(make_not_null(&scaled_comoving_energy_density),\n                  square(closure_factor()) * comoving_energy_density());\n\n  auto& h_difference_s_coef = get<::Tags::TempScalar<11>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&h_difference_s_coef),\n      fluid_lorentz_factor() * (fluid_velocity_norm() -\n                                tilde_e() * s_dot_fluid_velocity_normalized()));\n\n  auto& common_difference_term = get<::Tags::TempScalar<12>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&common_difference_term),\n      denom() *\n          ((2.0 * square(fluid_lorentz_factor()) - 3.0) * tilde_e() -\n           4.0 * square(fluid_lorentz_factor()) * s_dot_fluid_velocity()));\n\n  auto& j_difference = get<::Tags::TempScalar<13>>(temporaries);\n  tenex::evaluate(make_not_null(&j_difference),\n                  square(fluid_lorentz_factor()) *\n                      (fluid_velocity_norm() * common_difference_term() +\n                       tilde_e() * s_dot_fluid_velocity_squared_normalized()));\n\n  // Negated at use site.\n  auto& h_difference_v_coef = get<::Tags::TempScalar<14>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&h_difference_v_coef),\n      fluid_lorentz_factor() *\n          (common_difference_term() + j_difference() + s_dot_fluid_velocity()));\n\n  auto& h_difference = get<::Tags::Tempi<15, 3>>(temporaries);\n  tenex::evaluate<ti::i>(\n      make_not_null(&h_difference),\n      h_difference_s_coef() * tilde_s(ti::i) -\n          h_difference_v_coef() * fluid_velocity_lower(ti::i));\n\n  auto& deriv_e_h_v_coef = get<::Tags::TempScalar<16>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&deriv_e_h_v_coef),\n      square(fluid_lorentz_factor()) *\n          (-4.0 * d_thick() * denom() -\n           d_thin() * (1.0 + s_dot_fluid_velocity_squared_normalized())));\n\n  // Negated at use site.\n  auto& deriv_e_h_s_coef = get<::Tags::TempScalar<17>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_e_h_s_coef),\n                  d_thin() * s_dot_fluid_velocity_normalized());\n\n  auto& deriv_s_h_trace_coef = get<::Tags::TempScalar<18>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&deriv_s_h_trace_coef),\n      (1.0 / fluid_lorentz_factor() + d_thin() * h_difference_s_coef()) /\n          fluid_lorentz_factor());\n\n  auto& deriv_s_j_v_coef = get<::Tags::TempScalar<19>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_s_j_v_coef),\n                  -2.0 * fluid_lorentz_factor() *\n                      (deriv_s_h_trace_coef() +\n                       d_thick() * fluid_velocity_norm() * denom()));\n\n  auto& deriv_s_h_vv_coef = get<::Tags::TempScalar<20>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&deriv_s_h_vv_coef),\n      2.0 - denom() * d_thick() +\n          2.0 * d_thin() * fluid_lorentz_factor() * h_difference_s_coef());\n\n  // Negated at use site.\n  auto& deriv_s_h_vs_coef = get<::Tags::TempScalar<21>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_s_h_vs_coef),\n                  d_thin() * tilde_e() * inverse_s_norm());\n\n  auto& deriv_s_h_ss_coef = get<::Tags::TempScalar<22>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&deriv_s_h_ss_coef),\n      2.0 * s_dot_fluid_velocity_normalized() * deriv_s_h_vs_coef());\n\n  auto& deriv_s_j_s_coef = get<::Tags::TempScalar<23>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_s_j_s_coef),\n                  -2.0 * fluid_lorentz_factor() *\n                      s_dot_fluid_velocity_squared_normalized() *\n                      deriv_s_h_vs_coef());\n\n  // Negated at use site.\n  auto& deriv_s_h_sv_coef = get<::Tags::TempScalar<24>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_s_h_sv_coef),\n                  fluid_lorentz_factor() * deriv_s_j_s_coef());\n\n  auto& constant_d_deriv_e_j_over_lorentz_factor =\n      get<::Tags::TempScalar<25>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&constant_d_deriv_e_j_over_lorentz_factor),\n      fluid_lorentz_factor() *\n          (d_thin() * (1.0 + s_dot_fluid_velocity_squared_normalized()) +\n           3.0 * d_thick() * denom() * (1.0 + fluid_velocity_norm())));\n\n  auto& constant_d_deriv_s_j_over_lorentz_factor =\n      get<::Tags::TempI<26, 3>>(temporaries);\n  tenex::evaluate<ti::I>(\n      make_not_null(&constant_d_deriv_s_j_over_lorentz_factor),\n      deriv_s_j_v_coef() * fluid_velocity(ti::I) +\n          deriv_s_j_s_coef() * s_upper(ti::I));\n\n  auto& constant_d_deriv_e_h_over_lorentz_factor =\n      get<::Tags::Tempi<27, 3>>(temporaries);\n  tenex::evaluate<ti::i>(\n      make_not_null(&constant_d_deriv_e_h_over_lorentz_factor),\n      deriv_e_h_v_coef() * fluid_velocity_lower(ti::i) -\n          deriv_e_h_s_coef() * tilde_s(ti::i));\n\n  auto& constant_d_deriv_s_h_over_lorentz_factor =\n      get<::Tags::TempIj<28, 3>>(temporaries);\n  tenex::evaluate<ti::I, ti::j>(\n      make_not_null(&constant_d_deriv_s_h_over_lorentz_factor),\n      deriv_s_h_vv_coef() * fluid_velocity(ti::I) *\n              fluid_velocity_lower(ti::j) +\n          deriv_s_h_ss_coef() * s_upper(ti::I) * tilde_s(ti::j) -\n          deriv_s_h_sv_coef() * s_upper(ti::I) * fluid_velocity_lower(ti::j) -\n          deriv_s_h_vs_coef() * fluid_velocity(ti::I) * tilde_s(ti::j));\n  for (size_t i = 0; i < 3; ++i) {\n    constant_d_deriv_s_h_over_lorentz_factor.get(i, i) +=\n        get(deriv_s_h_trace_coef);\n  }\n\n  auto& deriv_dthin_prefactor = get<::Tags::TempScalar<29>>(temporaries);\n  tenex::evaluate(\n      make_not_null(&deriv_dthin_prefactor),\n      1.0 / (scaled_comoving_energy_density() * j_difference() -\n             comoving_four_momentum_density_upper(ti::J) * h_difference(ti::j) +\n             5.0 / 3.0 * square(comoving_energy_density()) /\n                 (2.0 + closure_factor() * (-1.0 + closure_factor() * 4.0))));\n\n  auto& deriv_e_dthin_over_lorentz_factor =\n      get<::Tags::TempScalar<30>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_e_dthin_over_lorentz_factor),\n                  deriv_dthin_prefactor() *\n                      (comoving_four_momentum_density_upper(ti::J) *\n                           constant_d_deriv_e_h_over_lorentz_factor(ti::j) -\n                       scaled_comoving_energy_density() *\n                           constant_d_deriv_e_j_over_lorentz_factor()));\n  auto& deriv_s_dthin_over_lorentz_factor =\n      get<::Tags::TempI<31, 3>>(temporaries);\n  tenex::evaluate<ti::I>(\n      make_not_null(&deriv_s_dthin_over_lorentz_factor),\n      deriv_dthin_prefactor() *\n          (comoving_four_momentum_density_upper(ti::J) *\n               constant_d_deriv_s_h_over_lorentz_factor(ti::I, ti::j) -\n           scaled_comoving_energy_density() *\n               constant_d_deriv_s_j_over_lorentz_factor(ti::I)));\n\n  auto& deriv_e_j =\n      get<::imex::Tags::Jacobian<LocalTags::TildeE, LocalTags::TildeJ>>(\n          temporaries);\n  tenex::evaluate(make_not_null(&deriv_e_j),\n                  fluid_lorentz_factor() *\n                      (constant_d_deriv_e_j_over_lorentz_factor() +\n                       j_difference() * deriv_e_dthin_over_lorentz_factor()));\n  auto& deriv_s_j =\n      get<::imex::Tags::Jacobian<LocalTags::TildeS, LocalTags::TildeJ>>(\n          temporaries);\n  tenex::evaluate<ti::I>(\n      make_not_null(&deriv_s_j),\n      fluid_lorentz_factor() *\n          (constant_d_deriv_s_j_over_lorentz_factor(ti::I) +\n           j_difference() * deriv_s_dthin_over_lorentz_factor(ti::I)));\n  auto& deriv_e_h =\n      get<::imex::Tags::Jacobian<LocalTags::TildeE, LocalTags::TildeHSpatial>>(\n          temporaries);\n  tenex::evaluate<ti::i>(\n      make_not_null(&deriv_e_h),\n      fluid_lorentz_factor() *\n          (constant_d_deriv_e_h_over_lorentz_factor(ti::i) +\n           deriv_e_dthin_over_lorentz_factor() * h_difference(ti::i)));\n  auto& deriv_s_h =\n      get<::imex::Tags::Jacobian<LocalTags::TildeS, LocalTags::TildeHSpatial>>(\n          temporaries);\n  tenex::evaluate<ti::I, ti::j>(\n      make_not_null(&deriv_s_h),\n      fluid_lorentz_factor() *\n          (constant_d_deriv_s_h_over_lorentz_factor(ti::I, ti::j) +\n           deriv_s_dthin_over_lorentz_factor(ti::I) * h_difference(ti::j)));\n\n  auto& deriv_source_e_j_coef = get<::Tags::TempScalar<32>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_source_e_j_coef),\n                  lapse() * fluid_lorentz_factor() * scattering_opacity());\n  auto& deriv_source_e_v_coef = get<::Tags::TempScalar<33>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_source_e_v_coef),\n                  lapse() * fluid_lorentz_factor() *\n                      (absorption_opacity() + scattering_opacity()));\n  auto& deriv_source_s_h_coef = get<::Tags::TempScalar<34>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_source_s_h_coef),\n                  -lapse() * (absorption_opacity() + scattering_opacity()));\n  auto& deriv_source_s_jv_coef = get<::Tags::TempScalar<35>>(temporaries);\n  tenex::evaluate(make_not_null(&deriv_source_s_jv_coef),\n                  -lapse() * fluid_lorentz_factor() * absorption_opacity());\n\n  tenex::evaluate(deriv_e_source_e, deriv_source_e_j_coef() * deriv_e_j() -\n                                        deriv_source_e_v_coef());\n  tenex::evaluate<ti::I>(deriv_s_source_e,\n                         deriv_source_e_j_coef() * deriv_s_j(ti::I) +\n                             deriv_source_e_v_coef() * fluid_velocity(ti::I));\n  tenex::evaluate<ti::i>(\n      deriv_e_source_s,\n      deriv_source_s_h_coef() * deriv_e_h(ti::i) +\n          deriv_source_s_jv_coef() * deriv_e_j() * fluid_velocity_lower(ti::i));\n  tenex::evaluate<ti::I, ti::j>(\n      deriv_s_source_s, deriv_source_s_h_coef() * deriv_s_h(ti::I, ti::j) +\n                            deriv_source_s_jv_coef() * deriv_s_j(ti::I) *\n                                fluid_velocity_lower(ti::j));\n}\n\n}  // namespace RadiationTransport::M1Grey::detail\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/M1HydroCoupling.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Defines functions to compute the M1-hydro\n/// coupling terms.\n\n#pragma once\n\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSource.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSourceJacobian.hpp\"\n#include \"Evolution/Imex/Tags/Jacobian.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Protocols/StaticReturnApplyable.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\n/// \\endcond\n\nnamespace RadiationTransport {\nnamespace M1Grey {\n\n// Implementation of the coupling terms for\n// individual species\nnamespace detail {\nvoid compute_m1_hydro_coupling_impl(\n    gsl::not_null<Scalar<DataVector>*> source_n,\n    gsl::not_null<tnsr::i<DataVector, 3>*> source_i,\n    const Scalar<DataVector>& emissivity,\n    const Scalar<DataVector>& absorption_opacity,\n    const Scalar<DataVector>& scattering_opacity,\n    const Scalar<DataVector>& comoving_energy_density,\n    const Scalar<DataVector>& comoving_momentum_density_normal,\n    const tnsr::i<DataVector, 3>& comoving_momentum_density_spatial,\n    const tnsr::I<DataVector, 3>& fluid_velocity,\n    const Scalar<DataVector>& fluid_lorentz_factor,\n    const Scalar<DataVector>& lapse,\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const Scalar<DataVector>& sqrt_det_spatial_metric);\n\nvoid compute_m1_hydro_coupling_jacobian_impl(\n    gsl::not_null<Scalar<DataVector>*> deriv_e_source_e,\n    gsl::not_null<tnsr::i<DataVector, 3>*> deriv_e_source_s,\n    gsl::not_null<tnsr::I<DataVector, 3>*> deriv_s_source_e,\n    gsl::not_null<tnsr::Ij<DataVector, 3>*> deriv_s_source_s,\n    const tnsr::i<DataVector, 3>& tilde_s, const Scalar<DataVector>& tilde_e,\n    const Scalar<DataVector>& emissivity,\n    const Scalar<DataVector>& absorption_opacity,\n    const Scalar<DataVector>& scattering_opacity,\n    const tnsr::I<DataVector, 3>& fluid_velocity,\n    const Scalar<DataVector>& fluid_lorentz_factor,\n    const Scalar<DataVector>& closure_factor,\n    const Scalar<DataVector>& comoving_energy_density,\n    const tnsr::i<DataVector, 3>& comoving_momentum_density_spatial,\n    const Scalar<DataVector>& comoving_momentum_density_normal,\n    const Scalar<DataVector>& lapse,\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const tnsr::II<DataVector, 3>& inverse_spatial_metric);\n}  // namespace detail\n\ntemplate <typename NeutrinoSpeciesList>\nstruct ComputeM1HydroCoupling;\n/*!\n * Compute the source terms of the M1 equation due to neutrino-matter\n * interactions.\n * These are:\n *\n * \\f{align}{\n * \\partial_t \\tilde E = \\alpha W (\\sqrt{\\gamma} \\eta - \\kappa_a \\tilde J)\n * + \\alpha (\\kappa_a + \\kappa_s) H_n\\\\\n * \\partial_t \\tilde S_i = \\alpha u_i (\\sqrt{\\gamma} \\eta - \\kappa_a \\tilde J)\n * - \\alpha  (\\kappa_a + \\kappa_s) H_i.\n * \\f}\n *\n * with \\f$W\\f$ the Lorentz factor, \\f$u_i = W v_i\\f$ the spatial components of\n * the fluid 4-velocity, \\f$\\eta\\f$ the emissivity, \\f$\\kappa_{a,s}\\f$\n * the absorption and scattering opacities, \\f$J\\f$ the comoving energy\n * density, and \\f$H_{n,i}\\f$ the normal and spatial components of the comoving\n * flux density.\n *\n * The function returns in `source_n` the energy source and in\n * `source_i` the momentum source. We write a separate action for these\n * sources to make it easier to switch between implicit / explicit time\n * stepping, as well as to add the source terms to both the fluid and M1\n * evolutions.\n */\ntemplate <typename... NeutrinoSpecies>\nstruct ComputeM1HydroCoupling<tmpl::list<NeutrinoSpecies...>>\n    : tt::ConformsTo<imex::protocols::ImplicitSource>,\n      tt::ConformsTo<protocols::StaticReturnApplyable> {\n  using return_tags = tmpl::list<\n      ::Tags::Source<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>>...,\n      ::Tags::Source<Tags::TildeS<Frame::Inertial, NeutrinoSpecies>>...>;\n\n  using argument_tags = tmpl::list<\n      Tags::GreyEmissivity<NeutrinoSpecies>...,\n      Tags::GreyAbsorptionOpacity<NeutrinoSpecies>...,\n      Tags::GreyScatteringOpacity<NeutrinoSpecies>...,\n      Tags::TildeJ<NeutrinoSpecies>..., Tags::TildeHNormal<NeutrinoSpecies>...,\n      Tags::TildeHSpatial<Frame::Inertial, NeutrinoSpecies>...,\n      hydro::Tags::SpatialVelocity<DataVector, 3>,\n      hydro::Tags::LorentzFactor<DataVector>, gr::Tags::Lapse<DataVector>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n\n  static void apply(\n      const gsl::not_null<typename Tags::M1HydroCouplingNormal<\n          NeutrinoSpecies>::type*>... source_n,\n      const gsl::not_null<typename Tags::M1HydroCouplingSpatial<\n          Frame::Inertial, NeutrinoSpecies>::type*>... source_i,\n      const typename Tags::GreyEmissivity<NeutrinoSpecies>::type&... emissivity,\n      const typename Tags::GreyAbsorptionOpacity<\n          NeutrinoSpecies>::type&... absorption_opacity,\n      const typename Tags::GreyScatteringOpacity<\n          NeutrinoSpecies>::type&... scattering_opacity,\n      const typename Tags::TildeJ<NeutrinoSpecies>::type&... tilde_j,\n      const typename Tags::TildeHNormal<NeutrinoSpecies>::type&... tilde_hn,\n      const typename Tags::TildeHSpatial<Frame::Inertial,\n                                         NeutrinoSpecies>::type&... tilde_hi,\n      const tnsr::I<DataVector, 3>& spatial_velocity,\n      const Scalar<DataVector>& lorentz_factor, const Scalar<DataVector>& lapse,\n      const tnsr::ii<DataVector, 3>& spatial_metric,\n      const Scalar<DataVector>& sqrt_det_spatial_metric) {\n    EXPAND_PACK_LEFT_TO_RIGHT(detail::compute_m1_hydro_coupling_impl(\n        source_n, source_i, emissivity, absorption_opacity, scattering_opacity,\n        tilde_j, tilde_hn, tilde_hi, spatial_velocity, lorentz_factor, lapse,\n        spatial_metric, sqrt_det_spatial_metric));\n  }\n};\n\ntemplate <typename NeutrinoSpecies>\nstruct ComputeM1HydroCouplingJacobian\n    : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n      tt::ConformsTo<protocols::StaticReturnApplyable> {\n  using return_tags = tmpl::list<\n      imex::Tags::Jacobian<\n          Tags::TildeE<Frame::Inertial, NeutrinoSpecies>,\n          ::Tags::Source<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>>>,\n      imex::Tags::Jacobian<\n          Tags::TildeE<Frame::Inertial, NeutrinoSpecies>,\n          ::Tags::Source<Tags::TildeS<Frame::Inertial, NeutrinoSpecies>>>,\n      imex::Tags::Jacobian<\n          Tags::TildeS<Frame::Inertial, NeutrinoSpecies>,\n          ::Tags::Source<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>>>,\n      imex::Tags::Jacobian<\n          Tags::TildeS<Frame::Inertial, NeutrinoSpecies>,\n          ::Tags::Source<Tags::TildeS<Frame::Inertial, NeutrinoSpecies>>>>;\n\n  using argument_tags = tmpl::list<\n      Tags::TildeS<Frame::Inertial, NeutrinoSpecies>,\n      Tags::TildeE<Frame::Inertial, NeutrinoSpecies>,\n      Tags::GreyEmissivity<NeutrinoSpecies>,\n      Tags::GreyAbsorptionOpacity<NeutrinoSpecies>,\n      Tags::GreyScatteringOpacity<NeutrinoSpecies>,\n      hydro::Tags::SpatialVelocity<DataVector, 3>,\n      hydro::Tags::LorentzFactor<DataVector>,\n      Tags::ClosureFactor<NeutrinoSpecies>, Tags::TildeJ<NeutrinoSpecies>,\n      Tags::TildeHSpatial<Frame::Inertial, NeutrinoSpecies>,\n      Tags::TildeHNormal<NeutrinoSpecies>, gr::Tags::Lapse<DataVector>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n\n  static void apply(\n      const gsl::not_null<Scalar<DataVector>*> deriv_e_source_e,\n      const gsl::not_null<tnsr::i<DataVector, 3>*> deriv_e_source_s,\n      const gsl::not_null<tnsr::I<DataVector, 3>*> deriv_s_source_e,\n      const gsl::not_null<tnsr::Ij<DataVector, 3>*> deriv_s_source_s,\n      const tnsr::i<DataVector, 3>& tilde_s, const Scalar<DataVector>& tilde_e,\n      const Scalar<DataVector>& emissivity,\n      const Scalar<DataVector>& absorption_opacity,\n      const Scalar<DataVector>& scattering_opacity,\n      const tnsr::I<DataVector, 3>& fluid_velocity,\n      const Scalar<DataVector>& fluid_lorentz_factor,\n      const Scalar<DataVector>& closure_factor,\n      const Scalar<DataVector>& comoving_energy_density,\n      const tnsr::i<DataVector, 3>& comoving_momentum_density_spatial,\n      const Scalar<DataVector>& comoving_momentum_density_normal,\n      const Scalar<DataVector>& lapse,\n      const tnsr::ii<DataVector, 3>& spatial_metric,\n      const tnsr::II<DataVector, 3>& inverse_spatial_metric) {\n    detail::compute_m1_hydro_coupling_jacobian_impl(\n        deriv_e_source_e, deriv_e_source_s, deriv_s_source_e, deriv_s_source_s,\n        tilde_s, tilde_e, emissivity, absorption_opacity, scattering_opacity,\n        fluid_velocity, fluid_lorentz_factor, closure_factor,\n        comoving_energy_density, comoving_momentum_density_spatial,\n        comoving_momentum_density_normal, lapse, spatial_metric,\n        inverse_spatial_metric);\n  }\n};\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Sources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Sources.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1HydroCoupling.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace {\nstruct AlphaTildeP : db::SimpleTag {\n  using type = tnsr::II<DataVector, 3>;\n};\n}  //  namespace\n\nnamespace RadiationTransport::M1Grey::detail {\nvoid compute_sources_impl(\n    const gsl::not_null<Scalar<DataVector>*> source_tilde_e,\n    const gsl::not_null<tnsr::i<DataVector, 3>*> source_tilde_s,\n    const Scalar<DataVector>& tilde_e, const tnsr::i<DataVector, 3>& tilde_s,\n    const tnsr::II<DataVector, 3>& tilde_p, const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, 3>& d_lapse,\n    const tnsr::iJ<DataVector, 3>& d_shift,\n    const tnsr::ijj<DataVector, 3>& d_spatial_metric,\n    const tnsr::II<DataVector, 3>& inv_spatial_metric,\n    const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const Scalar<DataVector>& emissivity,\n    const Scalar<DataVector>& absorption_opacity,\n    const Scalar<DataVector>& scattering_opacity,\n    const Scalar<DataVector>& tilde_j, const Scalar<DataVector>& tilde_h_normal,\n    const tnsr::i<DataVector, 3>& tilde_h_spatial,\n    const tnsr::I<DataVector, 3>& spatial_velocity,\n    const Scalar<DataVector>& lorentz,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  Variables<tmpl::list<Tags::TildeSVector<Frame::Inertial>, AlphaTildeP>>\n      temp_tensors(get(tilde_e).size());\n\n  // calculate stiff matter source terms source_n/source_i\n  Scalar<DataVector> source_n{};\n  tnsr::i<DataVector, 3> source_i{};\n\n  compute_m1_hydro_coupling_impl(\n      &source_n, &source_i, emissivity, absorption_opacity, scattering_opacity,\n      tilde_j, tilde_h_normal, tilde_h_spatial, spatial_velocity, lorentz,\n      lapse, spatial_metric, sqrt_det_spatial_metric);\n\n  constexpr size_t spatial_dim = 3;\n\n  auto& tilde_s_M = get<Tags::TildeSVector<Frame::Inertial>>(temp_tensors);\n  raise_or_lower_index(make_not_null(&tilde_s_M), tilde_s, inv_spatial_metric);\n  auto& alpha_tilde_p = get<AlphaTildeP>(temp_tensors);\n  for (size_t i = 0; i < spatial_dim; ++i) {\n    for (size_t j = 0; j < spatial_dim; ++j) {\n      alpha_tilde_p.get(i, j) = tilde_p.get(i, j) * get(lapse);\n    }\n  }\n\n  // unroll contributions from m=0 and n=0 to avoid initializing\n  // source terms to zero\n  get(*source_tilde_e) =\n      get<0, 0>(extrinsic_curvature) * get<0, 0>(alpha_tilde_p) -\n      get<0>(tilde_s_M) * get<0>(d_lapse) + get(source_n);\n  for (size_t m = 1; m < spatial_dim; ++m) {\n    get(*source_tilde_e) +=\n        extrinsic_curvature.get(0, m) * alpha_tilde_p.get(0, m) +\n        extrinsic_curvature.get(m, 0) * alpha_tilde_p.get(m, 0) -\n        tilde_s_M.get(m) * d_lapse.get(m);\n    for (size_t n = 1; n < spatial_dim; ++n) {\n      get(*source_tilde_e) +=\n          extrinsic_curvature.get(m, n) * alpha_tilde_p.get(m, n);\n    }\n  }\n\n  for (size_t i = 0; i < spatial_dim; ++i) {\n    source_tilde_s->get(i) =\n        -get(tilde_e) * d_lapse.get(i) + get<0>(tilde_s) * d_shift.get(i, 0) +\n        0.5 * get<0, 0>(alpha_tilde_p) * d_spatial_metric.get(i, 0, 0) +\n        source_i.get(i);\n    for (size_t m = 1; m < spatial_dim; ++m) {\n      source_tilde_s->get(i) +=\n          tilde_s.get(m) * d_shift.get(i, m) +\n          0.5 * (alpha_tilde_p.get(0, m) * d_spatial_metric.get(i, 0, m) +\n                 alpha_tilde_p.get(m, 0) * d_spatial_metric.get(i, m, 0));\n      for (size_t n = 1; n < spatial_dim; ++n) {\n        source_tilde_s->get(i) +=\n            0.5 * alpha_tilde_p.get(m, n) * d_spatial_metric.get(i, m, n);\n      }\n    }\n  }\n}\n\n}  // namespace RadiationTransport::M1Grey::detail\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Sources.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace RadiationTransport::M1Grey {\n\n// Implementation of the curvature source terms\n// for the M1 system, for an individual species.\nnamespace detail {\nvoid compute_sources_impl(gsl::not_null<Scalar<DataVector>*> source_tilde_e,\n                          gsl::not_null<tnsr::i<DataVector, 3>*> source_tilde_s,\n                          const Scalar<DataVector>& tilde_e,\n                          const tnsr::i<DataVector, 3>& tilde_s,\n                          const tnsr::II<DataVector, 3>& tilde_p,\n                          const Scalar<DataVector>& lapse,\n                          const tnsr::i<DataVector, 3>& d_lapse,\n                          const tnsr::iJ<DataVector, 3>& d_shift,\n                          const tnsr::ijj<DataVector, 3>& d_spatial_metric,\n                          const tnsr::II<DataVector, 3>& inv_spatial_metric,\n                          const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n                          const tnsr::ii<DataVector, 3>& spatial_metric,\n                          const Scalar<DataVector>& emissivity,\n                          const Scalar<DataVector>& absorption_opacity,\n                          const Scalar<DataVector>& scattering_opacity,\n                          const Scalar<DataVector>& tilde_j,\n                          const Scalar<DataVector>& tilde_h_normal,\n                          const tnsr::i<DataVector, 3>& tilde_h_spatial,\n                          const tnsr::I<DataVector, 3>& spatial_velocity,\n                          const Scalar<DataVector>& lorentz,\n                          const Scalar<DataVector>& sqrt_det_spatial_metric);\n}  // namespace detail\n\n/*!\n * \\brief Compute the curvature source terms for the flux-balanced\n * grey M1 radiation transport.\n *\n *\n * A flux-balanced system has the generic form:\n * \\f[\n * \\partial_t U_i + \\partial_m F^m(U_i) = S(U_i)\n * \\f]\n *\n * where \\f$F^a()\\f$ denotes the flux of a conserved variable \\f$U_i\\f$ and\n * \\f$S()\\f$ denotes the source term for the conserved variable.\n *\n * For the grey M1 formalism (neglecting coupling to the fluid):\n * \\f{align*}\n * S({\\tilde E}) &= \\alpha \\tilde P^{ij} K_{ij} - \\tilde S^i \\partial_i\n * \\alpha,\\\\ S({\\tilde S_i}) &= -\\tilde E \\partial_i \\alpha + \\tilde S_k\n * \\partial_i \\beta^k\n * + \\frac{1}{2} \\alpha \\tilde P^{jk} \\partial_i \\gamma_{jk},\n * \\f}\n *\n * where \\f${\\tilde E}\\f$, \\f${\\tilde S_i}\\f$, \\f${\\tilde P}^{ij}\\f$ are the\n * densitized energy, momentum, and pressure tensor of the neutrinos/photons,\n * \\f$K_{ij}\\f$ is the extrinsic curvature, and \\f$\\alpha\\f$, \\f$\\beta^i\\f$,\n * \\f$\\gamma_{ij}\\f$ are the lapse, shift and 3-metric.\n *\n * In the main function, we loop over all neutrino species, and then call\n * the actual implementation of the curvature source terms.\n */\ntemplate <typename... NeutrinoSpecies>\nstruct ComputeSources {\n  using return_tags = tmpl::list<\n      ::Tags::Source<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>>...,\n      ::Tags::Source<Tags::TildeS<Frame::Inertial, NeutrinoSpecies>>...>;\n\n  using argument_tags =\n      tmpl::list<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeP<Frame::Inertial, NeutrinoSpecies>...,\n                 gr::Tags::Lapse<DataVector>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 Tags::GreyEmissivity<NeutrinoSpecies>...,\n                 Tags::GreyAbsorptionOpacity<NeutrinoSpecies>...,\n                 Tags::GreyScatteringOpacity<NeutrinoSpecies>...,\n                 Tags::TildeJ<NeutrinoSpecies>...,\n                 Tags::TildeHNormal<NeutrinoSpecies>...,\n                 Tags::TildeHSpatial<Frame::Inertial, NeutrinoSpecies>...,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n\n  static void apply(\n      const gsl::not_null<typename Tags::TildeE<\n          Frame::Inertial, NeutrinoSpecies>::type*>... sources_tilde_e,\n      const gsl::not_null<typename Tags::TildeS<\n          Frame::Inertial, NeutrinoSpecies>::type*>... sources_tilde_s,\n      const typename Tags::TildeE<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_e,\n      const typename Tags::TildeS<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_s,\n      const typename Tags::TildeP<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_p,\n      const Scalar<DataVector>& lapse, const tnsr::i<DataVector, 3>& d_lapse,\n      const tnsr::iJ<DataVector, 3>& d_shift,\n      const tnsr::ijj<DataVector, 3>& d_spatial_metric,\n      const tnsr::II<DataVector, 3>& inv_spatial_metric,\n      const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n      const tnsr::ii<DataVector, 3>& spatial_metric,\n      const Scalar<DataVector>& emissivity,\n      const Scalar<DataVector>& absorption_opacity,\n      const Scalar<DataVector>& scattering_opacity,\n      const typename Tags::TildeJ<NeutrinoSpecies>::type&... tilde_j,\n      const typename Tags::TildeHNormal<\n          NeutrinoSpecies>::type&... tilde_h_normal,\n      const typename Tags::TildeHSpatial<\n          Frame::Inertial, NeutrinoSpecies>::type&... tilde_h_spatial,\n      const tnsr::I<DataVector, 3>& spatial_velocity,\n      const Scalar<DataVector>& lorentz,\n      const Scalar<DataVector>& sqrt_det_spatial_metric) {\n    EXPAND_PACK_LEFT_TO_RIGHT(detail::compute_sources_impl(\n        sources_tilde_e, sources_tilde_s, tilde_e, tilde_s, tilde_p, lapse,\n        d_lapse, d_shift, d_spatial_metric, inv_spatial_metric,\n        extrinsic_curvature, spatial_metric, emissivity, absorption_opacity,\n        scattering_opacity, tilde_j, tilde_h_normal, tilde_h_spatial,\n        spatial_velocity, lorentz, sqrt_det_spatial_metric));\n  }\n};\n\n}  // namespace RadiationTransport::M1Grey\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Protocols/ImexSystem.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Characteristics.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1Closure.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1HydroCoupling.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/TimeDerivativeTerms.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup EvolutionSystemsGroup\n/// \\brief Items related to general relativistic radiation transport\nnamespace RadiationTransport {\n/// The M1 scheme for radiation transport\n///\n/// References:\n/// - Post-merger evolution of a neutron star-black hole binary with\n///   neutrino transport, \\cite Foucart2015vpa\nnamespace M1Grey {\n\ntemplate <typename NeutrinoSpeciesList>\nstruct System;\n\ntemplate <typename... NeutrinoSpecies>\nstruct System<tmpl::list<NeutrinoSpecies...>>\n    : tt::ConformsTo<imex::protocols::ImexSystem> {\n  static constexpr bool is_in_flux_conservative_form = true;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = 3;\n  // If coupling to hydro, we'll want 3D equations of state\n  // i.e. P(rho,T,Ye)... but this is not implemented yet.\n  // For early tests of M1, we'll ignore coupling to the fluid\n  // and provide analytical expressions for its 4-velocity / LorentzFactor\n\n  using boundary_conditions_base =\n      BoundaryConditions::BoundaryCondition<tmpl::list<NeutrinoSpecies...>>;\n\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...>>;\n  using flux_variables =\n      tmpl::list<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...>;\n  using gradient_variables = tmpl::list<>;\n  using primitive_variables_tag = ::Tags::Variables<tmpl::list<\n      Tags::ClosureFactor<NeutrinoSpecies>...,\n      Tags::TildeP<Frame::Inertial, NeutrinoSpecies>...,\n      Tags::TildeJ<NeutrinoSpecies>..., Tags::TildeHNormal<NeutrinoSpecies>...,\n      Tags::TildeHSpatial<Frame::Inertial, NeutrinoSpecies>...,\n      Tags::M1HydroCouplingNormal<NeutrinoSpecies>...,\n      Tags::M1HydroCouplingSpatial<Frame::Inertial, NeutrinoSpecies>...>>;\n  // gr::tags_for_hydro contains all these tags plus SqrtDetSpatialMetric,\n  // so it can be used when adding M1 coupling to hydro\n  using spacetime_variables_tag = ::Tags::Variables<\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>>>;\n  using hydro_variables_tag = ::Tags::Variables<\n      tmpl::list<hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 Tags::GreyEmissivity<NeutrinoSpecies>...,\n                 Tags::GreyAbsorptionOpacity<NeutrinoSpecies>...,\n                 Tags::GreyScatteringOpacity<NeutrinoSpecies>...>>;\n\n  using compute_volume_time_derivative_terms =\n      TimeDerivativeTerms<NeutrinoSpecies...>;\n\n  using inverse_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, 3>;\n\n  template <typename Species>\n  struct ImplicitSector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n    using tensors = tmpl::list<Tags::TildeE<Frame::Inertial, Species>,\n                               Tags::TildeS<Frame::Inertial, Species>>;\n    // Need initial_guess\n    using initial_guess = imex::GuessExplicitResult;\n\n    struct M1Solve {\n      // tags in addition to sector tensors to be made available in databox\n      using tags_from_evolution = tmpl::list<\n          // spacetime\n          gr::Tags::Lapse<DataVector>, gr::Tags::SpatialMetric<DataVector, 3>,\n          gr::Tags::SqrtDetSpatialMetric<DataVector>,\n          gr::Tags::InverseSpatialMetric<DataVector, 3>,\n          // neutrino\n          Tags::GreyEmissivity<NeutrinoSpecies>...,\n          Tags::GreyAbsorptionOpacity<NeutrinoSpecies>...,\n          Tags::GreyScatteringOpacity<NeutrinoSpecies>...,\n          // hydro\n          hydro::Tags::LorentzFactor<DataVector>,\n          hydro::Tags::SpatialVelocity<DataVector, 3>>;\n\n      using simple_tags = tmpl::list<::Tags::Variables<tmpl::list<\n          Tags::ClosureFactor<NeutrinoSpecies>...,\n          Tags::TildeP<Frame::Inertial, NeutrinoSpecies>...,\n          Tags::TildeJ<NeutrinoSpecies>...,\n          Tags::TildeHNormal<NeutrinoSpecies>...,\n          Tags::TildeHSpatial<Frame::Inertial, NeutrinoSpecies>...>>>;\n      using compute_tags = tmpl::list<>;\n\n      using source_prep = tmpl::list<ComputeM1Closure<tmpl::list<Species>>>;\n      using jacobian_prep = tmpl::list<ComputeM1Closure<tmpl::list<Species>>>;\n\n      using source = ComputeM1HydroCoupling<tmpl::list<Species>>;\n      using jacobian = ComputeM1HydroCouplingJacobian<Species>;\n    };\n\n    using solve_attempts = tmpl::list<M1Solve>;\n  };\n\n  using implicit_sectors = tmpl::list<ImplicitSector<NeutrinoSpecies>...>;\n};\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n\nclass DataVector;\n\n/// Namespace for all radiation transport algorithms\nnamespace RadiationTransport {\n/// Namespace for the grey-M1 radiation transport scheme\nnamespace M1Grey {\n/// %Tags for the evolution of neutrinos using a grey M1 scheme.\nnamespace Tags {\n\n/// The characteristic speeds\nstruct CharacteristicSpeeds : db::SimpleTag {\n  using type = std::array<DataVector, 4>;\n};\n\n/// The densitized energy density of neutrinos of a given species\n/// \\f${\\tilde E}\\f$\ntemplate <typename Fr, class Species>\nstruct TildeE : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return Frame::prefix<Fr>() + \"TildeE_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The densitized momentum density of neutrinos of a given species\n/// \\f${\\tilde S_i}\\f$\ntemplate <typename Fr, class Species>\nstruct TildeS : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Fr>;\n  static std::string name() {\n    return Frame::prefix<Fr>() + \"TildeS_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The densitized pressure tensor of neutrinos of a given species\n/// \\f${\\tilde P^{ij}}\\f$\n/// computed from \\f${\\tilde E}\\f$, \\f${\\tilde S_i}\\f$ using the M1 closure\ntemplate <typename Fr, class Species>\nstruct TildeP : db::SimpleTag {\n  using type = tnsr::II<DataVector, 3, Fr>;\n  static std::string name() {\n    return Frame::prefix<Fr>() + \"TildeP_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The upper index momentum density of a neutrino species.\n///\n/// This tag does not know the species of neutrinos being used.\n/// \\f${\\tilde S^i}\\f$\ntemplate <typename Fr>\nstruct TildeSVector : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Fr>;\n  static std::string name() { return Frame::prefix<Fr>() + \"TildeSVector\"; }\n};\n\n/// The M1 closure factor of neutrinos of\n/// a given species \\f${\\xi}\\f$\ntemplate <class Species>\nstruct ClosureFactor : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"ClosureFactor_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The fluid-frame densitized energy density of neutrinos of\n/// a given species \\f${\\tilde J}\\f$\ntemplate <class Species>\nstruct TildeJ : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"TildeJ_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The normal component of the fluid-frame momentum density of neutrinos of\n/// a given species \\f${\\tilde H}^a t_a\\f$\ntemplate <class Species>\nstruct TildeHNormal : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"TildeHNormal_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The spatial components of the fluid-frame momentum density of neutrinos of\n/// a given species \\f${\\tilde H}^a {\\gamma}_{ia}\\f$\ntemplate <typename Fr, class Species>\nstruct TildeHSpatial : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Fr>;\n  static std::string name() {\n    return Frame::prefix<Fr>() + \"TildeHSpatial_\" +\n           neutrinos::get_name(Species{});\n  }\n};\n\n/// The emissivity of the fluid for neutrinos of a given species.\n///\n/// By convention, this is the emitted energy per unit volume.\ntemplate <class Species>\nstruct GreyEmissivity : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"GreyEmissivity_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The absorption opacity of the fluid for neutrinos of a\n/// given species.\n///\n/// As c=1, this is both the absorption per unit length\n/// and per unit time.\ntemplate <class Species>\nstruct GreyAbsorptionOpacity : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"GreyAbsorptionOpacity_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The scattering opacity of the fluid for neutrinos of a\n/// given species.\n///\n/// As c=1, this is both the absorption per unit length and per unit time.\ntemplate <class Species>\nstruct GreyScatteringOpacity : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"GreyScatteringOpacity_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The normal component of the source term coupling the M1 and hydro equations.\n///\n/// This is the source term for the evolution of \\f${\\tilde E}\\f$ (by\n/// convention, added with a positive sign to \\f${\\tilde E}\\f$ and a negative\n/// sign to the hydro variable \\f${\\tilde \\tau}\\f$)\ntemplate <class Species>\nstruct M1HydroCouplingNormal : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() {\n    return \"M1HydroCouplingNormal_\" + neutrinos::get_name(Species{});\n  }\n};\n\n/// The spatial components of source term coupling the M1 and hydro equations.\n///\n/// i.e. \\f${\\tilde G}^a \\gamma_{ia}\\f$.\n/// This is the source term for the evolution of \\f${\\tilde S}_i\\f$ (by\n/// convention, added with a positive sign to the neutrino \\f${\\tilde S}_i\\f$\n/// and a negative sign to the hydro variable \\f${\\tilde S}_i\\f$)\ntemplate <typename Fr, class Species>\nstruct M1HydroCouplingSpatial : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Fr>;\n  static std::string name() {\n    return Frame::prefix<Fr>() + \"M1HydroCouplingSpatial_\" +\n           neutrinos::get_name(Species{});\n  }\n};\n\n}  // namespace Tags\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/M1Grey/TimeDerivativeTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Fluxes.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Sources.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RadiationTransport::M1Grey {\ntemplate <typename... NeutrinoSpecies>\nstruct TimeDerivativeTerms {\n  struct TildeSUp : db::SimpleTag {\n    using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n  };\n\n  using temporary_tags =\n      tmpl::list<TildeSUp, gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n  using argument_tags =\n      tmpl::list<Tags::TildeE<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeS<Frame::Inertial, NeutrinoSpecies>...,\n                 Tags::TildeP<Frame::Inertial, NeutrinoSpecies>...,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                 Tags::GreyEmissivity<NeutrinoSpecies>...,\n                 Tags::GreyAbsorptionOpacity<NeutrinoSpecies>...,\n                 Tags::GreyScatteringOpacity<NeutrinoSpecies>...,\n                 Tags::TildeJ<NeutrinoSpecies>...,\n                 Tags::TildeHNormal<NeutrinoSpecies>...,\n                 Tags::TildeHSpatial<Frame::Inertial, NeutrinoSpecies>...,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n\n  static ::evolution::dg::TimeDerivativeDecisions<3> apply(\n      const gsl::not_null<typename Tags::TildeE<\n          Frame::Inertial,\n          NeutrinoSpecies>::type*>... non_flux_terms_dt_tilde_e,\n      const gsl::not_null<typename Tags::TildeS<\n          Frame::Inertial,\n          NeutrinoSpecies>::type*>... non_flux_terms_dt_tilde_s,\n\n      const gsl::not_null<typename ::Tags::Flux<\n          Tags::TildeE<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n          Frame::Inertial>::type*>... tilde_e_flux,\n      const gsl::not_null<typename ::Tags::Flux<\n          Tags::TildeS<Frame::Inertial, NeutrinoSpecies>, tmpl::size_t<3>,\n          Frame::Inertial>::type*>... tilde_s_flux,\n\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_s_M,\n      const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          temp_inv_spatial_metric,\n\n      const typename Tags::TildeE<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_e,\n      const typename Tags::TildeS<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_s,\n      const typename Tags::TildeP<Frame::Inertial,\n                                  NeutrinoSpecies>::type&... tilde_p,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n      const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n      const tnsr::i<DataVector, 3>& d_lapse,\n      const tnsr::iJ<DataVector, 3>& d_shift,\n      const tnsr::ijj<DataVector, 3>& d_spatial_metric,\n      const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n      const Scalar<DataVector>& emissivity,\n      const Scalar<DataVector>& absorption_opacity,\n      const Scalar<DataVector>& scattering_opacity,\n      const typename Tags::TildeJ<NeutrinoSpecies>::type&... tilde_j,\n      const typename Tags::TildeHNormal<\n          NeutrinoSpecies>::type&... tilde_h_normal,\n      const typename Tags::TildeHSpatial<\n          Frame::Inertial, NeutrinoSpecies>::type&... tilde_h_spatial,\n      const tnsr::I<DataVector, 3>& spatial_velocity,\n      const Scalar<DataVector>& lorentz,\n      const Scalar<DataVector>& sqrt_det_spatial_metric) {\n    *temp_inv_spatial_metric = inv_spatial_metric;\n    EXPAND_PACK_LEFT_TO_RIGHT(detail::compute_fluxes_impl(\n        tilde_e_flux, tilde_s_flux, tilde_s_M, tilde_e, tilde_s, tilde_p, lapse,\n        shift, spatial_metric, inv_spatial_metric));\n    EXPAND_PACK_LEFT_TO_RIGHT(detail::compute_sources_impl(\n        non_flux_terms_dt_tilde_e, non_flux_terms_dt_tilde_s, tilde_e, tilde_s,\n        tilde_p, lapse, d_lapse, d_shift, d_spatial_metric, inv_spatial_metric,\n        extrinsic_curvature, spatial_metric, emissivity, absorption_opacity,\n        scattering_opacity, tilde_j, tilde_h_normal, tilde_h_spatial,\n        spatial_velocity, lorentz, sqrt_det_spatial_metric));\n    return {true};\n  }\n};\n}  // namespace RadiationTransport::M1Grey\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/NoNeutrinos/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  System.hpp\n  )\n\n\n\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\ingroup EvolutionSystemsGroup\n/// \\brief Items related to general relativistic radiation transport\nnamespace RadiationTransport::NoNeutrinos {\n/// No neutrino placeholder\nstruct System {};\n}  // namespace RadiationTransport::NoNeutrinos\n"
  },
  {
    "path": "src/Evolution/Systems/RadiationTransport/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"Utilities/PrettyType.hpp\"\n\n#define MAX_NUMBER_OF_NEUTRINO_ENERGY_BINS 12\n\n/// Namespace for neutrino physics\nnamespace neutrinos {\n\ntemplate <size_t EnergyBin>\nstruct ElectronNeutrinos {\n  static constexpr size_t energy_bin = EnergyBin;\n};\ntemplate <size_t EnergyBin>\nstruct ElectronAntiNeutrinos {\n  static constexpr size_t energy_bin = EnergyBin;\n};\ntemplate <size_t EnergyBin>\nstruct HeavyLeptonNeutrinos {\n  static constexpr size_t energy_bin = EnergyBin;\n};\n\ntemplate <template <size_t> class U, size_t EnergyBin>\nstd::string get_name(const U<EnergyBin>& /*species*/) {\n  return pretty_type::short_name<U<EnergyBin>>() + std::to_string(EnergyBin);\n}\n\n}  // namespace neutrinos\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarAdvection::BoundaryConditions {\ntemplate <size_t Dim>\nBoundaryCondition<Dim>::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\ntemplate <size_t Dim>\nvoid BoundaryCondition<Dim>::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class BoundaryCondition<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace ScalarAdvection::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace ScalarAdvection {\n/// \\brief Boundary conditions for the ScalarAdvection system\nnamespace BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\ntemplate <size_t Dim>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace BoundaryConditions\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  Factory.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\ntemplate <size_t Dim>\nusing standard_boundary_conditions =\n    tmpl::list<domain::BoundaryConditions::Periodic<BoundaryCondition<Dim>>>;\n}  // namespace ScalarAdvection::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Rusanov.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  NamespaceDocs.hpp\n  Rusanov.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/ScalarAdvection/BoundaryCorrections/Rusanov.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::BoundaryCorrections {\ntemplate <size_t Dim>\nusing standard_boundary_corrections = tmpl::list<Rusanov<Dim>>;\n}  // namespace ScalarAdvection::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace ScalarAdvection::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryCorrections/Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/BoundaryCorrections/Rusanov.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarAdvection::BoundaryCorrections {\ntemplate <size_t Dim>\nRusanov<Dim>::Rusanov(CkMigrateMessage* msg) : BoundaryCorrection(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::BoundaryCorrection> Rusanov<Dim>::get_clone() const {\n  return std::make_unique<Rusanov>(*this);\n}\n\ntemplate <size_t Dim>\nvoid Rusanov<Dim>::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n}\n\ntemplate <size_t Dim>\ndouble Rusanov<Dim>::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_u,\n    const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_u,\n    const gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n    const Scalar<DataVector>& u,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_u,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity_field,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) {\n  Scalar<DataVector>& normal_dot_velocity = *packaged_u;\n  dot_product(make_not_null(&normal_dot_velocity), velocity_field,\n              normal_covector);\n  if (normal_dot_mesh_velocity.has_value()) {\n    get(*packaged_abs_char_speed) =\n        abs(get(normal_dot_velocity) - get(*normal_dot_mesh_velocity));\n  } else {\n    get(*packaged_abs_char_speed) = abs(get(normal_dot_velocity));\n  }\n  *packaged_u = u;\n  normal_dot_flux(packaged_normal_dot_flux_u, normal_covector, flux_u);\n  return max(get(*packaged_abs_char_speed));\n}\n\ntemplate <size_t Dim>\nvoid Rusanov<Dim>::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> boundary_correction_u,\n    const Scalar<DataVector>& u_int,\n    const Scalar<DataVector>& normal_dot_flux_u_int,\n    const Scalar<DataVector>& abs_char_speed_int,\n    const Scalar<DataVector>& u_ext,\n    const Scalar<DataVector>& normal_dot_flux_u_ext,\n    const Scalar<DataVector>& abs_char_speed_ext,\n    const dg::Formulation dg_formulation) {\n  if (dg_formulation == dg::Formulation::WeakInertial) {\n    get(*boundary_correction_u) =\n        0.5 * (get(normal_dot_flux_u_int) - get(normal_dot_flux_u_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(u_ext) - get(u_int));\n  } else {\n    get(*boundary_correction_u) =\n        -0.5 * (get(normal_dot_flux_u_int) + get(normal_dot_flux_u_ext)) -\n        0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n            (get(u_ext) - get(u_int));\n  }\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID\n    ScalarAdvection::BoundaryCorrections::Rusanov<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) template class Rusanov<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace ScalarAdvection::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/BoundaryCorrections/Rusanov.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection {\nnamespace BoundaryCorrections {\n/*!\n * \\brief A Rusanov/local Lax-Friedrichs Riemann solver\n *\n * Let \\f$U\\f$ be the evolved scalar variable, \\f$F^i\\f$ the corresponding\n * fluxes, and \\f$n_i\\f$ be the outward directed unit normal to the interface.\n * Denoting \\f$F := n_i F^i\\f$, the %Rusanov boundary correction is\n *\n * \\f{align*}\n * G_\\text{Rusanov} = \\frac{F_\\text{int} - F_\\text{ext}}{2} -\n * \\frac{\\text{max}\\left(|\\lambda_\\text{int}|,\n * |\\lambda_\\text{ext}|\\right)}{2} \\left(U_\\text{ext} - U_\\text{int}\\right),\n * \\f}\n *\n * where \"int\" and \"ext\" stand for interior and exterior, and\n * \\f$\\lambda\\f$ is the characteristic/signal speed. The minus sign in\n * front of the \\f$F_{\\text{ext}}\\f$ is necessary because the outward directed\n * normal of the neighboring element has the opposite sign, i.e.\n * \\f$n_i^{\\text{ext}}=-n_i^{\\text{int}}\\f$.\n *\n * For the ScalarAdvection system, \\f$\\lambda\\f$ is given as\n *\n * \\f{align*}\n * \\lambda = |v| = \\sqrt{v^iv_i}\n * \\f}\n *\n * where \\f$v^i\\f$ is the advection velocity field.\n *\n * \\note In the strong form the `dg_boundary_terms` function returns\n * \\f$G - F_\\text{int}\\f$\n */\ntemplate <size_t Dim>\nclass Rusanov final : public evolution::BoundaryCorrection {\n private:\n  struct AbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the Rusanov or local Lax-Friedrichs boundary correction term \"\n      \"for the ScalarAdvection system.\"};\n\n  Rusanov() = default;\n  Rusanov(const Rusanov&) = default;\n  Rusanov& operator=(const Rusanov&) = default;\n  Rusanov(Rusanov&&) = default;\n  Rusanov& operator=(Rusanov&&) = default;\n  ~Rusanov() override = default;\n\n  /// \\cond\n  explicit Rusanov(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Rusanov);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::U, ::Tags::NormalDotFlux<Tags::U>, AbsCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<Tags::VelocityField<Dim>>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  static double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_u,\n      gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_u,\n      gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n      const Scalar<DataVector>& u,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_u,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& velocity_field,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity);\n\n  static void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> boundary_correction_u,\n      const Scalar<DataVector>& u_int,\n      const Scalar<DataVector>& normal_dot_flux_u_int,\n      const Scalar<DataVector>& abs_char_speed_int,\n      const Scalar<DataVector>& u_ext,\n      const Scalar<DataVector>& normal_dot_flux_u_ext,\n      const Scalar<DataVector>& abs_char_speed_ext,\n      dg::Formulation dg_formulation);\n};\n}  // namespace BoundaryCorrections\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarAdvection)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Characteristics.cpp\n  Fluxes.cpp\n  TimeDerivativeTerms.cpp\n  VelocityField.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Characteristics.hpp\n  Fluxes.hpp\n  System.hpp\n  Tags.hpp\n  TimeDerivativeTerms.hpp\n  VelocityField.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  CoordinateMaps\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainBoundaryConditions\n  DomainStructure\n  DgSubcell\n  ErrorHandling\n  Events\n  Evolution\n  FiniteDifference\n  Options\n  Serialization\n  Spectral\n  Utilities\n  )\n\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Instantiations)\nadd_subdirectory(Subcell)\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/Characteristics.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarAdvection::Tags {\n\ntemplate <size_t Dim>\nvoid LargestCharacteristicSpeedCompute<Dim>::function(\n    const gsl::not_null<double*> speed,\n    const tnsr::I<DataVector, Dim>& velocity_field) {\n  *speed = max(get(magnitude<DataVector>(velocity_field)));\n}\n\n}  // namespace ScalarAdvection::Tags\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void ScalarAdvection::Tags::LargestCharacteristicSpeedCompute<DIM( \\\n      data)>::function(gsl::not_null<double*> speed,                          \\\n                       const tnsr::I<DataVector, DIM(data)>& velocity_field);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection {\nnamespace Tags {\n/*!\n * \\brief Compute the largest characteristic speed of the ScalarAdvection system\n *\n * \\f{align*}\n * \\lambda_\\text{maxabs} = \\sqrt{v^iv_i}\n * \\f}\n *\n * where \\f$v^i\\f$ is the velocity field.\n */\ntemplate <size_t Dim>\nstruct LargestCharacteristicSpeedCompute : LargestCharacteristicSpeed,\n                                           db::ComputeTag {\n  using argument_tags = tmpl::list<Tags::VelocityField<Dim>>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(const gsl::not_null<double*> speed,\n                       const tnsr::I<DataVector, Dim>& velocity_field);\n};\n}  // namespace Tags\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/AoWeno.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/AoWeno.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/AoWeno.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::fd {\ntemplate <size_t Dim>\nAoWeno53<Dim>::AoWeno53(const double gamma_hi, const double gamma_lo,\n                        const double epsilon,\n                        const size_t nonlinear_weight_exponent)\n    : gamma_hi_(gamma_hi),\n      gamma_lo_(gamma_lo),\n      epsilon_(epsilon),\n      nonlinear_weight_exponent_(nonlinear_weight_exponent) {\n  std::tie(reconstruct_, reconstruct_lower_neighbor_,\n           reconstruct_upper_neighbor_) =\n      ::fd::reconstruction::aoweno_53_function_pointers<Dim>(\n          nonlinear_weight_exponent_);\n}\n\ntemplate <size_t Dim>\nAoWeno53<Dim>::AoWeno53(CkMigrateMessage* const msg)\n    : Reconstructor<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<Reconstructor<Dim>> AoWeno53<Dim>::get_clone() const {\n  return std::make_unique<AoWeno53>(*this);\n}\n\ntemplate <size_t Dim>\nvoid AoWeno53<Dim>::pup(PUP::er& p) {\n  Reconstructor<Dim>::pup(p);\n  p | gamma_hi_;\n  p | gamma_lo_;\n  p | epsilon_;\n  p | nonlinear_weight_exponent_;\n  if (p.isUnpacking()) {\n    std::tie(reconstruct_, reconstruct_lower_neighbor_,\n             reconstruct_upper_neighbor_) =\n        ::fd::reconstruction::aoweno_53_function_pointers<Dim>(\n            nonlinear_weight_exponent_);\n  }\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID AoWeno53<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid AoWeno53<Dim>::reconstruct(\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_upper_face,\n    const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh) const {\n  reconstruct_work(\n      vars_on_lower_face, vars_on_upper_face,\n      [this](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n             const auto& volume_tensor, const auto& ghost_cell_vars,\n             const auto& subcell_extents, const size_t number_of_variables) {\n        reconstruct_(upper_face_vars_ptr, lower_face_vars_ptr, volume_tensor,\n                     ghost_cell_vars, subcell_extents, number_of_variables,\n                     gamma_hi_, gamma_lo_, epsilon_);\n      },\n      volume_vars, element, ghost_data, subcell_mesh, ghost_zone_size());\n}\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid AoWeno53<Dim>::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh,\n    const Direction<Dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work(\n      vars_on_face,\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<Dim>& subcell_extents,\n             const Index<Dim>& ghost_data_extents,\n             const Direction<Dim>& local_direction_to_reconstruct) {\n        reconstruct_lower_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, gamma_hi_, gamma_lo_, epsilon_);\n      },\n      [this](const auto tensor_component_on_face_ptr,\n             const auto& tensor_component_volume,\n             const auto& tensor_component_neighbor,\n             const Index<Dim>& subcell_extents,\n             const Index<Dim>& ghost_data_extents,\n             const Direction<Dim>& local_direction_to_reconstruct) {\n        reconstruct_upper_neighbor_(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct, gamma_hi_, gamma_lo_, epsilon_);\n      },\n      volume_vars, element, ghost_data, subcell_mesh, direction_to_reconstruct,\n      ghost_zone_size());\n}\n\ntemplate <size_t Dim>\nbool operator==(const AoWeno53<Dim>& lhs, const AoWeno53<Dim>& rhs) {\n  return lhs.gamma_hi_ == rhs.gamma_hi_ and lhs.gamma_lo_ == rhs.gamma_lo_ and\n         lhs.epsilon_ == rhs.epsilon_ and\n         lhs.nonlinear_weight_exponent_ == rhs.nonlinear_weight_exponent_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const AoWeno53<Dim>& lhs, const AoWeno53<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAGS_LIST(data)                                                      \\\n  tmpl::list<ScalarAdvection::Tags::U,                                       \\\n             ::Tags::Flux<ScalarAdvection::Tags::U, tmpl::size_t<DIM(data)>, \\\n                          Frame::Inertial>,                                  \\\n             ScalarAdvection::Tags::VelocityField<DIM(data)>>\n\n#define INSTANTIATION(r, data)                                         \\\n  template class AoWeno53<DIM(data)>;                                  \\\n  template bool operator==<DIM(data)>(const AoWeno53<DIM(data)>& lhs,  \\\n                                      const AoWeno53<DIM(data)>& rhs); \\\n  template bool operator!=<DIM(data)>(const AoWeno53<DIM(data)>& lhs,  \\\n                                      const AoWeno53<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n#undef INSTANTIATION\n\n#define INSTANTIATION(r, data)                                              \\\n  template void AoWeno53<DIM(data)>::reconstruct(                           \\\n      gsl::not_null<std::array<Variables<TAGS_LIST(data)>, DIM(data)>*>     \\\n          vars_on_lower_face,                                               \\\n      gsl::not_null<std::array<Variables<TAGS_LIST(data)>, DIM(data)>*>     \\\n          vars_on_upper_face,                                               \\\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,   \\\n      const Element<DIM(data)>& element,                                    \\\n      const DirectionalIdMap<DIM(data), evolution::dg::subcell::GhostData>& \\\n          ghost_data,                                                       \\\n      const Mesh<DIM(data)>& subcell_mesh) const;                           \\\n  template void AoWeno53<DIM(data)>::reconstruct_fd_neighbor(               \\\n      gsl::not_null<Variables<TAGS_LIST(data)>*> vars_on_face,              \\\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,   \\\n      const Element<DIM(data)>& element,                                    \\\n      const DirectionalIdMap<DIM(data), evolution::dg::subcell::GhostData>& \\\n          ghost_data,                                                       \\\n      const Mesh<DIM(data)>& subcell_mesh,                                  \\\n      const Direction<DIM(data)> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n#undef TAGS_LIST\n#undef DIM\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/AoWeno.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\nnamespace Tags {\ntemplate <typename TagsList>\nclass Variables;\n}  // namespace Tags\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace ScalarAdvection::fd {\n/*!\n * \\brief Adaptive-order WENO reconstruction hybridizing orders 5 and 3. See\n * ::fd::reconstruction::aoweno_53() for details.\n */\ntemplate <size_t Dim>\nclass AoWeno53 : public Reconstructor<Dim> {\n private:\n  using face_vars_tags =\n      tmpl::list<ScalarAdvection::Tags::U,\n                 ::Tags::Flux<ScalarAdvection::Tags::U, tmpl::size_t<Dim>,\n                              Frame::Inertial>>;\n  using volume_vars_tags = tmpl::list<ScalarAdvection::Tags::U>;\n\n public:\n  struct GammaHi {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The linear weight for the 5th-order stencil.\"};\n  };\n  struct GammaLo {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The linear weight for the central 3rd-order stencil.\"};\n  };\n  struct Epsilon {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The parameter added to the oscillation indicators to avoid division \"\n        \"by zero\"};\n  };\n  struct NonlinearWeightExponent {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The exponent q to which the oscillation indicators are raised\"};\n  };\n\n  using options =\n      tmpl::list<GammaHi, GammaLo, Epsilon, NonlinearWeightExponent>;\n  static constexpr Options::String help{\n      \"Adaptive-order WENO reconstruction hybridizing orders 5 and 3.\"};\n\n  AoWeno53() = default;\n  AoWeno53(AoWeno53&&) = default;\n  AoWeno53& operator=(AoWeno53&&) = default;\n  AoWeno53(const AoWeno53&) = default;\n  AoWeno53& operator=(const AoWeno53&) = default;\n  ~AoWeno53() override = default;\n\n  AoWeno53(double gamma_hi, double gamma_lo, double epsilon,\n           size_t nonlinear_weight_exponent);\n\n  explicit AoWeno53(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(Reconstructor<Dim>, AoWeno53);\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor<Dim>> override;\n\n  void pup(PUP::er& p) override;\n\n  size_t ghost_zone_size() const override { return 3; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<volume_vars_tags>,\n                 domain::Tags::Element<Dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>>;\n\n  template <typename TagsList>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_lower_face,\n      gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_upper_face,\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,\n      const Element<Dim>& element,\n      const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<Dim>& subcell_mesh) const;\n\n  template <typename TagsList>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<TagsList>*> vars_on_face,\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,\n      const Element<Dim>& element,\n      const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<Dim>& subcell_mesh,\n      const Direction<Dim> direction_to_reconstruct) const;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const AoWeno53<LocalDim>& lhs,\n                         const AoWeno53<LocalDim>& rhs);\n\n  double gamma_hi_ = std::numeric_limits<double>::signaling_NaN();\n  double gamma_lo_ = std::numeric_limits<double>::signaling_NaN();\n  double epsilon_ = std::numeric_limits<double>::signaling_NaN();\n  size_t nonlinear_weight_exponent_ = 0;\n\n  void (*reconstruct_)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                       gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                       const gsl::span<const double>&,\n                       const DirectionMap<Dim, gsl::span<const double>>&,\n                       const Index<Dim>&, size_t, double, double, double);\n  void (*reconstruct_lower_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<Dim>&, const Index<Dim>&,\n                                      const Direction<Dim>&, const double&,\n                                      const double&, const double&);\n  void (*reconstruct_upper_neighbor_)(gsl::not_null<DataVector*>,\n                                      const DataVector&, const DataVector&,\n                                      const Index<Dim>&, const Index<Dim>&,\n                                      const Direction<Dim>&, const double&,\n                                      const double&, const double&);\n};\n\ntemplate <size_t Dim>\nbool operator==(const AoWeno53<Dim>& lhs, const AoWeno53<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const AoWeno53<Dim>& lhs, const AoWeno53<Dim>& rhs);\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AoWeno.cpp\n  MonotonisedCentral.cpp\n  Reconstructor.cpp\n  RegisterDerivedWithCharm.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AoWeno.hpp\n  Factory.hpp\n  FiniteDifference.hpp\n  MonotonisedCentral.hpp\n  Reconstructor.hpp\n  ReconstructWork.hpp\n  ReconstructWork.tpp\n  RegisterDerivedWithCharm.hpp\n  Tags.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/AoWeno.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/FiniteDifference.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ScalarAdvection {\n/*!\n * \\brief Finite difference functionality for ScalarAdvection system\n */\nnamespace fd {}\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/MonotonisedCentral.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::fd {\ntemplate <size_t Dim>\nMonotonisedCentral<Dim>::MonotonisedCentral(CkMigrateMessage* const msg)\n    : Reconstructor<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<Reconstructor<Dim>> MonotonisedCentral<Dim>::get_clone() const {\n  return std::make_unique<MonotonisedCentral>(*this);\n}\n\ntemplate <size_t Dim>\nvoid MonotonisedCentral<Dim>::pup(PUP::er& p) {\n  Reconstructor<Dim>::pup(p);\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID MonotonisedCentral<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid MonotonisedCentral<Dim>::reconstruct(\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_upper_face,\n    const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh) const {\n  reconstruct_work(\n      vars_on_lower_face, vars_on_upper_face,\n      [](auto upper_face_vars_ptr, auto lower_face_vars_ptr,\n         const auto& volume_variables, const auto& ghost_cell_vars,\n         const auto& subcell_extents, const size_t number_of_variables) {\n        ::fd::reconstruction::monotonised_central(\n            upper_face_vars_ptr, lower_face_vars_ptr, volume_variables,\n            ghost_cell_vars, subcell_extents, number_of_variables);\n      },\n      volume_vars, element, ghost_data, subcell_mesh, ghost_zone_size());\n}\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid MonotonisedCentral<Dim>::reconstruct_fd_neighbor(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh,\n    const Direction<Dim> direction_to_reconstruct) const {\n  reconstruct_fd_neighbor_work(\n      vars_on_face,\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor, const auto& subcell_extents,\n         const auto& ghost_data_extents,\n         const auto& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Lower,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      [](const auto tensor_component_on_face_ptr,\n         const auto& tensor_component_volume,\n         const auto& tensor_component_neighbor, const auto& subcell_extents,\n         const auto& ghost_data_extents,\n         const auto& local_direction_to_reconstruct) {\n        ::fd::reconstruction::reconstruct_neighbor<\n            Side::Upper,\n            ::fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n            tensor_component_on_face_ptr, tensor_component_volume,\n            tensor_component_neighbor, subcell_extents, ghost_data_extents,\n            local_direction_to_reconstruct);\n      },\n      volume_vars, element, ghost_data, subcell_mesh, direction_to_reconstruct,\n      ghost_zone_size());\n}\n\ntemplate <size_t Dim>\nbool operator==(const MonotonisedCentral<Dim>& /*lhs*/,\n                const MonotonisedCentral<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const MonotonisedCentral<Dim>& lhs,\n                const MonotonisedCentral<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAGS_LIST(data)                                                      \\\n  tmpl::list<ScalarAdvection::Tags::U,                                       \\\n             ::Tags::Flux<ScalarAdvection::Tags::U, tmpl::size_t<DIM(data)>, \\\n                          Frame::Inertial>,                                  \\\n             Tags::VelocityField<DIM(data)>>\n\n#define INSTANTIATION(r, data)                                   \\\n  template class MonotonisedCentral<DIM(data)>;                  \\\n  template bool operator==                                       \\\n      <DIM(data)>(const MonotonisedCentral<DIM(data)>& /*lhs*/,  \\\n                  const MonotonisedCentral<DIM(data)>& /*rhs*/); \\\n  template bool operator!=                                       \\\n      <DIM(data)>(const MonotonisedCentral<DIM(data)>& lhs,      \\\n                  const MonotonisedCentral<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n#undef INSTANTIATION\n\n#define INSTANTIATION(r, data)                                              \\\n  template void MonotonisedCentral<DIM(data)>::reconstruct(                 \\\n      gsl::not_null<std::array<Variables<TAGS_LIST(data)>, DIM(data)>*>     \\\n          vars_on_lower_face,                                               \\\n      gsl::not_null<std::array<Variables<TAGS_LIST(data)>, DIM(data)>*>     \\\n          vars_on_upper_face,                                               \\\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,   \\\n      const Element<DIM(data)>& element,                                    \\\n      const DirectionalIdMap<DIM(data), evolution::dg::subcell::GhostData>& \\\n          ghost_data,                                                       \\\n      const Mesh<DIM(data)>& subcell_mesh) const;                           \\\n  template void MonotonisedCentral<DIM(data)>::reconstruct_fd_neighbor(     \\\n      gsl::not_null<Variables<TAGS_LIST(data)>*> vars_on_face,              \\\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,   \\\n      const Element<DIM(data)>& element,                                    \\\n      const DirectionalIdMap<DIM(data), evolution::dg::subcell::GhostData>& \\\n          ghost_data,                                                       \\\n      const Mesh<DIM(data)>& subcell_mesh,                                  \\\n      const Direction<DIM(data)> direction_to_reconstruct) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n#undef TAGS_LIST\n#undef DIM\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/MonotonisedCentral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace ScalarAdvection::fd {\n/*!\n * \\brief Monotonised central reconstruction. See\n * ::fd::reconstruction::monotonised_central() for details.\n */\ntemplate <size_t Dim>\nclass MonotonisedCentral : public Reconstructor<Dim> {\n private:\n  using volume_vars_tags = tmpl::list<ScalarAdvection::Tags::U>;\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Monotonised central reconstruction scheme.\"};\n\n  MonotonisedCentral() = default;\n  MonotonisedCentral(MonotonisedCentral&&) = default;\n  MonotonisedCentral& operator=(MonotonisedCentral&&) = default;\n  MonotonisedCentral(const MonotonisedCentral&) = default;\n  MonotonisedCentral& operator=(const MonotonisedCentral&) = default;\n  ~MonotonisedCentral() override = default;\n\n  void pup(PUP::er& p) override;\n\n  /// \\cond\n  explicit MonotonisedCentral(CkMigrateMessage* msg);\n  WRAPPED_PUPable_decl_base_template(Reconstructor<Dim>, MonotonisedCentral);\n  /// \\endcond\n\n  auto get_clone() const -> std::unique_ptr<Reconstructor<Dim>> override;\n\n  size_t ghost_zone_size() const override { return 2; }\n\n  using reconstruction_argument_tags =\n      tmpl::list<::Tags::Variables<volume_vars_tags>,\n                 domain::Tags::Element<Dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>>;\n\n  template <typename TagsList>\n  void reconstruct(\n      gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_lower_face,\n      gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_upper_face,\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,\n      const Element<Dim>& element,\n      const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<Dim>& subcell_mesh) const;\n\n  template <typename TagsList>\n  void reconstruct_fd_neighbor(\n      gsl::not_null<Variables<TagsList>*> vars_on_face,\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& volume_vars,\n      const Element<Dim>& element,\n      const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n          ghost_data,\n      const Mesh<Dim>& subcell_mesh,\n      const Direction<Dim> direction_to_reconstruct) const;\n};\n\ntemplate <size_t Dim>\nbool operator==(const MonotonisedCentral<Dim>& /*lhs*/,\n                const MonotonisedCentral<Dim>& /*rhs*/);\n\ntemplate <size_t Dim>\nbool operator!=(const MonotonisedCentral<Dim>& lhs,\n                const MonotonisedCentral<Dim>& rhs);\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/ReconstructWork.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\nnamespace gsl {\ntemplate <typename>\nclass not_null;\n}  // namespace gsl\nnamespace evolution::dg::subcell {\nclass GhostData;\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace ScalarAdvection::fd {\n/*!\n * \\brief Reconstructs the scalar field \\f$U\\f$. All results are written into\n * `vars_on_lower_face` and `vars_on_upper_face`.\n */\ntemplate <size_t Dim, typename TagsList, typename Reconstructor>\nvoid reconstruct_work(\n    gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_lower_face,\n    gsl::not_null<std::array<Variables<TagsList>, Dim>*> vars_on_upper_face,\n    const Reconstructor& reconstruct,\n    const Variables<tmpl::list<Tags::U>> volume_vars,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size);\n\n/*!\n * \\brief Reconstructs the scalar field \\f$U\\f$ for a given direction. All\n * results are written into `vars_on_face`.\n *\n * This is used on DG elements to reconstruct \\f$U\\f$ with their subcell\n * neighbors' solution (ghost data received) on the shared faces.\n */\ntemplate <size_t Dim, typename TagsList, typename ReconstructLower,\n          typename ReconstructUpper>\nvoid reconstruct_fd_neighbor_work(\n    gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const ReconstructLower& reconstruct_lower_neighbor,\n    const ReconstructUpper& reconstruct_upper_neighbor,\n    const Variables<tmpl::list<Tags::U>>& subcell_volume_vars,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh,\n    const Direction<Dim>& direction_to_reconstruct,\n    const size_t ghost_zone_size);\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/ReconstructWork.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::fd {\ntemplate <size_t Dim, typename TagsList, typename Reconstructor>\nvoid reconstruct_work(\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_lower_face,\n    const gsl::not_null<std::array<Variables<TagsList>, Dim>*>\n        vars_on_upper_face,\n    const Reconstructor& reconstruct,\n    const Variables<tmpl::list<Tags::U>> volume_vars,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size) {\n  // check if subcell mesh is isotropic\n  ASSERT(Mesh<Dim>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                   subcell_mesh.quadrature(0)) == subcell_mesh,\n         \"The subcell mesh should be isotropic but got \" << subcell_mesh);\n\n  // the number of subcell interface points, which is equal to the number of\n  // points we reconstruct to\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n\n  // create views/spans into the face data, which will be filled by the\n  // reconstructor\n  std::array<gsl::span<double>, Dim> upper_face_vars{};\n  std::array<gsl::span<double>, Dim> lower_face_vars{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(upper_face_vars, i) =\n        gsl::make_span(get<Tags::U>(gsl::at(*vars_on_upper_face, i))[0].data(),\n                       reconstructed_num_pts);\n    gsl::at(lower_face_vars, i) =\n        gsl::make_span(get<Tags::U>(gsl::at(*vars_on_lower_face, i))[0].data(),\n                       reconstructed_num_pts);\n  }\n\n  const size_t neighbor_num_ghost_fd_points =\n      ghost_zone_size * subcell_mesh.extents().slice_away(0).product();\n\n  // make span of ghost cell variables for each direction\n  DirectionMap<Dim, gsl::span<const double>> ghost_cell_vars{};\n\n  // for all Directions, make the pointers in ghost_cell_vars span to point\n  // the received neighbor data\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    const auto& neighbors_in_direction = element.neighbors().at(direction);\n\n    ASSERT(neighbors_in_direction.size() == 1,\n           \"Currently only support one neighbor in each direction, but \"\n           \"got \"\n               << neighbors_in_direction.size() << \" in direction \"\n               << direction);\n\n    const DataVector& neighbor_data =\n        ghost_data\n            .at(DirectionalId<Dim>{direction, *neighbors_in_direction.begin()})\n            .neighbor_ghost_data_for_reconstruction();\n\n    ASSERT(neighbor_data.size() != 0, \"The neighber data is empty in direction \"\n                                          << direction << \" on element id \"\n                                          << element.id());\n\n    ghost_cell_vars[direction] =\n        gsl::make_span(&neighbor_data[0], neighbor_num_ghost_fd_points);\n  }\n\n  // make span of volume variables\n  auto& volume_tensor = get<Tags::U>(volume_vars);\n  const size_t volume_num_pts = subcell_mesh.number_of_grid_points();\n  const gsl::span<const double> volume_vars_span =\n      gsl::make_span((volume_tensor)[0].data(), volume_num_pts);\n\n  // perform reconstruction\n  reconstruct(make_not_null(&upper_face_vars), make_not_null(&lower_face_vars),\n              volume_vars_span, ghost_cell_vars, subcell_mesh.extents(), 1);\n}\n\ntemplate <size_t Dim, typename TagsList, typename ReconstructLower,\n          typename ReconstructUpper>\nvoid reconstruct_fd_neighbor_work(\n    const gsl::not_null<Variables<TagsList>*> vars_on_face,\n    const ReconstructLower& reconstruct_lower_neighbor,\n    const ReconstructUpper& reconstruct_upper_neighbor,\n    const Variables<tmpl::list<Tags::U>>& subcell_volume_vars,\n    const Element<Dim>& element,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>& ghost_data,\n    const Mesh<Dim>& subcell_mesh,\n    const Direction<Dim>& direction_to_reconstruct,\n    const size_t ghost_zone_size) {\n  const DirectionalId<Dim> mortar_id{\n      direction_to_reconstruct,\n      *element.neighbors().at(direction_to_reconstruct).begin()};\n\n  Index<Dim> ghost_data_extents = subcell_mesh.extents();\n  ghost_data_extents[direction_to_reconstruct.dimension()] = ghost_zone_size;\n\n  // allocate Variable for storing data from neighbor\n  Variables<tmpl::list<Tags::U>> neighbor_vars{ghost_data_extents.product()};\n\n  {\n    ASSERT(ghost_data.contains(mortar_id),\n           \"The neighbor data does not contain the mortar: \" << mortar_id);\n\n    const DataVector& neighbor_data_in_direction =\n        ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n    std::copy(\n        neighbor_data_in_direction.begin(),\n        std::next(neighbor_data_in_direction.begin(),\n                  static_cast<std::ptrdiff_t>(ghost_data_extents.product())),\n        neighbor_vars.data());\n  }\n\n  const auto& tensor_volume = get<Tags::U>(subcell_volume_vars);\n  const auto& tensor_neighbor = get<Tags::U>(neighbor_vars);\n  auto& tensor_on_face = get<Tags::U>(*vars_on_face);\n\n  if (direction_to_reconstruct.side() == Side::Upper) {\n    for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n         ++tensor_index) {\n      reconstruct_upper_neighbor(\n          make_not_null(&tensor_on_face[tensor_index]),\n          tensor_volume[tensor_index], tensor_neighbor[tensor_index],\n          subcell_mesh.extents(), ghost_data_extents, direction_to_reconstruct);\n    }\n  } else {\n    for (size_t tensor_index = 0; tensor_index < tensor_on_face.size();\n         ++tensor_index) {\n      reconstruct_lower_neighbor(\n          make_not_null(&tensor_on_face[tensor_index]),\n          tensor_volume[tensor_index], tensor_neighbor[tensor_index],\n          subcell_mesh.extents(), ghost_data_extents, direction_to_reconstruct);\n    }\n  }\n}\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarAdvection::fd {\ntemplate <size_t Dim>\nReconstructor<Dim>::Reconstructor(CkMigrateMessage* const msg)\n    : PUP::able(msg) {}\n\ntemplate <size_t Dim>\nvoid Reconstructor<Dim>::pup(PUP::er& p) {\n  PUP::able::pup(p);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data) template class Reconstructor<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::fd {\n/// \\cond\ntemplate <size_t Dim>\nclass AoWeno53;\ntemplate <size_t Dim>\nclass MonotonisedCentral;\n/// \\endcond\n\n/*!\n * \\brief The base class from which all reconstruction schemes must inherit\n */\ntemplate <size_t Dim>\nclass Reconstructor : public PUP::able {\n public:\n  Reconstructor() = default;\n  Reconstructor(const Reconstructor&) = default;\n  Reconstructor& operator=(const Reconstructor&) = default;\n  Reconstructor(Reconstructor&&) = default;\n  Reconstructor& operator=(Reconstructor&&) = default;\n  ~Reconstructor() override = default;\n\n  void pup(PUP::er& p) override;\n\n  /// \\cond\n  explicit Reconstructor(CkMigrateMessage* msg);\n  WRAPPED_PUPable_abstract(Reconstructor<Dim>);  // NOLINT\n  /// \\endcond\n\n  using creatable_classes = tmpl::list<AoWeno53<Dim>, MonotonisedCentral<Dim>>;\n\n  virtual std::unique_ptr<Reconstructor<Dim>> get_clone() const = 0;\n\n  virtual size_t ghost_zone_size() const = 0;\n};\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/RegisterDerivedWithCharm.hpp\"\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace ScalarAdvection::fd {\nvoid register_derived_with_charm() {\n  register_derived_classes_with_charm<Reconstructor<1>>();\n  register_derived_classes_with_charm<Reconstructor<2>>();\n}\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ScalarAdvection::fd {\nvoid register_derived_with_charm();\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellSolver.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::fd {\nnamespace OptionTags {\n/*!\n * \\brief Holds the subcell reconstructor in the input file\n */\ntemplate <size_t Dim>\nstruct Reconstructor {\n  using type = std::unique_ptr<fd::Reconstructor<Dim>>;\n\n  static constexpr Options::String help = {\"The reconstruction scheme to use.\"};\n  using group = evolution::dg::subcell::OptionTags::SubcellSolverGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief Tag for the reconstructor\n */\ntemplate <size_t Dim>\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<fd::Reconstructor<Dim>>;\n  using option_tags = tmpl::list<OptionTags::Reconstructor<Dim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& reconstructor) {\n    return reconstructor->get_clone();\n  }\n};\n}  // namespace Tags\n}  // namespace ScalarAdvection::fd\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/Fluxes.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarAdvection {\ntemplate <size_t Dim>\nvoid Fluxes<Dim>::apply(const gsl::not_null<tnsr::I<DataVector, Dim>*> u_flux,\n                        const Scalar<DataVector>& u,\n                        const tnsr::I<DataVector, Dim>& velocity_field) {\n  for (size_t i = 0; i < Dim; ++i) {\n    u_flux->get(i) = velocity_field.get(i) * get(u);\n  }\n}\n\n}  // namespace ScalarAdvection\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) template class ScalarAdvection::Fluxes<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Fluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection {\n\n/*!\n * \\brief Compute the fluxes of the ScalarAdvection system \\f$F^i = v^iU\\f$\n * where \\f$v^i\\f$ is the velocity field.\n */\ntemplate <size_t Dim>\nstruct Fluxes {\n  using argument_tags = tmpl::list<Tags::U, Tags::VelocityField<Dim>>;\n  using return_tags =\n      tmpl::list<::Tags::Flux<Tags::U, tmpl::size_t<Dim>, Frame::Inertial>>;\n  static void apply(gsl::not_null<tnsr::I<DataVector, Dim>*> u_flux,\n                    const Scalar<DataVector>& u,\n                    const tnsr::I<DataVector, Dim>& velocity_field);\n};\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class Events::ObserveTimeStep<ScalarAdvection::System<DIM(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                               \\\n  template class ChangeTimeStepperOrder<ScalarAdvection::System<DIM(data)>>; \\\n  template class CleanHistory<ScalarAdvection::System<DIM(data)>>;           \\\n  template class RecordTimeStepperData<ScalarAdvection::System<DIM(data)>>;  \\\n  template class UpdateU<ScalarAdvection::System<DIM(data)>, false>;         \\\n  template class UpdateU<ScalarAdvection::System<DIM(data)>, true>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/TimeDerivativeTerms.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace evolution::dg::Actions::detail {\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                \\\n  template void                                                               \\\n  volume_terms<::ScalarAdvection::TimeDerivativeTerms<DIM(data)>>(            \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::dt, typename ::ScalarAdvection::System<DIM(                 \\\n                          data)>::variables_tag::tags_list>>*>                \\\n          dt_vars_ptr,                                                        \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::Flux,                                                       \\\n          typename ::ScalarAdvection::System<DIM(data)>::flux_variables,      \\\n          tmpl::size_t<DIM(data)>, Frame::Inertial>>*>                        \\\n          volume_fluxes,                                                      \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::deriv,                                                      \\\n          typename ::ScalarAdvection::System<DIM(data)>::gradient_variables,  \\\n          tmpl::size_t<DIM(data)>, Frame::Inertial>>*>                        \\\n          partial_derivs,                                                     \\\n      const gsl::not_null<Variables<typename ::ScalarAdvection::System<DIM(   \\\n          data)>::compute_volume_time_derivative_terms::temporary_tags>*>     \\\n          temporaries,                                                        \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::div,                                                        \\\n          db::wrap_tags_in<                                                   \\\n              ::Tags::Flux,                                                   \\\n              typename ::ScalarAdvection::System<DIM(data)>::flux_variables,  \\\n              tmpl::size_t<DIM(data)>, Frame::Inertial>>>*>                   \\\n          div_fluxes,                                                         \\\n      const Variables<typename ::ScalarAdvection::System<DIM(                 \\\n          data)>::variables_tag::tags_list>& evolved_vars,                    \\\n      const ::dg::Formulation dg_formulation, const Mesh<DIM(data)>& mesh,    \\\n      [[maybe_unused]] const tnsr::I<DataVector, DIM(data), Frame::Inertial>& \\\n          inertial_coordinates,                                               \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,     \\\n                            Frame::Inertial>&                                 \\\n          logical_to_inertial_inverse_jacobian,                               \\\n      [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,  \\\n      const std::optional<tnsr::I<DataVector, DIM(data), Frame::Inertial>>&   \\\n          mesh_velocity,                                                      \\\n      const std::optional<Scalar<DataVector>>& div_mesh_velocity,             \\\n      const Scalar<DataVector>& u,                                            \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& velocity_field);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace evolution::dg::Actions::detail\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GhostData.cpp\n  NeighborPackagedData.cpp\n  SetInitialRdmpData.cpp\n  TciOnDgGrid.cpp\n  TciOnFdGrid.cpp\n  TciOptions.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComputeFluxes.hpp\n  GhostData.hpp\n  NeighborPackagedData.hpp\n  SetInitialRdmpData.hpp\n  Subcell.hpp\n  TciOnDgGrid.hpp\n  TciOnFdGrid.hpp\n  TciOptions.hpp\n  TimeDerivative.hpp\n  VelocityAtFace.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/ComputeFluxes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Fluxes.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::subcell {\nnamespace detail {\n/*!\n * \\brief Helper function that calls `ScalarAdvection::Fluxes::apply` by\n * retrieving the return and argument tags from the Variables object `vars`.\n */\ntemplate <size_t Dim, typename TagsList, typename... ReturnTags,\n          typename... ArgumentTags>\nvoid compute_fluxes_impl(const gsl::not_null<Variables<TagsList>*> vars,\n                         tmpl::list<ReturnTags...> /*meta*/,\n                         tmpl::list<ArgumentTags...> /*meta*/) {\n  Fluxes<Dim>::apply(make_not_null(&get<ReturnTags>(*vars))...,\n                     get<ArgumentTags>(*vars)...);\n}\n}  // namespace detail\n\n/*!\n * \\brief Compute fluxes for subcell variables.\n */\ntemplate <size_t Dim, typename TagsList>\nvoid compute_fluxes(const gsl::not_null<Variables<TagsList>*> vars) {\n  detail::compute_fluxes_impl<Dim>(vars, typename Fluxes<Dim>::return_tags{},\n                                   typename Fluxes<Dim>::argument_tags{});\n}\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/Subcell/GhostData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::subcell {\nDataVector GhostVariables::apply(\n    const Variables<tmpl::list<ScalarAdvection::Tags::U>>& vars,\n    const size_t rdmp_size) {\n  DataVector buffer{vars.number_of_grid_points() + rdmp_size};\n  DataVector var_view{buffer.data(), vars.number_of_grid_points()};\n  var_view = get(get<ScalarAdvection::Tags::U>(vars));\n  return buffer;\n}\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/GhostData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename T>\nclass Variables;\nnamespace Tags {\ntemplate <typename TagsList>\nstruct Variables;\n}  // namespace Tags\n/// \\endcond\n\nnamespace ScalarAdvection::subcell {\n/*!\n * \\brief Returns \\f$U\\f$, the variables needed for reconstruction.\n *\n * This mutator is passed to\n * `evolution::dg::subcell::Actions::SendDataForReconstruction`.\n */\nclass GhostVariables {\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::Variables<tmpl::list<ScalarAdvection::Tags::U>>>;\n\n  static DataVector apply(\n      const Variables<tmpl::list<ScalarAdvection::Tags::U>>& vars,\n      size_t rdmp_size);\n};\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/Subcell/NeighborPackagedData.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.tpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Fluxes.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/VelocityAtFace.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::subcell {\ntemplate <size_t Dim>\nDirectionalIdMap<Dim, DataVector> NeighborPackagedData::apply(\n    const db::Access& box,\n    const std::vector<DirectionalId<Dim>>& mortars_to_reconstruct_to) {\n  // The object to return\n  DirectionalIdMap<Dim, DataVector> neighbor_package_data{};\n  if (mortars_to_reconstruct_to.empty()) {\n    return neighbor_package_data;\n  }\n\n  using evolved_vars_tags = typename System<Dim>::variables_tag::tags_list;\n  using fluxes_tags = typename Fluxes<Dim>::return_tags;\n\n  // subcell currently does not support moving mesh\n  ASSERT(not db::get<domain::Tags::MeshVelocity<Dim>>(box).has_value(),\n         \"Haven't yet added support for moving mesh to DG-subcell. This \"\n         \"should be easy to generalize, but we will want to consider \"\n         \"storing the mesh velocity on the faces instead of \"\n         \"re-slicing/projecting.\");\n\n  // Project volume variables from DG to subcell mesh\n  const Mesh<Dim>& dg_mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n  const Mesh<Dim>& subcell_mesh =\n      db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(box);\n  const auto& subcell_options =\n      db::get<evolution::dg::subcell::Tags::SubcellOptions<Dim>>(box);\n  const auto volume_vars_subcell = evolution::dg::subcell::fd::project(\n      db::get<typename System<Dim>::variables_tag>(box), dg_mesh,\n      subcell_mesh.extents());\n\n  const auto& ghost_subcell_data =\n      db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(\n          box);\n\n  const ScalarAdvection::fd::Reconstructor<Dim>& recons =\n      db::get<ScalarAdvection::fd::Tags::Reconstructor<Dim>>(box);\n\n  const auto& boundary_correction =\n      db::get<evolution::Tags::BoundaryCorrection>(box);\n  using derived_boundary_corrections =\n      ScalarAdvection::BoundaryCorrections::standard_boundary_corrections<Dim>;\n\n  // perform reconstruction\n  tmpl::for_each<derived_boundary_corrections>([&](auto derived_correction_v) {\n    using derived_correction = tmpl::type_from<decltype(derived_correction_v)>;\n    if (typeid(boundary_correction) == typeid(derived_correction)) {\n      using dg_package_field_tags =\n          typename derived_correction::dg_package_field_tags;\n      using dg_package_data_temporary_tags =\n          typename derived_correction::dg_package_data_temporary_tags;\n      using dg_package_data_argument_tags =\n          tmpl::append<evolved_vars_tags, fluxes_tags,\n                       dg_package_data_temporary_tags>;\n      const auto& element = db::get<domain::Tags::Element<Dim>>(box);\n\n      // Variables to store packaged data\n      Variables<dg_package_field_tags> packaged_data{};\n      // Variables to be reconstructed on the shared interfaces\n      Variables<dg_package_data_argument_tags> vars_on_face{};\n\n      for (const auto& mortar_id : mortars_to_reconstruct_to) {\n        const Direction<Dim>& direction = mortar_id.direction();\n        Index<Dim> extents = subcell_mesh.extents();\n\n        // Switch to face-centered instead of cell-centered points on the\n        // FD. There are num_cell_centered+1 face-centered points.\n        ++extents[direction.dimension()];\n        const size_t num_face_pts =\n            subcell_mesh.extents().slice_away(direction.dimension()).product();\n        vars_on_face.initialize(num_face_pts);\n\n        using velocity_field = ScalarAdvection::Tags::VelocityField<Dim>;\n        const auto& velocity_on_face = db::get<\n            evolution::dg::subcell::Tags::OnSubcellFaces<velocity_field, Dim>>(\n            box);\n        data_on_slice(make_not_null(&get<velocity_field>(vars_on_face)),\n                      gsl::at(velocity_on_face, direction.dimension()), extents,\n                      direction.dimension(),\n                      direction.side() == Side::Lower\n                          ? 0\n                          : extents[direction.dimension()] - 1);\n\n        // Reconstruct field variables on faces\n        call_with_dynamic_type<void, typename ScalarAdvection::fd::\n                                         Reconstructor<Dim>::creatable_classes>(\n            &recons,\n            [&element, &mortar_id, &ghost_subcell_data, &subcell_mesh,\n             &vars_on_face, &volume_vars_subcell](const auto& reconstructor) {\n              reconstructor->reconstruct_fd_neighbor(\n                  make_not_null(&vars_on_face), volume_vars_subcell, element,\n                  ghost_subcell_data, subcell_mesh, mortar_id.direction());\n            });\n\n        // Compute fluxes\n        ScalarAdvection::subcell::compute_fluxes<Dim>(\n            make_not_null(&vars_on_face));\n\n        tnsr::i<DataVector, Dim, Frame::Inertial> normal_covector =\n            get<evolution::dg::Tags::NormalCovector<Dim>>(\n                *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(\n                     box)\n                     .at(mortar_id.direction()));\n        for (auto& t : normal_covector) {\n          t *= -1.0;\n        }\n\n        // Note: need to do the projection in 2D\n        if constexpr (Dim > 1) {\n          const auto dg_normal_covector = normal_covector;\n          for (size_t i = 0; i < Dim; ++i) {\n            normal_covector.get(i) = evolution::dg::subcell::fd::project(\n                dg_normal_covector.get(i),\n                dg_mesh.slice_away(mortar_id.direction().dimension()),\n                subcell_mesh.extents().slice_away(\n                    mortar_id.direction().dimension()));\n          }\n        }\n\n        // Compute the packaged data\n        packaged_data.initialize(num_face_pts);\n        evolution::dg::Actions::detail::dg_package_data<System<Dim>>(\n            make_not_null(&packaged_data),\n            dynamic_cast<const derived_correction&>(boundary_correction),\n            vars_on_face, normal_covector, {std::nullopt}, box,\n            typename derived_correction::dg_package_data_volume_tags{},\n            dg_package_data_argument_tags{});\n\n        // Make a view so we can use iterators with std::copy\n        DataVector packaged_data_view{packaged_data.data(),\n                                      packaged_data.size()};\n        neighbor_package_data[mortar_id] = DataVector{packaged_data.size()};\n        std::copy(packaged_data_view.begin(), packaged_data_view.end(),\n                  neighbor_package_data[mortar_id].begin());\n\n        // Note : need to reconstruct from FD to DG grid in 2D\n        if constexpr (Dim > 1) {\n          const auto dg_packaged_data = evolution::dg::subcell::fd::reconstruct(\n              packaged_data,\n              dg_mesh.slice_away(mortar_id.direction().dimension()),\n              subcell_mesh.extents().slice_away(\n                  mortar_id.direction().dimension()),\n              subcell_options.reconstruction_method());\n          // Make a view so we can use iterators with std::copy\n          DataVector dg_packaged_data_view{\n              // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n              const_cast<double*>(dg_packaged_data.data()),\n              dg_packaged_data.size()};\n          neighbor_package_data[mortar_id] =\n              DataVector{dg_packaged_data.size()};\n          std::copy(dg_packaged_data_view.begin(), dg_packaged_data_view.end(),\n                    neighbor_package_data[mortar_id].begin());\n        }\n      }\n    }\n  });\n\n  return neighbor_package_data;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                     \\\n  template DirectionalIdMap<DIM(data), DataVector> \\\n  NeighborPackagedData::apply<DIM(data)>(          \\\n      const db::Access& box,                       \\\n      const std::vector<DirectionalId<DIM(data)>>& mortars_to_reconstruct_to);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n\n}  // namespace ScalarAdvection::subcell\n\n#define INSTANTIATION(r, data)                                                \\\n  template void evolution::dg::subcell::neighbor_reconstructed_face_solution< \\\n      DIM(data), ScalarAdvection::subcell::NeighborPackagedData>(             \\\n      gsl::not_null<db::Access*> box);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/NeighborPackagedData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n/// \\cond\nnamespace db {\nclass Access;\n}  // namespace db\nclass DataVector;\ntemplate <size_t VolumeDim>\nstruct DirectionalId;\ntemplate <size_t Dim, typename T>\nclass DirectionalIdMap;\n/// \\endcond\n\nnamespace ScalarAdvection::subcell {\n/*!\n * \\brief On elements using DG, reconstructs the interface data from a\n * neighboring element doing subcell.\n *\n * The neighbor's packaged data needed by the boundary correction is computed\n * and returned so that it can be used for solving the Riemann problem on the\n * interfaces.\n *\n * Note that for strict conservation the Riemann solve should be done on the\n * subcells, with the correction being projected back to the DG interface.\n * However, in practice such strict conservation doesn't seem to be necessary\n * and can be explained by that we only need strict conservation at shocks, and\n * if one element is doing DG, then we aren't at a shock.\n */\n\nstruct NeighborPackagedData {\n  template <size_t Dim>\n  static DirectionalIdMap<Dim, DataVector> apply(\n      const db::Access& box,\n      const std::vector<DirectionalId<Dim>>& mortars_to_reconstruct_to);\n};\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/Subcell/SetInitialRdmpData.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/TwoMeshRdmpTci.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarAdvection::subcell {\ntemplate <size_t Dim>\nvoid SetInitialRdmpData<Dim>::apply(\n    const gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n    const Scalar<DataVector>& u,\n    const evolution::dg::subcell::ActiveGrid active_grid,\n    const Mesh<Dim>& dg_mesh, const Mesh<Dim>& subcell_mesh) {\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Subcell) {\n    *rdmp_tci_data = {{max(get(u))}, {min(get(u))}};\n  } else {\n    using std::max;\n    using std::min;\n    const auto subcell_u = evolution::dg::subcell::fd::project(\n        get(u), dg_mesh, subcell_mesh.extents());\n\n    *rdmp_tci_data = {{max(max(get(u)), max(subcell_u))},\n                      {min(min(get(u)), min(subcell_u))}};\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template struct SetInitialRdmpData<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n\n#undef DIM\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/SetInitialRdmpData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace ScalarAdvection::subcell {\n/// \\brief Sets the initial RDMP data.\n///\n/// Used on the subcells after the TCI marked the DG solution as inadmissible.\ntemplate <size_t Dim>\nstruct SetInitialRdmpData {\n  using argument_tags = tmpl::list<\n      ScalarAdvection::Tags::U, evolution::dg::subcell::Tags::ActiveGrid,\n      ::domain::Tags::Mesh<Dim>, evolution::dg::subcell::Tags::Mesh<Dim>>;\n  using return_tags = tmpl::list<evolution::dg::subcell::Tags::DataForRdmpTci>;\n\n  static void apply(\n      gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n      const Scalar<DataVector>& u,\n      evolution::dg::subcell::ActiveGrid active_grid, const Mesh<Dim>& dg_mesh,\n      const Mesh<Dim>& subcell_mesh);\n};\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/Subcell.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ScalarAdvection {\n/*!\n * \\brief Code required by the DG-subcell/FD hybrid solver for ScalarAdvection\n * system\n */\nnamespace subcell {}\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOnDgGrid.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarAdvection::subcell {\ntemplate <size_t Dim>\nstd::tuple<bool, evolution::dg::subcell::RdmpTciData> TciOnDgGrid<Dim>::apply(\n    const Scalar<DataVector>& dg_u, const Mesh<Dim>& dg_mesh,\n    const Mesh<Dim>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const TciOptions& tci_options, const double persson_exponent,\n    [[maybe_unused]] const bool element_stays_on_dg) {\n  // Don't use buffer since we have only one memory allocation right now (until\n  // persson_tci can use a buffer)\n  const Scalar<DataVector> subcell_u{::evolution::dg::subcell::fd::project(\n      get(dg_u), dg_mesh, subcell_mesh.extents())};\n\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{\n      {max(max(get(dg_u)), max(get(subcell_u)))},\n      {min(min(get(dg_u)), min(get(subcell_u)))}};\n\n  const double max_abs_u = max(abs(get(dg_u)));\n\n  const bool cell_is_troubled =\n      evolution::dg::subcell::rdmp_tci(rdmp_tci_data.max_variables_values,\n                                       rdmp_tci_data.min_variables_values,\n                                       past_rdmp_tci_data.max_variables_values,\n                                       past_rdmp_tci_data.min_variables_values,\n                                       subcell_options.rdmp_delta0(),\n                                       subcell_options.rdmp_epsilon()) or\n      ((max_abs_u > tci_options.u_cutoff) and\n       ::evolution::dg::subcell::persson_tci(\n           dg_u, dg_mesh, persson_exponent,\n           subcell_options.persson_num_highest_modes()));\n\n  return {cell_is_troubled, std::move(rdmp_tci_data)};\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template struct TciOnDgGrid<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n\n#undef DIM\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/TciOnDgGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace ScalarAdvection::subcell {\n/*!\n * \\brief The troubled-cell indicator run on the DG grid to check if the\n * solution is admissible.\n *\n * Applies 1) the RDMP TCI to \\f$U\\f$ and 2) the Persson TCI to \\f$U\\f$ if the\n * \\f$\\max(|U|)\\f$ on the DG grid is greater than `tci_options.u_cutoff`.\n */\ntemplate <size_t Dim>\nstruct TciOnDgGrid {\n public:\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<ScalarAdvection::Tags::U, ::domain::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::DataForRdmpTci,\n                 evolution::dg::subcell::Tags::SubcellOptions<Dim>,\n                 Tags::TciOptions>;\n\n  static std::tuple<bool, evolution::dg::subcell::RdmpTciData> apply(\n      const Scalar<DataVector>& dg_u, const Mesh<Dim>& dg_mesh,\n      const Mesh<Dim>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      const TciOptions& tci_options, double persson_exponent,\n      bool element_stays_on_dg);\n};\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOnFdGrid.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarAdvection::subcell {\ntemplate <size_t Dim>\nstd::tuple<bool, evolution::dg::subcell::RdmpTciData> TciOnFdGrid<Dim>::apply(\n    const Scalar<DataVector>& subcell_u, const Mesh<Dim>& dg_mesh,\n    const Mesh<Dim>& subcell_mesh,\n    const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n    const evolution::dg::subcell::SubcellOptions& subcell_options,\n    const TciOptions& tci_options, const double persson_exponent,\n    const bool need_rdmp_data_only) {\n  const Scalar<DataVector> dg_u{evolution::dg::subcell::fd::reconstruct(\n      get(subcell_u), dg_mesh, subcell_mesh.extents(),\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)};\n\n  using std::max;\n  using std::min;\n  const evolution::dg::subcell::RdmpTciData rdmp_data_for_tci{\n      {max(max(get(dg_u)), max(get(subcell_u)))},\n      {min(min(get(dg_u)), min(get(subcell_u)))}};\n\n  if (need_rdmp_data_only) {\n    return {false, rdmp_data_for_tci};\n  }\n\n  const double max_abs_u = max(abs(get(dg_u)));\n\n  const auto cell_is_troubled =\n      static_cast<bool>(evolution::dg::subcell::rdmp_tci(\n          rdmp_data_for_tci.max_variables_values,\n          rdmp_data_for_tci.min_variables_values,\n          past_rdmp_tci_data.max_variables_values,\n          past_rdmp_tci_data.min_variables_values,\n          subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon()));\n\n  return {\n      cell_is_troubled or ((max_abs_u > tci_options.u_cutoff) and\n                           ::evolution::dg::subcell::persson_tci(\n                               dg_u, dg_mesh, persson_exponent,\n                               subcell_options.persson_num_highest_modes())),\n      {{max(get(subcell_u))}, {min(get(subcell_u))}}};\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template struct TciOnFdGrid<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2))\n\n#undef INSTANTIATION\n\n#undef DIM\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/TciOnFdGrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace ScalarAdvection::subcell {\n/*!\n * \\brief Troubled-cell indicator applied to the finite difference subcell\n * solution to check if the corresponding DG solution is admissible.\n *\n * Applies 1) the RDMP TCI to \\f$U\\f$ and 2) the Persson TCI to \\f$U\\f$ if the\n * \\f$\\max(|U|)\\f$ on the DG grid is greater than `tci_options.u_cutoff`.\n */\ntemplate <size_t Dim>\nstruct TciOnFdGrid {\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<ScalarAdvection::Tags::U, ::domain::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::DataForRdmpTci,\n                 evolution::dg::subcell::Tags::SubcellOptions<Dim>,\n                 Tags::TciOptions>;\n\n  static std::tuple<bool, evolution::dg::subcell::RdmpTciData> apply(\n      const Scalar<DataVector>& subcell_u, const Mesh<Dim>& dg_mesh,\n      const Mesh<Dim>& subcell_mesh,\n      const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n      const evolution::dg::subcell::SubcellOptions& subcell_options,\n      const TciOptions& tci_options, double persson_exponent,\n      bool need_rdmp_data_only);\n};\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/TciOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n\n#include <pup.h>\n\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace ScalarAdvection::subcell {\nvoid TciOptions::pup(PUP::er& p) { p | u_cutoff; }\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/OptionsGroup.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarAdvection::subcell {\nstruct TciOptions {\n  /*!\n   * \\brief The cutoff of the absolute value of the scalar field \\f$U\\f$ in an\n   * element to use the Persson TCI. Below this value the Persson TCI is not\n   * applied.\n   */\n  struct UCutoff {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The cutoff of the absolute value of the scalar field U in an \"\n        \"element to use Persson TCI.\"};\n  };\n\n  using options = tmpl::list<UCutoff>;\n  static constexpr Options::String help = {\n      \"Options for the troubled-cell indicator\"};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/);\n\n  double u_cutoff{std::numeric_limits<double>::signaling_NaN()};\n};\n\nnamespace OptionTags {\nstruct TciOptions {\n  using type = subcell::TciOptions;\n  static constexpr Options::String help =\n      \"ScalarAdvection-specific options for the TCI\";\n  using group = ::dg::OptionTags::DiscontinuousGalerkinGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\nstruct TciOptions : db::SimpleTag {\n  using type = subcell::TciOptions;\n  using option_tags = tmpl::list<typename OptionTags::TciOptions>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& tci_options) {\n    return tci_options;\n  }\n};\n}  // namespace Tags\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/ComputeBoundaryTerms.hpp\"\n#include \"Evolution/DgSubcell/CorrectPackagedData.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Fluxes.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace ScalarAdvection::subcell {\n/*!\n * \\brief Compute the time derivative on the subcell grid using FD\n * reconstruction.\n *\n * The code makes the following unchecked assumptions:\n * - Assumes Cartesian coordinates with a diagonal Jacobian matrix\n * from the logical to the inertial frame\n * - Assumes the mesh is not moving (grid and inertial frame are the same)\n */\ntemplate <size_t Dim>\nstruct TimeDerivative {\n  template <typename DbTagsList>\n  static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n    using metavariables =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>;\n    ASSERT((db::get<::domain::CoordinateMaps::Tags::CoordinateMap<\n                Dim, Frame::Grid, Frame::Inertial>>(*box))\n               .is_identity(),\n           \"Do not yet support moving mesh with DG-subcell.\");\n    // subcell is currently not supported for external boundary elements\n    const Element<Dim>& element = db::get<domain::Tags::Element<Dim>>(*box);\n    ASSERT(element.external_boundaries().size() == 0,\n           \"Can't have external boundaries right now with subcell. ElementID \"\n               << element.id());\n\n    using evolved_vars_tags = typename System<Dim>::variables_tag::tags_list;\n    using fluxes_tags = typename Fluxes<Dim>::return_tags;\n\n    // The copy of Mesh is intentional to avoid a GCC-7 internal compiler error.\n    const Mesh<Dim> subcell_mesh =\n        db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(*box);\n    ASSERT(\n        subcell_mesh == Mesh<Dim>(subcell_mesh.extents(0),\n                                  subcell_mesh.basis(0),\n                                  subcell_mesh.quadrature(0)),\n        \"The subcell/FD mesh must be isotropic for the FD time derivative but \"\n        \"got \"\n            << subcell_mesh);\n\n    const size_t num_reconstructed_pts =\n        (subcell_mesh.extents(0) + 1) *\n        subcell_mesh.extents().slice_away(0).product();\n\n    const ScalarAdvection::fd::Reconstructor<Dim>& recons =\n        db::get<ScalarAdvection::fd::Tags::Reconstructor<Dim>>(*box);\n\n    const auto& boundary_correction =\n        db::get<evolution::Tags::BoundaryCorrection>(*box);\n    using derived_boundary_corrections =\n        tmpl::at<typename metavariables::factory_creation::factory_classes,\n                 evolution::BoundaryCorrection>;\n\n    // Variables to store the boundary correction terms on FD subinterfaces\n    std::array<Variables<evolved_vars_tags>, Dim> fd_boundary_corrections{};\n\n    // package the data and compute the boundary correction\n    tmpl::for_each<derived_boundary_corrections>([&boundary_correction,\n                                                  &fd_boundary_corrections,\n                                                  &box, &element,\n                                                  &num_reconstructed_pts,\n                                                  &recons, &subcell_mesh](\n                                                     auto\n                                                         derived_correction_v) {\n      using derived_correction =\n          tmpl::type_from<decltype(derived_correction_v)>;\n      if (typeid(boundary_correction) == typeid(derived_correction)) {\n        using dg_package_field_tags =\n            typename derived_correction::dg_package_field_tags;\n        using dg_package_data_temporary_tags =\n            typename derived_correction::dg_package_data_temporary_tags;\n        using dg_package_data_argument_tags =\n            tmpl::append<evolved_vars_tags, fluxes_tags,\n                         dg_package_data_temporary_tags>;\n\n        // Variables that need to be reconstructed for dg_package_data()\n        auto package_data_argvars_lower_face = make_array<Dim>(\n            Variables<dg_package_data_argument_tags>(num_reconstructed_pts));\n        auto package_data_argvars_upper_face = make_array<Dim>(\n            Variables<dg_package_data_argument_tags>(num_reconstructed_pts));\n\n        // Reconstruct the fields on interfaces\n        call_with_dynamic_type<void, typename ScalarAdvection::fd::\n                                         Reconstructor<Dim>::creatable_classes>(\n            &recons,\n            [&box, &package_data_argvars_lower_face,\n             &package_data_argvars_upper_face](const auto& reconstructor) {\n              db::apply<typename std::decay_t<decltype(\n                  *reconstructor)>::reconstruction_argument_tags>(\n                  [&package_data_argvars_lower_face,\n                   &package_data_argvars_upper_face,\n                   &reconstructor](const auto&... args) {\n                    reconstructor->reconstruct(\n                        make_not_null(&package_data_argvars_lower_face),\n                        make_not_null(&package_data_argvars_upper_face),\n                        args...);\n                  },\n                  *box);\n            });\n\n        // copy over the face values of the velocity field\n        {\n          using tag = ::ScalarAdvection::Tags::VelocityField<Dim>;\n          const auto& velocity_on_face =\n              db::get<evolution::dg::subcell::Tags::OnSubcellFaces<tag, Dim>>(\n                  *box);\n          for (size_t d = 0; d < Dim; ++d) {\n            get<tag>(gsl::at(package_data_argvars_lower_face, d)) =\n                gsl::at(velocity_on_face, d);\n            get<tag>(gsl::at(package_data_argvars_upper_face, d)) =\n                gsl::at(velocity_on_face, d);\n          }\n        }\n\n        // Variables to store packaged data. Allocate outside of loop to\n        // reduce allocations\n        Variables<dg_package_field_tags> upper_packaged_data{\n            num_reconstructed_pts};\n        Variables<dg_package_field_tags> lower_packaged_data{\n            num_reconstructed_pts};\n\n        for (size_t dim = 0; dim < Dim; ++dim) {\n          auto& vars_upper_face = gsl::at(package_data_argvars_upper_face, dim);\n          auto& vars_lower_face = gsl::at(package_data_argvars_lower_face, dim);\n\n          // Compute fluxes on each faces\n          ScalarAdvection::subcell::compute_fluxes<Dim>(\n              make_not_null(&vars_upper_face));\n          ScalarAdvection::subcell::compute_fluxes<Dim>(\n              make_not_null(&vars_lower_face));\n\n          // Note that we use the sign convention on the normal vectors to\n          // be compatible with DG.\n          tnsr::i<DataVector, Dim, Frame::Inertial> lower_outward_conormal{\n              num_reconstructed_pts, 0.0};\n          lower_outward_conormal.get(dim) = 1.0;\n          tnsr::i<DataVector, Dim, Frame::Inertial> upper_outward_conormal{\n              num_reconstructed_pts, 0.0};\n          upper_outward_conormal.get(dim) = -1.0;\n\n          // Compute the packaged data\n          evolution::dg::Actions::detail::dg_package_data<System<Dim>>(\n              make_not_null(&upper_packaged_data),\n              dynamic_cast<const derived_correction&>(boundary_correction),\n              vars_upper_face, upper_outward_conormal, {std::nullopt}, *box,\n              typename derived_correction::dg_package_data_volume_tags{},\n              dg_package_data_argument_tags{});\n          evolution::dg::Actions::detail::dg_package_data<System<Dim>>(\n              make_not_null(&lower_packaged_data),\n              dynamic_cast<const derived_correction&>(boundary_correction),\n              vars_lower_face, lower_outward_conormal, {std::nullopt}, *box,\n              typename derived_correction::dg_package_data_volume_tags{},\n              dg_package_data_argument_tags{});\n\n          // Now need to check if any of our neighbors are doing DG, because\n          // if so then we need to use whatever boundary data they sent\n          // instead of what we computed locally.\n          //\n          // Note: We could check this beforehand to avoid the extra work of\n          // reconstruction and flux computations at the boundaries.\n          evolution::dg::subcell::correct_package_data<true>(\n              make_not_null(&lower_packaged_data),\n              make_not_null(&upper_packaged_data), dim, element, subcell_mesh,\n              db::get<evolution::dg::Tags::MortarData<Dim>>(*box), 0);\n\n          // Compute the corrections on the faces. We only need to compute this\n          // once because we can just flip the normal vectors then\n          gsl::at(fd_boundary_corrections, dim)\n              .initialize(num_reconstructed_pts);\n          evolution::dg::subcell::compute_boundary_terms(\n              make_not_null(&gsl::at(fd_boundary_corrections, dim)),\n              dynamic_cast<const derived_correction&>(boundary_correction),\n              upper_packaged_data, lower_packaged_data, db::as_access(*box),\n              typename derived_correction::dg_boundary_terms_volume_tags{});\n        }\n      }\n    });\n\n    std::array<double, Dim> one_over_delta_xi{};\n    {\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n          cell_centered_logical_coords =\n              db::get<evolution::dg::subcell::Tags::Coordinates<\n                  Dim, Frame::ElementLogical>>(*box);\n\n      for (size_t i = 0; i < Dim; ++i) {\n        // Note: assumes isotropic extents\n        gsl::at(one_over_delta_xi, i) =\n            1.0 / (get<0>(cell_centered_logical_coords)[1] -\n                   get<0>(cell_centered_logical_coords)[0]);\n      }\n    }\n\n    // Now compute the actual time derivative\n    using dt_variables_tag =\n        db::add_tag_prefix<::Tags::dt, typename System<Dim>::variables_tag>;\n    const size_t num_pts = subcell_mesh.number_of_grid_points();\n    db::mutate<dt_variables_tag>(\n        [&num_pts, &fd_boundary_corrections, &subcell_mesh, &one_over_delta_xi](\n            const auto dt_vars_ptr,\n            const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Grid>&\n                cell_centered_logical_to_grid_inv_jacobian) {\n          dt_vars_ptr->initialize(num_pts, 0.0);\n          auto& dt_u =\n              get<::Tags::dt<::ScalarAdvection::Tags::U>>(*dt_vars_ptr);\n\n          for (size_t dim = 0; dim < Dim; ++dim) {\n            Scalar<DataVector>& u_correction = get<::ScalarAdvection ::Tags::U>(\n                gsl::at(fd_boundary_corrections, dim));\n            evolution::dg::subcell::add_cartesian_flux_divergence(\n                make_not_null(&get(dt_u)), gsl::at(one_over_delta_xi, dim),\n                cell_centered_logical_to_grid_inv_jacobian.get(dim, dim),\n                get(u_correction), subcell_mesh.extents(), dim);\n          }\n        },\n        box,\n        db::get<evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGrid<\n            Dim>>(*box));\n  }\n};\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Subcell/VelocityAtFace.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/VelocityField.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\ntemplate <size_t VolumeDim>\nstruct Mesh;\n}  // namespace Tags\n}  // namespace domain\n/// \\endcond\n\nnamespace ScalarAdvection::subcell {\n/*!\n * \\brief Allocate and set the velocity field needed for evolving\n * ScalarAdvection system when using a DG-subcell hybrid scheme.\n *\n * Uses:\n * - DataBox:\n *   * `Tags::Time`\n *   * `evolution::dg::subcell::Tags::Mesh<Dim>`\n *   * `domain::Tags::ElementMap<Dim, Frame::Grid>`\n *   * `Tags::CoordinateMap<Dim, Frame::Grid, Frame::Inertial>`\n *   * `domain::Tags::FunctionsOfTime`\n *   * `evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>`\n *\n * DataBox changes:\n * - Adds:\n *   * `evolution::dg::subcell::Tags::Inactive<velocity_field>`\n *   * `evolution::dg::subcell::Tags::OnSubcellFaces<velocity_field, Dim>`\n * where `velocity_field` is `ScalarAdvection::Tags::VelocityField<Dim>`.\n *\n * - Removes: nothing\n * - Modifies: nothing\n *\n * \\note This mutator is meant to be used with\n * `Initialization::Actions::AddSimpleTags`.\n */\ntemplate <size_t Dim>\nstruct VelocityAtFace : tt::ConformsTo<db::protocols::Mutator> {\n  using simple_tags_from_options = tmpl::list<::Tags::Time>;\n\n  using velocity_field = ::ScalarAdvection::Tags::VelocityField<Dim>;\n  using subcell_velocity_field =\n      ::evolution::dg::subcell::Tags::Inactive<velocity_field>;\n  using subcell_faces_velocity_field =\n      ::evolution::dg::subcell::Tags::OnSubcellFaces<velocity_field, Dim>;\n\n  using vars = typename velocity_field::type;\n  using subcell_vars = typename subcell_velocity_field::type;\n  using face_vars = typename subcell_faces_velocity_field::type::value_type;\n\n  using return_tags =\n      tmpl::list<subcell_velocity_field, subcell_faces_velocity_field>;\n\n  using argument_tags = tmpl::list<\n      ::Tags::Time,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      domain::Tags::ElementMap<Dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                  Frame::Inertial>,\n      domain::Tags::FunctionsOfTime,\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>>;\n\n  static void apply(\n      const gsl::not_null<subcell_vars*> cell_centered_vars,\n      const gsl::not_null<std::array<face_vars, Dim>*> face_centered_vars,\n      const double initial_time, const Mesh<Dim>& subcell_mesh,\n      const ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n      const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&\n          grid_to_inertial_map,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n          subcell_logical_coordinates) {\n    // check if the subcell mesh is isotropic\n    ASSERT(Mesh<Dim>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                     subcell_mesh.quadrature(0)) == subcell_mesh,\n           \"The subcell mesh must have isotropic basis, quadrature. and \"\n           \"extents but got \"\n               << subcell_mesh);\n\n    const size_t num_grid_points = subcell_mesh.number_of_grid_points();\n    const auto cell_centered_inertial_coords =\n        grid_to_inertial_map(logical_to_grid_map(subcell_logical_coordinates),\n                             initial_time, functions_of_time);\n\n    // Set cell-centered vars. Need to first do without prefix then move into\n    // prefixed Variables.\n    vars no_prefix_cell_centered_vars{num_grid_points};\n    // Note: This assumes the velocity field is the hard-coded one in the\n    // compute tag\n    ::ScalarAdvection::Tags::VelocityFieldCompute<Dim>::function(\n        make_not_null(&no_prefix_cell_centered_vars),\n        cell_centered_inertial_coords);\n    *cell_centered_vars = std::move(no_prefix_cell_centered_vars);\n\n    // Set face-centered vars.\n    for (size_t dim = 0; dim < Dim; ++dim) {\n      const auto basis = make_array<Dim>(subcell_mesh.basis(0));\n      auto quadrature = make_array<Dim>(subcell_mesh.quadrature(0));\n      auto extents = make_array<Dim>(subcell_mesh.extents(0));\n      gsl::at(extents, dim) = subcell_mesh.extents(0) + 1;\n      gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n      const Mesh<Dim> face_centered_mesh{extents, basis, quadrature};\n      const auto face_centered_logical_coords =\n          logical_coordinates(face_centered_mesh);\n      const auto face_centered_inertial_coords = grid_to_inertial_map(\n          logical_to_grid_map(face_centered_logical_coords), initial_time,\n          functions_of_time);\n\n      ::ScalarAdvection::Tags::VelocityFieldCompute<Dim>::function(\n          make_not_null(&(gsl::at(*face_centered_vars, dim))),\n          face_centered_inertial_coords);\n    }\n  }\n};\n}  // namespace ScalarAdvection::subcell\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Characteristics.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/TimeDerivativeTerms.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup EvolutionSystemsGroup\n * \\brief Items related to evolving the scalar advection equation.\n *\n * \\f{align*}\n * \\partial_t U + \\nabla \\cdot (v U) = 0\n * \\f}\n *\n * Since the ScalarAdvection system is only used for testing limiters in the\n * current implementation, the velocity field \\f$v\\f$ is fixed throughout time.\n */\nnamespace ScalarAdvection {\ntemplate <size_t Dim>\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = true;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = Dim;\n\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition<Dim>;\n\n  using variables_tag = ::Tags::Variables<tmpl::list<Tags::U>>;\n  using flux_variables = tmpl::list<Tags::U>;\n  using gradient_variables = tmpl::list<>;\n\n  using compute_volume_time_derivative_terms = TimeDerivativeTerms<Dim>;\n\n  using compute_largest_characteristic_speed =\n      Tags::LargestCharacteristicSpeedCompute<Dim>;\n};\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\nnamespace ScalarAdvection {\n/*!\n * \\brief Tags for the ScalarAdvection system\n */\nnamespace Tags {\n/// The scalar field to evolve\nstruct U : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The advection velocity field\ntemplate <size_t Dim>\nstruct VelocityField : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n\n/// The largest characteristic speed\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\n}  // namespace Tags\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/TimeDerivativeTerms.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Fluxes.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarAdvection {\n\ntemplate <size_t Dim>\nevolution::dg::TimeDerivativeDecisions<Dim> TimeDerivativeTerms<Dim>::apply(\n    const gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_vars*/,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> flux,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> temp_velocity_field,\n    const Scalar<DataVector>& u,\n    const tnsr::I<DataVector, Dim>& velocity_field) {\n  *temp_velocity_field = velocity_field;\n  Fluxes<Dim>::apply(flux, u, velocity_field);\n  return {true};\n}\n\n}  // namespace ScalarAdvection\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) \\\n  template class ScalarAdvection::TimeDerivativeTerms<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/TimeDerivativeTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection {\n/*!\n * \\brief Computes the time derivative terms needed for the ScalarAdvection\n * system, which are just the fluxes.\n */\ntemplate <size_t Dim>\nstruct TimeDerivativeTerms {\n  using temporary_tags = tmpl::list<Tags::VelocityField<Dim>>;\n  using argument_tags = tmpl::list<Tags::U, Tags::VelocityField<Dim>>;\n\n  static evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      // Time derivatives returned by reference. No source terms or\n      // nonconservative products, so not used. All the tags in the\n      // variables_tag in the system struct.\n      gsl::not_null<Scalar<DataVector>*> /*non_flux_terms_dt_vars*/,\n\n      // Fluxes returned by reference. Listed in the system struct as\n      // flux_variables.\n      gsl::not_null<tnsr::I<DataVector, Dim>*> flux,\n\n      // Temporary tags\n      gsl::not_null<tnsr::I<DataVector, Dim>*> temp_velocity_field,\n\n      // Arguments listed in argument_tags above\n      const Scalar<DataVector>& u,\n      const tnsr::I<DataVector, Dim>& velocity_field);\n};\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/VelocityField.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarAdvection/VelocityField.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace ScalarAdvection::Tags {\n\ntemplate <size_t Dim>\nvoid VelocityFieldCompute<Dim>::function(\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> velocity_field,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  set_number_of_grid_points(velocity_field, inertial_coords);\n  if constexpr (Dim == 1) {\n    // 1D : advection to +x direction with velocity 1.0\n    get<0>(*velocity_field) = 1.0;\n  } else if constexpr (Dim == 2) {\n    // 2D : rotation about (0.5, 0.5)\n    for (size_t i = 0; i < get_size(get<0>(inertial_coords)); ++i) {\n      const auto& x = get<0>(inertial_coords)[i];\n      const auto& y = get<1>(inertial_coords)[i];\n      get<0>(*velocity_field)[i] = 0.5 - y;\n      get<1>(*velocity_field)[i] = -0.5 + x;\n    }\n  }\n}\n\n}  // namespace ScalarAdvection::Tags\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template void                                                      \\\n  ScalarAdvection::Tags::VelocityFieldCompute<DIM(data)>::function(  \\\n      gsl::not_null<tnsr::I<DataVector, DIM(data)>*> velocity_field, \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& inertial_coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarAdvection/VelocityField.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ScalarAdvection::Tags {\n/*!\n * \\brief Compute the advection velocity field \\f$v\\f$ of the ScalarAdvection\n * system\n *\n * - For 1D problem, \\f$v(x) = 1.0\\f$\n * - For 2D problem, \\f$v(x,y) = (0.5-y,-0.5+x)\\f$\n *\n */\ntemplate <size_t Dim>\nstruct VelocityFieldCompute : VelocityField<Dim>, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n  using return_type = tnsr::I<DataVector, Dim>;\n  using base = VelocityField<Dim>;\n\n  static void function(\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> velocity_field,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n};\n}  // namespace ScalarAdvection::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  SetInitialData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  SetInitialData.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Actions/SetInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarTensor/Actions/SetInitialData.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace ScalarTensor {\n\nNumericInitialData::NumericInitialData(\n    std::string file_glob, std::string subfile_name,\n    std::variant<double, importers::ObservationSelector> observation_value,\n    std::optional<double> observation_value_epsilon, bool enable_interpolation,\n    typename GhNumericId::Variables::type gh_selected_variables,\n    typename ScalarNumericId::Variables::type hydro_selected_variables)\n    : gh_numeric_id_(file_glob, subfile_name, observation_value,\n                     observation_value_epsilon.value_or(1.0e-12),\n                     enable_interpolation, std::move(gh_selected_variables)),\n      scalar_numeric_id_(\n          std::move(file_glob), std::move(subfile_name), observation_value,\n          observation_value_epsilon.value_or(1.0e-12), enable_interpolation,\n          std::move(hydro_selected_variables)) {}\n\nNumericInitialData::NumericInitialData(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID NumericInitialData::my_PUP_ID = 0;\n\nsize_t NumericInitialData::volume_data_id() const {\n  size_t hash = 0;\n  boost::hash_combine(hash, gh_numeric_id_.volume_data_id());\n  boost::hash_combine(hash, scalar_numeric_id_.volume_data_id());\n  return hash;\n}\n\nvoid NumericInitialData::pup(PUP::er& p) {\n  p | gh_numeric_id_;\n  p | scalar_numeric_id_;\n}\n\nbool operator==(const NumericInitialData& lhs, const NumericInitialData& rhs) {\n  return lhs.gh_numeric_id_ == rhs.gh_numeric_id_ and\n         lhs.scalar_numeric_id_ == rhs.scalar_numeric_id_;\n}\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Actions/SetInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Evolution/NumericInitialData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor {\n\n/*!\n * \\brief Numeric initial data loaded from volume data files\n */\nclass NumericInitialData : public evolution::initial_data::InitialData,\n                           public evolution::NumericInitialData {\n private:\n  using GhNumericId = gh::NumericInitialData;\n  using ScalarNumericId = CurvedScalarWave::NumericInitialData;\n\n public:\n  using all_vars =\n      tmpl::append<GhNumericId::all_vars, ScalarNumericId::all_vars>;\n\n  struct GhVariables : GhNumericId::Variables {};\n  struct ScalarVariables : ScalarNumericId::Variables {};\n\n  using options = tmpl::push_back<importers::ImporterOptions::tags_list,\n                                  GhVariables, ScalarVariables>;\n\n  static constexpr Options::String help =\n      \"Numeric initial data for the Scalar Tensor system loaded from volume \"\n      \"data files\";\n\n  NumericInitialData() = default;\n  NumericInitialData(const NumericInitialData& rhs) = default;\n  NumericInitialData& operator=(const NumericInitialData& rhs) = default;\n  NumericInitialData(NumericInitialData&& /*rhs*/) = default;\n  NumericInitialData& operator=(NumericInitialData&& /*rhs*/) = default;\n  ~NumericInitialData() override = default;\n\n  /// \\cond\n  explicit NumericInitialData(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NumericInitialData);\n  /// \\endcond\n\n  std::unique_ptr<evolution::initial_data::InitialData> get_clone()\n      const override {\n    return std::make_unique<NumericInitialData>(*this);\n  }\n\n  NumericInitialData(\n      std::string file_glob, std::string subfile_name,\n      std::variant<double, importers::ObservationSelector> observation_value,\n      std::optional<double> observation_value_epsilon,\n      bool enable_interpolation,\n      typename GhNumericId::Variables::type gh_selected_variables,\n      typename ScalarNumericId::Variables::type hydro_selected_variables);\n\n  const importers::ImporterOptions& importer_options() const {\n    return gh_numeric_id_.importer_options();\n  }\n\n  const GhNumericId& gh_numeric_id() const { return gh_numeric_id_; }\n\n  const ScalarNumericId& scalar_numeric_id() const {\n    return scalar_numeric_id_;\n  }\n\n  size_t volume_data_id() const;\n\n  template <typename... AllTags>\n  void select_for_import(\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> fields) const {\n    gh_numeric_id_.select_for_import(fields);\n    scalar_numeric_id_.select_for_import(fields);\n  }\n\n  template <typename... AllTags>\n  void set_initial_data(\n      const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n      const gsl::not_null<tnsr::aa<DataVector, 3>*> pi,\n      const gsl::not_null<tnsr::iaa<DataVector, 3>*> phi,\n      const gsl::not_null<Scalar<DataVector>*> psi_scalar,\n      const gsl::not_null<Scalar<DataVector>*> pi_scalar,\n      const gsl::not_null<tnsr::i<DataVector, 3>*> phi_scalar,\n      const gsl::not_null<tuples::TaggedTuple<AllTags...>*> numeric_data,\n      const Mesh<3>& mesh,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian) const {\n    gh_numeric_id_.set_initial_data(spacetime_metric, pi, phi, numeric_data,\n                                    mesh, inv_jacobian);\n    scalar_numeric_id_.set_initial_data(psi_scalar, pi_scalar, phi_scalar,\n                                        numeric_data);\n  }\n\n  void pup(PUP::er& p) override;\n\n  friend bool operator==(const NumericInitialData& lhs,\n                         const NumericInitialData& rhs);\n\n private:\n  GhNumericId gh_numeric_id_{};\n  ScalarNumericId scalar_numeric_id_{};\n};\n\nnamespace Actions {\n\n/*!\n * \\brief Dispatch loading numeric initial data from files.\n *\n * Place this action before\n * ScalarTensor::Actions::SetNumericInitialData in the action list.\n * See importers::Actions::ReadAllVolumeDataAndDistribute for details, which is\n * invoked by this action.\n */\nstruct SetInitialData {\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const parallel_component) {\n    // Dispatch to the correct `apply` overload based on type of initial data\n    using initial_data_classes =\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 evolution::initial_data::InitialData>;\n    return call_with_dynamic_type<Parallel::iterable_action_return_t,\n                                  initial_data_classes>(\n        &db::get<evolution::initial_data::Tags::InitialData>(box),\n        [&box, &cache, &array_index,\n         &parallel_component](const auto* const initial_data) {\n          return apply(make_not_null(&box), *initial_data, cache, array_index,\n                       parallel_component);\n        });\n  }\n\n private:\n  static constexpr size_t Dim = 3;\n\n  // Numeric initial data\n  template <typename DbTagsList, typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,\n      const NumericInitialData& initial_data,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ParallelComponent* const /*meta*/) {\n    // If we are using GH Numeric ID, then we don't have to set Pi and Phi since\n    // we are reading them in. Also we only need to mutate this tag once so do\n    // it on the first element.\n    if (is_zeroth_element(array_index) and\n        std::holds_alternative<gh::NumericInitialData::GhVars>(\n            initial_data.gh_numeric_id().selected_variables())) {\n      Parallel::mutate<gh::Tags::SetPiAndPhiFromConstraints,\n                       gh::gauges::SetPiAndPhiFromConstraintsCacheMutator>(\n          cache, false);\n    }\n    // Select the subset of the available variables that we want to read from\n    // the volume data file\n    tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n        importers::Tags::Selected, NumericInitialData::all_vars>>\n        selected_fields{};\n    initial_data.select_for_import(make_not_null(&selected_fields));\n    // Dispatch loading the variables from the volume data file\n    // - Not using `ckLocalBranch` here to make sure the simple action\n    //   invocation is asynchronous.\n    auto& reader_component = Parallel::get_parallel_component<\n        importers::ElementDataReader<Metavariables>>(cache);\n    Parallel::simple_action<importers::Actions::ReadAllVolumeDataAndDistribute<\n        3, NumericInitialData::all_vars, ParallelComponent>>(\n        reader_component, initial_data.importer_options(),\n        initial_data.volume_data_id(), std::move(selected_fields));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  // \"AnalyticData\"-type initial data\n  template <typename DbTagsList, typename InitialData, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const InitialData& initial_data,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*meta*/) {\n    // Get ADM + scalar variables from analytic data / solution\n    const auto& [coords, mesh, inv_jacobian] = [&box]() {\n      return std::forward_as_tuple(\n          db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(*box),\n          db::get<domain::Tags::Mesh<Dim>>(*box),\n          db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                Frame::Inertial>>(*box));\n    }();\n    auto vars = evolution::Initialization::initial_data(\n        initial_data, coords, db::get<::Tags::Time>(*box),\n        tmpl::append<tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                                gr::Tags::Lapse<DataVector>,\n                                gr::Tags::Shift<DataVector, 3>,\n                                gr::Tags::ExtrinsicCurvature<DataVector, 3>>,\n                     // Don't use the scalar gradient\n                     tmpl::list<CurvedScalarWave::Tags::Psi,\n                                CurvedScalarWave::Tags::Pi>>{});\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n    const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n\n    const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n\n    const auto& extrinsic_curvature =\n        get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(vars);\n\n    // Compute GH vars from ADM vars\n    db::mutate<gr::Tags::SpacetimeMetric<DataVector, 3>,\n               gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>(\n        &gh::initial_gh_variables_from_adm<3>, box, spatial_metric, lapse,\n        shift, extrinsic_curvature, mesh, inv_jacobian);\n\n    // Move scalar variables and compute gradient\n    db::mutate<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n               CurvedScalarWave::Tags::Phi<3>>(\n        [&vars](const gsl::not_null<Scalar<DataVector>*> psi_scalar,\n                const gsl::not_null<Scalar<DataVector>*> pi_scalar,\n                const gsl::not_null<tnsr::i<DataVector, 3>*> phi_scalar,\n                const Mesh<3>& local_mesh,\n                const InverseJacobian<DataVector, 3_st, Frame::ElementLogical,\n                                      Frame::Inertial>& local_inv_jacobian) {\n          *psi_scalar = std::move(get<CurvedScalarWave::Tags::Psi>(vars));\n          *pi_scalar = std::move(get<CurvedScalarWave::Tags::Pi>(vars));\n          // Set Phi to the numerical spatial derivative of the scalar\n          partial_derivative(phi_scalar, *psi_scalar, local_mesh,\n                             local_inv_jacobian);\n        },\n        box, mesh, inv_jacobian);\n\n    // No need to import numeric initial data, so we terminate the phase by\n    // pausing the algorithm on this element\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Receive numeric initial data loaded by\n * ScalarTensor::Actions::SetInitialData.\n */\nstruct ReceiveNumericInitialData {\n  static constexpr size_t Dim = 3;\n  using inbox_tags =\n      tmpl::list<importers::Tags::VolumeData<NumericInitialData::all_vars>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox =\n        tuples::get<importers::Tags::VolumeData<NumericInitialData::all_vars>>(\n            inboxes);\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        db::get<evolution::initial_data::Tags::InitialData>(box));\n    const size_t volume_data_id = initial_data.volume_data_id();\n    if (inbox.find(volume_data_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    auto numeric_data = std::move(inbox.extract(volume_data_id).mapped());\n\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    const auto& inv_jacobian =\n        db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                              Frame::Inertial>>(box);\n\n    db::mutate<gr::Tags::SpacetimeMetric<DataVector, 3>,\n               gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n               CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n               CurvedScalarWave::Tags::Phi<3>>(\n        [&initial_data, &numeric_data, &mesh, &inv_jacobian](\n            const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n            const gsl::not_null<tnsr::aa<DataVector, 3>*> pi,\n            const gsl::not_null<tnsr::iaa<DataVector, 3>*> phi,\n            const gsl::not_null<Scalar<DataVector>*> psi_scalar,\n            const gsl::not_null<Scalar<DataVector>*> pi_scalar,\n            const gsl::not_null<tnsr::i<DataVector, 3>*> phi_scalar) {\n          initial_data.set_initial_data(spacetime_metric, pi, phi,\n\n                                        psi_scalar, pi_scalar, phi_scalar,\n\n                                        make_not_null(&numeric_data), mesh,\n                                        inv_jacobian);\n        },\n        make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarTensor::BoundaryConditions {\nBoundaryCondition::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\nvoid BoundaryCondition::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n}  // namespace ScalarTensor::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace ScalarTensor {\n/// \\brief Boundary conditions for the combined Generalized Harmonic and\n/// CurvedScalarWave systems\nnamespace BoundaryConditions {\n\n/// \\brief The base class for Generalized Harmonic and scalar field combined\n/// boundary conditions; all boundary conditions for this system must inherit\n/// from this base class.\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n} // namespace BoundaryCondition\n} // namespace ScalarTensor\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  ConstraintPreserving.cpp\n  DemandOutgoingCharSpeeds.cpp\n  DirichletAnalytic.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  ConstraintPreserving.hpp\n  DemandOutgoingCharSpeeds.hpp\n  DirichletAnalytic.hpp\n  Factory.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/ConstraintPreserving.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/ConstraintPreserving.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\nnamespace ScalarTensor::BoundaryConditions {\nConstraintPreserving::ConstraintPreserving(\n    gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType type,\n    std::optional<std::unique_ptr<::MathFunction<1, Frame::Inertial>>>\n        incoming_wave_profile)\n    : constraint_preserving_(type, std::move(incoming_wave_profile)) {}\n\n// LCOV_EXCL_START\nConstraintPreserving::ConstraintPreserving(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n// LCOV_EXCL_STOP\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nConstraintPreserving::get_clone() const {\n  return std::make_unique<ConstraintPreserving>(*this);\n}\n\nvoid ConstraintPreserving::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | constraint_preserving_;\n  p | csw_constraint_preserving_;\n}\n\nstd::optional<std::string> ConstraintPreserving::dg_ghost(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n\n    const gsl::not_null<Scalar<DataVector>*> psi_scalar,\n    const gsl::not_null<Scalar<DataVector>*> pi_scalar,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> phi_scalar,\n\n    // c.f. dg_package_data_temporary_tags from the combined Upwind correction\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    const gsl::not_null<Scalar<DataVector>*> gamma1_scalar,\n    const gsl::not_null<Scalar<DataVector>*> gamma2_scalar,\n\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n    /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& interior_spacetime_metric,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& interior_pi,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& interior_phi,\n\n    const Scalar<DataVector>& psi_scalar_interior,\n    const Scalar<DataVector>& pi_scalar_interior,\n    const tnsr::i<DataVector, 3>& phi_scalar_interior,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*coords*/,\n    const Scalar<DataVector>& interior_gamma1,\n    const Scalar<DataVector>& interior_gamma2,\n    const Scalar<DataVector>& interior_lapse,\n    const tnsr::I<DataVector, 3>& interior_shift,\n    const tnsr::II<DataVector, 3>& interior_inv_spatial_metric,\n    const tnsr::AA<DataVector, 3,\n                   Frame::Inertial>& /*inverse_spacetime_metric*/,\n    const tnsr::A<DataVector, 3, Frame::Inertial>&\n    /*spacetime_unit_normal_vector*/,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*three_index_constraint*/,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& /*gauge_source*/,\n    const tnsr::ab<DataVector, 3, Frame::Inertial>&\n    /*spacetime_deriv_gauge_source*/,\n    const Scalar<DataVector>& interior_gamma1_scalar,\n    const Scalar<DataVector>& interior_gamma2_scalar,\n\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, 3, Frame::Inertial>&\n    /*logical_dt_spacetime_metric*/,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& /*logical_dt_pi*/,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*logical_dt_phi*/,\n\n    const Scalar<DataVector>& /*logical_dt_psi_scalar*/,\n    const Scalar<DataVector>& /*logical_dt_pi_scalar*/,\n    const tnsr::i<DataVector, 3>& /*logical_dt_phi_scalar*/,\n\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*d_spacetime_metric*/,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*d_pi*/,\n    const tnsr::ijaa<DataVector, 3, Frame::Inertial>& /*d_phi*/,\n\n    const tnsr::i<DataVector, 3, Frame::Inertial>& /*d_psi_scalar*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& /*d_pi_scalar*/,\n    const tnsr::ij<DataVector, 3, Frame::Inertial>& /*d_phi_scalar*/) {\n  // GH\n  *gamma1 = interior_gamma1;\n  *gamma2 = interior_gamma2;\n  *spacetime_metric = interior_spacetime_metric;\n  *pi = interior_pi;\n  *phi = interior_phi;\n  *lapse = interior_lapse;\n  *shift = interior_shift;\n  *inv_spatial_metric = interior_inv_spatial_metric;\n\n  // Scalar\n  *psi_scalar = psi_scalar_interior;\n  *pi_scalar = pi_scalar_interior;\n  *phi_scalar = phi_scalar_interior;\n  *gamma1_scalar = interior_gamma1_scalar;\n  *gamma2_scalar = interior_gamma2_scalar;\n\n  return {};\n}\n\nstd::optional<std::string> ConstraintPreserving::dg_time_derivative(\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        dt_spacetime_metric_correction,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        dt_pi_correction,\n    const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*>\n        dt_phi_correction,\n\n    const gsl::not_null<Scalar<DataVector>*> dt_psi_scalar_correction,\n    const gsl::not_null<Scalar<DataVector>*> dt_pi_scalar_correction,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        dt_phi_scalar_correction,\n\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n    // c.f. dg_interior_evolved_variables_tags\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& pi,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& phi,\n\n    const Scalar<DataVector>& psi_scalar,\n    const Scalar<DataVector>& /*pi_scalar*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& phi_scalar,\n\n    // c.f. dg_interior_temporary_tags\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::II<DataVector, 3>& /*interior_inv_spatial_metric*/,\n    const tnsr::AA<DataVector, 3, Frame::Inertial>& inverse_spacetime_metric,\n    const tnsr::A<DataVector, 3, Frame::Inertial>& spacetime_unit_normal_vector,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& three_index_constraint,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& gauge_source,\n    const tnsr::ab<DataVector, 3, Frame::Inertial>&\n        spacetime_deriv_gauge_source,\n    const Scalar<DataVector>& gamma1_scalar,\n    const Scalar<DataVector>& gamma2_scalar,\n\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& logical_dt_spacetime_metric,\n    const tnsr::aa<DataVector, 3, Frame::Inertial>& logical_dt_pi,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& logical_dt_phi,\n\n    const Scalar<DataVector>& logical_dt_psi_scalar,\n    const Scalar<DataVector>& logical_dt_pi_scalar,\n    const tnsr::i<DataVector, 3>& logical_dt_phi_scalar,\n\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, 3, Frame::Inertial>& d_pi,\n    const tnsr::ijaa<DataVector, 3, Frame::Inertial>& d_phi,\n\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_psi_scalar,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_pi_scalar,\n    const tnsr::ij<DataVector, 3, Frame::Inertial>& d_phi_scalar) const {\n  // GH ConstraintPreserving\n  auto gh_string = constraint_preserving_.dg_time_derivative(\n      dt_spacetime_metric_correction, dt_pi_correction, dt_phi_correction,\n      face_mesh_velocity, normal_covector, normal_vector, spacetime_metric, pi,\n      phi, coords, gamma1, gamma2, lapse, shift, inverse_spacetime_metric,\n      spacetime_unit_normal_vector, three_index_constraint, gauge_source,\n      spacetime_deriv_gauge_source, logical_dt_spacetime_metric, logical_dt_pi,\n      logical_dt_phi, d_spacetime_metric, d_pi, d_phi);\n\n  // Scalar ConstraintPreservingSphericalRadiation boundary conditions\n  auto scalar_string = csw_constraint_preserving_.dg_time_derivative(\n      dt_psi_scalar_correction, dt_pi_scalar_correction,\n      dt_phi_scalar_correction, face_mesh_velocity, normal_covector,\n      normal_vector, psi_scalar, phi_scalar, coords, gamma1_scalar,\n      gamma2_scalar, lapse, shift, logical_dt_psi_scalar, logical_dt_pi_scalar,\n      logical_dt_phi_scalar, d_psi_scalar, d_pi_scalar, d_phi_scalar);\n\n  if (not gh_string.has_value() and not scalar_string.has_value()) {\n    return {};\n  }\n  if (not gh_string.has_value()) {\n    return scalar_string;\n  }\n  if (not scalar_string.has_value()) {\n    return gh_string;\n  }\n  return gh_string.value() + \";\" + scalar_string.value();\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID ConstraintPreserving::my_PUP_ID = 0;\n}  // namespace ScalarTensor::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/ConstraintPreserving.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor::BoundaryConditions {\n/*!\n * \\brief Sets constraint-preserving boundary conditions on the variables of the\n * ScalarTensor system.\n * \\details The constraint-preserving boundary conditions on the scalar\n * variables are approximate as they assume a fixed spacetime geometry.\n * Likewise, the constraint-preserving boundary conditions on the metric\n * variables assume that there is no back-reaction of the scalar stress energy\n * tensor on the metric.\n *\n */\nclass ConstraintPreserving final : public BoundaryCondition {\n public:\n  using options = tmpl::push_back<\n      typename gh::BoundaryConditions::ConstraintPreservingBjorhus<3>::options>;\n\n  static constexpr Options::String help{\n      \"Constraint-preserving boundary conditions are applied for the \"\n      \"Generalized Harmonic variables and spherical radiation constraint-\"\n      \"preserving boundary conditions are applied for the scalar variables.\"};\n\n  ConstraintPreserving() = default;\n  explicit ConstraintPreserving(\n      gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType type,\n      std::optional<std::unique_ptr<::MathFunction<1, Frame::Inertial>>>\n          incoming_wave_profile = std::nullopt);\n\n  ConstraintPreserving(ConstraintPreserving&&) = default;\n  ConstraintPreserving& operator=(ConstraintPreserving&&) = default;\n  ConstraintPreserving(const ConstraintPreserving&) = default;\n  ConstraintPreserving& operator=(const ConstraintPreserving&) = default;\n  ~ConstraintPreserving() override = default;\n\n  explicit ConstraintPreserving(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, ConstraintPreserving);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::GhostAndTimeDerivative;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                 CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<3>>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>,\n                 ::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 gr::Tags::InverseSpacetimeMetric<DataVector, 3>,\n                 gr::Tags::SpacetimeNormalVector<DataVector, 3>,\n                 gh::Tags::ThreeIndexConstraint<DataVector, 3>,\n                 gh::Tags::GaugeH<DataVector, 3>,\n                 gh::Tags::SpacetimeDerivGaugeH<DataVector, 3>,\n                 CurvedScalarWave::Tags::ConstraintGamma1,\n                 CurvedScalarWave::Tags::ConstraintGamma2>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_ghost(\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n\n      gsl::not_null<Scalar<DataVector>*> psi_scalar,\n      gsl::not_null<Scalar<DataVector>*> pi_scalar,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> phi_scalar,\n\n      // c.f. dg_package_data_temporary_tags from the combined Upwind correction\n      gsl::not_null<Scalar<DataVector>*> gamma1,\n      gsl::not_null<Scalar<DataVector>*> gamma2,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      gsl::not_null<Scalar<DataVector>*> gamma1_scalar,\n      gsl::not_null<Scalar<DataVector>*> gamma2_scalar,\n\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& interior_spacetime_metric,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& interior_pi,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& interior_phi,\n\n      const Scalar<DataVector>& psi_scalar_interior,\n      const Scalar<DataVector>& pi_scalar_interior,\n      const tnsr::i<DataVector, 3>& phi_scalar_interior,\n\n      const tnsr::I<DataVector, 3, Frame::Inertial>& /*coords*/,\n      const Scalar<DataVector>& interior_gamma1,\n      const Scalar<DataVector>& interior_gamma2,\n      const Scalar<DataVector>& interior_lapse,\n      const tnsr::I<DataVector, 3>& interior_shift,\n      const tnsr::II<DataVector, 3>& interior_inv_spatial_metric,\n      const tnsr::AA<DataVector, 3,\n                     Frame::Inertial>& /*inverse_spacetime_metric*/,\n      const tnsr::A<DataVector, 3, Frame::Inertial>&\n      /*spacetime_unit_normal_vector*/,\n      const tnsr::iaa<DataVector, 3,\n                      Frame::Inertial>& /*three_index_constraint*/,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& /*gauge_source*/,\n      const tnsr::ab<DataVector, 3, Frame::Inertial>&\n      /*spacetime_deriv_gauge_source*/,\n      const Scalar<DataVector>& interior_gamma1_scalar,\n      const Scalar<DataVector>& interior_gamma2_scalar,\n\n      // c.f. dg_interior_dt_vars_tags\n      const tnsr::aa<DataVector, 3, Frame::Inertial>&\n      /*logical_dt_spacetime_metric*/,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& /*logical_dt_pi*/,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*logical_dt_phi*/,\n\n      const Scalar<DataVector>& /* logical_dt_psi_scalar*/,\n      const Scalar<DataVector>& /*logical_dt_pi_scalar*/,\n      const tnsr::i<DataVector, 3>& /*logical_dt_phi_scalar*/,\n\n      // c.f. dg_interior_deriv_vars_tags\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*d_spacetime_metric*/,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& /*d_pi*/,\n      const tnsr::ijaa<DataVector, 3, Frame::Inertial>& /*d_phi*/,\n\n      const tnsr::i<DataVector, 3, Frame::Inertial>& /*d_psi_scalar*/,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& /*d_pi_scalar*/,\n      const tnsr::ij<DataVector, 3, Frame::Inertial>& /*d_phi_scalar*/);\n\n  using dg_interior_dt_vars_tags =\n      tmpl::list<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                 ::Tags::dt<gh::Tags::Pi<DataVector, 3>>,\n                 ::Tags::dt<gh::Tags::Phi<DataVector, 3>>,\n                 ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                 ::Tags::dt<CurvedScalarWave::Tags::Pi>,\n                 ::Tags::dt<CurvedScalarWave::Tags::Phi<3>>>;\n  using dg_interior_deriv_vars_tags =\n      tmpl::list<::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 ::Tags::deriv<gh::Tags::Pi<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<CurvedScalarWave::Tags::Pi, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<CurvedScalarWave::Tags::Phi<3>, tmpl::size_t<3>,\n                               Frame::Inertial>>;\n\n  std::optional<std::string> dg_time_derivative(\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n          dt_spacetime_metric_correction,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> dt_pi_correction,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*>\n          dt_phi_correction,\n\n      gsl::not_null<Scalar<DataVector>*> dt_psi_scalar_correction,\n      gsl::not_null<Scalar<DataVector>*> dt_pi_scalar_correction,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          dt_phi_scalar_correction,\n\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      // c.f. dg_interior_evolved_variables_tags\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& spacetime_metric,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& pi,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& phi,\n\n      const Scalar<DataVector>& psi_scalar, const Scalar<DataVector>& pi_scalar,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& phi_scalar,\n\n      // c.f. dg_interior_temporary_tags\n      const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n      const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const tnsr::II<DataVector, 3>& /*interior_inv_spatial_metric*/,\n      const tnsr::AA<DataVector, 3, Frame::Inertial>& inverse_spacetime_metric,\n      const tnsr::A<DataVector, 3, Frame::Inertial>&\n          spacetime_unit_normal_vector,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& three_index_constraint,\n      const tnsr::a<DataVector, 3, Frame::Inertial>& gauge_source,\n      const tnsr::ab<DataVector, 3, Frame::Inertial>&\n          spacetime_deriv_gauge_source,\n      const Scalar<DataVector>& gamma1_scalar,\n      const Scalar<DataVector>& gamma2_scalar,\n\n      // c.f. dg_interior_dt_vars_tags\n      const tnsr::aa<DataVector, 3, Frame::Inertial>&\n          logical_dt_spacetime_metric,\n      const tnsr::aa<DataVector, 3, Frame::Inertial>& logical_dt_pi,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& logical_dt_phi,\n\n      const Scalar<DataVector>& logical_dt_psi_scalar,\n      const Scalar<DataVector>& logical_dt_pi_scalar,\n      const tnsr::i<DataVector, 3>& logical_dt_phi_scalar,\n\n      // c.f. dg_interior_deriv_vars_tags\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& d_spacetime_metric,\n      const tnsr::iaa<DataVector, 3, Frame::Inertial>& d_pi,\n      const tnsr::ijaa<DataVector, 3, Frame::Inertial>& d_phi,\n\n      const tnsr::i<DataVector, 3, Frame::Inertial>& d_psi_scalar,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& d_pi_scalar,\n      const tnsr::ij<DataVector, 3, Frame::Inertial>& d_phi_scalar) const;\n\n private:\n  gh::BoundaryConditions::ConstraintPreservingBjorhus<3>\n      constraint_preserving_{};\n  CurvedScalarWave::BoundaryConditions::ConstraintPreservingSphericalRadiation<\n      3>\n      csw_constraint_preserving_{};\n};\n}  // namespace ScalarTensor::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace ScalarTensor::BoundaryConditions {\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDemandOutgoingCharSpeeds::get_clone() const {\n  return std::make_unique<DemandOutgoingCharSpeeds>(*this);\n}\n\nvoid DemandOutgoingCharSpeeds::pup(PUP::er& p) { BoundaryCondition::pup(p); }\n\nDemandOutgoingCharSpeeds::DemandOutgoingCharSpeeds(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::optional<std::string>\nDemandOutgoingCharSpeeds::dg_demand_outgoing_char_speeds(\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, 3>& outward_directed_normal_covector,\n    const tnsr::I<DataVector, 3>& /*outward_directed_normal_vector*/,\n    const Scalar<DataVector>& gh_gamma1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3>& shift, const Scalar<DataVector>& csw_gamma1) {\n  tnsr::a<DataVector, 3, Frame::Inertial> csw_char_speeds{lapse.size()};\n  ::CurvedScalarWave::characteristic_speeds(make_not_null(&csw_char_speeds),\n                                            csw_gamma1, lapse, shift,\n                                            outward_directed_normal_covector);\n\n  std::array<DataVector, 4> gh_char_speeds = ::gh::characteristic_speeds(\n      gh_gamma1, lapse, shift, outward_directed_normal_covector,\n      face_mesh_velocity);\n\n  if (face_mesh_velocity.has_value()) {\n    const auto face_speed =\n        dot_product(outward_directed_normal_covector, *face_mesh_velocity);\n    for (auto& char_speed : csw_char_speeds) {\n      char_speed -= get(face_speed);\n    }\n  }\n  for (size_t i = 0; i < csw_char_speeds.size(); ++i) {\n    if (min(csw_char_speeds[i]) < 0.) {\n      return MakeString{}\n             << \"Detected negative characteristic speed at boundary with \"\n                \"outgoing char speeds boundary conditions specified. The \"\n                \"speed is \"\n             << min(csw_char_speeds[i]) << \" for index \" << i\n             << \". To see which characteristic field this corresponds to, \"\n                \"check the function `characteristic_speeds` in \"\n                \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp.\";\n    }\n\n    if (min(gsl::at(gh_char_speeds, i)) < 0.) {\n      return MakeString{}\n             << \"Detected negative characteristic speed at boundary with \"\n                \"outgoing char speeds boundary conditions specified. The \"\n                \"speed is \"\n             << min(gsl::at(gh_char_speeds, i)) << \" for index \" << i\n             << \". To see which characteristic field this corresponds to, \"\n                \"check the function `characteristic_speeds` in \"\n                \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp.\";\n    }\n  }\n  return std::nullopt;  // LCOV_EXCL_LINE\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DemandOutgoingCharSpeeds::my_PUP_ID = 0;\n}  // namespace ScalarTensor::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/DemandOutgoingCharSpeeds.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor::BoundaryConditions {\n/// A `BoundaryCondition` that only verifies that all characteristic speeds are\n/// directed out of the domain; no boundary data is altered by this boundary\n/// condition.\nclass DemandOutgoingCharSpeeds final : public BoundaryCondition {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A boundary condition that only verifies the characteristic speeds are \"\n      \"all directed out of the domain.\"};\n\n  DemandOutgoingCharSpeeds() = default;\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<::gh::Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, 3>,\n                 ::CurvedScalarWave::Tags::ConstraintGamma1>;\n  using dg_gridless_tags = tmpl::list<>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>&\n      /*outward_directed_normal_vector*/,\n\n      const Scalar<DataVector>& gh_gamma1, const Scalar<DataVector>& lapse,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n      const Scalar<DataVector>& csw_gamma1);\n};\n}  // namespace ScalarTensor::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GhScalarTensor/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n\nnamespace ScalarTensor::BoundaryConditions {\nDirichletAnalytic::DirichletAnalytic(const DirichletAnalytic& rhs)\n    : BoundaryCondition{dynamic_cast<const BoundaryCondition&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()),\n      amplitude_(rhs.amplitude_) {}\n\nDirichletAnalytic& DirichletAnalytic::operator=(const DirichletAnalytic& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  amplitude_ = rhs.amplitude_;\n  return *this;\n}\n\nDirichletAnalytic::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription,\n    const double amplitude)\n    : analytic_prescription_(std::move(analytic_prescription)),\n      amplitude_(amplitude) {}\n\nDirichletAnalytic::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition(msg) {}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic::get_clone() const {\n  return std::make_unique<DirichletAnalytic>(*this);\n}\n\nvoid DirichletAnalytic::pup(PUP::er& p) {\n  BoundaryCondition::pup(p);\n  p | analytic_prescription_;\n  p | amplitude_;\n}\n\nstd::optional<std::string> DirichletAnalytic::dg_ghost(\n    // GH evolved variables\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*>\n        spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n    const gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n    // Scalar evolved variables\n    const gsl::not_null<Scalar<DataVector>*> psi_scalar,\n    const gsl::not_null<Scalar<DataVector>*> pi_scalar,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> phi_scalar,\n    // GH temporary variables\n    const gsl::not_null<Scalar<DataVector>*> gamma1,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n    // Scalar temporary variables\n    const gsl::not_null<Scalar<DataVector>*> gamma1_scalar,\n    const gsl::not_null<Scalar<DataVector>*> gamma2_scalar,\n    // Inverse metric\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n        inv_spatial_metric,\n    // Mesh variables\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n    /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& /*normal_vector*/,\n    // GH interior variables\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n    const Scalar<DataVector>& interior_gamma1,\n    const Scalar<DataVector>& interior_gamma2,\n    // Scalar interior variables\n    const Scalar<DataVector>& gamma1_interior_scalar,\n    const Scalar<DataVector>& gamma2_interior_scalar, const double time) const {\n  *gamma1_scalar = gamma1_interior_scalar;\n  *gamma2_scalar = gamma2_interior_scalar;\n  *psi_scalar =\n      make_with_value<Scalar<DataVector>>(interior_gamma1, amplitude_);\n  *pi_scalar = make_with_value<Scalar<DataVector>>(*psi_scalar, 0.0);\n  *phi_scalar = make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(\n      *psi_scalar, 0.0);\n\n  *gamma1 = interior_gamma1;\n  *gamma2 = interior_gamma2;\n  ASSERT(analytic_prescription_ != nullptr,\n         \"The analytic prescription must be set.\");\n  using evolved_vars_tags = typename ::gh::System<3>::variables_tag::tags_list;\n  auto boundary_values = call_with_dynamic_type<\n      tuples::tagged_tuple_from_typelist<evolved_vars_tags>,\n      gh::ScalarTensor::AnalyticData::all_analytic_data>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const analytic_solution_or_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*analytic_solution_or_data)>>) {\n          return analytic_solution_or_data->variables(coords, time,\n                                                      evolved_vars_tags{});\n\n        } else {\n          (void)time;\n          return analytic_solution_or_data->variables(coords,\n                                                      evolved_vars_tags{});\n        }\n      });\n\n  *spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_values);\n  *pi = get<gh::Tags::Pi<DataVector, 3>>(boundary_values);\n  *phi = get<gh::Tags::Phi<DataVector, 3>>(boundary_values);\n\n  // Now compute lapse and shift...\n  const auto spatial_metric = gr::spatial_metric(*spacetime_metric);\n  determinant_and_inverse(lapse, inv_spatial_metric, spatial_metric);\n  gr::shift(shift, *spacetime_metric, *inv_spatial_metric);\n  gr::lapse(lapse, *shift, *spacetime_metric);\n  return {};\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic::my_PUP_ID = 0;\n}  // namespace ScalarTensor::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace ScalarTensor::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\nclass DirichletAnalytic final : public BoundaryCondition {\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Amplitude of the scalar at the boundary\"};\n  };\n\n  using options = tmpl::list<AnalyticPrescription, Amplitude>;\n\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  DirichletAnalytic(std::unique_ptr<evolution::initial_data::InitialData>\n                        analytic_prescription,\n                    double amplitude);\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<3, Frame::Inertial>,\n                 ::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2,\n                 ::CurvedScalarWave::Tags::ConstraintGamma1,\n                 ::CurvedScalarWave::Tags::ConstraintGamma2>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_ghost(\n      // GH evolved variables\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, 3, Frame::Inertial>*> pi,\n      gsl::not_null<tnsr::iaa<DataVector, 3, Frame::Inertial>*> phi,\n      // Scalar evolved variables\n      gsl::not_null<Scalar<DataVector>*> psi_scalar,\n      gsl::not_null<Scalar<DataVector>*> pi_scalar,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*> phi_scalar,\n      // GH temporary variables\n      gsl::not_null<Scalar<DataVector>*> gamma1,\n      gsl::not_null<Scalar<DataVector>*> gamma2,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> shift,\n      // Scalar temporary variables\n      gsl::not_null<Scalar<DataVector>*> gamma1_scalar,\n      gsl::not_null<Scalar<DataVector>*> gamma2_scalar,\n      // Inverse metric\n      gsl::not_null<tnsr::II<DataVector, 3, Frame::Inertial>*>\n          inv_spatial_metric,\n      // Mesh variables\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      // GH interior variables\n      const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n      const Scalar<DataVector>& interior_gamma1,\n      const Scalar<DataVector>& interior_gamma2,\n      // Scalar interior variables\n      const Scalar<DataVector>& gamma1_interior_scalar,\n      const Scalar<DataVector>& gamma2_interior_scalar, double time) const;\n\n private:\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n  double amplitude_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace ScalarTensor::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/AnalyticConstant.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/ConstraintPreserving.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor::BoundaryConditions {\n/// Typelist of standard BoundaryConditions.\nusing standard_boundary_conditions =\n    tmpl::list<ScalarTensor::BoundaryConditions::ConstraintPreserving,\n               ScalarTensor::BoundaryConditions::DemandOutgoingCharSpeeds,\n               ScalarTensor::BoundaryConditions::DirichletAnalytic,\n               domain::BoundaryConditions::Periodic<BoundaryCondition>>;\n\n}  // namespace ScalarTensor::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  NamespaceDocs.hpp\n  ProductOfCorrections.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor::BoundaryCorrections {\nnamespace detail {\ntemplate <typename GhList, typename ScalarList>\nstruct AllProductCorrections;\n\ntemplate <typename GhList, typename... ScalarCorrections>\nstruct AllProductCorrections<GhList, tmpl::list<ScalarCorrections...>> {\n  using type = tmpl::flatten<tmpl::list<\n      tmpl::transform<GhList, tmpl::bind<ProductOfCorrections, tmpl::_1,\n                                         tmpl::pin<ScalarCorrections>>>...>>;\n};\n}  // namespace detail\n\nusing standard_boundary_corrections = typename detail::AllProductCorrections<\n    typename gh::BoundaryCorrections::standard_boundary_corrections<3>,\n    typename CurvedScalarWave::BoundaryCorrections::\n        standard_boundary_corrections<3>>::type;\n}  // namespace ScalarTensor::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace ScalarTensor::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/BoundaryCorrections/ProductOfCorrections.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace ScalarTensor::BoundaryCorrections {\n\n/*!\n * \\brief Apply a boundary condition to the combined Generalized Harmonic (::gh)\n * and scalar field (::CurvedScalarWave) system using boundary corrections\n * defined separately.\n * \\see gh::BoundaryCorrections and CurvedScalarWave::BoundaryCorrections.\n */\n/// @{\ntemplate <\n    typename DerivedGhCorrection, typename DerivedScalarCorrection,\n    typename = typename DerivedGhCorrection::dg_package_field_tags,\n    typename = typename DerivedScalarCorrection::dg_package_field_tags,\n    typename = typename DerivedGhCorrection::dg_package_data_temporary_tags,\n    typename = typename DerivedScalarCorrection::dg_package_data_temporary_tags,\n    typename = typename DerivedGhCorrection::dg_package_data_volume_tags,\n    typename = typename DerivedScalarCorrection::dg_package_data_volume_tags,\n    typename = typename DerivedGhCorrection::dg_boundary_terms_volume_tags,\n    typename = typename DerivedScalarCorrection::dg_boundary_terms_volume_tags>\nclass ProductOfCorrections;\n\ntemplate <typename DerivedGhCorrection, typename DerivedScalarCorrection,\n          typename... GhDgPackagedFieldTags,\n          typename... ScalarDgPackagedFieldTags,\n          typename... GhDgPackageDataTemporaryTags,\n          typename... ScalarDgPackageDataTemporaryTags,\n          typename... GhDgPackageDataVolumeTags,\n          typename... ScalarDgPackageDataVolumeTags,\n          typename... GhDgBoundaryTermsVolumeTags,\n          typename... ScalarDgBoundaryTermsVolumeTags>\nclass ProductOfCorrections<DerivedGhCorrection, DerivedScalarCorrection,\n                           tmpl::list<GhDgPackagedFieldTags...>,\n                           tmpl::list<ScalarDgPackagedFieldTags...>,\n                           tmpl::list<GhDgPackageDataTemporaryTags...>,\n                           tmpl::list<ScalarDgPackageDataTemporaryTags...>,\n                           tmpl::list<GhDgPackageDataVolumeTags...>,\n                           tmpl::list<ScalarDgPackageDataVolumeTags...>,\n                           tmpl::list<GhDgBoundaryTermsVolumeTags...>,\n                           tmpl::list<ScalarDgBoundaryTermsVolumeTags...>>\n    final : public evolution::BoundaryCorrection {\n public:\n  static constexpr size_t dim = 3;\n  using dg_package_field_tags =\n      tmpl::list<GhDgPackagedFieldTags..., ScalarDgPackagedFieldTags...>;\n\n  using dg_package_data_temporary_tags =\n      tmpl::list<GhDgPackageDataTemporaryTags...,\n                 ScalarDgPackageDataTemporaryTags...>;\n\n  using dg_package_data_primitive_tags = tmpl::list<>;\n\n  using dg_package_data_volume_tags =\n      tmpl::list<GhDgPackageDataVolumeTags...,\n                 ScalarDgPackageDataVolumeTags...>;\n\n  using dg_boundary_terms_volume_tags =\n      tmpl::list<GhDgBoundaryTermsVolumeTags...,\n                 ScalarDgBoundaryTermsVolumeTags...>;\n\n  static std::string name() {\n    return \"Product\" + pretty_type::name<DerivedGhCorrection>() + \"GH\" + \"And\" +\n           pretty_type::name<DerivedScalarCorrection>() + \"Scalar\";\n  }\n\n  struct GhCorrection {\n    using type = DerivedGhCorrection;\n    static std::string name() {\n      // We change the default name of the boundary correction to avoid errors\n      // during option parsing\n      return pretty_type::name<DerivedGhCorrection>() + \"GH\";\n    }\n    static constexpr Options::String help{\n        \"The Generalized Harmonic part of the product boundary condition\"};\n  };\n  struct ScalarCorrection {\n    using type = DerivedScalarCorrection;\n    static std::string name() {\n      // We change the default name of the boundary correction to avoid errors\n      // during option parsing\n      return pretty_type::name<DerivedScalarCorrection>() + \"Scalar\";\n    }\n    static constexpr Options::String help{\n        \"The scalar part of the product boundary condition\"};\n  };\n\n  using options = tmpl::list<GhCorrection, ScalarCorrection>;\n\n  static constexpr Options::String help = {\n      \"Direct product of a GH and CurvedScalarWave boundary correction. \"\n      \"See the documentation for the two individual boundary corrections for \"\n      \"further details.\"};\n\n  ProductOfCorrections() = default;\n  ProductOfCorrections(DerivedGhCorrection gh_correction,\n                       DerivedScalarCorrection scalar_correction)\n      : derived_gh_correction_{gh_correction},\n        derived_scalar_correction_{scalar_correction} {}\n  ProductOfCorrections(const ProductOfCorrections&) = default;\n  ProductOfCorrections& operator=(const ProductOfCorrections&) = default;\n  ProductOfCorrections(ProductOfCorrections&&) = default;\n  ProductOfCorrections& operator=(ProductOfCorrections&&) = default;\n  ~ProductOfCorrections() override = default;\n\n  /// \\cond\n  explicit ProductOfCorrections(CkMigrateMessage* msg)\n      : BoundaryCorrection(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ProductOfCorrections);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override {\n    BoundaryCorrection::pup(p);\n    p | derived_gh_correction_;\n    p | derived_scalar_correction_;\n  }\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override {\n    return std::make_unique<ProductOfCorrections>(*this);\n  }\n\n  double dg_package_data(\n      const gsl::not_null<\n          typename GhDgPackagedFieldTags::type*>... gh_packaged_fields,\n      const gsl::not_null<\n          typename ScalarDgPackagedFieldTags::type*>... scalar_packaged_fields,\n      // GH variables\n      const tnsr::aa<DataVector, dim, Frame::Inertial>& spacetime_metric,\n      const tnsr::aa<DataVector, dim, Frame::Inertial>& pi,\n      const tnsr::iaa<DataVector, dim, Frame::Inertial>& phi,\n      // Scalar variables\n      const Scalar<DataVector>& psi_scalar, const Scalar<DataVector>& pi_scalar,\n      const tnsr::i<DataVector, dim, Frame::Inertial>& phi_scalar,\n      // Temporaries\n      const typename GhDgPackageDataTemporaryTags::type&... gh_temporaries,\n      const typename ScalarDgPackageDataTemporaryTags::\n          type&... scalar_temporaries,\n      // Mesh variables\n      const tnsr::i<DataVector, dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      // Volume quantities\n      const typename GhDgPackageDataVolumeTags::type&... gh_volume_quantities,\n      const typename ScalarDgPackageDataVolumeTags::\n          type&... scalar_volume_quantities) const {\n    const double gh_correction_result = derived_gh_correction_.dg_package_data(\n        gh_packaged_fields..., spacetime_metric, pi, phi, gh_temporaries...,\n        normal_covector, normal_vector, mesh_velocity, normal_dot_mesh_velocity,\n        gh_volume_quantities...);\n\n    const double scalar_correction_result =\n        derived_scalar_correction_.dg_package_data(\n            scalar_packaged_fields..., psi_scalar, pi_scalar, phi_scalar,\n            scalar_temporaries..., normal_covector, normal_vector,\n            mesh_velocity, normal_dot_mesh_velocity,\n            scalar_volume_quantities...);\n    return std::max(gh_correction_result, scalar_correction_result);\n  }\n\n  void dg_boundary_terms(\n      // GH boundary corrections\n      const gsl::not_null<tnsr::aa<DataVector, dim, Frame::Inertial>*>\n          boundary_correction_spacetime_metric,\n      const gsl::not_null<tnsr::aa<DataVector, dim, Frame::Inertial>*>\n          boundary_correction_pi,\n      const gsl::not_null<tnsr::iaa<DataVector, dim, Frame::Inertial>*>\n          boundary_correction_phi,\n      // Scalar boundary corrections\n      const gsl::not_null<Scalar<DataVector>*> psi_boundary_correction_scalar,\n      const gsl::not_null<Scalar<DataVector>*> pi_boundary_correction_scalar,\n      const gsl::not_null<tnsr::i<DataVector, dim, Frame::Inertial>*>\n          phi_boundary_correction_scalar,\n      // Packaged fields\n      const typename GhDgPackagedFieldTags::type&... gh_packaged_fields_int,\n      const typename ScalarDgPackagedFieldTags::\n          type&... scalar_packaged_fields_int,\n      const typename GhDgPackagedFieldTags::type&... gh_packaged_fields_ext,\n      const typename ScalarDgPackagedFieldTags::\n          type&... scalar_packaged_fields_ext,\n      // DG formulation\n      const dg::Formulation dg_formulation) const {\n    derived_gh_correction_.dg_boundary_terms(\n        boundary_correction_spacetime_metric, boundary_correction_pi,\n        boundary_correction_phi, gh_packaged_fields_int...,\n        gh_packaged_fields_ext..., dg_formulation);\n\n    derived_scalar_correction_.dg_boundary_terms(\n        psi_boundary_correction_scalar, pi_boundary_correction_scalar,\n        phi_boundary_correction_scalar, scalar_packaged_fields_int...,\n        scalar_packaged_fields_ext..., dg_formulation);\n  }\n\n  const DerivedGhCorrection& gh_correction() const {\n    return derived_gh_correction_;\n  }\n\n  const DerivedScalarCorrection& scalar_correction() const {\n    return derived_scalar_correction_;\n  }\n\n private:\n  DerivedGhCorrection derived_gh_correction_;\n  DerivedScalarCorrection derived_scalar_correction_;\n};\n/// @}\n\n/// \\cond\ntemplate <typename DerivedGhCorrection, typename DerivedScalarCorrection,\n          typename... GhDgPackagedFieldTags,\n          typename... ScalarDgPackagedFieldTags,\n          typename... GhDgPackageDataTemporaryTags,\n          typename... ScalarDgPackageDataTemporaryTags,\n          typename... GhDgPackageDataVolumeTags,\n          typename... ScalarDgPackageDataVolumeTags,\n          typename... GhDgBoundaryTermsVolumeTags,\n          typename... ScalarDgBoundaryTermsVolumeTags>\nPUP::able::PUP_ID ProductOfCorrections<\n    DerivedGhCorrection, DerivedScalarCorrection,\n    tmpl::list<GhDgPackagedFieldTags...>,\n    tmpl::list<ScalarDgPackagedFieldTags...>,\n    tmpl::list<GhDgPackageDataTemporaryTags...>,\n    tmpl::list<ScalarDgPackageDataTemporaryTags...>,\n    tmpl::list<GhDgPackageDataVolumeTags...>,\n    tmpl::list<ScalarDgPackageDataVolumeTags...>,\n    tmpl::list<GhDgBoundaryTermsVolumeTags...>,\n    tmpl::list<ScalarDgBoundaryTermsVolumeTags...>>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace ScalarTensor::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarTensor)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  TimeDerivative.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Characteristics.hpp\n  Constraints.hpp\n  Initialize.hpp\n  System.hpp\n  Tags.hpp\n  TimeDerivative.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ConstraintDamping\n  CurvedScalarWave\n  DataStructures\n  DiscontinuousGalerkin\n  DomainBoundaryConditions\n  GeneralRelativity\n  GeneralizedHarmonic\n  GhScalarTensorAnalyticData\n  ScalarTensorPointwise\n  Serialization\n  Utilities\n  INTERFACE\n  Options\n  Parallel\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(Instantiations)\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor::Tags {\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\n/*!\n * \\brief Computes the largest magnitude of the characteristic speeds.\n *\n * \\details Compute the maximum of the largest characteristic speed of each\n * component system, i.e. the largest speed between ::gh and ::CurvedScalarWave.\n */\ntemplate <typename Frame = Frame::Inertial>\nstruct ComputeLargestCharacteristicSpeed : db::ComputeTag,\n                                           LargestCharacteristicSpeed {\n  static constexpr size_t Dim = 3_st;\n  using argument_tags =\n      tmpl::list<::gh::Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim, Frame>,\n                 gr::Tags::SpatialMetric<DataVector, Dim, Frame>,\n                 CurvedScalarWave::Tags::ConstraintGamma1>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  static void function(const gsl::not_null<double*> speed,\n                       // GH arguments\n                       const Scalar<DataVector>& gamma_1,\n                       const Scalar<DataVector>& lapse,\n                       const tnsr::I<DataVector, Dim, Frame>& shift,\n                       const tnsr::ii<DataVector, Dim, Frame>& spatial_metric,\n                       // Scalar arguments\n                       const Scalar<DataVector>& gamma_1_scalar) {\n    // Largest speed in for Generalized Harmonic\n    double gh_largest_speed = 0.0;\n    gh::Tags::ComputeLargestCharacteristicSpeed<Dim, Frame>::function(\n        make_not_null(&gh_largest_speed), gamma_1, lapse, shift,\n        spatial_metric);\n    // Largest speed for CurvedScalarWave\n    double scalar_largest_speed = 0.0;\n    CurvedScalarWave::Tags::ComputeLargestCharacteristicSpeed<Dim>::function(\n        make_not_null(&scalar_largest_speed), gamma_1_scalar, lapse, shift,\n        spatial_metric);\n    // Compute the maximum speed\n    *speed = std::max(gh_largest_speed, scalar_largest_speed);\n  }\n};\n}  // namespace ScalarTensor::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Constraints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor::Tags {\n/*!\n * \\brief Compute item to get the F-constraint for the generalized harmonic\n * evolution system with an scalar field stress-energy source.\n *\n * \\details See `gh::f_constraint()`. Can be retrieved using\n * `gh::Tags::FConstraint`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct FConstraintCompute\n    : gh::Tags::FConstraint<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gh::Tags::GaugeH<DataVector, SpatialDim, Frame>,\n      gh::Tags::SpacetimeDerivGaugeH<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalOneForm<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, Frame>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, SpatialDim, Frame>,\n      gh::Tags::Pi<DataVector, SpatialDim, Frame>,\n      gh::Tags::Phi<DataVector, SpatialDim, Frame>,\n      ::Tags::deriv<gh::Tags::Pi<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      ::Tags::deriv<gh::Tags::Phi<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      ::gh::Tags::ConstraintGamma2,\n      gh::Tags::ThreeIndexConstraint<DataVector, SpatialDim, Frame>,\n      Tags::TraceReversedStressEnergy<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::a<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*>,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::ab<DataVector, SpatialDim, Frame>&,\n      const tnsr::a<DataVector, SpatialDim, Frame>&,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::II<DataVector, SpatialDim, Frame>&,\n      const tnsr::AA<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::ijaa<DataVector, SpatialDim, Frame>&,\n      const Scalar<DataVector>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&)>(\n      &gh::f_constraint<DataVector, SpatialDim, Frame>);\n\n  using base = gh::Tags::FConstraint<DataVector, SpatialDim, Frame>;\n};\n}  // namespace ScalarTensor::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylMagnetic.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/ScalarSource.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/Tags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/SourceTags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace ScalarTensor {\nnamespace Initialization {\n/// \\brief List of basic compute tags to initialize the the ScalarTensor without\n/// scalar sources\ntemplate <size_t Dim, typename Fr = Frame::Inertial>\nusing scalar_tensor_basic_compute_tags = tmpl::list<\n    // Needed to compute the characteristic speeds for the AH finder\n    gr::Tags::SpatialMetricCompute<DataVector, Dim, Fr>,\n    gr::Tags::DetAndInverseSpatialMetricCompute<DataVector, Dim, Fr>,\n    gr::Tags::ShiftCompute<DataVector, Dim, Fr>,\n    gr::Tags::LapseCompute<DataVector, Dim, Fr>,\n\n    gr::Tags::SpacetimeNormalVectorCompute<DataVector, Dim, Fr>,\n    gh::Tags::DerivLapseCompute<Dim, Fr>,\n\n    gr::Tags::InverseSpacetimeMetricCompute<DataVector, Dim, Fr>,\n    gh::Tags::DerivShiftCompute<Dim, Fr>,\n\n    gh::Tags::DerivSpatialMetricCompute<Dim, Fr>,\n\n    // Compute tags for Trace of Christoffel and Extrinsic curvature\n    gr::Tags::SpatialChristoffelFirstKindCompute<DataVector, Dim, Fr>,\n    gr::Tags::SpatialChristoffelSecondKindCompute<DataVector, Dim, Fr>,\n    gr::Tags::TraceSpatialChristoffelSecondKindCompute<DataVector, Dim, Fr>,\n    gh::Tags::ExtrinsicCurvatureCompute<Dim, Fr>,\n    gh::Tags::TraceExtrinsicCurvatureCompute<Dim, Fr>,\n\n    // Compute constraint damping parameters.\n    gh::Tags::ConstraintGamma0Compute<Dim, Frame::Grid>,\n    gh::Tags::ConstraintGamma1Compute<Dim, Frame::Grid>,\n    gh::Tags::ConstraintGamma2Compute<Dim, Frame::Grid>,\n\n    ScalarTensor::Tags::ConstraintGamma1Compute<Dim, Frame::Grid>,\n    ScalarTensor::Tags::ConstraintGamma2Compute<Dim, Frame::Grid>,\n\n    ScalarTensor::Tags::ScalarSourceCompute>;\n\n/// \\brief List of compute tags to the coupling to curvature\ntemplate <size_t Dim, typename Fr = Frame::Inertial>\nusing sgb_extra_compute_tags = tmpl::list<\n    ::Tags::DerivTensorCompute<\n        gr::Tags::ExtrinsicCurvature<DataVector, Dim, Fr>,\n        ::domain::Tags::InverseJacobian<Dim, ::Frame::ElementLogical,\n                                        ::Frame::Inertial>,\n        ::domain::Tags::Mesh<Dim>>,\n    gr::Tags::CovariantDerivativeOfExtrinsicCurvatureCompute<Dim, Fr>,\n    ::Tags::DerivTensorCompute<\n        gr::Tags::SpatialChristoffelSecondKind<DataVector, Dim, Fr>,\n        ::domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                        Frame::Inertial>,\n        ::domain::Tags::Mesh<Dim>>,\n    gr::Tags::SpatialRicciCompute<DataVector, Dim, Fr>,\n    gr::Tags::SpatialRicciScalarCompute<DataVector, Dim, Fr>,\n    gr::Tags::WeylElectricCompute<DataVector, Dim, Fr>,\n    gr::Tags::WeylElectricScalarCompute<DataVector, Dim, Fr>,\n    gr::Tags::SqrtDetSpatialMetricCompute<DataVector, Dim, Fr>,\n    gr::Tags::WeylMagneticCompute<DataVector, Dim, Fr>,\n    gr::Tags::WeylMagneticScalarCompute<DataVector, Dim, Fr>>;\n\n/// \\brief List of compute tags to be initialized in the ScalarTensor system\n///\n/// \\details The compute tags required include those specified in\n/// ::gh::Actions::InitializeGhAnd3Plus1Variables as well as the tags required\n/// to compute spacetime quantities appearing in the scalar evolution equations.\n/// Namely, we include the compute tags associated to the trace of the extrinsic\n/// curvature and the trace of the spatial Christoffel symbol, as well as the\n/// compute tag required to calculate the source term of the scalar equation.\ntemplate <size_t Dim, typename Fr = Frame::Inertial>\nusing scalar_tensor_3plus1_compute_tags =\n    tmpl::append<scalar_tensor_basic_compute_tags<Dim, Fr>,\n                 sgb_extra_compute_tags<Dim, Fr>>;\n}  // namespace Initialization\n\nnamespace Actions {\nstruct InitializeGhAnd3Plus1Variables {\n  static constexpr size_t volume_dim = 3;\n  using frame = Frame::Inertial;\n  using compute_tags = db::AddComputeTags<\n      Initialization::scalar_tensor_3plus1_compute_tags<volume_dim, frame>>;\n\n  using const_global_cache_tags = tmpl::list<\n      gh::Tags::DampingFunctionGamma0<volume_dim, Frame::Grid>,\n      gh::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n      gh::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>,\n      ScalarTensor::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n      ScalarTensor::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>,\n      ScalarTensor::Tags::RampUpParameters,\n      ScalarTensor::Tags::CouplingParameters, ScalarTensor::Tags::ScalarMass>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.tpp\"\n\ntemplate class Events::ObserveTimeStep<ScalarTensor::System>;\ntemplate class dg::Events::ObserveTimeStepVolume<ScalarTensor::System>;\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n\ntemplate class ChangeTimeStepperOrder<ScalarTensor::System>;\ntemplate class CleanHistory<ScalarTensor::System>;\ntemplate class RecordTimeStepperData<ScalarTensor::System>;\ntemplate class UpdateU<ScalarTensor::System, false>;\ntemplate class UpdateU<ScalarTensor::System, true>;\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <optional>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Evolution/Systems/ScalarTensor/TimeDerivative.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n\ntemplate void\nevolution::dg::Actions::detail::volume_terms<::ScalarTensor::TimeDerivative>(\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::dt,\n        typename ::ScalarTensor::System::variables_tag::tags_list>>*>\n        dt_vars_ptr,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::Flux, typename ::ScalarTensor::System::flux_variables,\n        tmpl::size_t<3>, Frame::Inertial>>*>\n        volume_fluxes,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::deriv, typename ::ScalarTensor::System::gradient_variables,\n        tmpl::size_t<3>, Frame::Inertial>>*>\n        partial_derivs,\n    const gsl::not_null<\n        Variables<typename ::ScalarTensor::System::\n                      compute_volume_time_derivative_terms::temporary_tags>*>\n        temporaries,\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        ::Tags::div,\n        db::wrap_tags_in<::Tags::Flux,\n                         typename ::ScalarTensor::System::flux_variables,\n                         tmpl::size_t<3>, Frame::Inertial>>>*>\n        div_fluxes,\n    const Variables<typename ::ScalarTensor::System::variables_tag::tags_list>&\n        evolved_vars,\n    const ::dg::Formulation dg_formulation, const Mesh<3>& mesh,\n    [[maybe_unused]] const tnsr::I<DataVector, 3, Frame::Inertial>&\n        inertial_coordinates,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>&\n        logical_to_inertial_inverse_jacobian,\n    [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>& mesh_velocity,\n    const std::optional<Scalar<DataVector>>& div_mesh_velocity,\n    // GH argument variables\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::aa<DataVector, 3>& pi, const tnsr::iaa<DataVector, 3>& phi,\n    const Scalar<DataVector>& gamma0, const Scalar<DataVector>& gamma1,\n    const Scalar<DataVector>& gamma2,\n    const ::gh::gauges::GaugeCondition& gauge_condition,\n    const Mesh<3>& mesh_for_rhs, const double& time,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>& inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n        mesh_velocity_gh,\n    // Scalar argument variables\n    const Scalar<DataVector>& pi_scalar,\n    const tnsr::i<DataVector, 3>& phi_scalar,\n    const Scalar<DataVector>& lapse_scalar,\n    const tnsr::I<DataVector, 3>& shift_scalar,\n    const tnsr::i<DataVector, 3>& deriv_lapse,\n    const tnsr::iJ<DataVector, 3>& deriv_shift,\n    const tnsr::II<DataVector, 3>& upper_spatial_metric,\n    const tnsr::I<DataVector, 3>& trace_spatial_christoffel,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>& gamma1_scalar,\n    const Scalar<DataVector>& gamma2_scalar,\n    // Scalar Tensor extra argument tags\n    const Scalar<DataVector>& scalar_source);\n\nINSTANTIATE_PARTIAL_DERIVATIVES_WITH_SYSTEM(ScalarTensor::System, 3,\n                                            Frame::Inertial)\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Characteristics.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/TimeDerivative.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup EvolutionSystemsGroup\n * \\brief Items related to evolving the first-order scalar tensor system.\n */\nnamespace ScalarTensor {\n/*!\n * \\brief Scalar Tensor system obtained from combining the CurvedScalarWave and\n * gh systems.\n *\n * \\details The evolution equations follow from\n * \\f{align*}{\n *  R_{ab} &= 8 \\pi \\, T^{(\\Psi, \\text{TR})}_{ab} ~, \\\\\n *  \\Box \\Psi &= 0~,\n * \\f}\n *\n * where \\f$\\Psi\\f$ is the scalar field and the trace-reversed stress-energy\n * tensor of the scalar field is given by\n * \\f{align*}{\n    T^{(\\Psi, \\text{TR})}_{ab}\n *      &\\equiv T^{(\\Psi)}_{ab} - \\frac{1}{2} g_{ab} g^{cd} T^{(\\Psi)}_{cd} \\\\\n *      &= \\partial_a \\Psi \\partial_b \\Psi ~.\n * \\f}\n *\n * Both systems are recast as first-order systems in terms of the variables\n * \\f{align*}{\n * & g_{ab}~,                                                           \\\\\n * & \\Pi_{ab} = - \\dfrac{1}{\\alpha} \\left( \\partial_t g_{ab} - \\beta^k\n * \\partial_k g_{ab} \\right)~,                                          \\\\\n * & \\Phi_{iab} = \\partial_i g_{ab}~,                                   \\\\\n * & \\Psi~,                                                             \\\\\n * & \\Pi = - \\dfrac{1}{\\alpha} \\left(\\partial_t \\Psi - \\beta^k\n * \\partial_k \\Psi \\right)~,                                            \\\\\n * & \\Phi_i = \\partial_i \\Psi~,\n * \\f}\n *\n * where \\f$ \\alpha \\f$ and \\f$ \\beta^k \\f$ are the lapse and shift.\n *\n * The computation of the evolution equations is implemented in each system in\n * gh::TimeDerivative and CurvedScalarWave::TimeDerivative, respectively. We\n * take the additional step of adding the contribution of the trace-reversed\n * stress-energy tensor to the evolution equations of the metric.\n *\n * \\note Although both systems are templated in the spatial dimension, we\n * only implement this system in three spatial dimensions.\n */\nstruct System {\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = 3;\n\n  using gh_system = gh::System<3_st>;\n  using scalar_system = CurvedScalarWave::System<3_st>;\n\n  using variables_tag = ::Tags::Variables<\n      tmpl::append<typename gh_system::variables_tag::tags_list,\n                   typename scalar_system::variables_tag::tags_list>>;\n\n  using flux_variables = tmpl::append<typename gh_system::flux_variables,\n                                      typename scalar_system::flux_variables>;\n\n  using gradient_variables =\n      tmpl::append<typename gh_system::gradient_variables,\n                   typename scalar_system::gradient_variables>;\n  using gradients_tags = gradient_variables;\n\n  static constexpr bool is_in_flux_conservative_form = false;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed<>;\n\n  using compute_volume_time_derivative_terms = ScalarTensor::TimeDerivative;\n  using inverse_spatial_metric_tag =\n      typename gh_system::inverse_spatial_metric_tag;\n};\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Constraints.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace ScalarTensor::OptionTags {\nstruct Group;\n}  // namespace ScalarTensor::OptionTags\n/// \\endcond\n\n/*!\n * \\brief Tags for the scalar tensor system.\n */\nnamespace ScalarTensor {\nnamespace Tags {\n/*!\n * \\brief Represents the trace-reversed stress-energy tensor of the scalar\n * field.\n */\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct TraceReversedStressEnergy : db::SimpleTag {\n  using type = tnsr::aa<DataType, Dim, Fr>;\n};\n\n/*!\n * \\brief Tag holding the source term of the scalar equation.\n *\n * \\details This tag hold the source term \\f$ \\mathcal{S} \\f$,\n * entering a wave equation of the form\n * \\f[\n *   \\Box \\Psi = \\mathcal{S} ~.\n * \\f]\n */\nstruct ScalarSource : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n}  // namespace Tags\n\nnamespace OptionTags {\n/*!\n * \\brief Scalar mass parameter.\n */\nstruct ScalarMass {\n  static std::string name() { return \"ScalarMass\"; }\n  using type = double;\n  static constexpr Options::String help{\n      \"Mass of the scalar field in code units\"};\n  using group = ::ScalarTensor::OptionTags::Group;\n};\n\n/*!\n * \\ingroup OptionGroupsGroup\n * Groups option tags related to the ScalarTensor evolution system.\n */\nstruct Group {\n  static std::string name() { return \"ScalarTensor\"; }\n  static constexpr Options::String help{\n      \"Options for the ScalarTensor evolution system\"};\n  using group = evolution::OptionTags::SystemGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\nstruct ScalarMass : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::ScalarMass>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double mass_psi) { return mass_psi; }\n};\n\n/*!\n * \\brief Prefix tag to avoid ambiguities when observing variables with the same\n * name in both parent systems.\n * \\note Since we also add compute tags for these quantities, we do not make\n * this a derived class of `Tag`. Otherwise, we would have tags with repeated\n * base tags in the `ObservationBox`.\n */\ntemplate <typename Tag>\nstruct Csw : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/*!\n * \\brief Compute tag to retrieve the values of CurvedScalarWave variables\n * with the same name as in the ::gh systems.\n * \\note Since we also add compute tags for these quantities, we do not make\n * this a derived class of `Tag`. Otherwise, we would have tags with repeated\n * base tags in the `ObservationBox`.\n */\ntemplate <typename Tag>\nstruct CswCompute : Csw<Tag>, db::ComputeTag {\n  using argument_tags = tmpl::list<Tag>;\n  using base = Csw<Tag>;\n  using return_type = typename base::type;\n  static constexpr void function(const gsl::not_null<return_type*> result,\n                                 const return_type& csw_var) {\n    for (size_t i = 0; i < csw_var.size(); ++i) {\n      make_const_view(make_not_null(&std::as_const((*result)[i])), csw_var[i],\n                      0, csw_var[i].size());\n    }\n  }\n};\n\n/*!\n * \\brief Computes the scalar-wave one-index constraint.\n * \\details The one-index constraint is assigned to a wrapped tag to avoid\n * clashes with the ::gh constraints during observation.\n * \\note We do not use ScalarTensor::Tags::CswCompute to retrieve the\n * CurvedScalarWave constraints since we do not want to add the bare compute\n * tags (::CurvedScalarWave::Tags::OneIndexConstraintCompute and\n * ::CurvedScalarWave::Tags::TwoIndexConstraintCompute) directly in the\n * ObservationBox, since that will make them observable and would lead to a\n * clash with the ::gh constraint tags.\n */\ntemplate <size_t Dim>\nstruct CswOneIndexConstraintCompute\n    : Csw<CurvedScalarWave::Tags::OneIndexConstraint<Dim>>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<Dim>,\n                               Frame::Inertial>,\n                 CurvedScalarWave::Tags::Phi<Dim>>;\n  using return_type = tnsr::i<DataVector, Dim>;\n  static constexpr void (*function)(const gsl::not_null<return_type*> result,\n                                    const tnsr::i<DataVector, Dim>&,\n                                    const tnsr::i<DataVector, Dim>&) =\n      &CurvedScalarWave::one_index_constraint<Dim>;\n  using base = Csw<CurvedScalarWave::Tags::OneIndexConstraint<Dim>>;\n};\n\n/*!\n * \\brief Computes the scalar-wave two-index constraint.\n * \\details The two-index constraint is assigned to a wrapped tag to avoid\n * clashes with the ::gh constraints during observation.\n * \\note We do not use ScalarTensor::Tags::CswCompute to retrieve the\n * CurvedScalarWave constraints since we do not want to add the bare compute\n * tags (::CurvedScalarWave::Tags::OneIndexConstraintCompute and\n * ::CurvedScalarWave::Tags::TwoIndexConstraintCompute) directly in the\n * ObservationBox, since that will make them observable and would lead to a\n * clash with the ::gh constraint tags.\n */\ntemplate <size_t Dim>\nstruct CswTwoIndexConstraintCompute\n    : Csw<CurvedScalarWave::Tags::TwoIndexConstraint<Dim>>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::Tags::deriv<CurvedScalarWave::Tags::Phi<Dim>,\n                               tmpl::size_t<Dim>, Frame::Inertial>>;\n  using return_type = tnsr::ij<DataVector, Dim>;\n  static constexpr void (*function)(const gsl::not_null<return_type*> result,\n                                    const tnsr::ij<DataVector, Dim>&) =\n      &CurvedScalarWave::two_index_constraint<Dim>;\n  using base = Csw<CurvedScalarWave::Tags::TwoIndexConstraint<Dim>>;\n};\n\n}  // namespace Tags\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarTensor/TimeDerivative.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.tpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GhScalarTensor/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarSource.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/StressEnergy.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor {\nevolution::dg::TimeDerivativeDecisions<3> TimeDerivative::apply(\n    // GH dt variables\n    const gsl::not_null<tnsr::aa<DataVector, dim>*> dt_spacetime_metric,\n    const gsl::not_null<tnsr::aa<DataVector, dim>*> dt_pi,\n    const gsl::not_null<tnsr::iaa<DataVector, dim>*> dt_phi,\n    // Scalar dt variables\n    const gsl::not_null<Scalar<DataVector>*> dt_psi_scalar,\n    const gsl::not_null<Scalar<DataVector>*> dt_pi_scalar,\n    const gsl::not_null<tnsr::i<DataVector, dim, Frame::Inertial>*>\n        dt_phi_scalar,\n\n    // GH temporal variables\n    const gsl::not_null<Scalar<DataVector>*> temp_gamma1,\n    const gsl::not_null<Scalar<DataVector>*> temp_gamma2,\n    const gsl::not_null<tnsr::a<DataVector, dim>*> temp_gauge_function,\n    const gsl::not_null<tnsr::ab<DataVector, dim>*>\n        temp_spacetime_deriv_gauge_function,\n    const gsl::not_null<Scalar<DataVector>*> gamma1gamma2,\n    const gsl::not_null<Scalar<DataVector>*> half_half_pi_two_normals,\n    const gsl::not_null<Scalar<DataVector>*> normal_dot_gauge_constraint,\n    const gsl::not_null<Scalar<DataVector>*> gamma1_plus_1,\n    const gsl::not_null<tnsr::a<DataVector, dim>*> pi_one_normal,\n    const gsl::not_null<tnsr::a<DataVector, dim>*> gauge_constraint,\n    const gsl::not_null<tnsr::i<DataVector, dim>*> half_phi_two_normals,\n    const gsl::not_null<tnsr::aa<DataVector, dim>*>\n        shift_dot_three_index_constraint,\n    const gsl::not_null<tnsr::aa<DataVector, dim>*>\n        mesh_velocity_dot_three_index_constraint,\n    const gsl::not_null<tnsr::ia<DataVector, dim>*> phi_one_normal,\n    const gsl::not_null<tnsr::aB<DataVector, dim>*> pi_2_up,\n    const gsl::not_null<tnsr::iaa<DataVector, dim>*> three_index_constraint,\n    const gsl::not_null<tnsr::Iaa<DataVector, dim>*> phi_1_up,\n    const gsl::not_null<tnsr::iaB<DataVector, dim>*> phi_3_up,\n    const gsl::not_null<tnsr::abC<DataVector, dim>*>\n        christoffel_first_kind_3_up,\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<tnsr::I<DataVector, dim>*> shift,\n    const gsl::not_null<tnsr::II<DataVector, dim>*> inverse_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> det_spatial_metric,\n    const gsl::not_null<Scalar<DataVector>*> sqrt_det_spatial_metric,\n    const gsl::not_null<tnsr::AA<DataVector, dim>*> inverse_spacetime_metric,\n    const gsl::not_null<tnsr::abb<DataVector, dim>*> christoffel_first_kind,\n    const gsl::not_null<tnsr::Abb<DataVector, dim>*> christoffel_second_kind,\n    const gsl::not_null<tnsr::a<DataVector, dim>*> trace_christoffel,\n    const gsl::not_null<tnsr::A<DataVector, dim>*> normal_spacetime_vector,\n\n    // Scalar temporal variables\n    const gsl::not_null<Scalar<DataVector>*> result_gamma1_scalar,\n    const gsl::not_null<Scalar<DataVector>*> result_gamma2_scalar,\n\n    // Extra temporal tags\n    const gsl::not_null<tnsr::aa<DataVector, dim>*> stress_energy,\n\n    // GH spatial derivatives\n    const tnsr::iaa<DataVector, dim>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, dim>& d_pi,\n    const tnsr::ijaa<DataVector, dim>& d_phi,\n\n    // scalar spatial derivatives\n    const tnsr::i<DataVector, dim>& d_psi_scalar,\n    const tnsr::i<DataVector, dim>& d_pi_scalar,\n    const tnsr::ij<DataVector, dim>& d_phi_scalar,\n\n    // GH argument variables\n    const tnsr::aa<DataVector, dim>& spacetime_metric,\n    const tnsr::aa<DataVector, dim>& pi, const tnsr::iaa<DataVector, dim>& phi,\n    const Scalar<DataVector>& gamma0, const Scalar<DataVector>& gamma1,\n    const Scalar<DataVector>& gamma2,\n    const gh::gauges::GaugeCondition& gauge_condition, const Mesh<dim>& mesh,\n    double time,\n    const tnsr::I<DataVector, dim, Frame::Inertial>& inertial_coords,\n    const InverseJacobian<DataVector, dim, Frame::ElementLogical,\n                          Frame::Inertial>& inverse_jacobian,\n    const std::optional<tnsr::I<DataVector, dim, Frame::Inertial>>&\n        mesh_velocity,\n\n    // Scalar argument variables\n    const Scalar<DataVector>& pi_scalar,\n    const tnsr::i<DataVector, dim>& phi_scalar,\n    const Scalar<DataVector>& lapse_scalar,\n    const tnsr::I<DataVector, dim>& shift_scalar,\n    const tnsr::i<DataVector, dim>& deriv_lapse,\n    const tnsr::iJ<DataVector, dim>& deriv_shift,\n    const tnsr::II<DataVector, dim>& upper_spatial_metric,\n    const tnsr::I<DataVector, dim>& trace_spatial_christoffel,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>& gamma1_scalar,\n    const Scalar<DataVector>& gamma2_scalar,\n\n    const Scalar<DataVector>& scalar_source) {\n  // Compute sourceless part of the RHS of the metric equations\n  gh::TimeDerivative<gh::ScalarTensor::AnalyticData::all_analytic_data, dim>::\n      apply(\n          // GH dt variables\n          dt_spacetime_metric, dt_pi, dt_phi,\n\n          // GH temporal variables\n          temp_gamma1, temp_gamma2, temp_gauge_function,\n          temp_spacetime_deriv_gauge_function, gamma1gamma2,\n          half_half_pi_two_normals, normal_dot_gauge_constraint, gamma1_plus_1,\n          pi_one_normal, gauge_constraint, half_phi_two_normals,\n          shift_dot_three_index_constraint,\n          mesh_velocity_dot_three_index_constraint, phi_one_normal, pi_2_up,\n          three_index_constraint, phi_1_up, phi_3_up,\n          christoffel_first_kind_3_up, lapse, shift, inverse_spatial_metric,\n          det_spatial_metric, sqrt_det_spatial_metric, inverse_spacetime_metric,\n          christoffel_first_kind, christoffel_second_kind, trace_christoffel,\n          normal_spacetime_vector,\n\n          // GH argument variables\n          d_spacetime_metric, d_pi, d_phi, spacetime_metric, pi, phi, gamma0,\n          gamma1, gamma2, gauge_condition, mesh, time, inertial_coords,\n          inverse_jacobian, mesh_velocity);\n\n  // Compute sourceless part of the RHS of the scalar equation\n  CurvedScalarWave::TimeDerivative<dim>::apply(\n      // Scalar dt variables\n      dt_psi_scalar, dt_pi_scalar, dt_phi_scalar,\n\n      // Scalar temporal variables\n      lapse, shift, inverse_spatial_metric,\n\n      result_gamma1_scalar, result_gamma2_scalar,\n\n      // Scalar argument variables\n      d_psi_scalar, d_pi_scalar, d_phi_scalar, pi_scalar, phi_scalar,\n\n      lapse_scalar, shift_scalar,\n\n      deriv_lapse, deriv_shift, upper_spatial_metric, trace_spatial_christoffel,\n      trace_extrinsic_curvature, gamma1_scalar, gamma2_scalar);\n\n  // Compute the (trace-reversed) stress energy tensor here\n  trace_reversed_stress_energy(stress_energy, pi_scalar, phi_scalar,\n                               lapse_scalar, shift_scalar);\n\n  if constexpr (backreaction_is_enabled) {\n    add_stress_energy_term_to_dt_pi(dt_pi, *stress_energy, lapse_scalar);\n  }\n\n  add_scalar_source_to_dt_pi_scalar(dt_pi_scalar, scalar_source, lapse_scalar);\n  return {true};\n}\n}  // namespace ScalarTensor\n\ntemplate struct gh::TimeDerivative<\n    gh::ScalarTensor::AnalyticData::all_analytic_data, 3>;\n\ntemplate class gh::gauges::Tags::GaugeAndDerivativeCompute<\n    3, gh::ScalarTensor::AnalyticData::all_analytic_data>;\n\ntemplate class gh::gauges::SetPiAndPhiFromConstraints<\n    gh::ScalarTensor::AnalyticData::all_analytic_data, 3>;\n\nnamespace gh::gauges {\n#define GH_GAUGE_DISPATCH_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GH_GAUGE_DISPATCH_SOLUTION(data) \\\n  gh::ScalarTensor::AnalyticData::all_analytic_data\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_GH_GAUGE_DISPATCH, (3))\n\n#undef GH_GAUGE_DISPATCH_SOLUTION\n#undef GH_GAUGE_DISPATCH_DIM\n}  // namespace gh::gauges\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarTensor/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GhScalarTensor/Factory.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarSource.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/StressEnergy.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor {\n/*!\n * \\brief Compute the RHS terms of the evolution equations for the scalar tensor\n * system.\n *\n * \\details The bulk of the computations in this class dispatch to\n * `GeneralizedHarmonic::TimeDerivative` and\n * `CurvedScalarWave::TimeDerivative` as a 'product system' -- each\n * independently operating on its own subset of the supplied variable\n * collections.\n * The additional steps taken are to compute the trace-reversed stress energy\n * tensor associated with the scalar part of the system and add its contribution\n * to the \\f$\\partial_t \\Pi_{a b}\\f$ variable in the Generalized Harmonic\n * system, as well as adding any scalar sources to the variable \\f$\\partial_t\n * \\Pi\\f$.\n *\n * \\warning Backreaction of the scalar field onto the metric is currently\n * disabled and the system is evolved in the test-field (or decoupling)\n * approximation.\n */\nstruct TimeDerivative {\n  static constexpr size_t dim = 3;\n  static constexpr bool backreaction_is_enabled = false;\n  using gh_dt_tags =\n      db::wrap_tags_in<::Tags::dt,\n                       typename gh::System<dim>::variables_tag::tags_list>;\n  using scalar_dt_tags = db::wrap_tags_in<\n      ::Tags::dt,\n      typename CurvedScalarWave::System<dim>::variables_tag::tags_list>;\n  using dt_tags = tmpl::append<gh_dt_tags, scalar_dt_tags>;\n  using gh_temp_tags = typename gh::TimeDerivative<\n      gh::ScalarTensor::AnalyticData::all_analytic_data, dim>::temporary_tags;\n  using gh_gradient_tags = typename gh::System<dim>::gradients_tags;\n  using gh_arg_tags = typename gh::TimeDerivative<\n      gh::ScalarTensor::AnalyticData::all_analytic_data, dim>::argument_tags;\n  using scalar_temp_tags =\n      typename CurvedScalarWave::TimeDerivative<dim>::temporary_tags;\n  using scalar_extra_temp_tags =\n      tmpl::list<ScalarTensor::Tags::TraceReversedStressEnergy<\n          DataVector, dim, ::Frame::Inertial>>;\n  using scalar_gradient_tags =\n      typename CurvedScalarWave::System<dim>::gradients_tags;\n  using gradient_tags = tmpl::append<gh_gradient_tags, scalar_gradient_tags>;\n  using scalar_arg_tags =\n      typename CurvedScalarWave::TimeDerivative<dim>::argument_tags;\n  using temporary_tags = tmpl::remove_duplicates<\n      tmpl::append<gh_temp_tags, scalar_temp_tags, scalar_extra_temp_tags>>;\n  using argument_tags =\n      tmpl::append<gh_arg_tags, scalar_arg_tags,\n                   tmpl::list<ScalarTensor::Tags::ScalarSource>>;\n\n  static evolution::dg::TimeDerivativeDecisions<3> apply(\n      // GH dt variables\n      gsl::not_null<tnsr::aa<DataVector, dim>*> dt_spacetime_metric,\n      gsl::not_null<tnsr::aa<DataVector, dim>*> dt_pi,\n      gsl::not_null<tnsr::iaa<DataVector, dim>*> dt_phi,\n      // Scalar dt variables\n      gsl::not_null<Scalar<DataVector>*> dt_psi_scalar,\n      gsl::not_null<Scalar<DataVector>*> dt_pi_scalar,\n      gsl::not_null<tnsr::i<DataVector, dim, Frame::Inertial>*> dt_phi_scalar,\n\n      // GH temporal variables\n      gsl::not_null<Scalar<DataVector>*> temp_gamma1,\n      gsl::not_null<Scalar<DataVector>*> temp_gamma2,\n      gsl::not_null<tnsr::a<DataVector, dim>*> temp_gauge_function,\n      gsl::not_null<tnsr::ab<DataVector, dim>*>\n          temp_spacetime_deriv_gauge_function,\n      gsl::not_null<Scalar<DataVector>*> gamma1gamma2,\n      gsl::not_null<Scalar<DataVector>*> half_half_pi_two_normals,\n      gsl::not_null<Scalar<DataVector>*> normal_dot_gauge_constraint,\n      gsl::not_null<Scalar<DataVector>*> gamma1_plus_1,\n      gsl::not_null<tnsr::a<DataVector, dim>*> pi_one_normal,\n      gsl::not_null<tnsr::a<DataVector, dim>*> gauge_constraint,\n      gsl::not_null<tnsr::i<DataVector, dim>*> half_phi_two_normals,\n      gsl::not_null<tnsr::aa<DataVector, dim>*>\n          shift_dot_three_index_constraint,\n      gsl::not_null<tnsr::aa<DataVector, dim>*>\n          mesh_velocity_dot_three_index_constraint,\n      gsl::not_null<tnsr::ia<DataVector, dim>*> phi_one_normal,\n      gsl::not_null<tnsr::aB<DataVector, dim>*> pi_2_up,\n      gsl::not_null<tnsr::iaa<DataVector, dim>*> three_index_constraint,\n      gsl::not_null<tnsr::Iaa<DataVector, dim>*> phi_1_up,\n      gsl::not_null<tnsr::iaB<DataVector, dim>*> phi_3_up,\n      gsl::not_null<tnsr::abC<DataVector, dim>*> christoffel_first_kind_3_up,\n      gsl::not_null<Scalar<DataVector>*> lapse,\n      gsl::not_null<tnsr::I<DataVector, dim>*> shift,\n      gsl::not_null<tnsr::II<DataVector, dim>*> inverse_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> det_spatial_metric,\n      gsl::not_null<Scalar<DataVector>*> sqrt_det_spatial_metric,\n      gsl::not_null<tnsr::AA<DataVector, dim>*> inverse_spacetime_metric,\n      gsl::not_null<tnsr::abb<DataVector, dim>*> christoffel_first_kind,\n      gsl::not_null<tnsr::Abb<DataVector, dim>*> christoffel_second_kind,\n      gsl::not_null<tnsr::a<DataVector, dim>*> trace_christoffel,\n      gsl::not_null<tnsr::A<DataVector, dim>*> normal_spacetime_vector,\n\n      // Scalar temporal variables\n      gsl::not_null<Scalar<DataVector>*> result_gamma1_scalar,\n      gsl::not_null<Scalar<DataVector>*> result_gamma2_scalar,\n\n      // Extra temporal tags\n      gsl::not_null<tnsr::aa<DataVector, dim>*> stress_energy,\n\n      // GH spatial derivatives\n      const tnsr::iaa<DataVector, dim>& d_spacetime_metric,\n      const tnsr::iaa<DataVector, dim>& d_pi,\n      const tnsr::ijaa<DataVector, dim>& d_phi,\n\n      // scalar spatial derivatives\n      const tnsr::i<DataVector, dim>& d_psi_scalar,\n      const tnsr::i<DataVector, dim>& d_pi_scalar,\n      const tnsr::ij<DataVector, dim>& d_phi_scalar,\n\n      // GH argument variables\n      const tnsr::aa<DataVector, dim>& spacetime_metric,\n      const tnsr::aa<DataVector, dim>& pi,\n      const tnsr::iaa<DataVector, dim>& phi, const Scalar<DataVector>& gamma0,\n      const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n      const gh::gauges::GaugeCondition& gauge_condition, const Mesh<dim>& mesh,\n      double time,\n      const tnsr::I<DataVector, dim, Frame::Inertial>& inertial_coords,\n      const InverseJacobian<DataVector, dim, Frame::ElementLogical,\n                            Frame::Inertial>& inverse_jacobian,\n      const std::optional<tnsr::I<DataVector, dim, Frame::Inertial>>&\n          mesh_velocity,\n\n      // Scalar argument variables\n      const Scalar<DataVector>& pi_scalar,\n      const tnsr::i<DataVector, dim>& phi_scalar,\n      const Scalar<DataVector>& lapse_scalar,\n      const tnsr::I<DataVector, dim>& shift_scalar,\n      const tnsr::i<DataVector, dim>& deriv_lapse,\n      const tnsr::iJ<DataVector, dim>& deriv_shift,\n      const tnsr::II<DataVector, dim>& upper_spatial_metric,\n      const tnsr::I<DataVector, dim>& trace_spatial_christoffel,\n      const Scalar<DataVector>& trace_extrinsic_curvature,\n      const Scalar<DataVector>& gamma1_scalar,\n      const Scalar<DataVector>& gamma2_scalar,\n\n      const Scalar<DataVector>& scalar_source);\n};\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarWave::BoundaryConditions {\ntemplate <size_t Dim>\nBoundaryCondition<Dim>::BoundaryCondition(CkMigrateMessage* const msg)\n    : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\ntemplate <size_t Dim>\nvoid BoundaryCondition<Dim>::pup(PUP::er& p) {\n  domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class BoundaryCondition<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace ScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace ScalarWave {\n/// \\brief Boundary conditions for the scalar wave system\nnamespace BoundaryConditions {\n/// \\brief The base class off of which all boundary conditions must inherit\ntemplate <size_t Dim>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n};\n}  // namespace BoundaryConditions\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  ConstraintPreservingSphericalRadiation.cpp\n  DirichletAnalytic.cpp\n  SphericalRadiation.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BoundaryCondition.hpp\n  ConstraintPreservingSphericalRadiation.hpp\n  DirichletAnalytic.hpp\n  Factory.hpp\n  SphericalRadiation.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarWave::BoundaryConditions {\nnamespace detail {\nConstraintPreservingSphericalRadiationType\nconvert_constraint_preserving_spherical_radiation_type_from_yaml(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (\"Sommerfeld\" == type_read) {\n    return ConstraintPreservingSphericalRadiationType::Sommerfeld;\n  } else if (\"FirstOrderBaylissTurkel\" == type_read) {\n    return ConstraintPreservingSphericalRadiationType::FirstOrderBaylissTurkel;\n  } else if (\"SecondOrderBaylissTurkel\" == type_read) {\n    return ConstraintPreservingSphericalRadiationType::SecondOrderBaylissTurkel;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read\n                  << \"\\\" to ConstraintPreservingSphericalRadiation::Type. Must \"\n                     \"be one of Sommerfeld, FirstOrderBaylissTurkel, or \"\n                     \"SecondOrderBaylissTurkel.\");\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nConstraintPreservingSphericalRadiation<Dim>::\n    ConstraintPreservingSphericalRadiation(\n        const detail::ConstraintPreservingSphericalRadiationType type)\n    : type_(type) {}\n\ntemplate <size_t Dim>\nConstraintPreservingSphericalRadiation<\n    Dim>::ConstraintPreservingSphericalRadiation(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nConstraintPreservingSphericalRadiation<Dim>::get_clone() const {\n  return std::make_unique<ConstraintPreservingSphericalRadiation>(*this);\n}\n\ntemplate <size_t Dim>\nvoid ConstraintPreservingSphericalRadiation<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n  p | type_;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string>\nConstraintPreservingSphericalRadiation<Dim>::dg_time_derivative(\n    const gsl::not_null<Scalar<DataVector>*> dt_psi_correction,\n    const gsl::not_null<Scalar<DataVector>*> dt_pi_correction,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        dt_phi_correction,\n\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n\n    const Scalar<DataVector>& psi, const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const Scalar<DataVector>& gamma2,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& d_pi,\n    const tnsr::ij<DataVector, Dim, Frame::Inertial>& d_phi) const {\n  {\n    // The first contribution to dt_pi_correction is the negative of volume\n    // inertial dt Pi, where we have used the evolution equation to replace time\n    // derivatives by space derivatives.\n    get(*dt_pi_correction) = get<0, 0>(d_phi);\n    for (size_t i = 1; i < Dim; ++i) {\n      get(*dt_pi_correction) += d_phi.get(i, i);\n    }\n\n    // the dt_pi that would be passed in is actually the logical time\n    // derivative, and so we instead just compute the time derivatives with\n    // respect to the inertial time locally since the flat scalar wave system is\n    // quite simple.\n    const Scalar<DataVector> mesh_velocity_dot_d_pi =\n        face_mesh_velocity.has_value()\n            ? dot_product(face_mesh_velocity.value(), d_pi)\n            : Scalar<DataVector>{get(pi).size(), 0.0};\n    if (type_ ==\n        detail::ConstraintPreservingSphericalRadiationType::Sommerfeld) {\n      for (size_t i = 0; i < Dim; ++i) {\n        get(*dt_pi_correction) +=\n            normal_covector.get(i) *\n            (get(gamma2) * (d_psi.get(i) - phi.get(i)) - d_pi.get(i));\n      }\n    } else {\n      DataVector& inv_radius = get(*dt_psi_correction);\n      magnitude(dt_psi_correction, coords);\n      inv_radius = 1.0 / inv_radius;\n\n      if (type_ == detail::ConstraintPreservingSphericalRadiationType::\n                       FirstOrderBaylissTurkel) {\n        // dt_psi=-Pi, replace directly then we don't need to worry about\n        // logical vs. inertial time derivatives.\n        get(*dt_pi_correction) -= inv_radius * get(pi);\n        for (size_t i = 0; i < Dim; ++i) {\n          get(*dt_pi_correction) +=\n              normal_covector.get(i) *\n              (get(gamma2) * (d_psi.get(i) - phi.get(i)) - d_pi.get(i));\n        }\n      } else {\n        // second-order Bayliss-Turkel\n        get(*dt_pi_correction) -=\n            2.0 * inv_radius * (2.0 * get(pi) - inv_radius * get(psi));\n        for (size_t i = 0; i < Dim; ++i) {\n          // Note: here we are handling `dt dr Psi` as `n^i dt Phi_i` and\n          // `dr dt Psi` as `-n^i d_i Pi`. The only thing we are assuming is\n          // that `d_r` is time-independent. This is why we have `n^i (dt Phi_i\n          // - d_i Pi)` instead of `2 n^i dt Phi_i`.\n          //\n          // We can also replace `dt_phi_i - d_i Pi` with\n          //  `-2.0 d_i Pi + gamma_2 (d_i Psi - Phi_i)`\n          get(*dt_pi_correction) +=\n              normal_covector.get(i) *\n              (get(gamma2) * (d_psi.get(i) - phi.get(i)) - 2.0 * d_pi.get(i) +\n               4.0 * inv_radius * phi.get(i));\n          for (size_t j = 0; j < Dim; ++j) {\n            get(*dt_pi_correction) += normal_covector.get(i) *\n                                      normal_covector.get(j) * d_phi.get(i, j);\n          }\n        }\n      }\n    }\n  }\n  if (face_mesh_velocity.has_value()) {\n    // Compute dt psi correction\n    get(*dt_psi_correction) =\n        get<0>(normal_covector) * (get<0>(d_psi) - get<0>(phi));\n    for (size_t i = 1; i < Dim; ++i) {\n      get(*dt_psi_correction) +=\n          normal_covector.get(i) * (d_psi.get(i) - phi.get(i));\n    }\n    const auto negative_lambda0 =\n        dot_product(normal_covector, *face_mesh_velocity);\n    get(*dt_psi_correction) *= -get(negative_lambda0);\n    get(*dt_pi_correction) += get(gamma2) * get(*dt_psi_correction);\n\n    // Compute dt Phi 2-index constraint correction\n    for (size_t i = 0; i < Dim; ++i) {\n      dt_phi_correction->get(i) =\n          get<0>(normal_covector) * (d_phi.get(0, i) - d_phi.get(i, 0));\n      for (size_t j = 1; j < Dim; ++j) {\n        dt_phi_correction->get(i) +=\n            normal_covector.get(j) * (d_phi.get(j, i) - d_phi.get(i, j));\n      }\n      dt_phi_correction->get(i) *= -get(negative_lambda0);\n    }\n    if (min(-get(negative_lambda0)) < 0.0) {\n      return {\n          \"Incoming characteristic speeds for constraint preserving spherical \"\n          \"radiation boundary condition. It's unclear that proper boundary \"\n          \"conditions are imposed in this case. Please verify if you need this \"\n          \"feature.\"};\n    }\n  } else {\n    // Since the constraint-preserving terms are all multiplied by the\n    // characteristic speeds, when those speeds are zero there are no\n    // constraint-preserving terms to add.\n    get(*dt_psi_correction) = 0.0;\n    for (size_t i = 0; i < Dim; ++i) {\n      dt_phi_correction->get(i) = 0.0;\n    }\n  }\n  return {};\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID ConstraintPreservingSphericalRadiation<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class ConstraintPreservingSphericalRadiation<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace ScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace ScalarWave::BoundaryConditions {\nnamespace detail {\n/// The type of spherical radiation boundary condition to impose\nenum class ConstraintPreservingSphericalRadiationType {\n  /// Impose \\f$(\\partial_t + \\partial_r)\\Psi=0\\f$\n  Sommerfeld,\n  /// Impose \\f$(\\partial_t + \\partial_r + r^{-1})\\Psi=0\\f$\n  FirstOrderBaylissTurkel,\n  /// Imposes a second-order Bayliss-Turkel boundary condition\n  SecondOrderBaylissTurkel\n};\n\nConstraintPreservingSphericalRadiationType\nconvert_constraint_preserving_spherical_radiation_type_from_yaml(\n    const Options::Option& options);\n}  // namespace detail\n\n/*!\n * \\brief Constraint-preserving spherical radiation boundary condition that\n * seeks to avoid ingoing constraint violations and radiation.\n *\n * The constraint-preserving part of the boundary condition imposes the\n * following condition on the time derivatives of the characteristic fields:\n *\n * \\f{align*}{\n *   d_tw^\\Psi&\\to d_tw^{\\Psi}+\\lambda_{\\Psi}n^i\\mathcal{C}_i, \\\\\n *   d_tw^0_i&\\to d_tw^{0}_i+\\lambda_{0}n^jP^k_i\\mathcal{C}_{ik},\n * \\f}\n *\n * where\n *\n * \\f{align*}{\n * P^k{}_i=\\delta^k_i-n^kn_i\n * \\f}\n *\n * projects a tensor onto the spatial surface to which \\f$n_i\\f$ is normal, and\n * \\f$d_t w\\f$ is the evolved to characteristic field transformation applied to\n * the time derivatives of the evolved fields. That is,\n *\n * \\f{align*}{\n * d_t w^\\Psi&=\\partial_t \\Psi, \\\\\n * d_t w_i^0&=(\\delta^k_i-n^k n_i)\\partial_t \\Phi_k, \\\\\n * d_t w^{\\pm}&=\\partial_t\\Pi\\pm n^k\\partial_t\\Phi_k - \\gamma_2\\partial_t\\Psi.\n * \\f}\n *\n * The constraints are defined as:\n *\n * \\f{align*}{\n *  \\mathcal{C}_i&=\\partial_i\\Psi - \\Phi_i=0, \\\\\n *  \\mathcal{C}_{ij}&=\\partial_{[i}\\Phi_{j]}=0\n * \\f}\n *\n * Radiation boundary conditions impose a condition on \\f$\\Pi\\f$ or its time\n * derivative. We denote the boundary condition value of the time derivative of\n * \\f$\\Pi\\f$ by \\f$\\partial_t\\Pi^{\\mathrm{BC}}\\f$. With this, we can impose\n * boundary conditions on the time derivatives of the evolved variables as\n * follows:\n *\n * \\f{align*}{\n * \\partial_{t} \\Psi&\\to\\partial_{t}\\Psi +\n *                    \\lambda_\\Psi n^i \\mathcal{C}_i, \\\\\n * \\partial_{t}\\Pi&\\to\\partial_{t}\\Pi-\\left(\\partial_t\\Pi\n *                  - \\partial_t\\Pi^{\\mathrm{BC}}\\right)\n *                  +\\gamma_2\\lambda_\\Psi n^i \\mathcal{C}_i\n *                  =\\partial_t\\Pi^{\\mathrm{BC}}\n *                  +\\gamma_2\\lambda_\\Psi n^i \\mathcal{C}_i, \\\\\n * \\partial_{t}\\Phi_i&\\to\\partial_{t}\\Phi_i+ 2 \\lambda_0n^j \\mathcal{C}_{ji}.\n * \\f}\n *\n * Below we assume the normal vector \\f$n^i\\f$ is the radial unit normal vector.\n * That is, we assume the outer boundary is spherical. A Sommerfeld\n * \\cite Sommerfeld1949 radiation condition is given by\n *\n * \\f{align*}{\n *  \\partial_t\\Psi=n^i\\Phi_i\n * \\f}\n *\n * Or, assuming that \\f$\\partial_tn^i=0\\f$ (or is very small),\n *\n * \\f{align*}{\n *  \\partial_t\\Pi^{\\mathrm{BC}}=n^i\\partial_t\\Phi_i\n * \\f}\n *\n * The Bayliss-Turkel \\cite BaylissTurkel boundary conditions are given by:\n *\n * \\f{align*}{\n *  \\prod_{l=1}^m\\left(\\partial_t + \\partial_r + \\frac{2l-1}{r}\\right)\\Psi=0\n * \\f}\n *\n * The first-order form is\n *\n * \\f{align*}{\n *  \\partial_t\\Pi^{\\mathrm{BC}}=n^i\\partial_t\\Phi_i + \\frac{1}{r}\\partial_t\\Psi,\n * \\f}\n *\n * assuming \\f$\\partial_t n^i=0\\f$ and \\f$\\partial_t r=0\\f$.\n *\n * The second-order boundary condition is given by,\n *\n * \\f{align*}{\n *  \\partial_t\\Pi^{\\mathrm{BC}}\n *   &=\\left(\\partial_t\\partial_r + \\partial_r\\partial_t +\n *     \\partial_r^2+\\frac{4}{r}\\partial_t\n *     +\\frac{4}{r}\\partial_r + \\frac{2}{r^2}\\right)\\Psi \\notag \\\\\n *     &=n^i(\\partial_t\\Phi_i-\\partial_i\\Pi) + n^i n^j\\partial_i\\Phi_j -\n *     \\frac{4}{r}\\Pi+\\frac{4}{r}n^i\\Phi_i + \\frac{2}{r^2}\\Psi \\notag \\\\\n *     &=n^i(\\gamma_2(\\partial_i\\Psi - \\Phi_i)-2\\partial_i\\Pi)\n *     + n^i n^j\\partial_i\\Phi_j -\n *     \\frac{4}{r}\\Pi+\\frac{4}{r}n^i\\Phi_i + \\frac{2}{r^2}\\Psi,\n * \\f}\n *\n * where \\f$\\partial_t\\f$ is the derivative with respect to the inertial frame\n * time, and we are assuming \\f$\\partial_t n^i=0\\f$ and \\f$\\partial_t r=0\\f$.\n *\n * The moving mesh can be accounted for by using\n *\n * \\f{align*}{\n * \\partial_t r = \\frac{1}{r}x^i\\delta_{ij}\\partial_t x^j\n * \\f}\n *\n * \\note It is not clear if \\f$\\partial_t\\Phi_i\\f$ should be replaced by\n * \\f$-\\partial_i\\Pi\\f$, which is the evolution equation but without the\n * constraint.\n *\n * \\note On a moving mesh the characteristic speeds change according to\n * \\f$\\lambda\\to\\lambda-v^i_gn_i\\f$ where \\f$v^i_g\\f$ is the mesh velocity.\n *\n * \\note For the scalar wave system \\f$\\lambda_0 = \\lambda_\\psi\\f$\n *\n * \\warning The boundary conditions are implemented assuming the outer boundary\n * is spherical. It might be possible to generalize the condition to\n * non-spherical boundaries by using \\f$x^i/r\\f$ instead of \\f$n^i\\f$, but this\n * hasn't been tested.\n *\n * \\warning The received time derivatives are in the logical frame, not the\n * inertial frame and would need to first be corrected by subtracting the mesh\n * velocity. Similarly, the time derivative correction is in the logical frame.\n * We instead substitute the evolution equations directly in since the scalar\n * wave system is quite simple.\n */\ntemplate <size_t Dim>\nclass ConstraintPreservingSphericalRadiation final\n    : public BoundaryCondition<Dim> {\n public:\n  struct TypeOptionTag {\n    using type = detail::ConstraintPreservingSphericalRadiationType;\n    static std::string name() { return \"Type\"; }\n    static constexpr Options::String help{\n        \"Whether to impose Sommerfeld, first-order Bayliss-Turkel, or \"\n        \"second-order Bayliss-Turkel spherical radiation boundary conditions.\"};\n  };\n\n  using options = tmpl::list<TypeOptionTag>;\n  static constexpr Options::String help{\n      \"Constraint-preserving spherical radiation boundary conditions setting \"\n      \"the time derivatives of Psi, Phi, and Pi to avoid incoming constraint \"\n      \"violations, and imposing radiation boundary conditions.\"};\n\n  ConstraintPreservingSphericalRadiation(\n      detail::ConstraintPreservingSphericalRadiationType type);\n\n  ConstraintPreservingSphericalRadiation() = default;\n  /// \\cond\n  ConstraintPreservingSphericalRadiation(\n      ConstraintPreservingSphericalRadiation&&) = default;\n  ConstraintPreservingSphericalRadiation& operator=(\n      ConstraintPreservingSphericalRadiation&&) = default;\n  ConstraintPreservingSphericalRadiation(\n      const ConstraintPreservingSphericalRadiation&) = default;\n  ConstraintPreservingSphericalRadiation& operator=(\n      const ConstraintPreservingSphericalRadiation&) = default;\n  /// \\endcond\n  ~ConstraintPreservingSphericalRadiation() override = default;\n\n  explicit ConstraintPreservingSphericalRadiation(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition,\n      ConstraintPreservingSphericalRadiation);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::TimeDerivative;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                 ScalarWave::Tags::Phi<Dim>>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 Tags::ConstraintGamma2>;\n  // using dg_interior_dt_vars_tags = tmpl::list<>;\n  using dg_interior_deriv_vars_tags = tmpl::list<\n      ::Tags::deriv<ScalarWave::Tags::Psi, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::deriv<ScalarWave::Tags::Pi, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::deriv<ScalarWave::Tags::Phi<Dim>, tmpl::size_t<Dim>,\n                    Frame::Inertial>>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_time_derivative(\n      gsl::not_null<Scalar<DataVector>*> dt_psi_correction,\n      gsl::not_null<Scalar<DataVector>*> dt_pi_correction,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          dt_phi_correction,\n\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          face_mesh_velocity,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n\n      const Scalar<DataVector>& psi, const Scalar<DataVector>& pi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const Scalar<DataVector>& gamma2,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_psi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_pi,\n      const tnsr::ij<DataVector, Dim, Frame::Inertial>& d_phi) const;\n\n private:\n  detail::ConstraintPreservingSphericalRadiationType type_{\n      detail::ConstraintPreservingSphericalRadiationType::Sommerfeld};\n};\n}  // namespace ScalarWave::BoundaryConditions\n\ntemplate <>\nstruct Options::create_from_yaml<\n    ScalarWave::BoundaryConditions::detail::\n        ConstraintPreservingSphericalRadiationType> {\n  template <typename Metavariables>\n  static typename ScalarWave::BoundaryConditions::detail::\n      ConstraintPreservingSphericalRadiationType\n      create(const Options::Option& options) {\n    return ScalarWave::BoundaryConditions::detail::\n        convert_constraint_preserving_spherical_radiation_type_from_yaml(\n            options);\n  }\n};\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/DirichletAnalytic.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <type_traits>\n\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/Factory.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ScalarWave::BoundaryConditions {\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(const DirichletAnalytic& rhs)\n    : BoundaryCondition<Dim>{dynamic_cast<const BoundaryCondition<Dim>&>(rhs)},\n      analytic_prescription_(rhs.analytic_prescription_->get_clone()) {}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>& DirichletAnalytic<Dim>::operator=(\n    const DirichletAnalytic& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  analytic_prescription_ = rhs.analytic_prescription_->get_clone();\n  return *this;\n}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nDirichletAnalytic<Dim>::DirichletAnalytic(\n    std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription)\n    : analytic_prescription_(std::move(analytic_prescription)) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nDirichletAnalytic<Dim>::get_clone() const {\n  return std::make_unique<DirichletAnalytic>(*this);\n}\n\ntemplate <size_t Dim>\nvoid DirichletAnalytic<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n  p | analytic_prescription_;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string> DirichletAnalytic<Dim>::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> psi,\n    const gsl::not_null<Scalar<DataVector>*> pi,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> phi,\n    const gsl::not_null<Scalar<DataVector>*> gamma2,\n    const std::optional<\n        tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& /*normal_covector*/,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const Scalar<DataVector>& interior_gamma2,\n    [[maybe_unused]] const double time) const {\n  auto boundary_values = call_with_dynamic_type<\n      tuples::TaggedTuple<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                          ScalarWave::Tags::Phi<Dim>>,\n      tmpl::append<ScalarWave::Solutions::all_solutions<Dim>>>(\n      analytic_prescription_.get(),\n      [&coords, &time](const auto* const analytic_solution_or_data) {\n        if constexpr (is_analytic_solution_v<\n                          std::decay_t<decltype(*analytic_solution_or_data)>>) {\n          return analytic_solution_or_data->variables(\n              coords, time,\n              tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                         ScalarWave::Tags::Phi<Dim>>{});\n\n        } else {\n          (void)time;\n          return analytic_solution_or_data->variables(\n              coords, tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                 ScalarWave::Tags::Phi<Dim>>{});\n        }\n      });\n  *gamma2 = interior_gamma2;\n  *psi = get<ScalarWave::Tags::Psi>(boundary_values);\n  *pi = get<ScalarWave::Tags::Pi>(boundary_values);\n  *phi = get<ScalarWave::Tags::Phi<Dim>>(boundary_values);\n  return std::nullopt;\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DirichletAnalytic<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class DirichletAnalytic<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace ScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/DirichletAnalytic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace ScalarWave::BoundaryConditions {\n/*!\n * \\brief Sets Dirichlet boundary conditions using the analytic solution or\n * analytic data.\n */\ntemplate <size_t Dim>\nclass DirichletAnalytic final : public BoundaryCondition<Dim> {\n public:\n  /// \\brief What analytic solution/data to prescribe.\n  struct AnalyticPrescription {\n    static constexpr Options::String help =\n        \"What analytic solution/data to prescribe.\";\n    using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  };\n\n  using options = tmpl::list<AnalyticPrescription>;\n\n  static constexpr Options::String help{\n      \"DirichletAnalytic boundary conditions setting the value of Psi, Phi, \"\n      \"and Pi to the analytic solution or analytic data.\"};\n\n  DirichletAnalytic() = default;\n  DirichletAnalytic(DirichletAnalytic&&) = default;\n  DirichletAnalytic& operator=(DirichletAnalytic&&) = default;\n  DirichletAnalytic(const DirichletAnalytic&);\n  DirichletAnalytic& operator=(const DirichletAnalytic&);\n  ~DirichletAnalytic() override = default;\n\n  explicit DirichletAnalytic(\n      std::unique_ptr<evolution::initial_data::InitialData>\n          analytic_prescription);\n\n  explicit DirichletAnalytic(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DirichletAnalytic);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 Tags::ConstraintGamma2>;\n  using dg_gridless_tags = tmpl::list<::Tags::Time>;\n\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> psi,\n      const gsl::not_null<Scalar<DataVector>*> pi,\n      const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> phi,\n      const gsl::not_null<Scalar<DataVector>*> gamma2,\n      const std::optional<\n          tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& /*normal_covector*/,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const Scalar<DataVector>& interior_gamma2,\n      [[maybe_unused]] const double time) const;\n\n private:\n  std::unique_ptr<evolution::initial_data::InitialData> analytic_prescription_;\n};\n}  // namespace ScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/SphericalRadiation.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarWave::BoundaryConditions {\n/// Typelist of standard BoundaryConditions\ntemplate <size_t Dim>\nusing standard_boundary_conditions =\n    tmpl::list<ConstraintPreservingSphericalRadiation<Dim>,\n               DirichletAnalytic<Dim>,\n               domain::BoundaryConditions::Periodic<BoundaryCondition<Dim>>,\n               SphericalRadiation<Dim>>;\n}  // namespace ScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/SphericalRadiation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/SphericalRadiation.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarWave::BoundaryConditions {\nnamespace detail {\nSphericalRadiationType convert_spherical_radiation_type_from_yaml(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (\"Sommerfeld\" == type_read) {\n    return SphericalRadiationType::Sommerfeld;\n  } else if (\"BaylissTurkel\" == type_read) {\n    return SphericalRadiationType::BaylissTurkel;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read\n                  << \"\\\" to SphericalRadiation::Type. Must be one of \"\n                     \"Sommerfeld, or BaylissTurkel.\");\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nSphericalRadiation<Dim>::SphericalRadiation(\n    const detail::SphericalRadiationType type)\n    : type_(type) {}\n\ntemplate <size_t Dim>\nSphericalRadiation<Dim>::SphericalRadiation(CkMigrateMessage* const msg)\n    : BoundaryCondition<Dim>(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\nSphericalRadiation<Dim>::get_clone() const {\n  return std::make_unique<SphericalRadiation>(*this);\n}\n\ntemplate <size_t Dim>\nvoid SphericalRadiation<Dim>::pup(PUP::er& p) {\n  BoundaryCondition<Dim>::pup(p);\n  p | type_;\n}\n\ntemplate <size_t Dim>\nstd::optional<std::string> SphericalRadiation<Dim>::dg_ghost(\n    const gsl::not_null<Scalar<DataVector>*> psi_ext,\n    const gsl::not_null<Scalar<DataVector>*> pi_ext,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> phi_ext,\n    const gsl::not_null<Scalar<DataVector>*> gamma2_ext,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const Scalar<DataVector>& psi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const Scalar<DataVector>& gamma2) const {\n  *gamma2_ext = gamma2;\n  get(*pi_ext) = get<0>(normal_covector) * get<0>(phi);\n  for (size_t i = 1; i < Dim; ++i) {\n    get(*pi_ext) += normal_covector.get(i) * phi.get(i);\n  }\n\n  if (type_ == detail::SphericalRadiationType::BaylissTurkel) {\n    // computed radius, storing int psi_ext\n    DataVector& radius = get(*psi_ext);\n    radius = square(get<0>(coords));\n    for (size_t i = 1; i < Dim; ++i) {\n      radius += square(coords.get(i));\n    }\n    radius = sqrt(radius);\n    get(*pi_ext) += get(psi) / radius;\n  }\n\n  // Spherical radiation on a spherical boundary means no changes in Phi and\n  // Psi. These would ideally be controlled by a constraint-preserving boundary\n  // condition.\n  *phi_ext = phi;\n  get(*psi_ext) = get(psi);\n\n  if (face_mesh_velocity.has_value()) {\n    const Scalar<DataVector> char_speed =\n        dot_product(*face_mesh_velocity, normal_covector);\n    if (min(-get(char_speed)) < 0.0) {\n      return {\n          \"Incoming characteristic speeds for spherical radiation boundary \"\n          \"condition. It's unclear that proper boundary conditions are imposed \"\n          \"in this case. Please verify if you need this feature.\"};\n    }\n  }\n  return {};\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID SphericalRadiation<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) template class SphericalRadiation<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace ScalarWave::BoundaryConditions\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryConditions/SphericalRadiation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\nnamespace Options {\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace ScalarWave::BoundaryConditions {\nnamespace detail {\n/// The type of spherical radiation boundary condition to impose\nenum class SphericalRadiationType {\n  /// Impose \\f$(\\partial_t + \\partial_r)\\Psi=0\\f$\n  Sommerfeld,\n  /// Impose \\f$(\\partial_t + \\partial_r + r^{-1})\\Psi=0\\f$\n  BaylissTurkel\n};\n\nSphericalRadiationType convert_spherical_radiation_type_from_yaml(\n    const Options::Option& options);\n}  // namespace detail\n\n/*!\n * \\brief Impose spherical radiation boundary conditions.\n *\n * These can be imposed in one of two forms:\n *\n * \\f{align*}{\n *  \\Pi=\\partial_r\\Psi,\n * \\f}\n *\n * referred to as a Sommerfeld condition, or\n *\n * \\f{align*}{\n *  \\Pi=\\partial_r\\Psi + \\frac{1}{r}\\Psi\n * \\f}\n *\n * referred to as a Bayliss-Turkel condition.\n *\n * The Bayliss-Turkel condition produces fewer reflections than the Sommerfeld\n * condition.\n *\n * \\warning These are implemented assuming the outer boundary is spherical and\n * centered at the origin of the radiation because the code sets\n * \\f$\\Pi=n^i\\Phi_i\\f$, where \\f$n^i\\f$ is the outward pointing unit normal\n * vector. It might be possible to generalize the condition to non-spherical\n * boundaries by using \\f$x^i/r\\f$ instead of \\f$n^i\\f$, but this hasn't been\n * tested.\n */\ntemplate <size_t Dim>\nclass SphericalRadiation final : public BoundaryCondition<Dim> {\n public:\n  struct TypeOptionTag {\n    using type = detail::SphericalRadiationType;\n    static std::string name() { return \"Type\"; }\n    static constexpr Options::String help{\n        \"Whether to impose Sommerfeld or first-order Bayliss-Turkel spherical \"\n        \"radiation boundary conditions.\"};\n  };\n\n  using options = tmpl::list<TypeOptionTag>;\n  static constexpr Options::String help{\n      \"Spherical radiation boundary conditions setting the value of Psi, Phi, \"\n      \"and Pi either using the Sommerfeld or first-order Bayliss-Turkel \"\n      \"method.\"};\n\n  SphericalRadiation() = default;\n  SphericalRadiation(detail::SphericalRadiationType type);\n  SphericalRadiation(SphericalRadiation&&) = default;\n  SphericalRadiation& operator=(SphericalRadiation&&) = default;\n  SphericalRadiation(const SphericalRadiation&) = default;\n  SphericalRadiation& operator=(const SphericalRadiation&) = default;\n  ~SphericalRadiation() override = default;\n\n  explicit SphericalRadiation(CkMigrateMessage* msg);\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, SphericalRadiation);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override;\n\n  static constexpr evolution::BoundaryConditions::Type bc_type =\n      evolution::BoundaryConditions::Type::Ghost;\n\n  void pup(PUP::er& p) override;\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<Tags::Psi, Tags::Phi<Dim>>;\n  using dg_interior_temporary_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 Tags::ConstraintGamma2>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  std::optional<std::string> dg_ghost(\n      gsl::not_null<Scalar<DataVector>*> psi_ext,\n      gsl::not_null<Scalar<DataVector>*> pi_ext,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> phi_ext,\n      gsl::not_null<Scalar<DataVector>*> gamma2_ext,\n      const std::optional<\n          tnsr::I<DataVector, Dim, Frame::Inertial>>& /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const Scalar<DataVector>& psi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const Scalar<DataVector>& gamma2) const;\n\n private:\n  detail::SphericalRadiationType type_{\n      detail::SphericalRadiationType::Sommerfeld};\n};\n}  // namespace ScalarWave::BoundaryConditions\n\ntemplate <>\nstruct Options::create_from_yaml<\n    ScalarWave::BoundaryConditions::detail::SphericalRadiationType> {\n  template <typename Metavariables>\n  static typename ScalarWave::BoundaryConditions::detail::SphericalRadiationType\n  create(const Options::Option& options) {\n    return ScalarWave::BoundaryConditions::detail::\n        convert_spherical_radiation_type_from_yaml(options);\n  }\n};\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryCorrections/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  UpwindPenalty.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  NamespaceDocs.hpp\n  UpwindPenalty.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryCorrections/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarWave::BoundaryCorrections {\ntemplate <size_t Dim>\nusing standard_boundary_corrections = tmpl::list<UpwindPenalty<Dim>>;\n}  // namespace ScalarWave::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryCorrections/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Boundary corrections/numerical fluxes\nnamespace ScalarWave::BoundaryCorrections {}\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarWave::BoundaryCorrections {\ntemplate <size_t Dim>\nUpwindPenalty<Dim>::UpwindPenalty(CkMigrateMessage* msg)\n    : BoundaryCorrection(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::BoundaryCorrection> UpwindPenalty<Dim>::get_clone()\n    const {\n  return std::make_unique<UpwindPenalty>(*this);\n}\n\ntemplate <size_t Dim>\nvoid UpwindPenalty<Dim>::pup(PUP::er& p) {\n  BoundaryCorrection::pup(p);\n}\n\ntemplate <size_t Dim>\ndouble UpwindPenalty<Dim>::dg_package_data(\n    const gsl::not_null<Scalar<DataVector>*> packaged_char_speed_v_psi,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_v_zero,\n    const gsl::not_null<Scalar<DataVector>*> packaged_char_speed_v_plus,\n    const gsl::not_null<Scalar<DataVector>*> packaged_char_speed_v_minus,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_n_times_v_plus,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        packaged_char_speed_n_times_v_minus,\n    const gsl::not_null<Scalar<DataVector>*> packaged_char_speed_gamma2_v_psi,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        packaged_char_speeds,\n\n    const Scalar<DataVector>& psi, const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n\n    const Scalar<DataVector>& constraint_gamma2,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n    /*mesh_velocity*/,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n  if (normal_dot_mesh_velocity.has_value()) {\n    get<0>(*packaged_char_speeds) = -get(*normal_dot_mesh_velocity);\n    get<1>(*packaged_char_speeds) = 1.0 - get(*normal_dot_mesh_velocity);\n    get<2>(*packaged_char_speeds) = -1.0 - get(*normal_dot_mesh_velocity);\n  } else {\n    get<0>(*packaged_char_speeds) = 0.0;\n    get<1>(*packaged_char_speeds) = 1.0;\n    get<2>(*packaged_char_speeds) = -1.0;\n  }\n\n  // Computes the contribution to the boundary correction from one side of the\n  // interface.\n  //\n  // Note: when UpwindPenalty::dg_boundary_terms() is called, an Element passes\n  // in its own packaged data to fill the interior fields, and its neighbor's\n  // packaged data to fill the exterior fields. This introduces a sign flip for\n  // each normal used in computing the exterior fields.\n  get(*packaged_char_speed_gamma2_v_psi) = get(constraint_gamma2) * get(psi);\n  {\n    // Use v_psi allocation as n^i Phi_i\n    dot_product(packaged_char_speed_v_psi, normal_covector, phi);\n    const auto& normal_dot_phi = get(*packaged_char_speed_v_psi);\n\n    for (size_t i = 0; i < Dim; ++i) {\n      packaged_char_speed_v_zero->get(i) =\n          get<0>(*packaged_char_speeds) *\n          (phi.get(i) - normal_covector.get(i) * normal_dot_phi);\n    }\n\n    get(*packaged_char_speed_v_plus) =\n        get<1>(*packaged_char_speeds) *\n        (get(pi) + normal_dot_phi - get(*packaged_char_speed_gamma2_v_psi));\n    get(*packaged_char_speed_v_minus) =\n        get<2>(*packaged_char_speeds) *\n        (get(pi) - normal_dot_phi - get(*packaged_char_speed_gamma2_v_psi));\n  }\n\n  for (size_t d = 0; d < Dim; ++d) {\n    packaged_char_speed_n_times_v_plus->get(d) =\n        get(*packaged_char_speed_v_plus) * normal_covector.get(d);\n    packaged_char_speed_n_times_v_minus->get(d) =\n        get(*packaged_char_speed_v_minus) * normal_covector.get(d);\n  }\n\n  get(*packaged_char_speed_v_psi) = get<0>(*packaged_char_speeds) * get(psi);\n  get(*packaged_char_speed_gamma2_v_psi) *= get<0>(*packaged_char_speeds);\n\n  return max(max(get<0>(*packaged_char_speeds), get<1>(*packaged_char_speeds),\n                 get<2>(*packaged_char_speeds)));\n}\n\ntemplate <size_t Dim>\nvoid UpwindPenalty<Dim>::dg_boundary_terms(\n    const gsl::not_null<Scalar<DataVector>*> psi_boundary_correction,\n    const gsl::not_null<Scalar<DataVector>*> pi_boundary_correction,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        phi_boundary_correction,\n\n    const Scalar<DataVector>& char_speed_v_psi_int,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& char_speed_v_zero_int,\n    const Scalar<DataVector>& char_speed_v_plus_int,\n    const Scalar<DataVector>& char_speed_v_minus_int,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>&\n        char_speed_normal_times_v_plus_int,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>&\n        char_speed_normal_times_v_minus_int,\n    const Scalar<DataVector>& char_speed_constraint_gamma2_v_psi_int,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& char_speeds_int,\n\n    const Scalar<DataVector>& char_speed_v_psi_ext,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& char_speed_v_zero_ext,\n    const Scalar<DataVector>& char_speed_v_plus_ext,\n    const Scalar<DataVector>& char_speed_v_minus_ext,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>&\n        char_speed_minus_normal_times_v_plus_ext,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>&\n        char_speed_minus_normal_times_v_minus_ext,\n    const Scalar<DataVector>& char_speed_constraint_gamma2_v_psi_ext,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& char_speeds_ext,\n    dg::Formulation /*dg_formulation*/) const {\n  const size_t num_pts = char_speeds_int[0].size();\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>,\n                       ::Tags::TempScalar<2>, ::Tags::TempScalar<3>,\n                       ::Tags::TempScalar<4>, ::Tags::TempScalar<5>,\n                       ::Tags::TempScalar<6>, ::Tags::TempScalar<7>>>\n      buffer(num_pts);\n  DataVector& weighted_lambda_psi_int = get(get<::Tags::TempScalar<0>>(buffer));\n  weighted_lambda_psi_int = step_function(-char_speeds_int[0]);\n  DataVector& weighted_lambda_psi_ext = get(get<::Tags::TempScalar<1>>(buffer));\n  weighted_lambda_psi_ext = -step_function(char_speeds_ext[0]);\n\n  DataVector& weighted_lambda_zero_int =\n      get(get<::Tags::TempScalar<2>>(buffer));\n  weighted_lambda_zero_int = step_function(-char_speeds_int[0]);\n  DataVector& weighted_lambda_zero_ext =\n      get(get<::Tags::TempScalar<3>>(buffer));\n  weighted_lambda_zero_ext = -step_function(char_speeds_ext[0]);\n\n  DataVector& weighted_lambda_plus_int =\n      get(get<::Tags::TempScalar<4>>(buffer));\n  weighted_lambda_plus_int = step_function(-char_speeds_int[1]);\n  DataVector& weighted_lambda_plus_ext =\n      get(get<::Tags::TempScalar<5>>(buffer));\n  weighted_lambda_plus_ext = -step_function(char_speeds_ext[1]);\n\n  DataVector& weighted_lambda_minus_int =\n      get(get<::Tags::TempScalar<6>>(buffer));\n  weighted_lambda_minus_int = step_function(-char_speeds_int[2]);\n  DataVector& weighted_lambda_minus_ext =\n      get(get<::Tags::TempScalar<7>>(buffer));\n  weighted_lambda_minus_ext = -step_function(char_speeds_ext[2]);\n\n  // D_psi = Theta(-lambda_psi^{ext}) lambda_psi^{ext} v_psi^{ext}\n  //       - Theta(-lambda_psi^{int}) lambda_psi^{int} v_psi^{int}\n  // where the unit normals on both sides point in the same direction, out\n  // of the current element. Since lambda_psi from the neighbor is computing\n  // with the normal vector pointing into the current element in the code,\n  // we need to swap the sign of lambda_psi^{ext}. Theta is the heaviside step\n  // function.\n  psi_boundary_correction->get() =\n      weighted_lambda_psi_ext * get(char_speed_v_psi_ext) -\n      weighted_lambda_psi_int * get(char_speed_v_psi_int);\n\n  get(*pi_boundary_correction) =\n      0.5 * (weighted_lambda_plus_ext * get(char_speed_v_plus_ext) +\n             weighted_lambda_minus_ext * get(char_speed_v_minus_ext)) +\n      weighted_lambda_psi_ext * get(char_speed_constraint_gamma2_v_psi_ext)\n\n      - 0.5 * (weighted_lambda_plus_int * get(char_speed_v_plus_int) +\n               weighted_lambda_minus_int * get(char_speed_v_minus_int)) -\n      weighted_lambda_psi_int * get(char_speed_constraint_gamma2_v_psi_int);\n\n  for (size_t d = 0; d < Dim; ++d) {\n    phi_boundary_correction->get(d) =\n        0.5 * (weighted_lambda_plus_ext *\n                   char_speed_minus_normal_times_v_plus_ext.get(d) -\n               weighted_lambda_minus_ext *\n                   char_speed_minus_normal_times_v_minus_ext.get(d)) +\n        weighted_lambda_zero_ext * char_speed_v_zero_ext.get(d)\n\n        - 0.5 * (weighted_lambda_plus_int *\n                     char_speed_normal_times_v_plus_int.get(d) -\n                 weighted_lambda_minus_int *\n                     char_speed_normal_times_v_minus_int.get(d)) -\n        weighted_lambda_zero_int * char_speed_v_zero_int.get(d);\n  }\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID UpwindPenalty<Dim>::my_PUP_ID = 0;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) template class UpwindPenalty<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace ScalarWave::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarWave::BoundaryCorrections {\n/*!\n * \\brief Computes the scalar wave upwind multipenalty boundary\n * correction.\n *\n * This implements the upwind multipenalty boundary correction term\n * \\f$D_\\beta\\f$. The general form is given by:\n *\n * \\f{align*}{\n *   D_\\beta =\n *   T_{\\beta\\hat{\\beta}}^{\\mathrm{ext}}\n *   \\Lambda^{\\mathrm{ext},-}_{\\hat{\\beta}\\hat{\\alpha}}\n *   v^{\\mathrm{ext}}_{\\hat{\\alpha}}\n *   -T_{\\beta\\hat{\\beta}}^{\\mathrm{int}}\n *   \\Lambda^{\\mathrm{int},-}_{\\hat{\\beta}\\hat{\\alpha}}\n *   v^{\\mathrm{int}}_{\\hat{\\alpha}}.\n * \\f}\n *\n * We denote the evolved fields by \\f$u_{\\alpha}\\f$, the characteristic fields\n * by \\f$v_{\\hat{\\alpha}}\\f$, and implicitly sum over reapeated indices.\n * \\f$T_{\\beta\\hat{\\beta}}\\f$ transforms characteristic fields to evolved\n * fields, while \\f$\\Lambda_{\\hat{\\beta}\\hat{\\alpha}}^-\\f$ is a diagonal matrix\n * with only the negative characteristic speeds, and has zeros on the diagonal\n * for all other entries. The int and ext superscripts denote quantities on the\n * internal and external side of the mortar. Note that Eq. (6.3) of\n * \\cite Teukolsky2015ega is not exactly what's implemented since that boundary\n * term does not consistently treat both sides of the interface on the same\n * footing.\n *\n * For the scalar wave system the correction is:\n *\n * \\f{align}{\n *   D_{\\Psi} &= \\lambda_{v^{\\Psi}}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},\\Psi}\n *                - \\lambda_{v^{\\Psi}}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},\\Psi}, \\\\\n *   D_{\\Pi} &= \\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{ext},-}\n *             v^{\\mathrm{ext},+} +\n *             \\lambda_{v^-}^{\\mathrm{ext},-}\n *             v^{\\mathrm{ext},-}\\right)\n *             + \\lambda_{v^\\Psi}^{\\mathrm{ext},-}\\gamma_2\n *             v^{\\mathrm{ext},\\Psi}\n *             \\notag \\\\\n *           &-\\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{int},-}\n *             v^{\\mathrm{int},+} +\n *             \\lambda_{v^-}^{\\mathrm{int},-}\n *             v^{\\mathrm{int},-}\\right)\n *             - \\lambda_{v^\\Psi}^{\\mathrm{int},-}\\gamma_2\n *             v^{\\mathrm{int},\\Psi} , \\\\\n *   D_{\\Phi_{i}}\n *              &= \\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},+}\n *                - \\lambda_{v^-}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},-}\\right)n_i^{\\mathrm{ext}}\n *                + \\lambda_{v^0}^{\\mathrm{ext},-}\n *                v^{\\mathrm{ext},0}_{i}\n *                \\notag \\\\\n *              &-\n *                \\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},+}\n *                - \\lambda_{v^-}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},-}\\right)n_i^{\\mathrm{int}}\n *                - \\lambda_{v^0}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},0}_{i},\n * \\f}\n *\n * with characteristic fields\n *\n * \\f{align}{\n *   v^{\\Psi} &= \\Psi, \\\\\n *   v^{0}_{i} &= (\\delta^k_i-n^k n_i)\\Phi_{k}, \\\\\n *   v^{\\pm} &= \\Pi\\pm n^i\\Phi_{i} -\\gamma_2 \\Psi,\n * \\f}\n *\n * and characteristic speeds\n *\n * \\f{align}{\n *   \\lambda_{v^\\Psi} =& -v^i_g n_i, \\\\\n *   \\lambda_{v^0} =& -v^i_g n_i, \\\\\n *   \\lambda_{v^\\pm} =& \\pm 1 - v^i_g n_i,\n * \\f}\n *\n * where \\f$v_g^i\\f$ is the mesh velocity and \\f$n_i\\f$ is the outward directed\n * unit normal covector to the interface. We have also defined\n *\n * \\f{align}{\n *   \\lambda^{\\pm}_{\\hat{\\alpha}} =\n *    \\left\\{\n *        \\begin{array}{ll}\n *          \\lambda_{\\hat{\\alpha}} &\n *            \\mathrm{if}\\;\\pm\\lambda_{\\hat{\\alpha}}> 0 \\\\\n *          0 & \\mathrm{otherwise}\n *        \\end{array}\\right.\n * \\f}\n *\n * In the implementation we store the speeds in a rank-3 tensor with the zeroth\n * component being \\f$\\lambda_{v^\\Psi}\\f$, the first being \\f$\\lambda_{v^+}\\f$\n * and the second being \\f$\\lambda_{v^-}\\f$.\n *\n * Note that we have assumed \\f$n_i^{\\mathrm{ext}}\\f$ points in the same\n * direction as \\f$n_i^{\\mathrm{int}}\\f$, but in the code they point in opposite\n * directions. If \\f$n_i^{\\mathrm{ext}}\\f$ points in the opposite direction the\n * external speeds have their sign flipped and the \\f$\\pm\\f$ fields and their\n * speeds reverse roles (i.e. the \\f$v^{\\mathrm{ext},+}\\f$ field is now flowing\n * into the element, while \\f$v^{\\mathrm{ext},-}\\f$ flows out). In our\n * implementation this reversal actually cancels out, and we have the following\n * equations:\n *\n * \\f{align}{\n *   D_{\\Psi} &= -\\lambda_{v^{\\Psi}}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},\\Psi}\n *                - \\lambda_{v^{\\Psi}}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},\\Psi}, \\\\\n *   D_{\\Pi}\n *              &= \\frac{1}{2}\\left(-\\lambda_{v^+}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},+} -\n *                \\lambda_{v^-}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},-}\\right)\n *                - \\lambda_{v^\\Psi}^{\\mathrm{ext},+}\\gamma_2\n *                v^{\\mathrm{ext},\\Psi}\n *                \\notag \\\\\n *              &-\\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},+} +\n *                \\lambda_{v^-}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},-}\\right)\n *                - \\lambda_{v^\\Psi}^{\\mathrm{int},-}\\gamma_2\n *                v^{\\mathrm{int},\\Psi} , \\\\\n *   D_{\\Phi_{i}}\n *              &= \\frac{1}{2}\\left(-\\lambda_{v^+}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},+}\n *                + \\lambda_{v^-}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},-}\\right)n_i^{\\mathrm{ext}}\n *                - \\lambda_{v^0}^{\\mathrm{ext},+}\n *                v^{\\mathrm{ext},0}_{i}\n *                \\notag \\\\\n *              &-\n *                \\frac{1}{2}\\left(\\lambda_{v^+}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},+}\n *                - \\lambda_{v^-}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},-}\\right)n_i^{\\mathrm{int}}\n *                - \\lambda_{v^0}^{\\mathrm{int},-}\n *                v^{\\mathrm{int},0}_{i},\n * \\f}\n */\ntemplate <size_t Dim>\nclass UpwindPenalty final : public evolution::BoundaryCorrection {\n private:\n  struct NormalTimesVPlus : db::SimpleTag {\n    using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n  };\n  struct NormalTimesVMinus : db::SimpleTag {\n    using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n  };\n  struct Gamma2VPsi : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n  struct CharSpeedsTensor : db::SimpleTag {\n    using type = tnsr::i<DataVector, 3, Frame::Inertial>;\n  };\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Computes the UpwindPenalty boundary correction term for the scalar wave \"\n      \"system.\"};\n\n  UpwindPenalty() = default;\n  UpwindPenalty(const UpwindPenalty&) = default;\n  UpwindPenalty& operator=(const UpwindPenalty&) = default;\n  UpwindPenalty(UpwindPenalty&&) = default;\n  UpwindPenalty& operator=(UpwindPenalty&&) = default;\n  ~UpwindPenalty() override = default;\n\n  /// \\cond\n  explicit UpwindPenalty(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(UpwindPenalty);  // NOLINT\n  /// \\endcond\n  void pup(PUP::er& p) override;  // NOLINT\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override;\n\n  using dg_package_field_tags =\n      tmpl::list<Tags::VPsi, Tags::VZero<Dim>, Tags::VPlus, Tags::VMinus,\n                 NormalTimesVPlus, NormalTimesVMinus, Gamma2VPsi,\n                 CharSpeedsTensor>;\n  using dg_package_data_temporary_tags = tmpl::list<Tags::ConstraintGamma2>;\n  using dg_package_data_volume_tags = tmpl::list<>;\n  using dg_boundary_terms_volume_tags = tmpl::list<>;\n\n  double dg_package_data(\n      gsl::not_null<Scalar<DataVector>*> packaged_char_speed_v_psi,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_v_zero,\n      gsl::not_null<Scalar<DataVector>*> packaged_char_speed_v_plus,\n      gsl::not_null<Scalar<DataVector>*> packaged_char_speed_v_minus,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_n_times_v_plus,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_char_speed_n_times_v_minus,\n      gsl::not_null<Scalar<DataVector>*> packaged_char_speed_gamma2_v_psi,\n      gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n          packaged_char_speeds,\n\n      const Scalar<DataVector>& psi, const Scalar<DataVector>& pi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n\n      const Scalar<DataVector>& constraint_gamma2,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const;\n\n  void dg_boundary_terms(\n      gsl::not_null<Scalar<DataVector>*> psi_boundary_correction,\n      gsl::not_null<Scalar<DataVector>*> pi_boundary_correction,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          phi_boundary_correction,\n\n      const Scalar<DataVector>& char_speed_v_psi_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& char_speed_v_zero_int,\n      const Scalar<DataVector>& char_speed_v_plus_int,\n      const Scalar<DataVector>& char_speed_v_minus_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          char_speed_normal_times_v_plus_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          char_speed_normal_times_v_minus_int,\n      const Scalar<DataVector>& char_speed_constraint_gamma2_v_psi_int,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& char_speeds_int,\n\n      const Scalar<DataVector>& char_speed_v_psi_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& char_speed_v_zero_ext,\n      const Scalar<DataVector>& char_speed_v_plus_ext,\n      const Scalar<DataVector>& char_speed_v_minus_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          char_speed_minus_normal_times_v_plus_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n          char_speed_minus_normal_times_v_minus_ext,\n      const Scalar<DataVector>& char_speed_constraint_gamma2_v_psi_ext,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& char_speeds_ext,\n      dg::Formulation /*dg_formulation*/) const;\n};\n}  // namespace ScalarWave::BoundaryCorrections\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarWave)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Characteristics.cpp\n  Constraints.cpp\n  EnergyDensity.cpp\n  Equations.cpp\n  MomentumDensity.cpp\n  TimeDerivative.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Characteristics.hpp\n  Constraints.hpp\n  EnergyDensity.hpp\n  Equations.hpp\n  Initialize.hpp\n  MomentumDensity.hpp\n  System.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  TimeDerivative.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainBoundaryConditions\n  ErrorHandling\n  Events\n  Evolution\n  LinearOperators\n  Options\n  Utilities\n  INTERFACE\n  Initialization\n  Parallel\n  Serialization\n  PRIVATE\n  WaveEquationSolutions\n  )\n\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(BoundaryCorrections)\nadd_subdirectory(Instantiations)\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/Characteristics.hpp\"\n\n#include <array>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarWave {\ntemplate <size_t Dim>\nvoid characteristic_speeds(\n    const gsl::not_null<std::array<DataVector, 4>*> char_speeds,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n  set_number_of_grid_points(char_speeds, unit_normal_one_form);\n  (*char_speeds)[0] = 0.;   // v(VPsi)\n  (*char_speeds)[1] = 0.;   // v(VZero)\n  (*char_speeds)[2] = 1.;   // v(VPlus)\n  (*char_speeds)[3] = -1.;  // v(VMinus)\n}\n\ntemplate <size_t Dim>\nstd::array<DataVector, 4> characteristic_speeds(\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n  auto char_speeds = make_with_value<std::array<DataVector, 4>>(\n      get<0>(unit_normal_one_form), 0.);\n  characteristic_speeds(make_not_null(&char_speeds), unit_normal_one_form);\n  return char_speeds;\n}\n\ntemplate <size_t Dim>\nvoid characteristic_fields(\n    const gsl::not_null<Variables<\n        tmpl::list<Tags::VPsi, Tags::VZero<Dim>, Tags::VPlus, Tags::VMinus>>*>\n        char_fields,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n  if (UNLIKELY(char_fields->number_of_grid_points() != get(psi).size())) {\n    char_fields->initialize(get(psi).size());\n  }\n  // Compute phi_dot_normal = n^i \\Phi_{i} = \\sum_i n_i \\Phi_{i}\n  // (we use normal_one_form and normal_vector interchangeably in flat space)\n  const auto phi_dot_normal = dot_product(unit_normal_one_form, phi);\n\n  // Eq.(34) of Holst+ (2004) for VZero\n  for (size_t i = 0; i < Dim; ++i) {\n    get<Tags::VZero<Dim>>(*char_fields).get(i) =\n        phi.get(i) - unit_normal_one_form.get(i) * get(phi_dot_normal);\n  }\n\n  // Eq.(33) of Holst+ (2004) for VPsi\n  get<Tags::VPsi>(*char_fields) = psi;\n\n  // Eq.(35) of Holst+ (2004) for VPlus and VMinus\n  get(get<Tags::VPlus>(*char_fields)) =\n      get(pi) + get(phi_dot_normal) - get(gamma_2) * get(psi);\n  get(get<Tags::VMinus>(*char_fields)) =\n      get(pi) - get(phi_dot_normal) - get(gamma_2) * get(psi);\n}\n\ntemplate <size_t Dim>\nVariables<tmpl::list<Tags::VPsi, Tags::VZero<Dim>, Tags::VPlus, Tags::VMinus>>\ncharacteristic_fields(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n  Variables<tmpl::list<Tags::VPsi, Tags::VZero<Dim>, Tags::VPlus, Tags::VMinus>>\n      char_fields(get_size(get(gamma_2)));\n  characteristic_fields(make_not_null(&char_fields), gamma_2, psi, pi, phi,\n                        unit_normal_one_form);\n  return char_fields;\n}\n\ntemplate <size_t Dim>\nvoid evolved_fields_from_characteristic_fields(\n    const gsl::not_null<\n        Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>>*>\n        evolved_fields,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n  if (UNLIKELY(evolved_fields->number_of_grid_points() != get(v_psi).size())) {\n    evolved_fields->initialize(get(v_psi).size());\n  }\n  // Eq.(36) of Holst+ (2004) for Psi\n  get<Tags::Psi>(*evolved_fields) = v_psi;\n\n  // Eq.(37) - (38) of Holst+ (2004) for Pi and Phi\n  get<Tags::Pi>(*evolved_fields).get() =\n      0.5 * (get(v_plus) + get(v_minus)) + get(gamma_2) * get(v_psi);\n  for (size_t i = 0; i < Dim; ++i) {\n    get<Tags::Phi<Dim>>(*evolved_fields).get(i) =\n        0.5 * (get(v_plus) - get(v_minus)) * unit_normal_one_form.get(i) +\n        v_zero.get(i);\n  }\n}\n\ntemplate <size_t Dim>\nVariables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>>\nevolved_fields_from_characteristic_fields(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n  Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>> evolved_fields(\n      get_size(get(gamma_2)));\n  evolved_fields_from_characteristic_fields(make_not_null(&evolved_fields),\n                                            gamma_2, v_psi, v_zero, v_plus,\n                                            v_minus, unit_normal_one_form);\n  return evolved_fields;\n}\n}  // namespace ScalarWave\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void ScalarWave::characteristic_speeds(                           \\\n      const gsl::not_null<std::array<DataVector, 4>*> char_speeds,           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                 \\\n          unit_normal_one_form);                                             \\\n  template std::array<DataVector, 4> ScalarWave::characteristic_speeds(      \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                 \\\n          unit_normal_one_form);                                             \\\n  template struct ScalarWave::Tags::CharacteristicSpeedsCompute<DIM(data)>;  \\\n  template void ScalarWave::characteristic_fields(                           \\\n      const gsl::not_null<Variables<tmpl::list<                              \\\n          ScalarWave::Tags::VPsi, ScalarWave::Tags::VZero<DIM(data)>,        \\\n          ScalarWave::Tags::VPlus, ScalarWave::Tags::VMinus>>*>              \\\n          char_fields,                                                       \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,      \\\n      const Scalar<DataVector>& pi,                                          \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi,            \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                 \\\n          unit_normal_one_form);                                             \\\n  template Variables<                                                        \\\n      tmpl::list<ScalarWave::Tags::VPsi, ScalarWave::Tags::VZero<DIM(data)>, \\\n                 ScalarWave::Tags::VPlus, ScalarWave::Tags::VMinus>>         \\\n  ScalarWave::characteristic_fields(                                         \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,      \\\n      const Scalar<DataVector>& pi,                                          \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi,            \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                 \\\n          unit_normal_one_form);                                             \\\n  template struct ScalarWave::Tags::CharacteristicFieldsCompute<DIM(data)>;  \\\n  template void ScalarWave::evolved_fields_from_characteristic_fields(       \\\n      const gsl::not_null<                                                   \\\n          Variables<tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,  \\\n                               ScalarWave::Tags::Phi<DIM(data)>>>*>          \\\n          evolved_fields,                                                    \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,    \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& v_zero,         \\\n      const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,   \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                 \\\n          unit_normal_one_form);                                             \\\n  template Variables<tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi, \\\n                                ScalarWave::Tags::Phi<DIM(data)>>>           \\\n  ScalarWave::evolved_fields_from_characteristic_fields(                     \\\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,    \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& v_zero,         \\\n      const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,   \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&                 \\\n          unit_normal_one_form);                                             \\\n  template struct ScalarWave::Tags::                                         \\\n      EvolvedFieldsFromCharacteristicFieldsCompute<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Characteristics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <typename>\nclass Variables;\n\nnamespace Tags {\ntemplate <typename Tag>\nstruct Normalized;\n}  // namespace Tags\n/// \\endcond\n\nnamespace ScalarWave {\n/// @{\n/*!\n * \\brief Compute the characteristic speeds for the scalar wave system.\n *\n * Computes the speeds as described in \"Optimal constraint projection for\n * hyperbolic evolution systems\" by Holst et al. \\cite Holst2004wt\n * [see text following Eq.(32)]. The characteristic fields' names used here\n * differ from this paper:\n *\n * \\f{align*}\n * \\mathrm{SpECTRE} && \\mathrm{Holst} \\\\\n * v^{\\hat \\psi} && Z^1 \\\\\n * v^{\\hat 0}_{i} && Z^{2}_{i} \\\\\n * v^{\\hat \\pm} && u^{1\\pm}\n * \\f}\n *\n * The corresponding characteristic speeds \\f$\\lambda_{\\hat \\alpha}\\f$ are given\n * in the text following Eq.(38) of \\cite Holst2004wt :\n *\n * \\f{align*}\n * \\lambda_{\\hat \\psi} =& 0 \\\\\n * \\lambda_{\\hat 0} =& 0 \\\\\n * \\lambda_{\\hat \\pm} =& \\pm 1.\n * \\f}\n */\ntemplate <size_t Dim>\nstd::array<DataVector, 4> characteristic_speeds(\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form);\n\ntemplate <size_t Dim>\nvoid characteristic_speeds(\n    gsl::not_null<std::array<DataVector, 4>*> char_speeds,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form);\n\nnamespace Tags {\ntemplate <size_t Dim>\nstruct CharacteristicSpeedsCompute : Tags::CharacteristicSpeeds<Dim>,\n                                     db::ComputeTag {\n  using base = Tags::CharacteristicSpeeds<Dim>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim>>>;\n\n  static void function(\n      gsl::not_null<return_type*> char_speeds,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n    characteristic_speeds(char_speeds, unit_normal_one_form);\n  }\n};\n}  // namespace Tags\n/// @}\n\n/// @{\n/*!\n * \\brief Computes characteristic fields from evolved fields\n *\n * \\ref Tags::CharacteristicFieldsCompute and\n * \\ref Tags::EvolvedFieldsFromCharacteristicFieldsCompute convert between\n * characteristic and evolved fields for the scalar-wave system.\n *\n * \\ref Tags::CharacteristicFieldsCompute computes\n * characteristic fields as described in \"Optimal constraint projection for\n * hyperbolic evolution systems\" by Holst et al. \\cite Holst2004wt .\n * Their names used here differ from this paper:\n *\n * \\f{align*}\n * \\mathrm{SpECTRE} && \\mathrm{Holst} \\\\\n * v^{\\hat \\psi} && Z^1 \\\\\n * v^{\\hat 0}_{i} && Z^{2}_{i} \\\\\n * v^{\\hat \\pm} && u^{1\\pm}\n * \\f}\n *\n * The characteristic fields \\f${v}^{\\hat \\alpha}\\f$ are given in terms of\n * the evolved fields by Eq.(33) - (35) of \\cite Holst2004wt, respectively:\n *\n * \\f{align*}\n * v^{\\hat \\psi} =& \\psi \\\\\n * v^{\\hat 0}_{i} =& (\\delta^k_i - n_i n^k) \\Phi_{k} := P^k_i \\Phi_{k} \\\\\n * v^{\\hat \\pm} =& \\Pi \\pm n^i \\Phi_{i} - \\gamma_2\\psi\n * \\f}\n *\n * where \\f$\\psi\\f$ is the scalar field, \\f$\\Phi_{i}=\\partial_i \\psi\\f$ is an\n * auxiliary variable, \\f$\\Pi\\f$ is a conjugate momentum, \\f$\\gamma_2\\f$\n * is a constraint damping parameter, and \\f$n_k\\f$ is the unit normal to the\n * surface along which the characteristic fields are defined.\n *\n * \\ref Tags::EvolvedFieldsFromCharacteristicFieldsCompute computes evolved\n * fields \\f$u_\\alpha\\f$ in terms of the characteristic fields. This uses the\n * inverse of above relations:\n *\n * \\f{align*}\n * \\psi =& v^{\\hat \\psi}, \\\\\n * \\Pi =& \\frac{1}{2}(v^{\\hat +} + v^{\\hat -}) + \\gamma_2 v^{\\hat \\psi}, \\\\\n * \\Phi_{i} =& \\frac{1}{2}(v^{\\hat +} - v^{\\hat -}) n_i + v^{\\hat 0}_{i}.\n * \\f}\n *\n * The corresponding characteristic speeds \\f$\\lambda_{\\hat \\alpha}\\f$\n * are computed by \\ref Tags::CharacteristicSpeedsCompute .\n */\ntemplate <size_t Dim>\nVariables<tmpl::list<Tags::VPsi, Tags::VZero<Dim>, Tags::VPlus, Tags::VMinus>>\ncharacteristic_fields(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form);\n\ntemplate <size_t Dim>\nvoid characteristic_fields(\n    gsl::not_null<Variables<\n        tmpl::list<Tags::VPsi, Tags::VZero<Dim>, Tags::VPlus, Tags::VMinus>>*>\n        char_fields,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form);\n\nnamespace Tags {\ntemplate <size_t Dim>\nstruct CharacteristicFieldsCompute : Tags::CharacteristicFields<Dim>,\n                                     db::ComputeTag {\n  using base = Tags::CharacteristicFields<Dim>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::ConstraintGamma2, Psi, Pi, Phi<Dim>,\n                 ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim>>>;\n\n  static void function(\n      const gsl::not_null<return_type*> char_fields,\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n      const Scalar<DataVector>& pi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n    characteristic_fields(char_fields, gamma_2, psi, pi, phi,\n                          unit_normal_one_form);\n  };\n};\n}  // namespace Tags\n/// @}\n\n/// @{\n/*!\n * \\brief Compute evolved fields from characteristic fields.\n *\n * For expressions used here to compute evolved fields from characteristic ones,\n * see \\ref Tags::CharacteristicFieldsCompute.\n */\ntemplate <size_t Dim>\nVariables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>>\nevolved_fields_from_characteristic_fields(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form);\n\ntemplate <size_t Dim>\nvoid evolved_fields_from_characteristic_fields(\n    gsl::not_null<Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>>*>\n        evolved_fields,\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form);\n\nnamespace Tags {\ntemplate <size_t Dim>\nstruct EvolvedFieldsFromCharacteristicFieldsCompute\n    : Tags::EvolvedFieldsFromCharacteristicFields<Dim>,\n      db::ComputeTag {\n  using base = Tags::EvolvedFieldsFromCharacteristicFields<Dim>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<Tags::ConstraintGamma2, Tags::VPsi, Tags::VZero<Dim>,\n                 Tags::VPlus, Tags::VMinus,\n                 ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim>>>;\n\n  static void function(\n      const gsl::not_null<return_type*> evolved_fields,\n      const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero,\n      const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n    evolved_fields_from_characteristic_fields(evolved_fields, gamma_2, v_psi,\n                                              v_zero, v_plus, v_minus,\n                                              unit_normal_one_form);\n  };\n};\n\nstruct LargestCharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\n/// Compute the maximum magnitude of the characteristic speeds.\nstruct ComputeLargestCharacteristicSpeed : LargestCharacteristicSpeed,\n                                           db::ComputeTag {\n  using argument_tags = tmpl::list<>;\n  using return_type = double;\n  using base = LargestCharacteristicSpeed;\n  SPECTRE_ALWAYS_INLINE static constexpr void function(\n      const gsl::not_null<double*> speed) {\n    *speed = 1.0;\n  }\n};\n}  // namespace Tags\n/// @}\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/Constraints.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ScalarWave {\ntemplate <size_t SpatialDim>\ntnsr::i<DataVector, SpatialDim, Frame::Inertial> one_index_constraint(\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi) {\n  tnsr::i<DataVector, SpatialDim, Frame::Inertial> constraint(\n      get_size(get<0>(phi)));\n  one_index_constraint(make_not_null(&constraint), d_psi, phi);\n  return constraint;\n}\n\ntemplate <size_t SpatialDim>\nvoid one_index_constraint(\n    const gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*>\n        constraint,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi) {\n  // Declare iterators for d_psi and phi outside the for loop,\n  // because they are const but constraint is not\n  auto d_psi_it = d_psi.cbegin(), phi_it = phi.cbegin();\n\n  for (auto constraint_it = constraint->begin();\n       constraint_it != constraint->end();\n       ++constraint_it, (void)++d_psi_it, (void)++phi_it) {\n    *constraint_it = *d_psi_it - *phi_it;\n  }\n}\n\ntemplate <size_t SpatialDim>\ntnsr::ij<DataVector, SpatialDim, Frame::Inertial> two_index_constraint(\n    const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>& d_phi) {\n  tnsr::ij<DataVector, SpatialDim, Frame::Inertial> constraint(\n      get_size(get<0, 0>(d_phi)));\n  two_index_constraint(make_not_null(&constraint), d_phi);\n  return constraint;\n}\n\ntemplate <size_t SpatialDim>\nvoid two_index_constraint(\n    const gsl::not_null<tnsr::ij<DataVector, SpatialDim, Frame::Inertial>*>\n        constraint,\n    const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>& d_phi) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      constraint->get(i, j) = d_phi.get(i, j) - d_phi.get(j, i);\n    }\n  }\n}\n}  // namespace ScalarWave\n\n// Explicit Instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template tnsr::i<DataVector, DIM(data), Frame::Inertial>                    \\\n  ScalarWave::one_index_constraint(                                           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&,                 \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&);                \\\n  template void ScalarWave::one_index_constraint(                             \\\n      const gsl::not_null<tnsr::i<DataVector, DIM(data), Frame::Inertial>*>,  \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&,                 \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>&);                \\\n  template tnsr::ij<DataVector, DIM(data), Frame::Inertial>                   \\\n  ScalarWave::two_index_constraint(                                           \\\n      const tnsr::ij<DataVector, DIM(data), Frame::Inertial>&);               \\\n  template void ScalarWave::two_index_constraint(                             \\\n      const gsl::not_null<tnsr::ij<DataVector, DIM(data), Frame::Inertial>*>, \\\n      const tnsr::ij<DataVector, DIM(data), Frame::Inertial>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Constraints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ScalarWave {\n/// @{\n/*!\n * \\brief Compute the scalar-wave one-index constraint.\n *\n * \\details Computes the scalar-wave one-index constraint,\n * \\f$C_{i} = \\partial_i\\psi - \\Phi_{i},\\f$ which is\n * given by Eq. (19) of \\cite Holst2004wt\n */\ntemplate <size_t SpatialDim>\ntnsr::i<DataVector, SpatialDim, Frame::Inertial> one_index_constraint(\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi);\n\ntemplate <size_t SpatialDim>\nvoid one_index_constraint(\n    gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*> constraint,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi);\n/// @}\n\n/// @{\n/*!\n * \\brief Compute the scalar-wave 2-index constraint.\n *\n * \\details Computes the scalar-wave 2-index constraint\n * \\f$C_{ij} = \\partial_i\\Phi_j - \\partial_j\\Phi_i,\\f$\n * where \\f$\\Phi_{i} = \\partial_i\\psi\\f$, that is given by\n * Eq. (20) of \\cite Holst2004wt\n *\n * \\note We do not support custom storage for antisymmetric tensors yet.\n */\ntemplate <size_t SpatialDim>\ntnsr::ij<DataVector, SpatialDim, Frame::Inertial> two_index_constraint(\n    const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>& d_phi);\n\ntemplate <size_t SpatialDim>\nvoid two_index_constraint(\n    gsl::not_null<tnsr::ij<DataVector, SpatialDim, Frame::Inertial>*>\n        constraint,\n    const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>& d_phi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get the one-index constraint for the scalar-wave\n * evolution system.\n *\n * \\details See `one_index_constraint()`. Can be retrieved using\n * `ScalarWave::Tags::OneIndexConstraint`.\n */\ntemplate <size_t SpatialDim>\nstruct OneIndexConstraintCompute : OneIndexConstraint<SpatialDim>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::Tags::deriv<Psi, tmpl::size_t<SpatialDim>, Frame::Inertial>,\n                 Phi<SpatialDim>>;\n  using return_type = tnsr::i<DataVector, SpatialDim, Frame::Inertial>;\n  static constexpr void (*function)(\n      const gsl::not_null<return_type*> result,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&) =\n      &one_index_constraint<SpatialDim>;\n  using base = OneIndexConstraint<SpatialDim>;\n};\n\n/*!\n * \\brief Compute item to get the two-index constraint for the scalar-wave\n * evolution system.\n *\n * \\details See `two_index_constraint()`. Can be retrieved using\n * `ScalarWave::Tags::TwoIndexConstraint`.\n */\ntemplate <size_t SpatialDim>\nstruct TwoIndexConstraintCompute : TwoIndexConstraint<SpatialDim>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::Tags::deriv<Phi<SpatialDim>, tmpl::size_t<SpatialDim>,\n                               Frame::Inertial>>;\n  using return_type = tnsr::ij<DataVector, SpatialDim, Frame::Inertial>;\n  static constexpr void (*function)(\n      const gsl::not_null<return_type*> result,\n      const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>&) =\n      &two_index_constraint<SpatialDim>;\n  using base = TwoIndexConstraint<SpatialDim>;\n};\n\n}  // namespace Tags\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/EnergyDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/EnergyDensity.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarWave {\ntemplate <size_t SpatialDim>\nvoid energy_density(\n    gsl::not_null<Scalar<DataVector>*> result, const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi) {\n  dot_product(result, phi, phi);\n  get(*result) += square(get(pi));\n  get(*result) *= 0.5;\n}\n\ntemplate <size_t SpatialDim>\nScalar<DataVector> energy_density(\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi) {\n  Scalar<DataVector> result{get(pi).size()};\n  energy_density(make_not_null(&result), pi, phi);\n  return result;\n}\n\n}  // namespace ScalarWave\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void ScalarWave::energy_density(                                    \\\n      gsl::not_null<Scalar<DataVector>*> result, const Scalar<DataVector>& pi, \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/EnergyDensity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ScalarWave {\n/// @{\n/*!\n * \\brief Computes the energy density of the scalar wave system.\n *\n * Below is the function used to calculate the energy density.\n *\n * \\f{align*}\n * \\epsilon = \\frac{1}{2}\\left( \\Pi^{2} + \\abs{\\Phi}^{2} \\right)\n * \\f}\n */\ntemplate <size_t SpatialDim>\nvoid energy_density(\n    gsl::not_null<Scalar<DataVector>*> result, const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi);\n\ntemplate <size_t SpatialDim>\nScalar<DataVector> energy_density(\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi);\n/// @}\n\nnamespace Tags {\n/// \\brief Computes the energy density using ScalarWave::energy_density()\ntemplate <size_t SpatialDim>\nstruct EnergyDensityCompute : EnergyDensity<SpatialDim>, db::ComputeTag {\n  using argument_tags = tmpl::list<Pi, Phi<SpatialDim>>;\n\n  using return_type = Scalar<DataVector>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*> result, const Scalar<DataVector>&,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&)>(\n      &energy_density<SpatialDim>);\n\n  using base = EnergyDensity<SpatialDim>;\n};\n}  // namespace Tags\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/Equations.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarWave {\ntemplate <size_t Dim>\nvoid ComputeNormalDotFluxes<Dim>::apply(\n    const gsl::not_null<Scalar<DataVector>*> psi_normal_dot_flux,\n    const gsl::not_null<Scalar<DataVector>*> pi_normal_dot_flux,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n        phi_normal_dot_flux,\n    const Scalar<DataVector>& pi) {\n  set_number_of_grid_points(psi_normal_dot_flux, pi);\n  set_number_of_grid_points(pi_normal_dot_flux, pi);\n  set_number_of_grid_points(phi_normal_dot_flux, pi);\n  get(*pi_normal_dot_flux) = 0.0;\n  get(*psi_normal_dot_flux) = 0.0;\n  for (size_t i = 0; i < Dim; ++i) {\n    phi_normal_dot_flux->get(i) = 0.0;\n  }\n}\n}  // namespace ScalarWave\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data) \\\n  template class ScalarWave::ComputeNormalDotFluxes<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Equations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ScalarWave {\n/*!\n * \\brief A relic of an old incorrect way of handling boundaries for\n * non-conservative systems.\n */\ntemplate <size_t Dim>\nstruct ComputeNormalDotFluxes {\n  using argument_tags = tmpl::list<Tags::Pi>;\n  static void apply(gsl::not_null<Scalar<DataVector>*> psi_normal_dot_flux,\n                    gsl::not_null<Scalar<DataVector>*> pi_normal_dot_flux,\n                    gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n                        phi_normal_dot_flux,\n                    const Scalar<DataVector>& pi);\n};\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Norms.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarWave/Constraints.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace ScalarWave {\nnamespace Actions {\n/// \\ingroup InitializationGroup\n/// \\brief Initialize items related to constraints of the ScalarWave system\n///\n/// We add both constraints and the constraint damping parameter to the\n/// evolution databox.\n///\n/// DataBox changes:\n/// - Adds:\n///   * `ScalarWave::Tags::ConstraintGamma2`\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <size_t Dim>\nstruct InitializeConstraints {\n  using simple_tags = tmpl::list<ScalarWave::Tags::ConstraintGamma2>;\n\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    Scalar<DataVector> gamma_2{mesh.number_of_grid_points(), 0.};\n\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box),\n                                               std::move(gamma_2));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Instantiations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Events.cpp\n  TimeStepping.cpp\n  VolumeTerms.cpp\n  )\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Instantiations/Events.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class Events::ObserveTimeStep<ScalarWave::System<DIM(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Instantiations/TimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                          \\\n  template class ChangeTimeStepperOrder<ScalarWave::System<DIM(data)>>; \\\n  template class CleanHistory<ScalarWave::System<DIM(data)>>;           \\\n  template class RecordTimeStepperData<ScalarWave::System<DIM(data)>>;  \\\n  template class UpdateU<ScalarWave::System<DIM(data)>, false>;         \\\n  template class UpdateU<ScalarWave::System<DIM(data)>, true>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Instantiations/VolumeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Evolution/Systems/ScalarWave/TimeDerivative.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                \\\n  template void evolution::dg::Actions::detail::volume_terms<                 \\\n      ::ScalarWave::TimeDerivative<DIM(data)>>(                               \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::dt, typename ::ScalarWave::System<DIM(                      \\\n                          data)>::variables_tag::tags_list>>*>                \\\n          dt_vars_ptr,                                                        \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::Flux,                                                       \\\n          typename ::ScalarWave::System<DIM(data)>::flux_variables,           \\\n          tmpl::size_t<DIM(data)>, Frame::Inertial>>*>                        \\\n          volume_fluxes,                                                      \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::deriv,                                                      \\\n          typename ::ScalarWave::System<DIM(data)>::gradient_variables,       \\\n          tmpl::size_t<DIM(data)>, Frame::Inertial>>*>                        \\\n          partial_derivs,                                                     \\\n      const gsl::not_null<Variables<typename ::ScalarWave::System<DIM(        \\\n          data)>::compute_volume_time_derivative_terms::temporary_tags>*>     \\\n          temporaries,                                                        \\\n      const gsl::not_null<Variables<db::wrap_tags_in<                         \\\n          ::Tags::div,                                                        \\\n          db::wrap_tags_in<                                                   \\\n              ::Tags::Flux,                                                   \\\n              typename ::ScalarWave::System<DIM(data)>::flux_variables,       \\\n              tmpl::size_t<DIM(data)>, Frame::Inertial>>>*>                   \\\n          div_fluxes,                                                         \\\n      const Variables<typename ::ScalarWave::System<DIM(                      \\\n          data)>::variables_tag::tags_list>& evolved_vars,                    \\\n      const ::dg::Formulation dg_formulation, const Mesh<DIM(data)>& mesh,    \\\n      [[maybe_unused]] const tnsr::I<DataVector, DIM(data), Frame::Inertial>& \\\n          inertial_coordinates,                                               \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical,     \\\n                            Frame::Inertial>&                                 \\\n          logical_to_inertial_inverse_jacobian,                               \\\n      [[maybe_unused]] const Scalar<DataVector>* const det_inverse_jacobian,  \\\n      const std::optional<tnsr::I<DataVector, DIM(data), Frame::Inertial>>&   \\\n          mesh_velocity,                                                      \\\n      const std::optional<Scalar<DataVector>>& div_mesh_velocity,             \\\n      const Scalar<DataVector>& pi,                                           \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi,             \\\n      const Scalar<DataVector>& gamma2);                                      \\\n  INSTANTIATE_PARTIAL_DERIVATIVES_WITH_SYSTEM(ScalarWave::System<DIM(data)>,  \\\n                                              DIM(data), Frame::Inertial)     \\\n  INSTANTIATE_CARTOON_PARTIAL_DERIVATIVES_WITH_SYSTEM(                        \\\n      ScalarWave::System<DIM(data)>, DIM(data), Frame::Inertial)\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/MomentumDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/MomentumDensity.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarWave {\ntemplate <size_t SpatialDim>\nvoid momentum_density(\n    const gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*>\n        result,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi) {\n  for (size_t i = 0; i < SpatialDim; i++) {\n    result->get(i) = get(pi) * phi.get(i);\n  }\n}\n\ntemplate <size_t SpatialDim>\ntnsr::i<DataVector, SpatialDim, Frame::Inertial> momentum_density(\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi) {\n  tnsr::i<DataVector, SpatialDim, Frame::Inertial> result{get(pi).size()};\n  momentum_density(make_not_null(&result), pi, phi);\n  return result;\n}\n\n}  // namespace ScalarWave\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template void ScalarWave::momentum_density(                               \\\n      const gsl::not_null<tnsr::i<DataVector, DIM(data), Frame::Inertial>*> \\\n          result,                                                           \\\n      const Scalar<DataVector>& pi,                                         \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi);          \\\n  template tnsr::i<DataVector, DIM(data), Frame::Inertial>                  \\\n  ScalarWave::momentum_density(                                             \\\n      const Scalar<DataVector>& pi,                                         \\\n      const tnsr::i<DataVector, DIM(data), Frame::Inertial>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/MomentumDensity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ScalarWave {\n/// @{\n/*!\n * \\brief Computes the momentum density of the scalar wave system.\n *\n * Below is the function used to calculate the momentum density.\n *\n * \\f{align*}\n * P_i = \\Pi \\times \\Phi_i\n * \\f}\n */\ntemplate <size_t SpatialDim>\nvoid momentum_density(\n    gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*> result,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi);\n\ntemplate <size_t SpatialDim>\ntnsr::i<DataVector, SpatialDim, Frame::Inertial> momentum_density(\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi);\n/// @}\n\nnamespace Tags {\n/// \\brief Computes the momentum density using ScalarWave::momentum_density()\ntemplate <size_t SpatialDim>\nstruct MomentumDensityCompute : MomentumDensity<SpatialDim>, db::ComputeTag {\n  using argument_tags = tmpl::list<Pi, Phi<SpatialDim>>;\n\n  using return_type = tnsr::i<DataVector, SpatialDim, Frame::Inertial>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*> result,\n      const Scalar<DataVector>&,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&)>(\n      &momentum_density<SpatialDim>);\n\n  using base = MomentumDensity<SpatialDim>;\n};\n}  // namespace Tags\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class ScalarWaveSystem.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/ScalarWave/Equations.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarWave/TimeDerivative.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup EvolutionSystemsGroup\n * \\brief Items related to evolving the scalar wave equation.\n *\n * The equations of motion for the system augmented with constraint damping\n * terms are given by Eq. (15), (23) and (24) of \\cite Holst2004wt (setting\n * background spacetime to Minkowskian):\n *\n * \\f{align*}\n * \\partial_t \\psi =& -\\Pi \\\\\n * \\partial_t \\Pi  =& -\\partial^i \\Phi_i \\\\\n * \\partial_t \\Phi_i =& -\\partial_i \\Pi + \\gamma_2 (\\partial_i \\psi - \\Phi_i)\n * \\f}\n *\n * In our implementation here, to disable the constraint damping terms,\n * set \\f$\\gamma_2 = 0\\f$.\n */\nnamespace ScalarWave {\n\ntemplate <size_t Dim>\nstruct System {\n  using boundary_conditions_base = BoundaryConditions::BoundaryCondition<Dim>;\n\n  static constexpr bool is_in_flux_conservative_form = false;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = Dim;\n\n  using variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>>;\n  using flux_variables = tmpl::list<>;\n  using gradient_variables = tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>;\n\n  using compute_volume_time_derivative_terms = TimeDerivative<Dim>;\n\n  using compute_largest_characteristic_speed =\n      Tags::ComputeLargestCharacteristicSpeed;\n};\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines DataBox tags for scalar wave system\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarWave/TagsDeclarations.hpp\"\n\nclass DataVector;\n\nnamespace ScalarWave::Tags {\n/*!\n * \\brief The scalar field.\n */\nstruct Psi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Auxiliary variable which is analytically the negative time derivative\n * of the scalar field.\n * \\details If \\f$\\Psi\\f$ is the scalar field then we define\n * \\f$\\Pi = -\\partial_t \\Psi\\f$\n */\nstruct Pi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Auxiliary variable which is analytically the spatial derivative of the\n * scalar field.\n * \\details If \\f$\\Psi\\f$ is the scalar field then we define\n * \\f$\\Phi_{i} = \\partial_i \\Psi\\f$\n */\ntemplate <size_t Dim>\nstruct Phi : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\nstruct ConstraintGamma2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Tag for the one-index constraint of the ScalarWave system\n *\n * For details on how this is defined and computed, see\n * `OneIndexConstraintCompute`.\n */\ntemplate <size_t Dim>\nstruct OneIndexConstraint : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n/*!\n * \\brief Tag for the two-index constraint of the ScalarWave system\n *\n * For details on how this is defined and computed, see\n * `TwoIndexConstraintCompute`.\n */\ntemplate <size_t Dim>\nstruct TwoIndexConstraint : db::SimpleTag {\n  using type = tnsr::ij<DataVector, Dim, Frame::Inertial>;\n};\n\n/// @{\n/// \\brief Tags corresponding to the characteristic fields of the flat-spacetime\n/// scalar-wave system.\n///\n/// \\details For details on how these are defined and computed, \\see\n/// CharacteristicSpeedsCompute\nstruct VPsi : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\ntemplate <size_t Dim>\nstruct VZero : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\nstruct VPlus : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct VMinus : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n/// @}\n\ntemplate <size_t Dim>\nstruct CharacteristicSpeeds : db::SimpleTag {\n  using type = std::array<DataVector, 4>;\n};\n\ntemplate <size_t Dim>\nstruct CharacteristicFields : db::SimpleTag {\n  using type = Variables<tmpl::list<VPsi, VZero<Dim>, VPlus, VMinus>>;\n};\n\ntemplate <size_t Dim>\nstruct EvolvedFieldsFromCharacteristicFields : db::SimpleTag {\n  using type = Variables<tmpl::list<Psi, Pi, Phi<Dim>>>;\n};\n\n/// The energy density of the scalar wave\ntemplate <size_t Dim>\nstruct EnergyDensity : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// The momentum density of the scalar wave\ntemplate <size_t Dim>\nstruct MomentumDensity : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\n}  // namespace ScalarWave::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\brief Tags for the ScalarWave evolution system\nnamespace ScalarWave::Tags {\nstruct Psi;\nstruct Pi;\ntemplate <size_t Dim>\nstruct Phi;\n\nstruct ConstraintGamma2;\n\ntemplate <size_t Dim>\nstruct OneIndexConstraint;\ntemplate <size_t Dim>\nstruct TwoIndexConstraint;\n\nstruct VPsi;\ntemplate <size_t Dim>\nstruct VZero;\nstruct VPlus;\nstruct VMinus;\n\ntemplate <size_t Dim>\nstruct CharacteristicSpeeds;\ntemplate <size_t Dim>\nstruct CharacteristicFields;\ntemplate <size_t Dim>\nstruct EvolvedFieldsFromCharacteristicFields;\ntemplate <size_t Dim>\nstruct EnergyDensity;\ntemplate <size_t Dim>\nstruct MomentumDensity;\n}  // namespace ScalarWave::Tags\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/ScalarWave/TimeDerivative.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarWave {\ntemplate <size_t Dim>\nevolution::dg::TimeDerivativeDecisions<Dim> TimeDerivative<Dim>::apply(\n    const gsl::not_null<Scalar<DataVector>*> dt_psi,\n    const gsl::not_null<Scalar<DataVector>*> dt_pi,\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> dt_phi,\n\n    const gsl::not_null<Scalar<DataVector>*> result_gamma2,\n\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& d_psi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& d_pi,\n    const tnsr::ij<DataVector, Dim, Frame::Inertial>& d_phi,\n\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n    const Scalar<DataVector>& gamma2) {\n  // The constraint damping parameter gamma2 is needed for boundary corrections,\n  // which means we need it as a temporary tag in order to project it to the\n  // boundary. We prevent slicing/projecting directly from the volume to prevent\n  // people from adding many compute tags to the DataBox, instead preferring\n  // quantities be computed inside the TimeDerivative/Flux/Source structs. This\n  // keeps related code together and makes figuring out where something is\n  // computed a lot easier.\n  *result_gamma2 = gamma2;\n\n  get(*dt_psi) = -get(pi);\n  get(*dt_pi) = -get<0, 0>(d_phi);\n  for (size_t d = 1; d < Dim; ++d) {\n    get(*dt_pi) -= d_phi.get(d, d);\n  }\n  for (size_t d = 0; d < Dim; ++d) {\n    dt_phi->get(d) = -d_pi.get(d) + get(gamma2) * (d_psi.get(d) - phi.get(d));\n  }\n  return {true};\n}\n\ntemplate class TimeDerivative<1>;\ntemplate class TimeDerivative<2>;\ntemplate class TimeDerivative<3>;\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Systems/ScalarWave/TimeDerivative.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\nclass DataVector;\n/// \\endcond\n\nnamespace ScalarWave {\n/*!\n * \\brief Compute the time derivatives for scalar wave system\n */\ntemplate <size_t Dim>\nstruct TimeDerivative {\n  using temporary_tags = tmpl::list<Tags::ConstraintGamma2>;\n  using argument_tags =\n      tmpl::list<Tags::Pi, Tags::Phi<Dim>, Tags::ConstraintGamma2>;\n\n  static evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      // Time derivatives returned by reference. All the tags in the\n      // variables_tag in the system struct.\n      gsl::not_null<Scalar<DataVector>*> dt_psi,\n      gsl::not_null<Scalar<DataVector>*> dt_pi,\n      gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*> dt_phi,\n\n      gsl::not_null<Scalar<DataVector>*> result_gamma2,\n\n      // Partial derivative arguments. Listed in the system struct as\n      // gradient_variables.\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_psi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_pi,\n      const tnsr::ij<DataVector, Dim, Frame::Inertial>& d_phi,\n\n      // Terms list in argument_tags above\n      const Scalar<DataVector>& pi,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n      const Scalar<DataVector>& gamma2);\n};\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/Evolution/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Filter.hpp\n  )\n"
  },
  {
    "path": "src/Evolution/Tags/Filter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Groups the filtering configurations in the input file.\n */\nstruct FilteringGroup {\n  static std::string name() { return \"Filtering\"; }\n  static constexpr Options::String help = \"Options for filtering\";\n};\n\n/*!\n * \\ingroup OptionTagsGroup\n * \\brief The option tag that retrieves the parameters for the filter\n * from the input file\n */\ntemplate <typename FilterType>\nstruct Filter {\n  static std::string name() { return pretty_type::name<FilterType>(); }\n  static constexpr Options::String help = \"Options for the filter\";\n  using type = FilterType;\n  using group = FilteringGroup;\n};\n}  // namespace OptionTags\n\nnamespace Filters {\nnamespace Tags {\n/*!\n * \\brief The global cache tag for the filter\n *\n * Also checks if the specified blocks are actually in the domain.\n */\ntemplate <typename FilterType>\nstruct Filter : db::SimpleTag {\n  using type = FilterType;\n  template <typename Metavariables>\n  using option_tags =\n      tmpl::list<::OptionTags::Filter<FilterType>,\n                 domain::OptionTags::DomainCreator<Metavariables::volume_dim>>;\n\n  static constexpr bool pass_metavariables = true;\n  template <typename Metavariables>\n  static FilterType create_from_options(\n      const FilterType& filter,\n      const std::unique_ptr<DomainCreator<Metavariables::volume_dim>>&\n          domain_creator) {\n    const auto& blocks_to_filter = filter.blocks_to_filter();\n\n    // If this is nullopt, then we use all blocks\n    if (blocks_to_filter.has_value()) {\n      const auto& block_names = domain_creator->block_names();\n      const auto& block_groups = domain_creator->block_groups();\n\n      if (block_names.size() == 0) {\n        ERROR(\n            \"The domain chosen doesn't use block names, but the Filter tag has \"\n            \"specified block names to use.\");\n      }\n\n      // The name must either be a block or a block group\n      for (const std::string& block_to_filter : blocks_to_filter.value()) {\n        const auto block_name_iter = alg::find(block_names, block_to_filter);\n        if (block_name_iter == block_names.end() and\n            block_groups.count(block_to_filter) == 0) {\n          ERROR(\"Specified block (group) name '\"\n                << block_to_filter\n                << \"' is not a block name or a block \"\n                   \"group. Existing blocks are:\\n\"\n                << block_names << \"\\nExisting block groups are:\\n\"\n                << keys_of(block_groups));\n        }\n      }\n    }\n\n    return filter;\n  }\n};\n}  // namespace Tags\n}  // namespace Filters\n"
  },
  {
    "path": "src/Evolution/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"Options/String.hpp\"\n\nnamespace evolution {\nnamespace OptionTags {\n\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Groups option tags related to the time evolution, e.g. time step and\n * time stepper.\n */\nstruct Group {\n  static std::string name() { return \"Evolution\"; }\n  static constexpr Options::String help{\"Options for the time evolution\"};\n};\n\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Groups option tags related to the evolution system.\n *\n * The option tags for the evolution system should be placed in a subgroup that\n * carries the system name. See e.g. `OptionTags::ValenciaDivCleanGroup`.\n */\nstruct SystemGroup {\n  static std::string name() { return \"EvolutionSystem\"; }\n  static constexpr Options::String help{\"The system of hyperbolic PDEs\"};\n};\n\n}  // namespace OptionTags\n}  // namespace evolution\n"
  },
  {
    "path": "src/Evolution/TagsDomain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/TagsDomain.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::domain::Tags {\ntemplate <size_t Dim>\nvoid DivMeshVelocityCompute<Dim>::function(\n    const gsl::not_null<std::optional<Scalar<DataVector>>*> div_mesh_velocity,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    const ::Mesh<Dim>& mesh,\n    const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jac_logical_to_inertial) {\n  if (mesh_velocity.has_value()) {\n    *div_mesh_velocity =\n        divergence(*mesh_velocity, mesh, inv_jac_logical_to_inertial);\n    return;\n  }\n  *div_mesh_velocity = std::nullopt;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) template struct DivMeshVelocityCompute<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace evolution::domain::Tags\n"
  },
  {
    "path": "src/Evolution/TagsDomain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace evolution {\nnamespace domain {\nnamespace Tags {\n/// The divergence of the frame velocity\ntemplate <size_t Dim>\nstruct DivMeshVelocityCompute : db::ComputeTag,\n                                ::domain::Tags::DivMeshVelocity {\n  using base = DivMeshVelocity;\n  using return_type = typename base::type;\n\n  static void function(\n      gsl::not_null<std::optional<Scalar<DataVector>>*> div_mesh_velocity,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const ::Mesh<Dim>& mesh,\n      const ::InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                              Frame::Inertial>& inv_jac_logical_to_inertial);\n\n  using argument_tags =\n      tmpl::list<::domain::Tags::MeshVelocity<Dim, Frame::Inertial>,\n                 ::domain::Tags::Mesh<Dim>,\n                 ::domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                 Frame::Inertial>>;\n};\n}  // namespace Tags\n}  // namespace domain\n}  // namespace evolution\n"
  },
  {
    "path": "src/Evolution/Triggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EvolutionTriggers)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  SeparationLessThan.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  SeparationLessThan.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  EventsAndTriggers\n  FunctionsOfTime\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "src/Evolution/Triggers/SeparationLessThan.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Triggers/SeparationLessThan.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/EagerMath/Norms.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n\nnamespace Triggers {\ntemplate <bool UseGridCentersFunctionOfTime>\nSeparationLessThan<UseGridCentersFunctionOfTime>::SeparationLessThan(\n    const double separation)\n    : separation_(separation) {}\n\ntemplate <bool UseGridCentersFunctionOfTime>\nbool SeparationLessThan<UseGridCentersFunctionOfTime>::operator()(\n    const double time, const ::Domain<3>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const tnsr::I<double, 3, Frame::Grid>& grid_object_center_a,\n    const tnsr::I<double, 3, Frame::Grid>& grid_object_center_b) const {\n  const std::unordered_map<std::string, ExcisionSphere<3>>& excision_spheres =\n      domain.excision_spheres();\n\n  const auto check_excision_sphere =\n      [&excision_spheres](const std::string& object) {\n        if (excision_spheres.count(\"ExcisionSphere\" + object) != 1) {\n          ERROR(\n              \"SeparationLessThan trigger expects an excision sphere named \"\n              \"'ExcisionSphere\"\n              << object\n              << \"' in the domain, but there isn't one. Choose a \"\n                 \"DomainCreator that has this excision sphere.\");\n        }\n        if (not excision_spheres.at(\"ExcisionSphere\" + object)\n                    .is_time_dependent()) {\n          ERROR(\"SeparationLessThan expects ExcisionSphere\"\n                << object << \" to be time dependent, but it is not.\");\n        }\n      };\n\n  check_excision_sphere(get_output(domain::ObjectLabel::A));\n  check_excision_sphere(get_output(domain::ObjectLabel::B));\n\n  const auto& grid_to_inertial_map_a =\n      excision_spheres.at(\"ExcisionSphere\" + get_output(domain::ObjectLabel::A))\n          .moving_mesh_grid_to_inertial_map();\n  const auto& grid_to_inertial_map_b =\n      excision_spheres.at(\"ExcisionSphere\" + get_output(domain::ObjectLabel::B))\n          .moving_mesh_grid_to_inertial_map();\n\n  const tnsr::I<double, 3, Frame::Inertial> inertial_object_center_a =\n      grid_to_inertial_map_a(grid_object_center_a, time, functions_of_time);\n  const tnsr::I<double, 3, Frame::Inertial> inertial_object_center_b =\n      grid_to_inertial_map_b(grid_object_center_b, time, functions_of_time);\n\n  const tnsr::I<double, 3, Frame::Inertial> position_difference =\n      tenex::evaluate<ti::I>(inertial_object_center_a(ti::I) -\n                             inertial_object_center_b(ti::I));\n\n  const double calculated_separation =\n      sqrt(square(get<0>(position_difference)) +\n           square(get<1>(position_difference)) +\n           square(get<2>(position_difference)));\n\n  return calculated_separation < separation_;\n}\n\ntemplate <bool UseGridCentersFunctionOfTime>\nbool SeparationLessThan<UseGridCentersFunctionOfTime>::operator()(\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  if (dynamic_cast<const domain::FunctionsOfTime::SettleToConstantQuaternion*>(\n          functions_of_time.at(\"Rotation\").get()) != nullptr) {\n    return false;\n  }\n  const DataVector fot = functions_of_time.at(\"GridCenters\")->func(time)[0];\n  const double separation =\n      std::sqrt(square(fot[0] - fot[3]) + square(fot[1] - fot[4]) +\n                square(fot[2] - fot[5]));\n  return separation < separation_;\n}\n\ntemplate <bool UseGridCentersFunctionOfTime>\nvoid SeparationLessThan<UseGridCentersFunctionOfTime>::pup(PUP::er& p) {\n  p | separation_;\n}\n\ntemplate <bool UseGridCentersFunctionOfTime>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID SeparationLessThan<UseGridCentersFunctionOfTime>::my_PUP_ID =\n    0;\n\ntemplate class SeparationLessThan<true>;\ntemplate class SeparationLessThan<false>;\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Evolution/Triggers/SeparationLessThan.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\nstruct Distorted;\n}  // namespace Frame\nnamespace domain::Tags {\ntemplate <size_t VolumeDim>\nstruct Domain;\nstruct FunctionsOfTime;\ntemplate <size_t VolumeDim>\nstruct Element;\ntemplate <ObjectLabel Label>\nstruct ObjectCenter;\n}  // namespace domain::Tags\ntemplate <size_t VolumeDim>\nstruct Domain;\ntemplate <size_t VolumeDim>\nstruct Element;\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Triggers {\n/*!\n * \\brief A standard trigger that monitors the separation between two objects\n * (either black holes or neutron stars, typically) is below a threshold.\n *\n * If `UseGridCentersFunctionOfTime` is `true` then the distance between the\n * two object is computed using the `GridCenters` function of time. It is\n * assume that the `GridCenters` function of time holds the grid coordinates,\n * `{x_A, y_A, z_A, x_B, y_B, z_B}`. The Cartesian distance is computed\n * between these two locations.\n *\n * If `UseGridCentersFunctionOfTime` is `false` then\n * `domain::Tags::ObjectCenter<A>` and `domain::Tags::ObjectCenter<B>` (in the\n * inertial frame) are used to compute the separation.\n *\n * Once the separation is smaller than (or equal to) the input separation, then\n * the `operator()` returns true.\n *\n * \\note If `UseGridCentersFunctionOfTime` is `false` then this trigger requires\n * that `domain::Tags::ObjectCenter<domain::ObjectLabel::A>` and\n * `domain::Tags::ObjectCenter<domain::ObjectLabel::B>` are in the DataBox. It\n * also requires that there are two ExcisionSphere%s in the Domain named\n * `ExcisionSphereA/B` and that these ExcisionSphere%s have had time dependent\n * maps injected into them. The coordinate maps from these ExcisionSphere%s will\n * be used to calculate the separation in the inertial frame.\n *\n * \\note If `UseGridCentersFunctionOfTime` is `true` then this trigger\n * requires that `GridCenters` and `Rotation` are a valid function of time and\n * will only trigger `true` if the `Rotation` function of time is not a\n * `domain::FunctionsOfTime::SettleToConstantQuaternion`.\n */\ntemplate <bool UseGridCentersFunctionOfTime>\nclass SeparationLessThan : public Trigger {\n public:\n  /// \\cond\n  SeparationLessThan() = default;\n  explicit SeparationLessThan(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SeparationLessThan);  // NOLINT\n  /// \\endcond\n\n  struct Value {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Separation of the two horizons or neutron star centers to compare \"\n        \"against.\"};\n  };\n\n  using options = tmpl::list<Value>;\n  static constexpr Options::String help{\n      \"Trigger when the separation between the two horizons or neutron star \"\n      \"centers is less than a certain distance.\"};\n\n  explicit SeparationLessThan(double separation);\n\n  using argument_tags = tmpl::conditional_t<\n      UseGridCentersFunctionOfTime,\n      tmpl::list<Tags::Time, domain::Tags::FunctionsOfTime>,\n      tmpl::list<Tags::Time, domain::Tags::Domain<3>,\n                 domain::Tags::FunctionsOfTime,\n                 domain::Tags::ObjectCenter<domain::ObjectLabel::A>,\n                 domain::Tags::ObjectCenter<domain::ObjectLabel::B>>>;\n\n  bool operator()(\n      double time, const ::Domain<3>& domain,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const tnsr::I<double, 3, Frame::Grid>& grid_object_center_a,\n      const tnsr::I<double, 3, Frame::Grid>& grid_object_center_b) const;\n\n  bool operator()(double time,\n                  const std::unordered_map<\n                      std::string,\n                      std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n                      functions_of_time) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double separation_{std::numeric_limits<double>::signaling_NaN()};\n};\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Evolution/TypeTraits.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Evolution/Protocols.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace evolution {\n/// @{\n/// Helper metafunction that checks if the class `T` is marked as numeric\n/// initial data.\ntemplate <typename T>\nusing is_numeric_initial_data =\n    tt::conforms_to<T, protocols::NumericInitialData>;\n\ntemplate <typename T>\nconstexpr bool is_numeric_initial_data_v =\n    tt::conforms_to_v<T, protocols::NumericInitialData>;\n/// @}\n}  // namespace evolution\n"
  },
  {
    "path": "src/Evolution/VariableFixing/Actions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace VariableFixing {\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// \\ingroup VariableFixingGroup\n/// \\brief Adjust variables with a variable fixer.\n///\n/// Typically this action is called to adjust either conservative or primitive\n/// variables when they violate physical constraints.  See the individual\n/// variable fixers in the VariableFixing namespace for more details.\n///\n/// Uses:\n/// - DataBox:\n///   - Metavariables::variable_fixer::argument_tags\n/// - GlobalCache:\n///   - Metavariables::variable_fixer\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: Metavariables::variable_fixer::return_tags\ntemplate <typename VariableFixer>\nstruct FixVariables {\n  using const_global_cache_tags =\n      tmpl::list<Tags::VariableFixer<VariableFixer>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent,\n            Requires<tmpl::size<DbTagsList>::value != 0> = nullptr>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& variable_fixer = get<Tags::VariableFixer<VariableFixer>>(cache);\n    db::mutate_apply(variable_fixer, make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY VariableFixing)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FixToAtmosphere.cpp\n  LimitLorentzFactor.cpp\n  ParameterizedDeleptonization.cpp\n  RadiallyFallingFloor.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Actions.hpp\n  FixToAtmosphere.hpp\n  LimitLorentzFactor.hpp\n  ParameterizedDeleptonization.hpp\n  RadiallyFallingFloor.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  GeneralRelativity\n  Hydro\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "src/Evolution/VariableFixing/FixToAtmosphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n\n#include <limits>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace VariableFixing {\nstd::ostream& operator<<(std::ostream& os,\n                         const FixReconstructedStateToAtmosphere& t) {\n  switch (t) {\n    case FixReconstructedStateToAtmosphere::Always:\n      return os << \"Always\";\n    case FixReconstructedStateToAtmosphere::AtDgFdInterfaceOnly:\n      return os << \"AtDgFdInterfaceOnly\";\n    case FixReconstructedStateToAtmosphere::OnFdOnly:\n      return os << \"OnFdOnly\";\n    case FixReconstructedStateToAtmosphere::Never:\n      return os << \"Never\";\n    default:\n      ERROR(\"Unknown floating point type, must be Float or Double\");\n  }\n}\n}  // namespace VariableFixing\n\ntemplate <>\nVariableFixing::FixReconstructedStateToAtmosphere\nOptions::create_from_yaml<VariableFixing::FixReconstructedStateToAtmosphere>::\n    create<void>(const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  for (const auto t :\n       {VariableFixing::FixReconstructedStateToAtmosphere::Always,\n        VariableFixing::FixReconstructedStateToAtmosphere::AtDgFdInterfaceOnly,\n        VariableFixing::FixReconstructedStateToAtmosphere::OnFdOnly,\n        VariableFixing::FixReconstructedStateToAtmosphere::Never}) {\n    if (type_read == get_output(t)) {\n      return t;\n    }\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read << \"\\\" to FixReconstructedStateToAtmosphere.\");\n}\n\nnamespace VariableFixing {\ntemplate <size_t Dim>\nFixToAtmosphere<Dim>::FixToAtmosphere(\n    const double density_of_atmosphere, const double density_cutoff,\n    const std::optional<VelocityLimitingOptions> velocity_limiting,\n    const std::optional<KappaLimitingOptions> kappa_limiting,\n    const Options::Context& context)\n    : density_of_atmosphere_(density_of_atmosphere),\n      density_cutoff_(density_cutoff),\n      velocity_limiting_(velocity_limiting),\n      kappa_limiting_(kappa_limiting) {\n  if (density_of_atmosphere_ > density_cutoff_) {\n    PARSE_ERROR(context, \"The cutoff density (\"\n                             << density_cutoff_\n                             << \") must be greater than or equal to the \"\n                                \"density value in the atmosphere (\"\n                             << density_of_atmosphere_ << ')');\n  }\n\n  if (velocity_limiting_.has_value()) {\n    if (velocity_limiting_->atmosphere_max_velocity < 0.0) {\n      PARSE_ERROR(context,\n                  \"The AtmosphereMaxVelocity must be non-negative but is \"\n                      << velocity_limiting_->atmosphere_max_velocity);\n    }\n    if (velocity_limiting_->near_atmosphere_max_velocity < 0.0) {\n      PARSE_ERROR(context,\n                  \"The NearAtmosphereMaxVelocity must be non-negative but is \"\n                      << velocity_limiting_->near_atmosphere_max_velocity);\n    }\n    if (velocity_limiting_->atmosphere_max_velocity >\n        velocity_limiting_->near_atmosphere_max_velocity) {\n      PARSE_ERROR(context,\n                  \"The AtmosphereMaxVelocity (\"\n                      << velocity_limiting_->atmosphere_max_velocity\n                      << \") must be smaller NearAtmosphereMaxVelocity (\"\n                      << velocity_limiting_->near_atmosphere_max_velocity\n                      << \").\");\n    }\n    if (velocity_limiting_->atmosphere_density_cutoff < 0.0) {\n      PARSE_ERROR(context,\n                  \"The AtmosphereDensityCutoff must be non-negative but is \"\n                      << velocity_limiting_->atmosphere_density_cutoff);\n    }\n    if (velocity_limiting_->transition_density_bound < 0.0) {\n      PARSE_ERROR(context,\n                  \"The TransitionDensityBound must be non-negative but is \"\n                      << velocity_limiting_->transition_density_bound);\n    }\n    if (velocity_limiting_->atmosphere_density_cutoff <\n        density_of_atmosphere_) {\n      PARSE_ERROR(\n          context,\n          \"The AtmosphereDensityCutoff (\"\n              << velocity_limiting_->atmosphere_density_cutoff\n              << \") must be greater than or equal to the DensityOfAtmosphere (\"\n              << density_of_atmosphere_ << \").\");\n    }\n    if (velocity_limiting_->transition_density_bound <\n        velocity_limiting_->atmosphere_density_cutoff) {\n      PARSE_ERROR(context, \"The TransitionDensityBound (\"\n                               << velocity_limiting_->transition_density_bound\n                               << \") must be greater than or equal to the \"\n                                  \"AtmosphereDensityCutoff (\"\n                               << velocity_limiting_->atmosphere_density_cutoff\n                               << \").\");\n    }\n  }\n\n  if (kappa_limiting_.has_value()) {\n    if (kappa_limiting_->density_lower_bound >\n        kappa_limiting_->density_upper_bound) {\n      PARSE_ERROR(context,\n                  \"The DensityLowerBound (\"\n                      << kappa_limiting_->density_lower_bound\n                      << \") must be less than or equal to DensityUpperBound(\"\n                      << kappa_limiting_->density_upper_bound << \").\");\n    }\n    if (kappa_limiting_->min_temperature.has_value() and\n        kappa_limiting_->min_temperature.value() < 0.0) {\n      PARSE_ERROR(context, \"The MinTemperature must be non-negative but is \"\n                               << kappa_limiting_->min_temperature.value());\n    }\n  }\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid FixToAtmosphere<Dim>::pup(PUP::er& p) {\n  p | density_of_atmosphere_;\n  p | density_cutoff_;\n  p | velocity_limiting_;\n  p | kappa_limiting_;\n}\n\ntemplate <size_t Dim>\ntemplate <size_t ThermodynamicDim>\nvoid FixToAtmosphere<Dim>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n    const gsl::not_null<Scalar<DataVector>*> pressure,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const Scalar<DataVector>& electron_fraction,\n    const tnsr::ii<DataVector, Dim, Frame::Inertial>& spatial_metric,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) const {\n  for (size_t i = 0; i < rest_mass_density->get().size(); i++) {\n    if (UNLIKELY(rest_mass_density->get()[i] < density_cutoff_)) {\n      set_density_to_atmosphere(rest_mass_density, specific_internal_energy,\n                                temperature, pressure, electron_fraction,\n                                equation_of_state, i);\n    }\n    if (velocity_limiting_.has_value()) {\n      apply_velocity_limit(spatial_velocity, lorentz_factor, *rest_mass_density,\n                           spatial_metric, i);\n    }\n\n    // For 2D & 3D EoS, we also need to limit the temperature / energy\n    if constexpr (ThermodynamicDim > 1) {\n      bool changed_temperature = false;\n      if (const double min_temperature =\n              equation_of_state.temperature_lower_bound();\n          get(*temperature)[i] < min_temperature) {\n        get(*temperature)[i] = min_temperature;\n        changed_temperature = true;\n      }\n\n      // We probably need a better maximum temperature as well, but this is not\n      // as well defined. To be discussed once implementation needs improvement.\n      if (const double max_temperature =\n              equation_of_state.temperature_upper_bound();\n          get(*temperature)[i] > max_temperature) {\n        get(*temperature)[i] = max_temperature;\n        changed_temperature = true;\n      }\n\n      if (kappa_limiting_.has_value()) {\n        changed_temperature |=\n            apply_kappa_limit(temperature, *rest_mass_density,\n                              electron_fraction, equation_of_state, i);\n      }\n\n      if (changed_temperature) {\n        if constexpr (ThermodynamicDim == 2) {\n          specific_internal_energy->get()[i] =\n              get(equation_of_state\n                      .specific_internal_energy_from_density_and_temperature(\n                          Scalar<double>{rest_mass_density->get()[i]},\n                          Scalar<double>{get(*temperature)[i]}));\n          pressure->get()[i] =\n              get(equation_of_state.pressure_from_density_and_energy(\n                  Scalar<double>{rest_mass_density->get()[i]},\n                  Scalar<double>{specific_internal_energy->get()[i]}));\n        } else {\n          specific_internal_energy->get()[i] =\n              get(equation_of_state\n                      .specific_internal_energy_from_density_and_temperature(\n                          Scalar<double>{rest_mass_density->get()[i]},\n                          Scalar<double>{get(*temperature)[i]},\n                          Scalar<double>{get(electron_fraction)[i]}));\n          pressure->get()[i] =\n              get(equation_of_state.pressure_from_density_and_temperature(\n                  Scalar<double>{rest_mass_density->get()[i]},\n                  Scalar<double>{temperature->get()[i]},\n                  Scalar<double>{get(electron_fraction)[i]}));\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <size_t ThermodynamicDim>\nvoid FixToAtmosphere<Dim>::set_density_to_atmosphere(\n    const gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const gsl::not_null<Scalar<DataVector>*> pressure,\n    const Scalar<DataVector>& electron_fraction,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state,\n    const size_t grid_index) const {\n  const Scalar<double> atmosphere_density{density_of_atmosphere_};\n  rest_mass_density->get()[grid_index] = get(atmosphere_density);\n  get(*temperature)[grid_index] = equation_of_state.temperature_lower_bound();\n\n  if constexpr (ThermodynamicDim == 1) {\n    pressure->get()[grid_index] =\n        get(equation_of_state.pressure_from_density(atmosphere_density));\n    specific_internal_energy->get()[grid_index] =\n        get(equation_of_state.specific_internal_energy_from_density(\n            atmosphere_density));\n  } else {\n    const Scalar<double> atmosphere_temperature{get(*temperature)[grid_index]};\n    if constexpr (ThermodynamicDim == 2) {\n      specific_internal_energy->get()[grid_index] =\n          get(equation_of_state\n                  .specific_internal_energy_from_density_and_temperature(\n                      atmosphere_density, atmosphere_temperature));\n      pressure->get()[grid_index] =\n          get(equation_of_state.pressure_from_density_and_energy(\n              atmosphere_density,\n              Scalar<double>{specific_internal_energy->get()[grid_index]}));\n    } else {\n      specific_internal_energy->get()[grid_index] =\n          get(equation_of_state\n                  .specific_internal_energy_from_density_and_temperature(\n                      Scalar<double>{get(*rest_mass_density)[grid_index]},\n                      Scalar<double>{get(*temperature)[grid_index]},\n                      Scalar<double>{get(electron_fraction)[grid_index]}));\n      pressure->get()[grid_index] =\n          get(equation_of_state.pressure_from_density_and_temperature(\n              Scalar<double>{get(*rest_mass_density)[grid_index]},\n              Scalar<double>{get(*temperature)[grid_index]},\n              Scalar<double>{get(electron_fraction)[grid_index]}));\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid FixToAtmosphere<Dim>::apply_velocity_limit(\n    const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n        spatial_velocity,\n    const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n    const Scalar<DataVector>& rest_mass_density,\n    const tnsr::ii<DataVector, Dim, Frame::Inertial>& spatial_metric,\n    const size_t grid_index) const {\n  if (get(rest_mass_density)[grid_index] >\n      velocity_limiting_->transition_density_bound) {\n    return;\n  }\n\n  if (get(rest_mass_density)[grid_index] <\n      velocity_limiting_->atmosphere_density_cutoff) {\n    for (size_t i = 0; i < Dim; ++i) {\n      spatial_velocity->get(i)[grid_index] =\n          velocity_limiting_->atmosphere_max_velocity;\n    }\n    if (LIKELY(velocity_limiting_->atmosphere_max_velocity == 0.0)) {\n      get(*lorentz_factor)[grid_index] = 1.0;\n      return;\n    }\n  }\n\n  double magnitude_of_velocity = 0.0;\n  for (size_t j = 0; j < Dim; ++j) {\n    magnitude_of_velocity += spatial_velocity->get(j)[grid_index] *\n                             spatial_velocity->get(j)[grid_index] *\n                             spatial_metric.get(j, j)[grid_index];\n    for (size_t k = j + 1; k < Dim; ++k) {\n      magnitude_of_velocity += 2.0 * spatial_velocity->get(j)[grid_index] *\n                               spatial_velocity->get(k)[grid_index] *\n                               spatial_metric.get(j, k)[grid_index];\n    }\n  }\n  if (UNLIKELY(get(rest_mass_density)[grid_index] <\n               velocity_limiting_->atmosphere_density_cutoff)) {\n    // Note: magnitude_of_velocity is squared still.\n    get(*lorentz_factor)[grid_index] = 1.0 / sqrt(1.0 - magnitude_of_velocity);\n    return;\n  }\n  magnitude_of_velocity = sqrt(magnitude_of_velocity);\n  const double scale_factor = (get(rest_mass_density)[grid_index] -\n                               velocity_limiting_->atmosphere_density_cutoff) /\n                              (velocity_limiting_->transition_density_bound -\n                               velocity_limiting_->atmosphere_density_cutoff);\n  if (const double max_mag_of_velocity =\n          scale_factor * velocity_limiting_->near_atmosphere_max_velocity;\n      magnitude_of_velocity > max_mag_of_velocity) {\n    const double one_over_max_mag_of_velocity = 1.0 / magnitude_of_velocity;\n    for (size_t j = 0; j < Dim; ++j) {\n      spatial_velocity->get(j)[grid_index] *=\n          max_mag_of_velocity * one_over_max_mag_of_velocity;\n    }\n    get(*lorentz_factor)[grid_index] =\n        1.0 / sqrt(1.0 - max_mag_of_velocity * max_mag_of_velocity);\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <size_t ThermodynamicDim>\nbool FixToAtmosphere<Dim>::apply_kappa_limit(\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state,\n    const size_t grid_index) const {\n  const KappaLimitingOptions& opts = kappa_limiting_.value();\n  double& temp = temperature->get()[grid_index];\n  const double& density = rest_mass_density.get()[grid_index];\n  const double& y_e = electron_fraction.get()[grid_index];\n  const double min_temperature = kappa_limiting_->min_temperature.value_or(\n      equation_of_state.temperature_lower_bound());\n\n  const auto get_pressure = [&density, &equation_of_state,\n                             &y_e](const double local_temperature) -> double {\n    if constexpr (ThermodynamicDim == 2) {\n      (void)y_e;\n      return get(equation_of_state.pressure_from_density_and_energy(\n          Scalar<double>{density},\n          equation_of_state\n              .specific_internal_energy_from_density_and_temperature(\n                  Scalar<double>{density}, Scalar<double>{local_temperature})));\n    } else {\n      return get(equation_of_state.pressure_from_density_and_temperature(\n          Scalar<double>{density}, Scalar<double>{local_temperature},\n          Scalar<double>{y_e}));\n    }\n  };\n\n  const auto impl = [&get_pressure, &min_temperature, &temp,\n                     &y_e](const double local_kappa_max) -> bool {\n    const double p_temp = get_pressure(temp);\n    const double p_min = get_pressure(min_temperature);\n    const double p_max = p_min * local_kappa_max;\n    if (p_temp > p_max) {\n      if (UNLIKELY((p_temp - p_max) * (p_min - p_max) > 0.0)) {\n        ERROR(\n            \"The root for the pressure function while applying the kappa \"\n            \"limiting strategy was not bound.\\n\"\n            \"T_min=\"\n            << min_temperature << \"\\np_min=\" << p_min << \"\\nT=\" << temp\n            << \"\\np_temp=\" << p_temp << \"\\np_max=\" << p_max << \"\\nYe=\" << y_e);\n      }\n      if (temp - min_temperature > 1.0e-13) {\n        temp = RootFinder::toms748(\n            [&get_pressure, &p_max](const double local_temperature) {\n              return get_pressure(local_temperature) - p_max;\n            },\n            min_temperature, temp, 1e-50, 1.0e-13);\n      } else {\n        temp = 0.5 * (temp + min_temperature);\n      }\n      return true;\n    }\n    return false;\n  };\n\n  using std::abs;\n  if (density < opts.density_lower_bound) {\n    if (abs(temp - min_temperature) > opts.eplison_kappa_minus * abs(temp)) {\n      temp = min_temperature;\n    }\n    return true;\n  } else if (density < opts.density_upper_bound) {\n    const double kappa_max =\n        1.0 +\n        opts.epsilon_kappa_max *\n            std::min((density - opts.density_lower_bound) /\n                         (opts.density_upper_bound - opts.density_lower_bound),\n                     1.0);\n    return impl(kappa_max);\n  } else if (kappa_limiting_.value().limit_above_density_upper_bound) {\n    return impl(1.0 + opts.epsilon_kappa_max);\n  }\n  return false;\n}\n\ntemplate <size_t Dim>\nbool operator==(const FixToAtmosphere<Dim>& lhs,\n                const FixToAtmosphere<Dim>& rhs) {\n  return lhs.density_of_atmosphere_ == rhs.density_of_atmosphere_ and\n         lhs.density_cutoff_ == rhs.density_cutoff_ and\n         lhs.velocity_limiting_ == rhs.velocity_limiting_ and\n         lhs.kappa_limiting_ == rhs.kappa_limiting_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const FixToAtmosphere<Dim>& lhs,\n                const FixToAtmosphere<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\nvoid FixToAtmosphere<Dim>::VelocityLimitingOptions::pup(PUP::er& p) {\n  p | atmosphere_max_velocity;\n  p | near_atmosphere_max_velocity;\n  p | atmosphere_density_cutoff;\n  p | transition_density_bound;\n}\n\ntemplate <size_t Dim>\nbool FixToAtmosphere<Dim>::VelocityLimitingOptions::operator==(\n    const VelocityLimitingOptions& rhs) const {\n  return atmosphere_max_velocity == rhs.atmosphere_max_velocity and\n         near_atmosphere_max_velocity == rhs.near_atmosphere_max_velocity and\n         atmosphere_density_cutoff == rhs.atmosphere_density_cutoff and\n         transition_density_bound == rhs.transition_density_bound;\n}\n\ntemplate <size_t Dim>\nbool FixToAtmosphere<Dim>::VelocityLimitingOptions::operator!=(\n    const VelocityLimitingOptions& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <size_t Dim>\nvoid FixToAtmosphere<Dim>::KappaLimitingOptions::pup(PUP::er& p) {\n  p | density_lower_bound;\n  p | eplison_kappa_minus;\n  p | density_upper_bound;\n  p | epsilon_kappa_max;\n  p | min_temperature;\n  p | limit_above_density_upper_bound;\n}\n\ntemplate <size_t Dim>\nbool FixToAtmosphere<Dim>::KappaLimitingOptions::operator==(\n    const KappaLimitingOptions& rhs) const {\n  return density_lower_bound == rhs.density_lower_bound and\n         eplison_kappa_minus == rhs.eplison_kappa_minus and\n         density_upper_bound == rhs.density_upper_bound and\n         epsilon_kappa_max == rhs.epsilon_kappa_max and\n         min_temperature == rhs.min_temperature and\n         limit_above_density_upper_bound == rhs.limit_above_density_upper_bound;\n}\n\ntemplate <size_t Dim>\nbool FixToAtmosphere<Dim>::KappaLimitingOptions::operator!=(\n    const KappaLimitingOptions& rhs) const {\n  return not(*this == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                     \\\n  template class FixToAtmosphere<DIM(data)>;                       \\\n  template bool operator==(const FixToAtmosphere<DIM(data)>& lhs,  \\\n                           const FixToAtmosphere<DIM(data)>& rhs); \\\n  template bool operator!=(const FixToAtmosphere<DIM(data)>& lhs,  \\\n                           const FixToAtmosphere<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define INSTANTIATION(r, data)                                                \\\n  template void FixToAtmosphere<DIM(data)>::operator()(                       \\\n      const gsl::not_null<Scalar<DataVector>*> rest_mass_density,             \\\n      const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,      \\\n      const gsl::not_null<tnsr::I<DataVector, DIM(data), Frame::Inertial>*>   \\\n          spatial_velocity,                                                   \\\n      const gsl::not_null<Scalar<DataVector>*> lorentz_factor,                \\\n      const gsl::not_null<Scalar<DataVector>*> pressure,                      \\\n      const gsl::not_null<Scalar<DataVector>*> temperature,                   \\\n      const Scalar<DataVector>& electron_fraction,                            \\\n      const tnsr::ii<DataVector, DIM(data), Frame::Inertial>& spatial_metric, \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>&        \\\n          equation_of_state) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (1, 2, 3))\n\n#undef DIM\n#undef THERMO_DIM\n#undef INSTANTIATION\n\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/FixToAtmosphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace VariableFixing {\n/// \\brief How to apply atmosphere to the reconstructed state.\nenum class FixReconstructedStateToAtmosphere : uint8_t {\n  Always,\n  AtDgFdInterfaceOnly,\n  OnFdOnly,\n  Never\n};\n\nstd::ostream& operator<<(std::ostream& os,\n                         const FixReconstructedStateToAtmosphere& t);\n}  // namespace VariableFixing\n\ntemplate <>\nstruct Options::create_from_yaml<\n    VariableFixing::FixReconstructedStateToAtmosphere> {\n  template <typename Metavariables>\n  static VariableFixing::FixReconstructedStateToAtmosphere create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nVariableFixing::FixReconstructedStateToAtmosphere\nOptions::create_from_yaml<VariableFixing::FixReconstructedStateToAtmosphere>::\n    create<void>(const Options::Option& options);\n\nnamespace VariableFixing {\n/*!\n * \\ingroup VariableFixingGroup\n * \\brief Fix the primitive variables to an atmosphere in low density regions\n *\n * If the rest mass density is below \\f$\\rho_{\\textrm{cutoff}}\\f$\n * (DensityCutoff), it is set to \\f$\\rho_{\\textrm{atm}}\\f$\n * (DensityOfAtmosphere), and the pressure, and specific internal energy (for\n * one-dimensional equations of state) are adjusted to satisfy the equation of\n * state.  For a two-dimensional equation of state, the specific internal energy\n * is set to zero.\n */\ntemplate <size_t Dim>\nclass FixToAtmosphere {\n  struct Disabled {};\n  struct FromEos {};\n\n public:\n  /// \\brief Rest mass density of the atmosphere\n  struct DensityOfAtmosphere {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\"Density of atmosphere\"};\n  };\n  /// \\brief Rest mass density at which to impose the atmosphere. Should be\n  /// greater than or equal to the density of the atmosphere.\n  struct DensityCutoff {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Density to impose atmosphere at. Must be >= rho_atm\"};\n  };\n\n  /*!\n   * \\brief Limit the velocity in and near the atmosphere.\n   *\n   * Let $v_{\\max}$ be the maximum magnitude of the\n   * velocity near the atmosphere, which we typically set to $10^{-4}$.\n   * We let $v_{\\mathrm{atm}}$ be the maximum magnitude of the velocity\n   * in the atmosphere, which we typically set to $0$. We then define\n   * the maximum magnitude of the spatial velocity to be\n   *\n   * \\f{align*}{\n   *   \\tilde{v}\n   *   &=\\begin{cases}\n   *     v_{\\mathrm{atm}}, & \\mathrm{if}\\; \\rho < \\rho_{v_-} \\   \\\n   *     v_{\\mathrm{atm}} + \\left(v_{\\max} - v_{\\mathrm{atm}}\\right)\n   *     \\frac{\\rho - \\rho_{v_-}}{\\rho_{v_+} - \\rho_{v_-}},\n   *                       & \\mathrm{if}\\;\\rho_{v_-} \\le \\rho < \\rho_{v_+}\n   *   \\end{cases}\n   * \\f}\n   *\n   * We then rescale the velocity by\n   *\n   * \\f{align*}{\n   *   v^i\\to v^i\\frac{\\tilde{v}}{\\sqrt{v^i\\gamma_{ij}v^j}}.\n   * \\f}\n   */\n  struct VelocityLimitingOptions {\n    struct AtmosphereMaxVelocity {\n      using type = double;\n      static constexpr Options::String help = {\n          \"The maximum velocity magnitude IN the atmosphere. Typically set to \"\n          \"0.\"};\n    };\n\n    struct NearAtmosphereMaxVelocity {\n      using type = double;\n      static constexpr Options::String help = {\n          \"The maximum velocity magnitude NEAR the atmosphere. Typically set \"\n          \"to 1e-4.\"};\n    };\n\n    struct AtmosphereDensityCutoff {\n      using type = double;\n      static constexpr Options::String help = {\n          \"The rest mass density cutoff below which the velocity magnitude is \"\n          \"limited to AtmosphereMaxVelocity. Typically set to \"\n          \"(10 or 20)*DensityOfAtmosphere.\"};\n    };\n\n    struct TransitionDensityBound {\n      using type = double;\n      static constexpr Options::String help = {\n          \"The rest mass density above which no velocity limiting is done. \"\n          \"Between \"\n          \"this value and AtmosphereDensityCutoff a linear transition in the \"\n          \"maximum magnitude of the velocity between AtmosphereMaxVelocity and \"\n          \"NearAtmosphereMaxVelocity is done. Typically set to \"\n          \"10*AtmosphereDensityCutoff.\"};\n    };\n    using options = tmpl::list<AtmosphereMaxVelocity, NearAtmosphereMaxVelocity,\n                               AtmosphereDensityCutoff, TransitionDensityBound>;\n    static constexpr Options::String help = {\n        \"Limit the velocity in and near the atmosphere.\"};\n\n    // NOLINTNEXTLINE(google-runtime-references)\n    void pup(PUP::er& p);\n\n    bool operator==(const VelocityLimitingOptions& rhs) const;\n    bool operator!=(const VelocityLimitingOptions& rhs) const;\n\n    double atmosphere_max_velocity{\n        std::numeric_limits<double>::signaling_NaN()};\n    double near_atmosphere_max_velocity{\n        std::numeric_limits<double>::signaling_NaN()};\n    double atmosphere_density_cutoff{\n        std::numeric_limits<double>::signaling_NaN()};\n    double transition_density_bound{\n        std::numeric_limits<double>::signaling_NaN()};\n  };\n  struct VelocityLimiting {\n    using type = Options::Auto<VelocityLimitingOptions, Disabled>;\n    static constexpr Options::String help = VelocityLimitingOptions::help;\n  };\n\n  /*!\n   * \\brief Options for limiting the temperature in the atmosphere by\n   * effectively limiting the polytropic constant, with a generalization for\n   * finite temperature equations of state.\n   *\n   * The basic density limit is fine for a cold EOS, but when a finite\n   * temperature EOS is used the temperature needs to be controlled in the\n   * atmosphere. While the basic bound of\n   * $T\\in[T_{\\mathrm{min}},T_{\\mathrm{max}}]$ is a necessary condition, it is\n   * often not sufficient for stability in the atmosphere. We define lower and\n   * upper bounds $\\rho_{\\tau_-}$ and $\\rho_{\\tau_+}$ for $\\rho$ where we apply\n   * different limiting behavior when $\\rho<\\rho_{\\tau_-}$,\n   * $\\rho\\in[\\rho_{\\tau_-},\\rho_{\\tau_+}]$, and $\\rho>\\rho_{\\tau_+}$.\n   *\n   * When $\\rho<\\rho_{\\tau_-}$ we set $T=T_{\\mathrm{min}}$ if\n   * $|T-T_{\\mathrm{min}}|>\\epsilon_{\\kappa_-} |T|$, otherwise we don't change\n   * $T$. SpEC uses $\\epsilon_{\\kappa_-}=10^{-3}$.\n   *\n   * When $\\rho\\in[\\rho_{\\tau_-},\\rho_{\\tau_+}]$ we apply a more complicated\n   * algorithm. We define\n   *\n   * \\f{align*}{\n   *   \\kappa_{\\mathrm{max}} =\n   *   1 +\n   *   \\epsilon_{\\kappa,\\mathrm{max}}\n   *   \\min\\left(\\frac{\\rho-\\rho_{\\tau_-}}{\\rho_{\\tau_+}-\\rho_{\\tau_-}},1\\right)\n   * \\f}\n   *\n   * where $\\epsilon_{\\kappa,\\mathrm{max}}\\ge0$ (set to 0.01 in SpEC). With\n   * $\\kappa_{\\mathrm{max}}$ computed we now limit the temperature. We define\n   * $p_{\\mathrm{min}}=p(\\rho,T_{\\mathrm{min}},Y_e)$, $p=p(\\rho,T,Y_e)$, and\n   * $p_{\\kappa_{\\mathrm{max}}}=p_{\\mathrm{min}}\\kappa_{\\mathrm{max}}$. We then\n   * need to find $T$ such that $p(\\rho, T, Y_e) = p_{\\kappa_{\\mathrm{max}}}$.\n   * We do this by finding the root of\n   *\n   * \\f{align*}{\n   *   f(T')=p(\\rho, T', Y_e) - p_{\\kappa_{\\mathrm{max}}}.\n   * \\f}\n   *\n   * We bracket the temperature $T'\\in[T_{\\min}, T]$, where $T$ is the\n   * unmodified temperature. We solve the equation using the TOMS748 algorithm\n   * to avoid the need for derivatives of the pressure with respect to the\n   * temperature. If $T-T_{\\min}<10^{-13}$ we set $T'=\\frac{1}{2}(T+T_{\\min})$\n   * to avoid any root find.\n   *\n   * When $\\rho>\\rho_{\\tau_+}$ we optionally can apply the same procedure as in\n   * the previous case, but with\n   * $\\kappa_\\mathrm{max}=1+\\epsilon_{\\kappa,\\mathrm{max}}$. This is not\n   * typically done.\n   */\n  struct KappaLimitingOptions {\n    struct DensityLowerBound {\n      using type = double;\n      static type lower_bound() { return 0.0; }\n      static constexpr Options::String help = {\n          \"Below this value we set T=T_min if |T-T_min|<EplisonKappaMinus \"\n          \"|T|. Typically set to about a factor of 20 larger than the \"\n          \"atmosphere density.\"};\n    };\n    struct EplisonKappaMinus {\n      using type = double;\n      static constexpr Options::String help = {\n          \"Used to limit the temperature in conjunction with \"\n          \"DensityLowerBound. See that text for the formula. Typically set \"\n          \"to 1.0e-3.\"};\n    };\n    struct DensityUpperBound {\n      using type = double;\n      static type lower_bound() { return 0.0; }\n      static constexpr Options::String help = {\n          \"The density below which we limit the temperature by limiting the \"\n          \"pressure (at fixed rho, Y_e) to 'p(T)<p_min kappa_max'. Typically \"\n          \"set a factor of 10 larger than DensityLowerBound.\"};\n    };\n    struct EpsilonKappaMax {\n      using type = double;\n      static constexpr Options::String help = {\n          \"The epsilon factor in the KappaMax computation multiplying the \"\n          \"transition factor. Typically set to 0.01.\"};\n    };\n    struct MinTemperature {\n      using type = Options::Auto<double, FromEos>;\n      static constexpr Options::String help = {\"The minimum temperature.\"};\n    };\n    struct LimitAboveDensityUpperBound {\n      using type = bool;\n      static constexpr Options::String help = {\n          \"If true then we limit the temperature using the KappaMax procedure \"\n          \"at all densities above DensityUpperBound, but with \"\n          \"`KappaMax=1+EplisonKappaMax`. Typically set to False.\"};\n    };\n    using options = tmpl::list<DensityLowerBound, EplisonKappaMinus,\n                               DensityUpperBound, EpsilonKappaMax,\n                               MinTemperature, LimitAboveDensityUpperBound>;\n    static constexpr Options::String help = {\n        \"If set then we apply a limiting precodure on the temperature near the \"\n        \"atmosphere based on essentially limiting the polytropic constant in a \"\n        \"Gamma-law equation of state.\"};\n\n    // NOLINTNEXTLINE(google-runtime-references)\n    void pup(PUP::er& p);\n\n    bool operator==(const KappaLimitingOptions& rhs) const;\n    bool operator!=(const KappaLimitingOptions& rhs) const;\n\n    double density_lower_bound{std::numeric_limits<double>::signaling_NaN()};\n    double eplison_kappa_minus{std::numeric_limits<double>::signaling_NaN()};\n    double density_upper_bound{std::numeric_limits<double>::signaling_NaN()};\n    double epsilon_kappa_max{std::numeric_limits<double>::signaling_NaN()};\n    std::optional<double> min_temperature{std::nullopt};\n    bool limit_above_density_upper_bound{false};\n  };\n  /// \\brief If set then we apply a limiting precodure on the temperature near\n  /// the atmosphere based on essentially limiting the polytropic constant in a\n  /// Gamma-law equation of state.\n  struct KappaLimiting {\n    using type = Options::Auto<KappaLimitingOptions, Disabled>;\n    static constexpr Options::String help = KappaLimitingOptions::help;\n  };\n\n  using options = tmpl::list<DensityOfAtmosphere, DensityCutoff,\n                             VelocityLimiting, KappaLimiting>;\n  static constexpr Options::String help = {\n      \"If the rest mass density is below DensityCutoff, it is set\\n\"\n      \"to DensityOfAtmosphere, and the pressure, and specific internal energy\\n\"\n      \"(for one-dimensional equations of state) are\\n\"\n      \"adjusted to satisfy the equation of state. For a two-dimensional\\n\"\n      \"equation of state, the specific internal energy is set to zero.\\n\"\n      \"In addition, the spatial velocity is set to zero, and the Lorentz\\n\"\n      \"factor is set to one.\\n\"};\n\n  FixToAtmosphere(double density_of_atmosphere, double density_cutoff,\n                  std::optional<VelocityLimitingOptions> velocity_limiting,\n                  std::optional<KappaLimitingOptions> kappa_limiting,\n                  const Options::Context& context = {});\n\n  FixToAtmosphere() = default;\n  FixToAtmosphere(const FixToAtmosphere& /*rhs*/) = default;\n  FixToAtmosphere& operator=(const FixToAtmosphere& /*rhs*/) = default;\n  FixToAtmosphere(FixToAtmosphere&& /*rhs*/) = default;\n  FixToAtmosphere& operator=(FixToAtmosphere&& /*rhs*/) = default;\n  ~FixToAtmosphere() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  using return_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n  using argument_tags = tmpl::list<hydro::Tags::ElectronFraction<DataVector>,\n                                   gr::Tags::SpatialMetric<DataVector, Dim>,\n                                   hydro::Tags::GrmhdEquationOfState>;\n\n  // for use in `db::mutate_apply`\n  template <size_t ThermodynamicDim>\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          spatial_velocity,\n      gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      const Scalar<DataVector>& electron_fraction,\n      const tnsr::ii<DataVector, Dim, Frame::Inertial>& spatial_metric,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n          equation_of_state) const;\n\n  /// @{\n  /// \\brief Other algorithmic decisions may depend on the atmosphere treatment\n  /// so provide access to the values.\n  double density_of_atmosphere() const { return density_of_atmosphere_; }\n  double density_cutoff() const { return density_cutoff_; }\n  const std::optional<VelocityLimitingOptions>& velocity_limiting() const {\n    return velocity_limiting_;\n  }\n  const std::optional<KappaLimitingOptions>& kappa_limiting() const {\n    return kappa_limiting_;\n  }\n  /// @}\n\n private:\n  template <size_t ThermodynamicDim>\n  void set_density_to_atmosphere(\n      gsl::not_null<Scalar<DataVector>*> rest_mass_density,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      const Scalar<DataVector>& electron_fraction,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n          equation_of_state,\n      size_t grid_index) const;\n\n  void apply_velocity_limit(\n      gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          spatial_velocity,\n      gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      const Scalar<DataVector>& rest_mass_density,\n      const tnsr::ii<DataVector, Dim, Frame::Inertial>& spatial_metric,\n      size_t grid_index) const;\n\n  template <size_t ThermodynamicDim>\n  bool apply_kappa_limit(\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& electron_fraction,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n          equation_of_state,\n      size_t grid_index) const;\n\n  template <size_t SpatialDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const FixToAtmosphere<SpatialDim>& lhs,\n                         const FixToAtmosphere<SpatialDim>& rhs);\n\n  double density_of_atmosphere_{std::numeric_limits<double>::signaling_NaN()};\n  double density_cutoff_{std::numeric_limits<double>::signaling_NaN()};\n  std::optional<VelocityLimitingOptions> velocity_limiting_{std::nullopt};\n  std::optional<KappaLimitingOptions> kappa_limiting_{std::nullopt};\n};\n\ntemplate <size_t Dim>\nbool operator!=(const FixToAtmosphere<Dim>& lhs,\n                const FixToAtmosphere<Dim>& rhs);\n\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/LimitLorentzFactor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/VariableFixing/LimitLorentzFactor.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace VariableFixing {\nLimitLorentzFactor::LimitLorentzFactor(const double max_density_cutoff,\n                                       const double lorentz_factor_cap,\n                                       const bool enable)\n    : max_density_cuttoff_(max_density_cutoff),\n      lorentz_factor_cap_(lorentz_factor_cap),\n      enable_(enable) {}\n\nvoid LimitLorentzFactor::pup(PUP::er& p) {\n  p | max_density_cuttoff_;\n  p | lorentz_factor_cap_;\n  p | enable_;\n}\n\nvoid LimitLorentzFactor::operator()(\n    const gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        spatial_velocity,\n    const Scalar<DataVector>& rest_mass_density) const {\n  if (not enable_) {\n    return;\n  }\n\n  constexpr size_t dim = 3;\n  const size_t number_of_grid_points = get(rest_mass_density).size();\n  for (size_t s = 0; s < number_of_grid_points; ++s) {\n    if (get(*lorentz_factor)[s] > lorentz_factor_cap_ and\n        get(rest_mass_density)[s] < max_density_cuttoff_) {\n      const double velocity_renorm_factor =\n          sqrt((1. - 1. / square(lorentz_factor_cap_)) /\n               (1. - 1. / square(get(*lorentz_factor)[s])));\n\n      get(*lorentz_factor)[s] = lorentz_factor_cap_;\n      for (size_t d = 0; d < dim; ++d) {\n        spatial_velocity->get(d)[s] *= velocity_renorm_factor;\n      }\n    }\n  }\n}\n\nbool operator==(const LimitLorentzFactor& lhs, const LimitLorentzFactor& rhs) {\n  return lhs.max_density_cuttoff_ == rhs.max_density_cuttoff_ and\n         lhs.lorentz_factor_cap_ == rhs.lorentz_factor_cap_ and\n         lhs.enable_ == rhs.enable_;\n}\n\nbool operator!=(const LimitLorentzFactor& lhs, const LimitLorentzFactor& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/LimitLorentzFactor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace VariableFixing {\n/*!\n *\\ingroup VariableFixingGroup\n * \\brief Limit the maximum Lorentz factor to LorentzFactorCap in regions where\n * the density is below MaxDensityCutoff.\n *\n * The Lorentz factor is set to LorentzFactorCap and the spatial velocity is\n * adjusted according to:\n * \\f{align}\n * v^i_{(\\textrm{new})} = \\sqrt{\\left(1 - \\frac{1}{W_{(\\textrm{new})}^2}\\right)\n * \\left(1 - \\frac{1}{W_{(\\textrm{old})}^2}\\right)^{-1}} v^i_{(\\textrm{old})}\n * \\f}\n */\nclass LimitLorentzFactor {\n public:\n  /// Do not apply the Lorentz factor cap above this density\n  struct MaxDensityCutoff {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Do not apply the Lorentz factor cap above this density\"};\n  };\n  /// Largest Lorentz factor allowed. If a larger one is found, normalize\n  /// velocity to have the Lorentz factor be this value.\n  struct LorentzFactorCap {\n    using type = double;\n    static type lower_bound() { return 1.0; }\n    static constexpr Options::String help = {\"Largest Lorentz factor allowed.\"};\n  };\n  /// Whether or not the limiting is enabled\n  struct Enable {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If true then the limiting is applied.\"};\n  };\n\n  using options = tmpl::list<MaxDensityCutoff, LorentzFactorCap, Enable>;\n  static constexpr Options::String help = {\n      \"Limit the maximum Lorentz factor to LorentzFactorCap in regions where \"\n      \"the\\n\"\n      \"density is below MaxDensityCutoff. The Lorentz factor is set to\\n\"\n      \"LorentzFactorCap and the spatial velocity is adjusted accordingly.\"};\n\n  LimitLorentzFactor(double max_density_cutoff, double lorentz_factor_cap,\n                     bool enable);\n\n  LimitLorentzFactor() = default;\n  LimitLorentzFactor(const LimitLorentzFactor& /*rhs*/) = default;\n  LimitLorentzFactor& operator=(const LimitLorentzFactor& /*rhs*/) = default;\n  LimitLorentzFactor(LimitLorentzFactor&& /*rhs*/) = default;\n  LimitLorentzFactor& operator=(LimitLorentzFactor&& /*rhs*/) = default;\n  ~LimitLorentzFactor() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  using return_tags = tmpl::list<hydro::Tags::LorentzFactor<DataVector>,\n                                 hydro::Tags::SpatialVelocity<DataVector, 3>>;\n\n  using argument_tags = tmpl::list<hydro::Tags::RestMassDensity<DataVector>>;\n\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> lorentz_factor,\n      gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> spatial_velocity,\n      const Scalar<DataVector>& rest_mass_density) const;\n\n private:\n  friend bool operator==(const LimitLorentzFactor& lhs,\n                         const LimitLorentzFactor& rhs);\n\n  double max_density_cuttoff_;\n  double lorentz_factor_cap_;\n  bool enable_;\n};\n\nbool operator!=(const LimitLorentzFactor& lhs, const LimitLorentzFactor& rhs);\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/ParameterizedDeleptonization.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/VariableFixing/ParameterizedDeleptonization.hpp\"\n\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace VariableFixing {\n\nParameterizedDeleptonization::ParameterizedDeleptonization(\n    const bool enable, const double high_density_scale,\n    const double low_density_scale,\n    const double electron_fraction_at_high_density,\n    const double electron_fraction_at_low_density,\n    const double electron_fraction_correction_scale,\n    const Options::Context& context)\n    : enable_(enable),\n      high_density_scale_(high_density_scale),\n      low_density_scale_(low_density_scale),\n      electron_fraction_at_high_density_(electron_fraction_at_high_density),\n      electron_fraction_at_low_density_(electron_fraction_at_low_density),\n      electron_fraction_half_sum_(0.5 * (electron_fraction_at_high_density +\n                                         electron_fraction_at_low_density)),\n      electron_fraction_half_difference_(0.5 *\n                                         (electron_fraction_at_high_density -\n                                          electron_fraction_at_low_density)),\n      electron_fraction_correction_scale_(electron_fraction_correction_scale) {\n  if (high_density_scale_ < low_density_scale_) {\n    PARSE_ERROR(context, \"The high density scale(\"\n                             << high_density_scale_\n                             << \") should be greater than the low\"\n                                \" density scale (\"\n                             << low_density_scale_ << ')');\n  }\n  // high density material should have a lower Ye\n  if (electron_fraction_at_high_density_ > electron_fraction_at_low_density_) {\n    PARSE_ERROR(context, \"The Ye at high density(\"\n                             << electron_fraction_at_high_density_\n                             << \") should be less than the Ye at low\"\n                                \" density (\"\n                             << electron_fraction_at_low_density_ << ')');\n  }\n}\n\n// NOLINTNEXTLINE(google-runtime-references)\nvoid ParameterizedDeleptonization::pup(PUP::er& p) {\n  p | enable_;\n  p | high_density_scale_;\n  p | low_density_scale_;\n  p | electron_fraction_at_high_density_;\n  p | electron_fraction_at_low_density_;\n  p | electron_fraction_half_sum_;\n  p | electron_fraction_half_difference_;\n  p | electron_fraction_correction_scale_;\n}\n\ntemplate <size_t ThermodynamicDim>\nvoid ParameterizedDeleptonization::operator()(\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const gsl::not_null<Scalar<DataVector>*> pressure,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    const Scalar<DataVector>& rest_mass_density,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) const {\n  // Turn off param deleptonization if:\n  // i) disabled\n  // ii) if max rest mass density > 2e14 g/cm^3.  Note, at this point, bounce\n  // has occured/is close and one should switch to proper neutrino transport.\n  // Formally, it should disable when rest mass density > 2e14 g/cm^3 AND\n  // entropy is also > 3 k_B/baryon, but entropy is not a variable yet.\n  if (LIKELY(enable_) and max(rest_mass_density.get()) < 3.24e-4) {\n    for (size_t i = 0; i < rest_mass_density.get().size(); i++) {\n      correct_electron_fraction(specific_internal_energy, electron_fraction,\n                                pressure, temperature, rest_mass_density,\n                                equation_of_state, i);\n    }\n  }\n}\n\ntemplate <size_t ThermodynamicDim>\nvoid ParameterizedDeleptonization::correct_electron_fraction(\n    [[maybe_unused]] const gsl::not_null<Scalar<DataVector>*>\n        specific_internal_energy,\n    const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    [[maybe_unused]] const gsl::not_null<Scalar<DataVector>*> pressure,\n    [[maybe_unused]] const gsl::not_null<Scalar<DataVector>*> temperature,\n    const Scalar<DataVector>& rest_mass_density,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state,\n    const size_t grid_index) const {\n  // calculate x\n  const auto x_of_rho = std::max(\n      -1.0,\n      std::min(1.0, (2.0 * std::log10(rest_mass_density.get()[grid_index]) -\n                     std::log10(high_density_scale_) -\n                     std::log10(low_density_scale_)) /\n                        (std::log10(high_density_scale_) -\n                         std::log10(low_density_scale_))));\n\n  // calculate ye_new\n  const auto electron_fraction_from_analytic =\n      electron_fraction_half_sum_ +\n      x_of_rho * electron_fraction_half_difference_ +\n      electron_fraction_correction_scale_ *\n          (1.0 - abs(x_of_rho) +\n           4.0 * abs(x_of_rho) * (abs(x_of_rho) - 0.5) * (abs(x_of_rho) - 1.0));\n\n  // Ye can only decrease with this prescription\n  if (electron_fraction_from_analytic < electron_fraction->get()[grid_index]) {\n    electron_fraction->get()[grid_index] = electron_fraction_from_analytic;\n  }\n\n  if constexpr (ThermodynamicDim == 3) {\n    pressure->get()[grid_index] =\n        get(equation_of_state.pressure_from_density_and_energy(\n            Scalar<double>{rest_mass_density.get()[grid_index]},\n            Scalar<double>{specific_internal_energy->get()[grid_index]},\n            Scalar<double>{electron_fraction->get()[grid_index]}));\n\n    temperature->get()[grid_index] =\n        get(equation_of_state.temperature_from_density_and_energy(\n            Scalar<double>{rest_mass_density.get()[grid_index]},\n            Scalar<double>{specific_internal_energy->get()[grid_index]},\n            Scalar<double>{electron_fraction->get()[grid_index]}));\n  }\n}\n\nbool operator==(const ParameterizedDeleptonization& lhs,\n                const ParameterizedDeleptonization& rhs) {\n  return lhs.enable_ == rhs.enable_ and\n         lhs.high_density_scale_ == rhs.high_density_scale_ and\n         lhs.low_density_scale_ == rhs.low_density_scale_ and\n         lhs.electron_fraction_at_high_density_ ==\n             rhs.electron_fraction_at_high_density_ and\n         lhs.electron_fraction_at_low_density_ ==\n             rhs.electron_fraction_at_low_density_ and\n         lhs.electron_fraction_half_sum_ == rhs.electron_fraction_half_sum_ and\n         lhs.electron_fraction_half_difference_ ==\n             rhs.electron_fraction_half_difference_ and\n         lhs.electron_fraction_correction_scale_ ==\n             rhs.electron_fraction_correction_scale_;\n}\n\nbool operator!=(const ParameterizedDeleptonization& lhs,\n                const ParameterizedDeleptonization& rhs) {\n  return not(lhs == rhs);\n}\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                       \\\n  template class ParameterizedDeleptonization;                       \\\n  template bool operator==(const ParameterizedDeleptonization& lhs,  \\\n                           const ParameterizedDeleptonization& rhs); \\\n  template bool operator!=(const ParameterizedDeleptonization& lhs,  \\\n                           const ParameterizedDeleptonization& rhs);\n\n#undef INSTANTIATION\n\n#define INSTANTIATION(r, data)                                           \\\n  template void ParameterizedDeleptonization::operator()(                \\\n      const gsl::not_null<Scalar<DataVector>*> specific_internal_energy, \\\n      const gsl::not_null<Scalar<DataVector>*> electron_fraction,        \\\n      const gsl::not_null<Scalar<DataVector>*> pressure,                 \\\n      const gsl::not_null<Scalar<DataVector>*> temperature,              \\\n      const Scalar<DataVector>& rest_mass_density,                       \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>&   \\\n          equation_of_state) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef THERMO_DIM\n#undef INSTANTIATION\n\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/ParameterizedDeleptonization.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace VariableFixing {\n\n/*!\n * \\ingroup VariableFixingGroup\n * \\brief Adjust the electron fraction (Ye) based on rest mass density (rho).\n *\n * Based on \\cite Liebendorfer2005 spherically symmetric Boltzmann calculations,\n * during the collapse phase just before a core-collapse supernova, the electron\n * fraction naturally follows the rest mass density.  Intuitively, the higher\n * the density, the more electrons are captured onto protons, leading to\n * neutrino production.  As these neutrinos diffuse out of the center of the\n * collapsing star, the total number of leptons near center of the CCSN\n * drops---the process of deleptonization.\n *\n * https://iopscience.iop.org/article/10.1086/466517\n */\nclass ParameterizedDeleptonization {\n public:\n  /// \\brief Use an analytic expression vs rest mass density profile\n  ///\n  /// The option to choose between analytic and tabulated will be added in the\n  /// future.  If analytic is chosen (currently the only option), use the below\n  /// parameters.  If tabulated is chosen, a Ye (rho) profile will need to be\n  /// provided.\n  //   struct DeleptonizationFormat {\n  //     using type = double;\n  //     static type lower_bound() { return 0.0; }\n  //     static constexpr Options::String help = {\"Choose an 'analytic' or\n  //     'tabulated' expression to express electron fraction as a function of\n  //     rest mass density.\"};\n  //   };\n\n  /// \\brief Enable parameterized deleptonizations\n  struct Enable {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether or not to activate parameterized deleptonization for \"\n        \"supernovae.\"};\n  };\n\n  /// \\brief Density near the center of the supernova at bounce, above which\n  /// the central Ye is assumed\n  ///\n  /// In practice: rho(high) ~ 2x10^13 g / cm^3\n  struct HighDensityScale {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"High end of density scale for parameterized deleptonization.\"};\n  };\n  /// \\brief Density near the Silicon-Oxygen interface, below which the lower\n  /// Ye is assumed\n  ///\n  /// In practice: rho(low) ~ 2x10^7 g / cm^3\n  struct LowDensityScale {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Low end of density scale for parameterized deleptonization.\"};\n  };\n  /// \\brief Electron fraction of material when the rest mass density is above\n  /// HighDensityScale\n  ///\n  /// In practice: Y_e (high density) ~ 0.28.\n  struct ElectronFractionAtHighDensity {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 0.5; }\n    static constexpr Options::String help = {\n        \"For densities above HighDensityScale, the electron fraction will \"\n        \"take this value.\"};\n  };\n  /// \\brief Electron fraction of material when the rest mass density is below\n  /// LowDensityScale\n  ///\n  /// In practice: Y_e (low density) ~ 0.5\n  struct ElectronFractionAtLowDensity {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 0.5; }\n    static constexpr Options::String help = {\n        \"For densities below LowDensityScale, the electron fraction will \"\n        \"take this value.\"};\n  };\n\n  /// \\brief Electron fraction correction term.  The larger this value, the\n  /// higher the Ye of matter at densities between LowDensityScale and\n  /// HighDensityScale.\n  ///\n  /// In practice: Y_e (correction) ~ 0.035\n  struct ElectronFractionCorrectionScale {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"For densities between low and high limits, a higher value of \"\n        \"ElectronFractionCorrectionScale will increase the value of Ye.\"};\n  };\n\n  using options =\n      tmpl::list<Enable, HighDensityScale, LowDensityScale,\n                 ElectronFractionAtHighDensity, ElectronFractionAtLowDensity,\n                 ElectronFractionCorrectionScale>;\n  static constexpr Options::String help = {\n      \"Set electron fraction based on rest mass density.  \"\n      \"(Low/High)DensityScale sets the limits of the density, beyond which \"\n      \"the ElectronFractionAt(Low/High)Density is assumed.  At intermediate \"\n      \"densities, the higher the ElectronFractionCorrectionScale, the higher \"\n      \"the Ye.\"};\n\n  ParameterizedDeleptonization(bool enable, double high_density_scale,\n                               double low_density_scale,\n                               double electron_fraction_at_high_density,\n                               double electron_fraction_at_low_density,\n                               double electron_fraction_correction_scale,\n                               const Options::Context& context = {});\n\n  ParameterizedDeleptonization() = default;\n  ParameterizedDeleptonization(const ParameterizedDeleptonization& /*rhs*/) =\n      default;\n  ParameterizedDeleptonization& operator=(\n      const ParameterizedDeleptonization& /*rhs*/) = default;\n  ParameterizedDeleptonization(ParameterizedDeleptonization&& /*rhs*/) =\n      default;\n  ParameterizedDeleptonization& operator=(\n      ParameterizedDeleptonization&& /*rhs*/) = default;\n  ~ParameterizedDeleptonization() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  // Quantities being mutated\n  using return_tags =\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::Temperature<DataVector>>;\n\n  // Things you want from DataBox that won't be change and are passed in as\n  // const-refs\n  using argument_tags = tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                                   hydro::Tags::GrmhdEquationOfState>;\n\n  // for use in `db::mutate_apply`\n  template <size_t ThermodynamicDim>\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      const Scalar<DataVector>& rest_mass_density,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n          equation_of_state) const;\n\n private:\n  template <size_t ThermodynamicDim>\n  void correct_electron_fraction(\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      const Scalar<DataVector>& rest_mass_density,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n          equation_of_state,\n      size_t grid_index) const;\n\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const ParameterizedDeleptonization& lhs,\n                         const ParameterizedDeleptonization& rhs);\n\n  bool enable_{false};\n  double high_density_scale_{std::numeric_limits<double>::signaling_NaN()};\n  double low_density_scale_{std::numeric_limits<double>::signaling_NaN()};\n  double electron_fraction_at_high_density_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double electron_fraction_at_low_density_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double electron_fraction_half_sum_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double electron_fraction_half_difference_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double electron_fraction_correction_scale_{\n      std::numeric_limits<double>::signaling_NaN()};\n};\nbool operator!=(const ParameterizedDeleptonization& lhs,\n                const ParameterizedDeleptonization& rhs);\n\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/RadiallyFallingFloor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/VariableFixing/RadiallyFallingFloor.hpp\"\n\n#include <algorithm>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace VariableFixing {\n\ntemplate <size_t Dim>\nRadiallyFallingFloor<Dim>::RadiallyFallingFloor(\n    const double minimum_radius_at_which_to_apply_floor,\n    const double rest_mass_density_scale, const double rest_mass_density_power,\n    const double pressure_scale, const double pressure_power)\n    : minimum_radius_at_which_to_apply_floor_(\n          minimum_radius_at_which_to_apply_floor),\n      rest_mass_density_scale_(rest_mass_density_scale),\n      rest_mass_density_power_(rest_mass_density_power),\n      pressure_scale_(pressure_scale),\n      pressure_power_(pressure_power) {}\n\ntemplate <size_t Dim>\nvoid RadiallyFallingFloor<Dim>::pup(PUP::er& p) {  // NOLINT\n  p | minimum_radius_at_which_to_apply_floor_;\n  p | rest_mass_density_scale_;\n  p | rest_mass_density_power_;\n  p | pressure_scale_;\n  p | pressure_power_;\n}\n\ntemplate <size_t Dim>\ntemplate <size_t ThermodynamicDim>\nvoid RadiallyFallingFloor<Dim>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> density,\n    const gsl::not_null<Scalar<DataVector>*> pressure,\n    const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n    const gsl::not_null<Scalar<DataVector>*> temperature,\n    [[maybe_unused]] const gsl::not_null<Scalar<DataVector>*> electron_fraction,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) const {\n  const auto radii = magnitude(coords);\n\n  for (size_t i = 0; i < density->get().size(); i++) {\n    const double& radius = radii.get()[i];\n    if (UNLIKELY(radius < minimum_radius_at_which_to_apply_floor_)) {\n      continue;\n    }\n\n    if constexpr (ThermodynamicDim == 1) {\n      density->get()[i] = std::max(\n          density->get()[i],\n          rest_mass_density_scale_ * pow(radius, rest_mass_density_power_));\n\n      // For the 1D EoS, density will be used to calculate the remaining\n      // primitives, even overwriting pressure.  This decision prioritizes\n      // thermodynamic consistency over the user defined pressure profile.\n      pressure->get()[i] = get(equation_of_state.pressure_from_density(\n          Scalar<double>{density->get()[i]}));\n\n      specific_internal_energy->get()[i] =\n          get(equation_of_state.specific_internal_energy_from_density(\n              Scalar<double>{density->get()[i]}));\n\n      temperature->get()[i] = get(equation_of_state.temperature_from_density(\n          Scalar<double>{density->get()[i]}));\n    } else if constexpr (ThermodynamicDim == 2) {\n      density->get()[i] = std::max(\n          density->get()[i],\n          rest_mass_density_scale_ * pow(radius, rest_mass_density_power_));\n\n      pressure->get()[i] = std::max(\n          pressure->get()[i], pressure_scale_ * pow(radius, pressure_power_));\n\n      specific_internal_energy->get()[i] = get(\n          equation_of_state.specific_internal_energy_from_density_and_pressure(\n              Scalar<double>{density->get()[i]},\n              Scalar<double>{pressure->get()[i]}));\n\n      temperature->get()[i] =\n          get(equation_of_state.temperature_from_density_and_energy(\n              Scalar<double>{density->get()[i]},\n              Scalar<double>{specific_internal_energy->get()[i]}));\n\n    } else if constexpr (ThermodynamicDim == 3) {\n      density->get()[i] = std::max(\n          density->get()[i],\n          rest_mass_density_scale_ * pow(radius, rest_mass_density_power_));\n\n      temperature->get()[i] =\n          std::max(density->get()[i] * temperature->get()[i],\n                   pressure_scale_ * pow(radius, pressure_power_)) /\n          density->get()[i];\n\n      specific_internal_energy->get()[i] =\n          get(equation_of_state\n                  .specific_internal_energy_from_density_and_temperature(\n                      Scalar<double>{density->get()[i]},\n                      Scalar<double>{temperature->get()[i]},\n                      Scalar<double>{electron_fraction->get()[i]}));\n      pressure->get()[i] =\n          get(equation_of_state.pressure_from_density_and_energy(\n              Scalar<double>{density->get()[i]},\n              Scalar<double>{specific_internal_energy->get()[i]},\n              Scalar<double>{electron_fraction->get()[i]}));\n    } else {\n      ERROR(\"RadiallyFallingFloor: Must specify 1D, 2D, or 3D EoS\");\n    }\n  }\n}\n\ntemplate <size_t LocalDim>\nbool operator==(const RadiallyFallingFloor<LocalDim>& lhs,\n                const RadiallyFallingFloor<LocalDim>& rhs) {\n  return lhs.minimum_radius_at_which_to_apply_floor_ ==\n             rhs.minimum_radius_at_which_to_apply_floor_ and\n         lhs.rest_mass_density_scale_ == rhs.rest_mass_density_scale_ and\n         lhs.rest_mass_density_power_ == rhs.rest_mass_density_power_ and\n         lhs.pressure_scale_ == rhs.pressure_scale_ and\n         lhs.pressure_power_ == rhs.pressure_power_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const RadiallyFallingFloor<Dim>& lhs,\n                const RadiallyFallingFloor<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                              \\\n  template class RadiallyFallingFloor<GET_DIM(data)>;                       \\\n  template bool operator==(const RadiallyFallingFloor<GET_DIM(data)>& lhs,  \\\n                           const RadiallyFallingFloor<GET_DIM(data)>& rhs); \\\n  template bool operator!=(const RadiallyFallingFloor<GET_DIM(data)>& lhs,  \\\n                           const RadiallyFallingFloor<GET_DIM(data)>& rhs);\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define INSTANTIATION(r, data)                                           \\\n  template void RadiallyFallingFloor<GET_DIM(data)>::operator()(         \\\n      gsl::not_null<Scalar<DataVector>*> density,                        \\\n      gsl::not_null<Scalar<DataVector>*> pressure,                       \\\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,       \\\n      gsl::not_null<Scalar<DataVector>*> temperature,                    \\\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,              \\\n      const tnsr::I<DataVector, GET_DIM(data), Frame::Inertial>& coords, \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>&   \\\n          equation_of_state) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (1, 2, 3))\n#undef INSTANTIATION\n#undef THERMO_DIM\n#undef GET_DIM\n\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/RadiallyFallingFloor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nclass DataVector;\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace Tags\n}  // namespace domain\nnamespace hydro {\nnamespace Tags {\ntemplate <typename DataType>\nstruct Pressure;\ntemplate <typename DataType>\nstruct RestMassDensity;\ntemplate <typename DataType>\nstruct SpecificInternalEnergy;\ntemplate <typename DataType>\nstruct Temperature;\ntemplate <typename DataType>\nstruct ElectronFraction;\n}  // namespace Tags\n}  // namespace hydro\n/// \\endcond\n\n/// Contains all variable fixers.\nnamespace VariableFixing {\n\n/// \\ingroup VariableFixingGroup\n/// \\brief Applies a pressure and density floor dependent on the distance\n/// to the origin.\n///\n/// Applies the floors:\n/// \\f$\\rho(r) \\geq \\rho_{\\mathrm{fl}}(r) = C_\\rho r^{k_\\rho}\\f$\n/// and \\f$P(r) \\geq P_{\\mathrm{fl}}(r) = C_p r^{k_p}\\f$\n/// when \\f$ r > r_{min}\\f$, where \\f$C_\\rho\\f$ is given by the option\n/// `ScaleDensityFloor`, \\f$k_\\rho\\f$ is given by the option\n/// `PowerDensityFloor`,  \\f$C_p\\f$ is given by the option\n/// `ScalePressureFloor`, \\f$k_p\\f$ is given by the option\n/// `PowerPressureFloor`, and \\f$r_{min}\\f$ is given by the option\n/// `MinimumRadius`.\n///\n/// \\note In \\cite Porth2016rfi, the following floors are applied:\n/// \\f$\\rho(r) \\geq \\rho_{\\mathrm{fl}}(r) = 10^{-5}r^{-3/2}\\f$\n/// and \\f$P(r) \\geq P_{\\mathrm{fl}}(r) = \\frac{1}{3} \\times 10^{-7}r^{-5/2}\\f$\n///\n/// \\note For 3d equations of state we treat the product \\f$\\rho T\\f$ as the\n/// \"pressure\" that we floor and then compute the pressure from the\n/// temperature. The floor is applied after flooring the density.\ntemplate <size_t Dim>\nclass RadiallyFallingFloor {\n public:\n  /// \\brief The minimum radius at which to begin applying the floors on the\n  /// density and pressure.\n  struct MinimumRadius {\n    static constexpr Options::String help =\n        \"The radius at which to begin applying the lower bound.\";\n    using type = double;\n    static double lower_bound() { return 0.0; }\n  };\n\n  /// \\brief The scale of the floor of the rest mass density.\n  struct ScaleDensityFloor {\n    static constexpr Options::String help =\n        \"The rest mass density floor at r = 1.\";\n    using type = double;\n    static double lower_bound() { return 0.0; }\n  };\n\n  /// \\brief The power of the radius of the floor of the rest mass density.\n  struct PowerDensityFloor {\n    static constexpr Options::String help =\n        \"Radial power for the floor of the rest mass density.\";\n    using type = double;\n  };\n\n  /// \\brief The scale of the floor of the pressure.\n  struct ScalePressureFloor {\n    static constexpr Options::String help = \"The pressure floor at r = 1.\";\n    using type = double;\n    static double lower_bound() { return 0.0; }\n  };\n\n  /// \\brief The power of the radius of the floor of the pressure.\n  struct PowerPressureFloor {\n    static constexpr Options::String help =\n        \"The radial power for the floor of the pressure.\";\n    using type = double;\n  };\n\n  using options =\n      tmpl::list<MinimumRadius, ScaleDensityFloor, PowerDensityFloor,\n                 ScalePressureFloor, PowerPressureFloor>;\n  static constexpr Options::String help = {\n      \"Applies a pressure and density floor dependent on the distance to the \"\n      \"origin.\"};\n\n  RadiallyFallingFloor(double minimum_radius_at_which_to_apply_floor,\n                       double rest_mass_density_scale,\n                       double rest_mass_density_power, double pressure_scale,\n                       double pressure_power);\n\n  RadiallyFallingFloor() = default;\n  RadiallyFallingFloor(const RadiallyFallingFloor& /*rhs*/) = default;\n  RadiallyFallingFloor& operator=(const RadiallyFallingFloor& /*rhs*/) =\n      default;\n  RadiallyFallingFloor(RadiallyFallingFloor&& /*rhs*/) = default;\n  RadiallyFallingFloor& operator=(RadiallyFallingFloor&& /*rhs*/) = default;\n  ~RadiallyFallingFloor() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  using return_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 hydro::Tags::GrmhdEquationOfState>;\n\n  template <size_t ThermodynamicDim>\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> density,\n      gsl::not_null<Scalar<DataVector>*> pressure,\n      gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      gsl::not_null<Scalar<DataVector>*> temperature,\n      gsl::not_null<Scalar<DataVector>*> electron_fraction,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n      const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n          equation_of_state) const;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const RadiallyFallingFloor<LocalDim>& lhs,\n                         const RadiallyFallingFloor<LocalDim>& rhs);\n\n  double minimum_radius_at_which_to_apply_floor_{\n      std::numeric_limits<double>::signaling_NaN()};\n  double rest_mass_density_scale_{std::numeric_limits<double>::signaling_NaN()};\n  double rest_mass_density_power_{std::numeric_limits<double>::signaling_NaN()};\n  double pressure_scale_{std::numeric_limits<double>::signaling_NaN()};\n  double pressure_power_{std::numeric_limits<double>::signaling_NaN()};\n};\n\ntemplate <size_t Dim>\nbool operator!=(const RadiallyFallingFloor<Dim>& lhs,\n                const RadiallyFallingFloor<Dim>& rhs);\n\n}  // namespace VariableFixing\n"
  },
  {
    "path": "src/Evolution/VariableFixing/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Groups the variable fixer configurations in the input file.\n */\nstruct VariableFixingGroup {\n  static std::string name() { return \"VariableFixing\"; }\n  static constexpr Options::String help = \"Options for variable fixing\";\n};\n\n/*!\n * \\ingroup OptionTagsGroup\n * \\brief The global cache tag that retrieves the parameters for the variable\n * fixer from the input file.\n */\ntemplate <typename VariableFixerType>\nstruct VariableFixer {\n  static constexpr Options::String help = \"Options for the variable fixer\";\n  using type = VariableFixerType;\n  static std::string name() { return pretty_type::name<VariableFixerType>(); }\n  using group = VariableFixingGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief The global cache tag for the variable fixer\n */\ntemplate <typename VariableFixerType>\nstruct VariableFixer : db::SimpleTag {\n  using type = VariableFixerType;\n  using option_tags =\n      tmpl::list<::OptionTags::VariableFixer<VariableFixerType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static VariableFixerType create_from_options(\n      const VariableFixerType& variable_fixer) {\n    return variable_fixer;\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Executables/Benchmark/Benchmark.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wredundant-decls\"\n#include <benchmark/benchmark.h>\n#pragma GCC diagnostic pop\n#include <charm++.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n\n// Charm looks for this function but since we build without a main function or\n// main module we just have it be empty\nextern \"C\" void CkRegisterMainModule(void) {}\n\n// This file is an example of how to do microbenchmark with Google Benchmark\n// https://github.com/google/benchmark\n// For two examples in different anonymous namespaces\n\nnamespace {\n// Benchmark of push_back() in std::vector, following Chandler Carruth's talk\n// at CppCon in 2015,\n// https://www.youtube.com/watch?v=nXaxk27zwlk\n\n// void bench_create(benchmark::State &state) {\n//  while (state.KeepRunning()) {\n//    std::vector<int> v;\n//    benchmark::DoNotOptimize(&v);\n//    static_cast<void>(v);\n//  }\n// }\n// BENCHMARK(bench_create);\n\n// void bench_reserve(benchmark::State &state) {\n//  while (state.KeepRunning()) {\n//    std::vector<int> v;\n//    v.reserve(1);\n//    benchmark::DoNotOptimize(v.data());\n//  }\n// }\n// BENCHMARK(bench_reserve);\n\n// void bench_push_back(benchmark::State &state) {\n//  while (state.KeepRunning()) {\n//    std::vector<int> v;\n//    v.reserve(1);\n//    benchmark::DoNotOptimize(v.data());\n//    v.push_back(42);\n//    benchmark::ClobberMemory();\n//  }\n// }\n// BENCHMARK(bench_push_back);\n}  // namespace\n\nnamespace {\n// In this anonymous namespace is an example of microbenchmarking the\n// all_gradient routine for the GH system\n\ntemplate <size_t Dim>\nstruct Kappa : db::SimpleTag {\n  using type = tnsr::abb<DataVector, Dim, Frame::Grid>;\n};\ntemplate <size_t Dim>\nstruct Psi : db::SimpleTag {\n  using type = tnsr::aa<DataVector, Dim, Frame::Grid>;\n};\n\n// clang-tidy: don't pass be non-const reference\nvoid bench_all_gradient(benchmark::State& state) {  // NOLINT\n  constexpr const size_t pts_1d = 4;\n  constexpr const size_t Dim = 3;\n  const Mesh<Dim> mesh{pts_1d, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  domain::CoordinateMaps::Affine map1d(-1.0, 1.0, -1.0, 1.0);\n  using Map3d =\n      domain::CoordinateMaps::ProductOf3Maps<domain::CoordinateMaps::Affine,\n                                             domain::CoordinateMaps::Affine,\n                                             domain::CoordinateMaps::Affine>;\n  domain::CoordinateMap<Frame::ElementLogical, Frame::Grid, Map3d> map(\n      Map3d{map1d, map1d, map1d});\n\n  using VarTags = tmpl::list<Kappa<Dim>, Psi<Dim>>;\n  const InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Grid>\n      inv_jac = map.inv_jacobian(logical_coordinates(mesh));\n  const auto grid_coords = map(logical_coordinates(mesh));\n  Variables<VarTags> vars(mesh.number_of_grid_points(), 0.0);\n\n  while (state.KeepRunning()) {\n    benchmark::DoNotOptimize(partial_derivatives<VarTags>(vars, mesh, inv_jac));\n  }\n}\nBENCHMARK(bench_all_gradient);  // NOLINT\n}  // namespace\n\n// Ignore the warning about an extra ';' because some versions of benchmark\n// require it\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wpedantic\"\nBENCHMARK_MAIN();\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "src/Executables/Benchmark/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Since benchmarking is only interesting in release mode the executable isn't\n# added for Debug builds. Charm++'s main function is overridden with the main\n# from the Google Benchmark library. The executable is not added to the `all` make\n# target since it is only interesting in specific circumstances.\nif(\"${GoogleBenchmark_FOUND}\" AND NOT \"${CMAKE_BUILD_TYPE}\" STREQUAL \"Debug\")\n  set(executable Benchmark)\n\n  add_spectre_executable(\n    ${executable}\n    EXCLUDE_FROM_ALL\n    Benchmark.cpp\n    )\n\n  # Add specific libraries needed for the benchmark you are interested in.\n  target_link_libraries(\n    ${executable}\n    PRIVATE\n    CoordinateMaps\n    Domain\n    Informer\n    GoogleBenchmark\n    LinearOperators\n    Spectral\n    )\nendif()\n"
  },
  {
    "path": "src/Executables/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Benchmark)\nadd_subdirectory(ConvertComposeTable)\nadd_subdirectory(DebugPreprocessor)\nadd_subdirectory(ExportEquationOfStateForRotNS)\nadd_subdirectory(Examples)\nadd_subdirectory(ExportCoordinates)\nadd_subdirectory(ParallelInfo)\nadd_subdirectory(PreprocessCceWorldtube)\nadd_subdirectory(TimeStepperSummary)\nadd_subdirectory(WriteCceWorldtubeCoordsToFile)\n"
  },
  {
    "path": "src/Executables/ConvertComposeTable/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE ConvertComposeTable)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  ConvertComposeTable.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Boost::boost\n  Boost::program_options\n  DataStructures\n  H5\n  Informer\n  IO\n  Printf\n  )\n"
  },
  {
    "path": "src/Executables/ConvertComposeTable/ConvertComposeTable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <boost/program_options.hpp>\n\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"IO/ComposeTable.hpp\"\n#include \"IO/H5/EosTable.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n\n// Charm looks for this function but since we build without a main function or\n// main module we just have it be empty\nextern \"C\" void CkRegisterMainModule(void) {}\n\nnamespace {\nvoid convert_file(const std::string& compose_directory,\n                  const std::string& spectre_eos_filename,\n                  const std::string& spectre_eos_subfile) {\n  const io::ComposeTable compose_table(compose_directory);\n  h5::H5File<h5::AccessType::ReadWrite> spectre_file(spectre_eos_filename,\n                                                     true);\n  auto& spectre_eos = spectre_file.insert<h5::EosTable>(\n      spectre_eos_subfile,\n      std::vector<std::string>{\"number density\", \"temperature\",\n                               \"electron fraction\"},\n      std::vector{compose_table.number_density_bounds(),\n                  compose_table.temperature_bounds(),\n                  compose_table.electron_fraction_bounds()},\n      std::vector{compose_table.number_density_number_of_points(),\n                  compose_table.temperature_number_of_points(),\n                  compose_table.electron_fraction_number_of_points()},\n      std::vector{compose_table.number_density_log_spacing(),\n                  compose_table.temperature_log_spacing(),\n                  compose_table.electron_fraction_log_spacing()},\n      compose_table.beta_equilibrium());\n\n  // Now dump data into the EosTable file\n  for (const auto& [quantity_name, quantity_data] : compose_table.data()) {\n    spectre_eos.write_quantity(quantity_name, quantity_data);\n  }\n}\n}  // namespace\n\nint main(int argc, char** argv) {\n  namespace bpo = boost::program_options;\n  try {\n    bpo::options_description command_line_options(\n        \"This executable converts an ASCII formatted CompOSE 3d equation of \"\n        \"state table into a SpECTRE-formatted HDF5 table. This reduces the \"\n        \"file size by about a factor of 4. We don't use the CompOSE HDF5 \"\n        \"tables since that requires an HDF5 that works with Fortran.\\n\"\n        \"Note: support for 1d and 2d tables can be added if the CompOSE ASCII \"\n        \"reader is generalized to support them.\\n\\n\"\n        \"Generating the ASCII table using compose:\\n\"\n        \"1.\\n\"\n        \"Download from: https://compose.obspm.fr/software (there's a \"\n        \"GitLab link)\\n\\n\"\n        \"2.\\n\"\n        \"Build by running 'make' in the directory. This process will create \"\n        \"the 'compose' executable.\\n\\n\"\n        \"3.\\n\"\n        \"Download EOS from https://compose.obspm.fr/table We will use \"\n        \"https://compose.obspm.fr/eos/34 as an example. To download, use wget \"\n        \"on the link from the 'eos.zip' file, or download the 'eos.zip' file \"\n        \"directly.\\n\\n\"\n        \"4.\\n\"\n        \"Unzip the eos.zip file. This will create multiple 'eos.*' files in \"\n        \"the current directory.\\n\\n\"\n        \"5.\\n\"\n        \"Run the 'compose' executable in the directory with all the eos \"\n        \"files. There are 3 main options and you will run the executable \"\n        \"3 times. Each main option or 'task' has a bunch of numerical value \"\n        \"inputs.\"\n        \"\\n\"\n        \"Task 1\\n\"\n        \"How many regular thermodynamic quantities...\\n\"\n        \"7\\n\"\n        \"Please select the indices of the thermodynamic quantities...\\n\"\n        \" Index #           1 ?\"\n        \"1\\n\"\n        \" Index #           2 ?\"\n        \"2\\n\"\n        \"The remaining are: 3 4 5 7 12\\n\"\n        \"The following function values and derivatives of the free energy...\\n\"\n        \"1\\n\"\n        \"Please select the indices of the thermodynamic...\\n\"\n        \"1\\n\"\n        \"How many particles do you want to select for the file eos.table?\\n\"\n        \"0\\n\"\n        \"There are average mass, charge and neutron numbers...\\n\"\n        \"0\\n\"\n        \"There are microscopic data available of the following type:...\\n\"\n        \"0\\n\"\n        \"There are error estimates available of the following type:...\\n\"\n        \"0\\n\"\n        \"If successful, you should see new file 'eos.quantities' generated. \"\n        \"Now rerun compose for Task2.\\n\\n\"\n        \"Task 2\\n\"\n        \"Temperature interpolation order:\\n\"\n        \"3\\n\"\n        \"Baryon density interpolation order:\\n\"\n        \"3\\n\"\n        \"Hadronic charge fraction interpolation order:\\n\"\n        \"3\\n\"\n        \"beta-equilibrium\\n\"\n        \"0\\n\"\n        \"entropy per baryon\\n\"\n        \"0\\n\"\n        \"Please select the tabulation scheme for the parameters from\\n\"\n        \"1\\n\"\n        \"Get the lower and upper bounds as well as the grid points from the \"\n        \"compose website for your EOS. Spacing should be\\n\"\n        \"T: log\\n\"\n        \"n_b: log\\n\"\n        \"Y_q: linear\\n\"\n        \"You must enter the bounds as:\\n\"\n        \"lower upper\\n\"\n        \"If successful, you should see new file 'eos.parameters' generated. \"\n        \"Now rerun compose for Task3.\\n\\n\"\n        \"Task 3\\n\"\n        \"This will just run, no options needed, but it can take quite a long \"\n        \"time.\\n\"\n        \"If successful, it will list 'file eos.table written', along with the \"\n        \"respective labels [and units] of the columns, for example, '1 \"\n        \"temperature T [MeV]'.\\n\\n\"\n        \"Available options are\");\n\n    // clang-format off\n    command_line_options.add_options()\n        (\"help,h\", \"Describe program options.\\n\")\n        (\"compose-directory\", bpo::value<std::string>(),\n         \"The directory in which the CompOSE eos.quantities, eos.parameters, \"\n         \"and eos.table files are.\")\n        (\"eos-subfile\", bpo::value<std::string>(),\n         \"Path of where to write the subfile SpECTRE EOS Table inside the \"\n         \"HDF5 file.\")\n        (\"output,o\", bpo::value<std::string>(),\n         \"Path of the output HDF5 file to which the EOS subfile will be \"\n         \"written, including the .h5 extension.\")\n        ;\n    // clang-format on\n\n    bpo::command_line_parser command_line_parser(argc, argv);\n    command_line_parser.options(command_line_options);\n\n    bpo::variables_map parsed_command_line_options;\n    bpo::store(command_line_parser.run(), parsed_command_line_options);\n    bpo::notify(parsed_command_line_options);\n\n    if (parsed_command_line_options.count(\"help\") != 0 or\n        parsed_command_line_options.count(\"compose-directory\") == 0 or\n        parsed_command_line_options.count(\"output\") == 0 or\n        parsed_command_line_options.count(\"eos-subfile\") == 0) {\n      Parallel::printf(\"%s\\n\", command_line_options);\n      return 1;\n    }\n    convert_file(\n        parsed_command_line_options.at(\"compose-directory\").as<std::string>(),\n        parsed_command_line_options.at(\"output\").as<std::string>(),\n        parsed_command_line_options.at(\"eos-subfile\").as<std::string>());\n  } catch (const bpo::error& e) {\n    ERROR(e.what());\n  }\n  return 0;\n}\n"
  },
  {
    "path": "src/Executables/DebugPreprocessor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# The target is used for debugging preprocessor metaprograms by\n# outputting the preprocessed source file to stdout\n\n# Get a list of the include dirs, then prepend -I to all of them\nget_property(\n  DEBUG_PREPROCESSOR_INCLUDE_DIRS\n  DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n  PROPERTY INCLUDE_DIRECTORIES\n  )\nstring(\n  REGEX REPLACE \";\" \";-I\"\n  DEBUG_PREPROCESSOR_INCLUDE_DIRS\n  \"${DEBUG_PREPROCESSOR_INCLUDE_DIRS}\"\n  )\nset(DEBUG_PREPROCESSOR_INCLUDE_DIRS\n  \"-I${DEBUG_PREPROCESSOR_INCLUDE_DIRS}\"\n  )\n\nadd_custom_target(\n  DebugPreprocessor\n  COMMAND ${CMAKE_CXX_COMPILER}\n  ${DEBUG_PREPROCESSOR_INCLUDE_DIRS}\n  -std=c++14 # needed to get output\n  ${CMAKE_SOURCE_DIR}/src/Executables/DebugPreprocessor/DebugPreprocessor.cpp\n  -E\n  )\n"
  },
  {
    "path": "src/Executables/DebugPreprocessor/DebugPreprocessor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n// This is an example: replace the code with what you want to debug\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GEN_OP(op, dim) \\\n  template bool operator op(const Index<dim>& lhs, const Index<dim>& rhs);\n#define INSTANTIATION(r, data)         \\\n  template class Index<GET_DIM(data)>; \\\n  GEN_OP(==, GET_DIM(data))            \\\n  GEN_OP(!=, GET_DIM(data))\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (0, 1, 2, 3))\n\n#undef GET_DIM\n#undef GEN_OP\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/Executables/Examples/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(HelloWorld)\nadd_subdirectory(HelloWorldNoCharm)\n# [add_subdirectory]\nadd_subdirectory(ParallelTutorial)\n# [add_subdirectory]\nadd_subdirectory(PiMonteCarlo)\nadd_subdirectory(RandomAmr)\n"
  },
  {
    "path": "src/Executables/Examples/HelloWorld/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE SingletonHelloWorld)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  SingletonHelloWorld.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Charmxx::main\n  DataStructures\n  Informer\n  Options\n  Parallel\n  Printf\n  SystemUtilities\n  Utilities\n  )\n"
  },
  {
    "path": "src/Executables/Examples/HelloWorld/SingletonHelloWorld.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// [executable_example_includes]\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n// [executable_example_includes]\n\n// [executable_example_options]\nnamespace OptionTags {\nstruct Name {\n  using type = std::string;\n  static constexpr Options::String help{\"A name\"};\n};\n}  // namespace OptionTags\n\nnamespace Tags {\nstruct Name : db::SimpleTag {\n  using type = std::string;\n  using option_tags = tmpl::list<OptionTags::Name>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::string create_from_options(const std::string& name) {\n    return name;\n  }\n};\n}  // namespace Tags\n// [executable_example_options]\n\n// [executable_example_action]\nnamespace Actions {\nstruct PrintMessage {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    Parallel::printf(\"Hello %s from process %d on node %d!\\n\",\n                     Parallel::get<Tags::Name>(cache), sys::my_proc(),\n                     sys::my_node());\n  }\n};\n}  // namespace Actions\n// [executable_example_action]\n\n// [executable_example_singleton]\ntemplate <class Metavariables>\nstruct HelloWorld {\n  using const_global_cache_tags = tmpl::list<Tags::Name>;\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache);\n};\n\ntemplate <class Metavariables>\nvoid HelloWorld<Metavariables>::execute_next_phase(\n    const Parallel::Phase /* next_phase */,\n    Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n  Parallel::simple_action<Actions::PrintMessage>(\n      Parallel::get_parallel_component<HelloWorld>(\n          *Parallel::local_branch(global_cache)));\n}\n// [executable_example_singleton]\n\n// [executable_example_metavariables]\nstruct Metavars {\n  using component_list = tmpl::list<HelloWorld<Metavars>>;\n\n  static constexpr Options::String help{\n      \"Say hello from a singleton parallel component.\"};\n\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n// [executable_example_metavariables]\n\n// [executable_example_charm_init]\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavars>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n// [executable_example_charm_init]\n"
  },
  {
    "path": "src/Executables/Examples/HelloWorldNoCharm/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE HelloWorldNoCharm)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  HelloWorld.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  DataStructures\n  Printf\n  )\n"
  },
  {
    "path": "src/Executables/Examples/HelloWorldNoCharm/HelloWorld.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n\n// Charm looks for this function but since we build without a main function or\n// main module we just have it be empty\nextern \"C\" void CkRegisterMainModule(void) {}\n\nint main() {\n  DataVector a{1.0, 2.3, 8.9};\n  Parallel::printf(\"%s\\n\", a);\n}\n"
  },
  {
    "path": "src/Executables/Examples/ParallelTutorial/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# [add_spectre_executable]\nset(EXECUTABLE MinimalExample)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  MinimalExecutable.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Charmxx::main\n  Informer\n  Options\n  Parallel\n  Utilities\n  )\n# [add_spectre_executable]\n"
  },
  {
    "path": "src/Executables/Examples/ParallelTutorial/MinimalExecutable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n\n#include \"Options/String.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// [metavariables_definition]\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n\n  static constexpr std::array<Parallel::Phase, 2> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Exit}};\n\n  static constexpr Options::String help{\"A minimal executable\"};\n};\n// [metavariables_definition]\n\n// [main_function]\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n// [main_function]\n"
  },
  {
    "path": "src/Executables/Examples/PiMonteCarlo/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE PiMonteCarlo)\nset(TUTORIAL_EXECUTABLE PiMonteCarloTutorial)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  PiMonteCarlo.cpp\n  )\n\nadd_spectre_executable(\n  ${TUTORIAL_EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  PiMonteCarloTutorial.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Charmxx::main\n  DataStructures\n  ErrorHandling\n  Informer\n  Options\n  Parallel\n  Printf\n  Utilities\n  )\n\ntarget_link_libraries(\n  ${TUTORIAL_EXECUTABLE}\n  PRIVATE\n  Charmxx::main\n  DataStructures\n  ErrorHandling\n  Informer\n  Options\n  Parallel\n  Printf\n  Utilities\n  )\n"
  },
  {
    "path": "src/Executables/Examples/PiMonteCarlo/PiMonteCarlo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Includes from the C++ standard library needed for this executable\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <unordered_set>\n#include <vector>\n\n// Includes from SpECTRE libraries needed for this executable\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/DistributedObject.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InitializationFunctions.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// Forward declaration: promise that this way to access the global cache will\n// be defined elsewhere.\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n\n// Forward declarations: promise that the parallel components and actions will\n// eventually be defined. This lets you refer to them before you actually\n// define them.\n// A Singleton component that computes pi from how many of the darts thrown\n// at the unit square that hit the quarter unit circle.\ntemplate <typename Metavars>\nstruct PiEstimator;\n\n// An array component, where each element throws some darts at the unit square\n// and checks how many hit the unit quarter circle.\ntemplate <typename Metavars>\nstruct DartThrower;\n\n// Forward declare the actions implemented in this file, so they can be\n// referred to before being defined. There will be two actions:\n//      1. ThrowDarts: an \"iterable action\" that can be called repeatedly.\n//                     Throw some darts, check how many hit, then report\n//                     the number of hits.\n//      2. ProcessHitsAndThrows: a reduction action that sums how many darts\n//                               hit and how many were thrown across all\n//                               processors, and then uses that info to\n//                               calculate pi.\nnamespace Actions {\nstruct ThrowDarts;\nstruct ProcessHitsAndThrows;\n}  // namespace Actions\n\n// TUTORIAL PART 0: Set up options to read from input (yaml) file\nnamespace OptionTags {\n// TUTORIAL STEP 0.0: add structs for the two quantities the user will choose\n// when running the executable: DartsPerIteration and AccuracyGoal.\nstruct DartsPerIteration {\n  using type = size_t;\n  static constexpr Options::String help{\n      \"How many darts to throw on each processor in one iteration\"};\n};\n\nstruct AccuracyGoal {\n  using type = double;\n  static constexpr Options::String help{\n      \"Fractional accuracy goal for pi monte carlo estimate\"};\n};\n};  // namespace OptionTags\n\n// TUTORIAL PART 1: Set up quantities stored in DataBox\nnamespace Tags {\n// TUTORIAL STEP 1.0: add structs to hold the two user-specified options in\n// memory: DartsPerIteration and AccuracyGoal\nstruct DartsPerIteration : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::DartsPerIteration>;\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(const size_t& darts_per_iteration) {\n    return darts_per_iteration;\n  }\n};\n\nstruct AccuracyGoal : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::AccuracyGoal>;\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double& accuracy_goal) {\n    return accuracy_goal;\n  }\n};\n\n// TUTORIAL STEP 1.1: add structs to hold two counters in memory: how many darts\n// have been thrown on all processors so far (ThrowsAllProcs), and how many of\n// those have hit the quarter unit circle (HitsAllProcs).\n// Instead of initializing from a user-specified value, hard-code initializing\n// them to zero.\nstruct ThrowsAllProcs : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<>;\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options() { return 0; }\n};\n\nstruct HitsAllProcs : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<>;\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options() { return 0; }\n};\n}  // Namespace Tags\n\n// TUTORIAL PART 2: Complete the Actions ThrowDarts and EstimatePi.\nnamespace Actions {\n// In spectre, \"iterable actions\" (actions that can be done more than once) are\n// made by creating a struct with a function apply with the following\n// template parameters (compile-time parameters), parameters, and\n// return type.\nstruct ThrowDarts {\n  template <typename DbTags, typename... InboxTags, typename Metavars,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavars>& cache,\n      const ArrayIndex& array_index, const ActionList& /*meta*/,\n      const ParallelComponent* const /*meta*/\n  ) {\n    // TUTORIAL STEP 2.0: get how many darts to throw from the DataBox\n    const size_t number_of_darts = db::get<Tags::DartsPerIteration>(box);\n\n    // TUTORIAL STEP 2.1: throw N darts at the unit square, seeing how many\n    // hit the quarter circle\n    std::random_device device;\n    std::mt19937_64 generator(device());\n    std::uniform_real_distribution distribution{0.0, 1.0};\n    size_t hits = 0;\n    for (size_t i = 0; i < number_of_darts; ++i) {\n      const double x = distribution(generator);\n      const double y = distribution(generator);\n      if (x * x + y * y < 1) {\n        hits += 1;\n      }\n    }\n\n    // Get a proxy (an object that might live on another compute node)\n    // for each ParallelComponent. The PiEstimator Singleton component\n    // will run the ProcessHitsAndThrows action to estimate pi.\n    // The DartThrower parallel component calls ThrowDarts on a some processors.\n    // TUTORIAL STEP 2.2: get the PiEstimator and DartThrower parallel\n    // components.\n    const auto& pi_estimator_proxy =\n        Parallel::get_parallel_component<PiEstimator<Metavars>, Metavars>(\n            cache);\n    const auto& dart_thrower_element_proxy =\n        Parallel::get_parallel_component<DartThrower<Metavars>, Metavars>(\n            cache)[array_index];\n\n    // Tutorial STEP 2.3: contribute hits to reduction data\n    const Parallel::ReductionData<\n        Parallel::ReductionDatum<size_t, funcl::Plus<>>>\n        hits_to_send{hits};\n    Parallel::contribute_to_reduction<Actions::ProcessHitsAndThrows>(\n        hits_to_send, dart_thrower_element_proxy, pi_estimator_proxy);\n\n    // After this action completes, tell this element of the\n    // DartThrower array parallel component to pause until further notice.\n    // (That notice might come frm the ProcessHitsAndThrows action, if it\n    // decides that more darts should be thrown.)\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n// In spectre, \"reduction actions\" (actions that receive data from the\n// elements of an array parallel component and then reduce them to a single\n// result) are made by creating a struct with a function apply with the\n// following template parameters (compile-time parameters), parameters, and\n// return type.\nstruct ProcessHitsAndThrows {\n  template <typename ParallelComponent, typename DbTags, typename Metavars,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavars>& cache,\n                    const ArrayIndex& /*array_index*/, const size_t new_hits) {\n    // TUTORIAL STEP 2.4: get number of processors from the cache\n    const auto num_procs = Parallel::number_of_procs<size_t>(cache);\n\n    // TUTORIAL STEP 2.5: get number of darts thrown each iteration\n    // from the DataBox\n    const size_t darts_per_iteration = db::get<Tags::DartsPerIteration>(box);\n\n    // TUTORIAL STEP 2.6: complete this lambda that updates quantities\n    // in the DataBox:\n    //  STEP 2.6.1: Add Tags::HitsAllProcs, Tags::ThrowsAllProcs to first\n    //              tmpl::list<> in db::mutate_apply<>()\n    //  STEP 2.6.2: Add a corresponding const gsl::not_null<size_t*>\n    //              parameter corresponding to each of the two tags from\n    //              step 2.6.1\n    //  STEP 2.6.3: capture variables storing new_hits, darts_per_iteration,\n    //              and number_of_processors to the capture list\n    //  STEP 2.6.4: make the body increment the values pointed to by the\n    //              pointers (e.g. *hits_all_procs += new_hits)\n    db::mutate_apply<tmpl::list<Tags::HitsAllProcs, Tags::ThrowsAllProcs>,\n                     tmpl::list<>>(\n        [&new_hits, &darts_per_iteration, &num_procs](\n            const gsl::not_null<size_t*> hits_all_procs,\n            const gsl::not_null<size_t*> throws_all_procs) {\n          *hits_all_procs += new_hits;\n          *throws_all_procs += darts_per_iteration * num_procs;\n        },\n        make_not_null(&box));\n\n    // TUTORIAL STEP 2.7: estiamte pi, compute the fractional accuracy, and\n    // print the result using Parallel::printf\n    const double pi_estimate =\n        4.0 * static_cast<double>(db::get<Tags::HitsAllProcs>(box)) /\n        static_cast<double>(db::get<Tags::ThrowsAllProcs>(box));\n    const double fractional_accuracy = abs(pi_estimate - M_PI) / M_PI;\n\n    Parallel::printf(\"Pi ~ %1.15f (accuracy: %1.15f)\\n\", pi_estimate,\n                     fractional_accuracy);\n\n    // TUTORIAL STEP 2.8: if fractional accuracy is bigger than the accuracy\n    // goal, tell each element of the DartThrower parallel component to unpause\n    // (that is, throw some more darts).\n    if (fractional_accuracy > db::get<Tags::AccuracyGoal>(box)) {\n      for (size_t i = 0; i < num_procs; ++i) {\n        Parallel::get_parallel_component<DartThrower<Metavars>>(cache)[i]\n            .perform_algorithm(true);\n      }\n    }\n  }\n};\n}  // namespace Actions\n\n////////////////////////////////////////////////////////////////////////\n// TUTORIAL STEP 3: Set up parallel components\n////////////////////////////////////////////////////////////////////////\n\n// TUTORIAL STEP 3.0: Create the PiEstimator parallel component struct.\n// After defining the type aliases, insert the following boilerplate\n// declaration into the struct:\n/*\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavars>& global_cache);\n*/\ntemplate <typename Metavars>\nstruct PiEstimator {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavars;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<>>>;\n  using simple_tags_from_options =\n      tmpl::list<Tags::HitsAllProcs, Tags::ThrowsAllProcs,\n                 Tags::DartsPerIteration, Tags::AccuracyGoal>;\n  static void execute_next_phase(\n      Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavars>& global_cache);\n};\n\n// TUTORIAL STEP 3.1: After creating PiEstimator, uncomment this defintion.\n// This function is necessary boilerplate that tells\n// spectre when one phase ends, start the next one.\ntemplate <typename Metavars>\nvoid PiEstimator<Metavars>::execute_next_phase(\n    const Parallel::Phase next_phase,\n    const Parallel::CProxy_GlobalCache<Metavars>& global_cache) {\n  auto& local_cache = *Parallel::local_branch(global_cache);\n  Parallel::get_parallel_component<PiEstimator<Metavars>>(local_cache)\n      .start_phase(next_phase);\n}\n\n// TUTORIAL STEP 3.2: Create the DartThrower parallel component struct.\n// After defining the type aliases, insert the following boilerplate\n// static function declarations into the struct.\n/*\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavars>& global_cache);\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavars>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_options,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n          array_allocation_options = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {});\n*/\ntemplate <typename Metavars>\nstruct DartThrower {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavars;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Execute,\n                                        tmpl::list<Actions::ThrowDarts>>>;\n  using simple_tags_from_options = tmpl::list<Tags::DartsPerIteration>;\n  using array_index = int;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void execute_next_phase(\n      Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavars>& global_cache);\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavars>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_options,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n          array_allocation_options = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {});\n};\n\n// TUTORIAL STEP 3.3: After creating DartThrower, uncomment this function\n// definition.\n// Then add it to the parallel component struct as a static function.\n// This function is necessary boilerplate that tells\n// spectre when one phase ends, start the next one.\ntemplate <typename Metavars>\nvoid DartThrower<Metavars>::execute_next_phase(\n    const Parallel::Phase next_phase,\n    const Parallel::CProxy_GlobalCache<Metavars>& global_cache) {\n  auto& local_cache = *Parallel::local_branch(global_cache);\n  Parallel::get_parallel_component<DartThrower<Metavars>>(local_cache)\n      .start_phase(next_phase);\n}\n\n// TUTORIAL STEP 3.4: After creating DartThrower, uncomment this function.\n// Then add it to the parallel component struct as a static function.\n//\n// This function assigns the array elements to\n// specific cores (processors). The strategy is \"round robin:\" assign\n// one per core until each core (except any the user wants to skip) has one,\n// then repeat until each has two, etc.\n//\n// Note: since we choose here that there will be one DartThrower element\n// per core, each core will get one element, unless the user asks to skip\n// one or more cores.\ntemplate <typename Metavars>\nvoid DartThrower<Metavars>::allocate_array(\n    Parallel::CProxy_GlobalCache<Metavars>& global_cache,\n    const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n        initialization_options,\n    const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n    /*array_allocation_options*/,\n    const std::unordered_set<size_t>& procs_to_ignore) {\n  auto& local_cache = *Parallel::local_branch(global_cache);\n  auto& array_proxy =\n      Parallel::get_parallel_component<DartThrower<Metavars>>(local_cache);\n\n  size_t which_proc = 0;\n  const auto num_procs = Parallel::number_of_procs<size_t>(local_cache);\n  const size_t number_of_elements = num_procs;\n\n  for (size_t i = 0; i < number_of_elements; ++i) {\n    while (procs_to_ignore.find(which_proc) != procs_to_ignore.end()) {\n      which_proc = which_proc + 1 == num_procs ? 0 : which_proc + 1;\n    }\n    array_proxy[i].insert(global_cache, initialization_options, which_proc);\n    which_proc = which_proc + 1 == num_procs ? 0 : which_proc + 1;\n  }\n  array_proxy.doneInserting();\n}\n\n// TUTORIAL STEP 4: Complete the Metavariables struct\nstruct Metavariables {\n  // TUTORIAL STEP 4.1: Add the PiEstimator and DartThrower components\n  // to the component list\n  using component_list =\n      tmpl::list<PiEstimator<Metavariables>, DartThrower<Metavariables>>;\n\n  // TUTORIAL STEP 4.2: complete the help string for this executable.\n  static constexpr Options::String help{\"Compute pi with Monte Carlo\"};\n\n  // Boilerplate defining phases.\n  // All executables have an initialization and an exit phase.\n  // Synchronization points occur at phase boundaries. Here, there's just one\n  // phase in between where all the work gets done, called execute.\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::Exit}};\n\n  // Boilerplate stating that this metavariables truct has no run-time content\n  // that must be sent over the network when remote objects want the\n  // metavariables. This is done by defining a pup (pack-unpack) function\n  // that does nothing.\n  void pup(PUP::er& /*p*/) {}\n};\n\n// Required boilerplate to make charm++ work\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "src/Executables/Examples/PiMonteCarlo/PiMonteCarloTutorial.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Notes:\n// 1. This code is the starting point for a tutorial on how\n//    spectre executables work. Comments starting with \"TUTORIAL\" indicate\n//    steps to build a complete executable. This file already includes some\n//    necessary boilerplate and all necessary includes.\n//\n// 2. Building this target as-is will generate unused-variable compiler\n//    warnings. These warnings should vanish once the tutorial is successfully\n//    completed.\n//\n// 3. The file PiMonteCarlo.cpp contains a solution for each tutorial step.\n//    If you get stuck or run into trouble completing the tutorial, you can\n//    always compare with the solution in PiMonteCarlo.cpp.\n\n// Includes from the C++ standard library needed for this executable\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <unordered_set>\n#include <vector>\n\n// Includes from SpECTRE libraries needed for this executable\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/DistributedObject.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InitializationFunctions.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// Forward declaration: promise that this way to access the global cache will\n// be defined elsewhere.\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n\n// Forward declarations: promise that the parallel components and actions will\n// eventually be defined. This lets you refer to them before you actually\n// define them.\n// A Singleton component that computes pi from how many of the darts thrown\n// at the unit square that hit the quarter unit circle.\ntemplate <typename Metavars>\nstruct PiEstimator;\n\n// An array component, where each element throws some darts at the unit square\n// and checks how many hit the unit quarter circle.\ntemplate <typename Metavars>\nstruct DartThrower;\n\n// Forward declare the actions implemented in this file, so they can be\n// referred to before being defined. There will be two actions:\n//      1. ThrowDarts: an \"iterable action\" that can be called repeatedly.\n//                     Throw some darts, check how many hit, then report\n//                     the number of hits.\n//      2. ProcessHitsAndThrows: a reduction action that sums how many darts\n//                               hit and how many were thrown across all\n//                               processors, and then uses that info to\n//                               calculate pi.\nnamespace Actions {\nstruct ThrowDarts;\nstruct ProcessHitsAndThrows;\n}  // namespace Actions\n\n// TUTORIAL PART 0: Set up options to read from input (yaml) file\nnamespace OptionTags {\n// TUTORIAL STEP 0.0: add structs for the two quantities the user will choose\n// when running the executable: DartsPerIteration and AccuracyGoal.\n\n};  // namespace OptionTags\n\n// TUTORIAL PART 1: Set up quantities stored in DataBox\nnamespace Tags {\n// TUTORIAL STEP 1.0: add structs to hold the two user-specified options in\n// memory: DartsPerIteration and AccuracyGoal\n\n// TUTORIAL STEP 1.1: add structs to hold two counters in memory: how many darts\n// have been thrown on all processors so far (ThrowsAllProcs), and how many of\n// those have hit the quarter unit circle (HitsAllProcs).\n// Instead of initializing from a user-specified value, hard-code initializing\n// them to zero.\n\n}  // Namespace Tags\n\n// TUTORIAL PART 2: Complete the Actions ThrowDarts and EstimatePi.\nnamespace Actions {\n// In spectre, \"iterable actions\" (actions that can be done more than once) are\n// made by creating a struct with a function apply with the following\n// template parameters (compile-time parameters), parameters, and\n// return type.\nstruct ThrowDarts {\n  template <typename DbTags, typename... InboxTags, typename Metavars,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavars>& cache,\n      const ArrayIndex& array_index, const ActionList& /*meta*/,\n      const ParallelComponent* const /*meta*/\n  ) {\n    // TUTORIAL STEP 2.0: get how many darts to throw from the DataBox\n    (void)box;  // remove this line\n\n    // TUTORIAL STEP 2.1: throw N darts at the unit square, seeing how many\n    // hit the quarter circle\n    (void)cache;        // remove this line\n    (void)array_index;  // remove this line\n\n    // Get a proxy (an object that might live on another compute node)\n    // for each ParallelComponent. The PiEstimator Singleton component\n    // will run the ProcessHitsAndThrows action to estimate pi.\n    // The DartThrower parallel component calls ThrowDarts on a some processors.\n    // TUTORIAL STEP 2.2: get the PiEstimator and DartThrower parallel\n    // components.\n\n    // Tutorial STEP 2.3: contribute hits to reduction data\n\n    // After this action completes, tell this element of the\n    // DartThrower array parallel component to pause until further notice.\n    // (That notice might come frm the ProcessHitsAndThrows action, if it\n    // decides that more darts should be thrown.)\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n// In spectre, \"reduction actions\" (actions that receive data from the\n// elements of an array parallel component and then reduce them to a single\n// result) are made by creating a struct with a function apply with the\n// following template parameters (compile-time parameters), parameters, and\n// return type.\nstruct ProcessHitsAndThrows {\n  template <typename ParallelComponent, typename DbTags, typename Metavars,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavars>& cache,\n                    const ArrayIndex& /*array_index*/, const size_t new_hits) {\n    // TUTORIAL STEP 2.4: get number of processors from the cache\n\n    // TUTORIAL STEP 2.5: get number of darts thrown each iteration\n    // from the DataBox\n\n    // TUTORIAL STEP 2.6: complete this lambda that updates quantities\n    // in the DataBox:\n    //  STEP 2.6.1: Add Tags::HitsAllProcs, Tags::ThrowsAllProcs to first\n    //              tmpl::list<> in db::mutate_apply<>()\n    //  STEP 2.6.2: Add a corresponding const gsl::not_null<size_t*>\n    //              parameter corresponding to each of the two tags from\n    //              step 2.6.1\n    //  STEP 2.6.3: capture variables storing new_hits, darts_per_iteration,\n    //              and number_of_processors to the capture list\n    //  STEP 2.6.4: make the body increment the values pointed to by the\n    //              pointers (e.g. *hits_all_procs += new_hits)\n    db::mutate_apply<tmpl::list<>, tmpl::list<>>([]() {}, make_not_null(&box));\n\n    // TUTORIAL STEP 2.7: estiamte pi, compute the fractional accuracy, and\n    // print the result using Parallel::printf\n\n    // TUTORIAL STEP 2.8: if fractional accuracy is bigger than the accuracy\n    // goal, tell each element of the DartThrower parallel component to unpause\n    // (that is, throw some more darts).\n  }\n};\n}  // namespace Actions\n\n////////////////////////////////////////////////////////////////////////\n// TUTORIAL STEP 3: Set up parallel components\n////////////////////////////////////////////////////////////////////////\n\n// TUTORIAL STEP 3.0: Create the PiEstimator parallel component struct.\n// After defining the type aliases, insert the following boilerplate\n// declaration into the struct:\n/*\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavars>& global_cache);\n*/\ntemplate <typename Metavars>\nstruct PiEstimator {};\n\n// TUTORIAL STEP 3.1: After creating PiEstimator, uncomment this defintion.\n// This function is necessary boilerplate that tells\n// spectre when one phase ends, start the next one.\n/*\ntemplate <typename Metavars>\nvoid PiEstimator<Metavars>::execute_next_phase(\n    const Parallel::Phase next_phase,\n    const Parallel::CProxy_GlobalCache<Metavars>& global_cache) {\n  auto& local_cache = *Parallel::local_branch(global_cache);\n  Parallel::get_parallel_component<PiEstimator<Metavars>>(local_cache)\n      .start_phase(next_phase);\n}\n*/\n\n// TUTORIAL STEP 3.2: Create the DartThrower parallel component struct.\n// After defining the type aliases, insert the following boilerplate\n// static function declarations into the struct.\n/*\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavars>& global_cache);\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavars>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_options,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n          array_allocation_options = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {});\n*/\ntemplate <typename Metavars>\nstruct DartThrower {};\n\n// TUTORIAL STEP 3.3: After creating DartThrower, uncomment this function\n// definition.\n// Then add it to the parallel component struct as a static function.\n// This function is necessary boilerplate that tells\n// spectre when one phase ends, start the next one.\n/*\ntemplate <typename Metavars>\nvoid DartThrower<Metavars>::execute_next_phase(\n    const Parallel::Phase next_phase,\n    const Parallel::CProxy_GlobalCache<Metavars>& global_cache) {\n  auto& local_cache = *Parallel::local_branch(global_cache);\n  Parallel::get_parallel_component<DartThrower<Metavars>>(local_cache)\n      .start_phase(next_phase);\n}\n*/\n\n// TUTORIAL STEP 3.4: After creating DartThrower, uncomment this function.\n// Then add it to the parallel component struct as a static function.\n//\n// This function assigns the array elements to\n// specific cores (processors). The strategy is \"round robin:\" assign\n// one per core until each core (except any the user wants to skip) has one,\n// then repeat until each has two, etc.\n//\n// Note: since we choose here that there will be one DartThrower element\n// per core, each core will get one element, unless the user asks to skip\n// one or more cores.\n// template <typename Metavars>\n// void DartThrower<Metavars>::allocate_array(\n//     Parallel::CProxy_GlobalCache<Metavars>& global_cache,\n//     const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n//         initialization_options,\n//     const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n//     /*array_allocation_options*/,\n//     const std::unordered_set<size_t>& procs_to_ignore) {\n//   auto& local_cache = *Parallel::local_branch(global_cache);\n//   auto& array_proxy =\n//       Parallel::get_parallel_component<DartThrower<Metavars>>(local_cache);\n\n//   size_t which_proc = 0;\n//   const size_t num_procs = Parallel::number_of_procs<size_t>(local_cache);\n//   const size_t number_of_elements = num_procs;\n\n//   for (size_t i = 0; i < number_of_elements; ++i) {\n//     while (procs_to_ignore.find(which_proc) != procs_to_ignore.end()) {\n//       which_proc = which_proc + 1 == num_procs ? 0 : which_proc + 1;\n//     }\n//     array_proxy[i].insert(global_cache, initialization_options, which_proc);\n//     which_proc = which_proc + 1 == num_procs ? 0 : which_proc + 1;\n//   }\n//   array_proxy.doneInserting();\n// }\n\n// TUTORIAL STEP 4: Complete the Metavariables struct\nstruct Metavariables {\n  // TUTORIAL STEP 4.1: Add the PiEstimator and DartThrower components\n  // to the component list\n  using component_list = tmpl::list<>;\n\n  // TUTORIAL STEP 4.2: complete the help string for this executable.\n  static constexpr Options::String help{\"INSERT HELP TEXT HERE\"};\n\n  // Boilerplate defining phases.\n  // All executables have an initialization and an exit phase.\n  // Synchronization points occur at phase boundaries. Here, there's just one\n  // phase in between where all the work gets done, called execute.\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::Exit}};\n\n  // Boilerplate stating that this metavariables truct has no run-time content\n  // that must be sent over the network when remote objects want the\n  // metavariables. This is done by defining a pup (pack-unpack) function\n  // that does nothing.\n  void pup(PUP::er& /*p*/) {}\n};\n\n// Required boilerplate to make charm++ work\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "src/Executables/Examples/RandomAmr/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Charmxx::main\n  DomainCreators\n  Informer\n  Logging\n  Options\n  ParallelAmr\n  PhaseControl\n  Spectral\n  Utilities\n  )\n\nfunction(add_random_amr_executable DIM KEEP_COARSE_GRIDS)\n  set(KEEP_COARSE_GRIDS_SUFFIX \"\")\n  if (KEEP_COARSE_GRIDS)\n    set(KEEP_COARSE_GRIDS_SUFFIX \"KeepCoarseGrids\")\n  endif()\n  set(EXECUTABLE \"RandomAmr${KEEP_COARSE_GRIDS_SUFFIX}${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    RandomAmr.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    KEEP_COARSE_GRIDS=${KEEP_COARSE_GRIDS}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction()\n\nadd_random_amr_executable(1 false)\nadd_random_amr_executable(2 false)\nadd_random_amr_executable(3 false)\nadd_random_amr_executable(1 true)\nadd_random_amr_executable(2 true)\nadd_random_amr_executable(3 true)\n"
  },
  {
    "path": "src/Executables/Examples/RandomAmr/InitializeDomain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Tags/ArrayIndex.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Initialization {\n\n/// \\ingroup InitializationGroup\n/// \\brief Initialize items related to the structure of a Domain\n///\n/// \\see InitializeItems\n///\n/// \\note This only initializes the Element and the Mesh on each element of\n/// an array component.  This is all that is needed about the Domain in order to\n/// test the mechanics of adaptive mesh refinement.\ntemplate <size_t Dim>\nstruct Domain {\n  using const_global_cache_tags = tmpl::list<::domain::Tags::Domain<Dim>>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using simple_tags_from_options =\n      tmpl::list<::domain::Tags::InitialExtents<Dim>,\n                 ::domain::Tags::InitialRefinementLevels<Dim>,\n                 evolution::dg::Tags::Quadrature>;\n\n  using argument_tags =\n      tmpl::append<const_global_cache_tags, simple_tags_from_options,\n                   tmpl::list<::Parallel::Tags::ArrayIndex<ElementId<Dim>>>>;\n\n  using return_tags =\n      tmpl::list<::domain::Tags::Mesh<Dim>, ::domain::Tags::Element<Dim>>;\n\n  using simple_tags =\n      tmpl::push_back<return_tags, ::domain::Tags::NeighborMesh<Dim>>;\n  using compute_tags = tmpl::list<>;\n\n  /// Given the items fetched from a DataBox by the argument_tags, mutate\n  /// the items in the DataBox corresponding to return_tags\n  static void apply(\n      const gsl::not_null<Mesh<Dim>*> mesh,\n      const gsl::not_null<Element<Dim>*> element, const ::Domain<Dim>& domain,\n      const std::vector<std::array<size_t, Dim>>& initial_extents,\n      const std::vector<std::array<size_t, Dim>>& initial_refinement,\n      const Spectral::Quadrature& i1_quadrature,\n      const ElementId<Dim>& element_id) {\n    *element = ::domain::create_initial_element(element_id, domain.blocks(),\n                                                initial_refinement);\n    const Spectral::Basis i1_basis{Spectral::Basis::Legendre};\n    *mesh = ::domain::create_initial_mesh(initial_extents, *element, i1_basis,\n                                          i1_quadrature);\n  }\n};\n}  // namespace amr::Initialization\n"
  },
  {
    "path": "src/Executables/Examples/RandomAmr/RandomAmr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Executables/Examples/RandomAmr/RandomAmr.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = RandomAmrMetavars<DIM, KEEP_COARSE_GRIDS>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &amr::register_callbacks<metavariables,\n                                typename metavariables::dg_element_array>,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Executables/Examples/RandomAmr/RandomAmr.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/TagsDomain.hpp\"\n#include \"Executables/Examples/RandomAmr/InitializeDomain.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/ElementsRegistration.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Random.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct DummySystem {};\n}  // namespace\n\n/// \\page RandomAmrExecutablePage RandomAmr Executable\n/// The RandomAmr executable is being used to develop the mechanics of\n/// adaptive mesh refinement.\n///\n/// See RandomAmrMetavars for a description of the metavariables of this\n/// executable.\n\n/// \\brief The metavariables for the RandomAmr executable\ntemplate <size_t Dim, bool KeepCoarseGrids>\nstruct RandomAmrMetavars {\n  static constexpr size_t volume_dim = Dim;\n  using system = DummySystem;\n\n  static constexpr Options::String help{\n      \"Test anisotropic refinement by randomly refining a grid.\\n\"};\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<amr::Criterion,\n                   tmpl::list<amr::Criteria::DriveToTarget<\n                                  volume_dim, amr::Criteria::Type::h>,\n                              amr::Criteria::DriveToTarget<\n                                  volume_dim, amr::Criteria::Type::p>,\n                              amr::Criteria::Random<amr::Criteria::Type::h>,\n                              amr::Criteria::Random<amr::Criteria::Type::p>>>,\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<\n                PhaseControl::VisitAndReturn<\n                    Parallel::Phase::EvaluateAmrCriteria>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::AdjustDomain>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::UpdateSections>,\n                PhaseControl::VisitAndReturn<Parallel::Phase::CheckDomain>,\n                PhaseControl::CheckpointAndExitAfterWallclock>>,\n        tmpl::pair<Trigger, tmpl::list<Triggers::Always>>>;\n  };\n\n  static constexpr auto default_phase_order =\n      std::array{Parallel::Phase::Initialization, Parallel::Phase::Register,\n                 Parallel::Phase::UpdateSections, Parallel::Phase::CheckDomain,\n                 Parallel::Phase::Evolve,         Parallel::Phase::Exit};\n\n  using dg_element_array = DgElementArray<\n      RandomAmrMetavars,\n      tmpl::list<\n          Parallel::PhaseActions<\n              Parallel::Phase::Initialization,\n              tmpl::list<Initialization::Actions::InitializeItems<\n                             amr::Initialization::Domain<volume_dim>,\n                             amr::Initialization::Initialize<\n                                 volume_dim, RandomAmrMetavars>>,\n                         Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Register,\n                                 tmpl::list<amr::Actions::RegisterElement,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Evolve,\n              tmpl::list<PhaseControl::Actions::ExecutePhaseChange>>>>;\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array,\n                             tmpl::list<amr::Actions::RegisterElement>>>;\n  };\n\n  using component_list =\n      tmpl::list<amr::Component<RandomAmrMetavars>, dg_element_array>;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = tmpl::list<::amr::projectors::DefaultInitialize<\n        domain::Tags::InitialExtents<Dim>,\n        domain::Tags::InitialRefinementLevels<Dim>,\n        evolution::dg::Tags::Quadrature>>;\n    static constexpr bool keep_coarse_grids = KeepCoarseGrids;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n};\n"
  },
  {
    "path": "src/Executables/ExportCoordinates/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBS_TO_LINK\n  Charmxx::main\n  CoordinateMaps\n  DgSubcell\n  Domain\n  DomainCreators\n  EventsAndTriggers\n  Evolution\n  Informer\n  LinearOperators\n  Logging\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  PhaseControl\n  Spectral\n  Time\n  Utilities\n  )\n\nfunction(add_export_coordinates_executable DIM)\n  set(EXECUTABLE \"ExportCoordinates${DIM}D\")\n  add_spectre_executable(\n    ${EXECUTABLE}\n    EXCLUDE_FROM_ALL\n    ExportCoordinates.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE}\n    PRIVATE\n    DIM=${DIM}\n    )\n  target_link_libraries(${EXECUTABLE} PRIVATE ${LIBS_TO_LINK})\nendfunction()\n\nadd_export_coordinates_executable(1)\nadd_export_coordinates_executable(2)\nadd_export_coordinates_executable(3)\n"
  },
  {
    "path": "src/Executables/ExportCoordinates/ExportCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Executables/ExportCoordinates/ExportCoordinates.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = Metavariables<DIM>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &amr::register_callbacks<metavariables,\n                                typename metavariables::dg_element_array>,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "src/Executables/ExportCoordinates/ExportCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/FlatLogicalMetric.hpp\"\n#include \"Domain/JacobianDiagnostic.hpp\"\n#include \"Domain/MinimumGridSpacing.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Protocols/ReductionDataFormatter.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpatialDiscretization/OptionTags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ContributeMemoryData.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Random.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/TruncationError.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/Events/MonitorMemory.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/Tags/StepperErrorTolerancesCompute.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/Triggers/SlabCompares.hpp\"\n#include \"Time/Triggers/TimeCompares.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\n/// \\cond\nusing MinGridSpacingReductionData = Parallel::ReductionData<\n    // Time\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n    // Minimum grid spacing\n    Parallel::ReductionDatum<double, funcl::Min<>>>;\n\nstruct MinGridSpacingFormatter\n    : tt::ConformsTo<observers::protocols::ReductionDataFormatter> {\n  using reduction_data = MinGridSpacingReductionData;\n  std::string operator()(const double time, const double min_grid_spacing) {\n    return \"Time: \" + get_output(time) +\n           \", Global inertial minimum grid spacing: \" +\n           get_output(min_grid_spacing) + \"\\n\";\n  }\n  void pup(PUP::er& /*p*/) {}\n};\n\nnamespace Actions {\ntemplate <size_t Dim>\nstruct ExportCoordinates {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& /*box*/,\n                const ArrayIndex& /*array_index*/) {\n    return {observers::TypeOfObservation::Volume,\n            observers::ObservationKey(\"ObserveCoords\")};\n  }\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const double time = get<Tags::Time>(box);\n\n    const auto& mesh = get<domain::Tags::Mesh<Dim>>(box);\n    const auto& inv_jacobian =\n        db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                              Frame::Inertial>>(box);\n    const auto& inertial_coordinates =\n        db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box);\n    const auto deriv_inertial_coordinates =\n        partial_derivative(inertial_coordinates, mesh, inv_jacobian);\n    // Collect volume data\n    // Remove tensor types, only storing individual components\n    std::vector<TensorComponent> components;\n    components.reserve(Dim + 1);\n    for (size_t d = 0; d < Dim; d++) {\n      components.emplace_back(\"InertialCoordinates_\" +\n                                  inertial_coordinates.component_name(\n                                      inertial_coordinates.get_tensor_index(d)),\n                              inertial_coordinates.get(d));\n    }\n\n    for (size_t i = 0; i < deriv_inertial_coordinates.size(); ++i) {\n      components.emplace_back(\n          \"DerivInertialCoordinates_\" +\n              deriv_inertial_coordinates.component_name(\n                  deriv_inertial_coordinates.get_tensor_index(i)),\n          deriv_inertial_coordinates[i]);\n    }\n\n    // Also output the determinant of the inverse jacobian, which measures\n    // the expansion and compression of the grid\n    const auto& det_inv_jac = db::get<\n        domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>>(\n        box);\n    components.emplace_back(\n        db::tag_name<domain::Tags::DetInvJacobian<Frame::ElementLogical,\n                                                  Frame::Inertial>>(),\n        get(det_inv_jac));\n\n    // Also output the jacobian diagnostic, which compares the analytic\n    // Jacobian (via the CoordinateMap) to the numerical Jacobian\n    // (computed via logical_partial_derivative)\n    const auto& jacobian = determinant_and_inverse(inv_jacobian).second;\n    tnsr::i<DataVector, Dim, Frame::ElementLogical> jac_diag{\n        mesh.number_of_grid_points(), 0.0};\n    domain::jacobian_diagnostic(make_not_null(&jac_diag), jacobian,\n                                inertial_coordinates, mesh);\n    for (size_t i = 0; i < Dim; ++i) {\n      components.emplace_back(\n          \"JacobianDiagnostic_\" +\n              jac_diag.component_name(jac_diag.get_tensor_index(i)),\n          jac_diag.get(i));\n    }\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t j = 0; j < Dim; j++) {\n        components.emplace_back(\n            \"Jacobian_\" + jacobian.component_name(jacobian.get_tensor_index(\n                              jacobian.get_storage_index(i, j))),\n            jacobian.get(i, j));\n      }\n    }\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t j = 0; j < Dim; j++) {\n        components.emplace_back(\n            \"InvJacobian_\" +\n                inv_jacobian.component_name(inv_jacobian.get_tensor_index(\n                    inv_jacobian.get_storage_index(i, j))),\n            inv_jacobian.get(i, j));\n      }\n    }\n\n    // Also output the computation domain metric\n    const auto& flat_logical_metric =\n        db::get<domain::Tags::FlatLogicalMetric<Dim>>(box);\n    for (size_t i = 0; i < flat_logical_metric.size(); ++i) {\n      components.emplace_back(\n          db::tag_name<domain::Tags::FlatLogicalMetric<Dim>>() +\n              flat_logical_metric.component_suffix(i),\n          flat_logical_metric[i]);\n    }\n\n    observers::contribute_volume_data<\n        not Parallel::is_nodegroup_v<ParallelComponent>>(\n        cache, observers::ObservationId(time, \"ObserveCoords\"),\n        std::string{\"/element_data\"},\n        Parallel::make_array_component_id<ParallelComponent>(element_id),\n        ElementVolumeData{element_id, std::move(components), mesh});\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct FindGlobalMinimumGridSpacing {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& /*box*/,\n                const ArrayIndex& /*array_index*/) {\n    return {observers::TypeOfObservation::Reduction,\n            observers::ObservationKey(\"min_grid_spacing\")};\n  }\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const double time = get<Tags::Time>(box);\n    const double local_min_grid_spacing =\n        get<domain::Tags::MinimumGridSpacing<Dim, Frame::Inertial>>(box);\n    auto& local_observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<observers::Observer<Metavariables>>(\n            cache));\n    Parallel::simple_action<observers::Actions::ContributeReductionData>(\n        local_observer, observers::ObservationId(time, \"min_grid_spacing\"),\n        Parallel::make_array_component_id<ParallelComponent>(element_id),\n        std::string{\"/MinGridSpacing\"},\n        std::vector<std::string>{\"Time\", \"MinGridSpacing\"},\n        MinGridSpacingReductionData{time, local_min_grid_spacing},\n        std::make_optional(MinGridSpacingFormatter{}));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n\nnamespace Initialization {\ntemplate <size_t Dim>\nstruct SetMeshType {\n  using const_global_cache_tags =\n      tmpl::list<evolution::dg::subcell::Tags::ActiveGrid>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<evolution::dg::subcell::Tags::ActiveGrid>;\n  using simple_tags_from_options = tmpl::list<>;\n  using default_initialized_simple_tags = tmpl::list<>;\n  using return_tags = tmpl::list<domain::Tags::Mesh<Dim>>;\n  using simple_tags = return_tags;\n  using compute_tags = tmpl::list<>;\n\n  static void apply(const gsl::not_null<::Mesh<Dim>*> mesh,\n                    const evolution::dg::subcell::ActiveGrid active_grid) {\n    // Originally the mesh is DG so switch it to FD if we aren't using a DG grid\n    if (active_grid == evolution::dg::subcell::ActiveGrid::Subcell) {\n      *mesh = evolution::dg::subcell::fd::mesh(*mesh);\n    }\n  }\n};\n}  // namespace Initialization\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using TimeStepperBase = TimeStepper;\n\n  static constexpr bool local_time_stepping =\n      TimeStepperBase::local_time_stepping;\n\n  // A placeholder system for the domain creators\n  struct system {};\n\n  static constexpr Options::String help{\n      \"Export the inertial coordinates of the Domain specified in the input \"\n      \"file. The output can be used to compute initial data externally, for \"\n      \"instance. Also outputs the determinant of the inverse jacobian as a \"\n      \"diagnostic of Domain quality: values far from unity indicate \"\n      \"compression or expansion of the grid.\"};\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>, domain_creators<volume_dim>>,\n        tmpl::pair<\n            amr::Criterion,\n            tmpl::list<amr::Criteria::DriveToTarget<volume_dim,\n                                                    amr::Criteria::Type::h>,\n                       amr::Criteria::DriveToTarget<volume_dim,\n                                                    amr::Criteria::Type::p>,\n                       amr::Criteria::Random<amr::Criteria::Type::h>,\n                       amr::Criteria::Random<amr::Criteria::Type::p>,\n                       amr::Criteria::TruncationError<\n                           volume_dim, tmpl::list<::domain::Tags::Coordinates<\n                                           volume_dim, Frame::Inertial>>>>>,\n        tmpl::pair<Event, tmpl::list<Events::MonitorMemory<volume_dim>,\n                                     Events::Completion>>,\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>, tmpl::list<>>,\n        tmpl::pair<TimeStepper, TimeSteppers::time_steppers>,\n        tmpl::pair<Trigger, tmpl::list<Triggers::Always, Triggers::SlabCompares,\n                                       Triggers::TimeCompares>>>;\n  };\n\n  using dg_registration_list =\n      tmpl::list<observers::Actions::RegisterWithObservers<\n                     Actions::ExportCoordinates<Dim>>,\n                 observers::Actions::RegisterWithObservers<\n                     Actions::FindGlobalMinimumGridSpacing>>;\n\n  using dg_element_array = DgElementArray<\n      Metavariables,\n      tmpl::list<\n          Parallel::PhaseActions<\n              Parallel::Phase::Initialization,\n              tmpl::list<\n                  Initialization::Actions::InitializeItems<\n                      Initialization::TimeStepping<Metavariables,\n                                                   TimeStepperBase>,\n                      evolution::dg::Initialization::Domain<Metavariables>,\n                      ::amr::Initialization::Initialize<volume_dim,\n                                                        Metavariables>,\n                      Initialization::SetMeshType<Dim>>,\n                  Initialization::Actions::AddComputeTags<tmpl::list<\n                      ::domain::Tags::MinimumGridSpacingCompute<\n                          Dim, Frame::Inertial>,\n                      ::domain::Tags::FlatLogicalMetricCompute<Dim>,\n                      ::Tags::StepperErrorEstimatesEnabledCompute<false>>>,\n                  Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Register,\n              tmpl::push_back<dg_registration_list,\n                              Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Restart,\n              tmpl::push_back<dg_registration_list,\n                              Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<\n              Parallel::Phase::Execute,\n              tmpl::flatten<tmpl::list<\n                  Actions::MutateApply<AdvanceTime<>>,\n                  Actions::ExportCoordinates<Dim>,\n                  Actions::FindGlobalMinimumGridSpacing,\n                  std::conditional_t<local_time_stepping,\n                                     evolution::Actions::RunEventsAndTriggers<\n                                         Triggers::WhenToCheck::AtSteps>,\n                                     tmpl::list<>>,\n                  evolution::Actions::RunEventsAndTriggers<\n                      Triggers::WhenToCheck::AtSlabs>,\n                  PhaseControl::Actions::ExecutePhaseChange>>>>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = tmpl::list<\n        Initialization::ProjectTimeStepping<volume_dim>,\n        evolution::dg::Initialization::ProjectDomain<volume_dim>,\n        ::amr::projectors::DefaultInitialize<\n            Initialization::Tags::InitialTimeDelta,\n            Initialization::Tags::InitialSlabSize<local_time_stepping>,\n            ::domain::Tags::InitialExtents<Dim>,\n            ::domain::Tags::InitialRefinementLevels<Dim>,\n            evolution::dg::Tags::Quadrature>>;\n    static constexpr bool keep_coarse_grids = false;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<dg_element_array, dg_registration_list>>;\n  };\n\n  using component_list =\n      tmpl::list<::amr::Component<Metavariables>, dg_element_array,\n                 mem_monitor::MemoryMonitor<Metavariables>,\n                 observers::Observer<Metavariables>,\n                 observers::ObserverWriter<Metavariables>>;\n\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<MinGridSpacingReductionData>>;\n\n  static constexpr auto default_phase_order =\n      std::array{Parallel::Phase::Initialization, Parallel::Phase::Register,\n                 Parallel::Phase::CheckDomain, Parallel::Phase::Execute,\n                 Parallel::Phase::Exit};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n/// \\endcond\n"
  },
  {
    "path": "src/Executables/ExportEquationOfStateForRotNS/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE ExportEquationOfStateForRotNS)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  ExportEquationOfStateForRotNS.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Boost::boost\n  Boost::program_options\n  DataStructures\n  Hydro\n  Printf\n  Utilities\n  )\n"
  },
  {
    "path": "src/Executables/ExportEquationOfStateForRotNS/ExportEquationOfStateForRotNS.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <boost/program_options.hpp>\n#include <cmath>\n#include <cstddef>\n#include <fstream>\n#include <iomanip>\n#include <iterator>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// Charm looks for this function but since we build without a main function or\n// main module supplied by Charm++, we just have it be empty\nextern \"C\" void CkRegisterMainModule(void) {}\n\nnamespace {\nvoid dump_barotropic_eos(\n    const EquationsOfState::EquationOfState<true, 1>& eos,\n    const size_t number_of_log10_number_density_points_for_dump,\n    const std::string& output_file_name,\n    const double lower_bound_rest_mass_density_cgs,\n    const double upper_bound_rest_mass_density_cgs) {\n  using std::log10;\n  using std::pow;\n  // Baryon mass, used to go from number density to rest mass\n  // density. I.e. `rho_cgs = n_cgs * baryon_mass`, where `n_gcs` is the number\n  // density in CGS units. This is the baryon mass that RotNS uses. This\n  // might be different from the baryon mass that the EoS uses.\n  //\n  // https://github.com/sxs-collaboration/spectre/issues/4694\n  const double baryon_mass_of_rotns_cgs =\n      hydro::units::geometric::default_baryon_mass *\n      hydro::units::cgs::mass_unit;\n  const double log10_lower_bound_number_density_cgs =\n      log10(lower_bound_rest_mass_density_cgs / baryon_mass_of_rotns_cgs);\n  const double log10_upper_bound_number_density_cgs =\n      log10(upper_bound_rest_mass_density_cgs / baryon_mass_of_rotns_cgs);\n  const double delta_log_number_density_cgs =\n      (log10_upper_bound_number_density_cgs -\n       log10_lower_bound_number_density_cgs) /\n      static_cast<double>(number_of_log10_number_density_points_for_dump - 1);\n\n  if (file_system::check_if_file_exists(output_file_name)) {\n    ERROR_NO_TRACE(\"File \" << output_file_name\n                           << \" already exists. Refusing to overwrite.\");\n  }\n  std::ofstream outfile(output_file_name.c_str());\n\n  for (size_t log10_number_density_index = 0;\n       log10_number_density_index <\n       number_of_log10_number_density_points_for_dump;\n       ++log10_number_density_index) {\n    using std::pow;\n    const double number_density_cgs =\n        pow(10.0, log10_lower_bound_number_density_cgs +\n                      static_cast<double>(log10_number_density_index) *\n                          delta_log_number_density_cgs);\n\n    // Note: we will want to add the baryon mass to our EOS interface.\n    //\n    // https://github.com/sxs-collaboration/spectre/issues/4694\n    const Scalar<double> rest_mass_density_geometric{\n        number_density_cgs * cube(hydro::units::cgs::length_unit) *\n        eos.baryon_mass()};\n    const Scalar<double> pressure_geometric =\n        eos.pressure_from_density(rest_mass_density_geometric);\n    const Scalar<double> specific_internal_energy_geometric =\n        eos.specific_internal_energy_from_density(rest_mass_density_geometric);\n    const Scalar<double> total_energy_density_geometric{\n        get(rest_mass_density_geometric) *\n        (1.0 + get(specific_internal_energy_geometric))};\n\n    // Note: the energy density is divided by c^2\n    const double total_energy_density_cgs =\n        get(total_energy_density_geometric) *\n        hydro::units::cgs::rest_mass_density_unit;\n\n    // should be dyne cm^(-3)\n    const double pressure_cgs =\n        get(pressure_geometric) * hydro::units::cgs::pressure_unit;\n\n    outfile << std::scientific << std::setw(24) << std::setprecision(14)\n            << log10(number_density_cgs) << std::setw(24)\n            << std::setprecision(14) << log10(total_energy_density_cgs)\n            << std::setw(24) << std::setprecision(14) << log10(pressure_cgs)\n            << std::endl;\n  }\n  outfile.close();\n}\n\nnamespace OptionTags {\nstruct NumberOfPoints {\n  using type = size_t;\n  static constexpr Options::String help = {\n      \"Number of points at which to dump the EoS\"};\n};\n\nstruct OutputFileName {\n  using type = std::string;\n  static constexpr Options::String help = {\n      \"Name of the output file to dump the EoS to, including file extension.\"};\n};\n\nstruct LowerBoundRestMassDensityCgs {\n  using type = double;\n  static constexpr Options::String help = {\n      \"Lower bound of rest mass density in CGS units.\"};\n};\n\nstruct UpperBoundRestMassDensityCgs {\n  using type = double;\n  static constexpr Options::String help = {\n      \"Upper bound of rest mass density in CGS units.\"};\n};\n}  // namespace OptionTags\n}  // namespace\n\nint main(int argc, char** argv) {\n  namespace bpo = boost::program_options;\n  bpo::positional_options_description pos_desc;\n\n  const std::string help_string =\n      \"Dump a relativistic barotropic equation of state to disk.\\n\"\n      \"All options controlling input and output are read from the input file.\\n\"\n      \"This executable can be extended to support 2d and 3d EoS, where \"\n      \"temperature and electron fraction dependence is available.\";\n\n  bpo::options_description desc(help_string);\n  desc.add_options()(\"help,h,\", \"show this help message\")(\n      \"input-file\", bpo::value<std::string>()->required(), \"Input file name\")(\n      \"check-options\", \"Check input file options\");\n\n  bpo::variables_map vars;\n\n  bpo::store(bpo::command_line_parser(argc, argv)\n                 .positional(pos_desc)\n                 .options(desc)\n                 .run(),\n             vars);\n\n  if (vars.count(\"help\") != 0u or vars.count(\"input-file\") == 0u) {\n    Parallel::printf(\"%s\\n\", desc);\n    return 1;\n  }\n\n  using option_list =\n      tmpl::list<hydro::OptionTags::InitialDataEquationOfState<true, 1>,\n                 OptionTags::NumberOfPoints, OptionTags::OutputFileName,\n                 OptionTags::LowerBoundRestMassDensityCgs,\n                 OptionTags::UpperBoundRestMassDensityCgs>;\n\n  Options::Parser<option_list> option_parser(help_string);\n  option_parser.parse_file(vars[\"input-file\"].as<std::string>());\n\n  if (vars.count(\"check-options\") != 0) {\n    // Force all the options to be created.\n    option_parser.template apply<option_list>([](auto... args) {\n      (void)std::initializer_list<char>{((void)args, '0')...};\n    });\n    Parallel::printf(\"\\n%s parsed successfully!\\n\",\n                     vars[\"input-file\"].as<std::string>());\n\n    return 0;\n  }\n\n  const auto options =\n      option_parser.template apply<option_list>([](auto... args) {\n        return tuples::tagged_tuple_from_typelist<option_list>(\n            std::move(args)...);\n      });\n\n  dump_barotropic_eos(\n      *get<hydro::OptionTags::InitialDataEquationOfState<true, 1>>(options),\n      get<OptionTags::NumberOfPoints>(options),\n      get<OptionTags::OutputFileName>(options),\n      get<OptionTags::LowerBoundRestMassDensityCgs>(options),\n      get<OptionTags::UpperBoundRestMassDensityCgs>(options));\n\n  return 0;\n}\n"
  },
  {
    "path": "src/Executables/ParallelInfo/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE ParallelInfo)\n\nset(EXECUTABLE_SOURCES\n  ParallelInfo.cpp\n  ${EXECUTABLE}.decl.h\n  )\n\nadd_charm_module(${EXECUTABLE})\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  ${EXECUTABLE_SOURCES}\n  )\n\nadd_dependencies(\n  ${EXECUTABLE}\n  module_AlgorithmSingleton\n  module_GlobalCache\n  module_Main\n  module_ParallelInfo\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Charmxx::main\n  ErrorHandling\n  Informer\n  Parallel\n  Printf\n  SystemUtilities\n  )\n"
  },
  {
    "path": "src/Executables/ParallelInfo/ParallelInfo.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmainmodule ParallelInfo {\n  mainchare ParallelInfo {\n    entry ParallelInfo(CkArgMsg * msg);\n    entry void start_node_group_check();\n    entry void end_report();\n  };\n  group PeGroupReporter {\n    entry PeGroupReporter(CkCallback);\n  };\n  nodegroup NodeGroupReporter {\n    entry NodeGroupReporter(CkCallback);\n  };\n};\n"
  },
  {
    "path": "src/Executables/ParallelInfo/ParallelInfo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Executables/ParallelInfo/ParallelInfo.hpp\"\n\n#include <charm++.h>\n#include <ckcallback.h>\n#include <ostream>\n#include <string>\n\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/System/Exit.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nParallelInfo::ParallelInfo(CkArgMsg* msg) {\n  // clang-tidy: do not use pointer arithmetic\n  Parallel::printf(\"Executing '%s' using %d processors.\\n\",\n                   msg->argv[0],  // NOLINT\n                   sys::number_of_procs());\n  if (msg->argc > 1) {\n    std::stringstream error_msg;\n    error_msg << \"Expected zero command line options, not \" << msg->argc - 1\n              << \"\\nReceived:\\n\";\n    for (int i = 1; i < msg->argc; ++i) {\n      // clang-tidy: do not use pointer arithmetic\n      error_msg << \"'\" << msg->argv[i] << \"'\\n\\n\";  // NOLINT\n    }\n    ERROR(error_msg.str());\n  }\n\n  Parallel::printf(\"%s\\n\", info_from_build());\n\n  Parallel::printf(\n      \"\\nHeader:\\n\"\n      \"[1]Node ID            (my_node)\\n\"\n      \"[2]PE ID              (my_proc)\\n\"\n      \"[3]Number Of Procs    (number_of_procs)\\n\"\n      \"[4]Number Of Nodes    (number_of_nodes)\\n\"\n      \"[5]Procs On Node      (procs_on_node)\\n\"\n      \"[6]My Local Rank      (my_local_rank)\\n\"\n      \"[7]First Proc On Node (first_proc_on_node)\\n\"\n      \"[8]Node Of This PE    (node_of)\\n\"\n      \"[9]Local Rank Of PE   (local_rank_of)\\n\");\n  start_pe_group_check();\n}\n\nvoid ParallelInfo::start_pe_group_check() const {\n  Parallel::printf(\"\\n\\nStarting Group Check.\\n\");\n  CProxy_PeGroupReporter::ckNew(CkCallback(\n      CkIndex_ParallelInfo::start_node_group_check(), this->thisProxy));\n}\n\nvoid ParallelInfo::start_node_group_check() const {\n  CProxy_NodeGroupReporter::ckNew(\n      CkCallback(CkIndex_ParallelInfo::end_report(), this->thisProxy));\n}\n\n[[noreturn]] void ParallelInfo::end_report() {\n  Parallel::printf(\"\\nReport finished.\\n\");\n  sys::exit();\n}\n\nnamespace {\n// Helper print function so that both node and pe group prints can be changed\n// easily.\nvoid print_info() {\n  // NOLINTNEXTLINE(cppcoreguidelines-init-variables) false positive\n  const int digits_in_node = number_of_digits(sys::number_of_nodes() - 1);\n  // NOLINTNEXTLINE(cppcoreguidelines-init-variables) false positive\n  const int digits_in_pe = number_of_digits(sys::number_of_procs() - 1);\n  // The format string is generated based on the number of procs and nodes\n  // available so that the output is aligned over all nodes and procs\n  Parallel::printf(\n      \"%0\"s + std::to_string(digits_in_node) + \"d %0\" +  // my_node\n          std::to_string(digits_in_pe) +\n          \"d %d %d %0\" +  // my_proc, number procs, number nodes\n          std::to_string(digits_in_node) +\n          \"d %03d %d %0\" +  // procs_on_node, local rank, first proc\n          std::to_string(digits_in_node) + \"d %03d\\n\"s  // this node, local rank\n      ,\n      sys::my_node(), sys::my_proc(), sys::number_of_procs(),\n      sys::number_of_nodes(), sys::procs_on_node(sys::my_node()),\n      sys::my_local_rank(), sys::first_proc_on_node(sys::my_node()),\n      sys::node_of(sys::my_proc()), sys::local_rank_of(sys::my_proc()));\n}\n}  // namespace\n\nPeGroupReporter::PeGroupReporter(const CkCallback& cb_start_node_group_check) {\n  print_info();\n  this->contribute(cb_start_node_group_check);\n}\n\nNodeGroupReporter::NodeGroupReporter(const CkCallback& cb_end_report) {\n  print_info();\n  this->contribute(cb_end_report);\n}\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Weffc++\"\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"\n#pragma GCC diagnostic ignored \"-Wshadow\"\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\n#include \"Executables/ParallelInfo/ParallelInfo.def.h\"\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "src/Executables/ParallelInfo/ParallelInfo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares the chares for ParallelInfo\n\n#pragma once\n\n#include \"Executables/ParallelInfo/ParallelInfo.decl.h\"\n#include \"Parallel/Reduction.hpp\"\n\n/// \\cond\nclass CkArgMsg;\nclass CkCallback;\n/// \\endcond\n\n/*!\n * \\page ParallelInfoExecutablePage ParallelInfo Executable\n * \\tableofcontents\n * The %ParallelInfo executable can be used to check the number of nodes, and\n * processing elements (roughly number of cores) registered with Charm++.\n * Depending on the build of Charm++ the way Charm++ identifies nodes will vary.\n * Specifically, for non-SMP builds of Charm++ each processing element is\n * identified as a node instead of each physical node being identified as a\n * node. The substantially increased number of \"nodes\" for non-SMP builds can\n * cause problems if large amounts of read-only data is cached on a per-node\n * basis.\n *\n * The ParallelInfo executable starts one process on each processing element and\n * each node, which then all print identifying information such as the\n * processing element ID, node ID, etc.\n */\n\n/// \\cond HIDDEN_SYMBOLS\nclass ParallelInfo : public CBase_ParallelInfo {\n public:\n  explicit ParallelInfo(CkArgMsg* msg);\n  void start_node_group_check() const;\n  [[noreturn]] static void end_report();\n\n private:\n  void start_pe_group_check() const;\n};\n\nclass PeGroupReporter : public Group {\n public:\n  explicit PeGroupReporter(const CkCallback& cb_start_node_group_check);\n};\n\nclass NodeGroupReporter : public NodeGroup {\n public:\n  explicit NodeGroupReporter(const CkCallback& cb_end_report);\n};\n/// \\endcond\n"
  },
  {
    "path": "src/Executables/PreprocessCceWorldtube/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE PreprocessCceWorldtube)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  PreprocessCceWorldtube.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Boost::boost\n  Boost::program_options\n  Cce\n  GeneralRelativity\n  Informer\n  Options\n  Parallel\n  Printf\n  Spectral\n  SpinWeightedSphericalHarmonics\n  )\n\nif(BUILD_TESTING)\n  add_dependencies(test-executables ${EXECUTABLE})\nendif()\n"
  },
  {
    "path": "src/Executables/PreprocessCceWorldtube/PreprocessCceWorldtube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <boost/program_options.hpp>\n#include <chrono>\n#include <cstddef>\n#include <exception>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/ExtractionRadius.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeModeRecorder.hpp\"\n#include \"IO/H5/CombineH5.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/CreateFromOptions.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// Charm looks for this function but since we build without a main function or\n// main module we just have it be empty\nextern \"C\" void CkRegisterMainModule(void) {}\n\nnamespace {\n// Convenient tag lists\nusing modal_metric_input_tags = Cce::cce_metric_input_tags<ComplexModalVector>;\nusing nodal_metric_input_tags = Cce::cce_metric_input_tags<DataVector>;\nusing modal_bondi_input_tags = Cce::Tags::worldtube_boundary_tags_for_writing<\n    Spectral::Swsh::Tags::SwshTransform>;\nusing nodal_bondi_input_tags =\n    Cce::Tags::worldtube_boundary_tags_for_writing<Cce::Tags::BoundaryValue>;\n\nusing AdmOptions = Cce::MetricWorldtubeH5BufferUpdater<DataVector>::AdmOptions;\n\n// from a data-varies-fastest set of buffers provided by\n// `MetricWorldtubeH5BufferUpdater` extract the set of coefficients for a\n// particular time given by `buffer_time_offset` into the `time_span` size of\n// buffer.\nvoid slice_buffers_to_libsharp_modes(\n    const gsl::not_null<Variables<modal_metric_input_tags>*> coefficients_set,\n    const Variables<modal_metric_input_tags>& coefficients_buffers,\n    const size_t buffer_time_offset, const size_t computation_l_max) {\n  SpinWeighted<ComplexModalVector, 0> goldberg_mode_buffer;\n  SpinWeighted<ComplexModalVector, 0> libsharp_mode_buffer;\n\n  const size_t goldberg_mode_size = square(computation_l_max + 1);\n  const size_t libsharp_mode_size =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(computation_l_max);\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      tmpl::for_each<Cce::Tags::detail::apply_derivs_t<\n          Cce::Tags::detail::SpatialMetric<ComplexModalVector>>>(\n          [&](auto tag_v) {\n            using tag = typename decltype(tag_v)::type;\n            const auto& all_goldberg_modes =\n                get<tag>(coefficients_buffers).get(i, j);\n            auto& all_libsharp_modes = get<tag>(*coefficients_set).get(i, j);\n\n            // NOLINTBEGIN\n            goldberg_mode_buffer.set_data_ref(\n                const_cast<ComplexModalVector&>(all_goldberg_modes).data() +\n                    buffer_time_offset * goldberg_mode_size,\n                goldberg_mode_size);\n            libsharp_mode_buffer.set_data_ref(all_libsharp_modes.data(),\n                                              libsharp_mode_size);\n            // NOLINTEND\n\n            Spectral::Swsh::goldberg_to_libsharp_modes(\n                make_not_null(&libsharp_mode_buffer), goldberg_mode_buffer,\n                computation_l_max);\n          });\n    }\n    tmpl::for_each<Cce::Tags::detail::apply_derivs_t<\n        Cce::Tags::detail::Shift<ComplexModalVector>>>([&](auto tag_v) {\n      using tag = typename decltype(tag_v)::type;\n      const auto& all_goldberg_modes = get<tag>(coefficients_buffers).get(i);\n      auto& all_libsharp_modes = get<tag>(*coefficients_set).get(i);\n\n      // NOLINTBEGIN\n      goldberg_mode_buffer.set_data_ref(\n          const_cast<ComplexModalVector&>(all_goldberg_modes).data() +\n              buffer_time_offset * goldberg_mode_size,\n          goldberg_mode_size);\n      libsharp_mode_buffer.set_data_ref(all_libsharp_modes.data(),\n                                        libsharp_mode_size);\n      // NOLINTEND\n\n      Spectral::Swsh::goldberg_to_libsharp_modes(\n          make_not_null(&libsharp_mode_buffer), goldberg_mode_buffer,\n          computation_l_max);\n    });\n  }\n  tmpl::for_each<Cce::Tags::detail::apply_derivs_t<\n      Cce::Tags::detail::Lapse<ComplexModalVector>>>([&](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    const auto& all_goldberg_modes = get(get<tag>(coefficients_buffers));\n    auto& all_libsharp_modes = get(get<tag>(*coefficients_set));\n\n    // NOLINTBEGIN\n    goldberg_mode_buffer.set_data_ref(\n        const_cast<ComplexModalVector&>(all_goldberg_modes).data() +\n            buffer_time_offset * goldberg_mode_size,\n        goldberg_mode_size);\n    libsharp_mode_buffer.set_data_ref(all_libsharp_modes.data(),\n                                      libsharp_mode_size);\n    // NOLINTEND\n\n    Spectral::Swsh::goldberg_to_libsharp_modes(\n        make_not_null(&libsharp_mode_buffer), goldberg_mode_buffer,\n        computation_l_max);\n  });\n}\n\ntemplate <typename BoundaryData>\nvoid write_bondi_data_to_disk(\n    const gsl::not_null<Cce::WorldtubeModeRecorder*> recorder,\n    const BoundaryData& nodal_boundary_data, const double time,\n    const size_t data_l_max) {\n  tmpl::for_each<nodal_bondi_input_tags>([&](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n\n    const ComplexDataVector& nodal_data =\n        get(get<tag>(nodal_boundary_data)).data();\n\n    recorder->append_modal_data<tag::tag::type::type::spin>(\n        Cce::dataset_label_for_tag<typename tag::tag>(), time, nodal_data,\n        data_l_max);\n  });\n}\n\n// read in the data from a (previously standard) SpEC worldtube file\n// `input_file`, perform the boundary computation, and dump the (considerably\n// smaller) dataset associated with the spin-weighted scalars to\n// `output_file`.\nvoid perform_cce_worldtube_reduction(\n    const std::string& input_file, const std::string& output_file,\n    const size_t input_buffer_depth, const size_t l_max_factor,\n    const std::optional<double>& extraction_radius,\n    const bool fix_spec_normalization, const bool descending_m) {\n  Cce::MetricWorldtubeH5BufferUpdater<ComplexModalVector> buffer_updater{\n      input_file, extraction_radius, descending_m};\n  const size_t l_max = buffer_updater.get_l_max();\n  // Perform the boundary computation to scalars at some factor > 1 of the input\n  // l_max to be absolutely certain that there are no problems associated with\n  // aliasing.\n  const size_t computation_l_max = l_max_factor * l_max;\n\n  const DataVector& time_buffer = buffer_updater.get_time_buffer();\n  // We're not interpolating in time, this is just a reasonable number of rows\n  // to ingest at a time. If the buffer depth from the input file is larger than\n  // the number of times we have, just use the number of times\n  const size_t buffer_depth = std::min(time_buffer.size(), input_buffer_depth);\n  const size_t size_of_buffer = square(computation_l_max + 1) * buffer_depth;\n\n  Variables<modal_metric_input_tags> coefficients_buffers{size_of_buffer};\n  Variables<modal_metric_input_tags> coefficients_set{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(computation_l_max)};\n\n  Variables<Cce::Tags::characteristic_worldtube_boundary_tags<\n      Cce::Tags::BoundaryValue>>\n      boundary_data_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(computation_l_max)};\n\n  size_t time_span_start = 0;\n  size_t time_span_end = 0;\n  Cce::WorldtubeModeRecorder recorder{l_max, output_file};\n\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    const double time = time_buffer[i];\n    buffer_updater.update_buffers_for_time(\n        make_not_null(&coefficients_buffers), make_not_null(&time_span_start),\n        make_not_null(&time_span_end), time, computation_l_max, 0, buffer_depth,\n        false);\n\n    slice_buffers_to_libsharp_modes(make_not_null(&coefficients_set),\n                                    coefficients_buffers, i - time_span_start,\n                                    computation_l_max);\n\n    const auto create_boundary_data = [&](const auto&... tags) {\n      if (not buffer_updater.has_version_history() and fix_spec_normalization) {\n        Cce::create_bondi_boundary_data_from_unnormalized_spec_modes(\n            make_not_null(&boundary_data_variables),\n            get<tmpl::type_from<std::decay_t<decltype(tags)>>>(\n                coefficients_set)...,\n            buffer_updater.get_extraction_radius(), computation_l_max);\n      } else {\n        Cce::create_bondi_boundary_data(\n            make_not_null(&boundary_data_variables),\n            get<tmpl::type_from<std::decay_t<decltype(tags)>>>(\n                coefficients_set)...,\n            buffer_updater.get_extraction_radius(), computation_l_max);\n      }\n    };\n\n    tmpl::as_pack<modal_metric_input_tags>(create_boundary_data);\n\n    write_bondi_data_to_disk(make_not_null(&recorder), boundary_data_variables,\n                             time, computation_l_max);\n  }\n}\n\ntemplate <typename BoundaryTags>\ntuples::tagged_tuple_from_typelist<nodal_bondi_input_tags>\ncreate_bondi_nodal_views(const Variables<BoundaryTags>& bondi_boundary_data,\n                         const size_t time_offset, const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  tuples::tagged_tuple_from_typelist<nodal_bondi_input_tags> result;\n\n  tmpl::for_each<nodal_bondi_input_tags>([&](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n\n    make_const_view(\n        make_not_null(&std::as_const(get(tuples::get<tag>(result)).data())),\n        get(get<tag>(bondi_boundary_data)).data(),\n        time_offset * number_of_angular_points, number_of_angular_points);\n  });\n\n  return result;\n}\n\ntemplate <typename BoundaryTags>\ntuples::tagged_tuple_from_typelist<nodal_metric_input_tags>\ncreate_metric_nodal_views(const Variables<BoundaryTags>& bondi_boundary_data,\n                          const size_t time_offset, const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  tuples::tagged_tuple_from_typelist<nodal_metric_input_tags> result;\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      tmpl::for_each<Cce::Tags::detail::apply_derivs_t<\n          Cce::Tags::detail::SpatialMetric<DataVector>>>([&](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n\n        make_const_view(\n            make_not_null(&std::as_const(tuples::get<tag>(result).get(i, j))),\n            get<tag>(bondi_boundary_data).get(i, j),\n            time_offset * number_of_angular_points, number_of_angular_points);\n      });\n    }\n    tmpl::for_each<Cce::Tags::detail::apply_derivs_t<\n        Cce::Tags::detail::Shift<DataVector>>>([&](auto tag_v) {\n      using tag = typename decltype(tag_v)::type;\n\n      make_const_view(\n          make_not_null(&std::as_const(tuples::get<tag>(result).get(i))),\n          get<tag>(bondi_boundary_data).get(i),\n          time_offset * number_of_angular_points, number_of_angular_points);\n    });\n  }\n\n  tmpl::for_each<\n      Cce::Tags::detail::apply_derivs_t<Cce::Tags::detail::Lapse<DataVector>>>(\n      [&](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n\n        make_const_view(\n            make_not_null(&std::as_const(get(tuples::get<tag>(result)))),\n            get(get<tag>(bondi_boundary_data)),\n            time_offset * number_of_angular_points, number_of_angular_points);\n      });\n\n  return result;\n}\n\nvoid bondi_nodal_to_bondi_modal(\n    const std::string& input_file, const std::string& output_file,\n    const size_t input_buffer_depth,\n    const std::optional<double>& extraction_radius) {\n  Cce::BondiWorldtubeH5BufferUpdater<ComplexDataVector> buffer_updater{\n      input_file, extraction_radius};\n  const size_t l_max = buffer_updater.get_l_max();\n\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const DataVector& time_buffer = buffer_updater.get_time_buffer();\n  // We're not interpolating in time, this is just a reasonable number of rows\n  // to ingest at a time. If the buffer depth from the input file is larger than\n  // the number of times we have, just use the number of times\n  const size_t buffer_depth = std::min(time_buffer.size(), input_buffer_depth);\n  const size_t size_of_buffer = buffer_depth * number_of_angular_points;\n\n  Variables<nodal_bondi_input_tags> nodal_buffer{size_of_buffer};\n\n  size_t time_span_start = 0;\n  size_t time_span_end = 0;\n  Cce::WorldtubeModeRecorder recorder{l_max, output_file};\n\n  for (size_t i = 0; i < time_buffer.size(); i++) {\n    const double time = time_buffer[i];\n    buffer_updater.update_buffers_for_time(\n        make_not_null(&nodal_buffer), make_not_null(&time_span_start),\n        make_not_null(&time_span_end), time, l_max, 0, buffer_depth, false);\n\n    const auto nodal_data_at_time =\n        create_bondi_nodal_views(nodal_buffer, i - time_span_start, l_max);\n\n    write_bondi_data_to_disk(make_not_null(&recorder), nodal_data_at_time, time,\n                             l_max);\n  }\n}\n\nvoid metric_nodal_to_bondi_modal(const std::string& input_file,\n                                 const std::string& output_file,\n                                 const size_t input_buffer_depth,\n                                 const std::optional<double>& extraction_radius,\n                                 const std::optional<AdmOptions>& adm_options) {\n  Cce::MetricWorldtubeH5BufferUpdater<DataVector> buffer_updater{\n      input_file, extraction_radius, false, adm_options};\n  const size_t l_max = buffer_updater.get_l_max();\n\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const DataVector& time_buffer = buffer_updater.get_time_buffer();\n  // We're not interpolating in time, this is just a reasonable number of rows\n  // to ingest at a time. If the buffer depth from the input file is larger than\n  // the number of times we have, just use the number of times\n  const size_t buffer_depth = std::min(time_buffer.size(), input_buffer_depth);\n  const size_t size_of_buffer = buffer_depth * number_of_angular_points;\n\n  Variables<nodal_metric_input_tags> nodal_buffer{size_of_buffer};\n\n  Variables<Cce::Tags::characteristic_worldtube_boundary_tags<\n      Cce::Tags::BoundaryValue>>\n      boundary_data_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n  size_t time_span_start = 0;\n  size_t time_span_end = 0;\n  Cce::WorldtubeModeRecorder recorder{l_max, output_file};\n\n  for (size_t i = 0; i < time_buffer.size(); i++) {\n    const double time = time_buffer[i];\n\n    buffer_updater.update_buffers_for_time(\n        make_not_null(&nodal_buffer), make_not_null(&time_span_start),\n        make_not_null(&time_span_end), time, l_max, 0, buffer_depth, false);\n\n    const auto metric_nodal_data_at_time =\n        create_metric_nodal_views(nodal_buffer, i - time_span_start, l_max);\n\n    tmpl::as_pack<nodal_metric_input_tags>([&](const auto&... tags) {\n      Cce::create_bondi_boundary_data(\n          make_not_null(&boundary_data_variables),\n          get<tmpl::type_from<std::decay_t<decltype(tags)>>>(\n              metric_nodal_data_at_time)...,\n          buffer_updater.get_extraction_radius(), l_max);\n    });\n\n    write_bondi_data_to_disk(make_not_null(&recorder), boundary_data_variables,\n                             time, l_max);\n  }\n}\n\n// If we use the AdmOptions class directly in an option tag, the input file\n// would look like:\n//\n// InputDataFormat:\n//   AdvectiveLapse: True\n//   AdvectiveShift: True\n//   AuxiliaryShiftFactor: False\n//\n// However, this doesn't have the name `AdmMetricNodal`. This is consistent with\n// how our options work, but is confusing (especially for users who are\n// unfamiliar with spectre) since the actual input data format isn't shown. This\n// class now changes the input file to look like:\n//\n// InputDataFormat:\n//   AdmMetricNodal:\n//     AdvectiveLapse: True\n//     AdvectiveShift: True\n//     AuxiliaryShiftFactor: False\n//\n// which is much clearer.\nstruct AdmMetricNodalOptions {\n  struct AdmMetricNodal {\n    using type = ::AdmOptions;\n    static constexpr Options::String help = ::AdmOptions::help;\n  };\n\n  using options = tmpl::list<AdmMetricNodal>;\n  static constexpr Options::String help = ::AdmOptions::help;\n\n  AdmMetricNodalOptions() = default;\n  // NOLINTNEXTLINE\n  AdmMetricNodalOptions(::AdmOptions adm_metric_nodal_in)\n      : adm_metric_nodal(adm_metric_nodal_in) {}\n\n  ::AdmOptions adm_metric_nodal;\n};\n\nenum class InputDataFormat {\n  AdmMetricNodal,\n  MetricNodal,\n  MetricModal,\n  BondiNodal,\n  BondiModal\n};\n\nstd::ostream& operator<<(std::ostream& os,\n                         const InputDataFormat input_data_format) {\n  switch (input_data_format) {\n    case InputDataFormat::MetricNodal:\n      return os << \"MetricNodal\";\n    case InputDataFormat::MetricModal:\n      return os << \"MetricModal\";\n    case InputDataFormat::BondiNodal:\n      return os << \"BondiNodal\";\n    case InputDataFormat::BondiModal:\n      return os << \"BondiModal\";\n    default:\n      ERROR(\"Unknown InputDataFormat type\");\n  }\n}\n\nnamespace OptionTags {\nstruct InputH5Files {\n  static std::string name() { return \"InputH5File\"; }\n  using type = std::variant<std::string, std::vector<std::string>>;\n  static constexpr Options::String help =\n      \"Name of H5 worldtube file(s). A '.h5' extension will be added if \"\n      \"needed. Can specify a single file or if multiple files are specified, \"\n      \"this will combine the times in each file. If there are \"\n      \"duplicate/overlapping times, the last/latest of the times are chosen.\";\n};\n\nstruct InputDataFormat {\n  using type = std::variant<::InputDataFormat, AdmMetricNodalOptions>;\n  static constexpr Options::String help =\n      \"The type of data stored in the 'InputH5Files'. Can be 'AdmMetricNodal' \"\n      \"with additional options, 'MetricNodal', 'MetricModal', 'BondiNodal', or \"\n      \"'BondiModal'.\";\n};\n\nstruct OutputH5File {\n  using type = std::string;\n  static constexpr Options::String help =\n      \"Name of output H5 file. A '.h5' extension will be added if needed.\";\n};\n\nstruct ExtractionRadius {\n  using type = Options::Auto<double>;\n  static constexpr Options::String help =\n      \"The radius of the spherical worldtube. \"\n      \"If the 'InputH5File' supplied ends with '_RXXXX.h5' (where XXXX is the \"\n      \"zero-padded extraction radius rounded to the nearest integer), then \"\n      \"this option should be 'Auto'. If the extraction radius is not supplied \"\n      \"in the 'InputH5File' name, then this option must be supplied. If the \"\n      \"extraction radius is supplied in the 'InputH5File' name, and this \"\n      \"option is specified, then this option will take precedence.\";\n};\n\nstruct FixSpecNormalization {\n  using type = bool;\n  static constexpr Options::String help =\n      \"Apply corrections associated with documented SpEC worldtube file \"\n      \"errors. If you are using worldtube data from SpECTRE or from another \"\n      \"NR code but in the SpECTRE format, then this option must be 'False'\";\n};\n\nstruct DescendingM {\n  using type = bool;\n  static constexpr Options::String help =\n      \"Whether the order of 'm' modes of the data for a given 'l' are stored \"\n      \"in descending order (m goes from +l to -l) or not (m goes from -l to \"\n      \"+l). This option must be 'False' when using worldtube data from SpECTRE \"\n      \"or another NR code in the SpECTRE format. This should only be 'True' if \"\n      \"your worldtube file is from the SpEC code.\";\n  static bool suggested_value() { return false; }\n};\n\nstruct BufferDepth {\n  using type = Options::Auto<size_t>;\n  static constexpr Options::String help =\n      \"Number of time steps to load during each call to the file-accessing \"\n      \"routines. Higher values mean fewer, larger loads from file into RAM. \"\n      \"Set to 'Auto' to use a default value (2000).\";\n};\n\nstruct LMaxFactor {\n  using type = Options::Auto<size_t>;\n  static constexpr Options::String help =\n      \"The boundary computations will be performed at a resolution that is \"\n      \"'LMaxFactor' times the input file LMax to avoid aliasing. Set to \"\n      \"'Auto' to use a default value (2).\";\n};\n}  // namespace OptionTags\n\nusing option_tags =\n    tmpl::list<OptionTags::InputH5Files, OptionTags::InputDataFormat,\n               OptionTags::OutputH5File, OptionTags::ExtractionRadius,\n               OptionTags::FixSpecNormalization, OptionTags::DescendingM,\n               OptionTags::BufferDepth, OptionTags::LMaxFactor>;\nusing OptionTuple = tuples::tagged_tuple_from_typelist<option_tags>;\n\nnamespace ReduceCceTags {\nstruct InputH5Files : db::SimpleTag {\n  using type = std::vector<std::string>;\n  using option_tags = tmpl::list<OptionTags::InputH5Files>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const std::variant<std::string, std::vector<std::string>>& option) {\n    std::vector<std::string> result = std::visit(\n        Overloader{[](const std::vector<std::string>& input) { return input; },\n                   [](const std::string& input) { return std::vector{input}; }},\n        option);\n    for (std::string& filename : result) {\n      if (not filename.ends_with(\".h5\")) {\n        filename += \".h5\";\n      }\n    }\n\n    return result;\n  }\n};\n\nstruct InputDataFormat : db::SimpleTag {\n private:\n  using option_type = std::variant<::InputDataFormat, AdmMetricNodalOptions>;\n\n public:\n  using type = std::variant<::InputDataFormat, AdmOptions>;\n  using option_tags = tmpl::list<OptionTags::InputDataFormat>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(option_type input_data_format) {\n    type result{};\n    if (std::holds_alternative<::InputDataFormat>(input_data_format)) {\n      result = std::get<::InputDataFormat>(input_data_format);\n    } else {\n      result =\n          std::get<AdmMetricNodalOptions>(input_data_format).adm_metric_nodal;\n    }\n\n    return result;\n  }\n};\n\nstruct OutputH5File : db::SimpleTag {\n  using type = std::string;\n  using option_tags = tmpl::list<OptionTags::OutputH5File>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(std::string option) {\n    if (not option.ends_with(\".h5\")) {\n      option += \".h5\";\n    }\n    return option;\n  }\n};\n\nstruct ExtractionRadius : db::SimpleTag {\n  using type = std::optional<double>;\n  using option_tags = tmpl::list<OptionTags::ExtractionRadius>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const std::optional<double>& option) {\n    return option;\n  }\n};\n\nstruct FixSpecNormalization : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::FixSpecNormalization>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const bool option) { return option; }\n};\n\nstruct DescendingM : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::DescendingM>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const bool option) { return option; }\n};\n\nstruct BufferDepth : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::BufferDepth>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const std::optional<size_t>& option) {\n    return option.value_or(2000);\n  }\n};\n\nstruct LMaxFactor : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::BufferDepth>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const std::optional<size_t>& option) {\n    return option.value_or(2);\n  }\n};\n}  // namespace ReduceCceTags\n\nusing tags =\n    tmpl::list<ReduceCceTags::InputH5Files, ReduceCceTags::InputDataFormat,\n               ReduceCceTags::OutputH5File, ReduceCceTags::ExtractionRadius,\n               ReduceCceTags::FixSpecNormalization, ReduceCceTags::DescendingM,\n               ReduceCceTags::BufferDepth, ReduceCceTags::LMaxFactor>;\nusing TagsTuple = tuples::tagged_tuple_from_typelist<tags>;\n}  // namespace\n\n// Has to be outside the anon namespace\ntemplate <>\nstruct Options::create_from_yaml<InputDataFormat> {\n  template <typename Metavariables>\n  static InputDataFormat create(const Options::Option& options) {\n    const auto ordering = options.parse_as<std::string>();\n    if (ordering == \"MetricNodal\") {\n      return InputDataFormat::MetricNodal;\n    } else if (ordering == \"MetricModal\") {\n      return InputDataFormat::MetricModal;\n    } else if (ordering == \"BondiNodal\") {\n      return InputDataFormat::BondiNodal;\n    } else if (ordering == \"BondiModal\") {\n      return InputDataFormat::BondiModal;\n    }\n    PARSE_ERROR(options.context(),\n                \"InputDataFormat must be 'AdmMetricNodal', 'MetricNodal', \"\n                \"'MetricModal', 'BondiNodal', or 'BondiModal'\");\n  }\n};\n\n/*\n * This executable is used for converting the unnecessarily large SpEC worldtube\n * data format into a far smaller representation (roughly a factor of 4) just\n * storing the worldtube scalars that are required as input for CCE.\n */\nint main(int argc, char** argv) {\n  // Boost options for the input yaml and --help flag\n  boost::program_options::options_description desc(\"Options\");\n  desc.add_options()(\"help,h,\", \"show this help message\")(\n      \"input-file\", boost::program_options::value<std::string>()->required(),\n      \"Name of YAML input file to use.\");\n\n  boost::program_options::variables_map vars;\n  boost::program_options::store(\n      boost::program_options::command_line_parser(argc, argv)\n          .options(desc)\n          .run(),\n      vars);\n\n  // Option parser for all the actual options\n  Options::Parser<option_tags> parser{\n      \"This executable is used for converting the unnecessarily large metric \"\n      \"worldtube data format into a smaller representation (roughly a factor \"\n      \"of 4) just storing the worldtube scalars that are required as \"\n      \"input for CCE.\"};\n\n  // Help is a successful return\n  if (vars.contains(\"help\")) {\n    Parallel::printf(\"%s\\n%s\", desc, parser.help());\n    return 0;\n  }\n\n  // Not specifying an input file is an error\n  if (not vars.contains(\"input-file\")) {\n    Parallel::printf(\"Missing input file. Pass '--input-file'\");\n    return 1;\n  }\n\n  // Wrap in try-catch to print nice errors and terminate gracefully\n  try {\n    const std::string input_yaml = vars[\"input-file\"].as<std::string>();\n\n    // Actually parse the yaml. This does a check if it exists.\n    parser.parse_file(input_yaml, false);\n\n    // First create option tags, and then actual tags.\n    const OptionTuple options = parser.template apply<option_tags>(\n        [](auto... args) { return OptionTuple(std::move(args)...); });\n    const TagsTuple inputs =\n        Parallel::create_from_options<void>(options, tags{});\n\n    const auto& input_data_format =\n        tuples::get<ReduceCceTags::InputDataFormat>(inputs);\n    const InputDataFormat input_data_format_enum =\n        std::holds_alternative<InputDataFormat>(input_data_format)\n            ? std::get<InputDataFormat>(input_data_format)\n            : InputDataFormat::AdmMetricNodal;\n    const std::vector<std::string>& input_files =\n        tuples::get<ReduceCceTags::InputH5Files>(inputs);\n    const std::string& output_h5_file =\n        tuples::get<ReduceCceTags::OutputH5File>(inputs);\n\n    std::optional<std::string> temporary_combined_h5_file{};\n\n    if (input_files.size() != 1) {\n      // If the input format is BondiModal, then we don't actually have to do\n      // any transformations, only combining H5 files. So the temporary file\n      // name is just the output file\n      if (input_data_format_enum == InputDataFormat::BondiModal) {\n        temporary_combined_h5_file = output_h5_file;\n      } else {\n        // Otherwise we have to do a transformation so a temporary H5 file is\n        // necessary. Name the file based on the current time so it doesn't\n        // conflict with another h5 file\n        const auto now = std::chrono::system_clock::now();\n        const auto now_ns =\n            std::chrono::time_point_cast<std::chrono::nanoseconds>(now);\n        const auto value = now_ns.time_since_epoch();\n        temporary_combined_h5_file =\n            \"tmp_combined_\" + std::to_string(value.count()) + \".h5\";\n      }\n\n      // Now combine the h5 files into a single file\n      h5::combine_h5_dat(input_files, temporary_combined_h5_file.value(),\n                         Verbosity::Quiet);\n    } else if (input_data_format_enum == InputDataFormat::BondiModal) {\n      // Error here if the input data format is BondiModal since there's nothing\n      // to do\n      ERROR_NO_TRACE(\n          \"Only a single input H5 file was supplied and the input data \"\n          \"format is BondiModal. This means that no combination needs to be \"\n          \"done and running PreprocessCceWorldtube is unnecessary.\");\n    }\n\n    if (tuples::get<ReduceCceTags::FixSpecNormalization>(inputs)) {\n      if (input_data_format_enum != InputDataFormat::MetricModal) {\n        ERROR_NO_TRACE(\n            \"The option FixSpecNormalization can only be 'true' when the input \"\n            \"data format is MetricModal. Otherwise, it must be 'false'\");\n      }\n      if (not tuples::get<ReduceCceTags::DescendingM>(inputs)) {\n        ERROR_NO_TRACE(\n            \"The option FixSpecNormalization can only be 'true' if \"\n            \"'DescendingM' is true as well.\");\n      }\n    }\n\n    const auto input_worldtube_filename = [&]() -> const std::string& {\n      return temporary_combined_h5_file.has_value()\n                 ? temporary_combined_h5_file.value()\n                 : input_files[0];\n    };\n\n    const auto clean_temporary_file = [&temporary_combined_h5_file]() {\n      if (temporary_combined_h5_file.has_value()) {\n        file_system::rm(temporary_combined_h5_file.value(), false);\n      }\n    };\n\n    switch (input_data_format_enum) {\n      case InputDataFormat::BondiModal: {\n        // Nothing to do here because this is the desired output format and the\n        // H5 files were combined above\n        return 0;\n      }\n      case InputDataFormat::BondiNodal: {\n        bondi_nodal_to_bondi_modal(\n            input_worldtube_filename(), output_h5_file,\n            tuples::get<ReduceCceTags::BufferDepth>(inputs),\n            tuples::get<ReduceCceTags::ExtractionRadius>(inputs));\n\n        clean_temporary_file();\n        return 0;\n      }\n      case InputDataFormat::MetricModal: {\n        perform_cce_worldtube_reduction(\n            input_worldtube_filename(), output_h5_file,\n            tuples::get<ReduceCceTags::BufferDepth>(inputs),\n            tuples::get<ReduceCceTags::LMaxFactor>(inputs),\n            tuples::get<ReduceCceTags::ExtractionRadius>(inputs),\n            tuples::get<ReduceCceTags::FixSpecNormalization>(inputs),\n            tuples::get<ReduceCceTags::DescendingM>(inputs));\n\n        clean_temporary_file();\n        return 0;\n      }\n      case InputDataFormat::AdmMetricNodal: {\n        [[fallthrough]];\n      }\n      case InputDataFormat::MetricNodal: {\n        const std::optional<AdmOptions> adm_options =\n            std::holds_alternative<AdmOptions>(input_data_format)\n                ? std::optional{std::get<AdmOptions>(input_data_format)}\n                : std::nullopt;\n        metric_nodal_to_bondi_modal(\n            input_worldtube_filename(), output_h5_file,\n            tuples::get<ReduceCceTags::BufferDepth>(inputs),\n            tuples::get<ReduceCceTags::ExtractionRadius>(inputs), adm_options);\n\n        clean_temporary_file();\n        return 0;\n      }\n      default:\n        ERROR(\"Unknown input data format \" << input_data_format_enum);\n    }\n  } catch (const std::exception& exception) {\n    Parallel::printf(\"%s\\n\", exception.what());\n    return 1;\n  }\n}\n"
  },
  {
    "path": "src/Executables/TimeStepperSummary/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE TimeStepperSummary)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  TimeStepperSummary.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Boost::boost\n  Boost::program_options\n  ErrorHandling\n  Printf\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "src/Executables/TimeStepperSummary/TimeStepperSummary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <algorithm>\n#include <array>\n#include <boost/program_options.hpp>\n#include <cstddef>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass ImexTimeStepper;\nclass LtsTimeStepper;\nnamespace TimeSteppers {\nclass AdamsBashforth;\ntemplate <bool Monotonic>\nclass AdamsMoultonPc;\n}  // namespace TimeSteppers\n\n// Charm looks for this function but since we build without a main function or\n// main module we just have it be empty\nextern \"C\" void CkRegisterMainModule(void) {}\n\nnamespace {\nusing time_steppers_taking_order =\n    tmpl::list<TimeSteppers::AdamsBashforth,\n               TimeSteppers::AdamsMoultonPc<false>,\n               TimeSteppers::AdamsMoultonPc<true>>;\n\nconst char* const program_help =\n    \"Print various properties about SpECTRE's time steppers.  Abbreviations\\n\"\n    \"used to reduce the output width are:\\n\"\n    \"  stab  = stable step size\\n\"\n    \"  subs  = substeps\\n\"\n    \"  esubs = substeps with error estimation enabled\\n\";\n\nconst std::array columns{\"name\"s,       \"order\"s, \"subs\"s,\n                         \"esubs\"s,      \"stab\"s,  \"stab/subs\"s,\n                         \"stab/esubs\"s, \"IMEX\"s,  \"LTS\"s};\nusing Row = std::tuple<std::string, size_t, size_t, size_t, double, double,\n                       double, bool, bool>;\n\ntemplate <typename Stepper>\nRow generate_row(const Stepper& stepper, std::string name) {\n  return {std::move(name),\n          get<TimeSteppers::Tags::FixedOrder>(stepper.order()),\n          stepper.number_of_substeps(),\n          stepper.number_of_substeps_for_error(),\n          stepper.stable_step(),\n          stepper.stable_step() / stepper.number_of_substeps(),\n          stepper.stable_step() / stepper.number_of_substeps_for_error(),\n          std::is_base_of_v<ImexTimeStepper, Stepper>,\n          std::is_base_of_v<LtsTimeStepper, Stepper>};\n}\n\nstd::vector<Row> generate_table() {\n  std::vector<Row> table{};\n  tmpl::for_each<TimeSteppers::time_steppers>([&](auto stepper_v) {\n    using Stepper = tmpl::type_from<decltype(stepper_v)>;\n    if constexpr (tmpl::list_contains_v<time_steppers_taking_order, Stepper>) {\n      for (size_t order = Stepper::minimum_order;\n           order <= Stepper::maximum_order;\n           ++order) {\n        table.push_back(generate_row(\n            Stepper(order), MakeString{} << pretty_type::name<Stepper>() << \"[\"\n                                         << order << \"]\"));\n      }\n    } else {\n      table.push_back(generate_row(Stepper{}, pretty_type::name<Stepper>()));\n    }\n  });\n  return table;\n}\n\nstd::string bool_yn(const bool& b) { return b ? \"Y\" : \"N\"; }\ntemplate <typename T>\nconst T& bool_yn(const T& t) {\n  return t;\n}\n\nvoid print_table(std::vector<Row> table, const size_t sort_index) {\n  tmpl::for_each<tmpl::range<size_t, 0, columns.size()>>(\n      [&](auto constexpr_sort_index_v) {\n        constexpr size_t constexpr_sort_index =\n            tmpl::type_from<decltype(constexpr_sort_index_v)>::value;\n        if (constexpr_sort_index == sort_index) {\n          std::stable_sort(table.begin(), table.end(),\n                           [&](const auto& a, const auto& b) {\n                             return get<constexpr_sort_index>(a) <\n                                    get<constexpr_sort_index>(b);\n                           });\n        }\n      });\n\n  std::array<size_t, columns.size()> column_widths{};\n  for (size_t i = 0; i < columns.size(); ++i) {\n    gsl::at(column_widths, i) = gsl::at(columns, i).size();\n  }\n  std::vector<std::array<std::string, columns.size()>> stringified_table;\n  stringified_table.reserve(table.size());\n\n  for (const auto& row : table) {\n    stringified_table.emplace_back();\n    tmpl::for_each<tmpl::range<size_t, 0, columns.size()>>([&](auto column_v) {\n      constexpr size_t column = tmpl::type_from<decltype(column_v)>::value;\n      std::string stringified =\n          MakeString{} << std::setprecision(3) << bool_yn(get<column>(row));\n      gsl::at(column_widths, column) =\n          std::max(gsl::at(column_widths, column), stringified.size());\n      gsl::at(stringified_table.back(), column) = std::move(stringified);\n    });\n  }\n\n  for (size_t i = 0; i < columns.size(); ++i) {\n    Parallel::printf(\"%-*s\", gsl::at(column_widths, i) + 1,\n                     gsl::at(columns, i));\n  }\n  Parallel::printf(\"\\n\");\n  {\n    const size_t num_chars =\n        alg::accumulate(column_widths, 0_st) + columns.size();\n    for (size_t i = 0; i < num_chars; ++i) {\n      Parallel::printf(\"-\");\n    }\n  }\n  Parallel::printf(\"\\n\");\n  for (const auto& row : stringified_table) {\n    for (size_t i = 0; i < columns.size(); ++i) {\n      Parallel::printf(\"%-*s\", gsl::at(column_widths, i) + 1, gsl::at(row, i));\n    }\n    Parallel::printf(\"\\n\");\n  }\n}\n}  // namespace\n\nint main(const int argc, char** const argv) {\n  namespace bpo = boost::program_options;\n  try {\n    bpo::options_description command_line_options;\n\n    const std::string sort_help =\n        MakeString{} << \"Sort column.  One of \" << columns;\n    // clang-format off\n    command_line_options.add_options()\n        (\"help,h\", \"Describe program options\")\n        (\"sort\", bpo::value<std::string>()->default_value(columns.front()),\n         sort_help.c_str())\n        ;\n    // clang-format on\n\n    bpo::command_line_parser command_line_parser(argc, argv);\n    command_line_parser.options(command_line_options);\n\n    bpo::variables_map parsed_command_line_options;\n    bpo::store(command_line_parser.run(), parsed_command_line_options);\n    bpo::notify(parsed_command_line_options);\n\n    if (parsed_command_line_options.count(\"help\") != 0) {\n      Parallel::printf(\"%s\\n%s\", command_line_options, program_help);\n      return 0;\n    }\n\n    const std::string sort_column =\n        parsed_command_line_options.at(\"sort\").as<std::string>();\n    const auto sort_index =\n        static_cast<size_t>(alg::find(columns, sort_column) - columns.begin());\n    if (sort_index == columns.size()) {\n      ERROR_NO_TRACE(\"Invalid sort column.  Must be \" << columns);\n    }\n\n    print_table(generate_table(), sort_index);\n  } catch (const bpo::error& e) {\n    ERROR_NO_TRACE(e.what());\n  }\n  return 0;\n}\n"
  },
  {
    "path": "src/Executables/WriteCceWorldtubeCoordsToFile/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(EXECUTABLE WriteCceWorldtubeCoordsToFile)\n\nadd_spectre_executable(\n  ${EXECUTABLE}\n  EXCLUDE_FROM_ALL\n  WriteCceWorldtubeCoordsToFile.cpp\n  )\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  Boost::boost\n  Boost::program_options\n  Printf\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  )\n\nif(BUILD_TESTING)\n  add_dependencies(test-executables ${EXECUTABLE})\nendif()\n"
  },
  {
    "path": "src/Executables/WriteCceWorldtubeCoordsToFile/WriteCceWorldtubeCoordsToFile.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n#include <boost/program_options.hpp>\n#include <boost/program_options/value_semantic.hpp>\n#include <cstddef>\n#include <exception>\n#include <string>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/StrahlkorperCoordsToTextFile.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n\n// Charm looks for this function but since we build without a main function or\n// main module we just have it be empty\nextern \"C\" void CkRegisterMainModule(void) {}\n\n/*\n * This executable is used for writing the collocation points of a CCE\n * Strahlkorper to a text file. This is useful for other NR codes so they can\n * write data to certain points and we do the necessary transformations.\n */\nint main(int argc, char** argv) {\n  boost::program_options::options_description desc(\n      \"Program for writing the collocation points (coordinates) of a worldtube \"\n      \"sphere that SpECTRE CCE is able to read and interpret to a \"\n      \"file.\\nDetails about the output file:\\n\"\n      \" * There are no header or comment lines\\n\"\n      \" * Each point is written to a new line of the output file\\n\"\n      \" * The delimiter for the x, y, z components of each point is a space\\n\"\n      \" * The points are written in scientific notation\\n\"\n      \" * The sphere is centered on the origin (0, 0, 0)\");\n  desc.add_options()(\"help,h\", \"show this help message\")(\n      \"radius,r\", boost::program_options::value<double>()->required(),\n      \"Radius of the worltube.\")(\n      \"lmax,L\", boost::program_options::value<size_t>()->required(),\n      \"The spherical harmonic L of the surface. The surface will have (L + 1) \"\n      \"* (2L + 1) total points\")(\n      \"output_file,o\", boost::program_options::value<std::string>()->required(),\n      \"Output filename for the points. No extension will be added.\")(\n      \"force,f\", boost::program_options::bool_switch(),\n      \"Overwrite the output file if it already exists\");\n\n  boost::program_options::variables_map vars;\n\n  boost::program_options::store(\n      boost::program_options::command_line_parser(argc, argv)\n          .options(desc)\n          .run(),\n      vars);\n\n  if (vars.contains(\"help\")) {\n    Parallel::printf(\"%s\\n\", desc);\n    return 0;\n  }\n\n  for (const auto& option : {\"radius\", \"lmax\", \"output_file\"}) {\n    if (not vars.contains(option)) {\n      Parallel::printf(\"Missing option: %s\\n\\n%s\", option, desc);\n      return 1;\n    }\n  }\n\n  const double radius = vars[\"radius\"].as<double>();\n  const size_t l_max = vars[\"lmax\"].as<size_t>();\n  const std::array<double, 3> center{0.0, 0.0, 0.0};\n  const std::string output_file = vars[\"output_file\"].as<std::string>();\n  const bool overwrite = vars[\"force\"].as<bool>();\n\n  try {\n    ylm::write_strahlkorper_coords_to_text_file(\n        radius, l_max, center, output_file, ylm::AngularOrdering::Cce,\n        overwrite);\n  } catch (const std::exception& exception) {\n    Parallel::printf(\"%s\\n\", exception.what());\n    return 1;\n  }\n}\n"
  },
  {
    "path": "src/IO/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY IO)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ComposeTable.cpp\n  Connectivity.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComposeTable.hpp\n  Connectivity.hpp\n  )\n\n# Notes:\n# - The H5 lib depends in IO for the connectivity. Keep that in mind when adding\n#   dependencies here.\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Utilities\n  )\n\nadd_subdirectory(Exporter)\nadd_subdirectory(External)\nadd_subdirectory(H5)\nadd_subdirectory(Logging)\n\n# These two libraries should move to ParallelAlgorithms\nadd_subdirectory(Importers)\nadd_subdirectory(Observer)\n"
  },
  {
    "path": "src/IO/ComposeTable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/ComposeTable.hpp\"\n\n#include <algorithm>\n#include <fstream>\n#include <limits>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace io {\n// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)\nComposeTable::ComposeTable(std::string directory_to_read_from)\n    : directory_to_read_from_(std::move(directory_to_read_from)) {\n  parse_eos_quantities();\n  parse_eos_parameters();\n  parse_eos_table();\n}\n\nvoid ComposeTable::parse_eos_quantities() {\n  std::string line_buffer{};\n  const std::string filename{directory_to_read_from_ + \"/eos.quantities\"};\n  if (not file_system::check_if_file_exists(filename)) {\n    ERROR(\"File '\" << filename << \"' does not exist.\");\n  }\n\n  std::ifstream quantities_file(filename);\n\n  std::getline(quantities_file, line_buffer);  // Read first comment line\n  if (line_buffer !=\n      \" # number of regular, additional and derivative quantities (see table \"\n      \"7.1)\") {\n    ERROR(\"Read unexpected comment line: '\" << line_buffer << \"'\");\n  }\n  size_t number_of_regular_quantities = 0;\n  size_t number_of_additional_quantities = 0;\n  size_t number_of_derivative_quantities = 0;\n  quantities_file >> number_of_regular_quantities >>\n      number_of_additional_quantities >> number_of_derivative_quantities;\n  std::getline(quantities_file, line_buffer);  // Read newline\n  std::getline(quantities_file, line_buffer);  // Read second comment line\n  if (line_buffer !=\n      \" # indices of regular, additional and derivative quantities\") {\n    ERROR(\"Read unexpected comment line: '\" << line_buffer << \"'\");\n  }\n  const size_t number_of_regular_and_additional_quantities =\n      number_of_regular_quantities + number_of_additional_quantities;\n  available_quantities_.reserve(number_of_regular_and_additional_quantities +\n                                number_of_derivative_quantities);\n  data_.reserve(number_of_regular_and_additional_quantities +\n                number_of_derivative_quantities);\n\n  for (size_t i = 0; i < number_of_regular_and_additional_quantities; ++i) {\n    size_t quantity = std::numeric_limits<size_t>::max();\n    quantities_file >> quantity;\n    if (quantity > compose_regular_and_additional_index_to_names_.size() or\n        quantity < 1) {\n      ERROR(\"Read in unknown quantity with number \"\n            << quantity << \" but only know [1, \"\n            << compose_regular_and_additional_index_to_names_.size() << \"]\");\n    }\n    available_quantities_.emplace_back(\n        compose_regular_and_additional_index_to_names_[quantity - 1]);\n  }\n  for (size_t i = 0; i < number_of_derivative_quantities; ++i) {\n    size_t quantity = std::numeric_limits<size_t>::max();\n    quantities_file >> quantity;\n    if (quantity > compose_derivative_index_to_names_.size() or quantity < 1) {\n      ERROR(\"Read in unknown quantity with number \"\n            << quantity << \" but only know [1, \"\n            << compose_derivative_index_to_names_.size() << \"]\");\n    }\n    available_quantities_.emplace_back(\n        compose_derivative_index_to_names_[quantity - 1]);\n  }\n  // Make sure we don't have any duplicate quantities\n  std::vector<std::string> quantities_to_check = available_quantities();\n  alg::sort(quantities_to_check);\n  if (const auto adjacent_it = std::adjacent_find(quantities_to_check.begin(),\n                                                  quantities_to_check.end());\n      adjacent_it != quantities_to_check.end()) {\n    ERROR(\"Found quantity '\"\n          << *adjacent_it\n          << \"' more than once. If you requested the free energy from both the \"\n             \"'regular and additional quantities' and the 'derivative \"\n             \"quantities' you can only use one.\");\n  }\n}\n\nvoid ComposeTable::parse_eos_parameters() {\n  std::string line_buffer{};\n  const std::string filename{directory_to_read_from_ + \"/eos.parameters\"};\n  if (not file_system::check_if_file_exists(filename)) {\n    ERROR(\"File '\" << filename << \"' does not exist.\");\n  }\n  std::ifstream parameters_file(filename);\n\n  std::getline(parameters_file, line_buffer);  // Read first comment line\n  if (line_buffer !=\n      \" # order of interpolation in first, second and third index\") {\n    ERROR(\"Read unexpected comment line: '\" << line_buffer << \"'\");\n  }\n  for (size_t i = 0; i < 3; ++i) {\n    parameters_file >> gsl::at(interpolation_order_, i);\n  }\n  std::getline(parameters_file, line_buffer);  // Read newline\n  std::getline(parameters_file, line_buffer);  // Read second comment line\n  if (line_buffer !=\n      \" # calculation of beta-equilibrium (1: yes, else: no) and for given \"\n      \"entropy (1: yes, else: no)\") {\n    ERROR(\"Read unexpected comment line: '\" << line_buffer << \"'\");\n  }\n  parameters_file >> beta_equilibrium_;\n  std::getline(parameters_file, line_buffer);  // Read rest of line\n  std::getline(parameters_file, line_buffer);  // Read third comment line\n  if (line_buffer !=\n      \" # tabulation scheme (0 = explicit listing, 1 = loops, see manual)\") {\n    ERROR(\"Read unexpected comment line: '\" << line_buffer << \"'\");\n  }\n  std::getline(parameters_file, line_buffer);  // Read tabulation scheme\n  std::getline(parameters_file, line_buffer);  // Read fourth comment line\n  if (line_buffer !=\n      \" # parameter values (first, second and third index) depending on \"\n      \"tabulation scheme\") {\n    ERROR(\"Read unexpected comment line: '\" << line_buffer << \"'\");\n  }\n  parameters_file >> temperature_bounds_[0] >> number_density_bounds_[0] >>\n      electron_fraction_bounds_[0];\n  std::getline(parameters_file, line_buffer);  // Read rest of line (newline)\n  parameters_file >> temperature_bounds_[1] >> number_density_bounds_[1] >>\n      electron_fraction_bounds_[1];\n  std::getline(parameters_file, line_buffer);  // Read rest of line (newline)\n  parameters_file >> temperature_number_of_points_ >>\n      number_density_number_of_points_ >> electron_fraction_number_of_points_;\n  table_size_ = number_density_number_of_points_ *\n                temperature_number_of_points_ *\n                electron_fraction_number_of_points_;\n  std::getline(parameters_file, line_buffer);  // Read rest of line (newline)\n  parameters_file >> temperature_log_spacing_ >> number_density_log_spacing_ >>\n      electron_fraction_log_spacing_;\n}\n\nvoid ComposeTable::parse_eos_table() {\n  for (const auto& quantity_name : available_quantities()) {\n    data_[quantity_name] = DataVector{table_size_};\n  }\n\n  std::string line_buffer{};\n  const std::string filename{directory_to_read_from_ + \"/eos.table\"};\n  if (not file_system::check_if_file_exists(filename)) {\n    ERROR(\"File '\" << filename << \"' does not exist.\");\n  }\n  std::ifstream table_file(filename);\n\n  for (size_t i = 0; i < table_size_; ++i) {\n    // Read in number density, temperature, and electron fraction\n    double dummy = std::numeric_limits<double>::signaling_NaN();\n    table_file >> dummy >> dummy >> dummy;\n    for (const auto& quantity_name : available_quantities()) {\n      table_file >> data_[quantity_name][i];\n    }\n  }\n}\n\nvoid ComposeTable::pup(PUP::er& p) {\n  p | directory_to_read_from_;\n  p | available_quantities_;\n  p | interpolation_order_;\n  p | number_density_bounds_;\n  p | temperature_bounds_;\n  p | electron_fraction_bounds_;\n  p | number_density_number_of_points_;\n  p | temperature_number_of_points_;\n  p | electron_fraction_number_of_points_;\n  p | table_size_;\n  p | beta_equilibrium_;\n  p | number_density_log_spacing_;\n  p | temperature_log_spacing_;\n  p | electron_fraction_log_spacing_;\n  p | data_;\n}\n\nconst std::vector<std::string>\n    ComposeTable::compose_regular_and_additional_index_to_names_{\n        \"pressure\",\n        \"specific entropy\",\n        \"baryon chemical potential\",\n        \"charge chemical potential\",\n        \"lepton chemical potential\",\n        \"specific free energy\",\n        \"specific internal energy\",\n        \"specific enthalpy\",\n        \"specific free enthalpy\",\n        \"dp_drho\",\n        \"dp_depsilon\",\n        \"sound speed squared\",\n        \"specific heat at constant volume\",\n        \"specific heat at constant pressure\",\n        \"adiabatic index\",\n        \"expansion coefficient at constant pressure\",\n        \"tension coefficient at constant volume\",\n        \"isothermal compressibility\",\n        \"adiabatic compressibility\",\n        \"free energy\",\n        \"internal energy\",\n        \"enthalpy\",\n        \"free enthalpy\",\n        \"energy density\"};\n\nconst std::vector<std::string> ComposeTable::compose_derivative_index_to_names_{\n    \"free energy\",      \"d F / d T\",    \"d2 F / d T2\",   \"d2 F / d T d n_b\",\n    \"d2 F / d T d Y_e\", \"d F / d n_b\",  \"d2 F / d n_b2\", \"d2 F / d n_b d Y_e\",\n    \"d F / d Y_e\",      \"d2 F / d Y_e2\"};\n}  // namespace io\n"
  },
  {
    "path": "src/IO/ComposeTable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace io {\n/*!\n * \\brief Reader for the CompOSE (https://compose.obspm.fr/home) ASCII tabulated\n * equations of state.\n *\n * The directory from which the data is read must contain the `eos.quantities`,\n * `eos.parameters`, and `eos.table`.\n */\nclass ComposeTable {\n public:\n  ComposeTable() = default;\n  explicit ComposeTable(std::string directory_to_read_from);\n\n  const std::unordered_map<std::string, DataVector>& data() const {\n    return data_;\n  }\n\n  const DataVector& data(const std::string& quantity_name) const {\n    return data_.at(quantity_name);\n  }\n\n  const std::vector<std::string>& available_quantities() const {\n    return available_quantities_;\n  }\n\n  const std::array<double, 2>& number_density_bounds() const {\n    return number_density_bounds_;\n  }\n\n  const std::array<double, 2>& temperature_bounds() const {\n    return temperature_bounds_;\n  }\n\n  const std::array<double, 2>& electron_fraction_bounds() const {\n    return electron_fraction_bounds_;\n  }\n\n  size_t number_density_number_of_points() const {\n    return number_density_number_of_points_;\n  }\n\n  size_t temperature_number_of_points() const {\n    return temperature_number_of_points_;\n  }\n\n  size_t electron_fraction_number_of_points() const {\n    return electron_fraction_number_of_points_;\n  }\n\n  bool beta_equilibrium() const { return beta_equilibrium_; }\n\n  bool number_density_log_spacing() const {\n    return number_density_log_spacing_;\n  }\n\n  bool temperature_log_spacing() const { return temperature_log_spacing_; }\n\n  bool electron_fraction_log_spacing() const {\n    return electron_fraction_log_spacing_;\n  }\n\n  void pup(PUP::er& p);\n\n private:\n  void parse_eos_quantities();\n  void parse_eos_parameters();\n  void parse_eos_table();\n\n  static const std::vector<std::string>\n      compose_regular_and_additional_index_to_names_;\n  static const std::vector<std::string> compose_derivative_index_to_names_;\n  std::string directory_to_read_from_;\n  std::vector<std::string> available_quantities_;\n  std::array<size_t, 3> interpolation_order_;\n  std::array<double, 2> number_density_bounds_;\n  std::array<double, 2> temperature_bounds_;\n  std::array<double, 2> electron_fraction_bounds_;\n  size_t number_density_number_of_points_;\n  size_t temperature_number_of_points_;\n  size_t electron_fraction_number_of_points_;\n  size_t table_size_;\n  bool beta_equilibrium_;\n  bool number_density_log_spacing_;\n  bool temperature_log_spacing_;\n  bool electron_fraction_log_spacing_;\n  std::unordered_map<std::string, DataVector> data_;\n};\n}  // namespace io\n"
  },
  {
    "path": "src/IO/Connectivity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Connectivity.hpp\"\n\n#include <algorithm>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace vis::detail {\nstd::ostream& operator<<(std::ostream& os, const Topology& topology) {\n  switch (topology) {\n    case Topology::Line:\n      return os << \"Line\";\n    case Topology::Triangle:\n      return os << \"Triangle\";\n    case Topology::Quad:\n      return os << \"Quad\";\n    case Topology::Wedge:\n      return os << \"Wedge\";\n    case Topology::Hexahedron:\n      return os << \"Hexahedron\";\n    // LCOV_EXCL_START\n    default:\n      ERROR(\"Unknown value of Topology trying to be streamed\");\n      // LCOV_EXCL_STOP\n  }\n}\n\nnamespace {\nstd::vector<CellInTopology> cells_in_i1(const size_t number_of_points_in_i1) {\n  // The number of cells in an I1 with N collocation points is N-1. Each cell\n  // goes from the ith to the i+1th collocation point. This is stored in a\n  // vector.\n  std::vector<CellInTopology> result;\n  result.reserve(number_of_points_in_i1 - 1);\n  for (size_t i = 0; i < number_of_points_in_i1 - 1; ++i) {\n    result.emplace_back(Topology::Line, std::vector<size_t>{i, i + 1});\n  }\n  return result;\n}\n\nstd::vector<CellInTopology> tensor_product_cells(\n    const std::vector<CellInTopology>& first_topology_cells,\n    const size_t first_topology_cells_size,\n    const std::vector<CellInTopology>& second_topology_cells) {\n  // first_topology_cells_size is the number of cells plus one in the topology.\n  // For an I1 topology it is the number of grid points. Passing it in\n  // separately allows easier generalization to other topologies such as S2.\n  std::vector<CellInTopology> result;\n  for (const auto& first_cells : first_topology_cells) {\n    for (const auto& second_cells : second_topology_cells) {\n      const std::vector<size_t>& first_bounding_indices =\n          first_cells.bounding_indices;\n      const std::vector<size_t> second_bounding_indices =\n          [first_topology_cells_size](\n              const std::vector<size_t>& bounding_indices) {\n            std::vector<size_t> bounding_computed(bounding_indices.size());\n            std::transform(bounding_indices.begin(), bounding_indices.end(),\n                           bounding_computed.begin(),\n                           [first_topology_cells_size](const size_t element) {\n                             return element * first_topology_cells_size;\n                           });\n            return bounding_computed;\n          }(second_cells.bounding_indices);\n\n      if (first_cells.topology == Topology::Line and\n          second_cells.topology == Topology::Line) {\n        result.emplace_back(\n            Topology::Quad,\n            std::vector<size_t>{\n                first_bounding_indices[0] + second_bounding_indices[0],\n                first_bounding_indices[1] + second_bounding_indices[0],\n                first_bounding_indices[1] + second_bounding_indices[1],\n                first_bounding_indices[0] + second_bounding_indices[1]});\n      } else if (first_cells.topology == Topology::Line and\n                 second_cells.topology == Topology::Quad) {\n        result.emplace_back(\n            Topology::Hexahedron,\n            std::vector<size_t>{\n                first_bounding_indices[0] + second_bounding_indices[0],\n                first_bounding_indices[1] + second_bounding_indices[0],\n                first_bounding_indices[1] + second_bounding_indices[1],\n                first_bounding_indices[0] + second_bounding_indices[1],\n                first_bounding_indices[0] + second_bounding_indices[3],\n                first_bounding_indices[1] + second_bounding_indices[3],\n                first_bounding_indices[1] + second_bounding_indices[2],\n                first_bounding_indices[0] + second_bounding_indices[2]});\n      } else {\n        // LCOV_EXCL_START\n        ERROR(\n            \"Tensor product of cells that are unknown. The first topology is: \"\n            << first_cells.topology\n            << \" and the second is: \" << second_cells.topology);\n        // LCOV_EXCL_STOP\n      }\n    }\n  }\n  return result;\n}\n}  // namespace\n\ntemplate <size_t Dim>\nstd::vector<CellInTopology> compute_cells(const Index<Dim>& extents) {\n  std::vector<CellInTopology> cells;\n  std::vector<std::vector<CellInTopology>> cells_per_topology;\n  std::vector<size_t> size_per_topology;\n  // Compute the number of cells in each topology and the number of extents\n  // The extents are copied since this will need generalization for other\n  // topologies.\n  for (size_t i = 0; i < Dim; ++i) {\n    cells_per_topology.push_back(cells_in_i1(extents[i]));\n    size_per_topology.push_back(extents[i]);\n  }\n  cells = cells_per_topology.back();\n  // Compute the tensor product of the number of cells in each topology to get\n  // the cell structure of the full domain, ie 3D cell structure.\n  for (size_t i = cells_per_topology.size() - 1; i > 0; --i) {\n    cells = tensor_product_cells(cells_per_topology[i - 1],\n                                 size_per_topology[i - 1], cells);\n  }\n  return cells;\n}\n\nstd::vector<CellInTopology> compute_cells(const std::vector<size_t>& extents) {\n  if (extents.size() == 1) {\n    return compute_cells(Index<1>{extents[0]});\n  } else if (extents.size() == 2) {\n    return compute_cells(Index<2>{extents[0], extents[1]});\n  } else if (extents.size() == 3) {\n    return compute_cells(Index<3>{extents[0], extents[1], extents[2]});\n  }\n  ERROR(\n      \"Only know how to compute connectivity for extents of size 1, 2, and 3, \"\n      \"not \"\n      << extents.size());\n}\n\nint xdmf_topology_type(const Topology topology) {\n  return static_cast<int>(topology);\n}\n\n// Explicit instantiations\ntemplate std::vector<CellInTopology> compute_cells<1>(const Index<1>& extents);\ntemplate std::vector<CellInTopology> compute_cells<2>(const Index<2>& extents);\ntemplate std::vector<CellInTopology> compute_cells<3>(const Index<3>& extents);\n}  // namespace vis::detail\n\n"
  },
  {
    "path": "src/IO/Connectivity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions for computing the connectivity of an element\n\n#pragma once\n\n#include <cstddef>\n#include <ostream>\n#include <utility>\n#include <vector>\n\ntemplate <size_t Dim>\nclass Index;\n\n/// Holds functions needed for visualizing data\nnamespace vis {\nnamespace detail {\n/*!\n * \\brief The set of cell topologies used for visualization\n *\n * Tensor-product topologies (Line, Quad, Hexahedron) can be generated by\n * `compute_cells()`. Non-tensor-product topologies (Triangle, Wedge) are\n * necessary for filled cylinders, shells, and balls.\n *\n * The numerical values correspond to the XDMF topology types.\n */\nenum class Topology : int {\n  Line = 2,\n  Triangle = 4,\n  Quad = 5,\n  Wedge = 8,\n  Hexahedron = 9\n};\n\nstd::ostream& operator<<(std::ostream& os, const Topology& topology);\n\n/*!\n * \\brief Represents the number of cells in a particular topology\n *\n * Each `CellInTopology` holds an enum of type `Topology` whose\n * value denotes the type of the topology, e.g. line, quad or hexahedron, and a\n * vector of bounding indices which are the indices of the grid coordinates in\n * the contiguous arrays of x, y, and z coordinates that bound the cell.\n */\nstruct CellInTopology {\n  // cppcheck-suppress passedByValue\n  CellInTopology(const Topology& top, std::vector<size_t> bounding_ind)\n      : topology(top), bounding_indices(std::move(bounding_ind)) {}\n  CellInTopology() = default;\n  CellInTopology(const CellInTopology& /*rhs*/) = default;\n  CellInTopology(CellInTopology&& /*rhs*/) = default;\n  CellInTopology& operator=(const CellInTopology& /*rhs*/) = default;\n  CellInTopology& operator=(CellInTopology&& /*rhs*/) = default;\n  ~CellInTopology() = default;\n  Topology topology{Topology::Line};\n  std::vector<size_t> bounding_indices{};\n};\n\n/// @{\n/*!\n * \\brief Compute the cells in the element.\n *\n * Returns a vector of the cells in the topology I1^Dim, i.e. a line if Dim ==\n * 1, or a hexahedron if Dim == 3. The cells are bounded by lines connecting\n * grid points along the axes of the element, so if you have (n_x by n_y by n_z)\n * grid points, you have ((n_x-1) by (n_y-1) by (n_z-1)) cells.\n *\n * \\note As more topologies are added, e.g. S2, the interface will need slight\n * modification, however the return type is likely to be able to remain the\n * same.\n */\ntemplate <size_t Dim>\nstd::vector<CellInTopology> compute_cells(const Index<Dim>& extents);\n\nstd::vector<CellInTopology> compute_cells(const std::vector<size_t>& extents);\n/// @}\n\n/*!\n * \\brief Return the XDMF 3.0 integer type tag for a given topology.\n *\n * The mapping is: Line=2, Triangle=4, Quad=5, Wedge=8, Hexahedron=9.\n * Used when writing mixed-topology connectivity arrays to HDF5 files.\n */\nint xdmf_topology_type(Topology topology);\n\n}  // namespace detail\n}  // namespace vis\n"
  },
  {
    "path": "src/IO/Exporter/BundledExporter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file defines some symbols for compatibility with InfoAtLink and Charm++\n// when the BundledExporter library is linked into an external program.\n\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/Formaline.hpp\"\n\nstd::string link_date() { return \"Unavailable\"; }\n\nstd::string executable_name() { return \"Unavailable\"; }\n\nstd::string git_description() { return \"Unavailable\"; }\n\nstd::string git_branch() { return \"Unavailable\"; }\n\nnamespace formaline {\nstd::vector<char> get_archive() {\n  return {'N', 'o', 't', ' ', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd'};\n}\n\nstd::string get_environment_variables() { return \"Unavailable\"; }\n\nstd::string get_build_info() { return \"Unavailable\"; }\n\nstd::string get_paths() { return \"Unavailable.\"; }\n}  // namespace formaline\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmissing-declarations\"\nextern \"C\" void CkRegisterMainModule() {}\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "src/IO/Exporter/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Exporter)\n\n# Use 'add_library' rather than 'add_spectre_library' for multiple reasons:\n# - Don't build with PCH, because otherwise we'd have to depend the PCH on\n#   OpenMP (would probably be fine, but then _everything_ would link with OpenMP\n#   and it's probably better to avoid that).\n# - Don't try to stub the object files because the BundledExporter library\n#   (defined below) uses them.\nadd_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Exporter.cpp\n  PointwiseInterpolator.cpp\n  SelectObservation.cpp\n  SpacetimeInterpolator.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Exporter.hpp\n  PointwiseInterpolator.hpp\n  SelectObservation.hpp\n  SpacetimeInterpolator.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainCreators\n  FunctionsOfTime\n  H5\n  Interpolation\n  Serialization\n  Utilities\n  )\n\n# Link OpenMP if available\nif(TARGET OpenMP::OpenMP_CXX)\n  target_link_libraries(${LIBRARY} PRIVATE OpenMP::OpenMP_CXX)\nendif()\n\nadd_subdirectory(Python)\n\n# Create a library that can be linked into an external program. Notes:\n# - This is still experimental. The bundled library works (see\n#   Test_BundledExporter.cpp), but building the library on one system and then\n#   distributing it to another (i.e. making the library relocatable) will likely\n#   require some more work. Linking in external dependencies statically would\n#   probably help, so they don't have to be installed on the target system (e.g.\n#   HDF5, yaml-cpp, etc).\n# - A shared library seems to work better than a static library, since it\n#   includes symbols from linked static libs.\n# - The `TARGET_OBJECTS` generator expression links in the objects from the\n#   Exporter library, because otherwise they wouldn't be used and therefore\n#   wouldn't be linked in.\n#   Note that `TARGET_OBJECTS` also includes precompiled headers. LLVM's lld\n#   is smart enough to ignore them, but GNU's ld isn't, so we have to filter\n#   them out (see https://gitlab.kitware.com/cmake/cmake/-/issues/22832).\n# - The `EXPORTER_LINKED_LIBS` are needed to resolve the symbols for the\n#   `TARGET_OBJECTS`, just like in the Exporter library (of which this library\n#   is basically an extended copy).\n# - The `BundledExporter.cpp` file and the `CharmModuleInit` object library\n#   define some symbols for compatility with Charm++ and InfoAtLink.\nset(BUNDLED_EXPORTER_LIB BundledExporter)\nadd_library(${BUNDLED_EXPORTER_LIB} SHARED\n  BundledExporter.cpp)\nget_target_property(EXPORTER_LINKED_LIBS ${LIBRARY} LINK_LIBRARIES)\ntarget_link_libraries(\n  ${BUNDLED_EXPORTER_LIB}\n  PRIVATE\n  $<FILTER:$<TARGET_OBJECTS:${LIBRARY}>,EXCLUDE,.*\\\\.gch>\n  ${EXPORTER_LINKED_LIBS}\n  Informer\n  CharmModuleInit\n  Charmxx::charmxx\n  )\nadd_dependencies(${BUNDLED_EXPORTER_LIB} ${LIBRARY})\n# Also export the public header so that external programs can include it\nconfigure_file(\n  Exporter.hpp\n  ${CMAKE_BINARY_DIR}/include/spectre/Exporter.hpp\n  COPYONLY)\n"
  },
  {
    "path": "src/IO/Exporter/Exporter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Exporter/Exporter.hpp\"\n\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace spectre::Exporter {\n\ntemplate <size_t Dim>\nstd::vector<std::vector<double>> interpolate_to_points(\n    const std::variant<std::vector<std::string>, std::string>&\n        volume_files_or_glob,\n    const std::string& subfile_name, const ObservationVariant& observation,\n    const std::vector<std::string>& tensor_components,\n    const std::array<std::vector<double>, Dim>& target_points,\n    const bool extrapolate_into_excisions, const bool error_on_missing_points,\n    const std::optional<size_t> num_threads) {\n  tnsr::I<DataVector, Dim, Frame::Inertial> target_points_dv{};\n  for (size_t d = 0; d < Dim; ++d) {\n    target_points_dv.get(d).set_data_ref(\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        const_cast<double*>(gsl::at(target_points, d).data()),\n        gsl::at(target_points, d).size());\n  }\n  std::vector<std::vector<double>> result{};\n  interpolate_to_points(make_not_null(&result), volume_files_or_glob,\n                        subfile_name, observation, tensor_components,\n                        target_points_dv, extrapolate_into_excisions,\n                        error_on_missing_points, num_threads);\n  return result;\n}\n\n// Generate instantiations\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template std::vector<std::vector<double>> interpolate_to_points<DIM(data)>( \\\n      const std::variant<std::vector<std::string>, std::string>&              \\\n          volume_files_or_glob,                                               \\\n      const std::string& subfile_name, const ObservationVariant& observation, \\\n      const std::vector<std::string>& tensor_components,                      \\\n      const std::array<std::vector<double>, DIM(data)>& target_points,        \\\n      bool extrapolate_into_excisions, bool error_on_missing_points,          \\\n      std::optional<size_t> num_threads);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "src/IO/Exporter/Exporter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares functions for interpolating data in volume files to target points.\n/// This file is intended to be included in external programs, so it\n/// intentionally has no dependencies on any other headers.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <variant>\n#include <vector>\n\n/// Functions that are intended to be used by external programs, e.g. to\n/// interpolate data in volume files to target points.\nnamespace spectre::Exporter {\n\n/// Identifies an observation by its ID in the volume data file.\nstruct ObservationId {\n  ObservationId() = default;\n  explicit ObservationId(size_t local_value) : value(local_value) {}\n  size_t value;\n};\n\n/// Identifies an observation by its index in the ordered list of observations.\n/// Negative indices are counted from the end of the list.\nstruct ObservationStep {\n  ObservationStep() = default;\n  explicit ObservationStep(int local_value) : value(local_value) {}\n  int value;\n};\n\nusing ObservationVariant = std::variant<ObservationId, ObservationStep, double>;\n\n/*!\n * \\brief Interpolate data in volume files to target points\n *\n * \\tparam Dim Dimension of the domain\n * \\param volume_files_or_glob The list of H5 files, or a glob pattern\n * \\param subfile_name The name of the subfile in the H5 files containing the\n * volume data\n * \\param observation Either the observation ID as a `size_t`, or the index of\n * the observation in the volume files to interpolate as an `int` (a value of 0\n * would be the first observation, and a value of -1 would be the last\n * observation).\n * \\param tensor_components The tensor components to interpolate, e.g.\n * \"Lapse\", \"Shift_x\", \"Shift_y\", \"Shift_z\", \"SpatialMetric_xx\", etc.\n * Look into the H5 file to see what components are available.\n * \\param target_points The points to interpolate to, in inertial coordinates.\n * \\param extrapolate_into_excisions Enables extrapolation into excision regions\n * of the domain (default is `false`). This can be useful to fill the excision\n * region with (constraint-violating but smooth) data so it can be imported into\n * moving puncture codes. Specifically, we implement the strategy used in\n * \\cite Etienne2008re adjusted for distorted excisions: we choose uniformly\n * spaced radial anchor points spaced as $\\Delta r = 0.3 r_\\mathrm{AH}$ in the\n * grid frame (where the excision is spherical), then map the anchor points to\n * the distorted frame (where we have the target point) and do a 7th order\n * polynomial extrapolation into the excision region.\n * \\param error_on_missing_points If `true`, an error will be thrown if any of\n * the target points are outside the domain. If `false`, the result will be\n * filled with NaNs for points outside the domain (default is `false`).\n * \\param num_threads The number of threads to use if OpenMP is linked in. If\n * not specified, OpenMP will determine the number of threads automatically.\n * It's also possible to set the number of threads using the environment\n * variable OMP_NUM_THREADS. It's an error to specify num_threads if OpenMP is\n * not linked in. Set num_threads to 1 to disable OpenMP.\n * \\return std::vector<std::vector<double>> The interpolated data. The first\n * dimension corresponds to the selected tensor components, and the second\n * dimension corresponds to the target points.\n */\ntemplate <size_t Dim>\nstd::vector<std::vector<double>> interpolate_to_points(\n    const std::variant<std::vector<std::string>, std::string>&\n        volume_files_or_glob,\n    const std::string& subfile_name, const ObservationVariant& observation,\n    const std::vector<std::string>& tensor_components,\n    const std::array<std::vector<double>, Dim>& target_points,\n    bool extrapolate_into_excisions = false,\n    bool error_on_missing_points = false,\n    std::optional<size_t> num_threads = std::nullopt);\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "src/IO/Exporter/PointwiseInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n\n#include <csignal>  // For Blaze error handling without PCH\n#ifdef _OPENMP\n#include <omp.h>\n#endif  // _OPENMP\n\n#include \"DataStructures/Tensor/EagerMath/CartesianToSpherical.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"IO/Exporter/SelectObservation.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Interpolation/PolynomialInterpolation.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\n// Ignore OpenMP pragmas when OpenMP is not enabled\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n\nnamespace spectre::Exporter {\n\nnamespace {\n\nsize_t resolve_num_threads(const std::optional<size_t> num_threads) {\n  // Resolve number of threads to use in OpenMP parallelization\n#ifdef _OPENMP\n  return num_threads.value_or(omp_get_max_threads());\n#else\n  if (num_threads.has_value()) {\n    ERROR_NO_TRACE(\n        \"OpenMP is not available, so num_threads cannot be specified.\");\n  }\n  return 1;\n#endif  // _OPENMP\n}\n\ntemplate <size_t Dim>\nauto load_grids(const h5::VolumeData& volfile, const size_t obs_id) {\n  const auto grid_names = volfile.get_grid_names(obs_id);\n  const auto all_extents = volfile.get_extents(obs_id);\n  const auto all_bases = volfile.get_bases(obs_id);\n  const auto all_quadratures = volfile.get_quadratures(obs_id);\n  // Reconstruct element IDs & meshes in the volume data file.\n  // This can be simplified by using ElementId and Mesh in the VolumeData class.\n  std::vector<ElementId<Dim>> element_ids{};\n  std::unordered_map<ElementId<Dim>, std::tuple<Mesh<Dim>, size_t, size_t>>\n      meshes{};\n  element_ids.reserve(grid_names.size());\n  for (const auto& grid_name : grid_names) {\n    const ElementId<Dim> element_id(grid_name);\n    element_ids.push_back(element_id);\n    get<0>(meshes[element_id]) = h5::mesh_for_grid<Dim>(\n        grid_name, grid_names, all_extents, all_bases, all_quadratures);\n    const auto [offset, length] = h5::offset_and_length_for_grid(\n        get_output(element_id), grid_names, all_extents);\n    get<1>(meshes[element_id]) = offset;\n    get<2>(meshes[element_id]) = length;\n  }\n  return std::make_pair(std::move(element_ids), std::move(meshes));\n}\n\nstd::vector<std::variant<DataVector, std::vector<float>>> load_tensor_data(\n    const h5::VolumeData& volfile, const size_t obs_id,\n    const std::vector<std::string>& tensor_components) {\n  std::vector<std::variant<DataVector, std::vector<float>>> tensor_data{};\n  tensor_data.reserve(tensor_components.size());\n  for (const auto& tensor_component : tensor_components) {\n    tensor_data.push_back(\n        volfile.get_tensor_component(obs_id, tensor_component).data);\n  }\n  return tensor_data;\n}\n\ntemplate <typename ResultDataType, size_t Dim>\nvoid interpolate_to_points_impl(\n    const gsl::not_null<std::vector<ResultDataType>*> result,\n    const gsl::not_null<std::vector<bool>*> filled_data,\n    const std::unordered_map<ElementId<Dim>, ElementLogicalCoordHolder<Dim>>&\n        element_logical_coords,\n    const std::vector<ElementId<Dim>>& element_ids,\n    const std::unordered_map<ElementId<Dim>,\n                             std::tuple<Mesh<Dim>, size_t, size_t>>& meshes,\n    const std::vector<std::variant<DataVector, std::vector<float>>>&\n        tensor_data,\n    [[maybe_unused]] const size_t resolved_num_threads) {\n#pragma omp parallel num_threads(resolved_num_threads)\n  {\n    DataVector interpolated_data_double{};\n    std::vector<float> interpolated_data_float{};\n#pragma omp for\n    for (const auto& element_id : element_ids) {\n      const auto found_points = element_logical_coords.find(element_id);\n      if (found_points == element_logical_coords.end()) {\n        continue;\n      }\n      const auto& points = found_points->second;\n      const auto& [mesh, offset, length] = meshes.at(element_id);\n      // Interpolate!\n      // Possible optimization: rather than interpolating each tensor component\n      // separately, we could interpolate all components at once. This would\n      // need an offset and stride to be passed to the interpolator, since the\n      // tensor components for all elements are stored contiguously.\n      const intrp::Irregular<Dim> interpolant(mesh,\n                                              points.element_logical_coords);\n      const size_t num_element_target_points =\n          points.element_logical_coords.begin()->size();\n      for (size_t i = 0; i < tensor_data.size(); ++i) {\n        const auto& component = tensor_data[i];\n        if (std::holds_alternative<DataVector>(component)) {\n          if (interpolated_data_double.size() < num_element_target_points) {\n            interpolated_data_double.destructive_resize(\n                num_element_target_points);\n          }\n          auto output_data = gsl::make_span(interpolated_data_double.data(),\n                                            num_element_target_points);\n          const auto input_data = gsl::make_span(\n              std::get<DataVector>(component).data() + offset, length);\n          interpolant.interpolate(make_not_null(&output_data), input_data);\n          for (size_t j = 0; j < num_element_target_points; ++j) {\n            (*result)[i][points.offsets[j]] = interpolated_data_double[j];\n            (*filled_data)[points.offsets[j]] = true;\n          }\n        } else {\n          interpolated_data_float.resize(num_element_target_points);\n          auto output_data = gsl::make_span(interpolated_data_float.data(),\n                                            num_element_target_points);\n          const auto input_data = gsl::make_span(\n              // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n              std::get<std::vector<float>>(component).data() + offset, length);\n          interpolant.interpolate(make_not_null(&output_data), input_data);\n          for (size_t j = 0; j < num_element_target_points; ++j) {\n            (*result)[i][points.offsets[j]] = interpolated_data_float[j];\n            (*filled_data)[points.offsets[j]] = true;\n          }\n        }\n      }\n    }  // omp for\n  }  // omp parallel\n}\n\ntemplate <size_t Dim>\nvoid interpolate_to_point_impl(\n    const gsl::not_null<std::vector<double>*> result,\n    const tnsr::I<double, Dim, Frame::ElementLogical>& x_element_logical,\n    const Mesh<Dim>& mesh, const size_t offset, const size_t length,\n    const std::vector<std::variant<DataVector, std::vector<float>>>&\n        tensor_data) {\n  const intrp::Irregular<Dim> interpolant(mesh, x_element_logical);\n  const size_t num_components = tensor_data.size();\n  result->resize(num_components);\n  for (size_t i = 0; i < num_components; ++i) {\n    const auto& component = tensor_data[i];\n    if (std::holds_alternative<DataVector>(component)) {\n      const auto input_data = gsl::make_span(\n          std::get<DataVector>(component).data() + offset, length);\n      auto output_data = gsl::make_span(&(*result)[i], 1);\n      interpolant.interpolate(make_not_null(&output_data), input_data);\n    } else {\n      const auto input_data = gsl::make_span(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n          std::get<std::vector<float>>(component).data() + offset, length);\n      float output_value = std::numeric_limits<float>::signaling_NaN();\n      auto output_data = gsl::make_span(&output_value, 1);\n      interpolant.interpolate(make_not_null(&output_data), input_data);\n      (*result)[i] = output_value;\n    }\n  }\n}\n\n// Data structure for extrapolation of tensor components into excisions\ntemplate <size_t NumExtrapolationAnchors>\nstruct ExtrapolationInfo {\n  // Index of the target point in the result array (y target)\n  size_t target_index;\n  // Index of the first anchor point in the source array (y data)\n  size_t source_index;\n  // Coordinate of the target point (x target)\n  double target_point;\n  // Coordinates of the anchor points (x data)\n  std::array<double, NumExtrapolationAnchors> anchors;\n};\n\n// Construct anchor points for extrapolation into excisions.\n//\n// Anchor points are placed radially in the grid frame around the excision\n// sphere, which is spherical in the grid frame. Then the anchor points are\n// added to the `block_logical_coords` so data is interpolated to them. Also an\n// entry is added to `extrapolation_info` so the interpolated data on the anchor\n// points can be extrapolated to the target point in the excision.\n//\n// This function returns `true` if the target point is inside the excision and\n// the anchor points were added. Otherwise, returns `false` and the next\n// excision should be tried.\n//\n// Alternatives and notes:\n// - We could extrapolate directly from the nearest element, using the Lagrange\n//   polynomial basis that the element already has. However, this is unstable\n//   when the element is too small (possibly h-refined) and/or has log spacing.\n//   In those cases the logical coordinate that we're extrapolating to can be\n//   many logical element sizes away. Also, this relies on inverting the\n//   neighboring block's map outside the block, which is not guaranteed to work.\n// - We could construct anchor points in different frames, e.g. in the inertial\n//   frame. These choices probably don't make a big difference.\n// - We could do more performance optimizations for the case where the excision\n//   sphere is spherical. E.g. in that case the radial anchor points for all\n//   points are the same.\ntemplate <size_t Dim, size_t NumExtrapolationAnchors>\nbool add_extrapolation_anchors(\n    const gsl::not_null<std::vector<BlockLogicalCoords<Dim>>*>\n        block_logical_coords,\n    const gsl::not_null<\n        std::vector<ExtrapolationInfo<NumExtrapolationAnchors>>*>\n        extrapolation_info,\n    const ExcisionSphere<Dim>& excision_sphere, const Domain<Dim>& domain,\n    const tnsr::I<double, Dim, Frame::Inertial>& target_point,\n    const double time, const domain::FunctionsOfTimeMap& functions_of_time,\n    const double extrapolation_spacing) {\n  // Get spherical coordinates around the excision in distorted frame.\n  // Note that the excision sphere doesn't have a distorted map, but its\n  // grid-to-inertial map is just the neighboring block's\n  // distorted-to-inertial map, so we can use either of those.\n  auto x_distorted =\n      [&excision_sphere, &target_point, &time,\n       &functions_of_time]() -> tnsr::I<double, Dim, Frame::Distorted> {\n    if (excision_sphere.is_time_dependent()) {\n      const auto x_grid =\n          excision_sphere.moving_mesh_grid_to_inertial_map().inverse(\n              target_point, time, functions_of_time);\n      ASSERT(x_grid.has_value(),\n             \"Failed to invert grid-to-inertial map of excision sphere at \"\n                 << excision_sphere.center() << \" for point \" << target_point\n                 << \".\");\n      tnsr::I<double, Dim, Frame::Distorted> result{};\n      for (size_t d = 0; d < Dim; ++d) {\n        result.get(d) = x_grid->get(d);\n      }\n      return result;\n    } else {\n      tnsr::I<double, Dim, Frame::Distorted> result{};\n      for (size_t d = 0; d < Dim; ++d) {\n        result.get(d) = target_point.get(d);\n      }\n      return result;\n    }\n  }();\n  for (size_t d = 0; d < Dim; ++d) {\n    x_distorted.get(d) -= excision_sphere.center().get(d);\n  }\n  auto x_spherical_distorted = cartesian_to_spherical(x_distorted);\n  // Construct anchor points in grid frame\n  tnsr::I<DataVector, Dim, Frame::Spherical<Frame::Grid>>\n      anchors_grid_spherical{NumExtrapolationAnchors};\n  for (size_t i = 0; i < NumExtrapolationAnchors; ++i) {\n    get<0>(anchors_grid_spherical)[i] =\n        1. + static_cast<double>(i) * extrapolation_spacing;\n  }\n  get<0>(anchors_grid_spherical) *= excision_sphere.radius();\n  // The grid-distorted map preserves angles\n  if constexpr (Dim > 1) {\n    get<1>(anchors_grid_spherical) = get<1>(x_spherical_distorted);\n  }\n  if constexpr (Dim > 2) {\n    get<2>(anchors_grid_spherical) = get<2>(x_spherical_distorted);\n  }\n  auto anchors_grid = spherical_to_cartesian(anchors_grid_spherical);\n  for (size_t d = 0; d < Dim; ++d) {\n    anchors_grid.get(d) += excision_sphere.center().get(d);\n  }\n  // Map anchor points to block logical coordinates. These are needed for\n  // interpolation.\n  auto anchors_block_logical =\n      block_logical_coordinates(domain, anchors_grid, time, functions_of_time);\n  const size_t block_id = anchors_block_logical[0]->id.get_index();\n  const auto& block = domain.blocks()[block_id];\n  // Map anchor points to the distorted frame. We will extrapolate in\n  // the distorted frame because we have the target point in the distorted\n  // frame. The target point in the grid frame is undefined because there's\n  // no grid-distorted map in the excision sphere. We could extrapolate in\n  // the inertial frame, but that's just an unnecessary transformation.\n  auto anchors_distorted =\n      [&block, &anchors_grid, &time,\n       &functions_of_time]() -> tnsr::I<DataVector, Dim, Frame::Distorted> {\n    if (block.has_distorted_frame()) {\n      return block.moving_mesh_grid_to_distorted_map()(anchors_grid, time,\n                                                       functions_of_time);\n    } else {\n      tnsr::I<DataVector, Dim, Frame::Distorted> result{};\n      for (size_t d = 0; d < Dim; ++d) {\n        result.get(d) = anchors_grid.get(d);\n      }\n      return result;\n    }\n  }();\n  for (size_t d = 0; d < Dim; ++d) {\n    anchors_distorted.get(d) -= excision_sphere.center().get(d);\n  }\n  // Compute magnitude(anchors_distorted) and store in std::array\n  std::array<double, NumExtrapolationAnchors> radial_anchors_distorted_frame{};\n  for (size_t i = 0; i < NumExtrapolationAnchors; ++i) {\n    auto& radial_anchor_distorted_frame =\n        gsl::at(radial_anchors_distorted_frame, i);\n    radial_anchor_distorted_frame = square(get<0>(anchors_distorted)[i]);\n    if constexpr (Dim > 1) {\n      radial_anchor_distorted_frame += square(get<1>(anchors_distorted)[i]);\n    }\n    if constexpr (Dim > 2) {\n      radial_anchor_distorted_frame += square(get<2>(anchors_distorted)[i]);\n    }\n    radial_anchor_distorted_frame = sqrt(radial_anchor_distorted_frame);\n  }\n  // Return false if the target point is not inside the excision. It would be\n  // nice to do this earlier to avoid unnecessary work. Here we use the first\n  // anchor point in the distorted frame, which is at the excision boundary in\n  // the angular direction of the target point.\n  const double excision_radius_distorted = radial_anchors_distorted_frame[0];\n  if (get<0>(x_spherical_distorted) > excision_radius_distorted) {\n    return false;\n  }\n  // Add the anchor points to the the interpolation target points\n  for (size_t i = 0; i < NumExtrapolationAnchors; ++i) {\n    ASSERT(anchors_block_logical[i].has_value(),\n           \"Extrapolation anchor point is not in any block. This should \"\n           \"not happen.\");\n    block_logical_coords->push_back(std::move(anchors_block_logical[i]));\n  }\n  // Add the anchor points to the extrapolation info\n  extrapolation_info->push_back(\n      {// Target index is the point we're extrapolating to. It will be set\n       // outside this function.\n       std::numeric_limits<size_t>::max(),\n       // Source index will be adjusted outside this function as well.\n       extrapolation_info->size() * NumExtrapolationAnchors,\n       // Target point and anchors are the radii in the distorted frame\n       get<0>(x_spherical_distorted),\n       std::move(radial_anchors_distorted_frame)});\n  return true;\n}\n\ntemplate <typename ResultDataType, size_t NumExtrapolationAnchors>\nvoid extrapolate_into_excisions_impl(\n    const gsl::not_null<std::vector<ResultDataType>*> result,\n    const std::vector<ExtrapolationInfo<NumExtrapolationAnchors>>&\n        extrapolation_info,\n    [[maybe_unused]] const size_t resolved_num_threads) {\n#pragma omp parallel for num_threads(resolved_num_threads)\n  for (const auto& extrapolation : extrapolation_info) {\n    double extrapolation_error = 0.;\n    for (size_t i = 0; i < result->size(); ++i) {\n      intrp::polynomial_interpolation<NumExtrapolationAnchors - 1>(\n          make_not_null(&(*result)[i][extrapolation.target_index]),\n          make_not_null(&extrapolation_error), extrapolation.target_point,\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n          gsl::make_span((*result)[i].data() + extrapolation.source_index,\n                         NumExtrapolationAnchors),\n          gsl::make_span(extrapolation.anchors.data(),\n                         NumExtrapolationAnchors));\n    }\n  }  // omp for extrapolation_info\n}\n\ntemplate <size_t Dim>\nauto parse_volume_files(const std::variant<std::vector<std::string>,\n                                           std::string>& volume_files_or_glob,\n                        const std::string& subfile_name,\n                        const ObservationVariant& observation) {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  // Get the list of volume data files\n  std::vector<std::string> filenames =\n      std::visit(Overloader{[](const std::vector<std::string>& volume_files) {\n                              return volume_files;\n                            },\n                            [](const std::string& volume_files_glob) {\n                              return file_system::glob(volume_files_glob);\n                            }},\n                 volume_files_or_glob);\n  if (filenames.empty()) {\n    ERROR_NO_TRACE(\"No volume files found. Specify at least one volume file.\");\n  }\n\n  // Retrieve info from the first volume file\n  const h5::H5File<h5::AccessType::ReadOnly> first_h5file(filenames.front());\n  const auto& first_volfile = first_h5file.get<h5::VolumeData>(subfile_name);\n  const auto dim = first_volfile.get_dimension();\n  if (dim != Dim) {\n    ERROR_NO_TRACE(\"Mismatched dimensions: expected \"\n                   << Dim << \"D volume data, but got \" << dim << \"D.\");\n  }\n  // Get observation ID\n  // This currently assumes that all volume files contain the same observations,\n  // so we only look into the first file. For generalizing to volume files\n  // across multiple segments, see the Python function\n  // `Visualization.ReadH5:select_observation` and possibly move it to C++.\n  const size_t obs_id =\n      std::visit(SelectObservation{first_volfile}, observation);\n  const double time = first_volfile.get_observation_value(obs_id);\n  // Get domain, functions of time\n  auto domain = deserialize<Domain<Dim>>(first_volfile.get_domain()->data());\n  auto functions_of_time = [&first_volfile, &obs_id]() {\n    const auto serialized_fot = first_volfile.get_functions_of_time(obs_id);\n    if (not serialized_fot.has_value()) {\n      return domain::FunctionsOfTimeMap{};\n    }\n    return deserialize<domain::FunctionsOfTimeMap>(serialized_fot->data());\n  }();\n  first_h5file.close();\n  return std::make_tuple(std::move(filenames), obs_id, time, std::move(domain),\n                         std::move(functions_of_time));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nauto block_logical_coordinates(\n    const tnsr::I<DataType, Dim, Frame>& target_points,\n    const Domain<Dim>& domain, const double time,\n    const domain::FunctionsOfTimeMap& functions_of_time,\n    const bool extrapolate_into_excisions, const bool error_on_missing_points,\n    [[maybe_unused]] const size_t resolved_num_threads) {\n  const size_t num_target_points = get_size(get<0>(target_points));\n  // Look up block logical coordinates for all target points by mapping them\n  // through the domain. This is the most expensive part of the function, so we\n  // parallelize the loop.\n  std::vector<BlockLogicalCoords<Dim>> block_logical_coords(num_target_points);\n  // We also set up the extrapolation into excisions here. Anchor points are\n  // added to the `block_logical_coords` and additional information is collected\n  // in `extrapolation_info` for later extrapolation.\n  constexpr size_t num_extrapolation_anchors = 8;\n  [[maybe_unused]] const double extrapolation_spacing = 0.3;\n  std::vector<ExtrapolationInfo<num_extrapolation_anchors>>\n      extrapolation_info{};\n  if constexpr (not std::is_same_v<Frame, ::Frame::Inertial>) {\n    if (extrapolate_into_excisions) {\n      ERROR(\n          \"Extrapolation into excisions is only supported in the inertial \"\n          \"frame at the moment.\");\n    }\n  }\n#pragma omp parallel num_threads(resolved_num_threads)\n  {\n    // Set up thread-local variables\n    tnsr::I<double, Dim, Frame> target_point{};\n    std::vector<BlockLogicalCoords<Dim>> extra_block_logical_coords{};\n    std::vector<ExtrapolationInfo<num_extrapolation_anchors>>\n        extra_extrapolation_info{};\n    // Keep track of a priority order for blocks to accelerate the search.\n    // This helps when the target points are ordered so that they are likely to\n    // be in the same block as the previous point.\n    std::vector<size_t> block_order(domain.blocks().size());\n    std::iota(block_order.begin(), block_order.end(), 0);\n#pragma omp for\n    for (size_t s = 0; s < num_target_points; ++s) {\n      for (size_t d = 0; d < Dim; ++d) {\n        target_point.get(d) = get_element(target_points.get(d), s);\n      }\n      block_logical_coords[s] = block_logical_coordinates_single_point(\n          target_point, domain, time, functions_of_time,\n          make_not_null(&block_order));\n      if (block_logical_coords[s].has_value()) {\n        continue;\n      }\n      if (not extrapolate_into_excisions) {\n        // The point wasn't found in any block and we're not extrapolating.\n        // Check if we should throw an error or just skip this point.\n        if (error_on_missing_points) {\n          ERROR(\"Point is not in any block:\\n\" << target_point);\n        } else {\n          continue;\n        }\n      }\n      // The point wasn't found in any block. Check if it's in an excision and\n      // set up extrapolation if requested.\n      if constexpr (std::is_same_v<Frame, ::Frame::Inertial>) {\n        bool found_in_excision = false;\n        for (const auto& [name, excision_sphere] : domain.excision_spheres()) {\n          if (add_extrapolation_anchors(\n                  make_not_null(&extra_block_logical_coords),\n                  make_not_null(&extra_extrapolation_info), excision_sphere,\n                  domain, target_point, time, functions_of_time,\n                  extrapolation_spacing)) {\n            extra_extrapolation_info.back().target_index = s;\n            found_in_excision = true;\n            break;\n          }\n        }\n        if (not found_in_excision and error_on_missing_points) {\n          ERROR(\"Point is not in any block or excision:\\n\" << target_point);\n        }\n      }\n    }  // omp for target points\n#pragma omp critical\n    {\n      // Append the extra block logical coordinates and extrapolation info from\n      // this thread to the global vectors. Also set the source index to the\n      // index in `block_logical_coords` where we're going to insert the new\n      // coordinates.\n      for (auto& info : extra_extrapolation_info) {\n        info.source_index += block_logical_coords.size();\n      }\n      block_logical_coords.insert(block_logical_coords.end(),\n                                  extra_block_logical_coords.begin(),\n                                  extra_block_logical_coords.end());\n      extrapolation_info.insert(extrapolation_info.end(),\n                                extra_extrapolation_info.begin(),\n                                extra_extrapolation_info.end());\n    }  // omp critical\n  }  // omp parallel\n  return std::make_tuple(std::move(block_logical_coords),\n                         std::move(extrapolation_info));\n}\n\n}  // namespace\n\ntemplate <typename ResultDataType, size_t Dim, typename Frame>\nvoid interpolate_to_points(\n    const gsl::not_null<std::vector<ResultDataType>*> result,\n    const std::variant<std::vector<std::string>, std::string>&\n        volume_files_or_glob,\n    const std::string& subfile_name, const ObservationVariant& observation,\n    const std::vector<std::string>& tensor_components,\n    const tnsr::I<DataVector, Dim, Frame>& target_points,\n    const bool extrapolate_into_excisions, const bool error_on_missing_points,\n    const std::optional<size_t> num_threads) {\n  const size_t num_target_points = get<0>(target_points).size();\n  const auto [filenames, obs_id, time, domain, functions_of_time] =\n      parse_volume_files<Dim>(volume_files_or_glob, subfile_name, observation);\n\n  const size_t resolved_num_threads = resolve_num_threads(num_threads);\n\n  const auto [block_logical_coords, extrapolation_info] =\n      block_logical_coordinates(target_points, domain, time, functions_of_time,\n                                extrapolate_into_excisions,\n                                error_on_missing_points, resolved_num_threads);\n  const size_t num_interpolation_points = block_logical_coords.size();\n\n  // Allocate memory for result\n  const size_t num_components = tensor_components.size();\n  result->resize(num_components);\n  for (size_t i = 0; i < num_components; ++i) {\n    ResultDataType& component = (*result)[i];\n    if constexpr (std::is_same_v<ResultDataType, DataVector>) {\n      component.destructive_resize(num_interpolation_points);\n    } else {\n      component.resize(num_interpolation_points);\n    }\n    std::fill(component.begin(), component.end(),\n              std::numeric_limits<double>::signaling_NaN());\n  }\n  std::vector<bool> filled_data(num_interpolation_points, false);\n\n  // Process all volume files in serial, because loading data with H5 must be\n  // done in serial anyway. Instead, the loop over elements within each file is\n  // parallelized with OpenMP.\n  for (const auto& filename : filenames) {\n    const h5::H5File<h5::AccessType::ReadOnly> h5file(filename);\n    const auto& volfile = h5file.get<h5::VolumeData>(subfile_name);\n    const auto [element_ids, meshes] = load_grids<Dim>(volfile, obs_id);\n\n    // Map the target points to element-logical coordinates. This selects the\n    // subset of target points that are in the volume data file's elements.\n    const auto element_logical_coords =\n        element_logical_coordinates(element_ids, block_logical_coords);\n    if (element_logical_coords.empty()) {\n      continue;\n    }\n\n    // Load the tensor data for all grids in the file because it's stored\n    // contiguously\n    const auto tensor_data =\n        load_tensor_data(volfile, obs_id, tensor_components);\n    h5file.close();\n\n    // Interpolate the tensor data to the target points\n    interpolate_to_points_impl(result, make_not_null(&filled_data),\n                               element_logical_coords, element_ids, meshes,\n                               tensor_data, resolved_num_threads);\n\n    // Terminate early if all data has been filled\n    if (std::all_of(filled_data.begin(), filled_data.end(),\n                    [](const bool filled) { return filled; })) {\n      break;\n    }\n  }\n\n  if (extrapolate_into_excisions and not extrapolation_info.empty()) {\n    extrapolate_into_excisions_impl(result, extrapolation_info,\n                                    resolved_num_threads);\n    // Clear the anchor points from the result\n    for (size_t i = 0; i < result->size(); ++i) {\n      ResultDataType& component = (*result)[i];\n      if constexpr (std::is_same_v<ResultDataType, DataVector>) {\n        // Can't use `destructive_resize` because we want to preserve the\n        // leading elements of the DataVector\n        DataVector new_component(num_target_points);\n        std::copy(\n            component.begin(),\n            component.begin() + static_cast<std::ptrdiff_t>(num_target_points),\n            new_component.begin());\n        component = std::move(new_component);\n      } else {\n        component.resize(num_target_points);\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim, typename Frame>\nPointwiseInterpolator<Dim, Frame>::PointwiseInterpolator(\n    const std::variant<std::vector<std::string>, std::string>&\n        volume_files_or_glob,\n    const std::string& subfile_name, const ObservationVariant& observation,\n    const std::vector<std::string>& tensor_components) {\n  auto bindings =\n      parse_volume_files<Dim>(volume_files_or_glob, subfile_name, observation);\n  const auto& filenames = std::get<0>(bindings);\n  obs_id_ = std::get<1>(bindings);\n  time_ = std::get<2>(bindings);\n  domain_ = std::move(std::get<3>(bindings));\n  functions_of_time_ = std::move(std::get<4>(bindings));\n\n  // Load tensor data into memory\n  element_ids_.reserve(filenames.size());\n  element_search_trees_.reserve(filenames.size());\n  meshes_.reserve(filenames.size());\n  tensor_data_.reserve(filenames.size());\n  for (const auto& filename : filenames) {\n    const h5::H5File<h5::AccessType::ReadOnly> h5file(filename);\n    const auto& volfile = h5file.get<h5::VolumeData>(subfile_name);\n    const auto [element_ids, meshes] = load_grids<Dim>(volfile, obs_id_);\n    element_ids_.push_back(std::move(element_ids));\n    element_search_trees_.push_back(\n        domain::index_element_ids(element_ids_.back()));\n    meshes_.push_back(std::move(meshes));\n    tensor_data_.push_back(\n        load_tensor_data(volfile, obs_id_, tensor_components));\n    h5file.close();\n  }\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid PointwiseInterpolator<Dim, Frame>::interpolate_to_points(\n    const gsl::not_null<std::vector<DataVector>*> result,\n    const tnsr::I<DataVector, Dim, Frame>& target_points,\n    const bool extrapolate_into_excisions, const bool error_on_missing_points,\n    const std::optional<size_t> num_threads) const {\n  ASSERT(not tensor_data_.empty(),\n         \"PointwiseInterpolator has not been initialized with tensor data.\");\n  const size_t resolved_num_threads = resolve_num_threads(num_threads);\n  const size_t num_target_points = get<0>(target_points).size();\n\n  // Map the target points through the domain\n  const auto [block_logical_coords, extrapolation_info] =\n      block_logical_coordinates(target_points, domain_, time_,\n                                functions_of_time_, extrapolate_into_excisions,\n                                error_on_missing_points, resolved_num_threads);\n  const size_t num_interpolation_points = block_logical_coords.size();\n\n  // Allocate memory for result\n  const size_t num_components = tensor_data_.front().size();\n  result->resize(num_components);\n  for (size_t i = 0; i < num_components; ++i) {\n    DataVector& component = (*result)[i];\n    component.destructive_resize(num_interpolation_points);\n    std::fill(component.begin(), component.end(),\n              std::numeric_limits<double>::signaling_NaN());\n  }\n  std::vector<bool> filled_data(num_interpolation_points, false);\n\n  // Process all volume files in serial\n  for (size_t file_id = 0; file_id < element_ids_.size(); ++file_id) {\n    const auto& element_ids = element_ids_[file_id];\n    const auto& meshes = meshes_[file_id];\n    const auto& tensor_data = tensor_data_[file_id];\n\n    // Map the target points to element-logical coordinates. This selects the\n    // subset of target points that are in the volume data file's elements.\n    const auto element_logical_coords =\n        element_logical_coordinates(element_ids, block_logical_coords);\n    if (element_logical_coords.empty()) {\n      continue;\n    }\n\n    // Interpolate the tensor data to the target points\n    interpolate_to_points_impl(result, make_not_null(&filled_data),\n                               element_logical_coords, element_ids, meshes,\n                               tensor_data, resolved_num_threads);\n\n    // Terminate early if all data has been filled\n    if (std::all_of(filled_data.begin(), filled_data.end(),\n                    [](const bool filled) { return filled; })) {\n      break;\n    }\n  }\n\n  if (extrapolate_into_excisions) {\n    extrapolate_into_excisions_impl(result, extrapolation_info,\n                                    resolved_num_threads);\n    // Clear the anchor points from the result\n    for (size_t i = 0; i < result->size(); ++i) {\n      DataVector& component = (*result)[i];\n      // Can't use `destructive_resize` because we want to preserve the\n      // leading elements of the DataVector\n      DataVector new_component(num_target_points);\n      std::copy(\n          component.begin(),\n          component.begin() + static_cast<std::ptrdiff_t>(num_target_points),\n          new_component.begin());\n      component = std::move(new_component);\n    }\n  }\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid PointwiseInterpolator<Dim, Frame>::interpolate_to_point(\n    const gsl::not_null<std::vector<double>*> result,\n    const tnsr::I<double, Dim, Frame>& target_point,\n    const std::optional<gsl::not_null<std::vector<size_t>*>> block_order)\n    const {\n  ASSERT(not tensor_data_.empty(),\n         \"PointwiseInterpolator has not been initialized with tensor data.\");\n  const auto block_logical_coords = block_logical_coordinates_single_point(\n      target_point, domain_, time_, functions_of_time_, block_order);\n  if (not block_logical_coords.has_value()) {\n    ERROR(\"Point is not in any block:\\n\" << target_point);\n  }\n  interpolate_to_point(result, block_logical_coords.value());\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid PointwiseInterpolator<Dim, Frame>::interpolate_to_point(\n    const gsl::not_null<std::vector<double>*> result,\n    const IdPair<domain::BlockId, tnsr::I<double, Dim, ::Frame::BlockLogical>>&\n        target_point) const {\n  ASSERT(not tensor_data_.empty(),\n         \"PointwiseInterpolator has not been initialized with tensor data.\");\n  for (size_t file_id = 0; file_id < element_ids_.size(); ++file_id) {\n    const auto& element_search_tree = element_search_trees_[file_id];\n    const auto& meshes = meshes_[file_id];\n    const auto& tensor_data = tensor_data_[file_id];\n    const auto element_logical_coords =\n        element_logical_coordinates(target_point, element_search_tree);\n    if (not element_logical_coords.has_value()) {\n      continue;\n    }\n    const auto& element_id = element_logical_coords.value().first;\n    const auto& x_element_logical = element_logical_coords.value().second;\n    const auto& mesh_offset_length = meshes.at(element_id);\n    interpolate_to_point_impl(\n        result, x_element_logical, get<0>(mesh_offset_length),\n        get<1>(mesh_offset_length), get<2>(mesh_offset_length), tensor_data);\n    break;\n  }\n}\n\n// Generate instantiations\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void interpolate_to_points<DTYPE(data), DIM(data), FRAME(data)>(   \\\n      gsl::not_null<std::vector<DTYPE(data)>*> result,                        \\\n      const std::variant<std::vector<std::string>, std::string>&              \\\n          volume_files_or_glob,                                               \\\n      const std::string& subfile_name, const ObservationVariant& observation, \\\n      const std::vector<std::string>& tensor_components,                      \\\n      const tnsr::I<DataVector, DIM(data), FRAME(data)>& target_points,       \\\n      bool extrapolate_into_excisions, bool error_on_missing_points,          \\\n      std::optional<size_t> num_threads);\n\n#define INSTANTIATE_CLASSES(_, data) \\\n  template class PointwiseInterpolator<DIM(data), FRAME(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (DataVector, std::vector<double>))\nGENERATE_INSTANTIATIONS(INSTANTIATE_CLASSES, (1, 2, 3),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef INSTANTIATE\n#undef INSTANTIATE_CLASSES\n#undef DIM\n#undef FRAME\n#undef DTYPE\n\n}  // namespace spectre::Exporter\n\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "src/IO/Exporter/PointwiseInterpolator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementSearchTree.hpp\"\n#include \"IO/Exporter/Exporter.hpp\"\n\nnamespace spectre::Exporter {\n\n/// Collect tensor component names from Tags list\ntemplate <typename Tags>\nauto get_tensor_components() {\n  std::vector<std::string> tensor_components{};\n  tmpl::for_each<Tags>([&tensor_components](auto tag_v) {\n    using tensor_tag = tmpl::type_from<decltype(tag_v)>;\n    using TensorType = typename tensor_tag::type;\n    const std::string tag_name = db::tag_name<tensor_tag>();\n    for (size_t i = 0; i < TensorType::size(); ++i) {\n      const std::string component_name =\n          tag_name + TensorType::component_suffix(i);\n      tensor_components.push_back(component_name);\n    }\n  });\n  return tensor_components;\n}\n\n/// Convert tensor components to a tagged_tuple\ntemplate <typename Tags, typename DataType>\nauto make_tagged_tuple(std::vector<DataType> interpolated_data) {\n  tuples::tagged_tuple_from_typelist<Tags> result{};\n  size_t component_index = 0;\n  tmpl::for_each<Tags>(\n      [&component_index, &interpolated_data, &result](auto tag_v) {\n        using tensor_tag = tmpl::type_from<decltype(tag_v)>;\n        using TensorType = typename tensor_tag::type;\n        for (size_t i = 0; i < TensorType::size(); ++i) {\n          get<tensor_tag>(result)[i] =\n              std::move(interpolated_data[component_index]);\n          ++component_index;\n        }\n      });\n  return result;\n}\n\n/// @{\n/*!\n * \\brief Interpolates data in volume files to target points\n *\n * These are overloads of the `interpolate_to_points` function that work with\n * Tensor types and tags, rather than the raw C++ types that are used in\n * Exporter.hpp so it can be used by external programs.\n *\n * The `Tags` template parameter is a typelist of tags that should be read from\n * the volume files. The dataset names to read are constructed from the tag\n * names. Here is an example of how to use this function:\n *\n * \\snippet Test_Exporter.cpp interpolate_tensors_to_points_example\n */\ntemplate <typename ResultDataType, size_t Dim, typename Frame>\nvoid interpolate_to_points(\n    gsl::not_null<std::vector<ResultDataType>*> result,\n    const std::variant<std::vector<std::string>, std::string>&\n        volume_files_or_glob,\n    const std::string& subfile_name, const ObservationVariant& observation,\n    const std::vector<std::string>& tensor_components,\n    const tnsr::I<DataVector, Dim, Frame>& target_points,\n    bool extrapolate_into_excisions = false,\n    bool error_on_missing_points = false,\n    std::optional<size_t> num_threads = std::nullopt);\n\ntemplate <size_t Dim, typename Frame>\nstd::vector<DataVector> interpolate_to_points(\n    const std::variant<std::vector<std::string>, std::string>&\n        volume_files_or_glob,\n    const std::string& subfile_name, const ObservationVariant& observation,\n    const std::vector<std::string>& tensor_components,\n    const tnsr::I<DataVector, Dim, Frame>& target_points,\n    bool extrapolate_into_excisions = false,\n    bool error_on_missing_points = false,\n    std::optional<size_t> num_threads = std::nullopt) {\n  std::vector<DataVector> interpolated_data{};\n  interpolate_to_points(make_not_null(&interpolated_data), volume_files_or_glob,\n                        subfile_name, observation, tensor_components,\n                        target_points, extrapolate_into_excisions,\n                        error_on_missing_points, num_threads);\n  return interpolated_data;\n}\n\ntemplate <typename Tags, size_t Dim, typename Frame>\ntuples::tagged_tuple_from_typelist<Tags> interpolate_to_points(\n    const std::variant<std::vector<std::string>, std::string>&\n        volume_files_or_glob,\n    const std::string& subfile_name, const ObservationVariant& observation,\n    const tnsr::I<DataVector, Dim, Frame>& target_points,\n    bool extrapolate_into_excisions = false,\n    const bool error_on_missing_points = false,\n    std::optional<size_t> num_threads = std::nullopt) {\n  return make_tagged_tuple<Tags>(interpolate_to_points(\n      volume_files_or_glob, subfile_name, observation,\n      get_tensor_components<Tags>(), target_points, extrapolate_into_excisions,\n      error_on_missing_points, num_threads));\n}\n/// @}\n\n/*!\n * \\brief Interpolates data in volume files to target points by reading the\n * volume data into memory\n *\n * This class reads the volume data at the requested time into memory and can\n * then interpolate it to any number of target points.\n *\n * \\par Thread safety\n * Constructing the `PointwiseInterpolator` on multiple threads at once is not\n * thread safe (unless HDF5 was built with thread-safety support) because the\n * constructor opens the H5 files and reads in the data. However, once the data\n * is loaded, the `interpolate_to_points` and `interpolate_to_point` functions\n * are thread safe. This means the volume data can be loaded once in a single\n * thread and then used by multiple threads to interpolate to different points\n * in parallel.\n */\ntemplate <size_t Dim, typename Frame = ::Frame::Inertial>\nstruct PointwiseInterpolator {\n  PointwiseInterpolator() = default;\n  PointwiseInterpolator(const std::variant<std::vector<std::string>,\n                                           std::string>& volume_files_or_glob,\n                        const std::string& subfile_name,\n                        const ObservationVariant& observation,\n                        const std::vector<std::string>& tensor_components);\n\n  /*!\n   * \\brief Interpolate to many points\n   *\n   * \\param result the interpolated data at the target points. The outer vector\n   * is the number of components and the inner vector is the number of target\n   * points. Will be resized automatically.\n   * \\param target_points the points to interpolate to\n   * \\param extrapolate_into_excisions whether to extrapolate into excised\n   * regions\n   * \\param error_on_missing_points whether to throw an error if any of the\n   * target points are outside the domain\n   * \\param num_threads the number of OpenMP threads to use to parallelize the\n   * interpolation over the target points. If not provided, the default number\n   * of threads will be used.\n   */\n  void interpolate_to_points(\n      gsl::not_null<std::vector<DataVector>*> result,\n      const tnsr::I<DataVector, Dim, Frame>& target_points,\n      bool extrapolate_into_excisions = false,\n      bool error_on_missing_points = false,\n      std::optional<size_t> num_threads = std::nullopt) const;\n\n  /*!\n   * \\brief Interpolate to a single point\n   *\n   * This function is thread safe, so the interpolator can be constructed to\n   * load the volume data once and then used by multiple threads to interpolate\n   * to different points in parallel. Note that updating the `block_order` (if\n   * provided) is not thread safe, so each thread should manage a separate\n   * `block_order`.\n   *\n   * \\param result the interpolated data at the target point. The vector is over\n   * the number of components. Will be resized automatically.\n   * \\param target_point the point to interpolate to\n   * \\param block_order an optional priority order to search for the block\n   * containing the target point. Will be updated when the point is found.\n   * See `block_logical_coordinates_single_point` for more details.\n   */\n  void interpolate_to_point(gsl::not_null<std::vector<double>*> result,\n                            const tnsr::I<double, Dim, Frame>& target_point,\n                            std::optional<gsl::not_null<std::vector<size_t>*>>\n                                block_order = std::nullopt) const;\n\n  /*!\n   * \\brief Interpolate to a single point in block-logical coordinates\n   *\n   * \\param result the interpolated data at the target point. The vector is over\n   * the number of components. Will be resized automatically.\n   * \\param target_point the point to interpolate to in block-logical\n   * coordinates.\n   */\n  void interpolate_to_point(\n      gsl::not_null<std::vector<double>*> result,\n      const IdPair<domain::BlockId,\n                   tnsr::I<double, Dim, ::Frame::BlockLogical>>& target_point)\n      const;\n\n  size_t obs_id() const { return obs_id_; }\n  double time() const { return time_; }\n  const Domain<Dim>& domain() const { return domain_; }\n  const domain::FunctionsOfTimeMap& functions_of_time() const {\n    return functions_of_time_;\n  }\n\n private:\n  // Loaded from data files\n  size_t obs_id_ = std::numeric_limits<size_t>::max();\n  double time_ = std::numeric_limits<double>::signaling_NaN();\n  Domain<Dim> domain_;\n  domain::FunctionsOfTimeMap functions_of_time_;\n  // Outer vector is the source data file\n  std::vector<std::vector<ElementId<Dim>>> element_ids_;\n  std::vector<std::map<size_t, domain::ElementSearchTree<Dim>>>\n      element_search_trees_;\n  std::vector<\n      std::unordered_map<ElementId<Dim>, std::tuple<Mesh<Dim>, size_t, size_t>>>\n      meshes_;\n  std::vector<std::vector<std::variant<DataVector, std::vector<float>>>>\n      tensor_data_;\n};\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "src/IO/Exporter/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/numpy.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n#include \"IO/Exporter/SpacetimeInterpolator.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace py = pybind11;\n\nnamespace {\n\ntemplate <size_t Dim, typename Frame>\nvoid bind_interpolate_to_points_impl(py::module& m) {\n  m.def(\"interpolate_to_points\",\n        py::overload_cast<\n            const std::variant<std::vector<std::string>, std::string>&,\n            const std::string&, const spectre::Exporter::ObservationVariant&,\n            const std::vector<std::string>&,\n            const tnsr::I<DataVector, Dim, Frame>&, bool, bool,\n            std::optional<size_t>>(\n            &spectre::Exporter::interpolate_to_points<Dim, Frame>),\n        py::arg(\"volume_files_or_glob\"), py::arg(\"subfile_name\"),\n        py::arg(\"observation\"), py::arg(\"tensor_components\"),\n        py::arg(\"target_points\"), py::arg(\"extrapolate_into_excisions\") = false,\n        py::arg(\"error_on_missing_points\") = false,\n        py::arg(\"num_threads\") = std::nullopt);\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid bind_pointwise_interpolator_impl(py::module& m) {\n  using PointwiseInterpolator =\n      spectre::Exporter::PointwiseInterpolator<Dim, Frame>;\n  py::class_<PointwiseInterpolator>(\n      m, (\"PointwiseInterpolator\" + std::to_string(Dim) + \"D\" +\n          get_output(Frame{}))\n             .c_str())\n      .def(py::init<const std::variant<std::vector<std::string>, std::string>&,\n                    const std::string&,\n                    const spectre::Exporter::ObservationVariant&,\n                    const std::vector<std::string>&>(),\n           py::arg(\"volume_files_or_glob\"), py::arg(\"subfile_name\"),\n           py::arg(\"observation\"), py::arg(\"tensor_components\"))\n      .def(\"obs_id\", &PointwiseInterpolator::obs_id)\n      .def(\"time\", &PointwiseInterpolator::time)\n      .def(\"domain\", &PointwiseInterpolator::domain)\n      .def(\"functions_of_time\",\n           [](const PointwiseInterpolator& self) {\n             return clone_unique_ptrs(self.functions_of_time());\n           })\n      .def(\n          \"interpolate_to_point\",\n          [](const PointwiseInterpolator& self,\n             const tnsr::I<double, Dim, Frame>& target_point) {\n            std::vector<double> result{};\n            self.interpolate_to_point(make_not_null(&result), target_point);\n            return result;\n          },\n          py::arg(\"target_point\"));\n}\n\ntemplate <size_t Dim>\nvoid bind_spacetime_interpolator_impl(py::module& m) {\n  using SpacetimeInterpolator =\n      spectre::Exporter::SpacetimeInterpolator<Dim, Frame::Inertial>;\n  py::class_<SpacetimeInterpolator>(\n      m, (\"SpacetimeInterpolator\" + std::to_string(Dim) + \"D\").c_str())\n      .def(py::init<std::variant<std::vector<std::string>, std::string>,\n                    std::string, std::vector<std::string>>(),\n           py::arg(\"volume_files_or_glob\"), py::arg(\"subfile_name\"),\n           py::arg(\"tensor_components\"))\n      .def(\"max_time_bounds\", &SpacetimeInterpolator::max_time_bounds)\n      .def(\"load_time_bounds\", &SpacetimeInterpolator::load_time_bounds,\n           py::arg(\"time_bounds\"))\n      .def(\"time_bounds\", &SpacetimeInterpolator::time_bounds)\n      .def(\n          \"interpolate_to_point\",\n          [](const SpacetimeInterpolator& self,\n             const tnsr::I<double, Dim, Frame::Inertial>& target_point,\n             const double time) {\n            std::vector<double> result{};\n            self.interpolate_to_point(make_not_null(&result), target_point,\n                                      time);\n            return result;\n          },\n          py::arg(\"target_point\"), py::arg(\"time\"));\n}\n\n}  // namespace\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py::class_<spectre::Exporter::ObservationId>(m, \"ObservationId\")\n      .def(py::init<size_t>())\n      .def_readonly(\"value\", &spectre::Exporter::ObservationId::value);\n  py::class_<spectre::Exporter::ObservationStep>(m, \"ObservationStep\")\n      .def(py::init<int>())\n      .def_readonly(\"value\", &spectre::Exporter::ObservationStep::value);\n  bind_interpolate_to_points_impl<1, Frame::Grid>(m);\n  bind_interpolate_to_points_impl<2, Frame::Grid>(m);\n  bind_interpolate_to_points_impl<3, Frame::Grid>(m);\n  bind_interpolate_to_points_impl<1, Frame::Inertial>(m);\n  bind_interpolate_to_points_impl<2, Frame::Inertial>(m);\n  bind_interpolate_to_points_impl<3, Frame::Inertial>(m);\n  bind_pointwise_interpolator_impl<1, Frame::Grid>(m);\n  bind_pointwise_interpolator_impl<2, Frame::Grid>(m);\n  bind_pointwise_interpolator_impl<3, Frame::Grid>(m);\n  bind_pointwise_interpolator_impl<1, Frame::Inertial>(m);\n  bind_pointwise_interpolator_impl<2, Frame::Inertial>(m);\n  bind_pointwise_interpolator_impl<3, Frame::Inertial>(m);\n  bind_spacetime_interpolator_impl<1>(m);\n  bind_spacetime_interpolator_impl<2>(m);\n  bind_spacetime_interpolator_impl<3>(m);\n}\n"
  },
  {
    "path": "src/IO/Exporter/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyExporter\")\n\nspectre_python_add_module(\n  Exporter\n  MODULE_PATH \"IO\"\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n  InterpolateToPoints.py\n)\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ErrorHandling\n  Exporter\n  pybind11::module\n  Utilities\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyTensor\n  )\n"
  },
  {
    "path": "src/IO/Exporter/Python/InterpolateToPoints.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\n\nimport click\nimport numpy as np\nimport rich\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures.Tensor import DataVector, Frame, tnsr\nfrom spectre.IO.Exporter import ObservationId, interpolate_to_points\nfrom spectre.IO.H5 import open_volfiles_command, parse_points\n\nlogger = logging.getLogger(__name__)\n\n\n@click.command(name=\"interpolate-to-points\")\n@open_volfiles_command(obs_id_required=True, multiple_vars=True)\n@click.option(\n    \"--target-coords-file\",\n    \"-t\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    help=(\n        \"Text file with target coordinates to interpolate to. \"\n        \"Must have 'dim' columns with Cartesian coordinates. \"\n        \"Rows enumerate points. \"\n        \"Can be the output of 'numpy.savetxt'.\"\n    ),\n)\n@click.option(\n    \"--target-coords\",\n    \"-p\",\n    multiple=True,\n    callback=parse_points,\n    help=(\n        \"List target coordinates explicitly, e.g. '0,0,0'. \"\n        \"Can be specified multiple times to quickly interpolate \"\n        \"to a couple of target points.\"\n    ),\n)\n@click.option(\n    \"--frame\",\n    type=click.Choice(Frame.__members__),\n    callback=lambda ctx, param, value: Frame.__members__[value],\n    default=Frame.Inertial.name,\n    help=\"Frame in which coordinates are specified.\",\n)\n@click.option(\n    \"--extrapolate-into-excisions\",\n    is_flag=True,\n    help=(\n        \"Enables extrapolation into excision regions of the domain. \"\n        \"This can be useful to fill the excision region with \"\n        \"(constraint-violating but smooth) data so it can be imported into \"\n        \"moving puncture codes.\"\n    ),\n)\n@click.option(\n    \"--output\", \"-o\", type=click.Path(writable=True), help=\"Output text file\"\n)\n@click.option(\n    \"--delimiter\",\n    default=None,\n    show_default=\"whitespace\",\n    help=(\n        \"Delimiter separating columns for both the \"\n        \"'--target-coords-file' and the '--output' file.\"\n    ),\n)\n@click.option(\n    \"--num-threads\",\n    \"-j\",\n    type=int,\n    show_default=\"all available cores\",\n    help=(\n        \"Number of threads to use for interpolation. Only available if compiled\"\n        \" with OpenMP. Parallelization is over volume data files, so this only\"\n        \" has an effect if multiple files are specified.\"\n    ),\n)\ndef interpolate_to_points_command(\n    h5_files,\n    subfile_name,\n    obs_id,\n    obs_time,\n    vars,\n    target_coords,\n    target_coords_file,\n    frame,\n    output,\n    delimiter,\n    **kwargs,\n):\n    \"\"\"Interpolate volume data to target coordinates.\"\"\"\n    # Load target coords from file\n    if (target_coords is None) == (target_coords_file is None):\n        raise click.UsageError(\n            \"Specify either '--target-coords' / '-p' or \"\n            \"'--target-coords-file' / '-t'.\"\n        )\n    if target_coords_file:\n        target_coords = np.loadtxt(\n            target_coords_file, ndmin=2, delimiter=delimiter\n        )\n    dim = target_coords.shape[1]\n    target_points = tnsr.I[DataVector, dim, frame](target_coords.T)\n\n    # Interpolate!\n    interpolated_data = np.array(\n        interpolate_to_points(\n            h5_files,\n            subfile_name=subfile_name,\n            observation=ObservationId(obs_id),\n            tensor_components=vars,\n            target_points=target_points,\n            **kwargs,\n        )\n    )\n\n    # Output result\n    column_names = [\"X\", \"Y\", \"Z\"][:dim] + list(vars)\n    column_data = np.hstack([target_coords, interpolated_data.T])\n    if output:\n        np.savetxt(\n            output,\n            column_data,\n            delimiter=delimiter or \" \",\n            header=(\n                f\"t = {obs_time:g}\\n\" + (delimiter or \" \").join(column_names)\n            ),\n        )\n    else:\n        import rich.table\n\n        table = rich.table.Table(*column_names, box=None)\n        for row in column_data:\n            table.add_row(*list(map(str, row)))\n        rich.print(table)\n        rich.print(f\"(t = {obs_time:g})\")\n\n\nif __name__ == \"__main__\":\n    interpolate_to_points_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/IO/Exporter/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom typing import Sequence, Type\n\nfrom ._Pybindings import *\n\n\ndef interpolate_tensors_to_points(\n    *args,\n    tensor_names: Sequence[str],\n    tensor_types: Sequence[Type],\n    **kwargs,\n):\n    \"\"\"Wrapper around 'interpolate_to_points' for tensors.\n\n    Interpolates volume data to points by calling 'interpolate_to_points'.\n    However, instead of passing a list of tensor components\n    (like 'SpatialMetric_xx') you can pass a list of tensor names (like\n    'SpatialMetric') and their corresponding types (like\n    'tnsr.ii[DataVector, 3]') and this function will assemble the tensors\n    and return them as a list.\n\n    Note: It would be nice to move this function into C++, but that's not\n    trivial because the tensor types are not known at compile time. This can be\n    revisited when we have Python support for Variables.\n    \"\"\"\n    from spectre.DataStructures import DataVector\n\n    tensors = {\n        tensor_name: tensor_type()\n        for tensor_name, tensor_type in zip(tensor_names, tensor_types)\n    }\n    tensor_components = []\n    for name, tensor in tensors.items():\n        tensor_components += [\n            name + tensor.component_suffix(i) for i in range(tensor.size)\n        ]\n    data = interpolate_to_points(\n        *args,\n        **kwargs,\n        tensor_components=tensor_components,\n    )\n    j = 0\n    for tensor in tensors.values():\n        for i in range(tensor.size):\n            tensor[i] = DataVector(data[j])\n            j += 1\n    return list(tensors.values())\n\n\nSpacetimeInterpolator = {\n    1: SpacetimeInterpolator1D,\n    2: SpacetimeInterpolator2D,\n    3: SpacetimeInterpolator3D,\n}\n"
  },
  {
    "path": "src/IO/Exporter/SelectObservation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Exporter/SelectObservation.hpp\"\n\n#include <cstddef>\n\n#include \"IO/H5/VolumeData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace spectre::Exporter {\n\nsize_t SelectObservation::operator()(const ObservationId observation_id) const {\n  return observation_id.value;\n}\n\nsize_t SelectObservation::operator()(\n    const ObservationStep observation_step) const {\n  int step = observation_step.value;\n  const auto obs_ids = volfile.list_observation_ids();\n  if (step < 0) {\n    step += static_cast<int>(obs_ids.size());\n  }\n  if (step < 0 or static_cast<size_t>(step) >= obs_ids.size()) {\n    ERROR_NO_TRACE(\"Invalid observation step: \"\n                   << observation_step.value << \". There are \" << obs_ids.size()\n                   << \" observations in the file.\");\n  }\n  return obs_ids[static_cast<size_t>(step)];\n}\n\nsize_t SelectObservation::operator()(const double observation_value) const {\n  return volfile.find_observation_id(observation_value, obs_value_eps);\n}\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "src/IO/Exporter/SelectObservation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <variant>\n\n#include \"IO/Exporter/Exporter.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n\nnamespace spectre::Exporter {\n\n/// Determines the selected observation ID in the volume data file, given\n/// multiple possible ways to specify the observation.\nstruct SelectObservation {\n  size_t operator()(ObservationId observation_id) const;\n  size_t operator()(ObservationStep observation_step) const;\n  size_t operator()(double observation_value) const;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)\n  const h5::VolumeData& volfile;\n  double obs_value_eps = 1e-12;\n};\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "src/IO/Exporter/SpacetimeInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Exporter/SpacetimeInterpolator.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <mutex>\n#include <optional>\n#include <string>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n#include \"IO/Exporter/SelectObservation.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Interpolation/PolynomialInterpolation.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace spectre::Exporter {\n\ntemplate <size_t Dim, typename Frame>\nSpacetimeInterpolator<Dim, Frame>::SpacetimeInterpolator(\n    std::variant<std::vector<std::string>, std::string> volume_files_or_glob,\n    std::string subfile_name, std::vector<std::string> tensor_components)\n    : volume_files_or_glob_(std::move(volume_files_or_glob)),\n      subfile_name_(std::move(subfile_name)),\n      tensor_components_(std::move(tensor_components)) {\n  const std::vector<std::string> filenames =\n      std::visit(Overloader{[](const std::vector<std::string>& volume_files) {\n                              return volume_files;\n                            },\n                            [](const std::string& volume_files_glob) {\n                              return file_system::glob(volume_files_glob);\n                            }},\n                 volume_files_or_glob_);\n  if (filenames.empty()) {\n    ERROR(\"No volume data files found.\");\n  }\n  const h5::H5File<h5::AccessType::ReadOnly> first_h5file(filenames.front());\n  const auto& first_volfile = first_h5file.get<h5::VolumeData>(subfile_name_);\n  all_observation_ids_ = first_volfile.list_observation_ids();\n  all_observation_values_.resize(all_observation_ids_.size());\n  std::transform(all_observation_ids_.begin(), all_observation_ids_.end(),\n                 all_observation_values_.begin(),\n                 [&first_volfile](const size_t obs_id) {\n                   return first_volfile.get_observation_value(obs_id);\n                 });\n  if (all_observation_ids_.empty()) {\n    ERROR(\"No observation IDs found in the volume data files.\");\n  }\n  if (all_observation_ids_.size() < time_interpolation_order_ + 1) {\n    ERROR(\n        \"The number of available observations must be at least the time \"\n        \"interpolation order plus one (num observations >= \"\n        << time_interpolation_order_ + 1 << \"), but \"\n        << all_observation_ids_.size() << \" observations were found.\");\n  }\n  auto serialized_fots = first_volfile.get_global_functions_of_time();\n  if (serialized_fots.has_value()) {\n    functions_of_time_ =\n        deserialize<domain::FunctionsOfTimeMap>(serialized_fots->data());\n  }\n  first_h5file.close();\n}\n\ntemplate <size_t Dim, typename Frame>\nSpacetimeInterpolator<Dim, Frame>::SpacetimeInterpolator(\n    SpacetimeInterpolator&& rhs)\n    : volume_files_or_glob_(std::move(rhs.volume_files_or_glob_)),\n      subfile_name_(std::move(rhs.subfile_name_)),\n      tensor_components_(std::move(rhs.tensor_components_)),\n      functions_of_time_(std::move(rhs.functions_of_time_)),\n      all_observation_ids_(std::move(rhs.all_observation_ids_)),\n      all_observation_values_(std::move(rhs.all_observation_values_)),\n      time_bounds_(std::move(rhs.time_bounds_)),\n      interpolators_(std::move(rhs.interpolators_)) {}\n\ntemplate <size_t Dim, typename Frame>\nSpacetimeInterpolator<Dim, Frame>& SpacetimeInterpolator<Dim, Frame>::operator=(\n    SpacetimeInterpolator&& rhs) {\n  if (this != &rhs) {\n    volume_files_or_glob_ = std::move(rhs.volume_files_or_glob_);\n    subfile_name_ = std::move(rhs.subfile_name_);\n    tensor_components_ = std::move(rhs.tensor_components_);\n    functions_of_time_ = std::move(rhs.functions_of_time_);\n    all_observation_ids_ = std::move(rhs.all_observation_ids_);\n    all_observation_values_ = std::move(rhs.all_observation_values_);\n    time_bounds_ = std::move(rhs.time_bounds_);\n    interpolators_ = std::move(rhs.interpolators_);\n  }\n  return *this;\n}\n\ntemplate <size_t Dim, typename Frame>\nstd::array<double, 2> SpacetimeInterpolator<Dim, Frame>::max_time_bounds()\n    const {\n  return {*(all_observation_values_.begin() + num_ghost_slices_),\n          *(all_observation_values_.end() - num_ghost_slices_ - 1)};\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid SpacetimeInterpolator<Dim, Frame>::load_time_bounds(\n    std::array<double, 2> time_bounds) {\n  ASSERT(time_bounds[0] < time_bounds[1],\n         \"The lower time bound must be smaller than the upper time bound.\");\n  ASSERT(time_bounds[0] >= max_time_bounds()[0] and\n             time_bounds[1] <= max_time_bounds()[1],\n         \"The time bounds \" << time_bounds\n                            << \" must be within the maximum time bounds \"\n                            << max_time_bounds() << \".\");\n  // Collect all observations that we need to load\n  const auto lower_time =\n      std::lower_bound(all_observation_values_.begin(),\n                       all_observation_values_.end(), time_bounds[0],\n                       std::less_equal<double>{}) -\n      num_ghost_slices_ - 1;\n  const auto upper_time =\n      std::upper_bound(lower_time, all_observation_values_.end(),\n                       time_bounds[1], std::less_equal<double>{}) +\n      num_ghost_slices_ + 1;\n  time_bounds_[0] = *(lower_time + num_ghost_slices_);\n  time_bounds_[1] = *(upper_time - num_ghost_slices_ - 1);\n  const auto lower_id =\n      all_observation_ids_.begin() +\n      std::distance(all_observation_values_.begin(), lower_time);\n  const auto upper_id =\n      all_observation_ids_.begin() +\n      std::distance(all_observation_values_.begin(), upper_time);\n  const auto num_obs = static_cast<size_t>(std::distance(lower_id, upper_id));\n  ASSERT(num_obs >= time_interpolation_order_ + 1,\n         \"The number of time slices must be at least the time interpolation \"\n         \"order plus one (NumTimeSlices >= \"\n             << time_interpolation_order_ + 1 << \").\");\n\n  if (interpolators_.empty()) {\n    // No need to lock because we are loading interpolators for the first time\n    // so no other thread can be accessing the data.\n    for (auto it = lower_id; it != upper_id; ++it) {\n      interpolators_.emplace_back(volume_files_or_glob_, subfile_name_,\n                                  ObservationId{*it}, tensor_components_);\n    }\n    return;\n  }\n\n  // Drop interpolators that are not needed anymore. Do this early to free up\n  // memory.\n  auto first_interpolator_obs_id_it =\n      std::find(all_observation_ids_.begin(), all_observation_ids_.end(),\n                interpolators_.front().obs_id());\n  ASSERT(first_interpolator_obs_id_it != all_observation_ids_.end(),\n         \"The first interpolator is not in the list of observation IDs.\");\n  const auto last_interpolator_obs_id_it =\n      first_interpolator_obs_id_it +\n      static_cast<std::ptrdiff_t>(interpolators_.size());\n  const auto num_erase_front =\n      std::distance(first_interpolator_obs_id_it, lower_id);\n  const auto num_erase_back =\n      std::distance(upper_id, last_interpolator_obs_id_it);\n  if (num_erase_front == 0 and num_erase_back == 0) {\n    // No need to drop or load interpolators\n    return;\n  }\n  if (num_erase_front >= static_cast<std::ptrdiff_t>(interpolators_.size()) or\n      num_erase_back >= static_cast<std::ptrdiff_t>(interpolators_.size())) {\n    // We are dropping all interpolators and loading new ones. No need to lock\n    // because there's no overlapping time range where old data can still be\n    // used.\n    interpolators_.clear();\n    for (auto it = lower_id; it != upper_id; ++it) {\n      interpolators_.emplace_back(volume_files_or_glob_, subfile_name_,\n                                  ObservationId{*it}, tensor_components_);\n    }\n    return;\n  }\n  {\n    // Lock while we are modifying the vector\n    const std::unique_lock lock(interpolators_mutex_);\n    if (num_erase_front > 0) {\n      interpolators_.erase(interpolators_.begin(),\n                           interpolators_.begin() + num_erase_front);\n    }\n    if (num_erase_back > 0) {\n      interpolators_.erase(interpolators_.end() - num_erase_back,\n                           interpolators_.end());\n    }\n  }\n\n  // Load new interpolators\n  std::vector<PointwiseInterpolator<Dim, ::Frame::Grid>>\n      new_interpolators_front;\n  std::vector<PointwiseInterpolator<Dim, ::Frame::Grid>> new_interpolators_back;\n  if (num_erase_front < 0) {\n    const auto num_add_front =\n        std::min(static_cast<size_t>(-num_erase_front), num_obs);\n    new_interpolators_front.reserve(num_add_front);\n    for (auto it = lower_id;\n         it != lower_id + static_cast<std::ptrdiff_t>(num_add_front); ++it) {\n      new_interpolators_front.emplace_back(volume_files_or_glob_, subfile_name_,\n                                           ObservationId{*it},\n                                           tensor_components_);\n    }\n  }\n  if (num_erase_back < 0) {\n    const auto num_add_back =\n        std::min(static_cast<size_t>(-num_erase_back),\n                 num_obs - new_interpolators_front.size());\n    new_interpolators_back.reserve(num_add_back);\n    for (auto it = upper_id - static_cast<std::ptrdiff_t>(num_add_back);\n         it != upper_id; ++it) {\n      new_interpolators_back.emplace_back(volume_files_or_glob_, subfile_name_,\n                                          ObservationId{*it},\n                                          tensor_components_);\n    }\n  }\n  {\n    // Lock while we are modifying the vector\n    const std::unique_lock lock(interpolators_mutex_);\n    interpolators_.insert(\n        interpolators_.begin(),\n        std::make_move_iterator(new_interpolators_front.begin()),\n        std::make_move_iterator(new_interpolators_front.end()));\n    interpolators_.insert(\n        interpolators_.end(),\n        std::make_move_iterator(new_interpolators_back.begin()),\n        std::make_move_iterator(new_interpolators_back.end()));\n  }\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid SpacetimeInterpolator<Dim, Frame>::interpolate_to_point(\n    const gsl::not_null<std::vector<double>*> result,\n    const tnsr::I<double, Dim, Frame>& target_point, const double time,\n    const std::optional<gsl::not_null<std::vector<size_t>*>> block_order)\n    const {\n  // Lock to ensure that the `interpolators_` vector is not being modified while\n  // we access it. Multiple threads can still read the data concurrently even\n  // while loeading new data, just the short amount of time when the vector is\n  // being modified is locked.\n  const std::shared_lock lock(interpolators_mutex_);\n  ASSERT(not interpolators_.empty(),\n         \"SpacetimeInterpolator has not been initialized.\");\n  // Find the time slice that contains the target time\n  ASSERT(time >= time_bounds_[0] and time <= time_bounds_[1],\n         \"Requested time \" << time << \" is outside the time bounds.\");\n  const auto lower =\n      std::lower_bound(interpolators_.begin() + num_ghost_slices_ + 1,\n                       interpolators_.end() - num_ghost_slices_ - 1, time,\n                       [](const auto& interpolator, const double t) {\n                         return interpolator.time() <= t;\n                       });\n  // Transform target point to block-logical frame\n  // NOTE: We assume here that the grid frame and even the block-logical frame\n  // is the same in all time slices. This is true while the domain stays the\n  // same (just functions of time values change to control the grid-to-inertial\n  // map), but won't be true in general (e.g. with AMR regrids or ringdown\n  // transitions). Then, we have to do the transformation for each time slice\n  // separately (see https://arxiv.org/abs/1606.00437). This is not implemented\n  // yet.\n  const auto& any_interpolator = *lower;\n  const auto block_logical_coords = block_logical_coordinates_single_point(\n      target_point, any_interpolator.domain(), time, functions_of_time_,\n      block_order);\n  if (not block_logical_coords.has_value()) {\n    ERROR(\"Point is not in any block:\\n\" << target_point);\n  }\n  // Interpolate to the grid-frame target point in each time slice\n  std::array<std::vector<double>, time_interpolation_order_ + 1> data{};\n  std::array<double, time_interpolation_order_ + 1> times{};\n  const auto times_span = gsl::make_span(times);\n  for (size_t i = 0; i < time_interpolation_order_ + 1; ++i) {\n    const auto& interpolator =\n        *(lower - 1 - static_cast<std::ptrdiff_t>(num_ghost_slices_) +\n          static_cast<std::ptrdiff_t>(i));\n    interpolator.interpolate_to_point(make_not_null(&gsl::at(data, i)),\n                                      block_logical_coords.value());\n    gsl::at(times, i) = interpolator.time();\n  }\n  // Interpolate in time\n  const size_t num_components = tensor_components_.size();\n  result->resize(num_components);\n  std::array<double, time_interpolation_order_ + 1> values{};\n  const auto values_span = gsl::make_span(values);\n  double interpolation_error = 0.;\n  for (size_t j = 0; j < num_components; ++j) {\n    for (size_t k = 0; k < time_interpolation_order_ + 1; ++k) {\n      gsl::at(values, k) = gsl::at(data, k)[j];\n    }\n    intrp::polynomial_interpolation<time_interpolation_order_>(\n        make_not_null(&(*result)[j]), make_not_null(&interpolation_error), time,\n        values_span, times_span);\n  }\n}\n\n// Generate instantiations\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data) \\\n  template class SpacetimeInterpolator<DIM(data), FRAME(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Inertial))\n\n#undef INSTANTIATE\n#undef DIM\n#undef FRAME\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "src/IO/Exporter/SpacetimeInterpolator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <shared_mutex>\n#include <string>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace spectre::Exporter {\n\n/*!\n * \\brief Interpolates data in volume files in both space and time\n *\n * This class reads the volume data at multiple time steps into memory and can\n * then interpolate it to any number of target points at any time within the\n * time bounds of the loaded data.\n *\n * \\details The interpolation is first done in space and then in time. For the\n * interpolation in space, the PointwiseInterpolator class is used, which does\n * spectral interpolation within the elements. For the interpolation in time, a\n * polynomial interpolation with fixed order is used (currently third order,\n * meaning four time slices are used).\n *\n * \\par Coordinate frames\n * The target points are given in the `Frame` specified as template parameter\n * (defaults to `Frame::Inertial`). However, the interpolation is done in the\n * grid frame, which is time-independent and therefore best suitable for\n * interpolation in time. Details of this idea can be found in\n * \\cite Bohn:2016afc .\n *\n * \\par Thread safety\n * Loading volume data from H5 files on multiple threads at once is not\n * generally thread safe, so `load_time_bounds` may only be called on a single\n * thread. However, once the data is loaded, the `interpolate_to_point` function\n * is thread safe. This means the volume data can be loaded once in a single\n * thread and then used by multiple threads to interpolate to different points\n * in parallel. Also, `load_time_bounds` can be called again to load new data\n * while other threads are still accessing the old data, as long as they only\n * request data within both the old and the new time bounds.\n */\ntemplate <size_t Dim, typename Frame = ::Frame::Inertial>\nstruct SpacetimeInterpolator {\n  SpacetimeInterpolator() = default;\n  SpacetimeInterpolator(const SpacetimeInterpolator&) = delete;\n  SpacetimeInterpolator& operator=(const SpacetimeInterpolator&) = delete;\n  SpacetimeInterpolator(SpacetimeInterpolator&&);\n  SpacetimeInterpolator& operator=(SpacetimeInterpolator&&);\n  ~SpacetimeInterpolator() = default;\n\n  /*!\n   * \\brief Construct the interpolator without loading any volume data\n   *\n   * \\param volume_files_or_glob The list of H5 files, or a glob pattern\n   * \\param subfile_name The name of the subfile in the H5 files containing the\n   * volume data\n   * \\param tensor_components The tensor components to interpolate. The order of\n   * the components in the vector is the order in which they will be returned.\n   */\n  SpacetimeInterpolator(\n      std::variant<std::vector<std::string>, std::string> volume_files_or_glob,\n      std::string subfile_name, std::vector<std::string> tensor_components);\n\n  /// The maximum time bounds available in the volume data files (taking into\n  /// account the number of ghost slices needed for interpolation). Only time\n  /// bounds in this (inclusive) range can be requested with the\n  /// `load_time_bounds()` function.\n  std::array<double, 2> max_time_bounds() const;\n\n  /*!\n   * \\brief Load volume data into memory such that the given time bounds are\n   * covered.\n   *\n   * The given time bounds must be within the `max_time_bounds()` (inclusive).\n   *\n   * Previously loaded volume data slices outside the given time bounds will be\n   * dropped and new ones loaded.\n   *\n   * \\par Thread safety\n   * This function is not generally thread safe, as it loads data from H5 files.\n   * It should be called from a single thread only. It is safe to keep calling\n   * interpolation routines from other threads while this function is running,\n   * as long as they are requesting data within both the old and the new time\n   * bounds.\n   */\n  void load_time_bounds(std::array<double, 2> time_bounds);\n\n  /// The time bounds of the loaded data (inclusive). It is an error to call the\n  /// interpolation routines outside these bounds.\n  std::array<double, 2> time_bounds() const { return time_bounds_; }\n\n  /// The number of loaded time slices.\n  size_t num_loaded_slices() const { return interpolators_.size(); }\n\n  /*!\n   * \\brief Interpolate to a single point\n   *\n   * This function is thread safe, so the interpolator can be constructed to\n   * load the volume data once and then used by multiple threads to interpolate\n   * to different points in parallel. Note that updating the `block_order` (if\n   * provided) is not thread safe, so each thread should manage a separate\n   * `block_order`.\n   *\n   * \\param result the interpolated data at the target point. The vector is over\n   * the number of components. Will be resized automatically.\n   * \\param target_point the point to interpolate to.\n   * \\param time the time to interpolate to. Must be within the `time_bounds()`.\n   * \\param block_order an optional priority order to search for the block\n   * containing the target point. Will be updated when the point is found.\n   * See `block_logical_coordinates_single_point` for more details.\n   */\n  void interpolate_to_point(gsl::not_null<std::vector<double>*> result,\n                            const tnsr::I<double, Dim, Frame>& target_point,\n                            double time,\n                            std::optional<gsl::not_null<std::vector<size_t>*>>\n                                block_order = std::nullopt) const;\n\n private:\n  std::variant<std::vector<std::string>, std::string> volume_files_or_glob_;\n  std::string subfile_name_;\n  std::vector<std::string> tensor_components_;\n  constexpr static size_t time_interpolation_order_ = 3;  // must be odd\n  constexpr static size_t num_ghost_slices_ =\n      (time_interpolation_order_ + 1) / 2 - 1;\n  domain::FunctionsOfTimeMap functions_of_time_;\n  // Metadata collected from data files\n  std::vector<size_t> all_observation_ids_;\n  std::vector<double> all_observation_values_;\n  // State\n  std::array<double, 2> time_bounds_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  // Loaded from data files\n  std::vector<PointwiseInterpolator<Dim, ::Frame::Grid>> interpolators_{};\n  // Mutex to protect other threads from accessing the `interpolators_` vector\n  // while it is being modified\n  mutable std::shared_mutex interpolators_mutex_;  // NOLINT(spectre-mutable)\n};\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "src/IO/External/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ExternalIO)\n\nset(_LIB_TYPE INTERFACE)\nif (TARGET FUKA::Exporter)\n  set(_LIB_TYPE \"\")\nendif()\n\nadd_spectre_library(${LIBRARY} ${_LIB_TYPE})\n\nif (TARGET SpEC::Exporter)\n  spectre_target_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n    InterpolateFromSpec.hpp\n    )\n  target_link_libraries(\n    ${LIBRARY}\n    INTERFACE\n    DataStructures\n    ErrorHandling\n    SpEC::Exporter\n    Utilities\n    )\n  target_compile_definitions(\n    ${LIBRARY} INTERFACE HAS_SPEC_EXPORTER)\nendif()\n\nif (TARGET FUKA::Exporter)\n  spectre_target_sources(\n    ${LIBRARY}\n    PRIVATE\n    InterpolateFromFuka.cpp\n    )\n  spectre_target_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n    InterpolateFromFuka.hpp\n    )\n  target_link_libraries(\n    ${LIBRARY}\n    PUBLIC\n    DataStructures\n    FUKA::Exporter\n    GeneralRelativity\n    Hydro\n    Utilities\n    PRIVATE\n    ErrorHandling\n    )\n  target_compile_definitions(\n    ${LIBRARY} INTERFACE HAS_FUKA_EXPORTER)\nendif()\n"
  },
  {
    "path": "src/IO/External/InterpolateFromFuka.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/External/InterpolateFromFuka.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <mutex>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nstd::array<std::vector<double>, 16> KadathExportBH(\n    int npoints, double const* xx, double const* yy, double const* zz,\n    char const* fn, double interpolation_offset, int interp_order,\n    double delta_r_rel);\n\nstd::array<std::vector<double>, 16> KadathExportBBH(\n    int npoints, double const* xx, double const* yy, double const* zz,\n    char const* fn, double interpolation_offset, int interp_order,\n    double delta_r_rel);\n\nstd::array<std::vector<double>, 22> KadathExportNS(int npoints,\n                                                   double const* xx,\n                                                   double const* yy,\n                                                   double const* zz,\n                                                   char const* fn);\n\nstd::array<std::vector<double>, 22> KadathExportBNS(int npoints,\n                                                    double const* xx,\n                                                    double const* yy,\n                                                    double const* zz,\n                                                    char const* fn);\n\nstd::array<std::vector<double>, 22> KadathExportBHNS(\n    int npoints, double const* xx, double const* yy, double const* zz,\n    char const* fn, double interpolation_offset, int interp_order,\n    double delta_r_rel);\n\nnamespace io {\n\ntemplate <FukaIdType IdType>\ntuples::tagged_tuple_from_typelist<fuka_tags<IdType>> interpolate_from_fuka(\n    const gsl::not_null<std::mutex*> fuka_lock,\n    const std::string& info_filename,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& x,\n    [[maybe_unused]] const double interpolation_offset,\n    [[maybe_unused]] const int interp_order,\n    [[maybe_unused]] const double delta_r_rel) {\n  tuples::tagged_tuple_from_typelist<fuka_tags<IdType>> result{};\n  // The FUKA functions are not thread-safe, so we need to lock here.\n  fuka_lock->lock();\n  const auto fuka_data = [&x, &info_filename, &interpolation_offset,\n                          &interp_order, &delta_r_rel]() {\n    // FUKA throws FPEs for some reason. Just disabling them seems to work, but\n    // it is unclear what's causing this and if it can be a problem.\n    const ScopedFpeState disable_fpes(false);\n    if constexpr (IdType == FukaIdType::Bh) {\n      return KadathExportBH(static_cast<int>(x.begin()->size()),\n                            get<0>(x).data(), get<1>(x).data(),\n                            get<2>(x).data(), info_filename.c_str(),\n                            interpolation_offset, interp_order, delta_r_rel);\n    } else if constexpr (IdType == FukaIdType::Bbh) {\n      return KadathExportBBH(static_cast<int>(x.begin()->size()),\n                             get<0>(x).data(), get<1>(x).data(),\n                             get<2>(x).data(), info_filename.c_str(),\n                             interpolation_offset, interp_order, delta_r_rel);\n    } else if constexpr (IdType == FukaIdType::Ns) {\n      (void)interpolation_offset;\n      (void)interp_order;\n      (void)delta_r_rel;\n      return KadathExportNS(static_cast<int>(x.begin()->size()),\n                            get<0>(x).data(), get<1>(x).data(),\n                            get<2>(x).data(), info_filename.c_str());\n    } else if constexpr (IdType == FukaIdType::Bns) {\n      (void)interpolation_offset;\n      (void)interp_order;\n      (void)delta_r_rel;\n      return KadathExportBNS(static_cast<int>(x.begin()->size()),\n                             get<0>(x).data(), get<1>(x).data(),\n                             get<2>(x).data(), info_filename.c_str());\n    } else if constexpr (IdType == FukaIdType::Bhns) {\n      return KadathExportBHNS(static_cast<int>(x.begin()->size()),\n                              get<0>(x).data(), get<1>(x).data(),\n                              get<2>(x).data(), info_filename.c_str(),\n                              interpolation_offset, interp_order, delta_r_rel);\n    } else {\n      ERROR(\"Unrecognized enum value for 'FukaIdType'\");\n    }\n  }();\n  // Unlock before copying data to avoid holding the lock for too long.\n  fuka_lock->unlock();\n  // The FUKA functions return tensor components in this order.\n  // See: https://bitbucket.org/fukaws/kadathimporter/src/master/src/importer.h\n  auto& lapse = get<gr::Tags::Lapse<DataVector>>(result);\n  get(lapse) = DataVector(fuka_data[0]);\n  auto& shift = get<gr::Tags::Shift<DataVector, 3>>(result);\n  get<0>(shift) = DataVector(fuka_data[1]);\n  get<1>(shift) = DataVector(fuka_data[2]);\n  get<2>(shift) = DataVector(fuka_data[3]);\n  auto& spatial_metric = get<gr::Tags::SpatialMetric<DataVector, 3>>(result);\n  get<0, 0>(spatial_metric) = DataVector(fuka_data[4]);\n  get<0, 1>(spatial_metric) = DataVector(fuka_data[5]);\n  get<0, 2>(spatial_metric) = DataVector(fuka_data[6]);\n  get<1, 1>(spatial_metric) = DataVector(fuka_data[7]);\n  get<1, 2>(spatial_metric) = DataVector(fuka_data[8]);\n  get<2, 2>(spatial_metric) = DataVector(fuka_data[9]);\n  auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(result);\n  get<0, 0>(extrinsic_curvature) = DataVector(fuka_data[10]);\n  get<0, 1>(extrinsic_curvature) = DataVector(fuka_data[11]);\n  get<0, 2>(extrinsic_curvature) = DataVector(fuka_data[12]);\n  get<1, 1>(extrinsic_curvature) = DataVector(fuka_data[13]);\n  get<1, 2>(extrinsic_curvature) = DataVector(fuka_data[14]);\n  get<2, 2>(extrinsic_curvature) = DataVector(fuka_data[15]);\n  if constexpr (IdType == FukaIdType::Ns or IdType == FukaIdType::Bns or\n                IdType == FukaIdType::Bhns) {\n    auto& rest_mass_density =\n        get<hydro::Tags::RestMassDensity<DataVector>>(result);\n    get(rest_mass_density) = DataVector(fuka_data[16]);\n    auto& specific_internal_energy =\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(result);\n    get(specific_internal_energy) = DataVector(fuka_data[17]);\n    auto& pressure = get<hydro::Tags::Pressure<DataVector>>(result);\n    get(pressure) = DataVector(fuka_data[18]);\n    auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(result);\n    get<0>(spatial_velocity) = DataVector(fuka_data[19]);\n    get<1>(spatial_velocity) = DataVector(fuka_data[20]);\n    get<2>(spatial_velocity) = DataVector(fuka_data[21]);\n  }\n  return result;\n}\n\n#define ID_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                \\\n  template tuples::tagged_tuple_from_typelist<fuka_tags<ID_TYPE(data)>>       \\\n  interpolate_from_fuka<ID_TYPE(data)>(                                       \\\n      gsl::not_null<std::mutex*> fuka_lock, const std::string& info_filename, \\\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x,                       \\\n      double interpolation_offset, int interp_order, double delta_r_rel);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION,\n                        (FukaIdType::Bh, FukaIdType::Bbh, FukaIdType::Ns,\n                         FukaIdType::Bns, FukaIdType::Bhns))\n\n#undef INSTANTIATION\n#undef ID_TYPE\n}  // namespace io\n"
  },
  {
    "path": "src/IO/External/InterpolateFromFuka.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace io {\n\n/// Type of FUKA initial data\nenum class FukaIdType { Bh, Bbh, Ns, Bns, Bhns };\n\nnamespace detail {\nusing fuka_gr_tags =\n    tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n               gr::Tags::SpatialMetric<DataVector, 3>,\n               gr::Tags::ExtrinsicCurvature<DataVector, 3>>;\nusing fuka_hydro_tags =\n    tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n               hydro::Tags::SpecificInternalEnergy<DataVector>,\n               hydro::Tags::Pressure<DataVector>,\n               hydro::Tags::SpatialVelocity<DataVector, 3>>;\ntemplate <FukaIdType IdType>\nstruct FukaTags {\n  using type =\n      tmpl::conditional_t<IdType <= FukaIdType::Bh or IdType == FukaIdType::Bbh,\n                          fuka_gr_tags,\n                          tmpl::append<fuka_gr_tags, fuka_hydro_tags>>;\n};\n}  // namespace detail\n\n/// List of tags supplied by FUKA initial data\ntemplate <FukaIdType IdType>\nusing fuka_tags = typename detail::FukaTags<IdType>::type;\n\n/*!\n * \\brief Interpolate numerical FUKA initial data to arbitrary points\n *\n * \\tparam IdType Type of FUKA initial data\n * \\param fuka_lock Lock for accessing FUKA data. This is needed because\n * FUKA is not thread-safe. Pass in a lock that is shared with other\n * threads that are also calling this function.\n * \\param info_filename Path to the FUKA info file to load\n * \\param x Coordinates of points to interpolate to\n * \\param interpolation_offset See FUKA documentation for export functions\n * \\param interp_order See FUKA documentation for export functions\n * \\param delta_r_rel See FUKA documentation for export functions\n * \\return Data interpolated to the given points\n */\ntemplate <FukaIdType IdType>\ntuples::tagged_tuple_from_typelist<fuka_tags<IdType>> interpolate_from_fuka(\n    gsl::not_null<std::mutex*> fuka_lock, const std::string& info_filename,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& x,\n    double interpolation_offset = 0., int interp_order = 8,\n    double delta_r_rel = 0.3);\n\n}  // namespace io\n"
  },
  {
    "path": "src/IO/External/InterpolateFromSpec.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <Exporter.hpp>  // The SpEC Exporter\n#include <cstddef>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace io {\n\n/*!\n * \\brief Interpolate numerical SpEC initial data to arbitrary points\n *\n * \\tparam Tags List of tags to load. The tags must correspond exactly to the\n * list of variables that the `spec_exporter` was configured with. This function\n * does not support interpolating only a subset of the variables. This is a\n * limitation of the `spec::Exporter`.\n * \\tparam DataType `double` or `DataVector`.\n * \\tparam CoordFrame The frame of the coordinates `x`. These coordinates are\n * always assumed to be in SpEC's \"grid\" frame.\n *\n * \\param spec_exporter Has the numerical data already loaded. It must be\n * configured with the same number of variables that were passed as `Tags`, and\n * in the same order.\n * \\param x Interpolate to these coordinates. They are assumed to be in SpEC's\n * \"grid\" frame.\n * \\param which_interpolator Index of the interpolator. See `spec::Exporter`\n * documentation for details.\n */\ntemplate <typename Tags, typename DataType, typename CoordFrame>\ntuples::tagged_tuple_from_typelist<Tags> interpolate_from_spec(\n    const gsl::not_null<spec::Exporter*> spec_exporter,\n    const tnsr::I<DataType, 3, CoordFrame>& x,\n    const size_t which_interpolator) {\n  // The `spec::Exporter` doesn't currently expose its `vars_to_interpolate`.\n  // Once it does, we can assert the number of variables here.\n  // const auto& dataset_names = spec_exporter.vars_to_interpolate();\n  // ASSERT(tmpl::size<Tags>::value == dataset_names.size(),\n  //        \"Mismatch between number of tags and dataset names. The SpEC\n  //        exporter \" \"was configured with \" +\n  //            std::to_string(dataset_names.size()) +\n  //            \" variables to interpolate, but requested \" +\n  //            std::to_string(tmpl::size<Tags>::value) + \" tags.\");\n  // Transform coordinates into SpEC's expected format. SpEC expects grid\n  // coordinates. We assume that the grid coordinates coincide with inertial\n  // coordinates for the initial data.\n  const size_t num_points = get_size(get<0>(x));\n  std::vector<std::vector<double>> spec_grid_coords(num_points);\n  for (size_t i = 0; i < num_points; ++i) {\n    spec_grid_coords[i] = std::vector<double>{get_element(get<0>(x), i),\n                                              get_element(get<1>(x), i),\n                                              get_element(get<2>(x), i)};\n  }\n  // Allocate memory for the interpolation and point into it\n  tuples::tagged_tuple_from_typelist<Tags> interpolation_buffer{};\n  std::vector<std::vector<double*>> buffer_pointers(tmpl::size<Tags>::value);\n  size_t var_i = 0;\n  tmpl::for_each<Tags>([&interpolation_buffer, &buffer_pointers, &num_points,\n                        &var_i](const auto tag_v) {\n    using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n    auto& tensor = get<tag>(interpolation_buffer);\n    set_number_of_grid_points(make_not_null(&tensor), num_points);\n    const size_t num_components = tensor.size();\n    buffer_pointers[var_i].resize(num_components);\n    // The SpEC exporter supports tensors up to symmetric rank 2, which are\n    // ordered xx, yx, zx, yy, zy, zz. Because this is also the order in\n    // which we store components in the Tensor class, we don't have to do\n    // anything special here.\n    // WARNING: If the Tensor storage order changes for some reason, this\n    // code needs to be updated.\n    for (size_t component_i = 0; component_i < num_components; ++component_i) {\n#ifdef SPECTRE_DEBUG\n      const auto component_name =\n          tensor.component_name(tensor.get_tensor_index(component_i));\n      if constexpr (std::decay_t<decltype(tensor)>::rank() == 1) {\n        const std::array<std::string, 3> spec_component_order{{\"x\", \"y\", \"z\"}};\n        ASSERT(component_name == gsl::at(spec_component_order, component_i),\n               \"Unexpected Tensor component order. Expected \"\n                   << spec_component_order);\n      } else if constexpr (std::decay_t<decltype(tensor)>::rank() == 2 and\n                           std::decay_t<decltype(tensor)>::size() == 6) {\n        const std::array<std::string, 6> spec_component_order{\n            {\"xx\", \"yx\", \"zx\", \"yy\", \"zy\", \"zz\"}};\n        ASSERT(component_name == gsl::at(spec_component_order, component_i),\n               \"Unexpected Tensor component order. Expected \"\n                   << spec_component_order);\n      } else if constexpr (std::decay_t<decltype(tensor)>::rank() > 0) {\n        ASSERT(\n            false,\n            \"Unsupported Tensor type for SpEC import. Only scalars, \"\n            \"vectors, and symmetric rank-2 tensors are currently supported.\");\n      }\n#endif\n      auto& component = tensor[component_i];\n      auto& component_pointer = buffer_pointers[var_i][component_i];\n      if constexpr (std::is_same_v<DataType, double>) {\n        component_pointer = &component;\n      } else {\n        component_pointer = component.data();\n      }\n    }\n    ++var_i;\n  });\n  // Interpolate!\n  spec_exporter->interpolate(buffer_pointers, spec_grid_coords,\n                             which_interpolator);\n  return interpolation_buffer;\n}\n\n}  // namespace io\n"
  },
  {
    "path": "src/IO/H5/AccessType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/AccessType.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace h5 {\nstd::ostream& operator<<(std::ostream& os, const AccessType t) {\n  switch (t) {\n    case AccessType::ReadOnly:\n      return os << \"ReadOnly\";\n    case AccessType::ReadWrite:\n      return os << \"ReadWrite\";\n    default:\n      ERROR(\"Unknown h5::AccessType. Known values are ReadWrite and ReadOnly.\");\n  }\n}\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/AccessType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines enum for specifying whether the H5 file is ReadWrite or ReadOnly\n\n#pragma once\n\n#include <iosfwd>\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Contains functions and classes for manipulating HDF5 files\n *\n * Wraps many underlying C H5 routines making them easier to use and easier to\n * manipulate H5 files.\n */\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Set the access type to the H5File\n */\nenum class AccessType {\n  /// Allow read-write access to the file\n  ReadWrite,\n  /// Allow only read access to the file\n  ReadOnly\n};\n\nstd::ostream& operator<<(std::ostream& os, AccessType t);\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY H5)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AccessType.cpp\n  Cce.cpp\n  CheckH5PropertiesMatch.cpp\n  CombineH5.cpp\n  Dat.cpp\n  EosTable.cpp\n  ExtendConnectivityHelpers.cpp\n  File.cpp\n  Header.cpp\n  Helpers.cpp\n  OpenGroup.cpp\n  SourceArchive.cpp\n  SpectralIo.cpp\n  StellarCollapseEos.cpp\n  TensorData.cpp\n  Version.cpp\n  VolumeData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AccessType.hpp\n  Cce.hpp\n  CheckH5.hpp\n  CheckH5PropertiesMatch.hpp\n  CombineH5.hpp\n  Dat.hpp\n  EosTable.hpp\n  ExtendConnectivityHelpers.hpp\n  File.hpp\n  Header.hpp\n  Helpers.hpp\n  Object.hpp\n  OpenGroup.hpp\n  SourceArchive.hpp\n  SpectralIo.hpp\n  StellarCollapseEos.hpp\n  TensorData.hpp\n  Type.hpp\n  Version.hpp\n  VolumeData.hpp\n  Wrappers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  Domain\n  DomainStructure\n  ErrorHandling\n  HDF5::HDF5\n  Serialization\n  Spectral\n  Utilities\n  PRIVATE\n  Informer\n  IO\n  Printf\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/IO/H5/Cce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Cce.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <hdf5.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace h5 {\nCce::Cce(const bool exists, detail::OpenGroup&& group, const hid_t /*location*/,\n         const std::string& name, const size_t l_max, const uint32_t version)\n    : group_(std::move(group)),\n      name_(extension() == name.substr(name.size() > extension().size()\n                                           ? name.size() - extension().size()\n                                           : 0)\n                ? name\n                : name + extension()),\n      path_(group_.group_path_with_trailing_slash() + name),\n      version_(version),\n      l_max_(l_max),\n      legend_([this]() {\n        std::vector<std::string> legend;\n        legend.reserve(2 * square(l_max_ + 1) + 1);\n        legend.emplace_back(\"time\");\n        for (int i = 0; i <= static_cast<int>(l_max_); ++i) {\n          for (int j = -i; j <= i; ++j) {\n            legend.push_back(MakeString{} << \"Real Y_\" << i << \",\" << j);\n            legend.push_back(MakeString{} << \"Imag Y_\" << i << \",\" << j);\n          }\n        }\n        return legend;\n      }()),\n      cce_group_(group_.id(), name_, h5::AccessType::ReadWrite) {\n  for (const std::string& bondi_var : bondi_variables_) {\n    // We will set the id below\n    bondi_datasets_[bondi_var] = DataSet{-1, {0, legend_.size()}};\n  }\n\n  if (exists) {\n    {\n      // We treat this as an internal version for now. We'll need to deal with\n      // proper versioning later.\n\n      // Check if the version exists before calling the open_version.\n      const htri_t version_exists = H5Aexists(cce_group_.id(), \"version.ver\");\n      if (version_exists != 0) {\n        const Version open_version(true, detail::OpenGroup{}, cce_group_.id(),\n                                   \"version\");\n        version_ = open_version.get_version();\n      }\n    }\n    {\n      const htri_t header_exists = H5Aexists(cce_group_.id(), \"header.hdr\");\n      if (header_exists != 0) {\n        const Header header(true, detail::OpenGroup{}, cce_group_.id(),\n                            \"header\");\n        header_ = header.get_header();\n      }\n    }\n\n    for (const std::string& bondi_var : bondi_variables_) {\n      DataSet& dataset = bondi_datasets_.at(bondi_var);\n      dataset.id =\n          H5Dopen2(cce_group_.id(), bondi_var.c_str(), h5::h5p_default());\n      CHECK_H5(dataset.id, \"Failed to open dataset\");\n\n      hid_t space_id = H5Dget_space(dataset.id);\n      std::array<hsize_t, 2> max_dims{};\n      if (2 != H5Sget_simple_extent_dims(space_id, dataset.size.data(),\n                                         max_dims.data())) {\n        ERROR(\"Invalid number of dimensions (\" << max_dims << \") in cce file \"\n                                               << bondi_var << \" on disk.\");\n      }\n      CHECK_H5(H5Sclose(space_id), \"Failed to close dataspace\");\n\n      if (legend_ != read_rank1_attribute<std::string>(dataset.id, \"Legend\"s)) {\n        const size_t file_l_max_plus_1 = static_cast<size_t>(\n            sqrt(static_cast<double>(\n                     read_rank1_attribute<std::string>(dataset.id, \"Legend\"s)\n                         .size() -\n                     1_st) /\n                 2.0));\n        ERROR(\"l_max (\" << (file_l_max_plus_1 > 0 ? file_l_max_plus_1 - 1 : 0)\n                        << \") from cce file \" << bondi_var\n                        << \" does not match l_max (\" << l_max_\n                        << \") in constructor\");\n      }\n    }\n  } else {  // file does not exist\n    {\n      Version open_version(false, detail::OpenGroup{}, cce_group_.id(),\n                           \"version\", version_);\n    }\n    {\n      Header header(false, detail::OpenGroup{}, cce_group_.id(), \"header\");\n      header_ = header.get_header();\n    }\n    // So this file is compatible with the sxs/scri python packages offered by\n    // the SXS collaboration\n    write_to_attribute(cce_group_.id(), sxs_format_str_, sxs_version_str_);\n\n    for (const std::string& bondi_var : bondi_variables_) {\n      DataSet& dataset = bondi_datasets_.at(bondi_var);\n      dataset.id = h5::detail::create_extensible_dataset(\n          cce_group_.id(), bondi_var, dataset.size,\n          std::array<hsize_t, 2>{{4, legend_.size()}},\n          {{h5s_unlimited(), legend_.size()}});\n      CHECK_H5(dataset.id, \"Failed to create dataset\");\n\n      write_to_attribute(dataset.id, \"Legend\"s, legend_);\n      // So this dataset is compatible with the sxs/scri python packages offered\n      // by the SXS collaboration\n      write_to_attribute(dataset.id, sxs_format_str_, sxs_version_str_);\n    }\n  }\n}\n\nCce::~Cce() {\n#if defined(__GNUC__) and not defined(__clang__)\n// The pragma here is used to suppress the warning that the compile time branch\n// of `ERROR` will always call `terminate()` because it throws an error.\n// Throwing that error is a code path that will never actually be entered at\n// runtime, so we suppress the warning here.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wterminate\"\n#endif\n  // We don't use structured bindings because there was a bug that was fixed in\n  // C++20 about capturing structured bindings in a lambda (the lambda is in the\n  // internals of CHECK_H5), so older compilers that we support may not have\n  // fixed this bug.\n  for (const auto& name_and_dataset : bondi_datasets_) {\n#ifdef __CUDACC__\n    // nvcc warns that 'name' is unused\n    [[maybe_unused]]\n#endif\n    const auto& name = name_and_dataset.first;\n    const auto& dataset = name_and_dataset.second;\n    CHECK_H5(H5Dclose(dataset.id), \"Failed to close dataset \" << name);\n  }\n#if defined(__GNUC__) and not defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n}\n\nvoid Cce::append(\n    const std::unordered_map<std::string, std::vector<double>>& data) {\n  for (const std::string& bondi_var : bondi_variables_) {\n    if (not data.contains(bondi_var)) {\n      ERROR(\"Passed in data does not contain the bondi variable \" << bondi_var);\n    }\n    DataSet& dataset = bondi_datasets_.at(bondi_var);\n\n    const std::vector<double>& vec_data = data.at(bondi_var);\n    if (vec_data.size() != dataset.size[1]) {\n      ERROR(\"Cannot add columns to Cce files. Current number of columns is \"\n            << dataset.size[1] << \" but received \" << vec_data.size()\n            << \" entries.\");\n    }\n\n    dataset.size =\n        h5::append_to_dataset(dataset.id, bondi_var, vec_data, 1, dataset.size);\n  }\n}\n\ntemplate <typename T>\nstd::unordered_map<std::string, T> Cce::get_data() const {\n  std::unordered_map<std::string, T> result{};\n\n  for (const std::string& bondi_var : bondi_variables_) {\n    const DataSet& dataset = bondi_datasets_.at(bondi_var);\n    result[bondi_var] = h5::retrieve_dataset<T>(dataset.id, dataset.size);\n  }\n\n  return result;\n}\n\ntemplate <typename T>\nT Cce::get_data(const std::string& bondi_variable_name) const {\n  check_bondi_variable(bondi_variable_name);\n\n  const DataSet& dataset = bondi_datasets_.at(bondi_variable_name);\n  return h5::retrieve_dataset<T>(dataset.id, dataset.size);\n}\n\ntemplate <typename T>\nstd::unordered_map<std::string, T> Cce::get_data_subset(\n    const std::vector<size_t>& these_ell, const size_t first_row,\n    const size_t num_rows) const {\n  std::unordered_map<std::string, T> result{};\n\n  for (const std::string& bondi_var : bondi_variables_) {\n    result[bondi_var] =\n        get_data_subset<T>(bondi_var, these_ell, first_row, num_rows);\n  }\n\n  return result;\n}\n\ntemplate <typename T>\nT Cce::get_data_subset(const std::string& bondi_variable_name,\n                       const std::vector<size_t>& these_ell,\n                       const size_t first_row, const size_t num_rows) const {\n  check_bondi_variable(bondi_variable_name);\n\n  if (alg::any_of(these_ell,\n                  [this](const size_t ell) { return ell > l_max_; })) {\n    ERROR(\"One (or more) of the requested ells \"\n          << these_ell << \" is larger than the l_max \" << l_max_);\n  }\n\n  if (these_ell.empty()) {\n    if constexpr (std::is_same_v<T, Matrix>) {\n      return {num_rows, 0, 0.0};\n    } else {\n      return std::vector<std::vector<double>>(num_rows);\n    }\n  }\n\n  // Always grab the time\n  std::vector<size_t> these_columns{0};\n\n  // For a given ell, we nedd the first column index and the last column index.\n  // For a given ell, the total number of coefs is given by\n  // f(l) = 2 * (l + 1)^2 (first factor of 2 comes from having both real and\n  // imag components). Therefore, the first column index is just f(ell - 1) + 1,\n  // and the last column index is just f(ell). The +1 is because we have a\n  // time column\n  for (size_t ell : these_ell) {\n    if (ell == 0_st) {\n      these_columns.emplace_back(1);\n      these_columns.emplace_back(2);\n    } else {\n      const size_t first_col = 2 * square(ell) + 1;\n      const size_t last_col = 2 * square(ell + 1);\n      for (size_t col = first_col; col <= last_col; col++) {\n        these_columns.emplace_back(col);\n      }\n    }\n  }\n\n  if (num_rows == 0) {\n    if constexpr (std::is_same_v<T, Matrix>) {\n      return {0, these_columns.size(), 0.0};\n    } else {\n      return std::vector<std::vector<double>>{};\n    }\n  }\n\n  const DataSet& dataset = bondi_datasets_.at(bondi_variable_name);\n  return h5::retrieve_dataset_subset<T>(dataset.id, these_columns, first_row,\n                                        num_rows, dataset.size);\n}\n\nvoid Cce::check_bondi_variable(const std::string& bondi_variable_name) const {\n  if (not bondi_variables_.contains(bondi_variable_name)) {\n    ERROR(\"Requested bondi variable \" << bondi_variable_name\n                                      << \" not available\");\n  }\n}\n\nconst std::array<hsize_t, 2>& Cce::get_dimensions(\n    const std::string& bondi_variable_name) const {\n  check_bondi_variable(bondi_variable_name);\n\n  return bondi_datasets_.at(bondi_variable_name).size;\n}\n\n#define TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template std::unordered_map<std::string, TYPE(data)> Cce::get_data() const; \\\n  template TYPE(data) Cce::get_data(const std::string& bondi_variable_name)   \\\n      const;                                                                  \\\n  template std::unordered_map<std::string, TYPE(data)> Cce::get_data_subset(  \\\n      const std::vector<size_t>& these_ell, const size_t first_row,           \\\n      const size_t num_rows) const;                                           \\\n  template TYPE(data) Cce::get_data_subset(                                   \\\n      const std::string& bondi_variable_name,                                 \\\n      const std::vector<size_t>& these_ell, const size_t first_row,           \\\n      const size_t num_rows) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Matrix, std::vector<std::vector<double>>))\n\n#undef INSTANTIATE\n#undef TYPE\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Cce.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class h5::Cce\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <hdf5.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\n/// \\cond\nclass Matrix;\n/// \\endcond\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Represents Cauchy-Characteristic Extraction (CCE) bondi variables\n * inside of an HDF5 file.\n *\n * Within a Cce object, there are several datasets that correspond to the\n * `Cce::scri_plus_interpolation_set` bondi variables at future null infinity\n * represented as complex spherical harmonic coefficients. The names of these\n * bondi variables are\n *\n * - `EthInertialRetardedTime`\n * - `News`\n * - `Psi0`\n * - `Psi1`\n * - `Psi2`\n * - `Psi3`\n * - `Psi4`\n * - `Strain`\n *\n * Each dataset has a couple H5 attributes:\n *\n * - `Legend` which is determined by the `l_max` massed to the constructor.\n * - `sxs_format` which is a string. The value is \"SpECTRE_CCE_v1\"\n *\n * The Cce object itself also has the `sxs_format` attribute with the same\n * value, along with a `version` and `header` attribute.\n *\n * The columns of data are stored in each dataset in the following order:\n *\n * - `time`\n * - `Real Y_0,0`\n * - `Imag Y_0,0`\n * - `Real Y_1,-1`\n * - `Imag Y_1,-1`\n * - `Real Y_1,0`\n * - `Imag Y_1,0`\n * - `Real Y_1,1`\n * - `Imag Y_1,1`\n * - ...\n *\n * and so on until you reach the coefficients for `l_max`.\n *\n * \\note This class does not do any caching of data so all data is written as\n * soon as append() is called.\n */\nclass Cce : public h5::Object {\n  struct DataSet {\n    hid_t id;\n    std::array<hsize_t, 2> size;\n  };\n\n public:\n  /// \\cond HIDDEN_SYMBOLS\n  static std::string extension() { return \".cce\"; }\n\n  Cce(bool exists, detail::OpenGroup&& group, hid_t location,\n      const std::string& name, size_t l_max, uint32_t version = 1);\n\n  Cce(const Cce& /*rhs*/) = delete;\n  Cce& operator=(const Cce& /*rhs*/) = delete;\n  Cce(Cce&& /*rhs*/) = delete;             // NOLINT\n  Cce& operator=(Cce&& /*rhs*/) = delete;  // NOLINT\n\n  ~Cce() override;\n  /// \\endcond HIDDEN_SYMBOLS\n\n  /*!\n   * \\brief For each bondi variable name, appends the \\p data to the dataset in\n   * the H5 file.\n   *\n   * \\details The `data.at(name).size()` must be the same as the number of\n   * columns already in the dataset. Also, all bondi variable names listed in\n   * the constructor of `h5::Cce` must be present in the \\p data.\n   */\n  void append(const std::unordered_map<std::string, std::vector<double>>& data);\n\n  /// @{\n  /*!\n   * \\brief Return all currently stored data in the `h5::Cce` file in the form\n   * of a `Matrix` or a `std::vector<std::vector<double>>` for each bondi\n   * variable\n   */\n  template <typename T = Matrix>\n  std::unordered_map<std::string, T> get_data() const;\n\n  template <typename T = Matrix>\n  T get_data(const std::string& bondi_variable_name) const;\n  /// @}\n\n  /// @{\n  /*!\n   * \\brief Get only some values of $\\ell$ over a range of rows\n   *\n   * \\details The `time` column is always returned. The coefficients will be\n   * returned in the order that you requested them in. No sorting is done\n   * internally. All requested \\p these_ell must be less that or equal to the \\p\n   * l_max that this file was constructed with. Also both the first and last row\n   * requested must be less than or equal to the total number of rows.\n   */\n  template <typename T = Matrix>\n  std::unordered_map<std::string, T> get_data_subset(\n      const std::vector<size_t>& these_ell, size_t first_row = 0,\n      size_t num_rows = 1) const;\n\n  template <typename T = Matrix>\n  T get_data_subset(const std::string& bondi_variable_name,\n                    const std::vector<size_t>& these_ell, size_t first_row = 0,\n                    size_t num_rows = 1) const;\n  /// @}\n\n  /*!\n   * \\brief Return the legend. All bondi variables have the same legend.\n   */\n  const std::vector<std::string> get_legend() const { return legend_; }\n\n  /*!\n   * \\brief Return the number of rows (first index) and columns (second index)\n   * of the \\p bondi_variable_name dataset. All bondi variables will have the\n   * same dimensions.\n   */\n  const std::array<hsize_t, 2>& get_dimensions(\n      const std::string& bondi_variable_name) const;\n\n  /*!\n   * \\brief The header of the Cce file\n   */\n  const std::string& get_header() const { return header_; }\n\n  /*!\n   * \\brief The user-specified version number of the Cce file\n   *\n   * \\note h5::Version returns a uint32_t, so we return one here too for the\n   * version\n   */\n  uint32_t get_version() const { return version_; }\n\n  /*!\n   * \\brief Path to this Cce file.\n   */\n  const std::string& subfile_path() const override { return path_; }\n\n private:\n  void check_bondi_variable(const std::string& bondi_variable_name) const;\n  /// \\cond HIDDEN_SYMBOLS\n  detail::OpenGroup group_;\n  std::string name_;\n  std::string path_;\n  uint32_t version_;\n  size_t l_max_;\n  std::vector<std::string> legend_{};\n  detail::OpenGroup cce_group_{};\n  std::string header_;\n  std::unordered_map<std::string, DataSet> bondi_datasets_;\n  std::unordered_set<std::string> bondi_variables_{\"EthInertialRetardedTime\",\n                                                   \"News\",\n                                                   \"Psi0\",\n                                                   \"Psi1\",\n                                                   \"Psi2\",\n                                                   \"Psi3\",\n                                                   \"Psi4\",\n                                                   \"Strain\"};\n  std::string sxs_format_str_{\"sxs_format\"};\n  std::string sxs_version_str_{\"SpECTRE_CCE_v1\"};\n  /// \\endcond HIDDEN_SYMBOLS\n};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/CheckH5.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines macro CHECK_H5\n\n#pragma once\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Check if an HDF5 operation was successful\n *\n * \\requires `h5_status` is a valid HDF5 return type, `m` is a stringstream\n * syntax error message.\n *\n * \\effects if `h5_status` is set to error then aborts execution else none\n */\n#define CHECK_H5(h5_status, m)                        \\\n  if (h5_status < 0) {                     /*NOLINT*/ \\\n    ERROR(\"Failed HDF5 operation: \" << m); /*NOLINT*/ \\\n  } else                                              \\\n    static_cast<void>(0)\n"
  },
  {
    "path": "src/IO/H5/CheckH5PropertiesMatch.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/CheckH5PropertiesMatch.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n\nnamespace h5 {\nbool check_src_files_match(const std::vector<std::string>& input_filenames) {\n  // Holds source archive data of first file\n  std::vector<char> src_tar_initial;\n\n  // Braces to specify scope for H5 file\n  {\n    // Reads the 0th volume file to compare against\n    const h5::H5File<h5::AccessType::ReadOnly> initial_file(input_filenames[0],\n                                                            false);\n\n    // Obtains the source archive of the 0th volume file to compare against\n    const auto& src_archive_object_initial =\n        initial_file.get<h5::SourceArchive>(\"/src\");\n    src_tar_initial = src_archive_object_initial.get_archive();\n  }  // End of scope for H5 file\n\n  // Iterates through each of the other files and compares source archives\n  for (size_t i = 1; i < input_filenames.size(); ++i) {\n    // Reads the i-th volume file to compare with\n    const h5::H5File<h5::AccessType::ReadOnly> comparison_file(\n        input_filenames[i], false);\n\n    // Obtains the source archive of the i-th volume file to compare with\n    const auto& src_archive_object_compare =\n        comparison_file.get<h5::SourceArchive>(\"/src\");\n    const std::vector<char>& src_tar_compare =\n        src_archive_object_compare.get_archive();\n\n    // Returns false if the source archive data does not match\n    if (src_tar_initial != src_tar_compare) {\n      return false;\n    }\n  }\n  return true;\n}\n\nbool check_observation_ids_match(\n    const std::vector<std::string>& input_filenames,\n    const std::string& subfile_name) {\n  // Holds observation id data of first file\n  std::vector<size_t> obs_id_initial;\n\n  // Braces to specify scope for H5 file\n  {\n    // Reads the 0th volume file to compare against\n    const h5::H5File<h5::AccessType::ReadOnly> initial_file(input_filenames[0],\n                                                            false);\n\n    // Obtains the list of observation ids of the 0th volume file to compare\n    // against\n    const auto& volume_data_initial =\n        initial_file.get<h5::VolumeData>(\"/\" + subfile_name);\n    obs_id_initial = volume_data_initial.list_observation_ids();\n  }  // End of scope for H5 file\n\n  // Iterates through each of the other files and compares observation id lists\n  for (size_t i = 1; i < input_filenames.size(); ++i) {\n    // Reads the i-th volume file to compare against\n    const h5::H5File<h5::AccessType::ReadOnly> comparison_file(\n        input_filenames[i], false);\n\n    // Obtains the observation id list of the i-th volume file to compare with\n    const auto& volume_data_compare =\n        comparison_file.get<h5::VolumeData>(\"/\" + subfile_name);\n    const std::vector<size_t>& obs_id_compare =\n        volume_data_compare.list_observation_ids();\n\n    // Returns false if an observation id does not match\n    if (obs_id_initial != obs_id_compare) {\n      return false;\n    }\n  }\n  return true;\n}\n\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/CheckH5PropertiesMatch.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <vector>\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Check if all files within `input_filenames` have the same source\n * archive.\n */\nbool check_src_files_match(const std::vector<std::string>& input_filenames);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Check if all files within `input_filenames` with volume subfile\n * `subfile_name` have the same set of observation ids.\n */\nbool check_observation_ids_match(\n    const std::vector<std::string>& input_filenames,\n    const std::string& subfile_name);\n\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/CombineH5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/CombineH5.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdlib>\n#include <iterator>\n#include <limits>\n#include <optional>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/BlockGroups.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5PropertiesMatch.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\n// Returns all the observation_ids stored in the volume files. Assumes all\n// volume files have the same observation ids\nstd::vector<std::pair<size_t, double>> get_observation_ids(\n    const std::vector<std::string>& file_names,\n    const std::string& subfile_name) {\n  const h5::H5File<h5::AccessType::ReadOnly> initial_file(file_names[0], false);\n  const auto& initial_volume_file =\n      initial_file.get<h5::VolumeData>(subfile_name);\n  const std::vector<size_t> observation_ids =\n      initial_volume_file.list_observation_ids();\n  std::vector<std::pair<size_t, double>> observation_ids_and_values(\n      observation_ids.size());\n  for (size_t i = 0; i < observation_ids.size(); ++i) {\n    observation_ids_and_values[i] = std::pair{\n        observation_ids[i],\n        initial_volume_file.get_observation_value(observation_ids[i])};\n  }\n  // Sort by the observation value\n  alg::sort(observation_ids_and_values,\n            [](const std::pair<size_t, double>& id_and_value_a,\n               const std::pair<size_t, double>& id_and_value_b) {\n              return id_and_value_a.second < id_and_value_b.second;\n            });\n  return observation_ids_and_values;\n}\n\n// Returns total number of elements for an observation id across all volume data\n// files\nsize_t get_number_of_elements(const std::vector<std::string>& input_filenames,\n                              const std::string& subfile_name,\n                              const size_t& observation_id) {\n  size_t total_elements = 0;\n  for (const auto& input_filename : input_filenames) {\n    const h5::H5File<h5::AccessType::ReadOnly> original_file(input_filename,\n                                                             false);\n    const auto& original_volume_file =\n        original_file.get<h5::VolumeData>(subfile_name);\n    total_elements += original_volume_file.get_extents(observation_id).size();\n  }\n  return total_elements;\n}\n\nstd::optional<std::vector<size_t>> get_block_numbers_to_use(\n    const std::string& file_name, const std::string& subfile_name,\n    const std::optional<std::vector<std::string>>& blocks_to_combine) {\n  if (not blocks_to_combine.has_value() or blocks_to_combine.value().empty()) {\n    return std::nullopt;\n  }\n\n  const h5::H5File<h5::AccessType::ReadOnly> original_file(file_name, false);\n  const auto& volume_file = original_file.get<h5::VolumeData>(subfile_name);\n\n  const auto dim = volume_file.get_dimension();\n  auto serialized_domain = volume_file.get_domain();\n  if (not serialized_domain.has_value()) {\n    ERROR(\"Could not read the domain the from file \"\n          << file_name << \" and subfile \" << subfile_name\n          << \". This means we cannot filter based on block names. You can \"\n             \"still combine the files but will need to use all blocks.\");\n  }\n  switch (dim) {\n    case 1: {\n      const auto domain =\n          deserialize<Domain<1>>(serialized_domain.value().data());\n      return domain::block_ids_from_names(blocks_to_combine.value(),\n                                          domain.block_names(),\n                                          domain.block_groups());\n    }\n    case 2: {\n      const auto domain =\n          deserialize<Domain<2>>(serialized_domain.value().data());\n      return domain::block_ids_from_names(blocks_to_combine.value(),\n                                          domain.block_names(),\n                                          domain.block_groups());\n    }\n    case 3: {\n      const auto domain =\n          deserialize<Domain<3>>(serialized_domain.value().data());\n      return domain::block_ids_from_names(blocks_to_combine.value(),\n                                          domain.block_names(),\n                                          domain.block_groups());\n    }\n    default:\n      ERROR(\"Only can handle 1, 2, or 3d domains not \" << dim);\n  };\n}\n}  // namespace\n\nnamespace h5 {\n\nvoid combine_h5_vol(\n    const std::vector<std::string>& file_names, const std::string& subfile_name,\n    const std::string& output, const std::optional<double> start_value,\n    const std::optional<double> stop_value,\n    const std::optional<std::vector<std::string>>& blocks_to_combine,\n    const bool check_src) {\n  // Parses for and stores all input files to be looped over\n  Parallel::printf(\"Processing files:\\n%s\\n\",\n                   std::string{MakeString{} << file_names}.c_str());\n\n  // Checks that volume data was generated with identical versions of SpECTRE\n  if (check_src) {\n    if (not h5::check_src_files_match(file_names)) {\n      ERROR(\n          \"One or more of your files were found to have differing src.tar.gz \"\n          \"files, meaning that they may be from differing versions of \"\n          \"SpECTRE.\");\n    }\n  }\n\n  // Checks that volume data files contain the same observation ids\n  if (not h5::check_observation_ids_match(file_names, subfile_name)) {\n    ERROR(\n        \"One or more of your files were found to have differing observation \"\n        \"ids, meaning they may be from different runs of your SpECTRE \"\n        \"executable or were corrupted.\");\n  }\n\n  // Braces to specify scope for H5 file\n  {\n    // Instantiates the output file and the .vol subfile to be filled with the\n    // combined data later\n    Parallel::printf(\"Creating output file: %s\\n\", output.c_str());\n    h5::H5File<h5::AccessType::ReadWrite> new_file(output, true);\n    new_file.insert<h5::VolumeData>(subfile_name);\n    new_file.close_current_object();\n  }  // End of scope for H5 file\n\n  // Obtains list of observation ids to loop over\n  const std::vector<std::pair<size_t, double>> observation_ids_and_values =\n      get_observation_ids(file_names, subfile_name);\n\n  if (observation_ids_and_values.empty()) {\n    ERROR(\"No observation IDs found in subfile\" << subfile_name);\n  }\n\n  const std::optional<std::vector<size_t>> blocks_to_use =\n      get_block_numbers_to_use(file_names[0], subfile_name, blocks_to_combine);\n\n  // Loops over observation ids to write volume data by observation id\n  for (size_t obs_index = 0; obs_index < observation_ids_and_values.size();\n       ++obs_index) {\n    const double obs_value = observation_ids_and_values[obs_index].second;\n    if (obs_value > stop_value.value_or(std::numeric_limits<double>::max()) or\n        obs_value <\n            start_value.value_or(std::numeric_limits<double>::lowest())) {\n      Parallel::printf(\"Skipping observation value %1.6e\\n\", obs_value);\n      continue;\n    }\n\n    const size_t obs_id = observation_ids_and_values[obs_index].first;\n    // Pre-calculates size of vector to store element data and allocates\n    // corresponding memory\n    const size_t vector_dim =\n        get_number_of_elements(file_names, subfile_name, obs_id);\n    std::vector<ElementVolumeData> element_data;\n    element_data.reserve(vector_dim);\n\n    double obs_val = 0.0;\n    std::optional<std::vector<char>> serialized_domain{};\n    std::optional<std::vector<char>> serialized_observation_functions_of_time{};\n    std::optional<std::vector<char>> serialized_global_functions_of_time{};\n\n    // Loops over input files to append element data into a single vector to be\n    // stored in a single H5\n    bool printed = false;\n    for (auto const& file_name : file_names) {\n      const h5::H5File<h5::AccessType::ReadOnly> original_file(file_name,\n                                                               false);\n      const auto& original_volume_file =\n          original_file.get<h5::VolumeData>(subfile_name);\n      obs_val = original_volume_file.get_observation_value(obs_id);\n      if (not printed) {\n        Parallel::printf(\n            \"Processing observation ID %lo (%lo/%lo) with value %1.14e\\n\",\n            obs_id, obs_index, observation_ids_and_values.size(), obs_val);\n        printed = true;\n      }\n      Parallel::printf(\"  Processing file: %s\\n\", file_name.c_str());\n\n      const auto dim = original_volume_file.get_dimension();\n      serialized_domain = original_volume_file.get_domain();\n      serialized_observation_functions_of_time =\n          original_volume_file.get_functions_of_time(obs_id);\n      serialized_global_functions_of_time =\n          original_volume_file.get_global_functions_of_time();\n      // Get vector of element data for this `obs_id` and `file_name`\n      std::vector<ElementVolumeData> data_by_element =\n          std::move(std::get<2>(original_volume_file.get_data_by_element(\n              obs_val * (1.0 - 4.0 * std::numeric_limits<double>::epsilon()),\n              obs_val * (1.0 + 4.0 * std::numeric_limits<double>::epsilon()),\n              std::nullopt)[0]));\n\n      auto end_it = data_by_element.end();\n\n      if (blocks_to_use.has_value()) {\n        end_it = alg::remove_if(\n            data_by_element,\n            [&blocks_to_use, &dim](const ElementVolumeData& element) -> bool {\n              switch (dim) {\n                case 1:\n                  return not alg::found(\n                      blocks_to_use.value(),\n                      ElementId<1>{element.element_name}.block_id());\n                case 2:\n                  return not alg::found(\n                      blocks_to_use.value(),\n                      ElementId<2>{element.element_name}.block_id());\n                case 3:\n                  return not alg::found(\n                      blocks_to_use.value(),\n                      ElementId<3>{element.element_name}.block_id());\n                default:\n                  ERROR(\"Only can handle 1, 2, or 3d domains but got \" << dim);\n              };\n            });\n      }\n\n      // Append vector to total vector of element data for this `obs_id`\n      element_data.insert(element_data.end(),\n                          std::make_move_iterator(data_by_element.begin()),\n                          std::make_move_iterator(end_it));\n      data_by_element.clear();\n      original_file.close_current_object();\n    }\n\n    h5::H5File<h5::AccessType::ReadWrite> new_file(output, true);\n    auto& new_volume_file = new_file.get<h5::VolumeData>(subfile_name);\n    new_volume_file.write_volume_data(obs_id, obs_val, element_data,\n                                      serialized_domain,\n                                      serialized_observation_functions_of_time,\n                                      serialized_global_functions_of_time);\n\n    new_file.close_current_object();\n  }\n}\n\nvoid combine_h5_dat(const std::vector<std::string>& h5_files_to_combine,\n                    const std::string& output_h5_filename,\n                    const Verbosity verbosity) {\n  if (h5_files_to_combine.empty()) {\n    ERROR_NO_TRACE(\"No H5 files to combine!\");\n  }\n\n  std::vector<std::string> subfile_dat_names{};\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> file_to_combine{\n        h5_files_to_combine[0]};\n    subfile_dat_names = file_to_combine.all_files<h5::Dat>(\"/\");\n\n    if (subfile_dat_names.empty()) {\n      ERROR_NO_TRACE(\"No dat files in H5 file \" << h5_files_to_combine[0]\n                                                << \"to combine!\");\n    }\n  }\n\n  if (verbosity >= Verbosity::Quiet) {\n    Parallel::printf(\"Combining all dat files from %s into %s\\n\",\n                     h5_files_to_combine, output_h5_filename);\n  }\n\n  // We just copy if there's only 1 file. We could change this behavior to\n  // error, but that's less versatile when using globs\n  if (h5_files_to_combine.size() == 1) {\n    file_system::copy(h5_files_to_combine[0], output_h5_filename);\n    if (verbosity >= Verbosity::Quiet) {\n      Parallel::printf(\"Done!\\n\");\n    }\n    return;\n  }\n\n  // For each dat subfile, this holds the number of *sorted* times from each of\n  // the H5 files to combine so that we always use the latest times. The\n  // std::vector should be the same length as the number of H5 files to combine\n  std::unordered_map<std::string, std::vector<size_t>> num_time_map{};\n\n  // The outer loop is over dat files because we don't require different dat\n  // files to have the same times\n  for (const std::string& dat_filename : subfile_dat_names) {\n    num_time_map[dat_filename];\n    num_time_map.at(dat_filename).resize(h5_files_to_combine.size());\n\n    // The legend and version are sanity checks\n    std::optional<std::vector<std::string>> legend{};\n    std::optional<uint32_t> version{};\n    // Nullopt just means the first file we are looping over\n    std::optional<double> earliest_time{};\n    // We loop backwards to always ensure the \"latest\" time is used.\n    for (int i = static_cast<int>(h5_files_to_combine.size()) - 1; i >= 0;\n         i--) {\n      const auto index = static_cast<size_t>(i);\n      const std::string& filename = h5_files_to_combine[index];\n      const h5::H5File<h5::AccessType::ReadOnly> file_to_combine{filename};\n      const auto& dat_file = file_to_combine.get<h5::Dat>(dat_filename);\n      const auto dimensions = dat_file.get_dimensions();\n      if (not legend.has_value()) {\n        legend = dat_file.get_legend();\n        version = dat_file.get_version();\n      }\n\n      // Only grab the times for now\n      auto times = dat_file.get_data_subset<std::vector<std::vector<double>>>(\n          {0}, 0, dimensions[0]);\n      alg::sort(times,\n                [](const std::vector<double>& v1,\n                   const std::vector<double>& v2) { return v1[0] < v2[0]; });\n\n      // Makes things easier below.\n      if (UNLIKELY(times.empty())) {\n        ERROR_NO_TRACE(\"No times in dat file \" << dat_filename << \" in H5 file \"\n                                               << filename);\n      }\n      if (UNLIKELY(legend.value() != dat_file.get_legend())) {\n        ERROR_NO_TRACE(\"Legend of dat file \"\n                       << dat_filename << \" in H5 file \" << filename\n                       << \" doesn't match other H5 files.\");\n      }\n      if (UNLIKELY(version.value() != dat_file.get_version())) {\n        ERROR_NO_TRACE(\"Version of dat file \"\n                       << dat_filename << \" in H5 file \" << filename\n                       << \" doesn't match other H5 files.\");\n      }\n\n      // This is the first (last) file. We can't make any decisions here so just\n      // store the number of times and the earliest time of this file\n      if (not earliest_time.has_value()) {\n        num_time_map.at(dat_filename)[index] = dimensions[0];\n        earliest_time = times[0][0];\n        continue;\n      }\n\n      // Check that the earliest time of the previous file (previous = later\n      // in the sequence of files since we are looping backward) is after the\n      // first time of this file. We require the files to be passed in\n      // increasing order by their first time.\n      if (UNLIKELY(times[0][0] >= earliest_time.value())) {\n        ERROR_NO_TRACE(\"The H5 files passed in \"\n                       << h5_files_to_combine\n                       << \" are not monotonically increasing in their first \"\n                          \"times for dat file \"\n                       << dat_filename);\n      }\n\n      // Determine if the earliest time of the previous file is before any of\n      // the times in this file. If so, don't include those times.\n      size_t row = times.size() - 1;\n      while (times[row][0] >= earliest_time.value()) {\n        // Make sure we don't reach row 0 since that would mean the files are\n        // not ordered. We should've checked for this above, so this is an\n        // additional safety check.\n        if (UNLIKELY(row == 0)) {\n          ERROR_NO_TRACE(\"Internal consistency error. Please file an issue.\");\n        }\n\n        row--;\n      }\n\n      // So long as this file contains some times that need to be combined,\n      // store the number of times and the first time in this file.\n      num_time_map.at(dat_filename)[index] = row + 1;\n      earliest_time = times[0][0];\n\n      file_to_combine.close_current_object();\n    }\n  }\n\n  if (verbosity >= Verbosity::Verbose) {\n    std::stringstream ss{};\n    ss << \"Number of times selected to combine in each H5 file for each dat \"\n          \"subfile:\\n\";\n    for (const auto& [subfile_name, h5_files_num_times] : num_time_map) {\n      ss << \" Dat Subfile \" << subfile_name << \":\\n\";\n      for (size_t i = 0; i < h5_files_num_times.size(); i++) {\n        ss << \"  H5 File \" << h5_files_to_combine[i] << \": \"\n           << h5_files_num_times[i] << \"\\n\";\n      }\n    }\n\n    Parallel::printf(\"%s\", ss.str());\n  }\n\n  // Now that we know the time indices for each dat file for each H5 file to\n  // combine, we open the output file and combine everything\n  h5::H5File<h5::AccessType::ReadWrite> output_h5_file{output_h5_filename};\n\n  // Now we loop over H5 files first to avoid unnecessary filesystem access\n  for (size_t i = 0; i < h5_files_to_combine.size(); i++) {\n    const std::string& filename = h5_files_to_combine[i];\n    const h5::H5File<h5::AccessType::ReadOnly> file_to_combine{filename};\n    for (const std::string& dat_filename : subfile_dat_names) {\n      const auto& input_dat_file = file_to_combine.get<h5::Dat>(dat_filename);\n      const std::vector<std::string>& legend = input_dat_file.get_legend();\n\n      // Avoid copying the legend around if we don't have to\n      h5::Dat* output_dat_file = nullptr;\n      if (output_h5_file.exists<h5::Dat>(dat_filename)) {\n        output_dat_file = &output_h5_file.get<h5::Dat>(dat_filename);\n      } else {\n        const uint32_t version = input_dat_file.get_version();\n        output_dat_file =\n            &output_h5_file.insert<h5::Dat>(dat_filename, legend, version);\n      }\n\n      const size_t num_times = num_time_map.at(dat_filename)[i];\n\n      // We must get all data first and sort it by times, because the number of\n      // times is only meaningful for the sorted data\n      auto data_to_append =\n          input_dat_file.get_data<std::vector<std::vector<double>>>();\n      alg::sort(data_to_append,\n                [](const std::vector<double>& v1,\n                   const std::vector<double>& v2) { return v1[0] < v2[0]; });\n      data_to_append.resize(num_times);\n\n      output_dat_file->append(data_to_append);\n\n      file_to_combine.close_current_object();\n      output_h5_file.close_current_object();\n    }\n  }\n\n  if (verbosity >= Verbosity::Quiet) {\n    Parallel::printf(\"Done!\\n\");\n  }\n}\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/CombineH5.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"IO/Logging/Verbosity.hpp\"\n\nnamespace h5 {\n/*!\n * \\brief Combine a volume subfile across different HDF5 files.\n *\n * The argument `blocks_to_combine` can list block names and block groups that\n * should be combined. We ignore other blocks when combining the HDF5\n * files. This provides a way to filter volume data for easier visualization.\n */\nvoid combine_h5_vol(const std::vector<std::string>& file_names,\n                const std::string& subfile_name, const std::string& output,\n                std::optional<double> start_value = std::nullopt,\n                std::optional<double> stop_value = std::nullopt,\n                const std::optional<std::vector<std::string>>&\n                    blocks_to_combine = std::nullopt,\n                bool check_src = true);\n\n/*!\n * \\brief Combine the `h5::Dat` subfiles of multiple `h5::H5File`s into a single\n * H5 file.\n *\n * \\details The times in each `h5::Dat` subfile can be unordered. The necessary\n * sorting will be handled in this function. However, the \\p h5_files_to_combine\n * must be mononitcally increasing in time; meaning the earliest time in\n * `File1.h5` must come before the earliest time in `File2.h5`.\n *\n * If there are overlapping times, the \"latest\" one is always used;\n * meaning if you have data in `File1.h5` and `File2.h5` and if the earliest\n * time in `File2.h5` is before some times in `File1.h5`, those times in\n * `File1.h5` will be discarded and won't appear in the combined H5 file.\n *\n * If the H5 files in \\p h5_files_to_combine have other types of subfiles, those\n * will be ignored and will not appear in \\p output_h5_filename.\n *\n * If \\p h5_files_to_combine is empty, an error will occur.\n *\n * If there are no `h5::Dat` files in the \\p h5_files_to_combine, an error will\n * occur.\n *\n * If the legend or version of an `h5::Dat` is not the same in all of\n * \\p h5_files_to_combine, an error will occur.\n *\n * \\param h5_files_to_combine Vector of H5 files to combine. They must all have\n * the same `h5::Dat` filenames, and those `h5::Dat` subfiles must have the same\n * legends and versions. If not, an error will occur.\n * \\param output_h5_filename Name of the combined H5 file. The `h5::Dat` subfile\n * structure will be identical to the ones in \\p h5_files_to_combine.\n * \\param verbosity Controls how much is printed to stdout. Defaults to no\n * `Verbosity::Silent` or no output.\n */\nvoid combine_h5_dat(const std::vector<std::string>& h5_files_to_combine,\n                    const std::string& output_h5_filename,\n                    Verbosity verbosity = Verbosity::Silent);\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Dat.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Dat.hpp\"\n\n#include <algorithm>\n#include <hdf5.h>\n#include <iosfwd>\n#include <memory>\n#include <ostream>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/Type.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace h5 {\nDat::Dat(const bool exists, detail::OpenGroup&& group, const hid_t location,\n         const std::string& name, std::vector<std::string> legend,\n         const uint32_t version)\n    : group_(std::move(group)),\n      name_(extension() == name.substr(name.size() > extension().size()\n                                           ? name.size() - extension().size()\n                                           : 0)\n                ? name\n                : name + extension()),\n      path_(group_.group_path_with_trailing_slash() + name),\n      version_(version),\n      legend_(std::move(legend)),\n      size_{{0, legend_.size()}} {\n  if (exists) {\n    dataset_id_ = H5Dopen2(location, name_.c_str(), h5::h5p_default());\n    CHECK_H5(dataset_id_, \"Failed to open dataset\");\n    {\n      // We treat this as an internal version for now. We'll need to deal with\n      // proper versioning later.\n\n      // Check if the version exists before calling the open_version.\n      const htri_t version_exists = H5Aexists(dataset_id_, \"version.ver\");\n      if (version_exists != 0) {\n        const Version open_version(true, detail::OpenGroup{}, dataset_id_,\n                                   \"version\");\n        version_ = open_version.get_version();\n      }\n    }\n    {\n      const htri_t header_exists = H5Aexists(dataset_id_, \"header.hdr\");\n      if (header_exists != 0) {\n        const Header header(true, detail::OpenGroup{}, dataset_id_, \"header\");\n        header_ = header.get_header();\n      }\n    }\n\n    hid_t space_id = H5Dget_space(dataset_id_);\n    std::array<hsize_t, 2> maxdims{};\n    if (2 !=\n        H5Sget_simple_extent_dims(space_id, size_.data(), maxdims.data())) {\n      ERROR(\"Invalid number of dimensions in file on disk.\");  // LCOV_EXCL_LINE\n    }\n    CHECK_H5(H5Sclose(space_id), \"Failed to close dataspace\");\n    legend_ = read_rank1_attribute<std::string>(dataset_id_, \"Legend\"s);\n    size_[1] = legend_.size();\n  } else {  // file does not exist\n    dataset_id_ = h5::detail::create_extensible_dataset(\n        location, name_, size_, std::array<hsize_t, 2>{{4, legend_.size()}},\n        {{h5s_unlimited(), legend_.size()}});\n    CHECK_H5(dataset_id_, \"Failed to create dataset\");\n\n    {\n      Version open_version(false, detail::OpenGroup{}, dataset_id_, \"version\",\n                           version_);\n    }\n    {\n      Header header(false, detail::OpenGroup{}, dataset_id_, \"header\");\n      header_ = header.get_header();\n    }\n    // Capitalized for compatibility with SpEC output\n    write_to_attribute(dataset_id_, \"Legend\"s, legend_);\n  }\n}\n\nDat::~Dat() {\n#ifdef __clang__\n  CHECK_H5(H5Dclose(dataset_id_), \"Failed to close dataset\");\n#else\n// The pragma here is used to suppress the warning that the compile time branch\n// of `ERROR` will always call `terminate()` because it throws an error.\n// Throwing that error is a code path that will never actually be entered at\n// runtime, so we suppress the warning here.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wterminate\"\n  CHECK_H5(H5Dclose(dataset_id_), \"Failed to close dataset\");\n#pragma GCC diagnostic pop\n#endif\n}\n\nvoid Dat::append(const std::vector<double>& data) {\n  if (data.size() != size_[1]) {\n    ERROR(\"Cannot add columns to Dat files. Current number of columns is \"\n          << size_[1] << \" but received \" << data.size() << \" entries.\");\n  }\n\n  size_ = h5::append_to_dataset(dataset_id_, name_, data, 1, size_);\n}\n\nvoid Dat::append(const std::vector<std::vector<double>>& data) {\n  if (data.empty()) {\n    return;\n  }\n  if (data[0].size() != size_[1]) {\n    ERROR(\"Cannot add columns to Dat files. Current number of columns is \"\n          << size_[1] << \" but received \" << data[0].size() << \" entries.\");\n  }\n  const std::vector<double> contiguous_data =\n      [](const std::vector<std::vector<double>>& ldata) {\n        std::vector<double> result(ldata.size() * ldata[0].size());\n        for (size_t i = 0; i < ldata.size(); ++i) {\n          if (ldata[i].size() != ldata[0].size()) {\n            ERROR(\n                \"Each member of the vector<vector<double>> must be of the same \"\n                \"size, ie the number of columns must be the same.\");\n          }\n          result.insert(\n              result.begin() + static_cast<std::ptrdiff_t>(i * ldata[i].size()),\n              ldata[i].begin(), ldata[i].end());\n        }\n        return result;\n      }(data);\n\n  size_ = h5::append_to_dataset(dataset_id_, name_, contiguous_data,\n                                data.size(), size_);\n}\n\nvoid Dat::append(const Matrix& data) {\n  if (0 == data.rows() * data.columns()) {\n    return;\n  }\n  if (data.columns() != size_[1]) {\n    ERROR(\"Cannot add columns to Dat files. Current number of columns is \"\n          << size_[1] << \" but received \" << data.columns() << \" entries.\");\n  }\n  // can't use begin() and end() because the ordering ends up wrong\n  const std::vector<double> contiguous_data = [](const Matrix& ldata) {\n    std::vector<double> result(ldata.rows() * ldata.columns());\n    for (size_t i = 0; i < ldata.rows(); ++i) {\n      for (size_t j = 0; j < ldata.columns(); ++j) {\n        result[j + i * ldata.columns()] = ldata(i, j);\n      }\n    }\n    return result;\n  }(data);\n\n  size_ = h5::append_to_dataset(dataset_id_, name_, contiguous_data,\n                                data.rows(), size_);\n}\n\ntemplate <typename T>\nT Dat::get_data() const {\n  return h5::retrieve_dataset<T>(dataset_id_, size_);\n}\n\ntemplate <typename T>\nT Dat::get_data_subset(const std::vector<size_t>& these_columns,\n                       const size_t first_row, const size_t num_rows) const {\n  return retrieve_dataset_subset<T>(dataset_id_, these_columns, first_row,\n                                    num_rows, size_);\n}\n\ntemplate Matrix Dat::get_data() const;\ntemplate std::vector<std::vector<double>> Dat::get_data() const;\ntemplate Matrix Dat::get_data_subset(const std::vector<size_t>& these_columns,\n                                     const size_t first_row,\n                                     const size_t num_rows) const;\ntemplate std::vector<std::vector<double>> Dat::get_data_subset(\n    const std::vector<size_t>& these_columns, const size_t first_row,\n    const size_t num_rows) const;\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Dat.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class h5::Dat\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <hdf5.h>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\n/// \\cond\nclass Matrix;\n/// \\endcond\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Represents a multicolumn dat file inside an HDF5 file\n *\n * A Dat object represents a dat file inside an H5File. A dat file is a\n * multicolumn text file with a header describing what each column represents.\n * Typically dat files are space or tab delimited, and often represent time\n * series data. One common use for them is writing out error norms over the\n * computational domain as a function of time. Inside the H5File they are stored\n * as a string header, and a matrix of doubles holding the data. One problem\n * encountered with dat files is that they quickly increase the file count\n * causing users to run into number of file limitations on HPC systems. Since\n * multiple Dat objects can be stored inside a single H5File the problem of many\n * different dat files being stored as individual files is solved.\n *\n * \\note This class does not do any caching of data so all data is written as\n * soon as append() is called.\n */\nclass Dat : public h5::Object {\n public:\n  /// \\cond HIDDEN_SYMBOLS\n  static std::string extension() { return \".dat\"; }\n\n  Dat(bool exists, detail::OpenGroup&& group, hid_t location,\n      const std::string& name, std::vector<std::string> legend = {},\n      uint32_t version = 1);\n\n  Dat(const Dat& /*rhs*/) = delete;\n  Dat& operator=(const Dat& /*rhs*/) = delete;\n  Dat(Dat&& /*rhs*/) = delete;             // NOLINT\n  Dat& operator=(Dat&& /*rhs*/) = delete;  // NOLINT\n\n  ~Dat() override;\n  /// \\endcond HIDDEN_SYMBOLS\n\n  /*!\n   * \\requires `data.size()` is the same as the number of columns in the file\n   * \\effects appends `data` to the Dat file\n   */\n  void append(const std::vector<double>& data);\n\n  /*!\n   * \\requires `data[0].size()` is the same as the number of columns in the file\n   * \\effects appends `data` to the Dat file\n   */\n  void append(const std::vector<std::vector<double>>& data);\n\n  /*!\n   * \\requires `data.columns()` is the same as the number of columns in the file\n   * \\effects appends `data` to the Dat file\n   */\n  void append(const Matrix& data);\n\n  /*!\n   * \\returns the legend of the Dat file\n   */\n  const std::vector<std::string>& get_legend() const { return legend_; }\n\n  /*!\n   * \\returns all data stored in the Dat file\n   *\n   * \\example\n   * \\snippet Test_Dat.cpp h5dat_get_data\n   */\n  template <typename T = Matrix>\n  T get_data() const;\n\n  /*!\n   * \\brief Get only some columns over a range of rows\n   * \\requires all members of `these_columns` have a value less than the number\n   * of columns, `first_row < last_row` and `last_row` is less than or equal to\n   * the number of rows\n   * \\returns a subset of the data from the Dat file\n   *\n   * \\example\n   * \\snippet Test_Dat.cpp h5dat_get_subset\n   */\n  template <typename T = Matrix>\n  T get_data_subset(const std::vector<size_t>& these_columns,\n                    size_t first_row = 0, size_t num_rows = 1) const;\n\n  /*!\n   * \\returns the number of rows (first index) and columns (second index)\n   */\n  const std::array<hsize_t, 2>& get_dimensions() const { return size_; }\n\n  /*!\n   * \\returns the header of the Dat file\n   */\n  const std::string& get_header() const { return header_; }\n\n  /*!\n   * \\returns the user-specified version number of the Dat file\n   *\n   * \\note h5::Version returns a uint32_t, so we return one here too for the\n   * version\n   */\n  uint32_t get_version() const { return version_; }\n\n  const std::string& subfile_path() const override { return path_; }\n\n private:\n  /// \\cond HIDDEN_SYMBOLS\n  detail::OpenGroup group_;\n  std::string name_;\n  std::string path_;\n  uint32_t version_;\n  std::vector<std::string> legend_;\n  std::array<hsize_t, 2> size_;\n  std::string header_;\n  hid_t dataset_id_{-1};\n  /// \\endcond HIDDEN_SYMBOLS\n};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/EosTable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/EosTable.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <ostream>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace h5 {\nEosTable::EosTable(\n    const bool subfile_exists, detail::OpenGroup&& group,\n    const hid_t /*location*/, const std::string& name,\n    std::vector<std::string> independent_variable_names,\n    std::vector<std::array<double, 2>> independent_variable_bounds,\n    std::vector<size_t> independent_variable_number_of_points,\n    std::vector<bool> independent_variable_uses_log_spacing,\n    const bool beta_equilibrium, const uint32_t version)\n    : group_(std::move(group)),\n      name_(name.size() > extension().size()\n                ? (extension() == name.substr(name.size() - extension().size())\n                       ? name\n                       : name + extension())\n                : name + extension()),\n      path_(group_.group_path_with_trailing_slash() + name),\n      version_(version),\n      eos_table_group_(group_.id(), name_, h5::AccessType::ReadWrite),\n      independent_variable_names_(std::move(independent_variable_names)),\n      independent_variable_bounds_(std::move(independent_variable_bounds)),\n      independent_variable_number_of_points_(\n          std::move(independent_variable_number_of_points)),\n      independent_variable_uses_log_spacing_(\n          std::move(independent_variable_uses_log_spacing)),\n      beta_equilibrium_(beta_equilibrium) {\n  if (subfile_exists) {\n    ERROR(\n        \"Opening an equation of state table with the constructor for writing a \"\n        \"table, but the subfile \"\n        << name << \" already exists\");\n  }\n  // Subfiles are closed as they go out of scope, so we have the extra\n  // braces here to add the necessary scope\n  {\n    Version open_version(false, detail::OpenGroup{}, eos_table_group_.id(),\n                         \"version\", version_);\n  }\n  {\n    Header header(false, detail::OpenGroup{}, eos_table_group_.id(), \"header\");\n    header_ = header.get_header();\n  }\n\n  h5::write_to_attribute(eos_table_group_.id(), \"independent variable names\",\n                         independent_variable_names_);\n  h5::write_to_attribute(eos_table_group_.id(), \"independent variable bounds\",\n                         independent_variable_bounds_);\n  h5::write_to_attribute(eos_table_group_.id(), \"number of points\",\n                         independent_variable_number_of_points_);\n  h5::write_to_attribute(eos_table_group_.id(), \"uses log spacing\",\n                         independent_variable_uses_log_spacing_);\n  h5::write_to_attribute(eos_table_group_.id(), \"beta equilibium\",\n                         beta_equilibrium_);\n}\n\nEosTable::EosTable(const bool /*subfile_exists*/, detail::OpenGroup&& group,\n                   const hid_t /*location*/, const std::string& name)\n    : group_(std::move(group)),\n      name_(name.size() > extension().size()\n                ? (extension() == name.substr(name.size() - extension().size())\n                       ? name\n                       : name + extension())\n                : name + extension()),\n      path_(group_.group_path_with_trailing_slash() + name),\n      eos_table_group_(group_.id(), name_, h5::AccessType::ReadOnly) {\n  // We treat this as an internal version for now. We'll need to deal with\n  // proper versioning later.\n  const Version open_version(true, detail::OpenGroup{}, eos_table_group_.id(),\n                             \"version\");\n  version_ = open_version.get_version();\n  const Header header(true, detail::OpenGroup{}, eos_table_group_.id(),\n                      \"header\");\n  header_ = header.get_header();\n\n  independent_variable_names_ = h5::read_rank1_attribute<std::string>(\n      eos_table_group_.id(), \"independent variable names\");\n  independent_variable_bounds_ = h5::read_rank1_array_attribute<double, 2>(\n      eos_table_group_.id(), \"independent variable bounds\");\n  independent_variable_number_of_points_ = h5::read_rank1_attribute<size_t>(\n      eos_table_group_.id(), \"number of points\");\n  independent_variable_uses_log_spacing_ =\n      h5::read_rank1_attribute<bool>(eos_table_group_.id(), \"uses log spacing\");\n  beta_equilibrium_ =\n      h5::read_value_attribute<bool>(eos_table_group_.id(), \"beta equilibium\");\n  // Note: if we start writing more datasets than just the dependent variables,\n  // this list will need to be pruned.\n  available_quantities_ = h5::get_group_names(eos_table_group_.id(), {});\n}\n\nvoid EosTable::write_quantity(std::string name, const DataVector& data) {\n  h5::write_data(eos_table_group_.id(), data, name);\n  available_quantities_.push_back(std::move(name));\n}\n\nDataVector EosTable::read_quantity(const std::string& name) const {\n  return h5::read_data<1, DataVector>(eos_table_group_.id(), name);\n}\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/EosTable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <hdf5.h>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief An equation of state table subfile written inside an H5 file.\n *\n * ### Data Layout\n *\n * To be consistent with the CompOSE ASCII table format, the data is stored in a\n * last independent variable varies fastest. For example, if you had independent\n * variables \\f$\\rho\\f$, \\f$T\\f$, and \\f$Y_e\\f$, then \\f$Y_e\\f$ varies fastest\n * while \\f$\\rho\\f$ varies slowest.\n */\nclass EosTable : public h5::Object {\n public:\n  static std::string extension() { return \".eos\"; }\n\n  /// Constructor used when writing a new equation of state.\n  EosTable(bool subfile_exists, detail::OpenGroup&& group, hid_t location,\n           const std::string& name,\n           std::vector<std::string> independent_variable_names,\n           std::vector<std::array<double, 2>> independent_variable_bounds,\n           std::vector<size_t> independent_variable_number_of_points,\n           std::vector<bool> independent_variable_uses_log_spacing,\n           bool beta_equilibrium, uint32_t version = 1);\n\n  /// Constructor used when reading in an equation of state.\n  EosTable(bool subfile_exists, detail::OpenGroup&& group, hid_t location,\n           const std::string& name);\n\n  EosTable(const EosTable& /*rhs*/) = delete;\n  EosTable& operator=(const EosTable& /*rhs*/) = delete;\n  EosTable(EosTable&& /*rhs*/) = delete;             // NOLINT\n  EosTable& operator=(EosTable&& /*rhs*/) = delete;  // NOLINT\n\n  ~EosTable() override = default;\n\n  /*!\n   * \\returns the header of the EosTable file\n   */\n  const std::string& get_header() const { return header_; }\n\n  /*!\n   * \\returns the user-specified version number of the EosTable file\n   *\n   * \\note h5::Version returns a uint32_t, so we return one here too for the\n   * version\n   */\n  uint32_t get_version() const { return version_; }\n\n  const std::string& subfile_path() const override { return path_; }\n\n  /*!\n   * \\brief Write a thermodynamic quantity to disk.\n   */\n  void write_quantity(std::string name, const DataVector& data);\n\n  /*!\n   * \\brief Read a thermodynamic quantity to disk.\n   */\n  DataVector read_quantity(const std::string& name) const;\n\n  /// The available thermodynamic quantities.\n  const std::vector<std::string> available_quantities() const {\n    return available_quantities_;\n  }\n\n  /// Number of independent variables.\n  size_t number_of_independent_variables() const {\n    return independent_variable_names_.size();\n  }\n\n  /// Names of the independent variables.\n  const std::vector<std::string>& independent_variable_names() const {\n    return independent_variable_names_;\n  }\n\n  /// Lower and upper bounds of the independent variables.\n  const std::vector<std::array<double, 2>>& independent_variable_bounds()\n      const {\n    return independent_variable_bounds_;\n  }\n\n  /// The number of points for each of the independent variables.\n  const std::vector<size_t>& independent_variable_number_of_points() const {\n    return independent_variable_number_of_points_;\n  }\n\n  /// Whether each independent variable is in log spacing. Linear spacing is\n  /// used if `false`.\n  const std::vector<bool>& independent_variable_uses_log_spacing() const {\n    return independent_variable_uses_log_spacing_;\n  }\n\n  /// `true` if the EOS is in beta equilibrium.\n  bool beta_equilibrium() const { return beta_equilibrium_; }\n\n private:\n  detail::OpenGroup group_{};\n  std::string name_{};\n  std::string path_{};\n  uint32_t version_{};\n  detail::OpenGroup eos_table_group_{};\n  std::string header_{};\n\n  std::vector<std::string> independent_variable_names_{};\n  std::vector<std::array<double, 2>> independent_variable_bounds_{};\n  std::vector<size_t> independent_variable_number_of_points_{};\n  std::vector<bool> independent_variable_uses_log_spacing_{};\n  bool beta_equilibrium_ = false;\n  std::vector<std::string> available_quantities_{};\n};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/ExtendConnectivityHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/ExtendConnectivityHelpers.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/Connectivity.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\n\n// Given a std::vector of grid_names, computes the number of blocks that exist\n// and also returns a std::vector of block numbers that is a one-to-one mapping\n// to each element in grid_names. The returned tuple is of the form\n// [number_of_blocks, block_number_for_each_element, sorted_element_indices].\n// number_of_blocks is equal to the number of blocks in the domain.\n// block_number_for_each_element is a std::vector with length equal to the total\n// number of grid names. sorted_element_indices is a std::vector<std::vector>\n// with length equal to the number of blocks in the domain, since each subvector\n// represents a given block. These subvectors are of a length equal to the\n// number of elements which belong to that corresponding block.\nstd::tuple<size_t, std::vector<size_t>, std::vector<std::vector<size_t>>>\ncompute_and_organize_block_info(const std::vector<std::string>& grid_names) {\n  std::vector<size_t> block_number_for_each_element;\n  std::vector<std::vector<size_t>> sorted_element_indices;\n  block_number_for_each_element.reserve(grid_names.size());\n\n  // Fills block_number_for_each_element\n  for (const std::string& grid_name : grid_names) {\n    size_t end_position = grid_name.find(',', 1);\n    block_number_for_each_element.push_back(\n        static_cast<size_t>(std::stoi(grid_name.substr(2, end_position))));\n  }\n\n  const auto max_block_number =\n      *std::max_element(block_number_for_each_element.begin(),\n                        block_number_for_each_element.end());\n  auto number_of_blocks = max_block_number + 1;\n  sorted_element_indices.reserve(number_of_blocks);\n\n  // Properly sizes subvectors of sorted_element_indices\n  for (size_t i = 0; i < number_of_blocks; ++i) {\n    std::vector<size_t> sizing_vector;\n    auto number_of_elements_in_block =\n        static_cast<size_t>(std::count(block_number_for_each_element.begin(),\n                                       block_number_for_each_element.end(), i));\n    sizing_vector.reserve(number_of_elements_in_block);\n    sorted_element_indices.push_back(sizing_vector);\n  }\n\n  // Organizing grid_names by block\n  for (size_t i = 0; i < block_number_for_each_element.size(); ++i) {\n    sorted_element_indices[block_number_for_each_element[i]].push_back(i);\n  }\n\n  return std::make_tuple(number_of_blocks,\n                         std::move(block_number_for_each_element),\n                         std::move(sorted_element_indices));\n}\n\n// Takes in a std::vector<std::vector<size_t>> sorted_element_indices which\n// houses indices associated to the elements sorted by block (labelled by the\n// position of the subvector in the parent vector) and some std::vector\n// property_to_sort of some element property (e.g. extents) for all elements in\n// the domain. First creates a std::vector<std::vector> identical in structure\n// to sorted_element_indices. Then, sorts property_to_sort by block using the\n// indices for elements in each block as stored in sorted_element_indices.\ntemplate <typename T>\nstd::vector<std::vector<T>> sort_by_block(\n    const std::vector<std::vector<size_t>>& sorted_element_indices,\n    const std::vector<T>& property_to_sort) {\n  std::vector<std::vector<T>> sorted_property;\n  sorted_property.reserve(sorted_element_indices.size());\n\n  // Properly sizes subvectors\n  for (const auto& sorted_block_index : sorted_element_indices) {\n    std::vector<T> sizing_vector;\n    sizing_vector.reserve(sorted_block_index.size());\n    for (const auto& sorted_element_index : sorted_block_index) {\n      sizing_vector.push_back(property_to_sort[sorted_element_index]);\n    }\n    sorted_property.push_back(std::move(sizing_vector));\n  }\n\n  return sorted_property;\n}\n\n// Returns a std::tuple of the form\n// [expected_connectivity_length, expected_number_of_grid_points, h_ref_array],\n// where each of the quantities in the tuple is computed for each block\n// individually. expected_connectivity_length is the expected length of the\n// connectivity for the given block. expected_number_of_grid_points is the\n// number of grid points that are expected to be within the block. h_ref_array\n// is an array of the h-refinement in the x, y, and z directions. This function\n// computes properties at the block level, as our algorithm for constructing the\n// new connectivity works within a block, making it convenient to sort these\n// properties early.\ntemplate <size_t SpatialDim>\nstd::tuple<size_t, size_t, std::array<int, SpatialDim>>\ncompute_block_level_properties(\n    const std::vector<std::string>& block_grid_names,\n    const std::vector<std::vector<size_t>>& block_extents) {\n  size_t expected_connectivity_length = 0;\n  // Used for reserving the length of block_logical_coords\n  size_t expected_number_of_grid_points = 0;\n\n  for (const auto& extents : block_extents) {\n    size_t element_grid_points = 1;\n    size_t number_of_cells_in_element = 1;\n    for (size_t j = 0; j < SpatialDim; j++) {\n      element_grid_points *= extents[j];\n      number_of_cells_in_element *= extents[j] - 1;\n    }\n    // Connectivity that already exists\n    expected_connectivity_length +=\n        number_of_cells_in_element * pow(2, SpatialDim);\n    expected_number_of_grid_points += element_grid_points;\n  }\n\n  std::string grid_name_string = block_grid_names[0];\n  std::array<int, SpatialDim> h_ref_array = {};\n  size_t h_ref_previous_start_position = 0;\n  size_t additional_connectivity_length = 1;\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    const size_t h_ref_start_position =\n        grid_name_string.find('L', h_ref_previous_start_position + 1);\n    const size_t h_ref_end_position =\n        grid_name_string.find('I', h_ref_start_position);\n    const int h_ref = std::stoi(\n        grid_name_string.substr(h_ref_start_position + 1,\n                                h_ref_end_position - h_ref_start_position - 1));\n    gsl::at(h_ref_array, i) = h_ref;\n    additional_connectivity_length *= pow(2, h_ref + 1) - 1;\n    h_ref_previous_start_position = h_ref_start_position;\n  }\n\n  expected_connectivity_length +=\n      (additional_connectivity_length - block_extents.size()) * 8;\n\n  return std::tuple{expected_connectivity_length,\n                    expected_number_of_grid_points, h_ref_array};\n}\n\n// Returns a std::vector<std::array> where each std::array represents the\n// coordinates of a grid point in the block logical frame, and the entire\n// std::vector is the list of all such grid points\ntemplate <size_t SpatialDim>\nstd::vector<std::array<double, SpatialDim>> generate_block_logical_coordinates(\n    const std::vector<std::array<double, SpatialDim>>&\n        element_logical_coordinates,\n    const std::string& grid_name,\n    const std::array<int, SpatialDim>& h_refinement_array) {\n  size_t grid_points_x_start_position = 0;\n  std::vector<std::array<double, SpatialDim>> block_logical_coordinates;\n  block_logical_coordinates.reserve(element_logical_coordinates.size());\n  std::vector<double> number_of_elements_each_direction;\n  number_of_elements_each_direction.reserve(SpatialDim);\n  std::vector<double> shift_each_direction;\n  shift_each_direction.reserve(SpatialDim);\n\n  // Computes number_of_elements_each_direction, element_index, and\n  // shift_each_direction to each be used in the computation of the grid point\n  // coordinates in the block logical frame\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    double number_of_elements = pow(2, gsl::at(h_refinement_array, i));\n    number_of_elements_each_direction.push_back(number_of_elements);\n    size_t grid_points_start_position =\n        grid_name.find('I', grid_points_x_start_position + 1);\n    size_t grid_points_end_position =\n        grid_name.find(',', grid_points_start_position);\n    if (i == SpatialDim) {\n      grid_points_end_position =\n          grid_name.find(')', grid_points_start_position);\n    }\n    int element_index = std::stoi(grid_name.substr(\n        grid_points_start_position + 1,\n        grid_points_end_position - grid_points_start_position - 1));\n    double shift = (-1 + (2 * element_index + 1) / number_of_elements);\n    shift_each_direction.push_back(shift);\n    grid_points_x_start_position = grid_points_start_position;\n  }\n\n  // Computes the coordinates for each grid point in the block logical frame\n  for (size_t i = 0; i < element_logical_coordinates.size(); ++i) {\n    std::array<double, SpatialDim> grid_point_coordinate = {};\n    for (size_t j = 0; j < grid_point_coordinate.size(); ++j) {\n      gsl::at(grid_point_coordinate, j) =\n          1. / number_of_elements_each_direction[j] *\n              element_logical_coordinates[i][j] +\n          shift_each_direction[j];\n    }\n    block_logical_coordinates.push_back(grid_point_coordinate);\n  }\n\n  return block_logical_coordinates;\n}\n\n// Given a std::vector<double> where the elements are ordered in ascending order\n// a new std::vector<double> is generated where it is a list of the original\n// values in ascending order without duplicates\n// Example: [1,2,2,3] -> order() -> [1,2,3]\nstd::vector<double> order_sorted_elements(\n    const std::vector<double>& sorted_elements) {\n  std::vector<double> ordered_elements;\n  ordered_elements.push_back(sorted_elements[0]);\n  for (size_t i = 1; i < sorted_elements.size(); ++i) {\n    if (sorted_elements[i] != ordered_elements.end()[-1]) {\n      ordered_elements.push_back(sorted_elements[i]);\n    }\n  }\n  return ordered_elements;\n}\n\n// Returns a std::vector of std::pair where each std::pair is\n// composed of a number for the block a given grid point resides inside of, as\n// well as the grid point itself as a std::array. Generates the connectivity by\n// connecting grid points (in the block logical frame) to form either\n// hexahedrons, quadrilaterals, or lines depending on the SpatialDim. The\n// function iteratively generates all possible shapes with all of a grid point's\n// nearest neighbors. Example: Consider a 4x4 grid of evenly spaces points.\n// build_connectivity_by_hexahedron generates connectivity that forms 9 sqaures.\ntemplate <size_t SpatialDim>\nstd::vector<std::pair<size_t, std::array<double, SpatialDim>>>\nbuild_connectivity_by_hexahedron(const std::vector<double>& sorted_x,\n                                 const std::vector<double>& sorted_y,\n                                 const std::vector<double>& sorted_z,\n                                 const size_t& block_number) {\n  std::vector<std::pair<size_t, std::array<double, SpatialDim>>>\n      connectivity_of_keys;\n\n  std::array<double, SpatialDim> point_one = {};\n  std::array<double, SpatialDim> point_two = {};\n  [[maybe_unused]] std::array<double, SpatialDim> point_three = {};\n  [[maybe_unused]] std::array<double, SpatialDim> point_four = {};\n  [[maybe_unused]] std::array<double, SpatialDim> point_five = {};\n  [[maybe_unused]] std::array<double, SpatialDim> point_six = {};\n  [[maybe_unused]] std::array<double, SpatialDim> point_seven = {};\n  [[maybe_unused]] std::array<double, SpatialDim> point_eight = {};\n\n  // Algorithm for connecting grid points. Extended by if statments to account\n  // for 1D, 2D, and 3D\n  for (size_t i = 0; i < sorted_x.size() - 1; ++i) {\n    point_one[0] = sorted_x[i];\n    point_two[0] = sorted_x[i + 1];\n    // 2D or 3D\n    if constexpr (SpatialDim > 1) {\n      point_three[0] = sorted_x[i + 1];\n      point_four[0] = sorted_x[i];\n      for (size_t j = 0; j < sorted_y.size() - 1; ++j) {\n        point_one[1] = sorted_y[j];\n        point_two[1] = sorted_y[j];\n        point_three[1] = sorted_y[j + 1];\n        point_four[1] = sorted_y[j + 1];\n        // 3D\n        if constexpr (SpatialDim == 3) {\n          point_five[0] = sorted_x[i];\n          point_six[0] = sorted_x[i + 1];\n          point_seven[0] = sorted_x[i + 1];\n          point_eight[0] = sorted_x[i];\n          point_five[1] = sorted_y[j];\n          point_six[1] = sorted_y[j];\n          point_seven[1] = sorted_y[j + 1];\n          point_eight[1] = sorted_y[j + 1];\n          for (size_t k = 0; k < sorted_z.size() - 1; ++k) {\n            point_one[2] = sorted_z[k];\n            point_two[2] = sorted_z[k];\n            point_three[2] = sorted_z[k];\n            point_four[2] = sorted_z[k];\n            point_five[2] = sorted_z[k + 1];\n            point_six[2] = sorted_z[k + 1];\n            point_seven[2] = sorted_z[k + 1];\n            point_eight[2] = sorted_z[k + 1];\n\n            connectivity_of_keys.insert(\n                connectivity_of_keys.end(),\n                {std::make_pair(block_number, point_one),\n                 std::make_pair(block_number, point_two),\n                 std::make_pair(block_number, point_three),\n                 std::make_pair(block_number, point_four),\n                 std::make_pair(block_number, point_five),\n                 std::make_pair(block_number, point_six),\n                 std::make_pair(block_number, point_seven),\n                 std::make_pair(block_number, point_eight)});\n          }\n        } else {\n          connectivity_of_keys.insert(\n              connectivity_of_keys.end(),\n              {std::make_pair(block_number, point_one),\n               std::make_pair(block_number, point_two),\n               std::make_pair(block_number, point_three),\n               std::make_pair(block_number, point_four)});\n        }\n      }\n    } else {\n      connectivity_of_keys.insert(connectivity_of_keys.end(),\n                                  {std::make_pair(block_number, point_one),\n                                   std::make_pair(block_number, point_two)});\n    }\n  }\n  return connectivity_of_keys;\n}\n\n// Returns the output of build_connectivity_by_hexahedron after feeding in\n// specially prepared inputs. The output is the new connectivity\ntemplate <size_t SpatialDim>\nstd::vector<std::pair<size_t, std::array<double, SpatialDim>>>\ngenerate_new_connectivity(\n    std::vector<std::array<double, SpatialDim>>& block_logical_coordinates,\n    const size_t& block_number) {\n  std::vector<std::vector<double>> unsorted_coordinates;\n  unsorted_coordinates.reserve(SpatialDim);\n\n  // Takes the block_logical_coordinates and splits them up into a unique\n  // std::vector for x, y, and z. These three std::vector are then stored inside\n  // of a std::vector unsorted_coordinates\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    std::vector<double> coordinates_by_direction;\n    coordinates_by_direction.reserve(block_logical_coordinates.size());\n    for (size_t j = 0; j < block_logical_coordinates.size(); ++j) {\n      coordinates_by_direction.push_back(block_logical_coordinates[j][i]);\n    }\n    unsorted_coordinates.push_back(coordinates_by_direction);\n  }\n\n  // Creates ordered_x, ordered_y, and ordered_z by first sorting\n  // unsorted_coordinates x, y, and z, then passing these into\n  // order_sorted_elements()\n  sort(unsorted_coordinates[0].begin(), unsorted_coordinates[0].end());\n  std::vector<double> ordered_x =\n      order_sorted_elements(unsorted_coordinates[0]);\n  std::vector<double> ordered_y = {0.0};\n  std::vector<double> ordered_z = {0.0};\n\n  if (SpatialDim > 1) {\n    sort(unsorted_coordinates[1].begin(), unsorted_coordinates[1].end());\n    ordered_y = order_sorted_elements(unsorted_coordinates[1]);\n    if (SpatialDim == 3) {\n      sort(unsorted_coordinates[2].begin(), unsorted_coordinates[2].end());\n      ordered_z = order_sorted_elements(unsorted_coordinates[2]);\n    }\n  }\n\n  return build_connectivity_by_hexahedron<SpatialDim>(ordered_x, ordered_y,\n                                                      ordered_z, block_number);\n}\n}  // namespace\n\nnamespace h5::detail {\n\n// Write new connectivity connections given a std::vector of observation ids\ntemplate <size_t SpatialDim>\nstd::vector<int> extend_connectivity(\n    std::vector<std::string>& grid_names,\n    std::vector<std::vector<Spectral::Basis>>& bases,\n    std::vector<std::vector<Spectral::Quadrature>>& quadratures,\n    std::vector<std::vector<size_t>>& extents) {\n  auto [number_of_blocks, block_number_for_each_element,\n        sorted_element_indices] = compute_and_organize_block_info(grid_names);\n\n  const auto sorted_grid_names =\n      sort_by_block(sorted_element_indices, grid_names);\n  const auto sorted_extents = sort_by_block(sorted_element_indices, extents);\n\n  size_t total_expected_connectivity = 0;\n  std::vector<int> expected_grid_points_per_block;\n  expected_grid_points_per_block.reserve(number_of_blocks);\n  std::vector<std::array<int, SpatialDim>> h_ref_per_block;\n  h_ref_per_block.reserve(number_of_blocks);\n\n  // Loop over blocks\n  for (size_t j = 0; j < number_of_blocks; ++j) {\n    auto [expected_connectivity_length, expected_number_of_grid_points,\n          h_ref_array] =\n        compute_block_level_properties<SpatialDim>(sorted_grid_names[j],\n                                                   sorted_extents[j]);\n    total_expected_connectivity += expected_connectivity_length;\n    expected_grid_points_per_block.push_back(expected_number_of_grid_points);\n    h_ref_per_block.push_back(h_ref_array);\n  }\n\n  // Create an unordered_map to be used to associate a grid point's block\n  // number and coordinates as an array to the its label\n  // (B#, grid_point_coord_array) -> grid_point_number\n  std::unordered_map<\n      std::pair<size_t, std::array<double, SpatialDim>>, size_t,\n      boost::hash<std::pair<size_t, std::array<double, SpatialDim>>>>\n      block_and_grid_point_map;\n\n  // Create the sorted container for the grid points, which is a\n  // std::vector<std::vector>. The length of the first layer has length equal\n  // to the number of blocks, as each subvector corresponds to one of the\n  // blocks. Each subvector is of length equal to the number of grid points\n  // in the corresponding block, as we are storing an array of the block\n  // logical coordinates for each grid point.\n  std::vector<std::vector<std::array<double, SpatialDim>>>\n      block_logical_coordinates_by_block;\n  block_logical_coordinates_by_block.reserve(number_of_blocks);\n\n  // Reserve size for the subvectors\n  for (const auto& sorted_block_index : sorted_element_indices) {\n    std::vector<std::array<double, SpatialDim>> sizing_vector;\n    sizing_vector.reserve(sorted_block_index.size());\n    block_logical_coordinates_by_block.push_back(sizing_vector);\n  }\n\n  // Counter for the grid points when filling the unordered_map. Grid points\n  // are labelled by positive integers, so we are numbering them with this\n  // counter as we associate them to the key (B#, grid_point_coord_array) in\n  // the unordered map.\n  size_t grid_point_number = 0;\n\n  for (size_t element_index = 0;\n       element_index < block_number_for_each_element.size(); ++element_index) {\n    auto element_mesh = mesh_for_grid<SpatialDim>(\n        grid_names[element_index], grid_names, extents, bases, quadratures);\n    auto element_logical_coordinates_tensor = logical_coordinates(element_mesh);\n\n    std::vector<std::array<double, SpatialDim>> element_logical_coordinates;\n    element_logical_coordinates.reserve(\n        element_logical_coordinates_tensor.get(0).size());\n\n    for (size_t k = 0; k < element_logical_coordinates_tensor.get(0).size();\n         ++k) {\n      std::array<double, SpatialDim> logical_coords_element_increment = {};\n      for (size_t l = 0; l < SpatialDim; l++) {\n        gsl::at(logical_coords_element_increment, l) =\n            element_logical_coordinates_tensor.get(l)[k];\n      }\n      element_logical_coordinates.push_back(logical_coords_element_increment);\n    }\n\n    auto block_logical_coordinates =\n        generate_block_logical_coordinates<SpatialDim>(\n            element_logical_coordinates, grid_names[element_index],\n            h_ref_per_block[block_number_for_each_element[element_index]]);\n\n    // Stores (B#, grid_point_coord_array) -> grid_point_number in an\n    // unordered_map and grid_point_coord_array by block\n    for (size_t k = 0; k < block_logical_coordinates.size(); ++k) {\n      std::pair<size_t, std::array<double, SpatialDim>> block_and_grid_point(\n          block_number_for_each_element[element_index],\n          block_logical_coordinates[k]);\n      block_and_grid_point_map.insert(\n          std::pair<std::pair<size_t, std::array<double, SpatialDim>>, size_t>(\n              block_and_grid_point, grid_point_number));\n      grid_point_number += 1;\n\n      block_logical_coordinates_by_block\n          [block_number_for_each_element[element_index]]\n              .push_back(block_logical_coordinates[k]);\n    }\n  }\n\n  // Account for XDMF type tag (1 extra int per cell) in the reservation.\n  // Each cell has 2^SpatialDim vertices plus 1 type tag.\n  constexpr size_t vertices_per_cell = 1 << SpatialDim;\n  total_expected_connectivity =\n      total_expected_connectivity / vertices_per_cell * (vertices_per_cell + 1);\n  std::vector<int> new_connectivity;\n  new_connectivity.reserve(total_expected_connectivity);\n\n  // XDMF topology type tag via shared helper (Line=2, Quad=5, Hexahedron=9)\n  constexpr vis::detail::Topology cell_topology =\n      SpatialDim == 1 ? vis::detail::Topology::Line\n                      : (SpatialDim == 2 ? vis::detail::Topology::Quad\n                                         : vis::detail::Topology::Hexahedron);\n  const int xdmf_type_tag = vis::detail::xdmf_topology_type(cell_topology);\n  for (size_t j = 0; j < block_logical_coordinates_by_block.size(); ++j) {\n    auto block_number = j;\n    auto connectivity_of_keys = generate_new_connectivity<SpatialDim>(\n        block_logical_coordinates_by_block[j], block_number);\n    size_t vertices_in_cell = 0;\n    for (const std::pair<size_t, std::array<double, SpatialDim>>& it :\n         connectivity_of_keys) {\n      if (vertices_in_cell == 0) {\n        new_connectivity.push_back(xdmf_type_tag);\n      }\n      new_connectivity.push_back(block_and_grid_point_map[it]);\n      ++vertices_in_cell;\n      if (vertices_in_cell == vertices_per_cell) {\n        vertices_in_cell = 0;\n      }\n    }\n  }\n\n  return new_connectivity;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                            \\\n  template std::vector<int> h5::detail::extend_connectivity<DIM(data)>( \\\n      std::vector<std::string> & grid_names,                            \\\n      std::vector<std::vector<Spectral::Basis>> & bases,                \\\n      std::vector<std::vector<Spectral::Quadrature>> & quadratures,     \\\n      std::vector<std::vector<size_t>> & extents);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace h5::detail\n"
  },
  {
    "path": "src/IO/H5/ExtendConnectivityHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <string>\n#include <vector>\n\n/// \\cond\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n\nnamespace h5::detail {\ntemplate <size_t SpatialDim>\nstd::vector<int> extend_connectivity(\n    std::vector<std::string>& grid_names,\n    std::vector<std::vector<Spectral::Basis>>& bases,\n    std::vector<std::vector<Spectral::Quadrature>>& quadratures,\n    std::vector<std::vector<size_t>>& extents);\n}\n\n/// \\endcond\n"
  },
  {
    "path": "src/IO/H5/File.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/File.hpp\"\n\n#include <algorithm>\n#include <charm++.h>\n#include <exception>\n#include <hdf5.h>\n#include <string>\n\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/System/Abort.hpp\"\n\nnamespace h5 {\ntemplate <AccessType Access_t>\nH5File<Access_t>::H5File(std::string file_name, bool append_to_file,\n                         const std::string& input_source, bool use_file_locking)\n    : file_name_(std::move(file_name)) {\n  if (file_name_.size() - 3 != file_name_.find(\".h5\")) {\n    ERROR(\"All HDF5 file names must end in '.h5'. The path and file name '\"\n          << file_name_ << \"' does not satisfy this\");\n  }\n  const bool file_exists = file_system::check_if_file_exists(file_name_);\n  if (not file_exists and AccessType::ReadOnly == Access_t) {\n    ERROR(\"Trying to open the file '\"\n          << file_name_\n          << \"' in ReadOnly mode but the file does not exist. If you want to \"\n             \"create the file you must switch to ReadWrite mode.\");\n  }\n  if (append_to_file and AccessType::ReadOnly == Access_t) {\n    ERROR(\"Cannot append to a file opened in read-only mode. File name is: \"\n          << file_name_);\n  }\n  if (file_exists and not append_to_file and\n      h5::AccessType::ReadWrite == Access_t) {\n    ERROR(\"File '\" << file_name_\n                   << \"' already exists and we are not allowed to append. To \"\n                      \"reduce the risk of accidental deletion you must \"\n                      \"explicitly delete the file first using the file_system \"\n                      \"library in SpECTRE or through your shell.\");\n  }\n\n  const hid_t fapl_id = H5Pcreate(H5P_FILE_ACCESS);\n  CHECK_H5(fapl_id, \"Failed to create file access property list.\");\n#ifdef HDF5_SUPPORTS_SET_FILE_LOCKING\n  CHECK_H5(H5Pset_file_locking(\n               fapl_id, use_file_locking,\n               // Ignore file locks when they are disabled on the file system\n               true),\n           \"Failed to configure file locking.\");\n#else\n  (void)use_file_locking;\n#endif\n\n  file_id_ = file_exists\n                 ? H5Fopen(file_name_.c_str(),\n                           AccessType::ReadOnly == Access_t ? h5f_acc_rdonly()\n                                                            : h5f_acc_rdwr(),\n                           fapl_id)\n                 : H5Fcreate(file_name_.c_str(), h5f_acc_trunc(), h5p_default(),\n                             fapl_id);\n  CHECK_H5(file_id_, \"Failed to open file '\"\n                         << file_name_ << \"'. The file exists status is: \"\n                         << std::boolalpha << file_exists\n                         << \". Writing from directory: \" << file_system::cwd()\n                         << \". Trying to open in mode: \" << Access_t);\n  if (not file_exists) {\n    close_current_object();\n    insert_header();\n    close_current_object();\n    insert_source_archive();\n    write_to_attribute<std::string>(file_id_, \"InputSource.yaml\"s,\n                                    {{input_source}});\n  }\n  close_current_object();\n}\n\ntemplate <AccessType Access_t>\nH5File<Access_t>::H5File(H5File&& rhs) {\n  file_name_ = std::move(rhs.file_name_);\n  file_id_ = std::move(rhs.file_id_);\n  current_object_ = std::move(rhs.current_object_);\n  h5_groups_ = std::move(rhs.h5_groups_);\n  rhs.file_id_ = -1;\n}\n\ntemplate <AccessType Access_t>\nH5File<Access_t>& H5File<Access_t>::operator=(H5File&& rhs) {\n  if (file_id_ != -1) {\n    CHECK_H5(H5Fclose(file_id_),\n             \"Failed to close file: '\" << file_name_ << \"'\");\n  }\n\n  file_name_ = std::move(rhs.file_name_);\n  file_id_ = std::move(rhs.file_id_);\n  current_object_ = std::move(rhs.current_object_);\n  h5_groups_ = std::move(rhs.h5_groups_);\n  rhs.file_id_ = -1;\n  return *this;\n}\n\ntemplate <AccessType Access_t>\nH5File<Access_t>::~H5File() {\n  if (file_id_ != -1) {\n    const auto h5_status = H5Fclose(file_id_);\n    if (h5_status < 0) {\n      // Could not close the H5 file.  Since we cannot throw an exception\n      // from the destructor (without modifying the destructor of the Charm++\n      // class PUP::able), we just abort.  But before aborting we attempt to\n      // see if another exception is propagating, and if so print its error\n      // message.\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n      CkError(\"Failed to close H5 File '%s'\\n\", file_name_.c_str());\n      std::exception_ptr exception = std::current_exception();\n      if (exception) {\n        try {\n          std::rethrow_exception(exception);\n        } catch (std::exception& ex) {\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n          CkError(\"while handling the following exception:\\n\\n%s\\n\", ex.what());\n        }\n      }\n      sys::abort(\"\");\n    }\n  }\n}\n\ntemplate <AccessType Access_t>\nvoid H5File<Access_t>::close() const {\n  // Need to close current object because `H5Fclose` keeps the file open\n  // internally until all objects are closed\n  close_current_object();\n  if (file_id_ != -1) {\n    CHECK_H5(H5Fclose(file_id_), \"Failed to close the file.\");\n    file_id_ = -1;\n  }\n}\n\ntemplate <AccessType Access_t>\nstd::string H5File<Access_t>::input_source() const {\n  return h5::read_value_attribute<std::string>(file_id_, \"InputSource.yaml\"s);\n}\n\ntemplate <>\nvoid H5File<AccessType::ReadWrite>::insert_source_archive() {\n  insert<h5::SourceArchive>(\"/src\");\n}\ntemplate <>\nvoid H5File<AccessType::ReadOnly>::insert_source_archive() {}\n}  // namespace h5\n\ntemplate class h5::H5File<h5::AccessType::ReadOnly>;\ntemplate class h5::H5File<h5::AccessType::ReadWrite>;\n"
  },
  {
    "path": "src/IO/H5/File.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class h5::H5File\n\n#pragma once\n\n#include <algorithm>\n#include <exception>\n#include <hdf5.h>\n#include <memory>\n#include <ostream>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <typeinfo>\n#include <vector>\n\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Opens an HDF5 file for access and allows manipulation of data\n *\n * Opens an HDF5 file either in ReadOnly or ReadWrite mode depending on the\n * template parameter `Access_t`. In ReadWrite mode h5::Object's can be inserted\n * into the file, and objects can be retrieved to have their data manipulated.\n * Example objects are dat files, text files, and volume data files. A single\n * H5File can contain many different objects so that the number of files stored\n * during a simulation is reduced.\n *\n * When an h5::object inside an H5File is opened or created the H5File object\n * holds a copy of the h5::object.\n *\n * \\warning Only one object can be open at a time, which means if a reference to\n * the object is kept around after the H5File's current object is closed there\n * is a dangling reference. Also, this means that after you insert an object,\n * you must close that object before you can insert/open another.\n *\n * \\example\n * To open a file for read-write access:\n * \\snippet Test_H5File.cpp h5file_readwrite_file\n *\n * \\note The dangling reference issue could be fixed by having a function in\n * addition to `get` that takes a lambda. The lambda takes exactly one parameter\n * of the type of the h5::Object it will be operating on. While this approach is\n * likely to be syntactically strange for many users it will most likely be more\n * performant than the `shared_ptr` solution.\n *\n * @tparam Access_t either h5::AccessType::ReadWrite or h5::AccessType::ReadOnly\n */\ntemplate <AccessType Access_t>\nclass H5File {\n public:\n  // empty constructor for classes which store an H5File and need to be\n  // charm-compatible.\n  H5File() = default;\n\n  /*!\n   * \\requires `file_name` is a valid path and ends in `.h5`.\n   * \\effects On object creation opens the HDF5 file at `file_name`\n   *\n   * @param file_name the path to the file to open or create\n   * @param append_to_file if true allow appending to the file, otherwise abort\n   * the simulation if the file exists\n   * @param input_source a string containing the input source options (yaml\n   * formatted). Defaults to an empty string; when writing, specify the provided\n   * yaml input options (if any) to write them to the output file's\n   * `InputSource.yaml` attribute.\n   * @param use_file_locking Toggle file locking (default false).\n   * HDF5 file locking is explained here:\n   * https://github.com/HDFGroup/hdf5/blob/develop/doc/file-locking.md.\n   * This toggle only has an effect if the HDF5 library supports\n   * 'H5Pset_file_locking'. Otherwise, file locking is enabled if the HDF5\n   * library was built with it, which it probably was. If file locking is\n   * enabled, simulations may crash when the file they try to access is being\n   * read by another process (like an analysis tool). We could make this more\n   * resilient in the future by waiting to acquire the file lock with a timeout,\n   * and/or retrying IO operations after progressively longer wait times (e.g.\n   * first try again right away, then also print to terminal after some retries,\n   * then eventually abort to avoid wasting compute time on a run that can't do\n   * IO).\n   */\n  explicit H5File(std::string file_name, bool append_to_file = false,\n                  const std::string& input_source = \"\"s,\n                  bool use_file_locking = false);\n\n  /// \\cond HIDDEN_SYMBOLS\n  ~H5File();\n  /// \\endcond\n\n  /// @{\n  /*!\n   * \\brief It does not make sense to copy an object referring to a file, only\n   * to move it.\n   */\n  H5File(const H5File& /*rhs*/) = delete;\n  H5File& operator=(const H5File& /*rhs*/) = delete;\n  /// @}\n\n  /// \\cond HIDDEN_SYMBOLS\n  H5File(H5File&& rhs);             // NOLINT\n  H5File& operator=(H5File&& rhs);  // NOLINT\n  /// \\endcond\n\n  /// Get name of the H5 file\n  const std::string& name() const { return file_name_; }\n\n  /// Get a std::vector of the names of all immediate subgroups of the file\n  const std::vector<std::string> groups() const {\n    return h5::get_group_names(file_id_, \"/\");\n  }\n\n  /// \\brief Return a vector of all filenames in the H5 file\n  /// \\tparam ObjectType Only return a vector that contains this type of file.\n  /// Default is `void` which returns all files.\n  template <typename ObjectType = void>\n  const std::vector<std::string> all_files(const std::string& group_name) const;\n\n  /// Get the InputSource.yaml string embedded in the file\n  std::string input_source() const;\n\n  /// @{\n  /*!\n   * \\requires `ObjectType` is a valid h5::Object derived class, `path`\n   * is a valid path in the HDF5 file\n   * \\return a reference to the object inside the HDF5 file.\n   *\n   * @tparam ObjectType the type of the h5::Object to be retrieved, e.g. Dat\n   * @param path the path of the retrieved object\n   * @param args arguments forwarded to the ObjectType constructor\n   */\n  template <\n      typename ObjectType, typename... Args,\n      typename std::enable_if_t<((void)sizeof(ObjectType),\n                                 Access_t == AccessType::ReadWrite)>* = nullptr>\n  ObjectType& get(const std::string& path, Args&&... args);\n\n  template <typename ObjectType, typename... Args>\n  const ObjectType& get(const std::string& path, Args&&... args) const;\n  /// @}\n\n  /*!\n   * \\brief Insert an object into an H5 file.\n   *\n   * \\requires `ObjectType` is a valid h5::Object derived class, `path` is a\n   * valid path in the HDF5 file, and `args` are valid arguments to be forwarded\n   * to the constructor of `ObjectType`.\n   * \\effects Creates a new H5 object of type `ObjectType` at the location\n   * `path` in the HDF5 file.\n   *\n   * \\return a reference the created object.\n   *\n   * @tparam ObjectType the type of the h5::Object to be inserted, e.g. Dat\n   * @param path the path of the inserted object\n   * @param args additional arguments to be passed to the constructor of the\n   * object\n   */\n  template <typename ObjectType, typename... Args>\n  ObjectType& insert(const std::string& path, Args&&... args);\n\n  /*!\n   * \\brief Inserts an object like `insert` if it does not exist, returns the\n   * object if it does.\n   */\n  template <typename ObjectType, typename... Args>\n  ObjectType& try_insert(const std::string& path, Args&&... args);\n\n  /*!\n   * \\effects Closes the current object, if there is none then has no effect\n   */\n  void close_current_object() const { current_object_ = nullptr; }\n\n  /*!\n   * \\effects Closes the H5 file. No H5 operations are permitted after this\n   * operation.\n   */\n  void close() const;\n\n  template <typename ObjectType>\n  bool exists(const std::string& path) const {\n    auto exists_group_name = check_if_object_exists<ObjectType>(path);\n    return std::get<0>(exists_group_name);\n  }\n\n private:\n  /// \\cond HIDDEN_SYMBOLS\n  template <typename ObjectType,\n            std::enable_if_t<((void)sizeof(ObjectType),\n                              Access_t == AccessType::ReadWrite)>* = nullptr>\n  ObjectType& convert_to_derived(\n      std::unique_ptr<h5::Object>& current_object);  // NOLINT\n  template <typename ObjectType>\n  const ObjectType& convert_to_derived(\n      const std::unique_ptr<h5::Object>& current_object) const;\n\n  void insert_header();\n  void insert_source_archive();\n\n  template <typename ObjectType>\n  std::tuple<bool, detail::OpenGroup, std::string> check_if_object_exists(\n      std::string path) const;\n\n  std::string file_name_;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable hid_t file_id_{-1};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::unique_ptr<h5::Object> current_object_{nullptr};\n  std::vector<std::string> h5_groups_;\n  /// \\endcond HIDDEN_SYMBOLS\n};\n\n// ======================================================================\n// H5File Definitions\n// ======================================================================\n\ntemplate <AccessType Access_t>\ntemplate <typename ObjectType>\nconst std::vector<std::string> H5File<Access_t>::all_files(\n    const std::string& group_name) const {\n  std::vector<std::string> groups = h5::get_group_names(file_id_, group_name);\n\n  // Loop through the initial files and groups and get all subfiles and groups\n  std::vector<std::string> all_files_and_groups{};\n  for (auto it = groups.begin(); it != groups.end(); ++it) {\n    // Full group name\n    const std::string prefix =\n        group_name == \"/\" ? group_name : (group_name + \"/\");\n\n    // If this is a file, there aren't any subfiles so add it to the overall\n    // list and continue. Most extensions follow the \".XYZ\" rule. Headers are\n    // special though because they end in \".tar.gz\"\n    const auto extension_pos = it->find_last_of(\".\");\n    if (not(extension_pos == std::string::npos) and\n        (it->size() - extension_pos == 4 or\n         it->substr(extension_pos) == \".gz\")) {\n      all_files_and_groups.insert(all_files_and_groups.end(), prefix + *it);\n      continue;\n    }\n\n    // Get all sub files\n    auto extra_files_and_groups = all_files(prefix + *it);\n\n    // Insert the files to the overall list\n    all_files_and_groups.insert(all_files_and_groups.end(),\n                                extra_files_and_groups.begin(),\n                                extra_files_and_groups.end());\n  }\n\n  // Filter out the ones we don't want\n  if constexpr (not std::is_same_v<ObjectType, void>) {\n    const auto range_end = std::remove_if(\n        all_files_and_groups.begin(), all_files_and_groups.end(),\n        [](const std::string& t) {\n          return t.find(ObjectType::extension()) == std::string::npos;\n        });\n    // Shrink down the vector\n    all_files_and_groups.erase(range_end, all_files_and_groups.end());\n    all_files_and_groups.shrink_to_fit();\n  }\n\n  return all_files_and_groups;\n}\n\ntemplate <AccessType Access_t>\ntemplate <typename ObjectType, typename... Args,\n          typename std::enable_if_t<((void)sizeof(ObjectType),\n                                     Access_t == AccessType::ReadWrite)>*>\nObjectType& H5File<Access_t>::get(const std::string& path, Args&&... args) {\n  // Ensure we call the const version of the get function to avoid infinite\n  // recursion. The reason this is implemented in this manner is to avoid code\n  // duplication.\n  // clang-tidy: do not use const_cast\n  return const_cast<ObjectType&>(  // NOLINT\n      static_cast<H5File<Access_t> const*>(this)->get<ObjectType>(\n          path, std::forward<Args>(args)...));\n}\n\ntemplate <AccessType Access_t>\ntemplate <typename ObjectType, typename... Args>\nconst ObjectType& H5File<Access_t>::get(const std::string& path,\n                                        Args&&... args) const {\n  if (current_object_ != nullptr) {\n    ERROR(\"Object \" << current_object_->subfile_path()\n                    << \" already open. Cannot open object \" << path << \".\");\n  }\n  // C++17: structured bindings\n  auto exists_group_name = check_if_object_exists<ObjectType>(path);\n  hid_t group_id = std::get<1>(exists_group_name).id();\n  if (not std::get<0>(exists_group_name)) {\n    ERROR(\"Cannot open the object '\" << path + ObjectType::extension()\n                                     << \"' because it does not exist.\");\n  }\n  current_object_ = std::make_unique<ObjectType>(\n      std::get<0>(exists_group_name), std::move(std::get<1>(exists_group_name)),\n      group_id, std::move(std::get<2>(exists_group_name)),\n      std::forward<Args>(args)...);\n  return dynamic_cast<const ObjectType&>(*current_object_);\n}\n\ntemplate <AccessType Access_t>\ntemplate <typename ObjectType, typename... Args>\nObjectType& H5File<Access_t>::insert(const std::string& path, Args&&... args) {\n  static_assert(AccessType::ReadWrite == Access_t,\n                \"Can only insert into ReadWrite access H5 files.\");\n  if (current_object_ != nullptr) {\n    ERROR(\"Object \" << current_object_->subfile_path()\n                    << \" already open. Cannot insert object \" << path << \".\");\n  }\n  // C++17: structured bindings\n  auto exists_group_name = check_if_object_exists<ObjectType>(path);\n  if (std::get<0>(exists_group_name)) {\n    ERROR(\n        \"Cannot insert an Object that already exists. Failed to add Object \"\n        \"named: \"\n        << path);\n  }\n\n  hid_t group_id = std::get<1>(exists_group_name).id();\n  return convert_to_derived<ObjectType>(\n      current_object_ = std::make_unique<ObjectType>(\n          std::get<0>(exists_group_name),\n          std::move(std::get<1>(exists_group_name)), group_id,\n          std::move(std::get<2>(exists_group_name)),\n          std::forward<Args>(args)...));\n}\n\ntemplate <AccessType Access_t>\ntemplate <typename ObjectType, typename... Args>\nObjectType& H5File<Access_t>::try_insert(const std::string& path,\n                                         Args&&... args) {\n  static_assert(AccessType::ReadWrite == Access_t,\n                \"Can only insert into ReadWrite access H5 files.\");\n  if (current_object_ != nullptr) {\n    ERROR(\"Object \" << current_object_->subfile_path()\n                    << \" already open. Cannot try to insert object \" << path\n                    << \".\");\n  }\n  // C++17: structured bindings\n  auto exists_group_name = check_if_object_exists<ObjectType>(path);\n  hid_t group_id = std::get<1>(exists_group_name).id();\n  return convert_to_derived<ObjectType>(\n      current_object_ = std::make_unique<ObjectType>(\n          std::get<0>(exists_group_name),\n          std::move(std::get<1>(exists_group_name)), group_id,\n          std::move(std::get<2>(exists_group_name)),\n          std::forward<Args>(args)...));\n}\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <AccessType Access_t>\ntemplate <typename ObjectType,\n          typename std::enable_if_t<((void)sizeof(ObjectType),\n                                     Access_t == AccessType::ReadWrite)>*>\nObjectType& H5File<Access_t>::convert_to_derived(\n    std::unique_ptr<h5::Object>& current_object) {\n  if (nullptr == current_object) {\n    ERROR(\"No object to convert.\");  // LCOV_EXCL_LINE\n  }\n  try {\n    return dynamic_cast<ObjectType&>(*current_object);\n    // LCOV_EXCL_START\n  } catch (const std::bad_cast& e) {\n    ERROR(\"Failed to cast to object.\\nCast error: \" << e.what());\n    // LCOV_EXCL_STOP\n  }\n}\ntemplate <AccessType Access_t>\ntemplate <typename ObjectType>\nconst ObjectType& H5File<Access_t>::convert_to_derived(\n    const std::unique_ptr<h5::Object>& current_object) const {\n  if (nullptr == current_object) {\n    ERROR(\"No object to convert.\");\n  }\n  try {\n    return dynamic_cast<const ObjectType&>(*current_object);\n  } catch (const std::bad_cast& e) {\n    ERROR(\"Failed to cast to object.\\nCast error: \" << e.what());\n  }\n}\n\ntemplate <AccessType Access_t>\ntemplate <typename ObjectType>\nstd::tuple<bool, detail::OpenGroup, std::string>\nH5File<Access_t>::check_if_object_exists(std::string path) const {\n  // Normalize path to have a leading slash and the correct extension\n  if (path.front() != '/') {\n    path = '/' + path;\n  }\n  const size_t extension_length = ObjectType::extension().size();\n  if (path.size() > extension_length and\n      path.substr(path.size() - extension_length) == ObjectType::extension()) {\n    path = path.substr(0, path.size() - extension_length);\n  }\n  std::string name_only = \"/\";\n  if (path != \"/\") {\n    name_only = file_system::get_file_name(path);\n  }\n  const std::string name_with_extension = name_only + ObjectType::extension();\n  detail::OpenGroup group(file_id_, file_system::get_parent_path(path),\n                          Access_t);\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  const bool object_exists =\n      name_with_extension == \"/\" or\n      H5Lexists(group.id(), name_with_extension.c_str(), H5P_DEFAULT) or\n      H5Aexists(group.id(), name_with_extension.c_str());\n#pragma GCC diagnostic pop\n  return std::make_tuple(object_exists, std::move(group), std::move(name_only));\n}\n\ntemplate <>\ninline void H5File<AccessType::ReadWrite>::insert_header() {\n  insert<h5::Header>(\"/header\");\n}\n// Not tested because it is only required to get code to compile, if statement\n// in constructor prevents call.\ntemplate <>\ninline void H5File<AccessType::ReadOnly>::insert_header() {}  // LCOV_EXCL_LINE\n\n/// \\endcond\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Header.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Header.hpp\"\n\n#include <algorithm>\n#include <regex>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/Helpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/Formaline.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace h5 {\nHeader::Header(const bool exists, detail::OpenGroup&& group,\n               const hid_t location, const std::string& name)\n    : group_(std::move(group)),\n      path_(group_.group_path_with_trailing_slash() + name) {\n  if (exists) {\n    header_info_ =\n        h5::read_rank1_attribute<std::string>(location, name + extension())[0];\n    if (header_info_.find(printenv_delimiter_) != std::string::npos) {\n      const auto printenv_location =\n          header_info_.find(printenv_delimiter_) + printenv_delimiter_.size();\n      const auto build_info_location = header_info_.find(build_info_delimiter_);\n      environment_variables_ = header_info_.substr(\n          printenv_location, build_info_location - printenv_location);\n      build_info_ = header_info_.substr(\n          build_info_location + build_info_delimiter_.size());\n      header_info_.erase(printenv_location - printenv_delimiter_.size());\n    }\n\n    else {\n      // If we cannot find the Formaline delimiter in the file then the file\n      // was written without Formaline support and so we fill in fake info.\n      environment_variables_ =\n          \"Formaline was not supported when file was written\";\n      build_info_ = \"Formaline was not supported when file was written\";\n    }\n  } else {\n    auto build_info = info_from_build();\n    environment_variables_ = formaline::get_environment_variables();\n    build_info_ = formaline::get_build_info();\n    header_info_ = MakeString{}\n                   << \"#\\n# File created on \" << current_date_and_time() << \"# \"\n                   << std::regex_replace(build_info, std::regex{\"\\n\"}, \"\\n# \");\n    write_to_attribute(\n        location, name + extension(),\n        std::vector<std::string>{\n            MakeString{} << header_info_ << printenv_delimiter_\n                         << environment_variables_\n                         << build_info_delimiter_ << build_info_});\n  }\n}\n\nstd::string Header::get_env_variables() const { return environment_variables_; }\n\nstd::string Header::get_build_info() const { return build_info_; }\n\nconst std::string Header::printenv_delimiter_{\n    \"############### printenv ###############\\n\"};\nconst std::string Header::build_info_delimiter_{\n    \"############### library versions ###############\\n\"};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Header.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class h5::Header\n\n#pragma once\n\n#include <hdf5.h>\n#include <string>\n\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Writes header info about the build, git commit, branch, etc.\n *\n * A Header object is used to store the ::info_from_build() result in the HDF5\n * files. The Header is automatically added to every single file by the\n * constructor of H5File.\n *\n * \\example\n * You can read the header info out of an H5 file as shown in the example:\n * \\snippet Test_H5File.cpp h5file_readwrite_get_header\n */\nclass Header : public h5::Object {\n public:\n  /// \\cond HIDDEN_SYMOLS\n  static std::string extension() { return \".hdr\"; }\n\n  Header(bool exists, detail::OpenGroup&& group, hid_t location,\n         const std::string& name);\n\n  Header(const Header& /*rhs*/) = delete;\n  Header& operator=(const Header& /*rhs*/) = delete;\n\n  Header(Header&& /*rhs*/) = delete;             // NOLINT\n  Header& operator=(Header&& /*rhs*/) = delete;  // NOLINT\n\n  ~Header() override = default;\n  /// \\endcond\n\n  const std::string& get_header() const { return header_info_; }\n\n  /// Returns the environment variables at compile time of the simulation that\n  /// produced the file\n  std::string get_env_variables() const;\n\n  /// Returns the contents of the `BuildInfo.txt` file generated by CMake\n  /// of the simulation that produced the file.\n  std::string get_build_info() const;\n\n  const std::string& subfile_path() const override { return path_; }\n\n private:\n  /// \\cond HIDDEN_SYMBOLS\n  detail::OpenGroup group_;\n  std::string environment_variables_;\n  std::string build_info_;\n  std::string header_info_;\n  std::string path_;\n\n  static const std::string printenv_delimiter_;\n  static const std::string build_info_delimiter_;\n  /// \\endcond\n};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Helpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Helpers.hpp\"\n\n#include <algorithm>\n#include <functional>\n#include <iterator>\n#include <numeric>\n#include <ostream>\n#include <string>\n#include <type_traits>\n#include <typeinfo>\n#include <vector>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/Type.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/StaticAssert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\nnamespace {\n// Converts input data (either a std::vector or DataVector) to a T. Depending\n// on the rank of the input data, the following outputs T are allowed:\n// rank 0: double or int\n// rank 1: DataVector or std::vector\n// rank 2,3: DataVector or boost::multiarray\ntemplate <size_t Rank, typename T>\nstruct VectorTo;\n\ntemplate <typename T>\nstruct VectorTo<0, T> {\n  static T apply(std::vector<T> raw_data,\n                 const std::array<hsize_t, 0>& /*size*/) {\n    return raw_data[0];\n  }\n};\n\ntemplate <typename T>\nstruct VectorTo<1, T> {\n  static T apply(T raw_data, const std::array<hsize_t, 1>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <>\nstruct VectorTo<2, DataVector> {\n  static DataVector apply(DataVector raw_data,\n                          const std::array<hsize_t, 2>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <typename T>\nstruct VectorTo<2, std::vector<T>> {\n  static std::vector<T> apply(std::vector<T> raw_data,\n                              const std::array<hsize_t, 2>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <typename T>\nstruct VectorTo<3, std::vector<T>> {\n  static std::vector<T> apply(std::vector<T> raw_data,\n                              const std::array<hsize_t, 3>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <typename T>\nstruct VectorTo<4, std::vector<T>> {\n  static std::vector<T> apply(std::vector<T> raw_data,\n                              const std::array<hsize_t, 4>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <typename T>\nstruct VectorTo<5, std::vector<T>> {\n  static std::vector<T> apply(std::vector<T> raw_data,\n                              const std::array<hsize_t, 5>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <typename T>\nstruct VectorTo<2, boost::multi_array<T, 2>> {\n  static boost::multi_array<T, 2> apply(const std::vector<T>& raw_data,\n                                        const std::array<hsize_t, 2>& size) {\n    DEBUG_STATIC_ASSERT(std::is_fundamental_v<T>,\n                        \"VectorTo is optimized for fundamentals. Need to \"\n                        \"use move semantics for handling generic data types.\");\n    boost::multi_array<T, 2> temp(boost::extents[size[0]][size[1]]);\n    for (size_t i = 0; i < size[0]; ++i) {\n      for (size_t j = 0; j < size[1]; ++j) {\n        temp[i][j] = raw_data[j + i * size[1]];\n      }\n    }\n    return temp;\n  }\n};\n\ntemplate <>\nstruct VectorTo<2, Matrix> {\n  static Matrix apply(const std::vector<double>& raw_data,\n                      const std::array<hsize_t, 2>& size) {\n    Matrix temp(size[0], size[1]);\n    for (size_t i = 0; i < size[0]; ++i) {\n      for (size_t j = 0; j < size[1]; ++j) {\n        temp(i, j) = raw_data[j + i * size[1]];\n      }\n    }\n    return temp;\n  }\n};\n\ntemplate <>\nstruct VectorTo<2, std::vector<std::vector<double>>> {\n  static std::vector<std::vector<double>> apply(\n      const std::vector<double>& raw_data, const std::array<hsize_t, 2>& size) {\n    std::vector<std::vector<double>> temp(size[0]);\n    for (size_t i = 0; i < size[0]; ++i) {\n      temp[i].resize(size[1]);\n      for (size_t j = 0; j < size[1]; ++j) {\n        temp[i][j] = raw_data[j + i * size[1]];\n      }\n    }\n    return temp;\n  }\n};\n\ntemplate <>\nstruct VectorTo<3, DataVector> {\n  static DataVector apply(DataVector raw_data,\n                          const std::array<hsize_t, 3>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <>\nstruct VectorTo<3, std::vector<float>> {\n  static std::vector<float> apply(std::vector<float> raw_data,\n                                  const std::array<hsize_t, 3>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <typename T>\nstruct VectorTo<3, boost::multi_array<T, 3>> {\n  static boost::multi_array<T, 3> apply(const std::vector<T>& raw_data,\n                                        const std::array<hsize_t, 3>& size) {\n    DEBUG_STATIC_ASSERT(std::is_fundamental_v<T>,\n                        \"VectorTo is optimized for fundamentals. Need to \"\n                        \"use move semantics for handling generic data types.\");\n    boost::multi_array<T, 3> temp(boost::extents[size[0]][size[1]][size[2]]);\n    for (size_t i = 0; i < size[0]; ++i) {\n      for (size_t j = 0; j < size[1]; ++j) {\n        for (size_t k = 0; k < size[2]; ++k) {\n          temp[i][j][k] = raw_data[k + j * size[2] + i * size[2] * size[1]];\n        }\n      }\n    }\n    return temp;\n  }\n};\n\ntemplate <>\nstruct VectorTo<5, std::vector<float>> {\n  static std::vector<float> apply(std::vector<float> raw_data,\n                                  const std::array<hsize_t, 5>& /*size*/) {\n    return raw_data;\n  }\n};\n\ntemplate <typename T>\nstruct VectorTo<5, boost::multi_array<T, 5>> {\n  static boost::multi_array<T, 5> apply(const std::vector<T>& raw_data,\n                                        const std::array<hsize_t, 5>& size) {\n    DEBUG_STATIC_ASSERT(std::is_fundamental_v<T>,\n                        \"VectorTo is optimized for fundamentals. Need to \"\n                        \"use move semantics for handling generic data types.\");\n    boost::multi_array<T, 5> temp(\n        boost::extents[size[0]][size[1]][size[2]][size[3]][size[4]]);\n    for (size_t i = 0; i < size[0]; ++i) {\n      for (size_t j = 0; j < size[1]; ++j) {\n        for (size_t k = 0; k < size[2]; ++k) {\n          for (size_t l = 0; l < size[3]; ++l) {\n            for (size_t m = 0; m < size[4]; ++m) {\n              temp[i][j][k][l][m] =\n                  raw_data[m + size[4] *\n                                   (l + size[3] *\n                                            (k + size[2] * (j + size[1] * i)))];\n            }\n          }\n        }\n      }\n    }\n    return temp;\n  }\n};\n}  // namespace\n\nnamespace h5 {\nbool types_equal(const hid_t dtype1, const hid_t dtype2) {\n  const htri_t equal = H5Tequal(dtype1, dtype2);\n  CHECK_H5(equal, \"Unable to compare H5 data types.\");\n  return equal > 0;\n}\n\ntemplate <typename T>\nvoid write_data(const hid_t group_id, const std::vector<T>& data,\n                const std::vector<size_t>& extents, const std::string& name,\n                const bool overwrite_existing) {\n  std::vector<hsize_t> chunk_size(extents.size());\n  for (size_t i = 0; i < chunk_size.size(); ++i) {\n    // Setting the target number of bytes per chunk to a power of 2 is important\n    // for reducing the cost of writing to disk. Setting to a non-power of 2\n    // increases the compression overhead by ~>10x.\n    constexpr size_t target_number_of_bytes_per_chunk = 131'072;\n    chunk_size[i] = target_number_of_bytes_per_chunk / sizeof(T) > extents[i]\n                        ? extents[i]\n                        : target_number_of_bytes_per_chunk / sizeof(T);\n  }\n  ASSERT(alg::none_of(extents, [](const size_t extent) { return extent == 0; }),\n         \"Got zero extent when trying to write data.\");\n\n  const std::vector<hsize_t> dims(extents.begin(), extents.end());\n  const hid_t space_id = H5Screate_simple(dims.size(), dims.data(), nullptr);\n  CHECK_H5(space_id, \"Failed to create dataspace\");\n  const hid_t contained_type = h5::h5_type<tt::get_fundamental_type_t<T>>();\n\n  // Check for available filters and write with GZIP+shuffle if available\n  const bool use_gzip_filter = []() {\n    if (not static_cast<bool>(H5Zfilter_avail(H5Z_FILTER_DEFLATE))) {\n      return false;\n    }\n    unsigned int filter_info = 0;\n    const auto status = H5Zget_filter_info(H5Z_FILTER_DEFLATE, &filter_info);\n    return status >= 0 and (filter_info & H5Z_FILTER_CONFIG_ENCODE_ENABLED) and\n           (filter_info & H5Z_FILTER_CONFIG_DECODE_ENABLED);\n  }();\n  const bool use_shuffle_filter = []() {\n    if (not static_cast<bool>(H5Zfilter_avail(H5Z_FILTER_SHUFFLE))) {\n      return false;\n    }\n    unsigned int filter_info = 0;\n    const auto status = H5Zget_filter_info(H5Z_FILTER_SHUFFLE, &filter_info);\n    return status >= 0 and (filter_info & H5Z_FILTER_CONFIG_ENCODE_ENABLED) and\n           (filter_info & H5Z_FILTER_CONFIG_DECODE_ENABLED);\n  }();\n\n  hid_t property_list = h5::h5p_default();\n  // We can't compress a single number. Since there's not much to reduce anyway,\n  // we just skip compression.\n  if (not extents.empty() and use_gzip_filter) {\n    property_list = H5Pcreate(H5P_DATASET_CREATE);\n    if (use_shuffle_filter) {\n      CHECK_H5(H5Pset_shuffle(property_list),\n               \"Failed to enable shuffle filter on dataset \" << name);\n    }\n    CHECK_H5(H5Pset_deflate(property_list, 5),\n             \"Failed to enable gzip filter on dataset \" << name);\n    CHECK_H5(H5Pset_chunk(property_list, chunk_size.size(), chunk_size.data()),\n             \"Failed to set chunk size on dataset \" << name);\n    CHECK_H5(H5Pset_fill_time(property_list, H5D_FILL_TIME_NEVER),\n             \"Failed to disable setting default values on dataset creation for \"\n             \"dataset \"\n                 << name);\n  }\n\n  if (H5Lexists(group_id, name.c_str(), h5::h5p_default()) != 0) {\n    if (not overwrite_existing) {\n      ERROR(\"Dataset already exists with name '\" << name << \"'.\");\n    }\n    // Possible optimization: resize existing dataset if it has the same\n    // dimensions.\n    CHECK_H5(H5Ldelete(group_id, name.c_str(), h5::h5p_default()),\n             \"Failed to delete existing dataset\");\n  }\n  const hid_t dataset_id =\n      H5Dcreate2(group_id, name.c_str(), contained_type, space_id,\n                 h5::h5p_default(), property_list, h5::h5p_default());\n  CHECK_H5(dataset_id, \"Failed to create dataset\");\n  CHECK_H5(H5Dwrite(dataset_id, contained_type, h5::h5s_all(), h5::h5s_all(),\n                    h5::h5p_default(), static_cast<const void*>(data.data())),\n           \"Failed to write data to dataset\");\n  CHECK_H5(H5Sclose(space_id), \"Failed to close dataspace\");\n  CHECK_H5(H5Dclose(dataset_id), \"Failed to close dataset\");\n}\n\nvoid write_data(const hid_t group_id, const DataVector& data,\n                const std::string& name, const bool overwrite_existing) {\n  const auto number_of_points = static_cast<hsize_t>(data.size());\n  const hid_t space_id = H5Screate_simple(1, &number_of_points, nullptr);\n  CHECK_H5(space_id, \"Failed to create dataspace\");\n  const hid_t contained_type = h5::h5_type<double>();\n  if (H5Lexists(group_id, name.c_str(), h5::h5p_default()) != 0) {\n    if (not overwrite_existing) {\n      ERROR(\"Dataset already exists with name '\" << name << \"'.\");\n    }\n    // Possible optimization: resize existing dataset if it has the same\n    // dimensions.\n    CHECK_H5(H5Ldelete(group_id, name.c_str(), h5::h5p_default()),\n             \"Failed to delete existing dataset\");\n  }\n  const hid_t dataset_id =\n      H5Dcreate2(group_id, name.c_str(), contained_type, space_id,\n                 h5::h5p_default(), h5::h5p_default(), h5::h5p_default());\n  CHECK_H5(dataset_id, \"Failed to create dataset\");\n  CHECK_H5(H5Dwrite(dataset_id, contained_type, h5::h5s_all(), h5::h5s_all(),\n                    h5::h5p_default(), static_cast<const void*>(data.data())),\n           \"Failed to write data to dataset\");\n  CHECK_H5(H5Sclose(space_id), \"Failed to close dataspace\");\n  CHECK_H5(H5Dclose(dataset_id), \"Failed to close dataset\");\n}\n\ntemplate <size_t Dim>\nvoid write_extents(const hid_t group_id, const Index<Dim>& extents,\n                   const std::string& name) {\n  // Write the current extents as an attribute to the group\n  const hsize_t size = Dim;\n  const hid_t space_id = H5Screate_simple(1, &size, nullptr);\n  CHECK_H5(space_id, \"Failed to create dataspace\");\n  const hid_t att_id = H5Acreate2(\n      group_id, name.c_str(), h5::h5_type<std::decay_t<decltype(extents[0])>>(),\n      space_id, h5p_default(), h5p_default());\n  CHECK_H5(att_id, \"Failed to create attribute\");\n  CHECK_H5(H5Awrite(att_id, h5::h5_type<std::decay_t<decltype(extents[0])>>(),\n                    static_cast<const void*>(extents.data())),\n           \"Failed to write extents\");\n  CHECK_H5(H5Sclose(space_id), \"Failed to close dataspace\");\n  CHECK_H5(H5Aclose(att_id), \"Failed to close attribute\");\n}\n\nvoid write_connectivity(const hid_t group_id,\n                        const std::vector<int>& connectivity) {\n  const hsize_t size = connectivity.size();\n  const hid_t space_id = H5Screate_simple(1, &size, nullptr);\n  CHECK_H5(space_id, \"Failed to create dataspace\");\n  const hid_t dataset_id =\n      H5Dcreate2(group_id, \"connectivity\", h5_type<int>(), space_id,\n                 h5p_default(), h5p_default(), h5p_default());\n  CHECK_H5(dataset_id, \"Failed to create dataset\");\n  CHECK_H5(\n      H5Dwrite(dataset_id, h5_type<int>(), h5s_all(), h5s_all(), h5p_default(),\n               static_cast<const void*>(connectivity.data())),\n      \"Failed to write connectivity\");\n  CHECK_H5(H5Sclose(space_id), \"Failed to close dataspace\");\n  CHECK_H5(H5Dclose(dataset_id), \"Failed to close dataset\");\n}\n\nvoid delete_connectivity(const hid_t group_id) {\n  H5Ldelete(group_id, \"connectivity\", h5p_default());\n}\n\nstd::array<hsize_t, 2> append_to_dataset(\n    const hid_t file_id, const std::string& name,\n    const std::vector<double>& data, const hsize_t number_of_rows,\n    const std::array<hsize_t, 2>& current_file_size) {\n  {\n    std::array<hsize_t, 2> read_size{};\n    std::array<hsize_t, 2> read_max_size{};\n    const hid_t dataspace_id = H5Dget_space(file_id);\n    CHECK_H5(dataspace_id, \"Failed to get dataspace for appending\");\n    if (2 != H5Sget_simple_extent_dims(dataspace_id, read_size.data(),\n                                       read_max_size.data())) {\n      ERROR(\"Incorrect rank of file on disk\");  // LCOV_EXCL_LINE\n    }\n    CHECK_H5(H5Sclose(dataspace_id), \"Failed to close dataspace\");\n    if (read_size != current_file_size) {\n      using ::operator<<;\n      ERROR(\"Mismatch in the size of the read dataset. Read \"\n            << read_size << \" but have stored \" << current_file_size\n            << \". This means that another thread or process is writing data at \"\n               \"the same time it is being written by this process.\");\n    }\n  }\n\n  std::array<hsize_t, 2> new_size{\n      {current_file_size[0] + number_of_rows, current_file_size[1]}};\n  CHECK_H5(H5Dset_extent(file_id, new_size.data()),\n           \"Failed to append to the file '\" << name << \"'\");\n  const hid_t dataspace_id = H5Dget_space(file_id);\n  CHECK_H5(dataspace_id, \"Failed to get dataspace for appending\");\n  CHECK_H5(H5Sselect_all(dataspace_id),\n           \"Failed to select dataspace for appending\");\n  CHECK_H5(H5Sselect_hyperslab(dataspace_id, H5S_SELECT_NOTB,\n                               std::array<hsize_t, 2>{{0, 0}}.data(), nullptr,\n                               current_file_size.data(), nullptr),\n           \"Failed to select the new dataspace subset where the appended \"\n           \"data would have been written.\");\n  const std::array<hsize_t, 2> added_size{\n      {number_of_rows, current_file_size[1]}};\n  const hid_t memspace_id =\n      H5Screate_simple(2, added_size.data(), added_size.data());\n  CHECK_H5(memspace_id, \"Failed to create new simple memspace while appending\");\n  CHECK_H5(H5Dwrite(file_id, h5_type<double>(), memspace_id, dataspace_id,\n                    h5::h5p_default(), data.data()),\n           \"Failed to append to dataset while writing\");\n  CHECK_H5(H5Sclose(memspace_id), \"Failed to close memspace after appending\");\n  CHECK_H5(H5Sclose(dataspace_id), \"Failed to close dataspace after appending\");\n\n  return new_size;\n}\n\ntemplate <typename T>\nT retrieve_dataset(const hid_t file_id,\n                   const std::array<hsize_t, 2>& file_size) {\n  static_assert(std::is_same_v<T, Matrix> or\n                    std::is_same_v<T, std::vector<std::vector<double>>>,\n                \"'retrieve_dataset' can only return a Matrix or a \"\n                \"std::vector<std::vector<double>>.\");\n  const hid_t dataspace_id = H5Dget_space(file_id);\n  CHECK_H5(dataspace_id, \"Failed to get dataspace\");\n\n  std::array<hsize_t, 2> size{};\n  std::array<hsize_t, 2> max_size{};\n  if (2 !=\n      H5Sget_simple_extent_dims(dataspace_id, size.data(), max_size.data())) {\n    ERROR(\"Incorrect dimension in get_data()\");  // LCOV_EXCL_LINE\n  }\n  CHECK_H5(H5Sclose(dataspace_id), \"Failed to close dataspace\");\n\n  if (size != file_size) {\n    using ::operator<<;\n    ERROR(\"Mismatch in the size of the read dataset. Read \"\n          << size << \" but have stored \" << file_size\n          << \". This means that another thread or process is writing data at \"\n             \"the same time it is being read.\");\n  }\n\n  std::vector<double> temp(size[0] * size[1]);\n  if (0 != size[0] * size[1]) {\n    CHECK_H5(H5Dread(file_id, h5_type<double>(), h5::h5s_all(), h5::h5s_all(),\n                     h5::h5p_default(), temp.data()),\n             \"Failed to read data\");\n  }\n  return VectorTo<2, T>::apply(temp, size);\n}\n\ntemplate <typename T>\nT retrieve_dataset_subset(const hid_t file_id,\n                          const std::vector<size_t>& these_columns,\n                          const size_t first_row, const size_t num_rows,\n                          const std::array<hsize_t, 2>& file_size) {\n  static_assert(std::is_same_v<T, Matrix> or\n                    std::is_same_v<T, std::vector<std::vector<double>>>,\n                \"'retrieve_dataset_subset' can only return a Matrix or a \"\n                \"std::vector<std::vector<double>>.\");\n  Expects(first_row + num_rows <= file_size[0]);\n  Expects(std::all_of(\n      these_columns.begin(), these_columns.end(),\n      [size = file_size](const auto& column) { return column < size[1]; }));\n\n  const auto num_cols = these_columns.size();\n  if (0 == num_cols * num_rows) {\n    if constexpr (std::is_same_v<T, Matrix>) {\n      return {num_rows, num_cols, 0.0};\n    } else {\n      // Columns don't matter because either there are 0 columns, or there are 0\n      // rows. Either way we don't size the columns\n      return std::vector<std::vector<double>>(num_rows);\n    }\n  }\n\n  const hid_t dataspace_id = H5Dget_space(file_id);\n  CHECK_H5(dataspace_id, \"Failed to get dataspace\");\n  std::array<hsize_t, 2> size{};\n  std::array<hsize_t, 2> max_size{};\n  if (2 !=\n      H5Sget_simple_extent_dims(dataspace_id, size.data(), max_size.data())) {\n    ERROR(\"Incorrect dimension in get_data()\");  // LCOV_EXCL_LINE\n  }\n  if (size != file_size) {\n    using ::operator<<;\n    CHECK_H5(H5Sclose(dataspace_id), \"Failed to close dataspace\");\n    ERROR(\"Mismatch in the size of the read dataset. Read \"\n          << size << \" but have stored \" << file_size\n          << \". This means that another thread or process is writing data at \"\n             \"the same time it is being read.\");\n  }\n\n  CHECK_H5(H5Sselect_none(dataspace_id),\n           \"Failed to select none of the dataspace\");\n  for (auto& column : these_columns) {\n    const std::array<hsize_t, 2> start{\n        {first_row, static_cast<hsize_t>(column)}};\n    // offset between blocks (have only one anyway)\n    const std::array<hsize_t, 2> stride{{1, 1}};\n    const std::array<hsize_t, 2> count{{1, 1}};\n    const std::array<hsize_t, 2> block{{num_rows, 1}};\n\n    CHECK_H5(H5Sselect_hyperslab(dataspace_id, H5S_SELECT_OR, start.data(),\n                                 stride.data(), count.data(), block.data()),\n             \"Failed to select column \" << column);\n  }\n\n  std::vector<double> raw_data(num_rows * num_cols);\n  const std::array<hsize_t, 2> memspace_size{{num_rows, num_cols}};\n  const hid_t memspace_id =\n      H5Screate_simple(2, memspace_size.data(), memspace_size.data());\n  CHECK_H5(memspace_id, \"Failed to create memory space\");\n  CHECK_H5(H5Dread(file_id, h5_type<double>(), memspace_id, dataspace_id,\n                   h5::h5p_default(), raw_data.data()),\n           \"Failed to read data subset\");\n\n  CHECK_H5(H5Sclose(memspace_id), \"Failed to close memory space\");\n  CHECK_H5(H5Sclose(dataspace_id), \"Failed to close dataspace\");\n  return VectorTo<2, T>::apply(raw_data,\n                               std::array<hsize_t, 2>{{num_rows, num_cols}});\n}\n\nstd::vector<std::string> get_group_names(const hid_t file_id,\n                                         const std::string& group_name) {\n  // Opens the group, loads the group info and then loops over all the groups\n  // retrieving their names and storing them in names\n  detail::OpenGroup my_group(file_id, group_name, AccessType::ReadOnly);\n  const hid_t group_id = my_group.id();\n  H5G_info_t group_info{};\n  std::string name;\n  std::vector<std::string> names;\n  CHECK_H5(H5Gget_info(group_id, &group_info), \"Failed to get group info\");\n  names.reserve(group_info.nlinks);\n  for (size_t i = 0; i < group_info.nlinks; ++i) {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n    const hsize_t size =\n        static_cast<hsize_t>(1) + static_cast<hsize_t>(H5Lget_name_by_idx(\n                                      group_id, \".\", H5_INDEX_NAME, H5_ITER_INC,\n                                      i, nullptr, 0, h5p_default()));\n    name.resize(size);\n    H5Lget_name_by_idx(group_id, \".\", H5_INDEX_NAME, H5_ITER_INC, i, &name[0],\n                       size, h5p_default());\n#pragma GCC diagnostic pop\n    // We need to remove the bloody trailing \\0...\n    name.pop_back();\n    names.push_back(name);\n  }\n  return names;\n}\n\nbool contains_dataset_or_group(const hid_t id, const std::string& group_name,\n                               const std::string& dataset_name) {\n  return alg::found(get_group_names(id, group_name), dataset_name);\n}\n\ntemplate <typename Type>\n// NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\nvoid write_to_attribute(const hid_t location_id, const std::string& name,\n                        const Type& value) {\n  const hid_t space_id = H5Screate(H5S_SCALAR);\n  CHECK_H5(space_id, \"Failed to create scalar\");\n  const hid_t att_id = H5Acreate2(location_id, name.c_str(), h5_type<Type>(),\n                                  space_id, h5p_default(), h5p_default());\n  CHECK_H5(att_id, \"Failed to create attribute '\" << name << \"'\");\n  if constexpr (std::is_same_v<Type, std::string>) {\n    const auto cstr = value.c_str();\n    const hid_t str_id = h5_type<Type>();\n    CHECK_H5(H5Awrite(att_id, str_id, &cstr),\n             \"Failed to write value: \" << value);\n    CHECK_H5(H5Tclose(str_id),\n             \"Failed to close string attribute '\" << name << \"'\");\n  } else {\n    CHECK_H5(\n        H5Awrite(att_id, h5_type<Type>(), static_cast<const void*>(&value)),\n        \"Failed to write value: \" << value);\n  }\n  CHECK_H5(H5Aclose(att_id), \"Failed to close attribute '\" << name << \"'\");\n  CHECK_H5(H5Sclose(space_id), \"Unable to close dataspace\");\n}\n\ntemplate <typename Type>\nType read_value_attribute(const hid_t location_id, const std::string& name) {\n  const htri_t attribute_exists = H5Aexists(location_id, name.c_str());\n  if (not attribute_exists) {\n    ERROR(\"Could not find attribute '\" << name << \"'\");  // LCOV_EXCL_LINE\n  }\n  const hid_t attribute_id = H5Aopen(location_id, name.c_str(), h5p_default());\n  CHECK_H5(attribute_id, \"Failed to open attribute '\" << name << \"'\");\n  Type value;\n  if constexpr (std::is_same_v<Type, std::string>) {\n    char* value_c = nullptr;\n    const hid_t str_id = h5_type<Type>();\n    // NOLINTNEXTLINE(readability-suspicious-call-argument)\n    CHECK_H5(H5Aread(attribute_id, str_id, &value_c),\n             \"Failed to read attribute '\" << name << \"'\");\n    value = std::string{value_c};\n    H5free_memory(value_c);\n    CHECK_H5(H5Tclose(str_id),\n             \"Failed to close string attribute '\" << name << \"'\");\n  } else {\n    CHECK_H5(H5Aread(attribute_id, h5_type<Type>(), &value),\n             \"Failed to read attribute '\" << name << \"'\");\n  }\n  CHECK_H5(H5Aclose(attribute_id),\n           \"Failed to close attribute '\" << name << \"'\");\n  return value;\n}\n\ntemplate <typename T>\nvoid write_to_attribute(const hid_t group_id, const std::string& name,\n                        const std::vector<T>& data) {\n  const hsize_t size = data.size();\n  const hid_t space_id = H5Screate_simple(1, &size, nullptr);\n  CHECK_H5(space_id,\n           \"Failed to create dataspace for attribute  '\" << name << \"'\");\n  const hid_t att_id = H5Acreate2(group_id, name.c_str(), h5::h5_type<T>(),\n                                  space_id, h5p_default(), h5p_default());\n  CHECK_H5(att_id, \"Failed to create attribute '\" << name << \"'\");\n  CHECK_H5(\n      H5Awrite(att_id, h5::h5_type<T>(), static_cast<const void*>(data.data())),\n      \"Failed to write extents into attribute '\" << name << \"'\");\n  CHECK_H5(H5Sclose(space_id),\n           \"Failed to close dataspace when writing attribute '\" << name << \"'\");\n  CHECK_H5(H5Aclose(att_id),\n           \"Failed to close attribute '\" << name << \"' when writing it.\");\n}\n\ntemplate <typename T>\nstd::vector<T> read_rank1_attribute(const hid_t group_id,\n                                    const std::string& name) {\n  const hid_t attr_id = H5Aopen(group_id, name.c_str(), h5p_default());\n  CHECK_H5(attr_id, \"Failed to open attribute\");\n  {  // Check that the datatype in the file matches what we are reading.\n    const hid_t datatype_id = H5Aget_type(attr_id);\n    CHECK_H5(datatype_id, \"Failed to get datatype from attribute \" << name);\n    const hid_t datatype = H5Tget_native_type(datatype_id, H5T_DIR_DESCEND);\n    const auto size = H5Tget_size(datatype);\n    if (UNLIKELY(sizeof(T) != size)) {\n      ERROR(\"The read HDF5 type of the attribute (\"\n            << datatype\n            << \") has a different size than the type we are reading. The \"\n               \"stored size is \"\n            << size << \" while the expected size is \" << sizeof(T));\n    }\n    CHECK_H5(H5Tclose(datatype_id),\n             \"Failed to close datatype while reading attribute \" << name);\n  }\n  const auto size = [&attr_id, &name] {\n    const hid_t dataspace_id = H5Aget_space(attr_id);\n    const auto rank_of_space = H5Sget_simple_extent_ndims(dataspace_id);\n    if (UNLIKELY(rank_of_space < 0)) {\n      ERROR(\"Failed to get the rank of the dataspace inside the attribute \"\n            << name);\n    }\n    if (UNLIKELY(rank_of_space != 1)) {\n      ERROR(\n          \"The rank of the dataspace being read by read_rank1_attribute should \"\n          \"be 1 but is \"\n          << rank_of_space);\n    }\n    std::array<hsize_t, 1> dims{};\n    if (UNLIKELY(H5Sget_simple_extent_dims(dataspace_id, dims.data(),\n                                           nullptr) != 1)) {\n      ERROR(\n          \"The rank of the dataspace has changed after checking its rank. \"\n          \"Checked rank was \"\n          << rank_of_space);\n    }\n    H5Sclose(dataspace_id);\n    return dims[0];\n  }();\n  std::vector<T> data(size);\n  CHECK_H5(H5Aread(attr_id, h5::h5_type<T>(), data.data()),\n           \"Failed to read data from attribute \" << name);\n  H5Aclose(attr_id);\n  return data;\n}\n\ntemplate <>\nvoid write_to_attribute<std::string>(const hid_t group_id,\n                                     const std::string& name,\n                                     const std::vector<std::string>& data) {\n  // See the HDF5 example:\n  // https://support.hdfgroup.org/ftp/HDF5/examples/examples-by-api/\n  // hdf5-examples/1_8/C/H5T/h5ex_t_stringatt.c\n\n  const hid_t type_id = fortran_string();\n  // Create dataspace and attribute in dataspace where we will store the strings\n  const hsize_t dim = data.size();\n  const hid_t space_id = H5Screate_simple(1, &dim, nullptr);\n  CHECK_H5(space_id, \"Failed to create null space\");\n  const hid_t attr_id = H5Acreate2(group_id, name.c_str(), type_id, space_id,\n                                   h5p_default(), h5p_default());\n  CHECK_H5(attr_id, \"Could not create attribute \" << name);\n\n  // We are using C-style strings, which is type to be written into attribute\n  const auto memtype_id = h5_type<std::string>();\n\n  // In order to write strings to an attribute we must have a pointer to\n  // pointers, so we use a vector.\n  std::vector<const char*> string_pointers(data.size());\n  std::transform(data.begin(), data.end(), string_pointers.begin(),\n                 [](const auto& t) { return t.c_str(); });\n  CHECK_H5(H5Awrite(attr_id, memtype_id, string_pointers.data()),\n           \"Failed attribute write\");\n\n  CHECK_H5(H5Aclose(attr_id), \"Failed to close attribute \" << name);\n  CHECK_H5(H5Sclose(space_id), \"Failed to close space_id\");\n  CHECK_H5(H5Tclose(memtype_id), \"Failed to close memtype_id\");\n  CHECK_H5(H5Tclose(type_id), \"Failed to close type_id\");\n}\n\ntemplate <>\nstd::vector<std::string> read_rank1_attribute<std::string>(\n    const hid_t group_id, const std::string& name) {\n  const auto attribute_exists =\n      static_cast<bool>(H5Aexists(group_id, name.c_str()));\n  if (not attribute_exists) {\n    ERROR(\"Could not find attribute '\" << name << \"'\");  // LCOV_EXCL_LINE\n  }\n\n  // Open attribute that holds the strings\n  const hid_t attribute_id = H5Aopen(group_id, name.c_str(), h5p_default());\n  CHECK_H5(attribute_id, \"Failed to open attribute: '\" << name << \"'\");\n  const hid_t dataspace_id = H5Aget_space(attribute_id);\n  CHECK_H5(dataspace_id,\n           \"Failed to open dataspace for attribute '\" << name << \"'\");\n  // Get the size of the strings\n  std::array<hsize_t, 1> legend_dims{};\n  CHECK_H5(H5Sget_simple_extent_dims(dataspace_id, legend_dims.data(), nullptr),\n           \"Failed to get size of strings\");\n  // Read the strings as arrays of characters\n  std::vector<char*> temp(legend_dims[0]);\n  const hid_t memtype = h5_type<std::string>();\n  const hid_t type_in_file = H5Aget_type(attribute_id);\n  CHECK_H5(type_in_file,\n           \"Failed to read attribute type from file. Attribute name is '\"\n               << name << \"'\");\n  if (H5Tis_variable_str(type_in_file) < 0) {\n    char* type_name_ptr = H5Tget_tag(type_in_file);\n    const std::string type_name{type_name_ptr};\n    ERROR(\"The attribute type should be a variable string but got \"\n          << type_name);\n    free(type_name_ptr);  // NOLINT\n  }\n  if (H5Tget_cset(type_in_file) != H5T_CSET_ASCII) {\n    ERROR(\"Expected ASCII-encoded string but got H5T_cset_t of value \"\n          << H5Tget_cset(type_in_file)\n          << \". Likely the string is UTF8 encoded.\");\n  }\n  CHECK_H5(\n      H5Aread(attribute_id, memtype, static_cast<void*>(temp.data())),\n      \"Failed to read attribute: \" << name << \" with size \" << legend_dims);\n\n  std::vector<std::string> result(temp.size());\n  std::transform(temp.begin(), temp.end(), result.begin(),\n                 [](const auto& t) { return std::string(t); });\n\n  // Clean up memory from variable length arrays and close everything\n  CHECK_H5(H5Dvlen_reclaim(memtype, dataspace_id, h5p_default(), temp.data()),\n           \"Failed H5Dvlen_reclaim at \");\n  CHECK_H5(H5Aclose(attribute_id), \"Failed to close attribute\");\n  CHECK_H5(H5Sclose(dataspace_id), \"Failed to close space_id\");\n  CHECK_H5(H5Tclose(memtype), \"Failed to close memtype\");\n  return result;\n}\n\ntemplate <>\nvoid write_to_attribute<bool>(const hid_t group_id, const std::string& name,\n                              const std::vector<bool>& data) {\n  std::vector<unsigned short> temp(data.begin(), data.end());\n  write_to_attribute(group_id, name, temp);\n}\n\ntemplate <>\nstd::vector<bool> read_rank1_attribute<bool>(const hid_t group_id,\n                                             const std::string& name) {\n  const std::vector<unsigned short> temp =\n      read_rank1_attribute<unsigned short>(group_id, name);\n  return std::vector<bool>(temp.begin(), temp.end());\n}\n\ntemplate <typename T, size_t Size>\nvoid write_to_attribute(const hid_t group_id, const std::string& name,\n                        const std::vector<std::array<T, Size>>& data) {\n  static_assert(std::is_fundamental_v<T>);\n  const hsize_t size = data.size() * Size;\n  const hid_t space_id = H5Screate_simple(1, &size, nullptr);\n  CHECK_H5(space_id,\n           \"Failed to create dataspace for attribute  '\" << name << \"'\");\n  const hid_t att_id = H5Acreate2(group_id, name.c_str(), h5::h5_type<T>(),\n                                  space_id, h5p_default(), h5p_default());\n  CHECK_H5(att_id, \"Failed to create attribute '\" << name << \"'\");\n  CHECK_H5(\n      H5Awrite(att_id, h5::h5_type<T>(), static_cast<const void*>(data.data())),\n      \"Failed to write extents into attribute '\" << name << \"'\");\n  CHECK_H5(H5Sclose(space_id),\n           \"Failed to close dataspace when writing attribute '\" << name << \"'\");\n  CHECK_H5(H5Aclose(att_id),\n           \"Failed to close attribute '\" << name << \"' when writing it.\");\n}\n\ntemplate <typename T, size_t Size>\nstd::vector<std::array<T, Size>> read_rank1_array_attribute(\n    const hid_t group_id, const std::string& name) {\n  static_assert(std::is_fundamental_v<T>);\n  const hid_t attr_id = H5Aopen(group_id, name.c_str(), h5p_default());\n  CHECK_H5(attr_id, \"Failed to open attribute\");\n  {  // Check that the datatype in the file matches what we are reading.\n    const hid_t datatype_id = H5Aget_type(attr_id);\n    CHECK_H5(datatype_id, \"Failed to get datatype from attribute \" << name);\n    if (UNLIKELY(not h5::types_equal(datatype_id, h5::h5_type<T>()))) {\n      ERROR(\"Expected to read type \" << pretty_type::short_name<T>()\n                                     << \" for attribute \" << name);\n    }\n    CHECK_H5(H5Tclose(datatype_id),\n             \"Failed to close datatype while reading attribute \" << name);\n  }\n  const auto size = [&attr_id, &name] {\n    const hid_t dataspace_id = H5Aget_space(attr_id);\n    const auto rank_of_space = H5Sget_simple_extent_ndims(dataspace_id);\n    if (UNLIKELY(rank_of_space < 0)) {\n      ERROR(\"Failed to get the rank of the dataspace inside the attribute \"\n            << name);\n    }\n    if (UNLIKELY(rank_of_space != 1)) {\n      ERROR(\n          \"The rank of the dataspace being read by read_rank1_attribute should \"\n          \"be 1 but is \"\n          << rank_of_space);\n    }\n    std::array<hsize_t, 1> dims{};\n    if (UNLIKELY(H5Sget_simple_extent_dims(dataspace_id, dims.data(),\n                                           nullptr) != 1)) {\n      ERROR(\n          \"The rank of the dataspace has changed after checking its rank. \"\n          \"Checked rank was \"\n          << rank_of_space);\n    }\n    H5Sclose(dataspace_id);\n    if (UNLIKELY(dims[0] % Size != 0)) {\n      ERROR(\"The read size, \" << dims[0]\n                              << \", must be a multiple of the number of \"\n                                 \"elements in the std::array, \"\n                              << Size);\n    }\n    return dims[0] / Size;\n  }();\n  std::vector<std::array<T, Size>> data(size);\n  CHECK_H5(H5Aread(attr_id, h5::h5_type<T>(), data.data()),\n           \"Failed to read data from attribute \" << name);\n  H5Aclose(attr_id);\n  return data;\n}\n\nstd::vector<std::string> get_attribute_names(const hid_t file_id,\n                                             const std::string& group_name) {\n  // Opens the group, loads the group info and then loops over all the\n  // attributes retrieving their names and storing them in names\n  detail::OpenGroup my_group(file_id, group_name, AccessType::ReadOnly);\n  const hid_t group_id = my_group.id();\n  std::string name;\n  std::vector<std::string> names;\n#if H5_VERSION_GE(1, 12, 0)\n  H5O_info1_t group_info{};\n  CHECK_H5(H5Oget_info1(group_id, &group_info), \"Failed to get group info\");\n#else\n  H5O_info_t group_info{};\n  CHECK_H5(H5Oget_info(group_id, &group_info), \"Failed to get group info\");\n#endif\n  names.reserve(group_info.num_attrs);\n  for (size_t i = 0; i < group_info.num_attrs; ++i) {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n    const hsize_t size =\n        static_cast<hsize_t>(1) + static_cast<hsize_t>(H5Aget_name_by_idx(\n                                      group_id, \".\", H5_INDEX_NAME, H5_ITER_INC,\n                                      i, nullptr, 0, h5p_default()));\n    name.resize(size);\n    H5Aget_name_by_idx(group_id, \".\", H5_INDEX_NAME, H5_ITER_INC, i, &name[0],\n                       size, h5p_default());\n#pragma GCC diagnostic pop\n    // We need to remove the bloody trailing \\0...\n    name.pop_back();\n    names.push_back(name);\n  }\n  return names;\n}\n\nbool contains_attribute(const hid_t file_id, const std::string& group_name,\n                        const std::string& attribute_name) {\n  const std::vector<std::string> names(\n      get_attribute_names(file_id, group_name));\n  return std::find(std::begin(names), std::end(names), attribute_name) !=\n         std::end(names);\n}\n\nhid_t open_dataset(const hid_t group_id, const std::string& dataset_name) {\n  const hid_t dataset_id =\n      H5Dopen2(group_id, dataset_name.c_str(), h5p_default());\n  CHECK_H5(dataset_id, \"Failed to open dataset '\" << dataset_name << \"'\");\n  return dataset_id;\n}\n\nvoid close_dataset(const hid_t dataset_id) {\n  CHECK_H5(H5Dclose(dataset_id), \"Failed to close dataset\");\n}\n\nhid_t open_dataspace(const hid_t dataset_id) {\n  const hid_t dataspace_id = H5Dget_space(dataset_id);\n  CHECK_H5(dataspace_id, \"Failed to open dataspace\");\n  return dataspace_id;\n}\n\nvoid close_dataspace(const hid_t dataspace_id) {\n  CHECK_H5(H5Sclose(dataspace_id), \"Failed to close dataspace\");\n}\n\ntemplate <size_t Rank, typename T>\nT read_data(const hid_t group_id, const std::string& dataset_name) {\n  const hid_t dataset_id = open_dataset(group_id, dataset_name);\n  const hid_t dataspace_id = open_dataspace(dataset_id);\n  std::array<hsize_t, Rank> size{};\n  std::array<hsize_t, Rank> max_size{};\n  if (static_cast<int>(Rank) !=\n      H5Sget_simple_extent_dims(dataspace_id, nullptr, nullptr)) {\n    ERROR(\"Incorrect rank in get_data(). Expected rank = \"\n          << Rank << \" but received array with rank = \"\n          << H5Sget_simple_extent_dims(dataspace_id, nullptr, nullptr));\n  }\n  H5Sget_simple_extent_dims(dataspace_id, size.data(), max_size.data());\n  close_dataspace(dataspace_id);\n\n  // Load data from the H5 file by passing a pointer to 'data' to H5Dread\n  // The type of 'data' is determined by the template parameter 'T'\n  const size_t total_number_of_components = std::accumulate(\n      size.begin(), size.end(), static_cast<size_t>(1), std::multiplies<>());\n  if (UNLIKELY(total_number_of_components == 0)) {\n    using ::operator<<;\n    ERROR(\"At least one element in 'size' is 0. Expected data along \"\n          << Rank << \" dimensions. size = \" << size);\n  }\n  tmpl::conditional_t<std::is_same_v<T, DataVector>, DataVector,\n                      std::vector<tt::get_fundamental_type_t<T>>>\n      data(total_number_of_components);\n\n  CHECK_H5(H5Dread(dataset_id, h5_type<tt::get_fundamental_type_t<T>>(),\n                   h5s_all(), h5s_all(), h5p_default(), data.data()),\n           \"Failed to read dataset: '\" << dataset_name << \"'\");\n  close_dataset(dataset_id);\n  return VectorTo<Rank, T>::apply(std::move(data), size);\n}\n\ntemplate <size_t Dim>\nIndex<Dim> read_extents(const hid_t group_id, const std::string& extents_name) {\n  const hid_t attr_id = H5Aopen(group_id, extents_name.c_str(), h5p_default());\n  CHECK_H5(attr_id, \"Failed to open attribute\");\n  Index<Dim> extents;\n  CHECK_H5(H5Aread(attr_id, h5::h5_type<std::decay_t<decltype(extents[0])>>(),\n                   extents.data()),\n           \"Failed to read extents\");\n  H5Aclose(attr_id);\n  return extents;\n}\n\n// Explicit instantiations\ntemplate void write_extents<1>(const hid_t group_id, const Index<1>& extents,\n                               const std::string& name);\ntemplate void write_extents<2>(const hid_t group_id, const Index<2>& extents,\n                               const std::string& name);\ntemplate void write_extents<3>(const hid_t group_id, const Index<3>& extents,\n                               const std::string& name);\n\ntemplate Index<1> read_extents<1>(const hid_t group_id,\n                                  const std::string& extents_name);\ntemplate Index<2> read_extents<2>(const hid_t group_id,\n                                  const std::string& extents_name);\ntemplate Index<3> read_extents<3>(const hid_t group_id,\n                                  const std::string& extents_name);\n\n#define TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define RANK(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_WRITE_DATA(_, DATA)                            \\\n  template void write_data<TYPE(DATA)>(                            \\\n      const hid_t group_id, const std::vector<TYPE(DATA)>& data,   \\\n      const std::vector<size_t>& extents, const std::string& name, \\\n      bool overwrite_existing);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_WRITE_DATA,\n                        (float, double, int, unsigned int, long, unsigned long,\n                         long long, unsigned long long, char))\n\n#define INSTANTIATE_ATTRIBUTE(_, DATA)                                    \\\n  template void write_to_attribute<TYPE(DATA)>(                           \\\n      const hid_t group_id, const std::string& name,                      \\\n      const std::vector<TYPE(DATA)>& data);                               \\\n  template void write_to_attribute<TYPE(DATA)>(const hid_t location_id,   \\\n                                               const std::string& name,   \\\n                                               const TYPE(DATA) & value); \\\n  template TYPE(DATA) read_value_attribute<TYPE(DATA)>(                   \\\n      const hid_t location_id, const std::string& name);                  \\\n  template std::vector<TYPE(DATA)> read_rank1_attribute<TYPE(DATA)>(      \\\n      const hid_t group_id, const std::string& name);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_ATTRIBUTE,\n                        (double, unsigned int, unsigned long, int))\n\ntemplate std::string read_value_attribute<std::string>(const hid_t location_id,\n                                                       const std::string& name);\ntemplate void write_to_attribute(const hid_t location_id,\n                                 const std::string& name,\n                                 const std::string& value);\n// std::vector<bool> is annoying, so we instantiate single bools separately\ntemplate void write_to_attribute(const hid_t location_id,\n                                 const std::string& name, const bool& value);\ntemplate bool read_value_attribute<bool>(const hid_t location_id,\n                                         const std::string& name);\ntemplate Matrix retrieve_dataset(const hid_t file_id,\n                                 const std::array<hsize_t, 2>& file_size);\ntemplate std::vector<std::vector<double>> retrieve_dataset(\n    const hid_t file_id, const std::array<hsize_t, 2>& file_size);\ntemplate Matrix retrieve_dataset_subset(\n    const hid_t file_id, const std::vector<size_t>& these_columns,\n    const size_t first_row, const size_t num_rows,\n    const std::array<hsize_t, 2>& file_size);\ntemplate std::vector<std::vector<double>> retrieve_dataset_subset(\n    const hid_t file_id, const std::vector<size_t>& these_columns,\n    const size_t first_row, const size_t num_rows,\n    const std::array<hsize_t, 2>& file_size);\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_ATTRIBUTE_ARRAY(_, DATA)                        \\\n  template void write_to_attribute<TYPE(DATA), DIM(DATA)>(          \\\n      hid_t group_id, const std::string& name,                      \\\n      const std::vector<std::array<TYPE(DATA), DIM(DATA)>>& data);  \\\n  template std::vector<std::array<TYPE(DATA), DIM(DATA)>>           \\\n  read_rank1_array_attribute<TYPE(DATA), DIM(DATA)>(hid_t group_id, \\\n                                                    const std::string& name);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_ATTRIBUTE_ARRAY,\n                        (float, double, int, unsigned int, long, unsigned long,\n                         long long, unsigned long long, char),\n                        (1, 2, 3, 4, 5, 6, 7, 8, 9, 10))\n\n#undef INSTANTIATE_ATTRIBUTE_ARRAY\n#undef DIM\n\n#define INSTANTIATE_READ_SCALAR(_, DATA)                 \\\n  template TYPE(DATA) read_data<RANK(DATA), TYPE(DATA)>( \\\n      const hid_t group_id, const std::string& dataset_name);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_READ_SCALAR,\n                        (float, double, int, unsigned int, long, unsigned long,\n                         long long, unsigned long long, char),\n                        (0))\n\n#define INSTANTIATE_READ_VECTOR(_, DATA)          \\\n  template std::vector<TYPE(DATA)>                \\\n  read_data<RANK(DATA), std::vector<TYPE(DATA)>>( \\\n      const hid_t group_id, const std::string& dataset_name);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_READ_VECTOR,\n                        (float, double, int, unsigned int, long, unsigned long,\n                         long long, unsigned long long, char),\n                        (1, 2, 3, 4, 5))\n\n#undef INSTANTIATE_READ_VECTOR\n\n#define INSTANTIATE_READ_MULTIARRAY(_, DATA)                         \\\n  template boost::multi_array<TYPE(DATA), RANK(DATA)>                \\\n  read_data<RANK(DATA), boost::multi_array<TYPE(DATA), RANK(DATA)>>( \\\n      const hid_t group_id, const std::string& dataset_name);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_READ_MULTIARRAY,\n                        (double, int, unsigned int, long, unsigned long,\n                         long long, unsigned long long, char),\n                        (2, 3, 5))\n\n#define INSTANTIATE_READ_DATAVECTOR(_, DATA)             \\\n  template TYPE(DATA) read_data<RANK(DATA), TYPE(DATA)>( \\\n      const hid_t group_id, const std::string& dataset_name);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_READ_DATAVECTOR, (DataVector), (1, 2, 3))\n\n#undef INSTANTIATE_ATTRIBUTE\n#undef INSTANTIATE_WRITE_DATA\n#undef INSTANTIATE_READ_SCALAR\n#undef INSTANTIATE_READ_VECTOR\n#undef INSTANTIATE_READ_MULTIARRAY\n#undef INSTANTIATE_READ_DATAVECTOR\n#undef TYPE\n#undef RANK\n}  // namespace h5\n\nnamespace h5::detail {\ntemplate <size_t Dims>\nhid_t create_extensible_dataset(const hid_t group_id, const std::string& name,\n                                const std::array<hsize_t, Dims>& initial_size,\n                                const std::array<hsize_t, Dims>& chunk_size,\n                                const std::array<hsize_t, Dims>& max_size) {\n  const hid_t dataspace_id =\n      H5Screate_simple(Dims, initial_size.data(), max_size.data());\n  CHECK_H5(dataspace_id, \"Failed to create extensible dataspace\");\n\n  const auto property_list = H5Pcreate(H5P_DATASET_CREATE);\n  CHECK_H5(property_list, \"Failed to create property list\");\n  CHECK_H5(H5Pset_chunk(property_list, Dims, chunk_size.data()),\n           \"Failed to set chunk size\");\n  CHECK_H5(H5Pset_fill_time(property_list, H5D_FILL_TIME_NEVER),\n           \"Failed to disable setting default values on dataset creation for \"\n           \"dataset \"\n               << name);\n\n  const hid_t dataset_id =\n      H5Dcreate2(group_id, name.c_str(), h5_type<double>(), dataspace_id,\n                 h5p_default(), property_list, h5p_default());\n  CHECK_H5(dataset_id, \"Failed to create dataset\");\n  CHECK_H5(H5Pclose(property_list), \"Failed to close property list\");\n  CHECK_H5(H5Sclose(dataspace_id), \"Failed to close dataspace\");\n  return dataset_id;\n}\n\ntemplate hid_t create_extensible_dataset<1>(\n    const hid_t group_id, const std::string& name,\n    const std::array<hsize_t, 1>& initial_size,\n    const std::array<hsize_t, 1>& chunk_size,\n    const std::array<hsize_t, 1>& max_size);\ntemplate hid_t create_extensible_dataset<2>(\n    const hid_t group_id, const std::string& name,\n    const std::array<hsize_t, 2>& initial_size,\n    const std::array<hsize_t, 2>& chunk_size,\n    const std::array<hsize_t, 2>& max_size);\ntemplate hid_t create_extensible_dataset<3>(\n    const hid_t group_id, const std::string& name,\n    const std::array<hsize_t, 3>& initial_size,\n    const std::array<hsize_t, 3>& chunk_size,\n    const std::array<hsize_t, 3>& max_size);\n}  // namespace h5::detail\n"
  },
  {
    "path": "src/IO/H5/Helpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions for h5 manipulations\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <hdf5.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n\n/// \\cond\nclass DataVector;\nclass Matrix;\n/// \\endcond\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Check if `dtype1` and `dtype2` are the same HDF5 data type.\n */\nbool types_equal(hid_t dtype1, hid_t dtype2);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Write a std::vector named `name` to the group `group_id`\n */\ntemplate <typename T>\nvoid write_data(hid_t group_id, const std::vector<T>& data,\n                const std::vector<size_t>& extents,\n                const std::string& name = \"scalar\",\n                const bool overwrite_existing = false);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Write a DataVector named `name` to the group `group_id`\n */\nvoid write_data(hid_t group_id, const DataVector& data, const std::string& name,\n                const bool overwrite_existing = false);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Write the extents as an attribute named `name` to the group\n * `group_id`.\n */\ntemplate <size_t Dim>\nvoid write_extents(hid_t group_id, const Index<Dim>& extents,\n                   const std::string& name = \"Extents\");\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Write a value of type `Type` to an HDF5 attribute named `name`\n */\ntemplate <typename Type>\nvoid write_to_attribute(hid_t location_id, const std::string& name,\n                        const Type& value);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Write the vector `data` to the attribute `attribute_name` in the group\n * `group_id`.\n */\ntemplate <typename T>\nvoid write_to_attribute(hid_t group_id, const std::string& name,\n                        const std::vector<T>& data);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Write the `vector<array<fundamental, size>>` `data` to the attribute\n * `name` in the group `group_id`.\n */\ntemplate <typename T, size_t Size>\nvoid write_to_attribute(hid_t group_id, const std::string& name,\n                        const std::vector<std::array<T, Size>>& data);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Read a value of type `Type` from an HDF5 attribute named `name`\n */\ntemplate <typename Type>\nType read_value_attribute(hid_t location_id, const std::string& name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Read rank-1 of type `Type` from an HDF5 attribute named `name`\n */\ntemplate <typename Type>\nstd::vector<Type> read_rank1_attribute(hid_t group_id, const std::string& name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Read the `vector<array<fundamental, size>>` from the attribute\n * `name` in the group `group_id`.\n */\ntemplate <typename T, size_t Size>\nstd::vector<std::array<T, Size>> read_rank1_array_attribute(\n    hid_t group_id, const std::string& name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Get the names of all the attributes in a group\n */\nstd::vector<std::string> get_attribute_names(hid_t file_id,\n                                             const std::string& group_name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Write the connectivity into the group in the H5 file\n */\nvoid write_connectivity(hid_t group_id, const std::vector<int>& connectivity);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Delete the connectivity from the group in the H5 file\n */\nvoid delete_connectivity(hid_t group_id);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Append rows to an existing dataset\n *\n * \\return std::array<hsize_t, 2> New size of the data in the file\n */\nstd::array<hsize_t, 2> append_to_dataset(\n    hid_t file_id, const std::string& name, const std::vector<double>& data,\n    hsize_t number_of_rows, const std::array<hsize_t, 2>& current_file_size);\n\n/// @{\n/*!\n * \\ingroup HDF5Group\n * \\brief Convert the data in a dataset to a Matrix or a\n * std::vector<std::vector<double>>\n */\ntemplate <typename T>\nT retrieve_dataset(hid_t file_id, const std::array<hsize_t, 2>& file_size);\n\ntemplate <typename T>\nT retrieve_dataset_subset(hid_t file_id,\n                          const std::vector<size_t>& these_columns,\n                          size_t first_row, size_t num_rows,\n                          const std::array<hsize_t, 2>& file_size);\n/// @}\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Get the names of all the groups and datasets in a group\n */\nstd::vector<std::string> get_group_names(hid_t file_id,\n                                         const std::string& group_name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Check if `name` is a dataset or group in the subgroup `group_name` of\n * `id`.\n *\n * \\note To check the current id for `name`, pass `\"\"` as `group_name`.\n */\nbool contains_dataset_or_group(hid_t id, const std::string& group_name,\n                               const std::string& dataset_name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Check if an attribute is in a group\n */\nbool contains_attribute(hid_t file_id, const std::string& group_name,\n                        const std::string& attribute_name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Open an HDF5 dataset\n */\nhid_t open_dataset(hid_t group_id, const std::string& dataset_name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Close an HDF5 dataset\n */\nvoid close_dataset(hid_t dataset_id);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Open an HDF5 dataspace\n */\nhid_t open_dataspace(hid_t dataset_id);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Close an HDF5 dataspace\n */\nvoid close_dataspace(hid_t dataspace_id);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Read an array of rank 0-3 into an object.\n *\n * For each rank, the data can be read into objects of the following types:\n * rank 0: double or int\n * rank 1: std::vector or DataVector\n * rank 2: boost::multiarray or DataVector\n * rank 3: boost::multiarray or DataVector\n */\ntemplate <size_t Rank, typename T>\nT read_data(hid_t group_id, const std::string& dataset_name);\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Read the HDF5 attribute representing extents from a group\n */\ntemplate <size_t Dim>\nIndex<Dim> read_extents(hid_t group_id,\n                        const std::string& extents_name = \"Extents\");\n}  // namespace h5\n\nnamespace h5 {\nnamespace detail {\n/*!\n * \\ingroup HDF5Group\n * \\brief Create a dataset that can be extended/appended to\n *\n * \\requires group_id is an open group, each element of `initial_size` is less\n * than the respective element in `max_size`, and each element in `max_size` is\n * a positive integer or `H5S_UNLIMITED`\n * \\effects creates a potentially extensible dataset of dimension Dim inside the\n * group `group_id`\n * \\returns the HDF5 id to the created dataset\n *\n * See the tutorial at https://support.hdfgroup.org/HDF5/Tutor/extend.html\n * for details on the implementation choice.\n */\ntemplate <size_t Dims>\nhid_t create_extensible_dataset(hid_t group_id, const std::string& name,\n                                const std::array<hsize_t, Dims>& initial_size,\n                                const std::array<hsize_t, Dims>& chunk_size,\n                                const std::array<hsize_t, Dims>& max_size);\n}  // namespace detail\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Object.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class h5::Object abstract base class\n\n#pragma once\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Abstract base class representing an object in an HDF5 file\n */\nclass Object {\n public:\n  /// \\cond HIDDEN_SYMBOLS\n  Object() = default;\n  Object(const Object& /*rhs*/) = delete;\n  Object& operator=(const Object& /*rhs*/) = delete;\n  Object(Object&& /*rhs*/) = delete;             // NOLINT\n  Object& operator=(Object&& /*rhs*/) = delete;  // NOLINT\n  virtual ~Object() = default;\n  /// \\endcond\n\n  /// Return the path to the subfile where this object is stored\n  virtual const std::string& subfile_path() const = 0;\n};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/OpenGroup.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/OpenGroup.hpp\"\n\n#include <H5version.h>\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <regex>\n#include <sstream>\n\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace {\nstd::vector<std::string> split_strings(const std::string& s,\n                                       const bool match_multiple = true) {\n  std::regex split_delim(match_multiple ? R\"([^/]+)\" : R\"([^/])\");\n  auto words_begin = std::sregex_iterator(s.begin(), s.end(), split_delim);\n  auto words_end = std::sregex_iterator();\n  std::vector<std::string> split;\n  split.reserve(static_cast<size_t>(std::distance(words_begin, words_end)));\n  for (std::sregex_iterator i = words_begin; i != words_end; ++i) {\n    split.emplace_back(i->str());\n  }\n  return split;\n}\n}  // namespace\n\nnamespace h5::detail {\nOpenGroup::OpenGroup(hid_t file_id, const std::string& group_name,\n                     const h5::AccessType access_type)\n    : group_path_str_(group_name == \"/\" ? group_name : group_name + \"/\") {\n  const std::vector<std::string> path(split_strings(group_path_str_));\n  hid_t group_id = file_id;\n  group_path_.reserve(path.size());\n  for (const auto& current_group : path) {\n    if (not current_group.empty()) {\n      const auto status = H5Lexists(group_id, current_group.c_str(), 0);\n      if (0 < status) {\n        group_id = H5Gopen(group_id, current_group.c_str(), h5p_default());\n      } else if (0 == status) {\n        if (AccessType::ReadOnly == access_type) {\n          ERROR(\"Cannot create group '\" << current_group.c_str()\n                                        << \"' in path: \" << group_name\n                                        << \" because the access is ReadOnly\");\n        }\n        group_id = H5Gcreate(group_id, current_group.c_str(), h5p_default(),\n                             h5p_default(), h5p_default());\n      } else {\n        ERROR(\"Failed to open the group '\"\n              << current_group\n              << \"' because the file_id passed in is invalid, or because the \"\n                 \"group_id inside the OpenGroup constructor got corrupted. It \"\n                 \"is most likely that the file_id is invalid.\");\n      }\n      CHECK_H5(group_id, \"Failed to open group '\" << current_group << \"'\");\n      group_path_.push_back(group_id);\n    }\n  }\n  group_id_ = group_id;\n}\n\nOpenGroup::OpenGroup(OpenGroup&& rhs) {\n  group_path_ = std::move(rhs.group_path_);\n  // clang-tidy: moving trivial type has no effect: might change in future\n  group_id_ = std::move(rhs.group_id_);  // NOLINT\n  group_path_str_ = rhs.group_path_str_;\n\n  rhs.group_id_ = -1;\n}\n\nOpenGroup& OpenGroup::operator=(OpenGroup&& rhs) {\n  if (group_id_ != -1) {\n    for (auto rit = group_path_.rbegin(); rit != group_path_.rend(); ++rit) {\n      H5Gclose(*rit);\n    }\n  }\n\n  group_path_ = std::move(rhs.group_path_);\n  // clang-tidy: moving trivial type has no effect: might change in future\n  group_id_ = std::move(rhs.group_id_);  // NOLINT\n  group_path_str_ = rhs.group_path_str_;\n\n  rhs.group_id_ = -1;\n  return *this;\n}\n\nOpenGroup::~OpenGroup() {\n  if (group_id_ != -1) {\n    for (auto rit = group_path_.rbegin(); rit != group_path_.rend(); ++rit) {\n      H5Gclose(*rit);\n    }\n    // Allows destructor to be run manually in tests without relying on HDF5\n    // implementation\n    group_id_ = -1;\n  }\n}\n}  // namespace h5::detail\n"
  },
  {
    "path": "src/IO/H5/OpenGroup.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class OpenGroup for opening groups in HDF5\n\n#pragma once\n\n#include <hdf5.h>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/AccessType.hpp\"\n\nnamespace h5 {\nnamespace detail {\n/*!\n * \\ingroup HDF5Group\n * \\brief Open an H5 group\n *\n * Opens a group recursively on creation and closes the groups when destructed.\n */\nclass OpenGroup {\n public:\n  OpenGroup() = default;\n\n  /*!\n   * \\param file_id the root/base file/group id where to start opening\n   * \\param group_name the full path to the group to open\n   * \\param access_type either AccessType::ReadOnly or AccessType::ReadWrite\n   */\n  OpenGroup(hid_t file_id, const std::string& group_name,\n            h5::AccessType access_type);\n\n  /// \\cond HIDDEN_SYMBOLS\n  ~OpenGroup();\n  /// \\endcond\n\n  /// @{\n  /// \\cond HIDDEN_SYMBOLS\n  /// Copying does not make sense since the group will then be closed twice.\n  OpenGroup(const OpenGroup& /*rhs*/) = delete;\n  OpenGroup& operator=(const OpenGroup& /*rhs*/) = delete;\n  /// \\endcond\n  /// @}\n\n  OpenGroup(OpenGroup&& rhs);             // NOLINT\n  OpenGroup& operator=(OpenGroup&& rhs);  // NOLINT\n\n  const hid_t& id() const { return group_id_; }\n\n  const std::string& group_path_with_trailing_slash() const {\n    return group_path_str_;\n  }\n\n private:\n  /// \\cond HIDDEN_SYMBOLS\n  std::string group_path_str_;\n  std::vector<hid_t> group_path_;\n  hid_t group_id_{-1};\n  /// \\endcond\n};\n}  // namespace detail\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"IO/H5/Python/Cce.hpp\"\n#include \"IO/H5/Python/CombineH5.hpp\"\n#include \"IO/H5/Python/Dat.hpp\"\n#include \"IO/H5/Python/File.hpp\"\n#include \"IO/H5/Python/TensorData.hpp\"\n#include \"IO/H5/Python/VolumeData.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.Spectral\");\n  py_bindings::bind_h5file(m);\n  py_bindings::bind_h5dat(m);\n  py_bindings::bind_h5cce(m);\n  py_bindings::bind_h5vol(m);\n  py_bindings::bind_tensordata(m);\n  py_bindings::bind_h5combine(m);\n}\n"
  },
  {
    "path": "src/IO/H5/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyH5\")\n\nspectre_python_add_module(\n  H5\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  Cce.cpp\n  CombineH5.cpp\n  Dat.cpp\n  File.cpp\n  TensorData.cpp\n  VolumeData.cpp\n  PYTHON_FILES\n  __init__.py\n  CombineH5.py\n  CombineH5Dat.py\n  DeleteSubfiles.py\n  ExtendConnectivityData.py\n  ExtractDatFromH5.py\n  ExtractInputSourceYamlFromH5.py\n  FunctionsOfTimeFromVolume.py\n  InterpolateToMesh.py\n  IterElements.py\n  OpenVolfiles.py\n  ReadH5.py\n  TransformVolumeData.py\n  MODULE_PATH \"IO\"\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Cce.hpp\n  CombineH5.hpp\n  Dat.hpp\n  File.hpp\n  TensorData.hpp\n  VolumeData.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DomainCreators\n  DataStructures\n  H5\n  pybind11::module\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PySpectral\n  )\n"
  },
  {
    "path": "src/IO/H5/Python/Cce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Python/Cce.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/H5/Cce.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_h5cce(py::module& m) {\n  // Wrapper for basic H5Cce operations\n  py::class_<h5::Cce>(m, \"H5Cce\")\n      .def(\"append\", &h5::Cce::append, py::arg(\"data\"))\n      .def(\"get_legend\", &h5::Cce::get_legend)\n      // Use the matrix overload because it all gets converted to numpy arrays\n      // anways\n      .def(\"get_data\",\n           py::overload_cast<>(&h5::Cce::get_data<Matrix>, py::const_))\n      .def(\"get_data\",\n           py::overload_cast<const std::string&>(&h5::Cce::get_data<Matrix>,\n                                                 py::const_),\n           py::arg(\"bondi_variable_name\"))\n      .def(\"get_data_subset\",\n           py::overload_cast<const std::vector<size_t>&, size_t, size_t>(\n               &h5::Cce::get_data_subset<Matrix>, py::const_),\n           py::arg(\"ell\"), py::arg(\"first_row\") = 0, py::arg(\"num_rows\") = 1)\n      .def(\"get_data_subset\",\n           py::overload_cast<const std::string&, const std::vector<size_t>&,\n                             size_t, size_t>(&h5::Cce::get_data_subset<Matrix>,\n                                             py::const_),\n           py::arg(\"bondi_variable_name\"), py::arg(\"ell\"),\n           py::arg(\"first_row\") = 0, py::arg(\"num_rows\") = 1)\n      .def(\"get_dimensions\", &h5::Cce::get_dimensions)\n      .def(\"get_header\", &h5::Cce::get_header)\n      .def(\"get_version\", &h5::Cce::get_version);\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/Cce.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_h5cce(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/CombineH5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Python/CombineH5.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"IO/H5/CombineH5.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_h5combine(py::module& m) {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  // Wrapper for combining h5 files\n  m.def(\"combine_h5_vol\", &h5::combine_h5_vol, py::arg(\"file_names\"),\n        py::arg(\"subfile_name\"), py::arg(\"output\"), py::arg(\"start-time\"),\n        py::arg(\"stop-time\"), py::arg(\"blocks_to_combine\"),\n        py::arg(\"check_src\"));\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/CombineH5.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_h5combine(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/CombineH5.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\n\nimport click\nimport rich\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5.CombineH5Dat import combine_h5_dat_command\nfrom spectre.support.CliExceptions import RequiredChoiceError\n\n\n@click.group(name=\"combine-h5\")\ndef combine_h5_command():\n    \"\"\"Combines multiple HDF5 files\"\"\"\n    pass\n\n\ncombine_h5_command.add_command(combine_h5_dat_command, name=\"dat\")\n\n\n@combine_h5_command.command(name=\"vol\")\n@click.argument(\n    \"h5files\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=-1,\n    required=True,\n)\n@click.option(\n    \"--subfile-name\",\n    \"-d\",\n    help=\"subfile name of the volume file in the H5 file\",\n)\n@click.option(\n    \"--output\",\n    \"-o\",\n    required=True,\n    type=click.Path(\n        exists=False,\n        file_okay=True,\n        dir_okay=False,\n        writable=True,\n        readable=True,\n    ),\n    help=\"combined output filename\",\n)\n@click.option(\n    \"--start-time\",\n    type=float,\n    help=(\n        \"The earliest time at which to start visualizing. The start-time \"\n        \"value is included.\"\n    ),\n)\n@click.option(\n    \"--stop-time\",\n    type=float,\n    help=(\n        \"The time at which to stop visualizing. The stop-time value is \"\n        \"not included.\"\n    ),\n)\n@click.option(\n    \"--block\",\n    \"-b\",\n    \"block_or_group_names\",\n    multiple=True,\n    help=(\n        \"Name of block or block group to analyze. \"\n        \"Can be specified multiple times to plot several block(groups) at once.\"\n    ),\n)\n@click.option(\n    \"--check-src/--no-check-src\",\n    default=True,\n    show_default=True,\n    help=(\n        \"flag to check src files, True implies src files exist and can be\"\n        \" checked, False implies no src files to check.\"\n    ),\n)\ndef combine_h5_vol_command(\n    h5files,\n    subfile_name,\n    output,\n    start_time,\n    stop_time,\n    block_or_group_names,\n    check_src,\n):\n    \"\"\"Combines volume data spread over multiple H5 files into a single file\n\n    The typical use case is to combine volume data from multiple nodes into a\n    single file, if this is necessary for further processing (e.g. for the\n    'extend-connectivity' command). Note that for most use cases it is not\n    necessary to combine the volume data into a single file, as most commands\n    can operate on multiple input H5 files (e.g. 'generate-xdmf').\n\n    Note that this command does not currently combine volume data from different\n    time steps (e.g. from multiple segments of a simulation). All input H5 files\n    must contain the same set of observation IDs.\n    \"\"\"\n    # Print available subfile names and exit\n    if not subfile_name:\n        spectre_file = spectre_h5.H5File(h5files[0], \"r\")\n        raise RequiredChoiceError(\n            (\n                \"Specify '--subfile-name' / '-d' to select a\"\n                \" subfile containing volume data.\"\n            ),\n            choices=spectre_file.all_vol_files(),\n        )\n\n    if not output.endswith(\".h5\"):\n        output += \".h5\"\n\n    spectre_h5.combine_h5_vol(\n        h5files,\n        subfile_name,\n        output,\n        start_time,\n        stop_time,\n        block_or_group_names,\n        check_src,\n    )\n\n\nif __name__ == \"__main__\":\n    combine_h5_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/IO/H5/Python/CombineH5Dat.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport shutil\n\nimport click\nimport h5py\n\nfrom spectre.IO.H5 import available_subfiles\n\n\ndef combine_h5_dat(h5files, output, force):\n    \"\"\"Combines multiple HDF5 dat files\n\n    This executable is used for combining a series of HDF5 files, each\n    containing one or more dat files, into a single HDF5 file. A typical\n    use case is to join dat-containing HDF5 files from different segments\n    of a simulation, with each segment containing values of the dat files\n    during different time intervals.\n\n    \\f\n    Arguments:\n      h5files: List of H5 dat files to join\n      output: Output filename. An extension '.h5' will be added if not present.\n      force: If specified, overwrite output file if it already exists\n    \"\"\"\n    # Copy first input file to output file\n    if not output.endswith(\".h5\"):\n        output += \".h5\"\n    # If output file exists, exit unless the user specifies `--force`\n    if os.path.exists(output) and not force:\n        raise ValueError(f\"File '{output}' exists; to overwrite, use --force\")\n    shutil.copy(h5files[0], output)\n\n    # Open the output file for appending\n    with h5py.File(output, \"r+\") as out:\n        # Get a list of all dat file keys (keys ending in \".dat\")\n        dat_file_keys = available_subfiles(out, extension=\".dat\")\n\n        # Loop over remaining input files, appending each dat file\n        for input_file in h5files[1:]:\n            with h5py.File(input_file, \"r\") as input:\n                for dat_file_key in dat_file_keys:\n                    if dat_file_key in input.keys():\n                        data_to_append = input[dat_file_key]\n                        start_size = out[dat_file_key].shape[0]\n                        append_size = input[dat_file_key].shape[0]\n                        out[dat_file_key].resize(\n                            start_size + append_size, axis=0\n                        )\n                        out[dat_file_key][start_size:] = input[dat_file_key]\n                    else:\n                        logging.warning(\n                            f\"CombineH5Dat: Dat file '{dat_file_key}'\"\n                            f\" not found in input file '{input_file}'\"\n                        )\n\n\n@click.command(name=\"combine-h5-dat\", help=combine_h5_dat.__doc__)\n@click.argument(\n    \"h5files\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n    ),\n    nargs=-1,\n    required=True,\n)\n@click.option(\n    \"--output\",\n    \"-o\",\n    required=True,\n    type=click.Path(\n        exists=False,\n        file_okay=True,\n        dir_okay=False,\n        writable=True,\n        readable=True,\n    ),\n    help=\"Combined output filename.\",\n)\n@click.option(\n    \"--force\",\n    \"-f\",\n    is_flag=True,\n    help=\"If the output file already exists, overwrite it.\",\n)\ndef combine_h5_dat_command(**kwargs):\n    combine_h5_dat(kwargs[\"h5files\"], kwargs[\"output\"], kwargs[\"force\"])\n\n\nif __name__ == \"__main__\":\n    combine_h5_dat_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/IO/H5/Python/Dat.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Python/Dat.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/H5/Dat.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_h5dat(py::module& m) {\n  // Wrapper for basic H5Dat operations\n  py::class_<h5::Dat>(m, \"H5Dat\")\n      .def(\"append\",\n           static_cast<void (h5::Dat::*)(const std::vector<double>&)>(\n               &h5::Dat::append),\n           py::arg(\"data\"))\n      .def(\"append\",\n           static_cast<void (h5::Dat::*)(const Matrix&)>(&h5::Dat::append),\n           py::arg(\"data\"))\n      .def(\"append\",\n           static_cast<void (h5::Dat::*)(\n               const std::vector<std::vector<double>>&)>(&h5::Dat::append),\n           py::arg(\"data\"))\n      .def(\"get_legend\", &h5::Dat::get_legend)\n      // Use the matrix overload because it all gets converted to numpy arrays\n      // anways\n      .def(\"get_data\", &h5::Dat::get_data<Matrix>)\n      .def(\"get_data_subset\", &h5::Dat::get_data_subset<Matrix>,\n           py::arg(\"columns\"), py::arg(\"first_row\") = 0,\n           py::arg(\"num_rows\") = 1)\n      .def(\"get_dimensions\", &h5::Dat::get_dimensions)\n      .def(\"get_header\", &h5::Dat::get_header)\n      .def(\"get_version\", &h5::Dat::get_version);\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/Dat.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_h5dat(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/DeleteSubfiles.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport shutil\nimport subprocess\nimport tempfile\n\nimport click\nimport h5py\n\nlogger = logging.getLogger(__name__)\n\nHDF5_REPACK_EXECUTABLE = \"@HDF5_REPACK_EXECUTABLE@\"\n\n\n@click.command(name=\"delete-subfiles\")\n@click.argument(\n    \"h5files\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=-1,\n    required=True,\n)\n@click.option(\n    \"--subfile\",\n    \"-d\",\n    \"subfiles\",\n    required=True,\n    multiple=True,\n    help=(\n        \"Subfile to delete. Can be specified multiple times to delete many\"\n        \" subfiles at once.\"\n    ),\n)\n@click.option(\n    \"--repack/--no-repack\",\n    default=False,\n    help=(\n        \"Repack the H5 files after deleting subfiles \"\n        \"to reduce file size. Otherwise, the subfiles are deleted \"\n        \"but the file size remains unchanged.\"\n    ),\n)\ndef delete_subfiles_command(h5files, subfiles, repack):\n    \"\"\"Delete subfiles from the 'H5FILES'\"\"\"\n    for h5file in h5files:\n        with h5py.File(h5file, \"a\") as open_h5_file:\n            for subfile in subfiles:\n                if subfile in open_h5_file:\n                    del open_h5_file[subfile]\n                    logger.debug(\n                        f\"Deleted subfile '{subfile}' from file: {h5file}\"\n                    )\n                else:\n                    logger.warning(f\"No subfile '{subfile}' in file: {h5file}\")\n        if repack:\n            # h5repack must write to a new file, so we create a temporary one\n            with tempfile.TemporaryDirectory() as tempdir:\n                tmp_h5file = os.path.join(tempdir, \"temp.h5\")\n                subprocess.run(\n                    [HDF5_REPACK_EXECUTABLE, h5file, tmp_h5file],\n                    capture_output=True,\n                    text=True,\n                )\n                shutil.move(tmp_h5file, h5file)\n"
  },
  {
    "path": "src/IO/H5/Python/ExtendConnectivityData.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\n\nimport click\nimport rich\n\nimport spectre.IO.H5 as spectre_h5\n\n\n@click.command(name=\"extend-connectivity\")\n@click.argument(\n    \"filename\",\n    required=True,\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        writable=True,\n    ),\n)\n@click.option(\n    \"--subfile-name\",\n    \"-d\",\n    required=True,\n    type=str,\n    help=\"subfile name of the volume file in the H5 file (omit file extension)\",\n)\ndef extend_connectivity_data_command(filename, subfile_name):\n    \"\"\"Extend the connectivity inside a single HDF5 volume file.\n\n    This extended connectivity is for some SpECTRE evolution, in\n    order to fill in gaps between elements. Intended to be used as\n    a post-processing routine to improve the quality of\n    visualizations. Note: This does not work with subcell or AMR systems, and\n    the connectivity only extends *within* each block and not between them.\n    This only works for a single HDF5 volume file. If there are multiple\n    files, the combine-h5 executable must be run first. The extend-connectivity\n    command can then be run on the newly generated HDF5 file.\n    \"\"\"\n    # Read the h5 file and extract the volume file from it\n    h5_file = spectre_h5.H5File(filename, \"r+\")\n    vol_file = h5_file.get_vol(subfile_name)\n\n    observation_ids = vol_file.list_observation_ids()\n    dim = vol_file.get_dimension()\n\n    if dim == 1:\n        vol_file.extend_connectivity_data_1d(observation_ids)\n    elif dim == 2:\n        vol_file.extend_connectivity_data_2d(observation_ids)\n    elif dim == 3:\n        vol_file.extend_connectivity_data_3d(observation_ids)\n    else:\n        raise ValueError(\"Invalid Dimensionality\")\n\n\nif __name__ == \"__main__\":\n    extend_connectivity_data_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/IO/H5/Python/ExtractDatFromH5.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport multiprocessing as mp\nimport os\nimport shutil\n\nimport click\nimport h5py\nimport numpy as np\nimport pandas as pd\nimport rich\n\nfrom spectre.IO.H5 import available_subfiles\n\n\ndef write_dat_data(dat_path, h5_filename, out_dir, precision):\n    with h5py.File(h5_filename, \"r\") as h5file:\n        if not dat_path.endswith(\".dat\"):\n            dat_path = dat_path + \".dat\"\n        dat_file = h5file.get(dat_path)\n        if dat_file is None:\n            subfiles = available_subfiles(h5_filename, extension=\".dat\")\n            raise ValueError(\n                f\"Unable to find subfile {dat_path}. Known \"\n                f\"files are:\\n{subfiles}\"\n            )\n        legend = dat_file.attrs[\"Legend\"]\n        dat_data = np.array(dat_file)\n\n    header = \"\\n\".join(f\"[{i}] \" + \"{}\" for i in range(len(legend))).format(\n        *legend\n    )\n\n    format_string = f\"%.{precision}e\"\n\n    # Write to stdout\n    if not out_dir:\n        print(\n            pd.DataFrame(dat_data).to_string(\n                index=False,\n                header=legend,\n                float_format=format_string,\n                justify=\"left\",\n            )\n        )\n        return\n\n    dat_dir = os.path.join(out_dir, os.path.dirname(dat_path))\n    os.makedirs(dat_dir, exist_ok=True)\n    dat_filename = os.path.join(out_dir, dat_path)\n\n    np.savetxt(\n        dat_filename, dat_data, delimiter=\" \", fmt=format_string, header=header\n    )\n\n\ndef extract_dat_files(\n    filename,\n    out_dir,\n    num_cores,\n    precision,\n    list=False,\n    force=False,\n    subfiles=None,\n):\n    \"\"\"Extract dat files from an H5 file\n\n    Extract all Dat files inside a SpECTRE HDF5 file. The resulting files will\n    be put into the 'OUT_DIR' if specified, or printed to standard output. The\n    directory structure will be identical to the group structure inside the\n    HDF5 file.\n    \"\"\"\n    if list:\n        import rich.columns\n\n        subfiles = available_subfiles(filename, extension=\".dat\")\n        rich.print(rich.columns.Columns(subfiles))\n        return\n\n    if subfiles:\n        all_dat_files = subfiles\n    else:\n        all_dat_files = available_subfiles(filename, extension=\".dat\")\n\n    if out_dir:\n        if not os.path.exists(out_dir):\n            os.mkdir(out_dir)\n        else:\n            if force:\n                shutil.rmtree(out_dir, ignore_errors=True)\n                os.mkdir(out_dir)\n            else:\n                raise ValueError(\n                    f\"Could not make directory '{out_dir}'. Already exists.\"\n                )\n    else:\n        if subfiles and len(subfiles) > 1:\n            raise ValueError(\n                \"If no output directory is specified, can only write \"\n                f\"one subfile to stdout, not {len(subfiles)}\"\n            )\n\n    num_dat_files = len(all_dat_files)\n\n    # Only use multiprocessing if we are writing to a directory and using more\n    # than one core. Otherwise avoid the overhead\n    if out_dir and num_cores > 1:\n        with mp.Pool(processes=num_cores) as pool:\n            pool.starmap(\n                write_dat_data,\n                zip(\n                    all_dat_files,\n                    [filename] * num_dat_files,\n                    [out_dir] * num_dat_files,\n                    [precision] * num_dat_files,\n                ),\n            )\n    else:\n        for dat_filename in all_dat_files:\n            write_dat_data(dat_filename, filename, out_dir, precision)\n\n    if out_dir:\n        print(f\"Successfully extracted all Dat files into '{out_dir}'\")\n\n\n@click.command(name=\"extract-dat\", help=extract_dat_files.__doc__)\n@click.argument(\n    \"filename\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.argument(\n    \"out_dir\",\n    type=click.Path(file_okay=False, dir_okay=True, writable=True),\n    required=False,\n)\n@click.option(\n    \"--num-cores\",\n    \"-j\",\n    default=1,\n    show_default=True,\n    help=\"Number of cores to run on.\",\n)\n@click.option(\n    \"--precision\",\n    \"-p\",\n    default=16,\n    show_default=True,\n    help=\"Precision with which to save (or print) the data.\",\n)\n@click.option(\n    \"--force\",\n    \"-f\",\n    is_flag=True,\n    help=\"If the output directory already exists, overwrite it.\",\n)\n@click.option(\n    \"--list\",\n    \"-l\",\n    is_flag=True,\n    help=\"List all dat files in the HDF5 file and exit.\",\n)\n@click.option(\n    \"--subfile\",\n    \"-d\",\n    \"subfiles\",\n    multiple=True,\n    help=(\n        \"Full path of subfile to extract (including extension). Can be\"\n        \" specified multiple times to extract several subfiles at once. If\"\n        \" unspecified, all subfiles will be extracted.\"\n    ),\n)\ndef extract_dat_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    extract_dat_files(**kwargs)\n\n\nif __name__ == \"__main__\":\n    extract_dat_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/IO/H5/Python/ExtractInputSourceYamlFromH5.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport click\n\nimport spectre.IO.H5 as spectre_h5\n\n\n@click.command(name=\"extract-input\")\n@click.argument(\n    \"h5_file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.argument(\"output_file\", required=False, type=click.File(\"w\"))\ndef extract_input_source_from_h5_command(h5_file, output_file):\n    \"\"\"Extract input file from an H5 file\n\n    Extract InputSource.yaml from the 'H5_FILE' and write it to the\n    'OUTPUT_FILE', or print to stdout if `OUTPUT_FILE` is unspecified.\n    \"\"\"\n    with spectre_h5.H5File(h5_file, \"r\") as open_file:\n        input_source = open_file.input_source()\n\n    if output_file:\n        output_file.write(input_source)\n    else:\n        import rich.syntax\n\n        syntax = rich.syntax.Syntax(\n            input_source, lexer=\"yaml\", theme=\"ansi_dark\"\n        )\n        rich.print(syntax)\n\n\nif __name__ == \"__main__\":\n    extract_input_source_from_h5_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/IO/H5/Python/File.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Python/File.hpp\"\n\n#include <boost/algorithm/string/join.hpp>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <pybind11/stl/filesystem.h>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/Cce.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nnamespace {\ntemplate <h5::AccessType Access_t>\nvoid bind_h5file_impl(py::module& m) {  // NOLINT\n  // Wrapper for basic H5File operations\n  // Manually check for existence as we currently can not propagate exceptions\n  // that can be caught by pybind, see issue #2312\n  using H5File = h5::H5File<Access_t>;\n  auto bind_h5_file =\n      py::class_<H5File>(\n          m,\n          (std::string(\"_H5File\") +\n           (Access_t == h5::AccessType::ReadWrite ? \"ReadWrite\" : \"ReadOnly\"))\n              .c_str())\n          .def(\"name\", &H5File::name)\n          .def(\n              \"get_dat\",\n              [](const H5File& f, const std::string& path) -> const h5::Dat& {\n                return f.template get<h5::Dat>(path);\n              },\n              py::return_value_policy::reference, py::arg(\"path\"))\n          .def(\n              \"get_cce\",\n              [](const H5File& f, const std::string& path,\n                 const size_t l_max) -> const h5::Cce& {\n                return f.template get<h5::Cce>(path, l_max);\n              },\n              py::return_value_policy::reference, py::arg(\"path\"),\n              py::arg(\"l_max\"))\n          .def(\"close_current_object\", &H5File::close_current_object)\n          .def(\"close\", &H5File::close)\n          .def(\"all_files\",\n               [](const H5File& f) -> const std::vector<std::string> {\n                 return f.template all_files<void>(\"/\");\n               })\n          .def(\"all_dat_files\",\n               [](const H5File& f) -> const std::vector<std::string> {\n                 return f.template all_files<h5::Dat>(\"/\");\n               })\n          .def(\"all_cce_files\",\n               [](const H5File& f) -> const std::vector<std::string> {\n                 return f.template all_files<h5::Cce>(\"/\");\n               })\n          .def(\"all_vol_files\",\n               [](const H5File& f) -> const std::vector<std::string> {\n                 return f.template all_files<h5::VolumeData>(\"/\");\n               })\n          .def(\"groups\", &H5File::groups)\n          .def(\n              \"get_vol\",\n              [](const H5File& f,\n                 const std::string& path) -> const h5::VolumeData& {\n                return f.template get<h5::VolumeData>(path);\n              },\n              py::return_value_policy::reference, py::arg(\"path\"))\n          .def(\"input_source\", &H5File::input_source)\n          .def(\"__enter__\", [](H5File& file) -> H5File& { return file; })\n          .def(\"__exit__\",\n               [](H5File& f, const py::object& /* exception_type */,\n                  const py::object& /* val */,\n                  const py::object& /* traceback */) { f.close(); });\n\n  if constexpr (Access_t == h5::AccessType::ReadWrite) {\n    bind_h5_file\n        .def(\n            \"insert_dat\",\n            [](H5File& f, const std::string& path,\n               const std::vector<std::string>& legend,\n               const uint32_t version) -> h5::Dat& {\n              return f.template insert<h5::Dat>(path, legend, version);\n            },\n            py::return_value_policy::reference, py::arg(\"path\"),\n            py::arg(\"legend\"), py::arg(\"version\"))\n        .def(\n            \"try_insert_dat\",\n            [](H5File& f, const std::string& path,\n               const std::vector<std::string>& legend,\n               const uint32_t version) -> h5::Dat& {\n              return f.template try_insert<h5::Dat>(path, legend, version);\n            },\n            py::return_value_policy::reference, py::arg(\"path\"),\n            py::arg(\"legend\"), py::arg(\"version\"))\n        .def(\n            \"insert_cce\",\n            [](H5File& f, const std::string& path, const size_t l_max,\n               const uint32_t version) -> h5::Cce& {\n              return f.template insert<h5::Cce>(path, l_max, version);\n            },\n            py::return_value_policy::reference, py::arg(\"path\"),\n            py::arg(\"l_max\"), py::arg(\"version\"))\n        .def(\n            \"try_insert_cce\",\n            [](H5File& f, const std::string& path, const size_t l_max,\n               const uint32_t version) -> h5::Cce& {\n              return f.template try_insert<h5::Cce>(path, l_max, version);\n            },\n            py::return_value_policy::reference, py::arg(\"path\"),\n            py::arg(\"l_max\"), py::arg(\"version\"))\n        .def(\n            \"insert_vol\",\n            [](H5File& f, const std::string& path,\n               const uint32_t version) -> h5::VolumeData& {\n              return f.template insert<h5::VolumeData>(path, version);\n            },\n            py::return_value_policy::reference, py::arg(\"path\"),\n            py::arg(\"version\"))\n        .def(\n            \"try_insert_vol\",\n            [](H5File& f, const std::string& path,\n               const uint32_t version) -> h5::VolumeData& {\n              return f.template try_insert<h5::VolumeData>(path, version);\n            },\n            py::return_value_policy::reference, py::arg(\"path\"),\n            py::arg(\"version\"));\n  }\n}\n}  // namespace\n\nvoid bind_h5file(py::module& m) {\n  bind_h5file_impl<h5::AccessType::ReadOnly>(m);\n  bind_h5file_impl<h5::AccessType::ReadWrite>(m);\n\n  m.def(\n      \"H5File\",\n      [](const std::filesystem::path& file_name, const std::string& mode) {\n        if (mode == \"r\") {\n          return py::cast(\n              h5::H5File<h5::AccessType::ReadOnly>{file_name.string(), false});\n        }\n        return py::cast(h5::H5File<h5::AccessType::ReadWrite>{\n            file_name.string(), (mode == \"a\" or mode == \"r+\")});\n      },\n      py::arg(\"file_name\"), py::arg(\"mode\") = \"r\",\n      \"Open an H5File object\\n\\nfile_name: the name of the H5File to \"\n      \"open\\nmode: mode to open the file. Available modes are 'r', 'r+', 'w-', \"\n      \"'x', and 'a'. For details see \"\n      \"https://docs.h5py.org/en/stable/high/file.html\");\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/File.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_h5file(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/FunctionsOfTimeFromVolume.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\nfrom typing import Optional, Union\n\nimport click\nimport numpy as np\nimport yaml\nfrom rich.pretty import pretty_repr\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Domain import deserialize_functions_of_time\nfrom spectre.IO.H5 import select_observation\n\n\ndef functions_of_time_from_volume(\n    fot_vol_h5_path, fot_vol_subfile, match_time, fot_to_observe=None\n):\n    \"\"\"This function returns a dictionary of the FunctionsOfTime from a volume\n    h5 file evaluated at a desired time along with the time closest to the\n    match_time passed in.\n\n    Arguments:\n    fot_vol_h5_path: The full path to volume data containing functions of time\n    that have been evaluated at the match time.\n    fot_vol_subfile: The subfile containing volume data with functions of time\n    evaulated at match time.\n    match_time: The desired time to retrieve the function of time values.\n    \"\"\"\n    with spectre_h5.H5File(fot_vol_h5_path, \"r\") as h5file:\n        volfile = h5file.get_vol(fot_vol_subfile)\n        # If match time not specified, choose the last available time in the\n        # volume data\n        if match_time is None:\n            obs_id, match_time = select_observation(volfile, step=-1)\n        else:\n            obs_id, match_time = select_observation(volfile, time=match_time)\n        serialized_fots = volfile.get_functions_of_time(obs_id)\n    functions_of_time = deserialize_functions_of_time(serialized_fots)\n\n    functions_of_time_at_match_time_dict = {\"MatchTime\": match_time}\n\n    for fot_name, fot in functions_of_time.items():\n        fot_at_match_time = fot.func_and_2_derivs(match_time)\n        if len(fot_at_match_time[0]) != 1:\n            functions_of_time_at_match_time_dict[fot_name] = [\n                [coef for coef in x] for x in fot_at_match_time\n            ]\n        else:\n            functions_of_time_at_match_time_dict[fot_name] = [\n                x[0] for x in fot_at_match_time\n            ]\n\n    return functions_of_time_at_match_time_dict\n"
  },
  {
    "path": "src/IO/H5/Python/InterpolateToMesh.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport glob\nimport logging\nimport re\nimport sys\nfrom multiprocessing import Pool\n\nimport click\nimport numpy as np\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre import Interpolation, Spectral\nfrom spectre.DataStructures import DataVector\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\n\nlogger = logging.getLogger(__name__)\n\n\n# Function to forward arguments to the multiprocessing Pool. We can not use a\n# lambda because the function needs to be pickled. Note that `starmap` only\n# works with an iterable, not with a dict.\ndef forward_kwargs(kwargs):\n    interpolate_to_mesh(**kwargs)\n\n\ndef interpolate_to_mesh(\n    source_file_path,\n    target_mesh,\n    target_file_path,\n    source_volume_data,\n    target_volume_data,\n    components_to_interpolate=None,\n    obs_start=-np.inf,\n    obs_end=np.inf,\n    obs_stride=1,\n):\n    \"\"\"Interpolates an h5 file to a desired grid\n\n    The function reads data from `source_volume_data` inside `source_file_path`,\n    interpolates all components specified by `components_to_interpolate` to the\n    grid specified by `target_mesh` and writes the results into\n    `target_volume_data` inside `target_file_path`. The `target_file_path` can\n    be the same as the `source_file_path` if the volume subfile paths are\n    different.\n\n    \\f\n    Parameters\n    ----------\n    source_file_path: str\n        the path to the source file where the `source_volume_data` is\n    target_mesh: spectre.Spectral.Mesh\n        the mesh to which the data is interpolated\n    components_to_interpolate: list of str, optional\n        a list of all components that are to be interpolated. accepts regular\n        expressions. By default ALL tensor components are interpolated.\n    target_file_path: str, optional\n        the path to where the interpolated data is written. By default this is\n        set to `source_file_path` so the interpolated data is written to the\n        same file, but in a different subfile specified by `target_volume_data`.\n    source_volume_data: str, optional\n        the name of the .vol file inside the source file where the source data\n        can be found.\n    target_volume_data: str, optional\n        the name of the .vol file inside the target file where the target data\n        is written.\n    obs_start: float, optional\n        disregards all observations with observation value strictly before\n        `obs_start`\n    obs_end: float, optional\n        disregards all observations with observation value strictly after\n        `obs_end`\n    obs_stride: float, optional\n        will only take every `obs_stride` observation\n    \"\"\"\n\n    if source_volume_data.endswith(\".vol\"):\n        source_volume_data = source_volume_data[:-4]\n    if target_volume_data.endswith(\".vol\"):\n        target_volume_data = target_volume_data[:-4]\n    if not source_volume_data.startswith(\"/\"):\n        source_volume_data = \"/\" + source_volume_data\n    if not target_volume_data.startswith(\"/\"):\n        target_volume_data = \"/\" + target_volume_data\n\n    if target_file_path == source_file_path:\n        if source_volume_data == target_volume_data:\n            raise NameError(\n                \"If the source and target files are the same, \"\n                \"the source and target volume_data need to be different.\"\n            )\n        source_file = spectre_h5.H5File(source_file_path, \"r+\")\n        target_file = source_file\n    else:\n        source_file = spectre_h5.H5File(source_file_path, \"r\")\n        target_file = spectre_h5.H5File(target_file_path, \"a\")\n\n    source_vol = source_file.get_vol(source_volume_data)\n    dim = source_vol.get_dimension()\n    version = source_vol.get_version()\n    # apply observation filter\n    observations = [\n        obs\n        for obs in source_vol.list_observation_ids()\n        if obs_start <= source_vol.get_observation_value(obs) <= obs_end\n    ][::obs_stride]\n\n    source_file.close_current_object()\n    target_file.insert_vol(target_volume_data, version)\n    target_file.close_current_object()\n\n    for obs in observations:\n        # the vols memory address may shift as we write to file,\n        # so we need to get them every iteration\n        source_file.close_current_object()\n        source_vol = source_file.get_vol(source_volume_data)\n        extents = source_vol.get_extents(obs)\n        bases = source_vol.get_bases(obs)\n        quadratures = source_vol.get_quadratures(obs)\n        tensor_names = source_vol.list_tensor_components(obs)\n        grid_names = source_vol.get_grid_names(obs)\n        obs_value = source_vol.get_observation_value(obs)\n\n        if components_to_interpolate:\n            tensor_names = list(\n                set(\n                    tensor_name\n                    for pattern in components_to_interpolate\n                    for tensor_name in tensor_names\n                    if re.match(pattern, tensor_name)\n                )\n            )\n\n        # pre-load all tensors to avoid loading the full tensor for each element\n        tensors = [\n            np.array(\n                source_vol.get_tensor_component(obs, name).data, copy=False\n            )\n            for name in tensor_names\n        ]\n\n        source_file.close_current_object()\n\n        volume_data = []\n        # iterate over elements\n        for grid_name, extent, basis, quadrature in zip(\n            grid_names, extents, bases, quadratures\n        ):\n            source_mesh = Spectral.Mesh[dim](extent, basis, quadrature)\n\n            interpolant = Interpolation.RegularGrid[dim](\n                source_mesh, target_mesh\n            )\n\n            tensor_comps = []\n            offset, length = spectre_h5.offset_and_length_for_grid(\n                grid_name, grid_names, extents\n            )\n            # iterate over tensors\n            for j, tensor in enumerate(tensors):\n                component_data = DataVector(\n                    tensor[offset : offset + length], copy=False\n                )\n                interpolated_tensor = interpolant.interpolate(component_data)\n                tensor_path = tensor_names[j]\n                tensor_comps.append(\n                    TensorComponent(\n                        tensor_path, DataVector(interpolated_tensor, copy=False)\n                    )\n                )\n\n            volume_data.append(\n                ElementVolumeData(\n                    element_name=grid_name,\n                    components=tensor_comps,\n                    extents=target_mesh.extents(),\n                    basis=target_mesh.basis(),\n                    quadrature=target_mesh.quadrature(),\n                )\n            )\n        target_file.close_current_object()\n        target_vol = target_file.get_vol(target_volume_data)\n        target_vol.write_volume_data(obs, obs_value, volume_data)\n\n    source_file.close()\n    if not target_file is source_file:\n        target_file.close()\n\n\n@click.command(name=\"interpolate-to-mesh\", help=interpolate_to_mesh.__doc__)\n@click.option(\n    \"--source-file-prefix\",\n    required=True,\n    help=(\n        \"The prefix for the .h5 source files. All files starting with the \"\n        \"prefix followed by a number will be interpolated.\"\n    ),\n)\n@click.option(\n    \"--source-subfile-name\",\n    required=True,\n    help=(\n        \"The name of the volume data subfile within the \"\n        \"source files in which the data is contained\"\n    ),\n)\n@click.option(\n    \"--target-file-prefix\",\n    default=None,\n    help=(\n        \"The prefix for the target files where the interpolated data is \"\n        \"written. When no target file is specified, the interpolated data is \"\n        \"written to the corresponding source file in a new volume data \"\n        \"subfile.\"\n    ),\n)\n@click.option(\n    \"--target-subfile-name\",\n    required=True,\n    help=(\n        \"The name of the volume data subfile within the target \"\n        \"files where the data will be written.\"\n    ),\n)\n@click.option(\n    \"--tensor-component\",\n    \"-t\",\n    \"tensor_components\",\n    multiple=True,\n    help=(\n        \"The name of the tensor to be interpolated. Accepts regular\"\n        \" expression. Can be specified multiple times to interpolate several\"\n        \" tensors at once. If none are specified, all tensors are interpolated.\"\n    ),\n)\n@click.option(\n    \"--target-extents\",\n    callback=(\n        lambda ctx, param, value: (\n            list(map(int, value.split(\",\"))) if value else []\n        )\n    ),\n    required=True,\n    help=(\n        \"The extents of the target grid, as a comma-separated list without \"\n        \"spaces. Can be different for each dimension e.g. '3,5,4'\"\n    ),\n)\n@click.option(\n    \"--target-basis\",\n    type=click.Choice(Spectral.Basis.__members__),\n    callback=lambda ctx, param, value: Spectral.Basis.__members__[value],\n    required=True,\n    help=\"The basis of the target grid.\",\n)\n@click.option(\n    \"--target-quadrature\",\n    type=click.Choice(Spectral.Quadrature.__members__),\n    callback=lambda ctx, param, value: Spectral.Quadrature.__members__[value],\n    required=True,\n    help=\"The quadrature of the target grid.\",\n)\n@click.option(\n    \"--start-time\",\n    type=float,\n    default=-np.inf,\n    help=\"Disregard all observations with value before this point\",\n)\n@click.option(\n    \"--stop-time\",\n    type=float,\n    default=np.inf,\n    help=\"Disregard all observations with value after this point\",\n)\n@click.option(\n    \"--stride\",\n    type=int,\n    default=1,\n    help=\"Stride through observations with this step size.\",\n)\n@click.option(\n    \"-j\",\n    \"--num-jobs\",\n    type=int,\n    default=None,\n    help=(\n        \"The maximum number of processes to be started. \"\n        \"A process is spawned for each source file up to this number.\"\n    ),\n)\ndef interpolate_to_mesh_command(\n    source_file_prefix,\n    source_subfile_name,\n    target_file_prefix,\n    target_subfile_name,\n    target_extents,\n    target_basis,\n    target_quadrature,\n    tensor_components,\n    start_time,\n    stop_time,\n    stride,\n    num_jobs,\n):\n    _rich_traceback_guard = True  # Hide traceback until here\n\n    source_files = glob.glob(source_file_prefix + \"[0-9]*.h5\")\n    file_numbers = [\n        re.match(source_file_prefix + \"([0-9]*).h5\", source_file).group(1)\n        for source_file in source_files\n    ]\n    target_files = [\n        f\"{target_file_prefix}{number}.h5\" for number in file_numbers\n    ]\n\n    if len(source_files) == 0:\n        raise NameError(\"No files found matching the input pattern.\")\n\n    dim = len(target_extents)\n    target_mesh = Spectral.Mesh[dim](\n        target_extents, target_basis, target_quadrature\n    )\n\n    interpolate_kwargs = []\n    logger.info(\"Source and target files/volumes are as follows:\")\n\n    for source_file_path, target_file_path in zip(source_files, target_files):\n        interpolate_kwargs.append(\n            dict(\n                source_file_path=source_file_path,\n                target_mesh=target_mesh,\n                target_file_path=target_file_path,\n                source_volume_data=source_subfile_name,\n                target_volume_data=target_subfile_name,\n                components_to_interpolate=tensor_components,\n                obs_start=start_time,\n                obs_end=stop_time,\n                obs_stride=stride,\n            )\n        )\n\n        logger.info(\n            \"{}{} => {}{}\".format(\n                source_file_path,\n                source_subfile_name,\n                target_file_path,\n                target_subfile_name,\n            )\n        )\n\n    with Pool(num_jobs) as p:\n        p.map(forward_kwargs, interpolate_kwargs)\n\n\nif __name__ == \"__main__\":\n    interpolate_volume_data_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/IO/H5/Python/IterElements.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport fnmatch\nfrom dataclasses import dataclass\nfrom functools import cached_property\nfrom typing import Dict, Iterable, Optional, Sequence, Union\n\nimport numpy as np\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures.Tensor.EagerMath import determinant\nfrom spectre.Domain import (\n    ElementId,\n    ElementMap,\n    FunctionOfTime,\n    deserialize_domain,\n    deserialize_functions_of_time,\n)\nfrom spectre.Domain.CoordinateMaps import (\n    CoordinateMapElementLogicalToInertial1D,\n    CoordinateMapElementLogicalToInertial2D,\n    CoordinateMapElementLogicalToInertial3D,\n)\nfrom spectre.Spectral import Mesh, logical_coordinates\n\n\n@dataclass(frozen=True)\nclass Element:\n    id: Union[ElementId[1], ElementId[2], ElementId[3]]\n    mesh: Union[Mesh[1], Mesh[2], Mesh[3]]\n    map: Optional[\n        Union[\n            CoordinateMapElementLogicalToInertial1D,\n            CoordinateMapElementLogicalToInertial2D,\n            CoordinateMapElementLogicalToInertial3D,\n        ]\n    ]\n    time: Optional[float] = None\n    functions_of_time: Optional[Dict[str, FunctionOfTime]] = None\n    # Offset and length in contiguous tensor data corresponding to this element\n    data_slice: Optional[slice] = None\n\n    @property\n    def dim(self):\n        return self.mesh.dim\n\n    @cached_property\n    def logical_coordinates(self):\n        return logical_coordinates(self.mesh)\n\n    @cached_property\n    def grid_coordinates(self):\n        assert self.map, (\n            \"No element map available. Ensure the domain is written in the H5\"\n            \" file.\"\n        )\n        try:\n            return self.map.block_logical_to_grid(\n                self.map.element_logical_to_block_logical(\n                    self.logical_coordinates\n                )\n            )\n        except AttributeError:\n            return self.inertial_coordinates\n\n    @cached_property\n    def distorted_coordinates(self):\n        assert self.map, (\n            \"No element map available. Ensure the domain is written in the H5\"\n            \" file.\"\n        )\n        try:\n            return self.map.grid_to_distorted(\n                self.grid_coordinates, self.time, self.functions_of_time\n            )\n        except AttributeError:\n            return self.grid_coordinates\n\n    @cached_property\n    def inertial_coordinates(self):\n        assert self.map, (\n            \"No element map available. Ensure the domain is written in the H5\"\n            \" file.\"\n        )\n        return self.map(\n            self.logical_coordinates, self.time, self.functions_of_time\n        )\n\n    @cached_property\n    def inv_jacobian(self):\n        assert self.map, (\n            \"No element map available. Ensure the domain is written in the H5\"\n            \" file.\"\n        )\n        return self.map.inv_jacobian(\n            self.logical_coordinates, self.time, self.functions_of_time\n        )\n\n    @cached_property\n    def jacobian(self):\n        assert self.map, (\n            \"No element map available. Ensure the domain is written in the H5\"\n            \" file.\"\n        )\n        return self.map.jacobian(\n            self.logical_coordinates, self.time, self.functions_of_time\n        )\n\n    @cached_property\n    def det_jacobian(self):\n        return determinant(self.jacobian)\n\n\ndef stripped_element_name(\n    element_id: Union[str, ElementId[1], ElementId[2], ElementId[3]]\n) -> bool:\n    \"\"\"The element name without the leading and trailing square bracket\n\n    Square brackets have special meaning in glob patterns, so it's easier to\n    strip them away when dealing with element names on the command line.\n    \"\"\"\n    return str(element_id).strip(\"[]\")\n\n\ndef include_element(\n    element_id: Union[str, ElementId[1], ElementId[2], ElementId[3]],\n    element_patterns: Optional[Sequence[str]],\n) -> bool:\n    \"\"\"Whether or not the 'element_id' matches any of the 'element_patterns'\n\n    The 'element_patterns' are interpreted as glob patterns.\n    If 'element_patterns' is None, every element is included. If\n    'element_patterns' is an empty list, no element is included.\n    \"\"\"\n    if element_patterns is None:\n        return True\n    return any(\n        fnmatch.fnmatch(stripped_element_name(element_id), element_pattern)\n        for element_pattern in element_patterns\n    )\n\n\ndef iter_elements(\n    volfiles: Union[spectre_h5.H5Vol, Iterable[spectre_h5.H5Vol]],\n    obs_ids: Optional[Union[int, Sequence[int]]],\n    tensor_components: Optional[Iterable[str]] = None,\n    element_patterns: Optional[Sequence[str]] = None,\n):\n    \"\"\"Return volume data by element\n\n    Arguments:\n      volfiles: Open spectre H5 volume files. Can be a single volfile or a list,\n        but can also be an iterator that opens and closes the files on demand.\n      obs_id: An observation ID, a list of observation IDs, or None to iterate\n        over all observation IDs.\n      tensor_components: Tensor components to retrieve. Can be empty.\n      element_patterns: If specified, include only elements that match any of\n        these glob patterns. See 'IterElements.include_element' for details.\n\n    Returns: Iterator over all elements in all 'volfiles'. Yields either just\n      the 'Element' with structural information if 'tensor_components' is\n      empty, or both the 'Element' and an 'np.ndarray' with the tensor data\n      listed in 'tensor_components'. The tensor data has shape\n      `(len(tensor_components), num_points)`.\n    \"\"\"\n    if isinstance(volfiles, spectre_h5.H5Vol):\n        volfiles = [volfiles]\n    if isinstance(obs_ids, (int, np.integer)):\n        obs_ids = [obs_ids]\n    # Assuming the domain is the same in all volfiles at all observations to\n    # speed up the script\n    domain = None\n    for volfile in volfiles:\n        all_obs_ids = volfile.list_observation_ids()\n        if obs_ids:\n            selected_obs_ids = [\n                obs_id for obs_id in obs_ids if obs_id in all_obs_ids\n            ]\n        else:\n            selected_obs_ids = all_obs_ids\n        dim = volfile.get_dimension()\n        for obs_id in selected_obs_ids:\n            # Filter by element patterns first to avoid doing unnecessary work\n            # if the volfile doesn't contain any of the requested elements\n            all_grid_names = volfile.get_grid_names(obs_id)\n            if element_patterns is not None:\n                grid_names = [\n                    grid_name\n                    for grid_name in all_grid_names\n                    if include_element(grid_name, element_patterns)\n                ]\n            else:\n                grid_names = all_grid_names\n            if not grid_names:\n                continue\n            element_ids = [ElementId[dim](name) for name in grid_names]\n            # Reconstruct meshes\n            all_extents = volfile.get_extents(obs_id)\n            all_bases = volfile.get_bases(obs_id)\n            all_quadratures = volfile.get_quadratures(obs_id)\n            meshes = [\n                Mesh[dim](extents, bases, quadratures)\n                for grid_name, extents, bases, quadratures in zip(\n                    all_grid_names, all_extents, all_bases, all_quadratures\n                )\n                if grid_name in grid_names\n            ]\n            # Deserialize domain and functions of time\n            if not domain:\n                serialized_domain = volfile.get_domain()\n                if serialized_domain:\n                    domain = deserialize_domain[dim](serialized_domain)\n            time = volfile.get_observation_value(obs_id)\n            if domain and domain.is_time_dependent():\n                functions_of_time = deserialize_functions_of_time(\n                    volfile.get_functions_of_time(obs_id)\n                )\n            else:\n                functions_of_time = None\n            # Pre-load the tensor data because it's stored contiguously for all\n            # grids in the file\n            if tensor_components:\n                component_data = [\n                    volfile.get_tensor_component(obs_id, component).data\n                    for component in tensor_components\n                ]\n                # Ensure that all components have the same data type\n                for data in component_data:\n                    assert (\n                        data.dtype == component_data[0].dtype\n                    ), \"Tensor components have different data types\"\n                tensor_data = np.asarray(component_data)\n            # Iterate elements in this file\n            for grid_name, element_id, mesh in zip(\n                grid_names, element_ids, meshes\n            ):\n                offset, length = spectre_h5.offset_and_length_for_grid(\n                    grid_name, all_grid_names, all_extents\n                )\n                data_slice = slice(offset, offset + length)\n                if domain:\n                    element_map = ElementMap(element_id, domain)\n                else:\n                    element_map = None\n                element = Element(\n                    element_id,\n                    mesh=mesh,\n                    map=element_map,\n                    time=time,\n                    functions_of_time=functions_of_time,\n                    data_slice=data_slice,\n                )\n                if tensor_components:\n                    yield element, tensor_data[:, offset : offset + length]\n                else:\n                    yield element\n"
  },
  {
    "path": "src/IO/H5/Python/OpenVolfiles.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport fnmatch\nimport functools\nimport glob\nimport logging\nfrom pathlib import Path\nfrom typing import Iterable, Optional, Union\n\nimport click\nimport numpy as np\nimport rich\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5.ReadH5 import list_observations, select_observation\nfrom spectre.support.CliExceptions import RequiredChoiceError\n\nlogger = logging.getLogger(__name__)\n\n\ndef open_volfiles(\n    h5_files: Union[Iterable[Union[str, Path]], str, Path],\n    subfile_name: str,\n    obs_id: Optional[int] = None,\n):\n    \"\"\"Opens each volume data file in turn\n\n    Arguments:\n      h5_files: List of H5 files containing volume data, or a glob pattern.\n      subfile_name: Name of the H5 volume data subfile\n      obs_id: Optional. If specified, only yield volume data files that\n        contain the specified observation ID.\n    \"\"\"\n    if isinstance(h5_files, (str, Path)):\n        h5_files = sorted(glob.glob(h5_files))\n\n    subfile_found = False\n    files_exist = False\n\n    for h5_file in h5_files:\n        with spectre_h5.H5File(h5_file, \"r\") as open_h5_file:\n            files_exist = True\n            try:\n                volfile = open_h5_file.get_vol(subfile_name)\n            except RuntimeError:\n                continue\n\n            subfile_found = True\n            if obs_id is None or obs_id in volfile.list_observation_ids():\n                yield volfile\n\n    if files_exist and not subfile_found:\n        raise ValueError(\n            f\"Subfile '{subfile_name}' was not found in any of the provided\"\n            \" H5 files.\"\n        )\n\n\ndef parse_step(ctx, param, value):\n    \"\"\"Parse a CLI option as integer. Accepts 'first' and 'last'.\"\"\"\n    if value is None:\n        return None\n    if value.lower() == \"first\":\n        return 0\n    if value.lower() == \"last\":\n        return -1\n    return int(value)\n\n\ndef parse_point(ctx, param, value):\n    \"\"\"Parse a CLI option as comma-separated list of floats.\"\"\"\n    if not value:\n        return None\n    return np.array(list(map(float, value.split(\",\"))))\n\n\ndef parse_points(ctx, param, values):\n    \"\"\"Parse a CLI option as multiple comma-separated list of floats.\"\"\"\n    if not values:\n        return None\n    points = [parse_point(ctx, param, value) for value in values]\n    dim = len(points[0])\n    if any([len(point) != dim for point in points]):\n        raise click.BadParameter(\n            \"All specified points must have the same dimension\"\n        )\n    return np.array(points)\n\n\ndef open_volfiles_command(\n    obs_id_required=False, vars_required=True, multiple_vars=False\n):\n    \"\"\"CLI options for accessing volume data files\n\n    Use this decorator to add options for accessing volume data to a CLI\n    command. The decorated function should accept the following arguments in\n    addition to any other arguments it needs:\n\n    - h5_files: List of paths to h5 files containing volume data.\n    - subfile_name: Name of subfile within h5 file containing volume data.\n    - obs_id: Selected observation ID. Can be None if obs_id_required is False.\n    - obs_time: Time of observation. None if obs_id is also None.\n    - vars or var_name: If 'multiple_vars' is True, the list of selected\n        variable names in the volume data. Otherwise, the single selected\n        variable name.\n    \"\"\"\n\n    def decorator(f):\n        @click.argument(\n            \"h5_files\",\n            nargs=-1,\n            required=True,\n            type=click.Path(\n                exists=True, file_okay=True, dir_okay=False, readable=True\n            ),\n        )\n        @click.option(\n            \"--subfile-name\",\n            \"-d\",\n            help=(\n                \"Name of subfile within H5 file containing volume data to plot.\"\n                \" Optional if the H5 files have only one subfile.\"\n            ),\n        )\n        @click.option(\n            \"--list-vars\",\n            \"-l\",\n            is_flag=True,\n            help=\"Print available variables and exit.\",\n        )\n        @click.option(\n            \"--var\",\n            \"-y\",\n            \"vars_patterns\",\n            multiple=multiple_vars,\n            help=(\n                \"Variable to plot. List any tensor components \"\n                \"in the volume data file, such as 'Shift_x'. \"\n                \"Also accepts glob patterns like 'Shift_*'.\"\n            )\n            + (\" Can be specified multiple times.\" if multiple_vars else \"\")\n            + (\"  [required]\" if vars_required else \"\"),\n        )\n        @click.option(\n            \"--list-observations\",\n            \"--list-times\",\n            \"list_times\",\n            is_flag=True,\n            help=\"Print all available observation times and exit.\",\n        )\n        @click.option(\n            \"--step\",\n            callback=parse_step,\n            help=(\n                \"Observation step number. Specify '-1' or 'last' \"\n                \"for the last step in the file. \"\n                \"Mutually exclusive with '--time'.\"\n            ),\n        )\n        @click.option(\n            \"--time\",\n            type=float,\n            help=(\n                \"Observation time. \"\n                \"The observation step closest to the specified \"\n                \"time is selected. \"\n                \"Mutually exclusive with '--step'.\"\n            ),\n        )\n        # Preserve the original function's name and docstring\n        @functools.wraps(f)\n        def command(\n            h5_files,\n            subfile_name,\n            list_vars,\n            vars_patterns,\n            list_times,\n            step,\n            time,\n            **kwargs,\n        ):\n            # Print available subfile names and exit\n            if not subfile_name:\n                with spectre_h5.H5File(h5_files[0], \"r\") as open_h5_file:\n                    available_subfiles = open_h5_file.all_vol_files()\n                if len(available_subfiles) == 1:\n                    subfile_name = available_subfiles[0]\n                    logger.info(\n                        f\"Selected subfile {subfile_name}\"\n                        \" (the only available one).\"\n                    )\n                else:\n                    raise RequiredChoiceError(\n                        (\n                            \"Specify '--subfile-name' / '-d' to select a\"\n                            \" subfile containing volume data.\"\n                        ),\n                        choices=available_subfiles,\n                    )\n\n            # Print available observations/times and exit\n            if list_times:\n                import rich.columns\n\n                all_obs_ids, all_obs_times = list_observations(\n                    open_volfiles(h5_files, subfile_name)\n                )\n                rich.print(\n                    rich.columns.Columns(f\"{time:g}\" for time in all_obs_times)\n                )\n                return\n            # Select observation\n            if step is None and time is None:\n                if obs_id_required:\n                    all_obs_ids, all_obs_times = list_observations(\n                        open_volfiles(h5_files, subfile_name)\n                    )\n                    if len(all_obs_ids) == 1:\n                        obs_id, obs_time = all_obs_ids[0], all_obs_times[0]\n                        logger.info(\n                            f\"Selected observation at t = {obs_time:g}\"\n                            \" (the only available one).\"\n                        )\n                    else:\n                        raise click.UsageError(\n                            \"Specify '--step' or '--time' to select an\"\n                            \" observation in the volume data. The volume data\"\n                            f\" contains {len(all_obs_ids)} observations between\"\n                            f\" times {all_obs_times[0]} and\"\n                            f\" {all_obs_times[-1]}.\"\n                        )\n                else:\n                    obs_id, obs_time = None, None\n            else:\n                obs_id, obs_time = select_observation(\n                    open_volfiles(h5_files, subfile_name), step=step, time=time\n                )\n\n            # Print available variables and exit\n            for volfile in open_volfiles(h5_files, subfile_name, obs_id):\n                all_vars = volfile.list_tensor_components(\n                    obs_id or volfile.list_observation_ids()[0]\n                )\n                break\n            if list_vars:\n                import rich.columns\n\n                rich.print(rich.columns.Columns(all_vars))\n                return\n            elif vars_required and not vars_patterns:\n                raise RequiredChoiceError(\n                    \"Specify '--var' / '-y' to select a variable to plot.\",\n                    choices=all_vars,\n                )\n            # Expand globs in vars\n            vars = []\n            if not multiple_vars:\n                vars_patterns = [vars_patterns] if vars_patterns else []\n            for var_pattern in vars_patterns:\n                matched_vars = fnmatch.filter(all_vars, var_pattern)\n                if not matched_vars:\n                    raise RequiredChoiceError(\n                        f\"The pattern '{var_pattern}' matches no variables.\",\n                        choices=all_vars,\n                    )\n                vars.extend(matched_vars)\n            # Remove duplicates. Ordering is lost, but that's not important here.\n            vars = list(set(vars))\n            if not multiple_vars and len(vars) > 1:\n                raise click.UsageError(\"Only one variable can be selected.\")\n\n            return f(\n                h5_files=h5_files,\n                subfile_name=subfile_name,\n                obs_id=obs_id,\n                obs_time=obs_time,\n                **(\n                    dict(vars=vars)\n                    if multiple_vars\n                    else dict(var_name=vars[0] if vars else None)\n                ),\n                **kwargs,\n            )\n\n        return command\n\n    return decorator\n"
  },
  {
    "path": "src/IO/H5/Python/ReadH5.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\nfrom typing import Iterable, List, Tuple, Union\n\nimport h5py\nimport numpy as np\n\nlogger = logging.getLogger(__name__)\n\n\ndef available_subfiles(\n    h5files: Union[\n        str, Path, h5py.File, Iterable[str], Iterable[Path], Iterable[h5py.File]\n    ],\n    extension: str,\n) -> List[str]:\n    \"\"\"List all subfiles with the given 'extension' in the 'h5files'.\n\n    Parameters\n    ----------\n    h5files: Path(s) or open h5py file(s). Can be a single file or path, a list\n      of files or paths, or an iterable that opens and closes each file on\n      demand.\n    extension: str\n\n    Returns\n    -------\n    List of paths in the 'h5files' that end with the 'extension'\n    \"\"\"\n    # Make input files iterable. Strings and h5py files are iterable, so handle\n    # those first.\n    if isinstance(h5files, str) or isinstance(h5files, h5py.File):\n        h5files = [h5files]\n    try:\n        iter(h5files)\n    except TypeError:\n        h5files = [h5files]\n    subfiles = set()\n\n    def visitor(name):\n        if name.endswith(extension):\n            subfiles.add(name)\n\n    for h5file in h5files:\n        if isinstance(h5file, h5py.File):\n            h5file.visit(visitor)\n        else:\n            with h5py.File(h5file, \"r\") as open_h5_file:\n                open_h5_file.visit(visitor)\n    return sorted(subfiles)\n\n\ndef to_dataframe(\n    open_subfile: Union[h5py.Dataset, \"spectre.IO.H5.H5Dat\"], slice=None\n) -> \"pandas.DataFrame\":\n    \"\"\"Convert a '.dat' subfile to a Pandas DataFrame\n\n    This function isn't particularly complex, but it allows to convert a\n    subfile to a DataFrame in a single statement like this:\n\n        to_dataframe(open_h5_file[\"Norms.dat\"])\n\n    Without this function, you would have to store the subfile in an extra\n    variable to access its \"Legend\" attribute.\n\n    You can optionally pass a slice object which will slice the data for you\n    so the entire dataset isn't read in\n\n    Arguments:\n      open_subfile: An open h5py subfile representing a SpECTRE dat file,\n        or a spectre.IO.H5.H5Dat subfile, typically from a reductions file.\n      slice: A numpy slice object to choose specific rows. Defaults to None. If\n        you try to slice columns, an error will occur.\n\n    Returns: Pandas DataFrame with column names read from the \"Legend\"\n      attribute of the dat file.\n    \"\"\"\n    import pandas as pd\n\n    try:\n        # SpECTRE H5 dat subfile\n        data = np.asarray(open_subfile.get_data())\n        legend = open_subfile.get_legend()\n    except AttributeError:\n        # h5py subfile\n        data = open_subfile\n        legend = open_subfile.attrs[\"Legend\"]\n\n    if slice:\n        data = data[slice]\n\n    return pd.DataFrame(data, columns=legend)\n\n\ndef select_observation(\n    volfiles: Union[\"spectre.IO.H5.H5Vol\", Iterable[\"spectre.IO.H5.H5Vol\"]],\n    step: int = None,\n    time: float = None,\n) -> Tuple[int, float]:\n    \"\"\"Select an observation in the 'volfiles'\n\n    Arguments:\n      volfiles: Open spectre H5 volume files. Can be a single volfile or a list,\n        but can also be an iterator that opens and closes the files on demand.\n        The volfiles are assumed to be ordered in time, meaning that later\n        volfiles contain the same or later observation IDs than earlier\n        volfiles. This assumption should hold for volfiles from multiple nodes\n        in segments like 'Segment*/VolumeData*.h5' and exists to avoid opening\n        all volfiles from all segments. See 'step' and 'time' below for details.\n      step: Select the observation with this step number, counting unique\n        observation IDs from the start of the first volfile.\n        Mutually exclusive with 'time'.\n      time: Select the observation closest to this time. The search is stopped\n        once a volfile's closest time is further away than the previous.\n\n    Returns: Tuple of (observation ID, time).\n    \"\"\"\n    assert (step is None) != (\n        time is None\n    ), \"Specify either 'step' or 'time', but not both.\"\n    try:\n        iter(volfiles)\n    except TypeError:\n        volfiles = [volfiles]\n\n    if step is not None:\n        # Select the specified step\n        all_obs_ids = set()\n        for volfile in volfiles:\n            obs_ids = volfile.list_observation_ids()\n            idx = step - len(all_obs_ids)\n            if idx < len(obs_ids):\n                obs_id = obs_ids[idx]\n                obs_value = volfile.get_observation_value(obs_id)\n                logger.info(\n                    f\"Selected observation step {step} at t = {obs_value:g}.\"\n                )\n                return obs_id, obs_value\n            all_obs_ids.update(obs_ids)\n        raise ValueError(\n            f\"Number of observations ({len(all_obs_ids)}) is smaller than\"\n            f\" specified 'step' ({step}).\"\n        )\n    else:\n        # Find closest observation to the specified time\n        min_time_diff = np.inf\n        min_step, obs_id, obs_value = None, None, None\n        all_obs_ids = set()\n        for volfile in volfiles:\n            obs_ids = volfile.list_observation_ids()\n            obs_values = list(map(volfile.get_observation_value, obs_ids))\n            time_diff = np.abs(time - np.asarray(obs_values))\n            step = np.argmin(time_diff)\n            if time_diff[step] > min_time_diff:\n                # Times in this volfile are further away from the target than\n                # times in a previous volfile. Stop and return, since we assume\n                # that volfiles are ordered.\n                break\n            min_time_diff = time_diff[step]\n            min_step = len(all_obs_ids) + step\n            obs_id, obs_value = obs_ids[step], obs_values[step]\n            all_obs_ids.update(obs_ids)\n        if obs_value != time:\n            logger.info(\n                f\"Selected closest observation to t = {time}: \"\n                f\"step {min_step} at t = {obs_value:g}\"\n            )\n        return obs_id, obs_value\n\n\ndef list_observations(\n    volfiles: Union[\"spectre.IO.H5.H5Vol\", Iterable[\"spectre.IO.H5.H5Vol\"]],\n) -> Tuple[List[int], List[float]]:\n    \"\"\"Returns all observations in the 'volfiles' and their times\"\"\"\n    try:\n        iter(volfiles)\n    except TypeError:\n        volfiles = [volfiles]\n\n    all_obs_ids = []\n    all_obs_times = []\n    for volfile in volfiles:\n        obs_ids = [\n            obs_id\n            for obs_id in volfile.list_observation_ids()\n            if obs_id not in all_obs_ids\n        ]\n        obs_values = list(map(volfile.get_observation_value, obs_ids))\n        all_obs_ids.extend(obs_ids)\n        all_obs_times.extend(obs_values)\n    return all_obs_ids, all_obs_times\n"
  },
  {
    "path": "src/IO/H5/Python/TensorData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Python/TensorData.hpp\"\n\n#include <pybind11/numpy.h>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_tensordata(py::module& m) {\n  // Wrapper for TensorComponent\n  py::class_<TensorComponent>(m, \"TensorComponent\")\n      .def(py::init<std::string, DataVector>(), py::arg(\"name\"),\n           py::arg(\"data\"))\n      .def_readwrite(\"name\", &TensorComponent::name)\n      .def_property_readonly(\n          \"data\",\n          [](TensorComponent& self) {\n            // Return a numpy array view of the data with the correct dtype\n            // (float32 for std::vector<float> and float64 for DataVector)\n            return std::visit(\n                [&self](auto& storage) -> py::array {\n                  using ValueType =\n                      typename std::decay_t<decltype(storage)>::value_type;\n                  return py::array_t<ValueType>(\n                      {static_cast<py::ssize_t>(storage.size())},\n                      storage.data(), py::cast(&self));\n                },\n                self.data);\n          })\n      .def(\"__str__\", get_output<TensorComponent>)\n      .def(\"__repr__\", get_output<TensorComponent>)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self);\n\n  py::class_<ElementVolumeData>(m, \"ElementVolumeData\")\n      .def(py::init<std::string, std::vector<TensorComponent>,\n                    std::vector<size_t>, std::vector<Spectral::Basis>,\n                    std::vector<Spectral::Quadrature>>(),\n           py::arg(\"element_name\"), py::arg(\"components\"), py::arg(\"extents\"),\n           py::arg(\"basis\"), py::arg(\"quadrature\"))\n      .def(py::init<ElementId<1>, std::vector<TensorComponent>, Mesh<1>>(),\n           py::arg(\"element_id\"), py::arg(\"components\"), py::arg(\"mesh\"))\n      .def(py::init<ElementId<2>, std::vector<TensorComponent>, Mesh<2>>(),\n           py::arg(\"element_id\"), py::arg(\"components\"), py::arg(\"mesh\"))\n      .def(py::init<ElementId<3>, std::vector<TensorComponent>, Mesh<3>>(),\n           py::arg(\"element_id\"), py::arg(\"components\"), py::arg(\"mesh\"))\n      .def_readwrite(\"element_name\", &ElementVolumeData::element_name)\n      .def_readwrite(\"tensor_components\", &ElementVolumeData::tensor_components)\n      .def_readwrite(\"extents\", &ElementVolumeData::extents)\n      .def_readwrite(\"basis\", &ElementVolumeData::basis)\n      .def_readwrite(\"quadrature\", &ElementVolumeData::quadrature)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self);\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/TensorData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_tensordata(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/TransformVolumeData.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport importlib\nimport inspect\nimport logging\nimport re\nfrom dataclasses import dataclass\nfrom functools import cached_property\nfrom pydoc import locate\nfrom typing import Dict, Iterable, List, Optional, Sequence, Tuple, Type, Union\n\nimport click\nimport numpy as np\nimport rich\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import (\n    Frame,\n    InverseJacobian,\n    Jacobian,\n    Scalar,\n    Tensor,\n    tnsr,\n)\nfrom spectre.Domain import ElementId\nfrom spectre.IO.H5.IterElements import Element, iter_elements\nfrom spectre.NumericalAlgorithms.LinearOperators import definite_integral\nfrom spectre.Spectral import Mesh\nfrom spectre.support.CliExceptions import RequiredChoiceError\n\nlogger = logging.getLogger(__name__)\n\n\ndef snake_case_to_camel_case(s: str) -> str:\n    \"\"\"snake_case to CamelCase\"\"\"\n    return \"\".join([t.title() for t in s.split(\"_\")])\n\n\ndef parse_pybind11_signatures(callable) -> Iterable[inspect.Signature]:\n    # Pybind11 functions don't currently expose a signature that's easily\n    # readable, see issue https://github.com/pybind/pybind11/issues/945.\n    # Therefore, we parse the docstring. Its first line is always the function\n    # signature.\n    logger.debug(callable.__doc__)\n    all_matches = re.findall(\n        callable.__name__ + r\"\\((.+)\\) -> (.+)\", callable.__doc__\n    )\n    if not all_matches:\n        raise ValueError(\n            f\"Unable to extract signature for function '{callable.__name__}'. \"\n            \"Please make sure it is a pybind11 binding of a C++ function. \"\n            \"If it is, please file an issue and include the following \"\n            \"docstring:\\n\\n\"\n            + callable.__doc__\n        )\n    for match_args, match_ret in all_matches:\n        parameters = []\n        for arg in match_args.split(\",\"):\n            arg = arg.strip()\n            if \" = \" in arg:\n                arg, default_value = arg.split(\" = \")\n            else:\n                default = inspect.Parameter.empty\n            name, annotation = arg.split(\": \")\n            annotation = locate(annotation)\n            parameters.append(\n                inspect.Parameter(\n                    name=name,\n                    kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,\n                    default=default,\n                    annotation=annotation,\n                )\n            )\n        yield inspect.Signature(\n            parameters=parameters, return_annotation=locate(match_ret)\n        )\n\n\ndef get_tensor_component_names(\n    tensor_name: str, tensor_type: Type[Tensor]\n) -> List[str]:\n    \"\"\"Lists all independent components like 'Vector_x', 'Vector_y', etc.\"\"\"\n    # Pybind11 doesn't support class methods, so `component_suffix` is a member\n    # function. Construct a proxy object to call it.\n    tensor_proxy = tensor_type()\n    return [\n        tensor_name + tensor_proxy.component_suffix(i)\n        for i in range(tensor_type.size)\n    ]\n\n\nclass KernelArg:\n    \"\"\"Subclasses provide data for a kernel function argument\"\"\"\n\n    def get(self, all_tensor_data: Dict[str, Tensor], element: Element):\n        \"\"\"Get the data that will be passed to the kernel function\"\"\"\n        raise NotImplementedError\n\n\n@dataclass(frozen=True)\nclass TensorArg(KernelArg):\n    \"\"\"Kernel argument loaded from a volume file tensor dataset\"\"\"\n\n    tensor_type: Type[Tensor]\n    dataset_name: str\n    extract_single_component: bool = False\n\n    @cached_property\n    def component_names(self):\n        return get_tensor_component_names(self.dataset_name, self.tensor_type)\n\n    def get(\n        self, all_tensor_data: Dict[str, Tensor], element: Optional[Element]\n    ):\n        tensor_data = all_tensor_data[self.dataset_name]\n        if element:\n            components = np.asarray(tensor_data)[:, element.data_slice]\n            if self.extract_single_component:\n                return DataVector(components[0])\n            else:\n                return self.tensor_type(components)\n        else:\n            if self.extract_single_component:\n                return tensor_data.get()\n            else:\n                return tensor_data\n\n\n@dataclass(frozen=True)\nclass ElementArg(KernelArg):\n    \"\"\"Kernel argument retrieved from an element in the computational domain\"\"\"\n\n    element_attr: str\n\n    def get(self, all_tensor_data: Dict[str, Tensor], element: Element):\n        return getattr(element, self.element_attr)\n\n\ndef _get_dataset_name(\n    arg: inspect.Parameter,\n    map_input_names: Dict[str, str],\n    interactive: bool,\n):\n    if arg.name in map_input_names:\n        return map_input_names[arg.name]\n    if interactive:\n        return click.prompt(\n            f\"Dataset name for argument '{arg.name}'\",\n            default=snake_case_to_camel_case(arg.name),\n        )\n    else:\n        raise ValueError(\n            f\"Dataset name for argument '{arg.name}' is required. Add an\"\n            \" entry to 'map_input_names'.\"\n        )\n\n\ndef parse_kernel_arg(\n    arg: inspect.Parameter,\n    map_input_names: Dict[str, str],\n    interactive: bool = False,\n) -> KernelArg:\n    \"\"\"Determine how data for a kernel function argument will be retrieved\n\n    Examines the name and type annotation of the argument. The following\n    arguments are supported:\n\n    - Mesh: Annotate the argument with a 'Mesh' type.\n    - ElementId: Annotate the argument with an 'ElementId' type.\n    - Coordinates: Annotate the argument with a 'tnsr.I' type and name it\n        \"logical_coords\" / \"logical_coordinates\" or \"inertial_coords\" /\n        \"inertial_coordinates\" / \"x\".\n    - Jacobians: Annotate the argument with a 'Jacobian' or 'InverseJacobian'.\n    - Any tensor dataset: Annotate the argument with the tensor type, e.g.\n        'Scalar[DataVector]' or 'tnsr.ii[DataVector, 3]'.\n\n    For example, the following function is a possible kernel:\n\n        def one_index_constraint(\n            psi: Scalar[DataVector],\n            mesh: Mesh[3],\n            inv_jacobian: InverseJacobian[DataVector, 3],\n        ) -> tnsr.i[DataVector, 3]:\n            # ...\n\n    The function can also be a binding of a C++ function, such as this:\n\n        tnsr::i<DataVector, 3> one_index_constraint(\n            const Scalar<DataVector>& psi,\n            const Mesh<3>& mesh,\n            const InverseJacobian<\n              DataVector, 3, Frame::ElementLogical, Frame::Inertial>&\n              inv_jacobian) {\n            // ...\n        }\n\n    The annotation of the 'psi' argument is 'Scalar[DataVector]', so a scalar\n    dataset will be read from the volume data. If 'interactive' is True, the\n    user will be prompted for the dataset name to read for 'psi', otherwise the\n    'map_input_names' must contain a dataset name for 'psi'. In addition, the\n    mesh and inverse Jacobian will be read from the volume data and passed to\n    the function.\n\n    Arguments:\n      arg: The function argument. Must have a type annotation.\n      map_input_names: A map of argument names to dataset names.\n      interactive: Optional (default is False). Prompt the user for missing\n        dataset names.\n\n    Returns: A 'KernelArg' object that knows how to retrieve the data for the\n      argument.\n    \"\"\"\n    if arg.annotation is inspect.Parameter.empty:\n        raise ValueError(\n            \"Please annotate all arguments in the kernel function. \"\n            f\"Argument '{arg.name}' has no annotation.\"\n        )\n\n    if arg.annotation in Mesh.values():\n        return ElementArg(\"mesh\")\n    elif arg.annotation in ElementId.values():\n        return ElementArg(\"id\")\n    elif (\n        arg.name.endswith(\"_coords\")\n        or arg.name.endswith(\"_coordinates\")\n        or arg.name == \"x\"\n    ):\n        if arg.name == \"x\":\n            coords_name = \"inertial_coordinates\"\n        else:\n            coords_name = arg.name.replace(\"_coords\", \"_coordinates\")\n        expected_frame = {\n            \"logical\": Frame.ElementLogical,\n            \"grid\": Frame.Grid,\n            \"distorted\": Frame.Distorted,\n            \"inertial\": Frame.Inertial,\n        }[coords_name.split(\"_\")[0]]\n        assert arg.annotation in [\n            tnsr.I[DataVector, 1, expected_frame],\n            tnsr.I[DataVector, 2, expected_frame],\n            tnsr.I[DataVector, 3, expected_frame],\n        ], (\n            f\"Argument '{arg.name}' has unexpected type \"\n            f\"'{arg.annotation}'. Expected a \"\n            f\"'tnsr.I[DataVector, Dim, Frame.{expected_frame}]'.\"\n        )\n        return ElementArg(coords_name)\n    elif arg.annotation in [\n        Jacobian[DataVector, 1],\n        Jacobian[DataVector, 2],\n        Jacobian[DataVector, 3],\n    ]:\n        return ElementArg(\"jacobian\")\n    elif arg.annotation in [\n        InverseJacobian[DataVector, 1],\n        InverseJacobian[DataVector, 2],\n        InverseJacobian[DataVector, 3],\n    ]:\n        return ElementArg(\"inv_jacobian\")\n    elif arg.annotation == DataVector:\n        return TensorArg(\n            tensor_type=Scalar[DataVector],\n            dataset_name=_get_dataset_name(arg, map_input_names, interactive),\n            extract_single_component=True,\n        )\n    else:\n        try:\n            arg.annotation.rank\n        except AttributeError:\n            raise ValueError(\n                f\"Unknown argument type '{arg.annotation}' for argument \"\n                f\"'{arg.name}'. It must be either a Tensor or one of \"\n                \"the supported structural types listed in \"\n                \"'TransformVolumeData.parse_kernel_arg'.\"\n            )\n        return TensorArg(\n            tensor_type=arg.annotation,\n            dataset_name=_get_dataset_name(arg, map_input_names, interactive),\n        )\n\n\ndef parse_kernel_output(\n    output, output_name: Optional[str], num_points: int\n) -> Dict[str, Tensor]:\n    \"\"\"Transform the return value of a kernel to a dict of tensor datasets\n\n    The following return values of kernels are supported:\n\n    - Any Tensor.\n    - A DataVector: will be treated as a scalar.\n    - A Numpy array: will be treated as a scalar or vector.\n    - An int or float: will be expanded over the grid as a constant scalar.\n    - A dict of the above: can be used to return multiple datasets, and to\n      assign names to them. The keys of the dictionary are used as dataset\n      names.\n    - A list of the above: can be used to visualize output from C++ bindings\n      that return a `std::array` or `std::vector`. For example, a C++ binding\n      named `truncation_error` that returns 3 numbers (one per dimension) per\n      element can be visualized with this. The three numbers get expanded as\n      constants over the volume of the element and returned as three datasets\n      named 'TruncationError(_1,_2,_3)'.\n\n    Arguments:\n      output: The return value of a kernel function.\n      output_name: The dataset name. Unused if 'output' is a dict.\n      num_points: Number of grid points.\n\n    Returns: A mapping from dataset names to tensors.\n    \"\"\"\n    # Single tensor: return directly\n    try:\n        output.rank\n        return {output_name: output}\n    except AttributeError:\n        pass\n    # DataVector: parse as scalar\n    if isinstance(output, DataVector):\n        assert len(output) == num_points, (\n            f\"DataVector output named '{output_name}' has size \"\n            f\"{len(output)}, but expected size {num_points}.\"\n        )\n        return {output_name: Scalar[DataVector]([output])}\n    # Numpy array: parse as scalar or vector\n    if isinstance(output, np.ndarray):\n        assert output.shape[-1] == num_points, (\n            f\"Array output named '{output_name}' has shape {output.shape}, \"\n            f\"but expected {num_points} points in last dimension.\"\n        )\n        if output.ndim == 1 or (output.ndim == 2 and len(output) == 1):\n            return {output_name: Scalar[DataVector](output)}\n        elif output.ndim == 2 and len(output) in [1, 2, 3]:\n            dim = len(output)\n            return {output_name: tnsr.I[DataVector, dim](output)}\n        else:\n            raise ValueError(\n                \"Kernels may only return Numpy arrays representing \"\n                \"scalars or vectors. For higher-rank tensors, construct and \"\n                \"return the Tensor. \"\n                f\"Kernel output named '{output_name}' has shape \"\n                f\"{output.shape}.\"\n            )\n    # Single number: parse as constant scalar\n    if isinstance(output, (int, float)):\n        return {\n            output_name: Scalar[DataVector](num_points=num_points, fill=output)\n        }\n    # Dict or other map type: parse recursively, and use keys as dataset names\n    try:\n        result = {}\n        for name, value in output.items():\n            result.update(parse_kernel_output(value, name, num_points))\n        return result\n    except AttributeError:\n        pass\n    # List or other sequence: parse recursively, and enumerate dataset names\n    try:\n        result = {}\n        for i, value in enumerate(output):\n            result.update(\n                parse_kernel_output(\n                    value, output_name + \"_\" + str(i + 1), num_points\n                )\n            )\n        return result\n    except AttributeError:\n        pass\n    raise ValueError(\n        f\"Unsupported kernel output type '{type(output)}' \"\n        f\"(named '{output_name}'). \"\n        \"See 'TransformVolumeData.parse_kernel_output' \"\n        \"for supported kernel output types.\"\n    )\n\n\nclass Kernel:\n    def __init__(\n        self,\n        callable,\n        args: Optional[Sequence[KernelArg]] = None,\n        output_name: Optional[str] = None,\n        map_input_names: Dict[str, str] = {},\n        elementwise: Optional[bool] = None,\n        interactive: bool = False,\n    ):\n        \"\"\"Transforms volume data with a Python function\n\n        Arguments:\n          callable: A Python function that takes and returns tensors\n            (from 'spectre.DataStructures.Tensor') or a limited set of\n            structural information such as the mesh, coordinates, and Jacobians.\n            See the 'parse_kernel_arg' function for all supported argument\n            types. The function should return a single tensor, a dictionary that\n            maps dataset names to tensors, or one of the other supported types\n            listed in the 'parse_kernel_output' function.\n          args: Optional list of kernel arguments (KernelArg objects). If\n            unspecified, the arguments will be parsed from the 'callable'.\n          output_name: Name of the output dataset. Output names for multiple\n            datasets can be specified by returning a 'Dict[str, Tensor]' from\n            the 'callable' and setting the 'output_name' to None.\n          map_input_names: A map of argument names to dataset names.\n          elementwise: Call this kernel for each element. The default is to\n            call the kernel with all data in the volume file at once, unless\n            element-specific data such as a Mesh or Jacobian is requested.\n          interactive: Optional (default is False). Prompt the user for missing\n            dataset names and to select from multiple overloads of a pybind11\n            binding.\n        \"\"\"\n        self.callable = callable\n        if args is None:\n            # Parse function arguments\n            try:\n                # Try to parse as native Python function\n                signature = inspect.signature(callable)\n            except ValueError:\n                # Try to parse as pybind11 binding\n                overloads = list(parse_pybind11_signatures(callable))\n                # The function may have multiple overloads. Prompt the user to\n                # select one.\n                if len(overloads) == 1:\n                    signature = overloads[0]\n                elif interactive:\n                    rich.print(\n                        \"Available overloads:\\n\"\n                        + \"\\n\".join(\n                            [\n                                re.sub(\n                                    r\"spectre\\.\\S*\\._Pybindings\\.\",\n                                    \"\",\n                                    f\"{i + 1}. {callable.__name__}{overload}\",\n                                )\n                                for i, overload in enumerate(overloads)\n                            ]\n                        )\n                    )\n                    signature = overloads[\n                        click.prompt(\n                            f\"Select an overload (1 - {len(overloads)})\",\n                            type=int,\n                        )\n                        - 1\n                    ]\n                else:\n                    raise ValueError(\n                        f\"Function '{callable.__name__}' has multiple\"\n                        \" overloads. Wrap it in a Python function to select an\"\n                        \" overload.\"\n                    )\n            self.args = [\n                parse_kernel_arg(arg, map_input_names, interactive=interactive)\n                for arg in signature.parameters.values()\n            ]\n        else:\n            self.args = args\n        # If any argument is not a Tensor then we have to call the kernel\n        # elementwise\n        if elementwise:\n            self.elementwise = True\n        else:\n            self.elementwise = any(\n                isinstance(arg, ElementArg) for arg in self.args\n            )\n            assert elementwise is None or elementwise == self.elementwise, (\n                f\"Kernel '{callable.__name__}' must be called elementwise \"\n                \"because an argument is not a pointwise tensor.\"\n            )\n        # Use provided output name, or prompt the user for an output name\n        if output_name:\n            self.output_name = output_name\n        elif interactive:\n            self.output_name = click.prompt(\n                \"Output dataset name\",\n                default=snake_case_to_camel_case(callable.__name__),\n            )\n        else:\n            self.output_name = None\n\n    def __call__(\n        self, all_tensor_data: Dict[str, Tensor], element: Optional[Element]\n    ) -> Dict[str, Tensor]:\n        output = self.callable(\n            *(arg.get(all_tensor_data, element) for arg in self.args)\n        )\n        # Get the number of grid points from the element or from any tensor\n        num_points = (\n            element.mesh.number_of_grid_points()\n            if element\n            else len(next(iter(all_tensor_data.values()))[0])\n        )\n        return parse_kernel_output(output, self.output_name, num_points)\n\n\ndef transform_volume_data(\n    volfiles: Union[spectre_h5.H5Vol, Iterable[spectre_h5.H5Vol]],\n    kernels: Sequence[Kernel],\n    start_time: Optional[float] = None,\n    stop_time: Optional[float] = None,\n    stride: int = 1,\n    integrate: bool = False,\n    force: bool = False,\n) -> Union[None, Dict[str, Sequence[float]]]:\n    \"\"\"Transforms data in the 'volfiles' with a sequence of 'kernels'\n\n    Arguments:\n      volfiles: Iterable of open H5 volume files, or a single H5 volume file.\n        Must be opened in writable mode, e.g. in mode \"a\". The transformed\n        data will be written back into these files. All observations in these\n        files will be transformed.\n      kernels: List of transformations to apply to the volume data in the form\n        of 'Kernel' objects.\n      start_time: The earliest time at which to start processing data. The\n        start-time value is included.\n      stop_time: The time at which to stop processing data. The stop-time value\n        is included.\n      stride: Process only every 'stride'th time step.\n      integrate: Compute the volume integral over the kernels instead of\n        writing them back into the volume files. The integral is computed in\n        inertial coordinates for every tensor component of all kernels and over\n        all observations in the volume files. For example, if a kernel returns a\n        vector named 'Shift' then this function returns integrals named\n        'Shift(_x,_y,_z)'. In addition, the corresponding observation values are\n        returned (named 'Time'), and the inertial volume (named 'Volume').\n      force: Overwrite existing datasets in the volume files (default: False).\n\n    Returns:\n      None, or the volume integrals if 'integrate' is True.\n    \"\"\"\n    # Collect all tensor components that we need to apply the kernels\n    all_tensors: Dict[str, TensorArg] = {}\n    for kernel in kernels:\n        for tensor_arg in kernel.args:\n            # Skip non-tensors\n            if not isinstance(tensor_arg, TensorArg):\n                continue\n            tensor_name = tensor_arg.dataset_name\n            tensor_type = tensor_arg.tensor_type\n            if tensor_name in all_tensors:\n                assert all_tensors[tensor_name].tensor_type == tensor_type, (\n                    \"Two tensor arguments with the same name \"\n                    f\"'{tensor_name}' have different types: \"\n                    f\"'{all_tensors[tensor_name].tensor_type}' \"\n                    f\"and '{tensor_type}'\"\n                )\n            else:\n                all_tensors[tensor_name] = tensor_arg\n    logger.debug(\"Input datasets: \" + str(list(all_tensors.keys())))\n\n    if integrate:\n        # We collect integrals in this dict and return it\n        integrals: Dict[str, Sequence[float]] = {}\n    else:\n        # We collect output dataset names for logging so the user can find them\n        output_names = set()\n\n    if isinstance(volfiles, spectre_h5.H5Vol):\n        volfiles = [volfiles]\n    for volfile in volfiles:\n        all_observation_ids = np.array(\n            volfile.list_observation_ids(),\n            dtype=np.uint64,\n        )\n        all_times = np.array(\n            [\n                volfile.get_observation_value(obs_id)\n                for obs_id in all_observation_ids\n            ]\n        )\n        # Filter observations\n        observation_filter = np.ones_like(all_times, dtype=bool)\n        if start_time is not None:\n            observation_filter &= all_times >= start_time\n        if stop_time is not None:\n            observation_filter &= all_times <= stop_time\n        all_observation_ids = all_observation_ids[observation_filter][::stride]\n        all_times = all_times[observation_filter][::stride]\n        num_obs = len(all_observation_ids)\n\n        if integrate and \"Time\" not in integrals:\n            integrals[\"Time\"] = all_times\n\n        for i_obs, obs_id in enumerate(all_observation_ids):\n            # Load tensor data for all kernels\n            all_tensor_data = {\n                tensor_name: tensor_arg.tensor_type(\n                    np.array(\n                        [\n                            volfile.get_tensor_component(\n                                obs_id, component_name\n                            ).data\n                            for component_name in tensor_arg.component_names\n                        ]\n                    )\n                )\n                for tensor_name, tensor_arg in all_tensors.items()\n            }\n            total_num_points = np.sum(\n                np.prod(volfile.get_extents(obs_id), axis=1)\n            )\n\n            # For integrals we call the kernels elementwise, take the integral\n            # over the elements, and sum up the contributions. We then skip\n            # ahead to the next observation because we're not writing anything\n            # back to disk.\n            if integrate:\n                for element in iter_elements(volfile, obs_id):\n                    # Integrate volume\n                    volume = integrals.setdefault(\"Volume\", np.zeros(num_obs))\n                    volume[i_obs] += definite_integral(\n                        element.det_jacobian.get(), element.mesh\n                    )\n                    # Integrate kernels\n                    for kernel in kernels:\n                        transformed_tensors = kernel(all_tensor_data, element)\n                        for (\n                            output_name,\n                            transformed_tensor,\n                        ) in transformed_tensors.items():\n                            for i, component in enumerate(transformed_tensor):\n                                component_integral = integrals.setdefault(\n                                    output_name\n                                    + transformed_tensor.component_suffix(i),\n                                    np.zeros(num_obs),\n                                )\n                                component_integral[i_obs] += definite_integral(\n                                    element.det_jacobian.get() * component,\n                                    element.mesh,\n                                )\n                continue\n\n            # Apply kernels\n            for kernel in kernels:\n                # Elementwise kernels need to slice the tensor data into\n                # elements, and reassemble the result into a contiguous dataset\n                if kernel.elementwise:\n                    transformed_tensors_data: Dict[\n                        str, Tuple[np.ndarray, Type[Tensor]]\n                    ] = {}\n                    for element in iter_elements(volfile, obs_id):\n                        transformed_tensors = kernel(all_tensor_data, element)\n                        for (\n                            output_name,\n                            transformed_tensor,\n                        ) in transformed_tensors.items():\n                            transformed_tensor_data = (\n                                transformed_tensors_data.setdefault(\n                                    output_name,\n                                    (\n                                        np.zeros(\n                                            (\n                                                transformed_tensor.size,\n                                                total_num_points,\n                                            )\n                                        ),\n                                        type(transformed_tensor),\n                                    ),\n                                )\n                            )[0]\n                            for i, component in enumerate(transformed_tensor):\n                                transformed_tensor_data[\n                                    i, element.data_slice\n                                ] = component\n                    transformed_tensors = {\n                        output_name: tensor_type(\n                            transformed_tensor_data, copy=False\n                        )\n                        for output_name, (\n                            transformed_tensor_data,\n                            tensor_type,\n                        ) in transformed_tensors_data.items()\n                    }\n                else:\n                    transformed_tensors = kernel(all_tensor_data, None)\n\n                # Write result back into volfile\n                for (\n                    output_name,\n                    transformed_tensor,\n                ) in transformed_tensors.items():\n                    output_names.add(output_name)\n                    for i, component in enumerate(transformed_tensor):\n                        volfile.write_tensor_component(\n                            obs_id,\n                            component_name=(\n                                output_name\n                                + transformed_tensor.component_suffix(i)\n                            ),\n                            contiguous_tensor_data=component,\n                            overwrite_existing=force,\n                        )\n    if integrate:\n        return integrals\n    else:\n        logger.info(f\"Output datasets: {output_names}\")\n\n\ndef parse_input_names(ctx, param, all_values):\n    if all_values is None:\n        return {}\n    input_names = {}\n    for value in all_values:\n        key, value = value.split(\"=\")\n        input_names[key] = value\n    return input_names\n\n\ndef parse_kernels(kernels, exec_files, map_input_names, interactive=False):\n    # Load kernels from 'exec_files'\n    for exec_file in exec_files:\n        exec(exec_file.read(), globals(), globals())\n    # Look up all kernels\n    for kernel in kernels:\n        if \".\" in kernel:\n            # A module path was specified. Import function from the module\n            kernel_module_path, kernel_function = kernel.rsplit(\".\", maxsplit=1)\n            kernel_module = importlib.import_module(kernel_module_path)\n            if \":\" in kernel_function:\n                kernel_class, kernel_function = kernel_function.split(\":\")\n                kernel_class = getattr(kernel_module, kernel_class)\n                kernel_function = getattr(kernel_class, kernel_function)\n            else:\n                kernel_function = getattr(kernel_module, kernel_function)\n            yield Kernel(\n                kernel_function,\n                output_name=None,\n                map_input_names=map_input_names,\n                interactive=interactive,\n            )\n        elif kernel.startswith(\"Element:\"):\n            attr_name = kernel[len(\"Element:\") :]\n            yield Kernel(\n                lambda x: x,\n                args=[ElementArg(attr_name)],\n                output_name=snake_case_to_camel_case(attr_name),\n                interactive=interactive,\n            )\n        else:\n            # Only a function name was specified. Look up in 'globals()'.\n            yield Kernel(\n                globals()[kernel],\n                output_name=None,\n                map_input_names=map_input_names,\n                interactive=interactive,\n            )\n\n\n@click.command(name=\"transform-volume-data\")\n@click.argument(\n    \"h5files\",\n    nargs=-1,\n    required=True,\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.option(\n    \"--subfile-name\",\n    \"-d\",\n    help=\"Name of volume data subfile within each h5 file.\",\n)\n@click.option(\n    \"--kernel\",\n    \"-k\",\n    \"kernels\",\n    multiple=True,\n    help=(\n        \"Python function to apply to the volume data. \"\n        \"Specify as 'path.to.module:function_name', where the \"\n        \"module must be available to import. \"\n        \"Alternatively, specify just 'function_name' if the \"\n        \"function is defined in one of the '--exec' / '-e' \"\n        \"files. Can be specified multiple times.\"\n    ),\n)\n@click.option(\n    \"--exec\",\n    \"-e\",\n    \"exec_files\",\n    type=click.File(\"r\"),\n    multiple=True,\n    help=(\n        \"Python file to execute before loading kernels. \"\n        \"Load kernels from this file with the '--kernel' / '-k' \"\n        \"option. Can be specified multiple times.\"\n    ),\n)\n@click.option(\n    \"--input-name\",\n    \"-i\",\n    \"map_input_names\",\n    multiple=True,\n    callback=parse_input_names,\n    help=(\n        \"Map of function argument name to dataset name in the volume data file.\"\n        \" Specify key-value pair like 'spatial_metric=SpatialMetric'. Can be\"\n        \" specified multiple times. If unspecified, the argument name is\"\n        \" transformed to CamelCase.\"\n    ),\n)\n@click.option(\n    \"--start-time\",\n    type=float,\n    help=(\n        \"The earliest time at which to start processing data. The start-time \"\n        \"value is included.\"\n    ),\n)\n@click.option(\n    \"--stop-time\",\n    type=float,\n    help=(\n        \"The time at which to stop processing data. The stop-time value is \"\n        \"included.\"\n    ),\n)\n@click.option(\n    \"--stride\",\n    default=1,\n    type=int,\n    help=\"Process only every stride'th time step\",\n)\n@click.option(\n    \"--integrate\",\n    is_flag=True,\n    help=(\n        \"Compute the volume integral over the kernels instead of \"\n        \"writing them back into the data files. \"\n        \"Specify '--output' / '-o' to write the integrals to \"\n        \"a file.\"\n    ),\n)\n@click.option(\n    \"--output\",\n    \"-o\",\n    type=click.Path(writable=True),\n    help=(\n        \"Output file for integrals. Either a '.txt' file \"\n        \"or a '.h5' file. Also requires the '--output-subfile' \"\n        \"option if a '.h5' file is used. \"\n        \"Only used if the '--integrate' flag is set.\"\n    ),\n)\n@click.option(\n    \"--output-subfile\",\n    help=\"Subfile name in the '--output' / '-o' file, if it is an H5 file.\",\n)\n@click.option(\n    \"--force\",\n    \"-f\",\n    is_flag=True,\n    help=\"Overwrite existing data.\",\n)\ndef transform_volume_data_command(\n    h5files,\n    subfile_name,\n    kernels,\n    exec_files,\n    map_input_names,\n    integrate,\n    output,\n    output_subfile,\n    force,\n    **kwargs,\n):\n    \"\"\"Transform volume data with Python functions\n\n    Run Python functions (kernels) over all volume data in the 'H5FILES' and\n    write the output data back into the same files. You can use any Python\n    function as kernel that takes tensors as input arguments and returns a\n    tensor (from 'spectre.DataStructures.Tensor'). The function must be\n    annotated with tensor types, like this:\n\n        def shift_magnitude(\n                shift: tnsr.I[DataVector, 3],\n                spatial_metric: tnsr.ii[DataVector, 3]) -> Scalar[DataVector]:\n            # ...\n\n    Any pybind11 binding of a C++ function will also work, as long as it takes\n    only supported types as arguments. Supported types are tensors, as well as\n    structural information such as the mesh, coordinates, and Jacobians. See the\n    'parse_kernel_arg' function for all supported argument types, and\n    'parse_kernel_output' for all supported return types.\n\n    The kernels can be loaded from any available Python module. Specify them as\n    'path.to.module:function_name' or 'path.to.module.class:function_name'. You\n    can also load a Python file that defines kernels with the '--exec' / '-e'\n    option and then just specify the kernel as 'function_name'.\n    Examples of useful kernels:\n\n    - Anything in 'spectre.PointwiseFunctions'\n    - 'spectre.Spectral.Mesh3D:extents'\n    - 'spectre.NumericalAlgorithms.LinearOperators:relative_truncation_error'\n      and 'absolute_truncation_error'\n    - Coordinates in different frames like 'Element:grid_coordinates' or\n      'Element:distorted_coordinates'\n\n    ## Input and output dataset names\n\n    You will be prompted to specify dataset names for input and output tensors,\n    unless you specify them with '--input-name/-i'. For example, if you specify\n    '-i shift=Shift' for the kernel function above, the code will read the\n    datasets 'Shift(_x,_y,_z)' from the volume data and pass them to the kernel\n    function for the 'shift' argument.\n    \"\"\"\n    open_h5_files = [\n        spectre_h5.H5File(filename, \"r\" if integrate else \"a\")\n        for filename in h5files\n    ]\n\n    # Print available subfile names and exit\n    if not subfile_name:\n        available_subfile_names = open_h5_files[0].all_vol_files()\n        if len(available_subfile_names) == 1:\n            subfile_name = available_subfile_names[0]\n            logger.info(\n                f\"Selected subfile {subfile_name} (the only available one).\"\n            )\n        else:\n            raise RequiredChoiceError(\n                (\n                    \"Specify '--subfile-name' / '-d' to select a\"\n                    \" subfile containing volume data.\"\n                ),\n                choices=available_subfile_names,\n            )\n\n    volfiles = [h5file.get_vol(subfile_name) for h5file in open_h5_files]\n\n    # Load kernels\n    if not kernels:\n        raise click.UsageError(\"No '--kernel' / '-k' specified.\")\n    kernels = list(\n        parse_kernels(kernels, exec_files, map_input_names, interactive=True)\n    )\n\n    # Apply!\n    import rich.progress\n\n    progress = rich.progress.Progress(\n        rich.progress.TextColumn(\"[progress.description]{task.description}\"),\n        rich.progress.BarColumn(),\n        rich.progress.MofNCompleteColumn(),\n        rich.progress.TimeRemainingColumn(),\n        disable=(len(volfiles) == 1),\n    )\n    task_id = progress.add_task(\"Applying to files\")\n    volfiles_progress = progress.track(volfiles, task_id=task_id)\n    with progress:\n        integrals = transform_volume_data(\n            volfiles_progress,\n            kernels=kernels,\n            integrate=integrate,\n            force=force,\n            **kwargs,\n        )\n        progress.update(task_id, completed=len(volfiles))\n\n    # Write integrals to output file or print to terminal\n    if integrate:\n        integral_values = np.stack(list(integrals.values())).T\n        integral_names = list(integrals.keys())\n        if output:\n            if output.endswith(\".h5\"):\n                if not output_subfile:\n                    raise click.UsageError(\n                        \"The '--output-subfile' option is required \"\n                        \"when writing to H5 files.\"\n                    )\n                if not output_subfile.startswith(\"/\"):\n                    output_subfile = \"/\" + output_subfile\n                if output_subfile.endswith(\".dat\"):\n                    output_subfile = output_subfile[:-4]\n                with spectre_h5.H5File(output, \"a\") as open_output_file:\n                    integrals_file = open_output_file.insert_dat(\n                        output_subfile, legend=integral_names, version=1\n                    )\n                    integrals_file.append(integral_values)\n            else:\n                np.savetxt(\n                    output, integral_values, header=\",\".join(integral_names)\n                )\n        else:\n            import rich.table\n\n            table = rich.table.Table(*integral_names, box=None)\n            for i in range(len(integral_values)):\n                table.add_row(*[f\"{v:g}\" for v in integral_values[i]])\n            rich.print(table)\n\n\nif __name__ == \"__main__\":\n    transform_volume_data_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/IO/H5/Python/VolumeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Python/VolumeData.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_h5vol(py::module& m) {\n  // Wrapper for basic H5VolumeData operations\n  py::class_<h5::VolumeData>(m, \"H5Vol\")\n      .def_static(\"extension\", &h5::VolumeData::extension)\n      .def(\"get_header\", &h5::VolumeData::get_header)\n      .def(\"get_version\", &h5::VolumeData::get_version)\n      .def(\"get_dimension\", &h5::VolumeData::get_dimension)\n      .def(\"write_volume_data\", &h5::VolumeData::write_volume_data,\n           py::arg(\"observation_id\"), py::arg(\"observation_value\"),\n           py::arg(\"elements\"), py::arg(\"serialized_domain\") = std::nullopt,\n           py::arg(\"serialized_observation_functions_of_time\") = std::nullopt,\n           py::arg(\"serialized_global_functions_of_time\") = std::nullopt)\n      .def(\"write_tensor_component\",\n           py::overload_cast<size_t, const std::string&, const DataVector&,\n                             bool>(&h5::VolumeData::write_tensor_component),\n           py::arg(\"observation_id\"), py::arg(\"component_name\"),\n           py::arg(\"contiguous_tensor_data\"),\n           py::arg(\"overwrite_existing\") = false)\n      .def(\"list_observation_ids\", &h5::VolumeData::list_observation_ids)\n      .def(\"get_observation_value\", &h5::VolumeData::get_observation_value,\n           py::arg(\"observation_id\"))\n      .def(\"find_observation_id\", &h5::VolumeData::find_observation_id,\n           py::arg(\"observation_value\"),\n           py::arg(\"observation_value_epsilon\") = std::nullopt)\n      .def(\"get_grid_names\", &h5::VolumeData::get_grid_names,\n           py::arg(\"observation_id\"))\n      .def(\"list_tensor_components\", &h5::VolumeData::list_tensor_components,\n           py::arg(\"observation_id\"))\n      .def(\"get_tensor_component\", &h5::VolumeData::get_tensor_component,\n           py::arg(\"observation_id\"), py::arg(\"tensor_component\"))\n      .def(\"get_extents\", &h5::VolumeData::get_extents,\n           py::arg(\"observation_id\"))\n      .def(\"get_quadratures\", &h5::VolumeData::get_quadratures,\n           py::arg(\"observation_id\"))\n      .def(\"get_bases\", &h5::VolumeData::get_bases, py::arg(\"observation_id\"))\n      .def(\"get_domain\", &h5::VolumeData::get_domain)\n      .def(\"get_functions_of_time\", &h5::VolumeData::get_functions_of_time,\n           py::arg(\"observation_id\"))\n      .def(\"get_global_functions_of_time\",\n           &h5::VolumeData::get_global_functions_of_time)\n      .def(\"get_data_by_element\", &h5::VolumeData::get_data_by_element,\n           py::arg(\"start_observation_value\"), py::arg(\"end_observation_value\"),\n           py::arg(\"components_to_retrieve\") = std::nullopt)\n      .def(\"extend_connectivity_data_1d\",\n           &h5::VolumeData::extend_connectivity_data<1>,\n           py::arg(\"observation_ids\"))\n      .def(\"extend_connectivity_data_2d\",\n           &h5::VolumeData::extend_connectivity_data<2>,\n           py::arg(\"observation_ids\"))\n      .def(\"extend_connectivity_data_3d\",\n           &h5::VolumeData::extend_connectivity_data<3>,\n           py::arg(\"observation_ids\"));\n  m.def(\"offset_and_length_for_grid\", &h5::offset_and_length_for_grid,\n        py::arg(\"grid_name\"), py::arg(\"all_grid_names\"),\n        py::arg(\"all_extents\"));\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/VolumeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_h5vol(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/IO/H5/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\nfrom .OpenVolfiles import *\nfrom .ReadH5 import *\n"
  },
  {
    "path": "src/IO/H5/SourceArchive.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/SourceArchive.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/Helpers.hpp\"\n#include \"Utilities/Formaline.hpp\"\n\nnamespace h5 {\nSourceArchive::SourceArchive(const bool exists, detail::OpenGroup&& group,\n                             const hid_t location, const std::string& name)\n    : group_(std::move(group)),\n      path_(group_.group_path_with_trailing_slash() + name) {\n  if (exists) {\n    source_archive_ =\n        read_data<1, std::vector<char>>(location, name + extension());\n  } else {\n    source_archive_ = formaline::get_archive();\n    write_data(location, source_archive_, {source_archive_.size()},\n               name + extension());\n  }\n}\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/SourceArchive.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <hdf5.h>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Writes an archive of the source tree into a dataset.\n */\nclass SourceArchive : public h5::Object {\n public:\n  /// \\cond HIDDEN_SYMOLS\n  static std::string extension() { return \".tar.gz\"; }\n\n  SourceArchive(bool exists, detail::OpenGroup&& group, hid_t location,\n                const std::string& name);\n\n  SourceArchive(const SourceArchive&) = delete;\n  SourceArchive& operator=(const SourceArchive&) = delete;\n\n  SourceArchive(SourceArchive&&) = delete;             // NOLINT\n  SourceArchive& operator=(SourceArchive&&) = delete;  // NOLINT\n\n  ~SourceArchive() override = default;\n  /// \\endcond\n\n  const std::vector<char>& get_archive() const { return source_archive_; }\n\n  const std::string& subfile_path() const override { return path_; }\n\n private:\n  /// \\cond HIDDEN_SYMBOLS\n  detail::OpenGroup group_;\n  std::vector<char> source_archive_;\n  std::string path_;\n  /// \\endcond\n};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/SpectralIo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"SpectralIo.hpp\"\n\n#include <array>\n#include <string>\n#include <vector>\n\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\nnamespace h5_detail {\nvoid write_dictionary(const std::string& dict_name,\n                      const std::vector<std::string>& values,\n                      const h5::detail::OpenGroup& observation_group) {\n  h5::write_to_attribute<std::string>(observation_group.id(), dict_name,\n                                      values);\n}\n\nstd::vector<std::string> decode_with_dictionary_name(\n    const std::string& dict_name, const std::vector<int>& decodable,\n    const h5::detail::OpenGroup& observation_group) {\n  const auto dict =\n      h5::read_rank1_attribute<std::string>(observation_group.id(), dict_name);\n\n  std::vector<std::string> decoded(decodable.size());\n  for (size_t i = 0; i < decodable.size(); i++) {\n    decoded[i] = dict[static_cast<size_t>(decodable[i])];\n  }\n  return decoded;\n}\n\n}  // namespace h5_detail\n"
  },
  {
    "path": "src/IO/H5/SpectralIo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <string>\n#include <vector>\n\nnamespace h5::detail {\nclass OpenGroup;\n}  // namespace h5::detail\n\nnamespace h5_detail {\n/// Write a dictionary as an attribute to the volume file, can be used\n/// to decode integer sequence as values[i] represents the string\n/// value encoded with integer i in the h5 file\nvoid write_dictionary(const std::string& dict_name,\n                      const std::vector<std::string>& values,\n                      const h5::detail::OpenGroup& observation_group);\n\n/// A dictionary `dict_name` is used to decode the integer vector `decodable`\n/// into an vector of strings.  The `dict_name` should correspond to\n/// a dictionary written with `h5_detail::write_dictionary`\nstd::vector<std::string> decode_with_dictionary_name(\n    const std::string& dict_name, const std::vector<int>& decodable,\n    const h5::detail::OpenGroup& observation_group);\n\n}  // namespace h5_detail\n"
  },
  {
    "path": "src/IO/H5/StellarCollapseEos.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/StellarCollapseEos.hpp\"\n\n#include <algorithm>\n#include <hdf5.h>\n#include <ostream>\n\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace h5 {\nStellarCollapseEos::StellarCollapseEos(bool exists, detail::OpenGroup&& group,\n                                       const hid_t /*location*/,\n                                       const std::string& name)\n    : group_(std::move(group)),\n      path_(name == \"/\" ? name\n                        : group_.group_path_with_trailing_slash() + name) {\n  if (not exists) {\n    ERROR(\"The subfile '\" << name << \"' does not exist\");\n  }\n  if (name != \"/\") {\n    root_group_ = std::move(group_);\n    group_ = detail::OpenGroup(root_group_.id(), name, AccessType::ReadOnly);\n  }\n}\n\ntemplate <typename T>\nT StellarCollapseEos::get_scalar_dataset(\n    const std::string& dataset_name) const {\n  return read_data<0, T>(group_.id(), dataset_name);\n}\n\nstd::vector<double> StellarCollapseEos::get_rank1_dataset(\n    const std::string& dataset_name) const {\n  return read_data<1, std::vector<double>>(group_.id(), dataset_name);\n}\n\nboost::multi_array<double, 3> StellarCollapseEos::get_rank3_dataset(\n    const std::string& dataset_name) const {\n  return read_data<3, boost::multi_array<double, 3>>(group_.id(), dataset_name);\n}\n\ntemplate double StellarCollapseEos::get_scalar_dataset(\n    const std::string& dataset_name) const;\n\ntemplate int StellarCollapseEos::get_scalar_dataset(\n    const std::string& dataset_name) const;\n\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/StellarCollapseEos.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class h5::StellarCollapseEos\n\n#pragma once\n\n#include <hdf5.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Reads in tabulated equation of state file from\n * [stellarcollapse.org](https://stellarcollapse.org)\n *\n * Reads in H5 file containing data for tabulated equation of state.\n * Contains functions to obtain thermodynamic quantities from the file,\n * stored as either rank-1 or rank-3 datasets.\n *\n * It is assumed that the file is in the format of the\n * [SRO (Schneider, Roberts, Ott 2017) Equation of State files](https://\n * stellarcollapse.org/SROEOS)\n *\n * The description of each dataset in the file can be found\n * [here](https://bitbucket.org/andschn/sroeos/src/master/\n * User_Guide/User_Guide.pdf?fileviewer=file-view-default)\n *\n */\nclass StellarCollapseEos : public h5::Object {\n public:\n  /// \\cond\n  // The root-level HDF5 group in the SRO Equation of State files does not\n  // have an extension in its group name\n  static std::string extension() { return \"\"; }\n\n  StellarCollapseEos(bool exists, detail::OpenGroup&& group, hid_t location,\n                     const std::string& name);\n\n  StellarCollapseEos(const StellarCollapseEos& /*rhs*/) = delete;\n  StellarCollapseEos& operator=(const StellarCollapseEos& /*rhs*/) = delete;\n  StellarCollapseEos(StellarCollapseEos&& /*rhs*/) = delete;\n  StellarCollapseEos& operator=(StellarCollapseEos&& /*rhs*/) = delete;\n\n  ~StellarCollapseEos() override = default;\n  /// \\endcond\n\n  /*!\n   * \\ingroup HDF5Group\n   * \\brief reads a rank-0 dataset (contains only one element)\n   */\n  template <typename T>\n  T get_scalar_dataset(const std::string& dataset_name) const;\n\n  /*!\n   * \\ingroup HDF5Group\n   * \\brief reads a dataset with elements along 1 dimension\n   */\n  std::vector<double> get_rank1_dataset(const std::string& dataset_name) const;\n\n  /*!\n   * \\ingroup HDF5Group\n   * \\brief reads a dataset with elements along 3 dimensions\n   */\n  boost::multi_array<double, 3> get_rank3_dataset(\n      const std::string& dataset_name) const;\n\n  const std::string& subfile_path() const override { return path_; }\n\n private:\n  detail::OpenGroup root_group_;\n  detail::OpenGroup group_;\n  std::string path_;\n};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/TensorData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/TensorData.hpp\"\n\n#include <limits>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdHelpers.hpp\"  // std::vector ostream\n\nTensorComponent::TensorComponent(std::string in_name, DataVector in_data)\n    : name(std::move(in_name)), data(std::move(in_data)) {}\n\nTensorComponent::TensorComponent(std::string in_name,\n                                 std::vector<float> in_data)\n    : name(std::move(in_name)), data(std::move(in_data)) {}\n\nvoid TensorComponent::pup(PUP::er& p) {\n  p | name;\n  p | data;\n}\n\nstd::ostream& operator<<(std::ostream& os, const TensorComponent& t) {\n  if (t.data.index() == 0) {\n    return os << \"(\" << t.name << \", \" << std::get<DataVector>(t.data) << \")\";\n  } else if (t.data.index() == 1) {\n    return os << \"(\" << t.name << \", \" << std::get<std::vector<float>>(t.data)\n              << \")\";\n  } else {\n    ERROR(\"Unknown index value (\" << t.data.index()\n                                  << \") in std::variant of tensor component.\");\n  }\n}\n\nbool operator==(const TensorComponent& lhs, const TensorComponent& rhs) {\n  return lhs.name == rhs.name and lhs.data == rhs.data;\n}\n\nbool operator!=(const TensorComponent& lhs, const TensorComponent& rhs) {\n  return not(lhs == rhs);\n}\n\nElementVolumeData::ElementVolumeData(\n    std::string element_name_in, std::vector<TensorComponent> components,\n    std::vector<size_t> extents_in, std::vector<Spectral::Basis> basis_in,\n    std::vector<Spectral::Quadrature> quadrature_in)\n    : element_name(std::move(element_name_in)),\n      tensor_components(std::move(components)),\n      extents(std::move(extents_in)),\n      basis(std::move(basis_in)),\n      quadrature(std::move(quadrature_in)) {}\n\ntemplate <size_t Dim>\nElementVolumeData::ElementVolumeData(const ElementId<Dim>& element_id,\n                                     std::vector<TensorComponent> components,\n                                     const Mesh<Dim>& mesh)\n    : element_name(get_output(element_id)),\n      tensor_components(std::move(components)),\n      extents(mesh.dim),\n      basis(mesh.dim),\n      quadrature(mesh.dim) {\n  for (size_t i = 0; i < mesh.dim; ++i) {\n    extents[i] = mesh.extents(i);\n    basis[i] = mesh.basis(i);\n    quadrature[i] = mesh.quadrature(i);\n  }\n}\n\nvoid ElementVolumeData::pup(PUP::er& p) {\n  p | element_name;\n  p | tensor_components;\n  p | extents;\n  p | quadrature;\n  p | basis;\n}\n\nbool operator==(const ElementVolumeData& lhs, const ElementVolumeData& rhs) {\n  return lhs.element_name == rhs.element_name and\n         lhs.tensor_components == rhs.tensor_components and\n         lhs.extents == rhs.extents and lhs.quadrature == rhs.quadrature and\n         lhs.basis == rhs.basis;\n}\n\nbool operator!=(const ElementVolumeData& lhs, const ElementVolumeData& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                     \\\n  template ElementVolumeData::ElementVolumeData( \\\n      const ElementId<DIM(data)>& element_id,    \\\n      std::vector<TensorComponent> components, const Mesh<DIM(data)>& mesh);\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/IO/H5/TensorData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <string>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\ntemplate <size_t Dim>\nstruct ElementId;\ntemplate <size_t Dim>\nstruct Mesh;\n/// \\endcond\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief An untyped tensor component with a name for observation.\n *\n * The name should be just the name of the tensor component, such as 'Psi_xx'.\n * It must not include any slashes ('/').\n */\nstruct TensorComponent {\n  TensorComponent() = default;\n  TensorComponent(std::string in_name, DataVector in_data);\n  TensorComponent(std::string in_name, std::vector<float> in_data);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n  std::string name{};\n  std::variant<DataVector, std::vector<float>> data{};\n};\n\nstd::ostream& operator<<(std::ostream& os, const TensorComponent& t);\n\nbool operator==(const TensorComponent& lhs, const TensorComponent& rhs);\n\nbool operator!=(const TensorComponent& lhs, const TensorComponent& rhs);\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Holds tensor components on a grid, to be written into an H5 file\n */\nstruct ElementVolumeData {\n  ElementVolumeData() = default;\n  ElementVolumeData(std::string element_name_in,\n                    std::vector<TensorComponent> components,\n                    std::vector<size_t> extents_in,\n                    std::vector<Spectral::Basis> basis_in,\n                    std::vector<Spectral::Quadrature> quadrature_in);\n  template <size_t Dim>\n  ElementVolumeData(const ElementId<Dim>& element_id,\n                    std::vector<TensorComponent> components,\n                    const Mesh<Dim>& mesh);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n  /// Name of the grid (should be human-readable). For standard volume data this\n  /// name must be the string representation of an ElementId, such as\n  /// [B0,(L0I1,L0I0,L3I2)]. Code that reads the volume data may rely on this\n  /// pattern to reconstruct the ElementId.\n  std::string element_name{};\n  /// All tensor components on the grid\n  std::vector<TensorComponent> tensor_components{};\n  /// Number of grid points in every dimension of the grid\n  std::vector<size_t> extents{};\n  /// Spectral::Basis in every dimension of the grid\n  std::vector<Spectral::Basis> basis{};\n  /// Spectral::Quadrature in every dimension of the grid\n  std::vector<Spectral::Quadrature> quadrature{};\n};\n\nbool operator==(const ElementVolumeData& lhs, const ElementVolumeData& rhs);\nbool operator!=(const ElementVolumeData& lhs, const ElementVolumeData& rhs);\n"
  },
  {
    "path": "src/IO/H5/Type.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function for retrieving the HDF5 type for a template parameter\n\n#pragma once\n\n#include <hdf5.h>\n#include <string>\n#include <type_traits>\n\n#include \"IO/H5/CheckH5.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Returns the HDF5 datatype for the corresponding type `T`\n *\n * \\requires `T` is a valid type with a corresponding HDF5 datatype, i.e.\n * a fundamental or a std::string\n * \\returns the HDF5 datatype for the corresponding type `T`\n *\n * \\note For strings, the returned type must be released with H5Tclose\n */\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE hid_t h5_type();\n/// \\cond HIDDEN_SYMBOLS\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<float>() {\n  return H5T_NATIVE_FLOAT;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<double>() {\n  return H5T_NATIVE_DOUBLE;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<short>() {\n  return H5T_NATIVE_SHORT;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<unsigned short>() {\n  return H5T_NATIVE_USHORT;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<int>() {\n  return H5T_NATIVE_INT;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<unsigned int>() {\n  return H5T_NATIVE_UINT;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<long>() {\n  return H5T_NATIVE_LONG;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<unsigned long>() {\n  return H5T_NATIVE_ULONG;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<long long>() {\n  return H5T_NATIVE_LLONG;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<unsigned long long>() {\n  return H5T_NATIVE_ULLONG;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<char>() {\n  // Work around issue https://github.com/sxs-collaboration/spectre/issues/5029\n  // by always writing/reading signed chars. See issue for more suggestions for\n  // more permanent solutions.\n  return H5T_NATIVE_SCHAR;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<bool>() {\n  return H5T_NATIVE_HBOOL;  // LCOV_EXCL_LINE\n}\ntemplate <>\nSPECTRE_ALWAYS_INLINE hid_t h5_type<std::string>() {\n  hid_t datatype = H5Tcopy(H5T_C_S1);\n  CHECK_H5(datatype, \"Failed to allocate string.\");\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  CHECK_H5(H5Tset_size(datatype, H5T_VARIABLE),\n           \"Failed to set size of string.\");\n  CHECK_H5(H5Tset_cset(datatype, H5T_CSET_ASCII),\n           \"Failed to set string to ASCII encoding.\");\n  CHECK_H5(H5Tset_strpad(datatype, H5T_STR_NULLTERM),\n           \"Failed to set string to null terminate\");\n#pragma GCC diagnostic pop\n  return datatype;\n}\n/// \\endcond\n\n/*!\n * \\ingroup HDF5Group\n * \\brief Create an H5 FORTRAN string.\n */\nSPECTRE_ALWAYS_INLINE hid_t fortran_string() {\n  hid_t datatype = H5Tcopy(H5T_FORTRAN_S1);\n  CHECK_H5(datatype, \"Failed to allocate string.\");\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  CHECK_H5(H5Tset_size(datatype, H5T_VARIABLE),\n           \"Failed to set size of string.\");\n#pragma GCC diagnostic pop\n  return datatype;\n}\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Version.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/Version.hpp\"\n\n#include <algorithm>\n\n#include \"Helpers.hpp\"\n\nnamespace h5 {\nVersion::Version(bool exists, detail::OpenGroup&& group, hid_t location,\n                 std::string name, const uint32_t version)\n    : path_(group.group_path_with_trailing_slash() + name),\n      version_([&exists, &location, &version, &name]() {\n        if (exists) {\n          return read_value_attribute<uint32_t>(location, name + extension());\n        }\n        write_to_attribute(location, name + extension(), version);\n        return version;\n      }()),\n      group_(std::move(group)) {}\n\nVersion::Version(bool exists, detail::OpenGroup&& group, hid_t location,\n                 std::string name, const std::string version)\n    : path_(group.group_path_with_trailing_slash() + name),\n      version_([&exists, &location, &version, &name]() {\n        if (exists) {\n          return read_value_attribute<uint32_t>(location, name + extension());\n        }\n        write_to_attribute(location, name + extension(),\n                           std::vector<std::string>{version});\n        return static_cast<uint32_t>(0);\n      }()),\n      group_(std::move(group)) {}\n\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Version.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class h5::Version for storing version history of files\n\n#pragma once\n\n#include <cstdint>\n#include <hdf5.h>\n#include <string>\n\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief Used to store the version of the file\n *\n * A Version object should be stored inside each H5 object that is to represent\n * a file, e.g. Dat, or Text.\n *\n * \\example\n * To write the version use:\n * \\snippet Test_Version.cpp h5file_write_version\n * To read the version use:\n * \\snippet Test_Version.cpp h5file_read_version\n *\n */\nclass Version : public h5::Object {\n public:\n  /// \\cond HIDDEN_SYMOLS\n  static std::string extension() { return \".ver\"; }\n\n  Version(bool exists, detail::OpenGroup&& group, hid_t location,\n          std::string name, uint32_t version = 1);\n\n  // Write a single string as the version.\n  //\n  // This is used for fixing a bug in CCE data written by SpEC and\n  // will be removed once we no longer need to match that formatting. Do not\n  // use for SpECTRE versioning!\n  Version(bool exists, detail::OpenGroup&& group, hid_t location,\n          std::string name, std::string version);\n\n  Version(const Version& /*rhs*/) = delete;\n  Version& operator=(const Version& /*rhs*/) = delete;\n\n  Version(Version&& /*rhs*/) = delete;             // NOLINT\n  Version& operator=(Version&& /*rhs*/) = delete;  // NOLINT\n  ~Version() override = default;\n  /// \\endcond\n\n  uint32_t get_version() const { return version_; }\n\n  const std::string& subfile_path() const override { return path_; }\n\n private:\n  /// \\cond HIDDEN_SYMBOLS\n  std::string path_;\n  uint32_t version_;\n  // group_ is necessary since the when the h5::Object is destroyed it closes\n  // all groups that were opened to get to it.\n  detail::OpenGroup group_;\n  /// \\endcond\n};\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/VolumeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/H5/VolumeData.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <boost/algorithm/string.hpp>\n#include <boost/iterator/transform_iterator.hpp>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <hdf5.h>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <string_view>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Connectivity.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/ExtendConnectivityHelpers.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/SpectralIo.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/Type.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/ExpectsAndEnsures.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace h5 {\nnamespace {\n// Append the element extents and connectivity to the total extents and\n// connectivity\nconstexpr const char* global_functions_of_time_observation_value_attr =\n    \"global_functions_of_time_observation_value\";\n\nsize_t append_element_extents_and_connectivity(\n    const gsl::not_null<std::vector<size_t>*> total_extents,\n    const gsl::not_null<std::vector<int>*> total_connectivity,\n    const gsl::not_null<int*> total_points_so_far, const size_t dim,\n    const ElementVolumeData& element) {\n  size_t cell_count = 0;\n  // Process the element extents\n  // `dim` is the dimension of the computation except when we are doing Cartoon\n  // method with a 2D computational domain. In such a case, the true dimension\n  // is 3 but we are writing as 2D for ParaView visualization\n  const auto& extents = [&element, &dim]() {\n    if (dim == 2 and element.extents.size() == 3) {\n      ASSERT(gsl::at(element.basis, 2) == Spectral::Basis::Cartoon and\n                 gsl::at(element.basis, 1) != Spectral::Basis::Cartoon,\n             \"Trying to write data with mismatched dimensions (dim = 2, \"\n                 << \"extents = [\" << element.extents[0] << \", \"\n                 << element.extents[1] << \", \" << element.extents[2]\n                 << \"]) without a valid Cartoon basis (the computational \"\n                    \"dimension must be 2).\");\n      return std::vector<size_t>(element.extents.begin(),\n                                 element.extents.end() - 1);\n    } else {\n      return element.extents;\n    }\n  }();\n#ifdef SPECTRE_DEBUG\n  for (size_t i = 0; i < extents.size(); ++i) {\n    // The Cartoon check is for the 1D Cartoon case\n    ASSERT(extents[i] != 1 or element.basis[i] == Spectral::Basis::Cartoon,\n           \"Cannot generate connectivity for any single grid point elements \"\n           \"that don't use a Cartoon basis.\");\n  }\n#endif  // SPECTRE_DEBUG\n  if (extents.size() != dim) {\n    ERROR(\"Trying to write data of dimensionality \"\n          << extents.size() << \" but the VolumeData file has dimensionality \"\n          << dim << \".\");\n  }\n  total_extents->insert(total_extents->end(), extents.begin(), extents.end());\n  // Find the number of points in the local connectivity\n  const int element_num_points =\n      alg::accumulate(extents, 1, std::multiplies<>{});\n  // Generate the connectivity data for the element\n  // Possible optimization: local_connectivity.reserve(BLAH) if we can figure\n  // out size without computing all the connectivities.\n  const std::vector<int> connectivity = [&extents, &total_points_so_far,\n                                         &cell_count]() {\n    std::vector<int> local_connectivity;\n    for (const auto& cell : vis::detail::compute_cells(extents)) {\n      local_connectivity.emplace_back(\n          vis::detail::xdmf_topology_type(cell.topology));\n      ++cell_count;\n      for (const auto& bounding_indices : cell.bounding_indices) {\n        local_connectivity.emplace_back(*total_points_so_far +\n                                        static_cast<int>(bounding_indices));\n      }\n    }\n    return local_connectivity;\n  }();\n  // Capture the point offset for this element before incrementing. All\n  // extra connectivity added below (for special element types) must add\n  // this offset to convert local point indices to global ones.\n  const int element_start = *total_points_so_far;\n  *total_points_so_far += element_num_points;\n  total_connectivity->insert(total_connectivity->end(), connectivity.begin(),\n                             connectivity.end());\n\n  // 2D elements may require extra connections to close periodic/angular\n  // boundaries and fill any degenerate central region based on basis;\n  // generically do nothing\n  if (dim == 2) {\n    if (element.basis[0] == Spectral::Basis::SphericalHarmonic and\n        element.basis[1] == Spectral::Basis::SphericalHarmonic) {\n      // Extents are (l+1, 2l+1)\n      const int l = static_cast<int>(element.extents[0] - 1);\n\n      // Connect max(phi) and min(phi) by adding more quads\n      // to total_connectivity\n      for (int j = 0; j < l; ++j) {\n        total_connectivity->push_back(\n            vis::detail::xdmf_topology_type(vis::detail::Topology::Quad));\n        ++cell_count;\n        total_connectivity->push_back(element_start + j);\n        total_connectivity->push_back(element_start + j + 1);\n        total_connectivity->push_back(element_start + 2 * l * (l + 1) + j + 1);\n        total_connectivity->push_back(element_start + (2 * l) * (l + 1) + j);\n      }\n\n      // Add a new connectivity output for filling the poles\n      // First, get the points at min(theta), which define the\n      // boundary of the top pole to fill, and the points at\n      // max(theta), which define the boundary of the bottom\n      // pole to fill. Note: points are stored with theta\n      // varying faster than phi.\n      std::vector<int> top_pole_points{};\n      std::vector<int> bottom_pole_points{};\n      for (int k = 0; k < (2 * l + 1); ++k) {\n        top_pole_points.push_back(element_start + k * (l + 1));\n        bottom_pole_points.push_back(element_start + k * (l + 1) + l);\n      }\n\n      const size_t number_of_pole_points = top_pole_points.size();\n      if (number_of_pole_points < 3) {\n        ERROR_NO_TRACE(\n            \"Cannot write a 2D surface to file with l=0. Must have at least \"\n            \"l=1.\");\n      }\n\n      // Fill poles with triangles in a fan pattern. Choose the root point that\n      // is common with all triangles to be the first point for each pole\n      const int top_root_point = top_pole_points[0];\n      const int bottom_root_point = bottom_pole_points[0];\n\n      // We end such that the last triangle we make has indices (0, N-2, N-1)\n      // given number_of_pole_points = N\n      for (size_t i = 1; i <= number_of_pole_points - 2; i++) {\n        const int top_second_point = gsl::at(top_pole_points, i);\n        const int top_third_point = gsl::at(top_pole_points, i + 1);\n        const int bottom_second_point = gsl::at(bottom_pole_points, i);\n        const int bottom_third_point = gsl::at(bottom_pole_points, i + 1);\n\n        total_connectivity->push_back(\n            vis::detail::xdmf_topology_type(vis::detail::Topology::Triangle));\n        ++cell_count;\n        total_connectivity->push_back(top_root_point);\n        total_connectivity->push_back(top_second_point);\n        total_connectivity->push_back(top_third_point);\n        total_connectivity->push_back(\n            vis::detail::xdmf_topology_type(vis::detail::Topology::Triangle));\n        ++cell_count;\n        total_connectivity->push_back(bottom_root_point);\n        total_connectivity->push_back(bottom_second_point);\n        total_connectivity->push_back(bottom_third_point);\n      }\n    } else if ((element.basis[0] == Spectral::Basis::ZernikeB2 and\n                element.basis[1] == Spectral::Basis::ZernikeB2) or\n               element.basis[1] == Spectral::Basis::Fourier) {\n      ASSERT(element.basis[0] == Spectral::Basis::ZernikeB2 or\n                 (element.basis[0] == Spectral::Basis::Legendre or\n                  element.basis[0] == Spectral::Basis::Chebyshev),\n             \"Adding connectivity for Fourier in the second dimension requires \"\n             \"the first dimension to be Legendre or Chebychev, got \"\n                 << element.basis[0]);\n      const auto n_r = static_cast<int>(extents[0]);\n      const auto n_phi = static_cast<int>(extents[1]);\n\n      // Connect max(phi) and min(phi) by adding more quads\n      // to total_connectivity\n      for (int j = 0; j < n_r - 1; ++j) {\n        total_connectivity->push_back(\n            vis::detail::xdmf_topology_type(vis::detail::Topology::Quad));\n        ++cell_count;\n        total_connectivity->push_back(element_start + j);\n        total_connectivity->push_back(element_start + j + 1);\n        total_connectivity->push_back(element_start + (n_phi - 1) * n_r + j +\n                                      1);\n        total_connectivity->push_back(element_start + (n_phi - 1) * n_r + j);\n      }\n\n      // For a filled disk (ZernikeB2), also fill the central hole with\n      // triangles using a recursive fan pattern over the minimum r ring points.\n      if (element.basis[0] == Spectral::Basis::ZernikeB2) {\n        std::vector<int> inner_ring_points{};\n        inner_ring_points.reserve(static_cast<size_t>(n_phi));\n        for (int k = 0; k < n_phi; ++k) {\n          // minimum r for each phi slice\n          inner_ring_points.push_back(element_start + k * n_r);\n        }\n\n        std::vector<int> new_points;\n        while (inner_ring_points.size() >= 3) {\n          new_points.clear();\n          new_points.push_back(inner_ring_points[0]);\n          for (size_t i = 0; i < inner_ring_points.size() - 2; i += 2) {\n            total_connectivity->push_back(vis::detail::xdmf_topology_type(\n                vis::detail::Topology::Triangle));\n            ++cell_count;\n            total_connectivity->push_back(inner_ring_points[i]);\n            total_connectivity->push_back(inner_ring_points[i + 1]);\n            total_connectivity->push_back(inner_ring_points[i + 2]);\n            new_points.push_back(inner_ring_points[i + 2]);\n          }\n          if (inner_ring_points.size() % 2 == 0) {\n            // Add triangle closing the ring: connects last two points back to\n            // first\n            total_connectivity->push_back(vis::detail::xdmf_topology_type(\n                vis::detail::Topology::Triangle));\n            ++cell_count;\n            total_connectivity->push_back(\n                inner_ring_points[inner_ring_points.size() - 2]);\n            total_connectivity->push_back(\n                inner_ring_points[inner_ring_points.size() - 1]);\n            total_connectivity->push_back(inner_ring_points[0]);\n          }\n          inner_ring_points = std::move(new_points);\n        }\n      }\n    }\n    // generically do nothing if not a sphere, disk, or annulus\n  } else if (dim == 3) {\n    if ((element.basis[0] == Spectral::Basis::ZernikeB2 and\n         element.basis[1] == Spectral::Basis::ZernikeB2) or\n        element.basis[1] == Spectral::Basis::Fourier) {\n      ASSERT(element.basis[0] == Spectral::Basis::ZernikeB2 or\n                 ((element.basis[0] == Spectral::Basis::Legendre or\n                   element.basis[0] == Spectral::Basis::Chebyshev) and\n                  (element.basis[2] == Spectral::Basis::Legendre or\n                   element.basis[2] == Spectral::Basis::Chebyshev)),\n             \"Adding connectivity for Fourier in the second dimension requires \"\n             \"the first and third dimensions to be Legendre or Chebyshev, got \"\n                 << element.basis[0] << \", \" << element.basis[2]);\n      const auto n_r = static_cast<int>(extents[0]);\n      const auto n_ph = static_cast<int>(extents[1]);\n      const auto n_z = static_cast<int>(extents[2]);\n\n      // Helper: global point index for (index_r, index_phi, index_z)\n      const auto global_index = [&](const int index_radius, const int index_phi,\n                                    const int index_z) -> int {\n        return element_start + index_radius + n_r * index_phi +\n               n_r * n_ph * index_z;\n      };\n\n      // Close the phi seam: connect last phi strip (index_ph = n_ph-1) back to\n      // first (index_ph = 0) with hexahedra.\n      for (int j_r = 0; j_r < n_r - 1; ++j_r) {\n        for (int j_z = 0; j_z < n_z - 1; ++j_z) {\n          total_connectivity->push_back(vis::detail::xdmf_topology_type(\n              vis::detail::Topology::Hexahedron));\n          ++cell_count;\n          total_connectivity->push_back(global_index(j_r, n_ph - 1, j_z));\n          total_connectivity->push_back(global_index(j_r + 1, n_ph - 1, j_z));\n          total_connectivity->push_back(global_index(j_r + 1, 0, j_z));\n          total_connectivity->push_back(global_index(j_r, 0, j_z));\n          total_connectivity->push_back(global_index(j_r, n_ph - 1, j_z + 1));\n          total_connectivity->push_back(\n              global_index(j_r + 1, n_ph - 1, j_z + 1));\n          total_connectivity->push_back(global_index(j_r + 1, 0, j_z + 1));\n          total_connectivity->push_back(global_index(j_r, 0, j_z + 1));\n        }\n      }\n\n      // For a filled cylinder (ZernikeB2), fill the central hole with\n      // wedges between consecutive z layers.\n      if (element.basis[0] == Spectral::Basis::ZernikeB2) {\n        std::vector<int> ring_lo{};\n        std::vector<int> ring_hi{};\n        ring_lo.reserve(static_cast<size_t>(n_ph));\n        ring_hi.reserve(static_cast<size_t>(n_ph));\n        std::vector<int> new_lo{};\n        std::vector<int> new_hi{};\n        for (int j_z = 0; j_z < n_z - 1; ++j_z) {\n          ring_lo.clear();\n          ring_hi.clear();\n          // Collect the minimum r ring points in this z layer and the next\n          for (int k = 0; k < n_ph; ++k) {\n            ring_lo.push_back(global_index(0, k, j_z));\n            ring_hi.push_back(global_index(0, k, j_z + 1));\n          }\n\n          // Build wedge prisms using the same recursive fan as the 2D case,\n          // extruded between ring_lo and ring_hi.\n          while (ring_lo.size() >= 3) {\n            new_lo.clear();\n            new_hi.clear();\n            new_lo.push_back(ring_lo[0]);\n            new_hi.push_back(ring_hi[0]);\n            for (size_t i = 0; i < ring_lo.size() - 2; i += 2) {\n              // Wedge (triangular prism): bottom triangle (ring_lo) + top\n              // triangle (ring_hi), each 3 vertices = 6 total.\n              total_connectivity->push_back(vis::detail::xdmf_topology_type(\n                  vis::detail::Topology::Wedge));\n              ++cell_count;\n              total_connectivity->push_back(ring_lo[i]);\n              total_connectivity->push_back(ring_lo[i + 1]);\n              total_connectivity->push_back(ring_lo[i + 2]);\n              total_connectivity->push_back(ring_hi[i]);\n              total_connectivity->push_back(ring_hi[i + 1]);\n              total_connectivity->push_back(ring_hi[i + 2]);\n              new_lo.push_back(ring_lo[i + 2]);\n              new_hi.push_back(ring_hi[i + 2]);\n            }\n            if (ring_lo.size() % 2 == 0) {\n              // Closing wedge connecting last two points back to first\n              const size_t size = ring_lo.size();\n              total_connectivity->push_back(vis::detail::xdmf_topology_type(\n                  vis::detail::Topology::Wedge));\n              ++cell_count;\n              total_connectivity->push_back(ring_lo[size - 2]);\n              total_connectivity->push_back(ring_lo[size - 1]);\n              total_connectivity->push_back(ring_lo[0]);\n              total_connectivity->push_back(ring_hi[size - 2]);\n              total_connectivity->push_back(ring_hi[size - 1]);\n              total_connectivity->push_back(ring_hi[0]);\n            }\n            ring_lo = std::move(new_lo);\n            ring_hi = std::move(new_hi);\n          }\n        }\n      }\n    }\n    // If element is a 3D spherical shell (SphericalHarmonic in theta and phi\n    // directions), add phi-wrapping hexahedra and pole-cap wedges.\n    if (element.basis[1] == Spectral::Basis::SphericalHarmonic and\n        element.basis[2] == Spectral::Basis::SphericalHarmonic) {\n      const auto n_r = static_cast<int>(element.extents[0]);\n      const auto n_theta = static_cast<int>(element.extents[1]);\n      const auto n_phi = static_cast<int>(element.extents[2]);\n      // Global point index: local index (r fastest, then theta, then phi)\n      // plus the offset for this element's first point.\n      const auto idx = [&n_r, &n_theta, &element_start](\n                           const int ir, const int it, const int ip) -> int {\n        return element_start + ir + n_r * it + n_r * n_theta * ip;\n      };\n\n      // Step 1: Add phi-wrapping hexahedra to close the phi=2*pi boundary\n      for (int it = 0; it < n_theta - 1; ++it) {\n        for (int ir = 0; ir < n_r - 1; ++ir) {\n          total_connectivity->push_back(vis::detail::xdmf_topology_type(\n              vis::detail::Topology::Hexahedron));\n          ++cell_count;\n          total_connectivity->push_back(idx(ir, it, n_phi - 1));\n          total_connectivity->push_back(idx(ir + 1, it, n_phi - 1));\n          total_connectivity->push_back(idx(ir + 1, it + 1, n_phi - 1));\n          total_connectivity->push_back(idx(ir, it + 1, n_phi - 1));\n          total_connectivity->push_back(idx(ir, it, 0));\n          total_connectivity->push_back(idx(ir + 1, it, 0));\n          total_connectivity->push_back(idx(ir + 1, it + 1, 0));\n          total_connectivity->push_back(idx(ir, it + 1, 0));\n        }\n      }\n\n      // Step 2: Add pole-cap wedges using recursive halving of the pole ring.\n      // For each pole (theta=0 and theta=n_theta-1) and each radial layer, the\n      // ring of n_phi points at fixed (ir, it_pole) is recursively halved to\n      // produce Wedge cells (bottom tri at ir, top tri at ir+1).\n      //\n      // Winding order: phi increases CCW when viewed from the north (+z). For\n      // the top pole (it_pole=0) the forward ring order gives an\n      // outward-pointing bottom-triangle normal (positive signed volume). For\n      // the bottom pole (it_pole=n_theta-1) the same phi order produces an\n      // inward-pointing normal, so we reverse the ring to keep consistent\n      // winding and positive signed volume for all wedges.\n      for (const int it_pole : {0, n_theta - 1}) {\n        const bool reverse_ring = (it_pole == n_theta - 1);\n        for (int ir = 0; ir < n_r - 1; ++ir) {\n          std::vector<int> bottom_ring;\n          std::vector<int> top_ring;\n          bottom_ring.reserve(static_cast<size_t>(n_phi));\n          top_ring.reserve(static_cast<size_t>(n_phi));\n          for (int ip = 0; ip < n_phi; ++ip) {\n            bottom_ring.push_back(idx(ir, it_pole, ip));\n            top_ring.push_back(idx(ir + 1, it_pole, ip));\n          }\n          if (reverse_ring) {\n            std::reverse(bottom_ring.begin(), bottom_ring.end());\n            std::reverse(top_ring.begin(), top_ring.end());\n          }\n          std::vector<int> new_bottom;\n          std::vector<int> new_top;\n          while (bottom_ring.size() >= 3) {\n            new_bottom.clear();\n            new_top.clear();\n            new_bottom.push_back(bottom_ring[0]);\n            new_top.push_back(top_ring[0]);\n            for (size_t i = 0; i < bottom_ring.size() - 2; i += 2) {\n              total_connectivity->push_back(vis::detail::xdmf_topology_type(\n                  vis::detail::Topology::Wedge));\n              ++cell_count;\n              total_connectivity->push_back(bottom_ring[i]);\n              total_connectivity->push_back(bottom_ring[i + 1]);\n              total_connectivity->push_back(bottom_ring[i + 2]);\n              total_connectivity->push_back(top_ring[i]);\n              total_connectivity->push_back(top_ring[i + 1]);\n              total_connectivity->push_back(top_ring[i + 2]);\n              new_bottom.push_back(bottom_ring[i + 2]);\n              new_top.push_back(top_ring[i + 2]);\n            }\n            if (bottom_ring.size() % 2 == 0) {\n              const size_t sz = bottom_ring.size();\n              total_connectivity->push_back(vis::detail::xdmf_topology_type(\n                  vis::detail::Topology::Wedge));\n              ++cell_count;\n              total_connectivity->push_back(bottom_ring[sz - 2]);\n              total_connectivity->push_back(bottom_ring[sz - 1]);\n              total_connectivity->push_back(bottom_ring[0]);\n              total_connectivity->push_back(top_ring[sz - 2]);\n              total_connectivity->push_back(top_ring[sz - 1]);\n              total_connectivity->push_back(top_ring[0]);\n            }\n            bottom_ring = std::move(new_bottom);\n            top_ring = std::move(new_top);\n          }\n        }\n      }\n    }\n    // generically do nothing if not a cylinder or spherical shell\n  }\n  return cell_count;\n}\n}  // namespace\n\nVolumeData::VolumeData(const bool subfile_exists, detail::OpenGroup&& group,\n                       const hid_t /*location*/, const std::string& name,\n                       const uint32_t version)\n    : group_(std::move(group)),\n      name_(name.size() > extension().size()\n                ? (extension() == name.substr(name.size() - extension().size())\n                       ? name\n                       : name + extension())\n                : name + extension()),\n      path_(group_.group_path_with_trailing_slash() + name),\n      version_(version),\n      volume_data_group_(group_.id(), name_, h5::AccessType::ReadWrite) {\n  if (subfile_exists) {\n    // We treat this as an internal version for now. We'll need to deal with\n    // proper versioning later.\n    const Version open_version(true, detail::OpenGroup{},\n                               volume_data_group_.id(), \"version\");\n    version_ = open_version.get_version();\n    const Header header(true, detail::OpenGroup{}, volume_data_group_.id(),\n                        \"header\");\n    header_ = header.get_header();\n  } else {  // file does not exist\n    // Subfiles are closed as they go out of scope, so we have the extra\n    // braces here to add the necessary scope\n    {\n      Version open_version(false, detail::OpenGroup{}, volume_data_group_.id(),\n                           \"version\", version_);\n    }\n    {\n      Header header(false, detail::OpenGroup{}, volume_data_group_.id(),\n                    \"header\");\n      header_ = header.get_header();\n    }\n  }\n}\n\n// Write Volume Data stored in a vector of `ElementVolumeData` to\n// an `observation_group` in a `VolumeData` file.\nvoid VolumeData::write_volume_data(\n    const size_t observation_id, const double observation_value,\n    const std::vector<ElementVolumeData>& elements,\n    const std::optional<std::vector<char>>& serialized_domain,\n    const std::optional<std::vector<char>>&\n        serialized_observation_functions_of_time,\n    const std::optional<std::vector<char>>&\n        serialized_global_functions_of_time) {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadWrite);\n  if (contains_attribute(observation_group.id(), \"\", \"observation_value\")) {\n    ERROR_NO_TRACE(\"Trying to write ObservationId \"\n                   << std::to_string(observation_id)\n                   << \" with observation_value \" << observation_group.id()\n                   << \" which already exists in file at \" << path\n                   << \". Did you forget to clean up after an earlier run?\");\n  }\n  h5::write_to_attribute(observation_group.id(), \"observation_value\",\n                         observation_value);\n  // Get first element to extract the component names and dimension\n  const auto get_component_name = [](const auto& component) {\n    ASSERT(component.name.find_last_of('/') == std::string::npos,\n           \"The expected format of the tensor component names is \"\n           \"'COMPONENT_NAME' but found a '/' in '\"\n               << component.name << \"'.\");\n    return component.name;\n  };\n  const std::vector<std::string> component_names(\n      boost::make_transform_iterator(elements.front().tensor_components.begin(),\n                                     get_component_name),\n      boost::make_transform_iterator(elements.front().tensor_components.end(),\n                                     get_component_name));\n  // The dimension of the grid is the number of extents per element. I.e., if\n  // the extents are [8,5,7] for any element, the dimension of the grid is 3.\n  // Only written once per VolumeData file (All volume data in a single file\n  // should have the same dimensionality)\n  if (not contains_attribute(volume_data_group_.id(), \"\", \"dimension\")) {\n    h5::write_to_attribute(\n        volume_data_group_.id(), \"dimension\",\n        // Need to manually reduce dimensions with a Cartoon evolution\n        // using a 2d computational domain\n        [&elements]() -> size_t {\n          const auto& basis = elements.front().basis;\n          if (basis.size() == 3 and\n              (gsl::at(basis, 2) == Spectral::Basis::Cartoon and\n               gsl::at(basis, 1) != Spectral::Basis::Cartoon)) {\n            return 2;\n          }\n          return elements.front().extents.size();\n        }());\n  }\n  const auto dim =\n      h5::read_value_attribute<size_t>(volume_data_group_.id(), \"dimension\");\n  // Extract Tensor Data one component at a time\n  std::vector<size_t> total_extents;\n  std::string grid_names;\n  std::vector<int> total_connectivity;\n  std::vector<int> quadratures;\n  std::vector<int> bases;\n  std::vector<uint64_t> element_ids;\n  std::vector<uint64_t> block_ids;\n  // Keep a running count of the number of points so far to use as a global\n  // index for the connectivity\n  int total_points_so_far = 0;\n  // Loop over tensor components\n  for (size_t i = 0; i < component_names.size(); i++) {\n    std::string component_name = component_names[i];\n    // Write the data for the tensor component\n    if (h5::contains_dataset_or_group(observation_group.id(), \"\",\n                                      component_name)) {\n      ERROR(\"Trying to write tensor component '\"\n            << component_name\n            << \"' which already exists in HDF5 file in group '\" << name_ << '/'\n            << \"ObservationId\" << std::to_string(observation_id) << \"'\");\n    }\n\n    const auto fill_and_write_contiguous_tensor_data =\n        [&bases, &block_ids, &component_name, &dim, &element_ids, &elements,\n         &grid_names, i, &observation_group, &quadratures, &total_connectivity,\n         &total_extents,\n         &total_points_so_far](const auto contiguous_tensor_data_ptr) {\n          for (const auto& element : elements) {\n            if (UNLIKELY(i == 0)) {\n              // True if first tensor component being accessed\n              grid_names += element.element_name + h5::VolumeData::separator();\n              // append element basis\n              alg::transform(\n                  // Need to ensure size == dim (Cartoon method with 2d\n                  // computational domain needs to drop last dimension)\n                  std::vector<Spectral::Basis>(\n                      element.basis.begin(),\n                      element.basis.end() -\n                          static_cast<int>(element.basis.size() - dim)),\n                  std::back_inserter(bases), [](const Spectral::Basis t) {\n                    // Shift the basis to keep compatibility with old file\n                    // formats.\n                    return static_cast<int>(static_cast<uint8_t>(t) >>\n                                            Spectral::basis_shift);\n                  });\n              // append element quadrature\n              alg::transform(\n                  // Need to ensure size == dim (Cartoon method with 2d\n                  // computational domain needs to drop last dimension)\n                  std::vector<Spectral::Quadrature>(\n                      element.quadrature.begin(),\n                      element.quadrature.end() -\n                          static_cast<int>(element.basis.size() - dim)),\n                  std::back_inserter(quadratures),\n                  [](const Spectral::Quadrature t) {\n                    return static_cast<int>(t);\n                  });\n\n              const size_t number_of_cells =\n                  append_element_extents_and_connectivity(\n                      &total_extents, &total_connectivity,\n                      &total_points_so_far, dim, element);\n\n              // Element ID: hash of the element name string\n              if (element.element_name.size() >= 3 and\n                  element.element_name[0] == '[' and\n                  element.element_name[1] == 'B') {\n                // numerical dimension != computational dimension when using\n                // cartoon bases\n                const size_t num_dim = elements.front().basis.size();\n                if (num_dim == 1) {\n                  const ElementId<1> element_id{element.element_name};\n                  element_ids.insert(element_ids.end(), number_of_cells,\n                                     element_id.to_short_id());\n                  block_ids.insert(block_ids.end(), number_of_cells,\n                                   element_id.block_id());\n                } else if (num_dim == 2) {\n                  const ElementId<2> element_id{element.element_name};\n                  element_ids.insert(element_ids.end(), number_of_cells,\n                                     element_id.to_short_id());\n                  block_ids.insert(block_ids.end(), number_of_cells,\n                                   element_id.block_id());\n                } else if (num_dim == 3) {\n                  const ElementId<3> element_id{element.element_name};\n                  element_ids.insert(element_ids.end(), number_of_cells,\n                                     element_id.to_short_id());\n                  block_ids.insert(block_ids.end(), number_of_cells,\n                                   element_id.block_id());\n                } else {\n                  ERROR(\n                      \"Can only encode ElementID when simulation dim is 1, 2, \"\n                      \"or 3, got \"\n                      << dim);\n                }\n              } else {\n                element_ids.insert(\n                    element_ids.end(), number_of_cells,\n                    std::hash<std::string>{}(element.element_name));\n                block_ids.insert(block_ids.end(), number_of_cells, 0);\n              }\n            }\n            using type_from_variant = tmpl::conditional_t<\n                std::is_same_v<\n                    std::decay_t<decltype(*contiguous_tensor_data_ptr)>,\n                    std::vector<double>>,\n                DataVector, std::vector<float>>;\n            const auto& tensor_component = element.tensor_components[i];\n            ASSERT(tensor_component.name == component_name,\n                   \"Tensor components must be in the same order for all \"\n                   \"elements. Expected '\"\n                       << component_name << \"' but found '\"\n                       << tensor_component.name << \"' at index \" << i << \".\");\n            contiguous_tensor_data_ptr->insert(\n                contiguous_tensor_data_ptr->end(),\n                std::get<type_from_variant>(tensor_component.data).begin(),\n                std::get<type_from_variant>(tensor_component.data).end());\n          }  // for each element\n          h5::write_data(observation_group.id(), *contiguous_tensor_data_ptr,\n                         {contiguous_tensor_data_ptr->size()}, component_name);\n        };\n\n    if (elements[0].tensor_components[i].data.index() == 0) {\n      std::vector<double> contiguous_tensor_data{};\n      fill_and_write_contiguous_tensor_data(\n          make_not_null(&contiguous_tensor_data));\n    } else if (elements[0].tensor_components[i].data.index() == 1) {\n      std::vector<float> contiguous_tensor_data{};\n      fill_and_write_contiguous_tensor_data(\n          make_not_null(&contiguous_tensor_data));\n    } else {\n      ERROR(\"Unknown index value (\"\n            << elements[0].tensor_components[i].data.index()\n            << \") in std::variant of tensor component.\");\n    }\n  }  // for each component\n  grid_names.pop_back();\n\n  // Write the grid extents contiguously, the first `dim` belong to the\n  // First grid, the second `dim` belong to the second grid, and so on,\n  // Ordering is `x, y, z, ... `\n  h5::write_data(observation_group.id(), total_extents, {total_extents.size()},\n                 \"total_extents\");\n  // Write the names of the grids as vector of chars with individual names\n  // separated by `separator()`\n  std::vector<char> grid_names_as_chars(grid_names.begin(), grid_names.end());\n  h5::write_data(observation_group.id(), grid_names_as_chars,\n                 {grid_names_as_chars.size()}, \"grid_names\");\n  // Write the coded quadrature, along with the dictionary\n  const auto io_quadratures = Spectral::all_quadratures();\n  std::vector<std::string> quadrature_dict(io_quadratures.size());\n  alg::transform(io_quadratures, quadrature_dict.begin(),\n                 get_output<Spectral::Quadrature>);\n  h5_detail::write_dictionary(\"Quadrature dictionary\", quadrature_dict,\n                              observation_group);\n  h5::write_data(observation_group.id(), quadratures, {quadratures.size()},\n                 \"quadratures\");\n  // Write the coded basis, along with the dictionary\n  const auto io_bases = Spectral::all_bases();\n  std::vector<std::string> basis_dict(io_bases.size());\n  alg::transform(io_bases, basis_dict.begin(), get_output<Spectral::Basis>);\n  h5_detail::write_dictionary(\"Basis dictionary\", basis_dict,\n                              observation_group);\n  h5::write_data(observation_group.id(), bases, {bases.size()}, \"bases\");\n  // Write the Connectivity, which will only be empty when a CartoonSphere\n  // domain is used, which has 1 computational dimension and should not be\n  // visualized with ParaView\n  if (not total_connectivity.empty()) {\n    h5::write_data(observation_group.id(), total_connectivity,\n                   {total_connectivity.size()}, \"connectivity\");\n  }\n  // Write cell-centered element_id and block_id datasets for the mixed\n  // topology format. element_id is the hash of the element name; block_id is\n  // parsed from the \"[B<N>,...\" element name pattern.\n  if (not element_ids.empty()) {\n    h5::write_data(observation_group.id(), element_ids, {element_ids.size()},\n                   \"ElementId\");\n  }\n  if (not block_ids.empty()) {\n    h5::write_data(observation_group.id(), block_ids, {block_ids.size()},\n                   \"BlockId\");\n  }\n  // Store the serialized domain and functions of time at the subfile level\n  if (serialized_domain.has_value() and\n      not contains_dataset_or_group(volume_data_group_.id(), \"\", \"domain\")) {\n    h5::write_data(volume_data_group_.id(), *serialized_domain,\n                   {serialized_domain->size()}, \"domain\");\n  }\n  if (serialized_observation_functions_of_time.has_value()) {\n    h5::write_data(observation_group.id(),\n                   *serialized_observation_functions_of_time,\n                   {serialized_observation_functions_of_time->size()},\n                   \"functions_of_time\");\n  }\n  if (serialized_global_functions_of_time.has_value()) {\n    bool should_write_global_functions_of_time = true;\n    if (h5::contains_attribute(\n            volume_data_group_.id(), \"\",\n            global_functions_of_time_observation_value_attr)) {\n      const auto stored_observation_value = h5::read_value_attribute<double>(\n          volume_data_group_.id(),\n          global_functions_of_time_observation_value_attr);\n      should_write_global_functions_of_time =\n          observation_value > stored_observation_value;\n    }\n    if (should_write_global_functions_of_time) {\n      h5::write_data(volume_data_group_.id(),\n                     *serialized_global_functions_of_time,\n                     {serialized_global_functions_of_time->size()},\n                     \"global_functions_of_time\", true);\n      if (h5::contains_attribute(\n              volume_data_group_.id(), \"\",\n              global_functions_of_time_observation_value_attr)) {\n        CHECK_H5(H5Adelete(volume_data_group_.id(),\n                           global_functions_of_time_observation_value_attr),\n                 \"Failed to delete existing attribute '\"\n                     << global_functions_of_time_observation_value_attr << \"'\");\n      }\n      h5::write_to_attribute(volume_data_group_.id(),\n                             global_functions_of_time_observation_value_attr,\n                             observation_value);\n    }\n  }\n}\n\n// Write new connectivity connections given a std::vector of observation ids\ntemplate <size_t SpatialDim>\nvoid VolumeData::extend_connectivity_data(\n    const std::vector<size_t>& observation_ids) {\n  for (const size_t& obs_id : observation_ids) {\n    auto grid_names = get_grid_names(obs_id);\n    auto extents = get_extents(obs_id);\n    auto bases = get_bases(obs_id);\n    auto quadratures = get_quadratures(obs_id);\n\n    const std::vector<int>& new_connectivity =\n        h5::detail::extend_connectivity<SpatialDim>(grid_names, bases,\n                                                    quadratures, extents);\n\n    // Deletes the existing connectivity and replaces it with the new one\n    const std::string path = \"ObservationId\" + std::to_string(obs_id);\n    detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                        AccessType::ReadWrite);\n    const hid_t group_id = observation_group.id();\n    delete_connectivity(group_id);\n    write_connectivity(group_id, new_connectivity);\n  }\n}\n\nvoid VolumeData::write_tensor_component(\n    const size_t observation_id, const std::string& component_name,\n    const DataVector& contiguous_tensor_data, const bool overwrite_existing) {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadWrite);\n  h5::write_data(observation_group.id(), contiguous_tensor_data, component_name,\n                 overwrite_existing);\n}\n\nvoid VolumeData::write_tensor_component(\n    const size_t observation_id, const std::string& component_name,\n    const std::vector<float>& contiguous_tensor_data,\n    const bool overwrite_existing) {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadWrite);\n  h5::write_data(observation_group.id(), contiguous_tensor_data,\n                 {contiguous_tensor_data.size()}, component_name,\n                 overwrite_existing);\n}\n\nbool VolumeData::has_domain() const {\n  return contains_dataset_or_group(volume_data_group_.id(), \"\", \"domain\");\n}\n\nbool VolumeData::has_global_functions_of_time() const {\n  return contains_dataset_or_group(volume_data_group_.id(), \"\",\n                                   \"global_functions_of_time\");\n}\n\nstd::vector<size_t> VolumeData::list_observation_ids() const {\n  const auto names = get_group_names(volume_data_group_.id(), \"\");\n  std::vector<size_t> obs_ids{};\n  obs_ids.reserve(names.size());\n  constexpr std::string_view observation_prefix{\"ObservationId\"};\n  for (const auto& name : names) {\n    if (name.size() <= observation_prefix.size() or\n        name.compare(0, observation_prefix.size(), observation_prefix) != 0) {\n      continue;\n    }\n    obs_ids.push_back(std::stoul(name.substr(observation_prefix.size())));\n  }\n  // pre-compute the observation values as they are expensive to evaluate\n  std::unordered_map<size_t, double> obs_values{obs_ids.size()};\n  for (const auto& id : obs_ids) {\n    obs_values[id] = this->get_observation_value(id);\n  }\n  alg::sort(obs_ids, [&obs_values](const size_t lhs, const size_t rhs) {\n    return obs_values[lhs] < obs_values[rhs];\n  });\n  return obs_ids;\n}\n\ndouble VolumeData::get_observation_value(const size_t observation_id) const {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadOnly);\n  return h5::read_value_attribute<double>(observation_group.id(),\n                                          \"observation_value\");\n}\n\nsize_t VolumeData::find_observation_id(\n    const double observation_value,\n    const std::optional<double>& observation_value_epsilon) const {\n  std::optional<size_t> result_observation_id{};\n  for (const size_t observation_id : list_observation_ids()) {\n    const double file_observation_value = get_observation_value(observation_id);\n    // If we are given an epsilon, use that to compare within roundoff using the\n    // requested value as a scale (unless it's zero, then do 1.0). If we aren't\n    // given an epsilon, compare exactly.\n    if ((observation_value_epsilon.has_value()\n             ? equal_within_roundoff(\n                   observation_value, file_observation_value,\n                   observation_value_epsilon.value(),\n                   (observation_value == 0.0 ? 1.0 : observation_value))\n             : observation_value == file_observation_value)) {\n      if (result_observation_id.has_value()) {\n        ERROR_NO_TRACE(\"There are multiple observations with the same value \"\n                       << observation_value << \" within an epsilon of \"\n                       << observation_value_epsilon.value_or(\n                              std::numeric_limits<double>::epsilon())\n                       << \" in the volume file \" << name_);\n      }\n      result_observation_id = observation_id;\n    }\n  }\n  if (not result_observation_id.has_value()) {\n    ERROR_NO_TRACE(\"No observation with value \" << observation_value\n                                                << \" found in volume file.\");\n  }\n\n  return result_observation_id.value();\n}\n\nstd::vector<std::string> VolumeData::list_tensor_components(\n    const size_t observation_id) const {\n  auto tensor_components =\n      get_group_names(volume_data_group_.id(),\n                      \"ObservationId\" + std::to_string(observation_id));\n  // Remove names that are not tensor components\n  const std::unordered_set<std::string> non_tensor_components{\n      \"connectivity\",\n      \"pole_connectivity\",\n      \"tetrahedral_connectivity\",\n      \"total_extents\",\n      \"grid_names\",\n      \"quadratures\",\n      \"bases\",\n      \"domain\",\n      \"functions_of_time\",\n      \"ElementId\",\n      \"BlockId\"};\n  tensor_components.erase(\n      alg::remove_if(tensor_components,\n                     [&non_tensor_components](const std::string& name) {\n                       return non_tensor_components.find(name) !=\n                              non_tensor_components.end();\n                     }),\n      tensor_components.end());\n  return tensor_components;\n}\n\nstd::vector<std::string> VolumeData::get_grid_names(\n    const size_t observation_id) const {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadOnly);\n  const std::vector<char> names =\n      h5::read_data<1, std::vector<char>>(observation_group.id(), \"grid_names\");\n  const std::string all_names(names.begin(), names.end());\n  std::vector<std::string> grid_names{};\n  boost::split(grid_names, all_names,\n               [](const char c) { return c == h5::VolumeData::separator(); });\n  return grid_names;\n}\n\nTensorComponent VolumeData::get_tensor_component(\n    const size_t observation_id, const std::string& tensor_component) const {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadOnly);\n\n  const hid_t dataset_id =\n      h5::open_dataset(observation_group.id(), tensor_component);\n  const hid_t dataspace_id = h5::open_dataspace(dataset_id);\n  const auto rank =\n      static_cast<size_t>(H5Sget_simple_extent_ndims(dataspace_id));\n  h5::close_dataspace(dataspace_id);\n  const auto h5_data_type = H5Dget_type(dataset_id);\n  h5::close_dataset(dataset_id);\n\n  const auto get_data = [&observation_group, &rank,\n                         &tensor_component](auto type_to_get_v) {\n    using type_to_get = tmpl::type_from<decltype(type_to_get_v)>;\n    switch (rank) {\n      case 1:\n        return h5::read_data<1, type_to_get>(observation_group.id(),\n                                             tensor_component);\n      case 2:\n        return h5::read_data<2, type_to_get>(observation_group.id(),\n                                             tensor_component);\n      case 3:\n        return h5::read_data<3, type_to_get>(observation_group.id(),\n                                             tensor_component);\n      default:\n        ERROR(\"Rank must be 1, 2, or 3. Received data with Rank = \" << rank);\n    }\n  };\n\n  if (h5::types_equal(h5_data_type, h5::h5_type<float>())) {\n    return {tensor_component, get_data(tmpl::type_<std::vector<float>>{})};\n  } else if (h5::types_equal(h5_data_type, h5::h5_type<double>())) {\n    return {tensor_component, get_data(tmpl::type_<DataVector>{})};\n  } else if (h5::types_equal(h5_data_type, h5::h5_type<int>())) {\n    const std::vector<int> stored = get_data(tmpl::type_<std::vector<int>>{});\n    DataVector result{stored.size()};\n    std::ranges::copy(stored.begin(), stored.end(), result.begin());\n    return {tensor_component, result};\n  } else if (h5::types_equal(h5_data_type, h5::h5_type<unsigned int>())) {\n    const std::vector<unsigned int> stored =\n        get_data(tmpl::type_<std::vector<unsigned int>>{});\n    DataVector result{stored.size()};\n    std::ranges::copy(stored.begin(), stored.end(), result.begin());\n    return {tensor_component, result};\n  } else if (h5::types_equal(h5_data_type, h5::h5_type<unsigned long>())) {\n    const std::vector<unsigned long> stored =\n        get_data(tmpl::type_<std::vector<unsigned long>>{});\n    DataVector result{stored.size()};\n    std::ranges::copy(stored.begin(), stored.end(), result.begin());\n    return {tensor_component, result};\n  } else {\n    ERROR(\"Unknown H5 type \" << h5_data_type);\n  }\n}\n\nstd::vector<std::vector<size_t>> VolumeData::get_extents(\n    const size_t observation_id) const {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadOnly);\n  const auto dim =\n      h5::read_value_attribute<size_t>(volume_data_group_.id(), \"dimension\");\n  const auto extents_per_element = static_cast<long>(dim);\n  const auto total_extents = h5::read_data<1, std::vector<size_t>>(\n      observation_group.id(), \"total_extents\");\n  std::vector<std::vector<size_t>> individual_extents;\n  individual_extents.reserve(total_extents.size() / dim);\n  for (auto iter = total_extents.begin(); iter != total_extents.end();\n       iter += extents_per_element) {\n    individual_extents.emplace_back(iter, iter + extents_per_element);\n  }\n  return individual_extents;\n}\n\nstd::pair<size_t, size_t> offset_and_length_for_grid(\n    const std::string& grid_name,\n    const std::vector<std::string>& all_grid_names,\n    const std::vector<std::vector<size_t>>& all_extents) {\n  auto found_grid_name = alg::find(all_grid_names, grid_name);\n  if (found_grid_name == all_grid_names.end()) {\n    ERROR(\"Found no grid named '\" + grid_name + \"'.\");\n  } else {\n    const auto element_index =\n        std::distance(all_grid_names.begin(), found_grid_name);\n    const size_t element_data_offset = std::accumulate(\n        all_extents.begin(), all_extents.begin() + element_index, 0_st,\n        [](const size_t offset, const std::vector<size_t>& extents) {\n          return offset + alg::accumulate(extents, 1_st, std::multiplies<>{});\n        });\n    const size_t element_data_length = alg::accumulate(\n        gsl::at(all_extents, element_index), 1_st, std::multiplies<>{});\n    return {element_data_offset, element_data_length};\n  }\n}\n\nauto VolumeData::get_data_by_element(\n    const std::optional<double> start_observation_value,\n    const std::optional<double> end_observation_value,\n    const std::optional<std::vector<std::string>>& components_to_retrieve) const\n    -> std::vector<std::tuple<size_t, double, std::vector<ElementVolumeData>>> {\n  // First get list of all observations we need to retrieve\n  const std::vector<size_t> obs_ids = list_observation_ids();\n  std::vector<std::tuple<size_t, double, std::vector<ElementVolumeData>>>\n      result{};\n  result.reserve(obs_ids.size());\n  // Sort observation IDs and observation values into the result. This only\n  // copies observed times in\n  // [`start_observation_value`, `end_observation_value`]\n  for (const auto& observation_id : obs_ids) {\n    const double observation_value = get_observation_value(observation_id);\n    if (start_observation_value.value_or(\n            std::numeric_limits<double>::lowest()) <= observation_value and\n        observation_value <= end_observation_value.value_or(\n                                 std::numeric_limits<double>::max())) {\n      result.emplace_back(observation_id, observation_value,\n                          std::vector<ElementVolumeData>{});\n    }\n  }\n  result.shrink_to_fit();\n  // Sort by observation_value\n  alg::sort(result, [](const auto& lhs, const auto& rhs) {\n    return std::get<1>(lhs) < std::get<1>(rhs);\n  });\n\n  // Retrieve element data and insert into result\n  for (auto& single_time_data : result) {\n    const auto known_components =\n        list_tensor_components(std::get<0>(single_time_data));\n\n    std::vector<ElementVolumeData> element_volume_data{};\n    const auto grid_names = get_grid_names(std::get<0>(single_time_data));\n    const auto extents = get_extents(std::get<0>(single_time_data));\n    const auto bases = get_bases(std::get<0>(single_time_data));\n    const auto quadratures = get_quadratures(std::get<0>(single_time_data));\n    element_volume_data.reserve(grid_names.size());\n\n    const auto& component_names =\n        components_to_retrieve.value_or(known_components);\n    std::vector<TensorComponent> tensors{};\n    tensors.reserve(grid_names.size());\n    for (const std::string& component : component_names) {\n      if (not alg::found(known_components, component)) {\n        using ::operator<<;  // STL streams\n        ERROR(\"Could not find tensor component '\"\n              << component\n              << \"' in file. Known components are: \" << known_components);\n      }\n      tensors.emplace_back(\n          get_tensor_component(std::get<0>(single_time_data), component));\n    }\n    // Now split the data by element\n    for (size_t grid_index = 0, offset = 0; grid_index < grid_names.size();\n         ++grid_index) {\n      const size_t mesh_size =\n          alg::accumulate(extents[grid_index], 1_st, std::multiplies<>{});\n      std::vector<TensorComponent> tensor_components{tensors.size()};\n      for (size_t component_index = 0; component_index < tensors.size();\n           ++component_index) {\n        std::visit(\n            [component_index, &component_names, mesh_size, offset,\n             &tensor_components](const auto& tensor_component_data) {\n              std::decay_t<decltype(tensor_component_data)> component(\n                  mesh_size);\n              std::copy(\n                  std::next(tensor_component_data.begin(),\n                            static_cast<std::ptrdiff_t>(offset)),\n                  std::next(tensor_component_data.begin(),\n                            static_cast<std::ptrdiff_t>(offset + mesh_size)),\n                  component.begin());\n              tensor_components[component_index] = TensorComponent{\n                  component_names[component_index], std::move(component)};\n            },\n            tensors[component_index].data);\n      }\n\n      // Sort the tensor components by name so that they are in the same order\n      // in all elements.\n      alg::sort(tensor_components, [](const auto& lhs, const auto& rhs) {\n        return lhs.name < rhs.name;\n      });\n\n      element_volume_data.emplace_back(\n          grid_names[grid_index], std::move(tensor_components),\n          extents[grid_index], bases[grid_index], quadratures[grid_index]);\n      offset += mesh_size;\n    }  // for grid_index\n\n    // Sort the elements so they are in the same order at all time steps\n    alg::sort(element_volume_data,\n              [](const ElementVolumeData& lhs, const ElementVolumeData& rhs) {\n                return lhs.element_name < rhs.element_name;\n              });\n    std::get<2>(single_time_data) = std::move(element_volume_data);\n  }\n  return result;\n}\n\nsize_t VolumeData::get_dimension() const {\n  return h5::read_value_attribute<double>(volume_data_group_.id(), \"dimension\");\n}\n\nstd::vector<std::vector<Spectral::Basis>> VolumeData::get_bases(\n    const size_t observation_id) const {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadOnly);\n  const auto dim =\n      h5::read_value_attribute<size_t>(volume_data_group_.id(), \"dimension\");\n  const auto bases_per_element = static_cast<long>(dim);\n\n  const std::vector<int> bases_coded =\n      h5::read_data<1, std::vector<int>>(observation_group.id(), \"bases\");\n  const auto all_bases = h5_detail::decode_with_dictionary_name(\n      \"Basis dictionary\", bases_coded, observation_group);\n\n  std::vector<std::vector<Spectral::Basis>> element_bases;\n  for (auto iter = all_bases.begin(); iter != all_bases.end();\n       std::advance(iter, bases_per_element)) {\n    element_bases.emplace_back(\n        boost::make_transform_iterator(iter, Spectral::to_basis),\n        boost::make_transform_iterator(std::next(iter, bases_per_element),\n                                       Spectral::to_basis));\n  }\n  return element_bases;\n}\nstd::vector<std::vector<Spectral::Quadrature>> VolumeData::get_quadratures(\n    const size_t observation_id) const {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadOnly);\n  const auto dim =\n      h5::read_value_attribute<size_t>(volume_data_group_.id(), \"dimension\");\n  const auto quadratures_per_element = static_cast<long>(dim);\n  const std::vector<int> quadratures_coded =\n      h5::read_data<1, std::vector<int>>(observation_group.id(), \"quadratures\");\n  const auto all_quadratures = h5_detail::decode_with_dictionary_name(\n      \"Quadrature dictionary\", quadratures_coded, observation_group);\n  std::vector<std::vector<Spectral::Quadrature>> element_quadratures;\n  for (auto iter = all_quadratures.begin(); iter != all_quadratures.end();\n       std::advance(iter, quadratures_per_element)) {\n    element_quadratures.emplace_back(\n        boost::make_transform_iterator(iter, Spectral::to_quadrature),\n        boost::make_transform_iterator(std::next(iter, quadratures_per_element),\n                                       Spectral::to_quadrature));\n  }\n  return element_quadratures;\n}\n\nstd::optional<std::vector<char>> VolumeData::get_domain() const {\n  // we write the domain independently of the observation_id since a refactor\n  if (contains_dataset_or_group(volume_data_group_.id(), \"\", \"domain\")) {\n    return h5::read_data<1, std::vector<char>>(volume_data_group_.id(),\n                                               \"domain\");\n  }\n  // default to old location\n  const auto& observation_ids = list_observation_ids();\n  if (observation_ids.empty()) {\n    return std::nullopt;\n  }\n  const size_t observation_id = observation_ids.back();\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadOnly);\n  if (contains_dataset_or_group(observation_group.id(), \"\", \"domain\")) {\n    return h5::read_data<1, std::vector<char>>(observation_group.id(),\n                                               \"domain\");\n  }\n  return std::nullopt;\n}\n\nstd::optional<std::vector<char>> VolumeData::get_functions_of_time(\n    const size_t observation_id) const {\n  const std::string path = \"ObservationId\" + std::to_string(observation_id);\n  detail::OpenGroup observation_group(volume_data_group_.id(), path,\n                                      AccessType::ReadOnly);\n  if (not contains_dataset_or_group(observation_group.id(), \"\",\n                                    \"functions_of_time\")) {\n    return std::nullopt;\n  }\n  return h5::read_data<1, std::vector<char>>(observation_group.id(),\n                                             \"functions_of_time\");\n}\n\nstd::optional<std::vector<char>> VolumeData::get_global_functions_of_time()\n    const {\n  if (contains_dataset_or_group(volume_data_group_.id(), \"\",\n                                \"global_functions_of_time\")) {\n    return h5::read_data<1, std::vector<char>>(volume_data_group_.id(),\n                                               \"global_functions_of_time\");\n  }\n  // fall back to old location if global not present\n  const auto& observation_ids = list_observation_ids();\n  if (observation_ids.empty()) {\n    return std::nullopt;\n  }\n  return get_functions_of_time(observation_ids.back());\n}\n\ntemplate <size_t Dim>\nMesh<Dim> mesh_for_grid(\n    const std::string& grid_name,\n    const std::vector<std::string>& all_grid_names,\n    const std::vector<std::vector<size_t>>& all_extents,\n    const std::vector<std::vector<Spectral::Basis>>& all_bases,\n    const std::vector<std::vector<Spectral::Quadrature>>& all_quadratures) {\n  const auto found_grid_name = alg::find(all_grid_names, grid_name);\n  if (found_grid_name == all_grid_names.end()) {\n    ERROR(\"Found no grid named '\" + grid_name + \"'.\");\n  } else {\n    const auto element_index =\n        std::distance(all_grid_names.begin(), found_grid_name);\n    const auto& extents = gsl::at(all_extents, element_index);\n    const auto& bases = gsl::at(all_bases, element_index);\n    const auto& quadratures = gsl::at(all_quadratures, element_index);\n    ASSERT(extents.size() == Dim, \"Extents in \" << Dim << \"D should have size \"\n                                                << Dim << \", but found size \"\n                                                << extents.size() << \".\");\n    ASSERT(bases.size() == Dim, \"Bases in \" << Dim << \"D should have size \"\n                                            << Dim << \", but found size \"\n                                            << bases.size() << \".\");\n    ASSERT(quadratures.size() == Dim, \"Quadratures in \"\n                                          << Dim << \"D should have size \" << Dim\n                                          << \", but found size \"\n                                          << quadratures.size() << \".\");\n    return Mesh<Dim>{make_array<size_t, Dim>(extents),\n                     make_array<Spectral::Basis, Dim>(bases),\n                     make_array<Spectral::Quadrature, Dim>(quadratures)};\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template void h5::VolumeData::extend_connectivity_data<DIM(data)>( \\\n      const std::vector<size_t>& observation_ids);                   \\\n  template Mesh<DIM(data)> mesh_for_grid(                            \\\n      const std::string& grid_name,                                  \\\n      const std::vector<std::string>& all_grid_names,                \\\n      const std::vector<std::vector<size_t>>& all_extents,           \\\n      const std::vector<std::vector<Spectral::Basis>>& all_bases,    \\\n      const std::vector<std::vector<Spectral::Quadrature>>& all_quadratures);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/VolumeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"IO/H5/Object.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n\n/// \\cond\nclass DataVector;\nstruct ElementVolumeData;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\nstruct TensorComponent;\n/// \\endcond\n\nnamespace h5 {\n/*!\n * \\ingroup HDF5Group\n * \\brief A volume data subfile written inside an H5 file.\n *\n * The volume data inside the subfile can be of any dimensionality greater than\n * zero. This means that in a 3D simulation, data on 2-dimensional surfaces are\n * written as a VolumeData subfile. Data can be written using the\n * `write_volume_data()` method. An integral observation id is used to keep\n * track of the observation instance at which the data is written, and\n * associated with it is a floating point observation value, such as the\n * simulation time at which the data was written. The observation id will\n * generally be the result of hashing the temporal identifier used for the\n * simulation.\n *\n * \\par Grid names\n * The data stored in the subfile are the tensor components passed to the\n * `write_volume_data()` method as a `std::vector<ElementVolumeData>`. Typically\n * the `GRID_NAME` should be the output of the stream operator of the spatial ID\n * of the parallel component element sending the data to be observed. For\n * example, in the case of a dG evolution where the spatial IDs are\n * `ElementId`s, the grid names would be of the form `[B0,(L2I3,L2I3,L2I3)]`.\n *\n * \\par Data layout in the H5 file\n * The data are written contiguously inside of the H5 subfile, in that each\n * tensor component has a single dataset which holds all of the data from all\n * elements, e.g. a tensor component `T_xx` which is found on all grids appears\n * in the path `H5_FILE_NAME/SUBFILE_NAME.vol/OBSERVATION_ID/GRID_NAME/T_xx` and\n * that is where all of the `T_xx` data from all of the grids resides. Note that\n * coordinates must be written as tensors in order to visualize the data in\n * ParaView, Visit, etc.  In order to reconstruct which data came from which\n * grid, the `get_grid_names()`, and `get_extents()` methods list the grids\n * and their extents in the order which they and the data were written.\n * For example, if the first grid has name `GRID_NAME` with extents\n * `{2, 2, 2}`, it was responsible for contributing the first 2*2*2 = 8 grid\n * points worth of data in each tensor dataset. Use the\n * `h5::offset_and_length_for_grid` function to compute the offset into the\n * contiguous dataset that corresponds to a particular grid.\n *\n * \\par Domain and FunctionsOfTime\n * A serialized representation of the domain and the functions of time can be\n * written into the subfile alongside the tensor data. Reconstructing the domain\n * (specifically the block maps) is needed for accurate interpolation of the\n * tensor data to another set of points. The domain is currently stored as a\n * `std::vector<char>`, and it is up to the calling code to serialize and\n * deserialize the data, taking into account that files may be written and read\n * with different versions of the code.\n *\n * \\warning Currently the topology of the grids is assumed to be tensor products\n * of lines, i.e. lines, quadrilaterals, and hexahedrons. However, this can be\n * extended in the future. If support for more topologies is required, please\n * file an issue.\n */\nclass VolumeData : public h5::Object {\n public:\n  static std::string extension() { return \".vol\"; }\n\n  VolumeData(bool subfile_exists, detail::OpenGroup&& group, hid_t location,\n             const std::string& name, uint32_t version = 1);\n\n  VolumeData(const VolumeData& /*rhs*/) = delete;\n  VolumeData& operator=(const VolumeData& /*rhs*/) = delete;\n  VolumeData(VolumeData&& /*rhs*/) = delete;             // NOLINT\n  VolumeData& operator=(VolumeData&& /*rhs*/) = delete;  // NOLINT\n\n  ~VolumeData() override = default;\n\n  /*!\n   * \\returns the header of the VolumeData file\n   */\n  const std::string& get_header() const { return header_; }\n\n  /*!\n   * \\returns the user-specified version number of the VolumeData file\n   *\n   * \\note h5::Version returns a uint32_t, so we return one here too for the\n   * version\n   */\n  uint32_t get_version() const { return version_; }\n\n  /*!\n   * \\brief Write volume data at an observation id and observation value.\n   *\n   * \\param observation_id The integral observation id at which the data is\n   * written.\n   * \\param observation_value The floating point observation value (e.g. time)\n   * at which the data is written.\n   * \\param elements The volume data to write, passed as a vector of\n   * ElementVolumeData structs.\n   * \\param serialized_domain An optional serialized domain. It will only be\n   * written if there is not already a domain stored in the subfile.\n   * \\param serialized_observation_functions_of_time An optional serialized\n   * observation-specific functions of time. It should be valid at the given\n   * observation value, but not contain the entire history to save space.\n   * \\param serialized_global_functions_of_time An optional serialized global\n   * functions of time oject. It should be valid for the entire simulation. It\n   * will be used to overwrite the existing global functions of time iff the old\n   * object was written at a smaller observation_value (i.e. an earlier time).\n   */\n  void write_volume_data(\n      size_t observation_id, double observation_value,\n      const std::vector<ElementVolumeData>& elements,\n      const std::optional<std::vector<char>>& serialized_domain = std::nullopt,\n      const std::optional<std::vector<char>>&\n          serialized_observation_functions_of_time = std::nullopt,\n      const std::optional<std::vector<char>>&\n          serialized_global_functions_of_time = std::nullopt);\n\n  /// \\returns true if a serialized domain has been written to the subfile.\n  bool has_domain() const;\n\n  /// \\returns true if serialized functions of time have been written to the\n  /// subfile.\n  bool has_global_functions_of_time() const;\n\n  /// Overwrites the current connectivity dataset with a new one. This new\n  /// connectivity dataset builds connectivity within each block in the domain\n  /// for each observation id in a list of observation id's\n  template <size_t SpatialDim>\n  void extend_connectivity_data(const std::vector<size_t>& observation_ids);\n\n  void write_tensor_component(const size_t observation_id,\n                              const std::string& component_name,\n                              const DataVector& contiguous_tensor_data,\n                              bool overwrite_existing = false);\n\n  void write_tensor_component(const size_t observation_id,\n                              const std::string& component_name,\n                              const std::vector<float>& contiguous_tensor_data,\n                              bool overwrite_existing = false);\n\n  /// List all the integral observation ids in the subfile\n  ///\n  /// The list of observation IDs is sorted by their observation value, as\n  /// returned by get_observation_value(size_t).\n  std::vector<size_t> list_observation_ids() const;\n\n  /// Get the observation value at the the integral observation id in the\n  /// subfile\n  double get_observation_value(size_t observation_id) const;\n\n  /// Find the observation ID that matches the `observation_value`\n  ///\n  /// \\details An epsilon can be specified and the observation id that matches\n  /// within the epsilon of `observation_value` will be returned. If there is\n  /// more than one id that is within the epsilon, an error will occur. If no\n  /// epsilon is specified, this function will do exact comparison.\n  size_t find_observation_id(\n      double observation_value,\n      const std::optional<double>& observation_value_epsilon =\n          std::nullopt) const;\n\n  /// List all the tensor components at observation id `observation_id`\n  std::vector<std::string> list_tensor_components(size_t observation_id) const;\n\n  /// List the names of all the grids at observation id `observation_id`\n  std::vector<std::string> get_grid_names(size_t observation_id) const;\n\n  /// Read a tensor component with name `tensor_component` at observation id\n  /// `observation_id` from all grids in the file\n  TensorComponent get_tensor_component(\n      size_t observation_id, const std::string& tensor_component) const;\n\n  /// Read the extents of all the grids stored in the file at the observation id\n  /// `observation_id`\n  std::vector<std::vector<size_t>> get_extents(size_t observation_id) const;\n\n  /// Retrieve volume data for IDs in\n  /// `[start_observation_value, end_observation_value]`.\n  ///\n  /// Returns a `std::vector` over times, sorted in ascending order of the\n  /// observation value (the time in evolutions). The vector holds a\n  /// `std::tuple` that holds\n  /// 1. the integral observation ID\n  /// 2. the observation value (time in evolutions)\n  /// 3. a vector of `ElementVolumeData` sorted by the elements' name string.\n  ///\n  /// - If `start_observation_value` is `std::nullopt` then return\n  ///   everything from the beginning of the data to `end_observation_value`. If\n  ///   `end_observation_value` is `std::nullopt` then return everything from\n  ///   `start_observation_value` to the end of the data.\n  /// - If `components_to_retrieve` is `std::nullopt` then return all\n  ///   components.\n  auto get_data_by_element(std::optional<double> start_observation_value,\n                           std::optional<double> end_observation_value,\n                           const std::optional<std::vector<std::string>>&\n                               components_to_retrieve = std::nullopt) const\n      -> std::vector<\n          std::tuple<size_t, double, std::vector<ElementVolumeData>>>;\n\n  /// Read the dimensionality of the grids.  Note : This is the dimension of\n  /// the grids as manifolds, not the dimension of the embedding space.  For\n  /// example, the volume data of a sphere is 2-dimensional, even though\n  /// each point has an x, y, and z coordinate.\n  size_t get_dimension() const;\n\n  /// Return the character used as a separator between grids in the subfile.\n  static char separator() { return ':'; }\n\n  /// Return the basis being used for each element along each axis\n  std::vector<std::vector<Spectral::Basis>> get_bases(\n      size_t observation_id) const;\n\n  /// Return the quadrature being used for each element along each axis\n  std::vector<std::vector<Spectral::Quadrature>> get_quadratures(\n      size_t observation_id) const;\n\n  /*!\n   * \\brief Get the serialized domain if it was written.\n   */\n  std::optional<std::vector<char>> get_domain() const;\n\n  /*!\n   * \\brief Get the observation-specific serialized functions of time at an \\p\n   * observation_id if they were written.\n   */\n  std::optional<std::vector<char>> get_functions_of_time(\n      size_t observation_id) const;\n\n  /*!\n   * \\brief Get the serialized global functions of time in the subfile if they\n   * were written.\n   */\n  std::optional<std::vector<char>> get_global_functions_of_time() const;\n\n  const std::string& subfile_path() const override { return path_; }\n\n private:\n  detail::OpenGroup group_{};\n  std::string name_{};\n  std::string path_{};\n  uint32_t version_{};\n  detail::OpenGroup volume_data_group_{};\n  std::string header_{};\n};\n\n/*!\n * \\brief Find the interval within the contiguous dataset stored in\n * `h5::VolumeData` that holds data for a particular `grid_name`.\n *\n * `h5::VolumeData` stores data for all grids that compose the volume\n * contiguously. This function helps with reconstructing which part of that\n * contiguous dataset belongs to a particular grid. See the `h5::VolumeData`\n * documentation for more information on how it stores data.\n *\n * To use this function, call `h5::VolumeData::get_grid_names` and\n * `h5::VolumeData::get_extents` and pass the results for the `all_grid_names`\n * and `all_extents` arguments, respectively. This means you can retrieve this\n * information from an `h5::VolumeData` once and use it to call\n * `offset_and_length_for_grid` multiple times with different `grid_name`s.\n *\n * Here is an example for using this function:\n *\n * \\snippet Test_VolumeData.cpp find_offset\n *\n * \\see `h5::VolumeData`\n */\nstd::pair<size_t, size_t> offset_and_length_for_grid(\n    const std::string& grid_name,\n    const std::vector<std::string>& all_grid_names,\n    const std::vector<std::vector<size_t>>& all_extents);\n\ntemplate <size_t Dim>\nMesh<Dim> mesh_for_grid(\n    const std::string& grid_name,\n    const std::vector<std::string>& all_grid_names,\n    const std::vector<std::vector<size_t>>& all_extents,\n    const std::vector<std::vector<Spectral::Basis>>& all_bases,\n    const std::vector<std::vector<Spectral::Quadrature>>& all_quadratures);\n\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/H5/Wrappers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <hdf5.h>\n\n#include \"Utilities/ForceInline.hpp\"\n\n// H5F wrappers\nnamespace h5 {\n/// \\ingroup HDF5Group\nSPECTRE_ALWAYS_INLINE auto h5f_acc_rdonly() {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  return H5F_ACC_RDONLY;\n#pragma GCC diagnostic pop\n}\n\n/// \\ingroup HDF5Group\nSPECTRE_ALWAYS_INLINE auto h5f_acc_rdwr() {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  return H5F_ACC_RDWR;\n#pragma GCC diagnostic pop\n}\n\nSPECTRE_ALWAYS_INLINE auto h5f_acc_trunc() {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  return H5F_ACC_TRUNC;\n#pragma GCC diagnostic pop\n}\n}  // namespace h5\n\n// H5P wrappers\nnamespace h5 {\n/// \\ingroup HDF5Group\nSPECTRE_ALWAYS_INLINE auto h5p_default() {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  return H5P_DEFAULT;\n#pragma GCC diagnostic pop\n}\n}  // namespace h5\n\n// H5S wrappers\nnamespace h5 {\n/// \\ingroup HDF5Group\nSPECTRE_ALWAYS_INLINE auto h5s_all() {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  return H5S_ALL;\n#pragma GCC diagnostic pop\n}\n\n/// \\ingroup HDF5Group\nSPECTRE_ALWAYS_INLINE auto h5s_unlimited() {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  return H5S_UNLIMITED;\n#pragma GCC diagnostic pop\n}\n\n/// \\ingroup HDF5Group\nSPECTRE_ALWAYS_INLINE auto h5s_scalar() {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n  return H5S_SCALAR;\n#pragma GCC diagnostic pop\n}\n}  // namespace h5\n"
  },
  {
    "path": "src/IO/Importers/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ReadVolumeData.hpp\n  ReceiveVolumeData.hpp\n  RegisterWithElementDataReader.hpp\n  )\n"
  },
  {
    "path": "src/IO/Importers/Actions/ReadVolumeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"IO/Importers/ObservationSelector.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace importers {\n\n/// \\cond\ntemplate <typename Metavariables>\nstruct ElementDataReader;\nnamespace Actions {\ntemplate <size_t Dim, typename FieldTagsList, typename ReceiveComponent>\nstruct ReadAllVolumeDataAndDistribute;\n}  // namespace Actions\n/// \\endcond\n\nnamespace Tags {\n/*!\n * \\brief Indicates an available tensor field is selected for importing, along\n * with the name of the dataset in the volume data file.\n *\n * Set the value to a dataset name to import the `FieldTag` from that dataset,\n * or to `std::nullopt` to skip importing the `FieldTag`. The dataset name\n * excludes tensor component suffixes like \"_x\" or \"_xy\". These suffixes will be\n * added automatically. A sensible value for the dataset name is often\n * `db::tag_name<FieldTag>()`, but the user should generally be given the\n * opportunity to set the dataset name in the input file.\n */\ntemplate <typename FieldTag>\nstruct Selected : db::SimpleTag {\n  using type = std::optional<std::string>;\n};\n}  // namespace Tags\n\nnamespace detail {\n\n// Read the single `tensor_name` from the `volume_file`, taking care of suffixes\n// like \"_x\" etc for its components.\ntemplate <typename TensorType>\nvoid read_tensor_data(const gsl::not_null<TensorType*> tensor_data,\n                      const std::string& tensor_name,\n                      const h5::VolumeData& volume_file,\n                      const size_t observation_id) {\n  for (size_t i = 0; i < tensor_data->size(); ++i) {\n    const auto& tensor_component = volume_file.get_tensor_component(\n        observation_id, tensor_name + tensor_data->component_suffix(\n                                          tensor_data->get_tensor_index(i)));\n    if (not std::holds_alternative<DataVector>(tensor_component.data)) {\n      ERROR(\"The tensor component '\"\n            << tensor_component.name\n            << \"' is not a double-precision DataVector. Reading in \"\n               \"single-precision volume data is not supported.\");\n    }\n    (*tensor_data)[i] = std::get<DataVector>(tensor_component.data);\n  }\n}\n\n// Read the `selected_fields` from the `volume_file`. Reads the data\n// for all elements in the `volume_file` at once. Invoked lazily when data\n// for an element in the volume file is needed.\ntemplate <typename FieldTagsList>\ntuples::tagged_tuple_from_typelist<FieldTagsList> read_tensor_data(\n    const h5::VolumeData& volume_file, const size_t observation_id,\n    const tuples::tagged_tuple_from_typelist<\n        db::wrap_tags_in<Tags::Selected, FieldTagsList>>& selected_fields) {\n  tuples::tagged_tuple_from_typelist<FieldTagsList> all_tensor_data{};\n  tmpl::for_each<FieldTagsList>([&all_tensor_data, &volume_file,\n                                 &observation_id,\n                                 &selected_fields](auto field_tag_v) {\n    using field_tag = tmpl::type_from<decltype(field_tag_v)>;\n    const auto& selection = get<Tags::Selected<field_tag>>(selected_fields);\n    if (not selection.has_value()) {\n      return;\n    }\n    read_tensor_data(make_not_null(&get<field_tag>(all_tensor_data)),\n                     selection.value(), volume_file, observation_id);\n  });\n  return all_tensor_data;\n}\n\n// Extract this element's data from the read-in dataset\ntemplate <typename FieldTagsList>\ntuples::tagged_tuple_from_typelist<FieldTagsList> extract_element_data(\n    const std::pair<size_t, size_t>& element_data_offset_and_length,\n    const tuples::tagged_tuple_from_typelist<FieldTagsList>& all_tensor_data,\n    const tuples::tagged_tuple_from_typelist<\n        db::wrap_tags_in<Tags::Selected, FieldTagsList>>& selected_fields) {\n  tuples::tagged_tuple_from_typelist<FieldTagsList> element_data{};\n  tmpl::for_each<FieldTagsList>(\n      [&element_data, &offset = element_data_offset_and_length.first,\n       &num_points = element_data_offset_and_length.second, &all_tensor_data,\n       &selected_fields](auto field_tag_v) {\n        using field_tag = tmpl::type_from<decltype(field_tag_v)>;\n        const auto& selection = get<Tags::Selected<field_tag>>(selected_fields);\n        if (not selection.has_value()) {\n          return;\n        }\n        auto& element_tensor_data = get<field_tag>(element_data);\n        // Iterate independent components of the tensor\n        for (size_t i = 0; i < element_tensor_data.size(); ++i) {\n          const DataVector& data_tensor_component =\n              get<field_tag>(all_tensor_data)[i];\n          DataVector element_tensor_component{num_points};\n          // Retrieve data from slice of the contigious dataset\n          for (size_t j = 0; j < element_tensor_component.size(); ++j) {\n            element_tensor_component[j] = data_tensor_component[offset + j];\n          }\n          element_tensor_data[i] = element_tensor_component;\n        }\n      });\n  return element_data;\n}\n\n// Check that the inertial coordinates computed with the given domain are the\n// same as the ones passed to this function.\n// This is important to avoid hard-to-find bugs where data is loaded\n// to the wrong coordinates. For example, if the evolution domain deforms the\n// excision surfaces a bit but the initial data doesn't, then it would be wrong\n// to load the initial data to the evolution grid without an interpolation.\ntemplate <size_t Dim>\nvoid verify_inertial_coordinates(\n    const Domain<Dim>& domain, const double time,\n    const domain::FunctionsOfTimeMap& functions_of_time,\n    const ElementId<Dim>& element_id, const Mesh<Dim>& mesh,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  const auto logical_coords = logical_coordinates(mesh);\n  ElementMap<Dim, Frame::Inertial> element_map{\n      element_id, domain.blocks()[element_id.block_id()]};\n  const auto mapped_inertial_coords =\n      element_map(logical_coords, time, functions_of_time);\n  const double scale = blaze::max(get(magnitude(mapped_inertial_coords)));\n  if (not equal_within_roundoff(mapped_inertial_coords, inertial_coords,\n                                std::numeric_limits<double>::epsilon() * 100.0,\n                                scale)) {\n    DataVector diff =\n        square(get<0>(inertial_coords) - get<0>(mapped_inertial_coords));\n    for (size_t d = 1; d < Dim; ++d) {\n      diff += square(inertial_coords.get(d) - mapped_inertial_coords.get(d));\n    }\n    diff = sqrt(diff);\n    const double max_coord_distance = blaze::max(diff);\n    CAPTURE_FOR_ERROR(element_id);\n    CAPTURE_FOR_ERROR(max_coord_distance);\n    CAPTURE_FOR_ERROR(scale);\n    ERROR_NO_TRACE(\n        \"The source and target domain don't match. Set 'ElementsAreIdentical: \"\n        \"False' to enable interpolation between the grids.\");\n  }\n}\n\n// Interpolate only the `selected_fields` in `source_element_data` to the\n// arbitrary `target_logical_coords` (used when elements are not the same)\ntemplate <typename FieldTagsList, size_t Dim>\nvoid interpolate_selected_fields(\n    const gsl::not_null<tuples::tagged_tuple_from_typelist<FieldTagsList>*>\n        target_element_data,\n    const tuples::tagged_tuple_from_typelist<FieldTagsList>&\n        source_element_data,\n    const Mesh<Dim>& source_mesh,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n        target_logical_coords,\n    const std::vector<size_t>& offsets,\n    const tuples::tagged_tuple_from_typelist<\n        db::wrap_tags_in<Tags::Selected, FieldTagsList>>& selected_fields) {\n  const intrp::Irregular<Dim> interpolator{source_mesh, target_logical_coords};\n  const size_t target_num_points = target_logical_coords.begin()->size();\n  ASSERT(target_num_points == offsets.size(),\n         \"The number of target points (\"\n             << target_num_points << \") must match the number of offsets (\"\n             << offsets.size() << \").\");\n  DataVector target_tensor_component_buffer{target_num_points};\n  tmpl::for_each<FieldTagsList>([&source_element_data, &target_element_data,\n                                 &interpolator, &target_tensor_component_buffer,\n                                 &selected_fields, &offsets](auto field_tag_v) {\n    using field_tag = tmpl::type_from<decltype(field_tag_v)>;\n    const auto& selection = get<Tags::Selected<field_tag>>(selected_fields);\n    if (not selection.has_value()) {\n      return;\n    }\n    const auto& source_tensor_data = get<field_tag>(source_element_data);\n    auto& target_tensor_data = get<field_tag>(*target_element_data);\n    // Iterate independent components of the tensor\n    for (size_t i = 0; i < source_tensor_data.size(); ++i) {\n      const DataVector& source_tensor_component = source_tensor_data[i];\n      DataVector& target_tensor_component = target_tensor_data[i];\n      // Interpolate\n      interpolator.interpolate(make_not_null(&target_tensor_component_buffer),\n                               source_tensor_component);\n      // Fill target element data at corresponding offsets\n      for (size_t j = 0; j < target_tensor_component_buffer.size(); ++j) {\n        target_tensor_component[offsets[j]] = target_tensor_component_buffer[j];\n      }\n    }\n  });\n}\n\n// Interpolate only the `selected_fields` in `source_element_data` to the\n// `target_mesh` (used when elements differ only by p-refinement)\ntemplate <typename FieldTagsList, size_t Dim>\nvoid interpolate_selected_fields(\n    const gsl::not_null<tuples::tagged_tuple_from_typelist<FieldTagsList>*>\n        target_element_data,\n    const tuples::tagged_tuple_from_typelist<FieldTagsList>&\n        source_element_data,\n    const Mesh<Dim>& source_mesh, const Mesh<Dim>& target_mesh,\n    const tuples::tagged_tuple_from_typelist<\n        db::wrap_tags_in<Tags::Selected, FieldTagsList>>& selected_fields) {\n  const intrp::RegularGrid<Dim> interpolator{source_mesh, target_mesh};\n  tmpl::for_each<FieldTagsList>([&source_element_data, &target_element_data,\n                                 &interpolator,\n                                 &selected_fields](auto field_tag_v) {\n    using field_tag = tmpl::type_from<decltype(field_tag_v)>;\n    const auto& selection = get<Tags::Selected<field_tag>>(selected_fields);\n    if (not selection.has_value()) {\n      return;\n    }\n    const auto& source_tensor_data = get<field_tag>(source_element_data);\n    auto& target_tensor_data = get<field_tag>(*target_element_data);\n    // Iterate independent components of the tensor\n    for (size_t i = 0; i < source_tensor_data.size(); ++i) {\n      const DataVector& source_tensor_component = source_tensor_data[i];\n      DataVector& target_tensor_component = target_tensor_data[i];\n      // Interpolate\n      interpolator.interpolate(make_not_null(&target_tensor_component),\n                               source_tensor_component);\n    }\n  });\n}\n\n}  // namespace detail\n\nnamespace Actions {\n\n/*!\n * \\brief Read a volume data file and distribute the data to all registered\n * elements, interpolating to the target points if needed.\n *\n * \\note Use this action if you want to quickly load and distribute volume data.\n * If you need to beyond that (such as more control over input-file options),\n * write a new action and dispatch to\n * `importers::Actions::ReadAllVolumeDataAndDistribute`.\n *\n * \\details Invoke this action on the elements of an array parallel component to\n * dispatch reading the volume data file specified by options placed in the\n * `ImporterOptionsGroup`. The tensors in `FieldTagsList` will be loaded from\n * the file and distributed to all elements that have previously registered. Use\n * `importers::Actions::RegisterWithElementDataReader` to register the elements\n * of the array parallel component in a previous phase.\n *\n * Note that the volume data file will only be read once per node, triggered by\n * the first element that invokes this action. All subsequent invocations of\n * this action on the node will do nothing. See\n * `importers::Actions::ReadAllVolumeDataAndDistribute` for details.\n *\n * The data is distributed to the elements using `Parallel::receive_data`. The\n * elements can monitor `importers::Tags::VolumeData` in their inbox to wait for\n * the data and process it once it's available. We provide the action\n * `importers::Actions::ReceiveVolumeData` that waits for the data and moves it\n * directly into the DataBox. You can also implement a specialized action that\n * might verify and post-process the data before populating the DataBox.\n *\n * \\see Dev guide on \\ref dev_guide_importing\n */\ntemplate <typename ImporterOptionsGroup, typename FieldTagsList>\nstruct ReadVolumeData {\n  using const_global_cache_tags =\n      tmpl::list<Tags::ImporterOptions<ImporterOptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Not using `ckLocalBranch` here to make sure the simple action invocation\n    // is asynchronous.\n    auto& reader_component = Parallel::get_parallel_component<\n        importers::ElementDataReader<Metavariables>>(cache);\n    Parallel::simple_action<importers::Actions::ReadAllVolumeDataAndDistribute<\n        Dim, FieldTagsList, ParallelComponent>>(\n        reader_component,\n        get<Tags::ImporterOptions<ImporterOptionsGroup>>(cache), 0_st);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Read a volume data file and distribute the data to all registered\n * elements, interpolating to the target points if needed.\n *\n * This action can be invoked on the `importers::ElementDataReader` component\n * once all elements have been registered with it. It opens the data file, reads\n * the data for each registered element and uses `Parallel::receive_data` to\n * distribute the data to the elements. The elements can monitor\n * `importers::Tags::VolumeData` in their inbox to wait for the data and process\n * it once it's available. You can use `importers::Actions::ReceiveVolumeData`\n * to wait for the data and move it directly into the DataBox, or implement a\n * specialized action that might verify and post-process the data.\n *\n * Note that instead of invoking this action directly on the\n * `importers::ElementDataReader` component you can invoke the iterable action\n * `importers::Actions::ReadVolumeData` on the elements of an array parallel\n * component for simple use cases.\n *\n * - Pass along the following arguments to the simple action invocation:\n *   - `options`: `importers::ImporterOptions` that specify the H5 files\n *     with volume data to load.\n *   - `volume_data_id`: A number (or hash) that identifies this import\n *     operation. Will also be used to identify the loaded volume data in the\n *     inbox of the receiving elements.\n *   - `selected_fields` (optional): See below.\n * - The `FieldTagsList` parameter specifies a typelist of tensor tags that\n * can be read from the file and provided to each element. The subset of tensors\n * that will actually be read and distributed can be selected at runtime with\n * the `selected_fields` argument that is passed to this simple action. See\n * importers::Tags::Selected for details. By default, all tensors in the\n * `FieldTagsList` are selected, and read from datasets named\n * `db::tag_name<Tag>() + suffix`, where the `suffix` is empty for scalars, or\n * `\"_\"` followed by the `Tensor::component_name` for each independent tensor\n * component.\n * - `Parallel::receive_data` is invoked on each registered element of the\n * `ReceiveComponent` to populate `importers::Tags::VolumeData` in the element's\n * inbox with a `tuples::tagged_tuple_from_typelist<FieldTagsList>` containing\n * the tensor data for that element. The `ReceiveComponent` must the the same\n * that was encoded into the `Parallel::ArrayComponentId` used to register the\n * elements. The `volume_data_id` passed to this action is used as key.\n *\n * \\par Memory consumption\n * This action runs once on every node. It reads all volume data files on the\n * node, but doesn't keep them all in memory at once. The following items\n * contribute primarily to memory consumption and can be reconsidered if we run\n * into memory issues:\n *\n * - `all_tensor_data`: All requested tensor components in the volume data file\n *   at the specified observation ID. Only data from one volume data file is\n *   held in memory at any time. Only data from files that overlap with target\n *   elements on this node are read in.\n * - `target_element_data_buffer`: Holds incomplete interpolated data for each\n *   (target) element that resides on this node. In the worst case, when all\n *   target elements need data from the last source element in the last volume\n *   data file, the memory consumption of this buffer can grow to hold all\n *   requested tensor components on all elements that reside on this node.\n *   However, elements are erased from this buffer once their interpolated data\n *   is complete (and sent to the target element), so the memory consumption\n *   should remain much lower in practice.\n *\n * \\see Dev guide on \\ref dev_guide_importing\n */\ntemplate <size_t Dim, typename FieldTagsList, typename ReceiveComponent>\nstruct ReadAllVolumeDataAndDistribute {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(DataBox& box, Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const ImporterOptions& options, const size_t volume_data_id,\n                    tuples::tagged_tuple_from_typelist<\n                        db::wrap_tags_in<Tags::Selected, FieldTagsList>>\n                        selected_fields = select_all_fields(FieldTagsList{})) {\n    const bool elements_are_identical =\n        get<OptionTags::ElementsAreIdentical>(options);\n\n    // Only read and distribute the volume data once\n    // This action will be invoked by `importers::Actions::ReadVolumeData` from\n    // every element on the node, but only the first invocation reads the file\n    // and distributes the data to all elements. Subsequent invocations do\n    // nothing. The `volume_data_id` identifies whether or not we have already\n    // read the requested data. Doing this at runtime avoids having to collect\n    // all data files that will be read in at compile-time to initialize a flag\n    // in the DataBox for each of them.\n    const auto& has_read_volume_data =\n        db::get<Tags::ElementDataAlreadyRead>(box);\n    if (has_read_volume_data.find(volume_data_id) !=\n        has_read_volume_data.end()) {\n      return;\n    }\n    db::mutate<Tags::ElementDataAlreadyRead>(\n        [&volume_data_id](const auto local_has_read_volume_data) {\n          local_has_read_volume_data->insert(volume_data_id);\n        },\n        make_not_null(&box));\n\n    // This is the subset of elements that reside on this node. They have\n    // registered themselves before. Our job is to fill them with volume data.\n    std::unordered_set<ElementId<Dim>> target_element_ids{};\n    for (const auto& target_element : get<Tags::RegisteredElements<Dim>>(box)) {\n      const auto& element_array_component_id = target_element.first;\n      const CkArrayIndex& raw_element_index =\n          element_array_component_id.array_index();\n      // Check if the parallel component of the registered element matches the\n      // callback, because it's possible that elements from other components\n      // with the same index are also registered.\n      // Since the way the component is encoded in `ArrayComponentId` is\n      // private to that class, we construct one and compare.\n      // Can't use Parallel::make_array_component_id here because we need the\n      // original array_index type, not a CkArrayIndex.\n      if (element_array_component_id !=\n          Parallel::ArrayComponentId(\n              std::add_pointer_t<ReceiveComponent>{nullptr},\n              raw_element_index)) {\n        continue;\n      }\n      const auto target_element_id =\n          Parallel::ArrayIndex<ElementId<Dim>>(raw_element_index).get_index();\n      target_element_ids.insert(target_element_id);\n    }\n    if (UNLIKELY(target_element_ids.empty())) {\n      return;\n    }\n\n    // Temporary buffer for data on target elements. These variables get filled\n    // with interpolated data while we're reading in volume files. Once data on\n    // an element is complete, the data is sent to that element and removed from\n    // this list.\n    std::unordered_map<ElementId<Dim>,\n                       tuples::tagged_tuple_from_typelist<FieldTagsList>>\n        target_element_data_buffer{};\n    std::unordered_map<ElementId<Dim>, std::vector<size_t>>\n        all_indices_of_filled_interp_points{};\n\n    // Resolve the file glob\n    const std::string& file_glob = get<OptionTags::FileGlob>(options);\n    const std::vector<std::string> file_paths = file_system::glob(file_glob);\n    if (file_paths.empty()) {\n      ERROR_NO_TRACE(\"The file glob '\" << file_glob << \"' matches no files.\");\n    }\n\n    // Open every file in turn\n    std::optional<size_t> prev_observation_id{};\n    double observation_value = std::numeric_limits<double>::signaling_NaN();\n    std::optional<Domain<Dim>> source_domain{};\n    domain::FunctionsOfTimeMap source_domain_functions_of_time{};\n    for (const std::string& file_name : file_paths) {\n      // Open the volume data file\n      h5::H5File<h5::AccessType::ReadOnly> h5file(file_name);\n      constexpr size_t version_number = 0;\n      const auto& volume_file = h5file.get<h5::VolumeData>(\n          \"/\" + get<OptionTags::Subgroup>(options), version_number);\n\n      // Select observation ID\n      const size_t observation_id = std::visit(\n          Overloader{\n              [&volume_file, &options](const double local_obs_value) {\n                const auto& observation_value_epsilon =\n                    get<OptionTags::ObservationValueEpsilon>(options);\n                return volume_file.find_observation_id(\n                    local_obs_value, observation_value_epsilon);\n              },\n              [&volume_file](const ObservationSelector local_obs_selector) {\n                const std::vector<size_t> all_observation_ids =\n                    volume_file.list_observation_ids();\n                switch (local_obs_selector) {\n                  case ObservationSelector::First:\n                    return all_observation_ids.front();\n                  case ObservationSelector::Last:\n                    return all_observation_ids.back();\n                  default:\n                    ERROR(\"Unknown importers::ObservationSelector: \"\n                          << local_obs_selector);\n                }\n              }},\n          get<OptionTags::ObservationValue>(options));\n      if (prev_observation_id.has_value() and\n          prev_observation_id.value() != observation_id) {\n        ERROR(\"Inconsistent selection of observation ID in file \"\n              << file_name\n              << \". Make sure all files select the same observation ID.\");\n      }\n      prev_observation_id = observation_id;\n      observation_value = volume_file.get_observation_value(observation_id);\n\n      // Memory buffer for the tensor data stored in this file. The data is\n      // loaded lazily when it is needed. We may find that we can skip loading\n      // some files because none of their data is needed to fill the elements on\n      // this node.\n      std::optional<tuples::tagged_tuple_from_typelist<FieldTagsList>>\n          all_tensor_data{};\n\n      // Retrieve the information needed to reconstruct which element the data\n      // belongs to\n      const auto source_grid_names = volume_file.get_grid_names(observation_id);\n      const auto source_extents = volume_file.get_extents(observation_id);\n      const auto source_bases = volume_file.get_bases(observation_id);\n      const auto source_quadratures =\n          volume_file.get_quadratures(observation_id);\n      std::vector<ElementId<Dim>> source_element_ids{};\n      if (not elements_are_identical) {\n        // Need to parse all source grid names to element IDs\n        source_element_ids.reserve(source_grid_names.size());\n        for (const auto& grid_name : source_grid_names) {\n          source_element_ids.push_back(ElementId<Dim>(grid_name));\n        }\n      }\n      // Reconstruct domain from volume data file\n      const std::optional<std::vector<char>> serialized_domain =\n          volume_file.get_domain();\n      if (serialized_domain.has_value()) {\n        if (source_domain.has_value()) {\n#ifdef SPECTRE_DEBUG\n          // Check that the domain is the same in all files (only in debug\n          // mode)\n          const auto deserialized_domain =\n              deserialize<Domain<Dim>>(serialized_domain->data());\n          if (*source_domain != deserialized_domain) {\n            ERROR_NO_TRACE(\n                \"The domain in all volume files must be the same. Domain in \"\n                \"file '\"\n                << file_name << volume_file.subfile_path()\n                << \"' differs from a previously read file.\");\n          }\n#endif\n        } else {\n          source_domain = deserialize<Domain<Dim>>(serialized_domain->data());\n        }\n      } else {\n        if (elements_are_identical) {\n          Parallel::printf(\n              \"WARNING: No serialized domain found in file. \"\n              \"Verification that elements in the source and target domain \"\n              \"match will be skipped.\\n\");\n        } else {\n          ERROR_NO_TRACE(\"No serialized domain found in file '\"\n                         << file_name << volume_file.subfile_path()\n                         << \"'. The domain is needed for interpolation.\");\n        }\n      }\n      // Reconstruct functions of time from volume data file\n      if (source_domain_functions_of_time.empty() and\n          source_domain.has_value() and\n          alg::any_of(source_domain->blocks(), [](const auto& block) {\n            return block.is_time_dependent();\n          })) {\n        const std::optional<std::vector<char>> serialized_functions_of_time =\n            volume_file.get_functions_of_time(observation_id);\n        if (not serialized_functions_of_time.has_value()) {\n          ERROR_NO_TRACE(\"No domain functions of time found in file '\"\n                         << file_name << volume_file.subfile_path()\n                         << \"'. The functions of time are needed for \"\n                            \"interpolating with time-dependent maps.\");\n        }\n        source_domain_functions_of_time =\n            deserialize<domain::FunctionsOfTimeMap>(\n                serialized_functions_of_time->data());\n      }\n\n      // Distribute the tensor data to the registered (target) elements. We\n      // erase target elements when they are complete. This allows us to\n      // search only for incomplete elements in subsequent volume files, and\n      // to stop early when all registered elements are complete.\n      std::unordered_set<ElementId<Dim>> completed_target_elements{};\n      for (const auto& target_element_id : target_element_ids) {\n        const auto& [target_points, target_mesh] =\n            get<Tags::RegisteredElements<Dim>>(box).at(\n                Parallel::make_array_component_id<ReceiveComponent>(\n                    target_element_id));\n        const auto target_grid_name = get_output(target_element_id);\n\n        // Proceed with the registered element only if it overlaps with the\n        // volume file. It's possible that the volume file only contains data\n        // for a subset of elements, e.g., when each node of a simulation\n        // wrote volume data for its elements to a separate file.\n        std::vector<ElementId<Dim>> overlapping_source_element_ids{};\n        std::unordered_map<ElementId<Dim>, ElementLogicalCoordHolder<Dim>>\n            source_element_logical_coords{};\n        if (not elements_are_identical) {\n          // Transform the target points to block logical coords in the source\n          // domain\n          const auto source_block_logical_coords = block_logical_coordinates(\n              *source_domain, target_points, observation_value,\n              source_domain_functions_of_time);\n          // Find the target points in the subset of source elements contained\n          // in this volume file\n          source_element_logical_coords = element_logical_coordinates(\n              source_element_ids, source_block_logical_coords);\n          overlapping_source_element_ids.reserve(\n              source_element_logical_coords.size());\n          for (const auto& source_element_id_and_coords :\n               source_element_logical_coords) {\n            overlapping_source_element_ids.push_back(\n                source_element_id_and_coords.first);\n          }\n        } else {\n          // When elements match we process only volume files that contain the\n          // exact element\n          if (std::find(source_grid_names.begin(), source_grid_names.end(),\n                        target_grid_name) == source_grid_names.end()) {\n            continue;\n          }\n          overlapping_source_element_ids.push_back(target_element_id);\n        }\n\n        // Lazily load the tensor data from the file if needed\n        if (not overlapping_source_element_ids.empty() and\n            not all_tensor_data.has_value()) {\n          all_tensor_data = detail::read_tensor_data<FieldTagsList>(\n              volume_file, observation_id, selected_fields);\n        }\n\n        // Iterate over the source elements in this volume file that overlap\n        // with the target element\n        for (const auto& source_element_id : overlapping_source_element_ids) {\n          const auto source_grid_name = get_output(source_element_id);\n          const auto source_mesh = h5::mesh_for_grid<Dim>(\n              source_grid_name, source_grid_names, source_extents, source_bases,\n              source_quadratures);\n          // Find the data offset that corresponds to this element\n          const auto element_data_offset_and_length =\n              h5::offset_and_length_for_grid(source_grid_name,\n                                             source_grid_names, source_extents);\n          // Extract this element's data from the read-in dataset\n          auto source_element_data =\n              detail::extract_element_data<FieldTagsList>(\n                  element_data_offset_and_length, *all_tensor_data,\n                  selected_fields);\n\n          if (not elements_are_identical) {\n            const size_t target_num_points = target_points.begin()->size();\n\n            // Get and resize target buffer\n            auto& target_element_data =\n                target_element_data_buffer[target_element_id];\n            tmpl::for_each<FieldTagsList>([&target_element_data,\n                                           &target_num_points,\n                                           &selected_fields](auto field_tag_v) {\n              using field_tag = tmpl::type_from<decltype(field_tag_v)>;\n              if (get<Tags::Selected<field_tag>>(selected_fields).has_value()) {\n                for (auto& component : get<field_tag>(target_element_data)) {\n                  component.destructive_resize(target_num_points);\n                }\n              }\n            });\n            auto& indices_of_filled_interp_points =\n                all_indices_of_filled_interp_points[target_element_id];\n\n            // Interpolate!\n            const auto& source_logical_coords_of_target_points =\n                source_element_logical_coords.at(source_element_id);\n            detail::interpolate_selected_fields<FieldTagsList>(\n                make_not_null(&target_element_data), source_element_data,\n                source_mesh,\n                source_logical_coords_of_target_points.element_logical_coords,\n                source_logical_coords_of_target_points.offsets,\n                selected_fields);\n            indices_of_filled_interp_points.insert(\n                indices_of_filled_interp_points.end(),\n                source_logical_coords_of_target_points.offsets.begin(),\n                source_logical_coords_of_target_points.offsets.end());\n\n            if (indices_of_filled_interp_points.size() == target_num_points) {\n              // Pass the (interpolated) data to the element. Now it can\n              // proceed in parallel with transforming the data, taking\n              // derivatives on the grid, etc.\n              if constexpr (Parallel::is_dg_element_collection_v<\n                                ReceiveComponent>) {\n                ERROR(\"Can't yet do numerical initial data with nodegroups\");\n              } else {\n                Parallel::receive_data<Tags::VolumeData<FieldTagsList>>(\n                    Parallel::get_parallel_component<ReceiveComponent>(\n                        cache)[target_element_id],\n                    volume_data_id, std::move(target_element_data));\n              }\n              completed_target_elements.insert(target_element_id);\n              target_element_data_buffer.erase(target_element_id);\n              all_indices_of_filled_interp_points.erase(target_element_id);\n            }\n          } else {\n            // Source and target element are the same (matching domains and\n            // same h-refinement), so no interpolation across elements is\n            // needed. We still may have to interpolate between different\n            // meshes (p-refinement). First, verify this assumption:\n            if (source_domain.has_value()) {\n              detail::verify_inertial_coordinates(\n                  *source_domain, observation_value,\n                  source_domain_functions_of_time, target_element_id,\n                  target_mesh, target_points);\n            }\n            tuples::tagged_tuple_from_typelist<FieldTagsList>\n                target_element_data{};\n            if (source_mesh == target_mesh) {\n              target_element_data = std::move(source_element_data);\n            } else {\n              detail::interpolate_selected_fields<FieldTagsList>(\n                  make_not_null(&target_element_data), source_element_data,\n                  source_mesh, target_mesh, selected_fields);\n            }\n            // Pass data directly to the target element\n            if constexpr (Parallel::is_dg_element_collection_v<\n                              ReceiveComponent>) {\n              ERROR(\"Can't yet do numerical initial data with nodegroups\");\n            } else {\n              Parallel::receive_data<Tags::VolumeData<FieldTagsList>>(\n                  Parallel::get_parallel_component<ReceiveComponent>(\n                      cache)[target_element_id],\n                  volume_data_id, std::move(target_element_data));\n            }\n            completed_target_elements.insert(target_element_id);\n          }\n        }  // loop over overlapping source elements\n      }  // loop over registered elements\n      for (const auto& completed_element_id : completed_target_elements) {\n        target_element_ids.erase(completed_element_id);\n      }\n      // Stop early when all target elements are complete\n      if (target_element_ids.empty()) {\n        break;\n      }\n    }  // loop over volume files\n\n    // Have we completed all target elements? If we haven't, the target domain\n    // probably extends outside the source domain. In that case we report the\n    // coordinates that couldn't be filled.\n    if (not target_element_ids.empty()) {\n      std::unordered_map<ElementId<Dim>, std::vector<std::array<double, Dim>>>\n          missing_coords{};\n      size_t num_missing_points = 0;\n      for (const auto& target_element_id : target_element_ids) {\n        const auto& target_inertial_coords =\n            get<Tags::RegisteredElements<Dim>>(box)\n                .at(Parallel::make_array_component_id<ReceiveComponent>(\n                    target_element_id))\n                .first;\n        const size_t target_num_points = target_inertial_coords.begin()->size();\n        const auto& indices_of_filled_interp_points =\n            all_indices_of_filled_interp_points[target_element_id];\n        for (size_t i = 0; i < target_num_points; ++i) {\n          if (alg::find(indices_of_filled_interp_points, i) ==\n              indices_of_filled_interp_points.end()) {\n            missing_coords[target_element_id].push_back(\n                [&target_inertial_coords, &i]() {\n                  std::array<double, Dim> x{};\n                  for (size_t d = 0; d < Dim; ++d) {\n                    x[d] = target_inertial_coords[d][i];\n                  }\n                  return x;\n                }());\n            ++num_missing_points;\n          }\n        }\n      }\n      ERROR_NO_TRACE(\"The following \" << num_missing_points << \" point(s) in \"\n                                      << missing_coords.size()\n                                      << \" element(s) could not be filled:\\n\"\n                                      << missing_coords);\n    }\n  }\n\n private:\n  template <typename... LocalFieldTags>\n  static tuples::TaggedTuple<Tags::Selected<LocalFieldTags>...>\n  select_all_fields(tmpl::list<LocalFieldTags...> /*meta*/) {\n    return {db::tag_name<LocalFieldTags>()...};\n  }\n};\n\n}  // namespace Actions\n}  // namespace importers\n"
  },
  {
    "path": "src/IO/Importers/Actions/ReceiveVolumeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace importers::Actions {\n\n/*!\n * \\brief Wait for data from a volume data file to arrive and directly move it\n * into the DataBox\n *\n * Monitors `importers::Tags::VolumeData` in the element's inbox and moves the\n * received data directly into the `FieldTagsList` in the DataBox.\n *\n * \\see Dev guide on \\ref dev_guide_importing\n */\ntemplate <typename FieldTagsList>\nstruct ReceiveVolumeData {\n  using inbox_tags = tmpl::list<Tags::VolumeData<FieldTagsList>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox = tuples::get<Tags::VolumeData<FieldTagsList>>(inboxes);\n    // Using `0` for the temporal ID since we only read the volume data once, so\n    // there's no need to keep track of the temporal ID.\n    const auto received_data = inbox.find(0_st);\n    if (received_data == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    auto& element_data = received_data->second;\n    tmpl::for_each<FieldTagsList>([&box, &element_data](auto tag_v) {\n      using tag = tmpl::type_from<decltype(tag_v)>;\n      db::mutate<tag>(\n          [&element_data](const gsl::not_null<typename tag::type*> value) {\n            *value = std::move(tuples::get<tag>(element_data));\n          },\n          make_not_null(&box));\n    });\n    inbox.erase(received_data);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace importers::Actions\n"
  },
  {
    "path": "src/IO/Importers/Actions/RegisterWithElementDataReader.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace importers {\ntemplate <typename Metavariables>\nstruct ElementDataReader;\nnamespace Actions {\nstruct RegisterElementWithSelf;\n}  // namespace Actions\n}  // namespace importers\nnamespace evolution::dg::subcell {\ntemplate <typename DgTag, typename SubcellTag, typename DbTagsList>\nconst typename DgTag::type& get_active_tag(const db::DataBox<DbTagsList>& box);\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\ntemplate <size_t Dim>\nstruct Mesh;\nstruct ActiveGrid;\n}  // namespace Tags\n}  // namespace evolution::dg::subcell\n/// \\endcond\n\nnamespace importers::Actions {\n\n/*!\n * \\brief Register an element with the volume data reader component.\n *\n * Invoke this action on each element of an array parallel component to register\n * them for receiving imported volume data.\n *\n * \\note If the tags `evolution::dg::subcell::Tags::ActiveGrid` and\n * `evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Inertial>` are\n * retrievable from the DataBox, then interpolation to the FD/subcell grid is\n * possible.\n *\n * \\see Dev guide on \\ref dev_guide_importing\n */\nstruct RegisterWithElementDataReader {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& local_reader_component = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            importers::ElementDataReader<Metavariables>>(cache));\n    auto coords_and_mesh = [&box]() {\n      if constexpr (db::tag_is_retrievable_v<\n                        evolution::dg::subcell::Tags::ActiveGrid,\n                        db::DataBox<DbTagsList>> and\n                    db::tag_is_retrievable_v<\n                        evolution::dg::subcell::Tags::Coordinates<\n                            Dim, Frame::Inertial>,\n                        db::DataBox<DbTagsList>>) {\n        return std::make_pair(\n            evolution::dg::subcell::get_active_tag<\n                domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                evolution::dg::subcell::Tags::Coordinates<Dim,\n                                                          Frame::Inertial>>(\n                box),\n            evolution::dg::subcell::get_active_tag<\n                domain::Tags::Mesh<Dim>,\n                evolution::dg::subcell::Tags::Mesh<Dim>>(box));\n      } else {\n        return std::make_pair(\n            db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box),\n            db::get<domain::Tags::Mesh<Dim>>(box));\n      }\n    }();\n    Parallel::simple_action<importers::Actions::RegisterElementWithSelf>(\n        local_reader_component,\n        Parallel::make_array_component_id<ParallelComponent>(array_index),\n        std::move(coords_and_mesh));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Invoked on the `importers::ElementDataReader` component to store the\n * registered data.\n *\n * The `importers::Actions::RegisterWithElementDataReader` action, which is\n * performed on each element of an array parallel component, invokes this action\n * on the `importers::ElementDataReader` component.\n *\n * \\see Dev guide on \\ref dev_guide_importing\n */\nstruct RegisterElementWithSelf {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, size_t Dim>\n  static void apply(\n      db::DataBox<DbTagsList>& box,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const Parallel::ArrayComponentId& array_component_id,\n      std::pair<tnsr::I<DataVector, Dim, Frame::Inertial>, Mesh<Dim>>\n          coords_and_mesh) {\n    db::mutate<Tags::RegisteredElements<Dim>>(\n        [&array_component_id,\n         &coords_and_mesh](const auto registered_elements) {\n          (*registered_elements)[array_component_id] =\n              std::move(coords_and_mesh);\n        },\n        make_not_null(&box));\n  }\n};\n\n}  // namespace importers::Actions\n"
  },
  {
    "path": "src/IO/Importers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Importers)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ObservationSelector.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ElementDataReader.hpp\n  ObservationSelector.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ErrorHandling\n  H5\n  Observer\n  Options\n  INTERFACE\n  DataStructures\n  Domain\n  DomainStructure\n  Initialization\n  Interpolation\n  Parallel\n  Utilities\n  )\n\nadd_subdirectory(Actions)\n"
  },
  {
    "path": "src/IO/Importers/ElementDataReader.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <string>\n#include <unordered_set>\n\n#include \"IO/Importers/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n\nnamespace importers {\n\nnamespace detail {\ntemplate <size_t Dim>\nstruct InitializeElementDataReader;\n}  // namespace detail\n\n/*!\n * \\brief A nodegroup parallel component that reads in a volume data file and\n * distributes its data to elements of an array parallel component.\n *\n * Each element of the array parallel component must register itself before\n * data can be sent to it. To do so, invoke\n * `importers::Actions::RegisterWithElementDataReader` on each element. In a\n * subsequent phase you can then invoke\n * `importers::ThreadedActions::ReadVolumeData` on the `ElementDataReader`\n * component to read in the file and distribute its data to the registered\n * elements.\n *\n * \\see Dev guide on \\ref dev_guide_importing\n */\ntemplate <typename Metavariables>\nstruct ElementDataReader {\n  static constexpr size_t Dim = Metavariables::volume_dim;\n\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = false;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<detail::InitializeElementDataReader<Dim>>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void initialize(\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ElementDataReader>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\nnamespace detail {\ntemplate <size_t Dim>\nstruct InitializeElementDataReader {\n  using simple_tags =\n      tmpl::list<Tags::RegisteredElements<Dim>, Tags::ElementDataAlreadyRead>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n}  // namespace detail\n\n}  // namespace importers\n"
  },
  {
    "path": "src/IO/Importers/ObservationSelector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Importers/ObservationSelector.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace importers {\nstd::ostream& operator<<(std::ostream& os, const ObservationSelector value) {\n  switch (value) {\n    case ObservationSelector::First:\n      return os << \"First\";\n    case ObservationSelector::Last:\n      return os << \"Last\";\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Unknown importers::ObservationSelector\");\n      // LCOV_EXCL_STOP\n  }\n}\n}  // namespace importers\n\ntemplate <>\nimporters::ObservationSelector\nOptions::create_from_yaml<importers::ObservationSelector>::create<void>(\n    const Options::Option& options) {\n  const auto value = options.parse_as<std::string>();\n  if (value == \"First\") {\n    return importers::ObservationSelector::First;\n  } else if (value == \"Last\") {\n    return importers::ObservationSelector::Last;\n  }\n  PARSE_ERROR(options.context(), \"Failed to convert '\"\n                                     << value\n                                     << \"' to importers::ObservationSelector. \"\n                                        \"Must be one of First, Last.\");\n}\n"
  },
  {
    "path": "src/IO/Importers/ObservationSelector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\n/// \\cond\nnamespace Options {\nstruct Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace importers {\n\n/// Represents the first or last observation in a volume data file, to allow\n/// specifying it in an input file without knowledge of the specific observation\n/// values.\nenum class ObservationSelector { First, Last };\n\nstd::ostream& operator<<(std::ostream& os, const ObservationSelector value);\n\n}  // namespace importers\n\ntemplate <>\nstruct Options::create_from_yaml<importers::ObservationSelector> {\n  template <typename Metavariables>\n  static importers::ObservationSelector create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nimporters::ObservationSelector\nOptions::create_from_yaml<importers::ObservationSelector>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/IO/Importers/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iomanip>\n#include <map>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <variant>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"IO/Importers/ObservationSelector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\n/// Items related to loading data from files\nnamespace importers {\n\n/// The input file options associated with the data importer\nnamespace OptionTags {\n\n/*!\n * \\brief The file to read data from.\n */\nstruct FileGlob {\n  using type = std::string;\n  static constexpr Options::String help = \"Path to the data file\";\n};\n\n/*!\n * \\brief The subgroup within the file to read data from.\n *\n * This subgroup should conform to the `h5::VolumeData` format.\n */\nstruct Subgroup {\n  using type = std::string;\n  static constexpr Options::String help =\n      \"The subgroup within the file, excluding extensions\";\n};\n\n/*!\n * \\brief The observation value at which to read data from the file.\n */\nstruct ObservationValue {\n  using type = std::variant<double, ObservationSelector>;\n  static constexpr Options::String help =\n      \"The observation value at which to read data\";\n};\n\nstruct ObservationValueEpsilon {\n  using type = Options::Auto<double>;\n  static constexpr Options::String help =\n      \"Look for observations in the data within this epsilon of the \"\n      \"'ObservationValue'. Set to 'Auto' to use default of 1e-12. This option \"\n      \"is ignored if the 'ObservationValue' is a selector like 'First' or \"\n      \"'Last'.\";\n};\n\n/*!\n * \\brief Toggle interpolation of numeric data to the target domain\n */\nstruct ElementsAreIdentical {\n  using type = bool;\n  static constexpr Options::String help =\n      \"Indicate that the elements of the source and target domain are the \"\n      \"same, meaning that the domains are the same and their h-refinement is \"\n      \"the same. In this case, data can be transferred between the source and \"\n      \"target elements one-to-one, and interpolations only happen if the \"\n      \"elements have different meshes (p-refinement). \"\n      \"For example, you can enable this option if you have generated data \"\n      \"on the target points, or if you have already interpolated your data, \"\n      \"or if you import data from a simulation that differs only by \"\n      \"p-refinement. \"\n      \"When this option is enabled, datasets \"\n      \"'InertialCoordinates(_x,_y,_z)' must exist in the files. They are used \"\n      \"to verify that the target points indeed match the source data.\";\n};\n}  // namespace OptionTags\n\n/// Options that specify the volume data to load. See the option tags for\n/// details.\nstruct ImporterOptions\n    : tuples::TaggedTuple<OptionTags::FileGlob, OptionTags::Subgroup,\n                          OptionTags::ObservationValue,\n                          OptionTags::ObservationValueEpsilon,\n                          OptionTags::ElementsAreIdentical> {\n  using options = tags_list;\n  static constexpr Options::String help = \"The volume data to load.\";\n  using TaggedTuple::TaggedTuple;\n};\n\n/// The \\ref DataBoxGroup tags associated with the data importer\nnamespace Tags {\n\n/// Options that specify the volume data to load. See the option tags for\n/// details.\ntemplate <typename OptionsGroup>\nstruct ImporterOptions : db::SimpleTag {\n  static std::string name() { return \"VolumeData\"; }\n  using type = importers::ImporterOptions;\n  static constexpr Options::String help = importers::ImporterOptions::help;\n  using group = OptionsGroup;\n  using option_tags = tmpl::list<ImporterOptions>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(type value) { return value; }\n};\n\n/*!\n * \\brief The elements that will receive data from the importer.\n *\n * \\details Identifiers for elements from multiple parallel components can be\n * stored. Each element is identified by an `Parallel::ArrayComponentId` and\n * also needs to provide the inertial coordinates of its grid points. The\n * imported data will be interpolated to these grid points.\n */\ntemplate <size_t Dim>\nstruct RegisteredElements : db::SimpleTag {\n  using type = std::unordered_map<\n      Parallel::ArrayComponentId,\n      std::pair<tnsr::I<DataVector, Dim, Frame::Inertial>, ::Mesh<Dim>>>;\n};\n\n/// Indicates which volume data files have already been read.\nstruct ElementDataAlreadyRead : db::SimpleTag {\n  using type = std::unordered_set<size_t>;\n};\n\n/*!\n * \\brief Inbox tag that carries the data read from a volume data file.\n *\n * Since we read a volume data file only once, this tag's map will only ever\n * hold data at the index (i.e. the temporal ID) with value `0`.\n */\ntemplate <typename FieldTagsList>\nstruct VolumeData : Parallel::InboxInserters::Value<VolumeData<FieldTagsList>> {\n  using temporal_id = size_t;\n  using type =\n      std::map<temporal_id, tuples::tagged_tuple_from_typelist<FieldTagsList>>;\n\n  static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n\n    ss << std::scientific << std::setprecision(16);\n    ss << pad << \"VolumeDataInbox:\\n\";\n    // We don't really care about the variables, just the times\n    for (const auto& [index, variables] : inbox) {\n      (void)variables;\n      ss << pad << \" Index: \" << index << \"\\n\";\n    }\n\n    return ss.str();\n  }\n};\n}  // namespace Tags\n\n}  // namespace importers\n"
  },
  {
    "path": "src/IO/Logging/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Logging)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Verbosity.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Tags.hpp\n  Verbosity.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Options\n  PRIVATE\n  ErrorHandling\n  INTERFACE\n  DataStructures\n  )\n"
  },
  {
    "path": "src/IO/Logging/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\n/// \\cond\nenum class Verbosity;\n/// \\endcond\n\n/// \\ingroup LoggingGroup\n/// Items related to logging\nnamespace logging {\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup LoggingGroup\ntemplate <typename OptionsGroup>\nstruct Verbosity {\n  using type = ::Verbosity;\n  static constexpr Options::String help{\"Verbosity\"};\n  using group = OptionsGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\ingroup LoggingGroup\n/// \\brief Tag for putting `::Verbosity` in a DataBox.\ntemplate <typename OptionsGroup>\nstruct Verbosity : db::SimpleTag {\n  using type = ::Verbosity;\n  static std::string name() {\n    return \"Verbosity(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n\n  using option_tags = tmpl::list<OptionTags::Verbosity<OptionsGroup>>;\n  static constexpr bool pass_metavariables = false;\n  static ::Verbosity create_from_options(const ::Verbosity& verbosity) {\n    return verbosity;\n  }\n};\n}  // namespace Tags\n}  // namespace logging\n"
  },
  {
    "path": "src/IO/Logging/Verbosity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Logging/Verbosity.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\ntemplate <>\nVerbosity Options::create_from_yaml<Verbosity>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (\"Silent\" == type_read) {\n    return Verbosity::Silent;\n  } else if (\"Quiet\" == type_read) {\n    return Verbosity::Quiet;\n  } else if (\"Verbose\" == type_read) {\n    return Verbosity::Verbose;\n  } else if (\"Debug\" == type_read) {\n    return Verbosity::Debug;\n  }\n  PARSE_ERROR(options.context(), \"Failed to convert \\\"\"\n                                     << type_read\n                                     << \"\\\" to Verbosity. Must be one \"\n                                        \"of Silent, Quiet, Verbose, or Debug.\");\n}\n\nstd::ostream& operator<<(std::ostream& os, const Verbosity& verbosity) {\n  switch (verbosity) {\n    case Verbosity::Silent:\n      return os << \"Silent\";\n    case Verbosity::Quiet:\n      return os << \"Quiet\";\n    case Verbosity::Verbose:\n      return os << \"Verbose\";\n    case Verbosity::Debug:\n      return os << \"Debug\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Need to add another case, don't understand value of 'verbosity'\");\n      // LCOV_EXCL_STOP\n  }\n}\n"
  },
  {
    "path": "src/IO/Logging/Verbosity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\n/// \\ingroup LoggingGroup\n/// \\brief Indicates how much informative output a class should output.\nenum class Verbosity { Silent, Quiet, Verbose, Debug };\n\nstd::ostream& operator<<(std::ostream& os, const Verbosity& verbosity);\n\ntemplate <>\nstruct Options::create_from_yaml<Verbosity> {\n  template <typename Metavariables>\n  static Verbosity create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nVerbosity Options::create_from_yaml<Verbosity>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/IO/Observer/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GetLockPointer.hpp\n  ObserverRegistration.hpp\n  RegisterEvents.hpp\n  RegisterSingleton.hpp\n  RegisterWithObservers.hpp\n  )\n"
  },
  {
    "path": "src/IO/Observer/Actions/GetLockPointer.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <mutex>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace observers::Actions {\n\n/// Local synchronous action for retrieving a pointer to the `NodeLock` with tag\n/// `LockTag` on the component.\n///\n/// \\warning The retrieved pointer to a lock must only be treated as 'good'\n/// during the execution of the action from which this synchronous action is\n/// called. This is because we can only trust that charm will not migrate the\n/// component on which the action is running until after the action has\n/// completed, and it will not migrate the Nodegroup to which the lock points\n/// until a checkpoint.\ntemplate <typename LockTag>\nstruct GetLockPointer {\n  using return_type = Parallel::NodeLock*;\n\n  template <typename ParallelComponent, typename DbTagList>\n  static return_type apply(db::DataBox<DbTagList>& box,\n                           const gsl::not_null<Parallel::NodeLock*> node_lock) {\n    Parallel::NodeLock* result_lock;\n    const std::lock_guard hold_lock(*node_lock);\n    db::mutate<LockTag>(\n        [&result_lock](const gsl::not_null<Parallel::NodeLock*> lock) {\n          result_lock = lock;\n        },\n        make_not_null(&box));\n    return result_lock;\n  }\n};\n}  // namespace observers::Actions\n"
  },
  {
    "path": "src/IO/Observer/Actions/ObserverRegistration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <set>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace observers {\n/*!\n * \\ingroup ObserversGroup\n * \\brief %Actions used by the observer parallel component\n */\nnamespace Actions {\n/// \\brief Register an `ArrayComponentId` with a specific\n/// `ObservationIdRegistrationKey` that will call\n/// `observers::ThreadedActions::ContributeVolumeData`.\n///\n/// Should be invoked on ObserverWriter by the component that will be\n/// contributing the data.\nstruct RegisterVolumeContributorWithObserverWriter {\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const Parallel::ArrayComponentId& id_of_caller) {\n    db::mutate<Tags::ExpectedContributorsForObservations>(\n        [&id_of_caller,\n         &observation_key](const gsl::not_null<std::unordered_map<\n                               ObservationKey,\n                               std::unordered_set<Parallel::ArrayComponentId>>*>\n                               volume_observers_registered) {\n          if (volume_observers_registered->find(observation_key) ==\n              volume_observers_registered->end()) {\n            (*volume_observers_registered)[observation_key] =\n                std::unordered_set<Parallel::ArrayComponentId>{};\n          }\n\n          if (UNLIKELY(\n                  volume_observers_registered->at(observation_key)\n                      .find(id_of_caller) !=\n                  volume_observers_registered->at(observation_key).end())) {\n            ERROR(\"Trying to insert a Observer component more than once: \"\n                  << id_of_caller);\n          }\n\n          volume_observers_registered->at(observation_key).insert(id_of_caller);\n        },\n        make_not_null(&box));\n  }\n};\n\n/// \\brief Deregister an `ArrayComponentId` with a specific\n/// `ObservationIdRegistrationKey` that will no longer call\n/// `observers::ThreadedActions::ContributeVolumeData`\n///\n/// Should be invoked on ObserverWriter by the component that was previously\n/// registered with\n/// `observers::Actions::RegisterVolumeContributorWithObserverWriter`\nstruct DeregisterVolumeContributorWithObserverWriter {\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const Parallel::ArrayComponentId& id_of_caller) {\n    db::mutate<Tags::ExpectedContributorsForObservations>(\n        [&id_of_caller,\n         &observation_key](const gsl::not_null<std::unordered_map<\n                               ObservationKey,\n                               std::unordered_set<Parallel::ArrayComponentId>>*>\n                               volume_observers_registered) {\n          if (UNLIKELY(volume_observers_registered->find(observation_key) ==\n                       volume_observers_registered->end())) {\n            ERROR(\n                \"Trying to deregister a component associated with an \"\n                \"unregistered observation key: \"\n                << observation_key);\n          }\n\n          if (UNLIKELY(\n                  volume_observers_registered->at(observation_key)\n                      .find(id_of_caller) ==\n                  volume_observers_registered->at(observation_key).end())) {\n            ERROR(\"Trying to deregister an unregistered component: \"\n                  << id_of_caller);\n          }\n\n          volume_observers_registered->at(observation_key).erase(id_of_caller);\n          if (UNLIKELY(\n                  volume_observers_registered->at(observation_key).size() ==\n                  0)) {\n            volume_observers_registered->erase(observation_key);\n          }\n        },\n        make_not_null(&box));\n  }\n};\n\n/*!\n * \\brief Register a node with the node that writes the reduction data to disk.\n */\nstruct RegisterReductionNodeWithWritingNode {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const size_t caller_node_id) {\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    const auto node_id =\n        Parallel::my_node<size_t>(*Parallel::local_branch(my_proxy));\n    ASSERT(node_id == 0, \"Only node zero, not node \"\n                             << node_id\n                             << \", should be called from another node\");\n\n    db::mutate<Tags::NodesExpectedToContributeReductions>(\n        [&caller_node_id, &observation_key](\n            const gsl::not_null<\n                std::unordered_map<ObservationKey, std::set<size_t>>*>\n                reduction_observers_registered_nodes) {\n          if (reduction_observers_registered_nodes->find(observation_key) ==\n              reduction_observers_registered_nodes->end()) {\n            (*reduction_observers_registered_nodes)[observation_key] =\n                std::set<size_t>{};\n          }\n          auto& registered_nodes_for_key =\n              reduction_observers_registered_nodes->at(observation_key);\n          if (UNLIKELY(registered_nodes_for_key.find(caller_node_id) !=\n                       registered_nodes_for_key.end())) {\n            ERROR(\"Already registered node \" << caller_node_id\n                                             << \" for reduction observations.\");\n          }\n          registered_nodes_for_key.insert(caller_node_id);\n        },\n        make_not_null(&box));\n  }\n};\n\n/*!\n * \\brief Deregister a node with the node that writes the reduction data to\n * disk.\n */\nstruct DeregisterReductionNodeWithWritingNode {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const size_t caller_node_id) {\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    const auto node_id =\n        Parallel::my_node<size_t>(*Parallel::local_branch(my_proxy));\n    ASSERT(node_id == 0,\n           \"Only node zero, not node \"\n               << node_id << \" should deregister other nodes in the reduction\");\n\n    db::mutate<Tags::NodesExpectedToContributeReductions>(\n        [&caller_node_id, &observation_key](\n            const gsl::not_null<\n                std::unordered_map<ObservationKey, std::set<size_t>>*>\n                reduction_observers_registered_nodes) {\n          if (UNLIKELY(\n                  reduction_observers_registered_nodes->find(observation_key) ==\n                  reduction_observers_registered_nodes->end())) {\n            ERROR(\n                \"Trying to deregister a node associated with an unregistered \"\n                \"observation key: \"\n                << observation_key);\n          }\n          auto& registered_nodes_for_key =\n              reduction_observers_registered_nodes->at(observation_key);\n          if (UNLIKELY(registered_nodes_for_key.find(caller_node_id) ==\n                       registered_nodes_for_key.end())) {\n            ERROR(\"Trying to deregister an unregistered node: \"\n                  << caller_node_id);\n          }\n          registered_nodes_for_key.erase(caller_node_id);\n          if (UNLIKELY(registered_nodes_for_key.size() == 0)) {\n            reduction_observers_registered_nodes->erase(observation_key);\n          }\n        },\n        make_not_null(&box));\n  }\n};\n\n/// \\brief Register an `ArrayComponentId` that will call\n/// `observers::ThreadedActions::WriteReductionData` or\n/// `observers::ThreadedActions::ContributeReductionData` for a specific\n/// `ObservationIdRegistrationKey`\n///\n/// Should be invoked on ObserverWriter by the component that will be\n/// contributing the data.\nstruct RegisterReductionContributorWithObserverWriter {\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const Parallel::ArrayComponentId& id_of_caller) {\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    const auto node_id =\n        Parallel::my_node<size_t>(*Parallel::local_branch(my_proxy));\n    db::mutate<Tags::ExpectedContributorsForObservations>(\n        [&cache, &id_of_caller, &node_id,\n         &observation_key](const gsl::not_null<std::unordered_map<\n                               ObservationKey,\n                               std::unordered_set<Parallel::ArrayComponentId>>*>\n                               reduction_observers_registered) {\n          if (reduction_observers_registered->find(observation_key) ==\n              reduction_observers_registered->end()) {\n            (*reduction_observers_registered)[observation_key] =\n                std::unordered_set<Parallel::ArrayComponentId>{};\n            Parallel::simple_action<\n                Actions::RegisterReductionNodeWithWritingNode>(\n                Parallel::get_parallel_component<ObserverWriter<Metavariables>>(\n                    cache)[0],\n                observation_key, node_id);\n          }\n\n          if (LIKELY(\n                  reduction_observers_registered->at(observation_key)\n                      .find(id_of_caller) ==\n                  reduction_observers_registered->at(observation_key).end())) {\n            reduction_observers_registered->at(observation_key)\n                .insert(id_of_caller);\n          } else {\n            ERROR(\"Trying to insert a Observer component more than once: \"\n                  << id_of_caller\n                  << \" with observation key: \" << observation_key);\n          }\n        },\n        make_not_null(&box));\n  }\n};\n\n/// \\brief Deregister an `ArrayComponentId` that will no longer call\n/// `observers::ThreadedActions::WriteReductionData` or\n/// `observers::ThreadedActions::ContributeReductionData` for a specific\n/// `ObservationIdRegistrationKey`\n///\n/// Should be invoked on ObserverWriter by the component that was previously\n/// registered by\n/// `observers::Actions::RegisterReductionContributorWithObserverWriter`.\nstruct DeregisterReductionContributorWithObserverWriter {\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const Parallel::ArrayComponentId& id_of_caller) {\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    const auto node_id =\n        Parallel::my_node<size_t>(*Parallel::local_branch(my_proxy));\n    db::mutate<Tags::ExpectedContributorsForObservations>(\n        [&cache, &id_of_caller, &node_id,\n         &observation_key](const gsl::not_null<std::unordered_map<\n                               ObservationKey,\n                               std::unordered_set<Parallel::ArrayComponentId>>*>\n                               reduction_observers_registered) {\n          if (UNLIKELY(reduction_observers_registered->find(observation_key) ==\n                       reduction_observers_registered->end())) {\n            ERROR(\n                \"Trying to deregister a component associated with an \"\n                \"unregistered observation key: \"\n                << observation_key);\n          }\n          auto& contributors_for_key =\n              reduction_observers_registered->at(observation_key);\n          if (UNLIKELY(contributors_for_key.find(id_of_caller) ==\n                       contributors_for_key.end())) {\n            ERROR(\"Trying to deregister an unregistered component: \"\n                  << id_of_caller\n                  << \" with observation key: \" << observation_key);\n          }\n          contributors_for_key.erase(id_of_caller);\n          if (UNLIKELY(contributors_for_key.empty())) {\n            Parallel::simple_action<\n                Actions::DeregisterReductionNodeWithWritingNode>(\n                Parallel::get_parallel_component<ObserverWriter<Metavariables>>(\n                    cache)[0],\n                observation_key, node_id);\n            reduction_observers_registered->erase(observation_key);\n          }\n        },\n        make_not_null(&box));\n  }\n};\n\n/*!\n * \\brief Register the `ArrayComponentId` that will send the data to the\n * observer for the given `ObservationIdRegistrationKey`\n *\n * Should be invoked on the `Observer` by the contributing component.\n */\nstruct RegisterContributorWithObserver {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index,\n                    const observers::ObservationKey& observation_key,\n                    const Parallel::ArrayComponentId& component_id,\n                    const TypeOfObservation& type_of_observation) {\n    bool observation_key_already_registered = true;\n    db::mutate<observers::Tags::ExpectedContributorsForObservations>(\n        [&component_id, &observation_key, &observation_key_already_registered](\n            const gsl::not_null<std::unordered_map<\n                ObservationKey,\n                std::unordered_set<Parallel::ArrayComponentId>>*>\n                array_component_ids) {\n          observation_key_already_registered =\n              (array_component_ids->find(observation_key) !=\n               array_component_ids->end());\n          if (UNLIKELY(\n                  observation_key_already_registered and\n                  array_component_ids->at(observation_key).find(component_id) !=\n                      array_component_ids->at(observation_key).end())) {\n            ERROR(\n                \"Trying to insert a component_id more than once for \"\n                \"observation. This means an element is registering itself \"\n                \"with the observers more than once. The component_id is \"\n                << component_id << \" and the observation key is \"\n                << observation_key);\n          }\n          array_component_ids->operator[](observation_key).insert(component_id);\n        },\n        make_not_null(&box));\n\n    if (observation_key_already_registered) {\n      // We only need to register with the observer writer on the first call\n      // of this action. Later calls will have already been registered.\n      return;\n    }\n\n    auto& observer_writer = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            observers::ObserverWriter<Metavariables>>(cache));\n\n    switch (type_of_observation) {\n      case TypeOfObservation::Reduction:\n        Parallel::simple_action<\n            Actions::RegisterReductionContributorWithObserverWriter>(\n            observer_writer, observation_key,\n            Parallel::make_array_component_id<ParallelComponent>(array_index));\n        return;\n      case TypeOfObservation::Volume:\n        Parallel::simple_action<\n            Actions::RegisterVolumeContributorWithObserverWriter>(\n            observer_writer, observation_key,\n            Parallel::make_array_component_id<ParallelComponent>(array_index));\n        return;\n      default:\n        ERROR(\n            \"Registering an unknown TypeOfObservation. Should be one of \"\n            \"'Reduction' or 'Volume'\");\n    };\n  }\n};\n\n/*!\n * \\brief Deregister the `ArrayComponentId` that will no longer send the data to\n * the observer for the given `ObservationIdRegistrationKey`\n *\n * Should be invoked on the `Observer` by the component that was previously\n * registered with `observers::Actions::RegisterContributorWithObserver`.\n */\nstruct DeregisterContributorWithObserver {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index,\n                    const observers::ObservationKey& observation_key,\n                    const Parallel::ArrayComponentId& component_id,\n                    const TypeOfObservation& type_of_observation) {\n    bool all_array_components_have_been_deregistered = false;\n    db::mutate<observers::Tags::ExpectedContributorsForObservations>(\n        [&component_id, &observation_key,\n         &all_array_components_have_been_deregistered](\n            const gsl::not_null<std::unordered_map<\n                ObservationKey,\n                std::unordered_set<Parallel::ArrayComponentId>>*>\n                array_component_ids) {\n          if (UNLIKELY(array_component_ids->find(observation_key) ==\n                       array_component_ids->end())) {\n            ERROR(\n                \"Trying to deregister a component associated with an \"\n                \"unregistered observation key: \"\n                << observation_key);\n          }\n          auto& component_ids_for_key =\n              array_component_ids->at(observation_key);\n          if (UNLIKELY(component_ids_for_key.find(component_id) ==\n                       array_component_ids->at(observation_key).end())) {\n            ERROR(\"Trying to deregister an unregistered component: \"\n                  << component_id\n                  << \" with observation key: \" << observation_key);\n          }\n          component_ids_for_key.erase(component_id);\n          if (UNLIKELY(component_ids_for_key.size() == 0)) {\n            array_component_ids->erase(observation_key);\n            all_array_components_have_been_deregistered = true;\n          }\n        },\n        make_not_null(&box));\n\n    if (not all_array_components_have_been_deregistered) {\n      // Only deregister with the observer writer if this deregistration\n      // removes the last component for the provided `observation_key`.\n      return;\n    }\n\n    auto& observer_writer = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            observers::ObserverWriter<Metavariables>>(cache));\n\n    switch (type_of_observation) {\n      case TypeOfObservation::Reduction:\n        Parallel::simple_action<\n            Actions::DeregisterReductionContributorWithObserverWriter>(\n            observer_writer, observation_key,\n            Parallel::make_array_component_id<ParallelComponent>(array_index));\n        return;\n      case TypeOfObservation::Volume:\n        Parallel::simple_action<\n            Actions::DeregisterVolumeContributorWithObserverWriter>(\n            observer_writer, observation_key,\n            Parallel::make_array_component_id<ParallelComponent>(array_index));\n        return;\n      default:\n        ERROR(\n            \"Attempting to deregister an unknown TypeOfObservation. \"\n            \"Should be one of 'Reduction' or 'Volume'\");\n    };\n  }\n};\n}  // namespace Actions\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/Actions/RegisterEvents.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Protocols/ElementRegistrar.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n\nnamespace observers {\nnamespace detail {\nCREATE_HAS_TYPE_ALIAS(observation_registration_tags)\nCREATE_HAS_TYPE_ALIAS_V(observation_registration_tags)\n}  // namespace detail\n\n/*!\n * \\brief Retrieves the observation type and key from an event, if it is an\n * event that needs to register with the observers.\n *\n * The event must define an `observation_registration_tags` type alias that is a\n * `tmpl::list<>` of all the tags from the DataBox needed to construct the\n * `ObservationKey`, and a `get_observation_type_and_key_for_registration`\n * member function if it is to be registered automatically.\n */\ntemplate <typename DbTagsList>\nstd::optional<\n    std::pair<::observers::TypeOfObservation, ::observers::ObservationKey>>\nget_registration_observation_type_and_key(const Event& event,\n                                          const db::DataBox<DbTagsList>& box) {\n  std::optional<\n      std::pair<::observers::TypeOfObservation, ::observers::ObservationKey>>\n      result{};\n  bool already_registered = false;\n  using factory_classes =\n      typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n          box))>::factory_creation::factory_classes;\n  tmpl::for_each<tmpl::at<factory_classes, Event>>(\n      [&already_registered, &box, &event, &result](auto event_type_v) {\n        using EventType = typename decltype(event_type_v)::type;\n        if constexpr (detail::has_observation_registration_tags_v<EventType>) {\n          // We require that each event for which\n          // `has_observation_registration_tags_v` is true be downcastable to\n          // only *one* event type. This is checked by the `already_registered`\n          // bool and the error message below. We need to downcast because we do\n          // not want to impose that every event be registerable with the\n          // IO/observer components. `dynamic_cast` returns a `nullptr` in the\n          // case that the downcast failed, which we check for to see if we can\n          // call the `get_observation_type_and_key_for_registration` function.\n          const auto* const derived_class_ptr =\n              dynamic_cast<const EventType*>(&event);\n          if (derived_class_ptr != nullptr) {\n            if (already_registered) {\n              ERROR(\n                  \"Already registered the event by casting down to a \"\n                  \"different Event derived class. This means you have an \"\n                  \"Event where A inherits from B and both A and B define \"\n                  \"get_observation_type_and_id_for_registration. This \"\n                  \"behavior is not supported. Please make a separate Event.\");\n            }\n            already_registered = true;\n            result =\n                db::apply<typename EventType::observation_registration_tags>(\n                    [&derived_class_ptr](const auto&... args) {\n                      return derived_class_ptr\n                          ->get_observation_type_and_key_for_registration(\n                              args...);\n                    },\n                    box);\n          }\n        }\n      });\n  return result;\n}\n\nnamespace Actions {\n/*!\n * \\brief Registers this element of a parallel component with the local\n * `Observer` parallel component for each triggered observation.\n *\n * \\details This tells the `Observer` to expect data from this component, as\n * well as whether each observation is a Reduction or Volume observation.\n * Should be added to the phase dependent action list of the components that\n * contribute data for volume and reduction observations.\n *\n * When this struct is used as an action, the `apply` function will perform the\n * registration with observers. However, this struct also offers the static\n * member functions `perform_registration` and `perform_deregistration` that\n * are needed for either registering when an element is added to a core outside\n * of initialization or deregistering when an element is being eliminated from a\n * core. The use of separate functions is necessary to provide an interface\n * usable outside of iterable actions, e.g. in specialized `pup` functions.\n */\nstruct RegisterEventsWithObservers\n    : tt::ConformsTo<Parallel::protocols::ElementRegistrar> {\n private:\n  template <typename ParallelComponent, typename RegisterOrDeregisterAction,\n            typename DbTagList, typename Metavariables, typename ArrayIndex>\n  static void register_or_deregister_impl(\n      const db::DataBox<DbTagList>& box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index) {\n    std::vector<\n        std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n        type_of_observation_and_observation_key_pairs;\n    const auto collect_observations =\n        [&box,\n         &type_of_observation_and_observation_key_pairs](const auto& event) {\n          if (auto obs_type_and_obs_key =\n                  get_registration_observation_type_and_key(event, box);\n              obs_type_and_obs_key.has_value()) {\n            type_of_observation_and_observation_key_pairs.push_back(\n                *obs_type_and_obs_key);\n          }\n        };\n\n    if constexpr (db::tag_is_retrievable_v<\n                      ::Tags::EventsAndTriggers<\n                          Triggers::WhenToCheck::AtIterations>,\n                      db::DataBox<DbTagList>>) {\n      const auto& triggers_and_events = db::get<\n          ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtIterations>>(box);\n      triggers_and_events.for_each_event(collect_observations);\n    }\n\n    if constexpr (db::tag_is_retrievable_v<\n                      ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n                      db::DataBox<DbTagList>>) {\n      const auto& triggers_and_events =\n          db::get<::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n              box);\n      triggers_and_events.for_each_event(collect_observations);\n    }\n\n    if constexpr (db::tag_is_retrievable_v<\n                      ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>,\n                      db::DataBox<DbTagList>>) {\n      const auto& triggers_and_events =\n          db::get<::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>>(\n              box);\n      triggers_and_events.for_each_event(collect_observations);\n    }\n\n    if constexpr (db::tag_is_retrievable_v<\n                      ::Tags::EventsAndTriggers<\n                          Triggers::WhenToCheck::AtCheckpoints>,\n                      db::DataBox<DbTagList>>) {\n      const auto& triggers_and_events = db::get<\n          ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtCheckpoints>>(box);\n      triggers_and_events.for_each_event(collect_observations);\n    }\n\n    if constexpr (db::tag_is_retrievable_v<::Tags::EventsAndDenseTriggers,\n                                           db::DataBox<DbTagList>>) {\n      const auto& triggers_and_events =\n          db::get<::Tags::EventsAndDenseTriggers>(box);\n      triggers_and_events.for_each_event(collect_observations);\n    }\n\n    if constexpr (db::tag_is_retrievable_v<::Tags::EventsRunAtCleanup,\n                                           db::DataBox<DbTagList>>) {\n      for (const auto& event : db::get<::Tags::EventsRunAtCleanup>(box)) {\n        collect_observations(*event);\n      }\n    }\n\n    if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {\n      using volume_register_action =\n          tmpl::conditional_t<std::is_same_v<RegisterOrDeregisterAction,\n                                             RegisterContributorWithObserver>,\n                              RegisterVolumeContributorWithObserverWriter,\n                              DeregisterVolumeContributorWithObserverWriter>;\n      using reduction_register_action =\n          tmpl::conditional_t<std::is_same_v<RegisterOrDeregisterAction,\n                                             RegisterContributorWithObserver>,\n                              RegisterReductionContributorWithObserverWriter,\n                              DeregisterReductionContributorWithObserverWriter>;\n      auto* observer_writer = Parallel::local_branch(\n          Parallel::get_parallel_component<\n              observers::ObserverWriter<Metavariables>>(cache));\n      ASSERT(observer_writer != nullptr,\n             \"The observer writer component should be valid. This is an \"\n             \"internal bug.\");\n      for (const auto& [type_of_observation, observation_key] :\n           type_of_observation_and_observation_key_pairs) {\n        switch (type_of_observation) {\n          case TypeOfObservation::Reduction:\n            Parallel::simple_action<reduction_register_action>(\n                *observer_writer, observation_key,\n                Parallel::ArrayComponentId{\n                    std::add_pointer_t<ParallelComponent>{nullptr},\n                    Parallel::ArrayIndex<ArrayIndex>(array_index)});\n            break;\n          case TypeOfObservation::Volume:\n            Parallel::simple_action<volume_register_action>(\n                *observer_writer, observation_key,\n                Parallel::ArrayComponentId{\n                    std::add_pointer_t<ParallelComponent>{nullptr},\n                    Parallel::ArrayIndex<ArrayIndex>(array_index)});\n            break;\n          default:\n            ERROR(\n                \"Attempting to deregister an unknown TypeOfObservation. \"\n                \"Should be one of 'Reduction' or 'Volume'\");\n        };\n      }\n    } else {\n      static_assert(Parallel::is_array_v<ParallelComponent>);\n\n      for (const auto& [type_of_observation, observation_key] :\n           type_of_observation_and_observation_key_pairs) {\n        auto& observer =\n            *Parallel::local_branch(Parallel::get_parallel_component<\n                                    observers::Observer<Metavariables>>(cache));\n        Parallel::simple_action<RegisterOrDeregisterAction>(\n            observer, observation_key,\n            Parallel::ArrayComponentId(\n                std::add_pointer_t<ParallelComponent>{nullptr},\n                Parallel::ArrayIndex<std::decay_t<ArrayIndex>>{array_index}),\n            type_of_observation);\n      }\n    }\n  }\n\n public:  // ElementRegistrar protocol\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void perform_registration(const db::DataBox<DbTagList>& box,\n                                   Parallel::GlobalCache<Metavariables>& cache,\n                                   const ArrayIndex& array_index) {\n    register_or_deregister_impl<ParallelComponent,\n                                RegisterContributorWithObserver>(box, cache,\n                                                                 array_index);\n  }\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void perform_deregistration(\n      const db::DataBox<DbTagList>& box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index) {\n    register_or_deregister_impl<ParallelComponent,\n                                DeregisterContributorWithObserver>(box, cache,\n                                                                   array_index);\n  }\n\n public:  // Iterable action\n  template <typename DbTagList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    perform_registration<ParallelComponent>(box, cache, array_index);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/Actions/RegisterSingleton.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace observers::Actions {\n/*!\n * \\brief Registers a singleton with the ObserverWriter.\n *\n * The singleton that observes is expected to call WriteReductionData\n * on node 0.\n */\ntemplate <typename RegisterHelper>\nstruct RegisterSingletonWithObserverWriter {\n  template <typename DbTagList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto [type_of_observation, observation_key] =\n        RegisterHelper::template register_info<ParallelComponent>(box,\n                                                                  array_index);\n\n    switch (type_of_observation) {\n      case TypeOfObservation::Reduction:\n        break;\n      case TypeOfObservation::Volume:\n        ERROR(\n            \"Registering volume observations is not supported for singletons. \"\n            \"The TypeOfObservation should be 'Reduction'.\");\n      default:\n        ERROR(\n            \"Registering an unknown TypeOfObservation. It should be \"\n            \"'Reduction' for singleton.\");\n    };\n\n    // We call only on node 0; the observation call will occur only\n    // on node 0.\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    Parallel::simple_action<Actions::RegisterReductionNodeWithWritingNode>(\n        Parallel::get_parallel_component<\n            observers::ObserverWriter<Metavariables>>(cache)[0],\n        observation_key, Parallel::my_node<size_t>(*Parallel::local(my_proxy)));\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n}  // namespace observers::Actions\n"
  },
  {
    "path": "src/IO/Observer/Actions/RegisterWithObservers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Protocols/ElementRegistrar.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\n/// \\cond\ntemplate <class... Tags>\nclass TaggedTuple;\n/// \\endcond\n\nnamespace observers::Actions {\n/*!\n * \\brief Register an observation ID with the observers.\n *\n * \\warning If registering events, you should use RegisterEventsWithObservers\n * instead. If your event is not compatible with RegisterEventsWithObservers,\n * please make it so.\n *\n * The `RegisterHelper` passed as a template parameter must have a static\n * `register_info` function that takes as its first template parameter the\n * `ParallelComponent` and as function arguments a `db::DataBox` and the array\n * component index. The function must return a\n * `std::pair<observers::TypeOfObservation, observers::ObservationId>`\n *\n * When this struct is used as an action, the `apply` function will perform the\n * registration with observers. However, this struct also offers the static\n * member functions `perform_registriation` and `perform_deregistration` that\n * are needed for either registering when an element is added to a core outside\n * of initialization or deregistering when an element is being eliminated from a\n * core. The use of separate functions is necessary to provide an interface\n * usable outside of iterable actions, e.g. in specialized `pup` functions.\n */\ntemplate <typename RegisterHelper>\nstruct RegisterWithObservers\n    : tt::ConformsTo<Parallel::protocols::ElementRegistrar> {\n private:\n  template <typename ParallelComponent, typename RegisterOrDeregisterAction,\n            typename DbTagList, typename Metavariables, typename ArrayIndex>\n  static void register_or_deregister_impl(\n      db::DataBox<DbTagList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index) {\n    auto& observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<observers::Observer<Metavariables>>(\n            cache));\n    const auto [type_of_observation, observation_id] =\n        RegisterHelper::template register_info<ParallelComponent>(box,\n                                                                  array_index);\n\n    Parallel::simple_action<RegisterOrDeregisterAction>(\n        observer, observation_id,\n        Parallel::make_array_component_id<ParallelComponent>(array_index),\n        type_of_observation);\n  }\n\n public:  // ElementRegistrar protocol\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void perform_registration(db::DataBox<DbTagList>& box,\n                                   Parallel::GlobalCache<Metavariables>& cache,\n                                   const ArrayIndex& array_index) {\n    register_or_deregister_impl<ParallelComponent,\n                                RegisterContributorWithObserver>(box, cache,\n                                                                 array_index);\n  }\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void perform_deregistration(\n      db::DataBox<DbTagList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index) {\n    register_or_deregister_impl<ParallelComponent,\n                                DeregisterContributorWithObserver>(box, cache,\n                                                                   array_index);\n  }\n\n public:  // Iterable action\n  template <typename DbTagList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    perform_registration<ParallelComponent>(box, cache, array_index);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace observers::Actions\n"
  },
  {
    "path": "src/IO/Observer/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Observer)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ObservationId.cpp\n  ReductionActions.cpp\n  TypeOfObservation.cpp\n  VolumeActions.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GetSectionObservationKey.hpp\n  Helpers.hpp\n  Initialize.hpp\n  ObservationId.hpp\n  ObserverComponent.hpp\n  ReductionActions.hpp\n  Tags.hpp\n  TypeOfObservation.hpp\n  VolumeActions.hpp\n  WriteSimpleData.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  Charmxx::pup\n  DataStructures\n  Domain\n  DomainCreators\n  ErrorHandling\n  H5\n  Options\n  Parallel\n  Printf\n  Serialization\n  Utilities\n  INTERFACE\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Protocols)\n"
  },
  {
    "path": "src/IO/Observer/GetSectionObservationKey.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n\nnamespace observers {\n\n/// Retrieve the `observers::Tags::ObservationKey<SectionIdTag>`, or the empty\n/// string if `SectionIdTag` is void. This is useful to support sections in\n/// parallel algorithms. The return value can be used to construct a subfile\n/// path for observations, and to skip observations on elements that are not\n/// part of a section (`std::nullopt`).\ntemplate <typename SectionIdTag, typename Box>\nstd::optional<std::string> get_section_observation_key(\n    [[maybe_unused]] const Box& box) {\n  if constexpr (std::is_same_v<SectionIdTag, void>) {\n    return \"\";\n  } else {\n    using db::get;\n    return get<observers::Tags::ObservationKey<SectionIdTag>>(box);\n  }\n}\n\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/Helpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/Tags/InputSource.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace observers {\nnamespace detail {\ntemplate <class ObservingAction, class = std::void_t<>>\nstruct get_reduction_data_tags {\n  using type = tmpl::list<>;\n};\n\ntemplate <class ObservingAction>\nstruct get_reduction_data_tags<\n    ObservingAction,\n    std::void_t<typename ObservingAction::observed_reduction_data_tags>> {\n  using type = typename ObservingAction::observed_reduction_data_tags;\n};\n\ntemplate <class ReductionDataType>\nstruct make_reduction_data_tag_impl {\n  using type = tmpl::wrap<typename ReductionDataType::datum_list,\n                          ::observers::Tags::ReductionData>;\n};\n}  // namespace detail\n\n/// Function that returns from the global cache a string containing the\n/// options provided in the yaml-formatted input file, if those options are\n/// in the global cache. Otherwise, returns an empty string.\ntemplate <typename Metavariables>\nstd::string input_source_from_cache(\n    const Parallel::GlobalCache<Metavariables>& cache) {\n  if constexpr (tmpl::list_contains_v<\n                    ::Parallel::get_const_global_cache_tags<Metavariables>,\n                    ::Parallel::Tags::InputSource>) {\n    const std::vector<std::string> input_source_vector{\n        Parallel::get<::Parallel::Tags::InputSource>(cache)};\n    std::string input_source{};\n    for (auto it = input_source_vector.begin(); it != input_source_vector.end();\n         ++it) {\n      input_source += *it;\n    }\n    return input_source;\n  } else {\n    return \"\"s;\n  }\n}\n\n/// Each Action that sends data to the reduction Observer must specify\n/// a type alias `observed_reduction_data_tags` that describes the data it\n/// sends.  Given a list of such Actions (or other types that expose the alias),\n/// this metafunction is used to create\n/// `Metavariables::observed_reduction_data_tags` (which is required to\n/// initialize the Observer).\ntemplate <class ObservingActionList>\nusing collect_reduction_data_tags =\n    tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n        ObservingActionList, detail::get_reduction_data_tags<tmpl::_1>>>>;\n\n/// Produces the `tmpl::list` of `observers::Tags::ReductionData` tags that\n/// corresponds to the `tmpl::list` of `Parallel::ReductionData` passed into\n/// this metafunction.\ntemplate <typename ReductionDataList>\nusing make_reduction_data_tags = tmpl::remove_duplicates<tmpl::transform<\n    ReductionDataList, detail::make_reduction_data_tag_impl<tmpl::_1>>>;\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace observers {\nnamespace Actions {\nnamespace detail {\ntemplate <class Tag>\nusing reduction_data_to_reduction_names = typename Tag::names_tag;\n}  // namespace detail\n/*!\n * \\brief Initializes the DataBox on the observer parallel component\n *\n * Uses:\n * - Metavariables:\n *   - `observed_reduction_data_tags` (see ContributeReductionData)\n */\ntemplate <class Metavariables>\nstruct Initialize {\n  using simple_tags = tmpl::append<\n      tmpl::list<Tags::ExpectedContributorsForObservations,\n                 Tags::ContributorsOfReductionData,\n                 Tags::ContributorsOfTensorData, Tags::TensorData>,\n      typename Metavariables::observed_reduction_data_tags,\n      tmpl::transform<\n          typename Metavariables::observed_reduction_data_tags,\n          tmpl::bind<detail::reduction_data_to_reduction_names, tmpl::_1>>>;\n  using compute_tags = tmpl::list<>;\n\n  using return_tag_list = tmpl::append<simple_tags, compute_tags>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Initializes the DataBox of the observer parallel component that writes\n * to disk.\n *\n * Uses:\n * - Metavariables:\n *   - `observed_reduction_data_tags` (see ContributeReductionData)\n *\n */\ntemplate <class Metavariables>\nstruct InitializeWriter {\n  using simple_tags = tmpl::append<\n      tmpl::list<Tags::ExpectedContributorsForObservations,\n                 Tags::ContributorsOfReductionData, Tags::ReductionDataLock,\n                 Tags::ContributorsOfTensorData, Tags::TensorData,\n                 Tags::InterpolatorTensorData,\n                 Tags::NodesExpectedToContributeReductions,\n                 Tags::NodesThatContributedReductions, Tags::H5FileLock,\n                 Tags::SerializedFunctionsOfTime, Tags::Dependencies>,\n      typename Metavariables::observed_reduction_data_tags,\n      tmpl::transform<\n          typename Metavariables::observed_reduction_data_tags,\n          tmpl::bind<detail::reduction_data_to_reduction_names, tmpl::_1>>>;\n  using compute_tags = tmpl::list<>;\n\n  using return_tag_list = tmpl::append<simple_tags, compute_tags>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/ObservationId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Observer/ObservationId.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <utility>\n\nnamespace observers {\nObservationKey::ObservationKey(std::string tag)\n    : key_(std::hash<std::string>{}(tag)), tag_(std::move(tag)) {}\n\nvoid ObservationKey::pup(PUP::er& p) {\n  p | key_;\n  p | tag_;\n}\n\nconst std::string& ObservationKey::tag() const { return tag_; }\n\nbool operator==(const ObservationKey& lhs, const ObservationKey& rhs) {\n  return lhs.key() == rhs.key() and lhs.tag() == rhs.tag();\n}\n\nbool operator!=(const ObservationKey& lhs, const ObservationKey& rhs) {\n  return not(lhs == rhs);\n}\n\nstd::ostream& operator<<(std::ostream& os, const ObservationKey& t) {\n  return os << '(' << t.tag() << ')';\n}\n\nObservationId::ObservationId(const double t, std::string tag)\n    : observation_key_(std::move(tag)),\n      combined_hash_([&t](size_t type_hash) {\n        size_t combined = type_hash;\n        boost::hash_combine(combined, t);\n        return combined;\n      }(observation_key_.key())),\n      value_(t) {}\n\nvoid ObservationId::pup(PUP::er& p) {\n  p | observation_key_;\n  p | combined_hash_;\n  p | value_;\n}\n\nbool operator==(const ObservationId& lhs, const ObservationId& rhs) {\n  return lhs.hash() == rhs.hash() and lhs.value() == rhs.value();\n}\n\nbool operator!=(const ObservationId& lhs, const ObservationId& rhs) {\n  return not(lhs == rhs);\n}\n\nstd::ostream& operator<<(std::ostream& os, const ObservationId& t) {\n  return os << '(' << t.observation_key() << \",\" << t.hash() << ',' << t.value()\n            << ')';\n}\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/ObservationId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <functional>\n#include <iosfwd>\n#include <limits>\n#include <string>\n#include <type_traits>\n\n#include \"Utilities/PrettyType.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace observers {\n/// \\cond\nclass ObservationId;\n/// \\endcond\n\n/// Used as a key in maps to keep track of how many elements have registered.\nclass ObservationKey {\n public:\n  ObservationKey() = default;\n\n  explicit ObservationKey(std::string tag);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  size_t key() const { return key_; }\n\n  const std::string& tag() const;\n\n private:\n  size_t key_{0};\n  std::string tag_{};\n};\n\nbool operator==(const ObservationKey& lhs, const ObservationKey& rhs);\nbool operator!=(const ObservationKey& lhs, const ObservationKey& rhs);\n\nstd::ostream& operator<<(std::ostream& os, const ObservationKey& t);\n\n/*!\n * \\ingroup ObserversGroup\n * \\brief A unique identifier for an observation representing the type of\n * observation and the instance (e.g. time) at which the observation occurs.\n *\n * The ObservationId is used to uniquely identify an observation inside our H5\n * subfiles that can be agreed upon across the system. That is, it does not\n * depend on hardware rounding of floating points because it is an integral. One\n * example would be a `Time` for output observed every `N` steps in a global\n * time stepping evolution. Another example could be having a counter class for\n * dense output observations that increments the counter for each observation\n * but has a value equal to the physical time.\n *\n * The constructor takes a `double` representing the current \"time\" at which we\n * are observing. For an evolution this could be the physical time, while for an\n * elliptic solve this could be a combination of nonlinear and linear iteration.\n *\n * A specialization of `std::hash` is provided to allow using `ObservationId`\n * as a key in associative containers.\n */\nclass ObservationId {\n public:\n  ObservationId() = default;\n  /*!\n   * \\brief Construct from a value and a string tagging the observation\n   *\n   * The tag is a unique string used to identify the particular observation.\n   */\n  ObservationId(double t, std::string tag);\n\n  ObservationId(double t, ObservationKey key);\n\n  /// Hash used to distinguish between ObservationIds of different\n  /// types and subtypes. This hash does not contain any information about the\n  /// `Id` or its value.\n  const ObservationKey& observation_key() const { return observation_key_; }\n\n  /// Hash distinguishing different ObservationIds, including\n  /// the value of the `Id`.\n  size_t hash() const { return combined_hash_; }\n\n  /// The simulation \"time\".\n  double value() const { return value_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  ObservationKey observation_key_{};\n  size_t combined_hash_{0};\n  double value_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator==(const ObservationId& lhs, const ObservationId& rhs);\nbool operator!=(const ObservationId& lhs, const ObservationId& rhs);\n\nstd::ostream& operator<<(std::ostream& os, const ObservationId& t);\n}  // namespace observers\n\nnamespace std {\ntemplate <>\nstruct hash<observers::ObservationId> {\n  size_t operator()(const observers::ObservationId& t) const {\n    return t.hash();\n  }\n};\n\ntemplate <>\nstruct hash<observers::ObservationKey> {\n  size_t operator()(const observers::ObservationKey& t) const {\n    return t.key();\n  }\n};\n}  // namespace std\n"
  },
  {
    "path": "src/IO/Observer/ObserverComponent.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/Algorithms/AlgorithmGroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Tags/InputSource.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace observers {\n/*!\n * \\ingroup ObserversGroup\n * \\brief The group parallel component that is responsible for reducing data\n * to be observed.\n *\n * Once the data from all elements on the processing element (usually a core)\n * has been collected, it is copied (not sent over the network) to the local\n * nodegroup parallel component, `ObserverWriter`, for writing to disk.\n */\ntemplate <class Metavariables>\nstruct Observer {\n  using chare_type = Parallel::Algorithms::Group;\n  static constexpr bool checkpoint_data = false;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<Actions::Initialize<Metavariables>,\n                                        Parallel::Actions::TerminatePhase>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase /*next_phase*/,\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n};\n\n/*!\n * \\ingroup ObserversGroup\n * \\brief The nodegroup parallel component that is responsible for writing data\n * to disk.\n */\ntemplate <class Metavariables>\nstruct ObserverWriter {\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = false;\n  using const_global_cache_tags =\n      tmpl::list<Tags::ReductionFileName, Tags::VolumeFileName,\n                 ::Parallel::Tags::InputSource>;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<Actions::InitializeWriter<Metavariables>,\n                 Parallel::Actions::TerminatePhase>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase /*next_phase*/,\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n};\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ReductionDataFormatter.hpp\n  )\n"
  },
  {
    "path": "src/IO/Observer/Protocols/ReductionDataFormatter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Protocols related to observers\nnamespace observers::protocols {\n/*!\n * \\brief Conforming classes can create an informative message from reduction\n * data. The message will be printed to screen when the formatter is passed to a\n * reduction action such as `observers::Actions::ContributeReductionData`.\n *\n * Conforming classes must provide the following type aliases:\n * - `reduction_data`: The `Parallel::ReductionData` that the formatter is\n *   applicable to.\n *\n * Conforming classes must implement the following member functions:\n * - `operator()`: A call operator that takes the values of the reduction data\n *   and returns a `std::string`. The string should be an informative message\n *   such as \"Global minimum grid spacing at time 5.4 is: 0.1\". It should end\n *   with a newline character.\n * - `pup`: A PUP function for serialization.\n *\n * Here's an example for a formatter:\n *\n * \\snippet Test_ReductionObserver.cpp formatter_example\n */\nstruct ReductionDataFormatter {\n  template <typename ConformingType>\n  struct test {\n    using reduction_data = typename ConformingType::reduction_data;\n  };\n};\n}  // namespace observers::protocols\n"
  },
  {
    "path": "src/IO/Observer/ReductionActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Observer/ReductionActions.hpp\"\n\n#include <vector>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace observers::ThreadedActions::ReductionActions_detail {\n\nvoid append_to_reduction_data(\n    const gsl::not_null<std::vector<double>*> all_reduction_data,\n    const double t) {\n  all_reduction_data->push_back(t);\n}\n\ntemplate <typename T>\nvoid append_to_reduction_data(\n    const gsl::not_null<std::vector<double>*> all_reduction_data,\n    const std::vector<T>& t) {\n  all_reduction_data->insert(all_reduction_data->end(), t.begin(), t.end());\n}\n\nvoid append_to_reduction_data(\n    const gsl::not_null<std::vector<double>*> all_reduction_data,\n    const std::array<double, 3>& t) {\n    all_reduction_data->insert(all_reduction_data->end(), t.begin(), t.end());\n}\n\n// Generate instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                  \\\n  template void append_to_reduction_data<DTYPE(data)>(        \\\n      gsl::not_null<std::vector<double>*> all_reduction_data, \\\n      const std::vector<DTYPE(data)>& t);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, size_t))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace observers::ThreadedActions::ReductionActions_detail\n"
  },
  {
    "path": "src/IO/Observer/ReductionActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/Protocols/ReductionDataFormatter.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace observers {\n\n/// \\cond\ntemplate <class Metavariables>\nstruct ObserverWriter;\n/// \\endcond\n\nnamespace ThreadedActions {\n/// \\cond\nstruct CollectReductionDataOnNode;\nstruct WriteReductionData;\n/// \\endcond\n}  // namespace ThreadedActions\n\n/// Indicates no formatter is selected\nstruct NoFormatter {\n  void pup(PUP::er& /*p*/) {}\n};\n\nnamespace Actions {\n/// \\cond\nstruct ContributeReductionDataToWriter;\n/// \\endcond\n\n/*!\n * \\ingroup ObserversGroup\n * \\brief Send reduction data to the observer group.\n *\n * Once everything at a specific `ObservationId` has been contributed to the\n * reduction, the groups reduce to their local nodegroup.\n *\n * The caller of this Action (which is to be invoked on the Observer parallel\n * component) must pass in an `observation_id` used to uniquely identify the\n * observation in time, the name of the `h5::Dat` subfile in the HDF5 file (e.g.\n * `/element_data`, where the slash is important), a `std::vector<std::string>`\n * of names of the quantities being reduced (e.g. `{\"Time\", \"L1ErrorDensity\",\n * \"L2ErrorDensity\"}`), and the `Parallel::ReductionData` that holds the\n * `ReductionDatums` containing info on how to do the reduction.\n *\n * The observer components need to know all expected reduction data types by\n * compile-time, so they rely on the\n * `Metavariables::observed_reduction_data_tags` alias to collect them in one\n * place. To this end, each Action that contributes reduction data must expose\n * the type alias as:\n *\n * \\snippet ObserverHelpers.hpp make_reduction_data_tags\n *\n * Then, in the `Metavariables` collect them from all observing Actions using\n * the `observers::collect_reduction_data_tags` metafunction.\n *\n * This action also accepts a \"formatter\" that will be forwarded along with the\n * reduction data and used to print an informative message when the reduction is\n * complete. The formatter must conform to\n * `observers::protocols::ReductionDataFormatter`.\n *\n * This action also supports observing the intermediate stage of the reduction\n * over just the processing element that the element is currently on. This can\n * be useful e.g. to measure performance metric to assess load-balancing such as\n * the number of grid points on each core. Enable per-core observations by\n * passing `true` for the `observe_per_core` argument (default: `false`). The\n * data will be written in one H5 file per _node_ prefixed with the\n * `observers::Tags::ReductionFileName`, in a `Core{core_id}` subfile, where\n * `core_id` is an integer identifying the core across all nodes (see\n * `Parallel::my_proc`). For example, when running on 2 nodes with 2 cores each\n * you will end up with `Reductions0.h5` containing `/Core0/{subfile_name}.dat`\n * and `/Core1/{subfile_name}.dat`, and `Reductions1.h5` containing\n * `/Core2/{subfile_name}.dat` and `/Core3/{subfile_name}.dat`. This is in\n * addition to the usual reduction output over _all_ registered elements,\n * written to `Reductions.h5` (no node ID suffix in the file name).\n */\nstruct ContributeReductionData {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, typename... Ts,\n            typename Formatter = observers::NoFormatter>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index,\n                    const observers::ObservationId& observation_id,\n                    const Parallel::ArrayComponentId& sender_array_id,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& reduction_names,\n                    Parallel::ReductionData<Ts...>&& reduction_data,\n                    std::optional<Formatter>&& formatter = std::nullopt,\n                    const bool observe_per_core = false) {\n    db::mutate<Tags::ReductionData<Ts...>, Tags::ReductionDataNames<Ts...>,\n               Tags::ContributorsOfReductionData>(\n        [&array_index, &cache, &observation_id,\n         reduction_data = std::move(reduction_data), &reduction_names,\n         &sender_array_id, &subfile_name, &formatter, &observe_per_core](\n            const gsl::not_null<std::unordered_map<\n                ObservationId, Parallel::ReductionData<Ts...>>*>\n                reduction_data_map,\n            const gsl::not_null<\n                std::unordered_map<ObservationId, std::vector<std::string>>*>\n                reduction_names_map,\n            const gsl::not_null<std::unordered_map<\n                ObservationId, std::unordered_set<Parallel::ArrayComponentId>>*>\n                reduction_observers_contributed,\n            const std::unordered_map<\n                ObservationKey,\n                std::unordered_set<Parallel::ArrayComponentId>>&\n                observations_registered) mutable {  // NOLINT(spectre-mutable)\n          ASSERT(\n              observations_registered.find(observation_id.observation_key()) !=\n                  observations_registered.end(),\n              \"Couldn't find registration key \"\n                  << observation_id.observation_key()\n                  << \" in the registered observers. Known IDs are \"\n                  << keys_of(observations_registered)\n                  << \"\\nIt could be that you are using a nodegroup DG \"\n                     \"collection but the observation code for the event hasn't \"\n                     \"been updated to work with the nodegroup yet.\");\n\n          auto& contributed_array_ids =\n              (*reduction_observers_contributed)[observation_id];\n          if (UNLIKELY(contributed_array_ids.find(sender_array_id) !=\n                       contributed_array_ids.end())) {\n            ERROR(\"Already received reduction data to observation id \"\n                  << observation_id << \" from array component id \"\n                  << sender_array_id);\n          }\n          contributed_array_ids.insert(sender_array_id);\n\n          if (reduction_data_map->count(observation_id) == 0) {\n            reduction_data_map->emplace(observation_id,\n                                        std::move(reduction_data));\n            reduction_names_map->emplace(observation_id, reduction_names);\n          } else {\n            if (UNLIKELY(reduction_names_map->at(observation_id) !=\n                         reduction_names)) {\n              using ::operator<<;\n              ERROR(\"Reduction names differ at ObservationId \"\n                    << observation_id << \" with the expected names being \"\n                    << reduction_names_map->at(observation_id)\n                    << \" and the received names being \" << reduction_names);\n            }\n            reduction_data_map->operator[](observation_id)\n                .combine(std::move(reduction_data));\n          }\n\n          // Check if we have received all reduction data from the registered\n          // elements. If so, we reduce to the local ObserverWriter nodegroup.\n          if (UNLIKELY(\n                  contributed_array_ids.size() ==\n                  observations_registered.at(observation_id.observation_key())\n                      .size())) {\n            auto& local_writer = *Parallel::local_branch(\n                Parallel::get_parallel_component<ObserverWriter<Metavariables>>(\n                    cache));\n            auto& my_proxy =\n                Parallel::get_parallel_component<ParallelComponent>(cache);\n            const std::optional<int> observe_with_core_id =\n                observe_per_core ? std::make_optional(Parallel::my_proc<int>(\n                                       *Parallel::local_branch(my_proxy)))\n                                 : std::nullopt;\n            Parallel::threaded_action<\n                ThreadedActions::CollectReductionDataOnNode>(\n                local_writer, observation_id,\n                Parallel::make_array_component_id<ParallelComponent>(\n                    array_index),\n                subfile_name, (*reduction_names_map)[observation_id],\n                std::move((*reduction_data_map)[observation_id]),\n                std::move(formatter), observe_with_core_id);\n            reduction_data_map->erase(observation_id);\n            reduction_names_map->erase(observation_id);\n            reduction_observers_contributed->erase(observation_id);\n          }\n        },\n        make_not_null(&box),\n        db::get<Tags::ExpectedContributorsForObservations>(box));\n    // Silence a gcc <= 9 unused-variable warning\n    (void)observe_per_core;\n  }\n};\n}  // namespace Actions\n\nnamespace ThreadedActions {\n\nnamespace ReductionActions_detail {\nvoid append_to_reduction_data(\n    const gsl::not_null<std::vector<double>*> all_reduction_data,\n    const double t);\n\ntemplate <typename T>\nvoid append_to_reduction_data(\n    const gsl::not_null<std::vector<double>*> all_reduction_data,\n    const std::vector<T>& t);\n\nvoid append_to_reduction_data(\n    gsl::not_null<std::vector<double>*> all_reduction_data,\n    const std::array<double, 3>& t);\n\ntemplate <typename... Ts, size_t... Is>\nvoid write_data(const std::string& subfile_name,\n                const std::string& input_source,\n                std::vector<std::string> legend, const std::tuple<Ts...>& data,\n                const std::string& file_prefix,\n                std::index_sequence<Is...> /*meta*/) {\n  static_assert(sizeof...(Ts) > 0,\n                \"Must be reducing at least one piece of data\");\n  std::vector<double> data_to_append{};\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      append_to_reduction_data(&data_to_append, std::get<Is>(data)));\n\n  if (legend.size() != data_to_append.size()) {\n    ERROR(\n        \"There must be one name provided for each piece of data. You provided \"\n        << legend.size() << \" names: '\" << get_output(legend)\n        << \"' but there are \" << data_to_append.size()\n        << \" pieces of data being reduced\");\n  }\n\n  h5::H5File<h5::AccessType::ReadWrite> h5file(file_prefix + \".h5\", true,\n                                               input_source);\n  constexpr size_t version_number = 0;\n  auto& time_series_file = h5file.try_insert<h5::Dat>(\n      subfile_name, std::move(legend), version_number);\n  time_series_file.append(data_to_append);\n}\n}  // namespace ReductionActions_detail\n\n/*!\n * \\brief Gathers all the reduction data from all processing elements/cores on a\n * node.\n */\nstruct CollectReductionDataOnNode {\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename... ReductionDatums,\n            typename Formatter = observers::NoFormatter>\n  static void apply(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const gsl::not_null<Parallel::NodeLock*> node_lock,\n      const observers::ObservationId& observation_id,\n      Parallel::ArrayComponentId observer_group_id,\n      const std::string& subfile_name,\n      std::vector<std::string>&& reduction_names,\n      Parallel::ReductionData<ReductionDatums...>&& received_reduction_data,\n      std::optional<Formatter>&& formatter = std::nullopt,\n      const std::optional<int> observe_with_core_id = std::nullopt) {\n    // The below gymnastics with pointers is done in order to minimize the\n    // time spent locking the entire node, which is necessary because the\n    // DataBox does not allow any functions calls, both get and mutate, during\n    // a mutate. This design choice in DataBox is necessary to guarantee a\n    // consistent state throughout mutation. Here, however, we need to be\n    // reasonable efficient in parallel and so we manually guarantee that\n    // consistent state. To this end, we create pointers and assign to them\n    // the data in the DataBox which is guaranteed to be pointer stable. The\n    // data itself is guaranteed to be stable inside the ReductionDataLock.\n    std::unordered_map<observers::ObservationId,\n                       Parallel::ReductionData<ReductionDatums...>>*\n        reduction_data = nullptr;\n    std::unordered_map<ObservationId, std::vector<std::string>>*\n        reduction_names_map = nullptr;\n    std::unordered_map<observers::ObservationId,\n                       std::unordered_set<Parallel::ArrayComponentId>>*\n        reduction_observers_contributed = nullptr;\n    Parallel::NodeLock* reduction_data_lock = nullptr;\n    Parallel::NodeLock* reduction_file_lock = nullptr;\n    size_t observations_registered_with_id = std::numeric_limits<size_t>::max();\n\n    {\n      const std::lock_guard hold_lock(*node_lock);\n      db::mutate<Tags::ReductionData<ReductionDatums...>,\n                 Tags::ReductionDataNames<ReductionDatums...>,\n                 Tags::ContributorsOfReductionData, Tags::ReductionDataLock,\n                 Tags::H5FileLock>(\n          [&reduction_data, &reduction_names_map,\n           &reduction_observers_contributed, &reduction_data_lock,\n           &reduction_file_lock, &observation_id, &observer_group_id,\n           &observations_registered_with_id](\n              const gsl::not_null<std::unordered_map<\n                  observers::ObservationId,\n                  Parallel::ReductionData<ReductionDatums...>>*>\n                  reduction_data_ptr,\n              const gsl::not_null<\n                  std::unordered_map<ObservationId, std::vector<std::string>>*>\n                  reduction_names_map_ptr,\n              const gsl::not_null<std::unordered_map<\n                  observers::ObservationId,\n                  std::unordered_set<Parallel::ArrayComponentId>>*>\n                  reduction_observers_contributed_ptr,\n              const gsl::not_null<Parallel::NodeLock*> reduction_data_lock_ptr,\n              const gsl::not_null<Parallel::NodeLock*> reduction_file_lock_ptr,\n              const std::unordered_map<\n                  ObservationKey,\n                  std::unordered_set<Parallel::ArrayComponentId>>&\n                  observations_registered) {\n            const ObservationKey& key{observation_id.observation_key()};\n            const auto& registered_group_ids = observations_registered.at(key);\n            if (UNLIKELY(registered_group_ids.find(observer_group_id) ==\n                         registered_group_ids.end())) {\n              ERROR(\"The observer group id \"\n                    << observer_group_id\n                    << \" was not registered for the observation id \"\n                    << observation_id);\n            }\n            reduction_data = &*reduction_data_ptr;\n            reduction_names_map = &*reduction_names_map_ptr;\n            reduction_observers_contributed =\n                &*reduction_observers_contributed_ptr;\n            reduction_data_lock = &*reduction_data_lock_ptr;\n            reduction_file_lock = &*reduction_file_lock_ptr;\n            observations_registered_with_id =\n                observations_registered.at(key).size();\n          },\n          make_not_null(&box),\n          db::get<Tags::ExpectedContributorsForObservations>(box));\n    }\n\n    ASSERT(\n        observations_registered_with_id != std::numeric_limits<size_t>::max(),\n        \"Failed to set observations_registered_with_id when mutating the \"\n        \"DataBox. This is a bug in the code.\");\n\n    bool send_data = false;\n    // Now that we've retrieved pointers to the data in the DataBox we wish to\n    // manipulate, lock the data and manipulate it.\n    {\n      const std::lock_guard hold_data_lock(*reduction_data_lock);\n      auto& contributed_group_ids =\n          (*reduction_observers_contributed)[observation_id];\n\n      if (UNLIKELY(contributed_group_ids.find(observer_group_id) !=\n                   contributed_group_ids.end())) {\n        ERROR(\"Already received reduction data to observation id \"\n              << observation_id << \" from array component id \"\n              << observer_group_id);\n      }\n      contributed_group_ids.insert(observer_group_id);\n\n      // If requested, write the intermediate reduction data from the particular\n      // core to one file per node. This allows measuring reduction data\n      // per-core, e.g. performance metrics to assess load balancing.\n      if (observe_with_core_id.has_value()) {\n        auto reduction_data_this_core = received_reduction_data;\n        reduction_data_this_core.finalize();\n        auto reduction_names_this_core = reduction_names;\n        auto& my_proxy =\n            Parallel::get_parallel_component<ParallelComponent>(cache);\n        const std::lock_guard hold_file_lock(*reduction_file_lock);\n        ReductionActions_detail::write_data(\n            \"/Core\" + std::to_string(observe_with_core_id.value()) +\n                subfile_name,\n            observers::input_source_from_cache(cache),\n            std::move(reduction_names_this_core),\n            std::move(reduction_data_this_core.data()),\n            Parallel::get<Tags::ReductionFileName>(cache) +\n                std::to_string(\n                    Parallel::my_node<int>(*Parallel::local_branch(my_proxy))),\n            std::make_index_sequence<sizeof...(ReductionDatums)>{});\n      }\n\n      if (reduction_data->find(observation_id) == reduction_data->end()) {\n        // This Action has been called for the first time,\n        // so all we need to do is move the input data to the\n        // reduction_data in the DataBox.\n        reduction_data->operator[](observation_id) =\n            std::move(received_reduction_data);\n      } else {\n        // This Action is being called at least the second time\n        // (but not the final time if on node 0).\n        reduction_data->at(observation_id)\n            .combine(std::move(received_reduction_data));\n      }\n\n      if (UNLIKELY(reduction_names.empty())) {\n        ERROR(\n            \"The reduction names, which is a std::vector of the names of \"\n            \"the columns in the file, must be non-empty.\");\n      }\n      if (auto current_names = reduction_names_map->find(observation_id);\n          current_names == reduction_names_map->end()) {\n        reduction_names_map->emplace(observation_id,\n                                     std::move(reduction_names));\n      } else if (UNLIKELY(current_names->second != reduction_names)) {\n        ERROR(\n            \"The reduction names passed in must match the currently \"\n            \"known reduction names.\");\n      }\n\n      // Check if we have received all reduction data from the Observer\n      // group. If so we reduce to node 0 for writing to disk. We use a bool\n      // `send_data` to allow us to defer the send call until after we've\n      // unlocked the lock.\n      if (reduction_observers_contributed->at(observation_id).size() ==\n          observations_registered_with_id) {\n        send_data = true;\n        // We intentionally move the data out of the map and erase it\n        // before call `WriteReductionData` since if the call to\n        // `WriteReductionData` is inlined and we erase data from the maps\n        // afterward we would lose data.\n        reduction_names =\n            std::move(reduction_names_map->operator[](observation_id));\n        received_reduction_data =\n            std::move(reduction_data->operator[](observation_id));\n        reduction_observers_contributed->erase(observation_id);\n        reduction_data->erase(observation_id);\n        reduction_names_map->erase(observation_id);\n      }\n    }\n\n    if (send_data) {\n      auto& my_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      Parallel::threaded_action<WriteReductionData>(\n          Parallel::get_parallel_component<ObserverWriter<Metavariables>>(\n              cache)[0],\n          observation_id,\n          Parallel::my_node<size_t>(*Parallel::local_branch(my_proxy)),\n          subfile_name,\n          // NOLINTNEXTLINE(bugprone-use-after-move)\n          std::move(reduction_names), std::move(received_reduction_data),\n          std::move(formatter));\n    }\n  }\n};\n\n/*!\n * \\ingroup ObserversGroup\n * \\brief Write reduction data to disk from node 0.\n */\nstruct WriteReductionData {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename... ReductionDatums,\n            typename Formatter = observers::NoFormatter>\n  static void apply(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const gsl::not_null<Parallel::NodeLock*> node_lock,\n      const observers::ObservationId& observation_id,\n      const size_t sender_node_number, const std::string& subfile_name,\n      std::vector<std::string>&& reduction_names,\n      Parallel::ReductionData<ReductionDatums...>&& received_reduction_data,\n      std::optional<Formatter>&& formatter = std::nullopt) {\n    if constexpr (not std::is_same_v<Formatter, observers::NoFormatter>) {\n      static_assert(\n          tt::assert_conforms_to_v<Formatter,\n                                   protocols::ReductionDataFormatter>);\n      static_assert(\n          std::is_same_v<typename Formatter::reduction_data,\n                         Parallel::ReductionData<ReductionDatums...>>,\n          \"Mismatch between the formatter's `reduction_data` type alias and \"\n          \"the reduction data that is being reduced.\");\n    }\n    // The below gymnastics with pointers is done in order to minimize the\n    // time spent locking the entire node, which is necessary because the\n    // DataBox does not allow any functions calls, both get and mutate, during\n    // a mutate. This design choice in DataBox is necessary to guarantee a\n    // consistent state throughout mutation. Here, however, we need to be\n    // reasonable efficient in parallel and so we manually guarantee that\n    // consistent state. To this end, we create pointers and assign to them\n    // the data in the DataBox which is guaranteed to be pointer stable. The\n    // data itself is guaranteed to be stable inside the ReductionDataLock.\n    std::unordered_map<observers::ObservationId,\n                       Parallel::ReductionData<ReductionDatums...>>*\n        reduction_data = nullptr;\n    std::unordered_map<ObservationId, std::vector<std::string>>*\n        reduction_names_map = nullptr;\n    std::unordered_map<observers::ObservationId, std::unordered_set<size_t>>*\n        nodes_contributed = nullptr;\n    Parallel::NodeLock* reduction_data_lock = nullptr;\n    Parallel::NodeLock* reduction_file_lock = nullptr;\n    size_t observations_registered_with_id = std::numeric_limits<size_t>::max();\n\n    {\n      const std::lock_guard hold_lock(*node_lock);\n      db::mutate<Tags::ReductionData<ReductionDatums...>,\n                 Tags::ReductionDataNames<ReductionDatums...>,\n                 Tags::NodesThatContributedReductions, Tags::ReductionDataLock,\n                 Tags::H5FileLock>(\n          [&nodes_contributed, &reduction_data, &reduction_names_map,\n           &reduction_data_lock, &reduction_file_lock, &observation_id,\n           &observations_registered_with_id, &sender_node_number](\n              const gsl::not_null<\n                  typename Tags::ReductionData<ReductionDatums...>::type*>\n                  reduction_data_ptr,\n              const gsl::not_null<\n                  std::unordered_map<ObservationId, std::vector<std::string>>*>\n                  reduction_names_map_ptr,\n              const gsl::not_null<std::unordered_map<\n                  ObservationId, std::unordered_set<size_t>>*>\n                  nodes_contributed_ptr,\n              const gsl::not_null<Parallel::NodeLock*> reduction_data_lock_ptr,\n              const gsl::not_null<Parallel::NodeLock*> reduction_file_lock_ptr,\n              const std::unordered_map<ObservationKey, std::set<size_t>>&\n                  nodes_registered_for_reductions) {\n            const ObservationKey& key{observation_id.observation_key()};\n            ASSERT(nodes_registered_for_reductions.find(key) !=\n                       nodes_registered_for_reductions.end(),\n                   \"Performing reduction with unregistered ID key \"\n                       << observation_id.observation_key());\n            const auto& registered_nodes =\n                nodes_registered_for_reductions.at(key);\n\n            if (UNLIKELY(registered_nodes.find(sender_node_number) ==\n                         registered_nodes.end())) {\n              ERROR(\"Node \" << sender_node_number\n                            << \" was not registered for the observation id \"\n                            << observation_id);\n            }\n\n            reduction_data = &*reduction_data_ptr;\n            reduction_names_map = &*reduction_names_map_ptr;\n            nodes_contributed = &*nodes_contributed_ptr;\n            reduction_data_lock = &*reduction_data_lock_ptr;\n            reduction_file_lock = &*reduction_file_lock_ptr;\n            observations_registered_with_id =\n                nodes_registered_for_reductions.at(key).size();\n          },\n          make_not_null(&box),\n          db::get<Tags::NodesExpectedToContributeReductions>(box));\n    }\n\n    ASSERT(\n        observations_registered_with_id != std::numeric_limits<size_t>::max(),\n        \"Failed to set observations_registered_with_id when mutating the \"\n        \"DataBox. This is a bug in the code.\");\n\n    bool write_to_disk = false;\n    // Now that we've retrieved pointers to the data in the DataBox we wish to\n    // manipulate, lock the data and manipulate it.\n    {\n      const std::lock_guard hold_lock(*reduction_data_lock);\n      auto& nodes_contributed_to_observation =\n          (*nodes_contributed)[observation_id];\n      if (nodes_contributed_to_observation.find(sender_node_number) !=\n          nodes_contributed_to_observation.end()) {\n        ERROR(\"Already received reduction data at observation id \"\n              << observation_id << \" from node \" << sender_node_number);\n      }\n      nodes_contributed_to_observation.insert(sender_node_number);\n\n      if (UNLIKELY(reduction_names.empty())) {\n        ERROR(\n            \"The reduction names, which is a std::vector of the names of \"\n            \"the columns in the file, must be non-empty.\");\n      }\n      if (auto current_names = reduction_names_map->find(observation_id);\n          current_names == reduction_names_map->end()) {\n        reduction_names_map->emplace(observation_id,\n                                     std::move(reduction_names));\n      } else if (UNLIKELY(current_names->second != reduction_names)) {\n        using ::operator<<;\n        ERROR(\n            \"The reduction names passed in must match the currently \"\n            \"known reduction names. Current ones are \"\n            << current_names->second << \" while the received are \"\n            << reduction_names);\n      }\n\n      if (reduction_data->find(observation_id) == reduction_data->end()) {\n        // This Action has been called for the first time,\n        // so all we need to do is move the input data to the\n        // reduction_data in the DataBox.\n        reduction_data->operator[](observation_id) =\n            std::move(received_reduction_data);\n      } else {\n        // This Action is being called at least the second time\n        // (but not the final time if on node 0).\n        reduction_data->at(observation_id)\n            .combine(std::move(received_reduction_data));\n      }\n\n      // We use a bool `write_to_disk` to allow us to defer the data writing\n      // until after we've unlocked the lock. For the same reason, we move the\n      // final, reduced result into `received_reduction_data` and\n      // `reduction_names`.\n      if (nodes_contributed_to_observation.size() ==\n          observations_registered_with_id) {\n        write_to_disk = true;\n        received_reduction_data =\n            std::move(reduction_data->operator[](observation_id));\n        reduction_names =\n            std::move(reduction_names_map->operator[](observation_id));\n        reduction_data->erase(observation_id);\n        reduction_names_map->erase(observation_id);\n        nodes_contributed->erase(observation_id);\n      }\n    }\n\n    if (write_to_disk) {\n      const std::lock_guard hold_lock(*reduction_file_lock);\n      // NOLINTNEXTLINE(bugprone-use-after-move)\n      received_reduction_data.finalize();\n      if constexpr (not std::is_same_v<Formatter, NoFormatter>) {\n        if (formatter.has_value()) {\n          Parallel::printf(\n              std::apply(*formatter, received_reduction_data.data()));\n        }\n      }\n      ReductionActions_detail::write_data(\n          subfile_name, observers::input_source_from_cache(cache),\n          // NOLINTNEXTLINE(bugprone-use-after-move)\n          std::move(reduction_names), std::move(received_reduction_data.data()),\n          Parallel::get<Tags::ReductionFileName>(cache),\n          std::make_index_sequence<sizeof...(ReductionDatums)>{});\n    }\n  }\n};\n\n/*!\n * \\brief Write a single row of data to the reductions file without the need to\n * register or reduce anything, e.g. from a singleton component or from a\n * specific chare.\n *\n * Use observers::Actions::ContributeReductionData instead if you need to\n * perform a reduction before writing to the file.\n *\n * Invoke this action on the observers::ObserverWriter component on node 0. Pass\n * the following arguments when invoking this action:\n *\n * - `subfile_name`: the name of the `h5::Dat` subfile in the HDF5 file. Include\n *   a leading slash, e.g., `/element_data`.\n * - `legend`: a `std::vector<std::string>` of column labels for the quantities\n *   being observed (e.g. `{\"Time\", \"L1ErrorDensity\", \"L2ErrorDensity\"}`).\n * - `reduction_data`: a `std::tuple<...>` with the data to write. The tuple can\n *   hold either `double`s or `std::vector<double>`s and is flattened before it\n *   is written to the file to form a single row of values. The total number of\n *   values must match the length of the `legend`.\n */\nstruct WriteReductionDataRow {\n  /// \\brief The apply call for the threaded action\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, typename... Ts>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> node_lock,\n                    const std::string& subfile_name,\n                    std::vector<std::string> legend,\n                    std::tuple<Ts...>&& reduction_data) {\n    apply<ParallelComponent>(box, node_lock, cache, subfile_name,\n                             std::move(legend), std::move(reduction_data));\n  }\n\n  // The local synchronous action\n  using return_type = void;\n\n  /// \\brief The apply call for the local synchronous action\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename... Ts>\n  static return_type apply(\n      db::DataBox<DbTagList>& box,\n      const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const std::string& subfile_name, std::vector<std::string> legend,\n      std::tuple<Ts...>&& reduction_data) {\n    auto& reduction_file_lock =\n        db::get_mutable_reference<Tags::H5FileLock>(make_not_null(&box));\n    const std::lock_guard hold_lock(reduction_file_lock);\n    ThreadedActions::ReductionActions_detail::write_data(\n        subfile_name, observers::input_source_from_cache(cache),\n        std::move(legend), std::move(reduction_data),\n        Parallel::get<Tags::ReductionFileName>(cache),\n        std::make_index_sequence<sizeof...(Ts)>{});\n  }\n};\n\n}  // namespace ThreadedActions\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <atomic>\n#include <converse.h>\n#include <cstddef>\n#include <memory>\n#include <set>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace observers {\n/// \\ingroup ObserversGroup\n/// %Tags used on the observer parallel component\nnamespace Tags {\n/// \\brief The set of `ArrayComponentId`s that will contribute to each\n/// `ObservationId` for reduction.\n///\n/// This is set during registration is only read from later on in the\n/// simulation, except for possibly during migration of an `ArrayComponentId`\n/// component.\nstruct ExpectedContributorsForObservations : db::SimpleTag {\n  using type =\n      std::unordered_map<ObservationKey,\n                         std::unordered_set<Parallel::ArrayComponentId>>;\n};\n\n/// \\brief The set of `ArrayComponentId` that have contributed to each\n/// `ObservationId` for reductions\n///\n/// The tag is used both on the `Observer` and on the `ObserverWriter`\n/// components since all we need to do is keep track of array component IDs in\n/// both cases.\nstruct ContributorsOfReductionData : db::SimpleTag {\n  using type =\n      std::unordered_map<ObservationId,\n                         std::unordered_set<Parallel::ArrayComponentId>>;\n};\n\n/// \\brief The set of nodes that have contributed to each `ObservationId` for\n/// writing reduction data\n///\n/// This is used on node 0 (or whichever node has been designated as the one to\n/// write the reduction files). The `unordered_set` is the node IDs that have\n/// contributed so far.\nstruct NodesThatContributedReductions : db::SimpleTag {\n  using type = std::unordered_map<ObservationId, std::unordered_set<size_t>>;\n};\n\n/// \\brief The set of nodes that are registered with each\n/// `ObservationIdRegistrationKey` for writing reduction data\n///\n/// The set contains all the nodes that have been registered.\n///\n/// We need to keep track of this separately from the local reductions on the\n/// node that are contributing so we need a separate tag. Since nodes are easily\n/// indexed by an unsigned integer, we use `size_t` to keep track of them.\nstruct NodesExpectedToContributeReductions : db::SimpleTag {\n  using type = std::unordered_map<ObservationKey, std::set<size_t>>;\n};\n\n/// \\brief Lock used when contributing reduction data.\n///\n/// A separate lock from the node lock of the nodegroup is used in order to\n/// allow other cores to contribute volume data, write to disk, etc.\nstruct ReductionDataLock : db::SimpleTag {\n  using type = Parallel::NodeLock;\n};\n\n/// \\brief The set of `ArrayComponentId` that have contributed to each\n/// `ObservationId` for volume observation\n///\n/// The tag is used both on the `Observer` and on the `ObserverWriter`\n/// components since all we need to do is keep track of array component IDs in\n/// both cases.\nstruct ContributorsOfTensorData : db::SimpleTag {\n  using type =\n      std::unordered_map<ObservationId,\n                         std::unordered_set<Parallel::ArrayComponentId>>;\n};\n\n/// Volume tensor data to be written to disk.\nstruct TensorData : db::SimpleTag {\n  using type = std::unordered_map<\n      observers::ObservationId,\n      std::unordered_map<Parallel::ArrayComponentId, ElementVolumeData>>;\n};\n\n/// Volume tensor data to be written to disk from the Interpolator\nstruct InterpolatorTensorData : db::SimpleTag {\n  using type =\n      std::unordered_map<observers::ObservationId,\n                         std::unordered_map<Parallel::ArrayComponentId,\n                                            std::vector<ElementVolumeData>>>;\n};\n\n/// Map of serialized FunctionOfTime data for volume data to be written.\nstruct SerializedFunctionsOfTime : db::SimpleTag {\n  using type =\n      std::unordered_map<ObservationId, std::optional<std::vector<char>>>;\n};\n\n/// Map of ObservationIds that have dependencies for volume data to be written\n/// and whether or not the dependency has the volume data being written or\n/// discarded.\nstruct Dependencies : db::SimpleTag {\n  using type = std::unordered_map<ObservationId, std::pair<std::string, bool>>;\n};\n\n/// \\cond\ntemplate <class... ReductionDatums>\nstruct ReductionDataNames;\n/// \\endcond\n\n/// Reduction data to be written to disk.\n///\n/// If we have `M` `ArrayComponentIds` registered with a given `Observer`\n/// for a given `ObservationId`, then we expect the `Observer` to receive\n/// `M` contributions with the given `ObservationId`. We combine the reduction\n/// data as we receive it, so we only need one copy for each `ObservationId`.\ntemplate <class... ReductionDatums>\nstruct ReductionData : db::SimpleTag {\n  using type = std::unordered_map<observers::ObservationId,\n                                  Parallel::ReductionData<ReductionDatums...>>;\n  using names_tag = ReductionDataNames<ReductionDatums...>;\n};\n\n/// Names of the reduction data to be written to disk.\ntemplate <class... ReductionDatums>\nstruct ReductionDataNames : db::SimpleTag {\n  using type =\n      std::unordered_map<observers::ObservationId, std::vector<std::string>>;\n  using data_tag = ReductionData<ReductionDatums...>;\n};\n\n/// Node lock used when needing to read/write to H5 files on disk.\n///\n/// The reason for only having one lock for all files is that we currently don't\n/// require a thread-safe HDF5 installation. In the future we will need to\n/// experiment with different HDF5 configurations.\nstruct H5FileLock : db::SimpleTag {\n  using type = Parallel::NodeLock;\n};\n\n/*!\n * \\brief A string identifying observations related to the `Tag`.\n *\n * For example, the `Tag` could refer to the block in the computational domain\n * and the `ObservationKey` holds the name of the block, so reduction\n * observations can be registered per-block. A value of `std::nullopt` means\n * that the element doesn't participate in observations related to the `Tag`.\n *\n * This tag only provides a way to store and share the observation key related\n * to the `Tag`. How it is used to construct composite observation keys or\n * subfile paths is up to the code that performs the observations.\n */\ntemplate <typename Tag>\nstruct ObservationKey : db::SimpleTag {\n  using type = std::optional<std::string>;\n  static std::string name() {\n    return \"ObservationKey(\" + pretty_type::name<Tag>() + \")\";\n  }\n};\n\n}  // namespace Tags\n\n/// \\ingroup ObserversGroup\n/// Option tags related to recording data\nnamespace OptionTags {\n/// Groups option tags related to recording data, e.g. file names.\nstruct Group {\n  static std::string name() { return \"Observers\"; }\n  static constexpr Options::String help = {\"Options for recording data\"};\n};\n\n/// The name of the H5 file on disk to which all volume data is written.\nstruct VolumeFileName {\n  using type = std::string;\n  static constexpr Options::String help = {\n      \"Name of the volume data file without extension\"};\n  using group = Group;\n};\n\n/// The name of the H5 file on disk to which all reduction data is written.\nstruct ReductionFileName {\n  using type = std::string;\n  static constexpr Options::String help = {\n      \"Name of the reduction data file without extension\"};\n  using group = Group;\n};\n\n/// The name of the H5 file on disk to which all surface data is written.\nstruct SurfaceFileName {\n  using type = std::string;\n  static constexpr Options::String help = {\n      \"Name of the surface data file without extension\"};\n  using group = Group;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\brief The name of the HDF5 file on disk into which volume data is\n/// written.\n///\n/// By volume data we mean any data that is not written once across all nodes.\n/// For example, data on a 2d surface written from a 2d simulation is\n/// considered volume data, while an integral over the entire (or a subset of\n/// the) domain is considered reduction data. Data for a 2d surface in\n/// a 3d simulation, such as a horizon, is considered surface data and\n/// is written to a file specified by SurfaceFileName.\nstruct VolumeFileName : db::SimpleTag {\n  using type = std::string;\n  using option_tags = tmpl::list<::observers::OptionTags::VolumeFileName>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::string create_from_options(const std::string& volume_file_name) {\n    return volume_file_name;\n  }\n};\n\n/// \\brief The name of the HDF5 file on disk into which reduction data is\n/// written.\n///\n/// By reduction data we mean any data that is written once across all nodes.\n/// For example, an integral over the entire (or a subset of the) domain\n/// is considered reduction data, while data on a 3d surface written from a 3d\n/// simulation is considered volume data. Data for a 2d surface in\n/// a 3d simulation, such as a horizon, is considered surface data and\n/// is written to a file specified by SurfaceFileName.\nstruct ReductionFileName : db::SimpleTag {\n  using type = std::string;\n  using option_tags = tmpl::list<::observers::OptionTags::ReductionFileName>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::string create_from_options(\n      const std::string& reduction_file_name) {\n    return reduction_file_name;\n  }\n};\n\n/// \\brief The name of the HDF5 file on disk into which surface data is\n/// written.\n///\n/// By surface data we mean data for a 2d surface in\n/// a 3d simulation, such as a horizon. Surface data is written once across\n/// all nodes. For example, data on a 2d surface written from a 2d simulation is\n/// considered volume data, while an integral over the entire (or a subset of\n/// the) domain is considered reduction data.\nstruct SurfaceFileName : db::SimpleTag {\n  using type = std::string;\n  using option_tags = tmpl::list<::observers::OptionTags::SurfaceFileName>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::string create_from_options(const std::string& surface_file_name) {\n    return surface_file_name;\n  }\n};\n}  // namespace Tags\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/TypeOfObservation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Observer/TypeOfObservation.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace observers {\nstd::ostream& operator<<(std::ostream& os, const TypeOfObservation& t) {\n  switch (t) {\n    case TypeOfObservation::Reduction:\n      return os << \"Reduction\";\n    case TypeOfObservation::Volume:\n      return os << \"Volume\";\n    default:\n      ERROR(\"Unknown TypeOfObservation.\");\n  }\n  return os;\n}\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/TypeOfObservation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace observers {\n/*!\n * \\ingroup ObserversGroup\n * \\brief Specifies the type of observation.\n *\n * Below `sender` is the component passing the data, reduction or volume, to the\n * observer component.\n */\nenum class TypeOfObservation {\n  /// The sender will only perform reduction observations\n  Reduction,\n  /// The sender will only perform volume observations\n  Volume,\n};\n\nstd::ostream& operator<<(std::ostream& os, const TypeOfObservation& t);\n\n/// @{\n/// Inherits off of `std::true_type` if `T` has a member variable\n/// `RegisterWithObserver`\ntemplate <class T, class = std::void_t<>>\nstruct has_register_with_observer : std::false_type {};\n\n/// \\cond\ntemplate <class T>\nstruct has_register_with_observer<\n    T, std::void_t<decltype(T::RegisterWithObserver)>> : std::true_type {};\n/// \\endcond\n\ntemplate <class T>\nconstexpr bool has_register_with_observer_v =\n    has_register_with_observer<T>::value;\n/// @}\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/VolumeActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Observer/VolumeActions.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace observers::ThreadedActions::VolumeActions_detail {\nvoid write_data(const std::string& h5_file_name,\n                const std::string& input_source,\n                const std::string& subfile_path,\n                const observers::ObservationId& observation_id,\n                std::vector<ElementVolumeData>&& volume_data) {\n  const uint32_t version_number = 0;\n  {\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_name + \".h5\"s, true,\n                                                  input_source};\n    auto& volume_file =\n        h5_file.try_insert<h5::VolumeData>(subfile_path, version_number);\n    volume_file.write_volume_data(observation_id.hash(), observation_id.value(),\n                                  volume_data);\n  }\n}\n}  // namespace observers::ThreadedActions::VolumeActions_detail\n"
  },
  {
    "path": "src/IO/Observer/VolumeActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <mutex>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace observers {\n/// \\cond\nnamespace ThreadedActions {\nstruct ContributeVolumeDataToWriter;\n}  // namespace ThreadedActions\n/// \\endcond\nnamespace Actions {\n\n/*!\n * \\ingroup ObserversGroup\n * \\brief Send volume tensor data to the observer.\n *\n * The caller of this Action (which is to be invoked on the Observer parallel\n * component) must pass in an `observation_id` used to uniquely identify the\n * observation in time, the name of the `h5::VolumeData` subfile in the HDF5\n * file (e.g. `/element_data`, where the slash is important), the contributing\n * parallel component element's component id, and the `ElementVolumeData`\n * to be written to disk.\n */\nstruct ContributeVolumeData {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index,\n      const observers::ObservationId& observation_id,\n      const std::string& subfile_name,\n      const Parallel::ArrayComponentId& sender_array_id,\n      ElementVolumeData&& received_volume_data,\n      const std::optional<std::vector<char>>& serialized_functions_of_time =\n          std::nullopt,\n      const std::optional<std::string>& dependency = std::nullopt) {\n    db::mutate<Tags::TensorData, Tags::ContributorsOfTensorData>(\n        [&array_index, &cache, &received_volume_data, &observation_id,\n         &sender_array_id, &subfile_name, &serialized_functions_of_time,\n         &dependency](\n            const gsl::not_null<std::unordered_map<\n                observers::ObservationId,\n                std::unordered_map<Parallel::ArrayComponentId,\n                                   ElementVolumeData>>*>\n                volume_data,\n            const gsl::not_null<std::unordered_map<\n                ObservationId, std::unordered_set<Parallel::ArrayComponentId>>*>\n                contributed_volume_data_ids,\n            const std::unordered_map<\n                ObservationKey, std::unordered_set<Parallel::ArrayComponentId>>&\n                registered_array_component_ids) {\n          const ObservationKey& key{observation_id.observation_key()};\n          if (UNLIKELY(registered_array_component_ids.find(key) ==\n                       registered_array_component_ids.end())) {\n            ERROR(\"Receiving data from observation id \"\n                  << observation_id << \" that was never registered.\");\n          }\n          const auto& registered_ids = registered_array_component_ids.at(key);\n          if (UNLIKELY(registered_ids.find(sender_array_id) ==\n                       registered_ids.end())) {\n            ERROR(\"Receiving volume data from array component id \"\n                  << sender_array_id << \" that is not registered.\");\n          }\n\n          auto& contributed_array_ids =\n              (*contributed_volume_data_ids)[observation_id];\n          if (UNLIKELY(contributed_array_ids.find(sender_array_id) !=\n                       contributed_array_ids.end())) {\n            ERROR(\"Already received volume data to observation id \"\n                  << observation_id << \" from array component id \"\n                  << sender_array_id);\n          }\n          contributed_array_ids.insert(sender_array_id);\n\n          if ((not volume_data->contains(observation_id)) or\n              (not volume_data->at(observation_id).contains(sender_array_id))) {\n            volume_data->operator[](observation_id)\n                .emplace(sender_array_id, std::move(received_volume_data));\n          } else {\n            auto& current_data =\n                volume_data->at(observation_id).at(sender_array_id);\n            if (UNLIKELY(not alg::equal(current_data.extents,\n                                        received_volume_data.extents))) {\n              ERROR(\n                  \"The extents from the same volume component at a specific \"\n                  \"observation should always be the same. For example, the \"\n                  \"extents of a dG element should be the same for all calls to \"\n                  \"ContributeVolumeData that occur at the same time.\");\n            }\n            current_data.tensor_components.insert(\n                current_data.tensor_components.end(),\n                std::make_move_iterator(\n                    received_volume_data.tensor_components.begin()),\n                std::make_move_iterator(\n                    received_volume_data.tensor_components.end()));\n          }\n\n          // Check if we have received all \"volume\" data from the registered\n          // elements. If so we copy it to the nodegroup volume writer.\n          if (contributed_array_ids.size() == registered_ids.size()) {\n            auto& local_writer = *Parallel::local_branch(\n                Parallel::get_parallel_component<ObserverWriter<Metavariables>>(\n                    cache));\n            Parallel::threaded_action<\n                ThreadedActions::ContributeVolumeDataToWriter>(\n                local_writer, observation_id,\n                Parallel::make_array_component_id<ParallelComponent>(\n                    array_index),\n                subfile_name, std::move((*volume_data)[observation_id]),\n                serialized_functions_of_time, dependency);\n            volume_data->erase(observation_id);\n            contributed_volume_data_ids->erase(observation_id);\n          }\n        },\n        make_not_null(&box),\n        db::get<Tags::ExpectedContributorsForObservations>(box));\n  }\n};\n}  // namespace Actions\n\nnamespace ThreadedActions {\nnamespace VolumeActions_detail {\nvoid write_data(const std::string& h5_file_name,\n                const std::string& input_source,\n                const std::string& subfile_path,\n                const observers::ObservationId& observation_id,\n                std::vector<ElementVolumeData>&& volume_data);\n\ntemplate <typename ParallelComponent, typename Metavariables,\n          typename VolumeDataAtObsId>\nvoid write_combined_volume_data(\n    Parallel::GlobalCache<Metavariables>& cache,\n    const observers::ObservationId& observation_id,\n    const VolumeDataAtObsId& volume_data,\n    const gsl::not_null<Parallel::NodeLock*> volume_file_lock,\n    const std::string& subfile_name,\n    const std::optional<std::vector<char>>&\n        serialized_observation_functions_of_time) {\n  ASSERT(not volume_data.empty(),\n         \"Failed to populate volume_data before trying to write it.\");\n\n  std::vector<ElementVolumeData> volume_data_to_write;\n\n  if constexpr (std::is_same_v<tmpl::at_c<VolumeDataAtObsId, 1>,\n                               ElementVolumeData>) {\n    volume_data_to_write.reserve(volume_data.size());\n    for (const auto& [id, element] : volume_data) {\n      (void)id;  // avoid compiler warnings\n      volume_data_to_write.push_back(element);\n    }\n  } else {\n    size_t total_size = 0;\n    for (const auto& [id, vec_elements] : volume_data) {\n      (void)id;  // avoid compiler warnings\n      total_size += vec_elements.size();\n    }\n    volume_data_to_write.reserve(total_size);\n\n    for (const auto& [id, vec_elements] : volume_data) {\n      (void)id;  // avoid compiler warnings\n      volume_data_to_write.insert(volume_data_to_write.end(),\n                                  vec_elements.begin(), vec_elements.end());\n    }\n  }\n\n  // Write to file. We use a separate node lock because writing can be\n  // very time consuming (it's network dependent, depends on how full the\n  // disks are, what other users are doing, etc.) and we want to be able\n  // to continue to work on the nodegroup while we are writing data to\n  // disk.\n  const std::lock_guard hold_lock(*volume_file_lock);\n  {\n    // Scoping is for closing HDF5 file before we release the lock.\n    const auto& file_prefix = Parallel::get<Tags::VolumeFileName>(cache);\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    h5::H5File<h5::AccessType::ReadWrite> h5file(\n        file_prefix +\n            std::to_string(\n                Parallel::my_node<int>(*Parallel::local_branch(my_proxy))) +\n            \".h5\",\n        true, observers::input_source_from_cache(cache));\n    constexpr size_t version_number = 0;\n    auto& volume_file =\n        h5file.try_insert<h5::VolumeData>(subfile_name, version_number);\n\n    // Serialize domain. See `Domain` docs for details on the serialization.\n    // The domain is retrieved from the global cache using the standard\n    // domain tag. If more flexibility is required here later, then the\n    // domain can be passed along with the `ContributeVolumeData` action.\n    std::optional<std::vector<char>> serialized_domain{};\n    if (not volume_file.has_domain()) {\n      serialized_domain = serialize(\n          Parallel::get<domain::Tags::Domain<Metavariables::volume_dim>>(\n              cache));\n    }\n\n    std::optional<std::vector<char>> serialized_global_functions_of_time =\n        std::nullopt;\n    if constexpr (Parallel::is_in_global_cache<Metavariables,\n                                               domain::Tags::FunctionsOfTime>) {\n      const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n      serialized_global_functions_of_time = serialize(functions_of_time);\n    }\n\n    // Write the data to the file\n    volume_file.write_volume_data(observation_id.hash(), observation_id.value(),\n                                  volume_data_to_write, serialized_domain,\n                                  serialized_observation_functions_of_time,\n                                  serialized_global_functions_of_time);\n  }\n}\n}  // namespace VolumeActions_detail\n/*!\n * \\ingroup ObserversGroup\n * \\brief Move data to the observer writer for writing to disk.\n *\n * Once data from all cores is collected this action writes the data to disk if\n * there isn't a dependency. Or if there is a dependency and it has been\n * received already. If there is a dependency but it hasn't been received yet,\n * data will be written by a call to `ContributeDependency`.\n */\nstruct ContributeVolumeDataToWriter {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const gsl::not_null<Parallel::NodeLock*> node_lock,\n      const observers::ObservationId& observation_id,\n      Parallel::ArrayComponentId observer_group_id,\n      const std::string& subfile_name,\n      std::unordered_map<Parallel::ArrayComponentId,\n                         std::vector<ElementVolumeData>>&& received_volume_data,\n      const std::optional<std::vector<char>>& serialized_functions_of_time =\n          std::nullopt,\n      const std::optional<std::string>& dependency = std::nullopt) {\n    apply_impl<Tags::InterpolatorTensorData, ParallelComponent>(\n        box, cache, node_lock, observation_id, observer_group_id, subfile_name,\n        std::move(received_volume_data), serialized_functions_of_time,\n        dependency);\n  }\n\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const gsl::not_null<Parallel::NodeLock*> node_lock,\n      const observers::ObservationId& observation_id,\n      Parallel::ArrayComponentId observer_group_id,\n      const std::string& subfile_name,\n      std::unordered_map<Parallel::ArrayComponentId, ElementVolumeData>&&\n          received_volume_data,\n      const std::optional<std::vector<char>>& serialized_functions_of_time =\n          std::nullopt,\n      const std::optional<std::string>& dependency = std::nullopt) {\n    apply_impl<Tags::TensorData, ParallelComponent>(\n        box, cache, node_lock, observation_id, observer_group_id, subfile_name,\n        std::move(received_volume_data), serialized_functions_of_time,\n        dependency);\n  }\n\n private:\n  template <typename TensorDataTag, typename ParallelComponent,\n            typename DbTagsList, typename Metavariables,\n            typename VolumeDataAtObsId>\n  static void apply_impl(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const gsl::not_null<Parallel::NodeLock*> node_lock,\n      const observers::ObservationId& observation_id,\n      Parallel::ArrayComponentId observer_group_id,\n      const std::string& subfile_name, VolumeDataAtObsId received_volume_data,\n      const std::optional<std::vector<char>>& serialized_functions_of_time,\n      const std::optional<std::string>& dependency) {\n    // The below gymnastics with pointers is done in order to minimize the\n    // time spent locking the entire node, which is necessary because the\n    // DataBox does not allow any function calls, either get and mutate, during\n    // a mutate. We separate out writing from the operations that edit the\n    // DataBox since writing to disk can be very slow, but moving data around is\n    // comparatively quick.\n    Parallel::NodeLock* volume_file_lock = nullptr;\n    bool perform_write = false;\n    VolumeDataAtObsId volume_data{};\n\n    {\n      const std::lock_guard hold_lock(*node_lock);\n\n      // Set file lock for later\n      db::mutate<Tags::H5FileLock>(\n          [&volume_file_lock](\n              const gsl::not_null<Parallel::NodeLock*> volume_file_lock_ptr) {\n            volume_file_lock = &*volume_file_lock_ptr;\n          },\n          make_not_null(&box));\n\n      ASSERT(volume_file_lock != nullptr,\n             \"Failed to set volume_file_lock in the mutate\");\n\n      const auto& observations_registered =\n          db::get<Tags::ExpectedContributorsForObservations>(box);\n\n      const ObservationKey& key = observation_id.observation_key();\n      if (LIKELY(observations_registered.contains(key))) {\n        if (UNLIKELY(not observations_registered.at(key).contains(\n                observer_group_id))) {\n          ERROR(\"The observer group id \"\n                << observer_group_id\n                << \" was not registered for the observation id \"\n                << observation_id);\n        }\n      } else {\n        ERROR(\"key \" << key\n                     << \" not in the registered group ids. Known keys are \"\n                     << keys_of(observations_registered));\n      }\n\n      const size_t observations_registered_with_id =\n          observations_registered.at(key).size();\n\n      // Ok because we have the node lock\n      auto& volume_observers_contributed =\n          db::get_mutable_reference<Tags::ContributorsOfTensorData>(\n              make_not_null(&box));\n      auto& all_volume_data =\n          db::get_mutable_reference<TensorDataTag>(make_not_null(&box));\n      auto& all_serialized_functions_of_time =\n          db::get_mutable_reference<Tags::SerializedFunctionsOfTime>(\n              make_not_null(&box));\n      auto& box_dependencies =\n          db::get_mutable_reference<Tags::Dependencies>(make_not_null(&box));\n\n      auto& contributed_group_ids =\n          volume_observers_contributed[observation_id];\n\n      if (UNLIKELY(contributed_group_ids.contains(observer_group_id))) {\n        ERROR(\"Already received reduction data to observation id \"\n              << observation_id << \" from array component id \"\n              << observer_group_id);\n      }\n      contributed_group_ids.insert(observer_group_id);\n\n      // Add received volume data to the box\n      if (all_volume_data.contains(observation_id)) {\n        auto& current_data = all_volume_data.at(observation_id);\n        current_data.insert(\n            std::make_move_iterator(received_volume_data.begin()),\n            std::make_move_iterator(received_volume_data.end()));\n        ASSERT(all_serialized_functions_of_time.at(observation_id) ==\n                   serialized_functions_of_time,\n               \"Got different serialized functions of time from different \"\n               \"elements.\");\n      } else {\n        // We haven't been called before on this processing element.\n        all_volume_data[observation_id] = std::move(received_volume_data);\n        all_serialized_functions_of_time[observation_id] =\n            serialized_functions_of_time;\n      }\n\n      // Check if we have received all \"volume\" data from the Observer\n      // group. If so we write to disk.\n      if (volume_observers_contributed.at(observation_id).size() ==\n          observations_registered_with_id) {\n        // Check if\n        //  1. there is an external dependencies\n        if (dependency.has_value()) {\n          //  2. if there is, that we have received something at this time\n          //  3. that the dependencies are the same\n          if (box_dependencies.contains(observation_id)) {\n            if (UNLIKELY(box_dependencies.at(observation_id).first !=\n                         dependency.value())) {\n              ERROR(\n                  \"The dependency that was sent to the ObserverWriter from the \"\n                  \"elements (\"\n                  << dependency.value()\n                  << \") does not match the dependency received from \"\n                     \"ContributeDependency (\"\n                  << box_dependencies.at(observation_id).first << \").\");\n            }\n            //  4. that we are writing the volume data to disk\n            if (box_dependencies.at(observation_id).second) {\n              perform_write = true;\n              volume_data = std::move(all_volume_data[observation_id]);\n            }\n\n            // Whether or not we are writing data to disk, we clean up because\n            // we have received both the volume data and the dependency\n            all_volume_data.erase(observation_id);\n            all_serialized_functions_of_time.erase(observation_id);\n            volume_observers_contributed.erase(observation_id);\n            box_dependencies.erase(observation_id);\n          }\n        } else {\n          perform_write = true;\n          volume_data = std::move(all_volume_data[observation_id]);\n          all_volume_data.erase(observation_id);\n          all_serialized_functions_of_time.erase(observation_id);\n          volume_observers_contributed.erase(observation_id);\n        }\n      }\n    }\n\n    if (perform_write) {\n      VolumeActions_detail::write_combined_volume_data<ParallelComponent>(\n          cache, observation_id, volume_data, make_not_null(volume_file_lock),\n          subfile_name, serialized_functions_of_time);\n    }\n  }\n};\n\n/*!\n * \\brief Threaded action that will add a dependency to the ObserverWriter for a\n * given ObservationId ( \\p time + \\p volume_subfile_name).\n *\n * \\details If not all the volume data for this ObservationId has been received\n * yet, then this will just add the dependency to the box and exit without\n * writing anything. If all volume data arrives before this action is called,\n * then it will write out the volume data (or remove it if we aren't writing).\n */\nstruct ContributeDependency {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> node_lock,\n                    const double time, const std::string& dependency,\n                    std::string volume_subfile_name,\n                    const bool write_volume_data) {\n    if (not volume_subfile_name.starts_with(\"/\")) {\n      volume_subfile_name = \"/\" + volume_subfile_name;\n    }\n    if (not volume_subfile_name.ends_with(\".vol\")) {\n      volume_subfile_name += \".vol\";\n    }\n\n    const ObservationId observation_id{time, volume_subfile_name};\n\n    // The below gymnastics with pointers is done in order to minimize the\n    // time spent locking the entire node, which is necessary because the\n    // DataBox does not allow any function calls, either get and mutate, during\n    // a mutate. We separate out writing from the operations that edit the\n    // DataBox since writing to disk can be very slow, but moving data around is\n    // comparatively quick.\n    Parallel::NodeLock* volume_file_lock = nullptr;\n    bool perform_write = false;\n    std::unordered_map<Parallel::ArrayComponentId, ElementVolumeData>\n        volume_data{};\n    std::optional<std::vector<char>> serialized_functions_of_time{};\n\n    // For now just hold the entire node. We can optimize with different locks\n    // later on\n    {\n      const std::lock_guard hold_lock(*node_lock);\n\n      db::mutate<Tags::H5FileLock, Tags::Dependencies>(\n          [&](const gsl::not_null<Parallel::NodeLock*> volume_file_lock_ptr,\n              const gsl::not_null<std::unordered_map<\n                  ObservationId, std::pair<std::string, bool>>*>\n                  dependencies) {\n            volume_file_lock = &*volume_file_lock_ptr;\n            (*dependencies)[observation_id] =\n                std::pair{dependency, write_volume_data};\n          },\n          make_not_null(&box));\n\n      auto& volume_observers_contributed =\n          db::get_mutable_reference<Tags::ContributorsOfTensorData>(\n              make_not_null(&box));\n      auto& all_volume_data =\n          db::get_mutable_reference<Tags::TensorData>(make_not_null(&box));\n      auto& all_serialized_functions_of_time =\n          db::get_mutable_reference<Tags::SerializedFunctionsOfTime>(\n              make_not_null(&box));\n      auto& box_dependencies =\n          db::get_mutable_reference<Tags::Dependencies>(make_not_null(&box));\n      const auto& expected_contributors =\n          db::get<Tags::ExpectedContributorsForObservations>(box);\n\n      if (not expected_contributors.contains(\n              observation_id.observation_key())) {\n        ERROR(\"Key \" << observation_id.observation_key()\n                     << \" was not registered.\");\n      }\n\n      // We have not received any volume data at this time so we can't do\n      // anything\n      if (not(volume_observers_contributed.contains(observation_id) and\n              all_volume_data.contains(observation_id))) {\n        return;\n      }\n\n      // Check if we have received all \"volume\" data from the Observer\n      // group. If so we write to disk. Then always delete it since the volume\n      // data was waiting for this dependency to arrive to be written\n      if (volume_observers_contributed.at(observation_id).size() ==\n          expected_contributors.at(observation_id.observation_key()).size()) {\n        if (write_volume_data) {\n          perform_write = true;\n          volume_data = std::move(all_volume_data.at(observation_id));\n          serialized_functions_of_time =\n              std::move(all_serialized_functions_of_time.at(observation_id));\n        }\n\n        all_volume_data.erase(observation_id);\n        all_serialized_functions_of_time.erase(observation_id);\n        volume_observers_contributed.erase(observation_id);\n        box_dependencies.erase(observation_id);\n      }\n    }\n\n    if (perform_write) {\n      VolumeActions_detail::write_combined_volume_data<ParallelComponent>(\n          cache, observation_id, volume_data, make_not_null(volume_file_lock),\n          volume_subfile_name, serialized_functions_of_time);\n    }\n  }\n};\n\n/*!\n * \\brief Write volume data (such as surface data) at a given time (specified by\n * an `ObservationId`) without the need to register or reduce anything, e.g.\n * from a singleton component or from a specific chare.\n *\n * Use `observers::Actions::ContributeVolumeDataToWriter` instead if you need to\n * write volume data from an array chare (e.g., writing volume data from all the\n * elements in a domain).\n *\n * Invoke this action on the `observers::ObserverWriter` component on node 0.\n * Pass the following arguments when invoking this action:\n *\n * - `h5_file_name`: the name of the HDF5 file where the volume data is to be\n * written (without the .h5 extension).\n * - `subfile_path`: the path where the volume data should be written in the\n *   HDF5 file. Include a leading slash, e.g., `/AhA`.\n * - `observation_id`: the ObservationId corresponding to the volume data.\n * - `volume_data`: the volume data to be written.\n */\nstruct WriteVolumeData {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, typename... Ts,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const std::string& h5_file_name,\n                    const std::string& subfile_path,\n                    const observers::ObservationId& observation_id,\n                    std::vector<ElementVolumeData>&& volume_data) {\n    auto& volume_file_lock =\n        db::get_mutable_reference<Tags::H5FileLock>(make_not_null(&box));\n    const std::lock_guard hold_lock(volume_file_lock);\n    VolumeActions_detail::write_data(\n        h5_file_name, observers::input_source_from_cache(cache), subfile_path,\n        observation_id, std::move(volume_data));\n  }\n\n  // For a local synchronous action\n  using return_type = void;\n\n  /// \\brief The apply call for the local synchronous action\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename... Ts,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static return_type apply(\n      db::DataBox<DbTagsList>& box,\n      const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const std::string& h5_file_name, const std::string& subfile_path,\n      const observers::ObservationId& observation_id,\n      std::vector<ElementVolumeData>&& volume_data) {\n    auto& volume_file_lock =\n        db::get_mutable_reference<Tags::H5FileLock>(make_not_null(&box));\n    const std::lock_guard hold_lock(volume_file_lock);\n    VolumeActions_detail::write_data(\n        h5_file_name, observers::input_source_from_cache(cache), subfile_path,\n        observation_id, std::move(volume_data));\n  }\n};\n}  // namespace ThreadedActions\n\n/*!\n * \\brief Contribute volume data for observing from an element.\n *\n * \\tparam UseObserverComponent Whether to first send data to the\n * `observers::Observer` group component.  Generally should be true if\n * using an implementation where elements are bound to cores, and\n * false if they are only bound to nodes.\n */\ntemplate <bool UseObserverComponent, typename Metavariables>\nvoid contribute_volume_data(\n    Parallel::GlobalCache<Metavariables>& cache,\n    observers::ObservationId observation_id, std::string subfile_path,\n    const Parallel::ArrayComponentId& array_component_id,\n    ElementVolumeData element_volume_data,\n    std::optional<std::string> dependency = std::nullopt) {\n  std::optional<std::vector<char>> serialized_observation_functions_of_time{};\n  if constexpr (Parallel::is_in_global_cache<Metavariables,\n                                             domain::Tags::FunctionsOfTime>) {\n    const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n    // NOLINTNEXTLINE(misc-const-correctness)\n    domain::FunctionsOfTimeMap observation_functions_of_time{};\n    const double obs_time = observation_id.value();\n    // Generally, the functions of time should be valid when we\n    // perform an observation.  The exception is when running in an\n    // AtCleanup event, in which case the observation time is a\n    // bogus value and we just skip writing the values.\n    if (alg::all_of(functions_of_time, [&](const auto& fot) {\n          const auto bounds = fot.second->time_bounds();\n          return bounds[0] <= obs_time and obs_time <= bounds[1];\n        })) {\n      // create a new function of time, effectively truncating the history.\n      for (const auto& [name, fot_ptr] : functions_of_time) {\n        observation_functions_of_time[name] = fot_ptr->create_at_time(\n            obs_time, obs_time + 100.0 *\n                                     std::numeric_limits<double>::epsilon() *\n                                     std::max(std::abs(obs_time), 1.0));\n      }\n      serialized_observation_functions_of_time =\n          serialize(observation_functions_of_time);\n    }\n  }\n\n  if constexpr (UseObserverComponent) {\n    // Send data to volume observer\n    auto& local_observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<observers::Observer<Metavariables>>(\n            cache));\n\n    Parallel::simple_action<observers::Actions::ContributeVolumeData>(\n        local_observer, std::move(observation_id), std::move(subfile_path),\n        array_component_id, std::move(element_volume_data),\n        std::move(serialized_observation_functions_of_time),\n        std::move(dependency));\n  } else {\n    // Send data to reduction observer writer (nodegroup)\n    auto& local_observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            observers::ObserverWriter<Metavariables>>(cache));\n\n    std::unordered_map<Parallel::ArrayComponentId,\n                       std::vector<ElementVolumeData>>\n        data_to_send{};\n    data_to_send[array_component_id] =\n        make_vector(std::move(element_volume_data));\n    Parallel::threaded_action<\n        observers::ThreadedActions::ContributeVolumeDataToWriter>(\n        local_observer, std::move(observation_id), array_component_id,\n        std::move(subfile_path), std::move(data_to_send),\n        std::move(serialized_observation_functions_of_time),\n        std::move(dependency));\n  }\n}\n}  // namespace observers\n"
  },
  {
    "path": "src/IO/Observer/WriteSimpleData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace observers {\nnamespace ThreadedActions {\n\n/*!\n * \\brief Append data to an h5::Dat subfile of `Tags::VolumeFileName`.\n *\n * \\details This is a streamlined interface for getting data to the volume file\n * associated with a node; it will simply write the `.dat` object\n * `subfile_name`, giving it the `file_legend` if it does not yet exist,\n * appending `data_row` to the end of the dat.\n */\nstruct WriteSimpleData {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> node_lock,\n                    const std::vector<std::string>& file_legend,\n                    const std::vector<double>& data_row,\n                    const std::string& subfile_name) {\n    Parallel::NodeLock* file_lock = nullptr;\n    {\n      const std::lock_guard hold_lock(*node_lock);\n      db::mutate<Tags::H5FileLock>(\n          [&file_lock](const gsl::not_null<Parallel::NodeLock*> in_file_lock) {\n            file_lock = in_file_lock;\n          },\n          make_not_null(&box));\n    }\n\n    // scoped to close file\n    {\n      const std::lock_guard hold_lock(*file_lock);\n      const auto& file_prefix = Parallel::get<Tags::VolumeFileName>(cache);\n      auto& my_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      h5::H5File<h5::AccessType::ReadWrite> h5file(\n          file_prefix +\n              std::to_string(\n                  Parallel::my_node<int>(*Parallel::local_branch(my_proxy))) +\n              \".h5\",\n          true, observers::input_source_from_cache(cache));\n      const size_t version_number = 0;\n      auto& output_dataset =\n          h5file.try_insert<h5::Dat>(subfile_name, file_legend, version_number);\n      output_dataset.append(data_row);\n      h5file.close_current_object();\n    }\n  }\n};\n}  // namespace ThreadedActions\n}  // namespace observers\n"
  },
  {
    "path": "src/Informer/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Informer)\n\nadd_spectre_library(${LIBRARY})\n\n# Note that this library has two special source files: InfoAtCompile.cpp and\n# InfoAtLink.cpp. These files contain placeholder strings that CMake fills in\n# (in copies of the files in the build directory) when configuring the build.\n#\n# We track the *generated* InfoAtCompile.cpp as a library source. This means\n# that CMake must have been configured *before* checking library dependencies\n# via the CMake function check_spectre_libs_dependencies.\n#\n# We do *not* track InfoAtLink.cpp as a library source, as it is used only to\n# inject information late in the build process. The InfoAtLink target defined\n# below determines the approriate flags for compiling InfoAtLink.cpp.\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Informer.cpp\n  ${CMAKE_BINARY_DIR}/Informer/InfoAtCompile.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InfoFromBuild.hpp\n  Informer.hpp\n  )\n\n# The Darwin linker is not OK with undefined symbols at link time of the\n# library, so we need to appease it. This is needed so compiling InfoAtLink.cpp\n# can be deferred until we link an executable.\nif(CMAKE_VERSION GREATER_EQUAL 3.13)\n  target_link_options(\n    ${LIBRARY}\n    PUBLIC\n    \"$<$<PLATFORM_ID:Darwin>:-Wl,-U,_git_branch>\"\n    \"$<$<PLATFORM_ID:Darwin>:-Wl,-U,_git_description>\"\n    \"$<$<PLATFORM_ID:Darwin>:-Wl,-U,_link_date>\"\n    \"$<$<PLATFORM_ID:Darwin>:-Wl,-U,_executable_name>\"\n    )\nendif()\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ErrorHandling\n  Printf\n  SystemUtilities\n  )\n\n# Define the `InfoAtLink` target to determine the appropriate flags for\n# compiling `InfoAtLink.cpp`. The Python script stores the flags in the file\n# `${CMAKE_BINARY_DIR}/Informer/InfoAtLink_flags.txt` so they can be used for\n# compiling `InfoAtLink.cpp` at link time (see `WrapExecutableLinker.sh`).\nconfigure_file(\n  ${CMAKE_SOURCE_DIR}/src/Informer/InfoAtLinkExtractFlags.py\n  ${CMAKE_BINARY_DIR}/Informer/InfoAtLinkExtractFlags.py\n  )\nset(INFO_AT_LINK_LIB InfoAtLink)\nadd_library(${INFO_AT_LINK_LIB} OBJECT InfoAtLink.cpp)\ntarget_link_libraries(\n  ${INFO_AT_LINK_LIB}\n  PRIVATE\n  Boost::boost\n  )\nset_target_properties(\n  ${INFO_AT_LINK_LIB}\n  PROPERTIES\n  CXX_COMPILER_LAUNCHER\n  \"${Python_EXECUTABLE};${CMAKE_BINARY_DIR}/Informer/InfoAtLinkExtractFlags.py\"\n  )\nif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)\n  set_target_properties(\n    ${INFO_AT_LINK_LIB}\n    PROPERTIES\n    ADDITIONAL_CLEAN_FILES\n    ${CMAKE_BINARY_DIR}/Informer/InfoAtLink_flags.txt\n    )\nendif()\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/Informer/InfoAtCompile.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is configured into the build directory by CMake and compiled as\n// part of the build system. It exposes information from CMake to C++.\n\n#include \"@CMAKE_SOURCE_DIR@/src/Informer/InfoFromBuild.hpp\"\n\n#include <sstream>\n#include <string>\n\nstd::string spectre_version() { return std::string(\"@SPECTRE_VERSION@\"); }\n\nstd::string unit_test_build_path() { return \"@CMAKE_BINARY_DIR@/tests/Unit/\"; }\n\nstd::string unit_test_src_path() { return \"@CMAKE_SOURCE_DIR@/tests/Unit/\"; }\n\nstd::string info_from_build() {\n  std::ostringstream os;\n  os << \"SpECTRE Build Information:\\n\";\n  os << \"Version:                      \" << spectre_version() << \"\\n\";\n  os << \"Compiled on host:             @HOSTNAME@\\n\";\n  os << \"Compiled in directory:        @CMAKE_BINARY_DIR@\\n\";\n  os << \"Source directory is:          @CMAKE_SOURCE_DIR@\\n\";\n  os << \"Compiled on git branch:       \" << git_branch() << \"\\n\";\n  os << \"Compiled on git revision:     \" << git_description() << \"\\n\";\n  os << \"Linked on:                    \" << link_date() << \"\\n\";\n#ifdef SPECTRE_DEBUG\n  os << \"Build type:                   Debug\\n\";\n#else\n  os << \"Build type:                   Release\\n\";\n#endif\n  return os.str();\n}\n\nstd::string copyright_and_license_info() {\n  return std::string{R\"(\n@SPECTRE_LICENSE_CXX_STRING@\n)\"} + std::string{R\"(\n@SPECTRE_3PL_CXX_STRING0@\n)\"} + std::string{R\"(\n@SPECTRE_3PL_CXX_STRING1@\n)\"};\n}\n\n#ifndef __APPLE__\n/* Set up a pretty print script for GDB to print spectre types in GDB in a\n * more readable manner.\n *\n *\n * Note: The \"MS\" section flags are to remove duplicates.\n */\n#define DEFINE_GDB_PY_SCRIPT(script_name) \\\n  asm(\"\\\n.pushsection \\\".debug_gdb_scripts\\\", \\\"MS\\\",@progbits,1\\n\\\n.byte 1 /* Python */\\n\\\n.asciz \\\"\" script_name                    \\\n      \"\\\"\\n\\\n.popsection \\n\\\n\");\n\nDEFINE_GDB_PY_SCRIPT(\"@CMAKE_SOURCE_DIR@/tools/SpectrePrettyPrinters.py\")\n\n#undef DEFINE_GDB_PY_SCRIPT\n#endif // ndef __APPLE__\n"
  },
  {
    "path": "src/Informer/InfoAtLink.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is compiled into every executable at link time, so the information\n// that these functions provide reflects the time the executable was linked\n// instead of the time that CMake was last run.\n\n#include \"Informer/InfoFromBuild.hpp\"\n\n#include <boost/preprocessor.hpp>\n#include <string>\n\nstd::string link_date() { return BOOST_PP_STRINGIZE(LINK_TIME); }\n\nstd::string executable_name() { return BOOST_PP_STRINGIZE(EXECUTABLE_NAME); }\n\nstd::string git_description() { return BOOST_PP_STRINGIZE(GIT_DESCRIPTION); }\n\nstd::string git_branch() { return BOOST_PP_STRINGIZE(GIT_BRANCH); }\n"
  },
  {
    "path": "src/Informer/InfoAtLinkExtractFlags.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\"\"\"Extract appropriate compiler flags for compiling InfoAtLink.cpp\n\nThis script is invoked as if it was the compiler, compiling an object file from\nthe `InfoAtLink.cpp` source file. It extracts the flags and stores them in the\nfile `@CMAKE_BINARY_DIR@/Informer/InfoAtLink_flags.txt`. To compile\n`InfoAtLink.cpp` at link time, read the flags from this file and pass them to\nthe compiler.\n\"\"\"\n\nimport argparse\n\nparser = argparse.ArgumentParser(description=__doc__)\n# The first positional argument is the compiler\nparser.add_argument(\"compiler\")\n# Strip the object file name\nparser.add_argument(\"-o\", required=True)\n# Strip stage selection for an object file\nparser.add_argument(\"-c\", action=\"store_true\")\n# Strip depfile flags\nparser.add_argument(\"-MD\", action=\"store_true\")\nparser.add_argument(\"-MT\", required=False)\nparser.add_argument(\"-MF\", required=False)\n# Strip `-Werror` so `-fuse-ld=gold` compiler warnings from clang don't disrupt\n# the linking. See issue: https://github.com/sxs-collaboration/spectre/issues/2703\nparser.add_argument(\"-Werror\", nargs=\"?\")\n# Parse the CLI args, discarding the arguments specified above and extracting\n# the remaining compiler flags\nargs, compiler_flags = parser.parse_known_args()\n# Discard the source file name\ncompiler_flags.pop()\n# Write the extracted flags out to a file\noutput_filename = \"@CMAKE_BINARY_DIR@/Informer/InfoAtLink_flags.txt\"\nwith open(output_filename, \"w\") as output_file:\n    output_file.write(\" \".join(compiler_flags))\n# Write a stub object file so CMake only re-runs this script when the\n# build configuration changed.\nopen(args.o, \"w\").close()\n"
  },
  {
    "path": "src/Informer/InfoFromBuild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares function info_from_build.\n\n#pragma once\n\n#include <string>\n\n/*!\n * \\ingroup LoggingGroup\n * \\brief Information about the version, date, host, git commit, and link time\n *\n * The information returned by this function is invaluable for identifying\n * the version of the code used in a simulation, as well as which host, the\n * date the code was compiled, and the time of linkage.\n */\nstd::string info_from_build();\n\n/*!\n * \\ingroup LoggingGroup\n * \\brief Returns a string containing the copyright and license info for\n * SpECTRE and its dependencies.\n */\nstd::string copyright_and_license_info();\n\n// We declare these functions `extern \"C\"` so their symbols are not mangled and\n// we can ignore that they are undefined until link time (see `CMakeLists.txt`).\n// Note that the `std::string` return type is not compatible with C, but that's\n// OK as long as we're not calling or defining the functions in C. See\n// Microsoft's C4190:\n// https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4190\n#if defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wreturn-type-c-linkage\"\n#endif  // defined(__clang__)\n/*!\n * \\ingroup LoggingGroup\n * \\brief The time and date the executable was linked\n */\nextern \"C\" std::string link_date();\n/*!\n * \\ingroup LoggingGroup\n * \\brief Name of the linked executable\n */\nextern \"C\" std::string executable_name();\n/*!\n * \\ingroup LoggingGroup\n * \\brief The git description at the time the executable was linked\n */\nextern \"C\" std::string git_description();\n/*!\n * \\ingroup LoggingGroup\n * \\brief The git branch at the time the executable was linked\n */\nextern \"C\" std::string git_branch();\n#if defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__clang__)\n\n/*!\n * \\ingroup LoggingGroup\n * \\brief Retrieve a string containing the current version of SpECTRE\n */\nstd::string spectre_version();\n\n/*!\n * \\ingroup LoggingGroup\n * \\brief Returns the path to the Unit test directory.\n */\nstd::string unit_test_src_path();\n\n/*!\n * \\ingroup LoggingGroup\n * \\brief Returns the path to the Unit test directory in the build directory.\n */\nstd::string unit_test_build_path();\n"
  },
  {
    "path": "src/Informer/Informer.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Informer/Informer.hpp\"\n\n#include <charm++.h>\n#include <charm.h>\n#include <sstream>\n\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nvoid Informer::print_startup_info(CkArgMsg* msg) {\n  std::stringstream ss{};\n  for (int i = 0; i < msg->argc - 1; i++) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    ss << msg->argv[i] << \" \";\n  }\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  ss << msg->argv[msg->argc - 1];\n\n  Parallel::printf(\n      \"\\n\"\n      \"Executing '%s' using %d processors.\\n\"\n      \"Launch command line: %s\\n\"\n      \"Charm++ startup time in seconds: %f\\n\"\n      \"Date and time at startup: %s\\n\",\n      executable_name(), sys::number_of_procs(),\n      ss.str(),  // NOLINT\n      sys::wall_time(), current_date_and_time());\n\n  Parallel::printf(\"%s\\n\", info_from_build());\n}\n\nvoid Informer::print_exit_info() {\n  Parallel::printf(\n      \"\\n\"\n      \"Done!\\n\"\n      \"Wall time: %s\\n\"\n      \"Date and time at completion: %s\\n\",\n      sys::pretty_wall_time(), current_date_and_time());\n}\n"
  },
  {
    "path": "src/Informer/Informer.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class Informer.\n\n#pragma once\n\n/// \\cond\nclass CkArgMsg;\n/// \\endcond\n\n/// \\ingroup LoggingGroup\n/// The Informer manages textual output regarding the status of a simulation.\nclass Informer {\n public:\n  /// Print useful information at the beginning of a simulation.\n  ///\n  /// This includes the command used to start the executable such as\n  ///\n  /// ```\n  /// ./MyExecutable --input-file MyInputFile.yaml\n  /// ```\n  ///\n  /// If you used charmrun, mpirun, or something similar to start your\n  /// executable, you'll only see the options that have to do with the\n  /// executable itself. Meaning, for this command\n  ///\n  /// ```\n  /// mpirun -np 4 MyExecutable --input-file MyInputFile.yaml\n  /// ```\n  ///\n  /// only `MyExecutable` and onwards will be printed.\n  static void print_startup_info(CkArgMsg* msg);\n\n  /// Print useful information at the end of a simulation.\n  static void print_exit_info();\n};\n"
  },
  {
    "path": "src/Informer/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"Informer/Python/InfoAtCompile.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py_bindings::bind_info_at_compile(m);\n}\n"
  },
  {
    "path": "src/Informer/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyInformer\")\n\nspectre_python_add_module(\n  Informer\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  InfoAtCompile.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InfoAtCompile.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Informer\n  pybind11::module\n  )\n"
  },
  {
    "path": "src/Informer/Python/InfoAtCompile.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Informer/Python/InfoAtCompile.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"Informer/InfoFromBuild.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nvoid bind_info_at_compile(py::module& m) {\n  // Wrapper to make Build Info available from python\n  m.def(\"spectre_version\", &spectre_version);\n  m.def(\"unit_test_src_path\", &unit_test_src_path);\n  m.def(\"unit_test_build_path\", &unit_test_build_path);\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/Informer/Python/InfoAtCompile.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_info_at_compile(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/Informer/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/NumericalAlgorithms/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Convergence)\nadd_subdirectory(DiscontinuousGalerkin)\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Integration)\nadd_subdirectory(Interpolation)\nadd_subdirectory(LinearAlgebra)\nadd_subdirectory(LinearOperators)\nadd_subdirectory(LinearSolver)\nadd_subdirectory(OdeIntegration)\nadd_subdirectory(RootFinding)\nadd_subdirectory(SpatialDiscretization)\nadd_subdirectory(Spectral)\nadd_subdirectory(SphericalHarmonics)\nadd_subdirectory(SpinWeightedSphericalHarmonics)\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Convergence)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Criteria.cpp\n  HasConverged.cpp\n  Reason.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Convergence.hpp\n  Criteria.hpp\n  HasConverged.hpp\n  Reason.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Options\n  Serialization\n  Utilities\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/Convergence.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Documents the `Convergence` namespace\n\n#pragma once\n\n/// Items related to checking the convergence of numerical algorithms\nnamespace Convergence {}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/Criteria.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n\n#include <pup.h>\n\nnamespace Convergence {\n\nCriteria::Criteria(const size_t max_iterations_in,\n                   const double absolute_residual_in,\n                   const double relative_residual_in)\n    : max_iterations(max_iterations_in),\n      absolute_residual(absolute_residual_in),\n      relative_residual(relative_residual_in) {}\n\nvoid Criteria::pup(PUP::er& p) {\n  p | max_iterations;\n  p | absolute_residual;\n  p | relative_residual;\n}\n\nbool operator==(const Criteria& lhs, const Criteria& rhs) {\n  return lhs.max_iterations == rhs.max_iterations and\n         lhs.absolute_residual == rhs.absolute_residual and\n         lhs.relative_residual == rhs.relative_residual;\n}\n\nbool operator!=(const Criteria& lhs, const Criteria& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Convergence\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/Criteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Convergence {\n\n/*!\n * \\brief Criteria that determine an iterative algorithm has converged\n *\n * \\details Most criteria are based on a residual magnitude\n * \\f$r_k\\f$ after completion of an iteration \\f$k\\f$ (see, for instance, the\n * \\ref LinearSolverGroup documentation, `LinearSolver::Tags::Residual` and\n * `LinearSolver::Tags::Magnitude`).\n *\n * The following criteria are implemented, ordered from highest to lowest\n * priority:\n *\n * - AbsoluteResidual: Matches if the residual has reached this magnitude.\n * - RelativeResidual: Matches if the residual has decreased by this factor,\n * relative to the start of the first iteration.\n * - MaxIterations: Matches if the number of iterations exceeds this limit.\n */\nstruct Criteria {\n  static constexpr Options::String help =\n      \"The algorithm terminates when any of these criteria is matched.\";\n\n  struct MaxIterations {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The number of iterations exceeds this limit.\"};\n    static type lower_bound() { return 0; }\n  };\n\n  struct AbsoluteResidual {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The residual has reached this magnitude.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  struct RelativeResidual {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The residual has decreased by this factor.\"};\n    static type lower_bound() { return 0.; }\n    static type upper_bound() { return 1.; }\n  };\n\n  using options = tmpl::list<MaxIterations, AbsoluteResidual, RelativeResidual>;\n\n  Criteria() = default;\n  Criteria(size_t max_iterations_in, double absolute_residual_in,\n           double relative_residual_in);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  size_t max_iterations{};\n  double absolute_residual{};\n  double relative_residual{};\n};\n\nbool operator==(const Criteria& lhs, const Criteria& rhs);\nbool operator!=(const Criteria& lhs, const Criteria& rhs);\n\n}  // namespace Convergence\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/HasConverged.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <string>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace Convergence {\n\nstd::optional<Reason> criteria_match(const Criteria& criteria,\n                                     const size_t iteration_id,\n                                     const double residual_magnitude,\n                                     const double initial_residual_magnitude) {\n  if (residual_magnitude <= criteria.absolute_residual) {\n    return Reason::AbsoluteResidual;\n  }\n  if (residual_magnitude / initial_residual_magnitude <=\n      criteria.relative_residual) {\n    return Reason::RelativeResidual;\n  }\n  if (iteration_id >= criteria.max_iterations) {\n    return Reason::MaxIterations;\n  }\n  return std::nullopt;\n}\n\nHasConverged::HasConverged(const Criteria& criteria, const size_t iteration_id,\n                           const double residual_magnitude,\n                           const double initial_residual_magnitude)\n    : reason_(criteria_match(criteria, iteration_id, residual_magnitude,\n                             initial_residual_magnitude)),\n      criteria_(criteria),\n      iteration_id_(iteration_id),\n      residual_magnitude_(residual_magnitude),\n      initial_residual_magnitude_(initial_residual_magnitude) {}\n\nHasConverged::HasConverged(const size_t num_iterations,\n                           const size_t iteration_id)\n    : reason_(iteration_id >= num_iterations\n                  ? std::optional(Reason::NumIterations)\n                  : std::nullopt),\n      // Store the target num iterations in the convergence criteria's\n      // max_iterations\n      criteria_(num_iterations, 0, 0),\n      iteration_id_(iteration_id) {}\n\nHasConverged::HasConverged(Reason reason,\n                           std::optional<std::string> error_message,\n                           const size_t iteration_id)\n    : reason_(reason),\n      error_message_(std::move(error_message)),\n      iteration_id_(iteration_id) {\n  ASSERT(reason_ == Reason::Error,\n         \"Only allowed to construct HasConverged state manually with \"\n         \"'Reason::Error'. Use the other constructors for the other reasons.\");\n}\n\nReason HasConverged::reason() const {\n  ASSERT(reason_,\n         \"Tried to retrieve the convergence reason, but has not yet converged. \"\n         \"Check if the instance of HasConverged evaluates to `true` first.\");\n  return *reason_;\n}\n\nconst std::string& HasConverged::error_message() const {\n  ASSERT(error_message_.has_value(),\n         \"Tried to retrieve the error message, but the convergence reason is \"\n         \"not 'Error'. Check the `reason()` before calling this function.\");\n  return *error_message_;\n}\n\nsize_t HasConverged::num_iterations() const { return iteration_id_; }\n\ndouble HasConverged::residual_magnitude() const { return residual_magnitude_; }\n\ndouble HasConverged::initial_residual_magnitude() const {\n  return initial_residual_magnitude_;\n}\n\nvoid HasConverged::check_for_error() const {\n  if (reason_ == Reason::Error) {\n    ERROR_AS(error_message(), convergence_error);\n  }\n}\n\nstd::ostream& operator<<(std::ostream& os, const HasConverged& has_converged) {\n  if (has_converged) {\n    switch (has_converged.reason()) {\n      case Reason::NumIterations:\n        return os << \"Reached the target number of iterations (\"\n                  // The target num iterations is internally stored in the\n                  // max_iterations field\n                  << has_converged.criteria_.max_iterations << \").\";\n      case Reason::MaxIterations:\n        return os << \"Reached the maximum number of iterations (\"\n                  << has_converged.criteria_.max_iterations << \").\";\n      case Reason::AbsoluteResidual:\n        return os\n               << \"AbsoluteResidual - The residual magnitude has decreased to \"\n               << get_output(has_converged.criteria_.absolute_residual)\n               << \" or below (\" << get_output(has_converged.residual_magnitude_)\n               << \").\";\n      case Reason::RelativeResidual:\n        return os << \"RelativeResidual - The residual magnitude has decreased \"\n                     \"to a fraction of \"\n                  << get_output(has_converged.criteria_.relative_residual)\n                  << \" of its initial value or below (\"\n                  << get_output(has_converged.residual_magnitude_ /\n                                has_converged.initial_residual_magnitude_)\n                  << \").\";\n      case Reason::Error:\n        return os << *has_converged.error_message_;\n      default:\n        ERROR(\"Unknown convergence reason\");\n    };\n  } else {\n    return os << \"Not yet converged.\";\n  }\n}\n\nvoid HasConverged::pup(PUP::er& p) {\n  p | reason_;\n  p | error_message_;\n  p | criteria_;\n  p | iteration_id_;\n  p | residual_magnitude_;\n  p | initial_residual_magnitude_;\n}\n\nbool operator==(const HasConverged& lhs, const HasConverged& rhs) {\n  return lhs.reason_ == rhs.reason_ and\n         lhs.error_message_ == rhs.error_message_ and\n         lhs.criteria_ == rhs.criteria_ and\n         lhs.iteration_id_ == rhs.iteration_id_ and\n         (lhs.residual_magnitude_ == rhs.residual_magnitude_ or\n          (std::isnan(lhs.residual_magnitude_) and\n           std::isnan(rhs.residual_magnitude_))) and\n         (lhs.initial_residual_magnitude_ == rhs.initial_residual_magnitude_ or\n          (std::isnan(lhs.initial_residual_magnitude_) and\n           std::isnan(rhs.initial_residual_magnitude_)));\n}\n\nbool operator!=(const HasConverged& lhs, const HasConverged& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Convergence\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/HasConverged.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iosfwd>\n#include <limits>\n#include <optional>\n#include <string>\n\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Convergence {\n\n/*!\n * \\brief Determine whether the `criteria` are met.\n *\n * \\note This function assumes the `iteration_id` is that of the latest\n * completed step and that it is zero-indexed, where zero indicates the initial\n * state of the algorithm. Therefore, the `MaxIteration` criterion will match if\n * the `iteration_id` is equal or higher. For example, a `MaxIteration` of 0\n * means the algorithm should run no iterations, so it matches if the\n * `iteration_id` is 0 or higher since that's the initial state before any\n * steps have been performed. A `MaxIteration` of 1 matches if the\n * `iteration_id` is 1 or higher since one iteration is complete. At this point,\n * also the `residual_magnitude` reflects the state of the algorithm after\n * completion of the first iteration. The `initial_residual_magnitude` always\n * refers to the state before the first iteration has begun, i.e. where the\n * `iteration_id` is zero.\n *\n * \\returns A `Convergence::Reason` if the criteria are met, or\n * `std::nullopt` otherwise. The possible convergence reasons are:\n *\n * - `Convergence::Reason::AbsoluteResidual` if the `residual_magnitude`\n *   meets the convergence criteria's `absolute_residual`.\n * - `Convergence::Reason::RelativeResidual` if `residual_magnitude /\n *   initial_residual_magnitude` meets the convergence criteria's\n *   `relative_residual`.\n * - `Convergence::Reason::MaxIterations` if the `iteration_id` is the\n *   convergence_criteria's `max_iterations` or higher. This is often\n *   interpreted as an error because the algorithm did not converge in the\n *   alloted number of iterations.\n */\nstd::optional<Reason> criteria_match(const Criteria& criteria,\n                                     size_t iteration_id,\n                                     double residual_magnitude,\n                                     double initial_residual_magnitude);\n\n/*!\n * \\brief Signals convergence or termination of the algorithm.\n *\n * \\details Evaluates to `true` if the algorithm has converged or terminated and\n * no further iterations should be performed. In this case, the `reason()`\n * member function provides more information. If `false`, calling `reason()` is\n * an error.\n *\n * The stream operator provides a human-readable description of the convergence\n * status.\n *\n * This type default-constructs to a state that signals the algorithm has\n * not yet converged.\n */\nstruct HasConverged {\n public:\n  HasConverged() = default;\n  /*!\n   * \\brief Determine whether the \\p criteria are met by means of\n   * `Convergence::criteria_match`.\n   */\n  HasConverged(const Criteria& criteria, size_t iteration_id,\n               double residual_magnitude, double initial_residual_magnitude);\n\n  /// Construct at a state where `iteration_id` iterations of a total of\n  /// `num_iterations` have completed. Use when the algorithm is intended to run\n  /// for a fixed number of iterations. The convergence `reason()` will be\n  /// `Convergence::Reason::NumIterations`.\n  HasConverged(size_t num_iterations, size_t iteration_id);\n\n  /*!\n   * \\brief Construct a state manually.\n   *\n   * Currently only allows to construct a state with `Reason::Error`. Use the\n   * other constructors for the other `Reason`s.\n   */\n  HasConverged(Reason reason, std::optional<std::string> error_message,\n               size_t iteration_id);\n\n  explicit operator bool() const { return static_cast<bool>(reason_); }\n\n  /*!\n   * \\brief The reason the algorithm has converged.\n   *\n   * \\warning Calling this function is an error if the algorithm has not yet\n   * converged.\n   */\n  Reason reason() const;\n\n  /*!\n   * \\brief Error message if reason is `Reason::Error`\n   *\n   * \\warning You may only call this function if the `reason()` is\n   * `Reason::Error`.\n   */\n  const std::string& error_message() const;\n\n  /// The number of iterations the algorithm has completed\n  size_t num_iterations() const;\n\n  /// The residual magnitude after the last iteration. NaN if no iteration has\n  /// completed yet.\n  double residual_magnitude() const;\n\n  /// The residual magnitude before the first iteration. NaN if this information\n  /// is not available yet.\n  double initial_residual_magnitude() const;\n\n  /// Throw an exception if the `reason()` is `Reason::Error`.\n  void check_for_error() const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  friend bool operator==(const HasConverged& lhs, const HasConverged& rhs);\n  friend bool operator!=(const HasConverged& lhs, const HasConverged& rhs);\n\n  friend std::ostream& operator<<(std::ostream& os,\n                                  const HasConverged& has_converged);\n\n private:\n  std::optional<Reason> reason_{};\n  std::optional<std::string> error_message_ = std::nullopt;\n  Criteria criteria_{};\n  size_t iteration_id_{};\n  double residual_magnitude_ = std::numeric_limits<double>::quiet_NaN();\n  double initial_residual_magnitude_ = std::numeric_limits<double>::quiet_NaN();\n};\n\n}  // namespace Convergence\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/Reason.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace Convergence {\n\nstd::ostream& operator<<(std::ostream& os, const Reason& reason) {\n  switch (reason) {\n    case Reason::NumIterations:\n      return os << \"NumIterations\";\n    case Reason::MaxIterations:\n      return os << \"MaxIterations\";\n    case Reason::AbsoluteResidual:\n      return os << \"AbsoluteResidual\";\n    case Reason::RelativeResidual:\n      return os << \"RelativeResidual\";\n    case Reason::Error:\n      return os << \"Error\";\n    default:\n      ERROR(\"Unknown convergence reason\");\n  }\n}\n\n}  // namespace Convergence\n\ntemplate <>\nConvergence::Reason\nOptions::create_from_yaml<Convergence::Reason>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (type_read == get_output(Convergence::Reason::NumIterations)) {\n    return Convergence::Reason::NumIterations;\n  } else if (type_read == get_output(Convergence::Reason::MaxIterations)) {\n    return Convergence::Reason::MaxIterations;\n  } else if (type_read == get_output(Convergence::Reason::AbsoluteResidual)) {\n    return Convergence::Reason::AbsoluteResidual;\n  } else if (type_read == get_output(Convergence::Reason::RelativeResidual)) {\n    return Convergence::Reason::RelativeResidual;\n  } else if (type_read == get_output(Convergence::Reason::Error)) {\n    return Convergence::Reason::Error;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read << \"\\\" to Convergence::Reason. Must be one of: '\"\n                  << get_output(Convergence::Reason::NumIterations) << \"', '\"\n                  << get_output(Convergence::Reason::MaxIterations) << \"', '\"\n                  << get_output(Convergence::Reason::AbsoluteResidual) << \"', '\"\n                  << get_output(Convergence::Reason::RelativeResidual) << \"', '\"\n                  << get_output(Convergence::Reason::Error) << \"'.\");\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/Reason.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace Convergence {\n\n/*!\n * \\brief The reason the algorithm has converged or terminated.\n *\n * \\see Convergence::Criteria\n */\nenum class Reason {\n  /// Reached the target number of iterations\n  NumIterations,\n  /// Reached the maximum number of iterations. Can be interpreted as an error\n  /// condition.\n  MaxIterations,\n  /// Residual converged below absolute tolerance\n  AbsoluteResidual,\n  /// Residual converged below relative tolerance\n  RelativeResidual,\n  /// An error occurred during the algorithm\n  Error\n};\n\nstd::ostream& operator<<(std::ostream& os, const Reason& reason);\n\n}  // namespace Convergence\n\ntemplate <>\nstruct Options::create_from_yaml<Convergence::Reason> {\n  template <typename Metavariables>\n  static Convergence::Reason create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nConvergence::Reason\nOptions::create_from_yaml<Convergence::Reason>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/NumericalAlgorithms/Convergence/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace Convergence {\n/// Option tags related to the convergence of iterative algorithms\nnamespace OptionTags {\n\ntemplate <typename OptionsGroup>\nstruct Criteria {\n  static std::string name() { return \"ConvergenceCriteria\"; }\n  static constexpr Options::String help =\n      \"Determine convergence of the algorithm\";\n  using type = Convergence::Criteria;\n  using group = OptionsGroup;\n};\n\ntemplate <typename OptionsGroup>\nstruct Iterations {\n  static constexpr Options::String help =\n      \"Number of iterations to run the algorithm\";\n  using type = size_t;\n  using group = OptionsGroup;\n};\n\n}  // namespace OptionTags\n\n/// Tags related to the convergence of iterative algorithms\nnamespace Tags {\n\n/// `Convergence::Criteria` that determine the iterative algorithm has converged\ntemplate <typename OptionsGroup>\nstruct Criteria : db::SimpleTag {\n  static std::string name() {\n    return \"ConvergenceCriteria(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = Convergence::Criteria;\n\n  using option_tags = tmpl::list<OptionTags::Criteria<OptionsGroup>>;\n  static constexpr bool pass_metavariables = false;\n  static Convergence::Criteria create_from_options(\n      const Convergence::Criteria& convergence_criteria) {\n    return convergence_criteria;\n  }\n};\n\n/// A fixed number of iterations to run the iterative algorithm\ntemplate <typename OptionsGroup>\nstruct Iterations : db::SimpleTag {\n  static std::string name() {\n    return \"Iterations(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = size_t;\n\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::Iterations<OptionsGroup>>;\n  static size_t create_from_options(const size_t max_iterations) {\n    return max_iterations;\n  }\n};\n\n/// Identifies a step in an iterative algorithm\ntemplate <typename Label>\nstruct IterationId : db::SimpleTag {\n  static std::string name() {\n    return \"IterationId(\" + pretty_type::name<Label>() + \")\";\n  }\n  using type = size_t;\n};\n\n/*!\n * \\brief Holds a `Convergence::HasConverged` flag that signals the iterative\n * algorithm has converged, along with the reason for convergence.\n */\ntemplate <typename Label>\nstruct HasConverged : db::SimpleTag {\n  static std::string name() {\n    return \"HasConverged(\" + pretty_type::name<Label>() + \")\";\n  }\n  using type = Convergence::HasConverged;\n};\n\n}  // namespace Tags\n}  // namespace Convergence\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n\n#include <complex>\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace dg::detail {\n\ntemplate <typename ValueType, size_t Dim>\nvoid apply_mass_matrix_impl(const gsl::not_null<ValueType*> data,\n                            const Mesh<Dim>& mesh) {\n  if constexpr (Dim == 0) {\n    // Nothing to do\n    (void)data;\n    (void)mesh;\n  } else if constexpr (Dim == 1) {\n    const size_t x_size = mesh.extents(0);\n    const auto& w_x = Spectral::quadrature_weights(mesh);\n    for (size_t i = 0; i < x_size; ++i) {\n      data.get()[i] *= w_x[i];\n    }\n  } else if constexpr (Dim == 2) {\n    const size_t x_size = mesh.extents(0);\n    const size_t y_size = mesh.extents(1);\n    const auto& w_x = Spectral::quadrature_weights(mesh.slice_through(0));\n    const auto& w_y = Spectral::quadrature_weights(mesh.slice_through(1));\n    for (size_t j = 0; j < y_size; ++j) {\n      const size_t offset = j * x_size;\n      for (size_t i = 0; i < x_size; ++i) {\n        data.get()[offset + i] *= w_x[i] * w_y[j];\n      }\n    }\n  } else if constexpr (Dim == 3) {\n    const size_t x_size = mesh.extents(0);\n    const size_t y_size = mesh.extents(1);\n    const size_t z_size = mesh.extents(2);\n    const auto& w_x = Spectral::quadrature_weights(mesh.slice_through(0));\n    const auto& w_y = Spectral::quadrature_weights(mesh.slice_through(1));\n    const auto& w_z = Spectral::quadrature_weights(mesh.slice_through(2));\n    for (size_t k = 0; k < z_size; ++k) {\n      const size_t offset_z = k * y_size * x_size;\n      for (size_t j = 0; j < y_size; ++j) {\n        const double w_yz = w_y[j] * w_z[k];\n        const size_t offset = x_size * j + offset_z;\n        for (size_t i = 0; i < x_size; ++i) {\n          data.get()[offset + i] *= w_x[i] * w_yz;\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename ValueType, size_t Dim>\nvoid apply_inverse_mass_matrix_impl(const gsl::not_null<ValueType*> data,\n                                    const Mesh<Dim>& mesh) {\n  if constexpr (Dim == 0) {\n    // Nothing to do\n    (void)data;\n    (void)mesh;\n  } else if constexpr (Dim == 1) {\n    const size_t x_size = mesh.extents(0);\n    const auto& w_x = Spectral::quadrature_weights(mesh);\n    for (size_t i = 0; i < x_size; ++i) {\n      data.get()[i] /= w_x[i];\n    }\n  } else if constexpr (Dim == 2) {\n    const size_t x_size = mesh.extents(0);\n    const size_t y_size = mesh.extents(1);\n    const auto& w_x = Spectral::quadrature_weights(mesh.slice_through(0));\n    const auto& w_y = Spectral::quadrature_weights(mesh.slice_through(1));\n    for (size_t j = 0; j < y_size; ++j) {\n      const size_t offset = j * x_size;\n      for (size_t i = 0; i < x_size; ++i) {\n        data.get()[offset + i] /= w_x[i] * w_y[j];\n      }\n    }\n  } else if constexpr (Dim == 3) {\n    const size_t x_size = mesh.extents(0);\n    const size_t y_size = mesh.extents(1);\n    const size_t z_size = mesh.extents(2);\n    const auto& w_x = Spectral::quadrature_weights(mesh.slice_through(0));\n    const auto& w_y = Spectral::quadrature_weights(mesh.slice_through(1));\n    const auto& w_z = Spectral::quadrature_weights(mesh.slice_through(2));\n    for (size_t k = 0; k < z_size; ++k) {\n      const size_t offset_z = k * y_size * x_size;\n      for (size_t j = 0; j < y_size; ++j) {\n        const double w_yz = w_y[j] * w_z[k];\n        const size_t offset = x_size * j + offset_z;\n        for (size_t i = 0; i < x_size; ++i) {\n          data.get()[offset + i] /= w_x[i] * w_yz;\n        }\n      }\n    }\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template void apply_mass_matrix_impl(gsl::not_null<DTYPE(data)*>,         \\\n                                       const Mesh<DIM(data)>&);             \\\n  template void apply_inverse_mass_matrix_impl(gsl::not_null<DTYPE(data)*>, \\\n                                               const Mesh<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, std::complex<double>),\n                        (0, 1, 2, 3))\n\n#undef DTYPE\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace dg::detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace dg {\n\nnamespace detail {\ntemplate <typename ValueType, size_t Dim>\nvoid apply_mass_matrix_impl(gsl::not_null<ValueType*> data,\n                            const Mesh<Dim>& mesh);\ntemplate <typename ValueType, size_t Dim>\nvoid apply_inverse_mass_matrix_impl(gsl::not_null<ValueType*> data,\n                                    const Mesh<Dim>& mesh);\n}  // namespace detail\n\n/*!\n * \\brief Apply the DG mass matrix to the data, in the diagonal mass-matrix\n * approximation (\"mass-lumping\")\n *\n * The DG mass matrix is:\n *\n * \\f{equation}\n * M_{pq} = \\int_{\\Omega_k} \\psi_p(\\xi) \\psi_q(\\xi) \\mathrm{d}V\n * \\f}\n *\n * where \\f$\\psi_p(\\xi)\\f$ are the basis functions on the element\n * \\f$\\Omega_k\\f$. In the diagonal mass-matrix approximation (\"mass-lumping\") we\n * evaluate the integral directly on the collocation points, i.e. with a Gauss\n * or Gauss-Lobatto quadrature determined by the element mesh. Then it reduces\n * to:\n *\n * \\f{equation}\n * M_{pq} \\approx \\delta_{pq} \\prod_{i=1}^d w_{p_i}\n * \\f}\n *\n * where \\f$d\\f$ is the spatial dimension and \\f$w_{p_i}\\f$ are the quadrature\n * weights in dimension \\f$i\\f$. To apply the mass matrix in coordinates\n * different than logical, or to account for a curved background metric, the\n * data can be pre-multiplied with the Jacobian determinant and/or a metric\n * determinant.\n *\n * \\note The mass-lumping is exact on Legendre-Gauss meshes, but omits a\n * correction term on Legendre-Gauss-Lobatto meshes.\n */\n/// @{\ntemplate <typename DataType, size_t Dim>\nvoid apply_mass_matrix(const gsl::not_null<DataType*> data,\n                       const Mesh<Dim>& mesh) {\n  ASSERT(data->size() == mesh.number_of_grid_points(),\n         \"The data has size \" << data->size() << \", but expected size \"\n                              << mesh.number_of_grid_points()\n                              << \" on the given mesh.\");\n  detail::apply_mass_matrix_impl(make_not_null(data->data()), mesh);\n}\n\ntemplate <size_t Dim, typename TagsList>\nvoid apply_mass_matrix(const gsl::not_null<Variables<TagsList>*> data,\n                       const Mesh<Dim>& mesh) {\n  const size_t num_points = data->number_of_grid_points();\n  ASSERT(num_points == mesh.number_of_grid_points(),\n         \"The Variables data has \"\n             << num_points << \" grid points, but expected \"\n             << mesh.number_of_grid_points() << \" on the given mesh.\");\n  constexpr size_t num_comps =\n      Variables<TagsList>::number_of_independent_components;\n  for (size_t i = 0; i < num_comps; ++i) {\n    detail::apply_mass_matrix_impl(make_not_null(data->data() + i * num_points),\n                                   mesh);\n  }\n}\n/// @}\n\n/*!\n * \\brief Apply the inverse DG mass matrix to the data\n *\n * \\see dg::apply_mass_matrix\n */\n/// @{\ntemplate <typename DataType, size_t Dim>\nvoid apply_inverse_mass_matrix(const gsl::not_null<DataType*> data,\n                               const Mesh<Dim>& mesh) {\n  ASSERT(data->size() == mesh.number_of_grid_points(),\n         \"The data has size \" << data->size() << \", but expected size \"\n                              << mesh.number_of_grid_points()\n                              << \" on the given mesh.\");\n  detail::apply_inverse_mass_matrix_impl(make_not_null(data->data()), mesh);\n}\n\ntemplate <size_t Dim, typename TagsList>\nvoid apply_inverse_mass_matrix(const gsl::not_null<Variables<TagsList>*> data,\n                               const Mesh<Dim>& mesh) {\n  const size_t num_points = data->number_of_grid_points();\n  ASSERT(num_points == mesh.number_of_grid_points(),\n         \"The Variables data has \"\n             << num_points << \" grid points, but expected \"\n             << mesh.number_of_grid_points() << \" on the given mesh.\");\n  constexpr size_t num_comps =\n      Variables<TagsList>::number_of_independent_components;\n  for (size_t i = 0; i < num_comps; ++i) {\n    detail::apply_inverse_mass_matrix_impl(\n        make_not_null(data->data() + i * num_points), mesh);\n  }\n}\n/// @}\n\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DiscontinuousGalerkin)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ApplyMassMatrix.cpp\n  Formulation.cpp\n  InterpolateFromBoundary.cpp\n  LiftFromBoundary.cpp\n  MetricIdentityJacobian.cpp\n  MortarHelpers.cpp\n  MortarInterpolator.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApplyMassMatrix.hpp\n  Formulation.hpp\n  HasReceivedFromAllMortars.hpp\n  InterpolateFromBoundary.hpp\n  LiftFlux.hpp\n  LiftFromBoundary.hpp\n  MetricIdentityJacobian.hpp\n  MortarHelpers.hpp\n  MortarInterpolator.hpp\n  NormalDotFlux.hpp\n  ProjectToBoundary.hpp\n  SimpleBoundaryData.hpp\n  SimpleMortarData.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  Domain\n  DomainStructure\n  ErrorHandling\n  Interpolation\n  Options\n  Serialization\n  Spectral\n  Utilities\n  INTERFACE\n  Domain\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/Formulation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace dg {\nstd::ostream& operator<<(std::ostream& os, const Formulation t) {\n  switch (t) {\n    case Formulation::StrongInertial:\n      return os << \"StrongInertial\";\n    case Formulation::WeakInertial:\n      return os << \"WeakInertial\";\n    case Formulation::StrongLogical:\n      return os << \"StrongLogical\";\n    default:\n      ERROR(\"Unknown DG formulation.\");\n  }\n}\n}  // namespace dg\n\ntemplate <>\ndg::Formulation Options::create_from_yaml<dg::Formulation>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (\"StrongInertial\" == type_read) {\n    return dg::Formulation::StrongInertial;\n  } else if (\"WeakInertial\" == type_read) {\n    return dg::Formulation::WeakInertial;\n  } else if (\"StrongLogical\" == type_read) {\n    return dg::Formulation::StrongLogical;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read\n                  << \"\\\" to dg::Formulation. Must be one of \"\n                     \"StrongInertial, WeakInertial, or StrongLogical.\");\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace dg {\n/*!\n * \\ingroup DiscontinuousGalerkinGroup\n * \\brief The DG formulation to use\n *\n * - The `StrongInertial` formulation is also known as the integrate then\n *   transform formulation. The \"Inertial\" part of the name refers to the fact\n *   that the integration is done over the physical/inertial coordinates, while\n *   the \"strong\" part refers to the fact that the boundary correction terms are\n *   zero if the solution is continuous at the interfaces.\n *   See \\cite Teukolsky2015ega for an overview.\n * - The `WeakInertial` formulation is also known as the integrate then\n *   transform formulation. The \"Inertial\" part of the name refers to the fact\n *   that the integration is done over the physical/inertial coordinates, while\n *   the \"weak\" part refers to the fact that the boundary correction terms are\n *   non-zero even if the solution is continuous at the interfaces.\n *   See \\cite Teukolsky2015ega for an overview.\n * - The `StrongLogical` formulation is also known as the transform then\n *   integrate formulation. The \"logical\" part of the name refers to the fact\n *   that the integration is done over the logical coordinates, while the\n *   \"strong\" part refers to the fact that the boundary correction terms are\n *   zero if the solution is continuous at the interfaces. This formulation\n *   arises from the `StrongInertial` formulation by moving the Jacobians that\n *   appear when computing the divergence of fluxes into the divergence so the\n *   divergence is computed in logical coordinates:\n *   \\begin{equation}\n *     \\partial_i F^i = \\frac{1}{J} \\partial_\\hat{\\imath} J F^\\hat{\\imath}\n *   \\end{equation}\n *   where $J$ is the Jacobian determinant and $\\hat{\\imath}$ are the logical\n *   coordinates. This is possible because of the \"metric identities\"\n *   \\begin{equation}\n *    \\partial_\\hat{\\imath} \\left(J \\frac{\\partial \\xi^\\hat{\\imath}}\n *    {\\partial x^i}\\right) = 0.\n *   \\end{equation}\n *   See also `dg::metric_identity_det_jac_times_inv_jac` for details and for\n *   functions that compute the Jacobians so they satisfy the metric identities\n *   numerically (which may or may not be useful or necessary).\n */\nenum class Formulation { StrongInertial, WeakInertial, StrongLogical };\n\nstd::ostream& operator<<(std::ostream& os, Formulation t);\n}  // namespace dg\n\n/// \\cond\ntemplate <>\nstruct Options::create_from_yaml<dg::Formulation> {\n  template <typename Metavariables>\n  static dg::Formulation create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\ndg::Formulation Options::create_from_yaml<dg::Formulation>::create<void>(\n    const Options::Option& options);\n/// \\endcond\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n\nnamespace dg {\n\n/*!\n * \\ingroup DiscontinuousGalerkinGroup\n * \\brief Determines if data on all mortars has been received for the `InboxTag`\n * at time `temporal_id`.\n *\n * The `InboxTag` must hold a container indexed by the `TemporalIdType`,\n * representing received neighbor data at a given time. Each element in the\n * container, i.e. the received neighbor data at a given time, must be another\n * container indexed by the `dg::MortarId<Dim>`. The value it holds is not\n * relevant for this function. Here's an example for such a type:\n *\n * \\snippet Test_HasReceivedFromAllMortars.cpp inbox_example\n */\ntemplate <typename InboxTag, size_t Dim, typename TemporalIdType,\n          typename... InboxTags>\nbool has_received_from_all_mortars(\n    const TemporalIdType& temporal_id, const Element<Dim>& element,\n    const tuples::TaggedTuple<InboxTags...>& inboxes) {\n  if (UNLIKELY(element.number_of_neighbors() == 0)) {\n    return true;\n  }\n  const auto& inbox = tuples::get<InboxTag>(inboxes);\n  const auto temporal_received = inbox.find(temporal_id);\n  if (temporal_received == inbox.end()) {\n    return false;\n  }\n  const auto& received_neighbor_data = temporal_received->second;\n  for (const auto& direction_and_neighbors : element.neighbors()) {\n    const auto& direction = direction_and_neighbors.first;\n    for (const auto& neighbor : direction_and_neighbors.second) {\n      const auto neighbor_received =\n          received_neighbor_data.find(MortarId<Dim>{direction, neighbor});\n      if (neighbor_received == received_neighbor_data.end()) {\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/InterpolateFromBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/InterpolateFromBoundary.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/StripeIterator.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace dg::detail {\nnamespace {\n// We use a separate function in the  xi direction to avoid the expensive\n// SliceIterator\nvoid interpolate_dt_terms_gauss_points_impl_xi_dir(\n    const gsl::not_null<double*> volume_dt_vars,\n    const size_t num_independent_components, const size_t num_volume_pts,\n    const size_t num_boundary_pts,\n    const gsl::span<const double>& dt_corrections,\n    const DataVector& boundary_interpolation_term) {\n  DataVector volume_dt_vars_view{};\n  for (size_t component_index = 0; component_index < num_independent_components;\n       ++component_index) {\n    const size_t stripe_size = num_volume_pts / num_boundary_pts;\n    for (size_t boundary_index = 0; boundary_index < num_boundary_pts;\n         ++boundary_index) {\n      volume_dt_vars_view.set_data_ref(volume_dt_vars.get() +\n                                           component_index * num_volume_pts +\n                                           boundary_index * stripe_size,\n                                       stripe_size);\n      volume_dt_vars_view +=\n          boundary_interpolation_term *\n          dt_corrections[component_index * num_boundary_pts + boundary_index];\n    }\n  }\n}\n}  // namespace\n\ntemplate <size_t Dim>\nvoid interpolate_dt_terms_gauss_points_impl(\n    const gsl::not_null<double*> volume_dt_vars,\n    const size_t num_independent_components, const Mesh<Dim>& volume_mesh,\n    const size_t dimension, const size_t num_boundary_pts,\n    const gsl::span<const double>& dt_corrections,\n    const DataVector& boundary_interpolation_term) {\n  const size_t num_volume_pts = volume_mesh.number_of_grid_points();\n  if (dimension == 0) {\n    interpolate_dt_terms_gauss_points_impl_xi_dir(\n        volume_dt_vars, num_independent_components, num_volume_pts,\n        num_boundary_pts, dt_corrections, boundary_interpolation_term);\n    return;\n  }\n\n  // Developer note: A potential optimization is to re-order (not transpose!)\n  // the volume time derivative for all variables before lifting, so that the\n  // lifting can be done using vectorized math and DataVectors as views. It\n  // would need to be tested whether that actually increases performance.\n  // Another alternative would be to use a SIMD library, such as nsimd or xsimd.\n\n  const size_t stripe_size = volume_mesh.extents(dimension);\n  size_t boundary_index = 0;\n  for (StripeIterator si{volume_mesh.extents(), dimension}; si;\n       (void)++si, (void)++boundary_index) {\n    // Loop over each stripe in this logical direction. This is effectively\n    // looping over each boundary grid point.\n    for (size_t component_index = 0;\n         component_index < num_independent_components; ++component_index) {\n      for (size_t index_on_stripe = 0; index_on_stripe < stripe_size;\n           ++index_on_stripe) {\n        const size_t volume_index = si.offset() + si.stride() * index_on_stripe;\n        volume_dt_vars.get()[component_index * num_volume_pts + volume_index] +=\n            boundary_interpolation_term[index_on_stripe] *\n            dt_corrections[component_index * num_boundary_pts + boundary_index];\n      }\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(r, data)                                                 \\\n  template void interpolate_dt_terms_gauss_points_impl(                      \\\n      gsl::not_null<double*> volume_dt_vars,                                 \\\n      size_t num_independent_components, const Mesh<DIM(data)>& volume_mesh, \\\n      size_t dimension, size_t num_boundary_pts,                             \\\n      const gsl::span<const double>& dt_corrections,                         \\\n      const DataVector& boundary_interpolation_term);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace dg::detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/InterpolateFromBoundary.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationTerm.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace dg {\nnamespace detail {\ntemplate <size_t Dim>\nvoid interpolate_dt_terms_gauss_points_impl(\n    gsl::not_null<double*> volume_dt_vars, size_t num_independent_components,\n    const Mesh<Dim>& volume_mesh, size_t dimension, size_t num_boundary_pts,\n    const gsl::span<const double>& dt_corrections,\n    const DataVector& boundary_interpolation_term);\n}  // namespace detail\n\n/*!\n * \\brief Interpolate the Bjorhus/time derivative corrections to the volume time\n * derivatives in the specified direction.\n *\n * The general interpolation term (for the \\f$+\\xi\\f$-dimension) is:\n *\n * \\f{align*}{\n *   \\partial_t u_{\\alpha\\breve{\\imath}\\breve{\\jmath}\\breve{k}}=\\cdots\n *   +\\ell^{\\mathrm{Gauss-Lobatto}}_{N}\n *    \\left(\\xi_{\\breve{\\imath}}^{\\mathrm{Gauss}}\\right)\n *    \\partial_t u^{\\mathrm{BC}}_{\\alpha\\breve{\\jmath}\\breve{k}},\n * \\f}\n *\n * where \\f$\\breve{\\imath}\\f$, \\f$\\breve{\\jmath}\\f$, and \\f$\\breve{k}\\f$ are\n * indices in the logical \\f$\\xi\\f$, \\f$\\eta\\f$, and \\f$\\zeta\\f$ dimensions.\n * \\f$\\partial_t u^{\\mathrm{BC}}\\f$ is the time derivative correction, and\n * the  function Spectral::boundary_interpolation_term() is used to compute and\n * cache the terms from the lifting terms.\n */\ntemplate <size_t Dim, typename DtTagsList>\nvoid interpolate_dt_terms_gauss_points(\n    const gsl::not_null<Variables<DtTagsList>*> dt_vars,\n    const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction,\n    const Variables<DtTagsList>& dt_corrections) {\n  ASSERT(alg::all_of(volume_mesh.quadrature(),\n                     [](const Spectral::Quadrature quadrature) {\n                       return quadrature == Spectral::Quadrature::Gauss;\n                     }),\n         \"Must use Gauss points in all directions but got the mesh: \"\n             << volume_mesh);\n  const Mesh<Dim - 1> boundary_mesh =\n      volume_mesh.slice_away(direction.dimension());\n  const Mesh<1> volume_stripe_mesh =\n      volume_mesh.slice_through(direction.dimension());\n  const size_t num_boundary_grid_points = boundary_mesh.number_of_grid_points();\n  detail::interpolate_dt_terms_gauss_points_impl(\n      make_not_null(dt_vars->data()), dt_vars->number_of_independent_components,\n      volume_mesh, direction.dimension(), num_boundary_grid_points,\n      gsl::make_span(dt_corrections.data(), dt_corrections.size()),\n      direction.side() == Side::Upper\n          ? Spectral::boundary_interpolation_term(volume_stripe_mesh).second\n          : Spectral::boundary_interpolation_term(volume_stripe_mesh).first);\n}\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function lift_flux.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace dg {\n/// @{\n/// \\ingroup DiscontinuousGalerkinGroup\n/// \\brief Lifts the flux contribution from an interface to the volume.\n///\n/// The lifting operation takes the (d-1)-dimensional flux term at the\n/// interface and computes the corresponding d-dimensional term in the\n/// volume. SpECTRE implements an efficient DG method in which each\n/// interface grid point contributes only to that same grid point of the\n/// volume.\n///\n/// \\details\n/// SpECTRE implements a DG method with a diagonalized mass matrix (also\n/// known as a mass-lumping scheme). This choice gives a large\n/// reduction in the computational cost of the lifting operation, however,\n/// the scheme is slightly less accurate, especially when the grid is\n/// deformed by non-trivial Jacobians. For more details on the\n/// diagonalization of the mass matrix and its implications,\n/// \\cite Teukolsky2015ega, especially Section 3.\n///\n/// This function is implemented to handle Legendre, Chebyshev, and Zernike\n/// bases.\n///\n/// \\note The result is still provided only on the boundary grid.  The\n/// values away from the boundary are zero and are not stored.\ntemplate <typename... BoundaryCorrectionTags>\nvoid lift_flux(\n    const gsl::not_null<Variables<tmpl::list<BoundaryCorrectionTags...>>*>\n        boundary_correction_terms,\n    const size_t extent_perpendicular_to_boundary,\n    const Scalar<DataVector>& magnitude_of_face_normal,\n    const Spectral::Basis basis = Spectral::Basis::Legendre) {\n  if (basis == Spectral::Basis::Legendre) {\n    // For an Nth degree basis (i.e., one with N+1 basis functions), the LGL\n    // weights are:\n    //   w_i = 2 / ((N + 1) * N * (P_{N}(xi_i))^2)\n    // and so at the end points (xi = +/- 1) we get:\n    //   w_0 = 2 / ((N + 1) * N)\n    //   w_N = 2 / ((N + 1) * N)\n    // The negative sign comes from bringing the boundary correction over to the\n    // RHS of the equal sign (e.g. `du/dt=Source - div Flux - boundary corr`),\n    // while the magnitude of the normal vector above accounts for the ratios of\n    // spatial metrics and Jacobians.\n    *boundary_correction_terms *=\n        -0.5 *\n        static_cast<double>((extent_perpendicular_to_boundary *\n                             (extent_perpendicular_to_boundary - 1))) *\n        get(magnitude_of_face_normal);\n  } else if (basis == Spectral::Basis::Chebyshev) {\n    // Chebyshev with GaussLobotto, with N grid points, has the weight at\n    // the upper and lower sides\n    // w_0 = 1 / ((N - 1)^2 - a)\n    // w_N = 1 / ((N - 1)^2 - a)\n    // where a = 0 if N is even, else it is 1\n    // The negative sign follows from the Legendre discussion\n    *boundary_correction_terms *=\n        -static_cast<double>((extent_perpendicular_to_boundary - 1) *\n                                 (extent_perpendicular_to_boundary - 1) -\n                             extent_perpendicular_to_boundary % 2) *\n        get(magnitude_of_face_normal);\n  } else if (basis == Spectral::Basis::ZernikeB1) {\n    // ZernikeB1 with GaussRadauUpper, with N grid points, has the weight at\n    // the upper side\n    // w_N = 1 / (N * (2 * N - 1))\n    // The negative sign follows from the Legendre discussion\n    *boundary_correction_terms *=\n        -1.0 *\n        static_cast<double>((extent_perpendicular_to_boundary *\n                             (2 * extent_perpendicular_to_boundary - 1))) *\n        get(magnitude_of_face_normal);\n  } else if (basis == Spectral::Basis::ZernikeB2) {\n    // ZernikeB2 with GaussRadauUpper, with N grid points, has the weight at\n    // the upper side\n    // w_N = 1 / (2 * N^2)\n    // The negative sign follows from the Legendre discussion\n    *boundary_correction_terms *=\n        -1.0 *\n        static_cast<double>((2 * extent_perpendicular_to_boundary *\n                             extent_perpendicular_to_boundary)) *\n        get(magnitude_of_face_normal);\n  } else if (basis == Spectral::Basis::ZernikeB3) {\n    // ZernikeB3 with GaussRadauUpper, with N grid points, has the weight at\n    // the upper side\n    // w_N = 1 / (N * (2 * N + 1))\n    // The negative sign follows from the Legendre discussion\n    *boundary_correction_terms *=\n        -1.0 *\n        static_cast<double>((extent_perpendicular_to_boundary *\n                             (2 * extent_perpendicular_to_boundary + 1))) *\n        get(magnitude_of_face_normal);\n  } else {\n    ERROR(\"Got unexpected basis \" << basis);\n  }\n}\n\ntemplate <typename... FluxTags>\nauto lift_flux(Variables<tmpl::list<FluxTags...>> flux,\n               const size_t extent_perpendicular_to_boundary,\n               const Scalar<DataVector>& magnitude_of_face_normal,\n               const Spectral::Basis basis = Spectral::Basis::Legendre)\n    -> Variables<tmpl::list<db::remove_tag_prefix<FluxTags>...>> {\n  Variables<tmpl::list<db::remove_tag_prefix<FluxTags>...>> lifted_data(\n      std::move(flux));\n  lift_flux(make_not_null(&lifted_data), extent_perpendicular_to_boundary,\n            magnitude_of_face_normal, basis);\n  return lifted_data;\n}\n/// @}\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/StripeIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace dg::detail {\nnamespace {\n// We use a separate function in the  xi direction to avoid the expensive\n// SliceIterator\ntemplate <typename ValueType>\nvoid lift_boundary_terms_gauss_points_impl_xi_dir(\n    const gsl::not_null<ValueType*> volume_dt_vars,\n    const size_t num_independent_components, const size_t num_volume_pts,\n    const Scalar<DataVector>& volume_det_inv_jacobian,\n    const size_t num_boundary_pts,\n    const gsl::span<const ValueType>& boundary_corrections,\n    const DataVector& boundary_lifting_term,\n    const Scalar<DataVector>& magnitude_of_face_normal,\n    const Scalar<DataVector>& face_det_jacobian) {\n  using VectorType =\n      tmpl::conditional_t<std::is_same_v<ValueType, std::complex<double>>,\n                          ComplexDataVector, DataVector>;\n  VectorType volume_dt_vars_view{};\n  DataVector volume_inv_det_jacobian_view{};\n  for (size_t component_index = 0; component_index < num_independent_components;\n       ++component_index) {\n    const size_t stripe_size = num_volume_pts / num_boundary_pts;\n    for (size_t boundary_index = 0; boundary_index < num_boundary_pts;\n         ++boundary_index) {\n      volume_dt_vars_view.set_data_ref(volume_dt_vars.get() +\n                                           component_index * num_volume_pts +\n                                           boundary_index * stripe_size,\n                                       stripe_size);\n      // safe const_cast since used as a view\n      volume_inv_det_jacobian_view.set_data_ref(\n          // NOLINTNEXTLINE\n          const_cast<double*>(get(volume_det_inv_jacobian).data()) +\n              boundary_index * stripe_size,\n          stripe_size);\n      // Minus sign because we brought this from the LHS to the RHS.\n      volume_dt_vars_view -=\n          volume_inv_det_jacobian_view * boundary_lifting_term *\n          get(face_det_jacobian)[boundary_index] *\n          get(magnitude_of_face_normal)[boundary_index] *\n          boundary_corrections[component_index * num_boundary_pts +\n                               boundary_index];\n    }\n  }\n}\n\n// We use a separate function in the  xi direction to avoid the expensive\n// SliceIterator\ntemplate <typename ValueType>\nvoid lift_boundary_terms_gauss_points_impl_xi_dir(\n    const gsl::not_null<ValueType*> volume_dt_vars,\n    const size_t num_independent_components, const size_t num_volume_pts,\n    const Scalar<DataVector>& volume_det_inv_jacobian,\n    const size_t num_boundary_pts,\n    const gsl::span<const ValueType>& upper_boundary_corrections,\n    const DataVector& upper_boundary_lifting_term,\n    const Scalar<DataVector>& upper_magnitude_of_face_normal,\n    const Scalar<DataVector>& upper_face_det_jacobian,\n    const gsl::span<const ValueType>& lower_boundary_corrections,\n    const DataVector& lower_boundary_lifting_term,\n    const Scalar<DataVector>& lower_magnitude_of_face_normal,\n    const Scalar<DataVector>& lower_face_det_jacobian) {\n  using VectorType =\n      tmpl::conditional_t<std::is_same_v<ValueType, std::complex<double>>,\n                          ComplexDataVector, DataVector>;\n  VectorType volume_dt_vars_view{};\n  DataVector volume_inv_det_jacobian_view{};\n  for (size_t component_index = 0; component_index < num_independent_components;\n       ++component_index) {\n    const size_t stripe_size = num_volume_pts / num_boundary_pts;\n    for (size_t boundary_index = 0; boundary_index < num_boundary_pts;\n         ++boundary_index) {\n      volume_dt_vars_view.set_data_ref(volume_dt_vars.get() +\n                                           component_index * num_volume_pts +\n                                           boundary_index * stripe_size,\n                                       stripe_size);\n      // safe const_cast since used as a view\n      volume_inv_det_jacobian_view.set_data_ref(\n          // NOLINTNEXTLINE\n          const_cast<double*>(get(volume_det_inv_jacobian).data()) +\n              boundary_index * stripe_size,\n          stripe_size);\n      // Minus sign because we brought this from the LHS to the RHS.\n      volume_dt_vars_view -=\n          volume_inv_det_jacobian_view *\n          (upper_boundary_lifting_term *\n               get(upper_face_det_jacobian)[boundary_index] *\n               get(upper_magnitude_of_face_normal)[boundary_index] *\n               upper_boundary_corrections[component_index * num_boundary_pts +\n                                          boundary_index] +\n           lower_boundary_lifting_term *\n               get(lower_face_det_jacobian)[boundary_index] *\n               get(lower_magnitude_of_face_normal)[boundary_index] *\n               lower_boundary_corrections[component_index * num_boundary_pts +\n                                          boundary_index]);\n    }\n  }\n}\n}  // namespace\n\ntemplate <typename ValueType, size_t Dim>\nvoid lift_boundary_terms_gauss_points_impl(\n    const gsl::not_null<ValueType*> volume_dt_vars,\n    const size_t num_independent_components, const Mesh<Dim>& volume_mesh,\n    const size_t dimension, const Scalar<DataVector>& volume_det_inv_jacobian,\n    const size_t num_boundary_pts,\n    const gsl::span<const ValueType>& boundary_corrections,\n    const DataVector& boundary_lifting_term,\n    const Scalar<DataVector>& magnitude_of_face_normal,\n    const Scalar<DataVector>& face_det_jacobian) {\n  const size_t num_volume_pts = volume_mesh.number_of_grid_points();\n  if (dimension == 0) {\n    lift_boundary_terms_gauss_points_impl_xi_dir(\n        volume_dt_vars, num_independent_components, num_volume_pts,\n        volume_det_inv_jacobian, num_boundary_pts, boundary_corrections,\n        boundary_lifting_term, magnitude_of_face_normal, face_det_jacobian);\n    return;\n  }\n\n  // Developer note: A potential optimization is to re-order (not transpose!)\n  // the volume time derivative for all variables before lifting, so that the\n  // lifting can be done using vectorized math and DataVectors as views. It\n  // would need to be tested whether that actually increases performance.\n  // Another alternative would be to use a SIMD library, such as nsimd or xsimd.\n\n  const size_t stripe_size = volume_mesh.extents(dimension);\n  size_t boundary_index = 0;\n  for (StripeIterator si{volume_mesh.extents(), dimension}; si;\n       (void)++si, (void)++boundary_index) {\n    // Loop over each stripe in this logical direction. This is effectively\n    // looping over each boundary grid point.\n    for (size_t component_index = 0;\n         component_index < num_independent_components; ++component_index) {\n      for (size_t index_on_stripe = 0; index_on_stripe < stripe_size;\n           ++index_on_stripe) {\n        const size_t volume_index = si.offset() + si.stride() * index_on_stripe;\n        volume_dt_vars.get()[component_index * num_volume_pts + volume_index] -=\n            get(volume_det_inv_jacobian)[volume_index] *\n            boundary_lifting_term[index_on_stripe] *\n            get(face_det_jacobian)[boundary_index] *\n            get(magnitude_of_face_normal)[boundary_index] *\n            boundary_corrections[component_index * num_boundary_pts +\n                                 boundary_index];\n      }\n    }\n  }\n}\n\ntemplate <typename ValueType, size_t Dim>\nvoid lift_boundary_terms_gauss_points_impl(\n    const gsl::not_null<ValueType*> volume_dt_vars,\n    const size_t num_independent_components, const Mesh<Dim>& volume_mesh,\n    const size_t dimension, const Scalar<DataVector>& volume_det_inv_jacobian,\n    const size_t num_boundary_pts,\n    const gsl::span<const ValueType>& upper_boundary_corrections,\n    const DataVector& upper_boundary_lifting_term,\n    const Scalar<DataVector>& upper_magnitude_of_face_normal,\n    const Scalar<DataVector>& upper_face_det_jacobian,\n    const gsl::span<const ValueType>& lower_boundary_corrections,\n    const DataVector& lower_boundary_lifting_term,\n    const Scalar<DataVector>& lower_magnitude_of_face_normal,\n    const Scalar<DataVector>& lower_face_det_jacobian) {\n  const size_t num_volume_pts = volume_mesh.number_of_grid_points();\n  if (dimension == 0) {\n    lift_boundary_terms_gauss_points_impl_xi_dir(\n        volume_dt_vars, num_independent_components, num_volume_pts,\n        volume_det_inv_jacobian, num_boundary_pts, upper_boundary_corrections,\n        upper_boundary_lifting_term, upper_magnitude_of_face_normal,\n        upper_face_det_jacobian, lower_boundary_corrections,\n        lower_boundary_lifting_term, lower_magnitude_of_face_normal,\n        lower_face_det_jacobian);\n    return;\n  }\n  // Developer note: A potential optimization is to re-order (not transpose!)\n  // the volume time derivative for all variables before lifting, so that the\n  // lifting can be done using vectorized math and DataVectors as views. It\n  // would need to be tested whether that actually increases performance.\n  // Another alternative would be to use a SIMD library, such as nsimd or xsimd.\n\n  const size_t stripe_size = volume_mesh.extents(dimension);\n  size_t boundary_index = 0;\n  for (StripeIterator si{volume_mesh.extents(), dimension}; si;\n       (void)++si, (void)++boundary_index) {\n    // Loop over each stripe in this logical direction. This is effectively\n    // looping over each boundary grid point.\n    for (size_t component_index = 0;\n         component_index < num_independent_components; ++component_index) {\n      for (size_t index_on_stripe = 0; index_on_stripe < stripe_size;\n           ++index_on_stripe) {\n        const size_t volume_index = si.offset() + si.stride() * index_on_stripe;\n        volume_dt_vars.get()[component_index * num_volume_pts + volume_index] -=\n            get(volume_det_inv_jacobian)[volume_index] *\n            (upper_boundary_lifting_term[index_on_stripe] *\n                 get(upper_face_det_jacobian)[boundary_index] *\n                 get(upper_magnitude_of_face_normal)[boundary_index] *\n                 upper_boundary_corrections[component_index * num_boundary_pts +\n                                            boundary_index] +\n             lower_boundary_lifting_term[index_on_stripe] *\n                 get(lower_face_det_jacobian)[boundary_index] *\n                 get(lower_magnitude_of_face_normal)[boundary_index] *\n                 lower_boundary_corrections[component_index * num_boundary_pts +\n                                            boundary_index]);\n      }\n    }\n  }\n}\n\ntemplate <typename ValueType, size_t Dim>\nvoid lift_boundary_terms_gauss_points_impl(\n    const gsl::not_null<ValueType*> volume_data,\n    const size_t num_independent_components, const Mesh<Dim>& volume_mesh,\n    const Direction<Dim>& direction,\n    const gsl::span<const ValueType>& boundary_corrections) {\n  const size_t dimension = direction.dimension();\n  const auto& lower_and_upper_lifting_matrix =\n      Spectral::boundary_interpolation_matrices(\n          volume_mesh.slice_through(dimension));\n  // One row of values: \\ell_{\\breve{\\imath}}(\\xi=\\pm1)\n  const Matrix& lifting_matrix = direction.side() == Side::Lower\n                                     ? lower_and_upper_lifting_matrix.first\n                                     : lower_and_upper_lifting_matrix.second;\n  // Loop over each stripe in this logical direction. This is effectively\n  // looping over each boundary grid point.\n  const size_t stripe_size = volume_mesh.extents(dimension);\n  size_t boundary_index = 0;\n  const size_t num_volume_pts = volume_mesh.number_of_grid_points();\n  const size_t num_boundary_pts =\n      volume_mesh.slice_away(dimension).number_of_grid_points();\n  for (StripeIterator si{volume_mesh.extents(), dimension}; si;\n       (void)++si, (void)++boundary_index) {\n    for (size_t component_index = 0;\n         component_index < num_independent_components; ++component_index) {\n      for (size_t index_on_stripe = 0; index_on_stripe < stripe_size;\n           ++index_on_stripe) {\n        const size_t volume_index = si.offset() + si.stride() * index_on_stripe;\n        volume_data.get()[component_index * num_volume_pts + volume_index] +=\n            lifting_matrix(0, index_on_stripe) *\n            boundary_corrections[component_index * num_boundary_pts +\n                                 boundary_index];\n      }\n    }\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(r, data)                                                 \\\n  template void lift_boundary_terms_gauss_points_impl(                       \\\n      gsl::not_null<DTYPE(data)*> volume_dt_vars,                            \\\n      size_t num_independent_components, const Mesh<DIM(data)>& volume_mesh, \\\n      size_t dimension, const Scalar<DataVector>& volume_det_inv_jacobian,   \\\n      size_t num_boundary_pts,                                               \\\n      const gsl::span<const DTYPE(data)>& boundary_corrections,              \\\n      const DataVector& boundary_lifting_term,                               \\\n      const Scalar<DataVector>& magnitude_of_face_normal,                    \\\n      const Scalar<DataVector>& face_det_jacobian);                          \\\n  template void lift_boundary_terms_gauss_points_impl(                       \\\n      gsl::not_null<DTYPE(data)*> volume_dt_vars,                            \\\n      size_t num_independent_components, const Mesh<DIM(data)>& volume_mesh, \\\n      size_t dimension, const Scalar<DataVector>& volume_det_inv_jacobian,   \\\n      size_t num_boundary_pts,                                               \\\n      const gsl::span<const DTYPE(data)>& upper_boundary_corrections,        \\\n      const DataVector& upper_boundary_lifting_term,                         \\\n      const Scalar<DataVector>& upper_magnitude_of_face_normal,              \\\n      const Scalar<DataVector>& upper_face_det_jacobian,                     \\\n      const gsl::span<const DTYPE(data)>& lower_boundary_corrections,        \\\n      const DataVector& lower_boundary_lifting_term,                         \\\n      const Scalar<DataVector>& lower_magnitude_of_face_normal,              \\\n      const Scalar<DataVector>& lower_face_det_jacobian);                    \\\n  template void lift_boundary_terms_gauss_points_impl(                       \\\n      gsl::not_null<DTYPE(data)*> volume_data,                               \\\n      size_t num_independent_components, const Mesh<DIM(data)>& volume_mesh, \\\n      const Direction<DIM(data)>& direction,                                 \\\n      const gsl::span<const DTYPE(data)>& boundary_corrections);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, std::complex<double>), (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace dg::detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryLiftingTerm.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace dg {\nnamespace detail {\ntemplate <typename ValueType, size_t Dim>\nvoid lift_boundary_terms_gauss_points_impl(\n    gsl::not_null<ValueType*> volume_dt_vars, size_t num_independent_components,\n    const Mesh<Dim>& volume_mesh, size_t dimension,\n    const Scalar<DataVector>& volume_det_inv_jacobian, size_t num_boundary_pts,\n    const gsl::span<const ValueType>& boundary_corrections,\n    const DataVector& boundary_lifting_term,\n    const Scalar<DataVector>& magnitude_of_face_normal,\n    const Scalar<DataVector>& face_det_jacobian);\n\ntemplate <typename ValueType, size_t Dim>\nvoid lift_boundary_terms_gauss_points_impl(\n    gsl::not_null<ValueType*> volume_dt_vars, size_t num_independent_components,\n    const Mesh<Dim>& volume_mesh, size_t dimension,\n    const Scalar<DataVector>& volume_det_inv_jacobian, size_t num_boundary_pts,\n    const gsl::span<const ValueType>& upper_boundary_corrections,\n    const DataVector& upper_boundary_lifting_term,\n    const Scalar<DataVector>& upper_magnitude_of_face_normal,\n    const Scalar<DataVector>& upper_face_det_jacobian,\n    const gsl::span<const ValueType>& lower_boundary_corrections,\n    const DataVector& lower_boundary_lifting_term,\n    const Scalar<DataVector>& lower_magnitude_of_face_normal,\n    const Scalar<DataVector>& lower_face_det_jacobian);\n\ntemplate <typename ValueType, size_t Dim>\nvoid lift_boundary_terms_gauss_points_impl(\n    gsl::not_null<ValueType*> volume_data, size_t num_independent_components,\n    const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction,\n    const gsl::span<const ValueType>& boundary_corrections);\n}  // namespace detail\n\n/*!\n * \\brief Lift the boundary corrections to the volume time derivatives in the\n * specified direction.\n *\n * The general lifting term (for the \\f$\\xi\\f$-dimension) is:\n *\n * \\f{align*}{\n * \\partial_t u_{\\alpha\\breve{\\imath}\\breve{\\jmath}\\breve{k}}=\\cdots\n * -\\frac{\\ell_{\\breve{\\imath}}\\left(\\xi=1\\right)}\n *   {w_{\\breve{\\imath}}J_{\\breve{\\imath}\\breve{\\jmath}\\breve{k}}}\n *   \\left[J\\sqrt{\n *   \\frac{\\partial\\xi}{\\partial x^i} \\gamma^{ij}\n *   \\frac{\\partial\\xi}{\\partial x^j}}\n *   \\left(G_{\\alpha} + D_{\\alpha}\\right)\n *   \\right]_{\\breve{\\jmath}\\breve{k}}\\left(\\xi=1\\right),\n * \\f}\n *\n * where \\f$\\breve{\\imath}\\f$, \\f$\\breve{\\jmath}\\f$, and \\f$\\breve{k}\\f$ are\n * indices in the logical \\f$\\xi\\f$, \\f$\\eta\\f$, and \\f$\\zeta\\f$ dimensions.\n * The \\f$G+D\\f$ terms correspond to the `boundary_corrections` function\n * argument, and the function Spectral::boundary_lifting_term() is used to\n * compute and cache the terms from the lifting terms\n * \\f$\\ell_{\\breve{\\imath}}(\\xi=\\pm1)/w_{\\breve{\\imath}}\\f$.\n *\n * \\note that normal vectors are pointing out of the element.\n *\n * \\param dt_vars The volume time derivatives\n * $\\partial_t u_{\\alpha\\breve{\\imath}\\breve{\\jmath}\\breve{k}}$ to be updated\n * \\param volume_det_inv_jacobian The inverse Jacobian determinant in the volume\n * $J^{-1}_{\\breve{\\imath}\\breve{\\jmath}\\breve{k}}$\n * \\param volume_mesh The mesh of the volume\n * \\param direction The direction of the face on which the boundary corrections\n * are defined\n * \\param boundary_corrections The boundary corrections\n * $\\left(G_{\\alpha} + D_{\\alpha}\\right)$\n * \\param magnitude_of_face_normal The term $\\sqrt{\n * \\frac{\\partial\\xi}{\\partial x^i} \\gamma^{ij}\n * \\frac{\\partial\\xi}{\\partial x^j}}$\n * \\param face_det_jacobian The volume Jacobian determinant $J$ evaluated on the\n * face (not the surface Jacobian determinant)\n */\ntemplate <size_t Dim, typename DtTagsList, typename BoundaryCorrectionTagsList>\nvoid lift_boundary_terms_gauss_points(\n    const gsl::not_null<Variables<DtTagsList>*> dt_vars,\n    const Scalar<DataVector>& volume_det_inv_jacobian,\n    const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction,\n    const Variables<BoundaryCorrectionTagsList>& boundary_corrections,\n    const Scalar<DataVector>& magnitude_of_face_normal,\n    const Scalar<DataVector>& face_det_jacobian) {\n  ASSERT(volume_mesh.quadrature(direction.dimension()) ==\n             Spectral::Quadrature::Gauss,\n         \"Must use Gauss points in the face-normal direction \"\n             << direction.dimension() << \" but got mesh: \" << volume_mesh);\n  const Mesh<Dim - 1> boundary_mesh =\n      volume_mesh.slice_away(direction.dimension());\n  const Mesh<1> volume_stripe_mesh =\n      volume_mesh.slice_through(direction.dimension());\n  const size_t num_boundary_grid_points = boundary_mesh.number_of_grid_points();\n  detail::lift_boundary_terms_gauss_points_impl(\n      make_not_null(dt_vars->data()), dt_vars->number_of_independent_components,\n      volume_mesh, direction.dimension(), volume_det_inv_jacobian,\n      num_boundary_grid_points,\n      gsl::make_span(boundary_corrections.data(), boundary_corrections.size()),\n      direction.side() == Side::Upper\n          ? Spectral::boundary_lifting_term(volume_stripe_mesh).second\n          : Spectral::boundary_lifting_term(volume_stripe_mesh).first,\n      magnitude_of_face_normal, face_det_jacobian);\n}\n\n/*!\n * \\brief Lift both the upper and lower (in logical coordinates) boundary\n * corrections to the volume time derivatives in the specified logical\n * dimension.\n *\n * The upper and lower boundary corrections in the logical `dimension` are\n * lifted together in order to reduce the amount of striding through data that\n * is needed and to improve cache-friendliness.\n *\n * The general lifting term (for the \\f$\\xi\\f$-dimension) is:\n *\n * \\f{align*}{\n * \\partial_t u_{\\alpha\\breve{\\imath}\\breve{\\jmath}\\breve{k}}=\\cdots\n * -\\frac{\\ell_{\\breve{\\imath}}\\left(\\xi=1\\right)}\n *   {w_{\\breve{\\imath}}J_{\\breve{\\imath}\\breve{\\jmath}\\breve{k}}}\n *   \\left[J\\sqrt{\n *   \\frac{\\partial\\xi}{\\partial x^i} \\gamma^{ij}\n *   \\frac{\\partial\\xi}{\\partial x^j}}\n *   \\left(G_{\\alpha} + D_{\\alpha}\\right)\n *   \\right]_{\\breve{\\jmath}\\breve{k}}\\left(\\xi=1\\right)\n * - \\frac{\\ell_{\\breve{\\imath}}\\left(\\xi=-1\\right)}\n *   {w_{\\breve{\\imath}}J_{\\breve{\\imath}\\breve{\\jmath}\\breve{k}}}\n *   \\left[J\\sqrt{\n *   \\frac{\\partial\\xi}{\\partial x^i} \\gamma^{ij}\n *   \\frac{\\partial\\xi}{\\partial x^j}}\n *   \\left(G_{\\alpha} + D_{\\alpha}\\right)\n *   \\right]_{\\breve{\\jmath}\\breve{k}}\\left(\\xi=-1\\right),\n * \\f}\n *\n * where \\f$\\breve{\\imath}\\f$, \\f$\\breve{\\jmath}\\f$, and \\f$\\breve{k}\\f$ are\n * indices in the logical \\f$\\xi\\f$, \\f$\\eta\\f$, and \\f$\\zeta\\f$ dimensions.\n * The \\f$G+D\\f$ terms correspond to the `upper_boundary_corrections` and\n * `lower_boundary_corrections` function arguments, and the function\n * Spectral::boundary_lifting_term() is used to compute and cache the terms\n * from the lifting terms\n * \\f$\\ell_{\\breve{\\imath}}(\\xi=\\pm1)/w_{\\breve{\\imath}}\\f$.\n *\n * \\note that normal vectors are pointing out of the element and therefore both\n * terms have the same sign.\n */\ntemplate <size_t Dim, typename DtTagsList, typename BoundaryCorrectionTagsList>\nvoid lift_boundary_terms_gauss_points(\n    const gsl::not_null<Variables<DtTagsList>*> dt_vars,\n    const Scalar<DataVector>& volume_det_inv_jacobian,\n    const Mesh<Dim>& volume_mesh, const size_t dimension,\n    const Variables<BoundaryCorrectionTagsList>& upper_boundary_corrections,\n    const Scalar<DataVector>& upper_magnitude_of_face_normal,\n    const Scalar<DataVector>& upper_face_det_jacobian,\n    const Variables<BoundaryCorrectionTagsList>& lower_boundary_corrections,\n    const Scalar<DataVector>& lower_magnitude_of_face_normal,\n    const Scalar<DataVector>& lower_face_det_jacobian) {\n  ASSERT(volume_mesh.quadrature(dimension) == Spectral::Quadrature::Gauss,\n         \"Must use Gauss points in the face-normal dimension \"\n             << dimension << \" but got mesh: \" << volume_mesh);\n  const Mesh<Dim - 1> boundary_mesh = volume_mesh.slice_away(dimension);\n  const Mesh<1> volume_stripe_mesh = volume_mesh.slice_through(dimension);\n  const size_t num_boundary_grid_points = boundary_mesh.number_of_grid_points();\n  detail::lift_boundary_terms_gauss_points_impl(\n      make_not_null(dt_vars->data()), dt_vars->number_of_independent_components,\n      volume_mesh, dimension, volume_det_inv_jacobian, num_boundary_grid_points,\n      gsl::make_span(upper_boundary_corrections.data(),\n                     upper_boundary_corrections.size()),\n      Spectral::boundary_lifting_term(volume_stripe_mesh).second,\n      upper_magnitude_of_face_normal, upper_face_det_jacobian,\n      gsl::make_span(lower_boundary_corrections.data(),\n                     lower_boundary_corrections.size()),\n      Spectral::boundary_lifting_term(volume_stripe_mesh).first,\n      lower_magnitude_of_face_normal, lower_face_det_jacobian);\n}\n\n/*!\n * \\brief Lift the boundary corrections from the face to the volume for Gauss\n * points in the direction perpendicular to the face\n *\n * This function doesn't apply any Jacobians or quadrature weights. It only\n * applies the lifting matrix $\\ell_{\\breve{\\imath}}(\\xi=\\pm1)$ to the\n * `boundary_corrections` and adds the result to the `volume_data`.\n */\ntemplate <size_t Dim, typename... VolumeTags,\n          typename... BoundaryCorrectionTags>\nvoid lift_boundary_terms_gauss_points(\n    const gsl::not_null<Variables<tmpl::list<VolumeTags...>>*> volume_data,\n    const Variables<tmpl::list<BoundaryCorrectionTags...>>&\n        boundary_corrections,\n    const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {\n  detail::lift_boundary_terms_gauss_points_impl(\n      make_not_null(volume_data->data()),\n      volume_data->number_of_independent_components, volume_mesh, direction,\n      gsl::make_span(boundary_corrections.data(), boundary_corrections.size()));\n}\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid metric_identity_det_jac_times_inv_jac_impl(\n    const gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                        Frame::Inertial>*>\n        det_jac_times_inverse_jacobian,\n    const Mesh<Dim>& mesh,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian) {\n  static_assert(Dim == 1 or Dim == 2, \"Generic impl handles only 1d and 2d.\");\n  set_number_of_grid_points(det_jac_times_inverse_jacobian,\n                            mesh.number_of_grid_points());\n  if constexpr (Dim == 1) {\n    (void)jacobian;\n    get<0, 0>(*det_jac_times_inverse_jacobian) = 1.0;\n  } else if constexpr (Dim == 2) {\n    (void)jacobian;\n    const Mesh<1>& mesh0 = mesh.slice_through(0);\n    const Mesh<1>& mesh1 = mesh.slice_through(1);\n    const Matrix identity{};\n    auto diff_matrices = make_array<Dim>(std::cref(identity));\n\n    diff_matrices[1] = Spectral::differentiation_matrix(mesh1);\n    apply_matrices(make_not_null(&get<0, 0>(*det_jac_times_inverse_jacobian)),\n                   diff_matrices, get<1>(inertial_coords), mesh.extents());\n\n    apply_matrices(make_not_null(&get<0, 1>(*det_jac_times_inverse_jacobian)),\n                   diff_matrices, get<0>(inertial_coords), mesh.extents());\n    get<0, 1>(*det_jac_times_inverse_jacobian) *= -1.0;\n\n    diff_matrices[1] = std::cref(identity);\n    diff_matrices[0] = Spectral::differentiation_matrix(mesh0);\n    apply_matrices(make_not_null(&get<1, 0>(*det_jac_times_inverse_jacobian)),\n                   diff_matrices, get<1>(inertial_coords), mesh.extents());\n    get<1, 0>(*det_jac_times_inverse_jacobian) *= -1.0;\n\n    apply_matrices(make_not_null(&get<1, 1>(*det_jac_times_inverse_jacobian)),\n                   diff_matrices, get<0>(inertial_coords), mesh.extents());\n  }\n}\n\nvoid metric_identity_det_jac_times_inv_jac_impl(\n    const gsl::not_null<\n        InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>*>\n        det_jac_times_inverse_jacobian,\n    const gsl::not_null<DataVector*> buffer,\n    const gsl::not_null<DataVector*> buffer_component, const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const Jacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>&\n        jacobian) {\n  // The 3d case is handled separately because this actually requires a buffer.\n  // Basically, in 2d you can get into the situation where you have 2 unused\n  // components (and thus 2 buffers) and so taking the eta derivatives you have\n  // plenty of space for transposing. In 3d all components of the Jacobian\n  // depend on either eta or zeta derivatives and so you can't use one of them\n  // as a buffer to do a transpose.\n  //\n  // The 3d implementation is what should be generalized to higher dimensions if\n  // the need arises.\n  set_number_of_grid_points(det_jac_times_inverse_jacobian,\n                            mesh.number_of_grid_points());\n  const Mesh<1>& mesh0 = mesh.slice_through(0);\n  const Mesh<1>& mesh1 = mesh.slice_through(1);\n  const Mesh<1>& mesh2 = mesh.slice_through(2);\n  const Matrix identity{};\n  auto diff_matrices = make_array<3>(std::cref(identity));\n\n  ASSERT(buffer->size() == mesh.number_of_grid_points(),\n         \"The size of buffer must be \" << mesh.number_of_grid_points()\n                                       << \" but is \" << buffer->size());\n  ASSERT(buffer_component->size() == mesh.number_of_grid_points(),\n         \"The size of buffer_component must be \" << mesh.number_of_grid_points()\n                                                 << \" but is \"\n                                                 << buffer_component->size());\n\n  // Note that we will multiply everything by 0.5 once we have all terms\n  // contributing to each component computed.\n\n  // Compute all terms with logical derivatives in xi direction\n  diff_matrices[0] = Spectral::differentiation_matrix(mesh0);\n  *buffer = get<2>(inertial_coords) * get<1, 2>(jacobian) -\n            get<1>(inertial_coords) * get<2, 2>(jacobian);\n  apply_matrices(make_not_null(&get<1, 0>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  *buffer = get<1>(inertial_coords) * get<2, 1>(jacobian) -\n            get<2>(inertial_coords) * get<1, 1>(jacobian);\n  apply_matrices(make_not_null(&get<2, 0>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  *buffer = get<0>(inertial_coords) * get<2, 2>(jacobian) -\n            get<2>(inertial_coords) * get<0, 2>(jacobian);\n  apply_matrices(make_not_null(&get<1, 1>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  *buffer = get<2>(inertial_coords) * get<0, 1>(jacobian) -\n            get<0>(inertial_coords) * get<2, 1>(jacobian);\n  apply_matrices(make_not_null(&get<2, 1>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  *buffer = get<1>(inertial_coords) * get<0, 2>(jacobian) -\n            get<0>(inertial_coords) * get<1, 2>(jacobian);\n  apply_matrices(make_not_null(&get<1, 2>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  *buffer = get<0>(inertial_coords) * get<1, 1>(jacobian) -\n            get<1>(inertial_coords) * get<0, 1>(jacobian);\n  apply_matrices(make_not_null(&get<2, 2>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  // Compute all terms with logical derivatives in eta direction\n  diff_matrices[0] = identity;\n  diff_matrices[1] = Spectral::differentiation_matrix(mesh1);\n\n  // First do terms where the derivative can be directly written into the\n  // output buffer.\n  *buffer = get<1>(inertial_coords) * get<2, 2>(jacobian) -\n            get<2>(inertial_coords) * get<1, 2>(jacobian);\n  apply_matrices(make_not_null(&get<0, 0>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  *buffer = get<2>(inertial_coords) * get<0, 2>(jacobian) -\n            get<0>(inertial_coords) * get<2, 2>(jacobian);\n  apply_matrices(make_not_null(&get<0, 1>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  *buffer = get<0>(inertial_coords) * get<1, 2>(jacobian) -\n            get<1>(inertial_coords) * get<0, 2>(jacobian);\n  apply_matrices(make_not_null(&get<0, 2>(*det_jac_times_inverse_jacobian)),\n                 diff_matrices, *buffer, mesh.extents());\n\n  // Now do terms that need to be added to existing output.\n  // These we can multiply by 0.5\n  *buffer = get<2>(inertial_coords) * get<1, 0>(jacobian) -\n            get<1>(inertial_coords) * get<2, 0>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<2, 0>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<2, 0>(*det_jac_times_inverse_jacobian) *= 0.5;\n\n  *buffer = get<0>(inertial_coords) * get<2, 0>(jacobian) -\n            get<2>(inertial_coords) * get<0, 0>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<2, 1>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<2, 1>(*det_jac_times_inverse_jacobian) *= 0.5;\n\n  *buffer = get<1>(inertial_coords) * get<0, 0>(jacobian) -\n            get<0>(inertial_coords) * get<1, 0>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<2, 2>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<2, 2>(*det_jac_times_inverse_jacobian) *= 0.5;\n\n  // Compute all terms with logical derivatives in eta direction\n  diff_matrices[1] = identity;\n  diff_matrices[2] = Spectral::differentiation_matrix(mesh2);\n\n  // All terms need to be added to existing output.\n  // These we multiply by 0.5\n  *buffer = get<2>(inertial_coords) * get<1, 1>(jacobian) -\n            get<1>(inertial_coords) * get<2, 1>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<0, 0>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<0, 0>(*det_jac_times_inverse_jacobian) *= 0.5;\n\n  *buffer = get<1>(inertial_coords) * get<2, 0>(jacobian) -\n            get<2>(inertial_coords) * get<1, 0>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<1, 0>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<1, 0>(*det_jac_times_inverse_jacobian) *= 0.5;\n\n  *buffer = get<0>(inertial_coords) * get<2, 1>(jacobian) -\n            get<2>(inertial_coords) * get<0, 1>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<0, 1>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<0, 1>(*det_jac_times_inverse_jacobian) *= 0.5;\n\n  *buffer = get<2>(inertial_coords) * get<0, 0>(jacobian) -\n            get<0>(inertial_coords) * get<2, 0>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<1, 1>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<1, 1>(*det_jac_times_inverse_jacobian) *= 0.5;\n\n  *buffer = get<1>(inertial_coords) * get<0, 1>(jacobian) -\n            get<0>(inertial_coords) * get<1, 1>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<0, 2>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<0, 2>(*det_jac_times_inverse_jacobian) *= 0.5;\n\n  *buffer = get<0>(inertial_coords) * get<1, 0>(jacobian) -\n            get<1>(inertial_coords) * get<0, 0>(jacobian);\n  apply_matrices(buffer_component, diff_matrices, *buffer, mesh.extents());\n  get<1, 2>(*det_jac_times_inverse_jacobian) += *buffer_component;\n  get<1, 2>(*det_jac_times_inverse_jacobian) *= 0.5;\n}\n}  // namespace\n\nnamespace dg {\ntemplate <size_t Dim>\nvoid metric_identity_det_jac_times_inv_jac(\n    const gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                        Frame::Inertial>*>\n        det_jac_times_inverse_jacobian,\n    const Mesh<Dim>& mesh,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian) {\n  if constexpr (Dim == 3) {\n    const size_t num_grid_points = mesh.number_of_grid_points();\n    DataVector buffers{2 * num_grid_points};\n    DataVector buffer{buffers.data(), num_grid_points};\n    // clang-tidy: no pointer math\n    DataVector buffer_component{buffers.data() + num_grid_points,  // NOLINT\n                                num_grid_points};\n\n    metric_identity_det_jac_times_inv_jac_impl(\n        det_jac_times_inverse_jacobian, make_not_null(&buffer),\n        make_not_null(&buffer_component), mesh, inertial_coords, jacobian);\n  } else {\n    metric_identity_det_jac_times_inv_jac_impl(det_jac_times_inverse_jacobian,\n                                               mesh, inertial_coords, jacobian);\n  }\n}\n\ntemplate <size_t Dim>\nvoid metric_identity_jacobian_quantities(\n    const gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                        Frame::Inertial>*>\n        det_jac_times_inverse_jacobian,\n    const gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                        Frame::Inertial>*>\n        inverse_jacobian,\n    const gsl::not_null<\n        Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>\n        jacobian,\n    const gsl::not_null<Scalar<DataVector>*> det_jacobian,\n    const Mesh<Dim>& mesh,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3,\n                \"Only implemented for 1, 2, and 3d.\");\n  set_number_of_grid_points(det_jac_times_inverse_jacobian,\n                            mesh.number_of_grid_points());\n  set_number_of_grid_points(inverse_jacobian, mesh.number_of_grid_points());\n  set_number_of_grid_points(det_jacobian, mesh.number_of_grid_points());\n  ASSERT(\n      alg::all_of(*jacobian,\n                  [&mesh](const DataVector& jac_component) {\n                    return jac_component.size() == mesh.number_of_grid_points();\n                  }),\n      \"The Jacobian components must all be the same size as the number of grid \"\n      \"points on the mesh.\");\n  if constexpr (Dim > 1) {\n    ASSERT(min(get(determinant(*jacobian))) > 0.0,\n           \"The determinant of the Jacobian is assumed to be positive in 2d \"\n           \"and 3d\");\n  }\n\n  if constexpr (Dim == 3) {\n    // use inverse Jacobian as buffer in computation\n    metric_identity_det_jac_times_inv_jac_impl(\n        det_jac_times_inverse_jacobian,\n        make_not_null(&get<0, 0>(*inverse_jacobian)),\n        make_not_null(&get<0, 1>(*inverse_jacobian)), mesh, inertial_coords,\n        *jacobian);\n  } else {\n    metric_identity_det_jac_times_inv_jac_impl(\n        det_jac_times_inverse_jacobian, mesh, inertial_coords, *jacobian);\n  }\n\n  // Now compute the determinant of the Jacobian, the inverse Jacobian, and the\n  // Jacobian.\n  if constexpr (Dim == 1) {\n    // In 1d det(Jacobian) invJacobian == 1.0, so the metric identities are\n    // trivially satisfied. As a result, we must compute the quantities to be\n    // consistent with the Jacobian.\n    get(*det_jacobian) = get<0, 0>(*jacobian);\n    get<0, 0>(*inverse_jacobian) = 1.0 / get<0, 0>(*jacobian);\n  } else {\n    // Note that det_jacobian and jacobian are used as buffers here\n    determinant_and_inverse(det_jacobian, jacobian,\n                            *det_jac_times_inverse_jacobian);\n    if constexpr (Dim == 3) {\n      // In 3d det(J inv_jac) = J^2.\n      get(*det_jacobian) = sqrt(get(*det_jacobian));\n    }\n    for (size_t storage_index = 0; storage_index < inverse_jacobian->size();\n         ++storage_index) {\n      (*inverse_jacobian)[storage_index] =\n          (*det_jac_times_inverse_jacobian)[storage_index] / get(*det_jacobian);\n    }\n    for (size_t storage_index = 0; storage_index < jacobian->size();\n         ++storage_index) {\n      (*jacobian)[storage_index] *= get(*det_jacobian);\n    }\n  }\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void metric_identity_det_jac_times_inv_jac(                         \\\n      gsl::not_null<InverseJacobian<DataVector, GET_DIM(data),                 \\\n                                    Frame::ElementLogical, Frame::Inertial>*>  \\\n          det_jac_times_inverse_jacobian,                                      \\\n      const Mesh<GET_DIM(data)>& mesh,                                         \\\n      const tnsr::I<DataVector, GET_DIM(data), Frame::Inertial>&               \\\n          inertial_coords,                                                     \\\n      const Jacobian<DataVector, GET_DIM(data), Frame::ElementLogical,         \\\n                     Frame::Inertial>& jacobian);                              \\\n  template void metric_identity_jacobian_quantities(                           \\\n      gsl::not_null<InverseJacobian<DataVector, GET_DIM(data),                 \\\n                                    Frame::ElementLogical, Frame::Inertial>*>  \\\n          det_jac_times_inverse_jacobian,                                      \\\n      gsl::not_null<InverseJacobian<DataVector, GET_DIM(data),                 \\\n                                    Frame::ElementLogical, Frame::Inertial>*>  \\\n          inverse_jacobian,                                                    \\\n      gsl::not_null<Jacobian<DataVector, GET_DIM(data), Frame::ElementLogical, \\\n                             Frame::Inertial>*>                                \\\n          jacobian,                                                            \\\n      gsl::not_null<Scalar<DataVector>*> det_jacobian,                         \\\n      const Mesh<GET_DIM(data)>& mesh,                                         \\\n      const tnsr::I<DataVector, GET_DIM(data), Frame::Inertial>&               \\\n          inertial_coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace dg {\n/*!\n * \\ingroup DiscontinuousGalerkinGroup\n * \\brief Compute the Jacobian determinant times the inverse Jacobian so that\n * the result is divergence-free.\n *\n * The metric identities are given by\n *\n * \\f{align*}{\n *\n * \\partial_{\\hat{\\imath}}\\left(J\\frac{\\partial\\xi^{\\hat{\\imath}}}\n * {\\partial x^i}\\right)=0.\n *\n * \\f}\n *\n * We want to compute \\f$J\\partial\\xi^{\\hat{\\imath}}/\\partial x^i\\f$ in such a\n * way that the above metric identity is satisfied numerically/discretely. We\n * refer to the inverse Jacobian computed this way as the \"metric\n * identity-satisfying inverse Jacobian\".\n *\n * The discretized form, with the Jacobian determinant \\f$J\\f$ expanded, is\n * given by\n *\n * \\f{align*}{\n *\n * 2\\left(J\\frac{\\partial \\xi^{\\hat{\\imath}}}{\\partial x^i}\\right)_{s}\n *  &=\\epsilon_{ijk}\n *    \\sum_{t_{\\hat{1}}} \\epsilon^{\\hat{\\imath}\\hat{1}\\hat{k}}\n *    D^{(\\hat{1})}_{s t_1}\n *    \\left(x^j\\frac{\\partial x^k}{\\partial \\xi^{\\hat{k}}}\\right)_{t}\n *    \\notag \\\\\n *  &+\\epsilon_{ijk}\n *    \\sum_{t_{\\hat{2}}} \\epsilon^{\\hat{\\imath}\\hat{2}\\hat{k}}\n *    D^{(\\hat{2})}_{st_2}\n *    \\left(x^j\\frac{\\partial x^k}{\\partial \\xi^{\\hat{k}}}\\right)_{t}\n *    \\notag \\\\\n *  &+\\epsilon_{ijk}\n *    \\sum_{t_{\\hat{3}}}\\epsilon^{\\hat{\\imath}\\hat{3}\\hat{k}}\n *    D^{(\\hat{3})}_{st_3}\n *    \\left(x^j\\frac{\\partial x^k}{\\partial \\xi^{\\hat{k}}}\\right)_{t}.\n *\n * \\f}\n *\n * where indices \\f$s,t,t_1,t_2\\f$ and \\f$t_3\\f$ are over grid points. \\f$t_i\\f$\n * are the grid points in the particular logical direction.\n *\n * In 1d we have:\n *\n * \\f{align*}{\n *\n * J\\frac{\\partial \\xi^{\\hat{\\imath}}}{\\partial x^i}=\n * = J\\frac{\\partial \\xi^{\\hat{i}}}{\\partial x^i} = 1\n *\n * \\f}\n *\n * In 2d we have:\n *\n * \\f{align*}{\n *\n *  J\\frac{\\partial \\xi^{\\hat{1}}}{\\partial x^1}&=D^{\\hat{(2)}}x^2 &\n *  J\\frac{\\partial \\xi^{\\hat{2}}}{\\partial x^1}&=-D^{\\hat{(1)}}x^2 \\\\\n *  J\\frac{\\partial \\xi^{\\hat{1}}}{\\partial x^2}&=-D^{\\hat{(2)}}x^1 &\n *  J\\frac{\\partial \\xi^{\\hat{2}}}{\\partial x^2}&=D^{\\hat{(1)}}x^1 \\\\\n *\n * \\f}\n *\n * In 3d we have:\n *\n * \\f{align*}{\n *\n * 2J\\frac{\\partial \\xi^{\\hat{1}}}{\\partial x^1}&=\n * D^{(\\hat{2})}\\left(x^2\\frac{\\partial x^3}{\\partial \\xi^{\\hat{3}}}\\right)\n * -D^{(\\hat{2})}\\left(x^3\\frac{\\partial x^2}{\\partial \\xi^{\\hat{3}}}\\right)\n * +D^{(\\hat{3})}\\left(x^3\\frac{\\partial x^2}{\\partial \\xi^{\\hat{2}}}\\right)\n * -D^{(\\hat{3})}\\left(x^2\\frac{\\partial x^3}{\\partial \\xi^{\\hat{2}}}\\right)\\\\\n * 2J\\frac{\\partial \\xi^{\\hat{2}}}{\\partial x^1}&=\n * D^{(\\hat{1})}\\left(x^3\\frac{\\partial x^2}{\\partial\\xi^{\\hat{3}}}\\right)\n * -D^{(\\hat{1})}\\left(x^2\\frac{\\partial x^3}{\\partial\\xi^{\\hat{3}}}\\right)\n * +D^{(\\hat{3})}\\left(x^2\\frac{\\partial x^3}{\\partial\\xi^{\\hat{1}}}\\right)\n * -D^{(\\hat{3})}\\left(x^3\\frac{\\partial x^2}{\\partial\\xi^{\\hat{1}}}\\right) \\\\\n * 2J\\frac{\\partial \\xi^{\\hat{3}}}{\\partial x^1}&=\n * D^{(\\hat{1})}\\left(x^2\\frac{\\partial x^3}{\\partial\\xi^{\\hat{2}}}\\right)\n * -D^{(\\hat{1})}\\left(x^3\\frac{\\partial x^2}{\\partial\\xi^{\\hat{2}}}\\right)\n * +D^{(\\hat{2})}\\left(x^3\\frac{\\partial x^2}{\\partial\\xi^{\\hat{1}}}\\right)\n * -D^{(\\hat{2})}\\left(x^2\\frac{\\partial x^3}{\\partial\\xi^{\\hat{1}}}\\right)\\\\\n * 2J\\frac{\\partial \\xi^{\\hat{1}}}{\\partial x^2}&=\n * D^{(\\hat{2})}\\left(x^3\\frac{\\partial x^1}{\\partial \\xi^{\\hat{3}}}\\right)\n * -D^{(\\hat{2})}\\left(x^1\\frac{\\partial x^3}{\\partial \\xi^{\\hat{3}}}\\right)\n * +D^{(\\hat{3})}\\left(x^1\\frac{\\partial x^3}{\\partial \\xi^{\\hat{2}}}\\right)\n * -D^{(\\hat{3})}\\left(x^3\\frac{\\partial x^1}{\\partial \\xi^{\\hat{2}}}\\right) \\\\\n * 2J\\frac{\\partial \\xi^{\\hat{2}}}{\\partial x^2}&=\n * D^{(\\hat{1})}\\left(x^1\\frac{\\partial x^3}{\\partial \\xi^{\\hat{3}}}\\right)\n * -D^{(\\hat{1})}\\left(x^3\\frac{\\partial x^1}{\\partial \\xi^{\\hat{3}}}\\right)\n * +D^{(\\hat{3})}\\left(x^3\\frac{\\partial x^1}{\\partial \\xi^{\\hat{1}}}\\right)\n * -D^{(\\hat{3})}\\left(x^1\\frac{\\partial x^3}{\\partial \\xi^{\\hat{1}}}\\right)\\\\\n * 2J\\frac{\\partial \\xi^{\\hat{3}}}{\\partial x^2}&=\n * D^{(\\hat{1})}\\left(x^3\\frac{\\partial x^1}{\\partial \\xi^{\\hat{2}}}\\right)\n * -D^{(\\hat{1})}\\left(x^1\\frac{\\partial x^3}{\\partial \\xi^{\\hat{2}}}\\right)\n * +D^{(\\hat{2})}\\left(x^1\\frac{\\partial x^3}{\\partial \\xi^{\\hat{1}}}\\right)\n * -D^{(\\hat{2})}\\left(x^3\\frac{\\partial x^1}{\\partial \\xi^{\\hat{1}}}\\right) \\\\\n * 2J\\frac{\\partial \\xi^{\\hat{1}}}{\\partial x^3}&=\n * D^{(\\hat{2})}\\left(x^1\\frac{\\partial x^2}{\\partial \\xi^{\\hat{3}}}\\right)\n * -D^{(\\hat{2})}\\left(x^2\\frac{\\partial x^1}{\\partial \\xi^{\\hat{3}}}\\right)\n * +D^{(\\hat{3})}\\left(x^2\\frac{\\partial x^1}{\\partial \\xi^{\\hat{2}}}\\right)\n * -D^{(\\hat{3})}\\left(x^1\\frac{\\partial x^2}{\\partial \\xi^{\\hat{2}}}\\right) \\\\\n * 2J\\frac{\\partial \\xi^{\\hat{2}}}{\\partial x^3}&=\n * D^{(\\hat{1})}\\left(x^2\\frac{\\partial x^1}{\\partial \\xi^{\\hat{3}}}\\right)\n * -D^{(\\hat{1})}\\left(x^1\\frac{\\partial x^2}{\\partial \\xi^{\\hat{3}}}\\right)\n * +D^{(\\hat{3})}\\left(x^1\\frac{\\partial x^2}{\\partial \\xi^{\\hat{1}}}\\right)\n * -D^{(\\hat{3})}\\left(x^2\\frac{\\partial x^1}{\\partial \\xi^{\\hat{1}}}\\right) \\\\\n * 2J\\frac{\\partial \\xi^{\\hat{3}}}{\\partial x^3}&=\n * D^{(\\hat{1})}\\left(x^1\\frac{\\partial x^2}{\\partial \\xi^{\\hat{2}}}\\right)\n * -D^{(\\hat{1})}\\left(x^2\\frac{\\partial x^1}{\\partial \\xi^{\\hat{2}}}\\right)\n * +D^{(\\hat{2})}\\left(x^2\\frac{\\partial x^1}{\\partial \\xi^{\\hat{1}}}\\right)\n * -D^{(\\hat{2})}\\left(x^1\\frac{\\partial x^2}{\\partial \\xi^{\\hat{1}}}\\right)\n *\n * \\f}\n *\n * Again, this ensures that the metric identities are satisfied discretely. That\n * is,\n *\n * \\f{align*}{\n *\n * \\partial_{\\hat{\\imath}}\\left(J\\frac{\\partial\\xi^{\\hat{\\imath}}}\n * {\\partial x^i}\\right)=0\n *\n * \\f}\n *\n * numerically.\n *\n * The reason for calculating \\f$J\\partial\\xi^{\\hat{\\imath}}/\\partial x^i\\f$ in\n * this manner is because in the weak form of DG (and most spectral-type methods\n * can be recast as DG) we effectively evaluate\n *\n * \\f{align*}{\n *\n * \\partial_{\\hat{\\imath}}\\left(J\\frac{\\partial\\xi^{\\hat{\\imath}}}\n * {\\partial x^i} F^i\\right),\n *\n * \\f}\n *\n * which should be identically zero if \\f$F^i\\f$ is constant. This feature of a\n * scheme is referred to as free-stream preserving. Note that another way to\n * achieve free-stream preservation is to subtract off the metric identity\n * violations. That is,\n *\n * \\f{align*}{\n *\n * \\partial_{\\hat{\\imath}}\\left(J\\frac{\\partial\\xi^{\\hat{\\imath}}}\n * {\\partial x^i} F^i\\right) -\n * F^i \\partial_{\\hat{\\imath}}\\left(J\\frac{\\partial\\xi^{\\hat{\\imath}}}\n * {\\partial x^i}\\right).\n *\n * \\f}\n *\n * The subtraction technique is most commonly used in finite difference codes.\n */\ntemplate <size_t Dim>\nvoid metric_identity_det_jac_times_inv_jac(\n    gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>*>\n        det_jac_times_inverse_jacobian,\n    const Mesh<Dim>& mesh,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian);\n\n/*!\n * \\ingroup DiscontinuousGalerkinGroup\n * \\brief Compute the Jacobian, inverse Jacobian, and determinant of the\n * Jacobian so that they satisfy the metric identities.\n *\n * Uses `dg::metric_identity_jacobian()` to compute the determinant of the\n * Jacobian times the inverse Jacobian. By taking the determinant of this\n * product, we can isolate \\f$J\\f$, the determinant of the Jacobian. In \\f$d\\f$\n * dimensions, we have:\n *\n * \\f{align}{\n *\n *  \\mathrm{det}\\left(J\\frac{\\partial \\xi^{\\hat{\\imath}}}{\\partial x^i}\\right)\n *  = J^{d-1}.\n *\n * \\f}\n *\n * We assume the determinant of the Jacobian is positive, which means logical\n * and inertial coordinates have the same handedness. With this assumption, we\n * have\n *\n * \\f{align}{\n *\n *  J = \\sqrt[(d-1)]{\\mathrm{det}\\left(J\\frac{\\partial\n *      \\xi^{\\hat{\\imath}}}{\\partial x^i}\\right)}\n *\n * \\f}\n *\n * We can now compute the inverse Jacobian using:\n *\n * \\f{align}{\n *\n * \\frac{\\partial \\xi^{\\hat{\\imath}}}{\\partial x^i}=\n *  \\frac{1}{J}\\left(J\\frac{\\partial \\xi^{\\hat{\\imath}}}{\\partial x^i}\\right)\n *\n * \\f}\n *\n * This guarantees that multiplying the determinant of the Jacobian by the\n * inverse Jacobian gives a result that satisfies the metric identities. We also\n * compute the Jacobian by inverting the inverse Jacobian, which guarantees they\n * are (numerical) inverses of each other.\n *\n * \\warning on entry `jacobian` must be the Jacobian to use for computing the\n * determinant of the Jacobian times the inverse Jacobian so that it satisfies\n * the metric identities. The `jacobian` can be computed analytically or\n * numerically, either is fine. On output the `jacobian` is the inverse of the\n * inverse Jacobian that satisfies the metric identities.\n */\ntemplate <size_t Dim>\nvoid metric_identity_jacobian_quantities(\n    gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>*>\n        det_jac_times_inverse_jacobian,\n    gsl::not_null<InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>*>\n        inverse_jacobian,\n    gsl::not_null<\n        Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>*>\n        jacobian,\n    gsl::not_null<Scalar<DataVector>*> det_jacobian, const Mesh<Dim>& mesh,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n\n#include <algorithm>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace dg {\ntemplate <size_t Dim>\nMesh<Dim> mortar_mesh(const Mesh<Dim>& face_mesh1,\n                      const Mesh<Dim>& face_mesh2) {\n  if constexpr (Dim == 0) {\n    return {{{}}, {{}}, {{}}};\n  } else {\n    Index<Dim> mortar_extents{};\n    for (size_t i = 0; i < Dim; ++i) {\n      // From the point of view of mortars, ZernikeB2 with Equiangular\n      // quadrature are identical to Fourier with Equiangular quadrature\n      ASSERT(\n          face_mesh1.basis(i) == face_mesh2.basis(i) or\n              (face_mesh1.quadrature(i) == Spectral::Quadrature::Equiangular and\n               ((face_mesh1.basis(i) == Spectral::Basis::ZernikeB2 and\n                 face_mesh2.basis(0) == Spectral::Basis::Fourier) or\n                (face_mesh1.basis(i) == Spectral::Basis::Fourier and\n                 face_mesh2.basis(0) == Spectral::Basis::ZernikeB2))),\n          \"The quadrature on face_mesh1 and face_mesh2 must be equal in \"\n          \"direction \"\n              << i << \" but face_mesh1 is \" << face_mesh1.basis(i)\n              << \" while face_mesh2 is \" << face_mesh2.basis(i));\n      ASSERT(face_mesh1.quadrature(i) == face_mesh2.quadrature(i),\n             \"The quadrature on face_mesh1 and face_mesh2 must be equal in \"\n             \"direction \"\n                 << i << \" but face_mesh1 is \" << face_mesh1.quadrature(i)\n                 << \" while face_mesh2 is \" << face_mesh2.quadrature(i));\n      ASSERT(\n          face_mesh1.basis(i) != Spectral::Basis::FiniteDifference,\n          \"Cannot construct a mortar_mesh when the basis is FiniteDifference.\");\n      mortar_extents[i] =\n          std::max(face_mesh1.extents(i), face_mesh2.extents(i));\n    }\n    return {mortar_extents.indices(), face_mesh1.basis(),\n            face_mesh1.quadrature()};\n  }\n}\n\ntemplate <size_t Dim>\nstd::array<Spectral::SegmentSize, Dim - 1> mortar_size(\n    const ElementId<Dim>& self, const ElementId<Dim>& neighbor,\n    const size_t dimension, const OrientationMap<Dim>& orientation) {\n  const auto self_segments =\n      all_but_specified_element_of(self.segment_ids(), dimension);\n  const auto neighbor_segments = all_but_specified_element_of(\n      orientation.inverse_map()(neighbor.segment_ids()), dimension);\n\n  std::array<Spectral::SegmentSize, Dim - 1> result{};\n  for (size_t d = 0; d < Dim - 1; ++d) {\n    const auto& self_segment = gsl::at(self_segments, d);\n    const auto& neighbor_segment = gsl::at(neighbor_segments, d);\n    if (neighbor_segment == self_segment.id_of_child(Side::Lower)) {\n      gsl::at(result, d) = Spectral::SegmentSize::LowerHalf;\n    } else if (neighbor_segment == self_segment.id_of_child(Side::Upper)) {\n      gsl::at(result, d) = Spectral::SegmentSize::UpperHalf;\n    } else {\n      ASSERT(neighbor_segment == self_segment or\n             neighbor_segment == self_segment.id_of_parent(),\n             \"Neighbor elements do not overlap 1:1 or 2:1: \" << self_segment\n             << \" \" << neighbor_segment);\n      gsl::at(result, d) = Spectral::SegmentSize::Full;\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::array<SegmentId, Dim - 1> mortar_segments(\n    const ElementId<Dim>& self, const ElementId<Dim>& neighbor,\n    const size_t dimension, const OrientationMap<Dim>& orientation) {\n  const auto self_segments =\n      all_but_specified_element_of(self.segment_ids(), dimension);\n  const auto neighbor_segments = all_but_specified_element_of(\n      orientation.inverse_map()(neighbor.segment_ids()), dimension);\n\n  std::array<SegmentId, Dim - 1> result{};\n  for (size_t d = 0; d < Dim - 1; ++d) {\n    const auto& self_segment = gsl::at(self_segments, d);\n    const auto& neighbor_segment = gsl::at(neighbor_segments, d);\n    if (self_segment.refinement_level() ==\n        neighbor_segment.refinement_level()) {\n      ASSERT(self_segment == neighbor_segment,\n             \"Neighbor elements do not overlap: \"\n                 << self_segment << \" \" << neighbor_segment);\n      gsl::at(result, d) = self_segment;\n    } else if (self_segment.refinement_level() <\n               neighbor_segment.refinement_level()) {\n      ASSERT(self_segment == neighbor_segment.id_of_parent(),\n             \"Neighbor elements do not overlap 1:1 or 2:1: \"\n                 << self_segment << \" \" << neighbor_segment);\n      gsl::at(result, d) = neighbor_segment;\n    } else {\n      ASSERT(neighbor_segment == self_segment.id_of_parent(),\n             \"Neighbor elements do not overlap 1:1 or 2:1: \"\n                 << self_segment << \" \" << neighbor_segment);\n      gsl::at(result, d) = self_segment;\n    }\n  }\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                               \\\n  template Mesh<DIM(data)> mortar_mesh(const Mesh<DIM(data)>& face_mesh1,  \\\n                                       const Mesh<DIM(data)>& face_mesh2); \\\n  template std::array<Spectral::SegmentSize, DIM(data)> mortar_size(       \\\n      const ElementId<DIM(data) + 1>& self,                                \\\n      const ElementId<DIM(data) + 1>& neighbor, size_t dimension,          \\\n      const OrientationMap<DIM(data) + 1>& orientation);                   \\\n  template std::array<SegmentId, DIM(data)> mortar_segments(               \\\n      const ElementId<DIM(data) + 1>& self,                                \\\n      const ElementId<DIM(data) + 1>& neighbor, size_t dimension,          \\\n      const OrientationMap<DIM(data) + 1>& orientation);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n/// \\cond\ntemplate <size_t VolumeDim>\nclass ElementId;\ntemplate <size_t VolumeDim>\nclass OrientationMap;\n\n/// \\endcond\n\nnamespace dg {\n\ntemplate <size_t VolumeDim>\nusing MortarId = DirectionalId<VolumeDim>;\ntemplate <size_t MortarDim>\nusing MortarSize = std::array<Spectral::SegmentSize, MortarDim>;\ntemplate <size_t VolumeDim, typename ValueType>\nusing MortarMap = DirectionalIdMap<VolumeDim, ValueType>;\n\n/// \\ingroup DiscontinuousGalerkinGroup\n/// Find a mesh for a mortar capable of representing data from either\n/// of two faces.\n///\n/// \\warning Make sure the two face meshes are oriented the same, i.e.\n/// their dimensions align. This is facilitated by the `orientation`\n/// passed to `domain::create_initial_mesh`, for example.\ntemplate <size_t Dim>\nMesh<Dim> mortar_mesh(const Mesh<Dim>& face_mesh1, const Mesh<Dim>& face_mesh2);\n\n/// \\ingroup DiscontinuousGalerkinGroup\n/// Determine the size of the mortar (i.e., the part of the face it\n/// covers) for communicating with a neighbor.  This is the size\n/// relative to the size of \\p self, and will not generally agree with\n/// that determined by \\p neighbor.\ntemplate <size_t Dim>\nMortarSize<Dim - 1> mortar_size(const ElementId<Dim>& self,\n                                const ElementId<Dim>& neighbor,\n                                size_t dimension,\n                                const OrientationMap<Dim>& orientation);\n\n/// \\ingroup DiscontinuousGalerkinGroup\n/// Determine the size of the mortar in block coordinates.  The\n/// segments are given in the block of \\p self.\ntemplate <size_t Dim>\nstd::array<SegmentId, Dim - 1> mortar_segments(\n    const ElementId<Dim>& self, const ElementId<Dim>& neighbor,\n    size_t dimension, const OrientationMap<Dim>& orientation);\n\n/// @{\n/// \\ingroup DiscontinuousGalerkinGroup\n/// Project variables from a face to a mortar.\ntemplate <typename Tags, size_t Dim>\nvoid project_to_mortar(const gsl::not_null<Variables<Tags>*> result,\n                       const Variables<Tags>& vars, const Mesh<Dim>& face_mesh,\n                       const Mesh<Dim>& mortar_mesh,\n                       const MortarSize<Dim>& mortar_size) {\n  const auto projection_matrices = Spectral::projection_matrix_parent_to_child(\n      face_mesh, mortar_mesh, mortar_size);\n  // We don't add an ASSERT about sizes here because there's already one in\n  // apply_matrices\n  apply_matrices(result, projection_matrices, vars, face_mesh.extents());\n}\n\ntemplate <typename Tags, size_t Dim>\nVariables<Tags> project_to_mortar(const Variables<Tags>& vars,\n                                  const Mesh<Dim>& face_mesh,\n                                  const Mesh<Dim>& mortar_mesh,\n                                  const MortarSize<Dim>& mortar_size) {\n  Variables<Tags> result{mortar_mesh.number_of_grid_points()};\n  project_to_mortar(make_not_null(&result), vars, face_mesh, mortar_mesh,\n                    mortar_size);\n  return result;\n}\n/// @}\n\n/// @{\n/// \\ingroup DiscontinuousGalerkinGroup\n/// Project variables from a mortar to a face.\ntemplate <typename Tags, size_t Dim>\nvoid project_from_mortar(const gsl::not_null<Variables<Tags>*> result,\n                         const Variables<Tags>& vars,\n                         const Mesh<Dim>& face_mesh,\n                         const Mesh<Dim>& mortar_mesh,\n                         const MortarSize<Dim>& mortar_size) {\n  ASSERT(Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size),\n         \"project_from_mortar should not be called if the interface mesh and \"\n         \"mortar mesh are identical. Please elide the copy instead.\");\n  const auto projection_matrices = Spectral::projection_matrix_child_to_parent(\n      mortar_mesh, face_mesh, mortar_size);\n  // We don't add an ASSERT about sizes here because there's already one in\n  // apply_matrices\n  apply_matrices(result, projection_matrices, vars, mortar_mesh.extents());\n}\n\ntemplate <typename Tags, size_t Dim>\nVariables<Tags> project_from_mortar(const Variables<Tags>& vars,\n                                    const Mesh<Dim>& face_mesh,\n                                    const Mesh<Dim>& mortar_mesh,\n                                    const MortarSize<Dim>& mortar_size) {\n  ASSERT(Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size),\n         \"project_from_mortar should not be called if the interface mesh and \"\n         \"mortar mesh are identical. Please elide the copy instead.\");\n  Variables<Tags> result{face_mesh.number_of_grid_points()};\n  project_from_mortar(make_not_null(&result), vars, face_mesh, mortar_mesh,\n                      mortar_size);\n  return result;\n}\n/// @}\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/MortarInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarInterpolator.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim - 1, Frame::ElementLogical>\ncompute_neighbor_target_points(\n    const tnsr::I<DataVector, Dim, Frame::Grid>& host_grid_coordinates,\n    const ElementMap<Dim, Frame::Grid>& element_map,\n    const size_t boundary_dimension) {\n  tnsr::I<DataVector, Dim - 1, Frame::ElementLogical> result{\n      get<0>(host_grid_coordinates).size()};\n  tnsr::I<double, Dim, Frame::Grid> x{};\n  for (size_t s = 0; s < get<0>(host_grid_coordinates).size(); ++s) {\n    for (size_t d = 0; d < Dim; ++d) {\n      x.get(d) = host_grid_coordinates.get(d)[s];\n    }\n    const auto xi = element_map.inverse(x);\n    for (size_t d = 0; d < boundary_dimension; ++d) {\n      result.get(d)[s] = xi.get(d);\n    }\n    for (size_t d = boundary_dimension; d < Dim - 1; ++d) {\n      result.get(d)[s] = xi.get(d + 1);\n    }\n  }\n  return result;\n}\n}  // namespace\n\nnamespace dg {\ntemplate <size_t Dim>\nMortarInterpolator<Dim>::MortarInterpolator(\n    const ElementId<Dim>& host_id, const DirectionalId<Dim>& neighbor_id,\n    const Domain<Dim>& domain, const Mesh<Dim - 1>& host_mortar_mesh,\n    const Mesh<Dim - 1>& neighbor_mortar_mesh)\n    : host_id_(host_id), neighbor_id_(neighbor_id) {\n  reset_if_necessary(domain, host_mortar_mesh, neighbor_mortar_mesh);\n}\n\ntemplate <size_t Dim>\nvoid MortarInterpolator<Dim>::interpolate_to_host(\n    const gsl::not_null<DataVector*> result,\n    const DataVector& neighbor_data) const {\n  const size_t number_of_components =\n      neighbor_data.size() / neighbor_mortar_mesh_.number_of_grid_points();\n  ASSERT(result->size() ==\n             host_mortar_mesh_.number_of_grid_points() * number_of_components,\n         \"Expected result to have size \"\n             << host_mortar_mesh_.number_of_grid_points() * number_of_components\n             << \", not \" << result->size());\n  gsl::span<double> result_span{result->data(), result->size()};\n  neighbor_to_host_interpolant_.interpolate(\n      make_not_null(&result_span),\n      gsl::make_span(neighbor_data.data(), neighbor_data.size()));\n}\n\ntemplate <size_t Dim>\nDataVector MortarInterpolator<Dim>::interpolate_to_host(\n    const DataVector& neighbor_data) const {\n  const size_t number_of_components =\n      neighbor_data.size() / neighbor_mortar_mesh_.number_of_grid_points();\n  DataVector result{host_mortar_mesh_.number_of_grid_points() *\n                    number_of_components};\n  interpolate_to_host(make_not_null(&result), neighbor_data);\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid MortarInterpolator<Dim>::interpolate_to_neighbor(\n    const gsl::not_null<DataVector*> result,\n    const DataVector& host_data) const {\n  const size_t number_of_components =\n      host_data.size() / host_mortar_mesh_.number_of_grid_points();\n  ASSERT(\n      result->size() ==\n          interpolated_neighbor_data_offsets_.size() * number_of_components,\n      \"Expected result to have size \"\n          << interpolated_neighbor_data_offsets_.size() * number_of_components\n          << \", not \" << result->size());\n  gsl::span<double> result_span{result->data(), result->size()};\n  host_to_neighbor_interpolant_.interpolate(\n      make_not_null(&result_span),\n      gsl::make_span(host_data.data(), host_data.size()));\n}\n\ntemplate <size_t Dim>\nDataVector MortarInterpolator<Dim>::interpolate_to_neighbor(\n    const DataVector& host_data) const {\n  const size_t number_of_components =\n      host_data.size() / host_mortar_mesh_.number_of_grid_points();\n  DataVector result{interpolated_neighbor_data_offsets_.size() *\n                    number_of_components};\n  interpolate_to_neighbor(make_not_null(&result), host_data);\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid MortarInterpolator<Dim>::reset_if_necessary(\n    const Domain<Dim>& domain, const Mesh<Dim - 1>& host_mortar_mesh,\n    const Mesh<Dim - 1>& neighbor_mortar_mesh) {\n  if (host_mortar_mesh == host_mortar_mesh_ and\n      neighbor_mortar_mesh == neighbor_mortar_mesh_) {\n    return;\n  }\n  host_mortar_mesh_ = host_mortar_mesh;\n  neighbor_mortar_mesh_ = neighbor_mortar_mesh;\n  const Block<Dim>& host_block = domain.blocks()[host_id_.block_id()];\n  const Block<Dim>& neighbor_block =\n      domain.blocks()[neighbor_id_.id().block_id()];\n  const ElementMap<Dim, Frame::Grid> host_element_logical_to_grid_map{\n      host_id_, host_block};\n  const ElementMap<Dim, Frame::Grid> neighbor_element_logical_to_grid_map{\n      neighbor_id_.id(), neighbor_block};\n  const auto& orientation = host_block.neighbors()\n                                .at(neighbor_id_.direction())\n                                .orientations()\n                                .at(neighbor_block.id());\n\n  // Setup interpolator from neighbor (e.g. S2 shell) to host (e.g. cube face)\n  // The target points are the points of the host mortar mesh\n  // Do not need to worry about which block as there is a single neighbor\n  // and it must contain the target points\n  {\n    const auto host_element_logical_coords = interface_logical_coordinates(\n        host_mortar_mesh_, neighbor_id_.direction());\n    const auto host_grid_coords =\n        host_element_logical_to_grid_map(host_element_logical_coords);\n    const auto target_points_in_neighbor = compute_neighbor_target_points(\n        host_grid_coords, neighbor_element_logical_to_grid_map,\n        orientation(neighbor_id_.direction().dimension()));\n    neighbor_to_host_interpolant_ = intrp::Irregular<Dim - 1>{\n        neighbor_mortar_mesh_, target_points_in_neighbor};\n  }\n\n  // Setup interpolator from host (e.g. cube face) to neighbor (e.g. S2 shell)\n  // The target points are those of the neighbor mortar mesh that the host\n  // contains.\n  const auto neighbor_element_logical_coords = interface_logical_coordinates(\n      neighbor_mortar_mesh_, orientation(neighbor_id_.direction().opposite()));\n  const auto neighbor_grid_coords =\n      neighbor_element_logical_to_grid_map(neighbor_element_logical_coords);\n  const size_t npts_neighbor = neighbor_mortar_mesh_.number_of_grid_points();\n  interpolated_neighbor_data_offsets_.clear();\n  interpolated_neighbor_data_offsets_.reserve(npts_neighbor);\n  std::vector<tnsr::I<double, Dim, Frame::ElementLogical>>\n      host_element_logical_coords{};\n  host_element_logical_coords.reserve(npts_neighbor);\n  tnsr::I<double, Dim, Frame::Grid> x{};\n  for (size_t s = 0; s < npts_neighbor; ++s) {\n    for (size_t d = 0; d < Dim; ++d) {\n      x.get(d) = neighbor_grid_coords.get(d)[s];\n    }\n    const auto block_xi = block_logical_coordinates_single_point(x, host_block);\n    if (block_xi.has_value()) {\n      const auto xi = element_logical_coordinates(block_xi.value(), host_id_);\n      if (xi.has_value()) {\n        interpolated_neighbor_data_offsets_.emplace_back(s);\n        host_element_logical_coords.emplace_back(xi.value());\n      }\n    }\n  }\n  const size_t npts_neighbor_in_host =\n      interpolated_neighbor_data_offsets_.size();\n  tnsr::I<DataVector, Dim - 1, Frame::ElementLogical> target_points_in_host{\n      npts_neighbor_in_host};\n  const size_t boundary_dimension = neighbor_id_.direction().dimension();\n  for (size_t s = 0; s < npts_neighbor_in_host; ++s) {\n    for (size_t d = 0; d < boundary_dimension; ++d) {\n      target_points_in_host.get(d)[s] = host_element_logical_coords[s].get(d);\n    }\n    for (size_t d = boundary_dimension; d < Dim - 1; ++d) {\n      target_points_in_host.get(d)[s] =\n          host_element_logical_coords[s].get(d + 1);\n    }\n  }\n  host_to_neighbor_interpolant_ =\n      intrp::Irregular<Dim - 1>{host_mortar_mesh_, target_points_in_host};\n}\n\ntemplate <size_t Dim>\nvoid MortarInterpolator<Dim>::pup(PUP::er& p) {\n  p | host_id_;\n  p | neighbor_id_;\n  p | host_mortar_mesh_;\n  p | neighbor_mortar_mesh_;\n  p | neighbor_to_host_interpolant_;\n  p | host_to_neighbor_interpolant_;\n  p | interpolated_neighbor_data_offsets_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const MortarInterpolator<Dim>& lhs,\n                const MortarInterpolator<Dim>& rhs) {\n  return lhs.host_id_ == rhs.host_id_ and\n         lhs.neighbor_id_ == rhs.neighbor_id_ and\n         lhs.host_mortar_mesh_ == rhs.host_mortar_mesh_ and\n         lhs.neighbor_mortar_mesh_ == rhs.neighbor_mortar_mesh_ and\n         lhs.neighbor_to_host_interpolant_ ==\n             rhs.neighbor_to_host_interpolant_ and\n         lhs.host_to_neighbor_interpolant_ ==\n             rhs.host_to_neighbor_interpolant_ and\n         lhs.interpolated_neighbor_data_offsets_ ==\n             rhs.interpolated_neighbor_data_offsets_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const MortarInterpolator<Dim>& lhs,\n                const MortarInterpolator<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate class MortarInterpolator<2>;\ntemplate class MortarInterpolator<3>;\ntemplate bool operator==(const MortarInterpolator<2>&,\n                         const MortarInterpolator<2>&);\ntemplate bool operator==(const MortarInterpolator<3>&,\n                         const MortarInterpolator<3>&);\ntemplate bool operator!=(const MortarInterpolator<2>&,\n                         const MortarInterpolator<2>&);\ntemplate bool operator!=(const MortarInterpolator<3>&,\n                         const MortarInterpolator<3>&);\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/MortarInterpolator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Domain;\nclass DataVector;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace dg {\n/// \\brief Interpolator to be used for interpolating boundary data from (to) a\n/// Neighbor onto a mortar between nonconforming Blocks.\n///\n/// \\details This interpolator is designed to be used when multiple\n/// nonconforming elements are neighbors with a single element (e.g deformed\n/// cubes of a cubed sphere next to a single S2 spherical shell). Each of the\n/// multiple elements (the hosts) should create this class to both interpolate\n/// neighbor boundary data to the mortar of the host as well as interpolate the\n/// host boundary data onto the subset of points of the mortar of the neighbor.\n///\n/// \\note For nonconforming neighbors, the block logical coordinates of\n/// neighboring elements are not related by a discrete rotation (i.e. an\n/// OrientationMap). Therefore boundary data cannot be copied or projected to a\n/// neighbor, but must be interpolated.  As nonconforming neighbors cannot exist\n/// in one dimension, this class is not instantiated for Dim = 1.\ntemplate <size_t Dim>\nclass MortarInterpolator {\n public:\n  /// The Element with host_id is one of the multiple nonconforming Elements\n  /// abutting the single Element with neighbor_id.  The host_mortar_mesh and\n  /// neighbor_mortar_mesh are simply the appropriate face Mesh of the host and\n  /// neighbor.\n  MortarInterpolator(const ElementId<Dim>& host_id,\n                     const DirectionalId<Dim>& neighbor_id,\n                     const Domain<Dim>& domain,\n                     const Mesh<Dim - 1>& host_mortar_mesh,\n                     const Mesh<Dim - 1>& neighbor_mortar_mesh);\n\n  MortarInterpolator() = default;\n\n  /// @{\n  /// \\brief Interpolates the boundary data of the neighbor to the host mortar\n  ///\n  /// \\details neighbor_data is a type-erased Variables so will have a size\n  /// equal to the product of the number of components and the number of grid\n  /// points of the neighbor_mortar_mesh.  The returned DataVector will have a\n  /// size equal to the number of components and the number of grid points of\n  /// the host_mortar_mesh.\n  void interpolate_to_host(gsl::not_null<DataVector*> result,\n                           const DataVector& neighbor_data) const;\n  DataVector interpolate_to_host(const DataVector& neighbor_data) const;\n  /// @}\n\n  /// @{\n  /// \\brief Interpolates the boundary data of the host to a subset of the\n  /// points of the neighbor mortar\n  ///\n  /// \\details host_data is a type-erased Variables so will have a size\n  /// equal to the product of the number of components and the number of grid\n  /// points of the host_mortar_mesh.  The returned DataVector will have a\n  /// size equal to the number of components and the number of grid points of\n  /// the neighbor_mortar_mesh that the host is able to interpolate to.\n  void interpolate_to_neighbor(gsl::not_null<DataVector*> result,\n                               const DataVector& host_data) const;\n  DataVector interpolate_to_neighbor(const DataVector& host_data) const;\n  /// @}\n\n  /// \\brief The offsets of the data returned by interpolate_to_neighbor into\n  /// the full data on the neighbor_mortar_mesh.\n  ///\n  /// \\details  The DataVectors of type-erased Variables store data such that\n  /// the grid point index varies fastest (as opposed to the component index).\n  /// The size of the returned vector is equal to the number of grid points of\n  /// the neighbor_mortar_mesh that the host is able to interpolate to, and the\n  /// values of the returned vector map the grid point index of the partial\n  /// DataVector returned by interpolate_to_neighbor to the grid point index of\n  /// the full DataVector on the neighbor_mortar_mesh\n  const std::vector<size_t>& interpolated_neighbor_data_offsets() const {\n    return interpolated_neighbor_data_offsets_;\n  }\n\n  /// \\brief The neighbor mortar mesh that is used to compute the target points\n  /// and offsets when interpolating to the neighbor\n  const Mesh<Dim - 1>& neighbor_mortar_mesh() const {\n    return neighbor_mortar_mesh_;\n  }\n\n  /// \\brief Updates the interpolator if either of the mortar Mesh change\n  void reset_if_necessary(const Domain<Dim>& domain,\n                          const Mesh<Dim - 1>& host_mortar_mesh,\n                          const Mesh<Dim - 1>& neighbor_mortar_mesh);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <size_t LocalDim>\n  friend bool operator==(const MortarInterpolator<LocalDim>& lhs,\n                         const MortarInterpolator<LocalDim>& rhs);\n\n  ElementId<Dim> host_id_{};\n  DirectionalId<Dim> neighbor_id_{};\n  Mesh<Dim - 1> host_mortar_mesh_{};\n  Mesh<Dim - 1> neighbor_mortar_mesh_{};\n  intrp::Irregular<Dim - 1> neighbor_to_host_interpolant_{};\n  intrp::Irregular<Dim - 1> host_to_neighbor_interpolant_{};\n  std::vector<size_t> interpolated_neighbor_data_offsets_{};\n};\n\ntemplate <>\nclass MortarInterpolator<1> {\n public:  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*unused*/) {}\n};\n\ntemplate <size_t Dim>\nbool operator==(const MortarInterpolator<Dim>& lhs,\n                const MortarInterpolator<Dim>& rhs);\n\ntemplate <>\ninline bool operator==(const MortarInterpolator<1>& /*unused*/,\n                       const MortarInterpolator<1>& /*unused*/) {\n  return true;\n}\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/EagerMath/OuterProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// @{\n/*!\n * \\brief Contract a surface normal covector with the first index of a flux\n * tensor or variables\n *\n * \\details\n * Returns \\f$n_i F^i_{j\\ldots}\\f$, where the flux tensor \\f$F\\f$ must have an\n * upper spatial first index and may have arbitrary extra indices.\n */\ntemplate <size_t VolumeDim, typename Fr, typename DataType, typename Symm,\n          typename... RemainingIndices,\n          typename ResultTensor = Tensor<DataType, tmpl::pop_front<Symm>,\n                                         index_list<RemainingIndices...>>>\nvoid normal_dot_flux(\n    const gsl::not_null<ResultTensor*> normal_dot_flux,\n    const tnsr::i<DataVector, VolumeDim, Fr>& normal,\n    const Tensor<DataType, Symm,\n                 index_list<SpatialIndex<VolumeDim, UpLo::Up, Fr>,\n                            RemainingIndices...>>& flux_tensor) {\n  for (auto it = normal_dot_flux->begin(); it != normal_dot_flux->end(); it++) {\n    const auto result_indices = normal_dot_flux->get_tensor_index(it);\n    *it = get<0>(normal) * flux_tensor.get(prepend(result_indices, size_t{0}));\n    for (size_t d = 1; d < VolumeDim; d++) {\n      *it += normal.get(d) * flux_tensor.get(prepend(result_indices, d));\n    }\n  }\n}\n\ntemplate <typename... ReturnTags, typename... FluxTags, size_t VolumeDim,\n          typename Fr>\nvoid normal_dot_flux(\n    const gsl::not_null<Variables<tmpl::list<ReturnTags...>>*> result,\n    const tnsr::i<DataVector, VolumeDim, Fr>& normal,\n    const Variables<tmpl::list<FluxTags...>>& fluxes) {\n  if (result->number_of_grid_points() != fluxes.number_of_grid_points()) {\n    result->initialize(fluxes.number_of_grid_points());\n  }\n  EXPAND_PACK_LEFT_TO_RIGHT(normal_dot_flux(\n      make_not_null(&get<ReturnTags>(*result)), normal, get<FluxTags>(fluxes)));\n}\n\ntemplate <typename TagsList, size_t VolumeDim, typename Fr>\nauto normal_dot_flux(\n    const tnsr::i<DataVector, VolumeDim, Fr>& normal,\n    const Variables<db::wrap_tags_in<::Tags::Flux, TagsList,\n                                     tmpl::size_t<VolumeDim>, Fr>>& fluxes) {\n  Variables<db::wrap_tags_in<::Tags::NormalDotFlux, TagsList>> result{};\n  normal_dot_flux(make_not_null(&result), normal, fluxes);\n  return result;\n}\n/// @}\n\n/// @{\n/*!\n * \\brief Multiplies a surface normal covector with a tensor or variables\n *\n * \\details\n * Returns the outer product $n_j v_{k\\ldots}$, where $n_j$ is the `normal` and\n * $v_{k\\ldots}$ is the `rhs`.\n *\n * Note that this quantity is a \"normal dot flux\" where the flux involves\n * a Kronecker delta. For example:\n *\n * \\f{equation}\n * n_j v_{k\\ldots} = n_i \\delta^i_j v_{k\\ldots} = n_i F^i_{jk\\ldots}\n * \\f}\n *\n * This makes this quantity useful for optimizations of DG formulations for\n * \"sparse\" (i.e., Kronecker delta) fluxes.\n */\ntemplate <size_t VolumeDim, typename Fr, typename DataType, typename Symm,\n          typename Indices>\nvoid normal_times_flux(\n    const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, Symm, Indices>, VolumeDim, UpLo::Lo, Fr>*>\n        normal_times_flux,\n    const tnsr::i<DataVector, VolumeDim, Fr>& normal,\n    const Tensor<DataType, Symm, Indices>& rhs) {\n  outer_product(normal_times_flux, normal, rhs);\n}\n\ntemplate <typename... ReturnTags, typename... FluxTags, size_t VolumeDim,\n          typename Fr>\nvoid normal_times_flux(\n    const gsl::not_null<Variables<tmpl::list<ReturnTags...>>*> result,\n    const tnsr::i<DataVector, VolumeDim, Fr>& normal,\n    const Variables<tmpl::list<FluxTags...>>& fluxes) {\n  if (result->number_of_grid_points() != fluxes.number_of_grid_points()) {\n    result->initialize(fluxes.number_of_grid_points());\n  }\n  EXPAND_PACK_LEFT_TO_RIGHT(normal_times_flux(\n      make_not_null(&get<ReturnTags>(*result)), normal, get<FluxTags>(fluxes)));\n}\n/// @}\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace dg {\nnamespace detail {\ntemplate <typename TagsList>\nstruct NumberOfIndependentComponents;\n\ntemplate <typename... Tags>\nstruct NumberOfIndependentComponents<tmpl::list<Tags...>> {\n  static constexpr size_t value = (... + Tags::type::size());\n};\n}  // namespace detail\n\n/*!\n * \\brief Projects a `Variables` of volume data to a contiguous subset of\n * a boundary `Variables`\n *\n * The `volume_fields` are all projected into the `face_fields` in the direction\n * `direction`. The tags in `VolumeVarsTagsList` must be a contiguous subset of\n * the tags in `FaceVarsTagsList`. That is, `FaceVarsTagsList` must be\n * equivalent to `tmpl::append<Before, VolumeVarsTagsList, After>` where\n * `Before` and `After` are `tmpl::list`s of arbitrary size. This is because the\n * projection is applied on all of the tensor components of the volume variables\n * and is written into contiguous memory on the boundary.\n *\n * In general, this function will be used for projecting all the evolved\n * variables or all the volume fluxes to the faces. The function\n * `dg::project_tensors_to_boundary()` should be used for projecting\n * individual tensors to the face.\n *\n * \\note This function works for both Gauss and Gauss-Lobatto uniform meshes.\n */\ntemplate <typename VolumeVarsTagsList, typename FaceVarsTagsList, size_t Dim>\nvoid project_contiguous_data_to_boundary(\n    const gsl::not_null<Variables<FaceVarsTagsList>*> face_fields,\n    const Variables<VolumeVarsTagsList>& volume_fields,\n    const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {\n  static_assert(tmpl::size<VolumeVarsTagsList>::value != 0,\n                \"Must have non-zero number of volume fields\");\n  static_assert(tmpl::size<FaceVarsTagsList>::value >=\n                    tmpl::size<VolumeVarsTagsList>::value,\n                \"There must not be more volume tags than there are face tags.\");\n  static_assert(\n      tmpl::list_contains_v<FaceVarsTagsList, tmpl::front<VolumeVarsTagsList>>,\n      \"The first tag of VolumeVarsTagsList is not in the face tags. The \"\n      \"VolumeVarsTagsList must be a subset of the FaceVarsTagsList\");\n  static_assert(\n      tmpl::list_contains_v<FaceVarsTagsList, tmpl::back<VolumeVarsTagsList>>,\n      \"The last tag of VolumeVarsTagsList is not in the face tags. The \"\n      \"VolumeVarsTagsList must be a subset of the FaceVarsTagsList\");\n  using face_vars_excluding_extras_at_end = tmpl::front<\n      tmpl::split_at<FaceVarsTagsList,\n                     tmpl::next<tmpl::index_of<\n                         FaceVarsTagsList, tmpl::back<VolumeVarsTagsList>>>>>;\n  using front_face_vars_split = tmpl::split_at<\n      face_vars_excluding_extras_at_end,\n      tmpl::index_of<FaceVarsTagsList, tmpl::front<VolumeVarsTagsList>>>;\n  using volume_vars_face_subset_list = tmpl::back<front_face_vars_split>;\n  static_assert(\n      std::is_same_v<volume_vars_face_subset_list, VolumeVarsTagsList>,\n      \"The VolumeVarsTagsList must be a subset of the FaceVarsTagsList.\");\n  constexpr const size_t number_of_independent_components =\n      Variables<VolumeVarsTagsList>::number_of_independent_components;\n  using first_volume_tag = tmpl::front<VolumeVarsTagsList>;\n  using VectorType = typename Variables<FaceVarsTagsList>::vector_type;\n  using ValueType = typename VectorType::value_type;\n  const size_t sliced_dim = direction.dimension();\n  if (volume_mesh.quadrature(sliced_dim) == Spectral::Quadrature::Gauss) {\n    const Matrix identity{};\n    auto interpolation_matrices = make_array<Dim>(std::cref(identity));\n    const auto& matrix = Spectral::boundary_interpolation_matrices(\n        volume_mesh.slice_through(sliced_dim));\n    gsl::at(interpolation_matrices, sliced_dim) =\n        direction.side() == Side::Upper ? matrix.second : matrix.first;\n\n    auto& first_face_field = get<first_volume_tag>(*face_fields);\n    auto& first_volume_field = get<first_volume_tag>(volume_fields);\n\n    // The size is the number of tensor components we are projecting times the\n    // number of grid points on the face. Note that this is _not_ equal to the\n    // size of face_fields->size() since face_fields is a superset of the\n    // volume variables.\n    VectorType face_view{\n        first_face_field[0].data(),\n        first_face_field[0].size() * number_of_independent_components};\n\n    apply_matrices(\n        make_not_null(&face_view), interpolation_matrices,\n        VectorType{\n            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n            const_cast<ValueType*>(first_volume_field[0].data()),\n            first_volume_field[0].size() * number_of_independent_components},\n        volume_mesh.extents());\n  } else {\n    ASSERT(volume_mesh.quadrature(sliced_dim) ==\n                   Spectral::Quadrature::GaussLobatto or\n               (volume_mesh.quadrature(sliced_dim) ==\n                    Spectral::Quadrature::GaussRadauUpper and\n                direction.side() == Side::Upper),\n           \"Got quadrature without boundary collocation point at \"\n               << direction << \" with \" << volume_mesh.quadrature(sliced_dim));\n    const size_t fixed_index = direction.side() == Side::Upper\n                                   ? volume_mesh.extents(sliced_dim) - 1\n                                   : 0;\n\n    const size_t interface_grid_points =\n        volume_mesh.extents().slice_away(sliced_dim).product();\n    const size_t volume_grid_points = volume_mesh.number_of_grid_points();\n\n    const ValueType* vars_data = volume_fields.data();\n    // Since the face fields are a superset of the volume tags we need to find\n    // the first volume tag on the face and get the pointer for that.\n    ValueType* interface_vars_data =\n        get<first_volume_tag>(*face_fields)[0].data();\n\n    // The reason we can't use data_on_slice is because the volume and face tags\n    // are not the same, but data_on_slice assumes they are. In general, this\n    // function should replace data_on_slice in the long term since in\n    // additional to supporting different volume and face tags, it also supports\n    // Gauss and Gauss-Lobatto points.\n    //\n    // Run the SliceIterator as the outer-most loop since incrementing the slice\n    // iterator is surprisingly expensive.\n    for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;\n         ++si) {\n      for (size_t i = 0; i < number_of_independent_components; ++i) {\n        // clang-tidy: do not use pointer arithmetic\n        interface_vars_data[si.slice_offset() +                      // NOLINT\n                            i * interface_grid_points] =             // NOLINT\n            vars_data[si.volume_offset() + i * volume_grid_points];  // NOLINT\n      }\n    }\n  }\n}\n\n/*!\n * \\brief Projects a subset of the tensors in the `volume_fields` onto the face\n *\n * The tensors to project are listed in the `TagsToProjectList`.\n *\n * \\note This function works for both Gauss and Gauss-Lobatto uniform meshes.\n */\ntemplate <typename TagsToProjectList, typename VolumeVarsTagsList,\n          typename FaceVarsTagsList, size_t Dim>\nvoid project_tensors_to_boundary(\n    const gsl::not_null<Variables<FaceVarsTagsList>*> face_fields,\n    const Variables<VolumeVarsTagsList>& volume_fields,\n    const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {\n  static_assert(tmpl::size<VolumeVarsTagsList>::value != 0,\n                \"Must have non-zero number of volume fields\");\n  static_assert(\n      tmpl::size<\n          tmpl::list_difference<TagsToProjectList, FaceVarsTagsList>>::value ==\n          0,\n      \"All of the tags in TagsToProjectList must be in FaceVarsTagsList\");\n  static_assert(\n      tmpl::size<tmpl::list_difference<TagsToProjectList,\n                                       VolumeVarsTagsList>>::value == 0,\n      \"All of the tags in TagsToProjectList must be in VolumeVarsTagsList\");\n  const size_t sliced_dim = direction.dimension();\n  if (volume_mesh.quadrature(sliced_dim) == Spectral::Quadrature::Gauss) {\n    const Matrix identity{};\n    auto interpolation_matrices = make_array<Dim>(std::cref(identity));\n    const std::pair<Matrix, Matrix>& matrices =\n        Spectral::boundary_interpolation_matrices(\n            volume_mesh.slice_through(sliced_dim));\n    gsl::at(interpolation_matrices, sliced_dim) =\n        direction.side() == Side::Upper ? matrices.second : matrices.first;\n    tmpl::for_each<TagsToProjectList>([&face_fields, &interpolation_matrices,\n                                       &volume_fields,\n                                       &volume_mesh](auto tag_v) {\n      using tag = typename decltype(tag_v)::type;\n      auto& face_field = get<tag>(*face_fields);\n      const auto& volume_field = get<tag>(volume_fields);\n      DataVector face_view{face_field[0].data(),\n                           face_field[0].size() * face_field.size()};\n      apply_matrices(make_not_null(&face_view), interpolation_matrices,\n                     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n                     DataVector{const_cast<double*>(volume_field[0].data()),\n                                volume_field[0].size() * volume_field.size()},\n                     volume_mesh.extents());\n    });\n  } else {\n    ASSERT(volume_mesh.quadrature(sliced_dim) ==\n                   Spectral::Quadrature::GaussLobatto or\n               (volume_mesh.quadrature(sliced_dim) ==\n                    Spectral::Quadrature::GaussRadauUpper and\n                direction.side() == Side::Upper),\n           \"Got quadrature without boundary collocation point at \"\n               << direction << \" with \" << volume_mesh.quadrature(sliced_dim));\n    const size_t fixed_index = direction.side() == Side::Upper\n                                   ? volume_mesh.extents(sliced_dim) - 1\n                                   : 0;\n\n    const size_t interface_grid_points =\n        volume_mesh.extents().slice_away(sliced_dim).product();\n    const size_t volume_grid_points = volume_mesh.number_of_grid_points();\n\n    // Run the SliceIterator as the outer-most loop since incrementing the slice\n    // iterator is surprisingly expensive.\n    for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;\n         ++si) {\n      tmpl::for_each<TagsToProjectList>([&face_fields, interface_grid_points,\n                                         &si, &volume_fields,\n                                         volume_grid_points](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n\n        const double* vars_data = get<tag>(volume_fields)[0].data();\n        double* interface_vars_data = get<tag>(*face_fields)[0].data();\n        static constexpr size_t number_of_independent_components_in_tensor =\n            std::decay_t<decltype(get<tag>(volume_fields))>::size();\n\n        for (size_t i = 0; i < number_of_independent_components_in_tensor;\n             ++i) {\n          // clang-tidy: do not use pointer arithmetic\n          interface_vars_data[si.slice_offset() +                      // NOLINT\n                              i * interface_grid_points] =             // NOLINT\n              vars_data[si.volume_offset() + i * volume_grid_points];  // NOLINT\n        }\n      });\n    }\n  }\n}\n\n/// @{\n/*!\n * \\brief Projects a tensor to the face\n *\n * \\note This function works for both Gauss and Gauss-Lobatto uniform meshes.\n */\ntemplate <typename Symm, typename IndexList, size_t Dim>\nvoid project_tensor_to_boundary(\n    const gsl::not_null<Tensor<DataVector, Symm, IndexList>*> face_field,\n    const Tensor<DataVector, Symm, IndexList>& volume_field,\n    const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {\n  const size_t sliced_dim = direction.dimension();\n  if (volume_mesh.quadrature(sliced_dim) == Spectral::Quadrature::Gauss) {\n    const Matrix identity{};\n    auto interpolation_matrices = make_array<Dim>(std::cref(identity));\n    const std::pair<Matrix, Matrix>& matrices =\n        Spectral::boundary_interpolation_matrices(\n            volume_mesh.slice_through(sliced_dim));\n    gsl::at(interpolation_matrices, sliced_dim) =\n        direction.side() == Side::Upper ? matrices.second : matrices.first;\n    for (size_t tensor_storage_index = 0;\n         tensor_storage_index < volume_field.size(); ++tensor_storage_index) {\n      apply_matrices(make_not_null(&(*face_field)[tensor_storage_index]),\n                     interpolation_matrices, volume_field[tensor_storage_index],\n                     volume_mesh.extents());\n    }\n  } else {\n    ASSERT(volume_mesh.quadrature(sliced_dim) ==\n                   Spectral::Quadrature::GaussLobatto or\n               (volume_mesh.quadrature(sliced_dim) ==\n                    Spectral::Quadrature::GaussRadauUpper and\n                direction.side() == Side::Upper),\n           \"Got quadrature without boundary collocation point at \"\n               << direction << \" with \" << volume_mesh.quadrature(sliced_dim));\n    const size_t fixed_index = direction.side() == Side::Upper\n                                   ? volume_mesh.extents(sliced_dim) - 1\n                                   : 0;\n\n    // Run the SliceIterator as the outer-most loop since incrementing the slice\n    // iterator is surprisingly expensive.\n    for (SliceIterator si(volume_mesh.extents(), sliced_dim, fixed_index); si;\n         ++si) {\n      for (size_t tensor_storage_index = 0;\n           tensor_storage_index < volume_field.size(); ++tensor_storage_index) {\n        (*face_field)[tensor_storage_index][si.slice_offset()] =\n            volume_field[tensor_storage_index][si.volume_offset()];\n      }\n    }\n  }\n}\n\ntemplate <typename Symm, typename IndexList, size_t Dim>\nTensor<DataVector, Symm, IndexList> project_tensor_to_boundary(\n    const Tensor<DataVector, Symm, IndexList>& volume_field,\n    const Mesh<Dim>& volume_mesh, const Direction<Dim>& direction) {\n  Tensor<DataVector, Symm, IndexList> face_field{\n      volume_mesh.slice_away(direction.dimension()).number_of_grid_points()};\n  project_tensor_to_boundary(make_not_null(&face_field), volume_field,\n                             volume_mesh, direction);\n  return face_field;\n}\n/// @}\n\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/SimpleBoundaryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nstruct Index;\ntemplate <size_t Dim>\nstruct Mesh;\ntemplate <size_t Dim>\nstruct OrientationMap;\n/// \\endcond\n\nnamespace dg {\n\n/*!\n * \\brief Distinguishes between field data, which can be projected to a mortar,\n * and extra data, which will not be projected.\n */\ntemplate <typename FieldTags, typename ExtraDataTags = tmpl::list<>>\nstruct SimpleBoundaryData {\n  using field_tags = FieldTags;\n  using extra_data_tags = ExtraDataTags;\n\n  /// Data projected to the mortar mesh\n  Variables<FieldTags> field_data;\n\n  /// Data on the element face that needs no projection to the mortar mesh.\n  /// This is a `TaggedTuple` to support non-tensor quantities. It also helps\n  /// supporting an empty list of `ExtraDataTags`.\n  tuples::tagged_tuple_from_typelist<ExtraDataTags> extra_data;\n\n  SimpleBoundaryData() = default;\n  SimpleBoundaryData(const SimpleBoundaryData&) = default;\n  SimpleBoundaryData& operator=(const SimpleBoundaryData&) = default;\n  SimpleBoundaryData(SimpleBoundaryData&&) = default;\n  SimpleBoundaryData& operator=(SimpleBoundaryData&&) = default;\n  ~SimpleBoundaryData() = default;\n\n  explicit SimpleBoundaryData(const size_t num_points)\n      : field_data{num_points}, extra_data{} {}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | field_data;\n    p | extra_data;\n  }\n\n  /// Project the `field_data` to the mortar\n  ///\n  /// \\see `dg::project_to_mortar`\n  template <size_t MortarDim>\n  SimpleBoundaryData<FieldTags, ExtraDataTags> project_to_mortar(\n      const Mesh<MortarDim>& face_mesh, const Mesh<MortarDim>& mortar_mesh,\n      const MortarSize<MortarDim>& mortar_size) const {\n    SimpleBoundaryData<FieldTags, ExtraDataTags> projected_data{};\n    projected_data.field_data = dg::project_to_mortar(\n        this->field_data, face_mesh, mortar_mesh, mortar_size);\n    projected_data.extra_data = this->extra_data;\n    return projected_data;\n  }\n\n  /// Orient the `field_data`\n  ///\n  /// \\see `orient_variables_on_slice`\n  template <size_t MortarDim>\n  void orient_on_slice(\n      const Index<MortarDim>& slice_extents, const size_t sliced_dim,\n      const OrientationMap<MortarDim + 1>& orientation_of_neighbor) {\n    this->field_data = orient_variables_on_slice(\n        this->field_data, slice_extents, sliced_dim, orientation_of_neighbor);\n  }\n};\n\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/SimpleMortarData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <utility>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace dg {\n\n/// \\ingroup DiscontinuousGalerkinGroup\n/// \\brief Storage of boundary data on two sides of a mortar\n///\n/// Typically, values are inserted into this container by the flux\n/// communication actions.\ntemplate <typename TemporalId, typename LocalVars, typename RemoteVars>\nclass SimpleMortarData {\n public:\n  SimpleMortarData() = default;\n  SimpleMortarData(const SimpleMortarData&) = default;\n  SimpleMortarData(SimpleMortarData&&) = default;\n  SimpleMortarData& operator=(const SimpleMortarData&) = default;\n  SimpleMortarData& operator=(SimpleMortarData&&) = default;\n  ~SimpleMortarData() = default;\n\n  /// The argument is ignored.  It exists for compatibility with\n  /// BoundaryHistory.\n  explicit SimpleMortarData(const size_t /*integration_order*/) {}\n\n  /// These functions do nothing.  They exist for compatibility with\n  /// BoundaryHistory.\n  /// @{\n  size_t integration_order() const { return 0; }\n  void integration_order(const size_t /*integration_order*/) {}\n  /// @}\n\n  /// Add a value.  This function must be called once between calls to\n  /// extract.\n  /// @{\n  void local_insert(TemporalId temporal_id, LocalVars vars);\n  void remote_insert(TemporalId temporal_id, RemoteVars vars);\n  /// @}\n\n  /// Return the inserted data and reset the state to empty.\n  std::pair<LocalVars, RemoteVars> extract();\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// Retrieve the local data at `temporal_id`\n  const LocalVars& local_data(const TemporalId& temporal_id) const {\n    ASSERT(local_data_, \"Local data not available.\");\n    ASSERT(temporal_id == temporal_id_,\n           \"Only have local data at temporal_id \"\n               << temporal_id_ << \", but requesting at \" << temporal_id);\n    return *local_data_;\n  };\n\n  /// Retrieve the remote data at `temporal_id`\n  const RemoteVars& remote_data(const TemporalId& temporal_id) const {\n    ASSERT(remote_data_, \"Remote data not available.\");\n    ASSERT(temporal_id == temporal_id_,\n           \"Only have remote data at temporal_id \"\n               << temporal_id_ << \", but requesting at \" << temporal_id);\n    return *remote_data_;\n  };\n\n private:\n  TemporalId temporal_id_{};\n  std::optional<LocalVars> local_data_{};\n  std::optional<RemoteVars> remote_data_{};\n};\n\ntemplate <typename TemporalId, typename LocalVars, typename RemoteVars>\nvoid SimpleMortarData<TemporalId, LocalVars, RemoteVars>::local_insert(\n    TemporalId temporal_id, LocalVars vars) {\n  ASSERT(not local_data_.has_value(), \"Already received local data.\");\n  ASSERT(not remote_data_.has_value() or temporal_id == temporal_id_,\n         \"Received local data at \" << temporal_id\n                                   << \", but already have remote data at \"\n                                   << temporal_id_);\n  temporal_id_ = std::move(temporal_id);\n  local_data_ = std::move(vars);\n}\n\ntemplate <typename TemporalId, typename LocalVars, typename RemoteVars>\nvoid SimpleMortarData<TemporalId, LocalVars, RemoteVars>::remote_insert(\n    TemporalId temporal_id, RemoteVars vars) {\n  ASSERT(not remote_data_.has_value(), \"Already received remote data.\");\n  ASSERT(not local_data_.has_value() or temporal_id == temporal_id_,\n         \"Received remote data at \" << temporal_id\n                                    << \", but already have local data at \"\n                                    << temporal_id_);\n  temporal_id_ = std::move(temporal_id);\n  remote_data_ = std::move(vars);\n}\n\ntemplate <typename TemporalId, typename LocalVars, typename RemoteVars>\nstd::pair<LocalVars, RemoteVars>\nSimpleMortarData<TemporalId, LocalVars, RemoteVars>::extract() {\n  ASSERT(local_data_.has_value() and remote_data_.has_value(),\n         \"Tried to extract boundary data, but do not have \"\n             << (local_data_ ? \"remote\" : remote_data_ ? \"local\" : \"any\")\n             << \" data.\");\n  auto result =\n      std::make_pair(std::move(*local_data_), std::move(*remote_data_));\n  local_data_ = std::nullopt;\n  remote_data_ = std::nullopt;\n  return result;\n}\n\ntemplate <typename TemporalId, typename LocalVars, typename RemoteVars>\nvoid SimpleMortarData<TemporalId, LocalVars, RemoteVars>::pup(PUP::er& p) {\n  p | temporal_id_;\n  p | local_data_;\n  p | remote_data_;\n}\n\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Formulation.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Formulation.hpp\n  OptionsGroup.hpp\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp\"\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n\nnamespace dg::Tags {\ndg::Formulation Formulation::create_from_options(\n    const dg::Formulation& formulation) {\n  return formulation;\n}\n}  // namespace dg::Tags\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/OptionsGroup.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace dg {\nnamespace OptionTags {\nstruct Formulation {\n  using type = dg::Formulation;\n  using group = DiscontinuousGalerkinGroup;\n  static constexpr Options::String help =\n      \"Discontinuous Galerkin formulation to use, e.g. StrongInertial for the \"\n      \"strong form.\";\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief The DG formulation to use.\n */\nstruct Formulation : db::SimpleTag {\n  using type = dg::Formulation;\n\n  using option_tags = tmpl::list<OptionTags::Formulation>;\n  static constexpr bool pass_metavariables = false;\n  static dg::Formulation create_from_options(\n      const dg::Formulation& formulation);\n};\n}  // namespace Tags\n}  // namespace dg\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/Tags/OptionsGroup.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/SpatialDiscretization/OptionTags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace dg::OptionTags {\n/*!\n * \\brief Group holding options for controlling the DG discretization.\n *\n * For example, this would hold whether to use the strong or weak form, what\n * boundary correction/numerical flux to use, and which quadrature rule to use.\n *\n * \\note The `DiscontinuousGalerkinGroup` is a subgroup of\n * `SpatialDiscretization::OptionTags::SpatialDiscretizationGroup`.\n */\nstruct DiscontinuousGalerkinGroup {\n  static std::string name() { return \"DiscontinuousGalerkin\"; }\n  static constexpr Options::String help{\n      \"Options controlling the discontinuous Galerkin spatial discretization \"\n      \"of the PDE system.\\n\\n\"\n      \"Contains options such as whether to use the strong or weak form, what \"\n      \"boundary correction/numerical flux to use, and which quadrature rule to \"\n      \"use.\"};\n  using group = SpatialDiscretization::OptionTags::SpatialDiscretizationGroup;\n};\n}  // namespace dg::OptionTags\n"
  },
  {
    "path": "src/NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/SimpleMortarData.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\n/// Functionality related to discontinuous Galerkin schemes\nnamespace dg {}\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup DiscontinuousGalerkinGroup\n/// Data on mortars, indexed by a DirectionalId\ntemplate <typename Tag, size_t VolumeDim>\nstruct Mortars : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = DirectionalIdMap<VolumeDim, typename Tag::type>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup DiscontinuousGalerkinGroup\n/// Size of a mortar, relative to the element face.  That is, the part\n/// of the face that it covers.\ntemplate <size_t Dim>\nstruct MortarSize : db::SimpleTag {\n  using type = std::array<Spectral::SegmentSize, Dim>;\n};\n}  // namespace Tags\n\nnamespace OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Holds the `OptionTags::NumericalFlux` option in the input file\n */\nstruct NumericalFluxGroup {\n  static std::string name() { return \"NumericalFlux\"; }\n  static constexpr Options::String help = \"The numerical flux scheme\";\n};\n\n/*!\n * \\ingroup OptionTagsGroup\n * \\brief The option tag that retrieves the parameters for the numerical\n * flux from the input file\n */\ntemplate <typename NumericalFluxType>\nstruct NumericalFlux {\n  static std::string name() { return pretty_type::name<NumericalFluxType>(); }\n  static constexpr Options::String help = \"Options for the numerical flux\";\n  using type = NumericalFluxType;\n  using group = NumericalFluxGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief The global cache tag for the numerical flux\n */\ntemplate <typename NumericalFluxType>\nstruct NumericalFlux : db::SimpleTag {\n  using type = NumericalFluxType;\n  using option_tags =\n      tmpl::list<::OptionTags::NumericalFlux<NumericalFluxType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static NumericalFluxType create_from_options(\n      const NumericalFluxType& numerical_flux) {\n    return numerical_flux;\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/AoWeno.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/AoWeno.hpp\"\n\n#include <array>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace fd::reconstruction {\ntemplate <size_t Dim>\nstd::tuple<\n    void (*)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n             gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n             const gsl::span<const double>&,\n             const DirectionMap<Dim, gsl::span<const double>>&,\n             const Index<Dim>&, size_t, double, double, double),\n    void (*)(gsl::not_null<DataVector*>, const DataVector&, const DataVector&,\n             const Index<Dim>&, const Index<Dim>&, const Direction<Dim>&,\n             const double&, const double&, const double&),\n    void (*)(gsl::not_null<DataVector*>, const DataVector&, const DataVector&,\n             const Index<Dim>&, const Index<Dim>&, const Direction<Dim>&,\n             const double&, const double&, const double&)>\naoweno_53_function_pointers(const size_t nonlinear_weight_exponent) {\n  switch (nonlinear_weight_exponent) {\n    case 2:\n      return {&aoweno_53<2, Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Lower,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<2>, true,\n                  Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Upper,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<2>, true,\n                  Dim>};\n    case 4:\n      return {&aoweno_53<4, Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Lower,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<4>, true,\n                  Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Upper,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<4>, true,\n                  Dim>};\n    case 6:\n      return {&aoweno_53<6, Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Lower,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<6>, true,\n                  Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Upper,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<6>, true,\n                  Dim>};\n    case 8:\n      return {&aoweno_53<8, Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Lower,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<8>, true,\n                  Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Upper,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<8>, true,\n                  Dim>};\n    case 10:\n      return {&aoweno_53<10, Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Lower,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<10>, true,\n                  Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Upper,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<10>, true,\n                  Dim>};\n    case 12:\n      return {&aoweno_53<12, Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Lower,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<12>, true,\n                  Dim>,\n              &::fd::reconstruction::reconstruct_neighbor<\n                  Side::Upper,\n                  ::fd::reconstruction::detail::AoWeno53Reconstructor<12>, true,\n                  Dim>};\n    default:\n      ERROR(\"Unsupported nonlinear weight exponent \"\n            << nonlinear_weight_exponent << \" only have 2,4,6,8,10,12 allowed\");\n  };\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                           \\\n  template std::tuple<                                                   \\\n      void (*)(gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>, \\\n               gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>, \\\n               const gsl::span<const double>&,                           \\\n               const DirectionMap<DIM(data), gsl::span<const double>>&,  \\\n               const Index<DIM(data)>&, size_t, double, double, double), \\\n      void (*)(gsl::not_null<DataVector*>, const DataVector&,            \\\n               const DataVector&, const Index<DIM(data)>&,               \\\n               const Index<DIM(data)>&, const Direction<DIM(data)>&,     \\\n               const double&, const double&, const double&),             \\\n      void (*)(gsl::not_null<DataVector*>, const DataVector&,            \\\n               const DataVector&, const Index<DIM(data)>&,               \\\n               const Index<DIM(data)>&, const Direction<DIM(data)>&,     \\\n               const double&, const double&, const double&)>             \\\n  aoweno_53_function_pointers(const size_t nonlinear_weight_exponent);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define NONLINEAR_WEIGHT_EXPONENT(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void                                                                \\\n  reconstruct<AoWeno53Reconstructor<NONLINEAR_WEIGHT_EXPONENT(data)>>(         \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_upper_side_of_face_vars,                               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_lower_side_of_face_vars,                               \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Index<DIM(data)>& volume_extents,                                  \\\n      const size_t number_of_variables, const double& gamma_hi,                \\\n      const double& gamma_lo, const double& epsilon);\n\nnamespace detail {\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (2, 4, 6, 8, 10, 12))\n}  // namespace detail\n\n#undef INSTANTIATION\n\n#define SIDE(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define EXTERIOR_CELL(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct_neighbor<                                          \\\n      SIDE(data),                                                              \\\n      detail::AoWeno53Reconstructor<NONLINEAR_WEIGHT_EXPONENT(data)>,          \\\n      EXTERIOR_CELL(data)>(                                                    \\\n      gsl::not_null<DataVector*> face_data, const DataVector& volume_data,     \\\n      const DataVector& neighbor_data, const Index<DIM(data)>& volume_extents, \\\n      const Index<DIM(data)>& ghost_data_extents,                              \\\n      const Direction<DIM(data)>& direction_to_reconstruct,                    \\\n      const double& gamma_hi, const double& gamma_lo, const double& epsilon);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (2, 4, 6, 8, 10, 12),\n                        (Side::Upper, Side::Lower), (true, false))\n\n#undef INSTANTIATION\n#undef SIDE\n#undef NONLINEAR_WEIGHT_EXPONENT\n#undef DIM\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/AoWeno.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <utility>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace fd::reconstruction {\nnamespace detail {\ntemplate <size_t NonlinearWeightExponent>\nstruct AoWeno53Reconstructor {\n  SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n      const double* const u, const int stride, const double gamma_hi,\n      const double gamma_lo, const double epsilon) {\n    ASSERT(gamma_hi <= 1.0 and gamma_hi >= 0.0,\n           \"gamma_hi must be in [0.0, 1.0] but is \" << gamma_hi);\n    ASSERT(gamma_lo <= 1.0 and gamma_lo >= 0.0,\n           \"gamma_lo must be in [0.0, 1.0] but is \" << gamma_lo);\n    ASSERT(epsilon > 0.0,\n           \"epsilon must be greater than zero but is \" << epsilon);\n\n    const std::array moments_sr3_1{\n        1.041666666666666 * u[0] - 0.08333333333333333 * u[-stride] +\n            0.04166666666666666 * u[-2 * stride],\n        0.5 * u[-2 * stride] - 2.0 * u[-stride] + 1.5 * u[0],\n        0.5 * u[-2 * stride] - u[-stride] + 0.5 * u[0]};\n    const std::array moments_sr3_2{0.04166666666666666 * u[stride] +\n                                       0.9166666666666666 * u[0] +\n                                       0.04166666666666666 * u[-stride],\n                                   0.5 * (u[stride] - u[-stride]),\n                                   0.5 * u[-stride] - u[0] + 0.5 * u[stride]};\n    const std::array moments_sr3_3{\n        0.04166666666666666 * u[2 * stride] - 0.08333333333333333 * u[stride] +\n            1.04166666666666666 * u[0],\n        -1.5 * u[0] + 2.0 * u[stride] - 0.5 * u[2 * stride],\n        0.5 * u[0] - u[stride] + 0.5 * u[2 * stride]};\n    const std::array moments_sr5{-2.95138888888888881e-03 * u[-2 * stride] +\n                                     5.34722222222222196e-02 * u[-stride] +\n                                     8.98958333333333304e-01 * u[0] +\n                                     5.34722222222222196e-02 * u[stride] +\n                                     -2.95138888888888881e-03 * u[2 * stride],\n                                 7.08333333333333315e-02 * u[-2 * stride] +\n                                     -6.41666666666666718e-01 * u[-stride] +\n                                     6.41666666666666718e-01 * u[stride] +\n                                     -7.08333333333333315e-02 * u[2 * stride],\n                                 -3.27380952380952397e-02 * u[-2 * stride] +\n                                     6.30952380952380931e-01 * u[-stride] +\n                                     -1.19642857142857140e+00 * u[0] +\n                                     6.30952380952380931e-01 * u[stride] +\n                                     -3.27380952380952397e-02 * u[2 * stride],\n                                 -8.33333333333333287e-02 * u[-2 * stride] +\n                                     1.66666666666666657e-01 * u[-stride] +\n                                     -1.66666666666666657e-01 * u[stride] +\n                                     8.33333333333333287e-02 * u[2 * stride],\n                                 4.16666666666666644e-02 * u[-2 * stride] +\n                                     -1.66666666666666657e-01 * u[-stride] +\n                                     2.50000000000000000e-01 * u[0] +\n                                     -1.66666666666666657e-01 * u[stride] +\n                                     4.16666666666666644e-02 * u[2 * stride]};\n\n    // These are the \"first alternative\" oscillation indicators\n    constexpr double beta_r3_factor = 37.0 / 3.0;\n    const std::array beta_r3{\n        square(moments_sr3_1[1]) + beta_r3_factor * square(moments_sr3_1[2]),\n        square(moments_sr3_2[1]) + beta_r3_factor * square(moments_sr3_2[2]),\n        square(moments_sr3_3[1]) + beta_r3_factor * square(moments_sr3_3[2])};\n    const double beta_sr5 = square(moments_sr5[1]) +\n                            61.0 / 5.0 * moments_sr5[1] * moments_sr5[3] +\n                            37.0 / 3.0 * square(moments_sr5[2]) +\n                            1538.0 / 7.0 * moments_sr5[2] * moments_sr5[4] +\n                            8973.0 / 50.0 * square(moments_sr5[3]) +\n                            167158.0 / 49.0 * square(moments_sr5[4]);\n\n    // Compute linear and normalized nonlinear weights\n    const std::array linear_weights{\n        gamma_hi, 0.5 * (1.0 - gamma_hi) * (1.0 - gamma_lo),\n        (1.0 - gamma_hi) * gamma_lo, 0.5 * (1.0 - gamma_hi) * (1.0 - gamma_lo)};\n    std::array nonlinear_weights{\n        linear_weights[0] / pow<NonlinearWeightExponent>(beta_sr5 + epsilon),\n        linear_weights[1] / pow<NonlinearWeightExponent>(beta_r3[0] + epsilon),\n        linear_weights[2] / pow<NonlinearWeightExponent>(beta_r3[1] + epsilon),\n        linear_weights[3] / pow<NonlinearWeightExponent>(beta_r3[2] + epsilon)};\n    const double normalization = nonlinear_weights[0] + nonlinear_weights[1] +\n                                 nonlinear_weights[2] + nonlinear_weights[3];\n    for (double& nw : nonlinear_weights) {\n      nw /= normalization;\n    }\n\n    const std::array<double, 5> moments{\n        {nonlinear_weights[0] / linear_weights[0] *\n                 (moments_sr5[0] - linear_weights[1] * moments_sr3_1[0] -\n                  linear_weights[2] * moments_sr3_2[0] -\n                  linear_weights[3] * moments_sr3_3[0]) +\n             nonlinear_weights[1] * moments_sr3_1[0] +\n             nonlinear_weights[2] * moments_sr3_2[0] +\n             nonlinear_weights[3] * moments_sr3_3[0],\n         nonlinear_weights[0] / linear_weights[0] *\n                 (moments_sr5[1] - linear_weights[1] * moments_sr3_1[1] -\n                  linear_weights[2] * moments_sr3_2[1] -\n                  linear_weights[3] * moments_sr3_3[1]) +\n             nonlinear_weights[1] * moments_sr3_1[1] +\n             nonlinear_weights[2] * moments_sr3_2[1] +\n             nonlinear_weights[3] * moments_sr3_3[1],\n         nonlinear_weights[0] / linear_weights[0] *\n                 (moments_sr5[2] - linear_weights[1] * moments_sr3_1[2] -\n                  linear_weights[2] * moments_sr3_2[2] -\n                  linear_weights[3] * moments_sr3_3[2]) +\n             nonlinear_weights[1] * moments_sr3_1[2] +\n             nonlinear_weights[2] * moments_sr3_2[2] +\n             nonlinear_weights[3] * moments_sr3_3[2],\n         nonlinear_weights[0] / linear_weights[0] * moments_sr5[3],\n         nonlinear_weights[0] / linear_weights[0] * moments_sr5[4]}};\n\n    // The polynomial values (excluding the L_0(x)) at the lower/upper faces.\n    // The polynomials are (x in [-0.5,0.5]):\n    // L0(x)=1.0 (not in array)\n    // L1(x)=x\n    // L2(x)=x^2-1/12\n    // L3(x)=x^3 - (3/20) x\n    // L4(x)=x^4 - (3/14) x^2 + 3/560\n    const std::array polys_at_plus_half{0.5, 0.16666666666666666, 0.05,\n                                        0.014285714285714289};\n\n    return {{moments[0] - polys_at_plus_half[0] * moments[1] +\n                 polys_at_plus_half[1] * moments[2] -\n                 polys_at_plus_half[2] * moments[3] +\n                 polys_at_plus_half[3] * moments[4],\n             moments[0] + polys_at_plus_half[0] * moments[1] +\n                 polys_at_plus_half[1] * moments[2] +\n                 polys_at_plus_half[2] * moments[3] +\n                 polys_at_plus_half[3] * moments[4]}};\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() { return 5; }\n};\n}  // namespace detail\n\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Performs an adaptive order (AO) WENO reconstruction using a single 5th\n * order stencil and a 3rd-order CWENO scheme.\n *\n * The AO-WENO(5,3) scheme is based on the scheme presented in\n * \\cite Balsara2016780 but adjusted to do reconstruction on variables instead\n * of fluxes. The Legendre basis functions on the domain \\f$\\xi\\in[-1/2,1/2]\\f$\n * are given by:\n *\n * \\f{align*}{\n * L_0(\\xi) &= 1 \\\\\n * L_1(\\xi) &= \\xi \\\\\n * L_2(\\xi) &= \\xi^2-\\frac{1}{12} \\\\\n * L_3(\\xi) &= \\xi^3 - \\frac{3}{20} \\xi \\\\\n * L_4(\\xi) &= \\xi^4 - \\frac{3}{14} \\xi^2 + \\frac{3}{560}\n * \\f}\n *\n * The oscillation indicators are given by\n *\n * \\f{align*}{\n * \\beta_l = \\sum_{k=1}^{p}\\sum_{m=1}^{p}\\sigma_{km} u_{k,l} u_{m,l}\n * \\f}\n *\n * where \\f$p\\f$ is the maximum degree of the basis function used for the\n * stencil \\f$l\\f$, and\n *\n * \\f{align*}{\n * \\sigma_{km}=\\sum_{i=1}^{p}\\int_{-1/2}^{1/2}\n * \\frac{d^i L_k(\\xi)}{d\\xi^i}\\frac{d^i L_m(\\xi)}{d\\xi^i}d\\xi\n * \\f}\n *\n * We write the 3rd-order reconstructed polynomial \\f$P^{r3}_l(\\xi)\\f$\n * associated with the stencil \\f$S^{r3}_l\\f$ as\n *\n * \\f{align*}{\n * P^{r3}_l(\\xi) = u_0 +u_{\\xi} L_1(\\xi) + u_{\\xi 2} L_2(\\xi)\n * \\f}\n *\n * For the stencil \\f$S^{r3}_1\\f$ we get\n *\n * \\f{align*}{\n *  u_0 &= \\frac{25}{24} u_{j} -\\frac{1}{12} u_{j-1} + \\frac{1}{24} u_{j-2} \\\\\n *  u_{\\xi} &= \\frac{1}{2}u_{j-2} - 2 u_{j-1} + \\frac{3}{2} u_j \\\\\n *  u_{\\xi2} &= \\frac{1}{2} u_{j-2} - u_{j-1} + \\frac{1}{2} u_j\n * \\f}\n *\n * For the stencil \\f$S^{r3}_2\\f$ we get\n *\n * \\f{align*}{\n *  u_0 &= \\frac{1}{24} u_{j-1} + \\frac{11}{12} u_{j} + \\frac{1}{24} u_{j+1} \\\\\n *  u_{\\xi} &= \\frac{1}{2}(u_{j+1} - u_{j-1}) \\\\\n *  u_{\\xi2} &= \\frac{1}{2} u_{j-1} - u_{j} + \\frac{1}{2} u_{j+1}\n * \\f}\n *\n * For the stencil \\f$S^{r3}_3\\f$ we get\n *\n * \\f{align*}{\n *  u_0 &= \\frac{25}{24} u_{j} -\\frac{1}{12} u_{j+1} + \\frac{1}{24} u_{j+2} \\\\\n *  u_{\\xi} &= -\\frac{1}{2}u_{j+2} + 2 u_{j+1} - \\frac{3}{2} u_j \\\\\n *  u_{\\xi2} &= \\frac{1}{2} u_{j+2} - u_{j+1} + \\frac{1}{2} u_j\n * \\f}\n *\n * The oscillation indicator for the 3rd-order stencils is given by\n *\n * \\f{align*}{\n *  \\beta^{r3}_l = \\left(u_{\\xi}\\right)^2+\\frac{13}{3}\\left(u_{\\xi2}\\right)^2\n * \\f}\n *\n * We write the 5th-order reconstructed polynomial \\f$P^{r5}(\\xi)\\f$ as\n *\n * \\f{align*}{\n * P^{r5}_l(\\xi) = u_0 +u_{\\xi} L_1(\\xi) + u_{\\xi 2} L_2(\\xi)\n *  + u_{\\xi3} L_3(\\xi) + u_{\\xi4} L_4(\\xi)\n * \\f}\n *\n * with\n *\n * \\f{align*}{\n * u_0 &= -\\frac{17}{5760} u_{j-2} + \\frac{77}{1440} u_{j-1} +\n *        \\frac{863}{960} u_{j} + \\frac{77}{1440} u_{j+1} -\n *        \\frac{17}{5760} u_{j+2} \\\\\n * u_{\\xi} &= \\frac{17}{240} u_{j-2} - \\frac{77}{120} u_{j-1} +\n *           \\frac{77}{120} u_{j+1} - \\frac{17}{240} u_{j+2} \\\\\n * u_{\\xi2} &= -\\frac{11}{336} u_{j-2} + \\frac{53}{84} u_{j-1} -\n *            \\frac{67}{56} u_{j} + \\frac{53}{84} u_{j+1} -\n *            \\frac{11}{336} u_{j+2} \\\\\n * u_{\\xi3} &= -\\frac{1}{12} u_{j-2} + \\frac{1}{6} u_{j-1}\n *            -\\frac{1}{6} u_{j+1} + \\frac{1}{12} u_{j+2} \\\\\n * u_{\\xi4} &= \\frac{1}{24} u_{j-2} -\\frac{1}{6} u_{j-1}\n *           +\\frac{1}{4} u_{j} - \\frac{1}{6}u_{j+1}+\\frac{1}{24}u_{j+2}\n * \\f}\n *\n * The oscillation indicator is given by\n *\n * \\f{align*}{\n *  \\beta^{r5}&=\\left(u_{\\xi}+\\frac{1}{10}u_{\\xi3}\\right)^2 \\\\\n *             &+\\frac{13}{3}\\left(u_{\\xi2}+\\frac{123}{455}u_{\\xi4}\\right)^2\\\\\n *             &+\\frac{781}{20}(u_{\\xi3})^2+\\frac{1421461}{2275}(u_{\\xi4})^2\n * \\f}\n *\n * There are two linear weights \\f$\\gamma_{\\mathrm{hi}}\\f$ and\n * \\f$\\gamma_{\\mathrm{lo}}\\f$. \\f$\\gamma_{\\mathrm{hi}}\\f$ controls how much of\n * the 5th-order stencil is used in smooth regions, while\n * \\f$\\gamma_{\\mathrm{lo}}\\f$ controls the linear weight of the central\n * 3rd-order stencil. For larger \\f$\\gamma_{\\mathrm{lo}}\\f$, the 3rd-order\n * method is more centrally weighted. The linear weights for the stencils\n * are given by\n *\n * \\f{align*}{\n *  \\gamma^{r5}&=\\gamma_{\\mathrm{hi}} \\\\\n *  \\gamma^{r3}_1& = (1-\\gamma_{\\mathrm{hi}})(1-\\gamma_{\\mathrm{lo}})/2 \\\\\n *  \\gamma^{r3}_2& = (1-\\gamma_{\\mathrm{hi}})\\gamma_{\\mathrm{lo}} \\\\\n *  \\gamma^{r3}_3& = (1-\\gamma_{\\mathrm{hi}})(1-\\gamma_{\\mathrm{lo}})/2\n * \\f}\n *\n * We use the standard nonlinear weights instead of the \"Z\" weights of\n * \\cite Borges20083191\n *\n * \\f{align*}{\n *  w^{r5}&=\\frac{\\gamma^{r5}}{(\\beta^{r5}+\\epsilon)^q} \\\\\n *  w^{r3}_l&=\\frac{\\gamma^{r3}_l}{(\\beta^{r3}_l+\\epsilon)^q}\n * \\f}\n *\n * where \\f$\\epsilon\\f$ is a small number used to avoid division by zero. The\n * normalized nonlinear weights are denoted by \\f$\\bar{w}^{r5}\\f$ and\n * \\f$\\bar{w}_l^{r3}\\f$. The final reconstructed polynomial \\f$P(\\xi)\\f$ is\n * given by\n *\n * \\f{align*}{\n *  P(\\xi)&=\\frac{\\bar{w}^{r5}}{\\gamma^{r5}}\n *   \\left(P^{r5}(\\xi)-\\gamma^{r3}_1P^{r3}_1(\\xi)\n *         -\\gamma^{r3}_2P^{r3}_2(\\xi)-\\gamma^{r3}_3P^{r3}_3(\\xi)\\right) \\\\\n *  &+\\bar{w}^{r3}_1P^{r3}_1(\\xi)+\\bar{w}^{r3}_2P^{r3}_2(\\xi)\n *  +\\bar{w}^{r3}_3P^{r3}_3(\\xi)\n * \\f}\n *\n * The weights \\f$\\gamma_{\\mathrm{hi}}\\f$ and \\f$\\gamma_{\\mathrm{lo}}\\f$\n * are typically chosen to be in the range \\f$[0.85,0.95]\\f$.\n *\n * ### First alternative oscillation indicators\n *\n * Instead of integrating over just the cell, we can instead integrate the basis\n * functions over the entire fit interval, \\f$[-5/2,5/2]\\f$. Using this interval\n * for the \\f$S^{r3}_l\\f$ and the \\f$S^{r5}\\f$ stencils we get\n *\n * \\f{align*}{\n *  \\beta^{r3}_l&=(u_{\\xi})^2 + \\frac{37}{3} (u_{\\xi2})^2 \\\\\n *  \\beta^{r5}  &=\\left(u_{\\xi}+\\frac{61}{10}u_{\\xi3}\\right)^2\n *   + \\frac{569}{4}u_{\\xi3}^2 \\\\\n *  &+ \\frac{1}{8190742}\\left(5383 u_{\\xi2} + 167158 u_{\\xi4}\\right)^2\n *  +\\frac{4410763}{501474}(u_{\\xi2})^2 \\\\\n * &=u_{\\xi}^2 + \\frac{61}{5}u_{\\xi}u_{\\xi3} + \\frac{37}{3}u_{\\xi2}^2\n *  + \\frac{1538}{7}u_{\\xi2}u_{\\xi4} \\\\\n * &+ \\frac{8973}{50}u_{\\xi3}^2 + \\frac{167158}{49}u_{\\xi4}^2\n * \\f}\n *\n * Note that the indicator is manifestly non-negative, a required property of\n * oscillation indicators. These indicators weight high modes more, which means\n * the scheme is more sensitive to high-frequency features in the solution.\n *\n * \\note currently it is the alternative indicators that are used. However, an\n * option to control which are used can readily be added, probably best done\n * as a template parameter with `if constexpr` to avoid conditionals inside\n * tight loops.\n */\ntemplate <size_t NonlinearWeightExponent, size_t Dim>\nvoid aoweno_53(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const double gamma_hi, const double gamma_lo, const double epsilon) {\n  detail::reconstruct<detail::AoWeno53Reconstructor<NonlinearWeightExponent>>(\n      reconstructed_upper_side_of_face_vars,\n      reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n      volume_extents, number_of_variables, gamma_hi, gamma_lo, epsilon);\n}\n\n/*!\n * \\brief Returns function pointers to the `aoweno_53` function, lower neighbor\n * reconstruction, and upper neighbor reconstruction.\n *\n * This is useful for controlling template parameters like the\n * `NonlinearWeightExponent` from an input file by setting a function pointer.\n * Note that the reason the reconstruction functions instead of say the\n * `pointwise` member function is returned is to avoid function pointers inside\n * tight loops.\n */\ntemplate <size_t Dim>\nauto aoweno_53_function_pointers(size_t nonlinear_weight_exponent)\n    -> std::tuple<void (*)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                           gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                           const gsl::span<const double>&,\n                           const DirectionMap<Dim, gsl::span<const double>>&,\n                           const Index<Dim>&, size_t, double, double, double),\n                  void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                           const DataVector&, const Index<Dim>&,\n                           const Index<Dim>&, const Direction<Dim>&,\n                           const double&, const double&, const double&),\n                  void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                           const DataVector&, const Index<Dim>&,\n                           const Index<Dim>&, const Direction<Dim>&,\n                           const double&, const double&, const double&)>;\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY FiniteDifference)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AoWeno.cpp\n  DerivativeOrder.cpp\n  FallbackReconstructorType.cpp\n  Filter.cpp\n  Minmod.cpp\n  MonotonicityPreserving5.cpp\n  MonotonisedCentral.cpp\n  NonUniform1D.cpp\n  PartialDerivatives.cpp\n  PositivityPreservingAdaptiveOrder.cpp\n  SecondPartialDerivatives.cpp\n  Unlimited.cpp\n  Wcns5z.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AoWeno.hpp\n  DerivativeOrder.hpp\n  FallbackReconstructorType.hpp\n  Filter.hpp\n  FiniteDifference.hpp\n  HighOrderFluxCorrection.hpp\n  Minmod.hpp\n  MonotonicityPreserving5.hpp\n  MonotonisedCentral.hpp\n  NeighborDataAsVariables.hpp\n  NonUniform1D.hpp\n  PartialDerivatives.hpp\n  PartialDerivatives.tpp\n  PositivityPreservingAdaptiveOrder.hpp\n  Reconstruct.hpp\n  Reconstruct.tpp\n  SecondPartialDerivatives.hpp\n  SecondPartialDerivatives.tpp\n  Unlimited.hpp\n  Wcns5z.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Utilities\n  PRIVATE\n  DataStructures\n  DomainStructure\n  ErrorHandling\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/DerivativeOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace fd {\nstd::ostream& operator<<(std::ostream& os, DerivativeOrder der_order) {\n  switch (der_order) {\n    case DerivativeOrder::OneHigherThanRecons:\n      return os << \"OneHigherThanRecons\";\n    case DerivativeOrder::OneHigherThanReconsButFiveToFour:\n      return os << \"OneHigherThanReconsButFiveToFour\";\n    case DerivativeOrder::Two:\n      return os << \"2\";\n    case DerivativeOrder::Four:\n      return os << \"4\";\n    case DerivativeOrder::Six:\n      return os << \"6\";\n    case DerivativeOrder::Eight:\n      return os << \"8\";\n    case DerivativeOrder::Ten:\n      return os << \"10\";\n    default:\n      ERROR(\"Unknown value for DerivativeOrder\");\n  };\n}\n}  // namespace fd\n\ntemplate <>\nfd::DerivativeOrder\nOptions::create_from_yaml<fd::DerivativeOrder>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (type_read == get_output(fd::DerivativeOrder::OneHigherThanRecons)) {\n    return fd::DerivativeOrder::OneHigherThanRecons;\n  } else if (type_read ==\n             get_output(\n                 fd::DerivativeOrder::OneHigherThanReconsButFiveToFour)) {\n    return fd::DerivativeOrder::OneHigherThanReconsButFiveToFour;\n  } else if (type_read == get_output(fd::DerivativeOrder::Two)) {\n    return fd::DerivativeOrder::Two;\n  } else if (type_read == get_output(fd::DerivativeOrder::Four)) {\n    return fd::DerivativeOrder::Four;\n  } else if (type_read == get_output(fd::DerivativeOrder::Six)) {\n    return fd::DerivativeOrder::Six;\n  } else if (type_read == get_output(fd::DerivativeOrder::Eight)) {\n    return fd::DerivativeOrder::Eight;\n  } else if (type_read == get_output(fd::DerivativeOrder::Ten)) {\n    return fd::DerivativeOrder::Ten;\n  }\n  PARSE_ERROR(\n      options.context(),\n      \"Failed to convert \\\"\"\n          << type_read << \"\\\" to DerivativeOrder. Must be one of '\"\n          << get_output(fd::DerivativeOrder::OneHigherThanRecons) << \"', '\"\n          << get_output(fd::DerivativeOrder::OneHigherThanReconsButFiveToFour)\n          << \"', 2, 4, 6, 8, or 10.\");\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace fd {\n/// Controls which FD derivative order is used.\nenum class DerivativeOrder : int {\n  /// \\brief Use one order high derivative.\n  ///\n  /// For example, if fifth order reconstruction is used, then a sixth-order\n  /// derivative is used.\n  OneHigherThanRecons = -1,\n  /// \\brief Same as `OneHigherThanRecons` except uses a fourth-order derivative\n  /// if fifth-order reconstruction was used.\n  OneHigherThanReconsButFiveToFour = -2,\n  /// \\brief Use 2nd order derivatives\n  Two = 2,\n  /// \\brief Use 4th order derivatives\n  Four = 4,\n  /// \\brief Use 6th order derivatives\n  Six = 6,\n  /// \\brief Use 8th order derivatives\n  Eight = 8,\n  /// \\brief Use 10th order derivatives\n  Ten = 10\n};\n\nstd::ostream& operator<<(std::ostream& os, DerivativeOrder der_order);\n}  // namespace fd\n\ntemplate <>\nstruct Options::create_from_yaml<fd::DerivativeOrder> {\n  template <typename Metavariables>\n  static fd::DerivativeOrder create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nfd::DerivativeOrder\nOptions::create_from_yaml<fd::DerivativeOrder>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nstd::ostream& fd::reconstruction::operator<<(\n    std::ostream& os,\n    const fd::reconstruction::FallbackReconstructorType recons_type) {\n  switch (recons_type) {\n    case fd::reconstruction::FallbackReconstructorType::Minmod:\n      return os << \"Minmod\";\n    case fd::reconstruction::FallbackReconstructorType::MonotonisedCentral:\n      return os << \"MonotonisedCentral\";\n    case fd::reconstruction::FallbackReconstructorType::None:\n      return os << \"None\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Missing a case for operator<<(FallbackReconstructorType)\");\n      // LCOV_EXCL_STOP\n  }\n}\n\ntemplate <>\nfd::reconstruction::FallbackReconstructorType\nOptions::create_from_yaml<fd::reconstruction::FallbackReconstructorType>::\n    create<void>(const Options::Option& options) {\n  const auto recons_type_read = options.parse_as<std::string>();\n  if (recons_type_read == \"Minmod\") {\n    return fd::reconstruction::FallbackReconstructorType::Minmod;\n  } else if (recons_type_read == \"MonotonisedCentral\") {\n    return fd::reconstruction::FallbackReconstructorType::MonotonisedCentral;\n  } else if (recons_type_read == \"None\") {\n    return fd::reconstruction::FallbackReconstructorType::None;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << recons_type_read\n                  << \"\\\" to FallbackReconstructorType. Expected one of: \"\n                     \"{Minmod, MonotonisedCentral, None}.\");\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n\nnamespace fd::reconstruction {\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Possible types of reconstruction routines to fall back to from\n * higher-order reconstruction when using adaptive method.\n *\n * \\note FD reconstructions with input arguments or options (e.g. AoWeno,\n * Wcns5z, ..) are currently not supported as fallback types. We _may_ need it\n * at some point in the future but it would require using a different option\n * parsing method (e.g. factory creation).\n *\n */\nenum class FallbackReconstructorType { Minmod, MonotonisedCentral, None };\n\nstd::ostream& operator<<(\n    std::ostream& os,\n    fd::reconstruction::FallbackReconstructorType recons_type);\n}  // namespace fd::reconstruction\n\ntemplate <>\nstruct Options::create_from_yaml<\n    fd::reconstruction::FallbackReconstructorType> {\n  template <typename Metavariables>\n  static fd::reconstruction::FallbackReconstructorType create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nfd::reconstruction::FallbackReconstructorType\nOptions::create_from_yaml<fd::reconstruction::FallbackReconstructorType>::\n    create<void>(const Options::Option& options);\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Filter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace fd {\nnamespace {\ntemplate <size_t Order, bool UnitStride>\nstruct KoDissipationImpl;\n\ntemplate <bool UnitStride>\nstruct KoDissipationImpl<2, UnitStride> {\n  static constexpr size_t fd_order = 2;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      return epsilon *\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             ((q[-1] + q[1]) - 2.0 * q[0]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return epsilon * ((q[-stride] + q[stride]) - 2.0 * q[0]);\n    }\n  }\n};\n\ntemplate <bool UnitStride>\nstruct KoDissipationImpl<4, UnitStride> {\n  static constexpr size_t fd_order = 4;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      return -epsilon *\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             (0.0625 * (q[-2] + q[2]) - 0.25 * (q[-1] + q[1]) + 0.375 * q[0]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return -epsilon * (0.0625 * (q[-2 * stride] + q[2 * stride]) -\n                         0.25 * (q[-stride] + q[stride]) + 0.375 * q[0]);\n    }\n  }\n};\n\ntemplate <bool UnitStride>\nstruct KoDissipationImpl<6, UnitStride> {\n  static constexpr size_t fd_order = 6;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return epsilon * (0.015625 * (q[-3] + q[3]) - 0.09375 * (q[-2] + q[2]) +\n                        0.234375 * (q[-1] + q[1]) - 0.3125 * q[0]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return epsilon * (0.015625 * (q[-3 * stride] + q[3 * stride]) -\n                        0.09375 * (q[-2 * stride] + q[2 * stride]) +\n                        0.234375 * (q[-stride] + q[stride]) - 0.3125 * q[0]);\n    }\n  }\n};\n\ntemplate <bool UnitStride>\nstruct KoDissipationImpl<8, UnitStride> {\n  static constexpr size_t fd_order = 8;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return epsilon * (-0.00390625 * (q[-4] + q[4]) +\n                        0.03125 * (q[-3] + q[3]) - 0.109375 * (q[-2] + q[2]) +\n                        0.21875 * (q[-1] + q[1]) - 0.2734375 * q[0]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return epsilon * (-0.00390625 * (q[-4 * stride] + q[4 * stride]) +\n                        0.03125 * (q[-3 * stride] + q[3 * stride]) -\n                        0.109375 * (q[-2 * stride] + q[2 * stride]) +\n                        0.21875 * (q[-stride] + q[stride]) - 0.2734375 * q[0]);\n    }\n  }\n};\n\ntemplate <bool UnitStride>\nstruct KoDissipationImpl<10, UnitStride> {\n  static constexpr size_t fd_order = 10;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return epsilon *\n             (0.0009765625 * (q[-5] + q[5]) - 0.009765625 * (q[-4] + q[4]) +\n              0.0439453125 * (q[-3] + q[3]) - 0.1171875 * (q[-2] + q[2]) +\n              0.205078125 * (q[-1] + q[1]) - 0.24609375 * q[0]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return epsilon *\n             (0.0009765625 * (q[-5 * stride] + q[5 * stride]) -\n              0.009765625 * (q[-4 * stride] + q[4 * stride]) +\n              0.0439453125 * (q[-3 * stride] + q[3 * stride]) -\n              0.1171875 * (q[-2 * stride] + q[2 * stride]) +\n              0.205078125 * (q[-stride] + q[stride]) - 0.24609375 * q[0]);\n    }\n  }\n};\n\ntemplate <size_t Order, bool UnitStride>\nstruct LowPassFilterImpl;\n\ntemplate <bool UnitStride>\nstruct LowPassFilterImpl<2, UnitStride> {\n  static constexpr size_t fd_order = 2;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    // 3/8 = 0.375\n    // 3/4 = 0.75\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      return epsilon * (0.375 * (q[-1] + q[1]) - 0.75 * q[0]);\n    } else {\n      return epsilon * (0.375 * (q[-stride] + q[stride]) - 0.75 * q[0]);\n    }\n  }\n};\n\ntemplate <bool UnitStride>\nstruct LowPassFilterImpl<4, UnitStride> {\n  static constexpr size_t fd_order = 4;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    // 125/896 = 0.13950892857142858\n    // 125/224 = 0.5580357142857143\n    // 375/448 = 0.8370535714285714\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      return epsilon *\n             (-0.13950892857142858 * (q[-2] + q[2]) +\n              0.5580357142857143 * (q[-1] + q[1]) - 0.8370535714285714 * q[0]);\n    } else {\n      return epsilon *\n             (-0.13950892857142858 * (q[-2 * stride] + q[2 * stride]) +\n              0.5580357142857143 * (q[-stride] + q[stride]) -\n              0.8370535714285714 * q[0]);\n    }\n  }\n};\n\ntemplate <bool UnitStride>\nstruct LowPassFilterImpl<6, UnitStride> {\n  static constexpr size_t fd_order = 6;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    // 16807/304128 = 0.05526291561447812\n    // 16807/50688 = 0.3315774936868687\n    // 84035/101376 = 0.8289437342171717\n    // 84035/76032 = 1.1052583122895623\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      return epsilon *\n             (0.05526291561447812 * (q[-3] + q[3]) -\n              0.3315774936868687 * (q[-2] + q[2]) +\n              0.8289437342171717 * (q[-1] + q[1]) - 1.1052583122895623 * q[0]);\n    } else {\n      return epsilon * (0.05526291561447812 * (q[-3 * stride] + q[3 * stride]) -\n                        0.3315774936868687 * (q[-2 * stride] + q[2 * stride]) +\n                        0.8289437342171717 * (q[-stride] + q[stride]) -\n                        1.1052583122895623 * q[0]);\n    }\n  }\n};\n\ntemplate <bool UnitStride>\nstruct LowPassFilterImpl<8, UnitStride> {\n  static constexpr size_t fd_order = 8;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const q,\n                                                const int stride,\n                                                const double epsilon) {\n    // 531441/6406400 * 35 / 128 = 0.022682926204654723\n    // 531441/800800 * 35 / 128 = 0.18146340963723778\n    // 531441/228800 * 35 / 128 = 0.6351219337303321\n    // 531441/114400 * 35 / 128 = 1.2702438674606642\n    // 531441/91520 * 35 / 128 = 1.5878048343258304\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      return epsilon *\n             (-0.022682926204654723 * (q[-4] + q[4]) +\n              0.18146340963723778 * (q[-3] + q[3]) -\n              0.6351219337303321 * (q[-2] + q[2]) +\n              1.2702438674606642 * (q[-1] + q[1]) - 1.5878048343258304 * q[0]);\n    } else {\n      return epsilon *\n             (-0.022682926204654723 * (q[-4 * stride] + q[4 * stride]) +\n              0.18146340963723778 * (q[-3 * stride] + q[3 * stride]) -\n              0.6351219337303321 * (q[-2 * stride] + q[2 * stride]) +\n              1.2702438674606642 * (q[-stride] + q[stride]) -\n              1.5878048343258304 * q[0]);\n    }\n  }\n};\n\ntemplate <bool UnitStride>\nstruct LowPassFilterImpl<10, UnitStride> {\n  static constexpr size_t fd_order = 10;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(const double* const /*q*/,\n                                                const int /*stride*/,\n                                                const double /*epsilon*/) {\n    ERROR(\"Not implemented\");\n  }\n};\n\ntemplate <typename FilterComputer, size_t Dim, typename... Args>\nvoid filter_fastest_dim(const gsl::not_null<gsl::span<double>*> derivative,\n                        const gsl::span<const double>& volume_vars,\n                        const gsl::span<const double>& lower_ghost_data,\n                        const gsl::span<const double>& upper_ghost_data,\n                        const Index<Dim>& volume_extents,\n                        const size_t number_of_variables, const Args&... args) {\n  constexpr size_t fd_order = FilterComputer::fd_order;\n  ASSERT(\n      fd_order % 2 == 0,\n      \"The finite difference order with should be even but got \" << fd_order);\n  constexpr size_t stencil_width = fd_order + 1;\n  ASSERT(volume_extents[0] >= stencil_width - 1,\n         \" Subcell volume extent (current value: \"\n             << volume_extents[0]\n             << \") must be not smaller than the stencil width (current value: \"\n             << stencil_width << \") minus 1\");\n  constexpr size_t ghost_zone_for_stencil = fd_order / 2;\n  // Compute the number of ghost cells.\n  const size_t number_of_stripes =\n      volume_extents.slice_away(0).product() * number_of_variables;\n  ASSERT(lower_ghost_data.size() % number_of_stripes == 0,\n         \"The lower ghost data must be a multiple of the number of stripes (\"\n             << number_of_stripes\n             << \"), which is defined as the number of variables (\"\n             << number_of_variables\n             << \") times the number of grid points on a 2d slice (\"\n             << volume_extents.slice_away(0).product() << \")\");\n  ASSERT(upper_ghost_data.size() == lower_ghost_data.size(),\n         \"The lower ghost data size (\"\n             << lower_ghost_data.size()\n             << \") must match the upper ghost data size, \"\n             << upper_ghost_data.size());\n  const size_t ghost_pts_in_neighbor_data =\n      lower_ghost_data.size() / number_of_stripes;\n\n  std::array<double, stencil_width> q{};\n  for (size_t slice = 0; slice < number_of_stripes; ++slice) {\n    const size_t vars_slice_offset = slice * volume_extents[0];\n    const size_t vars_neighbor_slice_offset =\n        slice * ghost_pts_in_neighbor_data;\n\n    // Deal with lower ghost data.\n    for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n      // offset comes from accounting for the 1 extra point in our ghost\n      // cells plus how far away from the boundary we are differentiating.\n      for (size_t j = 0, offset = vars_neighbor_slice_offset +\n                                  ghost_pts_in_neighbor_data -\n                                  (ghost_zone_for_stencil - i);\n           j < ghost_zone_for_stencil - i; ++j) {\n        gsl::at(q, j) = lower_ghost_data[offset + j];\n      }\n      for (size_t j = ghost_zone_for_stencil - i, k = 0; j < stencil_width;\n           ++j, ++k) {\n        gsl::at(q, j) = volume_vars[vars_slice_offset + k];\n      }\n      (*derivative)[vars_slice_offset + i] = FilterComputer::pointwise(\n          q.data() + ghost_zone_for_stencil, 1, args...);\n    }\n\n    // Differentiate in the bulk\n    const size_t slice_end = volume_extents[0] - ghost_zone_for_stencil;\n    for (size_t vars_index = vars_slice_offset + ghost_zone_for_stencil,\n                i = ghost_zone_for_stencil;\n         i < slice_end; ++vars_index, ++i) {\n      // Note: we keep the `stride` here because we may want to\n      // experiment/support non-unit strides in the bulk in the future. For\n      // cells where the derivative needs boundary data we copy into a\n      // `std::array` buffer, which means we always have unit stride.\n      constexpr int stride = 1;\n      (*derivative)[vars_slice_offset + i] =\n          FilterComputer::pointwise(&volume_vars[vars_index], stride, args...);\n    }\n\n    // Differentiate using upper neighbor data\n    for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n      // offset comes from accounting for the 1 extra point in our ghost\n      // cells plus how far away from the boundary we are differentiating.\n      //\n      // Note:\n      // - q has size stencil_width\n      // - we need to copy over (stencil_width - 1 - i) from the volume\n      // - we need to copy (i + 1) from the neighbor\n      //\n      // Here is an example of a case with stencil_width 5:\n      //\n      //  Interior points| Neighbor points\n      // x x x x x x x x | o o o\n      //             ^\n      //         c c c c | c\n      //  c = points used for derivative\n\n      size_t j = 0;\n      for (size_t k =\n               vars_slice_offset + volume_extents[0] - (stencil_width - 1 - i);\n           j < stencil_width - 1 - i; ++j, ++k) {\n        gsl::at(q, j) = volume_vars[k];\n      }\n      for (size_t k = 0; j < stencil_width; ++j, ++k) {\n        gsl::at(q, j) = upper_ghost_data[vars_neighbor_slice_offset + k];\n      }\n\n      (*derivative)[vars_slice_offset + slice_end + i] =\n          FilterComputer::pointwise(q.data() + ghost_zone_for_stencil, 1,\n                                    args...);\n    }\n  }  // for slices\n}\n\ntemplate <typename FilterComputer, size_t Dim, typename... Args>\nvoid filter_impl(\n    const gsl::not_null<gsl::span<double>*> filtered_data,\n    gsl::span<double>* const in_buffer,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const Args&... args) {\n  size_t comp_dim{};\n  if constexpr (Dim == 3) {\n    if (volume_mesh.basis(2) == Spectral::Basis::Cartoon and\n        volume_mesh.basis(1) == Spectral::Basis::Cartoon) {\n      comp_dim = 1;\n    } else if (volume_mesh.basis(2) == Spectral::Basis::Cartoon) {\n      comp_dim = 2;\n    } else {\n      comp_dim = 3;\n    }\n  } else {\n    comp_dim = Dim;\n  }\n#ifdef SPECTRE_DEBUG\n  if constexpr (Dim == 3) {\n    if (comp_dim == 3) {\n      ASSERT(\n          volume_mesh == Mesh<3>(volume_mesh.extents(0), volume_mesh.basis(0),\n                                 volume_mesh.quadrature(0)),\n          \"The mesh must be isotropic, but got \" << volume_mesh);\n    } else if (comp_dim == 2) {\n      ASSERT(volume_mesh.slice_through(0, 1) ==\n                 Mesh<2>(volume_mesh.extents(0), volume_mesh.basis(0),\n                         volume_mesh.quadrature(0)),\n             \"The non-cartoon sub-mesh must be isotropic, but got \"\n                 << volume_mesh);\n    }\n  } else {\n    ASSERT(\n        volume_mesh == Mesh<Dim>(volume_mesh.extents(0), volume_mesh.basis(0),\n                                 volume_mesh.quadrature(0)),\n        \"The mesh must be isotropic, but got \" << volume_mesh);\n  }\n  ASSERT(\n      volume_mesh.basis(0) == Spectral::Basis::FiniteDifference,\n      \"Mesh basis must be FiniteDifference but got \" << volume_mesh.basis(0));\n  ASSERT(volume_mesh.quadrature(0) == Spectral::Quadrature::CellCentered,\n         \"Mesh quadrature must be CellCentered but got \"\n             << volume_mesh.quadrature(0));\n  const size_t number_of_points = volume_mesh.number_of_grid_points();\n  ASSERT(volume_vars.size() == number_of_points * number_of_variables,\n         \"The size of the volume vars must be the number of points (\"\n             << number_of_points << \") times the number of variables (\"\n             << number_of_variables << \") but is \" << volume_vars.size());\n  ASSERT(filtered_data->size() == volume_vars.size(),\n         \"The filtered data must have size \" << volume_vars.size()\n                                             << \" but has size \"\n                                             << filtered_data->size());\n#endif  // SPECTRE_DEBUG\n\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_xi()),\n         \"Couldn't find lower ghost data in lower-xi\");\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_xi()),\n         \"Couldn't find upper ghost data in upper-xi\");\n  const Index<Dim>& volume_extents = volume_mesh.extents();\n\n  filter_fastest_dim<FilterComputer>(\n      filtered_data, volume_vars,\n      ghost_cell_vars.at(Direction<Dim>::lower_xi()),\n      ghost_cell_vars.at(Direction<Dim>::upper_xi()), volume_extents,\n      number_of_variables, args...);\n\n  if constexpr (Dim > 1) {\n    if (comp_dim > 1) {\n      ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_eta()),\n             \"Couldn't find lower ghost data in lower-eta\");\n      ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_eta()),\n             \"Couldn't find upper ghost data in upper-eta\");\n\n      // We transpose from (x,y,z,vars) ordering to (y,z,vars,x) ordering\n      // Might not be the most efficient (unclear), but easiest.\n      // We use a single large buffer for both the y and z derivatives\n      // to reduce the number of memory allocations and improve data locality.\n      // Note: the eta and zeta ghost data sizes are assumed equal to the\n      // xi ghost data size (requires isotropic FD extents).\n      const auto& lower_eta_ghost =\n          ghost_cell_vars.at(Direction<Dim>::lower_eta());\n      const auto& upper_eta_ghost =\n          ghost_cell_vars.at(Direction<Dim>::upper_eta());\n      const size_t filter_size = filtered_data->size();\n      DataVector buffer{};\n      if (in_buffer != nullptr) {\n        ASSERT(\n            (in_buffer->size() >= volume_vars.size() + lower_eta_ghost.size() +\n                                      upper_eta_ghost.size() + 3 * filter_size),\n            \"The buffer must have size greater than or equal to \"\n                << (volume_vars.size() + lower_eta_ghost.size() +\n                    upper_eta_ghost.size() + 3 * filter_size)\n                << \" but has size \" << in_buffer->size());\n        buffer.set_data_ref(in_buffer->data(), in_buffer->size());\n      } else {\n        buffer = DataVector{volume_vars.size() + lower_eta_ghost.size() +\n                            upper_eta_ghost.size() + 3 * filter_size};\n      }\n      raw_transpose(make_not_null(buffer.data()), volume_vars.data(),\n                    volume_extents[0], volume_vars.size() / volume_extents[0]);\n      raw_transpose(make_not_null(&buffer[volume_vars.size()]),\n                    lower_eta_ghost.data(), volume_extents[0],\n                    lower_eta_ghost.size() / volume_extents[0]);\n      raw_transpose(\n          make_not_null(&buffer[volume_vars.size() + lower_eta_ghost.size()]),\n          upper_eta_ghost.data(), volume_extents[0],\n          upper_eta_ghost.size() / volume_extents[0]);\n\n      const size_t filter_offset_in_buffer =\n          volume_vars.size() + lower_eta_ghost.size() + upper_eta_ghost.size();\n      gsl::span<double> filter_view =\n          gsl::make_span(&buffer[filter_offset_in_buffer], filter_size);\n\n      filter_fastest_dim<FilterComputer>(\n          make_not_null(&filter_view),\n          gsl::make_span(buffer.data(), volume_vars.size()),\n          gsl::make_span(&buffer[volume_vars.size()], lower_eta_ghost.size()),\n          gsl::make_span(&buffer[volume_vars.size() + lower_eta_ghost.size()],\n                         upper_eta_ghost.size()),\n          volume_extents, number_of_variables, args...);\n      // Transpose result back and add to filtered_data\n      const gsl::span<double> filter_data_in_xyz_order =\n          gsl::make_span(&buffer[filter_offset_in_buffer + volume_vars.size()],\n                         volume_vars.size());\n      raw_transpose(make_not_null(filter_data_in_xyz_order.data()),\n                    filter_view.data(), filter_view.size() / volume_extents[0],\n                    volume_extents[0]);\n      {\n        DataVector t0{filter_data_in_xyz_order.data(),\n                      filter_data_in_xyz_order.size()};\n        DataVector t1{filtered_data->data(), filtered_data->size()};\n        t1 += t0;\n      }\n      if constexpr (Dim > 2) {\n        if (comp_dim > 2) {\n          ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_zeta()),\n                 \"Couldn't find lower ghost data in lower-zeta\");\n          ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_zeta()),\n                 \"Couldn't find upper ghost data in upper-zeta\");\n\n          const auto& lower_zeta_ghost =\n              ghost_cell_vars.at(Direction<Dim>::lower_zeta());\n          const auto& upper_zeta_ghost =\n              ghost_cell_vars.at(Direction<Dim>::upper_zeta());\n          const size_t chunk_size = volume_extents[0] * volume_extents[1];\n          const size_t number_of_volume_chunks =\n              volume_vars.size() / chunk_size;\n          const size_t number_of_neighbor_chunks =\n              lower_zeta_ghost.size() / chunk_size;\n\n          raw_transpose(make_not_null(buffer.data()), volume_vars.data(),\n                        chunk_size, number_of_volume_chunks);\n          raw_transpose(make_not_null(&buffer[volume_vars.size()]),\n                        lower_zeta_ghost.data(), chunk_size,\n                        number_of_neighbor_chunks);\n          raw_transpose(\n              make_not_null(\n                  &buffer[volume_vars.size() + lower_zeta_ghost.size()]),\n              upper_zeta_ghost.data(), chunk_size, number_of_neighbor_chunks);\n\n          filter_fastest_dim<FilterComputer>(\n              make_not_null(&filter_view),\n              gsl::make_span(buffer.data(), volume_vars.size()),\n              gsl::make_span(&buffer[volume_vars.size()],\n                             lower_zeta_ghost.size()),\n              gsl::make_span(\n                  &buffer[volume_vars.size() + lower_zeta_ghost.size()],\n                  upper_zeta_ghost.size()),\n              volume_extents, number_of_variables, args...);\n          // Transpose result back\n          raw_transpose(make_not_null(filter_data_in_xyz_order.data()),\n                        filter_view.data(), filter_view.size() / chunk_size,\n                        chunk_size);\n          {\n            const DataVector t0{filter_data_in_xyz_order.data(),\n                                filter_data_in_xyz_order.size()};\n            DataVector t1{filtered_data->data(), filtered_data->size()};\n            t1 += t0;\n          }\n        }\n      }\n    }\n  }\n  // Add volume vars to filter\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n  const DataVector view_volume_vars{const_cast<double*>(volume_vars.data()),\n                                    volume_vars.size()};\n  DataVector view_filtered_data{filtered_data->data(), filtered_data->size()};\n  view_filtered_data += view_volume_vars;\n}\n\ntemplate <template <size_t Order, bool UnitStride> class FilterComputer,\n          size_t Dim, typename... Args>\nvoid forward_to_filter_impl(\n    const gsl::not_null<gsl::span<double>*> filtered_data,\n    gsl::span<double>* const buffer, const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order, const Args&... args) {\n  switch (fd_order) {\n    case 2:\n      ::fd::filter_impl<FilterComputer<2, true>>(\n          filtered_data, buffer, volume_vars, ghost_cell_vars, volume_mesh,\n          number_of_variables, args...);\n      break;\n    case 4:\n      ::fd::filter_impl<FilterComputer<4, true>>(\n          filtered_data, buffer, volume_vars, ghost_cell_vars, volume_mesh,\n          number_of_variables, args...);\n      break;\n    case 6:\n      ::fd::filter_impl<FilterComputer<6, true>>(\n          filtered_data, buffer, volume_vars, ghost_cell_vars, volume_mesh,\n          number_of_variables, args...);\n      break;\n    case 8:\n      ::fd::filter_impl<FilterComputer<8, true>>(\n          filtered_data, buffer, volume_vars, ghost_cell_vars, volume_mesh,\n          number_of_variables, args...);\n      break;\n    case 10:\n      ::fd::filter_impl<FilterComputer<10, true>>(\n          filtered_data, buffer, volume_vars, ghost_cell_vars, volume_mesh,\n          number_of_variables, args...);\n      break;\n    default:\n      ERROR(\"Cannot do finite difference filter of order \" << fd_order);\n  };\n}\n}  // namespace\n\ntemplate <size_t Dim>\nvoid low_pass_filter(\n    const gsl::not_null<gsl::span<double>*> filtered_data,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order, const double epsilon) {\n  forward_to_filter_impl<LowPassFilterImpl>(\n      filtered_data, nullptr, volume_vars, ghost_cell_vars, volume_mesh,\n      number_of_variables, fd_order, epsilon);\n}\n\ntemplate <size_t Dim>\nvoid kreiss_oliger_filter(\n    const gsl::not_null<gsl::span<double>*> filtered_data,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order, const double epsilon) {\n  forward_to_filter_impl<KoDissipationImpl>(\n      filtered_data, nullptr, volume_vars, ghost_cell_vars, volume_mesh,\n      number_of_variables, fd_order, epsilon);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void low_pass_filter(                                               \\\n      gsl::not_null<gsl::span<double>*> filtered_data,                         \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Mesh<DIM(data)>& volume_mesh, size_t number_of_variables,          \\\n      size_t fd_order, double epsilon);                                        \\\n  template void kreiss_oliger_filter(                                          \\\n      gsl::not_null<gsl::span<double>*> filtered_data,                         \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Mesh<DIM(data)>& volume_mesh, size_t number_of_variables,          \\\n      size_t fd_order, double epsilon);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Filter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace fd {\n/*!\n * \\brief Apply a low-pass filter to the data.\n *\n * The filter fits a Legendre polynomial of degree equal to `fd_order` to each\n * grid point and its neighboring points, then subtracts out the highest mode\n * contribution at the grid point. This is inspired by a Heaviside filter for\n * Legendre polynomial-based spectral methods.\n *\n * The filter at different orders is given by:\n *\n * \\f{align*}{\n * F^{(9)} u_i&= -\\frac{35 \\times 531441}{128} \\left(\n *                \\frac{1}{6406400} (u_{i-4} + u_{i+4}) -\n *                \\frac{1}{800800} (u_{i-3} + u_{i+3}) +\n *                \\frac{1}{228800} (u_{i-2} + u_{i+2}) -\n *                \\frac{1}{114400} (u_{i-1} + u_{i+1}) +\n *                \\frac{1}{91520} u_i\\right) \\\\\n * F^{(7)} u_i&= \\frac{5 \\times 16807}{16} \\left(\n *                \\frac{1}{95040} (u_{i-3} + u_{i+3}) -\n *                \\frac{1}{15840} (u_{i-2} + u_{i+2}) +\n *                \\frac{1}{6336} (u_{i-1} + u_{i+1}) -\n *                \\frac{1}{4572} u_i\\right) \\\\\n * F^{(5)} u_i&= -\\frac{3 \\times 125}{8} \\left(\n *                \\frac{1}{336} (u_{i-2} + u_{i+2}) -\n *                \\frac{1}{84} (u_{i-1} + u_{i+1}) +\n *                \\frac{1}{56} u_i\\right) \\\\\n * F^{(3)} u_i&= \\frac{1 \\times 3}{2} \\left(\n *                \\frac{1}{4} (u_{i-1} + u_{i+1}) -\n *                \\frac{1}{2} u_i\\right)\n * \\f}\n *\n * Dimensions with Cartoon bases are not filtered as there is nothing to filter.\n *\n * \\note The \\f$F^{(11)}\\f$ filter isn't implemented yet.\n *\n * \\note The argument \\f$\\epsilon\\f$ controls how much of the mode is filter\n * out. \\f$\\epsilon=1\\f$ means the highest mode is completely filtered while\n * \\f$\\epsilon=0\\f$ means it's not at all filtered.\n */\ntemplate <size_t Dim>\nvoid low_pass_filter(\n    gsl::not_null<gsl::span<double>*> filtered_data,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, size_t number_of_variables, size_t fd_order,\n    double epsilon);\n\n/*!\n * \\brief Apply Kreiss-Oliger dissipation \\cite Kreiss1973 of order `fd_order`\n * to the variables.\n *\n * Define the operators \\f$D_+\\f$ and \\f$D_-\\f$ be defined as:\n *\n * \\f{align*}{\n * D_+f_i&=\\frac{(f_{i+1}-f_i)}{\\Delta x} \\\\\n * D_-f_i&=\\frac{(f_i-f_{i-1})}{\\Delta x}\n * \\f}\n *\n * where the subscript \\f$i\\f$ refers to the grid index. The dissipation\n * operators are generally applied dimension-by-dimension, and so we have:\n *\n * \\f{align*}{\n * \\mathcal{D}^{(2m)}=-\\frac{(-1)^m}{2^{2m}}\\Delta x^{2m-1}\n * \\epsilon(D_{+})^m(D_{-})^m\n * \\f}\n *\n * where \\f$\\epsilon\\f$ controls the amount of dissipation and is restricted to\n * \\f$0\\leq\\epsilon\\leq1\\f$, and \\f$m\\f$ is the order of the finite difference\n * derivative so as not to spoil the accuracy of the scheme. That is, for\n * second order FD, \\f$m=2\\f$ and one should use \\f$\\mathcal{D}^{(4)}\\f$.\n * However, this choice requires a larger stencil and whether or not this is\n * necessary also depends on when and how in the algorithm the operators are\n * applied.\n *\n * We arrive at the following operators:\n *\n * \\f{align*}{\n *  \\mathcal{D}^{(2)}f_i&=-\\frac{\\epsilon}{\\Delta x}\n *   (f_{i+1} - 2f_i+f_{i-1}) \\\\\n *  \\mathcal{D}^{(4)}f_i&=-\\frac{\\epsilon}{16\\Delta x}\n *   (f_{i+2}-4f_{i+1}+6f_i-4f_{i-1}+f_{i-2}) \\\\\n *  \\mathcal{D}^{(6)}f_i&=\\frac{\\epsilon}{64\\Delta x}(f_{i+3}-6f_{i+2}+15f_{i+1}\n *    -20f_i+15f_{i-1}-6f_{i-2}+f_{i-3}) \\\\\n *  \\mathcal{D}^{(8)}f_i&=-\\frac{\\epsilon}{256\\Delta x}\n *    (f_{i+4}-8f_{i+3}+28f_{i+2}-56f_{i+1}+70f_{i}-56f_{i-1}+28f_{i-2}\n *    -8f_{i-3}+f_{i-4}) \\\\\n *  \\mathcal{D}^{(10)}f_i&=\\frac{\\epsilon}{1024\\Delta x}\n *    (f_{i+5}-10f_{i+4}+45f_{i+3}\n *    -120f_{i+2}+210f_{i+1}-252f_{i}+210f_{i-1}-120f_{i-2}+45f_{i-3}\n *    -10f_{i-4}+f_{i-5})\n * \\f}\n *\n * Dimensions with Cartoon bases are not filtered as there is nothing to filter.\n *\n * \\note This function applies \\f$\\Delta x \\mathcal{D}^{(2m)}\\f$.\n */\ntemplate <size_t Dim>\nvoid kreiss_oliger_filter(\n    gsl::not_null<gsl::span<double>*> filtered_data,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, size_t number_of_variables, size_t fd_order,\n    double epsilon);\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/FiniteDifference.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup FiniteDifferenceGroup\n/// \\brief Functions and classes for finite difference methods.\nnamespace fd {}\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/HighOrderFluxCorrection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/OptionalHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace fd {\n/// @{\n/*!\n * \\brief Computes a high-order boundary correction $G$ at the FD interface.\n *\n * The correction to the second-order boundary correction is given by\n *\n * \\f{align*}{\n *  G=G^{(2)}-G^{(4)}+G^{(6)}-G^{(8)}+G^{(10)},\n * \\f}\n *\n * where\n *\n *\\f{align*}{\n * G^{(4)}_{j+1/2}&=\\frac{1}{6}\\left(G_j -2 G^{(2)} +\n *                         G_{j+1}\\right), \\\\\n * G^{(6)}_{j+1/2}&=\\frac{1}{180}\\left(G_{j-1} - 9 G_j + 16 G^{(2)}\n *                         -9 G_{j+1} + G_{j+2}\\right), \\\\\n * G^{(8)}_{j+1/2}&=\\frac{1}{2100}\\left(G_{j-2} - \\frac{25}{3} G_{j-1}\n *                         + 50 G_j - \\frac{256}{3} G^{(2)} + 50 G_{j+1}\n *                         - \\frac{25}{3} G_{j+2} +G_{j+3}\\right), \\\\\n * G^{(10)}_{j+1/2}&=\\frac{1}{17640}\n *                         \\left(G_{j-3} - \\frac{49}{5} G_{j-2}\n *                     + 49 G_{j-1} - 245 G_j + \\frac{2048}{5} G^{(2)}\\right.\n *                         \\nonumber \\\\\n *                       &\\left.- 245 G_{j+1}+ 49 G_{j+2} - \\frac{49}{5} G_{j+3}\n *                         + G_{j+4}\\right),\n * \\f}\n *\n * where\n *\n * \\f{align*}{\n *  G_{j} &= F^i_j n_i^{j+1/2}, \\\\\n *  G_{j\\pm1} &= F^i_{j\\pm1} n_i^{j+1/2}, \\\\\n *  G_{j\\pm2} &= F^i_{j\\pm2} n_i^{j+1/2}, \\\\\n *  G_{j\\pm3} &= F^i_{j\\pm3} n_i^{j+1/2}, \\\\\n *  G_{j\\pm4} &= F^i_{j\\pm4} n_i^{j+1/2}.\n * \\f}\n *\n * This is a generalization of the correction presented in \\cite CHEN2016604.\n *\n * This high-order flux can be fed into a flux limiter, e.g. to guarantee\n * positivity.\n *\n * \\note This implementation should be profiled and optimized.\n *\n * \\warning This documentation is for the general case. In the restricted\n * Cartesian case we use the cell-centered flux as opposed to `G^{(4)}`, which\n * differs by a minus sign. This amounts to a minus sign change in front of the\n * $G^{(k)}$ terms in computing $G$ for $k>2$, and also a sign change in front\n * of $G^{(2)}$ in all $G^{(k)}$ for $k>2$.\n */\ntemplate <DerivativeOrder DerivOrder, size_t Dim, typename... EvolvedVarsTags>\nvoid cartesian_high_order_fluxes_using_nodes(\n    const gsl::not_null<\n        std::array<Variables<tmpl::list<EvolvedVarsTags...>>, Dim>*>\n        high_order_boundary_corrections_in_logical_direction,\n\n    const std::array<Variables<tmpl::list<EvolvedVarsTags...>>, Dim>&\n        second_order_boundary_corrections_in_logical_direction,\n    const Variables<tmpl::list<\n        ::Tags::Flux<EvolvedVarsTags, tmpl::size_t<Dim>, Frame::Inertial>...>>&\n        cell_centered_inertial_flux,\n    const DirectionMap<\n        Dim, Variables<tmpl::list<::Tags::Flux<\n                 EvolvedVarsTags, tmpl::size_t<Dim>, Frame::Inertial>...>>>&\n        ghost_cell_inertial_flux,\n    const Mesh<Dim>& subcell_mesh, const size_t number_of_ghost_cells,\n    [[maybe_unused]] const std::array<gsl::span<std::uint8_t>, Dim>&\n        reconstruction_order = {}) {\n  using std::min;\n  constexpr int max_correction_order = 10;\n  static_assert(static_cast<int>(DerivOrder) <= max_correction_order);\n  constexpr size_t stencil_size = static_cast<int>(DerivOrder) < 0\n                                      ? 8\n                                      : (static_cast<size_t>(DerivOrder) - 2);\n  const size_t correction_width =\n      min(static_cast<size_t>(DerivOrder) / 2 - 1,\n          min(number_of_ghost_cells, stencil_size / 2));\n  ASSERT(correction_width <= number_of_ghost_cells,\n         \"The width of the derivative correction (\"\n             << correction_width\n             << \") must be less than or equal to the number of ghost cells \"\n             << number_of_ghost_cells);\n  ASSERT(alg::all_of(reconstruction_order,\n                     [](const auto& t) { return not t.empty(); }) or\n             static_cast<int>(DerivOrder) > 0,\n         \"For adaptive derivative orders the reconstruction_order must be set\");\n  for (size_t dim = 0; dim < Dim; ++dim) {\n    gsl::at(*high_order_boundary_corrections_in_logical_direction, dim)\n        .initialize(\n            gsl::at(second_order_boundary_corrections_in_logical_direction, dim)\n                .number_of_grid_points());\n  }\n\n  // Reconstruction order is always first-varying fastest since we don't\n  // transpose that back to {x,y,z} ordering.\n  Index<Dim> reconstruction_extents = subcell_mesh.extents();\n  reconstruction_extents[0] += 2;\n\n  const auto impl = [&cell_centered_inertial_flux, &ghost_cell_inertial_flux,\n                     &high_order_boundary_corrections_in_logical_direction,\n                     number_of_ghost_cells,\n                     &second_order_boundary_corrections_in_logical_direction,\n                     &subcell_mesh, &correction_width, &reconstruction_order,\n                     &reconstruction_extents](auto tag_v, auto dim_v) {\n    (void)reconstruction_extents;\n    using tag = decltype(tag_v);\n    constexpr size_t dim = decltype(dim_v)::value;\n\n    auto& high_order_var_correction =\n        get<tag>((*high_order_boundary_corrections_in_logical_direction)[dim]);\n    const auto& second_order_var_correction =\n        get<tag>(second_order_boundary_corrections_in_logical_direction[dim]);\n    const auto& recons_order = reconstruction_order[dim];\n    const auto& cell_centered_flux =\n        get<::Tags::Flux<tag, tmpl::size_t<Dim>, Frame::Inertial>>(\n            cell_centered_inertial_flux);\n    const auto& lower_neighbor_cell_centered_flux =\n        get<::Tags::Flux<tag, tmpl::size_t<Dim>, Frame::Inertial>>(\n            ghost_cell_inertial_flux.at(Direction<Dim>{dim, Side::Lower}));\n    const auto& upper_neighbor_cell_centered_flux =\n        get<::Tags::Flux<tag, tmpl::size_t<Dim>, Frame::Inertial>>(\n            ghost_cell_inertial_flux.at(Direction<Dim>{dim, Side::Upper}));\n    using FluxTensor = std::decay_t<decltype(cell_centered_flux)>;\n    const auto& subcell_extents = subcell_mesh.extents();\n    auto subcell_face_extents = subcell_extents;\n    ++subcell_face_extents[dim];\n    auto neighbor_extents = subcell_extents;\n    neighbor_extents[dim] = number_of_ghost_cells;\n    const size_t number_of_components = second_order_var_correction.size();\n    for (size_t storage_index = 0; storage_index < number_of_components;\n         ++storage_index) {\n      const auto flux_multi_index = prepend(\n          second_order_var_correction.get_tensor_index(storage_index), dim);\n      const size_t flux_storage_index =\n          FluxTensor::get_storage_index(flux_multi_index);\n      // Loop over each face\n      for (size_t k = 0; k < (Dim == 3 ? subcell_face_extents[2] : 1); ++k) {\n        for (size_t j = 0; j < (Dim >= 2 ? subcell_face_extents[1] : 1); ++j) {\n          for (size_t i = 0; i < subcell_face_extents[0]; ++i) {\n            const Index<Dim> face_index = [i, j, k]() -> Index<Dim> {\n              if constexpr (Dim == 3) {\n                return Index<Dim>{i, j, k};\n              } else if constexpr (Dim == 2) {\n                (void)k;\n                return Index<Dim>{i, j};\n              } else {\n                (void)k, (void)j;\n                return Index<Dim>{i};\n              }\n            }();\n            const size_t face_storage_index =\n                collapsed_index(face_index, subcell_face_extents);\n            Index<Dim> neighbor_index{};\n            for (size_t l = 0; l < Dim; ++l) {\n              if (l != dim) {\n                neighbor_index[l] = face_index[l];\n              }\n            }\n\n            double& correction =\n                high_order_var_correction[storage_index][face_storage_index] =\n                    0.0;\n\n            std::array<double, stencil_size> cell_centered_fluxes_for_stencil{};\n            // fill if we have to retrieve from lower neighbor\n            size_t stencil_index = 0;\n            for (int grid_index = static_cast<int>(face_index[dim]) -\n                                  static_cast<int>(correction_width);\n                 grid_index < static_cast<int>(face_index[dim]) +\n                                  static_cast<int>(correction_width);\n                 ++grid_index, ++stencil_index) {\n              if (grid_index < 0) {\n                neighbor_index[dim] = static_cast<size_t>(\n                    static_cast<int>(number_of_ghost_cells) + grid_index);\n                gsl::at(cell_centered_fluxes_for_stencil, stencil_index) =\n                    lower_neighbor_cell_centered_flux[flux_storage_index]\n                                                     [collapsed_index(\n                                                         neighbor_index,\n                                                         neighbor_extents)];\n              } else if (grid_index >= static_cast<int>(subcell_extents[dim])) {\n                neighbor_index[dim] = static_cast<size_t>(\n                    grid_index - static_cast<int>(subcell_extents[dim]));\n                gsl::at(cell_centered_fluxes_for_stencil, stencil_index) =\n                    upper_neighbor_cell_centered_flux[flux_storage_index]\n                                                     [collapsed_index(\n                                                         neighbor_index,\n                                                         neighbor_extents)];\n              } else {\n                Index<Dim> volume_index = face_index;\n                volume_index[dim] = static_cast<size_t>(grid_index);\n                gsl::at(cell_centered_fluxes_for_stencil, stencil_index) =\n                    cell_centered_flux[flux_storage_index][collapsed_index(\n                        volume_index, subcell_extents)];\n              }\n            }\n\n            size_t lower_neighbor_index = std::numeric_limits<size_t>::max();\n            size_t upper_neighbor_index = std::numeric_limits<size_t>::max();\n            if constexpr (static_cast<int>(DerivOrder) < 0) {\n              Index<Dim> lower_n{};\n              Index<Dim> upper_n{};\n              if constexpr (dim == 0) {\n                if constexpr (Dim == 1) {\n                  lower_n = Index<Dim>{i};\n                  upper_n = Index<Dim>{i + 1};\n                } else if constexpr (Dim == 2) {\n                  lower_n = Index<Dim>{i, j};\n                  upper_n = Index<Dim>{i + 1, j};\n                } else if constexpr (Dim == 3) {\n                  lower_n = Index<Dim>{i, j, k};\n                  upper_n = Index<Dim>{i + 1, j, k};\n                }\n              } else if constexpr (dim == 1) {\n                if constexpr (Dim == 2) {\n                  lower_n = Index<Dim>{j, i};\n                  upper_n = Index<Dim>{j + 1, i};\n                } else if constexpr (Dim == 3) {\n                  lower_n = Index<Dim>{j, k, i};\n                  upper_n = Index<Dim>{j + 1, k, i};\n                }\n              } else if constexpr (dim == 2) {\n                if constexpr (Dim == 3) {\n                  lower_n = Index<Dim>{k, i, j};\n                  upper_n = Index<Dim>{k + 1, i, j};\n                }\n              }\n              lower_neighbor_index =\n                  collapsed_index(lower_n, reconstruction_extents);\n              upper_neighbor_index =\n                  collapsed_index(upper_n, reconstruction_extents);\n            }\n\n            if (static_cast<int>(DerivOrder) >= 10 or\n                (static_cast<int>(DerivOrder) < 0 and\n                 min(recons_order[lower_neighbor_index],\n                     recons_order[upper_neighbor_index]) >= 9)) {\n              correction -=\n                  5.6689342403628117913e-5 *\n                  (gsl::at(cell_centered_fluxes_for_stencil,\n                           correction_width - 4) +\n                   gsl::at(cell_centered_fluxes_for_stencil,\n                           correction_width + 3) -\n                   9.8 * (gsl::at(cell_centered_fluxes_for_stencil,\n                                  correction_width - 3) +\n                          gsl::at(cell_centered_fluxes_for_stencil,\n                                  correction_width + 2)) +\n                   49.0 * (gsl::at(cell_centered_fluxes_for_stencil,\n                                   correction_width - 2) +\n                           gsl::at(cell_centered_fluxes_for_stencil,\n                                   correction_width + 1)) -\n                   245.0 * (gsl::at(cell_centered_fluxes_for_stencil,\n                                    correction_width - 1) +\n                            gsl::at(cell_centered_fluxes_for_stencil,\n                                    correction_width)) -\n                   409.6 * second_order_var_correction[storage_index]\n                                                      [face_storage_index]);\n            }\n            if (static_cast<int>(DerivOrder) >= 8 or\n                (static_cast<int>(DerivOrder) < 0 and\n                 min(recons_order[lower_neighbor_index],\n                     recons_order[upper_neighbor_index]) >= 7)) {\n              correction +=\n                  4.7619047619047619047e-4 *\n                  (gsl::at(cell_centered_fluxes_for_stencil,\n                           correction_width - 3) +\n                   gsl::at(cell_centered_fluxes_for_stencil,\n                           correction_width + 2) -\n                   8.3333333333333333333 *\n                       (gsl::at(cell_centered_fluxes_for_stencil,\n                                correction_width - 2) +\n                        gsl::at(cell_centered_fluxes_for_stencil,\n                                correction_width + 1)) +\n                   50.0 * (gsl::at(cell_centered_fluxes_for_stencil,\n                                   correction_width - 1) +\n                           gsl::at(cell_centered_fluxes_for_stencil,\n                                   correction_width)) +\n                   85.333333333333333333 *\n                       second_order_var_correction[storage_index]\n                                                  [face_storage_index]);\n            }\n            if (static_cast<int>(DerivOrder) >= 6 or\n                (static_cast<int>(DerivOrder) < 0 and\n                 min(recons_order[lower_neighbor_index],\n                     recons_order[upper_neighbor_index]) >=\n                     (DerivOrder ==\n                              DerivativeOrder::OneHigherThanReconsButFiveToFour\n                          ? 6\n                          : 5))) {\n              correction -=\n                  5.5555555555555555555e-3 *\n                  (gsl::at(cell_centered_fluxes_for_stencil,\n                           correction_width - 2) +\n                   gsl::at(cell_centered_fluxes_for_stencil,\n                           correction_width + 1) -\n                   9.0 * (gsl::at(cell_centered_fluxes_for_stencil,\n                                  correction_width - 1) +\n                          gsl::at(cell_centered_fluxes_for_stencil,\n                                  correction_width)) -\n                   16.0 * second_order_var_correction[storage_index]\n                                                     [face_storage_index]);\n            }\n            if (static_cast<int>(DerivOrder) >= 4 or\n                (static_cast<int>(DerivOrder) < 0 and\n                 min(recons_order[lower_neighbor_index],\n                     recons_order[upper_neighbor_index]) >= 3)) {\n              correction +=\n                  0.166666666666666666 *\n                  (gsl::at(cell_centered_fluxes_for_stencil,\n                           correction_width - 1) +\n                   gsl::at(cell_centered_fluxes_for_stencil, correction_width) +\n                   2.0 * second_order_var_correction[storage_index]\n                                                    [face_storage_index]);\n            }\n\n            // Add second-order correction last\n            correction +=\n                second_order_var_correction[storage_index][face_storage_index];\n          }\n        }\n      }\n    }\n  };\n\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      impl(EvolvedVarsTags{}, std::integral_constant<size_t, 0>{}));\n  if constexpr (Dim > 1) {\n    EXPAND_PACK_LEFT_TO_RIGHT(\n        impl(EvolvedVarsTags{}, std::integral_constant<size_t, 1>{}));\n    if constexpr (Dim > 2) {\n      EXPAND_PACK_LEFT_TO_RIGHT(\n          impl(EvolvedVarsTags{}, std::integral_constant<size_t, 2>{}));\n    }\n  }\n}\n\ntemplate <size_t Dim, typename... EvolvedVarsTags>\nvoid cartesian_high_order_fluxes_using_nodes(\n    const gsl::not_null<\n        std::array<Variables<tmpl::list<EvolvedVarsTags...>>, Dim>*>\n        high_order_boundary_corrections_in_logical_direction,\n\n    const std::array<Variables<tmpl::list<EvolvedVarsTags...>>, Dim>&\n        second_order_boundary_corrections_in_logical_direction,\n    const Variables<tmpl::list<\n        ::Tags::Flux<EvolvedVarsTags, tmpl::size_t<Dim>, Frame::Inertial>...>>&\n        cell_centered_inertial_flux,\n    const DirectionMap<\n        Dim, Variables<tmpl::list<::Tags::Flux<\n                 EvolvedVarsTags, tmpl::size_t<Dim>, Frame::Inertial>...>>>&\n        ghost_cell_inertial_flux,\n    const Mesh<Dim>& subcell_mesh, const size_t number_of_ghost_cells,\n    const DerivativeOrder derivative_order,\n    [[maybe_unused]] const std::array<gsl::span<std::uint8_t>, Dim>&\n        reconstruction_order = {}) {\n  switch (derivative_order) {\n    case DerivativeOrder::OneHigherThanRecons:\n      cartesian_high_order_fluxes_using_nodes<\n          DerivativeOrder::OneHigherThanRecons>(\n          high_order_boundary_corrections_in_logical_direction,\n          second_order_boundary_corrections_in_logical_direction,\n          cell_centered_inertial_flux, ghost_cell_inertial_flux, subcell_mesh,\n          number_of_ghost_cells, reconstruction_order);\n      break;\n    case DerivativeOrder::OneHigherThanReconsButFiveToFour:\n      cartesian_high_order_fluxes_using_nodes<\n          DerivativeOrder::OneHigherThanReconsButFiveToFour>(\n          high_order_boundary_corrections_in_logical_direction,\n          second_order_boundary_corrections_in_logical_direction,\n          cell_centered_inertial_flux, ghost_cell_inertial_flux, subcell_mesh,\n          number_of_ghost_cells, reconstruction_order);\n      break;\n    case DerivativeOrder::Two:\n      cartesian_high_order_fluxes_using_nodes<DerivativeOrder::Two>(\n          high_order_boundary_corrections_in_logical_direction,\n          second_order_boundary_corrections_in_logical_direction,\n          cell_centered_inertial_flux, ghost_cell_inertial_flux, subcell_mesh,\n          number_of_ghost_cells, reconstruction_order);\n      break;\n    case DerivativeOrder::Four:\n      cartesian_high_order_fluxes_using_nodes<DerivativeOrder::Four>(\n          high_order_boundary_corrections_in_logical_direction,\n          second_order_boundary_corrections_in_logical_direction,\n          cell_centered_inertial_flux, ghost_cell_inertial_flux, subcell_mesh,\n          number_of_ghost_cells, reconstruction_order);\n      break;\n    case DerivativeOrder::Six:\n      cartesian_high_order_fluxes_using_nodes<DerivativeOrder::Six>(\n          high_order_boundary_corrections_in_logical_direction,\n          second_order_boundary_corrections_in_logical_direction,\n          cell_centered_inertial_flux, ghost_cell_inertial_flux, subcell_mesh,\n          number_of_ghost_cells, reconstruction_order);\n      break;\n    case DerivativeOrder::Eight:\n      cartesian_high_order_fluxes_using_nodes<DerivativeOrder::Eight>(\n          high_order_boundary_corrections_in_logical_direction,\n          second_order_boundary_corrections_in_logical_direction,\n          cell_centered_inertial_flux, ghost_cell_inertial_flux, subcell_mesh,\n          number_of_ghost_cells, reconstruction_order);\n      break;\n    case DerivativeOrder::Ten:\n      cartesian_high_order_fluxes_using_nodes<DerivativeOrder::Ten>(\n          high_order_boundary_corrections_in_logical_direction,\n          second_order_boundary_corrections_in_logical_direction,\n          cell_centered_inertial_flux, ghost_cell_inertial_flux, subcell_mesh,\n          number_of_ghost_cells, reconstruction_order);\n      break;\n    default:\n      ERROR(\"Unsupported correction order \" << derivative_order);\n  };\n}\n/// @}\n\n/*!\n * \\brief Fill the `flux_neighbor_data` with pointers into the\n * `all_ghost_data`.\n *\n * The `all_ghost_data` is stored in the tag\n * `evolution::dg::subcell::Tags::GhostDataForReconstruction`, and the\n * `ghost_zone_size` should come from the FD reconstructor.\n */\ntemplate <size_t Dim, typename FluxesTags>\nvoid set_cartesian_neighbor_cell_centered_fluxes(\n    const gsl::not_null<DirectionMap<Dim, Variables<FluxesTags>>*>\n        flux_neighbor_data,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size,\n    const size_t number_of_rdmp_values_in_ghost_data) {\n  for (const auto& [direction_id, ghost_data] : all_ghost_data) {\n    const size_t neighbor_flux_size =\n        subcell_mesh.number_of_grid_points() /\n        subcell_mesh.extents(direction_id.direction().dimension()) *\n        ghost_zone_size *\n        Variables<FluxesTags>::number_of_independent_components;\n    const DataVector& neighbor_data =\n        ghost_data.neighbor_ghost_data_for_reconstruction();\n    (*flux_neighbor_data)[direction_id.direction()].set_data_ref(\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        const_cast<double*>(std::next(\n            neighbor_data.data(),\n            static_cast<std::ptrdiff_t>(neighbor_data.size() -\n                                        number_of_rdmp_values_in_ghost_data -\n                                        neighbor_flux_size))),\n        neighbor_flux_size);\n  }\n}\n\n/*!\n * \\brief Computes the high-order Cartesian flux corrections if necessary.\n *\n * The `cell_centered_fluxes` is stored in the tag\n * `evolution::dg::subcell::Tags::CellCenteredFlux`, `fd_derivative_order` is\n * from `evolution::dg::subcell::Tags::SubcellOptions`\n * (`.finite_difference_derivative_order()`), the `all_ghost_data`\n * is stored in the tag\n * `evolution::dg::subcell::Tags::GhostDataForReconstruction`, the\n * `ghost_zone_size` should come from the FD reconstructor.\n *\n * By default we assume no RDMP data is in the `ghost_data` buffer. In the\n * future we will want to update how we store the data in order to eliminate\n * more memory allocations and copies, in which case that value will be\n * non-zero.\n *\n * \\note `high_order_corrections` must either not have a value or have all\n * elements be of the same size as\n * `second_order_boundary_corrections[0].number_of_grid_points()`, where we've\n * assumed `second_order_boundary_corrections` is the same in all directions.\n */\ntemplate <size_t Dim, typename... EvolvedVarsTags,\n          typename FluxesTags = tmpl::list<::Tags::Flux<\n              EvolvedVarsTags, tmpl::size_t<Dim>, Frame::Inertial>...>>\nvoid cartesian_high_order_flux_corrections(\n    const gsl::not_null<std::optional<\n        std::array<Variables<tmpl::list<EvolvedVarsTags...>>, Dim>>*>\n        high_order_corrections,\n\n    const std::optional<Variables<FluxesTags>>& cell_centered_fluxes,\n    const std::array<Variables<tmpl::list<EvolvedVarsTags...>>, Dim>&\n        second_order_boundary_corrections,\n    const fd::DerivativeOrder& fd_derivative_order,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const Mesh<Dim>& subcell_mesh, const size_t ghost_zone_size,\n    [[maybe_unused]] const std::array<gsl::span<std::uint8_t>, Dim>&\n        reconstruction_order = {},\n    const size_t number_of_rdmp_values_in_ghost_data = 0) {\n  if (cell_centered_fluxes.has_value()) {\n    ASSERT(alg::all_of(\n               second_order_boundary_corrections,\n               [expected_size = second_order_boundary_corrections[0]\n                                    .number_of_grid_points()](const auto& e) {\n                 return e.number_of_grid_points() == expected_size;\n               }),\n           \"All second-order boundary corrections must be of the same size, \"\n               << second_order_boundary_corrections[0].number_of_grid_points());\n    if (fd_derivative_order != DerivativeOrder::Two) {\n      if (not high_order_corrections->has_value()) {\n        (*high_order_corrections) =\n            make_array<Dim>(Variables<tmpl::list<EvolvedVarsTags...>>{\n                second_order_boundary_corrections[0].number_of_grid_points()});\n      }\n      ASSERT(\n          high_order_corrections->has_value() and\n              alg::all_of(high_order_corrections->value(),\n                          [expected_size =\n                               second_order_boundary_corrections[0]\n                                   .number_of_grid_points()](const auto& e) {\n                            return e.number_of_grid_points() == expected_size;\n                          }),\n          \"The high_order_corrections must all have size \"\n              << second_order_boundary_corrections[0].number_of_grid_points());\n      DirectionMap<Dim, Variables<FluxesTags>> flux_neighbor_data{};\n      set_cartesian_neighbor_cell_centered_fluxes(\n          make_not_null(&flux_neighbor_data), all_ghost_data, subcell_mesh,\n          ghost_zone_size, number_of_rdmp_values_in_ghost_data);\n\n      cartesian_high_order_fluxes_using_nodes(\n          make_not_null(&(high_order_corrections->value())),\n          second_order_boundary_corrections, cell_centered_fluxes.value(),\n          flux_neighbor_data, subcell_mesh, ghost_zone_size,\n          fd_derivative_order, reconstruction_order);\n    }\n  }\n}\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Minmod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/Minmod.hpp\"\n\n#include <array>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n// NOLINTNEXTLINE(modernize-concat-nested-namespaces)\nnamespace fd::reconstruction {\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct<MinmodReconstructor>(                              \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_upper_side_of_face_vars,                               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_lower_side_of_face_vars,                               \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Index<DIM(data)>& volume_extents,                                  \\\n      const size_t number_of_variables);\n\nnamespace detail {\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n}  // namespace detail\n\n\n#undef INSTANTIATION\n\n#define SIDE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct_neighbor<SIDE(data), detail::MinmodReconstructor>( \\\n      gsl::not_null<DataVector*> face_data, const DataVector& volume_data,     \\\n      const DataVector& neighbor_data, const Index<DIM(data)>& volume_extents, \\\n      const Index<DIM(data)>& ghost_data_extents,                              \\\n      const Direction<DIM(data)>& direction_to_reconstruct);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (Side::Upper, Side::Lower))\n\n#undef INSTANTIATION\n#undef SIDE\n#undef DIM\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Minmod.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <utility>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace fd::reconstruction {\nnamespace detail {\nstruct MinmodReconstructor {\n  SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n      const double* const q, const int stride) {\n    using std::min;\n    using std::abs;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    const double a = q[stride] - q[0];\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    const double b = q[0] - q[-stride];\n    const double slope = 0.5 * (sgn(a) + sgn(b)) * min(abs(a), abs(b));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    return {{q[0] - 0.5 * slope, q[0] + 0.5 * slope}};\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() { return 3; }\n};\n}  // namespace detail\n\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Performs minmod reconstruction on the `volume_vars` in each direction.\n *\n * On a 1d mesh with spacing \\f$\\Delta x\\f$ we denote the solution at the\n * \\f$j\\f$th point by \\f$u_j\\f$. The minmod reconstruction \\cite RezzollaBook\n * first computes:\n *\n * \\f{align}\n * \\sigma_j\\equiv \\textrm{minmod}\\left(\\frac{u_j-u_{j-1}}{\\Delta x},\n *           \\frac{u_{j+1}-u_j}{\\Delta x}\\right)\n * \\f}\n *\n * where\n *\n * \\f{align}\n *  \\mathrm{minmod}(a,b)=\n * \\left\\{\n *   \\begin{array}{ll}\n *    \\mathrm{sgn}(a)\\min(\\lvert a\\rvert, \\lvert b\\rvert)\n *      & \\mathrm{if} \\; \\mathrm{sgn}(a)=\\mathrm{sgn}(b) \\\\\n *    0 & \\mathrm{otherwise}\n *   \\end{array}\\right.\n * \\f}\n *\n * The reconstructed solution \\f$u_j(x)\\f$ in the \\f$j\\f$th cell is given by\n *\n * \\f{align}\n * u_j(x)=u_j + \\sigma_j (x-x_j),\n * \\f}\n *\n * where \\f$x_j\\f$ is the coordinate \\f$x\\f$ at the center of the cell.\n */\ntemplate <size_t Dim>\nvoid minmod(const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n                reconstructed_upper_side_of_face_vars,\n            const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n                reconstructed_lower_side_of_face_vars,\n            const gsl::span<const double>& volume_vars,\n            const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n            const Index<Dim>& volume_extents,\n            const size_t number_of_variables) {\n  detail::reconstruct<detail::MinmodReconstructor>(\n      reconstructed_upper_side_of_face_vars,\n      reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n      volume_extents, number_of_variables);\n}\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/MonotonicityPreserving5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/MonotonicityPreserving5.hpp\"\n\n#include <array>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n// NOLINTNEXTLINE(modernize-concat-nested-namespaces)\nnamespace fd::reconstruction {\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct<MonotonicityPreserving5Reconstructor>(             \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_upper_side_of_face_vars,                               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_lower_side_of_face_vars,                               \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Index<DIM(data)>& volume_extents,                                  \\\n      const size_t number_of_variables, const double& alpha,                   \\\n      const double& epsilon);\n\nnamespace detail {\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n}  // namespace detail\n\n#undef INSTANTIATION\n\n#define SIDE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct_neighbor<                                          \\\n      SIDE(data), detail::MonotonicityPreserving5Reconstructor>(               \\\n      gsl::not_null<DataVector*> face_data, const DataVector& volume_data,     \\\n      const DataVector& neighbor_data, const Index<DIM(data)>& volume_extents, \\\n      const Index<DIM(data)>& ghost_data_extents,                              \\\n      const Direction<DIM(data)>& direction_to_reconstruct,                    \\\n      const double& alpha, const double& epsilon);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (Side::Upper, Side::Lower))\n\n#undef INSTANTIATION\n#undef SIDE\n#undef DIM\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/MonotonicityPreserving5.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Unlimited.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Direction;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace fd::reconstruction {\nnamespace detail {\nstruct MonotonicityPreserving5Reconstructor {\n  SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n      const double* const q, const int stride, const double alpha,\n      const double epsilon) {\n    using std::abs;\n    using std::max;\n    using std::min;\n\n    // define minmod function for 2 and 4 args\n    const auto minmod2 = [](const double x, const double y) {\n      return 0.5 * (sgn(x) + sgn(y)) * min(abs(x), abs(y));\n    };\n    const auto minmod4 = [](const double w, const double x, const double y,\n                            const double z) {\n      const double sign_w = sgn(w);\n      return 0.125 * (sign_w + sgn(x)) *\n             abs((sign_w + sgn(y)) * (sign_w + sgn(z))) *\n             min(abs(w), min(abs(x), min(abs(y), abs(z))));\n    };\n\n    // first, compute unlimited fifth-order finite difference reconstruction\n    // values\n    auto result = UnlimitedReconstructor<4>::pointwise(q, stride);\n\n    // compute q_{j+1/2}\n    const double q_mp_plus =\n        q[0] + minmod2(q[stride] - q[0], alpha * (q[0] - q[-stride]));\n    // compute q_{j-1/2}\n    const double q_mp_minus =\n        q[0] + minmod2(q[-stride] - q[0], alpha * (q[0] - q[stride]));\n\n    const bool limit_q_plus =\n        ((result[1] - q[0]) * (result[1] - q_mp_plus) > epsilon);\n    const bool limit_q_minus =\n        ((result[0] - q[0]) * (result[0] - q_mp_minus) > epsilon);\n\n    if (limit_q_plus or limit_q_minus) {\n      const double dp = q[2 * stride] + q[0] - 2.0 * q[stride];\n      const double dj = q[stride] + q[-stride] - 2.0 * q[0];\n      const double dm = q[0] + q[-2 * stride] - 2.0 * q[-stride];\n      const double dm4_plus = minmod4(4.0 * dj - dp, 4 * dp - dj, dj, dp);\n      const double dm4_minus = minmod4(4.0 * dj - dm, 4 * dm - dj, dj, dm);\n\n      if (limit_q_plus) {\n        const double q_ul = q[0] + alpha * (q[0] - q[-stride]);\n        const double q_md =\n            0.5 * (q[0] + q[stride] - dm4_plus);  // inline q^{AV}\n        const double q_lc =\n            q[0] + 0.5 * (q[0] - q[-stride]) + 1.3333333333333333 * dm4_minus;\n        const double q_min =\n            max(min(q[0], min(q[stride], q_md)), min(q[0], min(q_ul, q_lc)));\n        const double q_max =\n            min(max(q[0], max(q[stride], q_md)), max(q[0], max(q_ul, q_lc)));\n\n        result[1] = result[1] + minmod2(q_min - result[1], q_max - result[1]);\n      }\n\n      if (limit_q_minus) {\n        const double q_ul = q[0] + alpha * (q[0] - q[stride]);\n        const double q_md =\n            0.5 * (q[0] + q[-stride] - dm4_minus);  // inline q^{AV}\n        const double q_lc =\n            q[0] + 0.5 * (q[0] - q[stride]) + 1.3333333333333333 * dm4_plus;\n        const double q_min =\n            max(min(q[0], min(q[-stride], q_md)), min(q[0], min(q_ul, q_lc)));\n        const double q_max =\n            min(max(q[0], max(q[-stride], q_md)), max(q[0], max(q_ul, q_lc)));\n\n        result[0] = result[0] + minmod2(q_min - result[0], q_max - result[0]);\n      }\n    }\n\n    return result;\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() { return 5; }\n};\n}  // namespace detail\n\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Performs the fifth order monotonicity-preserving (MP5) reconstruction\n * \\cite Suresh1997.\n *\n * First, calculate the original interface value \\f$q_{j+1/2}^\\text{OR}\\f$ with\n * the (unlimited) fifth order finite difference reconstruction\n *\n * \\f{align}\n *  q_{j+1/2}^\\text{OR} = \\frac{1}{128}( 3 q_{j-2} - 20 q_{j-1} + 90 q_{j}\n *            + 60 q_{j+1} - 5 q_{j+2}) .\n * \\f}\n *\n * and compute\n *\n * \\f{align}\n *  q^\\text{MP} = q_j + \\text{minmod}(q_{j+1} - q_j, \\alpha(q_j - q_{j-1}))\n * \\f}\n *\n * for a given value of \\f$\\alpha\\f$, which is usually set to \\f$\\alpha=4\\f$.\n *\n * If \\f$ (q_{j+1/2}^\\text{OR} - q_j)(q_{j+1/2}^\\text{OR} - q^\\text{MP}) \\leq\n * \\epsilon\\f$ where \\f$\\epsilon\\f$ is a small tolerance value, use $q_{1/2}\n * = q_{j+1/2}^\\text{OR}$.\n *\n * \\note A proper value of \\f$\\epsilon\\f$ may depend on the scale of the\n * quantity \\f$q\\f$ to be reconstructed; reference \\cite Suresh1997 suggests\n * 1e-10. For hydro simulations with atmosphere treatment, setting\n * \\f$\\epsilon=0.0\\f$ would be safe.\n *\n * Otherwise, calculate\n *\n * \\f{align}\n *  d_{j+1} & = q_{j+2} - 2q_{j+1} + q_{j}       \\\\\n *  d_j     & = q_{j+1} - 2q_j + q_{j-1}         \\\\\n *  d_{j-1} & = q_{j} - 2q_{j-1} + q_{j-2} ,\n * \\f}\n *\n * \\f{align}\n *  d^\\text{M4}_{j+1/2} =\n *                \\text{minmod}(4d_j - d_{j+1}, 4d_{j+1}-d_j, d_j, d_{j+1}) \\\\\n *  d^\\text{M4}_{j-1/2} =\n *                \\text{minmod}(4d_j - d_{j-1}, 4d_{j-1}-d_j, d_j, d_{j-1}),\n * \\f}\n *\n * \\f{align}\n *  q^\\text{UL} & = q_j + \\alpha(q_j - q_{j-1})                             \\\\\n *  q^\\text{AV} & = (q_j + q_{j+1}) / 2                                     \\\\\n *  q^\\text{MD} & = q^\\text{AV} -  d^\\text{M4}_{j+1/2} / 2                  \\\\\n *  q^\\text{LC} & = q_j + (q_j - q_{j-1}) / 2 + (4/3) d^\\text{M4}_{j-1/2}\n * \\f}\n *\n * and\n * \\f{align}\n *  q^\\text{min} & = \\text{max}[ \\text{min}(q_j, q_{j+1}, q^\\text{MD}),\n *                               \\text{min}(q_j, q^\\text{UL}, q^\\text{LC}) ] \\\\\n *  q^\\text{max} & = \\text{min}[ \\text{max}(q_j, q_{j+1}, q^\\text{MD}),\n *                               \\text{max}(q_j, q^\\text{UL}, q^\\text{LC}) ].\n * \\f}\n *\n * The reconstructed value is given as\n * \\f{align}\n *  q_{i+1/2} = \\text{median}(q_{j+1/2}^\\text{OR},  q^\\text{min}, q^\\text{max})\n * \\f}\n * where the median function can be expressed as\n * \\f{align}\n *  \\text{median}(x,y,z) = x + \\text{minmod}(y-x, z-x).\n * \\f}\n *\n */\ntemplate <size_t Dim>\nvoid monotonicity_preserving_5(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const double alpha, const double epsilon) {\n  detail::reconstruct<detail::MonotonicityPreserving5Reconstructor>(\n      reconstructed_upper_side_of_face_vars,\n      reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n      volume_extents, number_of_variables, alpha, epsilon);\n}\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n\n#include <array>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n// NOLINTNEXTLINE(modernize-concat-nested-namespaces)\nnamespace fd::reconstruction {\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct<MonotonisedCentralReconstructor>(                  \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_upper_side_of_face_vars,                               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_lower_side_of_face_vars,                               \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Index<DIM(data)>& volume_extents,                                  \\\n      const size_t number_of_variables);\n\nnamespace detail {\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n}  // namespace detail\n\n#undef INSTANTIATION\n\n#define SIDE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define EXTERIOR_CELL(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void                                                                \\\n  reconstruct_neighbor<SIDE(data), detail::MonotonisedCentralReconstructor,    \\\n                       EXTERIOR_CELL(data)>(                                   \\\n      gsl::not_null<DataVector*> face_data, const DataVector& volume_data,     \\\n      const DataVector& neighbor_data, const Index<DIM(data)>& volume_extents, \\\n      const Index<DIM(data)>& ghost_data_extents,                              \\\n      const Direction<DIM(data)>& direction_to_reconstruct);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (Side::Upper, Side::Lower),\n                        (true, false))\n\n#undef INSTANTIATION\n#undef SIDE\n#undef DIM\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <utility>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace fd::reconstruction {\nnamespace detail {\nstruct MonotonisedCentralReconstructor {\n  SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n      const double* const q, const int stride) {\n    using std::abs;\n\n    const double a = q[stride] - q[0];\n    const double b = q[0] - q[-stride];\n\n    if (sgn(a) != sgn(b)) {\n      return {{q[0], q[0]}};\n    }\n    if (3.0 * abs(a) <= abs(b)) {\n      return {{q[0] - a, q[stride]}};\n    } else if (3.0 * abs(b) <= abs(a)) {\n      return {{q[-stride], q[0] + b}};\n    } else {\n      const double slope = 0.5 * (q[stride] - q[-stride]);\n      return {{q[0] - 0.5 * slope, q[0] + 0.5 * slope}};\n    }\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() { return 3; }\n};\n}  // namespace detail\n\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Performs monotonised central-difference reconstruction on the `vars`\n * in each direction.\n *\n * On a 1d mesh with spacing \\f$\\Delta x\\f$ we denote the solution at the\n * \\f$j\\f$th point by \\f$u_j\\f$. The monotonised central-difference\n * reconstruction \\cite RezzollaBook first computes:\n *\n * \\f{align}\n * \\sigma_j\\equiv \\textrm{minmod}\n *           \\left(\\frac{u_{j+1} - u_{j-1}}{2\\Delta x},\n *                 2\\frac{u_j-u_{j-1}}{\\Delta x},\n *                 2\\frac{u_{j+1}-u_j}{\\Delta x}\\right)\n * \\f}\n *\n * where\n *\n * \\f{align}\n *  \\mathrm{minmod}(a,b,c)=\n * \\left\\{\n *   \\begin{array}{ll}\n *    \\mathrm{sgn}(a)\\min(\\lvert a\\rvert, \\lvert b\\rvert, \\lvert c\\rvert)\n *      & \\mathrm{if} \\; \\mathrm{sgn}(a)=\\mathrm{sgn}(b)=\\mathrm{sgn}(c) \\\\\n *    0 & \\mathrm{otherwise}\n *   \\end{array}\\right.\n * \\f}\n *\n * The reconstructed solution \\f$u_j(x)\\f$ in the \\f$j\\f$th cell is given by\n *\n * \\f{align}\n * u_j(x)=u_j + \\sigma_j (x-x_j)\n * \\f}\n *\n * where \\f$x_j\\f$ is the coordinate \\f$x\\f$ at the center of the cell.\n */\ntemplate <size_t Dim>\nvoid monotonised_central(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables) {\n  detail::reconstruct<detail::MonotonisedCentralReconstructor>(\n      reconstructed_upper_side_of_face_vars,\n      reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n      volume_extents, number_of_variables);\n}\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace fd {\n/*!\n * \\brief Given the type-erased neighbor data for reconstruction stored in a\n * `DataVector`, have `Variables` point into them.\n *\n * This function is helpful for reconstruction, especially when wanting to apply\n * different reconstruction methods to different tags. This can happen, for\n * example, when doing positivity-preserving reconstruction. The density should\n * remain positive, but negative velocities are fine.\n */\ntemplate <size_t Dim, typename ReconstructionTags>\nvoid neighbor_data_as_variables(\n    const gsl::not_null<DirectionalIdMap<Dim, Variables<ReconstructionTags>>*>\n        vars_neighbor_data,\n    const DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>&\n        all_ghost_data,\n    const size_t ghost_zone_size, const Mesh<Dim>& subcell_mesh) {\n  const size_t neighbor_num_pts =\n      ghost_zone_size * subcell_mesh.extents().slice_away(0).product();\n  ASSERT(\n      subcell_mesh == Mesh<Dim>(subcell_mesh.extents(0), subcell_mesh.basis(0),\n                                subcell_mesh.quadrature(0)),\n      \"subcell_mesh must be isotropic but got \" << subcell_mesh);\n  vars_neighbor_data->clear();\n  for (const auto& [neighbor_id, ghost_data] : all_ghost_data) {\n    const DataVector& data =\n        ghost_data.neighbor_ghost_data_for_reconstruction();\n    (*vars_neighbor_data)[neighbor_id] = {};\n    (*vars_neighbor_data)[neighbor_id].set_data_ref(\n        const_cast<double*>(data.data()),\n        Variables<ReconstructionTags>::number_of_independent_components *\n            neighbor_num_pts);\n  }\n}\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/NonUniform1D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/NonUniform1D.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <deque>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace fd {\ntemplate <size_t StencilSize>\nstd::array<std::array<double, StencilSize>, StencilSize> non_uniform_1d_weights(\n    const std::deque<double>& times) {\n  static_assert(StencilSize >= 2 and StencilSize <= 4,\n                \"Finite difference for a 1D non-uniform stencil is only \"\n                \"implemented for stencil sizes 2, 3, and 4.\");\n\n  ASSERT(times.size() == StencilSize,\n         \"The size of the times passed in (\"\n             << times.size() << \") must be the same as the stencil size (\"\n             << StencilSize << \").\");\n  using ::operator<<;\n  ASSERT(std::is_sorted(times.begin(), times.end(), std::greater<double>()),\n         \"Times must be monotonically decreasing: \" << times);\n\n  // initialize the finite difference coefs\n  std::array<std::array<double, StencilSize>, StencilSize> coefs{};\n\n  // These coefficients are the weights of the Lagrange interpolation\n  // polynomial and its derivatives evaluated at `time[0]`\n  if constexpr (StencilSize == 2) {\n    const double one_over_delta_t = 1.0 / (times[0] - times[1]);\n\n    // set coefs for function value\n    coefs[0] = {{1.0, 0.0}};\n    // first deriv coefs\n    coefs[1] = {{one_over_delta_t, -one_over_delta_t}};\n  } else if constexpr (StencilSize == 3) {\n    const double t0_minus_t1 = times[0] - times[1];\n    const double t1_minus_t2 = times[1] - times[2];\n    const double t0_minus_t2 = times[0] - times[2];\n    const double denom = 1.0 / t0_minus_t2;\n    const double one_over_mult_dts = 1.0 / (t0_minus_t1 * t1_minus_t2);\n\n    // set coefs for function value\n    coefs[0] = {{1.0, 0.0, 0.0}};\n    // first deriv coefs\n    coefs[1] = {{(2.0 + t1_minus_t2 / t0_minus_t1) * denom,\n                 -t0_minus_t2 * one_over_mult_dts,\n                 t0_minus_t1 / t1_minus_t2 * denom}};\n    // second deriv coefs\n    coefs[2] = {{2.0 / t0_minus_t1 * denom, -2.0 * one_over_mult_dts,\n                 2.0 / t1_minus_t2 * denom}};\n  } else if constexpr (StencilSize == 4) {\n    const double t1_minus_t0 = times[1] - times[0];\n    const double t2_minus_t0 = times[2] - times[0];\n    const double t3_minus_t0 = times[3] - times[0];\n    const double t1_minus_t2 = times[1] - times[2];\n    const double t1_minus_t3 = times[1] - times[3];\n    const double t2_minus_t3 = times[2] - times[3];\n\n    // set coefs for function value\n    coefs[0] = {{1.0, 0.0, 0.0, 0.0}};\n    // first deriv coefs\n    coefs[1] = {\n        {-1.0 / t1_minus_t0 - 1.0 / t2_minus_t0 - 1.0 / t3_minus_t0,\n         t2_minus_t0 * t3_minus_t0 / (t1_minus_t0 * t1_minus_t2 * t1_minus_t3),\n         -t1_minus_t0 * t3_minus_t0 / (t2_minus_t0 * t1_minus_t2 * t2_minus_t3),\n         t1_minus_t0 * t2_minus_t0 /\n             (t3_minus_t0 * t1_minus_t3 * t2_minus_t3)}};\n    // second deriv coefs\n    coefs[2] = {{2.0 * (t1_minus_t0 + t2_minus_t0 + t3_minus_t0) /\n                     (t1_minus_t0 * t2_minus_t0 * t3_minus_t0),\n                 -2.0 * (t2_minus_t0 + t3_minus_t0) /\n                     (t1_minus_t0 * t1_minus_t2 * t1_minus_t3),\n                 2.0 * (t1_minus_t0 + t3_minus_t0) /\n                     (t2_minus_t0 * t1_minus_t2 * t2_minus_t3),\n                 -2.0 * (t1_minus_t0 + t2_minus_t0) /\n                     (t3_minus_t0 * t1_minus_t3 * t2_minus_t3)}};\n    // third deriv coefs\n    coefs[3] = {{-6.0 / (t1_minus_t0 * t2_minus_t0 * t3_minus_t0),\n                 6.0 / (t1_minus_t0 * t1_minus_t2 * t1_minus_t3),\n                 -6.0 / (t2_minus_t0 * t1_minus_t2 * t2_minus_t3),\n                 6.0 / (t3_minus_t0 * t1_minus_t3 * t2_minus_t3)}};\n  }\n\n  return coefs;\n}\n\n#define STENCIL(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(_, data)                                          \\\n  template std::array<std::array<double, STENCIL(data)>, STENCIL(data)> \\\n  non_uniform_1d_weights(const std::deque<double>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (2, 3, 4))\n\n#undef STENCIL\n#undef INSTANTIATION\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/NonUniform1D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <deque>\n\nnamespace fd {\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Returns the weights for a 1D non-uniform finite difference stencil.\n *\n * These weights are for the Lagrange interpolation polynomial and its\n * derivatives evaluated at `times[0]`. If the number of times is not the same\n * as `StencilSize`, then an error will occur.\n *\n * The reason that `times` was chosen to be monotonically decreasing is because\n * the intended use of this function is with data that is stored in a\n * `std::deque` where the zeroth element is the \"most recent\" in time and then\n * later elements are further in the \"past\".\n *\n * \\param times A monotonically *decreasing* sequence of times\n * \\return A 2D array of all finite difference weights that correspond to the\n * input times. The outer dimension is the Nth derivative, and the inner\n * dimension loops over the weights for each of the times.\n *\n * \\note Only stencil sizes 2, 3, and 4 are implemented right now. If you need\n * more, you'll either need to add it yourself or generalize the algorithm.\n */\ntemplate <size_t StencilSize>\nstd::array<std::array<double, StencilSize>, StencilSize> non_uniform_1d_weights(\n    const std::deque<double>& times);\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/PartialDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace fd {\nnamespace {\ntemplate <size_t Order, bool UnitStride>\nstruct ComputeImpl;\n\ntemplate <bool UnitStride>\nstruct ComputeImpl<2, UnitStride> {\n  static constexpr size_t fd_order = 2;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(\n      const double* const q, const int stride,\n      const std::array<double, 1>& weights) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return weights[0] * (q[1] - q[-1]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return weights[0] * (q[stride] - q[-stride]);\n    }\n  }\n\n  static constexpr std::array<double, 1> derivative_weights(\n      const double one_over_delta) {\n    return {{0.5 * one_over_delta}};\n  }\n};\n\ntemplate <bool UnitStride>\nstruct ComputeImpl<4, UnitStride> {\n  static constexpr size_t fd_order = 4;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(\n      const double* const q, const int stride,\n      const std::array<double, 2>& weights) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return weights[1] * (q[-2] - q[2]) + weights[0] * (q[1] - q[-1]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return weights[1] * (q[-2 * stride] - q[2 * stride]) +\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             weights[0] * (q[stride] - q[-stride]);\n    }\n  }\n\n  static constexpr std::array<double, 2> derivative_weights(\n      const double one_over_delta) {\n    return {{0.6666666666666666 * one_over_delta,\n             0.08333333333333333 * one_over_delta}};\n  }\n};\n\ntemplate <bool UnitStride>\nstruct ComputeImpl<6, UnitStride> {\n  static constexpr size_t fd_order = 6;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(\n      const double* const q, const int stride,\n      const std::array<double, 3>& weights) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return weights[2] * (q[3] - q[-3]) - weights[1] * (q[2] - q[-2]) +\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             weights[0] * (q[1] - q[-1]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return weights[2] * (q[3 * stride] - q[-3 * stride]) -\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             weights[1] * (q[2 * stride] - q[-2 * stride]) +\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             weights[0] * (q[stride] - q[-stride]);\n    }\n  }\n\n  static constexpr std::array<double, 3> derivative_weights(\n      const double one_over_delta) {\n    return {{0.75 * one_over_delta, 0.15 * one_over_delta,\n             0.016666666666666666 * one_over_delta}};\n  }\n};\n\ntemplate <bool UnitStride>\nstruct ComputeImpl<8, UnitStride> {\n  static constexpr size_t fd_order = 8;\n\n  SPECTRE_ALWAYS_INLINE static double pointwise(\n      const double* const q, const int stride,\n      const std::array<double, 4>& weights) {\n    if constexpr (UnitStride) {\n      ASSERT(stride == 1, \"UnitStride is true but got stride \" << stride);\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return weights[3] * (q[4] - q[-4]) + weights[2] * (q[3] - q[-3]) -\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             weights[1] * (q[2] - q[-2]) + weights[0] * (q[1] - q[-1]);\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      return weights[3] * (q[4 * stride] - q[-4 * stride]) +\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             weights[2] * (q[3 * stride] - q[-3 * stride]) -\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             weights[1] * (q[2 * stride] - q[-2 * stride]) +\n             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n             weights[0] * (q[stride] - q[-stride]);\n    }\n  }\n\n  static constexpr std::array<double, 4> derivative_weights(\n      const double one_over_delta) {\n    return {{0.8 * one_over_delta, 0.2 * one_over_delta,\n             0.0380952380952381 * one_over_delta,\n             -0.0035714285714285713 * one_over_delta}};\n  }\n};\n\ntemplate <typename DerivativeComputer, size_t Dim>\nvoid logical_partial_derivatives_fastest_dim(\n    const gsl::not_null<gsl::span<double>*> derivative,\n    const gsl::span<const double>& volume_vars,\n    const gsl::span<const double>& lower_ghost_data,\n    const gsl::span<const double>& upper_ghost_data,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const double delta) {\n  constexpr size_t fd_order = DerivativeComputer::fd_order;\n  ASSERT(\n      fd_order % 2 == 0,\n      \"The finite difference order with should be even but got \" << fd_order);\n  constexpr size_t stencil_width = fd_order + 1;\n  ASSERT(volume_extents[0] >= stencil_width - 1,\n         \" Subcell volume extent (current value: \"\n             << volume_extents[0]\n             << \") must be not smaller than the stencil width (current value: \"\n             << stencil_width << \") minus 1\");\n  constexpr size_t ghost_zone_for_stencil = fd_order / 2;\n  // Compute the number of ghost cells.\n  const size_t number_of_stripes =\n      volume_extents.slice_away(0).product() * number_of_variables;\n  ASSERT(lower_ghost_data.size() % number_of_stripes == 0,\n         \"The lower ghost data must be a multiple of the number of stripes (\"\n             << number_of_stripes\n             << \"), which is defined as the number of variables (\"\n             << number_of_variables\n             << \") times the number of grid points on a 2d slice (\"\n             << volume_extents.slice_away(0).product() << \")\");\n  ASSERT(upper_ghost_data.size() == lower_ghost_data.size(),\n         \"The lower ghost data size (\"\n             << lower_ghost_data.size()\n             << \") must match the upper ghost data size, \"\n             << upper_ghost_data.size());\n  const size_t ghost_pts_in_neighbor_data =\n      lower_ghost_data.size() / number_of_stripes;\n\n  // Precompute derivative weights to minimize FLOPs\n  const std::array<double, fd_order / 2> derivative_weights =\n      DerivativeComputer::derivative_weights(1.0 / delta);\n\n  std::array<double, stencil_width> q{};\n  for (size_t slice = 0; slice < number_of_stripes; ++slice) {\n    const size_t vars_slice_offset = slice * volume_extents[0];\n    const size_t vars_neighbor_slice_offset =\n        slice * ghost_pts_in_neighbor_data;\n\n    // Deal with lower ghost data.\n    for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n      // offset comes from accounting for the 1 extra point in our ghost\n      // cells plus how far away from the boundary we are differentiating.\n      for (size_t j = 0, offset = vars_neighbor_slice_offset +\n                                  ghost_pts_in_neighbor_data -\n                                  (ghost_zone_for_stencil - i);\n           j < ghost_zone_for_stencil - i; ++j) {\n        gsl::at(q, j) = lower_ghost_data[offset + j];\n      }\n      for (size_t j = ghost_zone_for_stencil - i, k = 0; j < stencil_width;\n           ++j, ++k) {\n        gsl::at(q, j) = volume_vars[vars_slice_offset + k];\n      }\n      (*derivative)[vars_slice_offset + i] = DerivativeComputer::pointwise(\n          q.data() + ghost_zone_for_stencil, 1, derivative_weights);\n    }\n\n    // Differentiate in the bulk\n    const size_t slice_end = volume_extents[0] - ghost_zone_for_stencil;\n    for (size_t vars_index = vars_slice_offset + ghost_zone_for_stencil,\n                i = ghost_zone_for_stencil;\n         i < slice_end; ++vars_index, ++i) {\n      // Note: we keep the `stride` here because we may want to\n      // experiment/support non-unit strides in the bulk in the future. For\n      // cells where the derivative needs boundary data we copy into a\n      // `std::array` buffer, which means we always have unit stride.\n      constexpr int stride = 1;\n      (*derivative)[vars_slice_offset + i] = DerivativeComputer::pointwise(\n          &volume_vars[vars_index], stride, derivative_weights);\n    }\n\n    // Differentiate using upper neighbor data\n    for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n      // offset comes from accounting for the 1 extra point in our ghost\n      // cells plus how far away from the boundary we are differentiating.\n      //\n      // Note:\n      // - q has size stencil_width\n      // - we need to copy over (stencil_width - 1 - i) from the volume\n      // - we need to copy (i + 1) from the neighbor\n      //\n      // Here is an example of a case with stencil_width 5:\n      //\n      //  Interior points| Neighbor points\n      // x x x x x x x x | o o o\n      //             ^\n      //         c c c c | c\n      //  c = points used for derivative\n\n      size_t j = 0;\n      for (size_t k =\n               vars_slice_offset + volume_extents[0] - (stencil_width - 1 - i);\n           j < stencil_width - 1 - i; ++j, ++k) {\n        gsl::at(q, j) = volume_vars[k];\n      }\n      for (size_t k = 0; j < stencil_width; ++j, ++k) {\n        gsl::at(q, j) = upper_ghost_data[vars_neighbor_slice_offset + k];\n      }\n\n      (*derivative)[vars_slice_offset + slice_end + i] =\n          DerivativeComputer::pointwise(q.data() + ghost_zone_for_stencil, 1,\n                                        derivative_weights);\n    }\n  }  // for slices\n}\n\ntemplate <typename DerivativeComputer, size_t Dim>\nvoid logical_partial_derivatives_impl(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        logical_derivatives,\n    gsl::span<double>* const in_buffer,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables) {\n#ifdef SPECTRE_DEBUG\n  ASSERT(volume_mesh == Mesh<Dim>(volume_mesh.extents(0), volume_mesh.basis(0),\n                                  volume_mesh.quadrature(0)),\n         \"The mesh must be isotropic, but got \" << volume_mesh);\n  ASSERT(\n      volume_mesh.basis(0) == Spectral::Basis::FiniteDifference,\n      \"Mesh basis must be FiniteDifference but got \" << volume_mesh.basis(0));\n  ASSERT(volume_mesh.quadrature(0) == Spectral::Quadrature::CellCentered,\n         \"Mesh quadrature must be CellCentered but got \"\n             << volume_mesh.quadrature(0));\n  const size_t number_of_points = volume_mesh.number_of_grid_points();\n  ASSERT(volume_vars.size() == number_of_points * number_of_variables,\n         \"The size of the volume vars must be the number of points (\"\n             << number_of_points << \") times the number of variables (\"\n             << number_of_variables << \") but is \" << volume_vars.size());\n  for (size_t i = 0; i < Dim; ++i) {\n    ASSERT(gsl::at(*logical_derivatives, i).size() == volume_vars.size(),\n           \"The logical derivatives must have size \"\n               << volume_vars.size() << \" but has size \"\n               << gsl::at(*logical_derivatives, i).size() << \" in dimension \"\n               << i);\n  }\n#endif  // SPECTRE_DEBUG\n\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_xi()),\n         \"Couldn't find lower ghost data in lower-xi\");\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_xi()),\n         \"Couldn't find upper ghost data in upper-xi\");\n  const Index<Dim>& volume_extents = volume_mesh.extents();\n\n  const auto& logical_xi_coords =\n      Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                   Spectral::Quadrature::CellCentered>(\n          volume_mesh.extents(0));\n  logical_partial_derivatives_fastest_dim<DerivativeComputer>(\n      make_not_null(&(*logical_derivatives)[0]), volume_vars,\n      ghost_cell_vars.at(Direction<Dim>::lower_xi()),\n      ghost_cell_vars.at(Direction<Dim>::upper_xi()), volume_extents,\n      number_of_variables, logical_xi_coords[1] - logical_xi_coords[0]);\n\n  if constexpr (Dim > 1) {\n    ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_eta()),\n           \"Couldn't find lower ghost data in lower-eta\");\n    ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_eta()),\n           \"Couldn't find upper ghost data in upper-eta\");\n\n    // We transpose from (x,y,z,vars) ordering to (y,z,vars,x) ordering\n    // Might not be the most efficient (unclear), but easiest.\n    // We use a single large buffer for both the y and z derivatives\n    // to reduce the number of memory allocations and improve data locality.\n    const auto& lower_ghost = ghost_cell_vars.at(Direction<Dim>::lower_eta());\n    const auto& upper_ghost = ghost_cell_vars.at(Direction<Dim>::upper_eta());\n    const size_t derivative_size = (*logical_derivatives)[1].size();\n    DataVector buffer{};\n    if (in_buffer != nullptr) {\n      ASSERT((in_buffer->size() >= volume_vars.size() + lower_ghost.size() +\n                                       upper_ghost.size() + derivative_size),\n             \"The buffer must have size greater than or equal to \"\n                 << (volume_vars.size() + lower_ghost.size() +\n                     upper_ghost.size() + derivative_size)\n                 << \" but has size \" << in_buffer->size());\n      buffer.set_data_ref(in_buffer->data(), in_buffer->size());\n    } else {\n      buffer = DataVector{volume_vars.size() + lower_ghost.size() +\n                          upper_ghost.size() + derivative_size};\n    }\n    raw_transpose(make_not_null(&buffer[0]), volume_vars.data(),\n                  volume_extents[0], volume_vars.size() / volume_extents[0]);\n    raw_transpose(make_not_null(&buffer[volume_vars.size()]),\n                  lower_ghost.data(), volume_extents[0],\n                  lower_ghost.size() / volume_extents[0]);\n    raw_transpose(\n        make_not_null(&buffer[volume_vars.size() + lower_ghost.size()]),\n        upper_ghost.data(), volume_extents[0],\n        upper_ghost.size() / volume_extents[0]);\n\n    // Note: assumes isotropic extents\n    const size_t derivative_offset_in_buffer =\n        volume_vars.size() + lower_ghost.size() + upper_ghost.size();\n    gsl::span<double> derivative_view =\n        gsl::make_span(&buffer[derivative_offset_in_buffer], derivative_size);\n\n    const auto& logical_eta_coords =\n        Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                     Spectral::Quadrature::CellCentered>(\n            volume_mesh.extents(1));\n\n    logical_partial_derivatives_fastest_dim<DerivativeComputer>(\n        make_not_null(&derivative_view),\n        gsl::make_span(&buffer[0], volume_vars.size()),\n        gsl::make_span(&buffer[volume_vars.size()], lower_ghost.size()),\n        gsl::make_span(&buffer[volume_vars.size() + lower_ghost.size()],\n                       upper_ghost.size()),\n        volume_extents, number_of_variables,\n        logical_eta_coords[1] - logical_eta_coords[0]);\n    // Transpose result back\n    raw_transpose(\n        make_not_null((*logical_derivatives)[1].data()), derivative_view.data(),\n        derivative_view.size() / volume_extents[0], volume_extents[0]);\n\n    if constexpr (Dim > 2) {\n      ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_zeta()),\n             \"Couldn't find lower ghost data in lower-zeta\");\n      ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_zeta()),\n             \"Couldn't find upper ghost data in upper-zeta\");\n\n      const size_t chunk_size = volume_extents[0] * volume_extents[1];\n      const size_t number_of_volume_chunks = volume_vars.size() / chunk_size;\n      const size_t number_of_neighbor_chunks =\n          ghost_cell_vars.at(Direction<Dim>::lower_zeta()).size() / chunk_size;\n\n      raw_transpose(make_not_null(buffer.data()), volume_vars.data(),\n                    chunk_size, number_of_volume_chunks);\n      raw_transpose(make_not_null(&buffer[volume_vars.size()]),\n                    ghost_cell_vars.at(Direction<Dim>::lower_zeta()).data(),\n                    chunk_size, number_of_neighbor_chunks);\n      raw_transpose(\n          make_not_null(&buffer[volume_vars.size() + lower_ghost.size()]),\n          ghost_cell_vars.at(Direction<Dim>::upper_zeta()).data(), chunk_size,\n          number_of_neighbor_chunks);\n\n      const auto& logical_zeta_coords =\n          Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                       Spectral::Quadrature::CellCentered>(\n              volume_mesh.extents(2));\n\n      logical_partial_derivatives_fastest_dim<DerivativeComputer>(\n          make_not_null(&derivative_view),\n          gsl::make_span(&buffer[0], volume_vars.size()),\n          gsl::make_span(&buffer[volume_vars.size()], lower_ghost.size()),\n          gsl::make_span(&buffer[volume_vars.size() + lower_ghost.size()],\n                         upper_ghost.size()),\n          volume_extents, number_of_variables,\n          logical_zeta_coords[1] - logical_zeta_coords[0]);\n      // Transpose result back\n      raw_transpose(make_not_null((*logical_derivatives)[2].data()),\n                    derivative_view.data(), derivative_view.size() / chunk_size,\n                    chunk_size);\n    }\n  }\n}\n}  // namespace\n\nnamespace detail {\ntemplate <size_t Dim>\nvoid logical_partial_derivatives_impl(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        logical_derivatives,\n    gsl::span<double>* const buffer, const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order) {\n  switch (fd_order) {\n    case 2:\n      ::fd::logical_partial_derivatives_impl<ComputeImpl<2, true>>(\n          logical_derivatives, buffer, volume_vars, ghost_cell_vars,\n          volume_mesh, number_of_variables);\n      break;\n    case 4:\n      ::fd::logical_partial_derivatives_impl<ComputeImpl<4, true>>(\n          logical_derivatives, buffer, volume_vars, ghost_cell_vars,\n          volume_mesh, number_of_variables);\n      break;\n    case 6:\n      ::fd::logical_partial_derivatives_impl<ComputeImpl<6, true>>(\n          logical_derivatives, buffer, volume_vars, ghost_cell_vars,\n          volume_mesh, number_of_variables);\n      break;\n    case 8:\n      ::fd::logical_partial_derivatives_impl<ComputeImpl<8, true>>(\n          logical_derivatives, buffer, volume_vars, ghost_cell_vars,\n          volume_mesh, number_of_variables);\n      break;\n    default:\n      ERROR(\"Cannot do finite difference derivative of order \" << fd_order);\n  };\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nvoid logical_partial_derivatives(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        logical_derivatives,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order) {\n  detail::logical_partial_derivatives_impl(\n      logical_derivatives, nullptr, volume_vars, ghost_cell_vars, volume_mesh,\n      number_of_variables, fd_order);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void detail::logical_partial_derivatives_impl(                      \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*> derivative,     \\\n      gsl::span<double>* const buffer,                                         \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Mesh<DIM(data)>& volume_fd_mesh, size_t number_of_variables,       \\\n      size_t fd_order);                                                        \\\n  template void logical_partial_derivatives(                                   \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*> derivative,     \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Mesh<DIM(data)>& volume_fd_mesh, size_t number_of_variables,       \\\n      size_t fd_order);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\nnamespace fd {\n/*!\n * \\brief Compute the logical partial derivatives using cell-centered finite\n * difference derivatives.\n *\n * Up to 8th order stencils are supported.\n *\n * \\note Currently the stride is always one because we transpose the data before\n * reconstruction. However, it may be faster to have a non-unit stride without\n * the transpose. We have the `stride` parameter in the derivative stencils\n * to make testing performance easier in the future.\n *\n * \\note This code does not do any explicit SIMD vectorization. We will want to\n * profile and decide if optimization are possible. The Vc SIMD library has an\n * example of vectorizing single-precision FD derivatives. There is also a paper\n * \"Optimization of Finite-Differencing Kernels for Numerical Relativity\n * Applications\" by Alfieri, Bernuzzi, Perego, and Radice that uses compiler\n * auto-vectorization.\n */\ntemplate <size_t Dim>\nvoid logical_partial_derivatives(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        logical_derivatives,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, size_t number_of_variables, size_t fd_order);\n\n/*!\n * \\brief Compute the partial derivative on the `DerivativeFrame` using the\n * `inverse_jacobian`.\n *\n * Logical partial derivatives are first computed using the\n * `fd::logical_partial_derivatives()` function.\n */\ntemplate <typename DerivativeTags, size_t Dim, typename DerivativeFrame>\nvoid partial_derivatives(\n    gsl::not_null<Variables<db::wrap_tags_in<\n        Tags::deriv, DerivativeTags, tmpl::size_t<Dim>, DerivativeFrame>>*>\n        partial_derivatives,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, size_t number_of_variables, size_t fd_order,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian);\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/PartialDerivatives.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace fd {\nnamespace detail {\ntemplate <size_t Dim>\nvoid logical_partial_derivatives_impl(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        logical_derivatives,\n    gsl::span<double>* buffer, const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, size_t number_of_variables, size_t fd_order);\n}  // namespace detail\n\ntemplate <typename DerivativeTags, size_t Dim, typename DerivativeFrame>\nvoid partial_derivatives(\n    const gsl::not_null<Variables<db::wrap_tags_in<\n        Tags::deriv, DerivativeTags, tmpl::size_t<Dim>, DerivativeFrame>>*>\n        partial_derivatives,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  ASSERT(partial_derivatives->size() == Dim * volume_vars.size(),\n         \"The partial derivatives Variables must have size \"\n             << Dim * volume_vars.size()\n             << \" (Dim * volume_vars.size()) but has size \"\n             << partial_derivatives->size() << \" and \"\n             << partial_derivatives->number_of_grid_points()\n             << \" grid points.\");\n  const size_t logical_derivs_internal_buffer_size =\n      Dim == 1\n          ? static_cast<size_t>(0)\n          : (volume_vars.size() +\n             2 * alg::max_element(ghost_cell_vars,\n                                  [](const auto& a, const auto& b) {\n                                    return a.second.size() < b.second.size();\n                                  })\n                     ->second.size() +\n             volume_vars.size());\n  DataVector buffer(Dim * volume_vars.size() +\n                    logical_derivs_internal_buffer_size);\n  std::array<gsl::span<double>, Dim> logical_partial_derivs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(logical_partial_derivs, i) =\n        gsl::make_span(&buffer[i * volume_vars.size()], volume_vars.size());\n  }\n  if constexpr (Dim > 1) {\n    gsl::span<double> span_buffer = gsl::make_span(\n        &buffer[Dim * volume_vars.size()], logical_derivs_internal_buffer_size);\n    detail::logical_partial_derivatives_impl(\n        make_not_null(&logical_partial_derivs), &span_buffer, volume_vars,\n        ghost_cell_vars, volume_mesh, number_of_variables, fd_order);\n  } else {\n    // No buffer in 1d\n    logical_partial_derivatives(make_not_null(&logical_partial_derivs),\n                                volume_vars, ghost_cell_vars, volume_mesh,\n                                number_of_variables, fd_order);\n  }\n\n  std::array<const double*, Dim> logical_partial_derivs_ptrs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(logical_partial_derivs_ptrs, i) =\n        gsl::at(logical_partial_derivs, i).data();\n  }\n  ::partial_derivatives_detail::partial_derivatives_impl(\n      partial_derivatives, logical_partial_derivs_ptrs,\n      Variables<DerivativeTags>::number_of_independent_components,\n      inverse_jacobian);\n}\n\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/PositivityPreservingAdaptiveOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Minmod.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace fd::reconstruction {\nnamespace {\n// use type alias for the function pointer return type for brevity\ntemplate <size_t Dim, bool ReturnReconstructionOrder>\nusing pointer_return_type = std::tuple<\n    detail::ppao_recons_type<Dim, ReturnReconstructionOrder>,\n    void (*)(gsl::not_null<DataVector*>, const DataVector&, const DataVector&,\n             const Index<Dim>&, const Index<Dim>&, const Direction<Dim>&,\n             const double&, const double&, const double&),\n    void (*)(gsl::not_null<DataVector*>, const DataVector&, const DataVector&,\n             const Index<Dim>&, const Index<Dim>&, const Direction<Dim>&,\n             const double&, const double&, const double&)>;\n\ntemplate <typename LowOrderReconstructor, bool PositivityPreserving,\n          bool Use9thOrder, bool Use7thOrder, size_t Dim,\n          bool ReturnReconstructionOrder>\npointer_return_type<Dim, ReturnReconstructionOrder> function_pointer() {\n  return {\n      static_cast<detail::ppao_recons_type<Dim, ReturnReconstructionOrder>>(\n          &positivity_preserving_adaptive_order<LowOrderReconstructor,\n                                                PositivityPreserving,\n                                                Use9thOrder, Use7thOrder, Dim>),\n      &::fd::reconstruction::reconstruct_neighbor<\n          Side::Lower,\n          ::fd::reconstruction::detail::\n              PositivityPreservingAdaptiveOrderReconstructor<\n                  LowOrderReconstructor, PositivityPreserving, Use9thOrder,\n                  Use7thOrder>,\n          true, Dim>,\n      &::fd::reconstruction::reconstruct_neighbor<\n          Side::Upper,\n          ::fd::reconstruction::detail::\n              PositivityPreservingAdaptiveOrderReconstructor<\n                  LowOrderReconstructor, PositivityPreserving, Use9thOrder,\n                  Use7thOrder>,\n          true, Dim>};\n}\n\ntemplate <bool PositivityPreserving, bool Use9thOrder, bool Use7thOrder,\n          size_t Dim, bool ReturnReconstructionOrder>\npointer_return_type<Dim, ReturnReconstructionOrder>\npositivity_preserving_adaptive_order_function_pointers_select_recons(\n    const FallbackReconstructorType fallback_recons) {\n  switch (fallback_recons) {\n    case FallbackReconstructorType::Minmod:\n      return function_pointer<detail::MinmodReconstructor, PositivityPreserving,\n                              Use9thOrder, Use7thOrder, Dim,\n                              ReturnReconstructionOrder>();\n    case FallbackReconstructorType::MonotonisedCentral:\n      return function_pointer<detail::MonotonisedCentralReconstructor,\n                              PositivityPreserving, Use9thOrder, Use7thOrder,\n                              Dim, ReturnReconstructionOrder>();\n    case FallbackReconstructorType::None:\n      ERROR(\n          \"Can't have None as the low-order reconstructor in \"\n          \"positivity_preserving_adaptive_order.\");\n    default:  // LCOV_EXCL_LINE\n              // LCOV_EXCL_START\n      ERROR(\"Unsupported type of fallback reconstruction : \" << fallback_recons\n                                                             << \"\\n\");\n      // LCOV_EXCL_STOP\n  }\n}\n\ntemplate <size_t Dim, bool ReturnReconstructionOrder, bool Use9thOrder,\n          bool Use7thOrder>\npointer_return_type<Dim, ReturnReconstructionOrder>\nppao_function_pointers_select_positivity(\n    const bool positivity_preserving,\n    const FallbackReconstructorType fallback_recons) {\n  if (positivity_preserving) {\n    return positivity_preserving_adaptive_order_function_pointers_select_recons<\n        true, Use9thOrder, Use7thOrder, Dim, ReturnReconstructionOrder>(\n        fallback_recons);\n  }\n  return positivity_preserving_adaptive_order_function_pointers_select_recons<\n      false, Use9thOrder, Use7thOrder, Dim, ReturnReconstructionOrder>(\n      fallback_recons);\n}\n}  // namespace\n\ntemplate <size_t Dim, bool ReturnReconstructionOrder>\npointer_return_type<Dim, ReturnReconstructionOrder>\npositivity_preserving_adaptive_order_function_pointers(\n    const bool positivity_preserving, const bool use_9th_order,\n    const bool use_7th_order, const FallbackReconstructorType fallback_recons) {\n  if (use_7th_order) {\n    if (use_9th_order) {\n      return ppao_function_pointers_select_positivity<\n          Dim, ReturnReconstructionOrder, true, true>(positivity_preserving,\n                                                      fallback_recons);\n    } else {\n      return ppao_function_pointers_select_positivity<\n          Dim, ReturnReconstructionOrder, false, true>(positivity_preserving,\n                                                       fallback_recons);\n    }\n  } else {\n    if (use_9th_order) {\n      return ppao_function_pointers_select_positivity<\n          Dim, ReturnReconstructionOrder, true, false>(positivity_preserving,\n                                                       fallback_recons);\n    } else {\n      return ppao_function_pointers_select_positivity<\n          Dim, ReturnReconstructionOrder, false, false>(positivity_preserving,\n                                                        fallback_recons);\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define RETURN_RECONS_ORDER(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                            \\\n  template pointer_return_type<DIM(data), RETURN_RECONS_ORDER(data)>      \\\n  positivity_preserving_adaptive_order_function_pointers<                 \\\n      DIM(data), RETURN_RECONS_ORDER(data)>(                              \\\n      bool positivity_preserving, bool use_9th_order, bool use_7th_order, \\\n      FallbackReconstructorType fallback_recons);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (true, false))\n\n#undef RETURN_RECONS_ORDER\n#undef INSTANTIATION\n\n#define POSITIVITY_PRESERVING(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define USE_9TH_ORDER(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define USE_7TH_ORDER(data) BOOST_PP_TUPLE_ELEM(3, data)\n#define FALLBACK_RECONSTRUCTOR(data) BOOST_PP_TUPLE_ELEM(4, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct<PositivityPreservingAdaptiveOrderReconstructor<    \\\n      FALLBACK_RECONSTRUCTOR(data), POSITIVITY_PRESERVING(data),               \\\n      USE_9TH_ORDER(data), USE_7TH_ORDER(data)>>(                              \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_upper_side_of_face_vars,                               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_lower_side_of_face_vars,                               \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Index<DIM(data)>& volume_extents,                                  \\\n      const size_t number_of_variables, const double& four_to_the_alpha_5,     \\\n      const double& six_to_the_alpha_7, const double& eight_to_the_alpha_9);   \\\n  template void reconstruct<PositivityPreservingAdaptiveOrderReconstructor<    \\\n      FALLBACK_RECONSTRUCTOR(data), POSITIVITY_PRESERVING(data),               \\\n      USE_9TH_ORDER(data), USE_7TH_ORDER(data)>>(                              \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_upper_side_of_face_vars,                               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_lower_side_of_face_vars,                               \\\n      gsl::not_null<                                                           \\\n          std::optional<std::array<gsl::span<std::uint8_t>, DIM(data)>>*>      \\\n          reconstruction_order,                                                \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Index<DIM(data)>& volume_extents,                                  \\\n      const size_t number_of_variables, const double& four_to_the_alpha_5,     \\\n      const double& six_to_the_alpha_7, const double& eight_to_the_alpha_9);\n\nnamespace detail {\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (true, false), (true, false),\n                        (true, false),\n                        (MinmodReconstructor, MonotonisedCentralReconstructor))\n}  // namespace detail\n\n#undef INSTANTIATION\n\n#define SIDE(data) BOOST_PP_TUPLE_ELEM(5, data)\n#define EXTERIOR_CELL(data) BOOST_PP_TUPLE_ELEM(6, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct_neighbor<                                          \\\n      SIDE(data),                                                              \\\n      detail::PositivityPreservingAdaptiveOrderReconstructor<                  \\\n          FALLBACK_RECONSTRUCTOR(data), POSITIVITY_PRESERVING(data),           \\\n          USE_9TH_ORDER(data), USE_7TH_ORDER(data)>,                           \\\n      EXTERIOR_CELL(data)>(                                                    \\\n      gsl::not_null<DataVector*> face_data, const DataVector& volume_data,     \\\n      const DataVector& neighbor_data, const Index<DIM(data)>& volume_extents, \\\n      const Index<DIM(data)>& ghost_data_extents,                              \\\n      const Direction<DIM(data)>& direction_to_reconstruct,                    \\\n      const double& four_to_the_alpha_5, const double& six_to_the_alpha_7,     \\\n      const double& eight_to_the_alpha_9);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (true, false), (true, false),\n                        (true, false),\n                        (detail::MinmodReconstructor,\n                         detail::MonotonisedCentralReconstructor),\n                        (Side::Upper, Side::Lower), (true, false))\n\n#undef INSTANTIATION\n#undef SIDE\n#undef NONLINEAR_WEIGHT_EXPONENT\n#undef FALLBACK_RECONSTRUCTOR\n#undef DIM\n\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Unlimited.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace fd::reconstruction {\nnamespace detail {\ntemplate <typename LowOrderReconstructor, bool PositivityPreserving,\n          bool Use9thOrder, bool Use7thOrder>\nstruct PositivityPreservingAdaptiveOrderReconstructor {\n  using ReturnType = std::tuple<double, double, std::uint8_t>;\n  SPECTRE_ALWAYS_INLINE static ReturnType pointwise(\n      const double* const u, const int stride, const double four_to_the_alpha_5,\n      // GCC9 complains that six_to_the_alpha_7 and eight_to_the_alpha_9\n      // are unused because if-constexpr\n      [[maybe_unused]] const double six_to_the_alpha_7,\n      [[maybe_unused]] const double eight_to_the_alpha_9) {\n    using std::get;\n    if constexpr (Use9thOrder) {\n      const auto unlimited_9 = UnlimitedReconstructor<8>::pointwise(u, stride);\n      const ReturnType order_9_result{get<0>(unlimited_9), get<1>(unlimited_9),\n                                      9};\n\n      if (not PositivityPreserving or LIKELY(get<0>(order_9_result) > 0.0 and\n                                             get<1>(order_9_result) > 0.0)) {\n        const double order_9_norm_of_top_modal_coefficient = square(\n            -1.593380762005595 * u[stride] +\n            0.7966903810027975 * u[2 * stride] -\n            0.22762582314365648 * u[3 * stride] +\n            0.02845322789295706 * u[4 * stride] -\n            1.593380762005595 * u[-stride] +\n            0.7966903810027975 * u[-2 * stride] -\n            0.22762582314365648 * u[-3 * stride] +\n            0.02845322789295706 * u[-4 * stride] + 1.991725952506994 * u[0]);\n\n        const double order_9_norm_of_polynomial =\n            u[stride] * (25.393963433621668 * u[stride] -\n                         31.738453392103736 * u[2 * stride] +\n                         14.315575523531798 * u[3 * stride] -\n                         5.422933317103013 * u[4 * stride] +\n                         45.309550145164756 * u[-stride] -\n                         25.682667845756164 * u[-2 * stride] +\n                         10.394184200706238 * u[-3 * stride] -\n                         3.5773996341558414 * u[-4 * stride] -\n                         56.63693768145594 * u[0]) +\n            u[2 * stride] * (10.664627625179254 * u[2 * stride] -\n                             9.781510753231265 * u[3 * stride] +\n                             3.783820939683476 * u[4 * stride] -\n                             25.682667845756164 * u[-stride] +\n                             13.59830711617153 * u[-2 * stride] -\n                             5.064486634342602 * u[-3 * stride] +\n                             1.5850428636128617 * u[-4 * stride] +\n                             33.99576779042882 * u[0]) +\n            u[3 * stride] * (2.5801312593878514 * u[3 * stride] -\n                             1.812843724346584 * u[4 * stride] +\n                             10.394184200706238 * u[-stride] -\n                             5.064486634342602 * u[-2 * stride] +\n                             1.6716163773782988 * u[-3 * stride] -\n                             0.4380794296257583 * u[-4 * stride] -\n                             14.626643302060115 * u[0]) +\n            u[4 * stride] * (0.5249097623867759 * u[4 * stride] -\n                             3.5773996341558414 * u[-stride] +\n                             1.5850428636128617 * u[-2 * stride] -\n                             0.4380794296257583 * u[-3 * stride] +\n                             0.07624062080823268 * u[-4 * stride] +\n                             5.336843456576288 * u[0]) +\n            u[-stride] * (25.393963433621668 * u[-stride] -\n                          31.738453392103736 * u[-2 * stride] +\n                          14.315575523531798 * u[-3 * stride] -\n                          5.422933317103013 * u[-4 * stride] -\n                          56.63693768145594 * u[0]) +\n            u[-2 * stride] * (10.664627625179254 * u[-2 * stride] -\n                              9.781510753231265 * u[-3 * stride] +\n                              3.783820939683476 * u[-4 * stride] +\n                              33.99576779042882 * u[0]) +\n            u[-3 * stride] * (2.5801312593878514 * u[-3 * stride] -\n                              1.812843724346584 * u[-4 * stride] -\n                              14.626643302060115 * u[0]) +\n            u[-4 * stride] * (0.5249097623867759 * u[-4 * stride] +\n                              5.336843456576288 * u[0]) +\n            33.758463458609164 * square(u[0]);\n        if (square(eight_to_the_alpha_9) *\n                order_9_norm_of_top_modal_coefficient <=\n            order_9_norm_of_polynomial) {\n          return order_9_result;\n        }\n      }\n    }\n\n    if constexpr (Use7thOrder) {\n      const auto unlimited_7 = UnlimitedReconstructor<6>::pointwise(u, stride);\n      const ReturnType order_7_result{get<0>(unlimited_7), get<1>(unlimited_7),\n                                      7};\n\n      if (not PositivityPreserving or LIKELY(get<0>(order_7_result) > 0.0 and\n                                             get<1>(order_7_result) > 0.0)) {\n        const double order_7_norm_of_top_modal_coefficient =\n            square(0.06936287633138594 * u[-3 * stride] -\n                   0.4161772579883155 * u[-2 * stride] +\n                   1.040443144970789 * u[-stride] -  //\n                   1.3872575266277185 * u[0] +       //\n                   1.040443144970789 * u[stride] -\n                   0.4161772579883155 * u[2 * stride] +  //\n                   0.06936287633138594 * u[3 * stride]);\n\n        const double order_7_norm_of_polynomial =\n            u[stride] * (3.93094886671763 * u[stride] -\n                         4.4887583031366605 * u[2 * stride] +\n                         2.126671427664419 * u[3 * stride] +\n                         6.081742742499426 * u[-stride] -\n                         3.1180508323787337 * u[-2 * stride] +\n                         1.2660604719155235 * u[-3 * stride] -\n                         8.108990323332568 * u[0]) +\n            u[2 * stride] * (1.7504056695205172 * u[2 * stride] -\n                             1.402086588589091 * u[3 * stride] -\n                             3.1180508323787337 * u[-stride] +\n                             1.384291080027286 * u[-2 * stride] -\n                             0.46498946172145633 * u[-3 * stride] +\n                             4.614303600090953 * u[0]) +\n            u[3 * stride] * (0.5786954880513824 * u[3 * stride] +\n                             1.2660604719155235 * u[-stride] -\n                             0.46498946172145633 * u[-2 * stride] +\n                             0.10352871936656591 * u[-3 * stride] -\n                             2.0705743873313183 * u[0]) +\n            u[-stride] * (3.93094886671763 * u[-stride] -\n                          4.4887583031366605 * u[-2 * stride] +\n                          2.126671427664419 * u[-3 * stride] -\n                          8.108990323332568 * u[0]) +\n            u[-2 * stride] * (1.7504056695205172 * u[-2 * stride] -\n                              1.402086588589091 * u[-3 * stride] +\n                              4.614303600090953 * u[0]) +\n            u[-3 * stride] * (0.5786954880513824 * u[-3 * stride] -\n                              2.0705743873313183 * u[0]) +\n            5.203166203165525 * square(u[0]);\n        if (square(six_to_the_alpha_7) *\n                order_7_norm_of_top_modal_coefficient <=\n            order_7_norm_of_polynomial) {\n          return order_7_result;\n        }\n      }\n    }\n    const auto unlimited_5 = UnlimitedReconstructor<4>::pointwise(u, stride);\n    const ReturnType order_5_result{get<0>(unlimited_5), get<1>(unlimited_5),\n                                    5};\n    if (not PositivityPreserving or\n        LIKELY(get<0>(order_5_result) > 0.0 and get<1>(order_5_result) > 0.0)) {\n      // The Persson sensor is 4^alpha L2(\\hat{u}) <= L2(u)\n      const double order_5_norm_of_top_modal_coefficient =\n          0.2222222222222222 * square(-1.4880952380952381 * u[stride] +\n                                      0.37202380952380953 * u[2 * stride] -\n                                      1.4880952380952381 * u[-stride] +\n                                      0.37202380952380953 * u[-2 * stride] +\n                                      2.232142857142857 * u[0]);\n\n      // Potential optimization: Simple approximation to the integral since we\n      // only really need about 1 digit of accuracy. This does eliminate aliases\n      // and so, while reducing the number of FLOPs, might not be accurate\n      // enough in the cases we're interested in.\n      //\n      // const double order_5_norm_of_polynomial =\n      //     1.1935763888888888 * square(u[-2 * stride]) +\n      //     0.4340277777777778 * square(u[-stride]) +\n      //     1.7447916666666667 * square(u[0]) +\n      //     0.4340277777777778 * square(u[stride]) +\n      //     1.1935763888888888 * square(u[2 * stride]);\n      const double order_5_norm_of_polynomial =\n          (u[stride] * (1.179711612654321 * u[stride] -\n                        0.963946414792769 * u[2 * stride] +\n                        1.0904086750440918 * u[-stride] -\n                        0.5030502507716049 * u[-2 * stride] -\n                        1.6356130125661377 * u[0]) +\n           u[2 * stride] *\n               (0.6699388830329586 * u[2 * stride] -\n                0.5030502507716049 * u[-stride] +\n                0.154568572944224 * u[-2 * stride] + 0.927411437665344 * u[0]) +\n           u[-stride] * (1.179711612654321 * u[-stride] -\n                         0.963946414792769 * u[-2 * stride] -\n                         1.6356130125661377 * u[0]) +\n           u[-2 * stride] * (0.6699388830329586 * u[-2 * stride] +\n                             0.927411437665344 * u[0]) +\n           1.4061182415674602 * square(u[0]));\n      if (square(four_to_the_alpha_5) * order_5_norm_of_top_modal_coefficient <=\n          order_5_norm_of_polynomial) {\n        return order_5_result;\n      }\n    }\n    // Drop to low-order reconstructor\n    const auto low_order = LowOrderReconstructor::pointwise(u, stride);\n    const ReturnType low_order_result{get<0>(low_order), get<1>(low_order), 2};\n    if (not PositivityPreserving or LIKELY(get<0>(low_order_result) > 0.0 and\n                                           get<1>(low_order_result) > 0.0)) {\n      return low_order_result;\n    }\n    // 1st-order reconstruction to guarantee positivity\n    return {u[0], u[0], 1};\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() {\n    return Use9thOrder ? 9 : (Use7thOrder ? 7 : 5);\n  }\n};\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Performs positivity-preserving adaptive-order FD reconstruction.\n *\n * Performs a fifth-order unlimited reconstruction. If the reconstructed\n * values at the interfaces aren't positive (when `PositivityPreserving` is\n * `true`) or when the Persson TCI condition:\n *\n * \\f{align}{\n *  4^\\alpha \\int_{x_{i-5/2}}^{x_{i+5/2}} \\hat{u}^2(x) dx\n *   > \\int_{x_{i-5/2}}^{x_{i+5/2}} u^2(x) dx\n * \\f}\n *\n * is satisfied, where \\f$\\hat{u}\\f$ is the polynomial with only the largest\n * modal coefficient non-zero, then the `LowOrderReconstructor` is used.\n *\n * If `PositivityPreserving` is `true` then if the low-order reconstructed\n * solution isn't positive we use first-order (constant in space)\n * reconstruction.\n *\n * If `Use9thOrder` is `true` then first a ninth-order reconstruction is used,\n * followed by fifth-order. If `Use7thOrder` is `true` then seventh-order\n * reconstruction is used before fifth-order (but after ninth-order if\n * `Use9thOrder` is also `true`). This allows using the highest possible order\n * locally for reconstruction.\n *\n * Fifth order unlimited reconstruction is:\n *\n * \\f{align}{\n *   \\hat{u}_{i+1/2}=\\frac{3}{128}u_{i-2} - \\frac{5}{32}u_{i-1}\n *     + \\frac{45}{64}u_{i} + \\frac{15}{32}u_{i+1} - \\frac{5}{128}u_{i+2}\n * \\f}\n *\n * Seventh order unlimited reconstruction is:\n *\n * \\f{align}{\n *    \\hat{u}_{i+1/2}&=-\\frac{5}{1024}u_{i-3} + \\frac{21}{512}u_{i-2}\n *         - \\frac{175}{1024}u_{i-1} + \\frac{175}{256}u_{i} \\\\\n *         &+ \\frac{525}{1024}u_{i+1} - \\frac{35}{512}u_{i+2}\n *         + \\frac{7}{1024}u_{i+3}\n * \\f}\n *\n * Ninth order unlimited reconstruction is:\n *\n * \\f{align}{\n *   \\hat{u}_{i+1/2}&=\\frac{35}{32768}u_{i-4} - \\frac{45}{4096}u_{i-3}\n *         + \\frac{441}{8291}u_{i-2} - \\frac{735}{4096}u_{i-1} \\\\\n *         &+ \\frac{11025}{16384}u_{i}\n *         + \\frac{2205}{4096}u_{i+1} - \\frac{735}{8192}u_{i+2}\n *         + \\frac{63}{4096}u_{i+3} \\\\\n *         &- \\frac{45}{32768}u_{i+4}\n * \\f}\n *\n */\ntemplate <typename LowOrderReconstructor, bool PositivityPreserving,\n          bool Use9thOrder, bool Use7thOrder, size_t Dim>\nvoid positivity_preserving_adaptive_order(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const double four_to_the_alpha_5, const double six_to_the_alpha_7,\n    const double eight_to_the_alpha_9) {\n  detail::reconstruct<detail::PositivityPreservingAdaptiveOrderReconstructor<\n      LowOrderReconstructor, PositivityPreserving, Use9thOrder, Use7thOrder>>(\n      reconstructed_upper_side_of_face_vars,\n      reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n      volume_extents, number_of_variables, four_to_the_alpha_5,\n      six_to_the_alpha_7, eight_to_the_alpha_9);\n}\n\ntemplate <typename LowOrderReconstructor, bool PositivityPreserving,\n          bool Use9thOrder, bool Use7thOrder, size_t Dim>\nvoid positivity_preserving_adaptive_order(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::not_null<\n        std::optional<std::array<gsl::span<std::uint8_t>, Dim>>*>\n        reconstruction_order,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const double four_to_the_alpha_5, const double six_to_the_alpha_7,\n    const double eight_to_the_alpha_9) {\n  detail::reconstruct<detail::PositivityPreservingAdaptiveOrderReconstructor<\n      LowOrderReconstructor, PositivityPreserving, Use9thOrder, Use7thOrder>>(\n      reconstructed_upper_side_of_face_vars,\n      reconstructed_lower_side_of_face_vars, reconstruction_order, volume_vars,\n      ghost_cell_vars, volume_extents, number_of_variables, four_to_the_alpha_5,\n      six_to_the_alpha_7, eight_to_the_alpha_9);\n}\n/// @}\n\nnamespace detail {\ntemplate <size_t Dim, bool ReturnReconstructionOrder>\nusing ppao_recons_type = tmpl::conditional_t<\n    ReturnReconstructionOrder,\n    void (*)(\n        gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n        gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n        gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, Dim>>*>,\n        const gsl::span<const double>&,\n        const DirectionMap<Dim, gsl::span<const double>>&, const Index<Dim>&,\n        size_t, double, double, double),\n    void (*)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n             gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n             const gsl::span<const double>&,\n             const DirectionMap<Dim, gsl::span<const double>>&,\n             const Index<Dim>&, size_t, double, double, double)>;\n}\n\n/*!\n * \\brief Returns function pointers to the\n * `positivity_preserving_adaptive_order` function, lower neighbor\n * reconstruction, and upper neighbor reconstruction.\n */\ntemplate <size_t Dim, bool ReturnReconstructionOrder>\nauto positivity_preserving_adaptive_order_function_pointers(\n    bool positivity_preserving, bool use_9th_order, bool use_7th_order,\n    FallbackReconstructorType fallback_recons)\n    -> std::tuple<detail::ppao_recons_type<Dim, ReturnReconstructionOrder>,\n                  void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                           const DataVector&, const Index<Dim>&,\n                           const Index<Dim>&, const Direction<Dim>&,\n                           const double&, const double&, const double&),\n                  void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                           const DataVector&, const Index<Dim>&,\n                           const Index<Dim>&, const Direction<Dim>&,\n                           const double&, const double&, const double&)>;\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Reconstruct.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace fd {\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Variable and flux vector splitting reconstruction schemes for finite\n * difference methods.\n *\n * Implementations of reconstruction methods must call the function\n * `fd::reconstruction::detail::reconstruct` to perform the reconstruction.\n * This function performs the actual loops taking into account strides and ghost\n * zones for the reconstruction method. The `reconstruct` function has the\n * following signature:\n *\n * \\code\n * template <typename Reconstructor, typename... ArgsForReconstructor, size_t\n *           Dim>\n * void reconstruct(\n *     const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n *         reconstructed_upper_side_of_face_vars,\n *     const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n *         reconstructed_lower_side_of_face_vars,\n *     const gsl::span<const double>& volume_vars,\n *     const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n *     const Index<Dim>& volume_extents, const size_t number_of_variables,\n *     const ArgsForReconstructor&... args_for_reconstructor);\n * \\endcode\n *\n * The type of reconstruction done is specified with the `Reconstructor`\n * explicit template parameter. Parameters for the reconstruction scheme, such\n * as the linear weights and the epsilon tolerance in a CWENO reconstruction\n * scheme,  are forwarded along using the `args_for_reconstruction` parameter\n * pack.\n *\n * `Reconstructor` classes must define:\n * - a `static constexpr size_t stencil_width()` function that\n *   returns the size of the stencil, e.g. 3 for minmod, and 5 for 5-point\n *   reconstruction.\n * - a\n *   \\code\n *      SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n *            const double* const u, const int stride)\n *   \\endcode\n *   function that optionally takes the additional arguments.\n *   The `u` are the cell-centered values to reconstruct. The value \\f$u_i\\f$ in\n *   the current FD cell is located at `u[0]`, the value at neighbor cell\n *   \\f$u_{i+1}\\f$ is at `u[stride]`, and the value at neighbor cell\n *   \\f$u_{i-1}\\f$ is at `u[-stride]`. The returned values are the\n *   reconstructed solution on the lower and upper side of the cell.\n *\n * \\note Currently the stride is always one because we transpose the data before\n * reconstruction. However, it may be faster to have a non-unit stride without\n * the transpose. We have the `stride` parameter in the reconstruction schemes\n * to make testing performance easier in the future.\n *\n * Here is an ASCII illustration of the names of various quantities and where in\n * the cells they are:\n *\n * \\code\n *   reconstructed_upper_side_of_face_vars v\n * reconstructed_lower_side_of_face_vars v\n *   volume_vars (at the cell-center) v\n *                               |    x   |\n *                                ^ reconstructed_upper_side_of_face_vars\n *                              ^ reconstructed_lower_side_of_face_vars\n * \\endcode\n *\n * Notice that the `reconstructed_upper_side_of_face_vars` are actually on the\n * lower side of the cells, while the `reconstructed_lower_side_of_face_vars`\n * are on the upper side of the cells. The `upper` and `lower` here refers to\n * which side of the interface the quantity is on, not from the perspective of\n * each cells.\n */\nnamespace reconstruction {\nnamespace detail {\ntemplate <typename Reconstructor, size_t Dim, typename... ArgsForReconstructor>\nvoid reconstruct(\n    gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, size_t number_of_variables,\n    const ArgsForReconstructor&... args_for_reconstructor);\n\n// Overload used to get the reconstruction order used in each cell.\ntemplate <typename Reconstructor, size_t Dim, typename... ArgsForReconstructor>\nvoid reconstruct(\n    gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    gsl::not_null<std::optional<std::array<gsl::span<std::uint8_t>, Dim>>*>\n        reconstruction_order,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, size_t number_of_variables,\n    const ArgsForReconstructor&... args_for_reconstructor);\n}  // namespace detail\n\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief In a given direction, reconstruct the cells in the neighboring Element\n * (or cluster of cells) nearest to the shared boundary between the current and\n * neighboring Element (or cluster of cells).\n *\n * This is needed if one is sending reconstruction and flux data separately, or\n * if one is using DG-FD hybrid schemes. Below is an ASCII diagram of what is\n * reconstructed.\n *\n * ```\n *  Self      |  Neighbor\n *  x x x x x | o o o\n *            ^+\n *            Reconstruct to right/+ side of the interface\n * ```\n */\ntemplate <Side LowerOrUpperSide, typename Reconstructor,\n          bool UseExteriorCell = true,\n          size_t NumberOfGhostPoints = (Reconstructor::stencil_width() / 2 + 1),\n          size_t Dim, typename... ArgsForReconstructor>\nvoid reconstruct_neighbor(\n    gsl::not_null<DataVector*> face_data, const DataVector& volume_data,\n    const DataVector& neighbor_data, const Index<Dim>& volume_extents,\n    const Index<Dim>& ghost_data_extents,\n    const Direction<Dim>& direction_to_reconstruct,\n    const ArgsForReconstructor&... args_for_reconstructor);\n}  // namespace reconstruction\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Reconstruct.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/StripeIterator.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace fd::reconstruction {\nnamespace detail {\ntemplate <size_t IndexToSet, size_t DimToReplace, size_t... Is,\n          size_t Dim = sizeof...(Is)>\nauto generate_index_for_u_to_reconstruct_impl(\n    const std::array<size_t, sizeof...(Is)>& indices,\n    std::index_sequence<Is...>) -> ::Index<Dim> {\n  return ::Index<Dim>{(DimToReplace != Is ? indices[Is] : IndexToSet)...};\n}\n\ntemplate <size_t IndexToSet, size_t DimToReplace, size_t NumberOfNeighborCells,\n          size_t... Is, size_t Dim = sizeof...(Is)>\nauto generate_upper_volume_index_for_u_to_reconstruct_impl(\n    const std::array<size_t, sizeof...(Is)>& indices,\n    const ::Index<Dim>& volume_extents, std::index_sequence<Is...>)\n    -> ::Index<Dim> {\n  return ::Index<Dim>{\n      (DimToReplace != Is\n           ? indices[Is]\n           : (volume_extents[Is] - (NumberOfNeighborCells - IndexToSet)))...};\n}\n\ntemplate <Side UpperLower, size_t DimToReplace, size_t Dim,\n          size_t GhostOffset = 0, size_t... VolumeIndices,\n          size_t... GhostIndices>\nauto u_to_reconstruct_impl(const DataVector& volume_data,\n                           const DataVector& neighbor_data,\n                           const std::array<size_t, Dim>& indices,\n                           const Index<Dim>& volume_extents,\n                           const Index<Dim>& ghost_data_extents,\n                           std::index_sequence<VolumeIndices...> /*unused*/,\n                           std::index_sequence<GhostIndices...> /*unused*/) {\n  if constexpr (UpperLower == Side::Lower) {\n    return std::array{\n        neighbor_data[collapsed_index(\n            generate_index_for_u_to_reconstruct_impl<GhostIndices + GhostOffset,\n                                                     DimToReplace>(\n                indices, std::make_index_sequence<Dim>{}),\n            ghost_data_extents)]...,\n        volume_data[collapsed_index(\n            generate_index_for_u_to_reconstruct_impl<VolumeIndices,\n                                                     DimToReplace>(\n                indices, std::make_index_sequence<Dim>{}),\n            volume_extents)]...};\n  } else {\n    return std::array{\n        volume_data[collapsed_index(\n            generate_upper_volume_index_for_u_to_reconstruct_impl<\n                VolumeIndices, DimToReplace, sizeof...(VolumeIndices)>(\n                indices, volume_extents, std::make_index_sequence<Dim>{}),\n            volume_extents)]...,\n        neighbor_data[collapsed_index(\n            generate_index_for_u_to_reconstruct_impl<GhostIndices,\n                                                     DimToReplace>(\n                indices, std::make_index_sequence<Dim>{}),\n            ghost_data_extents)]...};\n  }\n}\n\ntemplate <bool ReturnReconstructionOrder, typename Reconstructor, size_t Dim,\n          typename... ArgsForReconstructor>\nvoid reconstruct_impl(\n    const gsl::not_null<gsl::span<double>*> recons_upper,\n    const gsl::not_null<gsl::span<double>*> recons_lower,\n    [[maybe_unused]] const gsl::not_null<gsl::span<std::uint8_t>*>\n        reconstruction_order,\n    const gsl::span<const double>& volume_vars,\n    const gsl::span<const double>& lower_ghost_data,\n    const gsl::span<const double>& upper_ghost_data,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const ArgsForReconstructor&... args_for_reconstructor) {\n  using std::get;\n  using std::min;\n  constexpr size_t stencil_width = Reconstructor::stencil_width();\n  ASSERT(stencil_width % 2 == 1, \"The stencil with should be odd but got \"\n                                     << stencil_width\n                                     << \" for the reconstructor.\");\n  const size_t ghost_zone_for_stencil = (stencil_width - 1) / 2;\n  // Assume we send one extra ghost cell so we can reconstruct our neighbor's\n  // external data.\n  const size_t ghost_pts_in_neighbor_data = ghost_zone_for_stencil + 1;\n\n  const size_t number_of_stripes_per_variable =\n      volume_extents.slice_away(0).product();\n  const size_t number_of_stripes =\n      number_of_stripes_per_variable * number_of_variables;\n  [[maybe_unused]] size_t recons_order_slice_offset = 0;\n  if constexpr (ReturnReconstructionOrder) {\n    ASSERT(reconstruction_order->size() ==\n               (number_of_stripes_per_variable * (volume_extents[0] + 2)),\n           \"Expected size \"\n               << (number_of_stripes_per_variable * (volume_extents[0] + 2))\n               << \" for reconstruction_order but got \"\n               << reconstruction_order->size());\n  }\n\n  std::array<double, stencil_width> q{};\n  for (size_t slice = 0; slice < number_of_stripes; ++slice) {\n    const size_t vars_slice_offset = slice * volume_extents[0];\n    const size_t vars_neighbor_slice_offset =\n        slice * ghost_pts_in_neighbor_data;\n    const size_t recons_slice_offset = (volume_extents[0] + 1) * slice;\n    // We use volume_extents + 2 because we need the order of the left and\n    // right cells for adjusting the correction at the interface. This means\n    // we include one neighbor on the upper and lower side.\n    recons_order_slice_offset =\n        slice % number_of_stripes_per_variable == 0\n            ? 0\n            : (recons_order_slice_offset + volume_extents[0] + 2);\n    [[maybe_unused]] size_t recons_order_index = 0;\n    const auto set_recons_order = [&reconstruction_order, &recons_order_index,\n                                   recons_order_slice_offset](\n                                      const auto& upper_lower_and_order) {\n      if constexpr (ReturnReconstructionOrder and\n                    std::tuple_size<\n                        std::decay_t<decltype(upper_lower_and_order)>>::value >\n                        2) {\n        (*reconstruction_order)[recons_order_slice_offset +\n                                recons_order_index] =\n            min(static_cast<std::uint8_t>(get<2>(upper_lower_and_order)),\n                (*reconstruction_order)[recons_order_slice_offset +\n                                        recons_order_index]);\n        ++recons_order_index;\n      }\n    };\n\n    // Deal with lower ghost data.\n    //\n    // There's one extra reconstruction for the upper face of the neighbor\n    for (size_t j = 0; j < ghost_pts_in_neighbor_data; ++j) {\n      q[j] = lower_ghost_data[vars_neighbor_slice_offset + j];\n    }\n    for (size_t j = ghost_pts_in_neighbor_data, k = 0; j < stencil_width;\n         ++j, ++k) {\n      gsl::at(q, j) = volume_vars[vars_slice_offset + k];\n    }\n    {\n      const auto upper_lower_and_order = Reconstructor::pointwise(\n          q.data() + ghost_zone_for_stencil, 1, args_for_reconstructor...);\n      (*recons_lower)[recons_slice_offset] = get<1>(upper_lower_and_order);\n      set_recons_order(upper_lower_and_order);\n    }\n\n    for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n      // offset comes from accounting for the 1 extra point in our ghost\n      // cells plus how far away from the boundary we are reconstructing.\n      for (size_t j = 0, offset = vars_neighbor_slice_offset +\n                                  ghost_pts_in_neighbor_data -\n                                  (ghost_zone_for_stencil - i);\n           j < ghost_zone_for_stencil - i; ++j) {\n        q[j] = lower_ghost_data[offset + j];\n      }\n      for (size_t j = ghost_zone_for_stencil - i, k = 0; j < stencil_width;\n           ++j, ++k) {\n        gsl::at(q, j) = volume_vars[vars_slice_offset + k];\n      }\n      const auto upper_lower_and_order = Reconstructor::pointwise(\n          q.data() + ghost_zone_for_stencil, 1, args_for_reconstructor...);\n      (*recons_upper)[recons_slice_offset + i] = get<0>(upper_lower_and_order);\n      (*recons_lower)[recons_slice_offset + 1 + i] =\n          get<1>(upper_lower_and_order);\n      set_recons_order(upper_lower_and_order);\n    }\n\n    // Reconstruct in the bulk\n    const size_t slice_end = volume_extents[0] - ghost_zone_for_stencil;\n    for (size_t vars_index = vars_slice_offset + ghost_zone_for_stencil,\n                i = ghost_zone_for_stencil;\n         i < slice_end; ++vars_index, ++i) {\n      // Note: we keep the `stride` here because we may want to\n      // experiment/support non-unit strides in the bulk in the future. For\n      // cells where the reconstruction needs boundary data we copy into a\n      // `std::array` buffer, which means we always have unit stride.\n      constexpr int stride = 1;\n      const auto upper_lower_and_order = Reconstructor::pointwise(\n          &volume_vars[vars_index], stride, args_for_reconstructor...);\n      (*recons_upper)[recons_slice_offset + i] = get<0>(upper_lower_and_order);\n      (*recons_lower)[recons_slice_offset + 1 + i] =\n          get<1>(upper_lower_and_order);\n      set_recons_order(upper_lower_and_order);\n    }\n\n    // Reconstruct using upper neighbor data\n    for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n      // offset comes from accounting for the 1 extra point in our ghost\n      // cells plus how far away from the boundary we are reconstructing.\n      //\n      // Note:\n      // - q has size stencil_width\n      // - we need to copy over (stencil_width - 1 - i) from the volume\n      // - we need to copy (i + 1) from the neighbor\n      //\n      // Here is an example of a case with stencil_width 5:\n      //\n      //  Interior points| Neighbor points\n      // x x x x x x x x | o o o\n      //             ^\n      //         c c c c | c\n      //  c = points used for reconstruction\n\n      ASSERT(\n          volume_extents[0] >= stencil_width - 1,\n          \" Subcell volume extent (current value: \"\n              << volume_extents[0]\n              << \") must be not smaller than the stencil width (current value: \"\n              << stencil_width << \") minus 1\");\n\n      size_t j = 0;\n      for (size_t k =\n               vars_slice_offset + volume_extents[0] - (stencil_width - 1 - i);\n           j < stencil_width - 1 - i; ++j, ++k) {\n        gsl::at(q, j) = volume_vars[k];\n      }\n      for (size_t k = 0; j < stencil_width; ++j, ++k) {\n        gsl::at(q, j) = upper_ghost_data[vars_neighbor_slice_offset + k];\n      }\n\n      const auto upper_lower_and_order = Reconstructor::pointwise(\n          q.data() + ghost_zone_for_stencil, 1, args_for_reconstructor...);\n      (*recons_upper)[recons_slice_offset + slice_end + i] =\n          get<0>(upper_lower_and_order);\n      (*recons_lower)[recons_slice_offset + slice_end + i + 1] =\n          get<1>(upper_lower_and_order);\n      set_recons_order(upper_lower_and_order);\n    }\n\n    // Reconstruct the upper side of the last face, this is what the\n    // neighbor would've reconstructed.\n    for (size_t j = 0; j < ghost_zone_for_stencil; ++j) {\n      gsl::at(q, j) = volume_vars[vars_slice_offset + volume_extents[0] -\n                                  ghost_zone_for_stencil + j];\n    }\n    for (size_t j = ghost_zone_for_stencil, k = 0; j < stencil_width;\n         ++j, ++k) {\n      gsl::at(q, j) = upper_ghost_data[vars_neighbor_slice_offset + k];\n    }\n    const auto upper_lower_and_order = Reconstructor::pointwise(\n        q.data() + ghost_zone_for_stencil, 1, args_for_reconstructor...);\n    (*recons_upper)[recons_slice_offset + volume_extents[0]] =\n        get<0>(upper_lower_and_order);\n    set_recons_order(upper_lower_and_order);\n\n  }  // for slices\n}\n\ntemplate <bool ReturnReconstructionOrder, typename Reconstructor, size_t Dim,\n          typename... ArgsForReconstructor>\nvoid reconstruct_impl(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::not_null<\n        std::optional<std::array<gsl::span<std::uint8_t>, Dim>>*>\n        reconstruction_order,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const ArgsForReconstructor&... args_for_reconstructor) {\n  ASSERT(not ReturnReconstructionOrder or reconstruction_order->has_value(),\n         \"If ReturnReconstructionOrder is true then reconstruction_order must \"\n         \"have a value\");\n#ifdef SPECTRE_DEBUG\n  ASSERT(volume_extents == Index<Dim>(volume_extents[0]),\n         \"The extents must be isotropic, but got \" << volume_extents);\n  const size_t number_of_points = volume_extents.product();\n  for (size_t i = 0; i < Dim; ++i) {\n    const size_t expected_pts =\n        number_of_points / volume_extents[i] * (volume_extents[i] + 1);\n    const size_t upper_num_pts =\n        gsl::at(*reconstructed_upper_side_of_face_vars, i).size() /\n        number_of_variables;\n    ASSERT(upper_num_pts == expected_pts,\n           \"Incorrect number of points for \"\n           \"reconstructed_upper_side_of_face_vars in direction \"\n               << i << \". Has \" << upper_num_pts << \" Expected \"\n               << expected_pts);\n    const size_t lower_num_pts =\n        gsl::at(*reconstructed_lower_side_of_face_vars, i).size() /\n        number_of_variables;\n    ASSERT(lower_num_pts == expected_pts,\n           \"Incorrect number of points for \"\n           \"reconstructed_lower_side_of_face_vars in direction \"\n               << i << \". Has \" << lower_num_pts << \" Expected \"\n               << expected_pts);\n  }\n#endif  // SPECTRE_DEBUG\n\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_xi()),\n         \"Couldn't find lower ghost data in lower-xi\");\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_xi()),\n         \"Couldn't find upper ghost data in upper-xi\");\n  // Because std::optional.value_or returns by value we want to ensure that we\n  // don't copy any data.\n  gsl::span<std::uint8_t> empty_span{};\n  reconstruct_impl<ReturnReconstructionOrder, Reconstructor>(\n      make_not_null(&(*reconstructed_upper_side_of_face_vars)[0]),\n      make_not_null(&(*reconstructed_lower_side_of_face_vars)[0]),\n      make_not_null(&(ReturnReconstructionOrder\n                          ? reconstruction_order->value()[0]\n                          : empty_span)),\n      volume_vars, ghost_cell_vars.at(Direction<Dim>::lower_xi()),\n      ghost_cell_vars.at(Direction<Dim>::upper_xi()), volume_extents,\n      number_of_variables, args_for_reconstructor...);\n\n  if constexpr (Dim > 1) {\n    // We transpose from (x,y,z,vars) ordering to (y,z,vars,x) ordering\n    // Might not be the most efficient (unclear), but easiest.\n    // We use a single large buffer for both the y and z reconstruction\n    // to reduce the number of memory allocations and improve data locality.\n    const auto& lower_ghost = ghost_cell_vars.at(Direction<Dim>::lower_eta());\n    const auto& upper_ghost = ghost_cell_vars.at(Direction<Dim>::upper_eta());\n    DataVector buffer(volume_vars.size() + lower_ghost.size() +\n                      upper_ghost.size() +\n                      2 * (*reconstructed_upper_side_of_face_vars)[1].size());\n    raw_transpose(make_not_null(buffer.data()), volume_vars.data(),\n                  volume_extents[0], volume_vars.size() / volume_extents[0]);\n    raw_transpose(make_not_null(buffer.data() + volume_vars.size()),\n                  lower_ghost.data(), volume_extents[0],\n                  lower_ghost.size() / volume_extents[0]);\n    raw_transpose(\n        make_not_null(buffer.data() + volume_vars.size() + lower_ghost.size()),\n        upper_ghost.data(), volume_extents[0],\n        upper_ghost.size() / volume_extents[0]);\n\n    // Note: assumes isotropic extents\n    const size_t recons_offset_in_buffer =\n        volume_vars.size() + lower_ghost.size() + upper_ghost.size();\n    const size_t recons_size =\n        (*reconstructed_upper_side_of_face_vars)[1].size();\n    gsl::span<double> recons_upper_view =\n        gsl::make_span(buffer.data() + recons_offset_in_buffer, recons_size);\n    gsl::span<double> recons_lower_view = gsl::make_span(\n        buffer.data() + recons_offset_in_buffer + recons_size, recons_size);\n    reconstruct_impl<ReturnReconstructionOrder, Reconstructor>(\n        make_not_null(&recons_upper_view), make_not_null(&recons_lower_view),\n        make_not_null(&(ReturnReconstructionOrder\n                            ? reconstruction_order->value()[1]\n                            : empty_span)),\n        gsl::make_span(&buffer[0], volume_vars.size()),\n        gsl::make_span(buffer.data() + volume_vars.size(), lower_ghost.size()),\n        gsl::make_span(buffer.data() + volume_vars.size() + lower_ghost.size(),\n                       upper_ghost.size()),\n        volume_extents, number_of_variables, args_for_reconstructor...);\n    // Transpose result back\n    raw_transpose(\n        make_not_null((*reconstructed_upper_side_of_face_vars)[1].data()),\n        recons_upper_view.data(), recons_upper_view.size() / volume_extents[0],\n        volume_extents[0]);\n    raw_transpose(\n        make_not_null((*reconstructed_lower_side_of_face_vars)[1].data()),\n        recons_lower_view.data(), recons_lower_view.size() / volume_extents[0],\n        volume_extents[0]);\n\n    if constexpr (Dim > 2) {\n      const size_t chunk_size = volume_extents[0] * volume_extents[1];\n      const size_t number_of_volume_chunks = volume_vars.size() / chunk_size;\n      const size_t number_of_neighbor_chunks =\n          ghost_cell_vars.at(Direction<Dim>::lower_zeta()).size() / chunk_size;\n\n      raw_transpose(make_not_null(buffer.data()), volume_vars.data(),\n                    chunk_size, number_of_volume_chunks);\n      raw_transpose(make_not_null(buffer.data() + volume_vars.size()),\n                    ghost_cell_vars.at(Direction<Dim>::lower_zeta()).data(),\n                    chunk_size, number_of_neighbor_chunks);\n      raw_transpose(make_not_null(buffer.data() + volume_vars.size() +\n                                  lower_ghost.size()),\n                    ghost_cell_vars.at(Direction<Dim>::upper_zeta()).data(),\n                    chunk_size, number_of_neighbor_chunks);\n\n      reconstruct_impl<ReturnReconstructionOrder, Reconstructor>(\n          make_not_null(&recons_upper_view), make_not_null(&recons_lower_view),\n          make_not_null(&(ReturnReconstructionOrder\n                              ? reconstruction_order->value()[2]\n                              : empty_span)),\n          gsl::make_span(&buffer[0], volume_vars.size()),\n          gsl::make_span(buffer.data() + volume_vars.size(),\n                         lower_ghost.size()),\n          gsl::make_span(\n              buffer.data() + volume_vars.size() + lower_ghost.size(),\n              upper_ghost.size()),\n          volume_extents, number_of_variables, args_for_reconstructor...);\n      // Transpose result back\n      raw_transpose(\n          make_not_null((*reconstructed_upper_side_of_face_vars)[2].data()),\n          recons_upper_view.data(), recons_upper_view.size() / chunk_size,\n          chunk_size);\n      raw_transpose(\n          make_not_null((*reconstructed_lower_side_of_face_vars)[2].data()),\n          recons_lower_view.data(), recons_lower_view.size() / chunk_size,\n          chunk_size);\n    }\n  }\n}\n\ntemplate <typename Reconstructor, size_t Dim, typename... ArgsForReconstructor>\nvoid reconstruct(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::not_null<\n        std::optional<std::array<gsl::span<std::uint8_t>, Dim>>*>\n        reconstruction_order,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const ArgsForReconstructor&... args_for_reconstructor) {\n  if (reconstruction_order->has_value()) {\n    reconstruct_impl<true, Reconstructor>(\n        reconstructed_upper_side_of_face_vars,\n        reconstructed_lower_side_of_face_vars, reconstruction_order,\n        volume_vars, ghost_cell_vars, volume_extents, number_of_variables,\n        args_for_reconstructor...);\n  } else {\n    reconstruct_impl<false, Reconstructor>(\n        reconstructed_upper_side_of_face_vars,\n        reconstructed_lower_side_of_face_vars, reconstruction_order,\n        volume_vars, ghost_cell_vars, volume_extents, number_of_variables,\n        args_for_reconstructor...);\n  }\n}\n\ntemplate <typename Reconstructor, size_t Dim, typename... ArgsForReconstructor>\nvoid reconstruct(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        reconstructed_lower_side_of_face_vars,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const ArgsForReconstructor&... args_for_reconstructor) {\n  // Do not store reconstruction order.\n  std::optional<std::array<gsl::span<std::uint8_t>, Dim>> reconstruction_order{\n      std::nullopt};\n  reconstruct<Reconstructor>(reconstructed_upper_side_of_face_vars,\n                             reconstructed_lower_side_of_face_vars,\n                             make_not_null(&reconstruction_order), volume_vars,\n                             ghost_cell_vars, volume_extents,\n                             number_of_variables, args_for_reconstructor...);\n}\n}  // namespace detail\n\ntemplate <Side LowerOrUpperSide, typename Reconstructor, bool UseExteriorCell,\n          size_t NumberOfGhostPoints, size_t Dim,\n          typename... ArgsForReconstructor>\nvoid reconstruct_neighbor(\n    const gsl::not_null<DataVector*> face_data, const DataVector& volume_data,\n    const DataVector& neighbor_data, const Index<Dim>& volume_extents,\n    const Index<Dim>& ghost_data_extents,\n    const Direction<Dim>& direction_to_reconstruct,\n    const ArgsForReconstructor&... args_for_reconstructor) {\n  using std::get;\n  ASSERT(LowerOrUpperSide == direction_to_reconstruct.side(),\n         \"The template parameter LowerOrUpperSide (\"\n             << LowerOrUpperSide\n             << \") must match the direction to reconstruct, \"\n             << direction_to_reconstruct\n             << \". Note that we pass the Side in as a template parameter to \"\n                \"avoid runtime branches in tight loops.\");\n  static_assert(Reconstructor::stencil_width() == 3 or\n                    Reconstructor::stencil_width() == 5 or\n                    Reconstructor::stencil_width() == 7 or\n                    Reconstructor::stencil_width() == 9,\n                \"currently only support stencil widths of 3, 5, 7, and 9.\");\n\n  constexpr size_t index_of_pointwise =\n      (UseExteriorCell ? (LowerOrUpperSide == Side::Upper ? 0 : 1)\n                       : (LowerOrUpperSide == Side::Upper ? 1 : 0));\n  constexpr size_t volume_index_offset = (UseExteriorCell ? 0 : 1);\n  constexpr size_t ghost_index_offset = (UseExteriorCell ? 1 : 0);\n  // ghost_zone_offset is the offset at the lower boundary that arises from\n  // using the interior cell value rather than the exterior cell\n  // value. E.g. for MC with 2 ghost zones this is 1, but for WCNS5Z with 2\n  // ghost zones (e.g. MC in the interior) this is 0.\n  constexpr size_t ghost_zone_offset =\n      (UseExteriorCell\n           ? 0\n           : (NumberOfGhostPoints - Reconstructor::stencil_width() / 2));\n\n  constexpr size_t offset_into_u_to_reconstruct =\n      (Reconstructor::stencil_width() - 1) / 2;\n  std::array<double, Reconstructor::stencil_width()> u_to_reconstruct{};\n  if constexpr (Dim == 1) {\n    (void)ghost_data_extents;\n    (void)direction_to_reconstruct;\n\n    if (direction_to_reconstruct == Direction<Dim>::lower_xi()) {\n      u_to_reconstruct =\n          detail::u_to_reconstruct_impl<Side::Lower, 0, Dim, ghost_zone_offset>(\n              volume_data, neighbor_data, {std::numeric_limits<size_t>::max()},\n              volume_extents, ghost_data_extents,\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       volume_index_offset>{},\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       ghost_index_offset>{});\n    } else if (direction_to_reconstruct == Direction<Dim>::upper_xi()) {\n      u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Upper, 0, Dim>(\n          volume_data, neighbor_data, {std::numeric_limits<size_t>::max()},\n          volume_extents, ghost_data_extents,\n          std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                   volume_index_offset>{},\n          std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                   ghost_index_offset>{});\n    }\n    const auto upper_lower_and_order = Reconstructor::pointwise(\n        u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n        args_for_reconstructor...);\n    (*face_data)[0] = get<index_of_pointwise>(upper_lower_and_order);\n  } else if constexpr (Dim == 2) {\n    (void)ghost_data_extents;\n    if (direction_to_reconstruct == Direction<Dim>::lower_xi()) {\n      for (size_t j = 0; j < volume_extents[1]; ++j) {\n        u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Lower, 0, Dim,\n                                                         ghost_zone_offset>(\n            volume_data, neighbor_data, {std::numeric_limits<size_t>::max(), j},\n            volume_extents, ghost_data_extents,\n            std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                     volume_index_offset>{},\n            std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                     ghost_index_offset>{});\n        const auto upper_lower_and_order = Reconstructor::pointwise(\n            u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n            args_for_reconstructor...);\n        (*face_data)[j] = get<index_of_pointwise>(upper_lower_and_order);\n      }\n    } else if (direction_to_reconstruct == Direction<Dim>::upper_xi()) {\n      for (size_t j = 0; j < volume_extents[1]; ++j) {\n        u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Upper, 0, Dim>(\n            volume_data, neighbor_data, {std::numeric_limits<size_t>::max(), j},\n            volume_extents, ghost_data_extents,\n            std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                     volume_index_offset>{},\n            std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                     ghost_index_offset>{});\n        const auto upper_lower_and_order = Reconstructor::pointwise(\n            u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n            args_for_reconstructor...);\n        (*face_data)[j] = get<index_of_pointwise>(upper_lower_and_order);\n      }\n    } else if (direction_to_reconstruct == Direction<Dim>::lower_eta()) {\n      for (size_t i = 0; i < volume_extents[0]; ++i) {\n        u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Lower, 1, Dim,\n                                                         ghost_zone_offset>(\n            volume_data, neighbor_data, {i, std::numeric_limits<size_t>::max()},\n            volume_extents, ghost_data_extents,\n            std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                     volume_index_offset>{},\n            std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                     ghost_index_offset>{});\n        const auto upper_lower_and_order = Reconstructor::pointwise(\n            u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n            args_for_reconstructor...);\n        (*face_data)[i] = get<index_of_pointwise>(upper_lower_and_order);\n      }\n    } else if (direction_to_reconstruct == Direction<Dim>::upper_eta()) {\n      for (size_t i = 0; i < volume_extents[0]; ++i) {\n        u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Upper, 1, Dim>(\n            volume_data, neighbor_data, {i, std::numeric_limits<size_t>::max()},\n            volume_extents, ghost_data_extents,\n            std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                     volume_index_offset>{},\n            std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                     ghost_index_offset>{});\n        const auto upper_lower_and_order = Reconstructor::pointwise(\n            u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n            args_for_reconstructor...);\n        (*face_data)[i] = get<index_of_pointwise>(upper_lower_and_order);\n      }\n    }\n  } else {  // if constexpr (Dim == 3) is true\n    if (direction_to_reconstruct == Direction<Dim>::lower_xi()) {\n      const Index<Dim - 1> face_extents = volume_extents.slice_away(0);\n      for (size_t k = 0; k < volume_extents[2]; ++k) {\n        for (size_t j = 0; j < volume_extents[1]; ++j) {\n          u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Lower, 0, Dim,\n                                                           ghost_zone_offset>(\n              volume_data, neighbor_data,\n              {std::numeric_limits<size_t>::max(), j, k}, volume_extents,\n              ghost_data_extents,\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       volume_index_offset>{},\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       ghost_index_offset>{});\n          const auto upper_lower_and_order = Reconstructor::pointwise(\n              u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n              args_for_reconstructor...);\n          (*face_data)[collapsed_index(Index<Dim - 1>(j, k), face_extents)] =\n              get<index_of_pointwise>(upper_lower_and_order);\n        }\n      }\n    } else if (direction_to_reconstruct == Direction<Dim>::upper_xi()) {\n      const Index<Dim - 1> face_extents = volume_extents.slice_away(0);\n      for (size_t k = 0; k < volume_extents[2]; ++k) {\n        for (size_t j = 0; j < volume_extents[1]; ++j) {\n          u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Upper, 0, Dim>(\n              volume_data, neighbor_data,\n              {std::numeric_limits<size_t>::max(), j, k}, volume_extents,\n              ghost_data_extents,\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       volume_index_offset>{},\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       ghost_index_offset>{});\n          const auto upper_lower_and_order = Reconstructor::pointwise(\n              u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n              args_for_reconstructor...);\n          (*face_data)[collapsed_index(Index<Dim - 1>(j, k), face_extents)] =\n              get<index_of_pointwise>(upper_lower_and_order);\n        }\n      }\n    } else if (direction_to_reconstruct == Direction<Dim>::lower_eta()) {\n      const Index<Dim - 1> face_extents = volume_extents.slice_away(1);\n      for (size_t k = 0; k < volume_extents[2]; ++k) {\n        for (size_t i = 0; i < volume_extents[0]; ++i) {\n          u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Lower, 1, Dim,\n                                                           ghost_zone_offset>(\n              volume_data, neighbor_data,\n              {i, std::numeric_limits<size_t>::max(), k}, volume_extents,\n              ghost_data_extents,\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       volume_index_offset>{},\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       ghost_index_offset>{});\n          const auto upper_lower_and_order = Reconstructor::pointwise(\n              u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n              args_for_reconstructor...);\n          (*face_data)[collapsed_index(Index<Dim - 1>(i, k), face_extents)] =\n              get<index_of_pointwise>(upper_lower_and_order);\n        }\n      }\n    } else if (direction_to_reconstruct == Direction<Dim>::upper_eta()) {\n      const Index<Dim - 1> face_extents = volume_extents.slice_away(1);\n      for (size_t k = 0; k < volume_extents[2]; ++k) {\n        for (size_t i = 0; i < volume_extents[0]; ++i) {\n          u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Upper, 1, Dim>(\n              volume_data, neighbor_data,\n              {i, std::numeric_limits<size_t>::max(), k}, volume_extents,\n              ghost_data_extents,\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       volume_index_offset>{},\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       ghost_index_offset>{});\n          const auto upper_lower_and_order = Reconstructor::pointwise(\n              u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n              args_for_reconstructor...);\n          (*face_data)[collapsed_index(Index<Dim - 1>(i, k), face_extents)] =\n              get<index_of_pointwise>(upper_lower_and_order);\n        }\n      }\n    } else if (direction_to_reconstruct == Direction<Dim>::lower_zeta()) {\n      const Index<Dim - 1> face_extents = volume_extents.slice_away(2);\n      for (size_t j = 0; j < volume_extents[1]; ++j) {\n        for (size_t i = 0; i < volume_extents[0]; ++i) {\n          u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Lower, 2, Dim,\n                                                           ghost_zone_offset>(\n              volume_data, neighbor_data,\n              {i, j, std::numeric_limits<size_t>::max()}, volume_extents,\n              ghost_data_extents,\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       volume_index_offset>{},\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       ghost_index_offset>{});\n          const auto upper_lower_and_order = Reconstructor::pointwise(\n              u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n              args_for_reconstructor...);\n          (*face_data)[collapsed_index(Index<Dim - 1>(i, j), face_extents)] =\n              get<index_of_pointwise>(upper_lower_and_order);\n        }\n      }\n    } else if (direction_to_reconstruct == Direction<Dim>::upper_zeta()) {\n      const Index<Dim - 1> face_extents = volume_extents.slice_away(2);\n      for (size_t j = 0; j < volume_extents[1]; ++j) {\n        for (size_t i = 0; i < volume_extents[0]; ++i) {\n          u_to_reconstruct = detail::u_to_reconstruct_impl<Side::Upper, 2, Dim>(\n              volume_data, neighbor_data,\n              {i, j, std::numeric_limits<size_t>::max()}, volume_extents,\n              ghost_data_extents,\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       volume_index_offset>{},\n              std::make_index_sequence<Reconstructor::stencil_width() / 2 +\n                                       ghost_index_offset>{});\n          const auto upper_lower_and_order = Reconstructor::pointwise(\n              u_to_reconstruct.data() + offset_into_u_to_reconstruct, 1,\n              args_for_reconstructor...);\n          (*face_data)[collapsed_index(Index<Dim - 1>(i, j), face_extents)] =\n              get<index_of_pointwise>(upper_lower_and_order);\n        }\n      }\n    }\n  }\n}\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace fd {\nnamespace {\ntemplate <size_t Order, bool UnitStride>\nstruct ComputeImpl;\n\n// only the 4-th order is implemented for now\ntemplate <bool UnitStride>\nstruct ComputeImpl<4, UnitStride> {\n  static constexpr size_t fd_order = 4;\n\n  // The stencil is used where ghost data from two neighbors is needed.\n  // It uses 13 points including the point of derivative (the 0th\n  // point \"o\"). We use 1-4 to label the four points distant \\delta from the 0th\n  // point, 5-6 to label the two diagonal points distant sqrt(2)*\\delta from the\n  // 0th point, 7-10 to label the four points distant 2*\\delta from the 0th\n  // point, and 11-12 to label the two diagonal points distant 2*sqrt(2)*\\delta\n  // from the 0th point. Since the weights associated with the points of the\n  // same distance to the 0th point are the same, the ordering with each\n  // subgroup above is arbitrary.\n  // Schematically, this stencil (with descending diagonal) appears\n  // 12  x  8  x  x\n  //  x  6  2  x  x\n  //  9  3 o0  1  7\n  //  x  x  4  5  x\n  //  x  x 10  x 11\n  SPECTRE_ALWAYS_INLINE static double mixed_second_deriv_special_pointwise(\n      const std::array<double, 13>& q, const int stride,\n      const std::array<double, 3>& weights) {\n    ASSERT(stride == 1, \"Only UnitStride is supported\" << stride);\n    return weights[0] * q[0] +\n           weights[1] * (q[1] + q[2] + q[3] + q[4] - q[5] - q[6]) +\n           weights[2] * (q[11] + q[12] - q[7] - q[8] - q[9] - q[10]);\n  }\n\n  static constexpr std::array<double, 3>\n  mixed_second_derivative_special_weights(const double one_over_delta_squared) {\n    return {{-1.25 * one_over_delta_squared,\n             0.66666666666666667 * one_over_delta_squared,\n             0.04166666666666667 * one_over_delta_squared}};\n  }\n\n  // This stencil is used in the bulk.\n  // 0 - (x+\\delta, y+\\delta), 1 - (x-\\delta, y-\\delta), 2 - (x+\\delta,\n  // y-\\delta), 3 - (x-\\delta, y+\\delta), 4 - (x+2\\delta, y+2\\delta), 5 -\n  // (x-2\\delta, y-2\\delta), 6 - (x+2\\delta, y-2\\delta), 7 - (x-2\\delta,\n  // y+2\\delta)\n  // 7 x x x 4\n  // x 3 x 0 x\n  // x x o x x\n  // x 1 x 2 x\n  // 5 x x x 6\n  SPECTRE_ALWAYS_INLINE static double mixed_second_deriv_pointwise(\n      const std::array<double, 8>& q, const int stride,\n      const std::array<double, 2>& weights) {\n    ASSERT(stride == 1, \"Only UnitStride is supported\" << stride);\n    return weights[0] * (q[0] + q[1] - q[2] - q[3]) +\n           weights[1] * (-q[4] - q[5] + q[6] + q[7]);\n  }\n\n  static constexpr std::array<double, 2> mixed_second_derivative_weights(\n      const double one_over_delta_squared) {\n    return {{0.33333333333333333 * one_over_delta_squared,\n             0.02083333333333333 * one_over_delta_squared}};\n  }\n\n  // This stencil is used both in the bulk and the ghost zone.\n  // We label the point of derivative as the 0th point. We use\n  // 1-2 to label the points distant \\delta to the 0th point and 3-4 to label\n  // the points distant 2*\\delta to the 0th point. Note that this is different\n  // from the pointer arithmetic convention in PartialDerivatives.cpp\n  // 4 2 0 1 3\n  SPECTRE_ALWAYS_INLINE static double pure_second_deriv_pointwise(\n      const std::array<double, 5>& q, const int stride,\n      const std::array<double, 3>& weights) {\n    ASSERT(stride == 1, \"Only UnitStride is supported\" << stride);\n    return weights[0] * q[0] + weights[1] * (q[1] + q[2]) +\n           weights[2] * (q[3] + q[4]);\n  }\n\n  static constexpr std::array<double, 3> pure_second_derivative_weights(\n      const double one_over_delta_squared) {\n    return {{-2.5 * one_over_delta_squared,\n             1.33333333333333333 * one_over_delta_squared,\n             -0.08333333333333333 * one_over_delta_squared}};\n  }\n};\n\n// Compute the xx, xy derivatives\n// x direction points to right_ghost_data; y direction points to\n// upper_ghost_data. Individual tensor components and different tensors are\n// treated the same, so they all count in number_of_variables\ntemplate <typename DerivativeComputer, size_t Dim>\nvoid second_logical_partial_derivatives_fastest_dim(\n    const gsl::not_null<gsl::span<double>*> pure_second_derivative,\n    const gsl::not_null<gsl::span<double>*> mixed_second_derivative,\n    const gsl::span<const double>& volume_vars,\n    const gsl::span<const double>& lower_ghost_data,\n    const gsl::span<const double>& upper_ghost_data,\n    const gsl::span<const double>& left_ghost_data,\n    const gsl::span<const double>& right_ghost_data,\n    const Index<Dim>& volume_extents, const size_t number_of_variables,\n    const double delta_squared) {\n  constexpr size_t fd_order = DerivativeComputer::fd_order;\n  ASSERT(Dim == 3, \"The dimension is hardcoded to be 3 at this moment.\");\n  ASSERT(fd_order == 4,\n         \"Only the 4th order finite difference has been implemented\");\n  constexpr size_t ghost_zone_for_stencil =\n      fd_order / 2;  // decide points of derivatives that need ghost data\n\n  const std::array<double, 2> mixed_second_derivative_weights =\n      DerivativeComputer::mixed_second_derivative_weights(1.0 / delta_squared);\n  const std::array<double, 3> mixed_second_derivative_special_weights =\n      DerivativeComputer::mixed_second_derivative_special_weights(\n          1.0 / delta_squared);\n  const std::array<double, 3> pure_second_derivative_weights =\n      DerivativeComputer::pure_second_derivative_weights(1.0 / delta_squared);\n\n  size_t number_of_stripes =\n      volume_extents.slice_away(0).product() * number_of_variables;\n  ASSERT(left_ghost_data.size() % number_of_stripes == 0,\n         \"The left ghost data must be a multiple of the number of stripes (\"\n             << number_of_stripes\n             << \"), which is defined as the number of variables (\"\n             << number_of_variables\n             << \") times the number of grid points on a 2d slice (\"\n             << volume_extents.slice_away(0).product() << \")\");\n  ASSERT(volume_extents[0] >= 5 and volume_extents[1] >= 5 and\n             volume_extents[2] >= 5,\n         \"At least 5 points must exist in each direction\");\n  ASSERT(right_ghost_data.size() == left_ghost_data.size(),\n         \"The left ghost data size (\"\n             << left_ghost_data.size()\n             << \") must match the right ghost data size, \"\n             << right_ghost_data.size());\n  const size_t ghost_pts_in_left_neighbor_data =\n      left_ghost_data.size() / number_of_stripes;\n  const Index<Dim> left_neighbor_extents(ghost_pts_in_left_neighbor_data,\n                                         volume_extents[1], volume_extents[2]);\n\n  // These following code can be simplified if we assume isotropic extents\n  number_of_stripes =\n      volume_extents.slice_away(1).product() * number_of_variables;\n  ASSERT(lower_ghost_data.size() % number_of_stripes == 0,\n         \"The lower ghost data must be a multiple of the number of stripes (\"\n             << number_of_stripes\n             << \"), which is defined as the number of variables (\"\n             << number_of_variables\n             << \") times the number of grid points on a 2d slice (\"\n             << volume_extents.slice_away(1).product() << \")\");\n  ASSERT(lower_ghost_data.size() == upper_ghost_data.size(),\n         \"The lower ghost data size (\"\n             << lower_ghost_data.size()\n             << \") must match the upper ghost data size, \"\n             << upper_ghost_data.size());\n  const size_t ghost_pts_in_lower_neighbor_data =\n      lower_ghost_data.size() / number_of_stripes;\n  const Index<Dim> lower_neighbor_extents(\n      volume_extents[0], ghost_pts_in_lower_neighbor_data, volume_extents[2]);\n\n  constexpr size_t special_mixed_stencil_width = 13;\n  constexpr size_t mixed_stencil_width = 8;\n  constexpr size_t pure_stencil_width = 5;\n  std::array<double, special_mixed_stencil_width> q_mixed_special{};\n  std::array<double, mixed_stencil_width> q_mixed{};\n  std::array<double, pure_stencil_width> q_pure{};\n\n  // deal with each variable or tensor component\n  for (size_t vars_slice = 0; vars_slice < number_of_variables; ++vars_slice) {\n    const size_t vars_slice_offset = vars_slice * volume_extents.product();\n    const size_t left_neighbor_vars_slice_offset =\n        vars_slice * left_ghost_data.size() / number_of_variables;\n    const size_t lower_neighbor_vars_slice_offset =\n        vars_slice * lower_ghost_data.size() / number_of_variables;\n\n    // deal with each 2d xy slice\n    for (size_t k = 0; k < volume_extents[2]; ++k) {\n      // compute second derivatives in the bulk\n      for (size_t i = ghost_zone_for_stencil;\n           i < volume_extents[0] - ghost_zone_for_stencil; ++i) {\n        for (size_t j = ghost_zone_for_stencil;\n             j < volume_extents[1] - ghost_zone_for_stencil; ++j) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          q_pure[1] = volume_vars[vars_collapsed_index - 1];\n          q_pure[2] = volume_vars[vars_collapsed_index + 1];\n          q_pure[3] = volume_vars[vars_collapsed_index - 2];\n          q_pure[4] = volume_vars[vars_collapsed_index + 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed[0] =\n              volume_vars[vars_collapsed_index + volume_extents[0] + 1];\n          q_mixed[1] =\n              volume_vars[vars_collapsed_index - volume_extents[0] - 1];\n          q_mixed[2] =\n              volume_vars[vars_collapsed_index - volume_extents[0] + 1];\n          q_mixed[3] =\n              volume_vars[vars_collapsed_index + volume_extents[0] - 1];\n          q_mixed[4] =\n              volume_vars[vars_collapsed_index + 2 * volume_extents[0] + 2];\n          q_mixed[5] =\n              volume_vars[vars_collapsed_index - 2 * volume_extents[0] - 2];\n          q_mixed[6] =\n              volume_vars[vars_collapsed_index - 2 * volume_extents[0] + 2];\n          q_mixed[7] =\n              volume_vars[vars_collapsed_index + 2 * volume_extents[0] - 2];\n\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::mixed_second_deriv_pointwise(\n                  q_mixed, 1, mixed_second_derivative_weights);\n        }\n      }\n\n      // compute second derivatives in the ghost zone\n      // compute second derivatives in the left and right strips\n      const size_t right_ghost_zone_start =\n          volume_extents[0] - ghost_zone_for_stencil;\n\n      for (size_t j = ghost_zone_for_stencil;\n           j < volume_extents[1] - ghost_zone_for_stencil; ++j) {\n        // compute second derivatives in the left strip\n        for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          const size_t neighbor_vars_collapsed_index =\n              collapsed_index(\n                  Index<Dim>(ghost_pts_in_left_neighbor_data - 1, j, k),\n                  left_neighbor_extents) +\n              left_neighbor_vars_slice_offset;\n          q_pure[1] = (i == 0 ? left_ghost_data[neighbor_vars_collapsed_index]\n                              : volume_vars[vars_collapsed_index - 1]);\n          q_pure[2] = volume_vars[vars_collapsed_index + 1];\n          q_pure[3] = left_ghost_data[neighbor_vars_collapsed_index - 1 + i];\n          q_pure[4] = volume_vars[vars_collapsed_index + 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed[0] =\n              volume_vars[vars_collapsed_index + volume_extents[0] + 1];\n          q_mixed[1] =\n              (i == 0\n                   ? left_ghost_data[neighbor_vars_collapsed_index -\n                                     left_neighbor_extents[0]]\n                   : volume_vars[vars_collapsed_index - volume_extents[0] - 1]);\n          q_mixed[2] =\n              volume_vars[vars_collapsed_index - volume_extents[0] + 1];\n          q_mixed[3] =\n              (i == 0\n                   ? left_ghost_data[neighbor_vars_collapsed_index +\n                                     left_neighbor_extents[0]]\n                   : volume_vars[vars_collapsed_index + volume_extents[0] - 1]);\n          q_mixed[4] =\n              volume_vars[vars_collapsed_index + 2 * volume_extents[0] + 2];\n          q_mixed[5] = left_ghost_data[neighbor_vars_collapsed_index -\n                                       2 * left_neighbor_extents[0] - 1 + i];\n          q_mixed[6] =\n              volume_vars[vars_collapsed_index - 2 * volume_extents[0] + 2];\n          q_mixed[7] = left_ghost_data[neighbor_vars_collapsed_index +\n                                       2 * left_neighbor_extents[0] - 1 + i];\n\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::mixed_second_deriv_pointwise(\n                  q_mixed, 1, mixed_second_derivative_weights);\n        }\n\n        // compute second derivatives in the right strip\n        for (size_t i = right_ghost_zone_start; i < volume_extents[0]; ++i) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          const size_t neighbor_vars_collapsed_index =\n              collapsed_index(Index<Dim>(0, j, k), left_neighbor_extents) +\n              left_neighbor_vars_slice_offset;\n          q_pure[1] = (i == right_ghost_zone_start\n                           ? volume_vars[vars_collapsed_index + 1]\n                           : right_ghost_data[neighbor_vars_collapsed_index]);\n          q_pure[2] = volume_vars[vars_collapsed_index - 1];\n          q_pure[3] = right_ghost_data[neighbor_vars_collapsed_index + i -\n                                       right_ghost_zone_start];\n          q_pure[4] = volume_vars[vars_collapsed_index - 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed[0] =\n              (i == right_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + volume_extents[0] + 1]\n                   : right_ghost_data[neighbor_vars_collapsed_index +\n                                      left_neighbor_extents[0]]);\n          q_mixed[1] =\n              volume_vars[vars_collapsed_index - volume_extents[0] - 1];\n          q_mixed[2] =\n              (i == right_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index - volume_extents[0] + 1]\n                   : right_ghost_data[neighbor_vars_collapsed_index -\n                                      left_neighbor_extents[0]]);\n          q_mixed[3] =\n              volume_vars[vars_collapsed_index + volume_extents[0] - 1];\n          q_mixed[4] = right_ghost_data[neighbor_vars_collapsed_index +\n                                        2 * left_neighbor_extents[0] + i -\n                                        right_ghost_zone_start];\n          q_mixed[5] =\n              volume_vars[vars_collapsed_index - 2 * volume_extents[0] - 2];\n          q_mixed[6] = right_ghost_data[neighbor_vars_collapsed_index -\n                                        2 * left_neighbor_extents[0] + i -\n                                        right_ghost_zone_start];\n          q_mixed[7] =\n              volume_vars[vars_collapsed_index + 2 * volume_extents[0] - 2];\n\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::mixed_second_deriv_pointwise(\n                  q_mixed, 1, mixed_second_derivative_weights);\n        }\n      }\n\n      const size_t upper_ghost_zone_start =\n          volume_extents[1] - ghost_zone_for_stencil;\n\n      // compute second derivatives in the upper and lower strips\n      for (size_t i = ghost_zone_for_stencil;\n           i < volume_extents[0] - ghost_zone_for_stencil; ++i) {\n        // compute second derivatives in the lower strip\n        for (size_t j = 0; j < ghost_zone_for_stencil; ++j) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n          const size_t neighbor_vars_collapsed_index =\n              collapsed_index(\n                  Index<Dim>(i, ghost_pts_in_lower_neighbor_data - 1, k),\n                  lower_neighbor_extents) +\n              lower_neighbor_vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          q_pure[1] = volume_vars[vars_collapsed_index - 1];\n          q_pure[2] = volume_vars[vars_collapsed_index + 1];\n          q_pure[3] = volume_vars[vars_collapsed_index - 2];\n          q_pure[4] = volume_vars[vars_collapsed_index + 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed[0] =\n              volume_vars[vars_collapsed_index + volume_extents[0] + 1];\n          q_mixed[1] =\n              (j == 0\n                   ? lower_ghost_data[neighbor_vars_collapsed_index - 1]\n                   : volume_vars[vars_collapsed_index - volume_extents[0] - 1]);\n          q_mixed[2] =\n              (j == 0\n                   ? lower_ghost_data[neighbor_vars_collapsed_index + 1]\n                   : volume_vars[vars_collapsed_index - volume_extents[0] + 1]);\n          q_mixed[3] =\n              volume_vars[vars_collapsed_index + volume_extents[0] - 1];\n          q_mixed[4] =\n              volume_vars[vars_collapsed_index + 2 * volume_extents[0] + 2];\n          q_mixed[5] =\n              lower_ghost_data[neighbor_vars_collapsed_index -\n                               (1 - j) * lower_neighbor_extents[0] - 2];\n          q_mixed[6] =\n              lower_ghost_data[neighbor_vars_collapsed_index -\n                               (1 - j) * lower_neighbor_extents[0] + 2];\n          q_mixed[7] =\n              volume_vars[vars_collapsed_index + 2 * volume_extents[0] - 2];\n\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::mixed_second_deriv_pointwise(\n                  q_mixed, 1, mixed_second_derivative_weights);\n        }\n\n        // compute second derivatives in the upper strip\n        for (size_t j = upper_ghost_zone_start; j < volume_extents[1]; ++j) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n          const size_t neighbor_vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, 0, k), lower_neighbor_extents) +\n              lower_neighbor_vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          q_pure[1] = volume_vars[vars_collapsed_index - 1];\n          q_pure[2] = volume_vars[vars_collapsed_index + 1];\n          q_pure[3] = volume_vars[vars_collapsed_index - 2];\n          q_pure[4] = volume_vars[vars_collapsed_index + 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed[0] =\n              (j == upper_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + volume_extents[0] + 1]\n                   : upper_ghost_data[neighbor_vars_collapsed_index + 1]);\n          q_mixed[1] =\n              volume_vars[vars_collapsed_index - volume_extents[0] - 1];\n          q_mixed[2] =\n              volume_vars[vars_collapsed_index - volume_extents[0] + 1];\n          q_mixed[3] =\n              (j == upper_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + volume_extents[0] - 1]\n                   : upper_ghost_data[neighbor_vars_collapsed_index - 1]);\n          q_mixed[4] = upper_ghost_data[neighbor_vars_collapsed_index + 2 +\n                                        (j - upper_ghost_zone_start) *\n                                            lower_neighbor_extents[0]];\n          q_mixed[5] =\n              volume_vars[vars_collapsed_index - 2 * volume_extents[0] - 2];\n          q_mixed[6] =\n              volume_vars[vars_collapsed_index - 2 * volume_extents[0] + 2];\n          q_mixed[7] = upper_ghost_data[neighbor_vars_collapsed_index - 2 +\n                                        (j - upper_ghost_zone_start) *\n                                            lower_neighbor_extents[0]];\n\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::mixed_second_deriv_pointwise(\n                  q_mixed, 1, mixed_second_derivative_weights);\n        }\n      }\n\n      // compute second derivatives in the lower left corner\n      for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n        for (size_t j = 0; j < ghost_zone_for_stencil; ++j) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n          const size_t left_neighbor_vars_collapsed_index =\n              collapsed_index(\n                  Index<Dim>(ghost_pts_in_left_neighbor_data - 1, j, k),\n                  left_neighbor_extents) +\n              left_neighbor_vars_slice_offset;\n          const size_t lower_neighbor_vars_collapsed_index =\n              collapsed_index(\n                  Index<Dim>(i, ghost_pts_in_lower_neighbor_data - 1, k),\n                  lower_neighbor_extents) +\n              lower_neighbor_vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          q_pure[1] =\n              (i == 0 ? left_ghost_data[left_neighbor_vars_collapsed_index]\n                      : volume_vars[vars_collapsed_index - 1]);\n          q_pure[2] = volume_vars[vars_collapsed_index + 1];\n          q_pure[3] =\n              left_ghost_data[left_neighbor_vars_collapsed_index - 1 + i];\n          q_pure[4] = volume_vars[vars_collapsed_index + 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed_special[0] = volume_vars[vars_collapsed_index];\n          q_mixed_special[1] =\n              (i == 0 ? left_ghost_data[left_neighbor_vars_collapsed_index]\n                      : volume_vars[vars_collapsed_index - 1]);\n          q_mixed_special[2] = volume_vars[vars_collapsed_index + 1];\n          q_mixed_special[3] =\n              (j == 0 ? lower_ghost_data[lower_neighbor_vars_collapsed_index]\n                      : volume_vars[vars_collapsed_index - volume_extents[0]]);\n          q_mixed_special[4] =\n              volume_vars[vars_collapsed_index + volume_extents[0]];\n          q_mixed_special[5] =\n              (i == 0\n                   ? left_ghost_data[left_neighbor_vars_collapsed_index +\n                                     left_neighbor_extents[0]]\n                   : volume_vars[vars_collapsed_index + volume_extents[0] - 1]);\n          q_mixed_special[6] =\n              (j == 0\n                   ? lower_ghost_data[lower_neighbor_vars_collapsed_index + 1]\n                   : volume_vars[vars_collapsed_index - volume_extents[0] + 1]);\n          q_mixed_special[7] =\n              left_ghost_data[left_neighbor_vars_collapsed_index - 1 + i];\n          q_mixed_special[8] = volume_vars[vars_collapsed_index + 2];\n          q_mixed_special[9] =\n              lower_ghost_data[lower_neighbor_vars_collapsed_index -\n                               (1 - j) * lower_neighbor_extents[0]];\n          q_mixed_special[10] =\n              volume_vars[vars_collapsed_index + 2 * volume_extents[0]];\n          q_mixed_special[11] =\n              left_ghost_data[left_neighbor_vars_collapsed_index +\n                              2 * left_neighbor_extents[0] - 1 + i];\n          q_mixed_special[12] =\n              lower_ghost_data[lower_neighbor_vars_collapsed_index -\n                               (1 - j) * lower_neighbor_extents[0] + 2];\n\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::mixed_second_deriv_special_pointwise(\n                  q_mixed_special, 1, mixed_second_derivative_special_weights);\n        }\n      }\n\n      // compute second derivative in the upper left corner\n      for (size_t i = 0; i < ghost_zone_for_stencil; ++i) {\n        for (size_t j = upper_ghost_zone_start; j < volume_extents[1]; ++j) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n          const size_t left_neighbor_vars_collapsed_index =\n              collapsed_index(\n                  Index<Dim>(ghost_pts_in_left_neighbor_data - 1, j, k),\n                  left_neighbor_extents) +\n              left_neighbor_vars_slice_offset;\n          const size_t upper_neighbor_vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, 0, k), lower_neighbor_extents) +\n              lower_neighbor_vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          q_pure[1] =\n              (i == 0 ? left_ghost_data[left_neighbor_vars_collapsed_index]\n                      : volume_vars[vars_collapsed_index - 1]);\n          q_pure[2] = volume_vars[vars_collapsed_index + 1];\n          q_pure[3] =\n              left_ghost_data[left_neighbor_vars_collapsed_index - 1 + i];\n          q_pure[4] = volume_vars[vars_collapsed_index + 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed_special[0] = volume_vars[vars_collapsed_index];\n          q_mixed_special[1] =\n              (i == 0 ? left_ghost_data[left_neighbor_vars_collapsed_index]\n                      : volume_vars[vars_collapsed_index - 1]);\n          q_mixed_special[2] = volume_vars[vars_collapsed_index + 1];\n          q_mixed_special[3] =\n              volume_vars[vars_collapsed_index - volume_extents[0]];\n          q_mixed_special[4] =\n              (j == upper_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + volume_extents[0]]\n                   : upper_ghost_data[upper_neighbor_vars_collapsed_index]);\n          q_mixed_special[5] =\n              (i == 0\n                   ? left_ghost_data[left_neighbor_vars_collapsed_index -\n                                     left_neighbor_extents[0]]\n                   : volume_vars[vars_collapsed_index - volume_extents[0] - 1]);\n          q_mixed_special[6] =\n              (j == upper_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + volume_extents[0] + 1]\n                   : upper_ghost_data[upper_neighbor_vars_collapsed_index + 1]);\n          q_mixed_special[7] =\n              left_ghost_data[left_neighbor_vars_collapsed_index - 1 + i];\n          q_mixed_special[8] = volume_vars[vars_collapsed_index + 2];\n          q_mixed_special[9] =\n              volume_vars[vars_collapsed_index - 2 * volume_extents[0]];\n          q_mixed_special[10] =\n              upper_ghost_data[upper_neighbor_vars_collapsed_index +\n                               (j - upper_ghost_zone_start) *\n                                   lower_neighbor_extents[0]];\n          q_mixed_special[11] =\n              left_ghost_data[left_neighbor_vars_collapsed_index -\n                              2 * left_neighbor_extents[0] - 1 + i];\n          q_mixed_special[12] =\n              upper_ghost_data[upper_neighbor_vars_collapsed_index + 2 +\n                               (j - upper_ghost_zone_start) *\n                                   lower_neighbor_extents[0]];\n\n          // -1.0 as the weights are opposite for asending diagonal of the\n          // stencil\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              -1.0 *\n              DerivativeComputer::mixed_second_deriv_special_pointwise(\n                  q_mixed_special, 1, mixed_second_derivative_special_weights);\n        }\n      }\n\n      // compute second derivatives in the lower right corner\n      for (size_t i = right_ghost_zone_start; i < volume_extents[0]; ++i) {\n        for (size_t j = 0; j < ghost_zone_for_stencil; ++j) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n          const size_t right_neighbor_vars_collapsed_index =\n              collapsed_index(Index<Dim>(0, j, k), left_neighbor_extents) +\n              left_neighbor_vars_slice_offset;\n          const size_t lower_neighbor_vars_collapsed_index =\n              collapsed_index(\n                  Index<Dim>(i, ghost_pts_in_lower_neighbor_data - 1, k),\n                  lower_neighbor_extents) +\n              lower_neighbor_vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          q_pure[1] =\n              (i == right_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + 1]\n                   : right_ghost_data[right_neighbor_vars_collapsed_index]);\n          q_pure[2] = volume_vars[vars_collapsed_index - 1];\n          q_pure[3] = right_ghost_data[right_neighbor_vars_collapsed_index + i -\n                                       right_ghost_zone_start];\n          q_pure[4] = volume_vars[vars_collapsed_index - 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed_special[0] = volume_vars[vars_collapsed_index];\n          q_mixed_special[1] = volume_vars[vars_collapsed_index - 1];\n          q_mixed_special[2] =\n              (i == right_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + 1]\n                   : right_ghost_data[right_neighbor_vars_collapsed_index]);\n          q_mixed_special[3] =\n              (j == 0 ? lower_ghost_data[lower_neighbor_vars_collapsed_index]\n                      : volume_vars[vars_collapsed_index - volume_extents[0]]);\n          q_mixed_special[4] =\n              volume_vars[vars_collapsed_index + volume_extents[0]];\n          q_mixed_special[5] =\n              (j == 0\n                   ? lower_ghost_data[lower_neighbor_vars_collapsed_index - 1]\n                   : volume_vars[vars_collapsed_index - volume_extents[0] - 1]);\n          q_mixed_special[6] =\n              (i == right_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + volume_extents[0] + 1]\n                   : right_ghost_data[right_neighbor_vars_collapsed_index +\n                                      left_neighbor_extents[0]]);\n          q_mixed_special[7] = volume_vars[vars_collapsed_index - 2];\n          q_mixed_special[8] =\n              right_ghost_data[right_neighbor_vars_collapsed_index + i -\n                               right_ghost_zone_start];\n          q_mixed_special[9] =\n              lower_ghost_data[lower_neighbor_vars_collapsed_index -\n                               (1 - j) * lower_neighbor_extents[0]];\n          q_mixed_special[10] =\n              volume_vars[vars_collapsed_index + 2 * volume_extents[0]];\n          q_mixed_special[11] =\n              lower_ghost_data[lower_neighbor_vars_collapsed_index -\n                               (1 - j) * lower_neighbor_extents[0] - 2];\n          q_mixed_special[12] =\n              right_ghost_data[right_neighbor_vars_collapsed_index +\n                               2 * left_neighbor_extents[0] + i -\n                               right_ghost_zone_start];\n\n          // -1.0 as the weights are opposite for asending diagonal of the\n          // stencil\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              -1.0 *\n              DerivativeComputer::mixed_second_deriv_special_pointwise(\n                  q_mixed_special, 1, mixed_second_derivative_special_weights);\n        }\n      }\n\n      // compute second derivatives in the upper right corner\n      for (size_t i = right_ghost_zone_start; i < volume_extents[0]; ++i) {\n        for (size_t j = upper_ghost_zone_start; j < volume_extents[1]; ++j) {\n          const size_t vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, j, k), volume_extents) +\n              vars_slice_offset;\n          const size_t right_neighbor_vars_collapsed_index =\n              collapsed_index(Index<Dim>(0, j, k), left_neighbor_extents) +\n              left_neighbor_vars_slice_offset;\n          const size_t upper_neighbor_vars_collapsed_index =\n              collapsed_index(Index<Dim>(i, 0, k), lower_neighbor_extents) +\n              lower_neighbor_vars_slice_offset;\n\n          q_pure[0] = volume_vars[vars_collapsed_index];\n          q_pure[1] =\n              (i == right_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + 1]\n                   : right_ghost_data[right_neighbor_vars_collapsed_index]);\n          q_pure[2] = volume_vars[vars_collapsed_index - 1];\n          q_pure[3] = right_ghost_data[right_neighbor_vars_collapsed_index + i -\n                                       right_ghost_zone_start];\n          q_pure[4] = volume_vars[vars_collapsed_index - 2];\n\n          (*pure_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::pure_second_deriv_pointwise(\n                  q_pure, 1, pure_second_derivative_weights);\n\n          q_mixed_special[0] = volume_vars[vars_collapsed_index];\n          q_mixed_special[1] = volume_vars[vars_collapsed_index - 1];\n          q_mixed_special[2] =\n              (i == right_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + 1]\n                   : right_ghost_data[right_neighbor_vars_collapsed_index]);\n          q_mixed_special[3] =\n              volume_vars[vars_collapsed_index - volume_extents[0]];\n          q_mixed_special[4] =\n              (j == upper_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + volume_extents[0]]\n                   : upper_ghost_data[upper_neighbor_vars_collapsed_index]);\n          q_mixed_special[5] =\n              (j == upper_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index + volume_extents[0] - 1]\n                   : upper_ghost_data[upper_neighbor_vars_collapsed_index - 1]);\n          q_mixed_special[6] =\n              (i == right_ghost_zone_start\n                   ? volume_vars[vars_collapsed_index - volume_extents[0] + 1]\n                   : right_ghost_data[right_neighbor_vars_collapsed_index -\n                                      left_neighbor_extents[0]]);\n          q_mixed_special[7] = volume_vars[vars_collapsed_index - 2];\n          q_mixed_special[8] =\n              right_ghost_data[right_neighbor_vars_collapsed_index + i -\n                               right_ghost_zone_start];\n          q_mixed_special[9] =\n              volume_vars[vars_collapsed_index - 2 * volume_extents[0]];\n          q_mixed_special[10] =\n              upper_ghost_data[upper_neighbor_vars_collapsed_index +\n                               (j - upper_ghost_zone_start) *\n                                   lower_neighbor_extents[0]];\n          q_mixed_special[11] =\n              upper_ghost_data[upper_neighbor_vars_collapsed_index - 2 +\n                               (j - upper_ghost_zone_start) *\n                                   lower_neighbor_extents[0]];\n          q_mixed_special[12] =\n              right_ghost_data[right_neighbor_vars_collapsed_index -\n                               2 * left_neighbor_extents[0] + i -\n                               right_ghost_zone_start];\n\n          (*mixed_second_derivative)[vars_collapsed_index] =\n              DerivativeComputer::mixed_second_deriv_special_pointwise(\n                  q_mixed_special, 1, mixed_second_derivative_special_weights);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DerivativeComputer, size_t Dim>\nvoid second_logical_partial_derivatives_impl(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        pure_second_logical_derivatives,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        mixed_second_logical_derivatives,\n    gsl::span<double>* const in_buffer,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables) {\n  const size_t number_of_points = volume_mesh.number_of_grid_points();\n#ifdef SPECTRE_DEBUG\n  ASSERT(Dim == 3, \"The dimension is hardcoded to be 3 at this moment.\");\n  ASSERT(volume_mesh == Mesh<Dim>(volume_mesh.extents(0), volume_mesh.basis(0),\n                                  volume_mesh.quadrature(0)),\n         \"The mesh must be isotropic, but got \" << volume_mesh);\n  ASSERT(\n      volume_mesh.basis(0) == Spectral::Basis::FiniteDifference,\n      \"Mesh basis must be FiniteDifference but got \" << volume_mesh.basis(0));\n  ASSERT(volume_mesh.quadrature(0) == Spectral::Quadrature::CellCentered,\n         \"Mesh quadrature must be CellCentered but got \"\n             << volume_mesh.quadrature(0));\n  // Note that number_of_variables here refers to number of independent\n  // components; e.g. a rank-1 tensor has 3 independent components\n  ASSERT(volume_vars.size() == number_of_points * number_of_variables,\n         \"The size of the volume vars must be the number of points (\"\n             << number_of_points << \") times the number of variables (\"\n             << number_of_variables << \") but is \" << volume_vars.size());\n  for (size_t i = 0; i < Dim; ++i) {\n    ASSERT(gsl::at(*pure_second_logical_derivatives, i).size() ==\n               volume_vars.size(),\n           \"The pure second logical derivatives must have size \"\n               << volume_vars.size() << \" but has size \"\n               << gsl::at(*pure_second_logical_derivatives, i).size()\n               << \" in dimension \" << i);\n    ASSERT(gsl::at(*mixed_second_logical_derivatives, i).size() ==\n               volume_vars.size(),\n           \"The mixed second logical derivatives must have size \"\n               << volume_vars.size() << \" but has size \"\n               << gsl::at(*mixed_second_logical_derivatives, i).size()\n               << \" in dimension \" << i);\n  }\n#endif  // SPECTRE_DEBUG\n\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_xi()),\n         \"Couldn't find ghost data in lower-xi\");\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_xi()),\n         \"Couldn't find ghost data in upper-xi\");\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_eta()),\n         \"Couldn't find ghost data in lower-eta\");\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_eta()),\n         \"Couldn't find ghost data in upper-eta\");\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::lower_zeta()),\n         \"Couldn't find ghost data in lower-zeta\");\n  ASSERT(ghost_cell_vars.contains(Direction<Dim>::upper_zeta()),\n         \"Couldn't find ghost data in upper-zeta\");\n\n  const Index<Dim>& volume_extents = volume_mesh.extents();\n\n  const auto& logical_xi_coords =\n      Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                   Spectral::Quadrature::CellCentered>(\n          volume_mesh.extents(0));\n\n  // compute the xx, xy derivatives\n  second_logical_partial_derivatives_fastest_dim<DerivativeComputer>(\n      make_not_null(&(*pure_second_logical_derivatives)[0]),\n      make_not_null(&(*mixed_second_logical_derivatives)[0]), volume_vars,\n      ghost_cell_vars.at(Direction<Dim>::lower_eta()),  // lower ghost data\n      ghost_cell_vars.at(Direction<Dim>::upper_eta()),  // upper ghost data\n      ghost_cell_vars.at(Direction<Dim>::lower_xi()),   // left ghost data\n      ghost_cell_vars.at(Direction<Dim>::upper_xi()),   // right ghost data\n      volume_extents, number_of_variables,\n      square(logical_xi_coords[1] - logical_xi_coords[0]));\n\n  // We transpose from (x,y,z,vars) ordering to (y,z,x,vars) ordering\n  // Might not be the most efficient (unclear), but easiest.\n  // We use a single large buffer for both the yz and xz derivatives\n  // to reduce the number of memory allocations and improve data locality.\n\n  // compute the yy, yz derivatives\n  auto& left_ghost = ghost_cell_vars.at(Direction<Dim>::lower_eta());\n  auto& right_ghost = ghost_cell_vars.at(Direction<Dim>::upper_eta());\n  auto& lower_ghost = ghost_cell_vars.at(Direction<Dim>::lower_zeta());\n  auto& upper_ghost = ghost_cell_vars.at(Direction<Dim>::upper_zeta());\n  const size_t derivative_size = (*pure_second_logical_derivatives)[1].size() +\n                                 (*mixed_second_logical_derivatives)[1].size();\n  DataVector buffer{};\n  if (in_buffer != nullptr) {\n    ASSERT(\n        (in_buffer->size() >= volume_vars.size() + lower_ghost.size() +\n                                  upper_ghost.size() + left_ghost.size() +\n                                  right_ghost.size() + derivative_size),\n        \"The buffer must have size greater than or equal to \"\n            << (volume_vars.size() + lower_ghost.size() + upper_ghost.size() +\n                left_ghost.size() + right_ghost.size() + derivative_size)\n            << \" but has size \" << in_buffer->size());\n    buffer.set_data_ref(in_buffer->data(), in_buffer->size());\n  } else {\n    buffer = DataVector{volume_vars.size() + lower_ghost.size() +\n                        upper_ghost.size() + left_ghost.size() +\n                        right_ghost.size() + derivative_size};\n  }\n\n  size_t number_of_pts_in_left_ghost = left_ghost.size() / number_of_variables;\n  size_t number_of_pts_in_right_ghost =\n      right_ghost.size() / number_of_variables;\n  size_t number_of_pts_in_lower_ghost =\n      lower_ghost.size() / number_of_variables;\n  size_t number_of_pts_in_upper_ghost =\n      upper_ghost.size() / number_of_variables;\n\n  for (size_t vars_slice = 0; vars_slice < number_of_variables; ++vars_slice) {\n    raw_transpose(\n        make_not_null(&buffer[vars_slice * number_of_points]),\n        volume_vars.subspan(vars_slice * number_of_points, number_of_points)\n            .data(),\n        volume_extents[0], number_of_points / volume_extents[0]);\n    raw_transpose(\n        make_not_null(&buffer[volume_vars.size() +\n                              vars_slice * number_of_pts_in_lower_ghost]),\n        lower_ghost\n            .subspan(vars_slice * number_of_pts_in_lower_ghost,\n                     number_of_pts_in_lower_ghost)\n            .data(),\n        volume_extents[0], number_of_pts_in_lower_ghost / volume_extents[0]);\n    raw_transpose(\n        make_not_null(&buffer[volume_vars.size() + lower_ghost.size() +\n                              vars_slice * number_of_pts_in_upper_ghost]),\n        upper_ghost\n            .subspan(vars_slice * number_of_pts_in_upper_ghost,\n                     number_of_pts_in_upper_ghost)\n            .data(),\n        volume_extents[0], number_of_pts_in_upper_ghost / volume_extents[0]);\n\n    raw_transpose(\n        make_not_null(&buffer[volume_vars.size() + lower_ghost.size() +\n                              upper_ghost.size() +\n                              vars_slice * number_of_pts_in_left_ghost]),\n        left_ghost\n            .subspan(vars_slice * number_of_pts_in_left_ghost,\n                     number_of_pts_in_left_ghost)\n            .data(),\n        volume_extents[0], number_of_pts_in_left_ghost / volume_extents[0]);\n\n    raw_transpose(\n        make_not_null(&buffer[volume_vars.size() + lower_ghost.size() +\n                              upper_ghost.size() + left_ghost.size() +\n                              vars_slice * number_of_pts_in_right_ghost]),\n        right_ghost\n            .subspan(vars_slice * number_of_pts_in_right_ghost,\n                     number_of_pts_in_right_ghost)\n            .data(),\n        volume_extents[0], number_of_pts_in_right_ghost / volume_extents[0]);\n  }\n\n  // Note: assumes isotropic extents\n  const size_t pure_second_derivative_offset_in_buffer =\n      volume_vars.size() + lower_ghost.size() + upper_ghost.size() +\n      left_ghost.size() + right_ghost.size();\n  const size_t mixed_second_derivative_offset_in_buffer =\n      pure_second_derivative_offset_in_buffer +\n      (*pure_second_logical_derivatives)[1].size();\n  gsl::span<double> pure_second_derivative_view =\n      gsl::make_span(&buffer[pure_second_derivative_offset_in_buffer],\n                     (*pure_second_logical_derivatives)[1].size());\n  gsl::span<double> mixed_second_derivative_view =\n      gsl::make_span(&buffer[mixed_second_derivative_offset_in_buffer],\n                     (*mixed_second_logical_derivatives)[1].size());\n\n  const auto& logical_eta_coords =\n      Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                   Spectral::Quadrature::CellCentered>(\n          volume_mesh.extents(1));\n\n  second_logical_partial_derivatives_fastest_dim<DerivativeComputer>(\n      make_not_null(&pure_second_derivative_view),\n      make_not_null(&mixed_second_derivative_view),\n      // NOLINTNEXTLINE(readability-container-data-pointer)\n      gsl::make_span(&buffer[0], volume_vars.size()),\n      gsl::make_span(&buffer[volume_vars.size()], lower_ghost.size()),\n      gsl::make_span(&buffer[volume_vars.size() + lower_ghost.size()],\n                     upper_ghost.size()),\n      gsl::make_span(\n          &buffer[volume_vars.size() + lower_ghost.size() + upper_ghost.size()],\n          left_ghost.size()),\n      gsl::make_span(&buffer[volume_vars.size() + lower_ghost.size() +\n                             upper_ghost.size() + left_ghost.size()],\n                     right_ghost.size()),\n      volume_extents, number_of_variables,\n      square(logical_eta_coords[1] - logical_eta_coords[0]));\n\n  // Transpose result back\n  for (size_t vars_slice = 0; vars_slice < number_of_variables; ++vars_slice) {\n    raw_transpose(make_not_null((*pure_second_logical_derivatives)[1].data() +\n                                vars_slice * number_of_points),\n                  pure_second_derivative_view\n                      .subspan(vars_slice * number_of_points, number_of_points)\n                      .data(),\n                  number_of_points / volume_extents[0], volume_extents[0]);\n\n    raw_transpose(make_not_null((*mixed_second_logical_derivatives)[1].data() +\n                                vars_slice * number_of_points),\n                  mixed_second_derivative_view\n                      .subspan(vars_slice * number_of_points, number_of_points)\n                      .data(),\n                  number_of_points / volume_extents[0], volume_extents[0]);\n  }\n\n  // compute the zz, zx derivatives\n  number_of_pts_in_left_ghost =\n      ghost_cell_vars.at(Direction<Dim>::lower_zeta()).size() /\n      number_of_variables;\n  number_of_pts_in_right_ghost =\n      ghost_cell_vars.at(Direction<Dim>::upper_zeta()).size() /\n      number_of_variables;\n  number_of_pts_in_lower_ghost =\n      ghost_cell_vars.at(Direction<Dim>::lower_xi()).size() /\n      number_of_variables;\n  number_of_pts_in_upper_ghost =\n      ghost_cell_vars.at(Direction<Dim>::upper_xi()).size() /\n      number_of_variables;\n\n  const size_t chunk_size = volume_extents[0] * volume_extents[1];\n  const size_t lower_chunk_size =\n      number_of_pts_in_lower_ghost / volume_extents[2];\n  const size_t upper_chunk_size =\n      number_of_pts_in_upper_ghost / volume_extents[2];\n  const size_t number_of_volume_chunks = number_of_points / chunk_size;\n  const size_t number_of_left_neighbor_chunks =\n      number_of_pts_in_left_ghost / chunk_size;\n  const size_t number_of_right_neighbor_chunks =\n      number_of_pts_in_right_ghost / chunk_size;\n  const size_t number_of_lower_neighbor_chunks =\n      number_of_pts_in_lower_ghost / lower_chunk_size;\n  const size_t number_of_upper_neighbor_chunks =\n      number_of_pts_in_upper_ghost / upper_chunk_size;\n\n  for (size_t vars_slice = 0; vars_slice < number_of_variables; ++vars_slice) {\n    raw_transpose(\n        make_not_null(buffer.data() + vars_slice * number_of_points),\n        volume_vars.subspan(vars_slice * number_of_points, number_of_points)\n            .data(),\n        chunk_size, number_of_volume_chunks);\n    raw_transpose(\n        make_not_null(&buffer[volume_vars.size() +\n                              vars_slice * number_of_pts_in_lower_ghost]),\n        ghost_cell_vars.at(Direction<Dim>::lower_xi())\n            .subspan(vars_slice * number_of_pts_in_lower_ghost,\n                     number_of_pts_in_lower_ghost)\n            .data(),\n        lower_chunk_size, number_of_lower_neighbor_chunks);\n    raw_transpose(\n        make_not_null(\n            &buffer[volume_vars.size() +\n                    ghost_cell_vars.at(Direction<Dim>::lower_xi()).size() +\n                    vars_slice * number_of_pts_in_upper_ghost]),\n        ghost_cell_vars.at(Direction<Dim>::upper_xi())\n            .subspan(vars_slice * number_of_pts_in_upper_ghost,\n                     number_of_pts_in_upper_ghost)\n            .data(),\n        upper_chunk_size, number_of_upper_neighbor_chunks);\n    raw_transpose(\n        make_not_null(\n            &buffer[volume_vars.size() +\n                    ghost_cell_vars.at(Direction<Dim>::lower_xi()).size() +\n                    ghost_cell_vars.at(Direction<Dim>::upper_xi()).size() +\n                    vars_slice * number_of_pts_in_left_ghost]),\n        ghost_cell_vars.at(Direction<Dim>::lower_zeta())\n            .subspan(vars_slice * number_of_pts_in_left_ghost,\n                     number_of_pts_in_left_ghost)\n            .data(),\n        chunk_size, number_of_left_neighbor_chunks);\n    raw_transpose(\n        make_not_null(\n            &buffer[volume_vars.size() +\n                    ghost_cell_vars.at(Direction<Dim>::lower_xi()).size() +\n                    ghost_cell_vars.at(Direction<Dim>::upper_xi()).size() +\n                    ghost_cell_vars.at(Direction<Dim>::lower_zeta()).size() +\n                    vars_slice * number_of_pts_in_right_ghost]),\n        ghost_cell_vars.at(Direction<Dim>::upper_zeta())\n            .subspan(vars_slice * number_of_pts_in_right_ghost,\n                     number_of_pts_in_right_ghost)\n            .data(),\n        chunk_size, number_of_right_neighbor_chunks);\n  }\n\n  const auto& logical_zeta_coords =\n      Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                   Spectral::Quadrature::CellCentered>(\n          volume_mesh.extents(2));\n\n  second_logical_partial_derivatives_fastest_dim<DerivativeComputer>(\n      make_not_null(&pure_second_derivative_view),\n      make_not_null(&mixed_second_derivative_view),\n      // NOLINTNEXTLINE(readability-container-data-pointer)\n      gsl::make_span(&buffer[0], volume_vars.size()),\n      gsl::make_span(&buffer[volume_vars.size()],\n                     ghost_cell_vars.at(Direction<Dim>::lower_xi()).size()),\n      gsl::make_span(\n          &buffer[volume_vars.size() +\n                  ghost_cell_vars.at(Direction<Dim>::lower_xi()).size()],\n          ghost_cell_vars.at(Direction<Dim>::upper_xi()).size()),\n      gsl::make_span(\n          &buffer[volume_vars.size() +\n                  ghost_cell_vars.at(Direction<Dim>::lower_xi()).size() +\n                  ghost_cell_vars.at(Direction<Dim>::upper_xi()).size()],\n          ghost_cell_vars.at(Direction<Dim>::lower_zeta()).size()),\n      gsl::make_span(\n          &buffer[volume_vars.size() +\n                  ghost_cell_vars.at(Direction<Dim>::lower_xi()).size() +\n                  ghost_cell_vars.at(Direction<Dim>::upper_xi()).size() +\n                  ghost_cell_vars.at(Direction<Dim>::lower_zeta()).size()],\n          ghost_cell_vars.at(Direction<Dim>::upper_zeta()).size()),\n      volume_extents, number_of_variables,\n      square(logical_zeta_coords[1] - logical_zeta_coords[0]));\n\n  // Transpose result back\n  for (size_t vars_slice = 0; vars_slice < number_of_variables; ++vars_slice) {\n    // NOLINTNEXTLINE(readability-suspicious-call-argument)\n    raw_transpose(make_not_null((*pure_second_logical_derivatives)[2].data() +\n                                vars_slice * number_of_points),\n                  pure_second_derivative_view\n                      .subspan(vars_slice * number_of_points, number_of_points)\n                      .data(),\n                  number_of_volume_chunks, chunk_size);\n    // NOLINTNEXTLINE(readability-suspicious-call-argument)\n    raw_transpose(make_not_null((*mixed_second_logical_derivatives)[2].data() +\n                                vars_slice * number_of_points),\n                  mixed_second_derivative_view\n                      .subspan(vars_slice * number_of_points, number_of_points)\n                      .data(),\n                  number_of_volume_chunks, chunk_size);\n  }\n}\n}  // namespace\n\nnamespace detail {\ntemplate <size_t Dim>\nvoid second_logical_partial_derivatives_impl(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        pure_second_logical_derivatives,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        mixed_second_logical_derivatives,\n    gsl::span<double>* const buffer, const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order) {\n  switch (fd_order) {\n    case 4:\n      ::fd::second_logical_partial_derivatives_impl<ComputeImpl<4, true>>(\n          pure_second_logical_derivatives, mixed_second_logical_derivatives,\n          buffer, volume_vars, ghost_cell_vars, volume_mesh,\n          number_of_variables);\n      break;\n    default:\n      ERROR(\"Cannot do finite difference derivative of order \" << fd_order);\n  };\n}\n}  // namespace detail\n\ntemplate <size_t Dim>\nvoid second_logical_partial_derivatives(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        pure_second_logical_derivatives,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        mixed_second_logical_derivatives,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order) {\n  detail::second_logical_partial_derivatives_impl(\n      pure_second_logical_derivatives, mixed_second_logical_derivatives,\n      nullptr, volume_vars, ghost_cell_vars, volume_mesh, number_of_variables,\n      fd_order);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void detail::second_logical_partial_derivatives_impl(               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          pure_second_derivative,                                              \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          mixed_second_derivative,                                             \\\n      gsl::span<double>* const buffer,                                         \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Mesh<DIM(data)>& volume_fd_mesh, size_t number_of_variables,       \\\n      size_t fd_order);                                                        \\\n  template void second_logical_partial_derivatives(                            \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          pure_second_derivative,                                              \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          mixed_second_derivative,                                             \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Mesh<DIM(data)>& volume_fd_mesh, size_t number_of_variables,       \\\n      size_t fd_order);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\n/// \\endcond\n\n/*!\n* \\brief The finite difference second derivatives originally\n* designed for implementing the CCZ4 system.\n*/\nnamespace fd {\n/*!\n * \\brief Compute the pure and mixed second logical partial derivatives using\n * finite difference derivatives. Only 3 dimensions 4th-order fd is supported.\n *\n * We use a symmetric stencil in the bulk and slightly asymetric stencil\n * for the mixed partial derivatives where ghost data from two\n * neighbors is needed (to avoid communication across diagonal elements).\n * The `mixed_second_logical_derivatives` return an 3D array corresponding to\n * xy, yz, xz mixed partial derivatives.\n *\n * The stencil for the pure second derivatives is\n \\f[ \\frac{\\partial^2 f}{\\partial x^2} =\n    - \\frac{1}{12\\delta^2}[f(x+2\\delta,y)+f(x-2\\delta,y)]\n    + \\frac{4}{3\\delta^2}[f(x+\\delta,y)+f(x-\\delta,y)]-\\frac{5}{2\\delta^2}f(x,y)\n + O(\\delta^4). \\f]\n *\n * The stencil for the mixed second derivatives in the bulk is\n \\f[ \\begin{aligned} \\frac{\\partial^2 f}{\\partial x \\partial y} =&\n      \\frac{1}{3\\delta^2}[f(x+\\delta,y+\\delta) + f(x-\\delta,y-\\delta) -\n f(x+\\delta,y-\\delta) - f(x-\\delta,y+\\delta)] \\\\\n    &+ \\frac{1}{48\\delta^2}[f(x+2\\delta,y-2\\delta) + f(x-2\\delta,y+2\\delta)\n        - f(x+2\\delta,y+2\\delta) - f(x-2\\delta,y-2\\delta)] + O(\\delta^4).\n \\end{aligned}\\f]\n *\n * The stencil for the mixed second derivatives in the ghost zone is (with\n * descending diagonal)\n \\f[ \\begin{aligned} \\frac{\\partial^2 f}{\\partial x \\partial y} =&\n      \\frac{1}{24\\delta^2}[f(x+2\\delta,y-2\\delta) + f(x-2\\delta,y+2\\delta)\n        - f(x+2\\delta,y) - f(x-2\\delta,y) - f(x,y+2\\delta) - f(x,y-2\\delta)] \\\\\n    &+ \\frac{2}{3\\delta^2}[f(x+\\delta,y) + f(x-\\delta,y) + f(x,y+\\delta) +\n        f(x,y-\\delta) - f(x+\\delta,y-\\delta) - f(x-\\delta,y+\\delta)]\n    - \\frac{5}{4\\delta^2}f(x,y) + O(\\delta^4),\n \\end{aligned}\\f]\n *\n * and the ascending diagonal stencil has the opposite weights.\n *\n * \\note Currently the stride is always one because we transpose the data before\n * reconstruction.\n */\ntemplate <size_t Dim>\nvoid second_logical_partial_derivatives(\n    gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        pure_second_logical_derivatives,\n    gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        mixed_second_logical_derivatives,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, size_t number_of_variables, size_t fd_order);\n\n/*!\n * \\brief Compute the second partial derivatives on the `DerivativeFrame` using\n * the `inverse_jacobian` and `inverse_hessian`.\n * Only 3 dimensions 4th-order fd is supported.\n *\n * First and second logical partial derivatives are first computed using the\n * `fd::logical_partial_derivatives()` and\n * `fd::second_logical_partial_derivatives()` function.\n */\ntemplate <typename DerivativeTags, size_t Dim, typename DerivativeFrame>\nvoid second_partial_derivatives(\n    gsl::not_null<\n        Variables<db::wrap_tags_in<::Tags::second_deriv, DerivativeTags,\n                                   tmpl::size_t<Dim>, DerivativeFrame>>*>\n        second_partial_derivatives,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, size_t number_of_variables, size_t fd_order,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const InverseHessian<DataVector, Dim, Frame::ElementLogical,\n                         DerivativeFrame>& inverse_hessian);\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace fd {\nnamespace detail {\ntemplate <size_t Dim>\nvoid second_logical_partial_derivatives_impl(\n    gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        pure_second_logical_derivatives,\n    gsl::not_null<std::array<gsl::span<double>, Dim>*>\n        mixed_second_logical_derivatives,\n    gsl::span<double>* buffer, const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, size_t number_of_variables, size_t fd_order);\n}  // namespace detail\n\nnamespace partial_derivatives_detail {\ntemplate <size_t Dim, typename VariableTags, typename DerivativeTags>\nstruct LogicalImpl;\n\n// Note that the index of ddu is (i, j, k, d1, d2, s)\n// where d's are the derivative indices and s is the tensor component and\n// i varying the fastest, which is different from any of the logical\n// derivatives index (d, i, j, k, s). We assume ddu is symmetric in the\n// two derivative indices.\ntemplate <typename ResultTags, size_t Dim, typename DerivativeFrame,\n          typename ValueType = typename Variables<ResultTags>::value_type,\n          typename VectorType = typename Variables<ResultTags>::vector_type>\nvoid second_partial_derivatives_impl(\n    const gsl::not_null<Variables<ResultTags>*> ddu,\n    const std::array<const ValueType*, Dim>&\n        first_logical_partial_derivatives_of_u,\n    const std::array<const ValueType*, Dim>&\n        pure_second_logical_partial_derivatives_of_u,\n    const std::array<const ValueType*, Dim>&\n        mixed_second_logical_partial_derivatives_of_u,\n    const size_t number_of_independent_components,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const InverseHessian<DataVector, Dim, Frame::ElementLogical,\n                         DerivativeFrame>& inverse_hessian) {\n  ValueType* ptr_ddu = ddu->data();\n  const size_t num_grid_points = ddu->number_of_grid_points();\n  VectorType lhs{};\n  VectorType first_logical_du{};\n  VectorType second_logical_du{};\n\n  // The storage indices into the inverse Jacobian are precomputed to avoid\n  // having to recompute them for each tensor component of `u`.\n  std::array<std::array<size_t, Dim>, Dim> jacobian_indices{};\n  for (size_t deriv_index = 0; deriv_index < Dim; ++deriv_index) {\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(gsl::at(jacobian_indices, d), deriv_index) =\n          InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>::get_storage_index(d, deriv_index);\n    }\n  }\n  std::array<std::array<std::array<size_t, Dim>, Dim>, Dim> hessian_indices{};\n  for (size_t d = 0; d < Dim; ++d) {\n    for (size_t first_deriv_index = 0; first_deriv_index < Dim;\n         ++first_deriv_index) {\n      for (size_t second_deriv_index = 0; second_deriv_index < Dim;\n           ++second_deriv_index) {\n        gsl::at(gsl::at(gsl::at(hessian_indices, d), first_deriv_index),\n                second_deriv_index) =\n            InverseHessian<\n                DataVector, Dim, Frame::ElementLogical,\n                DerivativeFrame>::get_storage_index(d, first_deriv_index,\n                                                    second_deriv_index);\n      }\n    }\n  }\n\n  for (size_t component_index = 0;\n       component_index < number_of_independent_components; ++component_index) {\n    for (size_t first_deriv_index = 0; first_deriv_index < Dim;\n         ++first_deriv_index) {\n      for (size_t second_deriv_index = first_deriv_index;\n           second_deriv_index < Dim; ++second_deriv_index) {\n        lhs.set_data_ref(ptr_ddu, num_grid_points);\n        ptr_ddu += num_grid_points;\n\n        // clang-tidy: const cast is fine since we won't modify the data and we\n        // need it to easily hook into the expression templates.\n        second_logical_du.set_data_ref(\n            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n            const_cast<ValueType*>(\n                gsl::at(pure_second_logical_partial_derivatives_of_u, 0)) +\n                component_index * num_grid_points,\n            num_grid_points);\n\n        lhs = (*(inverse_jacobian.begin() +\n                 gsl::at(gsl::at(jacobian_indices, 0), first_deriv_index))) *\n              (*(inverse_jacobian.begin() +\n                 gsl::at(gsl::at(jacobian_indices, 0), second_deriv_index))) *\n              second_logical_du;\n\n        for (size_t first_logical_deriv_index = 0;\n             first_logical_deriv_index < Dim; ++first_logical_deriv_index) {\n          for (size_t second_logical_deriv_index = 0;\n               second_logical_deriv_index < Dim; ++second_logical_deriv_index) {\n            if (first_logical_deriv_index + second_logical_deriv_index != 0) {\n              if (first_logical_deriv_index == second_logical_deriv_index) {\n                // clang-tidy: const cast is fine since we won't modify the data\n                // and we need it to easily hook into the expression templates.\n                second_logical_du.set_data_ref(\n                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n                    const_cast<ValueType*>(\n                        gsl::at(pure_second_logical_partial_derivatives_of_u,\n                                first_logical_deriv_index)) +\n                        component_index * num_grid_points,\n                    num_grid_points);\n              } else {\n                // we need to map (first_logical_deriv_index,\n                // second_logical_deriv_index) to an index\n                // single_mixed_partial_index into the\n                // mixed_second_logical_partial_derivatives_of_u. For\n                // single_mixed_partial_index, 0 is xy deriv, 1 is yz deriv,\n                // and 2 is xz deriv. So we need a symmetric map (0,1)->0,\n                // (0,2)->2, (1,2)->1.\n                const size_t single_mixed_partial_index =\n                    [first_logical_deriv_index,\n                     second_logical_deriv_index]() -> size_t {\n                  if (first_logical_deriv_index * second_logical_deriv_index !=\n                      0) {\n                    return 1;\n                  } else if (first_logical_deriv_index +\n                                 second_logical_deriv_index ==\n                             1) {\n                    return 0;\n                  } else {\n                    return 2;\n                  }\n                }();\n                // clang-tidy: const cast is fine since we won't modify the data\n                // and we need it to easily hook into the expression templates.\n                second_logical_du.set_data_ref(\n                    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n                    const_cast<ValueType*>(\n                        gsl::at(mixed_second_logical_partial_derivatives_of_u,\n                                single_mixed_partial_index)) +\n                        component_index * num_grid_points,\n                    num_grid_points);\n              }\n\n              lhs +=\n                  (*(inverse_jacobian.begin() +\n                     gsl::at(\n                         gsl::at(jacobian_indices, first_logical_deriv_index),\n                         first_deriv_index))) *\n                  (*(inverse_jacobian.begin() +\n                     gsl::at(\n                         gsl::at(jacobian_indices, second_logical_deriv_index),\n                         second_deriv_index))) *\n                  second_logical_du;\n            }\n          }\n        }\n        // Now add in the terms with the inverse hessian\n        for (size_t d = 0; d < Dim; ++d) {\n          // clang-tidy: const cast is fine since we won't modify the data\n          // and we need it to easily hook into the expression templates.\n          first_logical_du.set_data_ref(\n              // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n              const_cast<ValueType*>(\n                  gsl::at(first_logical_partial_derivatives_of_u, d)) +\n                  component_index * num_grid_points,\n              num_grid_points);\n          lhs += (*(inverse_hessian.begin() +\n                    gsl::at(\n                        gsl::at(gsl::at(hessian_indices, d), first_deriv_index),\n                        second_deriv_index))) *\n                 first_logical_du;\n        }\n      }\n    }\n  }\n}\n}  // namespace partial_derivatives_detail\n\ntemplate <typename DerivativeTags, size_t Dim, typename DerivativeFrame>\nvoid second_partial_derivatives(\n    const gsl::not_null<\n        Variables<db::wrap_tags_in<::Tags::second_deriv, DerivativeTags,\n                                   tmpl::size_t<Dim>, DerivativeFrame>>*>\n        second_partial_derivatives,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Mesh<Dim>& volume_mesh, const size_t number_of_variables,\n    const size_t fd_order,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const InverseHessian<DataVector, Dim, Frame::ElementLogical,\n                         DerivativeFrame>& inverse_hessian) {\n  // Note: Tags::second_deriv ensures the first two indices (partial derivative\n  // indices) of tensors in second_partial_derivatives are symmetric.\n  ASSERT(fd_order == 4, \"Only fd_order == 4 is supported at the moment.\");\n  ASSERT(Dim == 3, \"Only 3 dimensions is supported at the moment.\");\n  ASSERT(second_partial_derivatives->size() ==\n             (Dim - 1) * Dim * volume_vars.size(),\n         \"The second partial derivatives Variables must have size \"\n             << (Dim - 1) * Dim * volume_vars.size()\n             << \" ((Dim-1) * Dim * volume_vars.size()) but has size \"\n             << second_partial_derivatives->size() << \" and \"\n             << second_partial_derivatives->number_of_grid_points()\n             << \" grid points.\");\n\n  // compute the pure and second logical derivatives\n  const size_t second_logical_derivs_internal_buffer_size =\n      (volume_vars.size() +\n       4 * alg::max_element(ghost_cell_vars,\n                            [](const auto& a, const auto& b) {\n                              return a.second.size() < b.second.size();\n                            })\n               ->second.size() +\n       2 * volume_vars.size());\n  DataVector buffer(3 * Dim * volume_vars.size() +\n                    second_logical_derivs_internal_buffer_size);\n\n  // The first_logical_partial_derivs is only needed for inverse_hessian\n  std::array<gsl::span<double>, Dim> first_logical_partial_derivs{};\n  std::array<gsl::span<double>, Dim> pure_second_logical_partial_derivs{};\n  std::array<gsl::span<double>, Dim> mixed_second_logical_partial_derivs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(first_logical_partial_derivs, i) =\n        gsl::make_span(&buffer[i * volume_vars.size()], volume_vars.size());\n  }\n  for (size_t i = Dim; i < 2 * Dim; ++i) {\n    gsl::at(pure_second_logical_partial_derivs, i - Dim) =\n        gsl::make_span(&buffer[i * volume_vars.size()], volume_vars.size());\n  }\n  for (size_t i = 2 * Dim; i < 3 * Dim; ++i) {\n    gsl::at(mixed_second_logical_partial_derivs, i - 2 * Dim) =\n        gsl::make_span(&buffer[i * volume_vars.size()], volume_vars.size());\n  }\n  gsl::span<double> span_buffer =\n      gsl::make_span(&buffer[3 * Dim * volume_vars.size()],\n                     second_logical_derivs_internal_buffer_size);\n\n  // Compute first logical derivatives (used in the inverse-hessian terms)\n  // Potential optimization: since we compute first logical derivatives here,\n  // it may be a good idea to have an option to compute the first inertial\n  // derivatives as well together with the second inertial derivatives.\n  fd::logical_partial_derivatives(make_not_null(&first_logical_partial_derivs),\n                                  volume_vars, ghost_cell_vars, volume_mesh,\n                                  number_of_variables, fd_order);\n\n  detail::second_logical_partial_derivatives_impl(\n      make_not_null(&pure_second_logical_partial_derivs),\n      make_not_null(&mixed_second_logical_partial_derivs), &span_buffer,\n      volume_vars, ghost_cell_vars, volume_mesh, number_of_variables, fd_order);\n\n  std::array<const double*, Dim> first_logical_partial_derivs_ptrs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(first_logical_partial_derivs_ptrs, i) =\n        gsl::at(first_logical_partial_derivs, i).data();\n  }\n\n  std::array<const double*, Dim> pure_second_logical_partial_derivs_ptrs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(pure_second_logical_partial_derivs_ptrs, i) =\n        gsl::at(pure_second_logical_partial_derivs, i).data();\n  }\n\n  std::array<const double*, Dim> mixed_second_logical_partial_derivs_ptrs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(mixed_second_logical_partial_derivs_ptrs, i) =\n        gsl::at(mixed_second_logical_partial_derivs, i).data();\n  }\n\n  partial_derivatives_detail::second_partial_derivatives_impl(\n      second_partial_derivatives, first_logical_partial_derivs_ptrs,\n      pure_second_logical_partial_derivs_ptrs,\n      mixed_second_logical_partial_derivs_ptrs,\n      Variables<DerivativeTags>::number_of_independent_components,\n      inverse_jacobian, inverse_hessian);\n}\n}  // namespace fd\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Unlimited.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/Unlimited.hpp\"\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\n// NOLINTNEXTLINE(modernize-concat-nested-namespaces)\nnamespace fd::reconstruction {\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DEGREE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct<UnlimitedReconstructor<DEGREE(data)>>(             \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_upper_side_of_face_vars,                               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_lower_side_of_face_vars,                               \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Index<DIM(data)>& volume_extents,                                  \\\n      const size_t number_of_variables);\n\nnamespace detail {\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (2, 4, 6, 8))\n}  // namespace detail\n\n#undef INSTANTIATION\n\n#define SIDE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct_neighbor<                                          \\\n      SIDE(data), detail::UnlimitedReconstructor<DEGREE(data)>>(               \\\n      gsl::not_null<DataVector*> face_data, const DataVector& volume_data,     \\\n      const DataVector& neighbor_data, const Index<DIM(data)>& volume_extents, \\\n      const Index<DIM(data)>& ghost_data_extents,                              \\\n      const Direction<DIM(data)>& direction_to_reconstruct);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (2, 4, 6, 8),\n                        (Side::Upper, Side::Lower))\n\n#undef INSTANTIATION\n#undef SIDE\n#undef DEGREE\n#undef DIM\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Unlimited.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace fd::reconstruction {\nnamespace detail {\ntemplate <size_t Degree>\nstruct UnlimitedReconstructor {\n  static_assert(Degree == 2 or Degree == 4 or Degree == 6 or Degree == 8);\n  SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n      const double* const q, const int stride) {\n    if constexpr (Degree == 2) {\n      // quadratic polynomial\n      return {{0.375 * q[-stride] + 0.75 * q[0] - 0.125 * q[stride],\n               -0.125 * q[-stride] + 0.75 * q[0] + 0.375 * q[stride]}};\n    } else if constexpr (Degree == 4) {\n      // quintic polynomial\n      return {{-0.0390625 * q[-2 * stride] + 0.46875 * q[-stride] +\n               0.703125 * q[0] - 0.15625 * q[stride] +\n               0.0234375 * q[2 * stride],\n               0.0234375 * q[-2 * stride] - 0.15625 * q[-stride] +\n               0.703125 * q[0] + 0.46875 * q[stride] -\n               0.0390625 * q[2 * stride]}};\n    } else if constexpr (Degree == 6) {\n      return {{-0.1708984375 * q[stride] + 0.041015625 * q[2 * stride] -\n                   0.0048828125 * q[3 * stride] + 0.5126953125 * q[-stride] -\n                   0.068359375 * q[-2 * stride] +\n                   0.0068359375 * q[-3 * stride] + 0.68359375 * q[0],\n               0.5126953125 * q[stride] - 0.068359375 * q[2 * stride] +\n                   0.0068359375 * q[3 * stride] - 0.1708984375 * q[-stride] +\n                   0.041015625 * q[-2 * stride] -\n                   0.0048828125 * q[-3 * stride] + 0.68359375 * q[0]}};\n    } else if constexpr (Degree == 8) {\n      return {\n          {-0.179443359375 * q[stride] + 0.0538330078125 * q[2 * stride] -\n               0.010986328125 * q[3 * stride] +\n               0.001068115234375 * q[4 * stride] + 0.538330078125 * q[-stride] -\n               0.0897216796875 * q[-2 * stride] +\n               0.015380859375 * q[-3 * stride] -\n               0.001373291015625 * q[-4 * stride] + 0.67291259765625 * q[0],\n           0.538330078125 * q[stride] - 0.0897216796875 * q[2 * stride] +\n               0.015380859375 * q[3 * stride] -\n               0.001373291015625 * q[4 * stride] - 0.179443359375 * q[-stride] +\n               0.0538330078125 * q[-2 * stride] -\n               0.010986328125 * q[-3 * stride] +\n               0.001068115234375 * q[-4 * stride] + 0.67291259765625 * q[0]}};\n    }\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() {\n    return Degree + 1;\n  }\n};\n}  // namespace detail\n\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Performs unlimited reconstruction on the `vars` in each direction.\n *\n * On a 1d mesh with spacing \\f$\\Delta x\\f$ we denote the solution at the\n * \\f$j\\f$th point by \\f$u_j\\f$. The degree 2 reconstruction is\n *\n * \\f{align}{\n * u_{j+1/2} &= -\\frac{1}{8} u_{j-1} + \\frac{3}{4} u_j + \\frac{3}{8} u_{j+1}, \\\\\n * u_{j-1/2} &= \\frac{3}{8} u_{j-1} + \\frac{3}{4} u_j - \\frac{1}{8} u_{j+1}.\n * \\f}\n *\n * The degree 4 reconstruction is\n *\n * \\f{align}{\n *  u_{j-1/2} &= - \\frac{5}{128}u_{j-2} + \\frac{15}{32}u_{j-1} +\n *            \\frac{45}{64}u_{j} -\\frac{5}{32}u_{j+1} + \\frac{3}{128}u_{j+2}, \\\\\n *  u_{j+1/2} &= \\frac{3}{128}u_{j-2} - \\frac{5}{32}u_{j-1} + \\frac{45}{64}u_{j}\n *            + \\frac{15}{32}u_{j+1} - \\frac{5}{128}u_{j+2}.\n * \\f}\n *\n * Degree 6 and 8 reconstruction is also supported.\n */\ntemplate <size_t Degree, size_t Dim>\nvoid unlimited(\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n    reconstructed_upper_side_of_face_vars,\n    const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n    reconstructed_lower_side_of_face_vars,\n    const gsl::span<const double>& volume_vars,\n    const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n    const Index<Dim>& volume_extents, const size_t number_of_variables) {\n  detail::reconstruct<detail::UnlimitedReconstructor<Degree>>(\n      reconstructed_upper_side_of_face_vars,\n      reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n      volume_extents, number_of_variables);\n}\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Wcns5z.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/FiniteDifference/Wcns5z.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Minmod.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.tpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace fd::reconstruction {\n\nnamespace {\n// use type alias for the function pointer return type for brevity\ntemplate <size_t Dim>\nusing pointer_return_type =\n    std::tuple<void (*)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                        gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                        const gsl::span<const double>&,\n                        const DirectionMap<Dim, gsl::span<const double>>&,\n                        const Index<Dim>&, size_t, double, size_t),\n               void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                        const DataVector&, const Index<Dim>&, const Index<Dim>&,\n                        const Direction<Dim>&, const double&, const size_t&),\n               void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                        const DataVector&, const Index<Dim>&, const Index<Dim>&,\n                        const Direction<Dim>&, const double&, const size_t&)>;\n\n// returns functions pointer\ntemplate <size_t NonlinearWeightExponent, typename Reconstructor, size_t Dim>\npointer_return_type<Dim> function_pointer() {\n  return {&wcns5z<NonlinearWeightExponent, Reconstructor, Dim>,\n          &::fd::reconstruction::reconstruct_neighbor<\n              Side::Lower,\n              ::fd::reconstruction::detail::Wcns5zReconstructor<\n                  NonlinearWeightExponent, Reconstructor>,\n              true, Dim>,\n          &::fd::reconstruction::reconstruct_neighbor<\n              Side::Upper,\n              ::fd::reconstruction::detail::Wcns5zReconstructor<\n                  NonlinearWeightExponent, Reconstructor>,\n              true, Dim>};\n}\n\n// wrapper of the `function_pointer()` template, checking\n// FallbackReconstructorType and plugging in the corresponding reconstructor\n// struct\ntemplate <size_t NonlinearWeightExponent, size_t Dim>\npointer_return_type<Dim> wcns5z_function_pointers_helper(\n    const FallbackReconstructorType fallback_recons) {\n  switch (fallback_recons) {\n    case FallbackReconstructorType::Minmod:\n      return function_pointer<NonlinearWeightExponent,\n                                detail::MinmodReconstructor, Dim>();\n    case FallbackReconstructorType::MonotonisedCentral:\n      return function_pointer<NonlinearWeightExponent,\n                                detail::MonotonisedCentralReconstructor, Dim>();\n    case FallbackReconstructorType::None:\n      return function_pointer<NonlinearWeightExponent, void, Dim>();\n    default:  // LCOV_EXCL_LINE\n              // LCOV_EXCL_START\n      ERROR(\"Unsupported type of fallback reconstruction : \" << fallback_recons\n                                                             << \"\\n\");\n      // LCOV_EXCL_STOP\n  }\n}\n}  // namespace\n\ntemplate <size_t Dim>\nstd::tuple<void (*)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                    gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                    const gsl::span<const double>&,\n                    const DirectionMap<Dim, gsl::span<const double>>&,\n                    const Index<Dim>&, size_t, double, size_t),\n           void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                    const DataVector&, const Index<Dim>&, const Index<Dim>&,\n                    const Direction<Dim>&, const double&, const size_t&),\n           void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                    const DataVector&, const Index<Dim>&, const Index<Dim>&,\n                    const Direction<Dim>&, const double&, const size_t&)>\nwcns5z_function_pointers(const size_t nonlinear_weight_exponent,\n                         const FallbackReconstructorType fallback_recons) {\n  switch (nonlinear_weight_exponent) {\n    case 1:\n      return wcns5z_function_pointers_helper<1, Dim>(fallback_recons);\n    case 2:\n      return wcns5z_function_pointers_helper<2, Dim>(fallback_recons);\n    default:\n      ERROR(\"Nonlinear weight exponent should be 1 or 2 but is : \"\n            << nonlinear_weight_exponent << \"\\n\");\n  };\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                           \\\n  template std::tuple<                                                   \\\n      void (*)(gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>, \\\n               gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>, \\\n               const gsl::span<const double>&,                           \\\n               const DirectionMap<DIM(data), gsl::span<const double>>&,  \\\n               const Index<DIM(data)>&, size_t, double, size_t),         \\\n      void (*)(gsl::not_null<DataVector*>, const DataVector&,            \\\n               const DataVector&, const Index<DIM(data)>&,               \\\n               const Index<DIM(data)>&, const Direction<DIM(data)>&,     \\\n               const double&, const size_t&),                            \\\n      void (*)(gsl::not_null<DataVector*>, const DataVector&,            \\\n               const DataVector&, const Index<DIM(data)>&,               \\\n               const Index<DIM(data)>&, const Direction<DIM(data)>&,     \\\n               const double&, const size_t&)>                            \\\n  wcns5z_function_pointers(const size_t nonlinear_weight_exponent,       \\\n                           const FallbackReconstructorType fallback_recons);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define NONLINEAR_WEIGHT_EXPONENT(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FALLBACK_RECONSTRUCTOR(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct<Wcns5zReconstructor<                               \\\n      NONLINEAR_WEIGHT_EXPONENT(data), FALLBACK_RECONSTRUCTOR(data)>>(         \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_upper_side_of_face_vars,                               \\\n      gsl::not_null<std::array<gsl::span<double>, DIM(data)>*>                 \\\n          reconstructed_lower_side_of_face_vars,                               \\\n      const gsl::span<const double>& volume_vars,                              \\\n      const DirectionMap<DIM(data), gsl::span<const double>>& ghost_cell_vars, \\\n      const Index<DIM(data)>& volume_extents,                                  \\\n      const size_t number_of_variables, const double& epsilon,                 \\\n      const size_t& max_number_of_extrema);\n\nnamespace detail {\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (1, 2),\n                        (void, MinmodReconstructor,\n                         MonotonisedCentralReconstructor))\n}  // namespace detail\n\n#undef INSTANTIATION\n\n#define SIDE(data) BOOST_PP_TUPLE_ELEM(3, data)\n#define EXTERIOR_CELL(data) BOOST_PP_TUPLE_ELEM(4, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void reconstruct_neighbor<                                          \\\n      SIDE(data),                                                              \\\n      detail::Wcns5zReconstructor<NONLINEAR_WEIGHT_EXPONENT(data),             \\\n                                  FALLBACK_RECONSTRUCTOR(data)>,               \\\n      EXTERIOR_CELL(data)>(                                                    \\\n      gsl::not_null<DataVector*> face_data, const DataVector& volume_data,     \\\n      const DataVector& neighbor_data, const Index<DIM(data)>& volume_extents, \\\n      const Index<DIM(data)>& ghost_data_extents,                              \\\n      const Direction<DIM(data)>& direction_to_reconstruct,                    \\\n      const double& epsilon, const size_t& max_number_of_extrema);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3), (1, 2),\n                        (void, detail::MinmodReconstructor,\n                         detail::MonotonisedCentralReconstructor),\n                        (Side::Upper, Side::Lower), (true, false))\n\n#undef INSTANTIATION\n#undef SIDE\n#undef NONLINEAR_WEIGHT_EXPONENT\n#undef FALLBACK_RECONSTRUCTOR\n#undef DIM\n\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/FiniteDifference/Wcns5z.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <tuple>\n\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Direction;\ntemplate <size_t Dim, typename T>\nclass DirectionMap;\ntemplate <size_t Dim>\nclass Index;\n/// \\endcond\n\nnamespace fd::reconstruction {\nnamespace detail {\n// pointwise reconstruction routine for the original Wcns5z scheme\ntemplate <size_t NonlinearWeightExponent>\nstruct Wcns5zWork {\n  SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n      const double* const q, const int stride, const double epsilon) {\n    ASSERT(epsilon > 0.0,\n           \"epsilon must be greater than zero but is \" << epsilon);\n\n    using std::abs;\n\n    const std::array beta{\n        1.0833333333333333 * square(q[-2 * stride] - 2.0 * q[-stride] + q[0]) +\n            0.25 * square(q[-2 * stride] - 4.0 * q[-stride] + 3.0 * q[0]),\n        1.0833333333333333 * square(q[-stride] - 2.0 * q[0] + q[stride]) +\n            0.25 * square(q[stride] - q[-stride]),\n        1.0833333333333333 * square(q[2 * stride] - 2.0 * q[stride] + q[0]) +\n            0.25 * square(q[2 * stride] - 4.0 * q[stride] + 3.0 * q[0])};\n\n    const double tau5{abs(beta[2] - beta[0])};\n\n    const std::array epsilon_k{\n        epsilon * (1.0 + abs(q[0]) + abs(q[-stride]) + abs(q[-2 * stride])),\n        epsilon * (1.0 + abs(q[0]) + abs(q[-stride]) + abs(q[stride])),\n        epsilon * (1.0 + abs(q[0]) + abs(q[stride]) + abs(q[2 * stride]))};\n\n    const std::array nw_buffer{\n        1.0 + pow<NonlinearWeightExponent>(tau5 / (beta[0] + epsilon_k[0])),\n        1.0 + pow<NonlinearWeightExponent>(tau5 / (beta[1] + epsilon_k[1])),\n        1.0 + pow<NonlinearWeightExponent>(tau5 / (beta[2] + epsilon_k[2]))};\n\n    // nonlinear weights for upper and lower reconstructions. The factor 1/16\n    // for `alpha`s is omitted here since it is eventually canceled out by\n    // denominator when evaluating modified nonlinear weight `omega`s (see the\n    // documentation of the `wcns5z()` function below).\n    const std::array alpha_upper{nw_buffer[0], 10.0 * nw_buffer[1],\n                                 5.0 * nw_buffer[2]};\n    const std::array alpha_lower{nw_buffer[2], 10.0 * nw_buffer[1],\n                                 5.0 * nw_buffer[0]};\n    const double alpha_norm_upper =\n        alpha_upper[0] + alpha_upper[1] + alpha_upper[2];\n    const double alpha_norm_lower =\n        alpha_lower[0] + alpha_lower[1] + alpha_lower[2];\n\n    // reconstruction stencils\n    const std::array recons_stencils_upper{\n        0.375 * q[-2 * stride] - 1.25 * q[-stride] + 1.875 * q[0],\n        -0.125 * q[-stride] + 0.75 * q[0] + 0.375 * q[stride],\n        0.375 * q[0] + 0.75 * q[stride] - 0.125 * q[2 * stride]};\n    const std::array recons_stencils_lower{\n        0.375 * q[2 * stride] - 1.25 * q[stride] + 1.875 * q[0],\n        -0.125 * q[stride] + 0.75 * q[0] + 0.375 * q[-stride],\n        0.375 * q[0] + 0.75 * q[-stride] - 0.125 * q[-2 * stride]};\n\n    // reconstructed solutions\n    return {{(alpha_lower[0] * recons_stencils_lower[0] +\n              alpha_lower[1] * recons_stencils_lower[1] +\n              alpha_lower[2] * recons_stencils_lower[2]) /\n                 alpha_norm_lower,\n             (alpha_upper[0] * recons_stencils_upper[0] +\n              alpha_upper[1] * recons_stencils_upper[1] +\n              alpha_upper[2] * recons_stencils_upper[2]) /\n                 alpha_norm_upper}};\n  }\n};\n\ntemplate <size_t NonlinearWeightExponent, class FallbackReconstructor>\nstruct Wcns5zReconstructor {\n  SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n      const double* const q, const int stride, const double epsilon,\n      const size_t max_number_of_extrema) {\n    // count the number of extrema in the given FD stencil\n    size_t n_extrema{0};\n    for (int i = -1; i < 2; ++i) {\n      // check if q[i * stride] is local maximum\n      n_extrema += (q[i * stride] > q[(i - 1) * stride]) and\n                   (q[i * stride] > q[(i + 1) * stride]);\n      // check if q[i * stride] is local minimum\n      n_extrema += (q[i * stride] < q[(i - 1) * stride]) and\n                   (q[i * stride] < q[(i + 1) * stride]);\n    }\n\n    // if `n_extrema` is equal or smaller than a specified number, use the\n    // original Wcns5z reconstruction\n    if (n_extrema < max_number_of_extrema + 1) {\n      return Wcns5zWork<NonlinearWeightExponent>::pointwise(q, stride, epsilon);\n    } else {\n      // otherwise use a fallback reconstruction method\n      return FallbackReconstructor::pointwise(q, stride);\n    }\n  }\n\n  SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() { return 5; }\n};\n\ntemplate <size_t NonlinearWeightExponent>\nstruct Wcns5zReconstructor<NonlinearWeightExponent, void> {\n  SPECTRE_ALWAYS_INLINE static std::array<double, 2> pointwise(\n      const double* const q, const int stride, const double epsilon,\n      const size_t /*max_number_of_extrema*/) {\n    return Wcns5zWork<NonlinearWeightExponent>::pointwise(q, stride, epsilon);\n  }\n  SPECTRE_ALWAYS_INLINE static constexpr size_t stencil_width() { return 5; }\n};\n\n}  // namespace detail\n\n/*!\n * \\ingroup FiniteDifferenceGroup\n * \\brief Performs fifth order weighted compact nonlinear scheme reconstruction\n * based on the WENO-Z method (WCNS-5Z). Adaptive fallback combined with\n * an auxiliary reconstruction method (e.g. monotonised central) is also\n * supported.\n *\n * Using the WENO oscillation indicators given by \\cite Jiang1996\n *\n * \\f{align}\n *  \\beta_0 & = \\frac{13}{12} (q_{i-2} - 2q_{i-1} + q_{i})^2\n *              + \\frac{1}{4} (q_{i-2} - 4q_{i-1} + 3q_{i})^2  \\\\\n *  \\beta_1 & = \\frac{13}{12} (q_{i-1} - 2q_{i} + q_{i+1})^2\n *              + \\frac{1}{4} (q_{i+1} - q_{i-1})^2            \\\\\n *  \\beta_2 & = \\frac{13}{12} (q_{i} - 2q_{i+1} + q_{i+2})^2\n *              + \\frac{1}{4} (3q_{i} - 4q_{i+1} + q_{i+2})^2 ,\n * \\f}\n *\n * compute the modified nonlinear weights (cf. WENO-Z scheme: see Eq. 42 of\n * \\cite Borges20083191)\n *\n * \\f{align}\n *  \\omega_k^z = \\frac{\\alpha_k^z}{\\sum_{l=0}^z \\alpha_l^z}, \\quad\n *  \\alpha_k^z = c_k \\left(1 + \\left(\n *                       \\frac{\\tau_5}{\\beta_k + \\epsilon_k} \\right)^q \\right),\n *  \\quad k = 0, 1, 2\n * \\f}\n *\n * where \\f$\\epsilon_k\\f$ is a factor used to avoid division by zero and is set\n * to\n *\n * \\f{align}\n *  \\epsilon_k = \\varepsilon (1 + |q_{i+k}| + |q_{i+k-1}| + |q_{i+k-2}|),\n *  \\quad k = 0, 1, 2,\n * \\f}\n *\n * linear weights \\f$c_k\\f$ are adopted from \\cite Nonomura2013 (see Table 17\n * of it)\n *\n * \\f{align}\n *  c_0 = 1/16, \\quad c_1 = 10/16, \\quad c_2 = 5/16,\n * \\f}\n *\n * and \\f$\\tau_5 \\equiv |\\beta_2-\\beta_0|\\f$.\n *\n * Reconstruction stencils are given by Lagrange interpolations (e.g. see Eq.\n * 29d - 29f of \\cite Brehm2015)\n * \\f{align}\n * q_{i+1/2}^0 &= \\frac{3}{8}q_{i-2} -\\frac{5}{4}q_{i-1} +\\frac{15}{8}q_{i} \\\\\n * q_{i+1/2}^1 &= -\\frac{1}{8}q_{i-1} +\\frac{3}{4}q_{i} +\\frac{3}{8}q_{i+1} \\\\\n * q_{i+1/2}^2 &= \\frac{3}{8}q_{i} +\\frac{3}{4}q_{i+1} -\\frac{1}{8}q_{i+2}\n * \\f}\n *\n * and the final reconstructed solution is given by\n * \\f{align}\n *   q_{i+1/2} = \\sum_{k=0}^2 \\omega_k q_{i+1/2}^k .\n * \\f}\n *\n * The nonlinear weights exponent \\f$q (=1 \\text{ or } 2)\\f$ and the small\n * factor \\f$\\varepsilon\\f$ can be chosen via an input file.\n *\n * If the template parameter `FallbackReconstructor` is set to one of\n * the FD reconstructor structs of which names are listed in\n * `fd::reconstruction::FallbackReconstructorType`, adaptive reconstruction\n * is performed as follows. For each finite difference stencils, first check how\n * many extrema are in the stencil. If the number of local extrema is less than\n * or equal to a non-negative integer `max_number_of_extrema` which is given as\n * an input parameter, perform the WCNS-5Z reconstruction; otherwise switch to\n * the given `FallbackReconstructor` for performing reconstruction. If\n * `FallbackReconstructor` is set to `void`, the adaptive method is disabled and\n * WCNS-5Z is always used.\n *\n */\ntemplate <size_t NonlinearWeightExponent, class FallbackReconstructor,\n          size_t Dim>\nvoid wcns5z(const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n                reconstructed_upper_side_of_face_vars,\n            const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n                reconstructed_lower_side_of_face_vars,\n            const gsl::span<const double>& volume_vars,\n            const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n            const Index<Dim>& volume_extents, const size_t number_of_variables,\n            const double epsilon, const size_t max_number_of_extrema) {\n  detail::reconstruct<detail::Wcns5zReconstructor<NonlinearWeightExponent,\n                                                  FallbackReconstructor>>(\n      reconstructed_upper_side_of_face_vars,\n      reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n      volume_extents, number_of_variables, epsilon, max_number_of_extrema);\n}\n\n/*!\n * \\brief Returns function pointers to the `wcns5z` function, lower neighbor\n * reconstruction, and upper neighbor reconstruction.\n *\n */\ntemplate <size_t Dim>\nauto wcns5z_function_pointers(size_t nonlinear_weight_exponent,\n                              FallbackReconstructorType fallback_recons)\n    -> std::tuple<\n        void (*)(gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                 gsl::not_null<std::array<gsl::span<double>, Dim>*>,\n                 const gsl::span<const double>&,\n                 const DirectionMap<Dim, gsl::span<const double>>&,\n                 const Index<Dim>&, size_t, double, size_t),\n        void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                 const DataVector&, const Index<Dim>&, const Index<Dim>&,\n                 const Direction<Dim>&, const double&, const size_t&),\n        void (*)(gsl::not_null<DataVector*>, const DataVector&,\n                 const DataVector&, const Index<Dim>&, const Index<Dim>&,\n                 const Direction<Dim>&, const double&, const size_t&)>;\n\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "src/NumericalAlgorithms/Integration/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Integration)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GslQuadAdaptive.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GslQuadAdaptive.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ErrorHandling\n  GSL::gsl\n  Utilities\n  )\n\n"
  },
  {
    "path": "src/NumericalAlgorithms/Integration/GslQuadAdaptive.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Integration/GslQuadAdaptive.hpp\"\n\n#include <cstddef>\n#include <gsl/gsl_errno.h>\n#include <gsl/gsl_integration.h>\n#include <memory>\n#include <pup.h>\n\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace integration::detail {\n\nGslQuadAdaptiveImpl::GslQuadAdaptiveImpl(const size_t max_intervals)\n    : max_intervals_(max_intervals) {\n  initialize();\n}\n\nvoid GslQuadAdaptiveImpl::pup(PUP::er& p) {\n  p | max_intervals_;\n  if (p.isUnpacking()) {\n    initialize();\n  }\n}\n\nvoid GslQuadAdaptiveImpl::GslIntegrationWorkspaceDeleter::operator()(\n    gsl_integration_workspace* workspace) const {\n  gsl_integration_workspace_free(workspace);\n}\n\nvoid GslQuadAdaptiveImpl::initialize() {\n  workspace_ = std::unique_ptr<gsl_integration_workspace,\n                               GslIntegrationWorkspaceDeleter>{\n      gsl_integration_workspace_alloc(max_intervals_)};\n}\n\nvoid check_status_code(const int status_code) {\n  if (status_code != 0) {\n    throw convergence_error(MakeString{}\n                            << \"Integration failed with GSL error: \"\n                            << gsl_strerror(status_code));\n  }\n}\n\nvoid disable_gsl_error_handling() { gsl_set_error_handler_off(); }\n\n}  // namespace integration::detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/Integration/GslQuadAdaptive.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <gsl/gsl_integration.h>\n#include <limits>\n#include <memory>\n#include <vector>\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup NumericalAlgorithmsGroup\n/// Numerical integration algorithms\nnamespace integration {\n\nnamespace detail {\n\n// The GSL functions require the integrand to have this particular function\n// signature. In particular, any extra parameters to the functions must be\n// passed as a void* and re-interpreted appropriately.\ntemplate <typename IntegrandType>\ndouble integrand(const double x, void* const params) {\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n  auto* const function = reinterpret_cast<IntegrandType*>(params);\n  return (*function)(x);\n}\n\nclass GslQuadAdaptiveImpl {\n public:\n  explicit GslQuadAdaptiveImpl(size_t max_intervals);\n\n  GslQuadAdaptiveImpl() = default;\n  GslQuadAdaptiveImpl(const GslQuadAdaptiveImpl&) = delete;\n  GslQuadAdaptiveImpl& operator=(const GslQuadAdaptiveImpl&) = delete;\n  GslQuadAdaptiveImpl(GslQuadAdaptiveImpl&&) = default;\n  GslQuadAdaptiveImpl& operator=(GslQuadAdaptiveImpl&& rhs) = default;\n  ~GslQuadAdaptiveImpl() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n protected:\n  template <typename IntegrandType>\n  gsl_function make_gsl_integrand(IntegrandType& integrand) const {\n    gsl_function gsl_integrand;\n    gsl_integrand.function = &detail::integrand<IntegrandType>;\n    gsl_integrand.params = &integrand;\n    return gsl_integrand;\n  }\n\n  struct GslIntegrationWorkspaceDeleter {\n    void operator()(gsl_integration_workspace* workspace) const;\n  };\n\n  size_t max_intervals_ = 0;\n  std::unique_ptr<gsl_integration_workspace, GslIntegrationWorkspaceDeleter>\n      workspace_{};\n\n private:\n  void initialize();\n};\n\nvoid check_status_code(int status_code);\n\nvoid disable_gsl_error_handling();\n\n}  // namespace detail\n\n/// Each type specifies which algorithm from GSL should be used. It should be\n/// chosen according to the problem.\nenum class GslIntegralType {\n  /// gsl_integration_qag()\n  StandardGaussKronrod,\n  /// gsl_integration_qags()\n  IntegrableSingularitiesPresent,\n  /// gsl_integration_qagp()\n  IntegrableSingularitiesKnown,\n  /// gsl_integration_qagi()\n  InfiniteInterval,\n  /// gsl_integration_qagiu()\n  UpperBoundaryInfinite,\n  /// gsl_integration_qagil()\n  LowerBoundaryInfinite\n};\n\n/*!\n * \\brief A wrapper around the GSL adaptive integration procedures\n *\n * All templates take upper bounds to the absolute and relative error;\n * `tolerance_abs` and `tolerance_rel(default = 0.0)`, respectively. To compute\n * to a specified absolute error, set `tolerance_rel` to zero. To compute to a\n * specified relative error, set `tolerance_abs` to zero. For details on the\n * algorithm see the GSL documentation on `gsl_integration`.\n *\n * Here is an example how to use this class. For the function:\n *\n * \\snippet Test_GslQuadAdaptive.cpp integrated_function\n *\n * the integration should look like:\n *\n * \\snippet Test_GslQuadAdaptive.cpp integration_example\n */\ntemplate <GslIntegralType TheIntegralType>\nclass GslQuadAdaptive;\n\n/*!\n * \\brief Use Gauss-Kronrod rule to integrate a 1D-function\n *\n * The algorithm for \"StandardGaussKronrod\" uses the QAG algorithm to employ an\n * adaptive Gauss-Kronrod n-points integration rule. Its function takes a\n * `lower_boundary` and `upper_boundary` to the integration region, an upper\n * bound for the absolute error `tolerance_abs` and a `key`. The latter is an\n * index to the array [15, 21, 31, 41, 51, 61], where each element denotes how\n * many function evaluations take place in each subinterval. A higher-order rule\n * serves better for smooth functions, whereas a lower-order rule saves time for\n * functions with local difficulties, such as discontinuities.\n */\ntemplate <>\nclass GslQuadAdaptive<GslIntegralType::StandardGaussKronrod>\n    : public detail::GslQuadAdaptiveImpl {\n public:\n  using detail::GslQuadAdaptiveImpl::GslQuadAdaptiveImpl;\n  template <typename IntegrandType>\n  double operator()(IntegrandType&& integrand, const double lower_boundary,\n                    const double upper_boundary, const double tolerance_abs,\n                    const int key, const double tolerance_rel = 0.) const {\n    double result = std::numeric_limits<double>::signaling_NaN();\n    double error = std::numeric_limits<double>::signaling_NaN();\n    auto gsl_integrand = make_gsl_integrand(integrand);\n    detail::disable_gsl_error_handling();\n    const int status_code =\n        gsl_integration_qag(&gsl_integrand, lower_boundary, upper_boundary,\n                            tolerance_abs, tolerance_rel, this->max_intervals_,\n                            key, this->workspace_.get(), &result, &error);\n    detail::check_status_code(status_code);\n    return result;\n  }\n};\n\n/*!\n * \\brief Integrates a 1D-function with singularities\n *\n * The algorithm for \"IntegrableSingularitiesPresent\" concentrates new,\n * increasingly smaller subintervals around an unknown singularity and makes\n * successive approximations to the integral which should converge towards a\n * limit. The integration region is defined by `lower_boundary` and\n * `upper_boundary`.\n */\ntemplate <>\nclass GslQuadAdaptive<GslIntegralType::IntegrableSingularitiesPresent>\n    : public detail::GslQuadAdaptiveImpl {\n public:\n  using detail::GslQuadAdaptiveImpl::GslQuadAdaptiveImpl;\n  template <typename IntegrandType>\n  double operator()(IntegrandType&& integrand, const double lower_boundary,\n                    const double upper_boundary, const double tolerance_abs,\n                    const double tolerance_rel = 0.) const {\n    double result = std::numeric_limits<double>::signaling_NaN();\n    double error = std::numeric_limits<double>::signaling_NaN();\n    auto gsl_integrand = make_gsl_integrand(integrand);\n    detail::disable_gsl_error_handling();\n    const int status_code =\n        gsl_integration_qags(&gsl_integrand, lower_boundary, upper_boundary,\n                             tolerance_abs, tolerance_rel, this->max_intervals_,\n                             this->workspace_.get(), &result, &error);\n    detail::check_status_code(status_code);\n    return result;\n  }\n};\n\n/*!\n * \\brief Integrates a 1D-function where singularities are known\n *\n * The algorithm for \"IntegrableSingularitiesKnown\" uses user-defined\n * subintervals given by a vector of doubles `points`, where each element\n * denotes an interval boundary.\n */\ntemplate <>\nclass GslQuadAdaptive<GslIntegralType::IntegrableSingularitiesKnown>\n    : public detail::GslQuadAdaptiveImpl {\n public:\n  using detail::GslQuadAdaptiveImpl::GslQuadAdaptiveImpl;\n  template <typename IntegrandType>\n  double operator()(IntegrandType&& integrand,\n                    const std::vector<double>& points,\n                    const double tolerance_abs,\n                    const double tolerance_rel = 0.) const {\n    double result = std::numeric_limits<double>::signaling_NaN();\n    double error = std::numeric_limits<double>::signaling_NaN();\n    auto gsl_integrand = make_gsl_integrand(integrand);\n    detail::disable_gsl_error_handling();\n    // The const_cast is necessary because GSL has a weird interface where\n    // the number of singularities does not change, but it doesn't take\n    // the argument as a `const double*`. However, the first thing\n    // `gsl_integration_qagp` does internally is forward to another function\n    // that does take `points` by `const double*`. If `gsl_integration_qagp`\n    // were to change the size of `points` this code would be severely broken\n    // because `std::vector` allocates with `new`, while GSL would likely use\n    // `malloc` (or some other C allocator). Mixing (de)allocators in such a way\n    // is undefined behavior.\n    const int status_code = gsl_integration_qagp(\n        &gsl_integrand, const_cast<double*>(points.data()),  // NOLINT\n        points.size(), tolerance_abs, tolerance_rel, this->max_intervals_,\n        this->workspace_.get(), &result, &error);\n    detail::check_status_code(status_code);\n    return result;\n  }\n};\n\n/*!\n * \\brief Integrates a 1D-function over the interval \\f$ (-\\infty, +\\infty) \\f$\n *\n * The algorithm for \"InfiniteInterval\" uses the semi-open interval\n * \\f$ (0, 1] \\f$ to map to an infinite interval \\f$ (-\\infty, +\\infty) \\f$. Its\n * function takes no parameters other than limits `tolerance_abs` and optional\n * `tolerance_rel` for the absolute error and the relative error.\n */\ntemplate <>\nclass GslQuadAdaptive<GslIntegralType::InfiniteInterval>\n    : public detail::GslQuadAdaptiveImpl {\n public:\n  using detail::GslQuadAdaptiveImpl::GslQuadAdaptiveImpl;\n  template <typename IntegrandType>\n  double operator()(IntegrandType&& integrand, const double tolerance_abs,\n                    const double tolerance_rel = 0.) const {\n    double result = std::numeric_limits<double>::signaling_NaN();\n    double error = std::numeric_limits<double>::signaling_NaN();\n    auto gsl_integrand = make_gsl_integrand(integrand);\n    detail::disable_gsl_error_handling();\n    const int status_code = gsl_integration_qagi(\n        &gsl_integrand, tolerance_abs, tolerance_rel, this->max_intervals_,\n        this->workspace_.get(), &result, &error);\n    detail::check_status_code(status_code);\n    return result;\n  }\n};\n\n/*!\n * \\brief Integrates a 1D-function over the interval \\f$ [a, +\\infty) \\f$\n *\n * The algorithm for \"UpperBoundaryInfinite\" maps the semi-open interval\n * \\f$ (0, 1] \\f$ to a semi-infinite interval \\f$ [a, +\\infty) \\f$, where\n * \\f$ a \\f$ is given by `lower_boundary`.\n */\ntemplate <>\nclass GslQuadAdaptive<GslIntegralType::UpperBoundaryInfinite>\n    : public detail::GslQuadAdaptiveImpl {\n public:\n  using detail::GslQuadAdaptiveImpl::GslQuadAdaptiveImpl;\n  template <typename IntegrandType>\n  double operator()(IntegrandType&& integrand, const double lower_boundary,\n                    const double tolerance_abs,\n                    const double tolerance_rel = 0.) const {\n    double result = std::numeric_limits<double>::signaling_NaN();\n    double error = std::numeric_limits<double>::signaling_NaN();\n    auto gsl_integrand = make_gsl_integrand(integrand);\n    detail::disable_gsl_error_handling();\n    const int status_code = gsl_integration_qagiu(\n        &gsl_integrand, lower_boundary, tolerance_abs, tolerance_rel,\n        this->max_intervals_, this->workspace_.get(), &result, &error);\n    detail::check_status_code(status_code);\n    return result;\n  }\n};\n\n/*!\n * \\brief Integrates a 1D-function over the interval \\f$ (-\\infty, b] \\f$\n *\n * The algorithm for \"LowerBoundaryInfinite\" maps the semi-open interval\n * \\f$ (0, 1] \\f$ to a semi-infinite interval \\f$ (-\\infty, b] \\f$, where\n * \\f$ b \\f$ is given by `upper_boundary`.\n */\ntemplate <>\nclass GslQuadAdaptive<GslIntegralType::LowerBoundaryInfinite>\n    : public detail::GslQuadAdaptiveImpl {\n public:\n  using detail::GslQuadAdaptiveImpl::GslQuadAdaptiveImpl;\n  template <typename IntegrandType>\n  double operator()(IntegrandType&& integrand, const double upper_boundary,\n                    const double tolerance_abs,\n                    const double tolerance_rel = 0.) const {\n    double result = std::numeric_limits<double>::signaling_NaN();\n    double error = std::numeric_limits<double>::signaling_NaN();\n    auto gsl_integrand = make_gsl_integrand(integrand);\n    detail::disable_gsl_error_handling();\n    const int status_code = gsl_integration_qagil(\n        &gsl_integrand, upper_boundary, tolerance_abs, tolerance_rel,\n        this->max_intervals_, this->workspace_.get(), &result, &error);\n    detail::check_status_code(status_code);\n    return result;\n  }\n};\n}  // namespace integration\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/BarycentricRational.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/BarycentricRational.hpp\"\n\n#include <algorithm>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace intrp {\nBarycentricRational::BarycentricRational(std::vector<double> x_values,\n                                         std::vector<double> y_values,\n                                         const size_t order)\n    : x_values_(std::move(x_values)),\n      y_values_(std::move(y_values)),\n      weights_(x_values_.size(), 0.0),\n      order_(static_cast<ssize_t>(order)) {\n  ASSERT(x_values_.size() == y_values_.size(),\n         \"The x-value and y-value vectors must be of the same length, but \"\n         \"receives x-value of size: \"\n             << x_values_.size()\n             << \" and y-value of size: \" << y_values_.size());\n  ASSERT(x_values_.size() >= order,\n         \" The size of the x-value and y-value vectors must be at least the \"\n         \"requested order of interpolation. The requested order is: \"\n             << order_\n             << \" while the size of the vectors is: \" << x_values_.size());\n  // Use ssize_t because we do signed arithmetic\n  const auto size = static_cast<ssize_t>(x_values_.size());\n  // for each weights[k]...\n  for (ssize_t k = 0; k < size; ++k) {\n    const ssize_t i_lower_bound = std::max(k - order_, ssize_t{0});\n    const ssize_t i_upper_bound = std::min(size - order_ - 1, k);\n    // perform sum w_k = \\sum_{i=k-order, 0 \\le i < N - order}^{k} (-1)^i where\n    // N is the size of the input vectors, order the interpolation order, k is\n    // the index of the weights (which is in [0, order_]).\n    for (ssize_t i = i_lower_bound; i <= i_upper_bound; ++i) {\n      const ssize_t j_max = std::min(i + order_, size - 1);\n      double inv_product = 1.0;\n      // perform product: \\prod_{j=i, j!=k}^{i+order} 1 / (x_k - x_j)\n      for (ssize_t j = i; j <= j_max; ++j) {\n        if (UNLIKELY(j == k)) {\n          continue;\n        }\n        inv_product *= (x_values_[static_cast<size_t>(k)] -\n                        x_values_[static_cast<size_t>(j)]);\n      }\n      if ((i & static_cast<ssize_t>(1)) == 1) {\n        // if i is odd subtract\n        weights_[static_cast<size_t>(k)] -= 1.0 / inv_product;\n      } else {\n        // if i is even add\n        weights_[static_cast<size_t>(k)] += 1.0 / inv_product;\n      }\n    }\n  }\n}\n\ndouble BarycentricRational::operator()(const double x_to_interp_to) const {\n  double numerator = 0.0;\n  double denominator = 0.0;\n  const size_t size = x_values_.size();\n  for (size_t i = 0; i < size; ++i) {\n    if (x_to_interp_to == x_values_[i]) {\n      return y_values_[i];\n    }\n    const double temp = weights_[i] / (x_to_interp_to - x_values_[i]);\n    numerator += temp * y_values_[i];\n    denominator += temp;\n  }\n  return numerator / denominator;\n}\n\nsize_t BarycentricRational::order() const {\n  return static_cast<size_t>(order_);\n}\n\nvoid BarycentricRational::pup(PUP::er& p) {\n  p | x_values_;\n  p | y_values_;\n  p | weights_;\n  p | order_;\n}\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/BarycentricRational.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unistd.h>\n#include <vector>\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace intrp {\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief A barycentric rational interpolation class\n *\n * The class builds a barycentric rational interpolant of a specified order\n * using the `x_values` and `y_values` passed into the constructor.\n * Barycentric interpolation requires \\f$3N\\f$ storage, and costs\n * \\f$\\mathcal{O}(Nd)\\f$ to construct, where \\f$N\\f$ is the size of the x- and\n * y-value vectors and \\f$d\\f$ is the order of the interpolant. The evaluation\n * cost is \\f$\\mathcal{O}(N)\\f$ compared to \\f$\\mathcal{O}(d)\\f$ of a spline\n * method, but constructing the barycentric interpolant does not require any\n * derivatives of the function to be known.\n *\n * The interpolation function is\n *\n * \\f[\n *   \\mathcal{I}(x)=\\frac{\\sum_{i=0}^{N-1}w_i y_i /\n *   (x-x_i)}{\\sum_{i=0}^{N-1}w_i/(x-x_i)}\n * \\f]\n *\n * where \\f$w_i\\f$ are the weights. The weights are computed using\n *\n * \\f[\n *   w_k=\\sum_{i=k-d\\\\0\\le i < N-d}^{k}(-1)^{i}\n *       \\prod_{j=i\\\\j\\ne k}^{i+d}\\frac{1}{x_k-x_j} \\f]\n *\n * \\requires `x_values.size() == y_values.size()` and\n * `x_values_.size() >= order`\n */\nclass BarycentricRational {\n public:\n  BarycentricRational() = default;\n  BarycentricRational(std::vector<double> x_values,\n                      std::vector<double> y_values, size_t order);\n\n  double operator()(double x_to_interp_to) const;\n\n  const std::vector<double>& x_values() const { return x_values_; }\n\n  const std::vector<double>& y_values() const { return y_values_; }\n\n  size_t order() const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  std::vector<double> x_values_;\n  std::vector<double> y_values_;\n  std::vector<double> weights_;\n  ssize_t order_{0};\n};\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n\n#include <boost/math/interpolators/barycentric_rational.hpp>\n#include <complex>\n#include <cstddef>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n// Doesn't exist in boost before 1.77, but referenced below.\nnamespace boost::math::interpolators {}\n\nnamespace intrp {\n\nBarycentricRationalSpanInterpolator::BarycentricRationalSpanInterpolator(\n    size_t min_order, size_t max_order)\n    : min_order_{min_order}, max_order_{max_order} {\n  ASSERT(min_order <= max_order,\n         \"The minimum order for the Barycentric rational interpolator must be \"\n         \"less than the maximum order.\");\n}\n\ndouble BarycentricRationalSpanInterpolator::interpolate(\n    const gsl::span<const double>& source_points,\n    const gsl::span<const double>& values, const double target_point) const {\n  if (UNLIKELY(source_points.size() < min_order_ + 1)) {\n    ERROR(\"provided independent values for interpolation too small.\");\n  }\n  // Boost moved barycentric_rational from boost::math to\n  // boost::math::interpolators in version 1.77.\n  // NOLINTNEXTLINE(google-build-using-namespace)\n  using namespace boost::math;\n  // NOLINTNEXTLINE(google-build-using-namespace)\n  using namespace boost::math::interpolators;\n  barycentric_rational<double> interpolant(\n      source_points.data(), values.data(), source_points.size(),\n      std::min(source_points.size() - 1, max_order_));\n  return interpolant(target_point);\n}\n\nPUP::able::PUP_ID intrp::BarycentricRationalSpanInterpolator::my_PUP_ID = 0;\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace intrp {\n\n/// \\brief Performs a barycentric interpolation with an order in a range fixed\n/// at construction; this class can be chosen via the options factory mechanism\n/// as a possible `SpanInterpolator`.\n///\n/// \\details This will call a barycentric interpolation on a fixed minimum\n/// length, so that buffers that adjust length based on\n/// `required_points_before_and_after()` can be forced to use an interpolator of\n/// a target order.\nclass BarycentricRationalSpanInterpolator : public SpanInterpolator {\n public:\n  struct MinOrder {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Order of barycentric interpolation\"};\n    static type lower_bound() { return 1; }\n  };\n\n  struct MaxOrder {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Order of barycentric interpolation\"};\n    static type upper_bound() { return 10; }\n  };\n\n  using options = tmpl::list<MinOrder, MaxOrder>;\n  static constexpr Options::String help = {\n      \"Barycentric interpolator of option-defined maximum and minimum order.\"};\n\n  explicit BarycentricRationalSpanInterpolator(CkMigrateMessage* /*unused*/) {}\n\n  WRAPPED_PUPable_decl_template(BarycentricRationalSpanInterpolator);  // NOLINT\n\n  // clang-tidy: do not pass by non-const reference\n  void pup(PUP::er& p) override {  // NOLINT\n    p | min_order_;\n    p | max_order_;\n  }\n\n  BarycentricRationalSpanInterpolator() = default;\n  BarycentricRationalSpanInterpolator(\n      const BarycentricRationalSpanInterpolator&) = default;\n  BarycentricRationalSpanInterpolator& operator=(\n      const BarycentricRationalSpanInterpolator&) = default;\n  BarycentricRationalSpanInterpolator(BarycentricRationalSpanInterpolator&&) =\n      default;\n  BarycentricRationalSpanInterpolator& operator=(\n      BarycentricRationalSpanInterpolator&&) = default;\n  ~BarycentricRationalSpanInterpolator() override = default;\n\n  explicit BarycentricRationalSpanInterpolator(size_t min_order,\n                                               size_t max_order);\n\n  std::unique_ptr<SpanInterpolator> get_clone() const override {\n    return std::make_unique<BarycentricRationalSpanInterpolator>(*this);\n  }\n\n  // to get the generic overload:\n  using SpanInterpolator::interpolate;\n\n  double interpolate(const gsl::span<const double>& source_points,\n                     const gsl::span<const double>& values,\n                     double target_point) const override;\n\n  size_t required_number_of_points_before_and_after() const override {\n    return min_order_ / 2 + 1;\n  }\n\n private:\n  size_t min_order_ = 1;\n  size_t max_order_ = 10;\n};\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Interpolation)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BarycentricRational.cpp\n  BarycentricRationalSpanInterpolator.cpp\n  CardinalInterpolator.cpp\n  CubicSpanInterpolator.cpp\n  CubicSpline.cpp\n  IrregularInterpolant.cpp\n  LinearLeastSquares.cpp\n  LinearRegression.cpp\n  LinearSpanInterpolator.cpp\n  PolynomialInterpolation.cpp\n  PredictedZeroCrossing.cpp\n  RegularGridInterpolant.cpp\n  SpanInterpolator.cpp\n  ZeroCrossingPredictor.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BarycentricRational.hpp\n  BarycentricRationalSpanInterpolator.hpp\n  CardinalInterpolator.hpp\n  CubicSpanInterpolator.hpp\n  CubicSpline.hpp\n  IrregularInterpolant.hpp\n  LagrangePolynomial.hpp\n  LinearLeastSquares.hpp\n  LinearRegression.hpp\n  MultiLinearSpanInterpolation.hpp\n  LinearSpanInterpolator.hpp\n  PolynomialInterpolation.hpp\n  PredictedZeroCrossing.hpp\n  RegularGridInterpolant.hpp\n  SpanInterpolator.hpp\n  ZeroCrossingPredictor.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  BLAS::BLAS\n  DataStructures\n  ErrorHandling\n  Options\n  Serialization\n  Spectral\n  Utilities\n  PRIVATE\n  Boost::boost\n  GSL::gsl\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/CardinalInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"CardinalInterpolator.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace intrp {\n\nnamespace {\n\ntemplate <size_t Dim, typename DataType>\nstd::array<Matrix, Dim> interpolation_matrices_impl(\n    const Mesh<Dim>& source_mesh,\n    const tnsr::I<DataType, Dim, Frame::ElementLogical>& target_points) {\n  std::array<Matrix, Dim> interpolation_matrices{};\n  const size_t n_target_points = get_size(get<0>(target_points));\n  for (size_t d = 0; d < Dim; ++d) {\n    switch (source_mesh.basis(d)) {\n      case Spectral::Basis::Chebyshev:\n        [[fallthrough]];\n      case Spectral::Basis::Legendre: {\n        const DataVector xi =\n            get<0>(logical_coordinates(source_mesh.slice_through(d)));\n        gsl::at(interpolation_matrices, d) =\n            Spectral::fornberg_interpolation_matrix(target_points.get(d), xi);\n        break;\n      }\n      case Spectral::Basis::SphericalHarmonic: {\n        switch (source_mesh.quadrature(d)) {\n          case Spectral::Quadrature::Gauss: {\n            const DataVector theta =\n                get<0>(logical_coordinates(source_mesh.slice_through(d)));\n            const DataVector cos_theta_source = cos(theta);\n            const DataType cos_theta_target = cos(target_points.get(d));\n            const Matrix theta_matrix = Spectral::fornberg_interpolation_matrix(\n                cos_theta_target, cos_theta_source);\n            const size_t n_th = source_mesh.extents(d);\n            gsl::at(interpolation_matrices, d)\n                .resize(n_target_points, 2 * n_th);\n            const DataVector csc_theta_source = 1.0 / sin(theta);\n            const DataType sin_theta_target = sin(target_points.get(d));\n            for (size_t k = 0; k < n_target_points; ++k) {\n              for (size_t i_th = 0; i_th < n_th; ++i_th) {\n                const double factor = 0.5 * get_element(sin_theta_target, k) *\n                                      csc_theta_source[i_th];\n                gsl::at(interpolation_matrices, d)(k, i_th) =\n                    theta_matrix(k, i_th) * (0.5 + factor);\n                gsl::at(interpolation_matrices, d)(k, n_th + i_th) =\n                    theta_matrix(k, i_th) * (0.5 - factor);\n              }\n            }\n            break;\n          }\n          case Spectral::Quadrature::Equiangular: {\n            const DataVector phi =\n                get<0>(logical_coordinates(source_mesh.slice_through(d)));\n            const DataType& phi_target = target_points.get(d);\n            DataVector extended_phi_target{2 * n_target_points};\n            for (size_t k = 0; k < n_target_points; ++k) {\n              extended_phi_target[2 * k] = get_element(phi_target, k);\n              extended_phi_target[2 * k + 1] =\n                  get_element(phi_target, k) + M_PI;\n            }\n            gsl::at(interpolation_matrices, d) =\n                Spectral::fourier_interpolation_matrix(extended_phi_target,\n                                                       source_mesh.extents(d));\n            break;\n          }\n          default:\n            ERROR(\n                \"Quadrature must be Gauss or Equiangular for Basis \"\n                \"SphericalHarmonic, not \"\n                << source_mesh.quadrature(d));\n        }\n        break;\n      }\n      case Spectral::Basis::ZernikeB2: {\n        switch (source_mesh.quadrature(d)) {\n          case Spectral::Quadrature::GaussRadauUpper: {\n            const size_t n_r = source_mesh.extents(d);\n\n            // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n            auto buffer = cpp20::make_unique_for_overwrite<double[]>(\n                2 * source_mesh.extents(d) +\n                (std::is_same_v<DataType, DataVector> ? 2 * n_target_points\n                                                      : 0));\n            DataVector radius(&buffer[0], source_mesh.extents(d));\n            DataVector r_squared(&buffer[source_mesh.extents(d)],\n                                 source_mesh.extents(d));\n            DataType r_target;\n            DataType r_target_squared;\n            if constexpr (std::is_same_v<DataType, DataVector>) {\n              r_target.set_data_ref(&buffer[2 * source_mesh.extents(d)],\n                                    n_target_points);\n              r_target_squared.set_data_ref(\n                  &buffer[2 * source_mesh.extents(d) + n_target_points],\n                  n_target_points);\n            }\n            // Need to transform from [-1, 1] logical to true [0,1]\n            radius =\n                0.5 *\n                (get<0>(logical_coordinates(source_mesh.slice_through(d))) +\n                 1.0);\n            r_squared = square(radius);\n            r_target = 0.5 * (target_points.get(d) + 1.0);\n            r_target_squared = square(r_target);\n\n            // Storing for both even and odd modes\n            // First n_r of each column is even, second n_r is odd\n            Matrix interpolation_matrix{n_target_points, 2 * n_r};\n            Matrix even_matrix = Spectral::fornberg_interpolation_matrix(\n                r_target_squared, r_squared);\n            for (size_t k = 0; k < n_target_points; ++k) {\n              for (size_t i_r = 0; i_r < n_r; ++i_r) {\n                interpolation_matrix.at(k, i_r) = even_matrix.at(k, i_r);\n                interpolation_matrix.at(k, i_r + n_r) =\n                    even_matrix.at(k, i_r) * get_element(r_target, k) /\n                    radius.at(i_r);\n              }\n            }\n            gsl::at(interpolation_matrices, d) = interpolation_matrix;\n            break;\n          }\n          case Spectral::Quadrature::Equiangular: {\n            const size_t n_phi = source_mesh.extents(d);\n            const DataType& phi_target = target_points.get(d);\n            Matrix fourier_basis{n_target_points, n_phi};\n            for (size_t k = 0; k < n_target_points; ++k) {\n              const double p = get_element(phi_target, k);\n              for (size_t j = 0; j < n_phi; ++j) {\n                fourier_basis(k, j) = Spectral::compute_basis_function_value<\n                    Spectral::Basis::Fourier>(j, p);\n              }\n            }\n            gsl::at(interpolation_matrices, d) = fourier_basis;\n            break;\n          }\n          default:\n            ERROR(\n                \"Quadrature must be GaussRadauUpper or Equiangular for Basis \"\n                \"ZernikeB2, not \"\n                << source_mesh.quadrature(d));\n        }\n        break;\n      }\n      default:\n        ERROR(\"Basis \" << source_mesh.basis(d)\n                       << \" is not supported by the Cardinal interpolator.\");\n    }\n  }\n  return interpolation_matrices;\n}\n}  // namespace\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#elif defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmissing-noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\ntemplate <>\nDataVector Cardinal<1>::interpolate_zernike_b2(\n    const DataVector& /*f_source*/) const {\n  ERROR(\"ZernikeB2 interpolation is not supported for 1D\");\n}\n\ntemplate <>\nvoid Cardinal<1>::set_zernike_b2_weights() {\n  ERROR(\"ZernikeB2 interpolation is not supported for 1D\");\n}\n\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\ntemplate <size_t Dim>\nvoid Cardinal<Dim>::set_zernike_b2_weights() {\n  static_assert(Dim == 2 or Dim == 3,\n                \"ZernikeB2 interpolation only supports 2D and 3D\");\n  if (not using_zernike_b2_) {\n    return;\n  }\n  const size_t n_r = source_mesh_.extents(0);\n  const size_t n_phi = source_mesh_.extents(1);\n  const size_t n_z = Dim == 3 ? source_mesh_.extents(2) : 1;\n\n  ASSERT(n_phi % 2 == 1,\n         \"Need N_phi to be odd for stability, but got \" << n_phi);\n\n  const size_t combined_dim = n_phi * n_z;  // n_z=1 for 2D case\n\n  const Matrix& nodal_to_modal =\n      Spectral::nodal_to_modal_matrix<Spectral::Basis::Fourier,\n                                      Spectral::Quadrature::Equiangular>(n_phi);\n\n  // Pre-compute combined angular+z weights in interleaved format: [even_k0,\n  // odd_k0, even_k1, odd_k1, ...]\n  zernike_weights_.resize(2 * n_target_points_, combined_dim);\n\n  for (size_t k = 0; k < n_target_points_; ++k) {\n    for (size_t idx = 0; idx < combined_dim; ++idx) {\n      zernike_weights_(2 * k, idx) = 0.0;      // even (radial_offset=0)\n      zernike_weights_(2 * k + 1, idx) = 0.0;  // odd (radial_offset=n_r)\n    }\n\n    // m=0 mode (always uses radial_offset = 0)\n    for (size_t i_phi = 0; i_phi < n_phi; ++i_phi) {\n      const double angular_weight =\n          interpolation_matrices_[1](k, 0) * nodal_to_modal(0, i_phi);\n      if constexpr (Dim == 2) {\n        zernike_weights_(2 * k, i_phi) += angular_weight;\n      } else {  // Dim == 3\n        for (size_t i_z = 0; i_z < n_z; ++i_z) {\n          const size_t idx = i_z * n_phi + i_phi;\n          zernike_weights_(2 * k, idx) +=\n              angular_weight * interpolation_matrices_[2](k, i_z);\n        }\n      }\n    }\n\n    // m>0 modes: group by radial_offset\n    for (size_t i_m = 1; i_m < n_phi; ++i_m) {\n      const size_t m = (i_m + 1) / 2;\n      const size_t radial_offset = (m % 2) * n_r;\n\n      for (size_t i_phi = 0; i_phi < n_phi; ++i_phi) {\n        const double angular_weight =\n            interpolation_matrices_[1](k, i_m) * nodal_to_modal(i_m, i_phi);\n        if constexpr (Dim == 2) {\n          if (radial_offset == 0) {\n            zernike_weights_(2 * k, i_phi) += angular_weight;\n          } else {\n            zernike_weights_(2 * k + 1, i_phi) += angular_weight;\n          }\n        } else {  // Dim == 3\n          for (size_t i_z = 0; i_z < n_z; ++i_z) {\n            const size_t idx = i_z * n_phi + i_phi;\n            const double combined_weight =\n                angular_weight * interpolation_matrices_[2](k, i_z);\n            if (radial_offset == 0) {\n              zernike_weights_(2 * k, idx) += combined_weight;\n            } else {\n              zernike_weights_(2 * k + 1, idx) += combined_weight;\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nDataVector Cardinal<Dim>::interpolate_zernike_b2(\n    const DataVector& f_source) const {\n  static_assert(Dim == 2 or Dim == 3,\n                \"ZernikeB2 interpolation only supports 2D and 3D\");\n\n  ASSERT(f_source.size() == source_mesh_.number_of_grid_points(),\n         \"Size of source data (\"\n             << f_source.size() << \") does not match size of source mesh (\"\n             << source_mesh_.number_of_grid_points() << \")\");\n\n  const size_t n_r = source_mesh_.extents(0);\n  const size_t n_phi = source_mesh_.extents(1);\n  const size_t n_z = Dim == 3 ? source_mesh_.extents(2) : 1;\n\n  const size_t combined_dim = n_phi * n_z;  // n_z=1 for 2D case\n\n  DataVector result{n_target_points_};\n  DataVector intermediate{2 * n_r};\n\n  for (size_t k = 0; k < n_target_points_; ++k) {\n    // Angular (and z for 3D) contraction for even modes\n    dgemv_('N', n_r, combined_dim, 1.0, f_source.data(), n_r,\n           zernike_weights_.data() + 2 * k, 2 * n_target_points_, 0.0,\n           intermediate.data(), 1);\n    // Angular (and z for 3D) contraction for odd modes\n    dgemv_('N', n_r, combined_dim, 1.0, f_source.data(), n_r,\n           zernike_weights_.data() + (2 * k + 1), 2 * n_target_points_, 0.0,\n           intermediate.data() + n_r, 1);\n    // Radial interpolation for both even and odd contributions\n    result[k] = ddot_(2 * n_r, interpolation_matrices_[0].data() + k,\n                      n_target_points_, intermediate.data(), 1);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nCardinal<Dim>::Cardinal() = default;\n\ntemplate <size_t Dim>\nCardinal<Dim>::Cardinal(\n    const Mesh<Dim>& source_mesh,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& target_points)\n    : n_target_points_(get<0>(target_points).size()),\n      source_mesh_(source_mesh),\n      interpolation_matrices_(\n          interpolation_matrices_impl(source_mesh_, target_points)),\n      using_spherical_harmonics_(\n          alg::any_of(source_mesh_.basis(),\n                      [](const Spectral::Basis basis) {\n                        return basis == Spectral::Basis::SphericalHarmonic;\n                      })),\n      using_zernike_b2_(\n          alg::any_of(source_mesh_.basis(), [](const Spectral::Basis basis) {\n            return basis == Spectral::Basis::ZernikeB2;\n          })) {\n  if (using_zernike_b2_) {\n    set_zernike_b2_weights();\n  }\n}\n\ntemplate <size_t Dim>\nCardinal<Dim>::Cardinal(\n    const Mesh<Dim>& source_mesh,\n    const tnsr::I<double, Dim, Frame::ElementLogical>& target_point)\n    : n_target_points_(1),\n      source_mesh_(source_mesh),\n      interpolation_matrices_(\n          interpolation_matrices_impl(source_mesh_, target_point)),\n      using_spherical_harmonics_(\n          alg::any_of(source_mesh_.basis(),\n                      [](const Spectral::Basis basis) {\n                        return basis == Spectral::Basis::SphericalHarmonic;\n                      })),\n      using_zernike_b2_(\n          alg::any_of(source_mesh_.basis(), [](const Spectral::Basis basis) {\n            return basis == Spectral::Basis::ZernikeB2;\n          })) {\n  if (using_zernike_b2_) {\n    set_zernike_b2_weights();\n  }\n}\n\ntemplate <>\nDataVector Cardinal<1>::interpolate(const DataVector& f_source) const {\n  const size_t n_source_points = source_mesh_.number_of_grid_points();\n  ASSERT(f_source.size() == n_source_points,\n         \"Size of source data (\"\n             << f_source.size() << \") does not match size of source mesh (\"\n             << n_source_points\n             << \") that this interpolator was constructed with.\");\n  DataVector result{n_target_points_};\n  dgemv_('N', n_target_points_, n_source_points, 1.0,\n         interpolation_matrices_[0].data(),\n         interpolation_matrices_[0].spacing(), f_source.data(), 1, 0.0,\n         result.data(), 1);\n  return result;\n}\n\ntemplate <>\nDataVector Cardinal<2>::interpolate(const DataVector& f_source) const {\n  ASSERT(f_source.size() == source_mesh_.number_of_grid_points(),\n         \"Size of source data (\"\n             << f_source.size() << \") does not match size of source mesh (\"\n             << source_mesh_.number_of_grid_points()\n             << \") that this interpolator was constructed with.\");\n  DataVector result{n_target_points_};\n  if (using_spherical_harmonics_) {\n    const auto [n_th, n_ph] = source_mesh_.extents().indices();\n    DataVector intermediate_result{2 * n_th};\n    for (size_t k = 0; k < n_target_points_; ++k) {\n      dgemv_('N', n_th, n_ph, 1.0, f_source.data(), n_th,\n             interpolation_matrices_[1].data() + 2 * k, 2 * n_target_points_,\n             0.0, intermediate_result.data(), 1);\n      dgemv_('N', n_th, n_ph, 1.0, f_source.data(), n_th,\n             interpolation_matrices_[1].data() + (2 * k + 1),\n             2 * n_target_points_, 0.0, intermediate_result.data() + n_th, 1);\n      result[k] = ddot_(2 * n_th, interpolation_matrices_[0].data() + k,\n                        n_target_points_, intermediate_result.data(), 1);\n    }\n    return result;\n  } else if (using_zernike_b2_) {\n    return interpolate_zernike_b2(f_source);\n  }\n  const auto [n_xi, n_eta] = source_mesh_.extents().indices();\n  Matrix intermediate_result{n_target_points_, n_eta};\n  dgemm_('N', 'N', n_target_points_, n_eta, n_xi, 1.0,\n         interpolation_matrices_[0].data(),\n         interpolation_matrices_[0].spacing(), f_source.data(), n_xi, 0.0,\n         intermediate_result.data(), n_target_points_);\n  for (size_t k = 0; k < n_target_points_; ++k) {\n    result[k] =\n        ddot_(n_eta, interpolation_matrices_[1].data() + k, n_target_points_,\n              intermediate_result.data() + k, n_target_points_);\n  }\n  return result;\n}\n\ntemplate <>\nDataVector Cardinal<3>::interpolate(const DataVector& f_source) const {\n  ASSERT(f_source.size() == source_mesh_.number_of_grid_points(),\n         \"Size of source data (\"\n             << f_source.size() << \") does not match size of source mesh (\"\n             << source_mesh_.number_of_grid_points()\n             << \") that this interpolator was constructed with.\");\n  const auto [n0, n1, n2] = source_mesh_.extents().indices();\n  Matrix intermediate_matrix{n_target_points_, n1 * n2};\n  dgemm_('N', 'N', n_target_points_, n1 * n2, n0, 1.0,\n         interpolation_matrices_[0].data(),\n         interpolation_matrices_[0].spacing(), f_source.data(), n0, 0.0,\n         intermediate_matrix.data(), n_target_points_);\n  auto transpose = intermediate_matrix.transpose();\n  DataVector result{n_target_points_};\n  if (using_spherical_harmonics_) {\n    DataVector intermediate_vector{2 * n1};\n    for (size_t k = 0; k < n_target_points_; ++k) {\n      dgemv_('N', n1, n2, 1.0, transpose.data() + k * n1 * n2, n1,\n             interpolation_matrices_[2].data() + 2 * k, 2 * n_target_points_,\n             0.0, intermediate_vector.data(), 1);\n      dgemv_('N', n1, n2, 1.0, transpose.data() + k * n1 * n2, n1,\n             interpolation_matrices_[2].data() + (2 * k + 1),\n             2 * n_target_points_, 0.0, intermediate_vector.data() + n1, 1);\n      result[k] = ddot_(2 * n1, interpolation_matrices_[1].data() + k,\n                        n_target_points_, intermediate_vector.data(), 1);\n    }\n    return result;\n  } else if (using_zernike_b2_) {\n    return interpolate_zernike_b2(f_source);\n  }\n  DataVector intermediate_vector{n2};\n  for (size_t k = 0; k < n_target_points_; ++k) {\n    dgemv_('T', n1, n2, 1.0, transpose.data() + k * n1 * n2, n1,\n           interpolation_matrices_[1].data() + k, n_target_points_, 0.0,\n           intermediate_vector.data(), 1);\n    result[k] = ddot_(n2, interpolation_matrices_[2].data() + k,\n                      n_target_points_, intermediate_vector.data(), 1);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nconst std::array<Matrix, Dim>& Cardinal<Dim>::interpolation_matrices() const {\n  return interpolation_matrices_;\n}\n\ntemplate <size_t Dim>\nvoid Cardinal<Dim>::pup(PUP::er& p) {\n  p | n_target_points_;\n  p | source_mesh_;\n  p | interpolation_matrices_;\n  p | using_spherical_harmonics_;\n  p | using_zernike_b2_;\n  if (p.isUnpacking()) {\n    set_zernike_b2_weights();\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const Cardinal<Dim>& lhs, const Cardinal<Dim>& rhs) {\n  // Not all member variables are required:\n  //  - using_spherical_haromincs_ and using_zernike_b2_ are from source_mesh_\n  //  - zernike_weights_ is computable from n_target_points_ and source_mesh_\n  return lhs.n_target_points_ == rhs.n_target_points_ and\n         lhs.source_mesh_ == rhs.source_mesh_ and\n         lhs.interpolation_matrices_ == rhs.interpolation_matrices_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Cardinal<Dim>& lhs, const Cardinal<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate class Cardinal<1>;\ntemplate class Cardinal<2>;\ntemplate class Cardinal<3>;\ntemplate bool operator==(const Cardinal<1>& lhs, const Cardinal<1>& rhs);\ntemplate bool operator==(const Cardinal<2>& lhs, const Cardinal<2>& rhs);\ntemplate bool operator==(const Cardinal<3>& lhs, const Cardinal<3>& rhs);\ntemplate bool operator!=(const Cardinal<1>& lhs, const Cardinal<1>& rhs);\ntemplate bool operator!=(const Cardinal<2>& lhs, const Cardinal<2>& rhs);\ntemplate bool operator!=(const Cardinal<3>& lhs, const Cardinal<3>& rhs);\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/CardinalInterpolator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace intrp {\n/*!\n * \\brief Interpolates by doing partial summation in each dimension using\n * one-dimensional interpolation\n *\n * \\details The one-dimensional matrices used to do the interpolation depend\n * upon the Spectral::Basis used in each dimension:\n * - For a Chebyshev or Legendre basis, the matrices are given by\n * Spectral::fornberg_interpolation_matrix at the quadrature points of the\n * source_mesh.  (These are equivalent to those returned by\n * Spectral::interpolation_matrix.)\n * - For a Fourier basis, the matrix is given by\n * Spectral::fourier_interpolation_matrix at the quadrature points of the\n * source_mesh\n *\n * For multidimensional bases such as the SphericalHarmonic or ZernikeB2, the\n * matrices used for interpolating cannot be applied per dimension but must\n * be handled specially.\n *\n */\ntemplate <size_t Dim>\nclass Cardinal {\n public:\n  Cardinal(\n      const Mesh<Dim>& source_mesh,\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical>& target_points);\n  Cardinal(const Mesh<Dim>& source_mesh,\n           const tnsr::I<double, Dim, Frame::ElementLogical>& target_point);\n\n  Cardinal();\n\n  /// Interpolates the function `f` provided on the `source_mesh` to the\n  /// `target_points` with which the interpolator was constructed.\n  DataVector interpolate(const DataVector& f) const;\n\n  /// The one-dimensional interpolation matrices used to do the interpolation\n  const std::array<Matrix, Dim>& interpolation_matrices() const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  /// Precomputes `zernike_weights_`, which is all the work independent of\n  /// `f_source`, to avoid redundant computations. This is only needed when\n  /// the source mesh has a B2 basis\n  void set_zernike_b2_weights();\n\n  /// General routine called by `interpolate()` for a mesh using B2 bases\n  DataVector interpolate_zernike_b2(const DataVector& f_source) const;\n\n  template <size_t LocalDim>\n  // NOLINTNEXTILNE(readability-redundant-declaration)\n  friend bool operator==(const Cardinal<LocalDim>& lhs,\n                         const Cardinal<LocalDim>& rhs);\n\n  size_t n_target_points_ = 0;\n  Mesh<Dim> source_mesh_{};\n  std::array<Matrix, Dim> interpolation_matrices_{};\n  bool using_spherical_harmonics_{false};\n  bool using_zernike_b2_{false};\n  Matrix zernike_weights_{};\n};\n\ntemplate <size_t Dim>\nbool operator==(const Cardinal<Dim>& lhs, const Cardinal<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const Cardinal<Dim>& lhs, const Cardinal<Dim>& rhs);\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/CubicSpanInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace intrp {\n\nnamespace {\ntemplate <typename ValueType>\nSPECTRE_ALWAYS_INLINE ValueType interpolate_impl(\n    const gsl::span<const double>& source_points,\n    const gsl::span<const ValueType>& values, const double target_point) {\n  const double t0 = source_points[0];\n  const double t1 = source_points[1];\n  const double t2 = source_points[2];\n  const double t3 = source_points[3];\n\n  const auto d0 = values[0];\n  const auto d1 = values[1];\n  const auto d2 = values[2];\n  const auto d3 = values[3];\n\n  return (-((target_point - t2) *\n            (d3 * (target_point - t0) * (target_point - t1) * (t0 - t1) *\n                 (t0 - t2) * (t1 - t2) +\n             (d1 * (target_point - t0) * (t0 - t2) * (t0 - t3) -\n              d0 * (target_point - t1) * (t1 - t2) * (t1 - t3)) *\n                 (target_point - t3) * (t2 - t3))) +\n          d2 * (target_point - t0) * (target_point - t1) * (t0 - t1) *\n              (target_point - t3) * (t0 - t3) * (t1 - t3)) /\n         ((t0 - t1) * (t0 - t2) * (t1 - t2) * (t0 - t3) * (t1 - t3) *\n          (t2 - t3));\n}\n}  // namespace\n\ndouble CubicSpanInterpolator::interpolate(\n    const gsl::span<const double>& source_points,\n    const gsl::span<const double>& values, double target_point) const {\n  return interpolate_impl(source_points, values, target_point);\n}\n\nstd::complex<double> CubicSpanInterpolator::interpolate(\n    const gsl::span<const double>& source_points,\n    const gsl::span<const std::complex<double>>& values,\n    double target_point) const {\n  return interpolate_impl(source_points, values, target_point);\n}\n\nPUP::able::PUP_ID intrp::CubicSpanInterpolator::my_PUP_ID = 0;\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace intrp {\n\n/// \\brief Performs a cubic interpolation; this class can be chosen via the\n/// options factory mechanism as a possible `SpanInterpolator`.\n///\n/// \\details This interpolator is hand-coded to be identical to the SpEC\n/// implementation used for SpEC CCE so that comparison results can be as close\n/// as possible for diagnostics.\nclass CubicSpanInterpolator : public SpanInterpolator {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"Cubic interpolator.\"};\n\n  CubicSpanInterpolator() = default;\n  CubicSpanInterpolator(const CubicSpanInterpolator&) = default;\n  CubicSpanInterpolator& operator=(const CubicSpanInterpolator&) = default;\n  CubicSpanInterpolator(CubicSpanInterpolator&&) = default;\n  CubicSpanInterpolator& operator=(CubicSpanInterpolator&&) = default;\n  ~CubicSpanInterpolator() override = default;\n\n  explicit CubicSpanInterpolator(CkMigrateMessage* /*unused*/) {}\n\n  WRAPPED_PUPable_decl_template(CubicSpanInterpolator);  // NOLINT\n\n  // clang-tidy: do not pass by non-const reference\n  void pup(PUP::er& /*p*/) override {}\n\n  std::unique_ptr<SpanInterpolator> get_clone() const override {\n    return std::make_unique<CubicSpanInterpolator>(*this);\n  }\n\n  double interpolate(const gsl::span<const double>& source_points,\n                     const gsl::span<const double>& values,\n                     double target_point) const override;\n\n  std::complex<double> interpolate(\n      const gsl::span<const double>& source_points,\n      const gsl::span<const std::complex<double>>& values,\n      double target_point) const;\n\n  size_t required_number_of_points_before_and_after() const override {\n    return 2;\n  }\n};\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/CubicSpline.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/CubicSpline.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <gsl/gsl_spline.h>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace intrp {\nCubicSpline::CubicSpline(std::vector<double> x_values,\n                         std::vector<double> y_values)\n    : x_values_(std::move(x_values)), y_values_(std::move(y_values)) {\n  ASSERT(x_values_.size() == y_values_.size(),\n         \"The x-value and y-value vectors must be of the same length, but \"\n         \"received x-value of size: \"\n             << x_values_.size()\n             << \" and y-value of size: \" << y_values_.size());\n  ASSERT(std::is_sorted(x_values_.begin(), x_values_.end()),\n         \"The x-values must be sorted.\");\n  initialize_interpolant();\n}\n\nCubicSpline::CubicSpline(const CubicSpline& rhs)\n    : x_values_(rhs.x_values_), y_values_(rhs.y_values_) {\n  // potential optimization -- copy the interpolant rather than recomputing\n  initialize_interpolant();\n}\nbool CubicSpline::operator==(const CubicSpline& rhs) const {\n  return x_values_ == rhs.x_values_ and y_values_ == rhs.y_values_;\n}\n\nCubicSpline& CubicSpline::operator=(const CubicSpline& rhs) {\n  x_values_ = rhs.x_values_;\n  y_values_ = rhs.y_values_;\n  // potential optimization -- copy the interpolant rather than recomputing\n  initialize_interpolant();\n  return *this;\n}\n\nvoid CubicSpline::gsl_interp_accel_deleter::operator()(\n    gsl_interp_accel* const acc) const {\n  gsl_interp_accel_free(acc);\n}\n\nvoid CubicSpline::gsl_spline_deleter::operator()(\n    gsl_spline* const spline) const {\n  gsl_spline_free(spline);\n}\n\nvoid CubicSpline::initialize_interpolant() {\n  if (x_values_.empty()) {\n    // Do nothing for default-initialized objects\n    return;\n  }\n  const size_t num_points = x_values_.size();\n  acc_ = std::unique_ptr<gsl_interp_accel, gsl_interp_accel_deleter>{\n      gsl_interp_accel_alloc()};\n  spline_ = std::unique_ptr<gsl_spline, gsl_spline_deleter>{\n      gsl_spline_alloc(gsl_interp_cspline, num_points)};\n  gsl_spline_init(spline_.get(), x_values_.data(), y_values_.data(),\n                  num_points);\n}\n\ndouble CubicSpline::operator()(const double x_to_interp_to) const {\n  ASSERT(\n      x_to_interp_to >= x_values_.front() and\n          x_to_interp_to <= x_values_.back(),\n      \"The point \"\n          << x_to_interp_to\n          << \" to interpolate to is outside the domain of the interpolation [\"\n          << x_values_.front() << \", \" << x_values_.back() << \"].\");\n  return gsl_spline_eval(spline_.get(), x_to_interp_to, acc_.get());\n}\n\nvoid CubicSpline::pup(PUP::er& p) {\n  p | x_values_;\n  p | y_values_;\n  if (p.isUnpacking()) {\n    initialize_interpolant();\n  }\n}\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/CubicSpline.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <gsl/gsl_spline.h>\n#include <memory>\n#include <vector>\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace intrp {\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief A natural cubic spline interpolation class\n *\n * The class builds a cubic spline interpolant with natural boundary conditions\n * using the `x_values` and `y_values` passed into the constructor. For details\n * on the algorithm see the GSL documentation on `gsl_interp_cspline`.\n *\n * Here is an example how to use this class:\n *\n * \\snippet Test_CubicSpline.cpp interpolate_example\n */\nclass CubicSpline {\n public:\n  CubicSpline(std::vector<double> x_values, std::vector<double> y_values);\n\n  CubicSpline() = default;\n  CubicSpline(const CubicSpline& /*rhs*/);\n  CubicSpline& operator=(const CubicSpline& /*rhs*/);\n  CubicSpline(CubicSpline&& /*rhs*/) = default;\n  CubicSpline& operator=(CubicSpline&& rhs) = default;\n  ~CubicSpline() = default;\n\n  bool operator==(const CubicSpline& rhs) const;\n\n  double operator()(double x_to_interp_to) const;\n\n  const std::vector<double>& x_values() const { return x_values_; }\n\n  const std::vector<double>& y_values() const { return y_values_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  struct gsl_interp_accel_deleter {\n    void operator()(gsl_interp_accel* acc) const;\n  };\n  struct gsl_spline_deleter {\n    void operator()(gsl_spline* spline) const;\n  };\n\n  void initialize_interpolant();\n\n  std::vector<double> x_values_;\n  std::vector<double> y_values_;\n  std::unique_ptr<gsl_interp_accel, gsl_interp_accel_deleter> acc_;\n  std::unique_ptr<gsl_spline, gsl_spline_deleter> spline_;\n};\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/IrregularInterpolant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IrregularInterpolant.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <iterator>\n#include <limits>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CardinalInterpolator.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Zernike.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid compute_zernikeb2_interpolation(\n    gsl::not_null<Matrix*> result, const Mesh<Dim>& mesh,\n    const std::array<Matrix, Dim>& interpolation_matrices,\n    const size_t number_of_target_points) {\n  static_assert(Dim == 2 or Dim == 3,\n                \"ZernikeB2 interpolation only supports 2D and 3D\");\n\n  const size_t n_r = mesh.extents(0);\n  const size_t n_phi = mesh.extents(1);\n  const size_t n_z = Dim == 3 ? mesh.extents(2) : 1;\n\n  ASSERT(n_phi % 2 == 1, \"Need N_phi to be odd for stability, got \" << n_phi);\n\n  Matrix identity{};\n  const Matrix& m_r = interpolation_matrices[0];\n  const Matrix& m_ph = interpolation_matrices[1];\n  const Matrix& m_z = [&interpolation_matrices, &identity]() {\n    if constexpr (Dim == 3) {\n      (void)identity;\n      return interpolation_matrices[2];\n    } else {\n      (void)interpolation_matrices;\n      return identity;\n    }\n  }();\n\n  const Matrix& nodal_to_modal =\n      Spectral::nodal_to_modal_matrix<Spectral::Basis::Fourier,\n                                      Spectral::Quadrature::Equiangular>(n_phi);\n\n  // Pre-compute angular (and z for 3D) weights in interleaved format\n  const size_t combined_dim = n_phi * n_z;  // n_z=1 for 2D case\n  Matrix zernike_weights(2 * number_of_target_points, combined_dim);\n\n  for (size_t k = 0; k < number_of_target_points; ++k) {\n    // Initialize\n    for (size_t idx = 0; idx < combined_dim; ++idx) {\n      zernike_weights(2 * k, idx) = 0.0;      // even (radial_offset=0)\n      zernike_weights(2 * k + 1, idx) = 0.0;  // odd (radial_offset=n_r)\n    }\n\n    // m=0 mode (always uses radial_offset = 0)\n    for (size_t i_phi = 0; i_phi < n_phi; ++i_phi) {\n      const double angular_weight = m_ph(k, 0) * nodal_to_modal(0, i_phi);\n      if constexpr (Dim == 2) {\n        zernike_weights(2 * k, i_phi) += angular_weight;\n      } else {  // Dim == 3\n        for (size_t i_z = 0; i_z < n_z; ++i_z) {\n          const size_t idx = i_z * n_phi + i_phi;\n          zernike_weights(2 * k, idx) +=\n              Dim == 3 ? angular_weight * m_z(k, i_z) : angular_weight;\n        }\n      }\n    }\n\n    // m>0 modes: group by radial_offset\n    for (size_t i_m = 1; i_m < n_phi; ++i_m) {\n      const size_t m = (i_m + 1) / 2;\n      const size_t radial_offset = (m % 2) * n_r;\n\n      for (size_t i_phi = 0; i_phi < n_phi; ++i_phi) {\n        const double angular_weight = m_ph(k, i_m) * nodal_to_modal(i_m, i_phi);\n        if constexpr (Dim == 2) {\n          if (radial_offset == 0) {\n            zernike_weights(2 * k, i_phi) += angular_weight;\n          } else {\n            zernike_weights(2 * k + 1, i_phi) += angular_weight;\n          }\n        } else {  // Dim == 3\n          for (size_t i_z = 0; i_z < n_z; ++i_z) {\n            const size_t idx = i_z * n_phi + i_phi;\n            const double combined_weight =\n                Dim == 3 ? angular_weight * m_z(k, i_z) : angular_weight;\n            if (radial_offset == 0) {\n              zernike_weights(2 * k, idx) += combined_weight;\n            } else {\n              zernike_weights(2 * k + 1, idx) += combined_weight;\n            }\n          }\n        }\n      }\n    }\n  }\n\n  // Now fill the result matrix using the pre-computed weights\n  for (size_t k = 0; k < number_of_target_points; ++k) {\n    for (size_t s = 0; s < mesh.number_of_grid_points(); ++s) {\n      if constexpr (Dim == 2) {\n        const size_t i_phi = s / n_r;  // NOLINT(clang-analyzer-core.DivideZero)\n        const size_t i_r = s % n_r;\n\n        // Even contribution (radial_offset=0)\n        (*result)(k, s) = m_r(k, i_r) * zernike_weights(2 * k, i_phi);\n\n        // Odd contribution (radial_offset=n_r)\n        if (i_r + n_r < m_r.columns()) {\n          (*result)(k, s) +=\n              m_r(k, i_r + n_r) * zernike_weights(2 * k + 1, i_phi);\n        }\n      } else {  // Dim == 3\n        // NOLINTNEXTLINE(clang-analyzer-core.DivideZero)\n        const size_t i_z = s / (n_phi * n_r);\n        const size_t i_phi = (s % (n_phi * n_r)) / n_r;\n        const size_t i_r = s % n_r;\n        const size_t idx = i_z * n_phi + i_phi;\n\n        // Even contribution (radial_offset=0)\n        (*result)(k, s) = m_r(k, i_r) * zernike_weights(2 * k, idx);\n\n        // Odd contribution (radial_offset=n_r)\n        if (i_r + n_r < m_r.columns()) {\n          (*result)(k, s) +=\n              m_r(k, i_r + n_r) * zernike_weights(2 * k + 1, idx);\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t Order>\nstd::vector<double> fd_stencil(const DataVector& xi_source, double xi_target);\n\n// potential future optimization: use the barycentric formula\n\n// Just linear for now, can be extended to higher order...\n// Future optimization: it might be more efficient to use Blaze's sparse\n// matrices since the interpolation matrix is mostly zeros\ntemplate <>\nstd::vector<double> fd_stencil<1>(const DataVector& xi_source,\n                                  const double xi_target) {\n  ASSERT(std::is_sorted(std::begin(xi_source), std::end(xi_source)),\n         \"xi_source = \" << xi_source);\n  auto xi_u =\n      std::upper_bound(std::begin(xi_source), std::end(xi_source), xi_target);\n  if (std::end(xi_source) == xi_u) {\n    std::advance(xi_u, -1);\n  }\n  if (std::begin(xi_source) == xi_u) {\n    std::advance(xi_u, 1);\n  }\n  const auto xi_l = std::prev(xi_u);\n  auto index = std::distance(std::begin(xi_source), xi_l);\n  std::vector<double> result(xi_source.size(), 0.0);\n  const auto result_l = std::next(std::begin(result), index);\n  const auto result_u = std::next(result_l, 1);\n  *result_l = (*xi_u - xi_target) / (*xi_u - *xi_l);\n  *result_u = (xi_target - *xi_l) / (*xi_u - *xi_l);\n  return result;\n}\n\n// Quadratic (three-point) finite-difference interpolation stencil.\n// Returns a weight vector with exactly three non-zero entries corresponding\n// to a chosen triplet of consecutive grid points. For interior targets we pick\n// the more centered of the two possible triplets bracketing the target.\n// Edge targets (or outside) use the first or last 3 points, yielding\n// one-sided quadratic extrapolation.\ntemplate <>\nstd::vector<double> fd_stencil<2>(const DataVector& xi_source,\n                                  const double xi_target) {\n  ASSERT(std::is_sorted(std::begin(xi_source), std::end(xi_source)),\n         \"xi_source = \" << xi_source);\n  const size_t num_of_pts = xi_source.size();\n  ASSERT(num_of_pts >= 3,\n         \"Need at least 3 points for quadratic interpolation (got \"\n             << num_of_pts << \")\");\n\n  auto xi_u =\n      std::upper_bound(std::begin(xi_source), std::end(xi_source), xi_target);\n\n  // Determine the three indices to use.\n  size_t i0{0};\n  size_t i1{0};\n  size_t i2{0};\n\n  if (xi_u == std::begin(xi_source)) {\n    // Target at/below first point\n    i0 = 0;\n    i1 = 1;\n    i2 = 2;\n  } else if (xi_u == std::end(xi_source)) {\n    // Target above last point\n    i0 = num_of_pts - 3;\n    i1 = num_of_pts - 2;\n    i2 = num_of_pts - 1;\n  } else {\n    const auto iu =\n        static_cast<size_t>(std::distance(std::begin(xi_source), xi_u));\n    const size_t il = iu - 1;  // lower bracket index\n\n    if (il == 0) {\n      // Near lower boundary\n      i0 = 0;\n      i1 = 1;\n      i2 = 2;\n    } else if (iu == num_of_pts - 1) {\n      // Near upper boundary (target between last two points)\n      i0 = num_of_pts - 3;\n      i1 = num_of_pts - 2;\n      i2 = num_of_pts - 1;\n    } else {\n      // Interior: choose centered triplet\n      // Option A: (il-1, il, iu)\n      // Option B: (il, iu, iu+1)\n      const double x_il = xi_source[il];\n      const double x_iu = xi_source[iu];\n      // Compare proximity to pick more centered set\n      if (xi_target - x_il < x_iu - xi_target) {\n        i0 = il - 1;\n        i1 = il;\n        i2 = iu;\n      } else {\n        i0 = il;\n        i1 = iu;\n        i2 = iu + 1;\n      }\n    }\n  }\n\n  // Compute Lagrange weights for the selected three nodes.\n  const double x0 = xi_source[i0];\n  const double x1 = xi_source[i1];\n  const double x2 = xi_source[i2];\n\n  const double denom0 = (x0 - x1) * (x0 - x2);\n  const double denom1 = (x1 - x0) * (x1 - x2);\n  const double denom2 = (x2 - x0) * (x2 - x1);\n\n  std::vector<double> w(num_of_pts, 0.0);\n  w[i0] = (xi_target - x1) * (xi_target - x2) / denom0;\n  w[i1] = (xi_target - x0) * (xi_target - x2) / denom1;\n  w[i2] = (xi_target - x0) * (xi_target - x1) / denom2;\n\n  return w;\n}\n\n// Cubic (four-point) finite-difference interpolation stencil.\n// Uses four consecutive grid points to build a degree-3 Lagrange interpolant.\n// Boundary/out-of-domain targets use the first/last 4 points (one-sided cubic\n// extrapolation). Interior targets: choose among candidate 4-point windows that\n// contain the bracketing pair to best center the target (minimizing distance to\n// the window's midpoint).\ntemplate <>\nstd::vector<double> fd_stencil<3>(const DataVector& xi_source,\n                                  const double xi_target) {\n  ASSERT(std::is_sorted(std::begin(xi_source), std::end(xi_source)),\n         \"xi_source = \" << xi_source);\n  const size_t num_of_pts = xi_source.size();\n  ASSERT(num_of_pts >= 4, \"Need at least 4 points for cubic interpolation (got \"\n                              << num_of_pts << \")\");\n  auto xi_u =\n      std::upper_bound(std::begin(xi_source), std::end(xi_source), xi_target);\n\n  size_t start{0};  // start index of the 4-point window\n  if (xi_u == std::begin(xi_source)) {\n    // Below first point\n    start = 0;\n  } else if (xi_u == std::end(xi_source)) {\n    // Above last point\n    start = num_of_pts - 4;\n  } else {\n    const auto iu =\n        static_cast<size_t>(std::distance(std::begin(xi_source), xi_u));\n    const size_t il = iu - 1;  // lower bracket index\n    // Determine candidate window starts ensuring il >= s and iu <= s+3\n    const size_t min_start = (il >= 2 ? il - 2 : 0);\n    const size_t max_start = std::min(il, num_of_pts - 4);\n    // Collect candidates\n    double best_dist = std::numeric_limits<double>::max();\n    size_t best_start = min_start;\n    for (size_t s = min_start; s <= max_start; ++s) {\n      // Midpoint of inner two nodes (s+1, s+2) gives a reasonable center\n      const double mid = 0.5 * (xi_source[s + 1] + xi_source[s + 2]);\n      const double dist = std::abs(xi_target - mid);\n      if (dist < best_dist) {\n        best_dist = dist;\n        best_start = s;\n      }\n    }\n    start = best_start;\n  }\n\n  const size_t i0 = start;\n  const size_t i1 = start + 1;\n  const size_t i2 = start + 2;\n  const size_t i3 = start + 3;\n  const double x0 = xi_source[i0];\n  const double x1 = xi_source[i1];\n  const double x2 = xi_source[i2];\n  const double x3 = xi_source[i3];\n\n  // Compute Lagrange weights for 4 points.\n  auto lagrange_weight = [&](double xj, const std::array<double, 3>& others) {\n    double num = 1.0;\n    double denom = 1.0;\n    for (const double xo : others) {\n      num *= (xi_target - xo);\n      denom *= (xj - xo);\n    }\n    return num / denom;\n  };\n\n  std::vector<double> w(num_of_pts, 0.0);\n  w[i0] = lagrange_weight(x0, {x1, x2, x3});\n  w[i1] = lagrange_weight(x1, {x0, x2, x3});\n  w[i2] = lagrange_weight(x2, {x0, x1, x3});\n  w[i3] = lagrange_weight(x3, {x0, x1, x2});\n  return w;\n}\n\ntemplate <size_t Dim, typename DataType>\nMatrix interpolation_matrix(\n    const Mesh<Dim>& mesh,\n    const tnsr::I<DataType, Dim, Frame::ElementLogical>& points);\n\ntemplate <typename DataType>\nMatrix interpolation_matrix(\n    const Mesh<1>& mesh,\n    const tnsr::I<DataType, 1, Frame::ElementLogical>& points,\n    const std::optional<size_t>& fd_to_fd_interp_order) {\n  if (mesh.basis()[0] == Spectral::Basis::FiniteDifference) {\n    auto source_xi = logical_coordinates(mesh);\n    const auto number_of_source_points = mesh.number_of_grid_points();\n    const DataVector xi_source(get<0>(source_xi).data(),\n                               number_of_source_points);\n    const size_t number_of_target_points = get_size(get<0>(points));\n    Matrix result(number_of_target_points, number_of_source_points);\n    constexpr size_t default_order = 1;  // linear interpolation by default\n    for (size_t p = 0; p < number_of_target_points; ++p) {\n      const double xi_target = get_element(get<0>(points), p);\n      std::vector<double> stencil;\n      switch (fd_to_fd_interp_order.value_or(default_order)) {\n        case 1: {\n          stencil = fd_stencil<1>(xi_source, xi_target);\n          break;\n        }\n        case 2: {\n          stencil = fd_stencil<2>(xi_source, xi_target);\n          break;\n        }\n        case 3: {\n          stencil = fd_stencil<3>(xi_source, xi_target);\n          break;\n        }\n        default:\n          ERROR(\"Unsupported fd_to_fd_interp_order: \"\n                << fd_to_fd_interp_order.value_or(default_order)\n                << \"; expected 1, 2, or 3.\");\n      }\n      for (size_t i = 0; i < number_of_source_points; ++i) {\n        result(p, i) = stencil[i];\n      }\n    }\n    return result;\n  }\n\n  // Not FD, so use spectral interpolation\n  ASSERT(\n      fd_to_fd_interp_order == std::nullopt,\n      \"fd_to_fd_interp_order only applies to FD meshes. But Mesh is \" << mesh);\n  return Spectral::interpolation_matrix(mesh, get<0>(points));\n}\n\ntemplate <typename DataType>\nMatrix interpolation_matrix(\n    const Mesh<2>& mesh,\n    const tnsr::I<DataType, 2, Frame::ElementLogical>& points,\n    const std::optional<size_t>& fd_to_fd_interp_order) {\n  const auto number_of_target_points = get_size(get<0>(points));\n  Matrix result(number_of_target_points, mesh.number_of_grid_points());\n\n  ASSERT(\n      mesh.basis()[0] == Spectral::Basis::FiniteDifference or\n          fd_to_fd_interp_order == std::nullopt,\n      \"fd_to_fd_interp_order only applies to FD meshes. But Mesh is \" << mesh);\n\n  if (mesh.basis()[0] == Spectral::Basis::FiniteDifference) {\n    ASSERT(mesh.basis()[1] == Spectral::Basis::FiniteDifference,\n           \"Mixed FD and DG bases are not supported. Mesh = \" << mesh);\n    auto source_xi = logical_coordinates(mesh);\n    DataVector xi_source{get<0>(source_xi).data(), mesh.extents(0)};\n    DataVector eta_source;\n    if (mesh.extents(1) == mesh.extents(0)) {\n      eta_source.set_data_ref(&xi_source);\n    } else {\n      eta_source.destructive_resize(mesh.extents(1));\n      for (size_t j = 0; j < mesh.extents(1); ++j) {\n        eta_source[j] = get<1>(source_xi)[j * mesh.extents(0)];\n      }\n    }\n    constexpr size_t default_order = 1;  // linear interpolation by default\n    for (size_t p = 0; p < number_of_target_points; ++p) {\n      const double xi_target = get_element(get<0>(points), p);\n      const double eta_target = get_element(get<1>(points), p);\n      std::vector<double> xi_stencil;\n      std::vector<double> eta_stencil;\n      switch (fd_to_fd_interp_order.value_or(default_order)) {\n        case 1: {\n          xi_stencil = fd_stencil<1>(xi_source, xi_target);\n          eta_stencil = fd_stencil<1>(eta_source, eta_target);\n          break;\n        }\n        case 2: {\n          xi_stencil = fd_stencil<2>(xi_source, xi_target);\n          eta_stencil = fd_stencil<2>(eta_source, eta_target);\n          break;\n        }\n        case 3: {\n          xi_stencil = fd_stencil<3>(xi_source, xi_target);\n          eta_stencil = fd_stencil<3>(eta_source, eta_target);\n          break;\n        }\n        default:\n          ERROR(\"Unsupported fd_to_fd_interp_order: \"\n                << fd_to_fd_interp_order.value_or(default_order)\n                << \"; expected 1, 2, or 3.\");\n      }\n      for (size_t j = 0, s = 0; j < mesh.extents(1); ++j) {\n        for (size_t i = 0; i < mesh.extents(0); ++i) {\n          result(p, s) = xi_stencil[i] * eta_stencil[j];\n          ++s;\n        }\n      }\n    }\n    return result;\n  } else if (mesh.basis()[0] == Spectral::Basis::SphericalHarmonic) {\n    ASSERT(mesh.basis()[1] == Spectral::Basis::SphericalHarmonic,\n           \"Expected both dimensions to have spherical harmonic basis. Mesh = \"\n               << mesh);\n    const intrp::Cardinal cardinal_interpolator(mesh, points);\n    const auto [n_th, n_ph] = mesh.extents().indices();\n    const auto& [m_th, m_ph] = cardinal_interpolator.interpolation_matrices();\n    for (size_t i_ph = 0, s = 0; i_ph < n_ph; ++i_ph) {\n      for (size_t i_th = 0; i_th < n_th; ++i_th) {\n        for (size_t k = 0; k < number_of_target_points; ++k) {\n          result(k, s) = m_th(k, i_th) * m_ph(2 * k, i_ph) +\n                         m_th(k, i_th + n_th) * m_ph(2 * k + 1, i_ph);\n        }\n        ++s;\n      }\n    }\n    return result;\n  } else if (mesh.basis()[0] == Spectral::Basis::ZernikeB2) {\n    ASSERT(mesh.basis()[1] == Spectral::Basis::ZernikeB2,\n           \"Unexpected basis combination: \" << mesh.basis());\n    const intrp::Cardinal cardinal_interpolator(mesh, points);\n    const auto& interpolation_matrices =\n        cardinal_interpolator.interpolation_matrices();\n    compute_zernikeb2_interpolation<2>(make_not_null(&result), mesh,\n                                       interpolation_matrices,\n                                       number_of_target_points);\n    return result;\n  }\n\n  // Not FD or special basis, so use 1D spectral interpolation matrices\n  const std::array<Matrix, 2> matrices{\n      {Spectral::interpolation_matrix(mesh.slice_through(0), get<0>(points)),\n       Spectral::interpolation_matrix(mesh.slice_through(1), get<1>(points))}};\n\n  // First dimension of DataVector varies fastest.\n  for (size_t j = 0, s = 0; j < mesh.extents(1); ++j) {\n    for (size_t i = 0; i < mesh.extents(0); ++i) {\n      for (size_t p = 0; p < number_of_target_points; ++p) {\n        result(p, s) = matrices[0](p, i) * matrices[1](p, j);\n      }\n      ++s;\n    }\n  }\n  return result;\n}\n\ntemplate <typename DataType>\nMatrix interpolation_matrix(\n    const Mesh<3>& mesh,\n    const tnsr::I<DataType, 3, Frame::ElementLogical>& points,\n    const std::optional<size_t>& fd_to_fd_interp_order) {\n  const auto number_of_target_points = get_size(get<0>(points));\n  Matrix result(number_of_target_points, mesh.number_of_grid_points());\n\n  ASSERT(\n      mesh.basis()[0] == Spectral::Basis::FiniteDifference or\n          fd_to_fd_interp_order == std::nullopt,\n      \"fd_to_fd_interp_order only applies to FD meshes. But Mesh is \" << mesh);\n\n  if (mesh.basis() == std::array{Spectral::Basis::FiniteDifference,\n                                 Spectral::Basis::FiniteDifference,\n                                 Spectral::Basis::FiniteDifference}) {\n    auto source_xi = logical_coordinates(mesh);\n    DataVector xi_source{get<0>(source_xi).data(), mesh.extents(0)};\n    DataVector eta_source;\n    if (mesh.extents(1) == mesh.extents(0)) {\n      eta_source.set_data_ref(&xi_source);\n    } else {\n      eta_source.destructive_resize(mesh.extents(1));\n      for (size_t j = 0; j < mesh.extents(1); ++j) {\n        eta_source[j] = get<1>(source_xi)[j * mesh.extents(0)];\n      }\n    }\n    DataVector zeta_source;\n    if (mesh.extents(2) == mesh.extents(0)) {\n      zeta_source.set_data_ref(&xi_source);\n    } else if (mesh.extents(2) == mesh.extents(1)) {\n      zeta_source.set_data_ref(&eta_source);\n    } else {\n      zeta_source.destructive_resize(mesh.extents(2));\n      for (size_t k = 0; k < mesh.extents(2); ++k) {\n        zeta_source[k] =\n            get<2>(source_xi)[k * mesh.extents(0) * mesh.extents(1)];\n      }\n    }\n    constexpr size_t default_order = 1;  // linear interpolation by default\n    for (size_t p = 0; p < number_of_target_points; ++p) {\n      const double xi_target = get_element(get<0>(points), p);\n      const double eta_target = get_element(get<1>(points), p);\n      const double zeta_target = get_element(get<2>(points), p);\n      std::vector<double> xi_stencil;\n      std::vector<double> eta_stencil;\n      std::vector<double> zeta_stencil;\n      switch (fd_to_fd_interp_order.value_or(default_order)) {\n        case 1: {\n          xi_stencil = fd_stencil<1>(xi_source, xi_target);\n          eta_stencil = fd_stencil<1>(eta_source, eta_target);\n          zeta_stencil = fd_stencil<1>(zeta_source, zeta_target);\n          break;\n        }\n        case 2: {\n          xi_stencil = fd_stencil<2>(xi_source, xi_target);\n          eta_stencil = fd_stencil<2>(eta_source, eta_target);\n          zeta_stencil = fd_stencil<2>(zeta_source, zeta_target);\n          break;\n        }\n        case 3: {\n          xi_stencil = fd_stencil<3>(xi_source, xi_target);\n          eta_stencil = fd_stencil<3>(eta_source, eta_target);\n          zeta_stencil = fd_stencil<3>(zeta_source, zeta_target);\n          break;\n        }\n        default:\n          ERROR(\"Unsupported fd_to_fd_interp_order: \"\n                << fd_to_fd_interp_order.value_or(default_order)\n                << \"; expected 1, 2, or 3.\");\n      }\n      for (size_t k = 0, s = 0; k < mesh.extents(2); ++k) {\n        for (size_t j = 0; j < mesh.extents(1); ++j) {\n          for (size_t i = 0; i < mesh.extents(0); ++i) {\n            result(p, s) = xi_stencil[i] * eta_stencil[j] * zeta_stencil[k];\n            ++s;\n          }\n        }\n      }\n    }\n    return result;\n  } else if (mesh.basis() == std::array{Spectral::Basis::FiniteDifference,\n                                        Spectral::Basis::FiniteDifference,\n                                        Spectral::Basis::Cartoon}) {\n    // Axial Symmetry -> call 2D implementation\n    tnsr::I<DataType, 2, Frame::ElementLogical> points_2d{};\n    if constexpr (std::is_same_v<DataType, DataVector>) {\n      get<0>(points_2d).set_data_ref(\n          make_not_null(const_cast<DataType*>(&get<0>(points))));  // NOLINT\n      get<1>(points_2d).set_data_ref(\n          make_not_null(const_cast<DataType*>(&get<1>(points))));  // NOLINT\n    } else {\n      get<0>(points_2d) = get<0>(points);\n      get<1>(points_2d) = get<1>(points);\n    }\n    return interpolation_matrix(mesh.slice_through(0, 1), points_2d,\n                                fd_to_fd_interp_order);\n  } else if (mesh.basis() == std::array{Spectral::Basis::FiniteDifference,\n                                        Spectral::Basis::Cartoon,\n                                        Spectral::Basis::Cartoon}) {\n    // Spherical Symmetry -> call 1D implementation\n    tnsr::I<DataType, 1, Frame::ElementLogical> points_1d{};\n    if constexpr (std::is_same_v<DataType, DataVector>) {\n      get<0>(points_1d).set_data_ref(\n          make_not_null(const_cast<DataType*>(&get<0>(points))));  // NOLINT\n    } else {\n      get<0>(points_1d) = get<0>(points);\n    }\n    return interpolation_matrix(mesh.slice_through(0), points_1d,\n                                fd_to_fd_interp_order);\n  } else if (mesh.basis()[1] == Spectral::Basis::SphericalHarmonic) {\n    ASSERT(mesh.basis()[2] == Spectral::Basis::SphericalHarmonic,\n           \"Expected last two dimensions to each have spherical harmonic \"\n           \"basis. Mesh = \"\n               << mesh);\n    const intrp::Cardinal cardinal_interpolator(mesh, points);\n    const auto [n_r, n_th, n_ph] = mesh.extents().indices();\n    const auto& [m_r, m_th, m_ph] =\n        cardinal_interpolator.interpolation_matrices();\n    for (size_t i_ph = 0, s = 0; i_ph < n_ph; ++i_ph) {\n      for (size_t i_th = 0; i_th < n_th; ++i_th) {\n        for (size_t i_r = 0; i_r < n_r; ++i_r) {\n          for (size_t k = 0; k < number_of_target_points; ++k) {\n            result(k, s) =\n                m_r(k, i_r) * (m_th(k, i_th) * m_ph(2 * k, i_ph) +\n                               m_th(k, i_th + n_th) * m_ph(2 * k + 1, i_ph));\n          }\n          ++s;\n        }\n      }\n    }\n    return result;\n  } else if (mesh.basis()[0] == Spectral::Basis::ZernikeB2) {\n    ASSERT(mesh.basis()[1] == Spectral::Basis::ZernikeB2,\n           \"Unexpected basis combination: \" << mesh.basis());\n    const intrp::Cardinal cardinal_interpolator(mesh, points);\n    const auto& interpolation_matrices =\n        cardinal_interpolator.interpolation_matrices();\n    compute_zernikeb2_interpolation<3>(make_not_null(&result), mesh,\n                                       interpolation_matrices,\n                                       number_of_target_points);\n    return result;\n  }\n  ASSERT(mesh.basis(0) != Spectral::Basis::FiniteDifference and\n             mesh.basis(1) != Spectral::Basis::FiniteDifference and\n             mesh.basis(2) != Spectral::Basis::FiniteDifference,\n         \"Mixed FD and DG bases are not supported. Mesh = \" << mesh);\n\n  // Not FD or special basis, so use 1D spectral interpolation matrices\n  const std::array<Matrix, 3> matrices{\n      {Spectral::interpolation_matrix(mesh.slice_through(0), get<0>(points)),\n       Spectral::interpolation_matrix(mesh.slice_through(1), get<1>(points)),\n       Spectral::interpolation_matrix(mesh.slice_through(2), get<2>(points))}};\n\n  // First dimension of DataVector varies fastest.\n  for (size_t k = 0, s = 0; k < mesh.extents(2); ++k) {\n    for (size_t j = 0; j < mesh.extents(1); ++j) {\n      for (size_t i = 0; i < mesh.extents(0); ++i) {\n        for (size_t p = 0; p < number_of_target_points; ++p) {\n          result(p, s) =\n              matrices[0](p, i) * matrices[1](p, j) * matrices[2](p, k);\n        }\n        ++s;\n      }\n    }\n  }\n  return result;\n}\n}  // namespace\n\nnamespace intrp {\n\ntemplate <size_t Dim>\nIrregular<Dim>::Irregular() = default;\n\ntemplate <size_t Dim>\nIrregular<Dim>::Irregular(\n    const Mesh<Dim>& source_mesh,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& target_points,\n    const std::optional<size_t>& fd_to_fd_interp_order)\n    : interpolation_matrix_(interpolation_matrix(source_mesh, target_points,\n                                                 fd_to_fd_interp_order)) {}\n\ntemplate <size_t Dim>\nIrregular<Dim>::Irregular(\n    const Mesh<Dim>& source_mesh,\n    const tnsr::I<double, Dim, Frame::ElementLogical>& target_point,\n    const std::optional<size_t>& fd_to_fd_interp_order)\n    : interpolation_matrix_(interpolation_matrix(source_mesh, target_point,\n                                                 fd_to_fd_interp_order)) {}\n\ntemplate <size_t Dim>\nvoid Irregular<Dim>::pup(PUP::er& p) {\n  p | interpolation_matrix_;\n}\n\ntemplate <size_t Dim>\nvoid Irregular<Dim>::interpolate(const gsl::not_null<DataVector*> result,\n                                 const DataVector& input) const {\n  const size_t m = interpolation_matrix_.rows();\n  const size_t k = interpolation_matrix_.columns();\n  ASSERT(k == input.size(),\n         \"Number of points in 'input', \"\n             << input.size()\n             << \",\\n disagrees with the size of the source_mesh, \" << k\n             << \", that was passed into the constructor\");\n  if (result->size() != m) {\n    result->destructive_resize(m);\n  }\n  dgemv_('n', m, k, 1.0, interpolation_matrix_.data(),\n         interpolation_matrix_.spacing(), input.data(), 1, 0.0, result->data(),\n         1);\n}\n\ntemplate <size_t Dim>\nDataVector Irregular<Dim>::interpolate(const DataVector& input) const {\n  DataVector result{input.size()};\n  interpolate(make_not_null(&result), input);\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid Irregular<Dim>::interpolate(const gsl::not_null<ComplexDataVector*> result,\n                                 const ComplexDataVector& input) const {\n  const size_t m = interpolation_matrix_.rows();\n  const size_t k = interpolation_matrix_.columns();\n  ASSERT(k == input.size(),\n         \"Number of points in 'input', \"\n             << input.size()\n             << \",\\n disagrees with the size of the source_mesh, \" << k\n             << \", that was passed into the constructor\");\n  if (result->size() != m) {\n    result->destructive_resize(m);\n  }\n  // Possible performance optimization: can possibly be written as a single\n  // `dgemm` call, or might be faster using `zgemv`.\n  // NOLINTBEGIN\n  dgemv_('n', m, k, 1.0, interpolation_matrix_.data(),\n         interpolation_matrix_.spacing(),\n         reinterpret_cast<const double*>(input.data()), 2, 0.0,\n         reinterpret_cast<double*>(result->data()), 2);\n  dgemv_('n', m, k, 1.0, interpolation_matrix_.data(),\n         interpolation_matrix_.spacing(),\n         reinterpret_cast<const double*>(input.data()) + 1, 2, 0.0,\n         reinterpret_cast<double*>(result->data()) + 1, 2);\n  // NOLINTEND\n}\n\ntemplate <size_t Dim>\nComplexDataVector Irregular<Dim>::interpolate(\n    const ComplexDataVector& input) const {\n  ComplexDataVector result{input.size()};\n  interpolate(make_not_null(&result), input);\n  return result;\n}\n\nnamespace {\n\ntemplate <typename ValueType>\nvoid span_interpolate_impl(const gsl::not_null<gsl::span<ValueType>*> result,\n                           const gsl::span<const ValueType>& input,\n                           const Matrix& interpolation_matrix) {\n  const size_t m = interpolation_matrix.rows();\n  const size_t k = interpolation_matrix.columns();\n  ASSERT(input.size() % k == 0,\n         \"Number of points in 'input', \"\n             << input.size()\n             << \",\\n must be a multiple of the source grid points, \" << k\n             << \", that was passed into the constructor\");\n  const size_t number_of_components = input.size() / k;\n  ASSERT(result->size() == number_of_components * m,\n         \"The result must be of size \" << number_of_components * m\n                                       << \" but got \" << result->size());\n  if constexpr (std::is_same_v<ValueType, double>) {\n    dgemm_<true>('N', 'N', m, number_of_components, k, 1.0,\n                 interpolation_matrix.data(), interpolation_matrix.spacing(),\n                 input.data(), k, 0.0, result->data(), m);\n  } else if constexpr (std::is_same_v<ValueType, float>) {\n    // No BLAS function exists for mixed precision so we do the matrix multiply\n    // manually. A possible performance optimization would be to compute the\n    // interpolation matrix in single precision and then use sgemm.\n    for (size_t j = 0; j < number_of_components; ++j) {\n      for (size_t i = 0; i < m; ++i) {\n        float sum = interpolation_matrix.data()[i] * input[j * k];\n        for (size_t l = 1; l < k; ++l) {\n          sum += interpolation_matrix.data()[i + l * m] * input[l + j * k];\n        }\n        (*result)[i + j * m] = sum;\n      }\n    }\n  } else if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n    // BLAS zgemm operates on complex matrices, so we need to copy the real\n    // matrix to a complex matrix with zero imaginary part before calling zgemm.\n    // Note by Nils Vu (Aug 2024): Profiling of partial derivatives showed that\n    // this zgemm approach with a complex matrix is still faster than\n    // transposing the complex input data and applying the real matrix with\n    // dgemm (though maybe the transpose can be avoided with smarter dgemm\n    // strides). Possible performance optimization: avoid the copy here by\n    // caching the complex matrix.\n    const blaze::DynamicMatrix<std::complex<double>, blaze::columnMajor>\n        matrix_complex{interpolation_matrix};\n    zgemm_<true>('N', 'N', m, number_of_components, k,\n                 std::complex<double>{1.0, 0.0}, matrix_complex.data(),\n                 matrix_complex.spacing(), input.data(), k,\n                 std::complex<double>{0.0, 0.0}, result->data(), m);\n  }\n}\n}  // namespace\n\ntemplate <size_t Dim>\nvoid Irregular<Dim>::interpolate(const gsl::not_null<gsl::span<double>*> result,\n                                 const gsl::span<const double>& input) const {\n  span_interpolate_impl(result, input, interpolation_matrix_);\n}\n\ntemplate <size_t Dim>\nvoid Irregular<Dim>::interpolate(\n    const gsl::not_null<gsl::span<std::complex<double>>*> result,\n    const gsl::span<const std::complex<double>>& input) const {\n  span_interpolate_impl(result, input, interpolation_matrix_);\n}\n\ntemplate <size_t Dim>\nvoid Irregular<Dim>::interpolate(const gsl::not_null<gsl::span<float>*> result,\n                                 const gsl::span<const float>& input) const {\n  span_interpolate_impl(result, input, interpolation_matrix_);\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Irregular<Dim>& lhs, const Irregular<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate class Irregular<1>;\ntemplate class Irregular<2>;\ntemplate class Irregular<3>;\ntemplate bool operator!=(const Irregular<1>& lhs, const Irregular<1>& rhs);\ntemplate bool operator!=(const Irregular<2>& lhs, const Irregular<2>& rhs);\ntemplate bool operator!=(const Irregular<3>& lhs, const Irregular<3>& rhs);\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\n/// \\endcond\n\nnamespace intrp {\n\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Interpolates a `Variables` onto an arbitrary set of points.\n///\n/// \\details If the `source_mesh` uses Spectral::Basis::FiniteDifference,\n/// linear interpolation is done in each dimension; otherwise it uses the\n/// barycentric interpolation provided by Spectral::interpolation_matrix in each\n/// dimension.\ntemplate <size_t Dim>\nclass Irregular {\n public:\n  Irregular(\n      const Mesh<Dim>& source_mesh,\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical>& target_points,\n      const std::optional<size_t>& fd_to_fd_interp_order = std::nullopt);\n  Irregular(const Mesh<Dim>& source_mesh,\n            const tnsr::I<double, Dim, Frame::ElementLogical>& target_point,\n            const std::optional<size_t>& fd_to_fd_interp_order = std::nullopt);\n  Irregular();\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// @{\n  /// Performs the interpolation on a `Variables` with grid points corresponding\n  /// to the `Mesh<Dim>` specified in the constructor.\n  /// The result is a `Variables` whose internal `DataVector` goes over the\n  /// list of target_points that were specified in the constructor.\n  /// \\note for the void function, `result` will be resized to the proper size.\n  template <typename TagsList>\n  void interpolate(gsl::not_null<Variables<TagsList>*> result,\n                   const Variables<TagsList>& vars) const;\n  template <typename TagsList>\n  Variables<TagsList> interpolate(const Variables<TagsList>& vars) const;\n  /// @}\n\n  /// @{\n  /// \\brief Interpolate a DataVector or ComplexDataVector onto the target\n  /// points.\n  ///\n  /// \\note When interpolating multiple tensors, the Variables interface is more\n  /// efficient. However, this DataVector interface is useful for applications\n  /// where only some components of a Tensor or Variables need to be\n  /// interpolated.\n  void interpolate(gsl::not_null<DataVector*> result,\n                   const DataVector& input) const;\n  DataVector interpolate(const DataVector& input) const;\n  void interpolate(gsl::not_null<ComplexDataVector*> result,\n                   const ComplexDataVector& input) const;\n  ComplexDataVector interpolate(const ComplexDataVector& input) const;\n  /// @}\n\n  /// \\brief Interpolate multiple variables on the grid to the target points.\n  /// @{\n  void interpolate(gsl::not_null<gsl::span<double>*> result,\n                   const gsl::span<const double>& input) const;\n  void interpolate(gsl::not_null<gsl::span<std::complex<double>>*> result,\n                   const gsl::span<const std::complex<double>>& input) const;\n  void interpolate(gsl::not_null<gsl::span<float>*> result,\n                   const gsl::span<const float>& input) const;\n  /// @}\n\n private:\n  friend bool operator==(const Irregular& lhs, const Irregular& rhs) {\n    return lhs.interpolation_matrix_ == rhs.interpolation_matrix_;\n  }\n  Matrix interpolation_matrix_;\n};\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid Irregular<Dim>::interpolate(\n    const gsl::not_null<Variables<TagsList>*> result,\n    const Variables<TagsList>& vars) const {\n  if (UNLIKELY(result->number_of_grid_points() !=\n               interpolation_matrix_.rows())) {\n    *result = Variables<TagsList>(interpolation_matrix_.rows(), 0.);\n  }\n  ASSERT(interpolation_matrix_.columns() == vars.number_of_grid_points(),\n         \"Number of grid points in source 'vars', \"\n             << vars.number_of_grid_points()\n             << \",\\n disagrees with the size of the source_mesh, \"\n             << interpolation_matrix_.columns()\n             << \", that was passed into the constructor\");\n  auto result_span = gsl::make_span(result->data(), result->size());\n  const auto vars_span = gsl::make_span(vars.data(), vars.size());\n  interpolate(make_not_null(&result_span), vars_span);\n}\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nVariables<TagsList> Irregular<Dim>::interpolate(\n    const Variables<TagsList>& vars) const {\n  Variables<TagsList> result{interpolation_matrix_.rows()};\n  interpolate(make_not_null(&result), vars);\n  return result;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Irregular<Dim>& lhs, const Irregular<Dim>& rhs);\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/LagrangePolynomial.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions lagrange_polynomial\n\n#pragma once\n\n#include <iterator>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\n/// Evaluate the jth Lagrange interpolating polynomial with the given\n/// control points where j is the index of index_point.\ntemplate <typename Iterator>\ndouble lagrange_polynomial(const Iterator& index_point,\n                           double x,\n                           const Iterator& control_points_begin,\n                           const Iterator& control_points_end) {\n  ASSERT(control_points_begin != control_points_end, \"No control points\");\n\n  const double x_j = *index_point;\n  double result = 1.;\n  for (auto m_it = control_points_begin; m_it != control_points_end; ++m_it) {\n    if (m_it == index_point) { continue; }\n    const double x_m = *m_it;\n    result *= (x_m - x) / (x_m - x_j);\n  }\n  return result;\n}\n\n/// Evaluate the jth (zero-indexed) Lagrange interpolating polynomial\n/// with the given control points.\ntemplate <typename Iterator>\ndouble lagrange_polynomial(size_t j, double x,\n                           const Iterator& control_points_begin,\n                           const Iterator& control_points_end) {\n  const auto j_diff =\n      static_cast<typename std::iterator_traits<Iterator>::difference_type>(j);\n  ASSERT(j_diff < std::distance(control_points_begin, control_points_end),\n         \"Polynomial number out of range \" << j << \" > \"\n         << (std::distance(control_points_begin, control_points_end) - 1));\n  return lagrange_polynomial(std::next(control_points_begin, j_diff),\n                             x, control_points_begin, control_points_end);\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/LinearLeastSquares.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/LinearLeastSquares.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <deque>\n#include <gsl/gsl_matrix.h>\n#include <gsl/gsl_multifit.h>\n#include <gsl/gsl_vector.h>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace intrp {\nnamespace {\ntemplate <size_t Order, typename T>\nstd::array<double, Order + 1> least_squares_impl(\n    const gsl::not_null<gsl_matrix*> X,\n    const gsl::not_null<gsl_matrix*> covariance_matrix,\n    const gsl::not_null<gsl_vector*> y, const gsl::not_null<gsl_vector*> c,\n    const gsl::not_null<gsl_multifit_linear_workspace*> work, const T& x_values,\n    const T& y_values) {\n  ASSERT(x_values.size() == y_values.size(),\n         \"The x_values and y_values must be of the same size\");\n  std::array<double, Order + 1> coefficients{{}};\n  for (size_t i = 0; i < x_values.size(); i++) {\n    for (size_t j = 0; j < Order + 1; j++) {\n      gsl_matrix_set(X.get(), i, j, pow(x_values[i], j));\n    }\n    gsl_vector_set(y.get(), i, y_values[i]);\n  }\n\n  double chisq = 0.0;\n  gsl_multifit_linear(X.get(), y.get(), c.get(), covariance_matrix.get(),\n                      &chisq, work.get());\n\n  for (size_t i = 0; i < Order + 1; i++) {\n    gsl::at(coefficients, i) = gsl_vector_get(c.get(), i);\n  }\n  return coefficients;\n}\n\nstruct FreeVector {\n  void operator()(gsl_vector* const p) { gsl_vector_free(p); }\n};\n\nstruct FreeMatrix {\n  void operator()(gsl_matrix* const p) { gsl_matrix_free(p); }\n};\n\nstruct FreeWork {\n  void operator()(gsl_multifit_linear_workspace* const p) {\n    gsl_multifit_linear_free(p);\n  }\n};\n}  // namespace\n\ntemplate <size_t Order, typename T>\nstd::array<double, Order + 1> linear_least_squares(const T& x_values,\n                                                   const T& y_values) {\n  const std::unique_ptr<gsl_matrix, FreeMatrix> X(\n      gsl_matrix_alloc(x_values.size(), Order + 1));\n  const std::unique_ptr<gsl_vector, FreeVector> y(\n      gsl_vector_alloc(x_values.size()));\n  const std::unique_ptr<gsl_vector, FreeVector> c(gsl_vector_alloc(Order + 1));\n  const std::unique_ptr<gsl_matrix, FreeMatrix> covariance_matrix(\n      gsl_matrix_alloc(Order + 1, Order + 1));\n  const std::unique_ptr<gsl_multifit_linear_workspace, FreeWork> work(\n      gsl_multifit_linear_alloc(x_values.size(), Order + 1));\n  return least_squares_impl<Order>(X.get(), covariance_matrix.get(), y.get(),\n                                   c.get(), work.get(), x_values, y_values);\n}\n\ntemplate <size_t Order, typename T>\nstd::vector<std::array<double, Order + 1>> linear_least_squares(\n    const T& x_values, const std::vector<T>& y_values) {\n  const std::unique_ptr<gsl_matrix, FreeMatrix> X(\n      gsl_matrix_alloc(x_values.size(), Order + 1));\n  const std::unique_ptr<gsl_vector, FreeVector> y(\n      gsl_vector_alloc(x_values.size()));\n  const std::unique_ptr<gsl_vector, FreeVector> c(gsl_vector_alloc(Order + 1));\n  const std::unique_ptr<gsl_matrix, FreeMatrix> covariance_matrix(\n      gsl_matrix_alloc(Order + 1, Order + 1));\n  const std::unique_ptr<gsl_multifit_linear_workspace, FreeWork> work(\n      gsl_multifit_linear_alloc(x_values.size(), Order + 1));\n  std::vector<std::array<double, Order + 1>> fit_coeffs{};\n  for (size_t curve_index = 0; curve_index < y_values.size(); ++curve_index) {\n    fit_coeffs.push_back(least_squares_impl<Order>(\n        X.get(), covariance_matrix.get(), y.get(), c.get(), work.get(),\n        x_values, y_values[curve_index]));\n  }\n  return fit_coeffs;\n}\n\n// Explicit instantiations\n#define ORDER(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define INSTANTIATE(_, data)                                       \\\n  template std::array<double, ORDER(data) + 1>                     \\\n  linear_least_squares<ORDER(data)>(const DTYPE(data) & x_values,  \\\n                                    const DTYPE(data) & y_values); \\\n  template std::vector<std::array<double, ORDER(data) + 1>>        \\\n  linear_least_squares<ORDER(data)>(const DTYPE(data) & x_values,  \\\n                                    const std::vector<DTYPE(data)>& y_values);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3, 4),\n                        (std::vector<double>, DataVector, ModalVector,\n                         gsl::span<double>, std::deque<double>))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef ORDER\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/LinearLeastSquares.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\nnamespace intrp {\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief A linear least squares solver\n *\n * A wrapper class for the gsl linear least squares solver which determines\n * the coefficients of best fit for a polynomial of order `Order` given a set\n * of data points `x_values` and `y_values` representing some function y(x).\n * The coefficients can be passed to \\ref evaluate_polynomial for\n * interpolation.\n *\n * The form taking and returning vectors performs several fits in sequence,\n * reusing internal allocations for efficiency.\n *\n * The details of the linear least squares solver can be seen here:\n * [GSL documentation](https://www.gnu.org/software/gsl/doc/html/lls.html#).\n */\n/// @{\ntemplate <size_t Order, typename T>\nstd::array<double, Order + 1> linear_least_squares(const T& x_values,\n                                                   const T& y_values);\ntemplate <size_t Order, typename T>\nstd::vector<std::array<double, Order + 1>> linear_least_squares(\n    const T& x_values, const std::vector<T>& y_values);\n/// @}\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/LinearRegression.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/LinearRegression.hpp\"\n\n#include <cmath>\n#include <gsl/gsl_fit.h>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace intrp {\ntemplate <typename T>\nLinearRegressionResult linear_regression(const T& x_values, const T& y_values) {\n  ASSERT(x_values.size() == y_values.size(),\n         \"Size mismatch: \" << x_values.size() << \" vs \" << y_values.size());\n  LinearRegressionResult result{0.0, 0.0, 0.0, 0.0};\n  double cov01 = 0.0;  // Off-diagonal component of covariance matrix.\n  double sumsq = 0.0;  // Sum of the squares of residuals of the best fit.\n  gsl_fit_linear(x_values.data(), 1, y_values.data(), 1, x_values.size(),\n                 &result.intercept, &result.slope, &result.delta_intercept,\n                 &cov01, &result.delta_slope, &sumsq);\n  // After gsl_fit_linear is called, result.delta_intercept and\n  // result.delta_slope hold diagonal components of the covariance matrix.  But\n  // the error bars are the sqrt of those diagonal components, so take those\n  // sqrts here.\n  result.delta_intercept = sqrt(result.delta_intercept);\n  result.delta_slope = sqrt(result.delta_slope);\n  return result;\n}\n}  // namespace intrp\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                       \\\n  template intrp::LinearRegressionResult intrp::linear_regression( \\\n      const DTYPE(data) & x_values, const DTYPE(data) & y_values);\nGENERATE_INSTANTIATIONS(INSTANTIATE, (std::vector<double>, DataVector))\n#undef INSTANTIATE\n#undef DTYPE\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/LinearRegression.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace intrp {\n\nstruct LinearRegressionResult {\n  double intercept;\n  double slope;\n  double delta_intercept;\n  double delta_slope;\n};\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief A linear regression function.\n *\n * A wrapper for gsl linear regression.\n * Fits the data to \\f$y = m x + b\\f$. Returns a struct\n * containing \\f$(b, m, \\delta b, \\delta m)\\f$, where\n * \\f$\\delta b\\f$ and \\f$\\delta m\\f$ are the error bars in \\f$b\\f$ and\n * \\f$m\\f$.  The error bars are computed assuming unknown errors in \\f$y\\f$.\n *\n * linear_regression could be implemented by calling\n * LinearLeastSquares using Order=1, but we choose instead to\n * implement linear_regression in a simpler way, by calling a simpler gsl\n * function that involves no memory allocations, no copying, and no\n * `pow` functions.\n */\ntemplate <typename T>\nLinearRegressionResult linear_regression(const T& x_values, const T& y_values);\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/LinearSpanInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n\n#include <complex>\n\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace intrp {\n\nnamespace {\ntemplate <typename ValueType>\nSPECTRE_ALWAYS_INLINE ValueType interpolate_impl(\n    const gsl::span<const double>& source_points,\n    const gsl::span<const ValueType>& values, const double target_point) {\n  return values[0] + (values[1] - values[0]) /\n                         (source_points[1] - source_points[0]) *\n                         (target_point - source_points[0]);\n}\n}  // namespace\n\ndouble LinearSpanInterpolator::interpolate(\n    const gsl::span<const double>& source_points,\n    const gsl::span<const double>& values, const double target_point) const {\n  return interpolate_impl(source_points, values, target_point);\n}\n\nstd::complex<double> LinearSpanInterpolator::interpolate(\n    const gsl::span<const double>& source_points,\n    const gsl::span<const std::complex<double>>& values,\n    const double target_point) const {\n  return interpolate_impl(source_points, values, target_point);\n}\n\nPUP::able::PUP_ID intrp::LinearSpanInterpolator::my_PUP_ID = 0;\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace intrp {\n\n/// \\brief Performs a linear interpolation; this class can be chosen via the\n/// options factory mechanism as a possible `SpanInterpolator`\nclass LinearSpanInterpolator : public SpanInterpolator {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"Linear interpolator.\"};\n\n  LinearSpanInterpolator() = default;\n  LinearSpanInterpolator(const LinearSpanInterpolator&) = default;\n  LinearSpanInterpolator& operator=(const LinearSpanInterpolator&) = default;\n  LinearSpanInterpolator(LinearSpanInterpolator&&) = default;\n  LinearSpanInterpolator& operator=(LinearSpanInterpolator&&) = default;\n  ~LinearSpanInterpolator() override = default;\n\n  WRAPPED_PUPable_decl_template(LinearSpanInterpolator);  // NOLINT\n\n  explicit LinearSpanInterpolator(CkMigrateMessage* /*unused*/) {}\n\n  // clang-tidy: do not pass by non-const reference\n  void pup(PUP::er& /*p*/) override {}\n\n  std::unique_ptr<SpanInterpolator> get_clone() const override {\n    return std::make_unique<LinearSpanInterpolator>(*this);\n  }\n\n  double interpolate(const gsl::span<const double>& source_points,\n                     const gsl::span<const double>& values,\n                     double target_point) const override;\n\n  std::complex<double> interpolate(\n      const gsl::span<const double>& source_points,\n      const gsl::span<const std::complex<double>>& values,\n      double target_point) const;\n\n  size_t required_number_of_points_before_and_after() const override {\n    return 1;\n  }\n};\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/MultiLinearSpanInterpolation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <concepts>\n#include <memory>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace intrp {\n\n/*!\n * \\brief Performs linear interpolation in arbitrary dimensions.\n *  The class is non-owning and expects a C-ordered array, (n, x, y, z).\n *  The variable index, n, varies fastest in memory.\n *  Note that this class is intentionally non-pupable.\n *\n *  \\tparam Dimension dimensionality of the table\n *  \\tparam NumberOfVariables number of variables stored in the table\n *  \\tparam UniformSpacing indicated whether the table has uniform _spacing or\n * not. This is useful for performance reasons in finding the appropriate table\n * index.\n */\n\ntemplate <size_t Dimension, size_t NumberOfVariables, bool UniformSpacing>\nclass MultiLinearSpanInterpolation {\n\n public:\n  template <size_t ThisDimension>\n  struct Weight {\n    std::array<double, two_to_the(ThisDimension)> weights;\n    Index<two_to_the(ThisDimension)> index;\n  };\n\n  size_t extents(const size_t which_dimension) const {\n    return number_of_points_[which_dimension];\n  }\n\n  void extrapolate_above_data(const size_t which_dimension, const bool value) {\n    allow_extrapolation_abov_data_[which_dimension] = value;\n  }\n\n  void extrapolate_below_data(const size_t which_dimension, const bool value) {\n    allow_extrapolation_below_data_[which_dimension] = value;\n  }\n\n  /// Compute interpolation weights for 1D tables\n  Weight<1> get_weights(const double x1) const;\n\n  /// Compute interpolation weights for 2D tables\n  Weight<2> get_weights(const double x1, const double x2) const;\n\n  /// Compute interpolation weights for 3D tables\n  Weight<3> get_weights(const double x1, const double x2,\n                        const double x3) const;\n\n  double interpolate(const Weight<Dimension>& weights,\n                     const size_t which_variable = 0) const {\n    double result = 0;\n\n    for (size_t nn = 0; nn < weights.weights.size(); ++nn) {\n      result += weights.weights[nn] *\n                y_[which_variable + NumberOfVariables * weights.index[nn]];\n    }\n\n    return result;\n  }\n\n  template <size_t... variables_to_interpolate>\n  std::array<double, sizeof...(variables_to_interpolate)> interpolate(\n      const Weight<Dimension>& weights) const {\n    static_assert(sizeof...(variables_to_interpolate) <= NumberOfVariables,\n                  \"You are trying to interpolate more variables than this \"\n                  \"container holds.\");\n\n    return std::array<double, sizeof...(variables_to_interpolate)>{\n        interpolate(weights, variables_to_interpolate)...};\n  }\n\n  template <size_t NumberOfVariablesToInterpolate, std::floating_point... T>\n  std::array<double, NumberOfVariablesToInterpolate> interpolate(\n      std::array<size_t, NumberOfVariablesToInterpolate>&\n          variables_to_interpolate,\n      const T&... target_points) const {\n    static_assert(\n        sizeof...(T) == Dimension,\n        \"You need to provide the correct number of interpolation points\");\n    static_assert(NumberOfVariablesToInterpolate <= NumberOfVariables,\n                  \"You are trying to interpolate more variables than this \"\n                  \"container holds.\");\n\n    auto weights = get_weights(target_points...);\n\n    std::array<double, NumberOfVariablesToInterpolate> interpolatedValues;\n\n    for (size_t nn = 0; nn < NumberOfVariablesToInterpolate; ++nn) {\n      interpolatedValues[nn] =\n          interpolate(weights, variables_to_interpolate[nn]);\n    }\n\n    return interpolatedValues;\n  }\n\n  template <size_t NumberOfVariablesToInterpolate, size_t... I>\n  std::array<double, NumberOfVariablesToInterpolate> interpolate(\n      std::array<size_t, NumberOfVariablesToInterpolate>&\n          variables_to_interpolate,\n      std::array<double, Dimension>& target_points,\n      std::index_sequence<I...>) const {\n    return interpolate(variables_to_interpolate, target_points[I]...);\n  }\n\n  template <size_t NumberOfVariablesToInterpolate>\n  std::array<double, NumberOfVariablesToInterpolate> interpolate(\n      std::array<size_t, NumberOfVariablesToInterpolate>&\n          variables_to_interpolate,\n      std::array<double, Dimension>& target_points) const {\n    return interpolate(variables_to_interpolate, target_points,\n                       std::make_index_sequence<Dimension>{});\n  }\n\n  MultiLinearSpanInterpolation() = default;\n\n  MultiLinearSpanInterpolation(\n      std::array<gsl::span<const double>, Dimension> x_,\n      gsl::span<const double> y_, Index<Dimension> number_of_points__);\n\n  double lower_bound(const size_t which_dimension) const {\n    return x_[which_dimension][0];\n  }\n\n  double upper_bound(const size_t which_dimension) const {\n    return x_[which_dimension][number_of_points_[which_dimension] - 1];\n  }\n\n private:\n  /// Allow extrapolation above table bounds.\n  std::array<bool, Dimension> allow_extrapolation_below_data_;\n  /// Allow extrapolation below table bounds.\n  std::array<bool, Dimension> allow_extrapolation_abov_data_;\n  /// Inverse pacing of the table. Only used for uniform grids\n  std::array<double, Dimension> inverse_spacing_;\n  /// Spacing of the table. Only used for uniform grids\n  std::array<double, Dimension> spacing_;\n  /// Number of points per dimension\n  Index<Dimension> number_of_points_;\n\n  using DataPointer = gsl::span<const double>;\n  /// X values of the table. Only used if allocated\n  std::array<DataPointer, Dimension> x_;\n  /// Y values of the table. Only used if allocated\n  DataPointer y_;\n\n  /// Determine relative index bracketing for non-uniform _spacing\n  size_t find_index_general(const size_t which_dimension,\n                            const double& target_points) const;\n\n  /// Determine relative index bracketing for non-uniform _spacing\n  size_t find_index_uniform(const size_t which_dimension,\n                            const double& target_points) const;\n\n  size_t find_index(const size_t which_dimension,\n                    const double& target_points) const {\n    if constexpr (UniformSpacing) {\n      return find_index_uniform(which_dimension, target_points);\n    } else {\n      return find_index_general(which_dimension, target_points);\n    }\n  }\n};\n\ntemplate <size_t Dimension, size_t NumberOfVariables, bool UniformSpacing>\nsize_t MultiLinearSpanInterpolation<\n    Dimension, NumberOfVariables,\n    UniformSpacing>::find_index_general(const size_t which_dimension,\n                                        const double& target_points) const {\n  size_t lower_index_bound = 0;\n  size_t upper_index_bound = number_of_points_[which_dimension] - 1;\n\n  ASSERT((target_points > x_[which_dimension][lower_index_bound]) or\n             allow_extrapolation_below_data_[which_dimension],\n         \"Interpolation exceeds lower table bounds. \\nwhich_dimension: \"\n             << which_dimension\n             << \"\\nnumber of points: \" << number_of_points_[which_dimension]\n             << \"\\ntarget point: \" << target_points);\n  ASSERT((target_points < x_[which_dimension][upper_index_bound]) or\n             allow_extrapolation_abov_data_[which_dimension],\n         \"Interpolation exceeds upper table bounds. \\nwhich_dimension: \"\n             << which_dimension\n             << \"\\nnumber of points: \" << number_of_points_[which_dimension]\n             << \"\\ntarget point: \" << target_points);\n\n  while (upper_index_bound > 1 + lower_index_bound) {\n    size_t current_index =\n        lower_index_bound + (upper_index_bound - lower_index_bound) / 2;\n    if (target_points < x_[which_dimension][current_index]) {\n      upper_index_bound = current_index;\n    } else {\n      lower_index_bound = current_index;\n    }\n  }\n  return lower_index_bound;\n}\n\n/// Determine relative index bracketing for non-uniform _spacing\ntemplate <size_t Dimension, size_t NumberOfVariables, bool UniformSpacing>\nsize_t MultiLinearSpanInterpolation<\n    Dimension, NumberOfVariables,\n    UniformSpacing>::find_index_uniform(const size_t which_dimension,\n                                        const double& target_points) const {\n  // Compute coordinate relative to lowest table bound\n  const auto relative_coordinate = (target_points - x_[which_dimension][0]);\n\n  auto current_index = static_cast<size_t>(relative_coordinate *\n                                           inverse_spacing_[which_dimension]);\n\n  // We are exceeding the table bounds:\n  // Use linear extrapolation based of the lowest\n  // two points in the table\n  ASSERT(allow_extrapolation_below_data_[which_dimension] or\n             UNLIKELY(relative_coordinate >= 0.),\n         \"Interpolation exceeds lower table bounds.\\nwhich_dimension: \"\n             << which_dimension << \"\\ncurrent_index: \" << current_index\n             << \"\\nnumber of points: \" << number_of_points_[which_dimension]\n             << \"\\ntarget point: \" << target_points\n             << \"\\nrelative coordinate: \" << relative_coordinate\n             << \"\\nspacing: \" << inverse_spacing_[which_dimension]);\n\n  // Do not trigger errors for roundoff errors in index calculation\n  if (UNLIKELY(current_index + 1 == number_of_points_[which_dimension])) {\n    if (relative_coordinate * inverse_spacing_[which_dimension] -\n            static_cast<double>(current_index) <\n        1.e-13) {\n      current_index -= 1;\n    }\n  }\n\n  // We are exceeding the table bounds:\n  // Use linear extrapolation based of the highest\n  // two points in the table\n\n  ASSERT(\n      allow_extrapolation_abov_data_[which_dimension] or\n          UNLIKELY(current_index + 1 < number_of_points_[which_dimension]),\n      \"Interpolation exceeds upper table bounds.\\nwhich_dimension: \"\n          << which_dimension << \"\\ncurrent_index: \" << current_index\n          << \"\\nnumber of points: \" << number_of_points_[which_dimension]\n          << \"\\ntarget point: \" << target_points\n          << \"\\nrelative coordinate: \" << relative_coordinate << \"\\nposition: \"\n          << relative_coordinate * inverse_spacing_[which_dimension] -\n                 static_cast<double>(number_of_points_[which_dimension] - 1));\n\n  // Enforce index ranges\n  current_index = std::min(number_of_points_[which_dimension] - 2,\n                           std::max(0ul, current_index));\n\n  return current_index;\n}\n\n/// Compute interpolation weights for 1D tables\ntemplate <size_t Dimension, size_t NumberOfVariables, bool UniformSpacing>\nauto MultiLinearSpanInterpolation<Dimension, NumberOfVariables,\n                                  UniformSpacing>::get_weights(const double x1)\n    const -> Weight<1> {\n  // Relative normalized coordinate in x-direction\n  double xx;\n\n  Weight<1> weights;\n\n  auto index = Index<1>(find_index(0, x1));\n\n  if constexpr (UniformSpacing) {\n    xx = (x1 - x_[0][index[0]]) * inverse_spacing_[0];\n  } else {\n    xx = (x1 - x_[0][index[0]]) / (x_[0][index[0] + 1] - x_[0][index[0]]);\n  }\n\n  // Note: first index varies fastest\n  weights.weights = std::array<double, 2>({\n      (1. - xx),  // 0\n      xx,         // 1\n  });\n\n  // Compute indices\n\n  weights.index[0] = index[0];\n  weights.index[1] = index[0] + 1;\n\n  return weights;\n}\n\n/// Compute interpolation weights for 2D tables\n\ntemplate <size_t Dimension, size_t NumberOfVariables, bool UniformSpacing>\nauto MultiLinearSpanInterpolation<Dimension, NumberOfVariables,\n                                  UniformSpacing>::get_weights(const double x1,\n                                                               const double x2)\n    const -> Weight<2> {\n  Weight<2> weights;\n\n  auto index = Index<2>(find_index(0, x1), find_index(1, x2));\n\n  // Relative normalized coordinate in x,y-direction\n  double xx, yy;\n  if constexpr (UniformSpacing) {\n    xx = (x1 - x_[0][index[0]]) * inverse_spacing_[0];\n    yy = (x2 - x_[1][index[1]]) * inverse_spacing_[1];\n  } else {\n    xx = (x1 - x_[0][index[0]]) / (x_[0][index[0] + 1] - x_[0][index[0]]);\n    yy = (x2 - x_[1][index[1]]) / (x_[1][index[1] + 1] - x_[1][index[1]]);\n  }\n\n  // Note: first index varies fastest\n  weights.weights = std::array<double, 4>({\n      (1. - xx) * (1. - yy),  // 00\n      xx * (1. - yy),         // 10\n      (1. - xx) * yy,         // 01\n      xx * yy,                // 11\n  });\n\n  // Compute indices\n  //\n  for (size_t j = 0; j < 2; ++j) {\n    for (size_t i = 0; i < 2; ++i) {\n      auto tmp_index = Index<2>(index[0] + i, index[1] + j);\n      weights.index[i + 2 * j] = collapsed_index(tmp_index, number_of_points_);\n    }\n  }\n\n  return weights;\n}\n\n/// Compute interpolation weights for 3D tables\ntemplate <size_t Dimension, size_t NumberOfVariables, bool UniformSpacing>\nauto MultiLinearSpanInterpolation<Dimension, NumberOfVariables,\n                                  UniformSpacing>::get_weights(const double x1,\n                                                               const double x2,\n                                                               const double x3)\n    const -> Weight<3> {\n  // Relative normalized coordinate in x,y,z-direction\n  double xx, yy, zz;\n\n  Weight<3> weights;\n\n  auto index =\n      Index<3>(find_index(0, x1), find_index(1, x2), find_index(2, x3));\n\n  if constexpr (UniformSpacing) {\n    xx = (x1 - x_[0][index[0]]) * inverse_spacing_[0];\n    yy = (x2 - x_[1][index[1]]) * inverse_spacing_[1];\n    zz = (x3 - x_[2][index[2]]) * inverse_spacing_[2];\n  } else {\n    xx = (x1 - x_[0][index[0]]) / (x_[0][index[0] + 1] - x_[0][index[0]]);\n    yy = (x2 - x_[1][index[1]]) / (x_[1][index[1] + 1] - x_[1][index[1]]);\n    zz = (x3 - x_[2][index[2]]) / (x_[2][index[2] + 1] - x_[2][index[2]]);\n  }\n\n  // Note: first index varies fastest\n  weights.weights = std::array<double, 8>({\n      (1. - xx) * (1. - yy) * (1. - zz),  // 000\n      xx * (1. - yy) * (1. - zz),         // 100\n      (1. - xx) * yy * (1. - zz),         // 010\n      xx * yy * (1. - zz),                // 110\n      (1. - xx) * (1. - yy) * zz,         // 001\n      xx * (1. - yy) * zz,                // 101\n      (1. - xx) * yy * zz,                // 011\n      xx * yy * zz,                       // 111\n  });\n\n  // Compute indices\n  //\n  for (size_t k = 0; k < 2; ++k) {\n    for (size_t j = 0; j < 2; ++j) {\n      for (size_t i = 0; i < 2; ++i) {\n        auto tmp_index = Index<3>(index[0] + i, index[1] + j, index[2] + k);\n        weights.index[i + 2 * (j + 2 * k)] =\n            collapsed_index(tmp_index, number_of_points_);\n      }\n    }\n  }\n\n  return weights;\n}\n\ntemplate <size_t Dimension, size_t NumberOfVariables, bool UniformSpacing>\nMultiLinearSpanInterpolation<Dimension, NumberOfVariables, UniformSpacing>::\n    MultiLinearSpanInterpolation(\n        std::array<gsl::span<double const>, Dimension> x,\n        gsl::span<double const> y, Index<Dimension> number_of_points)\n    : number_of_points_(number_of_points), x_(x), y_(y) {\n  for (size_t i = 0; i < Dimension; ++i) {\n    spacing_[i] = x_[i][1] - x_[i][0];\n    inverse_spacing_[i] = 1. / spacing_[i];\n    allow_extrapolation_below_data_[i] = false;\n    allow_extrapolation_abov_data_[i] = false;\n  }\n}\n\n/// Multilinear span interpolation with uniform grid spacing\ntemplate <size_t Dimension, size_t NumberOfVariables>\nusing UniformMultiLinearSpanInterpolation =\n    MultiLinearSpanInterpolation<Dimension, NumberOfVariables, true>;\n\n/// Multilinear span interpolation with non-uniform grid spacing\ntemplate <size_t Dimension, size_t NumberOfVariables>\nusing GeneralMultiLinearSpanInterpolation =\n    MultiLinearSpanInterpolation<Dimension, NumberOfVariables, false>;\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/PolynomialInterpolation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/PolynomialInterpolation.hpp\"\n\n#include <cstddef>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace intrp {\ntemplate <size_t Degree>\nvoid polynomial_interpolation(const gsl::not_null<double*> y,\n                              const gsl::not_null<double*> error_in_y,\n                              const double target_x,\n                              const gsl::span<const double>& y_values,\n                              const gsl::span<const double>& x_values) {\n  constexpr size_t order = Degree + 1;\n  ASSERT(x_values.size() == order, \"The x_values span must be of size \"\n                                       << order << \" but got \"\n                                       << x_values.size());\n  ASSERT(y_values.size() == order, \"The x_values span must be of size \"\n                                       << order << \" but got \"\n                                       << x_values.size());\n  using std::abs;\n  // C and D are constants used in the interpolation algorithm. They're an\n  // intermediate variable to make life easier.\n  std::array<double, order> algorithm_c{};\n  std::array<double, order> algorithm_d{};\n  double minimum_dx = abs(target_x - x_values[0]);\n  size_t index_of_closest_point = 0;\n  for (size_t i = 0; i < order; ++i) {\n    const double local_dx = abs(target_x - x_values[i]);\n    if (local_dx < minimum_dx) {\n      index_of_closest_point = i;\n      minimum_dx = local_dx;\n    }\n    gsl::at(algorithm_c, i) = y_values[i];\n    gsl::at(algorithm_d, i) = y_values[i];\n  }\n\n  size_t ns = index_of_closest_point;\n  *y = y_values[ns--];\n  for (size_t m = 1; m < order; m++) {\n    for (size_t i = 0; i < order - m; i++) {\n      const double delta_x_o = x_values[i] - target_x;\n      const double delta_x_p = x_values[i + m] - target_x;\n      const double c_minus_d =\n          gsl::at(algorithm_c, i + 1) - gsl::at(algorithm_d, i);\n      ASSERT(delta_x_o - delta_x_p != 0.0,\n             \"Encountered repeated grid points. i:\" << i << \" m:\" << m\n                                                    << \" x:\" << x_values[i]);\n      const double adjustment_factor = c_minus_d / (delta_x_o - delta_x_p);\n      gsl::at(algorithm_d, i) = delta_x_p * adjustment_factor;\n      gsl::at(algorithm_c, i) = delta_x_o * adjustment_factor;\n    }\n    *error_in_y = 2 * (ns + 1) < (order - m) ? gsl::at(algorithm_c, ns + 1)\n                                             : gsl::at(algorithm_d, ns--);\n    *y += *error_in_y;\n  }\n}\n\n#define GET_DEGREE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                     \\\n  template void polynomial_interpolation<GET_DEGREE(data)>(        \\\n      gsl::not_null<double*> y, gsl::not_null<double*> error_in_y, \\\n      double target_x, const gsl::span<const double>& y_values,    \\\n      const gsl::span<const double>& x_values);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3, 4, 5, 6, 7))\n\n#undef INSTANTIATION\n#undef GET_DEGREE\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/PolynomialInterpolation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Utilities/Gsl.hpp\"\n\nnamespace intrp {\n/*!\n * \\brief Interpolate `y_values` to `target_x` from tabulated `x_values` using a\n * polynomial interpolant of degree `Degree`.\n *\n * `error_in_y` is an estimate of the error of the interpolated value. Note that\n * at least in the tests this is a significant overestimate of the errors\n * (several orders of magnitude). However, this could be because in the test the\n * polynomial can be represented exactly when all terms are present, but incurs\n * significant errors when the largest degree term is omitted.\n */\ntemplate <size_t Degree>\nvoid polynomial_interpolation(gsl::not_null<double*> y,\n                              gsl::not_null<double*> error_in_y,\n                              double target_x,\n                              const gsl::span<const double>& y_values,\n                              const gsl::span<const double>& x_values);\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/PredictedZeroCrossing.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/PredictedZeroCrossing.hpp\"\n\n#include <algorithm>\n#include <deque>\n#include <vector>\n\n#include \"NumericalAlgorithms/Interpolation/LinearRegression.hpp\"\n\nnamespace intrp {\n\ndouble predicted_zero_crossing_value(const std::vector<double>& x_values,\n                                     const std::vector<double>& y_values) {\n  ASSERT(*std::max_element(x_values.begin(), x_values.end()) <= 0.0,\n         \"predicted_zero_crossing_value assumes that the x values are \"\n         \"non-positive. This assumption is not necessary for the fit, but \"\n         \"it is necessary to make sense of the special treatment of the \"\n         \"return type for when the error bars are large.\");\n\n  const auto [intercept, slope, delta_intercept, delta_slope] =\n      linear_regression(x_values, y_values);\n\n  // See the doxygen comments for details of the implementation.\n  // For now, set all the x's to zero if denominators are zero.\n  const double x_best_fit = slope == 0.0 ? 0.0 : -intercept / slope;\n  const double x0 =\n      slope + delta_slope == 0.0\n          ? 0.0\n          : -(intercept - delta_intercept) / (slope + delta_slope);\n  const double x1 =\n      slope + delta_slope == 0.0\n          ? 0.0\n          : -(intercept + delta_intercept) / (slope + delta_slope);\n  const double x2 =\n      slope - delta_slope == 0.0\n          ? 0.0\n          : -(intercept - delta_intercept) / (slope - delta_slope);\n  const double x3 =\n      slope - delta_slope == 0.0\n          ? 0.0\n          : -(intercept + delta_intercept) / (slope - delta_slope);\n  if ((x_best_fit > 0.0 and x1 > 0.0 and x2 > 0.0 and x3 > 0.0 and x0 > 0.0) or\n      (x_best_fit < 0.0 and x1 < 0.0 and x2 < 0.0 and x3 < 0.0 and x0 < 0.0)) {\n    return x_best_fit;\n  }\n  return 0.0;  // That is, assign no crossing value at all\n}\n\nDataVector predicted_zero_crossing_value(\n    const std::deque<double>& x_values,\n    const std::deque<DataVector>& y_values) {\n  ASSERT(x_values.size() == y_values.size(),\n         \"The x_values and y_values must be of the same size\");\n  DataVector result(y_values.front().size());\n\n  const std::vector<double> tmp_x_values(x_values.begin(), x_values.end());\n  std::vector<double> tmp_y_values(x_values.size());\n  for (size_t i = 0; i < result.size(); i++) {\n    for (size_t j = 0; j < tmp_y_values.size(); j++) {\n      tmp_y_values[j] = y_values[j][i];\n    }\n    result[i] = predicted_zero_crossing_value(tmp_x_values, tmp_y_values);\n  }\n\n  return result;\n}\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/PredictedZeroCrossing.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <deque>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n\nnamespace intrp {\n\n/*!\n * \\brief Predicts the zero crossing of a function.\n *\n * Fits a linear function to a set of y_values at different x_values\n * and uses the fit to predict what x_value the y_value zero will be crossed.\n *\n * predicted_zero_crossing treats x=0 in a special way: All of the\n * x_values must be non-positive; one of the x_values is typically (but\n * is not required to be) zero.  In typical usage, x is time, and x=0\n * is the current time, and we are interested in whether the function\n * crosses zero in the past or in the future. If it cannot be\n * determined (within the error bars of the fit) whether the zero\n * crossing occurs for x < 0 versus x > 0, then we return zero.\n * Otherwise we return the best-fit x for when the function crosses\n * zero.\n *\n * \\details We fit to a straight line: y = intercept + slope*x.\n * So our best guess is that the function will cross zero at\n * x_best_fit = -intercept/slope.\n *\n * However, the data are assumed to be noisy.  The fit gives us error\n * bars for the slope and the intercept.  Given the error bars, we can\n * compute four limiting crossing values x0, x1, x2, and x3 by using\n * the maximum and minimum possible values of slope and intercept.\n * For example, if we assume slope<0 and intercept>0, then the\n * earliest possible crossing consistent with the error bars is\n * x3=(-intercept+delta_intercept)/(slope-delta_slope) and the latest\n * possible crossing consistent with the error bars is\n * x0=(-intercept-delta_intercept)/(slope+delta_slope).\n *\n * We compute all four crossing values and demand that all of them\n * are either at x>0 (i.e. in the future if x is time) or at x<0\n * (i.e. in the past if x is time).  Otherwise we conclude that we\n * cannot determine even the sign of the crossing value, so we return\n * zero.\n */\ndouble predicted_zero_crossing_value(const std::vector<double>& x_values,\n                                     const std::vector<double>& y_values);\n\n/*!\n * \\brief Predicts the zero crossing of multiple functions.\n *\n * For the ith element of the DataVector inside y_values, calls\n * predicted_zero_crossing_value(x_values,y_values[:][i]), where we\n * have used python-like notation.\n */\nDataVector predicted_zero_crossing_value(\n    const std::deque<double>& x_values, const std::deque<DataVector>& y_values);\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/BarycentricRational.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/Python/BarycentricRational.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"NumericalAlgorithms/Interpolation/BarycentricRational.hpp\"\n\nnamespace py = pybind11;\n\nnamespace intrp::py_bindings {\nvoid bind_barycentric_rational(py::module& m) {  // NOLINT\n  py::class_<BarycentricRational>(m, \"BarycentricRational\")\n      .def(py::init<std::vector<double>, std::vector<double>, size_t>(),\n           py::arg(\"x_values\"), py::arg(\"y_values\"), py::arg(\"order\"))\n      .def(\"x_values\", &BarycentricRational::x_values)\n      .def(\"y_values\", &BarycentricRational::y_values)\n      .def(\"order\", &BarycentricRational::order)\n      .def(\"__call__\", &BarycentricRational::operator());\n}\n}  // namespace intrp::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/BarycentricRational.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace intrp::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_barycentric_rational(pybind11::module& m);\n}  // namespace intrp::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"NumericalAlgorithms/Interpolation/Python/BarycentricRational.hpp\"\n#include \"NumericalAlgorithms/Interpolation/Python/CubicSpline.hpp\"\n#include \"NumericalAlgorithms/Interpolation/Python/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Interpolation/Python/RegularGridInterpolant.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.Spectral\");\n  intrp::py_bindings::bind_barycentric_rational(m);\n  intrp::py_bindings::bind_cubic_spline(m);\n  intrp::py_bindings::bind_irregular(m);\n  intrp::py_bindings::bind_regular_grid(m);\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyInterpolation\")\n\nspectre_python_add_module(\n  Interpolation\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  BarycentricRational.cpp\n  Bindings.cpp\n  CubicSpline.cpp\n  IrregularInterpolant.cpp\n  RegularGridInterpolant.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BarycentricRational.hpp\n  CubicSpline.hpp\n  IrregularInterpolant.hpp\n  RegularGridInterpolant.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Interpolation\n  DataStructures\n  Spectral\n  pybind11::module\n)\n\nspectre_python_add_dependencies(${LIBRARY} PyDataStructures PySpectral)\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/CubicSpline.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/Python/CubicSpline.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"NumericalAlgorithms/Interpolation/CubicSpline.hpp\"\n\nnamespace py = pybind11;\n\nnamespace intrp::py_bindings {\nvoid bind_cubic_spline(py::module& m) {  // NOLINT\n  py::class_<CubicSpline>(m, \"CubicSpline\")\n      .def(py::init<std::vector<double>, std::vector<double>>(),\n           py::arg(\"x_values\"), py::arg(\"y_values\"))\n      .def(\"x_values\", &CubicSpline::x_values)\n      .def(\"y_values\", &CubicSpline::y_values)\n      .def(\"__call__\", &CubicSpline::operator());\n}\n}  // namespace intrp::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/CubicSpline.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace intrp::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_cubic_spline(pybind11::module& m);\n}  // namespace intrp::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/IrregularInterpolant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/Python/IrregularInterpolant.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace py = pybind11;\n\nnamespace intrp::py_bindings {\nnamespace {\ntemplate <size_t Dim>\nvoid bind_irregular_impl(py::module& m) {  // NOLINT\n  py::class_<Irregular<Dim>>(m,\n                             (\"Irregular\" + std::to_string(Dim) + \"D\").c_str())\n      .def(py::init(\n               [](const Mesh<Dim>& source_mesh,\n                  const std::array<DataVector, Dim>& target_logical_coords) {\n                 return Irregular<Dim>{\n                     source_mesh,\n                     tnsr::I<DataVector, Dim, Frame::ElementLogical>{\n                         target_logical_coords}};\n               }),\n           py::arg(\"source_mesh\"), py::arg(\"target_logical_coords\"))\n      .def(\"interpolate\",\n           static_cast<DataVector (Irregular<Dim>::*)(const DataVector&) const>(\n               &Irregular<Dim>::interpolate),\n           py::arg(\"input\"));\n}\n}  // namespace\n\nvoid bind_irregular(py::module& m) {\n  bind_irregular_impl<1>(m);\n  bind_irregular_impl<2>(m);\n  bind_irregular_impl<3>(m);\n}\n\n}  // namespace intrp::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/IrregularInterpolant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace intrp::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_irregular(pybind11::module& m);\n}  // namespace intrp::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/RegularGridInterpolant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/Python/RegularGridInterpolant.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace py = pybind11;\n\nnamespace intrp::py_bindings {\nnamespace {\ntemplate <size_t Dim>\nvoid bind_regular_grid_impl(py::module& m) {  // NOLINT\n  // the bindings are not complete and can be extended\n  py::class_<RegularGrid<Dim>>(\n      m, (\"RegularGrid\" + std::to_string(Dim) + \"D\").c_str())\n      .def(py::init<const Mesh<Dim>&, const Mesh<Dim>&,\n                    const std::array<DataVector, Dim>&>(),\n           py::arg(\"source_mesh\"), py::arg(\"target_mesh\"),\n           py::arg(\"override_target_mesh_with_1d_logical_coords\") =\n               std::array<DataVector, Dim>{})\n      .def(\"interpolate\",\n           static_cast<DataVector (RegularGrid<Dim>::*)(const DataVector&)\n                           const>(&RegularGrid<Dim>::interpolate),\n           py::arg(\"input\"))\n      .def(\"interpolation_matrices\", &RegularGrid<Dim>::interpolation_matrices);\n}\n}  // namespace\n\nvoid bind_regular_grid(py::module& m) {\n  bind_regular_grid_impl<1>(m);\n  bind_regular_grid_impl<2>(m);\n  bind_regular_grid_impl<3>(m);\n}\n\n}  // namespace intrp::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/RegularGridInterpolant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace intrp::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_regular_grid(pybind11::module& m);\n}  // namespace intrp::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n\nIrregular = {1: Irregular1D, 2: Irregular2D, 3: Irregular3D}\nRegularGrid = {1: RegularGrid1D, 2: RegularGrid2D, 3: RegularGrid3D}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/RegularGridInterpolant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"RegularGridInterpolant.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace intrp {\n\ntemplate <size_t Dim>\nRegularGrid<Dim>::RegularGrid(const Mesh<Dim>& source_mesh,\n                              const Mesh<Dim>& target_mesh,\n                              const std::array<DataVector, Dim>&\n                                  override_target_mesh_with_1d_logical_coords)\n    : number_of_target_points_(1), source_extents_(source_mesh.extents()) {\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto source_mesh_1d = source_mesh.slice_through(d);\n    const auto target_mesh_1d = target_mesh.slice_through(d);\n    if (gsl::at(override_target_mesh_with_1d_logical_coords, d).size() == 0) {\n      // Normal mode: use target_mesh\n      // Only make a matrix if source and target meshes differ; when a default-\n      // constructed matrix is given instead, apply_matrices does no work.\n      if (source_mesh_1d != target_mesh_1d) {\n        gsl::at(interpolation_matrices_, d) = Spectral::interpolation_matrix(\n            source_mesh_1d, Spectral::collocation_points(target_mesh_1d));\n      }\n      number_of_target_points_ *= target_mesh_1d.number_of_grid_points();\n    } else {\n      // Override mode: override target_mesh with given points\n      const auto& target_coords_1d =\n          gsl::at(override_target_mesh_with_1d_logical_coords, d);\n      gsl::at(interpolation_matrices_, d) =\n          Spectral::interpolation_matrix(source_mesh_1d, target_coords_1d);\n      number_of_target_points_ *= target_coords_1d.size();\n    }\n  }\n}\n\ntemplate <size_t Dim>\nRegularGrid<Dim>::RegularGrid() = default;\n\ntemplate <size_t Dim>\nvoid RegularGrid<Dim>::pup(PUP::er& p) {\n  p | number_of_target_points_;\n  p | source_extents_;\n  p | interpolation_matrices_;\n}\n\ntemplate <size_t Dim>\nvoid RegularGrid<Dim>::interpolate(const gsl::not_null<DataVector*> result,\n                                   const DataVector& input) const {\n  result->destructive_resize(number_of_target_points_);\n  apply_matrices(result, interpolation_matrices_, input, source_extents_);\n}\n\ntemplate <size_t Dim>\nvoid RegularGrid<Dim>::interpolate(\n    const gsl::not_null<ComplexDataVector*> result,\n    const ComplexDataVector& input) const {\n  result->destructive_resize(number_of_target_points_);\n  apply_matrices(result, interpolation_matrices_, input, source_extents_);\n}\n\ntemplate <size_t Dim>\nDataVector RegularGrid<Dim>::interpolate(const DataVector& input) const {\n  DataVector result(number_of_target_points_);\n  interpolate(make_not_null(&result), input);\n  return result;\n}\ntemplate <size_t Dim>\nComplexDataVector RegularGrid<Dim>::interpolate(\n    const ComplexDataVector& input) const {\n  ComplexDataVector result(number_of_target_points_);\n  interpolate(make_not_null(&result), input);\n  return result;\n}\n\ntemplate <size_t Dim>\nconst std::array<Matrix, Dim>& RegularGrid<Dim>::interpolation_matrices()\n    const {\n  return interpolation_matrices_;\n}\n\ntemplate <size_t LocalDim>\nbool operator==(const RegularGrid<LocalDim>& lhs,\n                const RegularGrid<LocalDim>& rhs) {\n  return lhs.number_of_target_points_ == rhs.number_of_target_points_ and\n         lhs.source_extents_ == rhs.source_extents_ and\n         lhs.interpolation_matrices_ == rhs.interpolation_matrices_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const RegularGrid<Dim>& lhs, const RegularGrid<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                               \\\n  template class RegularGrid<DIM(data)>;                   \\\n  template bool operator==(const RegularGrid<DIM(data)>&,  \\\n                           const RegularGrid<DIM(data)>&); \\\n  template bool operator!=(const RegularGrid<DIM(data)>&,  \\\n                           const RegularGrid<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace intrp {\n\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Interpolate data from a Mesh onto a regular grid of points.\n///\n/// The target points must lie on a tensor-product grid that is aligned with the\n/// source Mesh. In any direction where the source and target points share an\n/// identical Mesh (i.e., where the underlying 1-dimensional meshes share the\n/// same extent, basis, and quadrature), the code is optimized to avoid\n/// performing identity interpolations.\n///\n/// Note, however, that in each dimension of the target grid, the points can\n/// be freely distributed; in particular, the grid points need not be the\n/// collocation points corresponding to a particular basis and quadrature. Note\n/// also that the target grid need not overlap the source grid. In this case,\n/// polynomial extrapolation is performed, with order set by the order of the\n/// basis in the source grid. The extrapolation will be correct but may suffer\n/// from reduced accuracy, especially for higher-order polynomials.\ntemplate <size_t Dim>\nclass RegularGrid {\n public:\n  /// \\brief An interpolator between two regular grids.\n  ///\n  /// When the optional third argument is NOT passed, creates an interpolator\n  /// between two regular meshes.\n  ///\n  /// The optional third argument allows the caller to override the distribution\n  /// of grid points in any dimension(s) of the target grid. Each non-empty\n  /// element of `override_target_mesh_with_1d_logical_coords` gives the logical\n  /// coordinates which will override the default coordinates of `target_mesh`.\n  RegularGrid(const Mesh<Dim>& source_mesh, const Mesh<Dim>& target_mesh,\n              const std::array<DataVector, Dim>&\n                  override_target_mesh_with_1d_logical_coords = {});\n\n  RegularGrid();\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// @{\n  /// \\brief Interpolate a Variables onto the target points.\n  template <typename TagsList>\n  void interpolate(gsl::not_null<Variables<TagsList>*> result,\n                   const Variables<TagsList>& vars) const;\n  template <typename TagsList>\n  Variables<TagsList> interpolate(const Variables<TagsList>& vars) const;\n  /// @}\n\n  /// @{\n  /// \\brief Interpolate a DataVector onto the target points.\n  ///\n  /// \\note When interpolating multiple tensors, the Variables interface is more\n  /// efficient. However, this DataVector interface is useful for applications\n  /// where only some components of a Tensor or Variables need to be\n  /// interpolated.\n  void interpolate(gsl::not_null<DataVector*> result,\n                   const DataVector& input) const;\n  DataVector interpolate(const DataVector& input) const;\n  void interpolate(gsl::not_null<ComplexDataVector*> result,\n                   const ComplexDataVector& input) const;\n  ComplexDataVector interpolate(const ComplexDataVector& input) const;\n  /// @}\n\n  /// \\brief Return the internally-stored matrices that interpolate from the\n  /// source grid to the target grid.\n  ///\n  /// Each matrix interpolates in one logical direction, and can be empty if the\n  /// interpolation in that direction is the identity (i.e., if the source\n  /// and target grids have the same logical coordinates in this direction).\n  const std::array<Matrix, Dim>& interpolation_matrices() const;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const RegularGrid<LocalDim>& lhs,\n                         const RegularGrid<LocalDim>& rhs);\n\n  size_t number_of_target_points_{};\n  Index<Dim> source_extents_;\n  std::array<Matrix, Dim> interpolation_matrices_;\n};\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nvoid RegularGrid<Dim>::interpolate(\n    const gsl::not_null<Variables<TagsList>*> result,\n    const Variables<TagsList>& vars) const {\n  if (result->number_of_grid_points() != number_of_target_points_) {\n    result->initialize(number_of_target_points_);\n  }\n  apply_matrices(result, interpolation_matrices_, vars, source_extents_);\n}\n\ntemplate <size_t Dim>\ntemplate <typename TagsList>\nVariables<TagsList> RegularGrid<Dim>::interpolate(\n    const Variables<TagsList>& vars) const {\n  Variables<TagsList> result;\n  interpolate(make_not_null(&result), vars);\n  return result;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const RegularGrid<Dim>& lhs, const RegularGrid<Dim>& rhs);\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/SpanInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace intrp {\n\nstd::complex<double> SpanInterpolator::interpolate(\n    const gsl::span<const double>& source_points,\n    const gsl::span<const std::complex<double>>& values,\n    const double target_point) const {\n  // the operation below to get the real and imag parts does not alter the\n  // contents of the span, so the const-cast is safe.\n  const ComplexDataVector view{\n      const_cast<std::complex<double>*>(values.data()),  // NOLINT\n      values.size()};\n  const DataVector real_part = real(view);\n  const DataVector imag_part = imag(view);\n  return std::complex<double>{\n      interpolate(source_points,\n                  gsl::span<const double>(real_part.data(), real_part.size()),\n                  target_point),\n      interpolate(source_points,\n                  gsl::span<const double>(imag_part.data(), imag_part.size()),\n                  target_point)};\n}\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/SpanInterpolator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace intrp {\n\nclass LinearSpanInterpolator;\nclass CubicSpanInterpolator;\nclass BarycentricRationalSpanInterpolator;\n\n/// \\brief Base class for interpolators so that the factory options\n/// mechanism can be used.\n///\n/// \\details The virtual functions in this class demand only that the real\n/// `interpolate` function, `get_clone` function, and\n/// `required_number_of_points_before_and_after` function be overridden in the\n/// derived class. The `interpolate` for complex values can just be used from\n/// this base class, which calls the real version for each component. If it is\n/// possible to make a specialized complex version that avoids allocations, that\n/// is probably more efficient.\nclass SpanInterpolator : public PUP::able {\n public:\n  using creatable_classes =\n      tmpl::list<LinearSpanInterpolator, CubicSpanInterpolator,\n                 BarycentricRationalSpanInterpolator>;\n\n  WRAPPED_PUPable_abstract(SpanInterpolator);  // NOLINT\n\n  /// Produce a `std::unique_ptr` that points to a copy of `*this``\n  virtual std::unique_ptr<SpanInterpolator> get_clone() const = 0;\n\n  /// Perform the interpolation of function represented by `values` at\n  /// `source_points` to the requested `target_point`, returning the\n  /// interpolation result.\n  virtual double interpolate(const gsl::span<const double>& source_points,\n                             const gsl::span<const double>& values,\n                             double target_point) const = 0;\n\n  /// Perform the interpolation of function represented by complex `values` at\n  /// `source_points` to the requested `target_point`, returning the\n  /// (complex) interpolation result.\n  std::complex<double> interpolate(\n      const gsl::span<const double>& source_points,\n      const gsl::span<const std::complex<double>>& values,\n      double target_point) const;\n\n  /// The number of domain points that should be both before and after the\n  /// requested target point for best interpolation. For instance, for a linear\n  /// interpolator, this function would return `1` to request that the target is\n  /// between the two domain points passed to `source_points`.\n  virtual size_t required_number_of_points_before_and_after() const = 0;\n};\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/ZeroCrossingPredictor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Interpolation/ZeroCrossingPredictor.hpp\"\n\n#include <algorithm>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Interpolation/PredictedZeroCrossing.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace intrp {\nZeroCrossingPredictor::ZeroCrossingPredictor(const size_t min_size,\n                                             const size_t max_size)\n    : min_size_(min_size), max_size_(max_size) {\n  if (min_size_ < 3) {\n    ERROR(\"min_size must be >= 3, not \" << min_size_);\n  }\n  if (min_size_ > max_size_) {\n    ERROR(\"min_size must be <= max_size, but \" << min_size_ << \" > \"\n                                               << max_size_);\n  }\n}\n\nvoid ZeroCrossingPredictor::add(const double t, DataVector data_at_time_t) {\n  times_.push_back(t);\n  data_.emplace_back(std::move(data_at_time_t));\n\n  while (times_.size() > max_size_) {\n    times_.pop_front();\n    data_.pop_front();\n  }\n}\n\nDataVector ZeroCrossingPredictor::zero_crossing_time(\n    const double current_time) const {\n  if (not is_valid()) {\n    ERROR(\"Invalid ZeroCrossingPredictor. min_size = \"\n          << min_size_ << \" times_.size() = \" << times_.size());\n  }\n\n  // We need times relative to current_time.\n  std::deque<double> relative_times(times_.begin(), times_.end());\n  for (auto& time : relative_times) {\n    time -= current_time;\n  }\n  return predicted_zero_crossing_value(relative_times, data_);\n}\n\nstd::optional<double> ZeroCrossingPredictor::min_positive_zero_crossing_time(\n    double current_time) const {\n  if (not is_valid()) {\n    return std::nullopt;\n  }\n  auto crossing_time = zero_crossing_time(current_time);\n  // Replace all negative crossing times with infinity.\n  std::for_each(crossing_time.begin(), crossing_time.end(), [](double& a) {\n    // Exactly 0.0 is sentinel from zero_crossing_time that error bars were too\n    // large\n    a = a <= 0.0 ? std::numeric_limits<double>::infinity() : a;\n  });\n\n  const double min_value =\n      *std::min_element(crossing_time.begin(), crossing_time.end());\n\n  return min_value == std::numeric_limits<double>::infinity()\n             ? std::nullopt\n             : std::optional{min_value};\n}\n\nbool ZeroCrossingPredictor::is_valid() const {\n  return times_.size() >= min_size_;\n}\n\nvoid ZeroCrossingPredictor::clear() {\n  times_.clear();\n  data_.clear();\n}\n\nvoid ZeroCrossingPredictor::pup(PUP::er& p) {\n  p | min_size_;\n  p | max_size_;\n  p | times_;\n  p | data_;\n}\n\nbool operator==(const ZeroCrossingPredictor& predictor1,\n                const ZeroCrossingPredictor& predictor2) {\n  return (predictor1.min_size_ == predictor2.min_size_) and\n         (predictor1.max_size_ == predictor2.max_size_) and\n         (predictor1.times_ == predictor2.times_) and\n         (predictor1.data_ == predictor2.data_);\n}\n\nbool operator!=(const ZeroCrossingPredictor& predictor1,\n                const ZeroCrossingPredictor& predictor2) {\n  return not(predictor1 == predictor2);\n}\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/Interpolation/ZeroCrossingPredictor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <deque>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace intrp {\n\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief A class that predicts when a function crosses zero\nclass ZeroCrossingPredictor {\n public:\n  /// Uses at most max_size times for the fit; throws away old\n  /// times as new times are added.\n  /// The is_valid function returns false until min_size times\n  /// have been added.  min_size must be at least 3.\n  ZeroCrossingPredictor(size_t min_size, size_t max_size);\n\n  ZeroCrossingPredictor() = default;\n\n  /// Adds a data point at time t to the ZeroCrossingPredictor.\n  void add(double t, DataVector data_at_time_t);\n\n  /// For each component of the data, returns the time, relative to\n  /// current_time, that a linear fit to the given component of the\n  /// data crosses zero.  The length of the return value is the same\n  /// as the length of `data_at_time_t` in the `add` function.\n  ///\n  /// The zero-crossing time that is computed has error bars\n  /// associated with it.  If the error bars are large enough that it\n  /// is not clear whether the zero-crossing time is positive or\n  /// negative, then zero is returned instead of the best-fit\n  /// zero-crossing time.\n  DataVector zero_crossing_time(double current_time) const;\n\n  /// The minimum positive value over the DataVector returned\n  /// by zero_crossing_time. If there is no minimum positive value (all are\n  /// negative), returns `std::nullopt`. Also returns `std::nullopt` if\n  /// is_valid() is false.\n  std::optional<double> min_positive_zero_crossing_time(\n      double current_time) const;\n\n  /// Returns whether we have enough data to call zero_crossing_time.\n  bool is_valid() const;\n\n  /// Clears the internal arrays.  Used to reset if there is a\n  /// discontinuous change in the data that should not be fit over.\n  void clear();\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  friend bool operator==(const ZeroCrossingPredictor&,   // NOLINT\n                         const ZeroCrossingPredictor&);  // NOLINT\n\n private:\n  size_t min_size_{3};\n  size_t max_size_{3};\n  std::deque<double> times_{};\n  std::deque<DataVector> data_{};\n};\n\nbool operator!=(const ZeroCrossingPredictor&, const ZeroCrossingPredictor&);\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearAlgebra/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY LinearAlgebra)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FindGeneralizedEigenvalues.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FindGeneralizedEigenvalues.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  BLAS::BLAS\n  DataStructures\n  ErrorHandling\n  PRIVATE\n  LAPACK::LAPACK\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearAlgebra/FindGeneralizedEigenvalues.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearAlgebra/FindGeneralizedEigenvalues.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <ostream>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n// LAPACK routine to do the generalized eigenvalue problem\nextern \"C\" {\n// The final two arguments are the \"hidden\" lengths of the first two.\n// https://gcc.gnu.org/onlinedocs/gfortran/Argument-passing-conventions.html\nextern void dggev_(char*, char*, int*, double*, int*, double*, int*, double*,\n                   double*, double*, double*, int*, double*, int*, double*,\n                   int*, int*, size_t, size_t);\n}\n\nvoid find_generalized_eigenvalues(\n    const gsl::not_null<DataVector*> eigenvalues_real_part,\n    const gsl::not_null<DataVector*> eigenvalues_imaginary_part,\n    const gsl::not_null<Matrix*> eigenvectors, Matrix matrix_a,\n    Matrix matrix_b) {\n  // Sanity checks on the sizes of the vectors and matrices\n  const size_t number_of_rows = matrix_a.rows();\n  ASSERT(number_of_rows == matrix_a.columns(),\n         \"Matrix A should be square, but A has \"\n             << matrix_a.rows() << \" rows and \" << matrix_a.columns()\n             << \" columns.\");\n  ASSERT(number_of_rows == matrix_b.rows() and\n             number_of_rows == matrix_b.columns(),\n         \"Matrix A and matrix B should be the same size, but A has \"\n             << matrix_a.rows() << \" rows and \" << matrix_a.columns()\n             << \" columns, while B has \" << matrix_b.rows() << \" rows and \"\n             << matrix_b.columns() << \" columns.\");\n  ASSERT(number_of_rows == eigenvectors->rows() and\n             number_of_rows == eigenvectors->columns(),\n         \"Matrix A and matrix eigenvectors should have the same size, \"\n             \"but A has \" << matrix_a.rows() << \" rows and \"\n             << matrix_a.columns() << \" columns, while the eigenvectors matrix \"\n             << \"has \" << eigenvectors->rows() << \" rows and \"\n             << eigenvectors->columns() << \" columns.\");\n  ASSERT(number_of_rows == eigenvalues_real_part->size() and\n             number_of_rows == eigenvalues_imaginary_part->size(),\n         \"eigenvalues DataVector sizes should equal number of columns \"\n         \"in Matrix A, but A has \"\n             << matrix_a.columns()\n             << \" columns, while the real eigenvalues DataVector size is \"\n             << eigenvalues_real_part->size()\n             << \" and the imaginary eigenvalues DataVector size is \"\n             << eigenvalues_imaginary_part->size() << \".\");\n\n  // Set up parameters for the lapack call\n  // Lapack uses chars to decide whether to compute the left eigenvectors,\n  // the right eigenvectors, both, or neither. 'N' means do not compute,\n  // 'V' means do compute. Note: not const because lapack does not want this\n  // option const.\n  char compute_left_eigenvectors = 'N';\n  char compute_right_eigenvectors = 'V';\n\n  // Lapack expects the sizes to be ints, not size_t.\n  // NOTE: not const because lapack function dggev_() arguments\n  // are not const.\n  auto matrix_and_vector_size = static_cast<int>(number_of_rows);\n\n  // Lapack splits the eigenvalues into unnormalized real and imaginary\n  // parts, which it calls alphar and alphai, and a normalization,\n  // which it calls beta. The real and imaginary parts of the eigenvalues are\n  // found by dividing the unnormalized results by the normalization.\n  DataVector eigenvalue_normalization(number_of_rows, 0.0);\n\n  // Lapack uses a work vector.  -1 indicates a query for the optimal\n  // size.\n  int work_size = -1;\n  double optimal_work_size = std::numeric_limits<double>::signaling_NaN();\n\n  //  Lapack uses an integer called info to return its status\n  //  info = 0 : success\n  //  info = -i: ith argument had bad value\n  //  info > 0: some other failure\n  int info = 0;\n\n  int matrix_a_spacing = matrix_a.spacing();\n  int matrix_b_spacing = matrix_b.spacing();\n  int eigenvectors_spacing = eigenvectors->spacing();\n\n  // Query for the work size\n  dggev_(&compute_left_eigenvectors, &compute_right_eigenvectors,\n         &matrix_and_vector_size, matrix_a.data(), &matrix_a_spacing,\n         matrix_b.data(), &matrix_b_spacing, eigenvalues_real_part->data(),\n         eigenvalues_imaginary_part->data(), eigenvalue_normalization.data(),\n         eigenvectors->data(), &eigenvectors_spacing, eigenvectors->data(),\n         &eigenvectors_spacing, &optimal_work_size, &work_size, &info, 1, 1);\n\n  if (UNLIKELY(info != 0)) {\n    ERROR(\n        \"Lapack failed to compute workspace requirements. Lapack's dggev \"\n        \"INFO = \"\n        << info);\n  }\n\n  work_size = static_cast<int>(optimal_work_size);\n  std::vector<double> lapack_work(static_cast<size_t>(work_size), 0.0);\n\n  dggev_(&compute_left_eigenvectors, &compute_right_eigenvectors,\n         &matrix_and_vector_size, matrix_a.data(), &matrix_a_spacing,\n         matrix_b.data(), &matrix_b_spacing, eigenvalues_real_part->data(),\n         eigenvalues_imaginary_part->data(), eigenvalue_normalization.data(),\n         eigenvectors->data(), &eigenvectors_spacing, eigenvectors->data(),\n         &eigenvectors_spacing, lapack_work.data(), &work_size, &info, 1, 1);\n\n  if (UNLIKELY(info != 0)) {\n    ERROR(\n        \"Lapack failed to compute generalized eigenvectors. Lapack's dggev \"\n        \"INFO = \"\n        << info);\n  }\n\n  *eigenvalues_real_part /= eigenvalue_normalization;\n  *eigenvalues_imaginary_part /= eigenvalue_normalization;\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearAlgebra/FindGeneralizedEigenvalues.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function find_generalized_eigenvalues.\n\n#pragma once\n\n/// \\cond\nclass DataVector;\nclass Matrix;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Solve the generalized eigenvalue problem for two matrices.\n *\n * This function uses the lapack routine dggev\n * (http://www.netlib.org/lapack/explore-3.1.1-html/dggev.f.html)\n * to solve the\n * generalized eigenvalue problem \\f$A v_a =\\lambda_a B v_a \\f$\n * for the generalized eigenvalues \\f$\\lambda_a\\f$ and corresponding\n * eigenvectors \\f$v_a\\f$.\n * `matrix_a` and `matrix_b` are each a `Matrix`; they correspond to square\n * matrices \\f$A\\f$ and \\f$B\\f$ that are the same dimension \\f$N\\f$.\n * `eigenvalues_real_part` is a `DataVector` of size \\f$N\\f$ that\n * will store the real parts of the eigenvalues,\n * `eigenvalues_imaginary_part` is a `DataVector` of size \\f$N\\f$\n * that will store the imaginary parts of the eigenvalues.\n * Complex eigenvalues always form complex conjugate pairs, and\n * the \\f$j\\f$ and \\f$j+1\\f$ eigenvalues will have the forms\n * \\f$a+ib\\f$ and \\f$a-ib\\f$, respectively. The eigenvectors\n * are returned as the columns of a square `Matrix` of dimension \\f$N\\f$\n * called `eigenvectors`. If eigenvalue \\f$j\\f$ is real, then column \\f$j\\f$ of\n * `eigenvectors` is\n * the corresponding eigenvector. If eigenvalue \\f$j\\f$ and \\f$j+1\\f$ are\n * complex-conjugate pairs, then the eigenvector for\n * eigenvalue \\f$j\\f$ is (column j) + \\f$i\\f$ (column j+1), and the\n * eigenvector for eigenvalue \\f$j+1\\f$ is (column j) - \\f$i\\f$ (column j+1).\n *\n */\nvoid find_generalized_eigenvalues(\n    gsl::not_null<DataVector*> eigenvalues_real_part,\n    gsl::not_null<DataVector*> eigenvalues_imaginary_part,\n    gsl::not_null<Matrix*> eigenvectors, Matrix matrix_a, Matrix matrix_b);\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY LinearOperators)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CoefficientTransforms.cpp\n  DefiniteIntegral.cpp\n  Divergence.cpp\n  ExponentialFilter.cpp\n  IndefiniteIntegral.cpp\n  Linearize.cpp\n  PartialDerivatives.cpp\n  MeanValue.cpp\n  PowerMonitors.cpp\n  WeakDivergence.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CoefficientTransforms.hpp\n  DefiniteIntegral.hpp\n  Divergence.hpp\n  Divergence.tpp\n  ExponentialFilter.hpp\n  IndefiniteIntegral.hpp\n  Linearize.hpp\n  MeanValue.hpp\n  PartialDerivatives.hpp\n  PartialDerivatives.tpp\n  PowerMonitors.hpp\n  WeakDivergence.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  BLAS::BLAS\n  DataStructures\n  ErrorHandling\n  Interpolation\n  Options\n  Serialization\n  Spectral\n  SphericalHarmonics\n  Utilities\n  INTERFACE\n  Domain\n  DomainStructure\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/CoefficientTransforms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n\n#include <array>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, size_t... Is>\nstd::array<std::reference_wrapper<const Matrix>, Dim> make_transform_matrices(\n    const Mesh<Dim>& mesh, const bool nodal_to_modal,\n    std::index_sequence<Is...> /*meta*/) {\n  return nodal_to_modal ? std::array<std::reference_wrapper<const Matrix>,\n                                     Dim>{{Spectral::nodal_to_modal_matrix(\n                              mesh.slice_through(Is))...}}\n                        : std::array<std::reference_wrapper<const Matrix>, Dim>{\n                              {Spectral::modal_to_nodal_matrix(\n                                  mesh.slice_through(Is))...}};\n}\n}  // namespace\n\ntemplate <size_t Dim>\nvoid to_modal_coefficients(\n    const gsl::not_null<ComplexModalVector*> modal_coefficients,\n    const ComplexDataVector& nodal_coefficients, const Mesh<Dim>& mesh) {\n  modal_coefficients->destructive_resize(nodal_coefficients.size());\n  apply_matrices<ComplexModalVector>(\n      modal_coefficients,\n      make_transform_matrices<Dim>(mesh, true, std::make_index_sequence<Dim>{}),\n      nodal_coefficients, mesh.extents());\n}\n\n// overload provided so that the most common case of transforming from\n// `DataVector` to `ModalVector` does not require additional `make_not_null`s\ntemplate <size_t Dim>\nvoid to_modal_coefficients(gsl::not_null<ModalVector*> modal_coefficients,\n                           const DataVector& nodal_coefficients,\n                           const Mesh<Dim>& mesh) {\n  modal_coefficients->destructive_resize(nodal_coefficients.size());\n  apply_matrices<ModalVector>(\n      modal_coefficients,\n      make_transform_matrices<Dim>(mesh, true, std::make_index_sequence<Dim>{}),\n      nodal_coefficients, mesh.extents());\n}\n\ntemplate <size_t Dim>\nModalVector to_modal_coefficients(const DataVector& nodal_coefficients,\n                                  const Mesh<Dim>& mesh) {\n  ModalVector modal_coefficients(nodal_coefficients.size());\n  to_modal_coefficients(make_not_null(&modal_coefficients), nodal_coefficients,\n                        mesh);\n  return modal_coefficients;\n}\n\ntemplate <size_t Dim>\nComplexModalVector to_modal_coefficients(\n    const ComplexDataVector& nodal_coefficients, const Mesh<Dim>& mesh) {\n  ComplexModalVector modal_coefficients(nodal_coefficients.size());\n  to_modal_coefficients(make_not_null(&modal_coefficients), nodal_coefficients,\n                        mesh);\n  return modal_coefficients;\n}\n\ntemplate <size_t Dim>\nvoid to_nodal_coefficients(\n    const gsl::not_null<ComplexDataVector*> nodal_coefficients,\n    const ComplexModalVector& modal_coefficients, const Mesh<Dim>& mesh) {\n  nodal_coefficients->destructive_resize(modal_coefficients.size());\n  apply_matrices<ComplexDataVector>(\n      nodal_coefficients,\n      make_transform_matrices<Dim>(mesh, false,\n                                   std::make_index_sequence<Dim>{}),\n      modal_coefficients, mesh.extents());\n}\n\ntemplate <size_t Dim>\nvoid to_nodal_coefficients(const gsl::not_null<DataVector*> nodal_coefficients,\n                           const ModalVector& modal_coefficients,\n                           const Mesh<Dim>& mesh) {\n  nodal_coefficients->destructive_resize(modal_coefficients.size());\n  apply_matrices<DataVector>(nodal_coefficients,\n                             make_transform_matrices<Dim>(\n                                 mesh, false, std::make_index_sequence<Dim>{}),\n                             modal_coefficients, mesh.extents());\n}\n\ntemplate <size_t Dim>\nDataVector to_nodal_coefficients(const ModalVector& modal_coefficients,\n                                 const Mesh<Dim>& mesh) {\n  DataVector nodal_coefficients(modal_coefficients.size());\n  to_nodal_coefficients(make_not_null(&nodal_coefficients), modal_coefficients,\n                        mesh);\n  return nodal_coefficients;\n}\n\ntemplate <size_t Dim>\nComplexDataVector to_nodal_coefficients(\n    const ComplexModalVector& modal_coefficients, const Mesh<Dim>& mesh) {\n  ComplexDataVector nodal_coefficients(modal_coefficients.size());\n  to_nodal_coefficients(make_not_null(&nodal_coefficients), modal_coefficients,\n                        mesh);\n  return nodal_coefficients;\n}\n\nnamespace {\ntemplate <typename Type>\nstruct modal_type_to_nodal_type;\n\ntemplate <>\nstruct modal_type_to_nodal_type<ModalVector> {\n  using type = DataVector;\n};\n\ntemplate <>\nstruct modal_type_to_nodal_type<ComplexModalVector> {\n  using type = ComplexDataVector;\n};\n}  // namespace\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_TYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_TO_MODAL_COEFFICIENTS(r, data)                   \\\n  template void to_modal_coefficients(                               \\\n      const gsl::not_null<GET_TYPE(data)*> modal_coefficients,       \\\n      const typename modal_type_to_nodal_type<GET_TYPE(data)>::type& \\\n          nodal_coefficients,                                        \\\n      const Mesh<GET_DIM(data)>& mesh);                              \\\n  template GET_TYPE(data) to_modal_coefficients(                     \\\n      const typename modal_type_to_nodal_type<GET_TYPE(data)>::type& \\\n          nodal_coefficients,                                        \\\n      const Mesh<GET_DIM(data)>& mesh);\n\n#define INSTANTIATE_TO_NODAL_COEFFICIENTS(r, data)                  \\\n  template void to_nodal_coefficients(                              \\\n      const gsl::not_null<                                          \\\n          typename modal_type_to_nodal_type<GET_TYPE(data)>::type*> \\\n          nodal_coefficients,                                       \\\n      const GET_TYPE(data) & modal_coefficients,                    \\\n      const Mesh<GET_DIM(data)>& mesh);                             \\\n  template typename modal_type_to_nodal_type<GET_TYPE(data)>::type  \\\n  to_nodal_coefficients(const GET_TYPE(data) & modal_coefficients,  \\\n                        const Mesh<GET_DIM(data)>& mesh);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_TO_MODAL_COEFFICIENTS, (1, 2, 3),\n                        (ModalVector, ComplexModalVector))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_TO_NODAL_COEFFICIENTS, (1, 2, 3),\n                        (ModalVector, ComplexModalVector))\n\n#undef GET_DIM\n#undef GET_TYPE\n#undef INSTANTIATE_TO_NODAL_COEFFICIENTS\n#undef INSTANTIATE_TO_MODAL_COEFFICIENTS\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\nclass ComplexModalVector;\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\nclass ModalVector;\n\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// @{\n/*!\n * \\ingroup SpectralGroup\n * \\brief Compute the modal coefficients from the nodal coefficients\n *\n * \\see Spectral::nodal_to_modal_matrix\n */\ntemplate <size_t Dim>\nvoid to_modal_coefficients(\n    gsl::not_null<ComplexModalVector*> modal_coefficients,\n    const ComplexDataVector& nodal_coefficients, const Mesh<Dim>& mesh);\n\n// overload provided instead of templating so that the most common case of\n// transforming from `DataVector` to `ModalVector` does not require additional\n// `make_not_null`s\ntemplate <size_t Dim>\nvoid to_modal_coefficients(gsl::not_null<ModalVector*> modal_coefficients,\n                           const DataVector& nodal_coefficients,\n                           const Mesh<Dim>& mesh);\n\ntemplate <size_t Dim>\nModalVector to_modal_coefficients(const DataVector& nodal_coefficients,\n                                  const Mesh<Dim>& mesh);\n\ntemplate <size_t Dim>\nComplexModalVector to_modal_coefficients(\n    const ComplexDataVector& nodal_coefficients, const Mesh<Dim>& mesh);\n/// @}\n\n/// @{\n/*!\n * \\ingroup SpectralGroup\n * \\brief Compute the nodal coefficients from the modal coefficients\n *\n * \\see Spectral::modal_to_nodal_matrix\n */\ntemplate <size_t Dim>\nvoid to_nodal_coefficients(gsl::not_null<ComplexDataVector*> nodal_coefficients,\n                           const ComplexModalVector& modal_coefficients,\n                           const Mesh<Dim>& mesh);\n\n// overload provided instead of templating so that the most common case of\n// transforming from `DataVector` to `ModalVector` does not require additional\n// `make_not_null`s\ntemplate <size_t Dim>\nvoid to_nodal_coefficients(gsl::not_null<DataVector*> nodal_coefficients,\n                           const ModalVector& modal_coefficients,\n                           const Mesh<Dim>& mesh);\n\ntemplate <size_t Dim>\nDataVector to_nodal_coefficients(const ModalVector& modal_coefficients,\n                                 const Mesh<Dim>& mesh);\n\ntemplate <size_t Dim>\nComplexDataVector to_nodal_coefficients(\n    const ComplexModalVector& modal_coefficients, const Mesh<Dim>& mesh);\n/// @}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/DefiniteIntegral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n\n#include <array>  // Used for mesh.slices\n#include <numbers>\n#include <ostream>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\n// The 2D and 3D definite integrals have been optimized and are up to 2x faster\n// than the previous implementation. The main differences are\n//\n// - No memory allocations are required\n// - Manual loop unrolling (this will be somewhat hardware specific on the\n//   ~decade time scale).\n// - Pointers to avoid any potential overhead from indexing\n//\n// Note: The inner loop is over x because the memory layout used by SpECTRE is\n//       x-varies-fastest.\n\n// This specialization enables dim-independent integrals on boundaries:\n// a 0-dimensional mesh arises at the boundary of a 1-dimensional element.\ntemplate <>\ndouble definite_integral<0>(const DataVector& integrand,\n                            const Mesh<0>& /*mesh*/) {\n  ASSERT(integrand.size() == 1,\n         \"A 0-dimensional mesh has one point, but integrand has \"\n             << integrand.size() << \" points\");\n  return integrand[0];\n}\n\ntemplate <>\ndouble definite_integral<1>(const DataVector& integrand, const Mesh<1>& mesh) {\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  ASSERT(integrand.size() == num_grid_points,\n         \"num_grid_points = \" << num_grid_points\n                              << \", integrand size = \" << integrand.size());\n  const DataVector& weights = Spectral::quadrature_weights(mesh);\n  return ddot_(num_grid_points, weights.data(), 1, integrand.data(), 1);\n}\n\ntemplate <>\ndouble definite_integral<2>(const DataVector& integrand, const Mesh<2>& mesh) {\n  ASSERT(integrand.size() == mesh.number_of_grid_points(),\n         \"num_grid_points = \" << mesh.number_of_grid_points()\n                              << \", integrand size = \" << integrand.size());\n  const auto sliced_meshes = mesh.slices();\n  const size_t x_size = sliced_meshes[0].number_of_grid_points();\n  const size_t x_last_unrolled = x_size - x_size % 2;\n  const size_t y_size = sliced_meshes[1].number_of_grid_points();\n  double result = 0.0;\n  if (mesh.basis(0) == Spectral::Basis::ZernikeB2) {\n    const DataVector w_radial =\n        Spectral::quadrature_weights(sliced_meshes[0]) * 2.0 /\n        (Spectral::collocation_points(sliced_meshes[0]) + 1.0);\n    const double* const w_r = w_radial.data();\n    // Fourier weights are constant 2 \\pi / n_\\phi, no need to fetch\n    for (size_t j = 0; j < y_size; ++j) {\n      const size_t offset = j * x_size;\n      for (size_t i = 0; i < x_last_unrolled; i += 2) {\n        result += (w_r[i] * integrand[i + offset] +          // NOLINT\n                   w_r[i + 1] * integrand[i + 1 + offset]);  // NOLINT\n      }\n      for (size_t i = x_last_unrolled; i < x_size; ++i) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        result += w_r[i] * integrand[i + offset];\n      }\n    }\n    result *= (2 * std::numbers::pi / static_cast<double>(y_size));\n  } else {\n    const double* const w_x =\n        Spectral::quadrature_weights(sliced_meshes[0]).data();\n    const double* const w_y =\n        Spectral::quadrature_weights(sliced_meshes[1]).data();\n\n    for (size_t j = 0; j < y_size; ++j) {\n      const size_t offset = j * x_size;\n      for (size_t i = 0; i < x_last_unrolled; i += 2) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        result += w_y[j] * w_x[i] * integrand[i + offset] +\n                  w_y[j] * w_x[i + 1] * integrand[i + 1 + offset];  // NOLINT\n      }\n      for (size_t i = x_last_unrolled; i < x_size; ++i) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        result += w_y[j] * w_x[i] * integrand[i + offset];\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <>\ndouble definite_integral<3>(const DataVector& integrand, const Mesh<3>& mesh) {\n  if (mesh.basis(2) == Spectral::Basis::Cartoon) {\n    if (mesh.quadrature(2) == Spectral::Quadrature::SphericalSymmetry) {\n      // spherical symmetry, integrand should have x^2 and logical to\n      // inertial jacobian determinant already multiplied\n      ASSERT(integrand.size() == mesh.slice_through(0).number_of_grid_points(),\n             \"Mismatched input sizes: num_grid_points = \"\n                 << mesh.slice_through(0).number_of_grid_points()\n                 << \", integrand size = \" << integrand.size());\n      return 4.0 * M_PI *\n             definite_integral<1>(integrand, mesh.slice_through(0));\n    } else {\n      // axial symmetry, integrand should have x and logical to inertial\n      // jacobian already multiplied\n      ASSERT(\n          integrand.size() == mesh.slice_through(0, 1).number_of_grid_points(),\n          \"Mismatched input sizes: num_grid_points = \"\n              << mesh.slice_through(0, 1).number_of_grid_points()\n              << \", integrand size = \" << integrand.size());\n      return 2 * M_PI *\n             definite_integral<2>(integrand, mesh.slice_through(0, 1));\n    }\n  }\n  ASSERT(integrand.size() == mesh.number_of_grid_points(),\n         \"num_grid_points = \" << mesh.number_of_grid_points()\n                              << \", integrand size = \" << integrand.size());\n  const auto sliced_meshes = mesh.slices();\n  const size_t x_size = sliced_meshes[0].number_of_grid_points();\n  const size_t x_last_unrolled = x_size - x_size % 2;\n  const double* const w_x =\n      Spectral::quadrature_weights(sliced_meshes[0]).data();\n  double result = 0.0;\n  if (mesh.basis(1) == Spectral::Basis::SphericalHarmonic) {\n    const auto& ylm = ylm::get_spherepack_cache(mesh.extents(1) - 1);\n    const size_t angular_size = ylm.physical_size();\n    const std::vector<double>& w_ylm = ylm.integration_weights();\n    for (size_t j = 0; j < angular_size; ++j) {\n      const size_t offset = j * x_size;\n      for (size_t i = 0; i < x_last_unrolled; i += 2) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        result += w_ylm[j] * w_x[i] * integrand[i + offset] +\n                  w_ylm[j] * w_x[i + 1] * integrand[i + 1 + offset];  // NOLINT\n      }\n      for (size_t i = x_last_unrolled; i < x_size; ++i) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        result += w_ylm[j] * w_x[i] * integrand[i + offset];\n      }\n    }\n  } else if (mesh.basis(0) == Spectral::Basis::ZernikeB2) {\n    const size_t y_size = sliced_meshes[1].number_of_grid_points();\n    const size_t z_size = sliced_meshes[2].number_of_grid_points();\n    const DataVector w_radial =\n        Spectral::quadrature_weights(sliced_meshes[0]) * 2.0 /\n        (Spectral::collocation_points(sliced_meshes[0]) + 1.0);\n    const double* const w_r = w_radial.data();\n    // Fourier weights are constant 2 \\pi / n_\\phi, no need to fetch\n    const double* const w_z =\n        Spectral::quadrature_weights(sliced_meshes[2]).data();\n\n    for (size_t k = 0; k < z_size; ++k) {\n      for (size_t j = 0; j < y_size; ++j) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        const double z_contribution = w_z[k];\n        const size_t offset = x_size * (j + y_size * k);\n        for (size_t i = 0; i < x_last_unrolled; i += 2) {\n          result += z_contribution * w_r[i] * integrand[i + offset] +  // NOLINT\n                    z_contribution * w_r[i + 1] *                      // NOLINT\n                        integrand[i + 1 + offset];                     // NOLINT\n        }\n        for (size_t i = x_last_unrolled; i < x_size; ++i) {\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n          result += z_contribution * w_r[i] * integrand[i + offset];\n        }\n      }\n    }\n    result *= 2 * std::numbers::pi / static_cast<double>(y_size);\n  } else {\n    const size_t y_size = sliced_meshes[1].number_of_grid_points();\n    const size_t z_size = sliced_meshes[2].number_of_grid_points();\n    const double* const w_y =\n        Spectral::quadrature_weights(sliced_meshes[1]).data();\n    const double* const w_z =\n        Spectral::quadrature_weights(sliced_meshes[2]).data();\n\n    for (size_t k = 0; k < z_size; ++k) {\n      for (size_t j = 0; j < y_size; ++j) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n        const double prod = w_z[k] * w_y[j];\n        const size_t offset = x_size * (j + y_size * k);\n        // Unrolling at 2 gives better performance on AVX machines (the most\n        // common in 2018). The stride will probably need to be updated as\n        // hardware changes. Note: using a single loop is ~15% faster when\n        // x_size == 3, and both loop styles are comparable for x_size == 2.\n        for (size_t i = 0; i < x_last_unrolled; i += 2) {\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n          result += prod * w_x[i] * integrand[i + offset] +\n                    prod * w_x[i + 1] * integrand[i + 1 + offset];  // NOLINT\n        }\n        for (size_t i = x_last_unrolled; i < x_size; ++i) {\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n          result += prod * w_x[i] * integrand[i + offset];\n        }\n      }\n    }\n  }\n  return result;\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function definite_integral.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Compute the definite integral of a function over a manifold.\n *\n * Given a function \\f$f\\f$, compute its integral \\f$I\\f$ with respect to\n * the logical coordinates \\f$\\boldsymbol{\\xi} = (\\xi, \\eta, \\zeta)\\f$.\n * E.g., in 1 dimension, \\f$I = \\int_{-1}^1 f d\\xi\\f$.\n *\n * The integral w.r.t. a different set of coordinates\n * \\f$\\boldsymbol{x} = \\boldsymbol{x}(\\boldsymbol{\\xi})\\f$ can be computed\n * by pre-multiplying \\f$f\\f$ by the Jacobian determinant\n * \\f$J = \\det d\\boldsymbol{x}/d\\boldsymbol{\\xi}\\f$ of the mapping\n * \\f$\\boldsymbol{x}(\\boldsymbol{\\xi})\\f$. Note that, in the\n * \\f$\\boldsymbol{x}\\f$ coordinates, the domain of integration is the image of\n * the logical cube (square in 2D, interval in 1D) under the mapping.\n *\n * The integral is computed by quadrature, using the quadrature rule for the\n * basis associated with the collocation points.\n *\n * If the mesh uses a Cartoon basis, the passed integrand must already be\n * multiplied by the appropriate Jacobian determinants (i.e. both the\n * \\f$\\boldsymbol{x}(\\boldsymbol{\\xi})\\f$ and the cartesian to spherical/polar\n * coordinates mappings, the latter being either\n * \\f$x^2\\f$ or \\f$x\\f$, respectively).\n *\n * \\param integrand the function to integrate.\n * \\param mesh the Mesh defining the grid points on the manifold.\n */\ntemplate <size_t Dim>\ndouble definite_integral(const DataVector& integrand, const Mesh<Dim>& mesh);\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Divergence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, DerivativeFrame>;\n};\n}  // namespace\n\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nScalar<DataType> divergence(\n    const tnsr::I<DataType, Dim, DerivativeFrame>& input, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  Scalar<DataType> div_input{mesh.number_of_grid_points()};\n  divergence(make_not_null(&div_input), input, mesh, inverse_jacobian);\n  return div_input;\n}\n\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nvoid divergence(const gsl::not_null<Scalar<DataType>*> div_input,\n                const tnsr::I<DataType, Dim, DerivativeFrame>& input,\n                const Mesh<Dim>& mesh,\n                const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                      DerivativeFrame>& inverse_jacobian) {\n  set_number_of_grid_points(div_input, mesh.number_of_grid_points());\n\n  // We have to copy into a Variables because we don't currently have partial\n  // derivative functions for anything other than Variables.\n  using VectorTag = VectorTag<DataType, Dim, DerivativeFrame>;\n  Variables<tmpl::list<VectorTag>> vars{get<0>(input).size()};\n  get<VectorTag>(vars) = input;\n  const auto logical_derivs =\n      logical_partial_derivatives<tmpl::list<VectorTag>>(vars, mesh);\n\n  get(*div_input) = 0.0;\n  for (size_t logical_i = 0; logical_i < Dim; ++logical_i) {\n    for (size_t deriv_i = 0; deriv_i < Dim; ++deriv_i) {\n      get(*div_input) +=\n          inverse_jacobian.get(logical_i, deriv_i) *\n          get<VectorTag>(gsl::at(logical_derivs, logical_i)).get(deriv_i);\n    }\n  }\n}\n\ntemplate <typename ResultTensor, typename FluxTensor, size_t Dim>\nvoid logical_divergence(const gsl::not_null<ResultTensor*> div_flux,\n                        const FluxTensor& flux, const Mesh<Dim>& mesh) {\n  // Note: This function hasn't been optimized much at all. Feel free to\n  // optimize if needed!\n  static const Matrix identity_matrix{};\n  for (size_t d = 0; d < Dim; ++d) {\n    auto matrices = make_array<Dim>(std::cref(identity_matrix));\n    gsl::at(matrices, d) =\n        Spectral::differentiation_matrix(mesh.slice_through(d));\n    for (size_t storage_index = 0; storage_index < div_flux->size();\n         ++storage_index) {\n      const auto div_flux_index = div_flux->get_tensor_index(storage_index);\n      const auto flux_index = prepend(div_flux_index, d);\n      div_flux->get(div_flux_index) +=\n          apply_matrices(matrices, flux.get(flux_index), mesh.extents());\n    }\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void divergence(                                               \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> div_input,                \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& input,          \\\n      const Mesh<DIM(data)>& mesh,                                        \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian);              \\\n  template Scalar<DTYPE(data)> divergence(                                \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& input,          \\\n      const Mesh<DIM(data)>& mesh,                                        \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            FRAME(data)>& inverse_jacobian);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, ComplexDataVector), (1, 2, 3),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DTYPE\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TENSOR(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATION_SCALAR(r, data)                                     \\\n  template void logical_divergence(                                       \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> div_flux,                 \\\n      const tnsr::I<DTYPE(data), DIM(data), Frame::ElementLogical>& flux, \\\n      const Mesh<DIM(data)>& mesh);\n#define INSTANTIATION_TENSOR(r, data)                                    \\\n  template void logical_divergence(                                      \\\n      const gsl::not_null<tnsr::TENSOR(data) < DTYPE(data), DIM(data),   \\\n                          Frame::Inertial>* > div_flux,                  \\\n      const TensorMetafunctions::prepend_spatial_index<                  \\\n          tnsr::TENSOR(data) < DTYPE(data), DIM(data), Frame::Inertial>, \\\n      DIM(data), UpLo::Up, Frame::ElementLogical > &flux,                \\\n      const Mesh<DIM(data)>& mesh);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION_SCALAR, (DataVector, ComplexDataVector),\n                        (1, 2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATION_TENSOR, (DataVector, ComplexDataVector),\n                        (1, 2, 3), (i, I))\n\ntemplate void logical_divergence(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3, Frame::Inertial>*>\n        div_flux,\n    const TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3, Frame::Inertial>, 2, UpLo::Up,\n        Frame::ElementLogical>& flux,\n    const Mesh<2>& mesh);\n\n#undef INSTANTIATION_SCALAR\n#undef INSTANTIATION_TENSOR\n#undef DTYPE\n#undef DIM\n#undef TENSOR\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Divergence.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions and tags for taking a divergence.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename TagsList>\nclass Variables;\n\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim>\nstruct Mesh;\n}  // namespace Tags\n}  // namespace domain\n/// \\endcond\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating the divergence\n///\n/// Prefix indicating the divergence of a Tensor.\n///\n/// \\see Tags::DivVectorCompute Tags::DivVariablesCompute\ntemplate <typename Tag>\n  requires(tt::is_a_v<Tensor, typename Tag::type>)\nstruct div : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = TensorMetafunctions::remove_first_index<typename Tag::type>;\n};\n}  // namespace Tags\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Compute the (Euclidean) divergence of fluxes\ntemplate <typename FluxTags, size_t Dim, typename DerivativeFrame>\nauto divergence(const Variables<FluxTags>& F, const Mesh<Dim>& mesh,\n                const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                      DerivativeFrame>& inverse_jacobian)\n    -> Variables<db::wrap_tags_in<Tags::div, FluxTags>>;\n\ntemplate <typename... DivTags, typename... FluxTags, size_t Dim,\n          typename DerivativeFrame>\nvoid divergence(\n    gsl::not_null<Variables<tmpl::list<DivTags...>>*> divergence_of_F,\n    const Variables<tmpl::list<FluxTags...>>& F, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian);\n/// @}\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Compute the divergence of the vector `input`\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nScalar<DataType> divergence(\n    const tnsr::I<DataType, Dim, DerivativeFrame>& input, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian);\n\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nvoid divergence(gsl::not_null<Scalar<DataType>*> div_input,\n                const tnsr::I<DataType, Dim, DerivativeFrame>& input,\n                const Mesh<Dim>& mesh,\n                const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                      DerivativeFrame>& inverse_jacobian);\n/// @}\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Compute the divergence of fluxes where a Cartoon basis is being\n * utilized.\n *\n * The additional parameter `inertial_coords` is used for division by the\n * \\f$x\\f$ coordinates. If \\f$x=0\\f$ is included in the domain, it is assumed to\n * be present only at the first index and is handled by L'H&ocirc;pital's rule.\n *\n * The mesh is required to have the Cartoon basis in the last and potentially\n * second-to-last coordinates and the inverse jacobian is accordingly used only\n * in the first and potentially second dimensions.\n *\n * \\see cartoon_partial_derivatives for details on Cartoon derivatives.\n */\ntemplate <typename... DivTags, typename... FluxTags, size_t Dim,\n          typename DerivativeFrame, Requires<Dim == 3> = nullptr>\nvoid cartoon_divergence(\n    gsl::not_null<Variables<tmpl::list<DivTags...>>*> divergence_of_F,\n    const Variables<tmpl::list<FluxTags...>>& F, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian_3d,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n/// @}\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Calls the correct divergence function, either normal divergence or\n * cartoon divergence, as determined by mesh basis.\n *\n * If you have a `Variables` with several tensors with Cartoon bases you need\n * to find the divergence of, you should use the `divergence` function\n * that operates on `Variables` since that'll be more efficient.\n */\ntemplate <typename... DivTags, typename... FluxTags, size_t Dim,\n          typename DerivativeFrame>\nvoid divergence(\n    gsl::not_null<Variables<tmpl::list<DivTags...>>*> div_fluxes,\n    const Variables<tmpl::list<FluxTags...>>& fluxes, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian_3d,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nvoid divergence(\n    gsl::not_null<Scalar<DataType>*> div_input,\n    const tnsr::I<DataType, Dim, DerivativeFrame>& input, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nScalar<DataType> divergence(\n    const tnsr::I<DataType, Dim, DerivativeFrame>& input, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n/// @}\n\n/// @{\n/*!\n * \\brief Compute the divergence of fluxes in logical coordinates\n *\n * Applies the logical differentiation matrix to the fluxes in each dimension\n * and sums over dimensions.\n *\n * \\see divergence\n */\ntemplate <typename ResultTensor, typename FluxTensor, size_t Dim>\nvoid logical_divergence(gsl::not_null<ResultTensor*> div_flux,\n                        const FluxTensor& flux, const Mesh<Dim>& mesh);\n\ntemplate <typename FluxTags, size_t Dim>\nauto logical_divergence(const Variables<FluxTags>& flux, const Mesh<Dim>& mesh)\n    -> Variables<db::wrap_tags_in<Tags::div, FluxTags>>;\n\ntemplate <typename... DivTags, typename... FluxTags, size_t Dim>\nvoid logical_divergence(\n    gsl::not_null<Variables<tmpl::list<DivTags...>>*> div_flux,\n    const Variables<tmpl::list<FluxTags...>>& flux, const Mesh<Dim>& mesh);\n/// @}\n\nnamespace Tags {\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\brief Compute the divergence of a Variables\n *\n * Computes the divergence of the every Tensor in the Variables represented by\n * `Tag`. The first index of each Tensor must be an upper spatial index, i.e.,\n * the first index must have type\n * `TensorIndexType<Dim, UpLo::Up, Frame::TargetFrame, IndexType::Spatial>`.\n * The divergence is computed in the frame `TargetFrame`, and\n * `InverseJacobianTag` must be associated with a map from\n * `Frame::ElementLogical` to `Frame::TargetFrame`.\n *\n * Note that each tensor may have additional tensor indices - in this case the\n * divergence is computed for each additional index. For instance, a tensor\n * \\f$F^i_{ab}\\f$ has divergence\n * \\f$Div_{ab} = \\partial_i F^i_{ab}\\f$. This is to accommodate evolution\n * equations where the evolved variables \\f$u_\\alpha\\f$ are higher-rank tensors\n * and thus their fluxes can be written as \\f$F^i_\\alpha\\f$. A simple example\n * would be the fluid velocity in hydro systems, where we would write the flux\n * as \\f$F^{ij}\\f$.\n *\n * This tag inherits from `db::add_tag_prefix<Tags::div, Tag>`.\n */\ntemplate <typename Tag, typename MeshTag, typename InverseJacobianTag>\nstruct DivVariablesCompute : db::add_tag_prefix<div, Tag>, db::ComputeTag {\n private:\n  using inv_jac_indices = typename InverseJacobianTag::type::index_list;\n  static constexpr auto dim = tmpl::back<inv_jac_indices>::dim;\n  static_assert(std::is_same_v<typename tmpl::front<inv_jac_indices>::Frame,\n                               Frame::ElementLogical>,\n                \"Must map from the logical frame.\");\n\n public:\n  using base = db::add_tag_prefix<div, Tag>;\n  using return_type = typename base::type;\n  static constexpr void (*function)(\n      const gsl::not_null<return_type*>, const typename Tag::type&,\n      const Mesh<dim>&, const typename InverseJacobianTag::type&) = &divergence;\n  using argument_tags =\n      tmpl::list<Tag, domain::Tags::Mesh<dim>, InverseJacobianTag>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Compute the divergence of a `tnsr::I` (vector)\n///\n/// This tag inherits from `db::add_tag_prefix<Tags::div, Tag>`.\n///\n/// For an executable that does not allow a Cartoon basis, the last parameter,\n/// `InertialCoordsTag`, should not be passed.\ntemplate <typename Tag, typename MeshTag, typename InverseJacobianTag,\n          typename InertialCoordsTag = void>\nstruct DivVectorCompute : div<Tag>, db::ComputeTag {\n private:\n  using inv_jac_indices = typename InverseJacobianTag::type::index_list;\n  static constexpr auto dim = tmpl::back<inv_jac_indices>::dim;\n  static_assert(std::is_same_v<typename tmpl::front<inv_jac_indices>::Frame,\n                               Frame::ElementLogical>,\n                \"Must map from the logical frame.\");\n\n public:\n  using base = div<Tag>;\n  using return_type = typename base::type;\n  static constexpr void function(\n      const gsl::not_null<return_type*> div_input,\n      const typename Tag::type& input, const Mesh<dim>& mesh,\n      const typename InverseJacobianTag::type& inverse_jacobian) {\n    divergence(div_input, input, mesh, inverse_jacobian);\n  }\n  static constexpr void function(\n      const gsl::not_null<return_type*> div_input,\n      const typename Tag::type& input, const Mesh<dim>& mesh,\n      const typename InverseJacobianTag::type& inverse_jacobian,\n      const tnsr::I<DataVector, dim, Frame::Inertial>& inertial_coords) {\n    divergence(div_input, input, mesh, inverse_jacobian, inertial_coords);\n  }\n  using argument_tags = tmpl::conditional_t<\n      std::is_same_v<void, InertialCoordsTag>,\n      tmpl::list<Tag, MeshTag, InverseJacobianTag>,\n      tmpl::list<Tag, MeshTag, InverseJacobianTag, InertialCoordsTag>>;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Divergence.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\ntemplate <typename FluxTags, size_t Dim, typename DerivativeFrame>\nVariables<db::wrap_tags_in<Tags::div, FluxTags>> divergence(\n    const Variables<FluxTags>& F, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  Variables<db::wrap_tags_in<Tags::div, FluxTags>> divergence_of_F(\n      F.number_of_grid_points());\n  divergence(make_not_null(&divergence_of_F), F, mesh, inverse_jacobian);\n  return divergence_of_F;\n}\n\ntemplate <typename... DivTags, typename... FluxTags, size_t Dim,\n          typename DerivativeFrame>\nvoid divergence(\n    const gsl::not_null<Variables<tmpl::list<DivTags...>>*> divergence_of_F,\n    const Variables<tmpl::list<FluxTags...>>& F, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  if (UNLIKELY(divergence_of_F->number_of_grid_points() !=\n               mesh.number_of_grid_points())) {\n    divergence_of_F->initialize(mesh.number_of_grid_points());\n  }\n\n  using DerivativeTags = tmpl::list<FluxTags...>;\n  using ValueType = typename Variables<DerivativeTags>::value_type;\n  const size_t vars_size =\n      Variables<DerivativeTags>::number_of_independent_components *\n      F.number_of_grid_points();\n  const auto logical_derivs_data =\n      cpp20::make_unique_for_overwrite<ValueType[]>(\n          (Dim > 1 ? (Dim + 2) : Dim) * vars_size);\n  std::array<ValueType*, Dim> logical_derivs{};\n  std::array<Variables<DerivativeTags>, Dim> logical_partial_derivatives_of_F{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(logical_derivs, i) = &(logical_derivs_data[i * vars_size]);\n    gsl::at(logical_partial_derivatives_of_F, i)\n        .set_data_ref(gsl::at(logical_derivs, i), vars_size);\n  }\n  if constexpr (Dim > 1) {\n    Variables<DerivativeTags> temp0{&logical_derivs_data[Dim * vars_size],\n                                    vars_size};\n    Variables<DerivativeTags> temp1{&logical_derivs_data[(Dim + 1) * vars_size],\n                                    vars_size};\n    partial_derivatives_detail::\n        LogicalImpl<Dim, tmpl::list<FluxTags...>, DerivativeTags>::apply(\n            make_not_null(&logical_derivs), &temp0, &temp1, F, mesh);\n  } else {\n    Variables<DerivativeTags>* temp = nullptr;\n    partial_derivatives_detail::\n        LogicalImpl<Dim, tmpl::list<FluxTags...>, DerivativeTags>::apply(\n            make_not_null(&logical_derivs), temp, temp, F, mesh);\n  }\n\n  const auto apply_div = [&divergence_of_F, &inverse_jacobian,\n                          &logical_partial_derivatives_of_F](auto flux_tag_v,\n                                                             auto div_tag_v) {\n    using FluxTag = std::decay_t<decltype(flux_tag_v)>;\n    using DivFluxTag = std::decay_t<decltype(div_tag_v)>;\n\n    using first_index = tmpl::front<typename FluxTag::type::index_list>;\n    static_assert(\n        std::is_same_v<typename first_index::Frame, DerivativeFrame> and\n            first_index::ul == UpLo::Up,\n        \"First index of tensor cannot be contracted with derivative \"\n        \"because either it is in the wrong frame or it has the wrong \"\n        \"valence\");\n\n    auto& divergence_of_flux = get<DivFluxTag>(*divergence_of_F);\n    for (auto it = divergence_of_flux.begin(); it != divergence_of_flux.end();\n         ++it) {\n      *it = 0.0;\n      const auto div_flux_indices = divergence_of_flux.get_tensor_index(it);\n      for (size_t i0 = 0; i0 < Dim; ++i0) {\n        const auto flux_indices = prepend(div_flux_indices, i0);\n        for (size_t d = 0; d < Dim; ++d) {\n          *it += inverse_jacobian.get(d, i0) *\n                 get<FluxTag>(gsl::at(logical_partial_derivatives_of_F, d))\n                     .get(flux_indices);\n        }\n      }\n    }\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(apply_div(FluxTags{}, DivTags{}));\n}\n\ntemplate <size_t CompDim, typename DivTags, typename FluxTags, size_t Dim,\n          typename DerivativeFrame>\nvoid cartoon_divergence_apply(\n    const gsl::not_null<Variables<DivTags>*> divergence_of_F,\n    const Variables<FluxTags>& F, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian_3d,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  static_assert(Dim == 3);\n  ASSERT((CompDim == 2 and\n          mesh.quadrature(2) == Spectral::Quadrature::AxialSymmetry) or\n             (CompDim == 1 and\n              (mesh.quadrature(1) == Spectral::Quadrature::SphericalSymmetry and\n               mesh.quadrature(2) == Spectral::Quadrature::SphericalSymmetry)),\n         \"Invalid Quadrature combinations: axial symmetry requires 2 \"\n         \"non-Cartoon dimensions, spherical symmetry requires 1 non-Cartoon \"\n         \"dimension. Got: \"\n             << mesh.quadrature());\n\n  if (UNLIKELY(divergence_of_F->number_of_grid_points() !=\n               mesh.number_of_grid_points())) {\n    divergence_of_F->initialize(mesh.number_of_grid_points());\n  }\n\n  using DerivativeTags = FluxTags;\n  using ValueType = typename Variables<DerivativeTags>::value_type;\n  const size_t vars_size =\n      Variables<DerivativeTags>::number_of_independent_components *\n      F.number_of_grid_points();\n  const auto logical_derivs_data =\n      cpp20::make_unique_for_overwrite<ValueType[]>(\n          (CompDim > 1 ? (CompDim + 2) : CompDim) * vars_size);\n  std::array<ValueType*, CompDim> logical_derivs{};\n  std::array<Variables<DerivativeTags>, CompDim>\n      logical_partial_derivatives_of_F{};\n  for (size_t i = 0; i < CompDim; ++i) {\n    gsl::at(logical_derivs, i) = &(logical_derivs_data[i * vars_size]);\n    gsl::at(logical_partial_derivatives_of_F, i)\n        .set_data_ref(gsl::at(logical_derivs, i), vars_size);\n  }\n\n  if constexpr (CompDim == 1) {\n    Variables<DerivativeTags>* temp = nullptr;\n    partial_derivatives_detail::LogicalImpl<CompDim, FluxTags, DerivativeTags>::\n        apply(make_not_null(&logical_derivs), temp, temp, F,\n              mesh.slice_through(0));\n  } else {\n    Variables<DerivativeTags> temp0{&logical_derivs_data[CompDim * vars_size],\n                                    vars_size};\n    Variables<DerivativeTags> temp1{\n        &logical_derivs_data[(CompDim + 1) * vars_size], vars_size};\n    partial_derivatives_detail::LogicalImpl<CompDim, FluxTags, DerivativeTags>::\n        apply(make_not_null(&logical_derivs), &temp0, &temp1, F,\n              mesh.slice_through(0, 1));\n  }\n\n  const Spectral::Quadrature quad_type = mesh.quadrature(2);\n  const auto numerical_deriv_in_this_direction = [](const size_t deriv_num) {\n    if constexpr (CompDim == 2) {\n      return deriv_num == 0 or deriv_num == 1;\n    } else {\n      return deriv_num == 0;\n    }\n  };\n\n  DataVector safe_x_coords = get<0>(inertial_coords);\n  const bool element_contains_zero_of_symmetry_axis = equal_within_roundoff(\n      0.0, safe_x_coords[0], std::numeric_limits<double>::epsilon() * 100.0,\n      max(safe_x_coords));\n  // Having x=0 requires both not dividing by zero (done here by setting to\n  // arbitrary non-zero value) and remembering to then do L'Hopital\n  if (element_contains_zero_of_symmetry_axis) {\n    safe_x_coords[0] = 1.0;\n    if constexpr (CompDim == 2) {\n      const size_t x_extents = mesh.extents(0);\n      const size_t y_extents = mesh.extents(1);\n      for (size_t i = 1; i < y_extents; ++i) {\n        ASSERT(\n            equal_within_roundoff(\n                0.0, safe_x_coords[i * x_extents],\n                std::numeric_limits<double>::epsilon() * 100.0,\n                max(safe_x_coords)),\n            \"The passed inertial coordinates do not follow the required format \"\n            \"of a rectangular mesh with x=0 being located at the first index \"\n            \"of each constant-y subdomain of the x DataVector. x=\"\n                << safe_x_coords);\n        safe_x_coords[i * x_extents] = 1.0;\n      }\n    }\n  }\n  // If doing L'Hopital's rule, we need to store temporary forms of the data\n  // in two different tensors (hence the 0 & 1 labels) for each tensor type\n  // They only have to hold the x=0 positions, so of size y_extents\n  using FluxTypes = tmpl::remove_duplicates<\n      tmpl::transform<FluxTags, tmpl::bind<tmpl::type_from, tmpl::_1>>>;\n\n  using TempTags0 = ::Tags::convert_to_temp_tensors<FluxTypes, 0>;\n  using TempTags1 = ::Tags::convert_to_temp_tensors<FluxTypes, 1>;\n  using VarTags = tmpl::append<TempTags0, TempTags1>;\n\n  Variables<VarTags> temp_vars{};\n  if (element_contains_zero_of_symmetry_axis) {\n    const size_t y_extents = mesh.extents(1);\n    temp_vars.initialize(y_extents);\n  }\n\n  tmpl::for_each<\n      FluxTags>([&divergence_of_F, &F, &inverse_jacobian_3d, &mesh,\n                 &safe_x_coords, &logical_partial_derivatives_of_F,\n                 &numerical_deriv_in_this_direction, &quad_type, &temp_vars,\n                 &element_contains_zero_of_symmetry_axis]<typename FluxTag>(\n                    tmpl::type_<FluxTag> /*meta*/) {\n    using DivFluxTag = tmpl::at<DivTags, tmpl::index_of<FluxTags, FluxTag>>;\n\n    using first_index = tmpl::front<typename FluxTag::type::index_list>;\n    static_assert(\n        std::is_same_v<typename first_index::Frame, DerivativeFrame> and\n            first_index::ul == UpLo::Up,\n        \"First index of tensor cannot be contracted with derivative \"\n        \"because either it is in the wrong frame or it has the wrong \"\n        \"valence\");\n    static_assert(first_index::index_type == IndexType::Spatial);\n\n    auto& divergence_of_flux = get<DivFluxTag>(*divergence_of_F);\n    auto& flux = get<FluxTag>(F);\n    TensorMetafunctions::prepend_spatial_index<typename FluxTag::type, Dim,\n                                               UpLo::Lo, Frame::Inertial>\n        cart_deriv_tensor;\n    cartoon_derivative<typename FluxTag::type, 3, Frame::Inertial>(\n        cart_deriv_tensor, flux, safe_x_coords, quad_type);\n\n    if (not element_contains_zero_of_symmetry_axis) {\n      for (auto it = divergence_of_flux.begin(); it != divergence_of_flux.end();\n           ++it) {\n        *it = 0.0;\n        const auto div_flux_indices = divergence_of_flux.get_tensor_index(it);\n        for (size_t i0 = 0; i0 < Dim; ++i0) {\n          const auto flux_indices = prepend(div_flux_indices, i0);\n          if (numerical_deriv_in_this_direction(i0)) {\n            // only accessing relevenat entries of inverse_jacobian_3d\n            for (size_t d = 0; d < CompDim; ++d) {\n              *it += inverse_jacobian_3d.get(d, i0) *\n                     get<FluxTag>(gsl::at(logical_partial_derivatives_of_F, d))\n                         .get(flux_indices);\n            }\n          } else {\n            const auto d_flux_indices = prepend(flux_indices, i0);\n            *it += cart_deriv_tensor.get(d_flux_indices);\n          }\n        }\n      }\n    } else {\n      const size_t x_extents = mesh.extents(0);\n      const size_t y_extents = mesh.extents(1);\n      const auto insert_with_stride =\n          [x_extents, y_extents](gsl::not_null<DataVector*> to_insert_into,\n                                 const DataVector& take_from,\n                                 const bool accumulate = false) {\n            auto& insert_into = *to_insert_into;\n            if (accumulate) {\n              for (size_t i = 0; i < y_extents; ++i) {\n                insert_into[i] += take_from[i * x_extents];\n              }\n            } else {\n              for (size_t i = 0; i < y_extents; ++i) {\n                insert_into[i] = take_from[i * x_extents];\n              }\n            }\n          };\n      // for storing intermediate x-derivatives\n      auto& dx_flux =\n          get<::Tags::TempTensor<0, typename FluxTag::type>>(temp_vars);\n      auto& contracted_dx_flux =\n          get<::Tags::TempTensor<1, typename FluxTag::type>>(temp_vars);\n\n      for (auto it = divergence_of_flux.begin(); it != divergence_of_flux.end();\n           ++it) {\n        *it = 0.0;\n        const auto div_flux_indices = divergence_of_flux.get_tensor_index(it);\n        for (size_t first_flux_index = 0; first_flux_index < index_dim<0>(flux);\n             ++first_flux_index) {\n          auto flux_indices = prepend(div_flux_indices, first_flux_index);\n          for (size_t d_i = 0; d_i < Dim; ++d_i) {\n            if (numerical_deriv_in_this_direction(d_i)) {\n              if (d_i == first_flux_index) {\n                const auto d_indices = prepend(div_flux_indices, d_i);\n                for (size_t d_contract = 0; d_contract < CompDim;\n                     ++d_contract) {\n                  *it += inverse_jacobian_3d.get(d_contract, d_i) *\n                         get<FluxTag>(gsl::at(logical_partial_derivatives_of_F,\n                                              d_contract))\n                             .get(d_indices);\n                }\n              }\n              if (d_i == 0) {\n                // storing all \\partial_x derivatives for L'Hopital\n                if (first_flux_index == d_i) {\n                  insert_with_stride(make_not_null(&dx_flux.get(flux_indices)),\n                                     *it);\n                } else {\n                  insert_with_stride(\n                      make_not_null(&dx_flux.get(flux_indices)),\n                      inverse_jacobian_3d.get(0, d_i) *\n                          get<FluxTag>(\n                              gsl::at(logical_partial_derivatives_of_F, 0))\n                              .get(flux_indices));\n                  if constexpr (CompDim == 2) {\n                    insert_with_stride(\n                        make_not_null(&dx_flux.get(flux_indices)),\n                        inverse_jacobian_3d.get(1, d_i) *\n                            get<FluxTag>(\n                                gsl::at(logical_partial_derivatives_of_F, 1))\n                                .get(flux_indices),\n                        true);\n                  }\n                }\n              }\n            } else if (first_flux_index == d_i) {\n              const auto d_flux_indices = prepend(flux_indices, d_i);\n              *it += cart_deriv_tensor.get(d_flux_indices);\n            }\n          }\n        }\n      }\n      const size_t start_index =\n          quad_type == Spectral::Quadrature::SphericalSymmetry ? 1 : 2;\n      for (size_t deriv_index = start_index; deriv_index < Dim; ++deriv_index) {\n        cartoon_contraction(make_not_null(&contracted_dx_flux), dx_flux,\n                            deriv_index, quad_type);\n        for (size_t component_index = 0;\n             component_index < contracted_dx_flux.size(); ++component_index) {\n          const auto input_index =\n              contracted_dx_flux.get_tensor_index(component_index);\n          if (gsl::at(input_index, 0) == deriv_index) {\n            std::array<size_t, input_index.size() - 1> output_indices{};\n            std::copy(input_index.begin() + 1, input_index.end(),\n                      output_indices.begin());\n            const size_t output_index =\n                divergence_of_flux.get_storage_index(output_indices);\n            for (size_t i = 0; i < y_extents; ++i) {\n              divergence_of_flux[output_index][i * x_extents] +=\n                  contracted_dx_flux[component_index][i];\n            }\n          }\n        }\n      }\n    }\n  });\n}\n\ntemplate <typename... DivTags, typename... FluxTags, size_t Dim,\n          typename DerivativeFrame, Requires<Dim == 3>>\nvoid cartoon_divergence(\n    const gsl::not_null<Variables<tmpl::list<DivTags...>>*> divergence_of_F,\n    const Variables<tmpl::list<FluxTags...>>& F, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian_3d,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  if (mesh.basis(0) != Spectral::Basis::Cartoon and\n      mesh.basis(2) == Spectral::Basis::Cartoon) {\n    if (mesh.basis(1) == Spectral::Basis::Cartoon) {\n      cartoon_divergence_apply<1, tmpl::list<DivTags...>,\n                               tmpl::list<FluxTags...>, Dim, DerivativeFrame>(\n          divergence_of_F, F, mesh, inverse_jacobian_3d, inertial_coords);\n    } else {\n      cartoon_divergence_apply<2, tmpl::list<DivTags...>,\n                               tmpl::list<FluxTags...>, Dim, DerivativeFrame>(\n          divergence_of_F, F, mesh, inverse_jacobian_3d, inertial_coords);\n    }\n  } else {\n    ERROR(\"Bases do not match valid Cartoon pattern. Got mesh \" << mesh);\n  }\n}\n\ntemplate <typename... DivTags, typename... FluxTags, size_t Dim,\n          typename DerivativeFrame>\nvoid divergence(\n    const gsl::not_null<Variables<tmpl::list<DivTags...>>*> div_fluxes,\n    const Variables<tmpl::list<FluxTags...>>& fluxes, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian_3d,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  if constexpr (Dim == 3) {\n    if (mesh.basis(2) == Spectral::Basis::Cartoon) {\n      cartoon_divergence(div_fluxes, fluxes, mesh, inverse_jacobian_3d,\n                         inertial_coords);\n    } else {\n      divergence(div_fluxes, fluxes, mesh, inverse_jacobian_3d);\n    }\n  } else {\n    (void)inertial_coords;\n    divergence(div_fluxes, fluxes, mesh, inverse_jacobian_3d);\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nvoid divergence(\n    const gsl::not_null<Scalar<DataType>*> div_input,\n    const tnsr::I<DataType, Dim, DerivativeFrame>& input, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  if constexpr (Dim == 3) {\n    if (mesh.basis(2) == Spectral::Basis::Cartoon) {\n      auto& div_f_tnsr = *div_input;\n      using f_type = tnsr::I<DataType, Dim, DerivativeFrame>;\n      using div_f_type = Scalar<DataType>;\n      using vars_list = ::Tags::convert_to_temp_tensors<tmpl::list<f_type>, 0>;\n      using div_vars_list =\n          ::Tags::convert_to_temp_tensors<tmpl::list<div_f_type>, 0>;\n      Variables<vars_list> f_vars{mesh.number_of_grid_points()};\n      get<tmpl::front<vars_list>>(f_vars) = input;\n      Variables<div_vars_list> div_f_vars{mesh.number_of_grid_points()};\n      cartoon_divergence(make_not_null(&div_f_vars), f_vars, mesh,\n                         inverse_jacobian, inertial_coords);\n      div_f_tnsr = get<tmpl::front<div_vars_list>>(div_f_vars);\n    } else {\n      divergence(div_input, input, mesh, inverse_jacobian);\n    }\n  } else {\n    (void)inertial_coords;\n    divergence(div_input, input, mesh, inverse_jacobian);\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename DerivativeFrame>\nScalar<DataType> divergence(\n    const tnsr::I<DataType, Dim, DerivativeFrame>& input, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  if constexpr (Dim == 3) {\n    if (mesh.basis(2) == Spectral::Basis::Cartoon) {\n      using f_type = tnsr::I<DataType, Dim, DerivativeFrame>;\n      using div_f_type = Scalar<DataType>;\n      using vars_list = ::Tags::convert_to_temp_tensors<tmpl::list<f_type>, 0>;\n      using div_vars_list =\n          ::Tags::convert_to_temp_tensors<tmpl::list<div_f_type>, 0>;\n      Variables<vars_list> f_vars{mesh.number_of_grid_points()};\n      get<tmpl::front<vars_list>>(f_vars) = input;\n      Variables<div_vars_list> div_f_vars{mesh.number_of_grid_points()};\n      cartoon_divergence(make_not_null(&div_f_vars), f_vars, mesh,\n                         inverse_jacobian, inertial_coords);\n      return get<tmpl::front<div_vars_list>>(div_f_vars);\n    } else {\n      return divergence(input, mesh, inverse_jacobian);\n    }\n  } else {\n    (void)inertial_coords;\n    return divergence(input, mesh, inverse_jacobian);\n  }\n}\n\ntemplate <typename FluxTags, size_t Dim>\nVariables<db::wrap_tags_in<Tags::div, FluxTags>> logical_divergence(\n    const Variables<FluxTags>& flux, const Mesh<Dim>& mesh) {\n  Variables<db::wrap_tags_in<Tags::div, FluxTags>> div_flux(\n      flux.number_of_grid_points());\n  logical_divergence(make_not_null(&div_flux), flux, mesh);\n  return div_flux;\n}\n\ntemplate <typename... ResultTags, typename... FluxTags, size_t Dim>\nvoid logical_divergence(\n    const gsl::not_null<Variables<tmpl::list<ResultTags...>>*> div_flux,\n    const Variables<tmpl::list<FluxTags...>>& flux, const Mesh<Dim>& mesh) {\n  static_assert(\n      (... and\n       std::is_same_v<tmpl::front<typename FluxTags::type::index_list>,\n                      SpatialIndex<Dim, UpLo::Up, Frame::ElementLogical>>),\n      \"The first index of each flux must be an upper spatial index in \"\n      \"element-logical coordinates.\");\n  static_assert(\n      (... and\n       std::is_same_v<\n           typename ResultTags::type,\n           TensorMetafunctions::remove_first_index<typename FluxTags::type>>),\n      \"The result tensors must have the same type as the flux tensors with \"\n      \"their first index removed.\");\n  div_flux->initialize(mesh.number_of_grid_points(), 0.);\n  // Note: This function hasn't been optimized much at all. Feel free to\n  // optimize if needed!\n  EXPAND_PACK_LEFT_TO_RIGHT(logical_divergence(\n      make_not_null(&get<ResultTags>(*div_flux)), get<FluxTags>(flux), mesh));\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/ExponentialFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n\n#include <string>\n#include <unordered_set>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Filtering.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Filters {\n\ntemplate <size_t FilterIndex>\nExponential<FilterIndex>::Exponential(\n    const double alpha, const unsigned half_power, const bool enable,\n    const std::optional<std::vector<std::string>>& blocks_to_filter,\n    const Options::Context& context)\n    : alpha_(alpha), half_power_(half_power), enable_(enable) {\n  if (blocks_to_filter.has_value()) {\n    blocks_to_filter_ = std::unordered_set<std::string>{};\n    for (const std::string& block_name : blocks_to_filter.value()) {\n      if (blocks_to_filter_->count(block_name) != 0) {\n        PARSE_ERROR(context,\n                    \"Duplicate block name '\"\n                        << block_name\n                        << \"' found when creating an Exponential filter.\");\n      }\n\n      blocks_to_filter_->emplace(block_name);\n    }\n  }\n}\n\ntemplate <size_t FilterIndex>\nconst Matrix& Exponential<FilterIndex>::filter_matrix(\n    const Mesh<1>& mesh) const {\n  const static double cached_alpha = alpha_;\n\n  ASSERT(cached_alpha == alpha_, \"Filter was cached with alpha = \"\n                                     << cached_alpha << \", but alpha is now \"\n                                     << alpha_\n                                     << \".\\nUse a different FilterIndex if you \"\n                                        \"need a filter with new parameters\\n\");\n  const static double cached_half_power = half_power_;\n  ASSERT(cached_half_power == half_power_,\n         \"Filter was cached with half power = \"\n             << cached_half_power << \", but half power is now \" << half_power_\n             << \".\\nUse a different FilterIndex if you need a filter with new \"\n                \"parameters\\n\");\n\n  const auto compute_filter = [alpha = alpha_, half_power = half_power_](\n                                  const size_t extents,\n                                  const Spectral::Basis basis,\n                                  const Spectral::Quadrature quadrature) {\n    return Spectral::filtering::exponential_filter(\n        Mesh<1>{extents, basis, quadrature}, alpha, half_power);\n  };\n  const static auto cache = make_static_cache<\n      CacheRange<1_st,\n                 Spectral::maximum_number_of_points<Spectral::Basis::Legendre> +\n                     1>,\n      CacheEnumeration<Spectral::Basis, Spectral::Basis::Legendre,\n                       Spectral::Basis::Chebyshev, Spectral::Basis::Cartoon>,\n      CacheEnumeration<Spectral::Quadrature, Spectral::Quadrature::Gauss,\n                       Spectral::Quadrature::GaussLobatto,\n                       Spectral::Quadrature::AxialSymmetry,\n                       Spectral::Quadrature::SphericalSymmetry>>(\n      compute_filter);\n  const static auto fourier_cache = make_static_cache<CacheRange<\n      1_st, Spectral::maximum_number_of_points<Spectral::Basis::Fourier> + 1>>(\n      [compute_filter](const size_t extents) {\n        return compute_filter(extents, Spectral::Basis::Fourier,\n                              Spectral::Quadrature::Equiangular);\n      });\n  if (mesh.basis(0) == Spectral::Basis::Fourier) {\n    return fourier_cache(mesh.extents(0));\n  }\n  return cache(mesh.extents(0), mesh.basis(0), mesh.quadrature(0));\n}\n\ntemplate <size_t FilterIndex>\nvoid Exponential<FilterIndex>::pup(PUP::er& p) {\n  p | alpha_;\n  p | half_power_;\n  p | enable_;\n  p | blocks_to_filter_;\n}\n\ntemplate <size_t LocalFilterIndex>\nbool operator==(const Exponential<LocalFilterIndex>& lhs,\n                const Exponential<LocalFilterIndex>& rhs) {\n  return lhs.alpha_ == rhs.alpha_ and lhs.half_power_ == rhs.half_power_ and\n         lhs.enable_ == rhs.enable_ and\n         lhs.blocks_to_filter_ == rhs.blocks_to_filter_;\n}\n\ntemplate <size_t FilterIndex>\nbool operator!=(const Exponential<FilterIndex>& lhs,\n                const Exponential<FilterIndex>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define FILTER_INDEX(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GEN_OP(op, filter_index)                                  \\\n  template bool operator op(const Exponential<filter_index>& lhs, \\\n                            const Exponential<filter_index>& rhs);\n#define INSTANTIATE(_, data)                      \\\n  template class Exponential<FILTER_INDEX(data)>; \\\n  GEN_OP(==, FILTER_INDEX(data))                  \\\n  GEN_OP(!=, FILTER_INDEX(data))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2, 3, 4, 5, 6, 7, 8, 9))\n\n#undef FILTER_INDEX\n#undef INSTANTIATE\n\n}  // namespace Filters\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <unordered_set>\n\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass Matrix;\ntemplate <size_t Dim>\nclass Mesh;\n\n/// \\endcond\n\nnamespace Filters {\n\n/*!\n * \\ingroup DiscontinuousGalerkinGroup\n * \\brief A cached exponential filter.\n *\n * Applies an exponential filter in each logical direction to each component of\n * the tensors `TagsToFilter`. The exponential filter rescales the 1d modal\n * coefficients \\f$c_i\\f$ as:\n *\n * \\f{align*}{\n *  c_i\\to c_i \\exp\\left[-\\alpha_{\\mathrm{ef}}\n *   \\left(\\frac{i}{N}\\right)^{2\\beta_{\\mathrm{ef}}}\\right]\n * \\f}\n *\n * where \\f$N\\f$ is the basis degree (number of grid points per element per\n * dimension minus one), \\f$\\alpha_{\\mathrm{ef}}\\f$ determines how much the\n * coefficients are rescaled, and \\f$\\beta_{\\mathrm{ef}}\\f$ (given by the\n * `HalfPower` option) determines how aggressive/broad the filter is (lower\n * values means filtering more coefficients). Setting\n * \\f$\\alpha_{\\mathrm{ef}}=36\\f$ results in effectively zeroing the highest\n * coefficient (in practice it gets rescaled by machine epsilon). The same\n * \\f$\\alpha_{\\mathrm{ef}}\\f$ and \\f$\\beta_{\\mathrm{ef}}\\f$ are used in each\n * logical direction. For a discussion of filtering see section 5.3 of\n * \\cite HesthavenWarburton.\n *\n * #### Design decision:\n *\n * - The reason for the `size_t` template parameter is to allow for different\n * `Alpha` and `HalfPower` parameters for different tensors while still being\n * able to cache the matrices.   If different `Alpha` or `HalfPower` parameters\n * are desired for filtering different tensors, then multiple filters must be\n * inserted into the GlobalCache with different `FilterIndex` values. In\n * the input file these will be specified as `ExpFilterFILTER_INDEX`, e.g.\n * \\snippet LinearOperators/Test_Filtering.cpp multiple_exponential_filters\n */\ntemplate <size_t FilterIndex>\nclass Exponential {\n public:\n  /// \\brief The value of `exp(-alpha)` is what the highest modal coefficient is\n  /// rescaled by.\n  struct Alpha {\n    using type = double;\n    static constexpr Options::String help =\n        \"exp(-alpha) is rescaling of highest coefficient\";\n    static type lower_bound() { return 0.0; }\n  };\n\n  /*!\n   * \\brief Half of the exponent in the exponential.\n   *\n   * \\f{align*}{\n   *  c_i\\to c_i \\exp\\left[-\\alpha \\left(\\frac{i}{N}\\right)^{2m}\\right]\n   * \\f}\n   */\n  struct HalfPower {\n    using type = unsigned;\n    static constexpr Options::String help =\n        \"Half of the exponent in the generalized Gaussian\";\n    static type lower_bound() { return 1; }\n  };\n\n  /// \\brief Turn the filter off\n  struct Enable {\n    using type = bool;\n    static constexpr Options::String help = {\"Enable the filter\"};\n  };\n\n  struct BlocksToFilter {\n    using type =\n        Options::Auto<std::vector<std::string>, Options::AutoLabel::All>;\n    static constexpr Options::String help = {\n        \"List of blocks or block groups to apply filtering to. All other \"\n        \"blocks will have no filtering. You can also specify 'All' to do \"\n        \"filtering in all blocks of the domain.\"};\n  };\n\n  using options = tmpl::list<Alpha, HalfPower, Enable, BlocksToFilter>;\n  static constexpr Options::String help = {\"An exponential filter.\"};\n  static std::string name() {\n    return \"ExpFilter\" + std::to_string(FilterIndex);\n  }\n\n  Exponential() = default;\n\n  Exponential(double alpha, unsigned half_power, bool enable,\n              const std::optional<std::vector<std::string>>& blocks_to_filter,\n              const Options::Context& context = {});\n\n  /// A cached matrix used to apply the filter to the given mesh\n  const Matrix& filter_matrix(const Mesh<1>& mesh) const;\n\n  bool enable() const { return enable_; }\n\n  const std::optional<std::unordered_set<std::string>>& blocks_to_filter()\n      const {\n    return blocks_to_filter_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <size_t LocalFilterIndex>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const Exponential<LocalFilterIndex>& lhs,\n                         const Exponential<LocalFilterIndex>& rhs);\n\n  double alpha_{36.0};\n  unsigned half_power_{16};\n  bool enable_{true};\n  std::optional<std::unordered_set<std::string>> blocks_to_filter_{};\n};\n\ntemplate <size_t LocalFilterIndex>\nbool operator==(const Exponential<LocalFilterIndex>& lhs,\n                const Exponential<LocalFilterIndex>& rhs);\n\ntemplate <size_t FilterIndex>\nbool operator!=(const Exponential<FilterIndex>& lhs,\n                const Exponential<FilterIndex>& rhs);\n}  // namespace Filters\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/IndefiniteIntegral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/IndefiniteIntegral.hpp\"\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/StripeIterator.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"NumericalAlgorithms/Spectral/IntegrationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nconst Matrix empty_matrix = Matrix{};\n\ntemplate <size_t Dim, size_t... Indices>\nauto make_integration_matrices(const Mesh<Dim>& mesh,\n                               const size_t dim_to_integrate,\n                               std::index_sequence<Indices...> /*meta*/) {\n  return std::array<std::reference_wrapper<const Matrix>, Dim>{\n      {(Indices == dim_to_integrate\n            ? Spectral::integration_matrix(mesh.slice_through(dim_to_integrate))\n            : empty_matrix)...}};\n}\n}  // namespace\n\ntemplate <size_t Dim, typename VectorType>\nvoid indefinite_integral(const gsl::not_null<VectorType*> integral,\n                         const VectorType& integrand, const Mesh<Dim>& mesh,\n                         const size_t dim_to_integrate) {\n  if (mesh.basis(dim_to_integrate) == Spectral::Basis::Cartoon) {\n    ERROR(\"An indefinite integral cannot be preformed on a Cartoon basis. \"\n          \"dim_to_integrate: \"\n          << dim_to_integrate << \", basis: \" << mesh.basis());\n  }\n  integral->destructive_resize(integrand.size());\n  apply_matrices(integral,\n                 make_integration_matrices<Dim>(\n                     mesh, dim_to_integrate, std::make_index_sequence<Dim>{}),\n                 integrand, mesh.extents());\n}\n\ntemplate <size_t Dim, typename VectorType>\nVectorType indefinite_integral(const VectorType& integrand,\n                               const Mesh<Dim>& mesh,\n                               const size_t dim_to_integrate) {\n  VectorType integral{integrand.size()};\n  indefinite_integral(make_not_null(&integral), integrand, mesh,\n                      dim_to_integrate);\n  return integral;\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_VECTORTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(r, data)                                                \\\n  template GET_VECTORTYPE(data) indefinite_integral(                          \\\n      const GET_VECTORTYPE(data)&, const Mesh<GET_DIM(data)>&, const size_t); \\\n  template void indefinite_integral(                                          \\\n      const gsl::not_null<GET_VECTORTYPE(data)*> integral,                    \\\n      const GET_VECTORTYPE(data)&, const Mesh<GET_DIM(data)>&, const size_t);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3),\n                        (DataVector, ComplexDataVector))\n\n#undef GET_DIM\n#undef GET_VECTORTYPE\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/IndefiniteIntegral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\ntemplate <size_t>\nclass Mesh;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Compute the indefinite integral of a function in the\n * `dim_to_integrate`, applying a zero boundary condition on each stripe.\n *\n * Integrates with respect to one of the logical coordinates\n * \\f$\\boldsymbol{\\xi} = (\\xi, \\eta, \\zeta)\\f$.\n *\n * The integral w.r.t. a different set of coordinates\n * \\f$\\boldsymbol{x} = \\boldsymbol{x}(\\boldsymbol{\\xi})\\f$ can be computed\n * by pre-multiplying `integrand` by the Jacobian determinant\n * \\f$J = \\det d\\boldsymbol{x}/d\\boldsymbol{\\xi}\\f$ of the mapping\n * \\f$\\boldsymbol{x}(\\boldsymbol{\\xi})\\f$. The integration is still performed\n * along one logical-coordinate direction, indicated by `dim_to_integrate`.\n *\n * \\requires number of points in `integrand` and `mesh` are equal.\n */\ntemplate <size_t Dim, typename VectorType>\nvoid indefinite_integral(gsl::not_null<VectorType*> integral,\n                         const VectorType& integrand, const Mesh<Dim>& mesh,\n                         size_t dim_to_integrate);\n\ntemplate <size_t Dim, typename VectorType>\nVectorType indefinite_integral(const VectorType& integrand,\n                               const Mesh<Dim>& mesh, size_t dim_to_integrate);\n/// @}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Linearize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/Linearize.hpp\"\n\n#include <functional>\n#include <ostream>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/LinearFilteringMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\ntemplate <size_t Dim>\nvoid linearize(const gsl::not_null<DataVector*> result, const DataVector& u,\n               const Mesh<Dim>& mesh) {\n  ASSERT(result->size() == u.size(),\n         \"The size of result passed to linearize must be equal to the size of \"\n         \"u. result.size(): \"\n             << result->size() << \" u.size(): \" << u.size());\n  const Matrix empty{};\n  auto filter = make_array<Dim>(std::cref(empty));\n  for (size_t d = 0; d < Dim; d++) {\n    gsl::at(filter, d) =\n        std::cref(Spectral::linear_filter_matrix(mesh.slice_through(d)));\n  }\n  apply_matrices(result, filter, u, mesh.extents());\n}\n\ntemplate <size_t Dim>\nDataVector linearize(const DataVector& u, const Mesh<Dim>& mesh) {\n  DataVector result(mesh.number_of_grid_points());\n  linearize(&result, u, mesh);\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid linearize(const gsl::not_null<DataVector*> result, const DataVector& u,\n               const Mesh<Dim>& mesh, const size_t d) {\n  ASSERT(result->size() == u.size(),\n         \"The size of result passed to linearize must be equal to the size of \"\n         \"u. result.size(): \"\n             << result->size() << \" u.size(): \" << u.size());\n  const Matrix empty{};\n  auto filter = make_array<Dim>(std::cref(empty));\n  gsl::at(filter, d) =\n      std::cref(Spectral::linear_filter_matrix(mesh.slice_through(d)));\n  apply_matrices(result, filter, u, mesh.extents());\n}\n\ntemplate <size_t Dim>\nDataVector linearize(const DataVector& u, const Mesh<Dim>& mesh,\n                     const size_t d) {\n  DataVector result(mesh.number_of_grid_points());\n  linearize(&result, u, mesh, d);\n  return result;\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                              \\\n  template void linearize<GET_DIM(data)>(const gsl::not_null<DataVector*>,  \\\n                                         const DataVector&,                 \\\n                                         const Mesh<GET_DIM(data)>&);       \\\n  template DataVector linearize<GET_DIM(data)>(const DataVector&,           \\\n                                               const Mesh<GET_DIM(data)>&); \\\n  template void linearize<GET_DIM(data)>(                                   \\\n      const gsl::not_null<DataVector*>, const DataVector&,                  \\\n      const Mesh<GET_DIM(data)>&, const size_t);                            \\\n  template DataVector linearize<GET_DIM(data)>(                             \\\n      const DataVector&, const Mesh<GET_DIM(data)>&, const size_t);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Linearize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function linearize.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\nnamespace gsl {\ntemplate <class>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Truncate u to a linear function in each dimension.\n *\n * Ex in 2D: \\f$u^{Lin} = U_0 + U_x x + U_y y + U_{xy} xy\\f$\n *\n * \\warning the `gsl::not_null` variant assumes `*result` is of the correct\n * size.\n */\ntemplate <size_t Dim>\nvoid linearize(gsl::not_null<DataVector*> result, const DataVector& u,\n               const Mesh<Dim>& mesh);\ntemplate <size_t Dim>\nDataVector linearize(const DataVector& u, const Mesh<Dim>& mesh);\n/// @}\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Truncate u to a linear function in the given dimension.\n *\n * **Parameters**\n * - `u` the function to linearize.\n * - `mesh` the Mesh of the grid on the manifold on which `u` is\n * located.\n * - `d` the dimension that is to be linearized.\n *\n * \\warning the `gsl::not_null` variant assumes `*result` is of the correct\n * size.\n */\ntemplate <size_t Dim>\nvoid linearize(gsl::not_null<DataVector*> result, const DataVector& u,\n               const Mesh<Dim>& mesh, size_t d);\ntemplate <size_t Dim>\nDataVector linearize(const DataVector& u, const Mesh<Dim>& mesh, size_t d);\n/// @}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/MeanValue.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n\n#include <ostream>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\ntemplate <>\ndouble mean_value_on_boundary(const DataVector& f, const Mesh<1>& mesh,\n                              size_t d, Side side) {\n  ASSERT(d == 0, \"d = \" << d);\n  return Side::Lower == side ? f[0] : f[mesh.extents(0) - 1];\n}\n\ndouble mean_value_on_boundary(\n    const gsl::not_null<DataVector*> /*boundary_buffer*/, const DataVector& f,\n    const Mesh<1>& mesh, size_t d, Side side) {\n  return mean_value_on_boundary(f, mesh, d, side);\n}\n\ndouble mean_value_on_boundary(\n    const gsl::not_null<DataVector*> /*boundary_buffer*/,\n    const gsl::span<std::pair<size_t, size_t>> /*volume_and_slice_indices*/,\n    const DataVector& f, const Mesh<1>& mesh, const size_t d, const Side side) {\n  return mean_value_on_boundary(f, mesh, d, side);\n}\n\ntemplate <size_t Dim>\ndouble mean_value_on_boundary(const DataVector& f, const Mesh<Dim>& mesh,\n                              size_t d, Side side) {\n  const Mesh<Dim - 1> mesh_on_boundary = mesh.slice_away(d);\n  DataVector f_on_boundary(mesh_on_boundary.number_of_grid_points());\n  return mean_value_on_boundary(&f_on_boundary, f, mesh, d, side);\n}\n\ntemplate <size_t Dim>\ndouble mean_value_on_boundary(const gsl::not_null<DataVector*> boundary_buffer,\n                              const DataVector& f, const Mesh<Dim>& mesh,\n                              size_t d, Side side) {\n  ASSERT(d < Dim, \"d = \" << d << \", Dim = \" << Dim);\n  const size_t N = mesh.extents(d);\n  const Mesh<Dim - 1> mesh_on_boundary = mesh.slice_away(d);\n  for (SliceIterator si(mesh.extents(), d, (Side::Lower == side ? 0 : N - 1));\n       si; ++si) {\n    (*boundary_buffer)[si.slice_offset()] = f[si.volume_offset()];\n  }\n  return definite_integral(*boundary_buffer, mesh_on_boundary) /\n         two_to_the(Dim - 1);\n}\n\ntemplate <size_t Dim>\ndouble mean_value_on_boundary(\n    const gsl::not_null<DataVector*> boundary_buffer,\n    const gsl::span<std::pair<size_t, size_t>> volume_and_slice_indices,\n    const DataVector& f, const Mesh<Dim>& mesh, const size_t d,\n    const Side /*side*/) {\n  ASSERT(d < Dim, \"d = \" << d << \", Dim = \" << Dim);\n  const Mesh<Dim - 1> mesh_on_boundary = mesh.slice_away(d);\n  for (const auto& volume_and_slice_index : volume_and_slice_indices) {\n    (*boundary_buffer)[volume_and_slice_index.second] =\n        f[volume_and_slice_index.first];\n  }\n  return definite_integral(*boundary_buffer, mesh_on_boundary) /\n         two_to_the(Dim - 1);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template double mean_value_on_boundary(                            \\\n      const gsl::not_null<DataVector*>, const DataVector&,           \\\n      const Mesh<DIM(data)>&, size_t, Side);                         \\\n  template double mean_value_on_boundary(                            \\\n      const DataVector&, const Mesh<DIM(data)>&, size_t, Side);      \\\n  template double mean_value_on_boundary(                            \\\n      const gsl::not_null<DataVector*>,                              \\\n      const gsl::span<std::pair<size_t, size_t>>, const DataVector&, \\\n      const Mesh<DIM(data)>&, const size_t, const Side);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/MeanValue.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions mean_value and mean_value_on_boundary.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Compute the mean value of a function over a manifold.\n *\n * Given a function \\f$f\\f$, compute its mean value \\f$\\bar{f}\\f$ with respect\n * to the logical coordinates \\f$\\boldsymbol{\\xi} = (\\xi, \\eta, \\zeta)\\f$. E.g.,\n * in 1 dimension, \\f$\\bar{f} = \\int_{-1}^1 f d\\xi \\Big/ \\int_{-1}^1 d\\xi\\f$.\n *\n * \\note\n * The mean w.r.t. a different set of coordinates\n * \\f$\\boldsymbol{x} = \\boldsymbol{x}(\\boldsymbol{\\xi})\\f$ can't be directly\n * computed using this function. Before calling `mean_value`, \\f$f\\f$ must be\n * pre-multiplied by the Jacobian determinant\n * \\f$J = \\det d\\boldsymbol{x}/d\\boldsymbol{\\xi}\\f$ of the mapping\n * \\f$\\boldsymbol{x}(\\boldsymbol{\\xi})\\f$. Additionally, the output of\n * `mean_value` must be multiplied by a factor\n * \\f$2^{\\text{d}} / \\int J d^{\\text{d}}\\xi\\f$ (in \\f$d\\f$ dimensions), to\n * account for the different volume of the manifold in the \\f$\\boldsymbol{x}\\f$\n * coordinates.\n *\n * \\param f the function to average.\n * \\param mesh the Mesh defining the grid points on the manifold.\n */\ntemplate <size_t Dim>\ndouble mean_value(const DataVector& f, const Mesh<Dim>& mesh) {\n  return definite_integral(f, mesh) / two_to_the(Dim);\n}\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Compute the mean value of a function over a boundary of a manifold.\n *\n * Given a function \\f$f\\f$, compute its mean value \\f$\\bar{f}\\f$, over a\n * boundary, with respect to the logical coordinates\n * \\f$\\boldsymbol{\\xi} = (\\xi, \\eta, \\zeta)\\f$.\n *\n * \\see `mean_value` for notes about means w.r.t. other coordinates.\n *\n * - `f` the function to average.\n * - `mesh` the Mesh defining the grid points on the manifold.\n * - `d` the dimension which is sliced away to get the boundary.\n * - `side` whether it is the lower or upper boundary in the d-th dimension.\n * - `boundary_buffer` is a pointer to a DataVector of size\n *   `mesh.slice_away(d).number_of_grid_points()` used as a temporary buffer\n *   when slicing the data to the boundary.\n * - `volume_and_slice_indices` a pair of `(volume_index_for_point,\n *   slice_index_for_point)` computed using the `SliceIterator`. Because\n *   `SliceIterator` is somewhat expensive, if computing the mean value on the\n *   same boundary for many different tensor components, prefer computing the\n *   slice indices once.\n */\ntemplate <size_t Dim>\ndouble mean_value_on_boundary(const DataVector& f, const Mesh<Dim>& mesh,\n                              size_t d, Side side);\n\ntemplate <size_t Dim>\ndouble mean_value_on_boundary(gsl::not_null<DataVector*> boundary_buffer,\n                              const DataVector& f, const Mesh<Dim>& mesh,\n                              size_t d, Side side);\n\ndouble mean_value_on_boundary(gsl::not_null<DataVector*> /*boundary_buffer*/,\n                              const DataVector& f, const Mesh<1>& mesh,\n                              size_t d, Side side);\n\ntemplate <size_t Dim>\ndouble mean_value_on_boundary(\n    gsl::not_null<DataVector*> boundary_buffer,\n    gsl::span<std::pair<size_t, size_t>> volume_and_slice_indices,\n    const DataVector& f, const Mesh<Dim>& mesh, size_t d, Side /*side*/);\n\ndouble mean_value_on_boundary(\n    gsl::not_null<DataVector*> /*boundary_buffer*/,\n    gsl::span<std::pair<size_t, size_t>> /*volume_and_slice_indices*/,\n    const DataVector& f, const Mesh<1>& mesh, size_t d, Side side);\n/// @}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/PartialDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n\n#include <array>\n#include <blaze/math/DynamicMatrix.h>\n#include <cstddef>\n#include <functional>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace partial_derivatives_detail {\nvoid apply_matrix_in_first_dim(double* result, const double* const input,\n                               const Matrix& matrix, const size_t size,\n                               const bool add_to_result) {\n  dgemm_<true>(\n      'N', 'N',\n      matrix.rows(),              // rows of matrix and result\n      size / matrix.columns(),    // columns of result and input\n      matrix.columns(),           // columns of matrix and rows of input\n      1.0,                        // overall multiplier\n      matrix.data(),              // matrix\n      matrix.spacing(),           // rows of matrix including padding\n      input,                      // input\n      matrix.columns(),           // rows of input\n      add_to_result ? 1.0 : 0.0,  // overwrite output with result or add to it\n      result,                     // result\n      matrix.rows());             // rows of result\n}\nvoid apply_matrix_in_first_dim(std::complex<double>* result,\n                               const std::complex<double>* const input,\n                               const Matrix& matrix, const size_t size,\n                               const bool add_to_result) {\n  // BLAS zgemm operates on complex matrices, so we need to copy the real matrix\n  // to a complex matrix with zero imaginary part before calling zgemm.\n  // Possible performance optimization: avoid the copy here by storing the\n  // complex matrix in a static cache. We probably only want to add this to\n  // Spectral.hpp once profiling shows that it becomes necessary.\n  const blaze::DynamicMatrix<std::complex<double>, blaze::columnMajor>\n      matrix_complex{matrix};\n  zgemm_<true>('N', 'N',\n               matrix.rows(),            // rows of matrix and result\n               size / matrix.columns(),  // columns of result and input\n               matrix.columns(),         // columns of matrix and rows of input\n               std::complex{1.0, 0.0},   // overall multiplier\n               matrix_complex.data(),    // matrix\n               matrix.spacing(),         // rows of matrix including padding\n               input,                    // input\n               matrix.columns(),         // rows of input\n               std::complex{add_to_result ? 1.0 : 0.0,\n                            0.0},  // overwrite output with result or add to it\n               result,             // result\n               matrix.rows());     // rows of result\n  // This implementation is ~1.35x slower than the implementation above (based\n  // on the \"Partial derivatives complex\" benchmark in\n  // Test_PartialDerivatives.cpp).\n  //   DataVector buffer(size * 2);\n  //   raw_transpose(make_not_null(reinterpret_cast<double*>(result)),\n  //                 reinterpret_cast<const double*>(input), 2, size);\n  //   apply_matrix_in_first_dim(buffer.data(),\n  //                             reinterpret_cast<const double*>(result),\n  //                             matrix, size * 2, add_to_result);\n  //   raw_transpose(make_not_null(reinterpret_cast<double*>(result)),\n  //                 buffer.data(), size, 2);\n}\n}  // namespace partial_derivatives_detail\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim>\nvoid logical_partial_derivative(\n    const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n        Frame::ElementLogical>*>\n        logical_derivative_of_u,\n    const gsl::not_null<gsl::span<typename DataType::value_type>*> buffer,\n    const Tensor<DataType, SymmList, IndexList>& u, const Mesh<Dim>& mesh) {\n  static_assert(\n      Dim > 0 and Dim < 4,\n      \"logical_partial_derivative is only implemented for 1, 2, and 3d\");\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  ASSERT(buffer->size() >= num_grid_points,\n         \"The buffer in logical_partial_derivative must be at least of size \"\n             << num_grid_points << \" but is of size \" << buffer->size());\n\n  set_number_of_grid_points(logical_derivative_of_u,\n                            mesh.number_of_grid_points());\n  if (Dim == 3 and mesh.basis(1) == Spectral::Basis::SphericalHarmonic) {\n    if constexpr (std::is_same_v<typename DataType::value_type, double>) {\n      const Matrix& differentiation_matrix_xi =\n          Spectral::differentiation_matrix(mesh.slice_through(0));\n      const auto& ylm = ylm::get_spherepack_cache(mesh.extents(1) - 1);\n      for (size_t storage_index = 0; storage_index < u.size();\n           ++storage_index) {\n        const auto u_tensor_index = u.get_tensor_index(storage_index);\n        partial_derivatives_detail::apply_matrix_in_first_dim(\n            // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n            logical_derivative_of_u->get(prepend(u_tensor_index, 0_st)).data(),\n            u[storage_index].data(), differentiation_matrix_xi,\n            num_grid_points);\n        const auto du = std::array{\n            // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n            logical_derivative_of_u->get(prepend(u_tensor_index, 1_st)).data(),\n            // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n            logical_derivative_of_u->get(prepend(u_tensor_index, 2_st)).data()};\n        ylm.gradient_all_offsets(du, make_not_null(u[storage_index].data()),\n                                 mesh.extents(0));\n      }\n    } else {\n      ERROR(\n          \"Support for complex numbers with spherical harmonics is not yet \"\n          \"implemented for logical_partial_derivative.\");\n    }\n  } else if (Dim == 2 and mesh.basis(0) == Spectral::Basis::ZernikeB2) {\n    if constexpr (std::is_same_v<typename DataType::value_type, double>) {\n      ASSERT(mesh.basis(0) == Spectral::Basis::ZernikeB2 and\n                 mesh.basis(1) == Spectral::Basis::ZernikeB2,\n             \"Unexpected basis combination: \" << mesh.basis());\n      const size_t n_r = mesh.extents(0);\n      const size_t n_phi = mesh.extents(1);\n      const size_t n_r_max = 2 * n_r - 2;\n      ASSERT(\n          n_phi % 2 == 1,\n          \"Fourier with an even number of grid points can be unstable due to \"\n          \"the top derivative not being representable\");\n      ASSERT(n_phi / 2 <= n_r_max,\n             \"Zernike & Fourier on a disk have angular resolution limited by \"\n             \"extents in both dimensions. We choose to enforce the restriction \"\n             \"that the Fourier modal space is not larger than the Zernike \"\n             \"angular capabilities (which would waste space and be physically \"\n             \"ill-motivated).\\nn_phi / 2 = \"\n                 << n_phi / 2 << \", Maximum from Zernike = \" << n_r_max);\n      const size_t M = n_phi / 2;\n\n      const Matrix& diff_matrix_r_even = Spectral::differentiation_matrix<\n          Spectral::Basis::ZernikeB2, Spectral::Quadrature::GaussRadauUpper>(\n          n_r, Spectral::Parity::Even);\n      const Matrix& diff_matrix_r_odd = Spectral::differentiation_matrix<\n          Spectral::Basis::ZernikeB2, Spectral::Quadrature::GaussRadauUpper>(\n          n_r, Spectral::Parity::Odd);\n      const Matrix& diff_matrix_phi = Spectral::differentiation_matrix<\n          Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n\n      const Matrix& nodal_to_modal_phi = Spectral::nodal_to_modal_matrix<\n          Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n      const Matrix& modal_to_nodal_phi = Spectral::modal_to_nodal_matrix<\n          Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n\n      for (size_t storage_index = 0; storage_index < u.size();\n           ++storage_index) {\n        const auto u_tensor_index = u.get_tensor_index(storage_index);\n        const auto r_deriv_tensor_index = prepend(u_tensor_index, 0_st);\n        const auto phi_deriv_tensor_index = prepend(u_tensor_index, 1_st);\n\n        // Do phi derivative\n        dgemm_<true>(\n            'N',\n            'T',  // transpose differentiation matrix to act on rows of data\n            n_r, n_phi, n_phi, 1.0, u[storage_index].data(), n_r,\n            diff_matrix_phi.data(), diff_matrix_phi.spacing(), 0.0,\n            // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n            logical_derivative_of_u->get(phi_deriv_tensor_index).data(), n_r);\n\n        // Get u in terms of r and m index\n        dgemm_<true>('N', 'T', n_r, n_phi, n_phi, 1.0, u[storage_index].data(),\n                     n_r, nodal_to_modal_phi.data(),\n                     nodal_to_modal_phi.spacing(), 0.0,\n                     // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n                     logical_derivative_of_u->get(r_deriv_tensor_index).data(),\n                     n_r);  // C and ldc\n\n        // Do radial derivative\n        // m = 0\n        dgemv_('N', n_r, n_r, 1.0, diff_matrix_r_even.data(),\n               diff_matrix_r_even.spacing(),\n               // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n               logical_derivative_of_u->get(r_deriv_tensor_index).data(), 1,\n               0.0,  // beta = 0.0\n               (*buffer).data(), 1);\n        // j is index into Fourier modal vector\n        // {u_0, u_1, u_{-1}, u_2, u_{-2}, ... , u_M, u_{-M}}\n        size_t j = 1;\n        for (size_t m = 1; m <= M; ++m) {\n          const Matrix& diff_r =\n              m % 2 == 0 ? diff_matrix_r_even : diff_matrix_r_odd;\n          dgemm_<true>(\n              'N', 'N', n_r, 2, n_r, 1.0, diff_r.data(), diff_r.spacing(),\n              // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n              logical_derivative_of_u->get(r_deriv_tensor_index).data() +\n                  j * n_r,\n              n_r,\n              0.0,  // beta = 0.0\n              // NOLINTNEXTLINE\n              (*buffer).data() + j * n_r, n_r);\n          j += 2;\n        }\n        // Transform from m back to phi\n        dgemm_<true>(\n            'N', 'T', n_r, n_phi, n_phi, 1.0, (*buffer).data(), n_r,\n            modal_to_nodal_phi.data(), modal_to_nodal_phi.spacing(), 0.0,\n            // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n            logical_derivative_of_u->get(r_deriv_tensor_index).data(), n_r);\n      }\n    } else {\n      ERROR(\n          \"Support for complex numbers with disk is not yet implemented for \"\n          \"logical_partial_derivative.\");\n    }\n  } else if (Dim == 3 and mesh.basis(0) == Spectral::Basis::ZernikeB2) {\n    if constexpr (std::is_same_v<typename DataType::value_type, double>) {\n      ASSERT(mesh.basis(0) == Spectral::Basis::ZernikeB2 and\n                 mesh.basis(1) == Spectral::Basis::ZernikeB2,\n             \"Unexpected basis combination: \" << mesh.basis());\n      const size_t n_r = mesh.extents(0);\n      const size_t n_phi = mesh.extents(1);\n      const size_t n_z = mesh.extents(2);\n      const size_t n_r_max = 2 * n_r - 2;\n      ASSERT(\n          n_phi % 2 == 1,\n          \"Fourier with an even number of grid points can be unstable due to \"\n          \"the top derivative not being representable\");\n      ASSERT(n_phi / 2 <= n_r_max,\n             \"Zernike & Fourier on a disk have angular resolution limited by \"\n             \"extents in both dimensions. We choose to enforce the restriction \"\n             \"that the Fourier modal space is not larger than the Zernike \"\n             \"angular capabilities (which would waste space and be physically \"\n             \"ill-motivated).\\nn_phi / 2 = \"\n                 << n_phi / 2 << \", Maximum from Zernike = \" << n_r_max);\n\n      const Matrix& diff_matrix_r_even = Spectral::differentiation_matrix<\n          Spectral::Basis::ZernikeB2, Spectral::Quadrature::GaussRadauUpper>(\n          n_r, Spectral::Parity::Even);\n      const Matrix& diff_matrix_r_odd = Spectral::differentiation_matrix<\n          Spectral::Basis::ZernikeB2, Spectral::Quadrature::GaussRadauUpper>(\n          n_r, Spectral::Parity::Odd);\n      const Matrix& diff_matrix_phi = Spectral::differentiation_matrix<\n          Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n      const Matrix& diff_matrix_z =\n          Spectral::differentiation_matrix(mesh.slice_through(2));\n\n      const Matrix& nodal_to_modal_phi = Spectral::nodal_to_modal_matrix<\n          Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n      const Matrix& modal_to_nodal_phi = Spectral::modal_to_nodal_matrix<\n          Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n\n      for (size_t storage_index = 0; storage_index < u.size();\n           ++storage_index) {\n        const DataType& u_component = u[storage_index];\n        const auto u_tensor_index = u.get_tensor_index(storage_index);\n        const auto r_deriv_tensor_index = prepend(u_tensor_index, 0_st);\n        DataType& r_deriv_component =\n            logical_derivative_of_u->get(r_deriv_tensor_index);\n        const auto phi_deriv_tensor_index = prepend(u_tensor_index, 1_st);\n        DataType& phi_deriv_component =\n            logical_derivative_of_u->get(phi_deriv_tensor_index);\n        const auto z_deriv_tensor_index = prepend(u_tensor_index, 2_st);\n        DataType& z_deriv_component =\n            logical_derivative_of_u->get(z_deriv_tensor_index);\n\n        // phi derivative\n        raw_transpose(make_not_null(z_deriv_component.data()),\n                      u_component.data(), n_r, n_phi * n_z);\n        partial_derivatives_detail::apply_matrix_in_first_dim(\n            buffer->data(), z_deriv_component.data(), diff_matrix_phi,\n            num_grid_points);\n        raw_transpose(make_not_null(phi_deriv_component.data()), buffer->data(),\n                      n_phi * n_z, n_r);\n\n        // See comments in disk_apply() in the .tpp for considerations about\n        // the following code. For this function, we don't have enough buffer\n        // space to cleanly use the approach in the .tpp, so we use the\n        // masking approach instead. Note that timing shows this to only be a\n        // few percent slower\n\n        // reuse transposed data to go to angular spectral space\n        partial_derivatives_detail::apply_matrix_in_first_dim(\n            r_deriv_component.data(), z_deriv_component.data(),\n            nodal_to_modal_phi, num_grid_points);\n        // r_deriv_component holds transposed angular modal rep\n\n        raw_transpose(make_not_null(buffer->data()), r_deriv_component.data(),\n                      n_phi * n_z, n_r);\n        std::copy(buffer->data(), buffer->data() + num_grid_points,\n                  z_deriv_component.data());\n        // buffer and z_deriv_component hold angular modal rep\n\n        // buffer stores even components, z_deriv_component stores odd\n        for (size_t k = 0; k < n_z; ++k) {\n          size_t offset = k * n_phi * n_r;\n          for (size_t i = 0; i < n_phi; ++i) {\n            if (i == 0) {\n              // This is an even column -> zero odd vals\n              std::fill(z_deriv_component.data() + offset,\n                        z_deriv_component.data() + offset + n_r, 0.0);\n            } else {\n              if ((i - 1) / 2 % 2 == 1) {\n                // This is an even column with even next to it -> zero odd vals\n                std::fill(z_deriv_component.data() + offset,\n                          z_deriv_component.data() + offset + 2 * n_r, 0.0);\n              } else {\n                // This is an even column with odd next to it -> zero even vals\n                std::fill(buffer->data() + offset,\n                          buffer->data() + offset + 2 * n_r, 0.0);\n              }\n              ++i;\n              offset += n_r;\n            }\n            offset += n_r;\n          }\n        }\n        partial_derivatives_detail::apply_matrix_in_first_dim(\n            r_deriv_component.data(), buffer->data(), diff_matrix_r_even,\n            num_grid_points, false);\n        partial_derivatives_detail::apply_matrix_in_first_dim(\n            r_deriv_component.data(), z_deriv_component.data(),\n            diff_matrix_r_odd, num_grid_points, true);\n        // r_deriv_component holds the radial derivative in angular modal rep\n\n        raw_transpose(make_not_null(buffer->data()), r_deriv_component.data(),\n                      n_r, n_phi * n_z);\n        // buffer holds transposed radial derivative in angular modal rep\n\n        partial_derivatives_detail::apply_matrix_in_first_dim(\n            z_deriv_component.data(), buffer->data(), modal_to_nodal_phi,\n            num_grid_points);\n        // z_deriv_component holds transposed radial derivative in angular nodal\n        // rep\n\n        raw_transpose(make_not_null(r_deriv_component.data()),\n                      z_deriv_component.data(), n_phi * n_z, n_r);\n\n        // z derivative\n        raw_transpose(make_not_null(z_deriv_component.data()),\n                      u_component.data(), n_r * n_phi, n_z);\n        partial_derivatives_detail::apply_matrix_in_first_dim(\n            buffer->data(), z_deriv_component.data(), diff_matrix_z,\n            num_grid_points);\n        raw_transpose(make_not_null(z_deriv_component.data()), buffer->data(),\n                      n_z, n_r * n_phi);\n      }\n    } else {\n      ERROR(\n          \"Support for complex numbers with cylinder is not yet implemented \"\n          \"for logical_partial_derivative.\");\n    }\n  } else {\n    const Matrix empty_matrix{};\n    std::array<std::reference_wrapper<const Matrix>, Dim> diff_matrices{\n        make_array<Dim, std::reference_wrapper<const Matrix>>(empty_matrix)};\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(diff_matrices, d) =\n          std::cref(Spectral::differentiation_matrix(mesh.slice_through(d)));\n    }\n\n    // It would be possible to check if the memory is contiguous and then\n    // differentiate all components at once. Note that the buffer in that case\n    // would also need to be the size of all components.\n    for (size_t storage_index = 0; storage_index < u.size(); ++storage_index) {\n      const auto u_tensor_index = u.get_tensor_index(storage_index);\n      const auto xi_deriv_tensor_index = prepend(u_tensor_index, 0_st);\n      partial_derivatives_detail::apply_matrix_in_first_dim(\n          // NOLINTNEXTLINE(readability-redundant-smartptr-get)\n          logical_derivative_of_u->get(xi_deriv_tensor_index).data(),\n          u[storage_index].data(), diff_matrices[0].get(), num_grid_points);\n      for (size_t i = 1; i < Dim; ++i) {\n        const auto deriv_tensor_index = prepend(u_tensor_index, i);\n        DataType& deriv_component =\n            logical_derivative_of_u->get(deriv_tensor_index);\n        size_t chunk_size =\n            diff_matrices[0].get().rows() *\n            (i == 1 ? 1 : gsl::at(diff_matrices, 1).get().rows());\n        raw_transpose(make_not_null(deriv_component.data()),\n                      u[storage_index].data(), chunk_size,\n                      num_grid_points / chunk_size);\n        partial_derivatives_detail::apply_matrix_in_first_dim(\n            buffer->data(), deriv_component.data(),\n            gsl::at(diff_matrices, i).get(), num_grid_points);\n        chunk_size =\n            i == 1 ? (Dim == 2 ? gsl::at(diff_matrices, 1).get().rows()\n                               : gsl::at(diff_matrices, 1).get().rows() *\n                                     gsl::at(diff_matrices, 2).get().rows())\n                   : gsl::at(diff_matrices, 2).get().rows();\n        raw_transpose(make_not_null(deriv_component.data()), buffer->data(),\n                      chunk_size, num_grid_points / chunk_size);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim>\nvoid logical_partial_derivative(\n    gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n        Frame::ElementLogical>*>\n        logical_derivative_of_u,\n    const Tensor<DataType, SymmList, IndexList>& u, const Mesh<Dim>& mesh) {\n  using ValueType = typename DataType::value_type;  // double or complex<double>\n  std::vector<ValueType> buffer(mesh.number_of_grid_points());\n  gsl::span<ValueType> buffer_view{buffer.data(), buffer.size()};\n  logical_partial_derivative(logical_derivative_of_u,\n                             make_not_null(&buffer_view), u, mesh);\n}\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim>\nauto logical_partial_derivative(const Tensor<DataType, SymmList, IndexList>& u,\n                                const Mesh<Dim>& mesh)\n    -> TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n        Frame::ElementLogical> {\n  TensorMetafunctions::prepend_spatial_index<\n      Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n      Frame::ElementLogical>\n      result{mesh.number_of_grid_points()};\n  logical_partial_derivative(make_not_null(&result), u, mesh);\n  return result;\n}\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivative(\n    const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo, DerivativeFrame>*>\n        du,\n    const TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n        Frame::ElementLogical>& logical_partial_derivative_of_u,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  for (size_t storage_index = 0;\n       storage_index < Tensor<DataType, SymmList, IndexList>::size();\n       ++storage_index) {\n    const auto u_multi_index =\n        Tensor<DataType, SymmList,\n               IndexList>::structure::get_canonical_tensor_index(storage_index);\n    for (size_t i = 0; i < Dim; i++) {\n      const auto du_multi_index = prepend(u_multi_index, i);\n      du->get(du_multi_index) =\n          inverse_jacobian.get(0, i) *\n          logical_partial_derivative_of_u.get(prepend(u_multi_index, 0_st));\n      for (size_t j = 1; j < Dim; j++) {\n        du->get(du_multi_index) +=\n            inverse_jacobian.get(j, i) *\n            logical_partial_derivative_of_u.get(prepend(u_multi_index, j));\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivative(\n    const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo, DerivativeFrame>*>\n        du,\n    const Tensor<DataType, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  TensorMetafunctions::prepend_spatial_index<\n      Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n      Frame::ElementLogical>\n      logical_partial_derivative_of_u{mesh.number_of_grid_points()};\n  logical_partial_derivative(make_not_null(&logical_partial_derivative_of_u), u,\n                             mesh);\n  partial_derivative<DataType, SymmList, IndexList>(\n      du, logical_partial_derivative_of_u, inverse_jacobian);\n}\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nauto partial_derivative(\n    const Tensor<DataType, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian)\n    -> TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo, DerivativeFrame> {\n  TensorMetafunctions::prepend_spatial_index<\n      Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo, DerivativeFrame>\n      result{mesh.number_of_grid_points()};\n  partial_derivative(make_not_null(&result), u, mesh, inverse_jacobian);\n  return result;\n}\n\n#define GET_DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define GET_FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define GET_TENSOR(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void logical_partial_derivative(                                    \\\n      gsl::not_null<TensorMetafunctions::prepend_spatial_index<                \\\n                        GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),     \\\n                        GET_FRAME(data)>,                                      \\\n                    GET_DIM(data), UpLo::Lo, Frame::ElementLogical>* >         \\\n          logical_derivative_of_u,                                             \\\n      gsl::not_null<gsl::span<typename GET_DTYPE(data)::value_type>*> buffer,  \\\n      const GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),                 \\\n      GET_FRAME(data) > &u, const Mesh<GET_DIM(data)>& mesh);                  \\\n  template void logical_partial_derivative(                                    \\\n      gsl::not_null<TensorMetafunctions::prepend_spatial_index<                \\\n                        GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),     \\\n                        GET_FRAME(data)>,                                      \\\n                    GET_DIM(data), UpLo::Lo, Frame::ElementLogical>* >         \\\n          logical_derivative_of_u,                                             \\\n      const GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),                 \\\n      GET_FRAME(data) > &u, const Mesh<GET_DIM(data)>& mesh);                  \\\n  template TensorMetafunctions::prepend_spatial_index<                         \\\n      GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data), GET_FRAME(data)>,     \\\n      GET_DIM(data), UpLo::Lo,                                                 \\\n      Frame::ElementLogical >                                                  \\\n          logical_partial_derivative(const GET_TENSOR(data) < GET_DTYPE(data), \\\n                                     GET_DIM(data), GET_FRAME(data) > &u,      \\\n                                     const Mesh<GET_DIM(data)>& mesh);         \\\n  template void                                                                \\\n      partial_derivative<GET_DTYPE(data), GET_TENSOR(data) < GET_DTYPE(data),  \\\n                         GET_DIM(data), GET_FRAME(data)>::symmetry,            \\\n      GET_TENSOR(                                                              \\\n          data)<GET_DTYPE(data), GET_DIM(data), GET_FRAME(data)>::index_list > \\\n          (const gsl::not_null<TensorMetafunctions::prepend_spatial_index<     \\\n                                   GET_TENSOR(data) < GET_DTYPE(data),         \\\n                                   GET_DIM(data), GET_FRAME(data)>,            \\\n                               GET_DIM(data), UpLo::Lo, GET_FRAME(data)>* >    \\\n               du,                                                             \\\n           const TensorMetafunctions::prepend_spatial_index<                   \\\n               GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),              \\\n               GET_FRAME(data)>,                                               \\\n           GET_DIM(data), UpLo::Lo,                                            \\\n           Frame::ElementLogical > &logical_partial_derivative_of_u,           \\\n           const InverseJacobian<DataVector, GET_DIM(data),                    \\\n                                 Frame::ElementLogical, GET_FRAME(data)>       \\\n               & inverse_jacobian);                                            \\\n  template void partial_derivative(                                            \\\n      const gsl::not_null<TensorMetafunctions::prepend_spatial_index<          \\\n                              GET_TENSOR(data) < GET_DTYPE(data),              \\\n                              GET_DIM(data), GET_FRAME(data)>,                 \\\n                          GET_DIM(data), UpLo::Lo, GET_FRAME(data)>* > du,     \\\n      const GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),                 \\\n      GET_FRAME(data) > &u, const Mesh<GET_DIM(data)>& mesh,                   \\\n      const InverseJacobian<DataVector, GET_DIM(data), Frame::ElementLogical,  \\\n                            GET_FRAME(data)>& inverse_jacobian);               \\\n  template TensorMetafunctions::prepend_spatial_index<                         \\\n      GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data), GET_FRAME(data)>,     \\\n      GET_DIM(data), UpLo::Lo,                                                 \\\n      GET_FRAME(data) >                                                        \\\n          partial_derivative(                                                  \\\n              const GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),         \\\n              GET_FRAME(data) > &u, const Mesh<GET_DIM(data)>& mesh,           \\\n              const InverseJacobian<DataVector, GET_DIM(data),                 \\\n                                    Frame::ElementLogical, GET_FRAME(data)>&   \\\n                  inverse_jacobian);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (DataVector, ComplexDataVector),\n                        (1, 2, 3),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial),\n                        (tnsr::a, tnsr::A, tnsr::i, tnsr::I, tnsr::ab, tnsr::Ab,\n                         tnsr::aB, tnsr::AB, tnsr::ij, tnsr::iJ, tnsr::Ij,\n                         tnsr::IJ, tnsr::iA, tnsr::ia, tnsr::aa, tnsr::AA,\n                         tnsr::ii, tnsr::II, tnsr::ijj, tnsr::Ijj, tnsr::iaa))\n\n#undef INSTANTIATION\n\n// Some additional mixed-dimension instantiations\ntemplate TensorMetafunctions::prepend_spatial_index<\n    tnsr::aa<ComplexDataVector, 3, Frame::Inertial>, 2, UpLo::Lo,\n    Frame::Inertial>\npartial_derivative(const tnsr::aa<ComplexDataVector, 3, Frame::Inertial>& u,\n                   const Mesh<2>& mesh,\n                   const InverseJacobian<DataVector, 2, Frame::ElementLogical,\n                                         Frame::Inertial>& inverse_jacobian);\n\n#define INSTANTIATION(r, data)                                                 \\\n  template void logical_partial_derivative(                                    \\\n      gsl::not_null<TensorMetafunctions::prepend_spatial_index<                \\\n          Scalar<GET_DTYPE(data)>, GET_DIM(data), UpLo::Lo,                    \\\n          Frame::ElementLogical>*>                                             \\\n          logical_derivative_of_u,                                             \\\n      gsl::not_null<gsl::span<typename GET_DTYPE(data)::value_type>*> buffer,  \\\n      const Scalar<GET_DTYPE(data)>& u, const Mesh<GET_DIM(data)>& mesh);      \\\n  template void logical_partial_derivative(                                    \\\n      gsl::not_null<TensorMetafunctions::prepend_spatial_index<                \\\n          Scalar<GET_DTYPE(data)>, GET_DIM(data), UpLo::Lo,                    \\\n          Frame::ElementLogical>*>                                             \\\n          logical_derivative_of_u,                                             \\\n      const Scalar<GET_DTYPE(data)>& u, const Mesh<GET_DIM(data)>& mesh);      \\\n  template TensorMetafunctions::prepend_spatial_index<                         \\\n      Scalar<GET_DTYPE(data)>, GET_DIM(data), UpLo::Lo, Frame::ElementLogical> \\\n  logical_partial_derivative(const Scalar<GET_DTYPE(data)>& u,                 \\\n                             const Mesh<GET_DIM(data)>& mesh);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (DataVector, ComplexDataVector),\n                        (1, 2, 3))\n\n#undef INSTANTIATION\n\n#define INSTANTIATE_JACOBIANS(r, data)                                       \\\n  template TensorMetafunctions::prepend_spatial_index<                       \\\n      GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),                     \\\n      Frame::ElementLogical, GET_FRAME(data)>,                               \\\n      GET_DIM(data), UpLo::Lo,                                               \\\n      GET_FRAME(data) >                                                      \\\n          partial_derivative(                                                \\\n              const GET_TENSOR(data) < GET_DTYPE(data), GET_DIM(data),       \\\n              Frame::ElementLogical, GET_FRAME(data) > &u,                   \\\n              const Mesh<GET_DIM(data)>& mesh,                               \\\n              const InverseJacobian<DataVector, GET_DIM(data),               \\\n                                    Frame::ElementLogical, GET_FRAME(data)>& \\\n                  inverse_jacobian);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_JACOBIANS, (DataVector), (1, 2, 3),\n                        (Frame::Inertial), (InverseJacobian))\n\n#undef INSTANTIATE_JACOBIANS\n\n#define INSTANTIATE_SCALAR(r, data)                                            \\\n  template void partial_derivative<GET_DTYPE(data), Symmetry<>, index_list<>>( \\\n      gsl::not_null<tnsr::i<GET_DTYPE(data), GET_DIM(data), GET_FRAME(data)>*> \\\n          du,                                                                  \\\n      const tnsr::i<GET_DTYPE(data), GET_DIM(data), Frame::ElementLogical>&    \\\n          logical_partial_derivative_of_u,                                     \\\n      const InverseJacobian<DataVector, GET_DIM(data), Frame::ElementLogical,  \\\n                            GET_FRAME(data)>& inverse_jacobian);               \\\n  template void partial_derivative(                                            \\\n      gsl::not_null<tnsr::i<GET_DTYPE(data), GET_DIM(data), GET_FRAME(data)>*> \\\n          du,                                                                  \\\n      const Scalar<GET_DTYPE(data)>& u, const Mesh<GET_DIM(data)>& mesh,       \\\n      const InverseJacobian<DataVector, GET_DIM(data), Frame::ElementLogical,  \\\n                            GET_FRAME(data)>& inverse_jacobian);               \\\n  template tnsr::i<GET_DTYPE(data), GET_DIM(data), GET_FRAME(data)>            \\\n  partial_derivative(                                                          \\\n      const Scalar<GET_DTYPE(data)>& u, const Mesh<GET_DIM(data)>& mesh,       \\\n      const InverseJacobian<DataVector, GET_DIM(data), Frame::ElementLogical,  \\\n                            GET_FRAME(data)>& inverse_jacobian);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALAR, (DataVector, ComplexDataVector),\n                        (1, 2, 3),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef INSTANTIATE_SCALAR\n#undef GET_FRAME\n#undef GET_DIM\n#undef GET_TENSOR\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions computing partial derivatives.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nclass DataVector;\nclass Matrix;\ntemplate <size_t Dim>\nclass Mesh;\n\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim>\nstruct Mesh;\n}  // namespace Tags\n}  // namespace domain\nnamespace Tags {\ntemplate <class TagList>\nstruct Variables;\n}  // namespace Tags\n/// \\endcond\n\nnamespace partial_derivatives_detail {\nvoid apply_matrix_in_first_dim(double* result, const double* input,\n                               const Matrix& matrix, size_t size,\n                               bool add_to_result = false);\nvoid apply_matrix_in_first_dim(std::complex<double>* result,\n                               const std::complex<double>* input,\n                               const Matrix& matrix, size_t size,\n                               bool add_to_result = false);\n}  // namespace partial_derivatives_detail\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Compute the partial derivatives of each variable with respect to\n/// the element logical coordinate.\n///\n/// \\requires `DerivativeTags` to be the head of `VariableTags`\n///\n/// Returns a `Variables` with a spatial tensor index appended to the front\n/// of each tensor within `u` and each `Tag` wrapped with a `Tags::deriv`.\n///\n/// \\tparam DerivativeTags the subset of `VariableTags` for which derivatives\n/// are computed.\ntemplate <typename DerivativeTags, typename VariableTags, size_t Dim>\nvoid logical_partial_derivatives(\n    gsl::not_null<std::array<Variables<DerivativeTags>, Dim>*>\n        logical_partial_derivatives_of_u,\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh);\n\ntemplate <typename DerivativeTags, typename VariableTags, size_t Dim>\nauto logical_partial_derivatives(const Variables<VariableTags>& u,\n                                 const Mesh<Dim>& mesh)\n    -> std::array<Variables<DerivativeTags>, Dim>;\n/// @}\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Computes the logical partial derivative of a tensor, prepending the\n * spatial derivative index, e.g. for \\f$\\partial_i T_{a}{}^{b}\\f$ the C++ call\n * is `get(i, a, b)`.\n *\n * There is an overload that accepts a `buffer` of size\n * `mesh.number_of_grid_points()` or larger. When passed this function performs\n * no memory allocations, which helps improve performance.\n *\n * If you have a `Variables` with several tensors you need to differentiate you\n * should use the `logical_partial_derivatives` function that operates on\n * `Variables` since that'll be more efficient.\n */\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim>\nvoid logical_partial_derivative(\n    gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n        Frame::ElementLogical>*>\n        logical_derivative_of_u,\n    gsl::not_null<gsl::span<typename DataType::value_type>*> buffer,\n    const Tensor<DataType, SymmList, IndexList>& u, const Mesh<Dim>& mesh);\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim>\nvoid logical_partial_derivative(\n    gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n        Frame::ElementLogical>*>\n        logical_derivative_of_u,\n    const Tensor<DataType, SymmList, IndexList>& u, const Mesh<Dim>& mesh);\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim>\nauto logical_partial_derivative(const Tensor<DataType, SymmList, IndexList>& u,\n                                const Mesh<Dim>& mesh)\n    -> TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n        Frame::ElementLogical>;\n/// @}\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Compute the partial derivatives of each variable, using the Cartoon\n * method for directions not in the computational domain.\n *\n * For a spacetime with some Killing vector \\f$\\xi^a\\f$, not only does the\n * metric respect the isometry via \\f$\\mathcal{L}_\\xi g_{ab} = 0\\f$, but the\n * fields must as well.\n *\n * In the case of axial symmetry about the \\f$y\\f$-axis, the Killing vector is\n * \\f$\\xi^a = (0, -z, 0, x)\\f$, so we have that for a vector \\f$V^a\\f$,\n *\n * \\f{align*}{\n *     \\mathcal{L}_\\xi V^a &= \\xi^b \\partial_b V^a - V^b \\partial_b \\xi^a\n *       = -z \\partial_x V^a + x \\partial_z V^a - V^b \\partial_b \\xi^a = 0.\n * \\f}\n *\n * Choosing the computational domain to be the \\f$z=0\\f$ plane, get the result:\n *\n * \\f{align*}{\n *     \\partial_z V^a &= \\frac{V^b \\partial_b \\xi^a}{x}.\n * \\f}\n *\n * In the case that $x=0$ is in the computational domain, L'H&ocirc;pital's\n * rule is used. For a general tensor,\n * \\f$T^{a_0,\\ldots,a_n}{}_{b_0,\\ldots,b_m}\\f$ we have\n *\n * \\f{align*}{\n *   \\partial_z T^{a_0,\\ldots,a_n}{}_{b_0,\\ldots,b_m} &=\n *   \\left\\{\n *   \\begin{array}{ll}\n *     \\partial_x(\\sum_k\n *     T^{a_0,\\ldots,c_k,\\ldots,a_n}{}_{b_0,\\ldots,b_m}\\partial_{c_k}\n *     \\xi^{a_k} & \\\\\n *     \\;\\;\\;\\;\\;-\\sum_k\n *     T^{a_0,\\ldots,a_n}{}_{b_0,\\ldots,c_k,\\ldots,b_m}\\partial_{b_k}\n *     \\xi^{c_k}\n *     ) & \\mathrm{if} \\; x=0  \\\\\n *     (\\sum_k\n *     T^{a_0,\\ldots,c_k,\\ldots,a_n}{}_{b_0,\\ldots,b_m}\\partial_{c_k}\n *     \\xi^{a_k} & \\\\\n *     \\;-\\sum_k\n *     T^{a_0,\\ldots,a_n}{}_{b_0,\\ldots,c_k,\\ldots,b_m}\\partial_{b_k}\n *     \\xi^{c_k})\\frac{1}{x} & \\mathrm{otherwise}\n *   \\end{array}\\right.\n * \\f}\n *\n * In the case of spherical symmetry, another Killing vector\n * \\f$\\xi'^a = (0, -y, x, 0) \\f$ is used as well, with the above equation\n * easily generalizing. In that case, the computational domain is only the\n * \\f$x\\f$-axis.\n *\n * This function computes the partial derivatives of _all_ variables in\n * `VariableTags`. The additional parameter `inertial_coords` is used for\n * division by the \\f$x\\f$ coordinates. If \\f$x=0\\f$ is included in the domain,\n * it is assumed to be present only at the first index and is handled by\n * L'H&ocirc;pital's rule.\n *\n * The mesh is required to have the Cartoon basis in the last and potentially\n * second-to-last coordinates and the inverse Jacobian is accordingly used only\n * in the first and potentially second dimensions.\n */\ntemplate <typename ResultTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame, Requires<Dim == 3> = nullptr>\nvoid cartoon_partial_derivatives(\n    gsl::not_null<Variables<ResultTags>*> du, const Variables<VariableTags>& u,\n    const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian_3d,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n/// @}\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Compute the partial derivatives of each variable with respect to\n/// the coordinates of `DerivativeFrame`.\n///\n/// Either compute partial derivatives of _all_ variables in `VariableTags`, or\n/// of a subset of the `VariablesTags`. The subset of tags (`DerivativeTags`)\n/// must be the head of `VariablesTags`.\n///\n/// The return-by-reference overload infers all template parameters from the\n/// arguments. The tensor types in the output buffer must have a spatial index\n/// appended to the front.\n///\n/// The return-by-value overload requires that the `DerivativeTags` are\n/// specified explicitly as the first template parameter. It returns a\n/// `Variables` with the `DerivativeTags` wrapped in `Tags::deriv`.\ntemplate <typename ResultTags, typename DerivativeTags, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivatives(\n    gsl::not_null<Variables<ResultTags>*> du,\n    const std::array<Variables<DerivativeTags>, Dim>&\n        logical_partial_derivatives_of_u,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian);\n\ntemplate <typename ResultTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivatives(\n    gsl::not_null<Variables<ResultTags>*> du, const Variables<VariableTags>& u,\n    const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian);\n\ntemplate <typename DerivativeTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame>\nauto partial_derivatives(\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian)\n    -> Variables<db::wrap_tags_in<Tags::deriv, DerivativeTags,\n                                  tmpl::size_t<Dim>, DerivativeFrame>>;\n/// @}\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Compute the partial derivative of a `Tensor` with respect to\n/// the coordinates of `DerivativeFrame`.\n///\n/// Returns a `Tensor` with a spatial tensor index appended to the front\n/// of the input `Tensor`.\n///\n/// If you have a `Variables` with several tensors you need to differentiate,\n/// you should use the `partial_derivatives` function that operates on\n/// `Variables` since that'll be more efficient.\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivative(\n    const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo, DerivativeFrame>*>\n        du,\n    const TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo,\n        Frame::ElementLogical>& logical_partial_derivative_of_u,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian);\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivative(\n    const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo, DerivativeFrame>*>\n        du,\n    const Tensor<DataType, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian);\n\ntemplate <typename DataType, typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nauto partial_derivative(\n    const Tensor<DataType, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian)\n    -> TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataType, SymmList, IndexList>, Dim, UpLo::Lo, DerivativeFrame>;\n/// @}\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Compute the partial derivative of a `Tensor` with respect to\n/// the coordinates of `DerivativeFrame` when a Cartoon basis is being used.\n///\n/// Returns a `Tensor` with a spatial tensor index appended to the front\n/// of the input `Tensor`.\n///\n/// If you have a `Variables` with several tensors with Cartoon bases you need\n/// to differentiate, you should use the `cartoon_partial_derivatives` function\n/// that operates on `Variables` since that'll be more efficient.\ntemplate <typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame, Requires<Dim == 3> = nullptr>\nvoid cartoon_partial_derivative(\n    gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataVector, SymmList, IndexList>, Dim, UpLo::Lo,\n        DerivativeFrame>*>\n        du,\n    const Tensor<DataVector, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n\ntemplate <typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame, Requires<Dim == 3> = nullptr>\nauto cartoon_partial_derivative(\n    const Tensor<DataVector, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords)\n    -> TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataVector, SymmList, IndexList>, Dim, UpLo::Lo,\n        DerivativeFrame>;\n/// @}\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Calls the correct partial derivatives function, either normal\n/// partials or cartoon partials, as determined by mesh basis.\n///\n/// To be used in executables that allow a Cartoon basis, a choice that is\n/// only known at runtime.\ntemplate <typename ResultTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivatives(\n    gsl::not_null<Variables<ResultTags>*> du,\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n/// @}\n\n/// @{\n/// \\ingroup NumericalAlgorithmsGroup\n/// \\brief Calls the correct partial derivative function, either normal\n/// partial or cartoon partial, as determined by mesh basis.\n///\n/// To be used in executables that allow a Cartoon basis, a choice that is\n/// only known at runtime.\ntemplate <typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivative(\n    gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataVector, SymmList, IndexList>, Dim, UpLo::Lo,\n        DerivativeFrame>*>\n        du,\n    const Tensor<DataVector, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords);\n\ntemplate <typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nauto partial_derivative(\n    const Tensor<DataVector, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords)\n    -> TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataVector, SymmList, IndexList>, Dim, UpLo::Lo,\n        DerivativeFrame>;\n/// @}\n\nnamespace Tags {\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\brief Compute the spatial derivatives of tags in a Variables\n *\n * Computes the spatial derivatives of the Tensors in the Variables represented\n * by `VariablesTag` in the frame mapped to by `InverseJacobianTag`. To only\n * take the derivatives of a subset of these Tensors you can set the\n * `DerivTags` template parameter. It takes a `tmpl::list` of the desired\n * tags and defaults to the full `tags_list` of the Variables.\n *\n * For an executable that does not allow a Cartoon basis, the last parameter,\n * `InertialCoordsTag`, should not be passed.\n *\n * This tag may be retrieved via `::Tags::Variables<db::wrap_tags_in<deriv,\n * DerivTags, Dim, deriv_frame>`.\n */\ntemplate <typename VariablesTag, typename MeshTag, typename InverseJacobianTag,\n          typename DerivTags = typename VariablesTag::type::tags_list,\n          typename InertialCoordsTag = void>\nstruct DerivCompute\n    : db::add_tag_prefix<\n          deriv, ::Tags::Variables<DerivTags>,\n          tmpl::size_t<\n              tmpl::back<typename InverseJacobianTag::type::index_list>::dim>,\n          typename tmpl::back<\n              typename InverseJacobianTag::type::index_list>::Frame>,\n      db::ComputeTag {\n private:\n  using inv_jac_indices = typename InverseJacobianTag::type::index_list;\n  static constexpr auto Dim = tmpl::back<inv_jac_indices>::dim;\n  using deriv_frame = typename tmpl::back<inv_jac_indices>::Frame;\n\n public:\n  using base = db::add_tag_prefix<\n      deriv, ::Tags::Variables<DerivTags>,\n      tmpl::size_t<\n          tmpl::back<typename InverseJacobianTag::type::index_list>::dim>,\n      typename tmpl::back<\n          typename InverseJacobianTag::type::index_list>::Frame>;\n  using return_type = typename base::type;\n  static constexpr void function(\n      gsl::not_null<return_type*> du, const typename VariablesTag::type& u,\n      const Mesh<Dim>& mesh,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            deriv_frame>& inverse_jacobian) {\n    partial_derivatives(du, u, mesh, inverse_jacobian);\n  }\n  static constexpr void function(\n      gsl::not_null<return_type*> du, const typename VariablesTag::type& u,\n      const Mesh<Dim>& mesh,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            deriv_frame>& inverse_jacobian,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n    partial_derivatives(du, u, mesh, inverse_jacobian, inertial_coords);\n  }\n  using argument_tags = tmpl::conditional_t<\n      std::is_same_v<void, InertialCoordsTag>,\n      tmpl::list<VariablesTag, MeshTag, InverseJacobianTag>,\n      tmpl::list<VariablesTag, MeshTag, InverseJacobianTag, InertialCoordsTag>>;\n};\n\n/*!\n * \\ingroup DataBoxTagsGroup\n * \\brief Computes the spatial derivative of a single tensor tag not in a\n * Variables.\n *\n * Computes the spatial derivative for a single tensor represented by\n * 'TensorTag' in the frame mapped to by 'InverseJacobianTag'. It takes a\n * single Tensor designated by 'TensorTag', the inverse Jacobian, and a mesh.\n *\n * For an executable that does not allow a Cartoon basis, the last parameter,\n * `InertialCoordsTag`, should not be passed.\n */\ntemplate <typename TensorTag, typename InverseJacobianTag, typename MeshTag,\n          typename InertialCoordsTag = void>\nstruct DerivTensorCompute\n    : ::Tags::deriv<TensorTag,\n                    tmpl::size_t<tmpl::back<\n                        typename InverseJacobianTag::type::index_list>::dim>,\n                    typename tmpl::back<\n                        typename InverseJacobianTag::type::index_list>::Frame>,\n      db::ComputeTag {\n private:\n  using inv_jac_indices = typename InverseJacobianTag::type::index_list;\n  static constexpr auto Dim = tmpl::back<inv_jac_indices>::dim;\n  using deriv_frame = typename tmpl::back<inv_jac_indices>::Frame;\n\n public:\n  using base = ::Tags::deriv<TensorTag, tmpl::size_t<Dim>, deriv_frame>;\n  using return_type = typename base::type;\n  static constexpr void function(\n      const gsl::not_null<return_type*> du, const typename TensorTag::type& u,\n      const Mesh<Dim>& mesh,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            deriv_frame>& inverse_jacobian) {\n    partial_derivative(du, u, mesh, inverse_jacobian);\n  }\n  static constexpr void function(\n      const gsl::not_null<return_type*> du, const typename TensorTag::type& u,\n      const Mesh<Dim>& mesh,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            deriv_frame>& inverse_jacobian,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n    partial_derivative(du, u, mesh, inverse_jacobian, inertial_coords);\n  }\n  using argument_tags = tmpl::conditional_t<\n      std::is_same_v<void, InertialCoordsTag>,\n      tmpl::list<TensorTag, domain::Tags::Mesh<Dim>, InverseJacobianTag>,\n      tmpl::list<TensorTag, domain::Tags::Mesh<Dim>, InverseJacobianTag,\n                 InertialCoordsTag>>;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n#include \"NumericalAlgorithms/Spectral/ParityFromSymmetry.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace partial_derivatives_detail {\ntemplate <size_t Dim, typename VariableTags, typename DerivativeTags>\nstruct LogicalImpl;\n\n// This routine has been optimized to perform really well. The following\n// describes what optimizations were made.\n//\n// - The `partial_derivatives` functions below have an overload where the\n//   logical derivatives may be passed in instead of being computed. In the\n//   overloads where the logical derivatives are not passed in they must be\n//   computed. However, it is more efficient to allocate the memory for the\n//   logical partial derivatives with respect to each coordinate at once. This\n//   requires the `partial_derivatives_impl` to accept raw pointers to doubles\n//   for the logical derivatives so it can be used for all overloads.\n//\n// - The resultant Variables `du` is a not_null pointer so that mutating compute\n//   items can be supported.\n//\n// - The storage indices into the inverse Jacobian are precomputed to avoid\n//   having to recompute them for each tensor component of `u`.\n//\n// - The DataVectors lhs and logical_du are non-owning DataVectors to be able to\n//   plug into the optimized expression templates. This requires a `const_cast`\n//   even though we will never change the `double*`.\n//\n// - Loop over every Tensor component in the variables by incrementing a raw\n//   pointer to the contiguous data (vs. looping over each Tensor in the\n//   variables with a tmpl::for_each then iterating over each component of this\n//   Tensor).\n//\n// - We factor out the `logical_deriv_index == 0` case so that we do not need to\n//   zero the memory in `du` before the computation.\ntemplate <typename ResultTags, size_t Dim, typename DerivativeFrame,\n          typename ValueType = typename Variables<ResultTags>::value_type,\n          typename VectorType = typename Variables<ResultTags>::vector_type>\nvoid partial_derivatives_impl(\n    const gsl::not_null<Variables<ResultTags>*> du,\n    const std::array<const ValueType*, Dim>& logical_partial_derivatives_of_u,\n    const size_t number_of_independent_components,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  ValueType* pdu = du->data();\n  const size_t num_grid_points = du->number_of_grid_points();\n  VectorType lhs{};\n  VectorType logical_du{};\n\n  std::array<std::array<size_t, Dim>, Dim> indices{};\n  for (size_t deriv_index = 0; deriv_index < Dim; ++deriv_index) {\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(gsl::at(indices, d), deriv_index) =\n          InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>::get_storage_index(d, deriv_index);\n    }\n  }\n\n  for (size_t component_index = 0;\n       component_index < number_of_independent_components; ++component_index) {\n    for (size_t deriv_index = 0; deriv_index < Dim; ++deriv_index) {\n      lhs.set_data_ref(pdu, num_grid_points);\n      // clang-tidy: const cast is fine since we won't modify the data and we\n      // need it to easily hook into the expression templates.\n      logical_du.set_data_ref(\n          const_cast<ValueType*>(                              // NOLINT\n              gsl::at(logical_partial_derivatives_of_u, 0)) +  // NOLINT\n              component_index * num_grid_points,\n          num_grid_points);\n      lhs = (*(inverse_jacobian.begin() + gsl::at(indices[0], deriv_index))) *\n            logical_du;\n      for (size_t logical_deriv_index = 1; logical_deriv_index < Dim;\n           ++logical_deriv_index) {\n        // clang-tidy: const cast is fine since we won't modify the data and we\n        // need it to easily hook into the expression templates.\n        logical_du.set_data_ref(const_cast<ValueType*>(  // NOLINT\n                                    gsl::at(logical_partial_derivatives_of_u,\n                                            logical_deriv_index)) +  // NOLINT\n                                    component_index * num_grid_points,\n                                num_grid_points);\n        lhs +=\n            (*(inverse_jacobian.begin() +\n               gsl::at(gsl::at(indices, logical_deriv_index), deriv_index))) *\n            logical_du;\n      }\n      // clang-tidy: no pointer arithmetic\n      pdu += num_grid_points;  // NOLINT\n    }\n  }\n}\n}  // namespace partial_derivatives_detail\n\ntemplate <typename DerivativeTags, typename VariableTags, size_t Dim>\nvoid logical_partial_derivatives(\n    const gsl::not_null<std::array<Variables<DerivativeTags>, Dim>*>\n        logical_partial_derivatives_of_u,\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh) {\n  using ValueType = typename Variables<VariableTags>::value_type;\n  if (UNLIKELY((*logical_partial_derivatives_of_u)[0].number_of_grid_points() !=\n               u.number_of_grid_points())) {\n    for (auto& deriv : *logical_partial_derivatives_of_u) {\n      deriv.initialize(u.number_of_grid_points());\n    }\n  }\n  std::array<ValueType*, Dim> deriv_pointers{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(deriv_pointers, i) =\n        gsl::at(*logical_partial_derivatives_of_u, i).data();\n  }\n  if constexpr (Dim == 1) {\n    Variables<DerivativeTags>* temp = nullptr;\n    partial_derivatives_detail::LogicalImpl<Dim, VariableTags, DerivativeTags>::\n        apply(make_not_null(&deriv_pointers), temp, temp, u, mesh);\n    return;\n  } else {\n    auto buffer = cpp20::make_unique_for_overwrite<ValueType[]>(\n        2 * u.number_of_grid_points() *\n        Variables<DerivativeTags>::number_of_independent_components);\n    Variables<DerivativeTags> temp0(\n        &buffer[0],\n        u.number_of_grid_points() *\n            Variables<DerivativeTags>::number_of_independent_components);\n    Variables<DerivativeTags> temp1(\n        &buffer[u.number_of_grid_points() *\n                Variables<DerivativeTags>::number_of_independent_components],\n        u.number_of_grid_points() *\n            Variables<DerivativeTags>::number_of_independent_components);\n    partial_derivatives_detail::LogicalImpl<Dim, VariableTags, DerivativeTags>::\n        apply(make_not_null(&deriv_pointers), &temp0, &temp1, u, mesh);\n  }\n}\n\ntemplate <typename DerivativeTags, typename VariableTags, size_t Dim>\nstd::array<Variables<DerivativeTags>, Dim> logical_partial_derivatives(\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh) {\n  auto logical_partial_derivatives_of_u =\n      make_array<Dim>(Variables<DerivativeTags>(u.number_of_grid_points()));\n  logical_partial_derivatives<DerivativeTags>(\n      make_not_null(&logical_partial_derivatives_of_u), u, mesh);\n  return logical_partial_derivatives_of_u;\n}\n\ntemplate <typename ResultTags, typename DerivativeTags, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivatives(\n    const gsl::not_null<Variables<ResultTags>*> du,\n    const std::array<Variables<DerivativeTags>, Dim>&\n        logical_partial_derivatives_of_u,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  using ValueType = typename Variables<DerivativeTags>::value_type;\n  auto& partial_derivatives_of_u = *du;\n  // For mutating compute items we must set the size.\n  if (UNLIKELY(partial_derivatives_of_u.number_of_grid_points() !=\n               logical_partial_derivatives_of_u[0].number_of_grid_points())) {\n    partial_derivatives_of_u.initialize(\n        logical_partial_derivatives_of_u[0].number_of_grid_points());\n  }\n\n  std::array<const ValueType*, Dim> logical_derivs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(logical_derivs, i) =\n        gsl::at(logical_partial_derivatives_of_u, i).data();\n  }\n  partial_derivatives_detail::partial_derivatives_impl(\n      make_not_null(&partial_derivatives_of_u), logical_derivs,\n      Variables<DerivativeTags>::number_of_independent_components,\n      inverse_jacobian);\n}\n\nconstexpr tnsr::iab<double, 3> Killing_vector_derivatives() {\n  // holds derivative of both (0,-y,x,0) for \\partial_y and (0,-z,0,x) for\n  // \\partial_z\n  tnsr::iab<double, 3> da_killing_vectors{};\n  for (size_t i = 0; i < da_killing_vectors.size(); ++i) {\n    da_killing_vectors[i] = 0.0;\n  }\n  // y derivatives\n  da_killing_vectors.get(1, 2, 1) = -1.0;\n  da_killing_vectors.get(1, 1, 2) = 1.0;\n  // z derivatives\n  da_killing_vectors.get(2, 3, 1) = -1.0;\n  da_killing_vectors.get(2, 1, 3) = 1.0;\n  return da_killing_vectors;\n}\n\ntemplate <typename Symm, typename Indices>\nvoid cartoon_contraction(\n    const gsl::not_null<Tensor<DataVector, Symm, Indices>*> result_tensor,\n    const Tensor<DataVector, Symm, Indices>& tensor, const size_t deriv_index,\n    const Spectral::Quadrature quad_type) {\n  // This function does the contractions in Eqn. (218) of the SXS book's\n  // Numerical Method's chapter, specifically the thing on the RHS that you take\n  // the derivative of or divide by x.\n  // Note that the result_tensor is written over and will have\n  // (*result_tensor)[0].size() == tensor[0].size()\n  auto& contract_tensor = *result_tensor;\n  ASSERT(quad_type == Spectral::Quadrature::AxialSymmetry or\n             quad_type == Spectral::Quadrature::SphericalSymmetry,\n         \"Must pass a valid Cartoon quadrature\");\n\n  constexpr tnsr::iab<double, 3> da_killing_vectors =\n      Killing_vector_derivatives();\n  const std::array<UpLo, Tensor<DataVector, Symm, Indices>::rank()> valences =\n      tensor.index_valences();\n  const auto type_index = tensor.index_types();\n\n  const size_t valence_size = valences.size();\n  auto input_tensor_array = make_array<valence_size>(0_st);\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)\n  std::array<size_t, 3> Killing_indices;\n  Killing_indices[0] = deriv_index;\n\n  for (size_t i = 0; i < tensor.size(); ++i) {\n    const auto tensor_index = tensor.get_tensor_index(i);\n    contract_tensor.get(tensor_index) = 0.0 * tensor.get(tensor_index);\n    for (size_t rank = 0; rank < valence_size; ++rank) {\n      const double sign = (valences[rank] == UpLo::Up) ? 1.0 : -1.0;\n      const size_t max_dummy = type_index[rank] == IndexType::Spacetime ? 4 : 3;\n      const size_t shift_index =\n          type_index[rank] == IndexType::Spacetime ? 0 : 1;\n\n      // loop over dimension of rank\n      for (size_t dummy = 0; dummy < max_dummy; ++dummy) {\n        if (valences[rank] == UpLo::Lo) {\n          // covariant index\n          Killing_indices[1] = tensor_index[rank] + shift_index;\n          Killing_indices[2] = dummy + shift_index;\n        } else {\n          // contravariant\n          Killing_indices[1] = dummy + shift_index;\n          Killing_indices[2] = tensor_index[rank] + shift_index;\n        }\n        if (da_killing_vectors.get(Killing_indices) != 0) {\n          for (size_t j = 0; j < tensor_index.size(); ++j) {\n            input_tensor_array[j] = tensor_index[j];\n          }\n          input_tensor_array[rank] = dummy;\n\n          contract_tensor.get(tensor_index) +=\n              sign * tensor.get(input_tensor_array) *\n              da_killing_vectors.get(Killing_indices);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename InputTensorDataType, size_t Dim, typename Frame,\n          Requires<Dim == 3> = nullptr>\nvoid cartoon_derivative(\n    TensorMetafunctions::prepend_spatial_index<InputTensorDataType, Dim,\n                                               UpLo::Lo, Frame>& d_tensor,\n    const InputTensorDataType& tensor, const DataVector& safe_x_coords,\n    const Spectral::Quadrature quad_type) {\n  // This function assumes `safe_x_coords` does not contain zero (safe in the\n  // sense that having x in the denominator of an expression  will not cause\n  // an FPE).\n  // Therefore, if the domain actually does have zero, the coordinates must be\n  // made safe by replacing all occurances of zero with a non-zero value, and\n  // the output from this function is then not valid at those coords, one must\n  // then manually use L'Hopital's rule\n  ASSERT(quad_type == Spectral::Quadrature::AxialSymmetry or\n             quad_type == Spectral::Quadrature::SphericalSymmetry,\n         \"Must pass a valid Cartoon quadrature\");\n  ASSERT(\n      not equal_within_roundoff(0.0, safe_x_coords[0],\n                                std::numeric_limits<double>::epsilon() * 100.0,\n                                max(safe_x_coords)),\n      \"Invalid coordinate: safe_x_coords[0] is too close to zero (\"\n          << safe_x_coords[0]\n          << \"). Division by this value may cause an FPE if the value is zero. \"\n             \"Maximum coordinate: \"\n          << max(safe_x_coords));\n\n  const bool spherical_sym =\n      quad_type == Spectral::Quadrature::SphericalSymmetry;\n  const size_t start_deriv_index = spherical_sym ? 1 : 2;\n\n  InputTensorDataType tensor_holder;\n\n  for (size_t deriv_index = start_deriv_index; deriv_index < 3; ++deriv_index) {\n    cartoon_contraction(make_not_null(&tensor_holder), tensor, deriv_index,\n                        quad_type);\n    for (size_t component_index = 0; component_index < tensor_holder.size();\n         ++component_index) {\n      const auto input_index = tensor_holder.get_tensor_index(component_index);\n      const auto output_index = prepend(input_index, deriv_index);\n      d_tensor.get(output_index) =\n          tensor_holder.get(input_index) / safe_x_coords;\n    }\n  }\n}\n\ntemplate <size_t CompDim, typename ResultTags, typename VariableTags,\n          size_t Dim, typename DerivativeFrame>\nvoid cartoon_partial_derivatives_apply(\n    const gsl::not_null<Variables<ResultTags>*> du,\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian_3d,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  using DerivativeTags =\n      tmpl::front<tmpl::split_at<VariableTags, tmpl::size<ResultTags>>>;\n  static_assert(Dim == 3);\n  static_assert(\n      std::is_same_v<\n          tmpl::transform<ResultTags, tmpl::bind<tmpl::type_from, tmpl::_1>>,\n          tmpl::transform<db::wrap_tags_in<Tags::deriv, DerivativeTags,\n                                           tmpl::size_t<Dim>, DerivativeFrame>,\n                          tmpl::bind<tmpl::type_from, tmpl::_1>>>);\n  ASSERT((CompDim == 2 and\n          mesh.quadrature(2) == Spectral::Quadrature::AxialSymmetry) or\n             (CompDim == 1 and\n              (mesh.quadrature(1) == Spectral::Quadrature::SphericalSymmetry and\n               mesh.quadrature(2) == Spectral::Quadrature::SphericalSymmetry)),\n         \"Invalid Quadrature combinations: axial symmetry requires 2 \"\n         \"non-Cartoon dimensions, spherical symmetry requires 1 non-Cartoon \"\n         \"dimension. Got: \"\n             << mesh.quadrature());\n\n  using ValueType = typename Variables<VariableTags>::value_type;\n  auto& partial_derivatives_of_u = *du;\n\n  if (UNLIKELY(partial_derivatives_of_u.number_of_grid_points() !=\n               mesh.number_of_grid_points())) {\n    partial_derivatives_of_u.initialize(mesh.number_of_grid_points());\n  }\n\n  const size_t vars_size =\n      u.number_of_grid_points() *\n      Variables<DerivativeTags>::number_of_independent_components;\n  const auto logical_derivs_data =\n      cpp20::make_unique_for_overwrite<ValueType[]>(\n          (CompDim > 1 ? (CompDim + 1) : CompDim) * vars_size);\n\n  std::array<ValueType*, CompDim> logical_derivs{};\n  for (size_t i = 0; i < CompDim; ++i) {\n    gsl::at(logical_derivs, i) = &(logical_derivs_data[i * vars_size]);\n  }\n  Variables<DerivativeTags> temp{};\n  if constexpr (CompDim > 1) {\n    temp.set_data_ref(&logical_derivs_data[CompDim * vars_size], vars_size);\n  }\n\n  if constexpr (CompDim == 1) {\n    partial_derivatives_detail::\n        LogicalImpl<CompDim, VariableTags, DerivativeTags>::apply(\n            make_not_null(&logical_derivs), &partial_derivatives_of_u, &temp, u,\n            mesh.slice_through(0));\n  } else {\n    partial_derivatives_detail::\n        LogicalImpl<CompDim, VariableTags, DerivativeTags>::apply(\n            make_not_null(&logical_derivs), &partial_derivatives_of_u, &temp, u,\n            mesh.slice_through(0, 1));\n  }\n\n  std::array<const ValueType*, CompDim> const_logical_derivs{};\n  for (size_t i = 0; i < CompDim; ++i) {\n    gsl::at(const_logical_derivs, i) = gsl::at(logical_derivs, i);\n  }\n\n  const Spectral::Quadrature quad_type = mesh.quadrature(2);\n  const auto numerical_deriv_in_this_direction = [](const size_t deriv_num) {\n    if constexpr (CompDim == 2) {\n      return deriv_num == 0 or deriv_num == 1;\n    } else {\n      return deriv_num == 0;\n    }\n  };\n  // only the 1st (possibly 2nd) dimensions of the Jacobian are relevant here\n  const InverseJacobian<DataVector, CompDim, Frame::ElementLogical,\n                        DerivativeFrame>\n      inverse_jacobian{inverse_jacobian_3d.begin()->size()};\n  for (size_t i = 0; i < CompDim; ++i) {\n    for (size_t j = 0; j < CompDim; ++j) {\n      make_const_view(make_not_null(&inverse_jacobian.get(i, j)),\n                      inverse_jacobian_3d.get(i, j), 0,\n                      inverse_jacobian_3d.begin()->size());\n    }\n  }\n  // pdu points to du\n  double* pdu = du->data();\n  const size_t num_grid_points = du->number_of_grid_points();\n  DataVector lhs{};\n  DataVector logical_du{};\n  DataVector safe_x_coords = get<0>(inertial_coords);\n  const bool element_contains_zero_of_symmetry_axis = equal_within_roundoff(\n      0.0, safe_x_coords[0], std::numeric_limits<double>::epsilon() * 100.0,\n      max(safe_x_coords));\n  // Having x=0 requires both not dividing by zero (done here by setting to\n  // arbitrary non-zero value) and remembering to then do L'Hopital\n  if (element_contains_zero_of_symmetry_axis) {\n    safe_x_coords[0] = 1.0;\n    if constexpr (CompDim == 2) {\n      const size_t x_extents = mesh.extents(0);\n      const size_t y_extents = mesh.extents(1);\n      for (size_t i = 1; i < y_extents; ++i) {\n        ASSERT(\n            equal_within_roundoff(\n                0.0, safe_x_coords[i * x_extents],\n                std::numeric_limits<double>::epsilon() * 100.0,\n                max(safe_x_coords)),\n            \"The passed inertial coordinates do not follow the required format \"\n            \"of a rectangular mesh with x=0 being located at the first index \"\n            \"of each constant-y subdomain of the x DataVector. x=\"\n                << safe_x_coords);\n        safe_x_coords[i * x_extents] = 1.0;\n      }\n    }\n  }\n  // If doing L'Hopital's rule, we need to store temporary forms of the data\n  // in two different tensors (hence the 0 & 1 labels) for each tensor type\n  // They only have to hold the x=0 positions, so of size y_extents\n  using VariableTypes = tmpl::remove_duplicates<\n      tmpl::transform<DerivativeTags, tmpl::bind<tmpl::type_from, tmpl::_1>>>;\n\n  using TempTags0 = ::Tags::convert_to_temp_tensors<VariableTypes, 0>;\n  using TempTags1 = ::Tags::convert_to_temp_tensors<VariableTypes, 1>;\n  using VarTags = tmpl::append<TempTags0, TempTags1>;\n\n  Variables<VarTags> temp_vars{};\n  if (element_contains_zero_of_symmetry_axis) {\n    const size_t y_extents = mesh.extents(1);\n    temp_vars.initialize(y_extents);\n  }\n\n  std::array<std::array<size_t, CompDim>, CompDim> indices{};\n  for (size_t deriv_index = 0; deriv_index < CompDim; ++deriv_index) {\n    for (size_t d = 0; d < CompDim; ++d) {\n      gsl::at(gsl::at(indices, d), deriv_index) =\n          InverseJacobian<DataVector, CompDim, Frame::ElementLogical,\n                          DerivativeFrame>::get_storage_index(d, deriv_index);\n    }\n  }\n\n  // the non-cartoon derivatives need to know where they are within `u` to\n  // contract with the inverse Jacobian to compute the inertial derivatives\n  size_t global_component_index = 0;\n  tmpl::for_each<DerivativeTags>(\n      [&u, &du, &lhs, &pdu, &num_grid_points, &logical_du,\n       &const_logical_derivs, &inverse_jacobian, &indices, &temp_vars,\n       &global_component_index, &safe_x_coords,\n       &numerical_deriv_in_this_direction, &quad_type, &mesh,\n       &element_contains_zero_of_symmetry_axis]<typename TensorTag>(\n          tmpl::type_<TensorTag> /*meta*/) {\n        auto& tensor = get<TensorTag>(u);\n\n        TensorMetafunctions::prepend_spatial_index<\n            typename TensorTag::type, Dim, UpLo::Lo, Frame::Inertial>\n            cart_deriv_tensor;\n        cartoon_derivative<typename TensorTag::type, 3, Frame::Inertial>(\n            cart_deriv_tensor, tensor, safe_x_coords, quad_type);\n\n        for (size_t component_index = 0;\n             component_index < TensorTag::type::size();\n             ++component_index, ++global_component_index) {\n          for (size_t deriv_index = 0; deriv_index < Dim; ++deriv_index) {\n            // lhs points to pdu (shifts by num grid points below)\n            lhs.set_data_ref(pdu, num_grid_points);\n\n            if (numerical_deriv_in_this_direction(deriv_index)) {\n              // do normal derivative for x (and possibly y) derivative\n              // clang-tidy: const cast is fine since we won't modify the data\n              // and we need it to easily hook into the expression templates.\n              logical_du.set_data_ref(\n                  const_cast<ValueType*>(                  // NOLINT\n                      gsl::at(const_logical_derivs, 0)) +  // NOLINT\n                      global_component_index * num_grid_points,\n                  num_grid_points);\n              lhs = (*(inverse_jacobian.begin() +\n                       gsl::at(indices[0], deriv_index))) *\n                    logical_du;\n              if constexpr (CompDim == 2) {\n                // clang-tidy: const cast is fine since we won't modify the data\n                // and we need it to easily hook into the expression templates.\n                logical_du.set_data_ref(\n                    const_cast<ValueType*>(                  // NOLINT\n                        gsl::at(const_logical_derivs, 1)) +  // NOLINT\n                        global_component_index * num_grid_points,\n                    num_grid_points);\n                lhs += (*(inverse_jacobian.begin() +\n                          gsl::at(gsl::at(indices, 1), deriv_index))) *\n                       logical_du;\n              }\n            } else {\n              // do cartoon dervative\n              const auto input_tensor_index =\n                  tensor.get_tensor_index(component_index);\n              const auto output_tensor_index =\n                  prepend(input_tensor_index, size_t{deriv_index});\n              lhs = cart_deriv_tensor.get(output_tensor_index);\n            }\n            // clang-tidy: no pointer arithmetic\n            pdu += num_grid_points;  // NOLINT\n          }\n        }\n        if (element_contains_zero_of_symmetry_axis) {\n          // need to use L'Hopital\n          using dtensor_type =\n              tmpl::at<ResultTags, tmpl::index_of<DerivativeTags, TensorTag>>;\n          auto& dtensor = get<dtensor_type>(*du);\n\n          // if spherical symmetry,the zero is only at the first index\n          // if axial symmetry, the zero is repeated every x_extents times\n          const size_t x_extents = mesh.extents(0);\n          // for spherical symmetry, y_extents = 1, so we could use a double\n          // type rather than a size 1 DataVector, but it requires a lot of code\n          // reuse\n          const size_t y_extents = mesh.extents(1);\n\n          auto& dx_tensor =\n              get<::Tags::TempTensor<0, typename TensorTag::type>>(temp_vars);\n          auto& contracted_dx_tensor =\n              get<::Tags::TempTensor<1, typename TensorTag::type>>(temp_vars);\n\n          for (size_t component_index = 0; component_index < dx_tensor.size();\n               ++component_index) {\n            const auto output_index =\n                dx_tensor.get_tensor_index(component_index);\n            // the 0 is because we want the x derivative\n            const size_t input_index =\n                dtensor.get_storage_index(prepend(output_index, size_t{0}));\n            for (size_t i = 0; i < y_extents; ++i) {\n              dx_tensor[component_index][i] =\n                  dtensor[input_index][i * x_extents];\n            }\n          }\n\n          const size_t start_index =\n              quad_type == Spectral::Quadrature::SphericalSymmetry ? 1 : 2;\n          for (size_t deriv_index = start_index; deriv_index < 3;\n               ++deriv_index) {\n            cartoon_contraction(make_not_null(&contracted_dx_tensor), dx_tensor,\n                                deriv_index, quad_type);\n            for (size_t component_index = 0;\n                 component_index < contracted_dx_tensor.size();\n                 ++component_index) {\n              const auto input_index =\n                  contracted_dx_tensor.get_tensor_index(component_index);\n              const size_t output_index =\n                  dtensor.get_storage_index(prepend(input_index, deriv_index));\n              for (size_t i = 0; i < y_extents; ++i) {\n                dtensor[output_index][i * x_extents] =\n                    contracted_dx_tensor[component_index][i];\n              }\n            }\n          }\n        }\n      });\n}\n\ntemplate <typename ResultTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame, Requires<Dim == 3>>\nvoid cartoon_partial_derivatives(\n    const gsl::not_null<Variables<ResultTags>*> du,\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian_3d,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  static_assert(Dim == 3);\n  if (mesh.basis(0) != Spectral::Basis::Cartoon and\n      mesh.basis(2) == Spectral::Basis::Cartoon) {\n    // computational dimension needs to be a constexpr\n    if (mesh.basis(1) == Spectral::Basis::Cartoon) {\n      cartoon_partial_derivatives_apply<1, ResultTags, VariableTags, Dim,\n                                        DerivativeFrame>(\n          du, u, mesh, inverse_jacobian_3d, inertial_coords);\n    } else {\n      cartoon_partial_derivatives_apply<2, ResultTags, VariableTags, Dim,\n                                        DerivativeFrame>(\n          du, u, mesh, inverse_jacobian_3d, inertial_coords);\n    }\n  } else {\n    ERROR(\"Bases do not match valid Cartoon pattern.\");\n  }\n}\n\ntemplate <typename ResultTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivatives(\n    const gsl::not_null<Variables<ResultTags>*> du,\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  using DerivativeTags =\n      tmpl::front<tmpl::split_at<VariableTags, tmpl::size<ResultTags>>>;\n  static_assert(\n      std::is_same_v<\n          tmpl::transform<ResultTags, tmpl::bind<tmpl::type_from, tmpl::_1>>,\n          tmpl::transform<db::wrap_tags_in<Tags::deriv, DerivativeTags,\n                                           tmpl::size_t<Dim>, DerivativeFrame>,\n                          tmpl::bind<tmpl::type_from, tmpl::_1>>>);\n  using ValueType = typename Variables<VariableTags>::value_type;\n  auto& partial_derivatives_of_u = *du;\n  // For mutating compute items we must set the size.\n  if (UNLIKELY(partial_derivatives_of_u.number_of_grid_points() !=\n               mesh.number_of_grid_points())) {\n    partial_derivatives_of_u.initialize(mesh.number_of_grid_points());\n  }\n\n  const size_t vars_size =\n      u.number_of_grid_points() *\n      Variables<DerivativeTags>::number_of_independent_components;\n  const auto logical_derivs_data =\n      cpp20::make_unique_for_overwrite<ValueType[]>(\n          (Dim > 1 ? (Dim + 1) : Dim) * vars_size);\n  std::array<ValueType*, Dim> logical_derivs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(logical_derivs, i) = &(logical_derivs_data[i * vars_size]);\n  }\n  Variables<DerivativeTags> temp{};\n  if constexpr (Dim > 1) {\n    temp.set_data_ref(&logical_derivs_data[Dim * vars_size], vars_size);\n  }\n  partial_derivatives_detail::LogicalImpl<\n      Dim, VariableTags, DerivativeTags>::apply(make_not_null(&logical_derivs),\n                                                &partial_derivatives_of_u,\n                                                &temp, u, mesh);\n\n  std::array<const ValueType*, Dim> const_logical_derivs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(const_logical_derivs, i) = gsl::at(logical_derivs, i);\n  }\n  partial_derivatives_detail::partial_derivatives_impl(\n      make_not_null(&partial_derivatives_of_u), const_logical_derivs,\n      Variables<DerivativeTags>::number_of_independent_components,\n      inverse_jacobian);\n}\n\ntemplate <typename DerivativeTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame>\nVariables<db::wrap_tags_in<Tags::deriv, DerivativeTags, tmpl::size_t<Dim>,\n                           DerivativeFrame>>\npartial_derivatives(\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  Variables<db::wrap_tags_in<Tags::deriv, DerivativeTags, tmpl::size_t<Dim>,\n                             DerivativeFrame>>\n      partial_derivatives_of_u(u.number_of_grid_points());\n  partial_derivatives(make_not_null(&partial_derivatives_of_u), u, mesh,\n                      inverse_jacobian);\n  return partial_derivatives_of_u;\n}\n\ntemplate <typename ResultTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivatives(\n    const gsl::not_null<Variables<ResultTags>*> du,\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  if constexpr (Dim == 3) {\n    if (mesh.basis(2) == Spectral::Basis::Cartoon) {\n      cartoon_partial_derivatives(du, u, mesh, inverse_jacobian,\n                                  inertial_coords);\n    } else {\n      partial_derivatives(du, u, mesh, inverse_jacobian);\n    }\n  } else {\n    (void)inertial_coords;\n    partial_derivatives(du, u, mesh, inverse_jacobian);\n  }\n}\n\ntemplate <typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame, Requires<Dim == 3>>\nvoid cartoon_partial_derivative(\n    const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataVector, SymmList, IndexList>, Dim, UpLo::Lo,\n        DerivativeFrame>*>\n        du,\n    const Tensor<DataVector, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  auto& du_tnsr = *du;\n  using u_type = Tensor<DataVector, SymmList, IndexList>;\n  using du_type =\n      TensorMetafunctions::prepend_spatial_index<u_type, Dim, UpLo::Lo,\n                                                 DerivativeFrame>;\n  using vars_list = ::Tags::convert_to_temp_tensors<tmpl::list<u_type>, 0>;\n  using d_vars_list = ::Tags::convert_to_temp_tensors<tmpl::list<du_type>, 0>;\n  Variables<vars_list> u_vars{mesh.number_of_grid_points()};\n  get<tmpl::front<vars_list>>(u_vars) = u;\n  Variables<d_vars_list> du_vars{mesh.number_of_grid_points()};\n  cartoon_partial_derivatives(make_not_null(&du_vars), u_vars, mesh,\n                              inverse_jacobian, inertial_coords);\n  du_tnsr = get<tmpl::front<d_vars_list>>(du_vars);\n}\n\ntemplate <typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame, Requires<Dim == 3>>\nauto cartoon_partial_derivative(\n    const Tensor<DataVector, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords)\n    -> TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataVector, SymmList, IndexList>, Dim, UpLo::Lo,\n        DerivativeFrame> {\n  using u_type = Tensor<DataVector, SymmList, IndexList>;\n  using du_type =\n      TensorMetafunctions::prepend_spatial_index<u_type, Dim, UpLo::Lo,\n                                                 DerivativeFrame>;\n  using vars_list = ::Tags::convert_to_temp_tensors<tmpl::list<u_type>, 0>;\n  using d_vars_list = ::Tags::convert_to_temp_tensors<tmpl::list<du_type>, 0>;\n  Variables<vars_list> u_vars{mesh.number_of_grid_points()};\n  get<tmpl::front<vars_list>>(u_vars) = u;\n  Variables<d_vars_list> du_vars{mesh.number_of_grid_points()};\n  cartoon_partial_derivatives(make_not_null(&du_vars), u_vars, mesh,\n                              inverse_jacobian, inertial_coords);\n  return get<tmpl::front<d_vars_list>>(du_vars);\n}\n\ntemplate <typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nvoid partial_derivative(\n    const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataVector, SymmList, IndexList>, Dim, UpLo::Lo,\n        DerivativeFrame>*>\n        du,\n    const Tensor<DataVector, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n  if constexpr (Dim == 3) {\n    if (mesh.basis(2) == Spectral::Basis::Cartoon) {\n      cartoon_partial_derivative(du, u, mesh, inverse_jacobian,\n                                 inertial_coords);\n    } else {\n      partial_derivative(du, u, mesh, inverse_jacobian);\n    }\n  } else {\n    (void)inertial_coords;\n    partial_derivative(du, u, mesh, inverse_jacobian);\n  }\n}\n\ntemplate <typename SymmList, typename IndexList, size_t Dim,\n          typename DerivativeFrame>\nauto partial_derivative(\n    const Tensor<DataVector, SymmList, IndexList>& u, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords)\n    -> TensorMetafunctions::prepend_spatial_index<\n        Tensor<DataVector, SymmList, IndexList>, Dim, UpLo::Lo,\n        DerivativeFrame> {\n  if constexpr (Dim == 3) {\n    if (mesh.basis(2) == Spectral::Basis::Cartoon) {\n      return cartoon_partial_derivative(u, mesh, inverse_jacobian,\n                                        inertial_coords);\n    } else {\n      return partial_derivative(u, mesh, inverse_jacobian);\n    }\n  } else {\n    (void)inertial_coords;\n    return partial_derivative(u, mesh, inverse_jacobian);\n  }\n}\n\nnamespace partial_derivatives_detail {\ntemplate <typename VariableTags, typename DerivativeTags>\nstruct LogicalImpl<1, VariableTags, DerivativeTags> {\n  static constexpr const size_t Dim = 1;\n  template <typename T, typename ValueType = typename Variables<T>::value_type>\n  static void apply(\n      const gsl::not_null<std::array<ValueType*, Dim>*> logical_du,\n      Variables<T>* /*unused_in_1d*/,\n      Variables<DerivativeTags>* const /*unused_in_1d*/,\n      const Variables<VariableTags>& u, const Mesh<Dim>& mesh) {\n    auto& logical_partial_derivatives_of_u = *logical_du;\n    const size_t deriv_size =\n        Variables<DerivativeTags>::number_of_independent_components *\n        u.number_of_grid_points();\n    if (UNLIKELY(mesh.basis(0) == Spectral::Basis::ZernikeB1)) {\n      if constexpr (std::is_same_v<ValueType, double>) {\n        zernikeb1_apply(make_not_null(logical_partial_derivatives_of_u[0]), u,\n                        mesh.number_of_grid_points(), mesh.extents(0));\n      } else {\n        ERROR(\n            \"Support for complex numbers with ZernikeB1 is not yet implemented \"\n            \"for logical_partial_derivative.\");\n      }\n    } else {\n      const Matrix& differentiation_matrix_xi =\n          Spectral::differentiation_matrix(mesh.slice_through(0));\n      apply_matrix_in_first_dim(logical_partial_derivatives_of_u[0], u.data(),\n                                differentiation_matrix_xi, deriv_size);\n    }\n  }\n\n  template <typename T, typename ValueType = typename Variables<T>::value_type>\n  static void zernikeb1_apply(\n      const gsl::not_null<ValueType*> logical_partial_derivative_of_u,\n      const Variables<VariableTags>& u, const size_t num_grid_points,\n      const size_t extents_xi, Variables<T>* const buffer1,\n      Variables<DerivativeTags>* const buffer2) {\n    // Zernike bases need a differentiation matrix based on their parity. For\n    // ZernikeB1, that information is stored in the index structure.\n    // We sort components to a buffer by parity, apply the appropriate\n    // matrices, and copy the values to their destination\n\n    const Matrix& even_diff_matrix =\n        Spectral::differentiation_matrix<Spectral::Basis::ZernikeB1,\n                                         Spectral::Quadrature::GaussRadauUpper>(\n            extents_xi, Spectral::Parity::Even);\n    const Matrix& odd_diff_matrix =\n        Spectral::differentiation_matrix<Spectral::Basis::ZernikeB1,\n                                         Spectral::Quadrature::GaussRadauUpper>(\n            extents_xi, Spectral::Parity::Odd);\n\n    ValueType* p_logical_derivs = logical_partial_derivative_of_u;\n    const ValueType* p_data = u.data();\n    bool even = true;\n\n    constexpr auto parity_info =\n        Spectral::compute_parity_list<DerivativeTags>();\n    const auto parity_list = std::get<0>(parity_info);\n    const auto num_even_comps = std::get<1>(parity_info);\n    const auto num_odd_comps = std::get<2>(parity_info);\n\n    const size_t required_buffer_size =\n        (num_even_comps + num_odd_comps) * num_grid_points;\n    ASSERT(buffer1->size() >= required_buffer_size,\n           \"Buffer1 size (\" << buffer1->size()\n                            << \") is insufficient for ZernikeB1 requirements (\"\n                            << required_buffer_size << \")\");\n    ASSERT(buffer2->size() >= required_buffer_size,\n           \"Buffer2 size (\" << buffer2->size()\n                            << \") is insufficient for ZernikeB1 requirements (\"\n                            << required_buffer_size << \")\");\n\n    using DataType = std::conditional_t<std::is_same_v<ValueType, double>,\n                                        DataVector, ComplexDataVector>;\n    DataType even_components{};\n    even_components.set_data_ref(buffer1->data(),\n                                 num_even_comps * num_grid_points);\n    DataType odd_components{};\n    odd_components.set_data_ref(\n        buffer1->data() + num_even_comps * num_grid_points,\n        num_odd_comps * num_grid_points);\n    ValueType* p_even_components = even_components.data();\n    ValueType* p_odd_components = odd_components.data();\n\n    DataType even_deriv{};\n    even_deriv.set_data_ref(buffer2->data(), num_even_comps * num_grid_points);\n    DataType odd_deriv{};\n    odd_deriv.set_data_ref(buffer2->data() + num_even_comps * num_grid_points,\n                           num_odd_comps * num_grid_points);\n    ValueType* p_even_deriv = even_deriv.data();\n    ValueType* p_odd_deriv = odd_deriv.data();\n\n    for (const auto seg_size : parity_list) {\n      // seg_size is the number of contiguous components with the same parity\n      // (based on the bool `even`). The pattern alternates even/odd, so if the\n      // first element is odd, the list has a leading 0. However, after the\n      // potential leading 0, any other zero in the list indicates all\n      // components have been handled and all further values are guaranteed to\n      // be zero (the list has its size set by the worst-case scenario). If\n      // there are any neighboring components with the same parity (so not\n      // worst-case size), the list will have the back padded with zeros, which\n      // we don't need to loop over\n      // See Spectral::compute_parity_list() for more info\n      if (seg_size == 0) {\n        if (even) {\n          // Will have leading zero if first element is odd, so check next value\n          even = false;\n          continue;\n        } else {\n          // Iterated through all non-zero values\n          break;\n        }\n      }\n      if (even) {\n        std::copy(p_data, p_data + seg_size * num_grid_points,\n                  p_even_components);\n        p_even_components += seg_size * num_grid_points;\n      } else {\n        std::copy(p_data, p_data + seg_size * num_grid_points,\n                  p_odd_components);\n        p_odd_components += seg_size * num_grid_points;\n      }\n      p_data += seg_size * num_grid_points;  // NOLINT\n      even = not even;\n    }\n    apply_matrix_in_first_dim(p_even_deriv, even_components.data(),\n                              even_diff_matrix,\n                              num_even_comps * num_grid_points);\n    apply_matrix_in_first_dim(p_odd_deriv, odd_components.data(),\n                              odd_diff_matrix, num_odd_comps * num_grid_points);\n    even = true;\n    for (const auto seg_size : parity_list) {\n      if (seg_size == 0) {\n        if (even) {\n          // Will have leading zero if first element is odd\n          even = not even;\n          continue;\n        } else {\n          // Iterated through all non-zero values\n          break;\n        }\n      }\n      // Now copy the data from the buffers to the correct location in\n      // p_logical_derivs\n      if (even) {\n        std::copy(p_even_deriv, p_even_deriv + seg_size * num_grid_points,\n                  p_logical_derivs);\n        p_even_deriv += seg_size * num_grid_points;\n      } else {\n        std::copy(p_odd_deriv, p_odd_deriv + seg_size * num_grid_points,\n                  p_logical_derivs);\n        p_odd_deriv += seg_size * num_grid_points;\n      }\n      p_logical_derivs += seg_size * num_grid_points;  // NOLINT\n      even = not even;\n    }\n  }\n\n  template <typename ValueType>\n  static void zernikeb1_apply(\n      const gsl::not_null<ValueType*> logical_partial_derivative_of_u,\n      const Variables<VariableTags>& u, const size_t num_grid_points,\n      const size_t extents_0) {\n    constexpr auto parity_info =\n        Spectral::compute_parity_list<DerivativeTags>();\n    const auto num_comps = std::get<1>(parity_info) + std::get<2>(parity_info);\n\n    // Allocate buffers locally for this overload (use 2 buffers to match the\n    // 2 buffers used in the LogicalImpl<2> call)\n    const auto logical_derivs_data =\n        cpp20::make_unique_for_overwrite<ValueType[]>(2 * num_comps *\n                                                      num_grid_points);\n    Variables<DerivativeTags> buffer1{};\n    buffer1.set_data_ref(&logical_derivs_data[0], num_comps * num_grid_points);\n    Variables<DerivativeTags> buffer2{};\n    buffer2.set_data_ref(&logical_derivs_data[num_comps * num_grid_points],\n                         num_comps * num_grid_points);\n\n    zernikeb1_apply(logical_partial_derivative_of_u, u, num_grid_points,\n                    extents_0, &buffer1, &buffer2);\n  }\n};\n\ntemplate <typename VariableTags, typename DerivativeTags>\nstruct LogicalImpl<2, VariableTags, DerivativeTags> {\n  static constexpr size_t Dim = 2;\n  template <typename T, typename ValueType = typename Variables<T>::value_type>\n  static void apply(\n      const gsl::not_null<std::array<ValueType*, Dim>*> logical_du,\n      Variables<T>* const partial_u_wrt_eta,\n      Variables<DerivativeTags>* const u_eta_fastest,\n      const Variables<VariableTags>& u, const Mesh<2>& mesh) {\n    static_assert(\n        Variables<DerivativeTags>::number_of_independent_components <=\n            Variables<T>::number_of_independent_components,\n        \"Temporary buffer in logical partial derivatives is too small\");\n    if (mesh.basis(0) == Spectral::Basis::ZernikeB2) {\n      if constexpr (std::is_same_v<ValueType, double>) {\n        disk_apply(logical_du, partial_u_wrt_eta, u_eta_fastest, u, mesh);\n      } else {\n        ERROR(\n            \"Support for complex numbers with a disk is not yet \"\n            \"implemented for logical_partial_derivative.\");\n      }\n    } else {\n      auto& logical_partial_derivatives_of_u = *logical_du;\n      const size_t deriv_size =\n          Variables<DerivativeTags>::number_of_independent_components *\n          u.number_of_grid_points();\n      if (UNLIKELY(mesh.basis(0) == Spectral::Basis::ZernikeB1)) {\n        if constexpr (std::is_same_v<ValueType, double>) {\n          LogicalImpl<1, VariableTags, DerivativeTags>::zernikeb1_apply(\n              make_not_null(logical_partial_derivatives_of_u[0]), u,\n              mesh.number_of_grid_points(), mesh.extents(0), partial_u_wrt_eta,\n              u_eta_fastest);\n        } else {\n          ERROR(\n              \"Support for complex numbers with ZernikeB1 is not yet \"\n              \"implemented \"\n              \"for logical_partial_derivative.\");\n        }\n      } else {\n        const Matrix& differentiation_matrix_xi =\n            Spectral::differentiation_matrix(mesh.slice_through(0));\n        apply_matrix_in_first_dim(logical_partial_derivatives_of_u[0], u.data(),\n                                  differentiation_matrix_xi, deriv_size);\n      }\n      const size_t num_components_times_xi_slices =\n          deriv_size / mesh.extents(0);\n      transpose<Variables<VariableTags>, Variables<DerivativeTags>>(\n          make_not_null(u_eta_fastest), u, mesh.extents(0),\n          num_components_times_xi_slices);\n      const Matrix& differentiation_matrix_eta =\n          Spectral::differentiation_matrix(mesh.slice_through(1));\n      apply_matrix_in_first_dim(partial_u_wrt_eta->data(),\n                                u_eta_fastest->data(),\n                                differentiation_matrix_eta, deriv_size);\n      raw_transpose(make_not_null(logical_partial_derivatives_of_u[1]),\n                    partial_u_wrt_eta->data(), num_components_times_xi_slices,\n                    mesh.extents(0));\n    }\n  }\n\n  template <typename T, typename ValueType = typename Variables<T>::value_type>\n  static void disk_apply(\n      const gsl::not_null<std::array<ValueType*, Dim>*> logical_du,\n      Variables<T>* const partial_u_wrt_eta,\n      Variables<DerivativeTags>* const u_eta_fastest,\n      const Variables<VariableTags>& u, const Mesh<2>& mesh) {\n    ASSERT(mesh.basis(0) == Spectral::Basis::ZernikeB2 and\n               mesh.basis(1) == Spectral::Basis::ZernikeB2,\n           \"Unexpected basis combination: \" << mesh.basis());\n    const auto [n_r, n_phi] = mesh.extents().indices();\n    const size_t n_r_max = 2 * n_r - 2;\n    ASSERT(n_phi % 2 == 1,\n           \"Fourier with an even number of grid points can be unstable due to \"\n           \"the top derivative not being representable\");\n    ASSERT(n_phi / 2 <= n_r_max,\n           \"Zernike & Fourier on a disk have angular resolution limited by \"\n           \"extents in both dimensions. We choose to enforce the restriction \"\n           \"that the Fourier modal space is not larger than the Zernike \"\n           \"angular capabilities (which would waste space and be physically \"\n           \"ill-motivated).\\nn_phi / 2 = \"\n               << n_phi / 2 << \", Maximum from Zernike = \" << n_r_max);\n\n    const Matrix& diff_matrix_r_even =\n        Spectral::differentiation_matrix<Spectral::Basis::ZernikeB2,\n                                         Spectral::Quadrature::GaussRadauUpper>(\n            n_r, Spectral::Parity::Even);\n    const Matrix& diff_matrix_r_odd =\n        Spectral::differentiation_matrix<Spectral::Basis::ZernikeB2,\n                                         Spectral::Quadrature::GaussRadauUpper>(\n            n_r, Spectral::Parity::Odd);\n    const Matrix& diff_matrix_phi = Spectral::differentiation_matrix<\n        Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n\n    const Matrix& nodal_to_modal_phi = Spectral::nodal_to_modal_matrix<\n        Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n    const Matrix& modal_to_nodal_phi = Spectral::modal_to_nodal_matrix<\n        Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n\n    const size_t local_buffer_size =\n        Variables<DerivativeTags>::number_of_independent_components *\n        u.number_of_grid_points();\n    // These just point to the passed buffers but it gets confusing to read when\n    // they are used for radial purposes that don't match their names\n    Variables<DerivativeTags> local_buffer1{};\n    Variables<DerivativeTags> local_buffer2{};\n    local_buffer1.set_data_ref(partial_u_wrt_eta->data(), local_buffer_size);\n    local_buffer2.set_data_ref(u_eta_fastest->data(), local_buffer_size);\n\n    const size_t deriv_size =\n        Variables<DerivativeTags>::number_of_independent_components *\n        u.number_of_grid_points();\n    const size_t num_components_times_xi_slices = deriv_size / mesh.extents(0);\n\n    // phi derivative\n    transpose<Variables<VariableTags>, Variables<DerivativeTags>>(\n        make_not_null(u_eta_fastest), u, mesh.extents(0),\n        num_components_times_xi_slices);\n    apply_matrix_in_first_dim(partial_u_wrt_eta->data(), u_eta_fastest->data(),\n                              diff_matrix_phi, deriv_size);\n    raw_transpose(make_not_null((*logical_du)[1]), partial_u_wrt_eta->data(),\n                  num_components_times_xi_slices, mesh.extents(0));\n\n    // There are a few ways to achieve the task of applying the appropriate even\n    // or odd radial differentiation matrices.\n    // Testing showed that manual looping to apply the matrices on contiguous\n    // even/odd subsets was moderately slower than reworking the data to only\n    // apply 2 BLAS calls on the full even/odd subsets.\n    // One can rework the data two ways to get the subsets:\n    //  (1) copy the even and odd columns so the different components are split\n    // in two different buffers\n    //  (2) copy the entire data and then mask with zeros where appropriate.\n    // Option (1) uses less space for the BLAS calls as they act on truly dense\n    // matrices, whereas option (2) maintains the structure of the columns by\n    // adding zeros. Option (1) uses more copies but smaller BLAS operations,\n    // which testing showed to be slightly faster.\n\n    // reuse transposed data to go to angular spectral space\n    apply_matrix_in_first_dim((*logical_du)[0], u_eta_fastest->data(),\n                              nodal_to_modal_phi, deriv_size);\n    // (*logical_du)[0] holds transposed angular modal rep\n\n    raw_transpose(make_not_null(local_buffer1.data()), (*logical_du)[0],\n                  num_components_times_xi_slices, mesh.extents(0));\n    // local_buffer 1 holds angular modal rep\n\n    // Copy even/odd subsets to compact regions\n    const size_t n_even_cols =\n        (1 + 2 * ((n_phi - 1) / 4)) *\n        Variables<DerivativeTags>::number_of_independent_components;\n    const size_t n_odd_cols =\n        (2 * ((n_phi + 1) / 4)) *\n        Variables<DerivativeTags>::number_of_independent_components;\n\n    // Use local_buffer2 for initial compact storage\n    const size_t even_size = n_even_cols * n_r;\n    const size_t odd_size = n_odd_cols * n_r;\n    ValueType* even_data = local_buffer2.data();\n    ValueType* odd_data = local_buffer2.data() + even_size;\n\n    size_t even_col = 0;\n    size_t odd_col = 0;\n    for (size_t i = 0;\n         i <\n         Variables<DerivativeTags>::number_of_independent_components * n_phi;\n         ++i) {\n      if (i % n_phi == 0) {\n        // m = 0 (even) - copy single column\n        std::copy(local_buffer1.data() + i * n_r,\n                  local_buffer1.data() + (i + 1) * n_r,\n                  even_data + even_col * n_r);\n        ++even_col;\n      } else if ((i % n_phi - 1) / 2 % 2 == 1) {\n        // Even m with cos/sin pair - copy two columns\n        std::copy(local_buffer1.data() + i * n_r,\n                  local_buffer1.data() + (i + 2) * n_r,\n                  even_data + even_col * n_r);\n        even_col += 2;\n        ++i;\n      } else {\n        // Odd m with cos/sin pair - copy two columns\n        std::copy(local_buffer1.data() + i * n_r,\n                  local_buffer1.data() + (i + 2) * n_r,\n                  odd_data + odd_col * n_r);\n        odd_col += 2;\n        ++i;\n      }\n    }\n\n    // Use local_buffer1 for differentiated compact storage\n    ValueType* even_result = local_buffer1.data();\n    ValueType* odd_result = local_buffer1.data() + even_size;\n\n    apply_matrix_in_first_dim(even_result, even_data, diff_matrix_r_even,\n                              even_size, false);\n\n    // Copy even derivative components back to interleaved regions\n    even_col = 0;\n    for (size_t i = 0;\n         i <\n         Variables<DerivativeTags>::number_of_independent_components * n_phi;\n         ++i) {\n      if (i % n_phi == 0) {\n        // m = 0 (even) - copy single column back\n        std::copy(even_result + even_col * n_r,\n                  even_result + (even_col + 1) * n_r,\n                  (*logical_du)[0] + i * n_r);\n        ++even_col;\n      } else if ((i % n_phi - 1) / 2 % 2 == 1) {\n        // Even m with cos/sin pair - copy two columns back\n        std::copy(even_result + even_col * n_r,\n                  even_result + (even_col + 2) * n_r,\n                  (*logical_du)[0] + i * n_r);\n        even_col += 2;\n        ++i;\n      } else {\n        ++i;\n      }\n    }\n\n    apply_matrix_in_first_dim(odd_result, odd_data, diff_matrix_r_odd, odd_size,\n                              false);\n\n    // Copy odd derivative components back to interleaved regions\n    odd_col = 0;\n    for (size_t i = 0;\n         i <\n         Variables<DerivativeTags>::number_of_independent_components * n_phi;\n         ++i) {\n      if (i % n_phi == 0) {\n        // m = 0 (even) - skip\n      } else if ((i % n_phi - 1) / 2 % 2 == 1) {\n        // Even m - skip\n        ++i;\n      } else {\n        // Odd m with cos/sin pair - copy both columns\n        std::copy(odd_result + odd_col * n_r, odd_result + (odd_col + 2) * n_r,\n                  (*logical_du)[0] + i * n_r);\n        odd_col += 2;\n        ++i;\n      }\n    }\n    // (*logical_du)[0] holds the radial derivative in angular modal rep\n\n    raw_transpose(make_not_null(local_buffer1.data()), (*logical_du)[0],\n                  mesh.extents(0), num_components_times_xi_slices);\n    // local_buffer1 holds transposed radial derivative in angular modal rep\n\n    apply_matrix_in_first_dim(local_buffer2.data(), local_buffer1.data(),\n                              modal_to_nodal_phi, deriv_size);\n    // local_buffer2 holds transposed radial derivative in angular nodal rep\n\n    raw_transpose(make_not_null((*logical_du)[0]), local_buffer2.data(),\n                  num_components_times_xi_slices, mesh.extents(0));\n  }\n};\n\ntemplate <typename VariableTags, typename DerivativeTags>\nstruct LogicalImpl<3, VariableTags, DerivativeTags> {\n  static constexpr size_t Dim = 3;\n  template <class T, typename ValueType = typename Variables<T>::value_type>\n  static void apply(\n      const gsl::not_null<std::array<ValueType*, Dim>*> logical_du,\n      Variables<T>* const partial_u_wrt_eta_or_zeta,\n      Variables<DerivativeTags>* const u_eta_or_zeta_fastest,\n      const Variables<VariableTags>& u, const Mesh<3>& mesh) {\n    if (mesh.basis(1) == Spectral::Basis::SphericalHarmonic) {\n      if constexpr (std::is_same_v<ValueType, double>) {\n        spherical_apply(logical_du, u, mesh);\n      } else {\n        ERROR(\n            \"Support for complex numbers with spherical harmonics is not yet \"\n            \"implemented for logical_partial_derivative.\");\n      }\n    } else if (mesh.basis(0) == Spectral::Basis::ZernikeB2) {\n      if constexpr (std::is_same_v<ValueType, double>) {\n        cylinder_apply(logical_du, partial_u_wrt_eta_or_zeta,\n                       u_eta_or_zeta_fastest, u, mesh);\n      } else {\n        ERROR(\n            \"Support for complex numbers with the cylinder is not yet \"\n            \"implemented for logical_partial_derivative.\");\n      }\n    } else {\n      static_assert(\n          Variables<DerivativeTags>::number_of_independent_components <=\n              Variables<T>::number_of_independent_components,\n          \"Temporary buffer in logical partial derivatives is too small\");\n      auto& logical_partial_derivatives_of_u = *logical_du;\n      const Matrix& differentiation_matrix_xi =\n          Spectral::differentiation_matrix(mesh.slice_through(0));\n      const size_t deriv_size =\n          Variables<DerivativeTags>::number_of_independent_components *\n          u.number_of_grid_points();\n      const size_t num_components_times_xi_slices =\n          deriv_size / mesh.extents(0);\n      apply_matrix_in_first_dim(logical_partial_derivatives_of_u[0], u.data(),\n                                differentiation_matrix_xi, deriv_size);\n      transpose<Variables<VariableTags>, Variables<DerivativeTags>>(\n          make_not_null(u_eta_or_zeta_fastest), u, mesh.extents(0),\n          num_components_times_xi_slices);\n      const Matrix& differentiation_matrix_eta =\n          Spectral::differentiation_matrix(mesh.slice_through(1));\n      apply_matrix_in_first_dim(partial_u_wrt_eta_or_zeta->data(),\n                                u_eta_or_zeta_fastest->data(),\n                                differentiation_matrix_eta, deriv_size);\n      raw_transpose(make_not_null(logical_partial_derivatives_of_u[1]),\n                    partial_u_wrt_eta_or_zeta->data(),\n                    num_components_times_xi_slices, mesh.extents(0));\n\n      const size_t chunk_size = mesh.extents(0) * mesh.extents(1);\n      const size_t number_of_chunks = deriv_size / chunk_size;\n      transpose(make_not_null(u_eta_or_zeta_fastest), u, chunk_size,\n                number_of_chunks);\n      const Matrix& differentiation_matrix_zeta =\n          Spectral::differentiation_matrix(mesh.slice_through(2));\n      apply_matrix_in_first_dim(partial_u_wrt_eta_or_zeta->data(),\n                                u_eta_or_zeta_fastest->data(),\n                                differentiation_matrix_zeta, deriv_size);\n      raw_transpose(make_not_null(logical_partial_derivatives_of_u[2]),\n                    partial_u_wrt_eta_or_zeta->data(), number_of_chunks,\n                    chunk_size);\n    }\n  }\n\n  static void spherical_apply(\n      const gsl::not_null<std::array<double*, Dim>*> logical_du,\n      const Variables<VariableTags>& u, const Mesh<3>& mesh) {\n    auto& logical_partial_derivatives_of_u = *logical_du;\n    const Matrix& differentiation_matrix_xi =\n        Spectral::differentiation_matrix(mesh.slice_through(0));\n    const size_t deriv_size =\n        Variables<DerivativeTags>::number_of_independent_components *\n        u.number_of_grid_points();\n    apply_matrix_in_first_dim(logical_partial_derivatives_of_u[0], u.data(),\n                              differentiation_matrix_xi, deriv_size);\n    const auto& ylm = ylm::get_spherepack_cache(mesh.extents(1) - 1);\n    for (size_t n = 0;\n         n < Variables<DerivativeTags>::number_of_independent_components; ++n) {\n      const size_t offset = n * u.number_of_grid_points();\n      const auto du = std::array{logical_partial_derivatives_of_u[1] + offset,\n                                 logical_partial_derivatives_of_u[2] + offset};\n      ylm.gradient_all_offsets(du, make_not_null(u.data() + offset),\n                               mesh.extents(0));\n    }\n  }\n\n  template <typename T, typename ValueType = typename Variables<T>::value_type>\n  static void cylinder_apply(\n      const gsl::not_null<std::array<ValueType*, Dim>*> logical_du,\n      Variables<T>* const partial_u_wrt_eta_or_zeta,\n      Variables<DerivativeTags>* const u_eta_or_zeta_fastest,\n      const Variables<VariableTags>& u, const Mesh<3>& mesh) {\n    ASSERT(mesh.basis(0) == Spectral::Basis::ZernikeB2 and\n               mesh.basis(1) == Spectral::Basis::ZernikeB2,\n           \"Unexpected basis combination: \" << mesh.basis());\n    const auto [n_r, n_phi, n_zeta] = mesh.extents().indices();\n    ASSERT(n_phi % 2 == 1,\n           \"Fourier with an even number of grid points can be unstable due to \"\n           \"the top derivative not being representable\");\n    ASSERT(n_phi / 2 <= 2 * n_r - 2,\n           \"Zernike & Fourier on a disk have angular resolution limited by \"\n           \"extents in both dimensions. We choose to enforce the restriction \"\n           \"that the Fourier modal space is not larger than the Zernike \"\n           \"angular capabilities (which would waste space and be physically \"\n           \"ill-motivated).\\nn_phi / 2 = \"\n               << n_phi / 2 << \", Maximum from Zernike = \" << 2 * n_r - 2);\n\n    const Matrix& diff_matrix_r_even =\n        Spectral::differentiation_matrix<Spectral::Basis::ZernikeB2,\n                                         Spectral::Quadrature::GaussRadauUpper>(\n            n_r, Spectral::Parity::Even);\n    const Matrix& diff_matrix_r_odd =\n        Spectral::differentiation_matrix<Spectral::Basis::ZernikeB2,\n                                         Spectral::Quadrature::GaussRadauUpper>(\n            n_r, Spectral::Parity::Odd);\n    const Matrix& diff_matrix_phi = Spectral::differentiation_matrix<\n        Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n    const Matrix& differentiation_matrix_zeta =\n        Spectral::differentiation_matrix(mesh.slice_through(2));\n\n    const Matrix& nodal_to_modal_phi = Spectral::nodal_to_modal_matrix<\n        Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n    const Matrix& modal_to_nodal_phi = Spectral::modal_to_nodal_matrix<\n        Spectral::Basis::Fourier, Spectral::Quadrature::Equiangular>(n_phi);\n\n    const size_t local_buffer_size =\n        Variables<DerivativeTags>::number_of_independent_components *\n        u.number_of_grid_points();\n    // These just point to the passed buffers but it gets confusing to read when\n    // they are used for radial purposes that don't match their names\n    Variables<DerivativeTags> local_buffer1{};\n    Variables<DerivativeTags> local_buffer2{};\n    local_buffer1.set_data_ref(partial_u_wrt_eta_or_zeta->data(),\n                               local_buffer_size);\n    local_buffer2.set_data_ref(u_eta_or_zeta_fastest->data(),\n                               local_buffer_size);\n\n    const size_t deriv_size =\n        Variables<DerivativeTags>::number_of_independent_components *\n        u.number_of_grid_points();\n    const size_t num_components_times_xi_slices = deriv_size / mesh.extents(0);\n\n    // phi derivative\n    transpose<Variables<VariableTags>, Variables<DerivativeTags>>(\n        make_not_null(u_eta_or_zeta_fastest), u, mesh.extents(0),\n        num_components_times_xi_slices);\n    apply_matrix_in_first_dim(partial_u_wrt_eta_or_zeta->data(),\n                              u_eta_or_zeta_fastest->data(), diff_matrix_phi,\n                              deriv_size);\n    raw_transpose(make_not_null((*logical_du)[1]),\n                  partial_u_wrt_eta_or_zeta->data(),\n                  num_components_times_xi_slices, mesh.extents(0));\n\n    // See comments in disk_apply() for why the following choices were made\n\n    // reuse transposed data to go to angular spectral space\n    apply_matrix_in_first_dim((*logical_du)[0], u_eta_or_zeta_fastest->data(),\n                              nodal_to_modal_phi, deriv_size);\n    // (*logical_du)[0] holds transposed angular modal rep\n\n    raw_transpose(make_not_null(local_buffer1.data()), (*logical_du)[0],\n                  num_components_times_xi_slices, mesh.extents(0));\n    std::copy(local_buffer1.data(), local_buffer1.data() + local_buffer_size,\n              local_buffer2.data());\n    // local_buffer 1 and local_buffer2 hold angular modal rep\n\n    // Copy even/odd subsets to compact regions for 3D cylinder\n    const size_t n_even_cols =\n        (1 + 2 * ((n_phi - 1) / 4)) *\n        Variables<DerivativeTags>::number_of_independent_components;\n    const size_t n_odd_cols =\n        (2 * ((n_phi + 1) / 4)) *\n        Variables<DerivativeTags>::number_of_independent_components;\n\n    // Use local_buffer2 for compact storage\n    const size_t total_even_size = n_even_cols * n_r * n_zeta;\n    const size_t total_odd_size = n_odd_cols * n_r * n_zeta;\n\n    ValueType* even_data = local_buffer2.data();\n    ValueType* odd_data = local_buffer2.data() + total_even_size;\n\n    size_t even_col = 0;\n    size_t odd_col = 0;\n    for (size_t i = 0;\n         i < Variables<DerivativeTags>::number_of_independent_components *\n                 n_phi * n_zeta;\n         ++i) {\n      if (i % n_phi == 0) {\n        // m = 0 (even) - copy single column\n        std::copy(local_buffer1.data() + i * n_r,\n                  local_buffer1.data() + (i + 1) * n_r,\n                  even_data + even_col * n_r);\n        ++even_col;\n      } else if ((i % n_phi - 1) / 2 % 2 == 1) {\n        // Even m with cos/sin pair - copy two columns\n        std::copy(local_buffer1.data() + i * n_r,\n                  local_buffer1.data() + (i + 2) * n_r,\n                  even_data + even_col * n_r);\n        even_col += 2;\n        ++i;\n      } else {\n        // Odd m with cos/sin pair - copy two columns\n        std::copy(local_buffer1.data() + i * n_r,\n                  local_buffer1.data() + (i + 2) * n_r,\n                  odd_data + odd_col * n_r);\n        odd_col += 2;\n        ++i;\n      }\n    }\n\n    // Use local_buffer1 for temporary storage since it's no longer needed\n    ValueType* even_result = local_buffer1.data();\n    ValueType* odd_result = local_buffer1.data() + total_even_size;\n\n    apply_matrix_in_first_dim(even_result, even_data, diff_matrix_r_even,\n                              total_even_size, false);\n\n    // Copy even derivative components back to interleaved regions\n    even_col = 0;\n    for (size_t i = 0;\n         i < Variables<DerivativeTags>::number_of_independent_components *\n                 n_phi * n_zeta;\n         ++i) {\n      if (i % n_phi == 0) {\n        // m = 0 (even) - copy single column back\n        std::copy(even_result + even_col * n_r,\n                  even_result + (even_col + 1) * n_r,\n                  (*logical_du)[0] + i * n_r);\n        ++even_col;\n      } else if ((i % n_phi - 1) / 2 % 2 == 1) {\n        // Even m with cos/sin pair - copy two columns back\n        std::copy(even_result + even_col * n_r,\n                  even_result + (even_col + 2) * n_r,\n                  (*logical_du)[0] + i * n_r);\n        even_col += 2;\n        ++i;\n      } else {\n        ++i;\n      }\n    }\n\n    apply_matrix_in_first_dim(odd_result, odd_data, diff_matrix_r_odd,\n                              total_odd_size, false);\n\n    // Copy odd derivative components back to interleaved regions\n    odd_col = 0;\n    for (size_t i = 0;\n         i < Variables<DerivativeTags>::number_of_independent_components *\n                 n_phi * n_zeta;\n         ++i) {\n      if (i % n_phi == 0) {\n        // m = 0 (even) - skip\n      } else if ((i % n_phi - 1) / 2 % 2 == 1) {\n        // Even m - skip\n        ++i;\n      } else {\n        // Odd m with cos/sin pair - copy both columns\n        std::copy(odd_result + odd_col * n_r, odd_result + (odd_col + 2) * n_r,\n                  (*logical_du)[0] + i * n_r);\n        odd_col += 2;\n        ++i;\n      }\n    }\n    // (*logical_du)[0] holds the radial derivative in angular modal rep\n\n    raw_transpose(make_not_null(local_buffer1.data()), (*logical_du)[0],\n                  mesh.extents(0), num_components_times_xi_slices);\n    // local_buffer1 holds transposed radial derivative in angular modal rep\n\n    apply_matrix_in_first_dim(local_buffer2.data(), local_buffer1.data(),\n                              modal_to_nodal_phi, deriv_size);\n    // local_buffer2 holds transposed radial derivative in angular nodal rep\n\n    raw_transpose(make_not_null((*logical_du)[0]), local_buffer2.data(),\n                  num_components_times_xi_slices, mesh.extents(0));\n\n    // zeta derivative\n    const size_t chunk_size = mesh.extents(0) * mesh.extents(1);\n    const size_t number_of_chunks = deriv_size / chunk_size;\n    transpose(make_not_null(u_eta_or_zeta_fastest), u, chunk_size,\n              number_of_chunks);\n    apply_matrix_in_first_dim(partial_u_wrt_eta_or_zeta->data(),\n                              u_eta_or_zeta_fastest->data(),\n                              differentiation_matrix_zeta, deriv_size);\n    raw_transpose(make_not_null((*logical_du)[2]),\n                  partial_u_wrt_eta_or_zeta->data(), number_of_chunks,\n                  chunk_size);\n  }\n};\n}  // namespace partial_derivatives_detail\n\n// Macro to explicitly instantiate partial_derivatives()\n// for a given system of equations\n#define INSTANTIATE_PARTIAL_DERIVATIVES_WITH_SYSTEM(SYSTEM, DIM,             \\\n                                                    DERIVATIVE_FRAME)        \\\n  template void partial_derivatives(                                         \\\n      gsl::not_null<Variables<                                               \\\n          db::wrap_tags_in<Tags::deriv, typename SYSTEM::gradient_variables, \\\n                           tmpl::size_t<DIM>, DERIVATIVE_FRAME>>*>           \\\n          du,                                                                \\\n      const std::array<Variables<typename SYSTEM::gradient_variables>, DIM>& \\\n          logical_partial_derivatives_of_u,                                  \\\n      const InverseJacobian<DataVector, DIM, Frame::ElementLogical,          \\\n                            DERIVATIVE_FRAME>& inverse_jacobian);            \\\n  template void partial_derivatives(                                         \\\n      gsl::not_null<Variables<                                               \\\n          db::wrap_tags_in<Tags::deriv, typename SYSTEM::gradient_variables, \\\n                           tmpl::size_t<DIM>, DERIVATIVE_FRAME>>*>           \\\n          du,                                                                \\\n      const Variables<typename SYSTEM::gradient_variables>& u,               \\\n      const Mesh<DIM>& mesh,                                                 \\\n      const InverseJacobian<DataVector, DIM, Frame::ElementLogical,          \\\n                            DERIVATIVE_FRAME>& inverse_jacobian);            \\\n  template Variables<                                                        \\\n      db::wrap_tags_in<Tags::deriv, typename SYSTEM::gradient_variables,     \\\n                       tmpl::size_t<DIM>, DERIVATIVE_FRAME>>                 \\\n  partial_derivatives<typename SYSTEM::gradient_variables>(                  \\\n      const Variables<typename SYSTEM::gradient_variables>& u,               \\\n      const Mesh<DIM>& mesh,                                                 \\\n      const InverseJacobian<DataVector, DIM, Frame::ElementLogical,          \\\n                            DERIVATIVE_FRAME>& inverse_jacobian);\n\n// Macro to explicitly instantiate cartoon_partial_derivatives()\n// for a given system of equations (distinct from non-cartoon call)\n#define INSTANTIATE_CARTOON_PARTIAL_DERIVATIVES_WITH_SYSTEM(SYSTEM, DIM,      \\\n                                                            DERIVATIVE_FRAME) \\\n  template void partial_derivatives(                                          \\\n      gsl::not_null<Variables<                                                \\\n          db::wrap_tags_in<Tags::deriv, typename SYSTEM::gradient_variables,  \\\n                           tmpl::size_t<DIM>, DERIVATIVE_FRAME>>*>            \\\n          du,                                                                 \\\n      const Variables<typename SYSTEM::gradient_variables>& u,                \\\n      const Mesh<DIM>& mesh,                                                  \\\n      const InverseJacobian<DataVector, DIM, Frame::ElementLogical,           \\\n                            DERIVATIVE_FRAME>& inverse_jacobian_3d,           \\\n      const tnsr::I<DataVector, DIM, Frame::Inertial>& inertial_coords);\n\n// This instantiates the chooser and underlying cartoon_partial_derivative()\n#define INSTANTIATE_CARTOON_PARTIAL_DERIVATIVE(DIM, DERIVATIVE_FRAME, TENSOR) \\\n  template void partial_derivative(                                           \\\n      const gsl::not_null<TensorMetafunctions::prepend_spatial_index<         \\\n          TENSOR<DataVector, DIM, DERIVATIVE_FRAME>, DIM, UpLo::Lo,           \\\n          DERIVATIVE_FRAME>*>                                                 \\\n          du,                                                                 \\\n      const TENSOR<DataVector, DIM, DERIVATIVE_FRAME>& u,                     \\\n      const Mesh<DIM>& mesh,                                                  \\\n      const InverseJacobian<DataVector, DIM, Frame::ElementLogical,           \\\n                            DERIVATIVE_FRAME>& inverse_jacobian,              \\\n      const tnsr::I<DataVector, DIM, Frame::Inertial>& inertial_coords);      \\\n  template TensorMetafunctions::prepend_spatial_index<                        \\\n      TENSOR<DataVector, DIM, DERIVATIVE_FRAME>, DIM, UpLo::Lo,               \\\n      DERIVATIVE_FRAME>                                                       \\\n  partial_derivative(                                                         \\\n      const TENSOR<DataVector, DIM, DERIVATIVE_FRAME>& u,                     \\\n      const Mesh<DIM>& mesh,                                                  \\\n      const InverseJacobian<DataVector, DIM, Frame::ElementLogical,           \\\n                            DERIVATIVE_FRAME>& inverse_jacobian,              \\\n      const tnsr::I<DataVector, DIM, Frame::Inertial>& inertial_coords);\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/PowerMonitors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/PowerMonitors.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <limits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearRegression.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace PowerMonitors {\n\ntemplate <typename VectorType, size_t Dim>\nvoid power_monitors(const gsl::not_null<std::array<DataVector, Dim>*> result,\n                    const VectorType& u, const Mesh<Dim>& mesh) {\n  ASSERT(u.size() == mesh.number_of_grid_points(),\n         \"The number of grid points per element (\"\n             << mesh.number_of_grid_points()\n             << \") must match the size of the \"\n                \"vector (\"\n             << u.size() << \").\");\n  // Get modal coefficients\n  const auto modal_coefficients = to_modal_coefficients(u, mesh);\n\n  double slice_sum = 0.0;\n  size_t n_slice = 0;\n  size_t n_stripe = 0;\n  for (size_t sliced_dim = 0; sliced_dim < Dim; ++sliced_dim) {\n    n_slice = mesh.extents().slice_away(sliced_dim).product();\n    n_stripe = mesh.extents(sliced_dim);\n\n    gsl::at(*result, sliced_dim).destructive_resize(n_stripe);\n\n    for (size_t index = 0; index < n_stripe; ++index) {\n      slice_sum = 0.0;\n      for (SliceIterator si(mesh.extents(), sliced_dim, index); si; ++si) {\n        const auto& mode = modal_coefficients[si.volume_offset()];\n        if constexpr (std::is_same_v<VectorType, ComplexDataVector>) {\n          slice_sum += square(abs(mode));\n        } else {\n          slice_sum += square(mode);\n        }\n      }\n      slice_sum /= static_cast<double>(n_slice);\n      slice_sum = sqrt(slice_sum);\n\n      gsl::at(*result, sliced_dim)[index] = slice_sum;\n    }\n  }\n}\n\ntemplate <typename VectorType, size_t Dim>\nstd::array<DataVector, Dim> power_monitors(const VectorType& u,\n                                           const Mesh<Dim>& mesh) {\n  std::array<DataVector, Dim> result{};\n  power_monitors(make_not_null(&result), u, mesh);\n  return result;\n}\n\n// The power_monitor argument should be made of type ModalVector\n// when pybindings for ModalVector are enabled\ndouble relative_truncation_error(const DataVector& power_monitor,\n                                 const size_t num_modes_to_use) {\n  ASSERT(\n      num_modes_to_use <= power_monitor.size(),\n      \"Number of modes needs less or equal than the number of power monitors\");\n  ASSERT(2_st <= num_modes_to_use,\n         \"Number of modes needs to be larger or equal than 2.\");\n  const size_t last_index = num_modes_to_use - 1;\n  const double max_mode = blaze::max(power_monitor);\n  const double cutoff =\n      100. * std::numeric_limits<double>::epsilon() * max_mode;\n  // If the last two or more modes are zero, assume that the function is\n  // represented exactly and return a relative truncation error of zero.\n  // Just one zero mode is not enough to make this assumption, as the function\n  // could have zero modes by symmetry.\n  if (num_modes_to_use >= 2 and power_monitor[last_index] < cutoff and\n      power_monitor[last_index - 1] < cutoff) {\n    return cutoff * 1.e-2;\n  }\n  // Compute weighted average and total sum in the current dimension\n  double weighted_average = 0.0;\n  double weight_sum = 0.0;\n  double weight_value = 0.0;\n  for (size_t index = 0; index <= last_index; ++index) {\n    const double mode = power_monitor[index];\n    if (mode < cutoff) {\n      // Ignore modes below this cutoff, so modes that are zero (e.g. by\n      // symmetry) don't make us underestimate the truncation error.\n      continue;\n    }\n    // Compute current weight\n    weight_value = exp(-square(static_cast<double>(last_index - index) - 0.5));\n    // Add weighted power monitor\n    weighted_average += weight_value * log10(mode);\n    // Add term to weighted sum\n    weight_sum += weight_value;\n  }\n  weighted_average /= weight_sum;\n\n  // Maximum between the first two power monitors\n  double leading_term = std::max(power_monitor[0], power_monitor[1]);\n  ASSERT(not(leading_term == 0.0),\n         \"The leading power monitor term is zero bitwise.\");\n\n  return std::pow(10.0, weighted_average) / leading_term;\n}\n\ntemplate <typename VectorType, size_t Dim>\nstd::array<double, Dim> relative_truncation_error(\n    const VectorType& tensor_component, const Mesh<Dim>& mesh) {\n  std::array<double, Dim> result{};\n  const auto modes = power_monitors(tensor_component, mesh);\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto& modes_d = gsl::at(modes, d);\n    gsl::at(result, d) = relative_truncation_error(modes_d, modes_d.size());\n  }\n  return result;\n}\n\ntemplate <typename VectorType, size_t Dim>\nstd::array<double, Dim> absolute_truncation_error(\n    const VectorType& tensor_component, const Mesh<Dim>& mesh) {\n  std::array<double, Dim> result{};\n  const auto modes = power_monitors(tensor_component, mesh);\n  // Use infinity norm to estimate the order of magnitude of the variable\n  const double umax = max(abs(tensor_component));\n  double relative_truncation_error_in_d = 0.0;\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto& modes_d = gsl::at(modes, d);\n    // Compute relative truncation error\n    relative_truncation_error_in_d =\n        relative_truncation_error(modes_d, modes_d.size());\n    // Compute absolute truncation error estimate\n    gsl::at(result, d) = umax * relative_truncation_error_in_d;\n  }\n  return result;\n}\n\nConvergenceInfo convergence_rate_and_number_of_pile_up_modes(\n    const DataVector& power_monitor, const size_t number_of_filtered_modes) {\n  // Need enough unfiltered modes to compute the convergence rate. Here,\n  // require at least 4 unfiltered modes.\n  ASSERT(\n      power_monitor.size() > number_of_filtered_modes + 3,\n      \"Power monitor needs at least 4 unfiltered modes to compute convergence \"\n      \"rate and number of pile up modes, but power monitor has size \"\n          << power_monitor.size() << \" with \" << number_of_filtered_modes\n          << \" filtered modes\");\n  ConvergenceInfo result{};\n\n  const size_t n_tilde = power_monitor.size() - number_of_filtered_modes;\n  std::vector<double> mode_numbers_for_fit{};\n  std::vector<double> mode_powers_for_fit{};\n  mode_numbers_for_fit.reserve(n_tilde);\n  mode_powers_for_fit.reserve(n_tilde);\n\n  const size_t n_tilde_minus_one = n_tilde - 1;\n  std::vector<double> slopes{};\n  std::vector<double> delta_slopes{};\n\n  // It turns out (as can be verified empirically) that the number of terms\n  // in the loop to compute the convergence rate is 3 * (n_tilde - 5) for\n  // n_tilde > 6, 4 for n_tilde == 5, and 3 for n_tilde == 4 or n_tilde == 3.\n  // Here reserve the correct number of elements in terms of n_tilde, except\n  // don't use an extra if to distinguish between the 3 and 4 special cases (no\n  // harm in reserving space for a single extra double).\n  const size_t max_slope_size = n_tilde > 6 ? 3 * (n_tilde - 5) : 4;\n  slopes.reserve(max_slope_size);\n  delta_slopes.reserve(max_slope_size);\n\n  // Compute log of the power monitor only for unfiltered modes, and\n  // ensure that log10 never causes a floating point exception here.\n  constexpr double eps_for_log = 100.0 * std::numeric_limits<double>::min();\n  const double log_floor = log10(eps_for_log);\n  DataVector log_power = power_monitor;\n  for (size_t i = 0; i < power_monitor.size() - number_of_filtered_modes; ++i) {\n    if (abs(log_power[i]) < eps_for_log) {\n      log_power[i] = log_floor;\n    } else {\n      log_power[i] = log10(abs(log_power[i]));\n    }\n  }\n\n  // Compute convergence rate\n  for (size_t k1 = 0; k1 < 3; ++k1) {\n    for (size_t k2 = std::min(k1 + 4, n_tilde_minus_one);\n         k2 <= n_tilde_minus_one; ++k2) {\n      mode_numbers_for_fit.resize(k2 - k1 + 1);\n      mode_powers_for_fit.resize(k2 - k1 + 1);\n      for (size_t k = k1; k <= k2; ++k) {\n        mode_numbers_for_fit[k - k1] = static_cast<double>(k);\n        mode_powers_for_fit[k - k1] = log_power[k];\n      }\n      if (mode_numbers_for_fit.size() > 2) {\n        const intrp::LinearRegressionResult regression_result =\n            intrp::linear_regression(mode_numbers_for_fit, mode_powers_for_fit);\n        slopes.push_back(regression_result.slope);\n        delta_slopes.push_back(regression_result.delta_slope);\n      } else if (mode_numbers_for_fit.size() == 2 and\n                 mode_numbers_for_fit[0] != mode_numbers_for_fit[1]) {\n        slopes.push_back((mode_powers_for_fit[1] - mode_powers_for_fit[0]) /\n                         (mode_numbers_for_fit[1] - mode_numbers_for_fit[0]));\n        delta_slopes.push_back(0.0);\n      } else {\n        // Cannot construct a slope; skip this term in sum\n        continue;\n      }\n    }\n  }\n  const auto max_delta =\n      std::max_element(delta_slopes.begin(), delta_slopes.end());\n  // Scale the small factor to be a few orders of magnitude smaller than\n  // the max slope delta, as SpEC does. But in case the fit happens to have\n  // identically zero errors, make sure eps is still nonzero.\n  const double eps = std::max(*max_delta * 1.e-3, 1.e-15);\n  double num = 0.0;\n  double denom = 0.0;\n  for (size_t i = 0; i < slopes.size(); ++i) {\n    const double one_over_denom_this_term =\n        1.0 / (eps + gsl::at(delta_slopes, i));\n    denom += one_over_denom_this_term;\n    num += gsl::at(slopes, i) * one_over_denom_this_term;\n  }\n  result.convergence_rate = -num / denom;\n\n  // Compute number of pile up modes\n  // First, if the convergence rate is nearly zero, return zero pile up modes.\n  // A convergence rate near zero typically means that the function is not at\n  // all resolved (e.g. a step function), so the power spectrum is approximately\n  // flat. If the convergence rate is approximately flat, it's not realistic to\n  // attempt to distinguish whatever small, residual convergence might be\n  // present vs. pile up modes. Returning zero for an approximately flat power\n  // monitor also avoids dividing by approximately zero (or, in the case of\n  // an exactly flat power monitor, by exactly zero).\n  if (std::abs(result.convergence_rate) < 1.e-10) {\n    result.number_of_pile_up_modes = 0.0;\n  } else {\n    double number_of_pile_up_modes = 0.0;\n    for (size_t j = 2; j < n_tilde - 1; ++j) {\n      const size_t j_max = std::min(n_tilde - 1, j + 4);\n      mode_numbers_for_fit.resize(j_max - j + 1);\n      mode_powers_for_fit.resize(j_max - j + 1);\n      for (size_t i = j; i <= j_max; ++i) {\n        mode_numbers_for_fit[i - j] = static_cast<double>(i);\n        mode_powers_for_fit[i - j] = log_power[i];\n      }\n      double local_convergence_rate =\n          std::numeric_limits<double>::signaling_NaN();\n      if (mode_numbers_for_fit.size() > 2) {\n        local_convergence_rate =\n            -intrp::linear_regression(mode_numbers_for_fit, mode_powers_for_fit)\n                 .slope;\n      } else if (mode_numbers_for_fit.size() == 2 and\n                 mode_numbers_for_fit[1] != mode_numbers_for_fit[0]) {\n        local_convergence_rate =\n            (mode_powers_for_fit[1] - mode_powers_for_fit[0]) /\n            (mode_numbers_for_fit[1] - mode_numbers_for_fit[0]);\n      } else {\n        // cannot measure slope, so skip this term in sum\n        continue;\n      }\n      const double conv_ratio =\n          square(local_convergence_rate / result.convergence_rate);\n      // Avoid underflow: if conv_ratio < 16, just add zero.\n      // exp(-32.0*16.0) ~ 1.0e-195, which is still large enough to\n      // avoid underflow.\n      number_of_pile_up_modes +=\n          conv_ratio < 16.0 ? exp(-32.0 * conv_ratio) : 0.0;\n    }\n    result.number_of_pile_up_modes = number_of_pile_up_modes;\n  }\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_DIM(_, data)                                          \\\n  template std::array<DataVector, DIM(data)> power_monitors(              \\\n      const DTYPE(data) & u, const Mesh<DIM(data)>& mesh);                \\\n  template void power_monitors(                                           \\\n      const gsl::not_null<std::array<DataVector, DIM(data)>*> result,     \\\n      const DTYPE(data) & u, const Mesh<DIM(data)>& mesh);                \\\n  template std::array<double, DIM(data)> relative_truncation_error(       \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh); \\\n  template std::array<double, DIM(data)> absolute_truncation_error(       \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_DIM, (DataVector, ComplexDataVector),\n                        (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace PowerMonitors\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/PowerMonitors.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\n/*!\n * \\brief Items for assessing truncation error in spectral methods.\n */\nnamespace PowerMonitors {\n\n/// @{\n/*!\n * \\ingroup SpectralGroup\n * \\brief Returns array of power monitors in each spatial dimension.\n *\n * Computed following Sec. 5.1 of Ref. \\cite Szilagyi2014fna.\n * For example, in the x dimension (indexed by \\f$ k_0 \\f$), we compute\n *\n * \\f{align*}{\n *  P_{k_0}[\\psi] = \\sqrt{ \\frac{1}{N_1 N_2}\n *   \\sum_{k_1,k_2} \\left| C_{k_0,k_1,k_2} \\right|^2} ,\n * \\f}\n *\n * where \\f$ C_{k_0,k_1,k_2}\\f$ are the modal coefficients\n * of variable \\f$ \\psi \\f$.\n *\n */\ntemplate <typename VectorType, size_t Dim>\nvoid power_monitors(gsl::not_null<std::array<DataVector, Dim>*> result,\n                    const VectorType& u, const Mesh<Dim>& mesh);\n\ntemplate <typename VectorType, size_t Dim>\nstd::array<DataVector, Dim> power_monitors(const VectorType& u,\n                                           const Mesh<Dim>& mesh);\n/// @}\n\n/// @{\n/*!\n * \\ingroup SpectralGroup\n * \\brief Compute the relative truncation error.\n *\n * The negative logarithm of this quantity is defined by Eqs. (57) and\n * (58) of Ref. \\cite Szilagyi2014fna, i.e.,\n *\n * \\f{align*}{\n *  \\mathcal{T}\\left[P_k\\right] = \\log_{10} \\max \\left(P_0, P_1\\right)\n *   - \\dfrac{\\sum_{j=0}^{j_{\\text{max}, k}} \\log_{10} \\left(P_j\\right) w_j}\n *   {\\sum_{j=0}^{j_{\\text{max}, k}} w_j} , \\f}\n *\n * with weights\n *\n * \\f{align*}{\n *  w_j = \\exp\\left[ - \\left(j - j_{\\text{max}, k}\n *          + \\dfrac{1}{2}\\right)^2 \\right] .\n * \\f}\n *\n * where \\f$ j_{\\text{max}, k}  = N_k - 1 \\f$ and  \\f$ N_k \\f$ is the number of\n * modes or gridpoints in dimension k. Here the second term is a weighted\n * average with larger weights toward the highest modes.\n *\n * \\note Modes below a cutoff of $100 \\epsilon \\mathrm{max}_k(P_k)$ are ignored\n * in the weighted average, where $\\epsilon$ is the machine epsilon. This\n * ensures that we don't underestimate the truncation error if some modes are\n * zero (e.g. by symmetry). Furthermore, if the last two or more modes are zero,\n * we assume that the function is represented exactly and return a relative\n * truncation error of zero.\n *\n * \\details The number of modes (`num_modes_to_use`) argument needs to be less\n * or equal than the total number of power monitors (`power_monitor.size()`).\n * In contrast with Ref. \\cite Szilagyi2014fna, here we index the modes starting\n * from zero.\n *\n */\ndouble relative_truncation_error(const DataVector& power_monitor,\n                                 size_t num_modes_to_use);\n/// @}\n\n/*!\n * \\brief The relative truncation error in each logical direction of the grid\n *\n * This overload is intended for visualization purposes only. It takes a tensor\n * component as input, so it can be used as a kernel to post-process volume data\n * with Python bindings (see `TransformVolumeData.py`).\n */\ntemplate <typename VectorType, size_t Dim>\nstd::array<double, Dim> relative_truncation_error(\n    const VectorType& tensor_component, const Mesh<Dim>& mesh);\n\n/// @{\n/*!\n * \\ingroup SpectralGroup\n * \\brief Returns an estimate of the absolute truncation error in each\n * dimension.\n *\n * The estimate of the numerical error is given by\n *\n * \\f{align*}{\n *  \\mathcal{E}\\left[P_k\\right] = u_\\mathrm{max} \\times 10^{- \\mathcal{T}[P_k]},\n * \\f}\n *\n * where \\f$ u_\\mathrm{max} = \\mathrm{max} |u|\\f$ in the corresponding element\n * and \\f$ \\mathcal{T}[P_k] \\f$ is the relative error estimate\n * computed from the power monitors \\f$ P_k \\f$.\n *\n * \\warning This estimate is intended for visualization purposes only.\n */\ntemplate <typename VectorType, size_t Dim>\nstd::array<double, Dim> absolute_truncation_error(\n    const VectorType& tensor_component, const Mesh<Dim>& mesh);\n/// @}\n\n/// Holds convergence rate and pile up modes of a power monitor\nstruct ConvergenceInfo {\n  double convergence_rate{std::numeric_limits<double>::signaling_NaN()};\n  double number_of_pile_up_modes{std::numeric_limits<double>::signaling_NaN()};\n};\n\n/*!\n * \\ingroup SpectralGroup\n * \\brief Returns the convergence rate and the number of pile up modes of a\n * power monitor as a ConvergenceInfo.\n *\n * \\details Computes the convergence rate of a power monitor as a weighted\n * average of slopes measured using different subsets of spectral modes\n * in the power monitor. Equation (53) of \\cite Szilagyi2014fna gives\n * the convergence rate $\\mathcal{C}$ in terms of a power monitor $P_k$ as\n * \\begin{equation}\n * \\mathcal{C}(P_k) = -\\frac{\\sum_{k_1=0}^2\\sum_{k_2=\\tilde{k}_1}^{\\tilde{N}-1}\n *   \\frac{\\mathcal{S}(k_1,k_2)}{\\epsilon + \\mathcal{E}(k_1,k_2)}}{\n *   \\sum_{k_1=0}^2\\sum_{k_2=\\tilde{k}_1}^{\\tilde{N}-1}\n *   \\frac{1}{\\epsilon + \\mathcal{E}(k_1,k_2)}}.\n * \\end{equation}\n * Here, $\\mathcal{S}(k_1,k_2)$ is the slope of a linear regression fit of\n * $\\log_{10}(P_k)$ with $k$ satisfying $k_1\\leq k \\leq k_2$,\n * $\\mathcal{E}(k_1,k_2)$ is the error of the slope in that fit,\n * $\\epsilon=\\max\\left(10^{-3}\\max\\left(\\mathcal{E}(k_1,k_2)\\right),\n * 10^{-15}\\right)$ is a small number to avoid dividing by zero in the event the\n * fit errors vanish,\n * $\\max\\left(\\mathcal{E}(k_1,k_2)\\right)$ is the maximum fit error of each\n * fit whose slope is included in the summation,\n * $\\tilde{k}_1 = \\min\\left(k_1+4,\\tilde{N}-1\\right)$,\n * $\\tilde{N} = N-N_f$, $N$ is the number of modes in the\n * power monitor, and the highest $N_f$ modes are filtered. Note that the\n * way $\\epsilon$ is defined is so that it matches SpEC's definition, while\n * also ensuring that it is nonzero even if the error in the slope fit is\n * exactly zero.\n *\n * Also computes the number of pile up modes in a power monitor. Pile up\n * modes are modes where the power is no longer converging at the overall\n * convergence rate. Following Eq. (56) of \\cite Szilagyi2014fna, the number of\n * pile up modes $\\mathcal{P}$ is defined as\n * \\begin{equation}\n * \\mathcal{P}(P_k) = \\sum_{j=2}^{\\tilde{N}-2}\n * \\exp\\left[-32\\left(\\frac{\\tilde{\\mathcal{C}}_j}\n * {\\mathcal{C}(P_k)}\\right)^2\\right],\n * \\end{equation}\n * where $\\mathcal{C}(P_k)$ is the convergence rate of the power monitor $P_k$,\n * the local convergence rate $\\tilde{\\mathcal{C}}_j$ of mode $j$ is\n * \\begin{equation}\n * \\tilde{\\mathcal{C}}_j = -\\mathcal{S}(j,\\min(\\tilde{N}-1,j+4)),\n * \\end{equation}\n * $\\mathcal{S}(k_1,k_2)$ is the slope of a linear regression fit of\n * $\\log_{10}(P_k)$ with $k$ satisfying $k_1\\leq k \\leq k_2$,\n * $\\tilde{N} = N-N_f$, $N$ is the number of modes in the\n * power monitor, and the highest $N_f$ modes are filtered.\n * The motivation of this definition is the following: if the local\n * convergence rate $\\tilde{\\mathcal{C}}_j$ is comparable to the overall\n * convergence rate $\\mathcal{C}(P_k)$, then the $j^{\\rm th}$ term in the\n * summation becomes $\\approx \\exp(-32) \\approx 10^{-14}$, while if\n * $\\tilde{\\mathcal{C}}_j \\ll \\mathcal{C}(P_k)$, then the $j^{\\rm th}$ term in\n * the summation is $\\approx \\exp(0) = 1$. Note that the coefficient value 32\n * is chosen to agree with SpEC.\n * \\note The summation goes up to $\\tilde{N}-2$ so that there is it least one\n * larger unfiltered mode for use in computing the slope. The highest mode\n * used when computing the slope is the highest unfiltered mode, $\\tilde{N}-1$.\n * Mode numbers in the power monitor are zero based. These choices are off by\n * one vs. Eqs. (55) and (56) of \\cite Szilagyi2014fna, because those formulas\n * apparently assume one-based indexing.\n * \\param power_monitor The power monitor.\n * \\param number_of_filtered_modes How many of the highest modes of the\n * power monitor are filtered (default 0).\n */\nConvergenceInfo convergence_rate_and_number_of_pile_up_modes(\n    const DataVector& power_monitor, size_t number_of_filtered_modes = 0);\n}  // namespace PowerMonitors\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"NumericalAlgorithms/LinearOperators/Python/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Python/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Python/PowerMonitors.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py::module_::import(\"spectre.Spectral\");\n  py_bindings::bind_definite_integral(m);\n  py_bindings::bind_partial_derivatives(m);\n  PowerMonitors::py_bindings::bind_power_monitors(m);\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyLinearOperators\")\n\nspectre_python_add_module(\n  LinearOperators\n  LIBRARY_NAME ${LIBRARY}\n  MODULE_PATH \"NumericalAlgorithms\"\n  SOURCES\n  Bindings.cpp\n  DefiniteIntegral.cpp\n  PartialDerivatives.cpp\n  PowerMonitors.cpp\n  PYTHON_FILES\n  __init__.py\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  DefiniteIntegral.hpp\n  PartialDerivatives.hpp\n  PowerMonitors.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  LinearOperators\n  pybind11::module\n  PythonBindings\n  Spectral\n  Utilities\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PySpectral\n  PyTensor\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/DefiniteIntegral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/Python/DefiniteIntegral.hpp\"\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_definite_integral_impl(py::module& m) {  // NOLINT\n  m.def(\"definite_integral\", &definite_integral<Dim>, py::arg(\"integrand\"),\n        py::arg(\"mesh\"));\n}\n}  // namespace\n\nvoid bind_definite_integral(py::module& m) {  // NOLINT\n  bind_definite_integral_impl<1>(m);\n  bind_definite_integral_impl<2>(m);\n  bind_definite_integral_impl<3>(m);\n}\n\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/DefiniteIntegral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_definite_integral(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/PartialDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/Python/PartialDerivatives.hpp\"\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nnamespace {\ntemplate <size_t Dim, typename DerivFrame, typename TensorType>\nvoid bind_partial_derivatives_impl(py::module& m) {  // NOLINT\n  m.def(\n      \"partial_derivative\",\n      // Wrap in a lambda so the template parameters and the correct overload\n      // get inferred\n      [](const TensorType& tensor, const Mesh<Dim>& mesh,\n         const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                               DerivFrame>& inv_jacobian) {\n        return partial_derivative(tensor, mesh, inv_jacobian);\n      },\n      py::arg(\"tensor\"), py::arg(\"mesh\"), py::arg(\"inv_jacobian\"));\n  m.def(\n      \"logical_partial_derivative\",\n      // Wrap in a lambda so the template parameters and the correct overload\n      // get inferred\n      [](const TensorType& tensor, const Mesh<Dim>& mesh) {\n        return logical_partial_derivative(tensor, mesh);\n      },\n      py::arg(\"tensor\"), py::arg(\"mesh\"));\n}\n}  // namespace\n\nvoid bind_partial_derivatives(py::module& m) {\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TNSR(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE_SCALAR(_, data) \\\n  bind_partial_derivatives_impl<DIM(data), FRAME(data), Scalar<DataVector>>(m);\n#define INSTANTIATE_TNSR(_, data)                         \\\n  bind_partial_derivatives_impl < DIM(data), FRAME(data), \\\n      TNSR(data) < DataVector, DIM(data), FRAME(data) >> (m);\n\n  GENERATE_INSTANTIATIONS(INSTANTIATE_SCALAR, (1, 2, 3), (Frame::Inertial))\n  GENERATE_INSTANTIATIONS(INSTANTIATE_TNSR, (1, 2, 3), (Frame::Inertial),\n                          (tnsr::I, tnsr::ii, tnsr::Ijj, tnsr::a, tnsr::A,\n                           tnsr::aa, tnsr::AA, tnsr::iaa, tnsr::ia, tnsr::iA))\n\n#undef INSTANTIATE_SCALAR\n#undef INSTANTIATE_TNSR\n#undef DIM\n#undef FRAME\n#undef TNSR\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/PartialDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_partial_derivatives(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/PowerMonitors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/Python/PowerMonitors.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PowerMonitors.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace py = pybind11;\n\nnamespace PowerMonitors::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_power_monitors_impl(py::module& m) {  // NOLINT\n  m.def(\"power_monitors\",\n        py::overload_cast<const DataVector&, const Mesh<Dim>&>(\n            &power_monitors<DataVector, Dim>),\n        py::arg(\"data_vector\"), py::arg(\"mesh\"));\n  m.def(\"relative_truncation_error\",\n        py::overload_cast<const DataVector&, const Mesh<Dim>&>(\n            &relative_truncation_error<DataVector, Dim>),\n        py::arg(\"tensor_component\"), py::arg(\"mesh\"));\n  m.def(\"absolute_truncation_error\",\n        py::overload_cast<const DataVector&, const Mesh<Dim>&>(\n            &absolute_truncation_error<DataVector, Dim>),\n        py::arg(\"tensor_component\"), py::arg(\"mesh\"));\n}\n}  // namespace\n\nvoid bind_power_monitors(py::module& m) {\n  bind_power_monitors_impl<1>(m);\n  bind_power_monitors_impl<2>(m);\n  bind_power_monitors_impl<3>(m);\n  m.def(\n      \"convergence_rate_and_number_of_pile_up_modes\",\n      [](const DataVector& power_monitor,\n         const size_t number_of_filtered_modes) {\n        py::dict result{};\n        const ConvergenceInfo info =\n            convergence_rate_and_number_of_pile_up_modes(\n                power_monitor, number_of_filtered_modes);\n        result[\"convergence_rate\"] = info.convergence_rate;\n        result[\"number_of_pile_up_modes\"] = info.number_of_pile_up_modes;\n        return result;\n      },\n      py::arg(\"power_monitor\"), py::arg(\"number_of_filtered_modes\") = 0);\n}\n\n}  // namespace PowerMonitors::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/PowerMonitors.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace PowerMonitors::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_power_monitors(pybind11::module& m);\n}  // namespace PowerMonitors::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/WeakDivergence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearOperators/WeakDivergence.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <typename ResultTensor, typename FluxTensor, size_t Dim>\nvoid logical_weak_divergence(const gsl::not_null<ResultTensor*> div_flux,\n                             const FluxTensor& flux, const Mesh<Dim>& mesh) {\n  // Note: This function hasn't been optimized much at all. Feel free to\n  // optimize if needed!\n  static const Matrix identity_matrix{};\n  for (size_t d = 0; d < Dim; ++d) {\n    auto matrices = make_array<Dim>(std::cref(identity_matrix));\n    gsl::at(matrices, d) =\n        Spectral::differentiation_matrix_transpose(mesh.slice_through(d));\n    for (size_t storage_index = 0; storage_index < div_flux->size();\n         ++storage_index) {\n      const auto div_flux_index = div_flux->get_tensor_index(storage_index);\n      const auto flux_index = prepend(div_flux_index, d);\n      div_flux->get(div_flux_index) +=\n          apply_matrices(matrices, flux.get(flux_index), mesh.extents());\n    }\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TENSOR(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATION_SCALAR(r, data)                                     \\\n  template void logical_weak_divergence(                                  \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> div_flux,                 \\\n      const tnsr::I<DTYPE(data), DIM(data), Frame::ElementLogical>& flux, \\\n      const Mesh<DIM(data)>& mesh);\n#define INSTANTIATION_TENSOR(r, data)                                    \\\n  template void logical_weak_divergence(                                 \\\n      const gsl::not_null<tnsr::TENSOR(data) < DTYPE(data), DIM(data),   \\\n                          Frame::Inertial>* > div_flux,                  \\\n      const TensorMetafunctions::prepend_spatial_index<                  \\\n          tnsr::TENSOR(data) < DTYPE(data), DIM(data), Frame::Inertial>, \\\n      DIM(data), UpLo::Up, Frame::ElementLogical > &flux,                \\\n      const Mesh<DIM(data)>& mesh);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION_SCALAR, (DataVector, ComplexDataVector),\n                        (1, 2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATION_TENSOR, (DataVector, ComplexDataVector),\n                        (1, 2, 3), (i, I, aa))\n\ntemplate void logical_weak_divergence(\n    const gsl::not_null<tnsr::aa<ComplexDataVector, 3, Frame::Inertial>*>\n        div_flux,\n    const TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3, Frame::Inertial>, 2, UpLo::Up,\n        Frame::ElementLogical>& flux,\n    const Mesh<2>& mesh);\n\n#undef INSTANTIATION_SCALAR\n#undef INSTANTIATION_TENSOR\n#undef DTYPE\n#undef DIM\n#undef TENSOR\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearOperators/WeakDivergence.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Compute the weak form divergence of fluxes\n *\n * In a discontinuous Galerkin scheme we integrate the equations against the\n * basis functions over the element. For the flux divergence term this gives:\n *\n * \\f{align*}{\n *  \\int_{\\Omega}d^n x \\phi_{\\breve{\\imath}}\\partial_i F^i,\n * \\f}\n *\n * where the basis functions are denoted by \\f$\\phi_{\\breve{\\imath}}\\f$.\n *\n * Integrating by parts we get\n *\n * \\f{align*}{\n *  \\int_{\\Omega}d^n x\\, \\phi_{\\breve{\\imath}}\\partial_i F^i\n *  = -\\int_{\\Omega}d^n x\\,  F^i \\partial_i \\phi_{\\breve{\\imath}}\n *   + \\int_{\\partial\\Omega} d^{(n-1)}\\Sigma\\, n_i F^i \\phi_{\\breve{\\imath}}\n * \\f}\n *\n * Next we expand the flux \\f$F^i\\f$ in terms of the basis functions, yielding\n *\n * \\f{align*}{\n *  - \\int_{\\Omega}d^n x\\,F^i_{\\breve{\\jmath}} \\phi_{\\breve{\\jmath}} \\partial_i\n *    \\phi_{\\breve{\\imath}}\n *  + \\int_{\\partial\\Omega} d^{(n-1)}\\Sigma\\, n_i F^i_{\\breve{\\jmath}}\n *    \\phi_{\\breve{\\jmath}} \\phi_{\\breve{\\imath}}\n * \\f}\n *\n * This function computes the volume term:\n *\n * \\f{align*}{\n * \\frac{1}{w_\\breve{\\imath}} \\int_{\\Omega}d^n x \\,\n *   F^i_{\\breve{\\jmath}} \\phi_{\\breve{\\jmath}} \\partial_i \\phi_{\\breve{\\imath}}\n * \\f}\n *\n * \\note When using Gauss-Lobatto points the numerical values of the\n * divergence are the same for the strong and weak divergence at the interior\n * points. When using Gauss points they are only the same at the central grid\n * point and only when an odd number of grid points is used.\n */\ntemplate <typename... ResultTags, typename... FluxTags, size_t Dim>\nvoid weak_divergence(\n    const gsl::not_null<Variables<tmpl::list<ResultTags...>>*>\n        divergence_of_fluxes,\n    const Variables<tmpl::list<FluxTags...>>& fluxes, const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>& det_jac_times_inverse_jacobian) {\n  if (UNLIKELY(divergence_of_fluxes->number_of_grid_points() !=\n               fluxes.number_of_grid_points())) {\n    divergence_of_fluxes->initialize(fluxes.number_of_grid_points());\n  }\n\n  using ValueType = typename Variables<tmpl::list<FluxTags...>>::value_type;\n\n  if constexpr (Dim == 1) {\n    (void)det_jac_times_inverse_jacobian;  // is identically 1.0 in 1d\n\n    partial_derivatives_detail::apply_matrix_in_first_dim(\n        divergence_of_fluxes->data(), fluxes.data(),\n        Spectral::weak_flux_differentiation_matrix(mesh), fluxes.size(), false);\n  } else {\n    // Multiplies the flux by det_jac_time_inverse_jacobian.\n    const auto transform_to_logical_frame =\n        [&det_jac_times_inverse_jacobian, &fluxes](\n            auto flux_tag_v, auto div_tag_v,\n            const gsl::not_null<Variables<tmpl::list<ResultTags...>>*>\n                result_buffer,\n            auto logical_index_of_jacobian) {\n          using flux_tag = tmpl::type_from<decltype(flux_tag_v)>;\n          using div_tag = tmpl::type_from<decltype(div_tag_v)>;\n          using TensorIndexType =\n              std::array<size_t, div_tag::type::num_tensor_indices>;\n          using FluxIndexType =\n              std::array<size_t, flux_tag::type::num_tensor_indices>;\n\n          auto& result = get<div_tag>(*result_buffer);\n          const auto& flux = get<flux_tag>(fluxes);\n\n          for (size_t result_storage_index = 0;\n               result_storage_index < result.size(); ++result_storage_index) {\n            const TensorIndexType result_tensor_index =\n                result.get_tensor_index(result_storage_index);\n            const FluxIndexType flux_x_tensor_index =\n                prepend(result_tensor_index, 0_st);\n            const FluxIndexType flux_y_tensor_index =\n                prepend(result_tensor_index, 1_st);\n            if constexpr (Dim == 2) {\n              result[result_storage_index] =\n                  get<std::decay_t<decltype(logical_index_of_jacobian)>::value,\n                      0>(det_jac_times_inverse_jacobian) *\n                      flux.get(flux_x_tensor_index) +\n                  get<std::decay_t<decltype(logical_index_of_jacobian)>::value,\n                      1>(det_jac_times_inverse_jacobian) *\n                      flux.get(flux_y_tensor_index);\n            } else {\n              const FluxIndexType flux_z_tensor_index =\n                  prepend(result_tensor_index, 2_st);\n              result[result_storage_index] =\n                  get<std::decay_t<decltype(logical_index_of_jacobian)>::value,\n                      0>(det_jac_times_inverse_jacobian) *\n                      flux.get(flux_x_tensor_index) +\n                  get<std::decay_t<decltype(logical_index_of_jacobian)>::value,\n                      1>(det_jac_times_inverse_jacobian) *\n                      flux.get(flux_y_tensor_index) +\n                  get<std::decay_t<decltype(logical_index_of_jacobian)>::value,\n                      2>(det_jac_times_inverse_jacobian) *\n                      flux.get(flux_z_tensor_index);\n            }\n          }\n        };\n\n    if constexpr (Dim == 2) {\n      Variables<tmpl::list<ResultTags...>> data_buffer{\n          divergence_of_fluxes->number_of_grid_points()};\n\n      const Matrix& eta_weak_div_matrix =\n          Spectral::weak_flux_differentiation_matrix(mesh.slice_through(1));\n      const Matrix& xi_weak_div_matrix =\n          Spectral::weak_flux_differentiation_matrix(mesh.slice_through(0));\n\n      // Compute the eta divergence term. Since that also needs a transpose,\n      // copy into result, then transpose into `data_buffer`\n      EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(\n          tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},\n          make_not_null(&data_buffer), std::integral_constant<size_t, 1>{}));\n      ValueType* div_ptr = divergence_of_fluxes->data();\n      raw_transpose(make_not_null(div_ptr), data_buffer.data(),\n                    xi_weak_div_matrix.rows(),\n                    divergence_of_fluxes->size() / xi_weak_div_matrix.rows());\n      partial_derivatives_detail::apply_matrix_in_first_dim(\n          data_buffer.data(), divergence_of_fluxes->data(), eta_weak_div_matrix,\n          data_buffer.size(), false);\n\n      const size_t chunk_size = Variables<tmpl::list<Tags::div<FluxTags>...>>::\n                                    number_of_independent_components *\n                                eta_weak_div_matrix.rows();\n      raw_transpose(make_not_null(div_ptr), data_buffer.data(), chunk_size,\n                    data_buffer.size() / chunk_size);\n\n      // Now compute xi divergence and *add* to eta divergence\n      EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(\n          tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},\n          make_not_null(&data_buffer), std::integral_constant<size_t, 0>{}));\n      partial_derivatives_detail::apply_matrix_in_first_dim(\n          divergence_of_fluxes->data(), data_buffer.data(), xi_weak_div_matrix,\n          data_buffer.size(), true);\n    } else if constexpr (Dim == 3) {\n      Variables<tmpl::list<ResultTags...>> data_buffer0{\n          divergence_of_fluxes->number_of_grid_points()};\n      Variables<tmpl::list<ResultTags...>> data_buffer1{\n          divergence_of_fluxes->number_of_grid_points()};\n      constexpr size_t number_of_independent_components =\n          decltype(data_buffer1)::number_of_independent_components;\n\n      const Matrix& zeta_weak_div_matrix =\n          Spectral::weak_flux_differentiation_matrix(mesh.slice_through(2));\n      const Matrix& eta_weak_div_matrix =\n          Spectral::weak_flux_differentiation_matrix(mesh.slice_through(1));\n      const Matrix& xi_weak_div_matrix =\n          Spectral::weak_flux_differentiation_matrix(mesh.slice_through(0));\n\n      // Compute the zeta divergence term. Since that also needs a transpose,\n      // copy into data_buffer0, then transpose into `data_buffer1`.\n      EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(\n          tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},\n          make_not_null(&data_buffer0), std::integral_constant<size_t, 2>{}));\n      size_t chunk_size =\n          xi_weak_div_matrix.rows() * eta_weak_div_matrix.rows();\n      ValueType* result_ptr = data_buffer1.data();\n      raw_transpose(make_not_null(result_ptr), data_buffer0.data(), chunk_size,\n                    data_buffer0.size() / chunk_size);\n      partial_derivatives_detail::apply_matrix_in_first_dim(\n          data_buffer0.data(), data_buffer1.data(), zeta_weak_div_matrix,\n          data_buffer0.size(), false);\n      chunk_size =\n          number_of_independent_components * zeta_weak_div_matrix.rows();\n      result_ptr = divergence_of_fluxes->data();\n      raw_transpose(make_not_null(result_ptr), data_buffer0.data(), chunk_size,\n                    data_buffer1.size() / chunk_size);\n\n      // Compute the eta divergence term. Since that also needs a transpose,\n      // copy into data_buffer0, then transpose into `data_buffer1`.\n      EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(\n          tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},\n          make_not_null(&data_buffer0), std::integral_constant<size_t, 1>{}));\n      chunk_size = xi_weak_div_matrix.rows();\n      result_ptr = data_buffer1.data();\n      raw_transpose(make_not_null(result_ptr), data_buffer0.data(), chunk_size,\n                    data_buffer1.size() / chunk_size);\n      partial_derivatives_detail::apply_matrix_in_first_dim(\n          data_buffer0.data(), data_buffer1.data(), eta_weak_div_matrix,\n          data_buffer0.size(), false);\n      chunk_size = number_of_independent_components *\n                   eta_weak_div_matrix.rows() * zeta_weak_div_matrix.rows();\n      result_ptr = data_buffer1.data();\n      raw_transpose(make_not_null(result_ptr), data_buffer0.data(), chunk_size,\n                    data_buffer0.size() / chunk_size);\n      *divergence_of_fluxes += data_buffer1;\n\n      // Now compute xi divergence and *add* to eta divergence\n      EXPAND_PACK_LEFT_TO_RIGHT(transform_to_logical_frame(\n          tmpl::type_<FluxTags>{}, tmpl::type_<ResultTags>{},\n          make_not_null(&data_buffer0), std::integral_constant<size_t, 0>{}));\n      partial_derivatives_detail::apply_matrix_in_first_dim(\n          divergence_of_fluxes->data(), data_buffer0.data(), xi_weak_div_matrix,\n          data_buffer0.size(), true);\n    } else {\n      static_assert(Dim == 1 or Dim == 2 or Dim == 3,\n                    \"Weak divergence only implemented in 1d, 2d, and 3d.\");\n    }\n  }\n}\n\n/// @{\n/*!\n * \\brief Compute the weak divergence of fluxes in logical coordinates\n *\n * Applies the transpose logical differentiation matrix to the fluxes in each\n * dimension and sums over dimensions.\n *\n * \\see weak_divergence\n */\ntemplate <typename ResultTensor, typename FluxTensor, size_t Dim>\nvoid logical_weak_divergence(gsl::not_null<ResultTensor*> div_flux,\n                             const FluxTensor& flux, const Mesh<Dim>& mesh);\n\n/*!\n * \\param divergence_of_fluxes Result buffer. Will be resized if necessary.\n * \\param fluxes The fluxes to take the divergence of. Their first index must be\n * an upper spatial index in element-logical coordinates.\n * \\param mesh The element mesh.\n * \\param add_to_buffer If `true`, add the divergence to the existing values in\n * `divergence_of_fluxes`. Otherwise, overwrite the existing values.\n */\ntemplate <typename... ResultTags, typename... FluxTags, size_t Dim>\nvoid logical_weak_divergence(\n    const gsl::not_null<Variables<tmpl::list<ResultTags...>>*>\n        divergence_of_fluxes,\n    const Variables<tmpl::list<FluxTags...>>& fluxes, const Mesh<Dim>& mesh,\n    const bool add_to_buffer = false) {\n  static_assert(\n      (... and\n       std::is_same_v<tmpl::front<typename FluxTags::type::index_list>,\n                      SpatialIndex<Dim, UpLo::Up, Frame::ElementLogical>>),\n      \"The first index of each flux must be an upper spatial index in \"\n      \"element-logical coordinates.\");\n  static_assert(\n      (... and\n       std::is_same_v<\n           typename ResultTags::type,\n           TensorMetafunctions::remove_first_index<typename FluxTags::type>>),\n      \"The result tensors must have the same type as the flux tensors with \"\n      \"their first index removed.\");\n  if (not add_to_buffer) {\n    divergence_of_fluxes->initialize(mesh.number_of_grid_points(), 0.);\n  }\n  EXPAND_PACK_LEFT_TO_RIGHT(logical_weak_divergence(\n      make_not_null(&get<ResultTags>(*divergence_of_fluxes)),\n      get<FluxTags>(fluxes), mesh));\n}\n/// @}\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/BuildMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <blaze/math/Column.h>\n#include <blaze/math/Matrix.h>\n#include <blaze/math/typetraits/IsDenseMatrix.h>\n#include <blaze/math/typetraits/IsSparseMatrix.h>\n#include <cstddef>\n#include <tuple>\n\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace LinearSolver::Serial {\n\nnamespace detail {\nCREATE_IS_CALLABLE(reset)\nCREATE_IS_CALLABLE_V(reset)\n}  // namespace detail\n\n/*!\n * \\brief Construct an explicit matrix representation by \"sniffing out\" the\n * linear operator, i.e. feeding it unit vectors.\n *\n * \\param matrix Output buffer for the operator matrix. Must be sized correctly\n * on entry. Can be any dense or sparse Blaze matrix.\n * \\param operand_buffer Memory buffer that can hold operand data for the\n * `linear_operator`. Must be sized correctly on entry, and must be filled with\n * zeros.\n * \\param result_buffer Memory buffer that can hold the result of the\n * `linear_operator` applied to the operand. Must be sized correctly on entry.\n * \\param linear_operator The linear operator of which the matrix representation\n * should be constructed. See `::LinearSolver::Serial::LinearSolver::solve` for\n * requirements on the linear operator.\n * \\param operator_args These arguments are passed along to the\n * `linear_operator` when it is applied to an operand.\n */\ntemplate <typename LinearOperator, typename OperandType, typename ResultType,\n          typename MatrixType, typename... OperatorArgs>\nvoid build_matrix(const gsl::not_null<MatrixType*> matrix,\n                  const gsl::not_null<OperandType*> operand_buffer,\n                  const gsl::not_null<ResultType*> result_buffer,\n                  const LinearOperator& linear_operator,\n                  const std::tuple<OperatorArgs...>& operator_args = {}) {\n  static_assert(\n      blaze::IsSparseMatrix_v<MatrixType> or blaze::IsDenseMatrix_v<MatrixType>,\n      \"Unexpected matrix type\");\n  if constexpr (blaze::IsSparseMatrix_v<MatrixType>) {\n    matrix->reset();\n  }\n  size_t i = 0;\n  // Re-using the iterators for all operator invocations\n  auto result_iterator_begin = result_buffer->begin();\n  auto result_iterator_end = result_buffer->end();\n  for (auto& unit_vector_data : *operand_buffer) {\n    // Set a 1 at the unit vector location i\n    unit_vector_data = 1.;\n    // Invoke the operator on the unit vector\n    std::apply(\n        linear_operator,\n        std::tuple_cat(std::forward_as_tuple(result_buffer, *operand_buffer),\n                       operator_args));\n    // Set the unit vector back to zero\n    unit_vector_data = 0.;\n    // Reset the iterator by calling its `reset` member function or by\n    // re-creating it\n    if constexpr (detail::is_reset_callable_v<\n                      decltype(result_iterator_begin)>) {\n      result_iterator_begin.reset();\n    } else {\n      result_iterator_begin = result_buffer->begin();\n      result_iterator_end = result_buffer->end();\n    }\n    // Store the result in column i of the matrix\n    auto col = column(*matrix, i);\n    if constexpr (blaze::IsSparseMatrix_v<MatrixType>) {\n      size_t k = 0;\n      while (result_iterator_begin != result_iterator_end) {\n        if (not equal_within_roundoff(*result_iterator_begin, 0.)) {\n          col[k] = *result_iterator_begin;\n        }\n        ++result_iterator_begin;\n        ++k;\n      }\n    } else {\n      std::copy(result_iterator_begin, result_iterator_end, col.begin());\n    }\n    ++i;\n  }\n  ASSERT(i == matrix->columns(), \"Incomplete matrix. Expected \"\n                                     << matrix->columns()\n                                     << \" columns, but only filled \" << i\n                                     << \" columns.\");\n}\n\n}  // namespace LinearSolver::Serial\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY LinearSolver)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Gmres.cpp\n  Lapack.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BuildMatrix.hpp\n  ExplicitInverse.hpp\n  Gmres.hpp\n  InnerProduct.hpp\n  Lapack.hpp\n  LinearSolver.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  BLAS::BLAS\n  Convergence\n  DataStructures\n  ErrorHandling\n  Logging\n  Options\n  Serialization\n  PRIVATE\n  LAPACK::LAPACK\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/ExplicitInverse.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <fstream>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/BuildMatrix.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/LinearSolver.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Tags/ArrayIndex.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\nnamespace LinearSolver::Serial {\n\n/// \\cond\ntemplate <typename ValueType, typename LinearSolverRegistrars>\nstruct ExplicitInverse;\n/// \\endcond\n\nnamespace Registrars {\n/// Registers the `LinearSolver::Serial::ExplicitInverse` linear solver\ntemplate <typename ValueType>\nstruct ExplicitInverse {\n  template <typename LinearSolverRegistrars>\n  using f = Serial::ExplicitInverse<ValueType, LinearSolverRegistrars>;\n};\n}  // namespace Registrars\n\n/*!\n * \\brief Linear solver that builds a matrix representation of the linear\n * operator and inverts it directly\n *\n * This solver first constructs an explicit matrix representation by \"sniffing\n * out\" the operator, i.e. feeding it with unit vectors, and then directly\n * inverts the matrix. The result is an operator that solves the linear problem\n * in a single step. This means that each element has a large initialization\n * cost, but all successive solves converge immediately.\n *\n * \\par Advice on using this linear solver:\n * - This solver is entirely agnostic to the structure of the linear operator.\n *   It is usually better to implement a linear solver that is specialized for\n *   your linear operator to take advantage of its properties. For example, if\n *   the operator has a tensor-product structure, the linear solver might take\n *   advantage of that. Only use this solver if no alternatives are available\n *   and if you have verified that it speeds up your solves.\n * - Since this linear solver stores the full inverse operator matrix it can\n *   have significant memory demands. For example, an operator representing a 3D\n *   first-order Elasticity system (9 variables) discretized on 12 grid points\n *   per dimension requires ca. 2GB of memory (per element) to store the matrix,\n *   scaling quadratically with the number of variables and with a power of 6\n *   with the number of grid points per dimension. Therefore, make sure to\n *   distribute the elements on a sufficient number of nodes to meet the memory\n *   requirements.\n * - This linear solver can be `reset()` when the operator changes (e.g. in each\n *   nonlinear-solver iteration). However, when using this solver as\n *   preconditioner it can be advantageous to avoid the reset and the\n *   corresponding cost of re-building the matrix and its inverse if the\n *   operator only changes \"a little\". In that case the preconditioner solves\n *   subdomain problems only approximately, but possibly still sufficiently to\n *   provide effective preconditioning.\n */\ntemplate <typename ValueType,\n          typename LinearSolverRegistrars =\n              tmpl::list<Registrars::ExplicitInverse<ValueType>>>\nclass ExplicitInverse : public LinearSolver<LinearSolverRegistrars> {\n private:\n  using Base = LinearSolver<LinearSolverRegistrars>;\n\n public:\n  struct WriteMatrixToFile {\n    using type = Options::Auto<std::string, Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        \"Write the matrix representation of the linear operator to a \"\n        \"space-delimited CSV file with this name. A '.txt' extension will be \"\n        \"added. Also a suffix with the element ID will be added if this linear \"\n        \"solver runs on an array element, so one file per element will be \"\n        \"written.\";\n  };\n  struct ComplexShift {\n    using type = double;\n    static constexpr Options::String help =\n        \"Subtracts i*ComplexShift from the operator. \"\n        \"This can help dampen high-frequency modes when preconditioning \"\n        \"complex-valued problems.\";\n  };\n\n  using options = tmpl::flatten<tmpl::list<\n      WriteMatrixToFile,\n      tmpl::conditional_t<std::is_same_v<ValueType, std::complex<double>>,\n                          ComplexShift, tmpl::list<>>>>;\n  static constexpr Options::String help =\n      \"Build a matrix representation of the linear operator and invert it \"\n      \"directly. This means that the first solve has a large initialization \"\n      \"cost, but all subsequent solves converge immediately.\";\n\n  ExplicitInverse(const ExplicitInverse& /*rhs*/) = default;\n  ExplicitInverse& operator=(const ExplicitInverse& /*rhs*/) = default;\n  ExplicitInverse(ExplicitInverse&& /*rhs*/) = default;\n  ExplicitInverse& operator=(ExplicitInverse&& /*rhs*/) = default;\n  ~ExplicitInverse() = default;\n\n  explicit ExplicitInverse(\n      std::optional<std::string> matrix_filename = std::nullopt,\n      const double complex_shift = 0.0)\n      : matrix_filename_(std::move(matrix_filename)),\n        complex_shift_(complex_shift) {}\n\n  /// \\cond\n  explicit ExplicitInverse(CkMigrateMessage* m) : Base(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ExplicitInverse);  // NOLINT\n  /// \\endcond\n\n  /*!\n   * \\brief Solve the equation \\f$Ax=b\\f$ by explicitly constructing the\n   * operator matrix \\f$A\\f$ and its inverse. The first solve is computationally\n   * expensive and successive solves are cheap.\n   *\n   * Building a matrix representation of the `linear_operator` requires\n   * iterating over the `SourceType` (which is also the type returned by the\n   * `linear_operator`) in a consistent way. This can be non-trivial for\n   * heterogeneous data structures because it requires they define a data\n   * ordering. Specifically, the `SourceType` must have a `size()` function as\n   * well as `begin()` and `end()` iterators that point into the data. If the\n   * iterators have a `reset()` function it is used to avoid repeatedly\n   * re-creating the `begin()` iterator. The `reset()` function must not\n   * invalidate the `end()` iterator.\n   */\n  template <typename LinearOperator, typename VarsType, typename SourceType,\n            typename... OperatorArgs>\n  Convergence::HasConverged solve(\n      gsl::not_null<VarsType*> solution, const LinearOperator& linear_operator,\n      const SourceType& source,\n      const std::tuple<OperatorArgs...>& operator_args = std::tuple{}) const;\n\n  /// Flags the operator to require re-initialization. No memory is released.\n  /// Call this function to rebuild the solver when the operator changed.\n  void reset() override { size_ = std::numeric_limits<size_t>::max(); }\n\n  /// Size of the operator. The stored matrix will have `size^2` entries.\n  size_t size() const { return size_; }\n\n  /// The matrix representation of the solver. This matrix approximates the\n  /// inverse of the subdomain operator.\n  const blaze::DynamicMatrix<ValueType, blaze::columnMajor>&\n  matrix_representation() const {\n    return inverse_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    p | matrix_filename_;\n    p | complex_shift_;\n    p | size_;\n    p | inverse_;\n    if (p.isUnpacking() and size_ != std::numeric_limits<size_t>::max()) {\n      source_workspace_.resize(size_);\n      solution_workspace_.resize(size_);\n    }\n  }\n\n  std::unique_ptr<Base> get_clone() const override {\n    return std::make_unique<ExplicitInverse>(*this);\n  }\n\n private:\n  std::optional<std::string> matrix_filename_{};\n  double complex_shift_ = std::numeric_limits<double>::signaling_NaN();\n  // Caches for successive solves of the same operator\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable size_t size_ = std::numeric_limits<size_t>::max();\n  // We currently store the matrix representation in a dense matrix because\n  // Blaze doesn't support the inversion of sparse matrices (yet).\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable blaze::DynamicMatrix<ValueType, blaze::columnMajor> inverse_{};\n\n  // Buffers to avoid re-allocating memory for applying the operator\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable blaze::DynamicVector<ValueType> source_workspace_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable blaze::DynamicVector<ValueType> solution_workspace_{};\n};\n\ntemplate <typename ValueType, typename LinearSolverRegistrars>\ntemplate <typename LinearOperator, typename VarsType, typename SourceType,\n          typename... OperatorArgs>\nConvergence::HasConverged\nExplicitInverse<ValueType, LinearSolverRegistrars>::solve(\n    const gsl::not_null<VarsType*> solution,\n    const LinearOperator& linear_operator, const SourceType& source,\n    const std::tuple<OperatorArgs...>& operator_args) const {\n  if (UNLIKELY(size_ == std::numeric_limits<size_t>::max())) {\n    const auto& used_for_size = source;\n    size_ = used_for_size.size();\n    source_workspace_.resize(size_);\n    solution_workspace_.resize(size_);\n    inverse_.resize(size_, size_);\n    // Construct explicit matrix representation by \"sniffing out\" the operator,\n    // i.e. feeding it unit vectors\n    auto operand_buffer = make_with_value<VarsType>(used_for_size, 0.);\n    auto result_buffer = make_with_value<SourceType>(used_for_size, 0.);\n    build_matrix(make_not_null(&inverse_), make_not_null(&operand_buffer),\n                 make_not_null(&result_buffer), linear_operator, operator_args);\n    // Write to file before inverting\n    if (UNLIKELY(matrix_filename_.has_value())) {\n      std::string filename_suffix{};\n      using DataBoxType =\n          std::decay_t<tmpl::front<tmpl::list<OperatorArgs..., NoSuchType>>>;\n      if constexpr (tt::is_a_v<db::DataBox, DataBoxType>) {\n        tmpl::for_each<tmpl::range<size_t, 1, 4>>(\n            [&]<size_t Dim>(tmpl::type_<tmpl::size_t<Dim>> /*meta*/) {\n              using index_tag = Parallel::Tags::ArrayIndex<ElementId<Dim>>;\n              if constexpr (db::tag_is_retrievable_v<index_tag, DataBoxType>) {\n                const auto& box = std::get<0>(operator_args);\n                filename_suffix = \"_\" + get_output(db::get<index_tag>(box));\n              } else {\n                (void)operator_args;\n              }\n            });\n      } else {\n        (void)operator_args;\n      }\n      std::ofstream matrix_file(matrix_filename_.value() + filename_suffix +\n                                \".txt\");\n      write_csv(matrix_file, inverse_, \" \");\n    }\n    // Apply complex shift\n    if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n      for (size_t i = 0; i < size_; ++i) {\n        inverse_(i, i) -= std::complex<double>(0.0, complex_shift_);\n      }\n    }\n    // Directly invert the matrix\n    try {\n      blaze::invert(inverse_);\n    } catch (const std::exception& e) {\n      ERROR(\"Could not invert subdomain matrix (size \" << size_\n                                                       << \"): \" << e.what());\n    }\n  }\n  // Copy source into contiguous workspace. In cases where the source and\n  // solution data are already stored contiguously we might avoid the copy and\n  // the associated workspace memory. However, compared to the cost of building\n  // and storing the matrix this is likely insignificant.\n  std::copy(source.begin(), source.end(), source_workspace_.begin());\n  // Apply inverse\n  solution_workspace_ = inverse_ * source_workspace_;\n  // Reconstruct solution data from contiguous workspace\n  std::copy(solution_workspace_.begin(), solution_workspace_.end(),\n            solution->begin());\n  return {0, 0};\n}\n\n/// \\cond\n// NOLINTBEGIN\ntemplate <typename ValueType, typename LinearSolverRegistrars>\nPUP::able::PUP_ID\n    ExplicitInverse<ValueType, LinearSolverRegistrars>::my_PUP_ID = 0;\n// NOLINTEND\n/// \\endcond\n\n}  // namespace LinearSolver::Serial\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/Gmres.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearSolver/Gmres.hpp\"\n\n#include <blaze/math/Column.h>\n#include <blaze/math/DynamicMatrix.h>\n#include <blaze/math/DynamicVector.h>\n#include <blaze/math/Submatrix.h>\n#include <blaze/math/Subvector.h>\n#include <blaze/math/lapack/trsv.h>\n#include <complex>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace LinearSolver::gmres::detail {\n\nnamespace {\n\ntemplate <typename ValueType>\nvoid update_givens_rotation(const gsl::not_null<ValueType*> givens_cosine,\n                            const gsl::not_null<double*> givens_sine,\n                            const ValueType rho, const double sigma) {\n  if (UNLIKELY(rho == 0.)) {\n    *givens_cosine = 0.;\n    *givens_sine = 1.;\n  } else {\n    const double tmp = sqrt(square(abs(rho)) + square(sigma));\n    *givens_cosine = rho / tmp;\n    *givens_sine = sigma / tmp;\n  }\n}\n\ntemplate <typename ValueType, typename Arg>\nvoid apply_and_update_givens_rotation(\n    Arg argument,\n    const gsl::not_null<blaze::DynamicVector<double>*> givens_sine_history,\n    const gsl::not_null<blaze::DynamicVector<ValueType>*> givens_cosine_history,\n    const size_t iteration) {\n  const size_t k = iteration + 1;\n  for (size_t i = 0; i < k - 1; ++i) {\n    const ValueType tmp = (*givens_cosine_history)[i] * argument[i] +\n                          (*givens_sine_history)[i] * argument[i + 1];\n    argument[i + 1] = (*givens_cosine_history)[i] * argument[i + 1] -\n                      (*givens_sine_history)[i] * argument[i];\n    argument[i] = tmp;\n  }\n  // Note: argument[k] is real, since it is a normalization\n  ASSERT(equal_within_roundoff(std::imag(argument[k]), 0.),\n         \"Normalization is not real: \" << argument[k]);\n  update_givens_rotation(make_not_null(&(*givens_cosine_history)[k - 1]),\n                         make_not_null(&(*givens_sine_history)[k - 1]),\n                         argument[k - 1], std::real(argument[k]));\n  argument[k - 1] = (*givens_cosine_history)[k - 1] * argument[k - 1] +\n                    (*givens_sine_history)[k - 1] * argument[k];\n  argument[k] = 0.;\n}\n\n}  // namespace\n\ntemplate <typename ValueType>\nvoid solve_minimal_residual(\n    const gsl::not_null<blaze::DynamicMatrix<ValueType>*>\n        orthogonalization_history,\n    const gsl::not_null<blaze::DynamicVector<ValueType>*> residual_history,\n    const gsl::not_null<blaze::DynamicVector<double>*> givens_sine_history,\n    const gsl::not_null<blaze::DynamicVector<ValueType>*> givens_cosine_history,\n    const size_t iteration) {\n  residual_history->resize(iteration + 2);\n  givens_sine_history->resize(iteration + 1);\n  givens_cosine_history->resize(iteration + 1);\n  // Givens-rotate the `orthogonalization_history` to eliminate its lower-right\n  // entry, making it an upper triangular matrix.\n  // Thus, we iteratively update the QR decomposition of the Hessenberg matrix\n  // that is built through orthogonalization of the Krylov basis vectors.\n  apply_and_update_givens_rotation(\n      blaze::column(*orthogonalization_history, iteration), givens_sine_history,\n      givens_cosine_history, iteration);\n  // Also Givens-rotate the `residual_history`. The vector excluding its last\n  // entry represents the accumulated Givens-rotation applied to the vector\n  // `(initial_residual, 0, 0, ...)` and the last entry represents the\n  // remaining residual.\n  (*residual_history)[iteration + 1] =\n      -(*givens_sine_history)[iteration] * (*residual_history)[iteration];\n  (*residual_history)[iteration] =\n      (*givens_cosine_history)[iteration] * (*residual_history)[iteration];\n}\n\ntemplate <typename ValueType>\nblaze::DynamicVector<ValueType> minimal_residual_vector(\n    const blaze::DynamicMatrix<ValueType>& orthogonalization_history,\n    const blaze::DynamicVector<ValueType>& residual_history) {\n  const size_t length = orthogonalization_history.columns();\n  blaze::DynamicVector<ValueType> minres =\n      blaze::subvector(residual_history, 0, length);\n  blaze::trsv(blaze::submatrix(orthogonalization_history, 0, 0, length, length),\n              minres, 'U', 'N', 'N');\n  return minres;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(r, data)                                          \\\n  template void solve_minimal_residual(                               \\\n      gsl::not_null<blaze::DynamicMatrix<DTYPE(data)>*>,              \\\n      gsl::not_null<blaze::DynamicVector<DTYPE(data)>*>,              \\\n      gsl::not_null<blaze::DynamicVector<double>*>,                   \\\n      gsl::not_null<blaze::DynamicVector<DTYPE(data)>*>, size_t);     \\\n  template blaze::DynamicVector<DTYPE(data)> minimal_residual_vector( \\\n      const blaze::DynamicMatrix<DTYPE(data)>&,                       \\\n      const blaze::DynamicVector<DTYPE(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, std::complex<double>))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace LinearSolver::gmres::detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/Gmres.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/LinearSolver.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Registration.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\nnamespace LinearSolver {\nnamespace gmres::detail {\n\n// Perform an Arnoldi orthogonalization to find a new `operand` that is\n// orthogonal to all vectors in `basis_history`. Appends a new column to the\n// `orthogonalization_history` that holds the inner product of the intermediate\n// `operand` with each vector in the `basis_history` and itself.\ntemplate <typename VarsType>\nvoid arnoldi_orthogonalize(\n    const gsl::not_null<VarsType*> operand,\n    const gsl::not_null<\n        blaze::DynamicMatrix<tt::get_complex_or_fundamental_type_t<VarsType>>*>\n        orthogonalization_history,\n    const std::vector<VarsType>& basis_history, const size_t iteration) {\n  // Resize matrix and make sure the new entries that are not being filled below\n  // are zero.\n  orthogonalization_history->resize(iteration + 2, iteration + 1);\n  for (size_t j = 0; j < iteration; ++j) {\n    (*orthogonalization_history)(iteration + 1, j) = 0.;\n  }\n  // Arnoldi orthogonalization\n  for (size_t j = 0; j < iteration + 1; ++j) {\n    const auto orthogonalization = inner_product(basis_history[j], *operand);\n    (*orthogonalization_history)(j, iteration) = orthogonalization;\n    *operand -= orthogonalization * basis_history[j];\n  }\n  const double normalization = sqrt(magnitude_square(*operand));\n  (*orthogonalization_history)(iteration + 1, iteration) = normalization;\n  // Avoid an FPE if the new operand norm is exactly zero. In that case the\n  // problem is solved and the algorithm will terminate (see Proposition 9.3 in\n  // \\cite Saad2003). Since there will be no next iteration we don't need to\n  // normalize the operand.\n  if (UNLIKELY(normalization == 0.)) {\n    return;\n  }\n  *operand /= normalization;\n}\n\n// Solve the linear least-squares problem `||beta - H * y||` for `y`, where `H`\n// is the Hessenberg matrix given by `orthogonalization_history` and `beta` is\n// the vector `(initial_residual, 0, 0, ...)` by updating the QR decomposition\n// of `H` from the previous iteration with a Givens rotation.\ntemplate <typename ValueType>\nvoid solve_minimal_residual(\n    gsl::not_null<blaze::DynamicMatrix<ValueType>*> orthogonalization_history,\n    gsl::not_null<blaze::DynamicVector<ValueType>*> residual_history,\n    gsl::not_null<blaze::DynamicVector<double>*> givens_sine_history,\n    gsl::not_null<blaze::DynamicVector<ValueType>*> givens_cosine_history,\n    size_t iteration);\n\n// Find the vector that minimizes the residual by inverting the upper\n// triangular matrix obtained above.\ntemplate <typename ValueType>\nblaze::DynamicVector<ValueType> minimal_residual_vector(\n    const blaze::DynamicMatrix<ValueType>& orthogonalization_history,\n    const blaze::DynamicVector<ValueType>& residual_history);\n\n}  // namespace gmres::detail\n\nnamespace Serial {\n\n/// Disables the iteration callback at compile-time\nstruct NoIterationCallback {};\n\n/// \\cond\ntemplate <typename VarsType, typename Preconditioner,\n          typename LinearSolverRegistrars>\nstruct Gmres;\n/// \\endcond\n\nnamespace Registrars {\n\n/// Registers the `LinearSolver::Serial::Gmres` linear solver.\ntemplate <typename VarsType>\nstruct Gmres {\n  template <typename LinearSolverRegistrars>\n  using f = Serial::Gmres<VarsType, LinearSolver<LinearSolverRegistrars>,\n                          LinearSolverRegistrars>;\n};\n}  // namespace Registrars\n\n/*!\n * \\brief A serial GMRES iterative solver for nonsymmetric linear systems of\n * equations.\n *\n * This is an iterative algorithm to solve general linear equations \\f$Ax=b\\f$\n * where \\f$A\\f$ is a linear operator. See \\cite Saad2003, chapter 6.5 for a\n * description of the GMRES algorithm, Algorithm 9.6 for this implementation,\n * and Sec. 6.5.9 for the extension to complex arithmetic.\n * The GMRES method is matrix-free, which means the operator \\f$A\\f$ needs not\n * be provided explicitly as a matrix but only the operator action \\f$A(x)\\f$\n * must be provided for an argument \\f$x\\f$.\n *\n * The GMRES algorithm does not require the operator \\f$A\\f$ to be symmetric or\n * positive-definite. Note that other algorithms such as conjugate gradients may\n * be more efficient for symmetric positive-definite operators.\n *\n * \\par Convergence:\n * Given a set of \\f$N_A\\f$ equations (e.g. through an \\f$N_A\\times N_A\\f$\n * matrix) the GMRES algorithm will converge to numerical precision in at most\n * \\f$N_A\\f$ iterations. However, depending on the properties of the linear\n * operator, an approximate solution can ideally be obtained in only a few\n * iterations. See \\cite Saad2003, section 6.11.4 for details on the convergence\n * of the GMRES algorithm.\n *\n * \\par Restarting:\n * This implementation of the GMRES algorithm supports restarting, as detailed\n * in \\cite Saad2003, section 6.5.5. Since the GMRES algorithm iteratively\n * builds up an orthogonal basis of the solution space the cost of each\n * iteration increases linearly with the number of iterations. Therefore it is\n * sometimes helpful to restart the algorithm every \\f$N_\\mathrm{restart}\\f$\n * iterations, discarding the set of basis vectors and starting again from the\n * current solution estimate. This strategy can improve the performance of the\n * solver, but note that the solver can stagnate for non-positive-definite\n * operators and is not guaranteed to converge within \\f$N_A\\f$ iterations\n * anymore. Set the `restart` argument of the constructor to\n * \\f$N_\\mathrm{restart}\\f$ to activate restarting, or set it to 'None' to\n * deactivate restarting.\n *\n * \\par Preconditioning:\n * This implementation of the GMRES algorithm also supports preconditioning.\n * You can provide a linear operator \\f$P\\f$ that approximates the inverse of\n * the operator \\f$A\\f$ to accelerate the convergence of the linear solve.\n * The algorithm is right-preconditioned, which allows the preconditioner to\n * change in every iteration (\"flexible\" variant). See \\cite Saad2003, sections\n * 9.3.2 and 9.4.1 for details. This implementation follows Algorithm 9.6 in\n * \\cite Saad2003.\n *\n * \\par Improvements:\n * Further improvements can potentially be implemented for this algorithm, see\n * e.g. \\cite Ayachour2003.\n *\n * \\example\n * \\snippet NumericalAlgorithms/LinearSolver/Test_Gmres.cpp gmres_example\n *\n * \\tparam VarsType The type of the vectors `x` and `b` in the linear equation\n * $Ax=b$. This type must support addition, subtraction, scalar multiplication,\n * and an inner product. It must expose an `ElementType` alias that is the type\n * of the components of the vector, e.g. `double` or `std::complex<double>`.\n */\ntemplate <typename VarsType, typename Preconditioner = NoPreconditioner,\n          typename LinearSolverRegistrars =\n              tmpl::list<Registrars::Gmres<VarsType>>>\nclass Gmres final : public PreconditionedLinearSolver<Preconditioner,\n                                                      LinearSolverRegistrars> {\n private:\n  using Base =\n      PreconditionedLinearSolver<Preconditioner, LinearSolverRegistrars>;\n  using ValueType = tt::get_complex_or_fundamental_type_t<VarsType>;\n\n  struct ConvergenceCriteria {\n    using type = Convergence::Criteria;\n    static constexpr Options::String help =\n        \"Determine convergence of the algorithm\";\n  };\n  struct Restart {\n    using type = Options::Auto<size_t, Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        \"Iterations to run before restarting, or 'None' to disable restarting. \"\n        \"Note that the solver is not guaranteed to converge anymore if you \"\n        \"enable restarting.\";\n    static type suggested_value() { return {}; }\n  };\n  struct Verbosity {\n    using type = ::Verbosity;\n    static constexpr Options::String help = \"Logging verbosity\";\n  };\n\n public:\n  static constexpr Options::String help =\n      \"A serial GMRES iterative solver for nonsymmetric linear systems of\\n\"\n      \"equations Ax=b. It will converge to numerical precision in at most N_A\\n\"\n      \"iterations, where N_A is the number of equations represented by the\\n\"\n      \"linear operator A, but will ideally converge to a reasonable\\n\"\n      \"approximation of the solution x in only a few iterations.\\n\"\n      \"\\n\"\n      \"Preconditioning: Specify a preconditioner to run in every GMRES \"\n      \"iteration to accelerate the solve, or 'None' to disable \"\n      \"preconditioning. The choice of preconditioner can be crucial to obtain \"\n      \"good convergence.\\n\"\n      \"\\n\"\n      \"Restarting: It is sometimes helpful to restart the algorithm every\\n\"\n      \"N_restart iterations to speed it up. Note that it can stagnate for\\n\"\n      \"non-positive-definite matrices and is not guaranteed to converge\\n\"\n      \"within N_A iterations anymore when restarting is activated.\\n\"\n      \"Activate restarting by setting the 'Restart' option to N_restart, or\\n\"\n      \"deactivate restarting by setting it to 'None'.\";\n  using options = tmpl::flatten<tmpl::list<\n      ConvergenceCriteria, Verbosity, Restart,\n      tmpl::conditional_t<std::is_same_v<Preconditioner, NoPreconditioner>,\n                          tmpl::list<>, typename Base::PreconditionerOption>>>;\n\n  Gmres(Convergence::Criteria convergence_criteria, ::Verbosity verbosity,\n        std::optional<size_t> restart = std::nullopt,\n        std::optional<typename Base::PreconditionerType> local_preconditioner =\n            std::nullopt,\n        const Options::Context& context = {});\n\n  Gmres() = default;\n  Gmres(Gmres&&) = default;\n  Gmres& operator=(Gmres&&) = default;\n  ~Gmres() override = default;\n\n  Gmres(const Gmres& rhs);\n  Gmres& operator=(const Gmres& rhs);\n\n  std::unique_ptr<LinearSolver<LinearSolverRegistrars>> get_clone()\n      const override {\n    return std::make_unique<Gmres>(*this);\n  }\n\n  /// \\cond\n  explicit Gmres(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Gmres);  // NOLINT\n  /// \\endcond\n\n  void initialize() {\n    orthogonalization_history_.reserve(restart_ + 1);\n    residual_history_.reserve(restart_ + 1);\n    givens_sine_history_.reserve(restart_);\n    givens_cosine_history_.reserve(restart_);\n    basis_history_.resize(restart_ + 1);\n    preconditioned_basis_history_.resize(restart_);\n  }\n\n  const Convergence::Criteria& convergence_criteria() const {\n    return convergence_criteria_;\n  }\n  ::Verbosity verbosity() const { return verbosity_; }\n  size_t restart() const { return restart_; }\n\n  void pup(PUP::er& p) override {  // NOLINT\n    Base::pup(p);\n    p | convergence_criteria_;\n    p | verbosity_;\n    p | restart_;\n    if (p.isUnpacking()) {\n      initialize();\n    }\n  }\n\n  template <typename LinearOperator, typename SourceType,\n            typename... OperatorArgs,\n            typename IterationCallback = NoIterationCallback>\n  Convergence::HasConverged solve(\n      gsl::not_null<VarsType*> initial_guess_in_solution_out,\n      const LinearOperator& linear_operator, const SourceType& source,\n      const std::tuple<OperatorArgs...>& operator_args = std::tuple{},\n      const IterationCallback& iteration_callback =\n          NoIterationCallback{}) const;\n\n  void reset() override {\n    // Nothing to reset. Only call into base class to reset preconditioner.\n    Base::reset();\n  }\n\n private:\n  Convergence::Criteria convergence_criteria_{};\n  ::Verbosity verbosity_{::Verbosity::Verbose};\n  size_t restart_{};\n\n  // Memory buffers to avoid re-allocating memory for successive solves:\n  // The `orthogonalization_history_` is built iteratively from inner products\n  // between existing and potential basis vectors and then Givens-rotated to\n  // become upper-triangular.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable blaze::DynamicMatrix<ValueType> orthogonalization_history_{};\n  // The `residual_history_` holds the remaining residual in its last entry, and\n  // the other entries `g` \"source\" the minimum residual vector `y` in\n  // `R * y = g` where `R` is the upper-triangular `orthogonalization_history_`.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable blaze::DynamicVector<ValueType> residual_history_{};\n  // These represent the accumulated Givens rotations up to the current\n  // iteration.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable blaze::DynamicVector<double> givens_sine_history_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable blaze::DynamicVector<ValueType> givens_cosine_history_{};\n  // These represent the orthogonal Krylov-subspace basis that is constructed\n  // iteratively by Arnoldi-orthogonalizing a new vector in each iteration and\n  // appending it to the `basis_history_`.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::vector<VarsType> basis_history_{};\n  // When a preconditioner is used it is applied to each new basis vector. The\n  // preconditioned basis is used to construct the solution when the algorithm\n  // has converged.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::vector<VarsType> preconditioned_basis_history_{};\n};\n\ntemplate <typename VarsType, typename Preconditioner,\n          typename LinearSolverRegistrars>\nGmres<VarsType, Preconditioner, LinearSolverRegistrars>::Gmres(\n    Convergence::Criteria convergence_criteria, ::Verbosity verbosity,\n    std::optional<size_t> restart,\n    std::optional<typename Base::PreconditionerType> local_preconditioner,\n    const Options::Context& context)\n    // clang-tidy: trivially copyable\n    : Base(std::move(local_preconditioner)),\n      convergence_criteria_(std::move(convergence_criteria)),  // NOLINT\n      verbosity_(std::move(verbosity)),                        // NOLINT\n      restart_(restart.value_or(convergence_criteria_.max_iterations)) {\n  if (restart_ == 0) {\n    PARSE_ERROR(context,\n                \"Can't restart every '0' iterations. Set to a nonzero \"\n                \"number, or to 'None' if you meant to disable restarting.\");\n  }\n  initialize();\n}\n\n// Define copy constructors. They don't have to copy the memory buffers but\n// only resize them. They take care of copying the preconditioner by calling\n// into the base class.\ntemplate <typename VarsType, typename Preconditioner,\n          typename LinearSolverRegistrars>\nGmres<VarsType, Preconditioner, LinearSolverRegistrars>::Gmres(const Gmres& rhs)\n    : Base(rhs),\n      convergence_criteria_(rhs.convergence_criteria_),\n      verbosity_(rhs.verbosity_),\n      restart_(rhs.restart_) {\n  initialize();\n}\ntemplate <typename VarsType, typename Preconditioner,\n          typename LinearSolverRegistrars>\nGmres<VarsType, Preconditioner, LinearSolverRegistrars>&\nGmres<VarsType, Preconditioner, LinearSolverRegistrars>::operator=(\n    const Gmres& rhs) {\n  Base::operator=(rhs);\n  convergence_criteria_ = rhs.convergence_criteria_;\n  verbosity_ = rhs.verbosity_;\n  restart_ = rhs.restart_;\n  initialize();\n  return *this;\n}\n\n/// \\cond\ntemplate <typename VarsType, typename Preconditioner,\n          typename LinearSolverRegistrars>\nGmres<VarsType, Preconditioner, LinearSolverRegistrars>::Gmres(\n    CkMigrateMessage* m)\n    : Base(m) {}\n/// \\endcond\n\ntemplate <typename VarsType, typename Preconditioner,\n          typename LinearSolverRegistrars>\ntemplate <typename LinearOperator, typename SourceType,\n          typename... OperatorArgs, typename IterationCallback>\nConvergence::HasConverged\nGmres<VarsType, Preconditioner, LinearSolverRegistrars>::solve(\n    const gsl::not_null<VarsType*> initial_guess_in_solution_out,\n    const LinearOperator& linear_operator, const SourceType& source,\n    const std::tuple<OperatorArgs...>& operator_args,\n    const IterationCallback& iteration_callback) const {\n  constexpr bool use_preconditioner =\n      not std::is_same_v<Preconditioner, NoPreconditioner>;\n  constexpr bool use_iteration_callback =\n      not std::is_same_v<IterationCallback, NoIterationCallback>;\n\n  // Could pre-allocate memory for the basis-history vectors here. Not doing\n  // that for now because we don't know how many iterations we'll need.\n  // Estimating the number of iterations and pre-allocating memory is a possible\n  // performance optimization.\n\n  auto& solution = *initial_guess_in_solution_out;\n  Convergence::HasConverged has_converged{};\n  size_t iteration = 0;\n\n  while (not has_converged) {\n    const auto& initial_guess = *initial_guess_in_solution_out;\n    auto& initial_operand = basis_history_[0];\n    // Apply the linear operator to the initial guess. This can be skipped if\n    // the initial guess is zero, because then the linear operator applied to it\n    // is also zero.\n    if (equal_within_roundoff(initial_guess, 0.)) {\n      initial_operand = source;\n    } else {\n      std::apply(\n          linear_operator,\n          std::tuple_cat(std::forward_as_tuple(make_not_null(&initial_operand),\n                                               initial_guess),\n                         operator_args));\n      initial_operand *= -1.;\n      initial_operand += source;\n    }\n    const double initial_residual_magnitude =\n        sqrt(magnitude_square(initial_operand));\n    has_converged = Convergence::HasConverged{convergence_criteria_, iteration,\n                                              initial_residual_magnitude,\n                                              initial_residual_magnitude};\n    if constexpr (use_iteration_callback) {\n      iteration_callback(has_converged);\n    }\n    if (UNLIKELY(has_converged)) {\n      break;\n    }\n    initial_operand /= initial_residual_magnitude;\n    residual_history_.resize(1);\n    residual_history_[0] = initial_residual_magnitude;\n    for (size_t k = 0; k < restart_; ++k) {\n      auto& operand = basis_history_[k + 1];\n      if constexpr (use_preconditioner) {\n        if (this->has_preconditioner()) {\n          // Begin the preconditioner at an initial guess of 0. Not all\n          // preconditioners take the initial guess into account.\n          preconditioned_basis_history_[k] =\n              make_with_value<VarsType>(initial_operand, 0.);\n          this->preconditioner().solve(\n              make_not_null(&preconditioned_basis_history_[k]), linear_operator,\n              basis_history_[k], operator_args);\n        }\n      }\n      std::apply(linear_operator,\n                 std::tuple_cat(std::forward_as_tuple(\n                                    make_not_null(&operand),\n                                    this->has_preconditioner()\n                                        ? preconditioned_basis_history_[k]\n                                        : basis_history_[k]),\n                                operator_args));\n      // Find a new orthogonal basis vector of the Krylov subspace\n      gmres::detail::arnoldi_orthogonalize(\n          make_not_null(&operand), make_not_null(&orthogonalization_history_),\n          basis_history_, k);\n      // Least-squares solve for the minimal residual\n      gmres::detail::solve_minimal_residual(\n          make_not_null(&orthogonalization_history_),\n          make_not_null(&residual_history_),\n          make_not_null(&givens_sine_history_),\n          make_not_null(&givens_cosine_history_), k);\n      ++iteration;\n      has_converged = Convergence::HasConverged{\n          convergence_criteria_, iteration, abs(residual_history_[k + 1]),\n          initial_residual_magnitude};\n      if constexpr (use_iteration_callback) {\n        iteration_callback(has_converged);\n      }\n      if (UNLIKELY(has_converged)) {\n        break;\n      }\n    }\n    // Find the vector w.r.t. the constructed orthogonal basis of the Krylov\n    // subspace that minimizes the residual\n    const auto minres = gmres::detail::minimal_residual_vector(\n        orthogonalization_history_, residual_history_);\n    // Construct the solution from the orthogonal basis and the minimal residual\n    // vector\n    for (size_t i = 0; i < minres.size(); ++i) {\n      solution += minres[i] * gsl::at(this->has_preconditioner()\n                                          ? preconditioned_basis_history_\n                                          : basis_history_,\n                                      i);\n    }\n  }\n  return has_converged;\n}\n\n/// \\cond\ntemplate <typename VarsType, typename Preconditioner,\n          typename LinearSolverRegistrars>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID\n    Gmres<VarsType, Preconditioner, LinearSolverRegistrars>::my_PUP_ID = 0;\n/// \\endcond\n\n}  // namespace Serial\n}  // namespace LinearSolver\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/InnerProduct.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Defines an inner product for the linear solver\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n\nnamespace LinearSolver {\n\n/// \\ingroup LinearSolverGroup\n/// Implementations of LinearSolver::inner_product.\nnamespace InnerProductImpls {\n\n/// The inner product between any types that have a `dot` product\ntemplate <typename Lhs, typename Rhs>\nstruct InnerProductImpl {\n  static auto apply(const Lhs& lhs, const Rhs& rhs) {\n    return dot(conj(lhs), rhs);\n  }\n};\n\n/// The inner product between `Variables`\ntemplate <typename LhsTagsList, typename RhsTagsList>\nstruct InnerProductImpl<Variables<LhsTagsList>, Variables<RhsTagsList>> {\n  using ValueType = typename Variables<LhsTagsList>::value_type;\n  static ValueType apply(const Variables<LhsTagsList>& lhs,\n                         const Variables<RhsTagsList>& rhs) {\n    const auto size = lhs.size();\n    ASSERT(size == rhs.size(),\n           \"The Variables must be of the same size to take an inner product\");\n    if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n      return zdotc_(size, lhs.data(), 1, rhs.data(), 1);\n    } else {\n      return ddot_(size, lhs.data(), 1, rhs.data(), 1);\n    }\n  }\n};\n\n}  // namespace InnerProductImpls\n\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief The local part of the Euclidean inner product on the vector space\n * w.r.t. which the addition and scalar multiplication of both `Lhs` and `Rhs`\n * is defined.\n *\n * \\details The linear solver works under the following assumptions:\n * - The data represented by \\p lhs and \\p rhs can each be interpreted as the\n * local chunk of a vector of the same vector space \\f$V\\f$. _Local_ means there\n * are vectors \\f$q, p\\in V\\f$ such that \\p lhs and \\p rhs represent the\n * components of these vectors w.r.t. a subset \\f$B_i\\f$ of a basis\n * \\f$B\\subset V\\f$.\n * - The `*` and `+` operators of `Lhs` and `Rhs` implement the scalar\n * multiplication and addition in the vector space _locally_, i.e. restricted to\n * \\f$B_i\\f$ in the above sense.\n * - The inner product is the local part \\f$\\langle p,q\\rangle|_{B_i}\\f$ of the\n * standard Euclidean dot product in the vector space so that globally it is\n * \\f$\\langle p,q\\rangle=\\sum_{i}\\langle p,q\\rangle|_{B_i}\\f$ for\n * \\f$B=\\mathop{\\dot{\\bigcup}}_i B_i\\f$.\n *\n * In practice this means that the full vectors \\f$p\\f$ and \\f$q\\f$ can be\n * distributed on many elements, where each only holds local chunks \\p lhs and\n * \\p rhs of the components. Scalar multiplication and addition can be performed\n * locally as expected, but computing the full inner product requires a global\n * reduction over all elements that sums their local `inner_product`s.\n */\ntemplate <typename Lhs, typename Rhs>\nSPECTRE_ALWAYS_INLINE auto inner_product(const Lhs& lhs, const Rhs& rhs) {\n  return InnerProductImpls::InnerProductImpl<Lhs, Rhs>::apply(lhs, rhs);\n}\n\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief The local part of the Euclidean inner product of a vector with itself.\n *\n * \\see LinearSolver::inner_product for details on the inner product. This\n * function just returns the inner product of a vector with itself, and ensures\n * that the result is real.\n */\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE double magnitude_square(const T& vector) {\n  auto result =\n      InnerProductImpls::InnerProductImpl<T, T>::apply(vector, vector);\n  if constexpr (std::is_same_v<std::decay_t<decltype(result)>,\n                               std::complex<double>>) {\n    ASSERT(equal_within_roundoff(imag(result), 0.0, 1e-14, abs(result)),\n           \"The magnitude squared is not real: \" << result);\n    return real(result);\n  } else {\n    return result;\n  }\n}\n\n}  // namespace LinearSolver\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/Lapack.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/LinearSolver/Lapack.hpp\"\n\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nextern \"C\" {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wredundant-decls\"\nextern void dgesv_(int*, int*, double*, int*, int*, double*, int*,  // NOLINT\n                   int*);\n#pragma GCC diagnostic pop\n}\n\nnamespace lapack {\nint general_matrix_linear_solve(\n    const gsl::not_null<DataVector*> rhs_in_solution_out,\n    const gsl::not_null<Matrix*> matrix_operator, const int number_of_rhs) {\n  const size_t rhs_vector_size = matrix_operator->rows();\n  std::vector<int> ipiv(rhs_vector_size);\n  return general_matrix_linear_solve(rhs_in_solution_out, make_not_null(&ipiv),\n                                     matrix_operator, number_of_rhs);\n}\n\nint general_matrix_linear_solve(\n    const gsl::not_null<DataVector*> rhs_in_solution_out,\n    const gsl::not_null<std::vector<int>*> pivots,\n    const gsl::not_null<Matrix*> matrix_operator, int number_of_rhs) {\n  int output_vector_size = matrix_operator->columns();\n  int rhs_vector_size = matrix_operator->rows();\n  int matrix_spacing = matrix_operator->spacing();\n  ASSERT(output_vector_size == rhs_vector_size,\n         \"The LAPACK-based general linear solve requires a square matrix \"\n         \"input, not \"\n             << rhs_vector_size << \" by \" << output_vector_size);\n\n  if (number_of_rhs == 0) {\n    ASSERT(\n        (rhs_in_solution_out->size() % matrix_operator->rows()) ==\n            0_st,\n        \"The provided DataVector does not have size equal to (number of \"\n        \"equations) * (larger matrix dimension), so the number of right-hand \"\n        \"sides cannot be inferred and must be provided explicitly\");\n    number_of_rhs = static_cast<int>(\n        rhs_in_solution_out->size() /\n        std::max(matrix_operator->rows(), matrix_operator->columns()));\n  }\n  int info = 0;\n  ASSERT(static_cast<size_t>(rhs_vector_size * number_of_rhs) <=\n                 static_cast<size_t>(rhs_in_solution_out->size()) and\n             static_cast<size_t>(output_vector_size * number_of_rhs) <=\n                 static_cast<size_t>(rhs_in_solution_out->size()),\n         \"The single DataVector passed to the LAPACK call must be sufficiently \"\n         \"large to contain x and b in A x = b\");\n  dgesv_(&rhs_vector_size, &number_of_rhs, matrix_operator->data(),\n         &matrix_spacing, pivots->data(), rhs_in_solution_out->data(),\n         &output_vector_size, &info);\n  return info;\n}\n\nint general_matrix_linear_solve(const gsl::not_null<DataVector*> solution,\n                                const gsl::not_null<Matrix*> matrix_operator,\n                                const DataVector& rhs,\n                                const int number_of_rhs) {\n  const size_t rhs_vector_size = matrix_operator->rows();\n  std::vector<int> ipiv(rhs_vector_size);\n  return general_matrix_linear_solve(solution, make_not_null(&ipiv),\n                                     matrix_operator, rhs, number_of_rhs);\n}\n\nint general_matrix_linear_solve(const gsl::not_null<DataVector*> solution,\n                                const gsl::not_null<std::vector<int>*> pivots,\n                                const gsl::not_null<Matrix*> matrix_operator,\n                                const DataVector& rhs, int number_of_rhs) {\n  // NOLINTNEXTLINE(clang-analyzer-deadcode)\n  const int output_vector_size = matrix_operator->columns();\n  const int rhs_vector_size = matrix_operator->rows();\n  if (number_of_rhs == 0) {\n    ASSERT(rhs.size() % matrix_operator->rows() == 0,\n           \"The provided DataVector does not have size equal to (number of \"\n           \"equations) * (number_of_matrix_rows), so the number of right-hand \"\n           \"sides cannot be inferred and must be provided explicitly\");\n    number_of_rhs = static_cast<int>(rhs.size() / matrix_operator->rows());\n  }\n  ASSERT(solution->size() >=\n             static_cast<size_t>(number_of_rhs) * matrix_operator->columns(),\n         \"The provided pointer for the output of the LAPACK linear solve is \"\n         \"too small for the operation. Solution size is: \"\n             << solution->size() << \" and should be: \"\n             << number_of_rhs * output_vector_size << \".\");\n  ASSERT(rhs.size() >=\n             static_cast<size_t>(number_of_rhs) * matrix_operator->rows(),\n         \"The provided pointer for the input right-hand side vector of the \"\n         \"LAPACK linear solve is too small for the operation. Vector size is: \"\n             << rhs.size()\n             << \" and should be: \" << number_of_rhs * rhs_vector_size << \".\");\n  std::copy(rhs.begin(), rhs.begin() + number_of_rhs * rhs_vector_size,\n            solution->begin());\n  return general_matrix_linear_solve(solution, pivots, matrix_operator,\n                                     number_of_rhs);\n}\n\nint general_matrix_linear_solve(\n    const gsl::not_null<DataVector*> rhs_in_solution_out,\n    const Matrix& matrix_operator, const int number_of_rhs) {\n  // LAPACK is permitted to modify the matrix in-place, so we copy before\n  // providing the operator if the original must be preserved.\n  Matrix copied_matrix_operator = matrix_operator;\n  return general_matrix_linear_solve(rhs_in_solution_out,\n                                     make_not_null(&copied_matrix_operator),\n                                     number_of_rhs);\n}\n\nint general_matrix_linear_solve(const gsl::not_null<DataVector*> solution,\n                                const Matrix& matrix_operator,\n                                const DataVector& rhs,\n                                const int number_of_rhs) {\n  // LAPACK is permitted to modify the matrix in-place, so we copy before\n  // providing the operator if the original must be preserved.\n  Matrix copied_matrix_operator = matrix_operator;\n  return general_matrix_linear_solve(\n      solution, make_not_null(&copied_matrix_operator), rhs, number_of_rhs);\n}\n\n}  // namespace lapack\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/Lapack.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\nclass Matrix;\n/// \\endcond\n\n/// LAPACK wrappers\nnamespace lapack {\n/// @{\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief Wrapper for LAPACK dgesv, which solves the general linear equation\n * \\f$A x = b\\f$ by LUP (Lower-triangular, upper-triangular, and permutation)\n * decomposition.\n * \\details Several interfaces are provided with combinations of\n * input parameters due to the versatility of the LAPACK utility.\n * - `rhs_in_solution_out` or `solution` : If the `rhs` `DataVector` is not\n * separately supplied, this single pass-by-pointer `DataVector` acts as the\n * input \\f$ b\\f$, and is overwritten with the solution \\f$x\\f$; in this case\n * `rhs_in_solution_out` must be sufficiently large to contain the solution. If\n * `rhs` is separately supplied, `solution` will contain \\f$x\\f$ at the end of\n * the algorithm, and must be sufficiently large to contain the solution.\n * - `rhs` (optional) : An input `DataVector` representing \\f$b\\f$ that is not\n * overwritten.\n * - `matrix_operator`: The `Matrix` \\f$A\\f$. If passed by pointer, this matrix\n * will be 'destroyed' and overwritten with LU decomposition information.\n * Optionally, a `pivots` vector may be passed by pointer so that the full LUP\n * decomposition information can be recovered from the LAPACK output. If passed\n * by const reference, the wrapper will make a copy so that LAPACK does not\n * overwrite the supplied matrix.\n * - `pivots` (optional) : a `std::vector<int>`, passed by pointer, which will\n * contain the permutation information necessary to reassemble the full matrix\n * information if LAPACK is permitted to modify the input matrix in-place.\n * - `number_of_rhs` : The number of columns of \\f$x\\f$ and \\f$b\\f$. This is\n * thought of as the 'number of right-hand-sides' to be solved for. In most\n * cases, that additional dimension can be inferred from the dimensionality of\n * the matrix and the input vector(s), which occurs if `number_of_rhs` is set to\n * 0 (default). If the provided `DataVector` is an inappropriate size for that\n * inference (i.e. is not a multiple of the largest dimension of the matrix), or\n * if you only want to solve for the first `number_of_rhs` columns, the\n * parameter `number_of_rhs` must be supplied.\n *\n * The function return `int` is the value provided by the `INFO` field of the\n * LAPACK call. It is 0 for a successful linear solve, and nonzero values code\n * for types of failures of the algorithm.\n * See LAPACK documentation for further details about `dgesv`:\n * http://www.netlib.org/lapack/\n */\nint general_matrix_linear_solve(gsl::not_null<DataVector*> rhs_in_solution_out,\n                                gsl::not_null<Matrix*> matrix_operator,\n                                int number_of_rhs = 0);\n\nint general_matrix_linear_solve(gsl::not_null<DataVector*> rhs_in_solution_out,\n                                gsl::not_null<std::vector<int>*> pivots,\n                                gsl::not_null<Matrix*> matrix_operator,\n                                int number_of_rhs = 0);\n\nint general_matrix_linear_solve(gsl::not_null<DataVector*> solution,\n                                gsl::not_null<Matrix*> matrix_operator,\n                                const DataVector& rhs, int number_of_rhs = 0);\n\nint general_matrix_linear_solve(gsl::not_null<DataVector*> solution,\n                                gsl::not_null<std::vector<int>*> pivots,\n                                gsl::not_null<Matrix*> matrix_operator,\n                                const DataVector& rhs, int number_of_rhs = 0);\n\nint general_matrix_linear_solve(gsl::not_null<DataVector*> rhs_in_solution_out,\n                                const Matrix& matrix_operator,\n                                int number_of_rhs = 0);\n\nint general_matrix_linear_solve(gsl::not_null<DataVector*> solution,\n                                const Matrix& matrix_operator,\n                                const DataVector& rhs, int number_of_rhs = 0);\n/// @}\n}  // namespace lapack\n"
  },
  {
    "path": "src/NumericalAlgorithms/LinearSolver/LinearSolver.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <type_traits>\n#include <utility>\n\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Registration.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace LinearSolver::Serial {\n\n/// Registrars for linear solvers\nnamespace Registrars {}\n\n/*!\n * \\brief Base class for serial linear solvers that supports factory-creation.\n *\n * Derive linear solvers from this class so they can be factory-created. If your\n * linear solver supports preconditioning, derive from\n * `PreconditionedLinearSolver` instead to inherit utility that allows using any\n * other factor-creatable linear solver as preconditioner.\n */\ntemplate <typename LinearSolverRegistrars>\nclass LinearSolver : public PUP::able {\n protected:\n  /// \\cond\n  LinearSolver() = default;\n  LinearSolver(const LinearSolver&) = default;\n  LinearSolver(LinearSolver&&) = default;\n  LinearSolver& operator=(const LinearSolver&) = default;\n  LinearSolver& operator=(LinearSolver&&) = default;\n  /// \\endcond\n\n public:\n  ~LinearSolver() override = default;\n\n  /// \\cond\n  explicit LinearSolver(CkMigrateMessage* m);\n  WRAPPED_PUPable_abstract(LinearSolver);  // NOLINT\n  /// \\endcond\n\n  using registrars = LinearSolverRegistrars;\n  using creatable_classes = Registration::registrants<LinearSolverRegistrars>;\n\n  virtual std::unique_ptr<LinearSolver<LinearSolverRegistrars>> get_clone()\n      const = 0;\n\n  /*!\n   * \\brief Solve the linear equation \\f$Ax=b\\f$ where \\f$A\\f$ is the\n   * `linear_operator` and \\f$b\\f$ is the `source`.\n   *\n   * - The (approximate) solution \\f$x\\f$ is returned in the\n   *   `initial_guess_in_solution_out` buffer, which also serves to provide an\n   *   initial guess for \\f$x\\f$. Not all solvers take the initial guess into\n   *   account, but all expect the buffer is sized correctly.\n   * - The `linear_operator` must be an invocable that takes a `VarsType` as\n   *   const-ref argument and returns a `SourceType` by reference. It also takes\n   *   all `OperatorArgs` as const-ref arguments.\n   *\n   * Each solve may mutate the private state of the solver, for example to cache\n   * quantities to accelerate successive solves for the same operator. Invoke\n   * `reset` to discard these caches.\n   */\n  template <typename LinearOperator, typename VarsType, typename SourceType,\n            typename... OperatorArgs, typename... Args>\n  Convergence::HasConverged solve(\n      gsl::not_null<VarsType*> initial_guess_in_solution_out,\n      const LinearOperator& linear_operator, const SourceType& source,\n      const std::tuple<OperatorArgs...>& operator_args, Args&&... args) const;\n\n  /// Discard caches from previous solves. Use before solving a different linear\n  /// operator.\n  virtual void reset() = 0;\n};\n\n/// \\cond\ntemplate <typename LinearSolverRegistrars>\nLinearSolver<LinearSolverRegistrars>::LinearSolver(CkMigrateMessage* m)\n    : PUP::able(m) {}\n/// \\endcond\n\ntemplate <typename LinearSolverRegistrars>\ntemplate <typename LinearOperator, typename VarsType, typename SourceType,\n          typename... OperatorArgs, typename... Args>\nConvergence::HasConverged LinearSolver<LinearSolverRegistrars>::solve(\n    const gsl::not_null<VarsType*> initial_guess_in_solution_out,\n    const LinearOperator& linear_operator, const SourceType& source,\n    const std::tuple<OperatorArgs...>& operator_args, Args&&... args) const {\n  return call_with_dynamic_type<Convergence::HasConverged, creatable_classes>(\n      this, [&initial_guess_in_solution_out, &linear_operator, &source,\n             &operator_args, &args...](auto* const linear_solver) {\n        return linear_solver->solve(initial_guess_in_solution_out,\n                                    linear_operator, source, operator_args,\n                                    std::forward<Args>(args)...);\n      });\n}\n\n/// Indicates the linear solver uses no preconditioner. It may perform\n/// compile-time optimization for this case.\nstruct NoPreconditioner {};\n\n/*!\n * \\brief Base class for serial linear solvers that supports factory-creation\n * and nested preconditioning.\n *\n * To enable support for preconditioning in your derived linear solver class,\n * pass any type that has a `solve` and a `reset` function as the\n * `Preconditioner` template parameter. It can also be an abstract\n * `LinearSolver` type, which means that any other linear solver can be used as\n * preconditioner. Pass `NoPreconditioner` to disable support for\n * preconditioning.\n */\ntemplate <typename Preconditioner, typename LinearSolverRegistrars>\nclass PreconditionedLinearSolver : public LinearSolver<LinearSolverRegistrars> {\n private:\n  using Base = LinearSolver<LinearSolverRegistrars>;\n\n public:\n  using PreconditionerType =\n      tmpl::conditional_t<std::is_abstract_v<Preconditioner>,\n                          std::unique_ptr<Preconditioner>, Preconditioner>;\n  struct PreconditionerOption {\n    static std::string name() { return \"Preconditioner\"; }\n    // Support factory-creatable preconditioners by storing them as unique-ptrs\n    using type = Options::Auto<PreconditionerType, Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        \"An approximate linear solve in every iteration that helps the \"\n        \"algorithm converge.\";\n  };\n\n protected:\n  PreconditionedLinearSolver() = default;\n  PreconditionedLinearSolver(PreconditionedLinearSolver&&) = default;\n  PreconditionedLinearSolver& operator=(PreconditionedLinearSolver&&) = default;\n\n  explicit PreconditionedLinearSolver(\n      std::optional<PreconditionerType> local_preconditioner);\n\n  PreconditionedLinearSolver(const PreconditionedLinearSolver& rhs);\n  PreconditionedLinearSolver& operator=(const PreconditionedLinearSolver& rhs);\n\n public:\n  ~PreconditionedLinearSolver() override = default;\n\n  /// \\cond\n  explicit PreconditionedLinearSolver(CkMigrateMessage* m);\n  /// \\endcond\n\n  void pup(PUP::er& p) override {  // NOLINT\n    PUP::able::pup(p);\n    if constexpr (not std::is_same_v<Preconditioner, NoPreconditioner>) {\n      p | preconditioner_;\n    }\n  }\n\n  /// Whether or not a preconditioner is set\n  bool has_preconditioner() const {\n    if constexpr (not std::is_same_v<Preconditioner, NoPreconditioner>) {\n      return preconditioner_.has_value();\n    } else {\n      return false;\n    }\n  }\n\n  /// @{\n  /// Access to the preconditioner. Check `has_preconditioner()` before calling\n  /// this function. Calling this function when `has_preconditioner()` returns\n  /// `false` is an error.\n  template <\n      bool Enabled = not std::is_same_v<Preconditioner, NoPreconditioner>,\n      Requires<Enabled and\n               not std::is_same_v<Preconditioner, NoPreconditioner>> = nullptr>\n  const Preconditioner& preconditioner() const {\n    ASSERT(has_preconditioner(),\n           \"No preconditioner is set. Please use `has_preconditioner()` to \"\n           \"check before trying to retrieve it.\");\n    if constexpr (std::is_abstract_v<Preconditioner>) {\n      return **preconditioner_;\n    } else {\n      return *preconditioner_;\n    }\n  }\n\n  template <\n      bool Enabled = not std::is_same_v<Preconditioner, NoPreconditioner>,\n      Requires<Enabled and\n               not std::is_same_v<Preconditioner, NoPreconditioner>> = nullptr>\n  Preconditioner& preconditioner() {\n    ASSERT(has_preconditioner(),\n           \"No preconditioner is set. Please use `has_preconditioner()` to \"\n           \"check before trying to retrieve it.\");\n    if constexpr (std::is_abstract_v<Preconditioner>) {\n      return **preconditioner_;\n    } else {\n      return *preconditioner_;\n    }\n  }\n\n  // Keep the function virtual so derived classes must provide an\n  // implementation, but also provide an implementation below that derived\n  // classes can use to reset the preconditioner\n  void reset() override = 0;\n\n protected:\n  /// Copy the preconditioner. Useful to implement `get_clone` when the\n  /// preconditioner has an abstract type.\n  template <\n      bool Enabled = not std::is_same_v<Preconditioner, NoPreconditioner>,\n      Requires<Enabled and\n               not std::is_same_v<Preconditioner, NoPreconditioner>> = nullptr>\n  std::optional<PreconditionerType> clone_preconditioner() const {\n    if constexpr (std::is_abstract_v<Preconditioner>) {\n      return has_preconditioner()\n                 ? std::optional((*preconditioner_)->get_clone())\n                 : std::nullopt;\n    } else {\n      return preconditioner_;\n    }\n  }\n\n private:\n  // Only needed when preconditioning is enabled, but current C++ can't remove\n  // this variable at compile-time. Keeping the variable shouldn't have any\n  // noticeable overhead though.\n  std::optional<PreconditionerType> preconditioner_{};\n};\n\ntemplate <typename Preconditioner, typename LinearSolverRegistrars>\nPreconditionedLinearSolver<Preconditioner, LinearSolverRegistrars>::\n    PreconditionedLinearSolver(\n        std::optional<PreconditionerType> local_preconditioner)\n    : preconditioner_(std::move(local_preconditioner)) {}\n\n// Override copy constructors so they can clone abstract preconditioners\ntemplate <typename Preconditioner, typename LinearSolverRegistrars>\nPreconditionedLinearSolver<Preconditioner, LinearSolverRegistrars>::\n    PreconditionedLinearSolver(const PreconditionedLinearSolver& rhs)\n    : Base(rhs) {\n  if constexpr (not std::is_same_v<Preconditioner, NoPreconditioner>) {\n    preconditioner_ = rhs.clone_preconditioner();\n  }\n}\ntemplate <typename Preconditioner, typename LinearSolverRegistrars>\nPreconditionedLinearSolver<Preconditioner, LinearSolverRegistrars>&\nPreconditionedLinearSolver<Preconditioner, LinearSolverRegistrars>::operator=(\n    const PreconditionedLinearSolver& rhs) {\n  Base::operator=(rhs);\n  if constexpr (not std::is_same_v<Preconditioner, NoPreconditioner>) {\n    preconditioner_ = rhs.clone_preconditioner();\n  }\n  return *this;\n}\n\n/// \\cond\ntemplate <typename Preconditioner, typename LinearSolverRegistrars>\nPreconditionedLinearSolver<Preconditioner, LinearSolverRegistrars>::\n    PreconditionedLinearSolver(CkMigrateMessage* m)\n    : Base(m) {}\n/// \\endcond\n\ntemplate <typename Preconditioner, typename LinearSolverRegistrars>\nvoid PreconditionedLinearSolver<Preconditioner,\n                                LinearSolverRegistrars>::reset() {\n  if constexpr (not std::is_same_v<Preconditioner, NoPreconditioner>) {\n    if (has_preconditioner()) {\n      preconditioner().reset();\n    }\n  }\n}\n\n}  // namespace LinearSolver::Serial\n"
  },
  {
    "path": "src/NumericalAlgorithms/OdeIntegration/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY OdeIntegration)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  OdeIntegration.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Boost::boost\n  DataStructures\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/OdeIntegration/OdeIntegration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n\n// Boost MultiArray is used internally in odeint, so odeint must be included\n// later\n#include <boost/numeric/odeint.hpp>\n\nnamespace detail {\nstruct vector_impl_algebra : boost::numeric::odeint::vector_space_algebra {\n  template <typename VectorType>\n  static double norm_inf(const VectorType& vector) {\n    return max(abs(vector));\n  }\n};\n\nstruct vector_impl_array_algebra : boost::numeric::odeint::array_algebra {\n  template <typename VectorType, size_t Size>\n  static double norm_inf(const std::array<VectorType, Size>& vector_array) {\n    return std::accumulate(\n        vector_array.begin(), vector_array.end(), 0.0,\n        [](const double maximum_so_far, const VectorType& element) {\n          return std::max(maximum_so_far, max(abs(element)));\n        });\n  }\n};\n}  // namespace detail\n\nnamespace boost {\nnamespace numeric {\nnamespace odeint {\n\nnamespace detail {\ntemplate <typename V>\nstruct set_unit_value_impl<ComplexDataVector, V, void> {\n  static void set_value(ComplexDataVector& t, const V& v) {  // NOLINT\n    // this ensures that the blaze expression template resolve the result to a\n    // complex vector\n    t = std::complex<double>(1.0, 0.0) * v;\n  }\n};\ntemplate <typename V>\nstruct set_unit_value_impl<ComplexModalVector, V, void> {\n  static void set_value(ComplexModalVector& t, const V& v) {  // NOLINT\n    // this ensures that the blaze expression template resolve the result to a\n    // complex vector\n    t = std::complex<double>(1.0, 0.0) * v;\n  }\n};\n}  // namespace detail\n\n// In some integration contexts, boost requires the ability to resize the\n// integration arguments in preparation for writing to output buffers passed by\n// reference. These specializations make the resize work correctly with spectre\n// vector types.\ntemplate <class VectorType1, class VectorType2>\nstruct resize_impl_sfinae<VectorType1, VectorType2,\n                          typename boost::enable_if_c<\n                              is_derived_of_vector_impl_v<VectorType1> and\n                              is_derived_of_vector_impl_v<VectorType2>>::type> {\n  static void resize(VectorType1& x1, const VectorType2& x2) {\n    x1.destructive_resize(x2.size());\n  }\n};\n\ntemplate <class VectorType>\nstruct is_resizeable_sfinae<\n    VectorType,\n    typename boost::enable_if_c<is_derived_of_vector_impl_v<VectorType>>::type>\n    :\n#if BOOST_VERSION >= 108600\n    std::true_type\n#else\n    boost::true_type\n#endif\n{\n};\n\ntemplate <>\nstruct algebra_dispatcher<DataVector> {\n  using algebra_type = ::detail::vector_impl_algebra;\n};\n\ntemplate <>\nstruct algebra_dispatcher<ComplexDataVector> {\n  using algebra_type = ::detail::vector_impl_algebra;\n};\n\ntemplate <>\nstruct algebra_dispatcher<ModalVector> {\n  using algebra_type = ::detail::vector_impl_algebra;\n};\n\ntemplate <>\nstruct algebra_dispatcher<ComplexModalVector> {\n  using algebra_type = ::detail::vector_impl_algebra;\n};\n\ntemplate <size_t Size>\nstruct algebra_dispatcher<std::array<DataVector, Size>> {\n  using algebra_type = ::detail::vector_impl_array_algebra;\n};\n\ntemplate <size_t Size>\nstruct algebra_dispatcher<std::array<ComplexDataVector, Size>> {\n  using algebra_type = ::detail::vector_impl_array_algebra;\n};\n\ntemplate <size_t Size>\nstruct algebra_dispatcher<std::array<ModalVector, Size>> {\n  using algebra_type = ::detail::vector_impl_array_algebra;\n};\n\ntemplate <size_t Size>\nstruct algebra_dispatcher<std::array<ComplexModalVector, Size>> {\n  using algebra_type = ::detail::vector_impl_array_algebra;\n};\n}  // namespace odeint\n}  // namespace numeric\n}  // namespace boost\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief For ODE integration, we suggest using the boost libraries whenever\n * possible.\n *\n * \\details Here we describe briefly the suggested setup to use a boost ODE\n * integrator in SpECTRE. The boost utilities have a number of more elaborate\n * features, including features that may result in simpler function calls than\n * the specific recipes below. For full documentation, see the Boost odeint\n * documentation:\n * https://www.boost.org/doc/libs/1_72_0/libs/numeric/odeint/doc/html/boost_numeric_odeint/\n *\n * The integration methods can be used largely as in the boost documentation. We\n * summarize the salient points necessary to get an example running. The main\n * steps in using a boost integrator are:\n * - define the system\n * - construct the stepper\n * - initialize (might be performed implicitly, depending on the stepper)\n * - perform steps\n * - (compute dense output, for dense-output steppers)\n *\n * For most cases the Dormand-Prince fifth order controlled, dense-output\n * stepper is recommended. That stepper is used in the section \"SpECTRE vectors\n * or `std::array` thereof in boost integrators\" below.\n *\n * #### Fundamental types or `std::array` thereof in fixed-step integrators\n *\n * Let us consider a simple oscillator system, which we'll declare as a lambda:\n *\n * \\snippet Test_OdeIntegration.cpp explicit_fundamental_array_system\n *\n * Note that the first argument is a `const` lvalue reference to the state type,\n * `std::array<double, 2>`, the second argument is an lvalue reference for the\n * time derivatives that is written to by the system function, and the final\n * argument is the current time.\n *\n * The construction and initialization of the stepper is simple:\n *\n * \\snippet Test_OdeIntegration.cpp explicit_fundamental_stepper_construction\n *\n * Finally, we can perform the steps and examine the output,\n *\n * \\snippet Test_OdeIntegration.cpp explicit_fundamental_stepper_use\n *\n * #### Fundamental types or `std::array` thereof in Dense-output integrators\n *\n * The dense-output and controlled-step-size ODE integrators in boost comply\n * with a somewhat different interface, as significantly more state information\n * must be managed. The result is a somewhat simpler, but more opaque, interface\n * to the user code.\n *\n * Once again, we start by constructing the system we'd like to integrate,\n *\n * \\snippet Test_OdeIntegration.cpp dense_output_fundamental_system\n *\n * The constructor for dense steppers takes optional arguments for tolerance,\n * and dense steppers need to be initialized.\n *\n * \\snippet Test_OdeIntegration.cpp dense_output_fundamental_construction\n *\n * It is important to take note of the tolerance arguments, as the defaults are\n * `1.0e-6`, which is often looser than we want for calculations in SpECTRE.\n *\n * We then perform the step supplying the system function, and can retrieve the\n * dense output state with `calc_state`, which returns by reference by the\n * second argument.\n *\n * \\snippet Test_OdeIntegration.cpp dense_output_fundamental_stepper_use\n *\n * #### SpECTRE vectors or `std::array` thereof in boost integrators\n *\n * The additional template specializations present in the `OdeIntegration.hpp`\n * file ensure that the boost ODE methods work transparently for SpECTRE vectors\n * as well. We'll run through a brief example to emphasize the functionality.\n *\n * \\snippet Test_OdeIntegration.cpp dense_output_vector_stepper\n */\nnamespace OdeIntegration {}\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY RootFinding)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GslMultiRoot.cpp\n  QuadraticEquation.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GslMultiRoot.hpp\n  NewtonRaphson.hpp\n  QuadraticEquation.hpp\n  RootBracketing.hpp\n  TOMS748.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  ErrorHandling\n  GSL::gsl\n  Logging\n  Printf\n  Simd\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/GslMultiRoot.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/RootFinding/GslMultiRoot.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace RootFinder {\nstd::ostream& operator<<(std::ostream& os, const Method& method) {\n  switch (method) {\n    case Method::Hybrids:\n      return os << \"Hybrids\";\n    case Method::Hybrid:\n      return os << \"Hybrid\";\n    case Method::Newton:\n      return os << \"Newton\";\n    default:\n      ERROR(\"Invalid method value specified.\");\n  }\n  return os;\n}\n\nstd::ostream& operator<<(std::ostream& os, const StoppingCondition& condition) {\n  return call_with_dynamic_type<std::ostream&,\n                                tmpl::list<StoppingConditions::Convergence,\n                                           StoppingConditions::Residual>>(\n      &condition, [&](const auto* c) -> decltype(auto) { return os << *c; });\n}\n\nnamespace StoppingConditions {\nstd::ostream& operator<<(std::ostream& os, const Convergence& condition) {\n  return os << \"Convergence(abs=\" << condition.absolute_tolerance\n            << \", rel=\" << condition.relative_tolerance << \")\";\n}\nstd::ostream& operator<<(std::ostream& os, const Residual& condition) {\n  return os << \"Residual(abs=\" << condition.absolute_tolerance << \")\";\n}\n}  // namespace StoppingConditions\n\nnamespace gsl_multiroot_detail {\nvoid print_rootfinding_parameters(const Method method,\n                                  const double maximum_absolute_tolerance,\n                                  const StoppingCondition& condition) {\n  Parallel::printf(\"\\nAttempting a root find.\\n\");\n  if (method == Method::Newton) {\n    Parallel::printf(\n        \"Method: Newton. Modified to improve global convergence if analytic\\n\"\n        \"jacobian is provided.\\n\");\n  } else if (method == Method::Hybrids) {\n    Parallel::printf(\"Method: Scaled Hybrid.\\n\");\n  } else if (method == Method::Hybrid) {\n    Parallel::printf(\"Method: Unscaled Hybrid.\\n\");\n  }\n  Parallel::printf(\"Stopping condition: %s\\n\", condition);\n  Parallel::printf(\n      \"A failed root find may still be \\\"forgiven\\\" (said to converge) if\\n\"\n      \"each component of f is below the maximum_absolute_tolerance provided.\\n\"\n      \"This value is zero by default, meaning that no failed root finds will\\n\"\n      \"be forgiven. Maximum absolute tolerance: %.17g\\n\",\n      maximum_absolute_tolerance);\n}\n}  // namespace gsl_multiroot_detail\n}  // namespace RootFinder\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/GslMultiRoot.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <gsl/gsl_errno.h>\n#include <gsl/gsl_matrix_double.h>\n#include <gsl/gsl_multiroots.h>\n#include <gsl/gsl_vector_double.h>\n#include <memory>\n#include <ostream>\n#include <string>\n\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace RootFinder {\nnamespace gsl_multiroot_detail {\ntemplate <typename Alloc, typename Dealloc, typename... Args>\nauto gsl_alloc(Alloc* allocator, Dealloc* deallocator, Args&&... args)\n    -> std::unique_ptr<\n        std::decay_t<decltype(*allocator(std::forward<Args>(args)...))>,\n        Dealloc*> {\n  return {allocator(std::forward<Args>(args)...), deallocator};\n}\n\ntemplate <size_t Dim, typename Solver>\nvoid print_state(const size_t iteration_number, const Solver& solver,\n                 const bool print_header = false) {\n  if (print_header) {\n    Parallel::printf(\"Iter\\t\");\n    for (size_t i = 0; i < Dim; ++i) {\n      Parallel::printf(\" x[%u]\\t\", i);\n    }\n    for (size_t i = 0; i < Dim; ++i) {\n      Parallel::printf(\" f[%u]\\t\", i);\n    }\n    Parallel::printf(\"\\n\");\n  }\n\n  Parallel::printf(\"%u\\t\", iteration_number);\n  for (size_t i = 0; i < Dim; ++i) {\n    Parallel::printf(\"%3.4f  \", gsl_vector_get(solver.x, i));\n  }\n  for (size_t i = 0; i < Dim; ++i) {\n    Parallel::printf(\"%1.3e  \", gsl_vector_get(solver.f, i));\n  }\n  Parallel::printf(\"\\n\");\n}\n\ntemplate <size_t Dim>\nstd::array<double, Dim> gsl_to_std_array(const gsl_vector* const x) {\n  std::array<double, Dim> input_as_std_array{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(input_as_std_array, i) = gsl_vector_get(x, i);\n  }\n  return input_as_std_array;\n}\n\ntemplate <size_t Dim>\nvoid gsl_vector_set_with_std_array(\n    gsl_vector* const func,\n    const std::array<double, Dim>& result_as_std_array) {\n  for (size_t i = 0; i < Dim; i++) {\n    gsl_vector_set(func, i, gsl::at(result_as_std_array, i));\n  }\n}\n\ntemplate <size_t Dim>\nvoid gsl_matrix_set_with_std_array(\n    gsl_matrix* const matrix,\n    const std::array<std::array<double, Dim>, Dim>& matrix_array) {\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = 0; j < Dim; j++) {\n      gsl_matrix_set(matrix, i, j, gsl::at(gsl::at(matrix_array, i), j));\n    }\n  }\n}\n\n// The gsl_multiroot_function_fdf expects its functions to be of the form\n// int (* f) (const gsl_vector * x, void * params, gsl_vector * f).\n// However, we would like to be able to perform rootfinding on functions\n// of the form std::array<double, Dim> f(const std::array<double, Dim>& x).\n// So, we pass the function wrapper below to gsl_multiroot_function_fdf.\n// In the gsl documentation the third parameter is refered to as \"void* params\",\n// referring to the parameters that select out a particular function out of a\n// family of possible functions described in a class, but we instead pass the\n// pointer to the entire function object itself here. The type of the function\n// object is passed through with the Function template parameter.\ntemplate <size_t Dim, typename Function>\nint gsl_multirootfunctionfdf_wrapper_f(const gsl_vector* const x,\n                                       void* const untyped_function_object,\n                                       gsl_vector* const f_of_x) {\n  const auto function_object =\n      static_cast<const Function*>(untyped_function_object);\n  gsl_vector_set_with_std_array(\n      f_of_x, function_object->operator()(gsl_to_std_array<Dim>(x)));\n  return GSL_SUCCESS;\n}\n\ntemplate <size_t Dim, typename Function>\nint gsl_multirootfunctionfdf_wrapper_df(const gsl_vector* const x,\n                                        void* const untyped_function_object,\n                                        gsl_matrix* const jacobian) {\n  const auto function_object =\n      static_cast<const Function*>(untyped_function_object);\n  gsl_matrix_set_with_std_array(\n      jacobian, function_object->jacobian(gsl_to_std_array<Dim>(x)));\n\n  return GSL_SUCCESS;\n}\n\ntemplate <size_t Dim, typename Function>\nint gsl_multirootfunctionfdf_wrapper_fdf(const gsl_vector* const x,\n                                         void* const untyped_function_object,\n                                         gsl_vector* const f_of_x,\n                                         gsl_matrix* const jacobian) {\n  const auto function_object =\n      static_cast<const Function*>(untyped_function_object);\n  const std::array<double, Dim> x_as_std_array = gsl_to_std_array<Dim>(x);\n  gsl_vector_set_with_std_array(f_of_x,\n                                function_object->operator()(x_as_std_array));\n  gsl_matrix_set_with_std_array(jacobian,\n                                function_object->jacobian(x_as_std_array));\n  return GSL_SUCCESS;\n}\n\nCREATE_IS_CALLABLE(jacobian)\nCREATE_IS_CALLABLE_V(jacobian)\n}  // namespace gsl_multiroot_detail\n\n/*!\n *  \\ingroup NumericalAlgorithmsGroup\n *  \\brief The different options for the rootfinding method of gsl_multiroot.\n *\n *  This enum is for setting the method used the rootfinder.\n *  The precise method used by the gsl rootfinder depends on whether or not the\n *  function passed to it has a callable `jacobian` member function. In the\n *  case where it doesn't, the jacobian is approximated with a finite\n *  difference. For example, if the Method specified is Hybrid, gsl will use\n *  the gsl_multiroot_fdfsolver_hybridj method in the case where a `jacobian`\n *  is provided, and gsl_multiroot_fsolver_hybrid in the case where one isn't.\n *  See\n *  [GSL's documentation for multidimensional\n *  rootfinding](https://www.gnu.org/software/gsl/manual/html_node/Multidimensional-Root_002dFinding.html)\n *  for information on the different methods.\n *  \\note gsl does not provide a finite difference version for the modified\n *  Newton method (gsl_multiroot_fdfsolver_gnewton). In the case where a\n *  jacobian is not provided the method used will be a non-modified Newton\n *  method.\n *\n */\nenum class Method {\n  /// Hybrid of Newton's method along with following the gradient direction.\n  Hybrids,\n  /// \"Unscaled version of Hybrids that uses a spherical trust region,\" see\n  /// GSL documentation for more details.\n  Hybrid,\n  /// If an analytic jacobian is provided, gsl uses a modification of Newton's\n  /// method to improve global convergence. Uses vanilla Newton's method if no\n  /// jacobian is provided.\n  Newton\n};\n\nstd::ostream& operator<<(std::ostream& /*os*/, const Method& /*method*/);\n\n/// \\see StoppingConditions\nstruct StoppingCondition {\n protected:\n  StoppingCondition() = default;\n  StoppingCondition(const StoppingCondition&) = default;\n  StoppingCondition(StoppingCondition&&) = default;\n  StoppingCondition& operator=(const StoppingCondition&) = default;\n  StoppingCondition& operator=(StoppingCondition&&) = default;\n  ~StoppingCondition() = default;\n\n public:\n  /// \\cond\n  template <typename Solver>\n  int test(const Solver& solver) const {\n    return test_impl(solver.x, solver.dx, solver.f);\n  }\n  /// \\endcond\n\n private:\n  virtual int test_impl(const gsl_vector* x, const gsl_vector* dx,\n                        const gsl_vector* f) const = 0;\n};\n\nstd::ostream& operator<<(std::ostream& os, const StoppingCondition& condition);\n\n/*!\n *  \\ingroup NumericalAlgorithmsGroup\n *  \\brief The different options for the convergence criterion of gsl_multiroot.\n *\n *  See\n *  [GSL's documentation for multidimensional\n *  rootfinding](https://www.gnu.org/software/gsl/manual/html_node/Multidimensional-Root_002dFinding.html)\n *  for information on the different stopping conditions.\n */\nnamespace StoppingConditions {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\n\n/// Terminate when the result converges to a value.  See GSL\n/// documentation for gsl_multiroot_test_delta.\nstruct Convergence : StoppingCondition {\n  double absolute_tolerance;\n  double relative_tolerance;\n\n  Convergence(const double absolute_tolerance_,\n              const double relative_tolerance_)\n      : absolute_tolerance(absolute_tolerance_),\n        relative_tolerance(relative_tolerance_) {}\n\n private:\n  int test_impl(const gsl_vector* const x, const gsl_vector* const dx,\n                const gsl_vector* const /*f*/) const override {\n    return gsl_multiroot_test_delta(dx, x, absolute_tolerance,\n                                    relative_tolerance);\n  }\n};\n\n/// Terminate when the residual is small.  See GSL documentation for\n/// gsl_multiroot_test_residual.\nstruct Residual : StoppingCondition {\n  double absolute_tolerance;\n\n  explicit Residual(const double absolute_tolerance_)\n      : absolute_tolerance(absolute_tolerance_) {}\n\n private:\n  int test_impl(const gsl_vector* const /*x*/, const gsl_vector* const /*dx*/,\n                const gsl_vector* const f) const override {\n    return gsl_multiroot_test_residual(f, absolute_tolerance);\n  }\n};\n\n#pragma GCC diagnostic pop\n\nstd::ostream& operator<<(std::ostream& os, const Convergence& condition);\nstd::ostream& operator<<(std::ostream& os, const Residual& condition);\n}  // namespace StoppingConditions\n\nnamespace gsl_multiroot_detail {\ntemplate <typename SolverType, typename SolverAlloc, typename SolverSet,\n          typename SolverIterate, typename SolverFree, size_t Dim,\n          typename Function>\nstd::array<double, Dim> gsl_multiroot_impl(\n    Function& f, const std::array<double, Dim>& initial_guess,\n    const StoppingCondition& condition, const size_t maximum_iterations,\n    const Verbosity verbosity, const double maximum_absolute_tolerance,\n    const Method method, const SolverType solver_type,\n    const SolverAlloc solver_alloc, const SolverSet solver_set,\n    const SolverIterate solver_iterate, const SolverFree solver_free) {\n  // Supply gsl_root with the initial guess:\n  const auto gsl_root = gsl_alloc(&gsl_vector_alloc, &gsl_vector_free, Dim);\n  gsl_vector_set_with_std_array(gsl_root.get(), initial_guess);\n  const auto solver = gsl_alloc(solver_alloc, solver_free, solver_type, Dim);\n  solver_set(solver.get(), &f, gsl_root.get());\n\n  // Take iterations:\n  int status;\n  size_t iteration_number = 0;\n  do {\n    if (UNLIKELY(verbosity >= Verbosity::Debug)) {\n      print_state<Dim>(iteration_number, *solver, iteration_number == 0);\n    }\n    iteration_number++;\n\n    if (gsl_to_std_array<Dim>(solver->f) == make_array<Dim>(0.0)) {\n      return gsl_to_std_array<Dim>(solver->x);\n    }\n\n    status = solver_iterate(solver.get());\n    // Check if solver is stuck\n    if (UNLIKELY(status == GSL_ENOPROG)) {\n      if (UNLIKELY(verbosity >= Verbosity::Debug)) {\n        Parallel::printf(\n            \"The iteration is not making any progress, preventing the \"\n            \"algorithm from continuing.\");\n      }\n      break;\n    }\n    status = condition.test(*solver);\n  } while (status == GSL_CONTINUE and iteration_number < maximum_iterations);\n  if (UNLIKELY(verbosity >= Verbosity::Verbose)) {\n    Parallel::printf(\"Finished iterating:\\n\");\n    print_state<Dim>(iteration_number, *solver,\n                     verbosity >= Verbosity::Verbose);\n  }\n  bool success = (status == GSL_SUCCESS);\n  if (UNLIKELY(verbosity > Verbosity::Silent)) {\n    Parallel::printf(\"\\n\");\n    if (not success) {\n      const std::string ascii_divider = std::string(70, '#');\n      const std::string failure_message =\n          ascii_divider + \"\\n\\t\\tWARNING: Root Finding FAILED\\n\" +\n          ascii_divider;\n      Parallel::printf(\"%s\\n\", failure_message);\n    } else {\n      Parallel::printf(\"Root finder converged.\\n\");\n    }\n  }\n  // If maximum_absolute_tolerance is given, return success = true\n  // as long as maximum_absolute_tolerance is achieved even if the\n  // root finder doesn't converge.\n  bool success_with_tolerance = true;\n  bool failed_root_is_forgiven = false;\n  if (not success and maximum_absolute_tolerance > 0.0) {\n    for (size_t i = 0; i < Dim; ++i) {\n      if (fabs(gsl_vector_get(solver->f, i)) > maximum_absolute_tolerance) {\n        success_with_tolerance = false;\n      }\n    }\n    failed_root_is_forgiven = success_with_tolerance;\n  }\n  if (success_with_tolerance and maximum_absolute_tolerance > 0.0) {\n    success = true;\n  }\n  if (UNLIKELY(failed_root_is_forgiven and verbosity >= Verbosity::Verbose)) {\n    Parallel::printf(\n        \"The failed root was forgiven as each component was found to be under \"\n        \"maximum_absolute_tolerance %f\",\n        maximum_absolute_tolerance);\n  }\n\n  if (UNLIKELY(not success)) {\n    std::stringstream error_message;\n    error_message << \"The root find failed and was not forgiven. An exception \"\n                     \"has been thrown.\\n\"\n                  << \"The gsl error returned is: \" << gsl_strerror(status)\n                  << \"\\n\"\n                  << \"Verbosity: \" << verbosity << \"\\n\"\n                  << \"Method: \" << method << \"\\n\"\n                  << \"StoppingCondition: \" << condition << \"\\n\"\n                  << \"Maximum absolute tolerance: \"\n                  << maximum_absolute_tolerance << \"\\n\"\n                  << \"Maximum number of iterations: \" << maximum_iterations\n                  << \"\\n\"\n                  << \"Number of iterations reached: \" << iteration_number\n                  << \"\\n\"\n                  << \"The last value of f in the root solver is:\\n\";\n    for (size_t i = 0; i < Dim; i++) {\n      error_message << gsl_vector_get(solver->f, i) << \"\\n\";\n    }\n    error_message << \"The last value of x in the root solver is:\\n\";\n    for (size_t i = 0; i < Dim; i++) {\n      error_message << gsl_vector_get(solver->x, i) << \"\\n\";\n    }\n    error_message << \"The last value of dx in the root solver is:\\n\";\n    for (size_t i = 0; i < Dim; i++) {\n      error_message << gsl_vector_get(solver->dx, i) << \"\\n\";\n    }\n\n    if (UNLIKELY(verbosity >= Verbosity::Debug)) {\n      Parallel::printf(\"Error: %s\\n\", gsl_strerror(status));\n      if (iteration_number >= maximum_iterations) {\n        Parallel::printf(\n            \"The number of iterations (%d) has reached the maximum number of \"\n            \"iterations (%d)\\n\",\n            iteration_number, maximum_iterations);\n      } else {\n        Parallel::printf(\n            \"The number of iterations (%d) failed to reach the maximum number \"\n            \"of iterations (%d)\\n\",\n            iteration_number, maximum_iterations);\n      }\n    }\n    throw convergence_error(error_message.str());\n  }\n\n  return gsl_to_std_array<Dim>(solver->x);\n}\n\nvoid print_rootfinding_parameters(Method method,\n                                  double maximum_absolute_tolerance,\n                                  const StoppingCondition& condition);\n}  // namespace gsl_multiroot_detail\n\n/// @{\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief A multidimensional root finder supporting Newton and Hybrid\n * methods, as well as modified methods based on these.\n *\n * This root finder accepts function objects with and without a\n * callable `jacobian` member function.  The call operator both\n * accepts and returns a `std::array<double, Dim>`, of the appropriate\n * dimension for the domain and range the function the root find is\n * being performed on.\n *\n * If a `jacobian` function is provided, it must accept a\n * `std::array<double, Dim>` and return a\n * `std::array<std::array<double, Dim>, Dim>` representing the\n * derivative of the call operator, with\n * \\begin{equation}\n *   \\text{jacobian[i][j]} = \\frac{\\partial f_i}{x_j}.\n * \\end{equation}\n *\n * Whether the jacobian is provided determines the details of the\n * implementation of the root-finding method that is selected by the\n * user using the Method enum.  That is, whether the jacobian is\n * computed analytically via the `jacobian` member function, or\n * whether the jacobian is computed numerically via a finite\n * difference approximation.\n *\n * \\note GSL does not provide a finite difference version of its modified\n * Newton method, so the unmodified one is used instead when the user\n * uses the Method::Newton method.\n *\n * The user can select one of two possible criteria for convergence,\n * StoppingCondition::Residual, where the sum of the absolute values of the\n * components of the residual vector f are compared against the value\n * provided to `absolute_tolerance`, and\n * StoppingCondition::Convergence, where the size of the most recent\n * step taken in the root-finding iteration is compared against\n * `absolute_tolerance` + `relative_tolerance` * |x_i|, for each component.\n * In either case, a `maximum_absolute_tolerance` may be specified if the user\n * anticipates that the convergence criterion specified with StoppingCondition\n * will be too strict for a few points out of a population of points found with\n * a sequence of root finds.\n *\n * See\n * [GSL's documentation for multidimensional\n * rootfinding](https://www.gnu.org/software/gsl/manual/html_node/Multidimensional-Root_002dFinding.html)\n * for reference.\n *\n * \\param func Function whose root is to be found.\n * \\param initial_guess Contains initial guess.\n * \\param maximum_iterations The maximum number of iterations.\n * \\param verbosity Whether to print diagnostic messages.\n * \\param maximum_absolute_tolerance Acceptable absolute tolerance when\n *                                   root finder doesn't converge.\n *                                   You may wish to use this if there\n *                                   are only a few \"problematic\" points where\n *                                   it is difficult to do a precise root find.\n * \\param method The method to use. See the documentation for the Method enum.\n * \\param condition The convergence condition to use. See the documentation\n *                                   for the StoppingCondition enum.\n */\ntemplate <size_t Dim, typename Function,\n          Requires<gsl_multiroot_detail::is_jacobian_callable_v<\n              Function, std::array<double, Dim>>> = nullptr>\nstd::array<double, Dim> gsl_multiroot(\n    const Function& func, const std::array<double, Dim>& initial_guess,\n    const StoppingCondition& condition, const size_t maximum_iterations,\n    const Verbosity verbosity = Verbosity::Silent,\n    const double maximum_absolute_tolerance = 0.0,\n    const Method method = Method::Newton) {\n  gsl_multiroot_function_fdf gsl_func = {\n      &gsl_multiroot_detail::gsl_multirootfunctionfdf_wrapper_f<Dim, Function>,\n      &gsl_multiroot_detail::gsl_multirootfunctionfdf_wrapper_df<Dim, Function>,\n      &gsl_multiroot_detail::gsl_multirootfunctionfdf_wrapper_fdf<Dim,\n                                                                  Function>,\n      Dim, const_cast<Function*>(&func)};  //NOLINT\n\n  // Set up method for solver:\n  const gsl_multiroot_fdfsolver_type* solver_type;\n  if (method == Method::Newton) {\n    solver_type = gsl_multiroot_fdfsolver_gnewton;\n  } else if (method == Method::Hybrids) {\n    solver_type = gsl_multiroot_fdfsolver_hybridsj;\n  } else if (method == Method::Hybrid) {\n    solver_type = gsl_multiroot_fdfsolver_hybridj;\n  } else {\n    ERROR(\n        \"Invalid method. Has to be one of Newton, Hybrids or \"\n        \"Hybrid.\");\n  }\n  // Print initial parameters\n  if (UNLIKELY(verbosity >= Verbosity::Verbose)) {\n    gsl_multiroot_detail::print_rootfinding_parameters(\n        method, maximum_absolute_tolerance, condition);\n  }\n  return gsl_multiroot_detail::gsl_multiroot_impl(\n      gsl_func, initial_guess, condition, maximum_iterations, verbosity,\n      maximum_absolute_tolerance, method, solver_type,\n      &gsl_multiroot_fdfsolver_alloc, &gsl_multiroot_fdfsolver_set,\n      &gsl_multiroot_fdfsolver_iterate, &gsl_multiroot_fdfsolver_free);\n}\n\ntemplate <size_t Dim, typename Function,\n          Requires<not gsl_multiroot_detail::is_jacobian_callable_v<\n              Function, std::array<double, Dim>>> = nullptr>\nstd::array<double, Dim> gsl_multiroot(\n    const Function& func, const std::array<double, Dim>& initial_guess,\n    const StoppingCondition& condition, const size_t maximum_iterations,\n    const Verbosity verbosity = Verbosity::Silent,\n    const double maximum_absolute_tolerance = 0.0,\n    const Method method = Method::Newton) {\n  gsl_multiroot_function gsl_func = {\n      &gsl_multiroot_detail::gsl_multirootfunctionfdf_wrapper_f<Dim, Function>,\n      Dim, const_cast<Function*>(&func)};  // NOLINT\n\n  // Set up method for solver:\n  const gsl_multiroot_fsolver_type* solver_type;\n  if (method == Method::Newton) {\n    solver_type = gsl_multiroot_fsolver_dnewton;\n  } else if (method == Method::Hybrids) {\n    solver_type = gsl_multiroot_fsolver_hybrids;\n  } else if (method == Method::Hybrid) {\n    solver_type = gsl_multiroot_fsolver_hybrid;\n  } else {\n    ERROR(\n        \"Invalid method. Has to be one of Newton, Hybrids or \"\n        \"Hybrid.\");\n  }\n  // Print initial parameters\n  if (UNLIKELY(verbosity >= Verbosity::Verbose)) {\n    gsl_multiroot_detail::print_rootfinding_parameters(\n        method, maximum_absolute_tolerance, condition);\n  }\n  return gsl_multiroot_detail::gsl_multiroot_impl(\n      gsl_func, initial_guess, condition, maximum_iterations, verbosity,\n      maximum_absolute_tolerance, method, solver_type,\n      &gsl_multiroot_fsolver_alloc, &gsl_multiroot_fsolver_set,\n      &gsl_multiroot_fsolver_iterate, &gsl_multiroot_fsolver_free);\n}\n/// @}\n}  // namespace RootFinder\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/LICENSE_1_0.txt",
    "content": "Boost Software License - Version 1.0 - August 17th, 2003\n\nPermission is hereby granted, free of charge, to any person or organization\nobtaining a copy of the software and accompanying documentation covered by\nthis license (the \"Software\") to use, reproduce, display, distribute,\nexecute, and transmit the Software, and to prepare derivative works of the\nSoftware, and to permit third-parties to whom the Software is furnished to\ndo so, all subject to the following:\n\nThe copyright notices in the Software and this entire statement, including\nthe above license grant, this restriction and the following disclaimer,\nmust be included in all copies of the Software, in whole or in part, and\nall derivative works of the Software, unless such copies or derivative\nworks are solely in the form of machine-executable object code generated by\na source language processor.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT\nSHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE\nFOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/NewtonRaphson.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#error \"RootFinding/NewtonRaphson.hpp does not provide any functionality.\"\n\nnamespace RootFinder {\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * For the nonlinear solver, see NonlinearSolver::newton_raphson.  We\n * do not provide a Newton-Raphson root finder.  The Boost\n * implementation is buggy and can return the wrong answer, and it is\n * not clear that performance would be better than RootFinder::toms748\n * in practice.\n *\n * Newton-Raphson is asymptotically faster than TOMS748 in the ideal\n * case, but that assumes that function evaluations take the same\n * amount of time for both solvers, while in reality the derivatives\n * are often as or even more expensive than the values.  Additionally,\n * for realistic problems, convergence is usually fast enough that\n * neither solver reaches the asymptotic regime.  Newton-Raphson also\n * has the advantage of requiring less work internally in the solver\n * implementation, but, again, for realistic problems solver overhead\n * is usually dwarfed by the function evaluations.\n */\nvoid newton_raphson() = delete;\n}  // namespace RootFinder\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/QuadraticEquation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n\n#include <gsl/gsl_poly.h>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\ndouble positive_root(const double a, const double b, const double c) {\n  const auto roots = real_roots(a, b, c);\n  ASSERT(roots.has_value(), \"There are no real roots with a=\" << a << \" b=\"\n         << b << \" c=\" << c);\n  ASSERT((*roots)[0] <= 0.0 and (*roots)[1] >= 0.0,\n         \"There are two positive roots, \" << (*roots)[0] << \" and \"\n         << (*roots)[1] << \", with a=\" << a << \" b=\" << b << \" c=\" << c);\n  return (*roots)[1];\n}\n\nstd::optional<std::array<double, 2>> real_roots(const double a, const double b,\n                                                const double c) {\n  ASSERT(a != 0.0, \"Must have non-zero quadratic term.\");\n  double x0 = std::numeric_limits<double>::signaling_NaN();\n  double x1 = std::numeric_limits<double>::signaling_NaN();\n  const int num_real_roots = gsl_poly_solve_quadratic(a, b, c, &x0, &x1);\n  if (num_real_roots == 0) {\n    return std::nullopt;\n  } else {\n    ASSERT(num_real_roots == 2,\n           \"There are \" << num_real_roots << \" real roots with a=\" << a\n           << \" b=\" << b << \" c=\" << c << \", which is not zero or two.  \"\n           \"This should not be possible.\");\n    return {{x0, x1}};\n  }\n}\n\nnamespace detail {\ntemplate <typename T>\nstruct root_between_values_impl;\nenum class RootToChoose { min, max };\n}  // namespace detail\n\ntemplate <typename T>\nT smallest_root_greater_than_value_within_roundoff(const T& a, const T& b,\n                                                   const T& c,\n                                                   const double value) {\n  return detail::root_between_values_impl<T>::function(\n      a, b, c, value, std::numeric_limits<double>::max(),\n      detail::RootToChoose::min);\n}\n\ntemplate <typename T>\nT largest_root_between_values_within_roundoff(const T& a, const T& b,\n                                              const T& c,\n                                              const double min_value,\n                                              const double max_value) {\n  return detail::root_between_values_impl<T>::function(\n      a, b, c, min_value, max_value, detail::RootToChoose::max);\n}\n\nnamespace detail {\ntemplate <>\nstruct root_between_values_impl<double> {\n  static double function(const double a, const double b, const double c,\n                         const double min_value, const double max_value,\n                         const RootToChoose min_or_max) {\n    // Roots are returned in increasing order.\n    const auto roots = real_roots(a, b, c);\n    ASSERT(roots.has_value(), \"No real roots for a=\" << a << \" b=\" << b\n           << \" c=\" << c);\n\n    const std::array<bool, 2> roots_out_of_bounds_low{\n        {(*roots)[0] < min_value and\n             not equal_within_roundoff((*roots)[0], min_value),\n         (*roots)[1] < min_value and\n             not equal_within_roundoff((*roots)[1], min_value)}};\n    const std::array<bool, 2> roots_out_of_bounds_high{\n        {(*roots)[0] > max_value and\n             not equal_within_roundoff((*roots)[0], max_value),\n         (*roots)[1] > max_value and\n             not equal_within_roundoff((*roots)[1], max_value)}};\n\n    double return_value = std::numeric_limits<double>::signaling_NaN();\n    const auto error_message = [&a, &b, &c,\n                                &roots](const std::string& message) {\n      ERROR(message << \" Roots are \" << (*roots)[0] << \" and \" << (*roots)[1]\n                    << \", with a=\" << a << \" b=\" << b << \" c=\" << c);\n    };\n\n    if (min_or_max == RootToChoose::min) {\n      // Check (*roots)[0] first because it is the smallest\n      if (roots_out_of_bounds_low[0]) {\n        if (roots_out_of_bounds_low[1]) {\n          error_message(\"No root >= (within roundoff) min_value.\");\n        }\n        if (roots_out_of_bounds_high[1]) {\n          error_message(\n              \"No root between min_value and max_value (within roundoff).\");\n        }\n        return_value = (*roots)[1];\n      } else {\n        if (roots_out_of_bounds_high[0]) {\n          error_message(\"No root <= (within roundoff) max_value.\");\n        }\n        return_value = (*roots)[0];\n      }\n    } else {\n      // Check roots[1] first because it is the largest\n      if (roots_out_of_bounds_high[1]) {\n        if (roots_out_of_bounds_high[0]) {\n          error_message(\"No root <= (within roundoff) max_value.\");\n        }\n        if (roots_out_of_bounds_low[0]) {\n          error_message(\n              \"No root between min_value and max_value (within \"\n              \"roundoff).\");\n        }\n        return_value = (*roots)[0];\n      } else {\n        if (roots_out_of_bounds_low[1]) {\n          error_message(\"No root >= (within roundoff) min_value.\");\n        }\n        return_value = (*roots)[1];\n      }\n    }\n    return return_value;\n  }\n};\n\ntemplate <>\nstruct root_between_values_impl<DataVector> {\n  static DataVector function(const DataVector& a, const DataVector& b,\n                             const DataVector& c, const double min_value,\n                             const double max_value,\n                             const RootToChoose min_or_max) {\n    ASSERT(a.size() == b.size(),\n           \"Size mismatch a vs b: \" << a.size() << \" \" << b.size());\n    ASSERT(a.size() == c.size(),\n           \"Size mismatch a vs c: \" << a.size() << \" \" << c.size());\n    DataVector result(a.size());\n    for (size_t i = 0; i < a.size(); ++i) {\n      result[i] = root_between_values_impl<double>::function(\n          a[i], b[i], c[i], min_value, max_value, min_or_max);\n    }\n    return result;\n  }\n};\n}  // namespace detail\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template DTYPE(data) smallest_root_greater_than_value_within_roundoff(   \\\n      const DTYPE(data) & a, const DTYPE(data) & b, const DTYPE(data) & c, \\\n      double value);                                                       \\\n  template DTYPE(data) largest_root_between_values_within_roundoff(        \\\n      const DTYPE(data) & a, const DTYPE(data) & b, const DTYPE(data) & c, \\\n      double min_value, double max_value);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/QuadraticEquation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares functions for solving quadratic equations\n\n#pragma once\n\n#include <array>\n#include <optional>\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Returns the positive root of a quadratic equation \\f$ax^2 +\n * bx + c = 0\\f$\n * \\returns The positive root of a quadratic equation.\n * \\requires That there are two real roots, of which only one is positive.\n */\ndouble positive_root(double a, double b, double c);\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Returns the smallest root of a quadratic equation \\f$ax^2 +\n * bx + c = 0\\f$ that is greater than the given value, within roundoff.\n * \\returns A root of a quadratic equation.\n * \\requires That there are two real roots.\n * \\requires At least one root is greater than the given value, to roundoff.\n */\ntemplate <typename T>\nT smallest_root_greater_than_value_within_roundoff(const T& a, const T& b,\n                                                   const T& c, double value);\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Returns the largest root of a quadratic equation\n * \\f$ax^2 + bx + c = 0\\f$ that is between min_value and max_value,\n * within roundoff.\n * \\returns A root of a quadratic equation.\n * \\requires That there are two real roots.\n * \\requires At least one root is between min_value and max_value, to roundoff.\n */\ntemplate <typename T>\nT largest_root_between_values_within_roundoff(const T& a, const T& b,\n                                              const T& c, double min_value,\n                                              double max_value);\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Returns the two real roots of a quadratic equation \\f$ax^2 +\n * bx + c = 0\\f$ with the root closer to \\f$-\\infty\\f$ first, or an\n * empty optional if there are no real roots.\n */\nstd::optional<std::array<double, 2>> real_roots(double a, double b, double c);\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/RootBracketing.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <sstream>\n#include <stdexcept>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace RootFinder {\n\nnamespace bracketing_detail {\n// Brackets a root, given a functor f(x) that returns a\n// std::optional<double> and given two arrays x and y (with y=f(x))\n// containing points that have already been tried for bracketing.\n//\n// Returns a std::tuple<double, double, double, double>\n// containing {{x1,x2,y1,y2}} where\n// x1 and x2 bracket the root, and y1=f(x1) and y2=f(x2).\n//\n// Note that y might be undefined (i.e. an invalid std::optional)\n// for values of x at or near the endpoints of the interval.  We\n// assume that if f(x1) and f(x2) are valid for some x1 and x2, then\n// f(x) is valid for all x between x1 and x2.\n//\n// Assumes that there is a root between the first and last points of\n// the array.\n// We also assume that there is only one root.\n//\n// So this means we have only 2 possibilities for the validity of the\n// points in the input x,y arrays:\n// 1) All points are invalid, e.g. \"X X X X X X X X\".\n//    (here X represents an invalid point)\n// 2) All valid points are adjacent, with the same sign, e.g. \"X X o o X X\"\n//    or \"o o X X X\" or \"X X X o o\".\n//    (here o represents a valid point)\n// Note that we assume that all valid points have the same sign; otherwise\n// the caller would have known that the root was bracketed and the caller would\n// not have called bracket_by_contracting.\n//\n// Also note that we exclude the case \"o o o o o\" (no roots), since the\n// caller would have known that too.  If such a case is found, an error is\n// thrown.  An error is also thrown if the size of the region where the\n// sign changes is so small that the number of iterations is exceeded.\n//\n// For case 1) above, we bisect each pair of points, and call\n// bracket_by_contracting recursively until we find a valid point.\n// For case 2) above, it is sufficent to check for a bracket only\n// between valid and invalid points.  That is, for \"X X + + X X\" we\n// check only between points 1 and 2 and between points 3 and 4 (where\n// points are numbered starting from zero).  For \"+ + X X X\" we check\n// only between points 1 and 2.\ntemplate <typename Functor>\nauto bracket_by_contracting(const std::vector<double>& x,\n                            const std::vector<std::optional<double>>& y,\n                            const Functor& f, const size_t level = 0)\n    -> std::tuple<double, double, double, double> {\n  constexpr size_t max_level = 27;\n  if (level > max_level) {\n    std::stringstream ss;\n    ss << \"Too many iterations in bracket_by_contracting.  Either the \"\n          \"region where the root changes sign is so small that we cannot \"\n          \"find it, or the given interval does not actually bracket a \"\n          \"root.  The points we are checking are \"\n       << x.size() << \" almost-evenly-spaced points from \" << x.front()\n       << \" to \" << x.back() << \" with difference \" << x.back()-x.front();\n    throw std::runtime_error(ss.str());\n  }\n\n  // First check if we have any valid points.\n  size_t last_valid_index = y.size();\n  for (size_t i = y.size(); i >= 1; --i) {\n    if (y[i - 1].has_value()) {\n      last_valid_index = i - 1;\n      break;\n    }\n  }\n\n  if (last_valid_index == y.size()) {\n    // No valid points!\n\n    // Create larger arrays with one point between each of the already\n    // computed points.\n    std::vector<double> bisected_x(x.size() * 2 - 1);\n    std::vector<std::optional<double>> bisected_y(y.size() * 2 - 1);\n\n    // Copy all even-numbered points in the range.\n    for (size_t i = 0; i < x.size(); ++i) {\n      bisected_x[2 * i] = x[i];\n      bisected_y[2 * i] = y[i];\n    }\n\n    // Fill midpoints and check for bracket on each one.\n    for (size_t i = 0; i < x.size() - 1; ++i) {\n      bisected_x[2 * i + 1] = x[i] + 0.5 * (x[i + 1] - x[i]);\n      bisected_y[2 * i + 1] = f(bisected_x[2 * i + 1]);\n      if (bisected_y[2 * i + 1].has_value()) {\n        // Valid point! We know that all the other points are\n        // invalid, so we need to check only 3 points in the next\n        // iteration: the new valid point and its neighbors.\n        return bracket_by_contracting({{x[i], bisected_x[2 * i + 1], x[i + 1]}},\n                                      {{y[i], bisected_y[2 * i + 1], y[i + 1]}},\n                                      f, level + 1);\n      }\n    }\n    // We still have no valid points. So recurse, using all points.\n    // The next iteration will bisect all the points.\n    return bracket_by_contracting(bisected_x, bisected_y, f, level + 1);\n  }\n\n  // If we get here, we have found a valid point; in particular we have\n  // found the last valid point in the array.\n\n  // Find the first valid point in the array.\n  size_t first_valid_index = 0;\n  for (size_t i = 0; i < y.size(); ++i) {\n    if (y[i].has_value()) {\n      first_valid_index = i;\n      break;\n    }\n  }\n\n  // Make a new set of points that includes only the points that\n  // neighbor the boundary between valid and invalid points.\n  std::vector<double> x_near_valid_point;\n  std::vector<std::optional<double>> y_near_valid_point;\n\n  if (first_valid_index == 0 and last_valid_index == y.size() - 1) {\n    ERROR(\n        \"bracket_while_contracting: found a case where all points are valid,\"\n        \"which should not happen under our assumptions.\");\n  }\n\n  if (first_valid_index > 0) {\n    // Check for a root between first_valid_index-1 and first_valid_index.\n    const double x_test =\n        x[first_valid_index - 1] +\n        0.5 * (x[first_valid_index] - x[first_valid_index - 1]);\n    const auto y_test = f(x_test);\n    if (y_test.has_value() and\n        y[first_valid_index].value() * y_test.value() <= 0.0) {\n      // Bracketed!\n      return {\n          x_test, x[first_valid_index], y_test.value(),\n          y[first_valid_index].value()};\n    } else {\n      x_near_valid_point.push_back(x[first_valid_index - 1]);\n      y_near_valid_point.push_back(y[first_valid_index - 1]);\n      x_near_valid_point.push_back(x_test);\n      y_near_valid_point.push_back(y_test);\n      x_near_valid_point.push_back(x[first_valid_index]);\n      y_near_valid_point.push_back(y[first_valid_index]);\n    }\n  }\n  if (last_valid_index < y.size() - 1) {\n    // Check for a root between last_valid_index and last_valid_index+1.\n    const double x_test = x[last_valid_index] +\n                          0.5 * (x[last_valid_index + 1] - x[last_valid_index]);\n    const auto y_test = f(x_test);\n    if (y_test.has_value() and\n        y[last_valid_index].value() * y_test.value() <= 0.0) {\n      // Bracketed!\n      return {x[last_valid_index], x_test, y[last_valid_index].value(),\n              y_test.value()};\n    } else {\n      if (first_valid_index != last_valid_index or first_valid_index == 0) {\n        x_near_valid_point.push_back(x[last_valid_index]);\n        y_near_valid_point.push_back(y[last_valid_index]);\n      }  // else we already pushed back last_valid_index (==first_valid_index).\n      x_near_valid_point.push_back(x_test);\n      y_near_valid_point.push_back(y_test);\n      x_near_valid_point.push_back(x[last_valid_index + 1]);\n      y_near_valid_point.push_back(y[last_valid_index + 1]);\n    }\n  }\n\n  // We have one or more valid points but we didn't find a bracket.\n  // That is, we have something like \"X X o o X X\" or \"X X o o\" or \"o o X X\".\n  // So recurse, zooming in to the boundary (either one boundary or two\n  // boundaries) between valid and invalid points.\n  // Note that \"o o o o\" is prohibited by our assumptions, and checked for\n  // above just in case it occurs by mistake.\n  return bracket_by_contracting(x_near_valid_point, y_near_valid_point, f,\n                                level + 1);\n}\n}  // namespace bracketing_detail\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Brackets the root of the function `f`, assuming a single\n * root in a given interval \\f$f[x_\\mathrm{lo},x_\\mathrm{up}]\\f$\n * and assuming that `f` is defined only in an unknown smaller\n * interval \\f$f[x_a,x_b]\\f$ where\n * \\f$x_\\mathrm{lo} \\leq x_a \\leq x_b \\leq x_\\mathrm{hi}\\f$.\n *\n * `f` is a unary invokable that takes a `double` which is the current value at\n * which to evaluate `f`.  `f` returns a `std::optional<double>` which\n * evaluates to false if the function is undefined at the supplied point.\n *\n * Assumes that there is only one root in the interval.\n *\n * Assumes that if \\f$f(x_1)\\f$ and \\f$f(x_2)\\f$ are both defined for\n * some \\f$(x_1,x_2)\\f$, then \\f$f(x)\\f$ is defined for all \\f$x\\f$\n * between \\f$x_1\\f$ and \\f$x_2\\f$.\n *\n * On input, assumes that the root lies in the interval\n * [`lower_bound`,`upper_bound`].  Optionally takes a `guess` for the\n * location of the root.  If `guess` is supplied, then evaluates the\n * function first at `guess` and `upper_bound` before trying\n * `lower_bound`: this means that it would be optimal if `guess`\n * underestimates the actual root and if `upper_bound` was less likely\n * to be undefined than `lower_bound`.\n *\n * On return, `lower_bound` and `upper_bound` are replaced with values that\n * bracket the root and for which the function is defined, and\n * `f_at_lower_bound` and `f_at_upper_bound` are replaced with\n * `f` evaluated at those bracketing points.\n *\n * `bracket_possibly_undefined_function_in_interval` throws an error if\n *  all points are valid but of the same sign (because that would indicate\n *  multiple roots but we assume only one root), if no root exists, or\n *  if the range of a sign change is sufficently small relative to the\n *  given interval that the number of iterations to find the root is exceeded.\n *\n */\ntemplate <typename Functor>\nvoid bracket_possibly_undefined_function_in_interval(\n    const gsl::not_null<double*> lower_bound,\n    const gsl::not_null<double*> upper_bound,\n    const gsl::not_null<double*> f_at_lower_bound,\n    const gsl::not_null<double*> f_at_upper_bound, const Functor& f,\n    const double guess) {\n  // Initial values of x1,x2,y1,y2.  Use `guess` and `upper_bound`,\n  // because in typical usage `guess` underestimates the actual\n  // root, and `lower_bound` is more likely than `upper_bound` to be\n  // invalid.\n  double x1 = guess;\n  double x2 = *upper_bound;\n  auto y1 = f(x1);\n  auto y2 = f(x2);\n  const bool y1_defined = y1.has_value();\n  const bool y2_defined = y2.has_value();\n  if (not(y1_defined and y2_defined and y1.value() * y2.value() <= 0.0)) {\n    // Root is not bracketed.\n    // Before moving to the general algorithm, try the remaining\n    // input point that was supplied.\n    const double x3 = *lower_bound;\n    const auto y3 = f(x3);\n    const bool y3_defined = y3.has_value();\n    if (y1_defined and y3_defined and y1.value() * y3.value() <= 0.0) {\n      // Bracketed! Throw out x2,y2.  Rename variables to keep x1 < x2.\n      x2 = x1;\n      y2 = y1;\n      x1 = x3;\n      y1 = y3;\n    } else {\n      // Our simple checks didn't work, so call the more general method.\n      // There are 8 cases:\n      //\n      // y3 y1 y2\n      // --------\n      // X  X  X\n      // o  X  X\n      // X  o  X\n      // o  o  X\n      // X  o  o\n      // o  o  o\n      // X  X  o\n      // o  X  o\n      //\n      // where X means an invalid point, o means a valid point.\n      // All valid points have the same sign, or we would have found a\n      // bracket already.\n      //\n      // Before calling the general case, error on \"o o o\" and \"o X o\".\n      // Both of these are prohibited by our assumptions (we\n      // assume the root is in the interval so no \"o o o\", and we\n      // assume that all invalid points are at the end of interval, so no\n      // \"o X o\").\n      if (y2_defined and y3_defined) {\n        ERROR(\n            \"bracket_possibly_undefined_function_in_interval: found \"\n            \"case that should not happen under our assumptions.\");\n      }\n      try {\n        std::tie(x1, x2, y1, y2) = bracketing_detail::bracket_by_contracting(\n            {{x3, x1, x2}}, {{y3, y1, y2}}, f);\n      } catch (std::runtime_error& e) {\n        std::stringstream ss;\n        ss << \"bracket_by_contracting: Cannot bracket root between \"\n           << *lower_bound << \" and \" << *upper_bound\n           << \". Internal error message is: '\" << e.what() << \"'\";\n        throw std::runtime_error(ss.str());\n      }\n    }\n  }\n  *f_at_lower_bound = y1.value();\n  *f_at_upper_bound = y2.value();\n  *lower_bound = x1;\n  *upper_bound = x2;\n}\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Brackets the single root of the\n * function `f` for each element in a `DataVector`, assuming the root\n * lies in the given interval and that `f` may be undefined at some\n * points in the interval.\n *\n * `f` is a binary invokable that takes a `double` and a `size_t` as\n * arguments.  The `double` is the current value at which to evaluate\n * `f`, and the `size_t` is the index into the `DataVector`s.  `f`\n * returns a `std::optional<double>` which evaluates to false if the\n * function is undefined at the supplied point.\n *\n * Assumes that there is only one root in the interval.\n *\n * Assumes that if \\f$f(x_1)\\f$ and \\f$f(x_2)\\f$ are both defined for\n * some \\f$(x_1,x_2)\\f$, then \\f$f(x)\\f$ is defined for all \\f$x\\f$\n * between \\f$x_1\\f$ and \\f$x_2\\f$.\n *\n * On input, assumes that the root lies in the interval\n * [`lower_bound`,`upper_bound`].  Optionally takes a `guess` for the\n * location of the root.\n *\n * On return, `lower_bound` and `upper_bound` are replaced with values that\n * bracket the root and for which the function is defined, and\n * `f_at_lower_bound` and `f_at_upper_bound` are replaced with\n * `f` evaluated at those bracketing points.\n *\n */\ntemplate <typename Functor>\nvoid bracket_possibly_undefined_function_in_interval(\n    const gsl::not_null<DataVector*> lower_bound,\n    const gsl::not_null<DataVector*> upper_bound,\n    const gsl::not_null<DataVector*> f_at_lower_bound,\n    const gsl::not_null<DataVector*> f_at_upper_bound, const Functor& f,\n    const DataVector& guess) {\n  for (size_t s = 0; s < lower_bound->size(); ++s) {\n    bracket_possibly_undefined_function_in_interval(\n        &((*lower_bound)[s]), &((*upper_bound)[s]), &((*f_at_lower_bound)[s]),\n        &((*f_at_upper_bound)[s]), [&f, &s](const double x) { return f(x, s); },\n        guess[s]);\n  }\n}\n\n/*\n * Version of `bracket_possibly_undefined_function_in_interval`\n * without a supplied initial guess; uses the mean of `lower_bound` and\n * `upper_bound` as the guess.\n */\ntemplate <typename Functor>\nvoid bracket_possibly_undefined_function_in_interval(\n    const gsl::not_null<double*> lower_bound,\n    const gsl::not_null<double*> upper_bound,\n    const gsl::not_null<double*> f_at_lower_bound,\n    const gsl::not_null<double*> f_at_upper_bound, const Functor& f) {\n  bracket_possibly_undefined_function_in_interval(\n      lower_bound, upper_bound, f_at_lower_bound, f_at_upper_bound, f,\n      *lower_bound + 0.5 * (*upper_bound - *lower_bound));\n}\n\n/*\n * Version of `bracket_possibly_undefined_function_in_interval`\n * without a supplied initial guess; uses the mean of `lower_bound` and\n * `upper_bound` as the guess.\n */\ntemplate <typename Functor>\nvoid bracket_possibly_undefined_function_in_interval(\n    const gsl::not_null<DataVector*> lower_bound,\n    const gsl::not_null<DataVector*> upper_bound,\n    const gsl::not_null<DataVector*> f_at_lower_bound,\n    const gsl::not_null<DataVector*> f_at_upper_bound, const Functor& f) {\n  bracket_possibly_undefined_function_in_interval(\n      lower_bound, upper_bound, f_at_lower_bound, f_at_upper_bound, f,\n      *lower_bound + 0.5 * (*upper_bound - *lower_bound));\n}\n}  // namespace RootFinder\n"
  },
  {
    "path": "src/NumericalAlgorithms/RootFinding/TOMS748.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares function RootFinder::toms748\n\n#pragma once\n\n#include <functional>\n#include <iomanip>\n#include <ios>\n#include <limits>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Simd/Simd.hpp\"\n\nnamespace RootFinder {\nnamespace toms748_detail {\n// Original implementation of TOMS748 is from Boost:\n//  (C) Copyright John Maddock 2006.\n//  Use, modification and distribution are subject to the\n//  Boost Software License, Version 1.0. (See accompanying file\n//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)\n//\n// Significant changes made to pretty much all of it to support SIMD by Nils\n// Deppe. Changes are copyrighted by SXS Collaboration under the MIT License.\n\ntemplate <typename T>\nT safe_div(const T& num, const T& denom, const T& r) {\n  if constexpr (std::is_floating_point_v<T>) {\n    using std::abs;\n    if (abs(denom) < static_cast<T>(1)) {\n      if (abs(denom * std::numeric_limits<T>::max()) <= abs(num)) {\n        return r;\n      }\n    }\n    return num / denom;\n  } else {\n    // return num / denom without overflow, return r if overflow would occur.\n    //\n    // To do this we need to handle the following cases:\n    // 1. fabs(denom) < 1 AND fabs(denom * max) <= fabs(num):\n    //    return r\n    // 2. return num / denom\n    //\n    // We do this by creating 2 masks.\n    // 1. `mask0` selects where fabs(denom) < 1. This is where we _may_ have\n    //    issues with division by zero or overflow.\n    // 2. `mask` selects where fabs(denom) < 1 AND fabs(denom * max)<=fabs(num)\n    //    * Note: the edge case of fabs(num)==max could be problematic, but if\n    //            you're dealing with numbers like that you are likely in\n    //            trouble anyway.\n    // The second mask is where we must avoid division by zero and instead\n    // return `r`.\n    const auto mask0 = fabs(denom) < static_cast<T>(1);\n    // Note: if denom >= 1.0 you get an FPE because of overflow from\n    // `max() * (1. + value)`, which is why `mask0` is necessary.\n    const auto mask = fabs(simd::select(mask0, denom, static_cast<T>(1.0)) *\n                           std::numeric_limits<T>::max()) <= fabs(num);\n    const auto new_denom = simd::select(mask, static_cast<T>(1), denom);\n    return simd::select(mask, r, num / new_denom);\n  }\n}\n\ntemplate <typename T>\nT secant_interpolate(const T& a, const T& b, const T& fa, const T& fb,\n                     const simd::mask_type_t<T>& incomplete_mask) {\n  //\n  // Performs standard secant interpolation of [a,b] given\n  // function evaluations f(a) and f(b).  Performs a bisection\n  // if secant interpolation would leave us very close to either\n  // a or b.  Rationale: we only call this function when at least\n  // one other form of interpolation has already failed, so we know\n  // that the function is unlikely to be smooth with a root very\n  // close to a or b.\n  //\n  // For lanes where `incomplete_mask` is false (already converged), the\n  // denominator `fb - fa` may be stale or zero. We substitute 1 to avoid\n  // undefined behavior (e.g. division by zero); the returned value for those\n  // lanes is meaningless and should not be used by the caller. See\n  // `cubic_interpolate` for the same pattern.\n  //\n  const T tol_batch = std::numeric_limits<T>::epsilon() * static_cast<T>(5);\n  // WARNING: There are several different ways to implement the interpolation\n  // that all have different rounding properties. Unfortunately this means that\n  // tolerances even at 1e-14 can be difficult to achieve generically.\n  //\n  // `g` below is:\n  // const T g = (fa / (fb - fa));\n  //\n  // const T c = simd::fma((fa / (fb - fa)), (a - b), a); // fails\n  //\n  // const T c = a * (static_cast<T>(1) - g) + g * b; // works\n  //\n  // const T c = simd::fma(a, (static_cast<T>(1) - g), g * b); // works\n  //\n  // Original Boost code:\n  // const T c = a - (fa / (fb - fa)) * (b - a); // works\n\n  const T c =\n      a - (fa / simd::select(incomplete_mask, fb - fa, static_cast<T>(1))) *\n              (b - a);\n  return simd::select((c <= simd::fma(fabs(a), tol_batch, a)) or\n                          (c >= simd::fnma(fabs(b), tol_batch, b)),\n                      static_cast<T>(0.5) * (a + b), c);\n}\n\ntemplate <bool AssumeFinite, typename T>\nT quadratic_interpolate(const T& a, const T& b, const T& d, const T& fa,\n                        const T& fb, const T& fd,\n                        const simd::mask_type_t<T>& incomplete_mask,\n                        const unsigned count) {\n  // Performs quadratic interpolation to determine the next point,\n  // takes count Newton steps to find the location of the\n  // quadratic polynomial.\n  //\n  // Point d must lie outside of the interval [a,b], it is the third\n  // best approximation to the root, after a and b.\n  //\n  // Note: this does not guarantee to find a root\n  // inside [a, b], so we fall back to a secant step should\n  // the result be out of range.\n  //\n  // Start by obtaining the coefficients of the quadratic polynomial:\n  const T B = safe_div(fb - fa, b - a, std::numeric_limits<T>::max());\n  T A = safe_div(fd - fb, d - b, std::numeric_limits<T>::max());\n  A = safe_div(A - B, d - a, static_cast<T>(0));\n\n  const auto secant_failure_mask = A == static_cast<T>(0) and incomplete_mask;\n  T result_secant{};\n  if (UNLIKELY(simd::any(secant_failure_mask))) {\n    // failure to determine coefficients, try a secant step:\n    result_secant = secant_interpolate(a, b, fa, fb, secant_failure_mask);\n    if (UNLIKELY(simd::all(secant_failure_mask or (not incomplete_mask)))) {\n      return result_secant;\n    }\n  }\n\n  // Determine the starting point of the Newton steps:\n  //\n  // Note: unlike Boost, we assume A*fa doesn't overflow. This speeds up the\n  // code quite a bit.\n  T c = AssumeFinite\n            ? simd::select(A * fa > static_cast<T>(0), a, b)\n            : simd::select(simd::sign(A) * simd::sign(fa) > static_cast<T>(0),\n                           a, b);\n\n  // Take the Newton steps:\n  const T two_A = static_cast<T>(2) * A;\n  const T half_a_plus_b = 0.5 * (a + b);\n  const T one_minus_a = static_cast<T>(1) - a;\n  const T B_minus_A_times_b = B - A * b;\n  for (unsigned i = 1; i <= count; ++i) {\n    c -= safe_div(simd::fma(simd::fma(A, c, B_minus_A_times_b), c - a, fa),\n                  simd::fma(two_A, c - half_a_plus_b, B), one_minus_a + c);\n  }\n  if (const auto mask = ((c <= a) or (c >= b)) and incomplete_mask;\n      simd::any(mask)) {\n    // Failure, try a secant step:\n    c = simd::select(mask, secant_interpolate(a, b, fa, fb, mask), c);\n  }\n  return simd::select(secant_failure_mask, result_secant, c);\n}\n\ntemplate <bool AssumeFinite, typename T>\nT cubic_interpolate(const T& a, const T& b, const T& d, const T& e, const T& fa,\n                    const T& fb, const T& fd, const T& fe,\n                    const simd::mask_type_t<T>& incomplete_mask) {\n  // Uses inverse cubic interpolation of f(x) at points\n  // [a,b,d,e] to obtain an approximate root of f(x).\n  // Points d and e lie outside the interval [a,b]\n  // and are the third and forth best approximations\n  // to the root that we have found so far.\n  //\n  // Note: this does not guarantee to find a root\n  // inside [a, b], so we fall back to quadratic\n  // interpolation in case of an erroneous result.\n  //\n  // This commented chunk is the original Boost implementation translated into\n  // simd. The actual code below is a heavily optimized version.\n  //\n  // const T q11 = (d - e) * fd / (fe - fd);\n  // const T q21 = (b - d) * fb / (fd - fb);\n  // const T q31 = (a - b) * fa / (fb - fa);\n  // const T d21 = (b - d) * fd / (fd - fb);\n  // const T d31 = (a - b) * fb / (fb - fa);\n  //\n  // const T q22 = (d21 - q11) * fb / (fe - fb);\n  // const T q32 = (d31 - q21) * fa / (fd - fa);\n  // const T d32 = (d31 - q21) * fd / (fd - fa);\n  // const T q33 = (d32 - q22) * fa / (fe - fa);\n  //\n  // T c = q31 + q32 + q33 + a;\n\n  // The optimized implementation here is L1-cache bound. That is, we aren't\n  // able to completely saturate the FP units because we are waiting on the L1\n  // cache. While not ideal, that's okay and just part of the algorithm.\n  const T denom_fb_fa = fb - fa;\n  const T denom_fd_fb = fd - fb;\n  const T denom_fd_fa = fd - fa;\n  const T denom_fe_fd = fe - fd;\n  const T denom_fe_fb = fe - fb;\n  const T denom =\n      denom_fe_fb * denom_fe_fd * denom_fd_fa * denom_fd_fb * (fe - fa);\n\n  // Avoid division by zero with mask.\n  const T fa_by_denom = fa / simd::select(incomplete_mask, denom_fb_fa * denom,\n                                          static_cast<T>(1));\n\n  const T d31 = (a - b);\n  const T q21 = (b - d);\n  const T q32 = simd::fms(denom_fd_fb, d31, denom_fb_fa * q21) * denom_fe_fb *\n                denom_fe_fd;\n  const T q22 = simd::fms(denom_fe_fd, q21, (d - e) * denom_fd_fb) * fd *\n                denom_fb_fa * denom_fd_fa;\n\n  // Note: the reduction in rounding error that comes from the improvement by\n  // Stoer & Bulirsch to Neville's algorithm is adding `a` at the very end as\n  // we do below. Alternative ways of evaluating polynomials do not delay this\n  // inclusion of `a`, and so then when the correction to `a` is small,\n  // floating point errors decrease the accuracy of the result.\n  T c = simd::fma(\n      fa_by_denom,\n      simd::fma(fb, simd::fms(q32, (fe + denom_fd_fa), q22), d31 * denom), a);\n\n  if (const auto mask = ((c <= a) or (c >= b)) and incomplete_mask;\n      simd::any(mask)) {\n    // Out of bounds step, fall back to quadratic interpolation:\n    //\n    // Note: we only apply quadratic interpolation at points where cubic\n    // failed and that aren't already at a root.\n    c = simd::select(\n        mask, quadratic_interpolate<AssumeFinite>(a, b, d, fa, fb, fd, mask, 3),\n        c);\n  }\n\n  return c;\n}\n\ntemplate <bool AssumeFinite, typename F, typename T>\nvoid bracket(F f, T& a, T& b, T c, T& fa, T& fb, T& d, T& fd,\n             const simd::mask_type_t<T>& incomplete_mask) {\n  // Given a point c inside the existing enclosing interval\n  // [a, b] sets a = c if f(c) == 0, otherwise finds the new\n  // enclosing interval: either [a, c] or [c, b] and sets\n  // d and fd to the point that has just been removed from\n  // the interval.  In other words d is the third best guess\n  // to the root.\n  //\n  // Note: `bracket` will only modify slots marked as `true` in\n  //       `incomplete_mask`\n  const T tol_batch = std::numeric_limits<T>::epsilon() * static_cast<T>(2);\n\n  // If the interval [a,b] is very small, or if c is too close\n  // to one end of the interval then we need to adjust the\n  // location of c accordingly. This is:\n  //\n  //   if ((b - a) < 2 * tol * a) {\n  //     c = a + (b - a) / 2;\n  //   } else if (c <= a + fabs(a) * tol) {\n  //     c = a + fabs(a) * tol;\n  //   } else if (c >= b - fabs(b) * tol) {\n  //     c = b - fabs(b) * tol;\n  //   }\n  const T a_filt = simd::fma(fabs(a), tol_batch, a);\n  const T b_filt = simd::fnma(fabs(b), tol_batch, b);\n  const T b_minus_a = b - a;\n  c = simd::select(\n      (static_cast<T>(2) * tol_batch * a > b_minus_a) and incomplete_mask,\n      simd::fma(b_minus_a, static_cast<T>(0.5), a),\n      simd::select(c <= a_filt, a_filt, simd::select(c >= b_filt, b_filt, c)));\n\n  // Invoke f(c):\n  T fc = f(c);\n\n  // if we have a zero then we have an exact solution to the root:\n  const auto fc_is_zero_mask = (fc == static_cast<T>(0));\n  if (const auto mask = fc_is_zero_mask and incomplete_mask;\n      UNLIKELY(simd::any(mask))) {\n    a = simd::select(mask, c, a);\n    fa = simd::select(mask, static_cast<T>(0), fa);\n    d = simd::select(mask, static_cast<T>(0), d);\n    fd = simd::select(mask, static_cast<T>(0), fd);\n    if (UNLIKELY(simd::all(mask or not incomplete_mask))) {\n      return;\n    }\n  }\n\n  // Non-zero fc, update the interval:\n  //\n  // Note: unlike Boost, we assume fa*fc doesn't overflow. This speeds up the\n  // code quite a bit.\n  //\n  // Boost code is:\n  // if (boost::math::sign(fa) * boost::math::sign(fc) < 0) {...} else {...}\n  using simd::sign;\n  const auto sign_mask = AssumeFinite\n                             ? (fa * fc < static_cast<T>(0))\n                             : (sign(fa) * sign(fc) < static_cast<T>(0));\n  const auto mask_if =\n      (sign_mask and (not fc_is_zero_mask)) and incomplete_mask;\n  d = simd::select(mask_if, b, d);\n  fd = simd::select(mask_if, fb, fd);\n  b = simd::select(mask_if, c, b);\n  fb = simd::select(mask_if, fc, fb);\n\n  const auto mask_else =\n      ((not sign_mask) and (not fc_is_zero_mask)) and incomplete_mask;\n  d = simd::select(mask_else, a, d);\n  fd = simd::select(mask_else, fa, fd);\n  a = simd::select(mask_else, c, a);\n  fa = simd::select(mask_else, fc, fa);\n}\n\ntemplate <bool AssumeFinite, class F, class T, class Tol>\nstd::pair<T, T> toms748_solve(F f, const T& ax, const T& bx, const T& fax,\n                              const T& fbx, Tol tol,\n                              const simd::mask_type_t<T>& ignore_filter,\n                              size_t& max_iter) {\n  // Main entry point and logic for Toms Algorithm 748\n  // root finder.\n  if (UNLIKELY(simd::any(ax > bx))) {\n    ERROR_AS(\"Lower bound is larger than upper bound\", std::domain_error);\n  }\n\n  // Sanity check - are we allowed to iterate at all?\n  if (UNLIKELY(max_iter == 0)) {\n    return std::pair{ax, bx};\n  }\n\n  size_t count = max_iter;\n  // mu is a parameter in the algorithm that must be between (0, 1).\n  static const T mu = 0.5f;\n\n  // initialise a, b and fa, fb:\n  T a = ax;\n  T b = bx;\n  T fa = fax;\n  T fb = fbx;\n  CAPTURE_FOR_ERROR(ax);\n  CAPTURE_FOR_ERROR(bx);\n  CAPTURE_FOR_ERROR(fax);\n  CAPTURE_FOR_ERROR(fbx);\n\n  const auto fa_is_zero_mask = (fa == static_cast<T>(0));\n  const auto fb_is_zero_mask = (fb == static_cast<T>(0));\n  auto completion_mask =\n      tol(a, b) or fa_is_zero_mask or fb_is_zero_mask or ignore_filter;\n  auto incomplete_mask = not completion_mask;\n  if (UNLIKELY(simd::all(completion_mask))) {\n    max_iter = 0;\n    return std::pair{simd::select(fb_is_zero_mask, b, a),\n                     simd::select(fa_is_zero_mask, a, b)};\n  }\n\n  // Note: unlike Boost, we can assume fa*fb doesn't overflow when possible.\n  // This speeds up the code quite a bit.\n  if (UNLIKELY(simd::any((AssumeFinite ? (fa * fb > static_cast<T>(0))\n                                       : (simd::sign(fa) * simd::sign(fb) >\n                                          static_cast<T>(0))) and\n                         (not fa_is_zero_mask) and (not fb_is_zero_mask)))) {\n    ERROR_AS(\"Parameters lower and upper bounds do not bracket a root.\",\n             std::domain_error);\n  }\n  // dummy value for fd, e and fe:\n  T fe(static_cast<T>(1e5F));\n  T e(static_cast<T>(1e5F));\n  T fd(static_cast<T>(1e5F));\n\n  T c(0.0);\n  T d(0.0);\n\n  // We can't use signaling_NaN() because we do a max comparison at the end to\n  // 0, and signaling_NaN when compared to 0 raises an FPE.\n  const T nan(std::numeric_limits<T>::quiet_NaN());\n  auto completed_a = simd::select(completion_mask, a, nan);\n  auto completed_b = simd::select(completion_mask, b, nan);\n  auto completed_fa = simd::select(completion_mask, fa, nan);\n  auto completed_fb = simd::select(completion_mask, fb, nan);\n  const auto update_completed = [&fa, &fb, &completion_mask, &incomplete_mask,\n                                 &completed_a, &completed_b, &a, &b,\n                                 &completed_fa, &completed_fb, &tol]() {\n    const auto new_completed =\n        (fa == static_cast<T>(0) or tol(a, b)) and (not completion_mask);\n    completed_a = simd::select(new_completed, a, completed_a);\n    completed_b = simd::select(new_completed, b, completed_b);\n    completed_fa = simd::select(new_completed, fa, completed_fa);\n    completed_fb = simd::select(new_completed, fb, completed_fb);\n    completion_mask = new_completed or completion_mask;\n    incomplete_mask = not completion_mask;\n    // returns true if _all_ simd registers have been completed\n    return simd::all(completion_mask);\n  };\n\n  if (simd::any(fa != static_cast<T>(0))) {\n    // On the first step we take a secant step:\n    c = toms748_detail::secant_interpolate(a, b, fa, fb, incomplete_mask);\n    toms748_detail::bracket<AssumeFinite>(f, a, b, c, fa, fb, d, fd,\n                                          incomplete_mask);\n    --count;\n\n    // Note: The Boost fa!=0 check is handled with the completion_mask.\n    if (count and not update_completed()) {\n      // On the second step we take a quadratic interpolation:\n      c = toms748_detail::quadratic_interpolate<AssumeFinite>(\n          a, b, d, fa, fb, fd, incomplete_mask, 2);\n      e = d;\n      fe = fd;\n      toms748_detail::bracket<AssumeFinite>(f, a, b, c, fa, fb, d, fd,\n                                            incomplete_mask);\n      --count;\n      update_completed();\n    }\n  }\n\n  T u(std::numeric_limits<T>::signaling_NaN());\n  T fu(std::numeric_limits<T>::signaling_NaN());\n  T a0(std::numeric_limits<T>::signaling_NaN());\n  T b0(std::numeric_limits<T>::signaling_NaN());\n\n  // Note: The Boost fa!=0 check is handled with the completion_mask.\n  while (count and not simd::all(completion_mask)) {\n    // save our brackets:\n    a0 = a;\n    b0 = b;\n    // Starting with the third step taken\n    // we can use either quadratic or cubic interpolation.\n    // Cubic interpolation requires that all four function values\n    // fa, fb, fd, and fe are distinct, should that not be the case\n    // then variable prof will get set to true, and we'll end up\n    // taking a quadratic step instead.\n    static const T min_diff = std::numeric_limits<T>::min() * 32;\n    bool prof =\n        simd::any(((fabs(fa - fb) < min_diff) or (fabs(fa - fd) < min_diff) or\n                   (fabs(fa - fe) < min_diff) or (fabs(fb - fd) < min_diff) or\n                   (fabs(fb - fe) < min_diff) or (fabs(fd - fe) < min_diff)) and\n                  incomplete_mask);\n    if (prof) {\n      c = toms748_detail::quadratic_interpolate<AssumeFinite>(\n          a, b, d, fa, fb, fd, incomplete_mask, 2);\n    } else {\n      c = toms748_detail::cubic_interpolate<AssumeFinite>(\n          a, b, d, e, fa, fb, fd, fe, incomplete_mask);\n    }\n    // re-bracket, and check for termination:\n    e = d;\n    fe = fd;\n    toms748_detail::bracket<AssumeFinite>(f, a, b, c, fa, fb, d, fd,\n                                          incomplete_mask);\n    if ((0 == --count) or update_completed()) {\n      break;\n    }\n    // Now another interpolated step:\n    prof =\n        simd::any(((fabs(fa - fb) < min_diff) or (fabs(fa - fd) < min_diff) or\n                   (fabs(fa - fe) < min_diff) or (fabs(fb - fd) < min_diff) or\n                   (fabs(fb - fe) < min_diff) or (fabs(fd - fe) < min_diff)) and\n                  incomplete_mask);\n    if (prof) {\n      c = toms748_detail::quadratic_interpolate<AssumeFinite>(\n          a, b, d, fa, fb, fd, incomplete_mask, 3);\n    } else {\n      c = toms748_detail::cubic_interpolate<AssumeFinite>(\n          a, b, d, e, fa, fb, fd, fe, incomplete_mask);\n    }\n    // Bracket again, and check termination condition, update e:\n    toms748_detail::bracket<AssumeFinite>(f, a, b, c, fa, fb, d, fd,\n                                          incomplete_mask);\n    if ((0 == --count) or update_completed()) {\n      break;\n    }\n\n    // Now we take a double-length secant step:\n    const auto fabs_fa_less_fabs_fb_mask =\n        (fabs(fa) < fabs(fb)) and incomplete_mask;\n    u = simd::select(fabs_fa_less_fabs_fb_mask, a, b);\n    fu = simd::select(fabs_fa_less_fabs_fb_mask, fa, fb);\n    const T b_minus_a = b - a;\n    // Assumes that bounds a & b are not so close that fa == fb. Boost makes\n    // this assumption too. If this is violated then the algorithm doesn't\n    // work since the function at least appears constant.\n\n    // Safeguard complete lanes: `fb - fa` may be stale or zero for lanes\n    // that have already converged. Use 1 as a harmless dummy denominator;\n    // the result for complete lanes is discarded downstream.\n    c = simd::fnma(\n        static_cast<T>(2) *\n            (fu / simd::select(incomplete_mask, fb - fa, static_cast<T>(1))),\n        b_minus_a, u);\n    c = simd::select(static_cast<T>(2) * fabs(c - u) > b_minus_a,\n                     simd::fma(static_cast<T>(0.5), b_minus_a, a), c);\n\n    // Bracket again, and check termination condition:\n    e = d;\n    fe = fd;\n    toms748_detail::bracket<AssumeFinite>(f, a, b, c, fa, fb, d, fd,\n                                          incomplete_mask);\n    if ((0 == --count) or update_completed()) {\n      break;\n    }\n\n    // And finally... check to see if an additional bisection step is\n    // to be taken, we do this if we're not converging fast enough:\n    const auto bisection_mask = (b - a) >= mu * (b0 - a0) and incomplete_mask;\n    if (LIKELY(simd::none(bisection_mask))) {\n      continue;\n    }\n    // bracket again on a bisection:\n    //\n    // Note: the mask ensures we only ever modify the slots that the mask has\n    // identified as needing to be modified.\n    e = simd::select(bisection_mask, d, e);\n    fe = simd::select(bisection_mask, fd, fe);\n    toms748_detail::bracket<AssumeFinite>(\n        f, a, b, simd::fma((b - a), static_cast<T>(0.5), a), fa, fb, d, fd,\n        bisection_mask);\n    --count;\n    if (update_completed()) {\n      break;\n    }\n  }  // while loop\n\n  max_iter -= count;\n  completed_b =\n      simd::select(completed_fa == static_cast<T>(0), completed_a, completed_b);\n  completed_a =\n      simd::select(completed_fb == static_cast<T>(0), completed_b, completed_a);\n  return std::pair{completed_a, completed_b};\n}\n}  // namespace toms748_detail\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Finds the root of the function `f` with the TOMS_748 method.\n *\n * `f` is a unary invokable that takes a `double` which is the current value at\n * which to evaluate `f`. An example is below.\n *\n * \\snippet Test_TOMS748.cpp double_root_find\n *\n * The TOMS_748 algorithm searches for a root in the interval [`lower_bound`,\n * `upper_bound`], and will throw if this interval does not bracket a root,\n * i.e. if `f(lower_bound) * f(upper_bound) > 0`.\n *\n * The arguments `f_at_lower_bound` and `f_at_upper_bound` are optional, and\n * are the function values at `lower_bound` and `upper_bound`. These function\n * values are often known because the user typically checks if a root is\n * bracketed before calling `toms748`; passing the function values here saves\n * two function evaluations.\n *\n * \\note if `AssumeFinite` is true than the code assumes all numbers are\n * finite and that `a > 0` is equivalent to `sign(a) > 0`. This reduces\n * runtime but will cause bugs if the numbers aren't finite. It also assumes\n * that products like `fa * fb` are also finite.\n *\n * \\requires Function `f` is invokable with a `double`\n *\n * \\throws `convergence_error` if the requested tolerance is not met after\n *                            `max_iterations` iterations.\n */\ntemplate <bool AssumeFinite = false, typename Function, typename T>\nT toms748(const Function& f, const T lower_bound, const T upper_bound,\n          const T f_at_lower_bound, const T f_at_upper_bound,\n          const simd::scalar_type_t<T> absolute_tolerance,\n          const simd::scalar_type_t<T> relative_tolerance,\n          const size_t max_iterations = 100,\n          const simd::mask_type_t<T> ignore_filter =\n              static_cast<simd::mask_type_t<T>>(0)) {\n  ASSERT(relative_tolerance >\n             std::numeric_limits<simd::scalar_type_t<T>>::epsilon(),\n         \"The relative tolerance is too small. Got \"\n             << relative_tolerance << \" but must be at least \"\n             << std::numeric_limits<simd::scalar_type_t<T>>::epsilon());\n  if (simd::any(f_at_lower_bound * f_at_upper_bound > 0.0)) {\n    ERROR(\"Root not bracketed: f(\" << lower_bound << \") = \" << f_at_lower_bound\n                                   << \", f(\" << upper_bound\n                                   << \") = \" << f_at_upper_bound);\n  }\n\n  std::size_t max_iters = max_iterations;\n\n  // This solver requires tol to be passed as a termination condition. This\n  // termination condition is equivalent to the convergence criteria used by the\n  // GSL\n  const auto tol = [absolute_tolerance, relative_tolerance](const T& lhs,\n                                                            const T& rhs) {\n    return simd::abs(lhs - rhs) <=\n           simd::fma(T(relative_tolerance),\n                     simd::min(simd::abs(lhs), simd::abs(rhs)),\n                     T(absolute_tolerance));\n  };\n  auto result = toms748_detail::toms748_solve<AssumeFinite>(\n      f, lower_bound, upper_bound, f_at_lower_bound, f_at_upper_bound, tol,\n      ignore_filter, max_iters);\n  if (max_iters >= max_iterations) {\n    ERROR_AS(\n        \"toms748 reached max iterations without converging.\\nAbsolute \"\n        \"tolerance: \"\n            << absolute_tolerance << \"\\nRelative tolerance: \"\n            << relative_tolerance << \"\\nResult: \" << get_output(result.first)\n            << \" \" << get_output(result.second),\n        convergence_error);\n  }\n  return simd::fma(static_cast<T>(0.5), (result.second - result.first),\n                   result.first);\n}\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Finds the root of the function `f` with the TOMS_748 method, where\n * function values are not supplied at the lower and upper bounds.\n *\n * \\note if `AssumeFinite` is true than the code assumes all numbers are\n * finite and that `a > 0` is equivalent to `sign(a) > 0`. This reduces\n * runtime but will cause bugs if the numbers aren't finite. It also assumes\n * that products like `fa * fb` are also finite.\n */\ntemplate <bool AssumeFinite = false, typename Function, typename T>\nT toms748(const Function& f, const T lower_bound, const T upper_bound,\n          const simd::scalar_type_t<T> absolute_tolerance,\n          const simd::scalar_type_t<T> relative_tolerance,\n          const size_t max_iterations = 100,\n          const simd::mask_type_t<T> ignore_filter =\n              static_cast<simd::mask_type_t<T>>(0)) {\n  return toms748<AssumeFinite>(\n      f, lower_bound, upper_bound, f(lower_bound), f(upper_bound),\n      absolute_tolerance, relative_tolerance, max_iterations, ignore_filter);\n}\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Finds the root of the function `f` with the TOMS_748 method on each\n * element in a `DataVector`.\n *\n * `f` is a binary invokable that takes a `double` as its first argument and a\n * `size_t` as its second. The `double` is the current value at which to\n * evaluate `f`, and the `size_t` is the current index into the `DataVector`s.\n * Below is an example of how to root find different functions by indexing into\n * a lambda-captured `DataVector` using the `size_t` passed to `f`.\n *\n * \\snippet Test_TOMS748.cpp datavector_root_find\n *\n * For each index `i` into the DataVector, the TOMS_748 algorithm searches for a\n * root in the interval [`lower_bound[i]`, `upper_bound[i]`], and will throw if\n * this interval does not bracket a root,\n * i.e. if `f(lower_bound[i], i) * f(upper_bound[i], i) > 0`.\n *\n * See the [Boost](http://www.boost.org/) documentation for more details.\n *\n * \\requires Function `f` be callable with a `double` and a `size_t`\n *\n * \\note if `AssumeFinite` is true than the code assumes all numbers are\n * finite and that `a > 0` is equivalent to `sign(a) > 0`. This reduces\n * runtime but will cause bugs if the numbers aren't finite. It also assumes\n * that products like `fa * fb` are also finite.\n *\n * \\throws `convergence_error` if, for any index, the requested tolerance is not\n * met after `max_iterations` iterations.\n */\ntemplate <bool UseSimd = true, bool AssumeFinite = false, typename Function>\nDataVector toms748(const Function& f, const DataVector& lower_bound,\n                   const DataVector& upper_bound,\n                   const double absolute_tolerance,\n                   const double relative_tolerance,\n                   const size_t max_iterations = 100) {\n  DataVector result_vector{lower_bound.size()};\n  if constexpr (UseSimd) {\n    constexpr size_t simd_width{simd::size<\n        std::decay_t<decltype(simd::load_unaligned(lower_bound.data()))>>()};\n    const size_t vectorized_size =\n        lower_bound.size() - lower_bound.size() % simd_width;\n    for (size_t i = 0; i < vectorized_size; i += simd_width) {\n      simd::store_unaligned(\n          &result_vector[i],\n          toms748<AssumeFinite>([&f, i](const auto x) { return f(x, i); },\n                                simd::load_unaligned(&lower_bound[i]),\n                                simd::load_unaligned(&upper_bound[i]),\n                                absolute_tolerance, relative_tolerance,\n                                max_iterations));\n    }\n    for (size_t i = vectorized_size; i < lower_bound.size(); ++i) {\n      result_vector[i] = toms748<AssumeFinite>(\n          [&f, i](const auto x) { return f(x, i); }, lower_bound[i],\n          upper_bound[i], absolute_tolerance, relative_tolerance,\n          max_iterations);\n    }\n  } else {\n    for (size_t i = 0; i < result_vector.size(); ++i) {\n      result_vector[i] = toms748<AssumeFinite>(\n          [&f, i](double x) { return f(x, i); }, lower_bound[i], upper_bound[i],\n          absolute_tolerance, relative_tolerance, max_iterations);\n    }\n  }\n  return result_vector;\n}\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Finds the root of the function `f` with the TOMS_748 method on each\n * element in a `DataVector`, where function values are supplied at the lower\n * and upper bounds.\n *\n * Supplying function values is an optimization that saves two\n * function calls per point.  The function values are often available\n * because one often checks if the root is bracketed before calling `toms748`.\n *\n * \\note if `AssumeFinite` is true than the code assumes all numbers are\n * finite and that `a > 0` is equivalent to `sign(a) > 0`. This reduces\n * runtime but will cause bugs if the numbers aren't finite. It also assumes\n * that products like `fa * fb` are also finite.\n */\ntemplate <bool UseSimd = true, bool AssumeFinite = false, typename Function>\nDataVector toms748(const Function& f, const DataVector& lower_bound,\n                   const DataVector& upper_bound,\n                   const DataVector& f_at_lower_bound,\n                   const DataVector& f_at_upper_bound,\n                   const double absolute_tolerance,\n                   const double relative_tolerance,\n                   const size_t max_iterations = 100) {\n  DataVector result_vector{lower_bound.size()};\n  if constexpr (UseSimd) {\n    constexpr size_t simd_width{simd::size<\n        std::decay_t<decltype(simd::load_unaligned(lower_bound.data()))>>()};\n    const size_t vectorized_size =\n        lower_bound.size() - lower_bound.size() % simd_width;\n    for (size_t i = 0; i < vectorized_size; i += simd_width) {\n      simd::store_unaligned(\n          &result_vector[i],\n          toms748<AssumeFinite>([&f, i](const auto x) { return f(x, i); },\n                                simd::load_unaligned(&lower_bound[i]),\n                                simd::load_unaligned(&upper_bound[i]),\n                                simd::load_unaligned(&f_at_lower_bound[i]),\n                                simd::load_unaligned(&f_at_upper_bound[i]),\n                                absolute_tolerance, relative_tolerance,\n                                max_iterations));\n    }\n    for (size_t i = vectorized_size; i < lower_bound.size(); ++i) {\n      result_vector[i] = toms748<AssumeFinite>(\n          [&f, i](double x) { return f(x, i); }, lower_bound[i], upper_bound[i],\n          f_at_lower_bound[i], f_at_upper_bound[i], absolute_tolerance,\n          relative_tolerance, max_iterations);\n    }\n  } else {\n    for (size_t i = 0; i < lower_bound.size(); ++i) {\n      result_vector[i] = toms748<AssumeFinite>(\n          [&f, i](double x) { return f(x, i); }, lower_bound[i], upper_bound[i],\n          f_at_lower_bound[i], f_at_upper_bound[i], absolute_tolerance,\n          relative_tolerance, max_iterations);\n    }\n  }\n  return result_vector;\n}\n}  // namespace RootFinder\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpatialDiscretization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SpatialDiscretization)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  OptionTags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Options\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpatialDiscretization/OptionTags.hpp",
    "content": "// // Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"Options/String.hpp\"\n\nnamespace SpatialDiscretization::OptionTags {\n/*!\n * \\brief Group holding all the options for the spatial discretization of the\n * PDE system.\n *\n * For example, when using a discontinuous Galerkin scheme, options like the\n * basis function choice are specified within the SpatialDiscretizationGroup.\n * The boundary correction/numerical flux to use is another example of what\n * would be specified in the SpatialDiscretizationGroup. In the future,\n * specifying the domain decomposition should also be done in the spatial\n * discretization group.\n */\nstruct SpatialDiscretizationGroup {\n  static std::string name() { return \"SpatialDiscretization\"; }\n  static constexpr Options::String help{\n      \"Options controlling the spatial discretization of the PDE system.\\n\\n\"\n      \"In a DG-subcell hybrid scheme subgroups would hold options for the DG \"\n      \"and FD/FV method used. For example, the choice of basis functions for \"\n      \"the DG scheme would be specified.\"};\n};\n}  // namespace SpatialDiscretization::OptionTags\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BarycentricWeights.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/BarycentricWeights.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct BarycentricWeightsGenerator {\n  DataVector operator()(const size_t num_points) const {\n    // Algorithm 30 in Kopriva, p. 75\n    // This is valid for any collocation points.\n    const DataVector& x =\n        collocation_points<BasisType, QuadratureType>(num_points);\n    DataVector bary_weights(num_points, 1.);\n    for (size_t j = 1; j < num_points; j++) {\n      for (size_t k = 0; k < j; k++) {\n        bary_weights[k] *= x[k] - x[j];\n        bary_weights[j] *= x[j] - x[k];\n      }\n    }\n    for (size_t j = 0; j < num_points; j++) {\n      bary_weights[j] = 1. / bary_weights[j];\n    }\n    return bary_weights;\n  }\n};\n}  // namespace\n\nnamespace detail {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst DataVector& barycentric_weights(const size_t num_points) {\n  return precomputed_spectral_quantity<\n      BasisType, QuadratureType,\n      BarycentricWeightsGenerator<BasisType, QuadratureType>>(num_points);\n}\n\ntemplate const DataVector&\n    barycentric_weights<Basis::Cartoon, Quadrature::AxialSymmetry>(size_t);\ntemplate const DataVector&\n    barycentric_weights<Basis::Cartoon, Quadrature::SphericalSymmetry>(size_t);\ntemplate const DataVector&\n    barycentric_weights<Basis::Chebyshev, Quadrature::Gauss>(size_t);\ntemplate const DataVector&\n    barycentric_weights<Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const DataVector&\n    barycentric_weights<Basis::Legendre, Quadrature::Gauss>(size_t);\ntemplate const DataVector&\n    barycentric_weights<Basis::Legendre, Quadrature::GaussLobatto>(size_t);\ntemplate const DataVector&\n    barycentric_weights<Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t);\ntemplate const DataVector&\n    barycentric_weights<Basis::ZernikeB2, Quadrature::GaussRadauUpper>(size_t);\ntemplate const DataVector&\n    barycentric_weights<Basis::ZernikeB3, Quadrature::GaussRadauUpper>(size_t);\n}  // namespace detail\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BarycentricWeights.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral::detail {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst DataVector& barycentric_weights(const size_t num_points);\n}  // namespace Spectral::detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Basis.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n\n#include <array>\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace Spectral {\n\nstd::array<Basis, 10> all_bases() {\n  return std::array{Basis::Uninitialized,\n                    Basis::Chebyshev,\n                    Basis::Legendre,\n                    Basis::FiniteDifference,\n                    Basis::SphericalHarmonic,\n                    Basis::Fourier,\n                    Basis::ZernikeB1,\n                    Basis::ZernikeB2,\n                    Basis::ZernikeB3,\n                    Basis::Cartoon};\n}\n\nBasis to_basis(const std::string& basis) {\n  for (const auto the_basis : all_bases()) {\n    if (basis == get_output(the_basis)) {\n      return the_basis;\n    }\n  }\n  using ::operator<<;\n  ERROR(\"Failed to convert \\\"\"\n        << basis << \"\\\" to Spectral::Basis.\\nMust be one of \" << all_bases()\n        << \".\");\n}\n\nstd::ostream& operator<<(std::ostream& os, const Basis& basis) {\n  switch (basis) {\n    case Basis::Uninitialized:\n      return os << \"Uninitialized\";\n    case Basis::Legendre:\n      return os << \"Legendre\";\n    case Basis::Chebyshev:\n      return os << \"Chebyshev\";\n    case Basis::FiniteDifference:\n      return os << \"FiniteDifference\";\n    case Basis::SphericalHarmonic:\n      return os << \"SphericalHarmonic\";\n    case Basis::Fourier:\n      return os << \"Fourier\";\n    case Basis::ZernikeB1:\n      return os << \"ZernikeB1\";\n    case Basis::ZernikeB2:\n      return os << \"ZernikeB2\";\n    case Basis::ZernikeB3:\n      return os << \"ZernikeB3\";\n    case Basis::Cartoon:\n      return os << \"Cartoon\";\n    default:\n      ERROR(\"Invalid basis\");\n  }\n}\n}  // namespace Spectral\n\ntemplate <>\nSpectral::Basis Options::create_from_yaml<Spectral::Basis>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  for (const auto basis : Spectral::all_bases()) {\n    if (type_read == get_output(basis)) {\n      return basis;\n    }\n  }\n  using ::operator<<;\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read << \"\\\" to Spectral::Basis.\\nMust be one of \"\n                  << Spectral::all_bases() << \".\");\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Basis.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <iosfwd>\n#include <string>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace Spectral {\n/// \\brief The amount we left-shift the basis integers by.\n///\n/// We do this to be able to represent the combined Basis and Quadrature as one\n/// 8-bit integer.\nconstexpr uint8_t basis_shift = 4;\n/*!\n * \\brief Either the basis functions used by a spectral or discontinuous\n * Galerkin (DG) method, or the value `FiniteDifference` when a finite\n * difference method is used\n *\n * \\details The particular choices of Basis and Quadrature determine where the\n * collocation points of a Mesh are located in an Element. For a spectral or DG\n * method, the Basis also represents the choice of basis functions used to\n * represent a function on an Element, which then provides a convenient choice\n * for the operators used for differentiation, interpolation, etc.  For a finite\n * difference method, one needs to choose the order of the scheme (and hence the\n * weights, differentiation matrix, integration weights, and interpolant)\n * locally in space and time to handle discontinuous solutions.\n *\n * \\note Choose `Legendre` for a general-purpose spectral or DG mesh, unless\n * you have a particular reason for choosing `Chebyshev`.\n *\n * \\note Choose `Fourier` for a dimension that is spatially periodic and in\n * which the expected solution is smooth.  A Mesh with this Basis cannot be\n * split by h-refinement in this dimension.\n *\n * \\note Choose two consecutive dimensions to have `SphericalHarmonic` to choose\n * a spherical harmonic basis.  By convention, the first dimension represents\n * the polar/zenith angle (or colatitude), while the second dimension represents\n * the azimuthal angle (or longitude).  A Mesh with this Basis cannot be split\n * by h-refinement in these dimensions.\n *\n * \\note Choose two consecutive dimensions to have `ZernikeB2` to choose\n * a basis used on a disk or cross-section of a cylinder.  By convention, the\n * first dimension represents the radial direction, while the second dimension\n * represents the azimuthal angle. A Mesh with this Basis cannot be split by\n * h-refinement in these dimensions.\n *\n * \\note Choose three consecutive dimensions to have `ZernikeB3` to choose\n * a basis used on a sphere.  By convention, the first dimension represents the\n * radial direction, the second dimension represents the polar/zenith angle (or\n * colatitude), while the third dimension represents the azimuthal angle (or\n * longitude). A Mesh with this Basis cannot be split by h-refinement in these\n * dimensions.\n *\n * \\note Choose `Cartoon` for a dimension (or consecutive dimensions) that\n * represent axial (or spherical) symmetry.\n *\n * \\remark We store these effectively as a 4-bit integer using the highest 4\n * bits of a uint8_t, which is why we do the left shift.  We cannot have more\n * than 16 bases to fit into the 4 bits, including the `Uninitialized` value.\n * The number of bits to shift is encoded in the variable\n * `Spectral::detail::basis_shift`.\n */\nenum class Basis : uint8_t {\n  Uninitialized = 0 << basis_shift,\n  Chebyshev = 1 << basis_shift,\n  Legendre = 2 << basis_shift,\n  FiniteDifference = 3 << basis_shift,\n  SphericalHarmonic = 4 << basis_shift,\n  Fourier = 5 << basis_shift,\n  ZernikeB1 = 6 << basis_shift,\n  ZernikeB2 = 7 << basis_shift,\n  ZernikeB3 = 8 << basis_shift,\n  Cartoon = 9 << basis_shift\n};\n\n/// All possible values of Basis\nstd::array<Basis, 10> all_bases();\n\n/// Convert a string to a Basis enum.\nBasis to_basis(const std::string& basis);\n\n/// Output operator for a Basis.\nstd::ostream& operator<<(std::ostream& os, const Basis& basis);\n}  // namespace Spectral\n\ntemplate <>\nstruct Options::create_from_yaml<Spectral::Basis> {\n  template <typename Metavariables>\n  static Spectral::Basis create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nSpectral::Basis Options::create_from_yaml<Spectral::Basis>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nnamespace Spectral {\nenum class Basis : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief Compute the normalization square of the basis function \\f$\\Phi_k\\f$\n * (zero-indexed), i.e. the weighted definite integral over its square.\n */\ntemplate <Basis BasisType>\ndouble compute_basis_function_normalization_square(size_t k);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctionValue.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nnamespace Spectral {\nenum class Basis : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief Compute the function values of the basis function \\f$\\Phi_k(x)\\f$\n * (zero-indexed).\n */\ntemplate <Basis BasisType, typename T>\nT compute_basis_function_value(size_t k, const T& x);\n\n/*!\n * \\brief Compute the function values of the basis function \\f$\\Phi^m_k(x)\\f$\n * (zero-indexed).\n */\ntemplate <Basis BasisType, typename T>\nT compute_basis_function_value(size_t k, size_t m, const T& x);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Chebyshev.cpp\n  Fourier.cpp\n  Jacobi.cpp\n  Legendre.cpp\n  Zernike.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Chebyshev.hpp\n  Fourier.hpp\n  Jacobi.hpp\n  Zernike.hpp\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Chebyshev.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Chebyshev.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/InverseWeightFunctionValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Spectral {\n\ntemplate <typename T>\nT Chebyshev::basis_function_value(const size_t k, const T& x) {\n  // Algorithm 21 in Kopriva, p. 60\n  switch (k) {\n    case 0:\n      return make_with_value<T>(x, 1.);\n    case 1:\n      return x;\n    default:\n      // These values can be computed either through recursion\n      // (implemented here), or analytically as `cos(k * acos(x))`.\n      // Since the trigonometric form is expensive to compute it is useful only\n      // for large k. See Kopriva, section 3.1 (p. 59) and Fig. 3.1 (p. 61) for\n      // a discussion.\n      T T_k_minus_2 = make_with_value<T>(x, 1.);\n      T T_k_minus_1 = x;\n      T T_k = make_with_value<T>(x, 0.);\n      for (size_t j = 2; j <= k; j++) {\n        T_k = 2. * x * T_k_minus_1 - T_k_minus_2;\n        T_k_minus_2 = T_k_minus_1;\n        T_k_minus_1 = T_k;\n      }\n      return T_k;\n  }\n}\n\ndouble Chebyshev::basis_function_normalization_square(const size_t k) {\n  if (k == 0) {\n    return M_PI;\n  } else {\n    return M_PI_2;\n  }\n}\n\ntemplate <>\nDataVector Chebyshev::collocation_points<Quadrature::Gauss>(\n    const size_t num_points) {\n  // Algorithm 26 in Kopriva, p. 67\n  ASSERT(num_points >= 1,\n         \"Chebyshev-Gauss quadrature requires at least one collocation point.\");\n  DataVector result(num_points);\n  for (size_t j = 0; j < num_points; j++) {\n    result[j] = -cos(M_PI_2 * (2. * static_cast<double>(j) + 1.) /\n                     static_cast<double>(num_points));\n  }\n  return result;\n}\n\ntemplate <>\nDataVector Chebyshev::collocation_points<Quadrature::GaussLobatto>(\n    const size_t num_points) {\n  // Algorithm 27 in Kopriva, p. 68\n  ASSERT(num_points >= 2,\n         \"Chebyshev-Gauss-Lobatto quadrature requires at least two collocation \"\n         \"points.\");\n  const size_t poly_degree = num_points - 1;\n  DataVector result(num_points);\n  for (size_t j = 0; j < num_points; j++) {\n    result[j] =\n        -cos(M_PI * static_cast<double>(j) / static_cast<double>(poly_degree));\n  }\n  return result;\n}\n\ntemplate <>\nDataVector Chebyshev::integration_weights<Quadrature::Gauss>(\n    const size_t num_points) {\n  return DataVector{num_points, M_PI / static_cast<double>(num_points)};\n}\n\ntemplate <>\nDataVector Chebyshev::integration_weights<Quadrature::GaussLobatto>(\n    const size_t num_points) {\n  // Algorithm 27 in Kopriva, p. 68\n  ASSERT(num_points >= 2,\n         \"Chebyshev-Gauss-Lobatto quadrature requires at least two collocation \"\n         \"points.\");\n  const size_t poly_degree = num_points - 1;\n  DataVector result(num_points, M_PI / static_cast<double>(poly_degree));\n  result[0] *= 0.5;\n  result[poly_degree] *= 0.5;\n  return result;\n}\n\nMatrix Chebyshev::indefinite_integral_matrix(const size_t num_points) {\n  // Tridiagonal matrix that gives the indefinite integral modulo a constant\n  Matrix indef_int(num_points, num_points, 0.0);\n  if (LIKELY(num_points > 1)) {\n    indef_int(1, 0) = 1.0;\n  }\n  if (LIKELY(num_points > 2)) {\n    indef_int(1, 2) = -0.5;\n    indef_int(num_points - 1, num_points - 2) =\n        1.0 / (2.0 * (num_points - 1.0));\n  }\n  for (size_t i = 2; i < num_points - 1; ++i) {\n    indef_int(i, i - 1) = 1.0 / (2.0 * i);\n    indef_int(i, i + 1) = -1.0 / (2.0 * i);\n  }\n\n  // Matrix that ensures that BC at left of interval is 0.0\n  Matrix constant(num_points, num_points, 0.0);\n  double fac = 1.0;\n  for (size_t i = 1; i < num_points; ++i) {\n    constant(i, i) = 1.0;\n    constant(0, i) = fac;\n    fac = -fac;\n  }\n  return constant * indef_int;\n}\n\nMatrix Chebyshev::definite_integral_matrix(const size_t num_points) {\n  Matrix result(1, num_points, 0.0);\n  for (size_t j = 0; j < num_points; j += 2) {\n    result(0, j) = -2.0 / (square(static_cast<double>(j)) - 1.0);\n  }\n  return result;\n}\n\n// Instantiations of function templates defined in the Spectral directory\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::Chebyshev>(const size_t k,\n                                                          const DataVector& x) {\n  return Chebyshev::basis_function_value(k, x);\n}\n\ntemplate <>\ndouble compute_basis_function_value<Basis::Chebyshev>(const size_t k,\n                                                      const double& x) {\n  return Chebyshev::basis_function_value(k, x);\n}\n\ntemplate <>\nDataVector compute_inverse_weight_function_values<Basis::Chebyshev>(\n    const DataVector& x) {\n  return sqrt(1. - square(x));\n}\n\ntemplate <>\ndouble compute_basis_function_normalization_square<Basis::Chebyshev>(\n    const size_t k) {\n  return Chebyshev::basis_function_normalization_square(k);\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector>\ncompute_collocation_points_and_weights<Basis::Chebyshev, Quadrature::Gauss>(\n    const size_t num_points) {\n  return std::make_pair(\n      Chebyshev::collocation_points<Quadrature::Gauss>(num_points),\n      Chebyshev::integration_weights<Quadrature::Gauss>(num_points));\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::Chebyshev, Quadrature::GaussLobatto>(const size_t num_points) {\n  return std::make_pair(\n      Chebyshev::collocation_points<Quadrature::GaussLobatto>(num_points),\n      Chebyshev::integration_weights<Quadrature::GaussLobatto>(num_points));\n}\n\ntemplate <Basis BasisType>\nMatrix spectral_indefinite_integral_matrix(size_t num_points);\n\ntemplate <>\nMatrix spectral_indefinite_integral_matrix<Basis::Chebyshev>(\n    const size_t num_points) {\n  return Chebyshev::indefinite_integral_matrix(num_points);\n}\n\ntemplate <Basis BasisType>\nMatrix spectral_definite_integral_matrix(size_t num_points);\n\ntemplate <>\nMatrix spectral_definite_integral_matrix<Basis::Chebyshev>(\n    const size_t num_points) {\n  return Chebyshev::definite_integral_matrix(num_points);\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Chebyshev.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\nclass Matrix;\nnamespace Spectral {\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n\n/*!\n * \\ingroup SpectralGroup\n *\n * \\brief A collection of helper functions for Chebyshev polynomials\n *\n * \\details The Chebyshev polynomials are given by:\n * \\f[\n * T_n (x) = \\cos(n \\theta), \\text{      } \\theta = \\arccos(x)\n * \\f]\n *\n * The Chebyshev expansion of a function \\f$f \\in [-1,1] \\f$ is given by:\n * \\f[\n * f(x) = \\sum_{n=0}^{\\infty} f_n T_n(x)\n * \\f]\n * where\n * \\f[\n * f_n = \\frac{1}{c_n} \\int_{-1}^1 f(x) T_n(x) w(x) dx\n * \\f]\n * where the weight function is \\f$w(x) = (1-x^2)^{-1/2}\\f$ and the basis\n * function normalization value is given by:\n * \\f{align*}{\n *   c_n\n *   &=\\begin{cases}\n *     \\hfil \\pi \\hfil & \\text{if } n = 0 \\\\\n *     \\hfil \\frac{\\pi}{2} \\hfil & \\text{otherwise}\n *   \\end{cases}\n * \\f}\n *\n * If a function is discretized at \\f$N+1\\f$ collocation points, the modal\n * representation will have \\f$N+1\\f$ spectral coefficients consisting of\n * \\f[\n * f_n \\qquad \\text{for } n = 0, \\ldots, N\n * \\f]\n *\n * For more details about using Chebyshev polynomials see e.g. \\cite Boyd2001\n * and \\cite Fornberg1996.\n */\nclass Chebyshev {\n public:\n  /*!\n   * \\brief Value of the basis function \\f$\\Phi_k(x) = T_k(x)\\f$\n   */\n  template <typename T>\n  static T basis_function_value(size_t k, const T& x);\n\n  /*!\n   * \\brief The normalization square \\f$c_k\\f$ of the basis function\n   * \\f$\\Phi_k(x)\\f$, i.e. the definite integral of its square.\n   */\n  static double basis_function_normalization_square(size_t k);\n\n  /*!\n   * \\brief Collocation points \\f$\\{x_i\\}\\f$\n   *\n   * \\details The collocation points on the interval \\f$[-1, 1]\\f$ are given by\n   * \\f{align*}{\n   *   x_i\n   *   &=\\begin{cases}\n   *     \\hfil - \\cos \\frac{(2i+1)\\pi}{2N+2} \\hfil &\n   *       \\text{for Quadrature::Gauss} \\\\\n   *     \\hfil - \\cos \\frac{i \\pi}{N} \\hfil &\n   *       \\text{for Quadrature::GaussLobatto}\n   *   \\end{cases}\n   * \\f}\n   */\n  template <Quadrature quadrature>\n  static DataVector collocation_points(size_t num_points);\n\n  /*!\n   * \\brief Integration weights \\f$\\{w_i\\}\\f$\n   *\n   * \\details The integration weights are used to approximate the weighted\n   * integral \\f$Q[f]=\\int f(x)w(x)\\mathrm{d}x\\approx \\sum_k f_k w_k\\f$.\n   * For Quadrature::Gauss, the weights are given by:\n   * \\f[\n   * w_i = \\frac{\\pi}{N+1}\n   * \\f]\n   *\n   * For Quadrature::GaussLobatto, the weights are given by:\n   * \\f{align*}{\n   *   w_i\n   *   &=\\begin{cases}\n   *     \\hfil \\frac{\\pi}{2N}  \\hfil & \\text{for } j = 0, N\\\\\n   *     \\hfil \\frac{\\pi}{N}   \\hfil & \\text{for } j = 1, \\ldots, N-1\n   *   \\end{cases}\n   * \\f}\n   *\n   * \\note These weights are used to compute the modes from the nodal\n   * values.  They are not used to evaluate definite or indefinite\n   * integrals directly from the nodal values.\n   */\n  template <Quadrature quadrature>\n  static DataVector integration_weights(size_t num_points);\n\n  /*!\n   * \\brief Matrix used to compute the modes of the indefinite\n   * integral from the modes of the integrand such that the constant\n   * of integration is determined by requiring the integral to be zero\n   * at \\f$x=-1\\f$.\n   *\n   * \\details Chebyshev polynomials satisfy the identity:\n   * \\f[\n   * \\int^x dy T_n (y) = \\left\\{ \\frac{T_{n+1}(x)}{2(n+1)} +\n   *   \\frac{T_{n-1}(x)}{2(n-1)} \\right\\}, \\qquad n \\geq 2\n   * \\f]\n   *\n   * Thus the modes \\f$\\tilde{f}_j\\f$ of the integral are given as:\n   * \\f{align*}{\n   *   \\tilde{f}_i\n   *   &=\\begin{cases}\n   *     \\hfil \\frac{f_{i-1}-f_{i+1}}{2i}, \\hfil & \\text{for } i > 1\\\\\n   *     \\hfil f_0 - \\frac{f_2}{2} \\hfil & \\text{for } i = 1\n   *   \\end{cases}\n   * \\f}\n   * where \\f$f_j\\f$ are the modes of the integrand.\n   *\\f$\\tilde{f}_0\\f$ is a constant of integration, which we choose such that\n   * the integral is 0 at the left boundary of the domain (\\f$x=-1\\f$). The\n   * condition for this is:\n   *\n   * \\f[\n   *   \\tilde{f}_0=\\sum_{i=1}^{N}(-1)^{i+1}\\tilde{f}_i\n   * \\f]\n   */\n  static Matrix indefinite_integral_matrix(size_t num_points);\n\n  /*!\n   * \\brief Row-vector used to compute the definite integral from the modes of\n   * the integrand\n   *\n   * \\details Given the modes \\f$\\tilde{f}_j\\f$ of the integral (see\n   * indefinite_integral_matrix), the definite integral is given by evaluating\n   * the series at \\f$x=1\\f$ and \\f$x=-1\\f$ and taking the difference.  Given\n   * that \\f$T_n(1) = 1\\f$ and \\f$T_n(-1) = (-1)^n\\f$, this means multiplying\n   * the indefinite_integral_matrix by the row-vector \\f$\\{0, 2, 0, 2, \\ldots\n   * \\}\\f$ which yields:\n   * \\f{align*}{\n   *   \\tilde{q}_i\n   *   &=\\begin{cases}\n   *     \\hfil -\\frac{2}{n^2-1}, \\hfil & \\text{for } i \\text{ even}\\\\\n   *     \\hfil 0 \\hfil & \\text{for } i \\text{ odd}\n   *   \\end{cases}\n   * \\f}\n   */\n  static Matrix definite_integral_matrix(size_t num_points);\n};\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Fourier.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Fourier.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Spectral {\ntemplate <typename T>\nT Fourier::basis_function_value(const int k, const T& x) {\n  if (UNLIKELY(k == 0)) {\n    return make_with_value<T>(x, 1.);\n  }\n  if (k > 0) {\n    return cos(k * x);\n  }\n  return sin(-k * x);\n}\n\ndouble Fourier::basis_function_normalization_square(const int k) {\n  if (UNLIKELY(k == 0)) {\n    return 2.0 * M_PI;\n  }\n  return M_PI;\n}\n\nDataVector Fourier::collocation_points(const size_t num_points) {\n  DataVector x{num_points, 2.0 * M_PI / static_cast<double>(num_points)};\n  for (size_t i = 0; i < num_points; ++i) {\n    x[i] *= static_cast<double>(i);\n  }\n  return x;\n}\n\nDataVector Fourier::quadrature_weights(const size_t num_points) {\n  return DataVector{num_points, 2.0 * M_PI / static_cast<double>(num_points)};\n}\n\nsize_t Fourier::modal_storage_index(const int k) {\n  if (UNLIKELY(k == 0)) {\n    return size_t{0};\n  }\n  if (k > 0) {\n    return static_cast<size_t>(2 * k - 1);\n  }\n  return 2 * static_cast<size_t>(-k);\n}\n\nint Fourier::mode_at_storage_index(const size_t storage_index) {\n  if (UNLIKELY(storage_index == 0)) {\n    return 0;\n  }\n  if (storage_index % 2 == 1) {\n    return 1 + static_cast<int>(storage_index) / 2;\n  }\n  return -static_cast<int>(storage_index) / 2;\n}\n\n// As the collocation points are evenly spaced and this is a periodic\n// domain, each row is identical to the previous row right-shifted by\n// one element.  Furthermore the matrix is anti-symmetric.  Thus there\n// are only N/2 unique elements that need to be computed; the rest can\n// be determined from them\nMatrix Fourier::differentiation_matrix(const size_t num_points) {\n  const bool n_is_even = num_points % 2 == 0;\n  Matrix result{num_points, num_points, 0.0};\n  const double half_dx = M_PI / static_cast<double>(num_points);\n  double coef = -0.5;\n  for (size_t j = 1; j < (num_points + 1) / 2; ++j) {\n    coef = -coef;\n    const double y = static_cast<double>(j) * half_dx;\n    result(0, j) = n_is_even ? coef * cos(y) / sin(y) : coef / sin(y);\n    result(0, num_points - j) = -result(0, j);\n  }\n  for (size_t i = 1; i < num_points; ++i) {\n    result(i, 0) = result(i - 1, num_points - 1);\n    for (size_t j = 1; j < num_points; ++j) {\n      result(i, j) = result(i - 1, j - 1);\n    }\n  }\n  return result;\n}\n\ntemplate <typename T>\nMatrix Fourier::interpolation_matrix(const size_t num_points,\n                                     const T& target_points) {\n  const size_t num_target_points = get_size(target_points);\n  Matrix result(num_target_points, num_points,\n                1.0 / static_cast<double>(num_points));\n  const DataVector x_source = collocation_points(num_points);\n  const bool n_is_even = num_points % 2 == 0;\n  for (size_t i = 0; i < num_target_points; ++i) {\n    const double x_target = get_element(target_points, i);\n    // Check where no interpolation is necessary since a target point\n    // matches the original collocation points\n    bool row_has_match = false;\n    for (size_t j = 0; j < num_points; j++) {\n      if (equal_within_roundoff(x_target, x_source[j])) {\n        result(i, j) = 1.0;\n        for (size_t m = 0; m < j; ++m) {\n          result(i, m) = 0.0;\n        }\n        for (size_t m = j + 1; m < num_points; ++m) {\n          result(i, m) = 0.0;\n        }\n        row_has_match = true;\n        break;\n      }\n    }\n    // Perform interpolation for non-matching points\n    if (not row_has_match) {\n      for (size_t j = 0; j < num_points; ++j) {\n        const double half_dx = 0.5 * (x_target - x_source[j]);\n        if (n_is_even) {\n          result(i, j) *= sin(static_cast<double>(num_points) * half_dx) *\n                          cos(half_dx) / sin(half_dx);\n        } else {\n          result(i, j) *=\n              sin(static_cast<double>(num_points) * half_dx) / sin(half_dx);\n        }\n      }\n    }\n  }\n  return result;\n}\n\ntemplate Matrix Fourier::interpolation_matrix(size_t num_points,\n                                              const double& target_points);\ntemplate Matrix Fourier::interpolation_matrix(size_t num_points,\n                                              const DataVector& target_points);\ntemplate Matrix Fourier::interpolation_matrix(\n    size_t num_points, const std::vector<double>& target_points);\n\n// Specializations of function templates defined in the Spectral directory\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::Fourier>(const size_t k,\n                                                        const DataVector& x) {\n  return Fourier::basis_function_value(Fourier::mode_at_storage_index(k), x);\n}\n\ntemplate <>\ndouble compute_basis_function_value<Basis::Fourier>(const size_t k,\n                                                    const double& x) {\n  return Fourier::basis_function_value(Fourier::mode_at_storage_index(k), x);\n}\n\ntemplate <>\ndouble compute_basis_function_normalization_square<Basis::Fourier>(\n    const size_t k) {\n  return Fourier::basis_function_normalization_square(\n      Fourier::mode_at_storage_index(k));\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector>\ncompute_collocation_points_and_weights<Basis::Fourier, Quadrature::Equiangular>(\n    const size_t num_points) {\n  return std::make_pair(Fourier::collocation_points(num_points),\n                        Fourier::quadrature_weights(num_points));\n}\n\ntemplate <Basis BasisType>\nMatrix spectral_indefinite_integral_matrix(size_t num_points);\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif\n\ntemplate <>\nMatrix spectral_indefinite_integral_matrix<Basis::Fourier>(\n    size_t /*num_points*/) {\n  ERROR(\"Indefinite integral matrix is not implemented for Fourier basis\");\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Fourier.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\nclass Matrix;\n/// \\endcond\n\nnamespace Spectral {\n\n/*!\n * \\ingroup SpectralGroup\n *\n * \\brief A collection of helper functions for using a Fourier series\n *\n * \\details The general real-valued Fourier series is given by:\n * \\f[\n * f(x) = a_0 + \\sum_{n=1}^{\\infty} a_n \\cos(nx) + b_n \\sin(nx)\n * \\f]\n * where\n * \\f{align*}\n * a_0 &= \\frac{1}{2\\pi} \\int_0^{2\\pi} f(x) dx \\\\\n * a_n &= \\frac{1}{\\pi} \\int_0^{2\\pi} f(x) \\cos(nx) dx \\\\\n * b_n &= \\frac{1}{\\pi} \\int_0^{2\\pi} f(x) \\sin(nx) dx\n * \\f}\n *\n * If a function is discretized at \\f$N\\f$ collocation points, the modal\n * representation will have \\f$N\\f$ spectral coefficients consisting of\n * \\f{align*}\n * a_n & \\qquad \\text{for } n = 0, \\ldots, \\frac{N}{2} \\\\\n * b_n & \\qquad \\text{for } n = 1, \\ldots, \\frac{N-1}{2}\n * \\f}\n * Thus to represent all terms through the harmonic \\f$M\\f$ requires \\f$N = 2M\n * +1\\f$ collocation points.\n *\n * For more details about using Fourier series see e.g. \\cite Boyd2001 and\n * \\cite Fornberg1996.\n */\nclass Fourier {\n public:\n  /*!\n   * \\brief Value of the basis function \\f$\\Phi_k(x)\\f$\n   *\n   * \\details We define the basis functions as\n   * \\f{align*}{\n   *   \\Phi_k(x)\n   *   &=\\begin{cases}\n   *     \\hfil \\cos(kx) \\hfil & \\text{if } k \\geq 0 \\\\\n   *     \\hfil \\sin(-kx) \\hfil & \\text{if } k < 0\n   *   \\end{cases}\n   * \\f}\n   */\n  template <typename T>\n  static T basis_function_value(int k, const T& x);\n\n  /*!\n   * \\brief The normalization square \\f$c_k\\f$ of the basis function\n   * \\f$\\Phi_k(x)\\f$, i.e. the definite integral of its square.\n   *\n   * \\details\n   * In particular,\n   * \\f{align*}{\n   *   c_k\n   *   &=\\begin{cases}\n   *     \\hfil 2 \\pi \\hfil & \\text{if } k = 0 \\\\\n   *     \\hfil \\pi \\hfil & \\text{otherwise}\n   *   \\end{cases}\n   * \\f}\n   */\n  static double basis_function_normalization_square(int k);\n\n  /*!\n   * \\brief Collocation points \\f$\\{x_i\\}\\f$\n   *\n   * \\details The collocation points on the interval \\f$0 \\leq x < 2 \\pi\\f$ are\n   * given by\n   * \\f[\n   * x_i = \\frac{2 \\pi i}{N}\n   * \\f]\n   */\n  static DataVector collocation_points(size_t num_points);\n\n  /*!\n   * \\brief Quadrature weights \\f$\\{w_i\\}\\f$\n   *\n   * \\details The quadrature weights are given by\n   * \\f[\n   * w_i = \\frac{2 \\pi}{N}\n   * \\f]\n   */\n  static DataVector quadrature_weights(size_t num_points);\n\n  /*!\n   * \\brief Storage index (offset) \\f$i\\f$ into a ModalVector (representing the\n   * coefficients) of the given mode \\f$k\\f$\n   *\n   * \\details The modal coefficients are stored in a ModalVector as\n   * \\f$\\{u_0, u_1, u_{-1}, u_2, u_{-2}, \\ldots, u_M, u_{-M}\\}\\f$,\n   * where \\f$u_{-M}\\f$ is omitted if the number of coefficients is even.\n   * Therefore the storage index \\f$i\\f$ for the mode \\f$u_k\\f$ is:\n   * \\f{align*}{\n   *   i\n   *   &=\\begin{cases}\n   *     \\hfil 0 \\hfil & \\text{if } k = 0 \\\\\n   *     \\hfil 2k-1 \\hfil & \\text{if } k > 0 \\\\\n   *     \\hfil -2k \\hfil & \\text{if } k < 0\n   *   \\end{cases}\n   * \\f}\n   */\n  static size_t modal_storage_index(int k);\n\n  /*!\n   * \\brief Mode \\f$k\\f$ corresponding to given storage index (offset) \\f$i\\f$\n   * into a ModalVector (representing the coefficients)\n   *\n   * \\details The modal coefficients are stored in a ModalVector as\n   * \\f$\\{u_0, u_1, u_{-1}, u_2, u_{-2}, \\ldots, u_M, u_{-M}\\}\\f$,\n   * where \\f$u_{-M}\\f$ is omitted if the number of coefficients is even.\n   * Therefore the storage index \\f$i\\f$ for the mode \\f$u_k\\f$ is:\n   * \\f{align*}{\n   *   k\n   *   &=\\begin{cases}\n   *     \\hfil 0 \\hfil & \\text{if } i = 0 \\\\\n   *     \\hfil 1 + \\frac{i}{2} \\hfil & \\text{if } i \\text{ is odd} \\\\\n   *     \\hfil -\\frac{i}{2} \\hfil & \\text{if } i \\text{ is even}\n   *   \\end{cases}\n   * \\f}\n   */\n  static int mode_at_storage_index(size_t storage_index);\n\n  /*!\n   * \\brief Matrix \\f$D_{i,j}\\f$ used to obtain the first derivative\n   *\n   * \\details The differentiation matrix is given by:\n   * \\f{align*}{\n   *   D_{i,j}\n   *   &=\\begin{cases}\n   *     \\hfil 0 \\hfil & \\text{if } i = j \\\\\n   *     \\hfil \\frac{1}{2} (-1)^{i-j} \\csc \\left[0.5 (x_i - x_j)\\right] \\hfil &\n   *     \\text{if } i \\neq j,\\; N \\text{ is odd} \\\\\n   *     \\hfil \\frac{1}{2} (-1)^{i-j} \\cot \\left[0.5 (x_i - x_j)\\right] \\hfil &\n   *     \\text{if } i \\neq j,\\; N \\text{ is even}\n   *   \\end{cases}\n   * \\f}\n   */\n  static Matrix differentiation_matrix(size_t num_points);\n\n  /*!\n   * \\brief %Matrix used to interpolate to the \\p target_points.\n   *\n   * \\details Each row of the matrix is given by the interpolation weights for\n   * interpolating to a particular target point \\f$x\\f$.  At each target point,\n   * \\f$x\\f$, the interpolation weights are given by:\n   * \\f{align*}{\n   *   C_j(x)\n   *   &=\\begin{cases}\n   *     \\hfil \\frac{1}{N} \\sin \\left[0.5 N(x - x_j)\\right]\n   *     \\csc \\left[0.5 (x - x_j)\\right] \\hfil & \\text{if } N \\text{ is odd} \\\\\n   *     \\hfil \\frac{1}{N} \\sin \\left[0.5 N (x - x_j)\\right]\n   *     \\cot \\left[0.5 (x - x_j)\\right] \\hfil & \\text{if } N \\text{ is even}\n   *   \\end{cases}\n   * \\f}\n   * where \\f$x_j\\f$ are the collocation_points.\n   */\n  template <typename T>\n  static Matrix interpolation_matrix(size_t num_points, const T& target_points);\n};\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Jacobi.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Jacobi.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Spectral {\ntemplate <typename T>\nT Jacobi::basis_function_value(const double alpha, const double beta,\n                               const size_t k, const T& x) {\n  ASSERT(alpha > -1.0, \"alpha must be > -1, got \" << alpha);\n  ASSERT(beta > -1.0, \"beta must be > -1, got \" << beta);\n  // P_0 = 1\n  T P_k_minus_2 = make_with_value<T>(x, 1.);\n  if (k == 0) {\n    return P_k_minus_2;\n  }\n  // P_1 = (2+a+b)/2*x + (a-b)/2\n  T P_k_minus_1 = 0.5 * (2.0 + alpha + beta) * x + 0.5 * (alpha - beta);\n  if (k == 1) {\n    return P_k_minus_1;\n  }\n  T P_k = make_with_value<T>(x, 0.);\n  // Recurrence from Apendix A of Fornberg1996, taking n+1 -> n\n  // 2n(n+a+b)(2n+a+b-2)P_{n} - [(2n+a+b-1)(a-b)(a+b) +\n  //  (2n+a+b-2)(2n+a+b-1)(2n+a+b)x]P_{n-1} + 2(n+a-1)(n+b-1)(2n+a+b)P_{n-2} = 0\n  // We solve the recurrence relation by defining the coefficients as\n  // c_0 P_n - (c_1 + c_2 x) P_{n-1} - c_3 P_{n-2}\n  for (size_t j = 2; j <= k; ++j) {\n    const auto n = static_cast<double>(j);\n    const double c0 =\n        2.0 * n * (n + alpha + beta) * (2.0 * n + alpha + beta - 2.0);\n    const double c1 =\n        (2.0 * n + alpha + beta - 1.0) * (alpha - beta) * (alpha + beta);\n    const double c2 = (2.0 * n + alpha + beta - 2.0) *\n                      (2.0 * n + alpha + beta - 1.0) * (2.0 * n + alpha + beta);\n    const double c3 =\n        2.0 * (n + alpha - 1.0) * (n + beta - 1.0) * (2.0 * n + alpha + beta);\n    P_k = ((c1 + c2 * x) * P_k_minus_1 - c3 * P_k_minus_2) / c0;\n    P_k_minus_2 = P_k_minus_1;\n    P_k_minus_1 = P_k;\n  }\n  return P_k;\n}\n\ntemplate double Jacobi::basis_function_value<double>(const double alpha,\n                                                     const double beta,\n                                                     const size_t k,\n                                                     const double& x);\ntemplate DataVector Jacobi::basis_function_value<DataVector>(\n    const double alpha, const double beta, const size_t k, const DataVector& x);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Jacobi.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\nclass Matrix;\nnamespace Spectral {\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n\n/*!\n * \\ingroup SpectralGroup\n *\n * \\brief A collection of helper functions for Jacobi polyomials\n *\n */\nclass Jacobi {\n public:\n  /*!\n   * \\brief Value of the basis function \\f$\\Phi^k(x) = P^(\\alpha,\\beta)_k(x)\\f$,\n   * implemented from \\cite Fornberg1996\n   */\n  template <typename T>\n  static T basis_function_value(double alpha, double beta, size_t k,\n                                const T& x);\n};\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Legendre.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/InverseWeightFunctionValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Legendre.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Spectral {\n\n// Algorithms to compute Legendre basis functions\n// These functions specialize the templates declared in `Spectral.hpp`.\n\nnamespace {\ntemplate <typename T>\nT compute_basis_function_value_impl(const size_t k, const T& x) {\n  // Algorithm 20 in Kopriva, p. 60\n  switch (k) {\n    case 0:\n      return make_with_value<T>(x, 1.);\n    case 1:\n      return x;\n    default:\n      T L_k_minus_2 = make_with_value<T>(x, 1.);\n      T L_k_minus_1 = x;\n      T L_k = make_with_value<T>(x, 0.);\n      for (size_t j = 2; j <= k; j++) {\n        L_k = ((2. * j - 1.) * x * L_k_minus_1 - (j - 1.) * L_k_minus_2) / j;\n        L_k_minus_2 = L_k_minus_1;\n        L_k_minus_1 = L_k;\n      }\n      return L_k;\n  }\n}\n}  // namespace\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::Legendre>(const size_t k,\n                                                         const DataVector& x) {\n  return compute_basis_function_value_impl(k, x);\n}\n\ntemplate <>\ndouble compute_basis_function_value<Basis::Legendre>(const size_t k,\n                                                     const double& x) {\n  return compute_basis_function_value_impl(k, x);\n}\n\ntemplate <>\nDataVector compute_inverse_weight_function_values<Basis::Legendre>(\n    const DataVector& x) {\n  return DataVector(x.size(), 1.);\n}\n\ntemplate <>\ndouble compute_basis_function_normalization_square<Basis::Legendre>(\n    const size_t k) {\n  return 2. / (2. * k + 1.);\n}\n\n// Algorithms to compute Legendre-Gauss quadrature\n\nnamespace {\nstruct LegendrePolynomialAndDerivative {\n  LegendrePolynomialAndDerivative(size_t poly_degree, double x);\n  double L;\n  double dL;\n};\n\nLegendrePolynomialAndDerivative::LegendrePolynomialAndDerivative(\n    const size_t poly_degree, const double x) {\n  // Algorithm 22 in Kopriva, p. 63\n  // The cases where `poly_degree` is `0` or `1` are omitted because they are\n  // never used.\n  double L_N_minus_2 = 1.;\n  double L_N_minus_1 = x;\n  double dL_N_minus_2 = 0.;\n  double dL_N_minus_1 = 1.;\n  double L_N = std::numeric_limits<double>::signaling_NaN();\n  double dL_N = std::numeric_limits<double>::signaling_NaN();\n  for (size_t k = 2; k <= poly_degree; k++) {\n    L_N = ((2. * k - 1.) * x * L_N_minus_1 - (k - 1.) * L_N_minus_2) / k;\n    dL_N = dL_N_minus_2 + (2. * k - 1.) * L_N_minus_1;\n    L_N_minus_2 = L_N_minus_1;\n    L_N_minus_1 = L_N;\n    dL_N_minus_2 = dL_N_minus_1;\n    dL_N_minus_1 = dL_N;\n  }\n  L = L_N;\n  dL = dL_N;\n}\n\n}  // namespace\n\ntemplate <>\nstd::pair<DataVector, DataVector>\ncompute_collocation_points_and_weights<Basis::Legendre, Quadrature::Gauss>(\n    const size_t num_points) {\n  // Algorithm 23 in Kopriva, p.64\n  ASSERT(num_points >= 1,\n         \"Legendre-Gauss quadrature requires at least one collocation point.\");\n  const size_t poly_degree = num_points - 1;\n  DataVector x(num_points);\n  DataVector w(num_points);\n  switch (poly_degree) {\n    case 0:\n      x[0] = 0.;\n      w[0] = 2.;\n      break;\n    case 1:\n      x[0] = -sqrt(1. / 3.);\n      x[1] = -x[0];\n      w[0] = w[1] = 1.;\n      break;\n    default:\n      for (size_t j = 0; j <= (poly_degree + 1) / 2 - 1; j++) {\n        double logical_coord = RootFinder::toms748(\n            [poly_degree](const double coord) {\n              return LegendrePolynomialAndDerivative(poly_degree + 1, coord).L;\n            },\n            -cos((2. * j) * M_PI / (2. * poly_degree + 2.)),\n            -cos((2. * j + 2.) * M_PI / (2. * poly_degree + 2.)), 0.0, 6.0e-16);\n        const LegendrePolynomialAndDerivative L_and_dL(poly_degree + 1,\n                                                       logical_coord);\n        x[j] = logical_coord;\n        x[poly_degree - j] = -logical_coord;\n        w[j] = w[poly_degree - j] =\n            2. / (1. - square(logical_coord)) / square(L_and_dL.dL);\n      }\n      if (poly_degree % 2 == 0) {\n        const LegendrePolynomialAndDerivative L_and_dL(poly_degree + 1, 0.);\n        x[poly_degree / 2] = 0.;\n        w[poly_degree / 2] = 2. / square(L_and_dL.dL);\n      }\n      break;\n  }\n  return std::make_pair(std::move(x), std::move(w));\n}\n\n// Algorithms to compute Legendre-Gauss-Lobatto quadrature\n\nnamespace {\nstruct EvaluateQandL {\n  EvaluateQandL(size_t poly_degree, double x);\n  double q;\n  double L;\n};\n\nEvaluateQandL::EvaluateQandL(const size_t poly_degree, const double x) {\n  // Algorithm 24 in Kopriva, p. 65\n  // Note: Book has errors in last 4 lines, corrected in errata on website\n  // https://www.math.fsu.edu/~kopriva/publications/errata.pdf\n  ASSERT(poly_degree >= 2, \"Polynomial degree must be at least two.\");\n  double L_n_minus_2 = 1.;\n  double L_n_minus_1 = x;\n  double L_prime_n_minus_2 = 0.;\n  double L_prime_n_minus_1 = 1.;\n  double L_n = std::numeric_limits<double>::signaling_NaN();\n  for (size_t k = 2; k <= poly_degree; k++) {\n    L_n = ((2. * k - 1.) * x * L_n_minus_1 - (k - 1.) * L_n_minus_2) / k;\n    const double L_prime_n = L_prime_n_minus_2 + (2. * k - 1.) * L_n_minus_1;\n    L_n_minus_2 = L_n_minus_1;\n    L_n_minus_1 = L_n;\n    L_prime_n_minus_2 = L_prime_n_minus_1;\n    L_prime_n_minus_1 = L_prime_n;\n  }\n  const size_t k = poly_degree + 1;\n  const double L_n_plus_1 =\n      ((2. * k - 1.) * x * L_n - (k - 1.) * L_n_minus_2) / k;\n  q = L_n_plus_1 - L_n_minus_2;\n  L = L_n;\n}\n\n}  // namespace\n\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::Legendre, Quadrature::GaussLobatto>(const size_t num_points) {\n  // Algorithm 25 in Kopriva, p. 66\n  ASSERT(num_points >= 2,\n         \"Legendre-Gauss-Lobatto quadrature requires at least two collocation \"\n         \"points.\");\n  const size_t poly_degree = num_points - 1;\n  DataVector x(num_points);\n  DataVector w(num_points);\n  switch (poly_degree) {\n    case 1:\n      x[0] = -1.;\n      x[1] = 1.;\n      w[0] = w[1] = 1.;\n      break;\n    default:\n      x[0] = -1.;\n      x[poly_degree] = 1.;\n      w[0] = w[poly_degree] = 2. / (poly_degree * (poly_degree + 1.));\n      for (size_t j = 1; j < (poly_degree + 1) / 2; j++) {\n        double logical_coord = RootFinder::toms748(\n            [poly_degree](const double coord) {\n              return EvaluateQandL(poly_degree, coord).q;\n            },\n            -cos((j - 0.25) * M_PI / poly_degree -\n                 0.375 / (poly_degree * M_PI * (j - 0.25))),\n            -cos((j + 0.75) * M_PI / poly_degree -\n                 0.375 / (poly_degree * M_PI * (j + 0.75))),\n            0.0, 6.0e-16);\n        const EvaluateQandL q_and_L(poly_degree, logical_coord);\n        x[j] = logical_coord;\n        x[poly_degree - j] = -logical_coord;\n        w[j] = w[poly_degree - j] =\n            2. / (poly_degree * (poly_degree + 1.) * square(q_and_L.L));\n      }\n      if (poly_degree % 2 == 0) {\n        const EvaluateQandL q_and_L(poly_degree, 0.);\n        x[poly_degree / 2] = 0.;\n        w[poly_degree / 2] =\n            2. / (poly_degree * (poly_degree + 1.) * square(q_and_L.L));\n      }\n      break;\n  }\n  return std::make_pair(std::move(x), std::move(w));\n}\n\ntemplate <Basis BasisType>\nMatrix spectral_indefinite_integral_matrix(size_t num_points);\n\ntemplate <>\nMatrix spectral_indefinite_integral_matrix<Basis::Legendre>(\n    const size_t num_points) {\n  // Tridiagonal matrix that gives the indefinite integral modulo a constant\n  Matrix indef_int(num_points, num_points, 0.0);\n  for (size_t i = 1; i < num_points - 1; ++i) {\n    indef_int(i, i - 1) = 1.0 / (2.0 * i - 1.0);\n    indef_int(i, i + 1) = -1.0 / (2.0 * i + 3.0);\n  }\n  if (LIKELY(num_points > 1)) {\n    indef_int(num_points - 1, num_points - 2) =\n        1.0 / (2.0 * (num_points - 1.0) - 1.0);\n  }\n\n  // Matrix that ensures that BC at left of interval is 0.0\n  Matrix constant(num_points, num_points, 0.0);\n  double fac = 1.0;\n  for (size_t i = 1; i < num_points; ++i) {\n    constant(i, i) = 1.0;\n    constant(0, i) = fac;\n    fac = -fac;\n  }\n  return constant * indef_int;\n}\n\nnamespace {\ndouble evaluate_legendre_segment(gsl::span<const double> coefficients,\n                                 const double x) {\n  if (coefficients.empty()) {\n    return 0.0;\n  }\n  if (coefficients.size() == 1) {\n    return gsl::at(coefficients, 0);\n  }\n  const size_t n = coefficients.size() - 1;\n  double b_k_plus_two = 0.0;\n  double b_k_plus_one = 0.0;\n  for (int k = static_cast<int>(n); k >= 0; --k) {\n    const auto k_double = static_cast<double>(k);\n    const double a_k = (2.0 * k_double + 1.0) / (k_double + 1.0) * x;\n    const double b_kp1 = -(k_double + 1.0) / (k_double + 2.0);\n    const double b_k_current =\n        gsl::at(coefficients, k) + a_k * b_k_plus_one + b_kp1 * b_k_plus_two;\n    b_k_plus_two = b_k_plus_one;\n    b_k_plus_one = b_k_current;\n  }\n  return b_k_plus_one;\n}\n}  // namespace\n\ntemplate <size_t Dim>\ndouble evaluate_legendre_series(\n    const ModalVector& coefficients, const Mesh<Dim>& mesh,\n    const tnsr::I<double, Dim, Frame::ElementLogical>& logical_coords) {\n  ASSERT(mesh.number_of_grid_points() == coefficients.size(),\n         \"Mesh and coefficient sizes do not match: mesh has \"\n             << mesh.number_of_grid_points() << \" points but coefficients hold \"\n             << coefficients.size() << \" entries.\");\n  for (size_t d = 0; d < Dim; ++d) {\n    ASSERT(mesh.basis(d) == Basis::Legendre,\n           \"evaluate_legendre_series only supports Legendre bases. Found \"\n               << mesh.basis(d) << \" in dimension \" << d << \".\");\n  }\n  std::vector<double> working(coefficients.begin(), coefficients.end());\n  size_t current_size = working.size();\n  for (size_t dim = 0; dim < Dim; ++dim) {\n    const size_t extent = mesh.extents(dim);\n    ASSERT(extent > 0, \"Mesh extent must be non-zero.\");\n    const size_t num_slices = current_size / extent;\n    const double x_dim = logical_coords.get(dim);\n    const gsl::span<const double> working_view(working);\n    for (size_t slice = 0; slice < num_slices; ++slice) {\n      const auto slice_view = working_view.subspan(slice * extent, extent);\n      working[slice] = evaluate_legendre_segment(slice_view, x_dim);\n    }\n    current_size = num_slices;\n  }\n  ASSERT(current_size == 1,\n         \"After evaluating all dimensions there should be a single value.\");\n  return working.front();\n}\n\n}  // namespace Spectral\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                        \\\n  template double Spectral::evaluate_legendre_series<DIM(data)>(    \\\n      const ModalVector& coefficients, const Mesh<DIM(data)>& mesh, \\\n      const tnsr::I<double, DIM(data), Frame::ElementLogical>&      \\\n          logical_coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Zernike.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Zernike.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Blaze/IntegerPow.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Jacobi.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/InverseWeightFunctionValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <size_t Dim, typename T>\nT Q(const size_t n, const size_t m, const T& r) {\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3);\n  ASSERT(n >= m, \"m, \" << m << \", must be at most n, \" << n);\n  ASSERT((n + m) % 2 == 0,\n         \"n, \" << n << \", plus m, \" << m << \", must be even.\");\n  const auto mm = static_cast<double>(m);\n\n  // Implemented from Matsushima1995\n  // Matsushima and Marcus paper notation varries from usual standards\n  // Note: their \\alpha and \\beta are NOT Jacobi polynomial \\alpha and \\beta\n  // For 1D, their \\alpha = 1, \\beta = 0\n  // For 2D, their \\alpha = 1, \\beta = 1\n  // For 3D, their \\alpha = 1, \\beta = 2\n  // This means their \\gamma := 2\\alpha + \\beta is either 3 for polar or\n  // 4 for spherical, which in SpEC's mBeta = \\gamma - 2\n  constexpr double betaM = Dim == 1 ? 0.0 : (Dim == 2 ? 1.0 : 2.0);\n  // Qnm is Q_n^m, p2/m2 being plus2 / minus2\n  // using betaM to indicate Marcus Beta\n  T Qmm = integer_pow(r, m);\n  if (n == m) {\n    return Qmm;\n  }\n  T Qmp2m =\n      0.5 * Qmm * ((2. * mm + betaM + 3.) * square(r) - (2. * mm + betaM + 1.));\n  if (n == (m + 2)) {\n    return Qmp2m;\n  }\n  T Qnm = Qmp2m;\n  T Qnm2m = Qmm;\n  for (size_t n_loop = m + 2; n_loop < n; n_loop += 2) {\n    const auto nn = static_cast<double>(n_loop);\n    const T Qnp2m =\n        ((2. * nn + betaM + 1.) *\n             ((2. * nn + betaM - 1.) * (2. * nn + betaM + 3.) * square(r) -\n              2. * nn * (nn + betaM + 1.) - 2. * mm * (mm + betaM - 1.) -\n              (betaM - 1.) * (betaM + 1.)) *\n             Qnm -\n         (nn - mm) * (nn + mm + betaM - 1.) * (2. * nn + betaM + 3.) * Qnm2m) /\n        ((nn - mm + 2.) * (nn + mm + betaM + 1.) * (2. * nn + betaM - 1.));\n    Qnm2m = Qnm;\n    Qnm = Qnp2m;\n  }\n  return Qnm;\n}\n\n// Pi(r) := Q^0_m(r)\ntemplate <size_t Dim, typename T>\nT Pi(const size_t m, const T& r) {\n  ASSERT(m > 1, \"Pi: m must be at least two, got m = \" << m);\n  return Q<Dim>(m, 0, r) - Q<Dim>(m - 2, 0, r);\n}\n\ntemplate <size_t Dim>\ndouble I(const size_t n, const size_t m) {\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3);\n  ASSERT(n >= m, \"n = \" << n << \"; m = \" << m);\n  ASSERT((n + m) % 2 == 0,\n         \"n and m must have same parity, got n = \" << n << \" m = \" << m);\n  constexpr double betaM = Dim == 1 ? 0.0 : (Dim == 2 ? 1.0 : 2.0);\n  double result = 0.5 / (static_cast<double>(m) + 0.5 + 0.5 * betaM);\n  for (size_t i = m + 2; i <= n; i += 2) {\n    const auto ii = static_cast<double>(i);\n    result *= (2. * ii + betaM - 3.) / (2. * ii + betaM + 1.);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::pair<DataVector, DataVector>\ncompute_collocation_points_and_weights_gauss_radau_impl(\n    const size_t num_points) {\n  // Implementing for true logical coordinates: [0,1]\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3);\n  ASSERT(\n      num_points >= 1,\n      \"Zernike-Gauss-Radau quadrature requires at least one collocation point\");\n  const size_t poly_degree = num_points - 1;\n  DataVector x(num_points);\n  DataVector w(num_points);\n  switch (poly_degree) {\n    case 0:\n      x[0] = 1.0;\n      w[0] = I<Dim>(0, 0);\n      break;\n    default:\n      size_t root_index = 0;\n      // Find the collocation points by finding the roots of pi(m, r)\n      // We know there is a root at r = 1 (and don't want one at 0)\n      const size_t M = 2 * num_points;\n      const size_t num_intervals = M * M;\n      const double delta = 0.1 / static_cast<double>(num_intervals);\n      const double dr = (1. - 2. * delta) / static_cast<double>(num_intervals);\n\n      double r_left = delta;\n      double f_left = Pi<Dim>(M, r_left);\n      for (size_t i = 0; i < num_intervals; ++i) {\n        const double r_right = r_left + dr;\n        const double f_right = Pi<Dim>(M, r_right);\n\n        // Checking if root has been bracketed\n        if (f_left * f_right <= 0) {\n          const double root =\n              RootFinder::toms748([M](const double r) { return Pi<Dim>(M, r); },\n                                  r_left, r_right, f_left, f_right, 0.0,\n                                  4.0 * std::numeric_limits<double>::epsilon());\n          x[root_index] = root;\n          ++root_index;\n        }\n        r_left = r_right;\n        f_left = f_right;\n      }\n      ASSERT(num_points - 1 == root_index,\n             \"Did not find all the roots of Pi, expect \"\n                 << num_points - 1 << \", but got \" << root_index);\n      x[num_points - 1] = 1.0;\n      // Computing quadrature weights\n      const auto mm = static_cast<double>(M);\n      constexpr double betaM = Dim == 1 ? 0.0 : (Dim == 2 ? 1.0 : 2.0);\n      for (size_t i = 0; i < num_points - 1; ++i) {\n        w[i] = 2. * (2. * mm + betaM - 3.) * square(x[i]) * I<Dim>(M - 2, 0) /\n               (mm * (mm + betaM - 1.) * square(Q<Dim>(M - 2, 0, x[i])));\n      }\n      w[num_points - 1] =\n          (2. * (2. * mm + betaM - 3.) * I<Dim>(M - 2, 0) /\n           (mm * (mm + betaM - 1.) * square(Q<Dim>(M - 2, 0, 1.0))));\n      break;\n  }\n  return std::make_pair(std::move(x), std::move(w));\n}\n}  // namespace\n\ntemplate <size_t Dim>\ntemplate <typename T>\nT Zernike<Dim>::basis_function_value(const size_t n, const size_t m,\n                                     const T& xi) {\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3);\n  // the sqrt(I) normalization is for normalizing \"with respect to the weight\n  // w(r),\" which is the integrating weight in the orthogonality condition\n  // Note that xi is logical coordinate, so in [-1, 1]. Need to shift back to\n  // true [0, 1] range\n  return Q<Dim>(n, m, static_cast<T>(0.5 * (xi + 1.0))) / sqrt(I<Dim>(n, m));\n}\n\ntemplate <size_t Dim>\nstd::pair<DataVector, DataVector>\nZernike<Dim>::compute_collocation_points_and_weights(const size_t num_points) {\n  // Manually adding an affine coordinate map to shift logical coordinates\n  // from [0,1] -> [-1,1] (simpler to keep track of this than generalize the\n  // [-1,1] assumption scattered through the code)\n  auto [points, weights] =\n      compute_collocation_points_and_weights_gauss_radau_impl<Dim>(num_points);\n  points = 2.0 * points - 1.0;\n  // Don't need to modify weights as the affine shift is only external, you\n  // will only ever use weights on \"real\" [0,1] coordinates\n  return std::make_pair(points, weights);\n}\n\ntemplate <size_t Dim>\nMatrix Zernike<Dim>::differentiation_matrix(const size_t num_points,\n                                            const Parity parity) {\n  ASSERT(parity != Parity::Uninitialized, \"Passed parity must set\");\n  // \"missing\" factor of 1/2 because logical coordinates have a Jacobian\n  // factor to go from [-1,1] -> [0,1]\n  const DataVector collocation_pts =\n      Zernike<Dim>::compute_collocation_points_and_weights(num_points).first +\n      1.0;\n  const size_t max_deriv = 1;\n  std::array<DataVector, max_deriv + 1> fornberg_weights{};\n  DataVector extended_collocation_pts(2 * num_points, 0.0);\n  Matrix extended_diff_matrix(num_points, 2 * num_points);\n  Matrix projector(2 * num_points, num_points, 0.0);\n  for (size_t i = 0; i < num_points; ++i) {\n    extended_collocation_pts[i] = collocation_pts[i];\n    extended_collocation_pts[i + num_points] = -collocation_pts[i];\n    projector(i, i) = 1.0;\n    projector(i + num_points, i) = parity == Parity::Odd ? -1.0 : 1.0;\n  }\n  for (size_t i = 0; i < num_points; ++i) {\n    Spectral::fornberg_derivative_interpolation_weights<max_deriv>(\n        make_not_null(&fornberg_weights), collocation_pts[i],\n        extended_collocation_pts);\n    for (size_t j = 0; j < 2 * num_points; ++j) {\n      extended_diff_matrix(i, j) = fornberg_weights[1][j];\n    }\n  }\n  return extended_diff_matrix * projector;\n}\n\n// Specializations of function templates defined in the Spectral directory\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::ZernikeB1>(\n    const size_t k, const size_t m, const DataVector& xi) {\n  return Zernike<1>::basis_function_value(k, m, xi);\n}\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::ZernikeB2>(\n    const size_t k, const size_t m, const DataVector& xi) {\n  return Zernike<2>::basis_function_value(k, m, xi);\n}\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::ZernikeB3>(\n    const size_t k, const size_t m, const DataVector& xi) {\n  return Zernike<3>::basis_function_value(k, m, xi);\n}\n\n// The bases orthonormal wrt n\ntemplate <>\ndouble compute_basis_function_normalization_square<Basis::ZernikeB1>(\n    const size_t /*k*/) {\n  return 1.;\n}\n\ntemplate <>\ndouble compute_basis_function_normalization_square<Basis::ZernikeB2>(\n    const size_t /*k*/) {\n  return 1.;\n}\n\ntemplate <>\ndouble compute_basis_function_normalization_square<Basis::ZernikeB3>(\n    const size_t /*k*/) {\n  return 1.;\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::ZernikeB1, Quadrature::GaussRadauUpper>(const size_t num_points) {\n  return Zernike<1>::compute_collocation_points_and_weights(num_points);\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::ZernikeB2, Quadrature::GaussRadauUpper>(const size_t num_points) {\n  return Zernike<2>::compute_collocation_points_and_weights(num_points);\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::ZernikeB3, Quadrature::GaussRadauUpper>(const size_t num_points) {\n  return Zernike<3>::compute_collocation_points_and_weights(num_points);\n}\n\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif\n// The following one-index basis function values need to be created so that\n// GetSpectralQuantityForMesh.hpp can correctly instantiate all things called\n// from SPECTRAL_QUANTITY_FOR_MESH that are genuinetly one-indexed\n// (e.g. collocation points and weights)\ntemplate <Basis BasisType>\nMatrix spectral_indefinite_integral_matrix(size_t num_points);\n\ntemplate <>\nMatrix spectral_indefinite_integral_matrix<Basis::ZernikeB1>(\n    const size_t /*num_points*/) {\n  ERROR(\"Indefinite integral matrix is not defined for ZernikeB1 basis\");\n}\ntemplate <>\nMatrix spectral_indefinite_integral_matrix<Basis::ZernikeB2>(\n    const size_t /*num_points*/) {\n  ERROR(\"Indefinite integral matrix is not defined for ZernikeB2 basis\");\n}\ntemplate <>\nMatrix spectral_indefinite_integral_matrix<Basis::ZernikeB3>(\n    const size_t /*num_points*/) {\n  ERROR(\"Indefinite integral matrix is not defined for ZernikeB3 basis\");\n}\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::ZernikeB1>(\n    const size_t /*k*/, const DataVector& /*x*/) {\n  ERROR(\"Calling one-index Zernike basis function\");\n}\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::ZernikeB2>(\n    const size_t /*k*/, const DataVector& /*x*/) {\n  ERROR(\"Calling one-index Zernike basis function\");\n}\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::ZernikeB3>(\n    const size_t /*k*/, const DataVector& /*x*/) {\n  ERROR(\"Calling one-index Zernike basis function\");\n}\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_TYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_BASIS_FUNCTION_VALUE(r, data)                       \\\n  template GET_TYPE(data) Zernike<GET_DIM(data)>::basis_function_value( \\\n      const size_t n, const size_t m, const GET_TYPE(data) & xi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_BASIS_FUNCTION_VALUE, (1, 2, 3),\n                        (double, DataVector))\n\n#undef INSTANTIATE_BASIS_FUNCTION_VALUE\n#undef GET_TYPE\n\n#define INSTANTIATE_DIFF_MATRICES(r, data)                        \\\n  template Matrix Zernike<GET_DIM(data)>::differentiation_matrix( \\\n      const size_t num_points, const Parity parity);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_DIFF_MATRICES, (1, 2, 3))\n\n#undef INSTANTIATE_DIFF_MATRICES\n#undef GET_DIM\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BasisFunctions/Zernike.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n\n/// \\cond\nclass DataVector;\nclass Matrix;\nnamespace Spectral {\nenum class Quadrature : uint8_t;\nenum class Parity : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n\n/*!\n * \\ingroup SpectralGroup\n *\n * \\brief A collection of helper functions for the radial functions used in\n * Zernike polyomials\n *\n */\ntemplate <size_t Dim>\nclass Zernike {\n public:\n  /*!\n   * \\brief Value of the basis function \\f$\\Phi^m_n(\\xi) = R^m_n(r)\\f$,\n   * where \\f$r \\equiv \\frac{1}{2} (\\xi + 1)\\f$, implemented from\n   * \\cite Matsushima1995\n   */\n  template <typename T>\n  static T basis_function_value(size_t n, size_t m, const T& xi);\n\n  /*!\n   * \\brief Collocation points \\f${x_i}\\f$ and quadrature weights \\f${w_i}\\f$\n   */\n  static std::pair<DataVector, DataVector>\n  compute_collocation_points_and_weights(size_t num_points);\n\n  /*!\n   * \\brief Matrix \\f$D_{i,j}\\f$ used to obtain the first derivative for a\n   * given parity.\n   *\n   * Due to the clustering of Zernike collocation toward the upper side, the\n   * generic implementation of derivatives with barycentric weights yields\n   * large errors. By utilizing the fact that the Zernike bases' \\f$m\\f$\n   * corresponds to parity of representable functions, we can extend the\n   * function to negative \\f$r\\f$ before forming the matrix, greatly improving\n   * accuracy.\n   */\n  static Matrix differentiation_matrix(size_t num_points, Parity parity);\n};\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Spectral {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst std::pair<Matrix, Matrix>& boundary_interpolation_matrices(\n    const size_t num_points) {\n  static_assert(BasisType == Spectral::Basis::Legendre);\n  static_assert(\n      QuadratureType == Spectral::Quadrature::Gauss,\n      \"We only compute the boundary interpolation for Gauss quadrature \"\n      \"since for Gauss-Lobatto you can just copy values off the volume.\");\n  static const auto cache = make_static_cache<\n      CacheRange<Spectral::minimum_number_of_points<BasisType, QuadratureType>,\n                 Spectral::maximum_number_of_points<BasisType> + 1>>(\n      [](const size_t local_num_points) {\n        return std::pair<Matrix, Matrix>{\n            interpolation_matrix<BasisType, QuadratureType>(local_num_points,\n                                                            -1.0),\n            interpolation_matrix<BasisType, QuadratureType>(local_num_points,\n                                                            1.0)};\n      });\n  return cache(num_points);\n}\n\nconst std::pair<Matrix, Matrix>& boundary_interpolation_matrices(\n    const Mesh<1>& mesh) {\n  ASSERT(mesh.basis(0) == Spectral::Basis::Legendre,\n         \"We only support DG with a Legendre basis.\");\n  ASSERT(mesh.quadrature(0) == Spectral::Quadrature::Gauss,\n         \"We only compute the boundary interpolation for Gauss quadrature \"\n         \"since for Gauss-Lobatto you can just copy values off the volume.\");\n  return boundary_interpolation_matrices<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::Gauss>(\n      mesh.extents(0));\n}\n\ntemplate const std::pair<Matrix, Matrix>& boundary_interpolation_matrices<\n    Basis::Legendre, Quadrature::Gauss>(const size_t num_points);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n/// \\cond\nclass Matrix;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/// @{\n/*!\n * \\brief Matrices that interpolate to the lower and upper boundaries of the\n * element.\n *\n * Assumes that the logical coordinates are \\f$[-1, 1]\\f$. The first element of\n * the pair interpolates to \\f$\\xi=-1\\f$ and the second to \\f$\\xi=1\\f$. These\n * are just the Lagrange interpolating polynomials evaluated at \\f$\\xi=\\pm1\\f$.\n * For Gauss-Lobatto points the only non-zero element is at the boundaries\n * and is one and so is not implemented.\n *\n * \\warning This can only be called with Gauss points.\n */\nconst std::pair<Matrix, Matrix>& boundary_interpolation_matrices(\n    const Mesh<1>& mesh);\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst std::pair<Matrix, Matrix>& boundary_interpolation_matrices(\n    size_t num_points);\n/// @}\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BoundaryInterpolationTerm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationTerm.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Spectral {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst std::pair<DataVector, DataVector>& boundary_interpolation_term(\n    const size_t num_points) {\n  static_assert(BasisType == Spectral::Basis::Legendre);\n  static_assert(\n      QuadratureType == Spectral::Quadrature::Gauss,\n      \"We only compute the time derivative correction interpolation for Gauss \"\n      \"quadrature since for Gauss-Lobatto you can just copy values off the \"\n      \"volume.\");\n  static const auto cache = make_static_cache<\n      CacheRange<Spectral::minimum_number_of_points<BasisType, QuadratureType>,\n                 Spectral::maximum_number_of_points<BasisType> + 1>>(\n      [](const size_t local_num_points) {\n        const Matrix interp_matrix =\n            interpolation_matrix<BasisType, Quadrature::GaussLobatto>(\n                local_num_points == 1 ? 2 : local_num_points,\n                collocation_points<BasisType, Quadrature::Gauss>(\n                    local_num_points));\n\n        std::pair<DataVector, DataVector> result{DataVector{local_num_points},\n                                                 DataVector{local_num_points}};\n        for (size_t i = 0; i < local_num_points; ++i) {\n          result.first[i] = interp_matrix(i, 0);\n          result.second[i] = interp_matrix(i, interp_matrix.columns() - 1);\n        }\n        return result;\n      });\n  return cache(num_points);\n}\n\nconst std::pair<DataVector, DataVector>& boundary_interpolation_term(\n    const Mesh<1>& mesh) {\n  ASSERT(mesh.basis(0) == Spectral::Basis::Legendre,\n         \"We only support DG with a Legendre basis.\");\n  ASSERT(\n      mesh.quadrature(0) == Spectral::Quadrature::Gauss,\n      \"We only compute the time derivative correction interpolation for Gauss \"\n      \"quadrature since for Gauss-Lobatto you can just copy values off the \"\n      \"volume.\");\n  return boundary_interpolation_term<Spectral::Basis::Legendre,\n                                     Spectral::Quadrature::Gauss>(\n      mesh.extents(0));\n}\n\ntemplate const std::pair<DataVector, DataVector>& boundary_interpolation_term<\n    Basis::Legendre, Quadrature::Gauss>(const size_t num_points);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BoundaryInterpolationTerm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/// @{\n/*!\n * \\brief Interpolates values from the boundary into the volume, which is needed\n * when applying time derivative or Bjorhus-type boundary conditions in a\n * discontinuous Galerkin scheme using Gauss points.\n *\n * Assumes that the logical coordinates are \\f$[-1, 1]\\f$.\n * The interpolation is done by assuming the time derivative correction is zero\n * on interior nodes. With a nodal Lagrange polynomial basis this means that\n * only the \\f$\\ell^{\\mathrm{Gauss-Lobatto}}_{0}\\f$ and\n * \\f$\\ell^{\\mathrm{Gauss-Lobatto}}_{N}\\f$ polynomials/basis functions\n * contribute to the correction. In order to interpolate the correction from the\n * nodes at the boundary, the Gauss-Lobatto Lagrange polynomials  must be\n * evaluated at the Gauss grid points. The returned pair of `DataVector`s stores\n *\n * \\f{align*}{\n *   &\\ell^{\\mathrm{Gauss-Lobatto}}_{0}(\\xi_j^{\\mathrm{Gauss}}), \\\\\n *   &\\ell^{\\mathrm{Gauss-Lobatto}}_{N}(\\xi_j^{\\mathrm{Gauss}}).\n * \\f}\n *\n * This is a different correction from lifting. Lifting is done using the mass\n * matrix, which is an integral over the basis functions, while here we use\n * interpolation.\n *\n * \\warning This can only be called with Gauss points.\n */\nconst std::pair<DataVector, DataVector>& boundary_interpolation_term(\n    const Mesh<1>& mesh);\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst std::pair<DataVector, DataVector>& boundary_interpolation_term(\n    size_t num_points);\n/// @}\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BoundaryLiftingTerm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/BoundaryLiftingTerm.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Spectral {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst std::pair<DataVector, DataVector>& boundary_lifting_term(\n    const size_t num_points) {\n  static_assert(BasisType == Spectral::Basis::Legendre);\n  static_assert(\n      QuadratureType == Spectral::Quadrature::Gauss,\n      \"We only compute the boundary lifting for Gauss quadrature \"\n      \"since for Gauss-Lobatto you can just copy values off the volume.\");\n  static const auto cache = make_static_cache<\n      CacheRange<Spectral::minimum_number_of_points<BasisType, QuadratureType>,\n                 Spectral::maximum_number_of_points<BasisType> + 1>>(\n      [](const size_t local_num_points) {\n        const auto& matrices =\n            boundary_interpolation_matrices<BasisType, QuadratureType>(\n                local_num_points);\n        std::pair<DataVector, DataVector> result{DataVector{local_num_points},\n                                                 DataVector{local_num_points}};\n        for (size_t i = 0; i < local_num_points; ++i) {\n          result.first[i] = matrices.first(0, i);\n          result.second[i] = matrices.second(0, i);\n        }\n        const DataVector& quad_weights =\n            quadrature_weights<BasisType, QuadratureType>(local_num_points);\n        result.first /= quad_weights;\n        result.second /= quad_weights;\n        return result;\n      });\n  return cache(num_points);\n}\n\nconst std::pair<DataVector, DataVector>& boundary_lifting_term(\n    const Mesh<1>& mesh) {\n  ASSERT(mesh.basis(0) == Spectral::Basis::Legendre,\n         \"We only support DG with a Legendre basis.\");\n  ASSERT(mesh.quadrature(0) == Spectral::Quadrature::Gauss,\n         \"We only compute the boundary lifting for Gauss quadrature \"\n         \"since for Gauss-Lobatto you can just copy values off the volume.\");\n  return boundary_lifting_term<Spectral::Basis::Legendre,\n                               Spectral::Quadrature::Gauss>(mesh.extents(0));\n}\n\ntemplate const std::pair<DataVector, DataVector>& boundary_lifting_term<\n    Basis::Legendre, Quadrature::Gauss>(const size_t num_points);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/BoundaryLiftingTerm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/// @{\n/*!\n * \\brief Terms used during the lifting portion of a discontinuous Galerkin\n * scheme when using Gauss points.\n *\n * Assumes that the logical coordinates are \\f$[-1, 1]\\f$. The first element of\n * the pair is the Lagrange polyonmials evaluated at \\f$\\xi=-1\\f$ divided by the\n * weights and the second at \\f$\\xi=1\\f$. Specifically,\n *\n * \\f{align*}{\n * \\frac{\\ell_j(\\xi=\\pm1)}{w_j}\n * \\f}\n *\n * \\warning This can only be called with Gauss points.\n */\nconst std::pair<DataVector, DataVector>& boundary_lifting_term(\n    const Mesh<1>& mesh);\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst std::pair<DataVector, DataVector>& boundary_lifting_term(\n    size_t num_points);\n/// @}\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Spectral)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BarycentricWeights.cpp\n  Basis.cpp\n  BoundaryInterpolationMatrices.cpp\n  BoundaryInterpolationTerm.cpp\n  BoundaryLiftingTerm.cpp\n  Cartoon.cpp\n  CollocationPoints.cpp\n  CollocationPointsAndWeights.cpp\n  DifferentiationMatrix.cpp\n  Filtering.cpp\n  FiniteDifference.cpp\n  IntegrationMatrix.cpp\n  InterpolationWeights.cpp\n  InterpolationMatrix.cpp\n  LinearFilteringMatrix.cpp\n  LogicalCoordinates.cpp\n  Mesh.cpp\n  ModalToNodalMatrix.cpp\n  NodalToModalMatrix.cpp\n  Parity.cpp\n  Projection.cpp\n  Quadrature.cpp\n  QuadratureWeights.cpp\n  SegmentSize.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BarycentricWeights.hpp\n  Basis.hpp\n  BasisFunctionNormalizationSquare.hpp\n  BasisFunctionValue.hpp\n  BoundaryInterpolationMatrices.hpp\n  BoundaryInterpolationTerm.hpp\n  BoundaryLiftingTerm.hpp\n  Clenshaw.hpp\n  CollocationPoints.hpp\n  CollocationPointsAndWeights.hpp\n  DifferentiationMatrix.hpp\n  Filtering.hpp\n  GetSpectralQuantityForMesh.hpp\n  IntegrationMatrix.hpp\n  InterpolationWeights.hpp\n  InterpolationMatrix.hpp\n  InverseWeightFunctionValues.hpp\n  Legendre.hpp\n  LinearFilteringMatrix.hpp\n  LogicalCoordinates.hpp\n  MaximumNumberOfPoints.hpp\n  Mesh.hpp\n  MinimumNumberOfPoints.hpp\n  ModalToNodalMatrix.hpp\n  Namespace.hpp\n  NodalToModalMatrix.hpp\n  Parity.hpp\n  ParityFromSymmetry.hpp\n  PrecomputedSpectralQuantity.hpp\n  Projection.hpp\n  Quadrature.hpp\n  QuadratureWeights.hpp\n  SegmentSize.hpp\n  SpectralQuantityForMesh.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Options\n  Utilities\n  PRIVATE\n  BLAS::BLAS\n  LAPACK::LAPACK\n  RootFinding\n  SPHEREPACK\n  )\n\nadd_subdirectory(BasisFunctions)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Cartoon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/InverseWeightFunctionValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Spectral {\n\n// There should be no calls to Cartoon basis functions, will error\n// These functions specialize the templates declared in `Spectral.hpp`.\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::Cartoon>(\n    const size_t /*k*/, const DataVector& /*x*/) {\n  ERROR(\"Invalid to compute a basis on a Cartoon basis.\");\n}\n\ntemplate <>\ndouble compute_basis_function_value<Basis::Cartoon>(const size_t /*k*/,\n                                                    const double& /*x*/) {\n  ERROR(\"Invalid to compute a basis on a Cartoon basis.\");\n}\n\ntemplate <>\nDataVector compute_inverse_weight_function_values<Basis::Cartoon>(\n    const DataVector& /*x*/) {\n  ERROR(\"Invalid to compute weights on a Cartoon basis.\");\n}\n\ntemplate <>\ndouble compute_basis_function_normalization_square<Basis::Cartoon>(\n    const size_t /*k*/) {\n  ERROR(\"Invalid to compute weights on a Cartoon basis.\");\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::Cartoon, Quadrature::AxialSymmetry>(const size_t /*num_points*/) {\n  ERROR(\n      \"Invalid to compute collocation points and weights for a Cartoon \"\n      \"basis.\");\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::Cartoon, Quadrature::SphericalSymmetry>(\n    const size_t /*num_points*/) {\n  ERROR(\n      \"Invalid to compute collocation points and weights for a Cartoon \"\n      \"basis.\");\n}\n\ntemplate <Basis BasisTYpe>\nMatrix spectral_indefinite_integral_matrix(size_t num_points);\n\ntemplate <>\nMatrix spectral_indefinite_integral_matrix<Basis::Cartoon>(\n    const size_t /*num_points*/) {\n  ERROR(\n      \"Invalid to compute a spectral indefinite integral matrix for  a\"\n      \"Cartoon basis.\");\n}\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Clenshaw.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <numeric>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Spectral {\n/// Clenshaw's algorithm for evaluating linear combinations of\n/// basis functions obeying a given three term recurrence relation.\n/// See numerical recipes 3rd edition 5.4.2 for details.\n/// `alpha` and `beta` define the recursion\n/// \\f$ f_n(x) = \\alpha f_{n-1}(x) + \\beta f_{n-2}(x) \\f$\n/// `f0_of_x` and `f1_of_x` are \\f$ f_0(x)\\f$ and \\f$f_1(x)\\f$ respectively\ntemplate <typename CoefficientDataType, typename DataType>\nDataType evaluate_clenshaw(const std::vector<CoefficientDataType>& coefficients,\n                           const DataType& alpha, const DataType& beta,\n                           const DataType& f0_of_x, const DataType& f1_of_x) {\n  if (coefficients.empty()) {\n    return make_with_value<DataType>(f0_of_x, 0.0);\n  }\n\n  DataType y_upper{make_with_value<DataType>(f0_of_x, 0.0)};  // y_{N+2} = 0\n  DataType new_y_lower;\n  // Do the loop to calculate y_1 and y_2 (the accumulate function leaves\n  // y_lower_final = y_1, and y_upper = y_2)\n  const DataType y_lower_final =\n      std::accumulate(coefficients.rbegin(), coefficients.rend() - 1,\n                      make_with_value<DataType>(f0_of_x, 0.0),\n                      [&y_upper, &alpha, &beta, &new_y_lower](\n                          const auto& y_lower, const auto& c_k) {\n                        new_y_lower = c_k + alpha * y_lower + beta * y_upper;\n                        y_upper = y_lower;\n                        return new_y_lower;\n                      });\n  return f0_of_x * coefficients[0] + f1_of_x * y_lower_final +\n         beta * f0_of_x * y_upper;\n}\n\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/CollocationPoints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SpectralQuantityForMesh.hpp\"\n\nnamespace Spectral {\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst DataVector& collocation_points(const size_t num_points) {\n  return detail::precomputed_spectral_quantity<\n             BasisType, QuadratureType,\n             detail::CollocationPointsAndWeightsGenerator<BasisType,\n                                                          QuadratureType>>(\n             num_points)\n      .first;\n}\n\nSPECTRAL_QUANTITY_FOR_MESH(collocation_points, DataVector)\n\n#undef SPECTRAL_QUANTITY_FOR_MESH\n\ntemplate const DataVector&\n    collocation_points<Basis::Cartoon, Quadrature::AxialSymmetry>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::Cartoon, Quadrature::SphericalSymmetry>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::Chebyshev, Quadrature::Gauss>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::Legendre, Quadrature::Gauss>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::Legendre, Quadrature::GaussLobatto>(size_t);\ntemplate const DataVector& collocation_points<Basis::FiniteDifference,\n                                              Quadrature::CellCentered>(size_t);\ntemplate const DataVector& collocation_points<Basis::FiniteDifference,\n                                              Quadrature::FaceCentered>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::Fourier, Quadrature::Equiangular>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::ZernikeB2, Quadrature::GaussRadauUpper>(size_t);\ntemplate const DataVector&\n    collocation_points<Basis::ZernikeB3, Quadrature::GaussRadauUpper>(size_t);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/CollocationPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief Collocation points\n * \\param num_points The number of collocation points\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst DataVector& collocation_points(size_t num_points);\n\n/*!\n * \\brief Collocation points for a one-dimensional mesh.\n *\n * \\see collocation_points(size_t)\n */\nconst DataVector& collocation_points(const Mesh<1>& mesh);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/CollocationPointsAndWeights.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral::detail {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstd::pair<DataVector, DataVector>\nCollocationPointsAndWeightsGenerator<BasisType, QuadratureType>::operator()(\n    const size_t num_points) const {\n  return compute_collocation_points_and_weights<BasisType, QuadratureType>(\n      num_points);\n}\n\ntemplate struct CollocationPointsAndWeightsGenerator<Basis::Cartoon,\n                                                     Quadrature::AxialSymmetry>;\ntemplate struct CollocationPointsAndWeightsGenerator<\n    Basis::Cartoon, Quadrature::SphericalSymmetry>;\ntemplate struct CollocationPointsAndWeightsGenerator<Basis::Chebyshev,\n                                                     Quadrature::Gauss>;\ntemplate struct CollocationPointsAndWeightsGenerator<Basis::Chebyshev,\n                                                     Quadrature::GaussLobatto>;\ntemplate struct CollocationPointsAndWeightsGenerator<Basis::Legendre,\n                                                     Quadrature::Gauss>;\ntemplate struct CollocationPointsAndWeightsGenerator<Basis::Legendre,\n                                                     Quadrature::GaussLobatto>;\ntemplate struct CollocationPointsAndWeightsGenerator<Basis::FiniteDifference,\n                                                     Quadrature::CellCentered>;\ntemplate struct CollocationPointsAndWeightsGenerator<Basis::FiniteDifference,\n                                                     Quadrature::FaceCentered>;\ntemplate struct CollocationPointsAndWeightsGenerator<Basis::Fourier,\n                                                     Quadrature::Equiangular>;\ntemplate struct CollocationPointsAndWeightsGenerator<\n    Basis::ZernikeB1, Quadrature::GaussRadauUpper>;\ntemplate struct CollocationPointsAndWeightsGenerator<\n    Basis::ZernikeB2, Quadrature::GaussRadauUpper>;\ntemplate struct CollocationPointsAndWeightsGenerator<\n    Basis::ZernikeB3, Quadrature::GaussRadauUpper>;\n}  // namespace Spectral::detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n/// \\cond\nclass DataVector;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\nnamespace detail {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct CollocationPointsAndWeightsGenerator {\n  std::pair<DataVector, DataVector> operator()(size_t num_points) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief Compute the collocation points and weights associated to the\n * basis and quadrature.\n *\n * \\details This function is expected to return the tuple\n * \\f$(\\xi_k,w_k)\\f$ where the \\f$\\xi_k\\f$ are the collocation\n * points and the \\f$w_k\\f$ are defined in the description of\n * `quadrature_weights(size_t)`.\n *\n * \\warning for a `FiniteDifference` basis or `CellCentered` and `FaceCentered`\n * quadratures, the weights are defined to integrate with the midpoint method\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights(\n    size_t num_points);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/DifferentiationMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/BarycentricWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Fourier.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Zernike.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/SpectralQuantityForMesh.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct DifferentiationMatrixGenerator {\n  Matrix operator()(const size_t num_points) const {\n    // Algorithm 37 in Kopriva, p. 82\n    // It is valid for any collocation points and barycentric weights.\n    const DataVector& collocation_pts =\n        collocation_points<BasisType, QuadratureType>(num_points);\n    Matrix diff_matrix(num_points, num_points);\n    if constexpr (BasisType == Spectral::Basis::FiniteDifference) {\n      ASSERT(QuadratureType == Spectral::Quadrature::CellCentered,\n             \"Currently only support cell-centered SBP FD derivatives. Most \"\n             \"likely supporting cell- or vertex-centered just requires \"\n             \"removing this ASSERT and adding tests.\");\n\n      // The summation by parts weights come from:\n      // arXiv:gr-qc/0512001\n      // https://arxiv.org/pdf/gr-qc/0512001.pdf\n      //\n      // New, efficient, and accurate high order derivative and dissipation\n      // operators satisfying summation by parts, and applications in\n      // three-dimensional multi-block evolutions\n      //\n      // by Peter Diener, Ernst Nils Dorband, Erik Schnetter, and Manuel Tiglio\n      if (num_points >= 10) {\n        // D_{6-5}\n        //\n        // Left boundary points\n        diff_matrix(0, 0) = -2.465354921110524023660777656111276003457;\n        diff_matrix(0, 1) = 6.092129526663144141964665936667656020742;\n        diff_matrix(0, 2) = -7.730323816657860354911664841669140051855;\n        diff_matrix(0, 3) = 6.973765088877147139882219788892186735807;\n        diff_matrix(0, 4) = -3.980323816657860354911664841669140051855;\n        diff_matrix(0, 5) = 1.292129526663144141964665936667656020742;\n        diff_matrix(0, 6) = -0.1820215877771906903274443227779426701237;\n        for (size_t i = 7; i < num_points; ++i) {\n          diff_matrix(0, i) = 0.0;\n        }\n\n        diff_matrix(1, 0) = -0.2234725650784319828746535134412736890421;\n        diff_matrix(1, 1) = -0.9329308121107134563129925525068570679651;\n        diff_matrix(1, 2) = 1.586820596545839371759081303802027231274;\n        diff_matrix(1, 3) = -0.3647002340377160216914505558624668821400;\n        diff_matrix(1, 4) = -0.2666957784872806143914117440166232718819;\n        diff_matrix(1, 5) = 0.3112949048634705032101261273629794071371;\n        diff_matrix(1, 6) = -0.1404504214762266650000768489896480092493;\n        diff_matrix(1, 7) = 0.03488568514730479833596013512958238764128;\n        diff_matrix(1, 8) = -0.004964021886392518344179263072091597647654;\n        diff_matrix(1, 9) = 0.0002126465201465853095969115943714918742904;\n\n        diff_matrix(2, 0) = 0.1582216737061633151406179477554921935333;\n        diff_matrix(2, 1) = -1.137049298003377811733609086574457439398;\n        diff_matrix(2, 2) = 1.212364522932578587741649981040340946798;\n        diff_matrix(2, 3) = -0.9562288729513894906148167047868730813830;\n        diff_matrix(2, 4) = 1.066548057336766350478498057851678826640;\n        diff_matrix(2, 5) = -0.3478788551267041838265477441805600110467;\n        diff_matrix(2, 6) = -0.03133923293520187620333693909408071632123;\n        diff_matrix(2, 7) = 0.04098845955755862691072597869183962277781;\n        diff_matrix(2, 8) = -0.005963188634687155197078928402509551508436;\n        diff_matrix(2, 9) = 0.0003367341182936373038974376991292099082999;\n\n        diff_matrix(3, 0) = 0.02915734641890708196910927068736798144670;\n        diff_matrix(3, 1) = -0.1169665089768926152768236581512624861308;\n        diff_matrix(3, 2) = -0.1112219092451476301503253995474190870412;\n        diff_matrix(3, 3) = -0.7924486261248032107393766820001361351677;\n        diff_matrix(3, 4) = 1.266650704820613624987450232358951199911;\n        diff_matrix(3, 5) = -0.2899273290506621673153239836530375587273;\n        diff_matrix(3, 6) = 0.002515684257201926199329020583484434062150;\n        diff_matrix(3, 7) = 0.01329713961871764653006682056620518602804;\n        diff_matrix(3, 8) = -0.001124464399630667352932212208930962568134;\n        diff_matrix(3, 9) = 0.00006796268169601114882659136477742818715059;\n\n        diff_matrix(4, 0) = -0.04582150000326981674750984653096293434777;\n        diff_matrix(4, 1) = 0.2240986548857151482718685516611524323427;\n        diff_matrix(4, 2) = -0.3246718493011818141660859125588209338018;\n        diff_matrix(4, 3) = -0.3929792921782506986152017485694441380503;\n        diff_matrix(4, 4) = 0.1166355818729375628072830916953646214341;\n        diff_matrix(4, 5) = 0.3449626905957060254933930895775644438105;\n        diff_matrix(4, 6) = 0.1430419813354607083034935179267283951745;\n        diff_matrix(4, 7) = -0.07764802499372607792980458731991885121073;\n        diff_matrix(4, 8) = 0.01332439335504217034559288889042994978834;\n        diff_matrix(4, 9) = -0.0009426355684332077630290447720929851395193;\n\n        diff_matrix(5, 0) = 0.003172814452954821196677290327889903944225;\n        diff_matrix(5, 1) = 0.00001061446045061551877105554145609103530766;\n        diff_matrix(5, 2) = -0.08747763580209736614983637747947172321794;\n        diff_matrix(5, 3) = 0.3975827322299876034907453299884380895682;\n        diff_matrix(5, 4) = -1.148835072393422871630425744497391344782;\n        diff_matrix(5, 5) = 0.3583006649535242306065761818925080902380;\n        diff_matrix(5, 6) = 0.5647665154270147564019144982190032455071;\n        diff_matrix(5, 7) = -0.09698196887272109736153117076061707705561;\n        diff_matrix(5, 8) = 0.008843905091972988427261446924164441884143;\n        diff_matrix(5, 9) = 0.0006174304523363194998474898440202828786385;\n\n        diff_matrix(6, 0) = -0.008639107540858839028043929986084287776394;\n        diff_matrix(6, 1) = 0.04722773954485212324714352753530343274219;\n        diff_matrix(6, 2) = -0.1008747537650261142294540111407681552350;\n        diff_matrix(6, 3) = 0.08043834953845218736895768965086958762389;\n        diff_matrix(6, 4) = 0.1295138674713300902982857323205417604553;\n        diff_matrix(6, 5) = -0.7909424166489541737614153656634872155367;\n        diff_matrix(6, 6) = 0.03807866847647628589685997987877954466259;\n        diff_matrix(6, 7) = 0.7367055699548196242687865288427927434250;\n        diff_matrix(6, 8) = -0.1480235854665196220062411065981933720158;\n        diff_matrix(6, 9) = 0.01651566843542843794512095516024596165494;\n        for (size_t i = 1; i < 7; ++i) {\n          for (size_t j = 10; j < num_points; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n        }\n\n        // central points\n        for (size_t i = 7; i < num_points - 7; ++i) {\n          for (size_t j = 0; j < i - 3; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n          diff_matrix(i, i - 3) = -1.0 / 60.0;\n          diff_matrix(i, i - 2) = 0.15;\n          diff_matrix(i, i - 1) = -0.75;\n          diff_matrix(i, i) = 0.0;\n          diff_matrix(i, i + 1) = 0.75;\n          diff_matrix(i, i + 2) = -0.15;\n          diff_matrix(i, i + 3) = 1.0 / 60.0;\n          for (size_t j = i + 4; j < num_points; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n        }\n\n        // Right boundary points. Reverse of left boundary, with opposite sign.\n        for (size_t i = std::max(num_points - 7, 7_st); i < num_points; ++i) {\n          for (size_t j = 0; j < num_points; ++j) {\n            diff_matrix(i, j) =\n                -diff_matrix(num_points - i - 1, num_points - j - 1);\n          }\n        }\n      } else if (num_points >= 7) {\n        for (size_t i = 0; i < num_points; ++i) {\n          for (size_t j = 0; j < num_points; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n        }\n        // D_{4-3}\n        //\n        // Left boundary points\n        diff_matrix(0, 0) = -2.09329763466349871588733;\n        diff_matrix(0, 1) = 4.0398572053206615302160;\n        diff_matrix(0, 2) = -3.0597858079809922953240;\n        diff_matrix(0, 3) = 1.37319053865399486354933;\n        diff_matrix(0, 4) = -0.25996430133016538255400;\n        for (size_t i = 5; i < num_points; ++i) {\n          diff_matrix(0, i) = 0.0;\n        }\n\n        diff_matrix(1, 0) = -0.31641585285940445272297;\n        diff_matrix(1, 1) = -0.53930788973980422327388;\n        diff_matrix(1, 2) = 0.98517732028644343383297;\n        diff_matrix(1, 3) = -0.05264665989297578146709;\n        diff_matrix(1, 4) = -0.113807251750624235013258;\n        diff_matrix(1, 5) = 0.039879767889849911803103;\n        diff_matrix(1, 6) = -0.0028794339334846531588787;\n\n        diff_matrix(2, 0) = 0.13026916185021164524452;\n        diff_matrix(2, 1) = -0.87966858995059249256890;\n        diff_matrix(2, 2) = 0.38609640961100070000134;\n        diff_matrix(2, 3) = 0.31358369072435588745988;\n        diff_matrix(2, 4) = 0.085318941913678384633511;\n        diff_matrix(2, 5) = -0.039046615792734640274641;\n        diff_matrix(2, 6) = 0.0034470016440805155042908;\n\n        diff_matrix(3, 0) = -0.01724512193824647912172;\n        diff_matrix(3, 1) = 0.16272288227127504381134;\n        diff_matrix(3, 2) = -0.81349810248648813029217;\n        diff_matrix(3, 3) = 0.13833269266479833215645;\n        diff_matrix(3, 4) = 0.59743854328548053399616;\n        diff_matrix(3, 5) = -0.066026434346299887619324;\n        diff_matrix(3, 6) = -0.0017244594505194129307249;\n\n        diff_matrix(4, 0) = -0.00883569468552192965061;\n        diff_matrix(4, 1) = 0.03056074759203203857284;\n        diff_matrix(4, 2) = 0.05021168274530854232278;\n        diff_matrix(4, 3) = -0.66307364652444929534068;\n        diff_matrix(4, 4) = 0.014878787464005191116088;\n        diff_matrix(4, 5) = 0.65882706381707471953820;\n        diff_matrix(4, 6) = -0.082568940408449266558615;\n        for (size_t i = 1; i < 5; ++i) {\n          for (size_t j = 7; j < num_points; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n        }\n\n        // central points\n        //\n        // Coded up for completeness and so we can test different (low-order)\n        // operators more easily. This won't always be used.\n        for (size_t i = 5; i < num_points - 5; ++i) {\n          for (size_t j = 0; j < i - 2; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n          diff_matrix(i, i - 2) = 1.0 / 12.0;\n          diff_matrix(i, i - 1) = -2.0 / 3.0;\n          diff_matrix(i, i) = 0.0;\n          diff_matrix(i, i + 1) = 2.0 / 3.0;\n          diff_matrix(i, i + 2) = -1.0 / 12.0;\n          for (size_t j = i + 3; j < num_points; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n        }\n\n        // Right boundary points. Reverse of left boundary, with opposite sign.\n        for (size_t i = std::max(num_points - 5, 5_st); i < num_points; ++i) {\n          for (size_t j = 0; j < num_points; ++j) {\n            diff_matrix(i, j) =\n                -diff_matrix(num_points - i - 1, num_points - j - 1);\n          }\n        }\n      } else if (num_points == 6) {\n        // D_{4-2}\n        //\n        // Left boundary points\n        diff_matrix(0, 0) = -24.0 / 17.0;\n        diff_matrix(0, 1) = 59.0 / 34.0;\n        diff_matrix(0, 2) = -4.0 / 17.0;\n        diff_matrix(0, 3) = -3.0 / 34.0;\n        for (size_t i = 4; i < num_points; ++i) {\n          diff_matrix(0, i) = 0.0;\n        }\n\n        diff_matrix(1, 0) = -1.0 / 2.0;\n        diff_matrix(1, 1) = 0.0;\n        diff_matrix(1, 2) = 1.0 / 2.0;\n        for (size_t i = 3; i < num_points; ++i) {\n          diff_matrix(1, i) = 0.0;\n        }\n\n        diff_matrix(2, 0) = 4.0 / 43.0;\n        diff_matrix(2, 1) = -59.0 / 86.0;\n        diff_matrix(2, 2) = 0.0;\n        diff_matrix(2, 3) = 59.0 / 86.0;\n        diff_matrix(2, 4) = -4.0 / 43.0;\n        for (size_t i = 5; i < num_points; ++i) {\n          diff_matrix(2, i) = 0.0;\n        }\n\n        diff_matrix(3, 0) = 3.0 / 98.0;\n        diff_matrix(3, 1) = 0.0;\n        diff_matrix(3, 2) = -59.0 / 98.0;\n        diff_matrix(3, 3) = 0.0;\n        diff_matrix(3, 4) = 32.0 / 49.0;\n        diff_matrix(3, 5) = -4.0 / 49.0;\n\n        // Coded up for completeness and so we can test different (low-order)\n        // operators more easily. This won't always be used.\n        for (size_t i = 6; i < num_points; ++i) {\n          diff_matrix(3, i) = 0.0;\n        }\n\n        // central points\n        //\n        // Coded up for completeness and so we can test different (low-order)\n        // operators more easily. This won't always be used.\n        for (size_t i = 4; i < num_points - 4; ++i) {\n          for (size_t j = 0; j < i - 2; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n          diff_matrix(i, i - 2) = 1.0 / 12.0;\n          diff_matrix(i, i - 1) = -2.0 / 3.0;\n          diff_matrix(i, i) = 0.0;\n          diff_matrix(i, i + 1) = 2.0 / 3.0;\n          diff_matrix(i, i + 2) = -1.0 / 12.0;\n          for (size_t j = i + 3; j < num_points; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n        }\n\n        // Right boundary points. Reverse of left boundary, with opposite sign.\n        for (size_t i = std::max(num_points - 4, 4_st); i < num_points; ++i) {\n          for (size_t j = 0; j < num_points; ++j) {\n            diff_matrix(i, j) =\n                -diff_matrix(num_points - i - 1, num_points - j - 1);\n          }\n        }\n      } else if (num_points > 1) {\n        // D_{2-1}\n        //\n        diff_matrix(0, 0) = -1.0;\n        diff_matrix(0, 1) = 1.0;\n        for (size_t i = 2; i < num_points; ++i) {\n          diff_matrix(0, i) = 0.0;\n        }\n\n        for (size_t i = 1; i < num_points - 1; ++i) {\n          for (size_t j = 0; j < i - 1; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n          diff_matrix(i, i - 1) = -0.5;\n          diff_matrix(i, i) = 0.0;\n          diff_matrix(i, i + 1) = 0.5;\n          for (size_t j = i + 2; j < num_points; ++j) {\n            diff_matrix(i, j) = 0.0;\n          }\n        }\n\n        // Right boundary points. Reverse of left boundary, with opposite\n        // sign.\n        for (size_t i = num_points - 1; i < num_points; ++i) {\n          for (size_t j = 0; j < num_points; ++j) {\n            diff_matrix(i, j) =\n                -diff_matrix(num_points - i - 1, num_points - j - 1);\n          }\n        }\n      } else {\n        diff_matrix(0, 0) = 0.0;\n        return diff_matrix;\n      }\n      const double inv_delta = 1.0 / (collocation_pts[1] - collocation_pts[0]);\n      for (size_t i = 0; i < num_points; ++i) {\n        for (size_t j = 0; j < num_points; ++j) {\n          diff_matrix(i, j) *= inv_delta;\n        }\n      }\n    } else if constexpr (BasisType == Spectral::Basis::Fourier) {\n      diff_matrix = Fourier::differentiation_matrix(num_points);\n    } else {\n      const DataVector& bary_weights =\n          detail::barycentric_weights<BasisType, QuadratureType>(num_points);\n      for (size_t i = 0; i < num_points; ++i) {\n        double& diagonal = diff_matrix(i, i) = 0.0;\n        for (size_t j = 0; j < num_points; ++j) {\n          if (LIKELY(i != j)) {\n            diff_matrix(i, j) =\n                bary_weights[j] /\n                (bary_weights[i] * (collocation_pts[i] - collocation_pts[j]));\n            diagonal -= diff_matrix(i, j);\n          }\n        }\n      }\n    }\n    return diff_matrix;\n  }\n};\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct DifferentiationMatrixTransposeGenerator {\n  Matrix operator()(const size_t num_points) const {\n    Matrix diff_matrix_transpose =\n        DifferentiationMatrixGenerator<BasisType, QuadratureType>{}.operator()(\n            num_points);\n    blaze::transpose(diff_matrix_transpose);\n    return diff_matrix_transpose;\n  }\n};\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct ParityBasedDifferentiationMatrixGenerator {\n  Matrix operator()(const size_t num_points, const Parity parity) const {\n    // We intentionally do not instantiate \"normal\" derivatives for Zernike\n    // bases due to their large errors\n    if constexpr (BasisType == Spectral::Basis::ZernikeB1) {\n      return Zernike<1>::differentiation_matrix(num_points, parity);\n    } else if constexpr (BasisType == Spectral::Basis::ZernikeB2) {\n      return Zernike<2>::differentiation_matrix(num_points, parity);\n    } else if constexpr (BasisType == Spectral::Basis::ZernikeB3) {\n      return Zernike<3>::differentiation_matrix(num_points, parity);\n    } else {\n      ERROR(\n          \"Calling ParityBasedDifferentiationMatrixGenerator with a \"\n          \"non-Zernike basis: call non-parity generator\");\n    }\n  }\n};\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct WeakFluxDifferentiationMatrixGenerator {\n  Matrix operator()(const size_t num_points) const {\n    if (BasisType != Basis::Legendre) {\n      // We cannot use a `static_assert` because of the way our instantiation\n      // macros work for creating matrices\n      ERROR(\n          \"Weak differentiation matrix only implemented for Legendre \"\n          \"polynomials.\");\n    }\n    const DataVector& weights =\n        quadrature_weights<BasisType, QuadratureType>(num_points);\n\n    Matrix weak_diff_matrix =\n        DifferentiationMatrixGenerator<BasisType, QuadratureType>{}.operator()(\n            num_points);\n    transpose(weak_diff_matrix);\n    for (size_t i = 0; i < num_points; ++i) {\n      for (size_t j = 0; j < num_points; ++j) {\n        weak_diff_matrix(i, j) *= weights[j] / weights[i];\n      }\n    }\n    return weak_diff_matrix;\n  }\n};\n}  // namespace\n\nPRECOMPUTED_SPECTRAL_QUANTITY(differentiation_matrix, Matrix,\n                              DifferentiationMatrixGenerator)\nPRECOMPUTED_SPECTRAL_QUANTITY(differentiation_matrix_transpose, Matrix,\n                              DifferentiationMatrixTransposeGenerator)\nPRECOMPUTED_SPECTRAL_QUANTITY(weak_flux_differentiation_matrix, Matrix,\n                              WeakFluxDifferentiationMatrixGenerator)\n\n#undef PRECOMPUTED_SPECTRAL_QUANTITY\n\nPRECOMPUTED_SPECTRAL_QUANTITY_WITH_PARITY(\n    differentiation_matrix, Matrix, ParityBasedDifferentiationMatrixGenerator)\n\n#undef PRECOMPUTED_SPECTRAL_QUANTITY_WITH_PARITY\n\nSPECTRAL_QUANTITY_FOR_MESH(differentiation_matrix, Matrix)\nSPECTRAL_QUANTITY_FOR_MESH(differentiation_matrix_transpose, Matrix)\nSPECTRAL_QUANTITY_FOR_MESH(weak_flux_differentiation_matrix, Matrix)\n\n#undef SPECTRAL_QUANTITY_FOR_MESH\n\ntemplate const Matrix&\n    differentiation_matrix<Basis::Cartoon, Quadrature::AxialSymmetry>(size_t);\ntemplate const Matrix& differentiation_matrix<\n    Basis::Cartoon, Quadrature::SphericalSymmetry>(size_t);\ntemplate const Matrix&\n    differentiation_matrix<Basis::Chebyshev, Quadrature::Gauss>(size_t);\ntemplate const Matrix&\n    differentiation_matrix<Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix&\n    differentiation_matrix<Basis::Fourier, Quadrature::Equiangular>(size_t);\ntemplate const Matrix&\n    differentiation_matrix<Basis::Legendre, Quadrature::Gauss>(size_t);\ntemplate const Matrix&\n    differentiation_matrix<Basis::Legendre, Quadrature::GaussLobatto>(size_t);\n\ntemplate const Matrix& differentiation_matrix<\n    Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t, Parity);\ntemplate const Matrix& differentiation_matrix<\n    Basis::ZernikeB2, Quadrature::GaussRadauUpper>(size_t, Parity);\ntemplate const Matrix& differentiation_matrix<\n    Basis::ZernikeB3, Quadrature::GaussRadauUpper>(size_t, Parity);\n\ntemplate const Matrix& weak_flux_differentiation_matrix<\n    Basis::Chebyshev, Quadrature::Gauss>(size_t);\ntemplate const Matrix& weak_flux_differentiation_matrix<\n    Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix& weak_flux_differentiation_matrix<\n    Basis::Legendre, Quadrature::Gauss>(size_t);\ntemplate const Matrix& weak_flux_differentiation_matrix<\n    Basis::Legendre, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix& differentiation_matrix<Basis::FiniteDifference,\n                                              Quadrature::CellCentered>(size_t);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass Matrix;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\nenum class Parity : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/// @{\n/*!\n * \\brief %Matrix used to compute the derivative of a function.\n *\n * \\details For a function represented by the nodal coefficients \\f$u_j\\f$ a\n * matrix multiplication with the differentiation matrix \\f$D_{ij}\\f$ gives the\n * coefficients of the function's derivative. Since \\f$u(x)\\f$ is expanded in\n * Lagrange polynomials \\f$u(x)=\\sum_j u_j l_j(x)\\f$ the differentiation matrix\n * is computed as \\f$D_{ij}=l_j^\\prime(\\xi_i)\\f$ where the \\f$\\xi_i\\f$ are the\n * collocation points.\n *\n * The finite difference matrix uses summation by parts operators,\n * \\f$D_{2-1}, D_{4-2}, D_{4-3}\\f$, and \\f$D_{6-5}\\f$ from \\cite Diener2005tn.\n *\n * \\param num_points The number of collocation points\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& differentiation_matrix(size_t num_points);\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& differentiation_matrix_transpose(size_t num_points);\n/// @}\n\n/*!\n * \\brief %Matrix used to compute the derivative of a function of known parity\n *\n * \\details For the Zernike basis, defined on \\f$[0,1]\\f$, `GaussRadauUpper`\n * quadratrue shifts the collocation points to the upper side, which\n * contributes to inaccurate differentiation at the lower side due to the low\n * density of points. By knowing the parity of functions in this basis as it\n * has two indices, we can extend the function to the negative \\f$r\\f$,\n * greatly reducing errors.\n *\n * \\param num_points The number of collocation points\n * \\param parity The Parity of the function\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& differentiation_matrix(size_t num_points, Parity parity);\n\n/// @{\n/*!\n * \\brief Differentiation matrix for a one-dimensional mesh.\n *\n * \\see differentiation_matrix(size_t)\n */\nconst Matrix& differentiation_matrix(const Mesh<1>& mesh);\nconst Matrix& differentiation_matrix_transpose(const Mesh<1>& mesh);\n/// @}\n\n/*!\n * \\brief %Matrix used to compute the divergence of the flux in weak form.\n *\n * This is the transpose of the differentiation matrix multiplied by quadrature\n * weights that appear in DG integrals:\n *\n * \\begin{equation}\n * \\frac{D^T_{ij}} \\frac{w_j}{w_i}\n * \\end{equation}\n *\n * \\param num_points The number of collocation points\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& weak_flux_differentiation_matrix(size_t num_points);\n\n/*!\n * \\brief %Matrix used to compute the divergence of the flux in weak form.\n *\n * \\see weak_flux_differentiation_matrix(size_t)\n */\nconst Matrix& weak_flux_differentiation_matrix(const Mesh<1>& mesh);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Filtering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Filtering.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Spectral::filtering {\nMatrix exponential_filter(const Mesh<1>& mesh, const double alpha,\n                          const unsigned half_power) {\n  if (UNLIKELY(mesh.number_of_grid_points() == 1)) {\n    return Matrix(1, 1, 1.0);\n  }\n  const Matrix& nodal_to_modal = Spectral::nodal_to_modal_matrix(mesh);\n  const Matrix& modal_to_nodal = Spectral::modal_to_nodal_matrix(mesh);\n  Matrix filter_matrix(mesh.number_of_grid_points(),\n                       mesh.number_of_grid_points(), 0.0);\n  const double order = mesh.number_of_grid_points() - 1.0;\n  for (size_t i = 0; i < mesh.number_of_grid_points(); ++i) {\n    filter_matrix(i, i) = exp(-alpha * pow(i / order, 2 * half_power));\n  }\n  return modal_to_nodal * filter_matrix * nodal_to_modal;\n}\n\nnamespace {\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nstruct ZeroLowestModesImpl {\n  Matrix operator()(const size_t num_points,\n                    const size_t num_modes_to_zero) const {\n    const Matrix& nodal_to_modal =\n        Spectral::nodal_to_modal_matrix<BasisType, QuadratureType>(num_points);\n    const Matrix& modal_to_nodal =\n        Spectral::modal_to_nodal_matrix<BasisType, QuadratureType>(num_points);\n    Matrix filter_matrix(num_points, num_points, 0.0);\n    for (size_t i = num_modes_to_zero; i < num_points; ++i) {\n      filter_matrix(i, i) = 1.0;\n    }\n    return Matrix(modal_to_nodal * filter_matrix * nodal_to_modal);\n  }\n};\n}  // namespace\n\nconst Matrix& zero_lowest_modes(const Mesh<1>& mesh,\n                                const size_t number_of_modes_to_zero) {\n  ASSERT(number_of_modes_to_zero < mesh.extents(0),\n         \"For a 1d mesh with \" << mesh.extents(0)\n                               << \" grid points, you cannot zero \"\n                               << number_of_modes_to_zero << \" modes.\");\n\n  switch (mesh.basis(0)) {\n    case Basis::Legendre:\n      switch (mesh.quadrature(0)) {\n        case Spectral::Quadrature::GaussLobatto: {\n          constexpr size_t max_num_points =\n              Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n          constexpr size_t min_num_points = Spectral::minimum_number_of_points<\n              Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>;\n          const auto cache =\n              make_static_cache<CacheRange<min_num_points, max_num_points + 1>,\n                                CacheRange<0_st, max_num_points>>(\n                  ZeroLowestModesImpl<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::GaussLobatto>{});\n          return cache(mesh.number_of_grid_points(), number_of_modes_to_zero);\n        }\n        case Spectral::Quadrature::Gauss: {\n          constexpr size_t max_num_points =\n              Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n          constexpr size_t min_num_points =\n              Spectral::minimum_number_of_points<Spectral::Basis::Legendre,\n                                                 Spectral::Quadrature::Gauss>;\n          const auto cache =\n              make_static_cache<CacheRange<min_num_points, max_num_points + 1>,\n                                CacheRange<0_st, max_num_points>>(\n                  ZeroLowestModesImpl<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::Gauss>{});\n          return cache(mesh.number_of_grid_points(), number_of_modes_to_zero);\n        }\n        default:\n          ERROR(\"Unsupported quadrature type in filtering lowest modes: \"\n                << mesh.quadrature(0));\n      };\n    case Basis::Chebyshev:\n      switch (mesh.quadrature(0)) {\n        case Spectral::Quadrature::GaussLobatto: {\n          constexpr size_t max_num_points =\n              Spectral::maximum_number_of_points<Spectral::Basis::Chebyshev>;\n          constexpr size_t min_num_points = Spectral::minimum_number_of_points<\n              Spectral::Basis::Chebyshev, Spectral::Quadrature::GaussLobatto>;\n          const auto cache =\n              make_static_cache<CacheRange<min_num_points, max_num_points + 1>,\n                                CacheRange<0_st, max_num_points>>(\n                  ZeroLowestModesImpl<Spectral::Basis::Chebyshev,\n                                      Spectral::Quadrature::GaussLobatto>{});\n          return cache(mesh.number_of_grid_points(), number_of_modes_to_zero);\n        }\n        case Spectral::Quadrature::Gauss: {\n          constexpr size_t max_num_points =\n              Spectral::maximum_number_of_points<Spectral::Basis::Chebyshev>;\n          constexpr size_t min_num_points =\n              Spectral::minimum_number_of_points<Spectral::Basis::Chebyshev,\n                                                 Spectral::Quadrature::Gauss>;\n          const auto cache =\n              make_static_cache<CacheRange<min_num_points, max_num_points + 1>,\n                                CacheRange<0_st, max_num_points>>(\n                  ZeroLowestModesImpl<Spectral::Basis::Chebyshev,\n                                      Spectral::Quadrature::Gauss>{});\n          return cache(mesh.number_of_grid_points(), number_of_modes_to_zero);\n        }\n        default:\n          ERROR(\"Unsupported quadrature type in filtering lowest modes: \"\n                << mesh.quadrature(0));\n      };\n    default:\n      ERROR(\"Cannot filter basis type: \" << mesh.basis(0));\n  };\n}\n}  // namespace Spectral::filtering\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Filtering.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass Matrix;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\ingroup SpectralGroup\n * \\brief Matrices for filtering spectral coefficients.\n */\nnamespace filtering {\n/*!\n * \\brief Returns a `Matrix` by which to multiply the nodal coefficients to\n * apply a stable exponential filter.\n *\n * The exponential filter rescales the modal coefficients according to:\n *\n * \\f{align*}{\n *  c_i\\to c_i \\exp\\left[-\\alpha \\left(\\frac{i}{N}\\right)^{2m}\\right]\n * \\f}\n *\n * where \\f$c_i\\f$ are the zero-indexed modal coefficients, \\f$N\\f$ is the basis\n * degree (number of grid points per element per dimension minus one),\n * \\f$\\alpha\\f$ determines how much the coefficients are rescaled, and \\f$m\\f$\n * determines how aggressive/broad the filter is (lower values means filtering\n * more coefficients). Setting \\f$\\alpha=36\\f$ results in setting the highest\n * coefficient to machine precision, effectively zeroing it out.\n *\n * \\note The filter matrix is not cached by the function because it depends on a\n * double, an integer, and the mesh, which could make caching very memory\n * intensive. The caller of this function is responsible for determining whether\n * or not the matrix should be cached.\n */\nMatrix exponential_filter(const Mesh<1>& mesh, double alpha,\n                          unsigned half_power);\n\n/*!\n * \\brief Zeros the lowest `number_of_modes_to_zero` modal coefficients. Note\n * that the matrix must be applied to a nodal representation.\n *\n * Given a function \\f$u\\f$\n *\n * \\f{align}{\n *   u(x)=\\sum_{i=0}^N c_i P_i(x),\n * \\f}\n *\n * where \\f$c_i\\f$ are the modal coefficients and \\f$P_i(x)\\f$ is the basis\n * (e.g. Legendre polynomials), the filter matrix will take the *nodal*\n * representation of \\f$u\\f$ and zero out the lowest `number_of_modes_to_zero`\n * modal coefficients. That is, after the filter is applied \\f$u\\to\\bar{u}\\f$ is\n *\n * \\f{align}{\n *   \\bar{u}(x)=\\sum_{i=k}^N c_i P_i(x),\n * \\f}\n *\n * where \\f$k\\f$ is the number of modes set to zero. The output \\f$\\bar{u}\\f$ is\n * also in the *nodal* representation.\n */\nconst Matrix& zero_lowest_modes(const Mesh<1>& mesh,\n                                size_t number_of_modes_to_zero);\n}  // namespace filtering\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/FiniteDifference.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/InverseWeightFunctionValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Spectral {\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::FiniteDifference, Quadrature::CellCentered>(\n    const size_t num_points) {\n  DataVector x{num_points};\n  // The finite difference grid cells cover the interval [-1, 1]\n  constexpr double lower_bound = -1.0;\n  constexpr double upper_bound = 1.0;\n  const double delta_x = (upper_bound - lower_bound) / num_points;\n  // Weights for integration using midpoint method\n  DataVector w{num_points, delta_x};\n  for (size_t i = 0; i < num_points; ++i) {\n    x[i] = lower_bound + 0.5 * delta_x + i * delta_x;\n  }\n  return std::make_pair(std::move(x), std::move(w));\n}\n\ntemplate <>\nstd::pair<DataVector, DataVector> compute_collocation_points_and_weights<\n    Basis::FiniteDifference, Quadrature::FaceCentered>(\n    const size_t num_points) {\n  DataVector x{num_points};\n  // The finite difference grid cells cover the interval [-1, 1]\n  constexpr double lower_bound = -1.0;\n  constexpr double upper_bound = 1.0;\n  const double delta_x = (upper_bound - lower_bound) / (num_points - 1.0);\n  // Weights for integration using midpoint method\n  DataVector w{num_points, delta_x};\n  for (size_t i = 0; i < num_points; ++i) {\n    x[i] = lower_bound + i * delta_x;\n  }\n  return std::make_pair(std::move(x), std::move(w));\n}\n\ntemplate <>\nDataVector compute_inverse_weight_function_values<Basis::FiniteDifference>(\n    const DataVector& x) {\n  DataVector iw{x.size(),1.0};\n  return iw;\n}\n\n// The below definitions are necessary to successfully link with some compilers.\ntemplate <Basis BasisType>\nMatrix spectral_indefinite_integral_matrix(size_t num_points);\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\ntemplate <>\nMatrix spectral_indefinite_integral_matrix<Basis::FiniteDifference>(\n    const size_t /*num_points*/) {\n  ERROR(\"No indefinite integration matrix exists.\\n\");\n}\n\ntemplate <>\nDataVector compute_basis_function_value<Basis::FiniteDifference>(\n    const size_t /*k*/, const DataVector& /*x*/) {\n  ERROR(\"No basis functions to compute.\\n\");\n}\n\ntemplate <>\ndouble compute_basis_function_value<Basis::FiniteDifference>(\n    const size_t /*k*/, const double& /*x*/) {\n  ERROR(\"No basis functions to compute.\\n\");\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__)\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Spectral::detail {\ntemplate <typename F>\ndecltype(auto) get_spectral_quantity_for_mesh(F&& f, const Mesh<1>& mesh) {\n  const auto num_points = mesh.extents(0);\n  // Switch on runtime values of basis and quadrature to select\n  // corresponding template specialization. For basis functions spanning\n  // multiple dimensions we can generalize this function to take a\n  // higher-dimensional Mesh.\n  switch (mesh.basis(0)) {\n    case Basis::Legendre:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::Gauss:\n          return f(std::integral_constant<Basis, Basis::Legendre>{},\n                   std::integral_constant<Quadrature, Quadrature::Gauss>{},\n                   num_points);\n        case Quadrature::GaussLobatto:\n          return f(\n              std::integral_constant<Basis, Basis::Legendre>{},\n              std::integral_constant<Quadrature, Quadrature::GaussLobatto>{},\n              num_points);\n        default:\n          ERROR(\"Missing quadrature case for spectral quantity\");\n      }\n    case Basis::Chebyshev:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::Gauss:\n          return f(std::integral_constant<Basis, Basis::Chebyshev>{},\n                   std::integral_constant<Quadrature, Quadrature::Gauss>{},\n                   num_points);\n        case Quadrature::GaussLobatto:\n          return f(\n              std::integral_constant<Basis, Basis::Chebyshev>{},\n              std::integral_constant<Quadrature, Quadrature::GaussLobatto>{},\n              num_points);\n        default:\n          ERROR(\"Missing quadrature case for spectral quantity\");\n      }\n    case Basis::Cartoon:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::AxialSymmetry:\n          return f(\n              std::integral_constant<Basis, Basis::Cartoon>{},\n              std::integral_constant<Quadrature, Quadrature::AxialSymmetry>{},\n              num_points);\n        case Quadrature::SphericalSymmetry:\n          return f(std::integral_constant<Basis, Basis::Cartoon>{},\n                   std::integral_constant<Quadrature,\n                                          Quadrature::SphericalSymmetry>{},\n                   num_points);\n        default:\n          ERROR(\n              \"Only Axial and Spherical Symmetry quadratures are allowed for \"\n              \"a Cartoon basis.\");\n      }\n    case Basis::Fourier:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::Equiangular:\n          return f(\n              std::integral_constant<Basis, Basis::Fourier>{},\n              std::integral_constant<Quadrature, Quadrature::Equiangular>{},\n              num_points);\n        default:\n          ERROR(\"Missing quadrature case for spectral quantity\");\n      }\n    case Basis::ZernikeB1:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::GaussRadauUpper:\n          return f(\n              std::integral_constant<Basis, Basis::ZernikeB1>{},\n              std::integral_constant<Quadrature, Quadrature::GaussRadauUpper>{},\n              num_points);\n        default:\n          ERROR(\"Missing quadrature case for spectral quantity\");\n      }\n    case Basis::ZernikeB2:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::GaussRadauUpper:\n          return f(\n              std::integral_constant<Basis, Basis::ZernikeB2>{},\n              std::integral_constant<Quadrature, Quadrature::GaussRadauUpper>{},\n              num_points);\n        case Quadrature::Equiangular:\n          // While ZernikeB2 with Equiangular is treated differently in\n          // operators (e.g. partial derivatives, interpolation), in terms of\n          // its spectral quantities it is Fourier with Equiangular\n          return f(\n              std::integral_constant<Basis, Basis::Fourier>{},\n              std::integral_constant<Quadrature, Quadrature::Equiangular>{},\n              num_points);\n        default:\n          ERROR(\"Missing quadrature case for spectral quantity\");\n      }\n    case Basis::ZernikeB3:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::GaussRadauUpper:\n          return f(\n              std::integral_constant<Basis, Basis::ZernikeB3>{},\n              std::integral_constant<Quadrature, Quadrature::GaussRadauUpper>{},\n              num_points);\n        default:\n          ERROR(\"Missing quadrature case for spectral quantity\");\n      }\n    case Basis::FiniteDifference:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::CellCentered:\n          return f(\n              std::integral_constant<Basis, Basis::FiniteDifference>{},\n              std::integral_constant<Quadrature, Quadrature::CellCentered>{},\n              num_points);\n        case Quadrature::FaceCentered:\n          return f(\n              std::integral_constant<Basis, Basis::FiniteDifference>{},\n              std::integral_constant<Quadrature, Quadrature::FaceCentered>{},\n              num_points);\n        default:\n          ERROR(\n              \"Only CellCentered and FaceCentered are supported for finite \"\n              \"difference quadrature.\");\n      }\n    case Basis::SphericalHarmonic:\n      ERROR(\n          \"Basis::SphericalHarmonic is a two-dimensional basis and is not \"\n          \"supported for this function.  If you want the collocation points, \"\n          \"use the function logical_coordinates.\");\n    default:\n      ERROR(\"Missing basis case for spectral quantity. The missing basis is: \"\n            << mesh.basis(0));\n  }\n}\n\ntemplate <typename F>\ndecltype(auto) get_two_indexed_spectral_quantity_for_mesh(F&& f,\n                                                          const Mesh<1>& mesh,\n                                                          const size_t m,\n                                                          const size_t N) {\n  const auto num_points = mesh.extents(0);\n  switch (mesh.basis(0)) {\n    case Basis::ZernikeB1:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::GaussRadauUpper:\n          return f(\n              std::integral_constant<Basis, Basis::ZernikeB1>{},\n              std::integral_constant<Quadrature, Quadrature::GaussRadauUpper>{},\n              num_points, m, N);\n        default:\n          ERROR(\"Missing quadrature case for two-indexed spectral quantity\");\n      }\n    case Basis::ZernikeB2:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::GaussRadauUpper:\n          return f(\n              std::integral_constant<Basis, Basis::ZernikeB2>{},\n              std::integral_constant<Quadrature, Quadrature::GaussRadauUpper>{},\n              num_points, m, N);\n        default:\n          ERROR(\"Missing quadrature case for two-indexed spectral quantity\");\n      }\n    case Basis::ZernikeB3:\n      switch (mesh.quadrature(0)) {\n        case Quadrature::GaussRadauUpper:\n          return f(\n              std::integral_constant<Basis, Basis::ZernikeB3>{},\n              std::integral_constant<Quadrature, Quadrature::GaussRadauUpper>{},\n              num_points, m, N);\n        default:\n          ERROR(\"Missing quadrature case for two-indexed spectral quantity\");\n      }\n    default:\n      ERROR(\n          \"Missing basis case for two-indexed spectral quantity. The \"\n          \"missing basis is: \"\n          << mesh.basis(0));\n  }\n}\n\n}  // namespace Spectral::detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/IntegrationMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/IntegrationMatrix.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SpectralQuantityForMesh.hpp\"\n\nnamespace Spectral {\ntemplate <Basis BasisType>\nMatrix spectral_indefinite_integral_matrix(size_t num_points);\n\nnamespace {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct IntegrationMatrixGenerator {\n  Matrix operator()(const size_t num_points) const {\n    return Spectral::modal_to_nodal_matrix<BasisType, QuadratureType>(\n               num_points) *\n           spectral_indefinite_integral_matrix<BasisType>(num_points) *\n           Spectral::nodal_to_modal_matrix<BasisType, QuadratureType>(\n               num_points);\n  }\n};\n}  // namespace\n\nPRECOMPUTED_SPECTRAL_QUANTITY(integration_matrix, Matrix,\n                              IntegrationMatrixGenerator)\n\n#undef PRECOMPUTED_SPECTRAL_QUANTITY\n\nSPECTRAL_QUANTITY_FOR_MESH(integration_matrix, Matrix)\n\n#undef SPECTRAL_QUANTITY_FOR_MESH\n\ntemplate const Matrix& integration_matrix<Basis::Chebyshev, Quadrature::Gauss>(\n    size_t);\ntemplate const Matrix&\n    integration_matrix<Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix& integration_matrix<Basis::Legendre, Quadrature::Gauss>(\n    size_t);\ntemplate const Matrix&\n    integration_matrix<Basis::Legendre, Quadrature::GaussLobatto>(size_t);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/IntegrationMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass Matrix;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief %Matrix used to perform an indefinite integral of a function over the\n * logical grid. The left boundary condition is such that the integral is 0 at\n * \\f$\\xi=-1\\f$\n *\n * Currently only Legendre and Chebyshev polynomials are implemented, but we\n * provide a derivation for how to compute the indefinite integration matrix for\n * general Jacobi polynomials.\n *\n * #### Legendre Polynomials\n * The Legendre polynomials have the identity:\n *\n * \\f{align*}{\n * P_n(x) = \\frac{1}{2n+1}\\frac{d}{dx}\\left(P_{n+1}(x) - P_{n-1}(x)\\right)\n * \\f}\n *\n * The goal is to evaluate the integral of a function \\f$u\\f$ expanded in terms\n * of Legendre polynomials as\n *\n * \\f{align*}{\n * u(x) = \\sum_{i=0}^{N} c_i P_i(x)\n * \\f}\n *\n * We similarly expand the indefinite integral of \\f$u\\f$ as\n *\n * \\f{align*}{\n * \\left.\\int u(y) dy\\right\\rvert_{x}=&\\sum_{i=0}^N \\tilde{c}_i P_i(x) \\\\\n *   =&\\left.\\int\\sum_{i=1}^{N}\\frac{c_i}{2i+1}\n *      \\left(P_{i+1}(y)-P_{i-1}(y)\\right)dy\\right\\rvert_{x}\n *      + \\tilde{c}_0 P_0(x) \\\\\n *   =&\\sum_{i=1}^{N}\\left(\\frac{c_{i-1}}{2i-1} - \\frac{c_{i+1}}{2i+3}\\right)\n *     P_i(x) + \\tilde{c}_0 P_0(x)\n * \\f}\n *\n * Thus we get that for \\f$i>0\\f$\n *\n * \\f{align*}{\n * \\tilde{c}_i=\\frac{c_{i-1}}{2i-1}-\\frac{c_{i+1}}{2i+3}\n * \\f}\n *\n * and \\f$\\tilde{c}_0\\f$ is a constant of integration, which we choose such that\n * the integral is 0 at the left boundary of the domain (\\f$x=-1\\f$). The\n * condition for this is:\n *\n * \\f{align*}{\n *   \\tilde{c}_0=\\sum_{i=1}^{N}(-1)^{i+1}\\tilde{c}_i\n * \\f}\n *\n * The matrix returned by this function is the product of the tridiagonal matrix\n * for the \\f$\\tilde{c}_i\\f$ and the matrix for the boundary condition.\n *\n * #### Chebyshev Polynomials\n *\n * A similar derivation leads to the relations:\n *\n * \\f{align*}{\n *  \\tilde{c}_i=&\\frac{c_{i-1}-c_{i+1}}{2i},&\\mathrm{if}\\;i>1 \\\\\n *  \\tilde{c}_1=&c_0 - \\frac{c_2}{2},&\\mathrm{if}\\;i=1 \\\\\n * \\f}\n *\n * We again have:\n *\n * \\f{align*}{\n * \\tilde{c}_0=\\sum_{i=1}^N(-1)^{i+1}\\tilde{c}_i\n * \\f}\n *\n * These are then used to define the indefinite integration matrix.\n *\n * #### Jacobi Polynomials\n *\n * For general Jacobi polynomials \\f$P^{(\\alpha,\\beta)}_n(x)\\f$ given by\n *\n * \\f{align*}{\n *  (1-x)^\\alpha(1+x)^\\beta P^{(\\alpha,\\beta)}_n(x)=\\frac{(-1)^n}{2^n n!}\n *  \\frac{d^n}{dx^n}\\left[(1-x)^{\\alpha+n}(1+x)^{\\beta+n}\\right]\n * \\f}\n *\n * we have that\n *\n * \\f{align*}{\n * P^{(\\alpha,\\beta)}_n(x)=\\frac{d}{dx}\\left(\n * b^{(\\alpha,\\beta)}_{n-1,n}P^{(\\alpha,\\beta)}_{n-1}(x) +\n * b^{(\\alpha,\\beta)}_{n,n}P^{(\\alpha,\\beta)}_n(x) +\n * b^{(\\alpha,\\beta)}_{n+1,n}P^{(\\alpha,\\beta)}_{n+1}(x)\n * \\right)\n * \\f}\n *\n * where\n *\n * \\f{align*}{\n * b^{(\\alpha,\\beta)}_{n-1,n}=&-\\frac{1}{n+\\alpha+\\beta}\n *                            a^{(\\alpha,\\beta)}_{n-1,n} \\\\\n * b^{(\\alpha,\\beta)}_{n,n}=&-\\frac{2}{\\alpha+\\beta}\n *                          a^{(\\alpha,\\beta)}_{n,n} \\\\\n * b^{(\\alpha,\\beta)}_{n+1,n}=&\\frac{1}{n+1}\n *                            a^{(\\alpha,\\beta)}_{n+1,n} \\\\\n * a^{(\\alpha,\\beta)}_{n-1,n}=&\\frac{2(n+\\alpha)(n+\\beta)}\n *            {(2n+\\alpha+\\beta+1)(2n+\\alpha+\\beta)} \\\\\n * a^{(\\alpha,\\beta)}_{n,n}=&-\\frac{\\alpha^2-\\beta^2}\n *            {(2n+\\alpha+\\beta+2)(2n+\\alpha+\\beta)} \\\\\n * a^{(\\alpha,\\beta)}_{n-1,n}=&\\frac{2(n+1)(n+\\alpha+\\beta+1)}\n *            {(2n+\\alpha+\\beta+2)(2n+\\alpha+\\beta+1)}\n * \\f}\n *\n * Following the same derivation we get that\n *\n * \\f{align*}{\n *   \\tilde{c}_i=c_{i+1}b^{(\\alpha,\\beta)}_{i,i+1}\n *              +c_i b^{(\\alpha,\\beta)}_{i,i}\n *              +c_{i-1}b^{(\\alpha,\\beta)}_{i,i-1}\n * \\f}\n *\n * and the boundary condition is\n *\n * \\f{align*}{\n *  \\tilde{c}_0=\\sum_{i=1}^N(-1)^{i+1}\n *              \\frac{\\Gamma(i+\\alpha+1)}{i!\\Gamma(\\alpha+1)} \\tilde{c}_i\n * \\f}\n *\n * where \\f$\\Gamma(x)\\f$ is the Gamma function.\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& integration_matrix(size_t num_points);\n\n/*!\n * \\brief Indefinite integration matrix for a one-dimensional mesh.\n *\n * \\see integration_matrix(size_t)\n */\nconst Matrix& integration_matrix(const Mesh<1>& mesh);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/InterpolationMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/BarycentricWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Fourier.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n\nnamespace Spectral {\ntemplate <Basis BasisType, Quadrature QuadratureType, typename T>\nMatrix interpolation_matrix(const size_t num_points, const T& target_points) {\n  if constexpr (BasisType == Spectral::Basis::FiniteDifference) {\n    ERROR(\n        \"Cannot do barycentric interpolation with Basis::FiniteDifference.  \"\n        \"Use an IrregularInterpolant\");\n  }\n  if constexpr (BasisType == Spectral::Basis::Cartoon) {\n    ASSERT(num_points == 1, \"A Cartoon basis can only have one point.\");\n    const size_t num_target_points = get_size(target_points);\n    Matrix interp_matrix(num_target_points, num_points);\n    for (size_t k = 0; k < num_target_points; ++k) {\n      interp_matrix(k, 0) = 1.0;\n    }\n    return interp_matrix;\n  } else if constexpr (BasisType == Spectral::Basis::Fourier) {\n    return Fourier::interpolation_matrix(num_points, target_points);\n  }\n  constexpr size_t max_num_points =\n      Spectral::maximum_number_of_points<BasisType>;\n  constexpr size_t min_num_points =\n      Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n  ASSERT(num_points >= min_num_points,\n         \"Tried to work with less than the minimum number of collocation \"\n         \"points for this quadrature.\");\n  ASSERT(num_points <= max_num_points,\n         \"Exceeded maximum number of collocation points.\");\n  const DataVector& collocation_pts =\n      collocation_points<BasisType, QuadratureType>(num_points);\n  const DataVector& bary_weights =\n      detail::barycentric_weights<BasisType, QuadratureType>(num_points);\n  const size_t num_target_points = get_size(target_points);\n  Matrix interp_matrix(num_target_points, num_points);\n  // Algorithm 32 in Kopriva, p. 76\n  // This is valid for any collocation points.\n  for (size_t k = 0; k < num_target_points; k++) {\n    // Check where no interpolation is necessary since a target point\n    // matches the original collocation points\n    bool row_has_match = false;\n    for (size_t j = 0; j < num_points; j++) {\n      interp_matrix(k, j) = 0.0;\n      if (equal_within_roundoff(get_element(target_points, k),\n                                collocation_pts[j])) {\n        interp_matrix(k, j) = 1.0;\n        row_has_match = true;\n      }\n    }\n    // Perform interpolation for non-matching points\n    if (not row_has_match) {\n      double sum = 0.0;\n      for (size_t j = 0; j < num_points; j++) {\n        interp_matrix(k, j) = bary_weights[j] / (get_element(target_points, k) -\n                                                 collocation_pts[j]);\n        sum += interp_matrix(k, j);\n      }\n      for (size_t j = 0; j < num_points; j++) {\n        interp_matrix(k, j) /= sum;\n      }\n    }\n  }\n  return interp_matrix;\n}\n\ntemplate <typename T>\nMatrix interpolation_matrix(const Mesh<1>& mesh, const T& target_points) {\n  return detail::get_spectral_quantity_for_mesh(\n      [target_points](const auto basis, const auto quadrature,\n                      const size_t num_points) -> Matrix {\n        return interpolation_matrix<decltype(basis)::value,\n                                    decltype(quadrature)::value>(num_points,\n                                                                 target_points);\n      },\n      mesh);\n}\n\ntemplate Matrix interpolation_matrix<Basis::Chebyshev, Quadrature::Gauss>(\n    size_t, const std::vector<double>&);\ntemplate Matrix\ninterpolation_matrix<Basis::Chebyshev, Quadrature::GaussLobatto>(\n    size_t, const std::vector<double>&);\ntemplate Matrix interpolation_matrix<Basis::Fourier, Quadrature::Equiangular>(\n    size_t, const std::vector<double>&);\ntemplate Matrix interpolation_matrix<Basis::Legendre, Quadrature::Gauss>(\n    size_t, const std::vector<double>&);\ntemplate Matrix interpolation_matrix<Basis::Legendre, Quadrature::GaussLobatto>(\n    size_t, const std::vector<double>&);\ntemplate Matrix interpolation_matrix<Basis::Chebyshev, Quadrature::Gauss>(\n    size_t, const DataVector&);\ntemplate Matrix interpolation_matrix<\n    Basis::Chebyshev, Quadrature::GaussLobatto>(size_t, const DataVector&);\ntemplate Matrix interpolation_matrix<Basis::Fourier, Quadrature::Equiangular>(\n    size_t, const DataVector&);\ntemplate Matrix interpolation_matrix<Basis::Legendre, Quadrature::Gauss>(\n    size_t, const DataVector&);\ntemplate Matrix interpolation_matrix<Basis::Legendre, Quadrature::GaussLobatto>(\n    size_t, const DataVector&);\ntemplate Matrix interpolation_matrix<Basis::Fourier, Quadrature::Equiangular>(\n    size_t, const double&);\ntemplate Matrix Spectral::interpolation_matrix(const Mesh<1>&,\n                                               const DataVector&);\ntemplate Matrix Spectral::interpolation_matrix(const Mesh<1>&,\n                                               const std::vector<double>&);\ntemplate Matrix Spectral::interpolation_matrix(const Mesh<1>&, const double&);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/InterpolationMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass Matrix;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief %Matrix used to interpolate to the \\p target_points.\n *\n * \\warning For each target point located outside of the logical coordinate\n * interval covered by `BasisType` (often \\f$[-1,1]\\f$), the resulting matrix\n * performs polynomial extrapolation instead of interpolation. The extrapolation\n * will be correct but may suffer from reduced accuracy, especially for\n * higher-order polynomials (i.e., larger values of `num_points`).\n *\n * \\param num_points The number of collocation points\n * \\param target_points The points to interpolate to\n */\ntemplate <Basis BasisType, Quadrature QuadratureType, typename T>\nMatrix interpolation_matrix(size_t num_points, const T& target_points);\n\n/*!\n * \\brief Interpolation matrix to the \\p target_points for a one-dimensional\n * mesh.\n *\n * \\see interpolation_matrix(size_t, const T&)\n */\ntemplate <typename T>\nMatrix interpolation_matrix(const Mesh<1>& mesh, const T& target_points);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/InterpolationWeights.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/InterpolationWeights.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Spectral {\ntemplate <typename TargetDataType>\nMatrix fornberg_interpolation_matrix(const TargetDataType& x_target,\n                                     const DataVector& x_source) {\n  Matrix result{get_size(x_target), x_source.size()};\n  const size_t n_source_pts = x_source.size();\n  for (size_t k = 0; k < get_size(x_target); ++k) {\n    double c1 = 1.0;\n    double c4 = x_source[0] - get_element(x_target, k);\n    result(k, 0) = 1.0;\n    for (size_t i = 1; i < n_source_pts; ++i) {\n      double c2 = 1.0;\n      const double c5 = c4;\n      c4 = x_source[i] - get_element(x_target, k);\n      for (size_t j = 0; j < i; ++j) {\n        const double c3 = x_source[i] - x_source[j];\n        c2 *= c3;\n        if (j + 1 == i) {\n          result(k, i) = -c1 * c5 * result(k, i - 1) / c2;\n        }\n        result(k, j) = c4 * result(k, j) / c3;\n      }\n      c1 = c2;\n    }\n  }\n  return result;\n}\n\ntemplate <size_t MaxOrder>\nvoid fornberg_derivative_interpolation_weights(\n    const gsl::not_null<std::array<DataVector, MaxOrder + 1>*> result,\n    const double x_target, const DataVector& x_source) {\n  auto& c = *result;\n\n  // Using convention that n is the index of the last point\n  const size_t n = x_source.size() - 1;\n\n  // Assign c and fill with zeros\n  for (size_t i = 0; i < MaxOrder + 1; ++i) {\n    gsl::at(c, i) = DataVector(n + 1, 0.0);\n  }\n\n  double c1 = 1.0;\n  double c4 = x_source[0] - x_target;\n  c[0][0] = 1.0;\n  for (size_t i = 1; i <= n; ++i) {\n    const size_t mn = (i < MaxOrder ? i : MaxOrder);\n    double c2 = 1.0;\n    const double c5 = c4;\n    c4 = x_source[i] - x_target;\n    for (size_t j = 0; j < i; ++j) {\n      const double c3 = x_source[i] - x_source[j];\n      c2 *= c3;\n      if (j == i - 1) {\n        for (size_t k = mn; k > 0; --k) {\n          gsl::at(c, k)[i] =\n              c1 * (k * gsl::at(c, k - 1)[i - 1] - c5 * gsl::at(c, k)[i - 1]) /\n              c2;\n        }\n        c[0][i] = -c1 * c5 * c[0][i - 1] / c2;\n      }\n      for (size_t k = mn; k > 0; --k) {\n        gsl::at(c, k)[j] =\n            (c4 * gsl::at(c, k)[j] - k * gsl::at(c, k - 1)[j]) / c3;\n      }\n      c[0][j] = c4 * c[0][j] / c3;\n    }\n    c1 = c2;\n  }\n}\n\ntemplate <typename TargetDataType>\nMatrix fourier_interpolation_matrix(const TargetDataType& x_target,\n                                    const size_t n_source_points) {\n  if (n_source_points == 1) {\n    return Matrix{get_size(x_target), 1, 1.0};\n  }\n  DataVector x_source{n_source_points,\n                      2.0 * M_PI / static_cast<double>(n_source_points)};\n  for (size_t i = 0; i < n_source_points; ++i) {\n    x_source[i] *= static_cast<double>(i);\n  }\n  Matrix result{get_size(x_target), n_source_points,\n                1.0 / static_cast<double>(n_source_points)};\n  const bool n_source_points_is_even = n_source_points % 2 == 0;\n  for (size_t k = 0; k < get_size(x_target); ++k) {\n    for (size_t i = 0; i < n_source_points; ++i) {\n      double c0 = 1.0;\n      double c1 = cos(get_element(x_target, k) - x_source[i]);\n      const double cdx2 = 2.0 * c1;\n      double sum = c1;\n      for (size_t j = 2; j <= n_source_points / 2; ++j) {\n        const double tmp = c1;\n        c1 = cdx2 * c1 - c0;\n        c0 = tmp;\n        sum += c1;\n      }\n      result(k, i) *=\n          n_source_points_is_even ? 2.0 * sum + 1.0 - c1 : 2.0 * sum + 1.0;\n    }\n  }\n  return result;\n}\n\n// Generate instantiations\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template Matrix fornberg_interpolation_matrix(const DTYPE(data) & x_target, \\\n                                                const DataVector& x_source);  \\\n  template Matrix fourier_interpolation_matrix(const DTYPE(data) & x_target,  \\\n                                               const size_t n_source_points);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n#define DNUM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DNUM_PLUS_ONE(data) BOOST_PP_ADD(BOOST_PP_TUPLE_ELEM(0, data), 1)\n\n#define INSTANTIATE(_, data)                                            \\\n  template void fornberg_derivative_interpolation_weights<DNUM(data)>(  \\\n      const gsl::not_null<std::array<DataVector, DNUM_PLUS_ONE(data)>*> \\\n          result,                                                       \\\n      const double x_target, const DataVector& x_source);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3, 4))\n\n#undef DNUM\n#undef DNUM_PLUS_ONE\n#undef INSTANTIATE\n\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/InterpolationWeights.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass DataVector;\nclass Matrix;\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief Computes the matrix for polynomial interpolation of a non-periodic\n * function known at the set of points \\f$x_{source}\\f$ to the set of points\n * \\f$x_{target}\\f$\n *\n * \\details The algorithm is from \\cite Fornberg1998.  The returned matrix\n * \\f$M\\f$ will have \\f$n_{target}\\f$ rows and \\f$n_{source}\\f$ columns so that\n * \\f$f_{target} = M f_{source}\\f$\n *\n * \\note The accuracy of the interpolation will depend upon the number and\n * distribution of the source points.  It is strongly suggested that you\n * carefully investigate the accuracy for your use case.\n */\ntemplate <typename TargetDataType>\nMatrix fornberg_interpolation_matrix(const TargetDataType& x_target,\n                                     const DataVector& x_source);\n\n/*!\n * \\brief Computes the weights for polynomial interpolation of a non-periodic\n * function known at the set of points \\f$x_{source}\\f$ to the point\n * \\f$x_{target}\\f$\n *\n * \\details The algorithm is from \\cite Fornberg1998. The DataVectors in the\n * array will be set to the size of \\f$x_{source}\\f$, where the first index\n * of the array holds the weights to interpolate a function from the grid\n * \\f$x_{source}\\f$ to \\f$x_{target}\\f$, the second index of the array holds\n * the weights to interpolate the first derivative of a function, and\n * so on.\n *\n * Calculating for the \\f$n\\f$-th derivative will yield all lower order\n * derivatives as well. If you just want the interpolation coefficients, use\n * `fornberg_interpolation_matrix`.\n *\n *\n * \\note The accuracy of the interpolation will depend upon the number and\n * distribution of the source points.  It is strongly suggested that you\n * carefully investigate the accuracy for your use case.\n */\ntemplate <size_t MaxOrder>\nvoid fornberg_derivative_interpolation_weights(\n    gsl::not_null<std::array<DataVector, MaxOrder + 1>*> result,\n    double x_target, const DataVector& x_source);\n\n/*!\n * \\brief Computes the matrix for interpolating a periodic function known at\n * the set of \\f$n\\f$ equally spaced points on the periodic domain \\f$[0, 2\n * \\pi]\\f$ to the set of points \\f$x_{target}\\f$\n *\n * \\details The returned matrix \\f$M\\f$ will have \\f$n_{target}\\f$ rows and\n * \\f$n_{source}\\f$ columns so that \\f$f_{target} = M f_{source}\\f$\n * Formally, this computes the sum\n * \\f[ n w_j = 1 + 2 \\sum_{k=1}^{\\lfloor (n-1)/2 \\rfloor} \\cos(k(x - X_j))\n *     \\left[ + \\cos\\left(\\frac{n}{2}(x - X_j)\\right) \\right] \\f]\n * for each target point \\f$x\\f$, where \\f$ X_j = \\frac{2 \\pi j}{n}\\f$, and\n * the term in brackets is evaluated only if \\f$n\\f$ is even.\n *\n */\ntemplate <typename TargetDataType>\nMatrix fourier_interpolation_matrix(const TargetDataType& x_target,\n                                    size_t n_source_points);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/InverseWeightFunctionValues.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\cond\nclass DataVector;\nnamespace Spectral {\nenum class Basis : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief Compute the inverse of the weight function \\f$w(x)\\f$ w.r.t. which\n * the basis functions are orthogonal. See the description of\n * `quadrature_weights(size_t)` for details.\n * This is arbitrarily set to 1 for FiniteDifference basis, to integrate\n * using the midpoint method (see `quadrature_weights (size_t)` for details).\n */\ntemplate <Basis>\nDataVector compute_inverse_weight_function_values(const DataVector&);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Legendre.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace Spectral {\n\n/*!\n * \\brief Evaluate a Legendre series from the modal coefficients at a given\n * logical point using Clenshaw recurrence.\n */\ntemplate <size_t Dim>\ndouble evaluate_legendre_series(\n    const ModalVector& coefficients, const Mesh<Dim>& mesh,\n    const tnsr::I<double, Dim, Frame::ElementLogical>& logical_coords);\n\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/LinearFilteringMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/LinearFilteringMatrix.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SpectralQuantityForMesh.hpp\"\n#include \"Utilities/Blas.hpp\"\n\nnamespace Spectral {\nnamespace {\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct LinearFilterMatrixGenerator {\n  Matrix operator()(const size_t num_points) const {\n    // We implement the expression\n    // \\f$\\mathcal{V}^{-1}\\cdot\\mathrm{diag}(1,1,0,0,...)\\cdot\\mathcal{V}\\f$\n    // (see description of `linear_filter_matrix`)\n    // which multiplies the first two columns of\n    // `nodal_to_modal_matrix` with the first two rows of\n    // `modal_to_nodal_matrix`.\n    Matrix lin_filter(num_points, num_points);\n    dgemm_(\n        'N', 'N', num_points, num_points, std::min(size_t{2}, num_points), 1.0,\n        modal_to_nodal_matrix<BasisType, QuadratureType>(num_points).data(),\n        modal_to_nodal_matrix<BasisType, QuadratureType>(num_points).spacing(),\n        nodal_to_modal_matrix<BasisType, QuadratureType>(num_points).data(),\n        nodal_to_modal_matrix<BasisType, QuadratureType>(num_points).spacing(),\n        0.0, lin_filter.data(), lin_filter.spacing());\n    return lin_filter;\n  }\n};\n}  // namespace\n\nPRECOMPUTED_SPECTRAL_QUANTITY(linear_filter_matrix, Matrix,\n                              LinearFilterMatrixGenerator)\n\n#undef PRECOMPUTED_SPECTRAL_QUANTITY\n\nSPECTRAL_QUANTITY_FOR_MESH(linear_filter_matrix, Matrix)\n\n#undef SPECTRAL_QUANTITY_FOR_MESH\n\ntemplate const Matrix&\n    linear_filter_matrix<Basis::Chebyshev, Quadrature::Gauss>(size_t);\ntemplate const Matrix&\n    linear_filter_matrix<Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix& linear_filter_matrix<Basis::Legendre, Quadrature::Gauss>(\n    size_t);\ntemplate const Matrix&\n    linear_filter_matrix<Basis::Legendre, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix&\nlinear_filter_matrix<Basis::Fourier, Quadrature::Equiangular>(size_t);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/LinearFilteringMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass Matrix;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief %Matrix used to linearize a function.\n *\n * \\details Filters out all except the lowest two modes by applying\n * \\f$\\mathcal{V}^{-1}\\cdot\\mathrm{diag}(1,1,0,0,...)\\cdot\\mathcal{V}\\f$ to the\n * nodal coefficients, where \\f$\\mathcal{V}\\f$ is the Vandermonde matrix\n * computed in `modal_to_nodal_matrix(size_t)`.\n *\n * \\param num_points The number of collocation points\n *\n * \\see modal_to_nodal_matrix(size_t)\n * \\see nodal_to_modal_matrix(size_t)\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& linear_filter_matrix(size_t num_points);\n\n/*!\n * \\brief Linear filter matrix for a one-dimensional mesh.\n *\n * \\see linear_filter_matrix(size_t)\n */\nconst Matrix& linear_filter_matrix(const Mesh<1>& mesh);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/LogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n\n#include <array>\n#include <numbers>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/Spherepack.hpp\"\n\ntemplate <size_t VolumeDim>\nvoid logical_coordinates(\n    const gsl::not_null<tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>*>\n        logical_coords,\n    const Mesh<VolumeDim>& mesh) {\n  set_number_of_grid_points(logical_coords, mesh.number_of_grid_points());\n\n  const auto I1 = [&logical_coords, &mesh](const size_t dim) {\n    const auto& collocation_points_in_this_dim =\n        Spectral::collocation_points(mesh.slice_through(dim));\n    for (IndexIterator<VolumeDim> index(mesh.extents()); index; ++index) {\n      logical_coords->get(dim)[index.collapsed_index()] =\n          collocation_points_in_this_dim[index()[dim]];\n    }\n  };\n  const auto S1 = [&logical_coords, &mesh](const size_t dim) {\n    const size_t n_phi = mesh.extents(dim);\n    const double two_pi_over_n_phi = 2.0 * std::numbers::pi / n_phi;\n    for (IndexIterator<VolumeDim> index(mesh.extents()); index; ++index) {\n      logical_coords->get(dim)[index.collapsed_index()] =\n          two_pi_over_n_phi * index()[dim];\n    }\n  };\n\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    switch (mesh.basis(d)) {\n      case Spectral::Basis::SphericalHarmonic: {\n        switch (mesh.quadrature(d)) {\n          case Spectral::Quadrature::Gauss: {\n            const size_t n_theta = mesh.extents(d);\n            std::vector<double> theta(n_theta + 1);\n            DataVector temp(2 * n_theta + 1);\n            auto work = gsl::make_span(temp.data(), n_theta);\n            auto unused_weights =\n                gsl::make_span(temp.data() + n_theta, n_theta + 1);\n\n            int err = 0;\n            gaqd_(static_cast<int>(n_theta), theta.data(),\n                  unused_weights.data(), work.data(),\n                  static_cast<int>(unused_weights.size()), &err);\n            if (UNLIKELY(err != 0)) {\n              ERROR(\"gaqd error \" << err << \" in LogicalCoordinates\");\n            }\n            for (IndexIterator<VolumeDim> index(mesh.extents()); index;\n                 ++index) {\n              logical_coords->get(d)[index.collapsed_index()] =\n                  theta[index()[d]];\n            }\n            break;\n          }\n          case Spectral::Quadrature::Equiangular: {\n            S1(d);\n            break;\n          }\n          default:\n            ERROR(\n                \"Quadrature must be Gauss or Equiangular for Basis \"\n                \"SphericalHarmonic\");\n        }\n        break;\n      }\n      case Spectral::Basis::Fourier: {\n        switch (mesh.quadrature(d)) {\n          case Spectral::Quadrature::Equiangular: {\n            S1(d);\n            break;\n          }\n          default:\n            ERROR(\"Quadrature must be Equiangular for Basis Fourier\");\n        }\n        break;\n      }\n      case Spectral::Basis::ZernikeB2: {\n        switch (mesh.quadrature(d)) {\n          case Spectral::Quadrature::GaussRadauUpper: {\n            I1(d);\n            break;\n          }\n          case Spectral::Quadrature::Equiangular: {\n            S1(d);\n            break;\n          }\n          default:\n            ERROR(\n                \"Quadrature must be GaussRadauUpper or Equiangular for Basis \"\n                \"ZernikeB2\");\n        }\n        break;\n      }\n      case Spectral::Basis::Cartoon: {\n        if (mesh.extents(d) != 1) {\n          ERROR(\"Only 1 grid point is allowed in a Cartoon basis.\");\n        }\n        for (IndexIterator<VolumeDim> index(mesh.extents()); index; ++index) {\n          logical_coords->get(d)[index.collapsed_index()] = 0.0;\n        }\n        break;\n      }\n      // NOLINTNEXTLINE(bugprone-branch-clone)\n      case Spectral::Basis::Chebyshev:\n        [[fallthrough]];\n      case Spectral::Basis::Legendre:\n        [[fallthrough]];\n      case Spectral::Basis::ZernikeB1:\n        [[fallthrough]];\n      case Spectral::Basis::ZernikeB3:\n        [[fallthrough]];\n      case Spectral::Basis::FiniteDifference: {\n        I1(d);\n        break;\n      }\n      default:\n        ERROR(\"Missing basis case for logical_coordinates\");\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\ntnsr::I<DataVector, VolumeDim, Frame::ElementLogical> logical_coordinates(\n    const Mesh<VolumeDim>& mesh) {\n  tnsr::I<DataVector, VolumeDim, Frame::ElementLogical> result{};\n  logical_coordinates(make_not_null(&result), mesh);\n  return result;\n}\n\n// Explicit instantiations\ntemplate tnsr::I<DataVector, 1, Frame::ElementLogical> logical_coordinates(\n    const Mesh<1>&);\ntemplate tnsr::I<DataVector, 2, Frame::ElementLogical> logical_coordinates(\n    const Mesh<2>&);\ntemplate tnsr::I<DataVector, 3, Frame::ElementLogical> logical_coordinates(\n    const Mesh<3>&);\ntemplate void logical_coordinates(\n    const gsl::not_null<tnsr::I<DataVector, 1, Frame::ElementLogical>*>,\n    const Mesh<1>&);\ntemplate void logical_coordinates(\n    const gsl::not_null<tnsr::I<DataVector, 2, Frame::ElementLogical>*>,\n    const Mesh<2>&);\ntemplate void logical_coordinates(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::ElementLogical>*>,\n    const Mesh<3>&);\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/LogicalCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions logical_coordinates and interface_logical_coordinates\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace Tags {\ntemplate<size_t Dim>\nstruct Mesh;\ntemplate <size_t, typename>\nstruct Coordinates;\n}  // namespace Tags\n}  // namespace domain\n\ntemplate <size_t Dim>\nclass Mesh;\nclass DataVector;\ntemplate <size_t Dim>\nclass Direction;\n\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// @{\n/*!\n * \\ingroup ComputationalDomainGroup\n * \\brief Compute the logical coordinates of a Mesh in an Element.\n *\n * \\details The logical coordinates are the collocation points\n * associated with the Spectral::Basis and Spectral::Quadrature of the \\p mesh.\n * The Spectral::Basis determines the domain of the logical coordinates, while\n * the Spectral::Quadrature determines their distribution.  For Legendre or\n * Chebyshev bases, the logical coordinates are in the interval \\f$[-1, 1]\\f$.\n * These bases may have either GaussLobatto or Gauss quadrature, which are not\n * uniformly distributed, and either include (GaussLobatto) or do not include\n * (Gauss) the end points.  For the FiniteDifference basis, the logical\n * coordinates are again in the interval \\f$[-1, 1]\\f$.  This basis may have\n * either FaceCentered or CellCentered quadrature, which are uniformly\n * distributed, and either include (FaceCentered) or do not include\n * (CellCentered) the end points. The Fourier basis has logical coordinates\n * in the interval \\f$[0, 2 \\pi]\\f$. The SphericalHarmonic basis corresponds to\n * the spherical coordinates \\f$(\\theta, \\phi)\\f$ where the polar angle\n * \\f$\\theta\\f$ is in the interval \\f$[0, \\pi]\\f$ and the azimuth \\f$\\phi\\f$ is\n * in the interval \\f$[0, 2 \\pi]\\f$.  The polar angle has Gauss quadrature\n * corresponding to the Legendre Gauss points of \\f$\\cos \\theta\\f$ (and thus\n * have no points at the poles), while the azimuth has Equiangular quadrature\n * which are distributed uniformly including the left endpoint, but not the\n * right. For the Cartoon basis, only one logical coordinate is allowed and it\n * is never used for computations. The Zernike bases have the radial basis\n * implemented such that the logical coordinates are \\f$[-1, 1]\\f$, while\n * an affine map is internally used to go to \\f$[0, 1]\\f$, where these bases are\n * defined. Being a scaled one-sided Jacobi polynomial, the gridpoint are\n * shifted to upper \\f$r\\f$, and GaussRadauUpper quadrature has been implemented\n * such that the lower edge does not have an end point but the upper does.\n *\n * \\example\n * \\snippet Test_LogicalCoordinates.cpp logical_coordinates_example\n */\ntemplate <size_t VolumeDim>\nvoid logical_coordinates(\n    gsl::not_null<tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>*>\n        logical_coords,\n    const Mesh<VolumeDim>& mesh);\n\ntemplate <size_t VolumeDim>\ntnsr::I<DataVector, VolumeDim, Frame::ElementLogical> logical_coordinates(\n    const Mesh<VolumeDim>& mesh);\n/// @}\n\nnamespace domain {\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ComputationalDomainGroup\n/// The logical coordinates in the Element\ntemplate <size_t VolumeDim>\nstruct LogicalCoordinates : Coordinates<VolumeDim, Frame::ElementLogical>,\n                            db::ComputeTag {\n  using base = Coordinates<VolumeDim, Frame::ElementLogical>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<Mesh<VolumeDim>>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<return_type*>, const ::Mesh<VolumeDim>&)>(\n      &logical_coordinates<VolumeDim>);\n};\n}  // namespace Tags\n}  // namespace domain\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n\nnamespace Spectral {\n/*!\n * \\brief Maximum number of allowed collocation points.\n *\n * \\details We choose a limit of 24 FD grid points because for DG-subcell the\n * number of points in an element is `2 * number_dg_points - 1` for cell\n * centered, and `2 * number_dg_points` for face-centered. Because there is no\n * way of generically retrieving the maximum number of grid points for a non-FD\n * basis, we need to hard-code both values here. If the number of grid points is\n * increased for the non-FD bases, it should also be increased for the FD basis.\n * Note that for good task-based parallelization 24 grid points is already a\n * fairly large number.\n */\ntemplate <Basis basis>\nconstexpr size_t maximum_number_of_points =\n    basis == Basis::Fourier            ? 81\n    : basis == Basis::FiniteDifference ? 40\n                                       : 20;\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Mesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\n#include <algorithm>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\nuint8_t combine(const Spectral::Basis basis,\n                const Spectral::Quadrature quadrature) {\n  return static_cast<uint8_t>(basis) bitor static_cast<uint8_t>(quadrature);\n}\n\nSpectral::Basis extract_basis(const uint8_t bits) {\n  return static_cast<Spectral::Basis>(0b11110000 bitand bits);\n}\n\nSpectral::Quadrature extract_quadrature(const uint8_t bits) {\n  return static_cast<Spectral::Quadrature>(0b00001111 bitand bits);\n}\n}  // namespace\n\ntemplate <size_t Dim>\nMesh<Dim>::Mesh(const size_t isotropic_extents, const Spectral::Basis basis,\n                const Spectral::Quadrature quadrature) {\n  static_assert(sizeof(Mesh<Dim>) == (Dim == 0 ? 2 : 2 * Dim));\n  if constexpr (Dim > 0) {\n    ASSERT(isotropic_extents <= 255, \"Cannot have more than 255 grid points\");\n    extents_[0] = isotropic_extents;\n    quadrature_and_basis_[0] = combine(basis, quadrature);\n    if constexpr (Dim > 1) {\n      extents_[1] = isotropic_extents;\n      quadrature_and_basis_[1] = combine(basis, quadrature);\n      if constexpr (Dim > 2) {\n        extents_[2] = isotropic_extents;\n        quadrature_and_basis_[2] = combine(basis, quadrature);\n      }\n    }\n  } else {\n    // Silence compiler warnings\n    (void)isotropic_extents;\n    (void)basis;\n    (void)quadrature;\n  }\n}\n\ntemplate <size_t Dim>\nMesh<Dim>::Mesh(const std::array<size_t, Dim>& extents,\n                const Spectral::Basis basis,\n                const Spectral::Quadrature quadrature) {\n  if constexpr (Dim > 0) {\n    ASSERT(\n        extents[0] <= 255,\n        \"Cannot have more than 255 grid points in direction 0: \" << extents[0]);\n    extents_[0] = extents[0];\n    quadrature_and_basis_[0] = combine(basis, quadrature);\n    if constexpr (Dim > 1) {\n      ASSERT(extents[1] <= 255,\n             \"Cannot have more than 255 grid points in direction 1: \"\n                 << extents[1]);\n      extents_[1] = extents[1];\n      quadrature_and_basis_[1] = combine(basis, quadrature);\n      if constexpr (Dim > 2) {\n        extents_[2] = extents[2];\n        ASSERT(extents[2] <= 255,\n               \"Cannot have more than 255 grid points in direction 2: \"\n                   << extents[2]);\n        quadrature_and_basis_[2] = combine(basis, quadrature);\n      }\n    }\n  } else {\n    // Silence compiler warnings\n    (void)extents;\n    (void)basis;\n    (void)quadrature;\n  }\n}\n\ntemplate <size_t Dim>\nMesh<Dim>::Mesh(const std::array<size_t, Dim>& extents,\n                const std::array<Spectral::Basis, Dim>& bases,\n                const std::array<Spectral::Quadrature, Dim>& quadratures) {\n  if constexpr (Dim > 0) {\n    ASSERT(\n        extents[0] <= 255,\n        \"Cannot have more than 255 grid points in direction 0: \" << extents[0]);\n    extents_[0] = extents[0];\n    quadrature_and_basis_[0] = combine(bases[0], quadratures[0]);\n    if constexpr (Dim > 1) {\n      ASSERT(extents[1] <= 255,\n             \"Cannot have more than 255 grid points in direction 1: \"\n                 << extents[1]);\n      extents_[1] = extents[1];\n      quadrature_and_basis_[1] = combine(bases[1], quadratures[1]);\n      if constexpr (Dim > 2) {\n        ASSERT(extents[2] <= 255,\n               \"Cannot have more than 255 grid points in direction 2: \"\n                   << extents[2]);\n        extents_[2] = extents[2];\n        quadrature_and_basis_[2] = combine(bases[2], quadratures[2]);\n      }\n    }\n  } else {\n    // Silence compiler warnings\n    (void)extents;\n    (void)bases;\n    (void)quadratures;\n  }\n}\n\ntemplate <size_t Dim>\nIndex<Dim> Mesh<Dim>::extents() const {\n  if constexpr (Dim == 0) {\n    return Index<Dim>{};\n  } else if constexpr (Dim == 1) {\n    return Index<Dim>{static_cast<size_t>(extents_[0])};\n  } else if constexpr (Dim == 2) {\n    return Index<Dim>{static_cast<size_t>(extents_[0]),\n                      static_cast<size_t>(extents_[1])};\n  } else {\n    return Index<Dim>{static_cast<size_t>(extents_[0]),\n                      static_cast<size_t>(extents_[1]),\n                      static_cast<size_t>(extents_[2])};\n  }\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif\ntemplate <size_t Dim>\nsize_t Mesh<Dim>::extents(const size_t d) const {\n  ASSERT(d < Dim,\n         \"Cannot get extent for dim \" << d << \" in a mesh of Dim \" << Dim);\n  return gsl::at(extents_, d);\n}\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n\ntemplate <size_t Dim>\nsize_t Mesh<Dim>::number_of_grid_points() const {\n  if constexpr (Dim == 0) {\n    return 1;\n  } else if constexpr (Dim == 1) {\n    return static_cast<size_t>(extents_[0]);\n  } else if constexpr (Dim == 2) {\n    // cast first so we don't overflow\n    return static_cast<size_t>(extents_[0]) * static_cast<size_t>(extents_[1]);\n  } else {\n    // cast first so we don't overflow\n    return static_cast<size_t>(extents_[0]) * static_cast<size_t>(extents_[1]) *\n           static_cast<size_t>(extents_[2]);\n  }\n}\n\ntemplate <size_t Dim>\nsize_t Mesh<Dim>::storage_index(const Index<Dim>& index) const {\n  return collapsed_index(index, extents());\n}\n\ntemplate <size_t Dim>\nstd::array<Spectral::Basis, Dim> Mesh<Dim>::basis() const {\n  if constexpr (Dim == 0) {\n    return {};\n  } else if constexpr (Dim == 1) {\n    return {extract_basis(quadrature_and_basis_[0])};\n  } else if constexpr (Dim == 2) {\n    return {extract_basis(quadrature_and_basis_[0]),\n            extract_basis(quadrature_and_basis_[1])};\n  } else {\n    return {extract_basis(quadrature_and_basis_[0]),\n            extract_basis(quadrature_and_basis_[1]),\n            extract_basis(quadrature_and_basis_[2])};\n  }\n}\n\ntemplate <size_t Dim>\nSpectral::Basis Mesh<Dim>::basis(const size_t d) const {\n  return extract_basis(gsl::at(quadrature_and_basis_, d));\n}\n\ntemplate <size_t Dim>\nstd::array<Spectral::Quadrature, Dim> Mesh<Dim>::quadrature() const {\n  if constexpr (Dim == 0) {\n    return {};\n  } else if constexpr (Dim == 1) {\n    return {extract_quadrature(quadrature_and_basis_[0])};\n  } else if constexpr (Dim == 2) {\n    return {extract_quadrature(quadrature_and_basis_[0]),\n            extract_quadrature(quadrature_and_basis_[1])};\n  } else {\n    return {extract_quadrature(quadrature_and_basis_[0]),\n            extract_quadrature(quadrature_and_basis_[1]),\n            extract_quadrature(quadrature_and_basis_[2])};\n  }\n}\n\ntemplate <size_t Dim>\nSpectral::Quadrature Mesh<Dim>::quadrature(const size_t d) const {\n  return extract_quadrature(gsl::at(quadrature_and_basis_, d));\n}\n\ntemplate <size_t Dim>\n// clang-tidy: incorrectly reported redundancy in template expression\ntemplate <size_t N, Requires<(N > 0 and N == Dim)>>  // NOLINT\nMesh<Dim - 1> Mesh<Dim>::slice_away(const size_t d) const {\n  ASSERT(d < Dim, \"Tried to slice away non-existing dimension \"\n                      << d << \" of \" << Dim << \"-dimensional mesh.\");\n  std::array<size_t, Dim - 1> dims{};\n  for (size_t i = 0; i < d; i++) {\n    gsl::at(dims, i) = i;\n  }\n  for (size_t i = d + 1; i < Dim; i++) {\n    gsl::at(dims, i - 1) = i;\n  }\n  return slice_through(dims);\n}\n\ntemplate <size_t Dim>\ntemplate <size_t SliceDim, Requires<(SliceDim <= Dim)>>\nMesh<SliceDim> Mesh<Dim>::slice_through(\n    const std::array<size_t, SliceDim>& dims) const {\n  // Check for duplicates in `dims`\n  ASSERT(\n      [&dims]() {\n        auto sorted_dims = dims;\n        std::sort(sorted_dims.begin(), sorted_dims.end());\n        auto last_unique = std::unique(sorted_dims.begin(), sorted_dims.end());\n        return last_unique == sorted_dims.end();\n      }(),\n      \"Dimensions to slice through contain duplicates.\");\n  Mesh<SliceDim> result{};\n  for (size_t i = 0; i < SliceDim; ++i) {\n    const auto& d = gsl::at(dims, i);\n    ASSERT(d < Dim, \"Tried to slice through non-existing dimension \"\n                        << d << \" of \" << Dim << \"-dimensional mesh.\");\n    gsl::at(result.extents_, i) = gsl::at(extents_, d);\n    gsl::at(result.quadrature_and_basis_, i) =\n        gsl::at(quadrature_and_basis_, d);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::array<Mesh<1>, Dim> Mesh<Dim>::slices() const {\n  std::array<Mesh<1>, Dim> result{};\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(result, d) = Mesh<1>(extents(d), basis(d), quadrature(d));\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid Mesh<Dim>::pup(PUP::er& p) {\n  p | extents_;\n  p | quadrature_and_basis_;\n}\n\ntemplate <size_t Dim>\nbool is_isotropic(const Mesh<Dim>& mesh) {\n  if constexpr (Dim == 0 or Dim == 1) {\n    return true;\n  } else {\n    return mesh.extents() == Index<Dim>(mesh.extents(0)) and\n           mesh.basis() == make_array<Dim>(mesh.basis(0)) and\n           mesh.quadrature() == make_array<Dim>(mesh.quadrature(0));\n  }\n}\n\ntemplate <size_t Dim>\nbool operator==(const Mesh<Dim>& lhs, const Mesh<Dim>& rhs) {\n  return lhs.extents_ == rhs.extents_ and\n         lhs.quadrature_and_basis_ == rhs.quadrature_and_basis_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Mesh<Dim>& lhs, const Mesh<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os, const Mesh<Dim>& mesh) {\n  using ::operator<<;\n  return os << '[' << mesh.extents() << ',' << mesh.basis() << ','\n            << mesh.quadrature() << ']';\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GEN_OP(op, dim) \\\n  template bool operator op(const Mesh<dim>& lhs, const Mesh<dim>& rhs);\n#define INSTANTIATE_MESH(_, data)                                 \\\n  template class Mesh<DIM(data)>;                                 \\\n  GEN_OP(==, DIM(data))                                           \\\n  GEN_OP(!=, DIM(data))                                           \\\n  template std::ostream& operator<<(std::ostream& os,             \\\n                                    const Mesh<DIM(data)>& mesh); \\\n  template bool is_isotropic(const Mesh<DIM(data)>& mesh);\n\n#define INSTANTIATE_SLICE_AWAY(_, data) \\\n  template Mesh<DIM(data) - 1> Mesh<DIM(data)>::slice_away(const size_t) const;\ntemplate Mesh<0> Mesh<0>::slice_through(const std::array<size_t, 0>&) const;\ntemplate Mesh<0> Mesh<1>::slice_through(const std::array<size_t, 0>&) const;\ntemplate Mesh<1> Mesh<1>::slice_through(const std::array<size_t, 1>&) const;\ntemplate Mesh<0> Mesh<2>::slice_through(const std::array<size_t, 0>&) const;\ntemplate Mesh<1> Mesh<2>::slice_through(const std::array<size_t, 1>&) const;\ntemplate Mesh<2> Mesh<2>::slice_through(const std::array<size_t, 2>&) const;\ntemplate Mesh<0> Mesh<3>::slice_through(const std::array<size_t, 0>&) const;\ntemplate Mesh<1> Mesh<3>::slice_through(const std::array<size_t, 1>&) const;\ntemplate Mesh<2> Mesh<3>::slice_through(const std::array<size_t, 2>&) const;\ntemplate Mesh<3> Mesh<3>::slice_through(const std::array<size_t, 3>&) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_MESH, (0, 1, 2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATE_SLICE_AWAY, (1, 2, 3))\n\n#undef DIM\n#undef GEN_OP\n#undef INSTANTIATE_MESH\n#undef INSTANTIATE_SLICE_AWAY\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Mesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the class template Mesh.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsInteger.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Holds the number of grid points, basis, and quadrature in each\n * direction of the computational grid.\n *\n * \\details A mesh encapsulates all information necessary to construct the\n * placement of grid points in the computational domain. It does so through a\n * choice of basis functions, quadrature and number of points \\f$N\\f$ in each\n * dimension. The grid points are the associated collocation points and can be\n * obtained from Spectral::collocation_points(const Mesh<1>&):\n *\n * \\snippet Test_Spectral.cpp get_points_for_mesh\n *\n * A simulated physical field can be represented by a DataVector of length\n * number_of_grid_points() that holds the field value on each point of\n * the computational grid. These values are identical to the field's nodal\n * expansion coefficients. They approximate the field by a polynomial of degree\n * \\f$p=N-1\\f$ through a linear combination of Lagrange polynomials.\n *\n * \\note Because we use a compact bit representation for the mesh, the number\n * of grid points/extents must be fewer than 256 per dimension.\n *\n * \\tparam Dim the number of dimensions of the computational grid.\n */\ntemplate <size_t Dim>\nclass Mesh {\n public:\n  static constexpr size_t dim = Dim;\n\n  struct Extents {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The number of collocation points per dimension\"};\n  };\n\n  struct Basis {\n    using type = Spectral::Basis;\n    static constexpr Options::String help = {\n        \"The choice of spectral basis to compute the collocation points\"};\n  };\n\n  struct Quadrature {\n    using type = Spectral::Quadrature;\n    static constexpr Options::String help = {\n        \"The choice of quadrature to compute the collocation points\"};\n  };\n\n  using options = tmpl::list<Extents, Basis, Quadrature>;\n\n  static constexpr Options::String help =\n      \"Holds the number of grid points, basis, and quadrature in each \"\n      \"direction of the computational grid. \"\n      \"A mesh encapsulates all information necessary to construct the \"\n      \"placement of grid points in the computational domain. It does so \"\n      \"through a choice of basis functions, quadrature and number of points \"\n      \"in each dimension.\";\n\n  Mesh() = default;\n\n  /*!\n   * \\brief Construct a computational grid with the same number of collocation\n   * points in each dimension.\n   *\n   * \\param isotropic_extents The number of collocation points in each\n   * dimension.\n   * \\param basis The choice of spectral basis to compute the\n   * collocation points\n   * \\param quadrature The choice of quadrature to compute\n   * the collocation points\n   *\n   * \\note Because a `Mesh<0>` extends over no dimensions, it has 1 grid point\n   * independent of the value of `isotropic_extents`, and the `basis` and\n   * `quadrature` are ignored.\n   */\n  Mesh(size_t isotropic_extents, Spectral::Basis basis,\n       Spectral::Quadrature quadrature);\n\n  /*!\n   * \\brief Construct a computational grid where each dimension can have a\n   * different number of collocation points.\n   *\n   * \\param extents The number of collocation points per dimension\n   * \\param basis The choice of spectral basis to compute the\n   * collocation points\n   * \\param quadrature The choice of quadrature to compute\n   * the collocation points\n   *\n   * \\note A `Mesh<0>` extends over no dimensions so the `basis` and\n   * `quadrature` are ignored.\n   */\n  Mesh(const std::array<size_t, Dim>& extents, Spectral::Basis basis,\n       Spectral::Quadrature quadrature);\n\n  /*!\n   * \\brief Construct a computational grid where each dimension can have both a\n   * different number and placement of collocation points.\n   *\n   * \\param extents The number of collocation points per dimension\n   * \\param bases The choice of spectral bases to compute the\n   * collocation points per dimension\n   * \\param quadratures The choice of quadratures to compute\n   * the collocation points per dimension\n   */\n  Mesh(const std::array<size_t, Dim>& extents,\n       const std::array<Spectral::Basis, Dim>& bases,\n       const std::array<Spectral::Quadrature, Dim>& quadratures);\n\n  /*!\n   * \\brief The number of grid points in each dimension of the grid.\n   */\n  Index<Dim> extents() const;\n\n  /*!\n   * \\brief The number of grid points in dimension \\p d of the grid\n   * (zero-indexed).\n   */\n  size_t extents(size_t d) const;\n\n  /*!\n   * \\brief The total number of grid points in all dimensions.\n   *\n   * \\details `DataVector`s that represent field values on the grid have this\n   * many entries.\n   *\n   * \\note A zero-dimensional mesh has one grid point, since it is the slice\n   * through a one-dimensional mesh (a line).\n   */\n  size_t number_of_grid_points() const;\n\n  /*!\n   * \\brief Returns the 1-dimensional index corresponding to the `Dim`\n   * dimensional `index`.\n   *\n   * The first dimension varies fastest.\n   *\n   * \\see collapsed_index()\n   */\n  size_t storage_index(const Index<Dim>& index) const;\n\n  /*!\n   * \\brief The basis chosen in each dimension of the grid.\n   */\n  std::array<Spectral::Basis, Dim> basis() const;\n\n  /*!\n   * \\brief The basis chosen in dimension \\p d of the grid (zero-indexed).\n   */\n  Spectral::Basis basis(size_t d) const;\n\n  /*!\n   * \\brief The quadrature chosen in each dimension of the grid.\n   */\n  std::array<Spectral::Quadrature, Dim> quadrature() const;\n\n  /*!\n   * \\brief The quadrature chosen in dimension \\p d of the grid (zero-indexed).\n   */\n  Spectral::Quadrature quadrature(size_t d) const;\n\n  /*!\n   * \\brief Returns a Mesh with dimension \\p d removed (zero-indexed).\n   *\n   * \\see slice_through()\n   */\n  // clang-tidy: incorrectly reported redundancy in template expression\n  template <size_t N = Dim, Requires<(N > 0 and N == Dim)> = nullptr>  // NOLINT\n  Mesh<Dim - 1> slice_away(size_t d) const;\n\n  /*!\n   * \\brief Returns a Mesh with the dimensions \\p d, ... present (zero-indexed).\n   *\n   * \\details Generally you use this method to obtain a lower-dimensional Mesh\n   * by slicing through a subset of the dimensions. However, you can also\n   * reorder dimensions using this method by slicing through the dimensions in\n   * an order you choose.\n   *\n   * \\see slice_away()\n   */\n  template <typename... D, Requires<(sizeof...(D) <= Dim)> = nullptr>\n  Mesh<sizeof...(D)> slice_through(D... d) const {\n    static_assert(std::conjunction_v<tt::is_integer<D>...>,\n                  \"The dimensions must be integers.\");\n    const std::array<size_t, sizeof...(D)> dims{{static_cast<size_t>(d)...}};\n    return slice_through(dims);\n  }\n\n  /*!\n   * \\brief Returns a Mesh with the dimensions \\p dims present (zero-indexed).\n   *\n   * \\see slice_through() The templated overload of this function\n   */\n  template <size_t SliceDim, Requires<(SliceDim <= Dim)> = nullptr>\n  Mesh<SliceDim> slice_through(const std::array<size_t, SliceDim>& dims) const;\n\n  /*!\n   * \\brief Returns the Meshes representing 1D slices of this Mesh.\n   *\n   * The is the same as the array filled with `slice_through(d)` for\n   * `d` from `0` to `Dim - 1` except in dimension 0 where\n   * `slice_through(d)` is not defined.\n   */\n  std::array<Mesh<1>, Dim> slices() const;\n\n  // clang-tidy: runtime-references\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <size_t LocalDim>\n  friend class Mesh;\n\n  template <size_t LocalDim>\n  friend bool operator==(const Mesh<LocalDim>& lhs, const Mesh<LocalDim>& rhs);\n\n  // - 8 bits per extent = 3 * 8 = 24 bits = 3 bytes\n  //   This limits us to 255 extent per direction. Since FD is 2N-1 DG, this\n  //   means at most 128 DG points, which is should be fine.\n  // - We encode the basis & quadrature as two sets of 4 bits in a uint8_t.\n  std::array<uint8_t, (Dim > 0 ? Dim : 1)> extents_{};\n  std::array<uint8_t, (Dim > 0 ? Dim : 1)> quadrature_and_basis_{};\n};\n\n/*!\n * \\ingroup DataStructuresGroup\n * \\brief Returns `true` if the mesh is isotropic, `false` otherwise.\n *\n * If `Dim` is zero, then `true` is always returned.\n */\ntemplate <size_t Dim>\nbool is_isotropic(const Mesh<Dim>& mesh);\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <size_t Dim>\nbool operator==(const Mesh<Dim>& lhs, const Mesh<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const Mesh<Dim>& lhs, const Mesh<Dim>& rhs);\n\ntemplate <size_t Dim>\nstd::ostream& operator<<(std::ostream& os, const Mesh<Dim>& mesh);\n/// \\endcond\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace detail {\nconstexpr size_t minimum_number_of_points(const Basis /*basis*/,\n                                          const Quadrature quadrature) {\n  // NOLINTNEXTLINE(bugprone-branch-clone)\n  if (quadrature == Quadrature::Gauss) {\n    return 1;\n    // NOLINTNEXTLINE(bugprone-branch-clone)\n  } else if (quadrature == Quadrature::GaussLobatto) {\n    return 2;\n    // NOLINTNEXTLINE(bugprone-branch-clone)\n  } else if (quadrature == Quadrature::GaussRadauUpper) {\n    return 1;\n    // NOLINTNEXTLINE(bugprone-branch-clone)\n  } else if (quadrature == Quadrature::CellCentered) {\n    return 1;\n    // NOLINTNEXTLINE(bugprone-branch-clone)\n  } else if (quadrature == Quadrature::FaceCentered) {\n    return 2;\n    // NOLINTNEXTLINE(bugprone-branch-clone)\n  } else if (quadrature == Quadrature::Equiangular) {\n    return 1;\n    // NOLINTNEXTLINE(bugprone-branch-clone)\n  } else if (quadrature == Quadrature::AxialSymmetry) {\n    return 1;\n  } else if (quadrature == Quadrature::SphericalSymmetry) {\n    return 1;\n  }\n  return std::numeric_limits<size_t>::max();\n}\n}  // namespace detail\n\n/*!\n * \\brief Minimum number of possible collocation points for a quadrature type.\n *\n * \\details Since Gauss-Lobatto quadrature has points on the domain boundaries\n * it must have at least two collocation points. Gauss quadrature can have only\n * one collocation point.\n *\n * \\details For `CellCentered` the minimum number of points is 1, while for\n * `FaceCentered` it is 2.\n */\ntemplate <Basis basis, Quadrature quadrature>\nconstexpr size_t minimum_number_of_points =\n    detail::minimum_number_of_points(basis, quadrature);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/ModalToNodalMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SpectralQuantityForMesh.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct ModalToNodalMatrixGenerator {\n  Matrix operator()(const size_t num_points) const {\n    ASSERT(BasisType != Basis::ZernikeB1 and BasisType != Basis::ZernikeB2 and\n               BasisType != Basis::ZernikeB3,\n           \"Zernike basis should not call one-indexed ModalToNodal matrix\");\n    // To obtain the Vandermonde matrix we need to compute the basis function\n    // values at the collocation points. Constructing the matrix proceeds\n    // the same for any basis.\n    const DataVector& collocation_pts =\n        collocation_points<BasisType, QuadratureType>(num_points);\n    Matrix vandermonde_matrix(num_points, num_points);\n    for (size_t j = 0; j < num_points; j++) {\n      const auto& basis_function_values =\n          compute_basis_function_value<BasisType>(j, collocation_pts);\n      for (size_t i = 0; i < num_points; i++) {\n        vandermonde_matrix(i, j) = basis_function_values[i];\n      }\n    }\n    return vandermonde_matrix;\n  }\n};\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct TwoIndexedModalToNodalMatrixGenerator {\n  Matrix operator()(const size_t num_points, const size_t m,\n                    const size_t N) const {\n    // To obtain the Vandermonde matrix for Zernike bases, we need to compute\n    // the basis function values at the collocation points for a given angular\n    // index m up to a given maximum index N.\n    static_assert(BasisType == Basis::ZernikeB1 or\n                  BasisType == Basis::ZernikeB2 or\n                  BasisType == Basis::ZernikeB3);\n    ASSERT(N < 2 * num_points - 1,\n           \"N must be less than 2 * num_points - 1, got N = \"\n               << N << \" for num_points = \" << num_points);\n    ASSERT(m <= N, \"m must be less than or equal to N, got m = \"\n                       << m << \" and N = \" << N);\n    const auto& collocation_pts =\n        collocation_points<BasisType, QuadratureType>(num_points);\n    // note the integer division\n    const size_t spectral_size = (N - m) / 2 + 1;\n    Matrix vandermonde_matrix(num_points, spectral_size);\n    for (size_t j = m, k = 0; j <= N; ++k, j += 2) {\n      const auto& basis_function_values =\n          compute_basis_function_value<BasisType>(j, m, collocation_pts);\n      for (size_t i = 0; i < num_points; ++i) {\n        vandermonde_matrix(i, k) = basis_function_values[i];\n      }\n    }\n    return vandermonde_matrix;\n  }\n};\n}  // namespace\n\nPRECOMPUTED_SPECTRAL_QUANTITY(modal_to_nodal_matrix, Matrix,\n                              ModalToNodalMatrixGenerator)\n\n#undef PRECOMPUTED_SPECTRAL_QUANTITY\n\nSPECTRAL_QUANTITY_FOR_MESH(modal_to_nodal_matrix, Matrix)\n\n#undef SPECTRAL_QUANTITY_FOR_MESH\n\nPRECOMPUTED_TWO_INDEXED_SPECTRAL_QUANTITY(modal_to_nodal_matrix, Matrix,\n                                          TwoIndexedModalToNodalMatrixGenerator)\n\n#undef PRECOMPUTED_TWO_INDEXED_SPECTRAL_QUANTITY\n\nTWO_INDEXED_SPECTRAL_QUANTITY_FOR_MESH(modal_to_nodal_matrix, Matrix)\n\n#undef TWO_INDEXED_SPECTRAL_QUANTITY_FOR_MESH\n\ntemplate const Matrix&\n    modal_to_nodal_matrix<Basis::Cartoon, Quadrature::AxialSymmetry>(size_t);\ntemplate const Matrix& modal_to_nodal_matrix<\n    Basis::Cartoon, Quadrature::SphericalSymmetry>(size_t);\ntemplate const Matrix&\n    modal_to_nodal_matrix<Basis::Chebyshev, Quadrature::Gauss>(size_t);\ntemplate const Matrix&\n    modal_to_nodal_matrix<Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix&\n    modal_to_nodal_matrix<Basis::Fourier, Quadrature::Equiangular>(size_t);\ntemplate const Matrix&\n    modal_to_nodal_matrix<Basis::Legendre, Quadrature::Gauss>(size_t);\ntemplate const Matrix&\n    modal_to_nodal_matrix<Basis::Legendre, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix& modal_to_nodal_matrix<\n    Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t, size_t, size_t);\ntemplate const Matrix& modal_to_nodal_matrix<\n    Basis::ZernikeB2, Quadrature::GaussRadauUpper>(size_t, size_t, size_t);\ntemplate const Matrix& modal_to_nodal_matrix<\n    Basis::ZernikeB3, Quadrature::GaussRadauUpper>(size_t, size_t, size_t);\n// Some compilers require these instantiations to exist\ntemplate const Matrix& modal_to_nodal_matrix<Basis::FiniteDifference,\n                                             Quadrature::CellCentered>(size_t);\ntemplate const Matrix& modal_to_nodal_matrix<Basis::FiniteDifference,\n                                             Quadrature::FaceCentered>(size_t);\ntemplate const Matrix& modal_to_nodal_matrix<\n    Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t);\ntemplate const Matrix& modal_to_nodal_matrix<\n    Basis::ZernikeB2, Quadrature::GaussRadauUpper>(size_t);\ntemplate const Matrix& modal_to_nodal_matrix<\n    Basis::ZernikeB3, Quadrature::GaussRadauUpper>(size_t);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass Matrix;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief %Matrix used to transform from the spectral coefficients (modes) of a\n * function to its nodal coefficients. Also referred to as the _Vandermonde\n * matrix_.\n *\n * \\details The Vandermonde matrix is computed as\n * \\f$\\mathcal{V}_{ij}=\\Phi_j(\\xi_i)\\f$ where the \\f$\\Phi_j(x)\\f$ are the\n * spectral basis functions used in the modal expansion\n * \\f$u(x)=\\sum_j \\widetilde{u}_j\\Phi_j(x)\\f$, e.g. normalized Legendre\n * polynomials, and the \\f$\\xi_i\\f$ are the collocation points used to construct\n * the interpolating Lagrange polynomials in the nodal expansion\n * \\f$u(x)=\\sum_j u_j l_j(x)\\f$. Then the Vandermonde matrix arises as\n * \\f$u(\\xi_i)=u_i=\\sum_j \\widetilde{u}_j\\Phi_j(\\xi_i)=\\sum_j\n * \\mathcal{V}_{ij}\\widetilde{u}_j\\f$.\n *\n * \\param num_points The number of collocation points\n\n * \\see nodal_to_modal_matrix(size_t)\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& modal_to_nodal_matrix(size_t num_points);\n\n/*!\n * \\brief %Matrix used to transform from the spectral coefficients (modes) of a\n * function to its nodal coefficients. This two-index version is used for\n * two-dimensional basis function (i.e. Zernike with GaussRadauUpper\n * quadrature).\n *\n * For Zernike, \\f$m\\f$ is the angular index and \\f$N\\f$ is the\n * maximum supported spectral index (usually taken to be the maximum value,\n * \\f$2 \\, \\texttt{num_points}-2\\f$). Note that the size of a spectral space\n * vector is \\f$(N - m) / 2 + 1\\f$, using integer division.\n *\n * \\see modal_to_nodal_matrix(size_t)\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& modal_to_nodal_matrix(size_t num_points, size_t m, size_t N);\n\n/*!\n * \\brief Transformation matrix from modal to nodal coefficients for a\n * one-dimensional mesh.\n *\n * \\see modal_to_nodal_matrix(size_t)\n */\nconst Matrix& modal_to_nodal_matrix(const Mesh<1>& mesh);\n\n/*!\n * \\brief Transformation matrix from modal to nodal coefficients for a\n * one-dimensional mesh with a Zernike basis.\n *\n * \\see modal_to_nodal_matrix(size_t, size_t, size_t)\n */\nconst Matrix& modal_to_nodal_matrix(const Mesh<1>& mesh, size_t m, size_t N);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Namespace.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup SpectralGroup\n/// Functionality associated with a particular choice of basis functions and\n/// quadrature for spectral operations\nnamespace Spectral {}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/NodalToModalMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SpectralQuantityForMesh.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct NodalToModalMatrixGenerator {\n  Matrix operator()(const size_t num_points) const {\n    // Numerically invert the matrix for this generic case\n    return inv(modal_to_nodal_matrix<BasisType, QuadratureType>(num_points));\n  }\n};\n\ntemplate <Basis BasisType>\nstruct NodalToModalMatrixGenerator<BasisType, Quadrature::Gauss> {\n  using data_type = Matrix;\n  Matrix operator()(const size_t num_points) const {\n    // For Gauss quadrature we implement the analytic expression\n    // \\f$\\mathcal{V}^{-1}_{ij}=\\mathcal{V}_{ji}\\frac{w_j}{\\gamma_i}\\f$\n    // (see description of `nodal_to_modal_matrix`).\n    const DataVector& weights =\n        detail::precomputed_spectral_quantity<\n            BasisType, Quadrature::Gauss,\n            detail::CollocationPointsAndWeightsGenerator<BasisType,\n                                                         Quadrature::Gauss>>(\n            num_points)\n            .second;\n    const Matrix& vandermonde_matrix =\n        modal_to_nodal_matrix<BasisType, Quadrature::Gauss>(num_points);\n    Matrix vandermonde_inverse(num_points, num_points);\n    // This should be vectorized when the functionality is implemented.\n    for (size_t i = 0; i < num_points; i++) {\n      for (size_t j = 0; j < num_points; j++) {\n        vandermonde_inverse(i, j) =\n            vandermonde_matrix(j, i) * weights[j] /\n            compute_basis_function_normalization_square<BasisType>(i);\n      }\n    }\n    return vandermonde_inverse;\n  }\n};\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct TwoIndexedNodalToModalMatrixGenerator {\n  Matrix operator()(const size_t num_points, const size_t m,\n                    const size_t N) const {\n    // To obtain the Vandermonde matrix for Zernike bases, we need to compute\n    // the basis function values at the collocation points for a given angular\n    // index m up to a given maximum index N.\n    static_assert(BasisType == Basis::ZernikeB1 or\n                  BasisType == Basis::ZernikeB2 or\n                  BasisType == Basis::ZernikeB3);\n    ASSERT(N < 2 * num_points - 1,\n           \"N must be less than 2 * num_points - 1, got N = \"\n               << N << \" for num_points = \" << num_points);\n    ASSERT(m <= N, \"m must be less than or equal to N, got m = \"\n                       << m << \" and N = \" << N);\n    // note the integer division\n    const size_t spectral_size = (N - m) / 2 + 1;\n    const auto& [collocation_pts, weights] =\n        compute_collocation_points_and_weights<BasisType, QuadratureType>(\n            num_points);\n    Matrix inv_matrix(spectral_size, num_points);\n    for (size_t j = m, k = 0; j <= N; ++k, j += 2) {\n      const auto& basis_function_values =\n          compute_basis_function_value<BasisType>(j, m, collocation_pts);\n      for (size_t i = 0; i < num_points; ++i) {\n        inv_matrix(k, i) = basis_function_values[i] * weights[i];\n      }\n    }\n    return inv_matrix;\n  }\n};\n}  // namespace\n\nPRECOMPUTED_SPECTRAL_QUANTITY(nodal_to_modal_matrix, Matrix,\n                              NodalToModalMatrixGenerator)\n\n#undef PRECOMPUTED_SPECTRAL_QUANTITY\n\nSPECTRAL_QUANTITY_FOR_MESH(nodal_to_modal_matrix, Matrix)\n\n#undef SPECTRAL_QUANTITY_FOR_MESH\n\nPRECOMPUTED_TWO_INDEXED_SPECTRAL_QUANTITY(nodal_to_modal_matrix, Matrix,\n                                          TwoIndexedNodalToModalMatrixGenerator)\n\n#undef PRECOMPUTED_TWO_INDEXED_SPECTRAL_QUANTITY\n\nTWO_INDEXED_SPECTRAL_QUANTITY_FOR_MESH(nodal_to_modal_matrix, Matrix)\n\n#undef TWO_INDEXED_SPECTRAL_QUANTITY_FOR_MESH\n\ntemplate const Matrix&\n    nodal_to_modal_matrix<Basis::Cartoon, Quadrature::AxialSymmetry>(size_t);\ntemplate const Matrix& nodal_to_modal_matrix<\n    Basis::Cartoon, Quadrature::SphericalSymmetry>(size_t);\ntemplate const Matrix&\n    nodal_to_modal_matrix<Basis::Chebyshev, Quadrature::Gauss>(size_t);\ntemplate const Matrix&\n    nodal_to_modal_matrix<Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix&\n    nodal_to_modal_matrix<Basis::Fourier, Quadrature::Equiangular>(size_t);\ntemplate const Matrix&\n    nodal_to_modal_matrix<Basis::Legendre, Quadrature::Gauss>(size_t);\ntemplate const Matrix&\n    nodal_to_modal_matrix<Basis::Legendre, Quadrature::GaussLobatto>(size_t);\ntemplate const Matrix& nodal_to_modal_matrix<\n    Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t, size_t, size_t);\ntemplate const Matrix& nodal_to_modal_matrix<\n    Basis::ZernikeB2, Quadrature::GaussRadauUpper>(size_t, size_t, size_t);\ntemplate const Matrix& nodal_to_modal_matrix<\n    Basis::ZernikeB3, Quadrature::GaussRadauUpper>(size_t, size_t, size_t);\n// Some compilers require these instantiations to exist\ntemplate const Matrix& nodal_to_modal_matrix<Basis::FiniteDifference,\n                                             Quadrature::CellCentered>(size_t);\ntemplate const Matrix& nodal_to_modal_matrix<Basis::FiniteDifference,\n                                             Quadrature::FaceCentered>(size_t);\ntemplate const Matrix& nodal_to_modal_matrix<\n    Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t);\ntemplate const Matrix& nodal_to_modal_matrix<\n    Basis::ZernikeB2, Quadrature::GaussRadauUpper>(size_t);\ntemplate const Matrix& nodal_to_modal_matrix<\n    Basis::ZernikeB3, Quadrature::GaussRadauUpper>(size_t);\n\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass Matrix;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief %Matrix used to transform from the nodal coefficients of a function to\n * its spectral coefficients (modes). Also referred to as the inverse\n * _Vandermonde matrix_.\n *\n * \\details This is the inverse to the Vandermonde matrix \\f$\\mathcal{V}\\f$\n * computed in modal_to_nodal_matrix(size_t). It can be computed\n * analytically for Gauss quadrature by evaluating\n * \\f$\\sum_j\\mathcal{V}^{-1}_{ij}u_j=\\widetilde{u}_i=\n * \\frac{(u,\\Phi_i)}{\\gamma_i}\\f$\n * for a Lagrange basis function \\f$u(x)=l_k(x)\\f$ to find\n * \\f$\\mathcal{V}^{-1}_{ij}=\\mathcal{V}_{ji}\\frac{w_j}{\\gamma_i}\\f$ where the\n * \\f$w_j\\f$ are the Gauss quadrature weights and \\f$\\gamma_i\\f$ is the norm\n * square of the spectral basis function \\f$\\Phi_i\\f$.\n *\n * \\param num_points The number of collocation points\n *\n * \\see modal_to_nodal_matrix(size_t)\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& nodal_to_modal_matrix(size_t num_points);\n\n/*!\n * \\brief %Matrix used to transform from the nodal coefficients of a function to\n * its spectral coefficients (modes). This two-index version is used for\n * two-dimensional basis function (i.e. Zernike with GaussRadauUpper\n * quadrature).\n *\n * For Zernike, \\f$m\\f$ is the angular index and \\f$N\\f$ is the\n * maximum supported spectral index (usually taken to be the maximum value,\n * \\f$2 \\, \\texttt{num_points}-2\\f$). Note that the size of a spectral space\n * vector is \\f$(N - m) / 2 + 1\\f$, using integer division.\n *\n * \\see nodal_to_modal_matrix(size_t)\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst Matrix& nodal_to_modal_matrix(size_t num_points, size_t m, size_t N);\n\n/*!\n * \\brief Transformation matrix from nodal to modal coefficients for a\n * one-dimensional mesh.\n *\n * \\see nodal_to_modal_matrix(size_t)\n */\nconst Matrix& nodal_to_modal_matrix(const Mesh<1>& mesh);\n\n/*!\n * \\brief Transformation matrix from nodal to modal coefficients for a\n * one-dimensional mesh with a Zernike basis.\n *\n * \\see nodal_to_modal_matrix(size_t, size_t, size_t)\n */\nconst Matrix& nodal_to_modal_matrix(const Mesh<1>& mesh, size_t m, size_t N);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Parity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n\n#include <array>\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Spectral {\n\nstd::array<Parity, 3> all_parities() {\n  return std::array{Parity::Uninitialized, Parity::Even, Parity::Odd};\n}\n\nstd::ostream& operator<<(std::ostream& os, const Parity& parity) {\n  switch (parity) {\n    case Parity::Even:\n      return os << \"Even\";\n    case Parity::Odd:\n      return os << \"Odd\";\n    case Parity::Uninitialized:\n      return os << \"Uninitialized\";\n    default:\n      ERROR(\"Unknown value of Parity trying to be streamed\");\n  }\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Parity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <ostream>\n\nnamespace Spectral {\n/*!\n * \\brief Used to label parity, either Even or Odd.\n */\nenum class Parity : std::uint8_t { Uninitialized, Even, Odd };\n\n/// All possible values of Parity\nstd::array<Parity, 3> all_parities();\n\n// Output operator for a Basis\nstd::ostream& operator<<(std::ostream& os, const Parity& parity);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/ParityFromSymmetry.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace Spectral {\n/*!\n * \\brief A compile-time function to determine parity, with respect to the\n * \\f$x\\f$ coordinate in an axisymmetric spacetime, of tensors in a Variables\n *\n * \\details In a \\f$d\\f$-dimensional space where fields are regular everywhere\n * (particularly at the axis), we utilize the fact that smoothness corresponds\n * to a well-defined Taylor expansion in all \\f$d\\f$ coordinates. Viewing the\n * components of a rank \\f$(p, q)\\f$ tensor as smooth functions of \\f$(x, y,\n * \\ldots, z)\\f$, consider a reflection transformation \\f$x \\to -x\\f$. As a\n * coordinate transformation, the Jacobian will look like \\f$\\mathrm{diag}(-1,\n * 1, 1, \\ldots)\\f$.\n *\n * If any of the \\f$(p, q)\\f$ indices correspond to the \\f$x\\f$-coordinate for\n * this particular component, there is a \\f$-1\\f$ factor associated with each\n * \\f$x\\f$-index, i.e., the Jacobian will look like \\f$(-1)^{n_x}\\f$, where\n * \\f$n_x\\f$ is the number of \\f$x\\f$-indices of the component.\n *\n * If this space is symmetric under this reflection, as it is for axisymmetry\n * about the \\f$y\\f$-axis in 3 dimensions, components can only represent\n * functions invariant under this transformation.\n *\n * This means for an even number of \\f$x\\f$-indices, the Taylor expansion must\n * only have even powers of \\f$x\\f$, and for an odd number of \\f$x\\f$-indices,\n * the expansion must have purely odd powers of \\f$x\\f$.\n *\n * The primary use-case for this is axisymmetric Cartoon simulations, where\n * elements touching the symmetry axis require ZernikeB1 bases for numerical\n * stability. These bases require knowledge of component parity for certain\n * operations (differentiation, interpolation, etc.).\n *\n * The returned array, alternating values for even/odd, stores the number of\n * next components with the same parity. When there are any neighboring parities\n * (i.e. not the worst-case scenario) the array will be padded with zeros to\n * be the correct size. The first index may be 0 if the first component is\n * odd, but any following zeros are guaranteed to only have zeros following it.\n * The first returned `size_t` is the number of even components, while the\n * second is the number of odd components.\n */\ntemplate <typename VariablesTags>\nconstexpr std::tuple<\n    std::array<size_t,\n               Variables<VariablesTags>::number_of_independent_components + 1>,\n    size_t, size_t>\ncompute_parity_list() {\n  const size_t N =\n      Variables<VariablesTags>::number_of_independent_components + 1;\n  std::array<size_t, N> parity_run_lengths{};\n\n  const auto is_x_coordinate_index = [](const IndexType index_type,\n                                        const size_t index_value) {\n    return (index_type == IndexType::Spacetime and index_value == 1) or\n           (index_type == IndexType::Spatial and index_value == 0);\n  };\n\n  size_t run_index = 0;\n  bool current_parity_is_even = true;\n  tmpl::for_each<VariablesTags>([&parity_run_lengths, &run_index,\n                                 &current_parity_is_even,\n                                 &is_x_coordinate_index]<typename TensorTag>(\n                                    tmpl::type_<TensorTag> /*meta*/) {\n    using tensor_type = typename TensorTag::type;\n    constexpr auto index_types = tensor_type::index_types();\n    constexpr size_t tensor_size = tensor_type::size();\n\n    for (size_t component_index = 0; component_index < tensor_size;\n         ++component_index) {\n      const auto tensor_index = tensor_type::get_tensor_index(component_index);\n      size_t x_coordinate_count = 0;\n      for (size_t index_position = 0; index_position < index_types.size();\n           ++index_position) {\n        if (is_x_coordinate_index(gsl::at(index_types, index_position),\n                                  gsl::at(tensor_index, index_position))) {\n          ++x_coordinate_count;\n        }\n      }\n      // If current parity doesn't match last\n      const bool component_is_even = (x_coordinate_count % 2 == 0);\n      if (component_is_even != current_parity_is_even) {\n        ++run_index;\n        current_parity_is_even = !current_parity_is_even;\n      }\n      gsl::at(parity_run_lengths, run_index) += 1;\n    }\n  });\n\n  const auto [num_even, num_odd] = [&parity_run_lengths]<size_t... Is>(\n                                       std::index_sequence<Is...> /*meta*/) {\n    std::size_t even_count = ((Is % 2 == 0 ? parity_run_lengths[Is] : 0) + ...);\n    std::size_t odd_count = ((Is % 2 == 1 ? parity_run_lengths[Is] : 0) + ...);\n    return std::pair{even_count, odd_count};\n  }(std::make_index_sequence<N>{});\n\n  return {parity_run_lengths, num_even, num_odd};\n}\n\n/*!\n * \\brief A compile-time function to determine parity, with respect to the\n * \\f$x\\f$ coordinate in an axisymmetric spacetime, of a tensor.\n *\n * \\see `compute_parity_list(Variables)`\n */\ntemplate <typename TensorType>\n  requires(tt::is_a_v<Tensor, TensorType>)\nconstexpr std::tuple<std::array<size_t, TensorType::size() + 1>, size_t, size_t>\ncompute_parity_list() {\n  using tensor_type = Tensor<DataVector, typename TensorType::symmetry,\n                             typename TensorType::index_list>;\n  using vars_list = ::Tags::convert_to_temp_tensors<tmpl::list<tensor_type>, 0>;\n  return compute_parity_list<vars_list>();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\n/// \\cond\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral::detail {\ntemplate <Basis BasisType, Quadrature QuadratureType,\n          typename SpectralQuantityGenerator>\nconst auto& precomputed_spectral_quantity(const size_t num_points) {\n  constexpr size_t max_num_points =\n      Spectral::maximum_number_of_points<BasisType>;\n  constexpr size_t min_num_points =\n      Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n  ASSERT(num_points >= min_num_points,\n         \"Tried to work with less than the minimum number of collocation \"\n         \"points for this quadrature.\");\n  ASSERT(num_points <= max_num_points,\n         \"Exceeded maximum number of collocation points.\");\n  // We compute the quantity for all possible `num_point`s the first time this\n  // function is called and keep the data around for the lifetime of the\n  // program. The computation is handled by the call operator of the\n  // `SpectralQuantityType` instance.\n  static const auto precomputed_data =\n      make_static_cache<CacheRange<min_num_points, max_num_points + 1>>(\n          SpectralQuantityGenerator{});\n  return precomputed_data(num_points);\n}\n\ntemplate <Basis BasisType, Quadrature QuadratureType,\n          typename SpectralQuantityGenerator>\nconst auto& precomputed_spectral_quantity_with_parity(const size_t num_points,\n                                                      const Parity parity) {\n  constexpr size_t max_num_points =\n      Spectral::maximum_number_of_points<BasisType>;\n  constexpr size_t min_num_points =\n      Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n  ASSERT(num_points >= min_num_points,\n         \"Tried to work with less than the minimum number of collocation \"\n         \"points for this quadrature.\");\n  ASSERT(num_points <= max_num_points,\n         \"Exceeded maximum number of collocation points.\");\n  ASSERT(parity != Parity::Uninitialized,\n         \"Tried to use a parity-based function without a definite parity\");\n  // We compute the quantity for all possible `num_point`s the first time this\n  // function is called and keep the data around for the lifetime of the\n  // program. The computation is handled by the call operator of the\n  // `SpectralQuantityType` instance.\n  static const auto precomputed_data =\n      make_static_cache<CacheRange<min_num_points, max_num_points + 1>,\n                        CacheEnumeration<Parity, Parity::Even, Parity::Odd>>(\n          SpectralQuantityGenerator{});\n  return precomputed_data(num_points, parity);\n}\n\ntemplate <Basis BasisType, Quadrature QuadratureType,\n          typename SpectralQuantityGenerator>\nconst auto& precomputed_two_indexed_spectral_quantity(const size_t num_points,\n                                           const size_t m, const size_t N) {\n  constexpr size_t max_num_points =\n      Spectral::maximum_number_of_points<BasisType>;\n  constexpr size_t min_num_points =\n      Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n  ASSERT(num_points >= min_num_points,\n         \"Tried to work with less than the minimum number of collocation \"\n         \"points for this quadrature.\");\n  ASSERT(num_points <= max_num_points,\n         \"Exceeded maximum number of collocation points.\");\n  // We compute the quantity for all possible `num_point`s the first time this\n  // function is called and keep the data around for the lifetime of the\n  // program. The computation is handled by the call operator of the\n  // `SpectralQuantityType` instance.\n  static const auto precomputed_data =\n      make_static_cache<CacheRange<min_num_points, max_num_points + 1>,\n                        CacheRange<0_st, 2 * max_num_points - 1>,\n                        CacheRange<0_st, 2 * max_num_points - 1>>(\n          SpectralQuantityGenerator{});\n  return precomputed_data(num_points, m, N);\n}\n}  // namespace Spectral::detail\n\n// clang-tidy: Macro arguments should be in parentheses, but we want to append\n// template parameters here.\n#define PRECOMPUTED_SPECTRAL_QUANTITY(function_name, return_type, \\\n                                      generator_name)             \\\n  template <Basis BasisType, Quadrature QuadratureType>           \\\n  const return_type& function_name(const size_t num_points) {     \\\n    return Spectral::detail::precomputed_spectral_quantity<       \\\n        BasisType, QuadratureType,                                \\\n        generator_name<BasisType, QuadratureType>>(/* NOLINT */   \\\n                                                   num_points);   \\\n  }\n#define PRECOMPUTED_SPECTRAL_QUANTITY_WITH_PARITY(function_name, return_type, \\\n                                                  generator_name)             \\\n  template <Basis BasisType, Quadrature QuadratureType>                       \\\n  const return_type& function_name(const size_t num_points,                   \\\n                                   const Parity parity) {                     \\\n    return Spectral::detail::precomputed_spectral_quantity_with_parity<       \\\n        BasisType, QuadratureType,                                            \\\n        generator_name<BasisType, QuadratureType>>(/* NOLINT */               \\\n                                                   num_points, parity);       \\\n  }\n#define PRECOMPUTED_TWO_INDEXED_SPECTRAL_QUANTITY(function_name, return_type, \\\n                                                  generator_name)             \\\n  template <Basis BasisType, Quadrature QuadratureType>                       \\\n  const return_type& function_name(const size_t num_points, const size_t m,   \\\n                                   const size_t N) {                          \\\n    return Spectral::detail::precomputed_two_indexed_spectral_quantity<       \\\n        BasisType, QuadratureType,                                            \\\n        generator_name<BasisType, QuadratureType>>(/* NOLINT */               \\\n                                                   num_points, m, N);         \\\n  }\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Projection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <functional>\n#include <initializer_list>\n#include <ostream>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Spectral {\nnamespace {\nvoid verify_meshes(const Mesh<1>& parent_mesh, const Mesh<1>& child_mesh,\n                   const SegmentSize size) {\n  constexpr size_t max_points_l = maximum_number_of_points<Basis::Legendre>;\n  constexpr size_t max_points_f = maximum_number_of_points<Basis::Fourier>;\n\n  // Legendre with Legendre (any segment size)\n  const bool both_legendre = parent_mesh.basis(0) == Basis::Legendre and\n                             child_mesh.basis(0) == Basis::Legendre;\n\n  // Fourier with Fourier (full segment only)\n  const bool both_fourier = parent_mesh.basis(0) == Basis::Fourier and\n                            child_mesh.basis(0) == Basis::Fourier and\n                            size == SegmentSize::Full;\n\n  // ZernikeB2 Equiangular with ZernikeB2 Equiangular (full segment only)\n  const bool both_zernike =\n      parent_mesh.basis(0) == Basis::ZernikeB2 and\n      parent_mesh.quadrature(0) == Quadrature::Equiangular and\n      child_mesh.basis(0) == Basis::ZernikeB2 and\n      child_mesh.quadrature(0) == Quadrature::Equiangular and\n      size == SegmentSize::Full;\n\n  // Fourier with ZernikeB2 Equiangular (full segment only)\n  const bool fourier_zernike =\n      parent_mesh.basis(0) == Basis::Fourier and\n      child_mesh.basis(0) == Basis::ZernikeB2 and\n      child_mesh.quadrature(0) == Quadrature::Equiangular and\n      size == SegmentSize::Full;\n\n  // ZernikeB2 Equiangular with Fourier (full segment only)\n  const bool zernike_fourier =\n      parent_mesh.basis(0) == Basis::ZernikeB2 and\n      parent_mesh.quadrature(0) == Quadrature::Equiangular and\n      child_mesh.basis(0) == Basis::Fourier and size == SegmentSize::Full;\n\n  ASSERT(both_legendre or both_fourier or both_zernike or fourier_zernike or\n             zernike_fourier,\n         \"Unsupported projection combination. Supported: Legendre <-> Legendre \"\n         \"or Fourier <-> Fourier (where ZernikeB2 with Equiangular quadrature \"\n         \"also works) with no h-refinement (size must be Full). Got parent: \"\n             << parent_mesh << \", child: \" << child_mesh << \", size: \" << size);\n\n  ASSERT((both_legendre and (parent_mesh.extents(0) <= max_points_l and\n                             child_mesh.extents(0) <= max_points_l)) or\n             (parent_mesh.extents(0) <= max_points_f and\n              child_mesh.extents(0) <= max_points_f),\n         \"Mesh has more points than supported by its quadrature.\");\n}\n}  // namespace\n\ntemplate <size_t Dim>\nbool needs_projection(const Mesh<Dim>& mesh1, const Mesh<Dim>& mesh2,\n                      const std::array<SegmentSize, Dim>& child_sizes) {\n  return mesh1 != mesh2 or\n         alg::any_of(child_sizes, [](const Spectral::SegmentSize child_size) {\n           ASSERT(child_size != Spectral::SegmentSize::Uninitialized,\n                  \"Received uninitialized child_size.\");\n           return child_size != Spectral::SegmentSize::Full;\n         });\n}\n\nconst Matrix& projection_matrix_child_to_parent(const Mesh<1>& child_mesh,\n                                                const Mesh<1>& parent_mesh,\n                                                const SegmentSize size,\n                                                const bool operand_is_massive) {\n  constexpr size_t max_points_l = maximum_number_of_points<Basis::Legendre>;\n  constexpr size_t max_points_f = maximum_number_of_points<Basis::Fourier>;\n  verify_meshes(parent_mesh, child_mesh, size);\n\n  if (operand_is_massive) {\n    ASSERT(parent_mesh.extents(0) <= child_mesh.extents(0),\n           \"Requested projection matrix from child with fewer points (\"\n               << child_mesh.extents(0) << \") than the parent (\"\n               << parent_mesh.extents(0) << \")\");\n    // The restriction operator for massive quantities is just the interpolation\n    // transpose\n    const auto compute_matrix =\n        [](const Basis parent_basis, const Quadrature parent_quadrature,\n           const size_t parent_extent, const Basis child_basis,\n           const Quadrature child_quadrature, const size_t child_extent,\n           const SegmentSize local_child_size) -> Matrix {\n      const auto& prolongation_operator = projection_matrix_parent_to_child(\n          {parent_extent, parent_basis, parent_quadrature},\n          {child_extent, child_basis, child_quadrature}, local_child_size);\n      return blaze::trans(prolongation_operator);\n    };\n    const static auto cache_legendre = make_static_cache<\n        CacheEnumeration<Quadrature, Quadrature::Gauss,\n                         Quadrature::GaussLobatto>,\n        CacheRange<2_st, max_points_l + 1>,\n        CacheEnumeration<Quadrature, Quadrature::Gauss,\n                         Quadrature::GaussLobatto>,\n        CacheRange<2_st, max_points_l + 1>,\n        CacheEnumeration<SegmentSize, SegmentSize::Full, SegmentSize::UpperHalf,\n                         SegmentSize::LowerHalf>>(\n        [compute_matrix](const Quadrature q_parent, const size_t n_parent,\n                         const Quadrature q_child, const size_t n_child,\n                         const SegmentSize local_child_size) {\n          return compute_matrix(Basis::Legendre, q_parent, n_parent,\n                                Basis::Legendre, q_child, n_child,\n                                local_child_size);\n        });\n    const static auto cache_fourier =\n        make_static_cache<CacheRange<2_st, max_points_f + 1>,\n                          CacheRange<2_st, max_points_f + 1>>(\n            [compute_matrix](const size_t n_parent, const size_t n_child) {\n              return compute_matrix(Basis::Fourier, Quadrature::Equiangular,\n                                    n_parent, Basis::Fourier,\n                                    Quadrature::Equiangular, n_child,\n                                    SegmentSize::Full);\n            });\n    if (parent_mesh.basis(0) == Basis::Legendre) {\n      return cache_legendre(parent_mesh.quadrature(0), parent_mesh.extents(0),\n                            child_mesh.quadrature(0), child_mesh.extents(0),\n                            size);\n    } else {\n      // Both Fourier and ZernikeB2 get here\n      // In terms of angular interpolation, ZernikeB2 with Equiangular\n      // quadrature is identical to Fourier with Equiangular quadrature, so the\n      // matrices do not make a distinction\n      return cache_fourier(parent_mesh.extents(0), child_mesh.extents(0));\n    }\n  }\n\n  switch (size) {\n    case SegmentSize::Full: {\n      const auto compute_matrix =\n          [](const Basis basis_element, const Quadrature quadrature_element,\n             const size_t extents_element, const Basis basis_mortar,\n             const Quadrature quadrature_mortar, const size_t extents_mortar) {\n            const Mesh<1> mesh_element(extents_element, basis_element,\n                                       quadrature_element);\n            const Mesh<1> mesh_mortar(extents_mortar, basis_mortar,\n                                      quadrature_mortar);\n\n            // The projection in spectral space is just a truncation\n            // of the modes.\n            const auto& spectral_to_grid_element =\n                modal_to_nodal_matrix(mesh_element);\n            const auto& grid_to_spectral_mortar =\n                nodal_to_modal_matrix(mesh_mortar);\n            const auto num_modes = std::min(extents_element, extents_mortar);\n            Matrix projection(extents_element, extents_mortar, 0.);\n            for (size_t i = 0; i < extents_element; ++i) {\n              for (size_t j = 0; j < extents_mortar; ++j) {\n                for (size_t k = 0; k < num_modes; ++k) {\n                  projection(i, j) += spectral_to_grid_element(i, k) *\n                                      grid_to_spectral_mortar(k, j);\n                }\n              }\n            }\n\n            return projection;\n          };\n      const static auto cache_legendre =\n          make_static_cache<CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>,\n                            CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>>(\n              [compute_matrix](const Quadrature q_parent, const size_t n_parent,\n                               const Quadrature q_child, const size_t n_child) {\n                return compute_matrix(Basis::Legendre, q_parent, n_parent,\n                                      Basis::Legendre, q_child, n_child);\n              });\n      const static auto cache_fourier =\n          make_static_cache<CacheRange<2_st, max_points_f + 1>,\n                            CacheRange<2_st, max_points_f + 1>>(\n              [compute_matrix](const size_t n_parent, const size_t n_child) {\n                return compute_matrix(Basis::Fourier, Quadrature::Equiangular,\n                                      n_parent, Basis::Fourier,\n                                      Quadrature::Equiangular, n_child);\n              });\n      if (parent_mesh.basis(0) == Basis::Legendre) {\n        return cache_legendre(parent_mesh.quadrature(0), parent_mesh.extents(0),\n                              child_mesh.quadrature(0), child_mesh.extents(0));\n      } else {\n        // Both Fourier and ZernikeB2 get here\n        return cache_fourier(parent_mesh.extents(0), child_mesh.extents(0));\n      }\n    }\n\n    case SegmentSize::UpperHalf: {\n      const static auto cache = make_static_cache<\n          CacheEnumeration<Quadrature, Quadrature::Gauss,\n                           Quadrature::GaussLobatto>,\n          CacheRange<2_st, max_points_l + 1>,\n          CacheEnumeration<Quadrature, Quadrature::Gauss,\n                           Quadrature::GaussLobatto>,\n          CacheRange<2_st, max_points_l + 1>>(\n          [](const Quadrature quadrature_element, const size_t extents_element,\n             const Quadrature quadrature_mortar, const size_t extents_mortar) {\n            const Mesh<1> mesh_element(extents_element, Basis::Legendre,\n                                       quadrature_element);\n            const Mesh<1> mesh_mortar(extents_mortar, Basis::Legendre,\n                                      quadrature_mortar);\n\n            // The transformation from the small interval to the large\n            // interval in spectral space.  This is a rearranged\n            // version of the equation given in the header.  This form\n            // was easier to code.\n            const auto spectral_transformation = [](const size_t large_index,\n                                                    const size_t small_index) {\n              ASSERT(large_index >= small_index,\n                     \"Above-diagonal entries are zero.  Don't use them.\");\n              double result = 1.;\n              for (size_t i = (large_index - small_index) / 2; i > 0; --i) {\n                result = 1 - result *\n                                 static_cast<double>(\n                                     (large_index + small_index + 3 - 2 * i) *\n                                     (large_index + small_index + 2 - 2 * i) *\n                                     (large_index - small_index + 2 - 2 * i) *\n                                     (large_index - small_index + 1 - 2 * i)) /\n                                 static_cast<double>(\n                                     2 * i * (2 * large_index + 1 - 2 * i) *\n                                     (large_index + 2 - 2 * i) *\n                                     (large_index + 1 - 2 * i));\n              }\n\n              for (size_t i = 1; i <= large_index - small_index; ++i) {\n                result *=\n                    1. + static_cast<double>(large_index + small_index + 1) /\n                             static_cast<double>(i);\n              }\n              result /= pow(2., static_cast<int>(large_index) + 1);\n              return result;\n            };\n\n            const auto& spectral_to_grid_element =\n                modal_to_nodal_matrix(mesh_element);\n            const auto& grid_to_spectral_mortar =\n                nodal_to_modal_matrix(mesh_mortar);\n\n            const auto num_modes = std::min(extents_element, extents_mortar);\n            Matrix temp(extents_element, num_modes, 0.);\n            for (size_t j = 0; j < num_modes; ++j) {\n              for (size_t k = j; k < extents_element; ++k) {\n                const double transformation_entry =\n                    spectral_transformation(k, j);\n                for (size_t i = 0; i < extents_element; ++i) {\n                  temp(i, j) +=\n                      spectral_to_grid_element(i, k) * transformation_entry;\n                }\n              }\n            }\n\n            Matrix projection(extents_element, extents_mortar, 0.);\n            for (size_t i = 0; i < extents_element; ++i) {\n              for (size_t j = 0; j < extents_mortar; ++j) {\n                for (size_t k = 0; k < num_modes; ++k) {\n                  projection(i, j) +=\n                      temp(i, k) * grid_to_spectral_mortar(k, j);\n                }\n              }\n            }\n\n            return projection;\n          });\n      return cache(parent_mesh.quadrature(0), parent_mesh.extents(0),\n                   child_mesh.quadrature(0), child_mesh.extents(0));\n    }\n\n    case SegmentSize::LowerHalf: {\n      const static auto cache =\n          make_static_cache<CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>,\n                            CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>>(\n              [](const Quadrature quadrature_element,\n                 const size_t extents_element,\n                 const Quadrature quadrature_mortar,\n                 const size_t extents_mortar) {\n                const Mesh<1> mesh_element(extents_element, Basis::Legendre,\n                                           quadrature_element);\n                const Mesh<1> mesh_mortar(extents_mortar, Basis::Legendre,\n                                          quadrature_mortar);\n\n                // The lower-half matrices are generated from the upper-half\n                // matrices using symmetry.\n                const auto& projection_upper_half =\n                    projection_matrix_child_to_parent(mesh_mortar, mesh_element,\n                                                      SegmentSize::UpperHalf);\n\n                Matrix projection_lower_half(extents_element, extents_mortar);\n                for (size_t i = 0; i < extents_element; ++i) {\n                  for (size_t j = 0; j < extents_mortar; ++j) {\n                    projection_lower_half(i, j) = projection_upper_half(\n                        extents_element - i - 1, extents_mortar - j - 1);\n                  }\n                }\n\n                return projection_lower_half;\n              });\n      return cache(parent_mesh.quadrature(0), parent_mesh.extents(0),\n                   child_mesh.quadrature(0), child_mesh.extents(0));\n    }\n\n    default:\n      ERROR(\"Invalid SegmentSize\");\n  }\n}\n\ntemplate <size_t Dim>\nstd::array<std::reference_wrapper<const Matrix>, Dim>\nprojection_matrix_child_to_parent(\n    const Mesh<Dim>& child_mesh, const Mesh<Dim>& parent_mesh,\n    const std::array<SegmentSize, Dim>& child_sizes,\n    const bool operand_is_massive) {\n  static const Matrix identity{};\n  auto projection_matrix = make_array<Dim>(std::cref(identity));\n  const auto child_mesh_slices = child_mesh.slices();\n  const auto parent_mesh_slices = parent_mesh.slices();\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto child_mesh_slice = gsl::at(child_mesh_slices, d);\n    const auto parent_mesh_slice = gsl::at(parent_mesh_slices, d);\n    const auto child_size = gsl::at(child_sizes, d);\n    ASSERT(child_mesh_slice.basis(0) == Basis::Legendre or\n               child_size == SegmentSize::Full,\n           \"The Fourier basis does not support h-refinement: SegmentSize must \"\n           \"be Full in dimension \"\n               << d << \", but got \" << child_size);\n    if (child_size == SegmentSize::Full and\n        child_mesh_slice == parent_mesh_slice) {\n      // No projection necessary, keep matrix the identity in this dimension\n      continue;\n    }\n    gsl::at(projection_matrix, d) = projection_matrix_child_to_parent(\n        child_mesh_slice, parent_mesh_slice, child_size, operand_is_massive);\n  }\n  return projection_matrix;\n}\n\nconst Matrix& projection_matrix_parent_to_child(const Mesh<1>& parent_mesh,\n                                                const Mesh<1>& child_mesh,\n                                                const SegmentSize size) {\n  constexpr size_t max_points_l = maximum_number_of_points<Basis::Legendre>;\n  verify_meshes(parent_mesh, child_mesh, size);\n\n  // For Fourier with SegmentSize::Full, the element-to-mortar projection is\n  // the same spectral truncation/zero-padding as child_to_parent with roles\n  // swapped. Delegate to avoid duplicating the cached computation.\n  if (parent_mesh.basis(0) == Basis::Fourier or\n      parent_mesh.basis(0) == Basis::ZernikeB2) {\n    return projection_matrix_child_to_parent(parent_mesh, child_mesh,\n                                             SegmentSize::Full);\n  }\n\n  ASSERT(child_mesh.extents(0) >= parent_mesh.extents(0),\n         \"Requested projection matrix to mortar with fewer points (\"\n             << child_mesh.extents(0) << \") than the element (\"\n             << parent_mesh.extents(0) << \")\");\n\n  // Element-to-mortar projections on Legendre meshes are interpolations.\n  const auto make_interpolators = [](auto interval_transform) {\n    return [interval_transform = std::move(interval_transform)](\n               const Quadrature quadrature_mortar, const size_t extents_mortar,\n               const Quadrature quadrature_element,\n               const size_t extents_element) {\n      if (extents_mortar < extents_element) {\n        return Matrix{};\n      }\n      const Mesh<1> mesh_element(extents_element, Basis::Legendre,\n                                 quadrature_element);\n      const Mesh<1> mesh_mortar(extents_mortar, Basis::Legendre,\n                                quadrature_mortar);\n      return interpolation_matrix(\n          mesh_element, interval_transform(collocation_points(mesh_mortar)));\n    };\n  };\n\n  switch (size) {\n    case SegmentSize::Full: {\n      const static auto cache =\n          make_static_cache<CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>,\n                            CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>>(\n              make_interpolators([](const DataVector& x) { return x; }));\n      return cache(child_mesh.quadrature(0), child_mesh.extents(0),\n                   parent_mesh.quadrature(0), parent_mesh.extents(0));\n    }\n\n    case SegmentSize::UpperHalf: {\n      const static auto cache =\n          make_static_cache<CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>,\n                            CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>>(\n              make_interpolators([](const DataVector& x) {\n                return DataVector(0.5 * (x + 1.));\n              }));\n      return cache(child_mesh.quadrature(0), child_mesh.extents(0),\n                   parent_mesh.quadrature(0), parent_mesh.extents(0));\n    }\n\n    case SegmentSize::LowerHalf: {\n      const static auto cache =\n          make_static_cache<CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>,\n                            CacheEnumeration<Quadrature, Quadrature::Gauss,\n                                             Quadrature::GaussLobatto>,\n                            CacheRange<2_st, max_points_l + 1>>(\n              make_interpolators([](const DataVector& x) {\n                return DataVector(0.5 * (x - 1.));\n              }));\n      return cache(child_mesh.quadrature(0), child_mesh.extents(0),\n                   parent_mesh.quadrature(0), parent_mesh.extents(0));\n    }\n\n    default:\n      ERROR(\"Invalid SegmentSize\");\n  }\n}\n\ntemplate <size_t Dim>\nstd::array<std::reference_wrapper<const Matrix>, Dim>\nprojection_matrix_parent_to_child(\n    const Mesh<Dim>& parent_mesh, const Mesh<Dim>& child_mesh,\n    const std::array<SegmentSize, Dim>& child_sizes) {\n  static const Matrix identity{};\n  auto projection_matrix = make_array<Dim>(std::cref(identity));\n  const auto child_mesh_slices = child_mesh.slices();\n  const auto parent_mesh_slices = parent_mesh.slices();\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto child_mesh_slice = gsl::at(child_mesh_slices, d);\n    const auto parent_mesh_slice = gsl::at(parent_mesh_slices, d);\n    const auto child_size = gsl::at(child_sizes, d);\n    ASSERT(child_mesh_slice.basis(0) == Basis::Legendre or\n               child_size == SegmentSize::Full,\n           \"The Fourier basis does not support h-refinement: SegmentSize must \"\n           \"be Full in dimension \"\n               << d << \", but got \" << child_size);\n    if (child_size == SegmentSize::Full and\n        child_mesh_slice == parent_mesh_slice) {\n      // No projection necessary, keep matrix the identity in this dimension\n      continue;\n    }\n    gsl::at(projection_matrix, d) = projection_matrix_parent_to_child(\n        parent_mesh_slice, child_mesh_slice, child_size);\n  }\n  return projection_matrix;\n}\n\ntemplate <size_t Dim>\nstd::array<std::reference_wrapper<const Matrix>, Dim> projection_matrices(\n    const Mesh<Dim>& source_mesh, const Mesh<Dim>& target_mesh,\n    const std::array<SegmentSize, Dim>& source_sizes,\n    const std::array<SegmentSize, Dim>& target_sizes) {\n  static const Matrix identity{};\n  auto projection_matrices = make_array<Dim>(std::cref(identity));\n  const auto source_mesh_slices = source_mesh.slices();\n  const auto target_mesh_slices = target_mesh.slices();\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto& source_mesh_slice = gsl::at(source_mesh_slices, d);\n    const auto& target_mesh_slice = gsl::at(target_mesh_slices, d);\n    const auto source_size = gsl::at(source_sizes, d);\n    const auto target_size = gsl::at(target_sizes, d);\n    if (source_size == target_size) {\n      if (source_mesh_slice == target_mesh_slice) {\n        // No projection necessary, keep matrix the identity in this dimension\n      } else {\n        gsl::at(projection_matrices, d) = projection_matrix_child_to_parent(\n            source_mesh_slice, target_mesh_slice, SegmentSize::Full);\n      }\n    } else if (source_size == SegmentSize::Full) {\n      gsl::at(projection_matrices, d) = projection_matrix_parent_to_child(\n          source_mesh_slice, target_mesh_slice, target_size);\n    } else {\n      ASSERT(target_size == SegmentSize::Full,\n             \"Cannot project from one half of segment to the other.\");\n      gsl::at(projection_matrices, d) = projection_matrix_child_to_parent(\n          source_mesh_slice, target_mesh_slice, source_size);\n    }\n  }\n  return projection_matrices;\n}\n\ntemplate <size_t Dim>\nstd::array<std::reference_wrapper<const Matrix>, Dim> p_projection_matrices(\n    const Mesh<Dim>& source_mesh, const Mesh<Dim>& target_mesh) {\n  return projection_matrices(source_mesh, target_mesh,\n                             make_array<Dim>(SegmentSize::Full),\n                             make_array<Dim>(SegmentSize::Full));\n}\n\ntemplate <size_t DimMinusOne>\nsize_t hash(const std::array<Spectral::SegmentSize, DimMinusOne>& mortar_size) {\n  if constexpr (DimMinusOne == 2) {\n    ASSERT(mortar_size[0] != Spectral::SegmentSize::Uninitialized and\n               mortar_size[1] != Spectral::SegmentSize::Uninitialized,\n           \"Cannot hash an uninitialized mortar_size: \" << mortar_size[0] << \" \"\n                                                        << mortar_size[1]);\n    return (static_cast<size_t>(mortar_size[0]) - 1) % 2 +\n           2 * ((static_cast<size_t>(mortar_size[1]) - 1) % 2);\n  } else if constexpr (DimMinusOne == 1) {\n    ASSERT(mortar_size[0] != Spectral::SegmentSize::Uninitialized,\n           \"Cannot hash an uninitialized mortar_size: \" << mortar_size[0]);\n    return (static_cast<size_t>(mortar_size[0]) - 1) % 2;\n  } else if constexpr (DimMinusOne == 0) {\n    (void)mortar_size;\n    return 0;\n  } else {\n    static_assert(DimMinusOne == 2 or DimMinusOne == 1 or DimMinusOne == 0);\n  }\n}\n\ntemplate <size_t Dim>\nsize_t MortarSizeHash<Dim>::operator()(\n    const std::array<Spectral::SegmentSize, Dim - 1>& mortar_size) {\n  return hash(mortar_size);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(r, data)                                                   \\\n  template bool needs_projection(                                              \\\n      const Mesh<DIM(data)>& mesh1, const Mesh<DIM(data)>& mesh2,              \\\n      const std::array<SegmentSize, DIM(data)>& child_sizes);                  \\\n  template std::array<std::reference_wrapper<const Matrix>, DIM(data)>         \\\n  projection_matrix_child_to_parent(                                           \\\n      const Mesh<DIM(data)>& child_mesh, const Mesh<DIM(data)>& parent_mesh,   \\\n      const std::array<SegmentSize, DIM(data)>& child_sizes,                   \\\n      bool operand_is_massive);                                                \\\n  template std::array<std::reference_wrapper<const Matrix>, DIM(data)>         \\\n  projection_matrix_parent_to_child(                                           \\\n      const Mesh<DIM(data)>& parent_mesh, const Mesh<DIM(data)>& child_mesh,   \\\n      const std::array<SegmentSize, DIM(data)>& child_sizes);                  \\\n  template std::array<std::reference_wrapper<const Matrix>, DIM(data)>         \\\n  projection_matrices(const Mesh<DIM(data)>& source_mesh,                      \\\n                      const Mesh<DIM(data)>& target_mesh,                      \\\n                      const std::array<SegmentSize, DIM(data)>& source_sizes,  \\\n                      const std::array<SegmentSize, DIM(data)>& target_sizes); \\\n  template std::array<std::reference_wrapper<const Matrix>, DIM(data)>         \\\n  p_projection_matrices(const Mesh<DIM(data)>& source_mesh,                    \\\n                        const Mesh<DIM(data)>& target_mesh);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2, 3))\n#undef INSTANTIATE\n\n#define INSTANTIATE(r, data) template class MortarSizeHash<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n#undef INSTANTIATE\n\n#define INSTANTIATE(r, data)       \\\n  template size_t hash<DIM(data)>( \\\n      const std::array<Spectral::SegmentSize, DIM(data)>& mortar_size);\nGENERATE_INSTANTIATIONS(INSTANTIATE, (0, 1, 2))\n#undef INSTANTIATE\n\n#undef DIM\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Projection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <ostream>\n\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\n/// \\cond\nclass Matrix;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace Spectral {\n/// Determine whether data needs to be projected between a child mesh and its\n/// parent mesh. If no projection is necessary the data may be used as-is.\n/// Projection is necessary if the child is either p-refined or h-refined\n/// relative to its parent, or both. This operation is symmetric, i.e. it is\n/// irrelevant in which order the child and the parent mesh are passed in.\ntemplate <size_t Dim>\nbool needs_projection(const Mesh<Dim>& mesh1, const Mesh<Dim>& mesh2,\n                      const std::array<SegmentSize, Dim>& child_sizes);\n\n/*!\n * \\brief The projection matrix from a child mesh to its parent.\n *\n * The projection matrices returned by this function (and by\n * projection_matrix_parent_to_child()) define orthogonal projection operators\n * between the spaces of functions on a parent mesh and its children. These\n * projections are usually the correct way to transfer data between meshes in\n * a mesh-refinement hierarchy, as well as between an element face and its\n * adjacent mortars.\n *\n * These functions assume that the `child_mesh` is at least as fine as the\n * `parent_mesh`, i.e. functions on the `parent_mesh` can be represented exactly\n * on the `child_mesh`. In practice this means that functions can be projected\n * to a mortar (the `child_mesh`) from both adjacent element faces (the\n * `parent_mesh`) without losing accuracy. Similarly, functions in a\n * mesh-refinement hierarchy don't lose accuracy when an element is split\n * (h-refined). For this reason, the `projection_matrix_child_to_parent` is\n * sometimes referred to as a \"restriction operator\" and the\n * `projection_matrix_parent_to_child` as a \"prolongation operator\".\n *\n * \\par Massive quantities\n * If the quantity that should be projected is not a function over the\n * computational grid but a \"massive\" residual, i.e. a quantity\n * \\f$\\int_{\\Omega_k} f(x) \\psi_p(x) \\mathrm{d}V\\f$ where \\f$\\psi_p\\f$ are the\n * basis functions on the mesh, then pass `true` for the parameter\n * `operand_is_massive` (default is `false`). The restriction operator for this\n * case is just the transpose of the prolongation operator, i.e. just an\n * interpolation matrix transpose. Note that the \"massive\" residual already\n * takes the difference in element size between parent and children into account\n * by including a Jacobian in the volume element of the integral.\n *\n * \\par Implementation details\n * The half-interval projections are based on an equation derived by\n * Saul.  This shows that the projection from the spectral basis for\n * the entire interval to the spectral basis for the upper half\n * interval is\n * \\f{equation*}\n * T_{jk} = \\frac{2 j + 1}{2} 2^j \\sum_{n=0}^{j-k} \\binom{j}{k+n}\n * \\binom{(j + k + n - 1)/2}{j} \\frac{(k + n)!^2}{(2 k + n + 1)! n!}\n * \\f}\n */\nconst Matrix& projection_matrix_child_to_parent(\n    const Mesh<1>& child_mesh, const Mesh<1>& parent_mesh, SegmentSize size,\n    bool operand_is_massive = false);\n\n/// The projection matrix from a child mesh to its parent, in `Dim` dimensions.\ntemplate <size_t Dim>\nstd::array<std::reference_wrapper<const Matrix>, Dim>\nprojection_matrix_child_to_parent(\n    const Mesh<Dim>& child_mesh, const Mesh<Dim>& parent_mesh,\n    const std::array<SegmentSize, Dim>& child_sizes,\n    bool operand_is_massive = false);\n\n/// The projection matrix from a parent mesh to one of its children.\n///\n/// \\see projection_matrix_child_to_parent()\nconst Matrix& projection_matrix_parent_to_child(const Mesh<1>& parent_mesh,\n                                                const Mesh<1>& child_mesh,\n                                                SegmentSize size);\n\n/// The projection matrix from a parent mesh to one of its children, in `Dim`\n/// dimensions\ntemplate <size_t Dim>\nstd::array<std::reference_wrapper<const Matrix>, Dim>\nprojection_matrix_parent_to_child(\n    const Mesh<Dim>& parent_mesh, const Mesh<Dim>& child_mesh,\n    const std::array<SegmentSize, Dim>& child_sizes);\n\n/// The projection matrices from a source mesh to a target mesh\n/// covering given portions of an element\ntemplate <size_t Dim>\nstd::array<std::reference_wrapper<const Matrix>, Dim> projection_matrices(\n    const Mesh<Dim>& source_mesh, const Mesh<Dim>& target_mesh,\n    const std::array<SegmentSize, Dim>& source_sizes,\n    const std::array<SegmentSize, Dim>& target_sizes);\n\n/// The projection matrices from a source mesh to a target mesh where the\n/// meshes cover the same physical volume\ntemplate <size_t Dim>\nstd::array<std::reference_wrapper<const Matrix>, Dim> p_projection_matrices(\n    const Mesh<Dim>& source_mesh, const Mesh<Dim>& target_mesh);\n\n/// @{\n/// \\brief Performs a perfect hash of the mortars into $2^{d-1}$ slots on the\n/// range $[0, 2^{d-1})$.\n///\n/// This is particularly useful when hashing into statically-sized maps based\n/// on the number of dimensions.\ntemplate <size_t DimMinusOne>\nsize_t hash(const std::array<Spectral::SegmentSize, DimMinusOne>& mortar_size);\n\ntemplate <size_t Dim>\nstruct MortarSizeHash {\n  template <size_t MaxSize>\n  static constexpr bool is_perfect = MaxSize == two_to_the(Dim);\n\n  size_t operator()(\n      const std::array<Spectral::SegmentSize, Dim - 1>& mortar_size);\n};\n/// @}\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Filtering.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/Python/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Python/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Python/Spectral.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  Spectral::py_bindings::bind_logical_coordinates(m);\n  Spectral::py_bindings::bind_spectral(m);\n  py_bindings::bind_mesh(m);\n  // Filtering\n  m.def(\"exponential_filter\", &Spectral::filtering::exponential_filter,\n        py::arg(\"mesh\"), py::arg(\"alpha\"), py::arg(\"half_power\"));\n  m.def(\"zero_lowest_modes\", &Spectral::filtering::zero_lowest_modes,\n        py::arg(\"mesh\"), py::arg(\"number_of_modes_to_zero\"),\n        py::return_value_policy::reference);\n  // Projection\n  py::enum_<Spectral::SegmentSize>(m, \"SegmentSize\")\n      .value(\"Uninitialized\", Spectral::SegmentSize::Uninitialized)\n      .value(\"Full\", Spectral::SegmentSize::Full)\n      .value(\"UpperHalf\", Spectral::SegmentSize::UpperHalf)\n      .value(\"LowerHalf\", Spectral::SegmentSize::LowerHalf);\n  m.def(\"projection_matrix_parent_to_child\",\n        static_cast<const Matrix& (*)(const Mesh<1>&, const Mesh<1>&,\n                                      Spectral::SegmentSize)>(\n            &Spectral::projection_matrix_parent_to_child),\n        py::arg(\"parent_mesh\"), py::arg(\"child_mesh\"), py::arg(\"size\"));\n  m.def(\"projection_matrix_child_to_parent\",\n        static_cast<const Matrix& (*)(const Mesh<1>&, const Mesh<1>&,\n                                      Spectral::SegmentSize, bool)>(\n            &Spectral::projection_matrix_child_to_parent),\n        py::arg(\"child_mesh\"), py::arg(\"parent_mesh\"), py::arg(\"size\"),\n        py::arg(\"operand_is_massive\"));\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PySpectral\")\n\nspectre_python_add_module(\n  Spectral\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  LogicalCoordinates.cpp\n  Mesh.cpp\n  Spectral.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  LogicalCoordinates.hpp\n  Mesh.hpp\n  Spectral.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Spectral\n  pybind11::module\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/LogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Python/LogicalCoordinates.hpp\"\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace py = pybind11;\n\nnamespace Spectral::py_bindings {\nnamespace {\ntemplate <size_t Dim>\nvoid bind_logical_coordinates_impl(py::module& m) {  // NOLINT\n  m.def(\"logical_coordinates\",\n        py::overload_cast<const Mesh<Dim>&>(&logical_coordinates<Dim>),\n        py::arg(\"mesh\"));\n}\n}  // namespace\n\nvoid bind_logical_coordinates(py::module& m) {\n  bind_logical_coordinates_impl<1>(m);\n  bind_logical_coordinates_impl<2>(m);\n  bind_logical_coordinates_impl<3>(m);\n}\n\n}  // namespace Spectral::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/LogicalCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace Spectral::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_logical_coordinates(pybind11::module& m);\n}  // namespace Spectral::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/Mesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Python/Mesh.hpp\"\n\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace py_bindings {\nnamespace {\ntemplate <size_t Dim>\nvoid bind_mesh_impl(py::module& m) {  // NOLINT\n  // the bindings here are not complete\n  py::class_<Mesh<Dim>>(m, (\"Mesh\" + std::to_string(Dim) + \"D\").c_str())\n      // Need __str__ for converting to string/printing\n      .def(\n          \"__str__\", +[](const Mesh<Dim>& t) { return get_output(t); })\n      // repr allows you to output the object in an interactive python terminal\n      // using obj to get the \"string REPResenting the object\".\n      .def(\n          \"__repr__\", +[](const Mesh<Dim>& t) { return get_output(t); })\n      .def_property_readonly_static(\n          \"dim\", [](const py::object& /*self */) { return Dim; })\n      .def(py::init<const size_t, const Spectral::Basis,\n                    const Spectral::Quadrature>(),\n           py::arg(\"isotropic_extents\"), py::arg(\"basis\"),\n           py::arg(\"quadrature\"))\n      .def(py::init<std::array<size_t, Dim>, const Spectral::Basis,\n                    const Spectral::Quadrature>(),\n           py::arg(\"extents\"), py::arg(\"basis\"), py::arg(\"quadrature\"))\n      .def(py::init<std::array<size_t, Dim>, std::array<Spectral::Basis, Dim>,\n                    std::array<Spectral::Quadrature, Dim>>(),\n           py::arg(\"extents\"), py::arg(\"bases\"), py::arg(\"quadratures\"))\n      .def(\n          \"extents\",\n          [](const Mesh<Dim>& mesh) { return mesh.extents(); },\n          \"The number of grid points in each dimension of the grid.\")\n      .def(\n          \"extents\",\n          static_cast<size_t (Mesh<Dim>::*)(size_t) const>(&Mesh<Dim>::extents),\n          py::arg(\"d\"),\n          \"The number of grid points in the requested dimension of the grid.\")\n      .def(\"number_of_grid_points\", &Mesh<Dim>::number_of_grid_points,\n           \"The total number of grid points in all dimensions.\")\n      .def(\"basis\",\n           static_cast<std::array<Spectral::Basis, Dim> (Mesh<Dim>::*)() const>(\n               &Mesh<Dim>::basis),\n           \"The basis chosen in each dimension of the grid.\")\n      .def(\"basis\",\n           static_cast<Spectral::Basis (Mesh<Dim>::*)(const size_t) const>(\n               &Mesh<Dim>::basis),\n           py::arg(\"d\"),\n           \"The basis chosen in the requested dimension of the grid.\")\n      .def(\"quadrature\",\n           static_cast<std::array<Spectral::Quadrature, Dim> (Mesh<Dim>::*)()\n                           const>(&Mesh<Dim>::quadrature),\n           \"The quadrature chosen in each dimension of the grid.\")\n      .def(\"quadrature\",\n           static_cast<Spectral::Quadrature (Mesh<Dim>::*)(const size_t) const>(\n               &Mesh<Dim>::quadrature),\n           py::arg(\"d\"),\n           \"The quadrature chosen in the requested dimension of the grid.\")\n      .def(\"slices\", &Mesh<Dim>::slices,\n           \"Returns the Meshes representing 1D slices of this Mesh.\")\n      .def(py::self == py::self)  // NOLINT\n      .def(py::self != py::self)  // NOLINT\n      .def(py::pickle(\n          [](const Mesh<Dim>& mesh) {\n            return py::make_tuple(mesh.extents().indices(), mesh.basis(),\n                                  mesh.quadrature());\n          },\n          [](const py::tuple& state) {\n            if (state.size() != 3) {\n              throw std::runtime_error(\"Invalid state for mesh!\");\n            }\n            return Mesh<Dim>(\n                state[0].cast<std::array<size_t, Dim>>(),\n                state[1].cast<std::array<Spectral::Basis, Dim>>(),\n                state[2].cast<std::array<Spectral::Quadrature, Dim>>());\n          }));\n}\n}  // namespace\n\nvoid bind_mesh(py::module& m) {\n  bind_mesh_impl<1>(m);\n  bind_mesh_impl<2>(m);\n  bind_mesh_impl<3>(m);\n}\n\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/Mesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_mesh(pybind11::module& m);\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/Spectral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Python/Spectral.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n\nnamespace py = pybind11;\n\nnamespace Spectral::py_bindings {\n\nvoid bind_spectral(py::module& m) {\n  py::enum_<Spectral::Basis>(m, \"Basis\")\n      .value(\"Legendre\", Spectral::Basis::Legendre)\n      .value(\"Chebyshev\", Spectral::Basis::Chebyshev)\n      .value(\"FiniteDifference\", Spectral::Basis::FiniteDifference)\n      .value(\"SphericalHarmonic\", Spectral::Basis::SphericalHarmonic);\n  py::enum_<Spectral::Quadrature>(m, \"Quadrature\")\n      .value(\"Gauss\", Spectral::Quadrature::Gauss)\n      .value(\"GaussLobatto\", Spectral::Quadrature::GaussLobatto)\n      .value(\"CellCentered\", Spectral::Quadrature::CellCentered)\n      .value(\"FaceCentered\", Spectral::Quadrature::FaceCentered)\n      .value(\"Equiangular\", Spectral::Quadrature::Equiangular);\n  m.def(\"collocation_points\",\n        static_cast<const DataVector& (*)(const Mesh<1>&)>(\n            &Spectral::collocation_points),\n        py::arg(\"mesh\"), py::return_value_policy::reference,\n        \"Collocation points for a one-dimensional mesh.\");\n  m.def(\"quadrature_weights\",\n        static_cast<const DataVector& (*)(const Mesh<1>&)>(\n            &Spectral::quadrature_weights),\n        py::arg(\"mesh\"), py::return_value_policy::reference);\n  m.def(\"differentiation_matrix\",\n        static_cast<const Matrix& (*)(const Mesh<1>&)>(\n            &Spectral::differentiation_matrix),\n        py::arg(\"mesh\"), py::return_value_policy::reference);\n  m.def(\"interpolation_matrix\",\n        static_cast<Matrix (*)(const Mesh<1>&, const std::vector<double>&)>(\n            &Spectral::interpolation_matrix),\n        py::arg(\"mesh\"), py::arg(\"target_points\"));\n  m.def(\"modal_to_nodal_matrix\",\n        static_cast<const Matrix& (*)(const Mesh<1>&)>(\n            &Spectral::modal_to_nodal_matrix),\n        py::arg(\"mesh\"), py::return_value_policy::reference,\n        \"Transformation matrix from modal to nodal coefficients for a \"\n        \"one-dimensional mesh.\");\n  m.def(\"nodal_to_modal_matrix\",\n        static_cast<const Matrix& (*)(const Mesh<1>&)>(\n            &Spectral::nodal_to_modal_matrix),\n        py::arg(\"mesh\"), py::return_value_policy::reference,\n        \"Transformation matrix from nodal to modal coefficients for a \"\n        \"one-dimensional mesh.\");\n}\n\n}  // namespace Spectral::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/Spectral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace Spectral::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_spectral(pybind11::module& m);\n}  // namespace Spectral::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n\nMesh = {1: Mesh1D, 2: Mesh2D, 3: Mesh3D}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Quadrature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\n#include <array>\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace Spectral {\nstd::array<Quadrature, 10> all_quadratures() {\n  return std::array{Quadrature::Uninitialized,   Quadrature::Gauss,\n                    Quadrature::GaussLobatto,    Quadrature::CellCentered,\n                    Quadrature::FaceCentered,    Quadrature::Equiangular,\n                    Quadrature::GaussRadauLower, Quadrature::GaussRadauUpper,\n                    Quadrature::AxialSymmetry,   Quadrature::SphericalSymmetry};\n}\n\nQuadrature to_quadrature(const std::string& quadrature) {\n  for (const auto the_quadrature : all_quadratures()) {\n    if (quadrature == get_output(the_quadrature)) {\n      return the_quadrature;\n    }\n  }\n  using ::operator<<;\n  ERROR(\"Failed to convert \\\"\" << quadrature\n                               << \"\\\" to Spectral::Quadrature.\\nMust be one of \"\n                               << all_quadratures() << \".\");\n}\n\nstd::ostream& operator<<(std::ostream& os, const Quadrature& quadrature) {\n  switch (quadrature) {\n    case Quadrature::Uninitialized:\n      return os << \"Uninitialized\";\n    case Quadrature::Gauss:\n      return os << \"Gauss\";\n    case Quadrature::GaussLobatto:\n      return os << \"GaussLobatto\";\n    case Quadrature::CellCentered:\n      return os << \"CellCentered\";\n    case Quadrature::FaceCentered:\n      return os << \"FaceCentered\";\n    case Quadrature::Equiangular:\n      return os << \"Equiangular\";\n    case Quadrature::GaussRadauLower:\n      return os << \"GaussRadauLower\";\n    case Quadrature::GaussRadauUpper:\n      return os << \"GaussRadauUpper\";\n    case Quadrature::AxialSymmetry:\n      return os << \"AxialSymmetry\";\n    case Quadrature::SphericalSymmetry:\n      return os << \"SphericalSymmetry\";\n    default:\n      ERROR(\"Invalid quadrature\");\n  }\n}\n}  // namespace Spectral\n\ntemplate <>\nSpectral::Quadrature\nOptions::create_from_yaml<Spectral::Quadrature>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  for (const auto quadrature : Spectral::all_quadratures()) {\n    if (type_read == get_output(quadrature)) {\n      return quadrature;\n    }\n  }\n  using ::operator<<;\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read << \"\\\" to Spectral::Quadrature.\\nMust be one of \"\n                  << Spectral::all_quadratures() << \".\");\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/Quadrature.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <iosfwd>\n#include <string>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief Either the choice of quadrature method to compute integration weights\n * for a spectral or discontinuous Galerkin (DG) method, or the locations of\n * grid points when a finite difference method is used\n *\n * \\details The particular choices of Basis and Quadrature determine\n * where the collocation points of a Mesh are located in an Element.  For a\n * spectral or DG method, integrals using \\f$N\\f$ collocation points with Gauss\n * quadrature are exact to polynomial order \\f$p=2N-1\\f$. Gauss-Lobatto\n * quadrature is exact only to polynomial order \\f$p=2N-3\\f$, but includes\n * collocation points at the Element boundary.  Gauss-Radau\n * quadrature is exact only to polynomial order \\f$p=2N-2\\f$, but includes a\n * collocation points at the lower or upper boundary of the Element.  For a\n * finite difference method, one needs to choose the order of the scheme (and\n * hence the weights, differentiation matrix, integration weights, and\n * interpolant) locally in space and time to handle discontinuous\n * solutions.\n *\n * \\note Choose `Gauss` or `GaussLobatto` when using Basis::Legendre or\n * Basis::Chebyshev.\n *\n * \\note Choose `CellCentered` or `FaceCentered` when using\n * Basis::FiniteDifference.\n *\n * \\note Choose `Equiangular` when using Basis::Fourier.\n *\n * \\note When using Basis::SphericalHarmonic in consecutive dimensions, choose\n * `Gauss` for the first dimension and `Equiangular` in the second dimension.\n *\n * \\note When using Basis::ZernikeB2 in consecutive dimensions, choose\n * `GaussRadauUpper` for the first dimension and `Equiangular` in the second\n * dimension.\n *\n * \\note When using Basis::ZernikeB3 in consecutive dimensions, choose\n * `GaussRadauUpper` for the first dimension, `Gauss` for the second dimension,\n * and `Equiangular` in the third dimension.\n *\n * \\remark We store these effectively as a 4-bit integer using the lowest 4\n * bits of a uint8_t. Unlike Basis, this does not need a bitshift. We cannot\n * have more than 16 quadratures to fit into the 4 bits, including the\n * `Uninitialized` value.\n */\nenum class Quadrature : uint8_t {\n  Uninitialized = 0,\n  Gauss,\n  GaussLobatto,\n  CellCentered,\n  FaceCentered,\n  Equiangular,\n  GaussRadauLower,\n  GaussRadauUpper,\n  AxialSymmetry,\n  SphericalSymmetry\n};\n\n/// All possible values of Quadrature\nstd::array<Quadrature, 10> all_quadratures();\n\n/// Convert a string to a Quadrature enum.\nQuadrature to_quadrature(const std::string& quadrature);\n\n/// Output operator for a Quadrature.\nstd::ostream& operator<<(std::ostream& os, const Quadrature& quadrature);\n}  // namespace Spectral\n\ntemplate <>\nstruct Options::create_from_yaml<Spectral::Quadrature> {\n  template <typename Metavariables>\n  static Spectral::Quadrature create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nSpectral::Quadrature\nOptions::create_from_yaml<Spectral::Quadrature>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/QuadratureWeights.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/GetSpectralQuantityForMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/InverseWeightFunctionValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/PrecomputedSpectralQuantity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SpectralQuantityForMesh.hpp\"\n\nnamespace Spectral {\ntemplate <Basis BasisType>\nMatrix spectral_definite_integral_matrix(size_t num_points);\n\nnamespace {\ntemplate <Basis BasisType, Quadrature QuadratureType>\nstruct QuadratureWeightsGenerator {\n  DataVector operator()(const size_t num_points) const {\n    if constexpr (BasisType == Basis::Chebyshev) {\n      DataVector result{num_points};\n      const Matrix q =\n          spectral_definite_integral_matrix<BasisType>(num_points) *\n          Spectral::nodal_to_modal_matrix<BasisType, QuadratureType>(\n              num_points);\n      for (size_t i = 0; i < num_points; ++i) {\n        result[i] = q(0, i);\n      }\n      return result;\n    } else {\n      return detail::precomputed_spectral_quantity<\n                 BasisType, QuadratureType,\n                 detail::CollocationPointsAndWeightsGenerator<BasisType,\n                                                              QuadratureType>>(\n                 num_points)\n          .second;\n    }\n  }\n};\n}  // namespace\n\nPRECOMPUTED_SPECTRAL_QUANTITY(quadrature_weights, DataVector,\n                              QuadratureWeightsGenerator)\n\n#undef PRECOMPUTED_SPECTRAL_QUANTITY\n\nSPECTRAL_QUANTITY_FOR_MESH(quadrature_weights, DataVector)\n\n#undef SPECTRAL_QUANTITY_FOR_MESH\n\ntemplate const DataVector&\n    quadrature_weights<Basis::Cartoon, Quadrature::AxialSymmetry>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::Cartoon, Quadrature::SphericalSymmetry>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::Chebyshev, Quadrature::Gauss>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::Chebyshev, Quadrature::GaussLobatto>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::Legendre, Quadrature::Gauss>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::Legendre, Quadrature::GaussLobatto>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::ZernikeB1, Quadrature::GaussRadauUpper>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::ZernikeB2, Quadrature::GaussRadauUpper>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::ZernikeB3, Quadrature::GaussRadauUpper>(size_t);\ntemplate const DataVector& quadrature_weights<Basis::FiniteDifference,\n                                              Quadrature::CellCentered>(size_t);\ntemplate const DataVector& quadrature_weights<Basis::FiniteDifference,\n                                              Quadrature::FaceCentered>(size_t);\ntemplate const DataVector&\n    quadrature_weights<Basis::Fourier, Quadrature::Equiangular>(size_t);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/QuadratureWeights.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\ntemplate <size_t>\nclass Mesh;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Spectral {\n/*!\n * \\brief Weights to compute definite integrals.\n *\n * \\details These are the coefficients to contract with the nodal\n * function values \\f$f_k\\f$ to approximate the definite integral \\f$I[f]=\\int\n * f(x)\\mathrm{d}x\\f$.\n *\n * Note that the term _quadrature_ also often refers to the quantity\n * \\f$Q[f]=\\int f(x)w(x)\\mathrm{d}x\\approx \\sum_k f_k w_k\\f$. Here, \\f$w(x)\\f$\n * denotes the basis-specific weight function w.r.t. to which the basis\n * functions \\f$\\Phi_k\\f$ are orthogonal, i.e \\f$\\int\\Phi_i(x)\\Phi_j(x)w(x)=0\\f$\n * for \\f$i\\neq j\\f$. The weights \\f$w_k\\f$ approximate this inner product. To\n * approximate the definite integral \\f$I[f]\\f$ we must employ the\n * coefficients \\f$\\frac{w_k}{w(\\xi_k)}\\f$ instead, where the \\f$\\xi_k\\f$ are\n * the collocation points. These are the coefficients this function returns.\n * Only for a unit weight function \\f$w(x)=1\\f$, i.e. a Legendre basis, is\n * \\f$I[f]=Q[f]\\f$ so this function returns the \\f$w_k\\f$ identically.\n *\n * For a `FiniteDifference` basis or `CellCentered` and `FaceCentered`\n * quadratures, the interpretation of the quadrature weights in term\n * of an approximation to \\f$I(q)\\f$ remains correct, but its explanation\n * in terms of orthonormal basis is not, i.e. we set \\f$w_k\\f$ to the grid\n * spacing at each point, and the inverse weight \\f$\\frac{1}{w(\\xi_k)}=1\\f$ to\n * recover the midpoint method for definite integrals.\n *\n * \\param num_points The number of collocation points\n */\ntemplate <Basis BasisType, Quadrature QuadratureType>\nconst DataVector& quadrature_weights(size_t num_points);\n\n/*!\n * \\brief Quadrature weights for a one-dimensional mesh.\n *\n * \\see quadrature_weights(size_t)\n */\nconst DataVector& quadrature_weights(const Mesh<1>& mesh);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/SegmentSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n#include <ostream>\n\nnamespace Spectral {\n\nstd::ostream& operator<<(std::ostream& os, SegmentSize segment_size) {\n  switch (segment_size) {\n    case SegmentSize::Uninitialized:\n      return os << \"Uninitialized\";\n    case SegmentSize::Full:\n      return os << \"Full\";\n    case SegmentSize::UpperHalf:\n      return os << \"UpperHalf\";\n    case SegmentSize::LowerHalf:\n      return os << \"LowerHalf\";\n    default:\n      ERROR(\n          \"Invalid SegmentSize. Expected one of: 'Uninitialized', 'Full', \"\n          \"'UpperHalf', 'LowerHalf'\");\n  }\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/SegmentSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <iosfwd>\n\nnamespace Spectral {\n\n/// \\brief The portion of a Segment (or the interval \\f$[-1, 1]\\f$)\n/// covered by a mesh.\n///\n/// \\details SegmentSize is used to denote the relationship between two meshes\n/// (in each dimension) such as:\n/// - a face mesh and a mortar mesh when computing boundary corrections on the\n///   mortars between neighboring elements.  In this case the face mesh will\n///   always have SegmentSize::Full, and the mortar mesh can have any value.\n/// - a parent mesh and a child mesh in a h-refinement hierarchy.  In this case\n///   the parent mesh will have SegmentSize::Full, while the child mesh will\n///   have either SegmentSize::UpperHalf or SegmentSize::LowerHalf.\n/// - for p-preojection, the source and target meshes should use\n///   SegmentSize::Full\nenum class SegmentSize : uint8_t {\n  Uninitialized = 0,\n  Full = 1,\n  UpperHalf = 2,\n  LowerHalf = 3\n};\n\nstd::ostream& operator<<(std::ostream& os, SegmentSize segment_size);\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/Spectral/SpectralQuantityForMesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\cond\n// clang-tidy: Macro arguments should be in parentheses, but we want to append\n// template parameters here.\n#define SPECTRAL_QUANTITY_FOR_MESH(function_name, return_type)           \\\n  const return_type& function_name(const Mesh<1>& mesh) {                \\\n    return Spectral::detail::get_spectral_quantity_for_mesh(             \\\n        [](const auto basis, const auto quadrature,                      \\\n           const size_t num_points) -> const return_type& {              \\\n          return function_name</* NOLINT */ decltype(basis)::value,      \\\n                               decltype(quadrature)::value>(num_points); \\\n        },                                                               \\\n        mesh);                                                           \\\n    }\n\n#define TWO_INDEXED_SPECTRAL_QUANTITY_FOR_MESH(function_name, return_type)   \\\n  const return_type& function_name(const Mesh<1>& mesh, const size_t m,      \\\n                                   const size_t N) {                         \\\n    return Spectral::detail::get_two_indexed_spectral_quantity_for_mesh(     \\\n        [](const auto basis, const auto quadrature, const size_t num_points, \\\n           const size_t mm, const size_t NN) -> const return_type& {         \\\n          return function_name</* NOLINT */ decltype(basis)::value,          \\\n                               decltype(quadrature)::value>(num_points, mm,  \\\n                                                            NN);             \\\n        },                                                                   \\\n        mesh, m, N);                                                         \\\n  }\n/// \\endcond\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/AngularOrdering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace ylm {\nstd::ostream& operator<<(std::ostream& os, const AngularOrdering ordering) {\n  switch (ordering) {\n    case AngularOrdering::Strahlkorper:\n      return os << \"Strahlkorper\";\n    case AngularOrdering::Cce:\n      return os << \"Cce\";\n    default:\n      ERROR(\"Unknown AngularOrdering type\");\n  }\n}\n}  // namespace ylm\n\ntemplate <>\nylm::AngularOrdering\nOptions::create_from_yaml<ylm::AngularOrdering>::create<void>(\n    const Options::Option& options) {\n  const auto ordering = options.parse_as<std::string>();\n  if (ordering == \"Strahlkorper\") {\n    return ylm::AngularOrdering::Strahlkorper;\n  } else if (ordering == \"Cce\") {\n    return ylm::AngularOrdering::Cce;\n  }\n  PARSE_ERROR(options.context(),\n              \"AngularOrdering must be 'Strahlkorper' or 'Cce'\");\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace ylm {\n/*!\n * \\brief Label for the ordering of spherical harmonic points on a sphere\n *\n * \\details `%Strahlkorper` refers to points on a sphere ordered by SPHEREPACK\n * because `Strahlkorper`s hold YlmSpherePacks internally. `%Cce` refers to\n * points on a sphere ordered by Libsharp because Cce uses Libsharp internally.\n */\nenum class AngularOrdering { Strahlkorper, Cce };\nstd::ostream& operator<<(std::ostream& os, AngularOrdering ordering);\n}  // namespace ylm\n\ntemplate <>\nstruct Options::create_from_yaml<ylm::AngularOrdering> {\n  template <typename Metavariables>\n  static ylm::AngularOrdering create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nylm::AngularOrdering\nOptions::create_from_yaml<ylm::AngularOrdering>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/ApplyTensorYlmFilter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace ylm {\nclass Spherepack;\n}  // namespace ylm\n/// \\endcond\n\nnamespace ylm::TensorYlm::filter_detail {\n\n/*!\n * \\brief Does a nodal to modal transform on I1xS2, only in the S2 direction.\n *\n * \\tparam VarsList A tmpl::list of tags to transform.\n * \\param nodal A Variables containing the input nodal values.\n * \\param modal A Variables containing the output modal values.\n * \\param ylm A Spherepack object with the correct ell_max.\n * \\param radial_extents Number of grid points in I1, or 1 if just an S2.\n */\ntemplate <typename VarsList>\nvoid nodal_to_modal_ylm(gsl::not_null<Variables<VarsList>*> modal,\n                        const Variables<VarsList>& nodal,\n                        const ::ylm::Spherepack& ylm, size_t radial_extents);\n\n/*!\n * \\brief Does a modal to nodal transform on I1xS2, only in the S2 direction.\n *\n * \\tparam VarsList A tmpl::list of tags to transform.\n * \\param modal A Variables containing the input modal values.\n * \\param nodal A Variables containing the output nodal values.\n * \\param ylm A Spherepack object with the correct ell_max.\n * \\param radial_extents Number of grid points in I1, or 1 if just an S2.\n */\ntemplate <typename VarsList>\nvoid modal_to_nodal_ylm(gsl::not_null<Variables<VarsList>*> nodal,\n                        const Variables<VarsList>& modal,\n                        const ::ylm::Spherepack& ylm, size_t radial_extents);\n}  // namespace ylm::TensorYlm::filter_detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/ApplyTensorYlmFilter.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n#pragma once\n\n#include \"NumericalAlgorithms/SphericalHarmonics/ApplyTensorYlmFilter.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ylm::TensorYlm::filter_detail {\n\ntemplate <typename VarsList>\nvoid nodal_to_modal_ylm(const gsl::not_null<Variables<VarsList>*> modal,\n                        const Variables<VarsList>& nodal,\n                        const ::ylm::Spherepack& ylm,\n                        const size_t radial_extents) {\n  tmpl::for_each<VarsList>([&nodal, &modal, radial_extents,\n                            &ylm]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n    constexpr size_t num_independent_components = Tag::type::structure::size();\n    for (size_t storage_index = 0; storage_index < num_independent_components;\n         ++storage_index) {\n      const double* src = get<Tag>(nodal)[storage_index].data();\n      double* dest = get<Tag>(*modal)[storage_index].data();\n      ylm.phys_to_spec_all_offsets(make_not_null(dest), make_not_null(src),\n                                   radial_extents);\n    }\n  });\n}\n\ntemplate <typename VarsList>\nvoid modal_to_nodal_ylm(const gsl::not_null<Variables<VarsList>*> nodal,\n                        const Variables<VarsList>& modal,\n                        const ::ylm::Spherepack& ylm,\n                        const size_t radial_extents) {\n  tmpl::for_each<VarsList>([&nodal, &modal, radial_extents,\n                            &ylm]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n    constexpr size_t num_independent_components = Tag::type::structure::size();\n    for (size_t storage_index = 0; storage_index < num_independent_components;\n         ++storage_index) {\n      const double* src = get<Tag>(modal)[storage_index].data();\n      double* dest = get<Tag>(*nodal)[storage_index].data();\n      ylm.spec_to_phys_all_offsets(make_not_null(dest), make_not_null(src),\n                                   radial_extents);\n    }\n  });\n}\n}  // namespace ylm::TensorYlm::filter_detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SphericalHarmonics)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AngularOrdering.cpp\n  ChangeCenterOfStrahlkorper.cpp\n  RealSphericalHarmonics.cpp\n  SpherepackIterator.cpp\n  Strahlkorper.cpp\n  StrahlkorperFunctions.cpp\n  Spherepack.cpp\n  SpherepackCache.cpp\n  SpherepackHelper.cpp\n  Tags.cpp\n  TensorYlmCartToSphere.cpp\n  TensorYlmFilter.cpp\n  TensorYlmHelpers.cpp\n  TensorYlmSphereToCart.cpp\n  WignerThreeJ.cpp\n  YlmToStf.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AngularOrdering.hpp\n  ApplyTensorYlmFilter.hpp\n  ApplyTensorYlmFilter.tpp\n  ChangeCenterOfStrahlkorper.hpp\n  RealSphericalHarmonics.hpp\n  SpherepackIterator.hpp\n  Strahlkorper.hpp\n  StrahlkorperFunctions.hpp\n  Spherepack.hpp\n  SpherepackCache.hpp\n  SpherepackHelper.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  TagsTypeAliases.hpp\n  TensorYlm.hpp\n  TensorYlmCartToSphere.hpp\n  TensorYlmFilter.hpp\n  TensorYlmHelpers.hpp\n  TensorYlmSphereToCart.hpp\n  WignerThreeJ.hpp\n  YlmToStf.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  BLAS::BLAS\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  slatec\n  SPHEREPACK\n  Utilities\n  PRIVATE\n  FiniteDifference\n  Interpolation\n  )\n\nadd_subdirectory(IO)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/ChangeCenterOfStrahlkorper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/ChangeCenterOfStrahlkorper.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ylm {\nnamespace {\n\n// Get the r_hat vector of the Strahlkorper, which defines the\n// collocation points.\ntemplate <typename Frame>\ntnsr::i<DataVector, 3, Frame> get_rhat(\n    const Strahlkorper<Frame>& strahlkorper) {\n  const auto new_theta_phi = strahlkorper.ylm_spherepack().theta_phi_points();\n  const DataVector sin_theta = sin(new_theta_phi[0]);\n  return tnsr::i<DataVector, 3, Frame>{{sin_theta * cos(new_theta_phi[1]),\n                                        sin_theta * sin(new_theta_phi[1]),\n                                        cos(new_theta_phi[0])}};\n}\n\n// Work function called by the two interface functions.\ntemplate <typename Frame>\nvoid change_expansion_center(\n    const gsl::not_null<Strahlkorper<Frame>*> strahlkorper,\n    const std::array<double, 3>& new_center,\n    const tnsr::i<DataVector, 3, Frame>& r_hat) {\n  // Get new_center minus old_center.\n  const auto& old_center = strahlkorper->expansion_center();\n  const std::array<double, 3> center_difference{\n      {new_center[0] - old_center[0], new_center[1] - old_center[1],\n       new_center[2] - old_center[2]}};\n\n  // For bracketing the root, get the min and max radius with respect\n  // to the new center.\n  const auto [r_min, r_max] = [&center_difference, &r_hat, &strahlkorper]() {\n    const auto radius_old = strahlkorper->ylm_spherepack().spec_to_phys(\n        strahlkorper->coefficients());\n    DataVector radius_new =\n        square(get<0>(r_hat) * radius_old - center_difference[0]);\n    for (size_t d = 1; d < 3; ++d) {  // Start at 1; already did zero.\n      radius_new +=\n          square(r_hat.get(d) * radius_old - gsl::at(center_difference, d));\n    }\n    radius_new = sqrt(radius_new);\n    const auto minmax =\n        std::minmax_element(radius_new.begin(), radius_new.end());\n    return std::make_pair(*(minmax.first), *(minmax.second));\n  }();\n\n  // Find the coordinate radius of the surface, with respect to the\n  // new center, at each of the angular collocation points of the\n  // surface with respect to the new center. To do so, for each index\n  // 's' (corresponding to an angular collocation point), find the\n  // root 'r_new' that zeroes this lambda function.\n  const auto radius_function = [&center_difference, &r_hat, &strahlkorper](\n                                   const double r_new, const size_t s) {\n    // Get cartesian coordinates of the point with respect to the old\n    // center.\n    const std::array<double, 3> x_old{\n        {r_new * get<0>(r_hat)[s] + center_difference[0],\n         r_new * get<1>(r_hat)[s] + center_difference[1],\n         r_new * get<2>(r_hat)[s] + center_difference[2]}};\n\n    // Find (r, theta, phi) of the same point with respect to the old_center.\n    const double r_old =\n        sqrt(square(x_old[0]) + square(x_old[1]) + square(x_old[2]));\n    const double theta_old = acos(x_old[2] / r_old);\n    const double phi_old = atan2(x_old[1], x_old[0]);\n\n    // Evaluate the radius of the surface on the old strahlkorper\n    // at theta_old, phi_old.\n    const double r_surf_old = strahlkorper->radius(theta_old, phi_old);\n\n    // If r_surf_old is r_old, then 'r_new' is on the surface.\n    return r_surf_old - r_old;\n  };\n\n  // We try to bracket the root between r_min and r_max.\n  // But r_min and r_max are only approximate, and there may be grid points\n  // with radii outside that range. So we pad by 10% to be safe.\n  const double padding = 0.10;\n\n  const auto radius_at_each_angle = RootFinder::toms748<false>(\n      radius_function,\n      make_with_value<DataVector>(get<0>(r_hat), r_min * (1.0 - padding)),\n      make_with_value<DataVector>(get<0>(r_hat), r_max * (1.0 + padding)),\n      std::numeric_limits<double>::epsilon() * (r_min + r_max),\n      2.0 * std::numeric_limits<double>::epsilon());\n\n  // Now reset the radius and center of the new strahlkorper.\n  *strahlkorper =\n      Strahlkorper<Frame>(strahlkorper->l_max(), strahlkorper->m_max(),\n                          radius_at_each_angle, new_center);\n}\n}  // namespace\n\ntemplate <typename Frame>\nvoid change_expansion_center_of_strahlkorper(\n    const gsl::not_null<Strahlkorper<Frame>*> strahlkorper,\n    const std::array<double, 3>& new_center) {\n  change_expansion_center(strahlkorper, new_center, get_rhat(*strahlkorper));\n}\n\ntemplate <typename Frame>\nvoid change_expansion_center_of_strahlkorper_to_physical(\n    const gsl::not_null<Strahlkorper<Frame>*> strahlkorper,\n    double relative_tolerance) {\n  const auto r_hat = get_rhat(*strahlkorper);\n\n  // Zeroth iteration.\n  change_expansion_center(strahlkorper, strahlkorper->physical_center(), r_hat);\n\n  // In the random number tests, it never needed more than 7\n  // iterations to converge to relative error of roundoff.  Allow 14\n  // iterations to be safe.\n  const size_t maxiter = 14;\n  for (size_t iter = 0; iter < maxiter; ++iter) {\n    const auto phys_center = strahlkorper->physical_center();\n    const auto exp_center = strahlkorper->expansion_center();\n    const auto average_radius = strahlkorper->average_radius();\n    const double relative_error = sqrt(square(exp_center[0] - phys_center[0]) +\n                                       square(exp_center[1] - phys_center[1]) +\n                                       square(exp_center[2] - phys_center[2])) /\n                                  average_radius;\n    if (relative_error <= relative_tolerance) {\n      return;\n    }\n    change_expansion_center(strahlkorper, phys_center, r_hat);\n  }\n  ERROR(\"Too many iterations in change_expansion_center_of_strahlkorper\");\n}\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template void change_expansion_center_of_strahlkorper(             \\\n      const gsl::not_null<Strahlkorper<FRAME(data)>*> strahlkorper,  \\\n      const std::array<double, 3>& new_center);                      \\\n  template void change_expansion_center_of_strahlkorper_to_physical( \\\n      const gsl::not_null<Strahlkorper<FRAME(data)>*> strahlkorper,  \\\n      double relative_tolerance);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (::Frame::Inertial, ::Frame::Distorted, ::Frame::Grid))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/ChangeCenterOfStrahlkorper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n\n/// \\cond\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ylm {\n/// Changes the expansion center of a Strahlkorper, where the\n/// expansion center is defined as the point about which the spectral\n/// basis of the Strahlkorper is expanded, which is the quantity\n/// returned by `Strahlkorper::expansion_center()`.\ntemplate <typename Frame>\nvoid change_expansion_center_of_strahlkorper(\n    gsl::not_null<Strahlkorper<Frame>*> strahlkorper,\n    const std::array<double, 3>& new_center);\n\n/// Changes the expansion center of a Strahlkorper to the physical\n/// center.  Because `Strahlkorper::physical_center()` returns only an\n/// approximate quantity,\n/// `change_expansion_center_of_strahlkorper_to_physical` is\n/// iterative, and does not return exactly the same result as passing\n/// `Strahlkorper::physical_center()` to\n/// `change_expansion_center_of_strahlkorper`.\ntemplate <typename Frame>\nvoid change_expansion_center_of_strahlkorper_to_physical(\n    gsl::not_null<Strahlkorper<Frame>*> strahlkorper,\n    double relative_tolerance = 2.0 * std::numeric_limits<double>::epsilon());\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/IO/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SphericalHarmonicsIO)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FillYlmLegendAndData.cpp\n  ReadSurfaceYlm.cpp\n  StrahlkorperCoordsToTextFile.cpp\n  StrahlkorperFromFile.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FillYlmLegendAndData.hpp\n  ReadSurfaceYlm.hpp\n  StrahlkorperCoordsToTextFile.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ErrorHandling\n  H5\n  PUBLIC\n  SphericalHarmonics\n  Utilities\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace ylm {\ntemplate <typename Frame>\nvoid fill_ylm_legend_and_data(\n    const gsl::not_null<std::vector<std::string>*> legend,\n    const gsl::not_null<std::vector<double>*> data,\n    const ylm::Strahlkorper<Frame>& strahlkorper, const double time,\n    const size_t max_l) {\n  ASSERT(max_l >= strahlkorper.l_max(),\n         \"The Lmax of the Ylm data to write (\"\n             << strahlkorper.l_max()\n             << \") is larger than the maximum value that l can be (\" << max_l\n             << \").\");\n  const std::array<double, 3> expansion_center =\n      strahlkorper.expansion_center();\n  const std::string frame{get_output(Frame{})};\n  // we only store half and thus only write half of the coefficients\n  const size_t num_coefficients =\n      ylm::Spherepack::spectral_size(max_l, max_l) / 2;\n  // time + 3 dims of expansion center + Lmax = 5 columns\n  const size_t num_columns = num_coefficients + 5;\n\n  legend->reserve(num_columns);\n  data->reserve(num_columns);\n\n  legend->emplace_back(\"Time\");\n  data->emplace_back(time);\n  legend->emplace_back(frame + \"ExpansionCenter_x\");\n  data->emplace_back(expansion_center[0]);\n  legend->emplace_back(frame + \"ExpansionCenter_y\");\n  data->emplace_back(expansion_center[1]);\n  legend->emplace_back(frame + \"ExpansionCenter_z\");\n  data->emplace_back(expansion_center[2]);\n  legend->emplace_back(\"Lmax\");\n  data->emplace_back(strahlkorper.l_max());\n\n  const DataVector& ylm_coefficients = strahlkorper.coefficients();\n  // fill coefficients for l in [0, l_max]\n  // l_max == m_max\n  ylm::SpherepackIterator iter(strahlkorper.l_max(), strahlkorper.l_max());\n  for (size_t l = 0; l <= strahlkorper.l_max(); l++) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n      legend->emplace_back(MakeString{} << \"coef(\" << l << \",\" << m << \")\");\n\n      iter.set(l, m);\n      data->emplace_back(ylm_coefficients[iter()]);\n    }\n  }\n  // fill coefficients for l in [l_max + 1, max_l],\n  // i.e. higher order coefficients beyond this Strahlkorper's l_max == 0.0\n  data->resize(num_columns, 0.0);\n  for (size_t l = strahlkorper.l_max() + 1; l <= max_l; l++) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n      legend->emplace_back(MakeString{} << \"coef(\" << l << \",\" << m << \")\");\n    }\n  }\n}\n}  // namespace ylm\n\n#define FRAMETYPE(instantiation_data) BOOST_PP_TUPLE_ELEM(0, instantiation_data)\n\n#define INSTANTIATE(_, instantiation_data)                                  \\\n  template void ylm::fill_ylm_legend_and_data(                              \\\n      gsl::not_null<std::vector<std::string>*> legend,                      \\\n      gsl::not_null<std::vector<double>*> data,                             \\\n      const ylm::Strahlkorper<FRAMETYPE(instantiation_data)>& strahlkorper, \\\n      double time, size_t max_l);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Inertial, Frame::Distorted))\n\n#undef INSTANTIATE\n#undef FRAMETYPE\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm {\n/*!\n * \\brief Fills the legend and row of spherical harmonic data to write to disk\n *\n * The number of coefficients to write is based on `max_l`, the maximum value\n * that the input `strahlkorper` could possibly have. When\n * `strahlkorper.l_max() < max_l`, coefficients with \\f$l\\f$ higher than\n * `strahlkorper.l_max()` will simply be zero. Assuming the same `max_l` is\n * always used for a given surface, we will always write the same number of\n * columns for each row, as `max_l` sets the number of columns to write\n */\ntemplate <typename Frame>\nvoid fill_ylm_legend_and_data(gsl::not_null<std::vector<std::string>*> legend,\n                              gsl::not_null<std::vector<double>*> data,\n                              const ylm::Strahlkorper<Frame>& strahlkorper,\n                              double time, size_t max_l);\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/IO/ReadSurfaceYlm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/ReadSurfaceYlm.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <numeric>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace ylm {\nnamespace {\nconst size_t num_non_coef_headers = 5;\n\n// check that the headers of the legend are what we expect them to be\ntemplate <typename Frame>\nvoid check_legend(const std::vector<std::string>& legend,\n                  const bool check_frame = true) {\n  const std::string expected_frame{get_output(Frame{})};\n\n  // check format and ordering of non-coefficient headers\n  const auto check_header = [&legend](const size_t column,\n                                      const std::string& expected_header,\n                                      const bool check = true) {\n    const std::string& read_header = gsl::at(legend, column);\n    if (check and read_header != expected_header) {\n      ERROR(\"In column \" << column << \" of the Ylm legend, expected header \"\n                         << expected_header << \" but got header \" << read_header\n                         << \".\");\n    }\n  };\n  check_header(0, \"Time\");\n  check_header(1, expected_frame + \"ExpansionCenter_x\", check_frame);\n  check_header(2, expected_frame + \"ExpansionCenter_y\", check_frame);\n  check_header(3, expected_frame + \"ExpansionCenter_z\", check_frame);\n  check_header(4, \"Lmax\");\n\n  // check format and ordering of coefficient headers\n  int expected_l = 0;\n  int expected_m = 0;\n  size_t coef_column_number = num_non_coef_headers;\n  while (coef_column_number < legend.size()) {\n    const std::string& coefficient_header = legend[coef_column_number];\n    const std::string expected_coefficient_header = {\n        MakeString{} << \"coef(\" << std::to_string(expected_l) << \",\"\n                     << std::to_string(expected_m) << \")\"};\n    if (coefficient_header != expected_coefficient_header) {\n      ERROR(MakeString{}\n            << \"In column \" << coef_column_number\n            << \" of the Ylm legend, expected header \"\n            << expected_coefficient_header << \" but got header \"\n            << coefficient_header\n            << \".\\n\\n Coefficient headers are expected to take the form \"\n               \"coef(l,m) and be ordered first by ascending l and then for \"\n               \"each l, ordered by ascending m, i.e. coef(0,0), coef(1,-1), \"\n               \"coef(1,0), coef(1,1), coef(2,-2), ...\");\n    }\n\n    if (expected_l == expected_m) {\n      expected_l++;\n      expected_m = -expected_l;\n    } else {\n      expected_m++;\n    }\n\n    coef_column_number++;\n  }\n}\n\n// checks if a double is a nonnegative integer\nbool is_nonnegative_int(const double n) {\n  return n == abs(n) and n == floor(n);\n}\n\ntemplate <typename Frame>\nStrahlkorper<Frame> read_surface_ylm_row(const Matrix& ylm_data,\n                                         const size_t row_number) {\n  const size_t l_max_column_number = 4;\n  const std::array<double, 3> expansion_center{{ylm_data(row_number, 1),\n                                                ylm_data(row_number, 2),\n                                                ylm_data(row_number, 3)}};\n  const double l_max_from_file = ylm_data(row_number, l_max_column_number);\n  if (not is_nonnegative_int(l_max_from_file)) {\n    ERROR(\"Row \" << row_number << \" of the Ylm data has an invalid Lmax value (\"\n                 << l_max_from_file << \") in column \" << l_max_column_number\n                 << \". The value of Lmax should be a nonnegative integer.\");\n  }\n\n  const auto l_max = static_cast<size_t>(l_max_from_file);\n  // l_max == m_max\n  const size_t spectral_size = Spherepack::spectral_size(l_max, l_max);\n  // We only write and store half of the coefficients. This is the minimum\n  // number of coefficients needed to describe the strahlkorper given the l_max,\n  // i.e. columns of 0.0 for higher coefficients are okay\n  const size_t min_expected_num_coefficients = spectral_size / 2;\n  const size_t min_expected_num_columns =\n      min_expected_num_coefficients + num_non_coef_headers;\n  const size_t actual_num_columns = ylm_data.columns();\n\n  if (actual_num_columns < min_expected_num_columns) {\n    ERROR(\"Row \" << row_number\n                 << \" of the Ylm data does not have enough coefficients for \"\n                    \"the Lmax. For Lmax = \"\n                 << l_max << \", expected at least \"\n                 << min_expected_num_coefficients << \" coefficient columns and \"\n                 << min_expected_num_columns << \" total columns.\");\n  }\n\n  ModalVector spectral_coefficients(spectral_size, 0.0);\n  size_t coef_column_number = num_non_coef_headers;\n  SpherepackIterator iter(l_max, l_max);\n  // read in expected coefficients for the given l_max\n  for (size_t l = 0; l <= l_max; l++) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n      iter.set(l, m);\n\n      const double coefficient = ylm_data(row_number, coef_column_number);\n      spectral_coefficients[iter()] = coefficient;\n\n      coef_column_number++;\n    }\n  }\n  // make sure any higher order coefficients (l > l_max) present in the data\n  // that was read in are 0.0\n  while (coef_column_number < actual_num_columns) {\n    if (ylm_data(row_number, coef_column_number) != 0.0) {\n      ERROR(\"Row \" << row_number << \" of the Ylm data has Lmax = \" << l_max\n                   << \" but non-zero coefficients for l > Lmax.\");\n    }\n    coef_column_number++;\n  }\n\n  Strahlkorper<Frame> strahlkorper(l_max, l_max, spectral_coefficients,\n                                   expansion_center);\n\n  return strahlkorper;\n}\n}  // namespace\n\ntemplate <typename Frame>\nylm::Strahlkorper<Frame> read_surface_ylm_single_time(\n    const std::string& file_name, const std::string& surface_subfile_name,\n    const double time, const double relative_epsilon, const bool check_frame) {\n  h5::H5File<h5::AccessType::ReadOnly> file{file_name};\n  const std::string ylm_subfile_name{std::string{\"/\"} + surface_subfile_name};\n  const auto& ylm_file = file.get<h5::Dat>(ylm_subfile_name);\n  const auto& ylm_legend = ylm_file.get_legend();\n  check_legend<Frame>(ylm_legend, check_frame);\n\n  std::vector<size_t> columns(ylm_legend.size());\n  std::iota(std::begin(columns), std::end(columns), 0);\n\n  const auto& dimensions = ylm_file.get_dimensions();\n  const auto num_rows = static_cast<size_t>(dimensions[0]);\n  const Matrix times =\n      ylm_file.get_data_subset(std::vector<size_t>{0_st}, 0, num_rows);\n\n  std::optional<size_t> row_number{};\n  for (size_t i = 0; i < num_rows; i++) {\n    if (equal_within_roundoff(time, times(i, 0), relative_epsilon, time)) {\n      if (row_number.has_value()) {\n        ERROR(\"Found more than one time in the subfile \"\n              << surface_subfile_name << \" of the H5 file \" << file_name\n              << \" that is within a relative_epsilon of \" << relative_epsilon\n              << \" of the time requested \" << time);\n      }\n\n      row_number = i;\n    }\n  }\n\n  if (not row_number.has_value()) {\n    ERROR(\"Could not find time \" << time << \" in subfile \"\n                                 << surface_subfile_name << \" of H5 file \"\n                                 << file_name << \". Available times are:\\n\"\n                                 << times);\n  }\n\n  const Matrix data = ylm_file.get_data_subset(columns, row_number.value());\n\n  // Zero for row number because this matrix only has one row\n  return read_surface_ylm_row<Frame>(data, 0);\n}\n\ntemplate <typename Frame>\nstd::vector<Strahlkorper<Frame>> read_surface_ylm(\n    const std::string& file_name, const std::string& surface_subfile_name,\n    const size_t requested_number_of_times_from_end) {\n  ASSERT(requested_number_of_times_from_end > 0,\n         \"Must request to read in at least one row (time) of Ylm data.\");\n\n  h5::H5File<h5::AccessType::ReadOnly> file{file_name};\n  const std::string ylm_subfile_name{std::string{\"/\"} + surface_subfile_name};\n  const auto& ylm_file = file.get<h5::Dat>(ylm_subfile_name);\n\n  // number of rows available to read in\n  const size_t total_number_of_times = gsl::at(ylm_file.get_dimensions(), 0);\n  if (total_number_of_times == 0) {\n    ERROR(\"The Ylm data to read from contain 0 rows (times) of data.\");\n  }\n\n  if (requested_number_of_times_from_end > total_number_of_times) {\n    ERROR(\"The requested number of time values (\"\n          << requested_number_of_times_from_end\n          << \") is more than the number of rows in the Ylm data that was read \"\n             \"in (\"\n          << total_number_of_times << \")\");\n  }\n\n  const auto& ylm_legend = ylm_file.get_legend();\n  check_legend<Frame>(ylm_legend);\n\n  std::vector<size_t> columns(ylm_legend.size());\n  std::iota(std::begin(columns), std::end(columns), 0);\n  // grab all columns of the last requested_number_of_times_from_end rows\n  const auto ylm_data_subset = ylm_file.get_data_subset(\n      columns, total_number_of_times - requested_number_of_times_from_end,\n      requested_number_of_times_from_end);\n\n  std::vector<Strahlkorper<Frame>> strahlkorpers(\n      requested_number_of_times_from_end);\n  for (size_t i = 0; i < requested_number_of_times_from_end; i++) {\n    strahlkorpers[i] = read_surface_ylm_row<Frame>(ylm_data_subset, i);\n  }\n\n  file.close_current_object();\n  return strahlkorpers;\n}\n}  // namespace ylm\n\n#define FRAMETYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template std::vector<ylm::Strahlkorper<FRAMETYPE(data)>>                   \\\n  ylm::read_surface_ylm<>(const std::string& file_name,                      \\\n                          const std::string& surface_subfile_name,           \\\n                          size_t requested_number_of_times_from_end);        \\\n  template ylm::Strahlkorper<FRAMETYPE(data)>                                \\\n  ylm::read_surface_ylm_single_time<>(                                       \\\n      const std::string& file_name, const std::string& surface_subfile_name, \\\n      double time, double relative_epsilon, bool check_frame);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Inertial, Frame::Distorted))\n\n#undef INSTANTIATE\n#undef FRAMETYPE\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/IO/ReadSurfaceYlm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n\nnamespace ylm {\n/// \\ingroup SurfacesGroup\n/// \\brief Returns a list of `ylm::Strahlkorper`s constructed from reading in\n/// spherical harmonic data for a surface at a requested list of times\n///\n/// \\details The `ylm::Strahlkorper`s are constructed by reading in data from\n/// an H5 subfile that is expected to be in the format described by\n/// `intrp::callbacks::ObserveSurfaceData`. It is assumed that\n/// \\f$l_{max} = m_{max}\\f$.\n///\n/// \\param file_name name of the H5 file containing the surface's spherical\n/// harmonic data\n/// \\param surface_subfile_name name of the subfile (with no leading slash nor\n/// the `.dat` extension) within `file_name` that contains the surface's\n/// spherical harmonic data to read in\n/// \\param requested_number_of_times_from_end the number of times to read in\n/// starting backwards from the final time found in `surface_subfile_name`\ntemplate <typename Frame>\nstd::vector<ylm::Strahlkorper<Frame>> read_surface_ylm(\n    const std::string& file_name, const std::string& surface_subfile_name,\n    size_t requested_number_of_times_from_end);\n\n/*!\n * \\brief Similar to `ylm::read_surface_ylm`, this reads in spherical harmonic\n * data for a surface and constructs a `ylm::Strahlkorper`. However, this\n * function only does it at a specific time and returns a single\n * `ylm::Strahlkorper`.\n *\n * \\note If two times are found within \\p epsilon of the \\p time, then an error\n * will occur. Similarly, if no \\p time is found within the \\p epsilon, then an\n * error will occur as well.\n *\n * \\param file_name name of the H5 file containing the surface's spherical\n * harmonic data\n * \\param surface_subfile_name name of the subfile (with no leading slash nor\n * the `.dat` extension) within `file_name` that contains the surface's\n * spherical harmonic data to read in\n * \\param time Time to read the coefficients at.\n * \\param relative_epsilon How much error is allowed when looking for a specific\n * time. This is useful so users don't have to know the specific time to machine\n * precision.\n * \\param check_frame Whether to check the frame in the subfile or not.\n */\ntemplate <typename Frame>\nylm::Strahlkorper<Frame> read_surface_ylm_single_time(\n    const std::string& file_name, const std::string& surface_subfile_name,\n    double time, double relative_epsilon, bool check_frame = true);\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/IO/StrahlkorperCoordsToTextFile.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/StrahlkorperCoordsToTextFile.hpp\"\n\n#include <fstream>\n#include <iomanip>\n#include <limits>\n#include <ostream>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace Frame {\nstruct Inertial;\nstruct Distorted;\nstruct Grid;\n}  // namespace Frame\n\nnamespace ylm {\ntemplate <typename Frame>\nvoid write_strahlkorper_coords_to_text_file(\n    const Strahlkorper<Frame>& strahlkorper,\n    const std::string& output_file_name, const AngularOrdering ordering,\n    const bool overwrite_file) {\n  if (not overwrite_file and\n      file_system::check_if_file_exists(output_file_name)) {\n    ERROR_NO_TRACE(\"The output file \" << output_file_name\n                                      << \" already exists.\");\n  }\n\n  tnsr::I<DataVector, 3, Frame> cartesian_coords =\n      ylm::cartesian_coords(strahlkorper);\n\n  // Cce expects coordinates in a different order than a typical Strahlkorper\n  if (ordering == AngularOrdering::Cce) {\n    const auto physical_extents =\n        strahlkorper.ylm_spherepack().physical_extents();\n    auto transposed_coords =\n        tnsr::I<DataVector, 3, Frame>(get<0>(cartesian_coords).size());\n    for (size_t i = 0; i < 3; ++i) {\n      transpose(make_not_null(&transposed_coords.get(i)),\n                cartesian_coords.get(i), physical_extents[0],\n                physical_extents[1]);\n    }\n\n    cartesian_coords = std::move(transposed_coords);\n  }\n\n  std::ofstream output_file(output_file_name);\n  output_file << std::fixed\n              << std::setprecision(std::numeric_limits<double>::digits10 + 4)\n              << std::scientific;\n\n  const size_t num_points = get<0>(cartesian_coords).size();\n  for (size_t i = 0; i < num_points; i++) {\n    output_file << get<0>(cartesian_coords)[i] << \" \"\n                << get<1>(cartesian_coords)[i] << \" \"\n                << get<2>(cartesian_coords)[i] << std::endl;\n  }\n}\n\nvoid write_strahlkorper_coords_to_text_file(const double radius,\n                                            const size_t l_max,\n                                            const std::array<double, 3>& center,\n                                            const std::string& output_file_name,\n                                            const AngularOrdering ordering,\n                                            const bool overwrite_file) {\n  const Strahlkorper<Frame::Inertial> strahlkorper{l_max, radius, center};\n  write_strahlkorper_coords_to_text_file(strahlkorper, output_file_name,\n                                         ordering, overwrite_file);\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void write_strahlkorper_coords_to_text_file(                    \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                       \\\n      const std::string& output_file_name, const AngularOrdering ordering, \\\n      const bool overwrite_file);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/IO/StrahlkorperCoordsToTextFile.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n\nnamespace ylm {\n/// @{\n/*!\n * \\brief Writes the collocation points of a `ylm::Strahlkorper` to an output\n * text file.\n *\n * \\details The ordering of the points can be either the typical\n * `ylm::Spherepack` ordering or CCE ordering that works with libsharp. Also, an\n * error will occur if the output file already exists, but the output file can\n * be overwritten with the \\p overwrite_file option.\n *\n * The second overload will construct a spherical `ylm::Strahlkorper` with the\n * given \\p radius, \\p l_max, and \\p center.\n *\n * The output file format will be as follows with no comment or header lines,\n * a space as the delimiter, and decimals written in scientific notation:\n *\n * ```\n * x0 y0 z0\n * x1 y1 z1\n * x2 y2 z2\n * ...\n * ```\n */\ntemplate <typename Frame>\nvoid write_strahlkorper_coords_to_text_file(\n    const Strahlkorper<Frame>& strahlkorper,\n    const std::string& output_file_name, AngularOrdering ordering,\n    bool overwrite_file = false);\n\nvoid write_strahlkorper_coords_to_text_file(double radius, size_t l_max,\n                                            const std::array<double, 3>& center,\n                                            const std::string& output_file_name,\n                                            AngularOrdering ordering,\n                                            bool overwrite_file = false);\n/// @}\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/IO/StrahlkorperFromFile.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n\n#include <string>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/ReadSurfaceYlm.hpp\"\n#include \"Options/Context.hpp\"\n\nnamespace Frame {\nstruct Distorted;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace ylm {\ntemplate <typename Frame>\nStrahlkorper<Frame>::Strahlkorper(const size_t l_max,\n                                  const std::string& h5_filename,\n                                  const std::string& subfile_name,\n                                  const double time, const double time_epsilon,\n                                  const bool check_frame,\n                                  const Options::Context& /*context*/)\n    : Strahlkorper(\n          l_max, l_max,\n          ylm::read_surface_ylm_single_time<Frame>(\n              h5_filename, subfile_name, time, time_epsilon, check_frame)) {}\n\ntemplate class Strahlkorper<Frame::Inertial>;\ntemplate class Strahlkorper<Frame::Grid>;\ntemplate class Strahlkorper<Frame::Distorted>;\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Python/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Python/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Python/StrahlkorperFunctions.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  ylm::py_bindings::bind_strahlkorper(m);\n  ylm::py_bindings::bind_strahlkorper_functions(m);\n  ylm::py_bindings::bind_spherepack(m);\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PySphericalHarmonics\")\n\nspectre_python_add_module(\n  SphericalHarmonics\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  Spherepack.cpp\n  Strahlkorper.cpp\n  StrahlkorperFunctions.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Spherepack.hpp\n  Strahlkorper.hpp\n  StrahlkorperFunctions.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  pybind11::module\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/Spherepack.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Python/Spherepack.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n\nnamespace py = pybind11;\n\nnamespace ylm::py_bindings {\nvoid bind_spherepack(pybind11::module& m) {  // NOLINT\n  py::class_<ylm::Spherepack>(m, \"Spherepack\")\n      .def(py::init<size_t, size_t>(), py::arg(\"l_max\"), py::arg(\"m_max\"))\n      .def_property_readonly(\"l_max\", &ylm::Spherepack::l_max)\n      .def_property_readonly(\"m_max\", &ylm::Spherepack::m_max)\n      .def_property_readonly(\"physical_size\",\n                             [](const ylm::Spherepack& spherepack) {\n                               return spherepack.physical_size();\n                             })\n      .def_property_readonly(\"spectral_size\",\n                             [](const ylm::Spherepack& spherepack) {\n                               return spherepack.spectral_size();\n                             })\n      .def_property_readonly(\"theta_phi_points\",\n                             &ylm::Spherepack::theta_phi_points)\n      .def(\"phys_to_spec\",\n           static_cast<DataVector (ylm::Spherepack::*)(\n               const DataVector& collocation_values,\n               const size_t physical_stride, const size_t physical_offset)\n                           const>(&ylm::Spherepack::phys_to_spec),\n           py::arg(\"collocation_values\"), py::arg(\"physical_stride\") = 1,\n           py::arg(\"physical_offset\") = 0)\n      .def(\"spec_to_phys\",\n           static_cast<DataVector (ylm::Spherepack::*)(\n               const DataVector& spectral_coefs, const size_t spectral_stride,\n               const size_t spectral_offset) const>(\n               &ylm::Spherepack::spec_to_phys),\n           py::arg(\"spectral_coefs\"), py::arg(\"spectral_stride\") = 1,\n           py::arg(\"spectral_offset\") = 0)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self);\n\n  py::class_<ylm::SpherepackIterator>(m, \"SpherepackIterator\")\n      .def(py::init<size_t, size_t, size_t>(), py::arg(\"l_max\"),\n           py::arg(\"m_max\"), py::arg(\"physical_stride\") = 1)\n      .def(\"__call__\", &SpherepackIterator::operator())\n      .def(\n          \"set\",\n          [](ylm::SpherepackIterator& spherepack, const size_t l_input,\n             const int m_input) { spherepack.set(l_input, m_input); },\n          py::arg(\"l\"), py::arg(\"m\"));\n}\n}  // namespace ylm::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/Spherepack.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace ylm::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_spherepack(pybind11::module& m);\n}  // namespace ylm::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/Strahlkorper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Python/Strahlkorper.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/ReadSurfaceYlm.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/StrahlkorperCoordsToTextFile.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace py = pybind11;\n\nnamespace ylm::py_bindings {\nnamespace {\ntemplate <typename Frame>\nvoid bind_strahlkorper_impl(pybind11::module& m) {  // NOLINT\n  using Strahlkorper = ylm::Strahlkorper<Frame>;\n  py::class_<Strahlkorper>(m, (\"Strahlkorper\" + get_output(Frame{})).c_str())\n      .def(py::init<size_t, double, std::array<double, 3>>(), py::arg(\"l_max\"),\n           py::arg(\"radius\"), py::arg(\"center\"))\n      .def(py::init<size_t, size_t, const DataVector&, std::array<double, 3>>(),\n           py::arg(\"l_max\"), py::arg(\"m_max\"),\n           py::arg(\"radius_at_collocation_points\"), py::arg(\"center\"))\n      .def(py::init<size_t, size_t, const Strahlkorper&>(), py::arg(\"l_max\"),\n           py::arg(\"m_max\"), py::arg(\"another_strahlkorper\"))\n      .def(\n          py::init<size_t, size_t, const ModalVector&, std::array<double, 3>>(),\n          py::arg(\"l_max\"), py::arg(\"m_max\"), py::arg(\"spectral_coefficients\"),\n          py::arg(\"center\"))\n      .def_property_readonly(\"l_max\", &Strahlkorper::l_max)\n      .def_property_readonly(\"m_max\", &Strahlkorper::m_max)\n      .def_property_readonly(\n          \"physical_extents\",\n          [](const Strahlkorper& strahlkorper) {\n            return strahlkorper.ylm_spherepack().physical_extents();\n          })\n      .def_property_readonly(\"expansion_center\",\n                             &Strahlkorper::expansion_center)\n      .def_property_readonly(\"physical_center\", &Strahlkorper::physical_center)\n      .def_property_readonly(\"average_radius\", &Strahlkorper::average_radius)\n      .def(\"radius\", &Strahlkorper::radius, py::arg(\"theta\"), py::arg(\"phi\"))\n      .def(\"point_is_contained\", &Strahlkorper::point_is_contained,\n           py::arg(\"x\"))\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self == py::self)\n      // NOLINTNEXTLINE(misc-redundant-expression)\n      .def(py::self != py::self);\n  m.def(\n      \"ylm_legend_and_data\",\n      [](const Strahlkorper& strahlkorper, const double time,\n         const size_t max_l)\n          -> std::pair<std::vector<std::string>, std::vector<double>> {\n        std::vector<std::string> legend{};\n        std::vector<double> data{};\n        ylm::fill_ylm_legend_and_data(make_not_null(&legend),\n                                      make_not_null(&data), strahlkorper, time,\n                                      max_l);\n        return std::make_pair(legend, data);\n      },\n      py::arg(\"strahlkorper\"), py::arg(\"time\"), py::arg(\"max_l\"));\n}\n}  // namespace\n\nvoid bind_strahlkorper(py::module& m) {\n  bind_strahlkorper_impl<Frame::Inertial>(m);\n  bind_strahlkorper_impl<Frame::Grid>(m);\n  bind_strahlkorper_impl<Frame::Distorted>(m);\n  m.def(\"read_surface_ylm\", &ylm::read_surface_ylm<Frame::Inertial>,\n        py::arg(\"file_name\"), py::arg(\"surface_subfile_name\"),\n        py::arg(\"requested_number_of_times_from_end\"));\n  m.def(\"read_surface_ylm_single_time\",\n        &ylm::read_surface_ylm_single_time<Frame::Inertial>,\n        py::arg(\"file_name\"), py::arg(\"surface_subfile_name\"), py::arg(\"time\"),\n        py::arg(\"relative_epsilon\"), py::arg(\"check_frame\"));\n  py::enum_<ylm::AngularOrdering>(m, \"AngularOrdering\")\n      .value(\"Strahlkorper\", ylm::AngularOrdering::Strahlkorper)\n      .value(\"Cce\", ylm::AngularOrdering::Cce);\n  m.def(\n      \"write_sphere_of_points_to_text_file\",\n      [](const double radius, const size_t l_max,\n         const std::array<double, 3>& center,\n         const std::string& output_file_name,\n         const ylm::AngularOrdering ordering, const bool overwrite_file) {\n        ylm::write_strahlkorper_coords_to_text_file(\n            radius, l_max, center, output_file_name, ordering, overwrite_file);\n      },\n      py::arg(\"radius\"), py::arg(\"l_max\"), py::arg(\"center\"),\n      py::arg(\"output_file_name\"), py::arg(\"ordering\"),\n      py::arg(\"overwrite_file\") = false);\n}\n}  // namespace ylm::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/Strahlkorper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace ylm::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_strahlkorper(pybind11::module& m);\n}  // namespace ylm::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/StrahlkorperFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Python/StrahlkorperFunctions.hpp\"\n\n#include <pybind11/pybind11.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n\nnamespace py = pybind11;\n\nnamespace ylm::py_bindings {\nnamespace {\ntemplate <typename Frame>\nvoid bind_strahlkorper_functions_impl(pybind11::module& m) {  // NOLINT\n  using Strahlkorper = ylm::Strahlkorper<Frame>;\n  m.def(\"cartesian_coords\",\n        py::overload_cast<const Strahlkorper&>(&ylm::cartesian_coords<Frame>),\n        py::arg(\"strahlkorper\"));\n  m.def(\"power_monitor\",\n        py::overload_cast<const Strahlkorper&>(&ylm::power_monitor<Frame>),\n        py::arg(\"strahlkorper\"));\n}\n}  // namespace\n\nvoid bind_strahlkorper_functions(pybind11::module& m) {  // NOLINT\n  bind_strahlkorper_functions_impl<Frame::Grid>(m);\n  bind_strahlkorper_functions_impl<Frame::Inertial>(m);\n}\n}  // namespace ylm::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/StrahlkorperFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace ylm::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_strahlkorper_functions(pybind11::module& m);\n}  // namespace ylm::py_bindings\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom spectre.DataStructures.Tensor import Frame\n\nfrom ._Pybindings import *\n\nStrahlkorper = {\n    Frame.Grid: StrahlkorperGrid,\n    Frame.Inertial: StrahlkorperInertial,\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.hpp\"\n\n#include <boost/math/special_functions/spherical_harmonic.hpp>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm {\nvoid real_spherical_harmonic(gsl::not_null<DataVector*> spherical_harmonic,\n                             const DataVector& theta, const DataVector& phi,\n                             size_t l, int m) {\n  ASSERT(size_t(abs(m)) <= l, \"m needs to be smaller than l\");\n  const int sign = (m % 2 == 0) ? 1 : -1;\n  const double prefactor = sign * M_SQRT2;\n  if (m < 0) {\n    for (size_t i = 0; i < theta.size(); ++i) {\n      (*spherical_harmonic)[i] = prefactor * boost::math::spherical_harmonic_i(\n                                                 l, abs(m), theta[i], phi[i]);\n    }\n  } else if (m > 0) {\n    for (size_t i = 0; i < theta.size(); ++i) {\n      (*spherical_harmonic)[i] =\n          prefactor * boost::math::spherical_harmonic_r(l, m, theta[i], phi[i]);\n    }\n  } else {\n    for (size_t i = 0; i < theta.size(); ++i) {\n      (*spherical_harmonic)[i] =\n          boost::math::spherical_harmonic_r(l, m, theta[i], phi[i]);\n    }\n  }\n}\n\nDataVector real_spherical_harmonic(const DataVector& theta,\n                                   const DataVector& phi, size_t l, int m) {\n  DataVector result(theta.size());\n  real_spherical_harmonic(make_not_null(&result), theta, phi, l, m);\n  return result;\n}\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm {\n/// @{\n/*!\n * \\ingroup SpectralGroup\n *\n * \\brief Evaluates a real spherical harmonic of order l at the requested\n * angles \\f$\\theta\\f$ and \\f$\\phi\\f$.\n *\n * The real spherical harmonics are defined as:\n * \\begin{equation}\n * Y_{lm}(\\theta, \\phi) =\n * \\begin{cases}\n * \\sqrt{2} (-1)^m \\mathcal{I}[Y_l^{|m|}] & \\text{if } m < 0 \\\\\n *  Y_l^{0} & \\text{if } m = 0 \\\\\n * \\sqrt{2} (-1)^m \\mathcal{R}[Y_l^{m}] & \\text{if } m > 0\n * \\end{cases}\n * \\end{equation}\n *\n * where \\f$Y_l^m\\f$ are the complex spherical harmonics and \\f$\\mathcal{R}\\f$\n * denotes the real part and \\f$\\mathcal{I}\\f$ denotes the imaginary part.\n *\n * \\note This implementation uses the boost implementation of spherical\n * harmonics. The calculation is not vectorized and may be slow.\n * Stability has not been tested for large l.\n */\nvoid real_spherical_harmonic(gsl::not_null<DataVector*> spherical_harmonic,\n                             const DataVector& theta, const DataVector& phi,\n                             size_t l, int m);\n\nDataVector real_spherical_harmonic(const DataVector& theta,\n                                   const DataVector& phi, size_t l, int m);\n/// @}\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Spherepack.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <ostream>\n#include <tuple>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Spherepack.hpp\"\n\nnamespace ylm {\n//============================================================================\n// Note that SPHEREPACK (which is wrapped by Spherepack) takes\n// n_theta and n_phi as input, and is ok with arbitrary values of\n// n_theta and n_phi.  SPHEREPACK computes the maximum Ylm l and m\n// using the formulas l_max=n_theta-1 and m_max=min(l_max,n_phi/2).\n//\n// However, some combinations of n_theta and n_phi have strange properties:\n// - The maximum m that is AT LEAST PARTIALLY represented by (n_theta,n_phi)\n//   is std::min(n_theta-1,n_phi/2). This is called m_max here. But an arbitrary\n//   (n_theta,n_phi) does not necessarily fully represent all m's up to m_max,\n//   because sin(m_max phi) might be zero at all collocation points, and\n//   therefore sin(m_max phi) might not be representable on the grid.\n// - The largest m that is fully represented by (n_theta,n_phi) is\n//   m_max_represented = std::min(n_theta-1,(n_phi-1)/2).\n// - Therefore, if n_phi is odd,  m_max = m_max_represented,\n//              if n_phi is even, m_max = m_max_represented+1.\n// - To remedy this situation, we choose Spherepack to take as arguments\n//   l_max and m_max, instead of n_theta and n_phi.\n//   We then choose\n//      n_theta = l_max+1\n//      n_phi   = 2*m_max+1\n//   This ensures that m_max = m_max_represented\n//   (as opposed to m_max = m_max_represented+1)\n//============================================================================\n\n// Need to initialize memory_pool_ here because it is static.  Note\n// that memory_pool_ is never destroyed or cleared, except at the end\n// of execution when static variables are destroyed.  One alternative\n// would be to reference-count the instances of Spherepack on the\n// local thread, and to clear the memory_pool_ when the reference\n// count drops to zero.  Another alternative would be to clear the\n// memory_pool_ in the destructor (so it is cleared when *any*\n// instance of Spherepack is destroyed), but there is no need to do\n// so, and doing so would increase the number of memory allocations\n// because other instances of Spherepack would then re-allocate\n// memory_pool.\nthread_local Spherepack_detail::MemoryPool Spherepack::memory_pool_ =\n    Spherepack_detail::MemoryPool();\n\nSpherepack::Spherepack(const size_t l_max, const size_t m_max)\n    : l_max_{l_max},\n      m_max_{m_max},\n      n_theta_{l_max_ + 1},\n      n_phi_{2 * m_max_ + 1},\n      spectral_size_{2 * (l_max_ + 1) * (m_max_ + 1)},\n      storage_(l_max_, m_max_) {\n  if (l_max_ < 2) {\n    ERROR(\"Must use l_max>=2, not l_max=\" << l_max_);\n  }\n  if (m_max_ < 2) {\n    ERROR(\"Must use m_max>=2, not m_max=\" << m_max_);\n  }\n  if (m_max_ > l_max_) {\n    ERROR(\"Must use m_max<=l_max, not l_max=\" << l_max_\n                                              << \", m_max=\" << m_max_);\n  }\n  calculate_collocation_points();\n  fill_scalar_work_arrays();\n  fill_vector_work_arrays();\n  calculate_interpolation_data();\n}\n\nvoid Spherepack::phys_to_spec_impl(\n    const gsl::not_null<double*> spectral_coefs,\n    const gsl::not_null<const double*> collocation_values,\n    const size_t physical_stride, const size_t physical_offset,\n    const size_t spectral_stride, const size_t spectral_offset,\n    const bool loop_over_offset) const {\n  size_t work_size = 2 * n_theta_ * n_phi_;\n  if (loop_over_offset) {\n    ASSERT(physical_stride == spectral_stride, \"invalid call\");\n    work_size *= spectral_stride;\n  }\n  auto& work = memory_pool_.get(work_size);\n  double* const a = spectral_coefs;\n  // clang-tidy: 'do not use pointer arithmetic'.\n  double* const b =\n      a + (m_max_ + 1) * (l_max_ + 1) * spectral_stride;  // NOLINT\n  int err = 0;\n  const int effective_physical_offset =\n      loop_over_offset ? -1 : int(physical_offset);\n  const int effective_spectral_offset =\n      loop_over_offset ? -1 : int(spectral_offset);\n  const auto& work_phys_to_spec = storage_.work_phys_to_spec;\n  shags_(static_cast<int>(physical_stride), static_cast<int>(spectral_stride),\n         effective_physical_offset, effective_spectral_offset,\n         static_cast<int>(n_theta_), static_cast<int>(n_phi_), 0, 1,\n         collocation_values, static_cast<int>(n_theta_),\n         static_cast<int>(n_phi_), a, b, static_cast<int>(m_max_ + 1),\n         static_cast<int>(l_max_ + 1), static_cast<int>(m_max_ + 1),\n         static_cast<int>(l_max_ + 1), work_phys_to_spec.data(),\n         static_cast<int>(work_phys_to_spec.size()), work.data(),\n         static_cast<int>(work_size), &err);\n  if (UNLIKELY(err != 0)) {\n    ERROR(\"shags error \" << err << \" in Spherepack\");\n  }\n  memory_pool_.free(work);\n}\n\nvoid Spherepack::spec_to_phys_impl(\n    const gsl::not_null<double*> collocation_values,\n    const gsl::not_null<const double*> spectral_coefs,\n    const size_t spectral_stride, const size_t spectral_offset,\n    const size_t physical_stride, const size_t physical_offset,\n    const bool loop_over_offset) const {\n  size_t work_size = 2 * n_theta_ * n_phi_;\n  if (loop_over_offset) {\n    ASSERT(physical_stride == spectral_stride, \"invalid call\");\n    work_size *= spectral_stride;\n  }\n  auto& work = memory_pool_.get(work_size);\n  // 'a' and 'b' are Spherepack's coefficient arrays.\n  const double* const a = spectral_coefs;\n  // clang-tidy: 'do not use pointer arithmetic'.\n  const double* const b =\n      a + (m_max_ + 1) * (l_max_ + 1) * spectral_stride;  // NOLINT\n  int err = 0;\n  const int effective_physical_offset =\n      loop_over_offset ? -1 : int(physical_offset);\n  const int effective_spectral_offset =\n      loop_over_offset ? -1 : int(spectral_offset);\n\n  const auto& work_scalar_spec_to_phys = storage_.work_scalar_spec_to_phys;\n  shsgs_(static_cast<int>(physical_stride), static_cast<int>(spectral_stride),\n         effective_physical_offset, effective_spectral_offset,\n         static_cast<int>(n_theta_), static_cast<int>(n_phi_), 0, 1,\n         collocation_values, static_cast<int>(n_theta_),\n         static_cast<int>(n_phi_), a, b, static_cast<int>(m_max_ + 1),\n         static_cast<int>(l_max_ + 1), static_cast<int>(m_max_ + 1),\n         static_cast<int>(l_max_ + 1), work_scalar_spec_to_phys.data(),\n         static_cast<int>(work_scalar_spec_to_phys.size()), work.data(),\n         static_cast<int>(work_size), &err);\n  if (UNLIKELY(err != 0)) {\n    ERROR(\"shsgs error \" << err << \" in Spherepack\");\n  }\n  memory_pool_.free(work);\n}\n\nDataVector Spherepack::phys_to_spec(const DataVector& collocation_values,\n                                    const size_t physical_stride,\n                                    const size_t physical_offset) const {\n  ASSERT(collocation_values.size() == physical_size() * physical_stride,\n         \"Sizes don't match: \" << collocation_values.size() << \" vs \"\n                               << physical_size() * physical_stride);\n  DataVector result(spectral_size());\n  phys_to_spec_impl(result.data(), collocation_values.data(), physical_stride,\n                    physical_offset, 1, 0, false);\n  return result;\n}\n\nDataVector Spherepack::spec_to_phys(const DataVector& spectral_coefs,\n                                    const size_t spectral_stride,\n                                    const size_t spectral_offset) const {\n  ASSERT(spectral_coefs.size() == spectral_size() * spectral_stride,\n         \"Sizes don't match: \" << spectral_coefs.size() << \" vs \"\n                               << spectral_size() * spectral_stride);\n  DataVector result(physical_size());\n  spec_to_phys_impl(result.data(), spectral_coefs.data(), spectral_stride,\n                    spectral_offset, 1, 0, false);\n  return result;\n}\n\nDataVector Spherepack::phys_to_spec_all_offsets(\n    const DataVector& collocation_values, const size_t stride) const {\n  ASSERT(collocation_values.size() == physical_size() * stride,\n         \"Sizes don't match: \" << collocation_values.size() << \" vs \"\n                               << physical_size() * stride);\n  DataVector result(spectral_size() * stride);\n  phys_to_spec_impl(result.data(), collocation_values.data(), stride, 0, stride,\n                    0, true);\n  return result;\n}\n\nDataVector Spherepack::spec_to_phys_all_offsets(\n    const DataVector& spectral_coefs, const size_t stride) const {\n  ASSERT(spectral_coefs.size() == spectral_size() * stride,\n         \"Sizes don't match: \" << spectral_coefs.size() << \" vs \"\n                               << spectral_size() * stride);\n  DataVector result(physical_size() * stride);\n  spec_to_phys_impl(result.data(), spectral_coefs.data(), stride, 0, stride, 0,\n                    true);\n  return result;\n}\n\nvoid Spherepack::gradient(const std::array<double*, 2>& df,\n                          const gsl::not_null<const double*> collocation_values,\n                          const size_t physical_stride,\n                          const size_t physical_offset) const {\n  auto& f_k = memory_pool_.get(spectral_size());\n  phys_to_spec_impl(f_k.data(), collocation_values, physical_stride,\n                    physical_offset, 1, 0, false);\n  gradient_from_coefs_impl(df, f_k.data(), 1, 0, physical_stride,\n                           physical_offset, false);\n  memory_pool_.free(f_k);\n}\n\nvoid Spherepack::gradient_all_offsets(\n    const std::array<double*, 2>& df,\n    const gsl::not_null<const double*> collocation_values,\n    const size_t stride) const {\n  const size_t spectral_stride = stride;\n  auto& f_k = memory_pool_.get(spectral_stride * spectral_size());\n  phys_to_spec_impl(f_k.data(), collocation_values, stride, 0, spectral_stride,\n                    0, true);\n  gradient_from_coefs_impl(df, f_k.data(), spectral_stride, 0, stride, 0, true);\n  memory_pool_.free(f_k);\n}\n\nvoid Spherepack::gradient_from_coefs_impl(\n    const std::array<double*, 2>& df,\n    const gsl::not_null<const double*> spectral_coefs,\n    const size_t spectral_stride, const size_t spectral_offset,\n    const size_t physical_stride, const size_t physical_offset,\n    bool loop_over_offset) const {\n  ASSERT((not loop_over_offset) or spectral_stride == physical_stride,\n         \"physical and spectral strides must be equal \"\n         \"for loop_over_offset=true\");\n\n  const size_t l1 = m_max_ + 1;\n  const double* const f_k = spectral_coefs;\n  const double* const a = f_k;\n  // clang-tidy: 'do not use pointer arithmetic'.\n  const double* const b = f_k + l1 * n_theta_ * spectral_stride;  // NOLINT\n\n  size_t work_size = n_theta_ * (3 * n_phi_ + 2 * l1 + 1);\n  if (loop_over_offset) {\n    work_size *= spectral_stride;\n  }\n  auto& work = memory_pool_.get(work_size);\n  int err = 0;\n  const int effective_physical_offset =\n      loop_over_offset ? -1 : int(physical_offset);\n  const int effective_spectral_offset =\n      loop_over_offset ? -1 : int(spectral_offset);\n  const auto& work_vector_spec_to_phys = storage_.work_vector_spec_to_phys;\n  gradgs_(static_cast<int>(physical_stride), static_cast<int>(spectral_stride),\n          effective_physical_offset, effective_spectral_offset,\n          static_cast<int>(n_theta_), static_cast<int>(n_phi_), 0, 1, df[0],\n          df[1], static_cast<int>(n_theta_), static_cast<int>(n_phi_), a, b,\n          static_cast<int>(l1), static_cast<int>(n_theta_),\n          work_vector_spec_to_phys.data(),\n          static_cast<int>(work_vector_spec_to_phys.size()), work.data(),\n          static_cast<int>(work_size), &err);\n  if (UNLIKELY(err != 0)) {\n    ERROR(\"gradgs error \" << err << \" in Spherepack\");\n  }\n  memory_pool_.free(work);\n}\n\nSpherepack::FirstDeriv Spherepack::gradient(\n    const DataVector& collocation_values, const size_t physical_stride,\n    const size_t physical_offset) const {\n  ASSERT(collocation_values.size() == physical_size() * physical_stride,\n         \"Sizes don't match: \" << collocation_values.size() << \" vs \"\n                               << physical_size() * physical_stride);\n  auto& f_k = memory_pool_.get(spectral_size());\n  phys_to_spec_impl(f_k.data(), collocation_values.data(), physical_stride,\n                    physical_offset, 1, 0, false);\n  FirstDeriv result(physical_size());\n  std::array<double*, 2> temp = {{result.get(0).data(), result.get(1).data()}};\n  gradient_from_coefs_impl(temp, f_k.data(), 1, 0, 1, 0, false);\n  memory_pool_.free(f_k);\n  return result;\n}\n\nSpherepack::FirstDeriv Spherepack::gradient_all_offsets(\n    const DataVector& collocation_values, const size_t stride) const {\n  ASSERT(collocation_values.size() == physical_size() * stride,\n         \"Sizes don't match: \" << collocation_values.size() << \" vs \"\n                               << physical_size() * stride);\n  FirstDeriv result(physical_size() * stride);\n  std::array<double*, 2> temp = {{result.get(0).data(), result.get(1).data()}};\n  gradient_all_offsets(temp, collocation_values.data(), stride);\n  return result;\n}\n\nSpherepack::FirstDeriv Spherepack::gradient_from_coefs(\n    const DataVector& spectral_coefs, const size_t spectral_stride,\n    const size_t spectral_offset) const {\n  ASSERT(spectral_coefs.size() == spectral_size() * spectral_stride,\n         \"Sizes don't match: \" << spectral_coefs.size() << \" vs \"\n                               << spectral_size() * spectral_stride);\n  FirstDeriv result(physical_size());\n  std::array<double*, 2> temp = {{result.get(0).data(), result.get(1).data()}};\n  gradient_from_coefs_impl(temp, spectral_coefs.data(), spectral_stride,\n                           spectral_offset, 1, 0, false);\n  return result;\n}\n\nSpherepack::FirstDeriv Spherepack::gradient_from_coefs_all_offsets(\n    const DataVector& spectral_coefs, const size_t stride) const {\n  ASSERT(spectral_coefs.size() == spectral_size() * stride,\n         \"Sizes don't match: \" << spectral_coefs.size() << \" vs \"\n                               << spectral_size() * stride);\n  FirstDeriv result(physical_size() * stride);\n  std::array<double*, 2> temp = {{result.get(0).data(), result.get(1).data()}};\n  gradient_from_coefs_impl(temp, spectral_coefs.data(), stride, 0, stride, 0,\n                           true);\n  return result;\n}\n\nvoid Spherepack::scalar_laplacian(\n    const gsl::not_null<double*> scalar_laplacian,\n    const gsl::not_null<const double*> collocation_values,\n    const size_t physical_stride, const size_t physical_offset) const {\n  auto& f_k = memory_pool_.get(spectral_size());\n  phys_to_spec(f_k.data(), collocation_values, physical_stride, physical_offset,\n               1, 0);\n  scalar_laplacian_from_coefs(scalar_laplacian, f_k.data(), 1, 0,\n                              physical_stride, physical_offset);\n  memory_pool_.free(f_k);\n}\n\nvoid Spherepack::scalar_laplacian_from_coefs(\n    const gsl::not_null<double*> scalar_laplacian,\n    const gsl::not_null<const double*> spectral_coefs,\n    const size_t spectral_stride, const size_t spectral_offset,\n    const size_t physical_stride, const size_t physical_offset) const {\n  const size_t l1 = m_max_ + 1;\n  const double* const a = spectral_coefs;\n  // clang-tidy: 'do not use pointer arithmetic'.\n  const double* const b = a + l1 * n_theta_ * spectral_stride;  // NOLINT\n  const size_t work_size = n_theta_ * (3 * n_phi_ + 2 * l1 + 1);\n  auto& work = memory_pool_.get(work_size);\n  int err = 0;\n  const auto& work_scalar_spec_to_phys = storage_.work_scalar_spec_to_phys;\n  slapgs_(static_cast<int>(physical_stride), static_cast<int>(spectral_stride),\n          static_cast<int>(physical_offset), static_cast<int>(spectral_offset),\n          static_cast<int>(n_theta_), static_cast<int>(n_phi_), 0, 1,\n          scalar_laplacian, static_cast<int>(n_theta_),\n          static_cast<int>(n_phi_), a, b, static_cast<int>(l1),\n          static_cast<int>(n_theta_), work_scalar_spec_to_phys.data(),\n          static_cast<int>(work_scalar_spec_to_phys.size()), work.data(),\n          static_cast<int>(work_size), &err);\n  if (UNLIKELY(err != 0)) {\n    ERROR(\"slapgs error \" << err << \" in Spherepack\");\n  }\n  memory_pool_.free(work);\n}\n\nDataVector Spherepack::scalar_laplacian(const DataVector& collocation_values,\n                                        const size_t physical_stride,\n                                        const size_t physical_offset) const {\n  ASSERT(collocation_values.size() == physical_size() * physical_stride,\n         \"Sizes don't match: \" << collocation_values.size() << \" vs \"\n                               << physical_size() * physical_stride);\n  DataVector result(physical_size());\n  scalar_laplacian(result.data(), collocation_values.data(), physical_stride,\n                   physical_offset);\n  return result;\n}\n\nDataVector Spherepack::scalar_laplacian_from_coefs(\n    const DataVector& spectral_coefs, const size_t spectral_stride,\n    const size_t spectral_offset) const {\n  ASSERT(spectral_coefs.size() == spectral_size() * spectral_stride,\n         \"Sizes don't match: \" << spectral_coefs.size() << \" vs \"\n                               << spectral_size() * spectral_stride);\n  DataVector result(physical_size());\n  scalar_laplacian_from_coefs(result.data(), spectral_coefs.data(),\n                              spectral_stride, spectral_offset);\n  return result;\n}\n\nstd::array<DataVector, 2> Spherepack::theta_phi_points() const {\n  std::array<DataVector, 2> result = make_array<2>(DataVector(physical_size()));\n  const auto& theta = theta_points();\n  const auto& phi = phi_points();\n  // Storage in SPHEREPACK: theta varies fastest (i.e. fortran ordering).\n  for (size_t i_phi = 0, s = 0; i_phi < n_phi_; ++i_phi) {\n    for (size_t i_theta = 0; i_theta < n_theta_; ++i_theta, ++s) {\n      result[0][s] = theta[i_theta];\n      result[1][s] = phi[i_phi];\n    }\n  }\n  return result;\n}\n\nvoid Spherepack::second_derivative(\n    const std::array<double*, 2>& df, const gsl::not_null<SecondDeriv*> ddf,\n    const gsl::not_null<const double*> collocation_values,\n    const size_t physical_stride, const size_t physical_offset) const {\n  const auto& cos_theta = storage_.cos_theta;\n  const auto& sin_theta = storage_.sin_theta;\n  const auto& sin_phi = storage_.sin_phi;\n  const auto& cos_phi = storage_.cos_phi;\n  const auto& cot_theta = storage_.cot_theta;\n  const auto& cosec_theta = storage_.cosec_theta;\n\n  // Get first derivatives\n  gradient(df, collocation_values, physical_stride, physical_offset);\n\n  // Now get Cartesian derivatives.\n\n  // First derivative\n  std::vector<double*> dfc(3, nullptr);\n  for (size_t i = 0; i < 3; ++i) {\n    dfc[i] = memory_pool_.get(physical_size()).data();\n  }\n  for (size_t j = 0, s = 0; j < n_phi_; ++j) {\n    for (size_t i = 0; i < n_theta_; ++i, ++s) {\n      dfc[0][s] = cos_theta[i] * cos_phi[j] *\n                      df[0][s * physical_stride + physical_offset] -\n                  sin_phi[j] * df[1][s * physical_stride + physical_offset];\n      dfc[1][s] = cos_theta[i] * sin_phi[j] *\n                      df[0][s * physical_stride + physical_offset] +\n                  cos_phi[j] * df[1][s * physical_stride + physical_offset];\n      dfc[2][s] = -sin_theta[i] * df[0][s * physical_stride + physical_offset];\n    }\n  }\n\n  // Take derivatives of Cartesian derivatives to get second derivatives.\n  std::vector<std::array<double*, 2>> ddfc(3, {{nullptr, nullptr}});\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 2; ++j) {\n      gsl::at(ddfc[i], j) = memory_pool_.get(physical_size()).data();\n    }\n    gradient(ddfc[i], dfc[i], 1, 0);\n    // Here we free the storage for dfc[i], so we can reuse it in ddfc.\n    memory_pool_.free(dfc[i]);\n  }\n\n  // Combine into Pfaffian second derivatives\n  for (size_t j = 0, s = 0; j < n_phi_; ++j) {\n    for (size_t i = 0; i < n_theta_; ++i, ++s) {\n      ddf->get(1, 0)[s * physical_stride + physical_offset] =\n          -ddfc[2][1][s] * cosec_theta[i];\n      ddf->get(0, 1)[s * physical_stride + physical_offset] =\n          ddf->get(1, 0)[s * physical_stride + physical_offset] -\n          cot_theta[i] * df[1][s * physical_stride + physical_offset];\n      ddf->get(1, 1)[s * physical_stride + physical_offset] =\n          cos_phi[j] * ddfc[1][1][s] - sin_phi[j] * ddfc[0][1][s] -\n          cot_theta[i] * df[0][s * physical_stride + physical_offset];\n      ddf->get(0, 0)[s * physical_stride + physical_offset] =\n          cos_theta[i] *\n              (cos_phi[j] * ddfc[0][0][s] + sin_phi[j] * ddfc[1][0][s]) -\n          sin_theta[i] * ddfc[2][0][s];\n    }\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 2; ++j) {\n      memory_pool_.free(gsl::at(ddfc[i], j));\n    }\n  }\n}\n\nstd::pair<Spherepack::FirstDeriv, Spherepack::SecondDeriv>\nSpherepack::first_and_second_derivative(\n    const DataVector& collocation_values) const {\n  ASSERT(collocation_values.size() == physical_size(),\n         \"Sizes don't match: \" << collocation_values.size() << \" vs \"\n                               << physical_size());\n  std::pair<FirstDeriv, SecondDeriv> result(\n      std::piecewise_construct, std::forward_as_tuple(physical_size()),\n      std::forward_as_tuple(physical_size()));\n  std::array<double*, 2> temp = {\n      {std::get<0>(result).get(0).data(), std::get<0>(result).get(1).data()}};\n  second_derivative(temp, &std::get<1>(result), collocation_values.data());\n  return result;\n}\n\ntemplate <typename T>\nSpherepack::InterpolationInfo<T>::InterpolationInfo(\n    const size_t l_max, const size_t m_max, const gsl::span<double> pmm,\n    const std::array<T, 2>& target_points)\n    : cos_theta(cos(target_points[0])),\n      cos_m_phi(DynamicBuffer<T>(m_max + 1, get_size(target_points[0]))),\n      sin_m_phi(DynamicBuffer<T>(m_max + 1, get_size(target_points[0]))),\n      pbar_factor(DynamicBuffer<T>(m_max + 1, get_size(target_points[0]))),\n      l_max_(l_max),\n      m_max_(m_max),\n      num_points_(get_size(target_points[0])) {\n  const auto& theta = target_points[0];\n  const auto& phi = target_points[1];\n\n  ASSERT(get_size(theta) == get_size(phi),\n         \"Size mismatch for thetas and phis: \" << get_size(theta) << \" and \"\n                                               << get_size(phi));\n  if (m_max < 2) {\n    ERROR(\"Must use m_max>=2, not m_max=\" << m_max);\n  }\n\n  // `DataVectors` for working. `pbar_factor` is guaranteed to be at least size\n  // 3 as demanded by the `Spherepack` constructor\n  auto& alpha = pbar_factor.at(0);\n  auto& beta = pbar_factor.at(1);\n  auto& deltasinmphi = pbar_factor.at(2);\n\n  // If T is DataVector, `sinmphi` and `cosmphi` will be created with\n  // size `num_points`, if T is double they will be initialized to value\n  // `num_points` but this value is not used.\n  T sinmphi(num_points_);\n  T cosmphi(num_points_);\n\n  // Evaluate cos(m*phi) and sin(m*phi) by numerical recipes eq. 5.5.6\n  {\n    alpha = 2.0 * square(sin(0.5 * phi));\n    beta = sin(phi);\n    sinmphi = 0.;\n    cosmphi = 1.;\n    cos_m_phi[0] = 1.;\n    sin_m_phi[0] = 0.;\n    for (size_t m = 1; m < m_max + 1; ++m) {\n      deltasinmphi = alpha * sinmphi - beta * cosmphi;\n      cosmphi -= alpha * cosmphi + beta * sinmphi;\n      sinmphi -= deltasinmphi;\n      cos_m_phi[m] = cosmphi;\n      sin_m_phi[m] = sinmphi;\n    }\n  }\n\n  T& sin_theta = sinmphi;\n  T& sinmtheta = cosmphi;\n  sin_theta = sin(theta);\n  sinmtheta = 1.;\n\n  // Fill pbar_factor[m] = Pbar(m,m)*sin(theta)^m  (m<l1)\n  for (size_t m = 0; m < m_max + 1; ++m) {\n    pbar_factor[m] = pmm[m] * sinmtheta;\n    sinmtheta *= sin_theta;\n  }\n}\n\ntemplate <typename T>\nSpherepack::InterpolationInfo<T> Spherepack::set_up_interpolation_info(\n    const std::array<T, 2>& target_points) const {\n  return InterpolationInfo(l_max_, m_max_, storage_.work_interp_pmm,\n                           target_points);\n}\n\ntemplate <typename T>\nvoid Spherepack::interpolate(\n    const gsl::not_null<T*> result,\n    const gsl::not_null<const double*> collocation_values,\n    const InterpolationInfo<T>& interpolation_info,\n    const size_t physical_stride, const size_t physical_offset) const {\n  ASSERT(get_size(*result) == interpolation_info.size(),\n         \"Size mismatch: \" << get_size(*result) << \",\"\n                           << interpolation_info.size());\n  auto& f_k = memory_pool_.get(spectral_size());\n  phys_to_spec(f_k.data(), collocation_values, physical_stride, physical_offset,\n               1, 0);\n  interpolate_from_coefs(result, f_k, interpolation_info);\n  memory_pool_.free(f_k);\n}\n\ntemplate <typename T, typename R>\nvoid Spherepack::interpolate_from_coefs(\n    const gsl::not_null<T*> result, const R& spectral_coefs,\n    const InterpolationInfo<T>& interpolation_info,\n    const size_t spectral_stride, const size_t spectral_offset) const {\n  if (UNLIKELY(m_max_ != interpolation_info.m_max())) {\n    ERROR(\"Different m_max for InterpolationInfo (\"\n          << interpolation_info.m_max() << \") and Spherepack instance (\"\n          << m_max_ << \")\");\n  };\n  if (UNLIKELY(l_max_ != interpolation_info.l_max())) {\n    ERROR(\"Different l_max for InterpolationInfo (\"\n          << interpolation_info.l_max() << \") and Spherepack instance (\"\n          << l_max_ << \")\");\n  };\n  const auto& alpha = storage_.work_interp_alpha;\n  const auto& beta = storage_.work_interp_beta;\n  const auto& index = storage_.work_interp_index;\n  // alpha holds alpha(n,m,x)/x, beta holds beta(n+1,m).\n  // index holds the index into the coefficient array.\n  // All are indexed together.\n\n  const size_t num_points = get_size(*result);\n  ASSERT(num_points == interpolation_info.size(),\n         \"Size mismatch: \" << num_points << \",\" << interpolation_info.size());\n\n  // initialize work `DataVectors` in `TempBuffer` to reduce allocations\n  TempBuffer<tmpl::list<::Tags::TempScalar<0, T>, ::Tags::TempScalar<1, T>,\n                        ::Tags::TempScalar<2, T>, ::Tags::TempScalar<3, T>,\n                        ::Tags::TempScalar<4, T>, ::Tags::TempScalar<5, T>>>\n      buffer(num_points);\n\n  auto& ycn = get(get<::Tags::TempScalar<0, T>>(buffer));\n  auto& ycnp1 = get(get<::Tags::TempScalar<1, T>>(buffer));\n  auto& ycnp2 = get(get<::Tags::TempScalar<2, T>>(buffer));\n  auto& ysn = get(get<::Tags::TempScalar<3, T>>(buffer));\n  auto& ysnp1 = get(get<::Tags::TempScalar<4, T>>(buffer));\n  auto& ysnp2 = get(get<::Tags::TempScalar<5, T>>(buffer));\n\n  const size_t l1 = m_max_ + 1;\n\n  // Offsets of 'a' and 'b' in spectral_coefs.\n  const size_t a_offset = spectral_offset;\n  const size_t b_offset = spectral_offset + (l1 * n_theta_) * spectral_stride;\n\n  const auto& cos_theta = interpolation_info.cos_theta;\n\n  // Clenshaw recurrence for m=0.  Separate because there is no phi\n  // dependence, and there is a factor of 1/2.\n  size_t idx = 0;\n  {\n    ycn = 0.;\n    ycnp1 = 0.;\n    for (size_t n = n_theta_ - 1; n > 0;\n         --n, ++idx) {  // Loops from n_theta_-1 to 1.\n      ycnp2 = ycnp1;\n      ycnp1 = ycn;\n      ycn = cos_theta * alpha[idx] * ycnp1 + beta[idx] * ycnp2 +\n            spectral_coefs[a_offset + spectral_stride * index[idx]];\n    }\n    *result = 0.5 * interpolation_info.pbar_factor[0] *\n              (beta[idx] * ycnp1 + cos_theta * alpha[idx] * ycn +\n               spectral_coefs[a_offset + spectral_stride * index[idx]]);\n    ++idx;\n  }\n  // Now do recurrence for other m.\n  for (size_t m = 1; m < l1; ++m) {\n    ycn = 0.;\n    ycnp1 = 0.;\n    ysn = 0.;\n    ysnp1 = 0.;\n    for (size_t n = n_theta_ - 1; n > m; --n, ++idx) {\n      ycnp2 = ycnp1;\n      ysnp2 = ysnp1;\n      ycnp1 = ycn;\n      ysnp1 = ysn;\n      ycn = cos_theta * alpha[idx] * ycnp1 + beta[idx] * ycnp2 +\n            spectral_coefs[a_offset + spectral_stride * index[idx]];\n      ysn = cos_theta * alpha[idx] * ysnp1 + beta[idx] * ysnp2 +\n            spectral_coefs[b_offset + spectral_stride * index[idx]];\n    }\n\n    auto& fc = ycnp2;\n    auto& fs = ysnp2;\n    fc = interpolation_info.pbar_factor[m] *\n         (beta[idx] * ycnp1 + cos_theta * alpha[idx] * ycn +\n          spectral_coefs[a_offset + spectral_stride * index[idx]]);\n    fs = interpolation_info.pbar_factor[m] *\n         (beta[idx] * ysnp1 + cos_theta * alpha[idx] * ysn +\n          spectral_coefs[b_offset + spectral_stride * index[idx]]);\n    *result += fc * interpolation_info.cos_m_phi[m] -\n               fs * interpolation_info.sin_m_phi[m];\n    ++idx;\n  }\n  ASSERT(idx == index.size(),\n         \"Wrong size \" << idx << \", expected \" << index.size());\n}\n\ntemplate <typename T>\nT Spherepack::interpolate(const DataVector& collocation_values,\n                          const std::array<T, 2>& target_points) const {\n  ASSERT(collocation_values.size() == physical_size(),\n         \"Sizes don't match: \" << collocation_values.size() << \" vs \"\n                               << physical_size());\n  auto result = make_with_value<T>(target_points[0], 0.);\n  interpolate<T>(make_not_null(&result), collocation_values.data(),\n                 set_up_interpolation_info<T>(target_points));\n  return result;\n}\n\ntemplate <typename T>\nT Spherepack::interpolate_from_coefs(\n    const DataVector& spectral_coefs,\n    const std::array<T, 2>& target_points) const {\n  ASSERT(spectral_coefs.size() == spectral_size(),\n         \"Sizes don't match: \" << spectral_coefs.size() << \" vs \"\n                               << spectral_size());\n  auto result = make_with_value<T>(target_points[0], 0.);\n  interpolate_from_coefs<T>(&result, spectral_coefs,\n                            set_up_interpolation_info<T>(target_points));\n  return result;\n}\n\nvoid Spherepack::calculate_collocation_points() {\n  // Theta\n  auto& theta = storage_.theta;\n  DataVector temp(2 * n_theta_ + 1);\n  auto work = gsl::make_span(temp.data(), n_theta_);\n  auto unused_weights = gsl::make_span(temp.data() + n_theta_, n_theta_ + 1);\n\n  int err = 0;\n  gaqd_(static_cast<int>(n_theta_), theta.data(), unused_weights.data(),\n        work.data(), static_cast<int>(unused_weights.size()), &err);\n  if (UNLIKELY(err != 0)) {\n    ERROR(\"gaqd error \" << err << \" in Spherepack\");\n  }\n\n  // Phi\n  auto& phi = storage_.phi;\n  const double two_pi_over_n_phi = 2.0 * M_PI / n_phi_;\n  for (size_t i = 0; i < n_phi_; ++i) {\n    phi[i] = two_pi_over_n_phi * i;\n  }\n\n  // Other trig functions at collocation points\n  auto& cos_theta = storage_.cos_theta;\n  auto& sin_theta = storage_.sin_theta;\n  auto& cot_theta = storage_.cot_theta;\n  auto& cosec_theta = storage_.cosec_theta;\n  for (size_t i = 0; i < n_theta_; ++i) {\n    cos_theta[i] = cos(theta[i]);\n    sin_theta[i] = sin(theta[i]);\n    cosec_theta[i] = 1.0 / sin_theta[i];\n    cot_theta[i] = cos_theta[i] * cosec_theta[i];\n  }\n\n  auto& sin_phi = storage_.sin_phi;\n  auto& cos_phi = storage_.cos_phi;\n  for (size_t i = 0; i < n_phi_; ++i) {\n    cos_phi[i] = cos(phi[i]);\n    sin_phi[i] = sin(phi[i]);\n  }\n}\n\nvoid Spherepack::calculate_interpolation_data() {\n  // SPHEREPACK expands f(theta,phi) as\n  //\n  // f(theta,phi) =\n  // 1/2 Sum_{l=0}^{l_max} Pbar(l,0) a(0,l)\n  //   + Sum_{m=1}^{m_max} Sum_{l=m}^{l_max} Pbar(l,m)(  a(m,l) cos(m phi)\n  //                                                   - b(m,l) sin(m phi))\n  //\n  // where Pbar(l,m) are unit-orthonormal Associated Legendre\n  // polynomials (which are functions of x = cos(theta)), and a(m,l)\n  // and b(m,l) are the SPHEREPACK spectral coefficients.\n  //\n  // Note that Pbar(l,m) = sqrt((2l+1)(l-m)!/(2(l+m)!)) P_l^m.\n  // and that Integral_{-1}^{+1} Pbar(k,m)(x) Pbar(l,m)(x) = delta_kl\n  //\n  // We will interpolate via Clenshaw's recurrence formula in l, for fixed m.\n  //\n  // The recursion relation between Associated Legendre polynomials is\n  // Pbar(l+1)(m) = alpha(l,x) Pbar(l)(m) + beta(l,x) Pbar(l-1)(m)\n  // where alpha(l,x) = x sqrt((2l+3)(2l+1)/((l+1-m)(l+1+m)))\n  // where beta(l,x)  = - sqrt((2l+3)(l-m)(l+m)/((l+1-m)(l+1+m)(2l-1)))\n  //\n  // The Clenshaw recurrence formula for\n  // f(x) = Sum_{l=m}^{l_max} c_(l)(m) Pbar(l)(m) is\n  // y_{l_max+1} = y_{l_max+2} = 0\n  // y_k  = alpha(k,x) y_{k+1} + beta(k+1,x) y_{k+2} + c_(k)(m)  (m<k<=l_max)\n  // f(x) = beta(m+1,x) Pbar(m,m) y_{m+2} + Pbar(m+1,m) y_{m+1}\n  //      + Pbar(m,m) c_(m)(m).\n  //\n  // So we will compute and store alpha(l,x)/x in 'alpha' and\n  // beta(l+1,x) [NOT beta(l,x)] in 'beta'.  We will also compute and\n  // store the x-independent piece of Pbar(m)(m) in 'pmm' and we\n  // will compute and store the x-independent piece of Pbar(m+1)(m)/Pbar(m)(m)\n  // in a component of 'alpha'. See below for storage.\n  // Note Pbar(m)(m)   is (2m-1)!! (1-x^2)^(n/2) sqrt((2m+1)/(2 (2m)!))\n  // and  Pbar(m+1)(m) is (2m+1)!! x(1-x^2)^(n/2)sqrt((2m+3)/(2(2m+1)!))\n  //  Ratio Pbar(m+1)(m)/Pbar(m)(m)   = x sqrt(2m+3)\n  //  Ratio Pbar(m+1)(m+1)/Pbar(m)(m) = sqrt(1-x^2) sqrt((2m+3)/(2m+2))\n  auto& alpha = storage_.work_interp_alpha;\n  auto& beta = storage_.work_interp_beta;\n  auto& pmm = storage_.work_interp_pmm;\n  auto& index = storage_.work_interp_index;\n\n  const size_t l1 = m_max_ + 1;\n\n  // Fill alpha,beta,index arrays in the same order as the Clenshaw\n  // recurrence, so that we can index them easier during the recurrence.\n  // First do m=0.\n  size_t idx = 0;\n  for (size_t n = n_theta_ - 1; n > 0; --n, ++idx) {\n    const auto n_dbl = static_cast<double>(n);\n    const double tnp1 = 2.0 * n_dbl + 1;\n    const double np1sq = n_dbl * n_dbl + 2.0 * n_dbl + 1.0;\n    alpha[idx] = sqrt(tnp1 * (tnp1 + 2.0) / np1sq);\n    beta[idx] = -sqrt((tnp1 + 4.0) / tnp1 * np1sq / (np1sq + 2 * n_dbl + 3));\n    index[idx] = n_dbl * l1;\n  }\n  // The next value of beta stores beta(n=1,m=0).\n  // The next value of alpha stores Pbar(n=1,m=0)/(x*Pbar(n=0,m=0)).\n  // These two values are needed for the final Clenshaw recurrence formula.\n  beta[idx] = -0.5 * sqrt(5.0);\n  alpha[idx] = sqrt(3.0);\n  index[idx] = 0;  // Index of coef in the final recurrence formula\n  ++idx;\n\n  // Now do other m.\n  for (size_t m = 1; m < l1; ++m) {\n    for (size_t n = n_theta_ - 1; n > m; --n, ++idx) {\n      const double tnp1 = 2.0 * n + 1;\n      const double np1sqmmsq = (n + 1.0 + m) * (n + 1.0 - m);\n      alpha[idx] = sqrt(tnp1 * (tnp1 + 2.0) / np1sqmmsq);\n      beta[idx] =\n          -sqrt((tnp1 + 4.0) / tnp1 * np1sqmmsq / (np1sqmmsq + 2. * n + 3.));\n      index[idx] = m + n * l1;\n    }\n    // The next value of beta stores beta(n=m+1,m).\n    // The next value of alpha stores Pbar(n=m+1,m)/(x*Pbar(n=m,m)).\n    // These two values are needed for the final Clenshaw recurrence formula.\n    beta[idx] = -0.5 * sqrt((2.0 * m + 5) / (m + 1.0));\n    alpha[idx] = sqrt(2.0 * m + 3);\n    index[idx] = m + m * l1;  // Index of coef in the final recurrence formula.\n    ++idx;\n  }\n  ASSERT(idx == index.size(),\n         \"Wrong size \" << idx << \", expected \" << index.size());\n\n  // Now do pmm, which stores Pbar(m,m).\n  pmm[0] = M_SQRT1_2;  // 1/sqrt(2) = Pbar(0)(0)\n  for (size_t m = 1; m < l1; ++m) {\n    pmm[m] = pmm[m - 1] * sqrt((2.0 * m + 1.0) / (2.0 * m));\n  }\n}\n\nvoid Spherepack::fill_vector_work_arrays() {\n  DataVector work((3 * n_theta_ * (n_theta_ + 3) + 2) / 2);\n\n  auto& work_vector_spec_to_phys = storage_.work_vector_spec_to_phys;\n  int err = 0;\n  vhsgsi_(static_cast<int>(n_theta_), static_cast<int>(n_phi_),\n          work_vector_spec_to_phys.data(),\n          static_cast<int>(work_vector_spec_to_phys.size()), work.data(),\n          static_cast<int>(work.size()), &err);\n  if (UNLIKELY(err != 0)) {\n    ERROR(\"vhsgsi error \" << err << \" in Spherepack\");\n  }\n}\n\nvoid Spherepack::fill_scalar_work_arrays() {\n  // Quadrature weights\n  {\n    DataVector temp(3 * n_theta_ + 1);\n    auto weights = gsl::make_span(temp.data(), n_theta_);\n    auto work0 = gsl::make_span(temp.data() + n_theta_, n_theta_);\n    auto work1 = gsl::make_span(temp.data() + 2 * n_theta_, n_theta_ + 1);\n    int err = 0;\n    gaqd_(static_cast<int>(n_theta_), work0.data(), weights.data(),\n          work1.data(), static_cast<int>(work1.size()), &err);\n    if (UNLIKELY(err != 0)) {\n      ERROR(\"gaqd error \" << err << \" in Spherepack\");\n    }\n    auto& quadrature_weights = storage_.quadrature_weights;\n    for (size_t i = 0; i < n_theta_; ++i) {\n      for (size_t j = 0; j < n_phi_; ++j) {\n        quadrature_weights[i + j * n_theta_] = (2 * M_PI / n_phi_) * weights[i];\n      }\n    }\n  }\n\n  // Scalar arrays\n  {\n    const size_t work0_size = 4 * n_theta_ * (n_theta_ + 2) + 2;\n    const size_t work1_size = n_theta_ * (n_theta_ + 4);\n    DataVector temp(work0_size + work1_size);\n    auto work0 = gsl::make_span(temp.data(), work0_size);\n    auto work1 = gsl::make_span(temp.data() + work0_size, work1_size);\n    auto& work_phys_to_spec = storage_.work_phys_to_spec;\n    int err = 0;\n    shagsi_(static_cast<int>(n_theta_), static_cast<int>(n_phi_),\n            work_phys_to_spec.data(),\n            static_cast<int>(work_phys_to_spec.size()), work0.data(),\n            static_cast<int>(work0.size()), work1.data(),\n            static_cast<int>(work1.size()), &err);\n    if (UNLIKELY(err != 0)) {\n      ERROR(\"shagsi error \" << err << \" in Spherepack\");\n    }\n    auto& work_scalar_spec_to_phys = storage_.work_scalar_spec_to_phys;\n    shsgsi_(static_cast<int>(n_theta_), static_cast<int>(n_phi_),\n            work_scalar_spec_to_phys.data(),\n            static_cast<int>(work_scalar_spec_to_phys.size()), work0.data(),\n            static_cast<int>(work0.size()), work1.data(),\n            static_cast<int>(work1.size()), &err);\n    if (UNLIKELY(err != 0)) {\n      ERROR(\"shsgsi error \" << err << \" in Spherepack\");\n    }\n  }\n}\n\nDataVector Spherepack::prolong_or_restrict(const DataVector& spectral_coefs,\n                                           const size_t l_max_coefs,\n                                           const size_t m_max_coefs,\n                                           const size_t l_max_target,\n                                           const size_t m_max_target) {\n  if (l_max_coefs == l_max_target and m_max_coefs == m_max_target) {\n    return spectral_coefs;\n  }\n\n  ASSERT(spectral_coefs.size() ==\n             Spherepack::spectral_size(l_max_coefs, m_max_coefs),\n         \"Expecting \" << Spherepack::spectral_size(l_max_coefs, m_max_coefs)\n                      << \", got \" << spectral_coefs.size());\n  const size_t spectral_size_target =\n      Spherepack::spectral_size(l_max_target, m_max_target);\n  DataVector result{spectral_size_target, 0.0};\n  SpherepackIterator src_it(l_max_coefs, m_max_coefs);\n  SpherepackIterator dest_it(l_max_target, m_max_target);\n  for (; dest_it; ++dest_it) {\n    if (dest_it.l() <= src_it.l_max() and dest_it.m() <= src_it.m_max()) {\n      src_it.set(dest_it.l(), dest_it.m(), dest_it.coefficient_array());\n      result[dest_it()] = spectral_coefs[src_it()];\n    }\n  }\n  return result;\n}\n\nDataVector Spherepack::prolong_or_restrict(const DataVector& spectral_coefs,\n                                           const Spherepack& target) const {\n  return prolong_or_restrict(spectral_coefs, l_max_, m_max_, target.l_max_,\n                             target.m_max_);\n}\n\nbool operator==(const Spherepack& lhs, const Spherepack& rhs) {\n  return lhs.l_max() == rhs.l_max() and lhs.m_max() == rhs.m_max();\n}\n\nbool operator!=(const Spherepack& lhs, const Spherepack& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template Spherepack::InterpolationInfo<DTYPE(data)>::InterpolationInfo( \\\n      size_t, size_t, const gsl::span<double>,                            \\\n      const std::array<DTYPE(data), 2>&);                                 \\\n  template Spherepack::InterpolationInfo<DTYPE(data)>                     \\\n  Spherepack::set_up_interpolation_info(                                  \\\n      const std::array<DTYPE(data), 2>& target_points) const;             \\\n  template void Spherepack::interpolate(                                  \\\n      gsl::not_null<DTYPE(data)*> result, gsl::not_null<const double*>,   \\\n      const Spherepack::InterpolationInfo<DTYPE(data)>&, size_t, size_t)  \\\n      const;                                                              \\\n  template void Spherepack::interpolate_from_coefs(                       \\\n      gsl::not_null<DTYPE(data)*> result, const DataVector&,              \\\n      const Spherepack::InterpolationInfo<DTYPE(data)>&, size_t, size_t)  \\\n      const;                                                              \\\n  template DTYPE(data) Spherepack::interpolate(                           \\\n      const DataVector&, const std::array<DTYPE(data), 2>&) const;        \\\n  template DTYPE(data) Spherepack::interpolate_from_coefs(                \\\n      const DataVector&, const std::array<DTYPE(data), 2>&) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicBuffer.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackHelper.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// Items related to spherical harmonics\nnamespace ylm {\n\n/*!\n * \\ingroup SpectralGroup\n *\n * \\brief Defines the C++ interface to SPHEREPACK.\n *\n * \\details The class `Spherepack` defines the C++ interface to the fortran\n * library SPHEREPACK used for computations on the surface of a sphere.\n *\n * Given a real-valued, scalar function \\f$g(\\theta, \\phi)\\f$, SPHEREPACK\n * expands it as:\n *\n * \\f{align}\n * g(\\theta, \\phi)\n * &=\\frac{1}{2}\\sum_{l=0}^{l_{\\max}}\\bar P_l^0(\\cos\\theta) a_{l0}\n * +\\sum_{l=1}^{l_{\\max}}\\sum_{m=1}^{\\min(l, m_{\\max})}\\bar P_l^m(\\cos\\theta)\\{\n *   a_{lm}\\cos m\\phi -b_{lm}\\sin m\\phi\\}\\label{eq:spherepack_expansion}\n * \\f}\n *\n * where \\f$a_{lm}\\f$ and \\f$b_{lm}\\f$ are real-valued\n * spectral coefficient arrays used by\n * SPHEREPACK, \\f$P_l^m(x)\\f$ are defined as\n *\n * \\f{align}\n * \\bar P_l^m(x)&=\\sqrt{\\frac{(2l+1)(l-m)!}{2(l+m)!}}\\;P_{lm}(x)\n * \\f}\n *\n * and \\f$P_{nm}(x)\\f$ are the associated Legendre polynomials as defined,\n * for example, in Jackson's \"Classical Electrodynamics\".\n *\n * #### Relationship to standard spherical harmonics\n *\n * The standard expansion of \\f$g(\\theta, \\phi)\\f$ in terms of scalar\n * spherical harmonics is\n * \\f{align}\n * g(\\theta, \\phi)\n * &=\n * \\sum_{l=0}^{l_{\\max}}\\sum_{m=-\\min(l, m_{\\max})}^{\\min(l, m_{\\max})}\n * A_{lm} Y_{lm}(\\theta,\\phi),\n * \\f}\n * where \\f$Y_{lm}(\\theta,\\phi)\\f$ are the usual complex-valued scalar\n * spherical harmonics (as defined, for example, in\n * Jackson's \"Classical Electrodynamics\")\n * and \\f$A_{lm}\\f$ are complex coefficients.\n *\n * The relationship between the complex coefficients \\f$A_{lm}\\f$ and\n * SPHEREPACK's real-valued \\f$a_{lm}\\f$ and \\f$b_{lm}\\f$ is\n * \\f{align}\n * a_{l0} & = \\sqrt{\\frac{2}{\\pi}}A_{l0}&\\qquad l\\geq 0,\\\\\n * a_{lm} & = (-1)^m\\sqrt{\\frac{2}{\\pi}} \\mathrm{Re}(A_{lm})\n * &\\qquad l\\geq 1, m\\geq 1, \\\\\n * b_{lm} & = (-1)^m\\sqrt{\\frac{2}{\\pi}} \\mathrm{Im}(A_{lm})\n * &\\qquad l\\geq 1, m\\geq 1.\n * \\f}\n *\n * \\note If \\f$g\\f$ is real,\n * \\f$A_{lm} = (-1)^m A^\\star_{l -m}\\f$ (where \\f${}^\\star\\f$ means\n * a complex conjugate); this is why we don't need to consider \\f$m<0\\f$\n * in the previous formulas or in SPHEREPACK's expansion.\n *\n * #### Relationship to real-valued spherical harmonics\n *\n * Sometimes it is useful to expand a real-valued function in the form\n * \\f{align}\n * g(\\theta, \\phi)\n * &= \\sum_{l=0}^\\infty\\sum_{m=0}^l\n * \\left[\n * c_{lm}\\mathrm{Re}(Y_{lm}(\\theta, \\phi))+\n * d_{nm}\\mathrm{Im}(Y_{lm}(\\theta, \\phi))\n * \\right].\n * \\f}\n * The coefficients here are therefore\n * \\f{align}\n * c_{l0} &= A_{l0},\\\\\n * c_{lm} &= 2\\mathrm{Re}(A_{lm}) \\qquad m\\geq 1,\\\\\n * d_{lm} &=-2\\mathrm{Im}(A_{lm}).\n * \\f}\n *\n * #### Modal and nodal representations\n *\n * Internally, SPHEREPACK can represent its expansion in two ways which we\n * will refer to as modal and nodal representations:\n *\n * -# modal: The spectral coefficient arrays \\f$a_{lm}\\f$ and \\f$b_{lm}\\f$,\n * referred to as `spectral_coefs` in the methods below. For this C++ interface,\n * they are saved in a single `DataVector`. To help you index the coefficients\n * as expected by this interface, use the class `SpherepackIterator`.\n *\n * -# nodal: The values at certain collocation points, referred to as\n * `collocation_values` in the methods below. This is an array of the expanded\n * function \\f$g(\\theta,\\phi)\\f$ evaluated at collocation values\n * \\f$(\\theta_i,\\phi_j)\\f$, where \\f$\\theta_i\\f$ are Gauss-Legendre quadrature\n * nodes in the interval \\f$(0, \\pi)\\f$ with \\f$i = 0, ..., l_{\\max}\\f$, and\n * \\f$\\phi_j\\f$ is distributed uniformly in \\f$(0, 2\\pi)\\f$ with \\f$i = 0, ...,\n * 2m_{\\max}\\f$. The angles of the collocation points can be computed with the\n * method `theta_phi_points`.\n *\n * To convert between the two representations the methods `spec_to_phys` and\n * `phys_to_spec` can be used. For internal calculations SPHEREPACK will usually\n * convert to spectral coefficients first, so it is in general more efficient to\n * use these directly.\n *\n * Most methods of SPHEREPACK will compute the requested values of e.g.\n * `gradient` or `scalar_laplacian` at the collocation points, effectively\n * returning an expansion in nodal form as defined above. To evaluate the\n * function at arbitrary angles \\f$\\theta\\f$, \\f$\\phi\\f$, these values have to\n * be \"interpolated\" (i.e. the new expansion evaluated) using `interpolate`.\n *\n * Spherepack stores two types of quantities:\n *   1. storage_, which is filled in the constructor and is always const.\n *   2. memory_pool_, which is dynamic and thread_local, and is overwritten\n *      by various member functions that need temporary storage.\n */\nclass Spherepack {\n public:\n  /// Type returned by gradient function.\n  using FirstDeriv = tnsr::i<DataVector, 2, Frame::ElementLogical>;\n  /// Type returned by second derivative function.\n  using SecondDeriv = tnsr::ij<DataVector, 2, Frame::ElementLogical>;\n\n  /// Struct to hold cached information at a set of target interpolation\n  /// points.\n  template <typename T>\n  struct InterpolationInfo {\n    InterpolationInfo(size_t l_max, size_t m_max, const gsl::span<double> pmm,\n                      const std::array<T, 2>& target_points);\n    T cos_theta;\n    // cos(m*phi)\n    DynamicBuffer<T> cos_m_phi;\n    // sin(m*phi)\n    DynamicBuffer<T> sin_m_phi;\n    // pbar_factor[m] = Pbar(m,m)*sin(theta)^m\n    DynamicBuffer<T> pbar_factor;\n\n    size_t size() const { return num_points_; }\n    size_t m_max() const { return m_max_; }\n    size_t l_max() const { return l_max_; }\n\n   private:\n    size_t l_max_;\n    size_t m_max_;\n    size_t num_points_;\n  };\n\n  /// Here l_max and m_max are the largest fully-represented l and m in\n  /// the Ylm expansion.\n  Spherepack(size_t l_max, size_t m_max);\n\n  /// @{\n  /// Static functions to return the correct sizes of vectors of\n  /// collocation points and spectral coefficients for a given l_max\n  /// and m_max.  Useful for allocating space without having to create\n  /// a Spherepack.\n  SPECTRE_ALWAYS_INLINE static constexpr size_t physical_size(\n      const size_t l_max, const size_t m_max) {\n    return (l_max + 1) * (2 * m_max + 1);\n  }\n  /// \\note `spectral_size` is the size of the buffer that holds the\n  /// coefficients; it is not the number of coefficients (which is\n  /// \\f$m_{\\rm max}^2+(l_{\\rm max}-m_{\\rm max})(2m_{\\rm max}+1)\\f$).\n  /// To simplify its internal indexing, SPHEREPACK uses a buffer with\n  /// more space than necessary. See SpherepackIterator for\n  /// how to index the coefficients in the buffer.\n  SPECTRE_ALWAYS_INLINE static constexpr size_t spectral_size(\n      const size_t l_max, const size_t m_max) {\n    return 2 * (l_max + 1) * (m_max + 1);\n  }\n  /// @}\n\n  /// @{\n  /// Sizes in physical and spectral space for this instance.\n  size_t l_max() const { return l_max_; }\n  size_t m_max() const { return m_max_; }\n  size_t physical_size() const { return n_theta_ * n_phi_; }\n  size_t spectral_size() const { return spectral_size_; }\n  /// @}\n\n  std::array<size_t, 2> physical_extents() const {\n    return {{n_theta_, n_phi_}};\n  }\n\n  /// @{\n  /// Collocation points theta and phi.\n  ///\n  /// The phi points are uniform in phi, with the first point\n  /// at phi=0.\n  ///\n  /// The theta points are Gauss-Legendre in \\f$\\cos(\\theta)\\f$,\n  /// so there are no points at the poles.\n  SPECTRE_ALWAYS_INLINE const std::vector<double>& theta_points() const {\n    return storage_.theta;\n  }\n  SPECTRE_ALWAYS_INLINE const std::vector<double>& phi_points() const {\n    return storage_.phi;\n  }\n  std::array<DataVector, 2> theta_phi_points() const;\n  /// @}\n\n  /// @{\n  /// Spectral transformations.\n  /// To act on a slice of the input and output arrays, specify strides\n  /// and offsets.\n  void phys_to_spec(gsl::not_null<double*> spectral_coefs,\n                    gsl::not_null<const double*> collocation_values,\n                    size_t physical_stride = 1, size_t physical_offset = 0,\n                    size_t spectral_stride = 1,\n                    size_t spectral_offset = 0) const {\n    phys_to_spec_impl(spectral_coefs, collocation_values, physical_stride,\n                      physical_offset, spectral_stride, spectral_offset, false);\n  }\n  void spec_to_phys(gsl::not_null<double*> collocation_values,\n                    gsl::not_null<const double*> spectral_coefs,\n                    size_t spectral_stride = 1, size_t spectral_offset = 0,\n                    size_t physical_stride = 1,\n                    size_t physical_offset = 0) const {\n    spec_to_phys_impl(collocation_values, spectral_coefs, spectral_stride,\n                      spectral_offset, physical_stride, physical_offset, false);\n  };\n  /// @}\n\n  /// @{\n  /// Spectral transformations where `collocation_values` and\n  /// `spectral_coefs` are assumed to point to 3-dimensional arrays\n  /// (I1 x S2 topology), and the transformations are done for all\n  /// 'radial' points at once by internally looping over all values of\n  /// the offset from zero to `stride`-1 (the physical and spectral\n  /// strides are equal and are called `stride`).\n  void phys_to_spec_all_offsets(gsl::not_null<double*> spectral_coefs,\n                                gsl::not_null<const double*> collocation_values,\n                                size_t stride) const {\n    phys_to_spec_impl(spectral_coefs, collocation_values, stride, 0, stride, 0,\n                      true);\n  }\n  void spec_to_phys_all_offsets(gsl::not_null<double*> collocation_values,\n                                gsl::not_null<const double*> spectral_coefs,\n                                size_t stride) const {\n    spec_to_phys_impl(collocation_values, spectral_coefs, stride, 0, stride, 0,\n                      true);\n  };\n  /// @}\n\n  /// @{\n  /// Simpler, less general interfaces to `phys_to_spec` and `spec_to_phys`.\n  /// Acts on a slice of the input and returns a unit-stride result.\n  DataVector phys_to_spec(const DataVector& collocation_values,\n                          size_t physical_stride = 1,\n                          size_t physical_offset = 0) const;\n  DataVector spec_to_phys(const DataVector& spectral_coefs,\n                          size_t spectral_stride = 1,\n                          size_t spectral_offset = 0) const;\n  /// @}\n\n  /// @{\n  /// Simpler, less general interfaces to `phys_to_spec_all_offsets`\n  /// and `spec_to_phys_all_offsets`.  Result has the same stride as\n  /// the input.\n  DataVector phys_to_spec_all_offsets(const DataVector& collocation_values,\n                                      size_t stride) const;\n  DataVector spec_to_phys_all_offsets(const DataVector& spectral_coefs,\n                                      size_t stride) const;\n  /// @}\n\n  /// Computes Pfaffian derivative (df/dtheta, csc(theta) df/dphi) at\n  /// the collocation values.\n  /// To act on a slice of the input and output arrays, specify stride\n  /// and offset (assumed to be the same for input and output).\n  void gradient(const std::array<double*, 2>& df,\n                gsl::not_null<const double*> collocation_values,\n                size_t physical_stride = 1, size_t physical_offset = 0) const;\n\n  /// Same as `gradient`, but takes the spectral coefficients (rather\n  /// than collocation values) of the function.  This is more\n  /// efficient if one happens to already have the spectral\n  /// coefficients.\n  /// To act on a slice of the input and output arrays, specify strides\n  /// and offsets.\n  void gradient_from_coefs(const std::array<double*, 2>& df,\n                           gsl::not_null<const double*> spectral_coefs,\n                           size_t spectral_stride = 1,\n                           size_t spectral_offset = 0,\n                           size_t physical_stride = 1,\n                           size_t physical_offset = 0) const {\n    gradient_from_coefs_impl(df, spectral_coefs, spectral_stride,\n                             spectral_offset, physical_stride, physical_offset,\n                             false);\n  }\n\n  /// @{\n  /// Same as `gradient` but pointers are assumed to point to\n  /// 3-dimensional arrays (I1 x S2 topology), and the gradient is\n  /// done for all 'radial' points at once by internally looping\n  /// over all values of the offset from zero to `stride`-1.\n  void gradient_all_offsets(const std::array<double*, 2>& df,\n                            gsl::not_null<const double*> collocation_values,\n                            size_t stride = 1) const;\n\n  SPECTRE_ALWAYS_INLINE void gradient_from_coefs_all_offsets(\n      const std::array<double*, 2>& df,\n      gsl::not_null<const double*> spectral_coefs, size_t stride = 1) const {\n    gradient_from_coefs_impl(df, spectral_coefs, stride, 0, stride, 0, true);\n  }\n  /// @}\n\n  /// @{\n  /// Simpler, less general interfaces to `gradient`.\n  /// Acts on a slice of the input and returns a unit-stride result.\n  FirstDeriv gradient(const DataVector& collocation_values,\n                      size_t physical_stride = 1,\n                      size_t physical_offset = 0) const;\n  FirstDeriv gradient_from_coefs(const DataVector& spectral_coefs,\n                                 size_t spectral_stride = 1,\n                                 size_t spectral_offset = 0) const;\n  /// @}\n\n  /// @{\n  /// Simpler, less general interfaces to `gradient_all_offsets`.\n  /// Result has the same stride as the input.\n  FirstDeriv gradient_all_offsets(const DataVector& collocation_values,\n                                  size_t stride = 1) const;\n  FirstDeriv gradient_from_coefs_all_offsets(const DataVector& spectral_coefs,\n                                             size_t stride = 1) const;\n  /// @}\n\n  /// Computes Laplacian in physical space.\n  /// To act on a slice of the input and output arrays, specify stride\n  /// and offset (assumed to be the same for input and output).\n  void scalar_laplacian(gsl::not_null<double*> scalar_laplacian,\n                        gsl::not_null<const double*> collocation_values,\n                        size_t physical_stride = 1,\n                        size_t physical_offset = 0) const;\n\n  /// Same as `scalar_laplacian` above, but the input is the spectral\n  /// coefficients (rather than collocation values) of the function.\n  /// This is more efficient if one happens to already have the\n  /// spectral coefficients.\n  /// To act on a slice of the input and output arrays, specify strides\n  /// and offsets.\n  void scalar_laplacian_from_coefs(gsl::not_null<double*> scalar_laplacian,\n                                   gsl::not_null<const double*> spectral_coefs,\n                                   size_t spectral_stride = 1,\n                                   size_t spectral_offset = 0,\n                                   size_t physical_stride = 1,\n                                   size_t physical_offset = 0) const;\n\n  /// @{\n  /// Simpler, less general interfaces to `scalar_laplacian`.\n  /// Acts on a slice of the input and returns a unit-stride result.\n  DataVector scalar_laplacian(const DataVector& collocation_values,\n                              size_t physical_stride = 1,\n                              size_t physical_offset = 0) const;\n  DataVector scalar_laplacian_from_coefs(const DataVector& spectral_coefs,\n                                         size_t spectral_stride = 1,\n                                         size_t spectral_offset = 0) const;\n  /// @}\n\n  /// Computes Pfaffian first and second derivative in physical space.\n  /// The first derivative is \\f$df(i) = d_i f\\f$, and the\n  /// second derivative is \\f$ddf(i,j) = d_i (d_j f)\\f$,\n  /// where \\f$d_0 = d/d\\theta\\f$ and \\f$d_1 = csc(\\theta) d/d\\phi\\f$.\n  /// ddf is not symmetric.\n  /// To act on a slice of the input and output arrays, specify stride\n  /// and offset (assumed to be the same for input and output).\n  void second_derivative(const std::array<double*, 2>& df,\n                         gsl::not_null<SecondDeriv*> ddf,\n                         gsl::not_null<const double*> collocation_values,\n                         size_t physical_stride = 1,\n                         size_t physical_offset = 0) const;\n\n  /// Simpler, less general interface to second_derivative\n  std::pair<FirstDeriv, SecondDeriv> first_and_second_derivative(\n      const DataVector& collocation_values) const;\n\n  /// Computes the integral over the sphere.\n  SPECTRE_ALWAYS_INLINE double definite_integral(\n      gsl::not_null<const double*> collocation_values,\n      size_t physical_stride = 1, size_t physical_offset = 0) const {\n    // clang-tidy: 'do not use pointer arithmetic'\n    return ddot_(n_theta_ * n_phi_, storage_.quadrature_weights.data(), 1,\n                 collocation_values.get() + physical_offset,  // NOLINT\n                 physical_stride);\n  }\n\n  /// Returns weights \\f$w_i\\f$ such that \\f$sum_i (c_i w_i)\\f$\n  /// is the definite integral, where \\f$c_i\\f$ are collocation values\n  /// at point i.\n  SPECTRE_ALWAYS_INLINE const std::vector<double>& integration_weights() const {\n    return storage_.quadrature_weights;\n  }\n\n  /// Adds a constant (i.e. \\f$f(\\theta,\\phi)\\f$ += \\f$c\\f$) to the function\n  /// given by the spectral coefficients, by modifying the coefficients.\n  SPECTRE_ALWAYS_INLINE static void add_constant(\n      const gsl::not_null<DataVector*> spectral_coefs, const double c) {\n    // The factor of sqrt(8) is because of the normalization of\n    // SPHEREPACK's coefficients.\n    (*spectral_coefs)[0] += sqrt(8.0) * c;\n  }\n\n  /// Returns the average of \\f$f(\\theta,\\phi)\\f$ over \\f$(\\theta,\\phi)\\f$.\n  SPECTRE_ALWAYS_INLINE static double average(\n      const DataVector& spectral_coefs) {\n    // The factor of sqrt(8) is because of the normalization of\n    // SPHEREPACK's coefficients.  All other coefficients average to zero.\n    return spectral_coefs[0] / sqrt(8.0);\n  }\n\n  /// Sets up the `InterpolationInfo` structure for interpolating onto\n  /// a set of target \\f$(\\theta,\\phi)\\f$ points.  Does not depend on\n  /// the function being interpolated.\n  template <typename T>\n  InterpolationInfo<T> set_up_interpolation_info(\n      const std::array<T, 2>& target_points) const;\n\n  /// Interpolates from `collocation_values` onto the points that have\n  /// been passed into the `set_up_interpolation_info` function.\n  /// To interpolate a different function on the same spectral grid, there\n  /// is no need to recompute `interpolation_info`.\n  /// If you specify stride and offset, acts on a slice of the input values.\n  /// The output has unit stride.\n  template <typename T>\n  void interpolate(gsl::not_null<T*> result,\n                   gsl::not_null<const double*> collocation_values,\n                   const InterpolationInfo<T>& interpolation_info,\n                   size_t physical_stride = 1,\n                   size_t physical_offset = 0) const;\n\n  /// Same as `interpolate`, but assumes you have spectral coefficients.\n  /// This is more efficient if you already have the spectral coefficients\n  /// available.\n  /// If you specify stride and offset, acts on a slice of the input coefs.\n  /// The output has unit stride.\n  template <typename T, typename R>\n  void interpolate_from_coefs(gsl::not_null<T*> result, const R& spectral_coefs,\n                              const InterpolationInfo<T>& interpolation_info,\n                              size_t spectral_stride = 1,\n                              size_t spectral_offset = 0) const;\n\n  /// Simpler interface to `interpolate`.  If you need to call this\n  /// repeatedly on different `spectral_coefs` or `collocation_values`\n  /// for the same target points, this is inefficient; instead use\n  /// `set_up_interpolation_info` and the functions that use\n  /// `InterpolationInfo`.\n  template <typename T>\n  T interpolate(const DataVector& collocation_values,\n                const std::array<T, 2>& target_points) const;\n  template <typename T>\n  T interpolate_from_coefs(const DataVector& spectral_coefs,\n                           const std::array<T, 2>& target_points) const;\n\n  /// Takes spectral coefficients compatible with a resolution given by\n  /// `l_max_coefs` and `m_max_coefs` and either prolongs them or restricts\n  /// them to be compatible with a resolution given by `l_max_target` and\n  /// `m_max_target`. This is done by truncation (restriction) or padding\n  /// with zeros (prolongation).\n  static DataVector prolong_or_restrict(const DataVector& spectral_coefs,\n                                        size_t l_max_coefs, size_t m_max_coefs,\n                                        size_t l_max_target,\n                                        size_t m_max_target);\n\n  /// Takes spectral coefficients compatible with `*this`, and either\n  /// prolongs them or restricts them to be compatible with `target`.\n  /// This is done by truncation (restriction) or padding with zeros\n  /// (prolongation).\n  DataVector prolong_or_restrict(const DataVector& spectral_coefs,\n                                 const Spherepack& target) const;\n\n private:\n  // Spectral transformations and gradient.\n  // If `loop_over_offset` is true, then `collocation_values` and\n  // `spectral_coefs` are assumed to point to 3-dimensional\n  // arrays (I1 x S2 topology), and the transformations are done for\n  // all 'radial' points at once by looping over all values of the\n  // offset from zero to stride-1.  If `loop_over_offset` is true,\n  // `physical_stride` must equal `spectral_stride`.\n  void phys_to_spec_impl(gsl::not_null<double*> spectral_coefs,\n                         gsl::not_null<const double*> collocation_values,\n                         size_t physical_stride = 1, size_t physical_offset = 0,\n                         size_t spectral_stride = 1, size_t spectral_offset = 0,\n                         bool loop_over_offset = false) const;\n  void spec_to_phys_impl(gsl::not_null<double*> collocation_values,\n                         gsl::not_null<const double*> spectral_coefs,\n                         size_t spectral_stride = 1, size_t spectral_offset = 0,\n                         size_t physical_stride = 1, size_t physical_offset = 0,\n                         bool loop_over_offset = false) const;\n  void gradient_from_coefs_impl(const std::array<double*, 2>& df,\n                                gsl::not_null<const double*> spectral_coefs,\n                                size_t spectral_stride = 1,\n                                size_t spectral_offset = 0,\n                                size_t physical_stride = 1,\n                                size_t physical_offset = 0,\n                                bool loop_over_offset = false) const;\n  void calculate_collocation_points();\n  void calculate_interpolation_data();\n  void fill_scalar_work_arrays();\n  void fill_vector_work_arrays();\n  size_t l_max_, m_max_, n_theta_, n_phi_;\n  size_t spectral_size_;\n  // memory_pool_ will be shared by multiple instances of\n  // Spherepack on the same thread.  Because these instances are on\n  // the same thread, member functions of two or more of these\n  // instances cannot be called simultaneously.  Note that member\n  // functions do not make any assumptions about the contents of\n  // memory_pool_ on entry, so between calls to member functions it is\n  // safe to resize objects in memory_pool_ or to overwrite them with\n  // arbitrary data.\n  static thread_local Spherepack_detail::MemoryPool memory_pool_;\n  Spherepack_detail::ConstStorage storage_;\n};  // class Spherepack\n\nbool operator==(const Spherepack& lhs, const Spherepack& rhs);\nbool operator!=(const Spherepack& lhs, const Spherepack& rhs);\n\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/SpherepackCache.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp\"\n\n#include <cstddef>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace ylm {\nconst Spherepack& get_spherepack_cache(const size_t l_max) {\n  static const auto spherepack_cache = make_static_cache<CacheRange<2, 151>>(\n      [](const size_t l) { return ylm::Spherepack(l, l); });\n  return spherepack_cache(l_max);\n}\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\nnamespace ylm {\nclass Spherepack;\n\n/// Retrieves a cached Spherepack object with m_max equal to l_max\nconst Spherepack& get_spherepack_cache(size_t l_max);\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/SpherepackHelper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <ostream>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackHelper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace ylm::Spherepack_detail {\n\nConstStorage::ConstStorage(const size_t l_max, const size_t m_max)\n    : l_max_(l_max), m_max_(m_max) {\n  point_spans_to_data_vector(true);\n}\n\nConstStorage::ConstStorage(const ConstStorage& rhs)\n    : work_interp_index(rhs.work_interp_index),\n      quadrature_weights(rhs.quadrature_weights),\n      theta(rhs.theta),\n      phi(rhs.phi),\n      storage_(rhs.storage_),\n      l_max_(rhs.l_max_),\n      m_max_(rhs.m_max_) {\n  point_spans_to_data_vector();\n}\n\nConstStorage& ConstStorage::operator=(const ConstStorage& rhs) {\n  l_max_ = rhs.l_max_;\n  m_max_ = rhs.m_max_;\n  work_interp_index = rhs.work_interp_index;\n  quadrature_weights = rhs.quadrature_weights;\n  theta = rhs.theta;\n  phi = rhs.phi;\n  storage_ = rhs.storage_;\n  point_spans_to_data_vector();\n  return *this;\n}\n\nConstStorage::ConstStorage(ConstStorage&& rhs)\n    : work_interp_index(std::move(rhs.work_interp_index)),\n      quadrature_weights(std::move(rhs.quadrature_weights)),\n      theta(std::move(rhs.theta)),\n      phi(std::move(rhs.phi)),\n      storage_(std::move(rhs.storage_)),\n      l_max_(rhs.l_max_),\n      m_max_(rhs.m_max_) {\n  point_spans_to_data_vector();\n}\n\nConstStorage& ConstStorage::operator=(ConstStorage&& rhs) {\n  l_max_ = rhs.l_max_;\n  m_max_ = rhs.m_max_;\n  work_interp_index = std::move(rhs.work_interp_index);\n  quadrature_weights = std::move(rhs.quadrature_weights);\n  theta = std::move(rhs.theta);\n  phi = std::move(rhs.phi);\n  storage_ = std::move(rhs.storage_);\n  point_spans_to_data_vector();\n  return *this;\n}\n\nvoid ConstStorage::point_spans_to_data_vector(const bool allocate) {\n  // Below note that n_theta, n_phi, l1, l2, and int_work_size are\n  // ints, not size_ts. The reason for this: The last term in the\n  // expression for int_work_size can sometimes be negative. If\n  // evaluated using unsigned ints instead of ints, it can underflow\n  // and give a huge number.\n  const auto n_theta = static_cast<int>(l_max_ + 1);\n  const auto n_phi = static_cast<int>(2 * m_max_ + 1);\n  const auto l1 = static_cast<int>(m_max_ + 1);\n  const auto l2 = (n_theta + 1) / 2;\n  const int int_work_size = n_phi + 15 + n_theta * (3 * (l1 + l2) - 2) +\n                            (l1 - 1) * (l2 * (2 * n_theta - l1) - 3 * l1) / 2;\n  ASSERT(int_work_size >= 0, \"Bad size \" << int_work_size);\n  const auto scalar_work_size = static_cast<size_t>(int_work_size);\n\n  // For vectors, the things that SPHEREPACK internally calls l1, mdb,\n  // and mdc have a minimum allowed size of\n  // min(n_theta_,(n_phi_+1)/2)).  However, here we set those\n  // quantities to min(n_theta_, (n_phi_+2)/2)) which is what\n  // SPHEREPACK requires for what it internally calls l1 and mdab for\n  // scalars.  This results in a simplification, but also a (very\n  // slightly) larger vector work array than technically required, for\n  // some choices of parameters.\n\n  // NOLINTNEXTLINE bugprone-misplaced-widening-cast\n  const auto vector_work_size = static_cast<size_t>(\n      n_theta * l2 * (n_theta + 1) + n_phi + 15 + 2 * n_theta);\n  // NOLINTNEXTLINE bugprone-misplaced-widening-cast\n  const auto quad_work_size = static_cast<size_t>(n_theta * n_phi);\n  const size_t theta_work_size = l_max_ + 1;\n  const size_t phi_work_size = 2 * m_max_ + 1;\n  const auto interp_work_size =\n      static_cast<size_t>(n_theta * l1 - l1 * (l1 - 1) / 2);\n  const size_t interp_work_pmm_size = m_max_ + 1;\n\n  const size_t full_size = 2 * scalar_work_size + vector_work_size +\n                           4 * theta_work_size + 2 * phi_work_size +\n                           2 * interp_work_size + interp_work_pmm_size;\n\n  if (allocate) {\n    work_interp_index.resize(interp_work_size);\n    quadrature_weights.resize(quad_work_size);\n    theta.resize(theta_work_size);\n    phi.resize(phi_work_size);\n    storage_ = DataVector(full_size);\n  }\n\n  size_t offset = 0;\n  auto set_up_span = [&offset, this](const size_t size) -> gsl::span<double> {\n    auto result = gsl::make_span(this->storage_.data() + offset, size);\n    offset += size;\n    return result;\n  };\n  work_phys_to_spec = set_up_span(scalar_work_size);\n  work_scalar_spec_to_phys = set_up_span(scalar_work_size);\n  work_vector_spec_to_phys = set_up_span(vector_work_size);\n  sin_theta = set_up_span(theta_work_size);\n  cos_theta = set_up_span(theta_work_size);\n  cosec_theta = set_up_span(theta_work_size);\n  cot_theta = set_up_span(theta_work_size);\n  sin_phi = set_up_span(phi_work_size);\n  cos_phi = set_up_span(phi_work_size);\n  work_interp_alpha = set_up_span(interp_work_size);\n  work_interp_beta = set_up_span(interp_work_size);\n  work_interp_pmm = set_up_span(interp_work_pmm_size);\n\n  ASSERT(offset == full_size, \"Bug in dividing up memory\");\n}\n\nstd::vector<double>& MemoryPool::get(size_t n_pts) {\n  for (auto& elem : memory_pool_) {\n    if (not elem.currently_in_use) {\n      elem.currently_in_use = true;\n      auto& temp = elem.storage;\n      if (temp.size() < n_pts) {\n        temp.resize(n_pts);\n      }\n      return temp;\n    }\n  }\n  ERROR(\"Attempt to allocate more than \" << num_temps_ << \" temps.\");\n}\n\n// We don't simply forward this to the const double* version\n// because std::vector<T>::data() might be nullptr for vectors\n// in the pool that are unallocated, for random vectors passed into\n// this function that have nothing to do with the pool, or for vectors\n// that are in the pool but have been size-zero allocated.\nvoid MemoryPool::free(const std::vector<double>& to_be_freed) {\n  for (auto& elem : memory_pool_) {\n    if (&(elem.storage) == &to_be_freed) {\n      elem.currently_in_use = false;\n      return;\n    }\n  }\n  ERROR(\"Attempt to free temp that was never allocated.\");\n}\n\nvoid MemoryPool::free(const gsl::not_null<double*> to_be_freed) {\n  for (auto& elem : memory_pool_) {\n    if (elem.storage.data() == to_be_freed) {\n      elem.currently_in_use = false;\n      return;\n    }\n  }\n  ERROR(\"Attempt to free temp that was never allocated.\");\n}\n\nvoid MemoryPool::clear() {\n  for (auto& elem : memory_pool_) {\n    if (elem.currently_in_use) {\n      ERROR(\"Attempt to clear element that is in use\");\n    }\n    elem.storage.clear();\n  }\n}\n\n}  // namespace ylm::Spherepack_detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/SpherepackHelper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm::Spherepack_detail {\n\n/// Holds the various constant 'work' arrays for Spherepack.\n/// These are computed only once during ylm::Spherepack construction\n/// and are re-used over and over again.\nclass ConstStorage {\n public:\n  ConstStorage(const size_t l_max, const size_t m_max);\n  ~ConstStorage() = default;\n  ConstStorage(const ConstStorage& rhs);\n  ConstStorage& operator=(const ConstStorage& rhs);\n  ConstStorage(ConstStorage&& rhs);\n  ConstStorage& operator=(ConstStorage&& rhs);\n\n  std::vector<size_t> work_interp_index;\n  // The following are vectors because they are returnable by\n  // member functions.\n  std::vector<double> quadrature_weights, theta, phi;\n  // All other storage is allocated in a single DataVector and then\n  // pointed to by gsl::spans.\n  DataVector storage_;\n  gsl::span<double> work_phys_to_spec, work_scalar_spec_to_phys;\n  gsl::span<double> work_vector_spec_to_phys;\n  gsl::span<double> sin_theta, cos_theta, cosec_theta, cot_theta;\n  gsl::span<double> sin_phi, cos_phi;\n  gsl::span<double> work_interp_alpha, work_interp_beta, work_interp_pmm;\n\n private:\n  size_t l_max_;\n  size_t m_max_;\n  void point_spans_to_data_vector(const bool allocate = false);\n};\n\n/// This is a quick way of providing temporary space that is\n/// re-utilized many times without the expense of mallocs.  This\n/// turned out to be important for optimizing SpEC (because\n/// ylm::Spherepack is used for the basis functions in the most\n/// expensive subdomains) but may or may not be important for SpECTRE.\nclass MemoryPool {\n public:\n  MemoryPool() = default;\n  std::vector<double>& get(size_t n_pts);\n  void free(const std::vector<double>& to_be_freed);\n  void free(gsl::not_null<double*> to_be_freed);\n  void clear();\n\n private:\n  struct StorageAndAvailability {\n    std::vector<double> storage;\n    bool currently_in_use{false};\n  };\n  // It turns out that the maximum number of temporaries needed in a\n  // single ylm::Spherepack calculation is 9.\n  static const constexpr size_t num_temps_ = 9;\n  std::array<StorageAndAvailability, num_temps_> memory_pool_;\n};\n}  // namespace ylm::Spherepack_detail\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n//\n// SPHEREPACK memory layout\n//\n// For a surface of topology S2 with a spherical harmonic basis and a\n// grid of n_th x n_ph points, the largest (partially) representable\n// spherical-harmonic Ylm modes (l,m) are:\n// * l_max = n_th-1\n// * m_max = min(l_max, n_ph/2) [integer division]\n//\n// Given collocation values on this n_th x n_ph grid, SPHEREPACK\n// stores the spectral coefficients in an array of length\n// (l_max+1)*(m_max+1).  The layout of this array is, in a\n// 2-dimensional representation\n//\n//                            m_max+1\n//     -----------------------------------------------------\n//     | (0,0)         x       x            x\n//     | (1,0)       (1,1)     x            x         ARRAY\n//     | (2,0)       (2,1)   (2,2)          x          \"a\"\n//     |   :           :       :            x\n//     | (m_max,  0)  ...            (m_max,   m_max)\n//     | (m_max+1,0)  ...            (m_max+1, m_max)\n//     |    :                  :            :\n//     | (l_max, 0)                  (l_max,   m_max)\n//     -----------------------------------------------------\n//     |    x        x          x          x\n//     |    x      (1,1)        x          x          ARRAY\n//     |    x      (2,1)      (2,2)        x           \"b\"\n//     |    :        :          :          x\n//     |    x    (m_max,  1)..      (m_max,   m_max)\n//     |    x    (m_max+1,1)..      (m_max+1, m_max)\n//     |    :         :               :\n//     |    x    (l_max,  1)..      (l_max,   m_max)\n//     -----------------------------------------------------\n//\n//\n// In this table,\n//\n// * There are 2*n_th rows and m_max+1 columns. The SPHEREPACK array\n//   stores this data \"row by row\", i.e. the numbers across a row vary\n//   fastest.\n//\n// * There are no entries with negative m. SPHEREPACK uses positive m\n//   only, but with complex spectral coefficients. The portions \"a\"\n//   and \"b\" of the SPHEREPACK array hold the real and imaginary parts\n//   of the spectral coefficients.\n//\n// * Each \"x\" stands for an entry that is not referenced by SPHEREPACK\n//   (so SPHEREPACK wastes some storage space).  There is also one\n//   complete unreferenced row in the middle: the first row of the \"b\"\n//   array (unless zero_m_is_real is false).\n//\n// * Note that the first column of the \"b\" array is unreferenced. However,\n//   if zero_m_is_real is false, then this first column\n//   contains the imaginary parts of the m=0 modes, in the same format as\n//   the first row of the \"a\" array.\n//\n// Note that SPHEREPACK accepts arbitrary values of n_th and n_ph as\n// input, and then computes the corresponding l_max, m_max.  However,\n// some combinations of n_th and n_ph have strange properties:\n// * The maximum M that is AT LEAST PARTIALLY represented by (n_th,n_ph)\n//   is std::min(n_th-1,n_ph/2). This is called m_max here. But an arbitrary\n//   (n_th,n_ph) does not necessarily fully represent all m's up to m_max,\n//   because sin(m_max phi) might be zero at all collocation points, and\n//   therefore sin(m_max phi) might not be representable on the grid.\n// * The largest m that is fully represented by (n_th,n_ph) is\n//   m_max_represented = std::min(n_th-1,(n_ph-1)/2).\n// * Therefore, if n_ph is odd,  m_max = m_max_represented,\n//              if n_ph is even, m_max = m_max_represented+1.\n// * To fully represent a desired (l_max, m_max), the grid resolution\n//   should satisfy\n//     n_th = l_max+1\n//     n_ph = 2*m_max_represented+1\n//   which ensures that m_max = m_max_represented.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n\nnamespace ylm {\nSpherepackIterator::SpherepackIterator(const size_t l_max_input,\n                                       const size_t m_max_input,\n                                       const size_t stride /*=1*/,\n                                       const bool zero_m_is_real)\n    : l_max_(l_max_input),\n      m_max_(m_max_input),\n      n_th_(l_max_ + 1),\n      n_ph_(2 * m_max_ + 1),\n      stride_(stride),\n      zero_m_is_real_(zero_m_is_real),\n      number_of_valid_entries_in_a_((m_max_ + 1) * (m_max_ + 2) / 2 +\n                                    (m_max_ + 1) * (l_max_ - m_max_)),\n      current_compact_index_(0) {\n  // fill offset_into_spherepack_array, compact_l_ and compact_m_ with the\n  // indices, l-values and m-values of all valid points\n  const size_t packed_size = [this]() {\n    if (zero_m_is_real_) {\n      return (m_max_ + 1) * (m_max_ + 2) / 2 + m_max_ * (m_max_ + 1) / 2 +\n             (m_max_ + 1) * (l_max_ - m_max_) + m_max_ * (l_max_ - m_max_);\n    } else {\n      return 2 * number_of_valid_entries_in_a_;\n    }\n  }();\n\n  offset_into_spherepack_array.assign(packed_size, 0);\n  compact_l_.assign(packed_size, 0);\n  compact_m_.assign(packed_size, 0);\n  offset_to_compact_index_.assign(spherepack_array_size() * stride_,\n                                  std::nullopt);\n\n  // go through arrays and fill them\n\n  // index corresponding to strided coefficient array\n  size_t idx = 0;\n  size_t idx_no_stride = 0;\n  // index for compact offset_into_spherepack_array, compact_l_,\n  // compact_m_\n  size_t k = 0;\n  for (size_t l = 0; l <= l_max_; ++l) {\n    for (size_t m = 0; m <= m_max_; ++m) {\n      // note: index m varies fastest in fortran array\n      if (l >= m) {  // valid entry in a\n        offset_into_spherepack_array[k] = idx;\n        offset_to_compact_index_[idx_no_stride] = k;\n        compact_l_[k] = l;\n        compact_m_[k] = m;\n        ++k;\n      }\n      idx += stride;\n      ++idx_no_stride;\n    }\n  }\n  for (size_t l = 0; l <= l_max_; ++l) {\n    for (size_t m = 0; m <= m_max_; ++m) {\n      // note: index m varies fastest in fortran array\n      if (zero_m_is_real_) {\n        if (m >= 1 and l >= m) {  // valid entry in b\n          offset_into_spherepack_array[k] = idx;\n          offset_to_compact_index_[idx_no_stride] = k;\n          compact_l_[k] = l;\n          compact_m_[k] = m;\n          ++k;\n        }\n      } else {\n        if (l >= m) {  // valid entry in b\n          offset_into_spherepack_array[k] = idx;\n          offset_to_compact_index_[idx_no_stride] = k;\n          compact_l_[k] = l;\n          compact_m_[k] = m;\n          ++k;\n        }\n      }\n      idx += stride;\n      ++idx_no_stride;\n    }\n  }\n}\n\nSpherepackIterator& SpherepackIterator::reset() {\n  current_compact_index_ = 0;\n  return *this;\n}\n\nSpherepackIterator& SpherepackIterator::set(\n    const size_t l_input, const size_t m_input,\n    const CoefficientArray coefficient_array_input) {\n  ASSERT(l_input <= l_max_, \"SpherepackIterator l_input=\"\n                                << l_input\n                                << \" too large, should be <= \" << l_max_);\n  ASSERT(l_input >= m_input, \"SpherepackIterator m_input \"\n                                 << m_input << \" greater than l_input \"\n                                 << l_input);\n  ASSERT(m_input <= m_max_,\n         \"m_input \" << m_input << \" too large, should be <= \" << m_max_);\n  if (coefficient_array_input == CoefficientArray::a) {\n    // jump to first element with 'l_input'\n    if (l_input <= m_max_ + 1) {\n      current_compact_index_ = (l_input * (l_input + 1)) / 2;\n    } else {\n      current_compact_index_ = (m_max_ + 1) * (m_max_ + 2) / 2 +\n                               (l_input - m_max_ - 1) * (m_max_ + 1);\n    }\n    current_compact_index_ += m_input;\n  } else {\n    if (zero_m_is_real_) {\n      ASSERT(m_input != 0, \"Array b does not contain m_input=0\");\n      if (l_input <= m_max_ + 1) {\n        current_compact_index_ = (l_input * (l_input - 1)) / 2;\n      } else {\n        current_compact_index_ =\n            m_max_ * (m_max_ + 1) / 2 + (l_input - m_max_ - 1) * m_max_;\n      }\n      current_compact_index_ += number_of_valid_entries_in_a_ + m_input - 1;\n    } else {\n      if (l_input <= m_max_ + 1) {\n        current_compact_index_ = (l_input * (l_input + 1)) / 2;\n      } else {\n        current_compact_index_ = (m_max_ + 1) * (m_max_ + 2) / 2 +\n                                 (l_input - m_max_ - 1) * (m_max_ + 1);\n      }\n      current_compact_index_ += number_of_valid_entries_in_a_ + m_input;\n    }\n  }\n\n  ASSERT(current_compact_index_ < offset_into_spherepack_array.size(),\n         \"Expected \" << current_compact_index_ << \" to be less than \"\n                     << offset_into_spherepack_array.size());\n  ASSERT(l_input == compact_l_[current_compact_index_],\n         \"Expected \" << l_input << \" but set produced \"\n                     << compact_l_[current_compact_index_]);\n  ASSERT(m_input == compact_m_[current_compact_index_],\n         \"Expected \" << m_input << \" but set produced \"\n                     << compact_m_[current_compact_index_]);\n\n  return *this;\n}\n\nSpherepackIterator& SpherepackIterator::set(const size_t compact_index) {\n  ASSERT(compact_index < offset_into_spherepack_array.size(),\n         \"Trying to set the current compact index to \"\n             << compact_index\n             << \" which is beyond the size of the offset array \"\n             << offset_into_spherepack_array.size());\n\n  current_compact_index_ = compact_index;\n  return *this;\n}\n\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\n/// \\file\n/// Defines class SpherepackIterator.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <ostream>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace ylm {\n\n/*!\n * \\ingroup SpectralGroup\n * \\brief\n * Iterates over spectral coefficients stored in SPHEREPACK format.\n *\n * \\details\n * The internal SPHEREPACK ordering is not intuitive, so\n * SpherepackIterator exists for the purpose of iterating over an\n * array containing SPHEREPACK coefficients and determining the\n * (l,m) of each entry in the array.\n *\n * SPHEREPACK expands \\f$f(\\theta,\\phi)\\f$ as\n * \\f[\n * f(\\theta,\\phi) =\n * \\frac{1}{2} \\sum_{l=0}^{l_{max}} \\bar{P}_l^0 a(0,l)\n *   + \\sum_{m=1}^{m_{max}} \\sum_{l=m}^{l_{max}} \\bar{P}_l^m\n *         \\left(  a(m,l) \\cos(m \\phi) - b(m,l) \\sin(m \\phi)\\right)\n * \\f]\n * where \\f$a(m,l)\\f$ and \\f$b(m,l)\\f$ are the SPHEREPACK\n * spectral coefficients, and \\f$\\bar{P}_l^m\\f$ are\n * unit-orthonormal associated Legendre polynomials:\n * \\f[\n * \\bar{P}_l^m =\n * (-1)^m \\sqrt{\\frac{(2l+1)(l-m)!}{2(l+m)!}} P_l^m,\n * \\f]\n * where \\f$P_l^m\\f$ are the associated Legendre polynomials as defined\n * for example in Jackson \"Classical Electrodynamics\".\n * \\example\n * \\snippet Test_SpherepackIterator.cpp spherepack_iterator_example\n */\nclass SpherepackIterator {\n public:\n  /// SPHEREPACK has two coefficient variables, 'a' and 'b', that hold\n  /// the cos(m*phi) and sin(m*phi) parts of the spectral\n  /// coefficients.\n  enum class CoefficientArray { a, b };\n\n  /// Construct a SpherepackIterator.\n  ///\n  /// For all uses of SpherepackIterator by the SPHEREPACK routines,\n  /// zero_m_is_real should be true, indicating that the m=0 values of\n  /// the coefficients are real (or in other words, the 'b' array has\n  /// no m=0 values).\n  ///\n  /// However, we sometimes use the same storage scheme (and hence\n  /// SpherepackIterator) to hold spin-weighted-spherical-harmonic\n  /// coefficients of complex quantities, in which case the m=0 values\n  /// of the coefficients can be complex; in that case, zero_m_is_real\n  /// should be false.\n  SpherepackIterator(size_t l_max_input, size_t m_max_input, size_t stride = 1,\n                     bool zero_m_is_real = true);\n\n  size_t l_max() const { return l_max_; }\n  size_t m_max() const { return m_max_; }\n  size_t n_th() const { return n_th_; }\n  size_t n_ph() const { return n_ph_; }\n  size_t stride() const { return stride_; }\n  bool zero_m_is_real() const { return zero_m_is_real_; }\n\n  /// Size of a SPHEREPACK coefficient array (a and b combined), not\n  /// counting stride.  For non-unit stride, the size of the array\n  /// should be spherepack_array_size()*stride.\n  size_t spherepack_array_size() const {\n    return (l_max_ + 1) * (m_max_ + 1) * 2;\n  }\n\n  SpherepackIterator& operator++() {\n    ASSERT(current_compact_index_ != offset_into_spherepack_array.size(),\n           \"Incrementing an invalid iterator: \"\n               << current_compact_index_ << \" \"\n               << offset_into_spherepack_array.size());\n    ++current_compact_index_;\n    return *this;\n  }\n\n  explicit operator bool() const {\n    return current_compact_index_ < offset_into_spherepack_array.size();\n  }\n\n  /// Current index into a SPHEREPACK coefficient array.\n  size_t operator()() const {\n    return offset_into_spherepack_array[current_compact_index_];\n  }\n\n  /// Given an offset into a SPHEREPACK coefficient array, return the compact\n  /// index corresponding to that offset.\n  ///\n  /// Essentially the inverse of operator(). If the offset points to an element\n  /// that SPHEREPACK doesn't actually use (i.e. no compact index can reach the\n  /// given offset), then a std::nullopt is returned.\n  std::optional<size_t> compact_index(const size_t offset) const {\n    return offset_to_compact_index_[offset];\n  }\n\n  /// Returns the current compact index that SpherepackIterator uses internally.\n  /// This does not index a SPHEREPACK coefficient array. \\see operator() for an\n  /// index into a SPHEREPACK coefficient array\n  size_t current_compact_index() const { return current_compact_index_; }\n\n  /// Current values of l and m.\n  size_t l() const { return compact_l_[current_compact_index_]; }\n  size_t m() const { return compact_m_[current_compact_index_]; }\n  ///  Whether the iterator points to an element of 'a' or 'b',\n  ///  i.e. points to the cos(m*phi) or sin(m*phi) part of a spectral\n  ///  coefficient.\n  CoefficientArray coefficient_array() const {\n    return (current_compact_index_ < number_of_valid_entries_in_a_\n                ? CoefficientArray::a\n                : CoefficientArray::b);\n  }\n\n  /// Reset iterator back to beginning value. Returns *this.\n  SpherepackIterator& reset();\n\n  /// Set iterator to specific value of l, m, array.  Returns *this.\n  SpherepackIterator& set(size_t l_input, size_t m_input,\n                          CoefficientArray coefficient_array_input);\n\n  /// Same as 'set' above, but assumes CoefficientArray is 'a' for m>=0 and\n  /// 'b' for m<0.  This is useful when converting between true\n  /// spherical harmonics (which allow negative values of m) and\n  /// SPHEREPACK coefficients (which have only positive values of m,\n  /// but two arrays for sin(m*phi) and cos(m*phi) parts).\n  SpherepackIterator& set(size_t l_input, int m_input) {\n    return set(l_input, size_t(abs(m_input)),\n               m_input >= 0 ? CoefficientArray::a : CoefficientArray::b);\n  }\n\n  /// Set iterator to a specific compact index. Returns *this.\n  SpherepackIterator& set(size_t compact_index);\n\n private:\n  size_t l_max_, m_max_, n_th_, n_ph_, stride_;\n  bool zero_m_is_real_;\n  size_t number_of_valid_entries_in_a_;\n  size_t current_compact_index_;\n  std::vector<size_t> offset_into_spherepack_array;\n  std::vector<size_t> compact_l_, compact_m_;\n  std::vector<std::optional<size_t>> offset_to_compact_index_;\n};\n\ninline bool operator==(const SpherepackIterator& lhs,\n                       const SpherepackIterator& rhs) {\n  return lhs.l_max() == rhs.l_max() and lhs.m_max() == rhs.m_max() and\n         lhs.stride() == rhs.stride() and lhs() == rhs() and\n         lhs.zero_m_is_real() == rhs.zero_m_is_real();\n}\n\ninline bool operator!=(const SpherepackIterator& lhs,\n                       const SpherepackIterator& rhs) {\n  return not(lhs == rhs);\n}\n\ninline std::ostream& operator<<(\n    std::ostream& os,\n    const SpherepackIterator::CoefficientArray& coefficient_array) {\n  return os << (coefficient_array == SpherepackIterator::CoefficientArray::a\n                    ? 'a'\n                    : 'b');\n}\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Strahlkorper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\nnamespace Frame {\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace ylm {\ntemplate <typename Frame>\nStrahlkorper<Frame>::Strahlkorper(const size_t l_max, const size_t m_max,\n                                  const double radius,\n                                  std::array<double, 3> center)\n    : l_max_(l_max),\n      m_max_(m_max),\n      ylm_(l_max, m_max),\n      // clang-tidy: do not std::move trivially constructable types\n      center_(std::move(center)),  // NOLINT\n      strahlkorper_coefs_(ylm_.spectral_size(), 0.0) {\n  ylm::Spherepack::add_constant(&strahlkorper_coefs_, radius);\n}\n\ntemplate <typename Frame>\nStrahlkorper<Frame>::Strahlkorper(\n    const size_t l_max, const size_t m_max,\n    const DataVector& radius_at_collocation_points,\n    std::array<double, 3> center)\n    : l_max_(l_max),\n      m_max_(m_max),\n      ylm_(l_max, m_max),\n      center_(center),\n      strahlkorper_coefs_(ylm_.phys_to_spec(radius_at_collocation_points)) {\n  ASSERT(radius_at_collocation_points.size() == ylm_.physical_size(),\n         \"Bad size \" << radius_at_collocation_points.size() << \", expected \"\n                     << ylm_.physical_size());\n}\n\ntemplate <typename Frame>\nStrahlkorper<Frame>::Strahlkorper(const size_t l_max, const size_t m_max,\n                                  const ModalVector& spectral_coefficients,\n                                  std::array<double, 3> center)\n    : l_max_(l_max), m_max_(m_max), ylm_(l_max, m_max), center_(center) {\n  ASSERT(spectral_coefficients.size() == ylm_.spectral_size(),\n         \"Bad size \" << spectral_coefficients.size() << \", expected \"\n                     << ylm_.spectral_size());\n\n  strahlkorper_coefs_.destructive_resize(ylm_.spectral_size());\n  // We can remove this copy once strahlkorper_coefs_ are stored as ModalVector\n  std::copy(spectral_coefficients.begin(), spectral_coefficients.end(),\n            strahlkorper_coefs_.begin());\n}\n\ntemplate <typename Frame>\nvoid Strahlkorper<Frame>::pup(PUP::er& p) {\n  p | l_max_;\n  p | m_max_;\n  p | center_;\n  p | strahlkorper_coefs_;\n\n  if (p.isUnpacking()) {\n    ylm_ = ylm::Spherepack(l_max_, m_max_);\n  }\n}\n\ntemplate <typename Frame>\nstd::array<double, 3> Strahlkorper<Frame>::physical_center() const {\n  // Uses Eqs. (38)-(40) in Hemberger et al, arXiv:1211.6079.  This is\n  // an approximation of Eq. (37) in the same paper, which gives the\n  // exact result.\n  std::array<double, 3> result = center_;\n  SpherepackIterator it(l_max_, m_max_);\n  result[0] += strahlkorper_coefs_[it.set(1, 1)()] * sqrt(0.75);\n  result[1] -= strahlkorper_coefs_[it.set(1, -1)()] * sqrt(0.75);\n  result[2] += strahlkorper_coefs_[it.set(1, 0)()] * sqrt(0.375);\n  return result;\n}\n\ntemplate <typename Frame>\ndouble Strahlkorper<Frame>::average_radius() const {\n  return ylm::Spherepack::average(coefficients());\n}\n\ntemplate <typename Frame>\ndouble Strahlkorper<Frame>::radius(const double theta, const double phi) const {\n  return ylm_.interpolate_from_coefs<double>(strahlkorper_coefs_, {theta, phi});\n}\n\ntemplate <typename Frame>\nbool Strahlkorper<Frame>::point_is_contained(\n    const std::array<double, 3>& x) const {\n  // The point `x` is assumed to be in Cartesian coords in the\n  // Strahlkorper frame.\n\n  // Make the point relative to the center of the Strahlkorper.\n  auto xmc = x - center_;\n\n  // Is the point inside the surface?\n  const double theta = atan2(std::hypot(xmc[0], xmc[1]), xmc[2]);\n  const double phi = atan2(xmc[1], xmc[0]);\n  // Note that atan2 returns phi in (-pi,pi], whereas our convention\n  // for spherical coordinates assumes phi is in [0,2pi).  In some\n  // contexts (e.g. matching to points in a spherical-coordinate\n  // element) this would cause problems, so we would need to add 2pi\n  // to phi if phi were negative.  But here we don't need to do this,\n  // because inside the 'radius' function, the only thing that phi is\n  // used for is computing cos(m*phi) and sin(m*phi) for integer m.\n  return magnitude(xmc) < radius(theta, phi);\n}\n\ntemplate class Strahlkorper<Frame::Inertial>;\ntemplate class Strahlkorper<Frame::Grid>;\ntemplate class Strahlkorper<Frame::Distorted>;\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace Options {\ntemplate <typename... AlternativeLists>\nstruct Alternatives;\n}  // namespace Options\n/// \\endcond\n\nnamespace ylm {\n/// \\ingroup SurfacesGroup\n/// \\brief A star-shaped surface expanded in spherical harmonics.\ntemplate <typename Frame>\nclass Strahlkorper {\n public:\n  struct LMax {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Strahlkorper is expanded in Ylms up to l=LMax\"};\n  };\n  struct Radius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Radius of spherical Strahlkorper\"};\n  };\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Center of spherical Strahlkorper\"};\n  };\n  struct H5Filename {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"H5 file containing the Strahlkorper coefficients\"};\n  };\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"Subfile (without leading slash or .dat extension) within \"\n        \"the H5 file that contains the Strahlkorper coefficients\"};\n  };\n  struct Time {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Time at which to read the Strahlkorper coefficients\"};\n  };\n  struct TimeEpsilon {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Tolerance for matching the requested time to read the Strahlkorper \"\n        \"coefficients\"};\n  };\n  struct CheckFrame {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether to check that the frame in the file matches the requested \"\n        \"frame\"};\n  };\n  using options =\n      tmpl::list<LMax,\n                 Options::Alternatives<tmpl::list<Radius, Center>,\n                                       tmpl::list<H5Filename, SubfileName, Time,\n                                                  TimeEpsilon, CheckFrame>>>;\n\n  static constexpr Options::String help{\n      \"A star-shaped surface expressed as an expansion in spherical \"\n      \"harmonics.\\n\"\n      \"A Strahlkorper can be constructed from Options in two ways:\\n\"\n      \"1. Specify LMax, Center, and Radius to construct a spherical \"\n      \"Strahlkorper.\\n\"\n      \"2. Specify LMax, H5Filename, SubfileName, Time, TimeEpsilon, and \"\n      \"CheckFrame to construct a Strahlkorper from coefficients read from an \"\n      \"H5 file. The Strahlkorper will be prolonged or restricted to the \"\n      \"specified LMax.\"};\n\n  // Pup needs default constructor\n  Strahlkorper() = default;\n  Strahlkorper(const Strahlkorper&) = default;\n  Strahlkorper(Strahlkorper&&) = default;\n  Strahlkorper& operator=(const Strahlkorper&) = default;\n  Strahlkorper& operator=(Strahlkorper&&) = default;\n\n  /// Construct a sphere of radius `radius` with a given center.\n  Strahlkorper(size_t l_max, size_t m_max, double radius,\n               std::array<double, 3> center);\n\n  /// Construct a sphere of radius `radius`, setting `m_max`=`l_max`.\n  Strahlkorper(size_t l_max, double radius, std::array<double, 3> center)\n      : Strahlkorper(l_max, l_max, radius, center) {}\n\n  /// Construct from options: either from radius and center, or from file.\n  /// These constructors handle both alternatives specified in the options.\n  Strahlkorper(size_t l_max, const std::string& h5_filename,\n               const std::string& subfile_name, double time,\n               double time_epsilon, bool check_frame,\n               const Options::Context& context);\n\n  /// Construct a Strahlkorper from a DataVector containing the radius\n  /// at the collocation points.\n  ///\n  /// \\note The collocation points of the constructed Strahlkorper\n  /// will not be exactly `radius_at_collocation_points`.  Instead,\n  /// the constructed Strahlkorper will match the shape given by\n  /// `radius_at_collocation_points` only to order (`l_max`,`m_max`).\n  /// This is because the ylm::Spherepack representation of the\n  /// Strahlkorper has more collocation points than spectral\n  /// coefficients.  Specifically, `radius_at_collocation_points` has\n  /// \\f$(l_{\\rm max} + 1) (2 m_{\\rm max} + 1)\\f$ degrees of freedom,\n  /// but because there are only\n  /// \\f$m_{\\rm max}^2+(l_{\\rm max}-m_{\\rm max})(2m_{\\rm max}+1)\\f$\n  /// spectral coefficients, it is not possible to choose spectral\n  /// coefficients to exactly match all points in\n  /// `radius_at_collocation_points`.\n  explicit Strahlkorper(size_t l_max, size_t m_max,\n                        const DataVector& radius_at_collocation_points,\n                        std::array<double, 3> center);\n\n  /// \\brief Construct a Strahlkorper from a `ModalVector` containing the\n  /// spectral coefficients\n  ///\n  /// \\details The spectral coefficients should be in the form defined by\n  /// `ylm::Strahlkorper::coefficients() const`.\n  explicit Strahlkorper(size_t l_max, size_t m_max,\n                        const ModalVector& spectral_coefficients,\n                        std::array<double, 3> center);\n\n  /// Copies a Strahlkorper from another frame into this Strahlkorper.\n  ///\n  /// \\note The `OtherFrame` is ignored.\n  template <typename OtherFrame>\n  explicit Strahlkorper(const Strahlkorper<OtherFrame>& another_strahlkorper)\n      : l_max_(another_strahlkorper.l_max()),\n        m_max_(another_strahlkorper.m_max()),\n        ylm_(l_max_, m_max_),\n        center_(another_strahlkorper.expansion_center()),\n        strahlkorper_coefs_(another_strahlkorper.coefficients()) {}\n\n  /// Prolong or restrict another surface to the given `l_max` and `m_max`.\n  ///\n  /// \\note The `OtherFrame` is ignored.\n  template <typename OtherFrame>\n  Strahlkorper(size_t l_max, size_t m_max,\n               const Strahlkorper<OtherFrame>& another_strahlkorper)\n      : l_max_(l_max),\n        m_max_(m_max),\n        ylm_(l_max, m_max),\n        center_(another_strahlkorper.expansion_center()),\n        strahlkorper_coefs_(\n            another_strahlkorper.ylm_spherepack().prolong_or_restrict(\n                another_strahlkorper.coefficients(), ylm_)) {}\n\n  /// Construct a Strahlkorper from another Strahlkorper,\n  /// but explicitly specifying the coefficients.\n  /// Here coefficients are in the same storage scheme\n  /// as the `coefficients()` member function returns.\n  ///\n  /// \\note The `OtherFrame` is ignored.\n  template <typename OtherFrame>\n  Strahlkorper(DataVector coefs,\n               const Strahlkorper<OtherFrame>& another_strahlkorper)\n      : l_max_(another_strahlkorper.l_max()),\n        m_max_(another_strahlkorper.m_max()),\n        ylm_(another_strahlkorper.ylm_spherepack()),\n        center_(another_strahlkorper.expansion_center()),\n        strahlkorper_coefs_(std::move(coefs)) {\n    ASSERT(\n        strahlkorper_coefs_.size() ==\n            another_strahlkorper.ylm_spherepack().spectral_size(),\n        \"Bad size \" << strahlkorper_coefs_.size() << \", expected \"\n                    << another_strahlkorper.ylm_spherepack().spectral_size());\n  }\n\n  /// Serialization for Charm++\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /*!\n   *  These coefficients are stored as SPHEREPACK coefficients.\n   *  Suppose you represent a set of coefficients \\f$F^{lm}\\f$ in the expansion\n   *  \\f[\n   *  f(\\theta,\\phi) =\n   *  \\sum_{l=0}^{l_{max}} \\sum_{m=-l}^{l} F^{lm} Y^{lm}(\\theta,\\phi)\n   *  \\f]\n   *  Here the \\f$Y^{lm}(\\theta,\\phi)\\f$ are the usual complex-valued scalar\n   *  spherical harmonics, so \\f$F^{lm}\\f$ are also complex-valued.\n   *  But here we assume that \\f$f(\\theta,\\phi)\\f$ is real, so therefore\n   *  the \\f$F^{lm}\\f$ obey \\f$F^{l-m} = (-1)^m (F^{lm})^\\star\\f$. So one\n   *  does not need to store both real and imaginary parts for both positive\n   *  and negative \\f$m\\f$, and the stored coefficients can all be real.\n   *\n   *  So the stored coefficients are:\n   * \\f{align}\n   *  \\text{coefficients()(l,m)} &= (-1)^m \\sqrt{\\frac{2}{\\pi}}\n   *     \\Re(F^{lm}) \\quad \\text{for} \\quad m\\ge 0, \\\\\n   *  \\text{coefficients()(l,m)} &= (-1)^m \\sqrt{\\frac{2}{\\pi}}\n   *     \\Im(F^{lm}) \\quad \\text{for} \\quad m<0\n   * \\f}\n   */\n  SPECTRE_ALWAYS_INLINE const DataVector& coefficients() const {\n    return strahlkorper_coefs_;\n  }\n  SPECTRE_ALWAYS_INLINE DataVector& coefficients() {\n    return strahlkorper_coefs_;\n  }\n\n  /// Point about which the spectral basis of the Strahlkorper is expanded.\n  /// The center is given in the frame in which the Strahlkorper is defined.\n  /// This center must be somewhere inside the Strahlkorper, but in principle\n  /// it can be anywhere.  See `physical_center()` for a different measure.\n  SPECTRE_ALWAYS_INLINE const std::array<double, 3>& expansion_center() const {\n    return center_;\n  }\n\n  /// Approximate physical center (determined by \\f$l=1\\f$ coefficients)\n  /// Implementation of Eqs. (38)-(40) in \\cite Hemberger2012jz\n  std::array<double, 3> physical_center() const;\n\n  /// Average radius of the surface (determined by \\f$Y_{00}\\f$ coefficient)\n  double average_radius() const;\n\n  /// Maximum \\f$l\\f$ in \\f$Y_{lm}\\f$ decomposition.\n  SPECTRE_ALWAYS_INLINE size_t l_max() const { return l_max_; }\n\n  /// Maximum \\f$m\\f$ in \\f$Y_{lm}\\f$ decomposition.\n  SPECTRE_ALWAYS_INLINE size_t m_max() const { return m_max_; }\n\n  /// Radius at a particular angle \\f$(\\theta,\\phi)\\f$.\n  /// This is inefficient if done at multiple points many times.\n  /// See ylm::Spherepack for alternative ways of computing this.\n  double radius(double theta, double phi) const;\n\n  /// Determine if a point `x` is contained inside the surface.\n  /// The point must be given in Cartesian coordinates in the frame in\n  /// which the Strahlkorper is defined.\n  /// This is inefficient if done at multiple points many times.\n  bool point_is_contained(const std::array<double, 3>& x) const;\n\n  SPECTRE_ALWAYS_INLINE const ylm::Spherepack& ylm_spherepack() const {\n    return ylm_;\n  }\n\n private:\n  size_t l_max_{2}, m_max_{2};\n  ylm::Spherepack ylm_{2, 2};\n  std::array<double, 3> center_{{0.0, 0.0, 0.0}};\n  DataVector strahlkorper_coefs_ = DataVector(ylm_.spectral_size(), 0.0);\n};\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup SurfacesGroup\n/// The input file tag for a Strahlkorper.\ntemplate <typename Frame>\nstruct Strahlkorper {\n  using type = ylm::Strahlkorper<Frame>;\n  static constexpr Options::String help{\"A star-shaped surface\"};\n};\n}  // namespace OptionTags\n\ntemplate <typename Frame>\nbool operator==(const Strahlkorper<Frame>& lhs,\n                const Strahlkorper<Frame>& rhs) {\n  return lhs.l_max() == rhs.l_max() and lhs.m_max() == rhs.m_max() and\n         lhs.expansion_center() == rhs.expansion_center() and\n         lhs.coefficients() == rhs.coefficients();\n}\n\ntemplate <typename Frame>\nbool operator!=(const Strahlkorper<Frame>& lhs,\n                const Strahlkorper<Frame>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n\n#include <algorithm>\n#include <deque>\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NonUniform1D.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearLeastSquares.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace ylm {\ntemplate <typename Fr>\nScalar<DataVector> radius(const Strahlkorper<Fr>& strahlkorper) {\n  Scalar<DataVector> result{\n      DataVector{strahlkorper.ylm_spherepack().physical_size()}};\n  radius(make_not_null(&result), strahlkorper);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid radius(const gsl::not_null<Scalar<DataVector>*> result,\n            const Strahlkorper<Fr>& strahlkorper) {\n  get(*result) =\n      strahlkorper.ylm_spherepack().spec_to_phys(strahlkorper.coefficients());\n}\n\ntemplate <typename Fr>\ntnsr::i<DataVector, 2, ::Frame::Spherical<Fr>> theta_phi(\n    const Strahlkorper<Fr>& strahlkorper) {\n  tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>> result{\n      DataVector{strahlkorper.ylm_spherepack().physical_size()}};\n  theta_phi(make_not_null(&result), strahlkorper);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid theta_phi(\n    const gsl::not_null<tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>*>\n        theta_phi,\n    const Strahlkorper<Fr>& strahlkorper) {\n  // If ylm_spherepack ever gets a not-null version of theta_phi_points,\n  // then that can be used here to avoid an allocation.\n  auto temp = strahlkorper.ylm_spherepack().theta_phi_points();\n  get<0>(*theta_phi) = temp[0];\n  get<1>(*theta_phi) = temp[1];\n}\n\ntemplate <typename Fr>\ntnsr::i<DataVector, 3, Fr> rhat(\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  tnsr::i<DataVector, 3, Fr> result{DataVector{theta_phi[0].size()}};\n  rhat(make_not_null(&result), theta_phi);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid rhat(const gsl::not_null<tnsr::i<DataVector, 3, Fr>*> r_hat,\n          const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  const auto& theta = get<0>(theta_phi);\n  const auto& phi = get<1>(theta_phi);\n\n  // Use one component of rhat for temporary storage, to avoid allocations.\n  get<1>(*r_hat) = sin(theta);\n\n  get<0>(*r_hat) = get<1>(*r_hat) * cos(phi);\n  get<1>(*r_hat) *= sin(phi);\n  get<2>(*r_hat) = cos(theta);\n}\n\ntemplate <typename Fr>\nylm::Tags::aliases::Jacobian<Fr> jacobian(\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  ylm::Tags::aliases::Jacobian<Fr> result{DataVector{theta_phi[0].size()}};\n  jacobian(make_not_null(&result), theta_phi);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid jacobian(const gsl::not_null<ylm::Tags::aliases::Jacobian<Fr>*> jac,\n              const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  const auto& theta = get<0>(theta_phi);\n  const auto& phi = get<1>(theta_phi);\n  set_number_of_grid_points(jac, theta);\n\n  // Use 1,0 component of jac for temporary storage, to avoid allocations.\n  get<1, 0>(*jac) = cos(theta);\n\n  // Fill in components in carefully chosen order so as to avoid allocations.\n  get<0, 1>(*jac) = -sin(phi);                          // 1/(R sin(th)) dx/dph\n  get<1, 1>(*jac) = cos(phi);                           // 1/(R sin(th)) dy/dph\n  get<2, 1>(*jac) = 0.0;                                // 1/(R sin(th)) dz/dph\n  get<0, 0>(*jac) = get<1, 0>(*jac) * get<1, 1>(*jac);  // 1/R dx/dth\n  get<1, 0>(*jac) *= -get<0, 1>(*jac);                  // 1/R dy/dth\n  get<2, 0>(*jac) = -sin(theta);                        // 1/R dz/dth\n}\n\ntemplate <typename Fr>\nylm::Tags::aliases::InvJacobian<Fr> inv_jacobian(\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  ylm::Tags::aliases::InvJacobian<Fr> result{DataVector{theta_phi[0].size()}};\n  inv_jacobian(make_not_null(&result), theta_phi);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid inv_jacobian(\n    const gsl::not_null<ylm::Tags::aliases::InvJacobian<Fr>*> inv_jac,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  const auto& theta = get<0>(theta_phi);\n  const auto& phi = get<1>(theta_phi);\n  set_number_of_grid_points(inv_jac, theta);\n\n  // Use 0,1 component of inv_jac for temporary storage, to avoid allocations.\n  get<0, 1>(*inv_jac) = cos(theta);\n\n  // Fill in components in carefully chosen order so as to avoid allocations.\n  get<1, 0>(*inv_jac) = -sin(phi);  // R sin(th) dph/dx\n  get<1, 1>(*inv_jac) = cos(phi);   // R sin(th) dph/dy\n  get<1, 2>(*inv_jac) = 0.0;        // R sin(th) dph/dz\n  get<0, 0>(*inv_jac) = get<0, 1>(*inv_jac) * get<1, 1>(*inv_jac);  // R dth/dx\n  get<0, 1>(*inv_jac) *= -get<1, 0>(*inv_jac);                      // R dth/dy\n  get<0, 2>(*inv_jac) = -sin(theta);                                // R dth/dz\n}\n\ntemplate <typename Fr>\nylm::Tags::aliases::InvHessian<Fr> inv_hessian(\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  ylm::Tags::aliases::InvHessian<Fr> result{DataVector{theta_phi[0].size()}};\n  inv_hessian(make_not_null(&result), theta_phi);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid inv_hessian(\n    const gsl::not_null<ylm::Tags::aliases::InvHessian<Fr>*> inv_hess,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  const auto& theta = get<0>(theta_phi);\n  const auto& phi = get<1>(theta_phi);\n  set_number_of_grid_points(inv_hess, theta);\n\n  // Use some components of inv_hess for temporary storage, to avoid\n  // allocations.\n  get<1, 2, 2>(*inv_hess) = cos(theta);\n  get<1, 1, 2>(*inv_hess) = sin(theta);\n  get<1, 0, 2>(*inv_hess) = cos(phi);\n  get<0, 2, 1>(*inv_hess) = sin(phi);\n  get<1, 1, 1>(*inv_hess) = square(get<1, 1, 2>(*inv_hess));  // sin^2 theta\n  get<0, 2, 0>(*inv_hess) = 1.0 / get<1, 1, 2>(*inv_hess);    // csc theta\n  get<1, 2, 1>(*inv_hess) = square(get<0, 2, 1>(*inv_hess));  // sin^2 phi\n  get<0, 2, 2>(*inv_hess) = square(get<1, 0, 2>(*inv_hess));  // cos^2 phi\n\n  // Fill in components of inv_hess in carefully chosen order,\n  // to avoid allocations.\n\n  // R^2 d^2 th/(dydz)\n  get<0, 1, 2>(*inv_hess) =\n      (2.0 * get<1, 1, 1>(*inv_hess) - 1.0) * get<0, 2, 1>(*inv_hess);\n  // R^2 d/dz (sin(th) dph/dx)\n  get<1, 2, 0>(*inv_hess) = get<1, 2, 2>(*inv_hess) * get<0, 2, 1>(*inv_hess);\n\n  // cos(phi) sin(phi) [overwrites sin_phi]\n  get<0, 2, 1>(*inv_hess) = get<1, 0, 2>(*inv_hess) * get<0, 2, 1>(*inv_hess);\n\n  // Fill in components of inv_hess in carefully chosen order,\n  // to avoid allocations.\n\n  // R^2 d/dx (sin(th) dph/dx)\n  get<1, 0, 0>(*inv_hess) = get<0, 2, 1>(*inv_hess) *\n                            (1.0 + get<1, 1, 1>(*inv_hess)) *\n                            get<0, 2, 0>(*inv_hess);\n  // R^2 d/dx (sin(th) dph/dy)\n  get<1, 0, 1>(*inv_hess) =\n      (get<1, 2, 1>(*inv_hess) -\n       get<1, 1, 1>(*inv_hess) * get<0, 2, 2>(*inv_hess)) *\n      get<0, 2, 0>(*inv_hess);\n  // R^2 d/dy (sin(th) dph/dx)\n  get<1, 1, 0>(*inv_hess) = (get<1, 1, 1>(*inv_hess) * get<1, 2, 1>(*inv_hess) -\n                             get<0, 2, 2>(*inv_hess)) *\n                            get<0, 2, 0>(*inv_hess);\n\n  // More temps: now don't need csc theta anymore\n  get<0, 2, 0>(*inv_hess) *= get<1, 2, 2>(*inv_hess);  // cot(theta)\n  // 1 + 2 sin^2(theta)\n  get<0, 0, 1>(*inv_hess) = 1.0 + 2.0 * get<1, 1, 1>(*inv_hess);\n\n  // Fill in more inv_hess components in careful order, since some\n  // of those components still contain temporaries.\n\n  // R^2 d^2 th/(dy^2)\n  get<0, 1, 1>(*inv_hess) =\n      get<0, 2, 0>(*inv_hess) *\n      (1.0 - get<1, 2, 1>(*inv_hess) * get<0, 0, 1>(*inv_hess));\n  // R^2 d^2 th/(dx^2)\n  get<0, 0, 0>(*inv_hess) =\n      get<0, 2, 0>(*inv_hess) *\n      (1.0 - get<0, 2, 2>(*inv_hess) * get<0, 0, 1>(*inv_hess));\n  // R^2 d^2 th/(dxdy)\n  get<0, 0, 1>(*inv_hess) = -get<0, 2, 0>(*inv_hess) * get<0, 2, 1>(*inv_hess) *\n                            get<0, 0, 1>(*inv_hess);\n  // R^2 d^2 th/(dxdz)\n  get<0, 0, 2>(*inv_hess) =\n      (2.0 * get<1, 1, 1>(*inv_hess) - 1.0) * get<1, 0, 2>(*inv_hess);\n  // R^2 d/dz (sin(th) dph/dy)\n  get<1, 2, 1>(*inv_hess) = -get<1, 2, 2>(*inv_hess) * get<1, 0, 2>(*inv_hess);\n  // R^2 d^2 th/(dz^2)\n  get<0, 2, 2>(*inv_hess) =\n      2.0 * get<1, 2, 2>(*inv_hess) * get<1, 1, 2>(*inv_hess);\n\n  // R^2 d^2 th/(dydx)\n  get<0, 1, 0>(*inv_hess) = get<0, 0, 1>(*inv_hess);\n  // R^2 d^2 th/(dzdx)\n  get<0, 2, 0>(*inv_hess) = get<0, 0, 2>(*inv_hess);\n  // R^2 d^2 th/(dzdy)\n  get<0, 2, 1>(*inv_hess) = get<0, 1, 2>(*inv_hess);\n  // R^2 d/dx (sin(th) dph/dz)\n  get<1, 0, 2>(*inv_hess) = 0.0;\n  // R^2 d/dy (sin(th) dph/dy)\n  get<1, 1, 1>(*inv_hess) = -get<1, 0, 0>(*inv_hess);\n  // R^2 d/dy (sin(th) dph/dz)\n  get<1, 1, 2>(*inv_hess) = 0.0;\n  // R^2 d/dz (sin(th) dph/dz)\n  get<1, 2, 2>(*inv_hess) = 0.0;\n}\n\ntemplate <typename Fr>\ntnsr::I<DataVector, 3, Fr> cartesian_coords(\n    const Strahlkorper<Fr>& strahlkorper, const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Fr>& r_hat) {\n  tnsr::I<DataVector, 3, Fr> result{DataVector{get(radius).size()}};\n  cartesian_coords(make_not_null(&result), strahlkorper, radius, r_hat);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid cartesian_coords(const gsl::not_null<tnsr::I<DataVector, 3, Fr>*> coords,\n                      const Strahlkorper<Fr>& strahlkorper,\n                      const Scalar<DataVector>& radius,\n                      const tnsr::i<DataVector, 3, Fr>& r_hat) {\n  for (size_t d = 0; d < 3; ++d) {\n    coords->get(d) = gsl::at(strahlkorper.expansion_center(), d) +\n                     r_hat.get(d) * get(radius);\n  }\n}\n\ntemplate <typename Fr>\ntnsr::I<DataVector, 3, Fr> cartesian_coords(\n    const Strahlkorper<Fr>& strahlkorper) {\n  const auto radius = ylm::radius(strahlkorper);\n  const auto theta_phi = ylm::theta_phi(strahlkorper);\n  const auto r_hat = ylm::rhat(theta_phi);\n  return cartesian_coords(strahlkorper, radius, r_hat);\n}\n\ntemplate <typename Fr>\ntnsr::i<DataVector, 3, Fr> cartesian_derivs_of_scalar(\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const Scalar<DataVector>& radius_of_strahlkorper,\n    const ylm::Tags::aliases::InvJacobian<Fr>& inv_jac) {\n  tnsr::i<DataVector, 3, Fr> result{DataVector{get(scalar).size()}};\n  cartesian_derivs_of_scalar(make_not_null(&result), scalar, strahlkorper,\n                             radius_of_strahlkorper, inv_jac);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid cartesian_derivs_of_scalar(\n    const gsl::not_null<tnsr::i<DataVector, 3, Fr>*> dx_scalar,\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const Scalar<DataVector>& radius_of_strahlkorper,\n    const ylm::Tags::aliases::InvJacobian<Fr>& inv_jac) {\n  // If ylm_spherepack().gradient() ever gets a not_null function,\n  // that function can be used here.\n  const auto gradient = strahlkorper.ylm_spherepack().gradient(get(scalar));\n\n  // Use dx_scalar component as temp to store 1/r to avoid allocation.\n  get<2>(*dx_scalar) = 1.0 / get(radius_of_strahlkorper);\n\n  // Now fill in components.\n  get<0>(*dx_scalar) = (get<0, 0>(inv_jac) * get<0>(gradient) +\n                        get<1, 0>(inv_jac) * get<1>(gradient)) *\n                       get<2>(*dx_scalar);\n  get<1>(*dx_scalar) = (get<0, 1>(inv_jac) * get<0>(gradient) +\n                        get<1, 1>(inv_jac) * get<1>(gradient)) *\n                       get<2>(*dx_scalar);\n  get<2>(*dx_scalar) *= get<0, 2>(inv_jac) * get<0>(gradient);\n}\n\ntemplate <typename Fr>\ntnsr::ii<DataVector, 3, Fr> cartesian_second_derivs_of_scalar(\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const Scalar<DataVector>& radius_of_strahlkorper,\n    const ylm::Tags::aliases::InvJacobian<Fr>& inv_jac,\n    const ylm::Tags::aliases::InvHessian<Fr>& inv_hess) {\n  tnsr::ii<DataVector, 3, Fr> result{DataVector{get(scalar).size()}};\n  cartesian_second_derivs_of_scalar(make_not_null(&result), scalar,\n                                    strahlkorper, radius_of_strahlkorper,\n                                    inv_jac, inv_hess);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid cartesian_second_derivs_of_scalar(\n    const gsl::not_null<tnsr::ii<DataVector, 3, Fr>*> d2x_scalar,\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const Scalar<DataVector>& radius_of_strahlkorper,\n    const ylm::Tags::aliases::InvJacobian<Fr>& inv_jac,\n    const ylm::Tags::aliases::InvHessian<Fr>& inv_hess) {\n  set_number_of_grid_points(d2x_scalar, scalar);\n  for (auto& component : *d2x_scalar) {\n    component = 0.0;\n  }\n\n  // If ylm_spherepack().first_and_second_derivative() ever gets a not_null\n  // function, that function can be used here.\n  const auto derivs =\n      strahlkorper.ylm_spherepack().first_and_second_derivative(get(scalar));\n\n  for (size_t i = 0; i < 3; ++i) {\n    // Diagonal terms.  Divide by square(r) later.\n    for (size_t k = 0; k < 2; ++k) {  // Angular derivs are 2-dimensional\n      d2x_scalar->get(i, i) += derivs.first.get(k) * inv_hess.get(k, i, i);\n      for (size_t l = 0; l < 2; ++l) {  // Angular derivs are 2-dimensional\n        d2x_scalar->get(i, i) +=\n            derivs.second.get(l, k) * inv_jac.get(k, i) * inv_jac.get(l, i);\n      }\n    }\n    d2x_scalar->get(i, i) /= square(get(radius_of_strahlkorper));\n\n    // off_diagonal terms.  Symmetrize over i and j.\n    // Divide by 2*square(r) later.\n    for (size_t j = i + 1; j < 3; ++j) {\n      for (size_t k = 0; k < 2; ++k) {  // Angular derivs are 2-dimensional\n        d2x_scalar->get(i, j) += derivs.first.get(k) * (inv_hess.get(k, i, j) +\n                                                        inv_hess.get(k, j, i));\n        for (size_t l = 0; l < 2; ++l) {  // Angular derivs are 2-dimensional\n          d2x_scalar->get(i, j) +=\n              derivs.second.get(l, k) * (inv_jac.get(k, i) * inv_jac.get(l, j) +\n                                         inv_jac.get(k, j) * inv_jac.get(l, i));\n        }\n      }\n      d2x_scalar->get(i, j) /= 2.0 * square(get(radius_of_strahlkorper));\n    }\n  }\n}\n\ntemplate <typename Fr>\nScalar<DataVector> laplacian_of_scalar(\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  Scalar<DataVector> result{DataVector{get(scalar).size()}};\n  laplacian_of_scalar(make_not_null(&result), scalar, strahlkorper, theta_phi);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid laplacian_of_scalar(\n    const gsl::not_null<Scalar<DataVector>*> laplacian,\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi) {\n  get(*laplacian).destructive_resize(get(scalar).size());\n  // If ylm_spherepack().first_and_second_derivative() ever gets a not_null\n  // function, that function can be used here.\n  const auto derivs =\n      strahlkorper.ylm_spherepack().first_and_second_derivative(get(scalar));\n  get(*laplacian) = get<0, 0>(derivs.second) + get<1, 1>(derivs.second) +\n                    get<0>(derivs.first) / tan(get<0>(theta_phi));\n}\n\ntemplate <typename Fr>\nylm::Tags::aliases::Jacobian<Fr> tangents(\n    const Strahlkorper<Fr>& strahlkorper, const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Fr>& r_hat,\n    const ylm::Tags::aliases::Jacobian<Fr>& jac) {\n  ylm::Tags::aliases::Jacobian<Fr> result{DataVector{get(radius).size()}};\n  tangents(make_not_null(&result), strahlkorper, radius, r_hat, jac);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid tangents(const gsl::not_null<ylm::Tags::aliases::Jacobian<Fr>*> result,\n              const Strahlkorper<Fr>& strahlkorper,\n              const Scalar<DataVector>& radius,\n              const tnsr::i<DataVector, 3, Fr>& r_hat,\n              const ylm::Tags::aliases::Jacobian<Fr>& jac) {\n  const auto dr = strahlkorper.ylm_spherepack().gradient(get(radius));\n  for (size_t i = 0; i < 2; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      result->get(j, i) =\n          dr.get(i) * r_hat.get(j) + get(radius) * jac.get(j, i);\n    }\n  }\n}\n\ntemplate <typename Fr>\ntnsr::i<DataVector, 3, Fr> normal_one_form(\n    const tnsr::i<DataVector, 3, Fr>& dx_radius,\n    const tnsr::i<DataVector, 3, Fr>& r_hat) {\n  tnsr::i<DataVector, 3, Fr> result{DataVector{r_hat.begin()->size()}};\n  normal_one_form(make_not_null(&result), dx_radius, r_hat);\n  return result;\n}\n\ntemplate <typename Fr>\nvoid normal_one_form(const gsl::not_null<tnsr::i<DataVector, 3, Fr>*> one_form,\n                     const tnsr::i<DataVector, 3, Fr>& dx_radius,\n                     const tnsr::i<DataVector, 3, Fr>& r_hat) {\n  for (size_t d = 0; d < 3; ++d) {\n    one_form->get(d) = r_hat.get(d) - dx_radius.get(d);\n  }\n}\n\ntemplate <typename Fr>\nstd::vector<std::array<double, 4>> fit_ylm_coeffs(\n    const DataVector& times,\n    const std::vector<Strahlkorper<Fr>>& strahlkorpers) {\n  size_t num_observations = strahlkorpers.size();\n  size_t num_coefficients = strahlkorpers[0].coefficients().size();\n  std::vector<DataVector> coefficients;\n  for (size_t i = 0; i < num_coefficients; ++i) {\n    DataVector same_coeff_of_each_strahlkorper(num_observations);\n    for (size_t j = 0; j < num_observations; ++j) {\n      // Contains the same coefficient of each strahlkorper in the time series.\n      same_coeff_of_each_strahlkorper[j] = strahlkorpers[j].coefficients()[i];\n      if (i == 0 and j > 0) {\n        ASSERT(strahlkorpers[j - 1].l_max() == strahlkorpers[j].l_max() and\n                   strahlkorpers[j - 1].m_max() == strahlkorpers[j].m_max(),\n               \"Each Strahlkorper must have the same l_max and m_max.\");\n      }\n    }\n    coefficients.push_back(std::move(same_coeff_of_each_strahlkorper));\n  }\n  return intrp::linear_least_squares<3>(times, coefficients);\n}\n\nnamespace {\ntemplate <size_t NumTimes>\nstatic DataVector compute_coefs(\n    const std::deque<double>& times,\n    const std::deque<const DataVector*>& coefficients) {\n  const auto weights = fd::non_uniform_1d_weights<NumTimes>(times);\n\n  DataVector new_coefficients{coefficients.front()->size(), 0.0};\n\n  for (size_t i = 0; i < NumTimes; i++) {\n    new_coefficients += *coefficients[i] * gsl::at(gsl::at(weights, 1), i);\n  }\n\n  return new_coefficients;\n}\n}  // namespace\n\ntemplate <typename Frame>\nvoid time_deriv_of_strahlkorper(\n    gsl::not_null<Strahlkorper<Frame>*> time_deriv,\n    const std::deque<std::pair<double, Strahlkorper<Frame>>>&\n        previous_strahlkorpers) {\n  std::deque<double> times{};\n  std::deque<const DataVector*> coefficients{};\n  std::deque<DataVector> prolonged_or_restricted_coefficients{};\n\n  // Can't take time deriv of 1 strahlkorper, so just zero the time derivative's\n  // coefficients\n  if (previous_strahlkorpers.size() == 1) {\n    time_deriv->coefficients() = 0.0;\n    return;\n  }\n\n  // Find the min and max l_max of each previous Strahlkorper, along with the\n  // index of max(l_max). Getting both min(l_max) and max(l_max) enables\n  // skipping the prolong/restrict call when min(l_max) == max(l_max), and\n  // getting the index of max(l_max) enables skipping the prolong/restrict\n  // call on whichever previous Strahlkorper has the highest l_max.\n  const auto max_l_it = std::max_element(\n      previous_strahlkorpers.begin(), previous_strahlkorpers.end(),\n      [](const auto& a, const auto& b) {\n        return a.second.l_max() < b.second.l_max();\n      });\n  const size_t max_prev_l = max_l_it->second.l_max();\n\n  // OK to static_cast to size_t from long here because\n  // max_l_it is inside the std::deque container, so distance\n  // from previous_strahlkorpers.begin() to max_l_it must be nonnegative.\n  const auto max_prev_l_index = static_cast<size_t>(\n      std::distance(previous_strahlkorpers.begin(), max_l_it));\n\n  // If needed, prolong the coefficients of the previous\n  // strahlkorpers to the highest resolution of the previous.\n  // Note: only need to restrict if min(l_max) and max(l_max) differ, and even\n  // then, no need to call prolong_or_restrict() on whichever previous\n  // Strahlkorper has the highest l_max.\n  for (size_t i = 0; i < previous_strahlkorpers.size(); ++i) {\n    ASSERT(previous_strahlkorpers[i].second.l_max() <= max_prev_l,\n           \"Calculated incorrect max(l_max) = \"\n               << max_prev_l << \" but got a surface with l_max =\"\n               << previous_strahlkorpers[i].second.l_max());\n    times.emplace_back(previous_strahlkorpers[i].first);\n    if (previous_strahlkorpers[i].second.l_max() != max_prev_l) {\n      prolonged_or_restricted_coefficients.emplace_back(\n          previous_strahlkorpers[i].second.ylm_spherepack().prolong_or_restrict(\n              previous_strahlkorpers[i].second.coefficients(),\n              previous_strahlkorpers[max_prev_l_index]\n                  .second.ylm_spherepack()));\n      coefficients.emplace_back(&(prolonged_or_restricted_coefficients.back()));\n    } else {\n      coefficients.emplace_back(\n          &(previous_strahlkorpers[i].second.coefficients()));\n    }\n  }\n\n  DataVector new_coefficients{};\n\n  // Switch needed to convert times size into template parameter\n  switch (previous_strahlkorpers.size()) {\n    case 2:\n      new_coefficients = compute_coefs<2>(times, coefficients);\n      break;\n    case 3:\n      new_coefficients = compute_coefs<3>(times, coefficients);\n      break;\n    case 4:\n      new_coefficients = compute_coefs<4>(times, coefficients);\n      break;\n    default:\n      ERROR(\"Unsupported size for number of previous Strahlkorpers \"\n            << previous_strahlkorpers.size());\n  }\n\n  // If needed, prolong or restrict the new coefficients to match the\n  // resolution of the Strahlkorper whose time derivative is being taken here\n  if (new_coefficients.size() != time_deriv->coefficients().size()) {\n    new_coefficients = previous_strahlkorpers[max_prev_l_index]\n                           .second.ylm_spherepack()\n                           .prolong_or_restrict(new_coefficients,\n                                                time_deriv->ylm_spherepack());\n  }\n  time_deriv->coefficients() = std::move(new_coefficients);\n}\n\ntemplate <typename Frame>\nvoid power_monitor(gsl::not_null<DataVector*> result,\n                   const Strahlkorper<Frame>& strahlkorper) {\n  result->destructive_resize(strahlkorper.l_max() + 1);\n  *result = 0.0;\n  for (SpherepackIterator it(strahlkorper.l_max(), strahlkorper.m_max()); it;\n       ++it) {\n    (*result)[it.l()] += square(strahlkorper.coefficients()[it()]);\n  }\n  for (size_t l = 0; l <= strahlkorper.l_max(); ++l) {\n    const double normalization =\n        2.0 * static_cast<double>(std::min(l, strahlkorper.m_max())) + 1.0;\n    (*result)[l] = sqrt((*result)[l] / normalization);\n  }\n}\n\ntemplate <typename Frame>\nDataVector power_monitor(const Strahlkorper<Frame>& strahlkorper) {\n  DataVector result{strahlkorper.l_max() + 1, 0.0};\n  power_monitor(make_not_null(&result), strahlkorper);\n  return result;\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template Scalar<DataVector> ylm::radius(                                    \\\n      const Strahlkorper<FRAME(data)>& strahlkorper);                         \\\n  template void ylm::radius(const gsl::not_null<Scalar<DataVector>*> result,  \\\n                            const Strahlkorper<FRAME(data)>& strahlkorper);   \\\n  template tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>            \\\n  ylm::theta_phi(const Strahlkorper<FRAME(data)>& strahlkorper);              \\\n  template void ylm::theta_phi(                                               \\\n      const gsl::not_null<                                                    \\\n          tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>*>           \\\n          theta_phi,                                                          \\\n      const Strahlkorper<FRAME(data)>& strahlkorper);                         \\\n  template tnsr::i<DataVector, 3, FRAME(data)> ylm::rhat(                     \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template void ylm::rhat(                                                    \\\n      const gsl::not_null<tnsr::i<DataVector, 3, FRAME(data)>*> r_hat,        \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template ylm::Tags::aliases::Jacobian<FRAME(data)> ylm::jacobian(           \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template void ylm::jacobian(                                                \\\n      const gsl::not_null<ylm::Tags::aliases::Jacobian<FRAME(data)>*> jac,    \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template ylm::Tags::aliases::InvJacobian<FRAME(data)> ylm::inv_jacobian(    \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template void ylm::inv_jacobian(                                            \\\n      const gsl::not_null<ylm::Tags::aliases::InvJacobian<FRAME(data)>*>      \\\n          inv_jac,                                                            \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template ylm::Tags::aliases::InvHessian<FRAME(data)> ylm::inv_hessian(      \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template void ylm::inv_hessian(                                             \\\n      const gsl::not_null<ylm::Tags::aliases::InvHessian<FRAME(data)>*>       \\\n          inv_hess,                                                           \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template tnsr::I<DataVector, 3, FRAME(data)> ylm::cartesian_coords(         \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const Scalar<DataVector>& radius,                                       \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat);                      \\\n  template void ylm::cartesian_coords(                                        \\\n      const gsl::not_null<tnsr::I<DataVector, 3, FRAME(data)>*> coords,       \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const Scalar<DataVector>& radius,                                       \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat);                      \\\n  template tnsr::I<DataVector, 3, FRAME(data)> ylm::cartesian_coords(         \\\n      const Strahlkorper<FRAME(data)>& strahlkorper);                         \\\n  template tnsr::i<DataVector, 3, FRAME(data)>                                \\\n  ylm::cartesian_derivs_of_scalar(                                            \\\n      const Scalar<DataVector>& scalar,                                       \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const Scalar<DataVector>& radius_of_strahlkorper,                       \\\n      const ylm::Tags::aliases::InvJacobian<FRAME(data)>& inv_jac);           \\\n  template void ylm::cartesian_derivs_of_scalar(                              \\\n      const gsl::not_null<tnsr::i<DataVector, 3, FRAME(data)>*> dx_scalar,    \\\n      const Scalar<DataVector>& scalar,                                       \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const Scalar<DataVector>& radius_of_strahlkorper,                       \\\n      const ylm::Tags::aliases::InvJacobian<FRAME(data)>& inv_jac);           \\\n  template tnsr::ii<DataVector, 3, FRAME(data)>                               \\\n  ylm::cartesian_second_derivs_of_scalar(                                     \\\n      const Scalar<DataVector>& scalar,                                       \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const Scalar<DataVector>& radius_of_strahlkorper,                       \\\n      const ylm::Tags::aliases::InvJacobian<FRAME(data)>& inv_jac,            \\\n      const ylm::Tags::aliases::InvHessian<FRAME(data)>& inv_hess);           \\\n  template void ylm::cartesian_second_derivs_of_scalar(                       \\\n      const gsl::not_null<tnsr::ii<DataVector, 3, FRAME(data)>*> result,      \\\n      const Scalar<DataVector>& scalar,                                       \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const Scalar<DataVector>& radius_of_strahlkorper,                       \\\n      const ylm::Tags::aliases::InvJacobian<FRAME(data)>& inv_jac,            \\\n      const ylm::Tags::aliases::InvHessian<FRAME(data)>& inv_hess);           \\\n  template Scalar<DataVector> ylm::laplacian_of_scalar(                       \\\n      const Scalar<DataVector>& scalar,                                       \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template void ylm::laplacian_of_scalar(                                     \\\n      const gsl::not_null<Scalar<DataVector>*> result,                        \\\n      const Scalar<DataVector>& scalar,                                       \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<FRAME(data)>>&          \\\n          theta_phi);                                                         \\\n  template ylm::Tags::aliases::Jacobian<FRAME(data)> ylm::tangents(           \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const Scalar<DataVector>& radius,                                       \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat,                       \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& jac);                  \\\n  template void ylm::tangents(                                                \\\n      const gsl::not_null<ylm::Tags::aliases::Jacobian<FRAME(data)>*> result, \\\n      const Strahlkorper<FRAME(data)>& strahlkorper,                          \\\n      const Scalar<DataVector>& radius,                                       \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat,                       \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& jac);                  \\\n  template tnsr::i<DataVector, 3, FRAME(data)> ylm::normal_one_form(          \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& dx_radius,                   \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat);                      \\\n  template void ylm::normal_one_form(                                         \\\n      const gsl::not_null<tnsr::i<DataVector, 3, FRAME(data)>*> one_form,     \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& dx_radius,                   \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat);                      \\\n  template std::vector<std::array<double, 4>> ylm::fit_ylm_coeffs(            \\\n      const DataVector& times,                                                \\\n      const std::vector<Strahlkorper<FRAME(data)>>& strahlkorpers);           \\\n  template void ylm::time_deriv_of_strahlkorper(                              \\\n      const gsl::not_null<Strahlkorper<FRAME(data)>*>,                        \\\n      const std::deque<std::pair<double, Strahlkorper<FRAME(data)>>>&);       \\\n  template void ylm::power_monitor(                                           \\\n      gsl::not_null<DataVector*> result,                                      \\\n      const Strahlkorper<FRAME(data)>& strahlkorper);                         \\\n  template DataVector ylm::power_monitor(                                     \\\n      const Strahlkorper<FRAME(data)>& strahlkorper);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Distorted, Frame::Grid, Frame::Inertial))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <deque>\n#include <utility>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TagsTypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\ntemplate <typename Fr>\nclass Strahlkorper;\n}  // namespace ylm\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// \\ingroup SurfacesGroup\n/// Contains functions that depend on a Strahlkorper but not on a metric.\nnamespace ylm {\n/// @{\n/*!\n * \\f$(\\theta,\\phi)\\f$ on the Strahlkorper surface.\n * Doesn't depend on the shape of the surface.\n *\n * We need to choose upper vs lower indices for theta_phi; it doesn't\n * matter because these are coordinates and not geometric objects, so\n * we choose lower indices arbitrarily.\n */\ntemplate <typename Fr>\ntnsr::i<DataVector, 2, ::Frame::Spherical<Fr>> theta_phi(\n    const Strahlkorper<Fr>& strahlkorper);\n\ntemplate <typename Fr>\nvoid theta_phi(\n    const gsl::not_null<tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>*>\n        theta_phi,\n    const Strahlkorper<Fr>& strahlkorper);\n/// @}\n\n/// @{\n/*!\n * `r_hat(i)` is \\f$\\hat{r}_i = x_i/\\sqrt{x^2+y^2+z^2}\\f$ on the\n * Strahlkorper surface.  Doesn't depend on the shape of the surface.\n *\n * We need to choose upper vs lower indices for rhat; it doesn't\n * matter because rhat is a quantity defined with a Euclidean metric,\n * so we choose the lower index arbitrarily.\n */\ntemplate <typename Fr>\ntnsr::i<DataVector, 3, Fr> rhat(\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n\ntemplate <typename Fr>\nvoid rhat(const gsl::not_null<tnsr::i<DataVector, 3, Fr>*> r_hat,\n          const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n/// @}\n\n/// @{\n/*!\n * `Jacobian(i,0)` is \\f$\\frac{1}{r}\\partial x^i/\\partial\\theta\\f$,\n * and `Jacobian(i,1)`\n * is \\f$\\frac{1}{r\\sin\\theta}\\partial x^i/\\partial\\phi\\f$.\n * Here \\f$r\\f$ means \\f$\\sqrt{x^2+y^2+z^2}\\f$.\n * `Jacobian` doesn't depend on the shape of the surface.\n */\ntemplate <typename Fr>\nylm::Tags::aliases::Jacobian<Fr> jacobian(\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n\ntemplate <typename Fr>\nvoid jacobian(const gsl::not_null<ylm::Tags::aliases::Jacobian<Fr>*> jac,\n              const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n/// @}\n\n/// @{\n/*!\n * `InvJacobian(0,i)` is \\f$r\\partial\\theta/\\partial x^i\\f$,\n * and `InvJacobian(1,i)` is \\f$r\\sin\\theta\\partial\\phi/\\partial x^i\\f$.\n * Here \\f$r\\f$ means \\f$\\sqrt{x^2+y^2+z^2}\\f$.\n * `InvJacobian` doesn't depend on the shape of the surface.\n */\ntemplate <typename Fr>\nylm::Tags::aliases::InvJacobian<Fr> inv_jacobian(\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n\ntemplate <typename Fr>\nvoid inv_jacobian(\n    const gsl::not_null<ylm::Tags::aliases::InvJacobian<Fr>*> inv_jac,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n/// @}\n\n/// @{\n/*!\n * `InvHessian(k,i,j)` is \\f$r\\partial (J^{-1}){}^k_j/\\partial x^i\\f$,\n * where \\f$(J^{-1}){}^k_j\\f$ is the inverse Jacobian.\n * Here \\f$r\\f$ means \\f$\\sqrt{x^2+y^2+z^2}\\f$.\n * `InvHessian` is not symmetric because the Jacobians are Pfaffian.\n * `InvHessian` doesn't depend on the shape of the surface.\n */\ntemplate <typename Fr>\nylm::Tags::aliases::InvHessian<Fr> inv_hessian(\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n\ntemplate <typename Fr>\nvoid inv_hessian(\n    const gsl::not_null<ylm::Tags::aliases::InvHessian<Fr>*> inv_hess,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n/// @}\n\n/// @{\n/*!\n * (Euclidean) distance \\f$r_{\\rm surf}(\\theta,\\phi)\\f$ from the\n * expansion center to each point of the Strahlkorper surface.\n */\ntemplate <typename Fr>\nScalar<DataVector> radius(const Strahlkorper<Fr>& strahlkorper);\n\ntemplate <typename Fr>\nvoid radius(const gsl::not_null<Scalar<DataVector>*> result,\n            const Strahlkorper<Fr>& strahlkorper);\n/// @}\n\n/// @{\n/*!\n * `cartesian_coords(i)` is \\f$x_{\\rm surf}^i\\f$, the vector of \\f$(x,y,z)\\f$\n * coordinates of each point on the Strahlkorper surface.\n *\n * \\param strahlkorper The Strahlkorper surface.\n * \\param radius The radius as a function of angle, as returned by\n * `ylm::radius`.\n * \\param r_hat The Euclidean radial unit vector as returned by\n * `ylm::rhat`.\n */\ntemplate <typename Fr>\ntnsr::I<DataVector, 3, Fr> cartesian_coords(\n    const Strahlkorper<Fr>& strahlkorper, const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Fr>& r_hat);\n\n/*!\n * \\param coords The returned Cartesian coordinates.\n * \\param strahlkorper The Strahlkorper surface.\n * \\param radius The radius as a function of angle, as returned by\n * `ylm::radius`.\n * \\param r_hat The Euclidean radial unit vector as returned by\n * `ylm::rhat`.\n */\ntemplate <typename Fr>\nvoid cartesian_coords(const gsl::not_null<tnsr::I<DataVector, 3, Fr>*> coords,\n                      const Strahlkorper<Fr>& strahlkorper,\n                      const Scalar<DataVector>& radius,\n                      const tnsr::i<DataVector, 3, Fr>& r_hat);\n\n/*!\n * This overload computes `radius`, `theta_phi`, and `r_hat` internally.\n * Use the other overloads if you already have these quantities.\n *\n * \\param strahlkorper The Strahlkorper surface.\n */\ntemplate <typename Fr>\ntnsr::I<DataVector, 3, Fr> cartesian_coords(\n    const Strahlkorper<Fr>& strahlkorper);\n/// @}\n\n/// @{\n/*!\n * `dx_scalar(i)` is \\f$\\partial f/\\partial x^i\\f$ evaluated on the\n * surface.  Here \\f$f=f(r,\\theta,\\phi)=f(\\theta,\\phi)\\f$ is some\n * scalar function independent of the radial coordinate. \\f$f\\f$ is\n * considered a function of Cartesian coordinates\n * \\f$f=f(\\theta(x,y,z),\\phi(x,y,z))\\f$ for this operation.\n *\n * \\param scalar The scalar to be differentiated.\n * \\param strahlkorper The Strahlkorper surface.\n * \\param radius_of_strahlkorper The radius of the Strahlkorper at each\n * point, as returned by `ylm::radius`.\n * \\param inv_jac The inverse Jacobian as returned by\n * `ylm::inv_jacobian`\n */\ntemplate <typename Fr>\ntnsr::i<DataVector, 3, Fr> cartesian_derivs_of_scalar(\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const Scalar<DataVector>& radius_of_strahlkorper,\n    const ylm::Tags::aliases::InvJacobian<Fr>& inv_jac);\n\n/*!\n * \\param dx_scalar The returned derivatives of the scalar.\n * \\param scalar The scalar to be differentiated.\n * \\param strahlkorper The Strahlkorper surface.\n * \\param radius_of_strahlkorper The radius of the Strahlkorper at each\n * point, as returned by `ylm::radius`.\n * \\param inv_jac The inverse Jacobian as returned by\n * `ylm::inv_jacobian`\n */\ntemplate <typename Fr>\nvoid cartesian_derivs_of_scalar(\n    const gsl::not_null<tnsr::i<DataVector, 3, Fr>*> dx_scalar,\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const Scalar<DataVector>& radius_of_strahlkorper,\n    const ylm::Tags::aliases::InvJacobian<Fr>& inv_jac);\n/// @}\n\n/// @{\n/*!\n * `d2x_scalar(i,j)` is \\f$\\partial^2 f/\\partial x^i\\partial x^j\\f$\n * evaluated on the surface. Here\n * \\f$f=f(r,\\theta,\\phi)=f(\\theta,\\phi)\\f$ is some scalar function\n * independent of the radial coordinate. \\f$f\\f$ is considered a\n * function of Cartesian coordinates\n * \\f$f=f(\\theta(x,y,z),\\phi(x,y,z))\\f$ for this operation.\n *\n * \\param scalar The scalar to be differentiated.\n * \\param strahlkorper The Strahlkorper surface.\n * \\param radius_of_strahlkorper The radius of the Strahlkorper at each\n * point, as returned by `ylm::radius`.\n * \\param inv_jac The inverse Jacobian as returned by\n * `ylm::inv_jacobian`\n * \\param inv_hess The inverse Hessian as returned by\n * `ylm::inv_hessian.\n */\ntemplate <typename Fr>\ntnsr::ii<DataVector, 3, Fr> cartesian_second_derivs_of_scalar(\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const Scalar<DataVector>& radius_of_strahlkorper,\n    const ylm::Tags::aliases::InvJacobian<Fr>& inv_jac,\n    const ylm::Tags::aliases::InvHessian<Fr>& inv_hess);\n\n/*!\n * \\param d2x_scalar The returned 2nd derivatives of the scalar.\n * \\param scalar The scalar to be differentiated.\n * \\param strahlkorper The Strahlkorper surface.\n * \\param radius_of_strahlkorper The radius of the Strahlkorper at each\n * point, as returned by `ylm::radius`.\n * \\param inv_jac The inverse Jacobian as returned by\n * `ylm::inv_jacobian`\n * \\param inv_hess The inverse Hessian as returned by\n * `ylm::inv_hessian.\n */\ntemplate <typename Fr>\nvoid cartesian_second_derivs_of_scalar(\n    const gsl::not_null<tnsr::ii<DataVector, 3, Fr>*> d2x_scalar,\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const Scalar<DataVector>& radius_of_strahlkorper,\n    const ylm::Tags::aliases::InvJacobian<Fr>& inv_jac,\n    const ylm::Tags::aliases::InvHessian<Fr>& inv_hess);\n/// @}\n\n/// @{\n/*!\n * \\f$\\nabla^2 f\\f$, the flat Laplacian of a scalar \\f$f\\f$ on the surface.\n * This is \\f$\\eta^{ij}\\partial^2 f/\\partial x^i\\partial x^j\\f$,\n * where \\f$f=f(r,\\theta,\\phi)=f(\\theta,\\phi)\\f$ is some scalar function\n * independent of the radial coordinate. \\f$f\\f$ is considered a\n * function of Cartesian coordinates\n * \\f$f=f(\\theta(x,y,z),\\phi(x,y,z))\\f$ for this operation.\n *\n */\ntemplate <typename Fr>\nScalar<DataVector> laplacian_of_scalar(\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n\ntemplate <typename Fr>\nvoid laplacian_of_scalar(\n    const gsl::not_null<Scalar<DataVector>*> laplacian,\n    const Scalar<DataVector>& scalar, const Strahlkorper<Fr>& strahlkorper,\n    const tnsr::i<DataVector, 2, ::Frame::Spherical<Fr>>& theta_phi);\n/// @}\n\n/// @{\n/*!\n * `tangents(i,j)` is \\f$\\partial x_{\\rm surf}^i/\\partial q^j\\f$,\n * where \\f$x_{\\rm surf}^i\\f$ are the Cartesian coordinates of the\n * surface (i.e. `cartesian_coords`) and are considered functions of\n * \\f$(\\theta,\\phi)\\f$.\n *\n * \\f$\\partial/\\partial q^0\\f$ means\n * \\f$\\partial/\\partial\\theta\\f$; and \\f$\\partial/\\partial q^1\\f$\n * means \\f$\\csc\\theta\\,\\,\\partial/\\partial\\phi\\f$.  Note that the\n * vectors `tangents(i,0)` and `tangents(i,1)` are orthogonal to the\n * `normal_one_form` \\f$s_i\\f$, i.e.\n * \\f$s_i \\partial x_{\\rm surf}^i/\\partial q^j = 0\\f$; this statement\n * is independent of a metric.  Also, `tangents(i,0)` and\n * `tangents(i,1)` are not necessarily orthogonal to each other,\n * since orthogonality between 2 vectors (as opposed to a vector and\n * a one-form) is metric-dependent.\n *\n * \\param strahlkorper The Strahlkorper surface.\n * \\param radius The radius of the Strahlkorper at each\n * point, as returned by `ylm::radius`.\n * \\param r_hat The radial unit vector as returned by\n * `ylm::rhat`.\n * \\param jac The jacobian as returned by `ylm::jacobian`.\n */\ntemplate <typename Fr>\nylm::Tags::aliases::Jacobian<Fr> tangents(\n    const Strahlkorper<Fr>& strahlkorper, const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Fr>& r_hat,\n    const ylm::Tags::aliases::Jacobian<Fr>& jac);\n\n/*!\n * \\param result The computed tangent vectors.\n * \\param strahlkorper The Strahlkorper surface.\n * \\param radius The radius of the Strahlkorper at each\n * point, as returned by `ylm::radius`.\n * \\param r_hat The radial unit vector as returned by\n * `ylm::rhat`.\n * \\param jac The jacobian as returned by `ylm::jacobian`.\n */\ntemplate <typename Fr>\nvoid tangents(const gsl::not_null<ylm::Tags::aliases::Jacobian<Fr>*> result,\n              const Strahlkorper<Fr>& strahlkorper,\n              const Scalar<DataVector>& radius,\n              const tnsr::i<DataVector, 3, Fr>& r_hat,\n              const ylm::Tags::aliases::Jacobian<Fr>& jac);\n/// @}\n\n/// @{\n/*!\n * `normal_one_form(i)` is \\f$s_i\\f$, the (unnormalized) normal one-form\n * to the surface, expressed in Cartesian components.\n * This is computed by \\f$x_i/r-\\partial r_{\\rm surf}/\\partial x^i\\f$,\n * where \\f$x_i/r\\f$ is `Rhat` and\n * \\f$\\partial r_{\\rm surf}/\\partial x^i\\f$ is `DxRadius`.\n * See Eq. (8) of \\cite Baumgarte1996hh.\n * Note on the word \"normal\": \\f$s_i\\f$ points in the correct direction\n * (it is \"normal\" to the surface), but it does not have unit length\n * (it is not \"normalized\"; normalization requires a metric).\n *\n * \\param dx_radius The Cartesian derivatives of the radius, as\n * returned by ylm::cartesian_derivs_of_scalar with\n * `ylm::radius` passed in as the scalar.\n * \\param r_hat The radial unit vector as returned by\n * `ylm::rhat`.\n */\ntemplate <typename Fr>\ntnsr::i<DataVector, 3, Fr> normal_one_form(\n    const tnsr::i<DataVector, 3, Fr>& dx_radius,\n    const tnsr::i<DataVector, 3, Fr>& r_hat);\n\n/*!\n * \\param one_form The returned normal one form.\n * \\param dx_radius The Cartesian derivatives of the radius, as\n * returned by ylm::cartesian_derivs_of_scalar with\n * `ylm::radius` passed in as the scalar.\n * \\param r_hat The radial unit vector as returned by\n * `ylm::rhat`.\n */\ntemplate <typename Fr>\nvoid normal_one_form(const gsl::not_null<tnsr::i<DataVector, 3, Fr>*> one_form,\n                     const tnsr::i<DataVector, 3, Fr>& dx_radius,\n                     const tnsr::i<DataVector, 3, Fr>& r_hat);\n\n/// @{\n/*!\n * The linear least squares fit of the polynomial of order 3\n * given a `std::vector` of `Strahlkorper`s to their \\f$Y_l^m\\f$ coefficients.\n * Assumes the the \\f$l_{\\max}\\f$ and \\f$m_{\\max}\\f$ of each `Strahlkorper`\n * are the same, and the returned vector consists of \\f$2l_{\\max}m_{\\max}\\f$\n * (the number of \\f$Y_l^m\\f$ coefficients) `std::array<double, 4>`s, each of\n * which consists of the four coefficients that define the best fit cubic to\n * each \\f$Y_l^m\\f$ coefficient of the `Strahlkorper` as a function of time.\n *\n * \\param times The time corresponding to each `Strahlkorper` to be fit to.\n * \\param strahlkorpers The `Strahlkorper` surfaces which consists of a set\n * of \\f$Y_l^m\\f$ coefficients corresponding to the shape of the `Strahlkorper`\n * at a particular time.\n */\ntemplate <typename Fr>\nstd::vector<std::array<double, 4>> fit_ylm_coeffs(\n    const DataVector& times,\n    const std::vector<Strahlkorper<Fr>>& strahlkorpers);\n\n/*!\n * \\brief Compute the time derivative of a Strahlkorper from a number of\n * previous Strahlkorpers\n *\n * \\details Does simple 1D FD with non-uniform spacing using\n * `fd::non_uniform_1d_weights`.\n * \\param time_deriv Strahlkorper whose coefficients are the time derivative of\n * `previous_strahlkorpers`' coefficients.\n * \\param previous_strahlkorpers All previous Strahlkorpers and the times they\n * are at. They are expected to have the most recent Strahlkorper in the front\n * and the Strahlkorper furthest in the past in the back of the deque.\n */\ntemplate <typename Frame>\nvoid time_deriv_of_strahlkorper(\n    gsl::not_null<Strahlkorper<Frame>*> time_deriv,\n    const std::deque<std::pair<double, Strahlkorper<Frame>>>&\n        previous_strahlkorpers);\n\n/// @{\n/*!\n * \\brief Compute the power monitor of a Strahlkorper\n *\n * \\details Computes a Strahlkorper represented as a spectral expansion\n * in scalar spherical harmonics $Y_{lm}(\\theta,\\phi)$ up to\n * $l=l_{\\rm max}$ and $m=m_{\\rm max}$. Then this function\n * computes the power in each $l$. If\n * a Strahlkorper's radius $r(\\theta,\\phi)$ is expanded as\n * \\begin{equation}\n * r(\\theta,\\phi) = \\sum_{l=0}^{l_{\\rm max}}\n *   \\sum_{m=-\\mathcal{M}}^{\\mathcal{M}} C_{lm} Y_{lm}(\\theta,\\phi),\n * \\end{equation}\n * where $\\mathcal{M}$ is $l$ or $m_{\\rm max}$, whichever is smaller,\n * then the power monitor $P_l$ is the square root of a normalized sum of\n * the magnitudes squared of the coefficients $C_{lm}$:\n * \\begin{equation}\n * P_l = \\sqrt{\\frac{1}{2 \\mathcal{M}+1}\\sum_{m=-\\mathcal{M}}^\\mathcal{M}\n *       \\left|C_{lm}\\right|^2}.\n * \\end{equation}\n * See Eq. (51) and the surrounding discussion in \\cite Szilagyi2014fna\n * for the motivation of this power monitor, which is useful in adaptively\n * selecting the angular resolution $l_{\\rm max}$ when finding\n * apparent horizons. But note that because internally the Strahlkorper is\n * represented as a Spherepack expansion, the implementation of this function\n * uses that expansion rather than a literal implementation of the\n * above equations. As a result, it's the Spherepack spectral coefficients\n * that get squared and (for each $l$) summed over.\n * \\param strahlkorper The Strahlkorper surface.\n */\ntemplate <typename Frame>\nDataVector power_monitor(const Strahlkorper<Frame>& strahlkorper);\n\n/*!\n * \\copydoc power_monitor\n * \\param result A DataVector containing the power monitor $P_l$ for each\n * $l$ up to the Strahlkorper's `l_max`.\n */\ntemplate <typename Frame>\nvoid power_monitor(gsl::not_null<DataVector*> result,\n                   const Strahlkorper<Frame>& strahlkorper);\n/// @}\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ylm::Tags {\n\ntemplate <typename Frame>\nvoid PhysicalCenterCompute<Frame>::function(\n    const gsl::not_null<std::array<double, 3>*> physical_center,\n    const ylm::Strahlkorper<Frame>& strahlkorper) {\n  *physical_center = strahlkorper.physical_center();\n}\n\n}  // namespace ylm::Tags\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data) \\\n  template struct ylm::Tags::PhysicalCenterCompute<FRAME(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Frame::Grid, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TagsDeclarations.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TagsTypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup SurfacesGroup\n/// Holds tags and ComputeItems associated with a `ylm::Strahlkorper`.\nnamespace ylm::Tags {\n\n/// Tag referring to a `ylm::Strahlkorper`\ntemplate <typename Frame>\nstruct Strahlkorper : db::SimpleTag {\n  using type = ylm::Strahlkorper<Frame>;\n};\n\n/// @{\n/// \\f$(\\theta,\\phi)\\f$ on the grid.\n/// Doesn't depend on the shape of the surface.\ntemplate <typename Frame>\nstruct ThetaPhi : db::SimpleTag {\n  using type = tnsr::i<DataVector, 2, ::Frame::Spherical<Frame>>;\n};\n\ntemplate <typename Frame>\nstruct ThetaPhiCompute : ThetaPhi<Frame>, db::ComputeTag {\n  using base = ThetaPhi<Frame>;\n  using return_type = tnsr::i<DataVector, 2, ::Frame::Spherical<Frame>>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::i<DataVector, 2, ::Frame::Spherical<Frame>>*>,\n      const ylm::Strahlkorper<Frame>&)>(&::ylm::theta_phi<Frame>);\n  using argument_tags = tmpl::list<Strahlkorper<Frame>>;\n};\n/// @}\n\n/// @{\n/// `Rhat(i)` is \\f$\\hat{r}^i = x_i/\\sqrt{x^2+y^2+z^2}\\f$ on the grid.\n/// Doesn't depend on the shape of the surface.\ntemplate <typename Frame>\nstruct Rhat : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Frame>;\n};\n\ntemplate <typename Frame>\nstruct RhatCompute : Rhat<Frame>, db::ComputeTag {\n  using base = Rhat<Frame>;\n  using return_type = tnsr::i<DataVector, 3, Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::i<DataVector, 3, Frame>*>,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<Frame>>&)>(\n      &::ylm::rhat<Frame>);\n  using argument_tags = tmpl::list<ThetaPhi<Frame>>;\n};\n/// @}\n\n/// @{\n/// `Jacobian(i,0)` is \\f$\\frac{1}{r}\\partial x^i/\\partial\\theta\\f$,\n/// and `Jacobian(i,1)`\n/// is \\f$\\frac{1}{r\\sin\\theta}\\partial x^i/\\partial\\phi\\f$.\n/// Here \\f$r\\f$ means \\f$\\sqrt{x^2+y^2+z^2}\\f$.\n/// `Jacobian` doesn't depend on the shape of the surface.\ntemplate <typename Frame>\nstruct Jacobian : db::SimpleTag {\n  using type = aliases::Jacobian<Frame>;\n};\n\ntemplate <typename Frame>\nstruct JacobianCompute : Jacobian<Frame>, db::ComputeTag {\n  using base = Jacobian<Frame>;\n  using return_type = aliases::Jacobian<Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<ylm::Tags::aliases::Jacobian<Frame>*>,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<Frame>>&)>(\n      &::ylm::jacobian<Frame>);\n  using argument_tags = tmpl::list<ThetaPhi<Frame>>;\n};\n/// @}\n\n/// @{\n/// `InvJacobian(0,i)` is \\f$r\\partial\\theta/\\partial x^i\\f$,\n/// and `InvJacobian(1,i)` is \\f$r\\sin\\theta\\partial\\phi/\\partial x^i\\f$.\n/// Here \\f$r\\f$ means \\f$\\sqrt{x^2+y^2+z^2}\\f$.\n/// `InvJacobian` doesn't depend on the shape of the surface.\ntemplate <typename Frame>\nstruct InvJacobian : db::SimpleTag {\n  using type = aliases::InvJacobian<Frame>;\n};\n\ntemplate <typename Frame>\nstruct InvJacobianCompute : InvJacobian<Frame>, db::ComputeTag {\n  using base = InvJacobian<Frame>;\n  using return_type = aliases::InvJacobian<Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<ylm::Tags::aliases::InvJacobian<Frame>*>,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<Frame>>&)>(\n      &::ylm::inv_jacobian<Frame>);\n  using argument_tags = tmpl::list<ThetaPhi<Frame>>;\n};\n/// @}\n\n/// @{\n/// `InvHessian(k,i,j)` is \\f$\\partial (J^{-1}){}^k_j/\\partial x^i\\f$,\n/// where \\f$(J^{-1}){}^k_j\\f$ is the inverse Jacobian.\n/// `InvHessian` is not symmetric because the Jacobians are Pfaffian.\n/// `InvHessian` doesn't depend on the shape of the surface.\ntemplate <typename Frame>\nstruct InvHessian : db::SimpleTag {\n  using type = aliases::InvHessian<Frame>;\n};\n\ntemplate <typename Frame>\nstruct InvHessianCompute : InvHessian<Frame>, db::ComputeTag {\n  using base = InvHessian<Frame>;\n  using return_type = aliases::InvHessian<Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<ylm::Tags::aliases::InvHessian<Frame>*>,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<Frame>>&)>(\n      &::ylm::inv_hessian<Frame>);\n  using argument_tags = tmpl::list<ThetaPhi<Frame>>;\n};\n/// @}\n\n/// @{\n/// (Euclidean) distance \\f$r_{\\rm surf}(\\theta,\\phi)\\f$ from the center to each\n/// point of the surface.\ntemplate <typename Frame>\nstruct Radius : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <typename Frame>\nstruct RadiusCompute : Radius<Frame>, db::ComputeTag {\n  using base = Radius<Frame>;\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function =\n      static_cast<void (*)(const gsl::not_null<Scalar<DataVector>*>,\n                           const ylm::Strahlkorper<Frame>&)>(\n          &(::ylm::radius<Frame>));\n  using argument_tags = tmpl::list<Strahlkorper<Frame>>;\n};\n/// @}\n\n/// @{\n/// The geometrical center of the surface.  Uses\n/// `ylm::Strahlkorper::physical_center`.\ntemplate <typename Frame>\nstruct PhysicalCenter : db::SimpleTag {\n  using type = std::array<double, 3>;\n};\n\ntemplate <typename Frame>\nstruct PhysicalCenterCompute : PhysicalCenter<Frame>, db::ComputeTag {\n  using base = PhysicalCenter<Frame>;\n  using return_type = std::array<double, 3>;\n  static void function(gsl::not_null<std::array<double, 3>*> physical_center,\n                       const ylm::Strahlkorper<Frame>& strahlkorper);\n  using argument_tags = tmpl::list<Strahlkorper<Frame>>;\n};\n/// @}\n\n/// @{\n/// `CartesianCoords(i)` is \\f$x_{\\rm surf}^i\\f$,\n/// the vector of \\f$(x,y,z)\\f$ coordinates of each point\n/// on the surface.\ntemplate <typename Frame>\nstruct CartesianCoords : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame>;\n};\n\ntemplate <typename Frame>\nstruct CartesianCoordsCompute : CartesianCoords<Frame>, db::ComputeTag {\n  using base = CartesianCoords<Frame>;\n  using return_type = tnsr::I<DataVector, 3, Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::I<DataVector, 3, Frame>*> coords,\n      const ylm::Strahlkorper<Frame>& strahlkorper,\n      const Scalar<DataVector>& radius,\n      const tnsr::i<DataVector, 3, Frame>& r_hat)>(&ylm::cartesian_coords);\n  using argument_tags =\n      tmpl::list<Strahlkorper<Frame>, Radius<Frame>, Rhat<Frame>>;\n};\n/// @}\n\n/// @{\n/// `DxRadius(i)` is \\f$\\partial r_{\\rm surf}/\\partial x^i\\f$.  Here\n/// \\f$r_{\\rm surf}=r_{\\rm surf}(\\theta,\\phi)\\f$ is the function\n/// describing the surface, which is considered a function of\n/// Cartesian coordinates\n/// \\f$r_{\\rm surf}=r_{\\rm surf}(\\theta(x,y,z),\\phi(x,y,z))\\f$\n/// for this operation.\ntemplate <typename Frame>\nstruct DxRadius : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Frame>;\n};\n\ntemplate <typename Frame>\nstruct DxRadiusCompute : DxRadius<Frame>, db::ComputeTag {\n  using base = DxRadius<Frame>;\n  using return_type = tnsr::i<DataVector, 3, Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::i<DataVector, 3, Frame>*> dx_radius,\n      const Scalar<DataVector>& scalar,\n      const ylm::Strahlkorper<Frame>& strahlkorper,\n      const Scalar<DataVector>& radius_of_strahlkorper,\n      const aliases::InvJacobian<Frame>& inv_jac)>(\n      &ylm::cartesian_derivs_of_scalar);\n  using argument_tags = tmpl::list<Radius<Frame>, Strahlkorper<Frame>,\n                                   Radius<Frame>, InvJacobian<Frame>>;\n};\n/// @}\n\n/// @{\n/// `D2xRadius(i,j)` is\n/// \\f$\\partial^2 r_{\\rm surf}/\\partial x^i\\partial x^j\\f$. Here\n/// \\f$r_{\\rm surf}=r_{\\rm surf}(\\theta,\\phi)\\f$ is the function\n/// describing the surface, which is considered a function of\n/// Cartesian coordinates\n/// \\f$r_{\\rm surf}=r_{\\rm surf}(\\theta(x,y,z),\\phi(x,y,z))\\f$\n/// for this operation.\ntemplate <typename Frame>\nstruct D2xRadius : db::SimpleTag {\n  using type = tnsr::ii<DataVector, 3, Frame>;\n};\n\ntemplate <typename Frame>\nstruct D2xRadiusCompute : D2xRadius<Frame>, db::ComputeTag {\n  using base = D2xRadius<Frame>;\n  using return_type = tnsr::ii<DataVector, 3, Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ii<DataVector, 3, Frame>*> d2x_radius,\n      const Scalar<DataVector>& scalar,\n      const ylm::Strahlkorper<Frame>& strahlkorper,\n      const Scalar<DataVector>& radius_of_strahlkorper,\n      const aliases::InvJacobian<Frame>& inv_jac,\n      const aliases::InvHessian<Frame>& inv_hess)>(\n      &ylm::cartesian_second_derivs_of_scalar);\n  using argument_tags =\n      tmpl::list<Radius<Frame>, Strahlkorper<Frame>, Radius<Frame>,\n                 InvJacobian<Frame>, InvHessian<Frame>>;\n};\n/// @}\n\n/// @{\n/// \\f$\\nabla^2 r_{\\rm surf}\\f$, the flat Laplacian of the surface.\n/// This is \\f$\\eta^{ij}\\partial^2 r_{\\rm surf}/\\partial x^i\\partial x^j\\f$,\n/// where \\f$r_{\\rm surf}=r_{\\rm surf}(\\theta(x,y,z),\\phi(x,y,z))\\f$.\ntemplate <typename Frame>\nstruct LaplacianRadius : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <typename Frame>\nstruct LaplacianRadiusCompute : LaplacianRadius<Frame>, db::ComputeTag {\n  using base = LaplacianRadius<Frame>;\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*> lap_radius,\n      const Scalar<DataVector>& radius,\n      const ylm::Strahlkorper<Frame>& strahlkorper,\n      const tnsr::i<DataVector, 2, ::Frame::Spherical<Frame>>& theta_phi)>(\n      &ylm::laplacian_of_scalar);\n  using argument_tags =\n      tmpl::list<Radius<Frame>, Strahlkorper<Frame>, ThetaPhi<Frame>>;\n};\n/// @}\n\n/// @{\n/// `NormalOneForm(i)` is \\f$s_i\\f$, the (unnormalized) normal one-form\n/// to the surface, expressed in Cartesian components.\n/// This is computed by \\f$x_i/r-\\partial r_{\\rm surf}/\\partial x^i\\f$,\n/// where \\f$x_i/r\\f$ is `Rhat` and\n/// \\f$\\partial r_{\\rm surf}/\\partial x^i\\f$ is `DxRadius`.\n/// See Eq. (8) of \\cite Baumgarte1996hh.\n/// Note on the word \"normal\": \\f$s_i\\f$ points in the correct direction\n/// (it is \"normal\" to the surface), but it does not have unit length\n/// (it is not \"normalized\"; normalization requires a metric).\ntemplate <typename Frame>\nstruct NormalOneForm : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Frame>;\n};\n\ntemplate <typename Frame>\nstruct NormalOneFormCompute : NormalOneForm<Frame>, db::ComputeTag {\n  using base = NormalOneForm<Frame>;\n  using return_type = tnsr::i<DataVector, 3, Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::i<DataVector, 3, Frame>*> one_form,\n      const tnsr::i<DataVector, 3, Frame>& dx_radius,\n      const tnsr::i<DataVector, 3, Frame>& r_hat)>(&ylm::normal_one_form);\n  using argument_tags = tmpl::list<DxRadius<Frame>, Rhat<Frame>>;\n};\n/// @}\n\n/// @{\n/// `Tangents(i,j)` is \\f$\\partial x_{\\rm surf}^i/\\partial q^j\\f$,\n/// where \\f$x_{\\rm surf}^i\\f$ are the Cartesian coordinates of the\n/// surface (i.e. `CartesianCoords`) and are considered functions of\n/// \\f$(\\theta,\\phi)\\f$.\n///\n/// \\f$\\partial/\\partial q^0\\f$ means\n/// \\f$\\partial/\\partial\\theta\\f$; and \\f$\\partial/\\partial q^1\\f$\n/// means \\f$\\csc\\theta\\,\\,\\partial/\\partial\\phi\\f$.  Note that the\n/// vectors `Tangents(i,0)` and `Tangents(i,1)` are orthogonal to the\n/// `NormalOneForm` \\f$s_i\\f$, i.e.\n/// \\f$s_i \\partial x_{\\rm surf}^i/\\partial q^j = 0\\f$; this statement\n/// is independent of a metric.  Also, `Tangents(i,0)` and\n/// `Tangents(i,1)` are not necessarily orthogonal to each other,\n/// since orthogonality between 2 vectors (as opposed to a vector and\n/// a one-form) is metric-dependent.\ntemplate <typename Frame>\nstruct Tangents : db::SimpleTag {\n  using type = aliases::Jacobian<Frame>;\n};\n\ntemplate <typename Frame>\nstruct TangentsCompute : Tangents<Frame>, db::ComputeTag {\n  using base = Tangents<Frame>;\n  using return_type = aliases::Jacobian<Frame>;\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<aliases::Jacobian<Frame>*> tangents,\n                           const ylm::Strahlkorper<Frame>& strahlkorper,\n                           const Scalar<DataVector>& radius,\n                           const tnsr::i<DataVector, 3, Frame>& r_hat,\n                           const aliases::Jacobian<Frame>& jac)>(\n          &ylm::tangents);\n  using argument_tags = tmpl::list<Strahlkorper<Frame>, Radius<Frame>,\n                                   Rhat<Frame>, Jacobian<Frame>>;\n};\n/// @}\n\n/// Tag for holding the previously-found values of a Strahlkorper,\n/// which are saved for extrapolation for future initial guesses\n/// and for computing the time deriv of a Strahlkorper.\ntemplate <typename Frame>\nstruct PreviousStrahlkorpers : db::SimpleTag {\n  using type = std::deque<std::pair<double, ylm::Strahlkorper<Frame>>>;\n};\n\n/// @{\n/// Tag to compute the time derivative of the coefficients of a Strahlkorper\n/// from a number of previous Strahlkorpers.\ntemplate <typename Frame>\nstruct TimeDerivStrahlkorper : db::SimpleTag {\n  using type = ylm::Strahlkorper<Frame>;\n};\n\ntemplate <typename Frame>\nstruct TimeDerivStrahlkorperCompute : db::ComputeTag,\n                                      TimeDerivStrahlkorper<Frame> {\n  using base = TimeDerivStrahlkorper<Frame>;\n  using return_type = typename base::type;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<ylm::Strahlkorper<Frame>*>,\n      const std::deque<std::pair<double, ylm::Strahlkorper<Frame>>>&)>(\n      &ylm::time_deriv_of_strahlkorper<Frame>);\n\n  using argument_tags = tmpl::list<PreviousStrahlkorpers<Frame>>;\n};\n/// @}\n\ntemplate <typename Frame>\nusing items_tags = tmpl::list<Strahlkorper<Frame>>;\n\ntemplate <typename Frame>\nusing compute_items_tags =\n    tmpl::list<ThetaPhiCompute<Frame>, RhatCompute<Frame>,\n               JacobianCompute<Frame>, InvJacobianCompute<Frame>,\n               InvHessianCompute<Frame>, RadiusCompute<Frame>,\n               CartesianCoordsCompute<Frame>, DxRadiusCompute<Frame>,\n               D2xRadiusCompute<Frame>, LaplacianRadiusCompute<Frame>,\n               NormalOneFormCompute<Frame>, TangentsCompute<Frame>>;\n}  // namespace ylm::Tags\n\n/// Tags related to symmetric trace-free tensors\nnamespace Stf::Tags {\n\n/*!\n * \\brief Tag used to hold a symmetric trace-free tensor of a certain rank.\n * \\details The type is a symmetric tensor of the requested rank. A\n * ScalarBaseTag of type `Scalar<double>` is used to identify the tag of which\n * the symmetric trace-free expansion is done.\n */\ntemplate <typename ScalarBaseTag, size_t rank, size_t Dim, typename Frame>\nstruct StfTensor : db::SimpleTag {\n  static_assert(std::is_same_v<typename ScalarBaseTag::type, Scalar<double>>,\n                \"StfTensor base tags must be a Scalar.\");\n  static_assert(rank <= 3, \"StfTensor tag is only implemented up to rank 3.\");\n  static std::string name() {\n    return MakeString{} << \"StfTensor(\" << db::tag_name<ScalarBaseTag>()\n                        << \",\" << rank << \")\";\n  }\n  using type_list =\n      tmpl::list<Scalar<double>, tnsr::i<double, Dim, Frame>,\n                 tnsr::ii<double, Dim, Frame>, tnsr::iii<double, Dim, Frame>>;\n  using type = tmpl::at<type_list, tmpl::size_t<rank>>;\n};\n}  // namespace Stf::Tags\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ylm::Tags {\ntemplate <typename Frame>\nstruct Strahlkorper;\ntemplate <typename Frame>\nstruct ThetaPhi;\ntemplate <typename Frame>\nstruct Rhat;\ntemplate <typename Frame>\nstruct Jacobian;\ntemplate <typename Frame>\nstruct InvJacobian;\ntemplate <typename Frame>\nstruct InvHessian;\ntemplate <typename Frame>\nstruct Radius;\ntemplate <typename Frame>\nstruct CartesianCoords;\ntemplate <typename Frame>\nstruct PhysicalCenter;\ntemplate <typename Frame>\nstruct DxRadius;\ntemplate <typename Frame>\nstruct D2xRadius;\ntemplate <typename Frame>\nstruct LaplacianRadius;\ntemplate <typename Frame>\nstruct NormalOneForm;\ntemplate <typename Frame>\nstruct Tangents;\n}  // namespace ylm::Tags\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TagsTypeAliases.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass DataVector;\n\nnamespace ylm::Tags {\n/// Defines type aliases used in Strahlkorper-related Tags.\nnamespace aliases {\ntemplate <typename Fr>\nusing Jacobian =\n    Tensor<DataVector, tmpl::integral_list<std::int32_t, 2, 1>,\n           index_list<SpatialIndex<3, UpLo::Up, Fr>,\n                      SpatialIndex<2, UpLo::Lo, ::Frame::Spherical<Fr>>>>;\ntemplate <typename Fr>\nusing InvJacobian =\n    Tensor<DataVector, tmpl::integral_list<std::int32_t, 2, 1>,\n           index_list<SpatialIndex<2, UpLo::Up, ::Frame::Spherical<Fr>>,\n                      SpatialIndex<3, UpLo::Lo, Fr>>>;\ntemplate <typename Fr>\nusing InvHessian = Tensor<\n    DataVector, tmpl::integral_list<std::int32_t, 3, 2, 1>,\n    index_list<SpatialIndex<2, UpLo::Up, ::Frame::Spherical<Fr>>,\n               SpatialIndex<3, UpLo::Lo, Fr>, SpatialIndex<3, UpLo::Lo, Fr>>>;\n}  // namespace aliases\n}  // namespace ylm::Tags\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief Holds functions that converts spatial tensors between scalar-Ylm\n * and tensor-Ylm basis, and functions that filter by doing such a conversion.\n *\n * \\details We expand a spatial tensor of arbitrary rank\n * in terms of Cartesian components as follows:\n * \\begin{align}\n * {\\mathbf T} &= \\sum_{\\ell,m,\\tilde{A}} {}_0 Y_{\\ell m}\n *                T^{\\tilde A}_{\\ell m}\\mathbf{e}_{\\tilde A},\n * \\end{align}\n * where ${}_0 Y_{\\ell m}$ are the usual scalar spherical harmonics,\n * $T^{\\tilde A}_{\\ell m}$ are complex-valued expansion coefficients,\n * $\\tilde A$ is a multi-index\n * $\\tilde{A}=(\\tilde{a}_1,\\tilde{a}_2,\\tilde{a}_3,\\ldots)$, where each\n * of the $\\tilde{a}_i$ take on values from 0 to 2, referring to\n * either $\\mathbf{e}_x$, $\\mathbf{e}_y$, or $\\mathbf{e}_z$.\n * The sum over $\\tilde A$ goes over all $\\tilde A$ of the same rank.\n *\n * Similarly, we can expand the same spatial\n * tensor in terms of a complex tetrad:\n * \\begin{align}\n * {\\mathbf T} &= \\sum_{\\ell,m,A} {}_{s(\\!A\\!)}Y_{\\ell m}\n *                T^A_{\\ell m} \\mathbf{e}_A,\n * \\end{align}\n * where ${}_sY_{\\ell m}$ are the spin-weighted harmonics,\n * $A$ is a multi-index $A=(a_1,a_2,a_3,\\ldots)$, where each of the\n * $a_i$ refer to either $\\mathbf{l}$,$\\mathbf{m}$, or $\\mathbf{\\bar{m}}$,\n * and $s(\\!A\\!)$ is the spin weight, which is a function of\n * $A$: each $\\mathbf{l}$ in $A$ adds the value zero, each $\\mathbf{m}$\n * adds the value -1, and each $\\mathbf{\\bar{m}}$ adds the value +1.\n * (These values are the spin weights of the complex conjugates\n * of the basis vectors.)\n *\n * To allow for a compact notation where we don't need to write different\n * equations for different combinations of basis vectors,\n * we define two auxiliary quantities $k_j$ and $m_j$\n * that are functions of the Cartesian basis vectors:\n * \\begin{align}\n *  k_j(\\mathbf{e}_x) &= -j,\\\\\n *  k_j(\\mathbf{e}_y) &=  i,\\\\\n *  k_j(\\mathbf{e}_z) &= 1/\\sqrt{2},\\\\\n *  m_j(\\mathbf{e}_x) &= j,\\\\\n *  m_j(\\mathbf{e}_y) &= j,\\\\\n *  m_j(\\mathbf{e}_z) &= 0,\n * \\end{align}\n * where the $i$ above is the imaginary unit $i=\\sqrt{-1}$.\n * These quantities will be used in explicit formulas for transformations\n * between expansion coefficients.\n *\n * ### Spherepack coefficients\n *\n * Instead of using the actual spherical-harmonic or\n * spin-weighted spherical-harmonic coefficients $T^{B}_{\\ell' m'}$ and\n * $T^{\\tilde A}_{\\ell'm'}$, we often work with\n * coefficients computed by Spherepack, which have a different normalization\n * and phase than the standard coefficients.  While Spherepack internally\n * stores real matrices it calls $a_{lm}$ and $b_{lm}$ (see Spherepack\n * documentation for details), we simplify things by defining\n * complex Spherepack coefficients\n * ${\\breve T}^{B}_{\\ell' m'}$ and ${\\breve T}^{\\tilde A}_{\\ell'm'}$ that\n * have Spherepack normalization. These are related to the standard coefficients\n * by\n * \\begin{align}\n *  {\\breve T}^{A}_{\\ell m} &= (-1)^m \\sqrt{\\frac{2}{\\pi}} T^{B}_{\\ell m},\\\\\n *  {\\breve T}^{\\tilde A}_{\\ell m} &= (-1)^m \\sqrt{\\frac{2}{\\pi}}\n *  T^{\\tilde A}_{\\ell m}.\n * \\end{align}\n * Some of the formulas below are slightly different when acting on\n * Spherepack coefficients compared to standard coefficients.\n *\n * ## Transformations\n *\n * We can define the following transformations between the expansion\n * coefficients $T^A_{\\ell m}$ and $T^{\\tilde A}_{\\ell m}$:\n * \\begin{align}\n * T^{\\tilde B}_{\\ell' m'} &=\n * \\sum_{\\ell,m,A} C_{\\ell' m' A}^{\\ell m\\tilde{B}} T^A_{\\ell m},\\\\\n * T^{B}_{\\ell' m'} &= \\sum_{\\ell m \\tilde{A}}\n *                  C_{\\ell' m'\\tilde{A}}^{\\ell mB}\n *                  T^{\\tilde A}_{\\ell m},\n * \\end{align}\n * where the above transformations define the coefficients\n * $C_{\\ell' m' A}^{\\ell m\\tilde{B}}$ and $C_{\\ell' m'\\tilde{A}}^{\\ell mB}$.\n * Analytic expressions for these coefficients are derived in Klinger\n * and Scheel (in prep) and will be used here to compute the coefficients.\n *\n * For real-valued tensors, the coefficients $T^A_{\\ell m}$ and\n * $T^{\\tilde A}_{\\ell m}$ for negative $m$ can be computed from the\n * complex conjugates of the same coefficients with positive $m$.  Therefore\n * we store and loop over only nonnegative $m$. This means we can write\n * \\begin{align}\n * T^{\\tilde B}_{\\ell m} &= \\sum_{A, \\ell',m'\\geq 0}\n *                  \\left[\n *              C_{\\ell m A}^{\\ell' m'\\tilde{B}} T^A_{\\ell' m'}\n *               +{\\hat C}_{\\ell m A}^{\\ell' m'\\tilde{B}} T^A_{\\ell' m'}{}^\\star\n *                \\right]\\label{eq:S2C},\\\\\n * T^{B}_{\\ell m} &= \\sum_{\\tilde{A},\\ell',m'\\geq 0}\n *                  \\left[\n *                  C_{\\ell m\\tilde{A}}^{\\ell' m' B}\n *                  T^{\\tilde A}_{\\ell' m'}\n *                  +{\\hat C}_{\\ell m\\tilde{A}}^{\\ell' m' B}\n *                  T^{\\tilde A}_{\\ell' m'}{}^\\star\n *                  \\right]\\label{eq:C2S},\n * \\end{align}\n * where\n * \\begin{align}\n * {\\hat C}_{\\ell m A}^{\\ell' m'\\tilde{B}} &=\n *                 (1-\\delta_{0m'})C_{\\ell m A^\\star}^{\\ell' -m'\\tilde{B}}\n *                 (-1)^{S(A)+m'},\\\\\n * {\\hat C}_{\\ell m\\tilde{A}}^{\\ell' m' B} &=\n *                (1-\\delta_{0m'})C_{\\ell m \\tilde{A}}^{\\ell' -m'B}\n *                 (-1)^{m'}.\n * \\end{align}\n *\n * The functions FillCartToSphere and FillSphereToCart fill\n * sparse matrices that encode Eqs. $(\\ref{eq:C2S})$ and\n * $(\\ref{eq:S2C})$. When working on Spherepack coefficients, the functions\n * FillCartToSphere and FillSphereToCart instead\n * fill sparse matrices that encode\n * \\begin{align}\n *  {\\breve T}^{\\tilde B}_{\\ell m} &= (-1)^m\\sum_{A, \\ell',m'\\geq 0}(-1)^{m'}\n *                  \\left[\n *              C_{\\ell m A}^{\\ell' m'\\tilde{B}} T^A_{\\ell' m'}\n *               +{\\hat C}_{\\ell m A}^{\\ell' m'\\tilde{B}}\n *                {\\breve T}^A_{\\ell' m'}{}^\\star\n *                \\right]\\label{eq:S2CSpherepack},\\\\\n *  {\\breve T}^{B}_{\\ell m} &= (-1)^m\\sum_{\\tilde{A},\\ell',m'\\geq 0}(-1)^{m'}\n *                  \\left[\n *                  C_{\\ell m\\tilde{A}}^{\\ell' m' B}\n *                  T^{\\tilde A}_{\\ell' m'}\n *                  +{\\hat C}_{\\ell m\\tilde{A}}^{\\ell' m' B}\n *                  {\\breve T}^{\\tilde A}_{\\ell' m'}{}^\\star\n *                  \\right]\\label{eq:C2SSpherepack}.\n * \\end{align}\n *\n * ## Filtering\n *\n * Consider starting with coefficients $T^{\\tilde A}_{\\ell' m'}$,\n * transforming to spin-weighted harmonics, applying a filter to\n * the spin-weighted harmonic coefficients, and transforming back.\n * We can write this entire operation as\n * \\begin{align}\n *   F_{\\ell m \\tilde{D}}^{\\ell'' m''\\tilde{A}} &=\n *   \\sum_{\\ell'=0}^{\\ell_{\\mathrm{cut}}^--1} C^{\\ell' m' \\tilde{A}}_{\\ell m B}\n *   C^{\\ell'' m'' B}_{\\ell'  m' \\tilde{D}}\n * + \\sum_{\\ell'=\\ell_{\\mathrm{cut}}^-}^{\\ell_{\\mathrm{cut}}^+}\n *   C^{\\ell' m' \\tilde{A}}_{\\ell m B}\n *   C^{\\ell'' m'' B}_{\\ell'  m' \\tilde{D}} f(\\ell'),\n * \\end{align}\n * where $f(\\ell')$ is a filter function,\n * $\\ell_{\\mathrm{cut}}^{-}$ is the smallest $\\ell$ mode that is filtered,\n * and\n * $\\ell_{\\mathrm{cut}}^{+}$ is the largest $\\ell$ mode that is retained.\n *\n * There are two cases we care about.\n * The first is simple \"Heaviside filtering\", where\n * $\\ell_{\\mathrm{cut}}^{-} = \\ell_{\\mathrm{cut}}^{+}$ and $f(\\ell')=1$.\n *\n * The second case is\n * \\begin{align}\n *  f(\\ell') &= \\exp\\left[-\\alpha\n * \\left(\\frac{\\ell'}{\\ell_{\\mathrm{cut}}^++1}\\right)^{2 \\sigma}\\right],\\\\\n * \\ell_{\\mathrm{cut}}^- &= \\mathrm{ceil}\\left[(\\ell_{\\mathrm{cut}}^++1)\n *    \\left(\\frac{\\epsilon}{\\alpha}\\right)^{1/(2\\sigma)}\\right],\n * \\end{align}\n * where $\\alpha$ is a parameter we choose to be 36, $\\sigma$ is an integer\n * parameter typically between 28 and 32, and\n * $\\epsilon$ is machine roundoff.\n * Note that the filter remains smooth at $\\ell' = \\ell_{\\mathrm{cut}}^-$ and\n * it reduces to the simple filter as $\\sigma \\to \\infty$.\n *\n * As with the transformations, we sum over only nonnegative $m$, so we can\n * write the action of the filter as\n * \\begin{align}\n * T^{\\tilde B}_{\\ell m} &= \\sum_{{\\tilde A}, \\ell',m'\\geq 0}\n *                  \\left[\n *              F_{\\ell m {\\tilde A}}^{\\ell' m'\\tilde{B}}\n *                T^{\\tilde A}_{\\ell' m'}\n *               +{\\hat F}_{\\ell m {\\tilde A}}^{\\ell' m'\\tilde{B}}\n *               T^{\\tilde A}_{\\ell' m'}{}^\\star\n *                \\right]\\label{eq:Filter},\n * \\end{align}\n * where\n * \\begin{align}\n * {\\hat F}_{\\ell m\\tilde{A}}^{\\ell' m' \\tilde{B}} &=\n *                (1-\\delta_{0m'})F_{\\ell m \\tilde{A}}^{\\ell' -m' \\tilde{B}}\n *                 (-1)^{m'}.\n * \\end{align}\n *\n * When filtering Spherepack coefficients, the expression is\n * \\begin{align}\n * {\\breve T}^{\\tilde B}_{\\ell m} &=\n *           (-1)^m\\sum_{{\\tilde A}, \\ell',m'\\geq 0}(-1)^{m'}\n *                  \\left[\n *              F_{\\ell m {\\tilde A}}^{\\ell' m'\\tilde{B}}\n *                {\\breve T}^{\\tilde A}_{\\ell' m'}\n *               +{\\hat F}_{\\ell m {\\tilde A}}^{\\ell' m'\\tilde{B}}\n *               {\\breve T}^{\\tilde A}_{\\ell' m'}{}^\\star\n *                \\right]\\label{eq:FilterSpherepack}.\n * \\end{align}\n *\n * The function FillFilter encapsulates Eq. $(\\ref{eq:Filter})$ or\n * Eq. $(\\ref{eq:FilterSpherepack})$,\n * depending what type of coefficients are being filtered.\n */\nnamespace ylm::TensorYlm {\n\n/// Instances of this class are passed to FillFilter,\n/// FillCartToSphere, and FillSphereToCart to identify how the\n/// coefficients are normalized.  This makes a difference because the\n/// normalization is not just a constant factor.\nenum class CoefficientNormalization {\n  /// The standard normalization of the spherical-harmonic coefficients.\n  Standard,\n  /// The Spherepack normalization. Spherepack coefficients are standard\n  /// coefficients multiplied by $(-1)^m \\sqrt{2/\\pi}$.\n  Spherepack\n};\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlmCartToSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmCartToSphere.hpp\"\n\n#include <blaze/math/CompressedMatrix.h>\n#include <complex>\n#include <optional>\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"DataStructures/SparseMatrixFiller.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/WignerThreeJ.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace ylm::TensorYlm {\n\nnamespace {\n// Inner loops of the rank-1 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\nvoid inner_loops_one(SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n                     SpherepackIterator& iter_dest, const size_t src_comp_index,\n                     const size_t dest_comp_index, const size_t ell_max,\n                     const size_t l_dest, const int m_dest, const int mj,\n                     const std::complex<double> kj, const int sign_ysb,\n                     WignerThreeJ& threej_m, WignerThreeJ& threej_s,\n                     const CoefficientNormalization coefficient_normalization) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, dest_comp_index,\n                            src_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  for (size_t l_src = threej_s.l1_min();\n       (l_src <= threej_s.l1_max() and l_src <= ell_max); ++l_src) {\n    const double sqterm =\n        sqrt(0.5 * static_cast<double>((2 * l_dest + 1) * (2 * l_src + 1)));\n    const int m_src = m_dest + mj;\n    const auto sign_m =\n        helpers::sign_m<double>(m_src + m_dest, coefficient_normalization);\n\n    const int sign_m_src = (m_src % 2 == 0 ? 1 : -1);\n    const double coef_without_kj = sign_m * sqterm * sign_ysb * sign_m_src *\n                                   threej_s(l_src) * threej_m(l_src);\n\n    if (m_src >= 0 and static_cast<size_t>(m_src) <= l_src) {\n      // Main term.\n      if (kj.imag() == 0) {\n        if (iter_dest.coefficient_array() ==\n            SpherepackIterator::CoefficientArray::a) {\n          // ReRe\n          iter_src.set(l_src, static_cast<size_t>(m_src),\n                       SpherepackIterator::CoefficientArray::a);\n          add_element(kj.real() * coef_without_kj);\n        } else if (iter_dest.coefficient_array() ==\n                   SpherepackIterator::CoefficientArray::b) {\n          // ImIm\n          iter_src.set(l_src, static_cast<size_t>(m_src),\n                       SpherepackIterator::CoefficientArray::b);\n          add_element(kj.real() * coef_without_kj);\n        }\n      } else {\n        if (iter_dest.coefficient_array() ==\n            SpherepackIterator::CoefficientArray::a) {\n          // ReIm\n          iter_src.set(l_src, static_cast<size_t>(m_src),\n                       SpherepackIterator::CoefficientArray::b);\n          add_element(-kj.imag() * coef_without_kj);\n        } else if (iter_dest.coefficient_array() ==\n                   SpherepackIterator::CoefficientArray::b) {\n          // ImRe\n          iter_src.set(l_src, static_cast<size_t>(m_src),\n                       SpherepackIterator::CoefficientArray::a);\n          add_element(kj.imag() * coef_without_kj);\n        }\n      }\n    } else if (m_src <= 0 and static_cast<size_t>(-m_src) <= l_src) {\n      if (kj.imag() == 0) {\n        // Other term, for negative m_src.\n        if (iter_dest.coefficient_array() ==\n            SpherepackIterator::CoefficientArray::a) {\n          // ReRe\n          iter_src.set(l_src, static_cast<size_t>(-m_src),\n                       SpherepackIterator::CoefficientArray::a);\n          add_element(kj.real() * coef_without_kj * sign_m_src);\n        } else if (iter_dest.coefficient_array() ==\n                   SpherepackIterator::CoefficientArray::b) {\n          // ImIm\n          iter_src.set(l_src, static_cast<size_t>(-m_src),\n                       SpherepackIterator::CoefficientArray::b);\n          add_element(-kj.real() * coef_without_kj * sign_m_src);\n        }\n      } else {\n        if (iter_dest.coefficient_array() ==\n            SpherepackIterator::CoefficientArray::a) {\n          // ReIm\n          iter_src.set(l_src, static_cast<size_t>(-m_src),\n                       SpherepackIterator::CoefficientArray::b);\n          add_element(kj.imag() * coef_without_kj * sign_m_src);\n        } else if (iter_dest.coefficient_array() ==\n                   SpherepackIterator::CoefficientArray::b) {\n          // ImRe\n          iter_src.set(l_src, static_cast<size_t>(-m_src),\n                       SpherepackIterator::CoefficientArray::a);\n          add_element(kj.imag() * coef_without_kj * sign_m_src);\n        }\n      }\n    }\n  }\n}\n\n// Inner loops of the rank-2 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\nvoid inner_loops_two(SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n                     SpherepackIterator& iter_dest, const size_t src_comp_index,\n                     const size_t dest_comp_index, const size_t ell_max,\n                     const size_t l_dest, const int m_dest, const size_t ltilde,\n                     const std::vector<int>& mtildes,\n                     std::vector<std::optional<WignerThreeJ>>& threej_mtildes,\n                     WignerThreeJ& threej_ells_stilde, const double symm_factor,\n                     const std::array<helpers::BasisVector, 2>& src_bvs,\n                     const int sign_ysb, std::vector<WignerThreeJ>& threej_uvs,\n                     const double threej_ones_stilde_val,\n                     const double ltilde_term, const int sign_stilde,\n                     const int sign_m_dest,\n                     const CoefficientNormalization coefficient_normalization) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, src_comp_index,\n                            dest_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  size_t mtilde_indx = 0;\n  for (int u = -1; u <= 1; u += 2) {\n    for (int v = -1; v <= 1; v += 2, ++mtilde_indx) {\n      if (static_cast<size_t>(std::abs(mtildes[mtilde_indx])) <= ltilde) {\n        const int m_src = m_dest - mtildes[mtilde_indx];\n        const auto sign_m =\n            helpers::sign_m<double>(m_src + m_dest, coefficient_normalization);\n        const int sign_m_src = (m_src % 2 == 0 ? 1 : -1);\n        const std::complex<double> kj =\n            helpers::bv_to_k(src_bvs[0], u) * helpers::bv_to_k(src_bvs[1], v);\n        for (size_t l_src = static_cast<size_t>(std::max(\n                 static_cast<size_t>(abs(m_src)), threej_ells_stilde.l1_min()));\n             (l_src <= threej_ells_stilde.l1_max() and l_src <= ell_max);\n             ++l_src) {\n          const double sqterm =\n              0.5 *\n              sqrt(static_cast<double>((2 * l_dest + 1) * (2 * l_src + 1)));\n          const double coef_without_kj =\n              sign_m * sign_ysb * ltilde_term * sqterm * sign_m_dest *\n              sign_stilde * threej_mtildes[mtilde_indx].value()(l_src) *\n              threej_uvs[mtilde_indx](ltilde) * threej_ells_stilde(l_src) *\n              threej_ones_stilde_val * symm_factor;\n\n          if (m_src >= 0) {\n            // Main term.\n            if (kj.imag() == 0) {  // This works because kj is either pure\n                                   // real or pure imaginary\n              if (iter_dest.coefficient_array() ==\n                  SpherepackIterator::CoefficientArray::a) {\n                // ReRe\n                iter_src.set(l_src, static_cast<size_t>(m_src),\n                             SpherepackIterator::CoefficientArray::a);\n                add_element(kj.real() * coef_without_kj);\n              } else if (iter_dest.coefficient_array() ==\n                         SpherepackIterator::CoefficientArray::b) {\n                // ImIm\n                iter_src.set(l_src, static_cast<size_t>(m_src),\n                             SpherepackIterator::CoefficientArray::b);\n                add_element(kj.real() * coef_without_kj);\n              }\n            } else {\n              if (iter_dest.coefficient_array() ==\n                  SpherepackIterator::CoefficientArray::a) {\n                // ReIm\n                iter_src.set(l_src, static_cast<size_t>(m_src),\n                             SpherepackIterator::CoefficientArray::b);\n                add_element(-kj.imag() * coef_without_kj);\n              } else if (iter_dest.coefficient_array() ==\n                         SpherepackIterator::CoefficientArray::b) {\n                // ImRe\n                iter_src.set(l_src, static_cast<size_t>(m_src),\n                             SpherepackIterator::CoefficientArray::a);\n                add_element(kj.imag() * coef_without_kj);\n              }\n            }\n          } else {\n            if (kj.imag() == 0) {\n              // Other term, for negative m_src.\n              if (iter_dest.coefficient_array() ==\n                  SpherepackIterator::CoefficientArray::a) {\n                // ReRe\n                iter_src.set(l_src, static_cast<size_t>(-m_src),\n                             SpherepackIterator::CoefficientArray::a);\n                add_element(kj.real() * coef_without_kj * sign_m_src);\n              } else if (iter_dest.coefficient_array() ==\n                         SpherepackIterator::CoefficientArray::b) {\n                // ImIm\n                iter_src.set(l_src, static_cast<size_t>(-m_src),\n                             SpherepackIterator::CoefficientArray::b);\n                add_element(-kj.real() * coef_without_kj * sign_m_src);\n              }\n            } else {\n              if (iter_dest.coefficient_array() ==\n                  SpherepackIterator::CoefficientArray::a) {\n                // ReIm\n                iter_src.set(l_src, static_cast<size_t>(-m_src),\n                             SpherepackIterator::CoefficientArray::b);\n                add_element(kj.imag() * coef_without_kj * sign_m_src);\n              } else if (iter_dest.coefficient_array() ==\n                         SpherepackIterator::CoefficientArray::b) {\n                // ImRe\n                iter_src.set(l_src, static_cast<size_t>(-m_src),\n                             SpherepackIterator::CoefficientArray::a);\n                add_element(kj.imag() * coef_without_kj * sign_m_src);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// Inner loops of the rank-3 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\ntemplate <typename Symm>\nvoid inner_loops_three(\n    SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n    SpherepackIterator& iter_dest, const size_t src_comp_index,\n    const size_t dest_comp_index, const size_t ell_max, const size_t l_dest,\n    const int m_dest, const size_t lhat, const int mhat, const int shat,\n    const int stilde, std::optional<WignerThreeJ>& threej_ones_stilde,\n    WignerThreeJ& threej_ells_stot, WignerThreeJ& threej_ems,\n    const std::vector<int>& mtildes,\n    const std::array<helpers::BasisVector, 3>& src_bvs,\n    const size_t src_multiplicity,\n    std::vector<std::optional<WignerThreeJ>>& threej_ells_stildes,\n    std::vector<WignerThreeJ>& threej_uvs,\n    std::vector<std::optional<WignerThreeJ>>& threej_ws, const int sign_ysb,\n    const int sign_m_dest,\n    const CoefficientNormalization coefficient_normalization) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, src_comp_index,\n                            dest_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  size_t mtilde_indx = 0;\n  for (int u = -1; u <= 1; u += 2) {\n    for (int v = -1; v <= 1; v += 2, ++mtilde_indx) {\n      const int sign_stilde =\n          ((stilde + shat + mtildes[mtilde_indx]) % 2 == 0 ? 1 : -1);\n      const std::complex<double> kj12 =\n          helpers::bv_to_k(src_bvs[1], u) * helpers::bv_to_k(src_bvs[2], v);\n      for (size_t ltilde = static_cast<size_t>(std::max(\n               std::abs(mtildes[mtilde_indx]), std::max(0, abs(stilde))));\n           ltilde <= 2; ++ltilde) {\n        const double threej_ones_stilde_val =\n            threej_ones_stilde.value()(ltilde);\n        const double threej_uv_val = threej_uvs[mtilde_indx](ltilde);\n        const int ltilde_term =\n            static_cast<int>((2 * ltilde + 1) * (2 * lhat + 1));\n        const double threej_ells_stilde_val =\n            threej_ells_stildes[ltilde].value()(lhat);\n\n        const double symm_factor =\n            helpers::get_symm_factor<Symm>(src_multiplicity, ltilde);\n        if (symm_factor != 0.0 and threej_ells_stilde_val != 0.0 and\n            threej_uv_val != 0.0 and threej_ones_stilde_val != 0.0) {\n          for (int w = -1; w <= 1; w += 2) {\n            const int mw = helpers::bv_to_m(src_bvs[0], w);\n            if (mhat == mtildes[mtilde_indx] - mw) {\n              const int m_src = m_dest - mhat;\n              const auto sign_m = helpers::sign_m<double>(\n                  m_src + m_dest, coefficient_normalization);\n              const int sign_m_src = (m_src % 2 == 0 ? 1 : -1);\n              const std::complex<double> kj =\n                  helpers::bv_to_k(src_bvs[0], w) * kj12;\n              const double threej_w_val =\n                  threej_ws[ltilde + 3 * static_cast<size_t>((w + 1) / 2) +\n                            6 * mtilde_indx]\n                      .value()(lhat);\n\n              for (size_t l_src =\n                       std::max(threej_ells_stot.l1_min(),\n                                std::max(static_cast<size_t>(std::abs(m_src)),\n                                         threej_ems.l1_min()));\n                   (l_src <= std::min(threej_ems.l1_max(),\n                                      threej_ells_stot.l1_max()) and\n                    l_src <= ell_max);\n                   ++l_src) {\n                const double threej_ells_stot_val = threej_ells_stot(l_src);\n                const double threej_ems_val = threej_ems(l_src);\n\n                const double sqterm = sqrt(\n                    static_cast<double>((2 * l_dest + 1) * (2 * l_src + 1)) /\n                    8.0);\n\n                const double coef_without_kj =\n                    sign_m * sign_ysb * sign_m_dest * sign_stilde *\n                    ltilde_term * sqterm * threej_uv_val *\n                    threej_ones_stilde_val * threej_w_val *\n                    threej_ells_stilde_val * threej_ells_stot_val *\n                    threej_ems_val * symm_factor;\n\n                if (m_src >= 0) {\n                  // Main term.\n                  if (kj.imag() == 0) {  // This works because kj is either\n                                         // pure real or pure imaginary\n                    if (iter_dest.coefficient_array() ==\n                        SpherepackIterator::CoefficientArray::a) {\n                      // ReRe\n                      iter_src.set(l_src, static_cast<size_t>(m_src),\n                                   SpherepackIterator::CoefficientArray::a);\n                      add_element(kj.real() * coef_without_kj);\n                    } else if (iter_dest.coefficient_array() ==\n                               SpherepackIterator::CoefficientArray::b) {\n                      // ImIm\n                      iter_src.set(l_src, static_cast<size_t>(m_src),\n                                   SpherepackIterator::CoefficientArray::b);\n                      add_element(kj.real() * coef_without_kj);\n                    }\n                  } else {\n                    if (iter_dest.coefficient_array() ==\n                        SpherepackIterator::CoefficientArray::a) {\n                      // ReIm\n                      iter_src.set(l_src, static_cast<size_t>(m_src),\n                                   SpherepackIterator::CoefficientArray::b);\n                      add_element(-kj.imag() * coef_without_kj);\n                    } else if (iter_dest.coefficient_array() ==\n                               SpherepackIterator::CoefficientArray::b) {\n                      // ImRe\n                      iter_src.set(l_src, static_cast<size_t>(m_src),\n                                   SpherepackIterator::CoefficientArray::a);\n                      add_element(kj.imag() * coef_without_kj);\n                    }\n                  }\n                } else {\n                  if (kj.imag() == 0) {\n                    // Other term, for negative m_src.\n                    if (iter_dest.coefficient_array() ==\n                        SpherepackIterator::CoefficientArray::a) {\n                      // ReRe\n                      iter_src.set(l_src, static_cast<size_t>(-m_src),\n                                   SpherepackIterator::CoefficientArray::a);\n                      add_element(kj.real() * coef_without_kj * sign_m_src);\n                    } else if (iter_dest.coefficient_array() ==\n                               SpherepackIterator::CoefficientArray::b) {\n                      // ImIm\n                      iter_src.set(l_src, static_cast<size_t>(-m_src),\n                                   SpherepackIterator::CoefficientArray::b);\n                      add_element(-kj.real() * coef_without_kj * sign_m_src);\n                    }\n                  } else {\n                    if (iter_dest.coefficient_array() ==\n                        SpherepackIterator::CoefficientArray::a) {\n                      // ReIm\n                      iter_src.set(l_src, static_cast<size_t>(-m_src),\n                                   SpherepackIterator::CoefficientArray::b);\n                      add_element(kj.imag() * coef_without_kj * sign_m_src);\n                    } else if (iter_dest.coefficient_array() ==\n                               SpherepackIterator::CoefficientArray::b) {\n                      // ImRe\n                      iter_src.set(l_src, static_cast<size_t>(-m_src),\n                                   SpherepackIterator::CoefficientArray::a);\n                      add_element(kj.imag() * coef_without_kj * sign_m_src);\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid fill_cart_to_sphere(\n    const gsl::not_null<SparseMatrixType*> matrix, const size_t ell_max,\n    const CoefficientNormalization coefficient_normalization) {\n  static constexpr size_t num_independent_components = TensorStructure::size();\n  static constexpr size_t rank = TensorStructure::rank();\n  static constexpr auto tensor_index_list =\n      TensorStructure::storage_to_tensor_index();\n\n  static_assert(rank > 0 and rank < 4, \"Implemented only for ranks 1,2,3\");\n\n  SpherepackIterator iter_src(ell_max, ell_max, 1, false);\n  SpherepackIterator iter_dest(ell_max, ell_max, 1, false);\n  SparseMatrixFiller filler(square(num_independent_components) *\n                                iter_src.spherepack_array_size() *\n                                iter_dest.spherepack_array_size(),\n                            true, 1.0);\n\n  // We allocate space for some (not all) of the 3J symbols here, so\n  // that they can be used and re-used later inside the inner loops.\n  std::vector<WignerThreeJ> threej_uvs;  // NOLINT(misc-const-correctness)\n  std::vector<int> mtildes;              // NOLINT(misc-const-correctness)\n  if constexpr (rank > 1) {\n    threej_uvs.reserve(4);\n    mtildes.reserve(4);\n  } else {\n    // For rank 1 we don't need threej_uvs but we need to declare them\n    // anyway for scoping; they will just be unused. We will compute\n    // the rank-1 3J symbols below, and below we will also compute\n    // additional 3J symbols for higher ranks.\n    (void)threej_uvs;\n    (void)mtildes;\n  }\n\n  // threej_ws contains std::optionals because some of the Wigner 3Js\n  // are not well-defined (because |m| > ell).  The ones without\n  // values are never used or referenced.  It is possible to compute\n  // fewer than 24 threej_ws, by pushing_back only those that have\n  // values, but then it is difficult to index the threej_ws.  So\n  // keeping 24 of them and making them std::optionals is easier.\n  //\n  // Same thing is true for threej_ells_stildes.\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<std::optional<WignerThreeJ>> threej_ws;\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<std::optional<WignerThreeJ>> threej_ells_stildes;\n  if constexpr (rank > 2) {\n    threej_ws.reserve(24);\n    threej_ells_stildes.reserve(3);\n  } else {\n    // Unneeded except for rank 3.\n    (void)threej_ws;\n    (void)threej_ells_stildes;\n  }\n\n  // Same thing for threej_mtildes, but for rank 2.\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<std::optional<WignerThreeJ>> threej_mtildes;\n  if constexpr (rank == 2) {\n    threej_mtildes.reserve(4);\n  } else {\n    // Unneeded except for rank 2.\n    (void)threej_mtildes;\n  }\n\n  for (size_t dest_comp_index = 0; dest_comp_index < num_independent_components;\n       dest_comp_index++) {\n    const auto dest_indices = tensor_index_list[dest_comp_index];\n    const auto dest_bvs = helpers::to_sphere_basis_vector(dest_indices);\n    const int sign_sb =\n        alg::accumulate(dest_bvs, 1, [](const double acc, const auto bv) {\n          return acc * (helpers::bv_to_s(bv) == -1 ? -1 : 1);\n        });\n\n    // threej_ones_stilde is a std::optional because it is\n    // defined/used only for rank > 1.\n    //\n    // threej_ones_stilde is the last 3J symbol in Eq. (21) and the\n    // fourth 3J symbol in Eq. (23)\n    // NOLINTNEXTLINE(misc-const-correctness)\n    int stilde = 0;\n    // NOLINTNEXTLINE(misc-const-correctness)\n    std::optional<WignerThreeJ> threej_ones_stilde;\n    if constexpr (rank > 1) {\n      stilde = helpers::bv_to_s(dest_bvs[rank - 1]) +\n               helpers::bv_to_s(dest_bvs[rank - 2]);\n      threej_ones_stilde =\n          WignerThreeJ(1, -helpers::bv_to_s(dest_bvs[rank - 2]), 1,\n                       -helpers::bv_to_s(dest_bvs[rank - 1]));\n    } else {\n      (void)stilde;\n      (void)threej_ones_stilde;\n    }\n\n    // shat is used only for rank 3.\n    const int shat = stilde + helpers::bv_to_s(dest_bvs[0]);\n\n    for (size_t src_comp_index = 0; src_comp_index < num_independent_components;\n         src_comp_index++) {\n      const auto src_indices = tensor_index_list[src_comp_index];\n      const auto src_bvs = helpers::to_cart_basis_vector(src_indices);\n      const size_t src_multiplicity =\n          TensorStructure::multiplicity(src_comp_index);\n\n      const int sign_y =\n          alg::accumulate(src_bvs, 1, [](const double acc, const auto bv) {\n            return acc * (bv == helpers::BasisVector::y ? -1 : 1);\n          });\n\n      // This is the overall factor of (-1)^bla in Eqs. (20), (21), and (22),\n      // where bla are the things that depend only on the tensor component\n      // (\\tilde(D) and B) and not on l,m.\n      const int sign_ysb = sign_y * sign_sb;\n\n      if constexpr (rank > 1) {\n        // threej_uvs is the penultimate 3J symbol in Eq. (21)\n        // and the third 3J symbol in Eq. (23)\n        threej_uvs.clear();\n        mtildes.clear();\n        for (int u = -1; u <= 1; u += 2) {\n          for (int v = -1; v <= 1; v += 2) {\n            mtildes.push_back(-(helpers::bv_to_m(src_bvs[rank - 2], u) +\n                                helpers::bv_to_m(src_bvs[rank - 1], v)));\n            threej_uvs.emplace_back(1, -helpers::bv_to_m(src_bvs[rank - 2], u),\n                                    1, -helpers::bv_to_m(src_bvs[rank - 1], v));\n          }\n        }\n      }\n\n      if constexpr (rank > 2) {\n        // threej_ws is the penultimate 3J symbol in Eq. (23)\n        threej_ws.clear();\n        threej_ells_stildes.clear();\n        for (const int mtilde : mtildes) {\n          for (int w = -1; w <= 1; w += 2) {\n            const int mw = helpers::bv_to_m(src_bvs[0], w);\n            for (size_t ltilde = 0; ltilde <= 2; ++ltilde) {\n              if (abs(mtilde) <= static_cast<int>(ltilde)) {\n                threej_ws.emplace_back(WignerThreeJ(1, -mw, ltilde, mtilde));\n              } else {\n                threej_ws.emplace_back(std::nullopt);\n              }\n            }\n          }\n        }\n        // threej_ells_stildes is the last 3J symbol in Eq. (23)\n        for (size_t ltilde = 0; ltilde <= 2; ++ltilde) {\n          if (static_cast<size_t>(abs(stilde)) <= ltilde) {\n            threej_ells_stildes.emplace_back(WignerThreeJ(\n                1, -helpers::bv_to_s(dest_bvs[0]), ltilde, -stilde));\n          } else {\n            threej_ells_stildes.emplace_back(std::nullopt);\n          }\n        }\n      }\n\n      for (iter_dest.reset(); iter_dest; ++iter_dest) {\n        const auto l_dest = static_cast<size_t>(iter_dest.l());\n        const auto m_dest = static_cast<int>(iter_dest.m());\n        const int sign_m_dest = (m_dest % 2 == 0 ? 1 : -1);\n        if constexpr (rank == 1) {\n          (void)sign_m_dest;\n          (void)src_multiplicity;\n          const int sb = helpers::bv_to_s(dest_bvs[0]);\n          if (static_cast<size_t>(std::abs(sb)) <= l_dest) {\n            // threej_s is the first 3j symbol in Eq. (20)\n            WignerThreeJ threej_s(l_dest, sb, 1, -sb);\n            for (int j = -1; j <= 1; j += 2) {\n              const int mj = helpers::bv_to_m(src_bvs[0], j);\n              // threej_m is the second 3j symbol in Eq. (20)\n              WignerThreeJ threej_m(l_dest, -m_dest, 1, -mj);\n              const std::complex<double> kj = helpers::bv_to_k(src_bvs[0], j);\n              inner_loops_one(filler, iter_src, iter_dest, src_comp_index,\n                              dest_comp_index, ell_max, l_dest, m_dest, mj, kj,\n                              sign_ysb, threej_m, threej_s,\n                              coefficient_normalization);\n            }\n          }\n        } else if constexpr (rank == 2) {\n          const int sign_stilde = (stilde % 2 == 0 ? 1 : -1);\n          for (size_t ltilde = 0; ltilde <= 2; ++ltilde) {\n            const double symm_factor =\n                helpers::get_symm_factor<typename TensorStructure::symmetry>(\n                    src_multiplicity, ltilde);\n            if (symm_factor != 0.0 and\n                static_cast<size_t>(std::abs(stilde)) <= ltilde and\n                static_cast<size_t>(std::abs(stilde)) <= l_dest) {\n              const double threej_ones_stilde_val =\n                  threej_ones_stilde.value()(ltilde);\n              const auto ltilde_term = static_cast<double>(2 * ltilde + 1);\n              // threej_mtildes is the first 3J symbol in Eq. (21)\n              threej_mtildes.clear();\n              for (const int mtilde : mtildes) {\n                if (static_cast<size_t>(std::abs(mtilde)) <= ltilde) {\n                  threej_mtildes.emplace_back(\n                      WignerThreeJ(l_dest, -m_dest, ltilde, mtilde));\n                } else {\n                  threej_mtildes.emplace_back(std::nullopt);\n                }\n              }\n              // threej_ells_stilde is the second 3J symbol in Eq. (21)\n              WignerThreeJ threej_ells_stilde(l_dest, stilde, ltilde, -stilde);\n              inner_loops_two(filler, iter_src, iter_dest, src_comp_index,\n                              dest_comp_index, ell_max, l_dest, m_dest, ltilde,\n                              mtildes, threej_mtildes, threej_ells_stilde,\n                              symm_factor, src_bvs, sign_ysb, threej_uvs,\n                              threej_ones_stilde_val, ltilde_term, sign_stilde,\n                              sign_m_dest, coefficient_normalization);\n            }\n          }\n        } else if constexpr (rank == 3) {\n          const int stot = helpers::bv_to_s(dest_bvs[0]) +\n                           helpers::bv_to_s(dest_bvs[1]) +\n                           helpers::bv_to_s(dest_bvs[2]);\n          if (static_cast<size_t>(std::abs(stot)) <= l_dest) {\n            for (size_t lhat = static_cast<size_t>(std::max(0, std::abs(shat)));\n                 lhat <= 3; ++lhat) {\n              // threej_ells_stot is the second 3J symbol in Eq. (23)\n              WignerThreeJ threej_ells_stot(lhat, -shat, l_dest, stot);\n              for (int mhat = -static_cast<int>(lhat);\n                   mhat <= static_cast<int>(lhat); ++mhat) {\n                // threej_ems is the first 3J symbol in Eq. (23)\n                WignerThreeJ threej_ems(lhat, mhat, l_dest, -m_dest);\n                inner_loops_three<typename TensorStructure::symmetry>(\n                    filler, iter_src, iter_dest, src_comp_index,\n                    dest_comp_index, ell_max, l_dest, m_dest, lhat, mhat, shat,\n                    stilde, threej_ones_stilde, threej_ells_stot, threej_ems,\n                    mtildes, src_bvs, src_multiplicity, threej_ells_stildes,\n                    threej_uvs, threej_ws, sign_ysb, sign_m_dest,\n                    coefficient_normalization);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n  filler.fill(matrix);\n}\n\n// Explicit instantiations\n#define TSTRUCT(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template void fill_cart_to_sphere<typename TSTRUCT(data) < DataVector, \\\n                                    3>::structure >                      \\\n      (gsl::not_null<SimpleSparseMatrix*> matrix, size_t ell_max,        \\\n       CoefficientNormalization coefficient_normalization);              \\\n  template void fill_cart_to_sphere<typename TSTRUCT(data) < DataVector, \\\n                                    3>::structure >                      \\\n      (gsl::not_null<blaze::CompressedMatrix<double, blaze::rowMajor>*>  \\\n           matrix,                                                       \\\n       size_t ell_max, CoefficientNormalization coefficient_normalization);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (tnsr::i, tnsr::ii, tnsr::ij, tnsr::ijk, tnsr::ijj))\n\n#undef INSTANTIATE\n#undef TSTRUCT\n\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlmCartToSphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp\"\n\n#include <cstddef>\n\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm::TensorYlm {\n\n/*!\n * \\brief Fills a sparse matrix that does a TensorYlm Cartesian\n * to Spherical operation.\n *\n * If the CoefficientNormalization parameter is set to Standard, then\n * assumes that the input, $T^{\\tilde A}_{\\ell' m'}$, is stored in a\n * Tensor<DataVector>.  Multiplying the resulting\n * sparse matrix by the Tensor<DataVector> is equivalent to\n * evaluating the right-hand side of Eq. $(\\ref{eq:C2S})$.\n * If the CoefficientNormalization parameter is set to Spherepack, then\n * assumes the matrix will multiply a Tensor<DataVector> containing\n * ${\\breve T}^{\\tilde A}_{\\ell' m'}$ and the transform operation is\n * equivalent to Eq. $(\\ref{eq:C2SSpherepack})$.\n *\n * We assume that the independent components of the Tensor<DataVector>\n * are stored contiguously in memory, in order of the `storage_index` of\n * the Tensor.  However, we do allow for a stride.  This means that we\n * can point to memory starting with the first element of\n * $T^{\\tilde A}_{\\ell' m'}$ and multiply that by the sparse matrix we compute\n * here, and get the result.\n *\n * The memory layout here is different than in SpEC.  In SpEC, each\n * tensor component is stored in separately-allocated memory, so the\n * SpEC equivalent of the fill_cart_to_sphere function fills $N^2$ sparse\n * matrices, where $N$ is the number of independent components of the\n * Tensor. The advantage of the SpEC method is that each sparse matrix\n * is smaller, so sorting elements into the correct order while\n * constructing each sparse matrix is faster (sorting is > linear in\n * the number of matrix elements).  The disadvantage of the SpEC\n * method is that evaluating the coefficients for a single Tensor involves\n * $N^2$ matrix-vector multiplications, whereas here evaluating the\n * coefficients involves only one matrix-vector multiplication, which should\n * have more efficient memory access.  It is not clear which method is\n * faster overall without more profiling.\n *\n * ## Explicit formulas\n *\n * The following formulas come from Klinger and Scheel, in prep.\n *\n * For rank-1 tensors, the expression for\n * $C_{\\ell' m'\\tilde{D}}^{\\ell'' m'' B}$ is\n * \\begin{align}\n *   C_{\\ell' m'\\tilde{D}}^{\\ell'' m'' B} &=\n *  \\sqrt{\\frac{(2\\ell'+1)(2\\ell''+1)}{2}}\n *  (-1)^{m''} (-1)^{\\delta(\\tilde{D},\\mathbf{e}_y)} (-1)^{\\delta(s_B,-1)}\n *    \\left(\\begin{array}{ccc}\n *     \\ell' & \\ell'' & 1 \\cr\n *      s_B & 0 & -s_B\n *    \\end{array}\\right)\n *  \\nonumber \\\\\n *  &\\times \\sum_j k_j(\\tilde{D})\n *    \\left(\\begin{array}{ccc}\n *     \\ell' & \\ell'' & 1 \\cr\n *      -m' & m'' & -m_j(\\tilde{D})\n *    \\end{array}\\right),\n * \\end{align}\n * where the 6-element \"matrices\"\n * in parentheses are Wigner 3-J symbols.\n *\n * For second-rank tensors, the expression for\n * $C_{\\ell' m'\\tilde{D}}^{\\ell'' m'' B}$ is\n * \\begin{align}\n *  C_{\\ell' m'\\tilde{D}}^{\\ell'' m'' B}\n *      &=\n *    \\frac{1}{2}(-1)^{\\delta(s_{B_1},-1)}(-1)^{\\delta(s_{B_2},-1)}(-1)^{m'}\n *    (-1)^{\\delta(\\tilde{D}_1,\\mathbf{e}_y)}\n *    (-1)^{\\delta(\\tilde{D}_2,\\mathbf{e}_y)}\n *    \\sqrt{(2\\ell''+1)(2\\ell'+1)}\n *    \\nonumber \\\\\n *    &\\times\n *    \\sum_{u,v,\\tilde{\\ell},\\tilde{m},\\tilde{s}}\n *    (2 \\tilde{\\ell}+1) (-1)^{\\tilde{s}} S(\\tilde{\\ell},\\tilde{D})\n *    k_u(\\tilde{D}_1) k_v(\\tilde{D}_2)\n *    \\left(\\begin{array}{ccc}\n *     \\ell' & \\ell'' & \\tilde{\\ell} \\cr\n *      -m' & m'' & \\tilde{m}\n *    \\end{array}\\right)\n *    \\nonumber \\\\\n *    &\\qquad\\times\n *    \\left(\\begin{array}{ccc}\n *     \\ell' & \\ell'' & \\tilde{\\ell} \\cr\n *      s_{B_1}+s_{B_2} & 0 & -\\tilde{s}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{ccc}\n *      1 & 1 & \\tilde{\\ell} \\cr\n *      -m_u(\\tilde{D}_1)&-m_v(\\tilde{D}_2)&-\\tilde{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{ccc}\n *      1 & 1 & \\tilde{\\ell} \\cr\n *      -s_{B_1}&-s_{B_2}&\\tilde{s}\n *    \\end{array}\\right),\n * \\end{align}\n * where the factor $S(\\tilde{\\ell},\\tilde{D})$ is a symmetry factor.\n * For a 2nd-rank tensor with no symmetries, $S(\\tilde{\\ell},\\tilde{D})$ is\n * unity, but for a tensor symmetric in $(\\tilde{D_1},\\tilde{D_2})$\n * the matrix elements $C_{\\ell' m'\\tilde{D}}^{\\ell'' m'' B}$ are\n * multiplied only by tensor components with $\\tilde{D_1}\\geq\\tilde{D_2}$\n * and the symmetry is accounted for by setting\n * \\begin{align}\n *   S(\\tilde{\\ell},\\tilde{D}) &=\n *   (1+(-1)^{\\tilde{\\ell}})\\frac{2-\\delta(\\tilde{D_1},\\tilde{D_2})}{2}.\n * \\end{align}\n *\n * For third-rank tensors, the expression for\n * $C_{\\ell' m'\\tilde{D}}^{\\ell'' m'' B}$ is\n * \\begin{align}\n *   C_{\\ell' m'\\tilde{D}}^{\\ell'' m'' B}\n *     &=\n *        (-1)^{m'}\n *        (-1)^{\\delta(s_{B_1},-1)}\n *        (-1)^{\\delta(s_{B_2},-1)}\n *        (-1)^{\\delta(s_{B_3},-1)}\n *     (-1)^{\\delta(\\tilde{D}_1,\\mathbf{e}_y)}\n *     (-1)^{\\delta(\\tilde{D}_2,\\mathbf{e}_y)}\n *     (-1)^{\\delta(\\tilde{D}_3,\\mathbf{e}_y)}\n *     \\nonumber \\\\\n *     &\\times\n *     \\sqrt{\\frac{(2\\ell''+1)(2\\ell'+1)}{8}}\n *     \\sum_{u,v,w,\\tilde{\\ell},\\tilde{m},\\tilde{s},\\hat{\\ell},\\hat{m},\\hat{s}}\n *     (2 \\tilde{\\ell}+1) (2 \\hat{\\ell}+1) (-1)^{\\tilde{s}+\\hat{s}+\\tilde{m}}\n *     k_u(\\tilde{D}_2) k_v(\\tilde{D}_3) k_w(\\tilde{D}_1)\n *     \\nonumber \\\\\n *     &\\qquad\\times\n *    S(\\tilde{\\ell},\\tilde{D})\n *    \\left(\\begin{array}{ccc}\n *     \\ell' & \\ell'' & \\hat{\\ell} \\cr\n *      -m' & m'' & \\hat{m}\n *    \\end{array}\\right)\n *     \\left(\\begin{array}{ccc}\n *     \\ell' & \\ell'' & \\hat{\\ell} \\cr\n *      s_{B_1}+s_{B_2}+s_{B_3} & 0 & -\\hat{s}\n *    \\end{array}\\right)\n *     \\nonumber \\\\\n *     &\\qquad\\times\n *    \\left(\\begin{array}{ccc}\n *      1 & 1 & \\tilde{\\ell} \\cr\n *      -m_u(\\tilde{D}_2)&-m_v(\\tilde{D}_3)&-\\tilde{m}\n *    \\end{array}\\right)\n *     \\left(\\begin{array}{ccc}\n *      1 & 1 & \\tilde{\\ell} \\cr\n *      -s_{B_2}&-s_{B_3}&\\tilde{s}\n *     \\end{array}\\right)\n *     \\nonumber \\\\\n *     &\\qquad\\times\n *     \\left(\\begin{array}{ccc}\n *      1 & \\tilde{\\ell} & \\hat{\\ell} \\cr\n *      -m_w(\\tilde{D}_1)&\\tilde{m}&-\\hat{m}\n *     \\end{array}\\right)\n *     \\left(\\begin{array}{ccc}\n *      1 & \\tilde{\\ell} & \\hat{\\ell} \\cr\n *      -s_{B_1}&-\\tilde{s}&\\hat{s}\n *     \\end{array}\\right).\n * \\end{align}\n * As in the rank-2 case,\n * $S(\\tilde{\\ell},\\tilde{D})$ is a symmetry factor that is unity\n * for a tensor with no symmetries and is\n * \\begin{align}\n *   S(\\tilde{\\ell},\\tilde{D}) &=\n *   (1+(-1)^{\\tilde{\\ell}})\\frac{2-\\delta(\\tilde{D_2},\\tilde{D_3})}{2}\n * \\end{align}\n * for a tensor symmetric on its last two indices. We don't consider\n * other symmetries because we don't find them in the cases we care\n * about.\n *\n * \\tparam TensorStructure A Tensor_detail::Structure\n * \\tparam SparseMatrixType A sparse matrix fillable by SparseMatrixFiller\n *\n * \\param matrix The sparse matrix to fill\n * \\param ell_max The maximum ylm ell value\n * \\param coefficient_normalization Describes the normalization of coefficients.\n *\n */\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid fill_cart_to_sphere(gsl::not_null<SparseMatrixType*> matrix,\n                         size_t ell_max,\n                         CoefficientNormalization coefficient_normalization);\n\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlmFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmFilter.hpp\"\n\n#include <blaze/math/CompressedMatrix.h>\n#include <complex>\n#include <optional>\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"DataStructures/SparseMatrixFiller.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/WignerThreeJ.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace ylm::TensorYlm {\n\nnamespace {\n\n// Inner loops of the rank-1 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\nvoid inner_loops_one(SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n                     SpherepackIterator& iter_dest, const size_t src_comp_index,\n                     const size_t dest_comp_index, const size_t ell_max,\n                     const int mdest, const int msrc,\n                     const std::complex<double>& coefjp, WignerThreeJ& threej_j,\n                     WignerThreeJ& threej_p) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, dest_comp_index,\n                            src_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  for (size_t ell = std::max(\n           {static_cast<size_t>(mdest), threej_j.l1_min(), threej_p.l1_min()});\n       ell <= std::min({ell_max, threej_j.l1_max(), threej_p.l1_max()});\n       ++ell) {\n    const std::complex<double> correction =\n        threej_j(ell) * threej_p(ell) * coefjp;\n    if (msrc > 0) {\n      // Main term, i.e. first term in Eq. (18).\n      if (correction.imag() == 0.0) {\n        // ReRe\n        iter_src.set(ell, static_cast<size_t>(msrc),\n                     SpherepackIterator::CoefficientArray::a);\n        iter_dest.set(ell, static_cast<size_t>(mdest),\n                      SpherepackIterator::CoefficientArray::a);\n        add_element(correction.real());\n\n        // ImIm\n        iter_src.set(ell, static_cast<size_t>(msrc),\n                     SpherepackIterator::CoefficientArray::b);\n        iter_dest.set(ell, static_cast<size_t>(mdest),\n                      SpherepackIterator::CoefficientArray::b);\n        add_element(correction.real());\n      } else {\n        // ReIm\n        iter_src.set(ell, static_cast<size_t>(msrc),\n                     SpherepackIterator::CoefficientArray::b);\n        iter_dest.set(ell, static_cast<size_t>(mdest),\n                      SpherepackIterator::CoefficientArray::a);\n        add_element(-correction.imag());\n\n        // ImRe\n        iter_src.set(ell, static_cast<size_t>(msrc),\n                     SpherepackIterator::CoefficientArray::a);\n        iter_dest.set(ell, static_cast<size_t>(mdest),\n                      SpherepackIterator::CoefficientArray::b);\n        add_element(correction.imag());\n      }\n    } else {\n      // Second term in Eq. (18).\n      const double sign = (msrc % 2 == 0 ? 1.0 : -1.0);\n      if (correction.imag() == 0.0) {\n        // ReRe\n        iter_src.set(ell, static_cast<size_t>(-msrc),\n                     SpherepackIterator::CoefficientArray::a);\n        iter_dest.set(ell, static_cast<size_t>(mdest),\n                      SpherepackIterator::CoefficientArray::a);\n        add_element(sign * correction.real());\n\n        // ImIm\n        iter_src.set(ell, static_cast<size_t>(-msrc),\n                     SpherepackIterator::CoefficientArray::b);\n        iter_dest.set(ell, static_cast<size_t>(mdest),\n                      SpherepackIterator::CoefficientArray::b);\n        add_element(-sign * correction.real());\n      } else {\n        // ReIm\n        iter_src.set(ell, static_cast<size_t>(-msrc),\n                     SpherepackIterator::CoefficientArray::b);\n        iter_dest.set(ell, static_cast<size_t>(mdest),\n                      SpherepackIterator::CoefficientArray::a);\n        add_element(sign * correction.imag());\n\n        // ImRe\n        iter_src.set(ell, static_cast<size_t>(-msrc),\n                     SpherepackIterator::CoefficientArray::a);\n        iter_dest.set(ell, static_cast<size_t>(mdest),\n                      SpherepackIterator::CoefficientArray::b);\n        add_element(sign * correction.imag());\n      }\n    }\n  }\n}\n\n// Inner loops of the rank-2 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\nvoid inner_loops_two(SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n                     SpherepackIterator& iter_dest, const size_t src_comp_index,\n                     const size_t dest_comp_index, const size_t ell_max,\n                     const size_t lprime, const int mprime, const size_t lbar,\n                     const int mbar, const int mtilde, const double coeflbar,\n                     const double coeflprime, WignerThreeJ& threej_lbar,\n                     WignerThreeJ& threej_ltilde, const std::vector<int>& mbars,\n                     const std::vector<int>& mtildes,\n                     const std::array<helpers::BasisVector, 2>& src_bvs,\n                     const std::array<helpers::BasisVector, 2>& dest_bvs,\n                     const double symm_factor, const double sign_y,\n                     std::vector<WignerThreeJ>& threej_pqs,\n                     std::vector<WignerThreeJ>& threej_uvs,\n                     const CoefficientNormalization coefficient_normalization) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, src_comp_index,\n                            dest_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  size_t mbar_indx = 0;\n  const int m_dest = mprime + mbar;\n  for (int p = -1; p <= 1; p += 2) {\n    for (int q = -1; q <= 1; q += 2, ++mbar_indx) {\n      if (mbar == mbars[mbar_indx]) {\n        const double threej_pq_val = threej_pqs[mbar_indx](lbar);\n        if (threej_pq_val != 0.0) {\n          size_t mtilde_indx = 0;\n          for (int u = -1; u <= 1; u += 2) {\n            for (int v = -1; v <= 1; v += 2, ++mtilde_indx) {\n              if (mtilde == mtildes[mtilde_indx]) {\n                const double threej_uv_val = threej_uvs[mtilde_indx](lbar);\n                if (threej_uv_val != 0.0) {\n                  const std::complex<double> k_coefs =\n                      helpers::bv_to_k(src_bvs[0], u) *\n                      helpers::bv_to_k(dest_bvs[0], p) *\n                      helpers::bv_to_k(src_bvs[1], v) *\n                      helpers::bv_to_k(dest_bvs[1], q);\n                  const int m_src = mprime - mtilde;\n                  const auto sign_m = helpers::sign_m<double>(\n                      m_src + m_dest, coefficient_normalization);\n                  const std::complex<double> coef3j =\n                      -coeflprime * coeflbar * sign_y * sign_m * k_coefs;\n                  for (size_t l_dest = std::max(\n                           std::max(static_cast<size_t>(\n                                        abs(static_cast<int>(lprime) -\n                                            static_cast<int>(lbar))),\n                                    static_cast<size_t>(abs(mprime + mbar))),\n                           static_cast<size_t>(\n                               std::max(abs(mtilde - mprime), m_dest)));\n                       l_dest <= std::min(lprime + lbar, ell_max); ++l_dest) {\n                    const double threej_lbar_val = threej_lbar(l_dest);\n                    const double threej_ltilde_val = threej_ltilde(l_dest);\n                    if (threej_lbar_val != 0.0 and threej_ltilde_val != 0.0) {\n                      const double sign_lbar =\n                          ((lprime + l_dest + lbar) % 2 == 0 ? 1.0 : -1.0);\n                      // Corrects for permutation of columns in\n                      // threej_lba\n                      const std::complex<double> correction =\n                          sign_lbar * threej_pq_val * threej_uv_val *\n                          threej_lbar_val * threej_ltilde_val * symm_factor *\n                          coef3j;\n                      if (m_src > 0) {\n                        // Main term, first term in Eq. (18)\n                        if (correction.imag() == 0.0) {\n                          // ReRe\n                          iter_src.set(l_dest, static_cast<size_t>(m_src),\n                                       SpherepackIterator::CoefficientArray::a);\n                          iter_dest.set(\n                              l_dest, static_cast<size_t>(m_dest),\n                              SpherepackIterator::CoefficientArray::a);\n                          add_element(correction.real());\n\n                          // ImIm\n                          iter_src.set(l_dest, static_cast<size_t>(m_src),\n                                       SpherepackIterator::CoefficientArray::b);\n                          iter_dest.set(\n                              l_dest, static_cast<size_t>(m_dest),\n                              SpherepackIterator::CoefficientArray::b);\n                          add_element(correction.real());\n                        } else {\n                          // ReIm\n                          iter_src.set(l_dest, static_cast<size_t>(m_src),\n                                       SpherepackIterator::CoefficientArray::b);\n                          iter_dest.set(\n                              l_dest, static_cast<size_t>(m_dest),\n                              SpherepackIterator::CoefficientArray::a);\n                          add_element(-correction.imag());\n\n                          // ImRe\n                          iter_src.set(l_dest, static_cast<size_t>(m_src),\n                                       SpherepackIterator::CoefficientArray::a);\n                          iter_dest.set(\n                              l_dest, static_cast<size_t>(m_dest),\n                              SpherepackIterator::CoefficientArray::b);\n                          add_element(correction.imag());\n                        }\n                      } else {\n                        // Second term in Eq. (18)\n                        const double sign = (m_src % 2 == 0 ? 1.0 : -1.0);\n                        if (correction.imag() == 0.0) {\n                          // ReRe\n                          iter_src.set(l_dest, static_cast<size_t>(-m_src),\n                                       SpherepackIterator::CoefficientArray::a);\n                          iter_dest.set(\n                              l_dest, static_cast<size_t>(m_dest),\n                              SpherepackIterator::CoefficientArray::a);\n                          add_element(sign * correction.real());\n\n                          // ImIm\n                          iter_src.set(l_dest, static_cast<size_t>(-m_src),\n                                       SpherepackIterator::CoefficientArray::b);\n                          iter_dest.set(\n                              l_dest, static_cast<size_t>(m_dest),\n                              SpherepackIterator::CoefficientArray::b);\n                          add_element(-sign * correction.real());\n                        } else {\n                          // ReIm\n                          iter_src.set(l_dest, static_cast<size_t>(-m_src),\n                                       SpherepackIterator::CoefficientArray::b);\n                          iter_dest.set(\n                              l_dest, static_cast<size_t>(m_dest),\n                              SpherepackIterator::CoefficientArray::a);\n                          add_element(sign * correction.imag());\n\n                          // ImRe\n                          iter_src.set(l_dest, static_cast<size_t>(-m_src),\n                                       SpherepackIterator::CoefficientArray::a);\n                          iter_dest.set(\n                              l_dest, static_cast<size_t>(m_dest),\n                              SpherepackIterator::CoefficientArray::b);\n                          add_element(sign * correction.imag());\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// Inner loops of the rank-3 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\ntemplate <typename Symm>\nvoid inner_loops_three(\n    SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n    SpherepackIterator& iter_dest, const size_t src_comp_index,\n    const size_t dest_comp_index, const size_t ell_max, const size_t lprime,\n    const double coeflprime, const int mprime, const size_t lhat,\n    const int mhat, WignerThreeJ& threej_mhat, const int mcheck,\n    WignerThreeJ& threej_mcheck, const size_t mbar_indx, const int p,\n    const int q, const int r, const int mr, const std::vector<int>& mbars,\n    const std::vector<int>& mtildes,\n    const std::array<helpers::BasisVector, 3>& src_bvs,\n    const std::array<helpers::BasisVector, 3>& dest_bvs,\n    const size_t src_multiplicity, std::vector<WignerThreeJ>& threej_pqs,\n    std::vector<WignerThreeJ>& threej_uvs,\n    std::vector<std::optional<WignerThreeJ>>& threej_ws,\n    std::vector<std::optional<WignerThreeJ>>& threej_rs, const double sign_y,\n    const CoefficientNormalization coefficient_normalization) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, src_comp_index,\n                            dest_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  const int m_dest = mprime + mcheck;\n  size_t mtilde_indx = 0;\n  for (int u = -1; u <= 1; u += 2) {\n    for (int v = -1; v <= 1; v += 2, ++mtilde_indx) {\n      for (size_t lbar = static_cast<size_t>(\n               std::max(abs(mbars[mbar_indx]), abs(mtildes[mtilde_indx])));\n           lbar <= 2; ++lbar) {\n        const double symm_factor =\n            helpers::get_symm_factor<Symm>(src_multiplicity, lbar);\n        if (symm_factor != 0.0) {\n          // The division inside the index of the following\n          // quantities is integer division.  Note that\n          // q,v,r,w,v are always odd.\n          const double threej_pq =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              threej_pqs[static_cast<size_t>((q + 1) / 2 + p + 1)](lbar);\n          const double threej_r =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              threej_rs[static_cast<size_t>(static_cast<int>(lbar) +\n                                            (r + 1) * 3 / 2 +\n                                            6 * ((q + 1) / 2 + p + 1))]\n                  .value()(lhat);\n          const double threej_uv =\n              // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n              threej_uvs[static_cast<size_t>((v + 1) / 2 + u + 1)](lbar);\n          if (threej_r != 0.0 and threej_uv != 0.0 and threej_pq != 0.0) {\n            for (int w = -1; w <= 1; w += 2) {\n              const int mw = helpers::bv_to_m(src_bvs[0], w);\n              if (mtildes[mtilde_indx] - mw == mhat and\n                  lhat >= static_cast<size_t>(std::max(\n                              abs(static_cast<int>(lbar) - 1),\n                              std::max(abs(mr + mbars[mbar_indx]),\n                                       abs(mw - mtildes[mtilde_indx])))) and\n                  lhat <= lbar + 1) {\n                const int m_src = mprime - mhat;\n                const auto sign_m = helpers::sign_m<double>(\n                    m_src + m_dest, coefficient_normalization);\n                const double sign_mtilde =\n                    sign_m * ((mtildes[mtilde_indx] - mbars[mbar_indx]) % 2 == 0\n                                  ? 1.0\n                                  : -1.0);\n                const double coeflhat = 0.5 * static_cast<double>(2 * lhat + 1);\n                const double coeflbar = 0.5 * static_cast<double>(2 * lbar + 1);\n                const std::complex<double> k_coefs =\n                    helpers::bv_to_k(src_bvs[1], u) *\n                    helpers::bv_to_k(dest_bvs[1], p) *\n                    helpers::bv_to_k(src_bvs[2], v) *\n                    helpers::bv_to_k(dest_bvs[2], q) *\n                    helpers::bv_to_k(dest_bvs[0], r) *\n                    helpers::bv_to_k(src_bvs[0], w);\n                const std::complex<double> coef3j =\n                    -coeflprime * coeflbar * coeflhat * sign_y * k_coefs;\n                for (size_t l_dest = static_cast<size_t>(std::max(\n                         {abs(static_cast<int>(lprime) -\n                              static_cast<int>(lhat)),\n                          abs(mhat - mprime), abs(m_dest), mcheck + mprime}));\n                     l_dest <= std::min(lprime + lhat, ell_max); ++l_dest) {\n                  const double threej_mhat_val = threej_mhat(l_dest);\n                  const double threej_mcheck_val = threej_mcheck(l_dest);\n                  const double threej_w =\n                      // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n                      threej_ws[static_cast<size_t>(static_cast<int>(lbar) +\n                                                    (w + 1) * 3 / 2 +\n                                                    6 * ((v + 1) / 2 + u + 1))]\n                          .value()(lhat);\n                  if (threej_mhat_val != 0.0 and threej_mcheck_val != 0.0 and\n                      threej_w != 0.0) {\n                    const double sign_lhat =\n                        ((lprime + l_dest + lhat) % 2 == 0 ? 1.0 : -1.0);\n                    const std::complex<double> correction =\n                        coef3j * threej_pq * threej_uv * threej_r * threej_w *\n                        threej_mhat_val * threej_mcheck_val * sign_lhat *\n                        sign_mtilde * symm_factor;\n                    if (m_src > 0) {\n                      // Main term, first term in Eq. (18)\n                      // ReRe\n                      if (correction.imag() == 0) {\n                        iter_src.set(l_dest, static_cast<size_t>(m_src),\n                                     SpherepackIterator::CoefficientArray::a);\n                        iter_dest.set(l_dest, static_cast<size_t>(m_dest),\n                                      SpherepackIterator::CoefficientArray::a);\n                        add_element(correction.real());\n\n                        // ImIm\n                        iter_src.set(l_dest, static_cast<size_t>(m_src),\n                                     SpherepackIterator::CoefficientArray::b);\n                        iter_dest.set(l_dest, static_cast<size_t>(m_dest),\n                                      SpherepackIterator::CoefficientArray::b);\n                        add_element(correction.real());\n                      } else {\n                        // ReIm\n                        iter_src.set(l_dest, static_cast<size_t>(m_src),\n                                     SpherepackIterator::CoefficientArray::b);\n                        iter_dest.set(l_dest, static_cast<size_t>(m_dest),\n                                      SpherepackIterator::CoefficientArray::a);\n                        add_element(-correction.imag());\n\n                        // ImRe\n                        iter_src.set(l_dest, static_cast<size_t>(m_src),\n                                     SpherepackIterator::CoefficientArray::a);\n                        iter_dest.set(l_dest, static_cast<size_t>(m_dest),\n                                      SpherepackIterator::CoefficientArray::b);\n                        add_element(correction.imag());\n                      }\n                    } else {\n                      // Second term in Eq. (18)\n                      const double sign = (m_src % 2 == 0 ? 1.0 : -1.0);\n                      if (correction.imag() == 0) {\n                        // ReRe\n                        iter_src.set(l_dest, static_cast<size_t>(-m_src),\n                                     SpherepackIterator::CoefficientArray::a);\n                        iter_dest.set(l_dest, static_cast<size_t>(m_dest),\n                                      SpherepackIterator::CoefficientArray::a);\n                        add_element(sign * correction.real());\n\n                        // ImIm\n                        iter_src.set(l_dest, static_cast<size_t>(-m_src),\n                                     SpherepackIterator::CoefficientArray::b);\n                        iter_dest.set(l_dest, static_cast<size_t>(m_dest),\n                                      SpherepackIterator::CoefficientArray::b);\n                        add_element(-sign * correction.real());\n                      } else {\n                        // ReIm\n                        iter_src.set(l_dest, static_cast<size_t>(-m_src),\n                                     SpherepackIterator::CoefficientArray::b);\n                        iter_dest.set(l_dest, static_cast<size_t>(m_dest),\n                                      SpherepackIterator::CoefficientArray::a);\n                        add_element(sign * correction.imag());\n\n                        // ImRe\n                        iter_src.set(l_dest, static_cast<size_t>(-m_src),\n                                     SpherepackIterator::CoefficientArray::a);\n                        iter_dest.set(l_dest, static_cast<size_t>(m_dest),\n                                      SpherepackIterator::CoefficientArray::b);\n                        add_element(sign * correction.imag());\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid fill_filter(const gsl::not_null<SparseMatrixType*> matrix,\n                 const size_t ell_max, const size_t number_of_ell_modes_to_kill,\n                 const std::optional<size_t> half_power,\n                 const CoefficientNormalization coefficient_normalization) {\n  static constexpr size_t num_independent_components = TensorStructure::size();\n  static constexpr size_t rank = TensorStructure::rank();\n  static constexpr double alpha = 36.0;\n\n  static_assert(rank >= 0 and rank < 4, \"Implemented only for ranks 0,1,2,3\");\n\n  const auto lcutplus =\n      static_cast<size_t>(ell_max - number_of_ell_modes_to_kill);\n  const size_t lcutminus =\n      half_power.has_value()\n          ? size_t(std::ceil(static_cast<double>(lcutplus + 1) *\n                             pow(std::numeric_limits<double>::epsilon() / alpha,\n                                 1.0 / (2.0 * double(half_power.value())))))\n          : lcutplus + 1;\n\n  SpherepackIterator iter_src(ell_max, ell_max, 1, false);\n  SpherepackIterator iter_dest(ell_max, ell_max, 1, false);\n  SparseMatrixFiller filler(square(num_independent_components) *\n                                iter_src.spherepack_array_size() *\n                                iter_dest.spherepack_array_size(),\n                            true, 1.0);\n\n  // Special case for rank 0, which is so much simpler than all the\n  // other ranks because there is actually no tensorylm\n  // transformation, no symmetry considerations, and no mixing of\n  // real/imaginary parts, just a filter based on ell.\n  if constexpr (rank == 0) {\n    for (iter_src.reset(); iter_src; ++iter_src) {\n      const size_t ell = iter_src.l();\n      if (ell >= lcutminus) {\n        const double coefl =\n            half_power.has_value() and ell <= lcutplus\n                ? (1.0 -\n                   exp(-alpha *\n                       integer_pow(double(ell) / double(lcutplus + 1),\n                                   2 * static_cast<int>(half_power.value()))))\n                : 1.0;\n        filler.add(-coefl, iter_src(), iter_src());\n      }\n    }\n    filler.fill(matrix);\n    return;\n  }\n\n  // We allocate space for some (not all) of the 3J symbols here, so\n  // that they can be used and re-used later inside the inner loops.\n  std::vector<WignerThreeJ> threej_pqs;  // NOLINT(misc-const-correctness)\n  std::vector<int> mbars;                // NOLINT(misc-const-correctness)\n  std::vector<WignerThreeJ> threej_uvs;  // NOLINT(misc-const-correctness)\n  std::vector<int> mtildes;              // NOLINT(misc-const-correctness)\n  if constexpr (rank > 1) {\n    threej_pqs.reserve(4);\n    mbars.reserve(4);\n    threej_uvs.reserve(4);\n    mtildes.reserve(4);\n  } else {\n    // For rank 1 we don't need threej_pqs, threej_uvs, mtildes, or mbars but we\n    // need to declare them anyway for scoping; they will just be unused. We\n    // will compute the rank-1 3J symbols below, and below we will also compute\n    // additional 3J symbols for higher ranks.\n    (void)threej_pqs;\n    (void)mbars;\n    (void)threej_uvs;\n    (void)mtildes;\n  }\n\n  // threej_rs and threej_ws contain std::optionals\n  // because some of the Wigner 3Js are not well-defined\n  // (because |m| > ell).\n  // The ones without values are never used or referenced.\n  // It is possible to compute fewer than 24\n  // threej_rs and threej_ws, by pushing_back only those\n  // that have values, but then it is difficult to index\n  // the threej_rs and threej_ws.  So keeping 24 of\n  // them and making them std::optionals is easier.\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<std::optional<WignerThreeJ>> threej_rs;\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<std::optional<WignerThreeJ>> threej_ws;\n  if constexpr (rank > 2) {\n    threej_rs.reserve(24);\n    threej_ws.reserve(24);\n  } else {\n    // Unneeded except for rank 3.\n    (void)threej_rs;\n    (void)threej_ws;\n  }\n\n  for (size_t dest_comp_index = 0; dest_comp_index < num_independent_components;\n       dest_comp_index++) {\n    static constexpr auto tensor_index_list =\n        TensorStructure::storage_to_tensor_index();\n\n    const auto dest_indices = tensor_index_list[dest_comp_index];\n    const auto dest_bvs = helpers::to_cart_basis_vector(dest_indices);\n\n    if constexpr (rank > 1) {\n      // threej_pqs is (as a function of p and q) the second 3J symbol\n      // in Eq. (22) for rank 2, and the first 3J symbol in Eq. (24) for\n      // rank 3.  It is not used for rank 1.\n      //\n      // mbars are the values of mbar for rank 2 and 3, which are uniquely\n      // determined by p and q because of the symmetries of threej_pqs.\n      threej_pqs.clear();\n      mbars.clear();\n      for (int p = -1; p <= 1; p += 2) {\n        for (int q = -1; q <= 1; q += 2) {\n          mbars.push_back(helpers::bv_to_m(dest_bvs[rank - 2], p) +\n                          helpers::bv_to_m(dest_bvs[rank - 1], q));\n          threej_pqs.emplace_back(1, helpers::bv_to_m(dest_bvs[rank - 2], p), 1,\n                                  helpers::bv_to_m(dest_bvs[rank - 1], q));\n        }\n      }\n    }\n\n    for (size_t src_comp_index = 0; src_comp_index < num_independent_components;\n         src_comp_index++) {\n      const auto src_indices = tensor_index_list[src_comp_index];\n      const auto src_bvs = helpers::to_cart_basis_vector(src_indices);\n      const size_t src_multiplicity =\n          TensorStructure::multiplicity(src_comp_index);\n\n      const double sign_y =\n          alg::accumulate(src_bvs, 1.0, [](const double acc, const auto bv) {\n            return acc * (bv == helpers::BasisVector::y ? -1.0 : 1.0);\n          });\n\n      if constexpr (rank > 1) {\n        // threej_uvs is (as a function of u and v) the last 3J symbol\n        // in Eq. (22) for rank 2, and the second 3J symbol in Eq. (24) for\n        // rank 3.  It is not used for rank 1.\n        //\n        // mtildes are the values of mbar for rank 2 and 3, which are uniquely\n        // determined by u and v because of the symmetries of threej_uvs.\n        threej_uvs.clear();\n        mtildes.clear();\n        for (int u = -1; u <= 1; u += 2) {\n          for (int v = -1; v <= 1; v += 2) {\n            mtildes.push_back(-(helpers::bv_to_m(src_bvs[rank - 2], u) +\n                                helpers::bv_to_m(src_bvs[rank - 1], v)));\n            threej_uvs.emplace_back(1, helpers::bv_to_m(src_bvs[rank - 2], u),\n                                    1, helpers::bv_to_m(src_bvs[rank - 1], v));\n          }\n        }\n      }\n\n      // threej_rs and threej_ws are (as a function of r, lbar, mbar,\n      // and mtilde) the last two 3J symbols in Eq. (24) for rank 3.\n      // They are not used for ranks 1 and 2.\n      if constexpr (rank > 2) {\n        threej_rs.clear();\n        for (const int mbar : mbars) {\n          for (int r = -1; r <= 1; r += 2) {\n            const int mr = helpers::bv_to_m(dest_bvs[0], r);\n            for (size_t lbar = 0; lbar <= 2; ++lbar) {\n              if (abs(mbar) <= static_cast<int>(lbar)) {\n                threej_rs.emplace_back(WignerThreeJ(1, mr, lbar, mbar));\n              } else {\n                threej_rs.emplace_back(std::nullopt);\n              }\n            }\n          }\n        }\n        threej_ws.clear();\n        for (const int mtilde : mtildes) {\n          for (int w = -1; w <= 1; w += 2) {\n            const int mw = helpers::bv_to_m(src_bvs[0], w);\n            for (size_t lbar = 0; lbar <= 2; ++lbar) {\n              if (abs(mtilde) <= static_cast<int>(lbar)) {\n                threej_ws.emplace_back(WignerThreeJ(1, mw, lbar, -mtilde));\n              } else {\n                threej_ws.emplace_back(std::nullopt);\n              }\n            }\n          }\n        }\n      }\n\n      for (size_t lprime = lcutminus; lprime <= ell_max + rank; ++lprime) {\n        // coeflprime is the factor (2 lprime+1) g(lprime)/2 that appears\n        // for all ranks.\n        const double coeflprime =\n            half_power.has_value() and lprime <= lcutplus\n                ? 0.5 * static_cast<double>(2 * lprime + 1) *\n                      (1.0 - exp(-alpha *\n                                 integer_pow(\n                                     double(lprime) / double(lcutplus + 1),\n                                     2 * static_cast<int>(half_power.value()))))\n                : 0.5 * static_cast<double>(2 * lprime + 1);\n        for (int mprime = -static_cast<int>(lprime);\n             mprime <= static_cast<int>(lprime); ++mprime) {\n          // Here is where the formulas differ for different ranks.\n          if constexpr (rank == 1) {\n            (void)src_multiplicity;  // Unused variable for rank 1.\n            for (int p = -1; p <= 1; p += 2) {\n              const int mdest = mprime + helpers::bv_to_m(dest_bvs[0], p);\n              if (mdest >= 0) {\n                // Fill only nonnegative m, since that is all we store.\n\n                // threej_p is the last 3J symbol in Eq. (20).\n                WignerThreeJ threej_p(lprime, mprime, 1,\n                                      helpers::bv_to_m(dest_bvs[0], p));\n                for (int j = -1; j <= 1; j += 2) {\n                  // threej_j is the first 3J symbol in Eq. (20).\n                  WignerThreeJ threej_j(lprime, mprime, 1,\n                                        helpers::bv_to_m(src_bvs[0], j));\n                  const int msrc = mprime + helpers::bv_to_m(src_bvs[0], j);\n                  // coefjp is all the factors other than the 3J symbols\n                  // that appears in Eq. (20).\n                  std::complex<double> coefjp =\n                      -coeflprime * helpers::bv_to_k(src_bvs[0], j) *\n                      helpers::bv_to_k(dest_bvs[0], p) * sign_y;\n                  if (coefficient_normalization ==\n                          CoefficientNormalization::Standard and\n                      (mdest + msrc) % 2 != 0) {\n                    coefjp *= -1.0;\n                  }\n                  inner_loops_one(filler, iter_src, iter_dest, src_comp_index,\n                                  dest_comp_index, ell_max, mdest, msrc, coefjp,\n                                  threej_j, threej_p);\n                }\n              }\n            }\n          } else if constexpr (rank == 2) {\n            for (size_t lbar = 0; lbar <= 2; ++lbar) {\n              const double symm_factor =\n                  helpers::get_symm_factor<typename TensorStructure::symmetry>(\n                      src_multiplicity, lbar);\n              if (symm_factor != 0.0) {\n                const double coeflbar = 0.5 * static_cast<double>(2 * lbar + 1);\n                for (int mbar = std::max(-static_cast<int>(lbar), -mprime);\n                     mbar <= static_cast<int>(lbar); ++mbar) {\n                  // The first 3J term in Eq. (22)\n                  WignerThreeJ threej_lbar(lprime, mprime, lbar, mbar);\n                  for (int mtilde = -static_cast<int>(lbar);\n                       mtilde <= static_cast<int>(lbar); ++mtilde) {\n                    // The penultimate 3J term in Eq. (22)\n                    WignerThreeJ threej_ltilde(lprime, -mprime, lbar, mtilde);\n                    inner_loops_two(filler, iter_src, iter_dest, src_comp_index,\n                                    dest_comp_index, ell_max, lprime, mprime,\n                                    lbar, mbar, mtilde, coeflbar, coeflprime,\n                                    threej_lbar, threej_ltilde, mbars, mtildes,\n                                    src_bvs, dest_bvs, symm_factor, sign_y,\n                                    threej_pqs, threej_uvs,\n                                    coefficient_normalization);\n                  }\n                }\n              }\n            }\n          } else if constexpr (rank == 3) {\n            for (size_t lhat = 0; lhat <= 3; ++lhat) {\n              for (int mhat = -static_cast<int>(lhat);\n                   mhat <= static_cast<int>(lhat); ++mhat) {\n                // The fourth 3J term in Eq. (24)\n                WignerThreeJ threej_mhat(lprime, -mprime, lhat, mhat);\n                for (int mcheck = -static_cast<int>(lhat);\n                     mcheck <= static_cast<int>(lhat); ++mcheck) {\n                  // The third 3J term in Eq. (24)\n                  WignerThreeJ threej_mcheck(lprime, mprime, lhat, mcheck);\n                  size_t mbar_indx = 0;\n                  for (int p = -1; p <= 1; p += 2) {\n                    for (int q = -1; q <= 1; q += 2, ++mbar_indx) {\n                      for (int r = -1; r <= 1; r += 2) {\n                        const int mr = helpers::bv_to_m(dest_bvs[0], r);\n                        if (mcheck == mr + mbars[mbar_indx] and\n                            mprime + mcheck >= 0) {\n                          inner_loops_three<typename TensorStructure::symmetry>(\n                              filler, iter_src, iter_dest, src_comp_index,\n                              dest_comp_index, ell_max, lprime, coeflprime,\n                              mprime, lhat, mhat, threej_mhat, mcheck,\n                              threej_mcheck, mbar_indx, p, q, r, mr, mbars,\n                              mtildes, src_bvs, dest_bvs, src_multiplicity,\n                              threej_pqs, threej_uvs, threej_ws, threej_rs,\n                              sign_y, coefficient_normalization);\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n  filler.fill(matrix);\n}\n\n// Explicit instantiations\n#define TSTRUCT(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void                                                              \\\n          fill_filter<typename TSTRUCT(data) < DataVector, 3>::structure >   \\\n      (gsl::not_null<SimpleSparseMatrix*> matrix, size_t ell_max,            \\\n       size_t number_of_ell_modes_to_kill, std::optional<size_t> half_power, \\\n       CoefficientNormalization coefficient_normalization);                  \\\n  template void                                                              \\\n          fill_filter<typename TSTRUCT(data) < DataVector, 3>::structure >   \\\n      (gsl::not_null<blaze::CompressedMatrix<double, blaze::rowMajor>*>      \\\n           matrix,                                                           \\\n       size_t ell_max, size_t number_of_ell_modes_to_kill,                   \\\n       std::optional<size_t> half_power,                                     \\\n       CoefficientNormalization coefficient_normalization);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (tnsr::i, tnsr::ii, tnsr::ij, tnsr::ijk, tnsr::ijj))\n\n#undef INSTANTIATE\n#undef TSTRUCT\n\n// Instantiation for scalars.\ntemplate void fill_filter<typename Scalar<DataVector>::structure>(\n    gsl::not_null<SimpleSparseMatrix*> matrix, size_t ell_max,\n    size_t number_of_ell_modes_to_kill, std::optional<size_t> half_power,\n    CoefficientNormalization coefficient_normalization);\n\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlmFilter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm::TensorYlm {\n\n/*!\n * \\brief Fills a sparse matrix that does a TensorYlm filter operation.\n *\n * If the CoefficientNormalization parameter is set to Standard, then\n * assumes that $T^{\\tilde A}_{\\ell' m'}$ is stored in a\n * Tensor<DataVector>.  Multiplying (one plus) the resulting\n * sparse matrix by the Tensor<DataVector> is equivalent to\n * evaluating the right-hand side of Eq. $(\\ref{eq:Filter})$.\n * If the CoefficientNormalization parameter is set to Spherepack, then\n * assumes the matrix will multiply a Tensor<DataVector> containing\n * ${\\breve T}^{\\tilde A}_{\\ell' m'}$ and the filter operation is equivalent to\n * Eq. $(\\ref{eq:FilterSpherepack})$.\n *\n * We assume that the independent components of the Tensor<DataVector>\n * are stored contiguously in memory, in order of the storage_index of\n * the Tensor.  However, we do allow for a stride.  This means that we\n * can point to memory starting with the first element of\n * $T^{\\tilde A}_{\\ell' m'}$ and multiply that by the sparse matrix we compute\n * here, and get the filtered result.\n *\n * The memory layout here is different than in SpEC.  In SpEC, each\n * tensor component is stored in separately-allocated memory, so the\n * SpEC equivalent of the fill_filter function fills $N^2$ sparse\n * matrices, where $N$ is the number of independent components of the\n * Tensor. The advantage of the SpEC method is that each sparse matrix\n * is smaller, so sorting elements into the correct order while\n * constructing each sparse matrix is faster (sorting is > linear in\n * the number of matrix elements).  The disadvantage of the SpEC\n * method is that evaluating the filter for a single Tensor involves\n * $N^2$ matrix-vector multiplications, whereas here evaluating the\n * filter involves only one matrix-vector multiplication, which should\n * have more efficient memory access.  It is not clear which method is\n * faster overall without more profiling.\n *\n * ## Explicit formulas\n *\n * The following formulas come from Klinger and Scheel, in prep.\n *\n * For rank-0 tensors, the expression is simple because there\n * is no change of basis, only a filter based on $\\ell$.\n * \\begin{align}\n *  F_{l m \\tilde{D}}^{\\ell'' m''\\tilde{A}} &=\n *  \\delta(\\tilde{D},\\tilde{A})\\delta_{\\ell \\ell''}\\delta_{m m''}\n *  \\left[1-\n *  \\delta(\\ell_{\\mathrm{cut}}^-\\leq \\ell \\leq \\ell_{\\mathrm{max}}) g(\\ell)\n *  \\right].\n * \\end{align}\n *\n * For rank-1 tensors, the expression for\n * $F_{l m \\tilde{D}}^{\\ell'' m''\\tilde{A}}$ is\n * \\begin{align}\n *  F_{l m \\tilde{D}}^{\\ell'' m''\\tilde{A}} &=\n *  \\delta(\\tilde{D},\\tilde{A})\\delta_{\\ell \\ell''}\\delta_{m m''}\n *  \\nonumber \\\\\n *  &- (-1)^{m+m''} (-1)^{\\delta(\\tilde{D},\\mathbf{e}_y)}\n *  \\delta(\\ell,\\ell'')\n *  \\sum_{\\ell'=\\ell_{\\mathrm{cut}}^-}^{\\ell_{\\mathrm{max}}+1}\n *  \\frac{2\\ell'+1}{2} g(\\ell') \\nonumber \\\\\n *  &\\times \\sum_{j,p,m'} k_j(\\tilde{D})k_p(\\tilde{A})\n *    \\left(\\begin{array}{rrr}\n *     \\ell&\\ell'&1\\cr\n *      -m''&m'&m_j(\\tilde{D})\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{rrr}\n *     \\ell&\\ell'&1\\cr\n *      -m&m'&m_p(\\tilde{A})\n *    \\end{array}\\right),\n * \\end{align}\n * where the 6-element \"matrices\"\n * in parentheses are Wigner 3-J symbols, and where\n * \\begin{align}\n *  g(\\ell') &=\n *  \\left\\{\\begin{array}{lr}\n *      1-f(\\ell') & \\ell' \\leq \\ell_{\\mathrm{cut}}^+,\\\\\n *      1 & \\ell' > \\ell_{\\mathrm{cut}}^+.\n *  \\end{array}\\right.\n * \\end{align}\n *\n * For second-rank tensors, the expression for\n * $F_{l m \\tilde{D}}^{\\ell'' m''\\tilde{A}}$ is\n * \\begin{align}\n *  F_{l m \\tilde{D}}^{\\ell'' m''\\tilde{A}}\n *  &=\n *  \\delta(\\tilde{D},\\tilde{A})\\delta_{\\ell \\ell''}\\delta_{m m''}\n * \\nonumber \\\\\n * &-\n *  \\frac{1}{4}\n * (-1)^{\\delta(\\tilde{D}_1,\\mathbf{e}_y)}\n * (-1)^{\\delta(\\tilde{D}_2,\\mathbf{e}_y)}\n *  \\delta_{\\ell \\ell''}\n * \\nonumber \\\\\n * &\\times\n * \\sum_{\\ell'=\\ell_{\\mathrm{cut}}^-}^{\\ell_{\\mathrm{max}}+2}\n * (2\\ell'+1) g(\\ell')\n * \\sum_{u,v,p,q,\\bar{\\ell},\\tilde{m},\\bar{m},m'}\n * (2 \\bar{\\ell}+1) S(\\bar{\\ell},\\tilde{D})\n * k_u(\\tilde{D}_1) k_v(\\tilde{D}_2)\n * k_p(\\tilde{A}_1) k_q(\\tilde{A}_2)\n * \\nonumber \\\\\n * &\\times\n *    \\left(\\begin{array}{rrr}\n *     \\ell&\\ell'&\\bar{\\ell}\\cr\n *      -m&m'&\\bar{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{rrr}\n *     1&1&\\bar{\\ell}\\cr\n *      m_p(\\tilde{A}_1)&m_q(\\tilde{A}_2)&-\\bar{m}\n *    \\end{array}\\right)\n * \\nonumber \\\\\n * &\\times\n *    \\left(\\begin{array}{rrr}\n *     \\ell'&\\ell&\\bar{\\ell}\\cr\n *      -m'&m''&\\tilde{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{rrr}\n *     1&1&\\bar{\\ell}\\cr\n *      m_u(\\tilde{D}_1)&m_v(\\tilde{D}_2)&\\tilde{m}\n *    \\end{array}\\right),\n *  \\label{eq:RankTwoTransformWithCut}\n * \\end{align}\n * where the factor $S(\\bar{\\ell},\\tilde{D})$ is a symmetry factor.\n * For a 2nd-rank tensor with no symmetries, $S(\\bar{\\ell},\\tilde{D})$ is\n * unity, but for a tensor symmetric in $(\\tilde{D_1},\\tilde{D_2})$\n * the matrix elements $F_{l m \\tilde{D}}^{\\ell'' m''\\tilde{A}}$ are\n * multiplied only by tensor components with $\\tilde{D_1}\\geq\\tilde{D_2}$\n * and the symmetry is accounted for by setting\n * \\begin{align}\n *   S(\\bar{\\ell},\\tilde{D}) &=\n *   (1+(-1)^{\\bar{\\ell}})\\frac{2-\\delta(\\tilde{D_1},\\tilde{D_2})}{2}.\n * \\end{align}\n *\n * For third-rank tensors, the expression for\n * $F_{l m \\tilde{D}}^{\\ell'' m''\\tilde{A}}$ is\n * \\begin{align}\n *  F_{l m \\tilde{D}}^{\\ell'' m''\\tilde{A}}\n *  &=\n *  \\delta(\\tilde{D},\\tilde{A})\\delta_{\\ell \\ell''}\\delta_{m m''}\n *  \\nonumber \\\\\n * &-\n *  \\frac{1}{8}\n *  (-1)^{\\delta(\\tilde{D}_1,\\mathbf{e}_y)}\n *  (-1)^{\\delta(\\tilde{D}_2,\\mathbf{e}_y)}\n *  (-1)^{\\delta(\\tilde{D}_3,\\mathbf{e}_y)}\n *  \\delta_{\\ell \\ell''}\n *  \\nonumber \\\\\n *  &\\times\n *  \\sum_{\\ell'=\\ell_{\\mathrm{cut}}^-}^{\\ell_{\\mathrm{max}}+3}\n *  (2\\ell'+1) g(\\ell')\n *  \\sum_{u,v,w,p,q,r,\\tilde{m},\\bar{\\ell},\\bar{m},\n *  \\hat{\\ell},\\hat{m},\\check{m},m'}\n *  (2 \\bar{\\ell}+1) S(\\bar{\\ell},\\tilde{D})\n *  (2 \\hat{\\ell}+1)\n *  \\nonumber \\\\\n *  &\\qquad\\times\n *  k_u(\\tilde{D}_2) k_v(\\tilde{D}_3) k_w(\\tilde{D}_1)\n *  k_p(\\tilde{A}_2) k_q(\\tilde{A}_3) k_r(\\tilde{A}_1)\n *  (-1)^{\\tilde{m}-\\bar{m}}\n *  \\nonumber \\\\\n *  &\\qquad\\times\n *    \\left(\\begin{array}{rrr}\n *     1&1&\\bar{\\ell}\\cr\n *     m_p(\\tilde{A}_2)&m_q(\\tilde{A}_3)&-\\bar{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{rrr}\n *     1&1&\\bar{\\ell}\\cr\n *     m_u(\\tilde{D}_2)&m_v(\\tilde{D}_3)&\\tilde{m}\n *    \\end{array}\\right)\n *  \\nonumber \\\\\n *  &\\qquad\\times\n *    \\left(\\begin{array}{rrr}\n *     \\ell&\\ell'&\\hat{\\ell}\\cr\n *     -m&m'&\\check{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{rrr}\n *     \\ell'&\\ell&\\hat{\\ell}\\cr\n *     -m'&m''&\\hat{m}\n *    \\end{array}\\right)\n *  \\nonumber \\\\\n *  &\\qquad\\times\n *    \\left(\\begin{array}{rrr}\n *     1&\\bar{\\ell}&\\hat{\\ell}\\cr\n *     m_r(\\tilde{A}_1)&\\bar{m}&-\\check{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{rrr}\n *     1&\\bar{\\ell}&\\hat{\\ell}\\cr\n *     m_w(\\tilde{D}_1)&-\\tilde{m}&\\hat{m}\n *    \\end{array}\\right).\n *    \\label{eq:RankThreeTransformWithCut}\n * \\end{align}\n * As in the rank-2 case,\n * $S(\\bar{\\ell},\\tilde{D})$ is a symmetry factor that is unity\n * for a tensor with no symmetries and is\n * \\begin{align}\n *   S(\\bar{\\ell},\\tilde{D}) &=\n *   (1+(-1)^{\\bar{\\ell}})\\frac{2-\\delta(\\tilde{D_2},\\tilde{D_3})}{2}\n * \\end{align}\n * for a tensor symmetric on its last two indices. We don't consider\n * other symmetries because we don't find them in the cases we care\n * about.\n *\n * \\tparam TensorStructure A Tensor_detail::Structure\n * \\tparam SparseMatrixType A sparse matrix fillable by SparseMatrixFiller\n *\n * \\param matrix The sparse matrix to fill\n * \\param ell_max The maximum ylm ell value.\n * \\param number_of_ell_modes_to_kill How many top ell modes to set to zero.\n * \\param half_power The half power $\\sigma$ for more complicated filtering.\n * \\param coefficient_normalization Describes the normalization of coefficients.\n *\n *  If half_power is std::nullopt, implements a Heaviside filter.\n *  Otherwise, the filter is the more complicated one described in the\n *  TensorYlm namespace documentation, with $\\sigma$ equal to\n *  half_power and $\\ell_{\\mathrm{cut}}^+$ equal to $\\ell_{\\rm max}$\n *  minus number_of_ell_modes_to_kill.\n *\n */\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid fill_filter(gsl::not_null<SparseMatrixType*> matrix, size_t ell_max,\n                 size_t number_of_ell_modes_to_kill,\n                 std::optional<size_t> half_power,\n                 CoefficientNormalization coefficient_normalization);\n\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlmHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmHelpers.hpp\"\n\n#include <array>\n#include <complex>\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm::TensorYlm::helpers {\n\ntemplate <size_t Rank>\nstd::array<BasisVector, Rank> to_cart_basis_vector(\n    const cpp20::array<size_t, Rank>& indices) {\n  static_assert(Rank <= 3, \"Implemented only for rank up to 3\");\n  std::array<BasisVector, Rank> result{};\n  for (size_t i = 0; i < Rank; ++i) {\n    switch (indices[i]) {\n      case 0:\n        gsl::at(result, i) = BasisVector::x;\n        break;\n      case 1:\n        gsl::at(result, i) = BasisVector::y;\n        break;\n      case 2:\n        gsl::at(result, i) = BasisVector::z;\n        break;\n      default:\n        ASSERT(false, \"Cannot get here\");\n    }\n  }\n  return result;\n}\n\nint bv_to_m(const BasisVector basis_vector, const int i) {\n  int result = std::numeric_limits<int>::min();\n  switch (basis_vector) {\n    case BasisVector::z:\n      result = 0;\n      break;\n    case BasisVector::y:\n    case BasisVector::x:\n      result = i;\n      break;\n    default:\n      ASSERT(false, \"Unknown basisvector\");\n  }\n  return result;\n}\n\nstd::complex<double> bv_to_k(const BasisVector basis_vector, const int i) {\n  std::complex<double> result{std::numeric_limits<double>::signaling_NaN(),\n                              std::numeric_limits<double>::signaling_NaN()};\n  switch (basis_vector) {\n    case BasisVector::z:\n      result = {1.0 / sqrt(2.0), 0.0};\n      break;\n    case BasisVector::y:\n      result = {0, 1};\n      break;\n    case BasisVector::x:\n      result = {double(-i), 0.0};\n      break;\n    default:\n      ASSERT(false, \"Unknown basisvector\");\n  }\n  return result;\n}\n\ntemplate <size_t Rank>\nstd::array<BasisVector, Rank> to_sphere_basis_vector(\n    const cpp20::array<size_t, Rank>& indices) {\n  static_assert(Rank <= 3, \"Implemented only for rank up to 3\");\n  std::array<BasisVector, Rank> result{};\n  for (size_t i = 0; i < Rank; ++i) {\n    switch (indices[i]) {\n      case 0:\n        gsl::at(result, i) = BasisVector::l;\n        break;\n      case 1:\n        gsl::at(result, i) = BasisVector::m;\n        break;\n      case 2:\n        gsl::at(result, i) = BasisVector::mbar;\n        break;\n      default:\n        ASSERT(false, \"Cannot get here\");\n    }\n  }\n  return result;\n}\n\nint bv_to_s(const BasisVector basis_vector) {\n  int result = std::numeric_limits<int>::min();\n  switch (basis_vector) {\n    case BasisVector::l:\n      result = 0;\n      break;\n    case BasisVector::m:\n      result = -1;\n      break;\n    case BasisVector::mbar:\n      result = 1;\n      break;\n    default:\n      ASSERT(false, \"Unknown basisvector \" << static_cast<int>(basis_vector));\n  }\n  return result;\n}\n\ntemplate <typename Symm>\ndouble get_symm_factor(const size_t src_multiplicity, const size_t lbar) {\n  static_assert(std::is_same_v<Symmetry<3, 2, 1>, Symm> or\n                    std::is_same_v<Symmetry<2, 1, 1>, Symm> or\n                    std::is_same_v<Symmetry<2, 1>, Symm> or\n                    std::is_same_v<Symmetry<1, 1>, Symm>,\n                \"Unimplemented symmetry\");\n  if constexpr (std::is_same_v<Symmetry<3, 2, 1>, Symm> or\n                std::is_same_v<Symmetry<2, 1>, Symm>) {\n    // Not symmetric on the 2 indices.\n    (void)src_multiplicity;\n    (void)lbar;\n  } else {\n    // symmetric on the 2 indices.\n    //\n    // Note that src_multiplicity is 1 if the indices are the same,\n    // 2 if they are different.  For more complicated symmetries, if\n    // we ever wanted to implement them, this formula would be different.\n    return (lbar % 2 == 0 ? static_cast<double>(src_multiplicity) : 0.0);\n  }\n  return 1.0;\n}\n\n// Explicit instantiations\ntemplate std::array<BasisVector, 1> to_cart_basis_vector(\n    const cpp20::array<size_t, 1>& indices);\ntemplate std::array<BasisVector, 2> to_cart_basis_vector(\n    const cpp20::array<size_t, 2>& indices);\ntemplate std::array<BasisVector, 3> to_cart_basis_vector(\n    const cpp20::array<size_t, 3>& indices);\ntemplate std::array<BasisVector, 1> to_sphere_basis_vector(\n    const cpp20::array<size_t, 1>& indices);\ntemplate std::array<BasisVector, 2> to_sphere_basis_vector(\n    const cpp20::array<size_t, 2>& indices);\ntemplate std::array<BasisVector, 3> to_sphere_basis_vector(\n    const cpp20::array<size_t, 3>& indices);\ntemplate double get_symm_factor<Symmetry<3, 2, 1>>(size_t src_multiplicity,\n                                                   size_t lbar);\ntemplate double get_symm_factor<Symmetry<2, 1, 1>>(size_t src_multiplicity,\n                                                   size_t lbar);\ntemplate double get_symm_factor<Symmetry<1, 1>>(size_t src_multiplicity,\n                                                size_t lbar);\ntemplate double get_symm_factor<Symmetry<2, 1>>(size_t src_multiplicity,\n                                                size_t lbar);\n\n}  // namespace ylm::TensorYlm::helpers\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlmHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstdint>\n\n#include \"Utilities/Array.hpp\"\n\nnamespace ylm::TensorYlm::helpers {\n\n/// Defines the three Cartesian and three spherical basis vectors.\nenum class BasisVector : uint8_t { x, y, z, l, m, mbar };\n\n/// Returns a Cartesian BasisVector for every index.\ntemplate <size_t Rank>\nstd::array<BasisVector, Rank> to_cart_basis_vector(\n    const cpp20::array<size_t, Rank>& indices);\n\n/// Returns the m value associated with a Cartesian basis vector.\nint bv_to_m(BasisVector basis_vector, int i);\n\n/// Returns the prefactor k associated with a Cartesian basis vector.\nstd::complex<double> bv_to_k(BasisVector basis_vector, int i);\n\n/// Returns a spherical BasisVector for every index.\ntemplate <size_t Rank>\nstd::array<BasisVector, Rank> to_sphere_basis_vector(\n    const cpp20::array<size_t, Rank>& indices);\n\n/// Returns minus the spinweight of a tetrad basis vector\nint bv_to_s(BasisVector basis_vector);\n\n/// Computes the symmetry factor S that appears in various equations.\ntemplate <typename Symm>\ndouble get_symm_factor(size_t src_multiplicity, size_t lbar);\n\n/// Computes the (-1)^m factor that is used depending on whether the\n/// coefficient normalization is Spherepack.\ntemplate <typename T>\nconstexpr T sign_m(const int m,\n                   const CoefficientNormalization coefficient_normalization) {\n  return coefficient_normalization == CoefficientNormalization::Spherepack and\n                 m % 2 != 0\n             ? static_cast<T>(-1)\n             : static_cast<T>(1);\n}\n\n}  // namespace ylm::TensorYlm::helpers\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlmSphereToCart.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmSphereToCart.hpp\"\n\n#include <blaze/math/CompressedMatrix.h>\n#include <complex>\n#include <optional>\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"DataStructures/SparseMatrixFiller.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/WignerThreeJ.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace ylm::TensorYlm {\n\nnamespace {\n// Inner loops of the rank-1 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\nvoid inner_loops_one(SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n                     SpherepackIterator& iter_dest, const size_t src_comp_index,\n                     const size_t dest_comp_index, const size_t ell_max,\n                     const size_t l_dest, const int m_dest, const int scheck,\n                     const int mj, const std::complex<double> kj,\n                     WignerThreeJ& threej_m, WignerThreeJ& threej_s,\n                     const int sign_m_dest, const int sign_delta_sb,\n                     const int sign_delta_sb_conj,\n                     const CoefficientNormalization coefficient_normalization) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, dest_comp_index,\n                            src_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  const int m_src = m_dest - mj;\n  const auto sign_m =\n      helpers::sign_m<int>(m_src + m_dest, coefficient_normalization);\n  for (size_t l_src =\n           std::max(static_cast<size_t>(abs(m_src)), threej_s.l1_min());\n       (l_src <= threej_s.l1_max() and l_src <= ell_max); ++l_src) {\n    const double sqterm =\n        sqrt(0.5 * static_cast<double>((2 * l_dest + 1) * (2 * l_src + 1)));\n    const double coef_without_kj = sqterm * threej_s(l_src) * threej_m(l_src);\n    const int sign_conjA =\n        ((static_cast<int>(l_src + l_dest) + 1 - scheck + m_src) % 2 == 0 ? 1\n                                                                          : -1);\n    const int sign_plus_msrc_term = sign_m_dest * sign_m * sign_delta_sb;\n    const int sign_min_msrc_term = sign_delta_sb_conj * sign_m * sign_conjA;\n    if (m_src >= 0) {\n      // Main term.\n      if (kj.imag() == 0) {\n        // Main term.\n        if (iter_dest.coefficient_array() ==\n            SpherepackIterator::CoefficientArray::a) {\n          // ReRe\n          iter_src.set(l_src, static_cast<size_t>(m_src),\n                       SpherepackIterator::CoefficientArray::a);\n          add_element(kj.real() * coef_without_kj * sign_plus_msrc_term);\n        } else if (iter_dest.coefficient_array() ==\n                   SpherepackIterator::CoefficientArray::b) {\n          // ImIm\n          iter_src.set(l_src, static_cast<size_t>(m_src),\n                       SpherepackIterator::CoefficientArray::b);\n          add_element(kj.real() * coef_without_kj * sign_plus_msrc_term);\n        }\n      } else {\n        if (iter_dest.coefficient_array() ==\n            SpherepackIterator::CoefficientArray::a) {\n          // ReIm\n          iter_src.set(l_src, static_cast<size_t>(m_src),\n                       SpherepackIterator::CoefficientArray::b);\n          add_element(-kj.imag() * coef_without_kj * sign_plus_msrc_term);\n        } else if (iter_dest.coefficient_array() ==\n                   SpherepackIterator::CoefficientArray::b) {\n          // ImRe\n          iter_src.set(l_src, static_cast<size_t>(m_src),\n                       SpherepackIterator::CoefficientArray::a);\n          add_element(kj.imag() * coef_without_kj * sign_plus_msrc_term);\n        }\n      }\n    } else {\n      if (kj.imag() == 0) {\n        // Other term, for negative m_src.\n        if (iter_dest.coefficient_array() ==\n            SpherepackIterator::CoefficientArray::a) {\n          // ReRe\n          iter_src.set(l_src, static_cast<size_t>(-m_src),\n                       SpherepackIterator::CoefficientArray::a);\n          add_element(kj.real() * coef_without_kj * sign_min_msrc_term);\n        } else if (iter_dest.coefficient_array() ==\n                   SpherepackIterator::CoefficientArray::b) {\n          // ImIm\n          iter_src.set(l_src, static_cast<size_t>(-m_src),\n                       SpherepackIterator::CoefficientArray::b);\n          add_element(-kj.real() * coef_without_kj * sign_min_msrc_term);\n        }\n      } else {\n        if (iter_dest.coefficient_array() ==\n            SpherepackIterator::CoefficientArray::a) {\n          // ReIm\n          iter_src.set(l_src, static_cast<size_t>(-m_src),\n                       SpherepackIterator::CoefficientArray::b);\n          add_element(kj.imag() * coef_without_kj * sign_min_msrc_term);\n        } else if (iter_dest.coefficient_array() ==\n                   SpherepackIterator::CoefficientArray::b) {\n          // ImRe\n          iter_src.set(l_src, static_cast<size_t>(-m_src),\n                       SpherepackIterator::CoefficientArray::a);\n          add_element(kj.imag() * coef_without_kj * sign_min_msrc_term);\n        }\n      }\n    }\n  }\n}\n\n// Inner loops of the rank-2 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\nvoid inner_loops_two(SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n                     SpherepackIterator& iter_dest, const size_t src_comp_index,\n                     const size_t dest_comp_index, const size_t ell_max,\n                     const size_t l_dest, const int m_dest, const size_t lbar,\n                     const std::vector<int>& mbars,\n                     std::vector<std::optional<WignerThreeJ>>& threej_mbars,\n                     WignerThreeJ& threej_ells_sbar, const double symm_factor,\n                     const std::array<helpers::BasisVector, 2>& dest_bvs,\n                     std::vector<WignerThreeJ>& threej_pqs,\n                     const double threej_ones_sbar_val, const int sign_sbar,\n                     const int sign_delta_sb, const int sign_delta_sb_conj,\n                     const CoefficientNormalization coefficient_normalization) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, src_comp_index,\n                            dest_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  size_t mbar_indx = 0;\n  for (int p = -1; p <= 1; p += 2) {\n    for (int q = -1; q <= 1; q += 2, ++mbar_indx) {\n      if (static_cast<size_t>(std::abs(mbars[mbar_indx])) <= lbar) {\n        const int m_src = m_dest - mbars[mbar_indx];\n        const auto sign_m =\n            helpers::sign_m<double>(m_src + m_dest, coefficient_normalization);\n        const int sign_m_src = (m_src % 2 == 0 ? 1 : -1);\n        const std::complex<double> kj =\n            helpers::bv_to_k(dest_bvs[0], p) * helpers::bv_to_k(dest_bvs[1], q);\n        for (size_t l_src = std::max(static_cast<size_t>(abs(m_src)),\n                                     threej_ells_sbar.l1_min());\n             (l_src <= threej_ells_sbar.l1_max() and l_src <= ell_max);\n             ++l_src) {\n          const double sqterm =\n              0.5 *\n              sqrt(static_cast<double>((2 * l_dest + 1) * (2 * l_src + 1)));\n          const int sign_toprow = ((l_src + l_dest) % 2 == 0 ? 1 : -1);\n          const int sign_plus_msrc_term =\n              sign_m_src * sign_delta_sb * sign_sbar;\n          const int sign_min_msrc_term = sign_delta_sb_conj * sign_toprow;\n          const double coef_without_kj =\n              sign_m * sqterm * threej_ells_sbar(l_src) * threej_ones_sbar_val *\n              threej_mbars[mbar_indx].value()(l_src) *\n              threej_pqs[mbar_indx](lbar) * symm_factor *\n              static_cast<double>(2 * lbar + 1);\n\n          if (m_src >= 0) {\n            if (kj.imag() == 0) {\n              // Main term.\n              if (iter_dest.coefficient_array() ==\n                  SpherepackIterator::CoefficientArray::a) {\n                // ReRe\n                iter_src.set(l_src, static_cast<size_t>(m_src),\n                             SpherepackIterator::CoefficientArray::a);\n                add_element(kj.real() * coef_without_kj * sign_plus_msrc_term);\n              } else if (iter_dest.coefficient_array() ==\n                         SpherepackIterator::CoefficientArray::b) {\n                // ImIm\n                iter_src.set(l_src, static_cast<size_t>(m_src),\n                             SpherepackIterator::CoefficientArray::b);\n                add_element(kj.real() * coef_without_kj * sign_plus_msrc_term);\n              }\n            } else {\n              if (iter_dest.coefficient_array() ==\n                  SpherepackIterator::CoefficientArray::a) {\n                // ReIm\n                iter_src.set(l_src, static_cast<size_t>(m_src),\n                             SpherepackIterator::CoefficientArray::b);\n                add_element(-kj.imag() * coef_without_kj * sign_plus_msrc_term);\n              } else if (iter_dest.coefficient_array() ==\n                         SpherepackIterator::CoefficientArray::b) {\n                // ImRe\n                iter_src.set(l_src, static_cast<size_t>(m_src),\n                             SpherepackIterator::CoefficientArray::a);\n                add_element(kj.imag() * coef_without_kj * sign_plus_msrc_term);\n              }\n            }\n          } else {\n            if (kj.imag() == 0) {\n              // Other term, for negative m_src.\n              if (iter_dest.coefficient_array() ==\n                  SpherepackIterator::CoefficientArray::a) {\n                // ReRe\n                iter_src.set(l_src, static_cast<size_t>(-m_src),\n                             SpherepackIterator::CoefficientArray::a);\n                add_element(kj.real() * coef_without_kj * sign_min_msrc_term);\n              } else if (iter_dest.coefficient_array() ==\n                         SpherepackIterator::CoefficientArray::b) {\n                // ImIm\n                iter_src.set(l_src, static_cast<size_t>(-m_src),\n                             SpherepackIterator::CoefficientArray::b);\n                add_element(-kj.real() * coef_without_kj * sign_min_msrc_term);\n              }\n            } else {\n              if (iter_dest.coefficient_array() ==\n                  SpherepackIterator::CoefficientArray::a) {\n                // ReIm\n                iter_src.set(l_src, static_cast<size_t>(-m_src),\n                             SpherepackIterator::CoefficientArray::b);\n                add_element(kj.imag() * coef_without_kj * sign_min_msrc_term);\n              } else if (iter_dest.coefficient_array() ==\n                         SpherepackIterator::CoefficientArray::b) {\n                // ImRe\n                iter_src.set(l_src, static_cast<size_t>(-m_src),\n                             SpherepackIterator::CoefficientArray::a);\n                add_element(kj.imag() * coef_without_kj * sign_min_msrc_term);\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// Inner loops of the rank-3 calculation.  The purpose of this\n// function is so that there are not so many nested loops inside of\n// the main function, making the main function and this function more\n// readable.\ntemplate <typename Symm>\nvoid inner_loops_three(\n    SparseMatrixFiller& filler, SpherepackIterator& iter_src,\n    SpherepackIterator& iter_dest, const size_t src_comp_index,\n    const size_t dest_comp_index, const size_t ell_max, const size_t l_dest,\n    const int m_dest, const size_t lcheck, const int mcheck, const int sbar,\n    const int scheck, std::vector<WignerThreeJ>& threej_ones_sbars,\n    WignerThreeJ& threej_ssum, WignerThreeJ& threej_msdc,\n    std::vector<std::optional<WignerThreeJ>>& threej_sb0s,\n    std::vector<WignerThreeJ>& threej_pqs,\n    std::vector<std::optional<WignerThreeJ>>& threej_rs,\n    const std::vector<int>& mbars,\n    const std::array<helpers::BasisVector, 3>& src_bvs,\n    const std::array<helpers::BasisVector, 3>& dest_bvs,\n    const size_t src_multiplicity, const int sign_all_s,\n    const int sign_delta_sb, const int sign_delta_sb_conj,\n    const CoefficientNormalization coefficient_normalization) {\n  const auto add_element = [&filler, &iter_src, &iter_dest, src_comp_index,\n                            dest_comp_index](const double element) {\n    const size_t indx_dest =\n        iter_dest() + dest_comp_index * iter_dest.spherepack_array_size();\n    const size_t indx_src =\n        iter_src() + src_comp_index * iter_src.spherepack_array_size();\n    filler.add(element, indx_dest, indx_src);\n  };\n  size_t mbar_indx = 0;\n  for (int p = -1; p <= 1; p += 2) {\n    for (int q = -1; q <= 1; q += 2, ++mbar_indx) {\n      const int sign_mbar =\n          ((sbar + scheck - mbars[mbar_indx]) % 2 == 0 ? 1 : -1);\n      const std::complex<double> kj12 =\n          helpers::bv_to_k(dest_bvs[1], p) * helpers::bv_to_k(dest_bvs[2], q);\n      for (size_t lbar = static_cast<size_t>(\n               std::max(std::abs(mbars[mbar_indx]), std::max(0, abs(sbar))));\n           lbar <= 2; ++lbar) {\n        const double threej_ones_sbar_val = threej_ones_sbars\n            [static_cast<size_t>(helpers::bv_to_s(src_bvs[2]) + 1) +\n             3 * static_cast<size_t>(helpers::bv_to_s(src_bvs[1]) + 1)](lbar);\n        const double threej_pq_val = threej_pqs[mbar_indx](lbar);\n        const int lbar_term =\n            static_cast<int>((2 * lbar + 1) * (2 * lcheck + 1));\n        const double threej_sb0_val = threej_sb0s[lbar].value()(lcheck);\n        const double symm_factor =\n            helpers::get_symm_factor<Symm>(src_multiplicity, lbar);\n        if (symm_factor != 0.0 and threej_pq_val != 0.0 and\n            threej_sb0_val != 0.0 and threej_ones_sbar_val != 0.0) {\n          for (int r = -1; r <= 1; r += 2) {\n            const int mr = helpers::bv_to_m(dest_bvs[0], r);\n            if (mcheck == mbars[mbar_indx] + mr) {\n              const int m_src = m_dest - mcheck;\n              const int sign_m_src = (m_src % 2 == 0 ? 1 : -1);\n              const auto sign_m = helpers::sign_m<double>(\n                  m_src + m_dest, coefficient_normalization);\n              const std::complex<double> kj =\n                  helpers::bv_to_k(dest_bvs[0], r) * kj12;\n              const double threej_r_val =\n                  threej_rs[lbar + 3 * static_cast<size_t>((r + 1) / 2) +\n                            6 * mbar_indx]\n                      .value()(lcheck);\n              for (size_t l_src =\n                       std::max(threej_ssum.l1_min(),\n                                std::max(static_cast<size_t>(std::abs(m_src)),\n                                         threej_msdc.l1_min()));\n                   (l_src <=\n                        std::min(threej_msdc.l1_max(), threej_ssum.l1_max()) and\n                    l_src <= ell_max);\n                   ++l_src) {\n                const double threej_msdc_val = threej_msdc(l_src);\n                const double threej_ssum_val = threej_ssum(l_src);\n                const double sqterm = sqrt(\n                    static_cast<double>((2 * l_dest + 1) * (2 * l_src + 1)) *\n                    0.125);\n                const int sign_toprow =\n                    ((l_src + l_dest + 1) % 2 == 0 ? 1 : -1);\n                const double coef_without_kj =\n                    sign_m * sqterm * threej_ones_sbar_val * threej_ssum_val *\n                    threej_pq_val * threej_r_val * threej_sb0_val *\n                    threej_msdc_val * symm_factor * lbar_term;\n                const int sign_plus_msrc_term =\n                    sign_m_src * sign_delta_sb * sign_mbar;\n                const int sign_min_msrc_term =\n                    sign_delta_sb_conj * sign_mbar * sign_toprow * sign_all_s;\n                if (m_src >= 0) {\n                  if (kj.imag() == 0) {\n                    // Main term.\n                    if (iter_dest.coefficient_array() ==\n                        SpherepackIterator::CoefficientArray::a) {\n                      // ReRe\n                      iter_src.set(l_src, static_cast<size_t>(m_src),\n                                   SpherepackIterator::CoefficientArray::a);\n                      add_element(kj.real() * coef_without_kj *\n                                  sign_plus_msrc_term);\n                    } else if (iter_dest.coefficient_array() ==\n                               SpherepackIterator::CoefficientArray::b) {\n                      // ImIm\n                      iter_src.set(l_src, static_cast<size_t>(m_src),\n                                   SpherepackIterator::CoefficientArray::b);\n                      add_element(kj.real() * coef_without_kj *\n                                  sign_plus_msrc_term);\n                    }\n                  } else {\n                    if (iter_dest.coefficient_array() ==\n                        SpherepackIterator::CoefficientArray::a) {\n                      // ReIm\n                      iter_src.set(l_src, static_cast<size_t>(m_src),\n                                   SpherepackIterator::CoefficientArray::b);\n                      add_element(-kj.imag() * coef_without_kj *\n                                  sign_plus_msrc_term);\n                    } else if (iter_dest.coefficient_array() ==\n                               SpherepackIterator::CoefficientArray::b) {\n                      // ImRe\n                      iter_src.set(l_src, static_cast<size_t>(m_src),\n                                   SpherepackIterator::CoefficientArray::a);\n                      add_element(kj.imag() * coef_without_kj *\n                                  sign_plus_msrc_term);\n                    }\n                  }\n                } else {\n                  if (kj.imag() == 0) {\n                    // Other term, for negative m_src.\n                    if (iter_dest.coefficient_array() ==\n                        SpherepackIterator::CoefficientArray::a) {\n                      // ReRe\n                      iter_src.set(l_src, static_cast<size_t>(-m_src),\n                                   SpherepackIterator::CoefficientArray::a);\n                      add_element(kj.real() * coef_without_kj *\n                                  sign_min_msrc_term);\n                    } else if (iter_dest.coefficient_array() ==\n                               SpherepackIterator::CoefficientArray::b) {\n                      // ImIm\n                      iter_src.set(l_src, static_cast<size_t>(-m_src),\n                                   SpherepackIterator::CoefficientArray::b);\n                      add_element(-kj.real() * coef_without_kj *\n                                  sign_min_msrc_term);\n                    }\n                  } else {\n                    if (iter_dest.coefficient_array() ==\n                        SpherepackIterator::CoefficientArray::a) {\n                      // ReIm\n                      iter_src.set(l_src, static_cast<size_t>(-m_src),\n                                   SpherepackIterator::CoefficientArray::b);\n                      add_element(kj.imag() * coef_without_kj *\n                                  sign_min_msrc_term);\n                    } else if (iter_dest.coefficient_array() ==\n                               SpherepackIterator::CoefficientArray::b) {\n                      // ImRe\n                      iter_src.set(l_src, static_cast<size_t>(-m_src),\n                                   SpherepackIterator::CoefficientArray::a);\n                      add_element(kj.imag() * coef_without_kj *\n                                  sign_min_msrc_term);\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid fill_sphere_to_cart(\n    const gsl::not_null<SparseMatrixType*> matrix, const size_t ell_max,\n    const CoefficientNormalization coefficient_normalization) {\n  static constexpr size_t num_independent_components = TensorStructure::size();\n  static constexpr size_t rank = TensorStructure::rank();\n  static constexpr auto tensor_index_list =\n      TensorStructure::storage_to_tensor_index();\n\n  static_assert(rank > 0 and rank < 4, \"Implemented only for ranks 1,2,3\");\n\n  SpherepackIterator iter_src(ell_max, ell_max, 1, false);\n  SpherepackIterator iter_dest(ell_max, ell_max, 1, false);\n  SparseMatrixFiller filler(square(num_independent_components) *\n                                iter_src.spherepack_array_size() *\n                                iter_dest.spherepack_array_size(),\n                            true, 1.0);\n\n  // We allocate space for some (not all) of the 3J symbols here, so\n  // that they can be used and re-used later inside the inner loops.\n  std::vector<WignerThreeJ> threej_pqs;  // NOLINT(misc-const-correctness)\n  std::vector<int> mbars;                // NOLINT(misc-const-correctness)\n  if constexpr (rank > 1) {\n    threej_pqs.reserve(4);\n    mbars.reserve(4);\n  } else {\n    // For rank 1 we don't need threej_pqs or mbars but we\n    // need to declare them anyway for scoping; they will just be unused. We\n    // will compute the rank-1 3J symbols below, and below we will also compute\n    // additional 3J symbols for higher ranks.\n    (void)threej_pqs;\n    (void)mbars;\n  }\n\n  // threej_rs contains std::optionals because some of the Wigner 3Js\n  // are not well-defined (because |m| > ell).  The ones without\n  // values are never used or referenced.  It is possible to compute\n  // fewer than 24 threej_rs, by pushing_back only those that have\n  // values, but then it is difficult to index the threej_rs.  So\n  // keeping 24 of them and making them std::optionals is easier.\n  //\n  // Same thing is true for threej_sb0s.\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<std::optional<WignerThreeJ>> threej_rs;\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<std::optional<WignerThreeJ>> threej_sb0s;\n  if constexpr (rank > 2) {\n    threej_rs.reserve(24);\n    threej_sb0s.reserve(3);\n  } else {\n    // Unneeded except for rank 3.\n    (void)threej_rs;\n    (void)threej_sb0s;\n  }\n\n  // Same thing for threej_mbars, but for rank 2.\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<std::optional<WignerThreeJ>> threej_mbars;\n  if constexpr (rank == 2) {\n    threej_mbars.reserve(4);\n  } else {\n    // Unneeded except for rank 2.\n    (void)threej_mbars;\n  }\n\n  // threej_ones_sbars is the last 3J symbol in Eq. (32)\n  // and the fourth 3J symbol in Eq. (34)\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::vector<WignerThreeJ> threej_ones_sbars;\n  if constexpr (rank > 1) {\n    threej_ones_sbars.reserve(9);\n    for (int sb0 = -1; sb0 <= 1; ++sb0) {\n      for (int sb1 = -1; sb1 <= 1; ++sb1) {\n        threej_ones_sbars.emplace_back(1, sb0, 1, sb1);\n      }\n    }\n  } else {\n    // Unneeded for rank 1.\n    (void)threej_ones_sbars;\n  }\n\n  for (size_t dest_comp_index = 0; dest_comp_index < num_independent_components;\n       dest_comp_index++) {\n    const auto dest_indices = tensor_index_list[dest_comp_index];\n    const auto dest_bvs = helpers::to_cart_basis_vector(dest_indices);\n\n    if constexpr (rank > 1) {\n      // threej_pqs is (as a function of p and q) the second 3J symbol\n      // in Eq. (22) for rank 2, and the first 3J symbol in Eq. (24) for\n      // rank 3.  It is not used for rank 1.\n      //\n      // mbars are the values of mbar for rank 2 and 3, which are uniquely\n      // determined by p and q because of the symmetries of threej_pqs.\n      threej_pqs.clear();\n      mbars.clear();\n      for (int p = -1; p <= 1; p += 2) {\n        for (int q = -1; q <= 1; q += 2) {\n          mbars.push_back(helpers::bv_to_m(dest_bvs[rank - 2], p) +\n                          helpers::bv_to_m(dest_bvs[rank - 1], q));\n          threej_pqs.emplace_back(1, helpers::bv_to_m(dest_bvs[rank - 2], p), 1,\n                                  helpers::bv_to_m(dest_bvs[rank - 1], q));\n        }\n      }\n    }\n\n    for (size_t src_comp_index = 0; src_comp_index < num_independent_components;\n         src_comp_index++) {\n      const auto src_indices = tensor_index_list[src_comp_index];\n      const auto src_bvs = helpers::to_sphere_basis_vector(src_indices);\n      const int sign_delta_sb =\n          alg::accumulate(src_bvs, 1, [](const double acc, const auto bv) {\n            return acc * (helpers::bv_to_s(bv) == -1 ? -1 : 1);\n          });\n      const int sign_delta_sb_conj =\n          alg::accumulate(src_bvs, 1, [](const double acc, const auto bv) {\n            return acc * (helpers::bv_to_s(bv) == 1 ? -1 : 1);\n          });\n      const size_t src_multiplicity =\n          TensorStructure::multiplicity(src_comp_index);\n\n      // scheck is used only in rank 1 and 3 but is easy to compute in general.\n      const int scheck =\n          -alg::accumulate(src_bvs, 0, [](const double acc, const auto bv) {\n            return acc + helpers::bv_to_s(bv);\n          });\n\n      // sbar is used only for rank 2 and 3.\n      // NOLINTNEXTLINE(misc-const-correctness)\n      int sbar = std::numeric_limits<int>::min();\n      if constexpr (rank > 1) {\n        sbar = -(helpers::bv_to_s(src_bvs[rank - 1]) +\n                 helpers::bv_to_s(src_bvs[rank - 2]));\n      } else {\n        (void)sbar;\n      }\n\n      if constexpr (rank == 3) {\n        // threej_rs is (as a function of r, lbar, mbar) the penultimate\n        // 3J symbol in Eq. (34) for rank 3.  It is not used for\n        // ranks 1 and 2.\n        threej_rs.clear();\n        for (const int mbar : mbars) {\n          for (int r = -1; r <= 1; r += 2) {\n            const int mr = helpers::bv_to_m(dest_bvs[0], r);\n            for (size_t lbar = 0; lbar <= 2; ++lbar) {\n              if (abs(mbar) <= static_cast<int>(lbar)) {\n                threej_rs.emplace_back(WignerThreeJ(1, mr, lbar, mbar));\n              } else {\n                threej_rs.emplace_back(std::nullopt);\n              }\n            }\n          }\n        }\n        // threej_sb0s is the last 3J symbol in Eq. (34)\n        threej_sb0s.clear();\n        for (size_t lbar = 0; lbar <= 2; ++lbar) {\n          if (static_cast<size_t>(abs(sbar)) <= lbar) {\n            threej_sb0s.emplace_back(\n                WignerThreeJ(1, helpers::bv_to_s(src_bvs[0]), lbar, -sbar));\n          } else {\n            threej_sb0s.emplace_back(std::nullopt);\n          }\n        }\n      }\n\n      for (iter_dest.reset(); iter_dest; ++iter_dest) {\n        // In the docs, l_dest and m_dest are just called l and m.\n        // l_src and m_src are called l' and m'.\n        const auto l_dest = static_cast<size_t>(iter_dest.l());\n        const auto m_dest = static_cast<int>(iter_dest.m());\n        if constexpr (rank == 1) {\n          (void)src_multiplicity;  // Unused variable for rank 1\n          const int sign_m_dest = (m_dest % 2 == 0 ? 1 : -1);\n          // threej_s is the first 3j symbol in Eq. (31)\n          WignerThreeJ threej_s(l_dest, 0, 1, -scheck);\n          for (int j = -1; j <= 1; j += 2) {\n            const int mj = helpers::bv_to_m(dest_bvs[0], j);\n            const std::complex<double> kj = helpers::bv_to_k(dest_bvs[0], j);\n            // threej_m is the second 3j symbol in Eq. (31)\n            WignerThreeJ threej_m(l_dest, -m_dest, 1, mj);\n            inner_loops_one(filler, iter_src, iter_dest, src_comp_index,\n                            dest_comp_index, ell_max, l_dest, m_dest, scheck,\n                            mj, kj, threej_m, threej_s, sign_m_dest,\n                            sign_delta_sb, sign_delta_sb_conj,\n                            coefficient_normalization);\n          }\n        } else if constexpr (rank == 2) {\n          (void)scheck;  // unused for rank 2\n          const int sign_sbar = (sbar % 2 == 0 ? 1 : -1);\n          for (size_t lbar = static_cast<size_t>(std::max(0, std::abs(sbar)));\n               lbar <= 2; ++lbar) {\n            const double symm_factor =\n                helpers::get_symm_factor<typename TensorStructure::symmetry>(\n                    src_multiplicity, lbar);\n            if (symm_factor != 0.0) {\n              const double threej_ones_sbar_val = threej_ones_sbars\n                  [static_cast<size_t>(helpers::bv_to_s(src_bvs[1]) + 1) +\n                   3 * static_cast<size_t>(helpers::bv_to_s(src_bvs[0]) + 1)](\n                      lbar);\n              // threej_mbars is the second 3J symbol in Eq. (32)\n              threej_mbars.clear();\n              for (const int mbar : mbars) {\n                if (static_cast<size_t>(std::abs(mbar)) <= lbar) {\n                  threej_mbars.emplace_back(\n                      WignerThreeJ(l_dest, -m_dest, lbar, mbar));\n                } else {\n                  threej_mbars.emplace_back(std::nullopt);\n                }\n              }\n              // threej_ells_sbar is the first 3J symbol in Eq. (32)\n              WignerThreeJ threej_ells_sbar(l_dest, 0, lbar, -sbar);\n              inner_loops_two(filler, iter_src, iter_dest, src_comp_index,\n                              dest_comp_index, ell_max, l_dest, m_dest, lbar,\n                              mbars, threej_mbars, threej_ells_sbar,\n                              symm_factor, dest_bvs, threej_pqs,\n                              threej_ones_sbar_val, sign_sbar, sign_delta_sb,\n                              sign_delta_sb_conj, coefficient_normalization);\n            }\n          }\n        } else if constexpr (rank == 3) {\n          const int sign_all_s = (-scheck % 2 == 0 ? 1 : -1);\n          for (size_t lcheck =\n                   static_cast<size_t>(std::max(0, std::abs(scheck)));\n               lcheck <= 3; ++lcheck) {\n            // threej_ssum is the second 3J symbol in Eq. (34)\n            WignerThreeJ threej_ssum(lcheck, -scheck, l_dest, 0);\n            for (int mcheck = -static_cast<int>(lcheck);\n                 mcheck <= static_cast<int>(lcheck); ++mcheck) {\n              // threej_msdc is the first 3J symbol in Eq. (34)\n              WignerThreeJ threej_msdc(lcheck, mcheck, l_dest, -m_dest);\n              inner_loops_three<typename TensorStructure::symmetry>(\n                  filler, iter_src, iter_dest, src_comp_index, dest_comp_index,\n                  ell_max, l_dest, m_dest, lcheck, mcheck, sbar, scheck,\n                  threej_ones_sbars, threej_ssum, threej_msdc, threej_sb0s,\n                  threej_pqs, threej_rs, mbars, src_bvs, dest_bvs,\n                  src_multiplicity, sign_all_s, sign_delta_sb,\n                  sign_delta_sb_conj, coefficient_normalization);\n            }\n          }\n        }\n      }\n    }\n  }\n  filler.fill(matrix);\n}\n\n// Explicit instantiations\n#define TSTRUCT(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template void fill_sphere_to_cart<typename TSTRUCT(data) < DataVector, \\\n                                    3>::structure >                      \\\n      (gsl::not_null<SimpleSparseMatrix*> matrix, size_t ell_max,        \\\n       CoefficientNormalization coefficient_normalization);              \\\n  template void fill_sphere_to_cart<typename TSTRUCT(data) < DataVector, \\\n                                    3>::structure >                      \\\n      (gsl::not_null<blaze::CompressedMatrix<double, blaze::rowMajor>*>  \\\n           matrix,                                                       \\\n       size_t ell_max, CoefficientNormalization coefficient_normalization);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (tnsr::i, tnsr::ii, tnsr::ij, tnsr::ijk, tnsr::ijj))\n\n#undef INSTANTIATE\n#undef TSTRUCT\n\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/TensorYlmSphereToCart.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp\"\n\n#include <cstddef>\n\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm::TensorYlm {\n\n/*!\n * \\brief Fills a sparse matrix that does a TensorYlm Spherical\n * to Cartesian operation.\n *\n * If the CoefficientNormalization parameter is set to Standard, then\n * assumes that the input, $T^B_{\\ell m}$, is stored in a\n * Tensor<DataVector>.  Multiplying the resulting\n * sparse matrix by the Tensor<DataVector> is equivalent to\n * evaluating the right-hand side of Eq. $(\\ref{eq:S2C})$.\n * If the CoefficientNormalization parameter is set to Spherepack, then\n * assumes the matrix will multiply a Tensor<DataVector> containing\n * ${\\breve T}^B_{\\ell m}$ and the transform operation is\n * equivalent to Eq. $(\\ref{eq:S2CSpherepack})$.\n *\n * We assume that the independent components of the Tensor<DataVector>\n * are stored contiguously in memory, in order of the `storage_index` of\n * the Tensor.  However, we do allow for a stride.  This means that we\n * can point to memory starting with the first element of\n * $T^B_{\\ell m}$ and multiply that by the sparse matrix we compute\n * here, and get the result.\n *\n * The memory layout here is different than in SpEC.  In SpEC, each\n * tensor component is stored in separately-allocated memory, so the\n * SpEC equivalent of the fill_sphere_to_cart function fills $N^2$ sparse\n * matrices, where $N$ is the number of independent components of the\n * Tensor. The advantage of the SpEC method is that each sparse matrix\n * is smaller, so sorting elements into the correct order while\n * constructing each sparse matrix is faster (sorting is > linear in\n * the number of matrix elements).  The disadvantage of the SpEC\n * method is that evaluating the coefficients for a single Tensor involves\n * $N^2$ matrix-vector multiplications, whereas here evaluating the\n * coefficients involves only one matrix-vector multiplication, which should\n * have more efficient memory access.  It is not clear which method is\n * faster overall without more profiling.\n *\n * ## Explicit formulas\n *\n * The following formulas come from Klinger and Scheel, in prep.\n *\n * For rank-1 tensors, the expression for\n * $C^{\\ell' m' \\tilde{A}}_{\\ell m B}$ is\n * \\begin{align}\n * C^{\\ell' m' \\tilde{A}}_{\\ell m B} &=\n * (-1)^{\\delta(s_B,-1)-m}\\sqrt{\\frac{(2 \\ell+1)(2 \\ell'  + 1)}{2}}\n * \\sum_j k_j(\\tilde{A})\n *    \\left(\\begin{array}{ccc}\n *     \\ell & \\ell' & 1 \\cr\n *      0 & -s_B & s_B\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{ccc}\n *     \\ell & \\ell' & 1 \\cr\n *      -m & m' & m_j(\\tilde{A})\n *    \\end{array}\\right),\n * \\end{align}\n * where the 6-element \"matrices\"\n * in parentheses are Wigner 3-J symbols.\n *\n * For second-rank tensors, the expression for\n * $C^{\\ell' m' \\tilde{A}}_{\\ell m B}$ is\n * \\begin{align}\n *  C^{\\ell' m' \\tilde{A}}_{\\ell m B}\n *    &=\n *    \\frac{1}{2}(-1)^{\\delta(s_{B_1},-1)}(-1)^{\\delta(s_{B_2},-1)}(-1)^{m'}\n *    \\sqrt{(2\\ell+1)(2\\ell'+1)}\n *    \\nonumber \\\\\n *    &\\times\n *    \\sum_{p,q,\\bar{\\ell},\\bar{m},\\bar{s}}\n *    (2 \\bar{\\ell}+1) (-1)^{\\bar{s}}\n *    k_p(\\tilde{A}_1) k_q(\\tilde{A}_2) S(\\bar{\\ell},B)\n *    \\left(\\begin{array}{ccc}\n *     \\ell & \\ell' & \\bar{\\ell} \\cr\n *      0&-s_{B_1}-s_{B_2}&-\\bar{s}\n *    \\end{array}\\right)\n *    \\nonumber \\\\\n *    &\\qquad\\times\n *    \\left(\\begin{array}{ccc}\n *     \\ell & \\ell' & \\bar{\\ell} \\cr\n *      -m & m' & \\bar{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{ccc}\n *     1 & 1 & \\bar{\\ell} \\cr\n *      m_p(\\tilde{A}_1)&m_q(\\tilde{A}_2)&-\\bar{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{ccc}\n *     1 & 1 & \\bar{\\ell} \\cr\n *     s_{B_1}&s_{B_2}&\\bar{s}\n *    \\end{array}\\right),\n * \\end{align}\n * where the factor $S(\\bar{\\ell},B)$ is a symmetry factor.\n * For a 2nd-rank tensor with no symmetries, $S(\\bar{\\ell},B)$ is\n * unity, but for a tensor symmetric in $(B_1,B_2)$\n * the matrix elements $C^{\\ell' m' \\tilde{A}}_{\\ell m B}$\n * are multiplied only by tensor components with $B_1\\geq B_2$\n * and the symmetry is accounted for by setting\n * \\begin{align}\n *   S(\\bar{\\ell},B) &=\n *   (1+(-1)^{\\bar{\\ell}})\\frac{2-\\delta(B_1,B_2)}{2}.\n * \\end{align}\n *\n * For third-rank tensors, the expression for\n * $C^{\\ell' m' \\tilde{A}}_{\\ell m B}$ is\n * \\begin{align}\n *  C^{\\ell' m' \\tilde{A}}_{\\ell m B}\n *    &=\n *       (-1)^{m'}\n *       (-1)^{\\delta(s_{B_1},-1)}\n *       (-1)^{\\delta(s_{B_2},-1)}\n *       (-1)^{\\delta(s_{B_3},-1)}\n *    \\sqrt{\\frac{(2\\ell+1)(2\\ell'+1)}{8}}\n *    \\nonumber \\\\\n *    &\\times\n *     \\sum_{p,q,r,\\bar{\\ell},\\bar{m},\\bar{s},\\check{\\ell},\\check{m},\\check{s}}\n *    (2 \\bar{\\ell}+1) (2 \\check{\\ell}+1) (-1)^{\\bar{s}+\\check{s}-\\bar{m}}\n *    k_p(\\tilde{A}_2) k_q(\\tilde{A}_3) k_r(\\tilde{A}_1) S(\\bar{\\ell},B)\n *    \\nonumber \\\\\n *    &\\qquad\\times\n *    \\left(\\begin{array}{ccc}\n *     \\ell & \\ell' & \\check{\\ell} \\cr\n *      -m & m' & \\check{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{ccc}\n *     \\ell & \\ell' & \\check{\\ell} \\cr\n *      0&-s_{B_1}-s_{B_2}-s_{B_3}&-\\check{s}\n *    \\end{array}\\right)\n *    \\nonumber \\\\\n *    &\\qquad\\times\n *    \\left(\\begin{array}{ccc}\n *     1 & 1 & \\bar{\\ell} \\cr\n *     m_p(\\tilde{A}_2)&m_q(\\tilde{A}_3)&-\\bar{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{ccc}\n *     1 & 1 & \\bar{\\ell} \\cr\n *     s_{B_2}&s_{B_3}&\\bar{s}\n *    \\end{array}\\right)\n *    \\nonumber \\\\\n *    &\\qquad\\times\n *    \\left(\\begin{array}{ccc}\n *     1 & \\bar{\\ell} & \\check{\\ell} \\cr\n *     m_r(\\tilde{A}_1)&\\bar{m}&-\\check{m}\n *    \\end{array}\\right)\n *    \\left(\\begin{array}{ccc}\n *     1 & \\bar{\\ell} & \\check{\\ell} \\cr\n *     s_{B_1}&-\\bar{s}&\\check{s}\n *    \\end{array}\\right),\n * \\end{align}\n * As in the rank-2 case,\n * $S(\\bar{\\ell},B)$ is a symmetry factor that is unity\n * for a tensor with no symmetries and is\n * \\begin{align}\n *   S(\\bar{\\ell},B) &=\n *   (1+(-1)^{\\bar{\\ell}})\\frac{2-\\delta(B_2,B_3)}{2}.\n * \\end{align}\n * for a tensor symmetric on its last two indices. We don't consider\n * other symmetries because we don't find them in the cases we care\n * about.\n *\n * \\tparam TensorStructure A Tensor_detail::Structure\n * \\tparam SparseMatrixType A sparse matrix fillable by SparseMatrixFiller\n *\n * \\param matrix The sparse matrix to fill\n * \\param ell_max The maximum ylm ell value\n * \\param coefficient_normalization Describes the normalization of coefficients.\n *\n */\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid fill_sphere_to_cart(gsl::not_null<SparseMatrixType*> matrix,\n                         size_t ell_max,\n                         CoefficientNormalization coefficient_normalization);\n\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/WignerThreeJ.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/WignerThreeJ.hpp\"\n\n#include <limits>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/ExpectsAndEnsures.hpp\"\n\nextern \"C\" {\nvoid drc3jj_(const double& l2, const double& l3, const double& m2,\n             const double& m3, double& l1min, double& l1max, double* answer,\n             const int& answersize, int& error_code, const double& doublemax);\n}\n\nWignerThreeJ::WignerThreeJ(const size_t l2, const int m2, const size_t l3,\n                           const int m3)\n    : l2_(static_cast<double>(l2)),\n      m2_(m2),\n      l3_(static_cast<double>(l3)),\n      m3_(m3),\n      l1_min_(static_cast<size_t>(\n          std::max(std::abs(static_cast<int>(l2) - static_cast<int>(l3)),\n                   std::abs(m2 + m3)))),\n      l1_max_(l2 + l3),\n      up_to_date_(false),\n      coefs_(l1_max_ - l1_min_ + 1) {\n  if (UNLIKELY(std::abs(m2) > static_cast<int>(l2))) {\n    ERROR(\"WignerThreeJ: Must have |m2| <= l2. m2 is \" << m2 << \" but l2 is \"\n                                                       << l2);\n  }\n  if (UNLIKELY(std::abs(m3) > static_cast<int>(l3))) {\n    ERROR(\"WignerThreeJ: Must have |m3| <= l3. m3 is \" << m3 << \" but l3 is \"\n                                                       << l3);\n  }\n}\n\ndouble WignerThreeJ::operator()(const size_t l1) {\n  if (l1 < l1_min_ or l1 > l1_max_) {\n    return 0.0;\n  }\n  if (not up_to_date_) {\n    recompute();\n  }\n  return coefs_[l1 - l1_min_];\n}\n\nvoid WignerThreeJ::recompute() {\n  // error_code, l1min, and l1max are output parameters of drc3jj.\n  // We don't care about l1min and l1max.\n  int error_code = 0;\n  double l1min = 0.0;\n  double l1max = 0.0;\n\n  drc3jj_(l2_, l3_, m2_, m3_, l1min, l1max, coefs_.data(),\n          static_cast<int>(coefs_.size()), error_code,\n          std::numeric_limits<double>::max());\n\n  if (UNLIKELY(error_code != 0)) {\n    ERROR(\"Nonzero error code \"\n          << error_code\n          << \", codes are:\\n\"\n             \"IER=0 No errors.\\n\"\n             \"IER=1 Either L2.LT.ABS(M2) or L3.LT.ABS(M3).\\n\"\n             \"IER=2 Either L2+ABS(M2) or L3+ABS(M3) non-integer.\\n\"\n             \"IER=3 L1MAX-L1MIN not an integer.\\n\"\n             \"IER=4 L1MAX less than L1MIN.\\n\"\n             \"IER=5 NDIM less than L1MAX-L1MIN+1.\\n\");\n  }\n  up_to_date_ = true;\n}\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/WignerThreeJ.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n/*!\n * \\brief Computes Wigner 3J symbols.\n *\n * Given $l_2$, $m_2$, $l_3$, and $m_3$,\n * computes and caches the Wigner 3J symbols\n *\n * \\begin{align}\n * \\left(\n * \\begin{array}{ccc}\n *  l_1 & l_2 & l_3 \\\\\n * -m_3-m_2 & m_2 & m_3\n * \\end{array}\\right)\n * \\end{align}\n *\n * for all $l_1$.\n *\n * Internally, uses recursion relations to efficiently compute many 3J symbols\n * at once.  This should be more efficient than having a single function\n * that takes $l_2$, $m_2$, $l_3$, $m_3$, $l_1$\n * and computes a single 3J symbol, assuming\n * that the user creates WignerThreeJ objects infrequently, and that the user\n * calls each WignerThreeJ object multiple times.\n *\n * Here we limit the quantum numbers to integer values.  It is trivial to\n * extend to half-integer values because the underlying routine (the\n * drc3jj.f function from the slatec library) supports it.\n *\n * Uses lazy evaluation: the constructor computes nothing, but the first time\n * operator() is called for a nonzero 3J symbol, it computes all the\n * nonzero 3J symbols.\n *\n * Internally, allocates a std::vector.  A future optimization may be to\n * pass in a buffer of the appropriate size, so that WignerThreeJ would not\n * need to do memory allocations.\n */\nclass WignerThreeJ {\n public:\n  WignerThreeJ(size_t l2, int m2, size_t l3, int m3);\n  /*!\n   * Returns the Wigner 3J symbol\n   * \\begin{align}\n   * \\left(\n   * \\begin{array}{ccc}\n   *  l_1 & l_2 & l_3 \\\\\n   * -m_3-m_2 & m_2 & m_3\n   * \\end{array}\\right).\n   * \\end{align}\n   *\n   * Internally, computes and caches all nonzero 3J symbols for all $l_1$.\n   * Returns zero for $l_1 < l_1^\\mathrm{min}$ and\n   * $l_1 > l_1^\\mathrm{max}$.\n   */\n  double operator()(size_t l1);\n  /// Returns the smallest value of $l_1$ that gives a nonzero 3J symbol.\n  size_t l1_min() const { return l1_min_; }\n  /// Returns the largest value of $l_1$ that gives a nonzero 3J symbol.\n  size_t l1_max() const { return l1_max_; }\n\n private:\n  void recompute();\n  // The underlying routine takes doubles (to deal with half-integer spins),\n  // so we need to store doubles here.\n  double l2_, m2_, l3_, m3_;\n  size_t l1_min_, l1_max_;\n  bool up_to_date_;\n  std::vector<double> coefs_;\n};\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/YlmToStf.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SphericalHarmonics/YlmToStf.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ylm {\nScalar<double> ylm_to_stf_0(const ModalVector& l0_coefs) {\n  ASSERT(l0_coefs.size() == 1,\n         \"Expected 1 spherical harmonic coefficient for l=0\");\n  return Scalar<double>{l0_coefs[0] * M_2_SQRTPI * 0.25};\n}\n\ntemplate <typename Frame>\ntnsr::i<double, 3, Frame> ylm_to_stf_1(const ModalVector& l1_coefs) {\n  ASSERT(l1_coefs.size() == 3,\n         \"Expected 3 spherical harmonic coefficients for l=1\");\n  // sqrt(3 / pi) / 2\n  const double coef = 0.4886025119029199;\n  tnsr::i<double, 3, Frame> result{};\n  get<0>(result) = coef * l1_coefs[2];\n  get<1>(result) = coef * l1_coefs[0];\n  get<2>(result) = coef * l1_coefs[1];\n  return result;\n}\n\ntemplate <typename Frame>\ntnsr::ii<double, 3, Frame> ylm_to_stf_2(const ModalVector& l2_coefs) {\n  ASSERT(l2_coefs.size() == 5,\n         \"Expected 5 spherical harmonic coefficients for l=2\");\n  // sqrt(5) / (4 * sqrt(pi))\n  const double coef1 = 0.3153915652525199;\n  // sqrt(15) / (4 * sqrt(pi))\n  const double coef2 = 0.5462742152960396;\n\n  tnsr::ii<double, 3, Frame> result{};\n  get<0, 0>(result) = -coef1 * l2_coefs[2] + coef2 * l2_coefs[4];\n  get<1, 0>(result) = coef2 * l2_coefs[0];\n  get<1, 1>(result) = -coef1 * l2_coefs[2] - coef2 * l2_coefs[4];\n  get<2, 0>(result) = coef2 * l2_coefs[3];\n  get<2, 1>(result) = coef2 * l2_coefs[1];\n  get<2, 2>(result) = 2. * coef1 * l2_coefs[2];\n  return result;\n}\n\ntemplate tnsr::i<double, 3, Frame::Grid> ylm_to_stf_1<Frame::Grid>(\n    const ModalVector& l1_coefs);\ntemplate tnsr::i<double, 3, Frame::Inertial> ylm_to_stf_1<Frame::Inertial>(\n    const ModalVector& l1_coefs);\ntemplate tnsr::ii<double, 3, Frame::Grid> ylm_to_stf_2<Frame::Grid>(\n    const ModalVector& l2_coefs);\ntemplate tnsr::ii<double, 3, Frame::Inertial> ylm_to_stf_2<Frame::Inertial>(\n    const ModalVector& l2_coefs);\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SphericalHarmonics/YlmToStf.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ylm {\n/// @{\n/*!\n * \\brief Converts real spherical harmonic coefficients of degree l into a\n * symmetric trace-free tensor of rank l.\n *\n * \\details Spherical harmonics of degree l are equivalent to symmetric\n * trace-free tensors of rank l. This equivalence and the transformation is\n * given e.g. in \\cite Thorne1980, Eqs. (2.10) - (2.14). The conversion\n * coefficients are hard-coded to numerical precision and implemented up to\n * order l=2.\n *\n * The spherical harmonic coefficients are expected to be sorted with ascending\n * m, i.e. (-m, -m+1, ... , m)\n */\nScalar<double> ylm_to_stf_0(const ModalVector& l0_coefs);\n\ntemplate <typename Frame>\ntnsr::i<double, 3, Frame> ylm_to_stf_1(const ModalVector& l1_coefs);\n\ntemplate <typename Frame>\ntnsr::ii<double, 3, Frame> ylm_to_stf_2(const ModalVector& l2_coefs);\n/// @}\n}  // namespace ylm\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SpinWeightedSphericalHarmonics)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ComplexDataView.cpp\n  SwshCoefficients.cpp\n  SwshCollocation.cpp\n  SwshDerivatives.cpp\n  SwshFiltering.cpp\n  SwshInterpolation.cpp\n  SwshTags.cpp\n  SwshTransform.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComplexDataView.hpp\n  SwshCoefficients.hpp\n  SwshCollocation.hpp\n  SwshDerivatives.hpp\n  SwshFiltering.hpp\n  SwshInterpolation.hpp\n  SwshSettings.hpp\n  SwshTags.hpp\n  SwshTransform.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Libsharp\n  Options\n  Spectral\n  PRIVATE\n  Boost::boost\n  )\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace Spectral {\nnamespace Swsh {\nnamespace detail {\n\ntemplate <typename T>\nvoid sizes_assert(const T& vector, const size_t size) {\n  ASSERT(vector.size() == size,\n         \"Assignment must be to the same size,\"\n         \" not \"\n             << vector.size() << \" assigned to \" << size);\n}\n\n// The constructor of `ComplexDataView<ComplexRepresentation::Interleaved>`\n// makes no copies or allocations\n\n// Due to the nature of this class performing manual memory management, indexing\n// into complex data with perscribed strides, it is necessary to violate type\n// system conventions to index into the complex data as though it is double\n// data, thus using a `reinterpret_cast`. The C++ standard guarantees that\n// complex numbers are represented in memory in such a way that this is correct,\n// despite the type subversion. We also cannot take the address of the\n// components of the complex data, as those are returned by value, not lvalue\n// reference.\ntemplate <>\nComplexDataView<ComplexRepresentation::Interleaved>::ComplexDataView(\n    const gsl::not_null<ComplexDataVector*> vector, const size_t size,\n    const size_t offset)\n    : size_{size},\n      real_slices_up_to_date_{false},\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n      data_real_{reinterpret_cast<double*>(vector->data() + offset)},\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      data_imag_{data_real_ + 1} {\n  complex_view_.set_data_ref(vector->data() + offset, size);\n}\n\n// Due to the nature of this class performing manual memory management, indexing\n// into complex data with perscribed strides, it is necessary to violate type\n// system conventions to index into the complex data as though it is double\n// data, thus using a `reinterpret_cast`. The C++ standard guarantees that\n// complex numbers are represented in memory in such a way that this is correct,\n// despite the type subversion. We also cannot take the address of the\n// components of the complex data, as those are returned by value, not lvalue\n// reference.\ntemplate <>\nComplexDataView<ComplexRepresentation::Interleaved>::ComplexDataView(\n    std::complex<double>* const start, const size_t size)\n    : size_{size},\n      real_slices_up_to_date_{false},\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n      data_real_{reinterpret_cast<double*>(start)},\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      data_imag_{data_real_ + 1} {\n  complex_view_.set_data_ref(start, size);\n}\n\n// RealsThenImags constructors cause an allocation and a copy for each of the\n// real and imaginary components of the vector. If it becomes a performance\n// concern, the `real_slice_` and `imag_slice_` could be made as non-owning\n// DataVectors which reference a larger block of memory allocated only once.\ntemplate <>\nComplexDataView<ComplexRepresentation::RealsThenImags>::ComplexDataView(\n    const gsl::not_null<ComplexDataVector*> vector, const size_t size,\n    const size_t offset)\n    : size_{size}, real_slices_up_to_date_{true}, complex_view_{} {\n  complex_view_.set_data_ref(vector->data() + offset, size_);\n  real_slice_ = real(complex_view_);\n  imag_slice_ = imag(complex_view_);\n  data_real_ = real_slice_.data();\n  data_imag_ = imag_slice_.data();\n}\n\ntemplate <>\nComplexDataView<ComplexRepresentation::RealsThenImags>::ComplexDataView(\n    std::complex<double>* const start, const size_t size)\n    : size_{size}, real_slices_up_to_date_{true}, complex_view_{} {\n  complex_view_.set_data_ref(start, size_);\n  real_slice_ = real(complex_view_);\n  imag_slice_ = imag(complex_view_);\n  data_real_ = real_slice_.data();\n  data_imag_ = imag_slice_.data();\n}\n\n// For `ComplexDataView<ComplexRepresentation::Interleaved>`, assigning\n// individual components is comparatively slow, and is currently implemented as\n// loop with the appropriate stride in the contiguous view.\n\ntemplate <>\nComplexDataView<ComplexRepresentation::Interleaved>&\nComplexDataView<ComplexRepresentation::Interleaved>::assign_real(\n    const DataVector& vector) {\n  if (real_slice_.data() != vector.data() or not real_slices_up_to_date_) {\n    sizes_assert(vector, size_);\n    for (size_t i = 0; i < size_; i++) {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      data_real_[stride_ * i] = vector[i];\n    }\n    real_slices_up_to_date_ = false;\n  }\n  return *this;\n}\n\ntemplate <>\nComplexDataView<ComplexRepresentation::Interleaved>&\nComplexDataView<ComplexRepresentation::Interleaved>::assign_imag(\n    const DataVector& vector) {\n  if (imag_slice_.data() != vector.data() or not real_slices_up_to_date_) {\n    sizes_assert(vector, size_);\n    for (size_t i = 0; i < size_; i++) {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      data_imag_[stride_ * i] = vector[i];\n    }\n    real_slices_up_to_date_ = false;\n  }\n  return *this;\n}\n\n// For `ComplexDataView<ComplexRepresentation::RealsThenImags>` views, the\n// assignment to individual components assigns directly to the internal\n// DataVectors of the separate components, which is comparatively fast.\ntemplate <>\nComplexDataView<ComplexRepresentation::RealsThenImags>&\nComplexDataView<ComplexRepresentation::RealsThenImags>::assign_real(\n    const DataVector& vector) {\n  if (real_slice_.data() != vector.data()) {\n    sizes_assert(vector, size_);\n    real_slice_ = vector;\n  }\n  return *this;\n}\n\ntemplate <>\nComplexDataView<ComplexRepresentation::RealsThenImags>&\nComplexDataView<ComplexRepresentation::RealsThenImags>::assign_imag(\n    const DataVector& vector) {\n  if (imag_slice_.data() != vector.data()) {\n    sizes_assert(vector, size_);\n    imag_slice_ = vector;\n  }\n  return *this;\n}\n\n// `ComplexDataView<ComplexRepresentation::Interleaved>` views are simply\n// references back to the source data, therefore the source is perpetually in\n// sync with the view. This function is a no-op.\ntemplate <>\nvoid ComplexDataView<\n    ComplexRepresentation::Interleaved>::copy_back_to_source() {}\n\n// `ComplexDataView<ComplexRepresentation::RealsThenImags>` views perform a true\n// copy back to the source vector when this function is called\ntemplate <>\nvoid ComplexDataView<\n    ComplexRepresentation::RealsThenImags>::copy_back_to_source() {\n  for (size_t i = 0; i < size_; i++) {\n    complex_view_[i] = std::complex<double>{real_slice_[i], imag_slice_[i]};\n  }\n}\n\n// `ComplexDataView<ComplexRepresentation::Interleaved>` views perform\n// assignments from complex vectors comparatively quickly, via the assignments\n// using the underlying complex vector types.\ntemplate <>\nComplexDataView<ComplexRepresentation::Interleaved>&\nComplexDataView<ComplexRepresentation::Interleaved>::operator=(\n    const ComplexDataVector& vector) {\n  if (complex_view_.data() != vector.data()) {\n    sizes_assert(vector, size_);\n    complex_view_ = vector;\n    real_slices_up_to_date_ = false;\n  }\n  return *this;\n}\n\ntemplate <>\nComplexDataView<ComplexRepresentation::Interleaved>&\nComplexDataView<ComplexRepresentation::Interleaved>::operator=(\n    const ComplexDataView<ComplexRepresentation::Interleaved>& view) {\n  if (this != &view) {\n    sizes_assert(view, size_);\n    complex_view_ = view.complex_view_;\n    real_slices_up_to_date_ = false;\n  }\n  return *this;\n}\n\ntemplate <>\nvoid ComplexDataView<ComplexRepresentation::Interleaved>::conjugate() {\n  complex_view_ = conj(complex_view_);\n  real_slices_up_to_date_ = false;\n}\n\n// `ComplexDataView<ComplexRepresentation::RealsThenImags>` views perform\n// assignments from complex vectors comparatively slowly. The complex data must\n// be separately converted to the real and imaginary components in the form of\n// DataVectors, then stored in the appropriate internal components.\ntemplate <>\nComplexDataView<ComplexRepresentation::RealsThenImags>&\nComplexDataView<ComplexRepresentation::RealsThenImags>::operator=(\n    const ComplexDataVector& vector) {\n  sizes_assert(vector, size_);\n  real_slice_ = real(vector);\n  imag_slice_ = imag(vector);\n  return *this;\n}\n\ntemplate <>\nComplexDataView<ComplexRepresentation::RealsThenImags>&\nComplexDataView<ComplexRepresentation::RealsThenImags>::operator=(\n    const ComplexDataView<ComplexRepresentation::RealsThenImags>& view) {\n  sizes_assert(view, size_);\n  if (real_slice_.data() != view.real_slice_.data()) {\n    real_slice_ = view.real_slice_;\n  }\n  if (imag_slice_.data() != view.imag_slice_.data()) {\n    imag_slice_ = view.imag_slice_;\n  }\n  return *this;\n}\n\ntemplate <>\nvoid ComplexDataView<ComplexRepresentation::RealsThenImags>::conjugate() {\n  imag_slice_ = -imag_slice_;\n}\n\n// `ComplexDataView<ComplexRepresentation::Interleaved>` views have the\n// individual pointer positions only one double length apart, with a memory\n// stride of 2, as the real and imaginary parts alternate in memory.\ntemplate <>\ndouble* ComplexDataView<ComplexRepresentation::Interleaved>::real_data() {\n  return data_real_;\n}\n\ntemplate <>\nconst double* ComplexDataView<ComplexRepresentation::Interleaved>::real_data()\n    const {\n  return data_real_;\n}\n\ntemplate <>\ndouble* ComplexDataView<ComplexRepresentation::Interleaved>::imag_data() {\n  return data_imag_;\n}\n\ntemplate <>\nconst double* ComplexDataView<ComplexRepresentation::Interleaved>::imag_data()\n    const {\n  return data_imag_;\n}\n\n// `ComplexDataView<ComplexRepresentation::RealsThenImags>` views have\n// potentially very different pointer positions for the start of the individual\n// vectors, but have a memory stride of 1, as each of the two blocks is a\n// contiguous representation.\ntemplate <>\ndouble* ComplexDataView<ComplexRepresentation::RealsThenImags>::real_data() {\n  return real_slice_.data();\n}\n\ntemplate <>\nconst double*\nComplexDataView<ComplexRepresentation::RealsThenImags>::real_data() const {\n  return real_slice_.data();\n}\n\ntemplate <>\ndouble* ComplexDataView<ComplexRepresentation::RealsThenImags>::imag_data() {\n  return imag_slice_.data();\n}\n\ntemplate <>\nconst double*\nComplexDataView<ComplexRepresentation::RealsThenImags>::imag_data() const {\n  return imag_slice_.data();\n}\n}  // namespace detail\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Spectral {\n/// \\ingroup SpectralGroup\n/// Namespace for spin-weighted spherical harmonic utilities.\nnamespace Swsh {\n\n/// \\brief A set of labels for the possible representations of complex numbers\n/// for the `ComplexDataView` and the computations performed in the\n/// spin-weighted spherical harmonic transform library.\n///\n/// \\details The representation describes one of two behaviors:\n///  - `Interleaved`: The vectors of complex numbers will be represented by\n///  alternating doubles in memory. This causes both the real and imaginary part\n///  at a given gridpoint to be near one another, but successive real values\n///  farther. This is the native representation of complex data in the C++\n///  standard, and is the representation needed for Blaze math\n///  operations. Therefore, using this representation type in libsharp\n///  computations will cause operations which access only the real or imaginary\n///  parts individually to trace over larger memory regions. However, this\n///  representation will give rise to fewer copying operations to perform the\n///  libsharp operations.\n///\n///  - `RealsThenImags`: The vectors of complex numbers will primarily be\n///  represented by a pair of vectors of doubles, one for the real values and\n///  one for the imaginary values (the full computation cannot be performed\n///  exclusively in this representation, as it must return to a vector of\n///  `std::complex<double>` for Blaze math operations). This causes the\n///  successive real values for different gridpoints to be closer in memory, but\n///  the real and imaginary parts for a given gridpoint to be farther in\n///  memory. This is not the native representation for complex data in C++, so\n///  the data must be transformed between operations which use Blaze and the\n///  transform operations which use `RealsThenImags`. Therefore, using this\n///  representation in libsharp computations will cause operations which act on\n///  real or imaginary parts individually to have better memory locality (so\n///  likely improved cache performance, but such statements are highly\n///  hardware-dependent). However, this representation will give rise to more\n///  copying operations to perform the libsharp operations.\n///\n/// \\note The pair of representations is provided as a means to 'turn a dial' in\n/// optimizations. It is unclear which of these representations will be\n/// preferable, and it may well be the case that different representations are\n/// better for different calculation types or different hardware. Therefore,\n/// when optimizing code which uses libsharp, it is encouraged to profile the\n/// cost of each representation for a computation and choose the one which\n/// performs best.\nenum class ComplexRepresentation { Interleaved, RealsThenImags };\n\nnamespace detail {\n// A storage container for storing sub-vector references (\"views\") of a\n// ComplexDataVector.\n//\n// This class takes as a template argument a ComplexRepresentation to use when\n// returning pointers to the components of the complex data. The representation\n// is either:\n// - `ComplexRepresentation::Interleaved`, which indicates that the complex data\n// is represented as a vector of `std::complex<double>`, and then\n// ComplexDataView acts as a view (a pure reference to a subvector).\n// - `ComplexRepresentation::RealsThenImags`, which indicates that the complex\n// data is represented as a pair of vectors of `double`. This is no longer a\n// strict view in the typical sense, as the memory layout is different from\n// the data which it references. In order to minimize copies, the user must\n// specify when edits to the ComplexDataView are complete by calling the\n// `copy_back_to_source()` member function.\n//\n// Warning: For optimization reasons, mutations applied via member functions or\n// manipulations of data pointed to by pointers returned by member functions\n// *may or may not* be immediately applied to the source vector used to\n// construct the ComplexDataView. Correct use of this class will first perform\n// (potentially several) manipulations of the data via the ComplexDataView, then\n// as a final step flush the data to the source vector via the member function\n// `copy_back_to_source()`. At that point, the vector is guaranteed to be\n// placed in the same state as the ComplexDataView. **The process of\n// constructing a ComplexDataView, then mutating data in both the\n// ComplexDataView and the source vector, then calling `copy_back_to_source()`\n// is considered undefined behavior, and depends on the details of the memory\n// layout chosen.**\ntemplate <ComplexRepresentation Representation>\nclass ComplexDataView {\n public:\n  // The Representation determines the internal data representation\n  static const ComplexRepresentation complex_representation = Representation;\n\n  // The internal storage type\n  using value_type = std::complex<double>;\n\n  // Construct a view which starts at index `offset` of the supplied\n  // vector, and extends for `size` elements\n  ComplexDataView(gsl::not_null<ComplexDataVector*> vector, size_t size,\n                  size_t offset = 0);\n\n  // Construct a view which starts at pointer `start` and extends for\n  // `size` elements. Need not be a part of a ComplexDataVector.\n  ComplexDataView(std::complex<double>* start, size_t size);\n\n  // For the lifetime of the data view, it points to the same portion of a\n  // single vector. We disallow default move assignment, as it would exhibit\n  // behavior that would contradict that. All assignment operations permitted by\n  // the ComplexDataView are copying operations which act only on the data, not\n  // the reference.\n  ComplexDataView() = delete;\n  ComplexDataView(const ComplexDataView&) = default;\n  ComplexDataView(ComplexDataView&&) = default;\n  ComplexDataView operator=(ComplexDataView&&) = delete;\n  ~ComplexDataView() = default;\n\n  // assign into the view the values of a same-sized ComplexDataVector\n  ComplexDataView<Representation>& operator=(const ComplexDataVector& vector);\n\n  // Assign into the view the values from a same-sized view\n  ComplexDataView<Representation>& operator=(\n      const ComplexDataView<Representation>& view);\n\n  // Conjugate the data.\n  void conjugate();\n\n  // Assign into the real components of a view the values from a\n  // provided `DataVector`\n  ComplexDataView<Representation>& assign_real(const DataVector& vector);\n\n  // Assign into the imaginary components of a view the values from a\n  // provided `DataVector`\n  ComplexDataView<Representation>& assign_imag(const DataVector& vector);\n\n  // Gets the size of the view (the number of complex entries).\n  size_t size() const { return size_; }\n\n  // Gets the stride between successive real or successive imaginary components\n  // in memory.\n  static constexpr size_t stride() { return stride_; }\n\n  // Gets the raw pointer to the start of the real data, which are separated\n  // from one another by `stride()`.\n  double* real_data();\n  const double* real_data() const;\n\n  // Gets the raw pointer to the start of the imaginary data, which are\n  // separated from one another by `stride()`.\n  double* imag_data();\n  const double* imag_data() const;\n\n  // A function for manually flushing the data back to the source. This class\n  // minimizes copies wherever possible, so the manual control of bringing the\n  // data back to the source vector only when necessary is important for\n  // optimization.\n  // Warning: Until this function is called, mutations applied via other member\n  // functions or mutations applied via the pointers obtained from other member\n  // functions are not guaranteed to be applied to the source vector.\n  void copy_back_to_source();\n\n private:\n  size_t size_;\n\n  static const size_t stride_ =\n      Representation == ComplexRepresentation::RealsThenImags ? 1 : 2;\n\n  // In either case, we want to avoid unnecessary copies, so we track whether\n  // the data has been copied from one representation to the other.\n  bool real_slices_up_to_date_;\n  // These two DataVectors are unused in the case of `Interleaved`\n  // representation\n  DataVector real_slice_;\n  DataVector imag_slice_;\n\n  double* data_real_;\n  double* data_imag_;\n\n  ComplexDataVector complex_view_;\n};\n\n}  // namespace detail\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details\n\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n\n#include <array>\n#include <cmath>\n#include <ostream>\n#include <sharp_cxx.h>\n#include <utility>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Spectral::Swsh {\n\nCoefficientsMetadata::CoefficientsMetadata(const size_t l_max) : l_max_(l_max) {\n  sharp_alm_info* alm_to_initialize = nullptr;\n  sharp_make_triangular_alm_info(l_max, l_max, 1, &alm_to_initialize);\n  alm_info_.reset(alm_to_initialize);\n}\n\nconst CoefficientsMetadata& cached_coefficients_metadata(const size_t l_max) {\n  const static auto lazy_coefficients_cache =\n      make_static_cache<CacheRange<0_st, detail::coefficients_maximum_l_max>>(\n          [](const size_t generator_l_max) {\n            return CoefficientsMetadata{generator_l_max};\n          });\n  return lazy_coefficients_cache(l_max);\n}\n\ntemplate <int Spin>\nstd::complex<double> libsharp_mode_to_goldberg_plus_m(\n    const LibsharpCoefficientInfo& coefficient_info,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes,\n    const size_t radial_offset) {\n  return sharp_swsh_sign(Spin, coefficient_info.m, true) *\n             libsharp_modes\n                 .data()[radial_offset * size_of_libsharp_coefficient_vector(\n                                             coefficient_info.l_max) +\n                         coefficient_info.transform_of_real_part_offset] +\n         std::complex<double>(0.0, 1.0) *\n             sharp_swsh_sign(Spin, coefficient_info.m, false) *\n             libsharp_modes\n                 .data()[radial_offset * size_of_libsharp_coefficient_vector(\n                                             coefficient_info.l_max) +\n                         coefficient_info.transform_of_imag_part_offset];\n}\n\ntemplate <int Spin>\nstd::complex<double> libsharp_mode_to_goldberg_minus_m(\n    const LibsharpCoefficientInfo& coefficient_info,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes,\n    const size_t radial_offset) {\n  return sharp_swsh_sign(Spin, -static_cast<int>(coefficient_info.m), true) *\n             conj(libsharp_modes\n                      .data()[radial_offset *\n                                  size_of_libsharp_coefficient_vector(\n                                      coefficient_info.l_max) +\n                              coefficient_info.transform_of_real_part_offset]) +\n         std::complex<double>(0.0, 1.0) *\n             sharp_swsh_sign(Spin, -static_cast<int>((coefficient_info.m)),\n                             false) *\n             conj(libsharp_modes\n                      .data()[radial_offset *\n                                  size_of_libsharp_coefficient_vector(\n                                      coefficient_info.l_max) +\n                              coefficient_info.transform_of_imag_part_offset]);\n}\n\ntemplate <int Spin>\nstd::complex<double> libsharp_mode_to_goldberg(\n    const size_t l, const int m, const size_t l_max,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes,\n    const size_t radial_offset) {\n  const CoefficientsMetadata::CoefficientsIndexIterator coefficients_iterator{\n      l_max, l, static_cast<size_t>(abs(m))};\n  if (m >= 0) {\n    return libsharp_mode_to_goldberg_plus_m(*coefficients_iterator,\n                                            libsharp_modes, radial_offset);\n  } else {\n    return libsharp_mode_to_goldberg_minus_m(*coefficients_iterator,\n                                             libsharp_modes, radial_offset);\n  }\n}\n\ntemplate <int Spin>\nvoid goldberg_modes_to_libsharp_modes_single_pair(\n    const LibsharpCoefficientInfo& coefficient_info,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> libsharp_modes,\n    const size_t radial_offset,\n    const std::complex<double> goldberg_plus_m_mode_value,\n    std::complex<double> goldberg_minus_m_mode_value) {\n  const auto sign_determinant = static_cast<double>(\n      sharp_swsh_sign(Spin, coefficient_info.m, true) *\n          sharp_swsh_sign(Spin, -static_cast<int>(coefficient_info.m), false) +\n      sharp_swsh_sign(Spin, coefficient_info.m, false) *\n          sharp_swsh_sign(Spin, -static_cast<int>(coefficient_info.m), true));\n  const auto i = std::complex<double>(0.0, 1.0);\n  if (coefficient_info.m == 0) {\n    goldberg_minus_m_mode_value = goldberg_plus_m_mode_value;\n  }\n  libsharp_modes->data()[radial_offset * size_of_libsharp_coefficient_vector(\n                                             coefficient_info.l_max) +\n                         coefficient_info.transform_of_real_part_offset] =\n      (sharp_swsh_sign(Spin, -static_cast<int>(coefficient_info.m), false) *\n           goldberg_plus_m_mode_value +\n       sharp_swsh_sign(Spin, coefficient_info.m, false) *\n           conj(goldberg_minus_m_mode_value)) /\n      sign_determinant;\n\n  libsharp_modes->data()[radial_offset * size_of_libsharp_coefficient_vector(\n                                             coefficient_info.l_max) +\n                         coefficient_info.transform_of_imag_part_offset] =\n      (-i * sharp_swsh_sign(Spin, -static_cast<int>(coefficient_info.m), true) *\n           goldberg_plus_m_mode_value +\n       i * sharp_swsh_sign(Spin, coefficient_info.m, true) *\n           conj(goldberg_minus_m_mode_value)) /\n      sign_determinant;\n}\n\ntemplate <int Spin>\nvoid goldberg_modes_to_libsharp_modes_single_pair(\n    const size_t l, const int m, const size_t l_max,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> libsharp_modes,\n    const size_t radial_offset,\n    const std::complex<double> goldberg_plus_m_mode_value,\n    const std::complex<double> goldberg_minus_m_mode_value) {\n  CoefficientsMetadata::CoefficientsIndexIterator coefficients_iterator{\n      l_max, l, static_cast<size_t>(abs(m))};\n  goldberg_modes_to_libsharp_modes_single_pair(\n      *coefficients_iterator, libsharp_modes, radial_offset,\n      goldberg_plus_m_mode_value, goldberg_minus_m_mode_value);\n}\n\ntemplate <int Spin>\nvoid libsharp_to_goldberg_modes(\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> goldberg_modes,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes,\n    const size_t l_max) {\n  const size_t number_of_radial_grid_points =\n      libsharp_modes.data().size() / size_of_libsharp_coefficient_vector(l_max);\n\n  goldberg_modes->destructive_resize(square(1 + l_max) *\n                                     number_of_radial_grid_points);\n\n  const auto& coefficients_metadata = cached_coefficients_metadata(l_max);\n  for (size_t i = 0; i < number_of_radial_grid_points; ++i) {\n    for (const auto coefficient_info : coefficients_metadata) {\n      goldberg_modes->data()[goldberg_mode_index(\n          coefficient_info.l_max, coefficient_info.l,\n          static_cast<int>(coefficient_info.m), i)] =\n          libsharp_mode_to_goldberg_plus_m(coefficient_info, libsharp_modes, i);\n      goldberg_modes->data()[goldberg_mode_index(\n          coefficient_info.l_max, coefficient_info.l,\n          -static_cast<int>(coefficient_info.m), i)] =\n          libsharp_mode_to_goldberg_minus_m(coefficient_info, libsharp_modes,\n                                            i);\n    }\n  }\n}\n\ntemplate <int Spin>\nSpinWeighted<ComplexModalVector, Spin> libsharp_to_goldberg_modes(\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes,\n    const size_t l_max) {\n  const size_t number_of_radial_grid_points =\n      libsharp_modes.data().size() / size_of_libsharp_coefficient_vector(l_max);\n  SpinWeighted<ComplexModalVector, Spin> result{square(1 + l_max) *\n                                                number_of_radial_grid_points};\n  libsharp_to_goldberg_modes(make_not_null(&result), libsharp_modes, l_max);\n  return result;\n}\n\ntemplate <int Spin>\nvoid goldberg_to_libsharp_modes(\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> libsharp_modes,\n    const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes,\n    const size_t l_max) {\n  const size_t number_of_radial_grid_points =\n      goldberg_modes.data().size() / square(l_max + 1);\n  for (size_t i = 0; i < number_of_radial_grid_points; ++i) {\n    for (const auto mode : cached_coefficients_metadata(l_max)) {\n      goldberg_modes_to_libsharp_modes_single_pair(\n          mode, libsharp_modes, i,\n          goldberg_modes.data()[goldberg_mode_index(\n              l_max, mode.l, static_cast<int>(mode.m), i)],\n          goldberg_modes.data()[goldberg_mode_index(\n              l_max, mode.l, -static_cast<int>(mode.m), i)]);\n    }\n  }\n}\n\ntemplate <int Spin>\nSpinWeighted<ComplexModalVector, Spin> goldberg_to_libsharp_modes(\n    const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes,\n    const size_t l_max) {\n  const size_t number_of_radial_grid_points =\n      goldberg_modes.data().size() / square(l_max + 1);\n  SpinWeighted<ComplexModalVector, Spin> result{\n      size_of_libsharp_coefficient_vector(l_max) *\n      number_of_radial_grid_points};\n  goldberg_to_libsharp_modes(make_not_null(&result), goldberg_modes, l_max);\n  return result;\n}\n\n#define GET_SPIN(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define LIBSHARP_TO_GOLDBERG_INSTANTIATION(r, data)                           \\\n  template std::complex<double> libsharp_mode_to_goldberg_plus_m(             \\\n      const LibsharpCoefficientInfo& coefficient_info,                        \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& libsharp_modes, \\\n      const size_t radial_offset);                                            \\\n  template std::complex<double> libsharp_mode_to_goldberg_minus_m(            \\\n      const LibsharpCoefficientInfo& coefficient_info,                        \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& libsharp_modes, \\\n      const size_t radial_offset);                                            \\\n  template std::complex<double> libsharp_mode_to_goldberg(                    \\\n      const size_t l, const int m, const size_t l_max,                        \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& libsharp_modes, \\\n      const size_t radial_offset);                                            \\\n  template void goldberg_modes_to_libsharp_modes_single_pair(                 \\\n      const LibsharpCoefficientInfo& coefficient_info,                        \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*>  \\\n          libsharp_modes,                                                     \\\n      const size_t radial_offset,                                             \\\n      const std::complex<double> goldberg_plus_m_mode_value,                  \\\n      const std::complex<double> goldberg_minus_m_mode_value);                \\\n  template void goldberg_modes_to_libsharp_modes_single_pair(                 \\\n      const size_t l, const int m, const size_t l_max,                        \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*>  \\\n          libsharp_modes,                                                     \\\n      const size_t radial_offset,                                             \\\n      const std::complex<double> goldberg_plus_m_mode_value,                  \\\n      const std::complex<double> goldberg_minus_m_mode_value);                \\\n  template void libsharp_to_goldberg_modes(                                   \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*>  \\\n          goldberg_modes,                                                     \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& libsharp_modes, \\\n      const size_t l_max);                                                    \\\n  template SpinWeighted<ComplexModalVector, GET_SPIN(data)>                   \\\n  libsharp_to_goldberg_modes(                                                 \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& libsharp_modes, \\\n      const size_t l_max);                                                    \\\n  template SpinWeighted<ComplexModalVector, GET_SPIN(data)>                   \\\n  goldberg_to_libsharp_modes(                                                 \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& goldberg_modes, \\\n      const size_t l_max);                                                    \\\n  template void goldberg_to_libsharp_modes(                                   \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*>  \\\n          libsharp_modes,                                                     \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& goldberg_modes, \\\n      const size_t l_max);\n\nGENERATE_INSTANTIATIONS(LIBSHARP_TO_GOLDBERG_INSTANTIATION, (-2, -1, 0, 1, 2))\n\n#undef LIBSHARP_TO_GOLDBERG_INSTANTIATION\n#undef GET_SPIN\n\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details\n\n#pragma once\n\n#include <complex>\n#include <memory>\n#include <sharp_cxx.h>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshSettings.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Spectral {\nnamespace Swsh {\n\n/// \\ingroup SwshGroup\n/// \\brief Convenience function for determining the number of spin-weighted\n/// spherical harmonics coefficients that are stored for a given `l_max`\n///\n/// \\details This includes the factor of 2 associated with needing\n/// to store both the transform of the real and imaginary parts, so is the\n/// full size of the result of a libsharp swsh transform.\n///\n/// \\note Assumes the triangular libsharp representation is used.\nconstexpr SPECTRE_ALWAYS_INLINE size_t\nsize_of_libsharp_coefficient_vector(const size_t l_max) {\n  return (l_max + 1) * (l_max + 2);  // \"triangular\" representation\n}\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Compute the relative sign change necessary to convert between the\n * libsharp basis for spin weight `from_spin_weight` to the basis for spin\n * weight `to_spin_weight`, for the real component coefficients if `real` is\n * true, otherwise for the imaginary component coefficients. The sign change for\n * a given coefficient is equivalent to the product of\n * `sharp_swsh_sign(from_spin, m, real) * sharp_swsh_sign(to_spin, m,\n * real)`. Due to the form of the signs, it does not end up depending on m (the\n * m's in the power of \\f$-1\\f$'s cancel).\n * For full details of the libsharp sign conventions, see the documentation for\n * TransformJob.\n *\n * \\details The sign change is obtained by the\n * difference between the libsharp convention and the convention which uses:\n *\n * \\f[\n * {}_s Y_{\\ell m} (\\theta, \\phi) = (-1)^m \\sqrt{\\frac{2 l + 1}{4 \\pi}}\n * D^{\\ell}{}_{-m s}(\\phi, \\theta, 0).\n * \\f]\n *\n * See \\cite Goldberg1966uu\n */\nconstexpr SPECTRE_ALWAYS_INLINE double sharp_swsh_sign_change(\n    const int from_spin_weight, const int to_spin_weight, const bool real) {\n  if (real) {\n    return (from_spin_weight == 0 ? -1.0 : 1.0) *\n           (from_spin_weight >= 0 ? -1.0 : 1.0) *\n           ((from_spin_weight < 0 and (from_spin_weight % 2 == 0)) ? -1.0\n                                                                   : 1.0) *\n           (to_spin_weight == 0 ? -1.0 : 1.0) *\n           (to_spin_weight >= 0 ? -1.0 : 1.0) *\n           ((to_spin_weight < 0 and (to_spin_weight % 2 == 0)) ? -1.0 : 1.0);\n  }\n  return (from_spin_weight == 0 ? -1.0 : 1.0) *\n         ((from_spin_weight < 0 and (from_spin_weight % 2 == 0)) ? 1.0 : -1.0) *\n         (to_spin_weight == 0 ? -1.0 : 1.0) *\n         ((to_spin_weight < 0 and (to_spin_weight % 2 == 0)) ? 1.0 : -1.0);\n}\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Compute the sign change between the libsharp convention and the set\n * of spin-weighted spherical harmonics given by the relation to the Wigner\n * rotation matrices.\n *\n * \\details The sign change is obtained via the difference between the libsharp\n * convention and the convention which uses:\n *\n * \\f[\n * {}_s Y_{\\ell m} (\\theta, \\phi) = (-1)^m \\sqrt{\\frac{2 l + 1}{4 \\pi}}\n * D^{\\ell}{}_{-m s}(\\phi, \\theta, 0).\n * \\f]\n *\n * See \\cite Goldberg1966uu.\n * The sign change is computed for the real component coefficients if `real` is\n * true, otherwise for the imaginary component coefficients. For full details on\n * the sign convention used in libsharp, see the documentation for TransformJob.\n * This function outputs the \\f$\\mathrm{sign}(m, s, \\mathrm{real})\\f$ necessary\n * to produce the conversion between Goldberg moments and libsharp moments:\n *\n * \\f{align*}{\n * {}_s Y_{\\ell m}^{\\mathrm{real}, \\mathrm{sharp}}  =& \\mathrm{sign}(m, s,\n * \\mathrm{real=true}) {}_s Y_{\\ell m}^{\\mathrm{Goldberg}}\\\\\n * {}_s Y_{\\ell m}^{\\mathrm{imag}, \\mathrm{sharp}}  =&\n * \\mathrm{sign}(m, s, \\mathrm{real=false}) {}_s Y_{\\ell\n * m}^{\\mathrm{Goldberg}}.\n * \\f}\n *\n * Note that in this equation, the \"real\" and \"imag\" superscripts refer to the\n * set of basis functions used for the decomposition of the real and imaginary\n * part of the spin-weighted collocation points, not real or imaginary parts of\n * the basis functions themselves.\n */\nconstexpr SPECTRE_ALWAYS_INLINE double sharp_swsh_sign(const int spin_weight,\n                                                       const int m,\n                                                       const bool real) {\n  if (real) {\n    if (m >= 0) {\n      return (spin_weight > 0 ? -1.0 : 1.0) *\n             ((spin_weight < 0 and (spin_weight % 2 == 0)) ? -1.0 : 1.0);\n    }\n    return (spin_weight == 0 ? -1.0 : 1.0) *\n           ((spin_weight >= 0 and (m % 2 == 0)) ? -1.0 : 1.0) *\n           (spin_weight < 0 and (m + spin_weight) % 2 == 0 ? -1.0 : 1.0);\n  }\n  if (m >= 0) {\n    return (spin_weight == 0 ? -1.0 : 1.0) *\n           ((spin_weight < 0 and (spin_weight % 2 == 0)) ? 1.0 : -1.0);\n  }\n  return (spin_weight == 0 ? -1.0 : 1.0) *\n         ((spin_weight >= 0 and (m % 2 == 0)) ? -1.0 : 1.0) *\n         ((spin_weight < 0 and (m + spin_weight) % 2 != 0) ? -1.0 : 1.0);\n}\n\nnamespace detail {\nstruct DestroySharpAlm {\n  void operator()(sharp_alm_info* to_delete) {\n    sharp_destroy_alm_info(to_delete);\n  }\n};\n}  // namespace detail\n\n/// Points to a single pair of modes in a libsharp-compatible\n/// spin-weighted spherical harmonic modal representation.\nstruct LibsharpCoefficientInfo {\n  size_t transform_of_real_part_offset;\n  size_t transform_of_imag_part_offset;\n  size_t l_max;\n  size_t l;\n  size_t m;\n};\n\n/*!\n * \\ingroup SwshGroup\n * \\brief A container for libsharp metadata for the spin-weighted spherical\n * harmonics modal representation.\n *\n * \\details\n * The CoefficientsMetadata class acts as a memory-safe container for a\n * `sharp_alm_info*`, required for use of libsharp transform utilities.\n * The libsharp utilities are currently constructed to only provide user\n * functions with collocation data for spin-weighted functions and\n * derivatives.  This class also provides an iterator for\n * easily traversing a libsharp-compatible modal representation.\n *\n * \\note The libsharp representation of coefficients is altered from the\n * standard mathematical definitions in a nontrivial way. There are a number of\n * important features to the data storage of the coefficients.\n * - they are stored as a set of complex values, but each vector of complex\n *   values is the transform of only the real or imaginary part of the\n *   collocation data\n * - because each vector of complex coefficients is related to the transform of\n *   a set of doubles, only (about) half of the m's are stored (m >= 0), because\n *   the remaining m modes are determinable by conjugation from the positive m\n *   modes, given that they represent the transform of a purely real or purely\n *   imaginary collocation quantity\n * - they are stored in an l-varies-fastest, triangular representation. To be\n *   concrete, for an l_max=2, the order of coefficient storage is (l, m):\n *   [(0, 0), (1, 0), (2, 0), (1, 1), (2, 1), (2, 2)]\n * - due to the restriction of representing only the transform of real\n *   quantities, the m=0 modes always have vanishing imaginary component.\n */\nclass CoefficientsMetadata {\n public:\n  /// An iterator for easily traversing a libsharp-compatible spin-weighted\n  /// spherical harmonic modal representation.\n  /// The `operator*()` returns a `LibsharpCoefficientInfo`, which  contains two\n  /// offsets, `transform_of_real_part_offset` and\n  /// `transform_of_imag_part_offset`, and the `l_max`, `l` and `m` associated\n  /// with the values at those offsets.\n  ///\n  /// \\note this currently assumes, as do many of the utilities in this file,\n  /// that the libsharp representation is chosen to be the triangular\n  /// coefficient representation. If alternative representations are desired,\n  /// alterations will be needed.\n  class CoefficientsIndexIterator {\n   public:\n    explicit CoefficientsIndexIterator(const size_t l_max,\n                                       const size_t start_l = 0,\n                                       const size_t start_m = 0)\n        : m_{start_m}, l_{start_l}, l_max_{l_max} {}\n\n    LibsharpCoefficientInfo operator*() const {\n      // permit dereferencing the iterator only if the return represents a\n      // viable location in the coefficient vector\n      ASSERT(l_ <= l_max_ && m_ <= l_max_, \"coefficients iterator overflow\");\n      size_t offset = ((3 + 2 * l_max_ - m_) * m_) / 2 + l_ - m_;\n      return LibsharpCoefficientInfo{\n          offset,\n          offset +\n              Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max_) / 2,\n          l_max_, l_, m_};\n    }\n\n    /// advance the iterator by one position (prefix)\n    CoefficientsIndexIterator& operator++() {\n      // permit altering the iterator only if the new value is between the\n      // anticipated begin and end, inclusive\n      ASSERT(l_ <= l_max_ && m_ <= l_max_, \"coefficients iterator overflow\");\n      if (l_ == l_max_) {\n        ++m_;\n        l_ = m_;\n      } else {\n        ++l_;\n      }\n      return *this;\n    };\n\n    /// advance the iterator by one position (postfix)\n    // NOLINTNEXTLINE(readability-const-return-type)\n    const CoefficientsIndexIterator operator++(int) {\n      auto pre_increment = *this;\n      ++*this;\n      return pre_increment;\n    }\n\n    /// retreat the iterator by one position (prefix)\n    CoefficientsIndexIterator& operator--() {\n      // permit altering the iterator only if the new value is between the\n      // anticipated begin and end, inclusive\n      ASSERT(l_ <= l_max_ + 1 && m_ <= l_max_ + 1,\n             \"coefficients iterator overflow\");\n      if (l_ == m_) {\n        --m_;\n        l_ = l_max_;\n      } else {\n        --l_;\n      }\n      return *this;\n    }\n\n    /// retreat the iterator by one position (postfix)\n    // NOLINTNEXTLINE(readability-const-return-type)\n    const CoefficientsIndexIterator operator--(int) {\n      auto pre_decrement = *this;\n      --*this;\n      return pre_decrement;\n    }\n\n    /// @{\n    /// (In)Equivalence checks the object as well as the l and m current\n    /// position.\n    bool operator==(const CoefficientsIndexIterator& rhs) const {\n      return m_ == rhs.m_ and l_ == rhs.l_ and l_max_ == rhs.l_max_;\n    }\n    bool operator!=(const CoefficientsIndexIterator& rhs) const {\n      return not(*this == rhs);\n    }\n    /// @}\n\n   private:\n    size_t m_;\n    size_t l_;\n    size_t l_max_;\n  };\n\n  explicit CoefficientsMetadata(size_t l_max);\n\n  ~CoefficientsMetadata() = default;\n  CoefficientsMetadata() = default;\n  CoefficientsMetadata(const CoefficientsMetadata&) = delete;\n  CoefficientsMetadata(CoefficientsMetadata&&) = default;\n  CoefficientsMetadata& operator=(const CoefficientsMetadata&) = delete;\n  CoefficientsMetadata& operator=(CoefficientsMetadata&&) = default;\n  sharp_alm_info* get_sharp_alm_info() const { return alm_info_.get(); }\n\n  size_t l_max() const { return l_max_; }\n\n  /// returns the number of (complex) entries in a libsharp-compatible\n  /// coefficients vector. This includes the factor of 2 associated with needing\n  /// to store both the transform of the real and imaginary parts, so is the\n  /// full size of the result of a libsharp swsh transform.\n  size_t size() const { return size_of_libsharp_coefficient_vector(l_max_); }\n\n  /// @{\n  /// \\brief Get a bidirectional iterator to the start of the series of modes.\n  CoefficientsMetadata::CoefficientsIndexIterator begin() const {\n    return CoefficientsIndexIterator(l_max_, 0, 0);\n  }\n  CoefficientsMetadata::CoefficientsIndexIterator cbegin() const {\n    return begin();\n  }\n  /// @}\n\n  /// @{\n  /// \\brief Get a bidirectional iterator to the end of the series of modes.\n  CoefficientsMetadata::CoefficientsIndexIterator end() const {\n    return CoefficientsIndexIterator(l_max_, l_max_ + 1, l_max_ + 1);\n  }\n  CoefficientsMetadata::CoefficientsIndexIterator cend() const { return end(); }\n  /// @}\n\n private:\n  std::unique_ptr<sharp_alm_info, detail::DestroySharpAlm> alm_info_;\n  size_t l_max_ = 0;\n};\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Generation function for obtaining a `CoefficientsMetadata` object\n * which is computed by the libsharp calls only once, then lazily cached as a\n * singleton via a static member of a function template. This is the preferred\n * method for obtaining a `CoefficientsMetadata` when the `l_max` is not very\n * large\n *\n * See the comments in the similar implementation found in `SwshCollocation.hpp`\n * for more details on the lazy cache.\n */\nconst CoefficientsMetadata& cached_coefficients_metadata(size_t l_max);\n\n/// @{\n/*!\n * \\ingroup SwshGroup\n * \\brief Compute the mode coefficient for the convention of\n * \\cite Goldberg1966uu.\n *\n * \\details  See the documentation for `TransformJob` for complete\n * details on the libsharp and Goldberg coefficient representations.\n * This interface takes a `LibsharpCoefficientInfo`, so that iterating over the\n * libsharp data structure and performing computations based on the\n * corresponding Goldberg modes is convenient. Two functions are provided, one\n * for the positive m modes in the Goldberg representation, and one for the\n * negative m modes, as the distinction is not made by the coefficient iterator.\n *\n * \\param coefficient_info An iterator which stores an \\f$l\\f$ and \\f$|m|\\f$\n * for the mode to be converted to the Goldberg form.\n * \\param libsharp_modes The libsharp-compatible modal storage to be accessed\n * \\param radial_offset The index of which angular slice is desired. Modes for\n * concentric spherical shells are stored as consecutive blocks of angular\n * modes.\n */\ntemplate <int Spin>\nstd::complex<double> libsharp_mode_to_goldberg_plus_m(\n    const LibsharpCoefficientInfo& coefficient_info,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes,\n    size_t radial_offset);\n\ntemplate <int Spin>\nstd::complex<double> libsharp_mode_to_goldberg_minus_m(\n    const LibsharpCoefficientInfo& coefficient_info,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes,\n    size_t radial_offset);\n/// @}\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Compute the mode coefficient for the convention of\n * \\cite Goldberg1966uu. See the documentation for `TransformJob` for complete\n * details on the libsharp and Goldberg coefficient representations.\n *\n * \\details This interface extracts the (`l`, `m`) Goldberg mode from the\n * provided libsharp-compatible `libsharp_modes`, at angular slice\n * `radial_offset` (Modes for concentric spherical shells are stored as\n * consecutive blocks of angular modes). `l_max` must also be specified to\n * determine the representation details.\n */\ntemplate <int Spin>\nstd::complex<double> libsharp_mode_to_goldberg(\n    size_t l, int m, size_t l_max,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes,\n    size_t radial_offset);\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Set modes of a libsharp-compatible data structure by specifying the\n * modes in the \\cite Goldberg1966uu representation.\n *\n * \\details This interface takes a `LibsharpCoefficientInfo`, so that iterating\n * over the libsharp data structure and performing edits based on the\n * corresponding Goldberg modes is convenient.\n *\n * \\param coefficient_info An iterator which stores an \\f$l\\f$ and \\f$|m|\\f$\n * for the mode to be written by the input Goldberg modes. Note that both the\n * \\f$+m\\f$ and \\f$-m\\f$ modes must be simultaneously changed, due to the\n * representation details. See discussion in `TransformJob` for details.\n * \\param libsharp_modes The libsharp-compatible modal storage to be altered.\n * \\param radial_offset The index of which angular slice is desired. Modes for\n * concentric spherical shells are stored as consecutive blocks of angular\n * modes.\n * \\param goldberg_plus_m_mode_value The coefficient in the Goldberg\n * representation (\\f$l\\f$, \\f$m\\f$)\n * \\param goldberg_minus_m_mode_value The coefficient in the Goldberg\n * representation (\\f$l\\f$, \\f$-m\\f$)\n */\ntemplate <int Spin>\nvoid goldberg_modes_to_libsharp_modes_single_pair(\n    const LibsharpCoefficientInfo& coefficient_info,\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> libsharp_modes,\n    size_t radial_offset, std::complex<double> goldberg_plus_m_mode_value,\n    std::complex<double> goldberg_minus_m_mode_value);\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Set modes of a libsharp-compatible data structure by specifying the\n * modes in the \\cite Goldberg1966uu representation.\n *\n * \\details This interface sets the (\\f$l\\f$, \\f$\\pm m\\f$), modes in a\n * libsharp-compatible representation from modes specified in the Goldberg\n * representation. Note that both the \\f$+m\\f$ and \\f$-m\\f$ modes must be\n * simultaneously changed, due to the representation details. See discussion in\n * `TransformJob` for details.\n * \\param libsharp_modes The libsharp-compatible modal storage to be altered.\n * \\param radial_offset The index of which angular slice is desired. Modes for\n * concentric spherical shells are stored as consecutive blocks of angular\n * modes.\n * \\param goldberg_plus_m_mode_value The coefficient in the Goldberg\n * representation (\\f$l\\f$, \\f$m\\f$)\n * \\param goldberg_minus_m_mode_value The coefficient in the Goldberg\n * representation (\\f$l\\f$, \\f$-m\\f$)\n * \\param l the spherical harmonic \\f$l\\f$ mode requested\n * \\param m the spherical harmonic \\f$m\\f$ mode requested\n * \\param l_max the `l_max` for the provided coefficient set\n */\ntemplate <int Spin>\nvoid goldberg_modes_to_libsharp_modes_single_pair(\n    size_t l, int m, size_t l_max,\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> libsharp_modes,\n    size_t radial_offset, std::complex<double> goldberg_plus_m_mode_value,\n    std::complex<double> goldberg_minus_m_mode_value);\n\n/// @{\n/*!\n * \\ingroup SwshGroup\n * \\brief Compute the set of Goldberg Spin-weighted spherical harmonic modes (in\n * the convention of  \\cite Goldberg1966uu) from a libsharp-compatible series of\n * modes.\n *\n * \\details Modes for concentric spherical shells are stored as consecutive\n * blocks of angular modes in both representations.\n */\ntemplate <int Spin>\nvoid libsharp_to_goldberg_modes(\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> goldberg_modes,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes, size_t l_max);\n\ntemplate <int Spin>\nSpinWeighted<ComplexModalVector, Spin> libsharp_to_goldberg_modes(\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_modes, size_t l_max);\n/// @}\n\n/// @{\n/*!\n * \\ingroup SwshGroup\n * \\brief Compute the set of libsharp-compatible spin-weighted spherical\n * harmonic modes  from a set of Goldberg modes (following the convention of\n * \\cite Goldberg1966uu)\n *\n * \\details Internally iterates and uses the\n * `goldberg_modes_to_libsharp_modes_single_pair()`. In many applications where\n * it is not necessary to store the full Goldberg representation, memory\n * allocation can be saved by manually performing the iteration and calls to\n * `goldberg_modes_to_libsharp_modes_single_pair()`.\n */\ntemplate <int Spin>\nvoid goldberg_to_libsharp_modes(\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> libsharp_modes,\n    const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes, size_t l_max);\n\ntemplate <int Spin>\nSpinWeighted<ComplexModalVector, Spin> goldberg_to_libsharp_modes(\n    const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes, size_t l_max);\n/// @}\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Returns the index into a vector of modes consistent with\n * \\cite Goldberg1966uu.\n *\n * \\details `radial_offset` is used to index into three-dimensional data in\n * which concentric spherical shells are stored as consecutive blocks of angular\n * modes. Goldberg modes are stored in an m varies fastest, and ascending\n * \\f$m\\f$ and \\f$l\\f$ values. So, the first several modes are (l, m):\n *\n * [(0, 0), (1, -1), (1, 0), (1, 1), (2, -2), ...]\n */\nconstexpr SPECTRE_ALWAYS_INLINE size_t\ngoldberg_mode_index(const size_t l_max, const size_t l, const int m,\n                    const size_t radial_offset = 0) {\n  return static_cast<size_t>(\n      static_cast<int>(square(l_max + 1) * radial_offset + square(l) + l) + m);\n}\n\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n#include <cmath>\n#include <ostream>\n#include <sharp_cxx.h>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Spectral::Swsh {\n\ntemplate <ComplexRepresentation Representation>\nCollocationMetadata<Representation>::CollocationMetadata(const size_t l_max)\n    : l_max_{l_max} {\n  sharp_geom_info* geometry_to_initialize = nullptr;\n  sharp_make_gauss_geom_info(\n      l_max_ + 1, 2 * l_max_ + 1, 0.0,\n      detail::ComplexDataView<Representation>::stride(),\n      detail::ComplexDataView<Representation>::stride() * (2 * l_max_ + 1),\n      &geometry_to_initialize);\n  geom_info_.reset(geometry_to_initialize);\n}\n\ntemplate <ComplexRepresentation Representation>\ndouble CollocationMetadata<Representation>::theta(const size_t offset) const {\n  ASSERT(offset < (2 * l_max_ + 1) * (l_max_ + 1),\n         \"invalid offset \" << offset\n                           << \" passed to phi lookup. Must be less than (2 * \"\n                              \"l_max + 1) * (l_max + 1) = \"\n                           << (2 * l_max_ + 1) * (l_max_ + 1));\n  // clang-tidy pointer arithmetic\n  if (offset < (2 * l_max_ + 1) * (l_max_ / 2 + 1)) {\n    return (geom_info_.get())  // NOLINT\n        ->pair[offset / (2 * l_max_ + 1)]\n        .r1.theta;  // NOLINT\n  } else {\n    return (geom_info_.get())                       // NOLINT\n        ->pair[l_max_ - offset / (2 * l_max_ + 1)]  // NOLINT\n        .r2.theta;\n  }\n}\n\ntemplate <ComplexRepresentation Representation>\ndouble CollocationMetadata<Representation>::phi(const size_t offset) const {\n  ASSERT(offset < (2 * l_max_ + 1) * (l_max_ + 1),\n         \"invalid offset \" << offset\n                           << \" passed to phi lookup. Must be less than (2 * \"\n                              \"l_max + 1) * (l_max + 1) = \"\n                           << (2 * l_max_ + 1) * (l_max_ + 1));\n  return 2.0 * M_PI * ((offset % (2 * l_max_ + 1)) / (2.0 * l_max_ + 1.0));\n}\n\ntemplate <ComplexRepresentation Representation>\nconst CollocationMetadata<Representation>& cached_collocation_metadata(\n    const size_t l_max) {\n  const static auto lazy_collocation_cache =\n      make_static_cache<CacheRange<0_st, collocation_maximum_l_max>>(\n          [](const size_t generator_l_max) {\n            return CollocationMetadata<Representation>{generator_l_max};\n          });\n  return lazy_collocation_cache(l_max);\n}\n\nvoid create_angular_and_cartesian_coordinates(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_coordinates,\n    const size_t l_max) {\n  set_number_of_grid_points(cartesian_coordinates,\n                            number_of_swsh_collocation_points(l_max));\n  set_number_of_grid_points(angular_coordinates,\n                            number_of_swsh_collocation_points(l_max));\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get<0>(*angular_coordinates)[collocation_point.offset] =\n        collocation_point.theta;\n    get<1>(*angular_coordinates)[collocation_point.offset] =\n        collocation_point.phi;\n  }\n  get<0>(*cartesian_coordinates) =\n      sin(get<0>(*angular_coordinates)) * cos(get<1>(*angular_coordinates));\n  get<1>(*cartesian_coordinates) =\n      sin(get<0>(*angular_coordinates)) * sin(get<1>(*angular_coordinates));\n  get<2>(*cartesian_coordinates) = cos(get<0>(*angular_coordinates));\n}\n\ntemplate class CollocationMetadata<ComplexRepresentation::Interleaved>;\ntemplate class CollocationMetadata<ComplexRepresentation::RealsThenImags>;\n\ntemplate const CollocationMetadata<ComplexRepresentation::Interleaved>&\ncached_collocation_metadata(const size_t l_max);\ntemplate const CollocationMetadata<ComplexRepresentation::RealsThenImags>&\ncached_collocation_metadata(const size_t l_max);\n\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <sharp_cxx.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshSettings.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Spectral {\nnamespace Swsh {\n\n/// \\ingroup SwshGroup\n/// \\brief Convenience function for determining the number of spin-weighted\n/// spherical harmonic collocation values that are stored for a given `l_max`\n/// for a libsharp-compatible set of collocation points.\nconstexpr SPECTRE_ALWAYS_INLINE size_t\nnumber_of_swsh_collocation_points(const size_t l_max) {\n  return (l_max + 1) * (2 * l_max + 1);\n}\n\n/// \\ingroup SwshGroup\n/// \\brief Returns the number of spin-weighted spherical harmonic collocation\n/// values in \\f$\\theta\\f$ for a libsharp-compatible set of collocation\n/// points.\n///\n/// \\details The full number of collocation points is the product of the number\n/// of \\f$\\theta\\f$ points and the number of \\f$\\phi\\f$ points (a 'rectangular'\n/// grid).\nconstexpr SPECTRE_ALWAYS_INLINE size_t\nnumber_of_swsh_theta_collocation_points(const size_t l_max) {\n  return (l_max + 1);\n}\n\n/// \\ingroup SwshGroup\n/// \\brief Returns the number of spin-weighted spherical harmonic collocation\n/// values in \\f$\\phi\\f$ for a libsharp-compatible set of collocation\n/// points.\n///\n/// \\details The full number of collocation points is the product of the number\n/// of \\f$\\theta\\f$ points and the number of \\f$\\phi\\f$ points (a 'rectangular'\n/// grid).\nconstexpr SPECTRE_ALWAYS_INLINE size_t\nnumber_of_swsh_phi_collocation_points(const size_t l_max) {\n  return (2 * l_max + 1);\n}\n\n/// \\ingroup SwshGroup\n/// \\brief Obtain the three-dimensional mesh associated with a\n/// libsharp-compatible sequence of spherical nodal shells.\n/// \\warning This is to be used only for operations in the radial direction! The\n/// angular collocation points should not be regarded as having a useful product\n/// representation for e.g. taking derivatives in just the theta direction.\n/// Instead, use spin-weighted utilities for all angular operations.\nSPECTRE_ALWAYS_INLINE Mesh<3> swsh_volume_mesh_for_radial_operations(\n    const size_t l_max, const size_t number_of_radial_points) {\n  return Mesh<3>{{{number_of_swsh_phi_collocation_points(l_max),\n                   number_of_swsh_theta_collocation_points(l_max),\n                   number_of_radial_points}},\n                 Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\n\nnamespace detail {\n// Helping functor to appropriately delete a stored `sharp_geom_info**`\nstruct DestroySharpGeometry {\n  void operator()(sharp_geom_info* to_delete) {\n    sharp_destroy_geom_info(to_delete);\n  }\n};\n}  // namespace detail\n\n/// A container for reporting a single collocation point for libsharp compatible\n/// data structures\nstruct LibsharpCollocationPoint {\n  size_t offset;\n  double theta;\n  double phi;\n};\n\n/// \\brief A wrapper class for the spherical harmonic library collocation data\n///\n/// \\details The currently chosen library for spin-weighted spherical harmonic\n/// transforms is libsharp. The `CollocationMetadata` class stores the\n/// libsharp `sharp_geom_info` object, which contains data about\n/// 1. The angular collocation points used in spin-weighted spherical harmonic\n/// transforms\n/// 2. The memory representation of double-type values at those collocation\n/// points\n///\n/// \\tparam Representation the ComplexRepresentation, either\n/// `ComplexRepresentation::Interleaved` or\n/// `ComplexRepresentation::RealsThenImags` compatible with the generated\n/// `CollocationMetadata` - this is necessary because the stored libsharp type\n/// contains memory stride information.\ntemplate <ComplexRepresentation Representation>\nclass CollocationMetadata {\n public:\n  class CollocationConstIterator {\n   public:\n    /// create a new iterator. defaults to the start of the supplied object\n    explicit CollocationConstIterator(\n        const gsl::not_null<const CollocationMetadata<Representation>*>\n            collocation,\n        const size_t start_index = 0)\n        : index_{start_index}, collocation_{collocation} {}\n\n    /// recovers the data at the collocation point using a\n    /// `LibsharpCollocationPoint`, which stores the vector `offset` of the\n    /// location of the `theta`, `phi` point in libsharp compatible data\n    LibsharpCollocationPoint operator*() const {\n      return LibsharpCollocationPoint{index_, collocation_->theta(index_),\n                                      collocation_->phi(index_)};\n    }\n    /// advance the iterator by one position (prefix)\n    CollocationConstIterator& operator++() {\n      ++index_;\n      return *this;\n    };\n    /// advance the iterator by one position (postfix)\n    // clang-tidy wants this to return a const iterator\n    CollocationConstIterator operator++(int) {  // NOLINT\n      return CollocationConstIterator(collocation_, index_++);\n    }\n\n    /// retreat the iterator by one position (prefix)\n    CollocationConstIterator& operator--() {\n      --index_;\n      return *this;\n    };\n    /// retreat the iterator by one position (prefix)\n    // clang-tidy wants this to return a const iterator\n    CollocationConstIterator operator--(int) {  // NOLINT\n      return CollocationConstIterator(collocation_, index_--);\n    }\n\n    /// @{\n    /// (In)Equivalence checks both the object and index for the iterator\n    bool operator==(const CollocationConstIterator& rhs) const {\n      return index_ == rhs.index_ and collocation_ == rhs.collocation_;\n    }\n    bool operator!=(const CollocationConstIterator& rhs) const {\n      return not(*this == rhs);\n    }\n    /// @}\n\n   private:\n    size_t index_;\n    const CollocationMetadata<Representation>* const collocation_;\n  };\n\n  /// The representation of the block of complex values, which sets the stride\n  /// inside the libsharp type\n  static constexpr ComplexRepresentation complex_representation =\n      Representation;\n\n  /// \\brief Generates the libsharp collocation information and stores it in\n  /// `geom_info_`.\n  ///\n  /// \\note If you will potentially use the same `l_max` collocation set more\n  /// than once, it is probably better to use the\n  /// `precomputed_spherical_harmonic_collocation` function\n  explicit CollocationMetadata(size_t l_max);\n\n  /// default constructor required for iterator use\n  ~CollocationMetadata() = default;\n  CollocationMetadata() = default;\n  CollocationMetadata(const CollocationMetadata&) = default;\n  CollocationMetadata(CollocationMetadata&&) = default;\n  CollocationMetadata& operator=(CollocationMetadata&) = default;\n  CollocationMetadata& operator=(CollocationMetadata&&) = default;\n\n  /// retrieve the `sharp_geom_info*` stored. This should largely be used only\n  /// for passing to other libsharp functions. Otherwise, access elements\n  /// through iterator or access functions.\n  sharp_geom_info* get_sharp_geom_info() const { return geom_info_.get(); }\n\n  /// Retrieve the \\f$\\theta\\f$ value for a given index in a libsharp-compatible\n  /// array\n  double theta(size_t offset) const;\n  /// Retrieve the \\f$\\phi\\f$ value for a given index in a libsharp-compatible\n  /// array\n  double phi(size_t offset) const;\n\n  constexpr size_t l_max() const { return l_max_; }\n\n  /// Compute the number of entries the libsharp-compatible data structure\n  /// should have\n  constexpr size_t size() const { return (l_max_ + 1) * (2 * l_max_ + 1); }\n\n  /// @{\n  /// Get a bidirectional iterator to the start of the grid. `operator*` for\n  /// that iterator gives a `LibsharpCollocationPoint` with members `offset`,\n  /// `theta`, and `phi`\n  CollocationMetadata<Representation>::CollocationConstIterator begin() const {\n    return CollocationConstIterator{make_not_null(this), 0};\n  }\n  CollocationMetadata<Representation>::CollocationConstIterator cbegin() const {\n    return begin();\n  }\n  /// @}\n  /// @{\n  /// Get a bidirectional iterator to the end of the grid. `operator*` for\n  /// that iterator gives a `LibsharpCollocationPoint` with members `offset`,\n  /// `theta`, and `phi`\n  CollocationMetadata<Representation>::CollocationConstIterator end() const {\n    return CollocationConstIterator{make_not_null(this), size()};\n  }\n  CollocationMetadata<Representation>::CollocationConstIterator cend() const {\n    return end();\n  }\n  /// @}\n\n private:\n  // an extra pointer layer is required for the peculiar way that libsharp\n  // constructs these values.\n  size_t l_max_ = 0;\n  std::unique_ptr<sharp_geom_info, detail::DestroySharpGeometry> geom_info_;\n};\n\n/// \\brief precomputation function for those collocation grids that are\n/// requested\n///\n/// \\details keeps a compile-time structure which acts as a thread-safe lookup\n/// table for all l_max values that have been requested so far during execution,\n/// so that the libsharp generation need not be re-run. If it has been\n/// generated, it's returned by reference. Otherwise, the new grid is generated\n/// and put in the lookup table before it is returned by reference.\ntemplate <ComplexRepresentation Representation>\nconst CollocationMetadata<Representation>& cached_collocation_metadata(\n    size_t l_max);\n\n/// \\brief Store the libsharp-compatible collocation grid and corresponding\n/// unit-sphere cartesian grid in the supplied buffers.\nvoid create_angular_and_cartesian_coordinates(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> cartesian_coordinates,\n    const gsl::not_null<\n        tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n        angular_coordinates,\n    size_t l_max);\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexDiagonalModalOperator.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace Spectral::Swsh {\nnamespace detail {\nnamespace {\ntemplate <typename DerivativeKind, int Spin>\nComplexDiagonalModalOperator derivative_factors(const size_t l_max) {\n  ComplexDiagonalModalOperator derivative_factors{\n      size_of_libsharp_coefficient_vector(l_max)};\n  for (const auto mode : cached_coefficients_metadata(l_max)) {\n    // note that the libsharp transform data is stored as a pair of complex\n    // vectors: one for the transform of the real part and one for the transform\n    // of the imaginary part of the collocation data.\n    derivative_factors[mode.transform_of_real_part_offset] =\n        detail::derivative_factor<DerivativeKind>(mode.l, Spin);\n    derivative_factors[mode.transform_of_imag_part_offset] =\n        detail::derivative_factor<DerivativeKind>(mode.l, Spin);\n  }\n  // apply the sign change as appropriate due to the adjusted spin\n  // this loop is over the (complex) transform coefficients of first the real\n  // nodal data, followed by the imaginary nodal data. They receive the same\n  // derivative factors, but potentially different signs.\n  for (size_t i = 0; i < 2; i++) {\n    ComplexDiagonalModalOperator view_of_derivative_factor{\n        derivative_factors.data() +\n            i * size_of_libsharp_coefficient_vector(l_max) / 2,\n        size_of_libsharp_coefficient_vector(l_max) / 2};\n    view_of_derivative_factor *= sharp_swsh_sign_change(\n        Spin, Spin + Tags::derivative_spin_weight<DerivativeKind>, i == 0);\n  }\n  return derivative_factors;\n}\n\ntemplate <typename DerivativeKind, int Spin>\nconst ComplexDiagonalModalOperator& cached_derivative_factors(\n    const size_t l_max) {\n  const static auto lazy_derivative_operator_cache =\n      make_static_cache<CacheRange<0_st, swsh_derivative_maximum_l_max>>(\n          [](const size_t generator_l_max) {\n            return derivative_factors<DerivativeKind, Spin>(generator_l_max);\n          });\n  return lazy_derivative_operator_cache(l_max);\n}\n}  // namespace\n\ntemplate <typename DerivativeKind, int Spin>\nvoid compute_coefficients_of_derivative(\n    const gsl::not_null<\n        SpinWeighted<ComplexModalVector,\n                     Spin + Tags::derivative_spin_weight<DerivativeKind>>*>\n        derivative_modes,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>\n        pre_derivative_modes,\n    const size_t l_max, const size_t number_of_radial_points) {\n  derivative_modes->data().destructive_resize(pre_derivative_modes->size());\n  const ComplexDiagonalModalOperator& modal_derivative_operator =\n      cached_derivative_factors<DerivativeKind, Spin>(l_max);\n  // multiply each radial chunk by the appropriate derivative factors.\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    ComplexModalVector output_mode_view{\n        derivative_modes->data().data() +\n            i * size_of_libsharp_coefficient_vector(l_max),\n        size_of_libsharp_coefficient_vector(l_max)};\n    const ComplexModalVector input_mode_view{\n        pre_derivative_modes->data().data() +\n            i * size_of_libsharp_coefficient_vector(l_max),\n        size_of_libsharp_coefficient_vector(l_max)};\n    output_mode_view = modal_derivative_operator * input_mode_view;\n  }\n}\n}  // namespace detail\n\ntemplate <typename DerivKind, ComplexRepresentation Representation, int Spin>\nSpinWeighted<ComplexDataVector, Tags::derivative_spin_weight<DerivKind> + Spin>\nangular_derivative(\n    const size_t l_max, const size_t number_of_radial_points,\n    const SpinWeighted<ComplexDataVector, Spin>& to_differentiate) {\n  auto result =\n      SpinWeighted<ComplexDataVector, Tags::derivative_spin_weight<DerivKind> +\n                                          Spin>{to_differentiate.size()};\n  angular_derivatives<tmpl::list<DerivKind>, Representation>(\n      l_max, number_of_radial_points, make_not_null(&result), to_differentiate);\n  return result;\n}\n\n#define GET_DERIVKIND(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_SPIN(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define DERIVKIND_AND_SPIN_INSTANTIATION(r, data)                            \\\n  namespace detail {                                                         \\\n  template void                                                              \\\n  compute_coefficients_of_derivative<GET_DERIVKIND(data), GET_SPIN(data)>(   \\\n      const gsl::not_null<SpinWeighted<                                      \\\n          ComplexModalVector, GET_SPIN(data) + Tags::derivative_spin_weight< \\\n                                                   GET_DERIVKIND(data)>>*>   \\\n          derivative_modes,                                                  \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*> \\\n          pre_derivative_modes,                                              \\\n      const size_t l_max, const size_t number_of_radial_points);             \\\n  }\n\nGENERATE_INSTANTIATIONS(DERIVKIND_AND_SPIN_INSTANTIATION,\n                        (Tags::EthEthbar, Tags::EthbarEth), (-2, -1, 0, 1, 2))\nGENERATE_INSTANTIATIONS(DERIVKIND_AND_SPIN_INSTANTIATION, (Tags::Eth),\n                        (-2, -1, 0, 1, 2))\nGENERATE_INSTANTIATIONS(DERIVKIND_AND_SPIN_INSTANTIATION, (Tags::InverseEthbar),\n                        (-2, -1, 0, 1))\nGENERATE_INSTANTIATIONS(DERIVKIND_AND_SPIN_INSTANTIATION, (Tags::EthEth),\n                        (-2, -1, 0))\nGENERATE_INSTANTIATIONS(DERIVKIND_AND_SPIN_INSTANTIATION, (Tags::Ethbar),\n                        (-1, 0, 1, 2))\nGENERATE_INSTANTIATIONS(DERIVKIND_AND_SPIN_INSTANTIATION, (Tags::InverseEth),\n                        (-1, 0, 1, 2))\nGENERATE_INSTANTIATIONS(DERIVKIND_AND_SPIN_INSTANTIATION, (Tags::EthbarEthbar),\n                        (0, 1, 2))\n\n#undef DERIVKIND_AND_SPIN_INSTANTIATION\n#undef GET_SPIN\n#undef GET_DERIVKIND\n\n#define GET_REPRESENTATION(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_DERIVKIND(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define GET_SPIN(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define FULL_DERIVATIVE_INSTANTIATION(r, data)                              \\\n  template SpinWeighted<ComplexDataVector,                                  \\\n                        Tags::derivative_spin_weight<GET_DERIVKIND(data)> + \\\n                            GET_SPIN(data)>                                 \\\n  angular_derivative<GET_DERIVKIND(data), GET_REPRESENTATION(data),         \\\n                     GET_SPIN(data)>(                                       \\\n      const size_t l_max, const size_t number_of_radial_points,             \\\n      const SpinWeighted<ComplexDataVector, GET_SPIN(data)>&                \\\n          to_differentiate);\n\nGENERATE_INSTANTIATIONS(FULL_DERIVATIVE_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags),\n                        (Tags::EthEthbar, Tags::EthbarEth), (-2, -1, 0, 1, 2))\nGENERATE_INSTANTIATIONS(FULL_DERIVATIVE_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags),\n                        (Tags::Eth), (-2, -1, 0, 1, 2))\nGENERATE_INSTANTIATIONS(FULL_DERIVATIVE_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags),\n                        (Tags::InverseEthbar), (-2, -1, 0, 1))\nGENERATE_INSTANTIATIONS(FULL_DERIVATIVE_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags),\n                        (Tags::EthEth), (-2, -1, 0))\nGENERATE_INSTANTIATIONS(FULL_DERIVATIVE_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags),\n                        (Tags::Ethbar), (-1, 0, 1, 2))\nGENERATE_INSTANTIATIONS(FULL_DERIVATIVE_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags),\n                        (Tags::InverseEth), (-1, 0, 1, 2))\nGENERATE_INSTANTIATIONS(FULL_DERIVATIVE_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags),\n                        (Tags::EthbarEthbar), (0, 1, 2))\n\n#undef GET_SPIN\n#undef GET_DERIVKIND\n#undef GET_REPRESENTATION\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexDiagonalModalOperator.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshSettings.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Spectral {\nnamespace Swsh {\nnamespace detail {\n\n// Factors that appear in the modal representation of spin-weighted angular\n// derivatives, needed for compute_coefficients_of_derivative\ntemplate <typename DerivativeKind>\nSPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor(int l, int s);\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::Eth>(\n    const int l, const int s) {\n  return sqrt(static_cast<std::complex<double>>((l - s) * (l + s + 1)));\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::Ethbar>(\n    const int l, const int s) {\n  return -sqrt(static_cast<std::complex<double>>((l + s) * (l - s + 1)));\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::EthEth>(\n    const int l, const int s) {\n  return sqrt(static_cast<std::complex<double>>((l - s - 1) * (l + s + 2) *\n                                                (l - s) * (l + s + 1)));\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE std::complex<double>\nderivative_factor<Tags::EthbarEthbar>(const int l, const int s) {\n  return sqrt(static_cast<std::complex<double>>((l + s - 1) * (l - s + 2) *\n                                                (l + s) * (l - s + 1)));\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::EthbarEth>(\n    const int l, const int s) {\n  return static_cast<std::complex<double>>(-(l - s) * (l + s + 1));\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::EthEthbar>(\n    const int l, const int s) {\n  return static_cast<std::complex<double>>(-(l + s) * (l - s + 1));\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE std::complex<double> derivative_factor<Tags::InverseEth>(\n    const int l, const int s) {\n  return (l - s + 1) * (l + s) == 0\n             ? 0.0\n             : 1.0 / sqrt(static_cast<std::complex<double>>((l - s + 1) *\n                                                            (l + s)));\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE std::complex<double>\nderivative_factor<Tags::InverseEthbar>(const int l, const int s) {\n  return (l + s + 1) * (l - s) == 0\n             ? 0.0\n             : 1.0 / -sqrt(static_cast<std::complex<double>>((l + s + 1) *\n                                                             (l - s)));\n}\n\n// For a particular derivative represented by `DerivativeKind` and input spin\n// `Spin`, multiplies the derivative spectral factors with\n// `pre_derivative_modes`, returning by pointer via parameter `derivative_modes`\ntemplate <typename DerivativeKind, int Spin>\nvoid compute_coefficients_of_derivative(\n    gsl::not_null<\n        SpinWeighted<ComplexModalVector,\n                     Spin + Tags::derivative_spin_weight<DerivativeKind>>*>\n        derivative_modes,\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> pre_derivative_modes,\n    size_t l_max, size_t number_of_radial_points);\n\n// Helper function for dealing with the parameter packs in the utilities which\n// evaluate several spin-weighted derivatives at once. The `apply` function of\n// this struct locates the appropriate mode buffer in the input tuple\n// `pre_derivative_mode_tuple`, and calls `compute_coefficients_of_derivative`,\n// deriving the coefficients for `DerivativeTag` and returning by pointer.\ntemplate <typename DerivativeTag, typename PreDerivativeTagList>\nstruct dispatch_to_compute_coefficients_of_derivative {\n  template <int Spin, typename... ModalTypes>\n  static void apply(const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>\n                        derivative_modes,\n                    const std::tuple<ModalTypes...>& pre_derivative_mode_tuple,\n                    const size_t l_max, const size_t number_of_radial_points) {\n    compute_coefficients_of_derivative<typename DerivativeTag::derivative_kind>(\n        derivative_modes,\n        get<tmpl::index_of<PreDerivativeTagList,\n                           typename DerivativeTag::derivative_of>::value>(\n            pre_derivative_mode_tuple),\n        l_max, number_of_radial_points);\n  }\n};\n\n// Helper function for dealing with the parameter packs in the utilities which\n// evaluate several spin-weighted derivatives at once. The `apply` function of\n// this struct locates the like-spin set of paired modes and nodes, and calls\n// the member function of `SwshTransform` or `InverseSwshTransform` to perform\n// the spin-weighted transform. This function is a friend of both\n// `SwshTransform` and `InverseSwshTransform` to gain access to the private\n// `apply_to_vectors`.\n// The calling code will pass as tuples a superset of the quantities to be\n// transformed, in the same order as the tags provided to `TagList`. The\n// `modal_tuple` must be the storage destinations of the transforms in the\n// same order as the `nodal_tuple`. The set of nodal tags to be transformed are\n// the `TransformTags...` determined by the `SwshTransform` or\n// `InverseSwshTransform`. The reason for using `tuple`s rather than\n// `TaggedTuple`s or similar is to pass around spin-weighted vectors directly,\n// rather than `Scalar`s, which allows for more condensed forwarding code.\ntemplate <typename Transform, typename TagList>\nstruct dispatch_to_transform;\n\ntemplate <ComplexRepresentation Representation, typename... TransformTags,\n          typename TagList>\nstruct dispatch_to_transform<\n    SwshTransform<tmpl::list<TransformTags...>, Representation>, TagList> {\n  template <typename... ModalTypes, typename... NodalTypes>\n  static void apply(const gsl::not_null<std::tuple<ModalTypes...>*> modal_tuple,\n                    const std::tuple<NodalTypes...>& nodal_tuple,\n                    const size_t l_max, const size_t number_of_radial_points) {\n    SwshTransform<tmpl::list<TransformTags...>, Representation>::\n        apply_to_vectors(\n            get<tmpl::index_of<TagList, TransformTags>::value>(*modal_tuple)...,\n            get<tmpl::index_of<TagList, TransformTags>::value>(nodal_tuple)...,\n            l_max, number_of_radial_points);\n  }\n};\n\ntemplate <ComplexRepresentation Representation, typename... TransformTags,\n          typename TagList>\nstruct dispatch_to_transform<\n    InverseSwshTransform<tmpl::list<TransformTags...>, Representation>,\n    TagList> {\n  template <typename... NodalTypes, typename... ModalTypes>\n  static void apply(const gsl::not_null<std::tuple<NodalTypes...>*> nodal_tuple,\n                    const std::tuple<ModalTypes...>& modal_tuple,\n                    const size_t l_max, const size_t number_of_radial_points) {\n    InverseSwshTransform<tmpl::list<TransformTags...>, Representation>::\n        apply_to_vectors(\n            get<tmpl::index_of<TagList, TransformTags>::value>(*nodal_tuple)...,\n            *get<tmpl::index_of<TagList, TransformTags>::value>(modal_tuple)...,\n            l_max, number_of_radial_points);\n  }\n};\n\n// template 'implementation' for the DataBox mutate-compatible interface to\n// spin-weighted derivative evaluation. This impl version is needed to have easy\n// access to the `UniqueDifferentiatedFromTagList` as a parameter pack\ntemplate <typename DerivativeTagList, typename UniqueDifferentiatedFromTagList,\n          ComplexRepresentation Representation>\nstruct AngularDerivativesImpl;\n\ntemplate <typename... DerivativeTags, typename... UniqueDifferentiatedFromTags,\n          ComplexRepresentation Representation>\nstruct AngularDerivativesImpl<tmpl::list<DerivativeTags...>,\n                              tmpl::list<UniqueDifferentiatedFromTags...>,\n                              Representation> {\n  using return_tags =\n      tmpl::list<DerivativeTags..., Tags::SwshTransform<DerivativeTags>...,\n                 Tags::SwshTransform<UniqueDifferentiatedFromTags>...>;\n  using argument_tags = tmpl::list<UniqueDifferentiatedFromTags..., Tags::LMax,\n                                   Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      const gsl::not_null<typename DerivativeTags::type*>... derivative_scalars,\n      const gsl::not_null<typename Tags::SwshTransform<\n          DerivativeTags>::type*>... transform_of_derivative_scalars,\n      const gsl::not_null<typename Tags::SwshTransform<\n          UniqueDifferentiatedFromTags>::type*>... transform_of_input_scalars,\n      const typename UniqueDifferentiatedFromTags::type&... input_scalars,\n      const size_t l_max, const size_t number_of_radial_points) {\n    apply_to_vectors(make_not_null(&get(*transform_of_derivative_scalars))...,\n                     make_not_null(&get(*transform_of_input_scalars))...,\n                     make_not_null(&get(*derivative_scalars))...,\n                     get(input_scalars)..., l_max, number_of_radial_points);\n  }\n\n  template <ComplexRepresentation FriendRepresentation,\n            typename... DerivativeKinds, typename... ArgumentTypes,\n            size_t... Is>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend void angular_derivatives_impl(const std::tuple<ArgumentTypes...>&,\n                                       size_t, size_t,\n                                       std::index_sequence<Is...>,\n                                       tmpl::list<DerivativeKinds...>,\n                                       std::bool_constant<true>);\n\n private:\n  // note inputs reordered to accommodate the alternative tag-free functions\n  // which call into this function.\n  static void apply_to_vectors(\n      const gsl::not_null<typename Tags::SwshTransform<\n          DerivativeTags>::type::type*>... transform_of_derivatives,\n      const gsl::not_null<typename Tags::SwshTransform<\n          UniqueDifferentiatedFromTags>::type::type*>... transform_of_inputs,\n      const gsl::not_null<typename DerivativeTags::type::type*>... derivatives,\n      const typename UniqueDifferentiatedFromTags::type::type&... inputs,\n      const size_t l_max, const size_t number_of_radial_points) {\n    // perform the forward transform on the minimal set of input nodal\n    // quantities to obtain all of the requested derivatives\n    using ForwardTransformList =\n        make_transform_list_from_derivative_tags<Representation,\n                                                 tmpl::list<DerivativeTags...>>;\n\n    tmpl::for_each<ForwardTransformList>(\n        [&number_of_radial_points, &l_max, &inputs...,\n         &transform_of_inputs...](auto transform_v) {\n          using transform = typename decltype(transform_v)::type;\n          auto input_transforms = std::make_tuple(transform_of_inputs...);\n          dispatch_to_transform<transform,\n                                tmpl::list<UniqueDifferentiatedFromTags...>>::\n              apply(make_not_null(&input_transforms),\n                    std::forward_as_tuple(inputs...), l_max,\n                    number_of_radial_points);\n        });\n\n    // apply the modal derivative factors and place the result in the\n    // `transform_of_derivatives`\n    EXPAND_PACK_LEFT_TO_RIGHT(\n        dispatch_to_compute_coefficients_of_derivative<\n            DerivativeTags, tmpl::list<UniqueDifferentiatedFromTags...>>::\n            apply(transform_of_derivatives,\n                  std::make_tuple(transform_of_inputs...), l_max,\n                  number_of_radial_points));\n\n    // perform the inverse transform on the derivative results, placing the\n    // result in the nodal `derivatives` passed by pointer.\n    using InverseTransformList =\n        make_inverse_transform_list<Representation,\n                                    tmpl::list<DerivativeTags...>>;\n\n    tmpl::for_each<InverseTransformList>([&number_of_radial_points, &l_max,\n                                          &derivatives...,\n                                          &transform_of_derivatives...](\n                                             auto transform_v) {\n      using transform = typename decltype(transform_v)::type;\n      auto derivative_tuple = std::make_tuple(derivatives...);\n      dispatch_to_transform<transform, tmpl::list<DerivativeTags...>>::apply(\n          make_not_null(&derivative_tuple),\n          std::make_tuple(transform_of_derivatives...), l_max,\n          number_of_radial_points);\n    });\n  }\n};\n\n// metafunction for determining the tags needed to evaluate the derivative tags\n// in `DerivativeTagList`, removing all duplicate tags.\ntemplate <typename DerivativeTagList>\nstruct unique_derived_from_list;\n\ntemplate <typename... DerivativeTags>\nstruct unique_derived_from_list<tmpl::list<DerivativeTags...>> {\n  using type = tmpl::remove_duplicates<\n      tmpl::list<typename DerivativeTags::derivative_of...>>;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup SpectralGroup\n * \\brief A \\ref DataBoxGroup mutate-compatible computational struct for\n * computing a set of spin-weighted spherical harmonic derivatives by\n * grouping and batch-computing spin-weighted spherical harmonic transforms.\n *\n * \\details A derivative is evaluated for each tag in `DerivativeTagList`. All\n * entries in `DerivativeTagList` must be the tag\n * `Spectral::Swsh::Tags::Derivative<Tag, DerivativeKind>` prefixing the `Tag`\n * to be differentiated, and indicating the spin-weighted derivative\n * `DerivativeKind` to be taken. A \\ref DataBoxGroup on which this struct is\n * invoked must contain:\n * - each of the tags in `DerivativeTagList` (the results of the computation)\n * - each of the tags `Tag` prefixed by `Spectral::Swsh::Tags::Derivative` in\n * `DerivativeTagList` (the inputs of the computation).\n * - each of the tags `Spectral::Swsh::Tags::SwshTransform<DerivativeTag>` for\n *  `DerivativeTag`in `DerivativeTagList` (the buffers for the derivative\n * applied to the modes)\n * - each of the tags `Spectral::Swsh::Tags::SwshTransform<Tag>` for `Tag`\n * prefixed by any `DerivativeTag` in `DerivativeTagList` (the buffers for the\n * transforms of the input data).\n *\n * This function optimizes the derivative taking process by clustering like\n * spins of tags, forward-transforming each spin cluster together, applying the\n * factor for the derivative to each modal vector, re-clustering according to\n * the new spin weights (the derivatives alter the spin weights), and finally\n * inverse-transforming in clusters.\n */\ntemplate <typename DerivativeTagList, ComplexRepresentation Representation =\n                                          ComplexRepresentation::Interleaved>\nusing AngularDerivatives = detail::AngularDerivativesImpl<\n    DerivativeTagList,\n    typename detail::unique_derived_from_list<DerivativeTagList>::type,\n    Representation>;\n\n/*!\n * \\ingroup SpectralGroup\n * \\brief Produces a `SpinWeighted<ComplexModalVector, Spin>` of the appropriate\n * size to be used as a modal buffer for `Spectral::Swsh::AngularDerivatives` or\n * `Spectral::Swsh::angular_derivatives`.\n *\n * \\details The `Spectral::Swsh::angular_derivatives` and\n * `Spectral::Swsh::AngularDerivatives` interfaces require that calling code\n * provides a buffer for the intermediate transform results, to ensure that\n * callers are aware of the allocations and can suitably reuse buffers if\n * possible. This utility eases the creation of those buffers.\n */\ntemplate <int Spin>\nauto swsh_buffer(const size_t l_max, const size_t number_of_radial_points) {\n  return SpinWeighted<ComplexModalVector, Spin>{\n      size_of_libsharp_coefficient_vector(l_max) * number_of_radial_points};\n}\n\nnamespace detail {\n// template 'implementation' for the `angular_derivatives` function below which\n// evaluates an arbitrary number of derivatives, and places them in the set of\n// nodal containers passed by pointer.\ntemplate <ComplexRepresentation Representation, typename... DerivativeKinds,\n          typename... ArgumentTypes, size_t... Is>\nvoid angular_derivatives_impl(\n    const std::tuple<ArgumentTypes...>& argument_tuple, const size_t l_max,\n    const size_t number_of_radial_points, std::index_sequence<Is...> /*meta*/,\n    tmpl::list<DerivativeKinds...> /*meta*/,\n    std::bool_constant<true> /*buffers_included_in_arguments*/) {\n  AngularDerivatives<\n      tmpl::list<Tags::Derivative<\n          ::Tags::SpinWeighted<\n              ::Tags::TempScalar<Is, ComplexDataVector>,\n              std::integral_constant<\n                  int, std::decay_t<decltype(get<Is + 3 * sizeof...(Is)>(\n                           argument_tuple))>::spin>>,\n          DerivativeKinds>...>,\n      Representation>::apply_to_vectors(get<Is>(argument_tuple)...,\n                                        get<Is + sizeof...(Is)>(\n                                            argument_tuple)...,\n                                        get<Is + 2 * sizeof...(Is)>(\n                                            argument_tuple)...,\n                                        get<Is + 3 * sizeof...(Is)>(\n                                            argument_tuple)...,\n                                        l_max, number_of_radial_points);\n}\n\ntemplate <ComplexRepresentation Representation, typename... DerivativeKinds,\n          typename... ArgumentTypes, size_t... Is>\nvoid angular_derivatives_impl(\n    const std::tuple<ArgumentTypes...>& argument_tuple, const size_t l_max,\n    const size_t number_of_radial_points,\n    std::index_sequence<Is...> index_sequence,\n    tmpl::list<DerivativeKinds...> derivative_kinds,\n    std::bool_constant<false> /*buffers_included_in_arguments*/) {\n  auto derivative_buffer_tuple = std::make_tuple(\n      swsh_buffer<std::decay_t<decltype(*get<Is>(argument_tuple))>::spin>(\n          l_max, number_of_radial_points)...);\n  auto input_buffer_tuple = std::make_tuple(\n      swsh_buffer<std::decay_t<decltype(get<Is + sizeof...(Is)>(\n          argument_tuple))>::spin>(l_max, number_of_radial_points)...);\n  angular_derivatives_impl<Representation>(\n      std::forward_as_tuple(make_not_null(&get<Is>(derivative_buffer_tuple))...,\n                            make_not_null(&get<Is>(input_buffer_tuple))...,\n                            get<Is>(argument_tuple)...,\n                            get<Is + sizeof...(Is)>(argument_tuple)...),\n      l_max, number_of_radial_points, index_sequence, derivative_kinds,\n      std::bool_constant<true>{});\n}\n}  // namespace detail\n\n/*!\n * \\ingroup SpectralGroup\n * \\brief Evaluate all of the spin-weighted derivatives in `DerivKindList` on\n * input `SpinWeighted<ComplexDataVector, Spin>` collocation data, returning by\n * pointer.\n *\n * \\details This function provides two interfaces, one in which the caller\n * provides the intermediate coefficient buffers needed during the computation\n * of the derivatives, and one in which those buffers are temporarily allocated\n * during the derivative function calls.\n *\n * For the interface in which the caller does not provide buffers, the arguments\n * must take the following structure (enforced by internal function calls):\n *\n * - `size_t l_max` : angular resolution for the spherical representation\n * - `size_t number_of_radial_points` : radial resolution (number of consecutive\n * blocks to evaluate derivatives, for each input vector )\n * - for each `DerivKind` in `DerivKindList`, a\n * `gsl::not_null<SpinWeighted<ComplexDataVector, Spin +\n * Tags::derivative_spin_weight<DerivKind>>>` : the output of the derivative\n * evaluation\n * - for each `DerivKind` in `DerivKindList`, a `const\n * SpinWeighted<ComplexDataVector, Spin>&` (where the `Spin` for these arguments\n * matches the corresponding vector from the previous set) : the input to the\n * derivative evaluation.\n *\n * For the interface in which the caller does provide buffers, the arguments\n * must take the following structure (enforced by internal function calls):\n *\n * - `size_t l_max` : angular resolution for the spherical representation\n * - `size_t number_of_radial_points` : radial resolution (number of consecutive\n * blocks to evaluate derivatives, for each input vector )\n * - for each `DerivKind` in `DerivKindList`, a\n * `gsl::not_null<SpinWeighted<ComplexModalVector, Spin +\n * Tags::derivative_spin_weight<DerivKind>>>` : the buffer for the spin-weighted\n * spherical harmonic modes of the derivative quantities.\n * - for each `DerivKind` in `DerivKindList`, a `const\n * SpinWeighted<ComplexModalVector, Spin>` (where the `Spin` for these arguments\n * matches the corresponding vector from the previous set) : the buffer for the\n * spin-weighted spherical harmonic modes of the input quantities.\n * - for each `DerivKind` in `DerivKindList`, a\n * `gsl::not_null<SpinWeighted<ComplexDataVector, Spin +\n * Tags::derivative_spin_weight<DerivKind>>>` : the output of the derivative\n * evaluation\n * - for each `DerivKind` in `DerivKindList`, a `const\n * SpinWeighted<ComplexDataVector, Spin>` (where the `Spin` for these arguments\n * matches the corresponding vector from the previous set) : the input to the\n * derivative evaluation.\n *\n * The function `swsh_buffer` assists in generating the modal buffers of\n * appropriate size.\n */\ntemplate <\n    typename DerivativeKindList,\n    ComplexRepresentation Representation = ComplexRepresentation::Interleaved,\n    typename... ArgumentTypes>\nvoid angular_derivatives(const size_t l_max,                    // NOLINT\n                         const size_t number_of_radial_points,  // NOLINT\n                         const ArgumentTypes&... arguments) {\n  static_assert(\n      tmpl::size<DerivativeKindList>::value * 2 == sizeof...(ArgumentTypes) or\n          tmpl::size<DerivativeKindList>::value * 4 == sizeof...(ArgumentTypes),\n      \"When using the tagless `angular_derivatives` interface, you must \"\n      \"provide either one nodal input and one nodal output per derivative \"\n      \"or one nodal input, one nodal output, and two appropriate \"\n      \"intermediate transform buffers per derivative.\");\n\n  detail::angular_derivatives_impl<Representation>(\n      std::forward_as_tuple(arguments...), l_max, number_of_radial_points,\n      std::make_index_sequence<tmpl::size<DerivativeKindList>::value>{},\n      DerivativeKindList{},\n      std::bool_constant<tmpl::size<DerivativeKindList>::value * 4 ==\n                         sizeof...(ArgumentTypes)>{});\n}\n\n/*!\n * \\ingroup SpectralGroup\n * \\brief Evaluate the spin-weighted derivative `DerivKind` on the provided\n * `SpinWeighted<ComplexDataVector, Spin>` collocation data, returning by value.\n */\ntemplate <\n    typename DerivKind,\n    ComplexRepresentation Representation = ComplexRepresentation::Interleaved,\n    int Spin>\nSpinWeighted<ComplexDataVector, Tags::derivative_spin_weight<DerivKind> + Spin>\nangular_derivative(\n    size_t l_max, size_t number_of_radial_points,\n    const SpinWeighted<ComplexDataVector, Spin>& to_differentiate);\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Filtering.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Spectral::Swsh {\n\ntemplate <int Spin>\nvoid filter_swsh_volume_quantity(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    const size_t l_max, const size_t filter_min_l, const size_t filter_max_l,\n    const double exponential_alpha, const size_t exponential_half_power,\n    const gsl::not_null<ComplexDataVector*> buffer,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>\n        transform_buffer) {\n  const size_t number_of_radial_grid_points =\n      to_filter->size() / number_of_swsh_collocation_points(l_max);\n\n  if (LIKELY(exponential_alpha != 0.0)) {\n    buffer->destructive_resize(to_filter->size());\n    // Filter the radial direction using the provided exponential parameters.\n    apply_matrices(buffer,\n                   make_array(Matrix{}, Matrix{},\n                              Spectral::filtering::exponential_filter(\n                                  Mesh<1>{number_of_radial_grid_points,\n                                          Spectral::Basis::Legendre,\n                                          Spectral::Quadrature::GaussLobatto},\n                                  exponential_alpha, exponential_half_power)),\n                   to_filter->data(),\n                   Index<3>{number_of_swsh_phi_collocation_points(l_max),\n                            number_of_swsh_theta_collocation_points(l_max),\n                            number_of_radial_grid_points});\n    to_filter->data() = *buffer;\n  }\n  if (LIKELY(filter_max_l < l_max or\n             filter_min_l > static_cast<size_t>(abs(Spin)))) {\n    transform_buffer->destructive_resize(\n        number_of_radial_grid_points *\n        size_of_libsharp_coefficient_vector(l_max));\n    // Filter the angular direction using a transform and `filter_max_l`\n    swsh_transform(l_max, number_of_radial_grid_points, transform_buffer,\n                   *to_filter);\n    const auto& coefficients_metadata = cached_coefficients_metadata(l_max);\n    for (size_t i = 0; i < number_of_radial_grid_points; ++i) {\n      for (const auto mode : coefficients_metadata) {\n        if (mode.l > filter_max_l or mode.l < filter_min_l) {\n          transform_buffer->data()[mode.transform_of_real_part_offset +\n                                   i * coefficients_metadata.size()] = 0.0;\n          transform_buffer->data()[mode.transform_of_imag_part_offset +\n                                   i * coefficients_metadata.size()] = 0.0;\n        }\n      }\n    }\n    inverse_swsh_transform(l_max, number_of_radial_grid_points, to_filter,\n                           *transform_buffer);\n  }\n}\n\ntemplate <int Spin>\nvoid filter_swsh_volume_quantity(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    const size_t l_max, const size_t filter_max_l,\n    const double exponential_alpha, const size_t exponential_half_power,\n    const gsl::not_null<ComplexDataVector*> buffer,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>\n        transform_buffer) {\n  filter_swsh_volume_quantity(to_filter, l_max, 0_st, filter_max_l,\n                              exponential_alpha, exponential_half_power, buffer,\n                              transform_buffer);\n}\n\ntemplate <int Spin>\nvoid filter_swsh_volume_quantity(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    const size_t l_max, const size_t filter_min_l, const size_t filter_max_l,\n    const double exponential_alpha, const size_t exponential_half_power) {\n  const size_t number_of_radial_grid_points =\n      to_filter->data().size() / number_of_swsh_collocation_points(l_max);\n  auto buffer = ComplexDataVector{to_filter->data().size()};\n  auto transform_buffer = SpinWeighted<ComplexModalVector, Spin>{\n      number_of_radial_grid_points *\n      size_of_libsharp_coefficient_vector(l_max)};\n  filter_swsh_volume_quantity(to_filter, l_max, filter_min_l, filter_max_l,\n                              exponential_alpha, exponential_half_power,\n                              make_not_null(&buffer),\n                              make_not_null(&transform_buffer));\n}\n\ntemplate <int Spin>\nvoid filter_swsh_volume_quantity(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    const size_t l_max, const size_t filter_max_l,\n    const double exponential_alpha, const size_t exponential_half_power) {\n  filter_swsh_volume_quantity(to_filter, l_max, 0_st, filter_max_l,\n                              exponential_alpha, exponential_half_power);\n}\n\ntemplate <int Spin>\nvoid filter_swsh_boundary_quantity(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    const size_t l_max, const size_t filter_min_l, const size_t filter_max_l,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>\n        transform_buffer) {\n  if (LIKELY(filter_max_l < l_max or\n             filter_min_l > static_cast<size_t>(abs(Spin)))) {\n    transform_buffer->destructive_resize(\n        size_of_libsharp_coefficient_vector(l_max));\n    swsh_transform(l_max, 1, transform_buffer, *to_filter);\n    const auto& coefficients_metadata = cached_coefficients_metadata(l_max);\n    for (const auto mode : coefficients_metadata) {\n      if (mode.l > filter_max_l or mode.l < filter_min_l) {\n        transform_buffer->data()[mode.transform_of_real_part_offset] = 0.0;\n        transform_buffer->data()[mode.transform_of_imag_part_offset] = 0.0;\n      }\n    }\n    inverse_swsh_transform(l_max, 1, to_filter, *transform_buffer);\n  }\n}\n\ntemplate <int Spin>\nvoid filter_swsh_boundary_quantity(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    const size_t l_max, const size_t filter_min_l, const size_t filter_max_l) {\n  const size_t number_of_radial_grid_points =\n      to_filter->data().size() / number_of_swsh_collocation_points(l_max);\n  auto transform_buffer = SpinWeighted<ComplexModalVector, Spin>{\n      number_of_radial_grid_points *\n      size_of_libsharp_coefficient_vector(l_max)};\n  filter_swsh_boundary_quantity(to_filter, l_max, filter_min_l, filter_max_l,\n                                make_not_null(&transform_buffer));\n}\n\ntemplate <int Spin>\nvoid filter_swsh_boundary_quantity(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    const size_t l_max, const size_t filter_max_l,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>\n        transform_buffer) {\n  filter_swsh_boundary_quantity(to_filter, l_max, 0_st, filter_max_l,\n                                transform_buffer);\n}\n\ntemplate <int Spin>\nvoid filter_swsh_boundary_quantity(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    const size_t l_max, const size_t filter_max_l) {\n  filter_swsh_boundary_quantity(to_filter, l_max, 0_st, filter_max_l);\n}\n\n#define GET_SPIN(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define SWSH_FILTER_INSTANTIATION(r, data)                                   \\\n  template void filter_swsh_volume_quantity(                                 \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>  \\\n          to_filter,                                                         \\\n      const size_t l_max, const size_t filter_max_l,                         \\\n      const double exponential_alpha, const size_t exponential_half_power,   \\\n      const gsl::not_null<ComplexDataVector*> buffer,                        \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*> \\\n          transform_buffer);                                                 \\\n  template void filter_swsh_volume_quantity(                                 \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>  \\\n          to_filter,                                                         \\\n      const size_t l_max, const size_t filter_min_l,                         \\\n      const size_t filter_max_l, const double exponential_alpha,             \\\n      const size_t exponential_half_power,                                   \\\n      const gsl::not_null<ComplexDataVector*> buffer,                        \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*> \\\n          transform_buffer);                                                 \\\n  template void filter_swsh_volume_quantity(                                 \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>  \\\n          to_filter,                                                         \\\n      const size_t l_max, const size_t filter_max_l,                         \\\n      const double exponential_alpha, const size_t exponential_half_power);  \\\n  template void filter_swsh_volume_quantity(                                 \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>  \\\n          to_filter,                                                         \\\n      const size_t l_max, const size_t filter_min_l,                         \\\n      const size_t filter_max_l, const double exponential_alpha,             \\\n      const size_t exponential_half_power);                                  \\\n  template void filter_swsh_boundary_quantity(                               \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>  \\\n          to_filter,                                                         \\\n      const size_t l_max, const size_t filter_max_l,                         \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*> \\\n          transform_buffer);                                                 \\\n  template void filter_swsh_boundary_quantity(                               \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>  \\\n          to_filter,                                                         \\\n      const size_t l_max, const size_t filter_min_l,                         \\\n      const size_t filter_max_l,                                             \\\n      const gsl::not_null<SpinWeighted<ComplexModalVector, GET_SPIN(data)>*> \\\n          transform_buffer);                                                 \\\n  template void filter_swsh_boundary_quantity(                               \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>  \\\n          to_filter,                                                         \\\n      const size_t l_max, const size_t filter_max_l);                        \\\n  template void filter_swsh_boundary_quantity(                               \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>  \\\n          to_filter,                                                         \\\n      const size_t l_max, const size_t filter_min_l,                         \\\n      const size_t filter_max_l);\n\nGENERATE_INSTANTIATIONS(SWSH_FILTER_INSTANTIATION, (-2, -1, 0, 1, 2))\n\n#undef GET_SPIN\n#undef SWSH_FILTER_INSTANTIATION\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Spectral {\nnamespace Swsh {\n/// @{\n/*!\n * \\ingroup SwshGroup\n * \\brief Filter a volume collocation set in the form of consecutive\n * libsharp-compatible spherical shells.\n *\n * \\details Two separate filters are applied. First, an exponential radial\n * filter is applied to each radial ray, with parameters `exponential_alpha` and\n * `exponential_half_power` (see `Spectral::filtering::exponential_filter` for\n * details on these parameters). Next, a modal Heaviside angular filter is\n * applied which simply sets to zero all `l > filter_max_l` modes.\n * \\note It is assumed that Gauss-Lobatto points are used for the radial\n * direction (as that is the representation for CCE evolution). If that is too\n * restrictive, this function will need generalization.\n * \\warning In principle, the radial filter in this function could cache the\n * matrix used, but currently does not. If such a cache becomes desirable for\n * performance, care must be taken regarding the exponential parameters. An\n * implementation similar to `dg::Actions::ExponentialFilter` may be necessary.\n * \\note  For comparisons with SpEC CCE, `exponential_half_power` of 8,\n * `exponential_alpha` of 108, and `filter_max_l` of `l_max - 3` should be used.\n * This gives a highly aggressive radial filter, though, and for runs not\n * attempting to compare with SpEC it is recommended to use smaller parameters\n * to preserve more of the radial modes.\n */\ntemplate <int Spin>\nvoid filter_swsh_volume_quantity(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    size_t l_max, size_t filter_max_l, double exponential_alpha,\n    size_t exponential_half_power, gsl::not_null<ComplexDataVector*> buffer,\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> transform_buffer);\n\ntemplate <int Spin>\nvoid filter_swsh_volume_quantity(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    size_t l_max, size_t filter_max_l, double exponential_alpha,\n    size_t exponential_half_power);\n/// @}\n/// @{\n/*!\n * \\ingroup SwshGroup\n * \\brief Filter a volume collocation set in the form of consecutive\n * libsharp-compatible spherical shells.\n *\n * \\details Two separate filters are applied. First, an exponential radial\n * filter is applied to each radial ray, with parameters `exponential_alpha` and\n * `exponential_half_power` (see `Spectral::filtering::exponential_filter` for\n * details on these parameters). Next, a modal Heaviside angular filter is\n * applied which simply sets to zero all `l > max_l` or `l < min_l` modes.\n * \\note It is assumed that Gauss-Lobatto points are used for the radial\n * direction (as that is the representation for CCE evolution). If that is too\n * restrictive, this function will need generalization.\n * \\warning In principle, the radial filter in this function could cache the\n * matrix used, but currently does not. If such a cache becomes desirable for\n * performance, care must be taken regarding the exponential parameters. An\n * implementation similar to `dg::Actions::ExponentialFilter` may be necessary.\n */\ntemplate <int Spin>\nvoid filter_swsh_volume_quantity(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    size_t l_max, size_t filter_min_l, size_t filter_max_l,\n    double exponential_alpha, size_t exponential_half_power,\n    gsl::not_null<ComplexDataVector*> buffer,\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> transform_buffer);\n\ntemplate <int Spin>\nvoid filter_swsh_volume_quantity(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    size_t l_max, size_t filter_min_l, size_t filter_max_l,\n    double exponential_alpha, size_t exponential_half_power);\n/// @}\n/// @{\n/*!\n * \\ingroup SwshGroup\n * \\brief Filter a libsharp-compatible set of collocation points on a spherical\n * surface.\n *\n * \\details A modal Heaviside angular filter is applied which simply sets to\n * zero all `l > filter_max_l` modes.\n * \\note For comparisons with SpEC CCE, `filter_max_l` of `l_max - 3` should be\n * used.\n */\ntemplate <int Spin>\nvoid filter_swsh_boundary_quantity(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    size_t l_max, size_t filter_max_l,\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> transform_buffer);\n\ntemplate <int Spin>\nvoid filter_swsh_boundary_quantity(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    size_t l_max, size_t filter_max_l);\n/// @}\n/// @{\n/*!\n * \\ingroup SwshGroup\n * \\brief Filter a libsharp-compatible set of collocation points on a spherical\n * surface.\n *\n * \\details A modal Heaviside angular filter is applied which simply sets to\n * zero all `l > max_l` and `l < min_l` modes.\n */\ntemplate <int Spin>\nvoid filter_swsh_boundary_quantity(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    size_t l_max, size_t filter_min_l, size_t filter_max_l,\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> transform_buffer);\n\ntemplate <int Spin>\nvoid filter_swsh_boundary_quantity(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> to_filter,\n    size_t l_max, size_t filter_min_l, size_t filter_max_l);\n/// @}\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <boost/math/special_functions/binomial.hpp>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Spectral::Swsh {\n\nSpinWeightedSphericalHarmonic::SpinWeightedSphericalHarmonic(const int spin,\n                                                             const size_t l,\n                                                             const int m)\n    : spin_{spin}, l_{l}, m_{m} {\n  overall_prefactor_ = 1.0;\n  const double double_l = l;\n  const double double_m = m;\n  const double double_spin = spin;\n  if (std::abs(m) > std::abs(spin)) {\n    for (size_t i = 0; i < static_cast<size_t>(std::abs(m) - std::abs(spin));\n         ++i) {\n      const double double_i = i;\n      overall_prefactor_ *= (double_l + std::abs(double_m) - double_i) /\n                            (double_l - (std::abs(double_spin) + double_i));\n    }\n  } else if (std::abs(spin) > std::abs(m)) {\n    for (size_t i = 0; i < static_cast<size_t>(std::abs(spin) - std::abs(m));\n         ++i) {\n      const double double_i = i;\n      overall_prefactor_ *= (double_l - (std::abs(double_m) + double_i)) /\n                            (double_l + std::abs(double_spin) - double_i);\n    }\n  }\n  // if neither is greater (they are equal), then the prefactor is 1.0\n  overall_prefactor_ *= (2.0 * l + 1.0) / (4.0 * M_PI);\n  overall_prefactor_ = sqrt(overall_prefactor_);\n  overall_prefactor_ *= (m % 2) == 0 ? 1.0 : -1.0;\n\n  // gcc warns about the casts in ways that are impossible to satisfy\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"\n  if (static_cast<int>(l) < std::abs(spin)) {\n    if (spin < 0) {\n      r_prefactors_ =\n          std::vector<double>(l + static_cast<size_t>(std::abs(spin)) + 1, 0.0);\n    }\n  } else {\n    // the casts in the reserve are in correct order, but clang-format\n    // erroneously requests a change\n\n    // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n    r_prefactors_.reserve(static_cast<size_t>(static_cast<int>(l) - spin + 1));\n    for (int r = 0; r <= (static_cast<int>(l) - spin); ++r) {\n      if (r + spin - m >= 0 and static_cast<int>(l) - r + m >= 0) {\n        r_prefactors_.push_back(\n            boost::math::binomial_coefficient<double>(\n                static_cast<size_t>(static_cast<int>(l) - spin),\n                static_cast<size_t>(r)) *\n            boost::math::binomial_coefficient<double>(\n                static_cast<size_t>(static_cast<int>(l) + spin),\n                static_cast<size_t>(spin - m + r)) *\n            (((static_cast<int>(l) - r - spin) % 2) == 0 ? 1.0 : -1.0));\n      } else {\n        r_prefactors_.push_back(0.0);\n      }\n    }\n  }\n#pragma GCC diagnostic pop\n}\n\nvoid SpinWeightedSphericalHarmonic::evaluate(\n    const gsl::not_null<ComplexDataVector*> result, const DataVector& theta,\n    const DataVector& phi, const DataVector& sin_theta_over_2,\n    const DataVector& cos_theta_over_2) const {\n  result->destructive_resize(theta.size());\n  *result = 0.0;\n  DataVector theta_factor{theta.size()};\n  for (int r = 0; r <= static_cast<int>(l_) + std::min(m_, -spin_); ++r) {\n    if (2 * static_cast<int>(l_) > 2 * r + spin_ - m_) {\n      theta_factor = pow(cos_theta_over_2, 2 * r + spin_ - m_) *\n                     pow(sin_theta_over_2,\n                         2 * static_cast<int>(l_) - (2 * r + spin_ - m_));\n    } else {\n      // this branch prevents a possible pow(0,0) when 2r = 2l - s + m\n      theta_factor = pow(cos_theta_over_2, 2 * l_);\n    }\n    *result += gsl::at(r_prefactors_, r) * theta_factor;\n  }\n  // optimization note: this has not been compared with a complex `exp`\n  // function, and it is not obvious which should be faster in practice.\n  *result *=\n      overall_prefactor_ *\n      (std::complex<double>(1.0, 0.0) * cos(static_cast<double>(m_) * phi) +\n       std::complex<double>(0.0, 1.0) * sin(static_cast<double>(m_) * phi));\n}\n\nComplexDataVector SpinWeightedSphericalHarmonic::evaluate(\n    const DataVector& theta, const DataVector& phi,\n    const DataVector& sin_theta_over_2,\n    const DataVector& cos_theta_over_2) const {\n  ComplexDataVector result{theta.size(), 0.0};\n  evaluate(make_not_null(&result), theta, phi, sin_theta_over_2,\n           cos_theta_over_2);\n  return result;\n}\n\nstd::complex<double> SpinWeightedSphericalHarmonic::evaluate(\n    const double theta, const double phi) const {\n  std::complex<double> accumulator = 0.0;\n  const double cos_theta_over_two = cos(0.5 * theta);\n  const double sin_theta_over_two = sin(0.5 * theta);\n  double theta_factor = std::numeric_limits<double>::signaling_NaN();\n  for (int r = 0; r <= static_cast<int>(l_) + std::min(m_, -spin_); ++r) {\n    if (2 * static_cast<int>(l_) > 2 * r + spin_ - m_) {\n      theta_factor = pow(cos_theta_over_two, 2 * r + spin_ - m_) *\n                     pow(sin_theta_over_two,\n                         2 * static_cast<int>(l_) - (2 * r + spin_ - m_));\n    } else {\n      // this branch prevents a possible pow(0,0) when 2r = 2l - s + m\n      theta_factor = pow(cos_theta_over_two, 2 * l_);\n    }\n    accumulator += gsl::at(r_prefactors_, r) *\n                   std::complex<double>(cos(static_cast<double>(m_) * phi),\n                                        sin(static_cast<double>(m_) * phi)) *\n                   theta_factor;\n  }\n  accumulator *= overall_prefactor_;\n  return accumulator;\n}\n\nvoid SpinWeightedSphericalHarmonic::pup(PUP::er& p) {\n  p | spin_;\n  p | l_;\n  p | m_;\n  p | overall_prefactor_;\n  p | r_prefactors_;\n}\n\nnamespace {\n// A function for indexing a desired element in one of the caches stored\n// in `ClenshawRecurrenceConstants`.\n// Useful for accessing the `beta_constant`, `alpha_constant`, or\n// `alpha_prefactor` recurrence constants.\nsize_t clenshaw_cache_index(const size_t l_max, const int spin, const int l,\n                            const int m) {\n  return goldberg_mode_index(l_max - 2, static_cast<size_t>(l - 2), m) -\n         static_cast<size_t>(square(spin));\n}\n\n// see the detailed doxygen for `SwshInterpolator` for full mathematical details\n// of the recurrence constant computations\ntemplate <int Spin>\nstruct ClenshawRecurrenceConstants {\n  ClenshawRecurrenceConstants() = default;\n\n  explicit ClenshawRecurrenceConstants(size_t l_max)\n      : alpha_prefactor{square(l_max + 1) -\n                        square(static_cast<size_t>(std::abs(Spin)))},\n        alpha_constant{square(l_max + 1) -\n                       square(static_cast<size_t>(std::abs(Spin)))},\n        beta_constant{square(l_max + 1) -\n                      square(static_cast<size_t>(std::abs(Spin)))},\n        harmonic_at_l_min_prefactors{2 * l_max + 1},\n        harmonic_at_l_min_plus_one_recurrence_prefactors{2 * l_max + 1},\n        harmonic_m_recurrence_prefactors{2 * l_max + 1} {\n    ASSERT(static_cast<int>(l_max) > Spin,\n           \"l_max must be greater than the spin-weight when computing \"\n           \"ClenshawRecurrenceConstants\");\n    double l_plus_k = std::numeric_limits<double>::signaling_NaN();\n    double l_min_plus_k = std::numeric_limits<double>::signaling_NaN();\n    double a = std::numeric_limits<double>::signaling_NaN();\n    double b = std::numeric_limits<double>::signaling_NaN();\n    int l_min = 0;\n    double prefactor_accumulator = std::numeric_limits<double>::signaling_NaN();\n    lambda.reserve(2 * l_max + 1);\n    for (int m = -static_cast<int>(l_max); m <= static_cast<int>(l_max); ++m) {\n      a = static_cast<double>(std::abs(Spin + m));\n      b = static_cast<double>(std::abs(Spin - m));\n      l_min = std::max(std::abs(m), std::abs(Spin));\n\n      // gcc warns about an optimization that doesn't work if we overflow. None\n      // of this will overflow provided l_max is not unreasonably high (less\n      // than ~10^5 will not overflow).\n      for (int l = l_min + 2; l <= static_cast<int>(l_max); ++l) {\n        // start caching at 2 greater than the l_min for a given m. Those are\n        // the last terms needed by (descending) Clenshaw sum.\n        l_plus_k = static_cast<double>(l) - 0.5 * (a + b);\n        alpha_prefactor[clenshaw_cache_index(l_max, Spin, l, m)] =\n            0.5 * sqrt((2.0 * l + 1.0) * (2.0 * l - 1.0) /\n                       (l_plus_k * (l_plus_k + a + b) * (l_plus_k + a) *\n                        (l_plus_k + b)));\n        alpha_constant[clenshaw_cache_index(l_max, Spin, l, m)] =\n            alpha_prefactor[clenshaw_cache_index(l_max, Spin, l, m)] *\n            ((square(a) - square(b)) / (2.0 * l - 2.0));\n        alpha_prefactor[clenshaw_cache_index(l_max, Spin, l, m)] *= (2.0 * l);\n        beta_constant[clenshaw_cache_index(l_max, Spin, l, m)] =\n            -sqrt((2.0 * l + 1.0) * (l_plus_k + a - 1.0) *\n                  (l_plus_k + b - 1.0) * (l_plus_k - 1.0) *\n                  (l_plus_k + a + b - 1.0) /\n                  ((2.0 * l - 3.0) * l_plus_k * (l_plus_k + a + b) *\n                   (l_plus_k + a) * (l_plus_k + b))) *\n            (2.0 * l) / (2.0 * l - 2.0);\n      }\n      lambda.push_back(Spin >= -m ? 0 : Spin + m);\n\n      // pre-compute the prefactors for the lowest order harmonics for each m\n      prefactor_accumulator = 1.0;\n      l_min_plus_k = -0.5 * (std::abs(Spin + m) + b) + l_min;\n      for (int i = 1; i <= b; ++i) {\n        if (l_min_plus_k + a + i > 0.0) {\n          prefactor_accumulator *= static_cast<double>(l_min_plus_k + a + i);\n        }\n        if (l_min_plus_k + i > 0.0) {\n          prefactor_accumulator /= static_cast<double>(l_min_plus_k + i);\n        }\n      }\n      prefactor_accumulator =\n          sqrt(prefactor_accumulator * (2.0 * l_min + 1.0) / (4.0 * M_PI));\n      prefactor_accumulator *=\n          ((m + gsl::at(lambda, m + static_cast<int>(l_max))) % 2) == 0 ? 1.0\n                                                                        : -1.0;\n      // this is the right order of the casts, other orders give the wrong\n      // answer\n\n      // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n      harmonic_at_l_min_prefactors[static_cast<size_t>(\n          m + static_cast<int>(l_max))] = prefactor_accumulator;\n\n      // pre-compute the prefactors for bootstrapping the second-to-lowest order\n      // harmonics for each m\n\n      // this is the right order of the casts, other orders give the wrong\n      // answer\n\n      // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n      harmonic_at_l_min_plus_one_recurrence_prefactors[static_cast<size_t>(\n          m + static_cast<int>(l_max))] =\n          sqrt((2.0 * (l_min) + 3.0) * (l_min_plus_k + 1.0) *\n               (l_min_plus_k + a + b + 1.0) /\n               ((2.0 * (l_min) + 1.0) * (l_min_plus_k + a + 1.0) *\n                (l_min_plus_k + b + 1.0)));\n    }\n    // separate loop because we'll need the lambdas entirely populated for this\n    // set of prefactors\n    int lambda_difference = 0;\n    for (int m = -static_cast<int>(l_max); m <= static_cast<int>(l_max); ++m) {\n      if (std::abs(m) > std::abs(Spin)) {\n        l_min = std::max(std::abs(m), std::abs(Spin));\n        a = std::abs(Spin + m);\n        b = std::abs(Spin - m);\n        l_min_plus_k = -0.5 * (std::abs(Spin + m) + b) + l_min;\n\n        prefactor_accumulator =\n            sqrt((2.0 * std::abs(m) + 1.0) * (l_min_plus_k + a + b - 1.0) *\n                 (l_min_plus_k + a + b) /\n                 ((2.0 * std::abs(m) - 1.0) * (l_min_plus_k + a) *\n                  (l_min_plus_k + b)));\n        // there is an extra `1` in these expressions to account for the -1 out\n        // front of the recurrence relations.\n        lambda_difference = gsl::at(lambda, m + static_cast<int>(l_max)) + 1;\n        if (m > 0) {\n          lambda_difference -= gsl::at(lambda, m - 1 + static_cast<int>(l_max));\n        } else {\n          lambda_difference -= gsl::at(lambda, m + 1 + static_cast<int>(l_max));\n        }\n        prefactor_accumulator *= lambda_difference % 2 == 0 ? 1.0 : -1.0;\n        // this is the right order of the casts, other orders give the wrong\n        // answer\n\n        // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n        harmonic_m_recurrence_prefactors[static_cast<size_t>(\n            m + static_cast<int>(l_max))] = prefactor_accumulator;\n      }\n    }\n  }\n\n  // Serialization for Charm++.\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | alpha_prefactor;\n    p | alpha_constant;\n    p | beta_constant;\n    p | lambda;\n    p | harmonic_at_l_min_prefactors;\n    p | harmonic_at_l_min_plus_one_recurrence_prefactors;\n    p | harmonic_m_recurrence_prefactors;\n  }\n\n  // Tables are stored in a triangular Goldberg style\n  DataVector alpha_prefactor;\n  DataVector alpha_constant;\n  DataVector beta_constant;\n  std::vector<int> lambda;\n  DataVector harmonic_at_l_min_prefactors;\n  DataVector harmonic_at_l_min_plus_one_recurrence_prefactors;\n  DataVector harmonic_m_recurrence_prefactors;\n};\n\n// A lazy static cache interface for retrieving `ClenshawRecurrenceConstants`.\ntemplate <int Spin>\nconst ClenshawRecurrenceConstants<Spin>& cached_clenshaw_factors(\n    const size_t l_max) {\n  const static auto lazy_clenshaw_cache =\n      make_static_cache<CacheRange<0_st, collocation_maximum_l_max>>(\n          [](const size_t local_l_max) {\n            return ClenshawRecurrenceConstants<Spin>{local_l_max};\n          });\n  return lazy_clenshaw_cache(l_max);\n}\n}  // namespace\n\nSwshInterpolator::SwshInterpolator(const DataVector& theta,\n                                   const DataVector& phi, const size_t l_max)\n    : l_max_{l_max},\n      raw_libsharp_coefficient_buffer_{\n          size_of_libsharp_coefficient_vector(l_max)},\n      raw_goldberg_coefficient_buffer_{square(l_max + 1)} {\n  cos_m_phi_ = std::vector<DataVector>(l_max + 1);\n  sin_m_phi_ = std::vector<DataVector>(l_max + 1);\n  cos_theta_ = cos(theta);\n  sin_theta_ = sin(theta);\n  cos_theta_over_two_ = cos(0.5 * theta);\n  sin_theta_over_two_ = sin(0.5 * theta);\n  // evaluate cos(m phi) and sin(m phi) via recurrence\n  cos_m_phi_[0] = DataVector{phi.size(), 1.0};\n  sin_m_phi_[0] = DataVector{phi.size(), 0.0};\n  const DataVector m_phi_beta = sin(phi);\n  const DataVector m_phi_alpha = 2.0 * square(sin(0.5 * phi));\n  for (size_t m = 1; m <= l_max; ++m) {\n    cos_m_phi_[m] = cos_m_phi_[m - 1] - (m_phi_alpha * cos_m_phi_[m - 1] +\n                                         m_phi_beta * sin_m_phi_[m - 1]);\n    sin_m_phi_[m] = sin_m_phi_[m - 1] - (m_phi_alpha * sin_m_phi_[m - 1] -\n                                         m_phi_beta * cos_m_phi_[m - 1]);\n  }\n}\n\ntemplate <int Spin>\nvoid SwshInterpolator::interpolate(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> interpolated,\n    const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes) const {\n  ASSERT(l_max_ != 0,\n         \"Attempting to perform interpolation with a default-constructed \"\n         \"SwshInterpolator. The SwshInterpolator must be constructed with the \"\n         \"angular coordinates to perform interpolation.\");\n  interpolated->destructive_resize(cos_theta_.size());\n  interpolated->data() = 0.0;\n\n  // used only if s=0;\n  SpinWeighted<ComplexDataVector, Spin> cached_base_harmonic;\n\n  // used during both recurrence legs\n  SpinWeighted<ComplexDataVector, Spin> current_cached_harmonic;\n  SpinWeighted<ComplexDataVector, Spin> current_cached_harmonic_l_plus_one;\n\n  // perform the Clenshaw sums over positive m >= 0.\n  for (int m = 0; m <= static_cast<int>(l_max_); ++m) {\n    if (std::abs(Spin) >= std::abs(m)) {\n      direct_evaluation_swsh_at_l_min(make_not_null(&current_cached_harmonic),\n                                      m);\n      evaluate_swsh_at_l_min_plus_one(\n          make_not_null(&current_cached_harmonic_l_plus_one),\n          current_cached_harmonic, m);\n    } else {\n      evaluate_swsh_m_recurrence_at_l_min(\n          make_not_null(&current_cached_harmonic), m);\n      evaluate_swsh_at_l_min_plus_one(\n          make_not_null(&current_cached_harmonic_l_plus_one),\n          current_cached_harmonic, m);\n    }\n    if (Spin == 0 and m == 0) {\n      cached_base_harmonic = current_cached_harmonic;\n    }\n    clenshaw_sum(interpolated, current_cached_harmonic,\n                 current_cached_harmonic_l_plus_one, goldberg_modes, m);\n  }\n  // perform the Clenshaw sums over m < 0.\n  for (int m = -1; m >= -static_cast<int>(l_max_); --m) {\n    // initialize the recurrence for negative m\n    if (m == -1 and Spin == 0) {\n      current_cached_harmonic = cached_base_harmonic;\n    }\n    if (std::abs(Spin) >= std::abs(m)) {\n      direct_evaluation_swsh_at_l_min(make_not_null(&current_cached_harmonic),\n                                      m);\n      evaluate_swsh_at_l_min_plus_one(\n          make_not_null(&current_cached_harmonic_l_plus_one),\n          current_cached_harmonic, m);\n    } else {\n      evaluate_swsh_m_recurrence_at_l_min(\n          make_not_null(&current_cached_harmonic), m);\n      evaluate_swsh_at_l_min_plus_one(\n          make_not_null(&current_cached_harmonic_l_plus_one),\n          current_cached_harmonic, m);\n    }\n    clenshaw_sum(interpolated, current_cached_harmonic,\n                 current_cached_harmonic_l_plus_one, goldberg_modes, m);\n  }\n}\n\ntemplate <int Spin>\nvoid SwshInterpolator::interpolate(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> interpolated,\n    const SpinWeighted<ComplexDataVector, Spin>& libsharp_collocation) const {\n  ASSERT(l_max_ != 0,\n         \"Attempting to perform interpolation with a default-constructed \"\n         \"SwshInterpolator. The SwshInterpolator must be constructed with the \"\n         \"angular coordinates to perform interpolation.\");\n  SpinWeighted<ComplexModalVector, Spin> libsharp_modes;\n  // this function is 'const', but modifies the internal buffer. The reason to\n  // allow it to be 'const' anyways is that no interface makes any assumption\n  // about the starting state of the internal buffer; it is kept exclusively to\n  // save allocations.\n  libsharp_modes.set_data_ref(raw_libsharp_coefficient_buffer_.data(),\n                              raw_libsharp_coefficient_buffer_.size());\n  swsh_transform(l_max_, 1, make_not_null(&libsharp_modes),\n                 libsharp_collocation);\n  SpinWeighted<ComplexModalVector, Spin> goldberg_modes;\n  libsharp_to_goldberg_modes(make_not_null(&goldberg_modes), libsharp_modes,\n                             l_max_);\n  interpolate(interpolated, goldberg_modes);\n}\n\ntemplate <int Spin>\nvoid SwshInterpolator::direct_evaluation_swsh_at_l_min(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> harmonic,\n    const int m) const {\n  ASSERT(l_max_ != 0,\n         \"Attempting to perform spin-weighted evaluation with a \"\n         \"default-constructed SwshInterpolator. The SwshInterpolator must be \"\n         \"constructed with the angular coordinates to perform function \"\n         \"evaluation.\");\n  const auto& clenshaw_factors = cached_clenshaw_factors<Spin>(l_max_);\n  // for this evaluation, we don't worry about recurrence because it will only\n  // be called for m between -s and +s, and s should always be small. In\n  // principle, it is probably true that a more complicated recurrence exists\n  // for this case, but would require a bit of derivation work\n  harmonic->data() =\n      // clang-tidy: this is the right order of the casts, other orders give the\n      // wrong answer\n\n      // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n      clenshaw_factors.harmonic_at_l_min_prefactors[static_cast<size_t>(\n          m + static_cast<int>(l_max_))] *\n      (std::complex<double>(1.0, 0.0) * gsl::at(cos_m_phi_, std::abs(m)) +\n       std::complex<double>(0.0, 1.0) * (m >= 0 ? 1.0 : -1.0) *\n           gsl::at(sin_m_phi_, std::abs(m))) *\n      pow(sin_theta_over_two_, static_cast<size_t>(std::abs(Spin + m))) *\n      pow(cos_theta_over_two_, static_cast<size_t>(std::abs(Spin - m)));\n}\n\ntemplate <int Spin>\nvoid SwshInterpolator::evaluate_swsh_at_l_min_plus_one(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> harmonic,\n    const SpinWeighted<ComplexDataVector, Spin>& harmonic_at_l_min,\n    const int m) const {\n  ASSERT(l_max_ != 0,\n         \"Attempting to perform spin-weighted evaluation with a \"\n         \"default-constructed SwshInterpolator. The SwshInterpolator must be \"\n         \"constructed with the angular coordinates to perform function \"\n         \"evaluation.\");\n  const auto& clenshaw_factors = cached_clenshaw_factors<Spin>(l_max_);\n  const double a = std::abs(Spin + m);\n  const double b = std::abs(Spin - m);\n  harmonic->data() =\n      clenshaw_factors\n          // clang-tidy: this is the right order of the casts, other orders give\n          // the wrong answer\n\n          // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n          .harmonic_at_l_min_plus_one_recurrence_prefactors[static_cast<size_t>(\n              m + static_cast<int>(l_max_))] *\n      harmonic_at_l_min.data() *\n      (a + 1.0 + 0.5 * (a + b + 2.0) * (cos_theta_ - 1.0));\n}\n\ntemplate <int Spin>\nvoid SwshInterpolator::evaluate_swsh_m_recurrence_at_l_min(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> harmonic,\n    const int m) const {\n  ASSERT(l_max_ != 0,\n         \"Attempting to perform spin-weighted evaluation with a \"\n         \"default-constructed SwshInterpolator. The SwshInterpolator must be \"\n         \"constructed with the angular coordinates to perform function \"\n         \"evaluation.\");\n  const auto& clenshaw_factors = cached_clenshaw_factors<Spin>(l_max_);\n  harmonic->data() =\n      // this is the right order of the casts, other orders give the wrong\n      // answer\n\n      // NOLINTNEXTLINE(bugprone-misplaced-widening-cast)\n      clenshaw_factors.harmonic_m_recurrence_prefactors[static_cast<size_t>(\n          m + static_cast<int>(l_max_))] *\n      (sin_theta_ / 2.0) * harmonic->data();\n  if (m > 0) {\n    harmonic->data() *= (std::complex<double>(1.0, 0.0) * cos_m_phi_[1] +\n                         std::complex<double>(0.0, 1.0) * sin_m_phi_[1]);\n  } else {\n    harmonic->data() *= (std::complex<double>(1.0, 0.0) * cos_m_phi_[1] -\n                         std::complex<double>(0.0, 1.0) * sin_m_phi_[1]);\n  }\n}\n\ntemplate <int Spin>\nvoid SwshInterpolator::clenshaw_sum(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> interpolation,\n    const SpinWeighted<ComplexDataVector, Spin>& l_min_harmonic,\n    const SpinWeighted<ComplexDataVector, Spin>& l_min_plus_one_harmonic,\n    const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes,\n    const int m) const {\n  ASSERT(l_max_ != 0,\n         \"Attempting to perform spin-weighted evaluation with a \"\n         \"default-constructed SwshInterpolator. The SwshInterpolator must be \"\n         \"constructed with the angular coordinates to perform function \"\n         \"evaluation.\");\n  // Since we need various combinations of the three-term recurrence constants\n  // up to two orders higher, we write recurrence results to a cyclic\n  // three-element cache\n  std::array<ComplexDataVector, 3> recurrence_cache;\n  recurrence_cache[2] = ComplexDataVector{interpolation->size(), 0.0};\n  recurrence_cache[1] = ComplexDataVector{interpolation->size(), 0.0};\n  recurrence_cache[0] = ComplexDataVector{interpolation->size(), 0.0};\n  const auto& clenshaw_factors = cached_clenshaw_factors<Spin>(l_max_);\n\n  for (auto l = static_cast<int>(l_max_);\n       l > std::max(std::abs(Spin), std::abs(m)); l--) {\n    // We want to define some cache_offset so that we can index the three\n    // elements of recurrence_cache with indices cache_offset%3,\n    // (cache_offset+1)%3, and (cache_offset+2)%3, and so that cache_offset\n    // decreases by one on each iteration. The \"obvious\" way to do this is to\n    // choose cache_offset = l - l_max, so that cache_offset starts at zero and\n    // then decreases each iteration. However, this gives negative values of\n    // cache_offset, and C++ modular arithmetic doesn't behave the way we'd want\n    // for that process at negative values. But note that adding any multiple of\n    // 3 to the \"obvious\" value of cache_offset will give identical indexing,\n    // and choosing this multiple of 3 large enough (i.e. larger than l_max)\n    // guarantees that the new cache_offset is positive for all l. So we choose\n    // to add 3*l_max to the \"obvious\" value of cache_offset; in other words we\n    // define cache_offset = l + 2 l_max.\n    // In future, if this trick needs to be re-implemented in another use-case,\n    // it should instead be factored out into a separate rotating cache utility.\n    const int cache_offset = (l + 2 * static_cast<int>(l_max_));\n    // gcc warns about the casts in ways that are impossible to satisfy\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"\n    gsl::at(recurrence_cache, (cache_offset) % 3) =\n        goldberg_modes.data()[square(static_cast<size_t>(l)) +\n                              static_cast<size_t>(l + m)];\n    if (l < static_cast<int>(l_max_)) {\n      gsl::at(recurrence_cache, (cache_offset) % 3) +=\n          (clenshaw_factors\n               .alpha_constant[clenshaw_cache_index(l_max_, Spin, l + 1, m)] +\n           cos_theta_ * clenshaw_factors.alpha_prefactor[clenshaw_cache_index(\n                            l_max_, Spin, l + 1, m)]) *\n          gsl::at(recurrence_cache, (cache_offset + 1) % 3);\n    }\n    if (l < static_cast<int>(l_max_) - 1) {\n      gsl::at(recurrence_cache, (cache_offset) % 3) +=\n          clenshaw_factors\n              .beta_constant[clenshaw_cache_index(l_max_, Spin, l + 2, m)] *\n          gsl::at(recurrence_cache, (cache_offset + 2) % 3);\n    }\n  }\n  const int l_min = std::max(std::abs(Spin), std::abs(m));\n  const int cache_offset = (l_min + 2 * static_cast<int>(l_max_));\n\n  if (l_max_ >=\n      static_cast<size_t>(std::max(std::abs(Spin), std::abs(m))) + 2) {\n    *interpolation +=\n        l_min_harmonic *\n            goldberg_modes.data()[square(static_cast<size_t>(l_min)) +\n                                  static_cast<size_t>(l_min + m)] +\n        l_min_plus_one_harmonic *\n            gsl::at(recurrence_cache, (cache_offset + 1) % 3) +\n        l_min_harmonic * gsl::at(recurrence_cache, (cache_offset + 2) % 3) *\n            clenshaw_factors.beta_constant[clenshaw_cache_index(\n                l_max_, Spin, std::max(std::abs(Spin), std::abs(m)) + 2, m)];\n  } else {\n    *interpolation +=\n        l_min_harmonic *\n            goldberg_modes.data()[square(static_cast<size_t>(l_min)) +\n                                  static_cast<size_t>(l_min + m)] +\n        l_min_plus_one_harmonic *\n            gsl::at(recurrence_cache, (cache_offset + 1) % 3);\n  }\n#pragma GCC diagnostic pop\n}\n\nvoid SwshInterpolator::pup(PUP::er& p) {\n  p | l_max_;\n  p | cos_theta_;\n  p | sin_theta_;\n  p | cos_theta_over_two_;\n  p | sin_theta_over_two_;\n  p | sin_m_phi_;\n  p | cos_m_phi_;\n  p | raw_libsharp_coefficient_buffer_;\n  p | raw_goldberg_coefficient_buffer_;\n}\n\n#define GET_SPIN(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INTERPOLATION_INSTANTIATION(r, data)                                  \\\n  template void SwshInterpolator::interpolate<GET_SPIN(data)>(                \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>   \\\n          interpolated,                                                       \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& goldberg_modes) \\\n      const;                                                                  \\\n  template void SwshInterpolator::interpolate<GET_SPIN(data)>(                \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>   \\\n          interpolated,                                                       \\\n      const SpinWeighted<ComplexDataVector, GET_SPIN(data)>&                  \\\n          libsharp_collocation) const;                                        \\\n  template void                                                               \\\n  SwshInterpolator::direct_evaluation_swsh_at_l_min<GET_SPIN(data)>(          \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>   \\\n          harmonic,                                                           \\\n      const int m) const;                                                     \\\n  template void                                                               \\\n  SwshInterpolator::evaluate_swsh_at_l_min_plus_one<GET_SPIN(data)>(          \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>   \\\n          harmonic,                                                           \\\n      const SpinWeighted<ComplexDataVector, GET_SPIN(data)>&                  \\\n          harmonic_at_l_min,                                                  \\\n      const int m) const;                                                     \\\n  template void                                                               \\\n  SwshInterpolator::evaluate_swsh_m_recurrence_at_l_min<GET_SPIN(data)>(      \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>   \\\n          harmonic,                                                           \\\n      const int m) const;                                                     \\\n  template void SwshInterpolator::clenshaw_sum<GET_SPIN(data)>(               \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*>   \\\n          interpolation,                                                      \\\n      const SpinWeighted<ComplexDataVector, GET_SPIN(data)>& l_min_harmonic,  \\\n      const SpinWeighted<ComplexDataVector, GET_SPIN(data)>&                  \\\n          l_min_plus_one_harmonic,                                            \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& goldberg_modes, \\\n      const int m) const;\n\nGENERATE_INSTANTIATIONS(INTERPOLATION_INSTANTIATION, (-2, -1, 0, 1, 2))\n\n#undef INTERPOLATION_INSTANTIATION\n#undef GET_SPIN\n\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Spectral {\nnamespace Swsh {\n\n/// \\ingroup SwshGroup\n/// A utility for evaluating a particular spin-weighted spherical harmonic\n/// function at arbitrary points.\n///\n/// \\warning This should NOT be used for interpolation; such an evaluation\n/// strategy is hopelessly slow compared to Clenshaw recurrence strategies.\n/// Instead use `SwshInterpolator`.\nclass SpinWeightedSphericalHarmonic {\n public:\n  // charm needs an empty constructor\n  SpinWeightedSphericalHarmonic() = default;\n\n  SpinWeightedSphericalHarmonic(int spin, size_t l, int m);\n\n  /*!\n   *  Return by pointer the values of the spin-weighted spherical harmonic\n   *  evaluated at `theta` and `phi`.\n   *\n   *  \\details The additional values `sin_theta_over_2` and `cos_theta_over_2`,\n   *  representing \\f$\\sin(\\theta/2)\\f$ and \\f$\\cos(\\theta/2)\\f$ are taken as\n   *  required input to improve the speed of evaluation when called more than\n   *  once.\n   *\n   *  The formula we evaluate (with various prefactors precomputed, cached, and\n   *  optimized from the factorials) is \\cite Goldberg1966uu\n   *\n   *  \\f{align*}{\n   *  {}_s Y_{l m} = (-1)^m \\sqrt{\\frac{(l + m)! (l-m)! (2l + 1)}\n   *  {4 \\pi (l + s)! (l - s)!}} \\sin^{2 l}(\\theta / 2)\n   *   \\sum_{r = 0}^{l - s} {l - s \\choose r} {l + s \\choose r + s - m}\n   *  (-1)^{l - r - s} e^{i m \\phi} \\cot^{2 r + s - m}(\\theta / 2).\n   *  \\f}\n   */\n  void evaluate(gsl::not_null<ComplexDataVector*> result,\n                const DataVector& theta, const DataVector& phi,\n                const DataVector& sin_theta_over_2,\n                const DataVector& cos_theta_over_2) const;\n\n  /// Return by value the spin-weighted spherical harmonic evaluated at `theta`\n  /// and `phi`.\n  ///\n  /// \\details The additional values `sin_theta_over_2` and `cos_theta_over_2`,\n  /// representing \\f$\\sin(\\theta/2)\\f$ and \\f$\\cos(\\theta/2)\\f$ are taken as\n  /// required input to improve the speed of evaluation when called more than\n  /// once.\n  ComplexDataVector evaluate(const DataVector& theta, const DataVector& phi,\n                             const DataVector& sin_theta_over_2,\n                             const DataVector& cos_theta_over_2) const;\n\n  /// Return by value the spin-weighted spherical harmonic evaluated at `theta`\n  /// and `phi`.\n  std::complex<double> evaluate(double theta, double phi) const;\n\n  /// Serialization for Charm++.\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  int spin_ = 0;\n  size_t l_ = 0;\n  int m_ = 0;\n  double overall_prefactor_ = 0.0;\n  std::vector<double> r_prefactors_;\n};\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Performs interpolation for spin-weighted spherical harmonics by\n * taking advantage of the Clenshaw method of expanding recurrence relations.\n *\n * \\details During construction, we cache several functions of the target\n * interpolation points that will be used during the Clenshaw evaluation.\n * A new `SwshInterpolator` object must be created for each new set of target\n * points, but the member function `SwshInterpolator::interpolate()` may be\n * called on several different coefficients or collocation sets, and of\n * different spin-weights.\n *\n * Recurrence constants\n * --------------------\n * This utility obtains the Clenshaw interpolation constants from a\n * `StaticCache`, so that 'universal' quantities can be calculated only once per\n * execution and re-used on each interpolation.\n *\n * We evaluate the recurrence coefficients \\f$\\alpha_l^{(a,b)}\\f$ and\n * \\f$\\beta_l^{(a,b)}\\f$, where \\f$a = |s + m|\\f$, \\f$b = |s - m|\\f$, and\n *\n * \\f[\n * {}_sY_{l, m}(\\theta, \\phi) = \\alpha_l^{(a,b)}(\\theta) {}_s Y_{l-1, m}\n * +  \\beta_l^{(a, b)} {}_s Y_{l - 2, m}(\\theta, \\phi)\n * \\f]\n *\n * The core Clenshaw recurrence is in the\\f$l\\f$ modes of the\n * spin-weighted spherical harmonic. For a set of modes \\f$a_l^{(a,b)}\\f$ at\n * fixed \\f$m\\f$, the function value is evaluated by the recurrence:\n *\n * \\f{align*}{\n * y^{(a, b)}_{l_\\text{max} + 2} &= y^{(a, b)}_{l_\\text{max} + 1} = 0 \\\\\n * y^{(a, b)}_{l}(\\theta) &= \\alpha_{l + 1}^{(a, b)} y^{(a, b)}_{l +\n * 1}(\\theta)\n * + \\beta_{l + 2}^{(a,b)} y^{(a, b)}_{l + 2}(\\theta) + a_l^{(a, b)} \\\\\n * f_m(\\theta, \\phi) &= \\beta_{l_{\\text{min}} + 2}\n * {}_s Y_{l_{\\text{min}}, m}(\\theta, \\phi) y^{(a, b)}_2(\\theta) +\n * {}_s Y_{l_{\\text{min}} + 1, m}(\\theta, \\phi) y^{(a, b)}_1(\\theta) +\n * a_0^{(a, b)} {}_s Y_{l_{\\text{min}}, m}(\\theta, \\phi),\n * \\f}\n *\n * where \\f$l_{\\text{min}} = \\max(|m|, |s|)\\f$ is the lowest nonvanishing\n * \\f$l\\f$ mode for a given \\f$m\\f$ and \\f$s\\f$.\n *\n * The coefficients to cache are inferred from a mechanical but lengthy\n * calculation involving the recurrence relation for the Jacobi polynomials.\n * The result is:\n *\n * \\f{align*}{\n * \\alpha_l^{(a, b)} &= \\frac{\\sqrt{(2l + 1)(2l - 1)}}\n * {2\\sqrt{(l + k)(l + k + a + b)(l + k + a)(l + k + b)}}\n * \\left[2 l \\cos(\\theta) +\n * \\frac{a^2 - b^2}{2 l - 2}\\right] \\\\\n * \\beta_l^{(a, b)} & = - \\sqrt{\\frac{(2 l + 1)(l + k + a - 1)(l + k + b - 1)\n * (l + k - 1)(l + k + a + b - 1)}\n * {(2l - 3)(l + k)(l + k + a + b)(l + k + a)(l + k + b)}}\n * \\frac{2 l}{2 l - 2},\n * \\f}\n * where \\f$k = - (a + b)/2\\f$ (which is always integral due to the properties\n * of \\f$a\\f$ and \\f$b\\f$). Note that because the values of \\f$\\alpha\\f$ and\n * \\f$\\beta\\f$ in the recurrence relation are not needed for any value below\n * \\f$l_{\\text{min}} + 2\\f$, so none of the values in the square-roots or\n * denominators take pathological values for any of the coefficients we require.\n *\n * The \\f$\\beta\\f$ constants are filled in member variable `beta_constant`. The\n * \\f$\\alpha\\f$ values are separately stored as the prefactor for the\n * \\f$\\cos(\\theta)\\f$ term and the constant term in `alpha_prefactor` and\n * `alpha_constant` member variables.\n *\n * In addition, it is efficient to cache recurrence coefficients necessary for\n * generating the first couple of spin-weighted spherical harmonic functions for\n * each \\f$m\\f$ used in the Clenshaw sum.\n *\n * The member variable `harmonic_at_l_min_prefactors` holds the prefactors for\n * directly evaluating the harmonics at \\f$s >= m\\f$,\n *\n * \\f{align*}{\n * {}_s Y_{|s| m}  = {}_s C_{m} e^{i m \\phi} \\sin^a(\\theta/2) \\cos^b(\\theta/2),\n * \\f}\n *\n * where \\f${}_s C_m\\f$ are the cached prefactors that take the values\n *\n * \\f{align*}{\n * {}_s C_m = (-1)^{m + \\lambda(m)} \\sqrt{\\frac{(2 |s| + 1) (|s| + k)!}\n * {4 \\pi (|s| + k + a)! (|s| + k + b)!}}\n * \\f}\n *\n * and\n *\n * \\f{align*}{\n * \\lambda(m) =\n * \\left\\{\n * \\begin{array}{ll}\n * 0 &\\text{ for } s \\ge -m\\\\\n * s + m &\\text{ for } s < -m\n * \\end{array} \\right..\n * \\f}\n *\n * The member variable `harmonic_m_recurrence_prefactors` holds the prefactors\n * necessary to evaluate the lowest harmonics for each \\f$m\\f$ from the next\n * lowest-in-magnitude \\f$m\\f$, allowing most leading harmonics to recursively\n * derived.\n *\n * \\f{align*}{\n * {}_s Y_{|m|, m} = {}_s D_m  \\sin(\\theta / 2) \\cos(\\theta / 2)\n * \\left\\{\n * \\begin{array}{ll}\n * e^{i \\phi} {}_s Y_{|m| - 1, m - 1} &\\text{ for } m > 0\\\\\n * e^{-i \\phi} {}_s Y_{|m| - 1, m + 1} &\\text{ for } m < 0\n * \\end{array}\\right.,\n * \\f}\n *\n * where \\f${}_s D_m\\f$ are the cached prefactors that take the values\n *\n * \\f{align*}{\n * {}_s D_m = -(-1)^{\\Delta \\lambda(m)} \\sqrt{\n * \\frac{(2 |m| + 1)(k + |m| + a + b - 1)(k + |m| + a + b)}\n * {(2 |m| - 1)(k + |m| + a)(k + |m| + b)}},\n * \\f}\n * and \\f$\\Delta \\lambda(m)\\f$ is the difference in \\f$\\lambda(m)\\f$ between\n * the harmonic on the left-hand side and right-hand side of the above\n * recurrence relation, that is \\f$\\lambda(m) - \\lambda(m - 1)\\f$ for positive\n * \\f$m\\f$ and \\f$\\lambda(m) - \\lambda(m + 1)\\f$ for negative \\f$m\\f$.\n *\n * Finally, the member variable\n * `harmonic_at_l_min_plus_one_recurrence_prefactors` holds the prefactors\n * necessary to evaluate parts of the recurrence relations from the lowest\n * \\f$l\\f$ for a given \\f$m\\f$ to the next-to-lowest \\f$l\\f$.\n *\n * \\f{align*}{\n *   {}_s Y_{l_{\\min} + 1, m} = {}_s E_m\n * \\left[(a + 1) + (a + b + 2) \\frac{(\\cos(\\theta) - 1)}{2}\\right]\n * {}_s Y_{l_{\\min}, m}.\n * \\f}\n *\n * where \\f${}_s E_m\\f$ are the cached prefactors that take the values\n *\n * \\f{align*}{\n * {}_s E_{m} = \\sqrt{\\frac{2 l_{\\min} + 3}{ 2 l_{\\min} + 1}}\n * \\sqrt{\\frac{(l_{\\min} + k + 1)(l_{\\min} + k + a + b + 1)}\n * {(l_{\\min} + k + a + 1)(l_{\\min} + k + b + 1)}}\n * \\f}\n */\nclass SwshInterpolator {\n public:\n  // charm needs the empty constructor\n  SwshInterpolator() = default;\n\n  SwshInterpolator(const SwshInterpolator&) = default;\n  SwshInterpolator(SwshInterpolator&&) = default;\n  SwshInterpolator& operator=(const SwshInterpolator&) = default;\n  SwshInterpolator& operator=(SwshInterpolator&&) = default;\n  ~SwshInterpolator() = default;\n\n  SwshInterpolator(const DataVector& theta, const DataVector& phi,\n                   size_t l_max);\n\n  /*!\n   * \\brief Perform the Clenshaw recurrence sum, returning by pointer\n   * `interpolated` of interpolating the `goldberg_modes` at the collocation\n   * points passed to the constructor.\n   *\n   * \\details The core Clenshaw recurrence is in the\\f$l\\f$ modes of the\n   * spin-weighted spherical harmonic. For a set of modes \\f$a_l^{(a,b)}\\f$ at\n   * fixed \\f$m\\f$, the function value is evaluated by the recurrence:\n   *\n   * \\f{align*}{\n   * y^{(a, b)}_{l_\\text{max} + 2} &= y^{(a, b)}_{l_\\text{max} + 1} = 0 \\\\\n   * y^{(a, b)}_{l}(\\theta) &= \\alpha_{l + 1}^{(a, b)} y^{(a, b)}_{l +\n   * 1}(\\theta)\n   * + \\beta_{l + 2}^{(a,b)} y^{(a, b)}_{l + 2}(\\theta) + a_l^{(a, b)} \\\\\n   * f_m(\\theta, \\phi) &= \\beta_{l_{\\text{min}} + 2}\n   * {}_s Y_{l_{\\text{min}}, m}(\\theta, \\phi) y^{(a, b)}_2(\\theta) +\n   * {}_s Y_{l_{\\text{min}} + 1, m}(\\theta, \\phi) y^{(a, b)}_1(\\theta) +\n   * a_0^{(a, b)} {}_s Y_{l_{\\text{min}}, m}(\\theta, \\phi)\n   * \\f}\n   *\n   * The recurrence in \\f$l\\f$ accomplishes much of the work, but for full\n   * efficiency, we also recursively evaluate the lowest handful of \\f$l\\f$s for\n   * each \\f$m\\f$. The details of those additional recurrence tricks can be\n   * found in the documentation for `ClenshawRecurrenceConstants`.\n   */\n  template <int Spin>\n  void interpolate(\n      gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> interpolated,\n      const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes) const;\n\n  /*!\n   * \\brief Perform the Clenshaw recurrence sum, returning by pointer\n   * `interpolated` of interpolating function represented by\n   * `libsharp_collocation` at the target points passed to the constructor.\n   *\n   * \\details The core Clenshaw recurrence is in the\\f$l\\f$ modes of the\n   * spin-weighted spherical harmonic. For a set of modes \\f$a_l^{(a,b)}\\f$ at\n   * fixed \\f$m\\f$, the function value is evaluated by the recurrence:\n   *\n   * \\f{align*}{\n   * y^{(a, b)}_{l_\\text{max} + 2} &= y^{(a, b)}_{l_\\text{max} + 1} = 0 \\\\\n   * y^{(a, b)}_{l}(\\theta) &= \\alpha_{l + 1}^{(a, b)} y^{(a, b)}_{l +\n   * 1}(\\theta)\n   * + \\beta_{l + 2}^{(a,b)} y^{(a, b)}_{l + 2}(\\theta) + a_l^{(a, b)} \\\\\n   * f_m(\\theta, \\phi) &= \\beta_{l_{\\text{min}} + 2}\n   * {}_s Y_{l_{\\text{min}}, m}(\\theta, \\phi) y^{(a, b)}_2(\\theta) +\n   * {}_s Y_{l_{\\text{min}} + 1, m}(\\theta, \\phi) y^{(a, b)}_1(\\theta) +\n   * a_0^{(a, b)} {}_s Y_{l_{\\text{min}}, m}(\\theta, \\phi)\n   * \\f}\n   *\n   * The recurrence in \\f$l\\f$ accomplishes much of the work, but for full\n   * efficiency, we also recursively evaluate the lowest handful of \\f$l\\f$s for\n   * each \\f$m\\f$. The details of those additional recurrence tricks can be\n   * found in the documentation for `ClenshawRecurrenceConstants`.\n   */\n  template <int Spin>\n  void interpolate(\n      gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> interpolated,\n      const SpinWeighted<ComplexDataVector, Spin>& libsharp_collocation) const;\n\n  /// \\brief Evaluate the SWSH function at the lowest \\f$l\\f$ value for a given\n  /// \\f$m\\f$ at the target interpolation points.\n  ///\n  /// \\details Included in the public interface for thorough testing, most use\n  /// cases should just use the `SwshInterpolator::interpolate()` member\n  /// function.\n  template <int Spin>\n  void direct_evaluation_swsh_at_l_min(\n      gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> harmonic,\n      int m) const;\n\n  /// \\brief Evaluate the SWSH function at the next-to-lowest \\f$l\\f$ value for\n  /// a given \\f$m\\f$ at the target interpolation points, given input harmonic\n  /// values for the lowest \\f$l\\f$ value.\n  ///\n  /// \\details Included in the public interface for thorough testing, most use\n  /// cases should just use the `SwshInterpolator::interpolate()` member\n  /// function.\n  template <int Spin>\n  void evaluate_swsh_at_l_min_plus_one(\n      gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> harmonic,\n      const SpinWeighted<ComplexDataVector, Spin>& harmonic_at_l_min,\n      int m) const;\n\n  /// \\brief Evaluate the SWSH function at the lowest \\f$l\\f$ value for a given\n  /// \\f$m\\f$ at the target interpolation points, given harmonic data at the\n  /// next lower \\f$m\\f$ (by magnitude), passed in by the same pointer used for\n  /// the return.\n  ///\n  /// \\details Included in the public interface for thorough testing, most use\n  /// cases should just use the `SwshInterpolator::interpolate()` member\n  /// function.\n  template <int Spin>\n  void evaluate_swsh_m_recurrence_at_l_min(\n      gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> harmonic,\n      int m) const;\n\n  /// \\brief Perform the core Clenshaw interpolation at fixed \\f$m\\f$,\n  /// accumulating the result in `interpolation`.\n  ///\n  /// \\details Included in the public interface for thorough testing, most use\n  /// cases should just use the `interpolate` member function.\n  template <int Spin>\n  void clenshaw_sum(\n      gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> interpolation,\n      const SpinWeighted<ComplexDataVector, Spin>& l_min_harmonic,\n      const SpinWeighted<ComplexDataVector, Spin>& l_min_plus_one_harmonic,\n      const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes,\n      int m) const;\n\n  /// Serialization for Charm++.\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  size_t l_max_ = 0;\n  DataVector cos_theta_;\n  DataVector sin_theta_;\n  DataVector cos_theta_over_two_;\n  DataVector sin_theta_over_two_;\n  std::vector<DataVector> sin_m_phi_;\n  std::vector<DataVector> cos_m_phi_;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable ComplexModalVector raw_libsharp_coefficient_buffer_;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable ComplexModalVector raw_goldberg_coefficient_buffer_;\n};\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshSettings.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Spectral {\nnamespace Swsh {\n\n// In the static caching mechanism, we permit an l_max up to this setting\n// value. `CollocationMetadata` can still be constructed manually above this\n// value.\nconstexpr size_t collocation_maximum_l_max = 200;\n\nnamespace detail {\n\n// currently, the operators for the swsh derivatives and for the coefficients\n// are cached to the same maximum l_max as the collocations, but it could\n// conceivably be useful to have distinct representation maximums for them, so\n// they have separate control parameters.\nconstexpr size_t swsh_derivative_maximum_l_max = collocation_maximum_l_max;\nconstexpr size_t coefficients_maximum_l_max = collocation_maximum_l_max;\n}  // namespace detail\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n\nnamespace Spectral {\nnamespace Swsh {\nnamespace Tags {\nnamespace detail {\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<Eth>(\n    const std::string& suffix) {\n  return \"Eth(\" + suffix + \")\";\n}\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<EthEth>(\n    const std::string& suffix) {\n  return \"EthEth(\" + suffix + \")\";\n}\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<EthEthbar>(\n    const std::string& suffix) {\n  return \"EthEthbar(\" + suffix + \")\";\n}\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<Ethbar>(\n    const std::string& suffix) {\n  return \"Ethbar(\" + suffix + \")\";\n}\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<EthbarEth>(\n    const std::string& suffix) {\n  return \"EthbarEth(\" + suffix + \")\";\n}\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<EthbarEthbar>(\n    const std::string& suffix) {\n  return \"EthbarEthbar(\" + suffix + \")\";\n}\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<InverseEth>(\n    const std::string& suffix) {\n  return \"InverseEth(\" + suffix + \")\";\n}\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<InverseEthbar>(\n    const std::string& suffix) {\n  return \"InverseEthbar(\" + suffix + \")\";\n}\ntemplate <>\nstd::string compose_spin_weighted_derivative_name<NoDerivative>(\n    const std::string& suffix) {\n  return \"NoDerivative(\" + suffix + \")\";\n}\n}  // namespace detail\n}  // namespace Tags\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\nnamespace Cce::OptionTags {\nstruct Cce;\n}  // namespace Cce::OptionTags\n/// \\endcond\n\nnamespace Spectral {\nnamespace Swsh {\n/// \\cond\nclass SwshInterpolator;\n/// \\endcond\n\nnamespace OptionTags {\nstruct LMax {\n  using type = size_t;\n  static constexpr Options::String help{\n      \"Maximum l value for spin-weighted spherical harmonics\"};\n  using group = Cce::OptionTags::Cce;\n};\nstruct NumberOfRadialPoints {\n  using type = size_t;\n  static constexpr Options::String help{\n      \"Number of radial grid points in the spherical domain\"};\n  using group = Cce::OptionTags::Cce;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n\n/// \\ingroup SwshGroup\n/// \\brief Struct for labeling the \\f$\\eth\\f$ spin-weighted derivative in tags\nstruct Eth {};\n/// \\ingroup SwshGroup\n/// \\brief Struct for labeling the \\f$\\bar{\\eth}\\f$ spin-weighted derivative in\n/// tags\nstruct Ethbar {};\n/// \\ingroup SwshGroup\n/// \\brief Struct for labeling the \\f$\\eth^2\\f$ spin-weighted derivative in tags\nstruct EthEth {};\n/// \\ingroup SwshGroup\n/// \\brief Struct for labeling the \\f$\\bar{\\eth} \\eth\\f$ spin-weighted\n/// derivative in tags\nstruct EthbarEth {};\n/// \\ingroup SwshGroup\n/// \\brief Struct for labeling the \\f$\\eth \\bar{\\eth}\\f$ spin-weighted\n/// derivative in tags\nstruct EthEthbar {};\n/// \\ingroup SwshGroup\n/// \\brief Struct for labeling the \\f$\\bar{\\eth}^2\\f$ spin-weighted derivative\n/// in tags\nstruct EthbarEthbar {};\n\n/// \\ingroup SwshGroup\n/// \\brief Struct for labeling the inverse \\f$\\eth^{-1}\\f$ spin-weighted\n/// derivative in tags\nstruct InverseEth {};\n/// \\ingroup SwshGroup\n/// \\brief Struct for labeling the inverse\\f$\\bar{\\eth}^{-1}\\f$ spin-weighted\n/// derivative in tags\nstruct InverseEthbar {};\n\n/// \\brief Struct which acts as a placeholder for a spin-weighted derivative\n/// label in the spin-weighted derivative utilities, but represents a 'no-op':\n/// no derivative is taken.\nstruct NoDerivative {};\n\nnamespace detail {\ntemplate <typename DerivativeKind>\ninline constexpr int derivative_spin_weight_impl() {\n  if (std::is_same_v<DerivativeKind, Eth> or\n      std::is_same_v<DerivativeKind, InverseEthbar>) {\n    return 1;\n  } else if (std::is_same_v<DerivativeKind, Ethbar> or\n             std::is_same_v<DerivativeKind, InverseEth>) {\n    return -1;\n  } else if (std::is_same_v<DerivativeKind, EthEth>) {\n    return 2;\n  } else if (std::is_same_v<DerivativeKind, EthbarEthbar>) {\n    return -2;\n  }\n  return 0;\n}\n}  // namespace detail\n\n// utility function for determining the change of spin after a spin-weighted\n// derivative has been applied.\ntemplate <typename DerivativeKind>\nconstexpr int derivative_spin_weight =\n    detail::derivative_spin_weight_impl<DerivativeKind>();\n\nnamespace detail {\n// The below tags are used to find the new type represented by the spin-weighted\n// derivative of a spin-weighted quantity. The derivatives alter the spin\n// weights, and so the utility `adjust_spin_weight_t<Tag, DerivativeKind>` is a\n// metafunction that determines the correct spin-weighted type for the\n// spin-weighted derivative `DerivativeKind` of `Tag`.\n//\n// if there is no `Tag::type::spin`, but the internal type is a compatible\n// complex vector type, the spin associated with `Tag` is assumed to be 0.\ntemplate <typename DataType, typename DerivativeKind>\nstruct adjust_spin_weight {\n  using type =\n      Scalar<SpinWeighted<DataType, derivative_spin_weight<DerivativeKind>>>;\n};\n\n// case for if there is a `Tag::type::spin`\ntemplate <typename DataType, int Spin, typename DerivativeKind>\nstruct adjust_spin_weight<SpinWeighted<DataType, Spin>, DerivativeKind> {\n  using type = Scalar<\n      SpinWeighted<DataType, Spin + derivative_spin_weight<DerivativeKind>>>;\n};\n\ntemplate <typename Tag, typename DerivativeKind>\nusing adjust_spin_weight_t =\n    typename adjust_spin_weight<typename Tag::type::type, DerivativeKind>::type;\n\n// Helper function for creating an appropriate prefix tag name from one of the\n// spin-weighted derivative types and an existing tag name\ntemplate <typename DerivativeKind>\nstd::string compose_spin_weighted_derivative_name(const std::string& suffix);\n\n}  // namespace detail\n\n/// Convenience metafunction for accessing the tag from which a `Derivative` was\n/// derived.\ntemplate <typename Tag>\nusing derived_from = typename Tag::derived_from;\n\n/// \\ingroup SwshGroup\n/// \\brief Prefix tag representing the spin-weighted derivative of a\n/// spin-weighted scalar.\n///\n/// Template Parameters:\n/// - `Tag`: The tag to prefix\n/// - `DerivativeKind`: The type of spin-weighted derivative. This may be any of\n///   the labeling structs: `Eth`, `Ethbar`, `EthEth`, `EthbarEth`, `EthEthbar`,\n///   `EthbarEthbar`, or `NoDerivative`.\n///\n/// Type Aliases and static values:\n/// - `type`: Always a `SpinWeighted<Scalar<ComplexDataVector>, Spin>`, where\n///   `Spin` is the spin weight after the derivative `DerivativeKind` has been\n///   applied.\n/// - `tag`: An alias to the wrapped tag `Tag`. Provided for applicability to\n///   general `db::PrefixTag` functionality.\n/// - `derivative_of`: Another alias to the wrapped tag `Tag`. Provided so that\n///   utilities that use this prefix for taking derivatives can have a more\n///   expressive code style.\n/// - `derivative_kind`: Type alias to `DerivativeKind`, represents the kind of\n///   spin-weighted derivative applied to `Tag`\n/// - `spin`: The spin weight of the scalar after the derivative has been\n///   applied.\ntemplate <typename Tag, typename DerivativeKind>\nstruct Derivative : db::PrefixTag, db::SimpleTag {\n  using type = detail::adjust_spin_weight_t<Tag, DerivativeKind>;\n  using tag = Tag;\n  // derivative_of is provided as a second alias so that utilities that assume a\n  // derivative prefix tag fail earlier during compilation\n  using derivative_of = Tag;\n  using derivative_kind = DerivativeKind;\n  const static int spin = type::type::spin;\n  static std::string name() {\n    return detail::compose_spin_weighted_derivative_name<DerivativeKind>(\n        db::tag_name<Tag>());\n  }\n};\n\n/// \\ingroup SwshGroup\n/// \\brief Prefix tag representing the spin-weighted spherical harmonic\n/// transform of a spin-weighted scalar.\n///\n/// Template Parameters:\n/// - `Tag`: The tag to prefix.\n///\n/// Type aliases and static values:\n/// - `type`: Always a `SpinWeighted<Scalar<ComplexModalVector>, Spin>`, where\n///   `Spin` is the same spin weight of the pre-transformed `Tag`, and 0 if\n///   provided a `Tag` with a `Type` that is not `SpinWeighted`\n/// - `tag`: An alias to the wrapped tag `Tag`. Provided for applicability to\n///   general `db::PrefixTag` functionality\n/// - `transform_of`: Another alias to the wrapped tag `Tag`. Provided so that\n///   utilities that use this prefix for taking transforms can have a more\n///   expressive code style.\ntemplate <typename Tag>\nstruct SwshTransform : db::PrefixTag, db::SimpleTag {\n  using type = Scalar<SpinWeighted<\n      ComplexModalVector,\n      detail::adjust_spin_weight_t<Tag, NoDerivative>::type::spin>>;\n  using tag = Tag;\n  // transform_of is provided as a second alias so that utilities that assume a\n  // derivative prefix tag fail earlier during compilation\n  using transform_of = Tag;\n  const static int spin = type::type::spin;\n};\n\n/// \\ingroup SwshGroup\n/// \\brief Tag for the maximum spin-weighted spherical harmonic l; sets angular\n/// resolution.\nstruct LMax : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::LMax>;\n\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(const size_t l_max) { return l_max; }\n};\n\n/// \\ingroup SwshGroup\n/// \\brief Tag for the number of radial grid points in the three-dimensional\n/// representation of radially concentric spherical shells\nstruct NumberOfRadialPoints : db::SimpleTag {\n  using type = size_t;\n  using option_tags = tmpl::list<OptionTags::NumberOfRadialPoints>;\n\n  static constexpr bool pass_metavariables = false;\n  static size_t create_from_options(const size_t number_of_radial_points) {\n    return number_of_radial_points;\n  }\n};\n\n/// \\ingroup SwshGroup\n/// \\brief Tag for a SwshInterpolator associated with a particular set\n/// of angular coordinates.\n///\n/// \\details It is recommended to use this to store a `SwshInterpolator` in the\n/// \\ref DataBoxGroup of a parallel component, as interpolations can be\n/// significantly faster if they don't have to re-construct the\n/// `SwhsInterpolator` as frequently.\ntemplate <typename Tag>\nstruct SwshInterpolator : db::SimpleTag, db::PrefixTag {\n  using tag = Tag;\n  using type = ::Spectral::Swsh::SwshInterpolator;\n};\n\n}  // namespace Tags\n\nnamespace detail {\ntemplate <typename Tag, typename S>\nstruct has_spin : std::bool_constant<Tag::type::type::spin == S::value> {};\n\ntemplate <typename TagList>\nstruct spins_in_tag_list;\n\ntemplate <typename... Tags>\nstruct spins_in_tag_list<tmpl::list<Tags...>> {\n  using type = tmpl::sort<tmpl::remove_duplicates<\n      tmpl::list<std::integral_constant<int, Tags::type::type::spin>...>>>;\n};\n}  // namespace detail\n\n/// \\ingroup SwshGroup\n/// \\brief A metafunction for determining the coefficient buffers needed by\n/// `angular_derivatives()` to avoid repeatedly allocating space for modal\n/// data each time a derivative is taken.\n/// \\note Using these buffers is required to use the batch\n/// `angular_derivatives()` rather than the individual `angular_derivative()`.\ntemplate <typename DerivativeTag>\nusing coefficient_buffer_tags_for_derivative_tag = tmpl::list<\n    Spectral::Swsh::Tags::SwshTransform<typename DerivativeTag::derivative_of>,\n    Spectral::Swsh::Tags::SwshTransform<DerivativeTag>>;\n\n/// \\ingroup SwshGroup\n/// \\brief Get a list of distinct spins appearing in `TagList`.\n///\n/// \\snippet Test_SwshTags.cpp spins_in_tag_list\ntemplate <typename TagList>\nusing spins_in_tag_list = typename detail::spins_in_tag_list<TagList>::type;\n\n/// \\ingroup SwshGroup\n/// \\brief Partition `TagList` into lists with the same spin.\n///\n/// \\snippet Test_SwshTags.cpp partition_tags_by_spin\ntemplate <typename TagList>\nusing partition_tags_by_spin = tmpl::transform<\n    spins_in_tag_list<TagList>,\n    tmpl::lazy::filter<\n        tmpl::pin<TagList>,\n        tmpl::defer<detail::has_spin<tmpl::_1, tmpl::parent<tmpl::_1>>>>>;\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace Spectral::Swsh {\n\nnamespace detail {\ntemplate <ComplexRepresentation Representation>\nvoid append_libsharp_collocation_pointers(\n    const gsl::not_null<std::vector<double*>*> collocation_data,\n    const gsl::not_null<std::vector<ComplexDataView<Representation>>*>\n        collocation_views,\n    const gsl::not_null<ComplexDataVector*> vector, const size_t l_max,\n    const bool positive_spin) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_radial_points =\n      vector->size() / number_of_angular_points;\n\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    collocation_views->push_back(detail::ComplexDataView<Representation>{\n        vector, number_of_angular_points, i * number_of_angular_points});\n    collocation_data->push_back(collocation_views->back().real_data());\n    // alteration needed because libsharp doesn't support negative spins\n    if (not positive_spin) {\n      collocation_views->back().conjugate();\n    }\n    collocation_data->push_back(collocation_views->back().imag_data());\n  }\n}\n\nvoid append_libsharp_coefficient_pointers(\n    const gsl::not_null<std::vector<std::complex<double>*>*> coefficient_data,\n    const gsl::not_null<ComplexModalVector*> vector, const size_t l_max) {\n  const size_t number_of_coefficients =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n  const size_t number_of_radial_points =\n      vector->size() / number_of_coefficients;\n\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    // coefficients associated with the real part\n    coefficient_data->push_back(vector->data() + i * number_of_coefficients);\n    // coefficients associated with the imaginary part\n    coefficient_data->push_back(vector->data() +\n                                (2 * i + 1) * (number_of_coefficients / 2));\n  }\n}\n\ntemplate <ComplexRepresentation Representation>\nvoid execute_libsharp_transform_set(\n    const sharp_jobtype& jobtype, const int spin,\n    const gsl::not_null<std::vector<std::complex<double>*>*> coefficient_data,\n    const gsl::not_null<std::vector<double*>*> collocation_data,\n    const gsl::not_null<const CollocationMetadata<Representation>*>\n        collocation_metadata,\n    const sharp_alm_info* alm_info, const size_t num_transforms) {\n  // libsharp considers two arrays per transform when spin is not zero.\n  const size_t number_of_arrays_per_transform = (spin == 0 ? 1 : 2);\n  // libsharp has an internal flag for the maximum number of transforms, so if\n  // we have more than max_libsharp_transforms, we have to do them in chunks\n  // of max_libsharp_transforms.\n  for (size_t transform_block = 0;\n       transform_block <\n       (num_transforms + max_libsharp_transforms - 1) / max_libsharp_transforms;\n       ++transform_block) {\n    if (transform_block < (num_transforms / max_libsharp_transforms)) {\n      // clang-tidy cppcoreguidelines-pro-bounds-pointer-arithmetic\n      sharp_execute(jobtype, abs(spin),\n                    coefficient_data->data() +  // NOLINT\n                        number_of_arrays_per_transform *\n                            max_libsharp_transforms * transform_block,\n                    collocation_data->data() +  // NOLINT\n                        number_of_arrays_per_transform *\n                            max_libsharp_transforms * transform_block,\n                    collocation_metadata->get_sharp_geom_info(), alm_info,\n                    max_libsharp_transforms, SHARP_DP, nullptr, nullptr);\n    } else {\n      // clang-tidy cppcoreguidelines-pro-bounds-pointer-arithmetic\n      sharp_execute(jobtype, abs(spin),\n                    coefficient_data->data() +  // NOLINT\n                        number_of_arrays_per_transform *\n                            max_libsharp_transforms * transform_block,\n                    collocation_data->data() +  // NOLINT\n                        number_of_arrays_per_transform *\n                            max_libsharp_transforms * transform_block,\n                    collocation_metadata->get_sharp_geom_info(), alm_info,\n                    num_transforms % max_libsharp_transforms, SHARP_DP, nullptr,\n                    nullptr);\n    }\n  }\n}\n}  // namespace detail\n\ntemplate <ComplexRepresentation Representation, int Spin>\nSpinWeighted<ComplexModalVector, Spin> swsh_transform(\n    const size_t l_max, const size_t number_of_radial_points,\n    const SpinWeighted<ComplexDataVector, Spin>& collocation) {\n  SpinWeighted<ComplexModalVector, Spin> result_vector{};\n  swsh_transform<Representation, Spin>(l_max, number_of_radial_points,\n                                       make_not_null(&result_vector),\n                                       collocation);\n  return result_vector;\n}\n\ntemplate <ComplexRepresentation Representation, int Spin>\nSpinWeighted<ComplexDataVector, Spin> inverse_swsh_transform(\n    const size_t l_max, const size_t number_of_radial_points,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_coefficients) {\n  SpinWeighted<ComplexDataVector, Spin> result_vector{};\n  inverse_swsh_transform<Representation, Spin>(l_max, number_of_radial_points,\n                                               make_not_null(&result_vector),\n                                               libsharp_coefficients);\n  return result_vector;\n}\n\ntemplate <int Spin>\nvoid interpolate_to_collocation(\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> target,\n    const SpinWeighted<ComplexDataVector, Spin>& source,\n    const size_t target_l_max, const size_t source_l_max,\n    const size_t number_of_radial_points) {\n  const auto source_modes =\n      swsh_transform(source_l_max, number_of_radial_points, source);\n  SpinWeighted<ComplexModalVector, Spin> target_modes{\n      size_of_libsharp_coefficient_vector(target_l_max) *\n      number_of_radial_points};\n\n  const auto& target_coefficient_metadata =\n      cached_coefficients_metadata(target_l_max);\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    auto source_coefficient_metadata_iterator =\n        cached_coefficients_metadata(source_l_max).begin();\n    for (const auto coefficient : target_coefficient_metadata) {\n      // first, we advance `source_coefficient_metadata_iterator` to the same\n      // mode that is represented by `coefficient`, or to the next mode\n      // following `coefficient` that is present in the source resolution, or to\n      // the iterator end if neither exists. Note: this assumes the libsharp\n      // coefficient ordering for optimizations\n      while (source_coefficient_metadata_iterator !=\n                 cached_coefficients_metadata(source_l_max).end() and\n             ((*source_coefficient_metadata_iterator).m < coefficient.m or\n              ((*source_coefficient_metadata_iterator).l < coefficient.l and\n               (*source_coefficient_metadata_iterator).m == coefficient.m))) {\n        ++source_coefficient_metadata_iterator;\n      }\n      // assign the current coefficient if present in both the source and the\n      // target\n      if (source_coefficient_metadata_iterator !=\n              cached_coefficients_metadata(source_l_max).end() and\n          coefficient.l == (*source_coefficient_metadata_iterator).l and\n          coefficient.m == (*source_coefficient_metadata_iterator).m) {\n        target_modes\n            .data()[i * size_of_libsharp_coefficient_vector(target_l_max) +\n                    coefficient.transform_of_real_part_offset] =\n            source_modes\n                .data()[i * size_of_libsharp_coefficient_vector(source_l_max) +\n                        (*source_coefficient_metadata_iterator)\n                            .transform_of_real_part_offset];\n        target_modes\n            .data()[i * size_of_libsharp_coefficient_vector(target_l_max) +\n                    coefficient.transform_of_imag_part_offset] =\n            source_modes\n                .data()[i * size_of_libsharp_coefficient_vector(source_l_max) +\n                        (*source_coefficient_metadata_iterator)\n                            .transform_of_imag_part_offset];\n      } else {\n        // assign 0.0 if the coefficient is present in the target and not in the\n        // source representation\n        target_modes\n            .data()[i * size_of_libsharp_coefficient_vector(target_l_max) +\n                    coefficient.transform_of_real_part_offset] =\n            std::complex<double>(0.0, 0.0);\n        target_modes\n            .data()[i * size_of_libsharp_coefficient_vector(target_l_max) +\n                    coefficient.transform_of_imag_part_offset] =\n            std::complex<double>(0.0, 0.0);\n      }\n    }\n  }\n  inverse_swsh_transform(target_l_max, number_of_radial_points, target,\n                         target_modes);\n}\n\n#define GET_REPRESENTATION(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define GET_SPIN(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define SWSH_TRANSFORM_INSTANTIATION(r, data)                              \\\n  template SpinWeighted<ComplexModalVector, GET_SPIN(data)>                \\\n  swsh_transform<GET_REPRESENTATION(data), GET_SPIN(data)>(                \\\n      const size_t l_max, const size_t number_of_radial_points,            \\\n      const SpinWeighted<ComplexDataVector, GET_SPIN(data)>& collocation); \\\n  template SpinWeighted<ComplexDataVector, GET_SPIN(data)>                 \\\n  inverse_swsh_transform<GET_REPRESENTATION(data), GET_SPIN(data)>(        \\\n      const size_t l_max, const size_t number_of_radial_points,            \\\n      const SpinWeighted<ComplexModalVector, GET_SPIN(data)>& coefficients);\n\n#define SWSH_TRANSFORM_UTILITIES_INSTANTIATION(r, data)                   \\\n  template void append_libsharp_collocation_pointers(                     \\\n      const gsl::not_null<std::vector<double*>*> collocation_data,        \\\n      const gsl::not_null<                                                \\\n          std::vector<ComplexDataView<GET_REPRESENTATION(data)>>*>        \\\n          collocation_views,                                              \\\n      const gsl::not_null<ComplexDataVector*> vector, const size_t l_max, \\\n      const bool positive_spin);                                          \\\n  template void execute_libsharp_transform_set(                           \\\n      const sharp_jobtype& jobtype, const int spin,                       \\\n      const gsl::not_null<std::vector<std::complex<double>*>*>            \\\n          coefficient_data,                                               \\\n      const gsl::not_null<std::vector<double*>*> collocation_data,        \\\n      const gsl::not_null<                                                \\\n          const CollocationMetadata<GET_REPRESENTATION(data)>*>           \\\n          collocation_metadata,                                           \\\n      const sharp_alm_info* alm_info, const size_t num_transforms);\n\nnamespace detail {\nGENERATE_INSTANTIATIONS(SWSH_TRANSFORM_UTILITIES_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags))\n}  // namespace detail\n\nGENERATE_INSTANTIATIONS(SWSH_TRANSFORM_INSTANTIATION,\n                        (ComplexRepresentation::Interleaved,\n                         ComplexRepresentation::RealsThenImags),\n                        (-2, -1, 0, 1, 2))\n\n#undef GET_REPRESENTATION\n#undef GET_SPIN\n\n#define GET_SPIN(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define SWSH_INTERPOLATION_INSTANTIATION(r, data)                           \\\n  template void interpolate_to_collocation<GET_SPIN(data)>(                 \\\n      const gsl::not_null<SpinWeighted<ComplexDataVector, GET_SPIN(data)>*> \\\n          target,                                                           \\\n      const SpinWeighted<ComplexDataVector, GET_SPIN(data)>& source,        \\\n      const size_t target_l_max, const size_t source_l_max,                 \\\n      const size_t number_of_radial_points);\n\nGENERATE_INSTANTIATIONS(SWSH_INTERPOLATION_INSTANTIATION, (-2, -1, 0, 1, 2))\n\n#undef GET_SPIN\n#undef SWSH_INTERPOLATION_INSTANTIATION\n#undef SWSH_TRANSFORM_INSTANTIATION\n#undef SWSH_TRANSFORM_UTILITIES_INSTANTIATION\n\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "src/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <sharp_cxx.h>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass ComplexDataVector;\nclass ComplexModalVector;\n\nnamespace Spectral {\nnamespace Swsh {\n\nnamespace detail {\n// libsharp has an internal maximum number of transforms that is not in the\n// public interface, so we must hard-code its value here\nstatic const size_t max_libsharp_transforms = 100;\n\n// appends to a vector of pointers `collocation_data` the set of pointers\n// associated with angular views of the provided collocation data `vector`. The\n// associated views are appended into `collocation_views`. If `positive_spin` is\n// false, this function will conjugate the views, and therefore potentially\n// conjugate (depending on `Representation`) the input data `vectors`. This\n// behavior is chosen to avoid frequent copies of the input data `vector` for\n// optimization. The conjugation must be undone after the call to libsharp\n// transforms using `conjugate_views` to restore the input data to its original\n// state.\ntemplate <ComplexRepresentation Representation>\nvoid append_libsharp_collocation_pointers(\n    gsl::not_null<std::vector<double*>*> collocation_data,\n    gsl::not_null<std::vector<ComplexDataView<Representation>>*>\n        collocation_views,\n    gsl::not_null<ComplexDataVector*> vector, size_t l_max, bool positive_spin);\n\n// Perform a conjugation on a vector of `ComplexDataView`s if `Spin` < 0. This\n// is used for undoing the conjugation described in the code comment for\n// `append_libsharp_collocation_pointers`\ntemplate <int Spin, ComplexRepresentation Representation>\nSPECTRE_ALWAYS_INLINE void conjugate_views(\n    gsl::not_null<std::vector<ComplexDataView<Representation>>*>\n        collocation_views) {\n  if (Spin < 0) {\n    for (auto& view : *collocation_views) {\n      view.conjugate();\n    }\n  }\n}\n\n// appends to a vector of pointers the set of pointers associated with\n// angular views of the provided coefficient vector. When working with the\n// libsharp coefficient representation, note the intricacies mentioned in\n// the documentation for `TransformJob`.\nvoid append_libsharp_coefficient_pointers(\n    gsl::not_null<std::vector<std::complex<double>*>*> coefficient_data,\n    gsl::not_null<ComplexModalVector*> vector, size_t l_max);\n\n// perform the actual libsharp execution calls on an input and output set of\n// pointers. This function handles the complication of a limited maximum number\n// of simultaneous transforms, performing multiple execution calls on pointer\n// blocks if necessary.\ntemplate <ComplexRepresentation Representation>\nvoid execute_libsharp_transform_set(\n    const sharp_jobtype& jobtype, int spin,\n    gsl::not_null<std::vector<std::complex<double>*>*> coefficient_data,\n    gsl::not_null<std::vector<double*>*> collocation_data,\n    gsl::not_null<const CollocationMetadata<Representation>*>\n        collocation_metadata,\n    const sharp_alm_info* alm_info, size_t num_transforms);\n\n// template 'implementation' for the `swsh_transform` function below which\n// performs an arbitrary number of transforms, and places them in the same\n// number of destination modal containers passed by pointer.\n// template notes: the parameter pack represents a collection of ordered modal\n// data passed by pointer, followed by ordered nodal data passed by const\n// reference. The first modal value is separated to infer the `Spin` template\n// parameter.\ntemplate <ComplexRepresentation Representation, int Spin,\n          typename... ModalThenNodalTypes, size_t... Is>\nvoid swsh_transform_impl(\n    size_t l_max, size_t number_of_radial_points,\n    std::index_sequence<Is...> /*meta*/,\n    gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*> first_coefficient,\n    const ModalThenNodalTypes&... coefficients_then_collocations);\n\n// template 'implementation' for the `inverse_swsh_transform` function below\n// which performs an arbitrary number of transforms, and places them in the same\n// number of destination nodal containers passed by pointer.\n// template notes: the parameter pack represents a collection of ordered nodal\n// data passed by pointer, followed by ordered modal data passed by const\n// reference. The first nodal value is separated to infer the `Spin` template\n// parameter.\ntemplate <ComplexRepresentation Representation, int Spin,\n          typename... NodalThenModalTypes, size_t... Is>\nvoid inverse_swsh_transform_impl(\n    size_t l_max, size_t number_of_radial_points,\n    std::index_sequence<Is...> /*meta*/,\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> first_collocation,\n    const NodalThenModalTypes&... collocations_then_coefficients);\n\n// forward declaration for friend specification of helper from\n// `SwshDerivatives.hpp`\ntemplate <typename Transform, typename FullTagList>\nstruct dispatch_to_transform;\n}  // namespace detail\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Perform a forward libsharp spin-weighted spherical harmonic transform\n * on any number of supplied `SpinWeighted<ComplexDataVector, Spin>`.\n *\n * \\details This function places the result in one or more\n * `SpinWeighted<ComplexModalVector, Spin>` returned via provided\n * pointer.  This is a simpler interface to the same functionality as the\n * \\ref DataBoxGroup mutation compatible `SwshTransform`.\n *\n * The following parameter ordering for the multiple input interface is enforced\n * by the interior function calls, but is not obvious from the explicit\n * parameter packs in this function signature:\n *\n * - `size_t l_max` : angular resolution for the transform\n * - `size_t number_of_radial_points` : radial resolution (number of consecutive\n * transforms in each of the vectors)\n * - any number of `gsl::not_null<SpinWeighted<ComplexModalVector,\n * Spin>*>`, all sharing the same `Spin` : The return-by-pointer containers for\n * the transformed modal quantities\n * - the same number of `const SpinWeighted<ComplexDataVector, Spin>&`, with the\n * same `Spin` as the previous function argument set : The input containers of\n * nodal spin-weighted spherical harmonic data.\n *\n * template parameters:\n * - `Representation`: Either `ComplexRepresentation::Interleaved` or\n * `ComplexRepresentation::RealsThenImags`, indicating the representation for\n * intermediate steps of the transformation. The two representations will give\n * identical results but may help or hurt performance depending on the task.\n * If unspecified, defaults to `ComplexRepresentation::Interleaved`.\n *\n * The result is a set of libsharp-compatible coefficients.\n * \\see SwshTransform for more details on the mathematics of the libsharp\n * data structures.\n *\n * \\warning The collocation data is taken by const reference, but can be\n * temporarily altered in-place during intermediate parts of the computation.\n * The input data is guaranteed to return to its original state by the end of\n * the function. In a setting in which multiple threads access the same data\n * passed as input to this function, a lock must be used to prevent access\n * during the execution of the transform.\n */\ntemplate <\n    ComplexRepresentation Representation = ComplexRepresentation::Interleaved,\n    int Spin, typename... ModalThenNodalTypes>\nvoid swsh_transform(\n    const size_t l_max, const size_t number_of_radial_points,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>\n        first_coefficient,\n    const ModalThenNodalTypes&... coefficients_then_collocations) {\n  static_assert(\n      sizeof...(ModalThenNodalTypes) % 2 == 1,\n      \"The number of modal arguments passed to swsh_transform must equal the \"\n      \"number of nodal arguments, so the total number of arguments must be \"\n      \"even.\");\n  detail::swsh_transform_impl<Representation>(\n      l_max, number_of_radial_points,\n      std::make_index_sequence<(sizeof...(coefficients_then_collocations) + 1) /\n                               2>{},\n      first_coefficient, coefficients_then_collocations...);\n}\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Perform a forward libsharp spin-weighted spherical harmonic transform\n * on a single supplied `SpinWeighted<ComplexDataVector, Spin>`.\n *\n * \\details This function returns a `SpinWeighted<ComplexModalVector, Spin>` by\n * value (causing an allocation). This is a simpler interface to the same\n * functionality as the \\ref DataBoxGroup mutation compatible `SwshTransform`.\n * If you have two or more vectors to transform at once, consider the\n * pass-by-pointer version of `Spectral::Swsh::swsh_transform` or the \\ref\n * DataBoxGroup interface `InverseSwshTransform`, as they are more efficient for\n * performing several transforms at once.\n *\n * The result is a set of libsharp-compatible coefficients.\n * \\see SwshTransform for more details on the mathematics of the libsharp\n * data structures.\n *\n * \\warning The collocation data is taken by const reference, but can be\n * temporarily altered in-place during intermediate parts of the computation.\n * The input data is guaranteed to return to its original state by the end of\n * the function. In a setting in which multiple threads access the same data\n * passed as input to this function, a lock must be used to prevent access\n * during the execution of the transform.\n */\ntemplate <\n    ComplexRepresentation Representation = ComplexRepresentation::Interleaved,\n    int Spin>\nSpinWeighted<ComplexModalVector, Spin> swsh_transform(\n    size_t l_max, size_t number_of_radial_points,\n    const SpinWeighted<ComplexDataVector, Spin>& collocation);\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Perform an inverse libsharp spin-weighted spherical harmonic transform\n * on any number of supplied `SpinWeighted<ComplexModalVector, Spin>`.\n *\n * \\details This function places the result in one or more\n * `SpinWeighted<ComplexDataVector, Spin>` returned via provided\n * pointer.  This is a simpler interface to the same functionality as the\n * \\ref DataBoxGroup mutation compatible `InverseSwshTransform`.\n *\n * The following parameter ordering for the multiple input interface is enforced\n * by the interior function calls, but is not obvious from the explicit\n * parameter packs in this function signature:\n *\n * - `size_t l_max` : angular resolution for the transform\n * - `size_t number_of_radial_points` : radial resolution (number of consecutive\n * transforms in each of the vectors)\n * - any number of `gsl::not_null<SpinWeighted<ComplexDataVector,\n * Spin>*>`, all sharing the same `Spin` : The return-by-pointer containers for\n * the transformed nodal quantities\n * - the same number of `const SpinWeighted<ComplexModalVector, Spin>&`, with\n * the same `Spin` as the previous function argument set : The input containers\n * of modal spin-weighted spherical harmonic data.\n *\n * template parameters:\n * - `Representation`: Either `ComplexRepresentation::Interleaved` or\n * `ComplexRepresentation::RealsThenImags`, indicating the representation for\n * intermediate steps of the transformation. The two representations will give\n * identical results but may help or hurt performance depending on the task.\n * If unspecified, defaults to `ComplexRepresentation::Interleaved`.\n *\n * The input is a set of libsharp-compatible coefficients.\n * \\see SwshTransform for more details on the mathematics of the libsharp\n * data structures.\n */\ntemplate <\n    ComplexRepresentation Representation = ComplexRepresentation::Interleaved,\n    int Spin, typename... NodalThenModalTypes>\nvoid inverse_swsh_transform(\n    const size_t l_max, const size_t number_of_radial_points,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*>\n        first_collocation,\n    const NodalThenModalTypes&... collocations_then_coefficients) {\n  static_assert(\n      sizeof...(NodalThenModalTypes) % 2 == 1,\n      \"The number of nodal arguments passed to inverse_swsh_transform must \"\n      \"equal the number of modal arguments, so the total number of arguments \"\n      \"must be even.\");\n  detail::inverse_swsh_transform_impl<Representation>(\n      l_max, number_of_radial_points,\n      std::make_index_sequence<(sizeof...(collocations_then_coefficients) + 1) /\n                               2>{},\n      first_collocation, collocations_then_coefficients...);\n}\n\n/*!\n * \\ingroup SwshGroup\n * \\brief Perform an inverse libsharp spin-weighted spherical harmonic transform\n * on a single supplied `SpinWeighted<ComplexModalVector, Spin>`.\n *\n * \\details This function returns a `SpinWeighted<ComplexDataVector, Spin>` by\n * value (causing an allocation). This is a simpler interface to the same\n * functionality as the \\ref DataBoxGroup mutation compatible\n * `InverseSwshTransform`. If you have two or more vectors to transform at once,\n * consider the pass-by-pointer version of\n * `Spectral::Swsh::inverse_swsh_transform` or the \\ref DataBoxGroup interface\n * `SwshTransform`, as they are more efficient for performing several transforms\n * at once.\n *\n * The input is a set of libsharp-compatible coefficients.\n * \\see SwshTransform for more details on the mathematics of the libsharp\n * data structures.\n */\ntemplate <\n    ComplexRepresentation Representation = ComplexRepresentation::Interleaved,\n    int Spin>\nSpinWeighted<ComplexDataVector, Spin> inverse_swsh_transform(\n    size_t l_max, size_t number_of_radial_points,\n    const SpinWeighted<ComplexModalVector, Spin>& libsharp_coefficients);\n\n/*!\n * \\ingroup SwshGroup\n * \\brief A \\ref DataBoxGroup mutate-compatible computational struct for\n * performing several spin-weighted spherical harmonic transforms. Internally\n * dispatches to libsharp.\n *\n * \\details\n * Template Parameters:\n * - `Representation`: The element of the `ComplexRepresentation` enum which\n *   parameterizes how the data passed to libsharp will be represented. Two\n *   options are available:\n *  - `ComplexRepresentation:Interleaved`: indicates that the real and imaginary\n *    parts of the collocation values will be passed to libsharp as pointers\n *    into existing complex data structures, minimizing copies, but maintaining\n *    a stride of 2 for 'adjacent' real or imaginary values.\n *  - `ComplexRepresentation::RealsThenImags`: indicates that the real and\n *    imaginary parts of the collocation values will be passed to libsharp as\n *    separate contiguous blocks. At current, this introduces both allocations\n *    and copies.  **optimization note** In the future most of the allocations\n *    can be aggregated to calling code, which would pass in buffers by\n *    `not_null` pointers.\n *\n *  For performance-sensitive code, both options should be tested, as each\n *  strategy has trade-offs.\n * - `TagList`: A `tmpl::list` of Tags to be forward transformed. The tags must\n * represent the nodal data.\n *\n * \\note The signs obtained from libsharp transformations must be handled\n * carefully. (In particular, it does not use the sign convention you will find\n * in [Wikipedia]\n * (https://en.wikipedia.org/wiki/Spin-weighted_spherical_harmonics)).\n * - libsharp deals only with the transformation of real values, so\n *   transformation of complex values must be done for real and imaginary parts\n *   separately.\n * - due to only transforming real components, it stores only the positive\n *   \\f$m\\f$ modes (as the rest would be redundant). Therefore, the negative\n *   \\f$m\\f$ modes must be inferred from conjugation and further sign changes.\n * - libsharp only has the capability of transforming positive spin weighted\n *   quantities. Therefore, additional steps are taken (involving further\n *   conjugation of the provided data) in order to use those utilities on\n *   negative spin-weighted quantities. The resulting modes have yet more sign\n *   changes that must be taken into account.\n *\n * The decomposition resulting from the libsharp transform for spin \\f$s\\f$ and\n * complex spin-weighted \\f${}_s f\\f$ can be represented mathematically as:\n *\n * \\f{align*}{\n * {}_s f(\\theta, \\phi) = \\sum_{\\ell = 0}^{\\ell_\\mathrm{max}} \\Bigg\\{&\n * \\left(\\sum_{m = 0}^{\\ell} a^\\mathrm{sharp, real}_{l m} {}_s Y_{\\ell\n * m}^\\mathrm{sharp, real}\\right) + \\left(\\sum_{m=1}^{\\ell}\n * \\left(a^\\mathrm{sharp, real}_{l m}{}\\right)^*\n * {}_s Y_{\\ell -m}^\\mathrm{sharp, real}\\right) \\notag\\\\\n * &+ i \\left[\\left(\\sum_{m = 0}^{\\ell} a^\\mathrm{sharp, imag}_{l m} {}_s\n * Y_{\\ell m}^\\mathrm{sharp, imag}\\right) + \\left(\\sum_{m=1}^{\\ell}\n * \\left(a^\\mathrm{sharp, imag}_{l m}{}\\right)^* {}_s Y_{\\ell -m}^\\mathrm{sharp,\n * imag} \\right)\\right] \\Bigg\\},\n * \\f}\n *\n * where\n *\n * \\f{align*}{\n * {}_s Y_{\\ell m}^\\mathrm{sharp, real} &=\n * \\begin{cases}\n * (-1)^{s + 1} {}_s Y_{\\ell m}, & \\mathrm{for}\\; s < 0, m \\ge 0 \\\\\n * {}_s Y_{\\ell m}, & \\mathrm{for}\\; s = 0, m \\ge 0 \\\\\n * - {}_s Y_{\\ell m}, & \\mathrm{for}\\; s > 0, m \\ge 0 \\\\\n * (-1)^{s + m + 1} {}_s Y_{\\ell m}, & \\mathrm{for}\\; s < 0, m < 0 \\\\\n * (-1)^{m} {}_s Y_{\\ell m}, & \\mathrm{for}\\; s = 0, m < 0 \\\\\n * (-1)^{m + 1} {}_s Y_{\\ell m}, & \\mathrm{for}\\; s > 0, m < 0\n * \\end{cases} \\\\\n * &\\equiv {}_s S_{\\ell m}^{\\mathrm{real}} {}_s Y_{\\ell m}\\\\\n * {}_s Y_{\\ell m}^\\mathrm{sharp, imag} &=\n * \\begin{cases}\n * (-1)^{s + 1} {}_s Y_{\\ell m}, & \\mathrm{for}\\; s < 0, m \\ge 0 \\\\\n * -{}_s Y_{\\ell m}, & \\mathrm{for}\\; s = 0, m \\ge 0 \\\\\n * {}_s Y_{\\ell m}, & \\mathrm{for}\\; s > 0, m \\ge 0 \\\\\n * (-1)^{s + m} {}_s Y_{\\ell m}, & \\mathrm{for}\\; s < 0, m < 0 \\\\\n * (-1)^{m} {}_s Y_{\\ell m}, & \\mathrm{for}\\; s = 0, m < 0 \\\\\n * (-1)^{m + 1} {}_s Y_{\\ell m}, & \\mathrm{for}\\; s > 0, m < 0\n * \\end{cases} \\\\\n * &\\equiv {}_s S_{\\ell m}^{\\mathrm{real}} {}_s Y_{\\ell m},\n * \\f}\n *\n * where the unadorned \\f${}_s Y_{\\ell m}\\f$ on the right-hand-sides follow the\n * (older) convention of \\cite Goldberg1966uu. Note the phase in these\n * expressions is not completely standardized, so should be checked carefully\n * whenever coefficient data is directly manipulated.\n *\n * For reference, we can compute the relation between Goldberg spin-weighted\n * moments \\f${}_s f_{\\ell m}\\f$, defined as\n *\n * \\f[ {}_s f(\\theta, \\phi) = \\sum_{\\ell = 0}^{\\ell_\\mathrm{max}} \\sum_{m =\n * -\\ell}^{\\ell} {}_s f_{\\ell m} {}_s Y_{\\ell m}\n * \\f]\n *\n * so,\n * \\f[\n * {}_s f_{\\ell m} =\n * \\begin{cases}\n * a_{\\ell m}^{\\mathrm{sharp}, \\mathrm{real}}  {}_s S_{\\ell m}^{\\mathrm{real}} +\n * a_{\\ell m}^{\\mathrm{sharp}, \\mathrm{imag}}  {}_s S_{\\ell m}^{\\mathrm{imag}} &\n * \\mathrm{for} \\; m \\ge 0 \\\\\n * \\left(a_{\\ell -m}^{\\mathrm{sharp}, \\mathrm{real}}\\right)^* {}_s S_{\\ell\n * m}^{\\mathrm{real}} + \\left(a_{\\ell -m}^{\\mathrm{sharp},\n * \\mathrm{imag}}\\right)^* {}_s S_{\\ell m}^{\\mathrm{imag}} &\n * \\mathrm{for} \\; m < 0 \\\\\n * \\end{cases} \\f]\n *\n *\n * The resulting coefficients \\f$a_{\\ell m}\\f$ are stored in a triangular,\n * \\f$\\ell\\f$-varies-fastest configuration. So, for instance, the first\n * \\f$\\ell_\\mathrm{max}\\f$ entries contain the coefficients for \\f$m=0\\f$ and\n * all \\f$\\ell\\f$s, and the next \\f$\\ell_\\mathrm{max} - 1\\f$ entries contain the\n * coefficients for \\f$m=1\\f$ and \\f$1 \\le \\ell \\le \\ell_\\mathrm{max} \\f$, and\n * so on.\n */\ntemplate <typename TagList, ComplexRepresentation Representation =\n                                ComplexRepresentation::Interleaved>\nstruct SwshTransform;\n\n/// \\cond\ntemplate <typename... TransformTags, ComplexRepresentation Representation>\nstruct SwshTransform<tmpl::list<TransformTags...>, Representation> {\n  static constexpr int spin =\n      tmpl::front<tmpl::list<TransformTags...>>::type::type::spin;\n\n  static_assert(tmpl2::flat_all_v<TransformTags::type::type::spin == spin...>,\n                \"All Tags in TagList submitted to SwshTransform must have the \"\n                \"same spin weight.\");\n\n  using return_tags = tmpl::list<Tags::SwshTransform<TransformTags>...>;\n  using argument_tags =\n      tmpl::list<TransformTags..., Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      const gsl::not_null<\n          typename Tags::SwshTransform<TransformTags>::type*>... coefficients,\n      const typename TransformTags::type&... collocations, const size_t l_max,\n      const size_t number_of_radial_points) {\n    // forward to the version which takes parameter packs of vectors\n    apply_to_vectors(make_not_null(&get(*coefficients))...,\n                     get(collocations)..., l_max, number_of_radial_points);\n  }\n\n  // the convenience transform functions call the private member directly, so\n  // need to be friends\n  // redundant declaration due to forward declaration needs in earlier part of\n  // file and friend requirements\n  template <ComplexRepresentation FriendRepresentation, int FriendSpin,\n            typename... CoefficientThenCollocationTypes, size_t... Is>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend void detail::swsh_transform_impl(\n      size_t, size_t, std::index_sequence<Is...>,\n      gsl::not_null<SpinWeighted<ComplexModalVector, FriendSpin>*>,\n      const CoefficientThenCollocationTypes&...);\n\n  // friend declaration for helper function from `SwshDerivatives.hpp`\n  template <typename Transform, typename FullTagList>\n  friend struct detail::dispatch_to_transform;\n\n private:\n  static void apply_to_vectors(\n      const gsl::not_null<typename Tags::SwshTransform<\n          TransformTags>::type::type*>... coefficients,\n      const typename TransformTags::type::type&... collocations, size_t l_max,\n      size_t number_of_radial_points);\n};\n/// \\endcond\n\n/*!\n * \\ingroup SwshGroup\n * \\brief A \\ref DataBoxGroup mutate-compatible computational struct for\n * performing several spin-weighted inverse spherical harmonic transforms.\n * Internally dispatches to libsharp.\n *\n * \\details\n * Template Parameters:\n * - `Representation`: The element of the `ComplexRepresentation` enum which\n *   parameterizes how the data passed to libsharp will be represented. Two\n *   options are available:\n *  - `ComplexRepresentation:Interleaved`: indicates that the real and imaginary\n *    parts of the collocation values will be passed to libsharp as pointers\n *    into existing complex data structures, minimizing copies, but maintaining\n *    a stride of 2 for 'adjacent' real or imaginary values.\n *  - `ComplexRepresentation::RealsThenImags`: indicates that the real and\n *    imaginary parts of the collocation values will be passed to libsharp as\n *    separate contiguous blocks. At current, this introduces both allocations\n *    and copies.  **optimization note** In the future most of the allocations\n *    can be aggregated to calling code, which would pass in buffers by\n *    `not_null` pointers.\n *\n *  For performance-sensitive code, both options should be tested, as each\n *  strategy has trade-offs.\n * - `TagList`: A `tmpl::list` of Tags to be inverse transformed. The tags must\n * represent the nodal data being transformed to.\n *\n * \\see `SwshTransform` for mathematical notes regarding the libsharp modal\n * representation taken as input for this computational struct.\n */\ntemplate <typename TagList, ComplexRepresentation Representation =\n                                ComplexRepresentation::Interleaved>\nstruct InverseSwshTransform;\n\n/// \\cond\ntemplate <typename... TransformTags, ComplexRepresentation Representation>\nstruct InverseSwshTransform<tmpl::list<TransformTags...>, Representation> {\n  static constexpr int spin =\n      tmpl::front<tmpl::list<TransformTags...>>::type::type::spin;\n\n  static_assert(\n      tmpl2::flat_all_v<TransformTags ::type::type::spin == spin...>,\n      \"All Tags in TagList submitted to InverseSwshTransform must have the \"\n      \"same spin weight.\");\n\n  using return_tags = tmpl::list<TransformTags...>;\n  using argument_tags = tmpl::list<Tags::SwshTransform<TransformTags>...,\n                                   Tags::LMax, Tags::NumberOfRadialPoints>;\n\n  static void apply(\n      const gsl::not_null<typename TransformTags::type*>... collocations,\n      const typename Tags::SwshTransform<TransformTags>::type&... coefficients,\n      const size_t l_max, const size_t number_of_radial_points) {\n    // forward to the version which takes parameter packs of vectors\n    apply_to_vectors(make_not_null(&get(*collocations))...,\n                     get(coefficients)..., l_max, number_of_radial_points);\n  }\n\n  // the convenience transform functions call the private member directly, so\n  // need to be friends\n  // redundant declaration due to forward declaration needs in earlier part of\n  // file and friend requirements\n  template <ComplexRepresentation FriendRepresentation, int FriendSpin,\n            typename... CollocationThenCoefficientTypes, size_t... Is>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend void detail::inverse_swsh_transform_impl(\n      size_t, size_t, std::index_sequence<Is...>,\n      gsl::not_null<SpinWeighted<ComplexDataVector, FriendSpin>*>,\n      const CollocationThenCoefficientTypes&...);\n\n  // friend declaration for helper function from `SwshDerivatives.hpp`\n  template <typename Transform, typename FullTagList>\n  friend struct detail::dispatch_to_transform;\n\n private:\n  static void apply_to_vectors(\n      const gsl::not_null<typename TransformTags::type::type*>... collocations,\n      const typename Tags::SwshTransform<\n          TransformTags>::type::type&... coefficients,\n      size_t l_max, size_t number_of_radial_points);\n};\n\ntemplate <typename... TransformTags, ComplexRepresentation Representation>\nvoid SwshTransform<tmpl::list<TransformTags...>, Representation>::\n    apply_to_vectors(const gsl::not_null<typename Tags::SwshTransform<\n                         TransformTags>::type::type*>... coefficients,\n                     const typename TransformTags::type::type&... collocations,\n                     const size_t l_max, const size_t number_of_radial_points) {\n  EXPAND_PACK_LEFT_TO_RIGHT(coefficients->destructive_resize(\n      size_of_libsharp_coefficient_vector(l_max) * number_of_radial_points));\n\n  // assemble a list of pointers into the collocation point data. This is\n  // required because libsharp expects pointers to pointers.\n  std::vector<detail::ComplexDataView<Representation>> pre_transform_views;\n  pre_transform_views.reserve(number_of_radial_points *\n                              sizeof...(TransformTags));\n  std::vector<double*> pre_transform_collocation_data;\n  pre_transform_collocation_data.reserve(2 * number_of_radial_points *\n                                         sizeof...(TransformTags));\n\n  // clang-tidy: const-cast, object is temporarily modified and returned to\n  // original state\n  EXPAND_PACK_LEFT_TO_RIGHT(detail::append_libsharp_collocation_pointers(\n      make_not_null(&pre_transform_collocation_data),\n      make_not_null(&pre_transform_views),\n      make_not_null(&const_cast<typename TransformTags::type::type&>(  // NOLINT\n                         collocations)\n                         .data()),\n      l_max, spin >= 0));\n\n  std::vector<std::complex<double>*> post_transform_coefficient_data;\n  post_transform_coefficient_data.reserve(2 * number_of_radial_points *\n                                          sizeof...(TransformTags));\n\n  EXPAND_PACK_LEFT_TO_RIGHT(detail::append_libsharp_coefficient_pointers(\n      make_not_null(&post_transform_coefficient_data),\n      make_not_null(&coefficients->data()), l_max));\n\n  const size_t num_transforms =\n      (spin == 0 ? 2 : 1) * number_of_radial_points * sizeof...(TransformTags);\n  const auto* collocation_metadata =\n      &cached_collocation_metadata<Representation>(l_max);\n  const auto* alm_info =\n      cached_coefficients_metadata(l_max).get_sharp_alm_info();\n\n  detail::execute_libsharp_transform_set(\n      SHARP_MAP2ALM, spin, make_not_null(&post_transform_coefficient_data),\n      make_not_null(&pre_transform_collocation_data),\n      make_not_null(collocation_metadata), alm_info, num_transforms);\n\n  detail::conjugate_views<spin>(make_not_null(&pre_transform_views));\n}\n\ntemplate <typename... TransformTags, ComplexRepresentation Representation>\nvoid InverseSwshTransform<tmpl::list<TransformTags...>, Representation>::\n    apply_to_vectors(const gsl::not_null<\n                         typename TransformTags::type::type*>... collocations,\n                     const typename Tags::SwshTransform<\n                         TransformTags>::type::type&... coefficients,\n                     const size_t l_max, const size_t number_of_radial_points) {\n  EXPAND_PACK_LEFT_TO_RIGHT(collocations->destructive_resize(\n      number_of_swsh_collocation_points(l_max) * number_of_radial_points));\n\n  std::vector<std::complex<double>*> pre_transform_coefficient_data;\n  pre_transform_coefficient_data.reserve(2 * number_of_radial_points *\n                                         sizeof...(TransformTags));\n  // clang-tidy: const-cast, object is temporarily modified and returned to\n  // original state\n  EXPAND_PACK_LEFT_TO_RIGHT(detail::append_libsharp_coefficient_pointers(\n      make_not_null(&pre_transform_coefficient_data),\n      make_not_null(&const_cast<typename Tags::SwshTransform<  // NOLINT\n                         TransformTags>::type::type&>(coefficients)\n                         .data()),\n      l_max));\n\n  std::vector<detail::ComplexDataView<Representation>> post_transform_views;\n  post_transform_views.reserve(number_of_radial_points *\n                               sizeof...(TransformTags));\n  std::vector<double*> post_transform_collocation_data;\n  post_transform_collocation_data.reserve(2 * number_of_radial_points *\n                                          sizeof...(TransformTags));\n\n  EXPAND_PACK_LEFT_TO_RIGHT(detail::append_libsharp_collocation_pointers(\n      make_not_null(&post_transform_collocation_data),\n      make_not_null(&post_transform_views),\n      make_not_null(&collocations->data()), l_max, true));\n\n  // libsharp considers two arrays per transform when spin is not zero.\n  const size_t num_transforms =\n      (spin == 0 ? 2 : 1) * number_of_radial_points * sizeof...(TransformTags);\n  const auto* collocation_metadata =\n      &cached_collocation_metadata<Representation>(l_max);\n  const auto* alm_info =\n      cached_coefficients_metadata(l_max).get_sharp_alm_info();\n\n  detail::execute_libsharp_transform_set(\n      SHARP_ALM2MAP, spin, make_not_null(&pre_transform_coefficient_data),\n      make_not_null(&post_transform_collocation_data),\n      make_not_null(collocation_metadata), alm_info, num_transforms);\n\n  detail::conjugate_views<spin>(make_not_null(&post_transform_views));\n\n  // The inverse transformed collocation data has just been placed in the\n  // memory blocks controlled by the `ComplexDataView`s. Finally, that data\n  // must be flushed back to the Variables.\n  for (auto& view : post_transform_views) {\n    view.copy_back_to_source();\n  }\n}\n/// \\endcond\n\nnamespace detail {\n\ntemplate <ComplexRepresentation Representation, int Spin,\n          typename... ModalThenNodalTypes, size_t... Is>\nvoid swsh_transform_impl(\n    const size_t l_max, const size_t number_of_radial_points,\n    std::index_sequence<Is...> /*meta*/,\n    const gsl::not_null<SpinWeighted<ComplexModalVector, Spin>*>\n        first_coefficient,\n    const ModalThenNodalTypes&... coefficients_then_collocations) {\n  SwshTransform<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<Is, ComplexDataVector>,\n                                      std::integral_constant<int, Spin>>...>>::\n      apply_to_vectors(first_coefficient, coefficients_then_collocations...,\n                       l_max, number_of_radial_points);\n}\n\ntemplate <ComplexRepresentation Representation, int Spin,\n          typename... NodalThenModalTypes, size_t... Is>\nvoid inverse_swsh_transform_impl(\n    const size_t l_max, const size_t number_of_radial_points,\n    std::index_sequence<Is...> /*meta*/,\n    const gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*>\n        first_collocation,\n    const NodalThenModalTypes&... collocations_then_coefficients) {\n  InverseSwshTransform<\n      tmpl::list<::Tags::SpinWeighted<::Tags::TempScalar<Is, ComplexDataVector>,\n                                      std::integral_constant<int, Spin>>...>>::\n      apply_to_vectors(first_collocation, collocations_then_coefficients...,\n                       l_max, number_of_radial_points);\n}\n\ntemplate <ComplexRepresentation Representation, typename PartitionedTagList>\nstruct make_transform_list_impl;\n\ntemplate <ComplexRepresentation Representation, typename... Partition>\nstruct make_transform_list_impl<Representation, tmpl::list<Partition...>> {\n  using type = tmpl::list<SwshTransform<Partition, Representation>...>;\n};\n\ntemplate <ComplexRepresentation Representation, typename PartitionedTagList>\nstruct make_inverse_transform_list_impl;\n\ntemplate <ComplexRepresentation Representation, typename... Partition>\nstruct make_inverse_transform_list_impl<Representation,\n                                        tmpl::list<Partition...>> {\n  using type = tmpl::list<InverseSwshTransform<Partition, Representation>...>;\n};\n}  // namespace detail\n\n/// @{\n/// \\ingroup SwshGroup\n/// \\brief Assemble a `tmpl::list` of `SwshTransform`s or\n/// `InverseSwshTransform`s given a list of tags `TagList` that need to be\n/// transformed. The `Representation` is the\n/// `Spectral::Swsh::ComplexRepresentation` to use for the transformations.\n///\n/// \\snippet Test_SwshTransform.cpp make_transform_list\ntemplate <ComplexRepresentation Representation, typename TagList>\nusing make_transform_list = typename detail::make_transform_list_impl<\n    Representation, partition_tags_by_spin<TagList>>::type;\n\ntemplate <ComplexRepresentation Representation, typename TagList>\nusing make_inverse_transform_list =\n    typename detail::make_inverse_transform_list_impl<\n        Representation, partition_tags_by_spin<TagList>>::type;\n/// @}\n\n/// \\ingroup SwshGroup\n/// \\brief Assemble a `tmpl::list` of `SwshTransform`s given a list of\n/// `Spectral::Swsh::Tags::Derivative<Tag, Derivative>` that need to be\n/// computed. The `SwshTransform`s constructed by this type alias correspond to\n/// the `Tag`s in the list.\n///\n/// \\details This is intended as a convenience utility for the first step of a\n/// derivative routine, where one transforms the set of fields for which\n/// derivatives are required.\n///\n/// \\snippet Test_SwshTransform.cpp make_transform_from_derivative_tags\ntemplate <ComplexRepresentation Representation, typename DerivativeTagList>\nusing make_transform_list_from_derivative_tags = make_transform_list<\n    Representation,\n    tmpl::remove_duplicates<tmpl::transform<\n        DerivativeTagList, tmpl::bind<db::remove_tag_prefix, tmpl::_1>>>>;\n\n/// \\ingroup SwshGroup\n/// \\brief Convert spin-weighted spherical harmonic data to a new set of\n/// collocation points (either downsampling or upsampling)\ntemplate <int Spin>\nvoid interpolate_to_collocation(\n    gsl::not_null<SpinWeighted<ComplexDataVector, Spin>*> target,\n    const SpinWeighted<ComplexDataVector, Spin>& source, size_t target_l_max,\n    size_t source_l_max, size_t number_of_radial_points);\n\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "src/Options/Auto.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace Options {\n/// The label representing the absence of a value for `Options::Auto`\nnamespace AutoLabel {\n/// 'Auto' label\nstruct Auto {};\n/// 'None' label\nstruct None {};\n/// 'All' label\nstruct All {};\n}  // namespace AutoLabel\n\n/// \\ingroup OptionParsingGroup\n/// \\brief A class indicating that a parsed value can be automatically\n/// computed instead of specified.\n///\n/// When an `Auto<T>` is parsed from an input file, the value may be specified\n/// either as the `AutoLabel` (defaults to \"Auto\") or as a value of type `T`.\n/// When this class is passed to the constructor of the class taking it as an\n/// option, it can be implicitly converted to a `std::optional<U>`, for any\n/// type `U` implicitly creatable from a `T`.\n///\n/// \\snippet Test_Auto.cpp example_class\n/// \\snippet Test_Auto.cpp example_create\ntemplate <typename T, typename Label = AutoLabel::Auto>\nclass Auto {\n public:\n  using value_type = std::optional<T>;\n\n  Auto() = default;\n  explicit Auto(T value) : value_(std::move(value)) {}\n\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  template <typename U>\n  operator std::optional<U>() && {\n    return std::move(value_);\n  }\n\n  void pup(PUP::er& p) { p | value_; }\n\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  operator const value_type&() const { return value_; }\n\n private:\n  value_type value_{};\n};\n\ntemplate <typename T, typename Label>\nbool operator==(const Auto<T, Label>& a, const Auto<T, Label>& b) {\n  return static_cast<const std::optional<T>&>(a) ==\n         static_cast<const std::optional<T>&>(b);\n}\n\ntemplate <typename T, typename Label>\nbool operator!=(const Auto<T, Label>& a, const Auto<T, Label>& b) {\n  return not(a == b);\n}\n\ntemplate <typename T, typename Label>\nstd::ostream& operator<<(std::ostream& os, const Auto<T, Label>& x) {\n  const std::optional<T>& value = x;\n  if (value) {\n    return os << get_output(*value);\n  } else {\n    return os << pretty_type::name<Label>();\n  }\n}\n\nnamespace Auto_detail {\ntemplate <typename Label>\nstruct AutoLabel {};\n}  // namespace Auto_detail\n\ntemplate <typename Label>\nstruct create_from_yaml<Auto_detail::AutoLabel<Label>> {\n  template <typename Metavariables>\n  static Auto_detail::AutoLabel<Label> create(const Option& options) {\n    const auto label_string = pretty_type::name<Label>();\n    try {\n      if (options.parse_as<std::string>() == label_string) {\n        return {};\n      }\n    } catch (...) {\n      // The node failed to parse as a string.  It is not the Label.\n    }\n    // The error if the std::variant parse fails will print the value\n    // from the input file (and the T parse probably will too), so we\n    // don't need to print it again.\n    PARSE_ERROR(options.context(),\n                \"Failed to parse as Auto label \\\"\" << label_string << \"\\\"\");\n  }\n};\n\ntemplate <typename T, typename Label>\nstruct create_from_yaml<Auto<T, Label>> {\n  template <typename Metavariables>\n  static Auto<T, Label> create(const Option& options) {\n    auto parsed_variant =\n        options.parse_as<std::variant<Auto_detail::AutoLabel<Label>, T>,\n                         Metavariables>();\n    if (std::holds_alternative<T>(parsed_variant)) {\n      return Auto<T, Label>{std::move(std::get<T>(parsed_variant))};\n    } else {\n      return Auto<T, Label>{};\n    }\n  }\n};\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Options)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Comparator.cpp\n  Context.cpp\n  ParseOptions.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Auto.hpp\n  Comparator.hpp\n  Context.hpp\n  Factory.hpp\n  FactoryHelpers.hpp\n  Options.hpp\n  OptionsDetails.hpp\n  ParseError.hpp\n  ParseOptions.hpp\n  StdComplex.hpp\n  String.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ErrorHandling\n  Printf\n  Utilities\n  yaml-cpp::yaml-cpp\n  PRIVATE\n  Informer\n  )\n\nadd_subdirectory(Protocols)\n"
  },
  {
    "path": "src/Options/Comparator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Options/Comparator.hpp\"\n\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/ParseOptions.hpp\"\n\nnamespace Options {\nComparator::Comparator(Comparison comparison) : comparison_(comparison) {}\n\n// NOLINTNEXTLINE(google-runtime-references)\nvoid Comparator::pup(PUP::er& p) { p | comparison_; }\n\nComparator create_from_yaml<Comparator>::create_impl(const Option& options) {\n  const auto name = options.parse_as<std::string>();\n  if (name == \"EqualTo\") {\n    return Comparator(Comparator::Comparison::EqualTo);\n  }\n  if (name == \"NotEqualTo\") {\n    return Comparator(Comparator::Comparison::NotEqualTo);\n  }\n  if (name == \"LessThan\") {\n    return Comparator(Comparator::Comparison::LessThan);\n  }\n  if (name == \"GreaterThan\") {\n    return Comparator(Comparator::Comparison::GreaterThan);\n  }\n  if (name == \"LessThanOrEqualTo\") {\n    return Comparator(Comparator::Comparison::LessThanOrEqualTo);\n  }\n  if (name == \"GreaterThanOrEqualTo\") {\n    return Comparator(Comparator::Comparison::GreaterThanOrEqualTo);\n  }\n  PARSE_ERROR(options.context(),\n              \"Invalid comparison \" << name << \".  Should be EqualTo, \"\n              \"NotEqualTo, LessThan, GreaterThan, LessThanOrEqualTo, or \"\n              \"GreaterThanOrEqualTo.\");\n}\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/Comparator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Options {\n/// An option-creatable mathematical comparison.\nclass Comparator {\n public:\n  enum class Comparison {\n    EqualTo,\n    NotEqualTo,\n    LessThan,\n    GreaterThan,\n    LessThanOrEqualTo,\n    GreaterThanOrEqualTo\n  };\n\n  Comparator() = default;\n  Comparator(Comparison comparison);\n\n  template <typename T1, typename T2>\n  bool operator()(const T1& t1, const T2& t2) const {\n    switch (comparison_) {\n      case Comparison::EqualTo:\n        return t1 == t2;\n      case Comparison::NotEqualTo:\n        return t1 != t2;\n      case Comparison::LessThan:\n        return t1 < t2;\n      case Comparison::GreaterThan:\n        return t1 > t2;\n      case Comparison::LessThanOrEqualTo:\n        return t1 <= t2;\n      case Comparison::GreaterThanOrEqualTo:\n        return t1 >= t2;\n      default:\n        ERROR(\"Invalid comparison\");\n    }\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  Comparison comparison_;\n};\n\ntemplate <>\nstruct create_from_yaml<Comparator> {\n  template <typename Metavariables>\n  static Comparator create(const Option& options) {\n    return create_impl(options);\n  }\n\n private:\n  static Comparator create_impl(const Option& options);\n};\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/Context.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Options/Context.hpp\"\n\n#include <ostream>\n\nnamespace Options {\nstd::ostream& operator<<(std::ostream& s, const Context& c) {\n  s << c.context;\n  if (c.line >= 0 and c.column >= 0) {\n    s << \"At line \" << c.line + 1 << \" column \" << c.column + 1 << \":\\n\";\n  }\n  return s;\n}\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/Context.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n#include <string>\n\nnamespace Options {\n/// \\ingroup OptionParsingGroup\n/// Information about the nested operations being performed by the\n/// parser, for use in printing errors.  A default-constructed\n/// Context is printed as an empty string.  This struct is\n/// primarily used as an argument to PARSE_ERROR for reporting input\n/// file parsing errors.  Users outside of the core option parsing\n/// code should not need to manipulate the contents.\nstruct Context {\n  bool top_level{true};\n  /// (Part of) the parsing \"backtrace\" printed with an error\n  std::string context;\n  /// File line number (0 based)\n  int line{-1};\n  /// File column number (0 based)\n  int column{-1};\n\n  /// Append a line to the context.  Automatically appends a colon.\n  void append(const std::string& c) { context += c + \":\\n\"; }\n};\n\nstd::ostream& operator<<(std::ostream& s, const Context& c);\n\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class template Factory.\n\n#pragma once\n\n#include <algorithm>\n#include <iomanip>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <yaml-cpp/yaml.h>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetStaticMemberVariableOrDefault.hpp\"\n\nnamespace Options {\nnamespace Factory_detail {\nstruct print_derived {\n  // Not a stream because brigand requires the functor to be copyable.\n  std::string value;\n  template <typename T>\n  void operator()(tmpl::type_<T> /*meta*/) {\n    // These are zero-based\n    const size_t name_col = 2;\n    const size_t help_col = 22;\n    const size_t end_col = 80;\n\n    std::ostringstream ss;\n    ss << std::left << std::setw(name_col) << \"\"\n       << std::setw(help_col - name_col - 1) << pretty_type::name<T>();\n    if (ss.str().size() >= help_col) {\n      ss << \"\\n\" << std::setw(help_col - 1) << \"\";\n    }\n\n    std::string help_snippet(T::help);\n    if (help_snippet.size() > end_col - help_col) {\n      help_snippet.resize(end_col - help_col - 3);\n      help_snippet += \"...\";\n    }\n    std::replace(help_snippet.begin(), help_snippet.end(), '\\n', ' ');\n    ss << \" \" << help_snippet << \"\\n\";\n\n    value += ss.str();\n  }\n};\n\ntemplate <typename CreatableClasses>\nstd::string help_derived() {\n  return \"Known Ids:\\n\" +\n         tmpl::for_each<CreatableClasses>(\n             Factory_detail::print_derived{})\n             .value;\n}\n\n// This is for handling legacy code that still uses creatable_classes.\n// It should be inlined once everything is converted.\ntemplate <typename BaseClass, typename Metavariables, typename = std::void_t<>>\nstruct get_creatable_classes {\n  using factory_creation = typename Metavariables::factory_creation;\n  // This assertion is normally done in tests, but the executable\n  // metavariables don't have tests, so we do it here.\n  static_assert(tt::assert_conforms_to_v<factory_creation,\n                                         Options::protocols::FactoryCreation>);\n  using type = tmpl::at<typename factory_creation::factory_classes, BaseClass>;\n};\n\ntemplate <typename BaseClass, typename Metavariables>\nstruct get_creatable_classes<\n  BaseClass, Metavariables,\n  std::void_t<typename BaseClass::creatable_classes>> {\n  using type = typename BaseClass::creatable_classes;\n};\n\nCREATE_GET_STATIC_MEMBER_VARIABLE_OR_DEFAULT(factory_creatable)\n\ntemplate <typename T>\nstruct is_factory_creatable\n    : std::bool_constant<get_factory_creatable_or_default_v<T, true>> {};\n\ntemplate <typename BaseClass, typename Metavariables>\nstd::unique_ptr<BaseClass> create(const Option& options) {\n  using all_creatable_classes =\n      typename get_creatable_classes<BaseClass, Metavariables>::type;\n  static_assert(not std::is_same_v<all_creatable_classes, tmpl::no_such_type_>,\n                \"List of creatable derived types for this class is missing \"\n                \"from Metavariables::factory_classes.\");\n  using creatable_classes =\n      tmpl::filter<all_creatable_classes, is_factory_creatable<tmpl::_1>>;\n\n  const auto& node = options.node();\n  Option derived_opts(options.context());\n  derived_opts.append_context(\"While operating factory for \" +\n                              pretty_type::name<BaseClass>());\n  std::string id;\n  if (node.IsScalar()) {\n    id = node.as<std::string>();\n  } else if (node.IsMap()) {\n    if (node.size() != 1) {\n      PARSE_ERROR(derived_opts.context(),\n                  \"Expected a class name (and possibly options), got multiple \"\n                  \"key-value pairs:\\n\" << node);\n    }\n    id = node.begin()->first.as<std::string>();\n    derived_opts.set_node(node.begin()->second);\n  } else if (node.IsNull()) {\n    PARSE_ERROR(derived_opts.context(),\n                \"Expected a class name (and possibly options):\\n\"\n                << help_derived<creatable_classes>());\n  } else {\n    PARSE_ERROR(derived_opts.context(),\n                \"Expected a class or a class with options, got:\\n\"\n                << node);\n  }\n\n  std::unique_ptr<BaseClass> result;\n  tmpl::for_each<creatable_classes>(\n      [&id, &derived_opts, &result](auto derived_v) {\n        using Derived = tmpl::type_from<decltype(derived_v)>;\n        if (pretty_type::name<Derived>() == id) {\n          ASSERT(result == nullptr, \"Duplicate factory id: \" << id);\n          result = std::make_unique<Derived>(\n              derived_opts.parse_as<Derived, Metavariables>());\n        }\n      });\n  if (result != nullptr) {\n    return result;\n  }\n  PARSE_ERROR(derived_opts.context(),\n              \"Unknown Id '\" << id << \"'\\n\"\n              << help_derived<creatable_classes>());\n}\n}  // namespace Factory_detail\n\ntemplate <typename T>\nstruct create_from_yaml<std::unique_ptr<T>> {\n  template <typename Metavariables>\n  static std::unique_ptr<T> create(const Option& options) {\n    return Factory_detail::create<T, Metavariables>(options);\n  }\n};\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/FactoryHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Options {\nnamespace detail {\ntemplate <typename FactoryClasses, typename... NewClasses>\nstruct add_factory_classes;\n\ntemplate <typename FactoryClasses, typename BaseClass, typename NewClasses,\n          typename... RestNewClasses>\nstruct add_factory_classes<FactoryClasses, tmpl::pair<BaseClass, NewClasses>,\n                           RestNewClasses...>\n    : add_factory_classes<\n          tmpl::insert<\n              tmpl::erase<FactoryClasses, BaseClass>,\n              tmpl::pair<\n                  BaseClass,\n                  tmpl::remove_duplicates<tmpl::append<\n                      tmpl::conditional_t<\n                          tmpl::has_key<FactoryClasses, BaseClass>::value,\n                          tmpl::at<FactoryClasses, BaseClass>, tmpl::list<>>,\n                      NewClasses>>>>,\n          RestNewClasses...> {};\n\ntemplate <typename FactoryClasses>\nstruct add_factory_classes<FactoryClasses> {\n  using type = FactoryClasses;\n};\n}  // namespace detail\n\n/// Add new factory-creatable classes to the list in a\n/// `factory_creation` struct.\n///\n/// For each `tmpl::pair<Base, DerivedList>` in the `NewClasses`\n/// parameter pack, append the classes in `DerivedList` to the list\n/// for `Base` in `FactoryClasses`.  The `FactoryClasses` map need not\n/// have a preexisting entry for `Base`.\n///\n/// This example adds three new derived classes, two for one base\n/// class and one for another.\n///\n/// \\snippet Test_FactoryHelpers.cpp add_factory_classes\n///\n/// \\see Options::protocols::FactoryCreation\ntemplate <typename FactoryClasses, typename... NewClasses>\nusing add_factory_classes =\n    typename detail::add_factory_classes<FactoryClasses, NewClasses...>::type;\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/Options.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines classes and functions for making classes creatable from\n/// input files.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <type_traits>\n\n#include \"Options/Context.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace YAML {\nclass Node;\n}  // namespace YAML\n/// \\endcond\n\n/// Utilities for parsing input files.\nnamespace Options {\n/// \\ingroup OptionParsingGroup\n/// The type that options are passed around as.  Contains YAML node\n/// data and an Context.\n///\n/// \\note To use any methods on this class in a concrete function you\n/// must include ParseOptions.hpp, but you do *not* need to include\n/// that header to use this in an uninstantiated\n/// `create_from_yaml::create` function.\nclass Option {\n public:\n  const Context& context() const;\n\n  /// Append a line to the contained context.\n  void append_context(const std::string& context);\n\n  /// Convert to an object of type `T`.\n  template <typename T, typename Metavariables = NoSuchType>\n  T parse_as() const;\n\n  /// \\note This constructor overwrites the mark data in the supplied\n  /// context with the one from the node.\n  ///\n  /// \\warning This method is for internal use of the option parser.\n  explicit Option(YAML::Node node, Context context = {});\n\n  /// \\warning This method is for internal use of the option parser.\n  explicit Option(Context context);\n\n  /// \\warning This method is for internal use of the option parser.\n  const YAML::Node& node() const;\n\n  /// Sets the node and updates the context's mark to correspond to it.\n  ///\n  /// \\warning This method is for internal use of the option parser.\n  void set_node(YAML::Node node);\n\n private:\n  std::unique_ptr<YAML::Node> node_;\n  Context context_;\n};\n\n/// \\ingroup OptionParsingGroup\n/// Used by the parser to create an object.  The default action is to\n/// parse options using `T::options`.  This struct may be specialized\n/// to change that behavior for specific types.\n///\n/// Do not call create directly.  Use Option::parse_as instead.\ntemplate <typename T>\nstruct create_from_yaml {\n  template <typename Metavariables>\n  static T create(const Option& options);\n};\n\n/// Provide multiple ways to construct a class.\n///\n/// This type may be included in an option list along with option\n/// tags.  When creating the class, the parser will choose one of the\n/// lists of options to use, depending on the user input.\n///\n/// The class must be prepared to accept any of the possible\n/// alternatives as arguments to its constructor.  To disambiguate\n/// multiple alternatives with the same types, a constructor may take\n/// the full list of option tags expected as its first argument.\n///\n/// \\snippet Test_Options.cpp alternatives\ntemplate <typename... AlternativeLists>\nstruct Alternatives {\n  static_assert(sizeof...(AlternativeLists) >= 1,\n                \"Option alternatives must provide at least one alternative.\");\n  static_assert(\n      tmpl::all<tmpl::list<tt::is_a<tmpl::list, AlternativeLists>...>>::value,\n      \"Option alternatives must be given as tmpl::lists.\");\n  static_assert(\n      tmpl::none<\n          tmpl::list<std::is_same<tmpl::list<>, AlternativeLists>...>>::value,\n      \"All option alternatives must have at least one option.\");\n};\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/OptionsDetails.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines helpers for the Parser<T> class\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <iomanip>\n#include <list>\n#include <map>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsInteger.hpp\"\n#include \"Utilities/WrapText.hpp\"\n\nnamespace Options::Options_detail {\n\ntemplate <typename Tag>\nstruct flatten_alternatives {\n  using type = Tag;\n};\n\ntemplate <typename... Tags>\nstruct flatten_alternatives<tmpl::list<Tags...>> {\n  using type =\n      tmpl::flatten<tmpl::list<typename flatten_alternatives<Tags>::type...>>;\n};\n\ntemplate <typename... Tags>\nstruct flatten_alternatives<Options::Alternatives<Tags...>> {\n  using type = tmpl::list<typename flatten_alternatives<Tags>::type...>;\n};\n\n// Traverses the group hierarchy of `Tag`, returning the topmost group that is\n// a subgroup of `Root`. Directly returns `Tag` if it has no group.\n// This means that the returned type is always the direct child node of `Root`\n// that contains `Tag` in its hierarchy.\n// If `Root` is not in the group hierarchy of `Tag`, this function returns the\n// topmost group of `Tag` (meaning that `Root` is treated as the root of its\n// group hierarchy).\ntemplate <typename Tag, typename Root, typename = std::void_t<>>\nstruct find_subgroup {\n  using type = Tag;\n};\n\ntemplate <typename Tag>\nstruct find_subgroup<Tag, typename Tag::group,\n                     std::void_t<typename Tag::group>> {\n  using type = Tag;\n};\n\ntemplate <typename Tag, typename Root>\nstruct find_subgroup<Tag, Root, std::void_t<typename Tag::group>> {\n  using type = typename find_subgroup<typename Tag::group, Root>::type;\n};\n\n/// Checks if `Tag` is within the group hierarchy of `Group`.\ntemplate <typename Tag, typename Group, typename = std::void_t<>>\nstruct is_in_group : std::false_type {};\n\ntemplate <typename Tag>\nstruct is_in_group<Tag, typename Tag::group, std::void_t<typename Tag::group>>\n    : std::true_type {};\n\ntemplate <typename Tag, typename Group>\nstruct is_in_group<Tag, Group, std::void_t<typename Tag::group>>\n    : is_in_group<typename Tag::group, Group> {};\n\n/// The subset of tags in `OptionList` that are in the hierarchy of `Group`\ntemplate <typename OptionList, typename Group>\nusing options_in_group =\n    tmpl::filter<OptionList, is_in_group<tmpl::_1, tmpl::pin<Group>>>;\n\n// Display a type in a pseudo-YAML form, leaving out likely irrelevant\n// information.\ntemplate <typename T>\nstruct yaml_type {\n  static std::string value() { return pretty_type::name<T>(); }\n};\n\ntemplate <typename T>\nstruct yaml_type<std::unique_ptr<T>> {\n  static std::string value() { return yaml_type<T>::value(); }\n};\n\ntemplate <typename T>\nstruct yaml_type<std::vector<T>> {\n  static std::string value() { return \"[\" + yaml_type<T>::value() + \", ...]\"; }\n};\n\ntemplate <typename T>\nstruct yaml_type<std::list<T>> {\n  static std::string value() { return \"[\" + yaml_type<T>::value() + \", ...]\"; }\n};\n\ntemplate <typename T, size_t N>\nstruct yaml_type<std::array<T, N>> {\n  static std::string value() {\n    return \"[\" + yaml_type<T>::value() + \" x\" + std::to_string(N) + \"]\";\n  }\n};\n\ntemplate <typename K, typename V, typename C>\nstruct yaml_type<std::map<K, V, C>> {\n  static std::string value() {\n    return \"{\" + yaml_type<K>::value() + \": \" + yaml_type<V>::value() + \"}\";\n  }\n};\n\ntemplate <typename K, typename V, typename H, typename E>\nstruct yaml_type<std::unordered_map<K, V, H, E>> {\n  static std::string value() {\n    return \"{\" + yaml_type<K>::value() + \": \" + yaml_type<V>::value() + \"}\";\n  }\n};\n\ntemplate <typename T, typename U>\nstruct yaml_type<std::pair<T, U>> {\n  static std::string value() {\n    return \"[\" + yaml_type<T>::value() + \", \" + yaml_type<U>::value() + \"]\";\n  }\n};\n\ntemplate <typename... T>\nstruct yaml_type<std::variant<T...>> {\n  static std::string value() {\n    bool first = true;\n    std::string result;\n    const auto add_type = [&first, &result](auto alternative) {\n      if (not first) {\n        result += \" or \";\n      }\n      first = false;\n      result += yaml_type<tmpl::type_from<decltype(alternative)>>::value();\n    };\n    EXPAND_PACK_LEFT_TO_RIGHT(add_type(tmpl::type_<T>{}));\n    return result;\n  }\n};\n\ntemplate <typename S, typename = std::void_t<>>\nstruct has_suggested : std::false_type {};\ntemplate <typename S>\nstruct has_suggested<\n    S, std::void_t<decltype(std::declval<S>().suggested_value())>>\n    : std::true_type {};\n\ntemplate <typename S, typename = std::void_t<>>\nstruct has_lower_bound : std::false_type {};\ntemplate <typename S>\nstruct has_lower_bound<S,\n                       std::void_t<decltype(std::declval<S>().lower_bound())>>\n    : std::true_type {};\n\ntemplate <typename S, typename = std::void_t<>>\nstruct has_upper_bound : std::false_type {};\ntemplate <typename S>\nstruct has_upper_bound<S,\n                       std::void_t<decltype(std::declval<S>().upper_bound())>>\n    : std::true_type {};\n\ntemplate <typename S, typename = std::void_t<>>\nstruct has_lower_bound_on_size : std::false_type {};\ntemplate <typename S>\nstruct has_lower_bound_on_size<\n    S, std::void_t<decltype(std::declval<S>().lower_bound_on_size())>>\n    : std::true_type {};\n\ntemplate <typename S, typename = std::void_t<>>\nstruct has_upper_bound_on_size : std::false_type {};\ntemplate <typename S>\nstruct has_upper_bound_on_size<\n    S, std::void_t<decltype(std::declval<S>().upper_bound_on_size())>>\n    : std::true_type {};\n\ntemplate <typename Tag>\nstd::string print_tag(const std::string& indent) {\n  const std::string new_line = \"\\n\" + indent + \"  \";\n  std::ostringstream ss;\n  ss << indent << pretty_type::name<Tag>() << \":\" << new_line\n     << \"type=\" << yaml_type<typename Tag::type>::value();\n  if constexpr (has_suggested<Tag>::value) {\n    if constexpr (tt::is_a_v<std::unique_ptr, typename Tag::type>) {\n      call_with_dynamic_type<\n          void, typename Tag::type::element_type::creatable_classes>(\n          Tag::suggested_value().get(), [&new_line, &ss](const auto* derived) {\n            ss << new_line << \"suggested=\" << std::boolalpha\n               << pretty_type::short_name<decltype(*derived)>();\n          });\n    } else {\n      ss << new_line << \"suggested=\"\n         << (MakeString{} << std::boolalpha << Tag::suggested_value());\n    }\n  }\n  if constexpr (has_lower_bound<Tag>::value) {\n    ss << new_line << \"min=\" << (MakeString{} << Tag::lower_bound());\n  }\n  if constexpr (has_upper_bound<Tag>::value) {\n    ss << new_line << \"max=\" << (MakeString{} << Tag::upper_bound());\n  }\n  if constexpr (has_lower_bound_on_size<Tag>::value) {\n    ss << new_line << \"min size=\" << Tag::lower_bound_on_size();\n  }\n  if constexpr (has_upper_bound_on_size<Tag>::value) {\n    ss << new_line << \"max size=\" << Tag::upper_bound_on_size();\n  }\n  ss << \"\\n\" << wrap_text(Tag::help, 77, indent + \"  \") << \"\\n\\n\";\n  return ss.str();\n}\n\ntemplate <typename OptionList, typename TagsAndSubgroups>\nstruct print;\n\ntemplate <typename Tag, typename OptionList>\nstruct print_impl {\n  static std::string apply(const std::string& indent) {\n    if constexpr (tmpl::list_contains_v<OptionList, Tag>) {\n      return print_tag<Tag>(indent);\n    } else {\n      // A group\n      std::ostringstream ss;\n      ss << indent << pretty_type::name<Tag>() << \":\\n\"\n         << wrap_text(Tag::help, 77, indent + \"  \") << \"\\n\\n\";\n      return ss.str();\n    }\n  }\n};\n\ntemplate <typename... Alternatives>\nstd::string print_alternatives(const std::string& header,\n                               const std::string& indent) {\n  return (\n      indent + header + \"\\n\" + ... +\n      (print<tmpl::list<Alternatives...>, Alternatives>::apply(indent + \"  \")));\n}\n\ntemplate <typename FirstAlternative, typename... OtherAlternatives,\n          typename OptionList>\nstruct print_impl<Alternatives<FirstAlternative, OtherAlternatives...>,\n                  OptionList> {\n  static std::string apply(const std::string& indent) {\n    return print_alternatives<FirstAlternative>(\"EITHER\", indent) +\n           print_alternatives<OtherAlternatives...>(\"OR\", indent);\n  }\n};\n\ntemplate <typename OptionList, typename... TagsAndSubgroups>\nstruct print<OptionList, tmpl::list<TagsAndSubgroups...>> {\n  static std::string apply(const std::string& indent) {\n    return (\"\" + ... +\n            (print_impl<TagsAndSubgroups, OptionList>::apply(indent)));\n  }\n};\n\ntemplate <typename T, typename Metavariables>\nstruct CreateWrapper {\n  using metavariables = Metavariables;\n  T data{};\n};\n#define CREATE_WRAPPER_FORWARD_OP(op)                          \\\n  template <typename T, typename Metavariables>                \\\n  bool operator op(const CreateWrapper<T, Metavariables>& a,   \\\n                   const CreateWrapper<T, Metavariables>& b) { \\\n    return a.data op b.data;                                   \\\n  }\nCREATE_WRAPPER_FORWARD_OP(==)\nCREATE_WRAPPER_FORWARD_OP(!=)\nCREATE_WRAPPER_FORWARD_OP(<)\nCREATE_WRAPPER_FORWARD_OP(>)\nCREATE_WRAPPER_FORWARD_OP(<=)\nCREATE_WRAPPER_FORWARD_OP(>=)\n#undef CREATE_WRAPPER_FORWARD_OP\n\ntemplate <typename T, typename = std::nullptr_t>\nstruct wrap_create_types_impl;\n\ntemplate <typename T, typename Metavariables>\nusing wrap_create_types =\n    typename wrap_create_types_impl<T>::template wrapped_type<Metavariables>;\n\ntemplate <typename T>\nauto unwrap_create_types(T wrapped) {\n  return wrap_create_types_impl<T>::unwrap(std::move(wrapped));\n}\n\ntemplate <typename T, typename>\nstruct wrap_create_types_impl {\n  template <typename Metavariables>\n  using wrapped_type = CreateWrapper<T, Metavariables>;\n};\n\ntemplate <typename T, typename Metavariables>\nstruct wrap_create_types_impl<CreateWrapper<T, Metavariables>> {\n  // Never actually used, but instantiated during unwrapping\n  template <typename /*Metavars*/>\n  using wrapped_type = void;\n\n  static T unwrap(CreateWrapper<T, Metavariables> wrapped) {\n    return std::move(wrapped.data);\n  }\n};\n\ntemplate <typename T>\nstruct wrap_create_types_impl<\n    T, Requires<std::is_fundamental_v<T> and not tt::is_integer_v<T>>> {\n  template <typename Metavariables>\n  using wrapped_type = T;\n\n  static T unwrap(T wrapped) { return wrapped; }\n};\n\n// Classes convertible by yaml-cpp\ntemplate <>\nstruct wrap_create_types_impl<std::string> {\n  template <typename Metavariables>\n  using wrapped_type = std::string;\n\n  static std::string unwrap(std::string wrapped) { return wrapped; }\n};\n\ntemplate <typename K, typename V>\nstruct wrap_create_types_impl<std::map<K, V>> {\n  template <typename Metavariables>\n  using wrapped_type = std::map<wrap_create_types<K, Metavariables>,\n                                wrap_create_types<V, Metavariables>>;\n\n  static auto unwrap(std::map<K, V> wrapped) {\n    using UnwrappedK = decltype(unwrap_create_types<K>(std::declval<K>()));\n    using UnwrappedV = decltype(unwrap_create_types<V>(std::declval<V>()));\n    std::map<UnwrappedK, UnwrappedV> result;\n    for (auto it = wrapped.begin(); it != wrapped.end();) {\n      auto node = wrapped.extract(it++);\n      result.emplace(unwrap_create_types<K>(std::move(node.key())),\n                     unwrap_create_types<V>(std::move(node.mapped())));\n    }\n    return result;\n  }\n};\n\ntemplate <typename T>\nstruct wrap_create_types_impl<std::vector<T>> {\n  template <typename Metavariables>\n  using wrapped_type = std::vector<wrap_create_types<T, Metavariables>>;\n\n  static auto unwrap(std::vector<T> wrapped) {\n    using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));\n    std::vector<UnwrappedT> result;\n    result.reserve(wrapped.size());\n    for (auto& w : wrapped) {\n      result.push_back(unwrap_create_types<T>(std::move(w)));\n    }\n    return result;\n  }\n};\n\ntemplate <typename T>\nstruct wrap_create_types_impl<std::list<T>> {\n  template <typename Metavariables>\n  using wrapped_type = std::list<wrap_create_types<T, Metavariables>>;\n\n  static auto unwrap(std::list<T> wrapped) {\n    using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));\n    std::list<UnwrappedT> result;\n    for (auto& w : wrapped) {\n      result.push_back(unwrap_create_types<T>(std::move(w)));\n    }\n    return result;\n  }\n};\n\ntemplate <typename T, size_t N>\nstruct wrap_create_types_impl<std::array<T, N>> {\n  template <typename Metavariables>\n  using wrapped_type = std::array<wrap_create_types<T, Metavariables>, N>;\n\n  static auto unwrap(std::array<T, N> wrapped) {\n    return unwrap_helper(std::move(wrapped), std::make_index_sequence<N>{});\n  }\n\n  template <size_t... Is>\n  static auto unwrap_helper(std::array<T, N> wrapped,\n                            std::integer_sequence<size_t, Is...> /*meta*/) {\n    using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));\n    static_cast<void>(wrapped);  // Work around broken GCC warning\n    return std::array<UnwrappedT, N>{\n        {unwrap_create_types<T>(std::move(wrapped[Is]))...}};\n  }\n};\n\ntemplate <typename T, typename U>\nstruct wrap_create_types_impl<std::pair<T, U>> {\n  template <typename Metavariables>\n  using wrapped_type = std::pair<wrap_create_types<T, Metavariables>,\n                                 wrap_create_types<U, Metavariables>>;\n\n  static auto unwrap(std::pair<T, U> wrapped) {\n    using UnwrappedT = decltype(unwrap_create_types<T>(std::declval<T>()));\n    using UnwrappedU = decltype(unwrap_create_types<U>(std::declval<U>()));\n    return std::pair<UnwrappedT, UnwrappedU>(\n        unwrap_create_types<T>(std::move(wrapped.first)),\n        unwrap_create_types<U>(std::move(wrapped.second)));\n  }\n};\n}  // namespace Options::Options_detail\n"
  },
  {
    "path": "src/Options/ParseError.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <exception>\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include \"Options/Context.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Options {\n/// \\cond\nnamespace detail {\nclass propagate_context : public std::exception {\n public:\n  // cppcheck-suppress passedByValue\n  explicit propagate_context(std::string message)\n      : message_(std::move(message)) {}\n\n  const char* what() const noexcept(true) override { return message_.c_str(); }\n  const std::string& message() const { return message_; }\n\n private:\n  std::string message_;\n};\n}  // namespace detail\n/// \\endcond\n\n/// \\ingroup OptionParsingGroup\n/// Like ERROR(\"\\n\" << (context) << m), but instead throws an\n/// exception that will be caught in a higher level Options if not\n/// passed a top-level context.  This is used to print a parsing\n/// \"backtrace\" since we can't pass any extra data through the\n/// yaml-cpp code.\n///\n/// \\param context Context used to print a parsing traceback\n/// \\param m error message, as for ERROR\n#define PARSE_ERROR(context, m)                                         \\\n  do {                                                                  \\\n    if ((context).top_level) {                                          \\\n      /* clang-tidy: macro arg in parentheses */                        \\\n      ERROR_NO_TRACE(\"\\n\" << (context) << m); /* NOLINT */              \\\n    } else {                                                            \\\n      std::ostringstream avoid_name_collisions_PARSE_ERROR;             \\\n      /* clang-tidy: macro arg in parentheses */                        \\\n      avoid_name_collisions_PARSE_ERROR << (context) << m; /* NOLINT */ \\\n      throw ::Options::detail::propagate_context(                       \\\n          avoid_name_collisions_PARSE_ERROR.str());                     \\\n    }                                                                   \\\n  } while (false)\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/ParseOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Options/ParseOptions.hpp\"\n\n#include <cstddef>\n#include <exception>\n#include <limits>\n#include <ostream>\n#include <string>\n#include <unordered_set>\n#include <vector>\n#include <yaml-cpp/yaml.h>\n\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace Options {\nnamespace detail {\nnamespace {\nvoid check_metadata(const YAML::Node& metadata) {\n  // Validate executable name\n  if (const auto& exec_name = metadata[\"Executable\"]) {\n    if (file_system::get_file_name(exec_name.as<std::string>()) !=\n        executable_name()) {\n      throw std::runtime_error(\"Input file metadata lists executable '\" +\n                               exec_name.as<std::string>() +\n                               \"', but the running executable is '\" +\n                               executable_name() + \"'.\");\n    }\n  }\n  // Validate version\n  if (const auto& version = metadata[\"Version\"]) {\n    if (version.as<std::string>() != spectre_version()) {\n      throw std::runtime_error(\n          \"Input file metadata lists version \" + version.as<std::string>() +\n          \", but running version \" + spectre_version() + \".\");\n    }\n  }\n}\n}  // namespace\n\nYAML::Node load_and_check_yaml(const std::string& options,\n                               const bool require_metadata) {\n  std::vector<YAML::Node> yaml_docs = YAML::LoadAll(options);\n  if (yaml_docs.empty()) {\n    // yaml-cpp v 0.9.0 returns immediately on an empty root, so need to\n    // handle this\n    if (require_metadata) {\n      const size_t pos = options.find(\"---\\n---\\n\");\n      if (pos == std::string::npos) {\n        throw std::runtime_error(\n            \"Missing metadata in input file. YAML input files begin with a \"\n            \"metadata section terminated by '---':\\n\\n\"\n            \"# Metadata here\\n\\n\"\n            \"---\\n\\n\"\n            \"# Options start here\\n\\n\"\n            \"The metadata section may also be empty:\\n\\n\"\n            \"---\\n\"\n            \"---\\n\\n\"\n            \"# Options start here\\n\\n\"\n            \"See option parsing documentation for details.\");\n      } else {\n        std::string options_after_empty_metadata = options;\n        options_after_empty_metadata.erase(pos, 8);\n        std::vector<YAML::Node> yaml_docs_after_metadata =\n            YAML::LoadAll(options_after_empty_metadata);\n        return yaml_docs_after_metadata[0];\n      }\n    }\n    return {};\n  } else if (yaml_docs.size() == 1) {\n    if (require_metadata) {\n      throw std::runtime_error(\n          \"Missing metadata in input file. YAML input files begin with a \"\n          \"metadata section terminated by '---':\\n\\n\"\n          \"# Metadata here\\n\\n\"\n          \"---\\n\\n\"\n          \"# Options start here\\n\\n\"\n          \"The metadata section may also be empty:\\n\\n\"\n          \"---\\n\"\n          \"---\\n\\n\"\n          \"# Options start here\\n\\n\"\n          \"See option parsing documentation for details.\");\n    }\n    return yaml_docs[0];\n  } else if (yaml_docs.size() == 2) {\n    check_metadata(yaml_docs[0]);\n    return yaml_docs[1];\n  } else {\n    throw std::runtime_error(\"Expected either one or two YAML documents, not \" +\n                             std::to_string(yaml_docs.size()) + \".\");\n  }\n}\n}  // namespace detail\n\nnamespace parse_detail {\nstd::unordered_set<std::string> get_given_options(\n    const Options::Context& context, const YAML::Node& node,\n    const std::string& help) {\n  if (not(node.IsMap() or node.IsNull())) {\n    PARSE_ERROR(context, \"'\" << node << \"' does not look like options.\\n\"\n                             << help);\n  }\n\n  std::unordered_set<std::string> given_options{};\n  for (const auto& name_and_value : node) {\n    given_options.insert(name_and_value.first.as<std::string>());\n  }\n  return given_options;\n}\n\nvoid check_for_unique_choice(const std::vector<size_t>& alternative_choices,\n                             const Options::Context& context,\n                             const std::string& parsing_help) {\n  if (alg::any_of(alternative_choices, [](const size_t x) {\n        return x == std::numeric_limits<size_t>::max();\n      })) {\n    PARSE_ERROR(context, \"Cannot decide between alternative options.\\n\"\n                             << parsing_help);\n  }\n}\n\nvoid add_name_to_valid_option_names(\n    const gsl::not_null<std::vector<std::string>*> valid_option_names,\n    const std::string& label) {\n  ASSERT(alg::find(*valid_option_names, label) == valid_option_names->end(),\n         \"Duplicate option name: \" << label);\n  valid_option_names->push_back(label);\n}\n\n[[noreturn]] void option_specified_twice_error(\n    const Options::Context& context, const std::string& name,\n    const std::string& parsing_help) {\n  PARSE_ERROR(context, \"Option '\" << name << \"' specified twice.\\n\"\n                                  << parsing_help);\n}\n\n[[noreturn]] void unused_key_error(const Context& context,\n                                   const std::string& name,\n                                   const std::string& parsing_help) {\n  PARSE_ERROR(context, \"Option '\"\n                           << name\n                           << \"' is unused because of other provided options.\\n\"\n                           << parsing_help);\n}\n\n[[noreturn]] void option_invalid_error(const Options::Context& context,\n                                       const std::string& name,\n                                       const std::string& parsing_help) {\n  PARSE_ERROR(context, \"Option '\" << name << \"' is not a valid option.\\n\"\n                                  << parsing_help);\n}\n\nvoid check_for_missing_option(const std::vector<std::string>& valid_names,\n                              const Options::Context& context,\n                              const std::string& parsing_help) {\n  if (not valid_names.empty()) {\n    PARSE_ERROR(context, \"You did not specify the option\"\n                             << (valid_names.size() == 1 ? \" \" : \"s \")\n                             << (MakeString{} << valid_names) << \"\\n\"\n                             << parsing_help);\n  }\n}\n\nstd::string add_group_prefix_to_name(const std::string& name) {\n  return \"In group \" + name;\n}\n\nvoid print_top_level_error_message() {\n  Parallel::printf_error(\n      \"The following options differ from their suggested values:\\n\");\n}\n}  // namespace parse_detail\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/ParseOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines classes and functions that handle parsing of input parameters.\n\n#pragma once\n\n#include <cerrno>\n#include <cstring>\n#include <exception>\n#include <fstream>\n#include <ios>\n#include <iterator>\n#include <limits>\n#include <map>\n#include <ostream>\n#include <pup.h>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <variant>\n#include <vector>\n#include <yaml-cpp/yaml.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/OptionsDetails.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/Tags.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsInteger.hpp\"\n#include \"Utilities/TypeTraits/IsMaplike.hpp\"\n#include \"Utilities/TypeTraits/IsStdArray.hpp\"\n#include \"Utilities/TypeTraits/IsStdArrayOfSize.hpp\"\n\nnamespace Options {\n// Defining methods as inline in a different header from the class\n// definition is somewhat strange.  It is done here to minimize the\n// amount of code in the frequently-included Options.hpp file.  The\n// only external consumers of Option should be create_from_yaml\n// specializations, and they should only be instantiated by code in\n// this file.  (Or explicitly instantiated in cpp files, which can\n// include this file.)\n\n// clang-tidy: YAML::Node not movable (as of yaml-cpp-0.5.3)\n// NOLINTNEXTLINE(performance-unnecessary-value-param)\ninline Option::Option(YAML::Node node, Context context)\n    : node_(std::make_unique<YAML::Node>(std::move(node))),\n      context_(std::move(context)) {  // NOLINT\n  context_.line = node.Mark().line;\n  context_.column = node.Mark().column;\n}\n\ninline Option::Option(Context context)\n    : node_(std::make_unique<YAML::Node>()), context_(std::move(context)) {}\n\ninline const YAML::Node& Option::node() const { return *node_; }\ninline const Context& Option::context() const { return context_; }\n\n/// Append a line to the contained context.\ninline void Option::append_context(const std::string& context) {\n  context_.append(context);\n}\n\n// NOLINTNEXTLINE(performance-unnecessary-value-param)\ninline void Option::set_node(YAML::Node node) {\n  // clang-tidy: YAML::Node not movable (as of yaml-cpp-0.5.3)\n  *node_ = std::move(node);  // NOLINT\n  context_.line = node_->Mark().line;\n  context_.column = node_->Mark().column;\n}\n\ntemplate <typename T, typename Metavariables>\nT Option::parse_as() const {\n  try {\n    // yaml-cpp's `as` method won't parse empty nodes, so we need to\n    // inline a bit of its logic.\n    Options_detail::wrap_create_types<T, Metavariables> result{};\n    if (YAML::convert<decltype(result)>::decode(node(), result)) {\n      return Options_detail::unwrap_create_types(std::move(result));\n    }\n    // clang-tidy: thrown exception is not nothrow copy constructible\n    throw YAML::BadConversion(node().Mark());  // NOLINT\n  } catch (const YAML::BadConversion& e) {\n    // This happens when trying to parse an empty value as a container\n    // with no entries.\n    if ((tt::is_a_v<std::vector, T> or tt::is_std_array_of_size_v<0, T> or\n         tt::is_maplike_v<T>) and node().IsNull()) {\n      return T{};\n    }\n    Context error_context = context();\n    error_context.line = e.mark.line;\n    error_context.column = e.mark.column;\n    std::ostringstream ss;\n    ss << \"Failed to convert value to type \"\n       << Options_detail::yaml_type<T>::value() << \":\";\n\n    const std::string value_text = YAML::Dump(node());\n    if (value_text.find('\\n') == std::string::npos) {\n      ss << \" \" << value_text;\n    } else {\n      // Indent each line of the value by two spaces and start on a new line\n      ss << \"\\n  \";\n      for (char c : value_text) {\n        ss << c;\n        if (c == '\\n') {\n          ss << \"  \";\n        }\n      }\n    }\n\n    if (tt::is_a_v<std::vector, T> or tt::is_std_array_v<T>) {\n      ss << \"\\n\\nNote: For sequences this can happen because the length of the \"\n            \"sequence specified\\nin the input file is not equal to the length \"\n            \"expected by the code. Sequences in\\nfiles can be denoted either \"\n            \"as a bracket enclosed list ([foo, bar]) or with each\\nentry on a \"\n            \"separate line, indented and preceeded by a dash (  - foo).\";\n    }\n    PARSE_ERROR(error_context, ss.str());\n  } catch (const Options::detail::propagate_context& e) {\n    Context error_context = context();\n    // Avoid line numbers in the middle of the trace\n    error_context.line = -1;\n    error_context.column = -1;\n    PARSE_ERROR(error_context, e.message());\n  } catch (std::exception& e) {\n    ERROR(\"Unexpected exception: \" << e.what());\n  }\n}\n\nnamespace Options_detail {\ntemplate <typename T, typename Metavariables, typename Subgroup>\nstruct get_impl;\n}  // namespace Options_detail\n\n/// \\ingroup OptionParsingGroup\n/// \\brief Class that handles parsing an input file\n///\n/// Options must be given YAML data to parse before output can be\n/// extracted.  This can be done either from a file (parse_file\n/// method), from a string (parse method), or, in the case of\n/// recursive parsing, from an Option (parse method).  The options\n/// can then be extracted using the get method.\n///\n/// \\example\n/// \\snippet Test_Options.cpp options_example_scalar_struct\n/// \\snippet Test_Options.cpp options_example_scalar_parse\n///\n/// \\see the \\ref dev_guide_option_parsing tutorial\n///\n/// \\tparam OptionList the list of option structs to parse\n/// \\tparam Group the option group with a group hierarchy\ntemplate <typename OptionList, typename Group = NoSuchType>\nclass Parser {\n private:\n  /// All top-level options and top-level groups of options. Every option in\n  /// `OptionList` is either in this list or in the hierarchy of one of the\n  /// groups in this list.\n  using tags_and_subgroups_list = tmpl::remove_duplicates<tmpl::transform<\n      OptionList, Options_detail::find_subgroup<tmpl::_1, tmpl::pin<Group>>>>;\n\n public:\n  Parser() = default;\n\n  /// \\param help_text an overall description of the options\n  explicit Parser(std::string help_text);\n\n  /// Parse a string to obtain options and their values.\n  ///\n  /// \\param options the string holding the YAML formatted options\n  void parse(std::string options);\n\n  /// Parse an Option to obtain options and their values.\n  void parse(const Option& options);\n\n  /// Parse a file containing options\n  ///\n  /// \\param file_name the path to the file to parse\n  /// \\param require_metadata require the input file to have a metadata section\n  void parse_file(const std::string& file_name, bool require_metadata = true);\n\n  /// Overlay the options from a string or file on top of the\n  /// currently parsed options.\n  ///\n  /// Any tag included in the list passed as the template parameter\n  /// can be overridden by a new parsed value.  Newly parsed options\n  /// replace the previous values.  Any tags not appearing in the new\n  /// input are left unchanged.\n  /// @{\n  template <typename OverlayOptions>\n  void overlay(std::string options);\n\n  template <typename OverlayOptions>\n  void overlay_file(const std::string& file_name);\n  /// @}\n\n  /// Get the value of the specified option\n  ///\n  /// \\tparam T the option to retrieve\n  /// \\return the value of the option\n  template <typename T, typename Metavariables = NoSuchType>\n  typename T::type get() const;\n\n  /// Call a function with the specified options as arguments.\n  ///\n  /// \\tparam TagList a typelist of options to pass\n  /// \\return the result of the function call\n  template <typename TagList, typename Metavariables = NoSuchType, typename F>\n  decltype(auto) apply(F&& func) const;\n\n  /// Call a function with the typelist of parsed options (i.e., the\n  /// supplied option list with the chosen branches of any\n  /// Alternatives inlined) and the option values as arguments.\n  ///\n  /// \\return the result of the function call.  This must have the\n  /// same type for all valid sets of parsed arguments.\n  template <typename Metavariables = NoSuchType, typename F>\n  decltype(auto) apply_all(F&& func) const;\n\n  /// Get the help string\n  template <typename TagsAndSubgroups = tags_and_subgroups_list>\n  std::string help() const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <typename, typename>\n  friend class Parser;\n  template <typename, typename, typename>\n  friend struct Options_detail::get_impl;\n\n  static_assert(tt::is_a<tmpl::list, OptionList>::value,\n                \"The OptionList template parameter to Options must be a \"\n                \"tmpl::list<...>.\");\n\n  // All options that could be specified, including those that have\n  // alternatives and are therefore not required.\n  using all_possible_options = tmpl::remove_duplicates<\n      typename Options_detail::flatten_alternatives<OptionList>::type>;\n\n  static_assert(\n      std::is_same_v<\n          typename Options_detail::flatten_alternatives<OptionList>::type,\n          OptionList> or\n          tmpl::all<\n              all_possible_options,\n              std::is_same<tmpl::_1, Options_detail::find_subgroup<\n                                         tmpl::_1, tmpl::pin<Group>>>>::value,\n      \"Option parser cannot handle Alternatives and options with groups \"\n      \"simultaneously.\");\n\n  /// All top-level subgroups\n  using subgroups = tmpl::list_difference<tags_and_subgroups_list, OptionList>;\n\n  // The maximum length of an option label.\n  static constexpr int max_label_size_ = 70;\n\n  /// Parse a YAML node containing options\n  void parse(const YAML::Node& node);\n\n  /// Overlay data from a YAML node\n  template <typename OverlayOptions>\n  void overlay(const YAML::Node& node);\n\n  /// Check that the size is not smaller than the lower bound\n  ///\n  /// \\tparam T the option struct\n  /// \\param t the value of the read in option\n  template <typename T>\n  void check_lower_bound_on_size(const typename T::type& t,\n                                 const Context& context) const;\n\n  /// Check that the size is not larger than the upper bound\n  ///\n  /// \\tparam T the option struct\n  /// \\param t the value of the read in option\n  template <typename T>\n  void check_upper_bound_on_size(const typename T::type& t,\n                                 const Context& context) const;\n\n  /// If the options has a lower bound, check it is satisfied.\n  ///\n  /// Note: Lower bounds are >=, not just >.\n  /// \\tparam T the option struct\n  /// \\param t the value of the read in option\n  template <typename T>\n  void check_lower_bound(const typename T::type& t,\n                         const Context& context) const;\n\n  /// If the options has a upper bound, check it is satisfied.\n  ///\n  /// Note: Upper bounds are <=, not just <.\n  /// \\tparam T the option struct\n  /// \\param t the value of the read in option\n  template <typename T>\n  void check_upper_bound(const typename T::type& t,\n                         const Context& context) const;\n\n  /// Get the help string for parsing errors\n  template <typename TagsAndSubgroups = tags_and_subgroups_list>\n  std::string parsing_help(const YAML::Node& options) const;\n\n  /// Error message when failed to parse an input file.\n  [[noreturn]] void parser_error(const YAML::Exception& e) const;\n\n  template <typename ChosenOptions, typename RemainingOptions, typename F>\n  auto call_with_chosen_alternatives_impl(F&& func,\n                                          std::vector<size_t> choices) const;\n\n  template <typename F>\n  auto call_with_chosen_alternatives(F&& func) const {\n    return call_with_chosen_alternatives_impl<tmpl::list<>, OptionList>(\n        std::forward<F>(func), alternative_choices_);\n  }\n\n  std::string help_text_{};\n  Context context_{};\n  std::vector<std::string> input_source_{};\n  std::unordered_map<std::string, YAML::Node> parsed_options_{};\n\n  template <typename Subgroup>\n  struct SubgroupParser {\n    using type = Parser<Options_detail::options_in_group<OptionList, Subgroup>,\n                        Subgroup>;\n  };\n\n  tuples::tagged_tuple_from_typelist<\n      tmpl::transform<subgroups, tmpl::bind<SubgroupParser, tmpl::_1>>>\n      subgroup_parsers_ =\n          tmpl::as_pack<subgroups>([this](auto... subgroup_tags) {\n            (void)this;  // gcc wants this for subgroup_parsers_\n            return decltype(subgroup_parsers_)(\n                tmpl::type_from<decltype(subgroup_tags)>::help...);\n          });\n\n  // The choices made for option alternatives in a depth-first order.\n  // Starting from the front of the option list, when reaching the\n  // first Alternatives object, replace it with the options in the nth\n  // choice, where n is the *last* element of this vector.  Continue\n  // processing from the start of those options, using the second to\n  // last value here for the next choice, and so on.\n  std::vector<size_t> alternative_choices_{};\n};\n\ntemplate <typename OptionList, typename Group>\nParser<OptionList, Group>::Parser(std::string help_text)\n    : help_text_(std::move(help_text)) {\n  tmpl::for_each<all_possible_options>([](auto t) {\n    using T = typename decltype(t)::type;\n    const std::string label = pretty_type::name<T>();\n    ASSERT(label.size() <= max_label_size_,\n           \"The option name \" << label\n                              << \" is too long for nice formatting, \"\n                                 \"please shorten the name to \"\n                              << max_label_size_ << \" characters or fewer\");\n    ASSERT(std::strlen(T::help) > 0,\n           \"You must supply a help string of non-zero length for \" << label);\n  });\n}\n\nnamespace detail {\nYAML::Node load_and_check_yaml(const std::string& options,\n                               bool require_metadata);\n}\n\ntemplate <typename OptionList, typename Group>\nvoid Parser<OptionList, Group>::parse(std::string options) {\n  context_.append(\"In string\");\n  input_source_.push_back(std::move(options));\n  try {\n    parse(detail::load_and_check_yaml(input_source_.back(), false));\n  } catch (const YAML::Exception& e) {\n    parser_error(e);\n  }\n}\n\ntemplate <typename OptionList, typename Group>\nvoid Parser<OptionList, Group>::parse(const Option& options) {\n  context_ = options.context();\n  parse(options.node());\n}\n\nnamespace Options_detail {\ninline std::ifstream open_file(const std::string& file_name) {\n  errno = 0;\n  std::ifstream input(file_name);\n  if (not input) {\n    // There is no standard way to get an error message from an\n    // fstream, but this works on many implementations.\n    ERROR(\"Could not open \" << file_name << \": \" << strerror(errno));\n  }\n  return input;\n}\n}  // namespace Options_detail\n\ntemplate <typename OptionList, typename Group>\nvoid Parser<OptionList, Group>::parse_file(const std::string& file_name,\n                                           const bool require_metadata) {\n  context_.append(\"In \" + file_name);\n  auto input = Options_detail::open_file(file_name);\n  input_source_.push_back(std::string(std::istreambuf_iterator(input), {}));\n  try {\n    parse(detail::load_and_check_yaml(input_source_.back(), require_metadata));\n  } catch (const YAML::Exception& e) {\n    parser_error(e);\n  }\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename OverlayOptions>\nvoid Parser<OptionList, Group>::overlay(std::string options) {\n  context_ = Context{};\n  context_.append(\"In string\");\n  input_source_.push_back(std::move(options));\n  try {\n    overlay<OverlayOptions>(\n        detail::load_and_check_yaml(input_source_.back(), false));\n  } catch (const YAML::Exception& e) {\n    parser_error(e);\n  }\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename OverlayOptions>\nvoid Parser<OptionList, Group>::overlay_file(const std::string& file_name) {\n  context_ = Context{};\n  context_.append(\"In \" + file_name);\n  auto input = Options_detail::open_file(file_name);\n  input_source_.push_back(std::string(std::istreambuf_iterator(input), {}));\n  try {\n    overlay<OverlayOptions>(\n        detail::load_and_check_yaml(input_source_.back(), false));\n  } catch (const YAML::Exception& e) {\n    parser_error(e);\n  }\n}\n\nnamespace Options_detail {\n// Attempts to match the given_options against OptionList, choosing\n// between any alternatives to maximize the number of matches.\n// Returns the highest number of matches and the choices required to\n// obtain that number.  (See the description of\n// Parser::alternative_choices_ for the format of the choices.)\n//\n// In the case of multiple equally good matches, the choice between\n// them will be given as std::numeric_limits<size_t>::max().  This may\n// cause a failure later or may be ignored if a better choice is\n// found.\n//\n// This does not handle groups correctly, but we disallow alternatives\n// when groups are present so there is only one possible choice that\n// this function could make.\ntemplate <typename OptionList>\nstd::pair<int, std::vector<size_t>> choose_alternatives(\n    const std::unordered_set<std::string>& given_options) {\n  int num_matched = 0;\n  std::vector<size_t> alternative_choices{};\n  tmpl::for_each<OptionList>([&alternative_choices, &num_matched,\n                              &given_options](auto opt) {\n    using Opt = tmpl::type_from<decltype(opt)>;\n    if constexpr (not tt::is_a_v<Options::Alternatives, Opt>) {\n      if (given_options.count(pretty_type::name<Opt>()) == 1) {\n        ++num_matched;\n      }\n    } else {\n      int most_matches = 0;\n      std::vector<size_t> best_alternatives{std::numeric_limits<size_t>::max()};\n\n      size_t alternative_number = 0;\n      tmpl::for_each<Opt>([&alternative_number, &best_alternatives,\n                           &most_matches, &given_options](auto alternative) {\n        using Alternative = tmpl::type_from<decltype(alternative)>;\n        auto alternative_match =\n            choose_alternatives<Alternative>(given_options);\n        if (alternative_match.first > most_matches) {\n          most_matches = alternative_match.first;\n          alternative_match.second.push_back(alternative_number);\n          best_alternatives = std::move(alternative_match.second);\n        } else if (alternative_match.first == most_matches) {\n          // Two equally good matches\n          best_alternatives.clear();\n          best_alternatives.push_back(std::numeric_limits<size_t>::max());\n        }\n        ++alternative_number;\n      });\n      num_matched += most_matches;\n      alternative_choices.insert(alternative_choices.begin(),\n                                 best_alternatives.begin(),\n                                 best_alternatives.end());\n    }\n  });\n  return {num_matched, std::move(alternative_choices)};\n}\n}  // namespace Options_detail\n\nnamespace Options_detail {\ntemplate <typename Tag, typename Metavariables, typename Subgroup>\nstruct get_impl {\n  template <typename OptionList, typename Group>\n  static typename Tag::type apply(const Parser<OptionList, Group>& opts) {\n    static_assert(\n        tmpl::list_contains_v<OptionList, Tag>,\n        \"Could not find requested option in the list of options provided. Did \"\n        \"you forget to add the option tag to the OptionList?\");\n    return tuples::get<typename Parser<\n        OptionList, Group>::template SubgroupParser<Subgroup>>(\n               opts.subgroup_parsers_)\n        .template get<Tag, Metavariables>();\n  }\n};\n\ntemplate <typename Tag, typename Metavariables>\nstruct get_impl<Tag, Metavariables, Tag> {\n  template <typename OptionList, typename Group>\n  static typename Tag::type apply(const Parser<OptionList, Group>& opts) {\n    static_assert(\n        tmpl::list_contains_v<\n            typename Parser<OptionList, Group>::all_possible_options, Tag>,\n        \"Could not find requested option in the list of options provided. Did \"\n        \"you forget to add the option tag to the OptionList?\");\n    const std::string label = pretty_type::name<Tag>();\n\n    const auto supplied_option = opts.parsed_options_.find(label);\n    ASSERT(supplied_option != opts.parsed_options_.end(),\n           \"Requested option from alternative that was not supplied.\");\n    Option option(supplied_option->second, opts.context_);\n    option.append_context(\"While parsing option \" + label);\n\n    auto t = option.parse_as<typename Tag::type, Metavariables>();\n\n    if constexpr (Options_detail::has_suggested<Tag>::value) {\n      static_assert(\n          std::is_same_v<decltype(Tag::suggested_value()), typename Tag::type>,\n          \"Suggested value is not of the same type as the option.\");\n\n      // This can be easily relaxed, but using it would require\n      // writing comparison operators for abstract base classes.  If\n      // someone wants this enough to go though the effort of doing\n      // that, it would just require comparing the dereferenced\n      // pointers below to decide whether the suggestion was followed.\n      static_assert(not tt::is_a_v<std::unique_ptr, typename Tag::type>,\n                    \"Suggestions are not supported for pointer types.\");\n\n      const auto suggested_value = Tag::suggested_value();\n      {\n        Context context;\n        context.append(\"Checking SUGGESTED value for \" +\n                       pretty_type::name<Tag>());\n        opts.template check_lower_bound_on_size<Tag>(suggested_value, context);\n        opts.template check_upper_bound_on_size<Tag>(suggested_value, context);\n        opts.template check_lower_bound<Tag>(suggested_value, context);\n        opts.template check_upper_bound<Tag>(suggested_value, context);\n      }\n\n      if (t != suggested_value) {\n        Parallel::printf_error(\n            \"%s, line %d:\\n  Specified: %s\\n  Suggested: %s\\n\",\n            label, option.context().line + 1,\n            (MakeString{} << std::boolalpha << t),\n            (MakeString{} << std::boolalpha << suggested_value));\n      }\n    }\n\n    opts.template check_lower_bound_on_size<Tag>(t, option.context());\n    opts.template check_upper_bound_on_size<Tag>(t, option.context());\n    opts.template check_lower_bound<Tag>(t, option.context());\n    opts.template check_upper_bound<Tag>(t, option.context());\n    return t;\n  }\n};\n\ntemplate <typename Metavariables>\nstruct get_impl<Tags::InputSource, Metavariables, Tags::InputSource> {\n  template <typename OptionList, typename Group>\n  static Tags::InputSource::type apply(const Parser<OptionList, Group>& opts) {\n    return opts.input_source_;\n  }\n};\n}  // namespace Options_detail\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename Tag, typename Metavariables>\ntypename Tag::type Parser<OptionList, Group>::get() const {\n  return Options_detail::get_impl<\n      Tag, Metavariables,\n      typename Options_detail::find_subgroup<Tag, Group>::type>::apply(*this);\n}\n\nnamespace Options_detail {\ntemplate <typename>\nstruct apply_helper;\n\ntemplate <typename... Tags>\nstruct apply_helper<tmpl::list<Tags...>> {\n  template <typename Metavariables, typename Options, typename F>\n  static decltype(auto) apply(const Options& opts, F&& func) {\n    return func(opts.template get<Tags, Metavariables>()...);\n  }\n};\n}  // namespace Options_detail\n\n/// \\cond\n// Doxygen is confused by decltype(auto)\ntemplate <typename OptionList, typename Group>\ntemplate <typename TagList, typename Metavariables, typename F>\ndecltype(auto) Parser<OptionList, Group>::apply(F&& func) const {\n  return Options_detail::apply_helper<TagList>::template apply<Metavariables>(\n      *this, std::forward<F>(func));\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename Metavariables, typename F>\ndecltype(auto) Parser<OptionList, Group>::apply_all(F&& func) const {\n  return call_with_chosen_alternatives([this, &func](\n                                           auto chosen_alternatives /*meta*/) {\n    using ChosenAlternatives = decltype(chosen_alternatives);\n    return this->apply<ChosenAlternatives, Metavariables>([&func](\n                                                              auto&&... args) {\n      return std::forward<F>(func)(ChosenAlternatives{}, std::move(args)...);\n    });\n  });\n}\n/// \\endcond\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename TagsAndSubgroups>\nstd::string Parser<OptionList, Group>::help() const {\n  std::ostringstream ss;\n  ss << \"\\n==== Description of expected options:\\n\" << help_text_;\n  if (tmpl::size<TagsAndSubgroups>::value > 0) {\n    ss << \"\\n\\nOptions:\\n\"\n       << Options_detail::print<OptionList, TagsAndSubgroups>::apply(\"  \");\n  } else {\n    ss << \"\\n\\n<No options>\\n\";\n  }\n  return ss.str();\n}\n\ntemplate <typename OptionList, typename Group>\nvoid Parser<OptionList, Group>::pup(PUP::er& p) {\n  static_assert(std::is_same_v<Group, NoSuchType>,\n                \"Inner parsers should be recreated by the root parser, not \"\n                \"serialized.\");\n  // We reconstruct most of the state when deserializing, rather than\n  // trying to package it.\n  p | help_text_;\n  p | input_source_;\n  if (p.isUnpacking() and not input_source_.empty()) {\n    // input_source_ is populated by the `parse` and `overlay` calls\n    // below, so we have to clear out the old values before calling\n    // them.\n    auto received_source = std::move(input_source_);\n    input_source_.clear();\n    parse(std::move(received_source[0]));\n    for (size_t i = 1; i < received_source.size(); ++i) {\n      overlay<OptionList>(std::move(received_source[i]));\n    }\n  }\n}\n\n// implementation of Options::Parser::parse() factored out to save on compile\n// time and compile memory\nnamespace parse_detail {\nstd::unordered_set<std::string> get_given_options(\n    const Options::Context& context, const YAML::Node& node,\n    const std::string& help);\n\nvoid check_for_unique_choice(const std::vector<size_t>& alternative_choices,\n                             const Options::Context& context,\n                             const std::string& parsing_help);\n\nvoid add_name_to_valid_option_names(\n    gsl::not_null<std::vector<std::string>*> valid_option_names,\n    const std::string& label);\n\ntemplate <typename TopLevelOptionsAndGroups>\nstruct get_valid_option_names;\n\ntemplate <typename... TopLevelOptionsAndGroups>\nstruct get_valid_option_names<tmpl::list<TopLevelOptionsAndGroups...>> {\n  static std::vector<std::string> apply() {\n    // Use an ordered container so the missing options are reported in\n    // the order they are given in the help string.\n    std::vector<std::string> valid_option_names;\n    valid_option_names.reserve(\n        tmpl::size<tmpl::list<TopLevelOptionsAndGroups...>>{});\n    (add_name_to_valid_option_names(\n         make_not_null(&valid_option_names),\n         pretty_type::name<TopLevelOptionsAndGroups>()),\n     ...);\n    return valid_option_names;\n  }\n};\n\n[[noreturn]] void option_specified_twice_error(const Options::Context& context,\n                                               const std::string& name,\n                                               const std::string& parsing_help);\n\n[[noreturn]] void unused_key_error(const Context& context,\n                                   const std::string& name,\n                                   const std::string& parsing_help);\n\ntemplate <typename Tag>\nvoid check_for_unused_key(const Context& context, const std::string& name,\n                          const std::string& parsing_help) {\n  if (name == pretty_type::name<Tag>()) {\n    unused_key_error(context, name, parsing_help);\n  }\n}\n\ntemplate <typename AllPossibleOptions>\nstruct check_for_unused_key_helper;\n\ntemplate <typename... AllPossibleOptions>\nstruct check_for_unused_key_helper<tmpl::list<AllPossibleOptions...>> {\n  static void apply(const Context& context, const std::string& name,\n                    const std::string& parsing_help) {\n    (check_for_unused_key<AllPossibleOptions>(context, name, parsing_help),\n     ...);\n  }\n};\n\n[[noreturn]] void option_invalid_error(const Options::Context& context,\n                                       const std::string& name,\n                                       const std::string& parsing_help);\n\nvoid check_for_missing_option(const std::vector<std::string>& valid_names,\n                              const Options::Context& context,\n                              const std::string& parsing_help);\n\nstd::string add_group_prefix_to_name(const std::string& name);\n\nvoid print_top_level_error_message();\n}  // namespace parse_detail\n\ntemplate <typename OptionList, typename Group>\nvoid Parser<OptionList, Group>::parse(const YAML::Node& node) {\n  std::unordered_set<std::string> given_options =\n      parse_detail::get_given_options(context_, node, help());\n\n  alternative_choices_ =\n      Options_detail::choose_alternatives<OptionList>(given_options).second;\n  const std::string parsing_help_message = parsing_help(node);\n  parse_detail::check_for_unique_choice(alternative_choices_, context_,\n                                        parsing_help_message);\n\n  auto valid_names = call_with_chosen_alternatives([](auto option_list_v) {\n    using option_list = decltype(option_list_v);\n    using top_level_options_and_groups =\n        tmpl::remove_duplicates<tmpl::transform<\n            option_list,\n            Options_detail::find_subgroup<tmpl::_1, tmpl::pin<Group>>>>;\n    return parse_detail::get_valid_option_names<\n        top_level_options_and_groups>::apply();\n  });\n\n  for (const auto& name_and_value : node) {\n    const auto& name = name_and_value.first.as<std::string>();\n    const auto& value = name_and_value.second;\n    auto context = context_;\n    context.line = name_and_value.first.Mark().line;\n    context.column = name_and_value.first.Mark().column;\n\n    // Check for duplicate key\n    if (0 != parsed_options_.count(name)) {\n      parse_detail::option_specified_twice_error(context, name,\n                                                 parsing_help_message);\n    }\n\n    // Check for invalid key\n    const auto name_it = alg::find(valid_names, name);\n    if (name_it == valid_names.end()) {\n      parse_detail::check_for_unused_key_helper<all_possible_options>::apply(\n          context, name, parsing_help_message);\n      parse_detail::option_invalid_error(context, name, parsing_help_message);\n    }\n\n    parsed_options_.emplace(name, value);\n    valid_names.erase(name_it);\n  }\n\n  parse_detail::check_for_missing_option(valid_names, context_,\n                                         parsing_help_message);\n\n  tmpl::for_each<subgroups>([this](auto subgroup_v) {\n    using subgroup = tmpl::type_from<decltype(subgroup_v)>;\n    auto& subgroup_parser =\n        tuples::get<SubgroupParser<subgroup>>(subgroup_parsers_);\n    subgroup_parser.context_ = context_;\n    subgroup_parser.context_.append(\n        parse_detail::add_group_prefix_to_name(pretty_type::name<subgroup>()));\n    subgroup_parser.parse(\n        parsed_options_.find(pretty_type::name<subgroup>())->second);\n  });\n\n  // Any actual warnings will be printed by later calls to get or\n  // apply, but it is not clear how to determine in those functions\n  // whether this message should be printed.\n  if constexpr (std::is_same_v<Group, NoSuchType>) {\n    if (context_.top_level) {\n      parse_detail::print_top_level_error_message();\n    }\n  }\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename OverlayOptions>\nvoid Parser<OptionList, Group>::overlay(const YAML::Node& node) {\n  // This could be relaxed to allow mandatory options in a list with\n  // alternatives to be overlaid (or even any options in the chosen\n  // alternative), but overlaying is only done at top level and we\n  // don't use alternatives there (because they conflict with groups,\n  // which we do use).\n  static_assert(\n      std::is_same_v<\n          typename Options_detail::flatten_alternatives<OptionList>::type,\n          OptionList>,\n      \"Cannot overlay options when using alternatives.\");\n  static_assert(\n      std::is_same_v<tmpl::list_difference<OverlayOptions, OptionList>,\n                     tmpl::list<>>,\n      \"Can only overlay options that were originally parsed.\");\n\n  using overlayable_tags_and_subgroups_list =\n      tmpl::remove_duplicates<tmpl::transform<\n          OverlayOptions,\n          Options_detail::find_subgroup<tmpl::_1, tmpl::pin<Group>>>>;\n\n  if (not(node.IsMap() or node.IsNull())) {\n    PARSE_ERROR(context_, \"'\" << node << \"' does not look like options.\\n\"\n                              << help<overlayable_tags_and_subgroups_list>());\n  }\n\n  std::unordered_set<std::string> overlaid_options{};\n  overlaid_options.reserve(node.size());\n\n  for (const auto& name_and_value : node) {\n    const auto& name = name_and_value.first.as<std::string>();\n    const auto& value = name_and_value.second;\n    auto context = context_;\n    context.line = name_and_value.first.Mark().line;\n    context.column = name_and_value.first.Mark().column;\n\n    if (tmpl::as_pack<tags_and_subgroups_list>([&name](auto... opts) {\n          return (\n              (name != pretty_type::name<tmpl::type_from<decltype(opts)>>()) and\n              ...);\n        })) {\n      PARSE_ERROR(context,\n                  \"Option '\" << name << \"' is not a valid option.\\n\"\n                  << parsing_help<overlayable_tags_and_subgroups_list>(node));\n    }\n\n    if (tmpl::as_pack<overlayable_tags_and_subgroups_list>(\n            [&name](auto... opts) {\n              return ((name !=\n                       pretty_type::name<tmpl::type_from<decltype(opts)>>()) and\n                      ...);\n            })) {\n      PARSE_ERROR(context,\n                  \"Option '\" << name << \"' is not overlayable.\\n\"\n                  << parsing_help<overlayable_tags_and_subgroups_list>(node));\n    }\n\n    // Check for duplicate key\n    if (0 != overlaid_options.count(name)) {\n      PARSE_ERROR(context,\n                  \"Option '\" << name << \"' specified twice.\\n\"\n                  << parsing_help<overlayable_tags_and_subgroups_list>(node));\n    }\n\n    overlaid_options.insert(name);\n    parsed_options_.at(name) = value;\n  }\n\n  tmpl::for_each<subgroups>([this, &overlaid_options](auto subgroup_v) {\n    using subgroup = tmpl::type_from<decltype(subgroup_v)>;\n    if (overlaid_options.count(pretty_type::name<subgroup>()) == 1) {\n      auto& subgroup_parser =\n          tuples::get<SubgroupParser<subgroup>>(subgroup_parsers_);\n      subgroup_parser.template overlay<\n          Options_detail::options_in_group<OverlayOptions, subgroup>>(\n          parsed_options_.find(pretty_type::name<subgroup>())->second);\n    }\n  });\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename T>\nvoid Parser<OptionList, Group>::check_lower_bound_on_size(\n    const typename T::type& t, const Context& context) const {\n  if constexpr (Options_detail::has_lower_bound_on_size<T>::value) {\n    static_assert(std::is_same_v<decltype(T::lower_bound_on_size()), size_t>,\n                  \"lower_bound_on_size() is not a size_t.\");\n    if (t.size() < T::lower_bound_on_size()) {\n      PARSE_ERROR(context, \"Value must have at least \"\n                               << T::lower_bound_on_size() << \" entries, but \"\n                               << t.size() << \" were given.\\n\"\n                               << help());\n    }\n  }\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename T>\nvoid Parser<OptionList, Group>::check_upper_bound_on_size(\n    const typename T::type& t, const Context& context) const {\n  if constexpr (Options_detail::has_upper_bound_on_size<T>::value) {\n    static_assert(std::is_same_v<decltype(T::upper_bound_on_size()), size_t>,\n                  \"upper_bound_on_size() is not a size_t.\");\n    if (t.size() > T::upper_bound_on_size()) {\n      PARSE_ERROR(context, \"Value must have at most \"\n                               << T::upper_bound_on_size() << \" entries, but \"\n                               << t.size() << \" were given.\\n\"\n                               << help());\n    }\n  }\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename T>\ninline void Parser<OptionList, Group>::check_lower_bound(\n    const typename T::type& t, const Context& context) const {\n  if constexpr (Options_detail::has_lower_bound<T>::value) {\n    static_assert(std::is_same_v<decltype(T::lower_bound()), typename T::type>,\n                  \"Lower bound is not of the same type as the option.\");\n    static_assert(not std::is_same_v<typename T::type, bool>,\n                  \"Cannot set a lower bound for a bool.\");\n    if (t < T::lower_bound()) {\n      PARSE_ERROR(context, \"Value \" << (MakeString{} << t)\n                                    << \" is below the lower bound of \"\n                                    << (MakeString{} << T::lower_bound())\n                                    << \".\\n\" << help());\n    }\n  }\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename T>\ninline void Parser<OptionList, Group>::check_upper_bound(\n    const typename T::type& t, const Context& context) const {\n  if constexpr (Options_detail::has_upper_bound<T>::value) {\n    static_assert(std::is_same_v<decltype(T::upper_bound()), typename T::type>,\n                  \"Upper bound is not of the same type as the option.\");\n    static_assert(not std::is_same_v<typename T::type, bool>,\n                  \"Cannot set an upper bound for a bool.\");\n    if (t > T::upper_bound()) {\n      PARSE_ERROR(context, \"Value \" << (MakeString{} << t)\n                                    << \" is above the upper bound of \"\n                                    << (MakeString{} << T::upper_bound())\n                                    << \".\\n\" << help());\n    }\n  }\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename TagsAndSubgroups>\nstd::string Parser<OptionList, Group>::parsing_help(\n    const YAML::Node& options) const {\n  std::ostringstream os;\n  // At top level this would dump the entire input file, which is very\n  // verbose and not very informative.  At lower levels the result\n  // should be much shorter and may actually give useful context for\n  // what part of the file is being parsed.\n  if (not context_.top_level) {\n    os << \"\\n==== Parsing the option string:\\n\" << options << \"\\n\";\n  }\n  os << help<TagsAndSubgroups>();\n  return os.str();\n}\n\ntemplate <typename OptionList, typename Group>\n[[noreturn]] void Parser<OptionList, Group>::parser_error(\n    const YAML::Exception& e) const {\n  auto context = context_;\n  context.line = e.mark.line;\n  context.column = e.mark.column;\n  // Inline the top_level branch of PARSE_ERROR to avoid warning that\n  // the other branch would call terminate.  (Parser errors can only\n  // be generated at top level.)\n  ERROR(\n      \"\\n\"\n      << context\n      << \"Unable to correctly parse the input file because of a syntax error.\\n\"\n         \"This is often due to placing a suboption on the same line as an \"\n         \"option, e.g.:\\nDomainCreator: CreateInterval:\\n  IsPeriodicIn: \"\n         \"[false]\\n\\nShould be:\\nDomainCreator:\\n  CreateInterval:\\n    \"\n         \"IsPeriodicIn: [true]\\n\\nSee an example input file for help.\");\n}\n\ntemplate <typename OptionList, typename Group>\ntemplate <typename ChosenOptions, typename RemainingOptions, typename F>\nauto Parser<OptionList, Group>::call_with_chosen_alternatives_impl(\n    F&& func, std::vector<size_t> choices) const {\n  if constexpr (std::is_same_v<RemainingOptions, tmpl::list<>>) {\n    return std::forward<F>(func)(ChosenOptions{});\n  } else {\n    using next_option = tmpl::front<RemainingOptions>;\n    using remaining_options = tmpl::pop_front<RemainingOptions>;\n\n    if constexpr (not tt::is_a_v<Options::Alternatives, next_option>) {\n      return call_with_chosen_alternatives_impl<\n          tmpl::push_back<ChosenOptions, next_option>, remaining_options>(\n          std::forward<F>(func), std::move(choices));\n    } else {\n      using Result =\n          decltype(call_with_chosen_alternatives_impl<\n                   ChosenOptions,\n                   tmpl::append<tmpl::front<next_option>, remaining_options>>(\n              std::forward<F>(func), std::move(choices)));\n\n      const size_t choice = choices.back();\n      choices.pop_back();\n\n      Result result{};\n      size_t alternative_number = 0;\n      tmpl::for_each<next_option>([this, &alternative_number, &choice, &choices,\n                                   &func, &result](auto alternative) {\n        using Alternative = tmpl::type_from<decltype(alternative)>;\n        if (choice == alternative_number++) {\n          result = this->call_with_chosen_alternatives_impl<\n              ChosenOptions, tmpl::append<Alternative, remaining_options>>(\n              std::forward<F>(func), std::move(choices));\n        }\n      });\n      return result;\n    }\n  }\n}\n\nnamespace Options_detail {\n// Work around Clang bug: https://github.com/llvm/llvm-project/issues/33002\ntemplate <typename... T>\nstruct my_void_t_impl {\n  using type = void;\n};\ntemplate <typename... T>\nusing my_void_t = typename my_void_t_impl<T...>::type;\n\ntemplate <typename T, typename Metavariables, typename = my_void_t<>>\nstruct has_options_list : std::false_type {};\n\ntemplate <typename T, typename Metavariables>\nstruct has_options_list<T, Metavariables,\n                        my_void_t<typename T::template options<Metavariables>>>\n    : std::true_type {};\n\ntemplate <typename T, typename Metavariables>\nstruct has_options_list<T, Metavariables, my_void_t<typename T::options>>\n    : std::true_type {};\n\ntemplate <typename T, typename Metavariables, typename = std::void_t<>>\nstruct get_options_list {\n  using type = typename T::template options<Metavariables>;\n};\n\ntemplate <typename T, typename Metavariables>\nstruct get_options_list<T, Metavariables, std::void_t<typename T::options>> {\n  using type = typename T::options;\n};\n\ntemplate <typename T, typename Metavariables>\nstruct ClassConstructor {\n  const Options::Context& context;\n\n  template <typename ParsedOptions, typename... Args>\n  T operator()(ParsedOptions /*meta*/, Args&&... args) const {\n    if constexpr (std::is_constructible<T, ParsedOptions,\n                                        decltype(std::move(args))...,\n                                        const Context&, Metavariables>{}) {\n      return T(ParsedOptions{}, std::move(args)..., context, Metavariables{});\n    } else if constexpr (std::is_constructible<T, ParsedOptions,\n                                               decltype(std::move(args))...,\n                                               const Context&>{}) {\n      return T(ParsedOptions{}, std::move(args)..., context);\n    } else if constexpr (std::is_constructible<T, ParsedOptions,\n                                               decltype(std::move(\n                                                   args))...>{}) {\n      return T(ParsedOptions{}, std::move(args)...);\n    } else if constexpr (std::is_constructible<T, decltype(std::move(args))...,\n                                               const Context&,\n                                               Metavariables>{}) {\n      return T(std::move(args)..., context, Metavariables{});\n    } else if constexpr (std::is_constructible<T, decltype(std::move(args))...,\n                                               const Context&>{}) {\n      return T(std::move(args)..., context);\n    } else {\n      return T{std::move(args)...};\n    }\n  }\n};\n}  // namespace Options_detail\n\ntemplate <typename T>\ntemplate <typename Metavariables>\nT create_from_yaml<T>::create(const Option& options) {\n  Parser<typename Options_detail::get_options_list<T, Metavariables>::type>\n      parser(T::help);\n  parser.parse(options);\n  return parser.template apply_all<Metavariables>(\n      Options_detail::ClassConstructor<T, Metavariables>{options.context()});\n}\n\n// yaml-cpp doesn't handle C++11 types yet\ntemplate <typename K, typename V, typename H, typename P>\nstruct create_from_yaml<std::unordered_map<K, V, H, P>> {\n  template <typename Metavariables>\n  static std::unordered_map<K, V, H, P> create(const Option& options) {\n    auto ordered = options.parse_as<std::map<K, V>, Metavariables>();\n    std::unordered_map<K, V, H, P> result;\n    for (auto it = ordered.begin(); it != ordered.end();) {\n      auto node = ordered.extract(it++);\n      result.emplace(std::move(node.key()), std::move(node.mapped()));\n    }\n    return result;\n  }\n};\n\nnamespace Options_detail {\n// To get the full parse backtrace for a variant parse error the\n// failure should occur inside a nested call to parse_as.  This is a\n// type that will produce the correct error by failing to parse.\ntemplate <typename... T>\nstruct variant_parse_error {};\n\ntemplate <typename... T>\nstruct yaml_type<variant_parse_error<T...>> : yaml_type<std::variant<T...>> {};\n}  // namespace Options_detail\n\ntemplate <typename... T>\nstruct create_from_yaml<Options::Options_detail::variant_parse_error<T...>> {\n  template <typename Metavariables>\n  [[noreturn]] static Options::Options_detail::variant_parse_error<T...> create(\n      const Option& options) {\n    throw YAML::BadConversion(options.node().Mark());\n  }\n};\n\nnamespace Options_variant_detail {\ntemplate <typename T, typename Metavariables,\n          typename =\n              typename Options_detail::has_options_list<T, Metavariables>::type>\nstruct is_alternative_parsable_impl : std::false_type {};\n\ntemplate <typename T, typename Metavariables>\nstruct is_alternative_parsable_impl<T, Metavariables, std::true_type>\n    : tmpl::all<\n          typename Options_detail::get_options_list<T, Metavariables>::type,\n          std::is_same<tmpl::_1,\n                       Options_detail::find_subgroup<tmpl::_1, void>>> {};\n\ntemplate <typename T, typename Metavariables>\nstruct is_alternative_parsable\n    : is_alternative_parsable_impl<T, Metavariables> {};\n\ntemplate <typename Result, typename Metavariables, typename... Alternatives>\nResult parse_as_alternatives(const Options::Option& options,\n                             tmpl::list<Alternatives...> /*meta*/) {\n  using options_list = tmpl::list<\n      Options::Alternatives<typename Options_detail::get_options_list<\n          Alternatives, Metavariables>::type...>>;\n  std::string help = (\"\" + ... +\n                      (Options_detail::yaml_type<Alternatives>::value() + \"\\n\" +\n                       wrap_text(Alternatives::help, 77, \"  \") + \"\\n\\n\"));\n  help.resize(help.size() - 2);\n  Options::Parser<options_list> parser(std::move(help));\n  parser.parse(options);\n  return parser.template apply_all<Metavariables>([&](auto parsed_options,\n                                                      auto... args) -> Result {\n    // Actually matching against the whole option list is hard in the\n    // presence of possible nested alternatives, so we just check if\n    // all the options are individually valid.\n    using possible_alternatives = tmpl::filter<\n        tmpl::list<Alternatives...>,\n        std::is_same<tmpl::bind<tmpl::list_difference,\n                                tmpl::pin<decltype(parsed_options)>,\n                                Options_detail::flatten_alternatives<\n                                    Options_detail::get_options_list<\n                                        tmpl::_1, tmpl::pin<Metavariables>>>>,\n                     tmpl::pin<tmpl::list<>>>>;\n    static_assert(tmpl::size<possible_alternatives>::value == 1,\n                  \"Option lists for variant alternatives are too similar.\");\n    return Options_detail::ClassConstructor<tmpl::front<possible_alternatives>,\n                                            Metavariables>{options.context()}(\n        parsed_options, std::move(args)...);\n  });\n}\n}  // namespace Options_variant_detail\n\ntemplate <typename... T>\nstruct create_from_yaml<std::variant<T...>> {\n  using Result = std::variant<T...>;\n  static_assert(std::is_same_v<tmpl::list<T...>,\n                               tmpl::remove_duplicates<tmpl::list<T...>>>,\n                \"Cannot parse variants with duplicate types.\");\n\n  template <typename Metavariables>\n  static Result create(const Option& options) {\n    using alternative_parsable_types =\n        tmpl::filter<tmpl::list<T...>,\n                     Options_variant_detail::is_alternative_parsable<\n                         tmpl::_1, tmpl::pin<Metavariables>>>;\n\n    static constexpr bool use_alternative_parsing =\n        std::is_same_v<alternative_parsable_types, tmpl::list<T...>>;\n    static constexpr bool use_hybrid_parsing =\n        not use_alternative_parsing and\n        tmpl::size<alternative_parsable_types>::value > 1;\n\n    if constexpr (use_alternative_parsing) {\n      return Options_variant_detail::parse_as_alternatives<Result,\n                                                           Metavariables>(\n          options, alternative_parsable_types{});\n    } else {\n      const std::string error_separator =\n          \"\\n--------------------------------------------------\\n\";\n      Result result{};\n      std::string errors{};\n      bool constructed = false;\n\n      if constexpr (use_hybrid_parsing) {\n        try {\n          result = Options_variant_detail::parse_as_alternatives<Result,\n                                                                 Metavariables>(\n              options, alternative_parsable_types{});\n          constructed = true;\n        } catch (const Options::detail::propagate_context& e) {\n          // This alternative failed, but a later one may succeed.\n          errors += error_separator + e.message();\n        }\n      }\n\n      const auto try_parse = [&constructed, &error_separator, &errors, &options,\n                              &result](auto alternative_v) {\n        using Alternative = tmpl::type_from<decltype(alternative_v)>;\n        if (use_hybrid_parsing and\n            tmpl::any<alternative_parsable_types,\n                      std::is_same<tmpl::_1, tmpl::pin<Alternative>>>::value) {\n          return;\n        }\n        if (constructed) {\n          return;\n        }\n        try {\n          result = options.parse_as<Alternative, Metavariables>();\n          constructed = true;\n        } catch (const Options::detail::propagate_context& e) {\n          // This alternative failed, but a later one may succeed.\n          errors += error_separator + e.message();\n        }\n      };\n      EXPAND_PACK_LEFT_TO_RIGHT(try_parse(tmpl::type_<T>{}));\n      if (not constructed) {\n        try {\n          options.parse_as<Options_detail::variant_parse_error<T...>,\n                           Metavariables>();\n        } catch (const Options::detail::propagate_context& e) {\n          throw Options::detail::propagate_context(\n              e.message() + \"\\n\\nPossible errors:\" + errors);\n        }\n      }\n      return result;\n    }\n  }\n};\n}  // namespace Options\n\n/// \\cond\ntemplate <typename T, typename Metavariables>\nstruct YAML::convert<Options::Options_detail::CreateWrapper<T, Metavariables>> {\n  static bool decode(\n      const Node& node,\n      Options::Options_detail::CreateWrapper<T, Metavariables>& rhs) {\n    if constexpr (tt::is_integer_v<T>) {\n      // Try parsing integers as floating point to allow scientific\n      // notation for large values.\n      T result{};\n      if (not YAML::convert<T>::decode(node, result)) {\n        double float_result{};\n        if (not YAML::convert<double>::decode(node, float_result) or\n            float_result > static_cast<double>(std::numeric_limits<T>::max()) or\n            float_result < static_cast<double>(std::numeric_limits<T>::min())) {\n          return false;\n        }\n        result = static_cast<T>(float_result);\n        if (static_cast<double>(result) != float_result) {\n          return false;\n        }\n      }\n      rhs = Options::Options_detail::CreateWrapper<T, Metavariables>{result};\n      return true;\n    } else {\n      Options::Context context;\n      context.top_level = false;\n      context.append(\"While creating a \" + pretty_type::name<T>());\n      Options::Option options(node, std::move(context));\n      rhs = Options::Options_detail::CreateWrapper<T, Metavariables>{\n          Options::create_from_yaml<T>::template create<Metavariables>(\n              options)};\n      return true;\n    }\n  }\n};\n/// \\endcond\n\n#include \"Options/Factory.hpp\"\n"
  },
  {
    "path": "src/Options/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FactoryCreation.hpp\n  )\n"
  },
  {
    "path": "src/Options/Protocols/FactoryCreation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Options::protocols {\n/*!\n * \\brief Compile-time information for factory-creation\n *\n * A class conforming to this protocol is placed in the metavariables\n * to provide information on the set of derived classes for each\n * factory-creatable base class.  The conforming class must provide\n * the following type aliases:\n *\n * - `factory_classes`: A `tmpl::map` from the base class to the list\n *   of derived classes. List all derived classes that should be\n *   factory-creatable, e.g. through input-file options.\n *\n * Here's an example for a class conforming to this protocol:\n *\n * \\snippet Options/Test_Factory.cpp factory_creation\n */\nstruct FactoryCreation {\n  template <typename ConformingType>\n  struct test {\n    using factory_classes = typename ConformingType::factory_classes;\n\n    template <typename BaseClass, typename DerivedClass>\n    struct factory_class_is_derived_from_base_class : std::true_type {\n      static_assert(std::is_base_of_v<BaseClass, DerivedClass>,\n                    \"FactoryCreation protocol: The first template argument to \"\n                    \"this struct is not a base class of the second.\");\n    };\n\n    using all_classes_are_derived_classes = tmpl::all<\n        factory_classes,\n        tmpl::bind<\n            tmpl::all, tmpl::bind<tmpl::back, tmpl::_1>,\n            tmpl::defer<factory_class_is_derived_from_base_class<\n                tmpl::bind<tmpl::front, tmpl::parent<tmpl::_1>>, tmpl::_1>>>>;\n\n    static_assert(all_classes_are_derived_classes::value);\n  };\n};\n}  // namespace Options::protocols\n"
  },
  {
    "path": "src/Options/StdComplex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <utility>\n\n#include \"Options/Options.hpp\"\n\n/// Parse complex numbers as pairs of doubles.\ntemplate <>\nstruct Options::create_from_yaml<std::complex<double>> {\n  template <typename Metavariables>\n  static std::complex<double> create(const Options::Option& options) {\n    const auto pair_of_doubles = options.parse_as<std::pair<double, double>>();\n    return std::complex<double>(pair_of_doubles.first, pair_of_doubles.second);\n  }\n};\n"
  },
  {
    "path": "src/Options/String.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Options {\n/// The string used in option structs\nusing String = const char* const;\n}  // namespace Options\n"
  },
  {
    "path": "src/Options/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace Options::Tags {\n/// Option parser tag to retrieve the YAML source and all applied\n/// overlays.  This tag can be requested without providing it as a\n/// template parameter to the Parser.\nstruct InputSource {\n  using type = std::vector<std::string>;\n};\n}  // namespace Options::Tags\n"
  },
  {
    "path": "src/Parallel/AlgorithmExecution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n#include <optional>\n#include <tuple>\n\nnamespace Parallel {\n\n/// The possible options for altering the current execution of the algorithm,\n/// used in the return type of iterable actions.\nenum class AlgorithmExecution {\n  /// Continue executing iterable actions.\n  Continue,\n  /// Temporarily stop executing iterable actions, but try the same\n  /// action again after receiving data from other distributed\n  /// objects.\n  Retry,\n  /// Stop the execution of iterable actions, but allow entry methods\n  /// (communication) to explicitly request restarting the execution.\n  Pause,\n  /// Stop the execution of iterable actions and do not allow their execution\n  /// until after a phase change. Simple actions will still execute.\n  Halt\n};\n\n/// Return type for iterable actions.  The std::optional can be used to specify\n/// the index of the action to be called next in the phase dependent action\n/// list.  Passing std::nullopt will execute the next action in the list.\nusing iterable_action_return_t =\n    std::tuple<AlgorithmExecution, std::optional<size_t>>;\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/AlgorithmMetafunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel::Algorithm_detail {\n\ntemplate <typename Action, typename enable = std::void_t<>>\nstruct optional_simple_tags {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Action>\nstruct optional_simple_tags<Action, std::void_t<typename Action::simple_tags>> {\n  using type = typename Action::simple_tags;\n};\n\ntemplate <typename Action, typename enable = std::void_t<>>\nstruct optional_compute_tags {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Action>\nstruct optional_compute_tags<Action,\n                             std::void_t<typename Action::compute_tags>> {\n  using type = typename Action::compute_tags;\n};\n\ntemplate <typename ActionList>\nstruct get_action_list_simple_tags {\n  using type = tmpl::flatten<tmpl::transform<typename ActionList::action_list,\n                                             optional_simple_tags<tmpl::_1>>>;\n};\n\ntemplate <typename ActionList>\nstruct get_action_list_compute_tags {\n  using type = tmpl::flatten<tmpl::transform<typename ActionList::action_list,\n                                             optional_compute_tags<tmpl::_1>>>;\n};\n\ntemplate <typename Pdal>\nusing get_pdal_simple_tags = tmpl::remove_duplicates<tmpl::flatten<\n    tmpl::transform<Pdal, get_action_list_simple_tags<tmpl::_1>>>>;\n\ntemplate <typename Pdal>\nusing get_pdal_compute_tags = tmpl::remove_duplicates<tmpl::flatten<\n    tmpl::transform<Pdal, get_action_list_compute_tags<tmpl::_1>>>>;\n\ntemplate <typename ParallelComponent>\nusing action_list_simple_tags = get_pdal_simple_tags<\n    typename ParallelComponent::phase_dependent_action_list>;\n\ntemplate <typename ParallelComponent>\nusing action_list_compute_tags = tmpl::remove_duplicates<get_pdal_compute_tags<\n    typename ParallelComponent::phase_dependent_action_list>>;\n}  // namespace Parallel::Algorithm_detail\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmArray.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmodule AlgorithmArray {\n\n  include \"DataStructures/DataBox/DataBox.hpp\";\n  include \"DataStructures/TaggedTuple.hpp\";\n  include \"Parallel/Callback.hpp\";\n  include \"Parallel/GlobalCache.decl.h\";\n  include \"Parallel/MaxInlineMethodsReached.hpp\";\n  include \"Parallel/Phase.hpp\";\n\n  // note: the array index here is a hack to use our own types in the chare\n  // array index. The way the ci file is parsed as of charm++ 6.10.2 is such\n  // that:\n  // - the array index type in `AlgorithmArray.decl.h` prepends \"CkArrayIndex\"\n  //   to what appears in the square brackets, which then becomes\n  //   `CkArrayIndexSpectreIndex_detail::ArrayIndex<SpectreArrayIndex>`, which\n  //   refers to our type alias in `Parallel/ArrayIndex.hpp`.\n  // - the array index type in `AlgorithmArray.def.h` is precisely what appears\n  //   in the square brackets, so becomes\n  //   `SpectreIndex_detail::ArrayIndex<SpectreArrayIndex>`, which also refers\n  //   to one of our type aliases in `Parallel/ArrayIndex.hpp`\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  array [SpectreIndex_detail::ArrayIndex<SpectreArrayIndex>] AlgorithmArray {\n    entry AlgorithmArray(\n        Parallel::CProxy_GlobalCache<typename ParallelComponent::metavariables>,\n        tuples::tagged_tuple_from_typelist<\n            typename ParallelComponent::simple_tags_from_options>\n            initialization_items);\n\n    entry AlgorithmArray(\n        Parallel::CProxy_GlobalCache<typename ParallelComponent::metavariables>,\n        Parallel::Phase, std::unordered_map<Parallel::Phase, size_t>,\n        std::unique_ptr<Parallel::Callback>);\n\n    // Ordinarily, indiscriminate use of [inline] has the danger of arbitrarily\n    // deep recursive function calls, which then exceed the stack limits of the\n    // machine that it is running on and errors. We modify the charm definition\n    // of this function (via a patch in tools/CharmModulePatches) to switch to\n    // message-passing when the recursive depth is high, using\n    // Parallel::detail::max_inline_methods_reached() in\n    // MaxInlineMethodsReached.hpp. This allows us to inline up to a set limit\n    // (improving performance) and support load-balancing and debug\n    // instrumentation performed during charm entry method calls, while being\n    // certain to avoid stack overflows.\n\n    // The charm [inline] flag has been temporarily removed from the following\n    // entry methods:\n    // - `simple action` (both overloads)\n    // - `perform_algorithm` (both overloads)\n    // - `receive_data`\n    // The [inline] has been removed because of a charm bug that causes inlined\n    // entry methods to not be instrumented for load-balancing.\n    // Once the charm fix is published and we have updated our support, the\n    // [inline] flag should be restored to the listed entry methods.\n    // Related charm pull request: https://github.com/UIUC-PPL/charm/pull/3352\n\n    template <typename Action, typename... Args>\n    entry void simple_action(std::tuple<Args...> & args);\n\n    template <typename Action>\n    entry void simple_action();\n\n    template <typename Action, typename Arg>\n    entry [reductiontarget] void reduction_action(Arg arg);\n\n    entry void perform_algorithm();\n\n    entry void perform_algorithm(bool);\n\n    entry void start_phase(Parallel::Phase, bool force = false);\n\n    template <typename ReceiveTag, typename MessageType>\n    entry void receive_data(MessageType*);\n\n    template <typename ReceiveTag, typename ReceiveData_t>\n    entry [inline] void receive_data(typename ReceiveTag::temporal_id&,\n                                     ReceiveData_t&,\n                                     bool enable_if_disabled = false);\n\n    entry void set_terminate(bool);\n\n    template <typename ThisAction, typename PhaseIndex, typename DataBoxIndex>\n    entry[local] bool invoke_iterable_action();\n\n    entry void contribute_termination_status_to_main();\n  }\n}\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmArray.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// This file should be included in each file which defines an array\n/// parallel component; doing so ensures that the correct charm++ chares are\n/// defined for executables that use that parallel component.\n\n#pragma once\n\n#include \"Parallel/Algorithms/AlgorithmArrayDeclarations.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/DistributedObject.hpp\"\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A Spectre algorithm object that wraps a charm++ array chare.\n *\n * \\details This object is the definition of the distributed charm++ object\n * associated with the SpECTRE component wrappers (see \\ref\n * dev_guide_parallelization_foundations). See also the charm++ documentation:\n * https://charm.readthedocs.io/en/latest/charm++/manual.html#chare-arrays\n * When comparing against the implementations in the charm++ documentation, this\n * object is the one directly associated to declarations in the corresponding\n * `AlgorithmArray.ci` file.\n *\n * Unlike the techniques described in the charm++ documentation, however, we\n * define `AlgorithmArray` as a strong typedef so that the core functionality\n * can be shared between chare types by introducing an additional inheritance\n * layer. The documentation suggests the inheritance path `A -> CBase_A`, where\n * we have the inheritance path:\n * ```\n * AlgorithmArray -> DistributedObject -> CBase_AlgorithmArray\n * ```\n * That allows us to introduce the template class\n * <code><a href=\"classParallel_1_1DistributedObject_3_01ParallelComponent_00_01tmpl_1_1list_3_01PhaseDepActionListsPack_8_8_8_01_4_01_4.html\">\n * Parallel::DistributedObject</a></code>\n * that handles all generalizable control-flow.\n */\n// Note that the above manual link is needed because doxygen can't properly link\n// template specializations, but we'd really like to link to the\n// DistributedObject\ntemplate <typename ParallelComponent, typename SpectreArrayIndex>\nclass AlgorithmArray\n    : public Parallel::DistributedObject<\n          ParallelComponent,\n          typename ParallelComponent::phase_dependent_action_list> {\n  using algorithm = Parallel::Algorithms::Array;\n\n public:\n  using Parallel::DistributedObject<\n      ParallelComponent, typename ParallelComponent::\n                             phase_dependent_action_list>::DistributedObject;\n};\n\n#define CK_TEMPLATES_ONLY\n#include \"Parallel/Algorithms/AlgorithmArray.def.h\"\n#undef CK_TEMPLATES_ONLY\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmArrayDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Parallel/ArrayIndex.hpp\"\n\n#include \"Parallel/Algorithms/AlgorithmArray.decl.h\"\n\nnamespace Parallel {\nnamespace Algorithms {\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A struct that stores the charm++ types relevant for a particular array\n * component\n *\n * \\details The type traits are:\n * - `cproxy`: the charm++ proxy.\n *   See https://charm.readthedocs.io/en/latest/faq/manual.html#what-is-a-proxy\n * - `cbase`: the charm++ base class. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html#chare-objects\n * - `algorithm_type`: the chare type (`AlgorithmArray`) for the array\n *   component.\n * - `ckindex`: A charm++ chare index object. Useful for obtaining entry\n *   method indices that are needed for creating callbacks. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html#creating-a-ckcallback-object\n * - `cproxy_section`: The charm++ section proxy class. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html?#sections-subsets-of-a-chare-array-group\n * - component_type: this object. Serves to allow checking the component type in\n *   both source code and the testing framework.\n */\nstruct Array {\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using cproxy = CProxy_AlgorithmArray<ParallelComponent,\n                                       SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using cbase = CBase_AlgorithmArray<ParallelComponent,\n                                     SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using algorithm_type = AlgorithmArray<ParallelComponent,\n                                        SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using ckindex = CkIndex_AlgorithmArray<ParallelComponent,\n                                         SpectreArrayIndex>;\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  using cproxy_section =\n      CProxySection_AlgorithmArray<ParallelComponent, SpectreArrayIndex>;\n\n  using component_type = Array;\n};\n}  // namespace Algorithms\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmGroup.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmodule AlgorithmGroup {\n\n  include \"DataStructures/TaggedTuple.hpp\";\n  include \"Parallel/GlobalCache.decl.h\";\n  include \"Parallel/Phase.hpp\";\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  group [migratable] AlgorithmGroup {\n    entry AlgorithmGroup(\n        Parallel::CProxy_GlobalCache<typename ParallelComponent::metavariables>,\n        tuples::tagged_tuple_from_typelist<\n            typename ParallelComponent::simple_tags_from_options>\n            initialization_items);\n\n    template <typename Action, typename... Args>\n    entry void simple_action(std::tuple<Args...> & args);\n\n    template <typename Action>\n    entry void simple_action();\n\n    template <typename Action, typename Arg>\n    entry [reductiontarget] void reduction_action(Arg arg);\n\n    entry void perform_algorithm();\n\n    entry void perform_algorithm(bool);\n\n    entry void start_phase(Parallel::Phase);\n\n    template <typename ReceiveTag, typename ReceiveData_t>\n    entry [inline] void receive_data(typename ReceiveTag::temporal_id&,\n                                     ReceiveData_t&,\n                                     bool enable_if_disabled = false);\n\n    entry void set_terminate(bool);\n\n    entry void contribute_termination_status_to_main();\n  }\n}\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmGroup.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// This file should be included in each file which defines a group\n/// parallel component; doing so ensures that the correct charm++ chares are\n/// defined for executables that use that parallel component.\n\n#pragma once\n\n#include \"Parallel/Algorithms/AlgorithmGroupDeclarations.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/DistributedObject.hpp\"\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A Spectre algorithm object that wraps a charm++ group chare.\n *\n * \\details This object is the definition of the distributed charm++ object\n * associated with the SpECTRE component wrappers (see \\ref\n * dev_guide_parallelization_foundations). See also the charm++ documentation:\n * https://charm.readthedocs.io/en/latest/charm++/manual.html#group-objects\n * When comparing against the implementations in the charm++ documentation, this\n * object is the one directly associated to declarations in the corresponding\n * `AlgorithmGroup.ci` file.\n *\n * Unlike the techniques described in the charm++ documentation, however, we\n * define `AlgorithmGroup` as a strong typedef so that the core functionality\n * can be shared between chare types by introducing an additional inheritance\n * layer. The documentation suggests the inheritance path `A -> CBase_A`, where\n * we have the inheritance path:\n * ```\n * AlgorithmGroup -> DistributedObject -> CBase_AlgorithmGroup\n * ```\n * That allows us to introduce the template class\n * <code><a href=\"classParallel_1_1DistributedObject_3_01ParallelComponent_00_01tmpl_1_1list_3_01PhaseDepActionListsPack_8_8_8_01_4_01_4.html\">\n * Parallel::DistributedObject</a></code>\n * that handles all generalizable control-flow.\n */\n// Note that the above manual link is needed because doxygen can't properly link\n// template specializations, but we'd really like to link to the\n// DistributedObject\ntemplate <typename ParallelComponent, typename SpectreArrayIndex>\nclass AlgorithmGroup\n    : public Parallel::DistributedObject<\n          ParallelComponent,\n          typename ParallelComponent::phase_dependent_action_list> {\n  using algorithm = Parallel::Algorithms::Group;\n\n public:\n  using Parallel::DistributedObject<\n      ParallelComponent, typename ParallelComponent::\n                             phase_dependent_action_list>::DistributedObject;\n};\n\n#define CK_TEMPLATES_ONLY\n#include \"Parallel/Algorithms/AlgorithmGroup.def.h\"\n#undef CK_TEMPLATES_ONLY\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmGroupDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Parallel/ArrayIndex.hpp\"\n\n#include \"Parallel/Algorithms/AlgorithmGroup.decl.h\"\n\nnamespace Parallel {\nnamespace Algorithms {\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A struct that stores the charm++ types relevant for a particular group\n * component\n *\n * \\details The type traits are:\n * - `cproxy`: the charm++ proxy.\n *   See https://charm.readthedocs.io/en/latest/faq/manual.html#what-is-a-proxy\n * - `cbase`: the charm++ base class. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html#chare-objects\n * - `algorithm_type`: the chare type (`AlgorithmGroup`) for the group\n *   component.\n * - `ckindex`: A charm++ chare index object. Useful for obtaining entry\n *   method indices that are needed for creating callbacks. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html#creating-a-ckcallback-object\n * - `cproxy_section`: The charm++ section proxy class. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html?#sections-subsets-of-a-chare-array-group\n * - component_type: this object. Serves to allow checking the component type in\n *   both source code and the testing framework.\n */\nstruct Group {\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using cproxy = CProxy_AlgorithmGroup<ParallelComponent,\n                                       SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using cbase = CBase_AlgorithmGroup<ParallelComponent,\n                                     SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using algorithm_type = AlgorithmGroup<ParallelComponent,\n                                        SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using ckindex = CkIndex_AlgorithmGroup<ParallelComponent,\n                                         SpectreArrayIndex>;\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  using cproxy_section =\n      CProxySection_AlgorithmGroup<ParallelComponent, SpectreArrayIndex>;\n\n  using component_type = Group;\n};\n}  // namespace Algorithms\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmNodegroup.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmodule AlgorithmNodegroup {\n\n  include \"DataStructures/TaggedTuple.hpp\";\n  include \"Parallel/GlobalCache.decl.h\";\n  include \"Parallel/Phase.hpp\";\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  nodegroup [migratable] AlgorithmNodegroup {\n    entry AlgorithmNodegroup(\n        Parallel::CProxy_GlobalCache<typename ParallelComponent::metavariables>,\n        tuples::tagged_tuple_from_typelist<\n            typename ParallelComponent::simple_tags_from_options>\n            initialization_items);\n\n    template <typename Action, typename... Args>\n    entry void simple_action(std::tuple<Args...> & args);\n\n    template <typename Action>\n    entry void simple_action();\n\n    template <typename Action, typename Arg>\n    entry [reductiontarget] void reduction_action(Arg arg);\n\n    entry void perform_algorithm();\n\n    entry void perform_algorithm(bool);\n\n    entry void start_phase(Parallel::Phase);\n\n    template <typename Action, typename... Args>\n    entry void threaded_action(std::tuple<Args...> & args);\n\n    template <typename Action>\n    entry void threaded_action();\n\n    template <typename ReceiveTag, typename ReceiveData_t>\n    entry [inline] void receive_data(typename ReceiveTag::temporal_id&,\n                                     ReceiveData_t&,\n                                     bool enable_if_disabled = false);\n\n    entry void set_terminate(bool);\n\n    entry void contribute_termination_status_to_main();\n  }\n}\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmNodegroup.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// This file should be included in each file which defines a nodegroup\n/// parallel component; doing so ensures that the correct charm++ chares are\n/// defined for executables that use that parallel component.\n\n#pragma once\n\n#include \"Parallel/Algorithms/AlgorithmNodegroupDeclarations.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/DistributedObject.hpp\"\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A Spectre algorithm object that wraps a charm++ nodegroup chare.\n *\n * \\details This object is the definition of the distributed charm++ object\n * associated with the SpECTRE component wrappers (see \\ref\n * dev_guide_parallelization_foundations). See also the charm++ documentation:\n * https://charm.readthedocs.io/en/latest/charm++/manual.html#nodegroup-objects\n * When comparing against the implementations in the charm++ documentation, this\n * object is the one directly associated to declarations in the corresponding\n * `AlgorithmNodegroup.ci` file.\n *\n * Unlike the techniques described in the charm++ documentation, however, we\n * define `AlgorithmNodegroup` as a strong typedef so that the core\n * functionality can be shared between chare types by introducing an additional\n * inheritance layer. The documentation suggests the inheritance path `A ->\n * CBase_A`, where we have the inheritance path:\n * ```\n * AlgorithmNodegroup -> DistributedObject -> CBase_AlgorithmNodegroup\n * ```\n * That allows us to introduce the template class\n * <code><a href=\"classParallel_1_1DistributedObject_3_01ParallelComponent_00_01tmpl_1_1list_3_01PhaseDepActionListsPack_8_8_8_01_4_01_4.html\">\n * Parallel::DistributedObject</a></code>\n * that handles all generalizable control-flow.\n */\n// Note that the above manual link is needed because doxygen can't properly link\n// template specializations, but we'd really like to link to the\n// DistributedObject\ntemplate <typename ParallelComponent, typename SpectreArrayIndex>\nclass AlgorithmNodegroup\n    : public Parallel::DistributedObject<\n          ParallelComponent,\n          typename ParallelComponent::phase_dependent_action_list> {\n  using algorithm = Parallel::Algorithms::Nodegroup;\n\n public:\n  using Parallel::DistributedObject<\n      ParallelComponent, typename ParallelComponent::\n                             phase_dependent_action_list>::DistributedObject;\n};\n\n#define CK_TEMPLATES_ONLY\n#include \"Parallel/Algorithms/AlgorithmNodegroup.def.h\"\n#undef CK_TEMPLATES_ONLY\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmNodegroupDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Parallel/ArrayIndex.hpp\"\n\n#include \"Parallel/Algorithms/AlgorithmNodegroup.decl.h\"\n\nnamespace Parallel {\nnamespace Algorithms {\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A struct that stores the charm++ types relevant for a particular\n * nodegroup component\n *\n * \\details The type traits are:\n * - `cproxy`: the charm++ proxy.\n *   See https://charm.readthedocs.io/en/latest/faq/manual.html#what-is-a-proxy\n * - `cbase`: the charm++ base class. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html#chare-objects\n * - `algorithm_type`: the chare type (`AlgorithmNodegroup`) for the nodegroup\n *   component.\n * - `ckindex`: A charm++ chare index object. Useful for obtaining entry\n *   method indices that are needed for creating callbacks. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html#creating-a-ckcallback-object\n * - `cproxy_section`: The charm++ section proxy class. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html?#sections-subsets-of-a-chare-array-group\n * - component_type: this object. Serves to allow checking the component type in\n *   both source code and the testing framework.\n */\nstruct Nodegroup {\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using cproxy = CProxy_AlgorithmNodegroup<ParallelComponent,\n                                           SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using cbase = CBase_AlgorithmNodegroup<ParallelComponent,\n                                         SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using algorithm_type = AlgorithmNodegroup<ParallelComponent,\n                                            SpectreArrayIndex>;\n\n  template <typename ParallelComponent,\n            typename SpectreArrayIndex>\n  using ckindex = CkIndex_AlgorithmNodegroup<ParallelComponent,\n                                             SpectreArrayIndex>;\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  using cproxy_section =\n      CProxySection_AlgorithmNodegroup<ParallelComponent, SpectreArrayIndex>;\n\n  using component_type = Nodegroup;\n};\n}  // namespace Algorithms\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmSingleton.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmodule AlgorithmSingleton {\n\n  include \"DataStructures/TaggedTuple.hpp\";\n  include \"Parallel/GlobalCache.decl.h\";\n  include \"Parallel/MaxInlineMethodsReached.hpp\";\n  include \"Parallel/Phase.hpp\";\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  array [SpectreIndex_detail::ArrayIndex<SpectreArrayIndex>] AlgorithmSingleton{\n    entry AlgorithmSingleton(\n        Parallel::CProxy_GlobalCache<typename ParallelComponent::metavariables>,\n        tuples::tagged_tuple_from_typelist<\n            typename ParallelComponent::simple_tags_from_options>\n            initialization_items);\n\n    template <typename Action, typename... Args>\n    entry void simple_action(std::tuple<Args...> & args);\n\n    template <typename Action>\n    entry void simple_action();\n\n    template <typename Action, typename Arg>\n    entry [reductiontarget] void reduction_action(Arg arg);\n\n    entry void perform_algorithm();\n\n    entry void perform_algorithm(bool);\n\n    entry void start_phase(Parallel::Phase);\n\n    template <typename ReceiveTag, typename ReceiveData_t>\n    entry [inline] void receive_data(typename ReceiveTag::temporal_id&,\n                                     ReceiveData_t&,\n                                     bool enable_if_disabled = false);\n\n    entry void set_terminate(bool);\n\n    entry void contribute_termination_status_to_main();\n  }\n}\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmSingleton.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// This file should be included in each file which defines a singleton\n/// parallel component; doing so ensures that the correct charm++ chares are\n/// defined for executables that use that parallel component.\n\n#pragma once\n\n#include \"Parallel/Algorithms/AlgorithmSingletonDeclarations.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/DistributedObject.hpp\"\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A Spectre algorithm object that wraps a single element charm++ array\n * chare.\n *\n * \\details Charm++ does offer a distributed object called a singleton, however\n * we don't use this for a few reasons:\n *\n * 1. Charm++ singletons cannot be (easily) placed on particular processors.\n *    Typically we will want singletons on their own processors.\n * 2. Charm++ singletons don't participate in load balancing.\n * 3. Charm++ singletons don't participate in checkpoint/restart when restarted\n *    on a different number of procs.\n *\n * For implementation details, see AlgorithmArray.\n */\ntemplate <typename ParallelComponent, typename SpectreArrayIndex>\nclass AlgorithmSingleton\n    : public Parallel::DistributedObject<\n          ParallelComponent,\n          typename ParallelComponent::phase_dependent_action_list> {\n  using algorithm = Parallel::Algorithms::Singleton;\n\n public:\n  using Parallel::DistributedObject<\n      ParallelComponent, typename ParallelComponent::\n                             phase_dependent_action_list>::DistributedObject;\n};\n\n#define CK_TEMPLATES_ONLY\n#include \"Parallel/Algorithms/AlgorithmSingleton.def.h\"\n#undef CK_TEMPLATES_ONLY\n"
  },
  {
    "path": "src/Parallel/Algorithms/AlgorithmSingletonDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Parallel/ArrayIndex.hpp\"\n\n#include \"Parallel/Algorithms/AlgorithmSingleton.decl.h\"\n\nnamespace Parallel {\nnamespace Algorithms {\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A struct that stores the charm++ types relevant for a particular\n * singleton component\n *\n * \\details The type traits are:\n * - `cproxy`: the charm++ proxy.\n *   See https://charm.readthedocs.io/en/latest/faq/manual.html#what-is-a-proxy\n * - `cbase`: the charm++ base class. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html#chare-objects\n * - `algorithm_type`: the chare type (`AlgorithmSingleton`) for the singleton\n *   component.\n * - `ckindex`: A charm++ chare index object. Useful for obtaining entry\n *   method indices that are needed for creating callbacks. See\n *   https://charm.readthedocs.io/en/latest/charm++/manual.html#creating-a-ckcallback-object\n * - component_type: this object. Serves to allow checking the component type in\n *   both source code and the testing framework.\n */\nstruct Singleton {\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  using cproxy =\n      CProxy_AlgorithmSingleton<ParallelComponent, SpectreArrayIndex>;\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  using cbase = CBase_AlgorithmSingleton<ParallelComponent, SpectreArrayIndex>;\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  using algorithm_type =\n      AlgorithmSingleton<ParallelComponent, SpectreArrayIndex>;\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  using ckindex =\n      CkIndex_AlgorithmSingleton<ParallelComponent, SpectreArrayIndex>;\n\n  template <typename ParallelComponent, typename SpectreArrayIndex>\n  using cproxy_section = void;\n\n  using component_type = Singleton;\n};\n}  // namespace Algorithms\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Algorithms/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_charm_module(AlgorithmArray)\nadd_charm_module(AlgorithmGroup)\nadd_charm_module(AlgorithmNodegroup)\nadd_charm_module(AlgorithmSingleton)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AlgorithmArray.hpp\n  AlgorithmArrayDeclarations.hpp\n  AlgorithmGroup.hpp\n  AlgorithmGroupDeclarations.hpp\n  AlgorithmNodegroup.hpp\n  AlgorithmNodegroupDeclarations.hpp\n  AlgorithmSingleton.hpp\n  AlgorithmSingletonDeclarations.hpp\n)\n\nadd_dependencies(\n  ${LIBRARY}\n  module_AlgorithmArray\n  module_AlgorithmGroup\n  module_AlgorithmNodegroup\n  module_AlgorithmSingleton\n  )\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ParallelArrayCollection)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CreateElementCollection.hpp\n  CreateElementsUsingDistribution.hpp\n  DgElementArrayMember.hpp\n  DgElementArrayMemberBase.hpp\n  DgElementCollection.hpp\n  IsDgElementArrayMember.hpp\n  IsDgElementCollection.hpp\n  PerformAlgorithmOnElement.hpp\n  ReceiveDataForElement.hpp\n  SendDataToElement.hpp\n  SetTerminateOnElement.hpp\n  SimpleActionOnElement.hpp\n  SpawnInitializeElementsInCollection.hpp\n  StartPhaseOnNodegroup.hpp\n  TransformPdalForNodegroup.hpp\n)\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  DgElementArrayMemberBase.cpp\n)\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DomainStructure\n  ErrorHandling\n  Parallel\n  Serialization\n  Utilities\n  INTERFACE\n  Domain\n  DomainCreators\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/CreateElementCollection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags/ElementDistribution.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/CreateElementsUsingDistribution.hpp\"\n#include \"Parallel/ArrayCollection/SpawnInitializeElementsInCollection.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/ArrayCollection/Tags/NumberOfElementsTerminated.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel::Actions {\n/*!\n * \\brief Creates the `DgElementArrayMember`s on the (node)group component.\n *\n * First the distribution of elements is computed using\n * `Parallel::create_elements_using_distribution()`, they are inserted on\n * each (node)group element. A reduction is done over the (node)group before\n * initializing the `DgElementArrayMember`s themselves, since they are allowed\n * to communicate with each other during their initialization. The reduction\n * target is `Parallel::Actions::SpawnInitializeElementsInCollection`.\n *\n * Uses:\n * - DataBox:\n *   - `domain::Tags::Domain<Dim>`\n *   - `domain::Tags::InitialRefinementLevels<Dim>`\n *   - `domain::Tags::InitialExtents<Dim>`\n *   - `evolution::dg::Tags::Quadrature`\n *   - `domain::Tags::ElementDistribution`\n *\n * DataBox changes:\n * - Adds:\n *   - `Parallel::Tags::ElementCollection`\n *   - `Parallel::Tags::ElementLocations<Dim>`\n *   - `Parallel::Tags::NumberOfElementsTerminated`\n * - Removes: nothing\n * - Modifies:\n *   - `Parallel::Tags::ElementCollection`\n *   - `Parallel::Tags::ElementLocations<Dim>`\n *   - `Parallel::Tags::NumberOfElementsTerminated`\n */\ntemplate <size_t Dim, class Metavariables, class PhaseDepActionList,\n          typename SimpleTagsFromOptions>\nstruct CreateElementCollection {\n  using simple_tags = tmpl::list<\n      Parallel::Tags::ElementCollection<Dim, Metavariables, PhaseDepActionList,\n                                        SimpleTagsFromOptions>,\n      Parallel::Tags::ElementLocations<Dim>, Tags::NumberOfElementsTerminated>;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::domain::Tags::Domain<Dim>,\n                 ::domain::Tags::ElementDistribution>;\n\n  using return_tag_list = tmpl::append<simple_tags, compute_tags>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& local_cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const std::unordered_set<size_t> procs_to_ignore{};\n\n    const auto& domain = Parallel::get<domain::Tags::Domain<Dim>>(local_cache);\n    const auto& initial_refinement_levels =\n        get<domain::Tags::InitialRefinementLevels<Dim>>(box);\n    const auto& initial_extents = get<domain::Tags::InitialExtents<Dim>>(box);\n    const auto i1_basis = Spectral::Basis::Legendre;\n    const auto& i1_quadrature = get<evolution::dg::Tags::Quadrature>(box);\n    const std::optional<domain::ElementWeight>& element_weight =\n        Parallel::get<domain::Tags::ElementDistribution>(local_cache);\n\n    const size_t number_of_procs =\n        Parallel::number_of_procs<size_t>(local_cache);\n    const size_t number_of_nodes =\n        Parallel::number_of_nodes<size_t>(local_cache);\n    const size_t num_of_procs_to_use = number_of_procs - procs_to_ignore.size();\n\n    const auto& blocks = domain.blocks();\n\n    const size_t total_num_elements = [&blocks, &initial_refinement_levels]() {\n      size_t result = 0;\n      for (const auto& block : blocks) {\n        const auto& initial_ref_levs = initial_refinement_levels[block.id()];\n        for (const size_t ref_lev : initial_ref_levs) {\n          result += two_to_the(ref_lev);\n        }\n      }\n      return result;\n    }();\n    std::vector<std::pair<ElementId<Dim>, size_t>> my_elements_and_cores{};\n    my_elements_and_cores.reserve(total_num_elements / number_of_nodes + 1);\n    std::unordered_map<ElementId<Dim>, size_t> node_of_elements{};\n    const size_t my_node = Parallel::my_node<size_t>(local_cache);\n\n    Parallel::create_elements_using_distribution(\n        [&my_elements_and_cores, my_node, &node_of_elements](\n            const ElementId<Dim>& element_id, const size_t target_proc,\n            const size_t target_node) {\n          node_of_elements.insert(std::pair{element_id, target_node});\n          if (target_node == my_node) {\n            my_elements_and_cores.push_back(std::pair{element_id, target_proc});\n          }\n        },\n        element_weight, blocks, initial_extents, initial_refinement_levels,\n        i1_basis, i1_quadrature,\n        // The below arguments control how the elements are mapped to the\n        // hardware.\n        procs_to_ignore, number_of_procs, number_of_nodes, num_of_procs_to_use,\n        local_cache, my_node == 0);\n\n    tuples::tagged_tuple_from_typelist<SimpleTagsFromOptions>\n        initialization_items = db::copy_items<SimpleTagsFromOptions>(box);\n\n    const gsl::not_null<Parallel::NodeLock*> node_lock = make_not_null(\n        &Parallel::local_branch(\n             Parallel::get_parallel_component<ParallelComponent>(local_cache))\n             ->get_node_lock());\n    db::mutate<Tags::ElementLocations<Dim>,\n               Tags::ElementCollection<Dim, Metavariables, PhaseDepActionList,\n                                       SimpleTagsFromOptions>,\n               Tags::NumberOfElementsTerminated>(\n        [&local_cache, &initialization_items, &my_elements_and_cores,\n         &node_of_elements](\n            const auto element_locations_ptr, const auto collection_ptr,\n            const gsl::not_null<size_t*> number_of_elements_terminated) {\n          *number_of_elements_terminated = 0;\n          const auto serialized_initialization_items =\n              serialize(initialization_items);\n          *element_locations_ptr = std::move(node_of_elements);\n          for (const auto& element_id_and_core : my_elements_and_cores) {\n            const auto& element_id = element_id_and_core.first;\n            const auto core = element_id_and_core.second;\n            if (not collection_ptr\n                        ->emplace(\n                            std::piecewise_construct,\n                            std::forward_as_tuple(element_id),\n                            std::forward_as_tuple(\n                                local_cache.get_this_proxy(),\n                                deserialize<tuples::tagged_tuple_from_typelist<\n                                    SimpleTagsFromOptions>>(\n                                    serialized_initialization_items.data()),\n                                element_id))\n                        .second) {\n              ERROR(\"Failed to insert element with ID: \" << element_id);\n            }\n            if (collection_ptr->at(element_id).get_terminate() == true) {\n              ++(*number_of_elements_terminated);\n            } else {\n              ERROR(\"Inserted element with ID \"\n                    << element_id\n                    << \" was not initialized in a terminated state. This is a \"\n                       \"bug.\");\n            }\n            collection_ptr->at(element_id).set_core(core);\n          }\n          if (*number_of_elements_terminated != collection_ptr->size()) {\n            ERROR(\n                \"The number of elements inserted must match the number of \"\n                \"elements set to terminate since the default state of an \"\n                \"inserted element is terminated. This is a bug.\");\n          }\n        },\n        make_not_null(&box));\n\n    Parallel::contribute_to_reduction<\n        Parallel::Actions::SpawnInitializeElementsInCollection>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<int, funcl::AssertEqual<>>>{0},\n        Parallel::get_parallel_component<ParallelComponent>(\n            local_cache)[my_node],\n        Parallel::get_parallel_component<ParallelComponent>(local_cache));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/CreateElementsUsingDistribution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/ElementDistribution.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/DomainDiagnosticInfo.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\n/// \\cond\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace Parallel {\n/*!\n * \\brief Creates elements using a chosen distribution.\n *\n * The `func` is called with `(element_id, target_proc, target_node)` allowing\n * the `func` to insert the element with `element_id` on the target processor\n * and node.\n */\ntemplate <typename F, size_t Dim, typename Metavariables>\nvoid create_elements_using_distribution(\n    const F& func, const std::optional<domain::ElementWeight>& element_weight,\n    const std::vector<Block<Dim>>& blocks,\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const std::vector<std::array<size_t, Dim>>& initial_refinement_levels,\n    const Spectral::Basis i1_basis, const Spectral::Quadrature i1_quadrature,\n\n    const std::unordered_set<size_t>& procs_to_ignore,\n    const size_t number_of_procs, const size_t number_of_nodes,\n    const size_t num_of_procs_to_use,\n    const Parallel::GlobalCache<Metavariables>& local_cache,\n    const bool print_diagnostics) {\n  // Only need the element distribution if the element weight has a value\n  // because then we have to use the space filling curve and not just use round\n  // robin.\n  domain::BlockZCurveProcDistribution<Dim> element_distribution{};\n  if (element_weight.has_value()) {\n    const std::unordered_map<ElementId<Dim>, double> element_costs =\n        domain::get_element_costs(blocks, initial_refinement_levels,\n                                  initial_extents, element_weight.value(),\n                                  i1_basis, i1_quadrature);\n    element_distribution = domain::BlockZCurveProcDistribution<Dim>{\n        element_costs,   num_of_procs_to_use, blocks, initial_refinement_levels,\n        initial_extents, procs_to_ignore};\n  }\n\n  // Will be used to print domain diagnostic info\n  std::vector<size_t> elements_per_core(number_of_procs, 0_st);\n  std::vector<size_t> elements_per_node(number_of_nodes, 0_st);\n  std::vector<size_t> grid_points_per_core(number_of_procs, 0_st);\n  std::vector<size_t> grid_points_per_node(number_of_nodes, 0_st);\n\n  size_t which_proc = 0;\n  for (const auto& block : blocks) {\n    const auto& initial_ref_levs = initial_refinement_levels[block.id()];\n    const size_t grid_points_per_element =\n        alg::accumulate(initial_extents[block.id()], 1_st, std::multiplies<>());\n\n    const std::vector<ElementId<Dim>> element_ids =\n        initial_element_ids(block.id(), initial_ref_levs);\n\n    // Value means ZCurve. nullopt means round robin\n    if (element_weight.has_value()) {\n      for (const auto& element_id : element_ids) {\n        const size_t target_proc =\n            element_distribution.get_proc_for_element(element_id);\n        const size_t target_node =\n            Parallel::node_of<size_t>(target_proc, local_cache);\n        func(element_id, target_proc, target_node);\n\n        ++elements_per_core[target_proc];\n        ++elements_per_node[target_node];\n        grid_points_per_core[target_proc] += grid_points_per_element;\n        grid_points_per_node[target_node] += grid_points_per_element;\n      }\n    } else {\n      for (size_t i = 0; i < element_ids.size(); ++i) {\n        while (procs_to_ignore.find(which_proc) != procs_to_ignore.end()) {\n          which_proc = which_proc + 1 == number_of_procs ? 0 : which_proc + 1;\n        }\n        const size_t target_proc = which_proc;\n        const size_t target_node =\n            Parallel::node_of<size_t>(which_proc, local_cache);\n        const ElementId<Dim> element_id(element_ids[i]);\n        func(element_id, target_proc, target_node);\n\n        ++elements_per_core[which_proc];\n        ++elements_per_node[target_node];\n        grid_points_per_core[which_proc] += grid_points_per_element;\n        grid_points_per_node[target_node] += grid_points_per_element;\n\n        which_proc = which_proc + 1 == number_of_procs ? 0 : which_proc + 1;\n      }\n    }\n  }\n\n  if (print_diagnostics) {\n    Parallel::printf(\"\\n%s\\n\", domain::diagnostic_info(\n                                   blocks.size(), local_cache,\n                                   elements_per_core, elements_per_node,\n                                   grid_points_per_core, grid_points_per_node));\n  }\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/DgElementArrayMember.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <cstddef>\n#include <exception>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/AlgorithmMetafunctions.hpp\"\n#include \"Parallel/ArrayCollection/DgElementArrayMemberBase.hpp\"\n#include \"Parallel/ArrayCollection/SetTerminateOnElement.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/Tags/ArrayIndex.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/System/Abort.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <size_t Dim, class Metavariables, class PhaseDepActionList>\nstruct DgElementCollection;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Parallel {\n/*!\n * \\brief An element or multiple elements stored contiguously on a group or\n * nodegroup.\n *\n * Consider first the simpler case where each `DgElementArrayMember` is single\n * DG or FD element. Each has a DataBox, and inbox. The various bookkeeping\n * constructs are stored in the `DgElementArrayMemberBase`. The\n * `DgElementArrayMember` is effectively the distributed object that has\n * remote entry methods invoked on it. However, a `DgElementArrayMember` is\n * not tied to a particalur core since it lives on a nodegroup (the\n * `DgElementCollection`). It is also possible to use a group instead of a\n * nodegroup, but that is mostly of interest when using GPUs.\n */\ntemplate <size_t Dim, typename Metavariables, typename PhaseDepActionList,\n          typename SimpleTagsFromOptions>\nclass DgElementArrayMember;\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\n// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor)\nclass DgElementArrayMember<Dim, Metavariables,\n                           tmpl::list<PhaseDepActionListsPack...>,\n                           SimpleTagsFromOptions>\n    : public DgElementArrayMemberBase<Dim> {\n public:\n  using ParallelComponent =\n      DgElementCollection<Dim, Metavariables,\n                          tmpl::list<PhaseDepActionListsPack...>>;\n\n  /// List of Actions in the order that generates the DataBox types\n  using all_actions_list = tmpl::flatten<\n      tmpl::list<typename PhaseDepActionListsPack::action_list...>>;\n  /// The metavariables class passed to the Algorithm\n  using metavariables = Metavariables;\n  /// List of all the Tags that can be received into the Inbox\n  using inbox_tags_list = Parallel::get_inbox_tags<all_actions_list>;\n  using phase_dependent_action_lists = tmpl::list<PhaseDepActionListsPack...>;\n  using all_cache_tags = get_const_global_cache_tags<metavariables>;\n\n  using databox_type = db::compute_databox_type<tmpl::flatten<tmpl::list<\n      Tags::MetavariablesImpl<metavariables>, Tags::ArrayIndex<ElementId<Dim>>,\n      Tags::GlobalCacheProxy<metavariables>, SimpleTagsFromOptions,\n      Tags::GlobalCacheCompute<metavariables>,\n      Tags::ResourceInfoReference<metavariables>,\n      db::wrap_tags_in<Tags::FromGlobalCache, all_cache_tags, metavariables>,\n      Algorithm_detail::get_pdal_simple_tags<phase_dependent_action_lists>,\n      Algorithm_detail::get_pdal_compute_tags<phase_dependent_action_lists>>>>;\n\n  using inbox_type = tuples::tagged_tuple_from_typelist<inbox_tags_list>;\n\n  DgElementArrayMember() = default;\n\n  template <class... InitializationTags>\n  DgElementArrayMember(\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache_proxy,\n      tuples::TaggedTuple<InitializationTags...> initialization_items,\n      ElementId<Dim> element_id);\n\n  /// \\cond\n  ~DgElementArrayMember() override = default;\n\n  DgElementArrayMember(const DgElementArrayMember& /*unused*/) = default;\n  DgElementArrayMember& operator=(const DgElementArrayMember& /*unused*/) =\n      default;\n  DgElementArrayMember(DgElementArrayMember&& /*unused*/) = default;\n  DgElementArrayMember& operator=(DgElementArrayMember&& /*unused*/) = default;\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(DgElementArrayMemberBase<Dim>), DgElementArrayMember);\n\n  explicit DgElementArrayMember(CkMigrateMessage* msg);\n  /// \\endcond\n\n  /// Start execution of the phase-dependent action list in `next_phase`. If\n  /// `next_phase` has already been visited, execution will resume at the point\n  /// where the previous execution of the same phase left off.\n  ///\n  /// If \\p force is true, then regardless of how this component terminated\n  /// (error or deadlock), it will resume.\n  ///\n  /// \\warning Don't set \\p force to true unless you are absolutely sure you\n  /// want to. This can have very unintended consequences if used incorrectly.\n  void start_phase(Parallel::Phase next_phase, bool force = false) override;\n\n  const auto& databox() const { return box_; }\n\n  /// Start evaluating the algorithm until it is stopped by an action.\n  void perform_algorithm() override;\n\n  template <typename ThisAction, typename PhaseIndex, typename DataBoxIndex>\n  bool invoke_iterable_action();\n\n  /*!\n   * \\brief Invokes a simple action on the element.\n   *\n   * \\note This does not lock the element. It is up to the calling action to\n   * lock the element if needed.\n   */\n  template <typename Action, typename... Args>\n  void simple_action(Args&&... args) {\n    try {\n      if (this->performing_action_) {\n        ERROR(\n            \"Already performing an Action and cannot execute additional \"\n            \"Actions from inside of an Action. This is only possible if the \"\n            \"simple_action function is not invoked via a proxy, which \"\n            \"we do not allow.\");\n      }\n      this->performing_action_ = true;\n      Action::template apply<ParallelComponent>(\n          box_, *Parallel::local_branch(global_cache_proxy_), this->element_id_,\n          std::forward<Args>(args)...);\n      this->performing_action_ = false;\n      perform_algorithm();\n    } catch (const std::exception& exception) {\n      initiate_shutdown(exception);\n    }\n  }\n\n  /// Print the expanded type aliases\n  std::string print_types() const override;\n\n  /// Print the current contents of the inboxes\n  std::string print_inbox() const override;\n\n  /// Print the current contents of the DataBox\n  std::string print_databox() const override;\n\n  /// @{\n  /// Get read access to all the inboxes\n  auto& inboxes() { return inboxes_; }\n  const auto& inboxes() const { return inboxes_; }\n  const auto& get_inboxes() const { return inboxes(); }\n  /// @}\n\n  void pup(PUP::er& p) override;\n\n private:\n  size_t number_of_actions_in_phase(Parallel::Phase phase) const;\n\n  // After catching an exception, shutdown the simulation\n  void initiate_shutdown(const std::exception& exception);\n\n  template <typename PhaseDepActions, size_t... Is>\n  bool iterate_over_actions(std::index_sequence<Is...> /*meta*/);\n  static_assert(std::is_move_constructible_v<databox_type>);\n  static_assert(std::is_move_constructible_v<inbox_type>);\n\n  Parallel::CProxy_GlobalCache<Metavariables> global_cache_proxy_{};\n  databox_type box_{};\n  inbox_type inboxes_{};\n};\n\n/// \\cond\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nDgElementArrayMember<\n    Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n    SimpleTagsFromOptions>::DgElementArrayMember(CkMigrateMessage* msg)\n    : DgElementArrayMemberBase<Dim>(msg) {}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\ntemplate <class... InitializationTags>\nDgElementArrayMember<Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n                     SimpleTagsFromOptions>::\n    DgElementArrayMember(\n        const Parallel::CProxy_GlobalCache<Metavariables>& global_cache_proxy,\n        tuples::TaggedTuple<InitializationTags...> initialization_items,\n        ElementId<Dim> element_id)\n    : Parallel::DgElementArrayMemberBase<Dim>(\n          std::move(element_id),\n          Parallel::my_node<size_t>(\n              *Parallel::local_branch(global_cache_proxy))),\n      global_cache_proxy_(global_cache_proxy) {\n  (void)initialization_items;  // avoid potential compiler warnings if unused\n  ::Initialization::mutate_assign<\n      tmpl::list<Tags::ArrayIndex<ElementId<Dim>>,\n                 Tags::GlobalCacheProxy<Metavariables>, InitializationTags...>>(\n      make_not_null(&box_), this->element_id_, global_cache_proxy_,\n      std::move(get<InitializationTags>(initialization_items))...);\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nvoid DgElementArrayMember<\n    Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n    SimpleTagsFromOptions>::start_phase(const Parallel::Phase next_phase,\n                                        const bool force) {\n  try {\n    auto& cache = *Parallel::local_branch(global_cache_proxy_);\n    // terminate should be true since we exited a phase previously.\n    if (force) {\n      Parallel::local_synchronous_action<\n          Parallel::Actions::SetTerminateOnElement>(\n          Parallel::get_parallel_component<ParallelComponent>(cache),\n          make_not_null(&cache), this->element_id_, true);\n    }\n    if (not this->get_terminate() and\n        not this->halt_algorithm_until_next_phase_) {\n      ERROR(\n          \"An algorithm must always be set to terminate at the beginning of a \"\n          \"phase. Since this is not the case the previous phase did not end \"\n          \"correctly. The previous phase is: \"\n          << this->phase_ << \" and the next phase is: \" << next_phase\n          << \", The termination flag is: \" << this->get_terminate()\n          << \", and the halt flag is: \"\n          << this->halt_algorithm_until_next_phase_ << ' '\n          << this->element_id_);\n    }\n    // set terminate to true if there are no actions in this PDAL\n    Parallel::local_synchronous_action<\n        Parallel::Actions::SetTerminateOnElement>(\n        Parallel::get_parallel_component<ParallelComponent>(cache),\n        make_not_null(&cache), this->element_id_,\n        number_of_actions_in_phase(next_phase) == 0);\n\n    // Ideally, we'd set the bookmarks as we are leaving a phase, but there is\n    // no 'clean-up' code that we run when departing a phase, so instead we set\n    // the bookmark for the previous phase (still stored in `phase_` at this\n    // point), before we update the member variable `phase_`.\n    // Then, after updating `phase_`, we check if we've ever stored a bookmark\n    // for the new phase previously. If so, we start from where we left off,\n    // otherwise, start from the beginning of the action list.\n    this->phase_bookmarks_[this->phase_] = this->algorithm_step_;\n    this->phase_ = next_phase;\n    if (this->phase_bookmarks_.count(this->phase_) != 0) {\n      this->algorithm_step_ = this->phase_bookmarks_.at(this->phase_);\n    } else {\n      this->algorithm_step_ = 0;\n    }\n    this->halt_algorithm_until_next_phase_ = false;\n    perform_algorithm();\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nvoid DgElementArrayMember<Dim, Metavariables,\n                          tmpl::list<PhaseDepActionListsPack...>,\n                          SimpleTagsFromOptions>::perform_algorithm() {\n  try {\n    if (this->performing_action_ or this->get_terminate() or\n        this->halt_algorithm_until_next_phase_) {\n      return;\n    }\n    const auto invoke_for_phase = [this](auto phase_dep_v) {\n      using PhaseDep = decltype(phase_dep_v);\n      constexpr Parallel::Phase phase = PhaseDep::phase;\n      using actions_list = typename PhaseDep::action_list;\n      if (this->phase_ == phase) {\n        while (\n            tmpl::size<actions_list>::value > 0 and\n            not this->get_terminate() and\n            not this->halt_algorithm_until_next_phase_ and\n            iterate_over_actions<PhaseDep>(\n                std::make_index_sequence<tmpl::size<actions_list>::value>{})) {\n        }\n        tmpl::for_each<actions_list>([this](auto action_v) {\n          using action = tmpl::type_from<decltype(action_v)>;\n          if (this->algorithm_step_ ==\n              tmpl::index_of<actions_list, action>::value) {\n            this->deadlock_analysis_next_iterable_action_ =\n                pretty_type::name<action>();\n          }\n        });\n      }\n    };\n    // Loop over all phases, once the current phase is found we perform the\n    // algorithm in that phase until we are no longer able to because we are\n    // waiting on data to be sent or because the algorithm has been marked as\n    // terminated.\n    EXPAND_PACK_LEFT_TO_RIGHT(invoke_for_phase(PhaseDepActionListsPack{}));\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nstd::string\nDgElementArrayMember<Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n                     SimpleTagsFromOptions>::print_types() const {\n  std::ostringstream os;\n  os << \"Algorithm type aliases:\\n\";\n  os << \"using all_actions_list = \" << pretty_type::get_name<all_actions_list>()\n     << \";\\n\";\n\n  os << \"using metavariables = \" << pretty_type::get_name<metavariables>()\n     << \";\\n\";\n  os << \"using inbox_tags_list = \" << pretty_type::get_name<inbox_tags_list>()\n     << \";\\n\";\n  os << \"using array_index = \" << pretty_type::get_name<ElementId<Dim>>()\n     << \";\\n\";\n  os << \"using parallel_component = \"\n     << pretty_type::get_name<ParallelComponent>() << \";\\n\";\n  os << \"using phase_dependent_action_lists = \"\n     << pretty_type::get_name<phase_dependent_action_lists>() << \";\\n\";\n  os << \"using all_cache_tags = \" << pretty_type::get_name<all_cache_tags>()\n     << \";\\n\";\n  os << \"using databox_type = \" << pretty_type::get_name<databox_type>()\n     << \";\\n\";\n  return os.str();\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nstd::string\nDgElementArrayMember<Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n                     SimpleTagsFromOptions>::print_inbox() const {\n  std::ostringstream os;\n  os << \"inboxes_ = \" << inboxes_ << \";\\n\";\n  return os.str();\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nstd::string\nDgElementArrayMember<Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n                     SimpleTagsFromOptions>::print_databox() const {\n  std::ostringstream os;\n  os << \"box_:\\n\" << box_;\n  return os.str();\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\ntemplate <typename PhaseDepActions, size_t... Is>\nbool DgElementArrayMember<Dim, Metavariables,\n                          tmpl::list<PhaseDepActionListsPack...>,\n                          SimpleTagsFromOptions>::\n    iterate_over_actions(const std::index_sequence<Is...> /*meta*/) {\n  bool take_next_action = true;\n  const auto helper = [this, &take_next_action](auto iteration) {\n    constexpr size_t iter = decltype(iteration)::value;\n    if (not(take_next_action and not this->terminate_ and\n            not this->halt_algorithm_until_next_phase_ and\n            this->algorithm_step_ == iter)) {\n      return;\n    }\n    using actions_list = typename PhaseDepActions::action_list;\n    using this_action = tmpl::at_c<actions_list, iter>;\n\n    constexpr size_t phase_index =\n        tmpl::index_of<phase_dependent_action_lists, PhaseDepActions>::value;\n    this->performing_action_ = true;\n    ++(this->algorithm_step_);\n    // While the overhead from using the local entry method to enable\n    // profiling is fairly small (<2%), we still avoid it when we aren't\n    // tracing.\n    // #ifdef SPECTRE_CHARM_PROJECTIONS\n    //     if constexpr (Parallel::is_array<parallel_component>::value) {\n    //       if (not this->thisProxy[array_index_]\n    //                   .template invoke_iterable_action<\n    //                       this_action, std::integral_constant<size_t,\n    //                       phase_index>, std::integral_constant<size_t,\n    //                       iter>>()) {\n    //         take_next_action = false;\n    //         --algorithm_step_;\n    //       }\n    //     } else {\n    // #endif  // SPECTRE_CHARM_PROJECTIONS\n    if (not invoke_iterable_action<this_action,\n                                   std::integral_constant<size_t, phase_index>,\n                                   std::integral_constant<size_t, iter>>()) {\n      take_next_action = false;\n      --(this->algorithm_step_);\n    }\n    // #ifdef SPECTRE_CHARM_PROJECTIONS\n    //     }\n    // #endif  // SPECTRE_CHARM_PROJECTIONS\n    this->performing_action_ = false;\n    // Wrap counter if necessary\n    if (this->algorithm_step_ >= tmpl::size<actions_list>::value) {\n      this->algorithm_step_ = 0;\n    }\n  };\n  // In case of no Actions avoid compiler warning.\n  (void)helper;\n  // This is a template for loop for Is\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(std::integral_constant<size_t, Is>{}));\n  return take_next_action;\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\ntemplate <typename ThisAction, typename PhaseIndex, typename DataBoxIndex>\nbool DgElementArrayMember<Dim, Metavariables,\n                          tmpl::list<PhaseDepActionListsPack...>,\n                          SimpleTagsFromOptions>::invoke_iterable_action() {\n  using phase_dep_action =\n      tmpl::at_c<phase_dependent_action_lists, PhaseIndex::value>;\n  using actions_list = typename phase_dep_action::action_list;\n\n#ifdef SPECTRE_CHARM_PROJECTIONS\n  if constexpr (Parallel::is_array<ParallelComponent>::value) {\n    (void)Parallel::charmxx::RegisterInvokeIterableAction<\n        ParallelComponent, ThisAction, PhaseIndex, DataBoxIndex>::registrar;\n  }\n#endif  // SPECTRE_CHARM_PROJECTIONS\n\n  const auto& [requested_execution_return, next_action_step] =\n      ThisAction::apply(box_, inboxes_,\n                        *Parallel::local_branch(global_cache_proxy_),\n                        std::as_const(this->element_id_), actions_list{},\n                        std::add_pointer_t<ParallelComponent>{});\n  const auto& requested_execution = requested_execution_return;\n\n  if (next_action_step.has_value()) {\n    ASSERT(\n        AlgorithmExecution::Retry != requested_execution,\n        \"Switching actions on Retry doesn't make sense. Specify std::nullopt \"\n        \"as the second argument of the iterable action return type\");\n    this->algorithm_step_ = next_action_step.value();\n  }\n\n  switch (requested_execution) {\n    case AlgorithmExecution::Continue:\n      return true;\n    case AlgorithmExecution::Retry:\n      return false;\n    case AlgorithmExecution::Pause: {\n      auto& cache = *Parallel::local_branch(global_cache_proxy_);\n      Parallel::local_synchronous_action<\n          Parallel::Actions::SetTerminateOnElement>(\n          Parallel::get_parallel_component<ParallelComponent>(cache),\n          make_not_null(&cache), this->element_id_, true);\n      return true;\n    }\n    case AlgorithmExecution::Halt: {\n      // Need to make sure halt also gets propagated to the nodegroup\n      this->halt_algorithm_until_next_phase_ = true;\n      auto& cache = *Parallel::local_branch(global_cache_proxy_);\n      Parallel::local_synchronous_action<\n          Parallel::Actions::SetTerminateOnElement>(\n          Parallel::get_parallel_component<ParallelComponent>(cache),\n          make_not_null(&cache), this->element_id_, true);\n      return true;\n    }\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"No case for a Parallel::AlgorithmExecution with integral value \"\n            << static_cast<std::underlying_type_t<AlgorithmExecution>>(\n                   requested_execution)\n            << \"\\n\");\n      // LCOV_EXCL_STOP\n  };\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nsize_t DgElementArrayMember<\n    Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n    SimpleTagsFromOptions>::number_of_actions_in_phase(const Parallel::Phase\n                                                           phase) const {\n  size_t number_of_actions = 0;\n  const auto helper = [&number_of_actions, phase](auto pdal_v) {\n    if (pdal_v.phase == phase) {\n      number_of_actions = pdal_v.number_of_actions;\n    }\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(PhaseDepActionListsPack{}));\n  return number_of_actions;\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nvoid DgElementArrayMember<\n    Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n    SimpleTagsFromOptions>::initiate_shutdown(const std::exception& exception) {\n  // In order to make it so that we can later run other actions for cleanup\n  // (e.g. dumping data) we need to make sure that we enable running actions\n  // again\n  this->performing_action_ = false;\n  // Send message to `Main` that we received an exception and set termination.\n  auto* global_cache = Parallel::local_branch(global_cache_proxy_);\n  if (UNLIKELY(global_cache == nullptr)) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n    CkError(\n        \"Global cache pointer is null. This is an internal inconsistency \"\n        \"error. Please file an issue.\");\n    sys::abort(\"\");\n  }\n  auto main_proxy = global_cache->get_main_proxy();\n  if (UNLIKELY(not main_proxy.has_value())) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n    CkError(\n        \"The main proxy has not been set in the global cache when terminating \"\n        \"the component. This is an internal inconsistency error. Please file \"\n        \"an issue.\");\n    sys::abort(\"\");\n  }\n  const std::string message = MakeString{}\n                              << \"Message: \" << exception.what() << \"\\nType: \"\n                              << pretty_type::get_runtime_type_name(exception);\n  main_proxy.value().add_exception_message(message);\n  auto& cache = *Parallel::local_branch(global_cache_proxy_);\n  Parallel::local_synchronous_action<Parallel::Actions::SetTerminateOnElement>(\n      Parallel::get_parallel_component<ParallelComponent>(cache),\n      make_not_null(&cache), this->element_id_, true);\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nvoid DgElementArrayMember<Dim, Metavariables,\n                          tmpl::list<PhaseDepActionListsPack...>,\n                          SimpleTagsFromOptions>::pup(PUP::er& p) {\n  DgElementArrayMemberBase<Dim>::pup(p);\n  p | global_cache_proxy_;\n  p | box_;\n  p | inboxes_;\n  if (p.isUnpacking()) {\n    // Since we need the global cache to set the node, the derived class\n    // does it instead of the base class.\n    this->my_node_ =\n        Parallel::my_node<size_t>(*Parallel::local_branch(global_cache_proxy_));\n  }\n}\n\ntemplate <size_t Dim, typename Metavariables,\n          typename... PhaseDepActionListsPack, typename SimpleTagsFromOptions>\nPUP::able::PUP_ID DgElementArrayMember<\n    Dim, Metavariables, tmpl::list<PhaseDepActionListsPack...>,\n    SimpleTagsFromOptions>::my_PUP_ID =  // NOLINT\n    0;\n/// \\endcond\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/DgElementArrayMemberBase.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/ArrayCollection/DgElementArrayMemberBase.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <sstream>\n#include <string>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace Parallel {\ntemplate <size_t Dim>\nDgElementArrayMemberBase<Dim>::DgElementArrayMemberBase(\n    ElementId<Dim> element_id, size_t node_number)\n    : element_id_(element_id), my_node_(node_number) {}\n\ntemplate <size_t Dim>\nDgElementArrayMemberBase<Dim>::DgElementArrayMemberBase(CkMigrateMessage* msg)\n    : PUP::able(msg) {}\n\ntemplate <size_t Dim>\nPhase DgElementArrayMemberBase<Dim>::phase() const {\n  return phase_;\n}\n\ntemplate <size_t Dim>\nvoid DgElementArrayMemberBase<Dim>::set_terminate(\n    const gsl::not_null<size_t*> number_of_elements_terminated,\n    const gsl::not_null<Parallel::NodeLock*> nodegroup_lock,\n    const bool terminate) {\n  ASSERT(nodegroup_lock->try_lock() == false,\n         \"The nodegroup lock must be locked in order to call set_terminate on \"\n         \"the DgElementArrayMember\");\n  if (not terminate_ and terminate) {\n    ++(*number_of_elements_terminated);\n  } else if (terminate_ and not terminate) {\n    if ((*number_of_elements_terminated) > 0) {\n      --(*number_of_elements_terminated);\n    }\n  } else {\n    ASSERT(terminate_ == terminate,\n           \"The DG element with id \"\n               << element_id_ << \" currently has termination status \"\n               << terminate_ << \" and is being set to \" << terminate\n               << \". This is an internal inconsistency problem.\");\n  }\n  terminate_ = terminate;\n}\n\ntemplate <size_t Dim>\nbool DgElementArrayMemberBase<Dim>::get_terminate() const {\n  return terminate_;\n}\n\ntemplate <size_t Dim>\nsize_t DgElementArrayMemberBase<Dim>::algorithm_step() const {\n  return algorithm_step_;\n}\n\ntemplate <size_t Dim>\nstd::string DgElementArrayMemberBase<Dim>::print_state() const {\n  using ::operator<<;\n  std::ostringstream os;\n  os << \"State:\\n\";\n  os << \"performing_action_ = \" << std::boolalpha << performing_action_\n     << \";\\n\";\n  os << \"phase_ = \" << phase_ << \";\\n\";\n  os << \"phase_bookmarks_ = \" << phase_bookmarks_ << \";\\n\";\n  os << \"algorithm_step_ = \" << algorithm_step_ << \";\\n\";\n  os << \"terminate_ = \" << terminate_ << \";\\n\";\n  os << \"halt_algorithm_until_next_phase_ = \"\n     << halt_algorithm_until_next_phase_ << \";\\n\";\n  os << \"array_index_ = \" << element_id_ << \";\\n\";\n  return os.str();\n}\n\ntemplate <size_t Dim>\nParallel::NodeLock& DgElementArrayMemberBase<Dim>::inbox_lock() {\n  return inbox_lock_;\n}\n\ntemplate <size_t Dim>\nParallel::NodeLock& DgElementArrayMemberBase<Dim>::element_lock() {\n  return element_lock_;\n}\n\ntemplate <size_t Dim>\nvoid DgElementArrayMemberBase<Dim>::set_core(size_t core) {\n  my_core_ = core;\n}\n\ntemplate <size_t Dim>\nsize_t DgElementArrayMemberBase<Dim>::get_core() const {\n  return my_core_;\n}\n\ntemplate <size_t Dim>\nvoid DgElementArrayMemberBase<Dim>::pup(PUP::er& p) {\n  PUP::able::pup(p);\n  p | performing_action_;\n  p | phase_;\n  p | phase_bookmarks_;\n  p | algorithm_step_;\n  p | terminate_;\n  p | halt_algorithm_until_next_phase_;\n  p | deadlock_analysis_next_iterable_action_;\n  p | element_id_;\n  p | my_core_;\n  if (p.isUnpacking()) {\n    // Node locks are default-constructed, which is fine\n    //\n    // Note: my_node_ is set by derived class pup\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data) \\\n  template class DgElementArrayMemberBase<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/DgElementArrayMemberBase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace Parallel {\n/*!\n * \\brief The base class of a member of an DG element array/map on a nodegroup.\n *\n * The nodegroup DgElementCollection stores all the elements on a node. Each\n * of those elements is a `DgElementArrayMember` and has this base class to\n * make access easier so it can be used in a type-erased context since\n * `DgElementArrayMember` depends on the metavariables,\n * phase-dependent-action-list, and the simple tags needed from options.\n *\n * This class essentially mimicks a lot of the functionality of\n * `Parallel::DistributedObject` but does not involve Charm++ beyond\n * serialization.\n */\ntemplate <size_t Dim>\nclass DgElementArrayMemberBase : PUP::able {\n public:\n  DgElementArrayMemberBase() = default;\n\n  DgElementArrayMemberBase(const DgElementArrayMemberBase& /*rhs*/) = default;\n  DgElementArrayMemberBase& operator=(const DgElementArrayMemberBase& /*rhs*/) =\n      default;\n  DgElementArrayMemberBase(DgElementArrayMemberBase&& /*rhs*/) = default;\n  DgElementArrayMemberBase& operator=(DgElementArrayMemberBase&& /*rhs*/) =\n      default;\n  ~DgElementArrayMemberBase() override = default;\n\n  WRAPPED_PUPable_abstract(DgElementArrayMemberBase);  // NOLINT\n\n  explicit DgElementArrayMemberBase(CkMigrateMessage* msg);\n\n  /// Start execution of the phase-dependent action list in `next_phase`. If\n  /// `next_phase` has already been visited, execution will resume at the point\n  /// where the previous execution of the same phase left off.\n  ///\n  /// If \\p force is true, then regardless of how this component terminated\n  /// (error or deadlock), it will resume.\n  ///\n  /// \\warning Don't set \\p force to true unless you are absolutely sure you\n  /// want to. This can have very unintended consequences if used incorrectly.\n  virtual void start_phase(Parallel::Phase next_phase, bool force = false) = 0;\n\n  /// Get the current phase\n  Parallel::Phase phase() const;\n\n  /// Tell the Algorithm it should no longer execute the algorithm. This does\n  /// not mean that the execution of the program is terminated, but only that\n  /// the algorithm has terminated. An algorithm can be restarted by passing\n  /// `true` as the second argument to the `receive_data` method or by calling\n  /// perform_algorithm(true).\n  void set_terminate(gsl::not_null<size_t*> number_of_elements_terminated,\n                     gsl::not_null<Parallel::NodeLock*> nodegroup_lock,\n                     bool terminate);\n\n  /// Check if an algorithm should continue being evaluated\n  bool get_terminate() const;\n\n  /// The zero-indexed step in the algorithm.\n  size_t algorithm_step() const;\n\n  /// Start evaluating the algorithm until it is stopped by an action.\n  virtual void perform_algorithm() = 0;\n\n  /// Print the expanded type aliases\n  virtual std::string print_types() const = 0;\n\n  /// Print the current state of the algorithm\n  std::string print_state() const;\n\n  /// Print the current contents of the inboxes\n  virtual std::string print_inbox() const = 0;\n\n  /// Print the current contents of the DataBox\n  virtual std::string print_databox() const = 0;\n\n  /// \\brief The `inbox_lock()` only locks the inbox, nothing else. The inbox is\n  /// unsafe to access without this lock.\n  ///\n  /// Use `element_lock()` to lock the rest of the element.\n  ///\n  /// This should always be managed by `std::unique_lock` or `std::lock_guard`.\n  Parallel::NodeLock& inbox_lock();\n\n  /// \\brief Locks the element, except for the inbox, which is guarded by the\n  /// `inbox_lock()`.\n  ///\n  /// This should always be managed by `std::unique_lock` or `std::lock_guard`.\n  Parallel::NodeLock& element_lock();\n\n  /// \\brief Set which core this element should pretend to be bound to.\n  void set_core(size_t core);\n\n  /// \\brief Get which core this element should pretend to be bound to.\n  size_t get_core() const;\n\n  /// Returns the name of the last \"next iterable action\" to be run before a\n  /// deadlock occurred.\n  const std::string& deadlock_analysis_next_iterable_action() const {\n    return deadlock_analysis_next_iterable_action_;\n  }\n\n  void pup(PUP::er& p) override;\n\n protected:\n  DgElementArrayMemberBase(ElementId<Dim> element_id, size_t node_number);\n\n  Parallel::NodeLock inbox_lock_{};\n  Parallel::NodeLock element_lock_{};\n  bool performing_action_ = false;\n  Parallel::Phase phase_{Parallel::Phase::Initialization};\n  std::unordered_map<Parallel::Phase, size_t> phase_bookmarks_{};\n  std::size_t algorithm_step_ = 0;\n\n  bool terminate_{true};\n  bool halt_algorithm_until_next_phase_{false};\n\n  // Records the name of the next action to be called so that during deadlock\n  // analysis we can print this out.\n  std::string deadlock_analysis_next_iterable_action_{};\n  ElementId<Dim> element_id_;\n  size_t my_node_{std::numeric_limits<size_t>::max()};\n  // There is no associated core. However, we use this as a method of\n  // interoperating with core-aware concepts like the interpolation\n  // framework. Once that framework is core-agnostic we will remove my_core_.\n  size_t my_core_{std::numeric_limits<size_t>::max()};\n};\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/DgElementCollection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"Parallel/Algorithms/AlgorithmNodegroupDeclarations.hpp\"\n#include \"Parallel/ArrayCollection/CreateElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/DgElementArrayMember.hpp\"\n#include \"Parallel/ArrayCollection/DgElementArrayMemberBase.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/TransformPdalForNodegroup.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\n/*!\n * \\brief A collection of DG elements on a node.\n *\n * The `PhaseDepActionList` is the PDAL that was used for the array\n * approach. Some actions will require updating to support nodegroups if they\n * haven't already been.\n */\ntemplate <size_t Dim, class Metavariables, class PhaseDepActionList>\nstruct DgElementCollection {\n  /// \\brief The `DgElementCollection` is currently a nodegroup.\n  ///\n  /// It should be possible to generalize this to work as a group too.\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = true;\n  /// \\brief The metavariables\n  using metavariables = Metavariables;\n  /// \\brief The simple tags necessary from option parsing.\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<PhaseDepActionList>>;\n  /// \\brief The type of the `Parallel::DgElementArrayMember` with all\n  /// template parameters.\n  using dg_element_array_member =\n      Parallel::DgElementArrayMember<Dim, Metavariables, PhaseDepActionList,\n                                     simple_tags_from_options>;\n  /// \\brief The tag `Parallel::Tags::ElementCollection` of this\n  /// `DgElementCollection`\n  using element_collection_tag =\n      Parallel::Tags::ElementCollection<Dim, Metavariables, PhaseDepActionList,\n                                        simple_tags_from_options>;\n\n  /// \\brief The phase dependent action lists.\n  ///\n  /// These are computed using\n  /// `Parallel::TransformPhaseDependentActionListForNodegroup` from the\n  /// `PhaseDepActionList` template parameter.\n  using phase_dependent_action_list = tmpl::append<\n      tmpl::list<Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<Actions::CreateElementCollection<Dim, Metavariables,\n                                                      PhaseDepActionList,\n                                                      simple_tags_from_options>,\n                     Parallel::Actions::TerminatePhase>>>,\n      TransformPhaseDependentActionListForNodegroup<PhaseDepActionList>>;\n\n  /// @{\n  /// \\brief The tags for the global cache.\n  ///\n  /// These are computed from the `PhaseDepActionList` template parameter.\n  using const_global_cache_tags = tmpl::remove_duplicates<\n      typename Parallel::detail::get_const_global_cache_tags_from_pdal<\n          PhaseDepActionList>::type>;\n  using mutable_global_cache_tags =\n      typename Parallel::detail::get_mutable_global_cache_tags_from_pdal<\n          PhaseDepActionList>::type;\n  /// @}\n\n  /// \\brief Starts the next phase of execution.\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    Parallel::printf(\"%s\\n\", next_phase);\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<DgElementCollection>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/IsDgElementArrayMember.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n/// \\cond\nnamespace Parallel {\ntemplate <size_t Dim, typename Metavariables, typename PhaseDepActionList,\n          typename SimpleTagsFromOptions>\nclass DgElementArrayMember;\ntemplate <size_t Dim>\nclass DgElementArrayMemberBase;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Parallel {\n/// \\brief Is `std::true_type` if `T` is a `Parallel::DgElementArrayMember` or\n/// `Parallel::DgElementArrayMemberBase`, else is `std::false_type`.\ntemplate <typename T>\nstruct is_dg_element_array_member : std::false_type {};\n\n/// \\cond\ntemplate <size_t Dim, typename Metavariables, typename PhaseDepActionList,\n          typename SimpleTagsFromOptions>\nstruct is_dg_element_array_member<DgElementArrayMember<\n    Dim, Metavariables, PhaseDepActionList, SimpleTagsFromOptions>>\n    : std::true_type {};\n\ntemplate <size_t Dim>\nstruct is_dg_element_array_member<DgElementArrayMemberBase<Dim>>\n    : std::true_type {};\n/// \\endcond\n\n/// \\brief Is `true` if `T` is a `Parallel::DgElementArrayMember` or\n/// `Parallel::DgElementArrayMemberBase`, else is `false`.\ntemplate <class T>\nconstexpr bool is_dg_element_array_member_v =\n    is_dg_element_array_member<T>::value;\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/IsDgElementCollection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n/// \\cond\nnamespace Parallel {\ntemplate <size_t Dim, typename Metavariables, typename PhaseDepActionList>\nclass DgElementCollection;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Parallel {\n/// \\brief Is `std::true_type` if `T` is a `Parallel::DgElementCollection`, else\n/// is `std::false_type`.\ntemplate <typename T>\nstruct is_dg_element_collection : std::false_type {};\n\n/// \\cond\ntemplate <size_t Dim, typename Metavariables, typename PhaseDepActionList>\nstruct is_dg_element_collection<\n    DgElementCollection<Dim, Metavariables, PhaseDepActionList>>\n    : std::true_type {};\n/// \\endcond\n\n/// \\brief Is `true` if `T` is a `Parallel::DgElementCollection`, else is\n/// `false`.\ntemplate <class T>\nconstexpr bool is_dg_element_collection_v = is_dg_element_collection<T>::value;\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/PerformAlgorithmOnElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel::Actions {\n/// \\brief A threaded action that calls `perform_algorithm()` for a specified\n/// element on the nodegroup.\n///\n/// If `Block` is `true` then the action will wait until the element is free\n/// to be operated on. If it is `false` then a message will be sent to the\n/// nodegroup to try invoking the element again.\n///\n/// This is a threaded action intended to be run on the DG nodegroup.\ntemplate <bool Block>\nstruct PerformAlgorithmOnElement {\n  /// \\brief Invoke `perform_algorithm()` on a single element\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DistributedObject, size_t Dim>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const DistributedObject* /*distributed_object*/,\n                    const ElementId<Dim>& element_to_execute_on) {\n    const size_t my_node = Parallel::my_node<size_t>(cache);\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    auto& element_collection = db::get_mutable_reference<\n        typename ParallelComponent::element_collection_tag>(\n        make_not_null(&box));\n    auto& element = element_collection.at(element_to_execute_on);\n    std::unique_lock element_lock(element.element_lock(), std::defer_lock);\n    if constexpr (Block) {\n      element_lock.lock();\n      element.perform_algorithm();\n    } else {\n      if (element_lock.try_lock()) {\n        element.perform_algorithm();\n      } else {\n        Parallel::threaded_action<\n            Parallel::Actions::PerformAlgorithmOnElement<Block>>(\n            my_proxy[my_node], element_to_execute_on);\n      }\n    }\n  }\n\n  /// \\brief Invoke `perform_algorithm()` on all elements\n  ///\n  /// \\note This loops over all elements and spawns a message for each element.\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DistributedObject, size_t Dim>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const DistributedObject* /*distributed_object*/) {\n    const size_t my_node = Parallel::my_node<size_t>(cache);\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    auto& element_collection = db::get_mutable_reference<\n        typename ParallelComponent::element_collection_tag>(\n        make_not_null(&box));\n    for (const auto& [element_id, element] : element_collection) {\n      Parallel::threaded_action<\n          Parallel::Actions::PerformAlgorithmOnElement<Block>>(\n          my_proxy[my_node], element_id);\n    }\n  }\n};\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/ReceiveDataForElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel::Actions {\n/// \\brief Receive data for a specific element on the nodegroup.\n///\n/// If `StartPhase` is `true` then `start_phase(phase)` is called on the\n/// `element_to_execute_on`, otherwise `perform_algorithm()` is called on the\n/// `element_to_execute_on`.\ntemplate <bool StartPhase = false>\nstruct ReceiveDataForElement {\n  /// \\brief Entry method called when receiving data from another node.\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, typename ReceiveData,\n            typename ReceiveTag, size_t Dim, typename DistributedObject>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const DistributedObject* /*distributed_object*/,\n                    const ReceiveTag& /*meta*/,\n                    const ElementId<Dim>& element_to_execute_on,\n                    typename ReceiveTag::temporal_id instance,\n                    ReceiveData receive_data) {\n    [[maybe_unused]] const size_t my_node = Parallel::my_node<size_t>(cache);\n    auto& element_collection = db::get_mutable_reference<\n        typename ParallelComponent::element_collection_tag>(\n        make_not_null(&box));\n\n    ASSERT(\n        element_collection.count(element_to_execute_on) == 1,\n        \"ElementId \" << element_to_execute_on << \" is not on node \" << my_node);\n    const bool run_algorithm = ReceiveTag::insert_into_inbox(\n        make_not_null(&tuples::get<ReceiveTag>(\n            element_collection.at(element_to_execute_on).inboxes())),\n        instance, std::move(receive_data));\n    if (run_algorithm) {\n      apply_impl<ParallelComponent>(cache, element_to_execute_on,\n                                    make_not_null(&element_collection));\n    }\n  }\n\n  /// \\brief Entry method call when receiving from same node.\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, size_t Dim,\n            typename DistributedObject>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const DistributedObject* /*distributed_object*/,\n                    const ElementId<Dim>& element_to_execute_on) {\n    auto& element_collection = db::get_mutable_reference<\n        typename ParallelComponent::element_collection_tag>(\n        make_not_null(&box));\n    apply_impl<ParallelComponent>(cache, element_to_execute_on,\n                                  make_not_null(&element_collection));\n  }\n\n private:\n  template <typename ParallelComponent, typename Metavariables,\n            typename ElementCollection, size_t Dim>\n  static void apply_impl(\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_to_execute_on,\n      const gsl::not_null<ElementCollection*> element_collection) {\n    if constexpr (StartPhase) {\n      const Phase current_phase =\n          Parallel::local_branch(\n              Parallel::get_parallel_component<ParallelComponent>(cache))\n              ->phase();\n      ASSERT(element_collection->count(element_to_execute_on) == 1,\n             \"ElementId \" << element_to_execute_on << \" is not on node \"\n                          << Parallel::my_node<size_t>(cache););\n      auto& element = element_collection->at(element_to_execute_on);\n      const std::lock_guard element_lock(element.element_lock());\n      // We always force the phase to start because if a previous phase\n      // terminated properly, then this does nothing. But if a phase didn't\n      // terminate properly, then it likely went into deadlock analysis or\n      // post-failure cleanup, in which case we want to run code after a\n      // failure, so we must force the phase to start.\n      element.start_phase(current_phase, true);\n    } else {\n      auto& element = element_collection->at(element_to_execute_on);\n      std::unique_lock element_lock(element.element_lock(), std::defer_lock);\n      if (element_lock.try_lock()) {\n        element.perform_algorithm();\n      } else {\n        const size_t my_node = Parallel::my_node<size_t>(cache);\n        auto& my_proxy =\n            Parallel::get_parallel_component<ParallelComponent>(cache);\n        Parallel::threaded_action<Parallel::Actions::ReceiveDataForElement<>>(\n            my_proxy[my_node], element_to_execute_on);\n      }\n    }\n  }\n};\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/SendDataToElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/AtomicInboxBoundaryData.hpp\"\n#include \"Parallel/ArrayCollection/ReceiveDataForElement.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel::Actions {\n/*!\n * \\brief A local synchronous action where data is communicated to neighbor\n * elements.\n *\n * If the inbox tag type is an `evolution::dg::AtomicInboxBoundaryData` then\n * remote insert for elements on the same node is done in a lock-free manner\n * between the sender and receiver elements, and in a wait-free manner between\n * different sender elements to the same receiver element.\n *\n * The number of messages needed to take the next time step on the receiver\n * element is kept track of and a message is sent to the parallel runtime\n * system (e.g. Charm++) only when the receiver/neighbor element has all the\n * data it needs to take the next time step. This is done so as to reduce\n * pressure on the runtime system by sending fewer messages.\n */\nstruct SendDataToElement {\n  using return_type = void;\n\n  template <typename ParallelComponent, typename DbTagList, size_t Dim,\n            typename ReceiveTag, typename ReceiveData, typename Metavariables>\n  static return_type apply(\n      db::DataBox<DbTagList>& box,\n      const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n      const gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,\n      const ReceiveTag& /*meta*/, const ElementId<Dim>& element_to_execute_on,\n      typename ReceiveTag::temporal_id instance, ReceiveData&& receive_data) {\n    const size_t my_node = Parallel::my_node<size_t>(*cache);\n    // While we don't mutate the value, we want to avoid locking the DataBox\n    // and the nodegroup by using `db::get_mutable_reference`. If/when we\n    // start dynamically inserting and removing elements, we'll need to update\n    // how we handle this. For example, we might need the containers to have\n    // strong stability guarantees.\n    ASSERT(db::get_mutable_reference<Parallel::Tags::ElementLocations<Dim>>(\n               make_not_null(&box))\n                   .count(element_to_execute_on) == 1,\n           \"Could not find ElementId \" << element_to_execute_on\n                                       << \" in the list of element locations\");\n    const size_t node_of_element =\n        db::get_mutable_reference<Parallel::Tags::ElementLocations<Dim>>(\n            make_not_null(&box))\n            .at(element_to_execute_on);\n    auto& my_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(*cache);\n    if (node_of_element == my_node) {\n      bool run_algorithm = false;\n      ASSERT(db::get_mutable_reference<\n                 typename ParallelComponent::element_collection_tag>(\n                 make_not_null(&box))\n                     .count(element_to_execute_on) == 1,\n             \"The element with ID \"\n                 << element_to_execute_on << \" is not on node \" << my_node\n                 << \". We should be sending data to node \" << node_of_element);\n      auto& element = db::get_mutable_reference<\n                          typename ParallelComponent::element_collection_tag>(\n                          make_not_null(&box))\n                          .at(element_to_execute_on);\n      if constexpr (std::is_same_v<evolution::dg::AtomicInboxBoundaryData<Dim>,\n                                   typename ReceiveTag::type>) {\n        run_algorithm = ReceiveTag::insert_into_inbox(\n            make_not_null(&tuples::get<ReceiveTag>(element.inboxes())),\n            instance, std::forward<ReceiveData>(receive_data));\n      } else {\n        // Scope so that we minimize how long we lock the inbox.\n        std::lock_guard inbox_lock(element.inbox_lock());\n        run_algorithm = ReceiveTag::insert_into_inbox(\n            make_not_null(&tuples::get<ReceiveTag>(element.inboxes())),\n            instance, std::forward<ReceiveData>(receive_data));\n      }\n      if (run_algorithm) {\n        Parallel::threaded_action<Parallel::Actions::ReceiveDataForElement<>>(\n            my_proxy[node_of_element], element_to_execute_on);\n      }\n    } else {\n      Parallel::threaded_action<Parallel::Actions::ReceiveDataForElement<>>(\n          my_proxy[node_of_element], ReceiveTag{}, element_to_execute_on,\n          instance, std::forward<ReceiveData>(receive_data));\n    }\n  }\n};\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/SetTerminateOnElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n#include <stdexcept>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/ArrayCollection/Tags/NumberOfElementsTerminated.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel::Actions {\n/// \\brief Set the element with ID `my_element_id` to be terminated. This is\n/// always invoked on the local component via\n/// `Parallel::local_synchronous_action`\n///\n/// In the future we can use this to try and elide Charm++ calls when the\n/// receiver is local.\nstruct SetTerminateOnElement {\n  using return_type = void;\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, size_t Dim>\n  static return_type apply(\n      db::DataBox<DbTagList>& box, gsl::not_null<Parallel::NodeLock*> node_lock,\n      gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,\n      const ElementId<Dim>& my_element_id, bool terminate_value);\n};\n\ntemplate <typename ParallelComponent, typename DbTagList,\n          typename Metavariables, size_t Dim>\nauto SetTerminateOnElement::apply(\n    db::DataBox<DbTagList>& box,\n    const gsl::not_null<Parallel::NodeLock*> node_lock,\n    const gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,\n    const ElementId<Dim>& my_element_id, const bool terminate_value)\n    -> return_type {\n  std::lock_guard node_guard(*node_lock);\n  db::mutate<typename ParallelComponent::element_collection_tag,\n             Parallel::Tags::NumberOfElementsTerminated>(\n      [&cache, &my_element_id, &node_lock, terminate_value](\n          const auto element_collection_ptr,\n          const auto num_terminated_ptr) -> void {\n        try {\n          // Note: The nodelock is checked by set_terminate as a sanity\n          // check and as a way of making set_terminate difficult to use\n          // without this action.\n          //\n          // Note: since this is a local synchronous action running on the\n          // element that called it, we already have the element locked and so\n          // setting terminate is threadsafe.\n          //\n          // Note: the element's set_terminate updates num_terminated\n          // correctly so we don't need to handle that here.\n          element_collection_ptr->at(my_element_id)\n              .set_terminate(num_terminated_ptr, node_lock, terminate_value);\n\n          // Update the state of the nodegroup. Specifically, if _all_\n          // elements are set to be terminated, we terminate the nodegroup,\n          // otherwise we do not.\n          auto* local_branch = Parallel::local_branch(\n              Parallel::get_parallel_component<ParallelComponent>(*cache));\n          ASSERT(local_branch != nullptr,\n                 \"The local branch is nullptr, which is inconsistent\");\n          local_branch->set_terminate(*num_terminated_ptr ==\n                                      element_collection_ptr->size());\n        } catch (const std::out_of_range&) {\n          ERROR(\"Could not find element with ID \" << my_element_id\n                                                  << \" on node\");\n        }\n      },\n      make_not_null(&box));\n}\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/SimpleActionOnElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <mutex>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel::Actions {\n/// \\brief A threaded action that calls the simple action `SimpleActionToCall`\n/// on either the specified element or broadcasts to the nodegroup.\n///\n/// If `Block` is `true` then the action will wait until the element is free\n/// to be operated on. If it is `false` then a message will be sent to the\n/// nodegroup to try invoking the element again.\n///\n/// This is a threaded action intended to be run on the DG nodegroup.\ntemplate <typename SimpleActionToCall, bool Block>\nstruct SimpleActionOnElement {\n  /// \\brief Invoke the simple action on a single element\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DistributedObject, size_t Dim, typename... Args>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const DistributedObject* /*distributed_object*/,\n                    const ElementId<Dim>& element_to_execute_on,\n                    Args&&... args) {\n    const size_t my_node = Parallel::my_node<size_t>(cache);\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    auto& element_collection = db::get_mutable_reference<\n        typename ParallelComponent::element_collection_tag>(\n        make_not_null(&box));\n    auto& element = element_collection.at(element_to_execute_on);\n    std::unique_lock element_lock(element.element_lock(), std::defer_lock);\n    if constexpr (Block) {\n      element_lock.lock();\n      element.template simple_action<SimpleActionToCall>(\n          std::forward<Args>(args)...);\n    } else {\n      if (element_lock.try_lock()) {\n        element.template simple_action<SimpleActionToCall>(\n            std::forward<Args>(args)...);\n      } else {\n        Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n            SimpleActionToCall, Block>>(my_proxy[my_node],\n                                        element_to_execute_on,\n                                        std::forward<Args>(args)...);\n      }\n    }\n  }\n\n  /// \\brief Invoke the simple action on all elements\n  ///\n  /// \\note This loops over all elements and spawns a message for each element.\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DistributedObject, typename... Args>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const DistributedObject* /*distributed_object*/,\n                    const Args&... args) {\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    auto& element_locations = db::get_mutable_reference<\n        Parallel::Tags::ElementLocations<Metavariables::volume_dim>>(\n        make_not_null(&box));\n    for (const auto& [element_id, node_id] : element_locations) {\n      if (node_id == Parallel::my_node<size_t>(cache)) {\n        Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n            SimpleActionToCall, Block>>(my_proxy[node_id], element_id, args...);\n      }\n    }\n  }\n};\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/SpawnInitializeElementsInCollection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/ReceiveDataForElement.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n\nnamespace Parallel::Actions {\n/*!\n * \\brief Reduction target that is called after all nodes have successfully\n * initialized the nodegroup portion of the `DgElementCollection`. This spawns\n * the messages that initialize the `DgElementArrayMember`s\n */\nstruct SpawnInitializeElementsInCollection {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const double /*unused_but_we_needed_to_reduce_something*/) {\n    auto my_proxy = Parallel::get_parallel_component<ParallelComponent>(\n        cache)[Parallel::my_node<size_t>(cache)];\n    db::mutate<typename ParallelComponent::element_collection_tag>(\n        [&my_proxy](const auto element_collection_ptr) {\n          for (auto& [element_id, element] : *element_collection_ptr) {\n            Parallel::threaded_action<ReceiveDataForElement<true>>(my_proxy,\n                                                                   element_id);\n          }\n        },\n        make_not_null(&box));\n  }\n};\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/StartPhaseOnNodegroup.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/ReceiveDataForElement.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n\nnamespace Parallel::Actions {\n/// \\brief Starts the next phase on the nodegroup and calls\n/// `ReceiveDataForElement` for each element on the node.\nstruct StartPhaseOnNodegroup {\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t my_node = Parallel::my_node<size_t>(cache);\n    auto proxy_to_this_node =\n        Parallel::get_parallel_component<ParallelComponent>(cache)[my_node];\n    for (const auto& [element_id, element] :\n         db::get<typename ParallelComponent::element_collection_tag>(box)) {\n      Parallel::threaded_action<ReceiveDataForElement<true>>(proxy_to_this_node,\n                                                             element_id);\n    }\n    return {Parallel::AlgorithmExecution::Halt, std::nullopt};\n  }\n};\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ElementCollection.hpp\n  ElementLocations.hpp\n  ElementLocationsReference.hpp\n  NumberOfElementsTerminated.hpp\n  )\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/Tags/ElementCollection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass ElementId;\nnamespace Parallel {\ntemplate <size_t Dim, typename Metavariables, typename PhaseDepActionList,\n          typename SimpleTagsFromOptions>\nclass DgElementArrayMember;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Parallel::Tags {\n/// \\brief Holds all of the `DgElementArrayMember` on a node.\n///\n/// This tag must be in the nodegroup DataBox.\ntemplate <size_t Dim, class Metavariables, class PhaseDepActionList,\n          typename SimpleTagsFromOptions>\nstruct ElementCollection : db::SimpleTag {\n  using type = std::unordered_map<\n      ElementId<Dim>,\n      Parallel::DgElementArrayMember<Dim, Metavariables, PhaseDepActionList,\n                                     SimpleTagsFromOptions>>;\n};\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/Tags/ElementLocations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass ElementId;\n/// \\endcond\n\nnamespace Parallel::Tags {\n/// \\brief The node (location) where different elements are.\n///\n/// This should be in the nodegroup's DataBox.\ntemplate <size_t Dim>\nstruct ElementLocations : db::SimpleTag {\n  using type = std::unordered_map<ElementId<Dim>, size_t>;\n};\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/Tags/ElementLocationsReference.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass ElementId;\nnamespace Parallel {\nclass NodeLock;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Parallel::Tags {\n/// \\brief The node (location) where different elements are.\n///\n/// This should be in the DgElementArrayMember's DataBox.\n///\n/// Implementation note: This should point to the ElementLocations located in\n/// the nodegroup's DataBox.\ntemplate <size_t Dim, typename Metavariables,\n          typename DgElementCollectionComponent>\nstruct ElementLocationsReference : ElementLocations<Dim>, db::ReferenceTag {\n private:\n  struct GetReference {\n    using return_type = const typename ElementLocations<Dim>::type&;\n\n    template <typename ParallelComponent, typename DbTagList>\n    static return_type apply(\n        db::DataBox<DbTagList>& box,\n        const gsl::not_null<Parallel::NodeLock*> /*node_lock*/) {\n      return db::get_mutable_reference<ElementLocations<Dim>>(\n          make_not_null(&box));\n    }\n  };\n\n public:\n  using base = ElementLocations<Dim>;\n  using type = typename base::type;\n  using argument_tags = tmpl::list<Parallel::Tags::GlobalCache<Metavariables>>;\n\n  static const type& get(\n      const Parallel::GlobalCache<Metavariables>* const& cache) {\n    auto& parallel_comp =\n        Parallel::get_parallel_component<DgElementCollectionComponent>(*cache);\n    return Parallel::local_synchronous_action<GetReference>(parallel_comp);\n  }\n};\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/Tags/NumberOfElementsTerminated.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace Parallel::Tags {\n/// \\brief A counter of the number of elements on the node that have marked\n/// themselves as having terminated their current phase.\nstruct NumberOfElementsTerminated : db::SimpleTag {\n  using type = size_t;\n};\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/Parallel/ArrayCollection/TransformPdalForNodegroup.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Parallel/ArrayCollection/StartPhaseOnNodegroup.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\nnamespace detail {\ntemplate <typename OnePhaseActions>\nstruct TransformPdalForNodegroup {\n  using type = tmpl::conditional_t<\n      OnePhaseActions::phase == Parallel::Phase::Initialization, tmpl::list<>,\n      Parallel::PhaseActions<OnePhaseActions::phase,\n                             tmpl::list<Actions::StartPhaseOnNodegroup>>>;\n};\n}  // namespace detail\n\n/// \\brief Transforms the `PhaseDepActionList` (phase dependent action\n/// list/PDAL) from one used for a `evolution::DgElementArray` to that for\n/// `Parallel::DgElementCollection`\ntemplate <typename PhaseDepActionList>\nusing TransformPhaseDependentActionListForNodegroup =\n    tmpl::flatten<tmpl::transform<PhaseDepActionList,\n                                  detail::TransformPdalForNodegroup<tmpl::_1>>>;\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayComponentId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/ArrayComponentId.hpp\"\n\n#include <pup.h>\n\nnamespace Parallel {\nvoid ArrayComponentId::pup(PUP::er& p) {\n  p | component_id_;\n  p | array_index_;\n}\n\nbool operator==(const ArrayComponentId& lhs, const ArrayComponentId& rhs) {\n  return lhs.component_id() == rhs.component_id() and\n         lhs.array_index() == rhs.array_index();\n}\n\nbool operator!=(const ArrayComponentId& lhs, const ArrayComponentId& rhs) {\n  return not(lhs == rhs);\n}\n\nstd::ostream& operator<<(std::ostream& os,\n                         const ArrayComponentId& array_component_id) {\n  return os << std::hash<ArrayComponentId>{}(array_component_id);\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ArrayComponentId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/functional/hash.hpp>\n#include <ckarrayindex.h>\n#include <cstddef>\n#include <functional>\n#include <string>\n#include <type_traits>\n\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace Parallel {\n/*!\n * \\brief An ID type that identifies both the parallel component and the index\n * in the parallel component.\n *\n * A specialization of `std::hash` is provided to allow using `ArrayComponentId`\n * as a key in associative containers.\n */\nclass ArrayComponentId {\n public:\n  ArrayComponentId() = default;  // Needed for Charm++ serialization\n\n  template <typename ParallelComponent>\n  ArrayComponentId(const ParallelComponent* /*meta*/,\n                   const CkArrayIndex& index);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  size_t component_id() const { return component_id_; }\n\n  const CkArrayIndex& array_index() const { return array_index_; }\n\n private:\n  size_t component_id_{0};\n  CkArrayIndex array_index_{};\n};\n\ntemplate <typename ParallelComponent>\nArrayComponentId::ArrayComponentId(const ParallelComponent* const /*meta*/,\n                                   const CkArrayIndex& index)\n    : component_id_(\n          std::hash<std::string>{}(pretty_type::get_name<ParallelComponent>())),\n      array_index_(index) {}\n\nbool operator==(const ArrayComponentId& lhs, const ArrayComponentId& rhs);\n\nbool operator!=(const ArrayComponentId& lhs, const ArrayComponentId& rhs);\n\nstd::ostream& operator<<(std::ostream& os,\n                         const ArrayComponentId& array_component_id);\n\n/*!\n * \\brief A convenience function that will make an `ArrayComponentId` from the\n * templated `ParallelComponent` and the passed in `array_index`.\n */\ntemplate <typename ParallelComponent, typename ArrayIndexType>\nArrayComponentId make_array_component_id(const ArrayIndexType& array_index) {\n  return ArrayComponentId{std::add_pointer_t<ParallelComponent>{nullptr},\n                          Parallel::ArrayIndex<ArrayIndexType>(array_index)};\n}\n}  // namespace Parallel\n\nnamespace std {\ntemplate <>\nstruct hash<Parallel::ArrayComponentId> {\n  size_t operator()(const Parallel::ArrayComponentId& t) const {\n    size_t result = t.component_id();\n    boost::hash_combine(result, t.array_index().hash());\n    return result;\n  }\n};\n}  // namespace std\n"
  },
  {
    "path": "src/Parallel/ArrayIndex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <type_traits>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace Parallel {\n/*!\n * \\ingroup ParallelGroup\n * \\brief The array index used for indexing Chare Arrays, mostly an\n * implementation detail\n *\n * The implementation is generic and can handle custom array indices. This\n * replaces the generated, hard-coded Charm++ array indices with a template,\n * allowing a single implementation to be used for different array indices.\n *\n * \\details Charm++ allocates memory for `CkArrayIndex`. The size can be\n * configured (in the Charm++ configuration) and defaults to the size of three\n * integers. We place the `Index` into this buffer using placement `new`. Then,\n * `CkArrayIndex::data()` can be safely reinterpreted as an `Index*`.\n */\ntemplate <class Index>\nstruct ArrayIndex : public CkArrayIndex {\n  static_assert(std::is_standard_layout_v<Index> and std::is_trivial_v<Index>,\n                \"The array index type must be a POD, plain-old-data\");\n  // clang-tidy: suspicious use of sizeof\n  static_assert(sizeof(Index) <= 3 * sizeof(int),  // NOLINT\n                \"The default Charm++ CK_ARRAYINDEX_MAXLEN is 3. If you have \"\n                \"changed this at Charm++ configuration time then please update \"\n                \"the static_assert, otherwise your Index type is too large.\");\n  // clang-tidy: suspicious use of sizeof\n  static_assert(sizeof(Index) % sizeof(int) == 0,  // NOLINT\n                \"The Charm++ array Index type must be exactly a multiple of \"\n                \"the size of an integer, but the user-provided one is not.\");\n  static_assert(\n      alignof(Index) == alignof(decltype(index)),\n      \"Incorrect alignment of Charm++ array Index type. The \"\n      \"alignment must match the alignment of the internal Charm++ type\");\n  static_assert(not tt::is_a_v<ArrayIndex, Index>,\n                \"The Index type passed to ArrayIndex cannot be an ArrayIndex\");\n\n  // Use placement new to ensure that the custom index object is placed in the\n  // memory reserved for it in the base class\n  // clang-tidy: mark explicit: it's a conversion constructor\n  ArrayIndex(const Index& array_index)  // NOLINT\n                                        // clang-tidy: do not use unions\n      : array_index_(new (index) Index(array_index)) {  // NOLINT\n    // clang-tidy: suspicious use of sizeof\n    nInts = sizeof(array_index) / sizeof(int);  // NOLINT\n  }\n\n  // clang-tidy: mark explicit: it's a conversion constructor\n  ArrayIndex(const CkArrayIndex& array_index)  // NOLINT\n      : CkArrayIndex(array_index),\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n        array_index_(reinterpret_cast<Index*>(CkArrayIndex::data())) {\n    ASSERT(CkArrayIndex::nInts * sizeof(int) == sizeof(Index),\n           \"The CkArrayIndex::nInts does not match the size of the custom \"\n           \"array index class.\");\n  }\n\n  ArrayIndex(const ArrayIndex& rhs) = delete;\n  ArrayIndex& operator=(const ArrayIndex& rhs) = delete;\n\n  ArrayIndex(ArrayIndex&& /*rhs*/) = delete;\n  ArrayIndex& operator=(ArrayIndex&& /*rhs*/) = delete;\n  ~ArrayIndex() = default;\n\n  const Index& get_index() const { return *array_index_; }\n\n private:\n  Index* array_index_ = nullptr;\n};\n\nusing ArrayIndex1D = ArrayIndex<CkIndex1D>;\nusing ArrayIndex2D = ArrayIndex<CkIndex2D>;\nusing ArrayIndex3D = ArrayIndex<CkIndex3D>;\nusing ArrayIndex4D = ArrayIndex<CkIndex4D>;\nusing ArrayIndex5D = ArrayIndex<CkIndex5D>;\nusing ArrayIndex6D = ArrayIndex<CkIndex6D>;\n}  // namespace Parallel\n\n// These namespaces have silly names because we are subverting the charm++\n// utilities that prepend \"CkArrayIndex\" to the name of the array index. See\n// comments in Parallel/Algorithms/AlgorithmArray.ci for further explanation.\n\nnamespace CkArrayIndexSpectreIndex_detail {\ntemplate <typename Index>\nusing ArrayIndex = ::Parallel::ArrayIndex<Index>;\n}  // namespace CkArrayIndexSpectreIndex_detail\n\nnamespace SpectreIndex_detail {\ntemplate <typename Index>\nusing ArrayIndex = Index;\n}  // namespace SpectreIndex_detail\n"
  },
  {
    "path": "src/Parallel/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_charm_module(GlobalCache)\nadd_charm_module(Main)\n\nset(LIBRARY Parallel)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ArrayComponentId.cpp\n  CharmRegistration.cpp\n  ExitCode.cpp\n  InitializationFunctions.cpp\n  Main.cpp\n  NodeLock.cpp\n  Phase.cpp\n  Reduction.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AlgorithmExecution.hpp\n  AlgorithmMetafunctions.hpp\n  ArrayComponentId.hpp\n  ArrayIndex.hpp\n  Callback.hpp\n  CharmMain.tpp\n  CharmRegistration.hpp\n  CreateFromOptions.hpp\n  DistributedObject.hpp\n  DomainDiagnosticInfo.hpp\n  ElementRegistration.hpp\n  ExitCode.hpp\n  FifoCache.hpp\n  GetSection.hpp\n  GlobalCache.hpp\n  GlobalCacheDeclare.hpp\n  InboxInserters.hpp\n  Info.hpp\n  InitializationFunctions.hpp\n  InitializationTag.hpp\n  Invoke.hpp\n  Local.hpp\n  Main.hpp\n  MaxInlineMethodsReached.hpp\n  MultiReaderSpinlock.hpp\n  NodeLock.hpp\n  OutputInbox.hpp\n  ParallelComponentHelpers.hpp\n  Phase.hpp\n  PhaseControlReductionHelpers.hpp\n  PhaseDependentActionList.hpp\n  Reduction.hpp\n  ReductionDeclare.hpp\n  ResourceInfo.hpp\n  Section.hpp\n  Spinlock.hpp\n  StaticSpscQueue.hpp\n  TypeTraits.hpp\n  )\n\n# Depends only on relatively low-level libraries so that compiling small\n# executables (and low-level unit tests) doesn't require building the entire\n# code base. E.g. doesn't depend on Domain.\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Boost::boost\n  Boost::program_options\n  DataStructures\n  Informer\n  PUBLIC\n  Charmxx::charmxx\n  ErrorHandling\n  Options\n  Printf\n  Serialization\n  SystemUtilities\n  Utilities\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_GlobalCache\n  module_Main          # GlobalCache module depends on Main module\n  )\n\nadd_subdirectory(Algorithms)\nadd_subdirectory(ArrayCollection)\nadd_subdirectory(MemoryMonitor)\nadd_subdirectory(PhaseControl)\nadd_subdirectory(Printf)\nadd_subdirectory(Protocols)\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/Parallel/Callback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines Parallel::Callback.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <utility>\n\n#include \"Parallel/Invoke.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TypeTraits/HasEquivalence.hpp\"\n\nnamespace Parallel {\nnamespace detail {\n// Not all tuple arguments are guaranteed to have operator==, so we check the\n// ones we can.\ntemplate <typename... Args>\nbool tuple_equal(const std::tuple<Args...>& tuple_1,\n                 const std::tuple<Args...>& tuple_2) {\n  bool result = true;\n  tmpl::for_each<tmpl::make_sequence<tmpl::size_t<0>,\n                                     tmpl::size<tmpl::list<Args...>>::value>>(\n      [&](const auto index_v) {\n        constexpr size_t index = tmpl::type_from<decltype(index_v)>::value;\n\n        if (not result) {\n          return;\n        }\n\n        if constexpr (tt::has_equivalence_v<decltype(std::get<index>(\n                          tuple_1))>) {\n          result =\n              result and std::get<index>(tuple_1) == std::get<index>(tuple_2);\n        }\n      });\n\n  return result;\n}\n}  // namespace detail\n\n/// An abstract base class, whose derived class holds a function that\n/// can be invoked at a later time.  The function is intended to be\n/// invoked only once.\nclass Callback : public PUP::able {\n public:\n  WRAPPED_PUPable_abstract(Callback);  // NOLINT\n  Callback() = default;\n  Callback(const Callback&) = default;\n  Callback& operator=(const Callback&) = default;\n  Callback(Callback&&) = default;\n  Callback& operator=(Callback&&) = default;\n  ~Callback() override = default;\n  explicit Callback(CkMigrateMessage* msg) : PUP::able(msg) {}\n  virtual void invoke() = 0;\n  virtual void register_with_charm() = 0;\n  /*!\n   * \\brief Returns if this callback is equal to the one passed in.\n   */\n  virtual bool is_equal_to(const Callback& rhs) const = 0;\n  virtual std::string name() const = 0;\n  virtual std::unique_ptr<Callback> get_clone() = 0;\n};\n\n/// Wraps a call to a simple action and its arguments.\n/// Can be invoked only once.\ntemplate <typename SimpleAction, typename Proxy, typename... Args>\nclass SimpleActionCallback : public Callback {\n public:\n  WRAPPED_PUPable_decl_template(SimpleActionCallback);  // NOLINT\n  SimpleActionCallback() = default;\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  SimpleActionCallback(Proxy proxy, std::decay_t<Args>... args)\n      : proxy_(proxy), args_(std::move(args)...) {}\n  explicit SimpleActionCallback(CkMigrateMessage* msg) : Callback(msg) {}\n  using PUP::able::register_constructor;\n  void invoke() override {\n    std::apply(\n        [this](auto&&... args) {\n          Parallel::simple_action<SimpleAction>(proxy_, args...);\n        },\n        std::move(args_));\n  }\n  void pup(PUP::er& p) override {\n    p | proxy_;\n    p | args_;\n  }\n\n  void register_with_charm() override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;\n    }\n    done_registration = true;\n    register_classes_with_charm<SimpleActionCallback>();\n  }\n\n  bool is_equal_to(const Callback& rhs) const override {\n    const auto* downcast_ptr = dynamic_cast<const SimpleActionCallback*>(&rhs);\n    if (downcast_ptr == nullptr) {\n      return false;\n    }\n    return detail::tuple_equal(args_, downcast_ptr->args_);\n  }\n\n  std::string name() const override {\n    // Use pretty_type::get_name with the action since we want to differentiate\n    // template paremeters. Only use pretty_type::name for proxy because it'll\n    // likely be really long with the template parameters which is unnecessary\n    return \"SimpleActionCallback(\" + pretty_type::get_name<SimpleAction>() +\n           \",\" + pretty_type::name<Proxy>() + \")\";\n  }\n\n  std::unique_ptr<Callback> get_clone() override {\n    return std::make_unique<SimpleActionCallback<SimpleAction, Proxy, Args...>>(\n        *this);\n  }\n\n private:\n  std::decay_t<Proxy> proxy_{};\n  std::tuple<std::decay_t<Args>...> args_{};\n};\n\n/// Wraps a call to a simple action without arguments.\ntemplate <typename SimpleAction, typename Proxy>\nclass SimpleActionCallback<SimpleAction, Proxy> : public Callback {\n public:\n  WRAPPED_PUPable_decl_template(SimpleActionCallback);  // NOLINT\n  SimpleActionCallback() = default;\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  SimpleActionCallback(Proxy proxy) : proxy_(proxy) {}\n  explicit SimpleActionCallback(CkMigrateMessage* msg) : Callback(msg) {}\n  using PUP::able::register_constructor;\n  void invoke() override { Parallel::simple_action<SimpleAction>(proxy_); }\n\n  void pup(PUP::er& p) override { p | proxy_; }\n\n  void register_with_charm() override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;\n    }\n    done_registration = true;\n    register_classes_with_charm<SimpleActionCallback>();\n  }\n\n  bool is_equal_to(const Callback& rhs) const override {\n    const auto* downcast_ptr = dynamic_cast<const SimpleActionCallback*>(&rhs);\n    return downcast_ptr != nullptr;\n  }\n\n  std::string name() const override {\n    // Use pretty_type::get_name with the action since we want to differentiate\n    // template paremeters. Only use pretty_type::name for proxy because it'll\n    // likely be really long with the template parameters which is unnecessary\n    return \"SimpleActionCallback(\" + pretty_type::get_name<SimpleAction>() +\n           \",\" + pretty_type::name<Proxy>() + \")\";\n  }\n\n  std::unique_ptr<Callback> get_clone() override {\n    return std::make_unique<SimpleActionCallback<SimpleAction, Proxy>>(*this);\n  }\n\n private:\n  std::decay_t<Proxy> proxy_{};\n};\n\n/// Wraps a call to a threaded action and its arguments.\n/// Can be invoked only once.\ntemplate <typename ThreadedAction, typename Proxy, typename... Args>\nclass ThreadedActionCallback : public Callback {\n public:\n  WRAPPED_PUPable_decl_template(ThreadedActionCallback);  // NOLINT\n  ThreadedActionCallback() = default;\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  ThreadedActionCallback(Proxy proxy, std::decay_t<Args>... args)\n      : proxy_(proxy), args_(std::move(args)...) {}\n  explicit ThreadedActionCallback(CkMigrateMessage* msg) : Callback(msg) {}\n  using PUP::able::register_constructor;\n  void invoke() override {\n    std::apply(\n        [this](auto&&... args) {\n          Parallel::threaded_action<ThreadedAction>(proxy_, args...);\n        },\n        std::move(args_));\n  }\n  void pup(PUP::er& p) override {\n    p | proxy_;\n    p | args_;\n  }\n\n  void register_with_charm() override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;\n    }\n    done_registration = true;\n    register_classes_with_charm<ThreadedActionCallback>();\n  }\n\n  bool is_equal_to(const Callback& rhs) const override {\n    const auto* downcast_ptr =\n        dynamic_cast<const ThreadedActionCallback*>(&rhs);\n    if (downcast_ptr == nullptr) {\n      return false;\n    }\n    return detail::tuple_equal(args_, downcast_ptr->args_);\n  }\n\n  std::string name() const override {\n    // Use pretty_type::get_name with the action since we want to differentiate\n    // template paremeters. Only use pretty_type::name for proxy because it'll\n    // likely be really long with the template parameters which is unnecessary\n    return \"ThreadedActionCallback(\" + pretty_type::get_name<ThreadedAction>() +\n           \",\" + pretty_type::name<Proxy>() + \")\";\n  }\n\n  std::unique_ptr<Callback> get_clone() override {\n    return std::make_unique<\n        ThreadedActionCallback<ThreadedAction, Proxy, Args...>>(*this);\n  }\n\n private:\n  std::decay_t<Proxy> proxy_{};\n  std::tuple<std::decay_t<Args>...> args_{};\n};\n\n/// Wraps a call to a threaded action without arguments.\ntemplate <typename ThreadedAction, typename Proxy>\nclass ThreadedActionCallback<ThreadedAction, Proxy> : public Callback {\n public:\n  WRAPPED_PUPable_decl_template(ThreadedActionCallback);  // NOLINT\n  ThreadedActionCallback() = default;\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  ThreadedActionCallback(Proxy proxy) : proxy_(proxy) {}\n  explicit ThreadedActionCallback(CkMigrateMessage* msg) : Callback(msg) {}\n  using PUP::able::register_constructor;\n  void invoke() override { Parallel::threaded_action<ThreadedAction>(proxy_); }\n\n  void pup(PUP::er& p) override { p | proxy_; }\n\n  void register_with_charm() override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;\n    }\n    done_registration = true;\n    register_classes_with_charm<ThreadedActionCallback>();\n  }\n\n  bool is_equal_to(const Callback& rhs) const override {\n    const auto* downcast_ptr =\n        dynamic_cast<const ThreadedActionCallback*>(&rhs);\n    return downcast_ptr != nullptr;\n  }\n\n  std::string name() const override {\n    // Use pretty_type::get_name with the action since we want to differentiate\n    // template paremeters. Only use pretty_type::name for proxy because it'll\n    // likely be really long with the template parameters which is unnecessary\n    return \"ThreadedActionCallback(\" + pretty_type::get_name<ThreadedAction>() +\n           \",\" + pretty_type::name<Proxy>() + \")\";\n  }\n\n  std::unique_ptr<Callback> get_clone() override {\n    return std::make_unique<ThreadedActionCallback<ThreadedAction, Proxy>>(\n        *this);\n  }\n\n private:\n  std::decay_t<Proxy> proxy_{};\n};\n\n/// Wraps a call to perform_algorithm.\ntemplate <typename Proxy>\nclass PerformAlgorithmCallback : public Callback {\n public:\n  WRAPPED_PUPable_decl_template(PerformAlgorithmCallback);  // NOLINT\n  PerformAlgorithmCallback() = default;\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  PerformAlgorithmCallback(Proxy proxy) : proxy_(proxy) {}\n  explicit PerformAlgorithmCallback(CkMigrateMessage* msg) : Callback(msg) {}\n  using PUP::able::register_constructor;\n  void invoke() override { proxy_.perform_algorithm(); }\n  void pup(PUP::er& p) override { p | proxy_; }\n\n  void register_with_charm() override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;\n    }\n    done_registration = true;\n    register_classes_with_charm<PerformAlgorithmCallback>();\n  }\n\n  bool is_equal_to(const Callback& rhs) const override {\n    const auto* downcast_ptr =\n        dynamic_cast<const PerformAlgorithmCallback*>(&rhs);\n    return downcast_ptr != nullptr;\n  }\n\n  std::string name() const override {\n    // Only use pretty_type::name for proxy because it'll likely be really long\n    // with the template parameters which is unnecessary\n    return \"PerformAlgorithmCallback(\" + pretty_type::name<Proxy>() + \")\";\n  }\n\n  std::unique_ptr<Callback> get_clone() override {\n    return std::make_unique<PerformAlgorithmCallback<Proxy>>(*this);\n  }\n\n private:\n  std::decay_t<Proxy> proxy_{};\n};\n\n/// \\cond\ntemplate <typename Proxy>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID PerformAlgorithmCallback<Proxy>::my_PUP_ID = 0;\ntemplate <typename SimpleAction, typename Proxy, typename... Args>\nPUP::able::PUP_ID\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    SimpleActionCallback<SimpleAction, Proxy, Args...>::my_PUP_ID =\n        0;  // NOLINT\ntemplate <typename SimpleAction, typename Proxy>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID SimpleActionCallback<SimpleAction, Proxy>::my_PUP_ID =\n    0;  // NOLINT\ntemplate <typename ThreadedAction, typename Proxy, typename... Args>\nPUP::able::PUP_ID\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    ThreadedActionCallback<ThreadedAction, Proxy, Args...>::my_PUP_ID =\n        0;  // NOLINT\ntemplate <typename ThreadedAction, typename Proxy>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID ThreadedActionCallback<ThreadedAction, Proxy>::my_PUP_ID =\n    0;  // NOLINT\n/// \\endcond\n\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/CharmMain.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions used by Charm++ to register classes/chares and entry\n/// methods\n\n#pragma once\n\n#include <algorithm>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#include \"Parallel/CharmRegistration.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InitializationFunctions.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n\nnamespace Parallel::charmxx {\n/// \\cond\nstd::unique_ptr<RegistrationHelper>* charm_register_list = nullptr;\nsize_t charm_register_list_capacity = 0;\nsize_t charm_register_list_size = 0;\n\nReducerFunctions* charm_reducer_functions_list = nullptr;\nsize_t charm_reducer_functions_capacity = 0;\nsize_t charm_reducer_functions_size = 0;\nstd::unordered_map<size_t, CkReduction::reducerType> charm_reducer_functions{};\n/// \\endcond\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Registers parallel components and their entry methods with Charm++\n */\ninline void register_parallel_components() {\n  static bool done_registration{false};\n  if (done_registration) {\n    return;  // LCOV_EXCL_LINE\n  }\n  done_registration = true;\n\n  Parallel::PrinterChare::register_with_charm();\n\n  // Charm++ requires the order of registration to be the same across all\n  // processors. To make sure this is satisfied regardless of any possible weird\n  // behavior with static variable initialization we sort the list before\n  // registering anything.\n  // clang-tidy: do not user pointer arithmetic\n  std::sort(charm_register_list,\n            charm_register_list + charm_register_list_size,  // NOLINT\n            [](const std::unique_ptr<RegistrationHelper>& a,\n               const std::unique_ptr<RegistrationHelper>& b) {\n              return a->name() < b->name();\n            });\n\n  // register chares and parallel components\n  for (size_t i = 0;\n       i < charm_register_list_size and i < charm_register_list_capacity; ++i) {\n    // clang-tidy: do not use pointer arithmetic\n    if (charm_register_list[i]->is_registering_chare()) {  // NOLINT\n      charm_register_list[i]->register_with_charm();       // NOLINT\n    }\n  }\n  // register reduction and simple actions, as well as receive_data\n  for (size_t i = 0;\n       i < charm_register_list_size and i < charm_register_list_capacity; ++i) {\n    // clang-tidy: do not use pointer arithmetic\n    if (not charm_register_list[i]->is_registering_chare()) {  // NOLINT\n      charm_register_list[i]->register_with_charm();           // NOLINT\n    }\n  }\n  // clang-tidy: use gsl::owner (we don't use raw owning pointers unless\n  // necessary)\n  delete[] charm_register_list;  // NOLINT\n}\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Takes `charm_reducer_functions_list` and creates an unordered_map from\n * hashes of the functions to `CkReduction::reducerType`, which is used when\n * calling custom reductions.\n *\n * \\note The reason `delete[]` isn't call here (or at all on\n * `charm_reducer_functions_list`) is because it has not been tested whether\n * that is safe to do in non-SMP builds.\n *\n * \\see Parallel::contribute_to_reduction()\n */\ninline void register_custom_reducer_functions() {\n  for (size_t i = 0; i < charm_reducer_functions_size; ++i) {\n    // clang-tidy: do not use pointer arithmetic\n    charm_reducer_functions.emplace(\n        std::hash<Parallel::charmxx::ReducerFunctions>{}(\n            charm_reducer_functions_list[i]),                        // NOLINT\n        CkReduction::addReducer(*charm_reducer_functions_list[i]));  // NOLINT\n  }\n}\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Register all init_node and init_proc functions with Charm++\n *\n * The `charm_init_node_funcs` are executed at startup by Charm++ on each node,\n * and the `charm_init_proc_funcs` on each processing element (PE). In addition\n * to these, some default init_node and init_proc functions are registered for\n * things like error handling.\n *\n * \\note The function that registers the custom reducers is the first init_node\n * function registered.\n */\ninline void register_init_node_and_proc(\n    const std::vector<void (*)()>& charm_init_node_funcs,\n    const std::vector<void (*)()>& charm_init_proc_funcs) {\n  static bool done_registration{false};\n  if (done_registration) {\n    return;  // LCOV_EXCL_LINE\n  }\n  done_registration = true;\n  // We explicitly register custom reducer functions first.\n  _registerInitCall(register_custom_reducer_functions, 1);\n  for (const auto& init_node_func :\n       {&setup_error_handling, &setup_memory_allocation_failure_reporting,\n        &disable_openblas_multithreading}) {\n    _registerInitCall(*init_node_func, 1);\n  }\n  for (const auto& init_node_func : charm_init_node_funcs) {\n    _registerInitCall(*init_node_func, 1);\n  }\n  for (const auto& init_proc_func :\n       {&enable_floating_point_exceptions, &enable_segfault_handler}) {\n    _registerInitCall(*init_proc_func, 0);\n  }\n\n  for (const auto& init_proc_func : charm_init_proc_funcs) {\n    _registerInitCall(*init_proc_func, 0);\n  }\n}\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Register the Charm++ mainchare, reducers, chares, and entry methods.\n *\n * Call this function in `CkRegisterMainModule()` to set up a Charm++ executable\n * defined by the `Metavariables`. Normally the `CkRegisterMainModule()` is\n * generated by the `charmc` parser while generating\n * header files from interface files. The layer we built on Charm++ does not use\n * interface files directly and so we need to supply our `CkRegisterMainModule`\n * function. This function is also responsible for registering all parallel\n * components, all entry methods, and all custom reduction functions.\n */\ntemplate <typename Metavariables>\nvoid register_main_module() {\n  using charmxx_main_component = Parallel::Main<Metavariables>;\n  (void)charmxx_main_component{\n      Parallel::charmxx::MainChareRegistrationConstructor{}};\n  Parallel::charmxx::register_parallel_components();\n}\n\n}  // namespace Parallel::charmxx\n"
  },
  {
    "path": "src/Parallel/CharmRegistration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/CharmRegistration.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <string>\n\nnamespace Parallel::charmxx {\nstd::string get_template_parameters_as_string_impl(\n    const std::string& function_name) {\n  std::string template_params =\n      function_name.substr(function_name.find(std::string(\"Args = \")) + 8);\n  template_params.erase(template_params.end() - 2, template_params.end());\n  // GCC's __PRETTY_FUNCTION__ adds the return type at the end, so we remove it.\n  if (const auto brace = template_params.find('}');\n      brace != std::string::npos) {\n    template_params.erase(brace, template_params.size());\n  }\n  // Remove all spaces\n  const auto new_end =\n      std::remove(template_params.begin(), template_params.end(), ' ');\n  template_params.erase(new_end, template_params.end());\n  std::replace(template_params.begin(), template_params.end(), '%', '>');\n  return template_params;\n}\n}  // namespace Parallel::charmxx\n"
  },
  {
    "path": "src/Parallel/CharmRegistration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/container/small_vector.hpp>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/ReductionDeclare.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"Utilities/Functional.hpp\"\n\n/// \\cond\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\ntemplate <size_t MaxSize, class Key, class ValueType, class Hash,\n          class KeyEqual>\nclass FixedHashMap;\ntemplate <size_t Dim, class ValueType>\nclass DirectionalIdMap;\ntemplate <size_t Dim>\nclass DirectionalId;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass CProxy_GlobalCache;\ntemplate <typename Metavariables>\nclass CProxy_Main;\ntemplate <typename Metavariables>\nclass CkIndex_GlobalCache;\ntemplate <typename Metavariables>\nclass CkIndex_Main;\ntemplate <typename Metavariables>\nclass GlobalCache;\ntemplate <typename Metavariables>\nclass Main;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Parallel {\nnamespace charmxx {\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Class to mark a constructor as a constructor for the main chare.\n *\n * The main chare must have a constructor that takes a `const\n * Parallel::charmxx::MainChareRegistrationConstructor&` as its only argument.\n * This constructor is only used to trigger the `RegisterChare::registrar` code\n * needed for automatic registration.\n * \\see RegisterChare\n */\nstruct MainChareRegistrationConstructor {};\n\n/// \\cond\nstruct RegistrationHelper;\nextern std::unique_ptr<RegistrationHelper>* charm_register_list;\nextern size_t charm_register_list_capacity;\nextern size_t charm_register_list_size;\n/// \\endcond\n\nstd::string get_template_parameters_as_string_impl(\n    const std::string& function_name);\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Returns the template parameter as a `std::string`\n *\n * Uses the __PRETTY_FUNCTION__ compiler intrinsic to extract the template\n * parameter names in the same form that Charm++ uses to register entry methods.\n * This is used by the generated Singleton, Array, Group and Nodegroup headers,\n * as well as in CharmMain.tpp.\n */\ntemplate <class... Args>\nstd::string get_template_parameters_as_string() {\n  std::string function_name(static_cast<char const*>(__PRETTY_FUNCTION__));\n  return get_template_parameters_as_string_impl(function_name);\n}\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief The base class used for automatic registration of entry methods.\n *\n * Entry methods are automatically registered by building a list of the entry\n * methods that need to be registered in the `charm_register_list`. All entry\n * methods in the list are later registered in the\n * `register_parallel_components` function, at which point the list is also\n * deleted.\n *\n * The reason for using an abstract base class mechanism is that we need to be\n * able to register entry method templates. The derived classes keep track of\n * the template parameters and override the `register_with_charm` function.\n * The result is that there must be one derived class template for each entry\n * method template, but since we only have a few entry method templates this is\n * not an issue.\n */\nstruct RegistrationHelper {\n  RegistrationHelper() = default;\n  RegistrationHelper(const RegistrationHelper&) = default;\n  RegistrationHelper& operator=(const RegistrationHelper&) = default;\n  RegistrationHelper(RegistrationHelper&&) = default;\n  RegistrationHelper& operator=(RegistrationHelper&&) = default;\n  virtual ~RegistrationHelper() = default;\n\n  virtual void register_with_charm() const = 0;\n  virtual std::string name() const = 0;\n  virtual bool is_registering_chare() const { return false; };\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Class used for automatic registration of Charm++ messages.\n *\n * Entry methods that use Charm++ messages are responsible for registering those\n * messages by calling the static `register_with_charm()` function of this\n * struct. Because an entry method can be templated on the message type, we\n * don't want to accidentally register the same message-type twice. This struct\n * will take care of that, rather than having the logic in the entry method\n * registration logic.\n */\ntemplate <typename MessageType>\nstruct RegisterCharmMessage {\n  static void register_with_charm() {\n    static bool done_registration = false;\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n\n    // NOTE: Assumes custom pack/unpack but default allocate.\n    static std::string name = MessageType::name();\n    MessageType::base::__register(\n        name.c_str(), sizeof(MessageType),\n        reinterpret_cast<CkPackFnPtr>(MessageType::pack),\n        reinterpret_cast<CkUnpackFnPtr>(MessageType::unpack));\n  }\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Derived class for registering parallel components.\n *\n * Calls the appropriate Charm++ function to register a parallel component.\n */\ntemplate <typename ParallelComponent>\nstruct RegisterParallelComponent : RegistrationHelper {\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using ckindex = typename charm_type::ckindex;\n  using algorithm = typename charm_type::algorithm;\n\n  RegisterParallelComponent() = default;\n  RegisterParallelComponent(const RegisterParallelComponent&) = default;\n  RegisterParallelComponent& operator=(const RegisterParallelComponent&) =\n      default;\n  RegisterParallelComponent(RegisterParallelComponent&&) = default;\n  RegisterParallelComponent& operator=(RegisterParallelComponent&&) = default;\n  ~RegisterParallelComponent() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    static const std::string parallel_component_name = name();\n    ckindex::__register(parallel_component_name.c_str(), sizeof(algorithm));\n  }\n\n  bool is_registering_chare() const override { return true; }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<algorithm>();\n  }\n\n  static bool registrar;\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Derived class for registering chares.\n *\n * Calls the appropriate Charm++ function to register a chare\n *\n * The chare that is being registered must have the following in the destructor:\n * \\code\n * (void)Parallel::charmxx::RegisterChare<ChareName,\n *     CkIndex_ChareName>::registrar;\n * \\endcode\n *\n * The main chare must also have a constructor that takes a `const\n * Parallel::charmxx::MainChareRegistrationConstructor&` as its only argument.\n * This constructor is only used to trigger the `RegisterChare::registrar` code\n * needed for automatic registration. The main chare is set up in\n * `Parallel::charmxx::register_main_module<Metavariables>`.\n */\ntemplate <typename Chare, typename CkIndex>\nstruct RegisterChare : RegistrationHelper {\n  RegisterChare() = default;\n  RegisterChare(const RegisterChare&) = default;\n  RegisterChare& operator=(const RegisterChare&) = default;\n  RegisterChare(RegisterChare&&) = default;\n  RegisterChare& operator=(RegisterChare&&) = default;\n  ~RegisterChare() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    static const std::string chare_name = name();\n    CkIndex::__register(chare_name.c_str(), sizeof(Chare));\n  }\n\n  bool is_registering_chare() const override { return true; }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<Chare>();\n  }\n\n  static bool registrar;\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Derived class for registering simple actions.\n *\n * Calls the appropriate Charm++ function to register a simple action.\n */\ntemplate <typename ParallelComponent, typename Action, typename... Args>\nstruct RegisterSimpleAction : RegistrationHelper {\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using cproxy = typename charm_type::cproxy;\n  using ckindex = typename charm_type::ckindex;\n  using algorithm = typename charm_type::algorithm;\n\n  RegisterSimpleAction() = default;\n  RegisterSimpleAction(const RegisterSimpleAction&) = default;\n  RegisterSimpleAction& operator=(const RegisterSimpleAction&) = default;\n  RegisterSimpleAction(RegisterSimpleAction&&) = default;\n  RegisterSimpleAction& operator=(RegisterSimpleAction&&) = default;\n  ~RegisterSimpleAction() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    ckindex::template idx_simple_action<Action>(\n        static_cast<void (algorithm::*)(const std::tuple<Args...>&)>(nullptr));\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterSimpleAction>();\n  }\n\n  static bool registrar;\n};\n\n/// \\cond\ntemplate <typename ParallelComponent, typename Action>\nstruct RegisterSimpleAction<ParallelComponent, Action> : RegistrationHelper {\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using cproxy = typename charm_type::cproxy;\n  using ckindex = typename charm_type::ckindex;\n  using algorithm = typename charm_type::algorithm;\n\n  RegisterSimpleAction() = default;\n  RegisterSimpleAction(const RegisterSimpleAction&) = default;\n  RegisterSimpleAction& operator=(const RegisterSimpleAction&) = default;\n  RegisterSimpleAction(RegisterSimpleAction&&) = default;\n  RegisterSimpleAction& operator=(RegisterSimpleAction&&) = default;\n  ~RegisterSimpleAction() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    ckindex::template idx_simple_action<Action>(\n        static_cast<void (algorithm::*)()>(nullptr));\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterSimpleAction>();\n  }\n\n  static bool registrar;\n};\n/// \\endcond\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Derived class for registering threaded actions.\n *\n * Calls the appropriate Charm++ function to register a threaded action.\n */\ntemplate <typename ParallelComponent, typename Action, typename... Args>\nstruct RegisterThreadedAction : RegistrationHelper {\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using cproxy = typename charm_type::cproxy;\n  using ckindex = typename charm_type::ckindex;\n  using algorithm = typename charm_type::algorithm;\n\n  RegisterThreadedAction() = default;\n  RegisterThreadedAction(const RegisterThreadedAction&) = default;\n  RegisterThreadedAction& operator=(const RegisterThreadedAction&) = default;\n  RegisterThreadedAction(RegisterThreadedAction&&) = default;\n  RegisterThreadedAction& operator=(RegisterThreadedAction&&) = default;\n  ~RegisterThreadedAction() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    ckindex::template idx_threaded_action<Action>(\n        static_cast<void (algorithm::*)(const std::tuple<Args...>&)>(nullptr));\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterThreadedAction>();\n  }\n\n  static bool registrar;\n};\n\n/// \\cond\ntemplate <typename ParallelComponent, typename Action>\nstruct RegisterThreadedAction<ParallelComponent, Action> : RegistrationHelper {\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using cproxy = typename charm_type::cproxy;\n  using ckindex = typename charm_type::ckindex;\n  using algorithm = typename charm_type::algorithm;\n\n  RegisterThreadedAction() = default;\n  RegisterThreadedAction(const RegisterThreadedAction&) = default;\n  RegisterThreadedAction& operator=(const RegisterThreadedAction&) = default;\n  RegisterThreadedAction(RegisterThreadedAction&&) = default;\n  RegisterThreadedAction& operator=(RegisterThreadedAction&&) = default;\n  ~RegisterThreadedAction() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    ckindex::template idx_threaded_action<Action>(\n        static_cast<void (algorithm::*)()>(nullptr));\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterThreadedAction>();\n  }\n\n  static bool registrar;\n};\n/// \\endcond\n\nnamespace detail {\ntemplate <class T>\nstruct get_value_type {\n  using type = T;\n};\n\ntemplate <class T>\nstruct get_value_type<std::vector<T>> {\n  using type = T;\n};\n\ntemplate <class T, size_t N>\nstruct get_value_type<boost::container::small_vector<T, N>> {\n  using type = T;\n};\n\ntemplate <class Key, class Mapped, class Hash, class KeyEqual, class Allocator>\nstruct get_value_type<\n    std::unordered_map<Key, Mapped, Hash, KeyEqual, Allocator>> {\n  // When sending data it is typical to use `std::make_pair(a, b)` which results\n  // in a non-const Key type, which is different from what\n  // `unordered_map::value_type` is (e.g. `std::pair<const Key, Mapped>`). This\n  // difference leads to issues with function registration with Charm++ because\n  // when registering `receive_data` methods we register the `inbox_type`'s\n  // `value_type` (`std::pair<const Key, Mapped>` in this case), not the type\n  // passed to `receive_data`.\n  using type = std::pair<Key, Mapped>;\n};\n\ntemplate <size_t Size, class Key, class Mapped, class Hash, class KeyEqual>\nstruct get_value_type<FixedHashMap<Size, Key, Mapped, Hash, KeyEqual>> {\n  // When sending data it is typical to use `std::make_pair(a, b)` which results\n  // in a non-const Key type, which is different from what\n  // `FixedHashMap::value_type` is (e.g. `std::pair<const Key, Mapped>`). This\n  // difference leads to issues with function registration with Charm++ because\n  // when registering `receive_data` methods we register the `inbox_type`'s\n  // `value_type` (`std::pair<const Key, Mapped>` in this case), not the type\n  // passed to `receive_data`.\n  using type = std::pair<Key, Mapped>;\n};\n\ntemplate <size_t Dim, class Mapped>\nstruct get_value_type<DirectionalIdMap<Dim, Mapped>> {\n  // When sending data it is typical to use `std::make_pair(a, b)` which results\n  // in a non-const Key type, which is different from what\n  // `FixedHashMap::value_type` is (e.g. `std::pair<const Key, Mapped>`). This\n  // difference leads to issues with function registration with Charm++ because\n  // when registering `receive_data` methods we register the `inbox_type`'s\n  // `value_type` (`std::pair<const Key, Mapped>` in this case), not the type\n  // passed to `receive_data`.\n  using type = std::pair<DirectionalId<Dim>, Mapped>;\n};\n\ntemplate <class Key, class Hash, class KeyEqual, class Allocator>\nstruct get_value_type<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>> {\n  using type = Key;\n};\n\ntemplate <class T>\nusing get_value_type_t = typename get_value_type<T>::type;\n}  // namespace detail\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Derived class for registering receive_data functions\n *\n * Calls the appropriate Charm++ function to register a receive_data function.\n * There is a bug in Charm++ that doesn't allow default values for entry method\n * arguments for groups and nodegroups, so we have to handle the (node)group\n * cases separately from the singleton and array cases.\n */\ntemplate <typename ParallelComponent, typename ReceiveTag, bool UsingMessages>\nstruct RegisterReceiveData : RegistrationHelper {\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using cproxy = typename charm_type::cproxy;\n  using ckindex = typename charm_type::ckindex;\n  using algorithm = typename charm_type::algorithm;\n\n  RegisterReceiveData() = default;\n  RegisterReceiveData(const RegisterReceiveData&) = default;\n  RegisterReceiveData& operator=(const RegisterReceiveData&) = default;\n  RegisterReceiveData(RegisterReceiveData&&) = default;\n  RegisterReceiveData& operator=(RegisterReceiveData&&) = default;\n  ~RegisterReceiveData() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    if constexpr (UsingMessages) {\n      using message_type = typename ReceiveTag::message_type;\n      ckindex::template idx_receive_data<ReceiveTag>(\n          static_cast<void (algorithm::*)(message_type*)>(nullptr));\n      RegisterCharmMessage<message_type>::register_with_charm();\n    } else {\n      ckindex::template idx_receive_data<ReceiveTag>(\n          static_cast<void (algorithm::*)(\n              const typename ReceiveTag::temporal_id&,\n              const detail::get_value_type_t<\n                  typename ReceiveTag::type::mapped_type>&,\n              bool)>(nullptr));\n    }\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterReceiveData>();\n  }\n\n  static bool registrar;\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Derived class for registering reduction actions\n *\n * Calls the appropriate Charm++ functions to register a reduction action.\n */\ntemplate <typename ParallelComponent, typename Action, typename ReductionType>\nstruct RegisterReductionAction : RegistrationHelper {\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using cproxy = typename charm_type::cproxy;\n  using ckindex = typename charm_type::ckindex;\n  using algorithm = typename charm_type::algorithm;\n\n  RegisterReductionAction() = default;\n  RegisterReductionAction(const RegisterReductionAction&) = default;\n  RegisterReductionAction& operator=(const RegisterReductionAction&) = default;\n  RegisterReductionAction(RegisterReductionAction&&) = default;\n  RegisterReductionAction& operator=(RegisterReductionAction&&) = default;\n  ~RegisterReductionAction() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    ckindex::template idx_reduction_action<Action, ReductionType>(\n        static_cast<void (algorithm::*)(const ReductionType&)>(nullptr));\n    ckindex::template redn_wrapper_reduction_action<Action, ReductionType>(\n        nullptr);\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterReductionAction>();\n  }\n\n  static bool registrar;\n};\n\ntemplate <typename Metavariables, typename InvokeCombine, typename... Tags>\nstruct RegisterPhaseChangeReduction : RegistrationHelper {\n  using cproxy = CProxy_Main<Metavariables>;\n  using ckindex = CkIndex_Main<Metavariables>;\n  using algorithm = Main<Metavariables>;\n\n  RegisterPhaseChangeReduction() = default;\n  RegisterPhaseChangeReduction(const RegisterPhaseChangeReduction&) = default;\n  RegisterPhaseChangeReduction& operator=(const RegisterPhaseChangeReduction&) =\n      default;\n  RegisterPhaseChangeReduction(RegisterPhaseChangeReduction&&) = default;\n  RegisterPhaseChangeReduction& operator=(RegisterPhaseChangeReduction&&) =\n      default;\n  ~RegisterPhaseChangeReduction() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    ckindex::template idx_phase_change_reduction<InvokeCombine, Tags...>(\n        static_cast<void (algorithm::*)(\n            const ReductionData<\n                ReductionDatum<tuples::TaggedTuple<Tags...>, InvokeCombine,\n                               funcl::Identity, std::index_sequence<>>>&)>(\n            nullptr));\n    ckindex::template redn_wrapper_phase_change_reduction<InvokeCombine,\n                                                          Tags...>(nullptr);\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterPhaseChangeReduction>();\n  }\n\n  static bool registrar;\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Derived class for registering GlobalCache::mutate\n *\n * Calls the appropriate Charm++ function to register the mutate function.\n */\ntemplate <typename Metavariables, typename GlobalCacheTag, typename Function,\n          typename... Args>\nstruct RegisterGlobalCacheMutate : RegistrationHelper {\n  using cproxy = CProxy_GlobalCache<Metavariables>;\n  using ckindex = CkIndex_GlobalCache<Metavariables>;\n  using algorithm = GlobalCache<Metavariables>;\n\n  RegisterGlobalCacheMutate() = default;\n  RegisterGlobalCacheMutate(const RegisterGlobalCacheMutate&) = default;\n  RegisterGlobalCacheMutate& operator=(const RegisterGlobalCacheMutate&) =\n      default;\n  RegisterGlobalCacheMutate(RegisterGlobalCacheMutate&&) = default;\n  RegisterGlobalCacheMutate& operator=(RegisterGlobalCacheMutate&&) = default;\n  ~RegisterGlobalCacheMutate() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    ckindex::template idx_mutate<GlobalCacheTag, Function>(\n        static_cast<void (algorithm::*)(const std::tuple<Args...>&)>(nullptr));\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterGlobalCacheMutate>();\n  }\n\n  static bool registrar;\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Derived class for registering the invoke_iterable_action entry method.\n * This is a `local` entry method that is only used for tracing the code with\n * Charm++'s Projections.\n *\n * Calls the appropriate Charm++ function to register the invoke_iterable_action\n * function.\n */\ntemplate <typename ParallelComponent, typename Action, typename PhaseIndex,\n          typename DataBoxIndex>\nstruct RegisterInvokeIterableAction : RegistrationHelper {\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using cproxy = typename charm_type::cproxy;\n  using ckindex = typename charm_type::ckindex;\n  using algorithm = typename charm_type::algorithm;\n\n  RegisterInvokeIterableAction() = default;\n  RegisterInvokeIterableAction(const RegisterInvokeIterableAction&) = default;\n  RegisterInvokeIterableAction& operator=(const RegisterInvokeIterableAction&) =\n      default;\n  RegisterInvokeIterableAction(RegisterInvokeIterableAction&&) = default;\n  RegisterInvokeIterableAction& operator=(RegisterInvokeIterableAction&&) =\n      default;\n  ~RegisterInvokeIterableAction() override = default;\n\n  void register_with_charm() const override {\n    static bool done_registration{false};\n    if (done_registration) {\n      return;  // LCOV_EXCL_LINE\n    }\n    done_registration = true;\n    ckindex::template idx_invoke_iterable_action<Action, PhaseIndex,\n                                                 DataBoxIndex>(\n        static_cast<bool (algorithm::*)()>(nullptr));\n  }\n\n  std::string name() const override {\n    return get_template_parameters_as_string<RegisterInvokeIterableAction>();\n  }\n\n  static bool registrar;\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Function that adds a pointer to a specific derived class to the\n * `charm_register_list`\n *\n * Used to initialize the `registrar` bool of derived classes of\n * `RegistrationHelper`. When the function is invoked it appends the derived\n * class to the `charm_register_list`.\n *\n * \\note The reason for not using a `std::vector` is that this did not behave\n * correctly when calling `push_back`. Specifically, the final vector was always\n * size 1, even though multiple elements were pushed back. The reason for that\n * behavior was never tracked down and so in the future it could be possible to\n * use a `std::vector`.\n */\ntemplate <typename Derived>\nbool register_func_with_charm() {\n#ifdef SPECTRE_CHARM_HAS_MAIN\n  if (charm_register_list_size >= charm_register_list_capacity) {\n    // clang-tidy: use gsl::owner (we don't use raw owning pointers unless\n    // necessary)\n    auto* const t =  // NOLINT\n        new std::unique_ptr<RegistrationHelper>[charm_register_list_capacity +\n                                                10];\n    for (size_t i = 0; i < charm_register_list_capacity; ++i) {\n      // clang-tidy: do not use pointer arithmetic\n      t[i] = std::move(charm_register_list[i]);  // NOLINT\n    }\n    // clang-tidy: use gsl::owner (we don't use raw owning pointers unless\n    // necessary)\n    delete[] charm_register_list;  // NOLINT\n    charm_register_list = t;\n    charm_register_list_capacity += 10;\n  }\n  charm_register_list_size++;\n  // clang-tidy: do not use pointer arithmetic\n  charm_register_list[charm_register_list_size - 1] =  // NOLINT\n      std::make_unique<Derived>();\n#endif  // SPECTRE_CHARM_HAS_MAIN\n  return true;\n}\n}  // namespace charmxx\n}  // namespace Parallel\n\n// clang-tidy: redundant declaration\ntemplate <typename ParallelComponent>\nbool Parallel::charmxx::RegisterParallelComponent<\n    ParallelComponent>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterParallelComponent<ParallelComponent>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename Chare, typename CkIndex>\nbool Parallel::charmxx::RegisterChare<Chare, CkIndex>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterChare<Chare, CkIndex>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename ParallelComponent, typename Action, typename... Args>\nbool Parallel::charmxx::RegisterSimpleAction<ParallelComponent, Action,\n                                             Args...>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterSimpleAction<ParallelComponent, Action, Args...>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename ParallelComponent, typename Action>\nbool Parallel::charmxx::RegisterSimpleAction<ParallelComponent,\n                                             Action>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterSimpleAction<ParallelComponent, Action>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename ParallelComponent, typename Action, typename... Args>\nbool Parallel::charmxx::RegisterThreadedAction<ParallelComponent, Action,\n                                               Args...>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterThreadedAction<ParallelComponent, Action, Args...>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename ParallelComponent, typename Action>\nbool Parallel::charmxx::RegisterThreadedAction<ParallelComponent,\n                                               Action>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterThreadedAction<ParallelComponent, Action>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename ParallelComponent, typename ReceiveTag, bool UsingMessages>\nbool Parallel::charmxx::RegisterReceiveData<\n    ParallelComponent, ReceiveTag, UsingMessages>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterReceiveData<ParallelComponent, ReceiveTag, UsingMessages>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename ParallelComponent, typename Action, typename ReductionType>\nbool Parallel::charmxx::RegisterReductionAction<\n    ParallelComponent, Action, ReductionType>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterReductionAction<ParallelComponent, Action, ReductionType>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename Metavariables, typename Invokable, typename... Tags>\nbool Parallel::charmxx::RegisterPhaseChangeReduction<\n    Metavariables, Invokable, Tags...>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<\n        RegisterPhaseChangeReduction<Metavariables, Invokable, Tags...>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename Metavariables, typename GlobalCacheTag, typename Function,\n          typename... Args>\nbool Parallel::charmxx::RegisterGlobalCacheMutate<\n    Metavariables, GlobalCacheTag, Function,\n    Args...>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<RegisterGlobalCacheMutate<\n        Metavariables, GlobalCacheTag, Function, Args...>>();\n\n// clang-tidy: redundant declaration\ntemplate <typename ParallelComponent, typename Action, typename PhaseIndex,\n          typename DataBoxIndex>\nbool Parallel::charmxx::RegisterInvokeIterableAction<\n    ParallelComponent, Action, PhaseIndex, DataBoxIndex>::registrar =  // NOLINT\n    Parallel::charmxx::register_func_with_charm<RegisterInvokeIterableAction<\n        ParallelComponent, Action, PhaseIndex, DataBoxIndex>>();\n\n/// \\cond\nclass CkReductionMsg;\n/// \\endcond\n\nnamespace Parallel {\nnamespace charmxx {\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief The type of a function pointer to a Charm++ custom reduction function.\n */\nusing ReducerFunctions = CkReductionMsg* (*)(int, CkReductionMsg**);\n/// \\cond\nextern ReducerFunctions* charm_reducer_functions_list;\nextern size_t charm_reducer_functions_capacity;\nextern size_t charm_reducer_functions_size;\nextern std::unordered_map<size_t, CkReduction::reducerType>\n    charm_reducer_functions;\n/// \\endcond\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Class used for registering custom reduction function\n *\n * The static member variable is initialized before main is entered. This means\n * we are able to inject code into the beginning of the execution of an\n * executable by \"using\" the variable (casting to void counts) the function that\n * initializes the variable is called. We \"use\" `registrar` inside the\n * `Parallel::contribute_to_reduction` function.\n */\ntemplate <ReducerFunctions>\nstruct RegisterReducerFunction {\n  static bool registrar;\n};\n\n/*!\n * \\ingroup CharmExtensionsGroup\n * \\brief Function that stores a function pointer to the custom reduction\n * function to be registered later.\n *\n * Used to initialize the `registrar` bool of `RegisterReducerFunction`. When\n * invoked it adds the function `F` of type `ReducerFunctions` to the list\n * `charm_reducer_functions_list`.\n *\n * \\note The reason for not using a `std::vector<ReducerFunctions>` is that this\n * did not behave correctly when calling `push_back`. Specifically, the final\n * vector was always size 1, even though multiple elements were pushed back. The\n * reason for that behavior was never tracked down and so in the future it could\n * be possible to use a `std::vector`.\n */\ntemplate <ReducerFunctions F>\nbool register_reducer_function() {\n#ifdef SPECTRE_CHARM_HAS_MAIN\n  if (charm_reducer_functions_size >= charm_reducer_functions_capacity) {\n    // clang-tidy: use gsl::owner (we don't use raw owning pointers unless\n    // necessary)\n    auto* const t =  // NOLINT\n        new ReducerFunctions[charm_reducer_functions_capacity + 10];\n    for (size_t i = 0; i < charm_reducer_functions_capacity; ++i) {\n      // clang-tidy: do not use pointer arithmetic\n      t[i] = std::move(charm_reducer_functions_list[i]);  // NOLINT\n    }\n    // clang-tidy: use gsl::owner (we don't use raw owning pointers unless\n    // necessary)\n    delete[] charm_reducer_functions_list;  // NOLINT\n    charm_reducer_functions_list = t;\n    charm_reducer_functions_capacity += 10;\n  }\n  charm_reducer_functions_size++;\n  // clang-tidy: do not use pointer arithmetic\n  charm_reducer_functions_list[charm_reducer_functions_size - 1] = F;  // NOLINT\n#endif  // SPECTRE_CHARM_HAS_MAIN\n  return true;\n}\n}  // namespace charmxx\n}  // namespace Parallel\n\n// clang-tidy: do not use pointer arithmetic\ntemplate <Parallel::charmxx::ReducerFunctions F>\nbool Parallel::charmxx::RegisterReducerFunction<F>::registrar =  // NOLINT\n    Parallel::charmxx::register_reducer_function<F>();\n"
  },
  {
    "path": "src/Parallel/CreateFromOptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/InitializationTag.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace Parallel {\nnamespace detail {\ntemplate <typename Metavariables, templated_initialization_tag Tag,\n          typename... OptionTags>\ntypename Tag::type create_initialization_item_from_options(\n    const tuples::TaggedTuple<OptionTags...>& options) {\n  return tuples::apply<typename Tag::template option_tags<Metavariables>>(\n      [](const auto&... option) {\n        return Tag::template create_from_options<Metavariables>(option...);\n      },\n      options);\n}\n\ntemplate <typename Metavariables, untemplated_initialization_tag Tag,\n          typename... OptionTags>\ntypename Tag::type create_initialization_item_from_options(\n    const tuples::TaggedTuple<OptionTags...>& options) {\n  return tuples::apply<typename Tag::option_tags>(\n      [](const auto&... option) { return Tag::create_from_options(option...); },\n      options);\n}\n}  // namespace detail\n\n/// \\ingroup ParallelGroup\n/// \\brief Given a list of tags and a tagged tuple containing items\n/// created from input options, return a tagged tuple of items constructed\n/// by calls to create_from_options for each tag in the list.\ntemplate <typename Metavariables, initialization_tag... Tags,\n          typename... OptionTags>\ntuples::TaggedTuple<Tags...> create_from_options(\n    const tuples::TaggedTuple<OptionTags...>& options,\n    tmpl::list<Tags...> /*meta*/) {\n  return {detail::create_initialization_item_from_options<Metavariables, Tags>(\n      options)...};\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/DistributedObject.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <converse.h>\n#include <cstddef>\n#include <exception>\n#include <initializer_list>\n#include <limits>\n#include <mutex>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <sstream>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/AlgorithmMetafunctions.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArrayDeclarations.hpp\"\n#include \"Parallel/Algorithms/AlgorithmGroupDeclarations.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroupDeclarations.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingletonDeclarations.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/CharmRegistration.hpp\"\n#include \"Parallel/ElementRegistration.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Tags/ArrayIndex.hpp\"\n#include \"Parallel/Tags/DistributedObjectTags.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass ElementId;\n\ntemplate <size_t Dim>\nbool is_zeroth_element(const ElementId<Dim>&, const std::optional<size_t>&);\n/// \\endcond\n\nnamespace Parallel {\n/// \\cond\ntemplate <typename ParallelComponent, typename PhaseDepActionList>\nclass DistributedObject;\n/// \\endcond\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A distributed object (Charm++ Chare) that executes a series of Actions\n * and is capable of sending and receiving data. Acts as an interface to\n * Charm++.\n *\n * ### Different Types of Algorithms\n * Charm++ chares can be one of four types, which is specified by the type alias\n * `chare_type` inside the `ParallelComponent`. The four available types of\n * Algorithms are:\n * 1. A Parallel::Algorithms::Singleton where there is only one\n * in the entire execution of the program.\n * 2. A Parallel::Algorithms::Array which holds zero or more\n * elements each of which is a distributed object on some core. An array can\n * grow and shrink in size dynamically if need be and can also be bound to\n * another array. That is, the bound array has the same number of elements as\n * the array it is bound to, and elements with the same ID are on the same core.\n * 3. A Parallel::Algorithms::Group, which is an array but there is\n * one element per core and they are not able to be moved around between cores.\n * These are typically useful for gathering data from array elements on their\n * core, and then processing or reducing it.\n * 4. A Parallel::Algorithms::Nodegroup, which is similar to a\n * group except that there is one element per node. For Charm++ SMP (shared\n * memory parallelism) builds a node corresponds to the usual definition of a\n * node on a supercomputer. However, for non-SMP builds nodes and cores are\n * equivalent. An important difference between groups and nodegroups is that\n * entry methods (remote calls to functions) are not threadsafe on nodegroups.\n * It is up to the person writing the Actions that will be executed on the\n * Nodegroup Algorithm to ensure they are threadsafe.\n *\n * ### What is an Algorithm?\n * An Algorithm is a distributed object, a Charm++ chare, that repeatedly\n * executes a series of Actions. An Action is a struct that has a `static` apply\n * function with signature:\n *\n * \\code\n * template <typename... DbTags, typename... InboxTags, typename Metavariables,\n * typename ArrayIndex,  typename ActionList>\n * static auto  apply(db::DataBox<tmpl::list<DbTags...>>& box,\n *                    tuples::TaggedTuple<InboxTags...>& inboxes,\n *                    const GlobalCache<Metavariables>& cache,\n *                    const ArrayIndex& array_index,\n *                    const TemporalId& temporal_id, const ActionList meta);\n * \\endcode\n *\n * Note that any of the arguments can be const or non-const references except\n * `array_index`, which must be a `const&`.\n *\n * ### Explicit instantiations of entry methods\n * The code in src/Parallel/CharmMain.tpp registers all entry methods, and if\n * one is not properly registered then a static_assert explains how to have it\n * be registered. If there is a bug in the implementation and an entry method\n * isn't being registered or hitting a static_assert then Charm++ will give an\n * error of the following form:\n *\n * \\verbatim\n * registration happened after init Entry point: simple_action(), addr:\n * 0x555a3d0e2090\n * ------------- Processor 0 Exiting: Called CmiAbort ------------\n * Reason: Did you forget to instantiate a templated entry method in a .ci file?\n * \\endverbatim\n *\n * If you encounter this issue please file a bug report supplying everything\n * necessary to reproduce the issue.\n */\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nclass DistributedObject<ParallelComponent,\n                        tmpl::list<PhaseDepActionListsPack...>>\n    : public ParallelComponent::chare_type::template cbase<\n          ParallelComponent,\n          typename get_array_index<typename ParallelComponent::chare_type>::\n              template f<ParallelComponent>> {\n  static_assert(\n      sizeof...(PhaseDepActionListsPack) > 0,\n      \"Must have at least one phase dependent action list \"\n      \"(PhaseActions) in a parallel component. See the first template \"\n      \"parameter of 'DistributedObject' in the error message to see which \"\n      \"component doesn't have any phase dependent action lists.\");\n\n public:\n  /// List of Actions in the order that generates the DataBox types\n  using all_actions_list = tmpl::flatten<\n      tmpl::list<typename PhaseDepActionListsPack::action_list...>>;\n  /// The metavariables class passed to the Algorithm\n  using metavariables = typename ParallelComponent::metavariables;\n  /// List off all the Tags that can be received into the Inbox\n  using inbox_tags_list = Parallel::get_inbox_tags<all_actions_list>;\n  /// The type of the object used to uniquely identify the element of the array,\n  /// group, or nodegroup. The default depends on the component, see\n  /// ParallelComponentHelpers.\n  using array_index = typename get_array_index<\n      typename ParallelComponent::chare_type>::template f<ParallelComponent>;\n\n  using parallel_component = ParallelComponent;\n  /// The type of the Chare\n  using chare_type = typename parallel_component::chare_type;\n  /// The Charm++ proxy object type\n  using cproxy_type =\n      typename chare_type::template cproxy<parallel_component, array_index>;\n  /// The Charm++ base object type\n  using cbase_type =\n      typename chare_type::template cbase<parallel_component, array_index>;\n\n  using phase_dependent_action_lists = tmpl::list<PhaseDepActionListsPack...>;\n\n  using inbox_type = tuples::tagged_tuple_from_typelist<inbox_tags_list>;\n  using all_cache_tags = get_const_global_cache_tags<metavariables>;\n  using distributed_object_tags =\n      typename Tags::distributed_object_tags<metavariables, array_index>;\n  using databox_type = db::compute_databox_type<tmpl::flatten<tmpl::list<\n      distributed_object_tags,\n      typename parallel_component::simple_tags_from_options,\n      Tags::GlobalCacheCompute<metavariables>,\n      Tags::ResourceInfoReference<metavariables>,\n      db::wrap_tags_in<Tags::FromGlobalCache, all_cache_tags, metavariables>,\n      Algorithm_detail::action_list_simple_tags<parallel_component>,\n      Algorithm_detail::action_list_compute_tags<parallel_component>>>>;\n\n  /// \\cond\n  // Needed for serialization\n  DistributedObject();\n  /// \\endcond\n\n  /// Constructor used by Main to initialize the algorithm\n  template <class... InitializationTags>\n  DistributedObject(\n      const Parallel::CProxy_GlobalCache<metavariables>& global_cache_proxy,\n      tuples::TaggedTuple<InitializationTags...> initialization_items);\n\n  /// Constructor used to dynamically add a new element of an array\n  /// The `callback` is executed after the element is created.\n  DistributedObject(\n      const Parallel::CProxy_GlobalCache<metavariables>& global_cache_proxy,\n      Parallel::Phase current_phase,\n      std::unordered_map<Parallel::Phase, size_t> phase_bookmarks,\n      const std::unique_ptr<Parallel::Callback>& callback);\n\n  /// Charm++ migration constructor, used after a chare is migrated\n  explicit DistributedObject(CkMigrateMessage* /*msg*/);\n\n  /// \\cond\n  ~DistributedObject() override;\n\n  DistributedObject(const DistributedObject& /*unused*/) = delete;\n  DistributedObject& operator=(const DistributedObject& /*unused*/) = delete;\n  DistributedObject(DistributedObject&& /*unused*/) = delete;\n  DistributedObject& operator=(DistributedObject&& /*unused*/) = delete;\n  /// \\endcond\n\n  /// Print the expanded type aliases\n  std::string print_types() const;\n\n  /// Print the current state of the algorithm\n  std::string print_state() const;\n\n  /// Print the current contents of the inboxes\n  std::string print_inbox() const;\n\n  /// Print the current contents of the DataBox\n  std::string print_databox() const;\n\n  /// Get read access to all the inboxes\n  const auto& get_inboxes() const { return inboxes_; }\n\n  auto& get_node_lock() { return node_lock_; }\n\n  void pup(PUP::er& p) override;  // NOLINT\n\n  /*!\n   * \\brief Calls the `apply` function `Action` after a reduction has been\n   * completed.\n   *\n   * The `apply` function must take `arg` as its last argument.\n   */\n  template <typename Action, typename Arg>\n  void reduction_action(Arg arg);\n\n  /// \\brief Explicitly call the action `Action`.\n  template <typename Action, typename... Args>\n  void simple_action(std::tuple<Args...> args);\n\n  template <typename Action>\n  void simple_action();\n\n  /// \\brief Call the `Action` sychronously, returning a result without any\n  /// parallelization. The action is called immediately and control flow returns\n  /// to the caller immediately upon completion.\n  ///\n  /// \\note `Action` must have a type alias `return_type` specifying its return\n  /// type. This constraint is to simplify the variant visitation logic for the\n  /// \\ref DataBoxGroup \"DataBox\".\n  template <typename Action, typename... Args>\n  typename Action::return_type local_synchronous_action(Args&&... args);\n\n  /// @{\n  /// Call an Action on a local nodegroup requiring the Action to handle thread\n  /// safety.\n  ///\n  /// The `Parallel::NodeLock` of the nodegroup is passed to the Action instead\n  /// of the `action_list` as a `const gsl::not_null<Parallel::NodeLock*>&`. The\n  /// node lock can be locked with the `Parallel::NodeLock::lock()` function,\n  /// and unlocked with `Parallel::unlock()`. `Parallel::NodeLock::try_lock()`\n  /// is also provided in case something useful can be done if the lock couldn't\n  /// be acquired.\n  template <\n      typename Action, typename... Args,\n      Requires<((void)sizeof...(Args),\n                std::is_same_v<Parallel::Algorithms::Nodegroup, chare_type>)> =\n          nullptr>\n  void threaded_action(std::tuple<Args...> args) {\n    // Note: this method is defined inline because GCC fails to compile when the\n    // definition is out of line.\n    (void)Parallel::charmxx::RegisterThreadedAction<ParallelComponent, Action,\n                                                    Args...>::registrar;\n    forward_tuple_to_threaded_action<Action>(\n        std::move(args), std::make_index_sequence<sizeof...(Args)>{});\n  }\n\n  template <typename Action>\n  void threaded_action();\n  /// @}\n\n  /// \\brief Receive data and store it in the Inbox, and try to continue\n  /// executing the algorithm\n  ///\n  /// When an algorithm has terminated it can be restarted by passing\n  /// `enable_if_disabled = true`. This allows long-term disabling and\n  /// re-enabling of algorithms\n  template <typename ReceiveTag, typename ReceiveDataType>\n  void receive_data(typename ReceiveTag::temporal_id instance,\n                    ReceiveDataType&& t, bool enable_if_disabled = false);\n\n  template <typename ReceiveTag, typename MessageType>\n  void receive_data(MessageType* message);\n\n  /// @{\n  /// Start evaluating the algorithm until it is stopped by an action.\n  void perform_algorithm();\n\n  void perform_algorithm(const bool restart_if_terminated);\n  /// @}\n\n  /// Start execution of the phase-dependent action list in `next_phase`. If\n  /// `next_phase` has already been visited, execution will resume at the point\n  /// where the previous execution of the same phase left off.\n  ///\n  /// If \\p force is true, then regardless of how this component terminated\n  /// (error or deadlock), it will resume.\n  ///\n  /// \\warning Don't set \\p force to true unless you are absolutely sure you\n  /// want to. This can have very unintended consequences if used incorrectly.\n  void start_phase(Parallel::Phase next_phase, bool force = false);\n\n  /// Get the current phase\n  Phase phase() const { return phase_; }\n\n  /// \\brief Get the phase bookmarks\n  ///\n  /// \\details These are used to allow a phase to be resumed at a specific\n  /// step in its iterable action list after PhaseControl is used to temporarily\n  /// switch to other phases.\n  const std::unordered_map<Parallel::Phase, size_t>& phase_bookmarks() const {\n    return phase_bookmarks_;\n  }\n\n  /// Tell the Algorithm it should no longer execute the algorithm. This does\n  /// not mean that the execution of the program is terminated, but only that\n  /// the algorithm has terminated. An algorithm can be restarted by passing\n  /// `true` as the second argument to the `receive_data` method or by calling\n  /// perform_algorithm(true).\n  constexpr void set_terminate(const bool t) { terminate_ = t; }\n\n  /// Check if an algorithm should continue being evaluated\n  constexpr bool get_terminate() const { return terminate_; }\n\n  /// @{\n  /// Wrappers for charm++ informational functions.\n\n  /// Number of processing elements\n  inline int number_of_procs() const { return sys::number_of_procs(); }\n\n  /// %Index of my processing element.\n  inline int my_proc() const { return sys::my_proc(); }\n\n  /// Number of nodes.\n  inline int number_of_nodes() const { return sys::number_of_nodes(); }\n\n  /// %Index of my node.\n  inline int my_node() const { return sys::my_node(); }\n\n  /// Number of processing elements on the given node.\n  inline int procs_on_node(const int node_index) const {\n    return sys::procs_on_node(node_index);\n  }\n\n  /// The local index of my processing element on my node.\n  /// This is in the interval 0, ..., procs_on_node(my_node()) - 1.\n  inline int my_local_rank() const { return sys::my_local_rank(); }\n\n  /// %Index of first processing element on the given node.\n  inline int first_proc_on_node(const int node_index) const {\n    return sys::first_proc_on_node(node_index);\n  }\n\n  /// %Index of the node for the given processing element.\n  inline int node_of(const int proc_index) const {\n    return sys::node_of(proc_index);\n  }\n\n  /// The local index for the given processing element on its node.\n  inline int local_rank_of(const int proc_index) const {\n    return sys::local_rank_of(proc_index);\n  }\n  /// @}\n\n  // Invoke the static `apply` method of `ThisAction`. The if constexprs are for\n  // handling the cases where the `apply` method returns a tuple of one, two,\n  // or three elements, in order:\n  // 1. A DataBox\n  // 2. Either:\n  //    2a. A bool determining whether or not to terminate (and potentially move\n  //        to the next phase), or\n  //    2b. An `AlgorithmExecution` object describing whether to continue,\n  //        pause, or halt.\n  // 3. An unsigned integer corresponding to which action in the current phase's\n  //    algorithm to execute next.\n  //\n  // Returns whether the action ran successfully, i.e., did not return\n  // AlgorithmExecution::Retry.\n  //\n  // Needs to be public for local entry methods to work.\n  //\n  // Note: The template parameters PhaseIndex and DataBoxIndex are used to\n  // shorten the function name to make profiling easier.\n  template <typename ThisAction, typename PhaseIndex, typename DataBoxIndex>\n  bool invoke_iterable_action();\n\n  /// Does a reduction over the component of the reduction status sending the\n  /// result to Main's did_all_elements_terminate member function.\n  void contribute_termination_status_to_main();\n\n  /// Returns the name of the last \"next iterable action\" to be run before a\n  /// deadlock occurred.\n  const std::string& deadlock_analysis_next_iterable_action() const {\n    return deadlock_analysis_next_iterable_action_;\n  }\n\n private:\n  void set_array_index();\n\n  template <typename PhaseDepActions, size_t... Is>\n  constexpr bool iterate_over_actions(std::index_sequence<Is...> /*meta*/);\n\n  template <typename Action, typename... Args, size_t... Is>\n  void forward_tuple_to_action(std::tuple<Args...>&& args,\n                               std::index_sequence<Is...> /*meta*/);\n\n  template <typename Action, typename... Args, size_t... Is>\n  void forward_tuple_to_threaded_action(\n      std::tuple<Args...>&& args, std::index_sequence<Is...> /*meta*/);\n\n  size_t number_of_actions_in_phase(const Parallel::Phase phase) const;\n\n  // After catching an exception, shutdown the simulation\n  void initiate_shutdown(const std::exception& exception);\n\n  // Member variables\n#ifdef SPECTRE_CHARM_PROJECTIONS\n  double non_action_time_start_;\n#endif\n\n  Parallel::CProxy_GlobalCache<metavariables> global_cache_proxy_;\n  bool performing_action_ = false;\n  Parallel::Phase phase_{Parallel::Phase::Initialization};\n  std::unordered_map<Parallel::Phase, size_t> phase_bookmarks_{};\n  std::size_t algorithm_step_ = 0;\n  tmpl::conditional_t<Parallel::is_node_group_proxy<cproxy_type>::value,\n                      Parallel::NodeLock, NoSuchType>\n      node_lock_;\n\n  bool terminate_{true};\n  bool halt_algorithm_until_next_phase_{false};\n\n  // Records the name of the next action to be called so that during deadlock\n  // analysis we can print this out.\n  std::string deadlock_analysis_next_iterable_action_{};\n\n  databox_type box_;\n  inbox_type inboxes_{};\n  array_index array_index_;\n};\n\n////////////////////////////////////////////////////////////////\n// Definitions\n////////////////////////////////////////////////////////////////\n\n/// \\cond\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nDistributedObject<ParallelComponent,\n                  tmpl::list<PhaseDepActionListsPack...>>::DistributedObject() {\n  set_array_index();\n}\n\nnamespace detail {\ninline bool is_zeroth_element(const int array_index) {\n  return 0 == array_index;\n}\n\ntemplate <size_t Dim>\nbool is_zeroth_element(const ElementId<Dim>& array_index) {\n  return ::is_zeroth_element(array_index, std::nullopt);\n}\n}  // namespace detail\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <class... InitializationTags>\nDistributedObject<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::\n    DistributedObject(\n        const Parallel::CProxy_GlobalCache<metavariables>& global_cache_proxy,\n        tuples::TaggedTuple<InitializationTags...> initialization_items)\n    : DistributedObject() {\n  try {\n    if (detail::is_zeroth_element(array_index_)) {\n      const auto check_for_phase = [](auto phase_dep_v) {\n        using PhaseDep = decltype(phase_dep_v);\n        constexpr Parallel::Phase phase = PhaseDep::phase;\n        // PostFailureCleanup is never in the default phase order, but is\n        // controlled by Main rather than PhaseControl\n        if (alg::count(metavariables::default_phase_order, phase) == 0 and\n            phase != Parallel::Phase::PostFailureCleanup) {\n          Parallel::printf(\n              \"NOTE: Phase::%s is in the phase dependent action list of\\n\"\n              \"component %s,\\nbut not in the default_phase_order specified by \"\n              \"the metavariables.\\nThis means that phase will not be executed \"\n              \"unless chosen by PhaseControl.\\n\\n\",\n              phase, pretty_type::name<parallel_component>());\n        }\n      };\n      EXPAND_PACK_LEFT_TO_RIGHT(check_for_phase(PhaseDepActionListsPack{}));\n    }\n    (void)initialization_items;  // avoid potential compiler warnings if unused\n    // When we are using the LoadBalancing phase, we want the Main component to\n    // handle the synchronization, so the components do not participate in the\n    // charm++ `AtSync` barrier.\n    // The array parallel components are migratable so they get balanced\n    // appropriately when load balancing is triggered by the LoadBalancing phase\n    // in Main\n    if constexpr (std::is_same_v<typename ParallelComponent::chare_type,\n                                 Parallel::Algorithms::Array>) {\n      this->usesAtSync = false;\n      this->setMigratable(true);\n    }\n    global_cache_proxy_ = global_cache_proxy;\n    ::Initialization::mutate_assign<\n        tmpl::push_back<distributed_object_tags, InitializationTags...>>(\n        make_not_null(&box_), metavariables{}, array_index_,\n        global_cache_proxy_,\n        std::move(get<InitializationTags>(initialization_items))...);\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nDistributedObject<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::\n    DistributedObject(\n        const Parallel::CProxy_GlobalCache<metavariables>& global_cache_proxy,\n        Parallel::Phase current_phase,\n        std::unordered_map<Parallel::Phase, size_t> phase_bookmarks,\n        const std::unique_ptr<Parallel::Callback>& callback)\n    : DistributedObject() {\n  static_assert(Parallel::is_array_proxy<cproxy_type>::value,\n                \"Can only dynamically add elements to an array component\");\n  try {\n    // When we are using the LoadBalancing phase, we want the Main component to\n    // handle the synchronization, so the components do not participate in the\n    // charm++ `AtSync` barrier.\n    // The array parallel components are migratable so they get balanced\n    // appropriately when load balancing is triggered by the LoadBalancing phase\n    // in Main\n    this->usesAtSync = false;\n    this->setMigratable(true);\n    global_cache_proxy_ = global_cache_proxy;\n    phase_ = current_phase;\n    phase_bookmarks_ = std::move(phase_bookmarks);\n    ::Initialization::mutate_assign<distributed_object_tags>(\n        make_not_null(&box_), metavariables{}, array_index_,\n        global_cache_proxy_);\n    callback->invoke();\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nDistributedObject<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::\n    DistributedObject(CkMigrateMessage* msg)\n    : cbase_type(msg) {}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nDistributedObject<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::\n    ~DistributedObject() {\n  // We place the registrar in the destructor since every DistributedObject will\n  // have a destructor, but we have different constructors so it's not clear\n  // which will be instantiated.\n  (void)Parallel::charmxx::RegisterParallelComponent<\n      ParallelComponent>::registrar;\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nstd::string\nDistributedObject<ParallelComponent,\n                  tmpl::list<PhaseDepActionListsPack...>>::print_types() const {\n  std::ostringstream os;\n  os << \"Algorithm type aliases:\\n\";\n  os << \"using all_actions_list = \" << pretty_type::get_name<all_actions_list>()\n     << \";\\n\";\n\n  os << \"using metavariables = \" << pretty_type::get_name<metavariables>()\n     << \";\\n\";\n  os << \"using inbox_tags_list = \" << pretty_type::get_name<inbox_tags_list>()\n     << \";\\n\";\n  os << \"using array_index = \" << pretty_type::get_name<array_index>() << \";\\n\";\n  os << \"using parallel_component = \"\n     << pretty_type::get_name<parallel_component>() << \";\\n\";\n  os << \"using chare_type = \" << pretty_type::get_name<chare_type>() << \";\\n\";\n  os << \"using cproxy_type = \" << pretty_type::get_name<cproxy_type>() << \";\\n\";\n  os << \"using cbase_type = \" << pretty_type::get_name<cbase_type>() << \";\\n\";\n  os << \"using phase_dependent_action_lists = \"\n     << pretty_type::get_name<phase_dependent_action_lists>() << \";\\n\";\n  os << \"using all_cache_tags = \" << pretty_type::get_name<all_cache_tags>()\n     << \";\\n\";\n  os << \"using databox_type = \" << pretty_type::get_name<databox_type>()\n     << \";\\n\";\n  return os.str();\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nstd::string\nDistributedObject<ParallelComponent,\n                  tmpl::list<PhaseDepActionListsPack...>>::print_state() const {\n  using ::operator<<;\n  std::ostringstream os;\n  os << \"State:\\n\";\n  os << \"performing_action_ = \" << std::boolalpha << performing_action_\n     << \";\\n\";\n  os << \"phase_ = \" << phase_ << \";\\n\";\n  os << \"phase_bookmarks_ = \" << phase_bookmarks_ << \";\\n\";\n  os << \"algorithm_step_ = \" << algorithm_step_ << \";\\n\";\n  os << \"terminate_ = \" << terminate_ << \";\\n\";\n  os << \"halt_algorithm_until_next_phase_ = \"\n     << halt_algorithm_until_next_phase_ << \";\\n\";\n  os << \"array_index_ = \" << array_index_ << \";\\n\";\n  return os.str();\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nstd::string\nDistributedObject<ParallelComponent,\n                  tmpl::list<PhaseDepActionListsPack...>>::print_inbox() const {\n  std::ostringstream os;\n  os << \"inboxes_ = \" << inboxes_ << \";\\n\";\n  return os.str();\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nstd::string DistributedObject<\n    ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::print_databox()\n    const {\n  std::ostringstream os;\n  os << \"box_:\\n\" << box_;\n  return os.str();\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nvoid DistributedObject<\n    ParallelComponent,\n    tmpl::list<PhaseDepActionListsPack...>>::pup(PUP::er& p) {  // NOLINT\n#ifdef SPECTRE_CHARM_PROJECTIONS\n  p | non_action_time_start_;\n#endif\n  if (performing_action_ and not p.isSizing()) {\n    ERROR(\"cannot serialize while performing action!\");\n  }\n  p | performing_action_;\n  p | phase_;\n  p | phase_bookmarks_;\n  p | algorithm_step_;\n  if constexpr (Parallel::is_node_group_proxy<cproxy_type>::value) {\n    p | node_lock_;\n  }\n  p | terminate_;\n  p | halt_algorithm_until_next_phase_;\n  p | array_index_;\n  p | global_cache_proxy_;\n\n  // We have no way to check that everything in the DataBox is\n  // temporary, but anything with non-trivial initialization isn't.\n  static_assert(\n      ParallelComponent::checkpoint_data or\n      std::is_same_v<typename ParallelComponent::simple_tags_from_options,\n                     tmpl::list<>>);\n  if constexpr (ParallelComponent::checkpoint_data) {\n    p | box_;\n    // After unpacking the DataBox, we \"touch\" the GlobalCache proxy inside.\n    // This forces the DataBox to recompute the GlobalCache* the next time it\n    // is needed, but delays this process until after the pupper is called.\n    // (This delay is important: updating the pointer requires calling\n    // ckLocalBranch() on the Charm++ proxy, and in a restart from checkpoint\n    // this call may not be well-defined until after components are finished\n    // unpacking.)\n    if (p.isUnpacking()) {\n      db::mutate<Tags::GlobalCacheProxy<metavariables>>(\n          [](const gsl::not_null<CProxy_GlobalCache<metavariables>*> proxy) {\n            (void)proxy;\n          },\n          make_not_null(&box_));\n    }\n    p | inboxes_;\n    if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {\n      if (phase_ == Parallel::Phase::LoadBalancing) {\n        ERROR(\n            \"Can't do load balacing phase with DG element collection right \"\n            \"now.\");\n      }\n    } else {\n      // Note that `perform_registration_or_deregistration` passes the `box_`\n      // by const reference. If mutable access is required to the box, this\n      // function call needs to be carefully considered with respect to the\n      // `p | box_` call in both packing and unpacking scenarios.\n      //\n      // Note also that we don't perform (de)registrations when pup'ing for a\n      // checkpoint/restart. This enables a simpler first-pass implementation\n      // of checkpointing, though it means the restart must occur on the same\n      // hardware configuration (same number of nodes and same procs per node)\n      // used when writing the checkpoint.\n      if (phase_ == Parallel::Phase::LoadBalancing) {\n        // The deregistration and registration below does not actually insert\n        // anything into the PUP::er stream, so nothing is done on a sizing pup.\n        if (p.isPacking()) {\n          deregister_element<ParallelComponent>(\n              box_, *Parallel::local_branch(global_cache_proxy_), array_index_);\n        }\n        if (p.isUnpacking()) {\n          register_element<ParallelComponent>(\n              box_, *Parallel::local_branch(global_cache_proxy_), array_index_);\n        }\n      }\n    }\n  } else if (p.isUnpacking()) {\n    box_ = decltype(box_){};\n    inboxes_ = decltype(inboxes_){};\n    db::mutate<Tags::GlobalCacheProxy<metavariables>>(\n        [&](const gsl::not_null<CProxy_GlobalCache<metavariables>*> proxy) {\n          *proxy = global_cache_proxy_;\n        },\n        make_not_null(&box_));\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename Action, typename Arg>\nvoid DistributedObject<\n    ParallelComponent,\n    tmpl::list<PhaseDepActionListsPack...>>::reduction_action(Arg arg) {\n  try {\n    (void)Parallel::charmxx::RegisterReductionAction<\n        ParallelComponent, Action, std::decay_t<Arg>>::registrar;\n    {\n      std::optional<std::lock_guard<Parallel::NodeLock>> hold_lock{};\n      if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {\n        hold_lock.emplace(node_lock_);\n      }\n      if (performing_action_) {\n        ERROR(\n            \"Already performing an Action and cannot execute additional \"\n            \"Actions from inside of an Action. This is only possible if the \"\n            \"reduction_action function is not invoked via a proxy, which makes \"\n            \"no sense for a reduction.\");\n      }\n      performing_action_ = true;\n      arg.finalize();\n      forward_tuple_to_action<Action>(\n          std::move(arg.data()), std::make_index_sequence<Arg::pack_size()>{});\n      performing_action_ = false;\n    }\n    perform_algorithm();\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename Action, typename... Args>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    simple_action(std::tuple<Args...> args) {\n  try {\n    (void)Parallel::charmxx::RegisterSimpleAction<ParallelComponent, Action,\n                                                  Args...>::registrar;\n    {\n      std::optional<std::lock_guard<Parallel::NodeLock>> hold_lock{};\n      if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {\n        hold_lock.emplace(node_lock_);\n      }\n      if (performing_action_) {\n        ERROR(\n            \"Already performing an Action and cannot execute additional \"\n            \"Actions from inside of an Action. This is only possible if the \"\n            \"simple_action function is not invoked via a proxy, which \"\n            \"we do not allow.\");\n      }\n      performing_action_ = true;\n      forward_tuple_to_action<Action>(\n          std::move(args), std::make_index_sequence<sizeof...(Args)>{});\n      performing_action_ = false;\n    }\n    perform_algorithm();\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename Action>\nvoid DistributedObject<\n    ParallelComponent,\n    tmpl::list<PhaseDepActionListsPack...>>::simple_action() {\n  try {\n    (void)Parallel::charmxx::RegisterSimpleAction<ParallelComponent,\n                                                  Action>::registrar;\n    {\n      std::optional<std::lock_guard<Parallel::NodeLock>> hold_lock{};\n      if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {\n        hold_lock.emplace(node_lock_);\n      }\n      if (performing_action_) {\n        ERROR(\n            \"Already performing an Action and cannot execute additional \"\n            \"Actions from inside of an Action. This is only possible if the \"\n            \"simple_action function is not invoked via a proxy, which \"\n            \"we do not allow.\");\n      }\n      performing_action_ = true;\n      Action::template apply<ParallelComponent>(\n          box_, *Parallel::local_branch(global_cache_proxy_),\n          static_cast<const array_index&>(array_index_));\n      performing_action_ = false;\n    }\n    perform_algorithm();\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename Action, typename... Args>\ntypename Action::return_type\nDistributedObject<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::\n    local_synchronous_action(Args&&... args) {\n  static_assert(Parallel::is_node_group_proxy<cproxy_type>::value,\n                \"Cannot call a (blocking) local synchronous action on a \"\n                \"chare that is not a NodeGroup\");\n  return Action::template apply<ParallelComponent>(\n      box_, make_not_null(&node_lock_), std::forward<Args>(args)...);\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename Action>\nvoid DistributedObject<\n    ParallelComponent,\n    tmpl::list<PhaseDepActionListsPack...>>::threaded_action() {\n  try {\n    // NOLINTNEXTLINE(modernize-redundant-void-arg)\n    (void)Parallel::charmxx::RegisterThreadedAction<ParallelComponent,\n                                                    Action>::registrar;\n    if constexpr (Parallel::is_dg_element_collection_v<parallel_component>) {\n      Action::template apply<ParallelComponent>(\n          box_, *Parallel::local_branch(global_cache_proxy_),\n          static_cast<const array_index&>(array_index_),\n          make_not_null(&node_lock_), this);\n    } else {\n      Action::template apply<ParallelComponent>(\n          box_, *Parallel::local_branch(global_cache_proxy_),\n          static_cast<const array_index&>(array_index_),\n          make_not_null(&node_lock_));\n    }\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename ReceiveTag, typename ReceiveDataType>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    receive_data(typename ReceiveTag::temporal_id instance, ReceiveDataType&& t,\n                 const bool enable_if_disabled) {\n  try {\n    (void)Parallel::charmxx::RegisterReceiveData<ParallelComponent, ReceiveTag,\n                                                 false>::registrar;\n    bool do_perform_algorithm = false;\n    {\n      std::optional<std::lock_guard<Parallel::NodeLock>> hold_lock{};\n      if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {\n        hold_lock.emplace(node_lock_);\n      }\n      if (enable_if_disabled) {\n        set_terminate(false);\n      }\n      do_perform_algorithm = ReceiveTag::insert_into_inbox(\n          make_not_null(&tuples::get<ReceiveTag>(inboxes_)), instance,\n          std::forward<ReceiveDataType>(t));\n    }\n    if (do_perform_algorithm) {\n      perform_algorithm();\n    }\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename ReceiveTag, typename MessageType>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    receive_data(MessageType* message) {\n  try {\n    (void)Parallel::charmxx::RegisterReceiveData<ParallelComponent, ReceiveTag,\n                                                 true>::registrar;\n    bool do_perform_algorithm = false;\n    {\n      std::optional<std::lock_guard<Parallel::NodeLock>> hold_lock{};\n      if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {\n        hold_lock.emplace(node_lock_);\n      }\n      if (message->enable_if_disabled) {\n        set_terminate(false);\n      }\n      do_perform_algorithm = ReceiveTag::insert_into_inbox(\n          make_not_null(&tuples::get<ReceiveTag>(inboxes_)), message);\n      // Cannot use message after this call because a std::unique_ptr now owns\n      // it. Doing so would result in undefined behavior\n    }\n    if (do_perform_algorithm) {\n      perform_algorithm();\n    }\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nvoid DistributedObject<\n    ParallelComponent,\n    tmpl::list<PhaseDepActionListsPack...>>::perform_algorithm() {\n  try {\n    if (performing_action_ or get_terminate() or\n        halt_algorithm_until_next_phase_) {\n      return;\n    }\n#ifdef SPECTRE_CHARM_PROJECTIONS\n    non_action_time_start_ = sys::wall_time();\n#endif\n    {\n      std::optional<std::lock_guard<Parallel::NodeLock>> hold_lock{};\n      if constexpr (std::is_same_v<Parallel::NodeLock, decltype(node_lock_)>) {\n        hold_lock.emplace(node_lock_);\n      }\n      const auto invoke_for_phase = [this](auto phase_dep_v) {\n        using PhaseDep = decltype(phase_dep_v);\n        constexpr Parallel::Phase phase = PhaseDep::phase;\n        using actions_list = typename PhaseDep::action_list;\n        if (phase_ == phase) {\n          while (tmpl::size<actions_list>::value > 0 and not get_terminate() and\n                 not halt_algorithm_until_next_phase_ and\n                 iterate_over_actions<PhaseDep>(\n                     std::make_index_sequence<\n                         tmpl::size<actions_list>::value>{})) {\n          }\n          tmpl::for_each<actions_list>([this](auto action_v) {\n            using action = tmpl::type_from<decltype(action_v)>;\n            if (algorithm_step_ ==\n                tmpl::index_of<actions_list, action>::value) {\n              deadlock_analysis_next_iterable_action_ =\n                  pretty_type::name<action>();\n            }\n          });\n        }\n      };\n      // Loop over all phases, once the current phase is found we perform the\n      // algorithm in that phase until we are no longer able to because we are\n      // waiting on data to be sent or because the algorithm has been marked as\n      // terminated.\n      EXPAND_PACK_LEFT_TO_RIGHT(invoke_for_phase(PhaseDepActionListsPack{}));\n    }\n#ifdef SPECTRE_CHARM_PROJECTIONS\n    traceUserBracketEvent(SPECTRE_CHARM_NON_ACTION_WALLTIME_EVENT_ID,\n                          non_action_time_start_, sys::wall_time());\n#endif\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    perform_algorithm(const bool restart_if_terminated) {\n  try {\n    if (restart_if_terminated) {\n      set_terminate(false);\n    }\n    perform_algorithm();\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    start_phase(const Parallel::Phase next_phase, const bool force) {\n  try {\n    // An algorithm must always be set to terminate at the beginning of a phase,\n    // otherwise it's an error (see below). Therefore if we want to force a\n    // phase start, we must set terminate to true regardless of what it was\n    // before.\n    if (force) {\n      set_terminate(true);\n    }\n    // terminate should be true since we exited a phase previously.\n    if (not get_terminate() and not halt_algorithm_until_next_phase_) {\n      ERROR(\n          \"An algorithm must always be set to terminate at the beginning of a \"\n          \"phase. Since this is not the case the previous phase did not end \"\n          \"correctly. The previous phase is: \"\n          << phase_ << \" and the next phase is: \" << next_phase\n          << \", The termination flag is: \" << get_terminate()\n          << \", and the halt flag is: \" << halt_algorithm_until_next_phase_);\n    }\n    // set terminate to true if there are no actions in this PDAL\n    set_terminate(number_of_actions_in_phase(next_phase) == 0);\n\n    // Ideally, we'd set the bookmarks as we are leaving a phase, but there is\n    // no 'clean-up' code that we run when departing a phase, so instead we set\n    // the bookmark for the previous phase (still stored in `phase_` at this\n    // point), before we update the member variable `phase_`.\n    // Then, after updating `phase_`, we check if we've ever stored a bookmark\n    // for the new phase previously. If so, we start from where we left off,\n    // otherwise, start from the beginning of the action list.\n    phase_bookmarks_[phase_] = algorithm_step_;\n    phase_ = next_phase;\n    if (phase_bookmarks_.count(phase_) != 0) {\n      algorithm_step_ = phase_bookmarks_.at(phase_);\n    } else {\n      algorithm_step_ = 0;\n    }\n    halt_algorithm_until_next_phase_ = false;\n    perform_algorithm();\n  } catch (const std::exception& exception) {\n    initiate_shutdown(exception);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nvoid DistributedObject<\n    ParallelComponent,\n    tmpl::list<PhaseDepActionListsPack...>>::set_array_index() {\n  // down cast to the algorithm_type, so that the `thisIndex` method can be\n  // called, which is defined in the CBase class\n  array_index_ = static_cast<typename chare_type::template algorithm_type<\n      ParallelComponent, array_index>&>(*this)\n                     .thisIndex;\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename PhaseDepActions, size_t... Is>\nconstexpr bool\nDistributedObject<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::\n    iterate_over_actions(const std::index_sequence<Is...> /*meta*/) {\n  bool take_next_action = true;\n  const auto helper = [this, &take_next_action](auto iteration) {\n    constexpr size_t iter = decltype(iteration)::value;\n    if (not(take_next_action and not terminate_ and\n            not halt_algorithm_until_next_phase_ and algorithm_step_ == iter)) {\n      return;\n    }\n    using actions_list = typename PhaseDepActions::action_list;\n    using this_action = tmpl::at_c<actions_list, iter>;\n\n    constexpr size_t phase_index =\n        tmpl::index_of<phase_dependent_action_lists, PhaseDepActions>::value;\n    performing_action_ = true;\n    ++algorithm_step_;\n    // While the overhead from using the local entry method to enable\n    // profiling is fairly small (<2%), we still avoid it when we aren't\n    // tracing.\n#ifdef SPECTRE_CHARM_PROJECTIONS\n    if constexpr (Parallel::is_array<parallel_component>::value) {\n      if (not this->thisProxy[array_index_]\n                  .template invoke_iterable_action<\n                      this_action, std::integral_constant<size_t, phase_index>,\n                      std::integral_constant<size_t, iter>>()) {\n        take_next_action = false;\n        --algorithm_step_;\n      }\n    } else {\n#endif  // SPECTRE_CHARM_PROJECTIONS\n      if (not invoke_iterable_action<\n              this_action, std::integral_constant<size_t, phase_index>,\n              std::integral_constant<size_t, iter>>()) {\n        take_next_action = false;\n        --algorithm_step_;\n      }\n#ifdef SPECTRE_CHARM_PROJECTIONS\n    }\n#endif  // SPECTRE_CHARM_PROJECTIONS\n    performing_action_ = false;\n    // Wrap counter if necessary\n    if (algorithm_step_ >= tmpl::size<actions_list>::value) {\n      algorithm_step_ = 0;\n    }\n  };\n  // In case of no Actions avoid compiler warning.\n  (void)helper;\n  // This is a template for loop for Is\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(std::integral_constant<size_t, Is>{}));\n  return take_next_action;\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename Action, typename... Args, size_t... Is>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    forward_tuple_to_action(std::tuple<Args...>&& args,\n                            std::index_sequence<Is...> /*meta*/) {\n  Action::template apply<ParallelComponent>(\n      box_, *Parallel::local_branch(global_cache_proxy_),\n      static_cast<const array_index&>(array_index_),\n      std::forward<Args>(std::get<Is>(args))...);\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename Action, typename... Args, size_t... Is>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    forward_tuple_to_threaded_action(std::tuple<Args...>&& args,\n                                     std::index_sequence<Is...> /*meta*/) {\n  const gsl::not_null<Parallel::NodeLock*> node_lock{&node_lock_};\n  if constexpr (Parallel::is_dg_element_collection_v<parallel_component>) {\n    Action::template apply<ParallelComponent>(\n        box_, *Parallel::local_branch(global_cache_proxy_),\n        static_cast<const array_index&>(array_index_), node_lock, this,\n        std::forward<Args>(std::get<Is>(args))...);\n  } else {\n    Action::template apply<ParallelComponent>(\n        box_, *Parallel::local_branch(global_cache_proxy_),\n        static_cast<const array_index&>(array_index_), node_lock,\n        std::forward<Args>(std::get<Is>(args))...);\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nsize_t\nDistributedObject<ParallelComponent, tmpl::list<PhaseDepActionListsPack...>>::\n    number_of_actions_in_phase(const Parallel::Phase phase) const {\n  size_t number_of_actions = 0;\n  const auto helper = [&number_of_actions, phase](auto pdal_v) {\n    if (pdal_v.phase == phase) {\n      number_of_actions = pdal_v.number_of_actions;\n    }\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(PhaseDepActionListsPack{}));\n  return number_of_actions;\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\ntemplate <typename ThisAction, typename PhaseIndex, typename DataBoxIndex>\nbool DistributedObject<\n    ParallelComponent,\n    tmpl::list<PhaseDepActionListsPack...>>::invoke_iterable_action() {\n  using phase_dep_action =\n      tmpl::at_c<phase_dependent_action_lists, PhaseIndex::value>;\n  using actions_list = typename phase_dep_action::action_list;\n\n#ifdef SPECTRE_CHARM_PROJECTIONS\n  if constexpr (Parallel::is_array<parallel_component>::value) {\n    (void)Parallel::charmxx::RegisterInvokeIterableAction<\n        ParallelComponent, ThisAction, PhaseIndex, DataBoxIndex>::registrar;\n  }\n#endif // SPECTRE_CHARM_PROJECTIONS\n\n  AlgorithmExecution requested_execution{};\n  std::optional<std::size_t> next_action_step{};\n  std::tie(requested_execution, next_action_step) = ThisAction::apply(\n      box_, inboxes_, *Parallel::local_branch(global_cache_proxy_),\n      std::as_const(array_index_), actions_list{},\n      std::add_pointer_t<ParallelComponent>{});\n\n  if (next_action_step.has_value()) {\n    ASSERT(\n        AlgorithmExecution::Retry != requested_execution,\n        \"Switching actions on Retry doesn't make sense. Specify std::nullopt \"\n        \"as the second argument of the iterable action return type\");\n    algorithm_step_ = next_action_step.value();\n  }\n\n  switch (requested_execution) {\n    case AlgorithmExecution::Continue:\n      return true;\n    case AlgorithmExecution::Retry:\n      return false;\n    case AlgorithmExecution::Pause:\n      terminate_ = true;\n      return true;\n    case AlgorithmExecution::Halt:\n      halt_algorithm_until_next_phase_ = true;\n      terminate_ = true;\n      return true;\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"No case for a Parallel::AlgorithmExecution with integral value \"\n            << static_cast<std::underlying_type_t<AlgorithmExecution>>(\n                   requested_execution)\n            << \"\\n\");\n      // LCOV_EXCL_STOP\n  }\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    contribute_termination_status_to_main() {\n  auto* global_cache = Parallel::local_branch(global_cache_proxy_);\n  if (UNLIKELY(global_cache == nullptr)) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n    CkError(\n        \"Global cache pointer is null. This is an internal inconsistency \"\n        \"error. Please file an issue.\");\n    sys::abort(\"\");\n  }\n  auto main_proxy = global_cache->get_main_proxy();\n  if (UNLIKELY(not main_proxy.has_value())) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n    CkError(\n        \"The main proxy has not been set in the global cache when \"\n        \"checking that all components have terminated. This is an internal \"\n        \"inconsistency error. Please file an issue.\");\n    sys::abort(\"\");\n  }\n  CkCallback cb(\n      CkReductionTarget(Main<metavariables>, did_all_elements_terminate),\n      main_proxy.value());\n  this->contribute(sizeof(bool), &terminate_, CkReduction::logical_and_bool,\n                   cb);\n}\n\ntemplate <typename ParallelComponent, typename... PhaseDepActionListsPack>\nvoid DistributedObject<ParallelComponent,\n                       tmpl::list<PhaseDepActionListsPack...>>::\n    initiate_shutdown(const std::exception& exception) {\n  // In order to make it so that we can later run other actions for cleanup\n  // (e.g. dumping data) we need to make sure that we enable running actions\n  // again\n  performing_action_ = false;\n  // Send message to `Main` that we received an exception and set termination.\n  auto* global_cache = Parallel::local_branch(global_cache_proxy_);\n  if (UNLIKELY(global_cache == nullptr)) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n    CkError(\n        \"Global cache pointer is null. This is an internal inconsistency \"\n        \"error. Please file an issue.\");\n    sys::abort(\"\");\n  }\n  auto main_proxy = global_cache->get_main_proxy();\n  if (UNLIKELY(not main_proxy.has_value())) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n    CkError(\n        \"The main proxy has not been set in the global cache when terminating \"\n        \"the component. This is an internal inconsistency error. Please file \"\n        \"an issue.\");\n    sys::abort(\"\");\n  }\n  const std::string message =\n      MakeString{} << \"Component: \" << pretty_type::name<parallel_component>()\n                   << \"\\nArray Index: \" << array_index_ << \"\\n\"\n                   << \"Phase: \" << phase_ << \"\\n\"\n                   << \"Algorithm Step: \" << algorithm_step_ << \"\\n\"\n                   << \"Message: \" << exception.what() << \"\\nType: \"\n                   << pretty_type::get_runtime_type_name(exception);\n  main_proxy.value().add_exception_message(message);\n  set_terminate(true);\n}\n/// \\endcond\n\ntemplate <typename ParallelComponent, typename PhaseDepActionLists>\nstd::ostream& operator<<(\n    std::ostream& os,\n    const DistributedObject<ParallelComponent, PhaseDepActionLists>&\n        algorithm_impl) {\n  os << algorithm_impl.print_types() << \"\\n\";\n  os << algorithm_impl.print_state() << \"\\n\";\n  os << algorithm_impl.print_inbox() << \"\\n\";\n  os << algorithm_impl.print_databox() << \"\\n\";\n  return os;\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/DomainDiagnosticInfo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain {\n/// Returns a `std::string` with diagnostic information about how elements and\n/// grid points are distributed on the nodes and cores.\ntemplate <typename Metavariables>\nstd::string diagnostic_info(const size_t total_number_of_blocks,\n                            const Parallel::GlobalCache<Metavariables>& cache,\n                            const std::vector<size_t>& elements_per_core,\n                            const std::vector<size_t>& elements_per_node,\n                            const std::vector<size_t>& grid_points_per_core,\n                            const std::vector<size_t>& grid_points_per_node) {\n  const size_t total_number_of_elements =\n      alg::accumulate(elements_per_node, 0_st);\n  const size_t total_number_of_grid_points =\n      alg::accumulate(grid_points_per_node, 0_st);\n  const size_t number_of_cores = Parallel::number_of_procs<size_t>(cache);\n  const size_t number_of_nodes = Parallel::number_of_nodes<size_t>(cache);\n\n  // Sanity checks\n  ASSERT(total_number_of_elements == alg::accumulate(elements_per_core, 0_st),\n         \"Number of elements determined from elements_per_core (\"\n             << alg::accumulate(elements_per_core, 0_st)\n             << \") does not match number of elements determined from \"\n                \"elements_per_node (\"\n             << total_number_of_elements << \").\");\n  ASSERT(total_number_of_grid_points ==\n             alg::accumulate(grid_points_per_core, 0_st),\n         \"Number of grid points determined from grid_points_per_core (\"\n             << alg::accumulate(grid_points_per_core, 0_st)\n             << \") does not match number of grid points determined from \"\n                \"grid points_per_node (\"\n             << total_number_of_grid_points << \").\");\n  ASSERT(number_of_cores == elements_per_core.size(),\n         \"Number of cores determined from elements_per_core (\"\n             << elements_per_core.size()\n             << \") does not match number of cores determined from \"\n                \"global cache (\"\n             << number_of_cores << \").\");\n  ASSERT(number_of_cores == grid_points_per_core.size(),\n         \"Number of cores determined from grid_points_per_core (\"\n             << grid_points_per_core.size()\n             << \") does not match number of cores determined from \"\n                \"global cache (\"\n             << number_of_cores << \").\");\n  ASSERT(number_of_nodes == elements_per_node.size(),\n         \"Number of nodes determined from elements_per_node (\"\n             << elements_per_node.size()\n             << \") does not match number of nodes determined from \"\n                \"global cache (\"\n             << number_of_nodes << \").\");\n  ASSERT(number_of_nodes == grid_points_per_node.size(),\n         \"Number of nodes determined from grid_points_per_node (\"\n             << grid_points_per_node.size()\n             << \") does not match number of nodes determined from \"\n                \"global cache (\"\n             << number_of_nodes << \").\");\n\n  using ::operator<<;\n  std::stringstream ss{};\n  ss << \"----- Domain Info -----\\n\"\n     << \"Total blocks: \" << total_number_of_blocks << \"\\n\"\n     << \"Total elements: \" << total_number_of_elements << \"\\n\"\n     << \"Total grid points: \" << total_number_of_grid_points << \"\\n\"\n     << \"Number of cores: \" << number_of_cores << \"\\n\"\n     << \"Number of nodes: \" << number_of_nodes << \"\\n\"\n     << \"Elements per core: \" << elements_per_core << \"\\n\"\n     << \"Elements per node: \" << elements_per_node << \"\\n\"\n     << \"Grid points per core: \" << grid_points_per_core << \"\\n\"\n     << \"Grid points per node: \" << grid_points_per_node << \"\\n\"\n     << \"-----------------------\\n\";\n\n  return ss.str();\n}\n}  // namespace domain\n"
  },
  {
    "path": "src/Parallel/ElementRegistration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Parallel/Protocols/ElementRegistrar.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n\n/// \\cond\nnamespace db {\ntemplate <typename DbTagList>\nclass DataBox;\n}  // namespace db\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Parallel {\nnamespace detail {\nCREATE_HAS_TYPE_ALIAS(registration)\nCREATE_HAS_TYPE_ALIAS_V(registration)\n}  // namespace detail\n\n/// @{\n/// \\brief (De)register an array element for specified actions\n///\n/// \\details Array elements are (de)registered with actions on components that\n/// need to know which elements are contributing data before the action can be\n/// executed.  If array elements are migrated (e.g. during load balancing),\n/// or are created/destroyed (e.g. during adaptive mesh refinement), these\n/// functions must be called in order to (un)register (old) new elements.\n/// The list of registration actions is obtained from\n/// `Metavariables::registration::element_registrars`.\n///\n/// \\see Parallel::protocols::RegistrationMetavariables\n/// \\see Parallel::protocols::ElementRegistrar\ntemplate <typename ParallelComponent, typename DbTagList,\n          typename Metavariables, typename ArrayIndex>\nvoid deregister_element(db::DataBox<DbTagList>& box,\n                        Parallel::GlobalCache<Metavariables>& cache,\n                        const ArrayIndex& array_index) {\n  if constexpr (detail::has_registration_v<Metavariables>) {\n    static_assert(tt::assert_conforms_to_v<\n                  typename Metavariables::registration,\n                  Parallel::protocols::RegistrationMetavariables>);\n    using element_registrars =\n        typename Metavariables::registration::element_registrars;\n    if constexpr (tmpl::has_key<element_registrars, ParallelComponent>::value) {\n      tmpl::for_each<tmpl::at<element_registrars, ParallelComponent>>(\n          [&box, &cache, &array_index](auto registration_v) {\n            using registration = typename decltype(registration_v)::type;\n            static_assert(tt::assert_conforms_to_v<\n                          registration, Parallel::protocols::ElementRegistrar>);\n            registration::template perform_deregistration<ParallelComponent>(\n                box, cache, array_index);\n          });\n    }\n  }\n}\n\ntemplate <typename ParallelComponent, typename DbTagList,\n          typename Metavariables, typename ArrayIndex>\nvoid register_element(db::DataBox<DbTagList>& box,\n                      Parallel::GlobalCache<Metavariables>& cache,\n                      const ArrayIndex& array_index) {\n  if constexpr (detail::has_registration_v<Metavariables>) {\n    static_assert(tt::assert_conforms_to_v<\n                  typename Metavariables::registration,\n                  Parallel::protocols::RegistrationMetavariables>);\n    using element_registrars =\n        typename Metavariables::registration::element_registrars;\n    if constexpr (tmpl::has_key<element_registrars, ParallelComponent>::value) {\n      tmpl::for_each<tmpl::at<element_registrars, ParallelComponent>>(\n          [&box, &cache, &array_index](auto registration_v) {\n            using registration = typename decltype(registration_v)::type;\n            static_assert(tt::assert_conforms_to_v<\n                          registration, Parallel::protocols::ElementRegistrar>);\n            registration::template perform_registration<ParallelComponent>(\n                box, cache, array_index);\n          });\n    }\n  }\n}\n/// @}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ExitCode.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/ExitCode.hpp\"\n\n#include <ostream>\n#include <type_traits>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Parallel {\nstd::ostream& operator<<(std::ostream& os, const ExitCode& code) {\n  os << static_cast<std::underlying_type_t<ExitCode>>(code);\n  switch (code) {\n    case Parallel::ExitCode::Complete:\n      return os << \" (Complete)\";\n    case Parallel::ExitCode::Abort:\n      return os << \" (Abort)\";\n    case Parallel::ExitCode::ContinueFromCheckpoint:\n      return os << \" (ContinueFromCheckpoint)\";\n    default:\n      ERROR(\"Unknown exit code: \"\n            << static_cast<std::underlying_type_t<ExitCode>>(code));\n  }\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ExitCode.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace Parallel {\n\n/*!\n * \\brief Exit code of an executable\n *\n * \\warning Don't change the integer values of the enum cases unless you have a\n * very good reason to do so. The integer values are used by external code, so\n * this is a public interface that should remain stable.\n */\nenum class ExitCode : int {\n  /// Program is complete\n  Complete = 0,\n  /// Program aborted because of an error\n  Abort = 1,\n  /// Program is incomplete and should be continued from the last checkpoint\n  ContinueFromCheckpoint = 2\n};\n\nstd::ostream& operator<<(std::ostream& os, const ExitCode& code);\n\nnamespace Tags {\n\n/*!\n * \\brief Exit code of an executable\n *\n * \\see Parallel::ExitCode\n */\nstruct ExitCode : db::SimpleTag {\n  using type = Parallel::ExitCode;\n};\n\n}  // namespace Tags\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/FifoCache.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <atomic>\n#include <concepts>\n#include <cstddef>\n#include <cstdint>\n#include <memory>\n#include <mutex>\n#include <new>  // for hardware_destructive_interference_size\n#include <stdexcept>\n#include <utility>\n#include <vector>\n\n#include \"Parallel/MultiReaderSpinlock.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel {\n/*!\n * \\brief A threadsafe parallel first-in-first-out cache.\n */\ntemplate <class T>\nclass FifoCache {\n  using stored_type = std::pair<MultiReaderSpinlock, T>;\n  using value_type = std::vector<stored_type>;\n\n public:\n  /*!\n   * \\brief Wrapper type used as the result from `find` to ensure correct\n   * thread safety.\n   *\n   * Use `.value()` to get the stored value.\n   *\n   * Use `.has_value()` to check if a value is stored.\n   */\n  struct Cached {\n   public:\n    Cached() = delete;\n    explicit Cached(const stored_type* t);\n    /// NOLINTNEXTLINE(google-explicit-constructor)\n    Cached(const std::nullopt_t /*unused*/) : Cached{nullptr} {}\n    Cached(const Cached& rhs);\n    Cached& operator=(const Cached& rhs);\n    Cached(Cached&& rhs) noexcept(true);\n    Cached& operator=(Cached&& rhs) noexcept(true);\n    ~Cached() noexcept(true);\n\n    /// \\brief Returns a reference to the held object.\n    ///\n    /// \\throws std::runtime_error if no value\n    auto value() const -> const T&;\n\n    /// \\brief Returns `true` if a value is stored, otherwise returns `false`.\n    bool has_value() const { return t_ != nullptr; }\n\n   private:\n    stored_type* t_;\n  };\n\n  /// \\brief Create a FifoCache that has \\p capacity.\n  explicit FifoCache(std::unsigned_integral auto capacity);\n\n  FifoCache() = delete;\n  FifoCache(const FifoCache& rhs) = delete;\n  FifoCache& operator=(const FifoCache& rhs) = delete;\n  FifoCache(FifoCache&& rhs) = delete;\n  FifoCache& operator=(FifoCache&& rhs) = delete;\n  ~FifoCache() = default;\n\n  /*!\n   * \\brief Pushes the entry computed by `compute_value()` to the front of the\n   * queue, ejecting the last entry if the capacity is reached. The inserted\n   * entry is returned.\n   *\n   * This function allows lazy computation of the value, which means the\n   * computation is elided if another thread pushes the new cache entry before\n   * this one.\n   *\n   * The predicate must satisfy `predicate(t) == true` to avoid inserting\n   * duplicates. This is best guaranteed by passing the same predicate that\n   * would be passed to calls to `find()`.\n   */\n  template <class ComputeValue, class UnaryPredicate>\n  auto push(ComputeValue&& compute_value, const UnaryPredicate& predicate)\n      -> Cached;\n\n  /*!\n   * \\brief Pushes the entry `t` to the front of the queue,\n   * ejecting the last entry if the capacity is reached. The inserted\n   * entry is returned.\n   *\n   * The predicate must satisfy `predicate(t) == true` to avoid inserting\n   * duplicates. This is best guaranteed by passing the same predicate that\n   * would be passed to calls to `find()`.\n   */\n  template <class UnaryPredicate>\n  auto push(T t, const UnaryPredicate& predicate) -> Cached;\n\n  /*!\n   * \\brief Get the first element that matches `predicate`.\n   *\n   * If no value in the cache matches the predicate then `result.has_value()`\n   * is `false` and `result.value()` will throw.\n   *\n   * The return type is designed to handle the locking and unlocking\n   * of the data to ensure thread safety.\n   */\n  template <class UnaryPredicate>\n  [[nodiscard]] auto find(const UnaryPredicate& predicate) const -> Cached;\n\n private:\n#if defined(__cpp_lib_hardware_interference_size)\n  static constexpr size_t cache_line_size_ =\n      std::hardware_destructive_interference_size;\n#else\n  static constexpr size_t cache_line_size_ = 64;\n#endif\n\n  // std::vector does not have an atomic size so in order to prevent tearing\n  // we have to track the size separately.\n  alignas(cache_line_size_) std::atomic<std::uint64_t> size_{0};\n  alignas(cache_line_size_) std::mutex write_lock_{};\n  alignas(cache_line_size_) value_type data_{};\n  // Ensure we pad the end to avoid false sharing. Unlikely to happen because\n  // of the layout, but better to be safe.\n#if defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-private-field\"\n#endif\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  char padding_[cache_line_size_ - sizeof(value_type) % cache_line_size_] = {};\n#if defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n};\n\ntemplate <class T>\nFifoCache<T>::FifoCache(const std::unsigned_integral auto capacity)\n    : data_(capacity) {\n  ASSERT(capacity > 0, \"Must have a positive capacity but got \" << capacity);\n}\n\ntemplate <class T>\ntemplate <class ComputeValue, class UnaryPredicate>\nauto FifoCache<T>::push(ComputeValue&& compute_value,\n                        const UnaryPredicate& predicate) -> Cached {\n  std::lock_guard guard(write_lock_);\n  auto size = size_.load(std::memory_order_acquire);\n  for (size_t i = 0; i < size; ++i) {\n    Cached vt{std::addressof(data_[i])};\n    if (predicate(vt.value())) {\n      return {vt};\n    }\n  }\n\n  // Compute the new value _before_ locking data to minimize locked time.\n  T new_value = std::forward<ComputeValue>(compute_value)();\n  if (size < data_.capacity()) {\n    ++size;\n  }\n  data_[size - 1].first.write_lock();\n  for (size_t i = size - 1; i > 0; --i) {\n    data_[i - 1].first.write_lock();\n    data_[i].second = std::move(data_[i - 1].second);\n  }\n  data_[0].second = std::move(new_value);\n  for (size_t i = 0; i < size; ++i) {\n    data_[i].first.write_unlock();\n  }\n  size_.store(size, std::memory_order_release);\n  return find(predicate);\n}\n\ntemplate <class T>\ntemplate <class UnaryPredicate>\nauto FifoCache<T>::push(T t, const UnaryPredicate& predicate) -> Cached {\n  return push(\n      [t_local = std::move(t)]() mutable -> T  // NOLINT(spectre-mutable)\n      { return std::move(t_local); },\n      predicate);\n}\n\ntemplate <class T>\ntemplate <class UnaryPredicate>\n[[nodiscard]] auto FifoCache<T>::find(const UnaryPredicate& predicate) const\n    -> Cached {\n  const auto size = size_.load(std::memory_order_relaxed);\n  for (size_t i = 0; i < size; ++i) {\n    Cached vt{std::addressof(data_[i])};\n    if (predicate(vt.value())) {\n      return vt;\n    }\n  }\n  return Cached{nullptr};\n}\n\ntemplate <class T>\nFifoCache<T>::Cached::Cached(const stored_type* t)\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n    : t_(const_cast<stored_type*>(t)) {\n  if (t_ != nullptr) {\n    t_->first.read_lock();\n  }\n}\n\ntemplate <class T>\nFifoCache<T>::Cached::Cached(const Cached& rhs) : t_(rhs.t_) {\n  if (t_ != nullptr) {\n    t_->first.read_lock();\n  }\n}\n\ntemplate <class T>\ntypename FifoCache<T>::Cached& FifoCache<T>::Cached::operator=(\n    const Cached& rhs) {\n  if (&rhs == this) {\n    return *this;\n  }\n  if (t_ != nullptr) {\n    t_->first.read_unlock();\n  }\n  t_ = rhs.t_;\n  if (t_ != nullptr) {\n    t_->first.read_lock();\n  }\n  return *this;\n}\n\ntemplate <class T>\nFifoCache<T>::Cached::Cached(Cached&& rhs) noexcept(true) : t_(rhs.t_) {\n  rhs.t_ = nullptr;\n}\n\ntemplate <class T>\ntypename FifoCache<T>::Cached& FifoCache<T>::Cached::operator=(\n    Cached&& rhs) noexcept(true) {\n  if (&rhs == this) {\n    return *this;\n  }\n  if (t_ != nullptr) {\n    t_->first.read_unlock();\n  }\n  t_ = rhs.t_;\n  rhs.t_ = nullptr;\n  return *this;\n}\n\ntemplate <class T>\nFifoCache<T>::Cached::~Cached() noexcept(true) {\n  if (t_ != nullptr) {\n    t_->first.read_unlock();\n  }\n}\n\ntemplate <class T>\nauto FifoCache<T>::Cached::value() const -> const T& {\n  if (UNLIKELY(not has_value())) {\n    throw std::runtime_error{\"No value in FifoCache.\"};\n  }\n  return t_->second;\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/GetSection.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel {\n\n/*!\n * \\brief Retrieve the section that the element belongs to, or\n * `Parallel::no_section()` if `SectionIdTag` is `void`.\n *\n * This function is useful to support sections in parallel algorithms. Specify\n * the `SectionIdTag` template parameter to retrieve the associated section, or\n * set it to `void` when the parallel algorithm runs over all elements of the\n * parallel component. See `Parallel::Section` for details on sections.\n *\n * Only call this function on elements that are part of a section. In case not\n * all elements are part of a section with the `SectionIdTag`, make sure to skip\n * those elements before calling this function.\n */\ntemplate <typename ParallelComponent, typename SectionIdTag,\n          typename DbTagsList>\nauto& get_section(\n    [[maybe_unused]] const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n  if constexpr (std::is_same_v<SectionIdTag, void>) {\n    return Parallel::no_section();\n  } else {\n    std::optional<Parallel::Section<ParallelComponent, SectionIdTag>>& section =\n        db::get_mutable_reference<\n            Parallel::Tags::Section<ParallelComponent, SectionIdTag>>(box);\n    ASSERT(section.has_value(),\n           \"Call 'get_section' only on elements that belong to a section. This \"\n           \"is probably a bug, because the other elements should presumably be \"\n           \"skipped. The section is: \"\n               << db::tag_name<SectionIdTag>());\n    return *section;\n  }\n}\n\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/GlobalCache.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmodule GlobalCache {\n\n  include \"DataStructures/TaggedTuple.hpp\";\n  include \"optional\";\n  include \"string\";\n  include \"Parallel/GlobalCacheDeclare.hpp\";\n  include \"Parallel/ParallelComponentHelpers.hpp\";\n  include \"Parallel/ResourceInfo.hpp\";\n  include \"Parallel/Main.decl.h\";\n\n  namespace Parallel {\n  template <typename Metavariables>\n  nodegroup [migratable] GlobalCache {\n    entry GlobalCache(tuples::tagged_tuple_from_typelist<\n                        get_const_global_cache_tags<Metavariables>>&,\n                    tuples::tagged_tuple_from_typelist<\n                        get_mutable_global_cache_tags<Metavariables>>&,\n                    std::optional<CProxy_Main<Metavariables>>);\n    entry void set_parallel_components(\n        tuples::tagged_tuple_from_typelist<tmpl::transform<\n            typename Metavariables::component_list,\n            tmpl::bind<tmpl::type_,\n                       tmpl::bind<Parallel::proxy_from_parallel_component,\n                                  tmpl::_1>>>>,\n        const CkCallback&);\n    template <typename GlobalCacheTag, typename Function, typename... Args>\n    entry void mutate(std::tuple<Args...> & args);\n    entry void compute_size_for_memory_monitor(double time);\n    entry void set_resource_info(\n        const Parallel::ResourceInfo<Metavariables>& resource_info);\n    entry void overlay_cache_data(\n        tuples::tagged_tuple_from_typelist<\n            Parallel::get_overlayable_option_list<Metavariables>>&);\n    entry void print_mutable_cache_callbacks(const std::string& file_name);\n  }\n  }  // namespace Parallel\n}\n"
  },
  {
    "path": "src/Parallel/GlobalCache.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class template GlobalCache.\n\n#pragma once\n\n#include <charm++.h>\n#include <memory>\n#include <mutex>\n#include <numeric>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/CharmRegistration.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/ResourceInfo.hpp\"\n#include \"Parallel/Tags/ResourceInfo.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers/RetrieveUniquePtr.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n#include \"Parallel/GlobalCache.decl.h\"\n#include \"Parallel/Main.decl.h\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\nnamespace mem_monitor {\ntemplate <typename Metavariables>\nstruct MemoryMonitor;\ntemplate <typename ContributingComponent>\nstruct ContributeMemoryData;\n}  // namespace mem_monitor\n/// \\endcond\n\nnamespace Parallel {\n\nnamespace GlobalCache_detail {\n\ntemplate <class GlobalCacheTag, class Metavariables>\nusing get_matching_tag = typename matching_tag_helper<\n    GlobalCacheTag,\n    tmpl::append<get_const_global_cache_tags<Metavariables>,\n                 get_mutable_global_cache_tags<Metavariables>>>::type;\n\ntemplate <class GlobalCacheTag, class Metavariables>\nusing type_for_get = typename type_for_get_helper<\n    typename get_matching_tag<GlobalCacheTag, Metavariables>::type>::type;\n\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(component_being_mocked)\n\ntemplate <typename... Tags>\nauto make_mutable_cache_tag_storage(tuples::TaggedTuple<Tags...>&& input) {\n  return tuples::TaggedTuple<MutableCacheTag<Tags>...>(std::make_tuple(\n      std::move(tuples::get<Tags>(input)),\n      std::unordered_map<Parallel::ArrayComponentId,\n                         std::vector<std::unique_ptr<Callback>>>{})...);\n}\n\ntemplate <typename ParallelComponent, typename ComponentList>\nauto get_component_if_mocked_impl() {\n  if constexpr (tmpl::list_contains_v<ComponentList, ParallelComponent>) {\n    return ParallelComponent{};\n  } else {\n    using mock_find = tmpl::find<\n        ComponentList,\n        std::is_same<get_component_being_mocked_or_default<tmpl::_1, void>,\n                     tmpl::pin<ParallelComponent>>>;\n    static_assert(\n        tmpl::size<mock_find>::value > 0,\n        \"The requested parallel component (first template argument) is not a \"\n        \"known parallel component (second template argument) or a component \"\n        \"being mocked by one of those components.\");\n    return tmpl::front<mock_find>{};\n  }\n}\n\n/// In order to be able to use a mock action testing framework we need to be\n/// able to get the correct parallel component from the global cache even when\n/// the correct component is a mock. We do this by having the mocked\n/// components have a member type alias `component_being_mocked`, and having\n/// `Parallel::get_component` check if the component to be retrieved is in the\n/// `metavariables::component_list`. If it is not in the `component_list` then\n/// we search for a mock component that is mocking the component we are trying\n/// to retrieve.\ntemplate <typename ComponentList, typename ParallelComponent>\nusing get_component_if_mocked =\n    decltype(get_component_if_mocked_impl<ParallelComponent, ComponentList>());\n\n/// This class replaces the Charm++ base class of the GlobalCache in unit tests\n/// that don't have a Charm++ main function.\nstruct MockGlobalCache {\n  MockGlobalCache() = default;\n  explicit MockGlobalCache(CkMigrateMessage* /*msg*/) {}\n  virtual ~MockGlobalCache() = default;\n  virtual void pup(PUP::er& /*p*/) {}\n\n  static bool isIrreducible() { return true; }\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#elif defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmissing-noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\n  template <typename T>\n  static void contribute(T&& /*unused*/) {\n    ERROR(\"Not implemented.\");\n  }\n#if defined(__GNUC__) || defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n};\n\n#ifdef SPECTRE_CHARM_HAS_MAIN\nstatic constexpr bool mock_global_cache = false;\n#else\nstatic constexpr bool mock_global_cache = true;\n#endif  // SPECTRE_CHARM_HAS_MAIN\n\n}  // namespace GlobalCache_detail\n\n/// \\cond\ntemplate <typename ParallelComponentTag, typename Metavariables>\nauto get_parallel_component(GlobalCache<Metavariables>& cache)\n    -> Parallel::proxy_from_parallel_component<\n        GlobalCache_detail::get_component_if_mocked<\n            typename Metavariables::component_list, ParallelComponentTag>>&;\n\ntemplate <typename ParallelComponentTag, typename Metavariables>\nauto get_parallel_component(const GlobalCache<Metavariables>& cache)\n    -> const Parallel::proxy_from_parallel_component<\n        GlobalCache_detail::get_component_if_mocked<\n            typename Metavariables::component_list, ParallelComponentTag>>&;\n/// \\endcond\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A Charm++ chare that caches global data once per Charm++ node.\n *\n * \\details There are two types of global data that are stored; const data and\n * mutable data. Once the GlobalCache is created, const data cannot be edited\n * but mutable data can be edited using `Parallel::mutate`.\n *\n * The template parameter `Metavariables` must define the following type\n * aliases:\n *   - `component_list`   typelist of ParallelComponents\n *   - `const_global_cache_tags`   (possibly empty) typelist of tags of\n *     constant data\n *   - `mutable_global_cache_tags` (possibly empty) typelist of tags of\n *     non-constant data\n *\n * The tag lists for the const items added to the GlobalCache is created by\n * combining the following tag lists:\n *   - `Metavariables::const_global_cache_tags` which should contain only those\n *     tags that cannot be added from the other tag lists below.\n *   - `Component::const_global_cache_tags` for each `Component` in\n *     `Metavariables::component_list` which should contain the tags needed by\n *     any simple actions called on the Component, as well as tags need by the\n *     `allocate_array` function of an array component.  The type alias may be\n *     omitted for an empty list.\n *   - `Action::const_global_cache_tags` for each `Action` in the\n *     `phase_dependent_action_list` of each `Component` of\n *     `Metavariables::component_list` which should contain the tags needed by\n *     that  Action.  The type alias may be omitted for an empty list.\n *\n * The tag lists for the mutable items added to the GlobalCache is created\n * by combining exactly the same tag lists as for the const items, except with\n * `const_global_cache_tags` replaced by `mutable_global_cache_tags`.\n *\n * The tags in the `const_global_cache_tags` and\n * `mutable_global_cache_tags` type lists are db::SimpleTag%s that\n * have a `using option_tags` type alias and a static function\n * `create_from_options` that are used to create the constant data (or initial\n * mutable data) from input file options.\n *\n * References to const items in the GlobalCache are also added to the\n * db::DataBox of each `Component` in the\n * `Metavariables::component_list` with the same tag with which they\n * were inserted into the GlobalCache.  References to mutable items\n * in the GlobalCache are not added to the db::DataBox.\n *\n * Since mutable data is stored once per Charm++ node, we require that\n * data structures held by mutable tags have some sort of thread-safety.\n * Particularly, we require data structures in mutable tags be Single\n * Producer-Multiple Consumer. This means that the data structure should be\n * readable/accessible by multiple threads at once, even while being mutated\n * (multiple consumer), but will not be edited/mutated simultaneously on\n * multiple threads (single producer).\n */\ntemplate <typename Metavariables>\nclass GlobalCache\n    : public std::conditional_t<GlobalCache_detail::mock_global_cache,\n                                GlobalCache_detail::MockGlobalCache,\n                                CBase_GlobalCache<Metavariables>> {\n  using Base = std::conditional_t<GlobalCache_detail::mock_global_cache,\n                                  GlobalCache_detail::MockGlobalCache,\n                                  CBase_GlobalCache<Metavariables>>;\n  using parallel_component_tag_list = tmpl::transform<\n      typename Metavariables::component_list,\n      tmpl::bind<\n          tmpl::type_,\n          tmpl::bind<Parallel::proxy_from_parallel_component, tmpl::_1>>>;\n  using ParallelComponentTuple =\n      tuples::tagged_tuple_from_typelist<parallel_component_tag_list>;\n\n public:\n  static constexpr bool is_mocked = GlobalCache_detail::mock_global_cache;\n  using proxy_type = CProxy_GlobalCache<Metavariables>;\n  using main_proxy_type = CProxy_Main<Metavariables>;\n  /// Access to the Metavariables template parameter\n  using metavariables = Metavariables;\n  /// Typelist of the ParallelComponents stored in the GlobalCache\n  using component_list = typename Metavariables::component_list;\n  // Even though the GlobalCache doesn't run the Algorithm, this type alias\n  // helps in identifying that the GlobalCache is a Nodegroup using\n  // Parallel::is_nodegroup_v\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  using const_tags_list = get_const_global_cache_tags<Metavariables>;\n  using ConstTagsTuple = tuples::tagged_tuple_from_typelist<const_tags_list>;\n  using ConstTagsStorage = ConstTagsTuple;\n  using mutable_tags_list = get_mutable_global_cache_tags<Metavariables>;\n  using MutableTagsTuple =\n      tuples::tagged_tuple_from_typelist<mutable_tags_list>;\n  using MutableTagsStorage = tuples::tagged_tuple_from_typelist<\n      get_mutable_global_cache_tag_storage<Metavariables>>;\n\n  /// Constructor meant to be used in the ActionTesting framework.\n  GlobalCache(ConstTagsTuple const_global_cache,\n              MutableTagsTuple mutable_global_cache = {},\n              std::vector<size_t> procs_per_node = {1}, const int my_proc = 0,\n              const int my_node = 0, const int my_local_rank = 0);\n\n  /// Constructor meant to be used in charm-aware settings (with a Main proxy).\n  GlobalCache(ConstTagsTuple const_global_cache,\n              MutableTagsTuple mutable_global_cache,\n              std::optional<main_proxy_type> main_proxy);\n\n  explicit GlobalCache(CkMigrateMessage* msg) : Base(msg) {}\n\n  ~GlobalCache() override {\n    (void)Parallel::charmxx::RegisterChare<\n        GlobalCache<Metavariables>,\n        CkIndex_GlobalCache<Metavariables>>::registrar;\n  }\n  /// \\cond\n  GlobalCache() = default;\n  GlobalCache(const GlobalCache&) = default;\n  GlobalCache& operator=(const GlobalCache&) = default;\n  GlobalCache(GlobalCache&&) = default;\n  GlobalCache& operator=(GlobalCache&&) = default;\n  /// \\endcond\n\n  /// Entry method to set the ParallelComponents (should only be called once)\n  void set_parallel_components(ParallelComponentTuple&& parallel_components,\n                               const CkCallback& callback);\n\n  /*!\n   * \\brief Returns whether the object referred to by `GlobalCacheTag`\n   * (which must be a mutable cache tag) is ready to be accessed by a\n   * `get` call.\n   *\n   * \\details `function` is a user-defined invokable that:\n   * - takes one argument: a const reference to the object referred to by the\n   *   `GlobalCacheTag`.\n   * - if the data is ready, returns a default constructed\n   *   `std::unique_ptr<CallBack>`\n   * - if the data is not ready, returns a `std::unique_ptr<CallBack>`,\n   *   where the `Callback` will re-invoke the current action on the\n   *   current parallel component. This callback should be a\n   *   `Parallel::PerformAlgorithmCallback`. Other types of callbacks are not\n   *   supported at this time.\n   *\n   * \\parblock\n   * \\warning The `function` may be called twice so it should not modify any\n   * state in its scope.\n   * \\endparblock\n   *\n   * \\parblock\n   * \\warning If there has already been a callback registered for the given\n   * `array_component_id`, then the callback returned by `function` will **not**\n   * be registered or called.\n   * \\endparblock\n   */\n  template <typename GlobalCacheTag, typename Function>\n  bool mutable_cache_item_is_ready(\n      const Parallel::ArrayComponentId& array_component_id,\n      const Function& function);\n\n  /// Mutates the non-const object identified by GlobalCacheTag.\n  /// \\requires `GlobalCacheTag` is a tag in `mutable_global_cache_tags`\n  /// defined by the Metavariables and in Actions.\n  ///\n  /// Internally calls `Function::apply()`, where `Function` is a\n  /// user-defined struct and `Function::apply()` is a user-defined\n  /// static function that mutates the object.  `Function::apply()`\n  /// takes as its first argument a `gsl::not_null` pointer to the\n  /// object named by the GlobalCacheTag (or if that object is a\n  /// `std::unique_ptr<T>`, a `gsl::not_null<T*>`), and takes the contents of\n  /// `args` as subsequent arguments.\n  template <typename GlobalCacheTag, typename Function, typename... Args>\n  void mutate(const std::tuple<Args...>& args);\n\n  /// Entry method that computes the size of the local branch of the\n  /// GlobalCache and sends it to the MemoryMonitor parallel component.\n  ///\n  /// \\note This can only be called if the MemoryMonitor component is in the\n  /// `component_list` of the metavariables. Also can't be called in the testing\n  /// framework. Trying to do either of these will result in an ERROR.\n  void compute_size_for_memory_monitor(const double time);\n\n  /// Entry method that will set the value of the Parallel::Tags::ResourceInfo\n  /// tag to the value passed in (if the tag exists in the GlobalCache)\n  ///\n  /// This is only meant to be called once.\n  void set_resource_info(\n      const Parallel::ResourceInfo<Metavariables>& resource_info);\n\n  /// Entry method to print the mutable global cache callbacks upon a deadlock\n  /// to a file.\n  ///\n  /// For each mutable cache tag, this prints\n  ///\n  /// - Tag name\n  /// - Node number\n  /// - Number of callbacks registered\n  /// - For each callback, the ArrayComponentId and the callback name\n  void print_mutable_cache_callbacks(const std::string& file_name);\n\n  /// Retrieve the resource_info\n  const Parallel::ResourceInfo<Metavariables>& get_resource_info() const {\n    return resource_info_;\n  }\n\n  /// Overlay new data onto a subset of the GlobalCache's tags.\n  ///\n  /// This is used when reparsing an input file to update option values during\n  /// a restart from a checkpoint.\n  void overlay_cache_data(const tuples::tagged_tuple_from_typelist<\n                          Parallel::get_overlayable_option_list<Metavariables>>&\n                              data_to_overlay);\n\n  /// Retrieve the proxy to the global cache\n  proxy_type get_this_proxy();\n\n  void pup(PUP::er& p) override;  // NOLINT\n\n  /// Retrieve the proxy to the Main chare (or std::nullopt if the proxy has not\n  /// been set, i.e. we are not charm-aware).\n  std::optional<main_proxy_type> get_main_proxy();\n\n  /// @{\n  /// Wrappers for charm++ informational functions.\n\n  /// Number of processing elements\n  int number_of_procs() const;\n  /// Number of nodes.\n  int number_of_nodes() const;\n  /// Number of processing elements on the given node.\n  int procs_on_node(const int node_index) const;\n  /// %Index of first processing element on the given node.\n  int first_proc_on_node(const int node_index) const;\n  /// %Index of the node for the given processing element.\n  int node_of(const int proc_index) const;\n  /// The local index for the given processing element on its node.\n  int local_rank_of(const int proc_index) const;\n  /// %Index of my processing element.\n  int my_proc() const;\n  /// %Index of my node.\n  int my_node() const;\n  /// The local index of my processing element on my node.\n  /// This is in the interval 0, ..., procs_on_node(my_node()) - 1.\n  int my_local_rank() const;\n  /// @}\n\n private:\n  // clang-tidy: false positive, redundant declaration\n  template <typename GlobalCacheTag, typename MV>\n  friend auto get(const GlobalCache<MV>& cache)  // NOLINT\n      -> const GlobalCache_detail::type_for_get<GlobalCacheTag, MV>&;\n\n  // clang-tidy: false positive, redundant declaration\n  template <typename ParallelComponentTag, typename MV>\n  friend auto get_parallel_component(  // NOLINT\n      GlobalCache<MV>& cache)\n      -> Parallel::proxy_from_parallel_component<\n          GlobalCache_detail::get_component_if_mocked<\n              typename MV::component_list, ParallelComponentTag>>&;\n\n  // clang-tidy: false positive, redundant declaration\n  template <typename ParallelComponentTag, typename MV>\n  friend auto get_parallel_component(  // NOLINT\n      const GlobalCache<MV>& cache)\n      -> const Parallel::proxy_from_parallel_component<\n          GlobalCache_detail::get_component_if_mocked<\n              typename MV::component_list,\n              ParallelComponentTag>>&;  // NOLINT\n\n  ConstTagsStorage const_global_cache_{};\n  MutableTagsStorage mutable_global_cache_{};\n  // Wrap mutable tags in Parallel::MutexTag. The type of MutexTag is a\n  // pair<mutex, mutex>. The first mutex is for editing the value of the mutable\n  // tag. The second mutex is for editing the vector of callbacks associated\n  // with the mutable tag.\n  tuples::tagged_tuple_from_typelist<\n      tmpl::transform<MutableTagsStorage, tmpl::bind<MutexTag, tmpl::_1>>>\n      mutexes_{};\n  ParallelComponentTuple parallel_components_{};\n  Parallel::ResourceInfo<Metavariables> resource_info_{};\n  bool parallel_components_have_been_set_{false};\n  bool resource_info_has_been_set_{false};\n  std::optional<main_proxy_type> main_proxy_;\n  // Defaults for testing framework\n  int my_proc_{0};\n  int my_node_{0};\n  int my_local_rank_{0};\n  std::vector<size_t> procs_per_node_{1};\n};\n\ntemplate <typename Metavariables>\nGlobalCache<Metavariables>::GlobalCache(ConstTagsTuple const_global_cache,\n                                        MutableTagsTuple mutable_global_cache,\n                                        std::vector<size_t> procs_per_node,\n                                        const int my_proc, const int my_node,\n                                        const int my_local_rank)\n    : const_global_cache_(std::move(const_global_cache)),\n      mutable_global_cache_(GlobalCache_detail::make_mutable_cache_tag_storage(\n          std::move(mutable_global_cache))),\n      main_proxy_(std::nullopt),\n      my_proc_(my_proc),\n      my_node_(my_node),\n      my_local_rank_(my_local_rank),\n      procs_per_node_(std::move(procs_per_node)) {}\n\ntemplate <typename Metavariables>\nGlobalCache<Metavariables>::GlobalCache(\n    ConstTagsTuple const_global_cache, MutableTagsTuple mutable_global_cache,\n    std::optional<main_proxy_type> main_proxy)\n    : const_global_cache_(std::move(const_global_cache)),\n      mutable_global_cache_(GlobalCache_detail::make_mutable_cache_tag_storage(\n          std::move(mutable_global_cache))),\n      main_proxy_(std::move(main_proxy)) {}\n\ntemplate <typename Metavariables>\nvoid GlobalCache<Metavariables>::set_parallel_components(\n    ParallelComponentTuple&& parallel_components, const CkCallback& callback) {\n  ASSERT(!parallel_components_have_been_set_,\n         \"Can only set the parallel_components once\");\n  parallel_components_ = std::move(parallel_components);\n  parallel_components_have_been_set_ = true;\n  this->contribute(callback);\n}\n\ntemplate <typename Metavariables>\ntemplate <typename GlobalCacheTag, typename Function>\nbool GlobalCache<Metavariables>::mutable_cache_item_is_ready(\n    const Parallel::ArrayComponentId& array_component_id,\n    const Function& function) {\n  using tag = MutableCacheTag<GlobalCache_detail::get_matching_mutable_tag<\n      GlobalCacheTag, Metavariables>>;\n  std::unique_ptr<Callback> optional_callback{};\n  // Returns true if a callback was returned from `function`. Returns false if\n  // nullptr was returned\n  const auto callback_was_registered = [this, &function,\n                                        &optional_callback]() -> bool {\n    // Reads don't need a lock.\n    if constexpr (tt::is_a_v<std::unique_ptr, typename tag::tag::type>) {\n      optional_callback =\n          function(*(std::get<0>(tuples::get<tag>(mutable_global_cache_))));\n    } else {\n      optional_callback =\n          function(std::get<0>(tuples::get<tag>(mutable_global_cache_)));\n    }\n\n    return optional_callback != nullptr;\n  };\n\n  if (callback_was_registered()) {\n    optional_callback->register_with_charm();\n    // Second mutex is for vector of callbacks\n    std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).second;\n    const std::unique_ptr<Callback> clone_of_optional_callback =\n        optional_callback->get_clone();\n    {\n      // Scoped for lock guard\n      const std::lock_guard<std::mutex> lock(mutex);\n      std::unordered_map<Parallel::ArrayComponentId,\n                         std::vector<std::unique_ptr<Callback>>>& callbacks =\n          std::get<1>(tuples::get<tag>(mutable_global_cache_));\n\n      if (callbacks.contains(array_component_id)) {\n        // If this array component id already exists, we don't want to add\n        // multiple of the same callback, so we loop over the existing callbacks\n        // and only if none of the existing callbacks are equal to the optional\n        // callback do we move the optional callback into the vector\n        auto& vec_callbacks = callbacks.at(array_component_id);\n        if (alg::none_of(vec_callbacks,\n                         [&](const std::unique_ptr<Callback>& local_callback) {\n                           return local_callback->is_equal_to(\n                               *optional_callback);\n                         })) {\n          vec_callbacks.emplace_back(std::move(optional_callback));\n        }\n      } else {\n        // If we don't have this array component id, then we create the vector\n        // and move the optional callback into the vector\n        callbacks[array_component_id] =\n            std::vector<std::unique_ptr<Callback>>(1);\n        callbacks.at(array_component_id)[0] = std::move(optional_callback);\n      }\n    }\n\n    // We must check if the tag is ready again. Consider the following example:\n    //\n    // We have two writers, A and B preparing to make independent changes to a\n    // cache object. We have an element E with a callback waiting for the change\n    // B is going to make. Suppose the following sequence of events:\n    //\n    // 1. A mutates the object, copies the callback list (below in `mutate`),\n    //    and starts the callback for element E.\n    // 2. E checks the current value and determines it is not ready.\n    // 3. B mutates the object, copies the empty callback list, and returns with\n    //    nothing to do.\n    // 4. E returns a new callback, which is added to the callback list.\n    // 5. A returns.\n    //\n    // We now have E waiting on a change that has already happened. This will\n    // most certainly result in a deadlock if E is blocking the Algorithm. Thus\n    // we must do another check for whether the cache object is ready or not. In\n    // the order of events, this check happens sometime after 4. When this check\n    // happens, E concludes that the cache object *is* ready (because 3 is when\n    // the object was mutated) and E can continue on.\n    //\n    // If this second check reveals that the object *is* ready, then we have an\n    // unecessary callback in our map, so we remove it.\n    //\n    // If this second check reveals that the object *isn't* ready, then we don't\n    // bother adding another callback to the map because one already exists. No\n    // need to call a callback twice.\n    //\n    // This function returns true if no callback was registered and false if one\n    // was registered.\n    const bool cache_item_is_ready = not callback_was_registered();\n    if (cache_item_is_ready) {\n      const std::lock_guard<std::mutex> lock(mutex);\n      std::unordered_map<Parallel::ArrayComponentId,\n                         std::vector<std::unique_ptr<Callback>>>& callbacks =\n          std::get<1>(tuples::get<tag>(mutable_global_cache_));\n\n      // It's possible that no new callbacks were registered, so make sure this\n      // array component id still has callbacks before trying to remove them.\n      if (callbacks.contains(array_component_id)) {\n        // If this callback was a duplicate, we'll have to search through all\n        // callbacks to determine which to remove. If it wasn't a duplicate,\n        // then it'll just be the last callback in the vector.\n        auto& vec_callbacks = callbacks.at(array_component_id);\n        std::erase_if(vec_callbacks,\n                      [&clone_of_optional_callback](const auto& t) {\n                        return t->is_equal_to(*clone_of_optional_callback);\n                      });\n\n        if (callbacks.at(array_component_id).empty()) {\n          callbacks.erase(array_component_id);\n        }\n      }\n    }\n\n    return cache_item_is_ready;\n  } else {\n    // The user-defined `function` didn't specify a callback, which\n    // means that the item is ready.\n    return true;\n  }\n}\n\ntemplate <typename Metavariables>\ntemplate <typename GlobalCacheTag, typename Function, typename... Args>\nvoid GlobalCache<Metavariables>::mutate(const std::tuple<Args...>& args) {\n  (void)Parallel::charmxx::RegisterGlobalCacheMutate<\n      Metavariables, GlobalCacheTag, Function, Args...>::registrar;\n  using tag = MutableCacheTag<GlobalCache_detail::get_matching_mutable_tag<\n      GlobalCacheTag, Metavariables>>;\n\n  // Do the mutate.\n  std::apply(\n      [this](const auto&... local_args) {\n        // First mutex is for value of mutable tag\n        std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).first;\n        const std::lock_guard<std::mutex> lock(mutex);\n        Function::apply(make_not_null(&StdHelpers::retrieve(std::get<0>(\n                            tuples::get<tag>(mutable_global_cache_)))),\n                        local_args...);\n      },\n      args);\n\n  // A callback might call mutable_cache_item_is_ready, which might add yet\n  // another callback to the vector of callbacks.  We don't want to immediately\n  // invoke this new callback as it might just add another callback again (and\n  // again in an infinite loop). And we don't want to remove it from the map of\n  // callbacks before it is invoked otherwise we could get a deadlock.\n  // Therefore, after locking it, we std::move the map of callbacks into a\n  // temporary map, clear the original map, and invoke the callbacks in the\n  // temporary map.\n  std::unordered_map<Parallel::ArrayComponentId,\n                     std::vector<std::unique_ptr<Callback>>>\n      callbacks{};\n  // Second mutex is for map of callbacks\n  std::mutex& mutex = tuples::get<MutexTag<tag>>(mutexes_).second;\n  {\n    // Scoped for lock guard\n    const std::lock_guard<std::mutex> lock(mutex);\n    callbacks = std::move(std::get<1>(tuples::get<tag>(mutable_global_cache_)));\n    std::get<1>(tuples::get<tag>(mutable_global_cache_)).clear();\n  }\n\n  // Invoke the callbacks.  Any new callbacks that are added to the\n  // list (if a callback calls mutable_cache_item_is_ready) will be\n  // saved and will not be invoked here.\n  for (auto& [array_component_id, vec_callbacks] : callbacks) {\n    for (auto& callback : vec_callbacks) {\n      callback->invoke();\n    }\n  }\n}\n\ntemplate <typename Metavariables>\nvoid GlobalCache<Metavariables>::overlay_cache_data(\n    const tuples::tagged_tuple_from_typelist<\n        Parallel::get_overlayable_option_list<Metavariables>>&\n        data_to_overlay) {\n  // Entries in `get_overlayable_tag_list` and `get_overlayable_option_list`\n  // have a 1-to-1 mapping: one is the option tag for the other. We loop\n  // over the tag because it's easy to call tag::option_tag but not vice versa.\n  tmpl::for_each<Parallel::get_overlayable_tag_list<Metavariables>>(\n      [this, &data_to_overlay](auto tag) {\n        using Tag = typename decltype(tag)::type;\n        static_assert(tmpl::size<typename Tag::option_tags>::value == 1,\n                      \"The current implementation can only update tags \"\n                      \"constructed from a single option tag.\");\n        using OptionTag = tmpl::front<typename Tag::option_tags>;\n        tuples::get<Tag>(const_global_cache_) =\n            Tag::create_from_options(tuples::get<OptionTag>(data_to_overlay));\n      });\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\ntemplate <typename Metavariables>\nvoid GlobalCache<Metavariables>::compute_size_for_memory_monitor(\n    const double time) {\n  if constexpr (tmpl::list_contains_v<\n                    typename Metavariables::component_list,\n                    mem_monitor::MemoryMonitor<Metavariables>>) {\n    const double size_in_bytes =\n        static_cast<double>(size_of_object_in_bytes(*this));\n    const double size_in_MB = size_in_bytes / 1.0e6;\n\n    auto& mem_monitor_proxy = Parallel::get_parallel_component<\n        mem_monitor::MemoryMonitor<Metavariables>>(*this);\n\n    const int my_node = Parallel::my_node<int>(*this);\n\n    Parallel::simple_action<\n        mem_monitor::ContributeMemoryData<GlobalCache<Metavariables>>>(\n        mem_monitor_proxy, time, my_node, size_in_MB);\n  } else {\n    (void)time;\n    ERROR(\n        \"GlobalCache::compute_size_for_memory_monitor can only be called if \"\n        \"the MemoryMonitor is in the component list in the metavariables.\\n\");\n  }\n}\n\ntemplate <typename Metavariables>\nvoid GlobalCache<Metavariables>::set_resource_info(\n    const Parallel::ResourceInfo<Metavariables>& resource_info) {\n  ASSERT(not resource_info_has_been_set_,\n         \"Can only set the resource info once\");\n  resource_info_ = resource_info;\n  resource_info_has_been_set_ = true;\n}\n\ntemplate <typename Metavariables>\nvoid GlobalCache<Metavariables>::print_mutable_cache_callbacks(\n    const std::string& file_name) {\n  std::stringstream ss{};\n  ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)\n     << std::scientific;\n\n  ss << \"========== BEGIN CALLBACKS ON NODE \" << this->my_node()\n     << \" ==========\\n\";\n\n  tmpl::for_each<typename MutableTagsStorage::tags_list>(\n      [this, &ss](auto tag_v) {\n        using Tag = tmpl::type_from<decltype(tag_v)>;\n\n        const auto& callbacks =\n            std::get<1>(tuples::get<Tag>(mutable_global_cache_));\n\n        ss << \"For tag \" << pretty_type::name<typename Tag::tag>()\n           << \", callbacks (\"\n           << alg::accumulate(callbacks, 0_st,\n                              [](const size_t cur_size, const auto& v) {\n                                return cur_size + v.second.size();\n                              })\n           << \"):\\n\";\n\n        for (const auto& [array_component_id, vec_callbacks] : callbacks) {\n          for (const auto& callback : vec_callbacks) {\n            ss << \"  ArrayComponentId \" << array_component_id << \": \"\n               << callback->name() << \"\\n\";\n          }\n        }\n      });\n\n  ss << \"========== END CALLBACKS ON NODE \" << this->my_node()\n     << \" ============\\n\";\n\n  Parallel::fprintf(file_name, \"%s\\n\", ss.str());\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__)\n\ntemplate <typename Metavariables>\ntypename Parallel::GlobalCache<Metavariables>::proxy_type\nGlobalCache<Metavariables>::get_this_proxy() {\n  if constexpr (is_mocked) {\n    // The proxy is not used in the testing framework\n    return Parallel::GlobalCache<Metavariables>::proxy_type{};\n  } else {\n    return this->thisProxy;\n  }\n}\n\ntemplate <typename Metavariables>\nstd::optional<typename Parallel::GlobalCache<Metavariables>::main_proxy_type>\nGlobalCache<Metavariables>::get_main_proxy() {\n  return main_proxy_;\n}\n\n// For all these functions, if the main proxy is set (meaning we are\n// charm-aware) then just call the sys:: functions. Otherwise, use the values\n// set for the testing framework (or the defaults).\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::number_of_procs() const {\n  return main_proxy_.has_value()\n             ? sys::number_of_procs()\n             : static_cast<int>(alg::accumulate(procs_per_node_, 0_st));\n}\n\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::number_of_nodes() const {\n  return main_proxy_.has_value() ? sys::number_of_nodes()\n                                 : static_cast<int>(procs_per_node_.size());\n}\n\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::procs_on_node(const int node_index) const {\n  return main_proxy_.has_value()\n             ? sys::procs_on_node(node_index)\n             : static_cast<int>(\n                   procs_per_node_[static_cast<size_t>(node_index)]);\n}\n\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::first_proc_on_node(const int node_index) const {\n  return main_proxy_.has_value()\n             ? sys::first_proc_on_node(node_index)\n             : static_cast<int>(\n                   std::accumulate(procs_per_node_.begin(),\n                                   procs_per_node_.begin() + node_index, 0_st));\n}\n\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::node_of(const int proc_index) const {\n  if (main_proxy_.has_value()) {\n    // For some reason gcov doesn't think this line is tested even though it is\n    // in Test_AlgorithmGlobalCache.cpp\n    return sys::node_of(proc_index);  // LCOV_EXCL_LINE\n  } else {\n    size_t procs_so_far = 0;\n    size_t node = 0;\n    while (procs_so_far <= static_cast<size_t>(proc_index)) {\n      procs_so_far += procs_per_node_[node];\n      ++node;\n    }\n    return static_cast<int>(--node);\n  }\n}\n\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::local_rank_of(const int proc_index) const {\n  return main_proxy_.has_value()\n             ? sys::local_rank_of(proc_index)\n             : proc_index - first_proc_on_node(node_of(proc_index));\n}\n\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::my_proc() const {\n  return main_proxy_.has_value() ? sys::my_proc() : my_proc_;\n}\n\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::my_node() const {\n  return main_proxy_.has_value() ? sys::my_node() : my_node_;\n}\n\ntemplate <typename Metavariables>\nint GlobalCache<Metavariables>::my_local_rank() const {\n  return main_proxy_.has_value() ? sys::my_local_rank() : my_local_rank_;\n}\n\ntemplate <typename Metavariables>\nvoid GlobalCache<Metavariables>::pup(PUP::er& p) {\n  p | const_global_cache_;\n  p | parallel_components_;\n  p | mutable_global_cache_;\n  p | main_proxy_;\n  p | parallel_components_have_been_set_;\n  p | resource_info_has_been_set_;\n  p | my_proc_;\n  p | my_node_;\n  p | my_local_rank_;\n  p | procs_per_node_;\n}\n\n/// @{\n/// \\ingroup ParallelGroup\n/// \\brief Access the Charm++ proxy associated with a ParallelComponent\n///\n/// \\requires ParallelComponentTag is a tag in component_list\n///\n/// \\returns a Charm++ proxy that can be used to call an entry method on the\n/// chare(s)\ntemplate <typename ParallelComponentTag, typename Metavariables>\nauto get_parallel_component(GlobalCache<Metavariables>& cache)\n    -> Parallel::proxy_from_parallel_component<\n        GlobalCache_detail::get_component_if_mocked<\n            typename Metavariables::component_list, ParallelComponentTag>>& {\n  return tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<\n      GlobalCache_detail::get_component_if_mocked<\n          typename Metavariables::component_list, ParallelComponentTag>>>>(\n      cache.parallel_components_);\n}\n\ntemplate <typename ParallelComponentTag, typename Metavariables>\nauto get_parallel_component(const GlobalCache<Metavariables>& cache)\n    -> const Parallel::proxy_from_parallel_component<\n        GlobalCache_detail::get_component_if_mocked<\n            typename Metavariables::component_list, ParallelComponentTag>>& {\n  return tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<\n      GlobalCache_detail::get_component_if_mocked<\n          typename Metavariables::component_list, ParallelComponentTag>>>>(\n      cache.parallel_components_);\n}\n/// @}\n\n/// @{\n/// \\ingroup ParallelGroup\n/// \\brief Access data in the cache\n///\n/// \\requires GlobalCacheTag is a tag in the `mutable_global_cache_tags`\n/// or `const_global_cache_tags` defined by the Metavariables and in Actions.\n///\n/// \\returns a constant reference to an object in the cache\ntemplate <typename GlobalCacheTag, typename Metavariables>\nauto get(const GlobalCache<Metavariables>& cache)\n    -> const GlobalCache_detail::type_for_get<GlobalCacheTag, Metavariables>& {\n  constexpr bool is_mutable =\n      is_in_mutable_global_cache<Metavariables, GlobalCacheTag>;\n  // We check if the tag is to be retrieved directly or via a base class\n  using tmp_tag =\n      GlobalCache_detail::get_matching_tag<GlobalCacheTag, Metavariables>;\n  using tag =\n      tmpl::conditional_t<is_mutable, MutableCacheTag<tmp_tag>, tmp_tag>;\n  if constexpr (is_mutable) {\n    // Tag is not in the const tags, so use mutable_global_cache_. No locks here\n    // because we require all mutable tags to be able to be read at all times\n    // (even when being written to)\n    if constexpr (tt::is_a_v<std::unique_ptr, typename tag::tag::type>) {\n      return *std::get<0>(tuples::get<tag>(cache.mutable_global_cache_));\n    } else {\n      return std::get<0>(tuples::get<tag>(cache.mutable_global_cache_));\n    }\n  } else {\n    // Tag is in the const tags, so use const_global_cache_\n    if constexpr (tt::is_a_v<std::unique_ptr, typename tag::type>) {\n      return *(tuples::get<tag>(cache.const_global_cache_));\n    } else {\n      return tuples::get<tag>(cache.const_global_cache_);\n    }\n  }\n}\n\n/// \\ingroup ParallelGroup\n/// \\brief Returns whether the object identified by `GlobalCacheTag`\n/// is ready to be accessed by `get`.\n///\n/// \\requires `GlobalCacheTag` is a tag in `mutable_global_cache_tags`\n/// defined by the Metavariables and in Actions.\n///\n/// \\requires `function` is a user-defined invokable that takes one argument:\n/// a const reference to the object referred to by the\n/// `GlobalCacheTag`.  `function` returns a\n/// `std::unique_ptr<CallBack>` that determines the readiness. To\n/// indicate that the item is ready, the `std::unique_ptr` returned\n/// by `function` must be nullptr; in this case\n/// `mutable_cache_item_is_ready` returns true. To indicate that the\n/// item is not ready, the `std::unique_ptr` returned by `function`\n/// must be valid; in this case, `mutable_cache_item_is_ready`\n/// appends the `std::unique_ptr<Callback>` to an\n/// internal list of callbacks to be called on `mutate`, and then\n/// returns false.\n///\n/// \\note If `function` is returning a valid callback, it should only return a\n/// `Parallel::PerformAlgorithmCallback`. Other types of callbacks are not\n/// supported at this time.\ntemplate <typename GlobalCacheTag, typename Function, typename Metavariables>\nbool mutable_cache_item_is_ready(\n    GlobalCache<Metavariables>& cache,\n    const Parallel::ArrayComponentId& array_component_id,\n    const Function& function) {\n  return cache.template mutable_cache_item_is_ready<GlobalCacheTag>(\n      array_component_id, function);\n}\n\n/// \\ingroup ParallelGroup\n///\n/// \\brief Mutates non-const data in the cache, by calling `Function::apply()`\n///\n/// \\requires `GlobalCacheTag` is a tag in tag_list.\n/// \\requires `Function` is a struct with a static void `apply()`\n/// function that mutates the object. `Function::apply()` takes as its\n/// first argument a `gsl::not_null` pointer to the object named by\n/// the `GlobalCacheTag`, and takes `args` as\n/// subsequent arguments.\ntemplate <typename GlobalCacheTag, typename Function, typename Metavariables,\n          typename... Args>\nvoid mutate(GlobalCache<Metavariables>& cache, Args&&... args) {\n  if (cache.get_main_proxy().has_value()) {\n    if constexpr (not GlobalCache<Metavariables>::is_mocked) {\n      cache.thisProxy.template mutate<GlobalCacheTag, Function>(\n          std::make_tuple<Args...>(std::forward<Args>(args)...));\n    } else {\n      ERROR(\n          \"Main proxy is set but global cache is being mocked. This is \"\n          \"currently not implemented.\");\n    }\n  } else {\n    cache.template mutate<GlobalCacheTag, Function>(\n        std::make_tuple<Args...>(std::forward<Args>(args)...));\n  }\n}\n\nnamespace Tags {\ntemplate <class Metavariables>\nstruct GlobalCacheProxy : db::SimpleTag {\n  using type = CProxy_GlobalCache<Metavariables>;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ParallelGroup\n/// Tag to retrieve the `Parallel::GlobalCache` from the DataBox.\ntemplate <class Metavariables>\nstruct GlobalCache : db::SimpleTag {\n  using type = Parallel::GlobalCache<Metavariables>*;\n};\n\ntemplate <class Metavariables>\nstruct GlobalCacheCompute : GlobalCache<Metavariables>, db::ComputeTag {\n  using base = GlobalCache<Metavariables>;\n  using argument_tags = tmpl::list<GlobalCacheProxy<Metavariables>>;\n  using return_type = Parallel::GlobalCache<Metavariables>*;\n  static void function(\n      const gsl::not_null<Parallel::GlobalCache<Metavariables>**>\n          local_branch_of_global_cache,\n      const CProxy_GlobalCache<Metavariables>& global_cache_proxy) {\n    *local_branch_of_global_cache = Parallel::local_branch(global_cache_proxy);\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ParallelGroup\n/// Tag used to retrieve data from the `Parallel::GlobalCache`. This is the\n/// recommended way for compute tags to retrieve data out of the global cache.\ntemplate <class CacheTag, class Metavariables>\nstruct FromGlobalCache : CacheTag, db::ReferenceTag {\n  static_assert(db::is_simple_tag_v<CacheTag>);\n  using base = CacheTag;\n  using argument_tags = tmpl::list<GlobalCache<Metavariables>>;\n\n  static const auto& get(\n      const Parallel::GlobalCache<Metavariables>* const& cache) {\n    return Parallel::get<CacheTag>(*cache);\n  }\n};\n\ntemplate <typename Metavariables>\nstruct ResourceInfoReference : ResourceInfo<Metavariables>, db::ReferenceTag {\n  using base = ResourceInfo<Metavariables>;\n  using argument_tags = tmpl::list<GlobalCache<Metavariables>>;\n\n  static const auto& get(\n      const Parallel::GlobalCache<Metavariables>* const& cache) {\n    return cache->get_resource_info();\n  }\n};\n}  // namespace Tags\n}  // namespace Parallel\n\nnamespace PUP {\n/// \\cond\n// Warning! This is an invalid and kludgey pupper, because when unpacking it\n// produces a `nullptr` rather than a valid `GlobalCache*`. This function is\n// provided _only_ to enable putting a `GlobalCache*` in the DataBox.\n//\n// SpECTRE parallel components with a `GlobalCache*` in their DataBox should\n// set this pointer using a compute item that calls `Parallel::local_branch` on\n// a stored proxy. When deserializing the DataBox, these components should call\n// `db:mutate<GlobalCacheProxy>(box)` to force the DataBox to update the\n// pointer from the new Charm++ proxy.\n//\n// We do not currently anticipate needing to (de)serialize a `GlobalCache*`\n// outside of a DataBox. But if this need arises, it will be necessary to\n// provide a non-kludgey pupper here.\n//\n// Correctly (de)serializing the `GlobalCache*` would require obtaining a\n// `CProxy_GlobalCache` item and calling `Parallel::local_branch` on it ---\n// just as in the workflow described above, but within the pupper vs in the\n// DataBox. But this strategy fails when restarting from a checkpoint file,\n// because calling `Parallel::local_branch` may not be well-defined in the\n// unpacking pup call when all Charm++ components may not yet be fully restored.\n// This difficulty is why we instead write an invalid pupper here.\n//\n// In future versions of Charm++, the pup function may know whether it is\n// called in the context of checkpointing, load balancing, etc. This knowledge\n// would enable us to write a valid pupper for non-checkpoint contexts, and\n// return a `nullptr` only when restoring from checkpoint.\ntemplate <typename Metavariables>\ninline void operator|(PUP::er& p,  // NOLINT\n                      Parallel::GlobalCache<Metavariables>*& t) {\n  if (p.isUnpacking()) {\n    t = nullptr;\n  }\n}\n/// \\endcond\n}  // namespace PUP\n\n#define CK_TEMPLATES_ONLY\n#include \"Parallel/GlobalCache.def.h\"\n#undef CK_TEMPLATES_ONLY\n"
  },
  {
    "path": "src/Parallel/GlobalCacheDeclare.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Forward-declares ResourceInfo which the GlobalCache has an entry method for.\n\n#pragma once\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct ResourceInfo;\ntemplate <class Metavariables>\nclass CProxy_GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n"
  },
  {
    "path": "src/Parallel/InboxInserters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n#include <utility>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace Parallel {\n/// \\ingroup ParallelGroup\n/// \\brief Structs that have `insert_into_inbox` methods for commonly used\n/// cases.\n///\n/// Inbox tags can inherit from the inserters to gain the `insert_into_inbox`\n/// static method used by the parallel infrastructure to store data in the\n/// inboxes. Collected in the `InboxInserters` namespace are implementations for\n/// the most common types of data sent that one may encounter.\nnamespace InboxInserters {\n/*!\n * \\brief Inserter for inserting data that is received as the `value_type` (with\n * non-const `key_type`) of a map data structure.\n *\n * An example would be a `std::unordered_map<int, int>`, where the first `int`\n * is the \"spatial\" id of the parallel component array. The data type that is\n * sent to the component would be a `std::pair<int, int>`, or more generally a\n * `std::pair<key_type, mapped_type>`. The inbox tag would be:\n *\n * \\snippet Test_InboxInserters.cpp map tag\n */\ntemplate <typename InboxTag>\nstruct Map {\n  template <typename Inbox, typename TemporalId, typename ReceiveDataType>\n  static bool insert_into_inbox(const gsl::not_null<Inbox*> inbox,\n                                const TemporalId& temporal_id,\n                                ReceiveDataType&& data) {\n    static_assert(std::is_same<std::decay_t<TemporalId>,\n                               typename InboxTag::temporal_id>::value,\n                  \"The temporal_id of the inbox tag does not match the \"\n                  \"received temporal id.\");\n    auto& current_inbox = (*inbox)[temporal_id];\n    ASSERT(0 == current_inbox.count(data.first),\n           \"Receiving data from the 'same' source twice. The message id is: \"\n               << data.first);\n    if (not current_inbox.insert(std::forward<ReceiveDataType>(data)).second) {\n      ERROR(\"Failed to insert data to receive at instance '\"\n            << temporal_id << \"' with tag '\"\n            << pretty_type::get_name<InboxTag>() << \"'.\\n\");\n    }\n    return true;\n  }\n};\n\n/*!\n * \\brief Inserter for inserting data that is received as the `value_type` of a\n * data structure that has an `insert(value_type&&)` member function.\n *\n * Can be used for receiving data into data structures like\n * `std::(unordered_)multiset`.\n *\n * \\snippet Test_InboxInserters.cpp member insert tag\n */\ntemplate <typename InboxTag>\nstruct MemberInsert {\n  template <typename Inbox, typename TemporalId, typename ReceiveDataType>\n  static bool insert_into_inbox(const gsl::not_null<Inbox*> inbox,\n                                const TemporalId& temporal_id,\n                                ReceiveDataType&& data) {\n    static_assert(std::is_same<std::decay_t<TemporalId>,\n                               typename InboxTag::temporal_id>::value,\n                  \"The temporal_id of the inbox tag does not match the \"\n                  \"received temporal id.\");\n    (*inbox)[temporal_id].insert(std::forward<ReceiveDataType>(data));\n    return true;\n  }\n};\n\n/*!\n * \\brief Inserter for data that is inserted by copy/move.\n *\n * \\snippet Test_InboxInserters.cpp value tag\n */\ntemplate <typename InboxTag>\nstruct Value {\n  template <typename Inbox, typename TemporalId, typename ReceiveDataType>\n  static bool insert_into_inbox(const gsl::not_null<Inbox*> inbox,\n                                const TemporalId& temporal_id,\n                                ReceiveDataType&& data) {\n    static_assert(std::is_same<std::decay_t<TemporalId>,\n                               typename InboxTag::temporal_id>::value,\n                  \"The temporal_id of the inbox tag does not match the \"\n                  \"received temporal id.\");\n    (*inbox)[temporal_id] = std::forward<ReceiveDataType>(data);\n    return true;\n  }\n};\n\n/*!\n * \\brief Inserter for inserting data that is received as the `value_type` of a\n * data structure that has an `push_back(value_type&&)` member function.\n *\n * Can be used for receiving data into data structures like\n * `std::vector`.\n *\n * \\snippet Test_InboxInserters.cpp pushback tag\n */\ntemplate <typename InboxTag>\nstruct Pushback {\n  template <typename Inbox, typename TemporalId, typename ReceiveDataType>\n  static bool insert_into_inbox(const gsl::not_null<Inbox*> inbox,\n                                const TemporalId& temporal_id,\n                                ReceiveDataType&& data) {\n    static_assert(std::is_same<std::decay_t<TemporalId>,\n                               typename InboxTag::temporal_id>::value,\n                  \"The temporal_id of the inbox tag does not match the \"\n                  \"received temporal id.\");\n    (*inbox)[temporal_id].push_back(std::forward<ReceiveDataType>(data));\n    return true;\n  }\n};\n}  // namespace InboxInserters\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Info.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions that provide low-level system information such as number\n/// of nodes and processors, in a way that they are mockable in ActionTesting.\n\n#pragma once\n\n#include <cstddef>\n\n/// Functionality for parallelization.\n///\n/// The functions in namespace `Parallel` that return information on\n/// nodes and cores are templated on DistribObject.  Actions should\n/// use these functions rather than the raw charm++ versions (in the\n/// sys namespace in Utilities/System/ParallelInfo.hpp) so that the\n/// mocking framework will see the mocked cores and nodes.\nnamespace Parallel {\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Number of processing elements.\n */\ntemplate <typename T, typename DistribObject>\nT number_of_procs(const DistribObject& distributed_object) {\n  return static_cast<T>(distributed_object.number_of_procs());\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief %Index of my processing element.\n */\ntemplate <typename T, typename DistribObject>\nT my_proc(const DistribObject& distributed_object) {\n  return static_cast<T>(distributed_object.my_proc());\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Number of nodes.\n */\ntemplate <typename T, typename DistribObject>\nT number_of_nodes(const DistribObject& distributed_object) {\n  return static_cast<T>(distributed_object.number_of_nodes());\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief %Index of my node.\n */\ntemplate <typename T, typename DistribObject>\nT my_node(const DistribObject& distributed_object) {\n  return static_cast<T>(distributed_object.my_node());\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Number of processing elements on the given node.\n */\ntemplate <typename T, typename R, typename DistribObject>\nT procs_on_node(const R node_index, const DistribObject& distributed_object) {\n  return static_cast<T>(\n      distributed_object.procs_on_node(static_cast<int>(node_index)));\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief The local index of my processing element on my node.\n * This is in the interval 0, ..., procs_on_node(my_node()) - 1.\n */\ntemplate <typename T, typename DistribObject>\nT my_local_rank(const DistribObject& distributed_object) {\n  return static_cast<T>(distributed_object.my_local_rank());\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief %Index of first processing element on the given node.\n */\ntemplate <typename T, typename R, typename DistribObject>\nT first_proc_on_node(const R node_index,\n                     const DistribObject& distributed_object) {\n  return static_cast<T>(\n      distributed_object.first_proc_on_node(static_cast<int>(node_index)));\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief %Index of the node for the given processing element.\n */\ntemplate <typename T, typename R, typename DistribObject>\nT node_of(const R proc_index, const DistribObject& distributed_object) {\n  return static_cast<T>(\n      distributed_object.node_of(static_cast<int>(proc_index)));\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief The local index for the given processing element on its node.\n */\ntemplate <typename T, typename R, typename DistribObject>\nT local_rank_of(const R proc_index, const DistribObject& distributed_object) {\n  return static_cast<T>(\n      distributed_object.local_rank_of(static_cast<int>(proc_index)));\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/InitializationFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/InitializationFunctions.hpp\"\n\n#include <charm++.h>\n#include <exception>\n#include <sstream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/System/Abort.hpp\"\n\nvoid setup_error_handling() {\n  std::set_terminate([]() {\n    std::exception_ptr exception = std::current_exception();\n    if (exception) {\n      try {\n        std::rethrow_exception(exception);\n      } catch (std::exception& ex) {\n        std::ostringstream os;\n        os << \"Terminated due to an uncaught exception:\\n\" << ex.what();\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n        CkError(\"%s\\n\", os.str().c_str());\n      } catch (...) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n        CkError(\"Terminated due to unknown exception\\n\");\n      }\n    } else {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n      CkError(\n          \"Terminate was called for an unknown reason (not an uncaught \"\n          \"exception), calling Charm++'s abort function to properly \"\n          \"terminate execution.\");\n    }\n    sys::abort(\"\");\n  });\n}\n"
  },
  {
    "path": "src/Parallel/InitializationFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <exception>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nvoid setup_error_handling();\n"
  },
  {
    "path": "src/Parallel/InitializationTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n\nnamespace Parallel {\nnamespace initialization_tag_detail {\ntemplate <template <typename> typename>\nconstexpr bool is_templated() {\n  return true;\n}\n}  // namespace initialization_tag_detail\n\n/*!\n * \\ingroup ParallelGroup\n * Concept for an initialization tag with `pass_metavariables` true.\n */\ntemplate <typename Tag>\nconcept templated_initialization_tag =\n    db::simple_tag<Tag> and Tag::pass_metavariables and\n    initialization_tag_detail::is_templated<Tag::template option_tags>();\n\n/*!\n * \\ingroup ParallelGroup\n * Concept for an initialization tag with `pass_metavariables` false.\n */\ntemplate <typename Tag>\nconcept untemplated_initialization_tag =\n    db::simple_tag<Tag> and not Tag::pass_metavariables and\n    requires { typename Tag::option_tags; };\n\n/*!\n * \\ingroup ParallelGroup\n * Concept for an initialization tag.\n */\ntemplate <typename Tag>\nconcept initialization_tag =\n    templated_initialization_tag<Tag> or untemplated_initialization_tag<Tag>;\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Invoke.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n#include <utility>\n\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/MaxInlineMethodsReached.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n\nnamespace Parallel {\n/*!\n * \\ingroup ParallelGroup\n * \\brief Send the data `args...` to the algorithm running on `proxy`, and tag\n * the message with the identifier `temporal_id`.\n *\n * If the algorithm was previously disabled, set `enable_if_disabled` to true to\n * enable the algorithm on the parallel component.\n */\ntemplate <typename ReceiveTag, typename Proxy, typename ReceiveDataType>\nvoid receive_data(Proxy&& proxy, typename ReceiveTag::temporal_id temporal_id,\n                  ReceiveDataType&& receive_data,\n                  const bool enable_if_disabled = false) {\n  // Both branches of this if statement call into the charm proxy system, but\n  // because we specify [inline] for array chares, we must specify the\n  // `ReceiveDataType` explicitly in the template arguments when dispatching to\n  // array chares.\n  // This is required because of inconsistent overloads in the generated charm++\n  // files when [inline] is specified vs when it is not. The [inline] version\n  // generates a function signature with template parameters associated with\n  // each function argument, ensuring a redundant template parameter for the\n  // `ReceiveDataType`. Then, that template parameter cannot be inferred so must\n  // be explicitly specified.\n  if constexpr (is_array_proxy<std::decay_t<Proxy>>::value) {\n    proxy.template receive_data<ReceiveTag, std::decay_t<ReceiveDataType>>(\n        std::move(temporal_id), std::forward<ReceiveDataType>(receive_data),\n        enable_if_disabled);\n  } else {\n    proxy.template receive_data<ReceiveTag>(\n        std::move(temporal_id), std::forward<ReceiveDataType>(receive_data),\n        enable_if_disabled);\n  }\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Send a pointer `message` to the algorithm running on `proxy`.\n *\n * Here, `message` should hold all the information you need as member variables\n * of the object. This includes, temporal ID identifiers, the data itself, and\n * any auxilary information that needs to be communicated. The `ReceiveTag`\n * associated with this `message` should be able unpack the information that was\n * sent.\n *\n * If the component associated with the `proxy` you are calling this on is\n * running on the same charm-node, the exact pointer `message` is sent to the\n * receiving component. No copies of data are done. If the receiving component\n * is on a different charm-node, then the data pointed to by `message` is\n * copied, sent through charm, and unpacked on the receiving component. The\n * pointer that is passed to the algorithm on the receiving component then\n * points to the copied data on the receiving component.\n *\n * \\warning You cannot use the `message` pointer after you call this function.\n * Doing so will result in undefined behavior because something else may be\n * controlling the pointer.\n */\ntemplate <typename ReceiveTag, typename Proxy, typename MessageType>\nvoid receive_data(Proxy&& proxy, MessageType* message) {\n  static_assert(is_array_proxy<std::decay_t<Proxy>>::value or\n                    is_array_element_proxy<std::decay_t<Proxy>>::value,\n                \"Charm++ messages can only be used with Array[Element] chares \"\n                \"at the moment. If you want to use them with other types of \"\n                \"components, you will need to implement it.\");\n  proxy.template receive_data<ReceiveTag, std::decay_t<MessageType>>(message);\n}\n\n/// @{\n/*!\n * \\ingroup ParallelGroup\n * \\brief Invoke a simple action on `proxy`\n */\ntemplate <typename Action, typename Proxy>\nvoid simple_action(Proxy&& proxy) {\n  proxy.template simple_action<Action>();\n}\n\ntemplate <typename Action, typename Proxy, typename Arg0, typename... Args>\nvoid simple_action(Proxy&& proxy, Arg0&& arg0, Args&&... args) {\n  proxy.template simple_action<Action, std::decay_t<Arg0>,\n                               std::decay_t<Args>...>(\n      std::tuple<std::decay_t<Arg0>, std::decay_t<Args>...>(\n          std::forward<Arg0>(arg0), std::forward<Args>(args)...));\n}\n/// @}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Invoke a local synchronous action on `proxy`\n */\ntemplate <typename Action, typename Proxy, typename... Args>\ndecltype(auto) local_synchronous_action(Proxy&& proxy, Args&&... args) {\n  return Parallel::local_branch(proxy)\n      ->template local_synchronous_action<Action>(std::forward<Args>(args)...);\n}\n\n/// @{\n/*!\n * \\ingroup ParallelGroup\n * \\brief Invoke a threaded action on `proxy`, where the proxy must be a\n * nodegroup.\n */\ntemplate <typename Action, typename Proxy>\nvoid threaded_action(Proxy&& proxy) {\n  proxy.template threaded_action<Action>();\n}\n\ntemplate <typename Action, typename Proxy, typename Arg0, typename... Args>\nvoid threaded_action(Proxy&& proxy, Arg0&& arg0, Args&&... args) {\n  proxy.template threaded_action<Action>(\n      std::tuple<std::decay_t<Arg0>, std::decay_t<Args>...>(\n          std::forward<Arg0>(arg0), std::forward<Args>(args)...));\n}\n/// @}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Local.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n\n#include \"Parallel/TypeTraits.hpp\"\n\nnamespace Parallel {\n/// \\cond\nnamespace Algorithms {\nstruct Singleton;\n}  // namespace Algorithms\n/// \\endcond\n\n/// Wrapper for calling Charm++'s `.ckLocal()` on a proxy\n///\n/// The Proxy must be to a Charm++ array chare (implementing a singleton\n/// design pattern using a single-element array) or to a Charm++ array element\n/// chare (i.e., the proxy obtained by indexing into a Charm++ array chare).\n///\n/// The function returns a pointer to the chare if it exists on the local\n/// processor, and NULL if it does not. See the Charm++ documentation.\n/// It is the responsibility of the user to check the result pointer is valid.\ntemplate <typename Proxy>\nauto* local(Proxy&& proxy) {\n  // It only makes sense to call .ckLocal() on some kinds of proxies\n  static_assert(is_array_element_proxy<std::decay_t<Proxy>>::value or\n                is_array_proxy<std::decay_t<Proxy>>::value);\n  if constexpr (std::is_same_v<\n                    Parallel::Algorithms::Singleton,\n                    typename Parallel::get_parallel_component_from_proxy<\n                        std::decay_t<Proxy>>::type::chare_type>) {\n    // The array case should be a single-element array serving as a singleton\n    return proxy[0].ckLocal();\n  } else {\n    return proxy.ckLocal();\n  }\n}\n\n/// Wrapper for calling Charm++'s `.ckLocalBranch()` on a proxy\n///\n/// The Proxy must be to a Charm++ group chare or nodegroup chare.\n///\n/// The function returns a pointer to the local group/nodegroup chare.\ntemplate <typename Proxy>\nauto* local_branch(Proxy&& proxy) {\n  // It only makes sense to call .ckLocalBranch() on some kinds of proxies\n  static_assert(is_group_proxy<std::decay_t<Proxy>>::value or\n                is_node_group_proxy<std::decay_t<Proxy>>::value);\n  return proxy.ckLocalBranch();\n}\n\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Main.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmodule Main {\n\n  include \"DataStructures/TaggedTuple.hpp\";\n  include \"string\";\n\n  include \"Parallel/ReductionDeclare.hpp\";\n  include \"Utilities/Functional.hpp\";\n\n  namespace Parallel {\n  template <typename Metavariables>\n  mainchare [migratable] Main {\n    entry Main(CkArgMsg* msg);\n    entry void allocate_remaining_components_and_execute_initialization_phase();\n\n    template <typename InvokeCombine, typename... Tags>\n    entry [reductiontarget] void phase_change_reduction(\n        Parallel::ReductionData<Parallel::ReductionDatum<\n            tuples::TaggedTuple<Tags...>, InvokeCombine, funcl::Identity,\n            std::index_sequence<>>>\n            reduction_data);\n\n    entry [reductiontarget] void did_all_elements_terminate(\n        bool all_elements_terminated);\n\n    entry void execute_next_phase();\n    entry void start_load_balance();\n    entry void start_write_checkpoint();\n    entry void add_exception_message(std::string exception_message);\n    entry void post_deadlock_analysis_termination();\n  }\n\n  namespace detail {\n  template <typename Metavariables>\n  array [1D] AtSyncIndicator {\n    entry AtSyncIndicator(CProxy_Main<Metavariables> main_proxy);\n\n    entry void IndicateAtSync();\n  }\n  }  // namespace detail\n  }  // namespace Parallel\n}\n"
  },
  {
    "path": "src/Parallel/Main.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/Main.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <regex>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace Parallel::detail {\nstd::tuple<std::string, std::string, size_t> checkpoints_dir_prefix_pad() {\n  const std::string checkpoints_dir = \"Checkpoints\";\n  const std::string prefix = \"Checkpoint_\";\n  constexpr size_t pad = 4;\n  return std::make_tuple(checkpoints_dir, prefix, pad);\n}\n\nstd::string next_checkpoint_dir(const size_t checkpoint_dir_counter) {\n  const auto [checkpoints_dir, prefix, pad] =\n      detail::checkpoints_dir_prefix_pad();\n  const std::string counter = std::to_string(checkpoint_dir_counter);\n  const std::string padded_counter =\n      std::string(pad - counter.size(), '0').append(counter);\n  std::string result = checkpoints_dir + \"/\" + prefix + padded_counter;\n  if (file_system::check_if_dir_exists(result)) {\n    ERROR(\"Can't write checkpoint: dir \" + result + \" already exists!\");\n  }\n  return result;\n}\n\nvoid check_future_checkpoint_dirs_available(\n    const size_t checkpoint_dir_counter) {\n  const auto [checkpoints_dir, prefix, pad] =\n      detail::checkpoints_dir_prefix_pad();\n  if (not file_system::check_if_dir_exists(checkpoints_dir)) {\n    return;\n  }\n  const auto next_checkpoint =\n      detail::next_checkpoint_dir(checkpoint_dir_counter);\n\n  // Find existing files with names that match the checkpoint dir name pattern\n  const auto all_files = file_system::ls(checkpoints_dir);\n  const std::regex re(prefix + \"[0-9]{\" + std::to_string(pad) + \"}\");\n  std::vector<std::string> checkpoint_files;\n  std::copy_if(all_files.begin(), all_files.end(),\n               std::back_inserter(checkpoint_files),\n               [&re](const std::string& s) { return std::regex_match(s, re); });\n\n  // Using string comparison of filenames, check that all the files we found\n  // are from older checkpoints, but not from future checkpoints\n  const bool found_older_checkpoints_only = std::all_of(\n      checkpoint_files.begin(), checkpoint_files.end(),\n      [&next_checkpoint](const std::string& s) { return s < next_checkpoint; });\n  if (not found_older_checkpoints_only) {\n    ERROR(\n        \"Can't start run: found checkpoints that may be overwritten!\\n\"\n        \"Dirs from \"\n        << next_checkpoint << \" onward must not exist.\\n\");\n  }\n}\n}  // namespace Parallel::detail\n"
  },
  {
    "path": "src/Parallel/Main.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the Charm++ mainchare.\n\n#pragma once\n\n#include <array>\n#include <boost/program_options.hpp>\n#include <charm++.h>\n#include <cstddef>\n#include <initializer_list>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <type_traits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Informer/Informer.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/Tags.hpp\"\n#include \"Parallel/AlgorithmMetafunctions.hpp\"\n#include \"Parallel/CharmRegistration.hpp\"\n#include \"Parallel/CreateFromOptions.hpp\"\n#include \"Parallel/ExitCode.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/InitializePhaseChangeDecisionData.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Parallel/PhaseControlReductionHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/ResourceInfo.hpp\"\n#include \"Parallel/Tags/ResourceInfo.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Formaline.hpp\"\n#include \"Utilities/Kokkos/KokkosCore.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/System/Exit.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\n#include \"Parallel/Main.decl.h\"\n\nnamespace Parallel {\nnamespace detail {\nCREATE_IS_CALLABLE(run_deadlock_analysis_simple_actions)\nCREATE_IS_CALLABLE_V(run_deadlock_analysis_simple_actions)\n\n// Return the dir name for the Charm++ checkpoints as well as the prefix for\n// checkpoint names and their padding. This is a \"detail\" function so that\n// these pieces can be defined in one place only.\nstd::tuple<std::string, std::string, size_t> checkpoints_dir_prefix_pad();\n\n// Return the dir name for the next Charm++ checkpoint; check and error if\n// this name already exists and writing the checkpoint would be unsafe.\nstd::string next_checkpoint_dir(size_t checkpoint_dir_counter);\n\n// Check if future checkpoint dirs are available; error if any already exist.\nvoid check_future_checkpoint_dirs_available(size_t checkpoint_dir_counter);\n}  // namespace detail\n\n/// \\ingroup ParallelGroup\n/// The main function of a Charm++ executable.\n/// See [the Parallelization documentation](group__ParallelGroup.html#details)\n/// for an overview of Metavariables, Phases, and parallel components.\ntemplate <typename Metavariables>\nclass Main : public CBase_Main<Metavariables> {\n public:\n  using component_list = typename Metavariables::component_list;\n  using const_global_cache_tags = get_const_global_cache_tags<Metavariables>;\n  using mutable_global_cache_tags =\n      get_mutable_global_cache_tags<Metavariables>;\n\n  using phase_change_tags_and_combines_list =\n      PhaseControl::get_phase_change_tags<Metavariables>;\n  /// \\cond HIDDEN_SYMBOLS\n  /// The constructor used to register the class\n  explicit Main(const Parallel::charmxx::\n                    MainChareRegistrationConstructor& /*used_for_reg*/) {}\n  ~Main() override {\n    (void)Parallel::charmxx::RegisterChare<\n        Main<Metavariables>, CkIndex_Main<Metavariables>>::registrar;\n  }\n  Main(const Main&) = default;\n  Main& operator=(const Main&) = default;\n  Main(Main&&) = default;\n  Main& operator=(Main&&) = default;\n  /// \\endcond\n\n  explicit Main(CkArgMsg* msg);\n  explicit Main(CkMigrateMessage* msg);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// Allocate singleton components and the initial elements of array\n  /// components, then execute the initialization phase on each component\n  void allocate_remaining_components_and_execute_initialization_phase();\n\n  /// Determine the next phase of the simulation and execute it.\n  void execute_next_phase();\n\n  /// Place the Charm++ call that starts load balancing\n  ///\n  /// \\details This call is wrapped within an entry method so that it may be\n  /// used as the callback after a quiescence detection.\n  void start_load_balance();\n\n  /// Place the Charm++ call that starts writing a checkpoint\n  /// Reset the checkpoint counter to zero if the checkpoints directory does not\n  /// exist (this happens when the simulation continues in a new segment).\n  ///\n  /// \\details This call is wrapped within an entry method so that it may be\n  /// used as the callback after a quiescence detection.\n  void start_write_checkpoint();\n\n  /// Reduction target for data used in phase change decisions.\n  ///\n  /// It is required that the `Parallel::ReductionData` holds a single\n  /// `tuples::TaggedTuple`.\n  template <typename InvokeCombine, typename... Tags>\n  void phase_change_reduction(\n      ReductionData<ReductionDatum<tuples::TaggedTuple<Tags...>, InvokeCombine,\n                                   funcl::Identity, std::index_sequence<>>>\n          reduction_data);\n\n  /// Add an exception to the list of exceptions. Upon a phase change we print\n  /// all the received exceptions and exit.\n  ///\n  /// Upon receiving an exception all algorithms are terminated to guarantee\n  /// quiescence occurs soon after the exception is reported.\n  void add_exception_message(std::string exception_message);\n\n  /// A reduction target used to determine if all the elements of the array,\n  /// group, nodegroup, or singleton parallel components terminated\n  /// successfully.\n  ///\n  /// This allows detecting deadlocks in iterable actions, but not simple or\n  /// reduction action.\n  void did_all_elements_terminate(bool all_elements_terminated);\n\n  /// Prints exit info and stops the executable with failure if a deadlock was\n  /// detected.\n  void post_deadlock_analysis_termination();\n\n private:\n  // Starts a reduction on the component specified by\n  // the current_termination_check_index_ member variable, then increment\n  // current_termination_check_index_\n  void check_if_component_terminated_correctly();\n\n  // After a restart, update the const global cache with new values constructed\n  // from parsing an overlay input file.\n  void update_const_global_cache_from_input_file();\n\n  // Lists of all parallel component types\n  using group_component_list =\n      tmpl::filter<component_list, tmpl::or_<Parallel::is_group<tmpl::_1>,\n                                             Parallel::is_nodegroup<tmpl::_1>>>;\n  using all_array_component_list =\n      tmpl::filter<component_list, Parallel::is_array<tmpl::_1>>;\n  using non_bound_array_component_list =\n      tmpl::filter<component_list,\n                   tmpl::and_<Parallel::is_array<tmpl::_1>,\n                              tmpl::not_<Parallel::is_bound_array<tmpl::_1>>>>;\n  using bound_array_component_list =\n      tmpl::filter<component_list,\n                   tmpl::and_<Parallel::is_array<tmpl::_1>,\n                              Parallel::is_bound_array<tmpl::_1>>>;\n  using singleton_component_list =\n      tmpl::filter<component_list, Parallel::is_singleton<tmpl::_1>>;\n\n  template <typename ParallelComponent>\n  using parallel_component_options = Parallel::get_option_tags<\n      typename ParallelComponent::simple_tags_from_options, Metavariables>;\n  template <typename ArrayComponent>\n  using array_component_allocation_options =\n      Parallel::get_option_tags<typename ArrayComponent::array_allocation_tags,\n                                Metavariables>;\n  using option_list = tmpl::remove_duplicates<tmpl::flatten<tmpl::list<\n      Parallel::OptionTags::ResourceInfo<Metavariables>,\n      Parallel::get_option_tags<const_global_cache_tags, Metavariables>,\n      Parallel::get_option_tags<mutable_global_cache_tags, Metavariables>,\n      tmpl::transform<component_list,\n                      tmpl::bind<parallel_component_options, tmpl::_1>>,\n      tmpl::transform<\n          all_array_component_list,\n          tmpl::bind<array_component_allocation_options, tmpl::_1>>>>>;\n  using overlayable_option_list =\n      Parallel::get_overlayable_option_list<Metavariables>;\n\n  Parallel::Phase current_phase_{Parallel::Phase::Initialization};\n  CProxy_GlobalCache<Metavariables> global_cache_proxy_;\n  detail::CProxy_AtSyncIndicator<Metavariables> at_sync_indicator_proxy_;\n  std::string input_file_{};\n  Options::Parser<tmpl::remove<option_list, Options::Tags::InputSource>>\n      parser_{Metavariables::help};\n  // This is only used during startup, and will be cleared after all\n  // the chares are created.  It is a member variable because passing\n  // local state through charm callbacks is painful.\n  tuples::tagged_tuple_from_typelist<option_list> options_{};\n  // type to be determined by the collection of available phase changers in the\n  // Metavariables\n  tuples::tagged_tuple_from_typelist<phase_change_tags_and_combines_list>\n      phase_change_decision_data_;\n  size_t checkpoint_dir_counter_ = 0_st;\n  Parallel::ResourceInfo<Metavariables> resource_info_{};\n  // All exception errors we've received so far.\n  std::vector<std::string> exception_messages_{};\n  // Used to keep track of which parallel component we are checking has\n  // successfully terminated.\n  size_t current_termination_check_index_{0};\n  std::vector<std::string> components_that_did_not_terminate_{};\n  bool just_restored_from_checkpoint_ = false;\n};\n\nnamespace detail {\n\n// Charm++ AtSync effectively requires an additional global sync to the\n// quiescence detection we do for switching phases. However, AtSync only needs\n// to be called for one array to trigger the sync-based load balancing, so the\n// AtSyncIndicator is a silly hack to have a centralized indication to start\n// load-balancing. It participates in the `AtSync` barrier, but is not\n// migratable, and should only be constructed by the `Main` chare on the same\n// processor as the `Main` chare. When load-balancing occurs, main invokes the\n// member function `IndicateAtSync()`, and when `ResumeFromSync()` is called\n// from charm, `AtSyncIndicator` simply passes control back to the Main chare\n// via `execute_next_phase()`.\ntemplate <typename Metavariables>\nclass AtSyncIndicator : public CBase_AtSyncIndicator<Metavariables> {\n public:\n  AtSyncIndicator(CProxy_Main<Metavariables> main_proxy);\n  AtSyncIndicator(const AtSyncIndicator&) = default;\n  AtSyncIndicator& operator=(const AtSyncIndicator&) = default;\n  AtSyncIndicator(AtSyncIndicator&&) = default;\n  AtSyncIndicator& operator=(AtSyncIndicator&&) = default;\n  ~AtSyncIndicator() override {\n    (void)Parallel::charmxx::RegisterChare<\n        AtSyncIndicator<Metavariables>,\n        CkIndex_AtSyncIndicator<Metavariables>>::registrar;\n  }\n\n  void IndicateAtSync();\n\n  void ResumeFromSync() override;\n\n  explicit AtSyncIndicator(CkMigrateMessage* msg)\n      : CBase_AtSyncIndicator<Metavariables>(msg) {}\n\n  void pup(PUP::er& p) override { p | main_proxy_; }\n\n private:\n  CProxy_Main<Metavariables> main_proxy_;\n};\n\ntemplate <typename Metavariables>\nAtSyncIndicator<Metavariables>::AtSyncIndicator(\n    CProxy_Main<Metavariables> main_proxy)\n    : main_proxy_{main_proxy} {\n  this->usesAtSync = true;\n  this->setMigratable(false);\n}\n\ntemplate <typename Metavariables>\nvoid AtSyncIndicator<Metavariables>::IndicateAtSync() {\n  this->AtSync();\n}\n\ntemplate <typename Metavariables>\nvoid AtSyncIndicator<Metavariables>::ResumeFromSync() {\n  main_proxy_.execute_next_phase();\n}\n}  // namespace detail\n\n// ================================================================\n\ntemplate <typename Metavariables>\nMain<Metavariables>::Main(CkArgMsg* msg) {\n#ifdef SPECTRE_KOKKOS\n  Kokkos::initialize(msg->argc, msg->argv);\n#endif  // SPECTRE_KOKKOS\n\n  Informer::print_startup_info(msg);\n\n  /// \\todo detail::register_events_to_trace();\n\n  namespace bpo = boost::program_options;\n  try {\n    bpo::options_description command_line_options;\n    // disable clang-format because it combines the repeated call operator\n    // invocations making the code more difficult to parse.\n    // clang-format off\n    command_line_options.add_options()\n        (\"help,h\", \"Describe program options\")\n        (\"check-options\", \"Check input file options\")\n        (\"dump-source-tree-as\", bpo::value<std::string>(),\n         \"If specified, then a gzip archive of the source tree is dumped \"\n         \"with the specified name. The archive can be expanded using \"\n         \"'tar -xzf ARCHIVE.tar.gz'\")\n        (\"dump-paths\",\n         \"Dump the PATH, CPATH, LD_LIBRARY_PATH, LIBRARY_PATH, and \"\n         \"CMAKE_PREFIX_PATH at compile time.\")\n        (\"dump-environment\",\n         \"Dump the result of printenv at compile time.\")\n        (\"dump-build-info\",\n         \"Dump the contents of SpECTRE's BuildInfo.txt\")\n        (\"dump-only\",\n         \"Exit after dumping requested information.\")\n        (\"copyright-and-licenses\",\n         \"Returns all of the copyright and license info for SpECTRE and \"\n         \"its dependencies.\")\n        ;\n    // clang-format on\n\n    // False if there are no other options besides the explicitly added\n    // Parallel::OptionTags::ResourceInfo<Metavariables>,\n    constexpr bool has_options = tmpl::size<option_list>::value > 1;\n    // Add input-file option if it makes sense\n    Overloader{\n        [&command_line_options](std::true_type /*meta*/, auto mv,\n                                int /*gcc_bug*/)\n            -> std::void_t<\n                decltype(tmpl::type_from<decltype(mv)>::input_file)> {\n          // Metavariables has options and default input file name\n          command_line_options.add_options()(\n              \"input-file\",\n              bpo::value<std::string>()->default_value(\n                  tmpl::type_from<decltype(mv)>::input_file),\n              \"Input file name\");\n        },\n        [&command_line_options](std::true_type /*meta*/, auto /*mv*/,\n                                auto... /*unused*/) {\n          // Metavariables has options and no default input file name\n          command_line_options.add_options()(\n              \"input-file\", bpo::value<std::string>(), \"Input file name\");\n        },\n        [](std::false_type /*meta*/, auto mv, int /*gcc_bug*/)\n            -> std::void_t<\n                decltype(tmpl::type_from<decltype(mv)>::input_file)> {\n          // Metavariables has no options and default input file name\n\n          // always false, but must depend on mv\n          static_assert(std::is_same_v<decltype(mv), void>,\n                        \"Metavariables supplies input file name, \"\n                        \"but there are no options\");\n          ERROR(\"This should have failed at compile time\");\n        },\n        [](std::false_type /*meta*/, auto... /*unused*/) {\n          // Metavariables has no options and no default input file name\n        }}(std::bool_constant<has_options>{}, tmpl::type_<Metavariables>{}, 0);\n\n    bpo::command_line_parser command_line_parser(msg->argc, msg->argv);\n    command_line_parser.options(command_line_options);\n\n    const bool ignore_unrecognized_command_line_options = Overloader{\n        [](auto mv, int /*gcc_bug*/)\n            -> decltype(tmpl::type_from<decltype(mv)>::\n                            ignore_unrecognized_command_line_options) {\n          return tmpl::type_from<\n              decltype(mv)>::ignore_unrecognized_command_line_options;\n        },\n        [](auto /*mv*/, auto... /*meta*/) { return false; }}(\n        tmpl::type_<Metavariables>{}, 0);\n    if (ignore_unrecognized_command_line_options) {\n      // Allow unknown --options\n      command_line_parser.allow_unregistered();\n    } else {\n      // Forbid positional parameters\n      command_line_parser.positional({});\n    }\n\n    bpo::variables_map parsed_command_line_options;\n    bpo::store(command_line_parser.run(), parsed_command_line_options);\n    bpo::notify(parsed_command_line_options);\n\n    if (parsed_command_line_options.count(\"help\") != 0) {\n      Parallel::printf(\"%s\\n%s\", command_line_options, parser_.help());\n      sys::exit();\n    }\n\n    if (parsed_command_line_options.count(\"dump-source-tree-as\") != 0) {\n      formaline::write_to_file(\n          parsed_command_line_options[\"dump-source-tree-as\"].as<std::string>());\n      Parallel::printf(\"Dumping archive of source tree at link time.\\n\");\n    }\n    if (parsed_command_line_options.count(\"dump-paths\") != 0) {\n      Parallel::printf(\"Paths at link time were:\\n%s\\n\",\n                       formaline::get_paths());\n    }\n    if (parsed_command_line_options.count(\"dump-environment\") != 0) {\n      Parallel::printf(\"Environment variables at link time were:\\n%s\\n\",\n                       formaline::get_environment_variables());\n    }\n    if (parsed_command_line_options.count(\"dump-build-info\") != 0) {\n      Parallel::printf(\"BuildInfo.txt at link time was:\\n%s\\n\",\n                       formaline::get_build_info());\n    }\n    if (parsed_command_line_options.count(\"copyright-and-licenses\") != 0) {\n      Parallel::printf(\"%s\\n\", copyright_and_license_info());\n    }\n    if (parsed_command_line_options.count(\"dump-only\") != 0) {\n      sys::exit();\n    }\n\n    if (has_options) {\n      if (parsed_command_line_options.count(\"input-file\") == 0) {\n        ERROR_NO_TRACE(\"No default input file name.  Pass --input-file.\");\n      }\n      input_file_ = parsed_command_line_options[\"input-file\"].as<std::string>();\n      parser_.parse_file(input_file_);\n    } else {\n      if constexpr (tmpl::size<singleton_component_list>::value > 0) {\n        parser_.parse(\n            \"ResourceInfo:\\n\"\n            \"  AvoidGlobalProc0: false\\n\"\n            \"  Singletons: Auto\\n\");\n      } else {\n        parser_.parse(\n            \"ResourceInfo:\\n\"\n            \"  AvoidGlobalProc0: false\\n\");\n      }\n    }\n\n    if (parsed_command_line_options.count(\"check-options\") != 0) {\n      // Force all the options to be created.\n      parser_.template apply<option_list, Metavariables>([](auto... args) {\n        (void)std::initializer_list<char>{((void)args, '0')...};\n      });\n      if (has_options) {\n        Parallel::printf(\"\\n%s parsed successfully!\\n\", input_file_);\n      } else {\n        // This is still considered successful, since it means the\n        // program would have started.\n        Parallel::printf(\"\\nNo options to check!\\n\");\n      }\n\n      // Include a check that the checkpoint dirs are available for writing as\n      // part of checking the option parsing. Doing these checks together helps\n      // catch more user errors before running the executable 'for real'.\n      //\n      // Note we don't do this check at the beginning of the Main chare\n      // constructor because we don't _always_ want to error if checkpoint dirs\n      // already exist. For example, running the executable with flags like\n      // `--help` or `--dump-source-tree-as` should succeed even if checkpoints\n      // were previously written.\n      detail::check_future_checkpoint_dirs_available(checkpoint_dir_counter_);\n\n      sys::exit();\n    }\n\n    options_ =\n        parser_.template apply<option_list, Metavariables>([](auto... args) {\n          return tuples::tagged_tuple_from_typelist<option_list>(\n              std::move(args)...);\n        });\n\n    resource_info_ =\n        tuples::get<Parallel::OptionTags::ResourceInfo<Metavariables>>(\n            options_);\n\n    Parallel::printf(\"\\nOption parsing completed.\\n\");\n  } catch (const bpo::error& e) {\n    ERROR(e.what());\n  }\n\n  detail::check_future_checkpoint_dirs_available(checkpoint_dir_counter_);\n\n  global_cache_proxy_ = CProxy_GlobalCache<Metavariables>::ckNew(\n      Parallel::create_from_options<Metavariables>(options_,\n                                                   const_global_cache_tags{}),\n      Parallel::create_from_options<Metavariables>(options_,\n                                                   mutable_global_cache_tags{}),\n      this->thisProxy);\n\n  // Now that the GlobalCache has been built, create the singleton map which\n  // will be used to allocate all the singletons. We need to be careful here\n  // because the parallel components have not been set at this point, so if we\n  // try to Parallel::get_parallel_component here, an error will occur. This\n  // call is OK though because build_singleton_map() only uses the parallel info\n  // functions from the GlobalCache (like cache.number_of_procs()).\n  resource_info_.build_singleton_map(\n      *Parallel::local_branch(global_cache_proxy_));\n\n  // Now that the singleton map has been built, set the resource info in the\n  // GlobalCache (if the tags exist). Since this info will be constant\n  // throughout a simulation, we opt for directly editing a const tag in the\n  // GlobalCache before we pass it to any other parallel component rather than\n  // having a mutable tag and using a mutate call to set it.\n  global_cache_proxy_.set_resource_info(resource_info_);\n\n  // Now that the singleton map has been built, we have to replace the\n  // ResourceInfo that was created from options with the one that has all the\n  // correct singleton assignments so simple tags can be created from options\n  // with a valid ResourceInfo.\n  get<Parallel::OptionTags::ResourceInfo<Metavariables>>(options_) =\n      resource_info_;\n\n  at_sync_indicator_proxy_ =\n      detail::CProxy_AtSyncIndicator<Metavariables>::ckNew();\n  at_sync_indicator_proxy_[0].insert(this->thisProxy, sys::my_proc());\n  at_sync_indicator_proxy_.doneInserting();\n\n  using parallel_component_tag_list = tmpl::transform<\n      component_list,\n      tmpl::bind<\n          tmpl::type_,\n          tmpl::bind<Parallel::proxy_from_parallel_component, tmpl::_1>>>;\n  tuples::tagged_tuple_from_typelist<parallel_component_tag_list>\n      the_parallel_components;\n\n  // Print info on DataBox variants\n#ifdef SPECTRE_DEBUG\n  Parallel::printf(\"\\nParallel components:\\n\");\n  tmpl::for_each<component_list>([](auto parallel_component_v) {\n    using parallel_component = tmpl::type_from<decltype(parallel_component_v)>;\n    using chare_type = typename parallel_component::chare_type;\n    using charm_type = Parallel::charm_types_with_parameters<\n        parallel_component, typename Parallel::get_array_index<\n                                chare_type>::template f<parallel_component>>;\n    Parallel::printf(\n        \"  %s (%s) has a DataBox with %u items.\\n\",\n        pretty_type::name<parallel_component>(),\n        pretty_type::name<chare_type>(),\n        tmpl::size<\n            typename charm_type::algorithm::databox_type::tags_list>::value);\n  });\n  Parallel::printf(\"\\n\");\n#endif  // SPECTRE_DEBUG\n\n  // Construct the group proxies with a dependency on the GlobalCache proxy\n  CkEntryOptions global_cache_dependency;\n  global_cache_dependency.setGroupDepID(global_cache_proxy_.ckGetGroupID());\n\n  tmpl::for_each<group_component_list>([this, &the_parallel_components,\n                                        &global_cache_dependency](\n                                           auto parallel_component_v) {\n    using parallel_component = tmpl::type_from<decltype(parallel_component_v)>;\n    using ParallelComponentProxy =\n        Parallel::proxy_from_parallel_component<parallel_component>;\n    tuples::get<tmpl::type_<ParallelComponentProxy>>(the_parallel_components) =\n        ParallelComponentProxy::ckNew(\n            global_cache_proxy_,\n            Parallel::create_from_options<Metavariables>(\n                options_,\n                typename parallel_component::simple_tags_from_options{}),\n            &global_cache_dependency);\n  });\n\n  // Create proxies for empty array chares (whose elements will be created by\n  // the allocate functions of the array components during\n  // execute_initialization_phase)\n  tmpl::for_each<non_bound_array_component_list>([&the_parallel_components](\n                                                     auto parallel_component) {\n    using ParallelComponentProxy = Parallel::proxy_from_parallel_component<\n        tmpl::type_from<decltype(parallel_component)>>;\n    tuples::get<tmpl::type_<ParallelComponentProxy>>(the_parallel_components) =\n        ParallelComponentProxy::ckNew();\n  });\n\n  // Create proxies for empty bound array chares\n  tmpl::for_each<bound_array_component_list>([&the_parallel_components](\n                                                 auto parallel_component) {\n    using ParallelComponentProxy = Parallel::proxy_from_parallel_component<\n        tmpl::type_from<decltype(parallel_component)>>;\n    CkArrayOptions opts;\n    opts.bindTo(\n        tuples::get<tmpl::type_<Parallel::proxy_from_parallel_component<\n            typename tmpl::type_from<decltype(parallel_component)>::bind_to>>>(\n            the_parallel_components));\n    tuples::get<tmpl::type_<ParallelComponentProxy>>(the_parallel_components) =\n        ParallelComponentProxy::ckNew(opts);\n  });\n\n  // Create proxies for singletons (which are single-element charm++ arrays)\n  tmpl::for_each<singleton_component_list>([&the_parallel_components](\n                                               auto parallel_component) {\n    using ParallelComponentProxy = Parallel::proxy_from_parallel_component<\n        tmpl::type_from<decltype(parallel_component)>>;\n    tuples::get<tmpl::type_<ParallelComponentProxy>>(the_parallel_components) =\n        ParallelComponentProxy::ckNew();\n  });\n\n  // Send the complete list of parallel_components to the GlobalCache on\n  // each Charm++ node.  After all nodes have finished, the callback is\n  // executed.\n  CkCallback callback(\n      CkIndex_Main<Metavariables>::\n          allocate_remaining_components_and_execute_initialization_phase(),\n      this->thisProxy);\n  global_cache_proxy_.set_parallel_components(the_parallel_components,\n                                              callback);\n\n  get<Tags::ExitCode>(phase_change_decision_data_) =\n      Parallel::ExitCode::Complete;\n  PhaseControl::initialize_phase_change_decision_data(\n      make_not_null(&phase_change_decision_data_),\n      *Parallel::local_branch(global_cache_proxy_));\n\n  printer_chare = CProxy_PrinterChare::ckNew(1);\n  printer_chare_is_set = true;\n}\n\ntemplate <typename Metavariables>\nMain<Metavariables>::Main(CkMigrateMessage* msg)\n    : CBase_Main<Metavariables>(msg) {}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::pup(PUP::er& p) {  // NOLINT\n  p | current_phase_;\n  p | global_cache_proxy_;\n  p | at_sync_indicator_proxy_;\n  p | input_file_;\n  p | parser_;\n  // Note: we don't serialize options_, because it's used as a temporary in\n  // startup only (see comment in class definition).\n  p | phase_change_decision_data_;\n\n  p | checkpoint_dir_counter_;\n  p | resource_info_;\n  p | exception_messages_;\n  p | current_termination_check_index_;\n  p | components_that_did_not_terminate_;\n  if (p.isUnpacking()) {\n    detail::check_future_checkpoint_dirs_available(checkpoint_dir_counter_);\n    // Main doesn't migrate unless checkpointing or restarting, so we can\n    // indicate here that we've just restored from checkpoint.\n    just_restored_from_checkpoint_ = true;\n    // Initialize Kokkos on restart\n#ifdef SPECTRE_KOKKOS\n  Kokkos::initialize();\n#endif  // SPECTRE_KOKKOS\n  }\n\n  // For now we only support restarts on the same hardware configuration (same\n  // number of nodes and same procs per node) used when writing the checkpoint.\n  // We check this by adding counters to the pup stream.\n  if (p.isUnpacking()) {\n    int previous_nodes = 0;\n    int previous_procs = 0;\n    p | previous_nodes;\n    p | previous_procs;\n    if (previous_nodes != sys::number_of_nodes() or\n        previous_procs != sys::number_of_procs()) {\n      ERROR(\n          \"Must restart on the same hardware configuration used when writing \"\n          \"the checkpoint.\\n\"\n          \"Checkpoint written with \"\n          << previous_nodes << \" nodes, \" << previous_procs\n          << \" procs.\\n\"\n             \"Restarted with \"\n          << sys::number_of_nodes() << \" nodes, \" << sys::number_of_procs()\n          << \" procs.\");\n    }\n  } else {\n    int current_nodes = sys::number_of_nodes();\n    int current_procs = sys::number_of_procs();\n    p | current_nodes;\n    p | current_procs;\n  }\n}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::\n    allocate_remaining_components_and_execute_initialization_phase() {\n  if (current_phase_ != Parallel::Phase::Initialization) {\n    ERROR(\"Must be in the Initialization phase.\");\n  }\n  // Since singletons are actually single-element Charm++ arrays, we have to\n  // allocate them here along with the other Charm++ arrays.\n  tmpl::for_each<singleton_component_list>([this](auto singleton_component_v) {\n    using singleton_component =\n        tmpl::type_from<decltype(singleton_component_v)>;\n    auto& local_cache = *Parallel::local_branch(global_cache_proxy_);\n    auto& singleton_proxy =\n        Parallel::get_parallel_component<singleton_component>(local_cache);\n    auto options = Parallel::create_from_options<Metavariables>(\n        options_, typename singleton_component::simple_tags_from_options{});\n\n    const size_t proc = resource_info_.template proc_for<singleton_component>();\n    singleton_proxy[0].insert(global_cache_proxy_, std::move(options), proc);\n    singleton_proxy.doneInserting();\n  });\n\n  // These are Spectre array components built on Charm++ array chares. Each\n  // component is in charge of allocating and distributing its elements over the\n  // computing system.\n  tmpl::for_each<all_array_component_list>([this](auto parallel_component_v) {\n    using parallel_component = tmpl::type_from<decltype(parallel_component_v)>;\n    parallel_component::allocate_array(\n        global_cache_proxy_,\n        Parallel::create_from_options<Metavariables>(\n            options_, typename parallel_component::simple_tags_from_options{}),\n        Parallel::create_from_options<Metavariables>(\n            options_, typename parallel_component::array_allocation_tags{}),\n        resource_info_.procs_to_ignore());\n  });\n\n  // Free any resources from the initial option parsing.\n  options_ = decltype(options_){};\n\n  tmpl::for_each<component_list>([this](auto parallel_component_v) {\n    using parallel_component = tmpl::type_from<decltype(parallel_component_v)>;\n    Parallel::get_parallel_component<parallel_component>(\n        *Parallel::local_branch(global_cache_proxy_))\n        .start_phase(current_phase_);\n  });\n  CkStartQD(CkCallback(CkIndex_Main<Metavariables>::execute_next_phase(),\n                       this->thisProxy));\n}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::execute_next_phase() {\n  if (not exception_messages_.empty()) {\n    // Print exceptions whether we errored during execution or cleanup\n    Parallel::printf(\n        \"\\n\\n###############################\\n\"\n        \"The following exceptions were reported during the phase: %s\\n\",\n        current_phase_);\n    for (const std::string& exception_message : exception_messages_) {\n      Parallel::printf(\"%s\\n\\n\", exception_message);\n    }\n    exception_messages_.clear();\n    Parallel::printf(\n        \"To determine where an exception is thrown, run gdb and do\\n\"\n        \"catch throw EXCEPTION_TYPE\\n\"\n        \"run\\n\"\n        \"where EXCEPTION_TYPE is the Type of the exception above.\\n\"\n        \"You may have to type `continue` to skip some option parser\\n\"\n        \"exceptions until you get to the one you care about\\n\"\n        \"You may also have to type `up` or `down` to go up and down\\n\"\n        \"the function calls in order to find a useful line number.\\n\\n\");\n\n    // Errored during cleanup. Can't have this, so just abort\n    if (current_phase_ == Parallel::Phase::PostFailureCleanup) {\n      Parallel::printf(\n          \"Received termination while cleaning up a previous termination. This \"\n          \"is cyclic behavior and cannot be supported. Cleanup must exit \"\n          \"cleanly without errors.\");\n      sys::abort(\"\");\n    }\n\n    // Errored during execution. Go to cleanup\n    current_phase_ = Parallel::Phase::PostFailureCleanup;\n    Parallel::printf(\"Entering phase: %s at time %s\\n\", current_phase_,\n                     sys::pretty_wall_time());\n  } else {\n    if (Parallel::Phase::Exit == current_phase_) {\n      ERROR(\"Current phase is Exit, but program did not exit!\");\n    }\n\n    if (current_phase_ == Parallel::Phase::PostFailureCleanup) {\n      Parallel::printf(\"PostFailureCleanup phase complete. Aborting.\\n\");\n      Informer::print_exit_info();\n      sys::abort(\"\");\n    }\n\n    const auto next_phase = PhaseControl::arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data_), current_phase_,\n        *Parallel::local_branch(global_cache_proxy_));\n    if (next_phase.has_value()) {\n      // Only print info if there was an actual phase change.\n      if (current_phase_ != next_phase.value()) {\n        Parallel::printf(\"Entering phase from phase control: %s at time %s\\n\",\n                         next_phase.value(), sys::pretty_wall_time());\n        current_phase_ = next_phase.value();\n      }\n    } else {\n      if (current_phase_ ==\n          Parallel::Phase::UpdateOptionsAtRestartFromCheckpoint) {\n        ERROR(\n            \"Failed to find the next phase after overlaying options during a \"\n            \"restart. This is caused by removing the phase change that caused \"\n            \"the restart.\");\n      }\n      const auto& default_order = Metavariables::default_phase_order;\n      auto it = alg::find(default_order, current_phase_);\n      using ::operator<<;\n      if (it == std::end(default_order)) {\n        ERROR(\"Cannot determine next phase as '\"\n              << current_phase_\n              << \"' is not in Metavariables::default_phase_order \"\n              << default_order << \"\\n\");\n      }\n      if (std::next(it) == std::end(default_order)) {\n        ERROR(\"Cannot determine next phase as '\"\n              << current_phase_\n              << \"' is last in Metavariables::default_phase_order \"\n              << default_order << \"\\n\");\n      }\n      current_phase_ = *std::next(it);\n\n      Parallel::printf(\"Entering phase: %s at time %s\\n\", current_phase_,\n                       sys::pretty_wall_time());\n    }\n  }\n\n  if (Parallel::Phase::Exit == current_phase_) {\n    check_if_component_terminated_correctly();\n#ifdef SPECTRE_KOKKOS\n    Kokkos::finalize();\n#endif  // SPECTRE_KOKKOS\n    return;\n  }\n  tmpl::for_each<component_list>([this](auto parallel_component) {\n    tmpl::type_from<decltype(parallel_component)>::execute_next_phase(\n        current_phase_, global_cache_proxy_);\n  });\n\n  // Here we handle phases with direct Charm++ calls. By handling these phases\n  // after calling each component's execute_next_phase entry method, we ensure\n  // that each component knows what phase it is in. This is useful for pup\n  // functions that need special handling that depends on the phase.\n  //\n  // Note that in future versions of Charm++ it may become possible for pup\n  // functions to have knowledge of the migration type. At that point, it\n  // should no longer be necessary to wait until after\n  // component::execute_next_phase to make the direct charm calls. Instead, the\n  // load balance or checkpoint work could be initiated *before* the call to\n  // component::execute_next_phase and *without* the need for a quiescence\n  // detection. This may be a slight optimization.\n  if (current_phase_ == Parallel::Phase::LoadBalancing) {\n    CkStartQD(CkCallback(CkIndex_Main<Metavariables>::start_load_balance(),\n                         this->thisProxy));\n    return;\n  }\n  if (current_phase_ == Parallel::Phase::WriteCheckpoint) {\n    CkStartQD(CkCallback(CkIndex_Main<Metavariables>::start_write_checkpoint(),\n                         this->thisProxy));\n    return;\n  }\n\n  // We skip the reparsing and overlaying if there are no eligible tags\n  if constexpr (tmpl::size<overlayable_option_list>::value > 0) {\n    // Make sure we only update the options the first time we run the phase\n    // UpdateOptionsAtRestartFromCheckpoint after restoring from checkpoint.\n    // Else, the code might try to update options each time the phase was\n    // encountered, even if there was no checkpoint/restart...\n    if (just_restored_from_checkpoint_ and\n        current_phase_ ==\n            Parallel::Phase::UpdateOptionsAtRestartFromCheckpoint) {\n      update_const_global_cache_from_input_file();\n      just_restored_from_checkpoint_ = false;\n    }\n  }\n\n  // The general case simply returns to execute_next_phase\n  CkStartQD(CkCallback(CkIndex_Main<Metavariables>::execute_next_phase(),\n                       this->thisProxy));\n}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::start_load_balance() {\n  at_sync_indicator_proxy_.IndicateAtSync();\n  // No need for a callback to return to execute_next_phase: this is done by\n  // ResumeFromSync instead.\n}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::start_write_checkpoint() {\n  // Reset the counter if the checkpoints directory does not exist.\n  // This happens when the simulation continues in a new segment.\n  const auto [checkpoints_dir, prefix, pad] =\n      detail::checkpoints_dir_prefix_pad();\n  if (not file_system::check_if_dir_exists(checkpoints_dir)) {\n    checkpoint_dir_counter_ = 0;\n  }\n  const std::string dir = detail::next_checkpoint_dir(checkpoint_dir_counter_);\n  checkpoint_dir_counter_++;\n  file_system::create_directory(dir);\n  CkStartCheckpoint(\n      dir.c_str(), CkCallback(CkIndex_Main<Metavariables>::execute_next_phase(),\n                              this->thisProxy));\n}\n\ntemplate <typename Metavariables>\ntemplate <typename InvokeCombine, typename... Tags>\nvoid Main<Metavariables>::phase_change_reduction(\n    ReductionData<ReductionDatum<tuples::TaggedTuple<Tags...>, InvokeCombine,\n                                 funcl::Identity, std::index_sequence<>>>\n        reduction_data) {\n  using tagged_tuple_type = std::decay_t<\n      std::tuple_element_t<0, std::decay_t<decltype(reduction_data.data())>>>;\n  (void)Parallel::charmxx::RegisterPhaseChangeReduction<\n      Metavariables, InvokeCombine, Tags...>::registrar;\n  static_assert(tt::is_a_v<tuples::TaggedTuple, tagged_tuple_type>,\n                \"The main chare expects a tagged tuple in the phase change \"\n                \"reduction target.\");\n  reduction_data.finalize();\n  PhaseControl::TaggedTupleMainCombine::apply(\n      make_not_null(&phase_change_decision_data_),\n      get<0>(reduction_data.data()));\n}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::add_exception_message(std::string exception_message) {\n  exception_messages_.push_back(std::move(exception_message));\n  auto* global_cache = Parallel::local_branch(global_cache_proxy_);\n  ASSERT(global_cache != nullptr, \"Could not retrieve the local global cache.\");\n  // Set terminate_=true on all components to cause them to stop the current\n  // phase.\n  tmpl::for_each<component_list>([global_cache](auto component_tag_v) {\n    using component_tag = tmpl::type_from<decltype(component_tag_v)>;\n    Parallel::get_parallel_component<component_tag>(*global_cache)\n        .set_terminate(true);\n  });\n}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::did_all_elements_terminate(\n    const bool all_elements_terminated) {\n  if (not all_elements_terminated) {\n    tmpl::for_each<component_list>([this](auto component_tag_v) {\n      using component_tag = tmpl::type_from<decltype(component_tag_v)>;\n      if (tmpl::index_of<component_list, component_tag>::value ==\n          current_termination_check_index_ - 1) {\n        components_that_did_not_terminate_.push_back(\n            pretty_type::name<component_tag>());\n      }\n    });\n  }\n  if (current_termination_check_index_ == tmpl::size<component_list>::value) {\n    if (not components_that_did_not_terminate_.empty()) {\n      using ::operator<<;\n      // Need the MakeString to avoid GCC compilation failure that it can't\n      // print out the vector...\n      Parallel::printf(\n          \"\\n############ ERROR ############\\n\"\n          \"The following components did not terminate cleanly:\\n\"\n          \"%s\\n\\n\"\n          \"This means the executable stopped because of a hang/deadlock.\\n\"\n          \"############ ERROR ############\\n\\n\",\n          std::string{MakeString{} << components_that_did_not_terminate_});\n      if constexpr (detail::is_run_deadlock_analysis_simple_actions_callable_v<\n                        Metavariables, Parallel::GlobalCache<Metavariables>&,\n                        const std::vector<std::string>&>) {\n        Parallel::printf(\"Starting deadlock analysis.\\n\");\n        Metavariables::run_deadlock_analysis_simple_actions(\n            *Parallel::local_branch(global_cache_proxy_),\n            components_that_did_not_terminate_);\n        CkStartQD(CkCallback(\n            CkIndex_Main<Metavariables>::post_deadlock_analysis_termination(),\n            this->thisProxy));\n        return;\n      } else {\n        Parallel::printf(\n            \"No deadlock analysis function found in metavariables. To enable \"\n            \"deadlock analysis via simple actions add a function:\\n\"\n            \"  static void run_deadlock_analysis_simple_actions(\\n\"\n            \"        Parallel::GlobalCache<metavariables>& cache,\\n\"\n            \"        const std::vector<std::string>& deadlocked_components);\\n\"\n            \"to your metavariables.\\n\");\n      }\n    }\n    post_deadlock_analysis_termination();\n  }\n\n  check_if_component_terminated_correctly();\n}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::check_if_component_terminated_correctly() {\n  if constexpr (tmpl::size<component_list>::value == 0) {\n    post_deadlock_analysis_termination();\n  }\n  auto* global_cache = Parallel::local_branch(global_cache_proxy_);\n  ASSERT(global_cache != nullptr, \"Could not retrieve the local global cache.\");\n\n  tmpl::for_each<component_list>([global_cache, this](auto component_tag_v) {\n    using component_tag = tmpl::type_from<decltype(component_tag_v)>;\n    if (tmpl::index_of<component_list, component_tag>::value ==\n        current_termination_check_index_) {\n      Parallel::get_parallel_component<component_tag>(*global_cache)\n          .contribute_termination_status_to_main();\n    }\n  });\n  current_termination_check_index_++;\n}\n\nnamespace detail {\nCREATE_IS_CALLABLE(execute_next_phase)\nCREATE_IS_CALLABLE_V(execute_next_phase)\n}  // namespace detail\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::post_deadlock_analysis_termination() {\n  // If no components deadlocked, then we just exit.\n  if (components_that_did_not_terminate_.empty()) {\n    Informer::print_exit_info();\n    const Parallel::ExitCode exit_code =\n        get<Tags::ExitCode>(phase_change_decision_data_);\n    sys::exit(static_cast<int>(exit_code));\n  } else {\n    // Even though we didn't raise an exception, a deadlock is still a\n    // failure and some components may want to run things during cleanup so\n    // we switch phases here and start the PostFailureCleanup phase on each\n    // component.\n    current_phase_ = Parallel::Phase::PostFailureCleanup;\n    Parallel::printf(\"Entering phase: %s at time %s\\n\", current_phase_,\n                     sys::pretty_wall_time());\n    tmpl::for_each<component_list>([this](auto component_tag_v) {\n      using component_tag = tmpl::type_from<decltype(component_tag_v)>;\n      // If a component deadlocked, then the terminate_ flag in the\n      // DistributedObject isn't correct and we can't start a new phase.\n      // Therefore, we have to manually set it so that it thinks it terminated\n      // cleanly.\n      if (alg::found(components_that_did_not_terminate_,\n                     pretty_type::name<component_tag>())) {\n        Parallel::printf(\"Setting terminate to true on %s\\n\",\n                         pretty_type::name<component_tag>());\n      }\n      if constexpr (detail::is_execute_next_phase_callable_v<\n                        component_tag, Parallel::Phase,\n                        CProxy_GlobalCache<Metavariables>, bool>) {\n        component_tag::execute_next_phase(current_phase_, global_cache_proxy_,\n                                          true);\n      } else {\n        component_tag::execute_next_phase(current_phase_, global_cache_proxy_);\n      }\n    });\n    // As a callback, we set execute_next_phase() because it will just\n    // immediately exit after the PostFailureCleanup phase is complete which is\n    // what we want\n    CkStartQD(CkCallback(CkIndex_Main<Metavariables>::execute_next_phase(),\n                         this->thisProxy));\n  }\n}\n\ntemplate <typename Metavariables>\nvoid Main<Metavariables>::update_const_global_cache_from_input_file() {\n  if (checkpoint_dir_counter_ < 1) {\n    ERROR(\"Executable is unaware of previous checkpoints, so can't reparse.\");\n  }\n\n  // Get the padded counter (e.g., 000XYZ) of the checkpoint we restarted from\n  const size_t restart_checkpoint = checkpoint_dir_counter_ - 1;\n  const std::string counter = std::to_string(restart_checkpoint);\n  const auto [checkpoints_dir, prefix, pad] =\n      detail::checkpoints_dir_prefix_pad();\n  const std::string padded_counter =\n      std::string(pad - counter.size(), '0').append(counter);\n  (void)checkpoints_dir;\n  (void)prefix;\n\n  // Given input file \"Input.yaml\", overlay file is \"Input.overlay_WXYZ.yaml\"\n  const std::string dot_yaml = \".yaml\";\n  const std::string overlay_counter = \".overlay_\" + padded_counter;\n  const auto found = input_file_.find(dot_yaml);\n  std::string input_file_for_reparse = input_file_;\n  if (found != std::string::npos) {\n    input_file_for_reparse.insert(found, overlay_counter);\n  } else {\n    ERROR(\"Overlaying assumes the input file has a .yaml extension\");\n  }\n\n  Parallel::printf(\"Attempting to overlay input file: %s\\n\",\n                   input_file_for_reparse);\n  if (file_system::check_if_file_exists(input_file_for_reparse)) {\n    parser_.template overlay_file<overlayable_option_list>(\n        input_file_for_reparse);\n    const auto data_to_overlay =\n        parser_.template apply<overlayable_option_list,\n                               Metavariables>([](auto... args) {\n          return tuples::tagged_tuple_from_typelist<overlayable_option_list>(\n              std::move(args)...);\n        });\n    global_cache_proxy_.overlay_cache_data(data_to_overlay);\n    Parallel::printf(\"... success!\\n\");\n  } else {\n    Parallel::printf(\n        \"... file not found. Continuing with values from previous run.\\n\");\n  }\n}\n\n}  // namespace Parallel\n\n#define CK_TEMPLATES_ONLY\n#include \"Parallel/Main.def.h\"\n#undef CK_TEMPLATES_ONLY\n"
  },
  {
    "path": "src/Parallel/MaxInlineMethodsReached.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\nnamespace Parallel::detail {\n\n// Allow 64 inline entry method calls before we fall back to Charm++. This is\n// done to avoid blowing the stack.\ninline bool max_inline_entry_methods_reached() {\n#ifndef SPECTRE_PROFILING\n  thread_local size_t approx_stack_depth = 0;\n  approx_stack_depth++;\n  if (approx_stack_depth < 64) {\n    return false;\n  }\n  approx_stack_depth = 0;\n#endif\n  return true;\n}\n}  // namespace Parallel::detail\n"
  },
  {
    "path": "src/Parallel/MemoryMonitor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  MemoryMonitor.hpp\n  Tags.hpp\n)\n"
  },
  {
    "path": "src/Parallel/MemoryMonitor/MemoryMonitor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingletonDeclarations.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/MemoryMonitor/Tags.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup ParallelGroup\n * Holds the MemoryMonitor parallel component and all actions and tags related\n * to the memory monitor.\n */\nnamespace mem_monitor {}\n\nnamespace mem_monitor {\nnamespace detail {\nstruct InitializeMutator : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<mem_monitor::Tags::MemoryHolder>;\n  using argument_tags = tmpl::list<>;\n\n  using tag_type = typename mem_monitor::Tags::MemoryHolder::type;\n\n  static void apply(const gsl::not_null<tag_type*> /*holder*/) {}\n};\n}  // namespace detail\n\n/*!\n * \\brief Singleton parallel component used for monitoring memory usage of other\n * parallel components\n */\ntemplate <class Metavariables>\nstruct MemoryMonitor {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<Initialization::Actions::AddSimpleTags<\n                     mem_monitor::detail::InitializeMutator>,\n                 Parallel::Actions::TerminatePhase>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<MemoryMonitor<Metavariables>>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace mem_monitor\n"
  },
  {
    "path": "src/Parallel/MemoryMonitor/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace mem_monitor {\n/*!\n * Gives full subfile path to the dat file for the memory monitor of a\n * parallel component\n */\ntemplate <typename ParallelComponent>\nstd::string subfile_name() {\n  return \"/MemoryMonitors/\" + pretty_type::name<ParallelComponent>();\n}\n\nnamespace Tags {\n/*!\n * \\brief Tag to hold memory usage of parallel components before it is written\n * to disk.\n *\n * \\details The types in the unordered_map are as follows:\n *\n * \\code\n * std::unordered_map<ComponentName, std::unordered_map<%Time,\n *      std::unordered_map<Node/Proc, Memory in MB>>>\n * \\endcode\n */\nstruct MemoryHolder : db::SimpleTag {\n  using type = std::unordered_map<\n      std::string, std::unordered_map<double, std::unordered_map<int, double>>>;\n};\n}  // namespace Tags\n}  // namespace mem_monitor\n"
  },
  {
    "path": "src/Parallel/MultiReaderSpinlock.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <atomic>\n#include <limits>\n#include <new>  // for hardware_destructive_interference_size\n\nnamespace Parallel {\n/*!\n * \\brief A two-state spinlock that allows multiple readers of a shared resource\n * to acquire the lock simultaneously.\n *\n * A spinlock (i.e. a non-yielding lock) that can be used to guard a resource\n * where multiple threads can safely perform some types of operations\n * (e.g. reader entries from a container), while only one thread can safely\n * perform other types of operations (e.g. modifying the container).\n *\n * ### Implementation\n * We lock by using a `std::atomic<std::int64_t>` (a signed integer). The lock\n * is not locked if the integer is `0`. It is read-locked if it is positive\n * and write-locked if it is negative. To read-lock we first check that the\n * integer is non-negative and then `fetch_add(1, std::memory_order_acq_rel)`,\n * then check that the number _before_ the increment is non-negative. The\n * reason for the second check is that between checking that the lock is not\n * write-locked and the `fetch_add()` operation, it could be write-locked by\n * another thread. To write-lock, we check that the integer is `0` and if so\n * we set it to `std::numeric_limits<std::int64_t>::lowest()` (i.e. `-2^{63}`).\n * We achieve this by using a `compare_exchange_strong()` with failure memory\n * order `relaxed` and success memory order `acq_rel`. This guarantees that\n * different threads synchronize using the lock.\n *\n * A thread read-unlocks the lock using a\n * `fetch_sub(1, std::memory_order_acq_rel)` operation.\n *\n * A thread write-unlocks the lock using a `store(0, std::memory_order_release)`\n * operation.\n */\nclass MultiReaderSpinlock {\n public:\n  MultiReaderSpinlock() = default;\n  MultiReaderSpinlock(const MultiReaderSpinlock&) = delete;\n  MultiReaderSpinlock& operator=(const MultiReaderSpinlock&) = delete;\n  MultiReaderSpinlock(MultiReaderSpinlock&&) = delete;\n  MultiReaderSpinlock& operator=(MultiReaderSpinlock&&) = delete;\n  ~MultiReaderSpinlock() = default;\n\n  /// \\brief Acquire the lock in a reader state.\n  ///\n  /// \\note Multiple threads can acquire a reader state simultaneously.\n  void read_lock() noexcept {\n    for (;;) {\n      while (lock_.load(std::memory_order_relaxed) < 0) {\n      }\n\n      if (lock_.fetch_add(1, std::memory_order_acquire) > -1) {\n        return;\n      }\n    }\n  }\n\n  /// \\brief Release the lock from a reader state.\n  ///\n  /// \\note Since multiple threads can simultaneously acquire a reader state,\n  /// unlock from a single reader does not guarantee a writer can acquire the\n  /// lock in a write state.\n  void read_unlock() noexcept { lock_.fetch_sub(1, std::memory_order_release); }\n\n  /// \\brief Acquire the lock in a writer state.\n  ///\n  /// Once acquired, no other thread can acquire the lock in either a read or\n  /// a write state until this thread unlocks it.\n  void write_lock() noexcept {\n    for (;;) {\n      std::int64_t current_value{0};\n      if (lock_.compare_exchange_strong(\n              current_value, std::numeric_limits<std::int64_t>::lowest(),\n              std::memory_order_acq_rel, std::memory_order_relaxed)) {\n        return;\n      }\n\n      while (lock_.load(std::memory_order_relaxed) != 0) {\n      }\n    }\n  }\n\n  /// \\brief Release the lock from a reader state.\n  void write_unlock() noexcept { lock_.store(0, std::memory_order_release); }\n\n private:\n#ifdef __cpp_lib_hardware_interference_size\n  static constexpr size_t cache_line_size_ =\n      std::hardware_destructive_interference_size;\n#else\n  static constexpr size_t cache_line_size_ = 64;\n#endif\n\n  alignas(cache_line_size_) std::atomic<std::int64_t> lock_{0};\n  // Ensure we pad the end to avoid false sharing. Unlikely to happen because\n  // of the layout, but better to be safe.\n#if defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-private-field\"\n#endif\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  char padding_[cache_line_size_ - sizeof(std::atomic<std::int64_t>)] = {};\n#if defined(__clang__)\n#pragma GCC diagnostic pop\n#endif\n};\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/NodeLock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/NodeLock.hpp\"\n\n#include <pup.h>\n\n#include \"Parallel/Spinlock.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Parallel {\n\nNodeLock::NodeLock()\n    : lock_(std::make_unique<typename decltype(lock_)::element_type>()) {}\n\nNodeLock::NodeLock(CkMigrateMessage* /*message*/) {}\n\nNodeLock::NodeLock(NodeLock&& moved_lock) noexcept\n    : lock_(std::move(moved_lock.lock_)) {\n  moved_lock.lock_ = nullptr;\n}\n\nNodeLock& NodeLock::operator=(NodeLock&& moved_lock) noexcept {\n  lock_ = std::move(moved_lock.lock_);\n  moved_lock.lock_ = nullptr;\n  return *this;\n}\n\nNodeLock::~NodeLock() { destroy(); }\n\nvoid NodeLock::lock() {\n  if (UNLIKELY(nullptr == lock_)) {\n    ERROR(\"Trying to lock a destroyed lock\");\n  }\n  lock_->lock();\n}\n\nbool NodeLock::try_lock() {\n  if (UNLIKELY(nullptr == lock_)) {\n    ERROR(\"Trying to try_lock a destroyed lock\");\n  }\n  return lock_->try_lock();\n}\n\nvoid NodeLock::unlock() {\n  if (UNLIKELY(nullptr == lock_)) {\n    ERROR(\"Trying to unlock a destroyed lock\");\n  }\n  lock_->unlock();\n}\n\nvoid NodeLock::destroy() {\n  if (nullptr == lock_) {\n    return;\n  }\n  lock_ = nullptr;\n}\n\nvoid NodeLock::pup(PUP::er& p) {  // NOLINT\n  bool is_null = (nullptr == lock_);\n  p | is_null;\n  if (is_null) {\n    lock_ = nullptr;\n  } else {\n    if (p.isUnpacking()) {\n      lock_ = std::make_unique<typename decltype(lock_)::element_type>();\n    }\n  }\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/NodeLock.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <memory>\n\n#include \"Parallel/Spinlock.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Parallel {\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief A typesafe wrapper for a lock for synchronization of shared resources\n * on a given node, with safe creation, destruction, and serialization.\n *\n * \\note If a locked NodeLock is serialized, it is deserialized as unlocked.\n */\nclass NodeLock {\n public:\n  NodeLock();\n\n  explicit NodeLock(CkMigrateMessage* /*message*/);\n\n  NodeLock(const NodeLock&) = delete;\n  NodeLock& operator=(const NodeLock&) = delete;\n  NodeLock(NodeLock&& moved_lock) noexcept;\n  NodeLock& operator=(NodeLock&& moved_lock) noexcept;\n  ~NodeLock();\n\n  void lock();\n\n  bool try_lock();\n\n  void unlock();\n\n  void destroy();\n\n  bool is_destroyed() { return nullptr == lock_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  std::unique_ptr<Spinlock> lock_;\n};\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/OutputInbox.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace Parallel {\nnamespace detail {\nCREATE_IS_CALLABLE(output_inbox)\nCREATE_IS_CALLABLE_V(output_inbox)\n}  // namespace detail\n\n/*!\n * \\brief Returns a string of the contents of the inbox.\n *\n * Calls a static `output_inbox` member function of the `InboxTag`. If the inbox\n * tag doesn't have the `output_inbox` function, then this function can't be\n * called with that inbox (a `static_assert` will fail).\n *\n * \\note It's up to the individual inbox what to include in the string. Data may\n * or may not be included\n *\n * \\tparam InboxTag Inbox tag with an `output_inbox` member function\n * \\tparam InboxTypes Types of all inboxes (deduced)\n * \\param inboxes All inboxes\n * \\param indent_pad Number of empty spaces to pad the message with incase\n * indenting is needed.\n * \\return std::string The contents of the inbox\n */\ntemplate <typename InboxTag, typename... InboxTypes>\nstd::string output_inbox(const tuples::TaggedTuple<InboxTypes...>& inboxes,\n                         const size_t indent_pad) {\n  static_assert(tmpl::list_contains_v<tmpl::list<InboxTypes...>, InboxTag>);\n\n  return InboxTag::output_inbox(tuples::get<InboxTag>(inboxes), indent_pad);\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ParallelComponentHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <mutex>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace Parallel {\nnamespace detail {\ntemplate <class Action, class = std::void_t<>>\nstruct get_inbox_tags_from_action {\n  using type = tmpl::list<>;\n};\n\ntemplate <class Action>\nstruct get_inbox_tags_from_action<Action,\n                                  std::void_t<typename Action::inbox_tags>> {\n  using type = typename Action::inbox_tags;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Given a list of Actions, get a list of the unique inbox tags\n */\ntemplate <class ActionsList>\nusing get_inbox_tags = tmpl::remove_duplicates<tmpl::join<tmpl::transform<\n    ActionsList, detail::get_inbox_tags_from_action<tmpl::_1>>>>;\n\nnamespace detail {\n// ParallelStruct is a metavariables, component, or action struct\ntemplate <class ParallelStruct, class = std::void_t<>>\nstruct get_const_global_cache_tags_from_parallel_struct {\n  using type = tmpl::list<>;\n};\n\ntemplate <class ParallelStruct>\nstruct get_const_global_cache_tags_from_parallel_struct<\n    ParallelStruct,\n    std::void_t<typename ParallelStruct::const_global_cache_tags>> {\n  using type = typename ParallelStruct::const_global_cache_tags;\n};\n\ntemplate <class PhaseDependentActionList>\nstruct get_const_global_cache_tags_from_pdal {\n  using type = tmpl::join<tmpl::transform<\n      tmpl::flatten<tmpl::transform<\n          PhaseDependentActionList,\n          get_action_list_from_phase_dep_action_list<tmpl::_1>>>,\n      get_const_global_cache_tags_from_parallel_struct<tmpl::_1>>>;\n};\n\ntemplate <class Component>\nstruct get_const_global_cache_tags_from_component {\n  using type = typename get_const_global_cache_tags_from_pdal<\n      typename Component::phase_dependent_action_list>::type;\n};\n\ntemplate <class ParallelStruct, class = std::void_t<>>\nstruct get_mutable_global_cache_tags_from_parallel_struct {\n  using type = tmpl::list<>;\n};\n\ntemplate <class ParallelStruct>\nstruct get_mutable_global_cache_tags_from_parallel_struct<\n    ParallelStruct,\n    std::void_t<typename ParallelStruct::mutable_global_cache_tags>> {\n  using type = typename ParallelStruct::mutable_global_cache_tags;\n};\n\ntemplate <class PhaseDependentActionList>\nstruct get_mutable_global_cache_tags_from_pdal {\n  using type = tmpl::join<tmpl::transform<\n      tmpl::flatten<tmpl::transform<\n          PhaseDependentActionList,\n          get_action_list_from_phase_dep_action_list<tmpl::_1>>>,\n      get_mutable_global_cache_tags_from_parallel_struct<tmpl::_1>>>;\n};\n\ntemplate <class Component>\nstruct get_mutable_global_cache_tags_from_component {\n  using type = typename get_mutable_global_cache_tags_from_pdal<\n      typename Component::phase_dependent_action_list>::type;\n};\n\n}  // namespace detail\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Given a list of Actions, get a list of the unique tags specified in\n * the actions' `const_global_cache_tags` aliases.\n */\ntemplate <class ActionsList>\nusing get_const_global_cache_tags_from_actions =\n    tmpl::remove_duplicates<tmpl::join<tmpl::transform<\n        ActionsList,\n        detail::get_const_global_cache_tags_from_parallel_struct<tmpl::_1>>>>;\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Given a list of Actions, get a list of the unique tags specified in\n * the actions' `mutable_global_cache_tags` aliases.\n */\ntemplate <class ActionsList>\nusing get_mutable_global_cache_tags_from_actions =\n    tmpl::remove_duplicates<tmpl::join<tmpl::transform<\n        ActionsList,\n        detail::get_mutable_global_cache_tags_from_parallel_struct<tmpl::_1>>>>;\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Given the metavariables, get a list of the unique tags that will\n * specify the items in the GlobalCache.\n */\ntemplate <typename Metavariables>\nusing get_const_global_cache_tags =\n    tmpl::remove_duplicates<tmpl::flatten<tmpl::list<\n        typename detail::get_const_global_cache_tags_from_parallel_struct<\n            Metavariables>::type,\n        tmpl::transform<\n            typename Metavariables::component_list,\n            detail::get_const_global_cache_tags_from_parallel_struct<tmpl::_1>>,\n        tmpl::transform<\n            typename Metavariables::component_list,\n            detail::get_const_global_cache_tags_from_component<tmpl::_1>>>>>;\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Given the metavariables, get a list of the unique tags that will\n * specify the mutable items in the GlobalCache.\n */\ntemplate <typename Metavariables>\nusing get_mutable_global_cache_tags =\n    tmpl::remove_duplicates<tmpl::flatten<tmpl::list<\n        typename detail::get_mutable_global_cache_tags_from_parallel_struct<\n            Metavariables>::type,\n        tmpl::transform<\n            typename Metavariables::component_list,\n            detail::get_mutable_global_cache_tags_from_parallel_struct<\n                tmpl::_1>>,\n        tmpl::transform<\n            typename Metavariables::component_list,\n            detail::get_mutable_global_cache_tags_from_component<tmpl::_1>>>>>;\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Check whether a tag is retrievable from the const portion of\n * the global cache.\n */\ntemplate <typename Metavariables, typename Tag>\nconstexpr bool is_in_const_global_cache =\n    tmpl::size<tmpl::filter<get_const_global_cache_tags<Metavariables>,\n                            std::is_base_of<tmpl::pin<Tag>, tmpl::_1>>>::value >\n    0;\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Check whether a tag is retrievable from the mutable portion of\n * the global cache.\n */\ntemplate <typename Metavariables, typename Tag>\nconstexpr bool is_in_mutable_global_cache =\n    tmpl::size<tmpl::filter<get_mutable_global_cache_tags<Metavariables>,\n                            std::is_base_of<tmpl::pin<Tag>, tmpl::_1>>>::value >\n    0;\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Check whether a tag is retrievable from the global cache.\n */\ntemplate <typename Metavariables, typename Tag>\nconstexpr bool is_in_global_cache =\n    is_in_const_global_cache<Metavariables, Tag> or\n    is_in_mutable_global_cache<Metavariables, Tag>;\n\ntemplate <typename Tag>\nstruct MutexTag {\n  using type = std::pair<std::mutex, std::mutex>;\n};\n\ntemplate <typename Tag>\nstruct MutableCacheTag {\n  using tag = Tag;\n  using type =\n      std::tuple<typename Tag::type,\n                 std::unordered_map<Parallel::ArrayComponentId,\n                                    std::vector<std::unique_ptr<Callback>>>>;\n};\n\ntemplate <typename Tag>\nstruct get_mutable_cache_tag {\n  using type = MutableCacheTag<Tag>;\n};\n\ntemplate <typename Metavariables>\nusing get_mutable_global_cache_tag_storage =\n    tmpl::transform<get_mutable_global_cache_tags<Metavariables>,\n                    get_mutable_cache_tag<tmpl::_1>>;\n\nnamespace GlobalCache_detail {\n\ntemplate <typename T>\nstruct type_for_get_helper {\n  using type = T;\n};\n\ntemplate <typename T, typename D>\nstruct type_for_get_helper<std::unique_ptr<T, D>> {\n  using type = T;\n};\n\n// This struct provides a better error message if\n// an unknown tag is requested from the GlobalCache.\ntemplate <typename GlobalCacheTag, typename ListOfPossibleTags>\nstruct matching_tag_helper {\n  using found_tags =\n      tmpl::filter<ListOfPossibleTags,\n                   std::is_base_of<tmpl::pin<GlobalCacheTag>, tmpl::_1>>;\n  static_assert(not std::is_same_v<found_tags, tmpl::list<>>,\n                \"Trying to get a nonexistent tag from the GlobalCache. \"\n                \"To diagnose the problem, search for \"\n                \"'matching_tag_helper' in the error message. \"\n                \"The first template parameter of \"\n                \"'matching_tag_helper' is the requested tag, and \"\n                \"the second template parameter is a tmpl::list of all the \"\n                \"tags in the GlobalCache.  One possible bug that may \"\n                \"lead to this error message is a missing or misspelled \"\n                \"const_global_cache_tags or mutable_global_cache_tags \"\n                \"type alias.\");\n  static_assert(tmpl::size<found_tags>::value == 1,\n                \"Found more than one matching tag. \"\n                \"To diagnose the problem, search for \"\n                \"'matching_tag_helper' in the error message. \"\n                \"The first template parameter of \"\n                \"'matching_tag_helper' is the requested tag, and \"\n                \"the second template parameter is a tmpl::list of all the \"\n                \"tags in the GlobalCache.\");\n  using type = tmpl::front<found_tags>;\n};\n\ntemplate <class GlobalCacheTag, class Metavariables>\nusing get_matching_mutable_tag = typename matching_tag_helper<\n    GlobalCacheTag, get_mutable_global_cache_tags<Metavariables>>::type;\n}  // namespace GlobalCache_detail\n\nnamespace detail {\ntemplate <typename PhaseAction>\nstruct get_initialization_actions_list {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename InitializationActionsList>\nstruct get_initialization_actions_list<Parallel::PhaseActions<\n    Parallel::Phase::Initialization, InitializationActionsList>> {\n  using type = InitializationActionsList;\n};\n}  // namespace detail\n\n/// \\ingroup ParallelGroup\n/// \\brief Given the phase dependent action list, return the list of\n/// actions in the Initialization phase (or an empty list if the Initialization\n/// phase is absent from the phase dependent action list)\ntemplate <typename PhaseDepActionList>\nusing get_initialization_actions_list = tmpl::flatten<tmpl::transform<\n    PhaseDepActionList, detail::get_initialization_actions_list<tmpl::_1>>>;\n\nnamespace detail {\ntemplate <typename Action, typename = std::void_t<>>\nstruct get_simple_tags_from_options_from_action {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Action>\nstruct get_simple_tags_from_options_from_action<\n    Action, std::void_t<typename Action::simple_tags_from_options>> {\n  using type = typename Action::simple_tags_from_options;\n};\n}  // namespace detail\n\n/// \\ingroup ParallelGroup\n/// \\brief Given a list of initialization actions, returns a list of the\n/// unique simple_tags_from_options for all the actions.\ntemplate <typename InitializationActionsList>\nusing get_simple_tags_from_options =\n    tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n        InitializationActionsList,\n        detail::get_simple_tags_from_options_from_action<tmpl::_1>>>>;\n\nnamespace detail {\ntemplate <typename SimpleTag, typename Metavariables,\n          bool PassMetavariables = SimpleTag::pass_metavariables>\nstruct get_option_tags_from_simple_tag_impl {\n  using type = typename SimpleTag::option_tags;\n};\ntemplate <typename SimpleTag, typename Metavariables>\nstruct get_option_tags_from_simple_tag_impl<SimpleTag, Metavariables, true> {\n  using type = typename SimpleTag::template option_tags<Metavariables>;\n};\ntemplate <typename Metavariables>\nstruct get_option_tags_from_simple_tag {\n  template <typename SimpleTag>\n  using f = tmpl::type_from<\n      get_option_tags_from_simple_tag_impl<SimpleTag, Metavariables>>;\n};\n}  // namespace detail\n\n/// \\ingroup ParallelGroup\n/// \\brief Given a list of simple tags, returns a list of the\n/// unique option tags required to construct them.\ntemplate <typename SimpleTagsList, typename Metavariables>\nusing get_option_tags = tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n    SimpleTagsList, tmpl::bind<detail::get_option_tags_from_simple_tag<\n                                   Metavariables>::template f,\n                               tmpl::_1>>>>;\n\nnamespace detail {\ntemplate <typename Tag, typename = std::void_t<>>\nstruct is_tag_overlayable : std::false_type {};\n\ntemplate <typename Tag>\nstruct is_tag_overlayable<Tag, std::void_t<decltype(Tag::is_overlayable)>>\n    : std::bool_constant<Tag::is_overlayable> {};\n\ntemplate <typename Tag>\nstruct GetUniqueOptionTag {\n  using type = tmpl::front<typename Tag::option_tags>;\n};\n}  // namespace detail\n\n/// Get the subset of `const_global_cache_tags` that are overlayable.\n///\n/// Note this is a list of tags in the GlobalCache, but is not a list of option\n/// tags. However, each tag in the list will have a corresponding option tag.\n///\n/// By default, tags are not overlayable, i.e., can not be reparsed to update\n/// a simulation value after a restart from checkpoint. Any tag can \"opt in\"\n/// and become overlayable by specifying a member variable\n/// `static constexpr bool is_overlayable = true`.\n/// This should be done with caution: changing the value of this tag\n/// must not have any side effects that might invalidate past of current\n/// simulation data. For example, the parameters determining the time stepper\n/// should not be overlayable, because it interacts with the time stepper\n/// history data. But parameters determining the frequency\n/// for (non-dense) reductions to file can be updated on restarts.\ntemplate <typename Metavariables>\nusing get_overlayable_tag_list =\n    tmpl::filter<get_const_global_cache_tags<Metavariables>,\n                 detail::is_tag_overlayable<tmpl::_1>>;\n\n/// Get the option tags corresponding to the output of\n/// `get_overlayable_tag_list`.\ntemplate <typename Metavariables>\nusing get_overlayable_option_list =\n    tmpl::transform<get_overlayable_tag_list<Metavariables>,\n                    detail::GetUniqueOptionTag<tmpl::_1>>;\n\n/// \\cond\nnamespace Algorithms {\nstruct Singleton;\nstruct Array;\nstruct Group;\nstruct Nodegroup;\n}  // namespace Algorithms\n\ntemplate <class ChareType>\nstruct get_array_index;\n\ntemplate <>\nstruct get_array_index<Parallel::Algorithms::Singleton> {\n  template <class ParallelComponent>\n  using f = int;\n};\n\ntemplate <>\nstruct get_array_index<Parallel::Algorithms::Array> {\n  template <class ParallelComponent>\n  using f = typename ParallelComponent::array_index;\n};\n\ntemplate <>\nstruct get_array_index<Parallel::Algorithms::Group> {\n  template <class ParallelComponent>\n  using f = int;\n};\n\ntemplate <>\nstruct get_array_index<Parallel::Algorithms::Nodegroup> {\n  template <class ParallelComponent>\n  using f = int;\n};\n\ntemplate <typename ParallelComponent>\nusing proxy_from_parallel_component =\n    typename ParallelComponent::chare_type::template cproxy<\n        ParallelComponent,\n        typename get_array_index<typename ParallelComponent::chare_type>::\n            template f<ParallelComponent>>;\n\ntemplate <typename ParallelComponent>\nusing index_from_parallel_component =\n    typename ParallelComponent::chare_type::template ckindex<\n        ParallelComponent,\n        typename get_array_index<typename ParallelComponent::chare_type>::\n            template f<ParallelComponent>>;\n\ntemplate <typename ParallelComponent>\nusing proxy_element_from_component =\n    typename proxy_from_parallel_component<ParallelComponent>::element_t;\n\ntemplate <class ParallelComponent, class... Args>\nstruct charm_types_with_parameters {\n  using cproxy =\n      typename ParallelComponent::chare_type::template cproxy<ParallelComponent,\n                                                              Args...>;\n  using cbase =\n      typename ParallelComponent::chare_type::template cbase<ParallelComponent,\n                                                             Args...>;\n  using algorithm =\n      typename ParallelComponent::chare_type::template algorithm_type<\n          ParallelComponent, Args...>;\n  using ckindex = typename ParallelComponent::chare_type::template ckindex<\n      ParallelComponent, Args...>;\n  using cproxy_section =\n      typename ParallelComponent::chare_type::template cproxy_section<\n          ParallelComponent, Args...>;\n};\n/// \\endcond\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Phase.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/Phase.hpp\"\n\n#include <ostream>\n#include <string>\n#include <utility>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace Parallel {\nstd::vector<Phase> known_phases() {\n  return {Phase::AdjustDomain,\n          Phase::BuildMatrix,\n          Phase::CheckDomain,\n          Phase::Cleanup,\n          Phase::DisableRotationControl,\n          Phase::EvaluateAmrCriteria,\n          Phase::Evolve,\n          Phase::Execute,\n          Phase::Exit,\n          Phase::ImportInitialData,\n          Phase::Initialization,\n          Phase::InitializeInitialDataDependentQuantities,\n          Phase::InitializeTimeStepperHistory,\n          Phase::LoadBalancing,\n          Phase::PostFailureCleanup,\n          Phase::Register,\n          Phase::RegisterWithElementDataReader,\n          Phase::Restart,\n          Phase::Solve,\n          Phase::Testing,\n          Phase::UpdateOptionsAtRestartFromCheckpoint,\n          Phase::UpdateSections,\n          Phase::WriteCheckpoint};\n}\n\nstd::ostream& operator<<(std::ostream& os, const Phase& phase) {\n  switch (phase) {\n    case Parallel::Phase::AdjustDomain:\n      return os << \"AdjustDomain\";\n    case Parallel::Phase::BuildMatrix:\n      return os << \"BuildMatrix\";\n    case Parallel::Phase::CheckDomain:\n      return os << \"CheckDomain\";\n    case Parallel::Phase::Cleanup:\n      return os << \"Cleanup\";\n    case Parallel::Phase::DisableRotationControl:\n      return os << \"DisableRotationControl\";\n    case Parallel::Phase::EvaluateAmrCriteria:\n      return os << \"EvaluateAmrCriteria\";\n    case Parallel::Phase::Evolve:\n      return os << \"Evolve\";\n    case Parallel::Phase::Execute:\n      return os << \"Execute\";\n    case Parallel::Phase::Exit:\n      return os << \"Exit\";\n    case Parallel::Phase::ImportInitialData:\n      return os << \"ImportInitialData\";\n    case Parallel::Phase::Initialization:\n      return os << \"Initialization\";\n    case Parallel::Phase::InitializeInitialDataDependentQuantities:\n      return os << \"InitializeInitialDataDependentQuantities\";\n    case Parallel::Phase::InitializeTimeStepperHistory:\n      return os << \"InitializeTimeStepperHistory\";\n    case Parallel::Phase::LoadBalancing:\n      return os << \"LoadBalancing\";\n    case Parallel::Phase::PostFailureCleanup:\n      return os << \"PostFailureCleanup\";\n    case Parallel::Phase::Register:\n      return os << \"Register\";\n    case Parallel::Phase::RegisterWithElementDataReader:\n      return os << \"RegisterWithElementDataReader\";\n    case Parallel::Phase::Restart:\n      return os << \"Restart\";\n    case Parallel::Phase::Solve:\n      return os << \"Solve\";\n    case Parallel::Phase::Testing:\n      return os << \"Testing\";\n    case Parallel::Phase::UpdateOptionsAtRestartFromCheckpoint:\n      return os << \"UpdateOptionsAtRestartFromCheckpoint\";\n    case Parallel::Phase::UpdateSections:\n      return os << \"UpdateSections\";\n    case Parallel::Phase::WriteCheckpoint:\n      return os << \"WriteCheckpoint\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Stream operator does not have case for Phase with integral value \"\n            << static_cast<std::underlying_type_t<Parallel::Phase>>(phase)\n            << \"\\n\");\n      // LCOV_EXCL_STOP\n  }\n}\n}  // namespace Parallel\n\ntemplate <>\nParallel::Phase Options::create_from_yaml<Parallel::Phase>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  for (const auto phase : Parallel::known_phases()) {\n    if (type_read == get_output(phase)) {\n      return phase;\n    }\n  }\n  using ::operator<<;\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read << \"\\\" to Parallel::Phase.\\nMust be one of \"\n                  << Parallel::known_phases() << \".\");\n}\n"
  },
  {
    "path": "src/Parallel/Phase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n#include <vector>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace Parallel {\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief The possible phases of an executable\n *\n * \\details Spectre executables are split into distinct phases separated by\n * global synchronizations.  Each executable will start with phase\n * `Initialization` and end with phase `Exit`.  The metavariables of each\n * executable must define `default_phase_order`, an array of Parallel::Phase\n * that must contain at least `Initialization` as the first element and `Exit`\n * as the last element.  Usually the next phase is determined from the\n * `default_phase_order` provided by the metavariables.  If more complex\n * decision making is desired, use the PhaseControl infrastructure.\n *\n * \\warning The phases are in alphabetical order.  Do not use the values of the\n * underlying integral type as they will change as new phases are added to the\n * list!\n *\n * \\see PhaseChange and \\ref dev_guide_parallelization_foundations\n * \"Parallelization infrastructure\" for details.\n *\n */\nenum class Phase {\n  // If you add a new phase: put it in the list alphabetically, add it to the\n  // vector created by known_phases() below, and add it to the stream operator\n  // If the new phase is the first or last in the list, update Test_Phase.\n\n  ///  phase in which AMR adjusts the domain\n  AdjustDomain,\n  ///  phase in which elliptic problems construct a matrix representation\n  BuildMatrix,\n  ///  phase in which sanity checks are done after AMR\n  CheckDomain,\n  ///  a cleanup phase\n  Cleanup,\n  ///  a phase in which the rotation control system is disable and the\n  ///  rotation function of time is switched to settling to a constant.\n  DisableRotationControl,\n  ///  phase in which AMR criteria are evaluated\n  EvaluateAmrCriteria,\n  ///  phase in which time steps are taken for an evolution executable\n  Evolve,\n  ///  generic execution phase of an executable\n  Execute,\n  ///  final phase of an executable\n  Exit,\n  ///  phase in which initial data is imported from volume files\n  ImportInitialData,\n  ///  initial phase of an executable\n  Initialization,\n  ///  phase in which quantities dependent on imported initial data are\n  ///  initialized\n  InitializeInitialDataDependentQuantities,\n  ///  phase in which the time stepper executes a self-start procedure\n  InitializeTimeStepperHistory,\n  ///  phase in which components are migrated\n  LoadBalancing,\n  ///  phase in which components know an error occurred and they need to do some\n  ///  sort of cleanup, such as dumping data to disk.\n  PostFailureCleanup,\n  ///  phase in which components register with other components\n  Register,\n  ///  phase in which components register with the data importer components\n  RegisterWithElementDataReader,\n  ///  phase run after a checkpoint-restart\n  Restart,\n  ///  phase in which something is solved\n  Solve,\n  ///  phase in which something is tested\n  Testing,\n  ///  phase in which options are changed after restart\n  UpdateOptionsAtRestartFromCheckpoint,\n  ///  phase in which array sections are updated\n  UpdateSections,\n  ///  phase in which checkpoint files are written to disk\n  WriteCheckpoint\n};\n\nstd::vector<Phase> known_phases();\n\n/// Output operator for a Phase.\nstd::ostream& operator<<(std::ostream& os, const Phase& phase);\n}  // namespace Parallel\n\ntemplate <>\nstruct Options::create_from_yaml<Parallel::Phase> {\n  template <typename Metavariables>\n  static Parallel::Phase create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nParallel::Phase Options::create_from_yaml<Parallel::Phase>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Parallel/PhaseControl/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY PhaseControl)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CheckpointAndExitAfterWallclock.cpp\n  PhaseControlTags.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CheckpointAndExitAfterWallclock.hpp\n  ContributeToPhaseChangeReduction.hpp\n  ExecutePhaseChange.hpp\n  Factory.hpp\n  InitializePhaseChangeDecisionData.hpp\n  PhaseChange.hpp\n  PhaseControlTags.hpp\n  VisitAndReturn.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  EventsAndTriggers\n  PUBLIC\n  Charmxx::charmxx\n  Options\n  Parallel\n  Serialization\n  Utilities\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_Main\n  )\n"
  },
  {
    "path": "src/Parallel/PhaseControl/CheckpointAndExitAfterWallclock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n\n#include <optional>\n#include <pup.h>\n\n#include \"Options/Options.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace PhaseControl {\n\nnamespace Tags {\nstd::optional<Parallel::Phase> RestartPhase::combine_method::operator()(\n    const std::optional<Parallel::Phase> /*first_phase*/,\n    const std::optional<Parallel::Phase>& /*second_phase*/) {\n  ERROR(\n      \"The restart phase should only be altered by the phase change \"\n      \"arbitration in the Main chare, so no reduction data should be \"\n      \"provided.\");\n}\n}  // namespace Tags\n\nCheckpointAndExitAfterWallclock::CheckpointAndExitAfterWallclock(\n    const std::optional<double> wallclock_hours,\n    const Options::Context& context)\n    : wallclock_hours_for_checkpoint_and_exit_(wallclock_hours) {\n  if (wallclock_hours.has_value() and wallclock_hours.value() < 0.0) {\n    PARSE_ERROR(context, \"Must give a positive time in hours, but got \"\n                             << wallclock_hours.value());\n  }\n}\n\nCheckpointAndExitAfterWallclock::CheckpointAndExitAfterWallclock(\n    CkMigrateMessage* msg)\n    : PhaseChange(msg) {}\n\nvoid CheckpointAndExitAfterWallclock::pup(PUP::er& p) {\n  PhaseChange::pup(p);\n  p | wallclock_hours_for_checkpoint_and_exit_;\n}\n}  // namespace PhaseControl\n\nPUP::able::PUP_ID PhaseControl::CheckpointAndExitAfterWallclock::my_PUP_ID = 0;\n"
  },
  {
    "path": "src/Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Parallel/AlgorithmMetafunctions.hpp\"\n#include \"Parallel/ExitCode.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/UtcTime.hpp\"\n\nnamespace PhaseControl {\n\nnamespace Tags {\n/// Storage in the phase change decision tuple so that the Main chare can record\n/// the phase to go to when restarting the run from a checkpoint file.\n///\n/// \\note This tag is not intended to participate in any of the reduction\n/// procedures, so will error if the combine method is called.\nstruct RestartPhase {\n  using type = std::optional<Parallel::Phase>;\n\n  struct combine_method {\n    [[noreturn]] std::optional<Parallel::Phase> operator()(\n        const std::optional<Parallel::Phase> /*first_phase*/,\n        const std::optional<Parallel::Phase>& /*second_phase*/);\n  };\n\n  using main_combine_method = combine_method;\n};\n\n/// Stores whether the checkpoint and exit has been requested.\n///\n/// Combinations are performed via `funcl::Or`, as the phase in question should\n/// be chosen if any component requests the jump.\nstruct CheckpointAndExitRequested {\n  using type = bool;\n\n  using combine_method = funcl::Or<>;\n  using main_combine_method = funcl::Or<>;\n};\n\n}  // namespace Tags\n\n/*!\n * \\brief Phase control object that runs the WriteCheckpoint and Exit phases\n * after a specified amount of wallclock time has elapsed.\n *\n * When the executable exits from here, it does so with\n * `Parallel::ExitCode::ContinueFromCheckpoint`.\n *\n * This phase control is useful for running SpECTRE executables performing\n * lengthy computations that may exceed a supercomputer's wallclock limits.\n * Writing a single checkpoint at the end of the job's allocated time allows\n * the computation to be continued, while minimizing the disc space taken up by\n * checkpoint files.\n *\n * When restarting from the checkpoint, this phase control sends the control\n * flow to a UpdateOptionsAtRestartFromCheckpoint phase, allowing the user to\n * update (some) simulation parameters for the continuation of the run.\n *\n * Note that this phase control is not a trigger on wallclock time. Rather,\n * it checks the elapsed wallclock time when called, likely from a global sync\n * point triggered by some other mechanism, e.g., at some slab boundary.\n * Therefore, the WriteCheckpoint and Exit phases will run the first time\n * this phase control is called after the specified wallclock time has been\n * reached.\n *\n * \\warning the global sync points _must_ be triggered often enough to ensure\n * there will be at least one sync point (i.e., one call to this phase control)\n * in the window between the requested checkpoint-and-exit time and the time at\n * which the batch system will kill the executable. To make this more concrete,\n * consider this example: when running on a 12-hour queue with a\n * checkpoint-and-exit requested after 11.5 hours, there is a 0.5-hour window\n * for a global sync to occur, the checkpoint files to be written to disc, and\n * the executable to clean up. In this case, triggering a global sync every\n * 2-10 minutes might be desirable. Matching the global sync frequency with the\n * time window for checkpoint and exit is the responsibility of the user!\n *\n * \\parblock\n * \\warning If modifying the phase-change logic on a\n * checkpoint-restart, this PhaseChange must remain in the list after\n * modification so that the end of the restart logic will run.  The\n * WallclockHours can be changed to None to disable further restarts.\n * \\endparblock\n */\nstruct CheckpointAndExitAfterWallclock : public PhaseChange {\n  CheckpointAndExitAfterWallclock(const std::optional<double> wallclock_hours,\n                                  const Options::Context& context = {});\n\n  explicit CheckpointAndExitAfterWallclock(CkMigrateMessage* msg);\n\n  /// \\cond\n  CheckpointAndExitAfterWallclock() = default;\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CheckpointAndExitAfterWallclock);  // NOLINT\n  /// \\endcond\n\n  struct WallclockHours {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = {\n        \"Time in hours after which to write the checkpoint and exit. \"\n        \"If 'None' is specified, no action will be taken.\"};\n  };\n\n  using options = tmpl::list<WallclockHours>;\n  static constexpr Options::String help{\n      \"Once the wallclock time has exceeded the specified amount, trigger \"\n      \"writing a checkpoint and then exit with the 'ContinueFromCheckpoint' \"\n      \"exit code.\"};\n\n  using argument_tags = tmpl::list<>;\n  using return_tags = tmpl::list<>;\n\n  using phase_change_tags_and_combines =\n      tmpl::list<Tags::RestartPhase, Tags::CheckpointAndExitRequested>;\n\n  template <typename Metavariables>\n  using participating_components = typename Metavariables::component_list;\n\n  template <typename... DecisionTags>\n  void initialize_phase_data_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data) const;\n\n  template <typename ParallelComponent, typename ArrayIndex,\n            typename Metavariables>\n  void contribute_phase_data_impl(Parallel::GlobalCache<Metavariables>& cache,\n                                  const ArrayIndex& array_index) const;\n\n  template <typename... DecisionTags, typename Metavariables>\n  typename std::optional<std::pair<Parallel::Phase, ArbitrationStrategy>>\n  arbitrate_phase_change_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data,\n      const Parallel::Phase current_phase,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/) const;\n\n  void pup(PUP::er& p) override;\n\n private:\n  std::optional<double> wallclock_hours_for_checkpoint_and_exit_ = std::nullopt;\n  // This flag is set during arbitration when the class decides to\n  // halt the run.  As it is not checkpointed, this distinguishes the\n  // state immediately after writing the checkpoint from that\n  // immediately after reading it during the restart.\n  //\n  // Phase arbitration is only run from Main, so there are no\n  // threading issues here.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable bool halting_ = false;\n};\n\ntemplate <typename... DecisionTags>\nvoid CheckpointAndExitAfterWallclock::initialize_phase_data_impl(\n    const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n        phase_change_decision_data) const {\n  tuples::get<Tags::RestartPhase>(*phase_change_decision_data) = std::nullopt;\n  tuples::get<Tags::CheckpointAndExitRequested>(*phase_change_decision_data) =\n      false;\n}\n\ntemplate <typename ParallelComponent, typename ArrayIndex,\n          typename Metavariables>\nvoid CheckpointAndExitAfterWallclock::contribute_phase_data_impl(\n    Parallel::GlobalCache<Metavariables>& cache,\n    const ArrayIndex& array_index) const {\n  if constexpr (std::is_same_v<typename ParallelComponent::chare_type,\n                               Parallel::Algorithms::Array>) {\n    Parallel::contribute_to_phase_change_reduction<ParallelComponent>(\n        tuples::TaggedTuple<Tags::CheckpointAndExitRequested>{true}, cache,\n        array_index);\n  } else {\n    Parallel::contribute_to_phase_change_reduction<ParallelComponent>(\n        tuples::TaggedTuple<Tags::CheckpointAndExitRequested>{true}, cache);\n  }\n}\n\ntemplate <typename... DecisionTags, typename Metavariables>\ntypename std::optional<std::pair<Parallel::Phase, ArbitrationStrategy>>\nCheckpointAndExitAfterWallclock::arbitrate_phase_change_impl(\n    const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n        phase_change_decision_data,\n    const Parallel::Phase current_phase,\n    const Parallel::GlobalCache<Metavariables>& /*cache*/) const {\n  const double elapsed_hours = sys::wall_time() / 3600.0;\n\n  auto& restart_phase =\n      tuples::get<Tags::RestartPhase>(*phase_change_decision_data);\n  auto& exit_code =\n      tuples::get<Parallel::Tags::ExitCode>(*phase_change_decision_data);\n  if (restart_phase.has_value()) {\n    // This `if` branch, where restart_phase has a value, is the\n    // post-checkpoint call to arbitrate_phase_change.\n    if (halting_) {\n      // Preserve restart_phase for use after restarting from the checkpoint\n      exit_code = Parallel::ExitCode::ContinueFromCheckpoint;\n      return std::make_pair(Parallel::Phase::Exit,\n                            ArbitrationStrategy::RunPhaseImmediately);\n    } else {\n      // if current_phase is WriteCheckpoint, we follow with updating options\n      if (current_phase == Parallel::Phase::WriteCheckpoint) {\n        Parallel::printf(\"Restarting from checkpoint. Date and time: %s\\n\",\n                         utc_time());\n        return std::make_pair(\n            Parallel::Phase::UpdateOptionsAtRestartFromCheckpoint,\n            ArbitrationStrategy::PermitAdditionalJumps);\n      } else if (current_phase ==\n                 Parallel::Phase::UpdateOptionsAtRestartFromCheckpoint) {\n        return std::make_pair(Parallel::Phase::Restart,\n                              ArbitrationStrategy::PermitAdditionalJumps);\n      }\n      // Reset restart_phase until it is needed for the next checkpoint\n      const auto result = restart_phase;\n      restart_phase.reset();\n      return std::make_pair(result.value(),\n                            ArbitrationStrategy::PermitAdditionalJumps);\n    }\n  }\n\n  auto& checkpoint_and_exit_requested =\n      tuples::get<Tags::CheckpointAndExitRequested>(\n          *phase_change_decision_data);\n  if (checkpoint_and_exit_requested) {\n    checkpoint_and_exit_requested = false;\n    if (elapsed_hours >= wallclock_hours_for_checkpoint_and_exit_.value_or(\n                             std::numeric_limits<double>::infinity())) {\n      // Record phase and actual elapsed time for determining following phase\n      restart_phase = current_phase;\n      ASSERT(not halting_, \"Halting for checkpoint recursively\");\n      halting_ = true;\n      return std::make_pair(Parallel::Phase::WriteCheckpoint,\n                            ArbitrationStrategy::RunPhaseImmediately);\n    }\n  }\n  return std::nullopt;\n}\n}  // namespace PhaseControl\n"
  },
  {
    "path": "src/Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <functional>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/CharmRegistration.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Parallel/PhaseControlReductionHelpers.hpp\"\n\n#include \"Parallel/Main.decl.h\"\n\nnamespace Parallel {\n/// @{\n/// Send data from a parallel component to the Main chare for making\n/// phase-change decisions.\n///\n/// This function is distinct from `Parallel::contribute_to_reduction` because\n/// this function contributes reduction data to the Main chare via the entry\n/// method `phase_change_reduction`. This must be done because the entry method\n/// must alter member data specific to the Main chare, and the Main chare cannot\n/// execute actions like most SpECTRE parallel components.\n/// For all cases other than sending phase-change decision data to the Main\n/// chare, you should use `Parallel::contribute_to_reduction`.\ntemplate <typename SenderComponent, typename ArrayIndex, typename Metavariables,\n          class... Ts>\nvoid contribute_to_phase_change_reduction(\n    tuples::TaggedTuple<Ts...> data_for_reduction,\n    Parallel::GlobalCache<Metavariables>& cache,\n    const ArrayIndex& array_index) {\n  using phase_change_tags_and_combines_list =\n      PhaseControl::get_phase_change_tags<Metavariables>;\n  using reduction_data_type =\n      PhaseControl::reduction_data<tmpl::list<Ts...>,\n                                   phase_change_tags_and_combines_list>;\n  (void)Parallel::charmxx::RegisterReducerFunction<\n      reduction_data_type::combine>::registrar;\n  CkCallback callback(CProxy_Main<Metavariables>::index_t::\n                          template redn_wrapper_phase_change_reduction<\n                              PhaseControl::TaggedTupleCombine, Ts...>(nullptr),\n                      cache.get_main_proxy().value());\n  reduction_data_type reduction_data{data_for_reduction};\n  Parallel::local(\n      Parallel::get_parallel_component<SenderComponent>(cache)[array_index])\n      ->contribute(static_cast<int>(reduction_data.size()),\n                   reduction_data.packed().get(),\n                   Parallel::charmxx::charm_reducer_functions.at(\n                       std::hash<Parallel::charmxx::ReducerFunctions>{}(\n                           &reduction_data_type::combine)),\n                   callback);\n}\n\ntemplate <typename SenderComponent, typename Metavariables, class... Ts>\nvoid contribute_to_phase_change_reduction(\n    tuples::TaggedTuple<Ts...> data_for_reduction,\n    Parallel::GlobalCache<Metavariables>& cache) {\n  using phase_change_tags_and_combines_list =\n      PhaseControl::get_phase_change_tags<Metavariables>;\n  using reduction_data_type =\n      PhaseControl::reduction_data<tmpl::list<Ts...>,\n                                   phase_change_tags_and_combines_list>;\n  (void)Parallel::charmxx::RegisterReducerFunction<\n      reduction_data_type::combine>::registrar;\n  CkCallback callback(CProxy_Main<Metavariables>::index_t::\n                          template redn_wrapper_phase_change_reduction<\n                              PhaseControl::TaggedTupleCombine, Ts...>(nullptr),\n                      cache.get_main_proxy().value());\n  reduction_data_type reduction_data{data_for_reduction};\n\n  if constexpr (std::is_same_v<typename SenderComponent::chare_type,\n                               Parallel::Algorithms::Singleton>) {\n    Parallel::local(Parallel::get_parallel_component<SenderComponent>(cache))\n        ->contribute(static_cast<int>(reduction_data.size()),\n                     reduction_data.packed().get(),\n                     Parallel::charmxx::charm_reducer_functions.at(\n                         std::hash<Parallel::charmxx::ReducerFunctions>{}(\n                             &reduction_data_type::combine)),\n                     callback);\n\n  } else {\n    Parallel::local_branch(\n        Parallel::get_parallel_component<SenderComponent>(cache))\n        ->contribute(static_cast<int>(reduction_data.size()),\n                     reduction_data.packed().get(),\n                     Parallel::charmxx::charm_reducer_functions.at(\n                         std::hash<Parallel::charmxx::ReducerFunctions>{}(\n                             &reduction_data_type::combine)),\n                     callback);\n  }\n}\n/// @}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/PhaseControl/ExecutePhaseChange.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\nnamespace PhaseControl {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Check if any triggers are activated, and perform phase changes as\n * needed.\n *\n * This action is intended to be executed on every component that repeatedly\n * runs iterable actions that would need to halt during a phase change. This\n * action sends data to the Main chare via a reduction.\n *\n * This action iterates over the `Tags::PhaseChangeAndTriggers`, sending\n * reduction data for the phase decision for each triggered `PhaseChange`, then\n * halts the algorithm execution so that the `Main` chare can make a phase\n * decision if any were triggered.\n *\n * Uses:\n * - GlobalCache: `Tags::PhaseChangeAndTriggers`\n * - DataBox: As specified by the `PhaseChange` option-created objects.\n *   - `PhaseChange` objects are permitted to perform mutations on the\n *     \\ref DataBoxGroup \"DataBox\" to store persistent state information.\n */\nstruct ExecutePhaseChange {\n  using const_global_cache_tags =\n      tmpl::list<PhaseControl::Tags::PhaseChangeAndTriggers>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*component*/) {\n    if constexpr (db::tag_is_retrievable_v<::Tags::TimeStepId,\n                                           db::DataBox<DbTags>>) {\n      if (not db::get<::Tags::TimeStepId>(box).is_at_slab_boundary()) {\n        return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n      }\n    }\n    const auto& phase_change_and_triggers =\n        Parallel::get<Tags::PhaseChangeAndTriggers>(cache);\n    bool should_halt = false;\n    for (const auto& trigger_and_phase_changes : phase_change_and_triggers) {\n      const auto& trigger = trigger_and_phase_changes.trigger;\n      if (trigger->is_triggered(box)) {\n        const auto& phase_changes = trigger_and_phase_changes.phase_changes;\n        for (const auto& phase_change : phase_changes) {\n          phase_change->template contribute_phase_data<ParallelComponent>(\n              make_not_null(&box), cache, array_index);\n        }\n        should_halt = true;\n      }\n    }\n    // if we halt, we need to make sure that the Main chare knows that it is\n    // because we are requesting phase change arbitration, regardless of what\n    // data was actually sent to make that decision.\n    if (should_halt) {\n      if constexpr (std::is_same_v<typename ParallelComponent::chare_type,\n                    Parallel::Algorithms::Array>) {\n        Parallel::contribute_to_phase_change_reduction<ParallelComponent>(\n            tuples::TaggedTuple<TagsAndCombines::UsePhaseChangeArbitration>{\n                true},\n            cache, array_index);\n      } else {\n        Parallel::contribute_to_phase_change_reduction<ParallelComponent>(\n            tuples::TaggedTuple<TagsAndCombines::UsePhaseChangeArbitration>{\n                true},\n            cache);\n      }\n    }\n    return {should_halt ? Parallel::AlgorithmExecution::Halt\n                        : Parallel::AlgorithmExecution::Continue,\n            std::nullopt};\n  }\n};\n}  // namespace Actions\n\n/*!\n * \\brief Use the runtime data aggregated in `phase_change_decision_data` to\n * decide which phase to execute next.\n *\n * \\details This function will iterate through each of the option-created pairs\n * of `PhaseChange`s, and obtain from each a\n * `std::optional<std::pair<Parallel::Phase,\n * PhaseControl::ArbitrationStrategy>`. Any `std::nullopt` is skipped. If all\n * `PhaseChange`s provide `std::nullopt`, the phase will either keep its\n * current value (if the halt was caused by one of the triggers associated with\n * an  option-created `PhaseChange`), or this function will return a\n * `std::nullopt` as well (otherwise), indicating that the phase should proceed\n * according to other information, such as global ordering.\n *\n * In the case of a `PhaseControl::ArbitrationStrategy::RunPhaseImmediately`,\n * the first such return value is immediately run, and no further `PhaseChange`s\n * are queried for their input.\n *\n * \\note There can be cases where multiple triggers activate, and/or multiple\n * `PhaseChange` objects have data in a state for which they would request a\n * specific phase. When multiple phases are requested, arbitration will\n * proceed in order of appearance in the `PhaseChangeAndTriggers`, determined\n * from the input file options. Therefore, if that order of execution is\n * important for the logic of the executable, the input file ordering and\n * `ArbitrationStrategy` must be chosen carefully.\n */\ntemplate <typename... DecisionTags, typename Metavariables>\ntypename std::optional<Parallel::Phase> arbitrate_phase_change(\n    const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n        phase_change_decision_data,\n    Parallel::Phase current_phase,\n    const Parallel::GlobalCache<Metavariables>& cache) {\n  if constexpr (tmpl::list_contains_v<typename Parallel::GlobalCache<\n                                          Metavariables>::const_tags_list,\n                                      Tags::PhaseChangeAndTriggers>) {\n    const auto& phase_change_and_triggers =\n        Parallel::get<Tags::PhaseChangeAndTriggers>(cache);\n    bool phase_chosen = false;\n    for (const auto& trigger_and_phase_changes : phase_change_and_triggers) {\n      for (const auto& phase_change : trigger_and_phase_changes.phase_changes) {\n        const auto phase_result = phase_change->arbitrate_phase_change(\n            phase_change_decision_data, current_phase, cache);\n        if (phase_result.has_value()) {\n          if (phase_result.value().second ==\n              ArbitrationStrategy::RunPhaseImmediately) {\n            tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(\n                *phase_change_decision_data) = false;\n            return phase_result.value().first;\n          }\n          current_phase = phase_result.value().first;\n          phase_chosen = true;\n        }\n      }\n    }\n    if (tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(\n            *phase_change_decision_data) == false and\n        not phase_chosen) {\n      return std::nullopt;\n    }\n    // if no phase change object suggests a specific phase, return to execution\n    // in the current phase.\n    tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(\n        *phase_change_decision_data) = false;\n    return current_phase;\n  } else {\n    (void)phase_change_decision_data;\n    (void)current_phase;\n    (void)cache;\n    return std::nullopt;\n  }\n}\n}  // namespace PhaseControl\n"
  },
  {
    "path": "src/Parallel/PhaseControl/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PhaseControl {\nusing factory_creatable_classes =\n    tmpl::list<VisitAndReturn<Parallel::Phase::EvaluateAmrCriteria>,\n               VisitAndReturn<Parallel::Phase::AdjustDomain>,\n               VisitAndReturn<Parallel::Phase::CheckDomain>,\n               VisitAndReturn<Parallel::Phase::DisableRotationControl>,\n               VisitAndReturn<Parallel::Phase::LoadBalancing>,\n               VisitAndReturn<Parallel::Phase::WriteCheckpoint>,\n               CheckpointAndExitAfterWallclock>;\n}\n"
  },
  {
    "path": "src/Parallel/PhaseControl/InitializePhaseChangeDecisionData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace PhaseControl {\n/*!\n * \\brief Initialize the Main chare's `phase_change_decision_data` for the\n * option-selected `PhaseChange`s.\n */\ntemplate <typename... DecisionTags, typename Metavariables>\nvoid initialize_phase_change_decision_data(\n    const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n        phase_change_decision_data,\n    const Parallel::GlobalCache<Metavariables>& cache) {\n  tuples::get<TagsAndCombines::UsePhaseChangeArbitration>(\n      *phase_change_decision_data) = false;\n  if constexpr (Parallel::is_in_global_cache<Metavariables,\n                                             Tags::PhaseChangeAndTriggers>) {\n    const auto& phase_change_and_triggers =\n        Parallel::get<Tags::PhaseChangeAndTriggers>(cache);\n    for (const auto& trigger_and_phase_changes : phase_change_and_triggers) {\n      for (const auto& phase_change : trigger_and_phase_changes.phase_changes) {\n        phase_change->template initialize_phase_data<Metavariables>(\n            phase_change_decision_data);\n      }\n    }\n  }\n}\n}  // namespace PhaseControl\n"
  },
  {
    "path": "src/Parallel/PhaseControl/PhaseChange.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Contains utilities for determining control-flow among phases\nnamespace PhaseControl {\n/// The possible options for instructing the Main chare in deciding the next\n/// phase to jump to.\n///\n/// An object of this enum type is packaged with a requested phase in the\n/// `PhaseChange::arbitrate_phase_change` function.\nenum ArbitrationStrategy {\n  /// Jump to the requested phase immediately, before considering other\n  /// requested phases.\n  ///\n  /// This will ensure that the requested phase is always run, where\n  /// alternative methods could have 'double-jumps' where the Main chare\n  /// replaces a requested phase immediately without actually entering the\n  /// phase.\n  RunPhaseImmediately,\n  /// After the requested phase is considered, continue considering other\n  /// requests, potentially replacing this request.\n  ///\n  /// This will permit reprocessing the phase-jumps to help with cases where\n  /// multiple phases are simultaneously requested.\n  /// The `PermitAdditionalJumps` permits 'double-jumps' where a requested phase\n  /// is immediately replaced by another phase to jump to.\n  PermitAdditionalJumps\n};\n}  // namespace PhaseControl\n\n/*!\n * \\brief `PhaseChange` objects determine the storage types and logic for\n * moving between phases based on runtime data.\n *\n * The phase control flow must have the cooperation of each parallel component,\n * but make phase decisions centrally so that at any point, all components are\n * in the same phase. The operations needed by the parallel components and by\n * the Main chare, are:\n *\n * 1. Parallel components must select and/or compute the runtime data necessary\n *    for choosing the next phase, then contribute it to a global reduction to\n *    the Main component.\n *    The components must then halt at a globally-valid state for the phase\n *    change.\n *    The requirements for the state will vary depending on the phase choices,\n *    so triggers must be selected appropriately for the `PhaseChange` object.\n *    For instance, selecting a common slab will usually represent a globally\n *    well-behaved state for a `DgElementArray`.\n * 2. On the Main chare, the `PhaseChange` objects must use the collected\n *    reduction data, or other persistent data stored in\n *    `phase_change_decision_data` to decide on a phase to request and an\n *    `PhaseControl::ArbitrationStrategy` to determine how to resolve multiple\n *    simultaneous requests.\n *    Additionally, the `PhaseChange` objects must specify initialization\n *    functions to set the starting state of the tags in\n *    `phase_change_decision_data` for which they are responsible.\n *\n * In addition to the `options` type alias and `static constexpr Options::String\n * help` variable needed to be option-creatable, a derived class of\n * `PhaseChange` must specify the type aliases:\n * - `argument_tags`: A `tmpl::list` of tags from the\n *   \\ref DataBoxGroup \"DataBox\" to be passed to `contribute_phase_data_impl` as\n *   const references.\n * - `return_tags`: A `tmpl::list` of mutable tags from the\n *   \\ref DataBoxGroup \"DataBox\" to be passed to `contribute_phase_data_impl` as\n *   `gsl::not_null` pointers. This should be used only for tags that may be\n *   altered during the `contribute_phase_data_impl` function.\n * - `phase_change_tags_and_combines`: A `tmpl::list` of tags for\n *   populating the `phase_change_decision_data` in the Main chare. Each tag in\n *   this list must also define a `combine_method` and a `main_combine_method`\n *   for performing the aggregation during reduction.\n * - `participating_components` (templated on `Metavariables`): A `tmpl::list`\n *   of components that contribute data during this reduction. This can be used\n *   to screen out components that will not have the necessary information to\n *   contribute to the reduction. If all components should participate, this\n *   type alias can be set to simply `typename Metavariables::component_list`.\n *\n * And member functions with signatures:\n *\n * ```\n * template <typename... DecisionTags>\n * void initialize_phase_data_impl(\n *     const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n *         phase_change_decision_data) const;\n * ```\n * - Must set all tags in `phase_change_tags_and_combines` to useful\n *   initial states in the `phase_change_decision_data`.\n *\n * ```\n * template <typename ParallelComponent, typename ArrayIndex>\n * void contribute_phase_data_impl(\n *     [DataBox return tags...], [DataBox argument tags...],\n *     Parallel::GlobalCache<Metavariables>& cache,\n *     const ArrayIndex& array_index) const;\n * ```\n * - Should send any data relevant for the associated phase change decision made\n *   in `arbitrate_phase_change_impl` to the Main chare via function\n *   `Parallel::contribute_to_phase_change_reduction`.\n *\n * ```\n * template <typename... DecisionTags, typename Metavariables>\n * typename std::optional<\n *     std::pair<Parallel::Phase, ArbitrationStrategy>>\n * arbitrate_phase_change_impl(\n *     const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n *         phase_change_decision_data,\n *     const Parallel::Phase current_phase,\n *     const Parallel::GlobalCache<Metavariables>& cache) const;\n * ```\n * - Should examine the collected data in `phase_change_decision_data` and\n *   optionally return a `std::pair` with the desired `Parallel::Phase` and\n *   an `PhaseControl::ArbitrationStrategy` indicating a method for arbitrating\n *   multiple simultaneous requests. Alternatively, it may return `std::nullopt`\n *   to abstain from the phase decision.\n *   The `arbitrate_phase_change_impl` may (and often will) mutate the\n *   `phase_change_decision_data`. For instance, it may be desirable to 'reset'\n *   the data to allow for future jumps associated with the same `PhaseChange`,\n *   or the `PhaseChange` will describe multiple changes in sequence, and the\n *   state of that sequential process can be recorded in\n *   `phase_change_decision_data`.\n */\nstruct PhaseChange : public PUP::able {\n protected:\n  /// \\cond\n  PhaseChange() = default;\n  PhaseChange(const PhaseChange&) = default;\n  PhaseChange(PhaseChange&&) = default;\n  PhaseChange& operator=(const PhaseChange&) = default;\n  PhaseChange& operator=(PhaseChange&&) = default;\n  /// \\endcond\n\n public:\n  PhaseChange(CkMigrateMessage* msg) : PUP::able(msg){};\n\n  ~PhaseChange() override = default;\n\n  WRAPPED_PUPable_abstract(PhaseChange);  // NOLINT\n\n  /// Send data from all `participating_components` to the Main chare for\n  /// determining the next phase.\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  void contribute_phase_data(const gsl::not_null<db::DataBox<DbTags>*> box,\n                             Parallel::GlobalCache<Metavariables>& cache,\n                             const ArrayIndex& array_index) const {\n    using factory_classes =\n        typename Metavariables::factory_creation::factory_classes;\n    call_with_dynamic_type<void, tmpl::at<factory_classes, PhaseChange>>(\n        this, [&box, &cache, &array_index](const auto* const phase_change) {\n          using phase_change_t = typename std::decay_t<decltype(*phase_change)>;\n          if constexpr (tmpl::list_contains_v<\n                            typename phase_change_t::\n                                template participating_components<\n                                    Metavariables>,\n                            ParallelComponent>) {\n            db::mutate_apply<typename phase_change_t::return_tags,\n                             typename phase_change_t::argument_tags>(\n                [&phase_change, &cache, &array_index](auto&&... args) {\n                  phase_change\n                      ->template contribute_phase_data_impl<ParallelComponent>(\n                          args..., cache, array_index);\n                },\n                box);\n          }\n        });\n  }\n\n  /// Determine a phase request and `PhaseControl::ArbitrationStrategy` based on\n  /// aggregated `phase_change_decision_data` on the Main Chare.\n  template <typename... DecisionTags, typename Metavariables>\n  std::optional<std::pair<Parallel::Phase, PhaseControl::ArbitrationStrategy>>\n  arbitrate_phase_change(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data,\n      const Parallel::Phase current_phase,\n      const Parallel::GlobalCache<Metavariables>& cache) const {\n    using factory_classes =\n        typename Metavariables::factory_creation::factory_classes;\n    return call_with_dynamic_type<\n        std::optional<\n            std::pair<Parallel::Phase, PhaseControl::ArbitrationStrategy>>,\n        tmpl::at<factory_classes, PhaseChange>>(\n        this, [&current_phase, &phase_change_decision_data,\n               &cache](const auto* const phase_change) {\n          return phase_change->arbitrate_phase_change_impl(\n              phase_change_decision_data, current_phase, cache);\n        });\n  }\n\n  /// Initialize the `phase_change_decision_data` on the main chare to starting\n  /// values.\n  template <typename Metavariables, typename... Tags>\n  void initialize_phase_data(const gsl::not_null<tuples::TaggedTuple<Tags...>*>\n                                 phase_change_decision_data) const {\n    using factory_classes =\n        typename Metavariables::factory_creation::factory_classes;\n    return call_with_dynamic_type<void, tmpl::at<factory_classes, PhaseChange>>(\n        this, [&phase_change_decision_data](const auto* const phase_change) {\n          return phase_change->initialize_phase_data_impl(\n              phase_change_decision_data);\n        });\n  }\n};\n"
  },
  {
    "path": "src/Parallel/PhaseControl/PhaseControlTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n\nnamespace PhaseControl {\nTriggerAndPhaseChanges::TriggerAndPhaseChanges() = default;\nTriggerAndPhaseChanges::TriggerAndPhaseChanges(\n    std::unique_ptr<::Trigger> trigger_in,\n    std::vector<std::unique_ptr<::PhaseChange>> phase_changes_in)\n    : trigger(std::move(trigger_in)),\n      phase_changes(std::move(phase_changes_in)) {}\n\nvoid TriggerAndPhaseChanges::pup(PUP::er& p) {\n  p | trigger;\n  p | phase_changes;\n}\n}  // namespace PhaseControl\n"
  },
  {
    "path": "src/Parallel/PhaseControl/PhaseControlTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"Options/String.hpp\"\n#include \"Parallel/ExitCode.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PhaseControl {\n\n/// Option-creatable pair of a trigger and associated phase change objects.\nstruct TriggerAndPhaseChanges {\n  struct Trigger {\n    using type = std::unique_ptr<::Trigger>;\n    static constexpr Options::String help =\n        \"Determines when the phase changes are evaluated.\";\n  };\n  struct PhaseChanges {\n    using type = std::vector<std::unique_ptr<::PhaseChange>>;\n    static constexpr Options::String help =\n        \"These phase changes are evaluated when the Trigger fires.\";\n  };\n  static constexpr Options::String help =\n      \"Phase changes that are evaluated when the Trigger fires.\";\n  using options = tmpl::list<Trigger, PhaseChanges>;\n\n  TriggerAndPhaseChanges();\n  TriggerAndPhaseChanges(\n      std::unique_ptr<::Trigger> trigger_in,\n      std::vector<std::unique_ptr<::PhaseChange>> phase_changes_in);\n  void pup(PUP::er& p);\n  std::unique_ptr<::Trigger> trigger{};\n  std::vector<std::unique_ptr<::PhaseChange>> phase_changes{};\n};\n\nnamespace OptionTags {\n/// Option tag for the collection of triggers that indicate synchronization\n/// points at which phase changes should be considered, and the associated\n/// `PhaseChange` objects for making the phase change decisions.\n///\n/// When the phase control is arbitrated on the main chare, the `PhaseChange`\n/// objects will be queried for their phase request in order of appearance in\n/// the nested list (i.e. first all of the `PhaseChange`s associated with the\n/// first trigger, in order, then those associated with the second trigger,\n/// etc.). The order therefore determines the order of resolution of\n/// simultaneous requests.\nstruct PhaseChangeAndTriggers {\n  static constexpr Options::String help{\n      \"A collection of pairs of triggers and collections of phase change \"\n      \"objects to determine runtime phase control-flow decisions. The order of \"\n      \"the phase change objects determines the order of the requests processed \"\n      \"by the Main chare during phase change arbitration.\"};\n\n  using type = std::vector<TriggerAndPhaseChanges>;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// Tag for the collection of triggers that indicate synchronization points at\n/// which phase changes should be considered, and the associated `PhaseChange`\n/// objects for making the phase change decisions.\nstruct PhaseChangeAndTriggers : db::SimpleTag {\n  using type = std::vector<TriggerAndPhaseChanges>;\n\n  using option_tags = tmpl::list<OptionTags::PhaseChangeAndTriggers>;\n  static constexpr bool is_overlayable = true;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& phase_control_and_triggers) {\n    return serialize_and_deserialize<type>(phase_control_and_triggers);\n  }\n};\n}  // namespace Tags\n\nnamespace TagsAndCombines {\n/// A tag for indicating that a halt was called by a trigger associated with\n/// `PhaseChange`s.\n///\n/// This is needed to disambiguate different quiescence conditions in the main\n/// chare. It is automatically included in\n/// `PhaseControl::get_phase_change_tags`, so shouldn't be explicitly included\n/// in the `phase_change_tags_and_combines` in derived classes of\n/// `PhaseChange`.\nstruct UsePhaseChangeArbitration {\n  using type = bool;\n  using combine_method = funcl::Or<>;\n  using main_combine_method = combine_method;\n};\n}  // namespace TagsAndCombines\n\nnamespace detail {\ntemplate <typename Metavariables, typename = std::void_t<>>\nstruct phase_change_derived_classes {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Metavariables>\nstruct phase_change_derived_classes<\n    Metavariables,\n  std::void_t<typename Metavariables::factory_creation::factory_classes>> {\n private:\n  using factory_entry =\n      tmpl::at<typename Metavariables::factory_creation::factory_classes,\n               PhaseChange>;\n\n public:\n  using type =\n      tmpl::conditional_t<std::is_same_v<factory_entry, tmpl::no_such_type_>,\n                          tmpl::list<>, factory_entry>;\n};\n\ntemplate <typename PhaseChangeDerived>\nstruct get_phase_change_tags_and_combines {\n  using type = typename PhaseChangeDerived::phase_change_tags_and_combines;\n};\n}  // namespace detail\n\n/// Metafunction for determining the merged collection of tags in\n/// `phase_change_tags_and_combines`s from all `PhaseChange` derived\n/// classes in `Metavariables::factory_creation`\ntemplate <typename Metavariables>\nusing get_phase_change_tags = tmpl::push_back<\n    tmpl::flatten<tmpl::transform<\n        typename detail::phase_change_derived_classes<Metavariables>::type,\n        detail::get_phase_change_tags_and_combines<tmpl::_1>>>,\n    TagsAndCombines::UsePhaseChangeArbitration, Parallel::Tags::ExitCode>;\n}  // namespace PhaseControl\n"
  },
  {
    "path": "src/Parallel/PhaseControl/VisitAndReturn.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PhaseControl {\nnamespace Tags {\n/// Storage in the phase change decision tuple so that the Main chare can record\n/// the phase to return to after a temporary phase.\n///\n/// \\note This tag is not intended to participate in any of the reduction\n/// procedures, so will error if the combine method is called.\ntemplate <Parallel::Phase Phase>\nstruct ReturnPhase {\n  using type = std::optional<Parallel::Phase>;\n\n  struct combine_method {\n    std::optional<Parallel::Phase> operator()(\n        const std::optional<Parallel::Phase> /*first_phase*/,\n        const std::optional<Parallel::Phase>& /*second_phase*/) {\n      ERROR(\n          \"The return phase should only be altered by the phase change \"\n          \"arbitration in the Main chare, so no reduction data should be \"\n          \"provided.\");\n    }\n  };\n\n  using main_combine_method = combine_method;\n};\n\n/// Stores whether the phase in question has been requested.\n///\n/// Combinations are performed via `funcl::Or`, as the phase in question should\n/// be chosen if any component requests the jump.\ntemplate <Parallel::Phase Phase>\nstruct TemporaryPhaseRequested {\n  using type = bool;\n\n  using combine_method = funcl::Or<>;\n  using main_combine_method = funcl::Or<>;\n};\n}  // namespace Tags\n\n/*!\n * \\brief Phase control object for temporarily visiting `TargetPhase`, until the\n * algorithm halts again, then returning to the original phase.\n *\n * The motivation for this type of procedure is e.g. load balancing,\n * checkpointing, and other maintenance tasks that should be performed\n * periodically during a lengthy evolution.\n * Once triggered, this will cause a change to `TargetPhase`, but store the\n * current phase to resume execution when the tasks in `TargetPhase` are\n * completed.\n *\n * Any parallel component can participate in the associated phase change\n * reduction data contribution, and if any component requests the temporary\n * phase, it will execute.\n *\n * \\note  If multiple such methods are specified (with different\n * `TargetPhase`s), then the order of phase jumps depends on their order in the\n * list.\n * - If multiple `VisitAndReturn`s trigger simultaneously, then they will visit\n *   in sequence specified by the input file: first going to the first\n *   `TargetPhase` until that phase resolves, then immediately entering the\n *   second `TargetPhase` (without yet returning to the original phase), then\n *   finally returning to the original phase.\n * - If a `VisitAndReturn` is triggered in a phase that is already a\n *   `TargetPhase` of another `VisitAndReturn`, it will be executed, and\n *   following completion, control will return to the original phase from before\n *   the first `VisitAndReturn`.\n */\ntemplate <Parallel::Phase TargetPhase>\nstruct VisitAndReturn : public PhaseChange {\n  /// \\cond\n  VisitAndReturn() = default;\n  explicit VisitAndReturn(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(VisitAndReturn);  // NOLINT\n  /// \\endcond\n\n  static std::string name() {\n    return MakeString{} << \"VisitAndReturn(\" << TargetPhase << \")\";\n  }\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Temporarily jump to the phase given by `TargetPhase`, returning to the \"\n      \"previously executing phase when complete.\"};\n\n  using argument_tags = tmpl::list<>;\n  using return_tags = tmpl::list<>;\n\n  using phase_change_tags_and_combines =\n      tmpl::list<Tags::ReturnPhase<TargetPhase>,\n                 Tags::TemporaryPhaseRequested<TargetPhase>>;\n\n  template <typename Metavariables>\n  using participating_components = typename Metavariables::component_list;\n\n  template <typename... DecisionTags>\n  void initialize_phase_data_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data) const {\n    tuples::get<Tags::ReturnPhase<TargetPhase>>(*phase_change_decision_data) =\n        std::nullopt;\n    tuples::get<Tags::TemporaryPhaseRequested<TargetPhase>>(\n        *phase_change_decision_data) = false;\n  }\n\n  template <typename ParallelComponent, typename ArrayIndex,\n            typename Metavariables>\n  void contribute_phase_data_impl(Parallel::GlobalCache<Metavariables>& cache,\n                                  const ArrayIndex& array_index) const {\n    if constexpr (std::is_same_v<typename ParallelComponent::chare_type,\n                                 Parallel::Algorithms::Array>) {\n      Parallel::contribute_to_phase_change_reduction<ParallelComponent>(\n          tuples::TaggedTuple<Tags::TemporaryPhaseRequested<TargetPhase>>{true},\n          cache, array_index);\n    } else {\n      Parallel::contribute_to_phase_change_reduction<ParallelComponent>(\n          tuples::TaggedTuple<Tags::TemporaryPhaseRequested<TargetPhase>>{true},\n          cache);\n    }\n  }\n\n  template <typename... DecisionTags, typename Metavariables>\n  typename std::optional<std::pair<Parallel::Phase, ArbitrationStrategy>>\n  arbitrate_phase_change_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data,\n      const Parallel::Phase current_phase,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/) const {\n    auto& return_phase = tuples::get<Tags::ReturnPhase<TargetPhase>>(\n        *phase_change_decision_data);\n    if (return_phase.has_value()) {\n      const auto result = return_phase;\n      return_phase.reset();\n      return std::make_pair(result.value(),\n                            ArbitrationStrategy::PermitAdditionalJumps);\n    }\n    auto& temporary_phase_requested =\n        tuples::get<Tags::TemporaryPhaseRequested<TargetPhase>>(\n            *phase_change_decision_data);\n    if (temporary_phase_requested) {\n      return_phase = current_phase;\n      temporary_phase_requested = false;\n      return std::make_pair(TargetPhase,\n                            ArbitrationStrategy::RunPhaseImmediately);\n    }\n    return std::nullopt;\n  }\n\n  void pup(PUP::er& /*p*/) override {}\n};\n}  // namespace PhaseControl\n\n/// \\cond\ntemplate <Parallel::Phase TargetPhase>\nPUP::able::PUP_ID PhaseControl::VisitAndReturn<TargetPhase>::my_PUP_ID = 0;\n/// \\endcond\n"
  },
  {
    "path": "src/Parallel/PhaseControlReductionHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PhaseControl {\n\n/// A type for denoting a piece of data for deciding a phase change.\n///\n/// `Tag` is intended to be a tag (with a type) for indexing a\n/// `tuples::TaggedTuple`, and `CombineMethod` is intended to be a\n/// `Parallel::ReductionDatum`-compatible invokable for combining the `type` of\n/// the `Tag`. The `MainCombineMethod` is provided to give flexibility for a\n/// different method of combination at the top level of the hierarchy (so, in\n/// the case of phase control reductions, performed by the main chare to combine\n/// reductions from different chares)\ntemplate <typename Tag, typename CombineMethod,\n          typename MainCombineMethod = CombineMethod>\nstruct TagAndCombine : Tag {\n  using tag = Tag;\n  using combine_method = CombineMethod;\n  using main_combine_method = MainCombineMethod;\n};\n\n/// A flexible combine invokable that combines into a `tuples::TaggedTuple` a\n/// new `tuples::TaggedTuple`, and combines according to type aliases\n/// `combination_method`s that are required to be defined in each tag.\nstruct TaggedTupleCombine {\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> operator()(\n      tuples::TaggedTuple<Tags...> current_state,\n      const tuples::TaggedTuple<Tags...>& element) {\n    tmpl::for_each<tmpl::list<Tags...>>([&current_state, &element](auto tag_v) {\n      using tag = typename decltype(tag_v)::type;\n      tuples::get<tag>(current_state) = typename tag::combine_method{}(\n          tuples::get<tag>(current_state), tuples::get<tag>(element));\n    });\n    return current_state;\n  }\n};\n\n/// A flexible combine invokable that combines into a `tuples::TaggedTuple` a\n/// new `tuples::TaggedTuple` with a subset of the original tags, and combines\n/// according to type aliases `main_combine_method`s that are required to be\n/// defined in each tag.\n///\n/// \\note This is _not_ usable with charm++ reductions; it mutates the current\n/// state in-place. This is constructed for the use-case where the main chare\n/// stores a persistent data structure and combines reduction data as it arrives\n/// from the other chares.\nstruct TaggedTupleMainCombine {\n  template <typename... CurrentTags, typename... CombineTags>\n  static void apply(\n      const gsl::not_null<tuples::TaggedTuple<CurrentTags...>*> current_state,\n      const tuples::TaggedTuple<CombineTags...>& element) {\n    tmpl::for_each<tmpl::list<CombineTags...>>([&current_state,\n                                                &element](auto tag_v) {\n      using tag = typename decltype(tag_v)::type;\n      tuples::get<tag>(*current_state) = typename tag::main_combine_method{}(\n          tuples::get<tag>(*current_state), tuples::get<tag>(element));\n    });\n  }\n};\n\n/// A `Parallel::ReductionData` with a single `Parallel::ReductionDatum` for a\n/// given tagged tuple type determined by `TagsPresent`, and performs the\n/// combine according to `TagsAndCombines`, which must be a `tmpl::list` of\n/// `PhaseControl::TagAndCombine`s.\n///\n/// Each tag in the `TagsAndCombinesPresent` may either be a `TagsAndCombines`\n/// or otherise define all three type traits `type`, `combine_method`, and\n/// `main_combine_method`.\ntemplate <typename TagsAndCombinesPresent, typename TagsAndCombines>\nusing reduction_data = Parallel::ReductionData<Parallel::ReductionDatum<\n    tuples::tagged_tuple_from_typelist<TagsAndCombinesPresent>,\n    TaggedTupleCombine>>;\n}  // namespace PhaseControl\n"
  },
  {
    "path": "src/Parallel/PhaseDependentActionList.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\n/*!\n * \\ingroup ParallelGroup\n * \\brief List of all the actions to be executed in the specified phase.\n */\ntemplate <Parallel::Phase Phase, typename ActionsList>\nstruct PhaseActions {\n  using action_list = tmpl::flatten<ActionsList>;\n  static constexpr Parallel::Phase phase = Phase;\n  static constexpr size_t number_of_actions = tmpl::size<action_list>::value;\n};\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief (Lazy) metafunction to get the action list from a `PhaseActions`\n */\ntemplate <typename PhaseDepActionList>\nstruct get_action_list_from_phase_dep_action_list {\n  using type = typename PhaseDepActionList::action_list;\n};\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Printf/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_charm_module(Printf)\n\nset(LIBRARY Printf)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Printf.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Printf.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ErrorHandling\n  INTERFACE\n  Utilities\n  PRIVATE\n  SystemUtilities\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_Printf\n  )\n"
  },
  {
    "path": "src/Parallel/Printf/Printf.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmodule Printf {\n\n  include \"string\";\n  include \"vector\";\n\n  namespace Parallel {\n  array [1D] PrinterChare {\n    entry PrinterChare();\n\n    entry void print(bool error, const std::vector<char>&);\n    entry void print_to_file(const std::string& file, const std::vector<char>&);\n  }\n\n  readonly CProxy_PrinterChare printer_chare;\n  readonly bool printer_chare_is_set;\n  }  // namespace Parallel\n}\n"
  },
  {
    "path": "src/Parallel/Printf/Printf.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/Printf/Printf.hpp\"\n\n#include <cerrno>\n#include <cstdio>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Strerror.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace {\nvoid do_print(const bool error, const std::vector<char>& message) {\n  FILE* const stream = error ? stderr : stdout;\n  // fprintf is atomic and threadsafe within a single process (via\n  // locks).  See flockfile(3).\n\n  // cert-err33-c: False positive: \"The return value of a call to\n  // fprintf() ... may be ignored if the output is being directed to\n  // stdout or stderr.\"\n\n  // cppcoreguidelines-pro-type-vararg: I cannot change the signature\n  // of fprintf.\n\n  // NOLINTNEXTLINE(cert-err33-c,cppcoreguidelines-pro-type-vararg)\n  fprintf(stream, \"%s\", message.data());\n}\n\nvoid do_print_to_file(const std::string& file,\n                      const std::vector<char>& message) {\n  struct DeleteFile {\n    void operator()(FILE* p) const {\n      const auto success = std::fclose(p);\n      if (success < 0) {\n        const auto errno_save = errno;\n        ERROR_NO_TRACE(\"Could not close stream: \"\n                       << strerror_threadsafe(errno_save) << \"\\n\");\n      }\n    }\n  };\n  const std::unique_ptr<FILE, DeleteFile> stream(std::fopen(file.c_str(), \"a\"));\n  if (stream == nullptr) {\n    const auto errno_save = errno;\n    ERROR_NO_TRACE(\"Could not open '\" << file << \"' for writing: \"\n                                      << strerror_threadsafe(errno_save)\n                                      << \"\\n\");\n  }\n  // cppcoreguidelines-pro-type-vararg: I cannot change the signature\n  // of fprintf.\n\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n  const auto success = fprintf(stream.get(), \"%s\", message.data());\n  if (success < 0) {\n    const auto errno_save = errno;\n    ERROR_NO_TRACE(\"Could not write '\"\n                   << file << \"': \" << strerror_threadsafe(errno_save) << \"\\n\");\n  }\n}\n}  // namespace\n\nnamespace Parallel {\nnamespace detail {\nvoid send_message(const bool error, const std::vector<char>& message) {\n  if (printer_chare_is_set and sys::my_node() != 0) {\n    printer_chare[0].print(error, message);\n  } else {\n    do_print(error, message);\n  }\n}\n\nvoid send_message_to_file(const std::string& file,\n                          const std::vector<char>& message) {\n  // Unlike in send_message, we always print through the printer\n  // chare, even on node 0.  The thread-safety of the stdio functions\n  // is on a per-stream basis, and all threads share the same stdout\n  // and stderr, but not the stream for any random file we open.\n  if (printer_chare_is_set) {\n    printer_chare[0].print_to_file(file, message);\n  } else {\n    do_print_to_file(file, message);\n  }\n}\n}  // namespace detail\n\nvoid PrinterChare::print(const bool error, const std::vector<char>& message) {\n  do_print(error, message);\n}\n\nvoid PrinterChare::print_to_file(const std::string& file,\n                                 const std::vector<char>& message) {\n  do_print_to_file(file, message);\n}\n\nvoid PrinterChare::register_with_charm() {\n  // From the code generated by charm from the ci file.\n  _registerPrintf();\n}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nCProxy_PrinterChare printer_chare;\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool printer_chare_is_set;\n}  // namespace Parallel\n\n#include \"Parallel/Printf/Printf.def.h\"\n"
  },
  {
    "path": "src/Parallel/Printf/Printf.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines Parallel::printf for writing to stdout\n\n#pragma once\n\n#include <cstddef>\n#include <cstdio>\n#include <iomanip>\n#include <limits>\n#include <sstream>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits/IsStreamable.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n#include \"Parallel/Printf/Printf.decl.h\"\n\nnamespace Parallel {\nnamespace detail {\n/*!\n * Fundamentals and pointers are already printable so there is no conversion\n * to a std::string necessary.\n */\ntemplate <typename T,\n          Requires<std::is_fundamental<std::decay_t<\n                       std::remove_pointer_t<std::decay_t<T>>>>::value or\n                   std::is_pointer<T>::value or\n                   std::is_pointer<std::decay_t<T>>::value> = nullptr>\ninline constexpr T stream_object_to_string(T&& t) {\n  return t;\n}\n\n/*!\n * Stream an object of type `T` into a std::stringstream and return it as a\n * std::string so that it is printable by calling `.c_str()` on it.\n * We need a 2-phase call so that the std::string doesn't go out of scope before\n * the C-style string is passed to printf.\n */\ntemplate <typename T,\n          Requires<std::is_class<std::decay_t<T>>::value or\n                   std::is_enum<std::decay_t<T>>::value> = nullptr>\ninline std::string stream_object_to_string(T&& t) {\n  using ::operator<<;\n  static_assert(tt::is_streamable<std::stringstream, T>::value,\n                \"Cannot stream type and therefore it cannot be printed. Please \"\n                \"define a stream operator for the type.\");\n  std::stringstream ss;\n  ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)\n     << std::scientific << t;\n  return ss.str();\n}\n\n/*!\n * Fundamentals are already printable, so nothing to do.\n */\ntemplate <typename T,\n          Requires<std::is_fundamental<std::decay_t<\n              std::remove_pointer_t<std::decay_t<T>>>>::value> = nullptr>\ninline constexpr T get_printable_type(T&& t) {\n  return t;\n}\n\n/*!\n * Get the pointer of the std::string so it can be passed to CkPrintf which\n * only works on fundamentals\n */\ninline const typename std::string::value_type* get_printable_type(\n    const std::string& t) {\n  return t.c_str();\n}\n\nvoid send_message(bool error, const std::vector<char>& message);\nvoid send_message_to_file(const std::string& file,\n                          const std::vector<char>& message);\n\ntemplate <typename... Ts>\ninline std::vector<char> allocate_message(const char* const format, Ts&&... t) {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"\n#pragma GCC diagnostic ignored \"-Wformat-security\"\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)\n  const auto length = static_cast<size_t>(snprintf(nullptr, 0, format, t...));\n  std::vector<char> message(length + 1);  // +1 for the null byte\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)\n  snprintf(message.data(), message.size(), format, t...);\n#pragma GCC diagnostic pop\n  return message;\n}\n\ntemplate <typename... Args>\ninline std::vector<char> format_message(const std::string& format,\n                                        Args&&... args) {\n  const ScopedFpeState disable_fpes(false);\n  return allocate_message(\n      format.c_str(),\n      get_printable_type(stream_object_to_string(std::forward<Args>(args)))...);\n}\n}  // namespace detail\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Print an atomic message to stdout with C printf usage.\n *\n * Similar to Python, you can print any object that's streamable by passing it\n * in as an argument and using the formatter \"%s\". For example,\n * \\code\n * std::vector<double> a{0.8, 73, 9.8};\n * Parallel::printf(\"%s\\n\", a);\n * \\endcode\n */\ntemplate <typename... Args>\ninline void printf(const std::string& format, Args&&... args) {\n  detail::send_message(\n      false, detail::format_message(format, std::forward<Args>(args)...));\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Print an atomic message to stderr with C printf usage.\n *\n * See Parallel::printf for details.\n */\ntemplate <typename... Args>\ninline void printf_error(const std::string& format, Args&&... args) {\n  detail::send_message(\n      true, detail::format_message(format, std::forward<Args>(args)...));\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Print an atomic message to a file with C printf usage.\n *\n * See Parallel::printf for details.\n */\ntemplate <typename... Args>\ninline void fprintf(const std::string& file, const std::string& format,\n                    Args&&... args) {\n  detail::send_message_to_file(\n      file, detail::format_message(format, std::forward<Args>(args)...));\n}\n\n/// Chare outputting all Parallel::printf results.\nclass PrinterChare : public CBase_PrinterChare {\n public:\n  PrinterChare() = default;\n  explicit PrinterChare(CkMigrateMessage* /*msg*/) {}\n\n  /// Prints a message to stdout or stderr.\n  static void print(bool error, const std::vector<char>& message);\n\n  /// Prints a message to a file.\n  static void print_to_file(const std::string& file,\n                            const std::vector<char>& message);\n\n  static void register_with_charm();\n\n  void pup(PUP::er& /*p*/) override {}\n};\n\n// Charm readonly variables set in Main.\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nextern CProxy_PrinterChare printer_chare;\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nextern bool printer_chare_is_set;\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Protocols/ArrayElementsAllocator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel::protocols {\n/*!\n * \\brief Conforming types implement a strategy to create elements for array\n * parallel components\n *\n * Conforming classes must provide the following type aliases:\n *\n * - `array_allocation_tags<ParallelComponent>`: A `tmpl::list` of tags that are\n *   needed to perform the allocation. These tags will be parsed from input-file\n *   options (see \\ref dev_guide_parallelization_parallel_components). The array\n *   parallel component will be passed in as a template parameter.\n *\n * Conforming classes must implement the following static member functions:\n *\n * - `apply<ParallelComponent>`: This function is responsible for creating the\n *   array elements. It has the same signature as the `allocate_array` function\n *   (see \\ref dev_guide_parallelization_parallel_components), but takes the\n *   array parallel component as an additional first template parameter.\n *\n * See `elliptic::DefaultElementsAllocator` for an example implementation of\n * this protocol.\n */\nstruct ArrayElementsAllocator {\n  template <typename ConformingType>\n  struct test {};\n};\n}  // namespace Parallel::protocols\n"
  },
  {
    "path": "src/Parallel/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ArrayElementsAllocator.hpp\n  ElementRegistrar.hpp\n  RegistrationMetavariables.hpp\n  )\n"
  },
  {
    "path": "src/Parallel/Protocols/ElementRegistrar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Parallel::protocols {\n/*!\n * \\brief Conforming types register and deregister array elements with other\n * parallel components\n *\n * For example, array elements may have to register and deregister with\n * interpolators or observers when the elements get created and destroyed during\n * AMR or migrated during load balancing. They may do so by sending messages to\n * the parallel components that notify of the creation and destruction of the\n * array elements.\n *\n * Conforming classes have the following static member functions:\n *\n * ```cpp\n * static void perform_registration<ParallelComponent>(const\n *   db::DataBox<DbTagList>& box, Parallel::GlobalCache<Metavariables>& cache,\n *   const ArrayIndex& array_index)\n *\n * static void perform_deregistration<ParallelComponent>(const\n *   db::DataBox<DbTagList>& box, Parallel::GlobalCache<Metavariables>& cache,\n *   const ArrayIndex& array_index)\n * ```\n *\n * Here is an example implementation of this protocol:\n *\n * \\snippet RegistrationHelpers.hpp element_registrar_example\n */\nstruct ElementRegistrar {\n  template <typename ConformingType>\n  struct test {};\n};\n}  // namespace Parallel::protocols\n"
  },
  {
    "path": "src/Parallel/Protocols/RegistrationMetavariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Parallel/Protocols/ElementRegistrar.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel::protocols {\n/*!\n * \\brief Conforming types provide compile-time information for registering and\n * deregistering array elements with other parallel components\n *\n * A class conforming to this protocol is placed in the metavariables to provide\n * a list of element registrars for each array parallel component. The element\n * registrars in the list must conform to the\n * `Parallel::protocols::ElementRegistrar` protocol.\n *\n * The class conforming to this protocol must provide the following type alias:\n *\n * - `element_registrars`: A `tmpl::map` from parallel components to their list\n *   of element registrars. The element registrars in the typelist must conform\n *   to the `Parallel::protocols::ElementRegistrar` protocol.\n *\n * Here is an example implementation of this protocol:\n *\n * \\snippet Test_InitializeParent.cpp registration_metavariables\n *\n * \\note We may consider retrieving the list of element registrars directly from\n * the `Parallel::Phase::Registration` phase of the parallel component instead\n * of requiring the metavariables to provide a separate list.\n */\nstruct RegistrationMetavariables {\n  template <typename ConformingType>\n  struct test {\n    using element_registrars = typename ConformingType::element_registrars;\n  };\n};\n}  // namespace Parallel::protocols\n"
  },
  {
    "path": "src/Parallel/Reduction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/Reduction.hpp\"\n\nnamespace Parallel {\nNoSection& no_section() {\n  static NoSection local_no_section{};\n  return local_no_section;\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Reduction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <tuple>\n\n#include \"Parallel/CharmRegistration.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Section.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace Parallel {\n/// \\cond\ntemplate <class... Ts>\nstruct ReductionData;\n/// \\endcond\n\nnamespace detail {\n/*!\n * \\ingroup ParallelGroup\n * \\brief Convert a `ReductionData` to a `CkReductionMsg`. Used in custom\n * reducers.\n */\ntemplate <class... Ts>\nCkReductionMsg* new_reduction_msg(ReductionData<Ts...>& reduction_data) {\n  return CkReductionMsg::buildNew(static_cast<int>(reduction_data.size()),\n                                  reduction_data.packed().get());\n}\n}  // namespace detail\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief The data to be reduced, and invokables to be called whenever two\n * reduction messages are combined and after the reduction has been completed.\n *\n * `InvokeCombine` is a binary invokable that maps `(T current_state, T element)\n * -> T`, where the `current_state` is the result of reductions so far. The\n * `InvokeFinal` is an n-ary that takes as its first argument a `T\n * result_of_reduction` and is invoked once after the reduction is completed.\n * The additional arguments correspond to the resultant data of earlier\n * `ReductionDatum` template parameters in the `ReductionData`, and are\n * identified via the `InvokeFinalExtraArgsIndices`, which must be a\n * `std::index_sequence`. Specifically, say you want the third\n * `ReductionDatum`'s `InvokeFinal` to be passed the first `ReductionDatum` then\n * `std::index_sequence<0>` would be passed for `InvokeFinalExtraArgsIndices`.\n * Here is an example of computing the RMS error of the evolved variables `u`\n * and `v`:\n *\n * \\snippet Test_AlgorithmReduction.cpp contribute_to_rms_reduction\n *\n * with the receiving action:\n *\n * \\snippet Test_AlgorithmReduction.cpp reduce_rms_action\n */\ntemplate <class T, class InvokeCombine, class InvokeFinal = funcl::Identity,\n          class InvokeFinalExtraArgsIndices = std::index_sequence<>>\nstruct ReductionDatum {\n  using value_type = T;\n  using invoke_combine = InvokeCombine;\n  using invoke_final = InvokeFinal;\n  using invoke_final_extra_args_indices = InvokeFinalExtraArgsIndices;\n  T value;\n};\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Used for reducing a possibly heterogeneous collection of types in a\n * single reduction call\n */\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\nstruct ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                    InvokeFinalExtraArgsIndices>...> {\n  static_assert(sizeof...(Ts) > 0,\n                \"Must be reducing at least one piece of data.\");\n  static constexpr size_t pack_size() { return sizeof...(Ts); }\n  using datum_list = tmpl::list<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                               InvokeFinalExtraArgsIndices>...>;\n\n  explicit ReductionData(ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                        InvokeFinalExtraArgsIndices>... args);\n\n  explicit ReductionData(Ts... args);\n\n  ReductionData() = default;\n  ReductionData(const ReductionData& /*rhs*/) = default;\n  ReductionData& operator=(const ReductionData& /*rhs*/) = default;\n  ReductionData(ReductionData&& /*rhs*/) = default;\n  ReductionData& operator=(ReductionData&& /*rhs*/) = default;\n  ~ReductionData() = default;\n\n  explicit ReductionData(CkReductionMsg* const message) {\n    PUP::fromMem creator(message->getData());\n    creator | *this;\n  }\n\n  static CkReductionMsg* combine(int number_of_messages, CkReductionMsg** msgs);\n\n  ReductionData& combine(ReductionData&& t) {\n    ReductionData::combine_helper(this, std::move(t),\n                                  std::make_index_sequence<sizeof...(Ts)>{});\n    return *this;\n  }\n\n  ReductionData& finalize() {\n    invoke_final_loop_over_tuple(std::make_index_sequence<sizeof...(Ts)>{});\n    return *this;\n  }\n\n  /// \\cond\n  // clang-tidy: non-const reference\n  void pup(PUP::er& p) { p | data_; }  // NOLINT\n\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  std::unique_ptr<char[]> packed();\n\n  size_t size();\n\n  const std::tuple<Ts...>& data() const { return data_; }\n\n  std::tuple<Ts...>& data() { return data_; }\n\n private:\n  template <size_t... Is>\n  static void combine_helper(gsl::not_null<ReductionData*> reduced,\n                             ReductionData&& current,\n                             std::index_sequence<Is...> /*meta*/);\n\n  template <size_t I, class InvokeFinal, size_t... Js>\n  void invoke_final_helper(std::index_sequence<Js...> /*meta*/);\n\n  template <size_t... Is>\n  void invoke_final_loop_over_tuple(std::index_sequence<Is...> /*meta*/);\n\n  std::tuple<Ts...> data_;\n  /// \\endcond\n};\n\n/// \\cond\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\nReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                             InvokeFinalExtraArgsIndices>...>::\n    ReductionData(ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                 InvokeFinalExtraArgsIndices>... args)\n    : data_(std::move(args.value)...) {}\n\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\nReductionData<\n    ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                   InvokeFinalExtraArgsIndices>...>::ReductionData(Ts... args)\n    : data_(std::move(args)...) {}\n\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\nCkReductionMsg* ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                             InvokeFinalExtraArgsIndices>...>::\n    combine(const int number_of_messages, CkReductionMsg** const msgs) {\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  ReductionData reduced(msgs[0]);\n  for (int msg_id = 1; msg_id < number_of_messages; ++msg_id) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    ReductionData current(msgs[msg_id]);\n    ReductionData::combine_helper(&reduced, std::move(current),\n                                  std::make_index_sequence<sizeof...(Ts)>{});\n  }\n  return detail::new_reduction_msg(reduced);\n}\n\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\n// NOLINTNEXTLINE(modernize-avoid-c-arrays)\nstd::unique_ptr<char[]>\nReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                             InvokeFinalExtraArgsIndices>...>::packed() {\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  auto result = std::make_unique<char[]>(size());\n  PUP::toMem packer(result.get());\n  packer | *this;\n  return result;\n}\n\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\nsize_t ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                    InvokeFinalExtraArgsIndices>...>::size() {\n  PUP::sizer size_pup;\n  size_pup | *this;\n  return size_pup.size();\n}\n\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\ntemplate <size_t... Is>\nvoid ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                  InvokeFinalExtraArgsIndices>...>::\n    combine_helper(const gsl::not_null<ReductionData*> reduced,\n                   ReductionData&& current,\n                   std::index_sequence<Is...> /*meta*/) {\n  EXPAND_PACK_LEFT_TO_RIGHT((std::get<Is>(reduced->data_) = InvokeCombines{}(\n                                 std::move(std::get<Is>(reduced->data_)),\n                                 std::move(std::get<Is>(current.data_)))));\n}\n\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\ntemplate <size_t I, class InvokeFinal, size_t... Js>\nvoid ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                  InvokeFinalExtraArgsIndices>...>::\n    invoke_final_helper(std::index_sequence<Js...> /*meta*/) {\n  std::get<I>(data_) = InvokeFinal{}(std::move(std::get<I>(data_)),\n                                     std::as_const(std::get<Js>(data_))...);\n}\n\ntemplate <class... Ts, class... InvokeCombines, class... InvokeFinals,\n          class... InvokeFinalExtraArgsIndices>\ntemplate <size_t... Is>\nvoid ReductionData<ReductionDatum<Ts, InvokeCombines, InvokeFinals,\n                                  InvokeFinalExtraArgsIndices>...>::\n    invoke_final_loop_over_tuple(std::index_sequence<Is...> /*meta*/) {\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      invoke_final_helper<Is, InvokeFinals>(InvokeFinalExtraArgsIndices{}));\n}\n/// \\endcond\n\n/// Can be used instead of a `Parallel::Section` when no section is desired.\n///\n/// \\see Parallel::contribute_to_reduction\n/// @{\nstruct NoSection {};\nNoSection& no_section();\n/// @}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Perform a reduction from the `sender_component` (typically your own\n * parallel component) to the `target_component`, performing the `Action` upon\n * receiving the reduction.\n *\n * \\par Section reductions\n * This function supports section reductions (see `Parallel::Section`). Pass\n * the `Parallel::Section` as the \\p section argument, or pass\n * `Parallel::no_section()` to perform a reduction over the entire parallel\n * component (default). Here's an example of a section reduction:\n *\n * \\snippet Test_SectionReductions.cpp section_reduction\n *\n * \\warning Section reductions currently don't support migrating elements, i.e.\n * either load-balancing or restoring a checkpoint to a different number of PEs.\n * Support for migrating elements may require [updating the \"section\n * cookie\"](https://charm.readthedocs.io/en/latest/charm++/manual.html#section-operations-with-migrating-elements).\n * One possibility to update the section cookie is to broadcast a CkMulticast\n * message to the section elements and invoke `CkGetSectionInfo` within the\n * message.\n */\ntemplate <class Action, class SenderProxy, class TargetProxy, class... Ts,\n          class SectionType = NoSection>\nvoid contribute_to_reduction(ReductionData<Ts...> reduction_data,\n                             const SenderProxy& sender_component,\n                             const TargetProxy& target_component,\n                             [[maybe_unused]] const gsl::not_null<SectionType*>\n                                 section = &no_section()) {\n  (void)Parallel::charmxx::RegisterReducerFunction<\n      &ReductionData<Ts...>::combine>::registrar;\n  CkCallback callback(\n      TargetProxy::index_t::template redn_wrapper_reduction_action<\n          Action, std::decay_t<ReductionData<Ts...>>>(nullptr),\n      target_component);\n  const auto& charm_reducer_function =\n      Parallel::charmxx::charm_reducer_functions.at(\n          std::hash<Parallel::charmxx::ReducerFunctions>{}(\n              &ReductionData<Ts...>::combine));\n  if constexpr (std::is_same_v<SectionType, NoSection>) {\n    if constexpr (is_array_element_proxy<SenderProxy>::value) {\n      Parallel::local(sender_component)\n          ->contribute(static_cast<int>(reduction_data.size()),\n                       reduction_data.packed().get(), charm_reducer_function,\n                       callback);\n    } else {\n      Parallel::local_branch(sender_component)\n          ->contribute(static_cast<int>(reduction_data.size()),\n                       reduction_data.packed().get(), charm_reducer_function,\n                       callback);\n    }\n  } else {\n    static_assert(\n        tt::is_a_v<Section, SectionType>,\n        \"Either pass a 'Parallel::Section' for the 'section' argument or \"\n        \"'Parallel::NoSection{}'. For the latter you can just omit the \"\n        \"argument.\");\n    using SectionProxy = typename SectionType::cproxy_section;\n    // Retrieve the section cookie that keeps track of the reduction\n    auto& section_cookie = section->cookie();\n    // Ideally we would update the section cookie here using\n    // `CkGetSectionInfo()`. However, that only works with CkMulticast messages\n    // (see\n    // https://github.com/UIUC-PPL/charm/blob/99cda7a11108f503b89dc847b58e62bc74267440/src/ck-core/ckmulticast.C#L1180).\n    // Dispatching a message to the `sender_component` doesn't help because\n    // sending a message to a single element doesn't go through CkMulticast. Not\n    // updating the section cookie seems to work, but might break when elements\n    // migrate (see\n    // https://charm.readthedocs.io/en/latest/charm++/manual.html#section-operations-with-migrating-elements).\n    // In that case we can possibly broadcast a CkMulticast message to all\n    // elements to update their section cookies.\n    SectionProxy::contribute(static_cast<int>(reduction_data.size()),\n                             reduction_data.packed().get(),\n                             charm_reducer_function, section_cookie, callback);\n  }\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/ReductionDeclare.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Forward-declares the public-facing Reduction classes. This file is needed to\n/// avoid circular dependencies in Main.ci, which uses these classes in its\n/// declarations.\n\n#pragma once\n\n/// \\cond\nnamespace Parallel {\ntemplate <class T, class InvokeCombine, class InvokeFinal,\n          class InvokeFinalExtraArgsIndices>\nstruct ReductionDatum;\n\ntemplate <class... Ts>\nstruct ReductionData;\n}  // namespace Parallel\n/// \\endcond\n"
  },
  {
    "path": "src/Parallel/ResourceInfo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <ios>\n#include <optional>\n#include <pup.h>\n#include <set>\n#include <sstream>\n#include <string>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingletonDeclarations.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n\n/// \\cond\nnamespace Parallel::Tags {\ntemplate <typename Component>\nstruct SingletonInfo;\nstruct AvoidGlobalProc0;\ntemplate <typename Metavariables>\nstruct ResourceInfo;\n}  // namespace Parallel::Tags\n/// \\endcond\n\nnamespace Parallel {\n/*!\n * \\ingroup ParallelGroup\n * \\brief Holds resource info for a single singleton component\n *\n * \\details Holds what proc the singleton is to be placed on and whether that\n * proc should be exclusive, i.e. no array component elements or other\n * singletons placed on that proc. Instead of specifying a proc, the proc can be\n * chosen automatically by using the `Options::Auto` option.\n *\n * The template parameter `Component` is only used to identify which singleton\n * component this SingletonInfoHolder belongs to.\n */\ntemplate <typename Component>\nstruct SingletonInfoHolder {\n  struct Proc {\n    using type = Options::Auto<int>;\n    static constexpr Options::String help = {\n        \"Proc to put singleton on. This can be determined automatically if \"\n        \"desired by specifying 'Auto' (without quotes).\"};\n  };\n\n  struct Exclusive {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Reserve this proc for this singleton. No array component elements or \"\n        \"other singleton components will be placed on this proc.\"};\n  };\n\n  using options = tmpl::list<Proc, Exclusive>;\n  static constexpr Options::String help = {\n      \"Resource options for a single singleton.\"};\n\n  SingletonInfoHolder(std::optional<int> input_proc, const bool input_exclusive,\n                      const Options::Context& context = {})\n      : exclusive_(input_exclusive) {\n    // If there is no value, we don't need to error so use 0 as a comparator\n    // in both cases\n    if (input_proc.value_or(0) < 0) {\n      PARSE_ERROR(\n          context,\n          \"Proc must be a non-negative integer. Please choose another proc.\");\n    }\n\n    proc_ = input_proc.has_value()\n                ? std::optional<size_t>(static_cast<size_t>(input_proc.value()))\n                : std::nullopt;\n  }\n\n  SingletonInfoHolder() = default;\n  SingletonInfoHolder(const SingletonInfoHolder& /*rhs*/) = default;\n  SingletonInfoHolder& operator=(const SingletonInfoHolder& /*rhs*/) = default;\n  SingletonInfoHolder(SingletonInfoHolder&& /*rhs*/) = default;\n  SingletonInfoHolder& operator=(SingletonInfoHolder&& /*rhs*/) = default;\n  ~SingletonInfoHolder() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | proc_;\n    p | exclusive_;\n  };\n\n  /// Proc that singleton is to be placed on. If the optional is a std::nullopt,\n  /// then the proc should be chosen automatically.\n  std::optional<size_t> proc() const { return proc_; }\n\n  /// Whether or not the singleton wants to be exclusive on the proc.\n  bool is_exclusive() const { return exclusive_; }\n\n private:\n  template <typename ParallelComponent>\n  friend bool operator==(const SingletonInfoHolder<ParallelComponent>& lhs,\n                         const SingletonInfoHolder<ParallelComponent>& rhs);\n  // We use size_t here because we want a non-negative integer, but we use int\n  // in the option because we want to protect against negative numbers. And a\n  // negative size_t is actually a really large value (it wraps around)\n  std::optional<size_t> proc_{std::nullopt};\n  bool exclusive_{false};\n};\n\ntemplate <typename ParallelComponent>\nbool operator==(const SingletonInfoHolder<ParallelComponent>& lhs,\n                const SingletonInfoHolder<ParallelComponent>& rhs) {\n  return lhs.proc_ == rhs.proc_ and lhs.exclusive_ == rhs.exclusive_;\n}\n\ntemplate <typename ParallelComponent>\nbool operator!=(const SingletonInfoHolder<ParallelComponent>& lhs,\n                const SingletonInfoHolder<ParallelComponent>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename ParallelComponents>\nstruct SingletonPack;\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Holds options for a group of singleton components.\n *\n * \\details The info for each singleton in the `ParallelComponents` template\n *  pack is stored in an individual `Parallel::SingletonInfoHolder`.\n *\n * You can pass `Auto` as an option for each singleton in an input file and each\n * singleton will be constructed as a default `Parallel::SingletonInfoHolder`.\n */\ntemplate <typename... ParallelComponents>\nstruct SingletonPack<tmpl::list<ParallelComponents...>> {\n private:\n  static_assert((Parallel::is_singleton_v<ParallelComponents> and ...),\n                \"At least one of the parallel components passed to \"\n                \"SingletonPack is not a Singleton.\");\n  using component_list = tmpl::list<ParallelComponents...>;\n\n  template <typename Component>\n  struct LocalTag {\n    using type = SingletonInfoHolder<Component>;\n  };\n  using local_tags =\n      tmpl::transform<component_list, tmpl::bind<LocalTag, tmpl::_1>>;\n\n public:\n  template <typename Component>\n  struct SingletonOption {\n    using type = Options::Auto<SingletonInfoHolder<Component>>;\n    static std::string name() { return pretty_type::name<Component>(); }\n    static constexpr Options::String help = {\n        \"Resource options for a specific singleton.\"};\n  };\n\n  using options =\n      tmpl::transform<component_list, tmpl::bind<SingletonOption, tmpl::_1>>;\n  static constexpr Options::String help = {\n      \"Resource options for all singletons.\"};\n\n  SingletonPack(\n      const std::optional<\n          SingletonInfoHolder<ParallelComponents>>&... singleton_info_holders,\n      const Options::Context& /*context*/ = {})\n      : procs_(tuples::tagged_tuple_from_typelist<local_tags>(\n            singleton_info_holders.value_or(\n                SingletonInfoHolder<ParallelComponents>{})...)) {}\n\n  SingletonPack() = default;\n  SingletonPack(const SingletonPack& /*rhs*/) = default;\n  SingletonPack& operator=(const SingletonPack& /*rhs*/) = default;\n  SingletonPack(SingletonPack&& /*rhs*/) = default;\n  SingletonPack& operator=(SingletonPack&& /*rhs*/) = default;\n  ~SingletonPack() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | procs_; };\n\n  /// Get a const reference to the SingletonInfoHolder for the `Component`\n  /// singleton\n  template <typename Component>\n  const auto& get() const {\n    return tuples::get<LocalTag<Component>>(procs_);\n  }\n\n private:\n  template <typename... Components>\n  friend bool operator==(const SingletonPack<tmpl::list<Components...>>& lhs,\n                         const SingletonPack<tmpl::list<Components...>>& rhs);\n\n  tuples::tagged_tuple_from_typelist<local_tags> procs_{};\n};\n\ntemplate <typename... Components>\nbool operator==(const SingletonPack<tmpl::list<Components...>>& lhs,\n                const SingletonPack<tmpl::list<Components...>>& rhs) {\n  return lhs.procs_ == rhs.procs_;\n}\n\ntemplate <typename... Components>\nbool operator!=(const SingletonPack<tmpl::list<Components...>>& lhs,\n                const SingletonPack<tmpl::list<Components...>>& rhs) {\n  return not(lhs == rhs);\n}\n\nnamespace detail {\ntemplate <typename Metavariables>\nusing singleton_components =\n    tmpl::filter<typename Metavariables::component_list,\n                 Parallel::is_singleton<tmpl::_1>>;\n}  // namespace detail\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Holds resource info for all singletons and for avoiding placing array\n * elements/singletons on the global proc 0.\n *\n * \\details This can be used for placing all singletons in an executable.\n *\n * If you have no singletons, you'll need the following block in the input file\n * (where you can set the value of AvoidGlobalProc0 to true or false):\n *\n * \\code {.yaml}\n * ResourceInfo:\n *   AvoidGlobalProc0: true\n * \\endcode\n *\n * If you have singletons, but do not want to assign any of them to a specific\n * proc or be exclusive on a proc, you'll need the following block in the input\n * file (where you can set the value of AvoidGlobalProc0 to true or false):\n *\n * \\code {.yaml}\n * ResourceInfo:\n *   AvoidGlobalProc0: true\n *   Singletons: Auto\n * \\endcode\n *\n * Otherwise, you will need to specify a block in the input file as below,\n * where you will need to specify the options for each singleton:\n *\n * \\code {.yaml}\n * ResourceInfo:\n *   AvoidGlobalProc0: true\n *   Singletons:\n *     MySingleton1:\n *       Proc: 2\n *       Exclusive: true\n *     MySingleton2: Auto\n * \\endcode\n *\n * where `MySingleton1` is the `pretty_type::name` of the singleton component\n * and the options for each singleton are described in\n * `Parallel::SingletonInfoHolder` (You can use `Auto` for each singleton that\n * you want to have it's proc determined automatically and be non-exclusive,\n * like `MySingleton2`).\n *\n * Several consistency checks are done during option parsing to avoid user\n * error. However, some checks can't be done during option parsing because the\n * number of nodes/procs is needed to determine if there is an inconsistency.\n * These checks are done during runtime, just before the map of singletons is\n * created.\n *\n * To automatically place singletons, we use a custom algorithm that will\n * distribute singletons evenly over the number of nodes, and evenly over the\n * procs on a node. This will help keep communication costs down by distributing\n * the workload over all of the communication cores (one communication core per\n * charm node), and ensure that our resources are being maximally utilized (i.e.\n * one core doesn't have all the singletons on it).\n *\n * Defining some terminology for singletons: `requested` means that a specific\n * processor was requested in the input file; `auto` means that the processor\n * should be chosen automatically; `exclusive` means that no other singletons or\n * array elements should be placed on this singleton's processor; `nonexclusive`\n * means that you *can* place other singletons or array elements on this\n * singleton's processor. The algorithm that distributes the singletons is as\n * follows:\n *\n * 1. Allocate all singletons that `requested` specific processors, both\n *    `exclusive` and `nonexclusive`. This is done during option parsing.\n * 2. Allocate `auto exclusive` singletons, distributing the total number of\n *    `exclusive` singletons (`auto` + `requested`) as evenly as possibly over\n *    the number of nodes. We say \"as evenly as possible\" because this depends\n *    on the `requested exclusive` singletons. For example, if we have 4 nodes\n *    and 5 cores per node, the number of `requested exclusive` singletons on\n *    each node is (0, 1, 4, 1), and we have 3 `auto exclusive` singletons to\n *    place, the best distribution of `exclusive` singletons we can achieve\n *    given our constraints is (2, 2, 4, 1). Clearly this is not the *most*\n *    evenly distributed the `exclusive` singletons could be. However, this *is*\n *    the most evenly distributed they could be given the starting distribution\n *    from the input file.\n * 3. Allocate `auto nonexclusive` singletons, distributing the total number of\n *    `nonexclusive` singletons (`auto` + `requested`): First, as evenly as\n *    possibly over the number of nodes. Then, on each node, distributing the\n *    singletons as evenly as possibly over the number of processors on that\n *    node. The same disclaimer about \"as evenly as possibly\" from the previous\n *    step applies here.\n *\n * The goal of this algorithm is to mimic, as best as possible, how a human\n * would distribute this workload. It isn't perfect, but is a significant\n * improvement over placing singletons on one proc after another starting from\n * global proc 0.\n */\ntemplate <typename Metavariables>\nstruct ResourceInfo {\n private:\n  using singletons = detail::singleton_components<Metavariables>;\n\n  template <typename Component>\n  struct LocalTag {\n    // exclusive, proc\n    using type = std::pair<bool, std::optional<size_t>>;\n  };\n  using local_tags =\n      tmpl::transform<singletons, tmpl::bind<LocalTag, tmpl::_1>>;\n\n public:\n  struct Singletons {\n    using type = Options::Auto<SingletonPack<singletons>>;\n    static constexpr Options::String help = {\n        \"Resource options for all singletons.\"};\n  };\n\n  struct AvoidGlobalProc0 {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether to avoid placing Array elements or singletons on global proc \"\n        \"0.\"};\n  };\n\n  using options = tmpl::push_front<\n      tmpl::conditional_t<tmpl::size<singletons>::value != 0,\n                          tmpl::list<Singletons>, tmpl::list<>>,\n      AvoidGlobalProc0>;\n\n  static constexpr Options::String help = {\n      \"Resource options for a simulation. This information will be used when \"\n      \"placing Array and Singleton parallel components on the requested \"\n      \"resources.\"};\n\n  /// The main constructor. All other constructors that take options will call\n  /// this one. This constructor holds all checks able to be done during option\n  /// parsing.\n  ResourceInfo(const bool avoid_global_proc_0,\n               const std::optional<SingletonPack<singletons>>& singleton_pack,\n               const Options::Context& context = {});\n\n  /// This constructor is used when only AvoidGlobalProc0 is specified, but no\n  /// SingletonInfoHolders are specified. Calls the main constructor with an\n  /// empty SingletonPack.\n  ResourceInfo(const bool avoid_global_proc_0,\n               const Options::Context& context = {});\n\n  ResourceInfo() = default;\n  ResourceInfo(const ResourceInfo& /*rhs*/) = default;\n  ResourceInfo& operator=(const ResourceInfo& /*rhs*/) = default;\n  ResourceInfo(ResourceInfo&& /*rhs*/) = default;\n  ResourceInfo& operator=(ResourceInfo&& /*rhs*/) = default;\n  ~ResourceInfo() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// Returns whether we should avoid placing array elements and singletons on\n  /// the global zeroth proc. Default `false`.\n  bool avoid_global_proc_0() const { return avoid_global_proc_0_; }\n\n  /// Return a SingletonInfoHolder corresponding to `Component`\n  template <typename Component>\n  auto get_singleton_info() const;\n\n  /// Returns a `std::unordered_set<size_t>` of processors that array components\n  /// should avoid placing elements on. This should be passed to the\n  /// `allocate_array` function of the array component\n  const std::unordered_set<size_t>& procs_to_ignore() const;\n\n  /// Returns a `std::set<size_t>` that has all processors available to put\n  /// elements on, meaning processors that aren't ignored.\n  const std::set<size_t>& procs_available_for_elements() const;\n\n  /// Returns the proc that the singleton `Component` should be placed on.\n  template <typename Component>\n  size_t proc_for() const;\n\n  /// \\brief Actually builds the singleton map and allocates all the singletons.\n  ///\n  /// \\details This could be done in the constructor, however, since we need the\n  /// number of nodes to do some sanity checks, it can't. If an executable is\n  /// run with the --check-options flag, we will be running on 1 proc and 1 node\n  /// so some of the checks done in this function would fail. Unfortunately,\n  /// that means the checks that require knowing the number of nodes now occur\n  /// at runtime instead of option parsing. This is why the\n  /// `singleton_map_has_been_set_` bool is necessary and why we check if this\n  /// function has been called in most other member functions.\n  ///\n  /// To avoid a cyclic dependency between the GlobalCache and ResourceInfo, we\n  /// template this function rather than explicitly use the GlobalCache because\n  /// the GlobalCache depends on ResourceInfo\n  ///\n  /// This function should only be called once.\n  template <typename Cache>\n  void build_singleton_map(const Cache& cache);\n\n private:\n  template <typename Metavars>\n  friend bool operator==(const ResourceInfo<Metavars>& lhs,\n                         const ResourceInfo<Metavars>& rhs);\n\n  void singleton_map_not_built() const {\n    ERROR(\n        \"The singleton map has not been built yet. You must call \"\n        \"build_singleton_map() before you call this function.\");\n  }\n  bool avoid_global_proc_0_{false};\n  bool singleton_map_has_been_set_{false};\n  // These are quantities that we will need for placing singletons which can be\n  // determined just by option parsing\n  size_t num_exclusive_singletons_{};\n  size_t num_procs_to_ignore_{};\n  size_t num_requested_exclusive_singletons_{};\n  size_t num_requested_nonexclusive_singletons_{};\n  std::unordered_multiset<size_t> requested_nonexclusive_procs_{};\n  // Procs that are exclusive. These may or may not be specifically requested\n  std::unordered_set<size_t> procs_to_ignore_{};\n  std::set<size_t> procs_available_for_elements_{};\n  // For each singleton (whether it has a SingletonInfo or not), maps whether\n  // it's exclusive and what proc it is on.\n  tuples::tagged_tuple_from_typelist<local_tags> singleton_map_{};\n};\n\ntemplate <typename Metavariables>\nResourceInfo<Metavariables>::ResourceInfo(\n    const bool avoid_global_proc_0,\n    const std::optional<SingletonPack<singletons>>& opt_singleton_pack,\n    const Options::Context& context)\n    : avoid_global_proc_0_(avoid_global_proc_0) {\n  if (avoid_global_proc_0_) {\n    procs_to_ignore_.insert(0);\n    ++num_procs_to_ignore_;\n  }\n\n  if constexpr (tmpl::size<singletons>::value > 0) {\n    const auto& singleton_pack =\n        opt_singleton_pack.value_or(SingletonPack<singletons>{});\n\n    // Procs that were specifically requested. These may or may not be exclusive\n    std::unordered_multiset<int> requested_procs{};\n\n    [[maybe_unused]] const auto parse_singletons = [this, &context,\n                                                    &singleton_pack,\n                                                    &requested_procs](\n                                                       const auto component_v) {\n      using component = tmpl::type_from<decltype(component_v)>;\n      auto& singleton_map = tuples::get<LocalTag<component>>(singleton_map_);\n\n      // This singleton has a SingletonInfoHolder associated with it. Get all\n      // the info necessary from it\n      if constexpr (tmpl::list_contains_v<singletons, component>) {\n        const auto& info_holder = singleton_pack.template get<component>();\n        // Assign proc. If a specific proc is requested, add it to a map. We'll\n        // check that exclusive singletons have unique procs once we've gone\n        // through everything once\n        const auto proc = info_holder.proc();\n        singleton_map.second = proc;\n\n        if (proc.has_value()) {\n          requested_procs.insert(*proc);\n        }\n\n        if (info_holder.is_exclusive()) {\n          // Check that no singleton has requested to be on proc 0 while\n          // AvoidGlobalProc0 is simultaneously true.\n          if (avoid_global_proc_0_ and proc.has_value() and *proc == 0) {\n            PARSE_ERROR(\n                context,\n                \"A singleton has requested to be exclusively on proc 0, \"\n                \"but the AvoidGlobalProc0 option is also set to true.\");\n          }\n\n          // This singleton is exclusive so set it.\n          singleton_map.first = true;\n          ++num_exclusive_singletons_;\n          ++num_procs_to_ignore_;\n          // If it requested a specific proc, ignore it when assigning the rest\n          // of the singletons\n          if (proc.has_value()) {\n            procs_to_ignore_.insert(static_cast<size_t>(*proc));\n            ++num_requested_exclusive_singletons_;\n          }\n        } else {\n          // This singleton is not exclusive.\n          singleton_map.first = false;\n          if (proc.has_value()) {\n            ++num_requested_nonexclusive_singletons_;\n            requested_nonexclusive_procs_.insert(static_cast<size_t>(*proc));\n          }\n        }\n      } else {\n        // This singleton doesn't have a SingletonInfoHolder so it automatically\n        // isn't exclusive and gets set assigned an automatic proc.\n        singleton_map.first = false;\n        // nullopt is a sentinel for auto\n        singleton_map.second = std::nullopt;\n      }\n    };\n\n    // Create a map between each singleton, whether it is exclusive, and which\n    // proc it wants to be on. Use nullopt as a sentinel for choosing the proc\n    // automatically.\n    tmpl::for_each<singletons>(parse_singletons);\n    [[maybe_unused]] const auto sanity_checks = [this, &context,\n                                                 &requested_procs](\n                                                    const auto component_v) {\n      using component = tmpl::type_from<decltype(component_v)>;\n      auto& singleton_map = tuples::get<LocalTag<component>>(singleton_map_);\n\n      const bool exclusive = singleton_map.first;\n      const auto proc = singleton_map.second;\n\n      // Check exclusive singletons that requested to be on a specific proc\n      // if any other singletons requested to be on the same proc (exclusive\n      // or not)\n      if (exclusive and proc.has_value() and requested_procs.count(*proc) > 1) {\n        PARSE_ERROR(context,\n                    \"Two singletons have requested to be on proc \"\n                        << proc.value()\n                        << \", but at least one of them has requested to be \"\n                           \"exclusively on this proc.\");\n      }\n    };\n\n    // Do some inter-singleton sanity checks to avoid inconsistencies\n    tmpl::for_each<singletons>(sanity_checks);\n  }\n}\n\ntemplate <typename Metavariables>\nResourceInfo<Metavariables>::ResourceInfo(const bool avoid_global_proc_0,\n                                          const Options::Context& context)\n    : ResourceInfo(avoid_global_proc_0, std::nullopt, context) {}\n\ntemplate <typename Metavariables>\nvoid ResourceInfo<Metavariables>::pup(PUP::er& p) {\n  p | avoid_global_proc_0_;\n  p | singleton_map_has_been_set_;\n  p | num_exclusive_singletons_;\n  p | num_procs_to_ignore_;\n  p | num_requested_exclusive_singletons_;\n  p | num_requested_nonexclusive_singletons_;\n  p | requested_nonexclusive_procs_;\n  p | procs_to_ignore_;\n  p | procs_available_for_elements_;\n  p | singleton_map_;\n}\n\ntemplate <typename Metavariables>\ntemplate <typename Component>\nauto ResourceInfo<Metavariables>::get_singleton_info() const {\n  if (not singleton_map_has_been_set_) {\n    singleton_map_not_built();\n  }\n\n  const auto& singleton_map = tuples::get<LocalTag<Component>>(singleton_map_);\n  return SingletonInfoHolder<Component>{\n      {static_cast<int>(*singleton_map.second)}, singleton_map.first};\n}\n\ntemplate <typename Metavariables>\nconst std::unordered_set<size_t>& ResourceInfo<Metavariables>::procs_to_ignore()\n    const {\n  if (not singleton_map_has_been_set_) {\n    singleton_map_not_built();\n  }\n  return procs_to_ignore_;\n}\n\ntemplate <typename Metavariables>\nconst std::set<size_t>&\nResourceInfo<Metavariables>::procs_available_for_elements() const {\n  if (not singleton_map_has_been_set_) {\n    singleton_map_not_built();\n  }\n  return procs_available_for_elements_;\n}\n\ntemplate <typename Metavariables>\ntemplate <typename Component>\nsize_t ResourceInfo<Metavariables>::proc_for() const {\n  if (not singleton_map_has_been_set_) {\n    singleton_map_not_built();\n  }\n  return *tuples::get<LocalTag<Component>>(singleton_map_).second;\n}\n\ntemplate <typename Metavars>\nbool operator==(const ResourceInfo<Metavars>& lhs,\n                const ResourceInfo<Metavars>& rhs) {\n  return lhs.avoid_global_proc_0_ == rhs.avoid_global_proc_0_ and\n         lhs.singleton_map_has_been_set_ == rhs.singleton_map_has_been_set_ and\n         lhs.num_exclusive_singletons_ == rhs.num_exclusive_singletons_ and\n         lhs.num_procs_to_ignore_ == rhs.num_procs_to_ignore_ and\n         lhs.num_requested_exclusive_singletons_ ==\n             rhs.num_requested_exclusive_singletons_ and\n         lhs.num_requested_nonexclusive_singletons_ ==\n             rhs.num_requested_nonexclusive_singletons_ and\n         lhs.requested_nonexclusive_procs_ ==\n             rhs.requested_nonexclusive_procs_ and\n         lhs.procs_to_ignore_ == rhs.procs_to_ignore_ and\n         lhs.procs_available_for_elements_ ==\n             rhs.procs_available_for_elements_ and\n         lhs.singleton_map_ == rhs.singleton_map_;\n}\n\ntemplate <typename Metavars>\nbool operator!=(const ResourceInfo<Metavars>& lhs,\n                const ResourceInfo<Metavars>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Metavariables>\ntemplate <typename Cache>\nvoid ResourceInfo<Metavariables>::build_singleton_map(const Cache& cache) {\n  const size_t num_procs = Parallel::number_of_procs<size_t>(cache);\n  const size_t num_nodes = Parallel::number_of_nodes<size_t>(cache);\n\n  // We don't do procs_to_ignore_.size() here because the auto singletons who\n  // requested to be exclusive haven't been assigned yet so their procs haven't\n  // been added to procs_to_ignore_\n  if (num_procs_to_ignore_ >= num_procs) {\n    ERROR(\n        \"The total number of cores requested is less than or equal to the \"\n        \"number of cores that requested to be exclusive, i.e. without \"\n        \"array elements or multiple singletons. The array elements have \"\n        \"nowhere to be placed. Number of cores requested: \"\n        << num_procs << \". Number of cores that requested to be exclusive: \"\n        << num_procs_to_ignore_ << \".\");\n  }\n\n  // Check if any singletons that requested to be on specific proc requested to\n  // be on a proc beyond the last proc.\n  tmpl::for_each<singletons>([this, &num_procs](const auto component_v) {\n    using component = tmpl::type_from<decltype(component_v)>;\n    auto& singleton_map = tuples::get<LocalTag<component>>(singleton_map_);\n    const auto proc = singleton_map.second;\n\n    if (proc.has_value() and *proc > num_procs - 1) {\n      ERROR(\"Singleton \" << pretty_type::name<component>()\n                         << \" requested to be placed on proc \" << *proc\n                         << \", but that proc is beyond the last proc \"\n                         << num_procs - 1 << \".\");\n    }\n  });\n\n  // At this point, all requested singletons have been allocated on their\n  // desired procs. This leaves just the auto singletons left, both exclusive\n  // and non-exclusive.\n\n  // First allocate auto exclusive singletons\n  // This first vector will keep track of the total number of singletons on each\n  // node so we can spread them out evenly\n  std::vector<size_t> singletons_on_each_node(num_nodes, 0_st);\n  // This second vector keeps track of only the auto exclusive singletons on\n  // each node\n  std::vector<size_t> auto_exclusive_singletons_on_each_node(num_nodes, 0_st);\n  // Populate requested exclusive singletons on each node with input options. We\n  // couldn't have done this in the constructor because we didn't know how many\n  // nodes there were or how many procs were on each node. We'll do the\n  // non-exclusive ones later.\n  tmpl::for_each<singletons>(\n      [this, &cache, &singletons_on_each_node](const auto component_v) {\n        using component = tmpl::type_from<decltype(component_v)>;\n        auto& singleton_map = tuples::get<LocalTag<component>>(singleton_map_);\n        const bool exclusive = singleton_map.first;\n        const auto proc = singleton_map.second;\n\n        if (exclusive and proc.has_value()) {\n          ++singletons_on_each_node[Parallel::node_of<size_t>(*proc, cache)];\n        }\n      });\n\n  size_t remaining_auto_exclusive_singletons =\n      num_exclusive_singletons_ - num_requested_exclusive_singletons_;\n  // Start with the min number of singletons on a node as our baseline. Then,\n  // while we still have auto exclusive singletons to place, we loop over all\n  // nodes and place singletons on nodes with this minimum number. Once all\n  // nodes have at least this minimum number, we increment the minimum number\n  // and loop over the nodes again\n  size_t min_num_singletons_on_a_node = *std::min_element(\n      singletons_on_each_node.begin(), singletons_on_each_node.end());\n  while (remaining_auto_exclusive_singletons > 0) {\n    for (size_t i = 0; i < num_nodes; i++) {\n      // If this node has more than the minimum number of singletons on it, skip\n      // it for now\n      if (singletons_on_each_node[i] > min_num_singletons_on_a_node) {\n        continue;\n      }\n      // Since nodes can have different number of procs, we check that we\n      // haven't exhausted the number of procs on this node. This check is ok\n      // right now because we haven't included any nonexclusive singletons in\n      // singletons_on_each_node yet.\n      if (not(singletons_on_each_node[i] <\n              Parallel::procs_on_node<size_t>(i, cache))) {\n        continue;\n      }\n\n      ++singletons_on_each_node[i];\n      ++auto_exclusive_singletons_on_each_node[i];\n      --remaining_auto_exclusive_singletons;\n\n      // We need to break out of both loops here. Use a goto.\n      if (remaining_auto_exclusive_singletons == 0) {\n        goto break_auto_exclusive_loops;\n      }\n    }  // for (size_t i = 0; i < num_nodes; i++)\n\n    ++min_num_singletons_on_a_node;\n  }  // while (remaining_auto_exclusive_singletons > 0)\nbreak_auto_exclusive_loops:\n\n  ASSERT(remaining_auto_exclusive_singletons == 0,\n         \"Not all exclusive singletons have been allocated. The remaining \"\n         \"number of singletons to be allocated is \"\n             << remaining_auto_exclusive_singletons << \".\");\n\n  // Actually allocate the auto exclusive singletons\n  size_t current_node = 0;\n  tmpl::for_each<singletons>([this, &cache, &current_node,\n                              &auto_exclusive_singletons_on_each_node](\n                                 const auto component_v) {\n    using component = tmpl::type_from<decltype(component_v)>;\n    auto& singleton_map = tuples::get<LocalTag<component>>(singleton_map_);\n    const bool exclusive = singleton_map.first;\n    const auto int_proc = singleton_map.second;\n\n    // Only allocating auto exclusive at the moment\n    if (exclusive and not int_proc.has_value()) {\n      while (auto_exclusive_singletons_on_each_node[current_node] == 0) {\n        ++current_node;\n      }\n\n      size_t proc = Parallel::first_proc_on_node<size_t>(current_node, cache);\n      // Don't place two exclusive singletons on the same proc, but also if\n      // a singleton requested a specific proc, whether or not it is\n      // exclusive, we can't place an exclusive singleton on that proc. That\n      // defeats the whole purpose of requesting the specific proc...\n      while (procs_to_ignore_.find(proc) != procs_to_ignore_.end() or\n             requested_nonexclusive_procs_.count(proc) > 0) {\n        ++proc;\n      }\n\n      singleton_map.second = proc;\n      procs_to_ignore_.insert(proc);\n\n      --auto_exclusive_singletons_on_each_node[current_node];\n    }\n  });\n\n  ASSERT(alg::accumulate(auto_exclusive_singletons_on_each_node, 0_st) == 0,\n         \"Not all auto exclusive singletons have been allocated. The remaining \"\n         \"number of auto exclusive singletons to be allocated is \"\n             << alg::accumulate(auto_exclusive_singletons_on_each_node, 0_st));\n\n  // procs_to_ignore_ is now complete. Now construct\n  // procs_available_for_elements_\n  for (size_t i = 0; i < num_procs; i++) {\n    if (procs_to_ignore_.find(i) == procs_to_ignore_.end()) {\n      procs_available_for_elements_.insert(i);\n    }\n  }\n\n  // At this point, all auto exclusive singletons have been allocated. Now the\n  // only singletons left are auto non-exclusive. We use vectors of\n  // std::optional<size_t> here as sentinels for procs which should be avoided.\n  // A nullopt means that the proc shouldn't have auto nonexclusive singletons\n  // on it. When we have lots of cores to run on (hundreds of thousands or even\n  // millions), these vectors will take up a non-negligible amount of memory.\n  // However, we only need to do this once an executable at the very beginning\n  // so it shouldn't really matter\n  std::vector<std::optional<size_t>> nonexclusive_singletons_on_each_proc(\n      num_procs, std::optional<size_t>(0_st));\n  // This vector has the default be nullopt rather than 0, because this will\n  // only be used when we actually place singletons. We only care which procs\n  // have singletons, which will usually be a small subset of the total procs.\n  std::vector<std::optional<size_t>> auto_nonexclusive_singletons_on_each_proc(\n      num_procs, std::nullopt);\n  for (const size_t proc : procs_to_ignore_) {\n    nonexclusive_singletons_on_each_proc[proc] = std::nullopt;\n  }\n  // Now we add in the requested nonexclusive to the total number of singletons\n  // per node\n  for (const auto& proc : requested_nonexclusive_procs_) {\n    ++*nonexclusive_singletons_on_each_proc[proc];\n    ++singletons_on_each_node[Parallel::node_of<size_t>(proc, cache)];\n  }\n\n  size_t remaining_auto_nonexclusive_singletons =\n      tmpl::size<singletons>::value - num_exclusive_singletons_ -\n      num_requested_nonexclusive_singletons_;\n\n  // This serves the same purpose as before\n  min_num_singletons_on_a_node = *std::min_element(\n      singletons_on_each_node.begin(), singletons_on_each_node.end());\n  while (remaining_auto_nonexclusive_singletons > 0) {\n    for (size_t i = 0; i < num_nodes; i++) {\n      const int first_proc = Parallel::first_proc_on_node<int>(i, cache);\n      const int procs_on_node = Parallel::procs_on_node<int>(i, cache);\n      const int first_proc_next_node = first_proc + procs_on_node;\n\n      auto first_proc_iter =\n          std::next(nonexclusive_singletons_on_each_proc.begin(), first_proc);\n      auto first_proc_next_node_iter = std::next(\n          nonexclusive_singletons_on_each_proc.begin(), first_proc_next_node);\n\n      // Get the proc on this node with the minimum number of singletons. This\n      // serves the same purpose as min_num_singletons_on_a_node except now for\n      // procs on a specific node\n      auto& min_num_singletons_on_a_proc_opt =\n          *std::min_element(first_proc_iter, first_proc_next_node_iter,\n                            [](const auto& a, const auto& b) {\n                              if (a.has_value() and b.has_value()) {\n                                return a.value() < b.value();\n                              } else {\n                                return a.has_value();\n                              }\n                            });\n\n      // Check if this node can accommodate more singletons. Do two checks:\n      // 1. This node doesn't have more than the minimum number of singletons\n      // 2. That this node isn't filled up with exclusive singletons (nullopt =\n      //    all procs on this node are taken)\n      if (singletons_on_each_node[i] > min_num_singletons_on_a_node or\n          not min_num_singletons_on_a_proc_opt.has_value()) {\n        continue;\n      }\n\n      // At this point, we have guaranteed that this node should have an auto\n      // nonexclusive singleton on it somewhere. Now determine where\n      size_t min_num_singletons_on_a_proc =\n          min_num_singletons_on_a_proc_opt.value();\n\n      // Find the first available proc on this node. Check that\n      // 1. This proc is available (i.e. no exclusive singletons on it)\n      // 2. This proc has the minimum number of singletons on it for this node\n      //    so we distribute the singletons evenly over all the procs on this\n      //    node\n      auto proc_iter =\n          std::find_if(first_proc_iter, first_proc_next_node_iter,\n                       [&min_num_singletons_on_a_proc](const auto& proc_opt) {\n                         return proc_opt.has_value() and\n                                *proc_opt == min_num_singletons_on_a_proc;\n                       });\n\n      // Get the index of the overall vector. We need this because we're going\n      // to be indexing two separate vectors, otherwise we could have just used\n      // the value of the iterator\n      const size_t proc = static_cast<size_t>(std::distance(\n          nonexclusive_singletons_on_each_proc.begin(), proc_iter));\n\n      // Increment things\n      ++*nonexclusive_singletons_on_each_proc[proc];\n      ++singletons_on_each_node[i];\n      if (auto_nonexclusive_singletons_on_each_proc[proc].has_value()) {\n        ++*auto_nonexclusive_singletons_on_each_proc[proc];\n      } else {\n        auto_nonexclusive_singletons_on_each_proc[proc] = 1;\n      }\n      --remaining_auto_nonexclusive_singletons;\n\n      // We need to break out of both loops here. Use a goto.\n      if (remaining_auto_nonexclusive_singletons == 0) {\n        goto break_auto_nonexclusive_loops;\n      }\n    }  // for (size_t i = 0; i < num_nodes; i++)\n\n    ++min_num_singletons_on_a_node;\n  }  // while (remaining_auto_nonexclusive_singletons > 0)\nbreak_auto_nonexclusive_loops:\n\n  ASSERT(remaining_auto_nonexclusive_singletons == 0,\n         \"Not all nonexclusive singletons have been allocated. The remaining \"\n         \"number of singletons to be allocated is \"\n             << remaining_auto_nonexclusive_singletons << \".\");\n\n  // Actually allocate the auto nonexclusive singletons\n  std::stringstream ss;\n  ss << \"\\nAllocating Singletons:\\n\";\n  size_t current_proc = 0;\n  tmpl::for_each<singletons>([this, &current_proc, &cache, &ss,\n                              &auto_nonexclusive_singletons_on_each_proc](\n                                 const auto component_v) {\n    using component = tmpl::type_from<decltype(component_v)>;\n    auto& singleton_map = tuples::get<LocalTag<component>>(singleton_map_);\n    const auto proc_opt = singleton_map.second;\n\n    // At this point, the only singletons that have this are nonexclusive\n    if (not proc_opt.has_value()) {\n      while (not auto_nonexclusive_singletons_on_each_proc[current_proc]\n                     .has_value()) {\n        ++current_proc;\n      }\n\n      singleton_map.second = current_proc;\n\n      --*auto_nonexclusive_singletons_on_each_proc[current_proc];\n      // Indicate that there are no more singletons to be placed on this proc\n      if (*auto_nonexclusive_singletons_on_each_proc[current_proc] == 0) {\n        auto_nonexclusive_singletons_on_each_proc[current_proc] = std::nullopt;\n      }\n    }\n\n    // Print some diagnostic info to stdout for each singleton. This can aid in\n    // debugging.\n    ss << pretty_type::name<component>();\n    ss << \" on node \" << Parallel::node_of<int>(*singleton_map.second, cache);\n    ss << \", global proc \" << *singleton_map.second;\n    ss << \", exclusive = \" << std::boolalpha << singleton_map.first << \"\\n\";\n  });\n\n  ss << \"\\n\";\n  Parallel::printf(\"%s\", ss.str());\n\n  // Now that everything has been set, signal that we don't have to do\n  // this again.\n  singleton_map_has_been_set_ = true;\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Section.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <optional>\n#include <pup.h>\n\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n\nnamespace Parallel {\n\n/*!\n * \\brief A subset of chares in a parallel component\n *\n * The section is identified at compile time by the parallel component and a\n * `SectionIdTag`. The `SectionIdTag` describes the quantity that partitions the\n * chares into one or more sections. For example, the `SectionIdTag` could be\n * the block ID in the computational domain, so elements are partitioned per\n * block. Chares can be a member of multiple sections.\n *\n * - [Details on sections in the Charm++\n *   documentation](https://charm.readthedocs.io/en/latest/charm++/manual.html#sections-subsets-of-a-chare-array-group)\n *\n * Here's an example how to work with sections in an array parallel component:\n *\n * \\snippet Test_SectionReductions.cpp sections_example\n *\n * \\warning The Charm++ documentation indicates some [creation order\n * restrictions](https://charm.readthedocs.io/en/latest/charm++/manual.html#creation-order-restrictions)\n * for sections that may become relevant if we encounter issues with race\n * conditions in the future.\n */\ntemplate <typename ParallelComponent, typename SectionIdTag>\nstruct Section {\n private:\n  using chare_type = typename ParallelComponent::chare_type;\n  using charm_type = charm_types_with_parameters<\n      ParallelComponent,\n      typename get_array_index<chare_type>::template f<ParallelComponent>>;\n  using IdType = typename SectionIdTag::type;\n\n public:\n  using parallel_component = ParallelComponent;\n  using cproxy_section = typename charm_type::cproxy_section;\n  using section_id_tag = SectionIdTag;\n\n  Section(IdType id, cproxy_section proxy)\n      : id_(id), proxy_(std::move(proxy)), cookie_(proxy_.ckGetSectionInfo()) {}\n\n  Section() = default;\n  Section(Section&& rhs) = default;\n  Section& operator=(Section&& rhs) = default;\n  // The copy constructors currently copy the cookie as well. This seems to work\n  // fine, but if issues come up with updating the cookie we can consider\n  // re-creating it when copying the section to each element.\n  Section(const Section&) = default;\n  Section& operator=(const Section&) = default;\n  ~Section() = default;\n\n  /// The section ID corresponding to the `SectionIdTag`\n  const IdType& id() const { return id_; }\n\n  /// @{\n  /// The Charm++ section proxy\n  const cproxy_section& proxy() const { return proxy_; }\n  cproxy_section& proxy() { return proxy_; }\n  /// @}\n\n  /*!\n   * \\brief The Charm++ section cookie that keeps track of reductions\n   *\n   * The section cookie must be stored on each element and updated when\n   * performing reductions. For details on Charm++ sections and section\n   * reductions see:\n   * https://charm.readthedocs.io/en/latest/charm++/manual.html?#sections-subsets-of-a-chare-array-group\n   */\n  /// @{\n  const CkSectionInfo& cookie() const { return cookie_; }\n  CkSectionInfo& cookie() { return cookie_; }\n  /// @}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | id_;\n    p | proxy_;\n    p | cookie_;\n  }\n\n private:\n  IdType id_{};\n  cproxy_section proxy_{};\n  CkSectionInfo cookie_{};\n};\n\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Spinlock.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <atomic>\n\nnamespace Parallel {\n/*!\n * \\brief A simple spinlock implemented in `std::atomic`s\n *\n * Implementation adapted from https://rigtorp.se/spinlock/\n */\nclass Spinlock {\n public:\n  Spinlock() = default;\n  Spinlock(const Spinlock&) = delete;\n  Spinlock& operator=(const Spinlock&) = delete;\n  Spinlock(Spinlock&&) = delete;\n  Spinlock& operator=(Spinlock&&) = delete;\n  ~Spinlock() = default;\n\n  /// \\brief Acquire the lock. Will block until the lock is acquired.\n  void lock() {\n    for (;;) {\n      // Optimistically assume the lock is free on the first try\n      if (not lock_.exchange(true, std::memory_order_acquire)) {\n        return;\n      }\n      // Wait for lock to be released without generating cache misses\n      while (lock_.load(std::memory_order_relaxed)) {\n        // Issue X86 PAUSE or ARM YIELD instruction to reduce contention between\n        // hyper-threads\n        //\n        // If no hyperthreading is being used, this will actually slow down the\n        // code.\n        //\n        // We keep this comment and code around just in case we want to\n        // experiment with it in the future.\n        //\n        // __builtin_ia32_pause();\n      }\n    }\n  }\n\n  /// \\brief Try to acquire the lock.\n  ///\n  /// Returns `true` if the lock was acquired, `false` if it wasn't.\n  bool try_lock() {\n    // First do a relaxed load to check if lock is free in order to prevent\n    // unnecessary cache misses if someone does while(!try_lock())\n    return not lock_.load(std::memory_order_relaxed) and\n           not lock_.exchange(true, std::memory_order_acquire);\n  }\n\n  /// \\brief Release the lock.\n  void unlock() { lock_.store(false, std::memory_order_release); }\n\n private:\n  std::atomic<bool> lock_{false};\n};\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/StaticSpscQueue.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/*\n *The original code is distributed under the following copyright and license:\n *\n * Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n *\n *\n * SXS Modifications:\n * 1. Casing to match SpECTRE conventions\n * 2. Static capacity\n * 3. Storage is std::array\n * 4. Switch to west-const\n *\n */\n\n#pragma once\n\n#include <atomic>\n#include <cassert>\n#include <cstddef>\n#include <new>  // Placement new\n#include <stdexcept>\n#include <type_traits>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Requires.hpp\"\n\nnamespace Parallel {\n/*!\n * \\brief A static capacity runtime-sized single-producer single-consumer\n * lockfree queue.\n *\n * As long as only one thread reads and writes simultaneously the queue is\n * threadsafe. Which threads read and write can change throughout program\n * execution, the important thing is that there is no instance during the\n * execution where more than one thread tries to read and where more than one\n * thread tries to write.\n *\n * \\note This class is intentionally not serializable since handling\n * threadsafety around serialization requires careful thought of the individual\n * circumstances.\n */\ntemplate <typename T, size_t Capacity>\nclass StaticSpscQueue {\n private:\n#ifdef __cpp_lib_hardware_interference_size\n  static constexpr size_t cache_line_size_ =\n      std::hardware_destructive_interference_size;\n#else\n  static constexpr size_t cache_line_size_ = 64;\n#endif\n\n  // Padding to avoid false sharing between slots_ and adjacent allocations\n  static constexpr size_t padding_ = (cache_line_size_ - 1) / sizeof(T) + 1;\n\n public:\n  StaticSpscQueue() = default;\n  ~StaticSpscQueue() {\n    // Destruct objects in the buffer.\n    while (front()) {\n      pop();\n    }\n  }\n\n  StaticSpscQueue(const StaticSpscQueue&) = delete;\n  StaticSpscQueue& operator=(const StaticSpscQueue&) = delete;\n  StaticSpscQueue(StaticSpscQueue&&) = delete;\n  StaticSpscQueue& operator=(StaticSpscQueue&&) = delete;\n\n  /// Construct a new element at the end of the queue in place.\n  ///\n  /// Uses placement new for in-place construction.\n  ///\n  /// \\warning This may overwrite existing elements if `capacity()` is\n  /// exceeded without warning.\n  template <typename... Args>\n  void emplace(Args&&... args) noexcept(\n      std::is_nothrow_constructible_v<T, Args&&...>) {\n    static_assert(std::is_constructible_v<T, Args&&...>,\n                  \"T must be constructible with Args&&...\");\n    const auto write_index = write_index_.load(std::memory_order_relaxed);\n    auto next_write_index = write_index + 1;\n    if (next_write_index == capacity_) {\n      next_write_index = 0;\n    }\n    while (next_write_index == read_index_cache_) {\n      read_index_cache_ = read_index_.load(std::memory_order_acquire);\n    }\n    new (&data_[write_index + padding_]) T(std::forward<Args>(args)...);\n    write_index_.store(next_write_index, std::memory_order_release);\n  }\n\n  /// Construct a new element at the end of the queue in place.\n  ///\n  /// Uses placement new for in-place construction.\n  ///\n  /// Returns `true` if the emplacement succeeded and `false` if it did\n  /// not. If it failed then the queue is currently full.\n  template <typename... Args>\n  [[nodiscard]] bool try_emplace(Args&&... args) noexcept(\n      std::is_nothrow_constructible_v<T, Args&&...>) {\n    static_assert(std::is_constructible_v<T, Args&&...>,\n                  \"T must be constructible with Args&&...\");\n    const auto write_index = write_index_.load(std::memory_order_relaxed);\n    auto next_write_index = write_index + 1;\n    if (next_write_index == capacity_) {\n      next_write_index = 0;\n    }\n    if (next_write_index == read_index_cache_) {\n      read_index_cache_ = read_index_.load(std::memory_order_acquire);\n      if (next_write_index == read_index_cache_) {\n        return false;\n      }\n    }\n    new (&data_[write_index + padding_]) T(std::forward<Args>(args)...);\n    write_index_.store(next_write_index, std::memory_order_release);\n    return true;\n  }\n\n  /// Push a new element to the end of the queue.\n  ///\n  /// Uses `emplace()` internally.\n  ///\n  /// \\warning This may overwrite existing elements if `capacity()` is\n  /// exceeded without warning.\n  void push(const T& v) noexcept(std::is_nothrow_copy_constructible_v<T>) {\n    static_assert(std::is_copy_constructible_v<T>,\n                  \"T must be copy constructible\");\n    emplace(v);\n  }\n\n  /// Push a new element to the end of the queue.\n  ///\n  /// Uses `emplace()` internally.\n  ///\n  /// \\warning This may overwrite existing elements if `capacity()` is\n  /// exceeded without warning.\n  template <typename P, Requires<std::is_constructible_v<T, P&&>> = nullptr>\n  void push(P&& v) noexcept(std::is_nothrow_constructible_v<T, P&&>) {\n    emplace(std::forward<P>(v));\n  }\n\n  /// Push a new element to the end of the queue. Returns `false` if the queue\n  /// is at capacity and does not push the new object, otherwise returns `true`.\n  ///\n  /// Uses `try_emplace()` internally.\n  [[nodiscard]] bool try_push(const T& v) noexcept(\n      std::is_nothrow_copy_constructible_v<T>) {\n    static_assert(std::is_copy_constructible_v<T>,\n                  \"T must be copy constructible\");\n    return try_emplace(v);\n  }\n\n  /// Push a new element to the end of the queue. Returns `false` if the queue\n  /// is at capacity and does not push the new object, otherwise returns `true`.\n  ///\n  /// Uses `try_emplace()` internally.\n  template <typename P, Requires<std::is_constructible_v<T, P&&>> = nullptr>\n  [[nodiscard]] bool try_push(P&& v) noexcept(\n      std::is_nothrow_constructible_v<T, P&&>) {\n    return try_emplace(std::forward<P>(v));\n  }\n\n  /// Returns the first element from the queue.\n  ///\n  /// \\note Returns `nullptr` if the queue is empty.\n  [[nodiscard]] T* front() noexcept {\n    const auto read_index = read_index_.load(std::memory_order_relaxed);\n    if (read_index == write_index_cache_) {\n      write_index_cache_ = write_index_.load(std::memory_order_acquire);\n      if (write_index_cache_ == read_index) {\n        return nullptr;\n      }\n    }\n    return &data_[read_index + padding_];\n  }\n\n  /// Removes the first element from the queue.\n  void pop() {\n    static_assert(std::is_nothrow_destructible_v<T>,\n                  \"T must be nothrow destructible\");\n    const auto read_index = read_index_.load(std::memory_order_relaxed);\n#ifdef SPECTRE_DEBUG\n    const auto write_index = write_index_.load(std::memory_order_acquire);\n    ASSERT(write_index != read_index,\n           \"Can't pop an element from an empty queue. read_index: \"\n               << read_index << \" write_index \" << write_index);\n#endif  // SPECTRE_DEBUG\n    data_[read_index + padding_].~T();\n    auto next_read_index = read_index + 1;\n    if (next_read_index == capacity_) {\n      next_read_index = 0;\n    }\n    if (read_index == write_index_cache_) {\n      write_index_cache_ = next_read_index;\n    }\n    read_index_.store(next_read_index, std::memory_order_release);\n  }\n\n  /// Returns the size of the queue at a particular hardware state.\n  ///\n  /// Note that while this can be checked in a threadsafe manner, it is up to\n  /// the user to guarantee that another thread does not change the queue\n  /// between when `size()` is called and how the result is used.\n  [[nodiscard]] size_t size() const noexcept {\n    std::ptrdiff_t diff = static_cast<std::ptrdiff_t>(\n                              write_index_.load(std::memory_order_acquire)) -\n                          static_cast<std::ptrdiff_t>(\n                              read_index_.load(std::memory_order_acquire));\n    if (diff < 0) {\n      diff += static_cast<std::ptrdiff_t>(capacity_);\n    }\n    return static_cast<size_t>(diff);\n  }\n\n  /// Returns `true` if the queue may be empty, otherwise `false`.\n  ///\n  /// Note that while this can be checked in a threadsafe manner, it is up to\n  /// the user to guarantee that another thread does not change the queue\n  /// between when `empty()` is called and how the result is used.\n  [[nodiscard]] bool empty() const noexcept {\n    return write_index_.load(std::memory_order_acquire) ==\n           read_index_.load(std::memory_order_acquire);\n  }\n\n  /// Returns the capacity of the queue.\n  [[nodiscard]] size_t capacity() const noexcept { return capacity_ - 1; }\n\n private:\n  static constexpr size_t capacity_ = Capacity + 1;\n  std::array<T, capacity_ + 2 * padding_> data_{};\n\n  // Align to cache line size in order to avoid false sharing\n  // read_index_cache_ and write_index_cache_ is used to reduce the amount of\n  // cache coherency traffic\n  alignas(cache_line_size_) std::atomic<size_t> write_index_{0};\n  alignas(cache_line_size_) size_t read_index_cache_{0};\n  alignas(cache_line_size_) std::atomic<size_t> read_index_{0};\n  alignas(cache_line_size_) size_t write_index_cache_{0};\n\n  // Padding to avoid adjacent allocations from sharing a cache line with\n  // write_index_cache_\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  char padding_data_[cache_line_size_ - sizeof(write_index_cache_)]{};\n};\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Tags/ArrayIndex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace Parallel::Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup ParallelGroup\n/// Tag to retrieve the `ArrayIndex` from the DataBox.\ntemplate <typename Index>\nstruct ArrayIndex : db::SimpleTag {\n  using type = Index;\n};\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/Parallel/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ArrayIndex.hpp\n  DistributedObjectTags.hpp\n  InputSource.hpp\n  Parallelization.hpp\n  ResourceInfo.hpp\n  Section.hpp\n)\n"
  },
  {
    "path": "src/Parallel/Tags/DistributedObjectTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel::Tags {\n/// \\cond\ntemplate <typename Index>\nstruct ArrayIndex;\ntemplate <typename Metavariables>\nstruct GlobalCacheProxy;\ntemplate <typename Metavariables>\nstruct MetavariablesImpl;\n/// \\endcond\n\n/// \\brief List of tags for mutable items that are automatically added to\n/// the DataBox of a DistributedObject\n///\n/// \\details It is the responsibility of DistributedObject to initialize the\n/// mutable items corresponding to these tags.\ntemplate <typename Metavariables, typename Index>\nusing distributed_object_tags =\n    tmpl::list<Tags::MetavariablesImpl<Metavariables>, Tags::ArrayIndex<Index>,\n               Tags::GlobalCacheProxy<Metavariables>>;\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/Parallel/Tags/InputSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/Tags.hpp\"\n\nnamespace Parallel::Tags {\n/// \\ingroup DataBoxTagsGroup\n/// A tag storing the input file yaml input source passed in at runtime, so that\n/// it can be accessed and written to files.\nstruct InputSource : db::SimpleTag {\n  using type = std::vector<std::string>;\n  using option_tags = tmpl::list<::Options::Tags::InputSource>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& input_source) {\n    return input_source;\n  }\n};\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/Parallel/Tags/Parallelization.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Options/String.hpp\"\n\nnamespace Parallel::OptionTags {\n/*!\n * \\brief Option group for all things related to parallelization.\n *\n * It is possible this group will need to be used in a library that cannot\n * depend on the `Parallel` library. In that case, this struct should just be\n * forward declared.\n */\nstruct Parallelization {\n  static constexpr Options::String help = {\n      \"Options related to parallelization aspects of the simulation.\"};\n};\n}  // namespace Parallel::OptionTags\n"
  },
  {
    "path": "src/Parallel/Tags/ResourceInfo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace Parallel {\n/// \\cond\ntemplate <typename Metavariables>\nstruct ResourceInfo;\n/// \\endcond\nnamespace OptionTags {\n/// \\ingroup ParallelGroup\n/// Options group for resource allocation\ntemplate <typename Metavariables>\nstruct ResourceInfo {\n  using type = Parallel::ResourceInfo<Metavariables>;\n  static constexpr Options::String help = {\n      \"Options for allocating resources. This information will be used when \"\n      \"placing Array and Singleton parallel components on the requested \"\n      \"resources.\"};\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\ingroup ParallelGroup\n/// Tag to retrieve the ResourceInfo.\ntemplate <typename Metavariables>\nstruct ResourceInfo : db::SimpleTag {\n  using type = Parallel::ResourceInfo<Metavariables>;\n};\n}  // namespace Tags\n}  // namespace Parallel\n"
  },
  {
    "path": "src/Parallel/Tags/Section.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Section.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace Parallel::Tags {\n\n/*!\n * \\brief The `Parallel::Section<ParallelComponent, SectionIdTag>` that this\n * element belongs to.\n *\n * The tag holds a `std::nullopt` if the element doesn't belong to a section of\n * the `SectionIdTag`. For example, a section may describe a specific region in\n * the computational domain and thus doesn't include all elements, so this tag\n * would hold `std::nullopt` on the elements outside that region.\n *\n * \\see Parallel::Section\n */\ntemplate <typename ParallelComponent, typename SectionIdTag>\nstruct Section : db::SimpleTag {\n  static std::string name() {\n    return \"Section(\" + db::tag_name<SectionIdTag>() + \")\";\n  }\n  using type =\n      std::optional<Parallel::Section<ParallelComponent, SectionIdTag>>;\n  // Allow default-constructing the tag so sections can be created and assigned\n  // to the tag in a parallel component's `allocate_array` function.\n  constexpr static bool pass_metavariables = false;\n  using option_tags = tmpl::list<>;\n  static type create_from_options() { return {}; };\n};\n\n}  // namespace Parallel::Tags\n"
  },
  {
    "path": "src/Parallel/TypeTraits.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines type traits related to Charm++ types\n\n#pragma once\n\n#include <charm++.h>\n#include <pup.h>\n\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\nnamespace Parallel::Algorithms {\nstruct Array;\nstruct Singleton;\nstruct Group;\nstruct Nodegroup;\n}  // namespace Parallel::Algorithms\n/// \\endcond\n\nnamespace Parallel {\n\n/// \\ingroup ParallelGroup\n/// Check if `T` is a Charm++ proxy for an array chare (the entire array)\ntemplate <typename T>\nstruct is_array_proxy : std::is_base_of<CProxy_ArrayElement, T>::type {};\n\n/// \\ingroup ParallelGroup\n/// Check if `T` is a Charm++ proxy for an array chare element (from indexing\n/// into the array chare)\ntemplate <typename T>\nstruct is_array_element_proxy\n    : std::is_base_of<CProxyElement_ArrayElement, T>::type {};\n\n/// \\ingroup ParallelGroup\n/// Check if `T` is a Charm++ proxy for a group chare\ntemplate <typename T>\nstruct is_group_proxy : std::is_base_of<CProxy_IrrGroup, T>::type {};\n\n/// \\ingroup ParallelGroup\n/// Check if `T` is a Charm++ proxy for a node group chare\ntemplate <typename T>\nstruct is_node_group_proxy : std::is_base_of<CProxy_NodeGroup, T>::type {};\n\n/// \\ingroup ParallelGroup\n/// Check if `T` is a ParallelComponent for a Charm++ bound array\ntemplate <typename T, typename = std::void_t<>>\nstruct is_bound_array : std::false_type {};\n\ntemplate <typename T>\nstruct is_bound_array<T, std::void_t<typename T::bind_to>> : std::true_type {\n  static_assert(Parallel::is_array_proxy<typename T::type>::value,\n                \"Can only bind an array chare\");\n  static_assert(Parallel::is_array_proxy<typename T::bind_to::type>::value,\n                \"Can only bind to an array chare\");\n};\n\n/// \\ingroup ParallelGroup\n/// @{\n/// Check if `T` is a SpECTRE Array\ntemplate <typename T>\nstruct is_array : std::is_same<Parallel::Algorithms::Array,\n                               typename T::chare_type::component_type> {};\ntemplate <typename T>\nconstexpr bool is_array_v = is_array<T>::value;\n/// @}\n\n/// \\ingroup ParallelGroup\n/// @{\n/// Check if `T` is a SpECTRE Singleton\ntemplate <typename T>\nstruct is_singleton : std::is_same<Parallel::Algorithms::Singleton,\n                                   typename T::chare_type::component_type> {};\ntemplate <typename T>\nconstexpr bool is_singleton_v = is_singleton<T>::value;\n/// @}\n\n/// \\ingroup ParallelGroup\n/// @{\n/// Check if `T` is a SpECTRE Group\ntemplate <typename T>\nstruct is_group : std::is_same<Parallel::Algorithms::Group,\n                               typename T::chare_type::component_type> {};\ntemplate <typename T>\nconstexpr bool is_group_v = is_group<T>::value;\n/// @}\n\n/// \\ingroup ParallelGroup\n/// @{\n/// Check if `T` is a SpECTRE Nodegroup\ntemplate <typename T>\nstruct is_nodegroup : std::is_same<Parallel::Algorithms::Nodegroup,\n                                   typename T::chare_type::component_type> {};\ntemplate <typename T>\nconstexpr bool is_nodegroup_v = is_nodegroup<T>::value;\n/// @}\n\n/// @{\n/// \\ingroup ParallelGroup\n/// Retrieve a parallel component from its proxy.\ntemplate <typename Proxy>\nstruct get_parallel_component_from_proxy;\n\ntemplate <typename ParallelComponent, template <typename...> class Proxy,\n          typename... Ts>\nstruct get_parallel_component_from_proxy<Proxy<ParallelComponent, Ts...>> {\n  using type = ParallelComponent;\n};\n/// @}\n\n/// @{\n/// \\ingroup ParallelGroup\n/// \\brief Check if `T` has a `pup` member function\n///\n/// \\details\n/// Inherits from std::true_type if the type `T` has a `pup(PUP::er&)` member\n/// function, otherwise inherits from std::false_type\n///\n/// \\usage\n/// For any type `T`,\n/// \\code\n/// using result = tt::has_pup_member<T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If the type `T` has a `pup(PUP::er&)` member function, then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Parallel/Test_TypeTraits.cpp has_pup_member_example\n/// \\see is_pupable\n/// \\tparam T the type to check\ntemplate <typename T, typename = std::void_t<>>\nstruct has_pup_member : std::false_type {};\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T>\nstruct has_pup_member<\n    T, std::void_t<decltype(std::declval<T>().pup(std::declval<PUP::er&>()))>>\n    : std::true_type {};\n/// \\endcond\n/// \\see has_pup_member\ntemplate <typename T>\nconstexpr bool has_pup_member_v = has_pup_member<T>::value;\n\n/// \\see has_pup_member\ntemplate <typename T>\nusing has_pup_member_t = typename has_pup_member<T>::type;\n/// @}\n\n/// @{\n/// \\ingroup ParallelGroup\n/// \\brief Check if type `T` has operator| defined for Charm++ serialization\n///\n/// \\details\n/// Inherits from std::true_type if the type `T` has operator| defined,\n/// otherwise inherits from std::false_type\n///\n/// \\usage\n/// For any type `T`,\n/// \\code\n/// using result = tt::is_pupable<T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If the type `T` has operator| defined, then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Parallel/Test_TypeTraits.cpp is_pupable_example\n/// \\see has_pup_member\n/// \\tparam T the type to check\ntemplate <typename T, typename U = void>\nstruct is_pupable : std::false_type {};\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T>\nstruct is_pupable<\n    T, std::void_t<decltype(std::declval<PUP::er&>() | std::declval<T&>())>>\n    : std::true_type {};\n/// \\endcond\n/// \\see is_pupable\ntemplate <typename T>\nconstexpr bool is_pupable_v = is_pupable<T>::value;\n\n/// \\see is_pupable\ntemplate <typename T>\nusing is_pupable_t = typename is_pupable<T>::type;\n/// @}\n\n}  // namespace Parallel\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/Actions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief Contains various actions.\n */\nnamespace Actions {}\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/AddComputeTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Initialization {\nnamespace Actions {\n/*!\n * \\ingroup ActionsGroup\n *\n * \\brief Initialize the list of compute tags in `ComputeTagsList`\n *\n * Uses: nothing\n *\n * DataBox changes:\n * - Adds:\n *   - Each compute tag in the list `ComputeTagsList`\n * - Removes:\n *   - nothing\n * - Modifies:\n *   - nothing\n */\ntemplate <typename ComputeTagsList>\nstruct AddComputeTags {\n  using compute_tags = ComputeTagsList;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Initialization\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/AddSimpleTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Initialization {\nnamespace Actions {\n/*!\n * \\ingroup ActionsGroup\n * \\brief Initialize the list of simple tags in `Mutators::return_tags` by\n * calling each mutator in the order they are specified\n *\n * There's a specialization for `AddSimpleTags<tmpl::list<Mutators...>>` that\n * can also be used if a `tmpl::list` is available.\n *\n * Uses: nothing\n *\n * DataBox changes:\n * - Adds:\n *   - Each simple tag in the list `Mutators::return_tags`\n * - Removes:\n *   - nothing\n * - Modifies:\n *   - Each simple tag in the list `Mutators::return_tags`\n */\ntemplate <typename... Mutators>\nstruct AddSimpleTags {\n  using simple_tags =\n      tmpl::flatten<tmpl::append<typename Mutators::return_tags...>>;\n  using compute_tags = tmpl::list<>;\n\n  static_assert((tt::assert_conforms_to_v<Mutators, db::protocols::Mutator> and\n                 ...));\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    EXPAND_PACK_LEFT_TO_RIGHT(db::mutate_apply<Mutators>(make_not_null(&box)));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\cond\ntemplate <typename... Mutators>\nstruct AddSimpleTags<tmpl::list<Mutators...>>\n    : public AddSimpleTags<Mutators...> {};\n/// \\endcond\n}  // namespace Actions\n}  // namespace Initialization\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Actions)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Actions.hpp\n  AddComputeTags.hpp\n  AddSimpleTags.hpp\n  FilterAction.hpp\n  FunctionsOfTimeAreReady.hpp\n  GetItemFromDistributedObject.hpp\n  Goto.hpp\n  InitializeItems.hpp\n  LimiterActions.hpp\n  MutateApply.hpp\n  RandomizeVariables.hpp\n  SetData.hpp\n  TerminatePhase.hpp\n  UpdateMessageQueue.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  ErrorHandling\n  FunctionsOfTime\n  Parallel\n  Serialization\n  Spectral\n  Utilities\n  )\n\nadd_subdirectory(MemoryMonitor)\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/FilterAction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Structure/BlockGroups.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t VolumeDim>\nstruct Domain;\n}  // namespace domain::Tags\nnamespace Filters::Tags {\ntemplate <typename FilterType>\nstruct Filter;\n}  // namespace Filters::Tags\n/// \\endcond\n\nnamespace dg {\nnamespace Actions {\nnamespace Filter_detail {\ntemplate <bool SameSize, bool SameList>\nstruct FilterAllEvolvedVars {\n  template <typename EvolvedVarsTagList, typename... FilterTags>\n  using f = std::integral_constant<bool, false>;\n};\n\ntemplate <>\nstruct FilterAllEvolvedVars<true, false> {\n  template <typename EvolvedVarsTagList, typename... FilterTags>\n  using f =\n      std::integral_constant<bool, tmpl2::flat_all_v<tmpl::list_contains_v<\n                                       EvolvedVarsTagList, FilterTags>...>>;\n};\n\ntemplate <>\nstruct FilterAllEvolvedVars<true, true> {\n  template <typename EvolvedVarsTagList, typename... FilterTags>\n  using f = std::integral_constant<bool, true>;\n};\n}  // namespace Filter_detail\n\n/// \\cond\ntemplate <typename FilterType, typename TagsToFilterList>\nstruct Filter;\n/// \\endcond\n\n/*!\n * \\ingroup DiscontinuousGalerkinGroup\n * \\brief Applies a filter to the specified tags.\n *\n * If different Filters are desired for different tags then multiple `Filter`\n * actions must be inserted into the action list with different `FilterType`.\n * Here is an example of an action list with two different exponential filters:\n *\n * \\snippet LinearOperators/Test_Filtering.cpp action_list_example\n *\n * Uses:\n * - GlobalCache:\n *   - `Filter`\n * - DataBox:\n *   - `Tags::Mesh`\n * - DataBox changes:\n *   - Adds: nothing\n *   - Removes: nothing\n *   - Modifies:\n *     - `TagsToFilter`\n * - System:\n *   - `volume_dim`\n *   - `variables_tag`\n *\n */\ntemplate <typename FilterType, typename... TagsToFilter>\nclass Filter<FilterType, tmpl::list<TagsToFilter...>> {\n public:\n  using const_global_cache_tags =\n      tmpl::list<::Filters::Tags::Filter<FilterType>>;\n\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    constexpr size_t volume_dim = Metavariables::system::volume_dim;\n    using evolved_vars_tag = typename Metavariables::system::variables_tag;\n    using evolved_vars_tags_list = typename evolved_vars_tag::tags_list;\n    const FilterType& filter_helper =\n        Parallel::get<::Filters::Tags::Filter<FilterType>>(cache);\n    const size_t block_id =\n        db::get<domain::Tags::Element<volume_dim>>(box).id().block_id();\n    const auto& domain = Parallel::get<domain::Tags::Domain<volume_dim>>(cache);\n    const auto& block_groups = domain.block_groups();\n    const std::string& block_name = domain.blocks()[block_id].name();\n\n    // Technically this whole next block could be done on a single line, but\n    // then it would be very dense and hard to understand. This way is easier to\n    // read and understand\n    bool enable = filter_helper.enable();\n    // Only do this check if filtering is enabled. A `nullopt` means all blocks\n    // are allowed to do filtering\n    if (enable and filter_helper.blocks_to_filter().has_value()) {\n      // Enable filtering for this block if it's in any of the listed groups\n      enable = alg::any_of(\n          filter_helper.blocks_to_filter().value(),\n          [&block_name, &block_groups](const std::string& block_to_filter) {\n            return domain::block_is_in_group(block_name, block_to_filter,\n                                             block_groups);\n          });\n    }\n\n    if (not enable) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const Mesh<volume_dim> mesh = db::get<domain::Tags::Mesh<volume_dim>>(box);\n    const Matrix empty{};\n    std::array<std::reference_wrapper<const Matrix>, volume_dim> filter =\n        make_array<volume_dim>(std::cref(empty));\n    for (size_t d = 0; d < volume_dim; d++) {\n      gsl::at(filter, d) =\n          std::cref(filter_helper.filter_matrix(mesh.slice_through(d)));\n    }\n\n    // In the case that the tags we are filtering are all the evolved variables\n    // we filter the entire Variables at once to be more efficient. This case is\n    // the first branch of the `if-else`.\n    if (Filter_detail::FilterAllEvolvedVars<\n            sizeof...(TagsToFilter) ==\n                tmpl::size<evolved_vars_tags_list>::value,\n            std::is_same_v<evolved_vars_tags_list,\n                           tmpl::list<TagsToFilter...>>>::\n            template f<evolved_vars_tags_list, TagsToFilter...>::value) {\n      db::mutate<typename Metavariables::system::variables_tag>(\n          [&filter](const gsl::not_null<\n                        typename Metavariables::system::variables_tag::type*>\n                        vars,\n                    const auto& local_mesh) {\n            *vars = apply_matrices(filter, *vars, local_mesh.extents());\n          },\n          make_not_null(&box), mesh);\n    } else {\n      db::mutate<TagsToFilter...>(\n          [](const gsl::not_null<\n                 typename TagsToFilter::type*>... tensors_to_filter,\n             const Mesh<volume_dim>& local_mesh,\n             const std::array<std::reference_wrapper<const Matrix>, volume_dim>&\n                 local_filter) {\n            DataVector temp(local_mesh.number_of_grid_points(), 0.0);\n            const auto helper = [&local_mesh, &local_filter,\n                                 &temp](const auto tensor) {\n              for (auto& component : *tensor) {\n                temp = 0.0;\n                apply_matrices(make_not_null(&temp), local_filter, component,\n                               local_mesh.extents());\n                component = temp;\n              }\n            };\n            EXPAND_PACK_LEFT_TO_RIGHT(helper(tensors_to_filter));\n          },\n          make_not_null(&box), mesh, filter);\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace dg\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/PerformAlgorithmOnElement.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace domain {\nnamespace detail {\ntemplate <typename CacheTag, typename Callback, typename Metavariables,\n          typename ArrayIndex, typename Component, typename... Args>\nbool functions_of_time_are_ready_impl(\n    Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,\n    const Component* /*meta*/, const double time,\n    const std::optional<std::unordered_set<std::string>>& functions_to_check,\n    Args&&... args) {\n  if constexpr (Parallel::is_in_mutable_global_cache<Metavariables, CacheTag>) {\n    const auto& proxy =\n        ::Parallel::get_parallel_component<Component>(cache)[array_index];\n    const Parallel::ArrayComponentId array_component_id =\n        [&]() -> Parallel::ArrayComponentId {\n      if constexpr (Parallel::is_dg_element_collection_v<Component>) {\n        static_assert(\n            sizeof...(args) == 1,\n            \"This currently assumes the only argument is the ElementId. If you \"\n            \"need extra arguments, this can be generalized. We need the \"\n            \"ElementId instead of the array_index (which is the nodegroup ID) \"\n            \"since each ArrayComponentId is only allowed to register one \"\n            \"callback, additional ones are ignored. This means of a \"\n            \"DgElementCollection only 1 callback _per node_ would be \"\n            \"registered, while we need 1 callback for each element. An \"\n            \"alternative approach would be to have the callback be a broadcast \"\n            \"to all elements instead of one specific one.\");\n        static_assert((std::is_same_v<ElementId<Metavariables::volume_dim>,\n                                      std::decay_t<Args>> and\n                       ...));\n        return Parallel::make_array_component_id<Component>(args...);\n      } else {\n        return Parallel::make_array_component_id<Component>(array_index);\n      }\n    }();\n\n    return Parallel::mutable_cache_item_is_ready<CacheTag>(\n        cache, array_component_id,\n        [&functions_to_check, &proxy, &time,\n         &args...](const std::unordered_map<\n                   std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n                       functions_of_time) {\n          using ::operator<<;\n          ASSERT(\n              alg::all_of(\n                  functions_to_check.value_or(\n                      std::unordered_set<std::string>{}),\n                  [&functions_of_time](const std::string& function_to_check) {\n                    return functions_of_time.count(function_to_check) == 1;\n                  }),\n              \"Not all functions to check (\"\n                  << functions_to_check.value() << \") are in the global cache (\"\n                  << keys_of(functions_of_time) << \")\");\n          for (const auto& [name, f_of_t] : functions_of_time) {\n            if (functions_to_check.has_value() and\n                functions_to_check->count(name) == 0) {\n              continue;\n            }\n            const double expiration_time = f_of_t->time_bounds()[1];\n            if (time > expiration_time) {\n              return std::unique_ptr<Parallel::Callback>(\n                  new Callback(proxy, std::forward<Args>(args)...));\n            }\n          }\n          return std::unique_ptr<Parallel::Callback>{};\n        });\n  } else {\n    (void)cache;\n    (void)array_index;\n    (void)time;\n    (void)functions_to_check;\n    EXPAND_PACK_LEFT_TO_RIGHT((void)args);\n    return true;\n  }\n}\n}  // namespace detail\n\n/// \\ingroup ComputationalDomainGroup\n/// Check that functions of time are up-to-date.\n///\n/// Check that functions of time in \\p CacheTag with names in \\p\n/// functions_to_check are ready at time \\p time.  If  \\p functions_to_check is\n/// a `std::nullopt`, checks all functions in \\p CacheTag.  If any function is\n/// not ready, schedules a `Parallel::SimpleActionCallback` with the GlobalCache\n/// which calls the simple action passed in as a template parameter. The `Args`\n/// are forwareded to the callback.\ntemplate <typename CacheTag, typename SimpleAction, typename Metavariables,\n          typename ArrayIndex, typename Component, typename... Args>\nbool functions_of_time_are_ready_simple_action_callback(\n    Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,\n    const Component* component_p, const double time,\n    const std::optional<std::unordered_set<std::string>>& functions_to_check,\n    Args&&... args) {\n  using ProxyType =\n      std::decay_t<decltype(::Parallel::get_parallel_component<Component>(\n          cache)[array_index])>;\n  return detail::functions_of_time_are_ready_impl<\n      CacheTag,\n      Parallel::SimpleActionCallback<SimpleAction, ProxyType, Args...>>(\n      cache, array_index, component_p, time, functions_to_check,\n      std::forward<Args>(args)...);\n}\n\n/// \\ingroup ComputationalDomainGroup\n/// Check that functions of time are up-to-date.\n///\n/// Check that functions of time in \\p CacheTag with names in \\p\n/// functions_to_check are ready at time \\p time.  If  \\p functions_to_check is\n/// a `std::nullopt`, checks all functions in \\p CacheTag.  If any function is\n/// not ready, schedules a `Parallel::ThreadedActionCallback` with the\n/// GlobalCache which calls the threaded action passed in as a template\n/// parameter. The `Args` are forwareded to the callback.\ntemplate <typename CacheTag, typename ThreadedAction, typename Metavariables,\n          typename ArrayIndex, typename Component, typename... Args>\nbool functions_of_time_are_ready_threaded_action_callback(\n    Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,\n    const Component* component_p, const double time,\n    const std::optional<std::unordered_set<std::string>>& functions_to_check,\n    Args&&... args) {\n  using ProxyType =\n      std::decay_t<decltype(::Parallel::get_parallel_component<Component>(\n          cache)[array_index])>;\n  return detail::functions_of_time_are_ready_impl<\n      CacheTag,\n      Parallel::ThreadedActionCallback<ThreadedAction, ProxyType, Args...>>(\n      cache, array_index, component_p, time, functions_to_check,\n      std::forward<Args>(args)...);\n}\n\n/// \\ingroup ComputationalDomainGroup\n/// Check that functions of time are up-to-date.\n///\n/// Check that functions of time in \\p CacheTag with names in \\p\n/// functions_to_check are ready at time \\p time.  If  \\p functions_to_check is\n/// a `std::nullopt`, checks all functions in \\p CacheTag.  If any function is\n/// not ready, schedules a `Parallel::PerformAlgorithmCallback` or\n/// `Parallel::Actions::PerformAlgorithmOnElement<false>` callback with the\n/// GlobalCache.\ntemplate <typename CacheTag, size_t Dim, typename Metavariables,\n          typename ArrayIndex, typename Component>\nbool functions_of_time_are_ready_algorithm_callback(\n    Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,\n    const Component* component_p, const double time,\n    const std::optional<std::unordered_set<std::string>>& functions_to_check =\n        std::nullopt) {\n  if constexpr (Parallel::is_dg_element_collection_v<Component>) {\n    const auto element_location =\n        static_cast<int>(Parallel::local_synchronous_action<\n                             Parallel::Actions::GetItemFromDistributedOject<\n                                 Parallel::Tags::ElementLocations<Dim>>>(\n                             Parallel::get_parallel_component<Component>(cache))\n                             ->at(array_index));\n    ASSERT(element_location == Parallel::my_node<int>(cache),\n           \"Expected to be running on node \"\n               << Parallel::my_node<int>(cache)\n               << \" but the record says it is on node \" << element_location);\n    return functions_of_time_are_ready_threaded_action_callback<\n        domain::Tags::FunctionsOfTime,\n        Parallel::Actions::PerformAlgorithmOnElement<false>>(\n        cache, element_location, component_p, time, std::nullopt, array_index);\n  } else {\n    using ProxyType =\n        std::decay_t<decltype(::Parallel::get_parallel_component<Component>(\n            cache)[array_index])>;\n    return detail::functions_of_time_are_ready_impl<\n        CacheTag, Parallel::PerformAlgorithmCallback<ProxyType>>(\n        cache, array_index, component_p, time, functions_to_check);\n  }\n}\n\nnamespace Actions {\n/// \\ingroup ComputationalDomainGroup\n/// Check that functions of time are up-to-date.\n///\n/// Wait for all functions of time in `domain::Tags::FunctionsOfTime`\n/// to be ready at `::Tags::Time`.  This ensures that the coordinates\n/// can be safely accessed in later actions without first verifying\n/// the state of the time-dependent maps.\ntemplate <size_t Dim>\nstruct CheckFunctionsOfTimeAreReady {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, ActionList /*meta*/,\n      const ParallelComponent* component) {\n    const bool ready = functions_of_time_are_ready_algorithm_callback<\n        domain::Tags::FunctionsOfTime, Dim>(cache, array_index, component,\n                                            db::get<::Tags::Time>(box));\n\n    return {ready ? Parallel::AlgorithmExecution::Continue\n                  : Parallel::AlgorithmExecution::Retry,\n            std::nullopt};\n  }\n};\n}  // namespace Actions\n\n/// \\ingroup ComputationalDomainGroup\n/// Dense-output postprocessor to check that functions of time are up-to-date.\n///\n/// Check that all functions of time in\n/// `domain::Tags::FunctionsOfTime` are ready at `::Tags::Time`.  This\n/// ensures that the coordinates can be safely accessed in later\n/// actions without first verifying the state of the time-dependent\n/// maps.  This postprocessor does not actually modify anything.\ntemplate <size_t Dim>\nstruct CheckFunctionsOfTimeAreReadyPostprocessor {\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n  static void apply() {}\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static bool is_ready(\n      const gsl::not_null<db::DataBox<DbTagsList>*> box,\n      const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ParallelComponent* component) {\n    return functions_of_time_are_ready_algorithm_callback<\n        domain::Tags::FunctionsOfTime, Dim>(cache, array_index, component,\n                                            db::get<::Tags::Time>(*box));\n  }\n};\n}  // namespace domain\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n\nnamespace Parallel::Actions {\n/*!\n * \\brief A local synchronous action that returns a pointer to the item\n * specified by the tag.\n *\n * The action uses `db::get_mutable_reference` to avoid DataBox locking\n * interference. However, this means that thread safety with respect to the\n * retrieved tag must be ensured by the user.\n */\ntemplate <typename Tag>\nstruct GetItemFromDistributedOject {\n  using return_type = typename Tag::type*;\n\n  template <typename ParallelComponent, typename DbTagList>\n  static return_type apply(\n      db::DataBox<DbTagList>& box,\n      const gsl::not_null<Parallel::NodeLock*> /*node_lock*/) {\n    return std::addressof(db::get_mutable_reference<Tag>(make_not_null(&box)));\n  }\n};\n}  // namespace Parallel::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/Goto.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// Labels a location in the action list that can be jumped to using Goto.\n///\n/// Uses:\n/// - GlobalCache: nothing\n/// - DataBox: nothing\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename Tag>\nstruct Label {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        tmpl::count_if<ActionList,\n                       std::is_same<tmpl::_1, tmpl::pin<Label<Tag>>>>::value ==\n            1,\n        \"Duplicate label\");\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\ingroup ActionsGroup\n/// Jumps to a Label.\n///\n/// Uses:\n/// - GlobalCache: nothing\n/// - DataBox: nothing\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename Tag>\nstruct Goto {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    constexpr size_t index =\n        tmpl::index_of<ActionList, Actions::Label<Tag>>::value;\n    return {Parallel::AlgorithmExecution::Continue, index};\n  }\n};\n\nnamespace Goto_detail {\n\ntemplate <typename ConditionTag>\nstruct RepeatEnd;\n\ntemplate <typename ConditionTag>\nstruct RepeatStart {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Continue,\n            db::get<ConditionTag>(box)\n                ? tmpl::index_of<ActionList, RepeatEnd<ConditionTag>>::value + 1\n                : tmpl::index_of<ActionList, RepeatStart>::value + 1};\n  }\n};\n\ntemplate <typename ConditionTag>\nstruct RepeatEnd {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {\n        Parallel::AlgorithmExecution::Continue,\n        db::get<ConditionTag>(box)\n            ? tmpl::index_of<ActionList, RepeatEnd>::value + 1\n            : tmpl::index_of<ActionList, RepeatStart<ConditionTag>>::value + 1};\n  }\n};\n\n}  // namespace Goto_detail\n\n/// Repeats the `ActionList` until `ConditionTag` is `True`.\ntemplate <typename ConditionTag, typename ActionList>\nusing RepeatUntil =\n    tmpl::flatten<tmpl::list<Goto_detail::RepeatStart<ConditionTag>, ActionList,\n                             Goto_detail::RepeatEnd<ConditionTag>>>;\n}  // namespace Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/InitializeItems.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Initialization {\nnamespace Actions {\n/*!\n * \\ingroup ActionsGroup\n * \\brief Mutate DataBox items by calling db::mutate_apply on each Mutator in\n *  the order they are specified\n *\n * There's a specialization for `InitializeItems<tmpl::list<Mutators...>>` that\n * can also be used if a `tmpl::list` is available.\n *\n * \\details In addition to the requirements specified by db::mutate_apply, each\n * Mutator must define the type aliases of this action.\n */\ntemplate <typename... Mutators>\nstruct InitializeItems {\n  /// Tags for constant items added to the GlobalCache.  These items are\n  /// initialized from input file options.\n  using const_global_cache_tags = tmpl::remove_duplicates<tmpl::flatten<\n      tmpl::append<typename Mutators::const_global_cache_tags...>>>;\n  /// Tags for mutable items added to the GlobalCache.  These items are\n  /// initialized from input file options.\n  using mutable_global_cache_tags = tmpl::remove_duplicates<tmpl::flatten<\n      tmpl::append<typename Mutators::mutable_global_cache_tags...>>>;\n  /// Tags for simple DataBox items that are initialized from input file options\n  using simple_tags_from_options = tmpl::remove_duplicates<tmpl::flatten<\n      tmpl::append<typename Mutators::simple_tags_from_options...>>>;\n  /// Tags for simple DataBox items that are default initialized.  They may be\n  /// mutated by the Mutators.\n  using simple_tags = tmpl::remove_duplicates<\n      tmpl::flatten<tmpl::append<typename Mutators::simple_tags...>>>;\n  /// Tags for immutable DataBox items (compute items or reference items).\n  using compute_tags = tmpl::remove_duplicates<\n      tmpl::flatten<tmpl::append<typename Mutators::compute_tags...>>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    EXPAND_PACK_LEFT_TO_RIGHT(db::mutate_apply<Mutators>(make_not_null(&box)));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\cond\ntemplate <typename... Mutators>\nstruct InitializeItems<tmpl::list<Mutators...>>\n    : public InitializeItems<Mutators...> {};\n/// \\endcond\n}  // namespace Actions\n}  // namespace Initialization\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/LimiterActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines actions ApplyLimiter and SendDataForLimiter\n\n#pragma once\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace Limiters {\nnamespace Tags {\n/// \\ingroup DiscontinuousGalerkinGroup\n/// \\ingroup LimitersGroup\n/// \\brief The inbox tag for limiter communication.\ntemplate <typename Metavariables>\nstruct LimiterCommunicationTag : public Parallel::InboxInserters::Map<\n                                     LimiterCommunicationTag<Metavariables>> {\n  static constexpr size_t volume_dim = Metavariables::system::volume_dim;\n  using packaged_data_t = typename Metavariables::limiter::type::PackagedData;\n  using temporal_id = typename Metavariables::temporal_id::type;\n  using type =\n      std::map<temporal_id,\n               std::unordered_map<DirectionalId<volume_dim>, packaged_data_t,\n                                  boost::hash<DirectionalId<volume_dim>>>>;\n};\n}  // namespace Tags\n\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// \\ingroup DiscontinuousGalerkinGroup\n/// \\ingroup LimitersGroup\n/// \\brief Receive limiter data from neighbors, then apply limiter.\n///\n/// Currently, is not tested for support of:\n/// - h-refinement\n/// Currently, does not support:\n/// - Local time-stepping\n///\n/// Uses:\n/// - GlobalCache:\n///   - Metavariables::limiter\n/// - DataBox:\n///   - Metavariables::limiter::type::limit_argument_tags\n///   - Metavariables::temporal_id\n///   - Tags::Element<volume_dim>\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies:\n///   - Metavariables::limiter::type::limit_tags\n///\n/// \\see SendDataForLimiter\ntemplate <typename Metavariables>\nstruct Limit {\n  using const_global_cache_tags = tmpl::list<typename Metavariables::limiter>;\n\n public:\n  using limiter_comm_tag =\n      Limiters::Tags::LimiterCommunicationTag<Metavariables>;\n  using inbox_tags = tmpl::list<limiter_comm_tag>;\n\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        not Metavariables::local_time_stepping,\n        \"Limiter communication actions do not yet support local time stepping\");\n\n    constexpr size_t volume_dim = Metavariables::system::volume_dim;\n\n    const auto& local_temporal_id =\n        db::get<typename Metavariables::temporal_id>(box);\n    auto& inbox = tuples::get<limiter_comm_tag>(inboxes);\n    const auto& received = inbox.find(local_temporal_id);\n\n    const auto& element = db::get<domain::Tags::Element<volume_dim>>(box);\n    const auto num_expected = element.neighbors().size();\n    if (num_expected > 0 and\n        (received == inbox.end() or received->second.size() != num_expected)) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    const auto& limiter = get<typename Metavariables::limiter>(cache);\n    using mutate_tags = typename Metavariables::limiter::type::limit_tags;\n    using argument_tags =\n        typename Metavariables::limiter::type::limit_argument_tags;\n    db::mutate_apply<mutate_tags, argument_tags>(limiter, make_not_null(&box),\n                                                 inbox[local_temporal_id]);\n\n    inbox.erase(local_temporal_id);\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\ingroup ActionsGroup\n/// \\ingroup DiscontinuousGalerkinGroup\n/// \\ingroup LimitersGroup\n/// \\brief Send local data needed for limiting.\n///\n/// Currently, is not tested for support of:\n/// - h-refinement\n/// Currently, does not support:\n/// - Local time-stepping\n///\n/// Uses:\n/// - GlobalCache:\n///   - Metavariables::limiter\n/// - DataBox:\n///   - Tags::Element<volume_dim>\n///   - Metavariables::limiter::type::package_argument_tags\n///   - Metavariables::temporal_id\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: nothing\n///\n/// \\see ApplyLimiter\ntemplate <typename Metavariables>\nstruct SendData {\n  using const_global_cache_tags = tmpl::list<typename Metavariables::limiter>;\n  using limiter_comm_tag =\n      Limiters::Tags::LimiterCommunicationTag<Metavariables>;\n\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    constexpr size_t volume_dim = Metavariables::system::volume_dim;\n\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    const auto& element = db::get<domain::Tags::Element<volume_dim>>(box);\n    const auto& temporal_id = db::get<typename Metavariables::temporal_id>(box);\n    const auto& limiter = get<typename Metavariables::limiter>(cache);\n\n    for (const auto& direction_neighbors : element.neighbors()) {\n      const auto& direction = direction_neighbors.first;\n      const size_t dimension = direction.dimension();\n      const auto& neighbors_in_direction = direction_neighbors.second;\n      ASSERT(neighbors_in_direction.size() == 1,\n             \"h-adaptivity is not supported yet.\\nDirection: \"\n                 << direction << \"\\nDimension: \" << dimension\n                 << \"\\nNeighbors:\\n\"\n                 << neighbors_in_direction);\n      const auto& neighbor_id = *(neighbors_in_direction.cbegin());\n      const auto& orientation = neighbors_in_direction.orientation(neighbor_id);\n      const auto direction_from_neighbor = orientation(direction.opposite());\n\n      using argument_tags =\n          typename Metavariables::limiter::type::package_argument_tags;\n      const auto packaged_data = db::apply<argument_tags>(\n          [&limiter](const auto&... args) {\n            // Note: orientation is received as last element of pack `args`\n            typename Metavariables::limiter::type::PackagedData pack{};\n            limiter.package_data(make_not_null(&pack), args...);\n            return pack;\n          },\n          box, orientation);\n\n      for (const auto& neighbor : neighbors_in_direction) {\n        Parallel::receive_data<limiter_comm_tag>(\n            receiver_proxy[neighbor], temporal_id,\n            std::make_pair(DirectionalId<volume_dim>{direction_from_neighbor,\n                                                     element.id()},\n                           packaged_data));\n\n      }  // loop over neighbors_in_direction\n    }    // loop over element.neighbors()\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace Limiters\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/MemoryMonitor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ContributeMemoryData.hpp\n  ProcessArray.hpp\n  ProcessGroups.hpp\n  ProcessSingleton.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/MemoryMonitor/ContributeMemoryData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <numeric>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/MemoryMonitor/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace mem_monitor {\n/*!\n * \\brief Simple action meant to be run on the MemoryMonitor component that\n * collects sizes from Groups and Nodegroups.\n *\n * \\details This action collects the sizes of all the local branches of a group\n * or nodegroup component, computes the total memory usage on a node for each,\n * then writes it to disk. For groups, the proc with the maximum memory usage is\n * also reported along with the size on the proc.\n *\n * The columns in the dat file for a nodegroup when running on 3 nodes will be\n *\n * - %Time\n * - Size on node 0 (MB)\n * - Size on node 1 (MB)\n * - Size on node 2 (MB)\n * - Average size per node (MB)\n *\n * The columns in the dat file for a group when running on 3 nodes will be\n *\n * - %Time\n * - Size on node 0 (MB)\n * - Size on node 1 (MB)\n * - Size on node 2 (MB)\n * - Proc of max size\n * - Size on proc of max size (MB)\n * - Average size per node (MB)\n *\n * The dat file will be placed in the `/MemoryMonitors/` group in the reduction\n * file. The name of the dat file is the `pretty_type::name` of the component.\n */\ntemplate <typename ContributingComponent>\nstruct ContributeMemoryData {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const double time,\n                    const int node_or_proc, const double size_in_megabytes) {\n    static_assert(Parallel::is_group_v<ContributingComponent> or\n                  Parallel::is_nodegroup_v<ContributingComponent>);\n\n    using tag = Tags::MemoryHolder;\n    db::mutate<tag>(\n        [&cache, &time, &node_or_proc, &size_in_megabytes](\n            const gsl::not_null<std::unordered_map<\n                std::string,\n                std::unordered_map<double, std::unordered_map<int, double>>>*>\n                memory_holder_all) {\n          auto memory_holder_pair = memory_holder_all->try_emplace(\n              pretty_type::name<ContributingComponent>());\n          auto& memory_holder = (*memory_holder_pair.first).second;\n\n          memory_holder.try_emplace(time);\n          memory_holder.at(time)[node_or_proc] = size_in_megabytes;\n\n          // If we have received data for every node/proc at a given\n          // time, get all the data, write it to disk, then remove the current\n          // time from the stored times as it's no longer needed\n\n          auto& mem_monitor_proxy =\n              Parallel::get_parallel_component<MemoryMonitor<Metavariables>>(\n                  cache);\n\n          constexpr bool is_group = Parallel::is_group_v<ContributingComponent>;\n\n          const size_t num_nodes = Parallel::number_of_nodes<size_t>(\n              *Parallel::local(mem_monitor_proxy));\n          const size_t num_procs = Parallel::number_of_procs<size_t>(\n              *Parallel::local(mem_monitor_proxy));\n          const size_t expected_number = is_group ? num_procs : num_nodes;\n          ASSERT(memory_holder.at(time).size() <= expected_number,\n                 \"ContributeMemoryData received more data than it was \"\n                 \"expecting. Was expecting \"\n                     << expected_number << \" calls but instead got \"\n                     << memory_holder.at(time).size());\n          if (memory_holder.at(time).size() == expected_number) {\n            // First column is always time\n            std::vector<double> data_to_append{time};\n            std::vector<std::string> legend{{\"Time\"}};\n\n            // Append a column for each node, and keep track of cumulative\n            // total. If we have proc data (from groups) do an additional loop\n            // over the procs to get the total on that node and get the proc\n            // of the maximum memory usage\n            double avg_size_per_node = 0.0;\n            double max_usage_on_proc = -std::numeric_limits<double>::max();\n            int proc_of_max = 0;\n            for (size_t node = 0; node < num_nodes; node++) {\n              double size_on_node = 0.0;\n              if (not is_group) {\n                size_on_node = memory_holder.at(time).at(node);\n              } else {\n                const int first_proc = Parallel::first_proc_on_node<int>(\n                    node, *Parallel::local(mem_monitor_proxy));\n                const int procs_on_node = Parallel::procs_on_node<int>(\n                    node, *Parallel::local(mem_monitor_proxy));\n                const int last_proc = first_proc + procs_on_node;\n                for (int proc = first_proc; proc < last_proc; proc++) {\n                  size_on_node += memory_holder.at(time).at(proc);\n                  if (memory_holder.at(time).at(proc) > max_usage_on_proc) {\n                    max_usage_on_proc = memory_holder.at(time).at(proc);\n                    proc_of_max = proc;\n                  }\n                }\n              }\n\n              data_to_append.push_back(size_on_node);\n              avg_size_per_node += size_on_node;\n              legend.emplace_back(\"Size on node \" + get_output(node) + \" (MB)\");\n            }\n\n            // If we have proc data, write the proc with the maximum usage to\n            // disk along with how much memory it's using\n            if (is_group) {\n              data_to_append.push_back(static_cast<double>(proc_of_max));\n              data_to_append.push_back(max_usage_on_proc);\n              legend.emplace_back(\"Proc of max size\");\n              legend.emplace_back(\"Size on proc of max size (MB)\");\n            }\n\n            avg_size_per_node /= static_cast<double>(num_nodes);\n\n            // Last column is average over all nodes\n            data_to_append.push_back(avg_size_per_node);\n            legend.emplace_back(\"Average size per node (MB)\");\n\n            auto& observer_writer_proxy = Parallel::get_parallel_component<\n                observers::ObserverWriter<Metavariables>>(cache);\n\n            Parallel::threaded_action<\n                observers::ThreadedActions::WriteReductionDataRow>(\n                // Node 0 is always the writer\n                observer_writer_proxy[0], subfile_name<ContributingComponent>(),\n                legend, std::make_tuple(data_to_append));\n\n            // Clean up finished time\n            auto finished_time_iter = memory_holder.find(time);\n            memory_holder.erase(finished_time_iter);\n          }\n        },\n        make_not_null(&box));\n  }\n};\n}  // namespace mem_monitor\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/MemoryMonitor/ProcessArray.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace mem_monitor {\n/*!\n * \\brief Simple action meant to be used as a callback for\n * Parallel::contribute_to_reduction that writes the size of an Array parallel\n * component to disk.\n *\n * \\details The columns in the dat file when running on 3 nodes will be\n *\n * - %Time\n * - Size on node 0 (MB)\n * - Size on node 1 (MB)\n * - Size on node 2 (MB)\n * - Average size per node (MB)\n *\n * The dat file will be placed in the `/MemoryMonitors/` group in the reduction\n * file. The name of the dat file is the `pretty_type::name` of the component.\n */\ntemplate <typename ArrayComponent>\nstruct ProcessArray {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const double time,\n                    const std::vector<double>& size_per_node) {\n    auto& observer_writer_proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    std::vector<std::string> legend{{\"Time\"}};\n    for (size_t i = 0; i < size_per_node.size(); i++) {\n      legend.emplace_back(\"Size on node \" + get_output(i) + \" (MB)\");\n    }\n    legend.emplace_back(\"Average size per node (MB)\");\n\n    const double avg_size = alg::accumulate(size_per_node, 0.0) /\n                            static_cast<double>(size_per_node.size());\n\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        // Node 0 is always the writer\n        observer_writer_proxy[0], subfile_name<ArrayComponent>(), legend,\n        std::make_tuple(time, size_per_node, avg_size));\n  }\n};\n}  // namespace mem_monitor\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/MemoryMonitor/ProcessGroups.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace mem_monitor {\n/*!\n * \\brief Simple action meant to be run on every branch of a Group or NodeGroup\n * that computes the size of the local branch and reports that size to the\n * MemoryMonitor using the ContributeMemoryData simple action.\n */\nstruct ProcessGroups {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index, const double time) {\n    static_assert(Parallel::is_group_v<ParallelComponent> or\n                      Parallel::is_nodegroup_v<ParallelComponent>,\n                  \"ProcessGroups can only be run on Group or Nodegroup \"\n                  \"parallel components.\");\n    static_assert(std::is_same_v<ArrayIndex, int>,\n                  \"ArrayIndex of Group or Nodegroup parallel components must \"\n                  \"be an int to use the ProcessGroups action.\");\n\n    auto& singleton_proxy =\n        Parallel::get_parallel_component<MemoryMonitor<Metavariables>>(cache);\n\n    auto& group_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    const double size_in_bytes = static_cast<double>(\n        size_of_object_in_bytes(*Parallel::local_branch(group_proxy)));\n\n    const double size_in_MB = size_in_bytes / 1.0e6;\n\n    // Note that we don't call Parallel::contribute_to_reduction here. This is\n    // because Charm requires that all calls to contribute_to_reduction happen\n    // in the exact same order every time, otherwise it is undefined behavior.\n    // However, this simple action (ProcessGroups) is called on each branch\n    // of a group or nodegroup. Because this is a simple action, the order that\n    // the branches run the simple actions is completely random based on\n    // communication patterns in charm. Thus, calling contribute_to_reduction\n    // here would result in undefined behavior.\n    // Also note that `array_index` here is my_node for nodegroups and my_proc\n    // for groups\n    Parallel::simple_action<ContributeMemoryData<ParallelComponent>>(\n        singleton_proxy, time, array_index, size_in_MB);\n  }\n};\n}  // namespace mem_monitor\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/MemoryMonitor/ProcessSingleton.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace mem_monitor {\n/*!\n * \\brief Simple action meant to be run on a Singleton that writes the size of\n * the Singleton component to disk.\n *\n * \\details The columns in the dat file are\n *\n * - %Time\n * - Proc\n * - Size (MB)\n *\n * The dat file will be placed in the `/MemoryMonitors/` group in the reduction\n * file. The name of the dat file is the `pretty_type::name` of the component.\n */\nstruct ProcessSingleton {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const double time) {\n    static_assert(\n        Parallel::is_singleton_v<ParallelComponent>,\n        \"ProcessSingleton can only be run on a Singleton parallel component.\");\n    auto& singleton_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    const double size_in_bytes = static_cast<double>(\n        size_of_object_in_bytes(*Parallel::local(singleton_proxy)));\n    const double size_in_MB = size_in_bytes / 1.0e6;\n\n    const std::vector<std::string> legend{{\"Time\", \"Proc\", \"Size (MB)\"}};\n\n    auto& observer_writer_proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        // Node 0 is always the writer\n        observer_writer_proxy[0], subfile_name<ParallelComponent>(), legend,\n        std::make_tuple(\n            time, Parallel::my_proc<size_t>(*Parallel::local(singleton_proxy)),\n            size_in_MB));\n  }\n};\n}  // namespace mem_monitor\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/MutateApply.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Actions {\nnamespace detail {\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(simple_tags)\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(compute_tags)\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(const_global_cache_tags)\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(mutable_global_cache_tags)\n}  // namespace detail\n/*!\n * \\ingroup ActionsGroup\n * \\brief Apply the function `Mutator::apply` to the DataBox\n *\n * The function `Mutator::apply` is invoked with the `Mutator::argument_tags`.\n * The result of this computation is stored in the `Mutator::return_tags`.\n *\n * Uses:\n * - DataBox:\n *   - All elements in `Mutator::argument_tags`\n *\n * DataBox changes:\n * - Modifies:\n *   - All elements in `Mutator::return_tags`\n */\ntemplate <typename Mutator>\nstruct MutateApply {\n  using simple_tags =\n      detail::get_simple_tags_or_default_t<Mutator, tmpl::list<>>;\n  using compute_tags =\n      detail::get_compute_tags_or_default_t<Mutator, tmpl::list<>>;\n  using const_global_cache_tags =\n      detail::get_const_global_cache_tags_or_default_t<Mutator, tmpl::list<>>;\n  using mutable_global_cache_tags =\n      detail::get_mutable_global_cache_tags_or_default_t<Mutator, tmpl::list<>>;\n\n  template <typename DataBox, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      DataBox& box, const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<Mutator>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/RandomizeVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <random>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\nnamespace tuples {\ntemplate <typename... Tags>\nstruct TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace Actions {\n\n/*!\n * \\brief Optionally add random noise to the initial guess.\n *\n * Use this action to add random noise to variables. The random noise can be\n * toggled and is configurable with input-file options. Adding random noise to\n * fields can be useful to test the convergence and stability.\n */\ntemplate <typename VariablesTag, typename Label>\nstruct RandomizeVariables {\n public:\n  // Bundle the options so they can be placed in Options::Auto\n  struct RandomParameters {\n    struct Amplitude {\n      using type = double;\n      static constexpr Options::String help = \"Amplitude of the uniform noise.\";\n    };\n    struct Seed {\n      using type = Options::Auto<size_t>;\n      static constexpr Options::String help =\n          \"Random seed for the noise generator.\";\n    };\n    using options = tmpl::list<Amplitude, Seed>;\n    static constexpr Options::String help = \"Parameters for the uniform noise.\";\n    void pup(PUP::er& p) {\n      p | amplitude;\n      p | seed;\n    }\n    double amplitude;\n    std::optional<size_t> seed;\n  };\n\n  struct RandomParametersOptionTag {\n    static std::string name() { return pretty_type::name<Label>(); }\n    using type = Options::Auto<RandomParameters, Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        \"Add uniform random noise to variables.\";\n  };\n\n  struct RandomParametersTag : db::SimpleTag {\n    using type = std::optional<RandomParameters>;\n    using option_tags = tmpl::list<RandomParametersOptionTag>;\n    static constexpr bool pass_metavariables = false;\n    static type create_from_options(const type& value) { return value; }\n  };\n\n  using const_global_cache_tags = tmpl::list<RandomParametersTag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Retrieve the options\n    const std::optional<RandomParameters>& params =\n        db::get<RandomParametersTag>(box);\n    if (not params.has_value()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    const auto& [amplitude, seed] = params.value();\n    // Seed a random generator. Include the element ID in the seed so the data\n    // is different on each element.\n    std::seed_seq seeds{std::hash<ElementId<Dim>>{}(element_id),\n                        seed.value_or(std::random_device{}())};\n    std::mt19937 generator{seeds};\n    // Set up the random distribution\n    std::uniform_real_distribution<> dist(-amplitude, amplitude);\n    // Add noise to the fields\n    db::mutate<VariablesTag>(\n        [&generator, &dist](const auto fields) {\n          for (size_t i = 0; i < fields->size(); ++i) {\n            fields->data()[i] += dist(generator);\n          }\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/SetData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Mutate the DataBox tags in `TagsList` according to the `data`.\n *\n * An example use case for this action is as the callback for the\n * `importers::ThreadedActions::ReadVolumeData`.\n *\n * DataBox changes:\n * - Modifies:\n *   - All tags in `TagsList`\n */\ntemplate <typename TagsList>\nstruct SetData;\n\n/// \\cond\ntemplate <typename... Tags>\nstruct SetData<tmpl::list<Tags...>> {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(DataBox& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    tuples::TaggedTuple<Tags...> data) {\n    tmpl::for_each<tmpl::list<Tags...>>([&box, &data](auto tag_v) {\n      using tag = tmpl::type_from<decltype(tag_v)>;\n      db::mutate<tag>(\n          [&data](const gsl::not_null<typename tag::type*> value) {\n            *value = std::move(tuples::get<tag>(data));\n          },\n          make_not_null(&box));\n    });\n  }\n};\n/// \\endcond\n\n}  // namespace Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/TerminatePhase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"Parallel/AlgorithmExecution.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename... InboxTags>\nstruct TaggedTuple;\n}  // namespace tuples\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Parallel {\nnamespace Actions {\n\n/*!\n * \\ingroup ActionsGroup\n * \\brief Terminate the algorithm to proceed to the next phase.\n */\nstruct TerminatePhase {\n  template <typename DataBox, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      DataBox& /*box*/, const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ActionList /*meta*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n}  // namespace Actions\n}  // namespace Parallel\n"
  },
  {
    "path": "src/ParallelAlgorithms/Actions/UpdateMessageQueue.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// \\brief Add data to a LinkedMessageQueue\n///\n/// Add the passed `id_and_previous` and `message` to the queue\n/// `QueueTag` in the `LinkedMessageQueue` stored in the DataBox tag\n/// `LinkedMessageQueueTag`.  Then, for each ID for which the message\n/// queue has all required messages, call `Processor::apply`.  The\n/// signature for an `apply` function for a message queue containing\n/// `Queue1` and `Queue2` with ID type `int` is:\n///\n/// \\snippet Test_UpdateMessageQueue.cpp Processor::apply\ntemplate <typename LinkedMessageQueueTag, typename Processor,\n          typename... QueueTags>\nstruct UpdateMessageQueue {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTags>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index,\n      const LinkedMessageId<typename LinkedMessageQueueTag::type::IdType>&\n          id_and_previous,\n      typename QueueTags::type... messages) {\n    if (not domain::functions_of_time_are_ready_simple_action_callback<\n            domain::Tags::FunctionsOfTime, UpdateMessageQueue>(\n            cache, array_index, std::add_pointer_t<ParallelComponent>{nullptr},\n            id_and_previous.id, std::nullopt, id_and_previous,\n            std::move(messages)...)) {\n      return;\n    }\n    auto& queue =\n        db::get_mutable_reference<LinkedMessageQueueTag>(make_not_null(&box));\n    queue.template insert<QueueTags...>(id_and_previous,\n                                        std::move(messages)...);\n    while (auto id = queue.next_ready_id()) {\n      Processor::apply(make_not_null(&box), cache, array_index, *id,\n                       queue.extract());\n    }\n  }\n};\n}  // namespace Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/Actions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup AmrGroup\nnamespace amr {\n/// \\brief %Actions for adaptive mesh refinement\nnamespace Actions {}\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/AdjustDomain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/NewNeighborIds.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Tags/DistributedObjectTags.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateParent.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr {\n/// \\cond\ntemplate <class Metavariables>\nstruct Component;\n/// \\endcond\n}  // namespace amr\n\nnamespace detail {\ntemplate <typename ListOfProjectors>\nstruct GetMutatedTags;\n\ntemplate <typename... Projectors>\nstruct GetMutatedTags<tmpl::list<Projectors...>> {\n  using type = tmpl::remove_duplicates<\n      tmpl::flatten<tmpl::append<typename Projectors::return_tags...>>>;\n};\n}  // namespace detail\n\nnamespace amr::Actions {\n/// \\brief Adjusts the domain given the refinement criteria\n///\n/// \\details\n/// - Checks if an Element wants to split in any dimension; if yes, determines\n///   the ElementId%s of the new children Element%s and calls\n///   amr::Actions::CreateChild on the amr::Component, then exits the action.\n/// - Checks if an Element wants to join in any dimension; if yes, either calls\n///   amr::Actions::CreateParent on the amr::Component if this is the child\n///   Element that should create the parent Element or does nothing, and then\n///   exits the action.\n/// - Checks if an Element wants to increase or decrease its resolution, if yes,\n///   mutates the Mesh\n/// - Updates the Neighbors of the Element\n/// - Resets amr::Tags::Flag%s to amr::Flag::Undefined\n/// - Resets amr::Tags::NeighborInfo to an empty map\n/// - Mutates all return_tags of Metavariables::amr::projectors\nstruct AdjustDomain {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ElementId<Metavariables::volume_dim>& element_id) {\n    if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {\n      ERROR(\"Can't adjust the domain for DG elements running on nodegroups.\");\n    } else {\n      constexpr size_t volume_dim = Metavariables::volume_dim;\n      using amr_projectors = typename Metavariables::amr::projectors;\n      static_assert(\n          tmpl::all<amr_projectors,\n                    tt::assert_conforms_to<tmpl::_1,\n                                           amr::protocols::Projector>>::value,\n          \"All AMR projectors must conform to 'amr::protocols::Projector'.\");\n\n      // To prevent bugs when new mutable items are added to a DataBox, we\n      // require that all mutable_item_creation_tags of box are either:\n      // - mutated by one of the projectors in Metavariables::amr::projectors\n      // - in the list of distributed_object_tags\n      // - or in the list of tags mutated by this action\n      using distributed_object_tags =\n          typename ::Parallel::Tags::distributed_object_tags<\n              Metavariables, ElementId<volume_dim>>;\n      using tags_mutated_by_this_action = tmpl::list<\n          ::domain::Tags::Element<volume_dim>, ::domain::Tags::Mesh<volume_dim>,\n          ::domain::Tags::NeighborMesh<volume_dim>, amr::Tags::Info<volume_dim>,\n          amr::Tags::NeighborInfo<volume_dim>, amr::Tags::ParentId<volume_dim>,\n          amr::Tags::ChildIds<volume_dim>, amr::Tags::ParentMesh<volume_dim>,\n          Parallel::Tags::Section<ParallelComponent, amr::Tags::GridIndex>,\n          Parallel::Tags::Section<ParallelComponent, amr::Tags::IsFinestGrid>>;\n      using mutated_tags =\n          tmpl::append<distributed_object_tags, tags_mutated_by_this_action,\n                       typename detail::GetMutatedTags<amr_projectors>::type>;\n      using mutable_tags =\n          typename db::DataBox<DbTagList>::mutable_item_creation_tags;\n      using mutable_tags_not_mutated =\n          tmpl::list_difference<mutable_tags, mutated_tags>;\n      static_assert(\n          std::is_same_v<mutable_tags_not_mutated, tmpl::list<>>,\n          \"All mutable tags in the DataBox must be explicitly mutated \"\n          \"by an amr::projector.  Default initialized objects can use \"\n          \"amr::projector::DefaultInitialize.\");\n\n      const auto& my_amr_info = db::get<amr::Tags::Info<volume_dim>>(box);\n      const auto& my_amr_flags = my_amr_info.flags;\n      auto& element_array =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      auto& amr_component =\n          Parallel::get_parallel_component<amr::Component<Metavariables>>(\n              cache);\n      const auto& phase_bookmarks =\n          Parallel::local(element_array[element_id])->phase_bookmarks();\n      const auto& verbosity =\n          db::get<logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>(box);\n\n      if (alg::all_of(my_amr_flags, [](amr::Flag flag) {\n            return flag == amr::Flag::Undefined;\n          })) {\n        // AMR flags are undefined. This state can be reached when the AMR\n        // component broadcasts the `AdjustDomain` simple action to the entire\n        // element array, then some of those elements run the simple action and\n        // create new child elements, and then the broadcast also arrives at\n        // these new elements for some reason (seems like a Charm++ bug). The\n        // AMR flags will be undefined in this case, so we just ignore the\n        // broadcast and return early here.\n        return;\n      } else if (alg::any_of(my_amr_flags, [](amr::Flag flag) {\n                   return flag == amr::Flag::Split;\n                 })) {\n        // h-refinement\n        using ::operator<<;\n        ASSERT(alg::count(my_amr_flags, amr::Flag::Join) == 0,\n               \"Element \" << element_id\n                          << \" cannot both split and join, but had AMR flags \"\n                          << my_amr_flags << \"\\n\");\n        const size_t child_grid_index = Metavariables::amr::keep_coarse_grids\n                                            ? element_id.grid_index() + 1\n                                            : element_id.grid_index();\n        auto children_ids =\n            amr::ids_of_children(element_id, my_amr_flags, child_grid_index);\n        if (verbosity >= Verbosity::Debug) {\n          Parallel::printf(\"Splitting element %s into %zu: %s\\n\", element_id,\n                           children_ids.size(), children_ids);\n        }\n        Parallel::simple_action<CreateChild>(amr_component, element_array,\n                                             element_id, children_ids, 0_st,\n                                             phase_bookmarks);\n        return;\n      } else if (alg::any_of(my_amr_flags, [](amr::Flag flag) {\n                   return flag == amr::Flag::Join;\n                 })) {\n        // h-coarsening\n        if (Metavariables::amr::keep_coarse_grids) {\n          ERROR(\n              \"When AMR keeps coarse grids then no h-coarsening during AMR is \"\n              \"allowed, but element \"\n              << element_id\n              << \" requested h-coarsening. Set the 'AllowCoarsening' policy to \"\n                 \"false to disable coarsening.\");\n          // Note: this restriction could be relaxed if ever needed.\n          return;\n        }\n        // Only one element should create the new parent\n        if (amr::is_child_that_creates_parent(element_id, my_amr_flags)) {\n          auto parent_id = amr::id_of_parent(element_id, my_amr_flags);\n          const auto& element =\n              db::get<::domain::Tags::Element<volume_dim>>(box);\n          auto ids_to_join =\n              amr::ids_of_joining_neighbors(element, my_amr_flags);\n          if (verbosity >= Verbosity::Debug) {\n            Parallel::printf(\"Joining %zu elements: %s -> %s\\n\",\n                             ids_to_join.size(), ids_to_join, parent_id);\n          }\n          Parallel::simple_action<CreateParent>(\n              amr_component, element_array, std::move(parent_id), element_id,\n              std::move(ids_to_join), phase_bookmarks);\n        }\n        return;\n      }\n\n      // Neither h-refinement nor h-coarsening. This element will remain.\n\n      if constexpr (Metavariables::amr::keep_coarse_grids) {\n        // Create new element that covers this one with an incremented grid\n        // index. p-refinement will be handled by the newly created element.\n        ElementId<volume_dim> new_element_id{element_id.block_id(),\n                                             element_id.segment_ids(),\n                                             element_id.grid_index() + 1};\n        Parallel::simple_action<CreateChild>(\n            amr_component, element_array, element_id,\n            std::vector<ElementId<volume_dim>>{new_element_id}, 0_st,\n            phase_bookmarks);\n        return;\n      }\n\n      const auto old_mesh_and_element =\n          std::make_pair(db::get<::domain::Tags::Mesh<volume_dim>>(box),\n                         db::get<::domain::Tags::Element<volume_dim>>(box));\n      const auto& old_mesh = old_mesh_and_element.first;\n\n      // Determine new neighbors and update the Element\n      const auto& amr_info_of_neighbors =\n          db::get<amr::Tags::NeighborInfo<volume_dim>>(box);\n      auto [new_neighbors, new_neighbor_meshes] =\n          neighbors_of_child(old_mesh_and_element.second, my_amr_info,\n                             amr_info_of_neighbors, element_id);\n      ::Initialization::mutate_assign<\n          tmpl::list<::domain::Tags::Element<volume_dim>,\n                     ::domain::Tags::NeighborMesh<volume_dim>>>(\n          make_not_null(&box),\n          Element<volume_dim>(element_id, std::move(new_neighbors)),\n          std::move(new_neighbor_meshes));\n\n      // Check for p-refinement\n      if (alg::any_of(my_amr_flags, [](amr::Flag flag) {\n            return (flag == amr::Flag::IncreaseResolution or\n                    flag == amr::Flag::DecreaseResolution);\n          })) {\n        db::mutate<::domain::Tags::Mesh<volume_dim>>(\n            [&old_mesh,\n             &my_amr_flags](const gsl::not_null<Mesh<volume_dim>*> mesh) {\n              *mesh = amr::projectors::mesh(old_mesh, my_amr_flags);\n            },\n            make_not_null(&box));\n\n        if (verbosity >= Verbosity::Debug) {\n          Parallel::printf(\n              \"Increasing order of element %s: %s -> %s\\n\", element_id,\n              old_mesh.extents(),\n              db::get<::domain::Tags::Mesh<volume_dim>>(box).extents());\n        }\n      }\n\n      // Run the projectors on all elements, even if they did no p-refinement.\n      // This allows projectors to update mutable items that depend upon the\n      // neighbors of the element.\n      tmpl::for_each<amr_projectors>(\n          [&box, &old_mesh_and_element](auto projector_v) {\n            using projector = typename decltype(projector_v)::type;\n            try {\n              db::mutate_apply<projector>(make_not_null(&box),\n                                          old_mesh_and_element);\n            } catch (std::exception& e) {\n              ERROR(\"Error in AMR projector '\"\n                    << pretty_type::get_name<projector>() << \"':\\n\"\n                    << e.what());\n            }\n          });\n\n      // Reset the AMR flags\n      db::mutate<amr::Tags::Info<volume_dim>,\n                 amr::Tags::NeighborInfo<volume_dim>>(\n          [](const gsl::not_null<amr::Info<volume_dim>*> amr_info,\n             const gsl::not_null<std::unordered_map<ElementId<volume_dim>,\n                                                    amr::Info<volume_dim>>*>\n                 local_amr_info_of_neighbors) {\n            local_amr_info_of_neighbors->clear();\n            for (size_t d = 0; d < volume_dim; ++d) {\n              amr_info->flags[d] = amr::Flag::Undefined;\n            }\n          },\n          make_not_null(&box));\n    }\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Actions.hpp\n  AdjustDomain.hpp\n  CollectDataFromChildren.hpp\n  Component.hpp\n  CreateChild.hpp\n  CreateParent.hpp\n  ElementsRegistration.hpp\n  EvaluateRefinementCriteria.hpp\n  Initialization.hpp\n  Initialize.hpp\n  InitializeChild.hpp\n  InitializeParent.hpp\n  RegisterCallbacks.hpp\n  RunAmrDiagnostics.hpp\n  SendAmrDiagnostics.hpp\n  SendDataToChildren.hpp\n  UpdateAmrDecision.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <deque>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Parallel/ElementRegistration.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/InitializeParent.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Actions {\n/// \\brief Collects data from child elements to send to their parent element\n/// during adaptive mesh refinement\nstruct CollectDataFromChildren {\n  /// \\brief  This function should be called after the parent element has been\n  /// created by amr::Actions::CreateParent.\n  ///\n  /// \\details This function sends a copy of all items corresponding to the\n  /// mutable_item_creation_tags of `box` of `child_id` to the first sibling in\n  /// `sibling_ids_to_collect` by invoking this action.  Finally, the child\n  /// element destroys itself.\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables>\n  static void apply(\n      db::DataBox<DbTagList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Metavariables::volume_dim>& child_id,\n      const ElementId<Metavariables::volume_dim>& parent_id,\n      std::deque<ElementId<Metavariables::volume_dim>> sibling_ids_to_collect) {\n    std::unordered_map<ElementId<Metavariables::volume_dim>,\n                       tuples::tagged_tuple_from_typelist<typename db::DataBox<\n                           DbTagList>::mutable_item_creation_tags>>\n        children_data{};\n    children_data.emplace(\n        child_id,\n        db::copy_items<\n            typename db::DataBox<DbTagList>::mutable_item_creation_tags>(box));\n    const auto next_child_id = sibling_ids_to_collect.front();\n    sibling_ids_to_collect.pop_front();\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    Parallel::simple_action<CollectDataFromChildren>(\n        array_proxy[next_child_id], parent_id, sibling_ids_to_collect,\n        std::move(children_data));\n\n    Parallel::deregister_element<ParallelComponent>(box, cache, child_id);\n    array_proxy[child_id].ckDestroy();\n  }\n\n  /// \\brief  This function should be called after a child element has added its\n  /// data to `children_data` by a previous invocation of this action.\n  ///\n  /// \\details This function adds a copy of all items corresponding to the\n  /// mutable_item_creation_tags of `box` of `child_id` to `children_data`.\n  /// In addition, it checks if there are additional siblings that need to be\n  /// added to `sibiling_ids_to_collect`.  (This is necessary as not all\n  /// siblings share a face.) If `sibling_ids_to_collect` is not empty, this\n  /// action is invoked again on the first sibling in `sibling_ids_to_collect`.\n  /// If it is empty, the data is sent to the parent element by calling\n  /// amr::Actions::InitializeParent. Finally, the child element destroys\n  /// itself.\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables>\n  static void apply(\n      db::DataBox<DbTagList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Metavariables::volume_dim>& child_id,\n      const ElementId<Metavariables::volume_dim>& parent_id,\n      std::deque<ElementId<Metavariables::volume_dim>> sibling_ids_to_collect,\n      std::unordered_map<\n          ElementId<Metavariables::volume_dim>,\n          tuples::tagged_tuple_from_typelist<\n              typename db::DataBox<DbTagList>::mutable_item_creation_tags>>\n          children_data) {\n    constexpr size_t volume_dim = Metavariables::volume_dim;\n\n    // Determine if there are additional siblings that are joining\n    // This is necessary because AMR flags are only communicated to face\n    // neighbors.\n    const auto& element = db::get<::domain::Tags::Element<volume_dim>>(box);\n    const auto& my_amr_flags = db::get<amr::Tags::Info<volume_dim>>(box).flags;\n    auto ids_to_join = amr::ids_of_joining_neighbors(element, my_amr_flags);\n    for (const auto& id_to_check : ids_to_join) {\n      if (alg::count(sibling_ids_to_collect, id_to_check) == 0 and\n          children_data.count(id_to_check) == 0) {\n        sibling_ids_to_collect.emplace_back(id_to_check);\n      }\n    }\n\n    children_data.emplace(\n        child_id,\n        db::copy_items<\n            typename db::DataBox<DbTagList>::mutable_item_creation_tags>(box));\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    if (sibling_ids_to_collect.empty()) {\n      Parallel::simple_action<InitializeParent>(array_proxy[parent_id],\n                                                std::move(children_data));\n    } else {\n      const auto next_child_id = sibling_ids_to_collect.front();\n      sibling_ids_to_collect.pop_front();\n      Parallel::simple_action<CollectDataFromChildren>(\n          array_proxy[next_child_id], parent_id, sibling_ids_to_collect,\n          std::move(children_data));\n    }\n\n    Parallel::deregister_element<ParallelComponent>(box, cache, child_id);\n    array_proxy[child_id].ckDestroy();\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/Component.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/SimpleActionOnElement.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/AdjustDomain.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/ElementsRegistration.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/EvaluateRefinementCriteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup AmrGroup\nnamespace amr {\n/// \\brief A singleton parallel component to manage adaptive mesh refinement\n///\n/// \\details This component can be used for:\n/// - Running actions that create new elements.  This may be necessary to\n///   work around Charm++ bugs, and may require the singleton to be placed\n///   on global processor 0.\n/// - As a reduction target to perform sanity checks after AMR, output\n///   AMR diagnostics, or determine when to trigger AMR.\ntemplate <class Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  static_assert(tt::assert_conforms_to_v<typename metavariables::amr,\n                                         amr::protocols::AmrMetavariables>);\n  using ElementArray = typename metavariables::amr::element_array;\n\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n\n  using const_global_cache_tags =\n      tmpl::list<amr::Criteria::Tags::Criteria,\n                 amr::Tags::AmrBlocks<volume_dim>, amr::Tags::Policies,\n                 logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<amr::Actions::InitializeElementsRegistration<volume_dim>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::UpdateSections,\n          tmpl::conditional_t<\n              metavariables::amr::keep_coarse_grids,\n              tmpl::list<::amr::Actions::UpdateSections<ElementArray>>,\n              tmpl::list<>>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache_proxy) {\n    auto& local_cache = *Parallel::local_branch(global_cache_proxy);\n    Parallel::get_parallel_component<Component>(local_cache)\n        .start_phase(next_phase);\n    auto& element_array =\n        Parallel::get_parallel_component<ElementArray>(local_cache);\n    if (Parallel::Phase::EvaluateAmrCriteria == next_phase) {\n      if constexpr (Parallel::is_dg_element_collection_v<ElementArray>) {\n        Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n            ::amr::Actions::EvaluateRefinementCriteria, true>>(element_array);\n      } else {\n        Parallel::simple_action<::amr::Actions::EvaluateRefinementCriteria>(\n            element_array);\n      }\n    }\n    if (Parallel::Phase::AdjustDomain == next_phase) {\n      if constexpr (Parallel::is_dg_element_collection_v<ElementArray>) {\n        Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n            ::amr::Actions::AdjustDomain, true>>(element_array);\n      } else {\n        Parallel::simple_action<::amr::Actions::AdjustDomain>(element_array);\n      }\n    }\n  }\n};\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/CreateChild.hpp",
    "content": "\n// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <iterator>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendDataToChildren.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\n/// \\cond\nnamespace db {\ntemplate <typename>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\nnamespace amr::Actions {\n/// \\brief Creates a new element in an ArrayAlgorithm whose id is `child_id`\n///\n/// \\details This action is meant to be initially invoked by\n/// amr::Actions::AdjustDomain on the amr::Component.  This action inserts a new\n/// element with id `children_ids[index_of_child_id]` in the array referenced by\n/// `element_proxy`.  A Parallel::SimpleActionCallback is passed to the\n/// constructor of the new DistributedObject.  If `index_of_child_id` is that of\n/// the last element of `children_ids`, the Parallel::SimpleActionCallback will\n/// invoke amr::Actions::SendDataToChildren on the element with id `parent_id`.\n/// Otherwise, it will invoke amr::Actions::CreateChild on the next element of\n/// `children_ids`.\n///\n/// This action does not modify anything in the DataBox\nstruct CreateChild {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ElementProxy>\n  static void apply(\n      db::DataBox<DbTagList>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& cache, const int /*array_index*/,\n      ElementProxy element_proxy,\n      ElementId<Metavariables::volume_dim> parent_id,\n      std::vector<ElementId<Metavariables::volume_dim>> children_ids,\n      const size_t index_of_child_id,\n      const std::unordered_map<Parallel::Phase, size_t>\n          parent_phase_bookmarks) {\n    auto my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    const ElementId<Metavariables::volume_dim>& child_id =\n        children_ids[index_of_child_id];\n    if (index_of_child_id + 1 == children_ids.size()) {\n      auto parent_proxy = element_proxy[parent_id];\n      element_proxy[child_id].insert(\n          cache.get_this_proxy(), Parallel::Phase::AdjustDomain,\n          parent_phase_bookmarks,\n          std::make_unique<Parallel::SimpleActionCallback<\n              SendDataToChildren, decltype(parent_proxy),\n              std::vector<ElementId<Metavariables::volume_dim>>>>(\n              parent_proxy, std::move(children_ids)));\n    } else {\n      element_proxy[child_id].insert(\n          cache.get_this_proxy(), Parallel::Phase::AdjustDomain,\n          parent_phase_bookmarks,\n          std::make_unique<Parallel::SimpleActionCallback<\n              CreateChild, decltype(my_proxy), ElementProxy,\n              ElementId<Metavariables::volume_dim>,\n              std::vector<ElementId<Metavariables::volume_dim>>, size_t,\n              std::unordered_map<Parallel::Phase, size_t>>>(\n              my_proxy, std::move(element_proxy), std::move(parent_id),\n              std::move(children_ids), index_of_child_id + 1,\n              parent_phase_bookmarks));\n    }\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/CreateParent.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <deque>\n#include <memory>\n#include <utility>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n/// \\cond\nnamespace db {\ntemplate <typename>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\nnamespace amr::Actions {\n/// \\brief Creates a new element in an ArrayAlgorithm whose id is `parent_id`\n///\n/// \\details This action is meant to be initially invoked by\n/// amr::Actions::AdjustDomain on the amr::Component.  This action inserts a\n/// new element with id `parent_id` in the array referenced by\n/// `element_proxy`.  A Parallel::SimpleActionCallback `callback` is passed to\n/// the constructor of the new DistributedObject, which will invoke\n/// amr::Actions::CollectDataFromChildren on the element with id `child_id`.\n///\n/// This action does not modify anything in the DataBox\nstruct CreateParent {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ElementProxy>\n  static void apply(\n      db::DataBox<DbTagList>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& cache, const int /*array_index*/,\n      ElementProxy element_proxy,\n      ElementId<Metavariables::volume_dim> parent_id,\n      const ElementId<Metavariables::volume_dim>& child_id,\n      std::deque<ElementId<Metavariables::volume_dim>> sibling_ids_to_collect,\n      const std::unordered_map<Parallel::Phase, size_t> child_phase_bookmarks) {\n    if (CHARM_VERSION_MAJOR < 8 and\n        Parallel::number_of_procs<size_t>(cache) > 1) {\n      ERROR_NO_TRACE(\n          \"Dynamically creating elements in parallel executables is broken \"\n          \"until charm 8.\");\n    }\n    auto child_proxy = element_proxy[child_id];\n    element_proxy[parent_id].insert(\n        cache.get_this_proxy(), Parallel::Phase::AdjustDomain,\n        child_phase_bookmarks,\n        std::make_unique<Parallel::SimpleActionCallback<\n            CollectDataFromChildren, decltype(child_proxy),\n            ElementId<Metavariables::volume_dim>,\n            std::deque<ElementId<Metavariables::volume_dim>>>>(\n            child_proxy, std::move(parent_id),\n            std::move(sibling_ids_to_collect)));\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/ElementsRegistration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// The actions in this file keep track of all element IDs during AMR and update\n/// the array sections that represent the grid hierarchy.\n\n#pragma once\n\n#include <algorithm>\n#include <charm++.h>\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ElementRegistration.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Protocols/ElementRegistrar.hpp\"\n#include \"Parallel/Section.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr {\ntemplate <typename Metavariables>\nstruct Component;\n}  // namespace amr\n\nnamespace amr::Actions {\n\ntemplate <size_t Dim>\nstruct InitializeElementsRegistration {\n  using simple_tags = tmpl::list<Tags::AllElementIds<Dim>>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename Metavariables, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct RegisterOrDeregisterElement {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, size_t Dim>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const ElementId<Dim>& element_id,\n                    const bool register_or_deregister) {\n    db::mutate<Tags::AllElementIds<Dim>>(\n        [&element_id, register_or_deregister](const auto all_element_ids) {\n          auto& element_ids = (*all_element_ids)[element_id.grid_index()];\n          if (register_or_deregister) {\n            element_ids.insert(element_id);\n          } else {\n            element_ids.erase(element_id);\n          }\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct RegisterElement : tt::ConformsTo<Parallel::protocols::ElementRegistrar> {\n public:  // ElementRegistrar protocol\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, size_t Dim>\n  static void perform_registration(const db::DataBox<DbTagList>& /*box*/,\n                                   Parallel::GlobalCache<Metavariables>& cache,\n                                   const ElementId<Dim>& element_id) {\n    Parallel::simple_action<RegisterOrDeregisterElement>(\n        Parallel::get_parallel_component<::amr::Component<Metavariables>>(\n            cache),\n        element_id, true);\n  }\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, size_t Dim>\n  static void perform_deregistration(\n      const db::DataBox<DbTagList>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id) {\n    Parallel::simple_action<RegisterOrDeregisterElement>(\n        Parallel::get_parallel_component<::amr::Component<Metavariables>>(\n            cache),\n        element_id, false);\n  }\n\n public:  // Iterable action\n  template <typename DbTagList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent, size_t Dim>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    perform_registration<ParallelComponent>(box, cache, element_id);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct UpdateSectionsOnElement {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, size_t Dim>\n  static void apply(\n      db::DataBox<DbTagsList>& box,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const size_t grid_index,\n      Parallel::Section<ParallelComponent, Tags::GridIndex> grid_index_section,\n      std::optional<Parallel::Section<ParallelComponent, Tags::IsFinestGrid>>\n          finest_grid_section) {\n    if (grid_index != element_id.grid_index()) {\n      // Discard broadcast to elements that are not part of the section. This\n      // happens because we broadcast to all elements, not just to the section.\n      // Broadcasting to the section fails with a segfault for some reason.\n      return;\n    }\n    db::mutate<Parallel::Tags::Section<ParallelComponent, Tags::GridIndex>,\n               Parallel::Tags::Section<ParallelComponent, Tags::IsFinestGrid>>(\n        [&grid_index_section, &finest_grid_section](\n            const auto stored_grid_index_section,\n            const auto stored_finest_grid_section) {\n          // Only update the grid index section if we don't have one already\n          // because the elements in the old grid didn't change. This avoids a\n          // bug(?) with Charm++ where section reductions don't work with the\n          // new section. It's possible that this issue is with (not) updating\n          // the section cookie, but it's not clear how to do that because a\n          // multicast message is needed for that and we can't even do a\n          // broadcast to the section without a segfault.\n          if (not stored_grid_index_section->has_value()) {\n            *stored_grid_index_section = std::move(grid_index_section);\n          }\n          *stored_finest_grid_section = std::move(finest_grid_section);\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct DestroyGrid {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, size_t Dim>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ElementId<Dim>& element_id, const size_t grid_index) {\n    if (grid_index == element_id.grid_index()) {\n      // Destroy the element\n      Parallel::deregister_element<ParallelComponent>(box, cache, element_id);\n      auto& array_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      array_proxy[element_id].ckDestroy();\n      return;\n    }\n    // Unregister the parent ID if it was destroyed\n    const auto& parent_id = db::get<amr::Tags::ParentId<Dim>>(box);\n    if (parent_id.has_value() and parent_id->grid_index() == grid_index) {\n      db::mutate<amr::Tags::ParentId<Dim>, amr::Tags::ParentMesh<Dim>>(\n          [](const gsl::not_null<std::optional<ElementId<Dim>>*>\n                 stored_parent_id,\n             const gsl::not_null<std::optional<Mesh<Dim>>*>\n                 stored_parent_mesh) {\n            *stored_parent_id = std::nullopt;\n            *stored_parent_mesh = std::nullopt;\n          },\n          make_not_null(&box));\n    }\n  }\n};\n\ntemplate <typename ElementArray>\nstruct UpdateSections {\n  using const_global_cache_tags = tmpl::list<amr::Tags::MaxCoarseLevels>;\n\n  template <typename DbTagList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static constexpr size_t Dim = Metavariables::volume_dim;\n    const auto& all_element_ids = db::get<Tags::AllElementIds<Dim>>(box);\n    auto& element_array = Parallel::get_parallel_component<ElementArray>(cache);\n    const size_t finest_grid_index = std::prev(all_element_ids.end())->first;\n    const std::optional<size_t> max_coarse_levels =\n        db::get<amr::Tags::MaxCoarseLevels>(box);\n    for (const auto& [grid_index, element_ids] : all_element_ids) {\n      if (max_coarse_levels.has_value() and\n          finest_grid_index - grid_index > max_coarse_levels.value()) {\n        // Delete grids that are coarser than the maximum allowed level\n        Parallel::simple_action<DestroyGrid>(element_array, grid_index);\n        continue;\n      }\n      std::vector<CkArrayIndex> array_indices(element_ids.size());\n      std::transform(\n          element_ids.begin(), element_ids.end(), array_indices.begin(),\n          [](const ElementId<Dim>& local_element_id) {\n            return Parallel::ArrayIndex<ElementId<Dim>>(local_element_id);\n          });\n      using GridIndexSection = Parallel::Section<ElementArray, Tags::GridIndex>;\n      GridIndexSection grid_index_section{\n          grid_index, GridIndexSection::cproxy_section::ckNew(\n                          element_array.ckGetArrayID(), array_indices.data(),\n                          array_indices.size())};\n      using FinestGridSection =\n          Parallel::Section<ElementArray, Tags::IsFinestGrid>;\n      const std::optional<FinestGridSection> finest_grid_section =\n          grid_index == finest_grid_index\n              ? std::make_optional(FinestGridSection{\n                    true, FinestGridSection::cproxy_section::ckNew(\n                              element_array.ckGetArrayID(),\n                              array_indices.data(), array_indices.size())})\n              : std::nullopt;\n      // Send new sections to all elements. Broadcasting to the section fails\n      // with a segfault for some reason.\n      Parallel::simple_action<UpdateSectionsOnElement>(\n          element_array, grid_index, grid_index_section, finest_grid_section);\n    }\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/EvaluateRefinementCriteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/SimpleActionOnElement.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/UpdateAmrDecision.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/EnforcePolicies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace detail {\ntemplate <typename Criterion>\nstruct get_tags {\n  using type = typename Criterion::compute_tags_for_observation_box;\n};\n\n}  // namespace detail\n\nnamespace amr::Actions {\n/// \\brief Evaluates the refinement criteria in order to set the amr::Info of an\n/// Element and sends this information to the neighbors of the Element.\n///\n/// DataBox:\n/// - Uses:\n///   * domain::Tags::Element<volume_dim>\n///   * amr::Tags::NeighborInfo<volume_dim>\n///   * amr::Criteria::Tags::Criteria (from GlobalCache)\n///   * amr::Tags::Policies (from GlobalCache)\n///   * any tags requested by the refinement criteria\n/// - Modifies:\n///   * amr::Tags::Info<volume_dim>\n///\n/// Invokes:\n/// - UpdateAmrDecision on all neighboring Element%s\n///\n/// \\details\n/// - Evaluates each refinement criteria held by amr::Criteria::Tags::Criteria,\n///   and in each dimension selects the amr::Flag with the highest\n///   priority (i.e the highest integral value).  If\n///   Metavariables::amr::p_refine_only_in_event is true, only h-refinement\n///   criteria will be evaluated\n/// - If necessary, changes the refinement decision in order to satisfy the\n///   amr::Policies\n/// - An Element that is splitting in one dimension is not allowed to join\n///   in another dimension.  If this is requested by the refinement critiera,\n///   the decision to join is changed to do nothing\n/// - Checks if any neighbors have sent their AMR decision, and if so, calls\n///   amr:::update_amr_decision with the decision of each neighbor in\n///   order to see if the current decision needs to be updated\n/// - Sends the (possibly updated) decision to all of the neighboring Elements\nstruct EvaluateRefinementCriteria {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, size_t Dim>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ElementId<Dim>& element_id) {\n    if constexpr (Metavariables::amr::keep_coarse_grids) {\n      // Only evaluate the criteria on the finest grid. The other elements keep\n      // their flags as Undefined and will therefore be skipped by AdjustDomain.\n      const bool is_finest_grid =\n          db::get<amr::Tags::ChildIds<Dim>>(box).empty();\n      if (not is_finest_grid) {\n        return;\n      }\n    }\n\n    constexpr size_t volume_dim = Metavariables::volume_dim;\n    auto overall_decision = make_array<volume_dim>(amr::Flag::Undefined);\n\n    // Evaluate criteria on blocks that have AMR enabled, and \"do nothing\" on\n    // the other blocks.\n    // Note that blocks that do not have AMR enabled can still be refined to\n    // satisfy 2:1 balance. When this happens, they won't coarsen back since \"do\n    // nothing\" has higher priority than coarsening. This behavior can be\n    // changed if needed.\n    const auto& amr_blocks = db::get<amr::Tags::AmrBlocks<volume_dim>>(box);\n    if (not amr_blocks.has_value() or\n        alg::found(amr_blocks.value(), element_id.block_id())) {\n      using compute_tags =\n          tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n              tmpl::at<\n                  typename Metavariables::factory_creation::factory_classes,\n                  Criterion>,\n              detail::get_tags<tmpl::_1>>>>;\n      auto observation_box =\n          make_observation_box<compute_tags>(make_not_null(&box));\n\n      const auto& refinement_criteria =\n          db::get<amr::Criteria::Tags::Criteria>(box);\n      for (const auto& criterion : refinement_criteria) {\n        if (Metavariables::amr::p_refine_only_in_event and\n            criterion->type() == amr::Criteria::Type::p) {\n          continue;\n        }\n        auto decision = criterion->evaluate(observation_box, cache, element_id);\n        if constexpr (Metavariables::amr::p_refine_only_in_event) {\n          ASSERT(alg::none_of(decision,\n                              [](amr::Flag flag) {\n                                return flag == amr::Flag::IncreaseResolution or\n                                       flag == amr::Flag::DecreaseResolution;\n                              }),\n                 \"The criterion '\"\n                     << typeid(*criterion).name()\n                     << \"' requested p-refinement, but claims to be \"\n                        \"for h-refinement.\");\n        }\n        for (size_t d = 0; d < volume_dim; ++d) {\n          overall_decision[d] = std::max(overall_decision[d], decision[d]);\n        }\n      }\n    }  // amr_blocks.contains(element_id.block_id())\n\n    // If no refinement criteria were called, then set flag to do nothing\n    for (size_t d = 0; d < volume_dim; ++d) {\n      if (overall_decision[d] == amr::Flag::Undefined) {\n        overall_decision[d] = amr::Flag::DoNothing;\n      }\n    }\n\n    const auto& policies = db::get<amr::Tags::Policies>(box);\n    amr::enforce_policies(make_not_null(&overall_decision), policies,\n                          element_id,\n                          get<::domain::Tags::Mesh<volume_dim>>(box));\n\n    // An element cannot join if it is splitting in another dimension.\n    // Update the flags now before sending to neighbors as each time\n    // a flag is changed by UpdateAmrDecision, it sends the new flags\n    // to its neighbors.  So updating now will save some commmunication.\n    amr::prevent_element_from_joining_while_splitting(\n        make_not_null(&overall_decision));\n\n    // Check if we received any neighbor flags prior to determining our own\n    // flags.  If yes, then possible update our flags (e.g. sibling doesn't want\n    // to join, maintain 2:1 balance, etc.)\n    const auto& my_element = get<::domain::Tags::Element<volume_dim>>(box);\n    const auto& my_neighbors_amr_info =\n        get<amr::Tags::NeighborInfo<volume_dim>>(box);\n    if (not my_neighbors_amr_info.empty()) {\n      for (const auto& [neighbor_id, neighbor_amr_info] :\n           my_neighbors_amr_info) {\n        amr::update_amr_decision(\n            make_not_null(&overall_decision), my_element, neighbor_id,\n            neighbor_amr_info.flags,\n            policies.enforce_two_to_one_balance_in_normal_direction());\n      }\n    }\n\n    const auto new_mesh = amr::projectors::new_mesh(\n        get<::domain::Tags::Mesh<volume_dim>>(box), overall_decision,\n        my_element, my_neighbors_amr_info);\n\n    db::mutate<amr::Tags::Info<Metavariables::volume_dim>>(\n        [&overall_decision,\n         &new_mesh](const gsl::not_null<amr::Info<volume_dim>*> amr_info) {\n          amr_info->flags = overall_decision;\n          amr_info->new_mesh = new_mesh;\n        },\n        make_not_null(&box));\n\n    auto& amr_element_array =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    const amr::Info<Metavariables::volume_dim>& my_info =\n        get<amr::Tags::Info<volume_dim>>(box);\n    for (const auto& [direction, neighbors] : my_element.neighbors()) {\n      (void)direction;\n      for (const auto& neighbor_id : neighbors.ids()) {\n        if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {\n          const auto neighbor_location = static_cast<int>(\n              Parallel::local_synchronous_action<\n                  Parallel::Actions::GetItemFromDistributedOject<\n                      Parallel::Tags::ElementLocations<volume_dim>>>(\n                  Parallel::get_parallel_component<ParallelComponent>(cache))\n                  ->at(neighbor_id));\n          Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n              UpdateAmrDecision, true>>(amr_element_array[neighbor_location],\n                                        neighbor_id, element_id, my_info);\n        } else {\n          Parallel::simple_action<UpdateAmrDecision>(\n              amr_element_array[neighbor_id], element_id, my_info);\n        }\n      }\n    }\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/Initialization.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace amr {\n/// \\brief %Mutators used for initialization of adaptive mesh refinement\nnamespace Initialization {}\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/Initialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Initialization {\n/// \\ingroup InitializationGroup\n/// \\brief Initialize items related to adaptive mesh refinement\n///\n/// \\see InitializeItems\ntemplate <size_t Dim, typename Metavariables>\nstruct Initialize {\n  static_assert(tt::assert_conforms_to_v<typename Metavariables::amr,\n                                         amr::protocols::AmrMetavariables>);\n  using ElementArray = typename Metavariables::amr::element_array;\n\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<>;\n  using simple_tags_from_options = tmpl::list<>;\n\n  using argument_tags = tmpl::list<>;\n  using return_tags = tmpl::list<amr::Tags::Info<Dim>>;\n  using simple_tags = tmpl::append<\n      return_tags, tmpl::list<amr::Tags::NeighborInfo<Dim>>,\n      tmpl::conditional_t<\n          Metavariables::amr::keep_coarse_grids,\n          tmpl::list<\n              amr::Tags::ParentId<Dim>, amr::Tags::ChildIds<Dim>,\n              amr::Tags::ParentMesh<Dim>,\n              Parallel::Tags::Section<ElementArray, amr::Tags::GridIndex>,\n              Parallel::Tags::Section<ElementArray, amr::Tags::IsFinestGrid>>,\n          tmpl::list<>>>;\n\n  using compute_tags = tmpl::conditional_t<\n      Metavariables::amr::keep_coarse_grids,\n      tmpl::list<amr::Tags::GridIndexObservationKeyCompute<Dim>,\n                 amr::Tags::IsFinestGridObservationKeyCompute<Dim>>,\n      tmpl::list<>>;\n\n  /// Given the items fetched from a DataBox by the argument_tags, mutate\n  /// the items in the DataBox corresponding to return_tags\n  static void apply(const gsl::not_null<amr::Info<Dim>*> amr_info) {\n    amr_info->flags = make_array<Dim>(amr::Flag::Undefined);\n  }\n};\n}  // namespace amr::Initialization\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/InitializeChild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/NeighborsOfChild.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/ElementRegistration.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace amr::Actions {\n/// \\brief Initializes the data of a newly created child element from the data\n/// of its parent element\n///\n/// DataBox:\n/// - Modifies:\n///   * domain::Tags::Element<volume_dim>\n///   * domain::Tags::Mesh<volume_dim>\n///   * all return_tags of Metavariables::amr::projectors\n///\n/// \\details This action is meant to be invoked by\n/// amr::Actions::SendDataToChildren\nstruct InitializeChild {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename... Tags>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ElementId<Metavariables::volume_dim>& child_id,\n                    const tuples::TaggedTuple<Tags...>& parent_items) {\n    constexpr size_t volume_dim = Metavariables::volume_dim;\n    const auto& parent =\n        tuples::get<::domain::Tags::Element<volume_dim>>(parent_items);\n    const auto& parent_info =\n        tuples::get<amr::Tags::Info<volume_dim>>(parent_items);\n    const auto& parent_neighbor_info =\n        tuples::get<amr::Tags::NeighborInfo<volume_dim>>(parent_items);\n    const auto& parent_mesh =\n        tuples::get<::domain::Tags::Mesh<volume_dim>>(parent_items);\n    auto neighbors = amr::neighbors_of_child(parent, parent_info,\n                                             parent_neighbor_info, child_id);\n    Element<volume_dim> child(child_id, std::move(neighbors.first));\n    Mesh<volume_dim> child_mesh =\n        amr::projectors::mesh(parent_mesh, parent_info.flags);\n\n    // Default initialization of amr::Tags::Info and amr::Tags::NeighborInfo\n    // is okay\n    ::Initialization::mutate_assign<tmpl::list<\n        ::domain::Tags::Element<volume_dim>, ::domain::Tags::Mesh<volume_dim>,\n        ::domain::Tags::NeighborMesh<volume_dim>>>(\n        make_not_null(&box), std::move(child), std::move(child_mesh),\n        std::move(neighbors.second));\n\n    if constexpr (Metavariables::amr::keep_coarse_grids) {\n      if (db::get<amr::Tags::MaxCoarseLevels>(box).value_or(\n              std::numeric_limits<size_t>::max()) > 0) {\n        ::Initialization::mutate_assign<\n            tmpl::list<amr::Tags::ParentId<volume_dim>,\n                       amr::Tags::ParentMesh<volume_dim>>>(\n            make_not_null(&box), parent.id(), parent_mesh);\n      }\n    }\n\n    tmpl::for_each<typename Metavariables::amr::projectors>(\n        [&box, &parent_items](auto projector_v) {\n          using projector = typename decltype(projector_v)::type;\n          try {\n            db::mutate_apply<projector>(make_not_null(&box), parent_items);\n          } catch (std::exception& e) {\n            ERROR(\"Error in AMR projector '\"\n                  << pretty_type::get_name<projector>() << \"':\\n\"\n                  << e.what());\n          }\n        });\n\n    Parallel::register_element<ParallelComponent>(box, cache, child_id);\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/InitializeParent.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Amr/NeighborsOfParent.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/ElementRegistration.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace amr::Actions {\n/// \\brief Initializes the data of a newly created parent element from the data\n/// of its children elements\n///\n/// DataBox:\n/// - Modifies:\n///   * domain::Tags::Element<volume_dim>\n///   * domain::Tags::Mesh<volume_dim>\n///   * all return_tags of Metavariables::amr::projectors\n///\n/// \\details This action is meant to be invoked by\n/// amr::Actions::CollectDataFromChildren\nstruct InitializeParent {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables>\n  static void apply(\n      db::DataBox<DbTagList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Metavariables::volume_dim>& parent_id,\n      std::unordered_map<\n          ElementId<Metavariables::volume_dim>,\n          tuples::tagged_tuple_from_typelist<\n              typename db::DataBox<DbTagList>::mutable_item_creation_tags>>\n          children_items) {\n    constexpr size_t volume_dim = Metavariables::volume_dim;\n    std::vector<std::tuple<\n        const Element<volume_dim>&,\n        const std::unordered_map<ElementId<volume_dim>, Info<volume_dim>>&>>\n        children_elements_and_neighbor_info;\n    for (const auto& [_, child_items] : children_items) {\n      children_elements_and_neighbor_info.emplace_back(std::forward_as_tuple(\n          tuples::get<::domain::Tags::Element<volume_dim>>(child_items),\n          tuples::get<amr::Tags::NeighborInfo<volume_dim>>(child_items)));\n    }\n    auto parent_neighbors = amr::neighbors_of_parent(\n        parent_id, children_elements_and_neighbor_info);\n    Element<volume_dim> parent(parent_id, std::move(parent_neighbors.first));\n\n    std::vector<Mesh<volume_dim>> projected_children_meshes{};\n    projected_children_meshes.reserve(children_items.size());\n    for (const auto& [child_id, child_items] : children_items) {\n      const auto& child_mesh =\n          tuples::get<::domain::Tags::Mesh<volume_dim>>(child_items);\n      const auto& child_flags =\n          tuples::get<amr::Tags::Info<volume_dim>>(child_items).flags;\n      projected_children_meshes.emplace_back(\n          amr::projectors::mesh(child_mesh, child_flags));\n    }\n    Mesh<volume_dim> parent_mesh =\n        amr::projectors::parent_mesh(projected_children_meshes);\n\n    // Default initialization of amr::Tags::Info and amr::Tags::NeighborInfo\n    // is okay\n    ::Initialization::mutate_assign<tmpl::list<\n        ::domain::Tags::Element<volume_dim>, ::domain::Tags::Mesh<volume_dim>,\n        ::domain::Tags::NeighborMesh<volume_dim>>>(\n        make_not_null(&box), std::move(parent), std::move(parent_mesh),\n        std::move(parent_neighbors.second));\n\n    tmpl::for_each<typename Metavariables::amr::projectors>(\n        [&box, &children_items](auto projector_v) {\n          using projector = typename decltype(projector_v)::type;\n          try {\n            db::mutate_apply<projector>(make_not_null(&box), children_items);\n          } catch (std::exception& e) {\n            ERROR(\"Error in AMR projector '\"\n                  << pretty_type::get_name<projector>() << \"':\\n\"\n                  << e.what());\n          }\n        });\n\n    Parallel::register_element<ParallelComponent>(box, cache, parent_id);\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/RegisterCallbacks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <deque>\n#include <vector>\n\n#include \"Parallel/Callback.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendDataToChildren.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr {\ntemplate <typename Metavariables, typename Component>\nvoid register_callbacks() {\n  using ArrayIndex = typename Component::array_index;\n  register_classes_with_charm(\n      tmpl::list<\n          Parallel::SimpleActionCallback<\n              amr::Actions::CreateChild,\n              CProxy_AlgorithmSingleton<amr::Component<Metavariables>, int>,\n              CProxy_AlgorithmArray<Component, ArrayIndex>, ArrayIndex,\n              std::vector<ArrayIndex>, size_t,\n              std::unordered_map<Parallel::Phase, size_t>>,\n          Parallel::SimpleActionCallback<\n              amr::Actions::SendDataToChildren,\n              CProxyElement_AlgorithmArray<Component, ArrayIndex>,\n              std::vector<ArrayIndex>>,\n          Parallel::SimpleActionCallback<\n              amr::Actions::CollectDataFromChildren,\n              CProxyElement_AlgorithmArray<Component, ArrayIndex>, ArrayIndex,\n              std::deque<ArrayIndex>>>{});\n}\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/RunAmrDiagnostics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/rational.hpp>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/System/Abort.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace domain::Tags {\ntemplate <size_t VolumeDim>\nstruct Domain;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace amr::Actions {\n\n/// \\brief Use the AMR diagnostics gathered from all of the Element%s\n///\n/// Checks the following:\n/// - That the fraction of Block volume (in the logical coordinate frame)\n/// covered by all Element%s is equal to the number of Block%s in the Domain\n///\n/// Prints the following (as integers):\n/// - The number of elements\n/// - The number of grid points\n/// - The average refinement level by logical dimension (i.e. not by the\n///   physical dimensions)\n/// - The average number of grid points by logical dimension\nstruct RunAmrDiagnostics {\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>;\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const size_t grid_index,\n                    const boost::rational<size_t>& volume,\n                    const size_t number_of_elements,\n                    const size_t number_of_grid_points,\n                    const std::vector<double>& avg_refinement_levels_by_dim,\n                    const std::vector<double>& avg_extents_by_dim) {\n    constexpr size_t volume_dim = Metavariables::volume_dim;\n    const boost::rational<size_t> number_of_blocks{\n        db::get<::domain::Tags::Domain<volume_dim>>(box).blocks().size()};\n    if (number_of_blocks != volume) {\n      sys::abort(MakeString{} << \"Check Domain failed!  Expected volume \"\n                              << number_of_blocks << \", not \" << volume\n                              << \"\\n\");\n    }\n    if (db::get<logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>(box) >=\n        Verbosity::Quiet) {\n      const std::string title = Metavariables::amr::keep_coarse_grids\n                                    ? MakeString{} << \"AMR level \" << grid_index\n                                                   << \":\\n\"\n                                    : std::string{\"\"};\n      const std::string string_gcc_needs_to_use_in_order_for_printf_to_compile =\n          MakeString{} << \"Average refinement levels: \"\n                       << avg_refinement_levels_by_dim\n                       << \"\\nAverage grid points: \" << avg_extents_by_dim\n                       << \"\\n\";\n      Parallel::printf(\n          \"%s\"\n          \"Number of elements: %zu\\n\"\n          \"Number of grid points: %zu\\n\"\n          \"%s\\n\",\n          title, number_of_elements, number_of_grid_points,\n          string_gcc_needs_to_use_in_order_for_printf_to_compile);\n    }\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/rational.hpp>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/GetSection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/RunAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/PupBoost.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace amr::Actions {\n\n/// \\brief Send AMR diagnostics about Element%s to amr::Component\n///\n/// Sends the following:\n/// - The fraction of a Block volume (in the logical coordinate frame) covered\n///   by the Element\n/// - One, in order to count the number of Element%s\n/// - The number of grid points\n/// - The refinement level in each logical dimension\n/// - The number of grid points in each logical dimension\n///\n/// The information is sent to amr::Component which runs the action\n/// amr::Actions::RunAmrDiagnostics after all Element%s have contributed to the\n/// reduction\nstruct SendAmrDiagnostics {\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>;\n\n  using ReductionData = Parallel::ReductionData<\n      // Grid index\n      Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n      // fraction of Block volume\n      Parallel::ReductionDatum<boost::rational<size_t>, funcl::Plus<>>,\n      // number of elements\n      Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n      // number of grid points\n      Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n      // average refinement level by dimension\n      Parallel::ReductionDatum<\n          std::vector<double>, funcl::ElementWise<funcl::Plus<>>,\n          funcl::ElementWise<funcl::Divides<>>, std::index_sequence<2>>,\n      // average number of grid points by dimension\n      Parallel::ReductionDatum<\n          std::vector<double>, funcl::ElementWise<funcl::Plus<>>,\n          funcl::ElementWise<funcl::Divides<>>, std::index_sequence<2>>>;\n\n  template <typename DbTagList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* /*meta*/) {\n    if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {\n      ERROR(\"Reductions are not yet implemented for nodegroup arrays.\");\n    } else {\n      const auto& mesh = db::get<::domain::Tags::Mesh<Dim>>(box);\n      const auto& my_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(\n              cache)[element_id];\n      const auto& target_proxy =\n          Parallel::get_parallel_component<amr::Component<Metavariables>>(\n              cache);\n      std::vector<double> refinement_levels_by_dim(Dim);\n      std::vector<double> extents_by_dim(Dim);\n      const auto refinement_levels = element_id.refinement_levels();\n      for (size_t d = 0; d < Dim; ++d) {\n        refinement_levels_by_dim[d] = gsl::at(refinement_levels, d);\n        extents_by_dim[d] = mesh.extents(d);\n      }\n      if (db::get<logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>(box) >=\n          Verbosity::Debug) {\n        Parallel::printf(\"%s h-refinement %s, p-refinement %s\\n\",\n                         get_output(element_id), get_output(refinement_levels),\n                         get_output(mesh.extents()));\n        if constexpr (Metavariables::amr::keep_coarse_grids) {\n          const auto& parent_id = db::get<amr::Tags::ParentId<Dim>>(box);\n          const auto& child_ids = db::get<amr::Tags::ChildIds<Dim>>(box);\n          Parallel::printf(\"%s parent %s, children %s\\n\",\n                           get_output(element_id), get_output(parent_id),\n                           get_output(child_ids));\n        }\n      }\n      auto& grid_index_section = Parallel::get_section<\n          ParallelComponent,\n          tmpl::conditional_t<Metavariables::amr::keep_coarse_grids,\n                              amr::Tags::GridIndex, void>>(make_not_null(&box));\n      Parallel::contribute_to_reduction<amr::Actions::RunAmrDiagnostics>(\n          ReductionData{element_id.grid_index(),\n                        amr::fraction_of_block_volume(element_id), 1,\n                        mesh.number_of_grid_points(), refinement_levels_by_dim,\n                        extents_by_dim},\n          my_proxy, target_proxy, make_not_null(&grid_index_section));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/SendDataToChildren.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/ElementRegistration.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/InitializeChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n\nnamespace amr::Actions {\n/// \\brief Sends data from the parent element to its children elements during\n/// adaptive mesh refinement\n///\n/// \\details  This action should be called after all children elements have been\n/// created by amr::Actions::CreateChild.  This action sends a copy of all items\n/// corresponding to the mutable_item_creation_tags of `box` to each of the\n/// elements with `ids_of_children`.  Finally, the parent element destroys\n/// itself.\nstruct SendDataToChildren {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, size_t Dim>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ElementId<Dim>& element_id,\n                    const std::vector<ElementId<Dim>>& ids_of_children) {\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    for (const auto& child_id : ids_of_children) {\n      Parallel::simple_action<amr::Actions::InitializeChild>(\n          array_proxy[child_id],\n          db::copy_items<\n              typename db::DataBox<DbTagList>::mutable_item_creation_tags>(\n              box));\n    }\n\n    if constexpr (Metavariables::amr::keep_coarse_grids) {\n      if (db::get<amr::Tags::MaxCoarseLevels>(box).value_or(\n              std::numeric_limits<size_t>::max()) > 0) {\n        // Register the children elements in the parent element\n        Parallel::deregister_element<ParallelComponent>(box, cache, element_id);\n        ::Initialization::mutate_assign<tmpl::list<amr::Tags::ChildIds<Dim>>>(\n            make_not_null(&box),\n            std::unordered_set<ElementId<Dim>>{ids_of_children.begin(),\n                                               ids_of_children.end()});\n        Parallel::register_element<ParallelComponent>(box, cache, element_id);\n        // Note: we don't run AMR projectors on the parent element because the\n        // parent element didn't change, with the exception of\n        // `amr::Tags::ChildIds<Dim>`.\n        // Reset the AMR flags\n        db::mutate<amr::Tags::Info<Dim>, amr::Tags::NeighborInfo<Dim>>(\n            [](const gsl::not_null<amr::Info<Dim>*> amr_info,\n               const gsl::not_null<\n                   std::unordered_map<ElementId<Dim>, amr::Info<Dim>>*>\n                   amr_info_of_neighbors) {\n              amr_info_of_neighbors->clear();\n              for (size_t d = 0; d < Dim; ++d) {\n                amr_info->flags[d] = amr::Flag::Undefined;\n              }\n            },\n            make_not_null(&box));\n        return;\n      }\n    }\n\n    // Destroy the parent element\n    Parallel::deregister_element<ParallelComponent>(box, cache, element_id);\n    array_proxy[element_id].ckDestroy();\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Actions/UpdateAmrDecision.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Amr/UpdateAmrDecision.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/SimpleActionOnElement.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Actions {\n/// \\brief Given the AMR decision of a neighboring Element, potentially update\n/// the AMR decision of the target Element.\n///\n/// DataBox:\n/// - Uses:\n///   * domain::Tags::Element<volume_dim>\n/// - Modifies:\n///   * amr::Tags::NeighborInfo\n///   * amr::Tags::Info (if AMR decision is updated)\n///\n/// Invokes:\n/// - amr::Actions::UpdateAmrDecision on all neighboring Element%s (if AMR\n///   decision is updated)\n///\n/// \\details This Element calls amr::update_amr_decision to see if its\n/// AMR decision needs to be updated.  If it does, the Element will call\n/// amr::Actions::UpdateAmrDecision on its neighbors.\n///\nstruct UpdateAmrDecision {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ElementId<Metavariables::volume_dim>& /*element_id*/,\n                    const ElementId<Metavariables::volume_dim>& neighbor_id,\n                    const Info<Metavariables::volume_dim>& neighbor_amr_info) {\n    constexpr size_t volume_dim = Metavariables::volume_dim;\n    auto& my_amr_info = db::get_mutable_reference<amr::Tags::Info<volume_dim>>(\n        make_not_null(&box));\n    auto& my_neighbors_amr_info =\n        db::get_mutable_reference<amr::Tags::NeighborInfo<volume_dim>>(\n            make_not_null(&box));\n\n    // Actions can be executed in any order.  Therefore we need to check:\n    // - If we received info from a neighbor multiple times, but not in the\n    //   order they were sent.  Neighbor info should only be sent again if\n    //   the flags have changed to a higher priority (i.e. higher integral value\n    //   of the flag) or the new mesh has a larger number of grid points.\n    if (1 == my_neighbors_amr_info.count(neighbor_id)) {\n      const auto& previously_received_info =\n          my_neighbors_amr_info.at(neighbor_id);\n      if (previously_received_info.new_mesh.number_of_grid_points() >=\n              neighbor_amr_info.new_mesh.number_of_grid_points() and\n          not std::lexicographical_compare(\n              previously_received_info.flags.begin(),\n              previously_received_info.flags.end(),\n              neighbor_amr_info.flags.begin(), neighbor_amr_info.flags.end())) {\n        return;\n      }\n    }\n\n    my_neighbors_amr_info.insert_or_assign(neighbor_id, neighbor_amr_info);\n\n    auto& my_amr_flags = my_amr_info.flags;\n    // Actions can be executed in any order.  Therefore we need to check:\n    // - If we have evaluated our own AMR decision.  If not, return.\n    if (amr::Flag::Undefined == my_amr_flags[0]) {\n      using ::operator<<;\n      ASSERT(volume_dim == alg::count(my_amr_flags, amr::Flag::Undefined),\n             \"Flags should be all Undefined, not \" << my_amr_flags);\n      return;\n    }\n\n    const auto& element = get<::domain::Tags::Element<volume_dim>>(box);\n    const auto my_initial_new_mesh = my_amr_info.new_mesh;\n\n    const bool my_amr_decision_changed = amr::update_amr_decision(\n        make_not_null(&my_amr_flags), element, neighbor_id,\n        neighbor_amr_info.flags,\n        get<amr::Tags::Policies>(box)\n            .enforce_two_to_one_balance_in_normal_direction());\n\n    auto& my_new_mesh = my_amr_info.new_mesh;\n    my_new_mesh =\n        amr::projectors::new_mesh(get<::domain::Tags::Mesh<volume_dim>>(box),\n                                  my_amr_flags, element, my_neighbors_amr_info);\n\n    if (my_amr_decision_changed or my_new_mesh != my_initial_new_mesh) {\n      auto& amr_element_array =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      for (const auto& direction_neighbors : element.neighbors()) {\n        for (const auto& id : direction_neighbors.second.ids()) {\n          if constexpr (Parallel::is_dg_element_collection_v<\n                            ParallelComponent>) {\n            const auto neighbor_location = static_cast<int>(\n                Parallel::local_synchronous_action<\n                    Parallel::Actions::GetItemFromDistributedOject<\n                        Parallel::Tags::ElementLocations<volume_dim>>>(\n                    Parallel::get_parallel_component<ParallelComponent>(cache))\n                    ->at(id));\n            Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n                UpdateAmrDecision, true>>(amr_element_array[neighbor_location],\n                                          id, element.id(), my_amr_info);\n          } else {\n            Parallel::simple_action<UpdateAmrDecision>(\n                amr_element_array[id], element.id(), my_amr_info);\n          }\n        }\n      }\n    }\n  }\n};\n}  // namespace amr::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ParallelAmr)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Tags.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  DomainStructure\n  INTERFACE\n  Amr\n  AmrCriteria\n  AmrPolicies\n  AmrProjectors\n  Initialization\n  Logging\n  Printf\n  Serialization\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Criteria)\nadd_subdirectory(Events)\nadd_subdirectory(Policies)\nadd_subdirectory(Projectors)\nadd_subdirectory(Protocols)\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY AmrCriteria)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Constraints.cpp\n  DriveToTarget.cpp\n  IncreaseResolution.cpp\n  Loehner.cpp\n  Persson.cpp\n  Random.cpp\n  TruncationError.cpp\n  Type.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Constraints.hpp\n  Criteria.hpp\n  Criterion.hpp\n  DriveToTarget.hpp\n  Factory.hpp\n  IncreaseResolution.hpp\n  Loehner.hpp\n  Persson.hpp\n  Random.hpp\n  TruncationError.hpp\n  Type.hpp\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_GlobalCache\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Amr\n  DomainStructure\n  Events\n  Options\n  Parallel\n  Spectral\n  Utilities\n  PRIVATE\n  LinearOperators\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Criteria/Constraints.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace amr::Criteria::Constraints_detail {\n\ntemplate <size_t Dim>\nvoid normalization_factor_square(\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::ElementLogical>*>\n        result,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian) {\n  set_number_of_grid_points(result, jacobian);\n  for (size_t k = 0; k < Dim; ++k) {\n    // Possible performance optimization: unroll first iteration of loop to\n    // avoid zeroing buffer\n    result->get(k) = 0.;\n    for (size_t i = 0; i < Dim; ++i) {\n      result->get(k) += square(jacobian.get(i, k));\n    }\n  }\n}\n\ntemplate <size_t Dim, typename TensorType>\nvoid logical_constraints(\n    const gsl::not_null<tnsr::i<DataVector, Dim, Frame::ElementLogical>*>\n        result,\n    const gsl::not_null<DataVector*> buffer,\n    const TensorType& constraints_tensor,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian,\n    const tnsr::i<DataVector, Dim, Frame::ElementLogical>&\n        normalization_factor_square) {\n  static_assert(TensorType::rank() >= 2,\n                \"The constraints tensor must have rank 2 or higher.\");\n  using first_index = tmpl::front<typename TensorType::index_list>;\n  static_assert(\n      first_index::index_type == IndexType::Spatial and\n          first_index::dim == Dim and first_index::ul == UpLo::Lo and\n          std::is_same_v<typename first_index::Frame, Frame::Inertial>,\n      \"The first index of the constraints tensor must be a lower \"\n      \"spatial index (that originates from a derivative).\");\n  using NonFirstIndexTensor =\n      TensorMetafunctions::remove_first_index<TensorType>;\n  set_number_of_grid_points(result, constraints_tensor);\n  set_number_of_grid_points(buffer, constraints_tensor);\n  for (size_t k = 0; k < Dim; ++k) {\n    result->get(k) = 0.;\n    for (size_t a = 0; a < NonFirstIndexTensor::size(); ++a) {\n      const auto non_first_index = NonFirstIndexTensor::get_tensor_index(a);\n      // Possible performance optimization: unroll first iteration of loop to\n      // avoid zeroing buffer\n      *buffer = 0.;\n      for (size_t i = 0; i < Dim; ++i) {\n        const auto full_index = prepend(non_first_index, i);\n        *buffer += jacobian.get(i, k) * constraints_tensor.get(full_index);\n      }\n      result->get(k) += square(*buffer);\n    }\n    result->get(k) = sqrt(result->get(k) / normalization_factor_square.get(k));\n  }\n}\n\ntemplate <size_t Dim>\nvoid max_over_components(\n    const gsl::not_null<std::array<Flag, Dim>*> result,\n    const tnsr::i<DataVector, Dim, Frame::ElementLogical>& logical_constraints,\n    const double abs_target, const double coarsening_factor) {\n  // We take the highest-priority refinement flag in each dimension, so if any\n  // constraint is above the target, the element will increase p refinement in\n  // that dimension. And only if all constraints are below the coarsening\n  // threshold will the element decrease p refinement in that dimension.\n  const size_t sqrt_num_points = sqrt(logical_constraints.begin()->size());\n  for (size_t d = 0; d < Dim; ++d) {\n    // Skip this dimension if we have already decided to refine it\n    if (gsl::at(*result, d) == Flag::IncreaseResolution) {\n      continue;\n    }\n    const double constraint_norm =\n        blaze::l2Norm(logical_constraints.get(d)) / sqrt_num_points;\n    // Increase p refinement if the constraint norm exceeds the target\n    if (constraint_norm > abs_target) {\n      gsl::at(*result, d) = Flag::IncreaseResolution;\n      continue;\n    }\n    // Dont' check if we want to (allow) decreasing p refinement if another\n    // tensor has already decided that decreasing p refinement is bad.\n    if (gsl::at(*result, d) == Flag::DoNothing) {\n      continue;\n    }\n    // Decrease p refinement if the constraint norm is below the coarsening\n    // threshold. Otherwise, request to stay at this resolution (or increase\n    // resolution if another constraint requested that).\n    if (constraint_norm < coarsening_factor * abs_target) {\n      gsl::at(*result, d) = Flag::DecreaseResolution;\n    } else {\n      gsl::at(*result, d) = Flag::DoNothing;\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TNSR(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template void normalization_factor_square(                       \\\n      const gsl::not_null<                                         \\\n          tnsr::i<DataVector, DIM(data), Frame::ElementLogical>*>  \\\n          result,                                                  \\\n      const Jacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                     Frame::Inertial>& jacobian);                  \\\n  template void max_over_components(                               \\\n      gsl::not_null<std::array<Flag, DIM(data)>*> result,          \\\n      const tnsr::i<DataVector, DIM(data), Frame::ElementLogical>& \\\n          logical_constraints,                                     \\\n      double abs_target, double coarsening_factor);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#define INSTANTIATE_TENSORS(_, data)                                          \\\n  template void logical_constraints(                                          \\\n      gsl::not_null<tnsr::i<DataVector, DIM(data), Frame::ElementLogical>*>   \\\n          result,                                                             \\\n      gsl::not_null<DataVector*> buffer, const tnsr::TNSR(data) < DataVector, \\\n      DIM(data), Frame::Inertial > &constraints_tensor,                       \\\n      const Jacobian<DataVector, DIM(data), Frame::ElementLogical,            \\\n                     Frame::Inertial>& jacobian,                              \\\n      const tnsr::i<DataVector, DIM(data), Frame::ElementLogical>&            \\\n          normalization_factor_square);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_TENSORS, (1, 2, 3), (ia, iaa))\n\n#undef INSTANTIATE\n#undef INSTANTIATE_TENSORS\n#undef DIM\n\n}  // namespace amr::Criteria::Constraints_detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Constraints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/ValidateSelection.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t>\nclass ElementId;\n/// \\endcond\n\nnamespace amr::Criteria {\n\nnamespace Constraints_detail {\n\n/*!\n * \\brief Computes the (squared) normalization factor $N_\\hat{k}^2$ (see\n * `logical_constraints` below)\n */\ntemplate <size_t Dim>\nvoid normalization_factor_square(\n    gsl::not_null<tnsr::i<DataVector, Dim, Frame::ElementLogical>*> result,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian);\n\n/*!\n * \\brief Computes a measure of the constraints in each logical direction of the\n * grid\n *\n * \\requires `constraints_tensor` to be a tensor of rank 2 or higher. The first\n * index must be a lower spatial index that originates from a derivative.\n *\n * We follow \\cite Szilagyi2014fna, Eq. (62)-(64) to compute:\n *\n * \\begin{align}\n * \\Epsilon_\\hat{k} &= \\frac{1}{N_\\hat{k}} \\sqrt{\\sum_{a\\ldots} \\left(\\sum_{i}\n *   \\frac{\\partial x^i}{\\partial x^\\hat{k}} C_{ia\\ldots}\\right)^2} \\\\\n * N_\\hat{k} &= \\sqrt{\\sum_{i} \\left(\\frac{\\partial x^i}{\\partial x^\\hat{k}}\n *   \\right)^2}\n * \\end{align}\n *\n * This transform the first lower spatial index of the tensor to the\n * element-logical frame, then takes an L2 norm over all remaining indices.\n * The (squared) normalization factor $N_\\hat{k}^2$ is computed by\n * `normalization_factor` and passed in as an argument.\n */\ntemplate <size_t Dim, typename TensorType>\nvoid logical_constraints(\n    gsl::not_null<tnsr::i<DataVector, Dim, Frame::ElementLogical>*> result,\n    gsl::not_null<DataVector*> buffer, const TensorType& constraints_tensor,\n    const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>&\n        jacobian,\n    const tnsr::i<DataVector, Dim, Frame::ElementLogical>&\n        normalization_factor_square);\n\n/*!\n * \\brief Apply the AMR criterion to one of the constraints\n *\n * The `result` is the current decision in each dimension based on the previous\n * constraints. This function will update the flags if necessary. It takes\n * the \"max\" of the current and new flags, where the \"highest\" flag is\n * `Flag::IncreaseResolution`, followed by `Flag::DoNothing`, and then\n * `Flag::DecreaseResolution`.\n */\ntemplate <size_t Dim>\nvoid max_over_components(\n    gsl::not_null<std::array<Flag, Dim>*> result,\n    const tnsr::i<DataVector, Dim, Frame::ElementLogical>& logical_constraints,\n    double abs_target, double coarsening_factor);\n\n}  // namespace Constraints_detail\n\n/*!\n * \\brief Refine the grid towards the target constraint violation\n *\n * - If any constraint is above the target value, the element will be p-refined.\n * - If all constraints are below the target times the \"coarsening factor\" the\n *   element will be p-coarsened.\n *\n * This criterion is based on Sec. 6.1.4 in \\cite Szilagyi2014fna .\n *\n * If the coarsening factor turns out to be hard to choose, then we can try to\n * eliminate it by projecting the variables to a lower polynomial order before\n * computing constraints, or something like that.\n *\n * \\tparam Dim Spatial dimension of the grid\n * \\tparam TensorTags List of tags of the constraints to be monitored. These\n * must be tensors of rank 2 or higher. The first index must be a lower spatial\n * index that originates from a derivative.\n */\ntemplate <size_t Dim, typename TensorTags>\nclass Constraints : public Criterion {\n public:\n  struct ConstraintsToMonitor {\n    using type = std::vector<std::string>;\n    static constexpr Options::String help = {\"The constraints to monitor.\"};\n    static size_t lower_bound_on_size() { return 1; }\n  };\n  struct AbsoluteTarget {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The absolute target constraint violation. If any constraint is above \"\n        \"this value, the element will be p-refined.\"};\n    static double lower_bound() { return 0.; }\n  };\n  struct CoarseningFactor {\n    using type = double;\n    static constexpr Options::String help = {\n        \"If all constraints are below the 'AbsoluteTarget' times this factor, \"\n        \"the element will be p-coarsened. \"\n        \"A reasonable value is 0.1.\"};\n    static double lower_bound() { return 0.; }\n    static double upper_bound() { return 1.; }\n  };\n\n  using options =\n      tmpl::list<ConstraintsToMonitor, AbsoluteTarget, CoarseningFactor>;\n\n  static constexpr Options::String help = {\n      \"Refine the grid towards the target constraint violation\"};\n\n  Constraints() = default;\n\n  Constraints(std::vector<std::string> vars_to_monitor, double abs_target,\n              double coarsening_factor, const Options::Context& context = {});\n\n  /// \\cond\n  explicit Constraints(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Constraints);  // NOLINT\n  /// \\endcond\n\n  using compute_tags_for_observation_box = tmpl::append<\n      TensorTags, tmpl::list<domain::Tags::JacobianCompute<\n                                 Dim, Frame::ElementLogical, Frame::Inertial>,\n                             Events::Tags::ObserverJacobianCompute<\n                                 Dim, Frame::ElementLogical, Frame::Inertial>>>;\n\n  Type type() override { return Type::p; }\n\n  std::string observation_name() override { return \"Constraints\"; }\n\n  using argument_tags = tmpl::list<::Tags::ObservationBox>;\n\n  template <typename ComputeTagsList, typename DataBoxType,\n            typename Metavariables>\n  std::array<Flag, Dim> operator()(\n      const ObservationBox<ComputeTagsList, DataBoxType>& box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id) const;\n\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<std::string> vars_to_monitor_{};\n  double abs_target_ = std::numeric_limits<double>::signaling_NaN();\n  double coarsening_factor_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n// Out-of-line definitions\n/// \\cond\n\ntemplate <size_t Dim, typename TensorTags>\nConstraints<Dim, TensorTags>::Constraints(\n    std::vector<std::string> vars_to_monitor, const double abs_target,\n    const double coarsening_factor, const Options::Context& context)\n    : vars_to_monitor_(std::move(vars_to_monitor)),\n      abs_target_(abs_target),\n      coarsening_factor_(coarsening_factor) {\n  db::validate_selection<TensorTags>(vars_to_monitor_, context);\n}\n\ntemplate <size_t Dim, typename TensorTags>\nConstraints<Dim, TensorTags>::Constraints(CkMigrateMessage* msg)\n    : Criterion(msg) {}\n\ntemplate <size_t Dim, typename TensorTags>\ntemplate <typename ComputeTagsList, typename DataBoxType,\n          typename Metavariables>\nstd::array<Flag, Dim> Constraints<Dim, TensorTags>::operator()(\n    const ObservationBox<ComputeTagsList, DataBoxType>& box,\n    Parallel::GlobalCache<Metavariables>& /*cache*/,\n    const ElementId<Dim>& /*element_id*/) const {\n  auto result = make_array<Dim>(Flag::Undefined);\n  const auto& jacobian =\n      get<Events::Tags::ObserverJacobian<Dim, Frame::ElementLogical,\n                                         Frame::Inertial>>(box);\n  // Set up memory buffers\n  const size_t num_points = jacobian.begin()->size();\n  TempBuffer<tmpl::list<::Tags::Tempi<0, Dim, Frame::ElementLogical>,\n                        ::Tags::Tempi<1, Dim, Frame::ElementLogical>,\n                        ::Tags::TempScalar<2>>>\n      buffer{num_points};\n  auto& normalization_factor_square =\n      get<::Tags::Tempi<0, Dim, Frame::ElementLogical>>(buffer);\n  auto& logical_constraints =\n      get<::Tags::Tempi<1, Dim, Frame::ElementLogical>>(buffer);\n  auto& scalar_buffer = get(get<::Tags::TempScalar<2>>(buffer));\n  Constraints_detail::normalization_factor_square(\n      make_not_null(&normalization_factor_square), jacobian);\n  // Check all constraints in turn\n  tmpl::for_each<TensorTags>([&result, &box, &jacobian,\n                              &normalization_factor_square,\n                              &logical_constraints, &scalar_buffer,\n                              this](const auto tag_v) {\n    // Stop if we have already decided to refine every dimension\n    if (result == make_array<Dim>(Flag::IncreaseResolution)) {\n      return;\n    }\n    using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n    const std::string tag_name = db::tag_name<tag>();\n    // Skip if this tensor is not being monitored\n    if (not alg::found(vars_to_monitor_, tag_name)) {\n      return;\n    }\n    Constraints_detail::logical_constraints(\n        make_not_null(&logical_constraints), make_not_null(&scalar_buffer),\n        get<tag>(box), jacobian, normalization_factor_square);\n    Constraints_detail::max_over_components(make_not_null(&result),\n                                            logical_constraints, abs_target_,\n                                            coarsening_factor_);\n  });\n  return result;\n}\n\ntemplate <size_t Dim, typename TensorTags>\nvoid Constraints<Dim, TensorTags>::pup(PUP::er& p) {\n  p | vars_to_monitor_;\n  p | abs_target_;\n  p | coarsening_factor_;\n}\n\ntemplate <size_t Dim, typename TensorTags>\nPUP::able::PUP_ID Constraints<Dim, TensorTags>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Criteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace amr {\n/// \\ingroup AmrGroup\n/// Criteria for deciding how a mesh should be adapted\nnamespace Criteria {}\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Criterion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr {\n/// \\ingroup AmrGroup\n/// \\brief Base class for something that determines how an adaptive mesh should\n/// be changed\n///\n/// \\details Each class derived from this class should (see the examples below):\n/// - Be option-creatable\n/// - Be serializable\n/// - Define a call operator that returns a std::array<amr::Flag, Dim>\n///   containing the recommended refinement choice in each logical dimension of\n///   the Element.\n/// - Define the type aliases `argument_tags` and\n///   `compute_tags_for_observation_box` that are type lists of tags used in the\n///   call operator.\n/// The call operator should take as arguments the values corresponding to each\n/// tag in `argument_tags` (in order), followed by the Parallel::GlobalCache,\n/// and the ElementId.  The tags listed in `argument_tags` should either be tags\n/// in the DataBox of the array component, or listed in\n/// `compute_tags_for_observation_box`.\n///\n/// \\example\n/// \\snippet Amr/Criteria/Test_Criterion.cpp criterion_examples\nclass Criterion : public PUP::able {\n protected:\n  /// \\cond\n  Criterion() = default;\n  Criterion(const Criterion&) = default;\n  Criterion(Criterion&&) = default;\n  Criterion& operator=(const Criterion&) = default;\n  Criterion& operator=(Criterion&&) = default;\n  /// \\endcond\n\n public:\n  ~Criterion() override = default;\n  explicit Criterion(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  WRAPPED_PUPable_abstract(Criterion);  // NOLINT\n\n  virtual Criteria::Type type() = 0;\n\n  virtual std::string observation_name() = 0;\n\n  /// Evaluates the AMR criteria by selecting the appropriate derived class\n  /// and forwarding its `argument_tags` from the ObservationBox (along with the\n  /// GlobalCache and ArrayIndex) to the call operator of the derived class\n  ///\n  /// \\note In order to be available, a derived Criterion must be listed in\n  /// the entry for Criterion in\n  /// Metavarialbes::factory_creation::factory_classes\n  ///\n  /// \\note The ComputeTagsList of the ObservationBox should contain the union\n  /// of the tags listed in `compute_tags_for_observation_box` for each derived\n  /// Criterion listed in the `factory_classes`.\n  template <typename ComputeTagsList, typename DataBoxType,\n            typename Metavariables>\n  auto evaluate(const ObservationBox<ComputeTagsList, DataBoxType>& box,\n                Parallel::GlobalCache<Metavariables>& cache,\n                const ElementId<Metavariables::volume_dim>& element_id) const {\n    using factory_classes =\n        typename std::decay_t<Metavariables>::factory_creation::factory_classes;\n    return call_with_dynamic_type<\n        std::array<amr::Flag, Metavariables::volume_dim>,\n        tmpl::at<factory_classes, Criterion>>(\n        this, [&box, &cache, &element_id](auto* const criterion) {\n          return apply(*criterion, box, cache, element_id);\n        });\n  }\n};\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/DriveToTarget.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace amr::Criteria {\ntemplate <size_t Dim, Type CriteriaType>\nDriveToTarget<Dim, CriteriaType>::DriveToTarget(\n    const std::array<size_t, Dim>& target,\n    const std::array<Flag, Dim>& flags_at_target)\n    : target_(target), flags_at_target_(flags_at_target) {\n  if constexpr (CriteriaType == Type::h) {\n    if (alg::any_of(flags_at_target_, [](Flag flag) {\n          return flag == Flag::IncreaseResolution or\n                 flag == Flag::DecreaseResolution;\n        })) {\n      ERROR(\"Cannot use p-refinement flag in OscillationAtTarget \"\n            << flags_at_target);\n    }\n  } else {\n    if (alg::any_of(flags_at_target_, [](Flag flag) {\n          return flag == Flag::Join or flag == Flag::Split;\n        })) {\n      ERROR(\"Cannot use h-refinement flag in OscillationAtTarget \"\n            << flags_at_target);\n    }\n  }\n}\n\ntemplate <size_t Dim, Type CriteriaType>\nDriveToTarget<Dim, CriteriaType>::DriveToTarget(CkMigrateMessage* msg)\n    : Criterion(msg) {}\n\n// NOLINTNEXTLINE(google-runtime-references)\ntemplate <size_t Dim, Type CriteriaType>\nvoid DriveToTarget<Dim, CriteriaType>::pup(PUP::er& p) {\n  Criterion::pup(p);\n  p | target_;\n  p | flags_at_target_;\n}\n\ntemplate <size_t Dim, Type CriteriaType>\nstd::array<Flag, Dim> DriveToTarget<Dim, CriteriaType>::impl(\n    const Mesh<Dim>& current_mesh, const ElementId<Dim>& element_id) const {\n  auto result = make_array<Dim>(Flag::DoNothing);\n  [[maybe_unused]] const std::array<size_t, Dim> levels =\n      element_id.refinement_levels();\n  bool is_at_target = true;\n  for (size_t d = 0; d < Dim; ++d) {\n    if constexpr (CriteriaType == Type::h) {\n      if (gsl::at(levels, d) < gsl::at(target_, d)) {\n        gsl::at(result, d) = Flag::Split;\n        is_at_target = false;\n      } else if (gsl::at(levels, d) > gsl::at(target_, d)) {\n        gsl::at(result, d) = Flag::Join;\n        is_at_target = false;\n      }\n    } else {\n      if (current_mesh.extents(d) < gsl::at(target_, d)) {\n        gsl::at(result, d) = Flag::IncreaseResolution;\n        is_at_target = false;\n      } else if (current_mesh.extents(d) > gsl::at(target_, d)) {\n        gsl::at(result, d) = Flag::DecreaseResolution;\n        is_at_target = false;\n      }\n    }\n  }\n  if (is_at_target) {\n    return flags_at_target_;\n  }\n\n  return result;\n}\n\ntemplate <size_t Dim, Type CriteriaType>\nPUP::able::PUP_ID DriveToTarget<Dim, CriteriaType>::my_PUP_ID = 0;  // NOLINT\n\ntemplate class DriveToTarget<1, Type::h>;\ntemplate class DriveToTarget<2, Type::h>;\ntemplate class DriveToTarget<3, Type::h>;\ntemplate class DriveToTarget<1, Type::p>;\ntemplate class DriveToTarget<2, Type::p>;\ntemplate class DriveToTarget<3, Type::p>;\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t>\nclass ElementId;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\nnamespace amr::Criteria {\n/*!\n * \\brief Refine the grid towards the target number of grid points and\n * refinement levels in each dimension and then oscillate about the target.\n *\n * \\details If the grid is at neither target in a given dimension, the\n * flag chosen will be in the priority order Split, IncreaseResolution,\n * DecreaseResolution, Join.\n *\n * \\note To remain at the target, set the OscillationAtTarget Flags to\n * DoNothing.\n *\n * \\note This criterion is primarily for testing the mechanics of refinement.\n */\ntemplate <size_t Dim, Type CriteriaType>\nclass DriveToTarget : public Criterion {\n public:\n  /// The target (number of grid points or refinement level) in each dimension\n  struct Target {\n    using type = std::array<size_t, Dim>;\n    static constexpr Options::String help = {\n        CriteriaType == Type::p\n            ? \"The target number of grid points in each dimension.\"\n            : \"The target refinement level in each dimension.\"};\n  };\n\n  /// The AMR flags chosen when the target in each dimension is reached\n  struct OscillationAtTarget {\n    using type = std::array<Flag, Dim>;\n    static constexpr Options::String help = {\n        \"The flags returned when at the target.\"};\n  };\n\n  using options = tmpl::list<Target, OscillationAtTarget>;\n\n  static constexpr Options::String help = {\n      \"Refine the grid towards the Target, and then oscillate about them by \"\n      \"applying OscillationAtTarget.\"};\n\n  DriveToTarget() = default;\n\n  DriveToTarget(const std::array<size_t, Dim>& target,\n                const std::array<Flag, Dim>& flags_at_target);\n\n  /// \\cond\n  explicit DriveToTarget(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(DriveToTarget);  // NOLINT\n  /// \\endcond\n\n  static std::string name() {\n    return CriteriaType == Type::p ? \"DriveToTargetNumberOfGridPoints\"\n                                   : \"DriveToTargetRefinementLevels\";\n  }\n\n  Type type() override { return CriteriaType; }\n\n  std::string observation_name() override { return \"DriveToTarget\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using argument_tags = tmpl::list<::domain::Tags::Mesh<Dim>>;\n\n  template <typename Metavariables>\n  auto operator()(const Mesh<Dim>& current_mesh,\n                  Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ElementId<Dim>& element_id) const;\n\n  void pup(PUP::er& p) override;\n\n private:\n  std::array<Flag, Dim> impl(const Mesh<Dim>& current_mesh,\n                             const ElementId<Dim>& element_id) const;\n\n  std::array<size_t, Dim> target_{};\n  std::array<Flag, Dim> flags_at_target_{};\n};\n\ntemplate <size_t Dim, Type CriteriaType>\ntemplate <typename Metavariables>\nauto DriveToTarget<Dim, CriteriaType>::operator()(\n    const Mesh<Dim>& current_mesh,\n    Parallel::GlobalCache<Metavariables>& /*cache*/,\n    const ElementId<Dim>& element_id) const {\n  return impl(current_mesh, element_id);\n}\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/IncreaseResolution.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Loehner.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Persson.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Random.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/TruncationError.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Criteria {\n\n/*!\n * \\brief AMR criteria that are generally applicable\n *\n * \\tparam Dim the spatial dimension\n * \\tparam TensorTags the tensor fields to monitor\n */\ntemplate <size_t Dim, typename TensorTags>\nusing standard_criteria = tmpl::list<\n    // p-AMR criteria\n    ::amr::Criteria::IncreaseResolution<Dim>,\n    ::amr::Criteria::TruncationError<Dim, TensorTags>,\n    // h-AMR criteria\n    ::amr::Criteria::Loehner<Dim, TensorTags>,\n    ::amr::Criteria::Persson<Dim, TensorTags>,\n    // Criteria for testing or experimenting\n    ::amr::Criteria::DriveToTarget<Dim, Type::h>,\n    ::amr::Criteria::DriveToTarget<Dim, Type::p>,\n    ::amr::Criteria::Random<Type::h>, ::amr::Criteria::Random<Type::p>>;\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/IncreaseResolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Criteria/IncreaseResolution.hpp\"\n\nnamespace amr::Criteria {\n\ntemplate <size_t Dim>\nIncreaseResolution<Dim>::IncreaseResolution(CkMigrateMessage* msg)\n    : Criterion(msg) {}\n\ntemplate class IncreaseResolution<1>;\ntemplate class IncreaseResolution<2>;\ntemplate class IncreaseResolution<3>;\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/IncreaseResolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Criteria {\n/*!\n * \\brief Uniformly increases the number of grid points by one\n *\n * Useful to do uniform p-refinement, possibly alongside a nontrivial\n * h-refinement criterion.\n */\ntemplate <size_t Dim>\nclass IncreaseResolution : public Criterion {\n public:\n  using options = tmpl::list<>;\n\n  static constexpr Options::String help = {\n      \"Uniformly increases the number of grid points by one.\"};\n\n  IncreaseResolution() = default;\n\n  /// \\cond\n  explicit IncreaseResolution(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(IncreaseResolution);  // NOLINT\n  /// \\endcond\n\n  Type type() override { return Type::p; }\n\n  std::string observation_name() override { return \"IncreaseResolution\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename Metavariables>\n  std::array<Flag, Dim> operator()(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/) const {\n    return make_array<Dim>(Flag::IncreaseResolution);\n  }\n};\n\n/// \\cond\ntemplate <size_t Dim>\nPUP::able::PUP_ID IncreaseResolution<Dim>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Loehner.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Criteria/Loehner.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace amr::Criteria {\n\ntemplate <typename VectorType, size_t Dim>\ndouble loehner_smoothness_indicator(\n    const gsl::not_null<VectorType*> first_deriv_buffer,\n    const gsl::not_null<VectorType*> second_deriv_buffer,\n    const VectorType& tensor_component, const Mesh<Dim>& mesh,\n    const size_t dimension) {\n  const size_t num_points = mesh.number_of_grid_points();\n  set_number_of_grid_points(first_deriv_buffer, num_points);\n  set_number_of_grid_points(second_deriv_buffer, num_points);\n  static const Matrix identity{};\n  // Compute second logical derivative in this dimension\n  // Possible performance optimization: pre-compute the second derivative matrix\n  // and store it either statically (like `Spectral::differentiation_matrix`)\n  // or pass it in as an argument so it's re-used for each tensor component.\n  const auto& logical_diff_matrix =\n      Spectral::differentiation_matrix(mesh.slice_through(dimension));\n  auto matrices = make_array<Dim>(std::cref(identity));\n  gsl::at(matrices, dimension) = logical_diff_matrix;\n  apply_matrices(first_deriv_buffer, matrices, tensor_component,\n                 mesh.extents());\n  apply_matrices(second_deriv_buffer, matrices, *first_deriv_buffer,\n                 mesh.extents());\n  // Take the L2 norm over all grid points\n  double norm = 0;\n  if constexpr (std::is_same_v<VectorType, ComplexDataVector>) {\n    norm = blaze::l2Norm(blaze::abs(*second_deriv_buffer));\n  } else {\n    norm = blaze::l2Norm(*second_deriv_buffer);\n  }\n  norm /= sqrt(second_deriv_buffer->size());\n  return norm;\n}\n\ntemplate <typename VectorType, size_t Dim>\nstd::array<double, Dim> loehner_smoothness_indicator(\n    const VectorType& tensor_component, const Mesh<Dim>& mesh) {\n  std::array<double, Dim> result{};\n  std::array<VectorType, 2> deriv_buffers{};\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(result, d) = loehner_smoothness_indicator(\n        make_not_null(&deriv_buffers[0]), make_not_null(&deriv_buffers[1]),\n        tensor_component, mesh, d);\n  }\n  return result;\n}\n\nnamespace Loehner_detail {\n\ntemplate <typename VectorType, size_t Dim>\nvoid max_over_components(\n    const gsl::not_null<std::array<Flag, Dim>*> result,\n    const gsl::not_null<std::array<VectorType, 2>*> deriv_buffers,\n    const VectorType& tensor_component, const Mesh<Dim>& mesh,\n    const double relative_tolerance, const double absolute_tolerance,\n    const double coarsening_factor) {\n  const double umax = max(abs(tensor_component));\n  for (size_t d = 0; d < Dim; ++d) {\n    // Skip this dimension if we have already decided to refine it\n    if (gsl::at(*result, d) == Flag::Split) {\n      continue;\n    }\n    const double indicator =\n        loehner_smoothness_indicator(make_not_null(&(*deriv_buffers)[0]),\n                                     make_not_null(&(*deriv_buffers)[1]),\n                                     tensor_component, mesh, d) /\n        (relative_tolerance * umax + absolute_tolerance);\n    if (indicator > 1.) {\n      gsl::at(*result, d) = Flag::Split;\n      continue;\n    }\n    // Don't check if we want to (allow) joining elements if another\n    // tensor has already decided that joining elements is bad.\n    if (gsl::at(*result, d) == Flag::DoNothing) {\n      continue;\n    }\n    if (indicator < coarsening_factor) {\n      gsl::at(*result, d) = Flag::Join;\n    } else {\n      gsl::at(*result, d) = Flag::DoNothing;\n    }\n  }\n}\n\n}  // namespace Loehner_detail\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template double loehner_smoothness_indicator(                           \\\n      gsl::not_null<DTYPE(data)*> first_deriv_buffer,                     \\\n      gsl::not_null<DTYPE(data)*> second_deriv_buffer,                    \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh,  \\\n      size_t dimension);                                                  \\\n  template std::array<double, DIM(data)> loehner_smoothness_indicator(    \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh); \\\n  template void Loehner_detail::max_over_components(                      \\\n      gsl::not_null<std::array<Flag, DIM(data)>*> result,                 \\\n      gsl::not_null<std::array<DTYPE(data), 2>*> deriv_buffers,           \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh,  \\\n      double relative_tolerance, double absolute_tolerance,               \\\n      double coarsening_factor);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, ComplexDataVector), (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Loehner.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/ValidateSelection.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t>\nclass ElementId;\n/// \\endcond\n\nnamespace amr::Criteria {\n\n/// @{\n/*!\n * \\brief Computes an anisotropic smoothness indicator based on the magnitude of\n * second derivatives\n *\n * This smoothness indicator is simply the L2 norm of the logical second\n * derivative of the tensor component in the given `dimension`:\n *\n * \\begin{equation}\n * \\epsilon_k =\n *   \\sqrt{\\frac{1}{N_{\\mathrm{points}}} \\sum_{p=1}^{N_\\mathrm{points}}\n *     \\left(\\partial^2 u / \\partial \\xi_k^2\\right)^2}\n * \\end{equation}\n *\n * If the smoothness indicator is large in a direction, meaning the tensor\n * component has a large second derivative in that direction, the element should\n * be h-refined. If the smoothness indicator is small, the element should be\n * h-coarsened. A coarsing threshold of about a third of the refinement\n * threshold seems to work well, but this will need more testing.\n *\n * Note that it is not at all clear that a smoothness indicator based on the\n * magnitude of second derivatives is useful in a DG context. Smooth functions\n * with higher-order derivatives can be approximated just fine by higher-order\n * DG elements without the need for h-refinement. The use of second derivatives\n * to indicate the need for refinement originated in the finite element context\n * with linear elements. Other smoothness indicators might prove more useful for\n * DG elements, e.g. based on jumps or oscillations of the solution. We can also\n * explore applying the troubled-cell indicators (TCIs) used in hydrodynamic\n * simulations as h-refinement indicators.\n *\n * Specifically, this smoothness indicator is based on \\cite Loehner1987 (hence\n * the name of the function), which is popular in the finite element community\n * and also used in a DG context by \\cite Dumbser2013, Eq. (34), and by\n * \\cite Renkhoff2023, Eq. (15). We make several modifications:\n *\n * - The original smoothness indicator is isotropic, i.e. it computes the norm\n *   over all (mixed) second derivatives. Here we compute an anisotropic\n *   indicator by computing second derivatives in each dimension separately\n *   and ignoring mixed derivatives.\n * - The original smoothness indicator is normalized by measures of the first\n *   derivative which don't generalize well to spectral elements. Therefore, we\n *   simplify the normalization to a standard relative and absolute tolerance.\n *   An alternative approach is proposed in \\cite Renkhoff2023, Eq.(15), where\n *   the authors take the absolute value of the differentiation matrix and apply\n *   the resulting matrix to the absolute value of the data on the grid to\n *   compute the normalization. However, this quantity can produce quite large\n *   numbers and hence overestimates the smoothness by suppressing the second\n *   derivative.\n * - We compute the second derivative in logical coordinates. This seems\n *   easiest for spectral elements, but note that \\cite Renkhoff2023 seem to\n *   use inertial coordinates.\n *\n * In addition to the above modifications, we can consider approximating the\n * second derivative using finite differences, as explored in the prototype\n * https://github.com/sxs-collaboration/dg-charm/blob/HpAmr/Evolution/HpAmr/LohnerRefiner.hpp.\n * This would allow falling back to the normalization used by Löhner and might\n * be cheaper to compute, but it requires an interpolation to the center and\n * maybe also to the faces, depending on the desired stencil.\n */\ntemplate <typename VectorType, size_t Dim>\ndouble loehner_smoothness_indicator(\n    gsl::not_null<VectorType*> first_deriv_buffer,\n    gsl::not_null<VectorType*> second_deriv_buffer,\n    const VectorType& tensor_component, const Mesh<Dim>& mesh,\n    size_t dimension);\ntemplate <typename VectorType, size_t Dim>\nstd::array<double, Dim> loehner_smoothness_indicator(\n    const VectorType& tensor_component, const Mesh<Dim>& mesh);\n/// @}\n\nnamespace Loehner_detail {\ntemplate <typename VectorType, size_t Dim>\nvoid max_over_components(\n    gsl::not_null<std::array<Flag, Dim>*> result,\n    gsl::not_null<std::array<VectorType, 2>*> deriv_buffers,\n    const VectorType& tensor_component, const Mesh<Dim>& mesh,\n    double relative_tolerance, double absolute_tolerance,\n    double coarsening_factor);\n}\n\n/*!\n * \\brief h-refine the grid based on a smoothness indicator\n *\n * The smoothness indicator used here is based on the magnitude of second\n * derivatives. See `amr::Criteria::loehner_smoothness_indicator` for details\n * and caveats.\n *\n * \\see amr::Criteria::loehner_smoothness_indicator\n */\ntemplate <size_t Dim, typename TensorTags>\nclass Loehner : public Criterion {\n public:\n  struct VariablesToMonitor {\n    using type = std::vector<std::string>;\n    static constexpr Options::String help = {\n        \"The tensors to monitor for h-refinement.\"};\n    static size_t lower_bound_on_size() { return 1; }\n  };\n  struct RelativeTolerance {\n    using type = double;\n    static constexpr Options::String help = {\n        \"If any tensor component has a second derivative magnitude above this \"\n        \"value times the max of the absolute tensor component over the \"\n        \"element, the element will be h-refined in that direction. \"\n        \"Set to 0 to disable.\"};\n    static double lower_bound() { return 0.; }\n  };\n  struct AbsoluteTolerance {\n    using type = double;\n    static constexpr Options::String help = {\n        \"If any tensor component has a second derivative magnitude above this \"\n        \"value, the element will be h-refined in that direction. \"\n        \"Set to 0 to disable.\"};\n    static double lower_bound() { return 0.; }\n  };\n  struct CoarseningFactor {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Factor applied to both relative and absolute tolerance to trigger \"\n        \"h-coarsening. Set to 0 to disable h-coarsening altogether. \"\n        \"Set closer to 1 to trigger h-coarsening more aggressively. \"\n        \"Values too close to 1 risk that coarsened elements will immediately \"\n        \"trigger h-refinement again. A reasonable value is 1/3.\"};\n    static double lower_bound() { return 0.; }\n    static double upper_bound() { return 1.; }\n  };\n\n  using options = tmpl::list<VariablesToMonitor, RelativeTolerance,\n                             AbsoluteTolerance, CoarseningFactor>;\n\n  static constexpr Options::String help = {\n      \"Refine the grid towards resolving an estimated error in the second \"\n      \"derivative\"};\n\n  Loehner() = default;\n\n  Loehner(std::vector<std::string> vars_to_monitor, double relative_tolerance,\n          double absolute_tolerance, double coarsening_factor,\n          const Options::Context& context = {});\n\n  /// \\cond\n  explicit Loehner(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Loehner);  // NOLINT\n  /// \\endcond\n\n  Type type() override { return Type::h; }\n\n  std::string observation_name() override { return \"Loehner\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using argument_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList, typename Metavariables>\n  std::array<Flag, Dim> operator()(const db::DataBox<DbTagsList>& box,\n                                   Parallel::GlobalCache<Metavariables>& cache,\n                                   const ElementId<Dim>& element_id) const;\n\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<std::string> vars_to_monitor_{};\n  double relative_tolerance_ = std::numeric_limits<double>::signaling_NaN();\n  double absolute_tolerance_ = std::numeric_limits<double>::signaling_NaN();\n  double coarsening_factor_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n// Out-of-line definitions\n/// \\cond\n\ntemplate <size_t Dim, typename TensorTags>\nLoehner<Dim, TensorTags>::Loehner(std::vector<std::string> vars_to_monitor,\n                                  const double relative_tolerance,\n                                  const double absolute_tolerance,\n                                  const double coarsening_factor,\n                                  const Options::Context& context)\n    : vars_to_monitor_(std::move(vars_to_monitor)),\n      relative_tolerance_(relative_tolerance),\n      absolute_tolerance_(absolute_tolerance),\n      coarsening_factor_(coarsening_factor) {\n  db::validate_selection<TensorTags>(vars_to_monitor_, context);\n  if (relative_tolerance == 0. and absolute_tolerance == 0.) {\n    PARSE_ERROR(\n        context,\n        \"Must specify non-zero RelativeTolerance, AbsoluteTolerance, or both.\");\n  }\n}\n\ntemplate <size_t Dim, typename TensorTags>\nLoehner<Dim, TensorTags>::Loehner(CkMigrateMessage* msg) : Criterion(msg) {}\n\ntemplate <size_t Dim, typename TensorTags>\ntemplate <typename DbTagsList, typename Metavariables>\nstd::array<Flag, Dim> Loehner<Dim, TensorTags>::operator()(\n    const db::DataBox<DbTagsList>& box,\n    Parallel::GlobalCache<Metavariables>& /*cache*/,\n    const ElementId<Dim>& /*element_id*/) const {\n  auto result = make_array<Dim>(Flag::Undefined);\n  const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n  using VectorType = typename Variables<TensorTags>::vector_type;\n  // Check all tensors and all tensor components in turn. We take the\n  // highest-priority refinement flag in each dimension, so if any tensor\n  // component is non-smooth, the element will split in that dimension. And only\n  // if all tensor components are smooth enough will elements join in that\n  // dimension.\n  std::array<VectorType, 2> deriv_buffers{};\n  tmpl::for_each<TensorTags>(\n      [&result, &box, &mesh, &deriv_buffers, this](const auto tag_v) {\n        // Stop if we have already decided to refine every dimension\n        if (result == make_array<Dim>(Flag::Split)) {\n          return;\n        }\n        using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n        const std::string tag_name = db::tag_name<tag>();\n        // Skip if this tensor is not being monitored\n        if (not alg::found(vars_to_monitor_, tag_name)) {\n          return;\n        }\n        const auto& tensor = db::get<tag>(box);\n        for (const VectorType& tensor_component : tensor) {\n          Loehner_detail::max_over_components(\n              make_not_null(&result), make_not_null(&deriv_buffers),\n              tensor_component, mesh, relative_tolerance_, absolute_tolerance_,\n              coarsening_factor_);\n        }\n      });\n  return result;\n}\n\ntemplate <size_t Dim, typename TensorTags>\nvoid Loehner<Dim, TensorTags>::pup(PUP::er& p) {\n  p | vars_to_monitor_;\n  p | relative_tolerance_;\n  p | absolute_tolerance_;\n  p | coarsening_factor_;\n}\n\ntemplate <size_t Dim, typename TensorTags>\nPUP::able::PUP_ID Loehner<Dim, TensorTags>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Persson.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Criteria/Persson.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"NumericalAlgorithms/Spectral/Filtering.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace amr::Criteria {\n\ntemplate <typename VectorType, size_t Dim>\ndouble persson_smoothness_indicator(\n    const gsl::not_null<VectorType*> filtered_component_buffer,\n    const VectorType& tensor_component, const Mesh<Dim>& mesh,\n    const size_t dimension, const size_t num_highest_modes) {\n  // Zero out the lowest modes in the given dimension\n  static const Matrix identity{};\n  auto matrices = make_array<Dim>(std::cref(identity));\n  gsl::at(matrices, dimension) = Spectral::filtering::zero_lowest_modes(\n      mesh.slice_through(dimension),\n      mesh.extents(dimension) - num_highest_modes);\n  set_number_of_grid_points(filtered_component_buffer,\n                            mesh.number_of_grid_points());\n  apply_matrices(filtered_component_buffer, matrices, tensor_component,\n                 mesh.extents());\n  // Take the L2 norm over all grid points\n  double norm = 0;\n  if constexpr (std::is_same_v<VectorType, ComplexDataVector>) {\n    norm = blaze::l2Norm(blaze::abs(*filtered_component_buffer));\n  } else {\n    norm = blaze::l2Norm(*filtered_component_buffer);\n  }\n  norm /= sqrt(filtered_component_buffer->size());\n  return norm;\n}\n\ntemplate <typename VectorType, size_t Dim>\nstd::array<double, Dim> persson_smoothness_indicator(\n    const VectorType& tensor_component, const Mesh<Dim>& mesh,\n    const size_t num_highest_modes) {\n  std::array<double, Dim> result{};\n  VectorType buffer{};\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(result, d) = persson_smoothness_indicator(\n        make_not_null(&buffer), tensor_component, mesh, d, num_highest_modes);\n  }\n  return result;\n}\n\nnamespace Persson_detail {\n\ntemplate <typename VectorType, size_t Dim>\nvoid max_over_components(const gsl::not_null<std::array<Flag, Dim>*> result,\n                         const gsl::not_null<VectorType*> buffer,\n                         const VectorType& tensor_component,\n                         const Mesh<Dim>& mesh, const size_t num_highest_modes,\n                         const double alpha, const double absolute_tolerance,\n                         const double coarsening_factor) {\n  const double umax = max(abs(tensor_component));\n  for (size_t d = 0; d < Dim; ++d) {\n    // Skip this dimension if we have already decided to refine it\n    if (gsl::at(*result, d) == Flag::Split) {\n      continue;\n    }\n    const double relative_tolerance =\n        pow(mesh.extents(d) - num_highest_modes, -alpha);\n    const double indicator =\n        persson_smoothness_indicator(buffer, tensor_component, mesh, d,\n                                     num_highest_modes) /\n        (relative_tolerance * umax + absolute_tolerance);\n    if (indicator > 1.) {\n      gsl::at(*result, d) = Flag::Split;\n      continue;\n    }\n    // Don't check if we want to (allow) joining elements if another\n    // tensor has already decided that joining elements is bad.\n    if (gsl::at(*result, d) == Flag::DoNothing) {\n      continue;\n    }\n    if (indicator < coarsening_factor) {\n      gsl::at(*result, d) = Flag::Join;\n    } else {\n      gsl::at(*result, d) = Flag::DoNothing;\n    }\n  }\n}\n\n}  // namespace Persson_detail\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template double persson_smoothness_indicator(                          \\\n      gsl::not_null<DTYPE(data)*> buffer,                                \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh, \\\n      size_t dimension, size_t num_highest_modes);                       \\\n  template std::array<double, DIM(data)> persson_smoothness_indicator(   \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh, \\\n      size_t num_highest_modes);                                         \\\n  template void Persson_detail::max_over_components(                     \\\n      gsl::not_null<std::array<Flag, DIM(data)>*> result,                \\\n      gsl::not_null<DTYPE(data)*> buffer,                                \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh, \\\n      size_t num_highest_modes, double alpha, double absolute_tolerance, \\\n      double coarsening_factor);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, ComplexDataVector), (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Persson.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/ValidateSelection.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t>\nclass ElementId;\n/// \\endcond\n\nnamespace amr::Criteria {\n\n/// @{\n/*!\n * \\brief Computes an anisotropic smoothness indicator based on the power in the\n * highest modes\n *\n * This smoothness indicator is the L2 norm of the tensor component with the\n * lowest N - M modes filtered out, where N is the number of grid points in the\n * given dimension and M is `num_highest_modes`.\n *\n * This strategy is similar to the Persson troubled-cell indicator (see\n * `evolution::dg::subcell::persson_tci`) with a few modifications:\n *\n * - The indicator is computed in each dimension separately for an anisotropic\n *   measure.\n * - The number of highest modes to keep can be chosen as a parameter.\n * - We don't normalize by the L2 norm of the unfiltered data $u$ here. This\n *   function just returns the L2 norm of the filtered data.\n */\ntemplate <typename VectorType, size_t Dim>\ndouble persson_smoothness_indicator(\n    gsl::not_null<VectorType*> filtered_component_buffer,\n    const VectorType& tensor_component, const Mesh<Dim>& mesh, size_t dimension,\n    size_t num_highest_modes);\ntemplate <typename VectorType, size_t Dim>\nstd::array<double, Dim> persson_smoothness_indicator(\n    const VectorType& tensor_component, const Mesh<Dim>& mesh,\n    size_t num_highest_modes);\n/// @}\n\nnamespace Persson_detail {\ntemplate <typename VectorType, size_t Dim>\nvoid max_over_components(gsl::not_null<std::array<Flag, Dim>*> result,\n                         gsl::not_null<VectorType*> buffer,\n                         const VectorType& tensor_component,\n                         const Mesh<Dim>& mesh, size_t num_highest_modes,\n                         double alpha, double absolute_tolerance,\n                         double coarsening_factor);\n}\n\n/*!\n * \\brief h-refine the grid based on power in the highest modes\n *\n * \\see persson_smoothness_indicator\n */\ntemplate <size_t Dim, typename TensorTags>\nclass Persson : public Criterion {\n public:\n  struct VariablesToMonitor {\n    using type = std::vector<std::string>;\n    static constexpr Options::String help = {\n        \"The tensors to monitor for h-refinement.\"};\n    static size_t lower_bound_on_size() { return 1; }\n  };\n  struct NumHighestModes {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Number of highest modes to monitor the power of.\"};\n    static size_t lower_bound() { return 1; }\n  };\n  struct Exponent {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The exponent at which the modes should decrease. \"\n        \"Corresponds to a \\\"relative tolerance\\\" of N^(-alpha), where N is the \"\n        \"number of grid points minus 'NumHighestModes'. \"\n        \"If any tensor component has power in the highest modes above this \"\n        \"value times the max of the absolute tensor component over the \"\n        \"element, the element will be h-refined in that direction.\"};\n    static double lower_bound() { return 0.; }\n  };\n  struct AbsoluteTolerance {\n    using type = double;\n    static constexpr Options::String help = {\n        \"If any tensor component has a power in the highest modes above this \"\n        \"value, the element will be h-refined in that direction. \"\n        \"Set to 0 to disable.\"};\n    static double lower_bound() { return 0.; }\n  };\n  struct CoarseningFactor {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Factor applied to both relative and absolute tolerance to trigger \"\n        \"h-coarsening. Set to 0 to disable h-coarsening altogether. \"\n        \"Set closer to 1 to trigger h-coarsening more aggressively. \"\n        \"Values too close to 1 risk that coarsened elements will immediately \"\n        \"trigger h-refinement again. A reasonable value is 0.1.\"};\n    static double lower_bound() { return 0.; }\n    static double upper_bound() { return 1.; }\n  };\n\n  using options = tmpl::list<VariablesToMonitor, NumHighestModes, Exponent,\n                             AbsoluteTolerance, CoarseningFactor>;\n\n  static constexpr Options::String help = {\n      \"Refine the grid so the power in the highest modes stays below the \"\n      \"tolerance\"};\n\n  Persson() = default;\n\n  Persson(std::vector<std::string> vars_to_monitor,\n          const size_t num_highest_modes, double alpha,\n          double absolute_tolerance, double coarsening_factor,\n          const Options::Context& context = {});\n\n  /// \\cond\n  explicit Persson(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Persson);  // NOLINT\n  /// \\endcond\n\n  Type type() override { return Type::h; }\n\n  std::string observation_name() override { return \"Persson\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using argument_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList, typename Metavariables>\n  std::array<Flag, Dim> operator()(const db::DataBox<DbTagsList>& box,\n                                   Parallel::GlobalCache<Metavariables>& cache,\n                                   const ElementId<Dim>& element_id) const;\n\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<std::string> vars_to_monitor_{};\n  size_t num_highest_modes_{};\n  double alpha_ = std::numeric_limits<double>::signaling_NaN();\n  double absolute_tolerance_ = std::numeric_limits<double>::signaling_NaN();\n  double coarsening_factor_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n// Out-of-line definitions\n/// \\cond\n\ntemplate <size_t Dim, typename TensorTags>\nPersson<Dim, TensorTags>::Persson(std::vector<std::string> vars_to_monitor,\n                                  const size_t num_highest_modes,\n                                  const double alpha,\n                                  const double absolute_tolerance,\n                                  const double coarsening_factor,\n                                  const Options::Context& context)\n    : vars_to_monitor_(std::move(vars_to_monitor)),\n      num_highest_modes_(num_highest_modes),\n      alpha_(alpha),\n      absolute_tolerance_(absolute_tolerance),\n      coarsening_factor_(coarsening_factor) {\n  db::validate_selection<TensorTags>(vars_to_monitor_, context);\n}\n\ntemplate <size_t Dim, typename TensorTags>\nPersson<Dim, TensorTags>::Persson(CkMigrateMessage* msg) : Criterion(msg) {}\n\ntemplate <size_t Dim, typename TensorTags>\ntemplate <typename DbTagsList, typename Metavariables>\nstd::array<Flag, Dim> Persson<Dim, TensorTags>::operator()(\n    const db::DataBox<DbTagsList>& box,\n    Parallel::GlobalCache<Metavariables>& /*cache*/,\n    const ElementId<Dim>& /*element_id*/) const {\n  auto result = make_array<Dim>(Flag::Undefined);\n  const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n  using VectorType = typename Variables<TensorTags>::vector_type;\n  // Check all tensors and all tensor components in turn. We take the\n  // highest-priority refinement flag in each dimension, so if any tensor\n  // component is non-smooth, the element will split in that dimension. And only\n  // if all tensor components are smooth enough will elements join in that\n  // dimension.\n  VectorType buffer(mesh.number_of_grid_points());\n  tmpl::for_each<TensorTags>(\n      [&result, &box, &mesh, &buffer, this](const auto tag_v) {\n        // Stop if we have already decided to refine every dimension\n        if (result == make_array<Dim>(Flag::Split)) {\n          return;\n        }\n        using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n        const std::string tag_name = db::tag_name<tag>();\n        // Skip if this tensor is not being monitored\n        if (not alg::found(vars_to_monitor_, tag_name)) {\n          return;\n        }\n        const auto& tensor = db::get<tag>(box);\n        for (const VectorType& tensor_component : tensor) {\n          Persson_detail::max_over_components(\n              make_not_null(&result), make_not_null(&buffer), tensor_component,\n              mesh, num_highest_modes_, alpha_, absolute_tolerance_,\n              coarsening_factor_);\n        }\n      });\n  return result;\n}\n\ntemplate <size_t Dim, typename TensorTags>\nvoid Persson<Dim, TensorTags>::pup(PUP::er& p) {\n  p | vars_to_monitor_;\n  p | num_highest_modes_;\n  p | alpha_;\n  p | absolute_tolerance_;\n  p | coarsening_factor_;\n}\n\ntemplate <size_t Dim, typename TensorTags>\nPUP::able::PUP_ID Persson<Dim, TensorTags>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Random.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Criteria/Random.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <unordered_map>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace amr::Criteria {\ntemplate <Type CriteriaType>\nRandom<CriteriaType>::Random(\n    std::unordered_map<amr::Flag, size_t> probability_weights)\n    : probability_weights_(std::move(probability_weights)) {\n  if constexpr (CriteriaType == Type::h) {\n    if (probability_weights_.contains(amr::Flag::IncreaseResolution) or\n        probability_weights_.contains(amr::Flag::DecreaseResolution)) {\n      ERROR(\"Cannot use p-refinement flag in ProbabilityWeights \"\n            << probability_weights_);\n    }\n  } else {\n    if (probability_weights_.contains(amr::Flag::Split) or\n        probability_weights_.contains(amr::Flag::Join)) {\n      ERROR(\"Cannot use h-refinement flag in ProbabilityWeights \"\n            << probability_weights_);\n    }\n  }\n}\n\ntemplate <Type CriteriaType>\nRandom<CriteriaType>::Random(CkMigrateMessage* msg) : Criterion(msg) {}\n\n// NOLINTNEXTLINE(google-runtime-references)\ntemplate <Type CriteriaType>\nvoid Random<CriteriaType>::pup(PUP::er& p) {\n  Criterion::pup(p);\n  p | probability_weights_;\n}\n\nnamespace detail {\namr::Flag random_flag(\n    const std::unordered_map<amr::Flag, size_t>& probability_weights) {\n  const size_t total_weight =\n      alg::accumulate(probability_weights, 0_st,\n                      [](const size_t total, const auto& flag_and_weight) {\n                        return total + flag_and_weight.second;\n                      });\n  if (total_weight == 0) {\n    return amr::Flag::DoNothing;\n  }\n  if (probability_weights.size() == 1) {\n    return probability_weights.begin()->first;\n  }\n\n  static std::random_device r;\n  static const auto seed = r();\n  static std::mt19937 generator(seed);\n  std::uniform_int_distribution<size_t> distribution{0, total_weight - 1};\n\n  const size_t random_number = distribution(generator);\n  size_t cumulative_weight = 0;\n  for (const auto& [flag, probability_weight] : probability_weights) {\n    cumulative_weight += probability_weight;\n    if (random_number < cumulative_weight) {\n      return flag;\n    }\n  }\n  ERROR(\"Should never reach here\");\n}\n}  // namespace detail\n\ntemplate <Type CriteriaType>\nPUP::able::PUP_ID Random<CriteriaType>::my_PUP_ID = 0;  // NOLINT\n\ntemplate class Random<Type::h>;\ntemplate class Random<Type::p>;\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Random.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <unordered_map>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Criteria {\nnamespace detail {\namr::Flag random_flag(\n    const std::unordered_map<amr::Flag, size_t>& probability_weights);\n}  // namespace detail\n\n/*!\n * \\brief Randomly refine (or coarsen) an Element in each dimension.\n *\n * You can specify a probability for each possible `amr::Flag`. It is evaluated\n * in each dimension separately. Details:\n *\n * - Probabilities are specified as integer weights. The probability for an\n *   `amr::Flag` is its weight over the sum of all weights.\n * - Flags with weight zero do not need to be specified.\n * - If all weights are zero, `amr::Flag::DoNothing` is always chosen.\n */\ntemplate <Type CriteriaType>\nclass Random : public Criterion {\n public:\n  struct ProbabilityWeights {\n    using type = std::unordered_map<amr::Flag, size_t>;\n    static constexpr Options::String help = {\n        \"Possible refinement types and their probability, specified as integer \"\n        \"weights. The probability for a refinement type is its weight over the \"\n        \"sum of all weights. For example, set 'Split: 1' and 'DoNothing: 4' to \"\n        \"split each element with 20% probability. The refinement is evaluated \"\n        \"in each dimension separately.\"};\n  };\n\n  using options = tmpl::list<ProbabilityWeights>;\n\n  static constexpr Options::String help = {\n      \"Randomly refine (or coarsen) the grid\"};\n\n  Random() = default;\n\n  explicit Random(std::unordered_map<amr::Flag, size_t> probability_weights);\n\n  /// \\cond\n  explicit Random(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Random);  // NOLINT\n  /// \\endcond\n\n  static std::string name() {\n    return CriteriaType == Type::p ? \"RandomP\" : \"RandomH\";\n  }\n\n  Type type() override { return CriteriaType; }\n\n  std::string observation_name() override { return \"Random\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using argument_tags = tmpl::list<>;\n\n  template <size_t Dim, typename Metavariables>\n  auto operator()(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ElementId<Dim>& element_id) const;\n\n  void pup(PUP::er& p) override;\n\n private:\n  std::unordered_map<amr::Flag, size_t> probability_weights_{};\n};\n\ntemplate <Type CriteriaType>\ntemplate <size_t Dim, typename Metavariables>\nauto Random<CriteriaType>::operator()(\n    Parallel::GlobalCache<Metavariables>& /*cache*/,\n    const ElementId<Dim>& /*element_id*/) const {\n  auto result = make_array<Dim>(amr::Flag::Undefined);\n  for (size_t d = 0; d < Dim; ++d) {\n    result[d] = detail::random_flag(probability_weights_);\n  }\n  return result;\n}\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Criteria.hpp\n  Tags.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Criteria {\n/// Option tags for AMR criteria\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// Options for AMR criteria\nstruct Criteria {\n  static constexpr Options::String help = \"Options for AMR criteria\";\n  using type = std::vector<std::unique_ptr<amr::Criterion>>;\n  using group = amr::OptionTags::AmrGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// The set of adaptive mesh refinement criteria\nstruct Criteria : db::SimpleTag {\n  using type = std::vector<std::unique_ptr<amr::Criterion>>;\n  using option_tags = tmpl::list<amr::Criteria::OptionTags::Criteria>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) {\n    return {serialize_and_deserialize<type>(value)};\n  }\n};\n}  // namespace Tags\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Tags/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup AmrGroup\nnamespace amr::Criteria {\n/// \\brief %Tags for adaptive mesh refinement criteria\nnamespace Tags {}\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/TruncationError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Criteria/TruncationError.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PowerMonitors.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace amr::Criteria::TruncationError_detail {\n\ntemplate <typename ValueType, size_t Dim>\nvoid max_over_components(\n    const gsl::not_null<std::array<Flag, Dim>*> result,\n    const gsl::not_null<std::array<DataVector, Dim>*> power_monitors_buffer,\n    const ValueType& tensor_component, const Mesh<Dim>& mesh,\n    const double target_abs_truncation_error,\n    const double target_rel_truncation_error) {\n  // We take the highest-priority refinement flag in each dimension, so if any\n  // tensor component has a truncation error above the target, the element will\n  // increase p refinement in that dimension. And only if all tensor components\n  // still satisfy the target with the highest mode removed will the element\n  // decrease p refinement in that dimension.\n  PowerMonitors::power_monitors(power_monitors_buffer, tensor_component, mesh);\n  const double umax = max(abs(tensor_component));\n  for (size_t d = 0; d < Dim; ++d) {\n    // Skip this dimension if we have already decided to refine it\n    if (gsl::at(*result, d) == Flag::IncreaseResolution) {\n      continue;\n    }\n    const auto& modes = gsl::at(*power_monitors_buffer, d);\n    // Avoid division by zero when calling on relative truncation error\n    const double max_mode = blaze::max(modes);\n    if (max_mode == 0) {\n      gsl::at(*result, d) = Flag::DoNothing;\n      continue;\n    }\n    // Increase p refinement if the truncation error exceeds the target\n    const double truncation_error =\n        umax * PowerMonitors::relative_truncation_error(modes, modes.size());\n    if (truncation_error >\n        target_abs_truncation_error + umax * target_rel_truncation_error) {\n      gsl::at(*result, d) = Flag::IncreaseResolution;\n      continue;\n    }\n    // Dont' check if we want to (allow) decreasing p refinement if another\n    // tensor has already decided that decreasing p refinement is bad.\n    if (gsl::at(*result, d) == Flag::DoNothing) {\n      continue;\n    }\n    // The `PowerMonitors::relative_truncation_error` function requires at\n    // least two modes (and we subtract one below)\n    if (modes.size() >= 3) {\n      // Decrease p refinement if the truncation error with the highest mode\n      // removed still satisfies the target. Otherwise, request to stay at\n      // this resolution (or increase resolution if another tensor component\n      // requested that).\n      const double truncation_error_coarsened =\n          umax *\n          PowerMonitors::relative_truncation_error(modes, modes.size() - 1);\n      if (truncation_error_coarsened <=\n          target_abs_truncation_error + umax * target_rel_truncation_error) {\n        gsl::at(*result, d) = Flag::DecreaseResolution;\n      } else {\n        gsl::at(*result, d) = Flag::DoNothing;\n      }\n    } else {\n      gsl::at(*result, d) = Flag::DoNothing;\n    }\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(_, data)                                           \\\n  template void max_over_components(                                     \\\n      gsl::not_null<std::array<Flag, DIM(data)>*> result,                \\\n      const gsl::not_null<std::array<DataVector, DIM(data)>*>            \\\n          power_monitors_buffer,                                         \\\n      const DTYPE(data) & tensor_component, const Mesh<DIM(data)>& mesh, \\\n      double target_abs_truncation_error, double target_rel_truncation_error);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (DataVector, ComplexDataVector),\n                        (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n\n}  // namespace amr::Criteria::TruncationError_detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/TruncationError.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/ValidateSelection.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t>\nclass ElementId;\n/// \\endcond\n\nnamespace amr::Criteria {\n\nnamespace TruncationError_detail {\n/*!\n * \\brief Apply the truncation error criterion to a single tensor component\n *\n * The `result` is the current decision in each dimension based on the previous\n * tensor components. This function will update the flags if necessary. It takes\n * the \"max\" of the current and new flags, where the \"highest\" flag is\n * `Flag::IncreaseResolution`, followed by `Flag::DoNothing`, and then\n * `Flag::DecreaseResolution`.\n */\ntemplate <typename ValueType, size_t Dim>\nvoid max_over_components(\n    gsl::not_null<std::array<Flag, Dim>*> result,\n    const gsl::not_null<std::array<DataVector, Dim>*> power_monitors_buffer,\n    const ValueType& tensor_component, const Mesh<Dim>& mesh,\n    double target_abs_truncation_error, double target_rel_truncation_error);\n}  // namespace TruncationError_detail\n\n/*!\n * \\brief Refine the grid towards the target truncation error\n *\n * - If any tensor component has a truncation error above the target value, the\n *   element will be p-refined.\n * - If all tensor components still satisfy the target even with one mode\n *   removed, the element will be p-coarsened.\n *\n * For details on how the truncation error is computed see\n * `PowerMonitors::truncation_error`.\n *\n * \\tparam Dim Spatial dimension of the grid\n * \\tparam TensorTags List of tags of the tensors to be monitored\n */\ntemplate <size_t Dim, typename TensorTags>\nclass TruncationError : public Criterion {\n public:\n  struct VariablesToMonitor {\n    using type = std::vector<std::string>;\n    static constexpr Options::String help = {\n        \"The tensors to monitor the truncation error of.\"};\n    static size_t lower_bound_on_size() { return 1; }\n  };\n  struct AbsoluteTargetTruncationError {\n    static std::string name() { return \"AbsoluteTarget\"; }\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The absolute target truncation error.\"};\n  };\n  struct RelativeTargetTruncationError {\n    static std::string name() { return \"RelativeTarget\"; }\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The relative target truncation error.\"};\n  };\n\n  using options = tmpl::list<VariablesToMonitor, AbsoluteTargetTruncationError,\n                             RelativeTargetTruncationError>;\n\n  static constexpr Options::String help = {\n      \"Refine the grid towards the target truncation error\"};\n\n  TruncationError() = default;\n\n  TruncationError(std::vector<std::string> vars_to_monitor,\n                  double target_abs_truncation_error,\n                  double target_rel_truncation_error,\n                  const Options::Context& context = {});\n\n  /// \\cond\n  explicit TruncationError(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TruncationError);  // NOLINT\n  /// \\endcond\n\n  Type type() override { return Type::p; }\n\n  std::string observation_name() override { return \"TruncationError\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using argument_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList, typename Metavariables>\n  std::array<Flag, Dim> operator()(const db::DataBox<DbTagsList>& box,\n                                   Parallel::GlobalCache<Metavariables>& cache,\n                                   const ElementId<Dim>& element_id) const;\n\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<std::string> vars_to_monitor_{};\n  double target_abs_truncation_error_{};\n  double target_rel_truncation_error_{};\n};\n\n// Out-of-line definitions\n/// \\cond\n\ntemplate <size_t Dim, typename TensorTags>\nTruncationError<Dim, TensorTags>::TruncationError(\n    std::vector<std::string> vars_to_monitor,\n    const double target_abs_truncation_error,\n    const double target_rel_truncation_error, const Options::Context& context)\n    : vars_to_monitor_(std::move(vars_to_monitor)),\n      target_abs_truncation_error_(target_abs_truncation_error),\n      target_rel_truncation_error_(target_rel_truncation_error) {\n  db::validate_selection<TensorTags>(vars_to_monitor_, context);\n}\n\ntemplate <size_t Dim, typename TensorTags>\nTruncationError<Dim, TensorTags>::TruncationError(CkMigrateMessage* msg)\n    : Criterion(msg) {}\n\ntemplate <size_t Dim, typename TensorTags>\ntemplate <typename DbTagsList, typename Metavariables>\nstd::array<Flag, Dim> TruncationError<Dim, TensorTags>::operator()(\n    const db::DataBox<DbTagsList>& box,\n    Parallel::GlobalCache<Metavariables>& /*cache*/,\n    const ElementId<Dim>& /*element_id*/) const {\n  auto result = make_array<Dim>(Flag::Undefined);\n  const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n  std::array<DataVector, Dim> power_monitors_buffer{};\n  // Check all tensors and all tensor components in turn\n  tmpl::for_each<TensorTags>(\n      [&result, &box, &mesh, &power_monitors_buffer, this](const auto tag_v) {\n        // Stop if we have already decided to refine every dimension\n        if (result == make_array<Dim>(Flag::IncreaseResolution)) {\n          return;\n        }\n        using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n        const std::string tag_name = db::tag_name<tag>();\n        // Skip if this tensor is not being monitored\n        if (not alg::found(vars_to_monitor_, tag_name)) {\n          return;\n        }\n        const auto& tensor = db::get<tag>(box);\n        for (const auto& tensor_component : tensor) {\n          TruncationError_detail::max_over_components(\n              make_not_null(&result), make_not_null(&power_monitors_buffer),\n              tensor_component, mesh, target_abs_truncation_error_,\n              target_rel_truncation_error_);\n        }\n      });\n  return result;\n}\n\ntemplate <size_t Dim, typename TensorTags>\nvoid TruncationError<Dim, TensorTags>::pup(PUP::er& p) {\n  p | vars_to_monitor_;\n  p | target_abs_truncation_error_;\n  p | target_rel_truncation_error_;\n}\n\ntemplate <size_t Dim, typename TensorTags>\nPUP::able::PUP_ID TruncationError<Dim, TensorTags>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Type.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n\n#include <ostream>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\nstd::vector<amr::Criteria::Type> known_amr_criteria_types() {\n  return std::vector{amr::Criteria::Type::h, amr::Criteria::Type::p};\n}\n}  // namespace\n\nnamespace amr::Criteria {\n\nstd::ostream& operator<<(std::ostream& os, const Type& type) {\n  switch (type) {\n    case Type::h:\n      os << \"h\";\n      break;\n    case Type::p:\n      os << \"p\";\n      break;\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"An unknown AMR criteria type was passed to the stream operator.\");\n      // LCOV_EXCL_STOP\n  }\n  return os;\n}\n}  // namespace amr::Criteria\n\ntemplate <>\namr::Criteria::Type\nOptions::create_from_yaml<amr::Criteria::Type>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  for (const auto type : known_amr_criteria_types()) {\n    if (type_read == get_output(type)) {\n      return type;\n    }\n  }\n  using ::operator<<;\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read << \"\\\" to amr::Criteria::Type.\\nMust be one of \"\n                  << known_amr_criteria_types() << \".\");\n}\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Criteria/Type.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <iosfwd>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace amr::Criteria {\n\n/// \\ingroup AmrGroup\n/// \\brief Type of mesh refinement\nenum class Type : uint8_t {\n  h, /**< Used to split or join elements */\n  p  /**< used to change the extents of a Mesh in an Element */\n};\n\n/// Output operator for a Type.\nstd::ostream& operator<<(std::ostream& os, const Type& type);\n}  // namespace amr::Criteria\n\ntemplate <>\nstruct Options::create_from_yaml<amr::Criteria::Type> {\n  template <typename Metavariables>\n  static amr::Criteria::Type create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\namr::Criteria::Type\nOptions::create_from_yaml<amr::Criteria::Type>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY AmrEvents)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ObserveAmrStats.cpp\n  RefineMesh.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Events.hpp\n  ObserveAmrCriteria.hpp\n  ObserveAmrStats.hpp\n  RefineMesh.hpp\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_GlobalCache\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Events\n  Utilities\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Events/Events.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup AmrGroup\n/// \\brief %Events for adaptive mesh refinement\nnamespace amr::Events {}  // namespace amr::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Events/ObserveAmrCriteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveConstantsPerElement.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Domain;\ntemplate <size_t VolumeDim>\nclass ElementId;\nnamespace domain {\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\nnamespace Tags {\ntemplate <size_t VolumeDim>\nstruct Domain;\nstruct FunctionsOfTime;\n}  // namespace Tags\n}  // namespace domain\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct Time;\nstruct TimeStep;\n}  // namespace Tags\n/// \\endcond\n\nnamespace amr::Events {\nnamespace detail {\ntemplate <typename Criterion>\nstruct get_compute_tags {\n  using type = typename Criterion::compute_tags_for_observation_box;\n};\n}  // namespace detail\n\n/// \\brief Observe the desired decisions of AMR criteria\n///\n/// \\details The event will return a vector of decisions (an independent choice\n/// in each logical dimension) for each of the AMR criteria.  These are the raw\n/// choices made by each AMR critera, not taking into account any AMR policies.\n/// Each element is output as a single cell with two points per dimension and\n/// the observation constant on all those points.  The decisions are converted\n/// to values as follows (in each logical dimension):\n/// - -2.0 is for join with sibling (if possible)\n/// - -1.0 is for decrease number of grid points\n/// - 0.0 is for no change\n/// - 1.0 is for increase number of grid points\n/// - 2.0 is for splitting the element\ntemplate <typename Metavariables>\nclass ObserveAmrCriteria\n    : public dg::Events::ObserveConstantsPerElement<Metavariables::volume_dim> {\n public:\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  /// \\cond\n  explicit ObserveAmrCriteria(CkMigrateMessage* m)\n      : dg::Events::ObserveConstantsPerElement<volume_dim>(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveAmrCriteria);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help =\n      \"Observe the desired decisions of AMR criteria in the volume.\";\n\n  ObserveAmrCriteria() = default;\n\n  ObserveAmrCriteria(const std::string& subfile_name,\n                     ::FloatingPointType coordinates_floating_point_type,\n                     ::FloatingPointType floating_point_type)\n      : dg::Events::ObserveConstantsPerElement<volume_dim>(\n            subfile_name, coordinates_floating_point_type,\n            floating_point_type) {}\n\n  using compute_tags_for_observation_box =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                   Criterion>,\n          detail::get_compute_tags<tmpl::_1>>>>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::ObservationBox>;\n\n  template <typename DataBoxType, typename ComputeTagsList,\n            typename ParallelComponent>\n  void operator()(const ObservationBox<DataBoxType, ComputeTagsList>& box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<Metavariables::volume_dim>& element_id,\n                  const ParallelComponent* const component,\n                  const Event::ObservationValue& observation_value) const {\n    const auto& refinement_criteria = get<amr::Criteria::Tags::Criteria>(box);\n    const double time = get<::Tags::Time>(box);\n    const auto& functions_of_time = get<::domain::Tags::FunctionsOfTime>(box);\n    const Domain<volume_dim>& domain =\n        get<::domain::Tags::Domain<volume_dim>>(box);\n\n    std::vector<TensorComponent> components = this->allocate_and_insert_coords(\n        volume_dim * refinement_criteria.size(), time, functions_of_time,\n        domain, element_id);\n\n    for (const auto& criterion : refinement_criteria) {\n      const auto decision = criterion->evaluate(box, cache, element_id);\n      for (size_t d = 0; d < volume_dim; ++d) {\n        this->add_constant(\n            make_not_null(&components),\n            criterion->observation_name() +\n                tnsr::i<double, volume_dim,\n                        Frame::ElementLogical>::component_suffix(d),\n            static_cast<double>(gsl::at(decision, d)) - 3.0);\n      }\n    }\n\n    this->observe(components, cache, element_id, component, observation_value);\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  void pup(PUP::er& p) override {\n    dg::Events::ObserveConstantsPerElement<volume_dim>::pup(p);\n  }\n};\n\n/// \\cond\ntemplate <typename Metavariables>\nPUP::able::PUP_ID ObserveAmrCriteria<Metavariables>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace amr::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Events/ObserveAmrStats.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrStats.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <sstream>\n\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace amr::Events {\n\nnamespace detail {\nstd::string FormatAmrStatsOutput::operator()(\n    const double time, const size_t total_num_elements,\n    const size_t total_num_points,\n    const std::vector<size_t>& num_points_per_dim,\n    const std::vector<size_t>& min_points_per_dim,\n    const std::vector<size_t>& max_points_per_dim) const {\n  std::ostringstream oss;\n  oss << \"AMR stats at time: \" << time << \"\\n\"\n      << \"  Total Elements: \" << total_num_elements << \"\\n\"\n      << \"  Total number of grid points: \" << total_num_points << \"\\n\"\n      << \"  Points per Dimension:\\n\";\n  for (size_t d = 0; d < num_points_per_dim.size(); ++d) {\n    oss << \"    Dimension \" << d << \": \" << num_points_per_dim[d] << \" (min \"\n        << min_points_per_dim[d] << \", max \" << max_points_per_dim[d]\n        << \" per element)\\n\";\n  }\n  return oss.str();\n}\nvoid FormatAmrStatsOutput::pup(PUP::er& /*p*/) {}\n}  // namespace detail\n\ntemplate <size_t Dim>\nObserveAmrStats<Dim>::ObserveAmrStats() = default;\n\ntemplate <size_t Dim>\nObserveAmrStats<Dim>::ObserveAmrStats(CkMigrateMessage* m) : Event(m) {}\n\ntemplate <size_t Dim>\nObserveAmrStats<Dim>::ObserveAmrStats(bool print_to_terminal,\n                                      bool observe_per_core)\n    : print_to_terminal_(print_to_terminal),\n      observe_per_core_(observe_per_core) {}\n\ntemplate <size_t Dim>\nvoid ObserveAmrStats<Dim>::pup(PUP::er& p) {\n  Event::pup(p);\n  p | print_to_terminal_;\n  p | observe_per_core_;\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID ObserveAmrStats<Dim>::my_PUP_ID = 0;  // NOLINT\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data) template class ObserveAmrStats<DIM(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n#undef DIM\n#undef INSTANTIATE\n}  // namespace amr::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Events/ObserveAmrStats.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Protocols/ReductionDataFormatter.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace amr::Events {\nnamespace detail {\nusing AmrStatsReductionData = Parallel::ReductionData<\n    // Observation value / time\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n    // Total number of elements\n    Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n    // Total number of grid points\n    Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n    // Number of grid points per dimension: total, min, max\n    Parallel::ReductionDatum<std::vector<size_t>,\n                             funcl::ElementWise<funcl::Plus<>>>,\n    Parallel::ReductionDatum<std::vector<size_t>,\n                             funcl::ElementWise<funcl::Min<>>>,\n    Parallel::ReductionDatum<std::vector<size_t>,\n                             funcl::ElementWise<funcl::Max<>>>>;\n\nstruct FormatAmrStatsOutput\n    : tt::ConformsTo<observers::protocols::ReductionDataFormatter> {\n  using reduction_data = AmrStatsReductionData;\n  std::string operator()(double time, size_t total_num_elements,\n                         size_t total_num_points,\n                         const std::vector<size_t>& num_points_per_dim,\n                         const std::vector<size_t>& min_points_per_dim,\n                         const std::vector<size_t>& max_points_per_dim) const;\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p);\n};\n}  // namespace detail\n\n/*!\n * \\ingroup AmrGroup\n * \\brief Observes AMR statistics, such as number of elements and grid points\n */\ntemplate <size_t Dim>\nclass ObserveAmrStats : public Event {\n public:\n  struct PrintToTerminal {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether to print reduction info to terminal.\"};\n  };\n  struct ObservePerCore {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Also write reduction observations per-core in a file per-node.\"};\n  };\n\n  /// \\cond\n  explicit ObserveAmrStats(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveAmrStats);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<PrintToTerminal, ObservePerCore>;\n  static constexpr Options::String help = {\"Observe AMR statistics\"};\n\n  ObserveAmrStats();\n  ObserveAmrStats(bool print_to_terminal, bool observe_per_core);\n\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<detail::AmrStatsReductionData>>;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using argument_tags = tmpl::list<domain::Tags::Mesh<Dim>>;\n  using return_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ParallelComponent>\n  void operator()(const Mesh<Dim>& mesh,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<Dim>& element_id,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    const auto& mesh_extents = mesh.extents();\n    const std::vector<size_t> mesh_extents_vec{mesh_extents.begin(),\n                                               mesh_extents.end()};\n    detail::AmrStatsReductionData reduction_data{observation_value.value,\n                                                 1_st,\n                                                 mesh.number_of_grid_points(),\n                                                 mesh_extents_vec,\n                                                 mesh_extents_vec,\n                                                 mesh_extents_vec};\n    std::vector<std::string> legend{observation_value.name, \"NumElements\",\n                                    \"TotalNumPoints\"};\n    std::vector<std::string> legend_num_p;\n    std::vector<std::string> legend_min_p;\n    std::vector<std::string> legend_max_p;\n    legend_num_p.reserve(Dim);\n    legend_min_p.reserve(Dim);\n    legend_max_p.reserve(Dim);\n    for (size_t d = 0; d < Dim; ++d) {\n      legend_num_p.push_back(\"NumPointsPerDim_\" + std::to_string(d));\n      legend_min_p.push_back(\"MinPointsPerDim_\" + std::to_string(d));\n      legend_max_p.push_back(\"MaxPointsPerDim_\" + std::to_string(d));\n    }\n    legend.insert(legend.end(), legend_num_p.begin(), legend_num_p.end());\n    legend.insert(legend.end(), legend_min_p.begin(), legend_min_p.end());\n    legend.insert(legend.end(), legend_max_p.begin(), legend_max_p.end());\n\n    auto& local_observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            tmpl::conditional_t<Parallel::is_nodegroup_v<ParallelComponent>,\n                                observers::ObserverWriter<Metavariables>,\n                                observers::Observer<Metavariables>>>(cache));\n    observers::ObservationId observation_id{observation_value.value,\n                                            subfile_path_ + \".dat\"};\n    Parallel::ArrayComponentId array_component_id{\n        std::add_pointer_t<ParallelComponent>{nullptr},\n        Parallel::ArrayIndex<ElementId<Dim>>(element_id)};\n    auto formatter = print_to_terminal_\n                         ? std::make_optional(detail::FormatAmrStatsOutput{})\n                         : std::nullopt;\n    if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {\n      const std::optional<int> observe_with_core_id =\n          observe_per_core_\n              ? std::make_optional(Parallel::my_node<int>(local_observer))\n              : std::nullopt;\n      Parallel::threaded_action<\n          observers::ThreadedActions::CollectReductionDataOnNode>(\n          local_observer, std::move(observation_id),\n          std::move(array_component_id), subfile_path_, std::move(legend),\n          std::move(reduction_data), std::move(formatter),\n          observe_with_core_id);\n    } else {\n      Parallel::simple_action<observers::Actions::ContributeReductionData>(\n          local_observer, std::move(observation_id),\n          std::move(array_component_id), subfile_path_, std::move(legend),\n          std::move(reduction_data), std::move(formatter), observe_per_core_);\n    }\n  }\n\n  using observation_registration_tags = tmpl::list<>;\n\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration() const {\n    return {{observers::TypeOfObservation::Reduction,\n             observers::ObservationKey(subfile_path_ + \".dat\")}};\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  bool print_to_terminal_{false};\n  bool observe_per_core_{false};\n  std::string subfile_path_{\"Amr\"};\n};\n}  // namespace amr::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Events/RefineMesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Events/RefineMesh.hpp\"\n\n#include <pup.h>\n\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n\nnamespace amr::Events {\nRefineMesh::RefineMesh() = default;\n\nRefineMesh::RefineMesh(CkMigrateMessage* m) : Event(m) {}\n\nvoid RefineMesh::pup(PUP::er& p) { Event::pup(p); }\n\nPUP::able::PUP_ID RefineMesh::my_PUP_ID = 0;  // NOLINT\n}  // namespace amr::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Events/RefineMesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <typeinfo>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/EnforcePolicies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\nnamespace evolution::dg::Tags {\ntemplate <size_t Dim>\nstruct MortarNextTemporalId;\n}  // namespace evolution::dg::Tags\n/// \\endcond\n\nnamespace amr::Events {\nnamespace detail {\ntemplate <typename Criterion>\nstruct get_tags {\n  using type = typename Criterion::compute_tags_for_observation_box;\n};\n\n}  // namespace detail\n\n/// \\ingroup AmrGroup\n/// \\brief Performs p-refinement on the domain\n///\n/// \\details\n/// - Loops over all p-refinement criteria specified in the input file.\n///   If no valid p-refinement decision is requested, no change is\n///   made to the Element.\n/// - Updates the Mesh and all return tags of Metavariables::amr::projectors\n///\n/// \\warning This does not communicate the new Mesh to its neighbors, nor does\n/// it update ::domain::Tags::NeighborMesh\nclass RefineMesh : public Event {\n public:\n  /// \\cond\n  explicit RefineMesh(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RefineMesh);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"Perform p-refinement\"};\n\n  RefineMesh();\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using return_tags = tmpl::list<::Tags::DataBox>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename DbTags, typename Metavariables, typename Component>\n  void operator()(const gsl::not_null<db::DataBox<DbTags>*> box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<Metavariables::volume_dim>& element_id,\n                  const Component* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    constexpr size_t volume_dim = Metavariables::volume_dim;\n    if (alg::any_of(\n            db::get<evolution::dg::Tags::MortarNextTemporalId<volume_dim>>(\n                *box),\n            [&](const auto& mortar) {\n              return mortar.second != db::get<::Tags::TimeStepId>(*box);\n            })) {\n      ERROR_NO_TRACE(\n          \"Cannot refine mesh when not temporally aligned with neighbors.\");\n    }\n\n    // Evaluate AMR p-refinement criteria\n    auto overall_decision = make_array<volume_dim>(amr::Flag::Undefined);\n\n    using compute_tags = tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 Criterion>,\n        detail::get_tags<tmpl::_1>>>>;\n    auto observation_box = make_observation_box<compute_tags>(box);\n\n    const auto& refinement_criteria =\n        db::get<amr::Criteria::Tags::Criteria>(*box);\n    for (const auto& criterion : refinement_criteria) {\n      if (criterion->type() == amr::Criteria::Type::h) {\n        continue;\n      }\n      auto decision = criterion->evaluate(observation_box, cache, element_id);\n      ASSERT(alg::none_of(decision,\n                          [](amr::Flag flag) {\n                            return flag == amr::Flag::Split or\n                                   flag == amr::Flag::Join;\n                          }),\n             \"The criterion '\" << typeid(*criterion).name()\n                               << \"' requested h-refinement, but claims to be \"\n                                  \"for p-refinement.\");\n      for (size_t d = 0; d < volume_dim; ++d) {\n        overall_decision[d] = std::max(overall_decision[d], decision[d]);\n      }\n    }\n    // If no refinement criteria requested p-refinement, then set flag to\n    // do nothing\n    for (size_t d = 0; d < volume_dim; ++d) {\n      if (overall_decision[d] == amr::Flag::Undefined) {\n        overall_decision[d] = amr::Flag::DoNothing;\n      }\n    }\n\n    // Now p-refine\n    const auto old_mesh_and_element =\n        std::make_pair(db::get<::domain::Tags::Mesh<volume_dim>>(*box),\n                       db::get<::domain::Tags::Element<volume_dim>>(*box));\n    const auto& old_mesh = old_mesh_and_element.first;\n    const auto& verbosity =\n        db::get<logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>(*box);\n\n    // Enforce policies\n    const auto& policies = db::get<amr::Tags::Policies>(*box);\n    amr::enforce_policies(make_not_null(&overall_decision), policies,\n                          element_id, old_mesh);\n\n    if (alg::any_of(overall_decision, [](amr::Flag flag) {\n          return (flag == amr::Flag::IncreaseResolution or\n                  flag == amr::Flag::DecreaseResolution);\n        })) {\n      db::mutate<::domain::Tags::Mesh<volume_dim>>(\n          [&old_mesh,\n           &overall_decision](const gsl::not_null<Mesh<volume_dim>*> mesh) {\n            *mesh = amr::projectors::mesh(old_mesh, overall_decision);\n          },\n          box);\n\n      if (verbosity >= Verbosity::Debug) {\n        Parallel::printf(\n            \"Increasing order of element %s: %s -> %s\\n\", element_id,\n            old_mesh.extents(),\n            db::get<::domain::Tags::Mesh<volume_dim>>(*box).extents());\n      }\n\n      // Run the projectors.\n      tmpl::for_each<typename Metavariables::amr::projectors>(\n          [&box, &old_mesh_and_element](auto projector_v) {\n            using projector = typename decltype(projector_v)::type;\n            try {\n              db::mutate_apply<projector>(box, old_mesh_and_element);\n            } catch (std::exception& e) {\n              ERROR(\"Error in AMR projector '\"\n                    << pretty_type::get_name<projector>() << \"':\\n\"\n                    << e.what());\n            }\n          });\n    }\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n};\n}  // namespace amr::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY AmrPolicies)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  EnforcePolicies.cpp\n  Isotropy.cpp\n  Limits.cpp\n  Policies.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  EnforcePolicies.hpp\n  Isotropy.hpp\n  Limits.hpp\n  Policies.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DomainStructure\n  Spectral\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/EnforcePolicies.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Policies/EnforcePolicies.hpp\"\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace amr {\ntemplate <size_t Dim>\nvoid enforce_policies(const gsl::not_null<std::array<Flag, Dim>*> amr_decision,\n                      const amr::Policies& amr_policies,\n                      const ElementId<Dim>& element_id, const Mesh<Dim>& mesh) {\n  if (amr_policies.isotropy() == amr::Isotropy::Isotropic) {\n    *amr_decision = make_array<Dim>(*alg::max_element(*amr_decision));\n  }\n\n  if (not amr_policies.allow_coarsening()) {\n    for (size_t d = 0; d < Dim; ++d) {\n      if (gsl::at(*amr_decision, d) < Flag::DoNothing) {\n        gsl::at(*amr_decision, d) = Flag::DoNothing;\n      }\n    }\n  }\n\n  const auto& limits = amr_policies.limits();\n\n  const auto error_if_beyond_limits = [&](const size_t direction,\n                                          const std::string& limit_reached) {\n    if (limits.error_beyond_limits()) {\n      ERROR(\"Tried refining beyond the AMR limits in element \"\n            << element_id << \" but we are not allowed to. In direction \"\n            << direction << \", reached limit \" << limit_reached);\n    }\n  };\n\n  const auto& levels = element_id.refinement_levels();\n  for (size_t d = 0; d < Dim; ++d) {\n    if (gsl::at(*amr_decision, d) == Flag::Join and\n        gsl::at(levels, d) <= limits.minimum_refinement_level()) {\n      error_if_beyond_limits(d, \"MinimumRefinement\");\n      gsl::at(*amr_decision, d) = Flag::DoNothing;\n    }\n    if (gsl::at(*amr_decision, d) == Flag::Split and\n        gsl::at(levels, d) >= limits.maximum_refinement_level()) {\n      error_if_beyond_limits(d, \"MaximumRefinement\");\n      gsl::at(*amr_decision, d) = Flag::DoNothing;\n    }\n    const size_t minimum_resolution = std::max(\n        limits.minimum_resolution(), Spectral::detail::minimum_number_of_points(\n                                         mesh.basis(d), mesh.quadrature(d)));\n    if (gsl::at(*amr_decision, d) == Flag::DecreaseResolution and\n        mesh.extents(d) <= minimum_resolution) {\n      error_if_beyond_limits(d, \"MinimumResolution\");\n      gsl::at(*amr_decision, d) = Flag::DoNothing;\n    }\n    if (gsl::at(*amr_decision, d) == Flag::IncreaseResolution and\n        mesh.extents(d) >= limits.maximum_resolution()) {\n      error_if_beyond_limits(d, \"MaximumResolution\");\n      gsl::at(*amr_decision, d) = Flag::DoNothing;\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                    \\\n  template void enforce_policies(                               \\\n      gsl::not_null<std::array<Flag, DIM(data)>*> amr_decision, \\\n      const amr::Policies& amr_policies,                        \\\n      const ElementId<DIM(data)>& element_id, const Mesh<DIM(data)>& mesh);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/EnforcePolicies.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n/// \\cond\nnamespace amr {\nenum class Flag;\nclass Policies;\n}  // namespace amr\ntemplate <size_t Dim>\nclass ElementId;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace amr {\n/// \\brief Updates amr_decision so that it satisfies amr_policies\n///\n/// \\details\n/// - If amr_policies.isotropy() is Isotropic, the Flag%s are changed to all be\n///   the same as the maximum priority flag\n/// - If any Flag would cause the refinement levels or resolution to violate\n///   their bounds, the Flag is changed to DoNothing.  Note that the minimum\n///   resolution is Basis and Quadrature dependent, so may be higher than the\n///   given amr_policies.refinement_limits().minimum_resolution()\n/// - If an `amr_decision` tried to go beyond the `amr::Limits` in any\n///   direction, this function will error if\n///   `amr::Limits::error_beyond_limits()` is true.\ntemplate <size_t Dim>\nvoid enforce_policies(gsl::not_null<std::array<Flag, Dim>*> amr_decision,\n                      const amr::Policies& amr_policies,\n                      const ElementId<Dim>& element_id, const Mesh<Dim>& mesh);\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/Isotropy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n\n#include <ostream>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\nstd::vector<amr::Isotropy> known_amr_isotropies() {\n  return std::vector{amr::Isotropy::Anisotropic, amr::Isotropy::Isotropic};\n}\n}  // namespace\n\nnamespace amr {\n\nstd::ostream& operator<<(std::ostream& os, const Isotropy& isotropy) {\n  switch (isotropy) {\n    case Isotropy::Anisotropic:\n      os << \"Anisotropic\";\n      break;\n    case Isotropy::Isotropic:\n      os << \"Isotropic\";\n      break;\n    default:\n      ERROR(\"An undefined isotropy was passed to the stream operator.\");\n  }\n  return os;\n}\n}  // namespace amr\n\ntemplate <>\namr::Isotropy Options::create_from_yaml<amr::Isotropy>::create<void>(\n    const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  for (const auto isotropy : known_amr_isotropies()) {\n    if (type_read == get_output(isotropy)) {\n      return isotropy;\n    }\n  }\n  using ::operator<<;\n  PARSE_ERROR(options.context(), \"Failed to convert \\\"\"\n                                     << type_read\n                                     << \"\\\" to amr::Isotropy.\\nMust be one of \"\n                                     << known_amr_isotropies() << \".\");\n}\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/Isotropy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines enum class amr::Isotropy.\n\n#pragma once\n\n#include <iosfwd>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace amr {\n\n/// \\ingroup AmrGroup\n/// \\brief Isotropy of adaptive mesh refinement\nenum class Isotropy {\n  Anisotropic, /**< each dimension can be refined independently */\n  Isotropic    /**< all dimensions must be refined the same */\n};\n\n/// Output operator for isotropy.\nstd::ostream& operator<<(std::ostream& os, const Isotropy& isotropy);\n}  // namespace amr\n\ntemplate <>\nstruct Options::create_from_yaml<amr::Isotropy> {\n  template <typename Metavariables>\n  static amr::Isotropy create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\namr::Isotropy Options::create_from_yaml<amr::Isotropy>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/Limits.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n\n#include <pup.h>\n#include <string>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace amr {\n\nLimits::Limits()\n    : maximum_refinement_level_(ElementId<1>::max_refinement_level),\n      maximum_resolution_(\n          Spectral::maximum_number_of_points<Spectral::Basis::Legendre>) {}\n\nLimits::Limits(\n    const std::optional<std::array<size_t, 2>>& refinement_level_bounds,\n    const std::optional<std::array<size_t, 2>>& resolution_bounds,\n    const bool error_beyond_limits, const Options::Context& context)\n    : minimum_refinement_level_(refinement_level_bounds.has_value()\n                                    ? refinement_level_bounds.value()[0]\n                                    : 0),\n      maximum_refinement_level_(refinement_level_bounds.has_value()\n                                    ? refinement_level_bounds.value()[1]\n                                    : ElementId<1>::max_refinement_level),\n      minimum_resolution_(\n          resolution_bounds.has_value() ? resolution_bounds.value()[0] : 1),\n      maximum_resolution_(\n          resolution_bounds.has_value()\n              ? resolution_bounds.value()[1]\n              : Spectral::maximum_number_of_points<Spectral::Basis::Legendre>),\n      error_beyond_limits_(error_beyond_limits) {\n  if (minimum_refinement_level_ > ElementId<1>::max_refinement_level) {\n    PARSE_ERROR(context,\n                \"RefinementLevel lower bound '\" +\n                    std::to_string(minimum_refinement_level_) +\n                    \"' cannot be larger than '\" +\n                    std::to_string(ElementId<1>::max_refinement_level) + \"'.\");\n  }\n  if (maximum_refinement_level_ > ElementId<1>::max_refinement_level) {\n    PARSE_ERROR(context,\n                \"RefinementLevel upper bound '\" +\n                    std::to_string(maximum_refinement_level_) +\n                    \"' cannot be larger than '\" +\n                    std::to_string(ElementId<1>::max_refinement_level) + \"'.\");\n  }\n  if (minimum_resolution_ < 1) {\n    PARSE_ERROR(context, \"NumGridPoints lower bound '\" +\n                             std::to_string(minimum_resolution_) +\n                             \"' cannot be smaller than '1'.\");\n  }\n  if (maximum_resolution_ < 1) {\n    PARSE_ERROR(context, \"NumGridPoints upper bound '\" +\n                             std::to_string(maximum_resolution_) +\n                             \"' cannot be smaller than '1'.\");\n  }\n  if (minimum_resolution_ >\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>) {\n    PARSE_ERROR(\n        context,\n        \"NumGridPoints lower bound '\" + std::to_string(minimum_resolution_) +\n            \"' cannot be larger than '\" +\n            std::to_string(\n                Spectral::maximum_number_of_points<Spectral::Basis::Legendre>) +\n            \"'.\");\n  }\n  if (maximum_resolution_ >\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>) {\n    PARSE_ERROR(\n        context,\n        \"NumGridPoints upper bound '\" + std::to_string(maximum_resolution_) +\n            \"' cannot be larger than '\" +\n            std::to_string(\n                Spectral::maximum_number_of_points<Spectral::Basis::Legendre>) +\n            \"'.\");\n  }\n  if (minimum_refinement_level_ > maximum_refinement_level_) {\n    PARSE_ERROR(context,\n                \"RefinementLevel lower bound '\" +\n                    std::to_string(minimum_refinement_level_) +\n                    \"' cannot be larger than RefinementLevel upper bound '\" +\n                    std::to_string(maximum_refinement_level_) + \"'.\");\n  }\n  if (minimum_resolution_ > maximum_resolution_) {\n    PARSE_ERROR(context,\n                \"NumGridPoints lower bound '\" +\n                    std::to_string(minimum_resolution_) +\n                    \"' cannot be larger than NumGridPoints upper bound '\" +\n                    std::to_string(maximum_resolution_) + \"'.\");\n  }\n}\n\nLimits::Limits(size_t minimum_refinement_level, size_t maximum_refinement_level,\n               size_t minimum_resolution, size_t maximum_resolution)\n    : minimum_refinement_level_(minimum_refinement_level),\n      maximum_refinement_level_(maximum_refinement_level),\n      minimum_resolution_(minimum_resolution),\n      maximum_resolution_(maximum_resolution) {\n  ASSERT(minimum_refinement_level_ <= maximum_refinement_level_,\n         \"The minimum refinement level '\" +\n             std::to_string(minimum_refinement_level_) +\n             \"' cannot be larger than the maximum refinement level '\" +\n             std::to_string(maximum_refinement_level_) + \"'.\");\n  ASSERT(minimum_resolution_ <= maximum_resolution_,\n         \"The minimum resolution '\" + std::to_string(minimum_resolution_) +\n             \"' cannot be larger than the maximum resolution '\" +\n             std::to_string(maximum_resolution_) + \"'.\");\n}\n\nvoid Limits::pup(PUP::er& p) {\n  p | minimum_refinement_level_;\n  p | maximum_refinement_level_;\n  p | minimum_resolution_;\n  p | maximum_resolution_;\n  p | error_beyond_limits_;\n}\n\nbool operator==(const Limits& lhs, const Limits& rhs) {\n  return lhs.minimum_refinement_level() == rhs.minimum_refinement_level() and\n         lhs.maximum_refinement_level() == rhs.maximum_refinement_level() and\n         lhs.minimum_resolution() == rhs.minimum_resolution() and\n         lhs.maximum_resolution() == rhs.maximum_resolution() and\n         lhs.error_beyond_limits() == rhs.error_beyond_limits();\n}\n\nbool operator!=(const Limits& lhs, const Limits& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/Limits.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace amr {\n/// \\brief The limits on refinement level and resolution for AMR\n///\n/// \\details\n/// - For a default constructed Limits, the refinement level is\n///   bounded between 0 and ElementId<Dim>::max_refinement_level, and the\n///   resolution is bounded between 1 and\n///   Spectral::maximum_number_of_points<Spectral::Basis::Legendre>\n///   which are limits based on the implementation details of ElementId and\n///   Mesh. There is no error for attempting to go beyond the limits.\n/// - If you specify the limits on the refinement levels and resolutions, they\n///   must respect the above limits.\n/// - Depending upon which Spectral::Basis is chosen, the actual minimum\n///   resolution may be higher (usually 2), but this is automatically enforced\n///   by EnforcePolicies.\nclass Limits {\n public:\n  /// Inclusive bounds on the refinement level\n  struct RefinementLevel {\n    using type = Options::Auto<std::array<size_t, 2>>;\n    static constexpr Options::String help = {\n        \"Inclusive bounds on the refinement level for AMR.\"};\n  };\n\n  /// Inclusive bounds on the number of grid points per dimension\n  struct NumGridPoints {\n    using type = Options::Auto<std::array<size_t, 2>>;\n    static constexpr Options::String help = {\n        \"Inclusive bounds on the number of grid points per dimension for AMR.\"};\n  };\n\n  /// \\brief Whether the code should error if EnforcePolicies has to prevent\n  /// refinement from going beyond the given limits.\n  ///\n  /// \\details The Limits class is just a holder for this value, the actual\n  /// error happens in `amr::Actions::EvaluateRefinementCriteria` or\n  /// `amr::Events::RefineMesh`\n  struct ErrorBeyondLimits {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If adaptive mesh refinement tries to go beyond the RefinementLevel or \"\n        \"NumGridPoints limit, error\"};\n  };\n\n  using options = tmpl::list<RefinementLevel, NumGridPoints, ErrorBeyondLimits>;\n\n  static constexpr Options::String help = {\n      \"Limits on refinement level and resolution for adaptive mesh \"\n      \"refinement.\"};\n\n  Limits();\n\n  Limits(const std::optional<std::array<size_t, 2>>& refinement_level_bounds,\n         const std::optional<std::array<size_t, 2>>& resolution_bounds,\n         bool error_beyond_limits, const Options::Context& context = {});\n\n  Limits(size_t minimum_refinement_level, size_t maximum_refinement_level,\n         size_t minimum_resolution, size_t maximum_resolution);\n\n  size_t minimum_refinement_level() const { return minimum_refinement_level_; }\n  size_t maximum_refinement_level() const { return maximum_refinement_level_; }\n  size_t minimum_resolution() const { return minimum_resolution_; }\n  size_t maximum_resolution() const { return maximum_resolution_; }\n  bool error_beyond_limits() const { return error_beyond_limits_; }\n\n  void pup(PUP::er& p);\n\n private:\n  size_t minimum_refinement_level_{0};\n  size_t maximum_refinement_level_{16};\n  size_t minimum_resolution_{1};\n  size_t maximum_resolution_{20};\n  bool error_beyond_limits_{false};\n};\n\nbool operator==(const Limits& lhs, const Limits& rhs);\n\nbool operator!=(const Limits& lhs, const Limits& rhs);\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/Policies.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n\n#include <pup.h>\n\nnamespace amr {\nPolicies::Policies(const amr::Isotropy isotropy, const amr::Limits& limits,\n                   const bool enforce_two_to_one_balance_in_normal_direction,\n                   const bool allow_coarsening)\n    : isotropy_(isotropy),\n      limits_(limits),\n      enforce_two_to_one_balance_in_normal_direction_(\n          enforce_two_to_one_balance_in_normal_direction),\n      allow_coarsening_(allow_coarsening) {}\n\nvoid Policies::pup(PUP::er& p) {\n  p | isotropy_;\n  p | limits_;\n  p | enforce_two_to_one_balance_in_normal_direction_;\n  p | allow_coarsening_;\n}\n\nbool operator==(const Policies& lhs, const Policies& rhs) {\n  return lhs.isotropy() == rhs.isotropy() and lhs.limits() == rhs.limits() and\n         lhs.enforce_two_to_one_balance_in_normal_direction() ==\n             rhs.enforce_two_to_one_balance_in_normal_direction() and\n         lhs.allow_coarsening() == rhs.allow_coarsening();\n}\n\nbool operator!=(const Policies& lhs, const Policies& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/Policies.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace amr {\n/// \\brief A set of runtime policies controlling adaptive mesh refinement\nclass Policies {\n public:\n  /// The isotropy of AMR\n  struct Isotropy {\n    using type = amr::Isotropy;\n    static constexpr Options::String help = {\n        \"Isotropy of adaptive mesh refinement (whether or not each dimension \"\n        \"can be refined independently).\"};\n  };\n\n  /// The limits on refinement level and resolution for AMR\n  struct Limits {\n    using type = amr::Limits;\n    static constexpr Options::String help = {\n        \"Limits on refinement level and resolution for adaptive mesh \"\n        \"refinement.\"};\n  };\n\n  /// Whether or not to enforce 2:1 balance in the direction normal to element\n  /// faces\n  struct EnforceTwoToOneBalanceInNormalDirection {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether or not to enforce 2:1 balance in the direction normal to \"\n        \"element faces.\"};\n  };\n\n  /// Whether or not to allow coarsening or only refining\n  struct AllowCoarsening {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether or not to allow coarsening or only refining.\"};\n  };\n\n  using options =\n      tmpl::list<Isotropy, Limits, EnforceTwoToOneBalanceInNormalDirection,\n                 AllowCoarsening>;\n\n  static constexpr Options::String help = {\n      \"Policies controlling adaptive mesh refinement.\"};\n\n  Policies() = default;\n\n  Policies(amr::Isotropy isotropy, const amr::Limits& limits,\n           bool enforce_two_to_one_balance_in_normal_direction,\n           bool allow_coarsening);\n\n  amr::Isotropy isotropy() const { return isotropy_; }\n\n  amr::Limits limits() const { return limits_; }\n\n  bool enforce_two_to_one_balance_in_normal_direction() const {\n    return enforce_two_to_one_balance_in_normal_direction_;\n  }\n\n  bool allow_coarsening() const { return allow_coarsening_; }\n\n  void pup(PUP::er& p);\n\n private:\n  amr::Isotropy isotropy_{amr::Isotropy::Anisotropic};\n  amr::Limits limits_{};\n  bool enforce_two_to_one_balance_in_normal_direction_{true};\n  bool allow_coarsening_{true};\n};\n\nbool operator==(const Policies& lhs, const Policies& rhs);\n\nbool operator!=(const Policies& lhs, const Policies& rhs);\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Policies/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr {\n/// Option tags for AMR polocies\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// Options for AMR policies\nstruct Policies {\n  static constexpr Options::String help = \"Options for AMR policies\";\n  using type = amr::Policies;\n  using group = amr::OptionTags::AmrGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// The policies for adaptive mesh refinement\nstruct Policies : db::SimpleTag {\n  using type = amr::Policies;\n  using option_tags = tmpl::list<amr::OptionTags::Policies>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) { return value; }\n};\n}  // namespace Tags\n}  // namespace amr\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Projectors/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY AmrProjectors)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Mesh.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CopyFromCreatorOrLeaveAsIs.hpp\n  DefaultInitialize.hpp\n  Mesh.hpp\n  NamespaceDocs.hpp\n  Tensors.hpp\n  Variables.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Amr\n  DataStructures\n  DomainStructure\n  Spectral\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <iterator>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace amr::projectors {\n\n/// \\brief For h-refinement copy the items from the parent/child, while for\n/// p-refinement leave the items unchanged\n///\n/// There is a specialization for\n/// `CopyFromCreatorOrLeaveAsIs<tmpl::list<Tags...>>` that can be used if a\n/// `tmpl::list` is available.\n///\n/// \\details For each item corresponding to each tag:\n/// - When changing resolution (p-refinement), leave unchanged\n/// - When splitting, copy the value from the parent to the child\n/// - When joining, check that the children have the same value, and copy to\n///   the parent\ntemplate <typename... Tags>\nstruct CopyFromCreatorOrLeaveAsIs : tt::ConformsTo<amr::protocols::Projector> {\n  using return_tags = tmpl::list<Tags...>;\n  using argument_tags = tmpl::list<>;\n\n  template <size_t Dim>\n  static void apply(\n      const gsl::not_null<typename Tags::type*>... /*items*/,\n      const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {\n    // do nothing, i.e. leave the items unchanged\n  }\n\n  template <typename... ParentTags>\n  static void apply(const gsl::not_null<typename Tags::type*>... items,\n                    const tuples::TaggedTuple<ParentTags...>& parent_items) {\n    ::expand_pack((*items = tuples::get<Tags>(parent_items))...);\n  }\n\n  template <size_t Dim, typename... ChildrenTags>\n  static void apply(const gsl::not_null<typename Tags::type*>... items,\n                    const std::unordered_map<\n                        ElementId<Dim>, tuples::TaggedTuple<ChildrenTags...>>&\n                        children_items) {\n    const auto& first_child_items = children_items.begin();\n#ifdef SPECTRE_DEBUG\n    for (auto it = std::next(first_child_items); it != children_items.end();\n         ++it) {\n      const bool children_agree =\n          ((tuples::get<Tags>(first_child_items->second) ==\n            tuples::get<Tags>(it->second)) and\n           ...);\n      ASSERT(children_agree, \"Children do not agree on all items!\");\n    }\n#endif  // #ifdef SPECTRE_DEBUG\n    ::expand_pack((*items = tuples::get<Tags>(first_child_items->second))...);\n  }\n};\n\n/// \\cond\ntemplate <typename... Tags>\nstruct CopyFromCreatorOrLeaveAsIs<tmpl::list<Tags...>>\n    : public CopyFromCreatorOrLeaveAsIs<Tags...> {};\n/// \\endcond\n}  // namespace amr::projectors\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Element;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace amr::projectors {\n\n/// \\brief Value initialize the items corresponding to Tags\n///\n/// There is a specialization for `DefaultInitialize<tmpl::list<Tags...>>` that\n/// can be used if a `tmpl::list` is available.\n///\n/// \\details For each item corresponding to each tag, value initialize\n/// the item by setting it equal to an object constructed with an\n/// empty initializer.  This is the default state of mutable items in\n/// a DataBox if they are neither set from input file options, nor\n/// mutated by initialization actions.\ntemplate <typename... Tags>\nstruct DefaultInitialize : tt::ConformsTo<amr::protocols::Projector> {\n  using return_tags = tmpl::list<Tags...>;\n  using argument_tags = tmpl::list<>;\n\n  template <size_t Dim>\n  static void apply(\n      const gsl::not_null<typename Tags::type*>... items,\n      const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {\n    expand_pack((*items = std::decay_t<decltype(*items)>{})...);\n  }\n\n  template <typename FinalArg>\n  static void apply(const gsl::not_null<typename Tags::type*>... /*items*/,\n                    const FinalArg& /*parent_or_children_items*/) {\n    // Mutable items on newly created elements are already value initialized\n  }\n};\n\n/// \\cond\ntemplate <typename... Tags>\nstruct DefaultInitialize<tmpl::list<Tags...>>\n    : public DefaultInitialize<Tags...> {};\n/// \\endcond\n}  // namespace amr::projectors\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Projectors/Mesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <deque>\n#include <numeric>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace amr::projectors {\ntemplate <size_t Dim>\nMesh<Dim> mesh(const Mesh<Dim>& old_mesh,\n               const std::array<amr::Flag, Dim>& flags) {\n  std::array<size_t, Dim> new_extents = old_mesh.extents().indices();\n  for (size_t d = 0; d < Dim; ++d) {\n    if (gsl::at(flags, d) == amr::Flag::IncreaseResolution) {\n      ++gsl::at(new_extents, d);\n    } else if (gsl::at(flags, d) == amr::Flag::DecreaseResolution) {\n      --gsl::at(new_extents, d);\n    }\n  }\n  return {new_extents, old_mesh.basis(), old_mesh.quadrature()};\n}\n\ntemplate <size_t Dim>\nMesh<Dim> parent_mesh(const std::vector<Mesh<Dim>>& children_meshes) {\n  const auto& parent_quadrature = children_meshes.front().quadrature();\n  const auto& parent_basis = children_meshes.front().basis();\n\n  ASSERT(alg::all_of(\n             children_meshes,\n             [&parent_quadrature, &parent_basis](const Mesh<Dim>& child_mesh) {\n               return (child_mesh.quadrature() == parent_quadrature and\n                       child_mesh.basis() == parent_basis);\n             }),\n         \"AMR does not currently support joining elements with different \"\n         \"quadratures or bases\");\n\n  // loop over each mesh, returning an array containing the max extent in each\n  // dimension\n  auto parent_extents = std::accumulate(\n      std::next(children_meshes.begin()), children_meshes.end(),\n      children_meshes.front().extents().indices(),\n      [](auto&& extents, const Mesh<Dim>& mesh) {\n        alg::transform(extents, mesh.extents().indices(), extents.begin(),\n                       [](size_t a, size_t b) { return std::max(a, b); });\n        return extents;\n      });\n\n  return {parent_extents, parent_basis, parent_quadrature};\n}\n\ntemplate <size_t Dim>\nMesh<Dim> new_mesh(\n    const Mesh<Dim>& current_mesh, const std::array<Flag, Dim>& flags,\n    const Element<Dim>& element,\n    const std::unordered_map<ElementId<Dim>, Info<Dim>>& neighbors_info) {\n  // If we are joining, the extents of the new mesh in each dimension will be\n  // the maximum of that of the element and the joining neighbors\n  if (alg::count(flags, Flag::Join) > 0 and not neighbors_info.empty()) {\n    const auto joining_neighbors = ids_of_joining_neighbors(element, flags);\n    std::vector<Mesh<Dim>> children_meshes;\n    children_meshes.reserve(8);\n    children_meshes.push_back(mesh(current_mesh, flags));\n    for (const auto& [neighbor_id, neighbor_info] : neighbors_info) {\n      if (alg::count(joining_neighbors, neighbor_id) > 0) {\n        children_meshes.push_back(neighbor_info.new_mesh);\n      }\n    }\n    return parent_mesh(children_meshes);\n  }\n\n  return mesh(current_mesh, flags);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template Mesh<DIM(data)> mesh(                                       \\\n      const Mesh<DIM(data)>& old_mesh,                                 \\\n      const std::array<amr::Flag, DIM(data)>& flags);                  \\\n  template Mesh<DIM(data)> parent_mesh(                                \\\n      const std::vector<Mesh<DIM(data)>>& children_meshes);            \\\n  template Mesh<DIM(data)> new_mesh(                                   \\\n      const Mesh<DIM(data)>& current_mesh,                             \\\n      const std::array<Flag, DIM(data)>& flags,                        \\\n      const Element<DIM(data)>& element,                               \\\n      const std::unordered_map<ElementId<DIM(data)>, Info<DIM(data)>>& \\\n          neighbors_info);\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace amr::projectors\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Projectors/Mesh.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n#include <vector>\n\n/// \\cond\nnamespace amr {\nenum class Flag;\ntemplate <size_t>\nstruct Info;\n}  // namespace amr\ntemplate <size_t>\nclass Element;\ntemplate <size_t>\nclass ElementId;\ntemplate <size_t>\nclass Mesh;\n/// \\endcond\n\nnamespace amr::projectors {\n/// Given `old_mesh` returns a new Mesh based on the refinement `flags`\ntemplate <size_t Dim>\nMesh<Dim> mesh(const Mesh<Dim>& old_mesh,\n               const std::array<amr::Flag, Dim>& flags);\n\n/// Given the Mesh%es for a set of joining Element%s, returns the Mesh\n/// for the newly created parent Element.\n///\n/// \\details In each dimension the extent of the parent Mesh will be the\n/// maximum over the extents of each child Mesh\ntemplate <size_t Dim>\nMesh<Dim> parent_mesh(const std::vector<Mesh<Dim>>& children_meshes);\n\n/// \\brief Computes the new Mesh of an Element after AMR\n///\n/// \\details The returned Mesh will be that of either `element` or its parent or\n/// children depending upon the `flags`.  If an Element is joining, the returned\n/// Mesh will be that given by amr::projectors::parent_mesh; otherwise it will\n/// be given by amr::projectors::mesh\ntemplate <size_t Dim>\nMesh<Dim> new_mesh(\n    const Mesh<Dim>& current_mesh, const std::array<Flag, Dim>& flags,\n    const Element<Dim>& element,\n    const std::unordered_map<ElementId<Dim>, Info<Dim>>& neighbors_info);\n}  // namespace amr::projectors\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Projectors/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// AMR projectors\nnamespace amr::projectors {}\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Projectors/Tensors.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::projectors {\n\n/// \\brief Update the Tensors corresponding to TensorTags after an AMR\n/// change\n///\n/// There is a specialization for\n/// `ProjectTensors<tmpl::list<TensorTags...>>` that can be used if a\n/// `tmpl::list` is available.\n///\n/// \\details For each item corresponding to each tag in TensorTags, project\n/// the data for each tensor from the old mesh to the new mesh\n///\n/// \\see ProjectVariables\ntemplate <size_t Dim, typename... TensorTags>\nstruct ProjectTensors : tt::ConformsTo<amr::protocols::Projector> {\n  using return_tags = tmpl::list<TensorTags...>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>>;\n\n  static void apply(\n      const gsl::not_null<typename TensorTags::type*>... tensors,\n      const Element<Dim>& /*element*/, const Mesh<Dim>& new_mesh,\n      const std::pair<Mesh<Dim>, Element<Dim>>& old_mesh_and_element) {\n    const auto& old_mesh = old_mesh_and_element.first;\n    if (old_mesh == new_mesh) {\n      return;  // mesh was not refined, so no projection needed\n    }\n    const auto projection_matrices =\n        Spectral::p_projection_matrices(old_mesh, new_mesh);\n    const auto& old_extents = old_mesh.extents();\n    const auto project_tensor = [&projection_matrices,\n                                 old_extents](auto& tensor) {\n      for (size_t i = 0; i < tensor.size(); ++i) {\n        tensor[i] = apply_matrices(projection_matrices, tensor[i], old_extents);\n      }\n    };\n    EXPAND_PACK_LEFT_TO_RIGHT(project_tensor(*tensors));\n  }\n\n  template <typename... Tags>\n  static void apply(const gsl::not_null<typename TensorTags::type*>... tensors,\n                    const Element<Dim>& element, const Mesh<Dim>& new_mesh,\n                    const tuples::TaggedTuple<Tags...>& parent_items) {\n    const auto& element_id = element.id();\n    const auto& parent_id = get<domain::Tags::Element<Dim>>(parent_items).id();\n    const auto& parent_mesh = get<domain::Tags::Mesh<Dim>>(parent_items);\n    const auto child_sizes =\n        domain::child_size(element_id.segment_ids(), parent_id.segment_ids());\n    const auto projection_matrices =\n        Spectral::projection_matrix_parent_to_child(parent_mesh, new_mesh,\n                                                    child_sizes);\n    const auto project_tensor = [&](auto& new_tensor, const auto& old_tensor) {\n      for (size_t i = 0; i < new_tensor.size(); ++i) {\n        new_tensor[i] = apply_matrices(projection_matrices, old_tensor[i],\n                                       parent_mesh.extents());\n      }\n      return 0;\n    };\n    expand_pack(project_tensor(*tensors, get<TensorTags>(parent_items))...);\n  }\n\n  template <typename... Tags>\n  static void apply(\n      const gsl::not_null<typename TensorTags::type*>... tensors,\n      const Element<Dim>& element, const Mesh<Dim>& new_mesh,\n      const std::unordered_map<ElementId<Dim>, tuples::TaggedTuple<Tags...>>&\n          children_items) {\n    const auto& element_id = element.id();\n    bool first_child = true;\n    for (const auto& [child_id, child_items] : children_items) {\n      const auto& child_mesh = get<domain::Tags::Mesh<Dim>>(child_items);\n      const auto child_sizes =\n          domain::child_size(child_id.segment_ids(), element_id.segment_ids());\n      const auto projection_matrices =\n          Spectral::projection_matrix_child_to_parent(child_mesh, new_mesh,\n                                                      child_sizes);\n      if (first_child) {\n        const auto project_tensor = [&](auto& new_tensor,\n                                        const auto& old_tensor) {\n          for (size_t i = 0; i < new_tensor.size(); ++i) {\n            new_tensor[i] = apply_matrices(projection_matrices, old_tensor[i],\n                                           child_mesh.extents());\n          }\n          return 0;\n        };\n        expand_pack(project_tensor(*tensors, get<TensorTags>(child_items))...);\n        first_child = false;\n      } else {\n        const auto project_tensor = [&](auto& new_tensor,\n                                        const auto& old_tensor) {\n          for (size_t i = 0; i < new_tensor.size(); ++i) {\n            new_tensor[i] += apply_matrices(projection_matrices, old_tensor[i],\n                                            child_mesh.extents());\n          }\n          return 0;\n        };\n        expand_pack(project_tensor(*tensors, get<TensorTags>(child_items))...);\n      }\n    }\n  }\n};\n\n/// \\cond\ntemplate <size_t Dim, typename... TensorTags>\nstruct ProjectTensors<Dim, tmpl::list<TensorTags...>>\n    : public ProjectTensors<Dim, TensorTags...> {};\n/// \\endcond\n}  // namespace amr::projectors\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Projectors/Variables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::projectors {\n\n/// \\brief Update the Variables corresponding to VariablesTags after an AMR\n/// change\n///\n/// There is a specialization for\n/// `ProjectVariables<tmpl::list<VariablesTags...>>` that can be used if a\n/// `tmpl::list` is available.\n///\n/// \\details For each item corresponding to each tag in VariablesTags, project\n/// the data for each variable from the old mesh to the new mesh\n///\n/// \\see ProjectTensors\ntemplate <size_t Dim, typename... VariablesTags>\nstruct ProjectVariables : tt::ConformsTo<amr::protocols::Projector> {\n  using return_tags = tmpl::list<VariablesTags...>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>>;\n\n  // p-refinement\n  static void apply(\n      const gsl::not_null<typename VariablesTags::type*>... vars,\n      const Element<Dim>& /*element*/, const Mesh<Dim>& new_mesh,\n      const std::pair<Mesh<Dim>, Element<Dim>>& old_mesh_and_element) {\n    const auto& old_mesh = old_mesh_and_element.first;\n    if (old_mesh == new_mesh) {\n      return;  // mesh was not refined, so no projection needed\n    }\n    const auto projection_matrices =\n        Spectral::p_projection_matrices(old_mesh, new_mesh);\n    const auto& old_extents = old_mesh.extents();\n    expand_pack(\n        (*vars = apply_matrices(projection_matrices, *vars, old_extents))...);\n  }\n\n  // h-refinement\n  template <typename... Tags>\n  static void apply(const gsl::not_null<typename VariablesTags::type*>... vars,\n                    const Element<Dim>& element, const Mesh<Dim>& new_mesh,\n                    const tuples::TaggedTuple<Tags...>& parent_items) {\n    const auto& element_id = element.id();\n    const auto& parent_id = get<domain::Tags::Element<Dim>>(parent_items).id();\n    const auto& parent_mesh = get<domain::Tags::Mesh<Dim>>(parent_items);\n    const auto child_sizes =\n        domain::child_size(element_id.segment_ids(), parent_id.segment_ids());\n    const auto projection_matrices =\n        Spectral::projection_matrix_parent_to_child(parent_mesh, new_mesh,\n                                                    child_sizes);\n    expand_pack((*vars = apply_matrices(projection_matrices,\n                                        get<VariablesTags>(parent_items),\n                                        parent_mesh.extents()))...);\n  }\n\n  // h-coarsening\n  template <typename... Tags>\n  static void apply(\n      const gsl::not_null<typename VariablesTags::type*>... vars,\n      const Element<Dim>& element, const Mesh<Dim>& new_mesh,\n      const std::unordered_map<ElementId<Dim>, tuples::TaggedTuple<Tags...>>&\n          children_items) {\n    const auto& element_id = element.id();\n    bool first_child = true;\n    for (const auto& [child_id, child_items] : children_items) {\n      const auto& child_mesh = get<domain::Tags::Mesh<Dim>>(child_items);\n      const auto child_sizes =\n          domain::child_size(child_id.segment_ids(), element_id.segment_ids());\n      const auto projection_matrices =\n          Spectral::projection_matrix_child_to_parent(child_mesh, new_mesh,\n                                                      child_sizes);\n      if (first_child) {\n        expand_pack((*vars = apply_matrices(projection_matrices,\n                                            get<VariablesTags>(child_items),\n                                            child_mesh.extents()))...);\n        first_child = false;\n      } else {\n        expand_pack((*vars += apply_matrices(projection_matrices,\n                                             get<VariablesTags>(child_items),\n                                             child_mesh.extents()))...);\n      }\n    }\n  }\n};\n\n/// \\cond\ntemplate <size_t Dim, typename... VariableTags>\nstruct ProjectVariables<Dim, tmpl::list<VariableTags...>>\n    : public ProjectVariables<Dim, VariableTags...> {};\n/// \\endcond\n}  // namespace amr::projectors\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::protocols {\n/// \\brief Compile-time information for AMR projectors\n///\n/// A class conforming to this protocol is placed in the metavariables to\n/// provide the following:\n/// - `element_array`: The array component on which AMR is performed.\n/// - `projectors`: A type list of AMR projectors (each of which must conform to\n///   amr::protocols::Projector) that will be applied by:\n///     - amr::Actions::InitializeChild and amr::Actions::InitializeParent in\n///       order to initialize data on newly created elements.\n///     - amr::Actions::AdjustDomain in order to update data on existing\n///       elements in case their Mesh or neighbors have changed.\n///   In these projectors you must handle _all_ mutable tags in the DataBox,\n///   except for a few tags that are handled by the AMR actions themselves\n///   (e.g. `domain::Tags::Element`, `domain::Tags::Mesh`, and\n///   `domain::Tags::NeighborMesh` are handled by AMR). See\n///   `amr::protocols::Projector` for details.\n/// - `p_refine_only_in_event`: A boolean indicating that only h-refinement\n///   criteria should be evaluated in\n///   `::amr::Actions::EvaluateRefinementCriteria`.  Only h-refinement will be\n///   done in `Phase::AdjustDomain`; p-refinement will only be done via\n///   `::amr::Events::RefineMesh`.\n/// - `keep_coarse_grids`: A boolean indicating that AMR should create a\n///   completely new grid at each AMR step with an incremented grid index, and\n///   keep the old grid around. This is useful for multigrid solvers.\n///   If this is true, then the `element_array` must include\n///   `::amr::Actions::RegisterElement` in the registration phase action list\n///   and in `Metavariables::registration::element_registrars`. You must also\n///   ensure to visit `Phase::UpdateSections` in the default phase order after\n///   registration, and in each AMR step after\n///   `Phase::EvaluateRefinementCriteria` and `Phase::AdjustDomain`.\n///   When this is enabled, you can use `amr::Tags::ParentId` and\n///   `amr::Tags::ChildIds` to traverse the grid hierarchy. However, you cannot\n///   rely on these tags to be up-to-date in the AMR projectors, as they are\n///   sometime updated after the projectors are run.\n///\n/// Here is an example for a class conforming to this protocol:\n///\n/// \\snippet Amr/Test_Protocols.cpp amr_projectors\nstruct AmrMetavariables {\n  template <typename ConformingType>\n  struct test {\n    using element_array = typename ConformingType::element_array;\n    using projectors = typename ConformingType::projectors;\n    static_assert(\n        tmpl::all<projectors,\n                  tt::assert_conforms_to<tmpl::_1, Projector>>::value);\n    static constexpr bool keep_coarse_grids = ConformingType::keep_coarse_grids;\n    static constexpr bool p_refine_only_in_event =\n        ConformingType::p_refine_only_in_event;\n  };\n};\n}  // namespace amr::protocols\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AmrMetavariables.hpp\n  NamespaceDocs.hpp\n  Projector.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Protocols/NamespaceDocs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// AMR protocols\nnamespace amr::protocols {}\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Protocols/Projector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace amr::protocols {\n\n/// \\brief A DataBox mutator used in AMR actions\n///\n/// A class conforming to this protocol can be used as a projector in the list\n/// of `projectors` for a class conforming to amr::protocols::AmrMetavariables.\n/// The conforming class will be used when adaptive mesh refinement occurs to\n/// either initialize items on a newly created element of a DgElementArray, or\n/// update items on an existing element.\n///\n/// The conforming class must provide the following:\n/// - `return_tags`: A type list of tags corresponding to mutable items in the\n///   DataBox that may be modified during refinement.\n/// - `argument_tags`: A type list of tags corresponding to items in the DataBox\n///   that are not changed, but used to initialize/update the items\n///   corresponding to the `return_tags`.\n/// - `apply`:  static functions whose return value are void, and that take as\n///   arguments:\n///      - A `const gsl::not_null<Tag::type*>` for each `Tag` in `return_tags`\n///      - A `const db::const_item_type<Tag, BoxTags>` for each `Tag` in\n///        `argument_tags`\n///      - and one additional argument which is either:\n///           - `const std::pair<Mesh<Dim>, Element<Dim>>&` (used by\n///             amr::Actions::AdjustDomain)\n///           - `const tuples::TaggedTuple<Tags...>&` (used by\n///             amr::Actions::InitializeChild)\n///           - `const std::unordered_map<ElementId<Dim>,\n///                                       tuples::TaggedTuple<Tags...>>&`\n///             (used by amr::Actions::InitializeParent)\n///\n///   The Mesh and Element passed to amr::Actions::AdjustDomain are their\n///   values before the grid changes.  The tuples passed to\n///   amr::Actions::InitializeChild and amr::Actions::InitializeParent hold the\n///   items corresponding to the `DataBox<BoxTags>::mutable_item_creation_tags`\n///   of the parent (children) of the child (parent) being initialized.\n///\n/// \\note In amr::Actions::AdjustDomain the projectors are called on\n/// all elements that were not h-refined (i.e. split or joined) even\n/// if their Mesh did not change.  This allows a projector to mutate a\n/// mutable item that depends upon information about the neighboring\n/// elements.  Therefore a particular projector may want to check\n/// whether or not the Mesh changed before projecting any data.\n///\n/// For examples, see Initialization::ProjectTimeStepping and\n/// evolution::dg::Initialization::ProjectDomain\nstruct Projector {\n  template <typename ConformingType>\n  struct test {\n    using argument_tags = typename ConformingType::argument_tags;\n    using return_tags = typename ConformingType::return_tags;\n  };\n};\n}  // namespace amr::protocols\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Structure/BlockGroups.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace amr::Tags {\n\ntemplate <size_t Dim>\nstd::optional<std::vector<size_t>> AmrBlocks<Dim>::create_from_options(\n    const std::optional<std::vector<std::string>>& block_names,\n    const std::unique_ptr<::DomainCreator<Dim>>& domain_creator) {\n  if (not block_names.has_value()) {\n    return std::nullopt;\n  }\n  return domain::block_ids_from_names(block_names.value(),\n                                      domain_creator->block_names(),\n                                      domain_creator->block_groups());\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                    \\\n  template std::optional<std::vector<size_t>>                     \\\n  AmrBlocks<DIM(data)>::create_from_options(                      \\\n      const std::optional<std::vector<std::string>>& block_names, \\\n      const std::unique_ptr<::DomainCreator<DIM(data)>>& domain_creator);\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n#undef INSTANTIATION\n#undef DIM\n\n}  // namespace amr::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/Amr/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <string>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Options for AMR\nnamespace amr::OptionTags {\n\n/// Group for AMR options\nstruct AmrGroup {\n  static std::string name() { return \"Amr\"; }\n  static constexpr Options::String help =\n      \"Options for adaptive mesh refinement (AMR)\";\n};\n\n/// Maximum number of AMR levels to keep.\nstruct MaxCoarseLevels {\n  using type = Options::Auto<size_t>;\n  static constexpr Options::String help =\n      \"Maximum number of coarser AMR levels to keep. A value of '0' means that \"\n      \"only the finest grid is kept, and 'Auto' means the number of levels \"\n      \"is not restricted.\";\n  using group = AmrGroup;\n};\n\n/// Blocks in which AMR is enabled.\nstruct AmrBlocks {\n  static std::string name() { return \"EnableInBlocks\"; }\n  struct All {};\n  using type = Options::Auto<std::vector<std::string>, All>;\n  static constexpr Options::String help =\n      \"Blocks in which AMR is enabled. Note that blocks that do not have AMR \"\n      \"enabled can still be refined to satisfy 2:1 balance. When this happens, \"\n      \"they won't coarsen back since 'do nothing' has higher priority than \"\n      \"coarsening. This behavior can be changed if needed.\";\n  using group = AmrGroup;\n};\n\n}  // namespace amr::OptionTags\n\n/// AMR tags\nnamespace amr::Tags {\n\n/// Maximum number of AMR levels that will be kept. A value of '0' means that\n/// only the finest grid is kept, and `std::nullopt` means the number of levels\n/// is not restricted.\nstruct MaxCoarseLevels : db::SimpleTag {\n  using type = std::optional<size_t>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::MaxCoarseLevels>;\n  static type create_from_options(const type value) { return value; }\n};\n\n/// The set of block IDs in which AMR is enabled. If `std::nullopt`, AMR is\n/// enabled in all blocks.\ntemplate <size_t Dim>\nstruct AmrBlocks : db::SimpleTag {\n  using type = std::optional<std::vector<size_t>>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::AmrBlocks,\n                                 ::domain::OptionTags::DomainCreator<Dim>>;\n  static type create_from_options(\n      const std::optional<std::vector<std::string>>& block_names,\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator);\n};\n\n/// All element IDs grouped by grid index are stored in this tag. The element\n/// IDs are registered and deregistered during AMR.\ntemplate <size_t Dim>\nstruct AllElementIds : db::SimpleTag {\n  using type = std::map<size_t, std::unordered_set<ElementId<Dim>>>;\n};\n\n/// The ID of the element that covers the same region or more on the coarser\n/// (parent) grid. Only important if AMR is configured to keep coarse grids\n/// around.\ntemplate <size_t Dim>\nstruct ParentId : db::SimpleTag {\n  using type = std::optional<ElementId<Dim>>;\n};\n\n/// The IDs of the elements that cover the same region on the finer (child)\n/// grid. Only important if AMR is configured to keep coarse grids around.\ntemplate <size_t Dim>\nstruct ChildIds : db::SimpleTag {\n  using type = std::unordered_set<ElementId<Dim>>;\n};\n\n/// The mesh of the parent element. Needed for projections between grids.\n/// Only important if AMR is configured to keep coarse grids around.\ntemplate <size_t Dim>\nstruct ParentMesh : db::SimpleTag {\n  using type = std::optional<Mesh<Dim>>;\n};\n\n/// The AMR level of the element. This is used to tag a\n/// `Parallel::Tags::Section` that contains all elements on the same grid.\n/// Only important if AMR is configured to keep coarse grids around.\nstruct GridIndex {\n  using type = size_t;\n};\n\n/// True on the finest AMR grid (the one with the highest grid index), false on\n/// all other grids. This is used to tag a `Parallel::Tags::Section` that\n/// contains all elements on the finest grid.\n/// Only important if AMR is configured to keep coarse grids around.\nstruct IsFinestGrid {\n  using type = bool;\n};\n\n/// An `observers::Tags::ObservationKey` that identifies the grid index.\n/// Can be used to tag observations with the grid index.\ntemplate <size_t Dim>\nstruct GridIndexObservationKeyCompute\n    : db::ComputeTag,\n      observers::Tags::ObservationKey<GridIndex> {\n  using base = observers::Tags::ObservationKey<GridIndex>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<domain::Tags::Element<Dim>, amr::Tags::ChildIds<Dim>>;\n  static void function(\n      const gsl::not_null<std::optional<std::string>*> observation_key,\n      const Element<Dim>& element,\n      const std::unordered_set<ElementId<Dim>>& child_ids) {\n    const auto& element_id = element.id();\n    const bool is_finest_grid = child_ids.empty();\n    *observation_key =\n        is_finest_grid\n            ? std::string{\"\"}\n            : (std::string{\"Level\"} + std::to_string(element_id.grid_index()));\n  }\n};\n\n/// An `observers::Tags::ObservationKey` that identifies the finest grid.\n/// Can be used to observe things only on the finest grid.\ntemplate <size_t Dim>\nstruct IsFinestGridObservationKeyCompute\n    : db::ComputeTag,\n      observers::Tags::ObservationKey<IsFinestGrid> {\n  using base = observers::Tags::ObservationKey<IsFinestGrid>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<amr::Tags::ChildIds<Dim>>;\n  static void function(\n      const gsl::not_null<std::optional<std::string>*> observation_key,\n      const std::unordered_set<ElementId<Dim>>& child_ids) {\n    const bool is_finest_grid = child_ids.empty();\n    if (is_finest_grid) {\n      *observation_key = std::string{\"\"};\n    } else {\n      *observation_key = std::nullopt;\n    }\n  }\n};\n\n}  // namespace amr::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/ApparentHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief Contains utilities for working with apparent horizons.\n */\nnamespace ah {}\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ApparentHorizonFinder)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CleanUp.cpp\n  ComputeCoords.cpp\n  ComputeVarsToInterpolateToTarget.cpp\n  CurrentTime.cpp\n  Destination.cpp\n  FastFlow.cpp\n  InterpolateVolumeVars.cpp\n  OptionTags.cpp\n  Storage.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ApparentHorizon.hpp\n  CleanUp.hpp\n  Component.hpp\n  ComputeCoords.hpp\n  ComputeVarsToInterpolateToTarget.hpp\n  CurrentTime.hpp\n  Destination.hpp\n  FastFlow.hpp\n  FindApparentHorizon.hpp\n  HorizonAliases.hpp\n  Initialization.hpp\n  InterpolateVolumeVars.hpp\n  OptionTags.hpp\n  PrintDeadlockAnalysis.hpp\n  Storage.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  GrSurfaces\n  FiniteDifference\n  GeneralRelativity\n  LinearAlgebra\n  LinearOperators\n  Logging\n  Options\n  Printf\n  SphericalHarmonics\n  PRIVATE\n  CoordinateMaps\n  Domain\n  INTERFACE\n  Initialization\n  Observer\n  Parallel\n  )\n\nadd_subdirectory(Callbacks)\nadd_subdirectory(Criteria)\nadd_subdirectory(Events)\nadd_subdirectory(Protocols)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Callbacks.hpp\n  FailedHorizonFind.hpp\n  InvokeCallbacks.hpp\n  ObserveCenters.hpp\n  ObserveFieldsOnHorizon.hpp\n  ObserveTimeSeriesOnHorizon.hpp\n  SendDependencyToObserverWriter.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/Callbacks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief Callbacks used by the apparent horizon finder.\n */\nnamespace ah::callbacks {}\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <set>\n#include <sstream>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace ah::callbacks {\n/*!\n * \\brief Callback when an apparent horizon find fails. The template \\p Ignore\n * says whether to ignore the failure or raise an ERROR.\n */\ntemplate <typename HorizonMetavars, bool Ignore>\nstruct FailedHorizonFind : tt::ConformsTo<ah::protocols::Callback> {\n private:\n  using Fr = typename HorizonMetavars::frame;\n\n public:\n  template <typename DbTags, typename Metavariables>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const FastFlow::Status failure_reason) {\n    std::ostringstream os{};\n\n    const auto& time = db::get<ah::Tags::CurrentTime>(box).value();\n    const auto& all_storage = db::get<ah::Tags::Storage<Fr>>(box);\n    const auto& current_time_storage = all_storage.at(time);\n    const auto& current_iteration = current_time_storage.current_iteration;\n\n    os << pretty_type::name<HorizonMetavars>()\n       << \": Horizon find failed at time \" << time\n       << \". Reason = \" << failure_reason\n       << \". Number of compute coords retries = \"\n       << current_iteration.compute_coords_retries << \".\";\n\n    if (failure_reason == FastFlow::Status::InterpolationFailure) {\n      const auto& block_coord_holders =\n          current_iteration.block_coord_holders.value();\n\n      std::vector<size_t> missing_indices{};\n      missing_indices.reserve(block_coord_holders.size());\n      for (size_t i = 0; i < block_coord_holders.size(); i++) {\n        if (not block_coord_holders[i].has_value()) {\n          missing_indices.push_back(i);\n        }\n      }\n\n      // Get the actual points\n      const auto& strahlkorper = current_iteration.strahlkorper;\n      const auto& fast_flow = db::get<ah::Tags::FastFlow>(box);\n      const size_t l_mesh = fast_flow.current_l_mesh(strahlkorper);\n      const auto prolonged_strahlkorper =\n          ylm::Strahlkorper<Fr>(l_mesh, l_mesh, strahlkorper);\n      const auto coords = ylm::cartesian_coords(prolonged_strahlkorper);\n\n      // Now output some information about them\n      os << \"\\n Invalid points (in \" << pretty_type::name<Fr>()\n         << \" frame) are:\\n\";\n      for (const size_t index : missing_indices) {\n        os << \" (\" << get<0>(coords)[index] << \",\" << get<1>(coords)[index]\n           << \",\" << get<2>(coords)[index] << \")\\n\";\n      }\n    }\n\n    if constexpr (Ignore) {\n      const ::Verbosity verbosity = db::get<ah::Tags::Verbosity>(box);\n      if (verbosity >= ::Verbosity::Quiet) {\n        Parallel::printf(\"%s\\n\", os.str());\n      }\n    } else {\n      ERROR(os.str());\n    }\n  }\n};\n}  // namespace ah::callbacks\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/InvokeCallbacks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <deque>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/StrahlkorperTransformations.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah {\n\nnamespace detail {\n// Update the previous surface in the mutable global cache\ntemplate <typename Fr>\nstruct UpdatePreviousSurface {\n  static void apply(const gsl::not_null<Storage::LockedPreviousSurface<Fr>*>\n                        locked_previous_surface,\n                    Storage::PreviousSurface<Fr> previous_surface) {\n    auto& lock = locked_previous_surface->lock;\n    lock.write_lock();\n    locked_previous_surface->surface = std::move(previous_surface);\n    lock.write_unlock();\n  }\n};\n}  // namespace detail\n\n/*!\n * \\brief Invoke the callbacks specified in the `horizon_find_callbacks` alias\n * of the \\p HorizonMetavars.\n *\n * \\details Before invoking the callbacks, this function\n *\n * 1. Restricts the final interpolated variables from the $L_\\mathrm{mesh}$ used\n *    for the FastFlow algorithm, to the actual $L$ of the Strahlkorper.\n * 2. Adds the current Strahlkorper to the `ah::Tags::PreviousSurfaces`.\n * 3. Copies the Strahlkorper, its time derivative, and the \\p dependency into\n *    the box. Also possibly computes the Inertial coordinates of the final\n *    Strahlkorper and stores them in the box if the `frame` of the\n *    \\p HorizonMetavars isn't the Inertial frame.\n */\ntemplate <typename HorizonMetavars, typename DbTags, typename Metavariables>\nvoid invoke_callbacks(const gsl::not_null<db::DataBox<DbTags>*> box,\n                      Parallel::GlobalCache<Metavariables>& cache,\n                      const std::optional<std::string>& dependency,\n                      const FastFlow::Status status) {\n  using Fr = typename HorizonMetavars::frame;\n\n  const auto& current_time = db::get<ah::Tags::CurrentTime>(*box).value();\n  const auto& all_storage = db::get<ah::Tags::Storage<Fr>>(*box);\n  const auto& current_time_storage = all_storage.at(current_time);\n  const auto& current_iteration = current_time_storage.current_iteration;\n  const auto& current_strahlkorper = current_iteration.strahlkorper;\n  auto& previous_surfaces =\n      db::get_mutable_reference<ah::Tags::PreviousSurfaces<Fr>>(box);\n  const auto& fast_flow = db::get<ah::Tags::FastFlow>(*box);\n\n  // Update the previous surfaces. We do this before the callbacks in case any\n  // of the callbacks need the previous strahlkorpers with the current\n  // strahlkorper already in it.\n  previous_surfaces.emplace_front(current_time, current_strahlkorper,\n                                  current_iteration.intersecting_element_ids);\n\n  // Broadcast the previous surface to the mutable global cache so that elements\n  // know whether they need to send data for the next horizon find. We do this\n  // early so that elements get this information as soon as possible.\n  Parallel::mutate<Tags::PreviousSurface<HorizonMetavars>,\n                   detail::UpdatePreviousSurface<Fr>>(\n      cache, previous_surfaces.front());\n\n  // Remove old previous_strahlkorpers that are no longer relevant.\n  const size_t num_previous_strahlkorpers = 3;\n  while (previous_surfaces.size() > num_previous_strahlkorpers) {\n    previous_surfaces.pop_back();\n  }\n\n  // The interpolated variables have been interpolated from the volume to\n  // the points on the prolonged_strahlkorper, not to the points on the\n  // actual strahlkorper. So here we do a restriction of these quantities\n  // onto the actual strahlkorper.\n  const auto& current_ylm = current_strahlkorper.ylm_spherepack();\n  const size_t L_mesh = fast_flow.current_l_mesh(current_strahlkorper);\n  const auto prolonged_strahlkorper =\n      ylm::Strahlkorper<Fr>(L_mesh, L_mesh, current_strahlkorper);\n  const auto& prolonged_ylm = prolonged_strahlkorper.ylm_spherepack();\n  const auto& prolonged_interpolated_vars = current_iteration.interpolated_vars;\n  db::mutate<::Tags::Variables<ah::vars_to_interpolate_to_target<3, Fr>>>(\n      [&](const gsl::not_null<\n          ::Variables<ah::vars_to_interpolate_to_target<3, Fr>>*>\n              new_interpolated_vars) {\n        new_interpolated_vars->initialize(current_ylm.physical_size());\n        tmpl::for_each<ah::vars_to_interpolate_to_target<3, Fr>>(\n            [&]<typename Tag>(tmpl::type_<Tag>) {\n              const auto& prolonged_var = get<Tag>(prolonged_interpolated_vars);\n              auto& new_var = get<Tag>(*new_interpolated_vars);\n              for (size_t i = 0; i < prolonged_var.size(); i++) {\n                new_var[i] =\n                    current_ylm.spec_to_phys(prolonged_ylm.prolong_or_restrict(\n                        prolonged_ylm.phys_to_spec(prolonged_var[i]),\n                        current_ylm));\n              }\n            });\n      },\n      box);\n\n  db::mutate<ylm::Tags::Strahlkorper<Fr>, ylm::Tags::TimeDerivStrahlkorper<Fr>,\n             ah::Tags::Dependency>(\n      [&](const gsl::not_null<ylm::Strahlkorper<Fr>*> horizon,\n          const gsl::not_null<ylm::Strahlkorper<Fr>*> time_deriv_of_horizon,\n          const gsl::not_null<std::optional<std::string>*>\n              callback_dependency) {\n        *horizon = current_strahlkorper;\n\n        // This is a hack to use ylm::time_deriv_of_strahlkorper function below\n        std::deque<std::pair<double, ylm::Strahlkorper<Fr>>>\n            previous_horizons{};\n        for (const auto& previous_surface : previous_surfaces) {\n          previous_horizons.emplace_back(previous_surface.time.id,\n                                         previous_surface.surface);\n        }\n\n        *time_deriv_of_horizon = current_strahlkorper;\n        ylm::time_deriv_of_strahlkorper(time_deriv_of_horizon,\n                                        previous_horizons);\n\n        *callback_dependency = dependency;\n      },\n      box);\n\n  // Put inertial coords in the box if they aren't already there\n  if constexpr (not std::is_same_v<Fr, Frame::Inertial>) {\n    db::mutate<ylm::Tags::CartesianCoords<Frame::Inertial>>(\n        [&](const gsl::not_null<tnsr::I<DataVector, 3>*> inertial_coords) {\n          const auto& domain = Parallel::get<domain::Tags::Domain<3>>(cache);\n          const auto& functions_of_time =\n              Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n          strahlkorper_coords_in_different_frame(\n              inertial_coords, current_strahlkorper, domain, functions_of_time,\n              current_time.id);\n        },\n        box);\n  }\n\n  // Finally call callbacks\n  tmpl::for_each<typename HorizonMetavars::horizon_find_callbacks>(\n      [&]<typename Callback>(tmpl::type_<Callback>) {\n        Callback::apply(*box, cache, status);\n      });\n}\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveCenters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <limits>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace ah::callbacks {\n/*!\n * \\brief Writes the center of an apparent horizon to disk in both the\n * `Frame` template parameter frame and Frame::Inertial frame. Intended to be\n * used in the `horizon_find_callbacks` list of a\n * `ah::protocols::HorizonMetavars`.\n *\n * The centers will be written to a subfile with the name\n * `/ApparentHorizons/TargetName_Centers.dat` where `TargetName` is the\n * pretty_type::name of the \\p HorizonMetavars template parameter.\n *\n * The columns of the dat file are:\n * - %Time\n * - GridCenter_x\n * - GridCenter_y\n * - GridCenter_z\n * - InertialCenter_x\n * - InertialCenter_y\n * - InertialCenter_z\n *\n * The `Frame` template parameter must be either `::Frame::Grid` or\n * `::Frame::Distorted`. Even though the template parameter can be\n * `::Frame::Distorted`, we still write `GridCenter_?` because the centers of\n * the objects are the same in the Grid and Distorted frames.\n *\n * \\note Requires `ylm::Tags::Strahlkorper<Frame>` and\n * `ylm::Tags::CartesianCoords<Frame::Inertial>` and\n * `ylm::Tags::EuclideanAreaElement<Frame>` to be in the DataBox of the horizon\n * finder.\n */\ntemplate <typename HorizonMetavars>\nstruct ObserveCenters : tt::ConformsTo<ah::protocols::Callback> {\n  template <typename DbTags, typename Metavariables>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const FastFlow::Status /*status*/) {\n    using frame = typename HorizonMetavars::frame;\n    static_assert(std::is_same_v<frame, ::Frame::Grid> or\n                      std::is_same_v<frame, ::Frame::Distorted>,\n                  \"Frame must be either Grid or Distorted.\");\n    using HorizonTag = ylm::Tags::Strahlkorper<frame>;\n    using CoordsTag = ylm::Tags::CartesianCoords<::Frame::Inertial>;\n    using EuclideanAreaElementTag = ylm::Tags::EuclideanAreaElement<frame>;\n    static_assert(db::tag_is_retrievable_v<HorizonTag, db::DataBox<DbTags>>,\n                  \"DataBox must contain ylm::Tags::Strahlkorper<Frame>\");\n    static_assert(db::tag_is_retrievable_v<CoordsTag, db::DataBox<DbTags>>,\n                  \"DataBox must contain \"\n                  \"ylm::Tags::CartesianCoords<Frame::Inertial>\");\n    static_assert(\n        db::tag_is_retrievable_v<EuclideanAreaElementTag, db::DataBox<DbTags>>,\n        \"DataBox must contain ylm::Tags::EuclideanAreaElement<Frame>\");\n\n    // Only print the centers if we want to.\n    if (not Parallel::get<ah::Tags::ObserveCenters>(cache)) {\n      return;\n    }\n\n    const auto& grid_horizon = db::get<HorizonTag>(box);\n    const std::array<double, 3> grid_center = grid_horizon.physical_center();\n\n    // computes the inertial center to be the average value of the\n    // inertial coordinates over the Strahlkorper, where the average is\n    // computed by a surface integral.\n    // Note that we use the Euclidean area element here, since we are trying\n    // to find a geometric center of a surface without regard to GR.\n    const auto& inertial_coords = db::get<CoordsTag>(box);\n    const auto& area_element = db::get<EuclideanAreaElementTag>(box);\n    std::array<double, 3> inertial_center =\n        make_array<3>(std::numeric_limits<double>::signaling_NaN());\n    auto integrand = make_with_value<Scalar<DataVector>>(get(area_element), 0.);\n    const double denominator = grid_horizon.ylm_spherepack().definite_integral(\n        get(area_element).data());\n    for (size_t i = 0; i < 3; ++i) {\n      get(integrand) = get(area_element) * inertial_coords.get(i);\n      gsl::at(inertial_center, i) =\n          grid_horizon.ylm_spherepack().definite_integral(\n              get(integrand).data()) /\n          denominator;\n    }\n\n    // time, grid_x, grid_y, grid_z, inertial_x, inertial_y, inertial_z\n    const auto& current_time = db::get<ah::Tags::CurrentTime>(box).value();\n    const auto center_tuple = std::make_tuple(\n        current_time.id, grid_center[0], grid_center[1], grid_center[2],\n        inertial_center[0], inertial_center[1], inertial_center[2]);\n\n    auto& observer_writer_proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    const std::string subfile_path{\"/ApparentHorizons/\" +\n                                   HorizonMetavars::name() + \"_Centers\"};\n\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        // Node 0 is always the writer\n        observer_writer_proxy[0], subfile_path, legend_, center_tuple);\n  }\n\n private:\n  const static inline std::vector<std::string> legend_{\n      {\"Time\", \"GridCenter_x\", \"GridCenter_y\", \"GridCenter_z\",\n       \"InertialCenter_x\", \"InertialCenter_y\", \"InertialCenter_z\"}};\n};\n}  // namespace ah::callbacks\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveFieldsOnHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Tests for this file are combined with the ones for\n// ObserveTimeSeriesOnHorizon.hpp in\n// Test_ObserveFieldsAndTimeSeriesOnHorizon.cpp since they are so similar.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah ::callbacks {\n/*!\n * \\brief A `ah::protocols::Callback` that outputs 2D \"volume\" data on a\n * surface and the surface's spherical harmonic data.\n *\n * \\details The columns of spherical harmonic data written take the form\n *\n * \\code\n * [Time, {Frame}ExpansionCenter_x, {Frame}ExpansionCenter_y,\n * {Frame}ExpansionCenter_z, Lmax, coef(0,0), ... coef(Lmax,Lmax)]\n * \\endcode\n *\n * where `coef(l,m)` refers to the strahlkorper coefficients stored and defined\n * by `ylm::Strahlkorper::coefficients() const`, and `Frame` is\n * `HorizonMetavars::frame`. It is assumed that $l_{\\mathrm{max}} =\n * m_{\\mathrm{max}}$.\n *\n * \\note Currently, $l_{\\mathrm{max}}$ for a given surface does not change over\n * the course of the simulation, which means that the total number of columns of\n * coefficients that we need to write is also constant. The current\n * implementation of writing the coefficients at one time assumes\n * $l_{\\mathrm{max}}$ of a surface remains constant. If and when in the future\n * functionality for an adaptive $l_{\\mathrm{max}}$ is added, the implementation\n * for writing the coefficients will need to be updated to account for this. One\n * possible way to address this is to have a known maximum $l_{\\mathrm{max}}$\n * for a given surface and write all coefficients up to that maximum\n * $l_{\\mathrm{max}}$.\n */\ntemplate <typename TagsToObserve, typename HorizonMetavars>\nstruct ObserveFieldsOnHorizon : tt::ConformsTo<ah::protocols::Callback> {\n  using Fr = typename HorizonMetavars::frame;\n\n  using const_global_cache_tags = tmpl::list<observers::Tags::SurfaceFileName>;\n\n  template <typename DbTags, typename Metavariables>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const FastFlow::Status /*status*/) {\n    const auto& current_time = db::get<ah::Tags::CurrentTime>(box).value();\n    const auto& strahlkorper = get<ylm::Tags::Strahlkorper<Fr>>(box);\n    const ylm::Spherepack& ylm = strahlkorper.ylm_spherepack();\n\n    // Output the inertial-frame coordinates of the Strahlkorper.\n    // Note that these coordinates are not Spherepack-evenly-distributed over\n    // the inertial-frame sphere (they are Spherepack-evenly-distributed over\n    // the frame sphere).\n    std::vector<TensorComponent> tensor_components;\n    const auto& inertial_strahlkorper_coords =\n        get<ylm::Tags::CartesianCoords<::Frame::Inertial>>(box);\n    tensor_components.push_back(\n        {\"InertialCoordinates_x\"s, get<0>(inertial_strahlkorper_coords)});\n    tensor_components.push_back(\n        {\"InertialCoordinates_y\"s, get<1>(inertial_strahlkorper_coords)});\n    tensor_components.push_back(\n        {\"InertialCoordinates_z\"s, get<2>(inertial_strahlkorper_coords)});\n\n    tmpl::for_each<TagsToObserve>([&box, &tensor_components](auto tag_v) {\n      using Tag = tmpl::type_from<decltype(tag_v)>;\n      const auto tag_name = db::tag_name<Tag>();\n      const auto& tensor = get<Tag>(box);\n      for (size_t i = 0; i < tensor.size(); ++i) {\n        tensor_components.emplace_back(tag_name + tensor.component_suffix(i),\n                                       tensor[i]);\n      }\n    });\n\n    const std::string& surface_name = pretty_type::name<HorizonMetavars>();\n    const std::string subfile_path{std::string{\"/\"} + surface_name};\n    const std::vector<size_t> extents_vector{\n        {ylm.physical_extents()[0], ylm.physical_extents()[1]}};\n    const std::vector<Spectral::Basis> bases_vector{\n        2, Spectral::Basis::SphericalHarmonic};\n    const std::vector<Spectral::Quadrature> quadratures_vector{\n        {Spectral::Quadrature::Gauss, Spectral::Quadrature::Equiangular}};\n    const observers::ObservationId observation_id{current_time.id,\n                                                  subfile_path + \".vol\"};\n\n    auto& proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    // We call this on proxy[0] because the 0th element of a NodeGroup is\n    // always guaranteed to be present.\n    Parallel::threaded_action<observers::ThreadedActions::WriteVolumeData>(\n        proxy[0], Parallel::get<observers::Tags::SurfaceFileName>(cache),\n        subfile_path, observation_id,\n        std::vector<ElementVolumeData>{{surface_name, tensor_components,\n                                        extents_vector, bases_vector,\n                                        quadratures_vector}});\n\n    std::vector<std::string> ylm_legend{};\n    std::vector<double> ylm_data{};\n\n    // When option LMax is present in the global cache,\n    // check that the Strahlkorper resolution does not exceed it.\n    // LMax sets a maximum resolution for\n    // the Strahlkorper resolution (when adjusted using adaptive\n    // horizon finding) and the number of (zero padded) columns to output,\n    // to avoid H5 errors caused by different rows (corresponding to\n    // different times) having different numbers of columns.\n    // If this option is not present in the cache, skip the check and just write\n    // the modes of the Strahlkorper, assuming that the Strahlkorper\n    // resolution never changes.\n    size_t max_l_to_write = strahlkorper.l_max();\n    if constexpr (Parallel::is_in_global_cache<Metavariables, ah::Tags::LMax>) {\n      const auto& max_resolution_and_output_l =\n          Parallel::get<ah::Tags::LMax>(cache);\n      if (UNLIKELY(max_resolution_and_output_l < strahlkorper.l_max())) {\n        ERROR(\"The option LMax (\"\n              << max_resolution_and_output_l << \") is smaller than the current \"\n              << \"Strahlkorper resolution L=\" << strahlkorper.l_max()\n              << \". Increase LMax or decrease \"\n              << \"Strahlkorper resolution L to avoid truncating data.\");\n      }\n      max_l_to_write = max_resolution_and_output_l;\n    }\n\n    ylm::fill_ylm_legend_and_data(make_not_null(&ylm_legend),\n                                  make_not_null(&ylm_data), strahlkorper,\n                                  current_time.id, max_l_to_write);\n\n    const std::string ylm_subfile_name{std::string{\"/\"} + surface_name +\n                                       \"_Ylm\"};\n\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        proxy[0], ylm_subfile_name, std::move(ylm_legend),\n        std::make_tuple(std::move(ylm_data)));\n  }\n};\n}  // namespace ah::callbacks\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveTimeSeriesOnHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Tests for this file are combined with the ones for\n// ObserveFieldsOnHorizon.hpp in\n// Test_ObserveFieldsAndTimeSeriesOnHorizon.cpp since they are so similar.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::callbacks {\nnamespace detail {\ntemplate <typename T>\nstruct is_array_of_double : std::false_type {};\n\ntemplate <std::size_t N>\nstruct is_array_of_double<std::array<double, N>> : std::true_type {};\n\ntemplate <typename... Ts>\nauto make_legend(tmpl::list<Ts...> /* meta */) {\n  std::vector<std::string> legend = {\"Time\"};\n\n  [[maybe_unused]] auto append_tags = [&legend](auto tag) {\n    using TagType = decltype(tag);\n    using ReturnType = typename TagType::type;\n\n    if constexpr (is_array_of_double<ReturnType>::value) {\n      constexpr std::array<const char*, 3> suffix = {\"_x\", \"_y\", \"_z\"};\n      for (size_t i = 0; i < std::tuple_size<ReturnType>::value; ++i) {\n        legend.push_back(db::tag_name<TagType>() + gsl::at(suffix, i));\n      }\n    } else {\n      legend.push_back(db::tag_name<TagType>());\n    }\n  };\n\n  (append_tags(Ts{}), ...);\n\n  return legend;\n}\n\ntemplate <typename DbTags, typename... Ts>\nauto make_reduction_data(const db::DataBox<DbTags>& box, double time,\n                         tmpl::list<Ts...> /* meta */) {\n  return std::make_tuple(time, get<Ts>(box)...);\n}\n\n}  // namespace detail\n\n/*!\n * \\brief A `ah::protocol::Callback` that outputs a `double` or\n * `std::array<double, N>` quantity on a surface as a time series in the\n * reductions file.\n */\ntemplate <typename TagsToObserve, typename HorizonMetavars>\nstruct ObserveTimeSeriesOnHorizon : tt::ConformsTo<ah::protocols::Callback> {\n  template <typename DbTags, typename Metavariables>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const FastFlow::Status /*status*/) {\n    const auto& time = db::get<ah::Tags::CurrentTime>(box).value();\n    auto& proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    // We call this on proxy[0] because the 0th element of a NodeGroup is\n    // always guaranteed to be present.\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        proxy[0], std::string{\"/\" + pretty_type::name<HorizonMetavars>()},\n        detail::make_legend(TagsToObserve{}),\n        detail::make_reduction_data(box, time.id, TagsToObserve{}));\n  }\n};\n}  // namespace ah::callbacks\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/SendDependencyToObserverWriter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\n/// \\cond\nnamespace logging::Tags {\ntemplate <typename OptionsGroup>\nstruct Verbosity;\n}  // namespace logging::Tags\n/// \\endcond\n\nnamespace ah::callbacks {\n/*!\n * \\brief A `ah::protocols::Callback` that will send a message to the\n * ObserverWriter if we have a dependency using the\n * `observers::ThreadedActions::ContributeDependency` action.\n *\n * \\details If we have a dependency, the template bool \\p WriteVolumeData\n * determines if the message sent to the ObserverWriter says to write the volume\n * data to disk or not.\n */\ntemplate <typename HorizonMetavars, bool WriteVolumeData>\nstruct SendDependencyToObserverWriter\n    : tt::ConformsTo<ah::protocols::Callback> {\n  // Callback for when horizon find succeeds\n  template <typename DbTags, typename Metavariables>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const FastFlow::Status /*status*/) {\n    const auto& time = db::get<ah::Tags::CurrentTime>(box).value();\n    const auto& dependency = db::get<ah::Tags::Dependency>(box);\n\n    if (dependency.has_value()) {\n      const auto& verbosity = db::get<ah::Tags::Verbosity>(box);\n\n      auto& observer_writer_proxy = Parallel::get_parallel_component<\n          observers::ObserverWriter<Metavariables>>(cache);\n\n      Parallel::threaded_action<\n          observers::ThreadedActions::ContributeDependency>(\n          observer_writer_proxy, time.id, pretty_type::name<HorizonMetavars>(),\n          dependency.value(), WriteVolumeData);\n\n      if (verbosity >= ::Verbosity::Verbose) {\n        Parallel::printf(\n            \"Remark: We are%s writing volume data for horizon finder %s\\n\",\n            WriteVolumeData ? \"\" : \" not\",\n            pretty_type::name<HorizonMetavars>());\n      }\n    }\n  }\n};\n}  // namespace ah::callbacks\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/CleanUp.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/CleanUp.hpp\"\n\n#include <optional>\n#include <set>\n#include <unordered_map>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ah {\ntemplate <typename Fr>\nvoid clean_up_horizon_finder(\n    const gsl::not_null<std::optional<LinkedMessageId<double>>*>\n        current_time_optional,\n    const gsl::not_null<std::unordered_map<LinkedMessageId<double>,\n                                           ah::Storage::SingleTimeStorage<Fr>>*>\n        all_storage,\n    const gsl::not_null<std::set<LinkedMessageId<double>>*> completed_times,\n    const gsl::not_null<FastFlow*> fast_flow) {\n  ASSERT(current_time_optional->has_value(),\n         \"Current time must be set in order to clean up the horizon finder.\");\n\n  const auto& current_time = current_time_optional->value();\n\n  all_storage->erase(current_time);\n\n  // We want to keep track of all completed times to deal with the\n  // possibility of late arrivals of volume data. We could keep all\n  // completed times forever, but we probably don't want it to get too\n  // large, so we limit its size.  We assume that asynchronous calls to\n  // this action do not span more than 1000 temporal_ids.\n  completed_times->insert(current_time);\n  while (completed_times->size() > 1000) {\n    completed_times->erase(completed_times->begin());\n  }\n\n  // Reset time before we choose a new time\n  current_time_optional->reset();\n\n  fast_flow->reset_for_next_find();\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void clean_up_horizon_finder(                                       \\\n      const gsl::not_null<std::optional<LinkedMessageId<double>>*>             \\\n          current_time_optional,                                               \\\n      const gsl::not_null<                                                     \\\n          std::unordered_map<LinkedMessageId<double>,                          \\\n                             ah::Storage::SingleTimeStorage<FRAME(data)>>*>    \\\n          all_storage,                                                         \\\n      const gsl::not_null<std::set<LinkedMessageId<double>>*> completed_times, \\\n      const gsl::not_null<FastFlow*> fast_flow);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Inertial, Frame::Distorted, Frame::Grid))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/CleanUp.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <set>\n#include <unordered_map>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ah {\n/*!\n * \\brief Cleans up the horizon finder after a horizon find has finished\n *\n * \\details Removes the current time from the storage map, adds the current time\n * to the completed times, and then resets the current time. If the completed\n * times have more than 1000 entries, this will limit the size to 1000.\n */\ntemplate <typename Fr>\nvoid clean_up_horizon_finder(\n    gsl::not_null<std::optional<LinkedMessageId<double>>*>\n        current_time_optional,\n    gsl::not_null<std::unordered_map<LinkedMessageId<double>,\n                                     ah::Storage::SingleTimeStorage<Fr>>*>\n        all_storage,\n    gsl::not_null<std::set<LinkedMessageId<double>>*> completed_times,\n    gsl::not_null<FastFlow*> fast_flow);\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Component.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <tuple>\n\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Initialization.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah {\n/*!\n * \\brief The singleton parallel component responsible for finding horizons.\n */\ntemplate <class Metavariables, typename HorizonMetavars>\nstruct Component {\n  static_assert(tt::assert_conforms_to_v<HorizonMetavars,\n                                         ah::protocols::HorizonMetavars>);\n  using horizon_metavars = HorizonMetavars;\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n\n  static std::string name() { return HorizonMetavars::name(); }\n\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<Initialization::Actions::InitializeItems<\n                     ::ah::Initialize<HorizonMetavars>>,\n                 Parallel::Actions::TerminatePhase>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<Component>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/ComputeCoords.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeCoords.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <deque>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ah {\ntemplate <typename Fr>\nbool set_current_iteration_coords(\n    const gsl::not_null<ah::Storage::Iteration<Fr>*> current_iteration,\n    const gsl::not_null<std::vector<size_t>*> block_order,\n    const LinkedMessageId<double>& time, const FastFlow& fast_flow,\n    const ylm::Strahlkorper<Fr>& initial_guess,\n    const ylm::Strahlkorper<Fr>& previous_iteration_surface,\n    const std::deque<ah::Storage::PreviousSurface<Fr>>& previous_surfaces,\n    const size_t max_compute_coords_retries, const Domain<3>& domain,\n    const domain::FunctionsOfTimeMap& functions_of_time,\n    const std::optional<size_t>& current_resolution_l,\n    const bool rerunning_with_higher_resolution) {\n  // If we are rerunning the horizon find with higher resolution, use\n  // the previous horizon as the initial guess.\n  if (fast_flow.current_iteration() == 0) {\n    if (rerunning_with_higher_resolution) {\n      if (not current_resolution_l.has_value()) {\n        ERROR(\n            \"Current resolution L is not set, even though this iteration is \"\n            \"marked as a rerun with higher resolution. This is an internal \"\n            \"error. Please file an issue.\");\n      }\n      if (previous_iteration_surface.l_max() >= *current_resolution_l) {\n        ERROR(\"Previous iteration surface has resolution \"\n              << previous_iteration_surface.l_max()\n              << \" but current resolution \" << *current_resolution_l\n              << \" is not higher, even though this iteration is marked as a \"\n                 \"rerun with higher resolution. This is an internal error. \"\n                 \"Please file an issue.\");\n      }\n      // Just set the initial guess by prolonging or restricting the previous\n      // iteration surface, i.e., the horizon already found at a lower\n      // resolution\n      current_iteration->strahlkorper =\n          ylm::Strahlkorper<Fr>(*current_resolution_l, *current_resolution_l,\n                                previous_iteration_surface);\n    } else {\n      // Need to set the first surface. If this is the very first, set it to the\n      // initial guess. If not, use the previous horizon surface. The surface\n      // will potentially be extrapolated below.\n      //\n      // If current_resolution_l is set, make sure the initial guess has the\n      // correct resolution. Only prolong or restrict if current_resolution_l\n      // is different from the initial guess / previous surface l_max().\n      // Otherwise, just set the initial guess.\n      if (current_resolution_l.has_value()) {\n        if (UNLIKELY(previous_surfaces.empty())) {\n          if (UNLIKELY(current_resolution_l != initial_guess.l_max())) {\n            current_iteration->strahlkorper = ylm::Strahlkorper<Fr>(\n                *current_resolution_l, *current_resolution_l, initial_guess);\n          } else {\n            current_iteration->strahlkorper = initial_guess;\n          }\n        } else {\n          if (UNLIKELY(current_resolution_l !=\n                       previous_surfaces.front().surface.l_max())) {\n            current_iteration->strahlkorper = ylm::Strahlkorper<Fr>(\n                *current_resolution_l, *current_resolution_l,\n                previous_surfaces.front().surface);\n          } else {\n            current_iteration->strahlkorper = previous_surfaces.front().surface;\n          }\n        }\n      } else {\n        current_iteration->strahlkorper =\n            UNLIKELY(previous_surfaces.empty())\n                ? initial_guess\n                : previous_surfaces.front().surface;\n      }\n\n      // If we have zero previous_surfaces, then the initial guess is already\n      // in strahlkorper, so do nothing.\n      //\n      // If we have one previous_surface, then we have had a successful\n      // horizon find, and the initial guess for the next horizon find is\n      // already in strahlkorper, so again we do nothing.\n      //\n      // If we have 2 valid previous_surfaces, then we set the initial guess\n      // by linear extrapolation in time using the last 2 previous_surfaces.\n      //\n      // If we have 3 valid previous_surfaces, then we set the initial guess\n      // by quadratic extrapolation in time using the last 3\n      // previous_surfaces.\n      //\n      // For extrapolation, we assume that the expansion center of all the\n      // Strahlkorpers are equal. If any surface has a different resolution,\n      // prolong to the max resolution, extrapolate, then restrict to either\n      // current_resolution.\n      if (LIKELY(previous_surfaces.size() > 2)) {\n        constexpr size_t number_of_previous_surfaces = 3;\n        // Quadratic extrapolation\n        const double dt_0 = previous_surfaces[0].time.id - time.id;\n        const double dt_1 = previous_surfaces[1].time.id - time.id;\n        const double dt_2 = previous_surfaces[2].time.id - time.id;\n        const double fac_0 = dt_1 * dt_2 / ((dt_1 - dt_0) * (dt_2 - dt_0));\n        const double fac_1 = dt_0 * dt_2 / ((dt_2 - dt_1) * (dt_0 - dt_1));\n        const double fac_2 = 1.0 - fac_0 - fac_1;\n\n        // Determine if resolutions are equal and, if not, which is greatest\n        const auto [min_res_it, max_res_it] = std::minmax_element(\n            previous_surfaces.begin(),\n            previous_surfaces.begin() + number_of_previous_surfaces,\n            [](const auto& a, const auto& b) {\n              return a.surface.l_max() < b.surface.l_max();\n            });\n        const size_t min_resolution_l = min_res_it->surface.l_max();\n        const size_t max_resolution_l = max_res_it->surface.l_max();\n        const auto which_surface_is_max = static_cast<size_t>(\n            std::distance(previous_surfaces.begin(), max_res_it));\n\n        if (UNLIKELY(min_resolution_l != max_resolution_l)) {\n          const std::array facs{fac_0, fac_1, fac_2};\n          DataVector new_coefs =\n              gsl::at(facs, which_surface_is_max) *\n              gsl::at(previous_surfaces, which_surface_is_max)\n                  .surface.coefficients();\n          for (size_t i = 0; i < number_of_previous_surfaces; ++i) {\n            if (i == which_surface_is_max) {\n              continue;\n            }\n            new_coefs +=\n                gsl::at(facs, i) *\n                gsl::at(previous_surfaces, i)\n                    .surface.ylm_spherepack()\n                    .prolong_or_restrict(\n                        gsl::at(previous_surfaces, i).surface.coefficients(),\n                        gsl::at(previous_surfaces, which_surface_is_max)\n                            .surface.ylm_spherepack());\n          }\n          current_iteration->strahlkorper.coefficients() =\n              gsl::at(previous_surfaces, which_surface_is_max)\n                  .surface.ylm_spherepack()\n                  .prolong_or_restrict(\n                      new_coefs,\n                      current_iteration->strahlkorper.ylm_spherepack());\n        } else {\n          if (current_iteration->strahlkorper.l_max() !=\n              previous_surfaces[0].surface.l_max()) {\n            current_iteration->strahlkorper.coefficients() =\n                previous_surfaces[0]\n                    .surface.ylm_spherepack()\n                    .prolong_or_restrict(\n                        fac_0 * previous_surfaces[0].surface.coefficients() +\n                            fac_1 *\n                                previous_surfaces[1].surface.coefficients() +\n                            fac_2 * previous_surfaces[2].surface.coefficients(),\n                        current_iteration->strahlkorper.ylm_spherepack());\n          } else {\n            current_iteration->strahlkorper.coefficients() =\n                fac_0 * previous_surfaces[0].surface.coefficients() +\n                fac_1 * previous_surfaces[1].surface.coefficients() +\n                fac_2 * previous_surfaces[2].surface.coefficients();\n          }\n        }\n      } else if (previous_surfaces.size() > 1) {\n        // Linear extrapolation\n        const double dt_0 = previous_surfaces[0].time.id - time.id;\n        const double dt_1 = previous_surfaces[1].time.id - time.id;\n        const double fac_0 = dt_1 / (dt_1 - dt_0);\n        const double fac_1 = 1.0 - fac_0;\n\n        const size_t l_0 = previous_surfaces[0].surface.l_max();\n        const size_t l_1 = previous_surfaces[1].surface.l_max();\n\n        if (l_0 == l_1) {\n          if (l_0 == current_iteration->strahlkorper.l_max()) {\n            current_iteration->strahlkorper.coefficients() =\n                fac_0 * previous_surfaces[0].surface.coefficients() +\n                fac_1 * previous_surfaces[1].surface.coefficients();\n          } else {\n            current_iteration->strahlkorper.coefficients() =\n                previous_surfaces[0]\n                    .surface.ylm_spherepack()\n                    .prolong_or_restrict(\n                        fac_0 * previous_surfaces[0].surface.coefficients() +\n                            fac_1 * previous_surfaces[1].surface.coefficients(),\n                        current_iteration->strahlkorper.ylm_spherepack());\n          }\n        } else {\n          const size_t which_surface_is_max = l_0 > l_1 ? 0 : 1;\n          const size_t which_surface_is_min = 1 - which_surface_is_max;\n          const std::array facs{fac_0, fac_1};\n          DataVector new_coefs =\n              gsl::at(facs, which_surface_is_max) *\n              gsl::at(previous_surfaces, which_surface_is_max)\n                  .surface.coefficients();\n          new_coefs += gsl::at(facs, which_surface_is_min) *\n                       gsl::at(previous_surfaces, which_surface_is_min)\n                           .surface.ylm_spherepack()\n                           .prolong_or_restrict(\n                               gsl::at(previous_surfaces, which_surface_is_min)\n                                   .surface.coefficients(),\n                               gsl::at(previous_surfaces, which_surface_is_max)\n                                   .surface.ylm_spherepack());\n          current_iteration->strahlkorper.coefficients() =\n              gsl::at(previous_surfaces, which_surface_is_max)\n                  .surface.ylm_spherepack()\n                  .prolong_or_restrict(\n                      new_coefs,\n                      current_iteration->strahlkorper.ylm_spherepack());\n        }\n      }\n    }\n  }  // if fastflow.current_iteration() == 0\n\n  const auto set_coords = [&]() {\n    const auto& strahlkorper = current_iteration->strahlkorper;\n\n    const size_t l_mesh = fast_flow.current_l_mesh(strahlkorper);\n\n    const auto prolonged_strahlkorper =\n        ylm::Strahlkorper<Fr>(l_mesh, l_mesh, strahlkorper);\n\n    // Frames are handled within block_logical_coordinates\n    current_iteration->block_coord_holders = ::block_logical_coordinates(\n        domain, ylm::cartesian_coords(prolonged_strahlkorper), time.id,\n        functions_of_time, block_order);\n  };\n\n  // Set the coordinates\n  set_coords();\n\n  // Check if any points were outside the domain\n  current_iteration->compute_coords_retries = 0;\n  while (alg::any_of(\n      current_iteration->block_coord_holders.value(),\n      [](const auto& coord_holder) { return not coord_holder.has_value(); })) {\n    // If so, try recomputation up to max_compute_coords_retries times\n    ++current_iteration->compute_coords_retries;\n\n    if (current_iteration->compute_coords_retries <=\n        max_compute_coords_retries) {\n      if (fast_flow.current_iteration() == 0) {\n        // If this is the zeroth iteration and we couldn't compute the coords,\n        // then just try increasing the size of the horizon by 50%\n        current_iteration->strahlkorper.coefficients()[0] *= 1.5;\n      } else {\n        // Otherwise move the new trial surface halfway between the current\n        // surface and the previous surface\n        current_iteration->strahlkorper.coefficients() +=\n            0.5 * (previous_iteration_surface.coefficients() -\n                   current_iteration->strahlkorper.coefficients());\n      }\n\n      // Set the coords using the new guess\n      set_coords();\n    } else {\n      // We didn't actually try computation here so we added one extra to the\n      // counter which we need to remove\n      --current_iteration->compute_coords_retries;\n      return false;\n    }\n  }\n\n  return true;\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                            \\\n  template bool set_current_iteration_coords(                           \\\n      const gsl::not_null<ah::Storage::Iteration<FRAME(data)>*>         \\\n          current_iteration,                                            \\\n      const gsl::not_null<std::vector<size_t>*> block_order,            \\\n      const LinkedMessageId<double>& time, const FastFlow& fast_flow,   \\\n      const ylm::Strahlkorper<FRAME(data)>& initial_guess,              \\\n      const ylm::Strahlkorper<FRAME(data)>& previous_iteration_surface, \\\n      const std::deque<ah::Storage::PreviousSurface<FRAME(data)>>&      \\\n          previous_surfaces,                                            \\\n      const size_t max_compute_coords_retries, const Domain<3>& domain, \\\n      const domain::FunctionsOfTimeMap& functions_of_time,              \\\n      const std::optional<size_t>& current_resolution_l,                \\\n      const bool rerunning_with_higher_resolution);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Inertial, Frame::Distorted, Frame::Grid))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/ComputeCoords.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <deque>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t VolumeDim>\nclass Domain;\nclass FastFlow;\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\n\nnamespace ah {\n/*!\n * \\brief Compute the target points for the current iteration.\n *\n * \\details Returns whether the computation of the target points was successful\n * or if there are some points outside the domain. If computation fails, one of\n * two attempts to recover will happen:\n *\n * - For the zeroth fast flow iteration, this will increase the $l=0,m=0$\n *   coefficient (i.e. the size) by 50%.\n * - For all other iterations, the coefficients become\n * \\begin{equation}\n * S^{\\mathrm{new}}_{lm} = \\frac{1}{2}\\left(S^{\\mathrm{previous}}_{lm} +\n * S^{\\mathrm{failed}}_{lm}\\right)\n * \\end{equation}\n *   where $S^{\\mathrm{failed}}_{lm}$ are the coefficients of the failed\n *   computation and $S^{\\mathrm{previous}}_{lm}$ are the coefficients from\n *   the previous successful iteration.\n *\n * This function will try recomputing the coords using the above two rules\n * \\p max_compute_coords_retries times before returning false.\n *\n * \\param current_iteration The returned pointer to the current Iteration object\n * \\param block_order Priority order to search blocks for containing points\n * (see `::block_logical_coordinates` for details)\n * \\param time The current time\n * \\param fast_flow The FastFlow object for the current horizon find\n * \\param initial_guess If the current iteration number is zero and\n * rerunning_with_higher_resolution == false, current_iteration is set to\n * this Strahlkorper\n * \\param previous_iteration_surface If empty, current_iteration is set to\n * initial_guess; if one previous iteration, current_iteration is set to\n * that previous iteration; if two previous iterations, current_iteration\n * is set by linearly extrapolating in time the two pervious iterations;\n * if three or more previous iterations, current_iteration is set by\n * quadratic extrpolation of the three most recent iterations\n * \\param previous_surfaces Previously successful iteratios used to attempt to\n * recover when some points are outside the domain.\n * \\param max_compute_coords_retries Retry up to this many times before\n * returning false\n * \\param domain The spatial domain in which the horizon is being found\n * \\param functions_of_time The functions of time for the current domain\n * \\param current_resolution_l Optional; if specified, current_iteration is\n * prolonged or restricted to this resolution\n * \\param rerunning_with_higher_resolution Must be false unless\n * current_resolution_l is set; if true, then on iteration zero, set\n * the initial guess to the previous iteration surface\n * \\returns Whether or not set_current_iteration_coords succeeded\n */\ntemplate <typename Fr>\nbool set_current_iteration_coords(\n    gsl::not_null<ah::Storage::Iteration<Fr>*> current_iteration,\n    gsl::not_null<std::vector<size_t>*> block_order,\n    const LinkedMessageId<double>& time, const FastFlow& fast_flow,\n    const ylm::Strahlkorper<Fr>& initial_guess,\n    const ylm::Strahlkorper<Fr>& previous_iteration_surface,\n    const std::deque<ah::Storage::PreviousSurface<Fr>>& previous_surfaces,\n    size_t max_compute_coords_retries, const Domain<3>& domain,\n    const domain::FunctionsOfTimeMap& functions_of_time,\n    const std::optional<size_t>& current_resolution_l = std::nullopt,\n    bool rerunning_with_higher_resolution = false);\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/ComputeVarsToInterpolateToTarget.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeVarsToInterpolateToTarget.hpp\"\n\n#include <type_traits>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/FrameTransform.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah {\nnamespace {\n// The jacobians can be omitted if the frame is truly the inertial one since\n// they won't be needed\ntemplate <bool ForceInertialFrame = false, typename Fr>\nvoid compute_vars_to_interpolate_to_target_impl(\n    const gsl::not_null<Variables<ah::vars_to_interpolate_to_target<3, Fr>>*>\n        target_vars,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::aa<DataVector, 3>& pi, const tnsr::iaa<DataVector, 3>& phi,\n    const tnsr::ijaa<DataVector, 3>& deriv_phi, const Mesh<3>& mesh,\n    const Jacobian<DataVector, 3, Fr, Frame::Inertial>& jac_frame_to_inertial =\n        {},\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical, Fr>&\n        invjac_logical_to_frame = {}) {\n  // Handle the time-independent case where the frame could be anything, but we\n  // only need the inertial computation\n  using FrameToUse =\n      tmpl::conditional_t<ForceInertialFrame, ::Frame::Inertial, Fr>;\n  Variables<ah::vars_to_interpolate_to_target<3, FrameToUse>>\n      vars_to_interpolate_to_target;\n  vars_to_interpolate_to_target.set_data_ref(target_vars->data(),\n                                             target_vars->size());\n\n  // Inertial tags\n  using inertial_metric_tag = gr::Tags::SpatialMetric<DataVector, 3>;\n  using inertial_inv_metric_tag = gr::Tags::InverseSpatialMetric<DataVector, 3>;\n  using inertial_shift_tag = ::Tags::TempI<1, 3, Frame::Inertial, DataVector>;\n  using inertial_ex_curvature_tag = gr::Tags::ExtrinsicCurvature<DataVector, 3>;\n  using inertial_spatial_ricci_tag = gr::Tags::SpatialRicci<DataVector, 3>;\n  using inertial_spacetime_normal_vector_tag =\n      ::Tags::TempA<2, 3, Frame::Inertial, DataVector>;\n  using inertial_spatial_christoffel_tag =\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>;\n  // Frame tags\n  using metric_tag = gr::Tags::SpatialMetric<DataVector, 3, FrameToUse>;\n  using inv_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, 3, FrameToUse>;\n  using lapse_tag = gr::Tags::Lapse<DataVector>;\n  using phi_tag = ::Tags::Tempijj<7, 3, FrameToUse, DataVector>;\n  using ex_curvature_tag =\n      gr::Tags::ExtrinsicCurvature<DataVector, 3, FrameToUse>;\n  using spatial_christoffel_tag =\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, FrameToUse>;\n  using spatial_ricci_tag = gr::Tags::SpatialRicci<DataVector, 3, FrameToUse>;\n\n  TempBuffer<tmpl::list<\n      inertial_metric_tag, inertial_inv_metric_tag, lapse_tag,\n      inertial_shift_tag, inertial_spacetime_normal_vector_tag,\n      inertial_ex_curvature_tag, inertial_spatial_ricci_tag, phi_tag>>\n      buffer{get<0, 0>(spacetime_metric).size()};\n\n  auto& lapse = get<lapse_tag>(buffer);\n  auto& inertial_shift = get<inertial_shift_tag>(buffer);\n  auto& inertial_spacetime_normal_vector =\n      get<inertial_spacetime_normal_vector_tag>(buffer);\n\n  // As an optimization, get these from the vars to interpolate if they are in\n  // the inertial frame so we don't have to do copies below. Otherwise just\n  // grab them from the temporary buffer.\n  auto& inertial_metric = *(get<inertial_metric_tag>(\n      make_not_null(&vars_to_interpolate_to_target), make_not_null(&buffer)));\n  auto& inertial_inv_metric = *(get<inertial_inv_metric_tag>(\n      make_not_null(&vars_to_interpolate_to_target), make_not_null(&buffer)));\n  auto& inertial_ex_curvature = *(get<inertial_ex_curvature_tag>(\n      make_not_null(&vars_to_interpolate_to_target), make_not_null(&buffer)));\n  auto& inertial_spatial_ricci = *(get<inertial_spatial_ricci_tag>(\n      make_not_null(&vars_to_interpolate_to_target), make_not_null(&buffer)));\n\n  gr::spatial_metric(make_not_null(&inertial_metric), spacetime_metric);\n  // put determinant of 3-metric temporarily into lapse to save memory.\n  determinant_and_inverse(make_not_null(&lapse),\n                          make_not_null(&inertial_inv_metric), inertial_metric);\n\n  // Compute inertial extrinsic curvature\n  gr::shift(make_not_null(&inertial_shift), spacetime_metric,\n            inertial_inv_metric);\n  gr::lapse(make_not_null(&lapse), inertial_shift, spacetime_metric);\n  gr::spacetime_normal_vector(make_not_null(&inertial_spacetime_normal_vector),\n                              lapse, inertial_shift);\n  gh::extrinsic_curvature(make_not_null(&inertial_ex_curvature),\n                          inertial_spacetime_normal_vector, pi, phi);\n\n  // Compute inertial spatial ricci\n  gh::spatial_ricci_tensor(make_not_null(&inertial_spatial_ricci), phi,\n                           deriv_phi, inertial_inv_metric);\n\n  if constexpr (std::is_same_v<FrameToUse, ::Frame::Inertial>) {\n    (void)jac_frame_to_inertial;\n    (void)invjac_logical_to_frame;\n\n    auto& inertial_christoffel_second_kind =\n        get<inertial_spatial_christoffel_tag>(vars_to_interpolate_to_target);\n    gh::christoffel_second_kind(\n        make_not_null(&inertial_christoffel_second_kind), phi,\n        inertial_inv_metric);\n  } else {\n    auto& frame_metric = get<metric_tag>(vars_to_interpolate_to_target);\n    transform::to_different_frame(make_not_null(&frame_metric), inertial_metric,\n                                  jac_frame_to_inertial);\n\n    // Invert transformed 3-metric and put determinant of 3-metric temporarily\n    // into lapse to save memory (since we don't need the lapse anymore)\n    auto& frame_inv_metric = get<inv_metric_tag>(vars_to_interpolate_to_target);\n    determinant_and_inverse(make_not_null(&lapse),\n                            make_not_null(&frame_inv_metric), frame_metric);\n\n    // Transform extrinsic curvature\n    auto& frame_extrinsic_curvature =\n        get<ex_curvature_tag>(vars_to_interpolate_to_target);\n    transform::to_different_frame(make_not_null(&frame_extrinsic_curvature),\n                                  inertial_ex_curvature, jac_frame_to_inertial);\n\n    // Differentiate 3-metric to compute spatial christoffel\n    auto& frame_phi = get<phi_tag>(buffer);\n    auto& frame_christoffel_second_kind =\n        get<spatial_christoffel_tag>(vars_to_interpolate_to_target);\n    partial_derivative(make_not_null(&frame_phi), frame_metric, mesh,\n                       invjac_logical_to_frame);\n    gr::christoffel_second_kind(make_not_null(&frame_christoffel_second_kind),\n                                frame_phi, frame_inv_metric);\n\n    // Transform spatial ricci\n    auto& frame_spatial_ricci =\n        get<spatial_ricci_tag>(vars_to_interpolate_to_target);\n    transform::to_different_frame(make_not_null(&frame_spatial_ricci),\n                                  inertial_spatial_ricci,\n                                  jac_frame_to_inertial);\n  }\n}\n}  // namespace\n\ntemplate <typename Fr>\nvoid compute_vars_to_interpolate_to_target(\n    const gsl::not_null<Variables<ah::vars_to_interpolate_to_target<3, Fr>>*>\n        target_vars,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::aa<DataVector, 3>& pi, const tnsr::iaa<DataVector, 3>& phi,\n    const tnsr::ijaa<DataVector, 3>& deriv_phi,\n    const LinkedMessageId<double>& time, const Domain<3>& domain,\n    const Mesh<3>& mesh, const ElementId<3>& element_id,\n    const domain::FunctionsOfTimeMap& functions_of_time) {\n  // This will only change the size if it isn't already the correct size\n  target_vars->initialize(get<0, 0>(spacetime_metric).size());\n\n  const auto& block = domain.blocks()[element_id.block_id()];\n  if (block.is_time_dependent()) {\n    ASSERT(not functions_of_time.empty(),\n           \"Functions of time are not passed to \"\n           \"compute_vars_to_interpolate_to_target but the block is \"\n           \"time-dependent.\");\n    if constexpr (std::is_same_v<Fr, ::Frame::Grid>) {\n      const ElementMap<3, Fr> map_logical_to_grid{\n          element_id, block.moving_mesh_logical_to_grid_map().get_clone()};\n      const auto logical_coords = logical_coordinates(mesh);\n      const auto jac_grid_to_inertial =\n          block.moving_mesh_grid_to_inertial_map().jacobian(\n              map_logical_to_grid(logical_coords), time.id, functions_of_time);\n      const auto invjac_logical_to_grid =\n          map_logical_to_grid.inv_jacobian(logical_coords);\n\n      compute_vars_to_interpolate_to_target_impl(\n          target_vars, spacetime_metric, pi, phi, deriv_phi, mesh,\n          jac_grid_to_inertial, invjac_logical_to_grid);\n    } else if constexpr (std::is_same_v<Fr, ::Frame::Distorted>) {\n      ASSERT(block.has_distorted_frame(),\n             \"Block doesn't have a distorted frame but this horizon is in the \"\n             \"distorted frame.\");\n      const domain::CoordinateMaps::Composition\n          element_logical_to_distorted_map{\n              domain::element_to_block_logical_map(element_id),\n              block.moving_mesh_logical_to_grid_map().get_clone(),\n              block.moving_mesh_grid_to_distorted_map().get_clone()};\n      const domain::CoordinateMaps::Composition element_logical_to_grid_map{\n          domain::element_to_block_logical_map(element_id),\n          block.moving_mesh_logical_to_grid_map().get_clone()};\n      const auto logical_coords = logical_coordinates(mesh);\n      const auto jac_distorted_to_inertial =\n          block.moving_mesh_distorted_to_inertial_map().jacobian(\n              element_logical_to_distorted_map(logical_coords, time.id,\n                                               functions_of_time),\n              time.id, functions_of_time);\n      const auto invjac_logical_to_distorted =\n          element_logical_to_distorted_map.inv_jacobian(logical_coords, time.id,\n                                                        functions_of_time);\n\n      compute_vars_to_interpolate_to_target_impl(\n          target_vars, spacetime_metric, pi, phi, deriv_phi, mesh,\n          jac_distorted_to_inertial, invjac_logical_to_distorted);\n    } else {\n      compute_vars_to_interpolate_to_target_impl(target_vars, spacetime_metric,\n                                                 pi, phi, deriv_phi, mesh);\n    }\n  } else {\n    // No frame transformations needed, since the maps are time-independent\n    // and therefore all the frames are the same.\n    //\n    // The frame tags may be different, but the source vars should all be in the\n    // Inertial frame, so we force the inertial frame (True template).\n    compute_vars_to_interpolate_to_target_impl<true>(\n        target_vars, spacetime_metric, pi, phi, deriv_phi, mesh);\n  }\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void compute_vars_to_interpolate_to_target(                        \\\n      const gsl::not_null<                                                    \\\n          Variables<ah::vars_to_interpolate_to_target<3, FRAME(data)>>*>      \\\n          target_vars,                                                        \\\n      const tnsr::aa<DataVector, 3>& spacetime_metric,                        \\\n      const tnsr::aa<DataVector, 3>& pi, const tnsr::iaa<DataVector, 3>& phi, \\\n      const tnsr::ijaa<DataVector, 3>& deriv_phi,                             \\\n      const LinkedMessageId<double>& time, const Domain<3>& domain,           \\\n      const Mesh<3>& mesh, const ElementId<3>& element_id,                    \\\n      const domain::FunctionsOfTimeMap& functions_of_time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Inertial, Frame::Distorted, Frame::Grid))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/ComputeVarsToInterpolateToTarget.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ah {\n/*!\n * \\brief Compute the `ah::vars_to_interpolate_to_target` for a given\n * \\p element_id from the `ah::source_vars` in that element.\n */\ntemplate <typename Fr>\nvoid compute_vars_to_interpolate_to_target(\n    gsl::not_null<Variables<ah::vars_to_interpolate_to_target<3, Fr>>*>\n        target_vars,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const tnsr::aa<DataVector, 3>& pi, const tnsr::iaa<DataVector, 3>& phi,\n    const tnsr::ijaa<DataVector, 3>& deriv_phi,\n    const LinkedMessageId<double>& time, const Domain<3>& domain,\n    const Mesh<3>& mesh, const ElementId<3>& element_id,\n    const domain::FunctionsOfTimeMap& functions_of_time);\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ApparentHorizonFinderCriteria)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  IncreaseResolution.cpp\n  Residual.cpp\n  Shape.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Criteria.hpp\n  Criterion.hpp\n  Factory.hpp\n  IncreaseResolution.hpp\n  Residual.hpp\n  Shape.hpp\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_GlobalCache\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  SphericalHarmonics\n  Parallel\n  Serialization\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup SurfacesGroup\n/// \\brief Criteria for deciding how resolution of an apparent horizon should be\n/// adapted\nnamespace ah::Criteria {}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace ah {\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Base class for criteria that determine how the resolution of an\n * apparent horizon should be changed\n *\n * \\details Each class derived from this class should\n * - Be option-creatable\n * - Be serializable\n * - Define the type aliases `argument_tags` and\n *   `compute_tags_for_observation_box` that are type lists of tags used in the\n *   call operator\n * - Define a call operator that returns a `size_t` that is the recommended\n *   resolution $L$ for the Strahlkorper representing the apparent horizon\n *\n * The call operator should take the following arguments, in order:\n * - An argument for each tag in `argument_tags`\n * - The Parallel::GlobalCache\n * - The Strahlkorper representing the apparent horizon\n * - A FastFlow::IterInfo corresponding to the horizon find that found the\n *   Strahlkorper\n */\nclass Criterion : public PUP::able {\n protected:\n  /// \\cond\n  Criterion() = default;\n  Criterion(const Criterion&) = default;\n  Criterion(Criterion&&) = default;\n  Criterion& operator=(const Criterion&) = default;\n  Criterion& operator=(Criterion&&) = default;\n  /// \\endcond\n public:\n  ~Criterion() override = default;\n  explicit Criterion(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  WRAPPED_PUPable_abstract(Criterion);\n\n  virtual std::string observation_name() = 0;\n\n  virtual bool is_equal(const Criterion& other) const = 0;\n\n  /// Evaluates the apparent horizon criteria by selecting the appropriate\n  /// derived class and forwarding its `argument_tags` from the ObservationBox\n  /// (along with the GlobalCache) to the call operator of the\n  /// derived class\n  ///\n  /// \\note In order to be available, a derived Criterion must be listed in\n  /// the entry for Criterion in\n  /// Metavarialbes::factory_creation::factory_classes\n  ///\n  /// \\note The ComputeTagsList of the ObservationBox should contain the union\n  /// of the tags listed in `compute_tags_for_observation_box` for each derived\n  /// Criterion listed in the `factory_classes`.\n  template <typename ComputeTagsList, typename DataBoxType,\n            typename Metavariables, typename Frame>\n  auto evaluate(const ObservationBox<ComputeTagsList, DataBoxType>& box,\n                Parallel::GlobalCache<Metavariables>& cache,\n                const ylm::Strahlkorper<Frame>& strahlkorper,\n                const FastFlow::IterInfo& info) const {\n    using factory_classes =\n        typename std::decay_t<Metavariables>::factory_creation::factory_classes;\n    return call_with_dynamic_type<size_t, tmpl::at<factory_classes, Criterion>>(\n        this, [&box, &cache, &strahlkorper, &info](auto* const criterion) {\n          return apply(*criterion, box, cache, strahlkorper, info);\n        });\n  }\n};\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/IncreaseResolution.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Residual.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Shape.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Criteria {\nusing standard_criteria = tmpl::list<Residual, Shape>;\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/IncreaseResolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/IncreaseResolution.hpp\"\n\nnamespace ah::Criteria {\nIncreaseResolution::IncreaseResolution(CkMigrateMessage* msg)\n    : Criterion(msg) {}\n\nbool IncreaseResolution::is_equal(const Criterion& other) const {\n  return dynamic_cast<const IncreaseResolution*>(&other) != nullptr;\n}\n\nPUP::able::PUP_ID IncreaseResolution::my_PUP_ID = 0;  // NOLINT\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/IncreaseResolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Criteria {\n/*!\n * \\brief Increases the resolution of the Strahlkorper by 2.\n *\n * Useful to force the horizon finder to adopt the maximum allowed resolution\n * and as a simple criterion for testing.\n */\nclass IncreaseResolution : public Criterion {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Increases the resolution of the Strahlkorper by 2.\"};\n\n  IncreaseResolution() = default;\n\n  /// \\cond\n  explicit IncreaseResolution(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(IncreaseResolution);  // NOLINT\n  /// \\endcond\n\n  std::string observation_name() override { return \"IncreaseResolution\"; }\n\n  using argument_tags = tmpl::list<>;\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  template <typename Metavariables, typename Frame>\n  size_t operator()(const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ylm::Strahlkorper<Frame>& strahlkorper,\n                    const FastFlow::IterInfo& /*info*/) const {\n    return strahlkorper.l_max() + 2;\n  }\n\n  bool is_equal(const Criterion& other) const override;\n};\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Residual.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Residual.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ah::Criteria {\nResidual::Residual(double min_residual, double max_residual,\n                   size_t min_resolution_l, const Options::Context& context)\n    : min_residual_(min_residual),\n      max_residual_(max_residual),\n      min_resolution_l_(min_resolution_l) {\n  if (min_residual_ >= max_residual_) {\n    PARSE_ERROR(context, \"MinResidual must be less than MaxResidual\");\n  }\n  if (min_resolution_l_ < 2) {\n    PARSE_ERROR(context, \"MinResolutionL must not be less than 2\");\n  }\n}\n\nvoid Residual::pup(PUP::er& p) {\n  p | min_residual_;\n  p | max_residual_;\n  p | min_resolution_l_;\n}\n\nbool Residual::is_equal(const Criterion& other) const {\n  const auto* other_residual = dynamic_cast<const Residual*>(&other);\n  if (other_residual == nullptr) {\n    return false;\n  }\n  return min_residual_ == other_residual->min_residual_ and\n         max_residual_ == other_residual->max_residual_ and\n         min_resolution_l_ == other_residual->min_resolution_l_;\n}\n\nResidual::Residual(CkMigrateMessage* msg) : Criterion(msg) {}\n\nPUP::able::PUP_ID ah::Criteria::Residual::my_PUP_ID = 0;  // NOLINT\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Residual.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <string>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Criteria {\n/*!\n * \\brief Recommend an updated resolution $L_{\\rm new}$ based on the\n * resolution $L$ of the Strahlkorper and its residual $\\delta$.\n *\n * \\details The returned recommended resolution $L_{\\rm new}$ depends on\n * the following options:\n * - MinResidual: a minimum residual $\\delta_{\\rm min}$\n * - MaxResidual: a maximum residual $\\delta_{\\rm max}$\n * - MinResolutionL: a minimum resolution $L_{\\rm min}$\n *\n * The maximum resolution $L_{\\rm max}$ is provided by the global cache entry\n * `ah::Tags::LMax`. The value returned for $L_{\\rm new}$ is\n * then determined as follows:\n * - If $L > L_{\\rm min}$ and $\\delta < \\delta_{\\rm min}$, then\n * $L_{\\rm new} = L - 1$\n * - If $L < L_{\\rm max}$ and $\\delta > \\delta_{\\rm max}$, then\n * $L_{\\rm new} = L + 1$\n * - Otherwise, $L_{\\rm new} = L$\n * Note that the residual is obtained from the provided FastFlow::IterInfo. The\n * residual is the gr::surfaces::expansion weighted by the FastFlow weights.\n */\nclass Residual : public Criterion {\n public:\n  struct MinResidual {\n    using type = double;\n    static constexpr Options::String help = {\"The minimum residual.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct MaxResidual {\n    using type = double;\n    static constexpr Options::String help = {\"The maximum residual.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct MinResolutionL {\n    using type = size_t;\n    static constexpr Options::String help = {\"The minimum resolution.\"};\n    // Strahlkorper default constructor sets L=2, so don't allow L below that\n    static type lower_bound() { return 2; }\n  };\n  using options = tmpl::list<MinResidual, MaxResidual, MinResolutionL>;\n  static constexpr Options::String help = {\n      \"Use Strahlkorper residual and resolution to suggest a new \"\n      \"resolution.\"};\n\n  Residual() = default;\n  Residual(double min_residual, double max_residual, size_t min_resolution_l,\n           const Options::Context& context = {});\n\n  /// \\cond\n  explicit Residual(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Residual);  // NOLINT\n  /// \\endcond\n\n  std::string observation_name() override { return \"Residual\"; }\n\n  using argument_tags = tmpl::list<>;\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  template <typename Metavariables, typename Frame>\n  size_t operator()(const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ylm::Strahlkorper<Frame>& strahlkorper,\n                    const FastFlow::IterInfo& info) const;\n\n  void pup(PUP::er& p) override;\n\n  bool is_equal(const Criterion& other) const override;\n\n private:\n  double min_residual_{std::numeric_limits<double>::signaling_NaN()};\n  double max_residual_{std::numeric_limits<double>::signaling_NaN()};\n  size_t min_resolution_l_{};\n};\n\n// Out-of-line definition\n/// \\cond\ntemplate <typename Metavariables, typename Frame>\nsize_t Residual::operator()(const Parallel::GlobalCache<Metavariables>& cache,\n                            const ylm::Strahlkorper<Frame>& strahlkorper,\n                            const FastFlow::IterInfo& info) const {\n  const auto& max_resolution_l = Parallel::get<ah::Tags::LMax>(cache);\n  ASSERT(min_resolution_l_ <= max_resolution_l,\n         \"MinResolutionL (\" << min_resolution_l_ << \") must not exceed LMax (\"\n                            << max_resolution_l << \").\");\n  if (UNLIKELY(min_resolution_l_ == max_resolution_l)) {\n    ASSERT(min_resolution_l_ == strahlkorper.l_max(),\n           \"If MinResolutionL == LMax, strahlkorper \"\n           \"resolution must also equal MinResolutionL, but here MinResolutionL \"\n           \"is \"\n               << min_resolution_l_ << \", LMax is \" << max_resolution_l\n               << \", and the current resolution is \" << strahlkorper.l_max());\n    return strahlkorper.l_max();\n  }\n  if (strahlkorper.l_max() > min_resolution_l_ and\n      info.residual_ylm < min_residual_ and strahlkorper.l_max() > 0) {\n    return strahlkorper.l_max() - 1;\n  }\n  if (strahlkorper.l_max() < max_resolution_l and\n      info.residual_ylm > max_residual_) {\n    return strahlkorper.l_max() + 1;\n  }\n  return strahlkorper.l_max();\n}\n/// \\endcond\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Shape.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Shape.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n\nnamespace ah::Criteria {\nShape::Shape(double min_truncation_error, double max_truncation_error,\n             size_t max_pile_up_modes, size_t min_resolution_l,\n             const Options::Context& context)\n    : min_truncation_error_(min_truncation_error),\n      max_truncation_error_(max_truncation_error),\n      max_pile_up_modes_(max_pile_up_modes),\n      min_resolution_l_(min_resolution_l) {\n  if (min_truncation_error_ >= max_truncation_error_) {\n    PARSE_ERROR(context,\n                \"MinTruncationError must be less than MaxTruncationError\");\n  }\n  if (min_resolution_l_ < MinResolutionL::lower_bound()) {\n    PARSE_ERROR(context, \"MinResolutionL must be at least \"\n                             << MinResolutionL::lower_bound());\n  }\n}\n\nvoid Shape::pup(PUP::er& p) {\n  p | min_truncation_error_;\n  p | max_truncation_error_;\n  p | max_pile_up_modes_;\n  p | min_resolution_l_;\n}\n\nbool Shape::is_equal(const Criterion& other) const {\n  const auto* other_shape = dynamic_cast<const Shape*>(&other);\n  if (other_shape == nullptr) {\n    return false;\n  }\n  return min_truncation_error_ == other_shape->min_truncation_error_ and\n         max_truncation_error_ == other_shape->max_truncation_error_ and\n         max_pile_up_modes_ == other_shape->max_pile_up_modes_ and\n         min_resolution_l_ == other_shape->min_resolution_l_;\n}\n\nShape::Shape(CkMigrateMessage* msg) : Criterion(msg) {}\n\nPUP::able::PUP_ID ah::Criteria::Shape::my_PUP_ID = 0;  // NOLINT\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Shape.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <string>\n\n#include \"NumericalAlgorithms/LinearOperators/PowerMonitors.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Criteria {\n/*!\n * \\brief Recommend an updated resolution $L_{\\rm new}$ based on how well\n * the shape of the apparent horizon is resolved in terms of its\n * truncation error and pile up modes.\n *\n * \\details The returned recommended resolution $L_{\\rm new}$ depends on\n * the following options:\n * - MinTruncationError: a minimum truncation error $\\mathcal{T}_{\\rm min}$\n * - MaxTruncationError: a maximum truncation error $\\mathcal{T}_{\\rm max}$\n * - MaxPileUpModes: a maximum number of pile up modes $\\mathcal{P}_{\\rm max}$\n * - MinResolutionL: a minimum resolution $L_{\\rm min}$\n *\n * The maximum resolution $L_{\\rm max}$ is supplied by the global cache entry\n * `ah::Tags::LMax`. The value returned for $L_{\\rm new}$ is\n * then determined as follows:\n * The value returned for $L_{\\rm new}$ is then determined as follows:\n * - If $L > L_{\\rm min}$ and $\\mathcal{T} < \\mathcal{T}_{\\rm min}$, then\n * $L_{\\rm new} = L - 1$\n * - If $L < L_{\\rm max}$ and $\\mathcal{T} > \\mathcal{T}_{\\rm max}$ and\n * $\\mathcal{P} \\le \\mathcal{P}_{\\rm max}$, then\n * $L_{\\rm new} = L + 1$\n * - Otherwise, $L_{\\rm new} = L$\n * Note that the truncation error is obtained via\n * PowerMonitors::relative_truncation_error, and the number of pileup modes\n * is given by PowerMonitors::convergence_rate_and_number_of_pile_up_modes\n */\nclass Shape : public Criterion {\n public:\n  struct MinTruncationError {\n    using type = double;\n    static constexpr Options::String help = {\"The minimum truncation error.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct MaxTruncationError {\n    using type = double;\n    static constexpr Options::String help = {\"The maximum truncation error.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct MaxPileUpModes {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The maximum number of pile up modes.\"};\n    static type lower_bound() { return 0; }\n  };\n  struct MinResolutionL {\n    using type = size_t;\n    static constexpr Options::String help = {\"The minimum resolution.\"};\n    // Power montior requires at least L=4, so don't allow L below that\n    static type lower_bound() { return 4; }\n  };\n  using options = tmpl::list<MinTruncationError, MaxTruncationError,\n                             MaxPileUpModes, MinResolutionL>;\n  static constexpr Options::String help = {\n      \"Use Strahlkorper truncation error, number of pile up modes, and \"\n      \"resolution to suggest a new resolution.\"};\n\n  Shape() = default;\n  Shape(double min_truncation_error, double max_truncation_error,\n        size_t max_pile_up_modes, size_t min_resolution_l,\n        const Options::Context& context = {});\n\n  /// \\cond\n  explicit Shape(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Shape);  // NOLINT\n  /// \\endcond\n\n  std::string observation_name() override { return \"Shape\"; }\n\n  using argument_tags = tmpl::list<>;\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  template <typename Metavariables, typename Frame>\n  size_t operator()(const Parallel::GlobalCache<Metavariables>& cache,\n                    const ylm::Strahlkorper<Frame>& strahlkorper,\n                    const FastFlow::IterInfo& /*info*/) const;\n\n  void pup(PUP::er& p) override;\n\n  bool is_equal(const Criterion& other) const override;\n\n private:\n  double min_truncation_error_{std::numeric_limits<double>::signaling_NaN()};\n  double max_truncation_error_{std::numeric_limits<double>::signaling_NaN()};\n  size_t max_pile_up_modes_{};\n  size_t min_resolution_l_{};\n};\n\n// Out-of-line definition\n/// \\cond\ntemplate <typename Metavariables, typename Frame>\nsize_t Shape::operator()(const Parallel::GlobalCache<Metavariables>& cache,\n                         const ylm::Strahlkorper<Frame>& strahlkorper,\n                         const FastFlow::IterInfo& /*info*/) const {\n  const auto& max_resolution_l = Parallel::get<ah::Tags::LMax>(cache);\n  ASSERT(min_resolution_l_ <= max_resolution_l,\n         \"MinResolutionL (\" << min_resolution_l_ << \") must not exceed LMax (\"\n                            << max_resolution_l << \").\");\n  if (UNLIKELY(min_resolution_l_ == max_resolution_l)) {\n    ASSERT(min_resolution_l_ == strahlkorper.l_max(),\n           \"If MinResolutionL == LMax, strahlkorper \"\n           \"resolution must also equal MinResolutionL, but here MinResolutionL \"\n           \"is \"\n               << min_resolution_l_ << \", LMax is \" << max_resolution_l\n               << \", and the current resolution is \" << strahlkorper.l_max());\n    return strahlkorper.l_max();\n  }\n\n  const DataVector& power_monitor = ylm::power_monitor(strahlkorper);\n  const double truncation_error = PowerMonitors::relative_truncation_error(\n      power_monitor, power_monitor.size());\n  const size_t pile_up_modes =\n      PowerMonitors::convergence_rate_and_number_of_pile_up_modes(power_monitor)\n          .number_of_pile_up_modes;\n  if (strahlkorper.l_max() > min_resolution_l_ and\n      truncation_error < min_truncation_error_ and strahlkorper.l_max() > 0) {\n    return strahlkorper.l_max() - 1;\n  }\n  if (strahlkorper.l_max() < max_resolution_l and\n      truncation_error > max_truncation_error_ and\n      pile_up_modes <= max_pile_up_modes_) {\n    return strahlkorper.l_max() + 1;\n  }\n  return strahlkorper.l_max();\n}\n/// \\endcond\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Criteria.hpp\n  Tags.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Tags/Criteria.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Criteria {\n/// Option tags for adaptive horizon finding criteria\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// Options for adaptive horizon finding criteria\nstruct Criteria {\n  static constexpr Options::String help =\n      \"Options for adaptive horizon finding criteria\";\n  using type = std::vector<std::unique_ptr<ah::Criterion>>;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// The set of adaptive horizon finding criteria\nstruct Criteria : db::SimpleTag {\n  using type = std::vector<std::unique_ptr<ah::Criterion>>;\n  using option_tags = tmpl::list<ah::Criteria::OptionTags::Criteria>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) {\n    return {serialize_and_deserialize<type>(value)};\n  }\n};\n}  // namespace Tags\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Tags/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup SurfacesGroup\n/// \\brief Tags for adaptive apparent horizon finding criteria\nnamespace ah::Criteria::Tags {}  // namespace ah::Criteria::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/CurrentTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/CurrentTime.hpp\"\n\n#include <optional>\n#include <set>\n#include <sstream>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ah {\ntemplate <typename Fr>\nvoid set_current_time(\n    const gsl::not_null<std::optional<LinkedMessageId<double>>*> current_time,\n    const gsl::not_null<std::set<LinkedMessageId<double>>*> pending_times,\n    const std::set<LinkedMessageId<double>>& completed_times,\n    const std::unordered_map<LinkedMessageId<double>,\n                             ah::Storage::SingleTimeStorage<Fr>>& all_storage,\n    const ::Verbosity& verbosity, const std::string& name) {\n  std::stringstream ss{};\n  const bool verbose_print = verbosity >= ::Verbosity::Verbose;\n  const bool debug_print = verbosity >= ::Verbosity::Debug;\n  if (debug_print) {\n    ss << name << \": \";\n  }\n\n  // If there's already a horizon find in progress, end here\n  if (current_time->has_value() or pending_times->empty()) {\n    if (debug_print) {\n      if (current_time->has_value()) {\n        ss << \"Interpolation already in progress at time \"\n           << current_time->value();\n      } else {\n        ss << \"No pending times.\";\n      }\n      Parallel::printf(\"%s\\n\", ss.str());\n    }\n\n    return;\n  }\n\n  // To determine if we are going to use the next pending time, we need to check\n  // if we have any completed times so we can check if this is the very first\n  // time or not. If this horizon find is used for observation, we don't have a\n  // previous time, so we just use the next pending time regardless. Though this\n  // is technically prone to async issues where times arrive out of order, we\n  // shouldn't be observing so often that this should matter.\n  const auto& next_pending_time = *pending_times->begin();\n  const Destination destination = all_storage.at(next_pending_time).destination;\n  const bool use_next_pending_time =\n      (destination == Destination::Observation) or\n      (UNLIKELY(completed_times.empty())\n           ? not next_pending_time.previous.has_value()\n           : next_pending_time.previous.value() ==\n                 std::prev(completed_times.end())->id);\n\n  // If we are using the next pending time:\n  // 1. Set the current time to this next pending time\n  // 2. Remove this time from pending\n  if (use_next_pending_time) {\n    (*current_time) = next_pending_time;\n    pending_times->erase(pending_times->begin());\n\n    if (verbose_print) {\n      ss << \"Setting current time to \" << *current_time;\n    }\n  } else if (verbose_print) {\n    ss << \"Not setting current time.\";\n  }\n\n  if (verbose_print) {\n    Parallel::printf(\"%s\\n\", ss.str());\n  }\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void set_current_time(                                            \\\n      const gsl::not_null<std::optional<LinkedMessageId<double>>*>           \\\n          current_time,                                                      \\\n      const gsl::not_null<std::set<LinkedMessageId<double>>*> pending_times, \\\n      const std::set<LinkedMessageId<double>>& completed_times,              \\\n      const std::unordered_map<LinkedMessageId<double>,                      \\\n                               ah::Storage::SingleTimeStorage<FRAME(data)>>& \\\n          all_storage,                                                       \\\n      const ::Verbosity& verbosity, const std::string& name);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Inertial, Frame::Distorted, Frame::Grid))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/CurrentTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <set>\n#include <sstream>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace ah {\ntemplate <class Metavariables, typename HorizonMetavars>\nstruct Component;\ntemplate <typename HorizonMetavars>\nstruct FindApparentHorizon;\n}  // namespace ah\n/// \\endcond\n\nnamespace ah {\n/*!\n * \\brief Determines what the current time should be.\n *\n * \\details If there's already a current time, or there are no pending times\n * available, then there's nothing to do. Otherwise, checks if the first pending\n * time is the next to use. If so, sets it as the current time and removes it\n * from pending.\n */\ntemplate <typename Fr>\nvoid set_current_time(\n    gsl::not_null<std::optional<LinkedMessageId<double>>*> current_time,\n    gsl::not_null<std::set<LinkedMessageId<double>>*> pending_times,\n    const std::set<LinkedMessageId<double>>& completed_times,\n    const std::unordered_map<LinkedMessageId<double>,\n                             ah::Storage::SingleTimeStorage<Fr>>& all_storage,\n    const ::Verbosity& verbosity, const std::string& name);\n\n/*!\n * \\brief Checks if the current time is ready.\n *\n * \\details If the current time is after any expiration time, registers a\n * callback for the `ah::FindApparentHorizon` action (but doesn't send the\n * volume variables again because we already did that). Returns if the current\n * time is ready or not.\n */\ntemplate <typename HorizonMetavars, typename Metavariables>\nbool check_if_current_time_is_ready(\n    const LinkedMessageId<double>& current_time,\n    Parallel::GlobalCache<Metavariables>& cache,\n    const LinkedMessageId<double>& incoming_time,\n    const ElementId<3>& incoming_element_id, const ::Mesh<3>& incoming_mesh,\n    const std::optional<std::string>& dependency) {\n  const auto& domain = get<domain::Tags::Domain<3>>(cache);\n\n  // Now we need to check the functions of time for the current time\n  if constexpr (Parallel::is_in_global_cache<Metavariables,\n                                             domain::Tags::FunctionsOfTime>) {\n    if (domain.is_time_dependent()) {\n      auto& this_proxy = Parallel::get_parallel_component<\n          Component<Metavariables, HorizonMetavars>>(cache);\n      double min_expiration_time = std::numeric_limits<double>::max();\n      const Parallel::ArrayComponentId array_component_id =\n          Parallel::make_array_component_id<\n              Component<Metavariables, HorizonMetavars>>(0);\n      // If the functions of time aren't ready, this will set a callback to\n      // FindApparentHorizon that will be called by the GlobalCache when\n      // domain::Tags::FunctionsOfTime is updated.\n      return ::Parallel::mutable_cache_item_is_ready<\n          domain::Tags::FunctionsOfTime>(\n          cache, array_component_id,\n          [&](const std::unordered_map<\n              std::string,\n              std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n                  functions_of_time) -> std::unique_ptr<Parallel::Callback> {\n            min_expiration_time =\n                std::min_element(functions_of_time.begin(),\n                                 functions_of_time.end(),\n                                 [](const auto& a, const auto& b) {\n                                   return a.second->time_bounds()[1] <\n                                          b.second->time_bounds()[1];\n                                 })\n                    ->second->time_bounds()[1];\n\n            // Success: the current time is ok.\n            // Failure: the current time is not ok.\n            using horizon_frame = typename HorizonMetavars::frame;\n            return current_time.id <= min_expiration_time\n                       ? std::unique_ptr<Parallel::Callback>{}\n                       : std::unique_ptr<Parallel::Callback>(\n                             new Parallel::SimpleActionCallback<\n                                 FindApparentHorizon<HorizonMetavars>,\n                                 decltype(this_proxy), LinkedMessageId<double>,\n                                 ElementId<3>, Mesh<3>,\n                                 Variables<ah::vars_to_interpolate_to_target<\n                                     3, horizon_frame>>,\n                                 std::optional<std::string>, bool>(\n                                 this_proxy, incoming_time, incoming_element_id,\n                                 incoming_mesh,\n                                 Variables<ah::vars_to_interpolate_to_target<\n                                     3, horizon_frame>>{},\n                                 dependency,\n                                 /* vars_have_already_been_received */ true));\n          });\n    }  // if (domain.is_time_dependent())\n  } else {\n    if (domain.is_time_dependent()) {\n      // We error here because the maps are time-dependent, yet\n      // the cache does not contain FunctionsOfTime.  It would be\n      // nice to make this a compile-time error; however, we want\n      // the code to compile for the completely time-independent\n      // case where there are no FunctionsOfTime in the cache at\n      // all.  Unfortunately, checking whether the maps are\n      // time-dependent is currently not constexpr.\n      ERROR(\n          \"There is a time-dependent CoordinateMap in at least one \"\n          \"of the Blocks, but FunctionsOfTime are not in the \"\n          \"GlobalCache.  If you intend to use a time-dependent \"\n          \"CoordinateMap, please add FunctionsOfTime to the GlobalCache.\");\n    }\n  }\n\n  // The current time is ready\n  return true;\n}\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Destination.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace ah {\nstd::ostream& operator<<(std::ostream& os, const Destination destination) {\n  switch (destination) {\n    case Destination::Observation:\n      return os << \"Observation\";\n    case Destination::ControlSystem:\n      return os << \"ControlSystem\";\n    default:\n      ERROR(\"Unknown Destination type\");\n  }\n}\n}  // namespace ah\n\ntemplate <>\nah::Destination Options::create_from_yaml<ah::Destination>::create<void>(\n    const Options::Option& options) {\n  const auto destination = options.parse_as<std::string>();\n  if (destination == \"Observation\") {\n    return ah::Destination::Observation;\n  } else if (destination == \"ControlSystem\") {\n    return ah::Destination::ControlSystem;\n  }\n  PARSE_ERROR(options.context(),\n              \"Destination must be 'Observation' or 'ControlSystem'\");\n}\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n\n/// \\cond\nnamespace Options {\nclass Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\n/// \\endcond\n\nnamespace ah {\n/*!\n * \\brief Label for what a horizon find will be used for.\n */\nenum class Destination { Observation, ControlSystem };\nstd::ostream& operator<<(std::ostream& os, Destination destination);\n}  // namespace ah\n\ntemplate <>\nstruct Options::create_from_yaml<ah::Destination> {\n  template <typename Metavariables>\n  static ah::Destination create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nah::Destination Options::create_from_yaml<ah::Destination>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FindApparentHorizon.hpp\n  FindCommonHorizon.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeVarsToInterpolateToTarget.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CleanupRoutine.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Events {\n/*!\n * \\brief Starts a horizon find by sending volume data (`ah::source_vars`) to\n * the horizon finder component (`ah::Component`) using the\n * `ah::FindApparentHorizon` simple action.\n *\n * \\details Only sends data if this Element is in the\n * `ah::Tags::BlocksForHorizonFind` tag.\n *\n */\ntemplate <typename HorizonMetavars>\nclass FindApparentHorizon : public Event {\n public:\n  /// \\cond\n  explicit FindApparentHorizon(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(FindApparentHorizon);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help = \"Start a horizon find.\";\n\n  static std::string name() { return pretty_type::name<HorizonMetavars>(); }\n\n  FindApparentHorizon() = default;\n\n  /// This constructor is not available through options\n  explicit FindApparentHorizon(std::optional<std::string> dependency)\n      : dependency_(std::move(dependency)) {}\n\n  using compute_tags_for_observation_box =\n      typename HorizonMetavars::compute_tags_on_element;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::append<\n      tmpl::list<typename HorizonMetavars::time_tag,\n                 ::Events::Tags::ObserverMesh<3>, domain::Tags::Element<3>>,\n      ah::source_vars<3>>;\n\n  template <typename Metavariables, typename ParallelComponent>\n  void operator()(const LinkedMessageId<double>& time, const Mesh<3>& mesh,\n                  const Element<3>& element,\n                  const tnsr::aa<DataVector, 3>& spacetime_metric,\n                  const tnsr::aa<DataVector, 3>& pi,\n                  const tnsr::iaa<DataVector, 3>& phi,\n                  const tnsr::ijaa<DataVector, 3>& deriv_phi,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<3>& element_id,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    const auto& blocks_to_interpolate =\n        Parallel::get<ah::Tags::BlocksForHorizonFind>(cache);\n    ASSERT(blocks_to_interpolate.contains(name_),\n           \"Blocks to interpolate doesn't contain target \" << name_);\n    const auto& blocks_to_interpolate_for_this_target =\n        blocks_to_interpolate.at(name_);\n    const auto& domain = Parallel::get<domain::Tags::Domain<3>>(cache);\n    const auto& blocks = domain.blocks();\n    const auto& block = blocks[element_id.block_id()];\n    const auto& block_name = block.name();\n\n    // Only send data if this target needs this blocks data\n    if (not blocks_to_interpolate_for_this_target.contains(block_name)) {\n      return;\n    }\n\n    // Send volume data ONLY if this element intersected with the previous\n    // horizon or if it's a neighbor of an intersecting element.\n    // - WARNING: the algorithm WILL deadlock if the horizon has moved outside\n    //   of the elements that send data here. So we can't be too greedy with the\n    //   elements that send data. We may have to send data from corner neighbors\n    //   as well if deadlocks occur.\n    // - WARNING: we don't currently wait for the `PreviousSurface` to be\n    //   updated by the last horizon find. This could be done by storing the\n    //   time of the last horizon find in the element DataBox and comparing it\n    //   to the time that's stored in `PreviousSurface`. However, this added\n    //   dependency would possibly introduce waiting. So far, I (NV) haven't\n    //   found that waiting for the update is necessary, meaning that whichever\n    //   intersecting element IDs are stored are close enough to the latest\n    //   state so that they cover the new apparent horizon (no deadlocks have\n    //   occurred in testing).\n    //   Alternatives: if we find that we need the up-to-date intersecting\n    //   elements we could just send the data without waiting if the\n    //   intersecting element IDs are not up-to-date, which just risks sending\n    //   more data than necessary (and therefore doing more work, potentially\n    //   undoing this performance optimization if it happens a lot).\n    const auto& locked_previous_surface =\n        Parallel::get<ah::Tags::PreviousSurface<HorizonMetavars>>(cache);\n    {\n      // Scope to lock and unlock the read lock\n      locked_previous_surface.lock.read_lock();\n      const CleanupRoutine unlock_read_lock = [&locked_previous_surface]() {\n        locked_previous_surface.lock.read_unlock();\n      };\n      // Only skip elements if we have a previous surface\n      if (locked_previous_surface.surface.has_value()) {\n        const auto& previous_surface = locked_previous_surface.surface.value();\n        // If we already found a horizon AFTER the current time, skip sending\n        // anything (the data won't be needed anymore). Unlikely to occur.\n        if (previous_surface.time.id >= time.id) {\n          return;\n        }\n        // Check if this element or any of its neighbors overlaps an element\n        // that intersected the horizon in the last find. Only send volume data\n        // if so.\n        const bool send_volume_data = alg::any_of(\n            previous_surface.intersecting_element_ids,\n            [&element_id,\n             &element](const ElementId<3>& intersecting_element_id) {\n              return overlapping(element_id, intersecting_element_id) or\n                     alg::any_of(\n                         element.neighbors(),\n                         [&intersecting_element_id](\n                             const std::pair<Direction<3>, Neighbors<3>>&\n                                 direction_and_neighbors) {\n                           return alg::any_of(\n                               direction_and_neighbors.second.ids(),\n                               [&intersecting_element_id](\n                                   const ElementId<3>& neighbor_id) {\n                                 return overlapping(neighbor_id,\n                                                    intersecting_element_id);\n                               });\n                         });\n            });\n        if (not send_volume_data) {\n          return;\n        }\n      }  // if previous_surface.has_value()\n    }    // Unlock read lock here\n\n    // Make Variables<ah::vars_to_interpolate_to_target> and\n    // fill it by calling compute_vars_to_interpolate_to_target().\n    // Then pass this variables to the FindApparentHorizon simple action.\n    using horizon_frame = typename HorizonMetavars::frame;\n    Variables<ah::vars_to_interpolate_to_target<3, horizon_frame>>\n        vars_to_interpolate_to_target{mesh.number_of_grid_points()};\n    if (block.is_time_dependent()) {\n      if constexpr (Parallel::is_in_global_cache<\n                        Metavariables, domain::Tags::FunctionsOfTime>) {\n        const auto& functions_of_time =\n            Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n        ah::compute_vars_to_interpolate_to_target(\n            make_not_null(&vars_to_interpolate_to_target), spacetime_metric, pi,\n            phi, deriv_phi, time, domain, mesh, element_id, functions_of_time);\n      } else {\n        ERROR(\n            \"Block is time-dependent but FunctionsOfTime are not available \"\n            \"in the global cache.\");\n      }\n    } else {\n      ah::compute_vars_to_interpolate_to_target(\n          make_not_null(&vars_to_interpolate_to_target), spacetime_metric, pi,\n          phi, deriv_phi, time, domain, mesh, element_id, {});\n    }\n\n    auto& horizon_finder_proxy = Parallel::get_parallel_component<\n        ah::Component<Metavariables, HorizonMetavars>>(cache);\n\n    Parallel::simple_action<ah::FindApparentHorizon<HorizonMetavars>>(\n        horizon_finder_proxy, time, element_id, mesh,\n        std::move(vars_to_interpolate_to_target), dependency_);\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*component*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | dependency_;\n  }\n\n private:\n  std::optional<std::string> dependency_;\n  // Evaluate the static name() function only once to avoid repeated allocations\n  std::string name_ = name();\n};\n\n/// \\cond\n// NOLINTBEGIN\ntemplate <typename HorizonMetavars>\nPUP::able::PUP_ID FindApparentHorizon<HorizonMetavars>::my_PUP_ID = 0;\n// NOLINTEND\n/// \\endcond\n}  // namespace ah::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Events/FindCommonHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/PerformAlgorithmOnElement.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Actions/GetItemFromDistributedObject.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveFields.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace Events::Tags {\ntemplate <size_t Dim>\nstruct ObserverMesh;\n}  // namespace Events::Tags\n/// \\endcond\n\nnamespace ah::Events {\n/*!\n * \\brief Event that combines the `ah::Events::FindApparentHorizon` event with\n * `dg::Events::ObserveFields` specifically for common horizon finding.\n *\n * \\details This is just a thin wrapper around each event and doesn't do\n * anything extra.\n */\ntemplate <typename HorizonMetavars, typename Tensors,\n          typename NonTensorComputeTagsList = tmpl::list<>>\nclass FindCommonHorizon;\n\ntemplate <typename HorizonMetavars, typename... Tensors,\n          typename... NonTensorComputeTags>\nclass FindCommonHorizon<HorizonMetavars, tmpl::list<Tensors...>,\n                        tmpl::list<NonTensorComputeTags...>> : public Event {\n  using ObserveFieldsEvent =\n      dg::Events::ObserveFields<3, tmpl::list<Tensors...>,\n                                tmpl::list<NonTensorComputeTags...>>;\n  using HorizonFindEvent = ah::Events::FindApparentHorizon<HorizonMetavars>;\n\n public:\n  /// \\cond\n  explicit FindCommonHorizon(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(FindCommonHorizon);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::append<typename ObserveFieldsEvent::options,\n                               typename HorizonFindEvent::options>;\n  static constexpr Options::String help =\n      \"Starts a common horizon find and sends the evolved variables to be \"\n      \"written to disk.\";\n\n  static std::string name() { return pretty_type::name<HorizonMetavars>(); }\n\n  FindCommonHorizon() = default;\n\n  FindCommonHorizon(\n      const std::string& subfile_name,\n      FloatingPointType coordinates_floating_point_type,\n      const std::vector<FloatingPointType>& floating_point_types,\n      const std::vector<std::string>& variables_to_observe,\n      std::optional<std::vector<std::string>> active_block_or_block_groups = {},\n      std::optional<Mesh<3>> interpolation_mesh = {},\n      std::optional<Mesh<3>> projection_mesh = {},\n      const Options::Context& context = {})\n      : observe_fields_event_(subfile_name, coordinates_floating_point_type,\n                              floating_point_types, variables_to_observe,\n                              active_block_or_block_groups, interpolation_mesh,\n                              projection_mesh, std::nullopt, context),\n        horizon_find_event_(subfile_name) {}\n\n  using compute_tags_for_observation_box = tmpl::remove_duplicates<tmpl::append<\n      typename ObserveFieldsEvent::compute_tags_for_observation_box,\n      typename HorizonFindEvent::compute_tags_for_observation_box>>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::ObservationBox, ::Events::Tags::ObserverMesh<3>,\n                 domain::Tags::Element<3>>;\n\n  template <typename DataBoxType, typename ComputeTagsList,\n            typename Metavariables, typename ParallelComponent>\n  void operator()(const ObservationBox<DataBoxType, ComputeTagsList>& box,\n                  const Mesh<3>& mesh, const Element<3>& element,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<3>& element_id,\n                  const ParallelComponent* const component,\n                  const ObservationValue& observation_value) const {\n    observe_fields_event_(box, mesh, cache, element_id, component,\n                          observation_value);\n\n    horizon_find_event_(\n        get<typename HorizonMetavars::time_tag>(box), mesh, element,\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(box),\n        get<gh::Tags::Pi<DataVector, 3>>(box),\n        get<gh::Tags::Phi<DataVector, 3>>(box),\n        get<::Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                          Frame::Inertial>>(box),\n        cache, element_id, component, observation_value);\n  }\n\n  using observation_registration_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList>\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration(\n      const db::DataBox<DbTagsList>& box) const {\n    return observe_fields_event_.get_observation_type_and_key_for_registration(\n        box);\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ElementId<3>& /*element_id*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | observe_fields_event_;\n    p | horizon_find_event_;\n  }\n\n private:\n  ObserveFieldsEvent observe_fields_event_;\n  HorizonFindEvent horizon_find_event_;\n};\n\n/// \\cond\n// NOLINTBEGIN\ntemplate <typename HorizonMetavars, typename... Tensors,\n          typename... NonTensorComputeTags>\nPUP::able::PUP_ID\n    FindCommonHorizon<HorizonMetavars, tmpl::list<Tensors...>,\n                      tmpl::list<NonTensorComputeTags...>>::my_PUP_ID = 0;\n// NOLINTEND\n/// \\endcond\n}  // namespace ah::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/FastFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <pup.h>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Expansion.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/GradUnitNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/InverseSurfaceMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/UnitNormalOneForm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nFastFlow::FastFlow(FastFlow::Flow::type flow, FastFlow::Alpha::type alpha,\n                   FastFlow::Beta::type beta, FastFlow::AbsTol::type abs_tol,\n                   FastFlow::TruncationTol::type trunc_tol,\n                   FastFlow::DivergenceTol::type divergence_tol,\n                   FastFlow::DivergenceIter::type divergence_iter,\n                   FastFlow::MaxIts::type max_its)\n    : alpha_(alpha),\n      beta_(beta),\n      abs_tol_(abs_tol),\n      trunc_tol_(trunc_tol),\n      divergence_tol_(divergence_tol),\n      divergence_iter_(divergence_iter),\n      max_its_(max_its),\n      flow_(flow),\n      current_iter_(0),\n      previous_residual_mesh_norm_(0.0),\n      min_residual_mesh_norm_(std::numeric_limits<double>::max()),\n      iter_at_min_residual_mesh_norm_(0) {}\n\ntemplate <typename Frame>\nsize_t FastFlow::current_l_mesh(\n    const ylm::Strahlkorper<Frame>& strahlkorper) const {\n  const size_t l_max = strahlkorper.ylm_spherepack().l_max();\n  // This is the formula used in SpEC (if l_max>=4). We may want to make this\n  // formula an option in the future, if we want to experiment with it.\n  return static_cast<size_t>(std::floor(1.5 * l_max));\n}\n\nnamespace {\ntemplate <typename Frame>\nDataVector fast_flow_weight(\n    const Scalar<DataVector>& one_form_magnitude,\n    const tnsr::i<DataVector, 3, Frame>& r_hat, const DataVector& radius,\n    const tnsr::II<DataVector, 3, Frame>& inverse_surface_metric) {\n  // Form Euclidean surface metric\n  auto flat_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame>>(radius, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    flat_metric.get(i, i) = 1.0;\n    for (size_t j = i; j < 3; ++j) {\n      flat_metric.get(i, j) -= r_hat.get(i) * r_hat.get(j);\n    }\n  }\n\n  auto denominator = make_with_value<DataVector>(radius, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      denominator += inverse_surface_metric.get(i, j) * flat_metric.get(i, j);\n    }\n  }\n\n  return 2.0 * get(one_form_magnitude) * square(radius) / denominator;\n}\n}  // namespace\n\ntemplate <typename Frame>\nstd::pair<FastFlow::Status, FastFlow::IterInfo>\nFastFlow::iterate_horizon_finder(\n    const gsl::not_null<ylm::Strahlkorper<Frame>*> current_strahlkorper,\n    const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::Ijj<DataVector, 3, Frame>& christoffel_2nd_kind) {\n  const size_t l_surface = current_strahlkorper->l_max();\n  const size_t l_mesh = current_l_mesh(*current_strahlkorper);\n\n  // Evaluate the Strahlkorper on a higher resolution mesh\n  const ylm::Strahlkorper<Frame> strahlkorper(l_mesh, l_mesh,\n                                              *current_strahlkorper);\n\n  // Make a DataBox with this strahlkorper.\n  // Do we want to define ComputeItems for expansion, normalized\n  // unit norms, etc in this DataBox? So far we do not.\n  auto box =\n      db::create<db::AddSimpleTags<ylm::Tags::items_tags<Frame>>,\n                 db::AddComputeTags<ylm::Tags::compute_items_tags<Frame>>>(\n          strahlkorper);\n\n  // Get minimum radius.\n  const auto& radius = db::get<ylm::Tags::Radius<Frame>>(box);\n  const auto r_minmax =\n      std::minmax_element(get(radius).begin(), get(radius).end());\n  const auto r_min = *r_minmax.first;\n  const auto r_max = *r_minmax.second;\n\n  if (r_min < 0.0) {\n    return std::make_pair(Status::NegativeRadius,\n                          IterInfo{current_iter_, r_min, r_max});\n  }\n\n  // Evaluate the current residual on the surface\n  const auto one_form_magnitude = magnitude(\n      db::get<ylm::Tags::NormalOneForm<Frame>>(box), upper_spatial_metric);\n  const DataVector one_over_one_form_magnitude = 1.0 / get(one_form_magnitude);\n  const auto unit_normal_one_form = gr::surfaces::unit_normal_one_form(\n      db::get<ylm::Tags::NormalOneForm<Frame>>(box),\n      one_over_one_form_magnitude);\n  const auto inverse_surface_metric = gr::surfaces::inverse_surface_metric(\n      raise_or_lower_index(unit_normal_one_form, upper_spatial_metric),\n      upper_spatial_metric);\n\n  const auto surface_residual = gr::surfaces::expansion(\n      gr::surfaces::grad_unit_normal_one_form(\n          db::get<ylm::Tags::Rhat<Frame>>(box),\n          db::get<ylm::Tags::Radius<Frame>>(box), unit_normal_one_form,\n          db::get<ylm::Tags::D2xRadius<Frame>>(box),\n          one_over_one_form_magnitude, christoffel_2nd_kind),\n      inverse_surface_metric, extrinsic_curvature);\n\n  auto weighted_residual = get(surface_residual);\n  switch (flow_) {\n    case FlowType::Jacobi:\n      // Do nothing\n      break;\n    case FlowType::Curvature: {\n      weighted_residual *= get(one_form_magnitude);\n    } break;\n    case FlowType::Fast: {\n      weighted_residual *= fast_flow_weight<Frame>(\n          one_form_magnitude, db::get<ylm::Tags::Rhat<Frame>>(box),\n          get(db::get<ylm::Tags::Radius<Frame>>(box)), inverse_surface_metric);\n    } break;\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Cannot find the specified value of FlowType, need to add a case?\");\n      // LCOV_EXCL_STOP\n  }\n\n  // Norm of the residual on the surface of size l_mesh.\n  // Note: In SpEC, this norm is computed as a pointwise L2 norm.  But\n  // here we compute the L2 integral norm.  The integral should be\n  // more accurate, but if it turns out that this integral is\n  // expensive, we can switch back to the pointwise L2 norm.\n  const double residual_mesh_norm = sqrt(strahlkorper.ylm_spherepack().average(\n      strahlkorper.ylm_spherepack().phys_to_spec(square(weighted_residual))));\n\n  if (residual_mesh_norm < min_residual_mesh_norm_) {\n    min_residual_mesh_norm_ = residual_mesh_norm;\n    iter_at_min_residual_mesh_norm_ = current_iter_;\n  }\n\n  // Transform the weighted residual\n  const auto weighted_residual_coefs =\n      strahlkorper.ylm_spherepack().phys_to_spec(weighted_residual);\n\n  // Restrict to the basis of the surface\n  const auto residual_on_surface =\n      strahlkorper.ylm_spherepack().prolong_or_restrict(\n          weighted_residual_coefs, current_strahlkorper->ylm_spherepack());\n\n  // Evaluate the norm of the residual on the surface of size l_surface.\n  // See comment on pointwise norm vs integral norm above.\n  const auto residual_ylm_norm =\n      sqrt(current_strahlkorper->ylm_spherepack().average(\n          current_strahlkorper->ylm_spherepack().phys_to_spec(\n              square(current_strahlkorper->ylm_spherepack().spec_to_phys(\n                  residual_on_surface)))));\n\n  // Fill iter_info\n  const auto minmax_residual =\n      std::minmax_element(weighted_residual.begin(), weighted_residual.end());\n  IterInfo iter_info{current_iter_,\n                     r_min,\n                     r_max,\n                     *minmax_residual.first,\n                     *minmax_residual.second,\n                     residual_ylm_norm,\n                     residual_mesh_norm};\n\n  // Exit if converged.\n  // What should happen is that as iterations proceed,\n  // residual_mesh_norm approaches a constant (the truncation error), and\n  // residual_ylm_norm decreases to roundoff.\n  //\n  // The first convergence condition is just\n  // residual_ylm_norm<abs_tol_.  The second convergence condition has\n  // two parts: First, we require that residual_ylm_norm <\n  // trunc_tol_*residual_mesh_norm.  But this may happen because\n  // residual_mesh_norm grows quickly, rather than because\n  // residual_ylm_norm shrinks.  So we require also that\n  // residual_mesh_norm-previous_residual_mesh_norm_ is small; that\n  // is, that residual_mesh_norm is converging to something.  But we\n  // can't require that\n  // residual_mesh_norm-previous_residual_mesh_norm_ is small on the\n  // first step, since previous_residual_mesh_norm_ is not defined, so\n  // we skip this part of the check on the first iteration.\n  if (residual_ylm_norm < abs_tol_) {\n    // clang-tidy: std::move of trivially-copyable type\n    return std::make_pair(Status::AbsTol, std::move(iter_info));  // NOLINT\n  } else if (residual_ylm_norm < trunc_tol_ * residual_mesh_norm) {\n    // This may be convergence by TruncationTol, but first make sure\n    // that either residual_mesh_norm is converging, or that it is the\n    // first step (i.e. previous_residual_mesh_norm_ == 0) so that we\n    // don't know yet if residual_mesh_norm is converging.\n    if (previous_residual_mesh_norm_ == 0 or\n        equal_within_roundoff(residual_mesh_norm, previous_residual_mesh_norm_,\n                              divergence_tol_ - 1.0, 0.0)) {\n      // clang-tidy: std::move of trivially-copyable type\n      return std::make_pair(Status::TruncationTol,\n                            std::move(iter_info));  // NOLINT\n    }\n  }\n\n  // Treat the case in which residual_mesh_norm is increasing\n  if (residual_mesh_norm > divergence_tol_ * min_residual_mesh_norm_ and\n      iter_at_min_residual_mesh_norm_ + divergence_iter_ <= current_iter_) {\n    // clang-tidy: std::move of trivially-copyable type\n    return std::make_pair(Status::DivergenceError,\n                          std::move(iter_info));  // NOLINT\n  }\n\n  if (current_iter_ == max_its_) {\n    // clang-tidy: std::move of trivially-copyable type\n    return std::make_pair(Status::MaxIts, std::move(iter_info));  // NOLINT\n  }\n\n  // We have succeeded in an iteration.  So return the next guess.\n  ++current_iter_;\n\n  // Construct new coefs.  Parameters flow_A and flow_B are from\n  // Gundlach, PRD 57, 863 (1998), eq. 44.\n  const double flow_A = alpha_ / (l_surface * (l_surface + 1)) + beta_;\n  const double flow_B = beta_ / alpha_;\n  auto coefs = current_strahlkorper->coefficients();\n  for (auto cit = ylm::SpherepackIterator(current_strahlkorper->l_max(),\n                                          current_strahlkorper->l_max());\n       cit; ++cit) {\n    coefs[cit()] -= flow_A /\n                    (1.0 + flow_B * static_cast<double>(cit.l()) *\n                               (static_cast<double>(cit.l()) + 1)) *\n                    residual_on_surface[cit()];\n  }\n  *current_strahlkorper =\n      ylm::Strahlkorper<Frame>(coefs, *current_strahlkorper);\n\n  // Set up for next iter\n  previous_residual_mesh_norm_ = residual_mesh_norm;\n\n  // clang-tidy: std::move of trivially-copyable type\n  return std::make_pair(Status::SuccessfulIteration,\n                        std::move(iter_info));  // NOLINT\n}\n\nstd::ostream& operator<<(std::ostream& os,\n                         const FastFlow::FlowType& flow_type) {\n  switch (flow_type) {\n    case FastFlow::FlowType::Jacobi:\n      return os << \"Jacobi\";\n    case FastFlow::FlowType::Curvature:\n      return os << \"Curvature\";\n    case FastFlow::FlowType::Fast:\n      return os << \"Fast\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Unknown FastFlow::FlowType\");\n      // LCOV_EXCL_STOP\n  }\n}\n\nvoid FastFlow::pup(PUP::er& p) {\n  p | alpha_;\n  p | beta_;\n  p | abs_tol_;\n  p | trunc_tol_;\n  p | divergence_tol_;\n  p | divergence_iter_;\n  p | max_its_;\n  p | flow_;\n  p | current_iter_;\n  p | previous_residual_mesh_norm_;\n  p | min_residual_mesh_norm_;\n  p | iter_at_min_residual_mesh_norm_;\n}\n\nstd::ostream& operator<<(std::ostream& os, const FastFlow::Status& status) {\n  switch (status) {\n    case FastFlow::Status::SuccessfulIteration:\n      return os << \"Still iterating\";\n    case FastFlow::Status::AbsTol:\n      return os << \"Converged: Absolute tolerance\";\n    case FastFlow::Status::TruncationTol:\n      return os << \"Converged: Truncation tolerance\";\n    case FastFlow::Status::MaxIts:\n      return os << \"Failed: Too many iterations\";\n    case FastFlow::Status::NegativeRadius:\n      return os << \"Failed: Negative radius\";\n    case FastFlow::Status::DivergenceError:\n      return os << \"Failed: Diverging\";\n    case FastFlow::Status::InterpolationFailure:\n      return os << \"Failed: Cannot interpolate onto surface\";\n    default:  // LCOV_EXCL_LINE\n      // LCOV_EXCL_START\n      ERROR(\"Need to add another case, don't understand value of 'status'\");\n      // LCOV_EXCL_STOP\n  }\n}\n\nbool operator==(const FastFlow& lhs, const FastFlow& rhs) {\n  return lhs.alpha_ == rhs.alpha_ and lhs.beta_ == rhs.beta_ and\n         lhs.abs_tol_ == rhs.abs_tol_ and lhs.trunc_tol_ == rhs.trunc_tol_ and\n         lhs.divergence_tol_ == rhs.divergence_tol_ and\n         lhs.divergence_iter_ == rhs.divergence_iter_ and\n         lhs.max_its_ == rhs.max_its_ and lhs.flow_ == rhs.flow_ and\n         lhs.current_iter_ == rhs.current_iter_ and\n         lhs.previous_residual_mesh_norm_ ==\n             rhs.previous_residual_mesh_norm_ and\n         lhs.min_residual_mesh_norm_ == rhs.min_residual_mesh_norm_ and\n         lhs.iter_at_min_residual_mesh_norm_ ==\n             rhs.iter_at_min_residual_mesh_norm_;\n}\n\ntemplate <>\nFastFlow::FlowType Options::create_from_yaml<FastFlow::FlowType>::create<void>(\n    const Options::Option& options) {\n  const auto flow_type_read = options.parse_as<std::string>();\n  if (\"Jacobi\" == flow_type_read) {\n    return FastFlow::FlowType::Jacobi;\n  } else if (\"Curvature\" == flow_type_read) {\n    return FastFlow::FlowType::Curvature;\n  } else if (\"Fast\" == flow_type_read) {\n    return FastFlow::FlowType::Fast;\n  }\n  PARSE_ERROR(options.context(), \"Failed to convert \\\"\"\n                                     << flow_type_read\n                                     << \"\\\" to FastFlow::FlowType. Must be \"\n                                        \"one of Jacobi, Curvature, Fast.\");\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                            \\\n  template size_t FastFlow::current_l_mesh(                             \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper) const;        \\\n  template std::pair<FastFlow::Status, FastFlow::IterInfo>              \\\n  FastFlow::iterate_horizon_finder<FRAME(data)>(                        \\\n      const gsl::not_null<ylm::Strahlkorper<FRAME(data)>*>              \\\n          current_strahlkorper,                                         \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& upper_spatial_metric, \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature,  \\\n      const tnsr::Ijj<DataVector, 3, FRAME(data)>& christoffel_2nd_kind);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <ostream>\n#include <utility>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\nnamespace ylm {\ntemplate <typename>\nclass Strahlkorper;\n}  // namespace ylm\n/// \\endcond\n\n/// \\ingroup SurfacesGroup\n/// \\brief Fast flow method for finding apparent horizons.\n///\n/// \\details Based on \\cite Gundlach1997us.\n//  The method is iterative.\nclass FastFlow {\n public:\n  enum class FlowType { Jacobi, Curvature, Fast };\n\n  // Error conditions are negative, successes are nonnegative\n  enum class Status {\n    // SuccessfulIteration means we should iterate again.\n    SuccessfulIteration = 0,\n    // The following indicate convergence, can stop iterating.\n    AbsTol = 1,\n    TruncationTol = 2,\n    // The following indicate errors.\n    MaxIts = -1,\n    NegativeRadius = -2,\n    DivergenceError = -3,\n    InterpolationFailure = -4\n  };\n\n  /// Holds information about an iteration of the algorithm.\n  struct IterInfo {\n    size_t iteration{std::numeric_limits<size_t>::max()};\n    double r_min{std::numeric_limits<double>::signaling_NaN()},\n        r_max{std::numeric_limits<double>::signaling_NaN()},\n        min_residual{std::numeric_limits<double>::signaling_NaN()},\n        max_residual{std::numeric_limits<double>::signaling_NaN()},\n        residual_ylm{std::numeric_limits<double>::signaling_NaN()},\n        residual_mesh{std::numeric_limits<double>::signaling_NaN()};\n  };\n\n  struct Flow {\n    using type = FlowType;\n    static constexpr Options::String help = {\n        \"Flow method: Jacobi, Curvature, or Fast\"};\n    static type suggested_value() { return FlowType::Fast; }\n  };\n\n  struct Alpha {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Alpha parameter in PRD 57, 863 (1998)\"};\n    static type suggested_value() { return 1.0; }\n  };\n\n  struct Beta {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Beta parameter in PRD 57, 863 (1998)\"};\n    static type suggested_value() { return 0.5; }\n  };\n\n  struct AbsTol {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Convergence found if R_{Y_lm} < AbsTol\"};\n    static type suggested_value() { return 1.e-12; }\n  };\n\n  struct TruncationTol {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Convergence found if R_{Y_lm} < TruncationTol*R_{mesh}\"};\n    static type suggested_value() { return 1.e-2; }\n  };\n\n  struct DivergenceTol {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fraction that residual can increase before dying\"};\n    static type suggested_value() { return 1.2; }\n    static type lower_bound() { return 1.0; }\n  };\n\n  struct DivergenceIter {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Num iterations residual can increase before dying\"};\n    static type suggested_value() { return 5; }\n  };\n\n  struct MaxIts {\n    using type = size_t;\n    static constexpr Options::String help = {\"Maximum number of iterations.\"};\n    static type suggested_value() { return 100; }\n  };\n\n  using options = tmpl::list<Flow, Alpha, Beta, AbsTol, TruncationTol,\n                             DivergenceTol, DivergenceIter, MaxIts>;\n\n  static constexpr Options::String help{\n      \"Find a Strahlkorper using a 'fast flow' method.\\n\"\n      \"Based on Gundlach, PRD 57, 863 (1998).\\n\"\n      \"Expands the surface in terms of spherical harmonics Y_lm up to a given\\n\"\n      \"l_surface, and varies the coefficients S_lm where 0<=l<=l_surface to\\n\"\n      \"minimize the residual of the apparent horizon equation.  Also keeps\\n\"\n      \"another representation of the surface that is expanded up to\\n\"\n      \"l_mesh > l_surface.  Let R_{Y_lm} be the residual computed using the\\n\"\n      \"surface represented up to l_surface; this residual can in principle be\\n\"\n      \"lowered to machine roundoff by enough iterations. Let R_{mesh} be the\\n\"\n      \"residual computed using the surface represented up to l_mesh; this\\n\"\n      \"residual represents the truncation error, since l_mesh>l_surface and\\n\"\n      \"since coefficients S_lm with l>l_surface are not modified in the\\n\"\n      \"iteration.\\n\\n\"\n      \"Convergence is achieved if R_{Y_lm}< TruncationTol*R_{mesh}, or if\\n\"\n      \"R_{Y_lm}<AbsTol, where TruncationTol and AbsTol are input parameters.\\n\"\n      \"If instead |R_{mesh}|_i > DivergenceTol * min_{j}(|R_{mesh}|_j) where\\n\"\n      \"i is the iteration index and j runs from 0 to i-DivergenceIter, then\\n\"\n      \"FastFlow exits with Status::DivergenceError.  Here DivergenceIter and\\n\"\n      \"DivergenceTol are input parameters.\"};\n\n  FastFlow(Flow::type flow, Alpha::type alpha, Beta::type beta,\n           AbsTol::type abs_tol, TruncationTol::type trunc_tol,\n           DivergenceTol::type divergence_tol,\n           DivergenceIter::type divergence_iter, MaxIts::type max_its);\n\n  FastFlow() : FastFlow(FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5, 100) {}\n\n  FastFlow(const FastFlow& /*rhs*/) = default;\n  FastFlow& operator=(const FastFlow& /*rhs*/) = default;\n  FastFlow(FastFlow&& /*rhs*/) = default;\n  FastFlow& operator=(FastFlow&& /*rhs*/) = default;\n  ~FastFlow() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// Evaluate residuals and compute the next iteration.  If\n  /// Status==SuccessfulIteration, then `current_strahlkorper` is\n  /// modified and `current_iteration()` is incremented.  Otherwise, we\n  /// end with success or failure, and neither `current_strahlkorper`\n  /// nor `current_iteration()` is changed.\n  template <typename Frame>\n  std::pair<Status, IterInfo> iterate_horizon_finder(\n      gsl::not_null<ylm::Strahlkorper<Frame>*> current_strahlkorper,\n      const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric,\n      const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n      const tnsr::Ijj<DataVector, 3, Frame>& christoffel_2nd_kind);\n\n  size_t current_iteration() const { return current_iter_; }\n\n  /// Given a Strahlkorper defined up to some maximum Y_lm l called\n  /// l_surface, returns a larger value of l, l_mesh, that is used for\n  /// evaluating convergence.\n  template <typename Frame>\n  size_t current_l_mesh(const ylm::Strahlkorper<Frame>& strahlkorper) const;\n\n  /// Resets the finder.\n  SPECTRE_ALWAYS_INLINE void reset_for_next_find() {\n    current_iter_ = 0;\n    previous_residual_mesh_norm_ = 0.0;\n    min_residual_mesh_norm_ = std::numeric_limits<double>::max();\n    iter_at_min_residual_mesh_norm_ = 0;\n  }\n\n private:\n  friend bool operator==(const FastFlow& /*lhs*/, const FastFlow& /*rhs*/);\n  double alpha_, beta_, abs_tol_, trunc_tol_, divergence_tol_;\n  size_t divergence_iter_, max_its_;\n  FlowType flow_;\n  size_t current_iter_;\n  double previous_residual_mesh_norm_, min_residual_mesh_norm_;\n  size_t iter_at_min_residual_mesh_norm_;\n};\n\nSPECTRE_ALWAYS_INLINE bool converged(const FastFlow::Status& status) {\n  return static_cast<int>(status) > 0;\n}\n\nstd::ostream& operator<<(std::ostream& os, const FastFlow::FlowType& flow_type);\n\nstd::ostream& operator<<(std::ostream& os, const FastFlow::Status& status);\n\nSPECTRE_ALWAYS_INLINE bool operator!=(const FastFlow& lhs,\n                                      const FastFlow& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <>\nstruct Options::create_from_yaml<FastFlow::FlowType> {\n  template <typename Metavariables>\n  static FastFlow::FlowType create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nFastFlow::FlowType Options::create_from_yaml<FastFlow::FlowType>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/FindApparentHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/InvokeCallbacks.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/CleanUp.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeCoords.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/CurrentTime.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/InterpolateVolumeVars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah {\nnamespace detail {\ntemplate <typename Criterion>\nstruct get_tags {\n  using type = typename Criterion::compute_tags_for_observation_box;\n};\n}  // namespace detail\n\n/*!\n * \\brief Simple action run on the horizon finder by the Elements which receives\n * volume data, finds the apparent horizon, and calls the callbacks after the\n * horizon is found.\n *\n * \\details First, the volume variables `ah::vars_to_interpolate_to_target` are\n * put into the `ah::Tags::Storage`. Then, we try to set the\n * `ah::Tags::CurrentTime` with the `ah::set_current_time` function. Once we\n * have a current time, we ensure the functions of time are up-to-date at this\n * current time with `ah::check_if_current_time_is_ready`. If they are not,\n * we re-trigger this action with `vars_have_already_been_received` set to\n * true and not sending any new volume data.\n *\n * Once we ensure the functions of time are ready, we compute the cartesian\n * coordinates for the current fast-flow iteration surface with\n * `ah::set_current_iteration_coords`. Once we have the coords, we interpolate\n * the volume date onto these coordinates. After that, we do one iteration of\n * the `FastFlow` algorithm to get a new surface. If we haven't converged yet,\n * we compute new cartesian and start another iteration. Once we have converged,\n * we call the callbacks with `ah::invoke_callbacks` and then clean up the\n * horizon finer with `ah::clean_up_horizon_finder`. Then we try to set a new\n * current time and the process starts over again. If `FastFlow` never\n * converges, we call the `horizon_find_failure_callbacks` from the\n * \\p HorizonMetavars.\n */\ntemplate <typename HorizonMetavars>\nstruct FindApparentHorizon {\n  using frame = typename HorizonMetavars::frame;\n\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const LinkedMessageId<double>& incoming_time,\n                    const ElementId<3>& incoming_element_id,\n                    const ::Mesh<3>& incoming_mesh,\n                    Variables<ah::vars_to_interpolate_to_target<3, frame>>&&\n                        incoming_vars_to_interpolate,\n                    const std::optional<std::string>& dependency,\n                    const bool vars_have_already_been_received = false) {\n    const auto& verbosity = db::get<ah::Tags::Verbosity>(box);\n    const bool quiet_print = verbosity >= ::Verbosity::Quiet;\n    const bool verbose_print = verbosity >= ::Verbosity::Verbose;\n    const bool debug_print = verbosity >= ::Verbosity::Debug;\n    const std::string& name = pretty_type::name<HorizonMetavars>();\n\n    // If we've already completed this horizon find, ignore the volume data and\n    // just return\n    auto& completed_times = db::get_mutable_reference<ah::Tags::CompletedTimes>(\n        make_not_null(&box));\n    if (completed_times.contains(incoming_time)) {\n      if (debug_print) {\n        Parallel::printf(\n            \"%s: Received volume data at finished time %s from Element %s. \"\n            \"Ignoring.\\n\",\n            name, incoming_time, incoming_element_id);\n      }\n      return;\n    }\n\n    auto& current_time_optional =\n        db::get_mutable_reference<ah::Tags::CurrentTime>(make_not_null(&box));\n    auto& pending_times =\n        db::get_mutable_reference<ah::Tags::PendingTimes>(make_not_null(&box));\n\n    // Only add to pending if it isn't the current time.\n    if (not(current_time_optional.has_value() and\n            current_time_optional.value() == incoming_time)) {\n      pending_times.insert(incoming_time);\n    }\n\n    auto& all_storage = db::get_mutable_reference<ah::Tags::Storage<frame>>(\n        make_not_null(&box));\n    auto& block_search_order =\n        db::get_mutable_reference<ah::Tags::BlockSearchOrder>(\n            make_not_null(&box));\n\n    // Add volume variables and destination to the box if they haven't already\n    // been received\n    if (not vars_have_already_been_received) {\n      auto& current_time_storage = all_storage[incoming_time];\n      current_time_storage.all_volume_variables.emplace(\n          incoming_element_id,\n          ah::Storage::VolumeVariables<frame>{\n              incoming_mesh, std::move(incoming_vars_to_interpolate)});\n      current_time_storage.destination = HorizonMetavars::destination;\n    }\n\n    // ========================================================================\n    // The incoming vars are moved and used in the above code. Below, we\n    // only use quantities at the current time, except for forwarding the\n    // incoming quantities to a callback for checking if the functions of time\n    // are ready.\n    // ========================================================================\n\n    // Keep trying to find horizons for as long as we can.\n    // In the first pass, interpolate only from the incoming element because\n    // this was missing data. On subsequent passes, interpolate from all\n    // elements. Notes:\n    // - The `vars_have_already_been_received` flag is used only in\n    //   `check_if_current_time_is_ready` to re-trigger the AH find in a\n    //   callback but not sending any new data.\n    // - If the incoming time is not the current time (but later), then we have\n    //   to do the full interpolation for the current time first before moving\n    //   on to the incoming time. In this case we also need to do the full\n    //   interpolation for the incoming time once we get there.\n    bool interpolate_only_from_incoming_element =\n        current_time_optional == incoming_time and\n        not vars_have_already_been_received;\n    while (true) {\n      // Set current time using the pending times if it isn't already set\n      set_current_time(make_not_null(&current_time_optional),\n                       make_not_null(&pending_times), completed_times,\n                       all_storage, verbosity, name);\n\n      // At this point, if we don't have a current id we can't do anything so\n      // just exit.\n      if (not current_time_optional.has_value()) {\n        if (verbose_print) {\n          Parallel::printf(\n              \"%s: No current time set. Stopping here. Incoming time: %s\\n\",\n              name, incoming_time);\n        }\n        return;\n      }\n\n      const LinkedMessageId<double>& current_time =\n          current_time_optional.value();\n      ASSERT(all_storage.contains(current_time),\n             \"Current time doesn't have any data.\");\n\n      auto& current_time_storage = all_storage.at(current_time);\n\n      // Only check if the current time is ready once\n      if (UNLIKELY(not current_time_storage.time_is_ready)) {\n        if (check_if_current_time_is_ready<HorizonMetavars>(\n                current_time, cache, incoming_time, incoming_element_id,\n                incoming_mesh, dependency)) {\n          // Set this so we don't check it again\n          current_time_storage.time_is_ready = true;\n\n          if (verbose_print) {\n            Parallel::printf(\"%s: Current time %s is ready.\\n\", name,\n                             current_time);\n          }\n        } else {\n          if (verbose_print) {\n            Parallel::printf(\"%s: Current time %s is NOT ready.\\n\", name,\n                             current_time);\n          }\n          // We need to wait for the functions of time to be updated so we can't\n          // do anything yet.\n          return;\n        }\n      }\n\n      const auto& domain = Parallel::get<domain::Tags::Domain<3>>(cache);\n      const auto& functions_of_time =\n          Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n      const auto& options =\n          Parallel::get<ah::Tags::ApparentHorizonOptions<HorizonMetavars>>(\n              cache);\n      auto& fast_flow =\n          db::get_mutable_reference<ah::Tags::FastFlow>(make_not_null(&box));\n\n      auto& all_volume_variables = current_time_storage.all_volume_variables;\n      auto& current_iteration_storage = current_time_storage.current_iteration;\n      auto& previous_iteration_surface =\n          current_time_storage.previous_iteration_surface;\n      auto& element_order = current_time_storage.element_order;\n      auto& previous_surfaces =\n          db::get_mutable_reference<ah::Tags::PreviousSurfaces<frame>>(\n              make_not_null(&box));\n\n      auto& current_resolution_l =\n          db::get_mutable_reference<ah::Tags::CurrentResolutionL>(\n              make_not_null(&box));\n      // Set current resolution to the initial guess resolution if it is not\n      // yet specified.\n      if (UNLIKELY(not current_resolution_l.has_value())) {\n        ASSERT(fast_flow.current_iteration() == 0,\n               \"Current resolution L is not set, but fast flow iteration > 0. \"\n               \"Iteration 0 should set the current resolution L.\");\n        ASSERT(previous_surfaces.empty(),\n               \"Current resolution L is not set, but previous horizons have \"\n               \"been found. Iteration 0 of each horizon find should set the \"\n               \"current resolution L.\");\n        current_resolution_l = options.initial_guess.l_max();\n      }\n\n      // Keep finding the horizon until it's found with sufficient resolution\n      bool rerunning_with_higher_resolution = false;\n      while (true) {\n        std::pair<FastFlow::Status, FastFlow::IterInfo> status_and_info;\n\n        // Keep doing fast flow iterations for as long as we can until we find a\n        // horizon\n        while (true) {\n          // Points haven't been set for this iteration so need to do so now\n          if (not current_iteration_storage.block_coord_holders.has_value() or\n              rerunning_with_higher_resolution) {\n            const bool coords_set_successfully = set_current_iteration_coords(\n                make_not_null(&current_iteration_storage),\n                make_not_null(&block_search_order), current_time, fast_flow,\n                options.initial_guess, previous_iteration_surface,\n                previous_surfaces, options.max_compute_coords_retries, domain,\n                functions_of_time, current_resolution_l,\n                rerunning_with_higher_resolution);\n\n            // Some points are outside the domain and we cannot recover\n            if (not coords_set_successfully) {\n              // This will possibly throw an error, depending on which\n              // callback is used in\n              // HorizonMetavars::horizon_find_failure_callbacks\n              // (ErrorOnFailedApparentHorizon or IgnoreFailedApparentHorizon)\n              tmpl::for_each<\n                  typename HorizonMetavars::horizon_find_failure_callbacks>(\n                  [&]<typename Callback>(tmpl::type_<Callback> /*meta*/) {\n                    Callback::apply(box, cache,\n                                    FastFlow::Status::InterpolationFailure);\n                  });\n\n              // Still clean up in case we didn't error so we don't keep\n              // accepting volume data for this time\n              clean_up_horizon_finder(make_not_null(&current_time_optional),\n                                      make_not_null(&all_storage),\n                                      make_not_null(&completed_times),\n                                      make_not_null(&fast_flow));\n\n              // If we didn't error, move on to the next horizon find\n              break;\n            }\n\n            if (debug_print) {\n              Parallel::printf(\n                  \"%s: For current time %s, at iteration %zu, coords have been \"\n                  \"set.\\n\",\n                  name, current_time, fast_flow.current_iteration());\n            }\n          }\n\n          // Interpolate to the current iteration points\n          bool interpolation_is_complete = false;\n          if (interpolate_only_from_incoming_element) {\n            // Data is coming in one element at a time, so try to interpolate\n            // only from the incoming element\n            ASSERT(all_volume_variables.contains(incoming_element_id),\n                   \"Trying to interpolate incoming data from element \"\n                       << incoming_element_id\n                       << \" but it's not available. This is a bug.\");\n            const bool interpolated_any_points = interpolate_volume_data(\n                make_not_null(&current_iteration_storage),\n                all_volume_variables.at(incoming_element_id),\n                incoming_element_id);\n            // Store which elements we found points in for next iteration\n            if (interpolated_any_points) {\n              element_order.push_back(incoming_element_id);\n            }\n            interpolation_is_complete =\n                current_iteration_storage.interpolation_is_complete();\n          } else {\n            // We already have some element data, so go through the elements in\n            // the order we found points in before\n            for (const auto& element_id : element_order) {\n              ASSERT(all_volume_variables.contains(element_id),\n                     \"Trying to interpolate data from element \"\n                         << element_id\n                         << \" that was found in a previous FastFlow iteration, \"\n                            \"but it's not available. This is a bug.\");\n              interpolate_volume_data(make_not_null(&current_iteration_storage),\n                                      all_volume_variables.at(element_id),\n                                      element_id);\n              interpolation_is_complete =\n                  current_iteration_storage.interpolation_is_complete();\n              if (interpolation_is_complete) {\n                break;\n              }\n            }\n            // If we still need more points, try going through all the elements\n            // that we have received data from so far\n            if (not interpolation_is_complete) {\n              for (const auto& [element_id, volume_vars_storage] :\n                   all_volume_variables) {\n                if (std::find(element_order.begin(), element_order.end(),\n                              element_id) != element_order.end()) {\n                  // Already tried this element above\n                  continue;\n                }\n                const bool interpolated_any_points = interpolate_volume_data(\n                    make_not_null(&current_iteration_storage),\n                    volume_vars_storage, element_id);\n                if (interpolated_any_points) {\n                  element_order.push_back(element_id);\n                }\n                interpolation_is_complete =\n                    current_iteration_storage.interpolation_is_complete();\n                if (interpolation_is_complete) {\n                  break;\n                }\n              }\n            }\n          }\n\n          if (interpolation_is_complete) {\n            // Next iteration try to interpolate from all elements\n            interpolate_only_from_incoming_element = false;\n          } else {\n            // We need more points, return and wait for more volume data.\n            // When this action is called again, we'll try to interpolate only\n            // from the incoming element until we have enough data.\n            if (debug_print) {\n              Parallel::printf(\n                  \"%s: For current time %s, at iteration %zu, need more volume \"\n                  \"data.\\n\",\n                  name, current_time, fast_flow.current_iteration());\n            }\n            return;\n          }\n\n          // Set this before we iterate the fast flow.\n          previous_iteration_surface = current_iteration_storage.strahlkorper;\n\n          // Do a single fast flow iteration.\n          {\n            using InverseSpatialMetric =\n                gr::Tags::InverseSpatialMetric<DataVector, 3, frame>;\n            using ExtrinsicCurvature =\n                gr::Tags::ExtrinsicCurvature<DataVector, 3, frame>;\n            using SpatialChristoffel =\n                gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, frame>;\n\n            auto& interpolated_vars =\n                current_iteration_storage.interpolated_vars;\n            status_and_info = fast_flow.iterate_horizon_finder(\n                make_not_null(&current_iteration_storage.strahlkorper),\n                get<InverseSpatialMetric>(interpolated_vars),\n                get<ExtrinsicCurvature>(interpolated_vars),\n                get<SpatialChristoffel>(interpolated_vars));\n          }\n\n          const auto& status = status_and_info.first;\n          const auto& info = status_and_info.second;\n          const auto has_converged = converged(status);\n\n          if (verbose_print or (quiet_print and has_converged)) {\n            Parallel::printf(\n                \"%s: t=%.6g: L=%zu: its=%zu: %.1e<R<%.0e, |R|=%.1g, \"\n                \"|R_grid|=%.1g, %.4g<r<%.4g\\n\",\n                name, current_time.id,\n                all_storage.at(current_time)\n                    .current_iteration.strahlkorper.l_max(),\n                info.iteration, info.min_residual, info.max_residual,\n                info.residual_ylm, info.residual_mesh, info.r_min, info.r_max);\n          }\n\n          if (status == FastFlow::Status::SuccessfulIteration) {\n            // Reset so we compute and interpolate to new points\n            current_iteration_storage.reset_for_next_iteration();\n\n            // Continue in the iteration while loop\n            continue;\n          } else if (not has_converged) {\n            // This will possibly throw an error\n            tmpl::for_each<\n                typename HorizonMetavars::horizon_find_failure_callbacks>(\n                [&](auto callback_v) {\n                  using callback = tmpl::type_from<decltype(callback_v)>;\n                  callback::apply(box, cache, status);\n                });\n\n            // Still clean up in case we didn't error so we don't keep accepting\n            // volume data for this time\n            clean_up_horizon_finder(make_not_null(&current_time_optional),\n                                    make_not_null(&all_storage),\n                                    make_not_null(&completed_times),\n                                    make_not_null(&fast_flow));\n\n            // If we didn't error, move on to the next horizon find\n            break;\n          }\n\n          // Break out of the iteration while loop. Go back into the\n          // while loop for sufficient resolution\n          break;\n        }  // while (true) [iterations]\n        if (converged(status_and_info.first)) {\n          std::vector<size_t> recommended_resolutions{};\n          const auto& criteria = options.criteria;\n\n          // Create an ObservationBox with the union of all compute tags needed\n          // by criteria\n          using compute_tags_for_obs_box =\n              tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n                  tmpl::at<\n                      typename Metavariables::factory_creation::factory_classes,\n                      ah::Criterion>,\n                  detail::get_tags<tmpl::_1>>>>;\n          const auto observation_box =\n              make_observation_box<compute_tags_for_obs_box>(\n                  make_not_null(&box));\n\n          for (const auto& criterion : criteria) {\n            recommended_resolutions.push_back(criterion->evaluate(\n                observation_box, cache,\n                current_time_storage.current_iteration.strahlkorper,\n                status_and_info.second));\n          }\n          const size_t next_resolution_l =\n              recommended_resolutions.empty()\n                  ? all_storage.at(current_time)\n                        .current_iteration.strahlkorper.l_max()\n                  : *std::max_element(recommended_resolutions.begin(),\n                                      recommended_resolutions.end());\n          if (next_resolution_l > *current_resolution_l) {\n            current_resolution_l = next_resolution_l;\n            rerunning_with_higher_resolution = true;\n\n            // Prepare for trying the find again with higher resolution:\n            // Reset fast flow, but don't \"clean up,\" which also erases the\n            // storage for the current time, resets the current time, and\n            // updates the completed times. None of those things happen yet,\n            // because the current time horizon finding is not yet finished.\n            // Also reset the current iteration storage, since the next find\n            // is effectively a new iteration.\n            fast_flow.reset_for_next_find();\n            current_time_storage.current_iteration.reset_for_next_iteration();\n\n            continue;\n          } else {\n            rerunning_with_higher_resolution = false;\n          }\n\n          // We have converged to the apparent horizon. Invoke the callbacks and\n          // clean up for the next horizon find\n          invoke_callbacks<HorizonMetavars>(make_not_null(&box), cache,\n                                            dependency, status_and_info.first);\n\n          if (debug_print) {\n            Parallel::printf(\n                \"%s: Horizon find finished at current time %s. Cleaning up and \"\n                \"moving to next time\\n\",\n                name, current_time);\n          }\n\n          current_resolution_l = next_resolution_l;\n          clean_up_horizon_finder(make_not_null(&current_time_optional),\n                                  make_not_null(&all_storage),\n                                  make_not_null(&completed_times),\n                                  make_not_null(&fast_flow));\n        }\n\n        // We have either i) converged with sufficient resolution, or\n        // ii) failed to converge but not thrown an error. In both cases,\n        // continue to the next time by breaking out of the resolution\n        // while loop, going back into the overall while loop for time.\n        break;\n      }  // while (true) [sufficient resolution]\n    }  // while (true) [time]\n  }\n};\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\nstruct DataVector;\n/// \\endcond\n\nnamespace ah {\ntemplate <size_t Dim>\nusing source_vars =\n    tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n               gh::Tags::Pi<DataVector, Dim>, gh::Tags::Phi<DataVector, Dim>,\n               ::Tags::deriv<gh::Tags::Phi<DataVector, Dim>, tmpl::size_t<Dim>,\n                             Frame::Inertial>>;\n\ntemplate <size_t Dim, typename Frame>\nusing vars_to_interpolate_to_target =\n    tmpl::list<gr::Tags::SpatialMetric<DataVector, Dim, Frame>,\n               gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>,\n               gr::Tags::ExtrinsicCurvature<DataVector, Dim, Frame>,\n               gr::Tags::SpatialChristoffelSecondKind<DataVector, Dim, Frame>,\n               gr::Tags::SpatialRicci<DataVector, Dim, Frame>>;\n\ntemplate <typename Frame>\nusing tags_for_observing =\n    tmpl::list<gr::surfaces::Tags::Area, gr::surfaces::Tags::IrreducibleMass,\n               ylm::Tags::MaxRicciScalar, ylm::Tags::MinRicciScalar,\n               gr::surfaces::Tags::ChristodoulouMass,\n               gr::surfaces::Tags::DimensionlessSpinMagnitude<Frame>,\n               gr::surfaces::Tags::DimensionfulSpinVector<Frame>>;\n\nusing surface_tags_for_observing = tmpl::list<ylm::Tags::RicciScalar>;\n\ntemplate <size_t Dim, typename Frame>\nusing compute_items_on_target = tmpl::list<\n    ylm::Tags::ThetaPhiCompute<Frame>, ylm::Tags::RadiusCompute<Frame>,\n    ylm::Tags::RhatCompute<Frame>, ylm::Tags::CartesianCoordsCompute<Frame>,\n    ylm::Tags::InvJacobianCompute<Frame>, ylm::Tags::InvHessianCompute<Frame>,\n    ylm::Tags::JacobianCompute<Frame>, ylm::Tags::DxRadiusCompute<Frame>,\n    ylm::Tags::D2xRadiusCompute<Frame>, ylm::Tags::NormalOneFormCompute<Frame>,\n    ylm::Tags::OneOverOneFormMagnitudeCompute<DataVector, Dim, Frame>,\n    ylm::Tags::TangentsCompute<Frame>,\n    ylm::Tags::UnitNormalOneFormCompute<Frame>,\n    ylm::Tags::UnitNormalVectorCompute<Frame>,\n    ylm::Tags::GradUnitNormalOneFormCompute<Frame>,\n    // Note that ylm::Tags::ExtrinsicCurvatureCompute is the\n    // 2d extrinsic curvature of the strahlkorper embedded in the 3d\n    // slice, whereas gr::tags::ExtrinsicCurvature is the 3d\n    // extrinsic curvature of the slice embedded in 4d spacetime.\n    // Both quantities are in the DataBox.\n    gr::surfaces::Tags::AreaElementCompute<Frame>,\n    ylm::Tags::EuclideanAreaElementCompute<Frame>,\n    ylm::Tags::ExtrinsicCurvatureCompute<Frame>,\n    ylm::Tags::RicciScalarCompute<Frame>,\n    gr::surfaces::Tags::SpinFunctionCompute<Frame>,\n    gr::surfaces::Tags::DimensionfulSpinMagnitudeCompute<Frame>,\n    gr::surfaces::Tags::AreaCompute<Frame>,\n    gr::surfaces::Tags::IrreducibleMassCompute<Frame>,\n    ylm::Tags::MaxRicciScalarCompute, ylm::Tags::MinRicciScalarCompute,\n    gr::surfaces::Tags::ChristodoulouMassCompute<Frame>,\n    gr::surfaces::Tags::DimensionlessSpinMagnitudeCompute<Frame>,\n    gr::surfaces::Tags::DimensionfulSpinVectorCompute<Frame, Frame>>;\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Initialization.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah {\n/*!\n * \\brief Initialize items related to the horizon finder\n *\n * GlobalCache:\n * - Uses:\n *   - `ah::Tags::ApparentHorizonOptions`\n *   - `ah::Tags::BlocksForHorizonFind`\n *   - `ah::Tags::LMax`\n *\n * DataBox:\n * - Uses: Nothing\n * - Adds:\n *   - `ah::Tags::Verbosity`\n *   - `ah::Tags::FastFlow`\n *   - `ah::Tags::CurrentTime`\n *   - `ah::Tags::PendingTimes`\n *   - `ah::Tags::CompletedTimes`\n *   - `ah::Tags::Storage`\n *   - `ah::Tags::PreviousSurfaces`\n *   - `ah::Tags::Strahlkorper`\n *   - `ah::Tags::TimeDerivStrahlkorper`\n *   - `ah::Tags::Dependency`\n *   - `::Tags::Variables<ah::vars_to_interpolate_to_target>`\n *   - `ah::Tags::CurrentResolutionL`\n * - Modifies:\n *   - `ah::Tags::Verbosity`\n *   - `ah::Tags::FastFlow`\n *   - `ah::Tags::CurrentTime`\n */\ntemplate <typename HorizonMetavars>\nstruct Initialize {\n  using Fr = typename HorizonMetavars::frame;\n\n  using simple_tags_from_options = tmpl::list<>;\n\n  using simple_tags = tmpl::append<\n      tmpl::list<Tags::CurrentResolutionL, Tags::Verbosity, Tags::FastFlow,\n                 Tags::CurrentTime, Tags::PendingTimes, Tags::CompletedTimes,\n                 Tags::Storage<Fr>, Tags::BlockSearchOrder,\n                 Tags::PreviousSurfaces<Fr>, ylm::Tags::Strahlkorper<Fr>,\n                 ylm::Tags::TimeDerivStrahlkorper<Fr>, ah::Tags::Dependency,\n                 ::Tags::Variables<ah::vars_to_interpolate_to_target<3, Fr>>>,\n      tmpl::conditional_t<\n          std::is_same_v<Fr, Frame::Inertial>, tmpl::list<>,\n          tmpl::list<ylm::Tags::CartesianCoords<Frame::Inertial>>>>;\n\n  using const_global_cache_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::append<\n          tmpl::list<Tags::ApparentHorizonOptions<HorizonMetavars>,\n                     Tags::BlocksForHorizonFind, Tags::LMax>,\n          Parallel::get_const_global_cache_tags_from_actions<tmpl::flatten<\n              tmpl::list<typename HorizonMetavars::horizon_find_callbacks,\n                         typename HorizonMetavars::\n                             horizon_find_failure_callbacks>>>>>>;\n\n  using mutable_global_cache_tags =\n      tmpl::list<Tags::PreviousSurface<HorizonMetavars>>;\n\n  using compute_tags = ah::compute_items_on_target<3, Fr>;\n\n  using return_tags = tmpl::list<Tags::CurrentResolutionL, Tags::Verbosity,\n                                 Tags::FastFlow, Tags::CurrentTime>;\n\n  using argument_tags =\n      tmpl::list<Tags::ApparentHorizonOptions<HorizonMetavars>>;\n\n  static void apply(\n      const gsl::not_null<std::optional<size_t>*> current_resolution_l,\n      const gsl::not_null<::Verbosity*> verbosity,\n      const gsl::not_null<::FastFlow*> fast_flow,\n      const gsl::not_null<std::optional<LinkedMessageId<double>>*> current_time,\n      const HorizonOptions<Fr>& options) {\n    (*verbosity) = options.verbosity;\n    (*fast_flow) = options.fast_flow;\n    current_time->reset();\n    current_resolution_l->reset();\n  }\n};\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/InterpolateVolumeVars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/InterpolateVolumeVars.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeVarsToInterpolateToTarget.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah {\ntemplate <typename Fr>\nbool interpolate_volume_data(\n    const gsl::not_null<ah::Storage::Iteration<Fr>*> current_iteration_storage,\n    const ah::Storage::VolumeVariables<Fr>& volume_vars_storage,\n    const ElementId<3>& element_id) {\n  ASSERT(current_iteration_storage->block_coord_holders.has_value(),\n         \"Block logical coordinates of horizon have not been set!\");\n\n  const auto& block_coord_holders =\n      current_iteration_storage->block_coord_holders.value();\n  auto& interpolated_vars = current_iteration_storage->interpolated_vars;\n  auto& indices_interpolated_to_thus_far =\n      current_iteration_storage->indices_interpolated_to_thus_far;\n  auto& offsets =\n      current_iteration_storage->offsets_of_newly_interpolated_points;\n  auto& x_element_logical =\n      current_iteration_storage->x_element_logical_of_newly_interpolated_points;\n\n  // Initialize interpolated_vars if needed and reserve memory\n  const size_t expected_num_points = block_coord_holders.size();\n  if (interpolated_vars.number_of_grid_points() != expected_num_points) {\n    interpolated_vars.initialize(expected_num_points);\n    offsets.reserve(expected_num_points);\n    for (size_t d = 0; d < 3; ++d) {\n      gsl::at(x_element_logical, d).reserve(expected_num_points);\n    }\n  }\n  if (indices_interpolated_to_thus_far.size() != expected_num_points) {\n    indices_interpolated_to_thus_far.resize(expected_num_points, false);\n  }\n\n  // Find points in this element\n  offsets.clear();\n  for (size_t d = 0; d < 3; ++d) {\n    gsl::at(x_element_logical, d).clear();\n  }\n  for (size_t p = 0; p < expected_num_points; ++p) {\n    if (indices_interpolated_to_thus_far[p]) {\n      // Skip points that have already been interpolated to\n      continue;\n    }\n    if (block_coord_holders[p]->id.get_index() != element_id.block_id()) {\n      // Skip points that are not in this block\n      continue;\n    }\n    const auto element_logical_coords =\n        element_logical_coordinates(block_coord_holders[p]->data, element_id);\n    if (not element_logical_coords.has_value()) {\n      // Skip points that are not in this element\n      continue;\n    }\n    // Collect points in this element\n    offsets.push_back(p);\n    for (size_t d = 0; d < 3; ++d) {\n      gsl::at(x_element_logical, d).push_back(element_logical_coords->get(d));\n    }\n  }  // for block_logical_coords\n\n  // Return early if no points are in this element\n  if (offsets.empty()) {\n    return false;\n  }\n\n  // Interpolate!\n  // Use non-owning wrappers around memory buffers to avoid allocations\n  tnsr::I<DataVector, 3, Frame::ElementLogical> element_logical_coords{};\n  for (size_t d = 0; d < 3; ++d) {\n    element_logical_coords.get(d).set_data_ref(\n        gsl::at(x_element_logical, d).data(),\n        gsl::at(x_element_logical, d).size());\n  }\n  const intrp::Irregular<3> interpolator(volume_vars_storage.mesh,\n                                         element_logical_coords);\n  auto& interpolated_vars_buffer =\n      current_iteration_storage->newly_interpolated_vars_buffer;\n  Variables<ah::vars_to_interpolate_to_target<3, Fr>> local_interpolated_vars{};\n  constexpr size_t num_components =\n      Variables<ah::vars_to_interpolate_to_target<3, Fr>>::\n          number_of_independent_components;\n  interpolated_vars_buffer.resize(offsets.size() * num_components);\n  local_interpolated_vars.set_data_ref(interpolated_vars_buffer.data(),\n                                       interpolated_vars_buffer.size());\n  interpolator.interpolate(make_not_null(&local_interpolated_vars),\n                           volume_vars_storage.vars_to_interpolate_to_target);\n\n  // Copy local results into overall result\n  // Loop over each tensor\n  tmpl::for_each<ah::vars_to_interpolate_to_target<3, Fr>>(\n      [&]<typename Tag>(tmpl::type_<Tag>) {\n        auto& individual_interpolated_var = get<Tag>(interpolated_vars);\n        const auto& local_individual_interpolated_var =\n            get<Tag>(local_interpolated_vars);\n\n        // Loop over components of tensor\n        for (size_t i = 0; i < individual_interpolated_var.size(); i++) {\n          // Loop over number of points that were interpolated\n          for (size_t p = 0; p < offsets.size(); ++p) {\n            // Copy the local interpolated value into the correct\n            // position in the overall tensor\n            individual_interpolated_var[i][offsets[p]] =\n                local_individual_interpolated_var[i][p];\n            indices_interpolated_to_thus_far[offsets[p]] = true;\n          }\n        }\n      });\n\n  current_iteration_storage->intersecting_element_ids.insert(element_id);\n  return true;\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template bool interpolate_volume_data(                                    \\\n      const gsl::not_null<ah::Storage::Iteration<FRAME(data)>*>             \\\n          current_iteration_storage,                                        \\\n      const ah::Storage::VolumeVariables<FRAME(data)>& volume_vars_storage, \\\n      const ElementId<3>& element_id);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Inertial, Frame::Distorted, Frame::Grid))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/InterpolateVolumeVars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_map>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Domain;\ntemplate <size_t VolumeDim>\nclass ElementId;\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace ah {\n/*!\n * \\brief Interpolate volume data from any new elements received by the horizon\n * finder to the target points.\n *\n * \\details For each new element, the `vars_to_interpolate_to_target` in\n * \\p all_volume_variables are interpolated to the target points and stored\n * in \\p current_iteration_storage.\n */\ntemplate <typename Fr>\nbool interpolate_volume_data(\n    gsl::not_null<ah::Storage::Iteration<Fr>*> current_iteration_storage,\n    const ah::Storage::VolumeVariables<Fr>& volume_vars_storage,\n    const ElementId<3>& element_id);\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/OptionTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace ah {\ntemplate <typename Fr>\nHorizonOptions<Fr>::HorizonOptions(\n    std::vector<std::unique_ptr<ah::Criterion>> criteria_in,\n    ylm::Strahlkorper<Fr> initial_guess_in, ::FastFlow fast_flow_in,\n    ::Verbosity verbosity_in, const size_t max_compute_coords_retries_in,\n    std::optional<std::vector<std::string>> blocks_for_horizon_find_in)\n    : criteria(std::move(criteria_in)),\n      initial_guess(std::move(initial_guess_in)),\n      fast_flow(std::move(fast_flow_in)),  // NOLINT\n      verbosity(std::move(verbosity_in)),  // NOLINT\n      max_compute_coords_retries(max_compute_coords_retries_in),\n      blocks_for_horizon_find(std::move(blocks_for_horizon_find_in)) {}\n\ntemplate <typename Fr>\nvoid HorizonOptions<Fr>::pup(PUP::er& p) {\n  p | criteria;\n  p | initial_guess;\n  p | fast_flow;\n  p | verbosity;\n  p | max_compute_coords_retries;\n  p | blocks_for_horizon_find;\n}\n\ntemplate <typename Fr>\nbool operator==(const HorizonOptions<Fr>& lhs, const HorizonOptions<Fr>& rhs) {\n  if (lhs.criteria.size() != rhs.criteria.size()) {\n    return false;\n  }\n  for (size_t i = 0; i < lhs.criteria.size(); ++i) {\n    if (not(lhs.criteria[i]->is_equal(*(rhs.criteria[i])))) {\n      return false;\n    }\n  }\n  return lhs.initial_guess == rhs.initial_guess and\n         lhs.fast_flow == rhs.fast_flow and lhs.verbosity == rhs.verbosity and\n         lhs.max_compute_coords_retries == rhs.max_compute_coords_retries and\n         lhs.blocks_for_horizon_find == rhs.blocks_for_horizon_find;\n}\n\ntemplate <typename Fr>\nbool operator!=(const HorizonOptions<Fr>& lhs, const HorizonOptions<Fr>& rhs) {\n  return not(lhs == rhs);\n}\n\n// Explicit instantiations\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                        \\\n  template struct HorizonOptions<FRAME(data)>;                      \\\n  template bool operator==(const HorizonOptions<FRAME(data)>& lhs,  \\\n                           const HorizonOptions<FRAME(data)>& rhs); \\\n  template bool operator!=(const HorizonOptions<FRAME(data)>& lhs,  \\\n                           const HorizonOptions<FRAME(data)>& rhs);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (::Frame::Grid, ::Frame::Distorted, ::Frame::Inertial))\n\n#undef FRAME\n#undef INSTANTIATE\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah {\n/// Options for finding an apparent horizon.\ntemplate <typename Fr>\nstruct HorizonOptions {\n private:\n  struct All {};\n\n public:\n  struct Criteria {\n    static constexpr Options::String help = {\n        \"List of criteria for adapting the horizon resolution\"};\n    using type = std::vector<std::unique_ptr<ah::Criterion>>;\n  };\n  /// See Strahlkorper for suboptions.\n  struct InitialGuess {\n    static constexpr Options::String help = {\"Initial guess\"};\n    using type = ylm::Strahlkorper<Fr>;\n  };\n  /// See ::FastFlow for suboptions.\n  struct FastFlow {\n    static constexpr Options::String help = {\"FastFlow options\"};\n    using type = ::FastFlow;\n  };\n  struct Verbosity {\n    static constexpr Options::String help = {\"Verbosity\"};\n    using type = ::Verbosity;\n  };\n  struct MaxComputeCoordsRetries {\n    static constexpr Options::String help = {\n        \"Number of times to retry computing the coordinates of the horizon for \"\n        \"each iteration. For the zeroth iteration, increases the 00 component \"\n        \"by 50%. For subsequent iterations, two previous surfaces are averaged \"\n        \"and that new surface is used.\"};\n    using type = size_t;\n  };\n  struct BlocksForHorizonFind {\n    static constexpr Options::String help = {\n        \"Volume data will be sent to the horizon finder from these block group \"\n        \"names. Set to 'All' to send volume data from the entire domain.\"};\n    using type = Options::Auto<std::vector<std::string>, All>;\n  };\n  using options = tmpl::list<Criteria, InitialGuess, FastFlow, Verbosity,\n                             MaxComputeCoordsRetries, BlocksForHorizonFind>;\n  static constexpr Options::String help = {\n      \"Provide an initial guess for the apparent horizon surface\\n\"\n      \"(Strahlkorper) and apparent-horizon-finding-algorithm (FastFlow)\\n\"\n      \"options.\"};\n\n  HorizonOptions(\n      std::vector<std::unique_ptr<ah::Criterion>> criteria_in,\n      ylm::Strahlkorper<Fr> initial_guess_in, ::FastFlow fast_flow_in,\n      ::Verbosity verbosity_in, size_t max_compute_coords_retries_in,\n      std::optional<std::vector<std::string>> blocks_for_horizon_find_in);\n\n  HorizonOptions() = default;\n  HorizonOptions(const HorizonOptions& /*rhs*/) = delete;\n  HorizonOptions& operator=(const HorizonOptions& /*rhs*/) = delete;\n  HorizonOptions(HorizonOptions&& /*rhs*/) = default;\n  HorizonOptions& operator=(HorizonOptions&& /*rhs*/) = default;\n  ~HorizonOptions() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  std::vector<std::unique_ptr<ah::Criterion>> criteria;\n  ylm::Strahlkorper<Fr> initial_guess{};\n  ::FastFlow fast_flow;\n  ::Verbosity verbosity{::Verbosity::Quiet};\n  size_t max_compute_coords_retries{};\n  std::optional<std::vector<std::string>> blocks_for_horizon_find;\n};\n\ntemplate <typename Fr>\nbool operator==(const HorizonOptions<Fr>& lhs, const HorizonOptions<Fr>& rhs);\ntemplate <typename Fr>\nbool operator!=(const HorizonOptions<Fr>& lhs, const HorizonOptions<Fr>& rhs);\n\nnamespace OptionTags {\nstruct ApparentHorizonGroup {\n  static constexpr Options::String help{\"Options for apparent horizon finders\"};\n  static std::string name() { return \"ApparentHorizons\"; }\n};\n\ntemplate <typename HorizonMetavars>\nstruct ApparentHorizonOptions {\n  using type = HorizonOptions<typename HorizonMetavars::frame>;\n  static constexpr Options::String help{\n      \"Options for interpolation onto apparent horizon.\"};\n  static std::string name() { return pretty_type::name<HorizonMetavars>(); }\n  using group = ApparentHorizonGroup;\n};\n\n/// \\ingroup OptionTagsGroup\n/// Maximum L used both for adaptive horizon resolution and output padding.\nstruct LMax {\n  using type = size_t;\n  static constexpr Options::String help = {\n      \"Maximum L for horizon resolution and output. Adaptive criteria clamp \"\n      \"the surface to this L, and output at smaller L is zero padded to \"\n      \"match this maximum L.\"};\n  using group = ApparentHorizonGroup;\n};\n}  // namespace OptionTags\n}  // namespace ah\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/PrintDeadlockAnalysis.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iomanip>\n#include <ios>\n#include <limits>\n#include <sstream>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Actions {\n/*!\n * \\brief Simple action to print deadlock info of the horizon finder.\n */\nstruct PrintDeadlockAnalysis {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(const db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const std::string& file_name) {\n    std::stringstream ss{};\n    ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)\n       << std::scientific;\n\n    using HorizonMetavars = typename ParallelComponent::horizon_metavars;\n    const std::string& name = pretty_type::name<HorizonMetavars>();\n    ss << \"Horizon finder \" << name << \":\\n\";\n\n    const auto& completed_times = db::get<ah::Tags::CompletedTimes>(box);\n    ss << \"  Completed times: \" << completed_times << \"\\n\";\n\n    const auto& current_time_optional = db::get<ah::Tags::CurrentTime>(box);\n    if (current_time_optional.has_value()) {\n      ss << \"  Current time: \" << current_time_optional.value() << \"\\n\";\n    } else {\n      ss << \"  No current time set.\\n\";\n    }\n\n    const auto& pending_times = db::get<ah::Tags::PendingTimes>(box);\n    ss << \"  Pending times: \" << pending_times << \"\\n\";\n\n    if (current_time_optional.has_value()) {\n      const auto& current_time = current_time_optional.value();\n      const auto& all_storage =\n          db::get<ah::Tags::Storage<typename HorizonMetavars::frame>>(box);\n      if (all_storage.contains(current_time)) {\n        const auto& current_time_storage = all_storage.at(current_time);\n        const auto& all_volume_variables =\n            current_time_storage.all_volume_variables;\n        const auto& current_iteration_storage =\n            current_time_storage.current_iteration;\n        const auto& fast_flow = db::get<ah::Tags::FastFlow>(box);\n\n        ss << \"  Time is ready: \" << current_time_storage.time_is_ready << \"\\n\";\n        ss << \"  FastFlow iteration: \" << fast_flow.current_iteration() << \"\\n\";\n        ss << \"  Coordinates are set: \"\n           << current_iteration_storage.block_coord_holders.has_value() << \"\\n\";\n\n        if (current_iteration_storage.block_coord_holders.has_value()) {\n          ss << \"  Number of points to interpolate to: \"\n             << current_iteration_storage.block_coord_holders->size() << \"\\n\";\n          const bool interpolation_is_complete =\n              current_iteration_storage.interpolation_is_complete();\n          ss << \"  Interpolation is complete: \" << interpolation_is_complete\n             << \"\\n\";\n          if (not interpolation_is_complete) {\n            ss << \"  THE HORIZON FINDER IS STUCK IN INTERPOLATION, WHICH \"\n                  \"LIKELY CAUSED THIS DEADLOCK. It is likely waiting for \"\n                  \"volume data that will never arrive. See \"\n                  \"'src/ParallelAlgorithms/ApparentHorizonFinder/Events/\"\n                  \"FindApparentHorizon.hpp' for possible causes and \"\n                  \"solutions.\\n\";\n            const auto& block_logical_coords =\n                current_iteration_storage.block_coord_holders.value();\n            ss << \"  Missing points (in block-logical coords):\\n\";\n            const auto& filled =\n                current_iteration_storage.indices_interpolated_to_thus_far;\n            for (size_t i = 0; i < filled.size(); ++i) {\n              if (not filled[i]) {\n                ss << \"    Index \" << i << \": \" << block_logical_coords[i]\n                   << \"\\n\";\n              }\n            }\n          }\n          ss << \"  Intersecting element IDs: \"\n             << current_iteration_storage.intersecting_element_ids << \"\\n\";\n          ss << \"  Element order: \" << current_time_storage.element_order\n             << \"\\n\";\n          ss << \"  Volume data received from \" << all_volume_variables.size()\n             << \" elements:\\n\";\n          for (const auto& [element_id, volume_vars_storage] :\n               all_volume_variables) {\n            ss << \"    \" << element_id << \"\\n\";\n          }\n        }\n      } else {\n        ss << \"  No storage for current time \" << current_time << \".\\n\";\n      }\n    }\n\n    Parallel::fprintf(file_name, \"%s\\n\", ss.str());\n  }\n};\n}  // namespace ah::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Callback.hpp\n  HorizonMetavars.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ah::protocols {\n/*!\n * \\brief A protocol for a callback to a horizon find.\n *\n * \\details Can be used in either `horizon_find_callbacks` or\n * `horizon_find_failure_callbacks` in the `ah::protocols::HorizonMetavars`.\n *\n * A struct conforming to the this protocol must have\n *\n * - An apply function with the signature in the example\n *\n * A struct conforming to this protocol can also optionally specify\n *\n * - a type alias `const_global_cache_tags` that holds global cache tags.\n *\n * \\snippet Helpers/ParallelAlgorithms/ApparentHorizonFinder/TestHelpers.hpp HorizonFindCallback\n */\nstruct Callback {\n  template <typename ConformingType>\n  struct test {};\n};\n}  // namespace ah::protocols\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::protocols {\n/*!\n * \\brief A protocol for `HorizonMetavars`s that are used in the\n * `ah::Component` parallel component.\n *\n * \\details A struct conforming to the `HorizonMetavars` protocol must\n * have\n *\n * - a type alias `time_tag` to a tag that tells the horizon finder\n *   which time tag to use (for example, `::Tags::TimeAndPrevious`).\n *\n * - a type alias `frame` to the frame that the horizon find happens in (e.g.\n *   `::Frame::Distorted`).\n *\n * - a type alias `compute_tags_on_element` which is a `tmpl::list` of compute\n *   tags used in the `ObservationBox` on the elements.\n *\n * - a type alias `horizon_find_callbacks` which is a `tmpl::list` of callbacks\n *   that conform to `ah::protocols::Callback`.\n *\n * - a type alias `horizon_find_failure_callbacks` which is a `tmpl::list` of\n *   callbacks that conform to `ah::protocols::Callback`.\n *\n * - a static function `name()` that returns a `std::string`.\n *\n * - a static constexpr `ah::Destination` named `destination` which tells what\n *   this horizon find is for.\n *\n * \\snippet Helpers/ParallelAlgorithms/ApparentHorizonFinder/TestHelpers.hpp HorizonMetavars\n */\nstruct HorizonMetavars {\n  template <typename ConformingType>\n  struct test {\n    using time_tag = typename ConformingType::time_tag;\n\n    using frame = typename ConformingType::frame;\n\n    using horizon_find_callbacks =\n        typename ConformingType::horizon_find_callbacks;\n    static_assert(tmpl::all<horizon_find_callbacks,\n                            tt::assert_conforms_to<tmpl::_1, Callback>>::value);\n    using horizon_find_failure_callbacks =\n        typename ConformingType::horizon_find_failure_callbacks;\n    static_assert(tmpl::all<horizon_find_failure_callbacks,\n                            tt::assert_conforms_to<tmpl::_1, Callback>>::value);\n\n    using compute_tags_on_element =\n        typename ConformingType::compute_tags_on_element;\n\n    static constexpr Destination destination = ConformingType::destination;\n\n    static std::string name() { return ConformingType::name(); }\n  };\n};\n}  // namespace ah::protocols\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.SphericalHarmonics\");\n  py::enum_<FastFlow::FlowType>(m, \"FlowType\")\n      .value(\"Jacobi\", FastFlow::FlowType::Jacobi)\n      .value(\"Curvature\", FastFlow::FlowType::Curvature)\n      .value(\"Fast\", FastFlow::FlowType::Fast);\n  py::enum_<FastFlow::Status>(m, \"Status\")\n      .value(\"SuccessfulIteration\", FastFlow::Status::SuccessfulIteration)\n      .value(\"AbsTol\", FastFlow::Status::AbsTol)\n      .value(\"TruncationTol\", FastFlow::Status::TruncationTol)\n      .value(\"MaxIts\", FastFlow::Status::MaxIts)\n      .value(\"NegativeRadius\", FastFlow::Status::NegativeRadius)\n      .value(\"DivergenceError\", FastFlow::Status::DivergenceError)\n      .value(\"InterpolationFailure\", FastFlow::Status::InterpolationFailure);\n  py::class_<FastFlow::IterInfo>(m, \"IterInfo\")\n      .def_readonly(\"iteration\", &FastFlow::IterInfo::iteration)\n      .def_readonly(\"r_min\", &FastFlow::IterInfo::r_min)\n      .def_readonly(\"r_max\", &FastFlow::IterInfo::r_max)\n      .def_readonly(\"min_residual\", &FastFlow::IterInfo::min_residual)\n      .def_readonly(\"max_residual\", &FastFlow::IterInfo::max_residual)\n      .def_readonly(\"residual_ylm\", &FastFlow::IterInfo::residual_ylm)\n      .def_readonly(\"residual_mesh\", &FastFlow::IterInfo::residual_mesh);\n  py::class_<FastFlow>(m, \"FastFlow\")\n      .def(py::init<FastFlow::FlowType, double, double, double, double, double,\n                    size_t, size_t>(),\n           py::arg(\"flow_type\"), py::arg(\"alpha\"), py::arg(\"beta\"),\n           py::arg(\"abs_tol\"), py::arg(\"truncation_tol\"),\n           py::arg(\"divergence_tol\"), py::arg(\"divergence_iter\"),\n           py::arg(\"max_its\"))\n      .def(\n          \"iterate_horizon_finder\",\n          [](FastFlow& fast_flow,\n             ylm::Strahlkorper<Frame::Inertial>& current_strahlkorper,\n             const tnsr::II<DataVector, 3>& upper_spatial_metric,\n             const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n             const tnsr::Ijj<DataVector, 3>& christoffel_2nd_kind) {\n            return fast_flow.iterate_horizon_finder<Frame::Inertial>(\n                make_not_null(&current_strahlkorper), upper_spatial_metric,\n                extrinsic_curvature, christoffel_2nd_kind);\n          },\n          py::arg(\"current_strahlkorper\"), py::arg(\"upper_spatial_metric\"),\n          py::arg(\"extrinsic_curvature\"), py::arg(\"christoffel_2nd_kind\"))\n      .def(\"current_l_mesh\", &FastFlow::current_l_mesh<Frame::Inertial>)\n      .def(\"reset_for_next_find\", &FastFlow::reset_for_next_find);\n}\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyApparentHorizonFinder\")\n\nspectre_python_add_module(\n  ApparentHorizonFinder\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ApparentHorizonFinder\n  SphericalHarmonics\n  pybind11::module\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PySphericalHarmonics\n)\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Storage.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace ah::Storage {\ntemplate <typename Fr>\nvoid VolumeVariables<Fr>::pup(PUP::er& p) {\n  p | mesh;\n  p | vars_to_interpolate_to_target;\n}\n\ntemplate <typename Fr>\nbool operator==(const VolumeVariables<Fr>& lhs,\n                const VolumeVariables<Fr>& rhs) {\n  return lhs.mesh == rhs.mesh and\n         lhs.vars_to_interpolate_to_target == rhs.vars_to_interpolate_to_target;\n}\ntemplate <typename Fr>\nbool operator!=(const VolumeVariables<Fr>& lhs,\n                const VolumeVariables<Fr>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Fr>\nbool Iteration<Fr>::interpolation_is_complete() const {\n  return alg::all_of(indices_interpolated_to_thus_far,\n                     [](const bool filled) { return filled; });\n}\n\ntemplate <typename Fr>\nvoid Iteration<Fr>::reset_for_next_iteration() {\n  // Leave the strahlkorper because this was set by FastFlow and is already\n  // the next surface\n  this->block_coord_holders.reset();\n  this->indices_interpolated_to_thus_far.clear();\n  this->intersecting_element_ids.clear();\n  this->compute_coords_retries = 0;\n}\n\ntemplate <typename Fr>\nvoid Iteration<Fr>::pup(PUP::er& p) {\n  p | strahlkorper;\n  p | block_coord_holders;\n  p | interpolated_vars;\n  p | indices_interpolated_to_thus_far;\n  p | intersecting_element_ids;\n  p | compute_coords_retries;\n  // No need to serialize the memory buffers because they are resized as needed\n}\n\ntemplate <typename Fr>\nbool operator==(const Iteration<Fr>& lhs, const Iteration<Fr>& rhs) {\n  return lhs.strahlkorper == rhs.strahlkorper and\n         lhs.block_coord_holders == rhs.block_coord_holders and\n         lhs.interpolated_vars == rhs.interpolated_vars and\n         lhs.indices_interpolated_to_thus_far ==\n             rhs.indices_interpolated_to_thus_far and\n         lhs.intersecting_element_ids == rhs.intersecting_element_ids and\n         lhs.compute_coords_retries == rhs.compute_coords_retries;\n  // No need to compare the memory buffers\n}\ntemplate <typename Fr>\nbool operator!=(const Iteration<Fr>& lhs, const Iteration<Fr>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Fr>\nvoid SingleTimeStorage<Fr>::pup(PUP::er& p) {\n  p | all_volume_variables;\n  p | current_iteration;\n  p | previous_iteration_surface;\n  p | destination;\n  p | time_is_ready;\n}\n\ntemplate <typename Fr>\nbool operator==(const SingleTimeStorage<Fr>& lhs,\n                const SingleTimeStorage<Fr>& rhs) {\n  return lhs.all_volume_variables == rhs.all_volume_variables and\n         lhs.current_iteration == rhs.current_iteration and\n         lhs.previous_iteration_surface == rhs.previous_iteration_surface and\n         lhs.destination == rhs.destination and\n         lhs.time_is_ready == rhs.time_is_ready;\n}\ntemplate <typename Fr>\nbool operator!=(const SingleTimeStorage<Fr>& lhs,\n                const SingleTimeStorage<Fr>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Fr>\nPreviousSurface<Fr>::PreviousSurface(\n    const LinkedMessageId<double>& time_in, ylm::Strahlkorper<Fr> surface_in,\n    std::unordered_set<ElementId<3>> intersecting_element_ids_in)\n    : time(time_in),\n      surface(std::move(surface_in)),\n      intersecting_element_ids(std::move(intersecting_element_ids_in)) {}\n\ntemplate <typename Fr>\nvoid PreviousSurface<Fr>::pup(PUP::er& p) {\n  p | time;\n  p | surface;\n  p | intersecting_element_ids;\n}\n\ntemplate <typename Fr>\nbool operator==(const PreviousSurface<Fr>& lhs,\n                const PreviousSurface<Fr>& rhs) {\n  return lhs.time == rhs.time and lhs.surface == rhs.surface and\n         lhs.intersecting_element_ids == rhs.intersecting_element_ids;\n}\ntemplate <typename Fr>\nbool operator!=(const PreviousSurface<Fr>& lhs,\n                const PreviousSurface<Fr>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename Fr>\nvoid LockedPreviousSurface<Fr>::pup(PUP::er& p) {\n  // Don't pup the lock\n  p | surface;\n}\n\ntemplate <typename Fr>\nLockedPreviousSurface<Fr>::LockedPreviousSurface() = default;\ntemplate <typename Fr>\nLockedPreviousSurface<Fr>::LockedPreviousSurface(const PreviousSurface<Fr>& rhs)\n    : surface(rhs) {}\ntemplate <typename Fr>\nLockedPreviousSurface<Fr>::LockedPreviousSurface(\n    const LockedPreviousSurface<Fr>& rhs)\n    : surface(rhs.surface) {}\ntemplate <typename Fr>\nLockedPreviousSurface<Fr>& LockedPreviousSurface<Fr>::operator=(\n    const LockedPreviousSurface<Fr>& rhs) {\n  surface = rhs.surface;\n  return *this;\n}\ntemplate <typename Fr>\nLockedPreviousSurface<Fr>::LockedPreviousSurface(\n    LockedPreviousSurface<Fr>&& rhs)\n    : surface(std::move(rhs.surface)) {}\ntemplate <typename Fr>\nLockedPreviousSurface<Fr>& LockedPreviousSurface<Fr>::operator=(\n    LockedPreviousSurface<Fr>&& rhs) {\n  surface = std::move(rhs.surface);\n  return *this;\n}\n\ntemplate <typename Fr>\nbool operator==(const LockedPreviousSurface<Fr>& lhs,\n                const LockedPreviousSurface<Fr>& rhs) {\n  return lhs.surface == rhs.surface;\n}\ntemplate <typename Fr>\nbool operator!=(const LockedPreviousSurface<Fr>& lhs,\n                const LockedPreviousSurface<Fr>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template struct VolumeVariables<FRAME(data)>;                        \\\n  template struct Iteration<FRAME(data)>;                              \\\n  template struct SingleTimeStorage<FRAME(data)>;                      \\\n  template struct PreviousSurface<FRAME(data)>;                        \\\n  template struct LockedPreviousSurface<FRAME(data)>;                  \\\n  template bool operator==(const VolumeVariables<FRAME(data)>&,        \\\n                           const VolumeVariables<FRAME(data)>&);       \\\n  template bool operator!=(const VolumeVariables<FRAME(data)>&,        \\\n                           const VolumeVariables<FRAME(data)>&);       \\\n  template bool operator==(const Iteration<FRAME(data)>&,              \\\n                           const Iteration<FRAME(data)>&);             \\\n  template bool operator!=(const Iteration<FRAME(data)>&,              \\\n                           const Iteration<FRAME(data)>&);             \\\n  template bool operator==(const SingleTimeStorage<FRAME(data)>&,      \\\n                           const SingleTimeStorage<FRAME(data)>&);     \\\n  template bool operator!=(const SingleTimeStorage<FRAME(data)>&,      \\\n                           const SingleTimeStorage<FRAME(data)>&);     \\\n  template bool operator==(const PreviousSurface<FRAME(data)>&,        \\\n                           const PreviousSurface<FRAME(data)>&);       \\\n  template bool operator!=(const PreviousSurface<FRAME(data)>&,        \\\n                           const PreviousSurface<FRAME(data)>&);       \\\n  template bool operator==(const LockedPreviousSurface<FRAME(data)>&,  \\\n                           const LockedPreviousSurface<FRAME(data)>&); \\\n  template bool operator!=(const LockedPreviousSurface<FRAME(data)>&,  \\\n                           const LockedPreviousSurface<FRAME(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Inertial, Frame::Distorted, Frame::Grid))\n\n#undef INSTANTIATE\n#undef FRAME\n}  // namespace ah::Storage\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <set>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Parallel/MultiReaderSpinlock.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n\nnamespace ah::Storage {\n/*!\n * \\brief Holds the `ah::source_vars`, mesh, and other variables on the horizon\n * finder for a given element.\n */\ntemplate <typename Fr>\nstruct VolumeVariables {\n  /*!\n   * \\brief The mesh that corresponds to the volume vars.\n   */\n  Mesh<3> mesh;\n\n  /*!\n   * \\brief A `Variables` of the tensors in the volume that we need to\n   * interpolate onto the horizon.\n   */\n  Variables<ah::vars_to_interpolate_to_target<3, Fr>>\n      vars_to_interpolate_to_target{};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n\ntemplate <typename Fr>\nbool operator==(const VolumeVariables<Fr>& lhs, const VolumeVariables<Fr>& rhs);\ntemplate <typename Fr>\nbool operator!=(const VolumeVariables<Fr>& lhs, const VolumeVariables<Fr>& rhs);\n\n/*!\n * \\brief Holds the `ylm::Strahlkorper` and associated quantities for a single\n * `FastFlow` iteration.\n */\ntemplate <typename Fr>\nstruct Iteration {\n  /*!\n   * \\brief The surface for this iteration\n   */\n  ylm::Strahlkorper<Fr> strahlkorper;\n  /*!\n   *  \\brief Holds the list of all points (in block logical coordinates) that\n   *  need to be interpolated onto.\n   *\n   * \\details If an element of the vector is `std::nullopt`, then that point is\n   * outside the domain.\n   */\n  std::optional<std::vector<BlockLogicalCoords<3>>> block_coord_holders;\n  /*!\n   * \\brief Holds the interpolated `Variables` on the points in\n   * `block_coord_holders`.\n   *\n   * \\details The grid points inside are indexed according to\n   * `block_coord_holders`.\n   */\n  Variables<ah::vars_to_interpolate_to_target<3, Fr>> interpolated_vars{};\n  /*!\n   * \\brief Keeps track of the indices in `interpolated_vars` that have\n   * already been interpolated to.\n   */\n  std::vector<bool> indices_interpolated_to_thus_far{};\n  /*!\n   * \\brief Holds the element IDs of all elements that intersect with the\n   * current iteration surface. Used to determine which elements will send\n   * data for the next horizon find.\n   */\n  std::unordered_set<ElementId<3>> intersecting_element_ids{};\n  /*!\n   * \\brief Offsets of newly interpolated points in the overall tensor (used as\n   * memory buffer)\n   */\n  std::vector<size_t> offsets_of_newly_interpolated_points{};\n  /*!\n   * \\brief Logical coordinates of newly interpolated points (used as memory\n   * buffer)\n   *\n   * These `std::vector`s are used to reserve memory and then append points to\n   * them as we find them in an element. The memory is reused for each element.\n   * Then, a non-owning DataVector is created by pointing into this memory.\n   * That's why this is a `std::array` of `std::vector`s, not vice versa.\n   */\n  std::array<std::vector<double>, 3>\n      x_element_logical_of_newly_interpolated_points{};\n  /*!\n   * \\brief Buffer for newly interpolated variables (used as memory buffer)\n   */\n  std::vector<double> newly_interpolated_vars_buffer{};\n\n  /*!\n   * \\brief How many times we've tried to compute the coordinates for this\n   * iteration.\n   */\n  size_t compute_coords_retries = 0;\n\n  /*!\n   * \\brief Whether all points in `interpolated_vars` have been filled.\n   */\n  bool interpolation_is_complete() const;\n\n  void reset_for_next_iteration();\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n\ntemplate <typename Fr>\nbool operator==(const Iteration<Fr>& lhs, const Iteration<Fr>& rhs);\ntemplate <typename Fr>\nbool operator!=(const Iteration<Fr>& lhs, const Iteration<Fr>& rhs);\n\n/*!\n * \\brief Holds all data necessary for a single horizon find.\n *\n * \\details This includes volume variables which persist for the entire horizon\n * find, and also interpolated variables that are updated for each iteration.\n */\ntemplate <typename Fr>\nstruct SingleTimeStorage {\n  /*!\n   * \\brief Map between `ElementId`s and the volume variables from that element.\n   */\n  std::unordered_map<ElementId<3>, VolumeVariables<Fr>> all_volume_variables;\n  /*!\n   * \\brief Elements in which we have found points to interpolate to in previous\n   * iterations, to try first before searching all elements.\n   *\n   * This is not only a performance optimization, but also important for\n   * robustness. If we try to interpolate from elements in a different order in\n   * each iteration, then points that lie directly on element boundaries can\n   * fluctuate in interpolated value, preventing convergence (see\n   * https://github.com/sxs-collaboration/spectre/issues/3899).\n   */\n  std::vector<ElementId<3>> element_order{};\n  /*!\n   * \\brief The `Iteration` data for the current fast flow iteration.\n   */\n  Iteration<Fr> current_iteration;\n  /*!\n   * \\brief The previous iteration surface, used if interpolation fails.\n   */\n  ylm::Strahlkorper<Fr> previous_iteration_surface;\n  /*!\n   * \\brief The `ah::Destination` for this horizon find.\n   */\n  Destination destination{};\n  /*!\n   * \\brief Whether we have checked if the functions of time are up to date for\n   * this horizon find.\n   */\n  bool time_is_ready = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n\ntemplate <typename Fr>\nbool operator==(const SingleTimeStorage<Fr>& lhs,\n                const SingleTimeStorage<Fr>& rhs);\ntemplate <typename Fr>\nbool operator!=(const SingleTimeStorage<Fr>& lhs,\n                const SingleTimeStorage<Fr>& rhs);\n\n/*!\n * \\brief The time and final surface for a previous horizon find.\n */\ntemplate <typename Fr>\nstruct PreviousSurface {\n  PreviousSurface() = default;\n  PreviousSurface(const LinkedMessageId<double>& time_in,\n                  ylm::Strahlkorper<Fr> surface_in,\n                  std::unordered_set<ElementId<3>> intersecting_element_ids_in);\n\n  LinkedMessageId<double> time;\n  ylm::Strahlkorper<Fr> surface;\n  std::unordered_set<ElementId<3>> intersecting_element_ids;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n\ntemplate <typename Fr>\nbool operator==(const PreviousSurface<Fr>& lhs, const PreviousSurface<Fr>& rhs);\ntemplate <typename Fr>\nbool operator!=(const PreviousSurface<Fr>& lhs, const PreviousSurface<Fr>& rhs);\n\n/*!\n * \\brief Holds a previous surface and a lock that protects it.\n *\n * \\details This is used to store and update a previous surface in the global\n * cache and allow multiple readers (elements) to access it simultaneously.\n */\ntemplate <typename Fr>\nstruct LockedPreviousSurface {\n  std::optional<PreviousSurface<Fr>> surface;\n  // Lock is mutable so it can be retrieved from the const global cache and put\n  // in read-lock mode by elements.\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable Parallel::MultiReaderSpinlock lock;\n\n  LockedPreviousSurface();\n  explicit LockedPreviousSurface(const PreviousSurface<Fr>& rhs);\n  LockedPreviousSurface(const LockedPreviousSurface& rhs);\n  LockedPreviousSurface& operator=(const LockedPreviousSurface& rhs);\n  LockedPreviousSurface(LockedPreviousSurface&& rhs);\n  LockedPreviousSurface& operator=(LockedPreviousSurface&& rhs);\n  ~LockedPreviousSurface() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n};\n\ntemplate <typename Fr>\nbool operator==(const LockedPreviousSurface<Fr>& lhs,\n                const LockedPreviousSurface<Fr>& rhs);\ntemplate <typename Fr>\nbool operator!=(const LockedPreviousSurface<Fr>& lhs,\n                const LockedPreviousSurface<Fr>& rhs);\n\n}  // namespace ah::Storage\n"
  },
  {
    "path": "src/ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <deque>\n#include <optional>\n#include <set>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Structure/BlockGroups.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n\n/// \\cond\nnamespace control_system::OptionTags {\nstruct WriteDataToDisk;\n}  // namespace control_system::OptionTags\nclass FastFlow;\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\nnamespace ah {\ntemplate <class Metavariables, typename HorizonMetavars>\nstruct Component;\n}  // namespace ah\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\n/*!\n * \\brief Tags for the apparent horizon finder.\n */\nnamespace ah::Tags {\n/*!\n * \\brief Verbosity of horizon finder\n */\nstruct Verbosity : db::SimpleTag {\n  using type = ::Verbosity;\n};\n\n/*!\n * \\brief Holds a `::FastFlow` object. Needs to be reset after each horizon\n * find.\n */\nstruct FastFlow : db::SimpleTag {\n  using type = ::FastFlow;\n};\n\n/*!\n * \\brief Tag that holds the current time.\n *\n * \\details The value of this tag is `std::nullopt` if the current time isn't\n * set.\n */\nstruct CurrentTime : db::SimpleTag {\n  using type = std::optional<LinkedMessageId<double>>;\n};\n\n/*!\n * \\brief List of times waiting for previous horizon finds to finish\n * before they can be started.\n */\nstruct PendingTimes : db::SimpleTag {\n  using type = std::set<LinkedMessageId<double>>;\n};\n\n/*!\n * \\brief Tag that holds all completed times\n */\nstruct CompletedTimes : db::SimpleTag {\n  using type = std::set<LinkedMessageId<double>>;\n};\n\n/*!\n * \\brief Holds potential dependency for apparent horizon callbacks.\n */\nstruct Dependency : db::SimpleTag {\n  using type = std::optional<std::string>;\n};\n\n/*!\n * \\brief Tag that holds the current resolution L.\n *\n * \\details The value of this tag is `std::nullopt` if the current resolution L\n * isn't set.\n */\nstruct CurrentResolutionL : db::SimpleTag {\n  using type = std::optional<size_t>;\n};\n\n/*!\n * \\brief Storage of all variables (volume or interpolated) for all times of the\n * horizon finder.\n */\ntemplate <typename Fr>\nstruct Storage : db::SimpleTag {\n  using type = std::unordered_map<LinkedMessageId<double>,\n                                  ah::Storage::SingleTimeStorage<Fr>>;\n};\n\n/*!\n * \\brief Order in which blocks are searched for horizon finding. See\n * `::block_logical_coordinates` for details.\n */\nstruct BlockSearchOrder : db::SimpleTag {\n  using type = std::vector<size_t>;\n};\n\n/*!\n * \\brief Deque of `ah::Storage::PreviousSurface`s.\n */\ntemplate <typename Fr>\nstruct PreviousSurfaces : db::SimpleTag {\n  using type = std::deque<ah::Storage::PreviousSurface<Fr>>;\n};\n\n/*!\n * \\brief Holds the previous surface. Used to determine which elements will send\n * data for the next horizon find.\n */\ntemplate <typename HorizonMetavars>\nstruct PreviousSurface : db::SimpleTag {\n  using type =\n      ah::Storage::LockedPreviousSurface<typename HorizonMetavars::frame>;\n  using option_tags = tmpl::list<>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options() { return {}; }\n};\n\n/*!\n * \\brief Global cache tag that holds horizon finder options\n */\ntemplate <typename HorizonMetavars>\nstruct ApparentHorizonOptions : db::SimpleTag {\n  using type = HorizonOptions<typename HorizonMetavars::frame>;\n  using option_tags =\n      tmpl::list<OptionTags::ApparentHorizonOptions<HorizonMetavars>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) {\n    return {serialize_and_deserialize<type>(option)};\n  }\n};\n\nnamespace tags_detail {\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(component_being_mocked)\n\ntemplate <typename HorizonComponent>\nstruct get_horizon_metavars_from_component {\n  using type = typename HorizonComponent::horizon_metavars;\n};\n\ntemplate <typename Metavariables>\nusing get_horizon_metavars = tmpl::transform<\n    tmpl::filter<tmpl::transform<\n                     typename Metavariables::component_list,\n                     get_component_being_mocked_or_default<tmpl::_1, tmpl::_1>>,\n                 tt::is_a<ah::Component, tmpl::_1>>,\n    get_horizon_metavars_from_component<tmpl::_1>>;\n\ntemplate <typename HorizonMetavars>\nstruct get_horizon_options;\n\ntemplate <typename... HorizonMetavars>\nstruct get_horizon_options<tmpl::list<HorizonMetavars...>> {\n  using type =\n      tmpl::list<OptionTags::ApparentHorizonOptions<HorizonMetavars>...>;\n};\n\n}  // namespace tags_detail\n\n/*!\n * \\brief Holds a map between horizon name and a set of block names that should\n * be used for interpolation for that horizon.\n */\nstruct BlocksForHorizonFind : db::SimpleTag {\n  using type = std::unordered_map<std::string, std::unordered_set<std::string>>;\n  template <typename Metavariables>\n  using option_tags = tmpl::push_front<\n      typename tags_detail::get_horizon_options<\n          tags_detail::get_horizon_metavars<Metavariables>>::type,\n      ::domain::OptionTags::DomainCreator<Metavariables::volume_dim>>;\n\n  static constexpr bool pass_metavariables = true;\n  template <typename Metavariables, typename... HorizonOptionClasses>\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator,\n      const HorizonOptionClasses&... all_horizon_options) {\n    return create_from_options_impl<Metavariables>(\n        domain_creator, std::forward_as_tuple(all_horizon_options...),\n        std::make_index_sequence<sizeof...(HorizonOptionClasses)>{});\n  }\n\n private:\n  // Need the names of the target tags which are in the option tags, but not the\n  // horizon options themselves. This just expands a tuple to be able to index\n  // the `option_tags` type alias so we can get the name of the target horizon\n  template <typename Metavariables, typename HorizonOptionsTuple, size_t... Is>\n  static type create_from_options_impl(\n      const std::unique_ptr<::DomainCreator<Metavariables::volume_dim>>&\n          domain_creator,\n      const HorizonOptionsTuple& all_horizon_options,\n      const std::index_sequence<Is...>& /*index_sequence*/\n  ) {\n    std::unordered_map<std::string, std::unordered_set<std::string>> result{};\n\n    const auto block_names = domain_creator->block_names();\n    const auto block_groups = domain_creator->block_groups();\n\n    const auto append_to_result = [&](const std::string& name,\n                                      const auto& horizon_options) {\n      if (horizon_options.blocks_for_horizon_find.has_value()) {\n        result[name] = domain::expand_block_groups_to_block_names(\n            horizon_options.blocks_for_horizon_find.value(), block_names,\n            block_groups);\n      } else {\n        // Insert all blocks\n        result[name].insert(block_names.begin(), block_names.end());\n      }\n\n      // Needed for the expand_pack below\n      return 0;\n    };\n\n    expand_pack(\n        append_to_result(tmpl::at_c<option_tags<Metavariables>, Is + 1>::name(),\n                         std::get<Is>(all_horizon_options))...);\n\n    return result;\n  }\n};\n\n/// @{\n/*!\n * \\brief Tag to be used for the `time_tag` alias of a `HorizonMetavars` for an\n * observation horizon find.\n *\n * \\details We need separate time tags for all horizon finders because of the\n * current design of the horizon finder. So we just make a simple compute tag\n * that takes the actual time out of the box since we still want the actual time\n * to be the same, just a different tag.\n *\n */\ntemplate <size_t Index>\nstruct ObservationTime : db::SimpleTag {\n  static std::string name() { return \"AhObservationTime\" + get_output(Index); }\n  using type = LinkedMessageId<double>;\n};\n\ntemplate <size_t Index>\nstruct ObservationTimeCompute : ObservationTime<Index>, db::ComputeTag {\n  using argument_tags = tmpl::list<::Tags::Time>;\n  using base = ObservationTime<Index>;\n  using return_type = LinkedMessageId<double>;\n\n  static void function(const gsl::not_null<LinkedMessageId<double>*> ah_time,\n                       const double time) {\n    // The horizon finder knows how to handle the nullopt\n    *ah_time = LinkedMessageId<double>{time, std::nullopt};\n  }\n};\n/// @}\n\n/// Simple tag for whether to write the centers of the horizons to disk.\nstruct ObserveCenters : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<control_system::OptionTags::WriteDataToDisk>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// DataBox tag that holds the maximum L for horizon resolution and output.\nstruct LMax : db::SimpleTag {\n  using type = size_t;\n\n  using option_tags = tmpl::list<OptionTags::LMax>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const size_t max_l) { return max_l; }\n};\n}  // namespace ah::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Actions)\nadd_subdirectory(ApparentHorizonFinder)\nadd_subdirectory(Amr)\nadd_subdirectory(Events)\nadd_subdirectory(EventsAndDenseTriggers)\nadd_subdirectory(EventsAndTriggers)\nadd_subdirectory(Initialization)\nadd_subdirectory(Interpolation)\nadd_subdirectory(LinearSolver)\nadd_subdirectory(NonlinearSolver)\nadd_subdirectory(RayTracer)\nadd_subdirectory(SurfaceFinder)\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Events)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Completion.cpp\n  ObserveAdaptiveSteppingDiagnostics.cpp\n  ObserveConstantsPerElement.cpp\n  ObserveDataBox.cpp\n  ObserveNorms.cpp\n  ObserveTimeStep.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Completion.hpp\n  ErrorIfDataTooBig.hpp\n  Factory.hpp\n  MonitorMemory.hpp\n  ObserveAdaptiveSteppingDiagnostics.hpp\n  ObserveConstantsPerElement.hpp\n  ObserveDataBox.hpp\n  ObserveAtExtremum.hpp\n  ObserveFields.hpp\n  ObserveNorms.hpp\n  ObserveTimeStep.hpp\n  ObserveTimeStep.tpp\n  ObserveTimeStepVolume.hpp\n  ObserveTimeStepVolume.tpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Charmxx::pup\n  LinearOperators\n  Observer\n  Options\n  Parallel\n  Serialization\n  Time\n  Utilities\n  INTERFACE\n  DataStructures\n  Domain\n  DomainStructure\n  ErrorHandling\n  EventsAndTriggers\n  H5\n  Interpolation\n  Printf\n  Spectral\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/Completion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n\nnamespace Events {\nPUP::able::PUP_ID Completion::my_PUP_ID = 0;  // NOLINT\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/Completion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel::Actions {\nstruct SetTerminateOnElement;\n}  // namespace Parallel::Actions\n/// \\endcond\n\nnamespace Events {\n/// \\ingroup EventsAndTriggersGroup\n/// Sets the termination flag for the code to exit.\nclass Completion : public Event {\n public:\n  /// \\cond\n  explicit Completion(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Completion);  // NOLINT\n  /// \\endcond\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Sets the termination flag for the code to exit.\"};\n\n  Completion() = default;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  void operator()(Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const Component* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    if constexpr (Parallel::is_nodegroup_v<Component>) {\n      Parallel::local_synchronous_action<\n          Parallel::Actions::SetTerminateOnElement>(\n          Parallel::get_parallel_component<Component>(cache),\n          make_not_null(&cache), array_index, true);\n    } else {\n      auto al_gore = Parallel::local(\n          Parallel::get_parallel_component<Component>(cache)[array_index]);\n      al_gore->set_terminate(true);\n    }\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n};\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ErrorIfDataTooBig.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataBox/ValidateSelection.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ExtractPoint.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace Events {\n/*!\n * \\brief ERROR if tensors get too big.\n *\n * The magnitudes of the components of the specified tensors are\n * checked, and if any exceed the specified threshold an ERROR is\n * thrown, terminating the evolution.\n *\n * Any `Tensor` in the `db::DataBox` can be checked but must be listed\n * in the `Tensors` template parameter. Any additional compute tags\n * that hold a `Tensor` can also be added to the `Tensors` template\n * parameter. Finally, `Variables` and other non-tensor compute tags\n * used to calculate tensors can be listed in the\n * `NonTensorComputeTags`.\n *\n * \\note The `NonTensorComputeTags` are intended to be used for `Variables`\n * compute tags like `Tags::DerivCompute`\n */\ntemplate <size_t Dim, typename Tensors, typename NonTensorComputeTags>\nclass ErrorIfDataTooBig : public Event {\n public:\n  /// \\cond\n  explicit ErrorIfDataTooBig(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ErrorIfDataTooBig);  // NOLINT\n  /// \\endcond\n\n  struct VariablesToCheck {\n    static constexpr Options::String help = \"Subset of variables to check\";\n    using type = std::vector<std::string>;\n    static size_t lower_bound_on_size() { return 1; }\n  };\n\n  struct Threshold {\n    static constexpr Options::String help = \"Threshold at which to ERROR\";\n    using type = double;\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options = tmpl::list<VariablesToCheck, Threshold>;\n\n  static constexpr Options::String help = \"ERROR if tensors get too big\";\n\n  ErrorIfDataTooBig() = default;\n\n  ErrorIfDataTooBig(const std::vector<std::string>& variables_to_check,\n                    const double threshold, const Options::Context& context)\n      : variables_to_check_(variables_to_check.begin(),\n                            variables_to_check.end()),\n        threshold_(threshold) {\n    db::validate_selection<Tensors>(variables_to_check, context);\n  }\n\n  using compute_tags_for_observation_box =\n      tmpl::append<Tensors, NonTensorComputeTags>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags =\n      tmpl::list<::Tags::ObservationBox,\n                 ::domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n\n  template <typename ComputeTagsList, typename DataBoxType,\n            typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(const ObservationBox<ComputeTagsList, DataBoxType>& box,\n                  const tnsr::I<DataVector, Dim>& coordinates,\n                  Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ArrayIndex& /*array_index*/,\n                  const ParallelComponent* const /*component*/,\n                  const ObservationValue& /*observation_value*/) const {\n    tmpl::for_each<Tensors>([&](const auto tensor_tag_v) {\n      using tensor_tag = tmpl::type_from<decltype(tensor_tag_v)>;\n      const std::string tag_name = db::tag_name<tensor_tag>();\n      if (variables_to_check_.count(tag_name) != 0) {\n        const auto check_components = [&](const auto& tensor) {\n          for (const auto& component : tensor) {\n            for (size_t point = 0; point < component.size(); ++point) {\n              if (std::abs(component[point]) > threshold_) {\n                ERROR_NO_TRACE(\n                    tag_name\n                    << \" too big with value \" << extract_point(tensor, point)\n                    << \" at position\\n\"\n                    << extract_point(coordinates, point) << \"\\nwith ElementId: \"\n                    << get<::domain::Tags::Element<Dim>>(box).id());\n              }\n            }\n          }\n        };\n        const auto& tensor = get<tensor_tag>(box);\n        if constexpr (tt::is_a_v<std::optional,\n                                 std::decay_t<decltype(tensor)>>) {\n          if (tensor.has_value()) {\n            check_components(*tensor);\n          }\n        } else {\n          check_components(tensor);\n        }\n      }\n    });\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | variables_to_check_;\n    p | threshold_;\n  }\n\n private:\n  std::unordered_set<std::string> variables_to_check_{};\n  double threshold_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <size_t Dim, typename Tensors, typename NonTensorComputeTags>\nPUP::able::PUP_ID\n    ErrorIfDataTooBig<Dim, Tensors,\n                      NonTensorComputeTags>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"ParallelAlgorithms/Events/ErrorIfDataTooBig.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveAdaptiveSteppingDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveFields.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveNorms.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.hpp\"\n#include \"Time/ChangeSlabSize/Event.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace dg::Events {\ntemplate <size_t VolumeDim, typename Fields, typename NonTensorComputeTagsList,\n          typename ArraySectionIdTag = void>\nusing field_observations = tmpl::flatten<tmpl::list<\n    ::Events::ErrorIfDataTooBig<VolumeDim, Fields, NonTensorComputeTagsList>,\n    ObserveFields<VolumeDim, Fields, NonTensorComputeTagsList,\n                  ArraySectionIdTag>,\n    ::Events::ObserveNorms<Fields, NonTensorComputeTagsList,\n                           ArraySectionIdTag>>>;\n}  // namespace dg::Events\n\nnamespace Events {\ntemplate <typename System>\nusing time_events =\n    tmpl::list<Events::ObserveAdaptiveSteppingDiagnostics,\n               Events::ObserveTimeStep<System>, Events::ChangeSlabSize>;\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/MonitorMemory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/MemoryMonitor/Tags.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ProcessArray.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ProcessGroups.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ProcessSingleton.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel::Algorithms {\nstruct Array;\nstruct Group;\nstruct Nodegroup;\nstruct Singleton;\n}  // namespace Parallel::Algorithms\n/// \\endcond\n\nnamespace Events {\n/*!\n * \\brief Event run on the DgElementArray that will monitor the memory usage of\n * parallel components in megabytes.\n *\n * \\details Given a list of parallel component names from Options, this will\n * calculate the memory usage of each component and write it to disk in the\n * reductions file under the `/MemoryMonitors/` group. The name of each file is\n * the `pretty_type::name` of each parallel component.\n *\n * The parallel components available to monitor are the ones defined in the\n * `component_list` type alias in the metavariables. In addition to these\n * components, you can also monitor the size of the GlobalCache. To see which\n * parallel components are available to monitor, request to monitor an invalid\n * parallel component (\"Blah\" for example) in the input file. An ERROR will\n * occur and a list of the available components to monitor will be printed.\n *\n * \\note Currently, the only Parallel::Algorithms::Array parallel component that\n * can be monitored is the DgElementArray itself.\n */\n\ntemplate <size_t Dim>\nclass MonitorMemory : public Event {\n private:\n  // Reduction data for arrays\n  using ReductionData = Parallel::ReductionData<\n      // Time\n      Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n      // Vector of total mem usage on each node\n      Parallel::ReductionDatum<std::vector<double>,\n                               funcl::ElementWise<funcl::Plus<>>>>;\n\n public:\n  explicit MonitorMemory(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MonitorMemory);  // NOLINT\n\n  struct ComponentsToMonitor {\n    using type =\n        Options::Auto<std::vector<std::string>, Options::AutoLabel::All>;\n    static constexpr Options::String help = {\n        \"Names of parallel components to monitor the memory usage of. If you'd \"\n        \"like to monitor all available parallel components, pass 'All' \"\n        \"instead.\"};\n  };\n\n  using options = tmpl::list<ComponentsToMonitor>;\n\n  static constexpr Options::String help =\n      \"Observe memory usage of parallel components.\";\n\n  MonitorMemory() = default;\n\n  template <typename Metavariables>\n  MonitorMemory(\n      const std::optional<std::vector<std::string>>& components_to_monitor,\n      const Options::Context& context, Metavariables /*meta*/);\n\n  using observed_reduction_data_tags =\n      observers::make_reduction_data_tags<tmpl::list<ReductionData>>;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<domain::Tags::Element<Dim>>;\n\n  template <typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(const ::Element<Dim>& element,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& observation_value) const;\n\n  using observation_registration_tags = tmpl::list<>;\n\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration() const {\n    return {};\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::unordered_set<std::string> components_to_monitor_{};\n};\n\n/// \\cond\ntemplate <size_t Dim>\nMonitorMemory<Dim>::MonitorMemory(CkMigrateMessage* msg) : Event(msg) {}\n\ntemplate <size_t Dim>\ntemplate <typename Metavariables>\nMonitorMemory<Dim>::MonitorMemory(\n    const std::optional<std::vector<std::string>>& components_to_monitor,\n    const Options::Context& context, Metavariables /*meta*/) {\n  using component_list = tmpl::push_back<typename Metavariables::component_list,\n                                         Parallel::GlobalCache<Metavariables>>;\n  std::unordered_map<std::string, std::string> existing_components{};\n  std::string str_component_list{};\n\n  tmpl::for_each<component_list>(\n      [&existing_components, &str_component_list](auto component_v) {\n        using component = tmpl::type_from<decltype(component_v)>;\n        const std::string component_name = pretty_type::name<component>();\n        const std::string chare_type_name =\n            pretty_type::name<typename component::chare_type>();\n        existing_components[component_name] = chare_type_name;\n        // Only Array we can monitor is DgElementArray\n        if (chare_type_name != \"Array\") {\n          str_component_list += \" - \" + component_name + \"\\n\";\n        } else if (component_name == \"DgElementArray\") {\n          str_component_list += \" - \" + component_name + \"\\n\";\n        }\n      });\n\n  // A list of names was specified\n  if (components_to_monitor.has_value()) {\n    for (const auto& component : *components_to_monitor) {\n      // Do some checks:\n      //  1. Make sure the components requested are viable components. This\n      //     protects against spelling errors.\n      //  2. Currently the only charm Array you can monitor the memory of is\n      //     the DgElementArray so enforce this.\n      if (existing_components.count(component) != 1) {\n        PARSE_ERROR(\n            context,\n            \"Cannot monitor memory usage of unknown parallel component '\"\n                << component\n                << \"'. Please choose from the existing parallel components:\\n\"\n                << str_component_list);\n      } else if (existing_components.at(component) == \"Array\" and\n                 component != \"DgElementArray\") {\n        PARSE_ERROR(\n            context,\n            \"Cannot monitor the '\"\n                << component\n                << \"' parallel component. Currently, the only Array parallel \"\n                   \"component allowed to be monitored is the \"\n                   \"DgElementArray.\");\n      }\n\n      components_to_monitor_.insert(component);\n    }\n  } else {\n    // 'All' was specified. Filter out Array components that are not the\n    // DgElementArray\n    for (const auto& [name, chare] : existing_components) {\n      if (chare != \"Array\") {\n        components_to_monitor_.insert(name);\n      } else if (name == \"DgElementArray\") {\n        components_to_monitor_.insert(name);\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename Metavariables, typename ArrayIndex,\n          typename ParallelComponent>\nvoid MonitorMemory<Dim>::operator()(\n    const ::Element<Dim>& element, Parallel::GlobalCache<Metavariables>& cache,\n    const ArrayIndex& array_index, const ParallelComponent* const /*meta*/,\n    const ObservationValue& observation_value) const {\n  using component_list = tmpl::push_back<typename Metavariables::component_list,\n                                         Parallel::GlobalCache<Metavariables>>;\n\n  tmpl::for_each<component_list>([this, &observation_value, &element, &cache,\n                                  &array_index](auto component_v) {\n    using component = tmpl::type_from<decltype(component_v)>;\n\n    // If we aren't monitoring this parallel component, then just exit now\n    if (components_to_monitor_.count(pretty_type::name<component>()) != 1) {\n      return;\n    }\n\n    // Certain components only need to be triggered once, so we have a special\n    // element designated to be the one that triggers memory monitoring, the\n    // 0th element.\n    const auto& element_id = element.id();\n    // Avoid GCC-7 compiler warning about unused variable (in the Array if\n    // constexpr branch)\n    [[maybe_unused]] const bool designated_element =\n        is_zeroth_element(element_id);\n\n    // If this is an array, this is run on every element. It has already\n    // been asserted in the constructor that the only Array the MemoryMonitor\n    // can monitor is the DgElementArray itself. If you want to monitor other\n    // Arrays, the implementation will need to be generalized.\n    if constexpr (Parallel::is_array_v<component>) {\n      auto& memory_monitor_proxy = Parallel::get_parallel_component<\n          mem_monitor::MemoryMonitor<Metavariables>>(cache);\n      auto array_element_proxy =\n          Parallel::get_parallel_component<component>(cache)[array_index];\n      const double size_in_bytes = static_cast<double>(\n          size_of_object_in_bytes(*Parallel::local(array_element_proxy)));\n      const double size_in_megabytes = size_in_bytes / 1.0e6;\n\n      // vector the size of the number of nodes we are running on. Set the\n      // 'my_node'th element of the vector to the size of this Element. Then\n      // when we reduce, we will have a vector with 'num_nodes' elements, each\n      // of which represents the total memory usage of all Elements on that\n      // node.\n      const size_t num_nodes = Parallel::number_of_nodes<size_t>(\n          *Parallel::local(array_element_proxy));\n      const size_t my_node =\n          Parallel::my_node<size_t>(*Parallel::local(array_element_proxy));\n      std::vector<double> data(num_nodes, 0.0);\n      data[my_node] = size_in_megabytes;\n\n      Parallel::contribute_to_reduction<\n          mem_monitor::ProcessArray<ParallelComponent>>(\n          ReductionData{observation_value.value, data}, array_element_proxy,\n          memory_monitor_proxy);\n    } else if constexpr (Parallel::is_singleton_v<component>) {\n      // If this is a singleton, we only run this once so use the designated\n      // element. Nothing to reduce with singletons so just call the simple\n      // action on the singleton\n      if (designated_element) {\n        auto& singleton_proxy =\n            Parallel::get_parallel_component<component>(cache);\n\n        Parallel::simple_action<mem_monitor::ProcessSingleton>(\n            singleton_proxy, observation_value.value);\n      }\n    } else if constexpr (Parallel::is_nodegroup_v<component> or\n                         Parallel::is_group_v<component>) {\n      // If this is a (node)group, call a simple action on each branch if on\n      // the designated element\n      if (designated_element) {\n        // Can't run simple actions on the cache so broadcast a specific entry\n        // method that will calculate the size and send it to the memory\n        // monitor\n        if constexpr (std::is_same_v<component,\n                                     Parallel::GlobalCache<Metavariables>>) {\n          auto cache_proxy = cache.get_this_proxy();\n\n          // This will be called on all branches of the GlobalCache\n          cache_proxy.compute_size_for_memory_monitor(observation_value.value);\n        } else {\n          // Groups and nodegroups share an action\n          auto& group_proxy =\n              Parallel::get_parallel_component<component>(cache);\n\n          // This will be called on all branches of the (node)group\n          Parallel::simple_action<mem_monitor::ProcessGroups>(\n              group_proxy, observation_value.value);\n        }\n      }\n    }\n  });\n}\n\ntemplate <size_t Dim>\nvoid MonitorMemory<Dim>::pup(PUP::er& p) {\n  Event::pup(p);\n  p | components_to_monitor_;\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID MonitorMemory<Dim>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveAdaptiveSteppingDiagnostics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Events/ObserveAdaptiveSteppingDiagnostics.hpp\"\n\nnamespace Events {\nPUP::able::PUP_ID ObserveAdaptiveSteppingDiagnostics::my_PUP_ID = 0;  // NOLINT\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveAdaptiveSteppingDiagnostics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct AdaptiveSteppingDiagnostics;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Events {\n/*!\n * \\brief %Observe diagnostics about adaptive time-stepping\n *\n * Writes reduction quantities:\n * - `%Time`\n * - `Number of slabs`\n * - `Number of slab size changes`\n * - `Total steps on all elements`\n * - `Number of LTS step changes`\n * - `Number of step rejections`\n *\n * The slab information is the same on all elements.  The step\n * information is summed over the elements.\n */\nclass ObserveAdaptiveSteppingDiagnostics : public Event {\n private:\n  using ReductionData = Parallel::ReductionData<\n      Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n      Parallel::ReductionDatum<uint64_t, funcl::AssertEqual<>>,\n      Parallel::ReductionDatum<uint64_t, funcl::AssertEqual<>>,\n      Parallel::ReductionDatum<uint64_t, funcl::Plus<>>,\n      Parallel::ReductionDatum<uint64_t, funcl::Plus<>>,\n      Parallel::ReductionDatum<uint64_t, funcl::Plus<>>>;\n\n public:\n  /// The name of the subfile inside the HDF5 file\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The name of the subfile inside the HDF5 file without an extension and \"\n        \"without a preceding '/'.\"};\n  };\n\n  /// \\cond\n  explicit ObserveAdaptiveSteppingDiagnostics(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveAdaptiveSteppingDiagnostics);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<SubfileName>;\n  static constexpr Options::String help =\n      \"Observe diagnostics about adaptive time-stepping\\n\"\n      \"\\n\"\n      \"Writes reduction quantities:\\n\"\n      \" - Time\\n\"\n      \" - Number of slabs\\n\"\n      \" - Number of slab size changes\\n\"\n      \" - Total steps on all elements\\n\"\n      \" - Number of LTS step changes\\n\"\n      \" - Number of step rejections\\n\"\n      \"\\n\"\n      \"The slab information is the same on all elements.  The step\\n\"\n      \"information is summed over the elements.\";\n\n  ObserveAdaptiveSteppingDiagnostics() = default;\n  explicit ObserveAdaptiveSteppingDiagnostics(const std::string& subfile_name)\n      : subfile_path_(\"/\" + subfile_name) {}\n\n  using observed_reduction_data_tags =\n      observers::make_reduction_data_tags<tmpl::list<ReductionData>>;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::AdaptiveSteppingDiagnostics>;\n\n  template <typename ArrayIndex, typename ParallelComponent,\n            typename Metavariables>\n  void operator()(const AdaptiveSteppingDiagnostics& diags,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    auto& local_observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<observers::Observer<Metavariables>>(\n            cache));\n    Parallel::simple_action<observers::Actions::ContributeReductionData>(\n        local_observer,\n        observers::ObservationId(observation_value.value,\n                                 subfile_path_ + \".dat\"),\n        Parallel::make_array_component_id<ParallelComponent>(array_index),\n        subfile_path_,\n        std::vector<std::string>{\n            observation_value.name, \"Number of slabs\",\n            \"Number of slab size changes\", \"Total steps on all elements\",\n            \"Number of LTS step changes\", \"Number of step rejections\"},\n        ReductionData{observation_value.value, diags.number_of_slabs,\n                      diags.number_of_slab_size_changes, diags.number_of_steps,\n                      diags.number_of_step_fraction_changes,\n                      diags.number_of_step_rejections});\n  }\n\n  using observation_registration_tags = tmpl::list<>;\n  std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  get_observation_type_and_key_for_registration() const {\n    return {observers::TypeOfObservation::Reduction,\n            observers::ObservationKey(subfile_path_ + \".dat\")};\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | subfile_path_;\n  }\n\n private:\n  std::string subfile_path_;\n};\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveAtExtremum.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/OptionalHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Events {\n/// @{\n/*!\n * \\brief Find the extremum of a Scalar<DataVector> over\n * all elements, as well as the value of other functions\n * at the location of that extremum.\n *\n *\n * Here is an example of an input file:\n *\n * \\snippet Test_ObserveAtExtremum.cpp input_file_examples\n *\n * \\par Array sections\n * This event supports sections (see `Parallel::Section`). Set the\n * `ArraySectionIdTag` template parameter to split up observations into subsets\n * of elements. The `observers::Tags::ObservationKey<ArraySectionIdTag>` must be\n * available in the DataBox. It identifies the section and is used as a suffix\n * for the path in the output file.\n */\ntemplate <typename ObservableTensorTagsList,\n          typename NonTensorComputeTagsList = tmpl::list<>,\n          typename ArraySectionIdTag = void>\nclass ObserveAtExtremum;\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag>\nclass ObserveAtExtremum<tmpl::list<ObservableTensorTags...>,\n                        tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag>\n    : public Event {\n private:\n  /// Reduction data will contain the time, and either the maximum\n  /// or the minimum of a function\n  template <typename MinMaxFunctional>\n  using ReductionData = Parallel::ReductionData<\n      // Observation value\n      Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n      // Maximum of first component of a vector\n      Parallel::ReductionDatum<std::vector<double>, MinMaxFunctional>>;\n\n  /// Information about the scalar whose extremum we search for,\n  /// the type of extremum, and the other tensors to observer at\n  /// that extremum\n  struct ObserveTensors {\n    static constexpr Options::String help = {\n        \"The tensor to extremize, and other tensors to observe.\"};\n\n    struct Name {\n      using type = std::string;\n      static constexpr Options::String help = {\n          \"The name of the scalar to extremize.\"};\n    };\n\n    struct ExtremumType {\n      using type = std::string;\n      static constexpr Options::String help = {\n          \"The type of extremum -- either Min or Max.\"};\n    };\n\n    struct AdditionalData {\n      using type = std::vector<std::string>;\n      static constexpr Options::String help = {\n          \"List of other tensors to observe at the extremum\"};\n    };\n\n    using options = tmpl::list<Name, ExtremumType, AdditionalData>;\n\n    ObserveTensors() = default;\n\n    ObserveTensors(std::string in_scalar, std::string in_extremum_type,\n                   std::vector<std::string> in_additional_data,\n                   const Options::Context& context = {});\n\n    std::string scalar_name;\n    std::string extremum_type;\n    std::vector<std::string> additional_data{};\n  };\n\n public:\n  /// The name of the subfile inside the HDF5 file\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The name of the subfile inside the HDF5 file without an extension and \"\n        \"without a preceding '/'.\"};\n  };\n  /// The scalar to extremize, and other tensors to observe at extremum\n  struct TensorsToObserve {\n    using type = ObserveTensors;\n    static constexpr Options::String help = {\n        \"Struct specifying the scalar to extremize, the type of extremum \"\n        \"and other tensors to observe at that extremum.\"};\n  };\n\n  explicit ObserveAtExtremum(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveAtExtremum);  // NOLINT\n\n  using options = tmpl::list<SubfileName, TensorsToObserve>;\n\n  static constexpr Options::String help =\n      \"Observe extremum of a scalar in the DataBox.\\n\"\n      \"\\n\"\n      \"Writes reduction quantities:\\n\"\n      \" * Observation value (e.g. Time or IterationId)\\n\"\n      \" * Extremum value of the desired scalar\\n\"\n      \" * Additional data at extremum\\n\";\n\n  ObserveAtExtremum() = default;\n\n  ObserveAtExtremum(std::string subfile_name,\n                    ObserveTensors observe_tensors);\n\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<ReductionData<funcl::Max<>>, ReductionData<funcl::Min<>>>>;\n\n  using compute_tags_for_observation_box =\n      tmpl::list<ObservableTensorTags..., NonTensorComputeTags...>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::ObservationBox>;\n\n  template <typename ComputeTagsList, typename DataBoxType,\n            typename Metavariables, size_t VolumeDim,\n            typename ParallelComponent>\n  void operator()(const ObservationBox<ComputeTagsList, DataBoxType>& box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<VolumeDim>& array_index,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& observation_value) const;\n\n  using observation_registration_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList>\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration(\n      const db::DataBox<DbTagsList>& box) const {\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (not section_observation_key.has_value()) {\n      return std::nullopt;\n    }\n    return {{observers::TypeOfObservation::Reduction,\n             observers::ObservationKey(\n                 subfile_path_ + section_observation_key.value() + \".dat\")}};\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::string subfile_path_;\n  std::string scalar_name_;\n  std::string extremum_type_;\n  std::vector<std::string> additional_tensor_names_{};\n};\n/// @}\n\n/// \\cond\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag>\nObserveAtExtremum<tmpl::list<ObservableTensorTags...>,\n                  tmpl::list<NonTensorComputeTags...>,\n                  ArraySectionIdTag>::ObserveAtExtremum(CkMigrateMessage* msg)\n    : Event(msg) {}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag>\nObserveAtExtremum<\n    tmpl::list<ObservableTensorTags...>, tmpl::list<NonTensorComputeTags...>,\n    ArraySectionIdTag>::ObserveAtExtremum(std::string subfile_name,\n                                          ObserveTensors observe_tensors)\n    : subfile_path_(\"/\" + subfile_name),\n      scalar_name_(std::move(observe_tensors.scalar_name)),\n      extremum_type_(std::move(observe_tensors.extremum_type)),\n      additional_tensor_names_(std::move(observe_tensors.additional_data)) {}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag>\nObserveAtExtremum<tmpl::list<ObservableTensorTags...>,\n                  tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag>::\n    ObserveTensors::ObserveTensors(std::string in_scalar,\n                                   std::string in_extremum_type,\n                                   std::vector<std::string> in_additional_data,\n                                   const Options::Context& context)\n    : scalar_name(std::move(in_scalar)),\n      extremum_type(std::move(in_extremum_type)),\n      additional_data(std::move(in_additional_data)) {\n  if (((scalar_name != db::tag_name<ObservableTensorTags>()) and ...)) {\n    PARSE_ERROR(\n        context, \"Tensor '\"\n                     << scalar_name << \"' is not known. Known tensors are: \"\n                     << ((db::tag_name<ObservableTensorTags>() + \",\") + ...));\n  }\n\n  tmpl::for_each<tmpl::list<ObservableTensorTags...>>(\n      [this, &context](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const std::string tensor_name = db::tag_name<tag>();\n        if (tensor_name == scalar_name) {\n          if constexpr (tt::is_a_v<std::optional, typename tag::type>) {\n            if (tag::type::value_type::rank() != 0) {\n              PARSE_ERROR(context,\n                          \"ObserveAtExtremum can only observe scalars!\");\n            }\n          } else if (tag::type::rank() != 0) {\n            PARSE_ERROR(context, \"ObserveAtExtremum can only observe scalars!\");\n          }\n        }\n      });\n\n  if (extremum_type != \"Max\" and extremum_type != \"Min\") {\n    PARSE_ERROR(context, \"Extremum type \" << extremum_type\n                                          << \" not recognized; use Max or Min\");\n  }\n  for (const auto& tensor : additional_data) {\n    if (((tensor != db::tag_name<ObservableTensorTags>()) and ...)) {\n      PARSE_ERROR(context,\n                  \"Tensor '\"\n                      << tensor << \"' is not known. Known tensors are: \"\n                      << ((db::tag_name<ObservableTensorTags>() + \",\") + ...));\n    }\n  }\n}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag>\ntemplate <typename ComputeTagsList, typename DataBoxType,\n          typename Metavariables, size_t VolumeDim, typename ParallelComponent>\nvoid ObserveAtExtremum<tmpl::list<ObservableTensorTags...>,\n                       tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag>::\noperator()(const ObservationBox<ComputeTagsList, DataBoxType>& box,\n           Parallel::GlobalCache<Metavariables>& cache,\n           const ElementId<VolumeDim>& array_index,\n           const ParallelComponent* const /*meta*/,\n           const ObservationValue& observation_value) const {\n  // Skip observation on elements that are not part of a section\n  const std::optional<std::string> section_observation_key =\n      observers::get_section_observation_key<ArraySectionIdTag>(box);\n  if (not section_observation_key.has_value()) {\n    return;\n  }\n\n  using tensor_tags = tmpl::list<ObservableTensorTags...>;\n\n  // Vector that will contain the local extremum, and the value\n  // of other tensors at that extremum\n  std::vector<double> data_to_reduce{};\n  // Vector containing a description of the data to be reduced.\n  std::vector<std::string> legend{observation_value.name};\n  // Location of the local extremum\n  size_t index_of_extremum = 0;\n  // First, look for local extremum of desired scalar\n  tmpl::for_each<tensor_tags>([this, &box, &data_to_reduce, &legend,\n                               &index_of_extremum](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const std::string tensor_name = db::tag_name<tag>();\n    if (tensor_name == scalar_name_) {\n      if (UNLIKELY(not has_value(get<tag>(box)))) {\n        ERROR(\"Cannot observe a norm of '\"\n              << tensor_name\n              << \"' because it is a std::optional and wasn't able to be \"\n                 \"computed. This can happen when you try to observe errors \"\n                 \"without an analytic solution.\");\n      }\n      const auto& scalar = value(get<tag>(box));\n      const auto components = get<1>(scalar.get_vector_of_data());\n      if (components.size() > 1) {\n        ERROR(\"Extremum should be taken on a scalar, yet we have \"\n              << components.size() << \" components in tensor \" << tensor_name);\n      }\n      for (size_t i = 1; i < components[0].size(); i++) {\n        if ((extremum_type_ == \"Max\" and\n             (components[0][i] > components[0][index_of_extremum])) or\n            (extremum_type_ == \"Min\" and\n             (components[0][i] < components[0][index_of_extremum]))) {\n          index_of_extremum = i;\n        }\n      }\n      data_to_reduce.push_back(components[0][index_of_extremum]);\n      if (extremum_type_ == \"Max\") {\n        legend.push_back(\"Max(\" + scalar_name_ + \")\");\n      } else {\n        legend.push_back(\"Min(\" + scalar_name_ + \")\");\n      }\n    }\n  });\n  // Now get value of additional tensors at extremum\n  tmpl::for_each<tensor_tags>([this, &box, &data_to_reduce, &legend,\n                               &index_of_extremum](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const std::string tensor_name = db::tag_name<tag>();\n    for (size_t i = 0; i < additional_tensor_names_.size(); ++i)\n      if (tensor_name == additional_tensor_names_[i]) {\n        if (UNLIKELY(not has_value(get<tag>(box)))) {\n          ERROR(\"Cannot observe a norm of '\"\n                << tensor_name\n                << \"' because it is a std::optional and wasn't able to be \"\n                   \"computed. This can happen when you try to observe errors \"\n                   \"without an analytic solution.\");\n        }\n        const auto& tensor = value(get<tag>(box));\n        const auto [component_names, components] = tensor.get_vector_of_data();\n        for (size_t j = 0; j < components.size(); j++) {\n          data_to_reduce.push_back(components[j][index_of_extremum]);\n          if (components.size() > 1) {\n            legend.push_back(\"At\" + scalar_name_ + extremum_type_ + \"(\" +\n                             tensor_name + \"_\" + component_names[j] + \")\");\n          } else {\n            legend.push_back(\"At\" + scalar_name_ + extremum_type_ + \"(\" +\n                             tensor_name + \")\");\n          }\n        }\n      }\n  });\n\n  // Send data to reduction observer\n  auto& local_observer = *Parallel::local_branch(\n      Parallel::get_parallel_component<observers::Observer<Metavariables>>(\n          cache));\n  const std::string subfile_path_with_suffix =\n      subfile_path_ + section_observation_key.value();\n\n  if (extremum_type_ == \"Max\") {\n    Parallel::simple_action<observers::Actions::ContributeReductionData>(\n        local_observer,\n        observers::ObservationId(observation_value.value,\n                                 subfile_path_with_suffix + \".dat\"),\n        Parallel::make_array_component_id<ParallelComponent>(array_index),\n        subfile_path_with_suffix, std::move(legend),\n        ReductionData<funcl::Max<>>{observation_value.value,\n                                    std::move(data_to_reduce)});\n  } else {\n    Parallel::simple_action<observers::Actions::ContributeReductionData>(\n        local_observer,\n        observers::ObservationId(observation_value.value,\n                                 subfile_path_with_suffix + \".dat\"),\n        Parallel::make_array_component_id<ParallelComponent>(array_index),\n        subfile_path_with_suffix, std::move(legend),\n        ReductionData<funcl::Min<>>{observation_value.value,\n                                    std::move(data_to_reduce)});\n  }\n}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag>\nvoid ObserveAtExtremum<tmpl::list<ObservableTensorTags...>,\n                       tmpl::list<NonTensorComputeTags...>,\n                       ArraySectionIdTag>::pup(PUP::er& p) {\n  Event::pup(p);\n  p | subfile_path_;\n  p | scalar_name_;\n  p | extremum_type_;\n  p | additional_tensor_names_;\n}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag>\nPUP::able::PUP_ID ObserveAtExtremum<tmpl::list<ObservableTensorTags...>,\n                                    tmpl::list<NonTensorComputeTags...>,\n                                    ArraySectionIdTag>::my_PUP_ID =\n    0;  // NOLINT\n/// \\endcond\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveConstantsPerElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Events/ObserveConstantsPerElement.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace dg::Events {\ntemplate <size_t VolumeDim>\nObserveConstantsPerElement<VolumeDim>::ObserveConstantsPerElement(\n    const std::string& subfile_name,\n    const ::FloatingPointType coordinates_floating_point_type,\n    const ::FloatingPointType floating_point_type)\n    : subfile_path_(\"/\" + subfile_name),\n      coordinates_floating_point_type_(coordinates_floating_point_type),\n      floating_point_type_(floating_point_type) {}\n\ntemplate <size_t VolumeDim>\nObserveConstantsPerElement<VolumeDim>::ObserveConstantsPerElement(\n    CkMigrateMessage* msg)\n    : Event(msg) {}\n\ntemplate <size_t VolumeDim>\nstd::optional<\n    std::pair<observers::TypeOfObservation, observers::ObservationKey>>\nObserveConstantsPerElement<\n    VolumeDim>::get_observation_type_and_key_for_registration() const {\n  return {{observers::TypeOfObservation::Volume,\n           observers::ObservationKey{subfile_path_ + \".vol\"}}};\n}\n\ntemplate <size_t VolumeDim>\nvoid ObserveConstantsPerElement<VolumeDim>::pup(PUP::er& p) {\n  Event::pup(p);\n  p | subfile_path_;\n  p | coordinates_floating_point_type_;\n  p | floating_point_type_;\n}\n\nnamespace {\ntemplate <typename T, size_t VolumeDim>\nvoid map_corners(const gsl::not_null<std::vector<TensorComponent>*> components,\n                 const double time,\n                 const domain::FunctionsOfTimeMap& functions_of_time,\n                 const Domain<VolumeDim>& domain,\n                 const ElementId<VolumeDim>& element_id) {\n  // Get the name from the tag even though we don't fetch it to make\n  // sure it's consistent with other observers.\n  using coordinates_tag =\n      ::domain::Tags::Coordinates<VolumeDim, Frame::Inertial>;\n  const std::string coordinates_name = db::tag_name<coordinates_tag>();\n\n  const ElementMap<VolumeDim, Frame::Inertial> map(\n      element_id, domain.blocks()[element_id.block_id()]);\n\n  auto corners = make_array<VolumeDim, T>(two_to_the(VolumeDim));\n  size_t index = 0;\n\n  const auto add_point =\n      [&](const tnsr::I<double, VolumeDim, Frame::ElementLogical>& point) {\n        const tnsr::I<double, VolumeDim, Frame::Inertial> mapped =\n            map(point, time, functions_of_time);\n        for (size_t i = 0; i < VolumeDim; ++i) {\n          gsl::at(corners, i)[index] = mapped.get(i);\n        }\n        ++index;\n      };\n\n  if constexpr (VolumeDim == 1) {\n    for (auto xi : {-1.0, 1.0}) {\n      add_point(tnsr::I<double, 1, Frame::ElementLogical>{{xi}});\n    }\n  } else if constexpr (VolumeDim == 2) {\n    for (auto eta : {-1.0, 1.0}) {\n      for (auto xi : {-1.0, 1.0}) {\n        add_point(tnsr::I<double, 2, Frame::ElementLogical>{{xi, eta}});\n      }\n    }\n  } else {\n    static_assert(VolumeDim == 3);\n    for (auto zeta : {-1.0, 1.0}) {\n      for (auto eta : {-1.0, 1.0}) {\n        for (auto xi : {-1.0, 1.0}) {\n          add_point(tnsr::I<double, 3, Frame::ElementLogical>{{xi, eta, zeta}});\n        }\n      }\n    }\n  }\n\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    const std::string component_name =\n        coordinates_name + coordinates_tag::type::component_suffix(i);\n    components->emplace_back(component_name, std::move(gsl::at(corners, i)));\n  }\n}\n}  // namespace\n\ntemplate <size_t VolumeDim>\nstd::vector<TensorComponent>\nObserveConstantsPerElement<VolumeDim>::allocate_and_insert_coords(\n    const size_t number_of_constants, const double time,\n    const domain::FunctionsOfTimeMap& functions_of_time,\n    const Domain<VolumeDim>& domain,\n    const ElementId<VolumeDim>& element_id) const {\n  std::vector<TensorComponent> components{};\n  components.reserve(VolumeDim + number_of_constants);\n\n  if (coordinates_floating_point_type_ == ::FloatingPointType::Float) {\n    map_corners<std::vector<float>>(&components, time, functions_of_time,\n                                    domain, element_id);\n  } else {\n    map_corners<DataVector>(&components, time, functions_of_time, domain,\n                            element_id);\n  }\n\n  return components;\n}\n\ntemplate <size_t VolumeDim>\nvoid ObserveConstantsPerElement<VolumeDim>::add_constant(\n    const gsl::not_null<std::vector<TensorComponent>*> components,\n    std::string name, const double value) const {\n  // We could save some space by using cell-centered data instead of\n  // the same value at each corner, but that would require also adding\n  // support to the rest of our visualization tools, and we'd still\n  // need the coordinates at the corners which would be most of the\n  // data.\n  constexpr auto num_corners = two_to_the(VolumeDim);\n\n  if (floating_point_type_ == ::FloatingPointType::Float) {\n    components->emplace_back(\n        std::move(name),\n        std::vector<float>(num_corners, static_cast<float>(value)));\n  } else {\n    components->emplace_back(std::move(name), DataVector(num_corners, value));\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) \\\n  template class ObserveConstantsPerElement<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace dg::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveConstantsPerElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Domain;\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace observers {\nclass ObservationKey;\nenum class TypeOfObservation;\n}  // namespace observers\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace dg::Events {\n/// \\brief Base class for Observers that write data that is constant within an\n/// Element\ntemplate <size_t VolumeDim>\nclass ObserveConstantsPerElement : public Event {\n public:\n  /// The name of the subfile inside the HDF5 file\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The name of the subfile inside the HDF5 file without an extension and \"\n        \"without a preceding '/'.\"};\n  };\n\n  /// The floating point type/precision with which to write the data to disk.\n  struct FloatingPointType {\n    static constexpr Options::String help =\n        \"The floating point type/precision with which to write the data to \"\n        \"disk.\";\n    using type = ::FloatingPointType;\n  };\n\n  /// The floating point type/precision with which to write the coordinates to\n  /// disk.\n  struct CoordinatesFloatingPointType {\n    static constexpr Options::String help =\n        \"The floating point type/precision with which to write the coordinates \"\n        \"to disk.\";\n    using type = ::FloatingPointType;\n  };\n\n  using options =\n      tmpl::list<SubfileName, CoordinatesFloatingPointType, FloatingPointType>;\n\n  ObserveConstantsPerElement() = default;\n\n  ObserveConstantsPerElement(\n      const std::string& subfile_name,\n      ::FloatingPointType coordinates_floating_point_type,\n      ::FloatingPointType floating_point_type);\n\n  /// \\cond\n  explicit ObserveConstantsPerElement(CkMigrateMessage* /*unused*/);\n  /// \\endcond\n\n  using observation_registration_tags = tmpl::list<>;\n\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration() const;\n\n  using is_ready_argument_tags = tmpl::list<::Tags::Time>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(const double time, Parallel::GlobalCache<Metavariables>& cache,\n                const ArrayIndex& array_index,\n                const Component* const component) const {\n    return ::domain::functions_of_time_are_ready_algorithm_callback<\n        ::domain::Tags::FunctionsOfTime, VolumeDim>(cache, array_index,\n                                                    component, time);\n  }\n\n  void pup(PUP::er& p) override;\n\n protected:\n  /// \\brief Creates the vector of tensor components that will be observed\n  /// and starts to fill it with the inertial coordinates\n  ///\n  /// \\details number_of_constants is the number of additional tensor components\n  /// that will be observed.  These are added by calling add_constant\n  std::vector<TensorComponent> allocate_and_insert_coords(\n      size_t number_of_constants, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const Domain<VolumeDim>& domain,\n      const ElementId<VolumeDim>& element_id) const;\n\n  /// \\brief Adds a TensorComponent to components with the given name and the\n  /// given value.\n  ///\n  /// \\details Should be called after allocate_and_insert_coords\n  void add_constant(gsl::not_null<std::vector<TensorComponent>*> components,\n                    std::string name, double value) const;\n\n  /// Observes the components as volume data\n  template <typename Metavariables, typename ParallelComponent>\n  void observe(std::vector<TensorComponent> components,\n               Parallel::GlobalCache<Metavariables>& cache,\n               const ElementId<VolumeDim>& element_id,\n               const ParallelComponent* /*component*/,\n               const ObservationValue& observation_value) const;\nprivate:\n  std::string subfile_path_;\n  ::FloatingPointType coordinates_floating_point_type_ =\n      ::FloatingPointType::Double;\n  ::FloatingPointType floating_point_type_ = ::FloatingPointType::Double;\n};\n\ntemplate <size_t VolumeDim>\ntemplate <typename Metavariables, typename ParallelComponent>\nvoid ObserveConstantsPerElement<VolumeDim>::observe(\n    std::vector<TensorComponent> components,\n    Parallel::GlobalCache<Metavariables>& cache,\n    const ElementId<VolumeDim>& element_id,\n    const ParallelComponent* const /*component*/,\n    const ObservationValue& observation_value) const {\n  const Mesh<VolumeDim> single_cell_mesh(2, Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto);\n  const Parallel::ArrayComponentId array_component_id{\n      std::add_pointer_t<ParallelComponent>{nullptr},\n      Parallel::ArrayIndex<ElementId<VolumeDim>>{element_id}};\n  ElementVolumeData element_volume_data{element_id, std::move(components),\n                                        single_cell_mesh};\n  observers::ObservationId observation_id{observation_value.value,\n                                          subfile_path_ + \".vol\"};\n\n  observers::contribute_volume_data<\n      not Parallel::is_nodegroup_v<ParallelComponent>>(\n      cache, std::move(observation_id), subfile_path_, array_component_id,\n      std::move(element_volume_data));\n}\n\n}  // namespace dg::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveDataBox.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Events/ObserveDataBox.hpp\"\n\n#include <fstream>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace Events {\nObserveDataBox::ObserveDataBox(\n    std::optional<std::string> file_name_for_tag_names)\n    : file_name_for_tag_names_(std::move(file_name_for_tag_names)) {}\n\nObserveDataBox::ObserveDataBox(CkMigrateMessage* m) : Event{m} {}\n\nvoid ObserveDataBox::pup(PUP::er& p) {\n  Event::pup(p);\n  p | file_name_for_tag_names_;\n}\n\ntemplate <size_t VolumeDim>\nvoid ObserveDataBox::impl(const db::Access& box_access,\n                          const ElementId<VolumeDim>& array_index,\n                          const ObservationValue& /*observation_value*/) const {\n  if (is_zeroth_element(array_index)) {\n    if (file_name_for_tag_names_.has_value()) {\n      std::ofstream of{file_name_for_tag_names_.value()};\n      of << box_access.print_tags() << \"\\n\";\n      of.close();\n    }\n  }\n}\n\nPUP::able::PUP_ID ObserveDataBox::my_PUP_ID = 0;  // NOLINT\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(r, data)                                                   \\\n  template void ObserveDataBox::impl<DIM(data)>(                               \\\n      const db::Access&, const ElementId<DIM(data)>&, const ObservationValue&) \\\n      const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveDataBox.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <map>\n#include <pup.h>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/DistributedObject.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Events {\n\nnamespace detail {\n\nstruct map_add {\n  std::map<std::string, size_t> operator()(\n      std::map<std::string, size_t> map_1,\n      const std::map<std::string, size_t>& map_2) {\n    for (const auto& [key, value] : map_2) {\n      map_1.at(key) += value;\n    }\n    return map_1;\n  }\n};\n\nusing ReductionType = Parallel::ReductionData<\n    // Time\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n    // Map of total mem usage in MB per item in DataBoxes\n    Parallel::ReductionDatum<std::map<std::string, size_t>, map_add>>;\n\ntemplate <typename ContributingComponent>\nstruct ReduceDataBoxSize {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const double time,\n                    const std::map<std::string, size_t>& item_sizes) {\n    auto& observer_writer_proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n    const std::string subfile_name =\n        \"/DataBoxSizeInMb/\" + pretty_type::name<ContributingComponent>();\n    std::vector<std::string> legend;\n    legend.reserve(item_sizes.size() + 1);\n    legend.emplace_back(\"Time\");\n    std::vector<double> columns;\n    columns.reserve(item_sizes.size() + 1);\n    columns.emplace_back(time);\n    const double scaling = 1.0 / 1048576.0;  // so size is in MB\n    for (const auto& [name, size] : item_sizes) {\n      legend.emplace_back(name);\n      columns.emplace_back(scaling * static_cast<double>(size));\n    }\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        // Node 0 is always the writer\n        observer_writer_proxy[0], subfile_name, legend,\n        std::make_tuple(columns));\n  }\n};\n\nstruct ContributeDataBoxSize {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index, const double time) {\n    const auto& my_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index];\n    auto& target_proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n    const auto item_sizes = box.size_of_items();\n    if constexpr (Parallel::is_singleton_v<ParallelComponent>) {\n      Parallel::simple_action<ReduceDataBoxSize<ParallelComponent>>(\n          target_proxy[0], time, item_sizes);\n    } else {\n      Parallel::contribute_to_reduction<ReduceDataBoxSize<ParallelComponent>>(\n          ReductionType{time, item_sizes}, my_proxy, target_proxy[0]);\n    }\n  }\n};\n}  // namespace detail\n\n/// \\brief Event that will collect the size in MBs used by each DataBox item on\n/// each parallel component.\n///\n/// \\details The data will be written to disk in the reductions file under the\n/// `/DataBoxSizeInMb/` group. The name of each file is the `pretty_type::name`\n/// of each parallel component.  There will be a column for each item in the\n/// DataBox that is not a subitem or reference item.\nclass ObserveDataBox : public Event {\n  struct DoNotWrite {};\n\n public:\n  /// \\cond\n  explicit ObserveDataBox(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveDataBox);  // NOLINT\n  /// \\endcond\n\n  struct WriteTagNamesToFile {\n    using type = ::Options::Auto<std::string, DoNotWrite>;\n    static constexpr Options::String help = {\n        \"The text file to write the names of the DataBox tags to.\"};\n  };\n\n  using options = tmpl::list<WriteTagNamesToFile>;\n  static constexpr Options::String help = {\n      \"Observe size (in MB) of each item (except reference items) in each \"\n      \"DataBox\"};\n\n  ObserveDataBox() = default;\n  explicit ObserveDataBox(std::optional<std::string> file_name_for_tag_names);\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::ObservationBox>;\n\n  template <typename DataBoxType, size_t VolumeDim, typename ParallelComponent,\n            typename Metavariables, typename ComputeTagsList>\n  void operator()(const ObservationBox<DataBoxType, ComputeTagsList>& box,\n                  Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ElementId<VolumeDim>& array_index,\n                  const ParallelComponent* /*meta*/,\n                  const ObservationValue& observation_value) const;\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  template <size_t VolumeDim>\n  void impl(const db::Access& box_access,\n            const ElementId<VolumeDim>& array_index,\n            const ObservationValue& observation_value) const;\n\n  std::optional<std::string> file_name_for_tag_names_;\n};\n\ntemplate <typename DataBoxType, size_t VolumeDim, typename ParallelComponent,\n          typename Metavariables, typename ComputeTagsList>\nvoid ObserveDataBox::operator()(\n    const ObservationBox<DataBoxType, ComputeTagsList>& box,\n    Parallel::GlobalCache<Metavariables>& cache,\n    const ElementId<VolumeDim>& array_index,\n    const ParallelComponent* const /*meta*/,\n    const ObservationValue& observation_value) const {\n  if (is_zeroth_element(array_index)) {\n    using component_list =\n        tmpl::push_back<typename Metavariables::component_list>;\n    tmpl::for_each<component_list>([&observation_value,\n                                    &cache](auto component_v) {\n      using component = tmpl::type_from<decltype(component_v)>;\n      auto& target_proxy = Parallel::get_parallel_component<component>(cache);\n      Parallel::simple_action<detail::ContributeDataBoxSize>(\n          target_proxy, observation_value.value);\n    });\n  }\n  impl(box.databox(), array_index, observation_value);\n}\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveFields.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <initializer_list>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataBox/ValidateSelection.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"Domain/Structure/BlockGroups.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/OptionalHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Mesh;\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace dg {\nnamespace Events {\n/// \\cond\ntemplate <size_t VolumeDim, typename Tensors,\n          typename NonTensorComputeTagsList = tmpl::list<>,\n          typename ArraySectionIdTag = void>\nclass ObserveFields;\n/// \\endcond\n\n/*!\n * \\ingroup DiscontinuousGalerkinGroup\n * \\brief %Observe volume tensor fields.\n *\n * A class that writes volume quantities to an h5 file during the simulation.\n * The observed quantitites are specified in the `VariablesToObserve` option.\n * Any `Tensor` in the `db::DataBox` can be observed but must be listed in the\n * `Tensors` template parameter. Any additional compute tags that hold a\n * `Tensor` can also be added to the `Tensors` template parameter. Finally,\n * `Variables` and other non-tensor compute tags can be listed in the\n * `NonTensorComputeTags` to facilitate observing. Note that the\n * `InertialCoordinates` are always observed.\n *\n * The user may specify an `interpolation_mesh` to which the\n * data is interpolated.\n *\n * \\note The `NonTensorComputeTags` are intended to be used for `Variables`\n * compute tags like `Tags::DerivCompute`\n *\n * \\par Array sections\n * This event supports sections (see `Parallel::Section`). Set the\n * `ArraySectionIdTag` template parameter to split up observations into subsets\n * of elements. The `observers::Tags::ObservationKey<ArraySectionIdTag>` must be\n * available in the DataBox. It identifies the section and is used as a suffix\n * for the path in the output file.\n */\ntemplate <size_t VolumeDim, typename... Tensors,\n          typename... NonTensorComputeTags, typename ArraySectionIdTag>\nclass ObserveFields<VolumeDim, tmpl::list<Tensors...>,\n                    tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag>\n    : public Event {\n public:\n  /// The name of the subfile inside the HDF5 file\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The name of the subfile inside the HDF5 file without an extension and \"\n        \"without a preceding '/'.\"};\n  };\n\n  /// \\cond\n  explicit ObserveFields(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveFields);  // NOLINT\n  /// \\endcond\n\n  struct VariablesToObserve {\n    static constexpr Options::String help = \"Subset of variables to observe\";\n    using type = std::vector<std::string>;\n    static size_t lower_bound_on_size() { return 1; }\n  };\n\n  struct InterpolateToMesh {\n    using type = Options::Auto<Mesh<VolumeDim>, Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        \"An optional mesh to which the variables are interpolated. This mesh \"\n        \"specifies any number of collocation points, basis, and quadrature on \"\n        \"which the observed quantities are evaluated. If no mesh is given, the \"\n        \"results will be evaluated on the mesh the simulation runs on. The \"\n        \"user may add several ObserveField Events e.g. with and without an \"\n        \"interpolating mesh to output the data both on the original mesh and \"\n        \"on a new mesh.\";\n  };\n\n  struct ProjectToMesh {\n    using type = Options::Auto<Mesh<VolumeDim>, Options::AutoLabel::None>;\n    static constexpr Options::String help =\n        \"An optional mesh to which the variables are projected. This mesh \"\n        \"must use a Legendre basis (Gauss or Gauss-Lobatto quadrature). This \"\n        \"option is mutually exclusive with InterpolateToMesh. Projection \"\n        \"truncates the fields in modal space but works only between two \"\n        \"Legendre meshes, whereas interpolation works with any mesh but is not \"\n        \"a clean truncation and therefore picks up an aliasing error.\";\n  };\n\n  /// The floating point type/precision with which to write the data to disk.\n  ///\n  /// Must be specified once for all data or individually for each variable\n  /// being observed.\n  struct FloatingPointTypes {\n    static constexpr Options::String help =\n        \"The floating point type/precision with which to write the data to \"\n        \"disk.\\n\\n\"\n        \"Must be specified once for all data or individually  for each \"\n        \"variable being observed.\";\n    using type = std::vector<FloatingPointType>;\n    static size_t upper_bound_on_size() { return sizeof...(Tensors); }\n    static size_t lower_bound_on_size() { return 1; }\n  };\n\n  /// The floating point type/precision with which to write the coordinates to\n  /// disk.\n  struct CoordinatesFloatingPointType {\n    static constexpr Options::String help =\n        \"The floating point type/precision with which to write the coordinates \"\n        \"to disk.\";\n    using type = FloatingPointType;\n  };\n\n  /// \\brief A list of block or group names on which to observe.\n  ///\n  /// Set to `All` to observe everywhere.\n  struct BlocksToObserve {\n    using type =\n        Options::Auto<std::vector<std::string>, Options::AutoLabel::All>;\n    static constexpr Options::String help = {\n        \"A list of block and group names on which to observe.\"};\n  };\n\n  using options = tmpl::list<SubfileName, CoordinatesFloatingPointType,\n                             FloatingPointTypes, VariablesToObserve,\n                             BlocksToObserve, InterpolateToMesh, ProjectToMesh>;\n\n  static constexpr Options::String help =\n      \"Observe volume tensor fields.\\n\"\n      \"\\n\"\n      \"Writes volume quantities:\\n\"\n      \" * InertialCoordinates\\n\"\n      \" * Tensors listed in the 'VariablesToObserve' option\\n\";\n\n  ObserveFields() = default;\n\n  // The dependency isn't available through Options\n  ObserveFields(\n      const std::string& subfile_name,\n      FloatingPointType coordinates_floating_point_type,\n      const std::vector<FloatingPointType>& floating_point_types,\n      const std::vector<std::string>& variables_to_observe,\n      std::optional<std::vector<std::string>> active_block_or_block_groups = {},\n      std::optional<Mesh<VolumeDim>> interpolation_mesh = {},\n      std::optional<Mesh<VolumeDim>> projection_mesh = {},\n      std::optional<std::string> dependency = {},\n      const Options::Context& context = {});\n\n  ObserveFields(\n      const std::string& subfile_name,\n      FloatingPointType coordinates_floating_point_type,\n      const std::vector<FloatingPointType>& floating_point_types,\n      const std::vector<std::string>& variables_to_observe,\n      std::optional<std::vector<std::string>> active_block_or_block_groups = {},\n      std::optional<Mesh<VolumeDim>> interpolation_mesh = {},\n      std::optional<Mesh<VolumeDim>> projection_mesh = {},\n      const Options::Context& context = {})\n      : ObserveFields(subfile_name, coordinates_floating_point_type,\n                      floating_point_types, variables_to_observe,\n                      std::move(active_block_or_block_groups),\n                      std::move(interpolation_mesh), std::move(projection_mesh),\n                      std::nullopt, context) {}\n\n  using compute_tags_for_observation_box =\n      tmpl::list<Tensors..., NonTensorComputeTags...>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::ObservationBox,\n                                   ::Events::Tags::ObserverMesh<VolumeDim>>;\n\n  template <typename DataBoxType, typename ComputeTagsList,\n            typename Metavariables, typename ParallelComponent>\n  void operator()(const ObservationBox<DataBoxType, ComputeTagsList>& box,\n                  const Mesh<VolumeDim>& mesh,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<VolumeDim>& array_index,\n                  const ParallelComponent* const component,\n                  const ObservationValue& observation_value) const {\n    if (not active_block(get<domain::Tags::Domain<VolumeDim>>(box),\n                         array_index)) {\n      return;\n    }\n    // Skip observation on elements that are not part of a section\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (not section_observation_key.has_value()) {\n      return;\n    }\n    call_operator_impl(subfile_path_ + *section_observation_key,\n                       variables_to_observe_, interpolation_mesh_,\n                       projection_mesh_, mesh, box, cache, array_index,\n                       component, observation_value, dependency_);\n  }\n\n  // We factor out the work into a static member function so it can  be shared\n  // with other field observing events, like the one that deals with DG-subcell\n  // where there are two grids. This is to avoid copy-pasting all of the code.\n  template <typename DataBoxType, typename ComputeTagsList,\n            typename Metavariables, typename ParallelComponent>\n  static void call_operator_impl(\n      const std::string& subfile_path,\n      const std::unordered_map<std::string, FloatingPointType>&\n          variables_to_observe,\n      const std::optional<Mesh<VolumeDim>>& interpolation_mesh,\n      const std::optional<Mesh<VolumeDim>>& projection_mesh,\n      const Mesh<VolumeDim>& mesh,\n      const ObservationBox<DataBoxType, ComputeTagsList>& box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<VolumeDim>& element_id,\n      const ParallelComponent* const /*meta*/,\n      const ObservationValue& observation_value,\n      const std::optional<std::string>& dependency) {\n    const bool do_projection = projection_mesh.has_value();\n    const Mesh<VolumeDim>& target_mesh =\n        do_projection ? projection_mesh.value()\n                      : interpolation_mesh.value_or(mesh);\n    if (do_projection) {\n      for (size_t d = 0; d < VolumeDim; ++d) {\n        if (mesh.basis(d) != Spectral::Basis::Legendre or\n            target_mesh.basis(d) != Spectral::Basis::Legendre) {\n          ERROR(\"ProjectToMesh requires Legendre basis.\");\n        }\n        if ((mesh.quadrature(d) != Spectral::Quadrature::Gauss and\n             mesh.quadrature(d) != Spectral::Quadrature::GaussLobatto) or\n            (target_mesh.quadrature(d) != Spectral::Quadrature::Gauss and\n             target_mesh.quadrature(d) != Spectral::Quadrature::GaussLobatto)) {\n          ERROR(\"ProjectToMesh requires Gauss or Gauss-Lobatto quadrature.\");\n        }\n      }\n    }\n    // If no interpolation_mesh is provided, the interpolation is essentially\n    // ignored by the RegularGridInterpolant except for a single copy.\n    const intrp::RegularGrid interpolant(mesh, target_mesh);\n    const std::optional<\n        std::array<std::reference_wrapper<const Matrix>, VolumeDim>>\n        projection_matrices =\n            do_projection ? std::make_optional(Spectral::p_projection_matrices(\n                                mesh, target_mesh))\n                          : std::nullopt;\n\n    // Remove tensor types, only storing individual components.\n    std::vector<TensorComponent> components;\n    // This is larger than we need if we are only observing some\n    // tensors, but that's not a big deal and calculating the correct\n    // size is nontrivial.\n    components.reserve(alg::accumulate(\n        std::initializer_list<size_t>{\n            std::decay_t<decltype(value(typename Tensors::type{}))>::size()...},\n        0_st));\n\n    const auto record_tensor_component_impl =\n        [&components](DataVector&& tensor_component,\n                      const FloatingPointType floating_point_type,\n                      const std::string& component_name) {\n          if (floating_point_type == FloatingPointType::Float) {\n            components.emplace_back(component_name,\n                                    std::vector<float>{tensor_component.begin(),\n                                                       tensor_component.end()});\n          } else {\n            components.emplace_back(component_name,\n                                    std::move(tensor_component));\n          }\n        };\n\n    const auto record_tensor_components_impl =\n        [&record_tensor_component_impl, &interpolant, &projection_matrices,\n         &mesh, do_projection](const auto& tensor,\n                               const FloatingPointType floating_point_type,\n                               const std::string& tag_name) {\n          using TensorType = std::decay_t<decltype(tensor)>;\n          using VectorType = typename TensorType::type;\n          for (size_t i = 0; i < tensor.size(); ++i) {\n            auto tensor_component =\n                do_projection ? apply_matrices(*projection_matrices, tensor[i],\n                                               mesh.extents())\n                              : interpolant.interpolate(tensor[i]);\n            const std::string component_name =\n                tag_name + tensor.component_suffix(i);\n            if constexpr (std::is_same_v<VectorType, ComplexDataVector>) {\n              record_tensor_component_impl(real(tensor_component),\n                                           floating_point_type,\n                                           \"Re(\" + component_name + \")\");\n              record_tensor_component_impl(imag(tensor_component),\n                                           floating_point_type,\n                                           \"Im(\" + component_name + \")\");\n            } else {\n              record_tensor_component_impl(std::move(tensor_component),\n                                           floating_point_type, component_name);\n            }\n          }\n        };\n    const auto record_tensor_components =\n        [&box, &record_tensor_components_impl,\n         &variables_to_observe](const auto tensor_tag_v) {\n          using tensor_tag = tmpl::type_from<decltype(tensor_tag_v)>;\n          const std::string tag_name = db::tag_name<tensor_tag>();\n          if (const auto var_to_observe = variables_to_observe.find(tag_name);\n              var_to_observe != variables_to_observe.end()) {\n            const auto& tensor = get<tensor_tag>(box);\n            if (not has_value(tensor)) {\n              // This will only print a warning the first time it's called on a\n              // node.\n              [[maybe_unused]] static bool t =\n                  ObserveFields::print_warning_about_optional<tensor_tag>();\n              return;\n            }\n            const auto floating_point_type = var_to_observe->second;\n            record_tensor_components_impl(value(tensor), floating_point_type,\n                                          tag_name);\n          }\n        };\n    EXPAND_PACK_LEFT_TO_RIGHT(record_tensor_components(tmpl::type_<Tensors>{}));\n\n    const Parallel::ArrayComponentId array_component_id{\n        std::add_pointer_t<ParallelComponent>{nullptr},\n        Parallel::ArrayIndex<ElementId<VolumeDim>>{element_id}};\n    ElementVolumeData element_volume_data{element_id, std::move(components),\n                                          target_mesh};\n    observers::ObservationId observation_id{observation_value.value,\n                                            subfile_path + \".vol\"};\n\n    observers::contribute_volume_data<\n        not Parallel::is_nodegroup_v<ParallelComponent>>(\n        cache, std::move(observation_id), subfile_path, array_component_id,\n        std::move(element_volume_data), dependency);\n  }\n\n  using observation_registration_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList>\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration(\n      const db::DataBox<DbTagsList>& box) const {\n    if (not active_block(db::get<domain::Tags::Domain<VolumeDim>>(box),\n                         db::get<domain::Tags::Element<VolumeDim>>(box).id())) {\n      return std::nullopt;\n    }\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (not section_observation_key.has_value()) {\n      return std::nullopt;\n    }\n    return {{observers::TypeOfObservation::Volume,\n             observers::ObservationKey{\n                 subfile_path_ + section_observation_key.value() + \".vol\"}}};\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | subfile_path_;\n    p | variables_to_observe_;\n    p | active_block_or_block_groups_;\n    p | interpolation_mesh_;\n    p | projection_mesh_;\n    p | dependency_;\n  }\n\n private:\n  template <typename Tag>\n  static bool print_warning_about_optional() {\n    Parallel::printf(\n        \"Warning: ObserveFields is trying to dump the tag %s \"\n        \"but it is stored as a std::optional and has not been \"\n        \"evaluated. This most commonly occurs when you are \"\n        \"trying to either observe an analytic solution or errors when \"\n        \"no analytic solution is available.\\n\",\n        db::tag_name<Tag>());\n    return false;\n  }\n\n  bool active_block(const Domain<VolumeDim>& domain,\n                    const ElementId<VolumeDim>& element_id) const {\n    if (not active_block_or_block_groups_.has_value()) {\n      return true;\n    }\n    const std::unordered_set<std::string> block_names =\n        domain::expand_block_groups_to_block_names(\n            active_block_or_block_groups_.value(), domain.block_names(),\n            domain.block_groups());\n    return alg::found(block_names,\n                      domain.blocks().at(element_id.block_id()).name());\n  }\n\n  std::string subfile_path_;\n  std::unordered_map<std::string, FloatingPointType> variables_to_observe_{};\n  std::optional<std::vector<std::string>> active_block_or_block_groups_{};\n  std::optional<Mesh<VolumeDim>> interpolation_mesh_{};\n  std::optional<Mesh<VolumeDim>> projection_mesh_{};\n  std::optional<std::string> dependency_;\n};\n\ntemplate <size_t VolumeDim, typename... Tensors,\n          typename... NonTensorComputeTags, typename ArraySectionIdTag>\nObserveFields<VolumeDim, tmpl::list<Tensors...>,\n              tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag>::\n    ObserveFields(\n        const std::string& subfile_name,\n        const FloatingPointType coordinates_floating_point_type,\n        const std::vector<FloatingPointType>& floating_point_types,\n        const std::vector<std::string>& variables_to_observe,\n        std::optional<std::vector<std::string>> active_block_or_block_groups,\n        std::optional<Mesh<VolumeDim>> interpolation_mesh,\n        std::optional<Mesh<VolumeDim>> projection_mesh,\n        std::optional<std::string> dependency, const Options::Context& context)\n    : subfile_path_(\"/\" + subfile_name),\n      variables_to_observe_([&context, &floating_point_types,\n                             &variables_to_observe]() {\n        if (floating_point_types.size() != 1 and\n            floating_point_types.size() != variables_to_observe.size()) {\n          PARSE_ERROR(context, \"The number of floating point types specified (\"\n                                   << floating_point_types.size()\n                                   << \") must be 1 or the number of variables \"\n                                      \"specified for observing (\"\n                                   << variables_to_observe.size() << \")\");\n        }\n        std::unordered_map<std::string, FloatingPointType> result{};\n        for (size_t i = 0; i < variables_to_observe.size(); ++i) {\n          result[variables_to_observe[i]] = floating_point_types.size() == 1\n                                                ? floating_point_types[0]\n                                                : floating_point_types[i];\n          ASSERT(\n              result.at(variables_to_observe[i]) == FloatingPointType::Float or\n                  result.at(variables_to_observe[i]) ==\n                      FloatingPointType::Double,\n              \"Floating point type for variable '\"\n                  << variables_to_observe[i]\n                  << \"' must be either Float or Double.\");\n        }\n        return result;\n      }()),\n      active_block_or_block_groups_(std::move(active_block_or_block_groups)),\n      interpolation_mesh_(interpolation_mesh),\n      projection_mesh_(projection_mesh),\n      dependency_(std::move(dependency)) {\n  if (interpolation_mesh_.has_value() and projection_mesh_.has_value()) {\n    PARSE_ERROR(context,\n                \"Specify only one of InterpolateToMesh or ProjectToMesh.\");\n  }\n  if (projection_mesh_.has_value()) {\n    const auto& proj_mesh = projection_mesh_.value();\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      if (proj_mesh.basis(d) != Spectral::Basis::Legendre) {\n        PARSE_ERROR(context, \"ProjectToMesh requires Legendre basis.\");\n      }\n      if (proj_mesh.quadrature(d) != Spectral::Quadrature::Gauss and\n          proj_mesh.quadrature(d) != Spectral::Quadrature::GaussLobatto) {\n        PARSE_ERROR(\n            context,\n            \"ProjectToMesh requires Gauss or Gauss-Lobatto quadrature.\");\n      }\n    }\n  }\n  ASSERT(\n      (... or (db::tag_name<Tensors>() == \"InertialCoordinates\")),\n      \"There is no tag with name 'InertialCoordinates' specified \"\n      \"for the observer. Please make sure you specify a tag in the 'Tensors' \"\n      \"list that has the 'db::tag_name()' 'InertialCoordinates'.\");\n  db::validate_selection<tmpl::list<Tensors...>>(variables_to_observe, context);\n  variables_to_observe_[\"InertialCoordinates\"] =\n      coordinates_floating_point_type;\n}\n\n/// \\cond\ntemplate <size_t VolumeDim, typename... Tensors,\n          typename... NonTensorComputeTags, typename ArraySectionIdTag>\nPUP::able::PUP_ID ObserveFields<VolumeDim, tmpl::list<Tensors...>,\n                                tmpl::list<NonTensorComputeTags...>,\n                                ArraySectionIdTag>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace Events\n}  // namespace dg\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveNorms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Events/ObserveNorms.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace Events::ObserveNorms_impl {\nvoid check_norm_is_observable(const std::string& tensor_name,\n                              const bool tag_has_value) {\n  if (UNLIKELY(not tag_has_value)) {\n    ERROR(\"Cannot observe a norm of '\"\n          << tensor_name\n          << \"' because it is a std::optional and wasn't able to be \"\n             \"computed. This can happen when you try to observe errors \"\n             \"without an analytic solution.\");\n  }\n}\n\ntemplate <size_t Dim>\nvoid fill_norm_values_and_names(\n    const gsl::not_null<std::unordered_map<\n        std::string, std::pair<std::vector<double>, std::vector<std::string>>>*>\n        norm_values_and_names,\n    const std::pair<std::vector<std::string>, std::vector<DataVector>>&\n        names_and_components,\n    const Mesh<Dim>& mesh, const DataVector& det_jacobian,\n    const std::string& tensor_name, const std::string& tensor_norm_type,\n    const std::string& tensor_component, const size_t number_of_points) {\n  auto& [values, names] = (*norm_values_and_names)[tensor_norm_type];\n  const auto& components = names_and_components.second;\n  if (components[0].size() != number_of_points) {\n    ERROR(\"The number of grid points of the mesh is \"\n          << number_of_points << \" but the tensor '\" << tensor_name << \"' has \"\n          << components[0].size()\n          << \" points. This means you're computing norms of tensors over \"\n             \"different grids, which will give the wrong answer for \"\n             \"norms that use the grid points.\");\n  }\n\n  using std::abs;\n  const auto& component_names = names_and_components.first;\n  if (tensor_component == \"Individual\") {\n    for (size_t storage_index = 0; storage_index < component_names.size();\n         ++storage_index) {\n      if (tensor_norm_type == \"Max\") {\n        values.push_back(max(components[storage_index]));\n      } else if (tensor_norm_type == \"Min\") {\n        values.push_back(min(components[storage_index]));\n      } else if (tensor_norm_type == \"L1Norm\") {\n        values.push_back(alg::accumulate(abs(components[storage_index]), 0.0));\n      } else if (tensor_norm_type == \"L1IntegralNorm\") {\n        values.push_back(definite_integral(\n            abs(components[storage_index]) * det_jacobian, mesh));\n      } else if (tensor_norm_type == \"L2Norm\") {\n        values.push_back(\n            alg::accumulate(square(components[storage_index]), 0.0));\n      } else if (tensor_norm_type == \"L2IntegralNorm\") {\n        values.push_back(definite_integral(\n            square(components[storage_index]) * det_jacobian, mesh));\n      } else if (tensor_norm_type == \"VolumeIntegral\") {\n        values.push_back(\n            definite_integral(components[storage_index] * det_jacobian, mesh));\n      }\n      names.push_back(\n          tensor_norm_type + \"(\" +\n          (component_names.size() == 1\n               ? tensor_name\n               : (tensor_name + \"_\" + component_names[storage_index])) +\n          \")\");\n    }\n  } else if (tensor_component == \"Sum\") {\n    double value = 0.0;\n    if (tensor_norm_type == \"Max\") {\n      value = std::numeric_limits<double>::min();\n    } else if (tensor_norm_type == \"Min\") {\n      value = std::numeric_limits<double>::max();\n    }\n    for (size_t storage_index = 0; storage_index < component_names.size();\n         ++storage_index) {\n      if (tensor_norm_type == \"Max\") {\n        value = std::max(value, max(components[storage_index]));\n      } else if (tensor_norm_type == \"Min\") {\n        value = std::min(value, min(components[storage_index]));\n      } else if (tensor_norm_type == \"L1Norm\") {\n        value += alg::accumulate(abs(components[storage_index]), 0.0);\n      } else if (tensor_norm_type == \"L1IntegralNorm\") {\n        value += definite_integral(\n            abs(components[storage_index]) * det_jacobian, mesh);\n      } else if (tensor_norm_type == \"L2Norm\") {\n        value += alg::accumulate(square(components[storage_index]), 0.0);\n      } else if (tensor_norm_type == \"L2IntegralNorm\") {\n        value += definite_integral(\n            square(components[storage_index]) * det_jacobian, mesh);\n      } else if (tensor_norm_type == \"VolumeIntegral\") {\n        value +=\n            definite_integral(components[storage_index] * det_jacobian, mesh);\n      }\n    }\n\n    names.push_back(tensor_norm_type + \"(\" + tensor_name + \")\");\n    values.push_back(value);\n  }\n}\n\nstd::pair<std::vector<std::string>, std::vector<DataVector>>\nsplit_complex_vector_of_data(\n    std::pair<std::vector<std::string>, std::vector<DataVector>>&&\n        names_and_components) {\n  return names_and_components;\n}\n\nstd::pair<std::vector<std::string>, std::vector<DataVector>>\nsplit_complex_vector_of_data(\n    const std::pair<std::vector<std::string>, std::vector<ComplexDataVector>>&\n        names_and_components) {\n  const auto& [names, components] = names_and_components;\n  std::vector<std::string> result_names{};\n  std::vector<DataVector> result_components{};\n  result_names.reserve(2 * names.size());\n  result_components.reserve(2 * names.size());\n  for (size_t i = 0; i < names.size(); ++i) {\n    result_names.emplace_back(\"Re(\" + names[i] + \")\");\n    result_components.emplace_back(real(components[i]));\n    result_names.emplace_back(\"Im(\" + names[i] + \")\");\n    result_components.emplace_back(imag(components[i]));\n  }\n  return std::make_pair(std::move(result_names), std::move(result_components));\n}\n\n}  // namespace Events::ObserveNorms_impl\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void Events::ObserveNorms_impl::fill_norm_values_and_names(     \\\n      gsl::not_null<std::unordered_map<                                    \\\n          std::string,                                                     \\\n          std::pair<std::vector<double>, std::vector<std::string>>>*>      \\\n          norm_values_and_names,                                           \\\n      const std::pair<std::vector<std::string>, std::vector<DataVector>>&  \\\n          names_and_components,                                            \\\n      const Mesh<DIM(data)>& mesh, const DataVector& det_jacobian,         \\\n      const std::string& tensor_name, const std::string& tensor_norm_type, \\\n      const std::string& tensor_component, size_t number_of_points);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveNorms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/OptionalHelpers.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Events {\n/// @{\n/*!\n * \\brief Compute norms of tensors in the DataBox and write them to disk.\n *\n * The L1 norm is computed as the mean absolute value, so\n *\n * \\f{align*}{\n * L_1(u)=\\frac{1}{N}\\sum_{i=0}^{N-1} |u_i|\n * \\f}\n *\n * where \\f$N\\f$ is the number of grid points.\n *\n * The L2 norm is computed as the RMS, so\n *\n * \\f{align*}{\n * L_2(u)=\\sqrt{\\frac{1}{N}\\sum_{i=0}^{N-1} u_i^2}\n * \\f}\n *\n * The norm can be taken for each individual component, or summed over\n * components. For the max/min it is then the max/min over all components, while\n * for the L1 norm we have (for a 3d vector, 2d and 1d are similar)\n *\n * \\f{align*}{\n * L_1(v^k)=\\frac{1}{N}\\sum_{i=0}^{N-1} \\left[|v^x_i| + |v^y_i|\n *          + |v^z_i|\\right]\n * \\f}\n *\n * and for the L2 norm\n *\n * \\f{align*}{\n * L_2(v^k)=\\sqrt{\\frac{1}{N}\\sum_{i=0}^{N-1} \\left[(v^x_i)^2 + (v^y_i)^2\n *          + (v^z_i)^2\\right]}\n * \\f}\n *\n * The L1 integral norm is:\n *\n * \\begin{equation}\n * L_{1,\\mathrm{int}}(v^k) = \\frac{1}{V}\\int_\\Omega \\left[\n *   |v^x_i| + |v^y_i| + |v^z_i|\\right] \\mathrm{d}V\n * \\end{equation}\n *\n * The L2 integral norm is:\n *\n * \\begin{equation}\n * L_{2,\\mathrm{int}}(v^k) = \\sqrt{\\frac{1}{V}\\int_\\Omega \\left[\n *   (v^x_i)^2 + (v^y_i)^2 + (v^z_i)^2\\right] \\mathrm{d}V}\n * \\end{equation}\n *\n * where $V=\\int_\\Omega$ is the volume of the entire domain in inertial\n * coordinates.\n *\n * VolumeIntegral only computes the volume integral without any normalization.\n *\n * Here is an example of an input file:\n *\n * \\snippet Test_ObserveNorms.cpp input_file_examples\n *\n * \\note The `NonTensorComputeTags` are intended to be used for `Variables`\n * compute tags like `Tags::DerivCompute`\n *\n * \\par Array sections\n * This event supports sections (see `Parallel::Section`). Set the\n * `ArraySectionIdTag` template parameter to split up observations into subsets\n * of elements. The `observers::Tags::ObservationKey<ArraySectionIdTag>` must be\n * available in the DataBox. It identifies the section and is used as a suffix\n * for the path in the output file.\n *\n * \\par Option name\n * The `OptionName` template parameter is used to give the event a name in the\n * input file. If it is not specified, the name defaults to \"ObserveNorms\". If\n * you have multiple `ObserveNorms` events in the input file, you must specify a\n * unique name for each one. This can happen, for example, if you want to\n * observe norms the full domain and also over a section of the domain.\n */\ntemplate <typename ObservableTensorTagsList,\n          typename NonTensorComputeTagsList = tmpl::list<>,\n          typename ArraySectionIdTag = void, typename OptionName = void>\nclass ObserveNorms;\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag, typename OptionName>\nclass ObserveNorms<tmpl::list<ObservableTensorTags...>,\n                   tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag,\n                   OptionName> : public Event {\n private:\n  struct ObserveTensor {\n    static constexpr Options::String help = {\n        \"The tensor to reduce, and how to reduce it.\"};\n\n    struct Name {\n      using type = std::string;\n      static constexpr Options::String help = {\n          \"The name of the tensor to observe.\"};\n    };\n    struct NormType {\n      using type = std::string;\n      static constexpr Options::String help = {\n          \"The type of norm to use. Must be one of Max, Min, L1Norm, \"\n          \"L1IntegralNorm, L2Norm, L2IntegralNorm, or VolumeIntegral.\"};\n    };\n    struct Components {\n      using type = std::string;\n      static constexpr Options::String help = {\n          \"How to handle tensor components. Must be Individual or Sum.\"};\n    };\n\n    using options = tmpl::list<Name, NormType, Components>;\n\n    ObserveTensor() = default;\n\n    ObserveTensor(std::string in_tensor, std::string in_norm_type,\n                  std::string in_components,\n                  const Options::Context& context = {});\n\n    std::string tensor{};\n    std::string norm_type{};\n    std::string components{};\n  };\n\n  using ReductionData = Parallel::ReductionData<\n      // Observation value\n      Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n      // Number of grid points\n      Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n      // Total volume\n      Parallel::ReductionDatum<double, funcl::Plus<>>,\n      // Max\n      Parallel::ReductionDatum<std::vector<double>,\n                               funcl::ElementWise<funcl::Max<>>>,\n      // Min\n      Parallel::ReductionDatum<std::vector<double>,\n                               funcl::ElementWise<funcl::Min<>>>,\n      // L1Norm\n      Parallel::ReductionDatum<\n          std::vector<double>, funcl::ElementWise<funcl::Plus<>>,\n          funcl::ElementWise<funcl::Divides<>>, std::index_sequence<1>>,\n      // L1IntegralNorm\n      Parallel::ReductionDatum<\n          std::vector<double>, funcl::ElementWise<funcl::Plus<>>,\n          funcl::ElementWise<funcl::Divides<>>, std::index_sequence<2>>,\n      // L2Norm\n      Parallel::ReductionDatum<\n          std::vector<double>, funcl::ElementWise<funcl::Plus<>>,\n          funcl::ElementWise<funcl::Sqrt<funcl::Divides<>>>,\n          std::index_sequence<1>>,\n      // L2IntegralNorm\n      Parallel::ReductionDatum<\n          std::vector<double>, funcl::ElementWise<funcl::Plus<>>,\n          funcl::ElementWise<funcl::Sqrt<funcl::Divides<>>>,\n          std::index_sequence<2>>,\n      // VolumeIntegral\n      Parallel::ReductionDatum<std::vector<double>,\n                               funcl::ElementWise<funcl::Plus<>>>>;\n\n public:\n  static std::string name() {\n    if constexpr (std::is_same_v<OptionName, void>) {\n      return \"ObserveNorms\";\n    } else {\n      return pretty_type::name<OptionName>();\n    }\n  }\n\n  /// The name of the subfile inside the HDF5 file\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The name of the subfile inside the HDF5 file without an extension and \"\n        \"without a preceding '/'.\"};\n  };\n  /// The tensor to observe and how to do the reduction\n  struct TensorsToObserve {\n    using type = std::vector<ObserveTensor>;\n    static constexpr Options::String help = {\n        \"List specifying each tensor to observe and how it is reduced.\"};\n  };\n\n  explicit ObserveNorms(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveNorms);  // NOLINT\n\n  using options = tmpl::list<SubfileName, TensorsToObserve>;\n\n  static constexpr Options::String help =\n      \"Observe norms of tensors in the DataBox.\\n\"\n      \"\\n\"\n      \"You can choose the norm type for each observation. Note that 'L1Norm'\\n\"\n      \"(mean absolute value) and 'L2Norm' (root mean square) emphasize \"\n      \"regions\\n\"\n      \"of the domain with many grid points, whereas 'L1IntegralNorm' and\\n\"\n      \"'L2IntegralNorm' emphasize regions of the domain with large volume.\\n\"\n      \"Choose wisely! When in doubt, try the 'L2Norm' first.\\n\"\n      \"\\n\"\n      \"Writes reduction quantities:\\n\"\n      \" * Observation value (e.g. Time or IterationId)\\n\"\n      \" * NumberOfPoints = total number of points in the domain\\n\"\n      \" * Volume = total volume of the domain in inertial coordinates\\n\"\n      \" * Max values\\n\"\n      \" * Min values\\n\"\n      \" * L1-norm values\\n\"\n      \" * L1 integral norm values\\n\"\n      \" * L2-norm values\\n\"\n      \" * L2 integral norm values\\n\"\n      \" * Volume integral values\\n\";\n\n  ObserveNorms() = default;\n\n  ObserveNorms(const std::string& subfile_name,\n               const std::vector<ObserveTensor>& observe_tensors);\n\n  using observed_reduction_data_tags =\n      observers::make_reduction_data_tags<tmpl::list<ReductionData>>;\n\n  using compute_tags_for_observation_box =\n      tmpl::list<ObservableTensorTags..., NonTensorComputeTags...>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<::Tags::ObservationBox>;\n\n  template <typename TensorToObserveTag, typename ComputeTagsList,\n            typename DataBoxType, size_t Dim>\n  void observe_norms_impl(\n      gsl::not_null<\n          std::unordered_map<std::string, std::pair<std::vector<double>,\n                                                    std::vector<std::string>>>*>\n          norm_values_and_names,\n      const ObservationBox<ComputeTagsList, DataBoxType>& box,\n      const Mesh<Dim>& mesh, const DataVector& det_jacobian,\n      size_t number_of_points) const;\n\n  template <typename ComputeTagsList, typename DataBoxType,\n            typename Metavariables, size_t VolumeDim,\n            typename ParallelComponent>\n  void operator()(const ObservationBox<ComputeTagsList, DataBoxType>& box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ElementId<VolumeDim>& array_index,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& observation_value) const;\n\n  using observation_registration_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTagsList>\n  std::optional<\n      std::pair<observers::TypeOfObservation, observers::ObservationKey>>\n  get_observation_type_and_key_for_registration(\n      const db::DataBox<DbTagsList>& box) const {\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (not section_observation_key.has_value()) {\n      return std::nullopt;\n    }\n    return {{observers::TypeOfObservation::Reduction,\n             observers::ObservationKey(\n                 subfile_path_ + section_observation_key.value() + \".dat\")}};\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::string subfile_path_;\n  std::vector<std::string> tensor_names_{};\n  std::vector<std::string> tensor_norm_types_{};\n  std::vector<std::string> tensor_components_{};\n};\n/// @}\n\n/// \\cond\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag, typename OptionName>\nObserveNorms<tmpl::list<ObservableTensorTags...>,\n             tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag,\n             OptionName>::ObserveNorms(CkMigrateMessage* msg)\n    : Event(msg) {}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag, typename OptionName>\nObserveNorms<tmpl::list<ObservableTensorTags...>,\n             tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag,\n             OptionName>::ObserveNorms(const std::string& subfile_name,\n                                       const std::vector<ObserveTensor>&\n                                           observe_tensors)\n    : subfile_path_(\"/\" + subfile_name) {\n  tensor_names_.reserve(observe_tensors.size());\n  tensor_norm_types_.reserve(observe_tensors.size());\n  tensor_components_.reserve(observe_tensors.size());\n  for (const auto& observe_tensor : observe_tensors) {\n    tensor_names_.push_back(observe_tensor.tensor);\n    tensor_norm_types_.push_back(observe_tensor.norm_type);\n    tensor_components_.push_back(observe_tensor.components);\n  }\n}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag, typename OptionName>\nObserveNorms<\n    tmpl::list<ObservableTensorTags...>, tmpl::list<NonTensorComputeTags...>,\n    ArraySectionIdTag,\n    OptionName>::ObserveTensor::ObserveTensor(std::string in_tensor,\n                                              std::string in_norm_type,\n                                              std::string in_components,\n                                              const Options::Context& context)\n    : tensor(std::move(in_tensor)),\n      norm_type(std::move(in_norm_type)),\n      components(std::move(in_components)) {\n  if (((tensor != db::tag_name<ObservableTensorTags>()) and ...)) {\n    PARSE_ERROR(\n        context, \"Tensor '\"\n                     << tensor << \"' is not known. Known tensors are: \"\n                     << ((db::tag_name<ObservableTensorTags>() + \",\") + ...));\n  }\n  if (norm_type != \"Max\" and norm_type != \"Min\" and norm_type != \"L1Norm\" and\n      norm_type != \"L1IntegralNorm\" and norm_type != \"L2Norm\" and\n      norm_type != \"L2IntegralNorm\" and norm_type != \"VolumeIntegral\") {\n    PARSE_ERROR(\n        context,\n        \"NormType must be one of Max, Min, L1Norm, L1IntegralNorm, L2Norm, \"\n        \"L2IntegralNorm, or VolumeIntegral not \"\n            << norm_type);\n  }\n  if (components != \"Individual\" and components != \"Sum\") {\n    PARSE_ERROR(context,\n                \"Components must be Individual or Sum, not \" << components);\n  }\n}\n\n// implementation of ObserveNorms::operator() factored out to save on compile\n// time and compile memory\nnamespace ObserveNorms_impl {\nvoid check_norm_is_observable(const std::string& tensor_name,\n                              bool tag_has_value);\n\ntemplate <size_t Dim>\nvoid fill_norm_values_and_names(\n    gsl::not_null<std::unordered_map<\n        std::string, std::pair<std::vector<double>, std::vector<std::string>>>*>\n        norm_values_and_names,\n    const std::pair<std::vector<std::string>, std::vector<DataVector>>&\n        names_and_components,\n    const Mesh<Dim>& mesh, const DataVector& det_jacobian,\n    const std::string& tensor_name, const std::string& tensor_norm_type,\n    const std::string& tensor_component, size_t number_of_points);\n\n// Expand complex data into real and imaginary parts, or just forward real data\nstd::pair<std::vector<std::string>, std::vector<DataVector>>\nsplit_complex_vector_of_data(\n    std::pair<std::vector<std::string>, std::vector<DataVector>>&&\n        names_and_components);\nstd::pair<std::vector<std::string>, std::vector<DataVector>>\nsplit_complex_vector_of_data(\n    const std::pair<std::vector<std::string>, std::vector<ComplexDataVector>>&\n        names_and_components);\n}  // namespace ObserveNorms_impl\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag, typename OptionName>\ntemplate <typename TensorToObserveTag, typename ComputeTagsList,\n          typename DataBoxType, size_t Dim>\nvoid ObserveNorms<tmpl::list<ObservableTensorTags...>,\n                  tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag,\n                  OptionName>::\n    observe_norms_impl(\n        const gsl::not_null<std::unordered_map<\n            std::string,\n            std::pair<std::vector<double>, std::vector<std::string>>>*>\n            norm_values_and_names,\n        const ObservationBox<ComputeTagsList, DataBoxType>& box,\n        const Mesh<Dim>& mesh, const DataVector& det_jacobian,\n        const size_t number_of_points) const {\n  const std::string tensor_name = db::tag_name<TensorToObserveTag>();\n  for (size_t i = 0; i < tensor_names_.size(); ++i) {\n    if (tensor_name == tensor_names_[i]) {\n      ObserveNorms_impl::check_norm_is_observable(\n          tensor_name, has_value(get<TensorToObserveTag>(box)));\n      ObserveNorms_impl::fill_norm_values_and_names(\n          norm_values_and_names,\n          ObserveNorms_impl::split_complex_vector_of_data(\n              value(get<TensorToObserveTag>(box)).get_vector_of_data()),\n          mesh, det_jacobian, tensor_name, tensor_norm_types_[i],\n          tensor_components_[i], number_of_points);\n    }\n  }\n}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag, typename OptionName>\ntemplate <typename ComputeTagsList, typename DataBoxType,\n          typename Metavariables, size_t VolumeDim, typename ParallelComponent>\nvoid ObserveNorms<tmpl::list<ObservableTensorTags...>,\n                  tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag,\n                  OptionName>::\noperator()(const ObservationBox<ComputeTagsList, DataBoxType>& box,\n           Parallel::GlobalCache<Metavariables>& cache,\n           const ElementId<VolumeDim>& array_index,\n           const ParallelComponent* const /*meta*/,\n           const ObservationValue& observation_value) const {\n  // Skip observation on elements that are not part of a section\n  const std::optional<std::string> section_observation_key =\n      observers::get_section_observation_key<ArraySectionIdTag>(box);\n  if (not section_observation_key.has_value()) {\n    return;\n  }\n\n  const auto& mesh = get<::Events::Tags::ObserverMesh<VolumeDim>>(box);\n  const auto det_jacobian = [&box, &mesh]() -> DataVector {\n    if constexpr (VolumeDim == 3 and\n                  db::tag_is_retrievable_v<::Events::Tags::ObserverCoordinates<\n                                               VolumeDim, Frame::Inertial>,\n                                           std::decay_t<decltype(box)>>) {\n      if (mesh.basis(2) == Spectral::Basis::Cartoon) {\n        if (mesh.quadrature(2) == Spectral::Quadrature::SphericalSymmetry) {\n          // Spherical Symmetry, needs x^2 cartesian to spherical jacobian\n          return square(get<0>(get<::Events::Tags::ObserverCoordinates<\n                                   VolumeDim, Frame::Inertial>>(box))) /\n                 get(get<::Events::Tags::ObserverDetInvJacobian<\n                         Frame::ElementLogical, Frame::Inertial>>(box));\n        } else {\n          // Axial Symmetry, needs x cartesian to cylindrical jacobian\n          ASSERT(mesh.quadrature(2) == Spectral::Quadrature::AxialSymmetry,\n                 \"Unexpected quadrature \" << mesh.quadrature(2)\n                                          << \" (expected AxialSymmetry)\");\n          return get<0>(get<::Events::Tags::ObserverCoordinates<\n                            VolumeDim, Frame::Inertial>>(box)) /\n                 get(get<::Events::Tags::ObserverDetInvJacobian<\n                         Frame::ElementLogical, Frame::Inertial>>(box));\n        }\n      } else {\n        return 1. / get(get<::Events::Tags::ObserverDetInvJacobian<\n                            Frame::ElementLogical, Frame::Inertial>>(box));\n      }\n    } else {\n      (void)mesh;\n      return 1. / get(get<::Events::Tags::ObserverDetInvJacobian<\n                          Frame::ElementLogical, Frame::Inertial>>(box));\n    }\n  }();\n  const size_t number_of_points = mesh.number_of_grid_points();\n  const double local_volume = definite_integral(det_jacobian, mesh);\n\n  std::unordered_map<std::string,\n                     std::pair<std::vector<double>, std::vector<std::string>>>\n      norm_values_and_names{};\n  // Loop over ObservableTensorTags and see if it was requested to be observed.\n  // This approach allows us to delay evaluating any compute tags until they're\n  // actually needed for observing.\n  (observe_norms_impl<ObservableTensorTags>(\n       make_not_null(&norm_values_and_names), box, mesh, det_jacobian,\n       number_of_points),\n   ...);\n\n  // Concatenate the legend info together.\n  std::vector<std::string> legend{observation_value.name, \"NumberOfPoints\",\n                                  \"Volume\"};\n  legend.insert(legend.end(), norm_values_and_names[\"Max\"].second.begin(),\n                norm_values_and_names[\"Max\"].second.end());\n  legend.insert(legend.end(), norm_values_and_names[\"Min\"].second.begin(),\n                norm_values_and_names[\"Min\"].second.end());\n  legend.insert(legend.end(), norm_values_and_names[\"L1Norm\"].second.begin(),\n                norm_values_and_names[\"L1Norm\"].second.end());\n  legend.insert(legend.end(),\n                norm_values_and_names[\"L1IntegralNorm\"].second.begin(),\n                norm_values_and_names[\"L1IntegralNorm\"].second.end());\n  legend.insert(legend.end(), norm_values_and_names[\"L2Norm\"].second.begin(),\n                norm_values_and_names[\"L2Norm\"].second.end());\n  legend.insert(legend.end(),\n                norm_values_and_names[\"L2IntegralNorm\"].second.begin(),\n                norm_values_and_names[\"L2IntegralNorm\"].second.end());\n  legend.insert(legend.end(),\n                norm_values_and_names[\"VolumeIntegral\"].second.begin(),\n                norm_values_and_names[\"VolumeIntegral\"].second.end());\n\n  const std::string subfile_path_with_suffix =\n      subfile_path_ + section_observation_key.value();\n  // Send data to reduction observer\n  auto& local_observer = *Parallel::local_branch(\n      Parallel::get_parallel_component<\n          tmpl::conditional_t<Parallel::is_nodegroup_v<ParallelComponent>,\n                              observers::ObserverWriter<Metavariables>,\n                              observers::Observer<Metavariables>>>(cache));\n  observers::ObservationId observation_id{observation_value.value,\n                                          subfile_path_with_suffix + \".dat\"};\n  Parallel::ArrayComponentId array_component_id{\n      std::add_pointer_t<ParallelComponent>{nullptr},\n      Parallel::ArrayIndex<ElementId<VolumeDim>>(array_index)};\n  ReductionData reduction_data{\n      observation_value.value,\n      number_of_points,\n      local_volume,\n      std::move(norm_values_and_names[\"Max\"].first),\n      std::move(norm_values_and_names[\"Min\"].first),\n      std::move(norm_values_and_names[\"L1Norm\"].first),\n      std::move(norm_values_and_names[\"L1IntegralNorm\"].first),\n      std::move(norm_values_and_names[\"L2Norm\"].first),\n      std::move(norm_values_and_names[\"L2IntegralNorm\"].first),\n      std::move(norm_values_and_names[\"VolumeIntegral\"].first)};\n\n  if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {\n    Parallel::threaded_action<\n        observers::ThreadedActions::CollectReductionDataOnNode>(\n        local_observer, std::move(observation_id),\n        std::move(array_component_id), subfile_path_with_suffix,\n        std::move(legend), std::move(reduction_data));\n  } else {\n    Parallel::simple_action<observers::Actions::ContributeReductionData>(\n        local_observer, std::move(observation_id),\n        std::move(array_component_id), subfile_path_with_suffix,\n        std::move(legend), std::move(reduction_data));\n  }\n}\n\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag, typename OptionName>\nvoid ObserveNorms<tmpl::list<ObservableTensorTags...>,\n                  tmpl::list<NonTensorComputeTags...>, ArraySectionIdTag,\n                  OptionName>::pup(PUP::er& p) {\n  Event::pup(p);\n  p | subfile_path_;\n  p | tensor_names_;\n  p | tensor_norm_types_;\n  p | tensor_components_;\n}\n\n// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)\ntemplate <typename... ObservableTensorTags, typename... NonTensorComputeTags,\n          typename ArraySectionIdTag, typename OptionName>\nPUP::able::PUP_ID ObserveNorms<tmpl::list<ObservableTensorTags...>,\n                               tmpl::list<NonTensorComputeTags...>,\n                               ArraySectionIdTag, OptionName>::my_PUP_ID = 0;\n// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)\n/// \\endcond\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.hpp\"\n\n#include <cstddef>\n#include <sstream>\n#include <string>\n\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace Events::detail {\nstd::string FormatTimeOutput::operator()(\n    const double time, const size_t /* num_points */,\n    const double /* slab_size */, const double /* min_time_step */,\n    const double /* max_time_step */, const double /* effective_time_step */,\n    const double min_wall_time, const double max_wall_time) const {\n  std::stringstream ss;\n  ss << \"Simulation time: \" << std::to_string(time)\n     << \"\\n  Wall time: \" << sys::pretty_wall_time(min_wall_time) << \" (min) - \"\n     << sys::pretty_wall_time(max_wall_time) << \" (max)\\n\";\n  return ss.str();\n}\n\nvoid FormatTimeOutput::pup(PUP::er& /*p*/) {}\n}  // namespace Events::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveTimeStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Protocols/ReductionDataFormatter.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass TimeDelta;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\nstruct TimeStep;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Events {\nnamespace detail {\nusing ObserveTimeStepReductionData = Parallel::ReductionData<\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n    Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n    Parallel::ReductionDatum<double, funcl::Min<>>,\n    Parallel::ReductionDatum<double, funcl::Max<>>,\n    Parallel::ReductionDatum<\n        double, funcl::Plus<>,\n        funcl::Divides<funcl::Literal<1, double>, funcl::Divides<>>,\n        std::index_sequence<1>>,\n    Parallel::ReductionDatum<double, funcl::Min<>>,\n    Parallel::ReductionDatum<double, funcl::Max<>>>;\n\nstruct FormatTimeOutput\n    : tt::ConformsTo<observers::protocols::ReductionDataFormatter> {\n  using reduction_data = ObserveTimeStepReductionData;\n  std::string operator()(double time, size_t num_points, double slab_size,\n                         double min_time_step, double max_time_step,\n                         double effective_time_step, double min_wall_time,\n                         double max_wall_time) const;\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p);\n};\n}  // namespace detail\n\n/*!\n * \\brief %Observe the size of the time steps.\n *\n * Writes reduction quantities:\n * - `%Time`\n * - `NumberOfPoints`\n * - `%Slab size`\n * - `Minimum time step`\n * - `Maximum time step`\n * - `Effective time step`\n *\n * The effective time step is the step size of a global-time-stepping\n * method that would perform a similar amount of work.  This is the\n * harmonic mean of the step size over all grid points:\n *\n * \\f{equation}\n * (\\Delta t)_{\\text{eff}}^{-1} =\n * \\frac{\\sum_{i \\in \\text{points}} (\\Delta t)_i^{-1}}{N_{\\text{points}}}.\n * \\f}\n *\n * This corresponds to averaging the number of steps per unit time\n * taken by all points.\n *\n * All values are reported as positive numbers, even for backwards\n * evolutions.\n */\ntemplate <typename System>\nclass ObserveTimeStep : public Event {\n private:\n  using ReductionData = Events::detail::ObserveTimeStepReductionData;\n\n public:\n  /// The name of the subfile inside the HDF5 file\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The name of the subfile inside the HDF5 file without an extension and \"\n        \"without a preceding '/'.\"};\n  };\n\n  struct PrintTimeToTerminal {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Whether to print the time to screen.\"};\n  };\n\n  struct ObservePerCore {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"Also write the data per-core in a file per-node.\"};\n  };\n\n  /// \\cond\n  explicit ObserveTimeStep(CkMigrateMessage* /*m*/);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveTimeStep);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<SubfileName, PrintTimeToTerminal, ObservePerCore>;\n  static constexpr Options::String help =\n      \"Observe the size of the time steps.\\n\"\n      \"\\n\"\n      \"Writes reduction quantities:\\n\"\n      \"- Time\\n\"\n      \"- NumberOfPoints\\n\"\n      \"- Slab size\\n\"\n      \"- Minimum time step\\n\"\n      \"- Maximum time step\\n\"\n      \"- Effective time step\\n\"\n      \"\\n\"\n      \"The effective time step is the step size of a global-time-stepping\\n\"\n      \"method that would perform a similar amount of work.\\n\"\n      \"\\n\"\n      \"All values are reported as positive numbers, even for backwards\\n\"\n      \"evolutions.\";\n\n  ObserveTimeStep();\n  ObserveTimeStep(const std::string& subfile_name, bool output_time,\n                  bool observe_per_core);\n\n  using observed_reduction_data_tags =\n      observers::make_reduction_data_tags<tmpl::list<ReductionData>>;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  // We obtain the grid size from the variables, rather than the mesh,\n  // so that this observer is not DG-specific.\n  using argument_tags =\n      tmpl::list<::Tags::TimeStep, typename System::variables_tag>;\n\n  template <typename ArrayIndex, typename ParallelComponent,\n            typename Metavariables>\n  void operator()(const TimeDelta& time_step,\n                  const typename System::variables_tag::type& variables,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    auto [observation_id, legend, reduction_data, formatter] =\n        assemble_data(time_step, variables, observation_value);\n\n    auto& local_observer = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            tmpl::conditional_t<Parallel::is_nodegroup_v<ParallelComponent>,\n                                observers::ObserverWriter<Metavariables>,\n                                observers::Observer<Metavariables>>>(cache));\n\n    Parallel::ArrayComponentId array_component_id{\n        std::add_pointer_t<ParallelComponent>{nullptr},\n        Parallel::ArrayIndex<ArrayIndex>(array_index)};\n\n    if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {\n      const std::optional<int> observe_with_core_id =\n          observe_per_core_\n              ? std::make_optional(Parallel::my_node<int>(local_observer))\n              : std::nullopt;\n      Parallel::threaded_action<\n          observers::ThreadedActions::CollectReductionDataOnNode>(\n          local_observer, std::move(observation_id),\n          std::move(array_component_id), subfile_path_, std::move(legend),\n          std::move(reduction_data), std::move(formatter),\n          observe_with_core_id);\n    } else {\n      Parallel::simple_action<observers::Actions::ContributeReductionData>(\n          local_observer, std::move(observation_id),\n          std::move(array_component_id), subfile_path_, std::move(legend),\n          std::move(reduction_data), std::move(formatter), observe_per_core_);\n    }\n  }\n\n  using observation_registration_tags = tmpl::list<>;\n  std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  get_observation_type_and_key_for_registration() const;\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  auto assemble_data(const TimeDelta& time_step,\n                     const typename System::variables_tag::type& variables,\n                     const ObservationValue& observation_value) const\n      -> std::tuple<observers::ObservationId, std::vector<std::string>,\n                    ReductionData,\n                    std::optional<Events::detail::FormatTimeOutput>>;\n\n  std::string subfile_path_;\n  bool output_time_;\n  bool observe_per_core_;\n};\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveTimeStep.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.hpp\"\n\n#include <cmath>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Events {\ntemplate <typename System>\nObserveTimeStep<System>::ObserveTimeStep(CkMigrateMessage* const m)\n    : Event(m) {}\n\ntemplate <typename System>\nObserveTimeStep<System>::ObserveTimeStep() = default;\n\ntemplate <typename System>\nObserveTimeStep<System>::ObserveTimeStep(const std::string& subfile_name,\n                                         const bool output_time,\n                                         const bool observe_per_core)\n    : subfile_path_(\"/\" + subfile_name),\n      output_time_(output_time),\n      observe_per_core_(observe_per_core) {}\n\ntemplate <typename System>\nstd::pair<observers::TypeOfObservation, observers::ObservationKey>\nObserveTimeStep<System>::get_observation_type_and_key_for_registration() const {\n  return {observers::TypeOfObservation::Reduction,\n          observers::ObservationKey{subfile_path_ + \".dat\"}};\n}\n\ntemplate <typename System>\nbool ObserveTimeStep<System>::needs_evolved_variables() const {\n  return false;\n}\n\ntemplate <typename System>\nvoid ObserveTimeStep<System>::pup(PUP::er& p) {\n  Event::pup(p);\n  p | subfile_path_;\n  p | output_time_;\n  p | observe_per_core_;\n}\n\ntemplate <typename System>\nauto ObserveTimeStep<System>::assemble_data(\n    const TimeDelta& time_step,\n    const typename System::variables_tag::type& variables,\n    const ObservationValue& observation_value) const\n    -> std::tuple<observers::ObservationId, std::vector<std::string>,\n                  ReductionData,\n                  std::optional<Events::detail::FormatTimeOutput>> {\n  // For empty vars, we use 1 grid point to avoid divisions by zero\n  // after reduction.\n  size_t number_of_grid_points = 1;\n  if constexpr (not std::is_same_v<typename System::variables_tag::tags_list,\n                                   tmpl::list<>>) {\n    number_of_grid_points = variables.number_of_grid_points();\n  }\n  const double slab_size = time_step.slab().duration().value();\n  const double step_size = abs(time_step.value());\n  const double wall_time = sys::wall_time();\n\n  auto formatter = output_time_\n                       ? std::make_optional(Events::detail::FormatTimeOutput{})\n                       : std::nullopt;\n\n  observers::ObservationId observation_id{observation_value.value,\n                                          subfile_path_ + \".dat\"};\n  std::vector<std::string> legend{\n      observation_value.name, \"NumberOfPoints\",    \"Slab size\",\n      \"Minimum time step\",    \"Maximum time step\", \"Effective time step\",\n      \"Minimum Walltime\",     \"Maximum Walltime\"};\n  ReductionData reduction_data{observation_value.value,\n                               number_of_grid_points,\n                               slab_size,\n                               step_size,\n                               step_size,\n                               number_of_grid_points / step_size,\n                               wall_time,\n                               wall_time};\n\n  return {std::move(observation_id), std::move(legend),\n          std::move(reduction_data), std::move(formatter)};\n}\n\n/// \\cond\ntemplate <typename System>\nPUP::able::PUP_ID ObserveTimeStep<System>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveTimeStepVolume.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"IO/H5/TensorData.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveConstantsPerElement.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nenum class FloatingPointType;\ntemplate <size_t VolumeDim>\nclass Domain;\ntemplate <size_t VolumeDim>\nclass ElementId;\nclass TimeDelta;\nnamespace domain {\nnamespace FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace FunctionsOfTime\nnamespace Tags {\ntemplate <size_t VolumeDim>\nstruct Domain;\nstruct FunctionsOfTime;\ntemplate <size_t VolumeDim, typename Frame>\nstruct MinimumGridSpacing;\n}  // namespace Tags\n}  // namespace domain\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\nstruct Time;\nstruct TimeStep;\n}  // namespace Tags\nnamespace TimeSteppers {\ntemplate <typename Vars>\nclass History;\n}  // namespace TimeSteppers\n/// \\endcond\n\nnamespace dg::Events {\n/*!\n * \\brief %Observe the time step in the volume.\n *\n * Observe the time step size in each element.  Each element is output\n * as a single cell with two points per dimension and the observation\n * constant on all those points.\n *\n * Writes volume quantities:\n * - InertialCoordinates (only element corners)\n * - Time step\n * - Slab fraction\n * - Minimum grid spacing\n * - Integration order\n */\ntemplate <typename System>\nclass ObserveTimeStepVolume\n    : public ObserveConstantsPerElement<System::volume_dim> {\n public:\n  static constexpr size_t volume_dim = System::volume_dim;\n  static_assert(not tt::is_a_v<tmpl::list, typename System::variables_tag>,\n                \"Split variables systems not handled.\");\n\n  /// \\cond\n  explicit ObserveTimeStepVolume(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ObserveTimeStepVolume);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help =\n      \"Observe the time step and integration order in the volume.\";\n\n  ObserveTimeStepVolume();\n\n  ObserveTimeStepVolume(const std::string& subfile_name,\n                        ::FloatingPointType coordinates_floating_point_type,\n                        ::FloatingPointType floating_point_type);\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<\n      ::Tags::Time, ::domain::Tags::FunctionsOfTime,\n      ::domain::Tags::Domain<volume_dim>, ::Tags::TimeStep,\n      domain::Tags::MinimumGridSpacing<volume_dim, Frame::Inertial>,\n      ::Tags::HistoryEvolvedVariables<typename System::variables_tag>>;\n\n  template <typename Metavariables, typename ParallelComponent>\n  void operator()(\n      const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const Domain<volume_dim>& domain, const TimeDelta& time_step,\n      const double minimum_grid_spacing,\n      const TimeSteppers::History<typename System::variables_tag::type>&\n          history,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<volume_dim>& element_id,\n      const ParallelComponent* const component,\n      const Event::ObservationValue& observation_value) const {\n    std::vector<TensorComponent> components =\n        assemble_data(time, functions_of_time, domain, element_id, time_step,\n                      minimum_grid_spacing, history);\n\n    this->observe(components, cache, element_id, component, observation_value);\n  }\n\n  bool needs_evolved_variables() const override;\n\n private:\n  std::vector<TensorComponent> assemble_data(\n      double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time,\n      const Domain<volume_dim>& domain, const ElementId<volume_dim>& element_id,\n      const TimeDelta& time_step, double minimum_grid_spacing,\n      const TimeSteppers::History<typename System::variables_tag::type>&\n          history) const;\n};\n}  // namespace dg::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/ObserveTimeStepVolume.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveConstantsPerElement.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Domain;\ntemplate <size_t VolumeDim>\nclass ElementId;\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n/// \\endcond\n\nnamespace dg::Events {\ntemplate <typename System>\nObserveTimeStepVolume<System>::ObserveTimeStepVolume(CkMigrateMessage* const m)\n    : ObserveConstantsPerElement<volume_dim>(m) {}\n\ntemplate <typename System>\nObserveTimeStepVolume<System>::ObserveTimeStepVolume() = default;\n\ntemplate <typename System>\nObserveTimeStepVolume<System>::ObserveTimeStepVolume(\n    const std::string& subfile_name,\n    const ::FloatingPointType coordinates_floating_point_type,\n    const ::FloatingPointType floating_point_type)\n    : ObserveConstantsPerElement<volume_dim>(\n          subfile_name, coordinates_floating_point_type, floating_point_type) {}\n\ntemplate <typename System>\nbool ObserveTimeStepVolume<System>::needs_evolved_variables() const {\n  return false;\n}\n\ntemplate <typename System>\nstd::vector<TensorComponent> ObserveTimeStepVolume<System>::assemble_data(\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const Domain<volume_dim>& domain, const ElementId<volume_dim>& element_id,\n    const TimeDelta& time_step, const double minimum_grid_spacing,\n    const TimeSteppers::History<typename System::variables_tag::type>& history)\n    const {\n  std::vector<TensorComponent> components = this->allocate_and_insert_coords(\n      4, time, functions_of_time, domain, element_id);\n  this->add_constant(make_not_null(&components), \"Time step\",\n                     time_step.value());\n  this->add_constant(make_not_null(&components), \"Slab fraction\",\n                     time_step.fraction().value());\n  this->add_constant(make_not_null(&components), \"Minimum grid spacing\",\n                     minimum_grid_spacing);\n  this->add_constant(make_not_null(&components), \"Integration order\",\n                     static_cast<double>(history.integration_order()));\n  return components;\n}\n\n/// \\cond\ntemplate <typename System>\nPUP::able::PUP_ID ObserveTimeStepVolume<System>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace dg::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Events/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Events::Tags {\n/// \\brief The mesh for the observation computational grid. For hybrid methods\n/// like DG-FD the observer mesh changes throughout the evolution.\ntemplate <size_t Dim>\nstruct ObserverMesh : db::SimpleTag {\n  using type = ::Mesh<Dim>;\n};\n\n/// \\brief Sets the `ObserverMesh` to `domain::Tags::Mesh`\n///\n/// This is what you would use for a single numerical method simulation. Hybrid\n/// methods will supply their own tags.\ntemplate <size_t Dim>\nstruct ObserverMeshCompute : ObserverMesh<Dim>, db::ComputeTag {\n  using base = ObserverMesh<Dim>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<::domain::Tags::Mesh<Dim>>;\n  static void function(const gsl::not_null<return_type*> observer_mesh,\n                       const ::Mesh<Dim>& mesh) {\n    *observer_mesh = mesh;\n  }\n};\n\n/*!\n * \\brief The coordinates used for observation.\n *\n * In methods like DG-FD the mesh and coordinates change throughout the\n * simulation, so we need to always grab the right ones.\n */\ntemplate <size_t Dim, typename Fr>\nstruct ObserverCoordinates : db::SimpleTag {\n  static std::string name() { return get_output(Fr{}) + \"Coordinates\"; }\n  using type = tnsr::I<DataVector, Dim, Fr>;\n};\n\n/// \\brief Sets the `ObserverCoordinates` to `domain::Tags::Coordinates`\n///\n/// This is what you would use for a single numerical method simulation. Hybrid\n/// methods will supply their own tags.\ntemplate <size_t Dim, typename Fr>\nstruct ObserverCoordinatesCompute : ObserverCoordinates<Dim, Fr>,\n                                    db::ComputeTag {\n  using base = ObserverCoordinates<Dim, Fr>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<::domain::Tags::Coordinates<Dim, Fr>>;\n  static void function(const gsl::not_null<return_type*> observer_coords,\n                       const return_type& coords) {\n    for (size_t i = 0; i < Dim; ++i) {\n      observer_coords->get(i).set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          make_not_null(&const_cast<DataVector&>(coords.get(i))));\n    }\n  }\n};\n\n/*!\n * \\brief The inverse Jacobian used for observation.\n *\n * In methods like DG-FD the mesh and inverse Jacobian change throughout the\n * simulation, so we need to always grab the right ones.\n */\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct ObserverInverseJacobian : db::SimpleTag {\n  static std::string name() {\n    return \"InverseJacobian(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n  using type = ::InverseJacobian<DataVector, Dim, SourceFrame, TargetFrame>;\n};\n\n/// \\brief Sets the `ObserverInverseJacobian` to `domain::Tags::InverseJacobian`\n///\n/// This is what you would use for a single numerical method simulation. Hybrid\n/// methods will supply their own tags.\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct ObserverInverseJacobianCompute\n    : ObserverInverseJacobian<Dim, SourceFrame, TargetFrame>,\n      db::ComputeTag {\n  using base = ObserverInverseJacobian<Dim, SourceFrame, TargetFrame>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<\n      ::domain::Tags::InverseJacobian<Dim, SourceFrame, TargetFrame>>;\n  static void function(\n      const gsl::not_null<return_type*> observer_inverse_jacobian,\n      const return_type& inverse_jacobian) {\n    for (size_t i = 0; i < inverse_jacobian.size(); ++i) {\n      (*observer_inverse_jacobian)[i].set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          make_not_null(&const_cast<DataVector&>(inverse_jacobian[i])));\n    }\n  }\n};\n\n/*!\n * \\brief The Jacobian used for observation.\n *\n * In methods like DG-FD the mesh and  Jacobian change throughout the\n * simulation, so we need to always grab the right ones.\n */\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct ObserverJacobian : db::SimpleTag {\n  static std::string name() {\n    return \"Jacobian(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n  using type = ::Jacobian<DataVector, Dim, SourceFrame, TargetFrame>;\n};\n\n/// \\brief Sets the `ObserverJacobian` to `domain::Tags::Jacobian`\n///\n/// This is what you would use for a single numerical method simulation. Hybrid\n/// methods will supply their own tags.\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame>\nstruct ObserverJacobianCompute\n    : ObserverJacobian<Dim, SourceFrame, TargetFrame>,\n      db::ComputeTag {\n  using base = ObserverJacobian<Dim, SourceFrame, TargetFrame>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<::domain::Tags::Jacobian<Dim, SourceFrame, TargetFrame>>;\n  static void function(const gsl::not_null<return_type*> observer_jacobian,\n                       const return_type& jacobian) {\n    for (size_t i = 0; i < jacobian.size(); ++i) {\n      (*observer_jacobian)[i].set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          make_not_null(&const_cast<DataVector&>(jacobian[i])));\n    }\n  }\n};\n\n/*!\n * \\brief The determinant of the inverse Jacobian used for observation.\n *\n * In methods like DG-FD the mesh and inverse Jacobian change throughout the\n * simulation, so we need to always grab the right ones.\n */\ntemplate <typename SourceFrame, typename TargetFrame>\nstruct ObserverDetInvJacobian : db::SimpleTag {\n  static std::string name() {\n    return \"DetInvJacobian(\" + get_output(SourceFrame{}) + \",\" +\n           get_output(TargetFrame{}) + \")\";\n  }\n  using type = Scalar<DataVector>;\n};\n\n/// \\brief Sets the `ObserverDetInvJacobian` to `domain::Tags::DetInvJacobian`\n///\n/// This is what you would use for a single numerical method simulation. Hybrid\n/// methods will supply their own tags.\ntemplate <typename SourceFrame, typename TargetFrame>\nstruct ObserverDetInvJacobianCompute\n    : ObserverDetInvJacobian<SourceFrame, TargetFrame>,\n      db::ComputeTag {\n  using base = ObserverDetInvJacobian<SourceFrame, TargetFrame>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<::domain::Tags::DetInvJacobian<SourceFrame, TargetFrame>>;\n  static void function(\n      const gsl::not_null<return_type*> observer_det_inverse_jacobian,\n      const return_type& det_inverse_jacobian) {\n    for (size_t i = 0; i < det_inverse_jacobian.size(); ++i) {\n      (*observer_det_inverse_jacobian)[i].set_data_ref(\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          make_not_null(&const_cast<DataVector&>(det_inverse_jacobian[i])));\n    }\n  }\n};\n\n/// \\brief The mesh velocity used for observations\n///\n/// The type is a `std::optional`, which when it is not set indicates that the\n/// mesh is not moving.\ntemplate <size_t Dim, typename Frame = ::Frame::Inertial>\nstruct ObserverMeshVelocity : db::SimpleTag {\n  static std::string name() { return get_output(Frame{}) + \"MeshVelocity\"; }\n  using type = std::optional<tnsr::I<DataVector, Dim, Frame>>;\n};\n\n/// \\brief Sets the `ObserverMeshVelocty` to `domain::Tags::MeshVelocty`\n///\n/// This is what you would use for a single numerical method simulation. Hybrid\n/// methods will supply their own tags.\ntemplate <size_t Dim, typename Frame = ::Frame::Inertial>\nstruct ObserverMeshVelocityCompute : ObserverMeshVelocity<Dim, Frame>,\n                                     db::ComputeTag {\n  using base = ObserverMeshVelocity<Dim, Frame>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<::domain::Tags::MeshVelocity<Dim, Frame>>;\n  static void function(const gsl::not_null<return_type*> observer_mesh_velocity,\n                       const return_type& mesh_velocity) {\n    if (mesh_velocity.has_value()) {\n      *observer_mesh_velocity = tnsr::I<DataVector, Dim, Frame>{};\n      for (size_t i = 0; i < mesh_velocity->size(); ++i) {\n        observer_mesh_velocity->value()[i].set_data_ref(\n            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n            make_not_null(&const_cast<DataVector&>(mesh_velocity.value()[i])));\n      }\n    } else {\n      *observer_mesh_velocity = std::nullopt;\n    }\n  }\n};\n}  // namespace Events::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EventsAndDenseTriggers)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  EventsAndDenseTriggers.cpp\n  Tags.hpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  DenseTrigger.hpp\n  EventsAndDenseTriggers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  EventsAndTriggers\n  Options\n  Parallel\n  Serialization\n  Time\n  Utilities\n  )\n\nadd_subdirectory(DenseTriggers)\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\n/*!\n * \\ingroup EventsAndTriggersGroup\n * \\brief Contains denser triggers.\n */\nnamespace DenseTriggers {}\n\n/// \\ingroup EventsAndTriggersGroup\n/// Base class for checking whether to run an Event at arbitrary times.\n///\n/// The DataBox passed to the member functions will have\n/// `::Tags::Time`, and therefore any compute tags depending on that\n/// value, set to the time to be tested.  Any discrete properties of\n/// steps or slabs, such as the step size, may have the values from\n/// times off by one step.  The evolved variables will be in an\n/// unspecified state.\nclass DenseTrigger : public PUP::able {\n protected:\n  /// \\cond\n  DenseTrigger() = default;\n  DenseTrigger(const DenseTrigger&) = default;\n  DenseTrigger(DenseTrigger&&) = default;\n  DenseTrigger& operator=(const DenseTrigger&) = default;\n  DenseTrigger& operator=(DenseTrigger&&) = default;\n  /// \\endcond\n\n public:\n  ~DenseTrigger() override = default;\n\n  /// \\cond\n  explicit DenseTrigger(CkMigrateMessage* const msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(DenseTrigger);  // NOLINT\n  /// \\endcond\n\n  /// Check whether the trigger fires.  Returns std::nullopt if\n  /// insufficient data is available to make the decision.  The\n  /// trigger is not responsible for checking whether dense output of\n  /// the evolved variables is possible, but may need to check things\n  /// such as the availability of FunctionOfTime data.\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename Component>\n  std::optional<bool> is_triggered(\n      const gsl::not_null<db::DataBox<DbTags>*> box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const Component* const component) {\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>::factory_creation::factory_classes;\n    previous_trigger_time_ = next_previous_trigger_time_;\n    return call_with_dynamic_type<std::optional<bool>,\n                                  tmpl::at<factory_classes, DenseTrigger>>(\n        this,\n        [this, &array_index, &box, &cache, &component](auto* const trigger) {\n          using TriggerType = std::decay_t<decltype(*trigger)>;\n          const auto result = db::mutate_apply<\n              typename TriggerType::is_triggered_return_tags,\n              typename TriggerType::is_triggered_argument_tags>(\n              [&array_index, &cache, &component,\n               &trigger](const auto&... args) {\n                return trigger->is_triggered(cache, array_index, component,\n                                             args...);\n              },\n              box);\n          if (result == std::optional{true}) {\n            next_previous_trigger_time_ = db::get<::Tags::Time>(*box);\n          }\n          return result;\n        });\n  }\n\n  /// Obtain the next time to check the trigger, or std::nullopt if the\n  /// trigger is not ready to report yet.\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename Component>\n  std::optional<double> next_check_time(\n      const gsl::not_null<db::DataBox<DbTags>*> box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const Component* component) {\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            *box))>::factory_creation::factory_classes;\n    return call_with_dynamic_type<std::optional<double>,\n                                  tmpl::at<factory_classes, DenseTrigger>>(\n        this, [&array_index, &box, &cache, &component](auto* const trigger) {\n          using TriggerType = std::decay_t<decltype(*trigger)>;\n          return db::mutate_apply<\n              typename TriggerType::next_check_time_return_tags,\n              typename TriggerType::next_check_time_argument_tags>(\n              [&array_index, &cache, &component,\n               &trigger](const auto&... args) {\n                return trigger->next_check_time(cache, array_index, component,\n                                                args...);\n              },\n              box);\n        });\n  }\n\n  /// \\brief Reports the value of `::Tags::Time` when the trigger most recently\n  /// fired, except for the most recent call of `is_triggered`.\n  ///\n  /// \\details The most recent call of `is_triggered` is not used for reporting\n  /// the previous trigger so that the time reported to the event is actually\n  /// the previous time value on which the trigger fired and activated the\n  /// event. Without ignoring the most recent call of `is_triggered`, we'd just\n  /// always be reporting the current time to the event, because events always\n  /// run after their associated triggers fire via a call to `is_triggered`.\n  std::optional<double> previous_trigger_time() const {\n    return previous_trigger_time_;\n  }\n\n  void pup(PUP::er& p) override {\n    p | next_previous_trigger_time_;\n    p | previous_trigger_time_;\n  }\n\n private:\n  std::optional<double> next_previous_trigger_time_ = std::nullopt;\n  std::optional<double> previous_trigger_time_ = std::nullopt;\n};\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EventsAndDenseTriggers)\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Filter.cpp\n  Or.cpp\n  Times.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Filter.hpp\n  Or.hpp\n  Times.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Filter.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Or.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Times.hpp\"\n\nnamespace DenseTriggers {\nusing standard_dense_triggers =\n    tmpl::list<DenseTriggers::Filter, DenseTriggers::Or, DenseTriggers::Times>;\n}  // namespace DenseTriggers\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Filter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Filter.hpp\"\n\n#include <pup_stl.h>\n#include <utility>\n\nnamespace DenseTriggers {\nFilter::Filter(std::unique_ptr<DenseTrigger> trigger,\n               std::unique_ptr<Trigger> filter)\n    : trigger_(std::move(trigger)), filter_(std::move(filter)) {}\n\nvoid Filter::pup(PUP::er& p) {\n  DenseTrigger::pup(p);\n  p | trigger_;\n  p | filter_;\n}\n\nPUP::able::PUP_ID Filter::my_PUP_ID = 0;  // NOLINT\n}  // namespace DenseTriggers\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Filter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct DataBox;\n}  // namespace Tags\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\nnamespace DenseTriggers {\n/// \\ingroup EventsAndTriggersGroup\n/// %Filter activations of a dense trigger using a non-dense trigger.\n///\n/// For example, to trigger every 10 starting at 100, one could use\n///\n/// \\snippet DenseTriggers/Test_Filter.cpp example\nclass Filter : public DenseTrigger {\n public:\n  /// \\cond\n  Filter() = default;\n  explicit Filter(CkMigrateMessage* const msg) : DenseTrigger(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Filter);  // NOLINT\n  /// \\endcond\n\n  struct TriggerOption {\n    static std::string name() { return \"Trigger\"; }\n    using type = std::unique_ptr<DenseTrigger>;\n    static constexpr Options::String help = \"Dense trigger to filter\";\n  };\n\n  struct FilterOption {\n    static std::string name() { return \"Filter\"; }\n    using type = std::unique_ptr<Trigger>;\n    static constexpr Options::String help = \"Non-dense trigger to filter with\";\n  };\n\n  using options = tmpl::list<TriggerOption, FilterOption>;\n  static constexpr Options::String help =\n      \"Filter activations of a dense trigger using a non-dense trigger.\";\n\n  explicit Filter(std::unique_ptr<DenseTrigger> trigger,\n                  std::unique_ptr<Trigger> filter);\n\n  using is_triggered_return_tags = tmpl::list<Tags::DataBox>;\n  using is_triggered_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component,\n            typename DbTags>\n  std::optional<bool> is_triggered(\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const Component* component,\n      const gsl::not_null<db::DataBox<DbTags>*> box) const {\n    auto result = trigger_->is_triggered(box, cache, array_index, component);\n    if (result == std::optional{true}) {\n      return filter_->is_triggered(*box);\n    }\n    return result;\n  }\n\n  using next_check_time_return_tags = tmpl::list<Tags::DataBox>;\n  using next_check_time_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component,\n            typename DbTags>\n  std::optional<double> next_check_time(\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const Component* component,\n      const gsl::not_null<db::DataBox<DbTags>*> box) const {\n    return trigger_->next_check_time(box, cache, array_index, component);\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::unique_ptr<DenseTrigger> trigger_{};\n  std::unique_ptr<Trigger> filter_{};\n};\n}  // namespace DenseTriggers\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Or.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Or.hpp\"\n\n#include <pup_stl.h>\n#include <utility>\n\nnamespace DenseTriggers {\nOr::Or(std::vector<std::unique_ptr<DenseTrigger>> triggers)\n    : triggers_(std::move(triggers)) {}\n\nvoid Or::pup(PUP::er& p) {\n  DenseTrigger::pup(p);\n  p | triggers_;\n}\n\nPUP::able::PUP_ID Or::my_PUP_ID = 0;  // NOLINT\n}  // namespace DenseTriggers\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Or.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct DataBox;\nstruct TimeStepId;\n}  // namespace Tags\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\nnamespace DenseTriggers {\n/// \\ingroup EventsAndTriggersGroup\n/// Trigger when any of a collection of DenseTriggers triggers.\nclass Or : public DenseTrigger {\n public:\n  /// \\cond\n  Or() = default;\n  explicit Or(CkMigrateMessage* const msg) : DenseTrigger(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Or);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help =\n      \"Trigger when any of a collection of triggers triggers.\";\n\n  explicit Or(std::vector<std::unique_ptr<DenseTrigger>> triggers);\n\n  using is_triggered_return_tags = tmpl::list<Tags::DataBox>;\n  using is_triggered_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component,\n            typename DbTags>\n  std::optional<bool> is_triggered(\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const Component* component,\n      const gsl::not_null<db::DataBox<DbTags>*> box) const {\n    bool is_ready = true;\n    for (const auto& trigger : triggers_) {\n      const auto sub_result =\n          trigger->is_triggered(box, cache, array_index, component);\n      if (sub_result.has_value()) {\n        if (*sub_result) {\n          // No need to wait for all the other triggers to be ready.\n          return true;\n        }\n      } else {\n        is_ready = false;\n      }\n    }\n    return is_ready ? std::optional{false} : std::nullopt;\n  }\n\n  using next_check_time_return_tags = tmpl::list<Tags::DataBox>;\n  using next_check_time_argument_tags = tmpl::list<Tags::TimeStepId>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component,\n            typename DbTags>\n  std::optional<double> next_check_time(\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const Component* component,\n      const gsl::not_null<db::DataBox<DbTags>*>& box,\n      const TimeStepId& time_step_id) const {\n    const evolution_less<double> before{time_step_id.time_runs_forward()};\n    double result = before.infinity();\n    for (const auto& trigger : triggers_) {\n      const auto sub_result =\n          trigger->next_check_time(box, cache, array_index, component);\n      if (not sub_result.has_value()) {\n        return std::nullopt;\n      }\n      result = std::min(*sub_result, result, before);\n    }\n    return result;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<std::unique_ptr<DenseTrigger>> triggers_{};\n};\n}  // namespace DenseTriggers\n\ntemplate <>\nstruct Options::create_from_yaml<DenseTriggers::Or> {\n  template <typename Metavariables>\n  static DenseTriggers::Or create(const Option& options) {\n    return DenseTriggers::Or(\n        options.parse_as<std::vector<std::unique_ptr<DenseTrigger>>,\n                         Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Times.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Times.hpp\"\n\n#include <optional>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\nnamespace DenseTriggers {\nTimes::Times(std::unique_ptr<TimeSequence<double>> times)\n    : times_(std::move(times)) {}\n\nvoid Times::pup(PUP::er& p) {\n  DenseTrigger::pup(p);\n  p | times_;\n}\n\nstd::optional<bool> Times::is_triggered_impl(const double time) const {\n  const auto trigger_times = times_->times_near(time);\n\n  return time == trigger_times[1];\n}\n\nstd::optional<double> Times::next_check_time_impl(\n    const TimeStepId& time_step_id, const double time) const {\n  const evolution_less<double> before{time_step_id.time_runs_forward()};\n\n  const auto trigger_times = times_->times_near(time);\n  double next_time = before.infinity();\n  for (const auto& trigger_time : trigger_times) {\n    if (trigger_time.has_value() and before(time, *trigger_time) and\n        before(*trigger_time, next_time)) {\n      next_time = *trigger_time;\n    }\n  }\n\n  return next_time;\n}\n\nPUP::able::PUP_ID Times::my_PUP_ID = 0;  // NOLINT\n}  // namespace DenseTriggers\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Times.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass TimeStepId;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct Time;\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace DenseTriggers {\n/// \\ingroup EventsAndTriggersGroup\n/// \\ingroup TimeGroup\n/// Trigger at specified times.\nclass Times : public DenseTrigger {\n public:\n  /// \\cond\n  Times() = default;\n  explicit Times(CkMigrateMessage* const msg) : DenseTrigger(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Times);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help{\"Trigger at specified times.\"};\n\n  explicit Times(std::unique_ptr<TimeSequence<double>> times);\n\n  using is_triggered_return_tags = tmpl::list<>;\n  using is_triggered_argument_tags = tmpl::list<Tags::Time>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<bool> is_triggered(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/,\n      const double time) const {\n    return is_triggered_impl(time);\n  }\n\n  using next_check_time_return_tags = tmpl::list<>;\n  using next_check_time_argument_tags =\n      tmpl::list<Tags::TimeStepId, Tags::Time>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<double> next_check_time(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/,\n      const TimeStepId& time_step_id, const double time) const {\n    return next_check_time_impl(time_step_id, time);\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::optional<bool> is_triggered_impl(const double time) const;\n\n  std::optional<double> next_check_time_impl(const TimeStepId& time_step_id,\n                                             const double time) const;\n\n  std::unique_ptr<TimeSequence<double>> times_{};\n};\n}  // namespace DenseTriggers\n\ntemplate <>\nstruct Options::create_from_yaml<DenseTriggers::Times> {\n  template <typename Metavariables>\n  static DenseTriggers::Times create(const Option& options) {\n    return DenseTriggers::Times(\n        options\n            .parse_as<std::unique_ptr<TimeSequence<double>>, Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp\"\n\n#include <algorithm>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n\nEventsAndDenseTriggers::TriggerRecord::TriggerRecord() = default;\n\nEventsAndDenseTriggers::TriggerRecord::TriggerRecord(\n    double next_check_in, std::optional<bool> is_triggered_in,\n    size_t num_events_ready_in, std::unique_ptr<DenseTrigger> trigger_in,\n    std::vector<std::unique_ptr<Event>> events_in)\n    : next_check(next_check_in),\n      is_triggered(is_triggered_in),\n      num_events_ready(num_events_ready_in),\n      trigger(std::move(trigger_in)),\n      events(std::move(events_in)) {}\n\nvoid EventsAndDenseTriggers::TriggerRecord::pup(PUP::er& p) {\n  p | next_check;\n  p | is_triggered;\n  p | num_events_ready;\n  p | trigger;\n  p | events;\n}\n\nEventsAndDenseTriggers::TriggerAndEvents::TriggerAndEvents() = default;\n\nEventsAndDenseTriggers::TriggerAndEvents::TriggerAndEvents(\n    std::unique_ptr<::DenseTrigger> trigger_in,\n    std::vector<std::unique_ptr<::Event>> events_in)\n    : trigger(std::move(trigger_in)), events(std::move(events_in)) {}\n\nvoid EventsAndDenseTriggers::TriggerAndEvents::pup(PUP::er& p) {\n  p | trigger;\n  p | events;\n}\n\nEventsAndDenseTriggers::EventsAndDenseTriggers(\n    ConstructionType events_and_triggers) {\n  events_and_triggers_.reserve(events_and_triggers.size());\n  for (auto& events_and_trigger : events_and_triggers) {\n    events_and_triggers_.push_back(TriggerRecord{\n        std::numeric_limits<double>::signaling_NaN(), std::optional<bool>{}, 0,\n        std::move(events_and_trigger.trigger),\n        std::move(events_and_trigger.events)});\n  }\n}\n\nvoid EventsAndDenseTriggers::add_trigger_and_events(\n    std::unique_ptr<DenseTrigger> trigger,\n    std::vector<std::unique_ptr<Event>> events) {\n  ASSERT(not initialized(), \"Cannot add events after initialization\");\n  events_and_triggers_.reserve(events_and_triggers_.size() + 1);\n  events_and_triggers_.push_back(TriggerRecord{\n      std::numeric_limits<double>::signaling_NaN(), std::optional<bool>{}, 0,\n      std::move(trigger), std::move(events)});\n}\n\nvoid EventsAndDenseTriggers::pup(PUP::er& p) {\n  p | events_and_triggers_;\n  p | next_check_;\n  p | before_;\n}\n\nbool EventsAndDenseTriggers::initialized() const {\n  const ScopedFpeState disable_fpes(false);\n  return not std::isnan(next_check_);\n}\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct Time;\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Tags {\n/*!\n * \\ingroup EventsAndTriggersGroup\n * \\brief Previous time at which the trigger activated.\n *\n * \\details This tag is populated with the most recent time the current trigger\n * fired prior to the current activation.\n * \\note This tag is only populated with a meaningful value for events invoked\n * from `EventsAndDenseTriggers`; during other actions, the tag should not be\n * used.\n */\nstruct PreviousTriggerTime : db::SimpleTag {\n  using type = std::optional<double>;\n};\n}  // namespace Tags\n\n/// \\ingroup EventsAndTriggersGroup\n/// Class that checks dense triggers and runs events\nclass EventsAndDenseTriggers {\n private:\n  template <typename Event>\n  struct get_tags {\n    using type = typename Event::compute_tags_for_observation_box;\n  };\n\n public:\n  struct TriggerAndEvents {\n    struct Trigger {\n      using type = std::unique_ptr<::DenseTrigger>;\n      static constexpr Options::String help = \"Determines when the Events run.\";\n    };\n    struct Events {\n      using type = std::vector<std::unique_ptr<::Event>>;\n      static constexpr Options::String help =\n          \"These events run when the Trigger fires.\";\n    };\n    static constexpr Options::String help =\n        \"Events that run when the Trigger fires.\";\n    using options = tmpl::list<Trigger, Events>;\n    TriggerAndEvents();\n    TriggerAndEvents(std::unique_ptr<::DenseTrigger> trigger_in,\n                     std::vector<std::unique_ptr<::Event>> events_in);\n    void pup(PUP::er& p);\n    std::unique_ptr<::DenseTrigger> trigger{};\n    std::vector<std::unique_ptr<::Event>> events{};\n  };\n\n  using ConstructionType = std::vector<TriggerAndEvents>;\n\n private:\n  struct TriggerRecord {\n    TriggerRecord();\n    TriggerRecord(double next_check_in, std::optional<bool> is_triggered_in,\n                  size_t num_events_ready_in,\n                  std::unique_ptr<DenseTrigger> trigger_in,\n                  std::vector<std::unique_ptr<Event>> events_in);\n    double next_check{};\n    std::optional<bool> is_triggered{};\n    size_t num_events_ready{};\n    std::unique_ptr<DenseTrigger> trigger{};\n    std::vector<std::unique_ptr<Event>> events{};\n\n    // NOLINTNEXTLINE(google-runtime-references)\n    void pup(PUP::er& p);\n  };\n  using Storage = std::vector<TriggerRecord>;\n\n public:\n  EventsAndDenseTriggers() = default;\n  explicit EventsAndDenseTriggers(ConstructionType events_and_triggers);\n\n  template <typename DbTags>\n  double next_trigger(const db::DataBox<DbTags>& box);\n\n  enum class TriggeringState { Ready, NeedsEvolvedVariables, NotReady };\n\n  /// Check triggers fire and whether all events to run are ready.  If\n  /// this function returns anything other than NotReady, then\n  /// rerunning it will skip checks (except for\n  /// Event::needs_evolved_variables) until a successful call to\n  /// reschedule.\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename ComponentPointer>\n  TriggeringState is_ready(gsl::not_null<db::DataBox<DbTags>*> box,\n                           Parallel::GlobalCache<Metavariables>& cache,\n                           const ArrayIndex& array_index,\n                           const ComponentPointer component);\n\n  /// Run events associated with fired triggers.  This must be called\n  /// after is_ready returns something other then NotReady.  Any\n  /// repeated calls will be no-ops until a successful call to\n  /// reschedule.\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename ComponentPointer>\n  void run_events(db::DataBox<DbTags>& box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const ComponentPointer component);\n\n  /// Schedule the next check.  This must be called after run_events\n  /// for the current check.  Returns `true` on success, `false` if\n  /// insufficient data is available and the call should be retried\n  /// later.\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename ComponentPointer>\n  bool reschedule(gsl::not_null<db::DataBox<DbTags>*> box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const ComponentPointer component);\n\n  /// Add a new trigger and set of events.  This can only be called\n  /// during initialization.\n  void add_trigger_and_events(std::unique_ptr<DenseTrigger> trigger,\n                              std::vector<std::unique_ptr<Event>> events);\n\n  template <typename F>\n  void for_each_event(F&& f) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <typename DbTags>\n  void initialize(const db::DataBox<DbTags>& box);\n\n  bool initialized() const;\n\n  Storage events_and_triggers_;\n  double next_check_ = std::numeric_limits<double>::signaling_NaN();\n  evolution_less<double> before_{};\n};\n\ntemplate <typename DbTags>\ndouble EventsAndDenseTriggers::next_trigger(const db::DataBox<DbTags>& box) {\n  if (UNLIKELY(not initialized())) {\n    initialize(box);\n  }\n\n  if (events_and_triggers_.empty()) {\n    return before_.infinity();\n  }\n\n  return next_check_;\n}\n\ntemplate <typename DbTags, typename Metavariables, typename ArrayIndex,\n          typename ComponentPointer>\nEventsAndDenseTriggers::TriggeringState EventsAndDenseTriggers::is_ready(\n    const gsl::not_null<db::DataBox<DbTags>*> box,\n    Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,\n    const ComponentPointer component) {\n  ASSERT(initialized(), \"Not initialized\");\n  ASSERT(not events_and_triggers_.empty(),\n         \"Should not be calling is_ready with no triggers\");\n\n  for (auto& trigger_entry : events_and_triggers_) {\n    if (trigger_entry.next_check != next_check_) {\n      continue;\n    }\n    if (not trigger_entry.is_triggered.has_value()) {\n      const auto is_triggered = trigger_entry.trigger->is_triggered(\n          box, cache, array_index, component);\n      if (not is_triggered.has_value()) {\n        return TriggeringState::NotReady;\n      }\n\n      trigger_entry.is_triggered = *is_triggered;\n    }\n\n    if (not *trigger_entry.is_triggered) {\n      continue;\n    }\n\n    for (; trigger_entry.num_events_ready < trigger_entry.events.size();\n         ++trigger_entry.num_events_ready) {\n      if (not trigger_entry.events[trigger_entry.num_events_ready]->is_ready(\n              *box, cache, array_index, component)) {\n        return TriggeringState::NotReady;\n      }\n    }\n  }\n\n  for (auto& trigger_entry : events_and_triggers_) {\n    if (trigger_entry.is_triggered != std::optional{true}) {\n      continue;\n    }\n    for (const auto& event : trigger_entry.events) {\n      if (event->needs_evolved_variables()) {\n        return TriggeringState::NeedsEvolvedVariables;\n      }\n    }\n  }\n\n  return TriggeringState::Ready;\n}\n\ntemplate <typename DbTags, typename Metavariables, typename ArrayIndex,\n          typename ComponentPointer>\nvoid EventsAndDenseTriggers::run_events(\n    db::DataBox<DbTags>& box, Parallel::GlobalCache<Metavariables>& cache,\n    const ArrayIndex& array_index, const ComponentPointer component) {\n  ASSERT(initialized(), \"Not initialized\");\n  ASSERT(not events_and_triggers_.empty(),\n         \"Should not be calling run_events with no triggers\");\n  using compute_tags = tmpl::remove_duplicates<tmpl::filter<\n      tmpl::flatten<tmpl::transform<\n          tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                   Event>,\n          get_tags<tmpl::_1>>>,\n      db::is_compute_tag<tmpl::_1>>>;\n  const Event::ObservationValue observation_value{db::tag_name<::Tags::Time>(),\n                                                  db::get<::Tags::Time>(box)};\n  auto observation_box =\n      make_observation_box<compute_tags>(make_not_null(&box));\n\n  for (auto& trigger_entry : events_and_triggers_) {\n    if (trigger_entry.is_triggered == std::optional{true}) {\n      db::mutate<::Tags::PreviousTriggerTime>(\n          [&trigger_entry](const gsl::not_null<std::optional<double>*>\n                               previous_trigger_time) {\n            *previous_trigger_time =\n                trigger_entry.trigger->previous_trigger_time();\n          },\n          make_not_null(&box));\n      for (const auto& event : trigger_entry.events) {\n        event->run(make_not_null(&observation_box), cache, array_index,\n                   component, observation_value);\n      }\n      db::mutate<::Tags::PreviousTriggerTime>(\n          [](const gsl::not_null<std::optional<double>*>\n                 previous_trigger_time) {\n            *previous_trigger_time =\n                std::numeric_limits<double>::signaling_NaN();\n          },\n          make_not_null(&box));\n    }\n    // Mark this trigger as handled so we will not reprocess it if\n    // this method or is_ready is called again.\n    trigger_entry.is_triggered = false;\n  }\n}\n\ntemplate <typename DbTags, typename Metavariables, typename ArrayIndex,\n          typename ComponentPointer>\nbool EventsAndDenseTriggers::reschedule(\n    const gsl::not_null<db::DataBox<DbTags>*> box,\n    Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,\n    const ComponentPointer component) {\n  ASSERT(initialized(), \"Not initialized\");\n  ASSERT(not events_and_triggers_.empty(),\n         \"Should not be calling run_events with no triggers\");\n\n  double new_next_check = before_.infinity();\n  for (auto& trigger_entry : events_and_triggers_) {\n    if (trigger_entry.next_check == next_check_) {\n      const std::optional<double> next_check =\n          trigger_entry.trigger->next_check_time(box, cache, array_index,\n                                                 component);\n      if (not next_check.has_value()) {\n        return false;\n      }\n      trigger_entry.next_check = *next_check;\n      trigger_entry.num_events_ready = 0;\n    }\n    if (before_(trigger_entry.next_check, new_next_check)) {\n      new_next_check = trigger_entry.next_check;\n    }\n    trigger_entry.is_triggered.reset();\n  }\n\n  next_check_ = new_next_check;\n  return true;\n}\n\ntemplate <typename F>\nvoid EventsAndDenseTriggers::for_each_event(F&& f) const {\n  for (const auto& trigger_and_events : events_and_triggers_) {\n    for (const auto& event : trigger_and_events.events) {\n      f(*event);\n    }\n  }\n}\n\ntemplate <typename DbTags>\nvoid EventsAndDenseTriggers::initialize(const db::DataBox<DbTags>& box) {\n  before_ = evolution_less<double>{\n      db::get<::Tags::TimeStepId>(box).time_runs_forward()};\n  next_check_ = db::get<::Tags::Time>(box);\n  for (auto& trigger_record : events_and_triggers_) {\n    trigger_record.next_check = next_check_;\n  }\n}\n\ntemplate <>\nstruct Options::create_from_yaml<EventsAndDenseTriggers> {\n  using type = EventsAndDenseTriggers;\n  template <typename Metavariables>\n  static type create(const Options::Option& options) {\n    return type(\n        options.parse_as<typename type::ConstructionType, Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/Options.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace OptionTags {\n/*!\n * \\ingroup EventsAndTriggersGroup\n * \\brief The Event%s to run based on DenseTrigger%s, similar to\n * OptionTags::EventsAndTriggers\n */\nstruct EventsAndDenseTriggers {\n  using type = ::EventsAndDenseTriggers;\n  static constexpr Options::String help = \"Events to run at arbitrary times\";\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\ingroup EventsAndTriggersGroup\n * \\brief The Event%s to run based on DenseTrigger%s\n */\nstruct EventsAndDenseTriggers : db::SimpleTag {\n  using type = ::EventsAndDenseTriggers;\n  using option_tags = tmpl::list<OptionTags::EventsAndDenseTriggers>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& events_and_triggers) {\n    return serialize_and_deserialize<type>(events_and_triggers);\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  RunEventsOnFailure.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/Actions/RunEventsOnFailure.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace Actions {\n/*!\n * \\brief Invokes all events specified in `Tags::EventsRunAtCleanup`.\n *\n * Before running the events, floating point exceptions are disabled. This is\n * to allow manipulating data even if there are `NaN` or other problematic\n * values. We ultimately just want to be able to see the state of the\n * simulation at failure.\n *\n * This action is intended to be executed in the\n * `Parallel::Phase::PostFailureCleanup` phase.\n *\n * \\note The simulation will almost certainly fail with different\n * elements at different times.\n */\ntemplate <typename ObservationId>\nstruct RunEventsOnFailure {\n private:\n  template <typename Event>\n  struct get_tags {\n    using type = typename Event::compute_tags_for_observation_box;\n  };\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<::Tags::EventsRunAtCleanup,\n                 ::Tags::EventsRunAtCleanupObservationValue>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const component) {\n    // We explicitly disable FPEs because we are dumping during a failure and\n    // so can't rely on the data being safe.\n    disable_floating_point_exceptions();\n\n    const Event::ObservationValue observation_value{\n        db::tag_name<ObservationId>(),\n        db::get<Tags::EventsRunAtCleanupObservationValue>(box)};\n\n    using compute_tags = tmpl::remove_duplicates<tmpl::filter<\n        tmpl::flatten<tmpl::transform<\n            tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                     Event>,\n            get_tags<tmpl::_1>>>,\n        db::is_compute_tag<tmpl::_1>>>;\n    std::optional observation_box{\n        make_observation_box<compute_tags>(make_not_null(&box))};\n\n    for (const auto& event : db::get<::Tags::EventsRunAtCleanup>(box)) {\n      event->run(make_not_null(&observation_box.value()), cache, array_index,\n                 component, observation_value);\n    }\n\n    // Do not re-enable FPEs because other parts of the pipeline might rely on\n    // them being disabled. We generally have them disabled during cleanup.\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY EventsAndTriggers)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  EventsAndTriggers.cpp\n  LogicalTriggers.cpp\n  WhenToCheck.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Event.hpp\n  EventsAndTriggers.hpp\n  LogicalTriggers.hpp\n  Tags.hpp\n  Trigger.hpp\n  WhenToCheck.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  ErrorHandling\n  PUBLIC\n  Options\n  Serialization\n  Utilities\n  )\n\nadd_subdirectory(Actions)\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/Event.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\n/// \\ingroup EventsAndTriggersGroup\nnamespace Events {}\n\n/// \\ingroup EventsAndTriggersGroup\n/// Base class for something that can happen during a simulation (such\n/// as an observation).\n///\n/// Derived events must have a `compute_tags_for_observation_box` that is a\n/// `tmpl::list` of simple or compute tags. Simple tags are assumed to already\n/// be in the `DataBox`. Evolved variables, for example, would be listed as\n/// simple tags. The compute tags are used to compute additional quantities that\n/// may be observed. For example, in the scalar wave system the 1- and 2-index\n/// constraints would be added as compute tags, as well as anything they depend\n/// on that's not already in the `DataBox`.\nclass Event : public PUP::able {\n protected:\n  /// \\cond\n  Event() = default;\n  Event(const Event&) = default;\n  Event(Event&&) = default;\n  Event& operator=(const Event&) = default;\n  Event& operator=(Event&&) = default;\n  /// \\endcond\n\n public:\n  ~Event() override = default;\n  explicit Event(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  WRAPPED_PUPable_abstract(Event);  // NOLINT\n\n  struct ObservationValue {\n    std::string name{};\n    double value = std::numeric_limits<double>::signaling_NaN();\n  };\n\n  template <typename ComputeTagsList, typename DataBoxType,\n            typename Metavariables, typename ArrayIndex,\n            typename ComponentPointer>\n  void run(\n      const gsl::not_null<ObservationBox<ComputeTagsList, DataBoxType>*> box,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ComponentPointer /*meta*/,\n      const ObservationValue& observation_value) const {\n    using factory_classes =\n        typename std::decay_t<Metavariables>::factory_creation::factory_classes;\n    call_with_dynamic_type<void, tmpl::at<factory_classes, Event>>(\n        this, [&](auto* const event) {\n          mutate_apply(*event, box, cache, array_index, ComponentPointer{},\n                       observation_value);\n        });\n  }\n\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename ComponentPointer>\n  bool is_ready(const db::DataBox<DbTags>& box,\n                Parallel::GlobalCache<Metavariables>& cache,\n                const ArrayIndex& array_index,\n                const ComponentPointer /*meta*/) const {\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            box))>::factory_creation::factory_classes;\n    return call_with_dynamic_type<bool, tmpl::at<factory_classes, Event>>(\n        this, [&box, &cache, &array_index](auto* const event) {\n          return db::apply<\n              typename std::decay_t<decltype(*event)>::is_ready_argument_tags>(\n              [&event, &cache, &array_index](const auto&... args) {\n                return event->is_ready(args..., cache, array_index,\n                                       ComponentPointer{});\n              },\n              box);\n        });\n  }\n\n  /// Whether the event uses anything depending on the\n  /// evolved_variables.  If this returns false, anything depending on\n  /// the evolved variables may have an incorrect value when the event\n  /// is run.\n  virtual bool needs_evolved_variables() const = 0;\n};\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n\n#include <algorithm>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\nEventsAndTriggers::TriggerAndEvents::TriggerAndEvents() = default;\n\nEventsAndTriggers::TriggerAndEvents::TriggerAndEvents(\n    std::unique_ptr<::Trigger> trigger_in,\n    std::vector<std::unique_ptr<::Event>> events_in)\n    : trigger(std::move(trigger_in)), events(std::move(events_in)) {}\n\nvoid EventsAndTriggers::TriggerAndEvents::pup(PUP::er& p) {\n  p | trigger;\n  p | events;\n}\n\nEventsAndTriggers::EventsAndTriggers() = default;\n\nEventsAndTriggers::EventsAndTriggers(Storage events_and_triggers)\n    : events_and_triggers_(std::move(events_and_triggers)) {}\n\nvoid EventsAndTriggers::pup(PUP::er& p) { p | events_and_triggers_; }\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\n/// \\ingroup EventsAndTriggersGroup\n/// Class that checks triggers and runs events\nclass EventsAndTriggers {\n private:\n  template <typename Event>\n  struct get_tags {\n    using type = typename Event::compute_tags_for_observation_box;\n  };\n\n public:\n  struct TriggerAndEvents {\n    struct Trigger {\n      using type = std::unique_ptr<::Trigger>;\n      static constexpr Options::String help = \"Determines when the Events run.\";\n    };\n    struct Events {\n      using type = std::vector<std::unique_ptr<::Event>>;\n      static constexpr Options::String help =\n          \"These events run when the Trigger fires.\";\n    };\n    static constexpr Options::String help =\n        \"Events that run when the Trigger fires.\";\n    using options = tmpl::list<Trigger, Events>;\n    TriggerAndEvents();\n    TriggerAndEvents(std::unique_ptr<::Trigger> trigger_in,\n                     std::vector<std::unique_ptr<::Event>> events_in);\n    void pup(PUP::er& p);\n    std::unique_ptr<::Trigger> trigger;\n    std::vector<std::unique_ptr<::Event>> events;\n  };\n\n  using Storage = std::vector<TriggerAndEvents>;\n\n  EventsAndTriggers();\n  explicit EventsAndTriggers(Storage events_and_triggers);\n\n  /// Check the triggers and run the associated events.\n  ///\n  /// By default the trigger check just calls the `is_triggered`\n  /// method, but the last argument can be passed to override this.\n  /// It must be a functor taking a `const Trigger&` and returning\n  /// `bool`.\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename Component, typename CheckTrigger = std::nullptr_t>\n  void run_events(const gsl::not_null<db::DataBox<DbTags>*> box,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index, const Component* component,\n                  const Event::ObservationValue& observation_value,\n                  const CheckTrigger& check_trigger = nullptr) const {\n    using compute_tags = tmpl::remove_duplicates<tmpl::filter<\n        tmpl::flatten<tmpl::transform<\n            tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                     Event>,\n            get_tags<tmpl::_1>>>,\n        db::is_compute_tag<tmpl::_1>>>;\n    std::optional<decltype(make_observation_box<compute_tags>(box))>\n        observation_box{};\n    for (const auto& trigger_and_events : events_and_triggers_) {\n      const auto& trigger = trigger_and_events.trigger;\n      const auto& events = trigger_and_events.events;\n      const bool is_triggered = [&]() {\n        if constexpr (std::is_same_v<std::decay_t<CheckTrigger>,\n                                     std::nullptr_t>) {\n          return trigger->is_triggered(*box);\n        } else {\n          return check_trigger(std::as_const(*trigger));\n        }\n      }();\n      if (is_triggered) {\n        if (not observation_box.has_value()) {\n          observation_box = make_observation_box<compute_tags>(box);\n        }\n        for (const auto& event : events) {\n          event->run(make_not_null(&observation_box.value()), cache,\n                     array_index, component, observation_value);\n        }\n      }\n    }\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  template <typename F>\n  void for_each_event(F&& f) const {\n    for (const auto& trigger_and_events : events_and_triggers_) {\n      for (const auto& event : trigger_and_events.events) {\n        f(*event);\n      }\n    }\n  }\n\n private:\n  // The unique pointer contents *must* be treated as const everywhere\n  // in order to make the const global cache behave sanely.  They are\n  // only non-const to make pup work.\n  Storage events_and_triggers_;\n};\n\ntemplate <>\nstruct Options::create_from_yaml<EventsAndTriggers> {\n  using type = EventsAndTriggers;\n  template <typename Metavariables>\n  static type create(const Options::Option& options) {\n    return type(options.parse_as<typename type::Storage, Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n\nnamespace Triggers {\nPUP::able::PUP_ID Always::my_PUP_ID = 0;  // NOLINT\nPUP::able::PUP_ID Not::my_PUP_ID = 0;  // NOLINT\nPUP::able::PUP_ID And::my_PUP_ID = 0;  // NOLINT\nPUP::able::PUP_ID Or::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup_stl.h>\n#include <vector>\n\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// Always triggers.\nclass Always : public Trigger {\n public:\n  /// \\cond\n  explicit Always(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Always);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"Always trigger.\"};\n\n  Always() = default;\n\n  using argument_tags = tmpl::list<>;\n\n  bool operator()() const { return true; }\n};\n\n/// \\ingroup EventsAndTriggersGroup\n/// Negates another trigger.\nclass Not : public Trigger {\n public:\n  /// \\cond\n  Not() = default;\n  explicit Not(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Not);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help = {\"Negates another trigger.\"};\n\n  explicit Not(std::unique_ptr<Trigger> negated_trigger)\n      : negated_trigger_(std::move(negated_trigger)) {}\n\n  using argument_tags = tmpl::list<Tags::DataBox>;\n\n  template <typename DbTags>\n  bool operator()(const db::DataBox<DbTags>& box) const {\n    return not negated_trigger_->is_triggered(box);\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | negated_trigger_; }\n\n private:\n  std::unique_ptr<Trigger> negated_trigger_;\n};\n\n/// \\ingroup EventsAndTriggersGroup\n/// Short-circuiting logical AND of other triggers.\nclass And : public Trigger {\n public:\n  /// \\cond\n  And() = default;\n  explicit And(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(And);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help = {\n      \"Short-circuiting logical AND of other triggers.\"};\n\n  explicit And(std::vector<std::unique_ptr<Trigger>> combined_triggers)\n      : combined_triggers_(std::move(combined_triggers)) {}\n\n  using argument_tags = tmpl::list<Tags::DataBox>;\n\n  template <typename DbTags>\n  bool operator()(const db::DataBox<DbTags>& box) const {\n    for (auto& trigger : combined_triggers_) {\n      if (not trigger->is_triggered(box)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | combined_triggers_; }\n\n private:\n  std::vector<std::unique_ptr<Trigger>> combined_triggers_;\n};\n\n/// \\ingroup EventsAndTriggersGroup\n/// Short-circuiting logical OR of other triggers.\nclass Or : public Trigger {\n public:\n  /// \\cond\n  Or() = default;\n  explicit Or(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Or);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help = {\n      \"Short-circuiting logical OR of other triggers.\"};\n\n  explicit Or(std::vector<std::unique_ptr<Trigger>> combined_triggers)\n      : combined_triggers_(std::move(combined_triggers)) {}\n\n  using argument_tags = tmpl::list<Tags::DataBox>;\n\n  template <typename DbTags>\n  bool operator()(const db::DataBox<DbTags>& box) const {\n    for (auto& trigger : combined_triggers_) {\n      if (trigger->is_triggered(box)) {\n        return true;\n      }\n    }\n    return false;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | combined_triggers_; }\n\n private:\n  std::vector<std::unique_ptr<Trigger>> combined_triggers_;\n};\n\n/// A list of all the logical triggers.\nusing logical_triggers = tmpl::list<Always, And, Not, Or>;\n}  // namespace Triggers\n\ntemplate <>\nstruct Options::create_from_yaml<Triggers::Not> {\n  template <typename Metavariables>\n  static Triggers::Not create(const Options::Option& options) {\n    return Triggers::Not(\n        options.parse_as<std::unique_ptr<Trigger>, Metavariables>());\n  }\n};\n\ntemplate <>\nstruct Options::create_from_yaml<Triggers::And> {\n  template <typename Metavariables>\n  static Triggers::And create(const Options::Option& options) {\n    return Triggers::And(\n        options\n            .parse_as<std::vector<std::unique_ptr<Trigger>>, Metavariables>());\n  }\n};\n\ntemplate <>\nstruct Options::create_from_yaml<Triggers::Or> {\n  template <typename Metavariables>\n  static Triggers::Or create(const Options::Option& options) {\n    return Triggers::Or(\n        options\n            .parse_as<std::vector<std::unique_ptr<Trigger>>, Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines tags related to Events and Triggers\n\n#pragma once\n\n#include <memory>\n#include <pup_stl.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup EventsAndTriggersGroup\n/// Contains the events and triggers\n///\n/// In yaml this is specified as a map of triggers to lists of events:\n/// \\code{.yaml}\n/// EventsAndTriggersAtSlabs:\n///   ? TriggerA:\n///       OptionsForTriggerA\n///   : - Event1:\n///         OptionsForEvent1\n///     - Event2:\n///         OptionsForEvent2\n///   ? TriggerB:\n///       OptionsForTriggerB\n///   : - Event3:\n///         OptionsForEvent3\n///     - Event4:\n///         OptionsForEvent4\n/// \\endcode\ntemplate <Triggers::WhenToCheck WhenToCheck>\nstruct EventsAndTriggers {\n  using type = ::EventsAndTriggers;\n  static constexpr Options::String help = \"Events to run at triggers\";\n  // When the template arguments to this struct are sufficiently\n  // complicated, pretty_type::short_name() run on this struct returns\n  // something that is neither pretty nor short, and leads to an\n  // OptionParser run-time error saying that an option name is greater\n  // than 21 characters.  Adding the name() function below bypasses\n  // pretty_type::short_name().\n  static std::string name() {\n    return \"EventsAndTriggers\" + get_output(WhenToCheck);\n  }\n};\n\nnamespace EventsRunAtCleanup {\nstruct Group {\n  static std::string name() { return \"EventsRunAtCleanup\"; }\n  static constexpr Options::String help =\n      \"Options related to running events on failure.  This is generally \"\n      \"intended for dumping volume data to diagnose failure reasons.\";\n};\n\n/// \\brief A list of events to run at cleanup.\n///\n/// See `Actions::RunEventsOnFailure` for details and caveats.\nstruct Events {\n  static std::string name() { return \"Events\"; }\n  using type = std::vector<std::unique_ptr<::Event>>;\n  static constexpr Options::String help =\n      \"Events to run during the cleanup phase.\";\n  using group = Group;\n};\n\n/// \\brief Observation value for Actions::RunEventsOnFailure.\nstruct ObservationValue {\n  using type = double;\n  static constexpr Options::String help =\n      \"Observation value for events run during the cleanup phase.\";\n  using group = Group;\n};\n}  // namespace EventsRunAtCleanup\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\ingroup EventsAndTriggersGroup\n/// Contains the events and triggers\ntemplate <Triggers::WhenToCheck WhenToCheck>\nstruct EventsAndTriggers : db::SimpleTag {\n  using type = ::EventsAndTriggers;\n  using option_tags = tmpl::list<::OptionTags::EventsAndTriggers<WhenToCheck>>;\n  static constexpr bool is_overlayable = true;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& events_and_triggers) {\n    return serialize_and_deserialize<type>(events_and_triggers);\n  }\n  static std::string name() {\n    return \"EventsAndTriggers\" + get_output(WhenToCheck);\n  }\n};\n\n/// \\brief Events to be run on elements during the\n/// `Parallel::Phase::PostFailureCleanup` phase.\n///\n/// Useful for troubleshooting runs that are failing.\nstruct EventsRunAtCleanup : db::SimpleTag {\n  using type = std::vector<std::unique_ptr<::Event>>;\n  using option_tags = tmpl::list<OptionTags::EventsRunAtCleanup::Events>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& events_run_at_cleanup) {\n    return serialize_and_deserialize<type>(events_run_at_cleanup);\n  }\n};\n\n/// \\brief Observation value for Actions::RunEventsOnFailure.\nstruct EventsRunAtCleanupObservationValue : db::SimpleTag {\n  using type = double;\n  using option_tags =\n      tmpl::list<OptionTags::EventsRunAtCleanup::ObservationValue>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) { return value; }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/Trigger.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup EventsAndTriggersGroup\n * \\brief Contains the available triggers.\n */\nnamespace Triggers {}\n\n/// \\ingroup EventsAndTriggersGroup\n/// Base class for checking whether to run an Event.\nclass Trigger : public PUP::able {\n protected:\n  /// \\cond\n  Trigger() = default;\n  Trigger(const Trigger&) = default;\n  Trigger(Trigger&&) = default;\n  Trigger& operator=(const Trigger&) = default;\n  Trigger& operator=(Trigger&&) = default;\n  /// \\endcond\n\n public:\n  ~Trigger() override = default;\n\n  WRAPPED_PUPable_abstract(Trigger);  // NOLINT\n\n  template <typename DbTags>\n  bool is_triggered(const db::DataBox<DbTags>& box) const {\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            box))>::factory_creation::factory_classes;\n    return call_with_dynamic_type<bool, tmpl::at<factory_classes, Trigger>>(\n        this, [&box](auto* const trigger) { return db::apply(*trigger, box); });\n  }\n};\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/WhenToCheck.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Triggers {\n\nstd::ostream& operator<<(std::ostream& os, const WhenToCheck& when_to_check) {\n  switch (when_to_check) {\n    case WhenToCheck::AtIterations:\n      os << \"AtIterations\";\n      break;\n    case WhenToCheck::AtSlabs:\n      os << \"AtSlabs\";\n      break;\n    case WhenToCheck::AtSteps:\n      os << \"AtSteps\";\n      break;\n    case WhenToCheck::AtCheckpoints:\n      os << \"AtCheckpoints\";\n      break;\n    default:\n      ERROR(\"An unknown check was passed to the stream operator. \"\n            << static_cast<int>(when_to_check));\n  }\n  return os;\n}\n}  // namespace Triggers\n"
  },
  {
    "path": "src/ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines enum class Triggers::WhenToCheck.\n\n#pragma once\n\n#include <cstdint>\n#include <iosfwd>\n\nnamespace Triggers {\n\n/// \\ingroup EventsAndTriggersGroup\n/// \\brief Frequency at which Events and Triggers are checked\nenum class WhenToCheck : uint8_t {\n  AtIterations,  /**< checked at iterations e.g. of an elliptic solve */\n  AtSlabs,       /**< checked at time Slab boundaries */\n  AtSteps,       /**< checked at time step boundaries */\n  AtCheckpoints, /**< checked at checkpoints */\n};\n\n/// Output operator for a WhenToCheck.\nstd::ostream& operator<<(std::ostream& os, const WhenToCheck& when_to_check);\n}  // namespace Triggers\n"
  },
  {
    "path": "src/ParallelAlgorithms/Initialization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Initialization)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  MutateAssign.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  Utilities\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Initialization/MutateAssign.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Initialization {\nnamespace detail {\ntemplate <typename... MutateTags, typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr void mutate_assign_impl(\n    // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n    const gsl::not_null<db::DataBox<BoxTags>*> box,\n    tmpl::list<MutateTags...> /*meta*/, Args&&... args) {\n  static_assert(sizeof...(MutateTags) == sizeof...(args),\n                \"The number of arguments passed to `mutate_assign` must be \"\n                \"equal to the number of tags passed.\");\n  db::mutate<MutateTags...>(\n      [&args...](const auto... box_args) {\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)\n        EXPAND_PACK_LEFT_TO_RIGHT((*box_args = std::forward<Args>(args)));\n      },\n      box);\n}\n}  // namespace detail\n\n/*!\n * \\ingroup DataBoxGroup\n * \\brief Perform a mutation to the \\ref DataBoxGroup `box`, assigning the\n * `args` to the tags in `MutateTagList` in order.\n */\ntemplate <typename MutateTagList, typename BoxTags, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr void mutate_assign(\n    // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n    const gsl::not_null<db::DataBox<BoxTags>*> box, Args&&... args) {\n  // The impl works for zero tags, but we can skip it to improve\n  // compilation performance.\n  if constexpr (tmpl::size<MutateTagList>::value != 0) {\n    detail::mutate_assign_impl(box, MutateTagList{},\n                               std::forward<Args>(args)...);\n  } else {\n    (void)box;\n    expand_pack(args...);\n  }\n}\n}  // namespace Initialization\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ElementInitInterpPoints.hpp\n  ElementReceiveInterpPoints.hpp\n  InitializeInterpolationTarget.hpp\n  InterpolationTargetSendPoints.hpp\n  InterpolationTargetVarsFromElement.hpp\n  PrintInterpolationTargetForDeadlock.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/PointInfoTag.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace intrp::Actions {\n/// \\ingroup ActionsGroup\n/// \\brief Adds interpolation point holders to the Element's DataBox.\n///\n/// This action is for the case in which the points are time-independent.\n///\n/// This action should be placed in the Initialization PDAL for DgElementArray.\n///\n/// Uses: nothing\n///\n/// DataBox changes:\n/// - Adds:\n///   - `intrp::Tags::PointInfo` for each non-sequential target tag\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <size_t VolumeDim, typename AllInterpolationTargetTags>\nstruct ElementInitInterpPoints {\n  using simple_tags = tmpl::transform<\n      intrp::InterpolationTarget_detail::get_non_sequential_target_tags<\n          AllInterpolationTargetTags>,\n      tmpl::bind<intrp::Tags::PointInfo, tmpl::_1,\n                 tmpl::pin<tmpl::size_t<VolumeDim>>>>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Here we only want the `intrp::Tags::PointInfo` default constructed\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace intrp::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Actions/ElementReceiveInterpPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/IdPair.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"ParallelAlgorithms/Interpolation/PointInfoTag.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace intrp::Actions {\n\n/// \\ingroup ActionsGroup\n/// \\brief Receives interpolation points from an InterpolationTarget.\n///\n/// Uses: nothing\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies:\n///   - `intrp::Tags::PointInfo`\ntemplate <typename InterpolationTargetTag>\nstruct ElementReceiveInterpPoints {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex, size_t Dim>\n  static void apply(\n      db::DataBox<DbTags>& box,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      tnsr::I<DataVector, Dim,\n              typename InterpolationTargetTag::compute_target_points::frame>&&\n          coords) {\n    db::mutate<\n        intrp::Tags::PointInfo<InterpolationTargetTag, tmpl::size_t<Dim>>>(\n        [&coords](\n            const gsl::not_null<tnsr::I<\n                DataVector, Dim,\n                typename InterpolationTargetTag::compute_target_points::frame>*>\n                point_infos) { (*point_infos) = std::move(coords); },\n        make_not_null(&box));\n  }\n};\n}  // namespace intrp::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\n/// \\cond\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\n/// Holds Actions for Interpolator and InterpolationTarget.\nnamespace intrp::Actions {\n\n// The purpose of the metafunctions in this namespace is to allow\n// InterpolationTarget::compute_target_points to omit an initialize\n// function and a compute_tags and simple_tags type alias if it\n// doesn't add anything to the DataBox.\nnamespace initialize_interpolation_target_detail {\n\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(compute_tags)\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(simple_tags)\nCREATE_IS_CALLABLE(initialize)\nCREATE_IS_CALLABLE_V(initialize)\n\n}  // namespace initialize_interpolation_target_detail\n\n/// \\ingroup ActionsGroup\n/// \\brief Initializes an InterpolationTarget\n///\n/// Uses: nothing\n///\n/// DataBox changes:\n/// - Adds:\n///   - `Tags::IndicesOfFilledInterpPoints<TemporalId>`\n///   - `Tags::IndicesOfInvalidInterpPoints<TemporalId>`\n///   - `Tags::TemporalIds<TemporalId>`\n///   - `Tags::CompletedTemporalIds<TemporalId>`\n///   - `Tags::InterpolatedVars<InterpolationTargetTag,TemporalId>`\n///   - `::Tags::Variables<typename\n///                   InterpolationTargetTag::vars_to_interpolate_to_target>`\n/// - Removes: nothing\n/// - Modifies: nothing\n///\n/// For requirements on InterpolationTargetTag, see InterpolationTarget\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct InitializeInterpolationTarget {\n  using is_sequential =\n      typename InterpolationTargetTag::compute_target_points::is_sequential;\n  using TemporalId = typename InterpolationTargetTag::temporal_id::type;\n  using return_tag_list_initial = tmpl::list<\n      Tags::IndicesOfFilledInterpPoints<TemporalId>,\n      Tags::IndicesOfInvalidInterpPoints<TemporalId>,\n      Tags::TemporalIds<TemporalId>, Tags::CompletedTemporalIds<TemporalId>,\n      Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>,\n      ::Tags::Variables<\n          typename InterpolationTargetTag::vars_to_interpolate_to_target>>;\n\n  using simple_tags = tmpl::append<\n      return_tag_list_initial,\n      initialize_interpolation_target_detail::get_simple_tags_or_default_t<\n          typename InterpolationTargetTag::compute_target_points,\n          tmpl::list<>>>;\n  using compute_tags = tmpl::append<\n      initialize_interpolation_target_detail::get_compute_tags_or_default_t<\n          typename InterpolationTargetTag::compute_target_points, tmpl::list<>>,\n      typename InterpolationTargetTag::compute_items_on_target>;\n  using const_global_cache_tags = tmpl::list<intrp::Tags::Verbosity>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if constexpr (\n        initialize_interpolation_target_detail::is_initialize_callable_v<\n            typename InterpolationTargetTag::compute_target_points,\n            const gsl::not_null<db::DataBox<DbTagsList>*>,\n            const Parallel::GlobalCache<Metavariables>&>) {\n      InterpolationTargetTag::compute_target_points::initialize(\n          make_not_null(&box), cache);\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace intrp::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Actions/InterpolationTargetSendPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/SimpleActionOnElement.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementReceiveInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace intrp {\nnamespace Actions {\n\n/// \\ingroup ActionsGroup\n/// \\brief Sends interpolation points to all the Elements.\n///\n/// This action is for the case in which the points are time-independent\n/// in the frame of the InterpolationTarget (which may or may not mean that\n/// the points are time-independent in the grid frame).\n///\n/// This action should be placed in the Registration PDAL for\n/// InterpolationTarget.\n///\n/// Uses:\n/// - DataBox:\n///   - Anything that the particular\n///     InterpolationTargetTag::compute_target_points needs from the DataBox.\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename InterpolationTargetTag>\nstruct InterpolationTargetSendTimeIndepPointsToElements {\n  template <typename DbTags, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename Metavariables,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        not InterpolationTargetTag::compute_target_points::is_sequential::value,\n        \"Actions::InterpolationTargetSendTimeIndepPointsToElement can be used \"\n        \"only with non-sequential targets, since a sequential target is \"\n        \"time-dependent by definition.\");\n    auto coords = InterpolationTargetTag::compute_target_points::points(\n        box, tmpl::type_<Metavariables>{});\n    using ReceiverProxy =\n        typename InterpolationTargetTag::template interpolating_component<\n            Metavariables>;\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ReceiverProxy>(cache);\n    if constexpr (Parallel::is_dg_element_collection_v<ReceiverProxy>) {\n      Parallel::threaded_action<Parallel::Actions::SimpleActionOnElement<\n          ElementReceiveInterpPoints<InterpolationTargetTag>, true>>(\n          receiver_proxy, std::move(coords));\n    } else {\n      Parallel::simple_action<\n          ElementReceiveInterpPoints<InterpolationTargetTag>>(\n          receiver_proxy, std::move(coords));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Actions/InterpolationTargetVarsFromElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\nstruct FunctionsOfTime;\n}  // namespace domain::Tags\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace intrp {\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// \\brief Receives interpolated variables from an `Element` on a subset\n///  of the target points.\n///\n/// If interpolated variables for all target points have been received, then\n/// - Checks if functions of time are ready (if there are any). If they are not\n///   ready, register a simple action callback with the GlobalCache to this\n///   action.\n/// - Calls `InterpolationTargetTag::post_interpolation_callbacks`\n/// - Removes the finished `temporal_id` from `Tags::TemporalIds<TemporalId>`\n///   and adds it to `Tags::CompletedTemporalIds<TemporalId>`\n/// - Removes `Tags::InterpolatedVars<InterpolationTargetTag,TemporalId>`,\n///   `Tags::IndicesOfFilledInterpPoints`, and\n///   `Tags::IndicesOfInvalidInterpPoints` for the finished `temporal_id`.\n///\n/// Uses:\n/// - DataBox:\n///   - `Tags::TemporalIds<TemporalId>`\n///   - `Tags::IndicesOfFilledInterpPoints<TemporalId>`\n///   - `Tags::InterpolatedVars<InterpolationTargetTag,TemporalId>`\n///   - `Tags::InterpolatedVars<InterpolationTargetTag,TemporalId>`\n///   - `::Tags::Variables<typename\n///                   InterpolationTargetTag::vars_to_interpolate_to_target>`\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies:\n///   - `Tags::TemporalIds<TemporalId>`\n///   - `Tags::CompletedTemporalIds<TemporalId>`\n///   - `Tags::IndicesOfFilledInterpPoints<TemporalId>`\n///   - `Tags::InterpolatedVars<InterpolationTargetTag,TemporalId>`\n///   - `::Tags::Variables<typename\n///                   InterpolationTargetTag::vars_to_interpolate_to_target>`\n///\n/// For requirements on InterpolationTargetTag, see InterpolationTarget\n/// and intrp::protocols::InterpolationTargetTag\n///\n/// \\note This action can be used only with InterpolationTargets that are\n/// non-sequential.\ntemplate <typename InterpolationTargetTag>\nstruct InterpolationTargetVarsFromElement {\n  /// For requirements on Metavariables, see InterpolationTarget\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex, typename TemporalId>\n  static void apply(\n      db::DataBox<DbTags>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index,\n      const std::vector<Variables<\n          typename InterpolationTargetTag::vars_to_interpolate_to_target>>&\n          vars_src,\n      const std::vector<BlockLogicalCoords<Metavariables::volume_dim>>&\n          block_logical_coords,\n      const std::vector<std::vector<size_t>>& global_offsets,\n      const TemporalId& temporal_id,\n      const bool vars_have_already_been_received = false) {\n    static_assert(\n        not InterpolationTargetTag::compute_target_points::is_sequential::value,\n        \"Use InterpolationTargetGetVarsFromElement only with non-sequential\"\n        \" compute_target_points\");\n    std::stringstream ss{};\n    const ::Verbosity& verbosity = Parallel::get<intrp::Tags::Verbosity>(cache);\n    const bool debug_print = verbosity >= ::Verbosity::Debug;\n    const bool verbose_print = verbosity >= ::Verbosity::Verbose;\n    if (verbose_print) {\n      ss << InterpolationTarget_detail::target_output_prefix<\n                InterpolationTargetVarsFromElement, InterpolationTargetTag>(\n                temporal_id)\n         << \", \";\n    }\n    // Check if we already have completed interpolation at this\n    // temporal_id.\n    const auto& completed_ids =\n        db::get<Tags::CompletedTemporalIds<TemporalId>>(box);\n    // (Search from the end because temporal_id is more likely to be\n    // at the end of the list then at the beginning.)\n    if (UNLIKELY(std::find(completed_ids.rbegin(), completed_ids.rend(),\n                           temporal_id) != completed_ids.rend())) {\n      // The code will get into this 'if' statement in the following\n      // scenario:\n      // - There is at least one interpolation point exactly on the\n      //   boundary of two or more Elements, so that\n      //   InterpolationTargetVarsFromElement is called more than once\n      //   with data for the same interpolation point (this is ok,\n      //   and add_received_variables handles this).\n      // - The only Elements that have not yet called\n      //   InterpolationTargetVarsFromElement for this temporal_id are\n      //   those that have data only for duplicated interpolation\n      //   points, and the InterpolationTarget has already received\n      //   that data from other Elements.\n      // In this case, the InterpolationTarget proceeds to do its\n      // work because it has all the data it needs. There is now\n      // one more condition needed for the scenario that gets\n      // us inside this 'if':\n      // - The InterpolationTarget has already completed its work at\n      //   this temporal_id, and it has cleaned up its data structures\n      //   for this temporal_id before all of the remaining calls to\n      //   InterpolationTargetVarsFromElement have occurred at this\n      //   temporal_id, and now we are in one of those remaining\n      //   calls.\n      //\n      // If this scenario occurs, we just return. This is because the\n      // InterpolationTarget is done and there is nothing left to do\n      // at this temporal_id.  Note that if there were extra work to\n      // do at this temporal_id, then CompletedTemporalIds would not\n      // have an entry for this temporal_id.\n      return;\n    }\n\n    // Call set_up_interpolation only if it has not been called for this\n    // temporal_id.\n    // If flag_temporal_ids_for_interpolation returns an empty list, then\n    // flag_temporal_ids_for_interpolation has already been called for the\n    // same temporal_id (by an invocation of InterpolationTargetVarsFromElement\n    // by a different Element) and hence set_up_interpolation has already\n    // been called.\n    if (InterpolationTarget_detail::flag_temporal_id_for_interpolation<\n            InterpolationTargetTag>(make_not_null(&box), temporal_id)) {\n      InterpolationTarget_detail::set_up_interpolation<InterpolationTargetTag>(\n          make_not_null(&box), temporal_id, block_logical_coords);\n    }\n\n    if (not vars_have_already_been_received) {\n      InterpolationTarget_detail::add_received_variables<\n          InterpolationTargetTag>(make_not_null(&box), vars_src, global_offsets,\n                                  temporal_id);\n    }\n\n    if (InterpolationTarget_detail::have_data_at_all_points<\n            InterpolationTargetTag>(box, temporal_id, verbosity)) {\n      // Check if functions of time are ready on this component. Since this\n      // simple action has already been called, we don't need to resend the\n      // data, so we just pass empty vectors for vars_src and\n      // block_logical_coords\n      if (not domain::functions_of_time_are_ready_simple_action_callback<\n              domain::Tags::FunctionsOfTime,\n              InterpolationTargetVarsFromElement>(\n              cache, array_index,\n              std::add_pointer_t<ParallelComponent>{nullptr},\n              InterpolationTarget_detail::get_temporal_id_value(temporal_id),\n              std::nullopt, std::decay_t<decltype(vars_src)>{},\n              std::decay_t<decltype(block_logical_coords)>{}, global_offsets,\n              temporal_id, true)) {\n        return;\n      }\n      // All the valid points have been interpolated.\n      // We throw away the return value of call_callbacks in this case\n      // (it is known to be always true; it can be false only for\n      //  sequential interpolations, which is static-asserted against above).\n      InterpolationTarget_detail::call_callbacks<InterpolationTargetTag>(\n          make_not_null(&box), make_not_null(&cache), temporal_id);\n      InterpolationTarget_detail::clean_up_interpolation_target<\n          InterpolationTargetTag>(make_not_null(&box), temporal_id);\n      if (verbose_print) {\n        ss << \"calling callbacks and cleaning up target.\";\n        Parallel::printf(\"%s\\n\", ss.str());\n      }\n    } else if (debug_print) {\n      ss << \"not enough data. Waiting. See Total/valid/invalid points line.\";\n      Parallel::printf(\"%s\\n\", ss.str());\n    }\n  }\n};\n}  // namespace Actions\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Actions/PrintInterpolationTargetForDeadlock.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iomanip>\n#include <ios>\n#include <limits>\n#include <sstream>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace deadlock {\n/*!\n * \\brief Simple action to print deadlock info on an interpolation target.\n *\n * \\details This will print the following information for all temporal ids\n * stored in `intrp::Tags::TemporalIds`.\n *\n * - `intrp::Tags::IndicesOfFilledInterpPoints`\n * - `intrp::Tags::IndicesOfInvalidInterpPoints1\n * - Size of `intrp::Tags::InterpolatedVars`\n */\nstruct PrintInterpolationTarget {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(const db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const std::string& file_name) {\n    std::stringstream ss{};\n    ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)\n       << std::scientific;\n\n    using TargetTag = typename ParallelComponent::interpolation_target_tag;\n    using TemporalId = typename TargetTag::temporal_id::type;\n\n    const auto stream_points = [&](const TemporalId& temporal_id) {\n      const auto& filled_indices =\n          db::get<intrp::Tags::IndicesOfFilledInterpPoints<TemporalId>>(box);\n      const auto& invalid_indices =\n          db::get<intrp::Tags::IndicesOfInvalidInterpPoints<TemporalId>>(box);\n      const auto& interpolated_vars =\n          db::get<intrp::Tags::InterpolatedVars<TargetTag, TemporalId>>(box);\n\n      const size_t expected_size =\n          interpolated_vars.at(temporal_id).number_of_grid_points();\n      const size_t filled_size = filled_indices.count(temporal_id) > 0\n                                     ? filled_indices.at(temporal_id).size()\n                                     : 0_st;\n      const size_t invalid_size = invalid_indices.count(temporal_id) > 0\n                                      ? invalid_indices.at(temporal_id).size()\n                                      : 0_st;\n\n      ss << \"Total points expected = \" << expected_size\n         << \", valid points received = \" << filled_size\n         << \", invalid points received \" << invalid_size << \". \";\n    };\n\n    const auto& temporal_ids =\n        db::get<intrp::Tags::TemporalIds<TemporalId>>(box);\n\n    if (temporal_ids.empty()) {\n      ss << pretty_type::name<TargetTag>() << \", No temporal ids.\";\n      Parallel::printf(\"%s\\n\", ss.str());\n      return;\n    }\n\n    ss << \"========== BEGIN TARGET \" << pretty_type::name<TargetTag>()\n       << \" ==========\\n\";\n\n    for (const auto& temporal_id : temporal_ids) {\n      ss << \"Temporal id \" << temporal_id << \", \";\n\n      stream_points(temporal_id);\n\n      ss << \"\\n\";\n    }\n\n    ss << \"========== END TARGET \" << pretty_type::name<TargetTag>()\n       << \" ============\\n\";\n\n    Parallel::fprintf(file_name, \"%s\\n\", ss.str());\n  }\n};\n}  // namespace deadlock\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ParallelInterpolation)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComputeExcisionBoundaryVolumeQuantities.hpp\n  ComputeExcisionBoundaryVolumeQuantities.tpp\n  InterpolationTarget.hpp\n  InterpolationTargetDetail.hpp\n  Intrp.hpp\n  IntrpOptionHolders.hpp\n  PointInfoTag.hpp\n  Tags.hpp\n  TagsMetafunctions.hpp\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_GlobalCache\n  )\n\n# Some of these dependencies should be removed, see issue\n# https://github.com/sxs-collaboration/spectre/issues/3561\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  ErrorHandling\n  GeneralRelativity\n  Interpolation\n  Logging\n  Observer\n  Options\n  Parallel\n  Serialization\n  Spectral\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  Utilities\n  INTERFACE\n  Actions\n  EventsAndTriggers\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Callbacks)\nadd_subdirectory(Events)\nadd_subdirectory(Protocols)\nadd_subdirectory(Targets)\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Callbacks/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Callbacks.hpp\n  ObserveLineSegment.hpp\n  ObserveSurfaceData.hpp\n  ObserveTimeSeriesOnSurface.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Callbacks/Callbacks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace intrp {\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Contains callback functions called by `InterpolationTarget`s.\n */\nnamespace callbacks {}\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Callbacks/ObserveLineSegment.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace intrp {\nnamespace callbacks {\n\n/* \\brief post_interpolation_callback that outputs requested tensors\n * interpolated onto a LineSegment. The data is written as volume data into the\n * `Reductions` file with Quadrature `CellCentered` and Basis\n * `FiniteDifference`.\n *\n * Uses:\n * - Metavariables\n *   - `temporal_id`\n * - DataBox:\n *   - `TensorsToObserve`\n * - GlobalCache:\n *   - `observers::Tags::ReductionFileName`\n *\n * Conforms to the intrp::protocols::PostInterpolationCallback protocol\n *\n * For requirements on InterpolationTargetTag, see\n * intrp::protocols::InterpolationTargetTag\n *\n */\ntemplate <typename TensorsToObserve, typename InterpolationTargetTag>\nstruct ObserveLineSegment\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {\n  static constexpr double fill_invalid_points_with =\n      std::numeric_limits<double>::quiet_NaN();\n\n  using const_global_cache_tags =\n      tmpl::list<observers::Tags::ReductionFileName>;\n\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const TemporalId& temporal_id) {\n    static_assert(\n        tmpl::list_contains_v<TensorsToObserve,\n                              domain::Tags::Coordinates<\n                                  Metavariables::volume_dim, Frame::Inertial>>,\n        \"When observing tensors on a line segment, please include the inertial \"\n        \"coordinates in TensorsToObserve. This is so that the output file \"\n        \"contains the coordinates, so then the output file is all that is \"\n        \"needed for subsequent visualization or analysis of the output data.\");\n    std::vector<TensorComponent> tensor_components{};\n    const size_t number_of_points =\n        get<tmpl::front<TensorsToObserve>>(box)[0].size();\n    tmpl::for_each<TensorsToObserve>(\n        [&box, &tensor_components, &number_of_points](auto tag_v) {\n          using Tag = tmpl::type_from<decltype(tag_v)>;\n          const auto& tensor = get<Tag>(box);\n          for (size_t i = 0; i < tensor.size(); ++i) {\n            tensor_components.emplace_back(\n                db::tag_name<Tag>() + tensor.component_suffix(i), tensor[i]);\n            ASSERT(number_of_points == tensor[i].size(),\n                   \"All tensor components are expected to have the same size \"\n                       << number_of_points << \", but \"\n                       << db::tag_name<Tag>() + tensor.component_suffix(i)\n                       << \"has size \" << tensor[i].size());\n          }\n        });\n\n    const std::string& name = pretty_type::name<InterpolationTargetTag>();\n    const std::string subfile_path{std::string{\"/\"} + name};\n    const std::vector<size_t> extents_vector{number_of_points};\n    const std::vector<Spectral::Basis> bases_vector{\n        Spectral::Basis::FiniteDifference};\n    const std::vector<Spectral::Quadrature> quadratures_vector{\n        1, Spectral::Quadrature::CellCentered};\n    const observers::ObservationId& observation_id = observers::ObservationId(\n        InterpolationTarget_detail::get_temporal_id_value(temporal_id),\n        subfile_path + \".vol\");\n    auto& proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    // We call this on proxy[0] because the 0th element of a NodeGroup is\n    // always guaranteed to be present.\n    Parallel::threaded_action<observers::ThreadedActions::WriteVolumeData>(\n        proxy[0], Parallel::get<observers::Tags::ReductionFileName>(cache),\n        subfile_path, observation_id,\n        std::vector<ElementVolumeData>{{name, std::move(tensor_components),\n                                        extents_vector, bases_vector,\n                                        quadratures_vector}});\n  }\n};\n}  // namespace callbacks\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Callbacks/ObserveSurfaceData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace intrp {\nnamespace callbacks {\n/// \\brief post_interpolation_callback that outputs 2D \"volume\" data on a\n/// surface and the surface's spherical harmonic data\n///\n/// \\details\n/// Uses:\n/// - Metavariables\n///   - `temporal_id`\n/// - DataBox:\n///   - `TagsToObserve` (each tag must be a Scalar<DataVector>)\n///\n/// Conforms to the intrp::protocols::PostInterpolationCallback protocol\n///\n/// For requirements on InterpolationTargetTag, see\n/// intrp::protocols::InterpolationTargetTag\n///\n/// The columns of spherical harmonic data written take the form\n///\n/// \\code\n/// [Time, {Frame}ExpansionCenter_x, {Frame}ExpansionCenter_y,\n/// {Frame}ExpansionCenter_z, Lmax, coef(0,0), ... coef(Lmax,Lmax)]\n/// \\endcode\n///\n/// where `coef(l,m)` refers to the strahlkorper coefficients stored and defined\n/// by `ylm::Strahlkorper::coefficients() const`. It is assumed that\n/// \\f$l_{max} = m_{max}\\f$.\n///\n/// \\note Currently, \\f$l_{max}\\f$ for a given surface does not change over the\n/// course of the simulation, which means that the total number of columns of\n/// coefficients that we need to write is also constant. The current\n/// implementation of writing the coefficients at one time assumes \\f$l_{max}\\f$\n/// of a surface remains constant. If and when in the future functionality for\n/// an adaptive \\f$l_{max}\\f$ is added, the implementation for writing the\n/// coefficients will need to be updated to account for this. One possible way\n/// to address this is to have a known maximum \\f$l_{max}\\f$ for a given surface\n/// and write all coefficients up to that maximum \\f$l_{max}\\f$.\ntemplate <typename TagsToObserve, typename InterpolationTargetTag,\n          typename HorizonFrame>\nstruct ObserveSurfaceData\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {\n  static constexpr double fill_invalid_points_with =\n      std::numeric_limits<double>::quiet_NaN();\n\n  using const_global_cache_tags = tmpl::list<observers::Tags::SurfaceFileName>;\n\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const TemporalId& temporal_id) {\n    const auto& strahlkorper = get<ylm::Tags::Strahlkorper<HorizonFrame>>(box);\n    const ylm::Spherepack& ylm = strahlkorper.ylm_spherepack();\n\n    // Output the inertial-frame coordinates of the Stralhlkorper.\n    // Note that these coordinates are not\n    // Spherepack-evenly-distributed over the inertial-frame sphere\n    // (they are Spherepack-evenly-distributed over the HorizonFrame\n    // sphere).\n    std::vector<TensorComponent> tensor_components;\n    if constexpr (db::tag_is_retrievable_v<\n                      ylm::Tags::CartesianCoords<::Frame::Inertial>,\n                      db::DataBox<DbTags>>) {\n      const auto& inertial_strahlkorper_coords =\n          get<ylm::Tags::CartesianCoords<::Frame::Inertial>>(box);\n      tensor_components.push_back(\n          {\"InertialCoordinates_x\"s, get<0>(inertial_strahlkorper_coords)});\n      tensor_components.push_back(\n          {\"InertialCoordinates_y\"s, get<1>(inertial_strahlkorper_coords)});\n      tensor_components.push_back(\n          {\"InertialCoordinates_z\"s, get<2>(inertial_strahlkorper_coords)});\n    }\n\n    // Output each tag if it is a scalar. Otherwise, throw a compile-time\n    // error. This could be generalized to handle tensors of nonzero rank by\n    // looping over the components, so each component could be visualized\n    // separately as a scalar. But in practice, this generalization is\n    // probably unnecessary, because Strahlkorpers are typically only\n    // visualized with scalar quantities (used set the color at different\n    // points on the surface).\n    tmpl::for_each<TagsToObserve>([&box, &tensor_components](auto tag_v) {\n      using Tag = tmpl::type_from<decltype(tag_v)>;\n      const auto tag_name = db::tag_name<Tag>();\n      const auto& tensor = get<Tag>(box);\n      for (size_t i = 0; i < tensor.size(); ++i) {\n        tensor_components.emplace_back(tag_name + tensor.component_suffix(i),\n                                       tensor[i]);\n      }\n    });\n\n    const std::string& surface_name =\n        pretty_type::name<InterpolationTargetTag>();\n    const std::string subfile_path{std::string{\"/\"} + surface_name};\n    const std::vector<size_t> extents_vector{\n        {ylm.physical_extents()[0], ylm.physical_extents()[1]}};\n    const std::vector<Spectral::Basis> bases_vector{\n        2, Spectral::Basis::SphericalHarmonic};\n    const std::vector<Spectral::Quadrature> quadratures_vector{\n        {Spectral::Quadrature::Gauss, Spectral::Quadrature::Equiangular}};\n    const double time =\n        InterpolationTarget_detail::get_temporal_id_value(temporal_id);\n    const observers::ObservationId observation_id{time, subfile_path + \".vol\"};\n\n    auto& proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    // We call this on proxy[0] because the 0th element of a NodeGroup is\n    // always guaranteed to be present.\n    Parallel::threaded_action<observers::ThreadedActions::WriteVolumeData>(\n        proxy[0], Parallel::get<observers::Tags::SurfaceFileName>(cache),\n        subfile_path, observation_id,\n        std::vector<ElementVolumeData>{{surface_name, tensor_components,\n                                        extents_vector, bases_vector,\n                                        quadratures_vector}});\n\n    std::vector<std::string> ylm_legend{};\n    std::vector<double> ylm_data{};\n\n    // When option LMax is present in the global cache,\n    // check that the Strahlkorper resolution does not exceed it.\n    // LMax sets a maximum resolution for\n    // the Strahlkorper resolution (when adjusted using adaptive\n    // horizon finding) and the number of (zero padded) columns to output,\n    // to avoid H5 errors caused by different rows (corresponding to\n    // different times) having different numbers of columns.\n    // If this option is not present in the cache, skip the check and just write\n    // the modes of the Strahlkorper, assuming that the Strahlkorper\n    // resolution never changes.\n    size_t max_l_to_write = strahlkorper.l_max();\n    if constexpr (Parallel::is_in_global_cache<Metavariables, ah::Tags::LMax>) {\n      const auto& max_resolution_and_output_l =\n          Parallel::get<ah::Tags::LMax>(cache);\n      if (UNLIKELY(max_resolution_and_output_l < strahlkorper.l_max())) {\n        ERROR(\"The option LMax (\"\n              << max_resolution_and_output_l << \") is smaller than the current \"\n              << \"Strahlkorper resolution L=\" << strahlkorper.l_max()\n              << \". Increase LMax or decrease \"\n              << \"Strahlkorper resolution L to avoid truncating data.\");\n      }\n      max_l_to_write = max_resolution_and_output_l;\n    }\n\n    ylm::fill_ylm_legend_and_data(make_not_null(&ylm_legend),\n                                  make_not_null(&ylm_data), strahlkorper, time,\n                                  max_l_to_write);\n\n    const std::string ylm_subfile_name{std::string{\"/\"} + surface_name +\n                                       \"_Ylm\"};\n\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        proxy[0], ylm_subfile_name, std::move(ylm_legend),\n        std::make_tuple(std::move(ylm_data)));\n  }\n};\n}  // namespace callbacks\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace observers {\ntemplate <class Metavariables>\nstruct ObserverWriter;\n}  // namespace observers\n/// \\endcond\n\nnamespace intrp {\nnamespace callbacks {\n\nnamespace detail {\n\ntemplate<typename T>\nstruct is_array_of_double : std::false_type {};\n\ntemplate<std::size_t N>\nstruct is_array_of_double<std::array<double, N>> : std::true_type {};\n\ntemplate <typename... Ts>\nauto make_legend(tmpl::list<Ts...> /* meta */) {\n    std::vector<std::string> legend = {\"Time\"};\n\n    [[maybe_unused]] auto append_tags = [&legend](auto tag) {\n      using TagType = decltype(tag);\n      using ReturnType = typename TagType::type;\n\n      if constexpr (is_array_of_double<ReturnType>::value) {\n        constexpr std::array<const char*, 3> suffix = {\"_x\", \"_y\", \"_z\"};\n        for (size_t i = 0; i < std::tuple_size<ReturnType>::value; ++i) {\n          legend.push_back(db::tag_name<TagType>() + gsl::at(suffix, i));\n        }\n      } else {\n        legend.push_back(db::tag_name<TagType>());\n      }\n    };\n\n    (append_tags(Ts{}), ...);\n\n    return legend;\n}\n\ntemplate <typename DbTags, typename... Ts>\nauto make_reduction_data(const db::DataBox<DbTags>& box, double time,\n                         tmpl::list<Ts...> /* meta */) {\n  return std::make_tuple(time, get<Ts>(box)...);\n}\n\n}  // namespace detail\n\n/// \\brief post_interpolation_callback that outputs\n/// a time series on a surface.\n///\n/// Uses:\n/// - Metavariables\n///   - `temporal_id`\n/// - DataBox:\n///   - `TagsToObserve`\n///\n/// Conforms to the intrp::protocols::PostInterpolationCallback protocol\n///\n/// For requirements on InterpolationTargetTag, see\n/// intrp::protocols::InterpolationTargetTag\ntemplate <typename TagsToObserve, typename InterpolationTargetTag>\nstruct ObserveTimeSeriesOnSurface\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {\n  static constexpr double fill_invalid_points_with =\n      std::numeric_limits<double>::quiet_NaN();\n\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const TemporalId& temporal_id) {\n    auto& proxy = Parallel::get_parallel_component<\n        observers::ObserverWriter<Metavariables>>(cache);\n\n    // We call this on proxy[0] because the 0th element of a NodeGroup is\n    // always guaranteed to be present.\n    Parallel::threaded_action<\n        observers::ThreadedActions::WriteReductionDataRow>(\n        proxy[0],\n        std::string{\"/\" + pretty_type::name<InterpolationTargetTag>()},\n        detail::make_legend(TagsToObserve{}),\n        detail::make_reduction_data(\n            box, InterpolationTarget_detail::get_temporal_id_value(temporal_id),\n            TagsToObserve{}));\n  }\n};\n}  // namespace callbacks\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeVarsToInterpolate.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Mesh;\ntemplate <typename VariablesTags>\nclass Variables;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace intrp {\n\n/// Given the generalized harmonic variables in the volume, computes\n/// the quantities that will be interpolated onto an excision boundary.\n///\n/// This is meant to be the primary `compute_vars_to_interpolate`\n/// for the computation of the characteristic speeds on the\n/// excision boundary.\n///\n/// SrcTagList and DestTagList have limited flexibility, and their\n/// restrictions are static_asserted inside the apply functions.  The\n/// lack of complete flexibility is intentional, because most\n/// computations (e.g. for observers) should be done only on the\n/// horizon surface (i.e. after interpolation) as opposed to in the\n/// volume; only those computations that require data in the volume\n/// (e.g. volume numerical derivatives) should be done here.\n///\n/// For the dual-frame case, numerical derivatives of Jacobians are\n/// taken in order to avoid Hessians.\n///\n/// SrcTagList is usually `ah::source_vars`, and the allowed and required tags\n/// in SrcTagList are given by the type aliases `allowed_src_tags` and\n/// `required_src_tags` below.\n///\n/// DestTagList is usually `vars_to_interpolate_to_target` in the\n/// `InterpolationTarget` that uses `ComputeExcisionBoundaryVolumeQuantities`.\n/// The allowed and required tags in DestTagList are given by\n/// the type aliases `allowed_dest_tags` and `required_dest_tags` below.\nstruct ComputeExcisionBoundaryVolumeQuantities\n    : tt::ConformsTo<intrp::protocols::ComputeVarsToInterpolate> {\n  /// Single-frame case\n  template <typename SrcTagList, typename DestTagList>\n  static void apply(gsl::not_null<Variables<DestTagList>*> target_vars,\n                    const Variables<SrcTagList>& src_vars, const Mesh<3>& mesh);\n  /// Dual-frame case\n  template <typename SrcTagList, typename DestTagList, typename TargetFrame>\n  static void apply(\n      gsl::not_null<Variables<DestTagList>*> target_vars,\n      const Variables<SrcTagList>& src_vars, const Mesh<3>& mesh,\n      const Jacobian<DataVector, 3, TargetFrame, Frame::Inertial>&\n          jac_target_to_inertial,\n      const InverseJacobian<DataVector, 3, TargetFrame, Frame::Inertial>&\n          invjac_target_to_inertial,\n      const Jacobian<DataVector, 3, Frame::ElementLogical, TargetFrame>&\n          jac_logical_to_target,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical, TargetFrame>&\n          invjac_logical_to_target,\n      const InverseJacobian<DataVector, 3, Frame::Grid, TargetFrame>&\n          invjac_grid_to_target,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_mesh_velocity,\n      const tnsr::I<DataVector, 3, TargetFrame>&\n          grid_to_target_frame_mesh_velocity);\n\n  using allowed_src_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                 ::Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>>;\n\n  using required_src_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>>;\n\n  template <typename TargetFrame>\n  using allowed_dest_tags_target_frame = tmpl::list<\n      gr::Tags::SpatialMetric<DataVector, 3, TargetFrame>,\n      gr::Tags::SpacetimeMetric<DataVector, 3, TargetFrame>,\n      gr::Tags::Lapse<DataVector>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>, TargetFrame>,\n      gr::Tags::Shift<DataVector, 3, TargetFrame>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, 3, TargetFrame>,\n                    tmpl::size_t<3>, TargetFrame>,\n      gr::Tags::ShiftyQuantity<DataVector, 3, TargetFrame>,\n      ::domain::Tags::InverseJacobian<3, Frame::Grid, TargetFrame>,\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, TargetFrame>>;\n\n  template <typename TargetFrame>\n  using allowed_dest_tags = tmpl::remove_duplicates<\n      tmpl::append<allowed_dest_tags_target_frame<TargetFrame>,\n                   allowed_dest_tags_target_frame<Frame::Inertial>>>;\n\n  template <typename TargetFrame>\n  using required_dest_tags = tmpl::list<>;\n};\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.hpp\"\n\n#pragma once\n\n#include <cstdint>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/FrameTransform.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace intrp {\n\n/// Single frame case\ntemplate <typename SrcTagList, typename DestTagList>\nvoid ComputeExcisionBoundaryVolumeQuantities::apply(\n    const gsl::not_null<Variables<DestTagList>*> target_vars,\n    const Variables<SrcTagList>& src_vars, const Mesh<3>& /*mesh*/) {\n  static_assert(\n      std::is_same_v<tmpl::list_difference<SrcTagList, allowed_src_tags>,\n                     tmpl::list<>>,\n      \"Found a src tag that is not allowed\");\n  static_assert(\n      std::is_same_v<tmpl::list_difference<required_src_tags, SrcTagList>,\n                     tmpl::list<>>,\n      \"A required src tag is missing\");\n\n  static_assert(\n      std::is_same_v<tmpl::list_difference<DestTagList,\n                                           allowed_dest_tags<Frame::Inertial>>,\n                     tmpl::list<>>,\n      \"Found a dest tag that is not allowed\");\n  static_assert(\n      std::is_same_v<tmpl::list_difference<required_dest_tags<Frame::Inertial>,\n                                           DestTagList>,\n                     tmpl::list<>>,\n      \"A required dest tag is missing\");\n\n  if (target_vars->number_of_grid_points() !=\n      src_vars.number_of_grid_points()) {\n    target_vars->initialize(src_vars.number_of_grid_points());\n  }\n\n  using spacetime_metric_tag = gr::Tags::SpacetimeMetric<DataVector, 3>;\n  using spatial_metric_tag = gr::Tags::SpatialMetric<DataVector, 3>;\n  using inv_spatial_metric_tag = gr::Tags::InverseSpatialMetric<DataVector, 3>;\n  using lapse_tag = gr::Tags::Lapse<DataVector>;\n  using deriv_lapse_tag =\n      ::Tags::deriv<lapse_tag, tmpl::size_t<3>, Frame::Inertial>;\n  using shift_tag = gr::Tags::Shift<DataVector, 3>;\n  using deriv_shift_tag =\n      ::Tags::deriv<shift_tag, tmpl::size_t<3>, Frame::Inertial>;\n  using spatial_christoffel_tag =\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>;\n  // Needed for an intermediate computation\n  using spacetime_normal_vector_tag =\n      gr::Tags::SpacetimeNormalVector<DataVector, 3>;\n  using inv_spacetime_metric_tag =\n      gr::Tags::InverseSpacetimeMetric<DataVector, 3>;\n\n  // All of the temporary tags, including some that may be repeated\n  // in the target_variables (for now).\n  using full_temp_tags_list =\n      tmpl::list<spacetime_metric_tag, spatial_metric_tag,\n                 inv_spatial_metric_tag, lapse_tag, deriv_lapse_tag, shift_tag,\n                 deriv_shift_tag, spatial_christoffel_tag,\n                 spacetime_normal_vector_tag, inv_spacetime_metric_tag>;\n\n  // temp tags without variables that are already in DestTagList.\n  using temp_tags_list =\n      tmpl::list_difference<full_temp_tags_list, DestTagList>;\n  TempBuffer<temp_tags_list> buffer(src_vars.number_of_grid_points());\n\n  // These may or may not be temporaries\n  auto& lapse = *(get<lapse_tag>(target_vars, make_not_null(&buffer)));\n  auto& deriv_lapse =\n      *(get<deriv_lapse_tag>(target_vars, make_not_null(&buffer)));\n  auto& shift = *(get<shift_tag>(target_vars, make_not_null(&buffer)));\n  auto& deriv_shift =\n      *(get<deriv_shift_tag>(target_vars, make_not_null(&buffer)));\n  auto& spatial_metric =\n      *(get<spatial_metric_tag>(target_vars, make_not_null(&buffer)));\n  auto& spacetime_metric =\n      *(get<spacetime_metric_tag>(target_vars, make_not_null(&buffer)));\n  auto& inv_spatial_metric =\n      *(get<inv_spatial_metric_tag>(target_vars, make_not_null(&buffer)));\n  auto& spacetime_normal_vector =\n      *(get<spacetime_normal_vector_tag>(target_vars, make_not_null(&buffer)));\n  auto& inv_spacetime_metric =\n      *(get<inv_spacetime_metric_tag>(target_vars, make_not_null(&buffer)));\n  auto& spatial_christoffel =\n      *(get<spatial_christoffel_tag>(target_vars, make_not_null(&buffer)));\n\n  // Actual computation starts here\n  const auto& src_spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(src_vars);\n  const auto& phi = get<gh::Tags::Phi<DataVector, 3>>(src_vars);\n  spacetime_metric = src_spacetime_metric;\n\n  gr::spatial_metric(make_not_null(&spatial_metric), spacetime_metric);\n  // put determinant of 3-metric temporarily into lapse to save memory.\n  determinant_and_inverse(make_not_null(&lapse),\n                          make_not_null(&inv_spatial_metric), spatial_metric);\n  gr::shift(make_not_null(&shift), spacetime_metric, inv_spatial_metric);\n  gr::lapse(make_not_null(&lapse), shift, spacetime_metric);\n  gr::spacetime_normal_vector(make_not_null(&spacetime_normal_vector), lapse,\n                              shift);\n  gh::spatial_deriv_of_lapse(make_not_null(&deriv_lapse), lapse,\n                             spacetime_normal_vector, phi);\n  gr::inverse_spacetime_metric(make_not_null(&inv_spacetime_metric), lapse,\n                               shift, inv_spatial_metric);\n  gh::spatial_deriv_of_shift(make_not_null(&deriv_shift), lapse,\n                             inv_spacetime_metric, spacetime_normal_vector,\n                             phi);\n  gh::christoffel_second_kind(make_not_null(&spatial_christoffel), phi,\n                              inv_spatial_metric);\n}\n\n/// Dual frame case\ntemplate <typename SrcTagList, typename DestTagList, typename TargetFrame>\nvoid ComputeExcisionBoundaryVolumeQuantities::apply(\n    const gsl::not_null<Variables<DestTagList>*> target_vars,\n    const Variables<SrcTagList>& src_vars, const Mesh<3>& mesh,\n    const Jacobian<DataVector, 3, TargetFrame, Frame::Inertial>&\n        jac_target_to_inertial,\n    const InverseJacobian<DataVector, 3, TargetFrame, Frame::Inertial>&\n        invjac_target_to_inertial,\n    const Jacobian<DataVector, 3, Frame::ElementLogical, TargetFrame>&\n    /*jac_logical_to_target*/,\n    const InverseJacobian<DataVector, 3, Frame::ElementLogical, TargetFrame>&\n        invjac_logical_to_target,\n    const InverseJacobian<DataVector, 3, Frame::Grid, TargetFrame>&\n        invjac_grid_to_target,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_mesh_velocity,\n    const tnsr::I<DataVector, 3, TargetFrame>&\n        grid_to_target_frame_mesh_velocity) {\n  static_assert(\n      std::is_same_v<tmpl::list_difference<SrcTagList, allowed_src_tags>,\n                     tmpl::list<>>,\n      \"Found a src tag that is not allowed\");\n  static_assert(\n      std::is_same_v<tmpl::list_difference<required_src_tags, SrcTagList>,\n                     tmpl::list<>>,\n      \"A required src tag is missing\");\n\n  static_assert(\n      std::is_same_v<\n          tmpl::list_difference<DestTagList, allowed_dest_tags<TargetFrame>>,\n          tmpl::list<>>,\n      \"Found a dest tag that is not allowed\");\n  static_assert(\n      std::is_same_v<\n          tmpl::list_difference<required_dest_tags<TargetFrame>, DestTagList>,\n          tmpl::list<>>,\n      \"A required dest tag is missing\");\n\n  const auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(src_vars);\n\n  if (target_vars->number_of_grid_points() !=\n      src_vars.number_of_grid_points()) {\n    target_vars->initialize(src_vars.number_of_grid_points());\n  }\n\n  using spatial_metric_tag =\n      gr::Tags::SpatialMetric<DataVector, 3, TargetFrame>;\n  using inv_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, 3, TargetFrame>;\n  using lapse_tag = gr::Tags::Lapse<DataVector>;\n  using deriv_lapse_tag =\n      ::Tags::deriv<lapse_tag, tmpl::size_t<3>, TargetFrame>;\n  using shift_tag = gr::Tags::Shift<DataVector, 3, TargetFrame>;\n  // Note that this is NOT the same as the shift quantity\n  using deriv_shift_tag =\n      ::Tags::deriv<shift_tag, tmpl::size_t<3>, TargetFrame>;\n  using inertial_shift_tag = gr::Tags::Shift<DataVector, 3>;\n  using shifty_quantity_tag =\n      gr::Tags::ShiftyQuantity<DataVector, 3, TargetFrame>;\n  using spatial_christoffel_tag =\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, TargetFrame>;\n\n  // Additional temporary tags used for multiple frames\n  using inertial_spatial_metric_tag = gr::Tags::SpatialMetric<DataVector, 3>;\n  using inertial_inv_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, 3>;\n  using deriv_spatial_metric_tag =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3, TargetFrame>,\n                    tmpl::size_t<3>, TargetFrame>;\n  using target_phi_tag = gh::Tags::Phi<DataVector, 3, TargetFrame>;\n\n  // All of the temporary tags, including some that may be repeated\n  // in the target_variables (for now).\n  using full_temp_tags_list =\n      tmpl::list<spatial_metric_tag, inv_spatial_metric_tag, lapse_tag,\n                 deriv_lapse_tag, shift_tag, deriv_shift_tag,\n                 inertial_shift_tag, shifty_quantity_tag,\n                 spatial_christoffel_tag, inertial_spatial_metric_tag,\n                 inertial_inv_spatial_metric_tag, deriv_spatial_metric_tag,\n                 target_phi_tag>;\n\n  // temp tags without variables that are already in DestTagList.\n  using temp_tags_list =\n      tmpl::list_difference<full_temp_tags_list, DestTagList>;\n  TempBuffer<temp_tags_list> buffer(get<0, 0>(spacetime_metric).size());\n\n  // These may or may not be temporaries, depending on if they are asked for\n  // in target_vars.\n  auto& lapse = *(get<lapse_tag>(target_vars, make_not_null(&buffer)));\n  auto& deriv_lapse =\n      *(get<deriv_lapse_tag>(target_vars, make_not_null(&buffer)));\n  auto& shift = *(get<shift_tag>(target_vars, make_not_null(&buffer)));\n  auto& deriv_shift =\n      *(get<deriv_shift_tag>(target_vars, make_not_null(&buffer)));\n  auto& inertial_shift =\n      *(get<inertial_shift_tag>(target_vars, make_not_null(&buffer)));\n  auto& shifty_quantity =\n      *(get<shifty_quantity_tag>(target_vars, make_not_null(&buffer)));\n  auto& spatial_christoffel =\n      *(get<spatial_christoffel_tag>(target_vars, make_not_null(&buffer)));\n  auto& inertial_spatial_metric =\n      *(get<inertial_spatial_metric_tag>(target_vars, make_not_null(&buffer)));\n  auto& inertial_inv_spatial_metric = *(get<inertial_inv_spatial_metric_tag>(\n      target_vars, make_not_null(&buffer)));\n  auto& spatial_metric =\n      *(get<spatial_metric_tag>(target_vars, make_not_null(&buffer)));\n  auto& inv_spatial_metric =\n      *(get<inv_spatial_metric_tag>(target_vars, make_not_null(&buffer)));\n  auto& deriv_spatial_metric =\n      *(get<deriv_spatial_metric_tag>(target_vars, make_not_null(&buffer)));\n  auto& target_phi =\n      *(get<target_phi_tag>(target_vars, make_not_null(&buffer)));\n\n  // Actual computation starts here\n\n  using invjac_grid_to_target_tag =\n      domain::Tags::InverseJacobian<3, Frame::Grid, TargetFrame>;\n  if constexpr (tmpl::list_contains_v<DestTagList, invjac_grid_to_target_tag>) {\n    get<invjac_grid_to_target_tag>(*target_vars) = invjac_grid_to_target;\n  }\n\n  gr::spatial_metric(make_not_null(&inertial_spatial_metric), spacetime_metric);\n  // put determinant of 3-metric temporarily into lapse to save memory.\n  determinant_and_inverse(make_not_null(&lapse),\n                          make_not_null(&inertial_inv_spatial_metric),\n                          inertial_spatial_metric);\n\n  // Transform spatial metric\n  transform::to_different_frame(make_not_null(&spatial_metric),\n                                inertial_spatial_metric,\n                                jac_target_to_inertial);\n\n  // Invert transformed 3-metric.\n  // put determinant of 3-metric temporarily into lapse to save memory.\n  determinant_and_inverse(make_not_null(&lapse),\n                          make_not_null(&inv_spatial_metric), spatial_metric);\n\n  // Only the inertial shift is computed at this time, so there is no\n  // transformation done.\n  gr::shift(make_not_null(&inertial_shift), spacetime_metric,\n            inertial_inv_spatial_metric);\n  // We assume the lapse does note transform between the target and\n  // inertial frames.\n  gr::lapse(make_not_null(&lapse), inertial_shift, spacetime_metric);\n  partial_derivative(make_not_null(&deriv_lapse), lapse, mesh,\n                     invjac_logical_to_target);\n\n  // Transform the shift\n  const size_t VolumeDim = 3;\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    shift.get(i) = invjac_target_to_inertial.get(i, 0) *\n                   (inertial_shift.get(0) + inertial_mesh_velocity.get(0));\n    for (size_t j = 1; j < VolumeDim; ++j) {\n      shift.get(i) += invjac_target_to_inertial.get(i, j) *\n                      (inertial_shift.get(j) + inertial_mesh_velocity.get(j));\n    }\n  }\n  // Again, this is NOT the same as the shifty quantity\n  partial_derivative(make_not_null(&deriv_shift), shift, mesh,\n                     invjac_logical_to_target);\n\n  tenex::evaluate<ti::I>(\n      make_not_null(&shifty_quantity),\n      shift(ti::I) + grid_to_target_frame_mesh_velocity(ti::I));\n\n  partial_derivative(make_not_null(&deriv_spatial_metric), spatial_metric, mesh,\n                     invjac_logical_to_target);\n  gh::phi(make_not_null(&target_phi), lapse, deriv_lapse, shift, deriv_shift,\n          spatial_metric, deriv_spatial_metric);\n  gh::christoffel_second_kind(make_not_null(&spatial_christoffel), target_phi,\n                              inv_spatial_metric);\n}\n\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  GetComputeItemsOnSource.hpp\n  InterpolateWithoutInterpComponent.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Events/GetComputeItemsOnSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n\n/// \\cond\nnamespace intrp::Events::detail {\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(compute_items_on_source)\n}  // namespace intrp::Events::detail\n/// \\endcond\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <set>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/BlockId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InterpolationTargetVarsFromElement.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/GetComputeItemsOnSource.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/PointInfoTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetStaticMemberVariableOrDefault.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace Events::Tags {\ntemplate <size_t Dim>\nstruct ObserverMesh;\ntemplate <size_t Dim, typename Fr>\nstruct ObserverCoordinates;\n}  // namespace Events::Tags\nnamespace Tags {\ntemplate <typename TagsList>\nstruct Variables;\n}  // namespace Tags\ntemplate <size_t VolumeDim>\nclass ElementId;\nnamespace intrp {\ntemplate <typename Metavariables, typename Tag>\nstruct InterpolationTarget;\n}  // namespace intrp\n/// \\endcond\n\nnamespace intrp::Events {\n/// \\cond\ntemplate <size_t VolumeDim, typename InterpolationTargetTag,\n          typename SourceVarTags>\nclass InterpolateWithoutInterpComponent;\n/// \\endcond\n\n/*!\n * \\brief Does an interpolation onto an InterpolationTargetTag by calling\n * Actions on the InterpolationTarget component.\n *\n * \\note The `intrp::TargetPoints::Sphere` target is handled specially because\n * it has the potential to be very slow due to it usually having the most points\n * out of all the stationary targets. An optimization for the future would be to\n * have each target be responsible for intelligently computing the\n * `block_logical_coordinates` for it's own points.\n */\ntemplate <size_t VolumeDim, typename InterpolationTargetTag,\n          typename... SourceVarTags>\nclass InterpolateWithoutInterpComponent<VolumeDim, InterpolationTargetTag,\n                                        tmpl::list<SourceVarTags...>>\n    : public Event {\n private:\n  using frame = typename InterpolationTargetTag::compute_target_points::frame;\n\n public:\n  /// \\cond\n  explicit InterpolateWithoutInterpComponent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(InterpolateWithoutInterpComponent);  // NOLINT\n  /// \\endcond\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help =\n      \"Does interpolation using the given InterpolationTargetTag, \"\n      \"without an Interpolator ParallelComponent.\";\n\n  static std::string name() {\n    return pretty_type::name<InterpolationTargetTag>();\n  }\n\n  InterpolateWithoutInterpComponent() = default;\n\n  using compute_tags_for_observation_box =\n      detail::get_compute_items_on_source_or_default_t<InterpolationTargetTag,\n                                                       tmpl::list<>>;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<\n      typename InterpolationTargetTag::temporal_id,\n      Tags::PointInfo<InterpolationTargetTag, tmpl::size_t<VolumeDim>>,\n      ::Events::Tags::ObserverMesh<VolumeDim>,\n      // We always grab the DG coords because we use them to create a\n      // bounding box for the sphere target optimization. DG coords\n      // have points on the boundary, while FD coords don't. If we\n      // had used FD coords, it's possible a target point would fall\n      // between the outermost gridpoints of two elements. This point\n      // would be outside our bounding box, and thus wouldn't get\n      // interpolated to. We avoid this by always using DG coords,\n      // even if the mesh is FD.\n      domain::Tags::Coordinates<VolumeDim, frame>, SourceVarTags...>;\n\n  template <typename ParallelComponent, typename Metavariables>\n  void operator()(\n      const typename InterpolationTargetTag::temporal_id::type& temporal_id,\n      const tnsr::I<DataVector, VolumeDim, frame>& all_target_points,\n      const Mesh<VolumeDim>& mesh,\n      const tnsr::I<DataVector, VolumeDim, frame> coordinates,\n      const typename SourceVarTags::type&... source_vars_input,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<VolumeDim>& array_index,\n      const ParallelComponent* const /*meta*/,\n      const ObservationValue& /*observation_value*/) const {\n    std::vector<BlockLogicalCoords<VolumeDim>> block_logical_coords{};\n\n    std::stringstream ss{};\n    ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)\n       << std::scientific;\n    const ::Verbosity verbosity = Parallel::get<intrp::Tags::Verbosity>(cache);\n    const bool debug_print = verbosity >= ::Verbosity::Debug;\n    if (debug_print) {\n      ss << InterpolationTarget_detail::target_output_prefix<\n                InterpolateWithoutInterpComponent, InterpolationTargetTag>(\n                temporal_id)\n         << \", \" << array_index << \", \";\n    }\n\n    // The sphere target is special because we have a better idea of where the\n    // points will be.\n    if constexpr (tt::is_a_v<\n                      TargetPoints::Sphere,\n                      typename InterpolationTargetTag::compute_target_points>) {\n      static_assert(VolumeDim == 3,\n                    \"Sphere target can only be used for VolumeDim = 3.\");\n      // Extremum for x, y, z, r\n      std::array<std::pair<double, double>, 4> min_max_coordinates{};\n\n      const auto& sphere =\n          Parallel::get<Tags::Sphere<InterpolationTargetTag>>(cache);\n      const std::array<double, 3>& center = sphere.center;\n\n      // Calculate r^2 from center of sphere because sqrt is expensive\n      DataVector radii_squared{get<0>(coordinates).size(), 0.0};\n      for (size_t i = 0; i < VolumeDim; i++) {\n        radii_squared += square(coordinates.get(i) - gsl::at(center, i));\n      }\n\n      // Compute min and max\n      {\n        const auto [min, max] = alg::minmax_element(radii_squared);\n        min_max_coordinates[3].first = *min;\n        min_max_coordinates[3].second = *max;\n      }\n\n      const std::set<double>& radii_of_sphere_target = sphere.radii;\n      const size_t l_max = sphere.l_max;\n\n      const size_t number_of_angular_points = (l_max + 1) * (2 * l_max + 1);\n      // first size_t = position of first radius in bounds\n      // second size_t = total bounds to use/check\n      std::optional<std::pair<size_t, size_t>> offset_and_num_points{};\n\n      // Have a very small buffer just in case of roundoff\n      double epsilon =\n          (min_max_coordinates[3].second - min_max_coordinates[3].first) *\n          std::numeric_limits<double>::epsilon() * 100.0;\n      size_t offset_index = 0;\n      // Check if any radii of the target are within the radii of our element\n      for (double radius : radii_of_sphere_target) {\n        const double square_radius = square(radius);\n        if (square_radius >=\n                (gsl::at(min_max_coordinates, 3).first - epsilon) and\n            square_radius <=\n                (gsl::at(min_max_coordinates, 3).second + epsilon)) {\n          if (offset_and_num_points.has_value()) {\n            offset_and_num_points->second += number_of_angular_points;\n          } else {\n            offset_and_num_points =\n                std::make_pair(offset_index * number_of_angular_points,\n                               number_of_angular_points);\n          }\n        }\n        offset_index++;\n      }\n\n      // If no radii pass through this element, there's nothing to do so return\n      if (not offset_and_num_points.has_value()) {\n        if (debug_print) {\n          using ::operator<<;\n          ss << \"No radii in this element.\";\n          Parallel::printf(\"%s\\n\", ss.str());\n        }\n        return;\n      }\n\n      // Get the x,y,z bounds\n      for (size_t i = 0; i < VolumeDim; i++) {\n        const auto [min, max] = alg::minmax_element(coordinates.get(i));\n        gsl::at(min_max_coordinates, i).first = *min;\n        gsl::at(min_max_coordinates, i).second = *max;\n      }\n\n      const tnsr::I<DataVector, VolumeDim, frame> target_points_to_check{};\n      // Use the offset and number of points to create a view. We assume that if\n      // there are multiple radii in this element, that they are successive\n      // radii. If this wasn't true, that'd be a really weird topology.\n      for (size_t i = 0; i < VolumeDim; i++) {\n        make_const_view(make_not_null(&target_points_to_check.get(i)),\n                        all_target_points.get(i), offset_and_num_points->first,\n                        offset_and_num_points->second);\n      }\n\n      // To break out of inner loop and skip the point\n      bool skip_point = false;\n      tnsr::I<double, VolumeDim, frame> sphere_coords_to_map{};\n      // Other code below expects block_logical_coords to be sized to the total\n      // number of points on the target (including all radii). By default these\n      // will all be nullopt and we'll only fill the ones we need\n      block_logical_coords.resize(get<0>(all_target_points).size());\n\n      const Block<VolumeDim>& block =\n          Parallel::get<domain::Tags::Domain<VolumeDim>>(cache)\n              .blocks()[array_index.block_id()];\n\n      // Now for every radius in this element, we check if their points are\n      // within the x,y,z bounds of the element. If they are, map the point to\n      // the block logical frame and add it to the vector f all block logical\n      // coordinates.\n      for (size_t index = 0; index < get<0>(target_points_to_check).size();\n           index++) {\n        skip_point = false;\n        for (size_t i = 0; i < VolumeDim; i++) {\n          const double coord = target_points_to_check.get(i)[index];\n          epsilon = (gsl::at(min_max_coordinates, i).second -\n                     gsl::at(min_max_coordinates, i).first) *\n                    std::numeric_limits<double>::epsilon() * 100.0;\n          // If a point is outside any of the bounding box, skip it\n          if (coord < (gsl::at(min_max_coordinates, i).first - epsilon) or\n              coord > (gsl::at(min_max_coordinates, i).second + epsilon)) {\n            skip_point = true;\n            break;\n          }\n\n          sphere_coords_to_map.get(i) = coord;\n        }\n\n        if (skip_point) {\n          continue;\n        }\n\n        std::optional<tnsr::I<double, VolumeDim, ::Frame::BlockLogical>>\n            block_coords_of_target_point{};\n\n        if constexpr (Parallel::is_in_global_cache<\n                          Metavariables, domain::Tags::FunctionsOfTime>) {\n          const auto& functions_of_time =\n              Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n          const double time =\n              InterpolationTarget_detail::get_temporal_id_value(temporal_id);\n\n          block_coords_of_target_point = block_logical_coordinates_single_point(\n              sphere_coords_to_map, block, time, functions_of_time);\n        } else {\n          block_coords_of_target_point = block_logical_coordinates_single_point(\n              sphere_coords_to_map, block);\n        }\n\n        if (block_coords_of_target_point.has_value()) {\n          // Get index into vector of all grid points of the target. This is\n          // just the offset + index\n          block_logical_coords[offset_and_num_points->first + index] =\n              make_id_pair(domain::BlockId(array_index.block_id()),\n                           std::move(block_coords_of_target_point.value()));\n        }\n      }\n    } else {\n      (void)coordinates;\n      block_logical_coords = InterpolationTarget_detail::block_logical_coords<\n          InterpolationTargetTag>(cache, all_target_points, temporal_id);\n    }\n\n    const std::vector<ElementId<VolumeDim>> element_ids{{array_index}};\n    const auto element_coord_holders =\n        element_logical_coordinates(element_ids, block_logical_coords);\n\n    if (element_coord_holders.count(array_index) == 0) {\n      if (debug_print) {\n        ss << \"No target points in this element. Skipping.\";\n        Parallel::printf(\"%s\\n\", ss.str());\n      }\n      // There are no target points in this element, so we don't need\n      // to do anything.\n      return;\n    }\n\n    // There are points in this element, so interpolate to them and\n    // send the interpolated data to the target.  This is done\n    // in several steps:\n    const auto& element_coord_holder = element_coord_holders.at(array_index);\n\n    // 1. Get the list of variables\n    Variables<typename InterpolationTargetTag::vars_to_interpolate_to_target>\n        interp_vars(mesh.number_of_grid_points());\n\n    if constexpr (InterpolationTarget_detail::has_compute_vars_to_interpolate_v<\n                      InterpolationTargetTag>) {\n      // 1a. Call compute_vars_to_interpolate.  Need the source in a\n      // Variables, so copy the variables here.\n      // This copy would be unnecessary if we passed a Variables into\n      // InterpolateWithoutInterpComponent instead of passing\n      // individual Tensors, which would require that this Variables is\n      // something in the DataBox. (Note that\n      // InterpolationTarget_detail::compute_dest_vars_from_source_vars\n      // allows the source variables to be different from the\n      // destination variables).\n      Variables<tmpl::list<SourceVarTags...>> source_vars(\n          mesh.number_of_grid_points());\n      [[maybe_unused]] const auto copy_to_variables =\n          [&source_vars](const auto source_var_tag_v, const auto& source_var) {\n            using source_var_tag = tmpl::type_from<decltype(source_var_tag_v)>;\n            get<source_var_tag>(source_vars) = source_var;\n            return 0;\n          };\n      expand_pack(copy_to_variables(tmpl::type_<SourceVarTags>{},\n                                    source_vars_input)...);\n\n      InterpolationTarget_detail::compute_dest_vars_from_source_vars<\n          InterpolationTargetTag>(make_not_null(&interp_vars), source_vars,\n                                  get<domain::Tags::Domain<VolumeDim>>(cache),\n                                  mesh, array_index, cache, temporal_id);\n    } else {\n      // 1b. There is no compute_vars_to_interpolate. So copy the\n      // source vars directly into the variables.\n      // This copy would be unnecessary if:\n      //   - We passed a Variables into InterpolateWithoutInterpComponent\n      //     instead of passing individual Tensors.\n      //  and\n      //   - This Variables was actually something in the DataBox.\n      //  and\n      //   - Either the passed-in Variables was exactly the same as\n      //     InterpolationTargetTag::vars_to_interpolate_to_target,\n      //     or IrregularInterpolant::interpolate had the ability to\n      //     interpolate only a subset of the Variables passed into it,\n      //     or IrregularInterpolant::interpolate can interpolate individual\n      //     DataVectors.\n      [[maybe_unused]] const auto copy_to_variables =\n          [&interp_vars](const auto tensor_tag_v, const auto& tensor) {\n            using tensor_tag = tmpl::type_from<decltype(tensor_tag_v)>;\n            get<tensor_tag>(interp_vars) = tensor;\n            return 0;\n          };\n      expand_pack(copy_to_variables(tmpl::type_<SourceVarTags>{},\n                                    source_vars_input)...);\n    }\n\n    // 2. Set up interpolator\n    intrp::Irregular<VolumeDim> interpolator(\n        mesh, element_coord_holder.element_logical_coords);\n\n    // 3. Interpolate and send interpolated data to target\n    auto& receiver_proxy = Parallel::get_parallel_component<\n        InterpolationTarget<Metavariables, InterpolationTargetTag>>(cache);\n    Parallel::simple_action<\n        Actions::InterpolationTargetVarsFromElement<InterpolationTargetTag>>(\n        receiver_proxy,\n        std::vector<Variables<\n            typename InterpolationTargetTag::vars_to_interpolate_to_target>>(\n            {interpolator.interpolate(interp_vars)}),\n        block_logical_coords,\n        std::vector<std::vector<size_t>>({element_coord_holder.offsets}),\n        temporal_id);\n\n    if (debug_print) {\n      ss << \"Sending points and vars to target.\";\n      Parallel::printf(\"%s\\n\", ss.str());\n    }\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename ArrayIndex, typename Component, typename Metavariables>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n};\n\n/// \\cond\ntemplate <size_t VolumeDim, typename InterpolationTargetTag,\n          typename... SourceVarTags>\nPUP::able::PUP_ID\n    InterpolateWithoutInterpComponent<VolumeDim, InterpolationTargetTag,\n                                      tmpl::list<SourceVarTags...>>::my_PUP_ID =\n        0;  // NOLINT\n/// \\endcond\n\n}  // namespace intrp::Events\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/InterpolationTarget.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InterpolationTargetSendPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\nnamespace intrp::Actions {\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct InitializeInterpolationTarget;\n}  // namespace intrp::Actions\n/// \\endcond\n\nnamespace intrp {\n\n/// \\brief ParallelComponent representing a set of points to be interpolated\n/// to and a function to call upon interpolation to those points.\n///\n/// `InterpolationTargetTag` must conform to the\n/// intrp::protocols::InterpolationTargetTag protocol.\n///\n/// The metavariables must contain the following type alias:\n/// - interpolation_target_tags:\n///      A `tmpl::list` of all `InterpolationTargetTag`s.\n///\n/// `Metavariables` must contain the following static constexpr members:\n/// - size_t volume_dim:\n///      The dimension of the Domain.\ntemplate <class Metavariables, typename InterpolationTargetTag>\nstruct InterpolationTarget {\n  using interpolation_target_tag = InterpolationTargetTag;\n  static_assert(\n      tt::assert_conforms_to_v<interpolation_target_tag,\n                               intrp::protocols::InterpolationTargetTag>);\n  static std::string name() {\n    return pretty_type::name<InterpolationTargetTag>();\n  }\n  using chare_type = ::Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n          tmpl::flatten<tmpl::list<\n              typename InterpolationTargetTag::compute_target_points,\n              typename InterpolationTargetTag::post_interpolation_callbacks>>>;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<intrp::Actions::InitializeInterpolationTarget<\n                         Metavariables, InterpolationTargetTag>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<Actions::InterpolationTargetSendTimeIndepPointsToElements<\n                         InterpolationTargetTag>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Restart,\n          tmpl::list<\n              tmpl::conditional_t<\n                  InterpolationTargetTag::compute_target_points::is_sequential::\n                      value,\n                  tmpl::list<>,\n                  tmpl::list<\n                      Actions::InterpolationTargetSendTimeIndepPointsToElements<\n                          InterpolationTargetTag>>>,\n              Parallel::Actions::TerminatePhase>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<\n        InterpolationTarget<metavariables, InterpolationTargetTag>>(local_cache)\n        .start_phase(next_phase);\n  };\n};\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <ios>\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/TagsMetafunctions.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/CreateHasStaticMemberVariable.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\n\nnamespace intrp {\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nclass InterpolationTarget;\nnamespace Tags {\ntemplate <typename TemporalId>\nstruct IndicesOfFilledInterpPoints;\ntemplate <typename TemporalId>\nstruct IndicesOfInvalidInterpPoints;\ntemplate <typename TemporalId>\nstruct CompletedTemporalIds;\ntemplate <typename TemporalId>\nstruct TemporalIds;\n}  // namespace Tags\nnamespace TargetPoints {\ntemplate <typename InterpolationTargetTag, typename Frame>\nstruct Sphere;\n}  // namespace TargetPoints\n}  // namespace intrp\ntemplate <typename Id>\nstruct LinkedMessageId;\nclass TimeStepId;\ntemplate <typename TagsList>\nstruct Variables;\n/// \\endcond\n\nnamespace intrp::InterpolationTarget_detail {\ntemplate <typename InterpolationTarget>\nstruct get_interpolation_target_tag {\n  using type = typename InterpolationTarget::interpolation_target_tag;\n};\n\ntemplate <typename InterpolationTargetTag>\nstruct is_sequential\n    : InterpolationTargetTag::compute_target_points::is_sequential {};\n\ntemplate <typename InterpolationTargetTag>\nstruct is_non_sequential\n    : std::bool_constant<not InterpolationTargetTag::compute_target_points::\n                             is_sequential::value> {};\n\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(component_being_mocked)\n\ntemplate <typename Metavariables>\nusing get_sequential_target_tags = tmpl::filter<\n    tmpl::transform<\n        tmpl::filter<tmpl::transform<typename Metavariables::component_list,\n                                     get_component_being_mocked_or_default<\n                                         tmpl::_1, tmpl::_1>>,\n                     tt::is_a<intrp::InterpolationTarget, tmpl::_1>>,\n        get_interpolation_target_tag<tmpl::_1>>,\n    is_sequential<tmpl::_1>>;\n\ntemplate <typename AllInterpolationTargetTags>\nusing get_non_sequential_target_tags =\n    tmpl::filter<AllInterpolationTargetTags, is_non_sequential<tmpl::_1>>;\n\nCREATE_IS_CALLABLE(substep_time)\nCREATE_IS_CALLABLE_V(substep_time)\ntemplate <typename T>\ndouble get_temporal_id_value(const T& id) {\n  if constexpr (std::is_same_v<T, LinkedMessageId<double>>) {\n    return id.id;\n  } else if constexpr (is_substep_time_callable_v<T>) {\n    return id.substep_time();\n  } else {\n    static_assert(std::is_same_v<T, double>,\n                  \"get_temporal_id_value only supports 'double', \"\n                  \"'LinkedMessageId<double>', or 'TimeStepId'.\");\n    return id;\n  }\n}\n\ntemplate <typename Action, typename InterpolationTargetTag, typename TemporalId>\nstd::string target_output_prefix(const TemporalId& temporal_id) {\n  std::stringstream ss{};\n  ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)\n     << std::scientific;\n  ss << pretty_type::name<Action>() << \", \"\n     << pretty_type::name<InterpolationTargetTag>() << \", \" << temporal_id\n     << \", WC \" << sys::wall_time();\n\n  return ss.str();\n}\n\ntemplate <typename Action, typename InterpolationTargetTag>\nstd::string target_output_prefix() {\n  std::stringstream ss{};\n  ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)\n     << std::scientific;\n  ss << pretty_type::name<Action>() << \", \"\n     << pretty_type::name<InterpolationTargetTag>() << \", WC \"\n     << sys::wall_time();\n\n  return ss.str();\n}\n\nCREATE_IS_CALLABLE(apply)\n\ntemplate <typename T, typename DbTags, typename Metavariables,\n          typename TemporalId>\nbool apply_callbacks(\n    const gsl::not_null<db::DataBox<DbTags>*> box,\n    const gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,\n    const TemporalId& temporal_id) {\n  using callbacks = typename T::post_interpolation_callbacks;\n  constexpr bool all_simple_callbacks =\n      tmpl::all<callbacks,\n                is_apply_callable<tmpl::_1, tmpl::pin<decltype(*box)>,\n                                  tmpl::pin<decltype(*cache)>,\n                                  tmpl::pin<decltype(temporal_id)>>>::value;\n  if constexpr (all_simple_callbacks) {\n    tmpl::for_each<callbacks>([&](const auto callback_v) {\n      using callback = tmpl::type_from<decltype(callback_v)>;\n      callback::apply(*box, *cache, temporal_id);\n    });\n\n    // For the simpler callback function, we will always clean up volume data,\n    // so we return true here.\n    return true;\n  } else {\n    static_assert(tmpl::size<callbacks>::value == 1,\n                  \"Can only have 1 post_interpolation_callbacks that mutates \"\n                  \"the target box.\");\n    return tmpl::front<callbacks>::apply(box, cache, temporal_id);\n  }\n}\n\nCREATE_HAS_STATIC_MEMBER_VARIABLE(fill_invalid_points_with)\nCREATE_HAS_STATIC_MEMBER_VARIABLE_V(fill_invalid_points_with)\n\ntemplate <typename T, bool B>\nstruct fill_value_or_no_such_type;\n\ntemplate <typename T>\nstruct fill_value_or_no_such_type<T, false> {\n  using type = NoSuchType;\n};\n\ntemplate <typename T>\nstruct DoubleWrapper {\n  static constexpr double value = T::fill_invalid_points_with;\n};\n\ntemplate <typename T>\nstruct fill_value_or_no_such_type<T, true> {\n  using type = DoubleWrapper<T>;\n};\n\ntemplate <typename Callback>\nstruct get_fill_value {\n  using type = typename fill_value_or_no_such_type<\n      Callback, has_fill_invalid_points_with_v<Callback>>::type;\n};\n\ntemplate <typename Callbacks>\nstruct get_invalid_fill {\n  using type =\n      tmpl::filter<tmpl::transform<Callbacks, get_fill_value<tmpl::_1>>,\n                   tt::is_a<DoubleWrapper, tmpl::_1>>;\n};\n\ntemplate <typename InterpolationTargetTag, typename TemporalId, typename DbTags>\nvoid fill_invalid_points(const gsl::not_null<db::DataBox<DbTags>*> box,\n                         const TemporalId& temporal_id) {\n  using callbacks =\n      typename InterpolationTargetTag::post_interpolation_callbacks;\n  using invalid_fill = typename get_invalid_fill<callbacks>::type;\n  if constexpr (tmpl::size<invalid_fill>::value > 0) {\n    const auto& invalid_indices =\n        db::get<Tags::IndicesOfInvalidInterpPoints<TemporalId>>(*box);\n    if (invalid_indices.find(temporal_id) != invalid_indices.end() and\n        not invalid_indices.at(temporal_id).empty()) {\n      db::mutate<Tags::IndicesOfInvalidInterpPoints<TemporalId>,\n                 Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(\n          [&temporal_id](\n              const gsl::not_null<\n                  std::unordered_map<TemporalId, std::unordered_set<size_t>>*>\n                  indices_of_invalid_points,\n              const gsl::not_null<std::unordered_map<\n                  TemporalId, Variables<typename InterpolationTargetTag::\n                                            vars_to_interpolate_to_target>>*>\n                  vars_dest_all_times) {\n            auto& vars_dest = vars_dest_all_times->at(temporal_id);\n            const size_t npts_dest = vars_dest.number_of_grid_points();\n            const size_t nvars = vars_dest.number_of_independent_components;\n            for (auto index : indices_of_invalid_points->at(temporal_id)) {\n              for (size_t v = 0; v < nvars; ++v) {\n                // clang-tidy: no pointer arithmetic\n                vars_dest.data()[index + v * npts_dest] =  // NOLINT\n                    tmpl::front<invalid_fill>::value;\n              }\n            }\n            // Further functions may test if there are invalid points.\n            // Clear the invalid points now, since we have filled them.\n            indices_of_invalid_points->erase(temporal_id);\n          },\n          box);\n    }\n  } else {\n    (void)box;\n    (void)temporal_id;\n  }\n}\n\n/// Wraps calling the callback function on an InterpolationTarget.\n///\n/// First prepares for the callback, then calls the callback,\n/// and returns true if the callback is done and\n/// false if the callback is not done (e.g. if the callback is an\n/// apparent horizon search and it needs another iteration).\n///\n/// call_callbacks is called by an Action of InterpolationTarget.\n///\n/// Currently one Actions calls call_callbacks:\n/// - InterpolationTargetVarsFromElement (called by DgElementArray)\ntemplate <typename InterpolationTargetTag, typename DbTags,\n          typename Metavariables, typename TemporalId>\nbool call_callbacks(\n    const gsl::not_null<db::DataBox<DbTags>*> box,\n    const gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,\n    const TemporalId& temporal_id) {\n  // Before doing anything else, deal with the possibility that some\n  // of the points might be outside of the Domain.\n  fill_invalid_points<InterpolationTargetTag>(box, temporal_id);\n\n  // Fill ::Tags::Variables<typename\n  //      InterpolationTargetTag::vars_to_interpolate_to_target>\n  // with variables from correct temporal_id.\n  db::mutate_apply<\n      tmpl::list<::Tags::Variables<\n          typename InterpolationTargetTag::vars_to_interpolate_to_target>>,\n      tmpl::list<Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>>(\n      [&temporal_id](\n          const gsl::not_null<Variables<\n              typename InterpolationTargetTag::vars_to_interpolate_to_target>*>\n              vars,\n          const std::unordered_map<\n              TemporalId, Variables<typename InterpolationTargetTag::\n                                        vars_to_interpolate_to_target>>&\n              vars_at_all_times) { *vars = vars_at_all_times.at(temporal_id); },\n      box);\n\n  // apply_callbacks should return true if we are done with this\n  // temporal_id.  It should return false only if the callback\n  // calls another `intrp::Action` that needs the volume data at this\n  // same temporal_id.\n  return apply_callbacks<InterpolationTargetTag>(box, cache, temporal_id);\n}\n\n/// Frees InterpolationTarget's memory associated with the supplied\n/// temporal_id.\n///\n/// clean_up_interpolation_target is called by an Action of InterpolationTarget.\n///\n/// Currently one Action calls clean_up_interpolation_target:\n/// - InterpolationTargetVarsFromElement (called by DgElementArray)\ntemplate <typename InterpolationTargetTag, typename DbTags, typename TemporalId>\nvoid clean_up_interpolation_target(\n    const gsl::not_null<db::DataBox<DbTags>*> box,\n    const TemporalId& temporal_id) {\n  // We are now done with this temporal_id, so we can remove it and\n  // clean up data associated with it.\n  db::mutate<Tags::TemporalIds<TemporalId>>(\n      [&temporal_id](const gsl::not_null<std::unordered_set<TemporalId>*> ids) {\n        ASSERT(\n            ids->contains(temporal_id),\n            \"Temporal id \" << temporal_id << \" does not exist in ids \" << *ids);\n        ids->erase(temporal_id);\n      },\n      box);\n  db::mutate<Tags::CompletedTemporalIds<TemporalId>,\n             Tags::IndicesOfFilledInterpPoints<TemporalId>,\n             Tags::IndicesOfInvalidInterpPoints<TemporalId>,\n             Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(\n      [&temporal_id](\n          const gsl::not_null<std::deque<TemporalId>*> completed_ids,\n          const gsl::not_null<\n              std::unordered_map<TemporalId, std::unordered_set<size_t>>*>\n              indices_of_filled,\n          const gsl::not_null<\n              std::unordered_map<TemporalId, std::unordered_set<size_t>>*>\n              indices_of_invalid,\n          const gsl::not_null<std::unordered_map<\n              TemporalId, Variables<typename InterpolationTargetTag::\n                                        vars_to_interpolate_to_target>>*>\n              interpolated_vars) {\n        completed_ids->push_back(temporal_id);\n        // We want to keep track of all completed temporal_ids to deal with the\n        // possibility of late data.  We could keep all completed_ids forever,\n        // but we probably don't want it to get too large, so we limit its size.\n        // We assume that asynchronous calls do not span more than 1000\n        // temporal_ids.\n        if (completed_ids->size() > 1000) {\n          completed_ids->pop_front();\n        }\n        indices_of_filled->erase(temporal_id);\n        indices_of_invalid->erase(temporal_id);\n        interpolated_vars->erase(temporal_id);\n      },\n      box);\n}\n\nstruct HaveDataAtAllPoints {};\n/// Returns true if this InterpolationTarget has received data\n/// at all its points.\n///\n/// have_data_at_all_points is called by an Action of InterpolationTarget.\n///\n/// Since this will be called often, output is only printed to stdout if the\n/// verbosity is greater than or equal to `::Verbosity::Debug`.\n///\n/// Currently one Action calls have_data_at_all_points:\n/// - InterpolationTargetVarsFromElement (called by DgElementArray)\ntemplate <typename InterpolationTargetTag, typename DbTags, typename TemporalId>\nbool have_data_at_all_points(\n    const db::DataBox<DbTags>& box, const TemporalId& temporal_id,\n    const ::Verbosity verbosity = ::Verbosity::Silent) {\n  const auto& filled_indices =\n      db::get<Tags::IndicesOfFilledInterpPoints<TemporalId>>(box);\n\n  const size_t filled_size = filled_indices.count(temporal_id) > 0\n                                 ? filled_indices.at(temporal_id).size()\n                                 : 0;\n  const size_t invalid_size = [&box, &temporal_id]() {\n    const auto& invalid_indices =\n        db::get<Tags::IndicesOfInvalidInterpPoints<TemporalId>>(box);\n    if (invalid_indices.count(temporal_id) > 0) {\n      return invalid_indices.at(temporal_id).size();\n    }\n    return 0_st;\n  }();\n  const size_t interp_size =\n      db::get<Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(box)\n          .at(temporal_id)\n          .number_of_grid_points();\n  if (verbosity >= ::Verbosity::Debug) {\n    Parallel::printf(\n        \"%s, Total expected points = %d, valid points received = %d, \"\n        \"invalid points received = %d\\n\",\n        target_output_prefix<HaveDataAtAllPoints, InterpolationTargetTag>(\n            temporal_id),\n        interp_size, filled_size, invalid_size);\n  }\n  return (invalid_size + filled_size == interp_size);\n}\n\n/// Tells an InterpolationTarget that it should interpolate at\n/// the supplied temporal_id.  Changes the InterpolationTarget's DataBox\n/// accordingly.\n///\n/// Returns a bool indicating if the passed in id needs to be set up\n///\n/// flag_temporal_id_for_interpolation is called by an Action\n/// of InterpolationTarget\n///\n/// Currently one Action calls flag_temporal_id_for_interpolation:\n/// - InterpolationTargetVarsFromElement (called by DgElementArray)\ntemplate <typename InterpolationTargetTag, typename DbTags, typename TemporalId>\nbool flag_temporal_id_for_interpolation(\n    const gsl::not_null<db::DataBox<DbTags>*> box,\n    const TemporalId& temporal_id) {\n  // We allow this function to be called multiple times with the same\n  // temporal_ids (e.g. from each element, or from each node of a\n  // NodeGroup ParallelComponent such as Interpolator). If multiple\n  // calls occur, we care only about the first one, and ignore the\n  // others.  The first call will often begin interpolation.  So if\n  // multiple calls occur, it is possible that some of them may arrive\n  // late, even after interpolation has been completed on one or more\n  // of the temporal_ids (and after that id has already been removed\n  // from `ids`).  If this happens, we don't want to add the\n  // temporal_ids again. For that reason we keep track of the\n  // temporal_ids that we have already completed interpolation on.  So\n  // here we do not add any temporal_ids that are already present in\n  // `ids` or `completed_ids`.\n  bool id_has_been_flagged = false;\n\n  db::mutate_apply<tmpl::list<Tags::TemporalIds<TemporalId>>,\n                   tmpl::list<Tags::CompletedTemporalIds<TemporalId>>>(\n      [&temporal_id, &id_has_been_flagged](\n          const gsl::not_null<std::unordered_set<TemporalId>*> ids,\n          const std::deque<TemporalId>& completed_ids) {\n        if (not(alg::found(completed_ids, temporal_id) or\n                ids->contains(temporal_id))) {\n          ids->insert(temporal_id);\n          id_has_been_flagged = true;\n        }\n      },\n      box);\n\n  return id_has_been_flagged;\n}\n\n/// Adds the supplied interpolated variables and offsets to the\n/// InterpolationTarget's internal DataBox.\n///\n/// Note that the template argument to Variables in vars_src is called\n/// InterpolationTargetTag::vars_to_interpolate_to_target.  This is a list\n/// of tags, and is used for both the interpolated variables (in\n/// this function add_received_variables) and for the source variables\n/// (in other functions). The source and interpolated quantities are\n/// the same set of variables (but at different points).\n///\n/// add_received_variables is called by an Action of InterpolationTarget.\n///\n/// Currently one Action calls add_received_variables:\n/// - InterpolationTargetVarsFromElement (called by DgElementArray)\ntemplate <typename InterpolationTargetTag, typename DbTags, typename TemporalId>\nvoid add_received_variables(\n    const gsl::not_null<db::DataBox<DbTags>*> box,\n    const std::vector<Variables<\n        typename InterpolationTargetTag::vars_to_interpolate_to_target>>&\n        vars_src,\n    const std::vector<std::vector<size_t>>& global_offsets,\n    const TemporalId& temporal_id) {\n  db::mutate<Tags::IndicesOfFilledInterpPoints<TemporalId>,\n             Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(\n      [&temporal_id, &vars_src, &global_offsets](\n          const gsl::not_null<\n              std::unordered_map<TemporalId, std::unordered_set<size_t>>*>\n              indices_of_filled,\n          const gsl::not_null<std::unordered_map<\n              TemporalId, Variables<typename InterpolationTargetTag::\n                                        vars_to_interpolate_to_target>>*>\n              vars_dest_all_times) {\n        auto& vars_dest = vars_dest_all_times->at(temporal_id);\n        // Here we assume that vars_dest has been allocated to the correct\n        // size (but could contain garbage, since below we are filling it).\n        const size_t npts_dest = vars_dest.number_of_grid_points();\n        const size_t nvars = vars_dest.number_of_independent_components;\n        for (size_t j = 0; j < global_offsets.size(); ++j) {\n          const size_t npts_src = global_offsets[j].size();\n          for (size_t i = 0; i < npts_src; ++i) {\n            // If a point is on the boundary of two (or more)\n            // elements, it is possible that we have received data\n            // for this point from more than one Interpolator.\n            // This will rarely occur, but it does occur, e.g. when\n            // a point is exactly on some symmetry\n            // boundary (such as the x-y plane) and this symmetry\n            // boundary is exactly the boundary between two\n            // elements.  If this happens, we accept the first\n            // duplicated point, and we ignore subsequent\n            // duplicated points.  The points are easy to keep track\n            // of because global_offsets uniquely identifies them.\n            if ((*indices_of_filled)[temporal_id]\n                    .insert(global_offsets[j][i])\n                    .second) {\n              for (size_t v = 0; v < nvars; ++v) {\n                // clang-tidy: no pointer arithmetic\n                vars_dest.data()[global_offsets[j][i] +    // NOLINT\n                                 v * npts_dest] =          // NOLINT\n                    vars_src[j].data()[i + v * npts_src];  // NOLINT\n              }\n            }\n          }\n        }\n      },\n      box);\n}\n\n/// Computes the block logical coordinates of an InterpolationTarget.\n///\n/// block_logical_coords is called by an Action of InterpolationTarget.\n///\n/// Currently one Event directly calls this version of block_logical_coords:\n/// - InterpolateWithoutInterpComponent\ntemplate <typename InterpolationTargetTag, typename Metavariables,\n          typename TemporalId>\nauto block_logical_coords(\n    const Parallel::GlobalCache<Metavariables>& cache,\n    const tnsr::I<\n        DataVector, Metavariables::volume_dim,\n        typename InterpolationTargetTag::compute_target_points::frame>& coords,\n    const TemporalId& temporal_id) {\n  const auto& domain =\n      get<domain::Tags::Domain<Metavariables::volume_dim>>(cache);\n  if constexpr (std::is_same_v<typename InterpolationTargetTag::\n                                   compute_target_points::frame,\n                               ::Frame::Grid>) {\n    // Frame is grid frame, so don't need any FunctionsOfTime,\n    // whether or not the maps are time_dependent.\n    return ::block_logical_coordinates(domain, coords);\n  }\n\n  if (domain.is_time_dependent()) {\n    if constexpr (Parallel::is_in_global_cache<Metavariables,\n                                               domain::Tags::FunctionsOfTime>) {\n      // Whoever calls block_logical_coords when the maps are\n      // time-dependent is responsible for ensuring\n      // that functions_of_time are up to date at temporal_id.\n      const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n      return ::block_logical_coordinates(\n          domain, coords,\n          InterpolationTarget_detail::get_temporal_id_value(temporal_id),\n          functions_of_time);\n    } else {\n      // We error here because the maps are time-dependent, yet\n      // the cache does not contain FunctionsOfTime.  It would be\n      // nice to make this a compile-time error; however, we want\n      // the code to compile for the completely time-independent\n      // case where there are no FunctionsOfTime in the cache at\n      // all.  Unfortunately, checking whether the maps are\n      // time-dependent is currently not constexpr.\n      ERROR(\n          \"There is a time-dependent CoordinateMap in at least one \"\n          \"of the Blocks, but FunctionsOfTime are not in the \"\n          \"GlobalCache.  If you intend to use a time-dependent \"\n          \"CoordinateMap, please add FunctionsOfTime to the GlobalCache.\");\n    }\n  }\n\n  // Time-independent case.\n  return ::block_logical_coordinates(domain, coords);\n}\n\n/// Version of block_logical_coords that computes the interpolation\n/// points on the fly.  This version is called when the interpolation\n/// points are (or might be) time-dependent in the\n/// InterpolationTargetTag's frame.\n///\n/// This version of block_logical_coordinates currently only used in tests.\ntemplate <typename InterpolationTargetTag, typename DbTags,\n          typename Metavariables, typename TemporalId>\nauto block_logical_coords(db::DataBox<DbTags>& box,\n                          const Parallel::GlobalCache<Metavariables>& cache,\n                          const TemporalId& temporal_id) {\n  return block_logical_coords<InterpolationTargetTag>(\n      cache,\n      InterpolationTargetTag::compute_target_points::points(\n          box, tmpl::type_<Metavariables>{}, temporal_id),\n      temporal_id);\n}\n\n/// Version of block_logical_coords for when the coords are\n/// time-independent.\ntemplate <typename InterpolationTargetTag, typename DbTags,\n          typename Metavariables>\nauto block_logical_coords(const db::DataBox<DbTags>& box,\n                          const tmpl::type_<Metavariables>& meta) {\n  const auto& domain =\n      db::get<domain::Tags::Domain<Metavariables::volume_dim>>(box);\n  return ::block_logical_coordinates(\n      domain, InterpolationTargetTag::compute_target_points::points(box, meta));\n}\n\n/// Initializes InterpolationTarget's variables storage and lists of indices\n/// corresponding to the supplied block logical coordinates and `temporal_id`.\n///\n/// set_up_interpolation is called by an Action of InterpolationTarget.\n///\n/// Currently one Action call set_up_interpolation:\n/// - InterpolationTargetVarsFromElement (called by DgElementArray)\ntemplate <typename InterpolationTargetTag, typename DbTags, size_t VolumeDim,\n          typename TemporalId>\nvoid set_up_interpolation(\n    const gsl::not_null<db::DataBox<DbTags>*> box,\n    const TemporalId& temporal_id,\n    const std::vector<BlockLogicalCoords<VolumeDim>>& block_logical_coords) {\n  db::mutate<Tags::IndicesOfFilledInterpPoints<TemporalId>,\n             Tags::IndicesOfInvalidInterpPoints<TemporalId>,\n             Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(\n      [&block_logical_coords, &temporal_id](\n          const gsl::not_null<\n              std::unordered_map<TemporalId, std::unordered_set<size_t>>*>\n              indices_of_filled,\n          const gsl::not_null<\n              std::unordered_map<TemporalId, std::unordered_set<size_t>>*>\n              indices_of_invalid_points,\n          const gsl::not_null<std::unordered_map<\n              TemporalId, Variables<typename InterpolationTargetTag::\n                                        vars_to_interpolate_to_target>>*>\n              vars_dest_all_times) {\n        // We know that none of these points have been interpolated to,\n        // so clear the list.\n        indices_of_filled->erase(temporal_id);\n\n        // Set the indices of invalid points.\n        indices_of_invalid_points->erase(temporal_id);\n        for (size_t i = 0; i < block_logical_coords.size(); ++i) {\n          // The sphere target is optimized specially. Because of this, a\n          // nullopt in block_logical_coords from the sphere target doesn't\n          // actually mean the point is invalid. Therefore we ignore this check\n          // for the sphere target. The downside of this is that we don't catch\n          // invalid points here.\n          constexpr bool is_sphere = tt::is_a_v<\n              TargetPoints::Sphere,\n              typename InterpolationTargetTag::compute_target_points>;\n          if (not is_sphere and not block_logical_coords[i].has_value()) {\n            (*indices_of_invalid_points)[temporal_id].insert(i);\n          }\n        }\n\n        // At this point we don't know if vars_dest exists in the map;\n        // if it doesn't then we want to default construct it.\n        auto& vars_dest = (*vars_dest_all_times)[temporal_id];\n\n        // We will be filling vars_dest with interpolated data.\n        // Here we make sure it is allocated to the correct size.\n        if (vars_dest.number_of_grid_points() != block_logical_coords.size()) {\n          vars_dest = Variables<\n              typename InterpolationTargetTag::vars_to_interpolate_to_target>(\n              block_logical_coords.size());\n        }\n      },\n      box);\n}\n\nCREATE_HAS_TYPE_ALIAS(compute_vars_to_interpolate)\nCREATE_HAS_TYPE_ALIAS_V(compute_vars_to_interpolate)\n\nnamespace detail {\ntemplate <typename T>\nstruct is_an_invjac_impl : std::false_type {};\n\ntemplate <typename Arg1, size_t Arg2, typename Arg3, typename Arg4>\nstruct is_an_invjac_impl<InverseJacobian<Arg1, Arg2, Arg3, Arg4>>\n    : std::true_type {};\n\ntemplate <typename Tag>\nstruct is_an_invjac : is_an_invjac_impl<typename Tag::type> {};\n\ntemplate <typename Tag, typename Frame>\nusing any_index_in_frame_impl =\n    TensorMetafunctions::any_index_in_frame<typename Tag::type, Frame>;\n}  // namespace detail\n\n/// Returns true if any of the tensors in TagList have any of their\n/// indices in the given frame. We don't count InverseJacobians because these\n/// mess up the frames\ntemplate <typename TagList, typename Frame>\nconstexpr bool any_index_in_frame_v = tmpl::any<\n    tmpl::filter<TagList, tmpl::not_<detail::is_an_invjac<tmpl::_1>>>,\n    tmpl::bind<detail::any_index_in_frame_impl, tmpl::_1, Frame>>::value;\n\n/// Calls compute_vars_to_interpolate to compute\n/// InterpolationTargetTag::vars_to_interpolate_to_target from the source\n/// variables.  Does any frame transformations needed.\ntemplate <typename InterpolationTargetTag, typename SourceTags,\n          typename Metavariables, typename ElementId>\nvoid compute_dest_vars_from_source_vars(\n    const gsl::not_null<Variables<\n        typename InterpolationTargetTag::vars_to_interpolate_to_target>*>\n        dest_vars,\n    const Variables<SourceTags>& source_vars,\n    const Domain<Metavariables::volume_dim>& domain,\n    const Mesh<Metavariables::volume_dim>& mesh, const ElementId& element_id,\n    const Parallel::GlobalCache<Metavariables>& cache,\n    const typename InterpolationTargetTag::temporal_id::type& temporal_id) {\n  const auto& block = domain.blocks().at(element_id.block_id());\n  if (block.is_time_dependent()) {\n    // The functions of time are always guaranteed to be\n    // up-to-date here.\n    // For interpolation without an Interpolator ParallelComponent,\n    // this is because the InterpWithoutInterpComponent event will be called\n    // after the Action that keeps functions of time up to date.\n    if constexpr (any_index_in_frame_v<SourceTags, Frame::Inertial> and\n                  any_index_in_frame_v<typename InterpolationTargetTag::\n                                           vars_to_interpolate_to_target,\n                                       Frame::Grid>) {\n      // Need to do frame transformations to Grid frame.\n      const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n      ElementMap<3, ::Frame::Grid> map_logical_to_grid{\n          element_id, block.moving_mesh_logical_to_grid_map().get_clone()};\n      const auto logical_coords = logical_coordinates(mesh);\n      const auto time =\n          InterpolationTarget_detail::get_temporal_id_value(temporal_id);\n      const auto jac_logical_to_grid =\n          map_logical_to_grid.jacobian(logical_coords);\n      const auto invjac_logical_to_grid =\n          map_logical_to_grid.inv_jacobian(logical_coords);\n      const auto [inertial_coords, invjac_grid_to_inertial,\n                  jac_grid_to_inertial, inertial_mesh_velocity] =\n          block.moving_mesh_grid_to_inertial_map()\n              .coords_frame_velocity_jacobians(\n                  map_logical_to_grid(logical_coords), time, functions_of_time);\n      // Create an identity jacobian since the transformation from grid to grid\n      // is the identity\n      InverseJacobian<DataVector, 3, Frame::Grid, Frame::Grid>\n          invjac_grid_to_grid{\n              DataVector{get<0, 0>(jac_logical_to_grid).size(), 0.0}};\n      get<0, 0>(invjac_grid_to_grid) = 1.0;\n      get<1, 1>(invjac_grid_to_grid) = 1.0;\n      get<2, 2>(invjac_grid_to_grid) = 1.0;\n      InterpolationTargetTag::compute_vars_to_interpolate::apply(\n          dest_vars, source_vars, mesh, jac_grid_to_inertial,\n          invjac_grid_to_inertial, jac_logical_to_grid, invjac_logical_to_grid,\n          invjac_grid_to_grid, inertial_mesh_velocity,\n          tnsr::I<DataVector, 3, Frame::Grid>{\n              DataVector{get<0, 0>(jac_logical_to_grid).size(), 0.0}});\n    } else if constexpr (any_index_in_frame_v<SourceTags, Frame::Inertial> and\n                         any_index_in_frame_v<typename InterpolationTargetTag::\n                                                  vars_to_interpolate_to_target,\n                                              Frame::Distorted>) {\n      // Need to do frame transformations to Distorted frame.\n      const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);\n      ASSERT(block.has_distorted_frame(),\n             \"Cannot interpolate to distorted frame in a block that does not \"\n             \"have a distorted frame\");\n      const domain::CoordinateMaps::Composition\n          element_logical_to_distorted_map{\n              domain::element_to_block_logical_map(element_id),\n              block.moving_mesh_logical_to_grid_map().get_clone(),\n              block.moving_mesh_grid_to_distorted_map().get_clone()};\n      const domain::CoordinateMaps::Composition element_logical_to_grid_map{\n          domain::element_to_block_logical_map(element_id),\n          block.moving_mesh_logical_to_grid_map().get_clone()};\n      const auto logical_coords = logical_coordinates(mesh);\n      const auto time =\n          InterpolationTarget_detail::get_temporal_id_value(temporal_id);\n      const auto [inertial_coords, invjac_distorted_to_inertial,\n                  jac_distorted_to_inertial,\n                  distorted_to_inertial_mesh_velocity] =\n          block.moving_mesh_distorted_to_inertial_map()\n              .coords_frame_velocity_jacobians(\n                  element_logical_to_distorted_map(logical_coords, time,\n                                                   functions_of_time),\n                  time, functions_of_time);\n      const auto [distorted_coords, invjac_grid_to_distorted,\n                  jac_grid_to_distorted, grid_to_distorted_mesh_velocity] =\n          block.moving_mesh_grid_to_distorted_map()\n              .coords_frame_velocity_jacobians(\n                  element_logical_to_grid_map(logical_coords, time,\n                                              functions_of_time),\n                  time, functions_of_time);\n      InterpolationTargetTag::compute_vars_to_interpolate::apply(\n          dest_vars, source_vars, mesh, jac_distorted_to_inertial,\n          invjac_distorted_to_inertial,\n          element_logical_to_distorted_map.jacobian(logical_coords, time,\n                                                    functions_of_time),\n          element_logical_to_distorted_map.inv_jacobian(logical_coords, time,\n                                                        functions_of_time),\n          invjac_grid_to_distorted, distorted_to_inertial_mesh_velocity,\n          grid_to_distorted_mesh_velocity);\n    } else {\n      // No frame transformations needed.\n      InterpolationTargetTag::compute_vars_to_interpolate::apply(\n          dest_vars, source_vars, mesh);\n    }\n  } else {\n    // No frame transformations needed, since the maps are time-independent\n    // and therefore all the frames are the same.\n    //\n    // Sometimes dest_vars and source_vars have different Frame tags\n    // in the time-independent case, even though the frames are really\n    // the same.  The source vars should all be in the Inertial frame,\n    // so we create a new non-owning Variables called\n    // dest_vars_in_inertial_frame that points to dest_vars but is\n    // tagged as the inertial frame, and we pass\n    // dest_vars_in_inertial_frame to\n    // compute_vars_to_interpolate::apply.\n    using dest_vars_tags =\n        typename InterpolationTargetTag::vars_to_interpolate_to_target;\n    using dest_tags_in_inertial_frame =\n        TensorMetafunctions::replace_frame_in_taglist<dest_vars_tags,\n                                                      ::Frame::Inertial>;\n    Variables<dest_tags_in_inertial_frame> dest_vars_in_inertial_frame(\n        dest_vars->data(), dest_vars->size());\n    InterpolationTargetTag::compute_vars_to_interpolate::apply(\n        make_not_null(&dest_vars_in_inertial_frame), source_vars, mesh);\n  }\n}\n}  // namespace intrp::InterpolationTarget_detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Intrp.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Contains classes and functions for interpolation\n */\nnamespace intrp {}\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/IntrpOptionHolders.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace intrp {\n/*!\n * \\ingroup NumericalAlgorithmsGroup\n * \\brief Contains structs defining the options of various InterpolationTargets\n */\nnamespace OptionHolders;\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/PointInfoTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace intrp::Tags {\n/*!\n * \\brief  PointInfo holds the points to be interpolated onto,\n * in whatever frame those points are to be held constant.\n *\n * \\details PointInfo is used only for interpolation points that are\n * time-independent in some frame, so that there is no `Interpolator`\n * ParallelComponent. `VolumeDim` is a typename rather than a `size_t` to better\n * facilitate this tag being used in metafunctions. `VolumeDim` must be a\n * `tmpl::size_t`.\n */\ntemplate <typename InterpolationTargetTag, typename VolumeDim>\nstruct PointInfo : db::SimpleTag {\n  using type =\n      tnsr::I<DataVector, VolumeDim::value,\n              typename InterpolationTargetTag::compute_target_points::frame>;\n};\n}  // namespace intrp::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComputeTargetPoints.hpp\n  ComputeVarsToInterpolate.hpp\n  InterpolationTargetTag.hpp\n  PostInterpolationCallback.hpp\n  ProtocolsNamespace.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nstruct DataVector;\n/// \\endcond\n\nnamespace intrp::protocols {\n/*!\n * \\brief A protocol for the type alias `compute_target_points` found in an\n * InterpolationTargetTag.\n *\n * \\details A struct conforming to the `ComputeTargetPoints` protocol must have\n *\n * - a type alias `is_sequential` that is either `std::true_type` or\n *   `std::false_type` which indicates if interpolations depend on previous\n *   interpolations' results. It is assumed in the interpolation framework that\n *   only horizon finds are sequential (e.y. anything that uses the\n *   `intrp::Interpolator` component is a sequential horizon find).\n *\n * - a type alias `frame` that denotes the frame the target points are computed\n *   in\n *\n * - a function `points` with signature matching the one in the example that\n *   will compute the points in the given `frame` (with or without the `const &`\n *   on the box argument).\n *\n * A struct that conforms to this protocol can optionally have any of these\n * members as well:\n *\n * - a type alias `simple_tags` that list simple tags to be added to the DataBox\n *   of the InterpolationTarget\n *\n * - a type alias `compute_tags` that list compute tags to be added to the\n *   DataBox of the InterpolationTarget\n *\n * - a type alias `const_global_cache_tags` with tags to be put in the\n *   GlobalCache\n *\n * - a function `initialize` with signature matching the one in the example that\n *   is run during the Initialization phase of the InterpolationTarget and can\n *   initialize any of the `simple_tags` added.\n *\n * Here is an example of a class that conforms to this protocols:\n *\n * \\snippet Helpers/ParallelAlgorithms/Interpolation/Examples.hpp ComputeTargetPoints\n */\nstruct ComputeTargetPoints {\n  template <typename ConformingType>\n  struct test {\n    struct DummyMetavariables;\n    struct DummyTag : db::SimpleTag {\n      using type = int;\n    };\n\n    using is_sequential = typename ConformingType::is_sequential;\n    static_assert(std::is_same_v<is_sequential, std::true_type> or\n                  std::is_same_v<is_sequential, std::false_type>);\n\n    using frame = typename ConformingType::frame;\n    // We don't check the initialize() function because it is optional\n  };\n};\n}  // namespace intrp::protocols\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Protocols/ComputeVarsToInterpolate.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct ElementLogical;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\ntemplate <size_t Dim>\nstruct Mesh;\nstruct DataVector;\n/// \\endcond\n\nnamespace intrp::protocols {\n/*!\n * \\brief A protocol for the type alias `compute_vars_to_interpolate` that can\n * potentially be found in an InterpolationTargetTag (potentially because an\n * InterpolationTargetTag does not require this type alias).\n *\n * \\details A struct conforming to the `ComputeVarsToInterpolate` protocol must\n * have\n *\n * - a type alias `allowed_src_tags` which is a `tmpl::list` of tags from the\n *   volume that can be used to compute quantities that will be interpolated\n *   onto the target.\n *\n * - a type alias `required_src_tags` which is a `tmpl::list` of tags from the\n *   volume that are necessary to compute quantities that will be interpolated\n *   onto the target. This list must be a subset of `allowed_src_tags`.\n *\n * - a type alias `allowed_dest_tags<Frame>` which is a `tmpl::list` of tags on\n *   the target that can be computed from the source tags.\n *\n * - a type alias `required_dest_tags<Frame>` which is a `tmpl::list` of tags on\n *   the target that must be computed from the source tags. This list must be a\n *   subset of `allowed_dest_tags<Frame>`\n *\n * - an `apply` function with at least one of the signatures in the example.\n *   This apply function fills the `vars_to_interpolate_to_target` type alias\n *   from the InterpolationTargetTag. Here `Dim` is `Metavariables::volume_dim`,\n *   SrcFrame is typically `Frame::Inertial` and\n *   TargetFrame is the frame of `vars_to_interpolate_to_target` from the\n *   InterpolationTargetTag. The overload without Jacobians treats the case in\n *   which TargetFrame is the same as SrcFrame.\n *\n * Here is an example of a class that conforms to this protocols:\n *\n * \\snippet Helpers/ParallelAlgorithms/Interpolation/Examples.hpp ComputeVarsToInterpolate\n */\nstruct ComputeVarsToInterpolate {\n  template <typename ConformingType>\n  struct test {\n    template <size_t Dim>\n    struct DummyTag : db::SimpleTag {\n      using type = tnsr::a<DataVector, Dim, Frame::Grid>;\n    };\n    template <size_t Dim>\n    using example_list = tmpl::list<DummyTag<Dim>>;\n\n    template <typename T, size_t Dim, typename = std::void_t<>>\n    struct has_signature_1 : std::false_type {};\n\n    template <typename T, size_t Dim>\n    struct has_signature_1<\n        T, Dim,\n        std::void_t<decltype(T::apply(\n            std::declval<const gsl::not_null<Variables<example_list<Dim>>*>>(),\n            std::declval<const Variables<example_list<Dim>>&>(),\n            std::declval<const Mesh<Dim>&>()))>> : std::true_type {};\n\n    template <typename T, size_t Dim, typename = std::void_t<>>\n    struct has_signature_2 : std::false_type {};\n\n    template <typename T, size_t Dim>\n    struct has_signature_2<\n        T, Dim,\n        std::void_t<decltype(T::apply(\n            std::declval<const gsl::not_null<Variables<example_list<Dim>>*>>(),\n            std::declval<const Variables<example_list<Dim>>&>(),\n            std::declval<const Mesh<Dim>&>(),\n            std::declval<const Jacobian<DataVector, Dim, Frame::Grid,\n                                        Frame::Inertial>&>(),\n            std::declval<const InverseJacobian<DataVector, Dim, Frame::Grid,\n                                               Frame::Inertial>&>(),\n            std::declval<const Jacobian<DataVector, Dim, Frame::ElementLogical,\n                                        Frame::Grid>&>(),\n            std::declval<const InverseJacobian<\n                DataVector, Dim, Frame::ElementLogical, Frame::Grid>&>(),\n            std::declval<const tnsr::I<DataVector, Dim, Frame::Inertial>&>(),\n            std::declval<const tnsr::I<DataVector, Dim, Frame::Grid>&>()))>>\n        : std::true_type {};\n\n    static_assert(has_signature_1<ConformingType, 1>::value or\n                  has_signature_2<ConformingType, 1>::value or\n                  has_signature_1<ConformingType, 2>::value or\n                  has_signature_2<ConformingType, 2>::value or\n                  has_signature_1<ConformingType, 3>::value or\n                  has_signature_2<ConformingType, 3>::value);\n\n    using allowed_src_tags = typename ConformingType::allowed_src_tags;\n    using required_src_tags = typename ConformingType::required_src_tags;\n    static_assert(\n        tmpl::size<tmpl::list_difference<required_src_tags,\n                                         allowed_src_tags>>::value == 0,\n        \"Some of the required source tags are not in the allowed source tags.\");\n\n    // Checking this in the grid frame is just a choice, but it suffices to\n    // check conformance.\n    using allowed_dest_tags =\n        typename ConformingType::template allowed_dest_tags<Frame::Grid>;\n    using required_dest_tags =\n        typename ConformingType::template required_dest_tags<Frame::Grid>;\n    static_assert(tmpl::size<tmpl::list_difference<required_dest_tags,\n                                                   allowed_dest_tags>>::value ==\n                      0,\n                  \"Some of the required destination tags are not in the \"\n                  \"allowed destination tags.\");\n  };\n};\n}  // namespace intrp::protocols\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <type_traits>\n\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeVarsToInterpolate.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace intrp::protocols {\nnamespace detail {\n// if_alias_exists_assert_conforms_to::value will always be `true` because we\n// want the static assert in the protocol to pass regardless of if the\n// `compute_vars_to_interpolate` alias exists in the conforming type. It's just\n// that if the alias does exist, there's an additional static assert to ensure\n// that the alias conforms to the ComputeVarsToInterpolate protocol\ntemplate <typename ConformingType, bool HasAlias>\nstruct if_alias_exists_assert_conforms_to;\n\ntemplate <typename ConformingType>\nstruct if_alias_exists_assert_conforms_to<ConformingType, false> {\n  static constexpr bool value = true;\n};\n\ntemplate <typename ConformingType>\nstruct if_alias_exists_assert_conforms_to<ConformingType, true> {\n  static constexpr bool value = true;\n  static_assert(tt::assert_conforms_to_v<\n                typename ConformingType::compute_vars_to_interpolate,\n                ComputeVarsToInterpolate>);\n};\n\ntemplate <typename ConformingType, bool HasAlias>\nconstexpr bool if_alias_exists_assert_conforms_to_v =\n    if_alias_exists_assert_conforms_to<ConformingType, HasAlias>::value;\n}  // namespace detail\n\n/*!\n * \\brief A protocol for `InterpolationTargetTag`s that are used in the\n * intrp::InterpolationTarget parallel component.\n *\n * \\details A struct conforming to the `InterpolationTargetTag` protocol must\n * have\n *\n * - a type alias `temporal_id` to a tag that tells the interpolation target\n *   what values of time to use (for example, `::Tags::Time`).\n *\n * - a type alias `vars_to_interpolate_to_target` which is a `tmpl::list` of\n *   tags describing variables to interpolate. Will be used to construct a\n *   `Variables`.\n *\n * - a type alias `compute_items_on_target` which is a `tmpl::list` of compute\n *   items that uses `vars_to_interpolate_to_target` as input.\n *\n * - a type alias `compute_target_points` that conforms to the\n *   intrp::protocols::ComputeTargetPoints protocol. This will compute the\n *   points on the surface that we are interpolating onto.\n *\n * - a type alias `post_interpolation_callbacks` which is a `tmpl::list` where\n *   every element of that list conforms to the\n *   intrp::protocols::PostInterpolationCallback protocol. Only one callback\n *   with the signature with `gsl::not_null` for both the DataBox and\n *   GlobalCache (see `intrp::protocols::PostInterpolationCallback`) is allowed\n *   in this list. Furthermore, if a callback with this signature is in the\n *   list, it must also be the *only* callback in the list. After the\n *   interpolation is complete, call this struct's `apply` function.\n *\n * A struct conforming to this protocol can also optionally have\n *\n * - a type alias `compute_vars_to_interpolate` that conforms to the\n *   intrp::protocols::ComputeVarsToInterpolate protocol. This is a struct that\n *   computes quantities in the volume that are required to compute different\n *   quantities on the surface we are interpolating to.\n *\n * - a type alias `interpolating_component` to the parallel component that will\n *   be interpolating to the interpolation target. Only needed when *not* using\n *   the Interpolator ParallelComponent.\n *\n * An example of a struct that conforms to this protocol is\n *\n * \\snippet Helpers/ParallelAlgorithms/Interpolation/Examples.hpp InterpolationTargetTag\n */\nstruct InterpolationTargetTag {\n  template <typename ConformingType>\n  struct test {\n    using temporal_id = typename ConformingType::temporal_id;\n\n    using vars_to_interpolate_to_target =\n        typename ConformingType::vars_to_interpolate_to_target;\n\n    // If the conforming type has a `compute_vars_to_interpolate` alias, make\n    // sure it conforms to the ComputeVarsToInterpolate protocol, otherwise just\n    // return true\n    static_assert(detail::if_alias_exists_assert_conforms_to_v<\n                  ConformingType,\n                  InterpolationTarget_detail::has_compute_vars_to_interpolate_v<\n                      ConformingType>>);\n\n    using compute_items_on_target =\n        typename ConformingType::compute_items_on_target;\n\n    using compute_target_points =\n        typename ConformingType::compute_target_points;\n    static_assert(\n        tt::assert_conforms_to_v<compute_target_points, ComputeTargetPoints>);\n\n    using post_interpolation_callbacks =\n        typename ConformingType::post_interpolation_callbacks;\n    static_assert(tmpl::all<post_interpolation_callbacks,\n                            tt::assert_conforms_to<\n                                tmpl::_1, PostInterpolationCallback>>::value);\n  };\n};\n}  // namespace intrp::protocols\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace intrp::protocols {\nnamespace detail {\nstruct DummyMetavariables;\nstruct DummyTag : db::SimpleTag {\n  using type = int;\n};\n\ntemplate <typename T, typename = std::void_t<>>\nstruct has_signature_1 : std::false_type {};\n\ntemplate <typename T>\nstruct has_signature_1<\n    T, std::void_t<decltype(T::apply(\n           std::declval<const db::DataBox<DummyTag>&>(),\n           std::declval<const Parallel::GlobalCache<DummyMetavariables>&>(),\n           std::declval<const double&>()))>> : std::true_type {\n  static_assert(\n      std::is_same_v<\n          void,\n          decltype(T::apply(\n              std::declval<const db::DataBox<DummyTag>&>(),\n              std::declval<const Parallel::GlobalCache<DummyMetavariables>&>(),\n              std::declval<const double&>()))>);\n};\n\ntemplate <typename T, typename = std::void_t<>>\nstruct has_signature_2 : std::false_type {};\n\ntemplate <typename T>\nstruct has_signature_2<\n    T, std::void_t<decltype(\n           T::apply(std::declval<const db::DataBox<DummyTag>&>(),\n                    std::declval<Parallel::GlobalCache<DummyMetavariables>&>(),\n                    std::declval<const double&>()))>> : std::true_type {\n  static_assert(\n      std::is_same_v<\n          void, decltype(T::apply(\n                    std::declval<const db::DataBox<DummyTag>&>(),\n                    std::declval<Parallel::GlobalCache<DummyMetavariables>&>(),\n                    std::declval<const double&>()))>);\n};\n\ntemplate <typename T, typename = std::void_t<>>\nstruct has_signature_3 : std::false_type {};\n\ntemplate <typename T>\nstruct has_signature_3<\n    T, std::void_t<decltype(\n           T::apply(std::declval<const gsl::not_null<db::DataBox<DummyTag>*>>(),\n                    std::declval<const gsl::not_null<\n                        Parallel::GlobalCache<DummyMetavariables>*>>(),\n                    std::declval<const double&>()))>> : std::true_type {\n  static_assert(\n      std::is_same_v<\n          bool, decltype(T::apply(\n                    std::declval<const gsl::not_null<db::DataBox<DummyTag>*>>(),\n                    std::declval<const gsl::not_null<\n                        Parallel::GlobalCache<DummyMetavariables>*>>(),\n                    std::declval<const double&>()))>);\n};\n\n}  // namespace detail\n\n/*!\n * \\brief A protocol for the type alias `post_interpolation_callbacks` found in\n * an InterpolationTargetTag.\n *\n * \\details A struct conforming to the `PostInterpolationCallback` protocol must\n * have\n *\n * - a function `apply` with one of the 3 signatures in the example. This apply\n *   function will be called once the interpolation is complete. `DbTags`\n *   includes everything in the `vars_to_interpolate_to_target` alias and the\n *   `compute_items_on_target` alias of the InterpolationTargetTag. The `apply`\n *   that returns a bool should return false only if it calls another\n *   `intrp::Action` that still needs the volume data at this temporal_id (such\n *   as another iteration of the horizon finder). These functions must be able\n *   to take any type for the `TemporalId`. If a specific temporal ID type is\n *   required, it should be `static_assert`ed in the function itself.\n *\n * A struct conforming to this protocol can also have an optional `static\n * constexpr double fill_invalid_points_with`. Any points outside the Domain\n * will be filled with this value. If this variable is not defined, then the\n * `apply` function must check for invalid points, and should typically exit\n * with an error message if it finds any.\n *\n * Here is an example of a class that conforms to this protocols:\n *\n * \\snippet Helpers/ParallelAlgorithms/Interpolation/Examples.hpp PostInterpolationCallback\n */\nstruct PostInterpolationCallback {\n  template <typename ConformingType>\n  struct test {\n    static_assert(detail::has_signature_1<ConformingType>::value or\n                  detail::has_signature_2<ConformingType>::value or\n                  detail::has_signature_3<ConformingType>::value);\n  };\n};\n}  // namespace intrp::protocols\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Protocols/ProtocolsNamespace.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace intrp {\n\n/*!\n * \\brief Contains all protocols used in the interpolation framework\n */\nnamespace protocols {}\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <deque>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass ElementId;\n/// \\endcond\n\nnamespace intrp {\n\nnamespace OptionTags {\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Groups option tags for InterpolationTargets.\n */\nstruct InterpolationTargets {\n  static constexpr Options::String help{\"Options for interpolation targets\"};\n};\n\n/*!\n * \\ingroup OptionGroupsGroup\n * \\brief Groups option tags for the Interpolator.\n */\nstruct Interpolator {\n  static constexpr Options::String help{\n      \"Options related to the Interpolator parallel component\"};\n};\n}  // namespace OptionTags\n\n/// Tags for items held in the `DataBox` of `InterpolationTarget` or\n/// `Interpolator`.\nnamespace Tags {\n/// Tag that determines the verbosity of output from the interpolation target\nstruct Verbosity : db::SimpleTag {\n  using type = ::Verbosity;\n\n  using option_tags =\n      tmpl::list<logging::OptionTags::Verbosity<OptionTags::Interpolator>>;\n  static constexpr bool pass_metavariables = false;\n  static ::Verbosity create_from_options(const ::Verbosity& verbosity) {\n    return verbosity;\n  }\n};\n\n/// Keeps track of which points have been filled with interpolated data.\ntemplate <typename TemporalId>\nstruct IndicesOfFilledInterpPoints : db::SimpleTag {\n  using type = std::unordered_map<TemporalId, std::unordered_set<size_t>>;\n};\n\n/// Keeps track of points that cannot be filled with interpolated data.\n///\n/// The InterpolationTarget can decide what to do with these points.\n/// In most cases the correct action is to throw an error, but in other\n/// cases one might wish to fill these points with a default value or\n/// take some other action.\ntemplate <typename TemporalId>\nstruct IndicesOfInvalidInterpPoints : db::SimpleTag {\n  using type = std::unordered_map<TemporalId, std::unordered_set<size_t>>;\n};\n\n/// `temporal_id`s on which to interpolate.\n///\n/// \\note This tag is only used in non-sequential targets\ntemplate <typename TemporalId>\nstruct TemporalIds : db::SimpleTag {\n  using type = std::unordered_set<TemporalId>;\n};\n\n/// `temporal_id`s that we have already interpolated onto.\n///  This is used to prevent problems with multiple late calls.\ntemplate <typename TemporalId>\nstruct CompletedTemporalIds : db::SimpleTag {\n  using type = std::deque<TemporalId>;\n};\n\n/// Holds interpolated variables on an InterpolationTarget.\ntemplate <typename InterpolationTargetTag, typename TemporalId>\nstruct InterpolatedVars : db::SimpleTag {\n  using type = std::unordered_map<\n      TemporalId,\n      Variables<\n          typename InterpolationTargetTag::vars_to_interpolate_to_target>>;\n};\n}  // namespace Tags\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/TagsMetafunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines metafunctions for manipulating Tags that refer to Tensors\n\n#pragma once\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TensorMetafunctions {\n/// \\ingroup TensorGroup\n/// \\brief Replaces Tag with an equivalent Tag but in frame NewFrame\ntemplate <typename Tag, typename NewFrame>\nstruct replace_frame_in_tag {\n  // Base definition, for tags with no template parameters,\n  // like some scalars.\n  using type = Tag;\n};\n// Specialization for tensors in gr::Tags / gh::Tags\ntemplate <template <typename, size_t, typename> typename Tag, size_t Dim,\n          typename Frame, typename DataType, typename NewFrame>\nstruct replace_frame_in_tag<Tag<DataType, Dim, Frame>, NewFrame> {\n  using type = Tag<DataType, Dim, NewFrame>;\n};\n// Specialization for scalars in gr::Tags (which have a DataType).\ntemplate <template <typename> typename Tag, typename DataType,\n          typename NewFrame>\nstruct replace_frame_in_tag<Tag<DataType>, NewFrame> {\n  using type = Tag<DataType>;\n};\n// Specializations for Tags::deriv<Tag> with Tag in gh::Tags\ntemplate <template <typename, size_t, typename> typename Tag, typename DataType,\n          size_t Dim, typename Frame, typename NewFrame>\nstruct replace_frame_in_tag<\n    ::Tags::deriv<Tag<DataType, Dim, Frame>, tmpl::size_t<Dim>, Frame>,\n    NewFrame> {\n  using type =\n      ::Tags::deriv<Tag<DataType, Dim, NewFrame>, tmpl::size_t<Dim>, NewFrame>;\n};\ntemplate <template <typename> typename Tag, typename DataType, size_t Dim,\n          typename Frame, typename NewFrame>\nstruct replace_frame_in_tag<\n    ::Tags::deriv<Tag<DataType>, tmpl::size_t<Dim>, Frame>, NewFrame> {\n  using type = ::Tags::deriv<Tag<DataType>, tmpl::size_t<Dim>, NewFrame>;\n};\n// Specialization for inverse jacobian\ntemplate <template <size_t, typename, typename> typename Tag, size_t Dim,\n          typename SrcFrame, typename TargetFrame, typename NewTargetFrame>\nstruct replace_frame_in_tag<Tag<Dim, SrcFrame, TargetFrame>, NewTargetFrame> {\n  using type = Tag<Dim, SrcFrame, NewTargetFrame>;\n};\n\n/// \\ingroup TensorGroup\n/// \\brief Replaces Tag with an equivalent Tag but in frame NewFrame\ntemplate <typename Tag, typename NewFrame>\nusing replace_frame_in_tag_t =\n    typename replace_frame_in_tag<Tag, NewFrame>::type;\n\n/// \\ingroup TensorGroup\n/// \\brief Replaces every Tag in Taglist with an equivalent Tag\n/// but in frame NewFrame\ntemplate <typename TagList, typename NewFrame>\nusing replace_frame_in_taglist =\n    tmpl::transform<TagList,\n                    tmpl::bind<replace_frame_in_tag_t, tmpl::_1, NewFrame>>;\n}  // namespace TensorMetafunctions\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  KerrHorizon.cpp\n  LineSegment.cpp\n  SpecifiedPoints.cpp\n  Sphere.cpp\n  WedgeSectionTorus.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  KerrHorizon.hpp\n  LineSegment.hpp\n  SpecifiedPoints.hpp\n  Sphere.hpp\n  WedgeSectionTorus.hpp\n)\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/KerrHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Interpolation/Targets/KerrHorizon.hpp\"\n\n#include <algorithm>\n#include <pup.h>\n\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace intrp::OptionHolders {\nKerrHorizon::KerrHorizon(size_t l_max_in, std::array<double, 3> center_in,\n                         double mass_in,\n                         std::array<double, 3> dimensionless_spin_in,\n                         const ylm::AngularOrdering angular_ordering_in,\n                         const Options::Context& context)\n    : l_max(l_max_in),\n      center(center_in),\n      mass(mass_in),\n      dimensionless_spin(dimensionless_spin_in),\n      angular_ordering{angular_ordering_in} {\n  // above NOLINTs for std::move of trivially copyable type.\n  if (mass <= 0.0) {\n    // Check here, rather than put a lower_bound on the Tag, because\n    // we want to exclude mass being exactly zero.\n    PARSE_ERROR(context, \"KerrHorizon expects mass>0, not \" << mass);\n  }\n  if (magnitude(dimensionless_spin) > 1.0) {\n    PARSE_ERROR(context, \"KerrHorizon expects |dimensionless_spin|<=1, not \"\n                             << magnitude(dimensionless_spin));\n  }\n}\n\nvoid KerrHorizon::pup(PUP::er& p) {\n  p | l_max;\n  p | center;\n  p | mass;\n  p | dimensionless_spin;\n  p | angular_ordering;\n}\n\nbool operator==(const KerrHorizon& lhs, const KerrHorizon& rhs) {\n  return lhs.l_max == rhs.l_max and lhs.center == rhs.center and\n         lhs.mass == rhs.mass and\n         lhs.dimensionless_spin == rhs.dimensionless_spin and\n         lhs.angular_ordering == rhs.angular_ordering;\n}\n\nbool operator!=(const KerrHorizon& lhs, const KerrHorizon& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace intrp::OptionHolders\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/KerrHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace intrp {\nnamespace Tags {\ntemplate <typename TemporalId>\nstruct TemporalIds;\n}  // namespace Tags\n}  // namespace intrp\n/// \\endcond\n\nnamespace intrp {\n\nnamespace OptionHolders {\n/// A surface that conforms to the horizon of a Kerr black hole in\n/// Kerr-Schild coordinates.\n///\n/// \\details In addition to the parameters for the Kerr black hole, this holder\n/// contains the `LMax` which encodes the angular resolution of the spherical\n/// harmonic basis and `AngularOrdering` which encodes the collocation\n/// ordering.\nstruct KerrHorizon {\n  struct LMax {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"KerrHorizon is expanded in Ylms up to l=LMax\"};\n  };\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\"Center of black hole\"};\n  };\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\"Mass of black hole\"};\n  };\n  struct DimensionlessSpin {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Dimensionless spin of black hole\"};\n  };\n  struct AngularOrdering {\n    using type = ylm::AngularOrdering;\n    static constexpr Options::String help = {\n        \"Chooses theta,phi ordering in 2d array\"};\n  };\n  using options =\n      tmpl::list<LMax, Center, Mass, DimensionlessSpin, AngularOrdering>;\n  static constexpr Options::String help = {\n      \"A Strahlkorper conforming to the horizon (in Kerr-Schild coordinates)\"\n      \" of a Kerr black hole with a specified center, mass, and spin.\"};\n\n  KerrHorizon(size_t l_max_in, std::array<double, 3> center_in, double mass_in,\n              std::array<double, 3> dimensionless_spin_in,\n              ylm::AngularOrdering angular_ordering_in,\n              const Options::Context& context = {});\n\n  KerrHorizon() = default;\n  KerrHorizon(const KerrHorizon& /*rhs*/) = default;\n  KerrHorizon& operator=(const KerrHorizon& /*rhs*/) = delete;\n  KerrHorizon(KerrHorizon&& /*rhs*/) = default;\n  KerrHorizon& operator=(KerrHorizon&& /*rhs*/) = default;\n  ~KerrHorizon() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  size_t l_max{};\n  std::array<double, 3> center{};\n  double mass{};\n  std::array<double, 3> dimensionless_spin{};\n  ylm::AngularOrdering angular_ordering;\n};\n\nbool operator==(const KerrHorizon& lhs, const KerrHorizon& rhs);\nbool operator!=(const KerrHorizon& lhs, const KerrHorizon& rhs);\n\n}  // namespace OptionHolders\n\nnamespace OptionTags {\ntemplate <typename InterpolationTargetTag>\nstruct KerrHorizon {\n  using type = OptionHolders::KerrHorizon;\n  static constexpr Options::String help{\n      \"Options for interpolation onto Kerr horizon.\"};\n  static std::string name() {\n    return pretty_type::name<InterpolationTargetTag>();\n  }\n  using group = InterpolationTargets;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\ntemplate <typename InterpolationTargetTag>\nstruct KerrHorizon : db::SimpleTag {\n  using type = OptionHolders::KerrHorizon;\n  using option_tags =\n      tmpl::list<OptionTags::KerrHorizon<InterpolationTargetTag>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n}  // namespace Tags\n\nnamespace TargetPoints {\n/// \\brief Computes points on a Kerr horizon.\n///\n/// The points are such that they conform to the horizon of a Kerr\n/// black hole (in Kerr-Schild coordinates) with given center, mass,\n/// and dimensionless spin, as specified in the options.\n///\n/// \\note The returned points are not actually on the horizon;\n/// instead, they are collocation points of a Strahlkorper whose\n/// spectral representation matches the horizon shape up to order\n/// LMax, where LMax is the spherical-harmonic order specified in the\n/// options.  As LMax increases, the returned points converge to the\n/// horizon.\n///\n/// Conforms to the intrp::protocols::ComputeTargetPoints protocol\n///\n/// For requirements on InterpolationTargetTag, see\n/// intrp::protocols::InterpolationTargetTag\ntemplate <typename InterpolationTargetTag, typename Frame>\nstruct KerrHorizon : tt::ConformsTo<intrp::protocols::ComputeTargetPoints> {\n  using const_global_cache_tags =\n      tmpl::list<Tags::KerrHorizon<InterpolationTargetTag>>;\n  using is_sequential = std::false_type;\n  using frame = Frame;\n\n  using simple_tags = typename ylm::Tags::items_tags<Frame>;\n  using compute_tags = typename ylm::Tags::compute_items_tags<Frame>;\n\n  template <typename DbTags, typename Metavariables>\n  static void initialize(const gsl::not_null<db::DataBox<DbTags>*> box,\n                         const Parallel::GlobalCache<Metavariables>& cache) {\n    const auto& kerr_horizon =\n        Parallel::get<Tags::KerrHorizon<InterpolationTargetTag>>(cache);\n\n    // Make a Strahlkorper with the correct shape.\n    ylm::Strahlkorper<Frame> strahlkorper(\n        kerr_horizon.l_max, kerr_horizon.l_max,\n        get(gr::Solutions::kerr_horizon_radius(\n            ::ylm::Spherepack(kerr_horizon.l_max, kerr_horizon.l_max)\n                .theta_phi_points(),\n            kerr_horizon.mass, kerr_horizon.dimensionless_spin)),\n        kerr_horizon.center);\n    Initialization::mutate_assign<simple_tags>(box, std::move(strahlkorper));\n  }\n\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static tnsr::I<DataVector, 3, Frame> points(\n      const db::DataBox<DbTags>& box,\n      const tmpl::type_<Metavariables>& metavariables_v,\n      const TemporalId& /*temporal_id*/) {\n    return points(box, metavariables_v);\n  }\n  template <typename Metavariables, typename DbTags>\n  static tnsr::I<DataVector, 3, Frame> points(\n      const db::DataBox<DbTags>& box,\n      const tmpl::type_<Metavariables>& /*meta*/) {\n    const auto& kerr_horizon =\n        db::get<Tags::KerrHorizon<InterpolationTargetTag>>(box);\n    if (kerr_horizon.angular_ordering == ylm::AngularOrdering::Strahlkorper) {\n      return db::get<ylm::Tags::CartesianCoords<Frame>>(box);\n    } else {\n      const auto& strahlkorper = db::get<ylm::Tags::Strahlkorper<Frame>>(box);\n      const auto& coords = db::get<ylm::Tags::CartesianCoords<Frame>>(box);\n      const auto physical_extents =\n          strahlkorper.ylm_spherepack().physical_extents();\n      auto transposed_coords =\n          tnsr::I<DataVector, 3, Frame>(get<0>(coords).size());\n      for (size_t i = 0; i < 3; ++i) {\n        transpose(make_not_null(&transposed_coords.get(i)), coords.get(i),\n                  physical_extents[0], physical_extents[1]);\n      }\n      return transposed_coords;\n    }\n  }\n};\n\n}  // namespace TargetPoints\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/LineSegment.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Interpolation/Targets/LineSegment.hpp\"\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace intrp::OptionHolders {\n\ntemplate <size_t VolumeDim>\nLineSegment<VolumeDim>::LineSegment(std::array<double, VolumeDim> begin_in,\n                                    std::array<double, VolumeDim> end_in,\n                                    size_t number_of_points_in)\n    : begin(std::move(begin_in)),  // NOLINT\n      end(std::move(end_in)),      // NOLINT\n      number_of_points(number_of_points_in) {}\n// above NOLINT for std::move of trivially copyable type.\n\ntemplate <size_t VolumeDim>\nvoid LineSegment<VolumeDim>::pup(PUP::er& p) {\n  p | begin;\n  p | end;\n  p | number_of_points;\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const LineSegment<VolumeDim>& lhs,\n                const LineSegment<VolumeDim>& rhs) {\n  return lhs.begin == rhs.begin and lhs.end == rhs.end and\n         lhs.number_of_points == rhs.number_of_points;\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const LineSegment<VolumeDim>& lhs,\n                const LineSegment<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                               \\\n  template struct LineSegment<DIM(data)>;                  \\\n  template bool operator==(const LineSegment<DIM(data)>&,  \\\n                           const LineSegment<DIM(data)>&); \\\n  template bool operator!=(const LineSegment<DIM(data)>&,  \\\n                           const LineSegment<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace intrp::OptionHolders\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/LineSegment.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace intrp {\nnamespace Tags {\ntemplate <typename TemporalId>\nstruct TemporalIds;\n}  // namespace Tags\n}  // namespace intrp\n/// \\endcond\n\nnamespace intrp {\n\nnamespace OptionHolders {\n/// A line segment extending from `Begin` to `End`,\n/// containing `NumberOfPoints` uniformly-spaced points including the endpoints.\n///\n/// \\note Input coordinates are interpreted in `Frame::Inertial`\ntemplate <size_t VolumeDim>\nstruct LineSegment {\n  struct Begin {\n    using type = std::array<double, VolumeDim>;\n    static constexpr Options::String help = {\"Beginning endpoint\"};\n  };\n  struct End {\n    using type = std::array<double, VolumeDim>;\n    static constexpr Options::String help = {\"Ending endpoint\"};\n  };\n  struct NumberOfPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Number of points including endpoints\"};\n    static type lower_bound() { return 2; }\n  };\n  using options = tmpl::list<Begin, End, NumberOfPoints>;\n  static constexpr Options::String help = {\n      \"A line segment extending from Begin to End, containing NumberOfPoints\"\n      \" uniformly-spaced points including the endpoints.\"};\n\n  LineSegment(std::array<double, VolumeDim> begin_in,\n              std::array<double, VolumeDim> end_in, size_t number_of_points_in);\n\n  LineSegment() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  std::array<double, VolumeDim> begin{};\n  std::array<double, VolumeDim> end{};\n  size_t number_of_points{};\n};\n\ntemplate <size_t VolumeDim>\nbool operator==(const LineSegment<VolumeDim>& lhs,\n                const LineSegment<VolumeDim>& rhs);\ntemplate <size_t VolumeDim>\nbool operator!=(const LineSegment<VolumeDim>& lhs,\n                const LineSegment<VolumeDim>& rhs);\n\n}  // namespace OptionHolders\n\nnamespace OptionTags {\ntemplate <typename InterpolationTargetTag, size_t VolumeDim>\nstruct LineSegment {\n  using type = OptionHolders::LineSegment<VolumeDim>;\n  static constexpr Options::String help{\n      \"Options for interpolation onto line segment.\"};\n  static std::string name() {\n    return pretty_type::name<InterpolationTargetTag>();\n  }\n  using group = InterpolationTargets;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\ntemplate <typename InterpolationTargetTag, size_t VolumeDim>\nstruct LineSegment : db::SimpleTag {\n  using type = OptionHolders::LineSegment<VolumeDim>;\n  using option_tags =\n      tmpl::list<OptionTags::LineSegment<InterpolationTargetTag, VolumeDim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n}  // namespace Tags\n\nnamespace TargetPoints {\n/// \\brief Computes points on a line segment.\n///\n/// Conforms to the intrp::protocols::ComputeTargetPoints protocol\n///\n/// For requirements on InterpolationTargetTag, see\n/// intrp::protocols::InterpolationTargetTag\ntemplate <typename InterpolationTargetTag, size_t VolumeDim, typename Frame>\nstruct LineSegment : tt::ConformsTo<intrp::protocols::ComputeTargetPoints> {\n  using const_global_cache_tags =\n      tmpl::list<Tags::LineSegment<InterpolationTargetTag, VolumeDim>>;\n  using is_sequential = std::false_type;\n  using frame = Frame;\n\n  template <typename Metavariables, typename DbTags>\n  static tnsr::I<DataVector, VolumeDim, Frame> points(\n      const db::DataBox<DbTags>& box,\n      const tmpl::type_<Metavariables>& /*meta*/) {\n    const auto& options =\n        get<Tags::LineSegment<InterpolationTargetTag, VolumeDim>>(box);\n\n    // Fill points on a line segment\n    const double fractional_distance = 1.0 / (options.number_of_points - 1);\n    tnsr::I<DataVector, VolumeDim, Frame> target_points(\n        options.number_of_points);\n    for (size_t n = 0; n < options.number_of_points; ++n) {\n      for (size_t d = 0; d < VolumeDim; ++d) {\n        target_points.get(d)[n] =\n            gsl::at(options.begin, d) +\n            n * fractional_distance *\n                (gsl::at(options.end, d) - gsl::at(options.begin, d));\n      }\n    }\n    return target_points;\n  }\n\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static tnsr::I<DataVector, VolumeDim, Frame> points(\n      const db::DataBox<DbTags>& box, const tmpl::type_<Metavariables>& meta,\n      const TemporalId& /*temporal_id*/) {\n    return points(box, meta);\n  }\n};\n\n}  // namespace TargetPoints\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/SpecifiedPoints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Interpolation/Targets/SpecifiedPoints.hpp\"\n\n#include <pup.h>\n#include <utility>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace intrp::OptionHolders {\n\ntemplate <size_t VolumeDim>\nSpecifiedPoints<VolumeDim>::SpecifiedPoints(\n    std::vector<std::array<double, VolumeDim>> points_in)\n    : points(std::move(points_in)) {}\n\ntemplate <size_t VolumeDim>\nvoid SpecifiedPoints<VolumeDim>::pup(PUP::er& p) {\n  p | points;\n}\n\ntemplate <size_t VolumeDim>\nbool operator==(const SpecifiedPoints<VolumeDim>& lhs,\n                const SpecifiedPoints<VolumeDim>& rhs) {\n  return lhs.points == rhs.points;\n}\n\ntemplate <size_t VolumeDim>\nbool operator!=(const SpecifiedPoints<VolumeDim>& lhs,\n                const SpecifiedPoints<VolumeDim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                   \\\n  template struct SpecifiedPoints<DIM(data)>;                  \\\n  template bool operator==(const SpecifiedPoints<DIM(data)>&,  \\\n                           const SpecifiedPoints<DIM(data)>&); \\\n  template bool operator!=(const SpecifiedPoints<DIM(data)>&,  \\\n                           const SpecifiedPoints<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace intrp::OptionHolders\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/SpecifiedPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace intrp {\nnamespace Tags {\ntemplate <typename TemporalId>\nstruct TemporalIds;\n}  // namespace Tags\n}  // namespace intrp\n/// \\endcond\n\nnamespace intrp {\n\nnamespace OptionHolders {\n/// A list of specified points to interpolate to.\n///\n/// \\note Input coordinates are interpreted in `Frame::Inertial`\ntemplate <size_t VolumeDim>\nstruct SpecifiedPoints {\n  struct Points {\n    using type = std::vector<std::array<double, VolumeDim>>;\n    static constexpr Options::String help = {\"Coordinates of each point\"};\n  };\n  using options = tmpl::list<Points>;\n  static constexpr Options::String help = {\"A list of specified points\"};\n\n  explicit SpecifiedPoints(\n      std::vector<std::array<double, VolumeDim>> points_in);\n\n  SpecifiedPoints() = default;\n  SpecifiedPoints(const SpecifiedPoints& /*rhs*/) = default;\n  SpecifiedPoints& operator=(const SpecifiedPoints& /*rhs*/) = default;\n  SpecifiedPoints(SpecifiedPoints&& /*rhs*/) = default;\n  SpecifiedPoints& operator=(SpecifiedPoints&& /*rhs*/) = default;\n  ~SpecifiedPoints() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  std::vector<std::array<double, VolumeDim>> points{};\n};\n\ntemplate <size_t VolumeDim>\nbool operator==(const SpecifiedPoints<VolumeDim>& lhs,\n                const SpecifiedPoints<VolumeDim>& rhs);\ntemplate <size_t VolumeDim>\nbool operator!=(const SpecifiedPoints<VolumeDim>& lhs,\n                const SpecifiedPoints<VolumeDim>& rhs);\n\n}  // namespace OptionHolders\n\nnamespace OptionTags {\ntemplate <typename InterpolationTargetTag, size_t VolumeDim>\nstruct SpecifiedPoints {\n  using type = OptionHolders::SpecifiedPoints<VolumeDim>;\n  static constexpr Options::String help{\n      \"Options for interpolation onto a specified list of points.\"};\n  static std::string name() {\n    return pretty_type::name<InterpolationTargetTag>();\n  }\n  using group = InterpolationTargets;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\ntemplate <typename InterpolationTargetTag, size_t VolumeDim>\nstruct SpecifiedPoints : db::SimpleTag {\n  using type = OptionHolders::SpecifiedPoints<VolumeDim>;\n  using option_tags = tmpl::list<\n      OptionTags::SpecifiedPoints<InterpolationTargetTag, VolumeDim>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n}  // namespace Tags\n\nnamespace TargetPoints {\n/// \\brief Returns list of points as specified in input file.\n///\n/// Conforms to the intrp::protocols::ComputeTargetPoints protocol\n///\n/// For requirements on InterpolationTargetTag, see\n/// intrp::protocols::InterpolationTargetTag\ntemplate <typename InterpolationTargetTag, size_t VolumeDim>\nstruct SpecifiedPoints : tt::ConformsTo<intrp::protocols::ComputeTargetPoints> {\n  using const_global_cache_tags =\n      tmpl::list<Tags::SpecifiedPoints<InterpolationTargetTag, VolumeDim>>;\n  using is_sequential = std::false_type;\n  using frame = Frame::Inertial;\n\n  template <typename Metavariables, typename DbTags>\n  static tnsr::I<DataVector, VolumeDim, Frame::Inertial> points(\n      const db::DataBox<DbTags>& box,\n      const tmpl::type_<Metavariables>& /*meta*/) {\n    const auto& options =\n        get<Tags::SpecifiedPoints<InterpolationTargetTag, VolumeDim>>(box);\n    tnsr::I<DataVector, VolumeDim, Frame::Inertial> target_points(\n        options.points.size());\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      for (size_t i = 0; i < options.points.size(); ++i) {\n        target_points.get(d)[i] = gsl::at(options.points[i], d);\n      }\n    }\n    return target_points;\n  }\n\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static tnsr::I<DataVector, VolumeDim, Frame::Inertial> points(\n      const db::DataBox<DbTags>& box, const tmpl::type_<Metavariables>& meta,\n      const TemporalId& /*temporal_id*/) {\n    return points(box, meta);\n  }\n};\n\n}  // namespace TargetPoints\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/Sphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"Options/Options.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace intrp::OptionHolders {\nnamespace {\nstruct SphereVisitor {\n  const Options::Context& context;\n\n  std::set<double> operator()(const double radius) {\n    positive_radius(radius);\n    return std::set<double>{radius};\n  }\n\n  std::set<double> operator()(const std::vector<double>& radii) {\n    std::set<double> result;\n    for (const double radius : radii) {\n      if (result.count(radius) != 0) {\n        using ::operator<<;\n        PARSE_ERROR(context,\n                    \"Cannot insert radius \"\n                        << radius\n                        << \" into radii for Sphere interpolation target. It \"\n                           \"already exists. Existing radii are \"\n                        << result);\n      }\n      positive_radius(radius);\n      result.emplace(radius);\n    }\n    return result;\n  }\n\n private:\n  void positive_radius(const double radius) {\n    if (radius <= 0) {\n      PARSE_ERROR(context, \"Radius must be positive, not \" << radius);\n    }\n  }\n};\n}  // namespace\n\nSphere::Sphere(const size_t l_max_in, const std::array<double, 3> center_in,\n               const typename Radius::type& radius_in,\n               const ylm::AngularOrdering angular_ordering_in,\n               const Options::Context& context)\n    : l_max(l_max_in),\n      center(center_in),\n      radii(std::visit(SphereVisitor{context}, radius_in)),\n      angular_ordering(angular_ordering_in) {}\n\nvoid Sphere::pup(PUP::er& p) {\n  p | l_max;\n  p | center;\n  p | radii;\n  p | angular_ordering;\n}\n\nbool operator==(const Sphere& lhs, const Sphere& rhs) {\n  return lhs.l_max == rhs.l_max and lhs.center == rhs.center and\n         lhs.radii == rhs.radii and\n         lhs.angular_ordering == rhs.angular_ordering;\n}\n\nbool operator!=(const Sphere& lhs, const Sphere& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace intrp::OptionHolders\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/Sphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <set>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace intrp {\nnamespace Tags {\ntemplate <typename TemporalId>\nstruct TemporalIds;\n}  // namespace Tags\n}  // namespace intrp\n/// \\endcond\n\nnamespace intrp {\n\nnamespace OptionHolders {\n/// A series of concentric spherical surfaces.\n///\n/// \\details The parameter `LMax` sets the number of collocation points on\n/// each spherical surface equal to `(l_max + 1) * (2 * l_max + 1)`. The\n/// parameter `AngularOrdering` encodes the collocation ordering. For example,\n/// the apparent horizon finder relies on spherepack routines that require\n/// `Strahlkorper` for `AngularOrdering`, and using these surfaces for a CCE\n/// worldtube requires `Cce` for `AngularOrdering`.\nstruct Sphere {\n  struct LMax {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The number of collocation points on each sphere will be equal to \"\n        \"`(l_max + 1) * (2 * l_max + 1)`\"};\n  };\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\"Center of every sphere\"};\n  };\n  struct Radius {\n    using type = std::variant<double, std::vector<double>>;\n    static constexpr Options::String help = {\"Radius of the sphere(s)\"};\n  };\n  struct AngularOrdering {\n    using type = ylm::AngularOrdering;\n    static constexpr Options::String help = {\n        \"Chooses theta,phi ordering in 2d array\"};\n  };\n  using options = tmpl::list<LMax, Center, Radius, AngularOrdering>;\n  static constexpr Options::String help = {\n      \"An arbitrary number of spherical surface.\"};\n  Sphere(const size_t l_max_in, const std::array<double, 3> center_in,\n         const typename Radius::type& radius_in,\n         ylm::AngularOrdering angular_ordering_in,\n         const Options::Context& context = {});\n\n  Sphere() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  size_t l_max{0};\n  std::array<double, 3> center{std::numeric_limits<double>::signaling_NaN()};\n  std::set<double> radii;\n  ylm::AngularOrdering angular_ordering;\n};\n\nbool operator==(const Sphere& lhs, const Sphere& rhs);\nbool operator!=(const Sphere& lhs, const Sphere& rhs);\n\n}  // namespace OptionHolders\n\nnamespace OptionTags {\ntemplate <typename InterpolationTargetTag>\nstruct Sphere {\n  using type = OptionHolders::Sphere;\n  static constexpr Options::String help{\n      \"Options for interpolation onto a sphere(s).\"};\n  static std::string name() {\n    return pretty_type::name<InterpolationTargetTag>();\n  }\n  using group = InterpolationTargets;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\ntemplate <typename InterpolationTargetTag>\nstruct Sphere : db::SimpleTag {\n  using type = OptionHolders::Sphere;\n  using option_tags = tmpl::list<OptionTags::Sphere<InterpolationTargetTag>>;\n\n  static constexpr bool pass_metavariables = false;\n  static OptionHolders::Sphere create_from_options(\n      const OptionHolders::Sphere& option) {\n    return option;\n  }\n};\n\ntemplate <typename Frame>\nstruct AllCoords : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame>;\n};\n}  // namespace Tags\n\nnamespace TargetPoints {\n/// \\brief Computes points on spherical surfaces.\n///\n/// Conforms to the intrp::protocols::ComputeTargetPoints protocol\n///\n/// For requirements on InterpolationTargetTag, see\n/// intrp::protocols::InterpolationTargetTag\ntemplate <typename InterpolationTargetTag, typename Frame>\nstruct Sphere : tt::ConformsTo<intrp::protocols::ComputeTargetPoints> {\n  using const_global_cache_tags =\n      tmpl::list<Tags::Sphere<InterpolationTargetTag>>;\n  using is_sequential = std::false_type;\n  using frame = Frame;\n\n  using simple_tags =\n      tmpl::list<ylm::Tags::Strahlkorper<Frame>, Tags::AllCoords<Frame>>;\n  using compute_tags = typename ylm::Tags::compute_items_tags<Frame>;\n\n  template <typename DbTags, typename Metavariables>\n  static void initialize(const gsl::not_null<db::DataBox<DbTags>*> box,\n                         const Parallel::GlobalCache<Metavariables>& cache) {\n    const auto& sphere =\n        Parallel::get<Tags::Sphere<InterpolationTargetTag>>(cache);\n    const size_t l_max = sphere.l_max;\n    const auto& radii = sphere.radii;\n\n    // Total number of points is number of points for one sphere times the\n    // number of spheres we use.\n    const size_t num_points = radii.size() * (l_max + 1) * (2 * l_max + 1);\n\n    tnsr::I<DataVector, 3, Frame> all_coords{num_points};\n\n    size_t index = 0;\n    for (const double radius : radii) {\n      ylm::Strahlkorper<Frame> strahlkorper(\n          l_max, l_max, DataVector{(l_max + 1) * (2 * l_max + 1), radius},\n          sphere.center);\n\n      db::mutate<ylm::Tags::Strahlkorper<Frame>>(\n          [&strahlkorper](const gsl::not_null<ylm::Strahlkorper<Frame>*>\n                              local_strahlkorper) {\n            *local_strahlkorper = std::move(strahlkorper);\n          },\n          box);\n\n      // This copy is ok because it's just in initialization\n      auto coords = db::get<ylm::Tags::CartesianCoords<Frame>>(*box);\n\n      // If the angular ordering is Strahlkorper then we don't have to do\n      // anything to the coords because they are already in the right order\n      if (sphere.angular_ordering == ylm::AngularOrdering::Cce) {\n        const auto physical_extents =\n            strahlkorper.ylm_spherepack().physical_extents();\n        auto transposed_coords =\n            tnsr::I<DataVector, 3, Frame>(get<0>(coords).size());\n\n        for (size_t i = 0; i < 3; ++i) {\n          transpose(make_not_null(&transposed_coords.get(i)), coords.get(i),\n                    physical_extents[0], physical_extents[1]);\n        }\n        coords = std::move(transposed_coords);\n      }\n\n      const size_t tmp_index = index;\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t local_index = 0; local_index < coords.get(i).size();\n             local_index++, index++) {\n          all_coords.get(i)[index] = coords.get(i)[local_index];\n        }\n        if (i != 2) {\n          index = tmp_index;\n        }\n      }\n    }\n\n    // If this fails, there is a bug. Can't really test it\n    // LCOV_EXCL_START\n    ASSERT(index == all_coords.get(0).size(),\n           \"Didn't initialize points of Sphere target correctly. index = \"\n               << index << \" all_coords.size() = \" << all_coords.get(0).size());\n    // LCOV_EXCL_STOP\n\n    Initialization::mutate_assign<tmpl::list<Tags::AllCoords<Frame>>>(\n        box, std::move(all_coords));\n  }\n\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static tnsr::I<DataVector, 3, Frame> points(\n      const db::DataBox<DbTags>& box,\n      const tmpl::type_<Metavariables>& metavariables_v,\n      const TemporalId& /*temporal_id*/) {\n    return points(box, metavariables_v);\n  }\n  template <typename Metavariables, typename DbTags>\n  static tnsr::I<DataVector, 3, Frame> points(\n      const db::DataBox<DbTags>& box,\n      const tmpl::type_<Metavariables>& /*meta*/) {\n    return db::get<Tags::AllCoords<Frame>>(box);\n  }\n};\n}  // namespace TargetPoints\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/WedgeSectionTorus.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/Interpolation/Targets/WedgeSectionTorus.hpp\"\n\n#include <pup.h>\n\n#include \"Options/ParseError.hpp\"\n\nnamespace intrp::OptionHolders {\n\nWedgeSectionTorus::WedgeSectionTorus(\n    const double min_radius_in, const double max_radius_in,\n    const double min_theta_in, const double max_theta_in,\n    const size_t number_of_radial_points_in,\n    const size_t number_of_theta_points_in,\n    const size_t number_of_phi_points_in, const bool use_uniform_radial_grid_in,\n    const bool use_uniform_theta_grid_in, const Options::Context& context)\n    : min_radius(min_radius_in),\n      max_radius(max_radius_in),\n      min_theta(min_theta_in),\n      max_theta(max_theta_in),\n      number_of_radial_points(number_of_radial_points_in),\n      number_of_theta_points(number_of_theta_points_in),\n      number_of_phi_points(number_of_phi_points_in),\n      use_uniform_radial_grid(use_uniform_radial_grid_in),\n      use_uniform_theta_grid(use_uniform_theta_grid_in) {\n  if (min_radius >= max_radius) {\n    PARSE_ERROR(context, \"WedgeSectionTorus expects min_radius < max_radius\");\n  }\n  if (min_theta >= max_theta) {\n    PARSE_ERROR(context, \"WedgeSectionTorus expects min_theta < max_theta\");\n  }\n}\n\nvoid WedgeSectionTorus::pup(PUP::er& p) {\n  p | min_radius;\n  p | max_radius;\n  p | min_theta;\n  p | max_theta;\n  p | number_of_radial_points;\n  p | number_of_theta_points;\n  p | number_of_phi_points;\n  p | use_uniform_radial_grid;\n  p | use_uniform_theta_grid;\n}\n\nbool operator==(const WedgeSectionTorus& lhs, const WedgeSectionTorus& rhs) {\n  return lhs.min_radius == rhs.min_radius and\n         lhs.max_radius == rhs.max_radius and lhs.min_theta == rhs.min_theta and\n         lhs.max_theta == rhs.max_theta and\n         lhs.number_of_radial_points == rhs.number_of_radial_points and\n         lhs.number_of_theta_points == rhs.number_of_theta_points and\n         lhs.number_of_phi_points == rhs.number_of_phi_points and\n         lhs.use_uniform_radial_grid == rhs.use_uniform_radial_grid and\n         lhs.use_uniform_theta_grid == rhs.use_uniform_theta_grid;\n}\n\nbool operator!=(const WedgeSectionTorus& lhs, const WedgeSectionTorus& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace intrp::OptionHolders\n"
  },
  {
    "path": "src/ParallelAlgorithms/Interpolation/Targets/WedgeSectionTorus.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nnamespace intrp {\nnamespace Tags {\ntemplate <typename TemporalId>\nstruct TemporalIds;\n}  // namespace Tags\n}  // namespace intrp\n/// \\endcond\n\nnamespace intrp {\n\nnamespace OptionHolders {\n/// \\brief A solid torus of points, useful, e.g., when measuring data from an\n/// accretion disc.\n///\n/// The torus's cross section (e.g., a cut at \\f$\\phi=0\\f$) is a wedge-like\n/// shape bounded by \\f$r_{\\text{min}} \\le r \\le r_{\\text{max}}\\f$ and\n/// \\f$\\theta_{\\text{min}} \\le \\theta \\le \\theta_{\\text{max}}\\f$.\n///\n/// The grid points are located on surfaces of constant \\f$r\\f$, \\f$\\theta\\f$,\n/// and \\f$\\phi\\f$. There are `NumberRadialPoints` points in the radial\n/// direction between `MinRadius` and `MaxRadius` (including these endpoints);\n/// `NumberThetaPoints` points in the \\f$\\theta\\f$ direction between `MinTheta`\n/// and `MaxTheta` (including these endpoints); `NumberPhiPoints` points in the\n/// \\f$\\phi\\f$ direction (with one point always at \\f$\\phi=0\\f$).\n///\n/// By default, the points follow a Legendre Gauss-Lobatto distribution in the\n/// \\f$r\\f$ and \\f$\\theta\\f$ directions, and a uniform distribution in the\n/// \\f$\\phi\\f$ direction. The distribution in the \\f$r\\f$ (and/or \\f$\\theta\\f$)\n/// direction can be made uniform using the `UniformRadialGrid` (and/or\n/// `UniformThetaGrid`) option.\n///\n/// The `target_points` form a 3D mesh ordered with \\f$r\\f$ varying fastest,\n/// then \\f$\\theta\\f$, and finally \\f$\\phi\\f$ varying slowest.\n///\n/// \\note Input coordinates (radii, angles) are interpreted in the\n/// `Frame::Inertial`\nstruct WedgeSectionTorus {\n  struct MinRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Inner radius of torus\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct MaxRadius {\n    using type = double;\n    static constexpr Options::String help = {\"Outer radius of torus\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct MinTheta {\n    using type = double;\n    static constexpr Options::String help = {\"Angle of top of wedge (radians)\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return M_PI; }\n  };\n  struct MaxTheta {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Angle of bottom of wedge (radians)\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return M_PI; }\n  };\n  struct NumberRadialPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Number of radial points, including endpoints\"};\n    static type lower_bound() { return 2; }\n  };\n  struct NumberThetaPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Number of theta points, including endpoints\"};\n    static type lower_bound() { return 2; }\n  };\n  struct NumberPhiPoints {\n    using type = size_t;\n    static constexpr Options::String help = {\"Number of phi points\"};\n    static type lower_bound() { return 1; }\n  };\n  struct UniformRadialGrid {\n    using type = bool;\n    static constexpr Options::String help = {\"Use uniform radial grid\"};\n  };\n  struct UniformThetaGrid {\n    using type = bool;\n    static constexpr Options::String help = {\"Use uniform theta grid\"};\n  };\n\n  using options =\n      tmpl::list<MinRadius, MaxRadius, MinTheta, MaxTheta, NumberRadialPoints,\n                 NumberThetaPoints, NumberPhiPoints, UniformRadialGrid,\n                 UniformThetaGrid>;\n  static constexpr Options::String help = {\n      \"A torus extending from MinRadius to MaxRadius in r, MinTheta to MaxTheta\"\n      \" in theta, and 2pi in phi.\"};\n\n  WedgeSectionTorus(double min_radius_in, double max_radius_in,\n                    double min_theta_in, double max_theta_in,\n                    size_t number_of_radial_points_in,\n                    size_t number_of_theta_points_in,\n                    size_t number_of_phi_points_in,\n                    bool use_uniform_radial_grid_in,\n                    bool use_uniform_theta_grid_in,\n                    const Options::Context& context = {});\n\n  WedgeSectionTorus() = default;\n  WedgeSectionTorus(const WedgeSectionTorus& /*rhs*/) = default;\n  WedgeSectionTorus& operator=(const WedgeSectionTorus& /*rhs*/) = delete;\n  WedgeSectionTorus(WedgeSectionTorus&& /*rhs*/) = default;\n  WedgeSectionTorus& operator=(WedgeSectionTorus&& /*rhs*/) = default;\n  ~WedgeSectionTorus() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  double min_radius;\n  double max_radius;\n  double min_theta;\n  double max_theta;\n  size_t number_of_radial_points;\n  size_t number_of_theta_points;\n  size_t number_of_phi_points;\n  bool use_uniform_radial_grid;\n  bool use_uniform_theta_grid;\n};\n\nbool operator==(const WedgeSectionTorus& lhs, const WedgeSectionTorus& rhs);\nbool operator!=(const WedgeSectionTorus& lhs, const WedgeSectionTorus& rhs);\n\n}  // namespace OptionHolders\n\nnamespace OptionTags {\ntemplate <typename InterpolationTargetTag>\nstruct WedgeSectionTorus {\n  using type = OptionHolders::WedgeSectionTorus;\n  static constexpr Options::String help{\n      \"Options for interpolation onto Kerr horizon.\"};\n  static std::string name() {\n    return pretty_type::name<InterpolationTargetTag>();\n  }\n  using group = InterpolationTargets;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\ntemplate <typename InterpolationTargetTag>\nstruct WedgeSectionTorus : db::SimpleTag {\n  using type = OptionHolders::WedgeSectionTorus;\n  using option_tags =\n      tmpl::list<OptionTags::WedgeSectionTorus<InterpolationTargetTag>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n}  // namespace Tags\n\nnamespace TargetPoints {\n/// \\brief Computes points in a wedge-sectioned torus.\n///\n/// Conforms to the intrp::protocols::ComputeTargetPoints protocol\n///\n/// For requirements on InterpolationTargetTag, see\n/// intrp::protocols::InterpolationTargetTag\ntemplate <typename InterpolationTargetTag>\nstruct WedgeSectionTorus\n    : tt::ConformsTo<intrp::protocols::ComputeTargetPoints> {\n  using const_global_cache_tags =\n      tmpl::list<Tags::WedgeSectionTorus<InterpolationTargetTag>>;\n  using is_sequential = std::false_type;\n  using frame = Frame::Inertial;\n\n  template <typename Metavariables, typename DbTags>\n  static tnsr::I<DataVector, 3, Frame::Inertial> points(\n      const db::DataBox<DbTags>& box,\n      const tmpl::type_<Metavariables>& /*meta*/) {\n    const auto& options =\n        get<Tags::WedgeSectionTorus<InterpolationTargetTag>>(box);\n\n    // Compute locations of constant r/theta/phi surfaces\n    const size_t num_radial = options.number_of_radial_points;\n    const DataVector radii_1d = [&num_radial, &options]() {\n      DataVector result(num_radial);\n      if (options.use_uniform_radial_grid) {\n        // uniform point distribution\n        const double coefficient =\n            (options.max_radius - options.min_radius) / (num_radial - 1.0);\n        for (size_t r = 0; r < num_radial; ++r) {\n          result[r] = options.min_radius + coefficient * r;\n        }\n      } else {\n        // radial Legendre-Gauss-Lobatto distribution via linear rescaling\n        const double mean = 0.5 * (options.max_radius + options.min_radius);\n        const double diff = 0.5 * (options.max_radius - options.min_radius);\n        result =\n            mean + diff * Spectral::collocation_points<\n                              Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto>(num_radial);\n      }\n      return result;\n    }();\n    const size_t num_theta = options.number_of_theta_points;\n    const DataVector thetas_1d = [&num_theta, &options]() {\n      DataVector result(num_theta);\n      if (options.use_uniform_theta_grid) {\n        // uniform point distribution\n        const double coefficient =\n            (options.max_theta - options.min_theta) / (num_theta - 1.0);\n        for (size_t theta = 0; theta < num_theta; ++theta) {\n          result[theta] = options.min_theta + coefficient * theta;\n        }\n      } else {\n        // Legendre-Gauss-Lobatto point distribution (in theta)\n        const double mean = 0.5 * (options.max_theta + options.min_theta);\n        const double diff = 0.5 * (options.max_theta - options.min_theta);\n        result =\n            mean + diff * Spectral::collocation_points<\n                              Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto>(num_theta);\n      }\n      return result;\n    }();\n    const size_t num_phi = options.number_of_phi_points;\n    const DataVector phis_1d = [&num_phi]() {\n      DataVector result(num_phi);\n      // We do NOT want a grid point at phi = 2pi, as this would duplicate the\n      // phi = 0 data. So, divide by num_phi rather than (n-1) as elsewhere.\n      const double coefficient = 2.0 * M_PI / num_phi;\n      for (size_t phi = 0; phi < num_phi; ++phi) {\n        result[phi] = coefficient * phi;\n      }\n      return result;\n    }();\n\n    // Take tensor product to get full 3D r/theta/phi points\n    const size_t num_total = num_radial * num_theta * num_phi;\n    DataVector radii(num_total);\n    DataVector thetas(num_total);\n    DataVector phis(num_total);\n    for (size_t phi = 0; phi < num_phi; ++phi) {\n      for (size_t theta = 0; theta < num_theta; ++theta) {\n        for (size_t r = 0; r < num_radial; ++r) {\n          const size_t i =\n              r + theta * num_radial + phi * num_theta * num_radial;\n          radii[i] = radii_1d[r];\n          thetas[i] = thetas_1d[theta];\n          phis[i] = phis_1d[phi];\n        }\n      }\n    }\n\n    // Compute x/y/z coordinates\n    // Note: theta measured from +z axis, phi measured from +x axis\n    tnsr::I<DataVector, 3, Frame::Inertial> target_points(num_total);\n    get<0>(target_points) = radii * sin(thetas) * cos(phis);\n    get<1>(target_points) = radii * sin(thetas) * sin(phis);\n    get<2>(target_points) = radii * cos(thetas);\n\n    return target_points;\n  }\n\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static tnsr::I<DataVector, 3, Frame::Inertial> points(\n      const db::DataBox<DbTags>& box, const tmpl::type_<Metavariables>& meta,\n      const TemporalId& /*temporal_id*/) {\n    return points(box, meta);\n  }\n};\n\n}  // namespace TargetPoints\n}  // namespace intrp\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Actions/BuildMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/LinearSolver/Actions/BuildMatrix.hpp\"\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <utility>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace LinearSolver::Actions::detail {\n\ntemplate <size_t Dim>\nstd::pair<size_t, size_t> total_num_points_and_local_first_index(\n    const ElementId<Dim>& element_id,\n    const std::map<ElementId<Dim>, size_t>& num_points_per_element) {\n  size_t total_num_points = 0;\n  size_t local_first_index = 0;\n  for (const auto& [element_id_i, num_points] : num_points_per_element) {\n    if (element_id_i < element_id) {\n      local_first_index += num_points;\n    }\n    total_num_points += num_points;\n  }\n  return {total_num_points, local_first_index};\n}\n\nstd::optional<size_t> local_unit_vector_index(const size_t iteration_id,\n                                              const size_t local_first_index,\n                                              const size_t local_num_points) {\n  if (iteration_id < local_first_index or\n      iteration_id >= local_first_index + local_num_points) {\n    return std::nullopt;\n  } else {\n    return iteration_id - local_first_index;\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                               \\\n  template std::pair<size_t, size_t> total_num_points_and_local_first_index( \\\n      const ElementId<DIM(data)>& element_id,                                \\\n      const std::map<ElementId<DIM(data)>, size_t>& num_points_per_element);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n\n}  // namespace LinearSolver::Actions::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Actions/BuildMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Functionality to build the explicit matrix representation of the linear\n/// operator column-by-column. This is useful for debugging and analysis only,\n/// not to actually solve the elliptic problem (that should happen iteratively).\n\n#pragma once\n\n#include <blaze/math/Submatrix.h>\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/CompressedMatrix.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GetSection.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/Section.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver {\nnamespace OptionTags {\n\n/// Options for building the explicit matrix representation of the linear\n/// operator.\nstruct BuildMatrixOptionsGroup {\n  static std::string name() { return \"BuildMatrix\"; }\n  static constexpr Options::String help = {\n      \"Options for building the explicit matrix representation of the linear \"\n      \"operator. This is done by applying the linear operator to unit \"\n      \"vectors and is useful for debugging and analysis only, not to actually \"\n      \"solve the elliptic problem (that should happen iteratively).\"};\n};\n\n/// Subfile name in the volume data H5 files where the matrix will be stored.\nstruct MatrixSubfileName {\n  using type = Options::Auto<std::string, Options::AutoLabel::None>;\n  using group = BuildMatrixOptionsGroup;\n  static constexpr Options::String help = {\n      \"Subfile name in the volume data H5 files where the matrix will be \"\n      \"stored. Each observation in the subfile is a column of the matrix. The \"\n      \"row index is the order of elements defined by the ElementId in the \"\n      \"volume data, by the order of tensor components encoded in the name of \"\n      \"the components, and by the contiguous ordering of grid points for each \"\n      \"component.\"};\n};\n\n/// Option for enabling direct solve of the linear problem.\nstruct EnableDirectSolve {\n  using type = bool;\n  using group = BuildMatrixOptionsGroup;\n  static constexpr Options::String help = {\n      \"Directly invert the assembled matrix and solve the linear problem. \"\n      \"This can be unfeasible if the linear problem is too big.\"};\n};\n\n/// Option for skipping resets of the built matrix.\nstruct SkipResets {\n  using type = bool;\n  using group = BuildMatrixOptionsGroup;\n  static constexpr Options::String help = {\n      \"Skip resets of the built matrix. This only has an effect in cases \"\n      \"where the operator changes, e.g. between nonlinear-solver iterations. \"\n      \"Skipping resets avoids expensive re-building of the matrix, but comes \"\n      \"at the cost of less accurate preconditioning and thus potentially more \"\n      \"preconditioned iterations. Whether or not this helps convergence \"\n      \"overall is highly problem-dependent.\"};\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n\n/// Subfile name in the volume data H5 files where the matrix will be stored.\nstruct MatrixSubfileName : db::SimpleTag {\n  using type = std::optional<std::string>;\n  using option_tags = tmpl::list<OptionTags::MatrixSubfileName>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) { return value; }\n};\n\n/// Size of the matrix: number of grid points times number of variables\nstruct TotalNumPoints : db::SimpleTag {\n  using type = size_t;\n};\n\n/// Index of the first point in this element\nstruct LocalFirstIndex : db::SimpleTag {\n  using type = size_t;\n};\n\n/// Matrix representation of the linear operator. Stored as sparse matrix. The\n/// full matrix has size `total_num_points` by `total_num_points`. Each element\n/// only stores the rows corresponding to its grid points (starting at\n/// `local_first_index`), but all columns.\ntemplate <typename ValueType>\nstruct Matrix : db::SimpleTag {\n  using type = blaze::CompressedMatrix<ValueType>;\n};\n\n/// Option for enabling direct solve of the linear problem.\nstruct EnableDirectSolve : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::EnableDirectSolve>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) { return value; }\n};\n\n/// Option for skipping resets of the built matrix.\nstruct SkipResets : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::SkipResets>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) { return value; }\n};\n\n}  // namespace Tags\n\nnamespace Actions {\n\nnamespace detail {\n\ntemplate <typename FieldsTag, typename FixedSourcesTag, typename OperandTag,\n          typename OperatorAppliedToOperandTag, typename CoordsTag,\n          typename ArraySectionIdTag>\nstruct BuildMatrixMetavars {\n  using iteration_id_tag = Convergence::Tags::IterationId<\n      LinearSolver::OptionTags::BuildMatrixOptionsGroup>;\n  using fields_tag = FieldsTag;\n  using fixed_sources_tag = FixedSourcesTag;\n  using operand_tag = OperandTag;\n  using operator_applied_to_operand_tag = OperatorAppliedToOperandTag;\n  using coords_tag = CoordsTag;\n  using array_section_id_tag = ArraySectionIdTag;\n\n  using value_type = typename OperatorAppliedToOperandTag::type::value_type;\n\n  struct end_label {};\n};\n\n/// \\brief The total number of grid points (size of the matrix) and the index of\n/// the first grid point in this element (the offset into the matrix\n/// corresponding to this element). The `num_points_per_element` should hold\n/// the total number of degrees of freedom for each element, so the number of\n/// variables times the number of grid points.\ntemplate <size_t Dim>\nstd::pair<size_t, size_t> total_num_points_and_local_first_index(\n    const ElementId<Dim>& element_id,\n    const std::map<ElementId<Dim>, size_t>& num_points_per_element);\n\n/// \\brief The index of the '1' of the unit vector in this element, or\n/// std::nullopt if the '1' is in another element.\n///\n/// \\param iteration_id enumerates all grid points\n/// \\param local_first_index the index of the first grid point in this element\n/// \\param local_num_points the number of grid points in this element\nstd::optional<size_t> local_unit_vector_index(size_t iteration_id,\n                                              size_t local_first_index,\n                                              size_t local_num_points);\n\n/// \\brief Observe matrix column as volume data.\n///\n/// This is the format of the volume data:\n///\n/// - Columns of the matrix are enumerated by the observation ID\n/// - Rows are enumerated by the order of elements defined by the ElementId\n///   in the volume data, by the order of tensor components encoded in the\n///   name of the components, and by the contiguous ordering of grid points\n///   for each component.\n/// - We also observe coordinates so the data can be plotted as volume data.\n///\n/// The matrix can be reconstructed from this data in Python with the function\n/// `spectre.Elliptic.ReadH5.read_matrix`.\ntemplate <typename ParallelComponent, typename OperatorAppliedToOperandTags,\n          size_t Dim, typename CoordsFrame, typename Metavariables>\nvoid observe_matrix_column(\n    const size_t column,\n    const Variables<OperatorAppliedToOperandTags>& operator_applied_to_operand,\n    const ElementId<Dim>& element_id, const Mesh<Dim>& mesh,\n    const tnsr::I<DataVector, Dim, CoordsFrame>& coords,\n    const std::string& subfile_name, const std::string& section_observation_key,\n    Parallel::GlobalCache<Metavariables>& cache) {\n  std::vector<TensorComponent> observe_components{};\n  observe_components.reserve(\n      Dim +\n      Variables<\n          OperatorAppliedToOperandTags>::number_of_independent_components);\n  for (size_t i = 0; i < Dim; ++i) {\n    observe_components.emplace_back(\n        get_output(CoordsFrame{}) + \"Coordinates\" + coords.component_suffix(i),\n        coords.get(i));\n  }\n  size_t component_i = 0;\n  tmpl::for_each<OperatorAppliedToOperandTags>([&operator_applied_to_operand,\n                                                &observe_components,\n                                                &component_i](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const auto& tensor = get<tag>(operator_applied_to_operand);\n    using TensorType = std::decay_t<decltype(tensor)>;\n    using VectorType = typename TensorType::type;\n    using ValueType = typename VectorType::value_type;\n    for (size_t i = 0; i < tensor.size(); ++i) {\n      if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n        observe_components.emplace_back(\n            \"Re(Variable_\" + std::to_string(component_i) + \")\",\n            real(tensor[i]));\n        observe_components.emplace_back(\n            \"Im(Variable_\" + std::to_string(component_i) + \")\",\n            imag(tensor[i]));\n      } else {\n        observe_components.emplace_back(\n            \"Variable_\" + std::to_string(component_i), tensor[i]);\n      }\n      ++component_i;\n    }\n  });\n\n  observers::contribute_volume_data<\n      not Parallel::is_nodegroup_v<ParallelComponent>>(\n      cache,\n      observers::ObservationId(static_cast<double>(column),\n                               subfile_name + section_observation_key),\n      \"/\" + subfile_name,\n      Parallel::make_array_component_id<ParallelComponent>(element_id),\n      ElementVolumeData{element_id, std::move(observe_components), mesh});\n}\n\n/// \\brief Register with the volume observer\ntemplate <typename ArraySectionIdTag>\nstruct RegisterWithVolumeObserver {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& box,\n                const ArrayIndex& /*array_index*/) {\n    // Get the observation key, or \"Unused\" if the element does not belong\n    // to a section with this tag. In the latter case, no observations will\n    // ever be contributed.\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    ASSERT(section_observation_key != \"Unused\",\n           \"The identifier 'Unused' is reserved to indicate that no \"\n           \"observations with this key will be contributed. Use a different \"\n           \"key, or change the identifier 'Unused' to something else.\");\n    const auto& subfile_name = get<Tags::MatrixSubfileName>(box);\n    return {\n        observers::TypeOfObservation::Volume,\n        observers::ObservationKey(subfile_name.value_or(\"Unused\") +\n                                  section_observation_key.value_or(\"Unused\"))};\n  }\n};\n\n}  // namespace detail\n\n/// \\cond\ntemplate <typename BuildMatrixMetavars>\nstruct PrepareBuildMatrix;\ntemplate <typename BuildMatrixMetavars>\nstruct AssembleFullMatrix;\n/// \\endcond\n\n/// Dispatch global reduction to get the size of the matrix\ntemplate <typename BuildMatrixMetavars>\nstruct CollectTotalNumPoints {\n private:\n  using IterationIdTag = typename BuildMatrixMetavars::iteration_id_tag;\n  using OperandTag = typename BuildMatrixMetavars::operand_tag;\n  using ArraySectionIdTag = typename BuildMatrixMetavars::array_section_id_tag;\n  using value_type = typename BuildMatrixMetavars::value_type;\n\n public:\n  using simple_tags =\n      tmpl::list<Tags::TotalNumPoints, Tags::LocalFirstIndex, IterationIdTag,\n                 OperandTag, Tags::Matrix<value_type>>;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionTags::BuildMatrixOptionsGroup>>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Skip everything on elements that are not part of the section\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        constexpr size_t last_action_index = tmpl::index_of<\n            ActionList,\n            ::Actions::Label<typename BuildMatrixMetavars::end_label>>::value;\n        return {Parallel::AlgorithmExecution::Continue, last_action_index + 1};\n      }\n    }\n    if (db::get<Tags::TotalNumPoints>(box) != 0) {\n      // We have built the matrix already, so we can skip ahead\n      constexpr size_t assemble_matrix_index =\n          tmpl::index_of<ActionList,\n                         AssembleFullMatrix<BuildMatrixMetavars>>::value;\n      return {Parallel::AlgorithmExecution::Continue, assemble_matrix_index};\n    }\n    db::mutate<Tags::TotalNumPoints, OperandTag>(\n        [](const auto total_num_points, const auto operand,\n           const size_t num_points) {\n          // Set num points to zero until we know the total number of points\n          *total_num_points = 0;\n          // Size operand and fill with zeros\n          operand->initialize(num_points, 0.);\n        },\n        make_not_null(&box),\n        get<domain::Tags::Mesh<Dim>>(box).number_of_grid_points());\n    // Collect the total number of grid points\n    auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<PrepareBuildMatrix<BuildMatrixMetavars>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n            Parallel::ReductionDatum<std::map<ElementId<Dim>, size_t>,\n                                     funcl::Merge<>>>{\n            element_id.grid_index(),\n            std::map<ElementId<Dim>, size_t>{\n                std::make_pair(element_id, get<OperandTag>(box).size())}},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[element_id],\n        Parallel::get_parallel_component<ParallelComponent>(cache),\n        make_not_null(&section));\n    // Pause the algorithm for now. The reduction will be broadcast to the next\n    // action which is responsible for restarting the algorithm.\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n/// Receive the reduction and initialize the algorithm\ntemplate <typename BuildMatrixMetavars>\nstruct PrepareBuildMatrix {\n private:\n  using IterationIdTag = typename BuildMatrixMetavars::iteration_id_tag;\n  using OperandTag = typename BuildMatrixMetavars::operand_tag;\n  using value_type = typename BuildMatrixMetavars::value_type;\n\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, size_t Dim>\n  static void apply(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const size_t grid_index,\n      const std::map<ElementId<Dim>, size_t>& num_points_per_element) {\n    if (grid_index != element_id.grid_index()) {\n      return;\n    }\n    const auto [total_num_points, local_first_index] =\n        detail::total_num_points_and_local_first_index(element_id,\n                                                       num_points_per_element);\n    if (get<logging::Tags::Verbosity<OptionTags::BuildMatrixOptionsGroup>>(\n            box) >= Verbosity::Quiet and\n        local_first_index == 0) {\n      Parallel::printf(\n          \"Building explicit matrix representation of size %zu x %zu.\\n\",\n          total_num_points, total_num_points);\n    }\n    db::mutate<Tags::TotalNumPoints, Tags::LocalFirstIndex, IterationIdTag,\n               Tags::Matrix<value_type>>(\n        [captured_total_num_points = total_num_points,\n         captured_local_first_index = local_first_index,\n         local_num_points = num_points_per_element.at(element_id)](\n            const auto stored_total_num_points,\n            const auto stored_local_first_index, const auto iteration_id,\n            const gsl::not_null<blaze::CompressedMatrix<value_type>*> matrix) {\n          *stored_total_num_points = captured_total_num_points;\n          *stored_local_first_index = captured_local_first_index;\n          *iteration_id = 0;\n          matrix->clear();\n          matrix->resize(local_num_points, captured_total_num_points);\n        },\n        make_not_null(&box));\n    // Proceed with algorithm\n    Parallel::get_parallel_component<ParallelComponent>(cache)[element_id]\n        .perform_algorithm(true);\n  }\n};\n\n/// Set the operand to a unit vector with a '1' at the current grid point.\n/// Applying the operator to this operand gives a column of the matrix. We jump\n/// back to this until we have iterated over all grid points.\ntemplate <typename BuildMatrixMetavars>\nstruct SetUnitVector {\n private:\n  using IterationIdTag = typename BuildMatrixMetavars::iteration_id_tag;\n  using OperandTag = typename BuildMatrixMetavars::operand_tag;\n\n public:\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const std::optional<size_t> local_unit_vector_index =\n        detail::local_unit_vector_index(get<IterationIdTag>(box),\n                                        get<Tags::LocalFirstIndex>(box),\n                                        get<OperandTag>(box).size());\n    if (local_unit_vector_index.has_value()) {\n      db::mutate<OperandTag>(\n          [&local_unit_vector_index](const auto operand) {\n            operand->data()[*local_unit_vector_index] = 1.;\n          },\n          make_not_null(&box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// --- Linear operator will be applied to the unit vector here ---\n\n/// Write result out to disk, reset operand back to zero, and keep iterating\ntemplate <typename BuildMatrixMetavars>\nstruct StoreMatrixColumn {\n private:\n  using IterationIdTag = typename BuildMatrixMetavars::iteration_id_tag;\n  using OperandTag = typename BuildMatrixMetavars::operand_tag;\n  using OperatorAppliedToOperandTag =\n      typename BuildMatrixMetavars::operator_applied_to_operand_tag;\n  using CoordsTag = typename BuildMatrixMetavars::coords_tag;\n  using ArraySectionIdTag = typename BuildMatrixMetavars::array_section_id_tag;\n  using value_type = typename BuildMatrixMetavars::value_type;\n\n public:\n  using const_global_cache_tags = tmpl::list<Tags::MatrixSubfileName>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t iteration_id = get<IterationIdTag>(box);\n    const size_t local_first_index = get<Tags::LocalFirstIndex>(box);\n    if (get<logging::Tags::Verbosity<OptionTags::BuildMatrixOptionsGroup>>(\n            box) >= Verbosity::Verbose and\n        local_first_index == 0 and (iteration_id + 1) % 100 == 0) {\n      Parallel::printf(\"Column %zu / %zu done.\\n\", iteration_id + 1,\n                       get<Tags::TotalNumPoints>(box));\n    }\n    // This is the result of applying the linear operator to the unit vector. It\n    // is a column of the operator matrix.\n    const auto& operator_applied_to_operand =\n        get<OperatorAppliedToOperandTag>(box);\n    // Store in sparse matrix\n    db::mutate<Tags::Matrix<value_type>>(\n        [&operator_applied_to_operand, &iteration_id](\n            const gsl::not_null<blaze::CompressedMatrix<value_type>*> matrix) {\n          for (size_t i = 0; i < operator_applied_to_operand.size(); ++i) {\n            const auto value = operator_applied_to_operand.data()[i];\n            if (not equal_within_roundoff(value, 0.)) {\n              matrix->insert(i, iteration_id, value);\n            }\n          }\n        },\n        make_not_null(&box));\n    // Write it out to disk\n    if (const auto& subfile_name = get<Tags::MatrixSubfileName>(box);\n        subfile_name.has_value()) {\n      detail::observe_matrix_column<ParallelComponent>(\n          iteration_id, operator_applied_to_operand, element_id,\n          get<domain::Tags::Mesh<Dim>>(box), get<CoordsTag>(box), *subfile_name,\n          *observers::get_section_observation_key<ArraySectionIdTag>(box),\n          cache);\n    }\n    // Reset operand to zero\n    const std::optional<size_t> local_unit_vector_index =\n        detail::local_unit_vector_index(iteration_id, local_first_index,\n                                        get<OperandTag>(box).size());\n    if (local_unit_vector_index.has_value()) {\n      db::mutate<OperandTag>(\n          [&local_unit_vector_index](const auto operand) {\n            operand->data()[*local_unit_vector_index] = 0.;\n          },\n          make_not_null(&box));\n    }\n    // Keep iterating\n    db::mutate<IterationIdTag>(\n        [](const auto local_iteration_id) { ++(*local_iteration_id); },\n        make_not_null(&box));\n    if (get<IterationIdTag>(box) < get<Tags::TotalNumPoints>(box)) {\n      constexpr size_t set_unit_vector_index =\n          tmpl::index_of<ActionList, SetUnitVector<BuildMatrixMetavars>>::value;\n      return {Parallel::AlgorithmExecution::Continue, set_unit_vector_index};\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename Metavariables, typename BuildMatrixMetavars>\nstruct BuildMatrixSingleton {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using const_global_cache_tags = tmpl::list<>;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<BuildMatrixSingleton>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <typename ElementArrayComponent, typename BuildMatrixMetavars>\nstruct InvertMatrix;\n\ntemplate <typename BuildMatrixMetavars>\nstruct AssembleFullMatrix {\n private:\n  using value_type = typename BuildMatrixMetavars::value_type;\n  using ArraySectionIdTag = typename BuildMatrixMetavars::array_section_id_tag;\n  using FixedSourcesTag = typename BuildMatrixMetavars::fixed_sources_tag;\n  using ReductionType = std::pair<blaze::CompressedMatrix<value_type>,\n                                  typename FixedSourcesTag::type>;\n\n public:\n  using const_global_cache_tags = tmpl::list<Tags::EnableDirectSolve>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (not db::get<Tags::EnableDirectSolve>(box)) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<\n        InvertMatrix<ParallelComponent, BuildMatrixMetavars>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n            Parallel::ReductionDatum<std::map<ElementId<Dim>, ReductionType>,\n                                     funcl::Merge<>>>{\n            element_id.grid_index(),\n            std::map<ElementId<Dim>, ReductionType>{std::make_pair(\n                element_id, ReductionType{get<Tags::Matrix<value_type>>(box),\n                                          get<FixedSourcesTag>(box)})}},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[element_id],\n        Parallel::get_parallel_component<\n            BuildMatrixSingleton<Metavariables, BuildMatrixMetavars>>(cache),\n        make_not_null(&section));\n    // Pause the algorithm for now. The reduction will be received by the next\n    // action which is responsible for restarting the algorithm.\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <typename BuildMatrixMetavars>\nstruct StoreSolution;\n\ntemplate <typename ElementArrayComponent, typename BuildMatrixMetavars>\nstruct InvertMatrix {\n private:\n  using value_type = typename BuildMatrixMetavars::value_type;\n  using FixedSourcesTag = typename BuildMatrixMetavars::fixed_sources_tag;\n  using ReductionType = std::pair<blaze::CompressedMatrix<value_type>,\n                                  typename FixedSourcesTag::type>;\n\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, size_t Dim>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/, const size_t grid_index,\n                    const std::map<ElementId<Dim>, ReductionType>&\n                        matrix_and_sources_slices) {\n    const size_t total_num_points =\n        matrix_and_sources_slices.begin()->second.first.columns();\n    blaze::DynamicMatrix<value_type> matrix(total_num_points, total_num_points);\n    blaze::DynamicVector<value_type> source(total_num_points);\n    // Assemble the full matrix and source\n    size_t i = 0;\n    for (const auto& [element_id, slice] : matrix_and_sources_slices) {\n      const auto& [matrix_slice, source_slice] = slice;\n      ASSERT(matrix_slice.rows() == source_slice.size(),\n             \"Matrix and source slice have different sizes.\");\n      const size_t slice_length = matrix_slice.rows();\n      blaze::submatrix(matrix, i, 0, slice_length, matrix_slice.columns()) =\n          matrix_slice;\n      std::copy_n(source_slice.data(), slice_length, source.data() + i);\n      i += slice_length;\n    }\n    ASSERT(i == total_num_points, \"The matrix is not fully assembled. Expected \"\n                                      << total_num_points << \" rows, got \"\n                                      << i);\n    // Solve the equations Ax = b. Could use a more efficient solver here if\n    // needed. But anything iterative could be done with the parallel linear\n    // solver, the point here is to assemble the full matrix and invert it\n    // directly.\n    if (get<logging::Tags::Verbosity<OptionTags::BuildMatrixOptionsGroup>>(\n            box) >= Verbosity::Quiet) {\n      Parallel::printf(\"Inverting matrix of size %zu x %zu\\n\", total_num_points,\n                       total_num_points);\n    }\n    const blaze::DynamicVector<value_type> solution =\n        blaze::solve(matrix, source);\n    // Broadcast the solution to the elements\n    Parallel::simple_action<StoreSolution<BuildMatrixMetavars>>(\n        Parallel::get_parallel_component<ElementArrayComponent>(cache),\n        grid_index, solution);\n  }\n};\n\ntemplate <typename BuildMatrixMetavars>\nstruct StoreSolution {\n private:\n  using value_type = typename BuildMatrixMetavars::value_type;\n  using FieldsTag = typename BuildMatrixMetavars::fields_tag;\n  using FixedSourcesTag = typename BuildMatrixMetavars::fixed_sources_tag;\n\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, size_t Dim>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ElementId<Dim>& element_id, const size_t grid_index,\n                    const blaze::DynamicVector<value_type>& solution) {\n    if (grid_index != element_id.grid_index()) {\n      return;\n    }\n    const size_t local_first_index = db::get<Tags::LocalFirstIndex>(box);\n    db::mutate<\n        FieldsTag,\n        db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, FieldsTag>>(\n        [&solution, &local_first_index](const auto fields,\n                                        const auto operator_applied_to_fields,\n                                        const auto& fixed_sources) {\n          std::copy_n(\n              solution.begin() + static_cast<ptrdiff_t>(local_first_index),\n              fields->size(), fields->data());\n          // The linear problem is solved now, so Ax = b\n          *operator_applied_to_fields = fixed_sources;\n        },\n        make_not_null(&box), db::get<FixedSourcesTag>(box));\n    // Proceed with algorithm\n    Parallel::get_parallel_component<ParallelComponent>(cache)[element_id]\n        .perform_algorithm(true);\n  }\n};\n\ntemplate <typename BuildMatrixMetavars>\nstruct ProjectBuildMatrix : tt::ConformsTo<::amr::protocols::Projector> {\n private:\n  using IterationIdTag = typename BuildMatrixMetavars::iteration_id_tag;\n  using OperandTag = typename BuildMatrixMetavars::operand_tag;\n  using value_type = typename BuildMatrixMetavars::value_type;\n\n public:\n  using return_tags =\n      tmpl::list<Tags::TotalNumPoints, Tags::Matrix<value_type>,\n                 Tags::LocalFirstIndex, IterationIdTag, OperandTag>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename... AmrData>\n  static void apply(\n      const gsl::not_null<size_t*> total_num_points,\n      const gsl::not_null<blaze::CompressedMatrix<value_type>*> matrix,\n      const AmrData&... /*amr_data*/) {\n    // Reset the built matrix when AMR changes the grid.\n    // In case AMR is configured to keep coarse grids then the coarse-grid\n    // elements don't run projectors during AMR, so they are not reset and just\n    // keep the built matrix. This is good because the coarse grids are\n    // unchanged and so the matrix is still valid (and can be used as bottom\n    // solver in multigrid).\n    *total_num_points = 0;\n    matrix->clear();\n  }\n};\n\n/// Dispatch global reduction to get the size of the matrix\ntemplate <typename BuildMatrixMetavars>\nstruct ResetBuiltMatrix {\n private:\n  using value_type = typename BuildMatrixMetavars::value_type;\n  using ArraySectionIdTag = typename BuildMatrixMetavars::array_section_id_tag;\n\n public:\n  using const_global_cache_tags = tmpl::list<Tags::SkipResets>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Skip on elements that are not part of the section\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n      }\n    }\n    if (db::get<Tags::SkipResets>(box)) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    db::mutate<Tags::TotalNumPoints, Tags::Matrix<value_type>>(\n        [](const auto total_num_points, const auto matrix) {\n          *total_num_points = 0;\n          matrix->clear();\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/*!\n * \\brief Build the explicit matrix representation of the linear operator and\n * optionally invert it directly to solve the problem.\n *\n * This is useful for debugging and analysis only, not to actually solve the\n * elliptic problem (that should happen iteratively).\n *\n * Add the `actions` to the action list to build the matrix. The\n * `ApplyOperatorActions` template parameter are the actions that apply the\n * linear operator to the `OperandTag`. Also add the `amr_projectors` to the\n * list of AMR projectors and the `register_actions`\n *\n * \\tparam FieldsTag The solution will be stored here (if direct solve is\n * enabled).\n * \\tparam FixedSourcesTag The source `b` in the problem `Ax = b`. Only used\n * if direct solve is enabled.\n * \\tparam OperandTag Will be set to unit vectors, with the '1' moving through\n * all points over the course of the iteration.\n * \\tparam OperatorAppliedToOperandTag Where the `ApplyOperatorActions` store\n * the result of applying the linear operator to the `OperandTag`.\n * \\tparam CoordsTag The tag of the coordinates observed alongside the matrix\n * for volume data visualization.\n * \\tparam ArraySectionIdTag Can identify a subset of elements that this\n * algorithm should run over, e.g. in a multigrid setting.\n */\ntemplate <typename FieldsTag, typename FixedSourcesTag, typename OperandTag,\n          typename OperatorAppliedToOperandTag, typename CoordsTag,\n          typename ArraySectionIdTag = void>\nstruct BuildMatrix {\n private:\n  using BuildMatrixMetavars =\n      detail::BuildMatrixMetavars<FieldsTag, FixedSourcesTag, OperandTag,\n                                  OperatorAppliedToOperandTag, CoordsTag,\n                                  ArraySectionIdTag>;\n  static_assert(\n      std::is_same_v<FieldsTag, OperandTag>,\n      \"The operand and the fields tags must be the same. This is just so that \"\n      \"at the end of the algorithm the operator is applied to the solution \"\n      \"fields. This restriction can be lifted if needed by copying the fields \"\n      \"into the operand and back.\");\n\n public:\n  template <typename Metavariables>\n  using component_list =\n      tmpl::list<BuildMatrixSingleton<Metavariables, BuildMatrixMetavars>>;\n\n  template <typename ApplyOperatorActions>\n  using actions =\n      tmpl::list<CollectTotalNumPoints<BuildMatrixMetavars>,\n                 // PrepareBuildMatrix is called on reduction broadcast\n                 SetUnitVector<BuildMatrixMetavars>, ApplyOperatorActions,\n                 StoreMatrixColumn<BuildMatrixMetavars>,\n                 // Algorithm iterates until matrix is complete, then proceeds\n                 // below\n                 AssembleFullMatrix<BuildMatrixMetavars>,\n                 // Apply operator to the solution. Note that FieldsTag and\n                 // OperandTag must be the same for this (see assert above).\n                 // Note also that this operator application is not needed in\n                 // most cases, as the linear problem is solved exactly and\n                 // therefore Ax = b is set in 'StoreSolution'. However, this\n                 // operator application is needed if the operator changes\n                 // between nonlinear solver iterations but we skip the reset,\n                 // so the matrix represents the old operator. It's just one\n                 // more operator application, so we keep it always enabled for\n                 // now.\n                 ApplyOperatorActions,\n                 ::Actions::Label<typename BuildMatrixMetavars::end_label>>;\n\n  using amr_projectors = tmpl::list<ProjectBuildMatrix<BuildMatrixMetavars>>;\n\n  /// Add to the register phase to enable observations\n  using register_actions = tmpl::list<observers::Actions::RegisterWithObservers<\n      detail::RegisterWithVolumeObserver<ArraySectionIdTag>>>;\n\n  /// Add to the action list to reset the matrix\n  using reset_actions = tmpl::list<ResetBuiltMatrix<BuildMatrixMetavars>>;\n};\n\n}  // namespace Actions\n}  // namespace LinearSolver\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BuildMatrix.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BuildMatrix.hpp\n  MakeIdentityIfSkipped.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Actions/MakeIdentityIfSkipped.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename... Tags>\nstruct TaggedTuple;\n}  // namespace tuples\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace LinearSolver {\n/// Actions applicable to any parallel linear solver\nnamespace Actions {\n\n/*!\n * \\brief Make the iterative linear solve the identity operation on the source\n * vector if no iterations were performed at all. Useful for disabling a\n * preconditioner by setting its number of iterations to zero.\n *\n * When the linear solve is skipped, i.e. when it performs no iterations because\n * its number of iterations is set to zero, this action assumes \\f$A=1\\f$ so\n * \\f$Ax=b\\f$ solves to \\f$x=b\\f$. This is useful when the solver is used as\n * preconditioner, because then we can disable preconditioning by just not\n * iterating the preconditioner, i.e. by setting its number of iterations to\n * zero in the input file.\n *\n * To use this action, insert it into the action list just after iterating the\n * linear solver, i.e. after its `solve` action list:\n *\n * \\snippet LinearSolverAlgorithmTestHelpers.hpp make_identity_if_skipped\n *\n * This action will set the `LinearSolverType::fields_tag` to the\n * `LinearSolverType::source_tag` whenever the linear solver has converged with\n * the reason `Convergence::Reason::NumIterations` or\n * `Convergence::Reason::MaxIterations` without actually having performed any\n * iterations.\n *\n * To run additional actions after this action has triggered, i.e. when the\n * linear solver is skipped, place them after this action and follow them by an\n * `::Actions::Label<ProceedLabel>`, where `ProceedLabel` is a type used for\n * identification. Pass the `ProceedLabel` as the second template parameter to\n * this action. Then, the actions between this action and the label will run\n * only when the linear solver is skipped. This is useful to set DataBox tags\n * that are usually updated by the linear solver.\n *\n * \\par Details:\n * The standard behaviour of most linear solvers (i.e. when _not_ using this\n * action) is to keep the fields at their initial guess \\f$x=x_0\\f$ when it\n * performs no iterations. That behaviour is not handled well when the solver is\n * used as preconditioner and its parent always makes it start at \\f$x_0=0\\f$.\n * This is a reasonable choice to start the preconditioner but it also means\n * that the preconditioner doesn't reduce to the identity when it performs no\n * iterations. Using this action means not iterating the preconditioner at all\n * essentially disables preconditioning, switching the parent solver to an\n * unpreconditioned solve with some runtime and memory overhead associated with\n * initializing the preconditioner.\n */\ntemplate <typename LinearSolverType, typename ProceedLabel = void>\nstruct MakeIdentityIfSkipped {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    constexpr size_t this_action_index =\n        tmpl::index_of<ActionList, MakeIdentityIfSkipped>::value;\n    constexpr size_t proceed_action_index =\n        tmpl::conditional_t<\n            std::is_same_v<ProceedLabel, void>, tmpl::size_t<this_action_index>,\n            tmpl::index_of<ActionList, ::Actions::Label<ProceedLabel>>>::value +\n        1;\n\n    const auto& has_converged = get<Convergence::Tags::HasConverged<\n        typename LinearSolverType::options_group>>(box);\n    if (has_converged and\n        (has_converged.reason() == Convergence::Reason::NumIterations or\n         has_converged.reason() == Convergence::Reason::MaxIterations) and\n        has_converged.num_iterations() == 0) {\n      db::mutate<typename LinearSolverType::fields_tag>(\n          [](const auto fields, const auto& source) { *fields = source; },\n          make_not_null(&box), get<typename LinearSolverType::source_tag>(box));\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    return {Parallel::AlgorithmExecution::Continue, proceed_action_index};\n  }\n};\n\nnamespace detail {\ntemplate <typename Tag>\nstruct ProceedLabel {};\n}  // namespace detail\n\n/// Run `MakeIdentityIfSkipped`, and also run the `BuildOperatorActions` if\n/// the linear solver is skipped. See `MakeIdentityIfSkipped` for details.\ntemplate <typename LinearSolverType,\n          typename BuildOperatorActions = tmpl::list<>,\n          typename Label = typename LinearSolverType::options_group>\nusing make_identity_if_skipped = tmpl::conditional_t<\n    std::is_same_v<BuildOperatorActions, tmpl::list<>>,\n    MakeIdentityIfSkipped<LinearSolverType>,\n    tmpl::list<\n        MakeIdentityIfSkipped<LinearSolverType, detail::ProceedLabel<Label>>,\n        BuildOperatorActions, ::Actions::Label<detail::ProceedLabel<Label>>>>;\n\n}  // namespace Actions\n}  // namespace LinearSolver\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/AsynchronousSolvers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ElementActions.hpp\n  )\n\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/AsynchronousSolvers/ElementActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Protocols/ReductionDataFormatter.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/Gmres.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\nnamespace LinearSolver::async_solvers {\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag,\n          typename Label, typename ArraySectionIdTag,\n          bool ObserveInitialResidual>\nstruct CompleteStep;\n}  // namespace LinearSolver::async_solvers\n/// \\endcond\n\n/// Functionality shared between parallel linear solvers that have no global\n/// synchronization points\nnamespace LinearSolver::async_solvers {\n\nusing reduction_data = Parallel::ReductionData<\n    // Iteration\n    Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n    // Residual\n    Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Sqrt<>>>;\n\ntemplate <typename OptionsGroup>\nstruct ResidualReductionFormatter\n    : tt::ConformsTo<observers::protocols::ReductionDataFormatter> {\n  using reduction_data = async_solvers::reduction_data;\n  ResidualReductionFormatter() = default;\n  ResidualReductionFormatter(std::string local_section_observation_key)\n      : section_observation_key(std::move(local_section_observation_key)) {}\n  std::string operator()(const size_t iteration_id,\n                         const double residual) const {\n    if (iteration_id == 0) {\n      return pretty_type::name<OptionsGroup>() + section_observation_key +\n             \" initialized with residual: \" + get_output(residual) + \"\\n\";\n    } else {\n      return pretty_type::name<OptionsGroup>() + section_observation_key + \"(\" +\n             get_output(iteration_id) +\n             \") iteration complete. Remaining residual: \" +\n             get_output(residual) + \"\\n\";\n    }\n  }\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | section_observation_key; }\n  std::string section_observation_key{};\n};\n\ntemplate <typename OptionsGroup, typename ParallelComponent,\n          typename Metavariables, typename ArrayIndex>\nvoid contribute_to_residual_observation(\n    const size_t iteration_id, const double residual_magnitude_square,\n    Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,\n    const std::string& section_observation_key) {\n  auto& local_observer = *Parallel::local_branch(\n      Parallel::get_parallel_component<observers::Observer<Metavariables>>(\n          cache));\n  auto formatter =\n      UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(cache) >=\n               ::Verbosity::Quiet)\n          ? std::make_optional(ResidualReductionFormatter<OptionsGroup>{\n                section_observation_key})\n          : std::nullopt;\n  Parallel::simple_action<observers::Actions::ContributeReductionData>(\n      local_observer,\n      observers::ObservationId(\n          iteration_id,\n          pretty_type::get_name<OptionsGroup>() + section_observation_key),\n      Parallel::make_array_component_id<ParallelComponent>(array_index),\n      std::string{\"/\" + pretty_type::name<OptionsGroup>() +\n                  section_observation_key + \"Residuals\"},\n      std::vector<std::string>{\"Iteration\", \"Residual\"},\n      reduction_data{iteration_id, residual_magnitude_square},\n      std::move(formatter));\n  if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(cache) >=\n               ::Verbosity::Debug)) {\n    if (iteration_id == 0) {\n      Parallel::printf(\n          \"%s %s initialized with local residual: %e\\n\",\n          get_output(array_index),\n          pretty_type::name<OptionsGroup>() + section_observation_key,\n          sqrt(residual_magnitude_square));\n    } else {\n      Parallel::printf(\n          \"%s %s(%zu) iteration complete. Remaining local residual: %e\\n\",\n          get_output(array_index),\n          pretty_type::name<OptionsGroup>() + section_observation_key,\n          iteration_id, sqrt(residual_magnitude_square));\n    }\n  }\n}\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {\n private:\n  using fields_tag = FieldsTag;\n  using operator_applied_to_fields_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>;\n  using source_tag = SourceTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n  using residual_magnitude_square_tag =\n      LinearSolver::Tags::MagnitudeSquare<residual_tag>;\n\n public:  // Iterable action\n  using const_global_cache_tags =\n      tmpl::list<Convergence::Tags::Iterations<OptionsGroup>>;\n\n  using simple_tags = tmpl::list<Convergence::Tags::IterationId<OptionsGroup>,\n                                 Convergence::Tags::HasConverged<OptionsGroup>,\n                                 operator_applied_to_fields_tag>;\n  using compute_tags =\n      tmpl::list<LinearSolver::Tags::ResidualCompute<fields_tag, source_tag>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // The `PrepareSolve` action populates these tags with initial\n    // values, except for `operator_applied_to_fields_tag` which is\n    // expected to be updated in every iteration of the algorithm\n    Initialization::mutate_assign<\n        tmpl::list<Convergence::Tags::IterationId<OptionsGroup>>>(\n        make_not_null(&box), std::numeric_limits<size_t>::max());\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // amr::protocols::Projector\n  using argument_tags = tmpl::list<>;\n  using return_tags = simple_tags;\n\n  // p-refinement\n  template <size_t Dim>\n  static void apply(\n      const gsl::not_null<size_t*> /*iteration_id*/,\n      const gsl::not_null<Convergence::HasConverged*> /*has_converged*/,\n      const gsl::not_null<typename operator_applied_to_fields_tag::\n                              type*> /*operator_applied_to_fields*/,\n      const std::pair<Mesh<Dim>, Element<Dim>>& /*old_mesh_and_element*/) {}\n\n  // h-refinement\n  template <typename... ParentTags>\n  static void apply(\n      const gsl::not_null<size_t*> iteration_id,\n      const gsl::not_null<Convergence::HasConverged*> has_converged,\n      const gsl::not_null<typename operator_applied_to_fields_tag::\n                              type*> /*operator_applied_to_fields*/,\n      const tuples::TaggedTuple<ParentTags...>& parent_items) {\n    *iteration_id =\n        get<Convergence::Tags::IterationId<OptionsGroup>>(parent_items);\n    *has_converged =\n        get<Convergence::Tags::HasConverged<OptionsGroup>>(parent_items);\n  }\n\n  // h-coarsening\n  template <size_t Dim, typename... ChildTags>\n  static void apply(\n      const gsl::not_null<size_t*> iteration_id,\n      const gsl::not_null<Convergence::HasConverged*> has_converged,\n      const gsl::not_null<typename operator_applied_to_fields_tag::\n                              type*> /*operator_applied_to_fields*/,\n      const std::unordered_map<\n          ElementId<Dim>, tuples::TaggedTuple<ChildTags...>>& children_items) {\n    *iteration_id = get<Convergence::Tags::IterationId<OptionsGroup>>(\n        children_items.begin()->second);\n    *has_converged = get<Convergence::Tags::HasConverged<OptionsGroup>>(\n        children_items.begin()->second);\n  }\n};\n\ntemplate <typename OptionsGroup, typename ArraySectionIdTag = void>\nstruct RegisterObservers {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& box,\n                const ArrayIndex& /*array_index*/) {\n    // Get the observation key, or \"Unused\" if the element does not belong\n    // to a section with this tag. In the latter case, no observations will\n    // ever be contributed.\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    ASSERT(section_observation_key != \"Unused\",\n           \"The identifier 'Unused' is reserved to indicate that no \"\n           \"observations with this key will be contributed. Use a different \"\n           \"key, or change the identifier 'Unused' to something else.\");\n    return {\n        observers::TypeOfObservation::Reduction,\n        observers::ObservationKey{pretty_type::get_name<OptionsGroup>() +\n                                  section_observation_key.value_or(\"Unused\")}};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag,\n          typename ArraySectionIdTag = void>\nusing RegisterElement = observers::Actions::RegisterWithObservers<\n    RegisterObservers<OptionsGroup, ArraySectionIdTag>>;\n\n/*!\n * \\brief Prepare the asynchronous linear solver for a solve\n *\n * This action resets the asynchronous linear solver to its initial state, and\n * optionally observes the initial residual. If the initial residual should be\n * observed, both the `SourceTag` as well as the\n * `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, FieldsTag>`\n * must be up-to-date at the time this action is invoked.\n *\n * This action also provides an anchor point in the action list for looping the\n * linear solver, in the sense that the algorithm jumps back to the action\n * immediately following this one when a step is complete and the solver hasn't\n * yet converged (see `LinearSolver::async_solvers::CompleteStep`).\n *\n * \\tparam FieldsTag The data `x` in the linear equation `Ax = b` to be solved.\n * Should hold the initial guess `x_0` at this point in the algorithm.\n * \\tparam OptionsGroup An options group identifying the linear solver\n * \\tparam SourceTag The data `b` in `Ax = b`\n * \\tparam Label An optional compile-time label for the solver to distinguish\n * different solves with the same solver in the action list\n * \\tparam ArraySectionIdTag Observe the residual norm separately for each\n * array section identified by this tag (see `Parallel::Section`). Set to `void`\n * to observe the residual norm over all elements of the array (default). The\n * section only affects observations of residuals and has no effect on the\n * solver algorithm.\n * \\tparam ObserveInitialResidual Whether or not to observe the initial residual\n * `b - A x_0` (default: `true`). Disable when `b` or `A x_0` are not yet\n * available at the preparation stage.\n */\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag,\n          typename Label = OptionsGroup, typename ArraySectionIdTag = void,\n          bool ObserveInitialResidual = true>\nstruct PrepareSolve {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    constexpr size_t iteration_id = 0;\n\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s: Prepare solve\\n\", get_output(array_index),\n                       pretty_type::name<OptionsGroup>());\n    }\n\n    db::mutate<Convergence::Tags::IterationId<OptionsGroup>,\n               Convergence::Tags::HasConverged<OptionsGroup>>(\n        [](const gsl::not_null<size_t*> local_iteration_id,\n           const gsl::not_null<Convergence::HasConverged*> has_converged,\n           const size_t num_iterations) {\n          *local_iteration_id = iteration_id;\n          *has_converged =\n              Convergence::HasConverged{num_iterations, iteration_id};\n        },\n        make_not_null(&box),\n        get<Convergence::Tags::Iterations<OptionsGroup>>(box));\n\n    if constexpr (ObserveInitialResidual) {\n      // Observe the initial residual even if no steps are going to be performed\n      const std::optional<std::string> section_observation_key =\n          observers::get_section_observation_key<ArraySectionIdTag>(box);\n      if (section_observation_key.has_value()) {\n        const auto& residual = get<residual_tag>(box);\n        const double residual_magnitude_square = magnitude_square(residual);\n        contribute_to_residual_observation<OptionsGroup, ParallelComponent>(\n            iteration_id, residual_magnitude_square, cache, array_index,\n            *section_observation_key);\n      }\n    }\n\n    // Skip steps entirely if the solve has already converged\n    constexpr size_t step_end_index = tmpl::index_of<\n        ActionList,\n        CompleteStep<FieldsTag, OptionsGroup, SourceTag, Label,\n                     ArraySectionIdTag, ObserveInitialResidual>>::value;\n    constexpr size_t this_action_index =\n        tmpl::index_of<ActionList, PrepareSolve>::value;\n    return {Parallel::AlgorithmExecution::Continue,\n            get<Convergence::Tags::HasConverged<OptionsGroup>>(box)\n                ? (step_end_index + 1)\n                : (this_action_index + 1)};\n  }\n};\n\n/*!\n * \\brief Complete a step of the asynchronous linear solver\n *\n * This action prepares the next step of the asynchronous linear solver, and\n * observes the residual. To observe the correct residual, make sure the\n * `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, FieldsTag>` is\n * up-to-date at the time this action is invoked.\n *\n * This action checks if the algorithm has converged, i.e. it has completed the\n * requested number of steps. If it hasn't, the algorithm jumps back to the\n * action immediately following the `LinearSolver::async_solvers::PrepareSolve`\n * to perform another iteration. Make sure both actions use the same template\n * parameters.\n *\n * \\tparam FieldsTag The data `x` in the linear equation `Ax = b` to be solved.\n * \\tparam OptionsGroup An options group identifying the linear solver\n * \\tparam SourceTag The data `b` in `Ax = b`\n * \\tparam Label An optional compile-time label for the solver to distinguish\n * different solves with the same solver in the action list\n * \\tparam ArraySectionIdTag Observe the residual norm separately for each\n * array section identified by this tag (see `Parallel::Section`). Set to `void`\n * to observe the residual norm over all elements of the array (default). The\n * section only affects observations of residuals and has no effect on the\n * solver algorithm.\n * \\tparam ObserveInitialResidual Whether or not to observe the _initial_\n * residual `b - A x_0`. This parameter should match the one passed to\n * `PrepareSolve`.\n */\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag,\n          typename Label = OptionsGroup, typename ArraySectionIdTag = void,\n          bool ObserveInitialResidual = true>\nstruct CompleteStep {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Prepare for next iteration\n    db::mutate<Convergence::Tags::IterationId<OptionsGroup>,\n               Convergence::Tags::HasConverged<OptionsGroup>>(\n        [](const gsl::not_null<size_t*> iteration_id,\n           const gsl::not_null<Convergence::HasConverged*> has_converged,\n           const size_t num_iterations) {\n          ++(*iteration_id);\n          *has_converged =\n              Convergence::HasConverged{num_iterations, *iteration_id};\n        },\n        make_not_null(&box),\n        get<Convergence::Tags::Iterations<OptionsGroup>>(box));\n\n    // Observe element-local residual magnitude\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (section_observation_key.has_value()) {\n      const size_t completed_iterations =\n          get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n      const auto& residual = get<residual_tag>(box);\n      const double residual_magnitude_square = magnitude_square(residual);\n      contribute_to_residual_observation<OptionsGroup, ParallelComponent>(\n          completed_iterations, residual_magnitude_square, cache, array_index,\n          *section_observation_key);\n    }\n\n    // Repeat steps until the solve has converged\n    constexpr size_t step_begin_index =\n        tmpl::index_of<\n            ActionList,\n            PrepareSolve<FieldsTag, OptionsGroup, SourceTag, Label,\n                         ArraySectionIdTag, ObserveInitialResidual>>::value +\n        1;\n    constexpr size_t this_action_index =\n        tmpl::index_of<ActionList, CompleteStep>::value;\n    return {Parallel::AlgorithmExecution::Continue,\n            get<Convergence::Tags::HasConverged<OptionsGroup>>(box)\n                ? (this_action_index + 1)\n                : step_begin_index};\n  }\n};\n\n}  // namespace LinearSolver::async_solvers\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ParallelLinearSolver)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Observe.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Convergence\n  Initialization\n  LinearSolver\n  ParallelAmr\n  Serialization\n  SystemUtilities\n  PUBLIC\n  DataStructures\n  DomainStructure\n  H5\n  Logging\n  Observer\n  Parallel\n  Printf\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(AsynchronousSolvers)\nadd_subdirectory(ConjugateGradient)\nadd_subdirectory(Gmres)\nadd_subdirectory(Multigrid)\nadd_subdirectory(Richardson)\nadd_subdirectory(Schwarz)\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/ConjugateGradient/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ConjugateGradient.hpp\n  ElementActions.hpp\n  InitializeElement.hpp\n  ResidualMonitor.hpp\n  ResidualMonitorActions.hpp\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/ConjugateGradient/ConjugateGradient.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/InitializeElement.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/ResidualMonitor.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Items related to the conjugate gradient linear solver\n///\n/// \\see `LinearSolver::cg::ConjugateGradient`\nnamespace LinearSolver::cg {\n\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief A conjugate gradient solver for linear systems of equations \\f$Ax=b\\f$\n * where the operator \\f$A\\f$ is symmetric.\n *\n * \\details The only operation we need to supply to the algorithm is the\n * result of the operation \\f$A(p)\\f$ (see \\ref LinearSolverGroup) that in the\n * case of the conjugate gradient algorithm must be symmetric. Each step of the\n * algorithm expects that \\f$A(p)\\f$ is computed and stored in the DataBox as\n * %db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo,\n * db::add_tag_prefix<LinearSolver::Tags::Operand, FieldsTag>>. To perform a\n * solve, add the `solve` action list to an array parallel component. Pass the\n * actions that compute \\f$A(q)\\f$, as well as any further actions you wish to\n * run in each step of the algorithm, as the first template parameter to\n * `solve`. If you add the `solve` action list multiple times, use the second\n * template parameter to label each solve with a different type.\n *\n * Note that the operand \\f$p\\f$ for which \\f$A(p)\\f$ needs to be computed is\n * not the field \\f$x\\f$ we are solving for but\n * `db::add_tag_prefix<LinearSolver::Tags::Operand, FieldsTag>`. This field is\n * initially set to the residual \\f$r = b - A(x_0)\\f$ where \\f$x_0\\f$ is the\n * initial value of the `FieldsTag`.\n *\n * When the algorithm step is performed after the operator action \\f$A(p)\\f$ has\n * been computed and stored in the DataBox, the conjugate gradient algorithm\n * implemented here will converge the field \\f$x\\f$ towards the solution and\n * update the operand \\f$p\\f$ in the process. This requires two reductions over\n * all elements that are received by a `ResidualMonitor` singleton parallel\n * component, processed, and then broadcast back to all elements. The actions\n * are implemented in the `cg::detail` namespace and constitute the full\n * algorithm in the following order:\n * 1. `PerformStep` (on elements): Compute the inner product \\f$\\langle p,\n * A(p)\\rangle\\f$ and reduce.\n * 2. `ComputeAlpha` (on `ResidualMonitor`): Compute\n * \\f$\\alpha=\\frac{r^2}{\\langle p, A(p)\\rangle}\\f$ and broadcast.\n * 3. `UpdateFieldValues` (on elements): Update \\f$x\\f$ and \\f$r\\f$, then\n * compute the inner product \\f$\\langle r, r\\rangle\\f$ and reduce to find the\n * new \\f$r^2\\f$.\n * 4. `UpdateResidual` (on `ResidualMonitor`): Store the new \\f$r^2\\f$ and\n * broadcast the ratio of the new and old \\f$r^2\\f$, as well as a termination\n * flag if the `Convergence::Tags::Criteria` are met.\n * 5. `UpdateOperand` (on elements): Update \\f$p\\f$.\n *\n * \\see Gmres for a linear solver that can invert nonsymmetric operators\n * \\f$A\\f$.\n */\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup,\n          typename SourceTag =\n              db::add_tag_prefix<::Tags::FixedSource, FieldsTag>>\nstruct ConjugateGradient {\n  using fields_tag = FieldsTag;\n  using options_group = OptionsGroup;\n  using source_tag = SourceTag;\n\n  /// Apply the linear operator to this tag in each iteration\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n\n  /*!\n   * \\brief The parallel components used by the conjugate gradient linear solver\n   */\n  using component_list = tmpl::list<\n      detail::ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>;\n\n  using initialize_element = detail::InitializeElement<FieldsTag, OptionsGroup>;\n\n  using register_element = tmpl::list<>;\n\n  template <typename ApplyOperatorActions, typename Label = OptionsGroup>\n  using solve = tmpl::list<\n      detail::PrepareSolve<FieldsTag, OptionsGroup, Label, SourceTag>,\n      detail::InitializeHasConverged<FieldsTag, OptionsGroup, Label>,\n      ApplyOperatorActions, detail::PerformStep<FieldsTag, OptionsGroup, Label>,\n      detail::UpdateFieldValues<FieldsTag, OptionsGroup, Label>,\n      detail::UpdateOperand<FieldsTag, OptionsGroup, Label>>;\n};\n\n}  // namespace LinearSolver::cg\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/ConjugateGradient/ElementActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/ResidualMonitorActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/Tags/InboxTags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Convergence {\nstruct HasConverged;\n}  // namespace Convergence\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\nnamespace LinearSolver::cg::detail {\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup>\nstruct ResidualMonitor;\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label>\nstruct UpdateOperand;\n}  // namespace LinearSolver::cg::detail\n/// \\endcond\n\nnamespace LinearSolver::cg::detail {\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename SourceTag>\nstruct PrepareSolve {\n private:\n  using fields_tag = FieldsTag;\n  using source_tag = SourceTag;\n  using operator_applied_to_fields_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Convergence::Tags::IterationId<OptionsGroup>, operand_tag,\n               residual_tag>(\n        [](const gsl::not_null<size_t*> iteration_id, const auto operand,\n           const auto residual, const auto& source,\n           const auto& operator_applied_to_fields) {\n          *iteration_id = 0;\n          *operand = source - operator_applied_to_fields;\n          *residual = *operand;\n        },\n        make_not_null(&box), get<source_tag>(box),\n        get<operator_applied_to_fields_tag>(box));\n\n    // Perform global reduction to compute initial residual magnitude square for\n    // residual monitor\n    const auto& residual = get<residual_tag>(box);\n    Parallel::contribute_to_reduction<\n        InitializeResidual<FieldsTag, OptionsGroup, ParallelComponent>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<double, funcl::Plus<>>>{\n            magnitude_square(residual)},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        Parallel::get_parallel_component<\n            ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label>\nstruct InitializeHasConverged {\n  using inbox_tags = tmpl::list<Tags::InitialHasConverged<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox = get<Tags::InitialHasConverged<OptionsGroup>>(inboxes);\n    const auto& iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    if (inbox.find(iteration_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    auto has_converged = std::move(inbox.extract(iteration_id).mapped());\n\n    db::mutate<Convergence::Tags::HasConverged<OptionsGroup>>(\n        [&has_converged](const gsl::not_null<Convergence::HasConverged*>\n                             local_has_converged) {\n          *local_has_converged = std::move(has_converged);\n        },\n        make_not_null(&box));\n\n    // Skip steps entirely if the solve has already converged\n    constexpr size_t step_end_index =\n        tmpl::index_of<ActionList,\n                       UpdateOperand<FieldsTag, OptionsGroup, Label>>::value;\n    constexpr size_t this_action_index =\n        tmpl::index_of<ActionList, InitializeHasConverged>::value;\n    return {Parallel::AlgorithmExecution::Continue,\n            has_converged ? (step_end_index + 1) : (this_action_index + 1)};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label>\nstruct PerformStep {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    using fields_tag = FieldsTag;\n    using operand_tag =\n        db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n    using operator_tag =\n        db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, operand_tag>;\n\n    // At this point Ap must have been computed in a previous action\n    // We compute the inner product <p,p> w.r.t A. This requires a global\n    // reduction.\n    const double local_conj_grad_inner_product =\n        inner_product(get<operand_tag>(box), get<operator_tag>(box));\n\n    Parallel::contribute_to_reduction<\n        ComputeAlpha<FieldsTag, OptionsGroup, ParallelComponent>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n            Parallel::ReductionDatum<double, funcl::Plus<>>>{\n            get<Convergence::Tags::IterationId<OptionsGroup>>(box),\n            local_conj_grad_inner_product},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        Parallel::get_parallel_component<\n            ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label>\nstruct UpdateFieldValues {\n private:\n  using fields_tag = FieldsTag;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using operator_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, operand_tag>;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  using inbox_tags = tmpl::list<Tags::Alpha<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox = get<Tags::Alpha<OptionsGroup>>(inboxes);\n    const auto& iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    if (inbox.find(iteration_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    const double alpha = std::move(inbox.extract(iteration_id).mapped());\n\n    db::mutate<residual_tag, fields_tag>(\n        [alpha](const auto residual, const auto fields, const auto& operand,\n                const auto& operator_applied_to_operand) {\n          *fields += alpha * operand;\n          *residual -= alpha * operator_applied_to_operand;\n        },\n        make_not_null(&box), get<operand_tag>(box), get<operator_tag>(box));\n\n    // Compute new residual norm in a second global reduction\n    const auto& residual = get<residual_tag>(box);\n    const double local_residual_magnitude_square = magnitude_square(residual);\n\n    Parallel::contribute_to_reduction<\n        UpdateResidual<FieldsTag, OptionsGroup, ParallelComponent>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n            Parallel::ReductionDatum<double, funcl::Plus<>>>{\n            get<Convergence::Tags::IterationId<OptionsGroup>>(box),\n            local_residual_magnitude_square},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        Parallel::get_parallel_component<\n            ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label>\nstruct UpdateOperand {\n private:\n  using fields_tag = FieldsTag;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  using inbox_tags =\n      tmpl::list<Tags::ResidualRatioAndHasConverged<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox =\n        get<Tags::ResidualRatioAndHasConverged<OptionsGroup>>(inboxes);\n    const auto& iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    if (inbox.find(iteration_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    auto received_data = std::move(inbox.extract(iteration_id).mapped());\n    const double res_ratio = get<0>(received_data);\n    auto& has_converged = get<1>(received_data);\n\n    db::mutate<operand_tag, Convergence::Tags::IterationId<OptionsGroup>,\n               Convergence::Tags::HasConverged<OptionsGroup>>(\n        [res_ratio, &has_converged](\n            const auto operand, const gsl::not_null<size_t*> local_iteration_id,\n            const gsl::not_null<Convergence::HasConverged*> local_has_converged,\n            const auto& residual) {\n          *operand = residual + res_ratio * *operand;\n          ++(*local_iteration_id);\n          *local_has_converged = std::move(has_converged);\n        },\n        make_not_null(&box), get<residual_tag>(box));\n\n    // Repeat steps until the solve has converged\n    constexpr size_t this_action_index =\n        tmpl::index_of<ActionList, UpdateOperand>::value;\n    constexpr size_t prepare_step_index =\n        tmpl::index_of<ActionList, InitializeHasConverged<\n                                       FieldsTag, OptionsGroup, Label>>::value +\n        1;\n    return {Parallel::AlgorithmExecution::Continue,\n            get<Convergence::Tags::HasConverged<OptionsGroup>>(box)\n                ? (this_action_index + 1)\n                : prepare_step_index};\n  }\n};\n\n}  // namespace LinearSolver::cg::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/ConjugateGradient/InitializeElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace LinearSolver::cg::detail {\n\ntemplate <typename FieldsTag, typename OptionsGroup>\nstruct InitializeElement {\n private:\n  using fields_tag = FieldsTag;\n  using operator_applied_to_fields_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using operator_applied_to_operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, operand_tag>;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  using simple_tags =\n      tmpl::list<Convergence::Tags::IterationId<OptionsGroup>,\n                 operator_applied_to_fields_tag, operand_tag,\n                 operator_applied_to_operand_tag, residual_tag,\n                 Convergence::Tags::HasConverged<OptionsGroup>>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // The `PrepareSolve` action populates these tags with initial\n    // values, except for `operator_applied_to_fields_tag` which is\n    // expected to be filled at that point and\n    // `operator_applied_to_operand_tag` which is expected to be updated\n    // in every iteration of the algorithm.\n    Initialization::mutate_assign<\n        tmpl::list<Convergence::Tags::IterationId<OptionsGroup>>>(\n        make_not_null(&box), std::numeric_limits<size_t>::max());\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace LinearSolver::cg::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/ConjugateGradient/ResidualMonitor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\nnamespace LinearSolver::cg::detail {\ntemplate <typename FieldsTag, typename OptionsGroup>\nstruct InitializeResidualMonitor;\n}  // namespace LinearSolver::cg::detail\n/// \\endcond\n\nnamespace LinearSolver::cg::detail {\n\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup>\nstruct ResidualMonitor {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>,\n                 Convergence::Tags::Criteria<OptionsGroup>>;\n  using metavariables = Metavariables;\n  // The actions in `ResidualMonitorActions.hpp` are invoked as simple actions\n  // on this component as the result of reductions from the actions in\n  // `ElementActions.hpp`. See `LinearSolver::cg::ConjugateGradient` for\n  // details.\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<InitializeResidualMonitor<FieldsTag, OptionsGroup>>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ResidualMonitor>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup>\nstruct InitializeResidualMonitor {\n private:\n  using fields_tag = FieldsTag;\n  using residual_square_tag = LinearSolver::Tags::MagnitudeSquare<\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>;\n  using initial_residual_magnitude_tag =\n      ::Tags::Initial<LinearSolver::Tags::Magnitude<\n          db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>>;\n\n public:\n  using simple_tags =\n      tmpl::list<residual_square_tag, initial_residual_magnitude_tag>;\n  using compute_tags = tmpl::list<>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // The `InitializeResidual` action populates these tags with initial values\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), std::numeric_limits<double>::signaling_NaN(),\n        std::numeric_limits<double>::signaling_NaN());\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n}  // namespace LinearSolver::cg::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/ConjugateGradient/ResidualMonitorActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/Tags/InboxTags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Observe.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace LinearSolver::cg::detail {\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename BroadcastTarget>\nstruct InitializeResidual {\n private:\n  using fields_tag = FieldsTag;\n  using residual_square_tag = LinearSolver::Tags::MagnitudeSquare<\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>;\n  using initial_residual_magnitude_tag =\n      ::Tags::Initial<LinearSolver::Tags::Magnitude<\n          db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>>;\n\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const double residual_square) {\n    constexpr size_t iteration_id = 0;\n    const double residual_magnitude = sqrt(residual_square);\n\n    db::mutate<residual_square_tag, initial_residual_magnitude_tag>(\n        [residual_square, residual_magnitude](\n            const gsl::not_null<double*> local_residual_square,\n            const gsl::not_null<double*> initial_residual_magnitude) {\n          *local_residual_square = residual_square;\n          *initial_residual_magnitude = residual_magnitude;\n        },\n        make_not_null(&box));\n\n    LinearSolver::observe_detail::contribute_to_reduction_observer<\n        OptionsGroup, ParallelComponent>(iteration_id, residual_magnitude,\n                                         cache);\n\n    // Determine whether the linear solver has converged\n    Convergence::HasConverged has_converged{\n        get<Convergence::Tags::Criteria<OptionsGroup>>(box), iteration_id,\n        residual_magnitude, residual_magnitude};\n\n    // Do some logging\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(cache) >=\n                 ::Verbosity::Quiet)) {\n      Parallel::printf(\"%s initialized with residual: %e\\n\",\n                       pretty_type::name<OptionsGroup>(), residual_magnitude);\n    }\n    if (UNLIKELY(has_converged and get<logging::Tags::Verbosity<OptionsGroup>>(\n                                       cache) >= ::Verbosity::Quiet)) {\n      Parallel::printf(\"%s has converged without any iterations: %s\\n\",\n                       pretty_type::name<OptionsGroup>(), has_converged);\n    }\n\n    Parallel::receive_data<Tags::InitialHasConverged<OptionsGroup>>(\n        Parallel::get_parallel_component<BroadcastTarget>(cache), iteration_id,\n        // NOLINTNEXTLINE(performance-move-const-arg)\n        std::move(has_converged));\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename BroadcastTarget>\nstruct ComputeAlpha {\n private:\n  using fields_tag = FieldsTag;\n  using residual_square_tag = LinearSolver::Tags::MagnitudeSquare<\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>;\n\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const size_t iteration_id,\n                    const double conj_grad_inner_product) {\n    Parallel::receive_data<Tags::Alpha<OptionsGroup>>(\n        Parallel::get_parallel_component<BroadcastTarget>(cache), iteration_id,\n        get<residual_square_tag>(box) / conj_grad_inner_product);\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename BroadcastTarget>\nstruct UpdateResidual {\n private:\n  using fields_tag = FieldsTag;\n  using residual_square_tag = LinearSolver::Tags::MagnitudeSquare<\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>;\n  using initial_residual_magnitude_tag =\n      ::Tags::Initial<LinearSolver::Tags::Magnitude<\n          db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>>;\n\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const size_t iteration_id, const double residual_square) {\n    // Compute the residual ratio before mutating the DataBox\n    const double res_ratio = residual_square / get<residual_square_tag>(box);\n\n    db::mutate<residual_square_tag>(\n        [residual_square](const gsl::not_null<double*> local_residual_square) {\n          *local_residual_square = residual_square;\n        },\n        make_not_null(&box));\n\n    // At this point, the iteration is complete. We proceed with observing,\n    // logging and checking convergence before broadcasting back to the\n    // elements.\n\n    const size_t completed_iterations = iteration_id + 1;\n    const double residual_magnitude = sqrt(residual_square);\n    LinearSolver::observe_detail::contribute_to_reduction_observer<\n        OptionsGroup, ParallelComponent>(completed_iterations,\n                                         residual_magnitude, cache);\n\n    // Determine whether the linear solver has converged\n    Convergence::HasConverged has_converged{\n        get<Convergence::Tags::Criteria<OptionsGroup>>(box),\n        completed_iterations, residual_magnitude,\n        get<initial_residual_magnitude_tag>(box)};\n\n    // Do some logging\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(cache) >=\n                 ::Verbosity::Quiet)) {\n      Parallel::printf(\"%s(%zu) iteration complete. Remaining residual: %e\\n\",\n                       pretty_type::name<OptionsGroup>(), completed_iterations,\n                       residual_magnitude);\n    }\n    if (UNLIKELY(has_converged and get<logging::Tags::Verbosity<OptionsGroup>>(\n                                       cache) >= ::Verbosity::Quiet)) {\n      Parallel::printf(\"%s has converged in %zu iterations: %s\\n\",\n                       pretty_type::name<OptionsGroup>(), completed_iterations,\n                       has_converged);\n    }\n\n    Parallel::receive_data<Tags::ResidualRatioAndHasConverged<OptionsGroup>>(\n        Parallel::get_parallel_component<BroadcastTarget>(cache), iteration_id,\n        // NOLINTNEXTLINE(performance-move-const-arg)\n        std::make_tuple(res_ratio, std::move(has_converged)));\n  }\n};\n\n}  // namespace LinearSolver::cg::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/ConjugateGradient/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InboxTags.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/ConjugateGradient/Tags/InboxTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <tuple>\n\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::cg::detail::Tags {\n\ntemplate <typename OptionsGroup>\nstruct InitialHasConverged\n    : Parallel::InboxInserters::Value<InitialHasConverged<OptionsGroup>> {\n  using temporal_id = size_t;\n  using type = std::map<temporal_id, Convergence::HasConverged>;\n};\n\ntemplate <typename OptionsGroup>\nstruct Alpha : Parallel::InboxInserters::Value<Alpha<OptionsGroup>> {\n  using temporal_id = size_t;\n  using type = std::map<temporal_id, double>;\n};\n\ntemplate <typename OptionsGroup>\nstruct ResidualRatioAndHasConverged\n    : Parallel::InboxInserters::Value<\n          ResidualRatioAndHasConverged<OptionsGroup>> {\n  using temporal_id = size_t;\n  using type =\n      std::map<temporal_id, std::tuple<double, Convergence::HasConverged>>;\n};\n\n}  // namespace LinearSolver::cg::detail::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Gmres/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ElementActions.hpp\n  Gmres.hpp\n  InitializeElement.hpp\n  ResidualMonitor.hpp\n  ResidualMonitorActions.hpp\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Gmres/ElementActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GetSection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/ResidualMonitorActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Tags/InboxTags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\nnamespace LinearSolver::gmres::detail {\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup>\nstruct ResidualMonitor;\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct PrepareStep;\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct NormalizeOperandAndUpdateField;\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct CompleteStep;\n}  // namespace LinearSolver::gmres::detail\n/// \\endcond\n\nnamespace LinearSolver::gmres::detail {\n\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename SourceTag, typename ArraySectionIdTag>\nstruct PrepareSolve {\n private:\n  using fields_tag = FieldsTag;\n  using initial_fields_tag = db::add_tag_prefix<::Tags::Initial, fields_tag>;\n  using source_tag = SourceTag;\n  using operator_applied_to_fields_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using basis_history_tag =\n      LinearSolver::Tags::KrylovSubspaceBasis<operand_tag>;\n\n public:\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Convergence::Tags::IterationId<OptionsGroup>>(\n        [](const gsl::not_null<size_t*> iteration_id) { *iteration_id = 0; },\n        make_not_null(&box));\n\n    // Skip the initial reduction on elements that are not part of the section\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n      }\n    }\n\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s: Prepare solve\\n\", get_output(array_index),\n                       pretty_type::name<OptionsGroup>());\n    }\n\n    db::mutate<operand_tag, initial_fields_tag, basis_history_tag>(\n        [](const auto operand, const auto initial_fields,\n           const auto basis_history, const auto& source,\n           const auto& operator_applied_to_fields, const auto& fields) {\n          *operand = source - operator_applied_to_fields;\n          *initial_fields = fields;\n          *basis_history = typename basis_history_tag::type{};\n        },\n        make_not_null(&box), get<source_tag>(box),\n        get<operator_applied_to_fields_tag>(box), get<fields_tag>(box));\n\n    auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<InitializeResidualMagnitude<\n        FieldsTag, OptionsGroup, ParallelComponent>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Sqrt<>>>{\n            magnitude_square(get<operand_tag>(box))},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        Parallel::get_parallel_component<\n            ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache),\n        make_not_null(&section));\n\n    if constexpr (Preconditioned) {\n      using preconditioned_operand_tag =\n          db::add_tag_prefix<LinearSolver::Tags::Preconditioned, operand_tag>;\n      using preconditioned_basis_history_tag =\n          LinearSolver::Tags::KrylovSubspaceBasis<preconditioned_operand_tag>;\n\n      db::mutate<preconditioned_basis_history_tag>(\n          [](const auto preconditioned_basis_history) {\n            *preconditioned_basis_history =\n                typename preconditioned_basis_history_tag::type{};\n          },\n          make_not_null(&box));\n    }\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct NormalizeInitialOperand {\n private:\n  using fields_tag = FieldsTag;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using basis_history_tag =\n      LinearSolver::Tags::KrylovSubspaceBasis<operand_tag>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n  using inbox_tags = tmpl::list<Tags::InitialOrthogonalization<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    auto& inbox = get<Tags::InitialOrthogonalization<OptionsGroup>>(inboxes);\n    if (inbox.find(iteration_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    auto received_data = std::move(inbox.extract(iteration_id).mapped());\n    const double residual_magnitude = get<0>(received_data);\n    auto& has_converged = get<1>(received_data);\n    db::mutate<Convergence::Tags::HasConverged<OptionsGroup>>(\n        [&has_converged](const gsl::not_null<Convergence::HasConverged*>\n                             local_has_converged) {\n          *local_has_converged = std::move(has_converged);\n        },\n        make_not_null(&box));\n\n    // Skip steps entirely if the solve has already converged\n    constexpr size_t step_end_index =\n        tmpl::index_of<ActionList,\n                       CompleteStep<FieldsTag, OptionsGroup, Preconditioned,\n                                    Label, ArraySectionIdTag>>::value;\n    if (get<Convergence::Tags::HasConverged<OptionsGroup>>(box)) {\n      return {Parallel::AlgorithmExecution::Continue, step_end_index + 1};\n    }\n\n    // Skip the solve entirely on elements that are not part of the section. To\n    // do so, we skip ahead to the `ApplyOperatorActions` action list between\n    // `PrepareStep` and `PerformStep`. Those actions need a chance to run even\n    // on elements that are not part of the section, because they may take part\n    // in preconditioning (see Multigrid preconditioner).\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n      }\n    }\n\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Normalize initial operand\\n\",\n                       get_output(array_index),\n                       pretty_type::name<OptionsGroup>(), iteration_id);\n    }\n\n    db::mutate<operand_tag, basis_history_tag>(\n        [residual_magnitude](const auto operand, const auto basis_history) {\n          *operand /= residual_magnitude;\n          basis_history->push_back(*operand);\n        },\n        make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct PrepareStep {\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Convergence::Tags::IterationId<OptionsGroup>>(\n        [](const gsl::not_null<size_t*> iteration_id) { ++(*iteration_id); },\n        make_not_null(&box));\n\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n      }\n    }\n\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\n          \"%s %s(%zu): Prepare step\\n\", get_output(array_index),\n          pretty_type::name<OptionsGroup>(),\n          db::get<Convergence::Tags::IterationId<OptionsGroup>>(box));\n    }\n\n    if constexpr (Preconditioned) {\n      using fields_tag = FieldsTag;\n      using operand_tag =\n          db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n      using preconditioned_operand_tag =\n          db::add_tag_prefix<LinearSolver::Tags::Preconditioned, operand_tag>;\n      using operator_tag = db::add_tag_prefix<\n          LinearSolver::Tags::OperatorAppliedTo,\n          std::conditional_t<Preconditioned, preconditioned_operand_tag,\n                             operand_tag>>;\n\n      db::mutate<preconditioned_operand_tag, operator_tag>(\n          [](const auto preconditioned_operand,\n             const auto operator_applied_to_operand, const auto& operand) {\n            // Start the preconditioner at zero because we have no reason to\n            // expect the remaining residual to have a particular form.\n            // Another possibility would be to start the preconditioner with an\n            // initial guess equal to its source, so not running the\n            // preconditioner at all means it is the identity, but that approach\n            // appears to yield worse results.\n            *preconditioned_operand =\n                make_with_value<typename preconditioned_operand_tag::type>(\n                    operand, 0.);\n            // Also set the operator applied to the initial preconditioned\n            // operand to zero because it's linear. This may save the\n            // preconditioner an operator application if it's optimized for\n            // this.\n            *operator_applied_to_operand =\n                make_with_value<typename operator_tag::type>(operand, 0.);\n          },\n          make_not_null(&box), get<operand_tag>(box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct PerformStep {\n private:\n  using fields_tag = FieldsTag;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using preconditioned_operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Preconditioned, operand_tag>;\n  using ValueType =\n      tt::get_complex_or_fundamental_type_t<typename fields_tag::type>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Skip to the end of the step on elements that are not part of the section\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        constexpr size_t step_end_index =\n            tmpl::index_of<ActionList,\n                           NormalizeOperandAndUpdateField<\n                               FieldsTag, OptionsGroup, Preconditioned, Label,\n                               ArraySectionIdTag>>::value;\n        return {Parallel::AlgorithmExecution::Continue, step_end_index};\n      }\n    }\n\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Perform step\\n\", get_output(array_index),\n                       pretty_type::name<OptionsGroup>(), iteration_id);\n    }\n\n    using operator_tag = db::add_tag_prefix<\n        LinearSolver::Tags::OperatorAppliedTo,\n        std::conditional_t<Preconditioned, preconditioned_operand_tag,\n                           operand_tag>>;\n    using orthogonalization_iteration_id_tag =\n        LinearSolver::Tags::Orthogonalization<\n            Convergence::Tags::IterationId<OptionsGroup>>;\n    using basis_history_tag =\n        LinearSolver::Tags::KrylovSubspaceBasis<operand_tag>;\n\n    if constexpr (Preconditioned) {\n      using preconditioned_basis_history_tag =\n          LinearSolver::Tags::KrylovSubspaceBasis<preconditioned_operand_tag>;\n\n      db::mutate<preconditioned_basis_history_tag>(\n          [](const auto preconditioned_basis_history,\n             const auto& preconditioned_operand) {\n            preconditioned_basis_history->push_back(preconditioned_operand);\n          },\n          make_not_null(&box), get<preconditioned_operand_tag>(box));\n    }\n\n    db::mutate<operand_tag, orthogonalization_iteration_id_tag>(\n        [](const auto operand,\n           const gsl::not_null<size_t*> orthogonalization_iteration_id,\n           const auto& operator_action) {\n          *operand = typename operand_tag::type(operator_action);\n          *orthogonalization_iteration_id = 0;\n        },\n        make_not_null(&box), get<operator_tag>(box));\n\n    auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<\n        StoreOrthogonalization<FieldsTag, OptionsGroup, ParallelComponent>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n            Parallel::ReductionDatum<ValueType, funcl::Plus<>>>{\n            get<Convergence::Tags::IterationId<OptionsGroup>>(box),\n            get<orthogonalization_iteration_id_tag>(box),\n            inner_product(get<basis_history_tag>(box)[0],\n                          get<operand_tag>(box))},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        Parallel::get_parallel_component<\n            ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache),\n        make_not_null(&section));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct OrthogonalizeOperand {\n private:\n  using fields_tag = FieldsTag;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using orthogonalization_iteration_id_tag =\n      LinearSolver::Tags::Orthogonalization<\n          Convergence::Tags::IterationId<OptionsGroup>>;\n  using basis_history_tag =\n      LinearSolver::Tags::KrylovSubspaceBasis<operand_tag>;\n  using ValueType =\n      tt::get_complex_or_fundamental_type_t<typename fields_tag::type>;\n\n public:\n  using inbox_tags =\n      tmpl::list<Tags::Orthogonalization<OptionsGroup, ValueType>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    auto& inbox =\n        get<Tags::Orthogonalization<OptionsGroup, ValueType>>(inboxes);\n    if (inbox.find(iteration_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    const ValueType orthogonalization =\n        std::move(inbox.extract(iteration_id).mapped());\n\n    db::mutate<operand_tag, orthogonalization_iteration_id_tag>(\n        [orthogonalization](\n            const auto operand,\n            const gsl::not_null<size_t*> orthogonalization_iteration_id,\n            const auto& basis_history) {\n          *operand -= orthogonalization *\n                      gsl::at(basis_history, *orthogonalization_iteration_id);\n          ++(*orthogonalization_iteration_id);\n        },\n        make_not_null(&box), get<basis_history_tag>(box));\n\n    const auto& next_orthogonalization_iteration_id =\n        get<orthogonalization_iteration_id_tag>(box);\n    const bool orthogonalization_complete =\n        next_orthogonalization_iteration_id == iteration_id;\n    const ValueType local_orthogonalization =\n        inner_product(orthogonalization_complete\n                          ? get<operand_tag>(box)\n                          : gsl::at(get<basis_history_tag>(box),\n                                    next_orthogonalization_iteration_id),\n                      get<operand_tag>(box));\n\n    auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<\n        StoreOrthogonalization<FieldsTag, OptionsGroup, ParallelComponent>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n            Parallel::ReductionDatum<ValueType, funcl::Plus<>>>{\n            iteration_id, next_orthogonalization_iteration_id,\n            local_orthogonalization},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        Parallel::get_parallel_component<\n            ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache),\n        make_not_null(&section));\n\n    // Repeat this action until orthogonalization is complete\n    constexpr size_t this_action_index =\n        tmpl::index_of<ActionList, OrthogonalizeOperand>::value;\n    return {Parallel::AlgorithmExecution::Continue,\n            orthogonalization_complete ? (this_action_index + 1)\n                                       : this_action_index};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct NormalizeOperandAndUpdateField {\n private:\n  using fields_tag = FieldsTag;\n  using initial_fields_tag = db::add_tag_prefix<::Tags::Initial, fields_tag>;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using preconditioned_operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Preconditioned, operand_tag>;\n  using basis_history_tag =\n      LinearSolver::Tags::KrylovSubspaceBasis<operand_tag>;\n  using preconditioned_basis_history_tag =\n      LinearSolver::Tags::KrylovSubspaceBasis<std::conditional_t<\n          Preconditioned, preconditioned_operand_tag, operand_tag>>;\n  using ValueType =\n      tt::get_complex_or_fundamental_type_t<typename fields_tag::type>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n  using inbox_tags =\n      tmpl::list<Tags::FinalOrthogonalization<OptionsGroup, ValueType>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    auto& inbox =\n        get<Tags::FinalOrthogonalization<OptionsGroup, ValueType>>(inboxes);\n    if (inbox.find(iteration_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Retrieve reduction data from inbox\n    auto received_data = std::move(inbox.extract(iteration_id).mapped());\n    const double normalization = get<0>(received_data);\n    const auto& minres = get<1>(received_data);\n    db::mutate<Convergence::Tags::HasConverged<OptionsGroup>>(\n        [&received_data](\n            const gsl::not_null<Convergence::HasConverged*> has_converged) {\n          *has_converged = std::move(get<2>(received_data));\n        },\n        make_not_null(&box));\n\n    // Elements that are not part of the section jump directly to the\n    // `ApplyOperationActions` for the next step.\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      constexpr size_t complete_step_index =\n          tmpl::index_of<ActionList,\n                         CompleteStep<FieldsTag, OptionsGroup, Preconditioned,\n                                      Label, ArraySectionIdTag>>::value;\n      constexpr size_t prepare_step_index =\n          tmpl::index_of<ActionList,\n                         PrepareStep<FieldsTag, OptionsGroup, Preconditioned,\n                                     Label, ArraySectionIdTag>>::value;\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        return {Parallel::AlgorithmExecution::Continue,\n                get<Convergence::Tags::HasConverged<OptionsGroup>>(box)\n                    ? (complete_step_index + 1)\n                    : prepare_step_index};\n      }\n    }\n\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Update field\\n\", get_output(array_index),\n                       pretty_type::name<OptionsGroup>(), iteration_id);\n    }\n\n    db::mutate<operand_tag, basis_history_tag, fields_tag>(\n        [normalization, &minres](const auto operand, const auto basis_history,\n                                 const auto field, const auto& initial_field,\n                                 const auto& preconditioned_basis_history,\n                                 const auto& has_converged) {\n          // Avoid an FPE if the new operand norm is exactly zero. In that case\n          // the problem is solved and the algorithm will terminate (see\n          // Proposition 9.3 in \\cite Saad2003). Since there will be no next\n          // iteration we don't need to normalize the operand.\n          if (LIKELY(normalization > 0.)) {\n            *operand /= normalization;\n          }\n          basis_history->push_back(*operand);\n          // Don't update the solution if an error occurred\n          if (not(has_converged and\n                  has_converged.reason() == Convergence::Reason::Error)) {\n            *field = initial_field;\n            for (size_t i = 0; i < minres.size(); i++) {\n              *field += minres[i] * gsl::at(preconditioned_basis_history, i);\n            }\n          }\n        },\n        make_not_null(&box), get<initial_fields_tag>(box),\n        get<preconditioned_basis_history_tag>(box),\n        get<Convergence::Tags::HasConverged<OptionsGroup>>(box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Jump back to `PrepareStep` to continue iterating if the algorithm has not yet\n// converged, or complete the solve and proceed with the action list if it has\n// converged. This is a separate action because the user has the opportunity to\n// insert actions before the step completes, for example to do observations.\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned,\n          typename Label, typename ArraySectionIdTag>\nstruct CompleteStep {\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\n          \"%s %s(%zu): Complete step\\n\", get_output(array_index),\n          pretty_type::name<OptionsGroup>(),\n          db::get<Convergence::Tags::IterationId<OptionsGroup>>(box));\n    }\n\n    // Repeat steps until the solve has converged\n    constexpr size_t prepare_step_index =\n        tmpl::index_of<ActionList,\n                       PrepareStep<FieldsTag, OptionsGroup, Preconditioned,\n                                   Label, ArraySectionIdTag>>::value;\n    constexpr size_t this_action_index =\n        tmpl::index_of<ActionList, CompleteStep>::value;\n    return {Parallel::AlgorithmExecution::Continue,\n            get<Convergence::Tags::HasConverged<OptionsGroup>>(box)\n                ? (this_action_index + 1)\n                : prepare_step_index};\n  }\n};\n\n}  // namespace LinearSolver::gmres::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/InitializeElement.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/ResidualMonitor.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Items related to the GMRES linear solver\n///\n/// \\see `LinearSolver::gmres::Gmres`\nnamespace LinearSolver::gmres {\n\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief A GMRES solver for nonsymmetric linear systems of equations\n * \\f$Ax=b\\f$.\n *\n * \\details The only operation we need to supply to the algorithm is the\n * result of the operation \\f$A(p)\\f$ (see \\ref LinearSolverGroup). Each step of\n * the algorithm expects that \\f$A(q)\\f$ is computed and stored in the DataBox\n * as `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, operand_tag>`.\n * To perform a solve, add the `solve` action list to an array parallel\n * component. Pass the actions that compute \\f$A(q)\\f$ as the first template\n * parameter to `solve`. The second template parameter allows you to pass any\n * further actions you wish to run in each step of the algorithm (such as\n * observations). If you add the `solve` action list multiple times, use the\n * third template parameter to label each solve with a different type.\n *\n * This linear solver supports preconditioning. Enable preconditioning by\n * setting the `Preconditioned` template parameter to `true`. If you do, run a\n * preconditioner (e.g. another parallel linear solver) in each step. The\n * preconditioner should approximately solve the linear problem \\f$A(q)=b\\f$\n * where \\f$q\\f$ is the `operand_tag` and \\f$b\\f$ is the\n * `preconditioner_source_tag`. Make sure the tag\n * `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, operand_tag>`\n * is updated with the preconditioned result in each step of the algorithm, i.e.\n * that it is \\f$A(q)\\f$ where \\f$q\\f$ is the preconditioner's approximate\n * solution to \\f$A(q)=b\\f$. The preconditioner always begins at an initial\n * guess of zero. It does not need to compute the operator applied to the\n * initial guess, since it's zero as well due to the linearity of the operator.\n *\n * Note that the operand \\f$q\\f$ for which \\f$A(q)\\f$ needs to be computed is\n * not the field \\f$x\\f$ we are solving for but\n * `db::add_tag_prefix<LinearSolver::Tags::Operand, FieldsTag>`. This field is\n * initially set to the residual \\f$q_0 = b - A(x_0)\\f$ where \\f$x_0\\f$ is the\n * initial value of the `FieldsTag`.\n *\n * When the algorithm step is performed after the operator action \\f$A(q)\\f$ has\n * been computed and stored in the DataBox, the GMRES algorithm implemented here\n * will converge the field \\f$x\\f$ towards the solution and update the operand\n * \\f$q\\f$ in the process. This requires reductions over all elements that are\n * received by a `ResidualMonitor` singleton parallel component, processed, and\n * then broadcast back to all elements. Since the reductions are performed to\n * find a vector that is orthogonal to those used in previous steps, the number\n * of reductions increases linearly with iterations. No restarting mechanism is\n * currently implemented. The actions are implemented in the `gmres::detail`\n * namespace and constitute the full algorithm in the following order:\n * 1. `PerformStep` (on elements): Start an Arnoldi orthogonalization by\n * computing the inner product between \\f$A(q)\\f$ and the first of the\n * previously determined set of orthogonal vectors.\n * 2. `StoreOrthogonalization` (on `ResidualMonitor`): Keep track of the\n * computed inner product in a Hessenberg matrix, then broadcast.\n * 3. `OrthogonalizeOperand` (on elements): Proceed with the Arnoldi\n * orthogonalization by computing inner products and reducing to\n * `StoreOrthogonalization` on the `ResidualMonitor` until the new orthogonal\n * vector is constructed. Then compute its magnitude and reduce.\n * 4. `StoreOrthogonalization` (on `ResidualMonitor`): Perform a QR\n * decomposition of the Hessenberg matrix to produce a residual vector.\n * Broadcast to `NormalizeOperandAndUpdateField` along with a termination\n * flag if the `Convergence::Tags::Criteria` are met.\n * 5. `NormalizeOperandAndUpdateField` (on elements): Set the operand \\f$q\\f$ as\n * the new orthogonal vector and normalize. Use the residual vector and the set\n * of orthogonal vectors to determine the solution \\f$x\\f$.\n *\n * \\par Array sections\n * This linear solver supports running over a subset of the elements in the\n * array parallel component (see `Parallel::Section`). Set the\n * `ArraySectionIdTag` template parameter to restrict the solver to elements in\n * that section. Only a single section must be associated with the\n * `ArraySectionIdTag`. The default is `void`, which means running over all\n * elements in the array. Note that the actions in the `ApplyOperatorActions`\n * list passed to `solve` will _not_ be restricted to run only on section\n * elements, so all elements in the array may participate in preconditioning\n * (see LinearSolver::multigrid::Multigrid).\n *\n * \\see ConjugateGradient for a linear solver that is more efficient when the\n * linear operator \\f$A\\f$ is symmetric.\n */\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup,\n          bool Preconditioned,\n          typename SourceTag =\n              db::add_tag_prefix<::Tags::FixedSource, FieldsTag>,\n          typename ArraySectionIdTag = void>\nstruct Gmres {\n  using fields_tag = FieldsTag;\n  using options_group = OptionsGroup;\n  using source_tag = SourceTag;\n  static constexpr bool preconditioned = Preconditioned;\n\n  /// Apply the linear operator to this tag in each iteration\n  using operand_tag = std::conditional_t<\n      Preconditioned,\n      db::add_tag_prefix<\n          LinearSolver::Tags::Preconditioned,\n          db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>>,\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>>;\n\n  /// Invoke a linear solver on the `operand_tag` sourced by the\n  /// `preconditioner_source_tag` before applying the operator in each step\n  using preconditioner_source_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n\n  /*!\n   * \\brief The parallel components used by the GMRES linear solver\n   */\n  using component_list = tmpl::list<\n      detail::ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>;\n\n  using initialize_element =\n      detail::InitializeElement<FieldsTag, OptionsGroup, Preconditioned>;\n\n  using register_element = tmpl::list<>;\n\n  using amr_projectors = initialize_element;\n\n  template <typename ApplyOperatorActions,\n            typename ObserveActions = tmpl::list<>,\n            typename Label = OptionsGroup>\n  using solve = tmpl::list<\n      detail::PrepareSolve<FieldsTag, OptionsGroup, Preconditioned, Label,\n                           SourceTag, ArraySectionIdTag>,\n      ObserveActions,\n      detail::NormalizeInitialOperand<FieldsTag, OptionsGroup, Preconditioned,\n                                      Label, ArraySectionIdTag>,\n      detail::PrepareStep<FieldsTag, OptionsGroup, Preconditioned, Label,\n                          ArraySectionIdTag>,\n      ApplyOperatorActions,\n      detail::PerformStep<FieldsTag, OptionsGroup, Preconditioned, Label,\n                          ArraySectionIdTag>,\n      detail::OrthogonalizeOperand<FieldsTag, OptionsGroup, Preconditioned,\n                                   Label, ArraySectionIdTag>,\n      detail::NormalizeOperandAndUpdateField<\n          FieldsTag, OptionsGroup, Preconditioned, Label, ArraySectionIdTag>,\n      ObserveActions,\n      detail::CompleteStep<FieldsTag, OptionsGroup, Preconditioned, Label,\n                           ArraySectionIdTag>>;\n};\n\n}  // namespace LinearSolver::gmres\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Gmres/InitializeElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace LinearSolver::gmres::detail {\n\ntemplate <typename FieldsTag, typename OptionsGroup, bool Preconditioned>\nstruct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {\n private:\n  using fields_tag = FieldsTag;\n  using initial_fields_tag = db::add_tag_prefix<::Tags::Initial, fields_tag>;\n  using operator_applied_to_fields_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>;\n  using operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Operand, fields_tag>;\n  using preconditioned_operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Preconditioned, operand_tag>;\n  using operator_applied_to_operand_tag = db::add_tag_prefix<\n      LinearSolver::Tags::OperatorAppliedTo,\n      std::conditional_t<Preconditioned, preconditioned_operand_tag,\n                         operand_tag>>;\n  using orthogonalization_iteration_id_tag =\n      LinearSolver::Tags::Orthogonalization<\n          Convergence::Tags::IterationId<OptionsGroup>>;\n  using basis_history_tag =\n      LinearSolver::Tags::KrylovSubspaceBasis<operand_tag>;\n  using preconditioned_basis_history_tag =\n      LinearSolver::Tags::KrylovSubspaceBasis<preconditioned_operand_tag>;\n\n public:  // Iterable action\n  using simple_tags = tmpl::append<\n      tmpl::list<Convergence::Tags::IterationId<OptionsGroup>,\n                 initial_fields_tag, operator_applied_to_fields_tag,\n                 operand_tag, operator_applied_to_operand_tag,\n                 orthogonalization_iteration_id_tag, basis_history_tag,\n                 Convergence::Tags::HasConverged<OptionsGroup>>,\n      tmpl::conditional_t<Preconditioned,\n                          tmpl::list<preconditioned_basis_history_tag,\n                                     preconditioned_operand_tag>,\n                          tmpl::list<>>>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<\n        tmpl::list<Convergence::Tags::IterationId<OptionsGroup>,\n                   orthogonalization_iteration_id_tag>>(\n        make_not_null(&box), std::numeric_limits<size_t>::max(),\n        std::numeric_limits<size_t>::max());\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // amr::protocols::Projector\n  using argument_tags = tmpl::list<>;\n  using return_tags = simple_tags;\n\n  template <typename... AmrData>\n  static void apply(const gsl::not_null<size_t*> /*unused*/,\n                    const AmrData&... /*all_items*/) {\n    // No need to reset or initialize any of the items during AMR because they\n    // will be set in `PrepareSolve`. AMR can't happen _during_ a solve.\n  }\n};\n\n}  // namespace LinearSolver::gmres::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Gmres/ResidualMonitor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\nnamespace LinearSolver::gmres::detail {\ntemplate <typename FieldsTag, typename OptionsGroup>\nstruct InitializeResidualMonitor;\n}  // namespace LinearSolver::gmres::detail\n/// \\endcond\n\nnamespace LinearSolver::gmres::detail {\n\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup>\nstruct ResidualMonitor {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>,\n                 Convergence::Tags::Criteria<OptionsGroup>>;\n  using metavariables = Metavariables;\n  // The actions in `ResidualMonitorActions.hpp` are invoked as simple actions\n  // on this component as the result of reductions from the actions in\n  // `ElementActions.hpp`. See `LinearSolver::gmres::Gmres` for details.\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<InitializeResidualMonitor<FieldsTag, OptionsGroup>>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ResidualMonitor>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup>\nstruct InitializeResidualMonitor {\n private:\n  using fields_tag = FieldsTag;\n  using residual_magnitude_tag = LinearSolver::Tags::Magnitude<\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>;\n  using initial_residual_magnitude_tag =\n      ::Tags::Initial<residual_magnitude_tag>;\n  using previous_residual_magnitude_tag =\n      ::Tags::Previous<residual_magnitude_tag>;\n  using orthogonalization_history_tag =\n      LinearSolver::Tags::OrthogonalizationHistory<fields_tag>;\n\n public:\n  using simple_tags = tmpl::list<initial_residual_magnitude_tag,\n                                 previous_residual_magnitude_tag,\n                                 orthogonalization_history_tag>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename Metavariables, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // The `InitializeResidualMagnitude` action populates these tags\n    // with initial values\n    Initialization::mutate_assign<tmpl::list<initial_residual_magnitude_tag,\n                                             previous_residual_magnitude_tag>>(\n        make_not_null(&box), std::numeric_limits<double>::signaling_NaN(),\n        std::numeric_limits<double>::signaling_NaN());\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n}  // namespace LinearSolver::gmres::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Gmres/ResidualMonitorActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <blaze/math/DynamicMatrix.h>\n#include <blaze/math/DynamicVector.h>\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Tags/InboxTags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Observe.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace LinearSolver::gmres::detail {\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename BroadcastTarget>\nstruct InitializeResidualMagnitude {\n private:\n  using fields_tag = FieldsTag;\n  using residual_magnitude_tag = LinearSolver::Tags::Magnitude<\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>;\n  using initial_residual_magnitude_tag =\n      ::Tags::Initial<residual_magnitude_tag>;\n  using previous_residual_magnitude_tag =\n      ::Tags::Previous<residual_magnitude_tag>;\n  using orthogonalization_history_tag =\n      LinearSolver::Tags::OrthogonalizationHistory<fields_tag>;\n\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const double residual_magnitude) {\n    constexpr size_t iteration_id = 0;\n\n    db::mutate<initial_residual_magnitude_tag, previous_residual_magnitude_tag>(\n        [residual_magnitude](\n            const gsl::not_null<double*> initial_residual_magnitude,\n            const gsl::not_null<double*> previous_residual_magnitude) {\n          *initial_residual_magnitude = residual_magnitude;\n          *previous_residual_magnitude = residual_magnitude;\n        },\n        make_not_null(&box));\n\n    LinearSolver::observe_detail::contribute_to_reduction_observer<\n        OptionsGroup, ParallelComponent>(iteration_id, residual_magnitude,\n                                         cache);\n\n    // Determine whether the linear solver has already converged\n    Convergence::HasConverged has_converged{\n        get<Convergence::Tags::Criteria<OptionsGroup>>(box), iteration_id,\n        residual_magnitude, residual_magnitude};\n\n    // Do some logging\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(cache) >=\n                 ::Verbosity::Quiet)) {\n      Parallel::printf(\"%s initialized with residual: %e\\n\",\n                       pretty_type::name<OptionsGroup>(), residual_magnitude);\n    }\n    if (UNLIKELY(has_converged and get<logging::Tags::Verbosity<OptionsGroup>>(\n                                       cache) >= ::Verbosity::Quiet)) {\n      Parallel::printf(\"%s has converged without any iterations: %s\\n\",\n                       pretty_type::name<OptionsGroup>(), has_converged);\n    }\n\n    Parallel::receive_data<Tags::InitialOrthogonalization<OptionsGroup>>(\n        Parallel::get_parallel_component<BroadcastTarget>(cache), iteration_id,\n        // NOLINTNEXTLINE(performance-move-const-arg)\n        std::make_tuple(residual_magnitude, std::move(has_converged)));\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename BroadcastTarget>\nstruct StoreOrthogonalization {\n private:\n  using fields_tag = FieldsTag;\n  using residual_magnitude_tag = LinearSolver::Tags::Magnitude<\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>>;\n  using initial_residual_magnitude_tag =\n      ::Tags::Initial<residual_magnitude_tag>;\n  using previous_residual_magnitude_tag =\n      ::Tags::Previous<residual_magnitude_tag>;\n  using orthogonalization_history_tag =\n      LinearSolver::Tags::OrthogonalizationHistory<fields_tag>;\n  using ValueType =\n      tt::get_complex_or_fundamental_type_t<typename fields_tag::type>;\n\n public:\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const size_t iteration_id,\n                    const size_t orthogonalization_iteration_id,\n                    const ValueType orthogonalization) {\n    if (UNLIKELY(orthogonalization_iteration_id == 0)) {\n      // Append a row and a column to the orthogonalization history. Zero the\n      // entries that won't be set during the orthogonalization procedure below.\n      db::mutate<orthogonalization_history_tag>(\n          [iteration_id](const auto orthogonalization_history) {\n            orthogonalization_history->resize(iteration_id + 1, iteration_id);\n            for (size_t j = 0; j < orthogonalization_history->columns() - 1;\n                 ++j) {\n              (*orthogonalization_history)(\n                  orthogonalization_history->rows() - 1, j) = 0.;\n            }\n          },\n          make_not_null(&box));\n    }\n\n    // While the orthogonalization procedure is not complete, store the\n    // orthogonalization, broadcast it back to all elements and return early\n    if (orthogonalization_iteration_id < iteration_id) {\n      db::mutate<orthogonalization_history_tag>(\n          [orthogonalization, iteration_id, orthogonalization_iteration_id](\n              const auto orthogonalization_history) {\n            (*orthogonalization_history)(orthogonalization_iteration_id,\n                                         iteration_id - 1) = orthogonalization;\n          },\n          make_not_null(&box));\n\n      Parallel::receive_data<Tags::Orthogonalization<OptionsGroup, ValueType>>(\n          Parallel::get_parallel_component<BroadcastTarget>(cache),\n          iteration_id, orthogonalization);\n      return;\n    }\n\n    // At this point, the orthogonalization procedure is complete.\n    ASSERT(equal_within_roundoff(imag(orthogonalization), 0.0, 1e-14,\n                                 abs(orthogonalization)),\n           \"Normalization is not real: \" << orthogonalization);\n    const double normalization = sqrt(real(orthogonalization));\n    db::mutate<orthogonalization_history_tag>(\n        [normalization, iteration_id,\n         orthogonalization_iteration_id](const auto orthogonalization_history) {\n          (*orthogonalization_history)(orthogonalization_iteration_id,\n                                       iteration_id - 1) = normalization;\n        },\n        make_not_null(&box));\n\n    // Perform a QR decomposition of the Hessenberg matrix that was built during\n    // the orthogonalization\n    const auto& orthogonalization_history =\n        get<orthogonalization_history_tag>(box);\n    const auto num_rows = orthogonalization_iteration_id + 1;\n    blaze::DynamicMatrix<ValueType> qr_Q;\n    blaze::DynamicMatrix<ValueType> qr_R;\n    blaze::qr(orthogonalization_history, qr_Q, qr_R);\n    // Compute the residual vector from the QR decomposition\n    blaze::DynamicVector<double> beta(num_rows, 0.);\n    const double initial_residual_magnitude =\n        get<initial_residual_magnitude_tag>(box);\n    beta[0] = initial_residual_magnitude;\n    blaze::DynamicVector<ValueType> minres =\n        blaze::inv(qr_R) * blaze::ctrans(qr_Q) * beta;\n    blaze::DynamicVector<ValueType> res =\n        beta - orthogonalization_history * minres;\n    const double residual_magnitude = sqrt(magnitude_square(res));\n\n    // At this point, the iteration is complete. We proceed with observing,\n    // logging and checking convergence before broadcasting back to the\n    // elements.\n\n    LinearSolver::observe_detail::contribute_to_reduction_observer<\n        OptionsGroup, ParallelComponent>(iteration_id, residual_magnitude,\n                                         cache);\n\n    // Determine whether the linear solver has converged.\n    // GMRES is guaranteed to decrease the residual monotonically, so an\n    // increase in the residual is an error.\n    const auto& convergence_criteria =\n        get<Convergence::Tags::Criteria<OptionsGroup>>(box);\n    const double previous_residual_magnitude =\n        get<previous_residual_magnitude_tag>(box);\n    auto has_converged =\n        residual_magnitude < previous_residual_magnitude\n            ? Convergence::HasConverged{convergence_criteria, iteration_id,\n                                        residual_magnitude,\n                                        initial_residual_magnitude}\n            : Convergence::HasConverged{\n                  Convergence::Reason::Error,\n                  MakeString{} << std::scientific\n                               << \"Residual should decrease monotonically, but \"\n                                  \"increased from \"\n                               << previous_residual_magnitude << \" to \"\n                               << residual_magnitude << \".\",\n                  iteration_id};\n\n    db::mutate<previous_residual_magnitude_tag>(\n        [residual_magnitude](\n            const gsl::not_null<double*> stored_previous_residual_magnitude) {\n          *stored_previous_residual_magnitude = residual_magnitude;\n        },\n        make_not_null(&box));\n\n    // Do some logging\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(cache) >=\n                 ::Verbosity::Quiet)) {\n      Parallel::printf(\"%s(%zu) iteration complete. Remaining residual: %e\\n\",\n                       pretty_type::name<OptionsGroup>(), iteration_id,\n                       residual_magnitude);\n    }\n    if (UNLIKELY(has_converged and get<logging::Tags::Verbosity<OptionsGroup>>(\n                                       cache) >= ::Verbosity::Quiet)) {\n      if (has_converged.reason() == Convergence::Reason::Error) {\n        Parallel::printf(\"%s has encountered an error in iteration %zu: %s\\n\",\n                         pretty_type::name<OptionsGroup>(), iteration_id,\n                         has_converged.error_message());\n      } else {\n        Parallel::printf(\"%s has converged in %zu iterations: %s\\n\",\n                         pretty_type::name<OptionsGroup>(), iteration_id,\n                         has_converged);\n      }\n    }\n\n    Parallel::receive_data<\n        Tags::FinalOrthogonalization<OptionsGroup, ValueType>>(\n        Parallel::get_parallel_component<BroadcastTarget>(cache), iteration_id,\n        std::make_tuple(normalization, std::move(minres),\n                        // NOLINTNEXTLINE(performance-move-const-arg)\n                        std::move(has_converged)));\n  }\n};\n\n}  // namespace LinearSolver::gmres::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Gmres/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InboxTags.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Gmres/Tags/InboxTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <tuple>\n\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::gmres::detail::Tags {\n\ntemplate <typename OptionsGroup>\nstruct InitialOrthogonalization\n    : Parallel::InboxInserters::Value<InitialOrthogonalization<OptionsGroup>> {\n  using temporal_id = size_t;\n  using type =\n      std::map<temporal_id, std::tuple<double, Convergence::HasConverged>>;\n};\n\ntemplate <typename OptionsGroup, typename ValueType>\nstruct Orthogonalization : Parallel::InboxInserters::Value<\n                               Orthogonalization<OptionsGroup, ValueType>> {\n  using temporal_id = size_t;\n  using type = std::map<temporal_id, ValueType>;\n};\n\ntemplate <typename OptionsGroup, typename ValueType>\nstruct FinalOrthogonalization\n    : Parallel::InboxInserters::Value<\n          FinalOrthogonalization<OptionsGroup, ValueType>> {\n  using temporal_id = size_t;\n  using type =\n      std::map<temporal_id, std::tuple<double, blaze::DynamicVector<ValueType>,\n                                       Convergence::HasConverged>>;\n};\n\n}  // namespace LinearSolver::gmres::detail::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  RestrictFields.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/Actions/RestrictFields.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/FixedHashMap.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nstruct ElementId;\n/// \\endcond\n\nnamespace LinearSolver::multigrid {\n\ntemplate <size_t Dim, typename ReceiveTags>\nstruct DataFromChildrenInboxTag\n    : public Parallel::InboxInserters::Map<\n          DataFromChildrenInboxTag<Dim, ReceiveTags>> {\n  using temporal_id = size_t;\n  using type =\n      std::map<temporal_id,\n               FixedHashMap<two_to_the(Dim), ElementId<Dim>,\n                            tuples::tagged_tuple_from_typelist<ReceiveTags>,\n                            boost::hash<ElementId<Dim>>>>;\n};\n\n/// Actions related to the Multigrid linear solver\nnamespace Actions {\n/*!\n * \\brief Communicate and project the `FieldsTags` to the next-coarser grid\n * in the multigrid hierarchy\n *\n * \\tparam FieldsTags These tags will be communicated and projected. They can\n * hold any type that works with `::apply_matrices` and supports addition, e.g.\n * `Variables`.\n * \\tparam OptionsGroup The option group identifying the multigrid solver\n * \\tparam FieldsAreMassiveTag A boolean tag in the DataBox that indicates\n * whether or not the `FieldsTags` have already been multiplied by the mass\n * matrix. This setting influences the way the fields are projected. In\n * particular, the mass matrix already includes a Jacobian factor, so the\n * difference in size between the parent and the child element is already\n * accounted for.\n * \\tparam ReceiveTags The projected fields will be stored in these tags\n * (default: `FieldsTags`).\n */\ntemplate <typename FieldsTags, typename OptionsGroup,\n          typename FieldsAreMassiveTag, typename ReceiveTags = FieldsTags>\nstruct SendFieldsToCoarserGrid;\n\n/// \\cond\ntemplate <typename... FieldsTags, typename OptionsGroup,\n          typename FieldsAreMassiveTag, typename... ReceiveTags>\nstruct SendFieldsToCoarserGrid<tmpl::list<FieldsTags...>, OptionsGroup,\n                               FieldsAreMassiveTag,\n                               tmpl::list<ReceiveTags...>> {\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Skip restriction on coarsest level\n    const auto& parent_id = db::get<amr::Tags::ParentId<Dim>>(box);\n    if (not parent_id.has_value()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Send fields to coarser grid %s\\n\",\n                       element_id, pretty_type::name<OptionsGroup>(),\n                       iteration_id, parent_id);\n    }\n\n    // Restrict the fields to the coarser (parent) grid.\n    // We restrict before sending the data so the restriction operation is\n    // parellelized. The parent only needs to sum up all child contributions.\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    const auto& parent_mesh = db::get<amr::Tags::ParentMesh<Dim>>(box);\n    ASSERT(\n        parent_mesh.has_value(),\n        \"Should have a parent mesh, because a parent ID is set. This element: \"\n            << element_id << \", parent element: \" << *parent_id);\n    const auto child_size =\n        domain::child_size(element_id.segment_ids(), parent_id->segment_ids());\n    bool massive = false;\n    if constexpr (not std::is_same_v<FieldsAreMassiveTag, void>) {\n      massive = db::get<FieldsAreMassiveTag>(box);\n    }\n    tuples::TaggedTuple<ReceiveTags...> restricted_fields{};\n    if (Spectral::needs_projection(mesh, *parent_mesh, child_size)) {\n      const auto restriction_operator =\n          Spectral::projection_matrix_child_to_parent(mesh, *parent_mesh,\n                                                      child_size, massive);\n      const auto restrict_fields = [&restricted_fields, &restriction_operator,\n                                    &mesh](const auto receive_tag_v,\n                                           const auto& fields) {\n        using receive_tag = std::decay_t<decltype(receive_tag_v)>;\n        get<receive_tag>(restricted_fields) = typename receive_tag::type(\n            apply_matrices(restriction_operator, fields, mesh.extents()));\n        return '0';\n      };\n      expand_pack(restrict_fields(ReceiveTags{}, db::get<FieldsTags>(box))...);\n    } else {\n      expand_pack(\n          (get<ReceiveTags>(restricted_fields) =\n               typename ReceiveTags::type(db::get<FieldsTags>(box)))...);\n    }\n\n    // Send restricted fields to the parent\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    Parallel::receive_data<\n        DataFromChildrenInboxTag<Dim, tmpl::list<ReceiveTags...>>>(\n        receiver_proxy[*parent_id], iteration_id,\n        std::make_pair(element_id, std::move(restricted_fields)));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n/// \\endcond\n\n/// Receive the `FieldsTags` communicated from the finer grid in the multigrid\n/// hierarchy.\n///\n/// \\see LinearSolver::multigrid::Actions::SendFieldsToCoarserGrid\ntemplate <size_t Dim, typename FieldsTags, typename OptionsGroup,\n          typename ReceiveTags = FieldsTags>\nstruct ReceiveFieldsFromFinerGrid;\n\n/// \\cond\ntemplate <size_t Dim, typename FieldsTags, typename OptionsGroup,\n          typename... ReceiveTags>\nstruct ReceiveFieldsFromFinerGrid<Dim, FieldsTags, OptionsGroup,\n                                  tmpl::list<ReceiveTags...>> {\n  using inbox_tags =\n      tmpl::list<DataFromChildrenInboxTag<Dim, tmpl::list<ReceiveTags...>>>;\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Skip on finest grid\n    const auto& child_ids = db::get<amr::Tags::ChildIds<Dim>>(box);\n    const bool is_finest_grid = child_ids.empty();\n    if (is_finest_grid) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    // Wait for data from finer grid\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    auto& inbox =\n        tuples::get<DataFromChildrenInboxTag<Dim, tmpl::list<ReceiveTags...>>>(\n            inboxes);\n    const auto received_this_iteration = inbox.find(iteration_id);\n    if (received_this_iteration == inbox.end()) {\n      if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                   ::Verbosity::Debug)) {\n        Parallel::printf(\n            \"%s %s(%zu): Waiting for fine-grid data (still empty)\\n\",\n            element_id, pretty_type::name<OptionsGroup>(), iteration_id);\n      }\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    const auto& received_children_data = received_this_iteration->second;\n    for (const auto& child_id : child_ids) {\n      if (received_children_data.find(child_id) ==\n          received_children_data.end()) {\n        if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                     ::Verbosity::Debug)) {\n          Parallel::printf(\n              \"%s %s(%zu): Waiting for fine-grid data from child %s\\n\",\n              element_id, pretty_type::name<OptionsGroup>(), iteration_id,\n              child_id);\n        }\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n    }\n    auto children_data = std::move(inbox.extract(iteration_id).mapped());\n\n    if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Receive fields from finer grid\\n\",\n                       element_id, pretty_type::name<OptionsGroup>(),\n                       iteration_id);\n    }\n\n    // Assemble restricted data from children\n    const auto assemble_children_data =\n        [&children_data](const auto source, const auto receive_tag_v) {\n          using receive_tag = std::decay_t<decltype(receive_tag_v)>;\n          // Move the first child data directly into the buffer, then add the\n          // data from the remaining children.\n          auto child_id_and_data = children_data.begin();\n          *source = std::move(get<receive_tag>(child_id_and_data->second));\n          ++child_id_and_data;\n          while (child_id_and_data != children_data.end()) {\n            *source += get<receive_tag>(child_id_and_data->second);\n            ++child_id_and_data;\n          }\n          return '0';\n        };\n    expand_pack(db::mutate<ReceiveTags>(assemble_children_data,\n                                        make_not_null(&box), ReceiveTags{})...);\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n/// \\endcond\n\n}  // namespace Actions\n}  // namespace LinearSolver::multigrid\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ParallelMultigrid)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Hierarchy.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ElementActions.hpp\n  ElementsAllocator.hpp\n  Hierarchy.hpp\n  Multigrid.hpp\n  ObserveVolumeData.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DomainStructure\n  ErrorHandling\n  Utilities\n  INTERFACE\n  Convergence\n  DataStructures\n  Domain\n  Initialization\n  Logging\n  Observer\n  Options\n  Parallel\n  ParallelLinearSolver\n  Printf\n  Spectral\n  SystemUtilities\n  )\n\nadd_subdirectory(Actions)\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/ElementActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/FixedHashMap.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Actions/RestrictFields.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Hierarchy.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace LinearSolver::multigrid::detail {\n\n/// \\cond\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct SendCorrectionToFinerGrid;\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct SkipBottomSolver;\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct SkipPostSmoothingAtBottom;\n/// \\endcond\n\nstruct PostSmoothingBeginLabel {};\n\ntemplate <size_t Dim, typename FieldsTag, typename OptionsGroup,\n          typename SourceTag>\nstruct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {\n private:\n  using VolumeDataVars =\n      typename Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>::type;\n\n public:  // Iterable action\n  using simple_tags_from_options =\n      tmpl::list<Tags::ChildrenRefinementLevels<Dim>,\n                 Tags::ParentRefinementLevels<Dim>>;\n  using simple_tags =\n      tmpl::list<amr::Tags::ParentId<Dim>, amr::Tags::ChildIds<Dim>,\n                 amr::Tags::ParentMesh<Dim>,\n                 LinearSolver::Tags::ObservationId<OptionsGroup>,\n                 Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::InitialCoarseLevels<OptionsGroup>,\n                 LinearSolver::Tags::OutputVolumeData<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeElement>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // amr::protocols::Projector\n  using argument_tags =\n      tmpl::list<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                 domain::Tags::InitialRefinementLevels<Dim>,\n                 LinearSolver::Tags::OutputVolumeData<OptionsGroup>>;\n  using return_tags = tmpl::append<simple_tags, simple_tags_from_options>;\n\n  template <typename... AmrData>\n  static void apply(\n      const gsl::not_null<std::optional<ElementId<Dim>>*> parent_id,\n      const gsl::not_null<std::unordered_set<ElementId<Dim>>*> child_ids,\n      const gsl::not_null<std::optional<Mesh<Dim>>*> parent_mesh,\n      const gsl::not_null<size_t*> observation_id,\n      const gsl::not_null<VolumeDataVars*> volume_data_for_output,\n      const gsl::not_null<std::vector<std::array<size_t, Dim>>*>\n          children_refinement_levels,\n      const gsl::not_null<std::vector<std::array<size_t, Dim>>*>\n          parent_refinement_levels,\n      const Mesh<Dim>& mesh, const Element<Dim>& element,\n      const std::vector<std::array<size_t, Dim>> initial_refinement_levels,\n      const bool output_volume_data, const AmrData&... amr_data) {\n    // Note: This initialization code runs on elements of the initial multigrid\n    // hierarchy (created by LinearSolver::multigrid::ElementsAllocator) and on\n    // elements created by AMR. Elements in a block of the initial domain are\n    // assumed to have the same p-refinement.\n\n    if constexpr (sizeof...(AmrData) == 0) {\n      // Initialization: use initial domain to set up multigrid hierarchy\n      const auto& element_id = element.id();\n      const bool is_coarsest_grid =\n          initial_refinement_levels == *parent_refinement_levels;\n      const bool is_finest_grid =\n          initial_refinement_levels == *children_refinement_levels;\n      *parent_id = is_coarsest_grid\n                       ? std::nullopt\n                       : std::make_optional(multigrid::parent_id(element_id));\n      *child_ids =\n          is_finest_grid\n              ? std::unordered_set<ElementId<Dim>>{}\n              : multigrid::child_ids(\n                    element_id,\n                    (*children_refinement_levels)[element_id.block_id()]);\n      *parent_mesh = is_coarsest_grid ? std::nullopt : std::make_optional(mesh);\n      *observation_id = 0;\n    } else {\n      // These items are updated by AMR\n      (void)parent_id;\n      (void)child_ids;\n      (void)parent_mesh;\n      // These items are only needed during initialization\n      (void)parent_refinement_levels;\n      (void)children_refinement_levels;\n      // Preserve state of observation ID\n      if constexpr (tt::is_a_v<tuples::TaggedTuple, AmrData...>) {\n        // h-refinement: copy from the parent\n        *observation_id =\n            get<LinearSolver::Tags::ObservationId<OptionsGroup>>(amr_data...);\n      } else if constexpr (tt::is_a_v<std::unordered_map, AmrData...>) {\n        // h-coarsening: copy from one of the children (doesn't matter which)\n        *observation_id = get<LinearSolver::Tags::ObservationId<OptionsGroup>>(\n            amr_data.begin()->second...);\n      } else {\n        (void)observation_id;\n      }\n    }\n    // Initialize volume data output\n    if (output_volume_data) {\n      volume_data_for_output->initialize(mesh.number_of_grid_points());\n    }\n  }\n};\n\n// These two actions communicate and project the residual from the finer grid to\n// the coarser grid, storing it in the `SourceTag` on the coarser grid.\ntemplate <typename FieldsTag, typename OptionsGroup,\n          typename ResidualIsMassiveTag, typename SourceTag>\nusing SendResidualToCoarserGrid = Actions::SendFieldsToCoarserGrid<\n    tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,\n    OptionsGroup, ResidualIsMassiveTag, tmpl::list<SourceTag>>;\n\ntemplate <size_t Dim, typename FieldsTag, typename OptionsGroup,\n          typename SourceTag>\nusing ReceiveResidualFromFinerGrid = Actions::ReceiveFieldsFromFinerGrid<\n    Dim,\n    tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,\n    OptionsGroup, tmpl::list<SourceTag>>;\n\n// Once the residual from the finer grid has been received and stored in the\n// `SourceTag`, this action prepares the pre-smoothing that will determine\n// an approximate solution on this grid. The pre-smoother is a separate\n// linear solver that runs independently after this action.\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag,\n          bool EnableBottomSolver>\nstruct PreparePreSmoothing {\n private:\n  using fields_tag = FieldsTag;\n  using operator_applied_to_fields_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>;\n  using source_tag = SourceTag;\n\n public:\n  using const_global_cache_tags = tmpl::append<\n      tmpl::list<\n          LinearSolver::multigrid::Tags::EnablePreSmoothing<OptionsGroup>>,\n      tmpl::conditional_t<EnableBottomSolver,\n                          tmpl::list<LinearSolver::multigrid::Tags::\n                                         UseBottomSolver<OptionsGroup>>,\n                          tmpl::list<>>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    const bool is_coarsest_grid =\n        not db::get<::amr::Tags::ParentId<Dim>>(box).has_value();\n    if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Prepare %s\\n\", element_id,\n                       pretty_type::name<OptionsGroup>(), iteration_id,\n                       (EnableBottomSolver and is_coarsest_grid)\n                           ? \"bottom-solver\"\n                           : \"pre-smoothing\");\n    }\n\n    // On coarser grids the smoother solves for a correction to the finer-grid\n    // fields, so we set its initial guess to zero. On the finest grid we smooth\n    // the fields directly, so there's nothing to prepare.\n    const bool is_finest_grid = db::get<amr::Tags::ChildIds<Dim>>(box).empty();\n    if (not is_finest_grid) {\n      db::mutate<fields_tag, operator_applied_to_fields_tag>(\n          [](const auto fields, const auto operator_applied_to_fields,\n             const auto& source) {\n            *fields = make_with_value<typename fields_tag::type>(source, 0.);\n            // We can set the linear operator applied to the initial fields to\n            // zero as well, since it's linear\n            *operator_applied_to_fields =\n                make_with_value<typename operator_applied_to_fields_tag::type>(\n                    source, 0.);\n          },\n          make_not_null(&box), db::get<source_tag>(box));\n    }\n\n    // Record pre-smoothing initial fields and source\n    if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {\n      db::mutate<Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>(\n          [](const auto volume_data, const auto& initial_fields,\n             const auto& source) {\n            volume_data->assign_subset(\n                Variables<db::wrap_tags_in<Tags::PreSmoothingInitial,\n                                           typename fields_tag::tags_list>>(\n                    initial_fields));\n            volume_data->assign_subset(\n                Variables<db::wrap_tags_in<Tags::PreSmoothingSource,\n                                           typename fields_tag::tags_list>>(\n                    source));\n          },\n          make_not_null(&box), db::get<fields_tag>(box),\n          db::get<source_tag>(box));\n    }\n\n    // Skip pre-smoothing, if requested, or use bottom solver\n    if constexpr (EnableBottomSolver) {\n      if (db::get<LinearSolver::multigrid::Tags::UseBottomSolver<OptionsGroup>>(\n              box) and\n          is_coarsest_grid) {\n        const size_t bottom_solver_index =\n            tmpl::index_of<ActionList, SkipBottomSolver<FieldsTag, OptionsGroup,\n                                                        SourceTag>>::value +\n            1;\n        return {Parallel::AlgorithmExecution::Continue, bottom_solver_index};\n      }\n    }\n    const size_t first_action_after_pre_smoothing_index = tmpl::index_of<\n        ActionList,\n        SkipPostSmoothingAtBottom<FieldsTag, OptionsGroup, SourceTag>>::value;\n    const size_t this_action_index =\n        tmpl::index_of<ActionList, PreparePreSmoothing>::value;\n    return {\n        Parallel::AlgorithmExecution::Continue,\n        db::get<\n            LinearSolver::multigrid::Tags::EnablePreSmoothing<OptionsGroup>>(\n            box)\n            ? (this_action_index + 1)\n            : first_action_after_pre_smoothing_index};\n  }\n};\n\n// Once pre-smoothing is done, skip the bottom solver that comes next in the\n// action list. On the coarsest grid we directly jump to the bottom solver.\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct SkipBottomSolver {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t first_action_after_bottom_solver_index = tmpl::index_of<\n        ActionList,\n        SkipPostSmoothingAtBottom<FieldsTag, OptionsGroup, SourceTag>>::value;\n    return {Parallel::AlgorithmExecution::Continue,\n            first_action_after_bottom_solver_index};\n  }\n};\n\n// Once the pre-smoothing is done, we skip the second smoothing step on the\n// coarsest grid, i.e. at the \"tip\" of the V-cycle.\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct SkipPostSmoothingAtBottom {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  using const_global_cache_tags = tmpl::list<\n      LinearSolver::multigrid::Tags::EnablePostSmoothingAtBottom<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const bool is_coarsest_grid =\n        not db::get<amr::Tags::ParentId<Dim>>(box).has_value();\n\n    // Record pre-smoothing result fields and residual\n    if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {\n      db::mutate<Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>(\n          [](const auto volume_data, const auto& result_fields,\n             const auto& residuals) {\n            volume_data->assign_subset(\n                Variables<db::wrap_tags_in<Tags::PreSmoothingResult,\n                                           typename fields_tag::tags_list>>(\n                    result_fields));\n            volume_data->assign_subset(\n                Variables<db::wrap_tags_in<Tags::PreSmoothingResidual,\n                                           typename fields_tag::tags_list>>(\n                    residuals));\n          },\n          make_not_null(&box), db::get<fields_tag>(box),\n          db::get<residual_tag>(box));\n    }\n\n    // Skip post-smoothing on the coarsest grid, if requested\n    const size_t first_action_after_post_smoothing_index = tmpl::index_of<\n        ActionList,\n        SendCorrectionToFinerGrid<FieldsTag, OptionsGroup, SourceTag>>::value;\n    const size_t post_smoothing_begin_index =\n        tmpl::index_of<ActionList,\n                       ::Actions::Label<PostSmoothingBeginLabel>>::value +\n        1;\n    const size_t this_action_index =\n        tmpl::index_of<ActionList, SkipPostSmoothingAtBottom>::value;\n    return {Parallel::AlgorithmExecution::Continue,\n            is_coarsest_grid\n                ? (db::get<LinearSolver::multigrid::Tags::\n                               EnablePostSmoothingAtBottom<OptionsGroup>>(box)\n                       ? post_smoothing_begin_index\n                       : first_action_after_post_smoothing_index)\n                : (this_action_index + 1)};\n  }\n};\n\ntemplate <typename FieldsTag>\nstruct CorrectionInboxTag\n    : public Parallel::InboxInserters::Value<CorrectionInboxTag<FieldsTag>> {\n  using temporal_id = size_t;\n  using type = std::map<temporal_id, typename FieldsTag::type>;\n};\n\n// The next two actions communicate and project the coarse-grid correction, i.e.\n// the solution of the post-smoother, to the finer grid. The post-smoother on\n// finer grids runs after receiving this coarse-grid correction. Since the\n// post-smoother is skipped on the coarsest level, it directly sends the\n// solution of the pre-smoother to the finer grid, thus kicking off the\n// \"ascending\" branch of the V-cycle.\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct SendCorrectionToFinerGrid {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& child_ids = db::get<amr::Tags::ChildIds<Dim>>(box);\n\n    // Record post-smoothing result fields and residual\n    if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {\n      db::mutate<Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>(\n          [](const auto volume_data, const auto& result_fields,\n             const auto& residuals) {\n            volume_data->assign_subset(\n                Variables<db::wrap_tags_in<Tags::PostSmoothingResult,\n                                           typename fields_tag::tags_list>>(\n                    result_fields));\n            volume_data->assign_subset(\n                Variables<db::wrap_tags_in<Tags::PostSmoothingResidual,\n                                           typename fields_tag::tags_list>>(\n                    residuals));\n          },\n          make_not_null(&box), db::get<fields_tag>(box),\n          db::get<residual_tag>(box));\n    }\n\n    if (child_ids.empty()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Send correction to children\\n\", element_id,\n                       pretty_type::name<OptionsGroup>(), iteration_id);\n    }\n\n    // Send a copy of the correction to all children\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    for (const auto& child_id : child_ids) {\n      auto coarse_grid_correction = db::get<fields_tag>(box);\n      Parallel::receive_data<CorrectionInboxTag<FieldsTag>>(\n          receiver_proxy[child_id], iteration_id,\n          std::move(coarse_grid_correction));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <size_t Dim, typename FieldsTag, typename OptionsGroup,\n          typename SourceTag>\nstruct ReceiveCorrectionFromCoarserGrid {\n private:\n  using fields_tag = FieldsTag;\n  using source_tag = SourceTag;\n\n public:\n  using inbox_tags = tmpl::list<CorrectionInboxTag<FieldsTag>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& parent_id = db::get<amr::Tags::ParentId<Dim>>(box);\n    // We should always have a `parent_id` at this point because we skip this\n    // part of the algorithm on the coarsest grid with the\n    // `SkipPostSmoothingAtBottom` action\n    ASSERT(parent_id.has_value(),\n           \"Trying to receive data from parent but no parent is set on element \"\n               << element_id << \".\");\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n\n    // Wait for data from coarser grid\n    auto& inbox = tuples::get<CorrectionInboxTag<FieldsTag>>(inboxes);\n    if (inbox.find(iteration_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    auto parent_correction = std::move(inbox.extract(iteration_id).mapped());\n\n    if (UNLIKELY(db::get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Prolongate correction from parent\\n\",\n                       element_id, pretty_type::name<OptionsGroup>(),\n                       iteration_id);\n    }\n\n    // Apply prolongation operator\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    const auto& parent_mesh = db::get<amr::Tags::ParentMesh<Dim>>(box);\n    ASSERT(\n        parent_mesh.has_value(),\n        \"Should have a parent mesh, because a parent ID is set. This element: \"\n            << element_id << \", parent element: \" << *parent_id);\n    const auto child_size =\n        domain::child_size(element_id.segment_ids(), parent_id->segment_ids());\n    const auto prolongated_parent_correction =\n        [&parent_correction, &parent_mesh, &mesh, &child_size]() {\n          if (Spectral::needs_projection(*parent_mesh, mesh, child_size)) {\n            const auto prolongation_operator =\n                Spectral::projection_matrix_parent_to_child(*parent_mesh, mesh,\n                                                            child_size);\n            return apply_matrices(prolongation_operator, parent_correction,\n                                  parent_mesh->extents());\n          } else {\n            return std::move(parent_correction);\n          }\n        }();\n\n    // Add correction to the solution on this grid\n    db::mutate<fields_tag>(\n        [&prolongated_parent_correction](const auto fields) {\n          *fields += prolongated_parent_correction;\n        },\n        make_not_null(&box));\n\n    // Record post-smoothing initial fields and source\n    if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {\n      db::mutate<Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>>(\n          [](const auto volume_data, const auto& initial_fields,\n             const auto& source) {\n            volume_data->assign_subset(\n                Variables<db::wrap_tags_in<Tags::PostSmoothingInitial,\n                                           typename fields_tag::tags_list>>(\n                    initial_fields));\n            volume_data->assign_subset(\n                Variables<db::wrap_tags_in<Tags::PostSmoothingSource,\n                                           typename fields_tag::tags_list>>(\n                    source));\n          },\n          make_not_null(&box), db::get<fields_tag>(box),\n          db::get<source_tag>(box));\n    }\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace LinearSolver::multigrid::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <charm++.h>\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/ElementDistribution.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags/ElementDistribution.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Protocols/ArrayElementsAllocator.hpp\"\n#include \"Parallel/Section.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Hierarchy.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace LinearSolver::multigrid {\n\n/*!\n * \\brief A `Parallel::protocols::ArrayElementsAllocator` that creates array\n * elements to cover the initial computational domain multiple times at\n * different refinement levels, suitable for the\n * `LinearSolver::multigrid::Multigrid` algorithm\n *\n * The `initial_element_ids` function and the option-created\n * `domain::Tags::Domain` and `domain::Tags::InitialRefinementLevels` determine\n * the initial set of element IDs in the domain. This is taken as the finest\n * grid in the multigrid hierarchy. Coarser grids are determined by successively\n * applying `LinearSolver::multigrid::coarsen`, up to\n * `LinearSolver::multigrid::Tags::InitialCoarseLevels` coarser grids.\n *\n * Array elements are created for all element IDs on all grids, meaning they all\n * share the same parallel component, action list etc. Elements are connected to\n * their neighbors _on the same grid_ by the\n * `domain::create_initial_element` function (this is independent of the\n * multigrid code, the function is typically called in an initialization\n * action). Elements are connected to their parent and children _across grids_\n * by the multigrid-tags set here and in the multigrid initialization actions\n * (see `LinearSolver::multigrid::parent_id` and\n * `LinearSolver::multigrid::child_ids`).\n *\n * Once the multigrid hierarchy is created, the AMR infrastructure registers\n * the grids and creates the following sections (see `Parallel::Section`).\n * - `Parallel::Tags::Section<ElementArray, amr::Tags::GridIndex>`: One section\n *   per grid. All elements are part of a section with this tag.\n * - `Parallel::Tags::Section<ElementArray, amr::Tags::IsFinestGrid>`: A single\n *   section that holds only the elements on the finest grid. Holds\n *   `std::nullopt` on all other elements.\n *\n * The elements are distributed on processors using the\n * `domain::BlockZCurveProcDistribution` for every grid independently. An\n * unordered set of `size_t`s can be passed to the `apply` function which\n * represents physical processors to avoid placing elements on.\n */\ntemplate <size_t Dim, typename OptionsGroup>\nstruct ElementsAllocator\n    : tt::ConformsTo<Parallel::protocols::ArrayElementsAllocator> {\n  template <typename ElementArray>\n  using array_allocation_tags = tmpl::list<>;\n\n  template <typename ElementArray, typename Metavariables,\n            typename... InitializationTags>\n  static void apply(Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n                    const tuples::TaggedTuple<InitializationTags...>&\n                        original_initialization_items,\n                    const tuples::tagged_tuple_from_typelist<\n                        typename ElementArray::\n                            array_allocation_tags>& /*array_allocation_items*/\n                    = {},\n                    const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    // Copy the initialization items so we can adjust them on each refinement\n    // level\n    auto initialization_items =\n        serialize_and_deserialize<tuples::TaggedTuple<InitializationTags...>>(\n            original_initialization_items);\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& element_array =\n        Parallel::get_parallel_component<ElementArray>(local_cache);\n    const auto& domain = get<domain::Tags::Domain<Dim>>(local_cache);\n    const auto& initial_extents =\n        get<domain::Tags::InitialExtents<Dim>>(initialization_items);\n    auto& initial_refinement_levels =\n        get<domain::Tags::InitialRefinementLevels<Dim>>(initialization_items);\n    auto& children_refinement_levels =\n        get<Tags::ChildrenRefinementLevels<Dim>>(initialization_items);\n    auto& parent_refinement_levels =\n        get<Tags::ParentRefinementLevels<Dim>>(initialization_items);\n    const auto basis = Spectral::Basis::Legendre;\n    const auto& quadrature =\n        Parallel::get<elliptic::dg::Tags::Quadrature>(local_cache);\n    const std::optional<domain::ElementWeight>& element_weight =\n        get<domain::Tags::ElementDistribution>(local_cache);\n    std::optional<size_t> max_coarse_levels =\n        get<Tags::InitialCoarseLevels<OptionsGroup>>(local_cache);\n    const size_t number_of_procs =\n        Parallel::number_of_procs<size_t>(local_cache);\n    const size_t num_iterations =\n        get<Convergence::Tags::Iterations<OptionsGroup>>(local_cache);\n    if (UNLIKELY(num_iterations == 0)) {\n      Parallel::printf(\n          \"%s is disabled (zero iterations). Only creating a single grid.\\n\",\n          pretty_type::name<OptionsGroup>());\n      max_coarse_levels = 0;\n    }\n    const auto& blocks = domain.blocks();\n\n    // Determine number of initial multigrid levels\n    size_t num_levels = 1;\n    for (const auto& ref_levs : initial_refinement_levels) {\n      num_levels = std::max(\n          num_levels, *std::max_element(ref_levs.begin(), ref_levs.end()) + 1);\n    }\n    if (max_coarse_levels.has_value()) {\n      num_levels = std::min(num_levels, max_coarse_levels.value() + 1);\n    }\n\n    // Create the initial grids\n    for (int multigrid_level = static_cast<int>(num_levels) - 1;\n         multigrid_level >= 0; --multigrid_level) {\n      // Store the current grid as child grid before coarsening it\n      children_refinement_levels = initial_refinement_levels;\n      initial_refinement_levels = parent_refinement_levels;\n      // Construct coarsened (parent) grid\n      if (multigrid_level > 0) {\n        parent_refinement_levels =\n            LinearSolver::multigrid::coarsen(initial_refinement_levels);\n      }\n      // Create element IDs for all elements on this level\n      std::vector<ElementId<Dim>> element_ids{};\n      for (const auto& block : blocks) {\n        const std::vector<ElementId<Dim>> block_element_ids =\n            initial_element_ids(block.id(),\n                                initial_refinement_levels[block.id()],\n                                static_cast<size_t>(multigrid_level));\n        element_ids.insert(element_ids.begin(), block_element_ids.begin(),\n                           block_element_ids.end());\n      }\n      // Create the elements for this refinement level and distribute them among\n      // processors\n      const size_t num_of_procs_to_use =\n          static_cast<size_t>(sys::number_of_procs()) - procs_to_ignore.size();\n      // Distributed with weighted space filling curve\n      if (element_weight.has_value()) {\n        const std::unordered_map<ElementId<Dim>, double> element_costs =\n            domain::get_element_costs(blocks, initial_refinement_levels,\n                                      initial_extents, element_weight.value(),\n                                      basis, quadrature);\n        const domain::BlockZCurveProcDistribution<Dim> element_distribution{\n            element_costs,   num_of_procs_to_use,\n            blocks,          initial_refinement_levels,\n            initial_extents, procs_to_ignore};\n\n        for (const auto& element_id : element_ids) {\n          const size_t target_proc =\n              element_distribution.get_proc_for_element(element_id);\n          element_array(element_id)\n              .insert(global_cache, initialization_items, target_proc);\n        }\n      } else {\n        // Distributed with round-robin\n        size_t which_proc = 0;\n        for (const auto& element_id : element_ids) {\n          while (procs_to_ignore.find(which_proc) != procs_to_ignore.end()) {\n            which_proc = which_proc + 1 == number_of_procs ? 0 : which_proc + 1;\n          }\n\n          element_array(element_id)\n              .insert(global_cache, initialization_items, which_proc);\n\n          which_proc = which_proc + 1 == number_of_procs ? 0 : which_proc + 1;\n        }\n      }\n    }\n    element_array.doneInserting();\n  }\n};\n\n}  // namespace LinearSolver::multigrid\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/Hierarchy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Hierarchy.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace LinearSolver::multigrid {\n\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>> coarsen(\n    std::vector<std::array<size_t, Dim>> initial_refinement_levels) {\n  for (auto& block_refinement : initial_refinement_levels) {\n    for (size_t d = 0; d < Dim; ++d) {\n      auto& refinement_level = gsl::at(block_refinement, d);\n      if (refinement_level > 0) {\n        --refinement_level;\n      }\n    }\n  }\n  return initial_refinement_levels;\n}\n\ntemplate <size_t Dim>\nElementId<Dim> parent_id(const ElementId<Dim>& child_id) {\n  std::array<SegmentId, Dim> parent_segment_ids = child_id.segment_ids();\n  for (size_t d = 0; d < Dim; ++d) {\n    auto& segment_id = gsl::at(parent_segment_ids, d);\n    if (segment_id.refinement_level() > 0) {\n      segment_id = segment_id.id_of_parent();\n    }\n  }\n  ASSERT(child_id.grid_index() > 0,\n         \"Grid index must be positive to get a parent ID.\");\n  return {child_id.block_id(), std::move(parent_segment_ids),\n          child_id.grid_index() - 1};\n}\n\nnamespace {\nstd::unordered_set<SegmentId> child_segment_ids_impl(\n    const SegmentId& parent_segment_id,\n    const size_t children_refinement_level) {\n  ASSERT(parent_segment_id.refinement_level() == children_refinement_level or\n             (children_refinement_level > 0 and\n              parent_segment_id.refinement_level() ==\n                  children_refinement_level - 1),\n         \"The parent refinement level must be exactly 1 smaller than the \"\n         \"children refinement level, or the same. Parent refinement level: \"\n             << parent_segment_id.refinement_level()\n             << \", children refinement level: \" << children_refinement_level);\n  if (parent_segment_id.refinement_level() < children_refinement_level) {\n    return {parent_segment_id.id_of_child(Side::Lower),\n            parent_segment_id.id_of_child(Side::Upper)};\n  } else {\n    return {parent_segment_id};\n  }\n}\n}  // namespace\n\ntemplate <>\nstd::unordered_set<ElementId<1>> child_ids<1>(\n    const ElementId<1>& parent_id,\n    const std::array<size_t, 1>& children_refinement_levels) {\n  const std::unordered_set<SegmentId> child_segment_ids =\n      child_segment_ids_impl(parent_id.segment_id(0),\n                             children_refinement_levels[0]);\n  std::unordered_set<ElementId<1>> child_ids{};\n  for (const auto& child_segment_id : child_segment_ids) {\n    child_ids.emplace(parent_id.block_id(),\n                      std::array<SegmentId, 1>{child_segment_id},\n                      parent_id.grid_index() + 1);\n  }\n  return child_ids;\n}\n\ntemplate <>\nstd::unordered_set<ElementId<2>> child_ids<2>(\n    const ElementId<2>& parent_id,\n    const std::array<size_t, 2>& children_refinement_levels) {\n  std::array<std::unordered_set<SegmentId>, 2> child_segment_ids{};\n  for (size_t d = 0; d < 2; ++d) {\n    gsl::at(child_segment_ids, d) = child_segment_ids_impl(\n        parent_id.segment_id(d), gsl::at(children_refinement_levels, d));\n  }\n  std::unordered_set<ElementId<2>> child_ids{};\n  for (const auto& child_segment_id_x : child_segment_ids[0]) {\n    for (const auto& child_segment_id_y : child_segment_ids[1]) {\n      child_ids.emplace(\n          parent_id.block_id(),\n          std::array<SegmentId, 2>{{child_segment_id_x, child_segment_id_y}},\n          parent_id.grid_index() + 1);\n    }\n  }\n  return child_ids;\n}\n\ntemplate <>\nstd::unordered_set<ElementId<3>> child_ids<3>(\n    const ElementId<3>& parent_id,\n    const std::array<size_t, 3>& children_refinement_levels) {\n  std::array<std::unordered_set<SegmentId>, 3> child_segment_ids{};\n  for (size_t d = 0; d < 3; ++d) {\n    gsl::at(child_segment_ids, d) = child_segment_ids_impl(\n        parent_id.segment_id(d), gsl::at(children_refinement_levels, d));\n  }\n  std::unordered_set<ElementId<3>> child_ids{};\n  for (const auto& child_segment_id_x : child_segment_ids[0]) {\n    for (const auto& child_segment_id_y : child_segment_ids[1]) {\n      for (const auto& child_segment_id_z : child_segment_ids[2]) {\n        child_ids.emplace(\n            parent_id.block_id(),\n            std::array<SegmentId, 3>{\n                {child_segment_id_x, child_segment_id_y, child_segment_id_z}},\n            parent_id.grid_index() + 1);\n      }\n    }\n  }\n  return child_ids;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(r, data)                                                 \\\n  template std::vector<std::array<size_t, DIM(data)>> coarsen(               \\\n      std::vector<std::array<size_t, DIM(data)>> initial_refinement_levels); \\\n  template ElementId<DIM(data)> parent_id(const ElementId<DIM(data)>& child_id);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace LinearSolver::multigrid\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/Hierarchy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n#include <vector>\n\n/// \\cond\ntemplate <size_t Dim>\nstruct ElementId;\n/// \\endcond\n\nnamespace LinearSolver::multigrid {\n\n/*!\n * \\brief Coarsen the initial refinement levels of all blocks in the domain\n *\n * Simply decrement the refinement level uniformly over the entire domain.\n * Doesn't do anything for blocks that are already fully coarsened, so if the\n * return value equals the input argument the entire domain is fully coarsened.\n * Decrementing the refinement level means combining two elements into one,\n * thereby halving the number of elements per dimension.\n *\n * \\tparam Dim The spatial dimension of the domain\n * \\param initial_refinement_levels The refinement level in each block of the\n * domain and in every dimension.\n * \\return std::vector<std::array<size_t, Dim>> The coarsened refinement levels\n * by decrementing every entry in `initial_refinement_levels` unless it is\n * already zero.\n */\ntemplate <size_t Dim>\nstd::vector<std::array<size_t, Dim>> coarsen(\n    std::vector<std::array<size_t, Dim>> initial_refinement_levels);\n\n/*!\n * \\brief The element covering the `child_id` on the coarser grid.\n *\n * \\tparam Dim The spatial dimension of the domain\n * \\param child_id The ID of an element on the finer grid\n * \\return ElementId<Dim> The ID of the element on the coarser grid that\n * covers the `child_id`. This parent element covers at most two child elements\n * per dimension.\n */\ntemplate <size_t Dim>\nElementId<Dim> parent_id(const ElementId<Dim>& child_id);\n\n/*!\n * \\brief The elements covering the `parent_id` on the finer grid.\n *\n * \\tparam Dim The spatial dimension of the domain\n * \\param parent_id The ID of an element on the coarser grid\n * \\param children_refinement_levels The refinement level of the finer grid in\n * this block\n * \\return std::unordered_set<ElementId<Dim>> The IDs of the elements on the\n * finer grid that cover the `parent_id`. Returns at least one child\n * (if the grids have the same refinement levels) and at most\n * \\f$2^\\mathrm{Dim}\\f$ children (if the grid is finer in every dimension).\n */\ntemplate <size_t Dim>\nstd::unordered_set<ElementId<Dim>> child_ids(\n    const ElementId<Dim>& parent_id,\n    const std::array<size_t, Dim>& children_refinement_levels);\n\n}  // namespace LinearSolver::multigrid\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/Multigrid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/AsynchronousSolvers/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ObserveVolumeData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Items related to the multigrid linear solver\n///\n/// \\see `LinearSolver::multigrid::Multigrid`\nnamespace LinearSolver::multigrid {\n\n/// A label indicating the pre-smoothing step in a V-cycle multigrid algorithm,\n/// i.e. the smoothing step before sending the residual to the coarser (parent)\n/// grid\nstruct VcycleDownLabel {};\n\n/// A label indicating the post-smoothing step in a V-cycle multigrid algorithm,\n/// i.e. the smoothing step before sending the correction to the finer (child)\n/// grid\nstruct VcycleUpLabel {};\n\n/*!\n * \\brief A V-cycle geometric multgrid solver for linear equations \\f$Ax = b\\f$\n *\n * This linear solver iteratively corrects an initial guess \\f$x_0\\f$ by\n * restricting the residual \\f$b - Ax\\f$ to a series of coarser grids, solving\n * for a correction on the coarser grids, and then prolongating (interpolating)\n * the correction back to the finer grids. The solves on grids with different\n * scales can very effectively solve large-scale modes in the solution, which\n * Krylov-type linear solvers such as GMRES or Conjugate Gradients typically\n * struggle with. Therefore, a multigrid solver can be an effective\n * preconditioner for Krylov-type linear solvers (see\n * `LinearSolver::gmres::Gmres` and `LinearSolver::cg::ConjugateGradient`). See\n * \\cite Briggs2000jp for an introduction to multigrid methods.\n *\n * \\par Grid hierarchy\n * This geometric multigrid solver relies on a strategy to coarsen the\n * computational grid in a way that removes small-scale modes. We currently\n * h-coarsen the initial domain, meaning that we create multigrid levels by\n * successively combining two elements into one along every dimension of the\n * grid. We only p-coarsen the grid in the sense that we choose the smaller of\n * the two polynomial degrees when combining elements. This strategy follows\n * \\cite Vincent2019qpd. See `LinearSolver::multigrid::ElementsAllocator` and\n * `LinearSolver::multigrid::coarsen` for the code that creates the initial\n * multigrid hierarchy.\n * Once the initial grids are created, AMR steps can create incrementally finer\n * grids. For this to work, AMR must be configured with the compile-time option\n * `keep_coarse_grids = true` (see `amr::protocols::AmrMetavariables`).\n *\n * \\par Inter-mesh operators\n * The algorithm relies on operations that project data between grids. Residuals\n * are projected from finer to coarser grids (\"restriction\") and solutions are\n * projected from coarser to finer grids (\"prolongation\"). We use the standard\n * \\f$L_2\\f$-projections implemented in\n * `Spectral::projection_matrix_child_to_parent` and\n * `Spectral::projection_matrix_parent_to_child`, where \"child\" means the finer\n * grid and \"parent\" means the coarser grid. Note that the residuals \\f$b -\n * Ax\\f$ may or may not already include a mass matrix and Jacobian factors,\n * depending on the implementation of the linear operator \\f$A\\f$. To account\n * for this, provide a tag as the `ResidualIsMassiveTag` that holds a `bool`.\n * See section 3 in \\cite Fortunato2019jl for details on the inter-mesh\n * operators.\n *\n * \\par Smoother and bottom solver\n * On every level of the grid hierarchy the multigrid solver relies on a\n * \"smoother\" that performs an approximate linear solve on that level. The\n * smoother can be any linear solver that solves the `smooth_fields_tag` for the\n * source in the `smooth_source_tag`. Useful smoothers are asynchronous linear\n * solvers that parallelize well, such as `LinearSolver::Schwarz::Schwarz`. The\n * multigrid algorithm doesn't assume anything about the smoother, but requires\n * only that the `PreSmootherActions` and the `PostSmootherActions` passed to\n * `solve` leave the `smooth_fields_tag` in a state that represents an\n * approximate solution to the `smooth_source_tag`, and that\n * `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo,\n * smooth_fields_tag>` is left up-to-date as well.\n * The smoother can assume that, on entry, these two tags represent the initial\n * fields and the linear operator applied to the initial fields, respectively.\n * Here's an example of setting up a smoother for the multigrid solver:\n *\n * \\snippet Test_MultigridAlgorithm.cpp setup_smoother\n *\n * On the coarsest level of the grid hierarchy, the multigrid algorithm can also\n * use a separate \"bottom solver\". If enabled, the bottom solver replaces the\n * pre-smoother on the coarsest grid. The bottom solver can also be any linear\n * solver, but it is typically a direct solver that solves the linear problem\n * exactly (see `LinearSolver::Actions::BuildMatrix` for a good bottom solver).\n *\n * The smoother and bottom solver can be used to construct an action list like\n * this:\n *\n * \\snippet Test_MultigridAlgorithm.cpp action_list\n *\n * \\par Algorithm overview\n * Every iteration of the multigrid algorithm performs a V-cycle over the grid\n * hierarchy. One V-cycle consists of first \"going down\" the grid hierarchy,\n * from the finest to successively coarser grids, smoothing on every level\n * (\"pre-smoothing\", can be controlled by the option\n * `LinearSolver::multigrid::Tags::EnablePreSmoothing`),\n * and then \"going up\" the grid hierarchy again, smoothing on\n * every level again (\"post-smoothing\"). When going down, the algorithm projects\n * the remaining residual of the smoother to the next-coarser grid, setting it\n * as the source for the smoother on the coarser grid. When going up again, the\n * algorithm projects the solution of the smoother to the next-finer grid,\n * adding it to the solution on the finer grid as a correction. The bottom-most\n * coarsest grid (the \"tip\" of the V-cycle) may run the bottom solver instead of\n * the pre-smoother. It may also skip the post-smoother, so the result of the\n * bottom solver is immediately projected up to the finer grid (controlled by\n * the options `LinearSolver::multigrid::Tags::UseBottomSolver` and\n * `LinearSolver::multigrid::Tags::EnablePostSmoothingAtBottom`). On the\n * top-most finest grid (the \"original\" grid that represents the overall\n * solution) the algorithm applies the smoothing and the corrections from the\n * coarser grids directly to the solution fields.\n */\ntemplate <typename Metavariables, size_t Dim, typename FieldsTag,\n          typename OptionsGroup, typename ResidualIsMassiveTag,\n          typename SourceTag =\n              db::add_tag_prefix<::Tags::FixedSource, FieldsTag>>\nstruct Multigrid {\n  using fields_tag = FieldsTag;\n  using options_group = OptionsGroup;\n  using source_tag = SourceTag;\n\n  using operand_tag = FieldsTag;\n\n  using smooth_source_tag = source_tag;\n  using smooth_fields_tag = fields_tag;\n\n  using component_list = tmpl::list<>;\n\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<async_solvers::reduction_data>>;\n\n  using initialize_element = tmpl::list<\n      async_solvers::InitializeElement<FieldsTag, OptionsGroup, SourceTag>,\n      detail::InitializeElement<Dim, FieldsTag, OptionsGroup, SourceTag>>;\n\n  using amr_projectors = initialize_element;\n\n  using register_element = tmpl::list<\n      async_solvers::RegisterElement<FieldsTag, OptionsGroup, SourceTag,\n                                     ::amr::Tags::IsFinestGrid>,\n      observers::Actions::RegisterWithObservers<\n          detail::RegisterWithVolumeObserver<OptionsGroup>>>;\n\n  template <typename ApplyOperatorActions, typename PreSmootherActions,\n            typename PostSmootherActions,\n            typename BottomSolverActions = tmpl::list<>,\n            typename Label = OptionsGroup>\n  using solve = tmpl::list<\n      async_solvers::PrepareSolve<FieldsTag, OptionsGroup, SourceTag, Label,\n                                  ::amr::Tags::IsFinestGrid, false>,\n      detail::ReceiveResidualFromFinerGrid<Dim, FieldsTag, OptionsGroup,\n                                           SourceTag>,\n      detail::PreparePreSmoothing<\n          FieldsTag, OptionsGroup, SourceTag,\n          not std::is_same_v<BottomSolverActions, tmpl::list<>>>,\n      // No need to apply the linear operator here:\n      // - On the finest grid, the operator applied to the fields should have\n      //   already been computed at this point, either applied to the initial\n      //   fields before the multigrid solver is invoked, or by the smoother at\n      //   the end of the previous V-cycle.\n      // - On coarser grids, the initial fields are zero, so the operator\n      //   applied to them is also zero.\n      PreSmootherActions,\n      detail::SkipBottomSolver<FieldsTag, OptionsGroup, SourceTag>,\n      BottomSolverActions,\n      detail::SkipPostSmoothingAtBottom<FieldsTag, OptionsGroup, SourceTag>,\n      detail::SendResidualToCoarserGrid<FieldsTag, OptionsGroup,\n                                        ResidualIsMassiveTag, SourceTag>,\n      detail::ReceiveCorrectionFromCoarserGrid<Dim, FieldsTag, OptionsGroup,\n                                               SourceTag>,\n      ApplyOperatorActions, ::Actions::Label<detail::PostSmoothingBeginLabel>,\n      PostSmootherActions,\n      detail::SendCorrectionToFinerGrid<FieldsTag, OptionsGroup, SourceTag>,\n      detail::ObserveVolumeData<FieldsTag, OptionsGroup, SourceTag>,\n      async_solvers::CompleteStep<FieldsTag, OptionsGroup, SourceTag, Label,\n                                  ::amr::Tags::IsFinestGrid, false>>;\n};\n\n}  // namespace LinearSolver::multigrid\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/ObserveVolumeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::multigrid::detail {\n\ntemplate <typename OptionsGroup>\nstruct RegisterWithVolumeObserver {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& box,\n                const ArrayIndex& /*array_index*/) {\n    const std::string& level_observation_key =\n        *db::get<observers::Tags::ObservationKey<::amr::Tags::GridIndex>>(box);\n    const std::string subfile_path =\n        \"/\" + pretty_type::name<OptionsGroup>() + level_observation_key;\n    return {observers::TypeOfObservation::Volume,\n            observers::ObservationKey(subfile_path)};\n  }\n};\n\n// Contribute the volume data recorded in the other actions to the observer at\n// the end of a step.\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct ObserveVolumeData {\n private:\n  using volume_data_tag = Tags::VolumeDataForOutput<OptionsGroup, FieldsTag>;\n  using VolumeDataVars = typename volume_data_tag::type;\n\n public:\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (not db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    const auto& volume_data = db::get<volume_data_tag>(box);\n    const auto& observation_id =\n        db::get<LinearSolver::Tags::ObservationId<OptionsGroup>>(box);\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    const auto& inertial_coords =\n        db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box);\n    // Collect tensor components to observe\n    std::vector<TensorComponent> components{};\n    components.reserve(inertial_coords.size() +\n                       VolumeDataVars::number_of_independent_components);\n    const auto record_tensor_components = [&components](const auto tensor_tag_v,\n                                                        const auto& tensor) {\n      using tensor_tag = std::decay_t<decltype(tensor_tag_v)>;\n      using TensorType = std::decay_t<decltype(tensor)>;\n      using VectorType = typename TensorType::type;\n      using ValueType = typename VectorType::value_type;\n      for (size_t i = 0; i < tensor.size(); ++i) {\n        const std::string component_name =\n            db::tag_name<tensor_tag>() + tensor.component_suffix(i);\n        if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n          components.emplace_back(\"Re(\" + component_name + \")\",\n                                  real(tensor[i]));\n          components.emplace_back(\"Im(\" + component_name + \")\",\n                                  imag(tensor[i]));\n        } else {\n          components.emplace_back(component_name, tensor[i]);\n        }\n      }\n    };\n    record_tensor_components(domain::Tags::Coordinates<Dim, Frame::Inertial>{},\n                             inertial_coords);\n    tmpl::for_each<typename VolumeDataVars::tags_list>(\n        [&volume_data, &record_tensor_components](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          record_tensor_components(tag{}, get<tag>(volume_data));\n        });\n\n    // Contribute tensor components to observer\n    const auto& level_observation_key =\n        *db::get<observers::Tags::ObservationKey<::amr::Tags::GridIndex>>(box);\n    const std::string subfile_path =\n        \"/\" + pretty_type::name<OptionsGroup>() + level_observation_key;\n    observers::contribute_volume_data<\n        not Parallel::is_nodegroup_v<ParallelComponent>>(\n        cache, observers::ObservationId(observation_id, subfile_path),\n        subfile_path,\n        Parallel::make_array_component_id<ParallelComponent>(element_id),\n        ElementVolumeData{element_id, std::move(components), mesh});\n\n    // Increment observation ID\n    db::mutate<LinearSolver::Tags::ObservationId<OptionsGroup>>(\n        [](const auto local_observation_id) { ++(*local_observation_id); },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace LinearSolver::multigrid::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::multigrid {\n\nnamespace OptionTags {\n\ntemplate <typename OptionsGroup>\nstruct InitialCoarseLevels {\n  using type = Options::Auto<size_t>;\n  static constexpr Options::String help =\n      \"Maximum number of coarser levels in the initial multigrid hierarchy. \"\n      \"Set to '0' to create no coarser levels initially. Set to 'Auto' to \"\n      \"coarsen all the way up to single-element blocks. \"\n      \"AMR may create additional levels later.\";\n  using group = OptionsGroup;\n};\n\ntemplate <typename OptionsGroup>\nstruct OutputVolumeData {\n  using type = bool;\n  static constexpr Options::String help =\n      \"Record volume data for debugging purposes.\";\n  using group = OptionsGroup;\n  static bool suggested_value() { return false; }\n};\n\ntemplate <typename OptionsGroup>\nstruct EnablePreSmoothing {\n  static std::string name() { return \"PreSmoothing\"; }\n  using type = bool;\n  static constexpr Options::String help =\n      \"Set to 'False' to disable pre-smoothing altogether (\\\"cascading \"\n      \"multigrid\\\"). Note that pre-smoothing can be necessary to remove \"\n      \"high-frequency modes in the data that get restricted to coarser grids, \"\n      \"since such high-frequency modes can introduce aliasing. However, when \"\n      \"running only a single V-cycle as preconditioner, the initial field is \"\n      \"typically zero, so pre-smoothing may not be worthwile.\";\n  using group = OptionsGroup;\n};\n\ntemplate <typename OptionsGroup>\nstruct UseBottomSolver {\n  using type = bool;\n  static constexpr Options::String help =\n      \"Set to 'True' to use a separate bottom solver on the coarsest level \"\n      \"instead of pre-smoothing. The bottom solver typically builds the matrix \"\n      \"explicitly and inverts it directly. Use the bottom solver if \"\n      \"pre-smoothing is not sufficiently effective on the bottom grid. \"\n      \"The bottom solver can significantly cut down the number of iterations \"\n      \"needed to converge to the solution at the cost of building the matrix \"\n      \"explicitly and inverting it directly on the coarsest level.\";\n  using group = OptionsGroup;\n};\n\ntemplate <typename OptionsGroup>\nstruct EnablePostSmoothingAtBottom {\n  static std::string name() { return \"PostSmoothingAtBottom\"; }\n  using type = bool;\n  static constexpr Options::String help =\n      \"Set to 'False' to skip post-smoothing on the coarsest grid. This means \"\n      \"only pre-smoothing runs on the coarsest grid, so the coarsest grid \"\n      \"experiences less smoothing altogether. This is typically only \"\n      \"desirable if the coarsest grid covers the domain with a single \"\n      \"element, or very few, so pre-smoothing is already exceptionally \"\n      \"effective and hence post-smoothing is unnecessary on the coarsest grid.\";\n  using group = OptionsGroup;\n};\n\n}  // namespace OptionTags\n\n/// DataBox tags for the `LinearSolver::multigrid::Multigrid` linear solver\nnamespace Tags {\n\n/// Initial refinement of the next-finer (child) grid\ntemplate <size_t Dim>\nstruct ChildrenRefinementLevels : db::SimpleTag {\n private:\n  using base = domain::Tags::InitialRefinementLevels<Dim>;\n\n public:\n  using type = typename base::type;\n  static constexpr bool pass_metavariables = base::pass_metavariables;\n  using option_tags = typename base::option_tags;\n  static constexpr auto create_from_options = base::create_from_options;\n};\n\n/// Initial refinement of the next-coarser (parent) grid\ntemplate <size_t Dim>\nstruct ParentRefinementLevels : db::SimpleTag {\n private:\n  using base = domain::Tags::InitialRefinementLevels<Dim>;\n\n public:\n  using type = typename base::type;\n  static constexpr bool pass_metavariables = base::pass_metavariables;\n  using option_tags = typename base::option_tags;\n  static constexpr auto create_from_options = base::create_from_options;\n};\n\n/// Maximum number of multigrid levels that will be created. A value of '1'\n/// effectively disables the multigrid, and `std::nullopt` means the number\n/// of multigrid levels is not capped.\ntemplate <typename OptionsGroup>\nstruct InitialCoarseLevels : db::SimpleTag {\n  using type = std::optional<size_t>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::InitialCoarseLevels<OptionsGroup>>;\n  static type create_from_options(const type value) { return value; };\n  static std::string name() {\n    return \"InitialCoarseLevels(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n};\n\n/// Whether or not volume data should be recorded for debugging purposes\ntemplate <typename OptionsGroup>\nstruct OutputVolumeData : db::SimpleTag {\n  using type = bool;\n  static constexpr bool pass_metavariables = false;\n  using option_tags =\n      tmpl::list<LinearSolver::OptionTags::OutputVolumeData<OptionsGroup>>;\n  static type create_from_options(const type value) { return value; };\n  static std::string name() {\n    return \"OutputVolumeData(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n};\n\n/// Enable pre-smoothing. A value of `false` means that pre-smoothing is skipped\n/// altogether (\"cascading multigrid\").\ntemplate <typename OptionsGroup>\nstruct EnablePreSmoothing : db::SimpleTag {\n  using type = bool;\n  static constexpr bool pass_metavariables = false;\n  using option_tags =\n      tmpl::list<OptionTags::EnablePreSmoothing<OptionsGroup>>;\n  static type create_from_options(const type value) { return value; };\n  static std::string name() {\n    return \"EnablePreSmoothing(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n};\n\n/// Enable the bottom solver\ntemplate <typename OptionsGroup>\nstruct UseBottomSolver : db::SimpleTag {\n  using type = bool;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::UseBottomSolver<OptionsGroup>>;\n  static type create_from_options(const type value) { return value; };\n  static std::string name() {\n    return \"UseBottomSolver(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n};\n\n/// Enable post-smoothing on the coarsest grid. A value of `false` means that\n/// post-smoothing is skipped on the coarsest grid, so it runs only\n/// pre-smoothing.\ntemplate <typename OptionsGroup>\nstruct EnablePostSmoothingAtBottom : db::SimpleTag {\n  using type = bool;\n  static constexpr bool pass_metavariables = false;\n  using option_tags =\n      tmpl::list<OptionTags::EnablePostSmoothingAtBottom<OptionsGroup>>;\n  static type create_from_options(const type value) { return value; };\n  static std::string name() {\n    return \"EnablePostSmoothingAtBottom(\" + pretty_type::name<OptionsGroup>() +\n           \")\";\n  }\n};\n\n// The following tags are related to volume data output\n\n/// @{\n/// Prefix tag for recording volume data in\n/// `LinearSolver::multigrid::Tags::VolumeDataForOutput`\ntemplate <typename Tag>\nstruct PreSmoothingInitial : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\ntemplate <typename Tag>\nstruct PreSmoothingSource : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\ntemplate <typename Tag>\nstruct PreSmoothingResult : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\ntemplate <typename Tag>\nstruct PreSmoothingResidual : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\ntemplate <typename Tag>\nstruct PostSmoothingInitial : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\ntemplate <typename Tag>\nstruct PostSmoothingSource : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\ntemplate <typename Tag>\nstruct PostSmoothingResult : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\ntemplate <typename Tag>\nstruct PostSmoothingResidual : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n/// @}\n/// Buffer for recording volume data\ntemplate <typename OptionsGroup, typename FieldsTag>\nstruct VolumeDataForOutput : db::SimpleTag {\n  using fields_tags = typename FieldsTag::type::tags_list;\n  using type = Variables<\n      tmpl::append<db::wrap_tags_in<PreSmoothingInitial, fields_tags>,\n                   db::wrap_tags_in<PreSmoothingSource, fields_tags>,\n                   db::wrap_tags_in<PreSmoothingResult, fields_tags>,\n                   db::wrap_tags_in<PreSmoothingResidual, fields_tags>,\n                   db::wrap_tags_in<PostSmoothingInitial, fields_tags>,\n                   db::wrap_tags_in<PostSmoothingSource, fields_tags>,\n                   db::wrap_tags_in<PostSmoothingResult, fields_tags>,\n                   db::wrap_tags_in<PostSmoothingResidual, fields_tags>>>;\n  static std::string name() {\n    return \"VolumeDataForOutput(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n};\n\n}  // namespace Tags\n}  // namespace LinearSolver::multigrid\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Observe.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace LinearSolver {\nnamespace observe_detail {\n\n/*!\n * \\brief Contributes data from the residual monitor to the reduction observer\n */\ntemplate <typename OptionsGroup, typename ParallelComponent,\n          typename Metavariables>\nvoid contribute_to_reduction_observer(\n    const size_t iteration_id, const double residual_magnitude,\n    Parallel::GlobalCache<Metavariables>& cache) {\n  auto& reduction_writer = Parallel::get_parallel_component<\n      observers::ObserverWriter<Metavariables>>(cache);\n  Parallel::threaded_action<observers::ThreadedActions::WriteReductionDataRow>(\n      // Node 0 is always the writer, so directly call the component on that\n      // node\n      reduction_writer[0],\n      // When multiple linear solves are performed, e.g. for the nonlinear\n      // solver, we'll need to write into separate subgroups, e.g.:\n      // `/linear_residuals/<nonlinear_iteration_id>`\n      std::string{\"/\" + pretty_type::name<OptionsGroup>() + \"Residuals\"},\n      std::vector<std::string>{\"Iteration\", \"Walltime\", \"Residual\"},\n      std::make_tuple(iteration_id, sys::wall_time(), residual_magnitude));\n}\n\n}  // namespace observe_detail\n}  // namespace LinearSolver\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Richardson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Richardson.hpp\n  Tags.hpp\n  )\n\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Richardson/Richardson.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/AsynchronousSolvers/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Richardson/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\n/// Items related to the %Richardson linear solver\n///\n/// \\see `LinearSolver::Richardson::Richardson`\nnamespace LinearSolver::Richardson {\n\nnamespace detail {\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct UpdateFields {\n private:\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<Tags::RelaxationParameter<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Update the solution fields according to the Richardson scheme\n    db::mutate<FieldsTag>(\n        [](const auto fields, const auto& residual,\n           const double relaxation_parameter) {\n          *fields += relaxation_parameter * residual;\n        },\n        make_not_null(&box), get<residual_tag>(box),\n        get<Tags::RelaxationParameter<OptionsGroup>>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace detail\n\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief A simple %Richardson scheme for solving a system of linear equations\n * \\f$Ax=b\\f$\n *\n * \\warning This linear solver is useful only for basic preconditioning of\n * another linear solver or for testing purposes. See\n * `LinearSolver::cg::ConjugateGradient` or `LinearSolver::gmres::Gmres` for\n * more useful general-purpose linear solvers.\n *\n * In each step the solution is updated from its initial state \\f$x_0\\f$ as\n *\n * \\f[\n * x_{k+1} = x_k + \\omega \\left(b - Ax\\right)\n * \\f]\n *\n * where \\f$\\omega\\f$ is a _relaxation parameter_ that weights the residual.\n *\n * The scheme converges if the spectral radius (i.e. the largest absolute\n * eigenvalue) of the iteration operator \\f$G=1-\\omega A\\f$ is smaller than one.\n * For symmetric positive definite (SPD) matrices \\f$A\\f$ with largest\n * eigenvalue \\f$\\lambda_\\mathrm{max}\\f$ and smallest eigenvalue\n * \\f$\\lambda_\\mathrm{min}\\f$ choose\n *\n * \\f[\n * \\omega_\\mathrm{SPD,optimal} = \\frac{2}{\\lambda_\\mathrm{max} +\n * \\lambda_\\mathrm{min}}\n * \\f]\n *\n * for optimal convergence.\n *\n * \\par Array sections\n * This linear solver requires no synchronization between elements, so it runs\n * on all elements in the array parallel component. Partitioning of the elements\n * in sections is only relevant for observing residual norms. Pass the section\n * ID tag for the `ArraySectionIdTag` template parameter if residual norms\n * should be computed over a section. Pass `void` (default) to compute residual\n * norms over all elements in the array.\n */\ntemplate <typename FieldsTag, typename OptionsGroup,\n          typename SourceTag =\n              db::add_tag_prefix<::Tags::FixedSource, FieldsTag>,\n          typename ArraySectionIdTag = void>\nstruct Richardson {\n  using fields_tag = FieldsTag;\n  using options_group = OptionsGroup;\n  using source_tag = SourceTag;\n  using operand_tag = fields_tag;\n  using component_list = tmpl::list<>;\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<async_solvers::reduction_data>>;\n  using initialize_element =\n      async_solvers::InitializeElement<FieldsTag, OptionsGroup, SourceTag>;\n  using amr_projectors = initialize_element;\n  using register_element =\n      async_solvers::RegisterElement<FieldsTag, OptionsGroup, SourceTag,\n                                     ArraySectionIdTag>;\n  template <typename ApplyOperatorActions, typename Label = OptionsGroup>\n  using solve =\n      tmpl::list<async_solvers::PrepareSolve<FieldsTag, OptionsGroup, SourceTag,\n                                             Label, ArraySectionIdTag>,\n                 detail::UpdateFields<FieldsTag, OptionsGroup, SourceTag>,\n                 ApplyOperatorActions,\n                 async_solvers::CompleteStep<FieldsTag, OptionsGroup, SourceTag,\n                                             Label, ArraySectionIdTag>>;\n};\n}  // namespace LinearSolver::Richardson\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Richardson/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::Richardson {\n\nnamespace OptionTags {\n\ntemplate <typename OptionsGroup>\nstruct RelaxationParameter {\n  using type = double;\n  using group = OptionsGroup;\n  static constexpr Options::String help =\n      \"The weight for the residual in the scheme\";\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n\n/// The Richardson relaxation parameter \\f$\\omega\\f$\n///\n/// \\see `LinearSolver::Richardson::Richardson`\ntemplate <typename OptionsGroup>\nstruct RelaxationParameter : db::SimpleTag {\n  static std::string name() {\n    return \"RelaxationParameter(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = double;\n\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::RelaxationParameter<OptionsGroup>>;\n  static double create_from_options(const double value) { return value; }\n};\n\n}  // namespace Tags\n\n}  // namespace LinearSolver::Richardson\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CommunicateOverlapFields.hpp\n  ResetSubdomainSolver.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/Actions/CommunicateOverlapFields.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Actions for communicating data on regions that overlap with the subdomains\n/// of other elements\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nstruct ElementId;\n/// \\endcond\n\nnamespace LinearSolver::Schwarz {\n/// Actions related to the Schwarz solver\nnamespace Actions {\n\nnamespace detail {\ntemplate <size_t Dim, typename OverlapFields, typename OptionsGroup>\nstruct OverlapFieldsTag\n    : public Parallel::InboxInserters::Map<\n          OverlapFieldsTag<Dim, OverlapFields, OptionsGroup>> {\n  using temporal_id = size_t;\n  using type = std::map<\n      temporal_id,\n      OverlapMap<Dim, tmpl::conditional_t<\n                          (tmpl::size<OverlapFields>::value > 1),\n                          tuples::tagged_tuple_from_typelist<OverlapFields>,\n                          typename tmpl::front<OverlapFields>::type>>>;\n};\n}  // namespace detail\n\n/*!\n * \\brief Send data on regions that overlap with other subdomains to their\n * corresponding elements\n *\n * Collect the `OverlapFields` on \"intruding overlaps\", i.e. regions that\n * overlap with the subdomains of other elements, and send the data to those\n * elements. The `OverlapFields` can be tags holding either `Variables` or\n * `Tensor`s. The `RestrictToOverlap` flag controls whether the tags are simply\n * retrieved from the element and sent as-is (`false`) or only the data that\n * intersect the overlap region are sent (`true`). If `RestrictToOverlap` is\n * `false` this action can also be used to communicate non-tensor data.\n *\n * This actions should be followed by\n * `LinearSolver::Schwarz::Actions::ReceiveOverlapFields` in the action list.\n */\ntemplate <typename OverlapFields, typename OptionsGroup, bool RestrictToOverlap,\n          typename TemporalIdTag = Convergence::Tags::IterationId<OptionsGroup>>\nstruct SendOverlapFields;\n\n/// \\cond\ntemplate <typename... OverlapFields, typename OptionsGroup,\n          bool RestrictToOverlap, typename TemporalIdTag>\nstruct SendOverlapFields<tmpl::list<OverlapFields...>, OptionsGroup,\n                         RestrictToOverlap, TemporalIdTag> {\n  using const_global_cache_tags =\n      tmpl::list<Tags::MaxOverlap<OptionsGroup>,\n                 logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& element = get<domain::Tags::Element<Dim>>(box);\n\n    // Skip communicating if the overlap is empty and we RestrictToOverlap,\n    // because we would end up with empty tensor data.\n    if (UNLIKELY((RestrictToOverlap and\n                  db::get<Tags::MaxOverlap<OptionsGroup>>(box) == 0) or\n                 element.number_of_neighbors() == 0)) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    // Do some logging\n    const auto& iteration_id = get<TemporalIdTag>(box);\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Send overlap fields\\n\", element_id,\n                       pretty_type::name<OptionsGroup>(), iteration_id);\n    }\n\n    // Send data on intruding overlaps to the corresponding neighbors\n    auto& receiver_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    for (const auto& direction_and_neighbors : element.neighbors()) {\n      const auto& direction = direction_and_neighbors.first;\n      const auto& neighbors = direction_and_neighbors.second;\n      // Collect the data on intruding overlaps\n      tuples::TaggedTuple<OverlapFields...> overlap_fields{};\n      if constexpr (RestrictToOverlap) {\n        const auto& element_extents =\n            get<domain::Tags::Mesh<Dim>>(box).extents();\n        const size_t intruding_extent =\n            gsl::at(get<Tags::IntrudingExtents<Dim, OptionsGroup>>(box),\n                    direction.dimension());\n        expand_pack((get<OverlapFields>(overlap_fields) =\n                         LinearSolver::Schwarz::data_on_overlap(\n                             db::get<OverlapFields>(box), element_extents,\n                             intruding_extent, direction))...);\n      } else {\n        expand_pack((get<OverlapFields>(overlap_fields) =\n                         db::get<OverlapFields>(box))...);\n      }\n      // We elide the tagged tuple in the inbox tag if only a single tag is\n      // communicated. This optimization allows moving the overlap-map into the\n      // DataBox in one piece.\n      auto& collapsed_overlap_fields = [&overlap_fields]() -> auto& {\n        if constexpr (sizeof...(OverlapFields) > 1) {\n          return overlap_fields;\n        } else {\n          return get<OverlapFields...>(overlap_fields);\n        }\n      }\n      ();\n      // Copy data to send to neighbors, but move it for the last one\n      for (auto neighbor = neighbors.begin(); neighbor != neighbors.end();\n           ++neighbor) {\n        const auto direction_from_neighbor =\n            neighbors.orientation(*neighbor)(direction.opposite());\n        Parallel::receive_data<detail::OverlapFieldsTag<\n            Dim, tmpl::list<OverlapFields...>, OptionsGroup>>(\n            receiver_proxy[*neighbor], iteration_id,\n            std::make_pair(\n                OverlapId<Dim>{direction_from_neighbor, element.id()},\n                (std::next(neighbor) == neighbors.end())\n                    // NOLINTNEXTLINE(bugprone-use-after-move)\n                    ? std::move(collapsed_overlap_fields)\n                    : collapsed_overlap_fields));\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n/// \\endcond\n\n/*!\n * \\brief Receive data from regions of this element's subdomain that overlap\n * with other elements\n *\n * This action waits until overlap data from all neighboring elements has been\n * received and then moves the data into the DataBox as\n * `LinearSolver::Schwarz::Tags::Overlaps<OverlapFields...>`.\n *\n * This actions should be preceded by\n * `LinearSolver::Schwarz::Actions::SendOverlapFields` in the action list.\n */\ntemplate <size_t Dim, typename OverlapFields, typename OptionsGroup,\n          bool RestrictToOverlap,\n          typename TemporalIdTag = Convergence::Tags::IterationId<OptionsGroup>>\nstruct ReceiveOverlapFields;\n\n/// \\cond\ntemplate <size_t Dim, typename... OverlapFields, typename OptionsGroup,\n          bool RestrictToOverlap, typename TemporalIdTag>\nstruct ReceiveOverlapFields<Dim, tmpl::list<OverlapFields...>, OptionsGroup,\n                            RestrictToOverlap, TemporalIdTag> {\n private:\n  using overlap_fields_tag =\n      detail::OverlapFieldsTag<Dim, tmpl::list<OverlapFields...>, OptionsGroup>;\n\n public:\n  using simple_tags =\n      tmpl::list<Tags::Overlaps<OverlapFields, Dim, OptionsGroup>...>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::MaxOverlap<OptionsGroup>,\n                 logging::Tags::Verbosity<OptionsGroup>>;\n  using inbox_tags = tmpl::list<overlap_fields_tag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& iteration_id = get<TemporalIdTag>(box);\n    const auto& element = get<domain::Tags::Element<Dim>>(box);\n\n    // Nothing to receive if nothing was sent in the action above\n    if (UNLIKELY((RestrictToOverlap and\n                  db::get<Tags::MaxOverlap<OptionsGroup>>(box) == 0) or\n                 element.number_of_neighbors() == 0)) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    if (not dg::has_received_from_all_mortars<overlap_fields_tag>(\n            iteration_id, element, inboxes)) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Do some logging\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Receive overlap fields\\n\", element_id,\n                       pretty_type::name<OptionsGroup>(), iteration_id);\n    }\n\n    // Move received overlap data into DataBox\n    auto received_overlap_fields =\n        std::move(tuples::get<overlap_fields_tag>(inboxes)\n                      .extract(iteration_id)\n                      .mapped());\n    db::mutate<Tags::Overlaps<OverlapFields, Dim, OptionsGroup>...>(\n        [&received_overlap_fields](const auto... local_overlap_fields) {\n          if constexpr (sizeof...(OverlapFields) > 1) {\n            (local_overlap_fields->clear(), ...);\n            for (auto& [overlap_id, overlap_fields] : received_overlap_fields) {\n              expand_pack((*local_overlap_fields)[overlap_id] =\n                              std::move(get<OverlapFields>(overlap_fields))...);\n            }\n          } else {\n            expand_pack((*local_overlap_fields =\n                             std::move(received_overlap_fields))...);\n          }\n        },\n        make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n/// \\endcond\n\n}  // namespace Actions\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/Actions/ResetSubdomainSolver.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nstruct GlobalCache;\n}  // namespace Parallel\nnamespace tuples {\ntemplate <typename...>\nstruct TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace LinearSolver::Schwarz::Actions {\n\n/*!\n * \\brief Reset the subdomain solver, clearing its caches related to the linear\n * operator it has solved so far.\n *\n * Invoke this action when the linear operator has changed. For example, an\n * operator representing the linearization of a nonlinear operator changes\n * whenever the point around which it is being linearized gets updated, i.e. in\n * every iteration of a nonlinear solve. Note that the operator does _not_\n * change between iterations of a linear solve, so make sure you place this\n * action _outside_ the looping action list for the linear solve.\n *\n * \\par Skipping the reset:\n * Depending on the subdomain solver in use, the reset may incur significant\n * re-initialization cost the next time the subdomain solver is invoked. See the\n * `LinearSolver::Serial::ExplicitInverse` solver for an example of a subdomain\n * solver with high initialization cost. For this reason the reset can be\n * skipped with the option\n * `LinearSolver::Schwarz::Tags::SkipSubdomainSolverResets`. Skipping resets\n * means that caches built up for the linear operator are never cleared, so\n * expensive re-initializations are avoided but the subdomain solves may be\n * increasingly inaccurate or slow down as the linear operator changes over\n * nonlinear solver iterations. Whether or not skipping resets helps with the\n * overall convergence of the solve is highly problem-dependent. A possible\n * optimization would be to decide at runtime whether or not to reset the\n * subdomain solver.\n */\ntemplate <typename SubdomainSolverType, typename OptionsGroup>\nstruct ResetSubdomainSolver {\n  using const_global_cache_tags = tmpl::list<\n      LinearSolver::Schwarz::Tags::SkipSubdomainSolverResets<OptionsGroup>,\n      logging::Tags::Verbosity<OptionsGroup>>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (not get<LinearSolver::Schwarz::Tags::SkipSubdomainSolverResets<\n            OptionsGroup>>(box)) {\n      if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                   ::Verbosity::Debug)) {\n        Parallel::printf(\"%s %s: Reset subdomain solver\\n\", element_id,\n                         pretty_type::name<OptionsGroup>());\n      }\n      db::mutate<LinearSolver::Schwarz::Tags::SubdomainSolver<\n          SubdomainSolverType, OptionsGroup>>(\n          [](const auto subdomain_solver) {\n            // Dereference the gsl::not_null pointer, and then the\n            // std::unique_ptr for the subdomain solver's abstract superclass.\n            // This needs adjustment if the subdomain solver is stored in the\n            // DataBox directly as a derived class and thus there's no\n            // std::unique_ptr. Note that std::unique_ptr also has a `reset`\n            // function, which must not be confused with the serial linear\n            // solver's `reset` function here.\n            (*subdomain_solver)->reset();\n          },\n          make_not_null(&box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace LinearSolver::Schwarz::Actions\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ParallelSchwarz)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  OverlapHelpers.cpp\n  Weighting.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComputeTags.hpp\n  ElementActions.hpp\n  ElementCenteredSubdomainData.hpp\n  ObserveVolumeData.hpp\n  OverlapHelpers.hpp\n  Schwarz.hpp\n  SubdomainOperator.hpp\n  Tags.hpp\n  Weighting.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DomainStructure\n  ErrorHandling\n  Utilities\n  INTERFACE\n  Boost::boost\n  Convergence\n  DiscontinuousGalerkin\n  Domain\n  Initialization\n  LinearSolver\n  Logging\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelLinearSolver\n  Printf\n  Spectral\n  )\n\nadd_subdirectory(Actions)\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/ComputeTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::Schwarz::Tags {\n\n/*!\n * \\brief A diagnostic quantity to check that weights are conserved\n *\n * \\see `LinearSolver::Schwarz::Tags::SummedIntrudingOverlapWeights`\n */\ntemplate <size_t Dim, typename OptionsGroup>\nstruct SummedIntrudingOverlapWeightsCompute\n    : db::ComputeTag,\n      SummedIntrudingOverlapWeights<OptionsGroup> {\n  using base = SummedIntrudingOverlapWeights<OptionsGroup>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<domain::Tags::Interface<domain::Tags::InternalDirections<Dim>,\n                                         Weight<OptionsGroup>>,\n                 domain::Tags::Mesh<Dim>, IntrudingExtents<Dim, OptionsGroup>>;\n  static void function(\n      const gsl::not_null<return_type*> summed_intruding_overlap_weights,\n      const std::unordered_map<Direction<Dim>, Scalar<DataVector>>&\n          all_intruding_weights,\n      const Mesh<Dim>& mesh,\n      const std::array<size_t, Dim>& all_intruding_extents) {\n    set_number_of_grid_points(summed_intruding_overlap_weights,\n                              mesh.number_of_grid_points());\n    get(*summed_intruding_overlap_weights) = 0.;\n    for (const auto& [direction, intruding_weight] : all_intruding_weights) {\n      // Extend intruding weight to full extents\n      // There's not function to extend a single tensor, so we create a\n      // temporary Variables. This is only a diagnostic quantity that won't be\n      // used in production code, so it doesn't need to be particularly\n      // efficient and we don't need to add an overload of\n      // `LinearSolver::Schwarz::extended_overlap_data` just for this purpose.\n      using temp_tag = ::Tags::TempScalar<0>;\n      Variables<tmpl::list<temp_tag>> temp_vars{get(intruding_weight).size()};\n      get<temp_tag>(temp_vars) = intruding_weight;\n      temp_vars = LinearSolver::Schwarz::extended_overlap_data(\n          temp_vars, mesh.extents(),\n          gsl::at(all_intruding_extents, direction.dimension()), direction);\n      // Contribute to conserved weight\n      get(*summed_intruding_overlap_weights) += get(get<temp_tag>(temp_vars));\n    }\n  }\n};\n\n}  // namespace LinearSolver::Schwarz::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/ElementActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/Protocols/ReductionDataFormatter.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/ExplicitInverse.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/Gmres.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/AsynchronousSolvers/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Actions/CommunicateOverlapFields.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Weighting.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace LinearSolver::Schwarz::detail {\n\nusing reduction_data = Parallel::ReductionData<\n    // Iteration\n    Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n    // Number of subdomains (= number of elements)\n    Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n    // Average number of subdomain solver iterations\n    Parallel::ReductionDatum<double, funcl::Plus<>, funcl::Divides<>,\n                             std::index_sequence<1>>,\n    // Minimum number of subdomain solver iterations\n    Parallel::ReductionDatum<size_t, funcl::Min<>>,\n    // Maximum number of subdomain solver iterations\n    Parallel::ReductionDatum<size_t, funcl::Max<>>,\n    // Total number of subdomain solver iterations\n    Parallel::ReductionDatum<size_t, funcl::Plus<>>>;\n\ntemplate <typename OptionsGroup>\nstruct SubdomainStatsFormatter\n    : tt::ConformsTo<observers::protocols::ReductionDataFormatter> {\n  using reduction_data = Schwarz::detail::reduction_data;\n  SubdomainStatsFormatter() = default;\n  SubdomainStatsFormatter(std::string local_section_observation_key)\n      : section_observation_key(std::move(local_section_observation_key)) {}\n  std::string operator()(const size_t iteration_id, const size_t num_subdomains,\n                         const double avg_subdomain_its,\n                         const size_t min_subdomain_its,\n                         const size_t max_subdomain_its,\n                         const size_t total_subdomain_its) const {\n    return pretty_type::name<OptionsGroup>() + section_observation_key + \"(\" +\n           get_output(iteration_id) + \") completed all \" +\n           get_output(num_subdomains) +\n           \" subdomain solves. Average of number of iterations: \" +\n           get_output(avg_subdomain_its) + \" (min \" +\n           get_output(min_subdomain_its) + \", max \" +\n           get_output(max_subdomain_its) + \", total \" +\n           get_output(total_subdomain_its) + \").\\n\";\n  }\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | section_observation_key; }\n  std::string section_observation_key{};\n};\n\ntemplate <typename OptionsGroup, typename ArraySectionIdTag>\nstruct RegisterObservers {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& box,\n                const ArrayIndex& /*array_index*/) {\n    // Get the observation key, or \"Unused\" if the element does not belong\n    // to a section with this tag. In the latter case, no observations will\n    // ever be contributed.\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    ASSERT(section_observation_key != \"Unused\",\n           \"The identifier 'Unused' is reserved to indicate that no \"\n           \"observations with this key will be contributed. Use a different \"\n           \"key, or change the identifier 'Unused' to something else.\");\n    return {\n        observers::TypeOfObservation::Reduction,\n        observers::ObservationKey{pretty_type::get_name<OptionsGroup>() +\n                                  section_observation_key.value_or(\"Unused\") +\n                                  \"SubdomainSolves\"}};\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag,\n          typename ArraySectionIdTag>\nusing RegisterElement = observers::Actions::RegisterWithObservers<\n    RegisterObservers<OptionsGroup, ArraySectionIdTag>>;\n\ntemplate <typename OptionsGroup, typename ParallelComponent,\n          typename Metavariables, typename ArrayIndex>\nvoid contribute_to_subdomain_stats_observation(\n    const size_t iteration_id, const size_t subdomain_solve_num_iterations,\n    Parallel::GlobalCache<Metavariables>& cache, const ArrayIndex& array_index,\n    const std::string& section_observation_key, const bool observe_per_core) {\n  auto& local_observer = *Parallel::local_branch(\n      Parallel::get_parallel_component<observers::Observer<Metavariables>>(\n          cache));\n\n  auto formatter =\n      UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(cache) >=\n               ::Verbosity::Verbose)\n          ? std::make_optional(\n                SubdomainStatsFormatter<OptionsGroup>{section_observation_key})\n          : std::nullopt;\n  Parallel::simple_action<observers::Actions::ContributeReductionData>(\n      local_observer,\n      observers::ObservationId(iteration_id,\n                               pretty_type::get_name<OptionsGroup>() +\n                                   section_observation_key + \"SubdomainSolves\"),\n      Parallel::make_array_component_id<ParallelComponent>(array_index),\n      std::string{\"/\" + pretty_type::name<OptionsGroup>() +\n                  section_observation_key + \"SubdomainSolves\"},\n      std::vector<std::string>{\"Iteration\", \"NumSubdomains\", \"AvgNumIterations\",\n                               \"MinNumIterations\", \"MaxNumIterations\",\n                               \"TotalNumIterations\"},\n      reduction_data{\n          iteration_id, 1, static_cast<double>(subdomain_solve_num_iterations),\n          subdomain_solve_num_iterations, subdomain_solve_num_iterations,\n          subdomain_solve_num_iterations},\n      std::move(formatter), observe_per_core);\n}\n\ntemplate <typename SubdomainDataType, typename OptionsGroup>\nstruct SubdomainDataBufferTag : db::SimpleTag {\n  static std::string name() {\n    return \"SubdomainData(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = SubdomainDataType;\n};\n\n// Allow factory-creating any of these serial linear solvers for use as\n// subdomain solver\ntemplate <typename FieldsTag, typename SubdomainOperator,\n          typename SubdomainPreconditioners,\n          typename SubdomainData = ElementCenteredSubdomainData<\n              SubdomainOperator::volume_dim,\n              typename db::add_tag_prefix<LinearSolver::Tags::Residual,\n                                          FieldsTag>::tags_list>>\nusing subdomain_solver = LinearSolver::Serial::LinearSolver<tmpl::append<\n    tmpl::list<::LinearSolver::Serial::Registrars::Gmres<SubdomainData>,\n               ::LinearSolver::Serial::Registrars::ExplicitInverse<\n                   typename SubdomainData::value_type>>,\n    SubdomainPreconditioners>>;\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator,\n          typename SubdomainPreconditioners>\nstruct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n  static constexpr size_t Dim = SubdomainOperator::volume_dim;\n  using SubdomainData =\n      ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;\n  using SubdomainSolver =\n      subdomain_solver<FieldsTag, SubdomainOperator, SubdomainPreconditioners>;\n  using subdomain_solver_tag =\n      Tags::SubdomainSolver<SubdomainSolver, OptionsGroup>;\n  using volume_data_tag =\n      Tags::VolumeDataForOutput<SubdomainData, OptionsGroup>;\n\n public:  // Iterable action\n  using simple_tags_from_options = tmpl::list<subdomain_solver_tag>;\n\n  using const_global_cache_tags =\n      tmpl::list<Tags::MaxOverlap<OptionsGroup>,\n                 LinearSolver::Tags::OutputVolumeData<OptionsGroup>>;\n\n  using simple_tags = tmpl::list<\n      Tags::IntrudingExtents<Dim, OptionsGroup>, Tags::Weight<OptionsGroup>,\n      domain::Tags::Faces<Dim, Tags::Weight<OptionsGroup>>,\n      SubdomainDataBufferTag<SubdomainData, OptionsGroup>,\n      LinearSolver::Tags::ObservationId<OptionsGroup>, volume_data_tag>;\n  using compute_tags = tmpl::list<>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*element_id*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeElement>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // amr::protocols::Projector\n  using return_tags = tmpl::append<simple_tags, simple_tags_from_options>;\n  using argument_tags =\n      tmpl::list<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                 domain::Tags::Coordinates<Dim, Frame::ElementLogical>,\n                 Tags::MaxOverlap<OptionsGroup>>;\n\n  template <typename... AmrData>\n  static void apply(\n      const gsl::not_null<std::array<size_t, Dim>*> intruding_extents,\n      const gsl::not_null<Scalar<DataVector>*> element_weight,\n      const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>\n          intruding_overlap_weights,\n      const gsl::not_null<SubdomainData*> subdomain_data,\n      const gsl::not_null<size_t*> observation_id,\n      const gsl::not_null<SubdomainData*> /*volume_data_for_output*/,\n      const gsl::not_null<std::unique_ptr<SubdomainSolver>*> subdomain_solver,\n      const Element<Dim>& element, const Mesh<Dim>& mesh,\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n      const std::optional<size_t> max_overlap, const AmrData&... amr_data) {\n    const size_t num_points = mesh.number_of_grid_points();\n\n    // Intruding overlaps\n    std::array<double, Dim> intruding_overlap_widths{};\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(*intruding_extents, d) =\n          LinearSolver::Schwarz::overlap_extent(mesh.extents(d), max_overlap);\n      const auto& collocation_points =\n          Spectral::collocation_points(mesh.slice_through(d));\n      gsl::at(intruding_overlap_widths, d) =\n          LinearSolver::Schwarz::overlap_width(gsl::at(*intruding_extents, d),\n                                               collocation_points);\n    }\n\n    // Element weight\n    // For max_overlap > 0 all overlaps will have non-zero extents on an LGL\n    // mesh (because it has at least 2 points per dimension), so we don't need\n    // to check their extents are non-zero individually\n    if (LIKELY(max_overlap != 0)) {\n      LinearSolver::Schwarz::element_weight(element_weight, logical_coords,\n                                            intruding_overlap_widths,\n                                            element.external_boundaries());\n    } else {\n      set_number_of_grid_points(element_weight, num_points);\n      get(*element_weight) = 1.;\n    }\n\n    // Intruding overlap weights\n    intruding_overlap_weights->clear();\n    for (const auto& direction : element.internal_boundaries()) {\n      const size_t dim = direction.dimension();\n      if (gsl::at(*intruding_extents, dim) > 0) {\n        const auto intruding_logical_coords =\n            LinearSolver::Schwarz::data_on_overlap(\n                logical_coords, mesh.extents(),\n                gsl::at(*intruding_extents, dim), direction);\n        (*intruding_overlap_weights)[direction] =\n            LinearSolver::Schwarz::intruding_weight(\n                intruding_logical_coords, direction, intruding_overlap_widths,\n                element.neighbors().at(direction).size(),\n                element.external_boundaries());\n      }\n    }\n\n    // Subdomain data buffer\n    *subdomain_data = SubdomainData{num_points};\n\n    // Subdomain solver\n    // The subdomain solver initially gets created from options on each element.\n    // Then we have to copy it around during AMR.\n    if constexpr (sizeof...(AmrData) == 0) {\n      *observation_id = 0;\n      (void)subdomain_solver;\n    } else {\n      if constexpr (tt::is_a_v<tuples::TaggedTuple, AmrData...>) {\n        // h-refinement: copy from the parent\n        *observation_id =\n            get<LinearSolver::Tags::ObservationId<OptionsGroup>>(amr_data...);\n        *subdomain_solver = get<subdomain_solver_tag>(amr_data...)->get_clone();\n      } else if constexpr (tt::is_a_v<std::unordered_map, AmrData...>) {\n        // h-coarsening, copy from one of the children (doesn't matter which)\n        *observation_id = get<LinearSolver::Tags::ObservationId<OptionsGroup>>(\n            amr_data.begin()->second...);\n        *subdomain_solver =\n            get<subdomain_solver_tag>(amr_data.begin()->second...)->get_clone();\n      } else {\n        (void)observation_id;\n      }\n      (*subdomain_solver)->reset();\n    }\n  }\n};\n\n// Restrict the residual to neighboring subdomains that overlap with this\n// element and send the data to those elements\ntemplate <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>\nusing SendOverlapData = LinearSolver::Schwarz::Actions::SendOverlapFields<\n    tmpl::list<db::add_tag_prefix<LinearSolver::Tags::Residual, FieldsTag>>,\n    OptionsGroup, true>;\n\ntemplate <size_t Dim, typename OptionsGroup, typename OverlapSolution>\nstruct OverlapSolutionInboxTag\n    : public Parallel::InboxInserters::Map<\n          OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapSolution>> {\n  using temporal_id = size_t;\n  using type = std::map<temporal_id, OverlapMap<Dim, OverlapSolution>>;\n};\n\n// Wait for the residual data on regions of this element's subdomain that\n// overlap with other elements. Once the residual data is available on all\n// overlaps, solve the restricted problem for this element-centered subdomain.\n// Apply the weighted solution on this element directly and send the solution on\n// overlap regions to the neighbors that they overlap with.\ntemplate <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator,\n          typename SubdomainPreconditioners, typename ArraySectionIdTag>\nstruct SolveSubdomain {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n  static constexpr size_t Dim = SubdomainOperator::volume_dim;\n  using overlap_residuals_inbox_tag =\n      Actions::detail::OverlapFieldsTag<Dim, tmpl::list<residual_tag>,\n                                        OptionsGroup>;\n  using SubdomainData =\n      ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;\n  using SubdomainSolver =\n      subdomain_solver<FieldsTag, SubdomainOperator, SubdomainPreconditioners>;\n  using subdomain_solver_tag =\n      Tags::SubdomainSolver<SubdomainSolver, OptionsGroup>;\n  using OverlapData = typename SubdomainData::OverlapData;\n  using overlap_solution_inbox_tag =\n      OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapData>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<Tags::MaxOverlap<OptionsGroup>,\n                 logging::Tags::Verbosity<OptionsGroup>,\n                 Tags::ObservePerCoreReductions<OptionsGroup>>;\n  using inbox_tags = tmpl::list<overlap_residuals_inbox_tag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t iteration_id =\n        get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    const auto& element = db::get<domain::Tags::Element<Dim>>(box);\n    const auto& max_overlap = db::get<Tags::MaxOverlap<OptionsGroup>>(box);\n\n    // Wait for communicated overlap data\n    const bool has_overlap_data =\n        max_overlap != 0 and element.number_of_neighbors() > 0;\n    if (LIKELY(has_overlap_data) and\n        not dg::has_received_from_all_mortars<overlap_residuals_inbox_tag>(\n            iteration_id, element, inboxes)) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Do some logging\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Solve subdomain\\n\", element_id,\n                       pretty_type::name<OptionsGroup>(), iteration_id);\n    }\n\n    // Assemble the subdomain data from the data on the element and the\n    // communicated overlap data\n    db::mutate<SubdomainDataBufferTag<SubdomainData, OptionsGroup>>(\n        [&inboxes, &iteration_id, &has_overlap_data](\n            const gsl::not_null<SubdomainData*> subdomain_data,\n            const auto& residual) {\n          subdomain_data->element_data = residual;\n          // Nothing was communicated if the overlaps are empty\n          if (LIKELY(has_overlap_data)) {\n            subdomain_data->overlap_data =\n                std::move(tuples::get<overlap_residuals_inbox_tag>(inboxes)\n                              .extract(iteration_id)\n                              .mapped());\n          }\n        },\n        make_not_null(&box), db::get<residual_tag>(box));\n    const auto& subdomain_residual =\n        db::get<SubdomainDataBufferTag<SubdomainData, OptionsGroup>>(box);\n\n    // Allocate workspace memory for repeatedly applying the subdomain operator\n    const SubdomainOperator subdomain_operator{};\n\n    // Solve the subdomain problem\n    const auto& subdomain_solver = get<subdomain_solver_tag>(box);\n    auto subdomain_solve_initial_guess_in_solution_out =\n        make_with_value<SubdomainData>(subdomain_residual, 0.);\n    const auto subdomain_solve_has_converged = subdomain_solver.solve(\n        make_not_null(&subdomain_solve_initial_guess_in_solution_out),\n        subdomain_operator, subdomain_residual, std::forward_as_tuple(box));\n    // Re-naming the solution buffer for the code below\n    auto& subdomain_solution = subdomain_solve_initial_guess_in_solution_out;\n\n    // Do some logging and observing\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Quiet)) {\n      if (not subdomain_solve_has_converged or\n          subdomain_solve_has_converged.reason() ==\n              Convergence::Reason::MaxIterations) {\n        Parallel::printf(\n            \"%s %s(%zu): WARNING: Subdomain solver did not converge in %zu \"\n            \"iterations: %e -> %e\\n\",\n            element_id, pretty_type::name<OptionsGroup>(), iteration_id,\n            subdomain_solve_has_converged.num_iterations(),\n            subdomain_solve_has_converged.initial_residual_magnitude(),\n            subdomain_solve_has_converged.residual_magnitude());\n      } else if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                          ::Verbosity::Debug)) {\n        Parallel::printf(\n            \"%s %s(%zu): Subdomain solver converged in %zu iterations (%s): %e \"\n            \"-> %e\\n\",\n            element_id, pretty_type::name<OptionsGroup>(), iteration_id,\n            subdomain_solve_has_converged.num_iterations(),\n            subdomain_solve_has_converged.reason(),\n            subdomain_solve_has_converged.initial_residual_magnitude(),\n            subdomain_solve_has_converged.residual_magnitude());\n      }\n    }\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    if (section_observation_key.has_value()) {\n      contribute_to_subdomain_stats_observation<OptionsGroup,\n                                                ParallelComponent>(\n          iteration_id + 1, subdomain_solve_has_converged.num_iterations(),\n          cache, element_id, *section_observation_key,\n          db::get<Tags::ObservePerCoreReductions<OptionsGroup>>(box));\n    }\n\n    // Record subdomain solution for volume data output\n    if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {\n      db::mutate<Tags::VolumeDataForOutput<SubdomainData, OptionsGroup>>(\n          [&subdomain_solution](const auto volume_data) {\n            volume_data->element_data = subdomain_solution.element_data;\n          },\n          make_not_null(&box));\n    }\n\n    // Apply weighting\n    if (LIKELY(max_overlap != 0)) {\n      subdomain_solution.element_data *=\n          get(db::get<Tags::Weight<OptionsGroup>>(box));\n    }\n\n    // Apply solution to central element\n    db::mutate<fields_tag>(\n        [&subdomain_solution](const auto fields) {\n          *fields += subdomain_solution.element_data;\n        },\n        make_not_null(&box));\n\n    // Send overlap solutions back to the neighbors that they are on\n    if (LIKELY(max_overlap != 0)) {\n      auto& receiver_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      for (auto& [overlap_id, overlap_solution] :\n           subdomain_solution.overlap_data) {\n        const auto& direction = overlap_id.direction();\n        const auto& neighbor_id = overlap_id.id();\n        const auto& orientation =\n            element.neighbors().at(direction).orientation(neighbor_id);\n        const auto direction_from_neighbor = orientation(direction.opposite());\n        Parallel::receive_data<overlap_solution_inbox_tag>(\n            receiver_proxy[neighbor_id], iteration_id,\n            std::make_pair(\n                OverlapId<Dim>{direction_from_neighbor, element.id()},\n                std::move(overlap_solution)));\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Wait for the subdomain solutions on regions within this element that overlap\n// with neighboring element-centered subdomains. Combine the solutions as a\n// weighted sum.\ntemplate <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator>\nstruct ReceiveOverlapSolution {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n  static constexpr size_t Dim = SubdomainOperator::volume_dim;\n  using SubdomainData =\n      ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;\n  using OverlapSolution = typename SubdomainData::OverlapData;\n  using overlap_solution_inbox_tag =\n      OverlapSolutionInboxTag<Dim, OptionsGroup, OverlapSolution>;\n\n public:\n  using const_global_cache_tags = tmpl::list<Tags::MaxOverlap<OptionsGroup>>;\n  using inbox_tags = tmpl::list<overlap_solution_inbox_tag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t iteration_id =\n        get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    const auto& element = db::get<domain::Tags::Element<Dim>>(box);\n\n    // Nothing to do if overlap is empty\n    if (UNLIKELY(db::get<Tags::MaxOverlap<OptionsGroup>>(box) == 0 or\n                 element.number_of_neighbors() == 0)) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    if (not dg::has_received_from_all_mortars<overlap_solution_inbox_tag>(\n            iteration_id, element, inboxes)) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Do some logging\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s(%zu): Receive overlap solution\\n\", element_id,\n                       pretty_type::name<OptionsGroup>(), iteration_id);\n    }\n\n    // Extract overlap solutions from inbox\n    auto received_overlap_solutions =\n        std::move(tuples::get<overlap_solution_inbox_tag>(inboxes)\n                      .extract(iteration_id)\n                      .mapped());\n\n    // Record overlap solutions for volume data output\n    if (db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {\n      db::mutate<Tags::VolumeDataForOutput<SubdomainData, OptionsGroup>>(\n          [&received_overlap_solutions](const auto volume_data) {\n            volume_data->overlap_data = received_overlap_solutions;\n          },\n          make_not_null(&box));\n    }\n\n    // Add overlap solutions to this element's solution in a weighted sum\n    db::mutate<fields_tag>(\n        [&received_overlap_solutions](\n            const auto fields, const Index<Dim>& full_extents,\n            const std::array<size_t, Dim>& all_intruding_extents,\n            const DirectionMap<Dim, Scalar<DataVector>>&\n                all_intruding_overlap_weights) {\n          for (auto& [overlap_id, overlap_solution] :\n               received_overlap_solutions) {\n            const auto& direction = overlap_id.direction();\n            const auto& intruding_extents =\n                gsl::at(all_intruding_extents, direction.dimension());\n            const auto& overlap_weight =\n                all_intruding_overlap_weights.at(direction);\n            overlap_solution *= get(overlap_weight);\n            LinearSolver::Schwarz::add_overlap_data(\n                fields, overlap_solution, full_extents, intruding_extents,\n                direction);\n          }\n        },\n        make_not_null(&box), db::get<domain::Tags::Mesh<Dim>>(box).extents(),\n        db::get<Tags::IntrudingExtents<Dim, OptionsGroup>>(box),\n        db::get<domain::Tags::Faces<Dim, Tags::Weight<OptionsGroup>>>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace LinearSolver::Schwarz::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\nnamespace detail {\n// Defines a consistent ordering of overlap IDs\ntemplate <size_t Dim, typename ValueType>\nvoid ordered_overlap_ids(\n    const gsl::not_null<std::vector<OverlapId<Dim>>*> overlap_ids,\n    const OverlapMap<Dim, ValueType>& overlap_map) {\n  // Return early if the overlap IDs haven't changed\n  if (overlap_ids->size() == overlap_map.size() and\n      alg::all_of(*overlap_ids,\n                  [&overlap_map](const OverlapId<Dim>& overlap_id) {\n                    return overlap_map.contains(overlap_id);\n                  })) {\n    return;\n  }\n  overlap_ids->resize(overlap_map.size());\n  std::transform(overlap_map.begin(), overlap_map.end(), overlap_ids->begin(),\n                 [](const auto& overlap_id_and_value) {\n                   return overlap_id_and_value.first;\n                 });\n  std::sort(overlap_ids->begin(), overlap_ids->end());\n}\n}  // namespace detail\n\n/// \\cond\ntemplate <bool Const, size_t Dim, typename TagsList>\nstruct ElementCenteredSubdomainDataIterator;\n/// \\endcond\n\n/*!\n * \\brief Data on an element-centered subdomain\n *\n * An element-centered subdomain consists of a central element and overlap\n * regions with all neighboring elements. This class holds data on such a\n * subdomain. It supports vector space operations (addition and scalar\n * multiplication) and an inner product, which allows the use of this data type\n * with linear solvers (see e.g. `LinearSolver::Serial::Gmres`).\n */\ntemplate <size_t Dim, typename TagsList>\nstruct ElementCenteredSubdomainData {\n  static constexpr size_t volume_dim = Dim;\n  using ElementData = Variables<TagsList>;\n  using OverlapData = ElementData;\n  using iterator = ElementCenteredSubdomainDataIterator<false, Dim, TagsList>;\n  using const_iterator =\n      ElementCenteredSubdomainDataIterator<true, Dim, TagsList>;\n  using value_type = typename ElementData::value_type;\n\n  ElementCenteredSubdomainData() = default;\n  ElementCenteredSubdomainData(const ElementCenteredSubdomainData&) = default;\n  ElementCenteredSubdomainData& operator=(const ElementCenteredSubdomainData&) =\n      default;\n  ElementCenteredSubdomainData(ElementCenteredSubdomainData&&) = default;\n  ElementCenteredSubdomainData& operator=(ElementCenteredSubdomainData&&) =\n      default;\n  ~ElementCenteredSubdomainData() = default;\n\n  explicit ElementCenteredSubdomainData(const size_t element_num_points)\n      : element_data{element_num_points} {}\n\n  template <typename UsedForSizeTagsList>\n  void destructive_resize(\n      const ElementCenteredSubdomainData<Dim, UsedForSizeTagsList>&\n          used_for_size) {\n    if (UNLIKELY(element_data.number_of_grid_points() !=\n                 used_for_size.element_data.number_of_grid_points())) {\n      element_data.initialize(\n          used_for_size.element_data.number_of_grid_points());\n    }\n    // Erase overlaps that don't exist in `used_for_size`\n    for (auto it = overlap_data.cbegin(); it != overlap_data.cend();) {\n      if (used_for_size.overlap_data.find(it->first) ==\n          used_for_size.overlap_data.end()) {\n        it = overlap_data.erase(it);\n      } else {\n        ++it;\n      }\n    }\n    // Insert or resize overlaps to match `used_for_size`\n    for (const auto& [overlap_id, used_for_overlap_size] :\n         used_for_size.overlap_data) {\n      if (UNLIKELY(overlap_data[overlap_id].number_of_grid_points() !=\n                   used_for_overlap_size.number_of_grid_points())) {\n        overlap_data[overlap_id].initialize(\n            used_for_overlap_size.number_of_grid_points());\n      }\n    }\n  }\n\n  ElementCenteredSubdomainData(\n      Variables<TagsList> local_element_data,\n      OverlapMap<Dim, Variables<TagsList>> local_overlap_data)\n      : element_data{std::move(local_element_data)},\n        overlap_data{std::move(local_overlap_data)} {}\n\n  size_t size() const {\n    return std::accumulate(\n        overlap_data.begin(), overlap_data.end(), element_data.size(),\n        [](const size_t size, const auto& overlap_id_and_data) {\n          return size + overlap_id_and_data.second.size();\n        });\n  }\n  iterator begin() {\n    detail::ordered_overlap_ids(make_not_null(&ordered_overlap_ids_),\n                                overlap_data);\n    return {this};\n  }\n  iterator end() { return {}; }\n  const_iterator begin() const {\n    detail::ordered_overlap_ids(make_not_null(&ordered_overlap_ids_),\n                                overlap_data);\n    return {this};\n  }\n  const_iterator end() const { return {}; }\n  const_iterator cbegin() const { return begin(); }\n  const_iterator cend() const { return end(); }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | element_data;\n    p | overlap_data;\n  }\n\n  template <typename RhsTagsList>\n  ElementCenteredSubdomainData& operator+=(\n      const ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {\n    element_data += rhs.element_data;\n    for (auto& [overlap_id, data] : overlap_data) {\n      data += rhs.overlap_data.at(overlap_id);\n    }\n    return *this;\n  }\n\n  template <typename RhsTagsList>\n  ElementCenteredSubdomainData& operator-=(\n      const ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {\n    element_data -= rhs.element_data;\n    for (auto& [overlap_id, data] : overlap_data) {\n      data -= rhs.overlap_data.at(overlap_id);\n    }\n    return *this;\n  }\n\n  ElementCenteredSubdomainData& operator*=(const value_type scalar) {\n    element_data *= scalar;\n    for (auto& [overlap_id, data] : overlap_data) {\n      data *= scalar;\n      // Silence unused-variable warning on GCC 7\n      (void)overlap_id;\n    }\n    return *this;\n  }\n\n  ElementCenteredSubdomainData& operator/=(const value_type scalar) {\n    element_data /= scalar;\n    for (auto& [overlap_id, data] : overlap_data) {\n      data /= scalar;\n      // Silence unused-variable warning on GCC 7\n      (void)overlap_id;\n    }\n    return *this;\n  }\n\n  ElementData element_data{};\n  OverlapMap<Dim, OverlapData> overlap_data{};\n\n  private:\n   // Cache for iterators, so they don't have to allocate, fill and sort this\n   // vector every time\n   // NOLINTNEXTLINE(spectre-mutable)\n   mutable std::vector<OverlapId<Dim>> ordered_overlap_ids_{};\n\n   friend ElementCenteredSubdomainDataIterator<false, Dim, TagsList>;\n   friend ElementCenteredSubdomainDataIterator<true, Dim, TagsList>;\n};\n\ntemplate <size_t Dim, typename LhsTagsList, typename RhsTagsList>\ndecltype(auto) operator+(\n    ElementCenteredSubdomainData<Dim, LhsTagsList> lhs,\n    const ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {\n  lhs += rhs;\n  return lhs;\n}\n\ntemplate <size_t Dim, typename LhsTagsList, typename RhsTagsList>\ndecltype(auto) operator-(\n    ElementCenteredSubdomainData<Dim, LhsTagsList> lhs,\n    const ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {\n  lhs -= rhs;\n  return lhs;\n}\n\ntemplate <size_t Dim, typename TagsList,\n          typename ValueType =\n              typename ElementCenteredSubdomainData<Dim, TagsList>::value_type>\ndecltype(auto) operator*(const ValueType scalar,\n                         ElementCenteredSubdomainData<Dim, TagsList> data) {\n  data *= scalar;\n  return data;\n}\n\ntemplate <size_t Dim, typename TagsList,\n          typename ValueType =\n              typename ElementCenteredSubdomainData<Dim, TagsList>::value_type>\ndecltype(auto) operator*(ElementCenteredSubdomainData<Dim, TagsList> data,\n                         const ValueType scalar) {\n  data *= scalar;\n  return data;\n}\n\ntemplate <size_t Dim, typename TagsList,\n          typename ValueType =\n              typename ElementCenteredSubdomainData<Dim, TagsList>::value_type>\ndecltype(auto) operator/(ElementCenteredSubdomainData<Dim, TagsList> data,\n                         const ValueType scalar) {\n  data /= scalar;\n  return data;\n}\n\ntemplate <size_t Dim, typename TagsList>\nstd::ostream& operator<<(\n    std::ostream& os,\n    const ElementCenteredSubdomainData<Dim, TagsList>& subdomain_data) {\n  os << \"Element data:\\n\"\n     << subdomain_data.element_data << \"\\nOverlap data:\\n\"\n     << subdomain_data.overlap_data;\n  return os;\n}\n\ntemplate <size_t Dim, typename TagsList>\nbool operator==(const ElementCenteredSubdomainData<Dim, TagsList>& lhs,\n                const ElementCenteredSubdomainData<Dim, TagsList>& rhs) {\n  return lhs.element_data == rhs.element_data and\n         lhs.overlap_data == rhs.overlap_data;\n}\n\ntemplate <size_t Dim, typename TagsList>\nbool operator!=(const ElementCenteredSubdomainData<Dim, TagsList>& lhs,\n                const ElementCenteredSubdomainData<Dim, TagsList>& rhs) {\n  return not(lhs == rhs);\n}\n\n/*!\n * \\brief Iterate over `LinearSolver::Schwarz::ElementCenteredSubdomainData`\n *\n * This iterator guarantees that it steps through the data in the same order as\n * long as these conditions are satisfied:\n *\n * - The set of overlap IDs in the `overlap_data` doesn't change\n * - The extents of the `element_data` and the `overlap_data doesn't change\n *\n * Iterating requires sorting the overlap IDs. If you find this impacts\n * performance, be advised to implement the internal data storage in\n * `ElementCenteredSubdomainData` so it stores its data contiguously, e.g. by\n * implementing non-owning variables.\n */\ntemplate <bool Const, size_t Dim, typename TagsList>\nstruct ElementCenteredSubdomainDataIterator {\n private:\n  using PtrType =\n      tmpl::conditional_t<Const,\n                          const ElementCenteredSubdomainData<Dim, TagsList>*,\n                          ElementCenteredSubdomainData<Dim, TagsList>*>;\n  using VarsPtrType = tmpl::conditional_t<Const, const Variables<TagsList>*,\n                                          Variables<TagsList>*>;\n\n public:\n  using difference_type = ptrdiff_t;\n  using value_type = typename Variables<TagsList>::value_type;\n  using pointer = value_type*;\n  using reference = value_type&;\n  using iterator_category = std::forward_iterator_tag;\n\n  /// Construct begin state\n  ElementCenteredSubdomainDataIterator(PtrType data) : data_(data) {\n    reset();\n  }\n\n  void reset() {\n    overlap_index_ = (data_->element_data.size() == 0 and\n                      data_->ordered_overlap_ids_.empty())\n                         ? std::numeric_limits<size_t>::max()\n                         : 0;\n    update_vars_ref();\n    data_index_ = 0;\n  }\n\n  /// Construct end state\n  ElementCenteredSubdomainDataIterator() {\n    overlap_index_ = std::numeric_limits<size_t>::max();\n    data_index_ = 0;\n  }\n\n  ElementCenteredSubdomainDataIterator& operator++() {\n    ++data_index_;\n    if (data_index_ == vars_ref_->size()) {\n      ++overlap_index_;\n      update_vars_ref();\n      data_index_ = 0;\n    }\n    if (overlap_index_ == data_->ordered_overlap_ids_.size() + 1) {\n      overlap_index_ = std::numeric_limits<size_t>::max();\n    }\n    return *this;\n  }\n\n  tmpl::conditional_t<Const, value_type, value_type&> operator*() const {\n    return vars_ref_->data()[data_index_];\n  }\n\n private:\n  void update_vars_ref() {\n    // The random-access operation is relatively slow because it computes a\n    // hash, so it's important for performance to avoid repeating it at every\n    // data index. Instead, we update this reference only when the overlap index\n    // changes.\n    if (overlap_index_ > data_->ordered_overlap_ids_.size()) {\n      return;\n    }\n    vars_ref_ = overlap_index_ == 0\n                    ? &data_->element_data\n                    : &data_->overlap_data.at(\n                          data_->ordered_overlap_ids_[overlap_index_ - 1]);\n  }\n\n  friend bool operator==(const ElementCenteredSubdomainDataIterator& lhs,\n                         const ElementCenteredSubdomainDataIterator& rhs) {\n    return lhs.overlap_index_ == rhs.overlap_index_ and\n           lhs.data_index_ == rhs.data_index_;\n  }\n\n  friend bool operator!=(const ElementCenteredSubdomainDataIterator& lhs,\n                         const ElementCenteredSubdomainDataIterator& rhs) {\n    return not(lhs == rhs);\n  }\n\n  PtrType data_;\n  size_t overlap_index_;\n  size_t data_index_;\n  VarsPtrType vars_ref_ = nullptr;\n};\n\n}  // namespace LinearSolver::Schwarz\n\nnamespace LinearSolver::InnerProductImpls {\n\ntemplate <size_t Dim, typename LhsTagsList, typename RhsTagsList>\nstruct InnerProductImpl<\n    Schwarz::ElementCenteredSubdomainData<Dim, LhsTagsList>,\n    Schwarz::ElementCenteredSubdomainData<Dim, RhsTagsList>> {\n  static auto apply(\n      const Schwarz::ElementCenteredSubdomainData<Dim, LhsTagsList>& lhs,\n      const Schwarz::ElementCenteredSubdomainData<Dim, RhsTagsList>& rhs) {\n    auto result = inner_product(lhs.element_data, rhs.element_data);\n    for (const auto& [overlap_id, lhs_data] : lhs.overlap_data) {\n      result += inner_product(lhs_data, rhs.overlap_data.at(overlap_id));\n    }\n    return result;\n  }\n};\n\n}  // namespace LinearSolver::InnerProductImpls\n\nnamespace MakeWithValueImpls {\n\ntemplate <size_t Dim, typename TagsListOut, typename TagsListIn>\nstruct MakeWithValueImpl<\n    LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, TagsListOut>,\n    LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, TagsListIn>> {\n  using SubdomainDataIn =\n      LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, TagsListIn>;\n  using SubdomainDataOut =\n      LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, TagsListOut>;\n  static SPECTRE_ALWAYS_INLINE SubdomainDataOut\n  apply(const SubdomainDataIn& input, const double value) {\n    SubdomainDataOut output{};\n    output.element_data =\n        make_with_value<typename SubdomainDataOut::ElementData>(\n            input.element_data, value);\n    for (const auto& [overlap_id, input_data] : input.overlap_data) {\n      output.overlap_data.emplace(\n          overlap_id, make_with_value<typename SubdomainDataOut::OverlapData>(\n                          input_data, value));\n    }\n    return output;\n  }\n};\n\n}  // namespace MakeWithValueImpls\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/ObserveVolumeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/GetSectionObservationKey.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::Schwarz::detail {\n\ntemplate <typename OptionsGroup, typename ArraySectionIdTag>\nstruct RegisterWithVolumeObserver {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& box,\n                const ArrayIndex& /*array_index*/) {\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    const std::string subfile_path = \"/\" + pretty_type::name<OptionsGroup>() +\n                                     section_observation_key.value_or(\"\");\n    return {observers::TypeOfObservation::Volume,\n            observers::ObservationKey(subfile_path)};\n  }\n};\n\n// Contribute the volume data recorded in the other actions to the observer at\n// the end of a step.\ntemplate <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator,\n          typename ArraySectionIdTag>\nstruct ObserveVolumeData {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<LinearSolver::Tags::Residual, fields_tag>;\n  static constexpr size_t Dim = SubdomainOperator::volume_dim;\n  using SubdomainData =\n      ElementCenteredSubdomainData<Dim, typename residual_tag::tags_list>;\n  using volume_data_tag =\n      Tags::VolumeDataForOutput<SubdomainData, OptionsGroup>;\n  using VolumeDataVars = typename volume_data_tag::type::ElementData;\n\n public:\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (not db::get<LinearSolver::Tags::OutputVolumeData<OptionsGroup>>(box)) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n    const auto& volume_data = db::get<volume_data_tag>(box);\n    const auto& observation_id =\n        db::get<LinearSolver::Tags::ObservationId<OptionsGroup>>(box);\n    const auto& mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n    const auto& inertial_coords =\n        db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box);\n    // Collect tensor components to observe\n    std::vector<TensorComponent> components{};\n    const auto record_tensor_components = [&components](\n                                              const auto tensor_tag_v,\n                                              const auto& tensor,\n                                              const std::string& suffix = \"\") {\n      using tensor_tag = std::decay_t<decltype(tensor_tag_v)>;\n      using TensorType = std::decay_t<decltype(tensor)>;\n      using VectorType = typename TensorType::type;\n      using ValueType = typename VectorType::value_type;\n      for (size_t i = 0; i < tensor.size(); ++i) {\n        const std::string component_name =\n            db::tag_name<tensor_tag>() + suffix + tensor.component_suffix(i);\n        if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n          components.emplace_back(\"Re(\" + component_name + \")\",\n                                  real(tensor[i]));\n          components.emplace_back(\"Im(\" + component_name + \")\",\n                                  imag(tensor[i]));\n        } else {\n          components.emplace_back(component_name, tensor[i]);\n        }\n      }\n    };\n    record_tensor_components(domain::Tags::Coordinates<Dim, Frame::Inertial>{},\n                             inertial_coords);\n    const auto& all_intruding_extents =\n        db::get<Tags::IntrudingExtents<Dim, OptionsGroup>>(box);\n    const VolumeDataVars zero_vars{mesh.number_of_grid_points(), 0.};\n    tmpl::for_each<typename VolumeDataVars::tags_list>(\n        [&volume_data, &record_tensor_components, &mesh, &all_intruding_extents,\n         &zero_vars, &element_id](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          record_tensor_components(tag{}, get<tag>(volume_data.element_data),\n                                   \"_Center\");\n          for (const auto direction : Direction<Dim>::all_directions()) {\n            const auto direction_predicate =\n                [&direction](const auto& overlap_data) {\n                  return overlap_data.first.direction() == direction;\n                };\n            const auto num_overlaps =\n                alg::count_if(volume_data.overlap_data, direction_predicate);\n            if (num_overlaps == 0) {\n              // No overlap data for this direction (e.g. external boundary),\n              // record zero\n              record_tensor_components(tag{}, get<tag>(zero_vars),\n                                       \"_Overlap\" + get_output(direction));\n            } else if (num_overlaps == 1) {\n              // Overlap data from a single neighbor, record it\n              const auto& overlap_data =\n                  alg::find_if(volume_data.overlap_data, direction_predicate)\n                      ->second;\n              const auto& intruding_extents =\n                  gsl::at(all_intruding_extents, direction.dimension());\n              record_tensor_components(\n                  tag{},\n                  get<tag>(extended_overlap_data(overlap_data, mesh.extents(),\n                                                 intruding_extents, direction)),\n                  \"_Overlap\" + get_output(direction));\n            } else {\n              ERROR(\"Multiple neighbors (\"\n                    << num_overlaps << \") overlap with element \" << element_id\n                    << \" in direction \" << direction\n                    << \", but we can record only one for volume data output.\");\n            }\n          }\n        });\n\n    // Contribute tensor components to observer\n    const std::optional<std::string> section_observation_key =\n        observers::get_section_observation_key<ArraySectionIdTag>(box);\n    const std::string subfile_path = \"/\" + pretty_type::name<OptionsGroup>() +\n                                     section_observation_key.value_or(\"\");\n    observers::contribute_volume_data<\n        not Parallel::is_nodegroup_v<ParallelComponent>>(\n        cache, observers::ObservationId(observation_id, subfile_path),\n        subfile_path,\n        Parallel::make_array_component_id<ParallelComponent>(element_id),\n        ElementVolumeData{element_id, std::move(components), mesh});\n\n    // Increment observation ID\n    db::mutate<LinearSolver::Tags::ObservationId<OptionsGroup>>(\n        [](const auto local_observation_id) { ++(*local_observation_id); },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n}  // namespace LinearSolver::Schwarz::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <numeric>  // std::accumulate\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\nsize_t overlap_extent(const size_t volume_extent,\n                      const std::optional<size_t> max_overlap) {\n  return std::min(volume_extent, max_overlap.value_or(volume_extent));\n}\n\nnamespace {\ntemplate <size_t Dim>\nvoid assert_overlap_extent(const Index<Dim>& volume_extents,\n                           const size_t overlap_extent,\n                           const size_t overlap_dimension) {\n  ASSERT(overlap_dimension < Dim,\n         \"Invalid dimension '\" << overlap_dimension << \"' in \" << Dim << \"D.\");\n  ASSERT(overlap_extent <= volume_extents[overlap_dimension],\n         \"Overlap extent '\" << overlap_extent << \"' exceeds volume extents '\"\n                            << volume_extents << \"' in overlap dimension '\"\n                            << overlap_dimension << \"'.\");\n}\n}  // namespace\n\ntemplate <size_t Dim>\nsize_t overlap_num_points(const Index<Dim>& volume_extents,\n                          const size_t overlap_extent,\n                          const size_t overlap_dimension) {\n  assert_overlap_extent(volume_extents, overlap_extent, overlap_dimension);\n  return volume_extents.slice_away(overlap_dimension).product() *\n         overlap_extent;\n}\n\ndouble overlap_width(const size_t overlap_extent,\n                     const DataVector& collocation_points) {\n  ASSERT(overlap_extent <= collocation_points.size(),\n         \"Overlap extent is \"\n             << overlap_extent\n             << \" but must be at most the number of grid points (\"\n             << collocation_points.size() << \") to compute an overlap width.\");\n  for (size_t i = 0; i < collocation_points.size() / 2; ++i) {\n    ASSERT(equal_within_roundoff(\n               -collocation_points[i],\n               collocation_points[collocation_points.size() - i - 1]),\n           \"Assuming the 'collocation_points' are symmetric, but they are not: \"\n               << collocation_points);\n  }\n  if (overlap_extent == collocation_points.size()) {\n    // Full element width. Weight function is zero at element boundary, so\n    // outermost LGL grid point does not contribute but outermost GL grid point\n    // does contribute.\n    return 2.0;\n  } else {\n    // Partial element width. Weight function is zero at first grid point\n    // outside the `overlap_extent`, so all `overlap_extent` grid points\n    // contribute on LGL and GL meshes.\n    return collocation_points[overlap_extent] + 1.0;\n  }\n}\n\ntemplate <size_t Dim>\nOverlapIterator::OverlapIterator(const Index<Dim>& volume_extents,\n                                 const size_t overlap_extent,\n                                 const Direction<Dim>& direction)\n    : size_{overlap_num_points(volume_extents, overlap_extent,\n                               direction.dimension())},\n      num_slices_{overlap_extent},\n      stride_{std::accumulate(volume_extents.begin(),\n                              volume_extents.begin() + direction.dimension(),\n                              1_st, std::multiplies<size_t>())},\n      stride_count_{0},\n      jump_{(volume_extents[direction.dimension()] - num_slices_) * stride_},\n      initial_offset_{stride_ * (direction.side() == Side::Lower\n                                     ? 0\n                                     : (volume_extents[direction.dimension()] -\n                                        num_slices_))},\n      volume_offset_{initial_offset_},\n      overlap_offset_{0} {\n  assert_overlap_extent(volume_extents, overlap_extent, direction.dimension());\n}\n\nOverlapIterator::operator bool() const { return overlap_offset_ < size_; }\n\nOverlapIterator& OverlapIterator::operator++() {\n  ++volume_offset_;\n  ++overlap_offset_;\n  ++stride_count_;\n  if (stride_count_ == stride_ * num_slices_) {\n    volume_offset_ += jump_;\n    stride_count_ = 0;\n  }\n  return *this;\n}\n\nsize_t OverlapIterator::volume_offset() const { return volume_offset_; }\n\nsize_t OverlapIterator::overlap_offset() const { return overlap_offset_; }\n\nvoid OverlapIterator::reset() {\n  volume_offset_ = initial_offset_;\n  overlap_offset_ = 0;\n  stride_count_ = 0;\n}\n\nnamespace detail {\n\ntemplate <typename ValueType, size_t Dim>\nvoid data_on_overlap_impl(ValueType* overlap_data, const ValueType* volume_data,\n                          const size_t num_components,\n                          const Index<Dim>& volume_extents,\n                          const size_t overlap_extent,\n                          const Direction<Dim>& direction) {\n  const size_t volume_num_points = volume_extents.product();\n  const size_t overlap_num_points = LinearSolver::Schwarz::overlap_num_points(\n      volume_extents, overlap_extent, direction.dimension());\n  for (OverlapIterator overlap_iterator{volume_extents, overlap_extent,\n                                        direction};\n       overlap_iterator; ++overlap_iterator) {\n    for (size_t j = 0; j < num_components; ++j) {\n      const size_t volume_data_index =\n          overlap_iterator.volume_offset() + j * volume_num_points;\n      const size_t overlap_data_index =\n          overlap_iterator.overlap_offset() + j * overlap_num_points;\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      overlap_data[overlap_data_index] = volume_data[volume_data_index];\n    }\n  }\n}\n\ntemplate <typename ValueType, size_t Dim>\nvoid add_overlap_data_impl(ValueType* volume_data,\n                           const ValueType* overlap_data,\n                           const size_t num_components,\n                           const Index<Dim>& volume_extents,\n                           const size_t overlap_extent,\n                           const Direction<Dim>& direction) {\n  const size_t volume_num_points = volume_extents.product();\n  const size_t overlap_num_points = LinearSolver::Schwarz::overlap_num_points(\n      volume_extents, overlap_extent, direction.dimension());\n  for (OverlapIterator overlap_iterator{volume_extents, overlap_extent,\n                                        direction};\n       overlap_iterator; ++overlap_iterator) {\n    for (size_t j = 0; j < num_components; ++j) {\n      const size_t volume_data_index =\n          overlap_iterator.volume_offset() + j * volume_num_points;\n      const size_t overlap_data_index =\n          overlap_iterator.overlap_offset() + j * overlap_num_points;\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      volume_data[volume_data_index] += overlap_data[overlap_data_index];\n    }\n  }\n}\n\n}  // namespace detail\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(r, data)                                                   \\\n  template size_t overlap_num_points(const Index<DIM(data)>&, size_t, size_t); \\\n  template OverlapIterator::OverlapIterator(const Index<DIM(data)>&, size_t,   \\\n                                            const Direction<DIM(data)>&);\n#define INSTANTIATE_DTYPE(r, data)                                       \\\n  template void detail::add_overlap_data_impl(                           \\\n      DTYPE(data)*, const DTYPE(data)*, size_t, const Index<DIM(data)>&, \\\n      size_t, const Direction<DIM(data)>&);                              \\\n  template void detail::data_on_overlap_impl(                            \\\n      DTYPE(data)*, const DTYPE(data)*, size_t, const Index<DIM(data)>&, \\\n      size_t, const Direction<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATE_DTYPE, (1, 2, 3),\n                        (double, std::complex<double>))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\n/// Identifies a subdomain region that overlaps with another element\ntemplate <size_t Dim>\nusing OverlapId = DirectionalId<Dim>;\n\n/// Data structure that can store the `ValueType` on each possible overlap of an\n/// element-centered subdomain with its neighbors. Overlaps are identified by\n/// their `OverlapId`.\ntemplate <size_t Dim, typename ValueType>\nusing OverlapMap = DirectionalIdMap<Dim, ValueType>;\n\n/*!\n * \\brief The number of points that an overlap extends into the `volume_extent`\n *\n * In a dimension where an element has `volume_extent` points, the overlap\n * extent is `min(volume_extent, max_overlap)`.\n *\n * We then define the _width_ of the overlap as the element-logical coordinate\n * distance from the face of the element to the first collocation point\n * _outside_ the overlap extent, or to the other element face if the overlap\n * extent covers the full element.\n *\n *\n * Here's a few notes on the definition of the overlap extent and width:\n *\n * - A typical smooth weighting function goes to zero at the overlap width, so\n *   the grid points located at the overlap width do not contribute to the\n *   weighted sum of subdomain solutions. However, all overlap grid points take\n *   part in computing the subdomain solutions.\n * - On a Gauss-Lobatto grid (with grid points on element faces): When the\n *   overlap covers the full element then the outermost grid points (on the\n *   element face) don't contribute to the weighted sum but take part in\n *   computing the subdomain solutions. When the overlap extent is smaller than\n *   the element by 1 grid point then the grid points on the element face also\n *   don't contribute to the weighted sum _and_ they don't even take part in\n *   computing the subdomain solutions.\n * - On a Gauss grid (with no grid points on element faces): The outermost grid\n *   points of the overlap always contribute to the weighted sum, as the\n *   overlap width either extends to the next grid point or to the element face,\n *   which is always a nonzero distance away from the outermost overlap point.\n * - Defining the overlap width as the distance to the first point _outside_ the\n *   overlap extent makes it non-zero even for a single point of overlap into a\n *   Gauss-Lobatto grid (which has points located at the element face).\n * - Boundary contributions for many (but not all) discontinuous Galerkin\n *   schemes on Gauss-Lobatto grids are limited to the grid points on the\n *   element face, e.g. for a DG operator that is pre-multiplied by the mass\n *   matrix, or one where boundary contributions are lifted using the diagonal\n *   mass-matrix approximation. Not including the grid points facing away from\n *   the subdomain in the overlap allows to ignore that face altogether in the\n *   subdomain operator.\n */\nsize_t overlap_extent(size_t volume_extent, std::optional<size_t> max_overlap);\n\n/*!\n * \\brief Total number of grid points in an overlap region that extends\n * `overlap_extent` points into the `volume_extents` from either side in the\n * `overlap_dimension`\n *\n * The overlap region has `overlap_extent` points in the `overlap_dimension`,\n * and `volume_extents` points in the other dimensions. The number of grid\n * points returned by this function is the product of these extents.\n */\ntemplate <size_t Dim>\nsize_t overlap_num_points(const Index<Dim>& volume_extents,\n                          size_t overlap_extent, size_t overlap_dimension);\n\n/*!\n * \\brief Width of an overlap extending `overlap_extent` points into the\n * `collocation_points` from either side.\n *\n * The \"width\" of an overlap is the element-logical coordinate distance from the\n * element boundary to the first collocation point outside the overlap region in\n * the overlap dimension, i.e. the dimension perpendicular to the element face.\n * See `LinearSolver::Schwarz::overlap_extent` for details.\n *\n * This function assumes the `collocation_points` are mirrored around 0.\n */\ndouble overlap_width(size_t overlap_extent,\n                     const DataVector& collocation_points);\n\n/*!\n * \\brief Iterate over grid points in a region that extends partially into the\n * volume\n *\n * Here's an example how to use this iterator:\n *\n * \\snippet Test_OverlapHelpers.cpp overlap_iterator\n */\nclass OverlapIterator {\n public:\n  template <size_t Dim>\n  OverlapIterator(const Index<Dim>& volume_extents, size_t overlap_extent,\n                  const Direction<Dim>& direction);\n\n  explicit operator bool() const;\n\n  OverlapIterator& operator++();\n\n  /// Offset into a DataVector that holds full volume data\n  size_t volume_offset() const;\n\n  /// Offset into a DataVector that holds data only on the overlap region\n  size_t overlap_offset() const;\n\n  void reset();\n\n private:\n  size_t size_ = std::numeric_limits<size_t>::max();\n  size_t num_slices_ = std::numeric_limits<size_t>::max();\n  size_t stride_ = std::numeric_limits<size_t>::max();\n  size_t stride_count_ = std::numeric_limits<size_t>::max();\n  size_t jump_ = std::numeric_limits<size_t>::max();\n  size_t initial_offset_ = std::numeric_limits<size_t>::max();\n  size_t volume_offset_ = std::numeric_limits<size_t>::max();\n  size_t overlap_offset_ = std::numeric_limits<size_t>::max();\n};\n\n/// @{\n/// The part of the tensor data that lies within the overlap region\ntemplate <size_t Dim, typename DataType, typename... TensorStructure>\nvoid data_on_overlap(const gsl::not_null<Tensor<DataType, TensorStructure...>*>\n                         restricted_tensor,\n                     const Tensor<DataType, TensorStructure...>& tensor,\n                     const Index<Dim>& volume_extents,\n                     const size_t overlap_extent,\n                     const Direction<Dim>& direction) {\n  for (OverlapIterator overlap_iterator{volume_extents, overlap_extent,\n                                        direction};\n       overlap_iterator; ++overlap_iterator) {\n    for (size_t tensor_component = 0; tensor_component < tensor.size();\n         ++tensor_component) {\n      (*restricted_tensor)[tensor_component][overlap_iterator\n                                                 .overlap_offset()] =\n          tensor[tensor_component][overlap_iterator.volume_offset()];\n    }\n  }\n}\n\ntemplate <size_t Dim, typename DataType, typename... TensorStructure>\nTensor<DataType, TensorStructure...> data_on_overlap(\n    const Tensor<DataType, TensorStructure...>& tensor,\n    const Index<Dim>& volume_extents, const size_t overlap_extent,\n    const Direction<Dim>& direction) {\n  Tensor<DataType, TensorStructure...> restricted_tensor{overlap_num_points(\n      volume_extents, overlap_extent, direction.dimension())};\n  data_on_overlap(make_not_null(&restricted_tensor), tensor, volume_extents,\n                  overlap_extent, direction);\n  return restricted_tensor;\n}\n\nnamespace detail {\ntemplate <typename ValueType, size_t Dim>\nvoid data_on_overlap_impl(ValueType* overlap_data, const ValueType* volume_data,\n                          size_t num_components,\n                          const Index<Dim>& volume_extents,\n                          size_t overlap_extent,\n                          const Direction<Dim>& direction);\n}  // namespace detail\n\ntemplate <size_t Dim, typename OverlapTagsList, typename VolumeTagsList>\nvoid data_on_overlap(\n    const gsl::not_null<Variables<OverlapTagsList>*> overlap_data,\n    const Variables<VolumeTagsList>& volume_data,\n    const Index<Dim>& volume_extents, const size_t overlap_extent,\n    const Direction<Dim>& direction) {\n  constexpr size_t num_components =\n      Variables<VolumeTagsList>::number_of_independent_components;\n  ASSERT(volume_data.number_of_grid_points() == volume_extents.product(),\n         \"volume_data has wrong number of grid points.  Expected \"\n             << volume_extents.product() << \", got \"\n             << volume_data.number_of_grid_points());\n  ASSERT(overlap_data->number_of_grid_points() ==\n             overlap_num_points(volume_extents, overlap_extent,\n                                direction.dimension()),\n         \"overlap_data has wrong number of grid points.  Expected \"\n             << overlap_num_points(volume_extents, overlap_extent,\n                                   direction.dimension())\n             << \", got \" << overlap_data->number_of_grid_points());\n  detail::data_on_overlap_impl(overlap_data->data(), volume_data.data(),\n                               num_components, volume_extents, overlap_extent,\n                               direction);\n}\n\ntemplate <size_t Dim, typename TagsList>\nVariables<TagsList> data_on_overlap(const Variables<TagsList>& volume_data,\n                                    const Index<Dim>& volume_extents,\n                                    const size_t overlap_extent,\n                                    const Direction<Dim>& direction) {\n  Variables<TagsList> overlap_data{overlap_num_points(\n      volume_extents, overlap_extent, direction.dimension())};\n  data_on_overlap(make_not_null(&overlap_data), volume_data, volume_extents,\n                  overlap_extent, direction);\n  return overlap_data;\n}\n/// @}\n\nnamespace detail {\ntemplate <typename ValueType, size_t Dim>\nvoid add_overlap_data_impl(ValueType* volume_data,\n                           const ValueType* overlap_data, size_t num_components,\n                           const Index<Dim>& volume_extents,\n                           size_t overlap_extent,\n                           const Direction<Dim>& direction);\n}  // namespace detail\n\n/// Add the `overlap_data` to the `volume_data`\ntemplate <size_t Dim, typename VolumeTagsList, typename OverlapTagsList>\nvoid add_overlap_data(\n    const gsl::not_null<Variables<VolumeTagsList>*> volume_data,\n    const Variables<OverlapTagsList>& overlap_data,\n    const Index<Dim>& volume_extents, const size_t overlap_extent,\n    const Direction<Dim>& direction) {\n  constexpr size_t num_components =\n      Variables<VolumeTagsList>::number_of_independent_components;\n  ASSERT(volume_data->number_of_grid_points() == volume_extents.product(),\n         \"volume_data has wrong number of grid points.  Expected \"\n             << volume_extents.product() << \", got \"\n             << volume_data->number_of_grid_points());\n  ASSERT(overlap_data.number_of_grid_points() ==\n             overlap_num_points(volume_extents, overlap_extent,\n                                direction.dimension()),\n         \"overlap_data has wrong number of grid points.  Expected \"\n             << overlap_num_points(volume_extents, overlap_extent,\n                                   direction.dimension())\n             << \", got \" << overlap_data.number_of_grid_points());\n  detail::add_overlap_data_impl(volume_data->data(), overlap_data.data(),\n                                num_components, volume_extents, overlap_extent,\n                                direction);\n}\n\n/// @{\n/// Extend the overlap data to the full mesh by filling it with zeros outside\n/// the overlap region\ntemplate <size_t Dim, typename ExtendedTagsList, typename OverlapTagsList>\nvoid extended_overlap_data(\n    const gsl::not_null<Variables<ExtendedTagsList>*> extended_data,\n    const Variables<OverlapTagsList>& overlap_data,\n    const Index<Dim>& volume_extents, const size_t overlap_extent,\n    const Direction<Dim>& direction) {\n  *extended_data = Variables<ExtendedTagsList>{volume_extents.product(), 0.};\n  add_overlap_data(extended_data, overlap_data, volume_extents, overlap_extent,\n                   direction);\n}\n\ntemplate <size_t Dim, typename TagsList>\nVariables<TagsList> extended_overlap_data(\n    const Variables<TagsList>& overlap_data, const Index<Dim>& volume_extents,\n    const size_t overlap_extent, const Direction<Dim>& direction) {\n  Variables<TagsList> extended_data{volume_extents.product()};\n  extended_overlap_data(make_not_null(&extended_data), overlap_data,\n                        volume_extents, overlap_extent, direction);\n  return extended_data;\n}\n/// @}\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/Schwarz.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"IO/Observer/Helpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/AsynchronousSolvers/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ObserveVolumeData.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Items related to the %Schwarz linear solver\n///\n/// \\see `LinearSolver::Schwarz::Schwarz`\nnamespace LinearSolver::Schwarz {\n\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief An additive Schwarz subdomain solver for linear systems of equations\n * \\f$Ax=b\\f$.\n *\n * This Schwarz-type linear solver works by solving many sub-problems in\n * parallel and combining their solutions as a weighted sum to converge towards\n * the global solution. Each sub-problem is the restriction of the global\n * problem to an element-centered subdomain, which consists of a central element\n * and an overlap region with its neighbors. The decomposition into independent\n * sub-problems makes this linear solver very parallelizable, avoiding global\n * synchronization points altogether. It is commonly used as a preconditioner to\n * Krylov-type solvers such as `LinearSolver::gmres::Gmres` or\n * `LinearSolver::cg::ConjugateGradient`, or as the \"smoother\" for a Multigrid\n * solver (which may in turn precondition a Krylov-type solver).\n *\n * This linear solver relies on an implementation of the global linear operator\n * \\f$A(x)\\f$ and its restriction to a subdomain \\f$A_{ss}(x)\\f$. Each step of\n * the algorithm expects that \\f$A(x)\\f$ is computed and stored in the DataBox\n * as `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, operand_tag>`.\n * To perform a solve, add the `solve` action list to an array parallel\n * component. Pass the actions that compute \\f$A(x)\\f$, as well as any further\n * actions you wish to run in each step of the algorithm, as the first template\n * parameter to `solve`. If you add the `solve` action list multiple times, use\n * the second template parameter to label each solve with a different type.\n * Pass the subdomain operator as the `SubdomainOperator` template parameter to\n * this class (not to the `solve` action list template). See the paragraph below\n * for information on implementing a subdomain operator.\n *\n * \\par Subdomain geometry:\n * The image below gives an overview of the structure of an element-centered\n * subdomain:\n *\n * \\image html subdomain_structure.svg \"Fig. 1: An element-centered subdomain\" width=600px\n *\n * Fig. 1 shows part of a 2-dimensional computational domain. The domain is\n * composed of elements (light gray) that each carry a Legendre-Gauss-Lobatto\n * mesh of grid points (black dots). The diagonally shaded region to the left\n * illustrates an external domain boundary. The lines between neighboring\n * element faces illustrate mortar meshes, which are relevant when implementing\n * a subdomain operator in a DG context but play no role in the Schwarz\n * algorithm. The dashed line gives an example of an element-centered subdomain\n * with a maximum of 2 grid points of overlap into neighboring elements. For\n * details on how the number of overlap points is chosen see\n * `LinearSolver::Schwarz::overlap_extent`. Note that the subdomain does not\n * extend into corner- or edge-neighbors, which is different to both\n * \\cite Stiller2016a and \\cite Vincent2019qpd. The reason we don't include\n * such diagonal couplings is that in a DG context information only propagates\n * across faces, as noted in \\cite Stiller2016a. By eliminating the corner-\n * and edge-neighbors we significantly reduce the complexity of the subdomain\n * geometry and potentially also the communication costs. However, the\n * advantages and disadvantages of this choice have yet to be carefully\n * evaluated.\n *\n * \\par Subdomain operator:\n * The Schwarz subdomain solver relies on a restriction of the global linear\n * problem \\f$Ax=b\\f$ to the subdomains. The subdomain operator\n * \\f$A_{ss}=R_s A R_s^T\\f$, where \\f$R_s\\f$ is the restriction operator,\n * essentially assumes that its operand is zero everywhere but within the\n * subdomain (see Section 3.1 in \\cite Stiller2016a). Therefore it can be\n * evaluated entirely on an element-centered subdomain with no need to\n * communicate with neighbors within each subdomain-operator application, as\n * opposed to the global linear operator that typically requires\n * nearest-neighbor communications. See\n * `LinearSolver::Schwarz::SubdomainOperator` for details on how to\n * implement a subdomain operator for your problem.\n *\n * \\par Algorithm overview:\n * In each iteration, the Schwarz algorithm computes the residual \\f$r=b-Ax\\f$,\n * restricts it to all subdomains as \\f$r_s=R_s r\\f$ and communicates data on\n * overlap regions with neighboring elements. Once an element has received all\n * data on its subdomain it solves the sub-problem \\f$A_{ss}\\delta x_s=r_s\\f$\n * for the correction \\f$\\delta x_s\\f$, where \\f$\\delta x_s\\f$ and \\f$r_s\\f$\n * have data only on the points of the subdomain and \\f$A_{ss}\\f$ is the\n * subdomain operator. Since all elements perform such a subdomain-solve, we end\n * up with a subdomain solution \\f$\\delta x_s\\f$ on every subdomain, and the\n * solutions overlap. Therefore, the algorithm communicates subdomain solutions\n * on overlap regions with neighboring elements and adds them to the solution\n * field \\f$x\\f$ as a weighted sum.\n *\n * \\par Applying the global linear operator:\n * In order to compute the residual \\f$r=b-Ax\\f$ that will be restricted to the\n * subdomains to serve as source for the subdomain solves, we must apply the\n * global linear operator \\f$A\\f$ to the solution field \\f$x\\f$ once per Schwarz\n * iteration. The global linear operator typically introduces nearest-neighbor\n * couplings between elements but no global synchronization point (as opposed to\n * Krylov-type solvers such as `LinearSolver::gmres::Gmres` that require global\n * inner products in each iteration). The algorithm assumes that the action list\n * passed to `solve` applies the global linear operator, as described above.\n *\n * \\par Subdomain solves:\n * Each subdomain solve is local to an element, since the data on the subdomain\n * has been made available through communication with the neighboring elements.\n * We can now use any means to solve the subdomain problem that's appropriate\n * for the subdomain operator \\f$A_{ss}\\f$. For example, if the subdomain\n * operator was available as an explicit matrix we could invert it directly.\n * Since it is typically a matrix-free operator a common approach to solve the\n * subdomain problem is by means of an iterative Krylov-type method, such as\n * GMRES or Conjugate Gradient, ideally with an appropriate preconditioner (yes,\n * this would be preconditioned Krylov-methods solving the subdomains of the\n * Schwarz solver, which might in turn precondition a global Krylov-solver -\n * it's preconditioners all the way down). Currently, the linear solvers listed\n * in `LinearSolver::Serial` are available to solve subdomains. They include\n * Krylov-type methods that support nested preconditioning with any other linear\n * solver. Additional problem-specific subdomain preconditioners can be added\n * with the `SubdomainPreconditioners` template parameter. Note that the choice\n * of subdomain solver (and, by extension, the choice of subdomain\n * preconditioner) affects only the _performance_ of the Schwarz solver, not its\n * convergence or parallelization properties (assuming the subdomain solutions\n * it produces are sufficiently precise).\n *\n * \\par Weighting:\n * Once the subdomain solutions \\f$\\delta x_s\\f$ have been found they must be\n * combined where they have multiple values, i.e. on overlap regions of the\n * subdomains. We use an additive approach where we combine the subdomain\n * solutions as a weighted sum, which has the advantage over \"multiplicative\"\n * Schwarz methods that the subdomains decouple and can be solved in parallel.\n * See `LinearSolver::Schwarz::element_weight` and Section 3.1 of\n * \\cite Stiller2016a for details. Note that we must account for the missing\n * corner- and edge-neighbors when constructing the weights. See\n * `LinearSolver::Schwarz::intruding_weight` for a discussion.\n *\n * \\par Array sections\n * This linear solver requires no synchronization between elements, so it runs\n * on all elements in the array parallel component. Partitioning of the elements\n * in sections is only relevant for observing residual norms. Pass the section\n * ID tag for the `ArraySectionIdTag` template parameter if residual norms\n * should be computed over a section. Pass `void` (default) to compute residual\n * norms over all elements in the array.\n *\n * \\par Possible improvements:\n * - Specify the number of overlap points as a fraction of the element width\n * instead of a fixed number. This was shown in \\cite Stiller2016b to achieve\n * scale-independent convergence at the cost of increasing subdomain sizes.\n */\ntemplate <typename FieldsTag, typename OptionsGroup, typename SubdomainOperator,\n          typename SubdomainPreconditioners = tmpl::list<>,\n          typename SourceTag =\n              db::add_tag_prefix<::Tags::FixedSource, FieldsTag>,\n          typename ArraySectionIdTag = void>\nstruct Schwarz {\n  using operand_tag = FieldsTag;\n  using fields_tag = FieldsTag;\n  using source_tag = SourceTag;\n  using options_group = OptionsGroup;\n\n  using component_list = tmpl::list<>;\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<async_solvers::reduction_data, detail::reduction_data>>;\n  using subdomain_solver =\n      detail::subdomain_solver<FieldsTag, SubdomainOperator,\n                               SubdomainPreconditioners>;\n\n  using initialize_element = tmpl::list<\n      async_solvers::InitializeElement<FieldsTag, OptionsGroup, SourceTag>,\n      detail::InitializeElement<FieldsTag, OptionsGroup, SubdomainOperator,\n                                SubdomainPreconditioners>>;\n\n  using amr_projectors = initialize_element;\n\n  using register_element = tmpl::list<\n      async_solvers::RegisterElement<FieldsTag, OptionsGroup, SourceTag,\n                                     ArraySectionIdTag>,\n      detail::RegisterElement<FieldsTag, OptionsGroup, SourceTag,\n                              ArraySectionIdTag>,\n      observers::Actions::RegisterWithObservers<\n          detail::RegisterWithVolumeObserver<OptionsGroup, ArraySectionIdTag>>>;\n\n  template <typename ApplyOperatorActions, typename Label = OptionsGroup>\n  using solve = tmpl::list<\n      async_solvers::PrepareSolve<FieldsTag, OptionsGroup, SourceTag, Label,\n                                  ArraySectionIdTag>,\n      detail::SendOverlapData<FieldsTag, OptionsGroup, SubdomainOperator>,\n      detail::SolveSubdomain<FieldsTag, OptionsGroup, SubdomainOperator,\n                             SubdomainPreconditioners, ArraySectionIdTag>,\n      detail::ReceiveOverlapSolution<FieldsTag, OptionsGroup,\n                                     SubdomainOperator>,\n      detail::ObserveVolumeData<FieldsTag, OptionsGroup, SubdomainOperator,\n                                ArraySectionIdTag>,\n      ApplyOperatorActions,\n      async_solvers::CompleteStep<FieldsTag, OptionsGroup, SourceTag, Label,\n                                  ArraySectionIdTag>>;\n};\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/SubdomainOperator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\nnamespace LinearSolver::Schwarz {\n\n/*!\n * \\brief Abstract base class for the subdomain operator, i.e. the linear\n * operator restricted to an element-centered Schwarz subdomain\n *\n * A subdomain operator must implement these member function templates\n * - `operator()`: Applies the operator to the element-centered subdomain data.\n *   It must take these arguments, in this order:\n *   - `gsl::not_null<LinearSolver::Schwarz::ElementCenteredSubdomainData*>`:\n *     The data where the result of the operator applied to the operand should\n *     be written to\n *   - `LinearSolver::Schwarz::ElementCenteredSubdomainData`: The operand data\n *     to which the operator should be applied\n *   - `db::DataBox`: The element's DataBox. Can be used to retrieve any data on\n *     the subdomain geometry and to access and mutate persistent memory\n *     buffers.\n *\n * Since the subdomain operator has access to the element's DataBox, it can\n * retrieve any background data, i.e. data that does not depend on the variables\n * it operates on. Background data on overlap regions with other elements can be\n * either initialized in advance if possible or communicated with the\n * `LinearSolver::Schwarz::Actions::SendOverlapFields` and\n * `LinearSolver::Schwarz::Actions::ReceiveOverlapFields` actions. Note that\n * such communication should not be necessary between iterations of the Schwarz\n * solve, but only between successive solves, because background data should not\n * change during the solve. The Schwarz algorithm takes care of communicating\n * all variable data that the subdomain operator operates on, whenever\n * necessary. This variable data is passed to the operator as the `operand`\n * argument (see above). It includes the data on the central element of the\n * subdomain, as well as the data on overlap regions with neighbors. Since data\n * on the entire subdomain is available, applying the subdomain operator\n * requires _no_ communication between elements. This is the strength of the\n * Schwarz algorithm: all subdomain solves are independent of each other (see\n * `LinearSolver::Schwarz::Schwarz` for details).\n *\n * Here's an example of a subdomain operator that is the restriction of an\n * explicit global matrix:\n *\n * \\snippet Test_SchwarzAlgorithm.cpp subdomain_operator\n */\ntemplate <size_t Dim>\nclass SubdomainOperator {\n public:\n  static constexpr size_t volume_dim = Dim;\n\n  SubdomainOperator() = default;\n};\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Subitems.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\n/// Option tags related to the Schwarz solver\nnamespace OptionTags {\n\ntemplate <typename OptionsGroup>\nstruct MaxOverlap {\n  using type = Options::Auto<size_t>;\n  using group = OptionsGroup;\n  static constexpr Options::String help =\n      \"Number of points that subdomains can extend into neighbors. \"\n      \"'Auto' means no restriction, so the overlap covers the full neighbor.\";\n};\n\ntemplate <typename SolverType, typename OptionsGroup>\nstruct SubdomainSolver {\n  using type = SolverType;\n  using group = OptionsGroup;\n  static constexpr Options::String help = \"The linear solver on subdomains\";\n};\n\ntemplate <typename OptionsGroup>\nstruct SkipSubdomainSolverResets {\n  static std::string name() { return \"SkipResets\"; }\n  using type = bool;\n  using group = OptionsGroup;\n  static constexpr Options::String help =\n      \"Skip resets of the subdomain solver. This only has an effect in cases \"\n      \"where the operator changes, e.g. between nonlinear-solver iterations. \"\n      \"Skipping resets avoids expensive re-building of the operator, but comes \"\n      \"at the cost of less accurate preconditioning and thus potentially more \"\n      \"preconditioned iterations. Whether or not this helps convergence \"\n      \"overall is highly problem-dependent.\";\n};\n\ntemplate <typename OptionsGroup>\nstruct ObservePerCoreReductions {\n  using type = bool;\n  using group = OptionsGroup;\n  static constexpr Options::String help =\n      \"Output statistics per-core in a file per-node, e.g. to assess the load \"\n      \"(im)balance of subdomain solves.\";\n};\n\n}  // namespace OptionTags\n\n/// Tags related to the Schwarz solver\nnamespace Tags {\n\n/// Number of points a subdomain can overlap with its neighbor\ntemplate <typename OptionsGroup>\nstruct MaxOverlap : db::SimpleTag {\n  static std::string name() {\n    return \"MaxOverlap(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = std::optional<size_t>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::MaxOverlap<OptionsGroup>>;\n  static type create_from_options(const type& value) { return value; }\n};\n\n/// The serial linear solver of type `SolverType` used to solve subdomain\n/// operators\ntemplate <typename SolverType, typename OptionsGroup>\nstruct SubdomainSolver : db::SimpleTag {\n  using type = std::unique_ptr<SolverType>;\n  static std::string name() {\n    return \"SubdomainSolver(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<\n      OptionTags::SubdomainSolver<std::unique_ptr<SolverType>, OptionsGroup>>;\n  static type create_from_options(const type& value) {\n    return serialize_and_deserialize<type>(value);\n  }\n};\n\n/// Skip resets of the subdomain solver.\n///\n/// \\see LinearSolver::Schwarz::Actions::ResetSubdomainSolver\ntemplate <typename OptionsGroup>\nstruct SkipSubdomainSolverResets : db::SimpleTag {\n  using type = bool;\n  static constexpr bool pass_metavariables = false;\n  using option_tags =\n      tmpl::list<OptionTags::SkipSubdomainSolverResets<OptionsGroup>>;\n  static bool create_from_options(const bool value) { return value; }\n};\n\n/// Enable per-core reduction observations\ntemplate <typename OptionsGroup>\nstruct ObservePerCoreReductions : db::SimpleTag {\n  using type = bool;\n  static constexpr bool pass_metavariables = false;\n  using option_tags =\n      tmpl::list<OptionTags::ObservePerCoreReductions<OptionsGroup>>;\n  static bool create_from_options(const bool value) { return value; }\n};\n\n/*!\n * \\brief The `Tag` on the overlap region with each neighbor, i.e. on a region\n * extruding from the central element.\n *\n * Note that data on an overlap with a neighbor is typically oriented according\n * to the neighbor's orientation, so re-orientation needs to happen whenever\n * the data cross element boundaries.\n */\ntemplate <typename Tag, size_t Dim, typename OptionsGroup>\nstruct Overlaps : db::SimpleTag {\n  static std::string name() {\n    return \"Overlaps(\" + db::tag_name<Tag>() + \", \" +\n           pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using tag = Tag;\n  using type = OverlapMap<Dim, typename Tag::type>;\n};\n\n/// The number of points a neighbor's subdomain extends into the element\ntemplate <size_t Dim, typename OptionsGroup>\nstruct IntrudingExtents : db::SimpleTag {\n  static std::string name() {\n    return \"IntrudingExtents(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = std::array<size_t, Dim>;\n};\n\n/// The width in element-logical coordinates that a neighbor's subdomain extends\n/// into the element\ntemplate <size_t Dim, typename OptionsGroup>\nstruct IntrudingOverlapWidths : db::SimpleTag {\n  static std::string name() {\n    return \"IntrudingOverlapWidths(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = std::array<double, Dim>;\n};\n\n/// Weighting field for combining data from multiple overlapping subdomains\ntemplate <typename OptionsGroup>\nstruct Weight : db::SimpleTag {\n  static std::string name() {\n    return \"Weight(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief A diagnostic quantity to check that weights are conserved\n *\n * This quantity and the `Tags::Weight` on the element should sum to one on all\n * grid points. Residual values indicate that overlap data from neighboring\n * subdomains and data on the element are combined in a non-conservative way.\n */\ntemplate <typename OptionsGroup>\nstruct SummedIntrudingOverlapWeights : db::SimpleTag {\n  static std::string name() {\n    return \"SummedIntrudingOverlapWeights(\" +\n           pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = Scalar<DataVector>;\n};\n\n/// Buffer for recording volume data\ntemplate <typename SubdomainDataType, typename OptionsGroup>\nstruct VolumeDataForOutput : db::SimpleTag {\n  using type = SubdomainDataType;\n  static std::string name() {\n    return \"VolumeDataForOutput(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n};\n}  // namespace Tags\n}  // namespace LinearSolver::Schwarz\n\nnamespace db {\nnamespace detail {\n// This implementation mirrors the interface tag subitems in `Domain/Tags.hpp`.\n// Please see that implementation for details.\ntemplate <typename VariablesTag, size_t Dim, typename OptionsGroup,\n          typename Tags = typename VariablesTag::type::tags_list>\nstruct OverlapSubitemsImpl;\n\ntemplate <typename VariablesTag, size_t Dim, typename OptionsGroup,\n          typename... Tags>\nstruct OverlapSubitemsImpl<VariablesTag, Dim, OptionsGroup,\n                           tmpl::list<Tags...>> {\n  using type = tmpl::list<\n      LinearSolver::Schwarz::Tags::Overlaps<Tags, Dim, OptionsGroup>...>;\n  using tag =\n      LinearSolver::Schwarz::Tags::Overlaps<VariablesTag, Dim, OptionsGroup>;\n  using return_type = NoSuchType;\n  template <typename Subtag>\n  static void create_item(\n      const gsl::not_null<typename tag::type*> parent_value,\n      const gsl::not_null<typename Subtag::type*> sub_value) {\n    sub_value->clear();\n    for (auto& [overlap_id, parent_overlap_vars] : *parent_value) {\n      auto& parent_overlap_field =\n          get<typename Subtag::tag>(parent_overlap_vars);\n      auto& sub_overlap_field = (*sub_value)[overlap_id];\n      for (size_t i = 0; i < parent_overlap_field.size(); ++i) {\n        sub_overlap_field[i].set_data_ref(&parent_overlap_field[i]);\n      }\n    }\n  }\n  template <typename Subtag>\n  static void create_compute_item(\n      const gsl::not_null<typename Subtag::type*> sub_value,\n      const typename tag::type& parent_value) {\n    for (const auto& [overlap_id, parent_overlap_vars] : parent_value) {\n      const auto& parent_overlap_field =\n          get<typename Subtag::tag>(parent_overlap_vars);\n      auto& sub_overlap_field = (*sub_value)[overlap_id];\n      for (size_t i = 0; i < parent_overlap_field.size(); ++i) {\n        // clang-tidy: do not use const_cast\n        // The DataBox will only give out a const reference to the result of a\n        // compute item. Here, that is a reference to a const map to Tensors of\n        // DataVectors. There is no (publicly visible) indirection there, so\n        // having the map const will allow only const access to the contained\n        // DataVectors, so no modification through the pointer cast here is\n        // possible. See the implementation of subitems in `Domain/Tags.hpp` for\n        // details.\n        sub_overlap_field[i].set_data_ref(\n            const_cast<DataVector*>(&parent_overlap_field[i]));  // NOLINT\n      }\n    }\n  }\n};\n}  // namespace detail\n\ntemplate <typename VariablesTag, size_t Dim, typename OptionsGroup>\nstruct Subitems<\n    LinearSolver::Schwarz::Tags::Overlaps<VariablesTag, Dim, OptionsGroup>,\n    Requires<tt::is_a_v<Variables, typename VariablesTag::type>>>\n    : detail::OverlapSubitemsImpl<VariablesTag, Dim, OptionsGroup> {};\n\n}  // namespace db\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/Weighting.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Weighting.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <string>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Hypercube.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\nDataVector extruding_weight(const DataVector& logical_coords,\n                            const double width, const Side& side) {\n  const double sign = side == Side::Lower ? -1. : 1.;\n  return 0.5 + 0.5 * sign -\n         sign * smoothstep<2>(sign - width, sign + width, logical_coords);\n}\n\nnamespace {\nvoid apply_element_weight(const gsl::not_null<Scalar<DataVector>*> weight,\n                          const DataVector& logical_coords, const double width,\n                          const bool has_overlap_lower,\n                          const bool has_overlap_upper) {\n  if (has_overlap_lower and has_overlap_upper) {\n    get(*weight) *= extruding_weight(logical_coords, width, Side::Lower) +\n                    extruding_weight(logical_coords, width, Side::Upper) - 1.;\n  } else if (has_overlap_lower) {\n    get(*weight) *= extruding_weight(logical_coords, width, Side::Lower);\n  } else if (has_overlap_upper) {\n    get(*weight) *= extruding_weight(logical_coords, width, Side::Upper);\n  }\n}\n}  // namespace\n\ntemplate <size_t Dim>\nvoid element_weight(\n    const gsl::not_null<Scalar<DataVector>*> weight,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, Dim>& overlap_widths,\n    const std::unordered_set<Direction<Dim>>& external_boundaries) {\n  set_number_of_grid_points(weight, logical_coords);\n  get(*weight) = 1.;\n  for (size_t d = 0; d < Dim; ++d) {\n    ASSERT(gsl::at(overlap_widths, d) > 0,\n           \"Don't try to apply weighting when the overlap has zero width.\");\n    apply_element_weight(\n        weight, logical_coords.get(d), gsl::at(overlap_widths, d),\n        external_boundaries.find(Direction<Dim>{d, Side::Lower}) ==\n            external_boundaries.end(),\n        external_boundaries.find(Direction<Dim>{d, Side::Upper}) ==\n            external_boundaries.end());\n  }\n}\n\ntemplate <size_t Dim>\nScalar<DataVector> element_weight(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, Dim>& overlap_widths,\n    const std::unordered_set<Direction<Dim>>& external_boundaries) {\n  Scalar<DataVector> weight{logical_coords.begin()->size()};\n  element_weight(make_not_null(&weight), logical_coords, overlap_widths,\n                 external_boundaries);\n  return weight;\n}\n\nDataVector intruding_weight(const DataVector& logical_coords,\n                            const double width, const Side& side) {\n  const double sign = side == Side::Lower ? -1. : 1.;\n  return extruding_weight(logical_coords - sign * 2., width, opposite(side));\n}\n\nnamespace {\nsize_t dim_in_volume(const size_t dim_in_slice, const size_t sliced_dim) {\n  return dim_in_slice >= sliced_dim ? (dim_in_slice + 1) : dim_in_slice;\n}\n}  // namespace\n\ntemplate <size_t Dim>\nvoid intruding_weight(\n    const gsl::not_null<Scalar<DataVector>*> weight,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const Direction<Dim>& direction,\n    const std::array<double, Dim>& overlap_widths,\n    const size_t num_intruding_overlaps,\n    const std::unordered_set<Direction<Dim>>& external_boundaries) {\n  static_assert(Dim > 0 and Dim <= 3,\n                \"This function supports one, two and three dimensions.\");\n  ASSERT(gsl::at(overlap_widths, direction.dimension()) > 0,\n         \"Don't try to apply weighting when the overlap has zero width.\");\n  if constexpr (Dim == 1) {\n    get(*weight) =\n        intruding_weight(logical_coords.get(direction.dimension()),\n                         gsl::at(overlap_widths, direction.dimension()),\n                         direction.side()) /\n        num_intruding_overlaps;\n  } else {\n    const size_t num_points = logical_coords.begin()->size();\n    set_number_of_grid_points(weight, num_points);\n    get(*weight) = 1.;\n    const auto has_overlap = [&external_boundaries](const size_t dimension,\n                                                    const Side side) {\n      return external_boundaries.find(Direction<Dim>{dimension, side}) ==\n             external_boundaries.end();\n    };\n    // Apply weighting perpendicular to the overlap direction\n    for (size_t d = 0; (d == direction.dimension() ? ++d : d) < Dim; ++d) {\n      ASSERT(gsl::at(overlap_widths, d) > 0,\n             \"Don't try to apply weighting when the overlap has zero width.\");\n      apply_element_weight(\n          weight, logical_coords.get(d), gsl::at(overlap_widths, d),\n          has_overlap(d, Side::Lower), has_overlap(d, Side::Upper));\n    }\n    // Add contributions from the corners and edges of the subdomain\n    // These contributions account for the corner- and edge-neighbors not being\n    // part of the subdomain, and thus not contributing weights. To retain\n    // conservation, we add the missing weights to the intruding overlaps here.\n    // See the function documentation for details.\n    const auto skip_corner =\n        [&direction, &has_overlap](const Vertex<Dim - 1>& corner) {\n          // Skip corners to external boundaries\n          for (size_t d = 0; d < Dim - 1; ++d) {\n            if (not has_overlap(dim_in_volume(d, direction.dimension()),\n                                corner.side_in_parent_dimension(d))) {\n              return true;\n            }\n          }\n          return false;\n        };\n    Scalar<DataVector> corner_weight{num_points};\n    for (const auto corner : VertexIterator<Dim - 1>{}) {\n      if (skip_corner(corner)) {\n        continue;\n      }\n      get(corner_weight) = 1.;\n      for (size_t d = 0; d < Dim - 1; ++d) {\n        const size_t d_vol = dim_in_volume(d, direction.dimension());\n        get(corner_weight) *= intruding_weight(\n            logical_coords.get(d_vol), gsl::at(overlap_widths, d_vol),\n            corner.side_in_parent_dimension(d));\n      }\n      // Divide equally between all face-neighbors that share this corner\n      get(*weight) += get(corner_weight) / Dim;\n    }\n    if constexpr (Dim == 3) {\n      Scalar<DataVector> edge_weight{num_points};\n      for (const auto edge : EdgeIterator<Dim - 1>{}) {\n        const size_t d_perp =\n            dim_in_volume(dim_in_volume(0, edge.dimension_in_parent()),\n                          direction.dimension());\n        // Skip edges to external boundaries\n        if (not has_overlap(d_perp, edge.side())) {\n          continue;\n        }\n        get(edge_weight) = 1.;\n        const size_t d_edge =\n            dim_in_volume(edge.dimension_in_parent(), direction.dimension());\n        apply_element_weight(\n            make_not_null(&edge_weight), logical_coords.get(d_edge),\n            gsl::at(overlap_widths, d_edge), has_overlap(d_edge, Side::Lower),\n            has_overlap(d_edge, Side::Upper));\n        get(edge_weight) *=\n            intruding_weight(logical_coords.get(d_perp),\n                             gsl::at(overlap_widths, d_perp), edge.side());\n        // Divide equally between all face-neighbors that share this edge\n        get(*weight) += get(edge_weight) / (Dim - 1);\n      }\n    }\n    // Apply weighting along the overlap direction\n    get(*weight) *=\n        intruding_weight(logical_coords.get(direction.dimension()),\n                         gsl::at(overlap_widths, direction.dimension()),\n                         direction.side()) /\n        num_intruding_overlaps;\n  }\n}\n\ntemplate <size_t Dim>\nScalar<DataVector> intruding_weight(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const Direction<Dim>& direction,\n    const std::array<double, Dim>& overlap_widths,\n    const size_t num_intruding_overlaps,\n    const std::unordered_set<Direction<Dim>>& external_boundaries) {\n  Scalar<DataVector> weight{logical_coords.begin()->size()};\n  intruding_weight(make_not_null(&weight), logical_coords, direction,\n                   overlap_widths, num_intruding_overlaps, external_boundaries);\n  return weight;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(r, data)                                                \\\n  template void element_weight(                                             \\\n      gsl::not_null<Scalar<DataVector>*> weight,                            \\\n      const tnsr::I<DataVector, DIM(data), Frame::ElementLogical>&          \\\n          logical_coords,                                                   \\\n      const std::array<double, DIM(data)>& overlap_widths,                  \\\n      const std::unordered_set<Direction<DIM(data)>>& external_boundaries); \\\n  template Scalar<DataVector> element_weight(                               \\\n      const tnsr::I<DataVector, DIM(data), Frame::ElementLogical>&          \\\n          logical_coords,                                                   \\\n      const std::array<double, DIM(data)>& overlap_widths,                  \\\n      const std::unordered_set<Direction<DIM(data)>>& external_boundaries); \\\n  template void intruding_weight(                                           \\\n      gsl::not_null<Scalar<DataVector>*> weight,                            \\\n      const tnsr::I<DataVector, DIM(data), Frame::ElementLogical>&          \\\n          logical_coords,                                                   \\\n      const Direction<DIM(data)>& direction,                                \\\n      const std::array<double, DIM(data)>& overlap_widths,                  \\\n      size_t num_intruding_overlaps,                                        \\\n      const std::unordered_set<Direction<DIM(data)>>& external_boundaries); \\\n  template Scalar<DataVector> intruding_weight(                             \\\n      const tnsr::I<DataVector, DIM(data), Frame::ElementLogical>&          \\\n          logical_coords,                                                   \\\n      const Direction<DIM(data)>& direction,                                \\\n      const std::array<double, DIM(data)>& overlap_widths,                  \\\n      size_t num_intruding_overlaps,                                        \\\n      const std::unordered_set<Direction<DIM(data)>>& external_boundaries);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Schwarz/Weighting.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\n/*!\n * \\brief Weights for the solution on an element-centered subdomain, decreasing\n * from 1 to 0.5 towards the `side` over the logical distance `width`, and\n * further to 0 over the same distance outside the element.\n *\n * The weighting function over a full element-centered subdomain is\n *\n * \\f{equation}\n * w(\\xi) = \\frac{1}{2}\\left( \\phi\\left( \\frac{\\xi + 1}{\\delta} \\right) -\n * \\phi\\left( \\frac{\\xi - 1}{\\delta} \\right) \\right) \\f}\n *\n * where \\f$\\phi(\\xi)\\f$ is a second-order `::smoothstep`, i.e. the quintic\n * polynomial\n *\n * \\f{align*}\n * \\phi(\\xi) = \\begin{cases} \\mathrm{sign}(\\xi) \\quad \\text{for}\n * \\quad |\\xi| > 1 \\\\\n * \\frac{1}{8}\\left(15\\xi - 10\\xi^3 + 3\\xi^5\\right) \\end{cases}\n * \\f}\n *\n * (see Eq. (39) in \\cite Vincent2019qpd).\n *\n * The `LinearSolver::Schwarz::extruding_weight` and\n * `LinearSolver::Schwarz::intruding_weight` functions each compute one of the\n * two terms in \\f$w(\\xi)\\f$. For example, consider an element-centered\n * subdomain `A` that overlaps with a neighboring element-centered subdomain\n * `B`. To combine solutions on `A` and `B` to a weighted solution on `A`,\n * multiply the solution on `A` with the `extruding_weight` and the solution on\n * `B` with the `intruding_weight`, both evaluated at the logical coordinates in\n * `A` and at the `side` of `A` that faces `B`.\n */\nDataVector extruding_weight(const DataVector& logical_coords, double width,\n                            const Side& side);\n\n/// @{\n/*!\n * \\brief Weights for data on the central element of an element-centered\n * subdomain\n *\n * Constructs the weighting field\n *\n * \\f{equation}\n * W(\\boldsymbol{\\xi}) = \\prod^d_{i=0} w(\\xi^i)\n * \\f}\n *\n * where \\f$w(\\xi^i)\\f$ is the one-dimensional weighting function described in\n * `LinearSolver::Schwarz::extruding_weight` and \\f$\\xi^i\\f$ are the\n * element-logical coordinates (see Eq. (41) in \\cite Vincent2019qpd).\n */\ntemplate <size_t Dim>\nvoid element_weight(\n    gsl::not_null<Scalar<DataVector>*> element_weight,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, Dim>& overlap_widths,\n    const std::unordered_set<Direction<Dim>>& external_boundaries);\n\ntemplate <size_t Dim>\nScalar<DataVector> element_weight(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, Dim>& overlap_widths,\n    const std::unordered_set<Direction<Dim>>& external_boundaries);\n/// @}\n\n/*!\n * \\brief Weights for the intruding solution of a neighboring element-centered\n * subdomain, increasing from 0 to 0.5 towards the `side` over the logical\n * distance `width`, and further to 1 over the same distance outside the\n * element.\n *\n * \\see `LinearSolver::Schwarz::extruding_weight`\n */\nDataVector intruding_weight(const DataVector& logical_coords, double width,\n                            const Side& side);\n\n/// @{\n/*!\n * \\brief Weights for data on overlap regions intruding into an element-centered\n * subdomain\n *\n * Constructs the weighting field \\f$W(\\xi)\\f$ as described in\n * `LinearSolver::Schwarz::element_weight` for the data that overlaps with the\n * central element of an element-centered subdomain. The weights are constructed\n * in such a way that all weights at a grid point sum to one, i.e. the weight is\n * conserved. The `logical_coords` are the element-logical coordinates of the\n * central element.\n *\n * This function assumes that corner- and edge-neighbors of the central element\n * are not part of the subdomain, which means that no contributions from those\n * neighbors are expected although the weighting field is non-zero in overlap\n * regions with those neighbors. Therefore, to retain conservation we must\n * account for this missing weight by adding it to the central element, to the\n * intruding overlaps from face-neighbors, or split it between the two. We\n * choose to add the weight to the intruding overlaps, since that's where\n * information from the corner- and edge-regions propagates through in a DG\n * context.\n */\ntemplate <size_t Dim>\nvoid intruding_weight(\n    gsl::not_null<Scalar<DataVector>*> weight,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const Direction<Dim>& direction,\n    const std::array<double, Dim>& overlap_widths,\n    size_t num_intruding_overlaps,\n    const std::unordered_set<Direction<Dim>>& external_boundaries);\n\ntemplate <size_t Dim>\nScalar<DataVector> intruding_weight(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const Direction<Dim>& direction,\n    const std::array<double, Dim>& overlap_widths,\n    size_t num_intruding_overlaps,\n    const std::unordered_set<Direction<Dim>>& external_boundaries);\n/// @}\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "src/ParallelAlgorithms/LinearSolver/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines DataBox tags for the linear solver\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief Functionality for solving linear systems of equations\n */\nnamespace LinearSolver {\n\nnamespace OptionTags {\n\ntemplate <typename OptionsGroup>\nstruct OutputVolumeData {\n  using type = bool;\n  static constexpr Options::String help =\n      \"Record volume data for debugging purposes.\";\n  using group = OptionsGroup;\n  static bool suggested_value() { return false; }\n};\n\n}  // namespace OptionTags\n\n/*!\n * \\ingroup LinearSolverGroup\n * \\brief The \\ref DataBoxGroup tags associated with the linear solver\n */\nnamespace Tags {\n\n/*!\n * \\brief The operand that the local linear operator \\f$A\\f$ is applied to\n *\n * \\details The result of the operation should be wrapped in\n * `LinearSolver::Tags::OperatorAppliedTo`.\n */\ntemplate <typename Tag>\nstruct Operand : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    // Add \"Linear\" prefix to abbreviate the namespace for uniqueness\n    return \"LinearOperand(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/*!\n * \\brief The linear operator \\f$A\\f$ applied to the data in `Tag`\n */\ntemplate <typename Tag>\nstruct OperatorAppliedTo : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    // Add \"Linear\" prefix to abbreviate the namespace for uniqueness\n    return \"LinearOperatorAppliedTo(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/*!\n * \\brief The residual \\f$r=b - Ax\\f$\n */\ntemplate <typename Tag>\nstruct Residual : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    // Add \"Linear\" prefix to abbreviate the namespace for uniqueness\n    return \"LinearResidual(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// Compute the residual \\f$r=b - Ax\\f$ from the `SourceTag` \\f$b\\f$ and the\n/// `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, FieldsTag>`\n/// \\f$Ax\\f$.\ntemplate <typename FieldsTag, typename SourceTag>\nstruct ResidualCompute : db::add_tag_prefix<Residual, FieldsTag>,\n                         db::ComputeTag {\n  using base = db::add_tag_prefix<Residual, FieldsTag>;\n  using argument_tags =\n      tmpl::list<SourceTag, db::add_tag_prefix<OperatorAppliedTo, FieldsTag>>;\n  using return_type = typename base::type;\n  static void function(\n      const gsl::not_null<return_type*> residual,\n      const typename SourceTag::type& source,\n      const typename db::add_tag_prefix<OperatorAppliedTo, FieldsTag>::type&\n          operator_applied_to_fields) {\n    *residual = source - operator_applied_to_fields;\n  }\n};\n\n/*!\n * \\brief The magnitude square \\f$\\langle \\cdot,\\cdot\\rangle\\f$ w.r.t.\n * the `LinearSolver::inner_product`\n */\ntemplate <typename Tag>\nstruct MagnitudeSquare : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    // Add \"Linear\" prefix to abbreviate the namespace for uniqueness\n    return \"LinearMagnitudeSquare(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = double;\n  using tag = Tag;\n};\n\n/*!\n * \\brief The magnitude \\f$\\sqrt{\\langle \\cdot,\\cdot\\rangle}\\f$ w.r.t.\n * the `LinearSolver::inner_product`\n */\ntemplate <typename Tag>\nstruct Magnitude : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    // Add \"Linear\" prefix to abbreviate the namespace for uniqueness\n    return \"LinearMagnitude(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = double;\n  using tag = Tag;\n};\n\n/*!\n * \\brief The prefix for tags related to an orthogonalization procedure\n */\ntemplate <typename Tag>\nstruct Orthogonalization : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    // Add \"Linear\" prefix to abbreviate the namespace for uniqueness\n    return \"LinearOrthogonalization(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/*!\n * \\brief A Hessenberg matrix built up during an orthogonalization procedure\n */\ntemplate <typename Tag>\nstruct OrthogonalizationHistory : db::PrefixTag, db::SimpleTag {\n  using ValueType = tt::get_complex_or_fundamental_type_t<typename Tag::type>;\n  static std::string name() {\n    // Add \"Linear\" prefix to abbreviate the namespace for uniqueness\n    return \"LinearOrthogonalizationHistory(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = blaze::DynamicMatrix<ValueType>;\n  using tag = Tag;\n};\n\n/*!\n * \\brief A set of \\f$n\\f$ vectors that form a basis of the \\f$n\\f$-th Krylov\n * subspace \\f$K_n(A,b)\\f$\n *\n * \\details The Krylov subspace \\f$K_n(A,b)\\f$ spanned by this basis is the one\n * generated by the linear operator \\f$A\\f$ and source \\f$b\\f$ that are\n * represented by the tags\n * `db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo,\n * db::add_tag_prefix<LinearSolver::Tags::Operand, Tag>>` and\n * `db::add_tag_prefix<::Tags::FixedSource, Tag>`, respectively. Therefore, each\n * basis vector is of the type db::add_tag_prefix<Operand, Tag>::type.\n */\ntemplate <typename Tag>\nstruct KrylovSubspaceBasis : db::PrefixTag, db::SimpleTag {\n  // Use automatically generated name because a Krylov subspace always refers to\n  // a linear operator\n  using type = std::vector<typename Tag::type>;\n  using tag = Tag;\n};\n\n/// Indicates the `Tag` is related to preconditioning of the linear solve\ntemplate <typename Tag>\nstruct Preconditioned : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// Whether or not volume data should be recorded for debugging purposes\ntemplate <typename OptionsGroup>\nstruct OutputVolumeData : db::SimpleTag {\n  using type = bool;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::OutputVolumeData<OptionsGroup>>;\n  static type create_from_options(const type value) { return value; };\n  static std::string name() {\n    return \"OutputVolumeData(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n};\n\n/// Continuously incrementing ID for volume observations\ntemplate <typename OptionsGroup>\nstruct ObservationId : db::SimpleTag {\n  using type = size_t;\n  static std::string name() {\n    return \"ObservationId(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n};\n\n}  // namespace Tags\n}  // namespace LinearSolver\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ParallelNonlinearSolver)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Observe.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ErrorHandling\n  Utilities\n  INTERFACE\n  Convergence\n  DataStructures\n  Initialization\n  LinearSolver\n  Logging\n  Observer\n  Options\n  Parallel\n  ParallelAmr\n  ParallelLinearSolver\n  Printf\n  Serialization\n  SystemUtilities\n  )\n\nadd_subdirectory(NewtonRaphson)\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  LineSearch.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ElementActions.hpp\n  LineSearch.hpp\n  NewtonRaphson.hpp\n  ResidualMonitor.hpp\n  ResidualMonitorActions.hpp\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/ElementActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <tuple>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GetSection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/ResidualMonitorActions.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Tags/InboxTags.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace NonlinearSolver::newton_raphson::detail {\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup>\nstruct ResidualMonitor;\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct ReceiveInitialHasConverged;\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct PrepareStep;\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct Globalize;\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct CompleteStep;\n}  // namespace NonlinearSolver::newton_raphson::detail\n/// \\endcond\n\nnamespace NonlinearSolver::newton_raphson::detail {\n\nusing ResidualReductionData = Parallel::ReductionData<\n    // Iteration ID\n    Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n    // Globalization iteration ID\n    Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,\n    // Residual magnitude square\n    Parallel::ReductionDatum<double, funcl::Plus<>>,\n    // Step length\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>>;\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename SourceTag>\nstruct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {\n private:\n  using fields_tag = FieldsTag;\n  using source_tag = SourceTag;\n  using nonlinear_operator_applied_to_fields_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::OperatorAppliedTo, fields_tag>;\n  using correction_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Correction, fields_tag>;\n  using globalization_fields_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Globalization, fields_tag>;\n\n public:  // Iterable action\n  using simple_tags =\n      tmpl::list<Convergence::Tags::IterationId<OptionsGroup>,\n                 Convergence::Tags::HasConverged<OptionsGroup>,\n                 nonlinear_operator_applied_to_fields_tag, correction_tag,\n                 NonlinearSolver::Tags::Globalization<\n                     Convergence::Tags::IterationId<OptionsGroup>>,\n                 NonlinearSolver::Tags::StepLength<OptionsGroup>,\n                 globalization_fields_tag>;\n  using compute_tags = tmpl::list<\n      NonlinearSolver::Tags::ResidualCompute<fields_tag, source_tag>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    ::Initialization::mutate_assign<\n        tmpl::list<Convergence::Tags::IterationId<OptionsGroup>,\n                   NonlinearSolver::Tags::Globalization<\n                       Convergence::Tags::IterationId<OptionsGroup>>,\n                   NonlinearSolver::Tags::StepLength<OptionsGroup>>>(\n        make_not_null(&box), std::numeric_limits<size_t>::max(),\n        std::numeric_limits<size_t>::max(),\n        std::numeric_limits<double>::signaling_NaN());\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // amr::protocols::Projector\n  using argument_tags = tmpl::list<>;\n  using return_tags = simple_tags;\n\n  template <typename... AmrData>\n  static void apply(const gsl::not_null<size_t*> /*unused*/,\n                    const AmrData&... /*all_items*/) {\n    // No need to reset or initialize any of the items during AMR because they\n    // will be set in `PrepareSolve` below. AMR can't happen _during_ a solve.\n  }\n};\n\n// Reset to the initial state of the algorithm.\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct PrepareSolve {\n private:\n  using fields_tag = FieldsTag;\n  using nonlinear_residual_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Convergence::Tags::IterationId<OptionsGroup>>(\n        [](const gsl::not_null<size_t*> iteration_id) { *iteration_id = 0; },\n        make_not_null(&box));\n\n    // Skip the initial reduction on elements that are not part of the section\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        constexpr size_t receive_initial_index = tmpl::index_of<\n            ActionList,\n            ReceiveInitialHasConverged<FieldsTag, OptionsGroup, Label,\n                                       ArraySectionIdTag>>::value;\n        return {Parallel::AlgorithmExecution::Continue, receive_initial_index};\n      }\n    }\n\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\"%s %s: Prepare solve\\n\", get_output(array_index),\n                       pretty_type::name<OptionsGroup>());\n    }\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// To determine the initial residual magnitude and to check if the algorithm has\n// already converged, we perform a global reduction to the `ResidualMonitor`.\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct SendInitialResidualMagnitude {\n private:\n  using fields_tag = FieldsTag;\n  using nonlinear_residual_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Perform a global reduction to compute the initial residual magnitude\n    const auto& residual = db::get<nonlinear_residual_tag>(box);\n    const double local_residual_magnitude_square =\n        LinearSolver::magnitude_square(residual);\n    ResidualReductionData reduction_data{0, 0, local_residual_magnitude_square,\n                                         1.};\n    auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<\n        CheckResidualMagnitude<FieldsTag, OptionsGroup, ParallelComponent>>(\n        std::move(reduction_data),\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        Parallel::get_parallel_component<\n            ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache),\n        make_not_null(&section));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Wait for the broadcast from the `ResidualMonitor` to complete the preparation\n// for the solve. We skip the solve altogether if the algorithm has already\n// converged.\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct ReceiveInitialHasConverged {\n  using inbox_tags = tmpl::list<Tags::GlobalizationResult<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    auto& inbox = get<Tags::GlobalizationResult<OptionsGroup>>(inboxes);\n    if (inbox.find(db::get<Convergence::Tags::IterationId<OptionsGroup>>(\n            box)) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Retrieve reduction data from inbox\n    auto globalization_result = std::move(inbox.extract(iteration_id).mapped());\n    ASSERT(\n        std::holds_alternative<Convergence::HasConverged>(globalization_result),\n        \"No globalization should occur for the initial residual. This is a \"\n        \"bug, so please file an issue.\");\n    auto& has_converged = get<Convergence::HasConverged>(globalization_result);\n    db::mutate<Convergence::Tags::HasConverged<OptionsGroup>>(\n        [&has_converged](const gsl::not_null<Convergence::HasConverged*>\n                             local_has_converged) {\n          *local_has_converged = std::move(has_converged);\n        },\n        make_not_null(&box));\n\n    // Skip steps entirely if the solve has already converged\n    constexpr size_t complete_step_index =\n        tmpl::index_of<ActionList, CompleteStep<FieldsTag, OptionsGroup, Label,\n                                                ArraySectionIdTag>>::value;\n    if (get<Convergence::Tags::HasConverged<OptionsGroup>>(box)) {\n      return {Parallel::AlgorithmExecution::Continue, complete_step_index + 1};\n    }\n\n    // Skip the solve entirely on elements that are not part of the section. To\n    // do so, we skip ahead to the `SolveLinearization` action list between\n    // `PrepareStep` and `PerformStep`. Those actions need a chance to run even\n    // on elements that are not part of the section, because they may take part\n    // in preconditioning (see Multigrid preconditioner).\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<Parallel::Tags::Section<ParallelComponent,\n                                              ArraySectionIdTag>>(box)\n                  .has_value()) {\n        db::mutate<Convergence::Tags::IterationId<OptionsGroup>>(\n            [](const gsl::not_null<size_t*> local_iteration_id) {\n              *local_iteration_id = 1;\n            },\n            make_not_null(&box));\n        constexpr size_t prepare_step_index =\n            tmpl::index_of<ActionList,\n                           PrepareStep<FieldsTag, OptionsGroup, Label,\n                                       ArraySectionIdTag>>::value;\n        return {Parallel::AlgorithmExecution::Continue, prepare_step_index + 1};\n      }\n    }\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Prepare the next Newton-Raphson step. In particular, we prepare the DataBox\n// for the linear solver which will run after this action to solve the\n// linearized problem for the `correction_tag`. The source for the linear\n// solver is the residual, which is in the DataBox as a compute tag so needs no\n// preparation. We only need to set the initial guess.\n//\n// We also prepare the line-search globalization here. Since we don't know if\n// a step will be sufficient before taking it, we have to store the original\n// field values.\n//\n// The algorithm jumps back to this action from `CompleteStep` to continue\n// iterating the nonlinear solve.\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct PrepareStep {\n private:\n  using fields_tag = FieldsTag;\n  using correction_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Correction, fields_tag>;\n  using linear_operator_applied_to_correction_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, correction_tag>;\n  using globalization_fields_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Globalization, fields_tag>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<NonlinearSolver::Tags::DampingFactor<OptionsGroup>,\n                 logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\n          \"%s %s(%zu): Prepare step\\n\", get_output(array_index),\n          pretty_type::name<OptionsGroup>(),\n          db::get<Convergence::Tags::IterationId<OptionsGroup>>(box) + 1);\n    }\n\n    db::mutate<Convergence::Tags::IterationId<OptionsGroup>, correction_tag,\n               linear_operator_applied_to_correction_tag,\n               NonlinearSolver::Tags::Globalization<\n                   Convergence::Tags::IterationId<OptionsGroup>>,\n               NonlinearSolver::Tags::StepLength<OptionsGroup>,\n               globalization_fields_tag>(\n        [](const gsl::not_null<size_t*> iteration_id, const auto correction,\n           const auto linear_operator_applied_to_correction,\n           const gsl::not_null<size_t*> globalization_iteration_id,\n           const gsl::not_null<double*> step_length,\n           const auto globalization_fields, const auto& fields,\n           const double damping_factor) {\n          ++(*iteration_id);\n          // Begin the linear solve with a zero initial guess\n          *correction =\n              make_with_value<typename correction_tag::type>(fields, 0.);\n          // Since the initial guess is zero, we don't need to apply the linear\n          // operator to it but can just set it to zero as well. Linear things\n          // are nice :)\n          *linear_operator_applied_to_correction = make_with_value<\n              typename linear_operator_applied_to_correction_tag::type>(fields,\n                                                                        0.);\n          // Prepare line search globalization\n          *globalization_iteration_id = 0;\n          *step_length = damping_factor;\n          *globalization_fields = fields;\n        },\n        make_not_null(&box), db::get<fields_tag>(box),\n        db::get<NonlinearSolver::Tags::DampingFactor<OptionsGroup>>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Between `PrepareStep` and this action the linear solver has run, so the\n// `correction_tag` is now filled with the solution to the linearized problem.\n// We just take a step in the direction of the correction.\n//\n// The `Globalize` action will jump back here in case the step turned out to not\n// decrease the residual sufficiently. It will have adjusted the step length, so\n// we can just try again with a step of that length.\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct PerformStep {\n private:\n  using fields_tag = FieldsTag;\n  using correction_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Correction, fields_tag>;\n  using globalization_fields_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Globalization, fields_tag>;\n\n public:\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Skip to the end of the step on elements that are not part of the section\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<\n              Parallel::Tags::Section<ParallelComponent, ArraySectionIdTag>>(\n              box)) {\n        constexpr size_t globalize_index =\n            tmpl::index_of<ActionList, Globalize<FieldsTag, OptionsGroup, Label,\n                                                 ArraySectionIdTag>>::value;\n        return {Parallel::AlgorithmExecution::Continue, globalize_index};\n      }\n    }\n\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\n          \"%s %s(%zu): Perform step with length: %g\\n\", get_output(array_index),\n          pretty_type::name<OptionsGroup>(),\n          db::get<Convergence::Tags::IterationId<OptionsGroup>>(box),\n          db::get<NonlinearSolver::Tags::StepLength<OptionsGroup>>(box));\n    }\n\n    // Apply the correction that the linear solve has determined to attempt\n    // improving the nonlinear solution\n    db::mutate<fields_tag>(\n        [](const auto fields, const auto& correction, const double step_length,\n           const auto& globalization_fields) {\n          *fields = globalization_fields + step_length * correction;\n        },\n        make_not_null(&box), db::get<correction_tag>(box),\n        db::get<NonlinearSolver::Tags::StepLength<OptionsGroup>>(box),\n        db::get<globalization_fields_tag>(box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Between `PerformStep` and this action the nonlinear operator has been applied\n// to the updated fields. The residual is up-to-date because it is a compute\n// tag, so at this point we need to check if the step has sufficiently reduced\n// the residual. We perform a global reduction to the `ResidualMonitor` for this\n// purpose.\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct ContributeToResidualMagnitudeReduction {\n private:\n  using fields_tag = FieldsTag;\n  using nonlinear_residual_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;\n\n public:\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& residual = db::get<nonlinear_residual_tag>(box);\n    const double local_residual_magnitude_square =\n        LinearSolver::magnitude_square(residual);\n    ResidualReductionData reduction_data{\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box),\n        db::get<NonlinearSolver::Tags::Globalization<\n            Convergence::Tags::IterationId<OptionsGroup>>>(box),\n        local_residual_magnitude_square,\n        db::get<NonlinearSolver::Tags::StepLength<OptionsGroup>>(box)};\n    auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<\n        CheckResidualMagnitude<FieldsTag, OptionsGroup, ParallelComponent>>(\n        std::move(reduction_data),\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        Parallel::get_parallel_component<\n            ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache),\n        make_not_null(&section));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Wait for the `ResidualMonitor` to broadcast whether or not it has determined\n// the step has sufficiently decreased the residual. If it has, we just proceed\n// to `CompleteStep`. If it hasn't, the `ResidualMonitor` has also sent the new\n// step length along, so we mutate it in the DataBox and jump back to\n// `PerformStep` to try again with the updated step length.\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct Globalize {\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n  using inbox_tags = tmpl::list<Tags::GlobalizationResult<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox = get<Tags::GlobalizationResult<OptionsGroup>>(inboxes);\n    const size_t iteration_id =\n        db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);\n    if (inbox.find(iteration_id) == inbox.end()) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Retrieve reduction data from inbox\n    auto globalization_result = std::move(inbox.extract(iteration_id).mapped());\n\n    // Elements that are not part of the section jump directly to the\n    // `SolveLinearization` for the next step.\n    constexpr size_t complete_step_index =\n        tmpl::index_of<ActionList, CompleteStep<FieldsTag, OptionsGroup, Label,\n                                                ArraySectionIdTag>>::value;\n    constexpr size_t prepare_step_index =\n        tmpl::index_of<ActionList, PrepareStep<FieldsTag, OptionsGroup, Label,\n                                               ArraySectionIdTag>>::value;\n    if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {\n      if (not db::get<\n              Parallel::Tags::Section<ParallelComponent, ArraySectionIdTag>>(\n              box)) {\n        const bool globalization_is_complete =\n            std::holds_alternative<Convergence::HasConverged>(\n                globalization_result);\n        // Wait until globalization is complete\n        if (not globalization_is_complete) {\n          return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n        }\n        auto& has_converged =\n            get<Convergence::HasConverged>(globalization_result);\n\n        db::mutate<Convergence::Tags::HasConverged<OptionsGroup>,\n                   Convergence::Tags::IterationId<OptionsGroup>>(\n            [&has_converged](const gsl::not_null<Convergence::HasConverged*>\n                                 local_has_converged,\n                             const gsl::not_null<size_t*> local_iteration_id) {\n              *local_has_converged = std::move(has_converged);\n              ++(*local_iteration_id);\n            },\n            make_not_null(&box));\n\n        return {Parallel::AlgorithmExecution::Continue,\n                get<Convergence::Tags::HasConverged<OptionsGroup>>(box)\n                    ? (complete_step_index + 1)\n                    : (prepare_step_index + 1)};\n      }\n    }\n\n    if (std::holds_alternative<double>(globalization_result)) {\n      if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                   ::Verbosity::Debug)) {\n        Parallel::printf(\n            \"%s %s(%zu): Globalize(%zu)\\n\", get_output(array_index),\n            pretty_type::name<OptionsGroup>(),\n            db::get<Convergence::Tags::IterationId<OptionsGroup>>(box),\n            db::get<NonlinearSolver::Tags::Globalization<\n                Convergence::Tags::IterationId<OptionsGroup>>>(box));\n      }\n\n      // Update the step length\n      db::mutate<NonlinearSolver::Tags::StepLength<OptionsGroup>,\n                 NonlinearSolver::Tags::Globalization<\n                     Convergence::Tags::IterationId<OptionsGroup>>>(\n          [&globalization_result](\n              const gsl::not_null<double*> step_length,\n              const gsl::not_null<size_t*> globalization_iteration_id) {\n            *step_length = get<double>(globalization_result);\n            ++(*globalization_iteration_id);\n          },\n          make_not_null(&box));\n      // Continue globalization by taking a step with the updated step length\n      // and checking the residual again\n      constexpr size_t perform_step_index =\n          tmpl::index_of<ActionList, PerformStep<FieldsTag, OptionsGroup, Label,\n                                                 ArraySectionIdTag>>::value;\n      return {Parallel::AlgorithmExecution::Continue, perform_step_index};\n    }\n\n    // At this point globalization is complete, so we proceed with the algorithm\n    auto& has_converged = get<Convergence::HasConverged>(globalization_result);\n\n    db::mutate<Convergence::Tags::HasConverged<OptionsGroup>>(\n        [&has_converged](const gsl::not_null<Convergence::HasConverged*>\n                             local_has_converged) {\n          *local_has_converged = std::move(has_converged);\n        },\n        make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Jump back to `PrepareStep` to continue iterating if the algorithm has not yet\n// converged, or complete the solve and proceed with the action list if it has\n// converged. This is a separate action because the user has the opportunity to\n// insert actions before the step completes, for example to do observations.\ntemplate <typename FieldsTag, typename OptionsGroup, typename Label,\n          typename ArraySectionIdTag>\nstruct CompleteStep {\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Debug)) {\n      Parallel::printf(\n          \"%s %s(%zu): Complete step\\n\", get_output(array_index),\n          pretty_type::name<OptionsGroup>(),\n          db::get<Convergence::Tags::IterationId<OptionsGroup>>(box));\n    }\n\n    // Repeat steps until the solve has converged\n    constexpr size_t prepare_step_index =\n        tmpl::index_of<ActionList, PrepareStep<FieldsTag, OptionsGroup, Label,\n                                               ArraySectionIdTag>>::value;\n    constexpr size_t this_action_index =\n        tmpl::index_of<ActionList, CompleteStep>::value;\n    return {Parallel::AlgorithmExecution::Continue,\n            get<Convergence::Tags::HasConverged<OptionsGroup>>(box)\n                ? (this_action_index + 1)\n                : prepare_step_index};\n  }\n};\n\n}  // namespace NonlinearSolver::newton_raphson::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/LineSearch.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/LineSearch.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace NonlinearSolver::newton_raphson {\n\ndouble next_step_length(const size_t globalization_iteration_id,\n                        const double step_length, const double prev_step_length,\n                        const double residual, const double residual_slope,\n                        const double next_residual,\n                        const double prev_residual) {\n  ASSERT(residual_slope < 0.,\n         \"The residual slope must be negative but is: \" << residual_slope);\n  ASSERT(step_length > 0.,\n         \"Step lengths must be positive, but current is \" << step_length);\n  if (globalization_iteration_id == 0) {\n    // First globalization step: minimum of quadratic interpolation\n    // Note that the expression 10.3c.1T.1 in DennisSchnabel assumes step_length\n    // is 1 for this initial iteration, so it omits the factors of step_length\n    return -0.5 * square(step_length) * residual_slope /\n           (next_residual - residual - step_length * residual_slope);\n  } else {\n    ASSERT(prev_step_length > 0.,\n           \"Step lengths must be positive, but prev is \" << prev_step_length);\n    // Subsequent globalization steps: minimum of cubic interpolation\n    const double f1 = next_residual - residual - step_length * residual_slope;\n    const double f2 =\n        prev_residual - residual - prev_step_length * residual_slope;\n    const double a =\n        (f1 / square(step_length) - f2 / square(prev_step_length)) /\n        (step_length - prev_step_length);\n    const double b = (-f1 * prev_step_length / square(step_length) +\n                      f2 * step_length / square(prev_step_length)) /\n                     (step_length - prev_step_length);\n    if (equal_within_roundoff(a, 0.)) {\n      return -0.5 * residual_slope / b;\n    } else {\n      return (sqrt(square(b) - 3. * a * residual_slope) - b) / (3. * a);\n    }\n  }\n}\n\n}  // namespace NonlinearSolver::newton_raphson\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/LineSearch.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\nnamespace NonlinearSolver::newton_raphson {\n/*!\n * \\brief Find the next step length for the line-search globalization\n *\n * The next step length is chosen such that it minimizes the quadratic (first\n * globalization step, i.e., when `globalization_iteration_id` is 0) or cubic\n * (subsequent globalization steps) polynomial interpolation. This function\n * implements Algorithm A6.3.1 in \\cite DennisSchnabel (p. 325). This is how\n * argument names map to symbols in that algorithm:\n *\n * - `step_length`: \\f$\\lambda\\f$\n * - `prev_step_length`: \\f$\\lambda_\\mathrm{prev}\\f$\n * - `residual`: \\f$f_c\\f$\n * - `residual_slope`: \\f$g^T p < 0\\f$\n * - `next_residual`: \\f$f_+\\f$\n * - `prev_residual`: \\f$f_{+,\\mathrm{prev}}\\f$\n *\n * Note that the argument `residual_slope` is the derivative of the residual\n * function \\f$f\\f$ w.r.t. the step length, i.e.\n * \\f$\\frac{\\mathrm{d}f}{\\mathrm{d}\\lambda}\\f$, which must be negative. For the\n * common scenario where \\f$f(x)=|\\boldsymbol{r}(x)|^2\\f$, i.e. the residual\n * function is the L2 norm of a residual vector \\f$\\boldsymbol{r}(x)\\f$, and\n * where that in turn is the residual of a nonlinear equation\n * \\f$\\boldsymbol{r}(x)=b-A_\\mathrm{nonlinear}(x)\\f$ in a Newton-Raphson step as\n * described in `NonlinearSolver::newton_raphson::NewtonRaphson`, then the\n * `residual_slope` reduces to\n *\n * \\f{equation}\n * \\frac{\\mathrm{d}f}{\\mathrm{d}\\lambda} =\n * \\frac{\\mathrm{d}f}{\\mathrm{d}x^i} \\frac{\\mathrm{d}x^i}{\\mathrm{d}\\lambda} =\n * 2 \\boldsymbol{r}(x) \\cdot \\frac{\\mathrm{d}\\boldsymbol{r}}{\\mathrm{d}x^i}\n * \\frac{\\mathrm{d}x^i}{\\mathrm{d}\\lambda} =\n * -2 |\\boldsymbol{r}(x)|^2 = -2 f(x) \\equiv -2 f_c\n * \\text{.}\n * \\f}\n *\n * Here we have used the relation\n *\n * \\f{equation}\n * \\frac{\\mathrm{d}\\boldsymbol{r}}{\\mathrm{d}x^i}\n * \\frac{\\mathrm{d}x^i}{\\mathrm{d}\\lambda} =\n * -\\frac{\\delta A_\\mathrm{nonlinear}}{\\delta x}\\cdot\\delta x = -r\n * \\f}\n *\n * of a Newton-Raphson step of full length \\f$\\delta x\\f$.\n */\ndouble next_step_length(size_t globalization_iteration_id, double step_length,\n                        double prev_step_length, double residual,\n                        double residual_slope, double next_residual,\n                        double prev_residual);\n}  // namespace NonlinearSolver::newton_raphson\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/NewtonRaphson.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/ElementActions.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/ResidualMonitor.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NonlinearSolver {\n/// Items related to the NewtonRaphson nonlinear solver\nnamespace newton_raphson {\n\n/*!\n * \\brief A Newton-Raphson correction scheme for nonlinear systems of equations\n * \\f$A_\\mathrm{nonlinear}(x)=b\\f$.\n *\n * We can use a correction scheme to solve a nonlinear problem\n * \\f$A_\\mathrm{nonlinear}(x)=b\\f$ by repeatedly solving a linearization of it.\n * A Newton-Raphson scheme iteratively refines an initial guess \\f$x_0\\f$ by\n * repeatedly solving the linearized problem\n *\n * \\f{equation}\n * \\frac{\\delta A_\\mathrm{nonlinear}}{\\delta x}(x_k) \\delta x_k =\n * b-A_\\mathrm{nonlinear}(x_k) \\equiv r_k\n * \\f}\n *\n * for the correction \\f$\\delta x_k\\f$ and then updating the solution as\n * \\f$x_{k+1}=x_k + \\delta x_k\\f$.\n *\n * The operations we need to supply to the algorithm are the nonlinear operator\n * \\f$A_\\mathrm{nonlinear}(x)\\f$ and a linear solver for the linearized problem\n * \\f$\\frac{\\delta A_\\mathrm{nonlinear}}{\\delta x}(x_k) \\delta x_k = r_k\\f$.\n * Each step of the algorithm expects that \\f$A_\\mathrm{nonlinear}(x)\\f$ is\n * computed and stored in the DataBox as\n * `db::add_tag_prefix<NonlinearSolver::Tags::OperatorAppliedTo, FieldsTag>`.\n * To perform a solve, add the `solve` action list to an array parallel\n * component. Pass the actions that compute \\f$A_\\mathrm{nonlinear}(x)\\f$ as\n * the first template parameter to `solve`. As the second template parameter,\n * pass the action list that performs a linear solve of the linearized operator\n * \\f$\\frac{\\delta A_\\mathrm{nonlinear}}{\\delta x}(x)\\f$ for the field\n * \\f$\\delta x\\f$ (the `linear_solver_fields_tag`) sourced by\n * \\f$r\\f$ (the `linear_solver_source_tag`). You will find suitable iterative\n * linear solvers in the `LinearSolver` namespace. The third template parameter\n * allows you to pass any further actions you wish to run in each step of the\n * algorithm (such as observations). If you add the `solve` action list multiple\n * times, use the fourth template parameter to label each solve with a different\n * type.\n *\n * \\par Globalization:\n * This nonlinear solver supports a line-search (or \"backtracking\")\n * globalization. If a step does not sufficiently decrease the residual (see\n * `NonlinearSolver::OptionTags::SufficientDecrease` for details on the\n * sufficient decrease condition), the step length is reduced until the residual\n * sufficiently decreases or the maximum number of globalization steps is\n * reached. The reduction in step length is determined by the minimum of a\n * quadratic (first globalization step) or cubic (subsequent globalization\n * steps) polynomial interpolation according to Algorithm A6.1.3 in\n * \\cite DennisSchnabel (p. 325) (see\n * `NonlinearSolver::newton_raphson::next_step_length`). Alternatives to a\n * line-search globalization, such as a trust-region globalization or more\n * sophisticated nonlinear preconditioning techniques (see e.g. \\cite Brune2015\n * for an overview), are not currently implemented.\n *\n * \\par Array sections\n * This nonlinear solver supports running over a subset of the elements in the\n * array parallel component (see `Parallel::Section`). Set the\n * `ArraySectionIdTag` template parameter to restrict the solver to elements in\n * that section. Only a single section must be associated with the\n * `ArraySectionIdTag`. The default is `void`, which means running over all\n * elements in the array. Note that the actions in the `SolveLinearization`\n * list passed to `solve` will _not_ be restricted to run only on section\n * elements, so all elements in the array may participate in preconditioning\n * (see LinearSolver::multigrid::Multigrid).\n */\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup,\n          typename SourceTag =\n              db::add_tag_prefix<::Tags::FixedSource, FieldsTag>,\n          typename ArraySectionIdTag = void>\nstruct NewtonRaphson {\n  using fields_tag = FieldsTag;\n  using options_group = OptionsGroup;\n  using source_tag = SourceTag;\n\n  using operand_tag = fields_tag;\n  using linear_solver_fields_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Correction, fields_tag>;\n  using linear_solver_source_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;\n\n  using component_list = tmpl::list<\n      detail::ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>;\n\n  using initialize_element =\n      detail::InitializeElement<FieldsTag, OptionsGroup, SourceTag>;\n\n  using register_element = tmpl::list<>;\n\n  using amr_projectors = initialize_element;\n\n  template <typename ApplyNonlinearOperator, typename SolveLinearization,\n            typename ObserveActions = tmpl::list<>,\n            typename Label = OptionsGroup>\n  using solve = tmpl::list<\n      detail::PrepareSolve<FieldsTag, OptionsGroup, Label, ArraySectionIdTag>,\n      ApplyNonlinearOperator, ObserveActions,\n      detail::SendInitialResidualMagnitude<FieldsTag, OptionsGroup, Label,\n                                           ArraySectionIdTag>,\n      detail::ReceiveInitialHasConverged<FieldsTag, OptionsGroup, Label,\n                                         ArraySectionIdTag>,\n      detail::PrepareStep<FieldsTag, OptionsGroup, Label, ArraySectionIdTag>,\n      SolveLinearization,\n      detail::PerformStep<FieldsTag, OptionsGroup, Label, ArraySectionIdTag>,\n      ApplyNonlinearOperator,\n      detail::ContributeToResidualMagnitudeReduction<FieldsTag, OptionsGroup,\n                                                     Label, ArraySectionIdTag>,\n      detail::Globalize<FieldsTag, OptionsGroup, Label, ArraySectionIdTag>,\n      ObserveActions,\n      detail::CompleteStep<FieldsTag, OptionsGroup, Label, ArraySectionIdTag>>;\n};\n\n}  // namespace newton_raphson\n}  // namespace NonlinearSolver\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/ResidualMonitor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/Tags.hpp\"\n\n/// \\cond\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\nnamespace NonlinearSolver::newton_raphson::detail {\ntemplate <typename Metavariables, typename FieldsTag>\nstruct InitializeResidualMonitor;\n}  // namespace NonlinearSolver::newton_raphson::detail\n/// \\endcond\n\nnamespace NonlinearSolver::newton_raphson::detail {\n\ntemplate <typename Metavariables, typename FieldsTag, typename OptionsGroup>\nstruct ResidualMonitor {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<OptionsGroup>,\n                 Convergence::Tags::Criteria<OptionsGroup>,\n                 NonlinearSolver::Tags::SufficientDecrease<OptionsGroup>,\n                 NonlinearSolver::Tags::MaxGlobalizationSteps<OptionsGroup>>;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<InitializeResidualMonitor<FieldsTag, OptionsGroup>>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void initialize(\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ResidualMonitor>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <typename FieldsTag, typename OptionsGroup>\nstruct InitializeResidualMonitor {\n private:\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;\n  using residual_magnitude_square_tag =\n      LinearSolver::Tags::MagnitudeSquare<residual_tag>;\n  using initial_residual_magnitude_tag =\n      ::Tags::Initial<LinearSolver::Tags::Magnitude<residual_tag>>;\n  using prev_residual_magnitude_square_tag =\n      NonlinearSolver::Tags::Globalization<residual_magnitude_square_tag>;\n\n public:\n  using simple_tags =\n      db::AddSimpleTags<residual_magnitude_square_tag,\n                        initial_residual_magnitude_tag,\n                        NonlinearSolver::Tags::StepLength<OptionsGroup>,\n                        prev_residual_magnitude_square_tag>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), std::numeric_limits<double>::signaling_NaN(),\n        std::numeric_limits<double>::signaling_NaN(),\n        std::numeric_limits<double>::signaling_NaN(),\n        std::numeric_limits<double>::signaling_NaN());\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n}  // namespace NonlinearSolver::newton_raphson::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/ResidualMonitorActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/LineSearch.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Tags/InboxTags.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/Observe.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\n/// \\cond\nnamespace Convergence::Tags {\ntemplate <typename OptionsGroup>\nstruct Criteria;\n}  // namespace Convergence::Tags\nnamespace LinearSolver::Tags {\ntemplate <typename Tag>\nstruct Magnitude;\ntemplate <typename Tag>\nstruct MagnitudeSquare;\n}  // namespace LinearSolver::Tags\nnamespace NonlinearSolver::Tags {\ntemplate <typename Tag>\nstruct Globalization;\ntemplate <typename OptionsGroup>\nstruct MaxGlobalizationSteps;\ntemplate <typename Tag>\nstruct Residual;\ntemplate <typename OptionsGroup>\nstruct StepLength;\ntemplate <typename OptionsGroup>\nstruct SufficientDecrease;\n}  // namespace NonlinearSolver::Tags\nnamespace logging::Tags {\ntemplate <typename OptionsGroup>\nstruct Verbosity;\n}  // namespace logging::Tags\nnamespace tuples {\ntemplate <typename...>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace NonlinearSolver::newton_raphson::detail {\n\ntemplate <typename FieldsTag, typename OptionsGroup, typename BroadcastTarget>\nstruct CheckResidualMagnitude {\n  using fields_tag = FieldsTag;\n  using residual_tag =\n      db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;\n  using residual_magnitude_square_tag =\n      LinearSolver::Tags::MagnitudeSquare<residual_tag>;\n  using initial_residual_magnitude_tag =\n      ::Tags::Initial<LinearSolver::Tags::Magnitude<residual_tag>>;\n  using prev_residual_magnitude_square_tag =\n      NonlinearSolver::Tags::Globalization<residual_magnitude_square_tag>;\n\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ArrayIndex, typename... Args>\n  static void apply(DataBox& box, Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const size_t iteration_id,\n                    const size_t globalization_iteration_id,\n                    const double next_residual_magnitude_square,\n                    const double step_length) {\n    const double residual_magnitude = sqrt(next_residual_magnitude_square);\n\n    NonlinearSolver::observe_detail::contribute_to_reduction_observer<\n        OptionsGroup, ParallelComponent>(\n        iteration_id, globalization_iteration_id, residual_magnitude,\n        step_length, cache);\n\n    if (UNLIKELY(iteration_id == 0)) {\n      db::mutate<initial_residual_magnitude_tag>(\n          [residual_magnitude](\n              const gsl::not_null<double*> initial_residual_magnitude) {\n            *initial_residual_magnitude = residual_magnitude;\n          },\n          make_not_null(&box));\n    } else {\n      // Make sure we are converging. Far away from the solution the correction\n      // determined by the linear solve might be bad, so we employ a\n      // globalization strategy to guide the solver towards the solution when\n      // the residual doesn't decrease sufficiently. See the `NewtonRaphson`\n      // class documentation for details.\n      const double sufficient_decrease =\n          get<NonlinearSolver::Tags::SufficientDecrease<OptionsGroup>>(box);\n      const double residual_magnitude_square =\n          get<residual_magnitude_square_tag>(box);\n      const double initial_residual_magnitude =\n          get<initial_residual_magnitude_tag>(box);\n      const double abs_tolerance =\n          get<Convergence::Tags::Criteria<OptionsGroup>>(box).absolute_residual;\n      const double rel_tolerance =\n          get<Convergence::Tags::Criteria<OptionsGroup>>(box).relative_residual;\n      // This is the directional derivative of the residual magnitude square\n      // f(x) = |r(x)|^2 in the descent direction\n      const double residual_magnitude_square_slope =\n          -2. * residual_magnitude_square;\n      // Check the sufficient decrease condition. Also make sure the residual\n      // didn't hit the tolerance.\n      if (residual_magnitude > abs_tolerance and\n          residual_magnitude / initial_residual_magnitude > rel_tolerance and\n          next_residual_magnitude_square >\n              residual_magnitude_square + sufficient_decrease * step_length *\n                                              residual_magnitude_square_slope) {\n        // The residual didn't sufficiently decrease. Perform a globalization\n        // step.\n        if (globalization_iteration_id <\n            get<NonlinearSolver::Tags::MaxGlobalizationSteps<OptionsGroup>>(\n                box)) {\n          const double next_step_length = std::clamp(\n              NonlinearSolver::newton_raphson::next_step_length(\n                  globalization_iteration_id, step_length,\n                  get<NonlinearSolver::Tags::StepLength<OptionsGroup>>(box),\n                  residual_magnitude_square, residual_magnitude_square_slope,\n                  next_residual_magnitude_square,\n                  get<prev_residual_magnitude_square_tag>(box)),\n              0.1 * step_length, 0.5 * step_length);\n          db::mutate<NonlinearSolver::Tags::StepLength<OptionsGroup>,\n                     prev_residual_magnitude_square_tag>(\n              [step_length, next_residual_magnitude_square](\n                  const gsl::not_null<double*> prev_step_length,\n                  const gsl::not_null<double*> prev_residual_magnitude_square) {\n                *prev_step_length = step_length;\n                *prev_residual_magnitude_square =\n                    next_residual_magnitude_square;\n              },\n              make_not_null(&box));\n          // Do some logging\n          if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                       ::Verbosity::Verbose)) {\n            Parallel::printf(\n                \"%s(%zu): Step with length %g didn't sufficiently decrease the \"\n                \"residual (possible overshoot). Residual: %e. Next step \"\n                \"length: %g.\\n\",\n                pretty_type::name<OptionsGroup>(), iteration_id, step_length,\n                residual_magnitude, next_step_length);\n            if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                         ::Verbosity::Debug)) {\n              Parallel::printf(\"Residual magnitude slope: %e\\n\",\n                               residual_magnitude_square_slope);\n            }\n          }\n          // Broadcast back to the elements signaling that they should perform a\n          // globalization step, then return early.\n          Parallel::receive_data<Tags::GlobalizationResult<OptionsGroup>>(\n              Parallel::get_parallel_component<BroadcastTarget>(cache),\n              iteration_id,\n              std::variant<double, Convergence::HasConverged>{\n                  next_step_length});\n          return;\n        } else {\n          // We have performed the maximum number of globalization steps without\n          // sufficiently decreasing the residual. The step length is so small\n          // now that the algorithm won't be converging anymore. Treat this as\n          // an error and stop the Newton-Raphson algorithm.\n          Convergence::HasConverged convergence_error{\n              Convergence::Reason::Error,\n              \"Failed to sufficiently decrease the residual in \" +\n                  std::to_string(globalization_iteration_id) +\n                  \" globalization steps. This is usually indicative of an \"\n                  \"ill-posed problem, for example when the linearization of \"\n                  \"the nonlinear operator is not computed correctly.\",\n              iteration_id};\n          if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                       ::Verbosity::Quiet)) {\n            Parallel::printf(\"%s(%zu): WARNING: %s\\n\",\n                             pretty_type::name<OptionsGroup>(), iteration_id,\n                             convergence_error.error_message());\n          }\n          Parallel::receive_data<Tags::GlobalizationResult<OptionsGroup>>(\n              Parallel::get_parallel_component<BroadcastTarget>(cache),\n              iteration_id,\n              std::variant<double, Convergence::HasConverged>{\n                  std::move(convergence_error)});\n          return;\n        }  // min_step_length\n      }    // sufficient decrease condition\n    }      // initial iteration\n\n    db::mutate<residual_magnitude_square_tag>(\n        [next_residual_magnitude_square](\n            const gsl::not_null<double*> local_residual_magnitude_square) {\n          *local_residual_magnitude_square = next_residual_magnitude_square;\n        },\n        make_not_null(&box));\n\n    // At this point, the iteration is complete. We proceed with logging and\n    // checking convergence before broadcasting back to the elements.\n\n    // Determine whether the nonlinear solver has converged\n    Convergence::HasConverged has_converged{\n        get<Convergence::Tags::Criteria<OptionsGroup>>(box), iteration_id,\n        residual_magnitude, get<initial_residual_magnitude_tag>(box)};\n\n    // Do some logging\n    if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=\n                 ::Verbosity::Quiet)) {\n      if (UNLIKELY(iteration_id == 0)) {\n        Parallel::printf(\"%s initialized with residual: %e\\n\",\n                         pretty_type::name<OptionsGroup>(), residual_magnitude);\n      } else {\n        Parallel::printf(\n            \"%s(%zu) iteration complete (%zu globalization steps, step length \"\n            \"%g). Remaining residual: %e\\n\",\n            pretty_type::name<OptionsGroup>(), iteration_id,\n            globalization_iteration_id, step_length, residual_magnitude);\n      }\n    }\n    if (UNLIKELY(has_converged and get<logging::Tags::Verbosity<OptionsGroup>>(\n                                       box) >= ::Verbosity::Quiet)) {\n      if (UNLIKELY(iteration_id == 0)) {\n        Parallel::printf(\"%s has converged without any iterations: %s\\n\",\n                         pretty_type::name<OptionsGroup>(), has_converged);\n      } else {\n        Parallel::printf(\"%s has converged in %zu iterations: %s\\n\",\n                         pretty_type::name<OptionsGroup>(), iteration_id,\n                         has_converged);\n      }\n    }\n\n    Parallel::receive_data<Tags::GlobalizationResult<OptionsGroup>>(\n        Parallel::get_parallel_component<BroadcastTarget>(cache), iteration_id,\n        std::variant<double, Convergence::HasConverged>(\n            // NOLINTNEXTLINE(performance-move-const-arg)\n            std::move(has_converged)));\n  }\n};\n\n}  // namespace NonlinearSolver::newton_raphson::detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InboxTags.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Tags/InboxTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <variant>\n\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NonlinearSolver::newton_raphson::detail::Tags {\n\ntemplate <typename OptionsGroup>\nstruct GlobalizationResult\n    : Parallel::InboxInserters::Value<GlobalizationResult<OptionsGroup>> {\n  using temporal_id = size_t;\n  using type =\n      std::map<temporal_id, std::variant<double, Convergence::HasConverged>>;\n};\n\n}  // namespace NonlinearSolver::newton_raphson::detail::Tags\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/Observe.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace NonlinearSolver::observe_detail {\n\n/*!\n * \\brief Contributes data from the residual monitor to the reduction observer\n */\ntemplate <typename OptionsGroup, typename ParallelComponent,\n          typename Metavariables>\nvoid contribute_to_reduction_observer(\n    const size_t iteration_id, const size_t globalization_iteration_id,\n    const double residual_magnitude, const double step_length,\n    Parallel::GlobalCache<Metavariables>& cache) {\n  auto& reduction_writer = Parallel::get_parallel_component<\n      observers::ObserverWriter<Metavariables>>(cache);\n  Parallel::threaded_action<observers::ThreadedActions::WriteReductionDataRow>(\n      // Node 0 is always the writer, so directly call the component on that\n      // node\n      reduction_writer[0],\n      std::string{\"/\" + pretty_type::name<OptionsGroup>() + \"Residuals\"},\n      std::vector<std::string>{\"Iteration\", \"Walltime\", \"GlobalizationStep\",\n                               \"Residual\", \"StepLength\"},\n      std::make_tuple(iteration_id, sys::wall_time(),\n                      globalization_iteration_id, residual_magnitude,\n                      step_length));\n}\n\n}  // namespace NonlinearSolver::observe_detail\n"
  },
  {
    "path": "src/ParallelAlgorithms/NonlinearSolver/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines DataBox tags for the linear solver\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n\n/// Functionality for solving nonlinear systems of equations\nnamespace NonlinearSolver {\n\n/// Options related to nonlinear solvers\nnamespace OptionTags {\n\n/*!\n * \\brief Sufficient decrease parameter of the line search globalization\n *\n * The sufficient decrease parameter is the acceptable decrease of the residual\n * magnitude in each step of the nonlinear solver. It is measured as a fraction\n * of the predicted decrease in residual magnitude if the problem was linear.\n * For example, a sufficient decrease parameter of 1 means that a nonlinear\n * solver step is expected to decrease the residual exactly as expected for a\n * linear problem, i.e. immediately to zero. A sufficient decrease parameter of\n * 0.5 means that decreasing the residual by half of that amount in each\n * nonlinear solver step is acceptable.\n *\n * Nonlinear solver steps that fail the sufficient decrease condition (also\n * known as _Armijo condition_) undergo a globalization procedure such as a line\n * search.\n *\n * A typical value for the sufficient decrease parameter is \\f$10^{-4}\\f$. Set\n * to values closer to unity when the nonlinear solver overshoots, e.g. when the\n * initial guess is particularly bad. Larger values mean the nonlinear solver is\n * stricter with accepting steps, preferring to apply the globalization\n * strategy.\n */\ntemplate <typename OptionsGroup>\nstruct SufficientDecrease {\n  using type = double;\n  static constexpr Options::String help = {\n      \"Fraction of decrease predicted by linearization\"};\n  static type lower_bound() { return 0.; }\n  static type upper_bound() { return 1.; }\n  static type suggested_value() { return 1.e-4; }\n  using group = OptionsGroup;\n};\n\n/*!\n * \\brief Nonlinear solver steps are damped by this factor\n *\n * Instead of attempting to take full-length steps when correcting the solution\n * in each nonlinear solver step (see `NonlinearSolver::Tags::Correction`),\n * reduce the step length by this factor. This damping occurs before any\n * globalization steps that may further reduce the step length.\n */\ntemplate <typename OptionsGroup>\nstruct DampingFactor {\n  using type = double;\n  static constexpr Options::String help = {\n      \"Multiply corrections by this factor\"};\n  static type lower_bound() { return 0.; }\n  static type upper_bound() { return 1.; }\n  static type suggested_value() { return 1.; }\n  using group = OptionsGroup;\n};\n\n/*!\n * \\brief The maximum number of allowed globalization steps\n *\n * Nonlinear solves of well-posed problems should never hit this limit because\n * the step size shrinks and eventually triggers the sufficient-decrease\n * condition (see `NonlinearSolver::OptionTags::SufficientDecrease`). So the\n * suggested value just provides a safety-net to prevent the globalization\n * from running forever when the problem is ill-posed.\n */\ntemplate <typename OptionsGroup>\nstruct MaxGlobalizationSteps {\n  using type = size_t;\n  static constexpr Options::String help = {\n      \"Maximum number of globalization steps\"};\n  static type suggested_value() { return 40; }\n  using group = OptionsGroup;\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n\n/*!\n * \\brief The correction \\f$\\delta x\\f$ to improve a solution \\f$x_0\\f$\n *\n * A linear problem \\f$Ax=b\\f$ can be equivalently formulated as the problem\n * \\f$A\\delta x=b-A x_0\\f$ for the correction \\f$\\delta x\\f$ to an initial guess\n * \\f$x_0\\f$. More importantly, we can use a correction scheme to solve a\n * nonlinear problem \\f$A_\\mathrm{nonlinear}(x)=b\\f$ by repeatedly solving a\n * linearization of it. For instance, a Newton-Raphson scheme iteratively\n * refines an initial guess \\f$x_0\\f$ by repeatedly solving the linearized\n * problem\n *\n * \\f{equation}\n * \\frac{\\delta A_\\mathrm{nonlinear}}{\\delta x}(x_k)\\delta x_k =\n * b-A_\\mathrm{nonlinear}(x_k)\n * \\f}\n *\n * for the correction \\f$\\delta x_k\\f$ and then updating the solution as\n * \\f$x_{k+1}=x_k + \\delta x_k\\f$.\n */\ntemplate <typename Tag>\nstruct Correction : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/*!\n * \\brief The nonlinear operator \\f$A_\\mathrm{nonlinear}\\f$ applied to the data\n * in `Tag`\n */\ntemplate <typename Tag>\nstruct OperatorAppliedTo : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    // Add \"Nonlinear\" prefix to abbreviate the namespace for uniqueness\n    return \"NonlinearOperatorAppliedTo(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/*!\n * \\brief The nonlinear residual\n * \\f$r_\\mathrm{nonlinear} = b - A_\\mathrm{nonlinear}(\\delta x)\\f$\n */\ntemplate <typename Tag>\nstruct Residual : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    // Add \"Nonlinear\" prefix to abbreviate the namespace for uniqueness\n    return \"NonlinearResidual(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n/// Compute the residual \\f$r=b - Ax\\f$ from the `SourceTag` \\f$b\\f$ and the\n/// `db::add_tag_prefix<NonlinearSolver::Tags::OperatorAppliedTo, FieldsTag>`\n/// \\f$Ax\\f$.\ntemplate <typename FieldsTag, typename SourceTag>\nstruct ResidualCompute : db::add_tag_prefix<Residual, FieldsTag>,\n                         db::ComputeTag {\n  using base = db::add_tag_prefix<Residual, FieldsTag>;\n  using argument_tags =\n      tmpl::list<SourceTag, db::add_tag_prefix<OperatorAppliedTo, FieldsTag>>;\n  using return_type = typename base::type;\n  static void function(\n      const gsl::not_null<return_type*> residual,\n      const typename SourceTag::type& source,\n      const typename db::add_tag_prefix<OperatorAppliedTo, FieldsTag>::type&\n          operator_applied_to_fields) {\n    *residual = source - operator_applied_to_fields;\n  }\n};\n\n/*!\n * \\brief The length of nonlinear solver steps\n *\n * Instead of taking full-length nonlinear solver steps when correcting the\n * solution as detailed in `NonlinearSolver::Tags::Correction`, the correction\n * is multiplied by this step length.\n *\n * The `NonlinearSolver::Tags::DampingFactor` multiplies the initial length of\n * each step such that the nonlinear solver never takes full-length steps if the\n * damping factor is below one. The step length can be further reduced by the\n * globalization procedure. See `NonlinearSolver::NewtonRaphson` for details.\n */\ntemplate <typename OptionsGroup>\nstruct StepLength : db::SimpleTag {\n  static std::string name() {\n    return \"StepLength(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = double;\n};\n\n/*!\n * \\brief Sufficient decrease parameter of the line search globalization\n *\n * \\see `NonlinearSolver::OptionTags::SufficientDecrease`\n */\ntemplate <typename OptionsGroup>\nstruct SufficientDecrease : db::SimpleTag {\n  static std::string name() {\n    return \"SufficientDecrease(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = double;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::SufficientDecrease<OptionsGroup>>;\n  static type create_from_options(const type& option) { return option; }\n};\n\n/*!\n * \\brief Nonlinear solver steps are damped by this factor\n *\n * \\see `NonlinearSolver::OptionTags::DampingFactor`\n */\ntemplate <typename OptionsGroup>\nstruct DampingFactor : db::SimpleTag {\n  static std::string name() {\n    return \"DampingFactor(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = double;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<OptionTags::DampingFactor<OptionsGroup>>;\n  static type create_from_options(const type& option) { return option; }\n};\n\n/*!\n * \\brief The maximum number of allowed globalization steps\n *\n * \\see `NonlinearSolver::OptionTags::MinStepLength`\n */\ntemplate <typename OptionsGroup>\nstruct MaxGlobalizationSteps : db::SimpleTag {\n  static std::string name() {\n    return \"MaxGlobalizationSteps(\" + pretty_type::name<OptionsGroup>() + \")\";\n  }\n  using type = size_t;\n  static constexpr bool pass_metavariables = false;\n  using option_tags =\n      tmpl::list<OptionTags::MaxGlobalizationSteps<OptionsGroup>>;\n  static type create_from_options(const type& option) { return option; }\n};\n\n/// Prefix indicating the `Tag` is related to the globalization procedure\ntemplate <typename Tag>\nstruct Globalization : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n}  // namespace Tags\n}  // namespace NonlinearSolver\n"
  },
  {
    "path": "src/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/BackgroundSpacetime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ray_tracing {\n\n/*!\n * \\brief Abstract base class for background spacetimes in the ray tracer.\n *\n * Derived classes have to provide spacetime quantities at a given point on\n * request, e.g. by evaluating an analytic spacetime or by interpolating numeric\n * data from a file. The `initialize` function can be used to set up the\n * background spacetime, e.g. by reading data from a file. Then, the background\n * spacetime should be valid within the bounds returned by the `time_bounds`\n * function.\n */\nclass BackgroundSpacetime : public PUP::able {\n protected:\n  static constexpr size_t Dim = 3;\n  using DataType = double;\n  using Frame = ::Frame::Inertial;\n  using DerivLapse =\n      ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>, Frame>;\n  using DerivShift =\n      ::Tags::deriv<gr::Tags::Shift<DataType, Dim>, tmpl::size_t<Dim>, Frame>;\n  using DerivInvSpatialMetric =\n      ::Tags::deriv<gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>,\n                    tmpl::size_t<Dim>, Frame>;\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim, Frame>,\n                    tmpl::size_t<Dim>, Frame>;\n\n  BackgroundSpacetime() = default;\n\n public:\n  ~BackgroundSpacetime() override = default;\n\n  /// \\cond\n  explicit BackgroundSpacetime(CkMigrateMessage* msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(BackgroundSpacetime);\n  /// \\endcond\n\n  /// Copies the background spacetime. Must call `initialize` on the clone\n  /// before using it.\n  virtual auto get_clone() const -> std::unique_ptr<BackgroundSpacetime> = 0;\n\n  /*!\n   * \\brief Initialize the background spacetime, e.g. by reading data from a\n   * file.\n   *\n   * This function is called before the first call to `variables()`. It is\n   * valid to call `initialize` again with new time bounds. Derived classes\n   * must guarantee that the `variables` function can be called from other\n   * threads while `initialize` is running (e.g. loading new data from files),\n   * but only with times within the overlap of the previous and the new time\n   * bounds.\n   *\n   * \\param time_bounds The time bounds for which to initialize the\n   * background spacetime. The spacetime should be valid for all times in this\n   * range.\n   */\n  virtual void initialize(\n      [[maybe_unused]] const std::array<double, 2> time_bounds) {}\n\n  /// Time bounds for which the background spacetime is valid. The `variables`\n  /// function can be called for any time in this range (inclusive).\n  virtual std::array<double, 2> time_bounds() const {\n    return {-std::numeric_limits<double>::infinity(),\n            std::numeric_limits<double>::infinity()};\n  }\n\n  /// These tags can be retrieved from the background spacetime. They are\n  /// required to evaluate the `gr::geodesic_equation`.\n  using tags = tmpl::list<gr::Tags::Lapse<DataType>, DerivLapse,\n                          gr::Tags::Shift<DataType, Dim, Frame>, DerivShift,\n                          gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>,\n                          DerivInvSpatialMetric,\n                          gr::Tags::ExtrinsicCurvature<DataType, Dim, Frame>>;\n\n  /*!\n   * \\brief Returns all spacetime variables at a given point in space and time.\n   *\n   * This function must be thread-safe.\n   *\n   * \\param x Spatial coordinates\n   * \\param t Time\n   * \\param block_order Optional priority order for processing blocks during\n   * interpolation. If specified, it will be updated to push the block in which\n   * the point was found to the front. Can be empty, in which case it will be\n   * initially set to the default order. See `block_logical_coordinates` for\n   * more details.\n   */\n  virtual tuples::tagged_tuple_from_typelist<tags> variables(\n      const tnsr::I<DataType, Dim, Frame>& x, double t,\n      std::optional<gsl::not_null<std::vector<size_t>*>> block_order =\n          std::nullopt) const = 0;\n};\n\n}  // namespace ray_tracing\n"
  },
  {
    "path": "src/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  NumericData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BackgroundSpacetime.hpp\n  Factory.hpp\n  NumericData.hpp\n  WrappedGr.hpp\n  )\n"
  },
  {
    "path": "src/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"ParallelAlgorithms/RayTracer/BackgroundSpacetimes/NumericData.hpp\"\n#include \"ParallelAlgorithms/RayTracer/BackgroundSpacetimes/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ray_tracing {\n\nusing all_background_spacetimes =\n    tmpl::list<WrappedGr<gr::Solutions::KerrSchild>,\n               WrappedGr<gr::Solutions::Minkowski<3>>, NumericData>;\n\n}  // namespace ray_tracing\n"
  },
  {
    "path": "src/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/NumericData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/RayTracer/BackgroundSpacetimes/NumericData.hpp\"\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n#include \"IO/Exporter/SpacetimeInterpolator.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n\nnamespace ray_tracing {\n\nNumericData::NumericData(std::string file_glob, std::string subfile_name,\n                         std::optional<int> observation_step,\n                         ::Verbosity verbosity)\n    : file_glob_(std::move(file_glob)),\n      subfile_name_(std::move(subfile_name)),\n      observation_step_(observation_step),\n      verbosity_(verbosity) {\n  if (not observation_step_.has_value()) {\n    // Construct the spacetime interpolator but don't load any data yet\n    interpolator_ = spectre::Exporter::SpacetimeInterpolator<Dim, Frame>{\n        file_glob_, subfile_name_,\n        spectre::Exporter::get_tensor_components<tags>()};\n  }\n}\n\nNumericData::NumericData(const NumericData& rhs)\n    : NumericData(rhs.file_glob_, rhs.subfile_name_, rhs.observation_step_,\n                  rhs.verbosity_) {}\n\nNumericData& NumericData::operator=(const NumericData& rhs) {\n  if (this != &rhs) {\n    *this = NumericData(rhs);\n  }\n  return *this;\n}\n\nvoid NumericData::initialize(std::array<double, 2> new_time_bounds) {\n  if (verbosity_ >= ::Verbosity::Verbose) {\n    Parallel::printf(\"Loading numeric data...\\n\");\n  }\n  if (observation_step_.has_value()) {\n    interpolator_ = PointwiseInterpolator{\n        file_glob_, subfile_name_,\n        spectre::Exporter::ObservationStep{observation_step_.value()},\n        spectre::Exporter::get_tensor_components<tags>()};\n  } else {\n    std::get<SpacetimeInterpolator>(interpolator_)\n        .load_time_bounds(new_time_bounds);\n  }\n  if (verbosity_ >= ::Verbosity::Verbose) {\n    Parallel::printf(\"Numeric data loaded.\\n\");\n  }\n}\n\nstd::array<double, 2> NumericData::time_bounds() const {\n  if (std::holds_alternative<SpacetimeInterpolator>(interpolator_)) {\n    return std::get<SpacetimeInterpolator>(interpolator_).time_bounds();\n  } else {\n    return {{-std::numeric_limits<double>::infinity(),\n             std::numeric_limits<double>::infinity()}};\n  }\n}\n\ntuples::tagged_tuple_from_typelist<typename NumericData::tags>\nNumericData::variables(const tnsr::I<DataType, Dim, Frame>& x, const double t,\n                       const std::optional<gsl::not_null<std::vector<size_t>*>>\n                           block_order) const {\n  std::vector<double> result{};\n  if (std::holds_alternative<PointwiseInterpolator>(interpolator_)) {\n    std::get<PointwiseInterpolator>(interpolator_)\n        .interpolate_to_point(make_not_null(&result), x, block_order);\n  } else {\n    std::get<SpacetimeInterpolator>(interpolator_)\n        .interpolate_to_point(make_not_null(&result), x, t, block_order);\n  }\n  return spectre::Exporter::make_tagged_tuple<tags>(std::move(result));\n}\n\nvoid NumericData::pup(PUP::er& p) {\n  BackgroundSpacetime::pup(p);\n  p | file_glob_;\n  p | subfile_name_;\n  p | observation_step_;\n  p | verbosity_;\n  // Don't copy interpolator, it must be reinitialized\n}\n\nbool operator==(const NumericData& lhs, const NumericData& rhs) {\n  return lhs.file_glob_ == rhs.file_glob_ and\n         lhs.subfile_name_ == rhs.subfile_name_ and\n         lhs.observation_step_ == rhs.observation_step_ and\n         lhs.verbosity_ == rhs.verbosity_;\n}\n\nbool operator!=(const NumericData& lhs, const NumericData& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID NumericData::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace ray_tracing\n"
  },
  {
    "path": "src/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/NumericData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n#include \"IO/Exporter/SpacetimeInterpolator.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/RayTracer/BackgroundSpacetimes/BackgroundSpacetime.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ray_tracing {\n\n/// Numeric data from volume data files.\nclass NumericData : public BackgroundSpacetime {\n public:\n  NumericData() = default;\n  NumericData(const NumericData& /*rhs*/);\n  NumericData& operator=(const NumericData& /*rhs*/);\n  NumericData(NumericData&& /*rhs*/) = default;\n  NumericData& operator=(NumericData&& /*rhs*/) = default;\n  ~NumericData() override = default;\n\n  static constexpr Options::String help = \"Numeric data from volume data files\";\n\n  struct FileGlob {\n    using type = std::string;\n    static constexpr Options::String help =\n        \"Volume data files. Can be a glob pattern.\";\n  };\n\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = \"Subfile name in the volume files\";\n  };\n\n  struct ObservationStep {\n    using type = Options::Auto<int>;\n    static constexpr Options::String help =\n        \"Either a single observation step to load, or 'Auto' to load the full \"\n        \"spacetime. \"\n        \"When a single observation is loaded, then rays are traced \"\n        \"only through that single time slice (fast light approximation). \"\n        \"When 'Auto' is selected, then all time slices are loaded that cover \"\n        \"the required time range and the data is interpolated in both space \"\n        \"and time (slow light).\";\n  };\n\n  struct Verbosity {\n    using type = ::Verbosity;\n    static constexpr Options::String help = \"Verbosity of output.\";\n  };\n\n  using options = tmpl::list<FileGlob, SubfileName, ObservationStep, Verbosity>;\n\n  NumericData(std::string file_glob, std::string subfile_name,\n              std::optional<int> observation_step,\n              ::Verbosity verbosity = ::Verbosity::Silent);\n\n  auto get_clone() const -> std::unique_ptr<BackgroundSpacetime> override {\n    return std::make_unique<NumericData>(*this);\n  }\n\n  /// \\cond\n  explicit NumericData(CkMigrateMessage* msg) : BackgroundSpacetime(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NumericData);\n  /// \\endcond\n\n  void initialize(std::array<double, 2> new_time_bounds) override;\n\n  std::array<double, 2> time_bounds() const override;\n\n  tuples::tagged_tuple_from_typelist<tags> variables(\n      const tnsr::I<DataType, Dim, Frame>& x, double t,\n      std::optional<gsl::not_null<std::vector<size_t>*>> block_order =\n          std::nullopt) const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  friend bool operator==(const NumericData& lhs, const NumericData& rhs);\n\n private:\n  using PointwiseInterpolator =\n      spectre::Exporter::PointwiseInterpolator<Dim, Frame>;\n  using SpacetimeInterpolator =\n      spectre::Exporter::SpacetimeInterpolator<Dim, Frame>;\n\n  std::string file_glob_;\n  std::string subfile_name_;\n  std::optional<int> observation_step_;\n  ::Verbosity verbosity_ = ::Verbosity::Silent;\n  // Cache that holds tensor data in memory. This isn't copied and must be\n  // reinitialized after a copy.\n  std::variant<PointwiseInterpolator, SpacetimeInterpolator> interpolator_;\n};\n\nbool operator!=(const NumericData& lhs, const NumericData& rhs);\n\n}  // namespace ray_tracing\n"
  },
  {
    "path": "src/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/WrappedGr.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/RayTracer/BackgroundSpacetimes/BackgroundSpacetime.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TupleSlice.hpp\"\n\nnamespace ray_tracing {\n\n/// Analytic background spacetime from a GR or GRMHD solution.\ntemplate <typename SolutionType>\nclass WrappedGr : public BackgroundSpacetime {\n public:\n  using options = typename SolutionType::options;\n  static constexpr Options::String help = SolutionType::help;\n  static std::string name() { return pretty_type::name<SolutionType>(); }\n\n  WrappedGr() = default;\n  WrappedGr(const WrappedGr& /*rhs*/) = default;\n  WrappedGr& operator=(const WrappedGr& /*rhs*/) = default;\n  WrappedGr(WrappedGr&& /*rhs*/) = default;\n  WrappedGr& operator=(WrappedGr&& /*rhs*/) = default;\n  ~WrappedGr() override = default;\n\n  template <typename Arg1, typename Arg2, typename... Args>\n    requires(tmpl::size<options>::value > 0)\n  WrappedGr(Arg1&& /*arg1*/, Arg2&& arg2, Args&&... args)\n      : wrapped_solution_(\n            // Some gymnastics so this works with option parsing: skip the first\n            // argument (ParseOptions) and last two arguments (context,\n            // Metavars), then forward the rest to construct the SolutionType.\n            std::apply(\n                [](auto&&... forwarded_args) {\n                  return SolutionType(std::forward<decltype(forwarded_args)>(\n                      forwarded_args)...);\n                },\n                tuple_slice<0, sizeof...(Args) - 1>(std::forward_as_tuple(\n                    std::forward<Arg2>(arg2), std::forward<Args>(args)...)))) {}\n\n  explicit WrappedGr(SolutionType wrapped_solution)\n      : wrapped_solution_(std::move(wrapped_solution)) {}\n\n  const auto& wrapped_solution() const { return wrapped_solution_; }\n\n  auto get_clone() const -> std::unique_ptr<BackgroundSpacetime> override {\n    return std::make_unique<WrappedGr<SolutionType>>(*this);\n  }\n\n  /// \\cond\n  explicit WrappedGr(CkMigrateMessage* msg) : BackgroundSpacetime(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(WrappedGr);\n  /// \\endcond\n\n  tuples::tagged_tuple_from_typelist<tags> variables(\n      const tnsr::I<DataType, Dim, Frame>& x, double t,\n      const std::optional<gsl::not_null<std::vector<size_t>*>> /*block_order*/ =\n          std::nullopt) const override {\n    // Tags that we retrieve from the solution. Gets the deriv(SpatialMetric)\n    // instead of deriv(InvSpatialMetric) because the latter isn't available.\n    using retrieve_tags =\n        tmpl::replace<tags, DerivInvSpatialMetric, DerivSpatialMetric>;\n    auto intermediate_vars = wrapped_solution_.variables(x, t, retrieve_tags{});\n    tuples::tagged_tuple_from_typelist<tags> result{};\n    // Compute the deriv(InvSpatialMetric) from the deriv(SpatialMetric)\n    gr::deriv_inverse_spatial_metric(\n        make_not_null(&get<DerivInvSpatialMetric>(result)),\n        get<gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>>(\n            intermediate_vars),\n        get<DerivSpatialMetric>(intermediate_vars));\n    tmpl::for_each<tmpl::remove<retrieve_tags, DerivSpatialMetric>>(\n        [&result, &intermediate_vars](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          get<tag>(result) = std::move(get<tag>(intermediate_vars));\n        });\n    return result;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    BackgroundSpacetime::pup(p);\n    p | wrapped_solution_;\n  }\n\n  friend bool operator==(const WrappedGr& lhs, const WrappedGr& rhs) {\n    return lhs.wrapped_solution_ == rhs.wrapped_solution_;\n  }\n\n private:\n  SolutionType wrapped_solution_;\n};\n\ntemplate <typename SolutionType>\nbool operator!=(const WrappedGr<SolutionType>& lhs,\n                const WrappedGr<SolutionType>& rhs) {\n  return not(lhs == rhs);\n}\n\n/// \\cond\ntemplate <typename SolutionType>\nPUP::able::PUP_ID WrappedGr<SolutionType>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\n}  // namespace ray_tracing\n"
  },
  {
    "path": "src/ParallelAlgorithms/RayTracer/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY RayTracer)\n\nadd_spectre_library(${LIBRARY})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Exporter\n  GeneralRelativity\n  GeneralRelativitySolutions\n  IO\n  Logging\n  Options\n  Parallel\n  Utilities\n  )\n\nadd_subdirectory(BackgroundSpacetimes)\n"
  },
  {
    "path": "src/ParallelAlgorithms/SurfaceFinder/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SurfaceFinder)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  SurfaceFinder.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  SurfaceFinder.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Interpolation\n  RootFinding\n  Spectral\n  Utilities\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/ParallelAlgorithms/SurfaceFinder/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"ParallelAlgorithms/SurfaceFinder/SurfaceFinder.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace SurfaceFinder {\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.SphericalHarmonics\");\n  m.def(\"find_radial_surface\", &find_radial_surface, py::arg(\"data\"),\n        py::arg(\"target\"), py::arg(\"mesh\"), py::arg(\"angular_coords\"),\n        py::arg(\"relative_tolerance\") = 1e-10,\n        py::arg(\"absolute_tolerance\") = 1e-10);\n}\n\n}  // namespace SurfaceFinder\n"
  },
  {
    "path": "src/ParallelAlgorithms/SurfaceFinder/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PySurfaceFinder\")\n\nspectre_python_add_module(\n  SurfaceFinder\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n  FindRadialSurface.py\n)\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  SphericalHarmonics\n  SurfaceFinder\n  pybind11::module\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PySphericalHarmonics\n)\n"
  },
  {
    "path": "src/ParallelAlgorithms/SurfaceFinder/Python/FindRadialSurface.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\nfrom typing import Optional, Sequence, Union\n\nimport click\nimport numpy as np\n\nimport spectre.IO.H5 as spectre_h5\nimport spectre.SurfaceFinder\nfrom spectre.Domain import (\n    ElementId,\n    block_logical_coordinates,\n    deserialize_domain,\n    deserialize_functions_of_time,\n    element_logical_coordinates,\n)\nfrom spectre.IO.H5 import open_volfiles, open_volfiles_command\nfrom spectre.IO.H5.IterElements import iter_elements\nfrom spectre.Pipelines.Bbh.FindHorizon import _strahlkorper_vol_data\nfrom spectre.SphericalHarmonics import (\n    Frame,\n    Strahlkorper,\n    cartesian_coords,\n    ylm_legend_and_data,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef find_radial_surface(\n    h5_files: Sequence[str],\n    subfile_name: str,\n    obs_id: int,\n    obs_time: float,\n    var_name: str,\n    target: float,\n    initial_guess: Strahlkorper[Frame.Inertial],\n    output_surfaces_file: Optional[Union[str, Path]] = None,\n    output_coeffs_subfile: Optional[str] = None,\n    output_coords_subfile: Optional[str] = None,\n):\n    \"\"\"Find a radial surface where a variable equals a target value.\n\n    The surface is found by searching along radial rays that are defined by the\n    angular collocation points of the 'initial_guess' Strahlkorper. Only blocks\n    that intersect the initial guess and their radial neighbors are considered,\n    and only those neighbors that have a distorted frame. These blocks are\n    assumed to be wedges, so their third logical coordinate is radial.\n\n    This function is useful, for example, for finding the surface of a star\n    and then deforming the domain to match the surface.\n\n    \\f\n    Arguments:\n      h5_files: The H5 files containing the volume data.\n      subfile_name: Name of the volume data subfile in the 'h5_files'.\n      obs_id: Observation ID in the volume data.\n      obs_time: Time of the observation.\n      var_name: Name of the variable in the volume data to search for the\n        surface.\n      target: Value that the variable takes that defines the surface.\n      initial_guess: Initial guess for the surface. Only blocks that intersect\n        this Strahlkorper and their radial neighbors are considered. These\n        blocks must be wedges.\n      output_surfaces_file: Optional. H5 output file where the surface Ylm\n        coefficients will be written. Can be a new or existing file. Requires\n        either 'output_coeffs_subfile' or 'output_coords_subfile' is also\n        specified, or both.\n      output_coeffs_subfile: Optional. Name of the subfile in the\n        'output_surfaces_file' where the surface Ylm coefficients will be\n        written. These can be used to reconstruct the surface, e.g. to\n        deform the domain.\n      output_coords_subfile: Optional. Name of the subfile in the\n        'output_surfaces_file' where the surface coordinates will be written.\n        These can be used for visualization.\n    \"\"\"\n    # Validate input arguments\n    if output_surfaces_file:\n        assert output_coeffs_subfile or output_coords_subfile, (\n            \"Specify either 'output_coeffs_subfile' or 'output_coords_subfile'\"\n            \" or both.\"\n        )\n    # Deserialize domain and functions of time\n    for volfile in open_volfiles(h5_files, subfile_name, obs_id):\n        dim = volfile.get_dimension()\n        domain = deserialize_domain[dim](volfile.get_domain())\n        time = volfile.get_observation_value(obs_id)\n        functions_of_time = (\n            deserialize_functions_of_time(volfile.get_functions_of_time(obs_id))\n            if domain.is_time_dependent()\n            else None\n        )\n        break\n    # Map the initial guess through the domain. We need the angular logical\n    # coordinates to define the rays along which we search for the surface.\n    initial_guess_coords = cartesian_coords(initial_guess)\n    block_logical_coords = block_logical_coordinates(\n        domain, initial_guess_coords, time, functions_of_time\n    )\n    num_rays = len(block_logical_coords)\n    # Find the surface\n    surface_radii = np.empty(num_rays)\n    surface_radii.fill(np.nan)\n    filled = np.zeros(num_rays, dtype=bool)\n    for element, data in iter_elements(\n        open_volfiles(h5_files, subfile_name, obs_id),\n        obs_ids=obs_id,\n        tensor_components=[var_name],\n    ):\n        # Find the radial rays that go through this element\n        angular_coords = []\n        offsets = []\n        for i, block_logical_coord in enumerate(block_logical_coords):\n            if filled[i]:\n                continue\n            block_id = block_logical_coord.id.get_index()\n            # Radial neighbors that are wedges are also valid because they keep\n            # the angular logical coordinates the same\n            block_neighbors = [\n                block_neighbor_id\n                for direction, block_neighbor_id in domain.blocks[\n                    block_id\n                ].neighbors.items()\n                if direction.dimension == 2\n                and domain.blocks[block_neighbor_id].has_distorted_frame()\n            ]\n            valid_blocks = [block_id, *block_neighbors]\n            if element.id.block_id not in valid_blocks:\n                continue\n            angular_block_logical = np.array(block_logical_coord.data)[:2]\n            angular_element_id = ElementId[2](\n                element.id.block_id, element.id.segment_ids[:2]\n            )\n            angular_element_logical = element_logical_coordinates(\n                angular_block_logical, angular_element_id\n            )\n            if angular_element_logical is None:\n                continue\n            angular_coords.append(angular_element_logical)\n            offsets.append(i)\n        if len(angular_coords) == 0:\n            continue\n        # Find the surface in this element along each ray\n        angular_coords = np.array(angular_coords).T\n        surface_logical_radii = spectre.SurfaceFinder.find_radial_surface(\n            data[0], target, element.mesh, angular_coords\n        )\n        # For those rays where we found the surface in this element, map the\n        # surface radius to inertial coordinates\n        for offset, surface_logical_radius, angular_element_logical in zip(\n            offsets, surface_logical_radii, angular_coords.T\n        ):\n            if surface_logical_radius is None:\n                continue\n            surface_logical_point = [\n                *angular_element_logical,\n                surface_logical_radius,\n            ]\n            surface_radii[offset] = np.linalg.norm(\n                np.array(\n                    element.map(\n                        np.array(surface_logical_point).reshape((3, 1)),\n                        time,\n                        functions_of_time,\n                    )\n                )[:, 0]\n                - initial_guess.expansion_center\n            )\n            filled[offset] = True\n        if np.all(filled):\n            break\n    if not np.all(filled):\n        missing_points = np.array(initial_guess_coords).T[~filled]\n        raise ValueError(\n            f\"Unable to find the surface where {var_name} = {target:g} with\"\n            f\" center {initial_guess.expansion_center}. Missing values for\"\n            f\" {len(missing_points)} / {len(filled)} radial rays. One of the\"\n            f\" missing rays goes through: {missing_points[0]}\"\n        )\n    surface = Strahlkorper[Frame.Inertial](\n        l_max=initial_guess.l_max,\n        m_max=initial_guess.m_max,\n        radius_at_collocation_points=surface_radii,\n        center=initial_guess.expansion_center,\n    )\n    logger.info(\n        f\"Found radial surface where {var_name} = {target:g} with average\"\n        f\" radius {surface.average_radius:g} and center\"\n        f\" {surface.expansion_center}.\"\n    )\n    # Write the surface to a file and return it\n    if output_surfaces_file:\n        if Path(output_surfaces_file).suffix not in [\".h5\", \".hdf5\"]:\n            output_surfaces_file += \".h5\"\n        if output_coeffs_subfile:\n            legend, ylm_data = ylm_legend_and_data(\n                surface, obs_time, surface.l_max\n            )\n            with spectre_h5.H5File(\n                str(output_surfaces_file), \"a\"\n            ) as output_file:\n                datfile = output_file.try_insert_dat(\n                    output_coeffs_subfile, legend, 0\n                )\n                datfile.append(ylm_data)\n        if output_coords_subfile:\n            vol_data = _strahlkorper_vol_data(surface)\n            with spectre_h5.H5File(\n                str(output_surfaces_file), \"a\"\n            ) as output_file:\n                volfile = output_file.try_insert_vol(output_coords_subfile, 0)\n                volfile.write_volume_data(obs_id, obs_time, vol_data)\n    return surface\n\n\n@click.command(name=\"find-radial-surface\", help=find_radial_surface.__doc__)\n@open_volfiles_command(\n    obs_id_required=True, multiple_vars=False, vars_required=True\n)\n@click.option(\n    \"--target\",\n    \"-t\",\n    type=float,\n    required=True,\n    help=\"Target value for the surface.\",\n)\n@click.option(\n    \"--l-max\",\n    \"-l\",\n    type=int,\n    required=True,\n    help=\"Max l-mode for the Ylm representation of the surface.\",\n)\n@click.option(\n    \"--initial-radius\",\n    \"-r\",\n    type=float,\n    required=True,\n    help=\"Coordinate radius of the spherical initial guess for the surface.\",\n)\n@click.option(\n    \"--center\",\n    \"-C\",\n    nargs=3,\n    type=float,\n    required=True,\n    help=\"Coordinate center of the Ylm representation of the surface.\",\n)\n@click.option(\n    \"--output-surfaces-file\",\n    type=click.Path(writable=True),\n    help=(\n        \"H5 output file where the surface Ylm coefficients will be written. Can\"\n        \" be a new or existing file.\"\n    ),\n)\n@click.option(\n    \"--output-coeffs-subfile\",\n    help=(\n        \"Name of the subfile in the 'output_surfaces_file' where the surface\"\n        \" Ylm coefficients will be written. These can be used to reconstruct\"\n        \" the surface, e.g. to deform the domain.\"\n    ),\n)\n@click.option(\n    \"--output-coords-subfile\",\n    help=(\n        \"Name of the subfile in the 'output_surfaces_file' where the surface\"\n        \" coordinates will be written. These can be used for visualization.\"\n    ),\n)\ndef find_radial_surface_command(l_max, initial_radius, center, **kwargs):\n    initial_guess = Strahlkorper[Frame.Inertial](\n        l_max=l_max, radius=initial_radius, center=center\n    )\n    find_radial_surface(initial_guess=initial_guess, **kwargs)\n\n\nif __name__ == \"__main__\":\n    find_radial_surface_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/ParallelAlgorithms/SurfaceFinder/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/ParallelAlgorithms/SurfaceFinder/SurfaceFinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ParallelAlgorithms/SurfaceFinder/SurfaceFinder.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace SurfaceFinder {\nnamespace {\n// Wrapping for the interpolator for Toms748 rootfind.\nstruct RayInterpolant {\n public:\n  double operator()(const double x) const {\n    const intrp::Irregular<1> interpolant{\n        mesh,\n        tnsr::I<DataVector, 1, Frame::ElementLogical>{DataVector{1_st, x}}};\n    return interpolant.interpolate(values)[0];\n  }\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)\n  const DataVector& values;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)\n  const Mesh<1>& mesh;\n};\n}  // namespace\n\nstd::vector<std::optional<double>> find_radial_surface(\n    const Scalar<DataVector>& data, const double target, const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 2, Frame::ElementLogical>& angular_coords,\n    const double relative_tolerance, const double absolute_tolerance) {\n  const size_t num_rays = angular_coords[0].size();\n  std::vector<std::optional<double>> result(num_rays, std::nullopt);\n\n  Variables<tmpl::list<::Tags::TempI<0, 3, Frame::ElementLogical>,\n                       ::Tags::TempScalar<0>>>\n      temp_vars_on_rays{mesh.extents(2) * num_rays};\n  DataVector& data_on_rays = get(get<::Tags::TempScalar<0>>(temp_vars_on_rays));\n  {\n    const DataVector& zeta_logical_coords =\n        Spectral::collocation_points(mesh.slice_through(2));\n    tnsr::I<DataVector, 3, Frame::ElementLogical>& target_coords =\n        get<::Tags::TempI<0, 3, Frame::ElementLogical>>(temp_vars_on_rays);\n    for (size_t i = 0; i < num_rays; i++) {\n      DataVector view{&get<0>(target_coords)[i * mesh.extents(2)],\n                      mesh.extents(2)};\n      view = get<0>(angular_coords)[i];\n\n      view.set_data_ref(&get<1>(target_coords)[i * mesh.extents(2)],\n                        mesh.extents(2));\n      view = get<1>(angular_coords)[i];\n\n      view.set_data_ref(&get<2>(target_coords)[i * mesh.extents(2)],\n                        mesh.extents(2));\n      view = zeta_logical_coords;\n    }\n    // We root find the function `data - target`, so interpolate that to the\n    // rays.\n    intrp::Irregular<3>{mesh, target_coords}.interpolate(&data_on_rays,\n                                                         get(data) - target);\n  }\n\n  for (size_t i = 0; i < num_rays; i++) {\n    const DataVector data_on_ray{&data_on_rays[i * mesh.extents(2)],\n                                 mesh.extents(2)};\n    const RayInterpolant data_interpolator{data_on_ray, mesh.slice_through(2)};\n\n    // Perform root-find only if the element brackets a root.\n    const double lower_radial_bound =\n        mesh.quadrature(2) == Spectral::Quadrature::GaussLobatto\n            ? data_on_ray[0]\n            : data_interpolator(-1.);\n    const double upper_radial_bound =\n        mesh.quadrature(2) == Spectral::Quadrature::GaussLobatto\n            ? data_on_ray[mesh.extents(2) - 1]\n            : data_interpolator(1.);\n    if (std::signbit(lower_radial_bound) != std::signbit(upper_radial_bound)) {\n      result[i] = RootFinder::toms748(data_interpolator, -1., 1.,\n                                      lower_radial_bound, upper_radial_bound,\n                                      absolute_tolerance, relative_tolerance);\n    }\n  }\n  return result;\n}\n}  // namespace SurfaceFinder\n"
  },
  {
    "path": "src/ParallelAlgorithms/SurfaceFinder/SurfaceFinder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\n/// \\ingroup SurfacesGroup\n/// Contains functions that are used to find contour levels.\nnamespace SurfaceFinder {\n/*!\n * \\ingroup SurfacesGroup\n\n * \\brief Function that interpolates data onto radial rays in the direction\n * of the logical coordinates \\f$\\xi\\f$ and \\f$\\eta\\f$ and tries to perform a\n * root find of \\f$\\text{data}-\\text{target}\\f$. Returns the logical coordinates\n * of the roots found.\n *\n * \\details We assume the element is part of a wedge block, so that the\n * \\f$\\zeta\\f$ logical coordinate points in the radial direction. Will fail if\n * multiple roots are along a ray. This could be generalized to domains other\n * than a wedge if necessary by passing in which logical direction points\n * radially.\n *\n * \\param data data to find the contour in.\n * \\param target target value for the contour level in the data.\n * \\param mesh mesh for the element.\n * \\param angular_coords tensor containing the \\f$\\xi\\f$ and \\f$\\eta\\f$\n * values of the rays extending into \\f$\\zeta\\f$ direction.\n * \\param relative_tolerance relative tolerance for toms748 rootfind.\n * \\param absolute_tolerance relative tolerance for toms748 rootfind.\n */\nstd::vector<std::optional<double>> find_radial_surface(\n    const Scalar<DataVector>& data, const double target, const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 2, Frame::ElementLogical>& angular_coords,\n    const double relative_tolerance = 1e-10,\n    const double absolute_tolerance = 1e-10);\n\n}  // namespace SurfaceFinder\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n// note that std::is_convertible is used in the following type aliases as it\n// will not match private base classes (unlike std::is_base_of) and some\n// analytic data privately inherits from an analytic solution\n\n/// \\ingroup AnalyticDataGroup\n/// \\brief Empty base class for marking analytic data.\nstruct MarkAsAnalyticData {};\n\n/// \\ingroup AnalyticDataGroup\n/// \\brief Check if `T` is an analytic data\ntemplate <typename T>\nusing is_analytic_data = typename std::is_convertible<T*, MarkAsAnalyticData*>;\n\n/// \\ingroup AnalyticDataGroup\n/// \\brief `true` if `T` is an analytic data\ntemplate <typename T>\nconstexpr bool is_analytic_data_v =\n    std::is_convertible_v<T*, MarkAsAnalyticData*>;\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Burgers/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace Burgers {\n/*!\n * \\ingroup AnalyticDataGroup\n * \\brief Holds classes implementing analytic data for Burgers equation.\n */\nnamespace AnalyticData {}\n}  // namespace Burgers\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Burgers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY BurgersAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Sinusoid.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  Factory.hpp\n  Sinusoid.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  ErrorHandling\n  PUBLIC\n  Serialization\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Burgers/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/Burgers/Sinusoid.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::AnalyticData {\n/// \\brief List of all analytic data\nusing all_data = tmpl::list<Sinusoid>;\n}  // namespace Burgers::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Burgers/Sinusoid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/Burgers/Sinusoid.hpp\"\n\n#include <array>\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace Burgers::AnalyticData {\ntemplate <typename T>\nScalar<T> Sinusoid::u(const tnsr::I<T, 1>& x) const {\n  return Scalar<T>{sin(get<0>(x))};\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> Sinusoid::get_clone()\n    const {\n  return std::make_unique<Sinusoid>(*this);\n}\n\nSinusoid::Sinusoid(CkMigrateMessage* msg) : InitialData(msg) {}\n\ntuples::TaggedTuple<Tags::U> Sinusoid::variables(\n    const tnsr::I<DataVector, 1>& x, tmpl::list<Tags::U> /*meta*/) const {\n  return {u(x)};\n}\n\nvoid Sinusoid::pup(PUP::er& p) { InitialData::pup(p); }\n\nPUP::able::PUP_ID Sinusoid::my_PUP_ID = 0;\n\nbool operator==(const Sinusoid& /*lhs*/, const Sinusoid& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const Sinusoid& lhs, const Sinusoid& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace Burgers::AnalyticData\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template Scalar<DTYPE(data)> Burgers::AnalyticData::Sinusoid::u( \\\n      const tnsr::I<DTYPE(data), 1>& x) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Burgers/Sinusoid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Burgers::AnalyticData {\n/*!\n * \\brief Analytic data (with an \"exact\" solution known) that is periodic over\n * the interval \\f$[0,2\\pi]\\f$.\n *\n * The initial data is given by:\n *\n * \\f{align}{\n *   u(x, 0) = \\sin(x)\n * \\f}\n *\n * At future times the analytic solution can be found by solving the\n * transcendental equation \\cite Harten19973\n *\n * \\f{align}{\n *   \\label{eq:transcendental burgers periodic}\n *   \\mathcal{F}=\\sin\\left(x-\\mathcal{F}t\\right)\n * \\f}\n *\n * on the interval \\f$x\\in(0,\\pi)\\f$. The solution from \\f$x\\in(\\pi,2\\pi)\\f$ is\n * given by \\f$\\mathcal{F}(x, t)=-\\mathcal{F}(2\\pi-x,t)\\f$. The transcendental\n * equation \\f$(\\ref{eq:transcendental burgers periodic})\\f$ can be solved with\n * a Newton-Raphson iterative scheme. Since this can be quite sensitive to the\n * initial guess we implement this solution as analytic data. The python code\n * below can be used to compute the analytic solution if desired.\n *\n * At time \\f$1\\f$ the solution develops a discontinuity at \\f$x=\\pi\\f$ followed\n * by the amplitude of the solution decaying over time.\n *\n * \\note We have rescaled \\f$x\\f$ and \\f$t\\f$ by \\f$\\pi\\f$ compared to\n * \\cite Harten19973.\n *\n * \\code{py}\n   import numpy as np\n   from scipy.optimize import newton\n\n   # x_grid is a np.array of positions at which to evaluate the solution\n   def burgers_periodic(x_grid, time):\n       def opt_fun(F, x, t):\n           return np.sin((x - F * t)) - F\n\n       results = []\n       for i in range(len(x_grid)):\n           x = x_grid[i]\n           greater_than_pi = False\n           if x > np.pi:\n               x = x - np.pi\n               x = -x\n               x = x + np.pi\n               greater_than_pi = True\n\n           guess = 0.0\n           if len(results) > 0:\n               if results[-1] < 0.0:\n                   guess = -results[-1]\n               else:\n                   guess = results[-1]\n           res = newton(lambda F: opt_fun(F, x, time), x0=guess)\n\n           if greater_than_pi:\n               results.append(-res)\n           else:\n               results.append(res)\n\n       return np.asarray(results)\n * \\endcode\n */\nclass Sinusoid : public evolution::initial_data::InitialData,\n                 public MarkAsAnalyticData {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A solution that is periodic over the interval [0,2pi]. The solution \"\n      \"starts as a sinusoid: u(x,0) = sin(x) and develops a \"\n      \"discontinuity at x=pi and t=1.\"};\n\n  Sinusoid() = default;\n  Sinusoid(const Sinusoid&) = default;\n  Sinusoid& operator=(const Sinusoid&) = default;\n  Sinusoid(Sinusoid&&) = default;\n  Sinusoid& operator=(Sinusoid&&) = default;\n  ~Sinusoid() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit Sinusoid(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Sinusoid);\n  /// \\endcond\n\n  template <typename T>\n  Scalar<T> u(const tnsr::I<T, 1>& x) const;\n\n  tuples::TaggedTuple<Tags::U> variables(const tnsr::I<DataVector, 1>& x,\n                                         tmpl::list<Tags::U> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n};\n\nbool operator==(const Sinusoid& /*lhs*/, const Sinusoid& /*rhs*/);\n\nbool operator!=(const Sinusoid& lhs, const Sinusoid& rhs);\n}  // namespace Burgers::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY AnalyticData)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  Tags.hpp\n  )\n\nadd_subdirectory(Burgers)\nadd_subdirectory(CurvedWaveEquation)\nadd_subdirectory(ForceFree)\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(GhGrMhd)\nadd_subdirectory(GhScalarTensor)\nadd_subdirectory(GrMhd)\nadd_subdirectory(RadiationTransport)\nadd_subdirectory(NewtonianEuler)\nadd_subdirectory(Punctures)\nadd_subdirectory(ScalarTensor)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/CurvedWaveEquation/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace CurvedScalarWave {\n/*!\n * \\ingroup AnalyticDataGroup\n * \\brief Holds classes implementing analytic data for the CurvedScalarWave\n * system.\n */\nnamespace AnalyticData {}\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/CurvedWaveEquation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY CurvedWaveEquationAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  PureSphericalHarmonic.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  PureSphericalHarmonic.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  AnalyticData\n  CurvedScalarWave\n  Options\n  ScalarWave\n  PUBLIC\n  DataStructures\n  GeneralRelativity\n  GeneralRelativitySolutions\n  SpinWeightedSphericalHarmonics\n  Utilities\n  WaveEquationSolutions\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace CurvedScalarWave::AnalyticData {\n\nPureSphericalHarmonic::PureSphericalHarmonic(const double radius,\n                                             const double width,\n                                             std::pair<size_t, int> mode,\n                                             const Options::Context& context)\n    : radius_(radius), width_sq_(width * width), mode_{std::move(mode)} {\n  if (abs(mode_.second) > static_cast<int>(mode_.first)) {\n    PARSE_ERROR(\n        context,\n        \"The absolute value of the m_mode must be less than or equal to the \"\n        \"l-mode but the m-mode is \"\n            << mode_.second << \" and the l-mode is \" << mode_.first);\n  }\n  if (radius_ <= 0.) {\n    PARSE_ERROR(context,\n                \"The radius must be greater than 0 but is \" << radius_);\n  }\n  if (width <= 0.) {\n    PARSE_ERROR(context, \"The width must be greater than 0 but is \" << width);\n  }\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nPureSphericalHarmonic::get_clone() const {\n  return std::make_unique<PureSphericalHarmonic>(*this);\n}\n\nPureSphericalHarmonic::PureSphericalHarmonic(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\ntuples::TaggedTuple<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                    CurvedScalarWave::Tags::Phi<3>>\nPureSphericalHarmonic::variables(const tnsr::I<DataVector, 3>& x,\n                                 tags /*meta*/) const {\n  Scalar<DataVector> pi{get(magnitude(x)) - radius_};\n  get(pi) = exp(-get(pi) * get(pi) / width_sq_);\n  const Spectral::Swsh::SpinWeightedSphericalHarmonic spherical_harmonic(\n      0, mode_.first, mode_.second);\n  const auto theta = atan2(hypot(x[0], x[1]), x[2]);\n  const auto phi = atan2(x[1], x[0]);\n  get(pi) *= real(spherical_harmonic.evaluate(theta, phi, sin(theta / 2.),\n                                              cos(theta / 2.)));\n  return tuples::TaggedTuple<CurvedScalarWave::Tags::Psi,\n                             CurvedScalarWave::Tags::Pi,\n                             CurvedScalarWave::Tags::Phi<3>>{\n      make_with_value<Scalar<DataVector>>(x, 0.), std::move(pi),\n      make_with_value<tnsr::i<DataVector, 3>>(x, 0.)};\n}\n\nvoid PureSphericalHarmonic::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | radius_;\n  p | width_sq_;\n  p | mode_;\n}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID PureSphericalHarmonic::my_PUP_ID = 0;\n\nbool operator==(const PureSphericalHarmonic& lhs,\n                const PureSphericalHarmonic& rhs) {\n  return lhs.radius_ == rhs.radius_ and lhs.width_sq_ == rhs.width_sq_ and\n         lhs.mode_ == rhs.mode_;\n}\nbool operator!=(const PureSphericalHarmonic& lhs,\n                const PureSphericalHarmonic& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace CurvedScalarWave::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for a pure spherical harmonic in three\n * dimensions.\n *\n * \\details The initial data is taken from \\cite Scheel2003vs , Eqs. 4.1--4.3,\n * and sets the evolved variables of the scalar wave as follows:\n *\n * \\f{align}\n * \\Psi &= 0 \\\\\n * \\Phi_i &= 0 \\\\\n * \\Pi &= \\Pi_0(r, \\theta, \\phi) =  e^{- (r - r_0)^2 / w^2} Y_{lm}(\\theta,\n * \\phi), \\f}\n *\n * where \\f$r_0\\f$ is the radius of the profile and \\f$w\\f$ is its width. This\n * describes a pure spherical harmonic mode \\f$Y_{lm}(\\theta, \\phi)\\f$ truncated\n * by a circular Gaussian window function.\n *\n * When evolved, the scalar field \\f$\\Phi\\f$ will briefly build up around the\n * radius \\f$r_0\\f$ and then disperse. This can be used to study the ringdown\n * behavior and late-time tails in different background spacetimes.\n */\n\nclass PureSphericalHarmonic : public evolution::initial_data::InitialData,\n                              public MarkAsAnalyticData {\n public:\n  struct Radius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The radius of the spherical harmonic profile\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Width {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The width of the spherical harmonic profile.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Mode {\n    using type = std::pair<size_t, int>;\n    static constexpr Options::String help = {\n        \"The l-mode and m-mode of the spherical harmonic Ylm\"};\n  };\n\n  using options = tmpl::list<Radius, Width, Mode>;\n\n  static constexpr Options::String help = {\n      \"Initial data for a pure spherical harmonic mode truncated by a circular \"\n      \"Gaussian window funtion. The expression is taken from Scheel(2003), \"\n      \"equations 4.1-4.3.\"};\n\n  PureSphericalHarmonic() = default;\n\n  PureSphericalHarmonic(double radius, double width,\n                        std::pair<size_t, int> mode,\n                        const Options::Context& context = {});\n  PureSphericalHarmonic(const PureSphericalHarmonic&) = default;\n  PureSphericalHarmonic& operator=(const PureSphericalHarmonic&) = default;\n  PureSphericalHarmonic(PureSphericalHarmonic&&) = default;\n  PureSphericalHarmonic& operator=(PureSphericalHarmonic&&) = default;\n  ~PureSphericalHarmonic() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit PureSphericalHarmonic(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(PureSphericalHarmonic);\n  /// \\endcond\n\n  static constexpr size_t volume_dim = 3;\n  using tags =\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<3>>;\n\n  /// Retrieve the evolution variables at spatial coordinates `x`\n  tuples::TaggedTuple<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                      CurvedScalarWave::Tags::Phi<3>>\n  variables(const tnsr::I<DataVector, 3>& x, tags /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  double width_sq_{std::numeric_limits<double>::signaling_NaN()};\n  std::pair<size_t, int> mode_{std::numeric_limits<size_t>::signaling_NaN(),\n                               std::numeric_limits<int>::signaling_NaN()};\n\n  friend bool operator==(const PureSphericalHarmonic& lhs,\n                         const PureSphericalHarmonic& rhs);\n\n  friend bool operator!=(const PureSphericalHarmonic& lhs,\n                         const PureSphericalHarmonic& rhs);\n};\n\n}  // namespace CurvedScalarWave::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ForceFree {\n/*!\n * \\ingroup AnalyticDataGroup\n * \\brief Holds classes implementing analytic data for the GRFFE system.\n */\nnamespace AnalyticData {}\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ForceFreeAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FfeBreakdown.cpp\n  MagnetosphericWald.cpp\n  RotatingDipole.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  Factory.hpp\n  FfeBreakdown.hpp\n  MagnetosphericWald.hpp\n  RotatingDipole.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  Options\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/ForceFree/FfeBreakdown.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/MagnetosphericWald.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/RotatingDipole.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::AnalyticData {\n/*!\n * \\brief Typelist of all analytic data of GRFFE evolution system\n */\nusing all_data = tmpl::list<FfeBreakdown, MagnetosphericWald, RotatingDipole>;\n}  // namespace ForceFree::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/FfeBreakdown.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/ForceFree/FfeBreakdown.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ForceFree::AnalyticData {\n\nFfeBreakdown::FfeBreakdown(CkMigrateMessage* msg) : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> FfeBreakdown::get_clone()\n    const {\n  return std::make_unique<FfeBreakdown>(*this);\n}\n\nvoid FfeBreakdown::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | background_spacetime_;\n}\n\nPUP::able::PUP_ID FfeBreakdown::my_PUP_ID = 0;\n\ntuples::TaggedTuple<Tags::TildeE> FfeBreakdown::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildeE> /*meta*/) {\n  auto result = make_with_value<tnsr::I<DataVector, 3>>(coords, 0.0);\n  result.get(1) = 0.5;\n  result.get(2) = -0.5;\n  return result;\n}\n\ntuples::TaggedTuple<Tags::TildeB> FfeBreakdown::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildeB> /*meta*/) {\n  auto result = make_with_value<tnsr::I<DataVector, 3>>(coords, 1.0);\n  for (size_t i = 0; i < result.get(0).size(); ++i) {\n    const double& x = coords.get(0)[i];\n    if (x > -0.1) {\n      if (x <= 0.1) {\n        result.get(1)[i] = -10.0 * x;\n        result.get(2)[i] = -10.0 * x;\n      } else {\n        result.get(1)[i] = -1.0;\n        result.get(2)[i] = -1.0;\n      }\n    }\n  }\n  return result;\n}\n\ntuples::TaggedTuple<Tags::TildePsi> FfeBreakdown::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildePsi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(coords, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildePhi> FfeBreakdown::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildePhi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(coords, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildeQ> FfeBreakdown::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildeQ> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(coords, 0.0)};\n}\n\nbool operator==(const FfeBreakdown& lhs, const FfeBreakdown& rhs) {\n  return lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const FfeBreakdown& lhs, const FfeBreakdown& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/FfeBreakdown.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::AnalyticData {\n/*!\n * \\brief A test problem designed to show that the system initially satisfying\n * the force-free conditions may violate those in a later time.\n *\n * This test was originally performed by \\cite Komissarov2002. We use the\n * initial data with a linear transition layer as \\cite Etienne2017.\n *\n * \\f{align*}{\n *  B^x & = 1.0 , \\\\\n *  B^y & = B^z = \\left\\{\\begin{array}{ll}\n *  1.0         & \\text{if } x < -0.1 \\\\\n *  -10x        & \\text{if } -0.1 \\leq x \\leq 0.1 \\\\\n *  -1.0        & \\text{if } x > 0.1 \\\\\n * \\end{array}\\right\\}, \\\\\n *  E^x & = 0.0  , \\\\\n *  E^y & = 0.5  , \\\\\n *  E^z & = -0.5 .\n * \\f}\n *\n * As time progresses, $B^2-E^2$ approaches to zero.\n *\n */\nclass FfeBreakdown : public evolution::initial_data::InitialData,\n                     public MarkAsAnalyticData {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\"A FFE breakdown problem\"};\n\n  FfeBreakdown() = default;\n  FfeBreakdown(const FfeBreakdown&) = default;\n  FfeBreakdown& operator=(const FfeBreakdown&) = default;\n  FfeBreakdown(FfeBreakdown&&) = default;\n  FfeBreakdown& operator=(FfeBreakdown&&) = default;\n  ~FfeBreakdown() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit FfeBreakdown(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(FfeBreakdown);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// @{\n  /// Retrieve the EM variables at (x,t).\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildeE> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeE>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildeB> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeB>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildePsi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePsi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildePhi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePhi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildeQ> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeQ>;\n  /// @}\n\n  /// Retrieve a collection of EM variables at position x\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n private:\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  friend bool operator==(const FfeBreakdown& lhs, const FfeBreakdown& rhs);\n  friend bool operator!=(const FfeBreakdown& lhs, const FfeBreakdown& rhs);\n};\n\n}  // namespace ForceFree::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/MagnetosphericWald.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/ForceFree/MagnetosphericWald.hpp\"\n\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ForceFree::AnalyticData {\n\nMagnetosphericWald::MagnetosphericWald(const double spin,\n                                       const Options::Context& context)\n    : spin_(spin),\n      background_spacetime_{1.0, {{0.0, 0.0, spin_}}, {{0.0, 0.0, 0.0}}} {\n  if (abs(spin_) > 1.0) {\n    PARSE_ERROR(context, \"The magnitude of the dimensionless spin (\"\n                             << spin_ << \") cannot be bigger than 1.0\");\n  }\n}\n\nMagnetosphericWald::MagnetosphericWald(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nMagnetosphericWald::get_clone() const {\n  return std::make_unique<MagnetosphericWald>(*this);\n}\n\nvoid MagnetosphericWald::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | spin_;\n  p | background_spacetime_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID MagnetosphericWald::my_PUP_ID = 0;\n\ntuples::TaggedTuple<Tags::TildeE> MagnetosphericWald::variables(\n    const tnsr::I<DataVector, 3>& x, tmpl::list<Tags::TildeE> /*meta*/) {\n  return {make_with_value<tnsr::I<DataVector, 3>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildeB> MagnetosphericWald::variables(\n    const tnsr::I<DataVector, 3>& x, tmpl::list<Tags::TildeB> /*meta*/) const {\n  auto tilde_b =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(x, 0.0);\n\n  const double a_squared = square(spin_);\n\n  const auto& x_bar = get<0>(x);\n  const auto& y_bar = get<1>(x);\n  const auto& z_bar = get<2>(x);\n\n  const DataVector r_squared = get(dot_product(x, x));\n  const DataVector r = sqrt(r_squared);\n  const DataVector r_to_the_fourth = square(r_squared);\n  const DataVector z_squared = square(z_bar);\n\n  const DataVector temp1 = r_to_the_fourth + a_squared * z_squared;\n  const DataVector temp2 =\n      square(temp1) + 2.0 * r_to_the_fourth * r * (r_squared - a_squared);\n  const DataVector temp3 = temp1 * (r_squared - z_squared) -\n                           4.0 * r_to_the_fourth * (r_squared + z_squared);\n\n  get<0>(tilde_b) =\n      (spin_ * x_bar - r * y_bar) * temp2 + spin_ * r * x_bar * temp3;\n  get<1>(tilde_b) =\n      (r * x_bar + spin_ * y_bar) * temp2 + spin_ * r * y_bar * temp3;\n\n  get<0>(tilde_b) *= spin_ * z_bar / (r_to_the_fourth * square(temp1));\n  get<1>(tilde_b) *= spin_ * z_bar / (r_to_the_fourth * square(temp1));\n\n  get<2>(tilde_b) = 1.0 + (a_squared * z_squared / r_to_the_fourth);\n  get<2>(tilde_b) +=\n      a_squared *\n      (1.0 - z_squared * (a_squared + z_squared) *\n                 (5.0 * r_to_the_fourth + a_squared * z_squared) /\n                 square(temp1)) /\n      (r_squared * r);\n\n  return tilde_b;\n}\n\ntuples::TaggedTuple<Tags::TildePsi> MagnetosphericWald::variables(\n    const tnsr::I<DataVector, 3>& x, tmpl::list<Tags::TildePsi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildePhi> MagnetosphericWald::variables(\n    const tnsr::I<DataVector, 3>& x, tmpl::list<Tags::TildePhi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildeQ> MagnetosphericWald::variables(\n    const tnsr::I<DataVector, 3>& x, tmpl::list<Tags::TildeQ> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\nbool operator==(const MagnetosphericWald& lhs, const MagnetosphericWald& rhs) {\n  return lhs.spin_ == rhs.spin_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const MagnetosphericWald& lhs, const MagnetosphericWald& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/MagnetosphericWald.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::AnalyticData {\n/*!\n * \\brief The magnetospheric Wald problem proposed in \\cite Komissarov2004\n *\n * This is an initial value problem that evolves the magnetosphere of a rotating\n * black hole. The initial condition is given as same as the exact Wald solution\n * \\cite Wald1974 (see also documentation of ForceFree::Solutions::ExactWald)\n *\n * \\begin{equation}\n *  A_\\mu = \\frac{B_0}{2}(\\phi_\\mu + 2a t_\\mu) ,\n * \\end{equation}\n *\n * but electric field is set to zero at $t=0$.\n *\n * In the cartesian projection of the spherical Kerr-Schild coordinates\n * (which we use in the code for representing tensors), initial magnetic fields\n * is given as\n *\n * \\begin{align}\n * \\tilde{B}^{x} &= a B_0 z \\left[ (ax-ry) \\left\\{\n *      \\frac{1}{r^4} + \\frac{2M r (r^2-a^2)}{(r^4+a^2z^2)^2} \\right\\}\n *      + a M r x \\left\\{ \\frac{r^2-z^2}{r^4(r^4+a^2z^2)}\n *      - \\frac{4(r^2+z^2)}{(r^4+a^2z^2)^2} \\right\\} \\right] \\\\\n * \\tilde{B}^{y} &= a B_0 z \\left[ (rx+ay) \\left\\{\n *      \\frac{1}{r^4} + \\frac{2M r (r^2-a^2)}{(r^4+a^2z^2)^2} \\right\\}\n *      + a M r y \\left\\{ \\frac{r^2-z^2}{r^4(r^4+a^2z^2)}\n *      - \\frac{4(r^2+z^2)}{(r^4+a^2z^2)^2} \\right\\} \\right] \\\\\n * \\tilde{B}^{z} &= B_0 \\left[\n *      1 + \\frac{a^2z^2}{r^4} + \\frac{M a^2}{r^3}\\left\\{\n *      1 - \\frac{z^2(a^2+z^2)(5r^4+a^2z^2)}{(r^4+a^2z^2)^2} \\right\\} \\right] .\n * \\end{align}\n *\n * where $M$ and $a$ are mass and (dimensionless) spin of the Kerr black hole,\n * $B_0$ is the amplitude of magnetic field, and $r$ is the radial coordinate\n * defined in the spherical Kerr-Schild coordinate system (see the documentation\n * of gr::Solutions::SphericalKerrSchild). All other variables are set to zero\n * at $t=0$.\n *\n * There is no known exact solution to this problem, but numerical simulations\n * \\cite Komissarov2004 \\cite Paschalidis2013 \\cite Etienne2017 report that the\n * system converges to a steady state with an equatorial current sheet inside\n * the ergosphere.\n *\n * \\note We set $M=1$ and $B_0=1$ in the initial data to fix scales.\n *\n */\nclass MagnetosphericWald : public evolution::initial_data::InitialData,\n                           public MarkAsAnalyticData {\n public:\n  struct Spin {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The dimensionless spin of the Kerr BH\"};\n    static type upper_bound() { return 1.0; }\n    static type lower_bound() { return -1.0; }\n  };\n\n  using options = tmpl::list<Spin>;\n  static constexpr Options::String help{\n      \"Magnetospheric Wald initial value problem\"};\n\n  MagnetosphericWald() = default;\n  MagnetosphericWald(const MagnetosphericWald&) = default;\n  MagnetosphericWald& operator=(const MagnetosphericWald&) = default;\n  MagnetosphericWald(MagnetosphericWald&&) = default;\n  MagnetosphericWald& operator=(MagnetosphericWald&&) = default;\n  ~MagnetosphericWald() override = default;\n\n  explicit MagnetosphericWald(double spin,\n                              const Options::Context& context = {});\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit MagnetosphericWald(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MagnetosphericWald);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// @{\n  /// Retrieve the EM variables at (x,t).\n  static auto variables(const tnsr::I<DataVector, 3>& x,\n                        tmpl::list<Tags::TildeE> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeE>;\n\n  auto variables(const tnsr::I<DataVector, 3>& x,\n                 tmpl::list<Tags::TildeB> /*meta*/) const\n      -> tuples::TaggedTuple<Tags::TildeB>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x,\n                        tmpl::list<Tags::TildePsi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePsi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x,\n                        tmpl::list<Tags::TildePhi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePhi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x,\n                        tmpl::list<Tags::TildeQ> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeQ>;\n  /// @}\n\n  /// Retrieve a collection of EM variables at position x\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n private:\n  double spin_ = std::numeric_limits<double>::signaling_NaN();\n  gr::Solutions::SphericalKerrSchild background_spacetime_{};\n\n  friend bool operator==(const MagnetosphericWald& lhs,\n                         const MagnetosphericWald& rhs);\n};\n\nbool operator!=(const MagnetosphericWald& lhs, const MagnetosphericWald& rhs);\n\n}  // namespace ForceFree::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/RotatingDipole.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/ForceFree/RotatingDipole.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ForceFree::AnalyticData {\n\nRotatingDipole::RotatingDipole(const double vector_potential_amplitude,\n                               const double varpi0, const double delta,\n                               const double angular_velocity,\n                               const double tilt_angle,\n                               const Options::Context& context)\n    : vector_potential_amplitude_(vector_potential_amplitude),\n      varpi0_(varpi0),\n      delta_(delta),\n      angular_velocity_(angular_velocity),\n      tilt_angle_(tilt_angle) {\n  if (varpi0 < 0.0) {\n    PARSE_ERROR(context, \"The length constant varpi0 (\"\n                             << varpi0_ << \") cannot be negative\");\n  }\n  if (delta < 0.0) {\n    PARSE_ERROR(context,\n                \"The small number delta (\" << delta_ << \") cannot be negative\");\n  }\n  if (abs(angular_velocity) >= 1.0) {\n    PARSE_ERROR(context, \"The rotation angular velocity (\"\n                             << angular_velocity_\n                             << \") must be between -1.0 and 1.0\");\n  }\n  if ((tilt_angle < 0.0) or (tilt_angle > M_PI)) {\n    PARSE_ERROR(context, \"The rotator tilt angle (\"\n                             << tilt_angle_ << \") must be between 0 and Pi\");\n  }\n}\n\nRotatingDipole::RotatingDipole(CkMigrateMessage* msg) : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nRotatingDipole::get_clone() const {\n  return std::make_unique<RotatingDipole>(*this);\n}\n\nvoid RotatingDipole::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | vector_potential_amplitude_;\n  p | varpi0_;\n  p | delta_;\n  p | angular_velocity_;\n  p | tilt_angle_;\n  p | background_spacetime_;\n}\n\nPUP::able::PUP_ID RotatingDipole::my_PUP_ID = 0;\n\ntuples::TaggedTuple<Tags::TildeE> RotatingDipole::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildeE> /*meta*/) {\n  return {make_with_value<tnsr::I<DataVector, 3>>(coords, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildeB> RotatingDipole::variables(\n    const tnsr::I<DataVector, 3>& coords,\n    tmpl::list<Tags::TildeB> /*meta*/) const {\n  auto result = make_with_value<tnsr::I<DataVector, 3>>(coords, 0.0);\n\n  const double sin_alpha = sin(tilt_angle_);\n  const double cos_alpha = cos(tilt_angle_);\n\n  // Coordinates and magnetic fields in the tilted axis\n  const auto& x = get<0>(coords);\n  const auto& y = get<1>(coords);\n  const auto& z = get<2>(coords);\n  const DataVector x_prime = cos_alpha * x - sin_alpha * z;\n  const DataVector z_prime = sin_alpha * x + cos_alpha * z;\n\n  auto tilde_b_prime = make_with_value<tnsr::I<DataVector, 3>>(coords, 0.0);\n\n  // Regularized dipole field\n  const DataVector r_squared = get(dot_product(coords, coords));\n  const DataVector one_over_radius_factor =\n      1.0 / pow<5>(sqrt(r_squared + square(delta_)));\n  get<0>(tilde_b_prime) = 3.0 * x_prime * z_prime * one_over_radius_factor;\n  get<1>(tilde_b_prime) = 3.0 * y * z_prime * one_over_radius_factor;\n  get<2>(tilde_b_prime) =\n      (3.0 * square(z_prime) - r_squared + 2.0 * square(delta_)) *\n      one_over_radius_factor;\n\n  // Rotation\n  get<0>(result) =\n      cos_alpha * get<0>(tilde_b_prime) + sin_alpha * get<2>(tilde_b_prime);\n  get<1>(result) = get<1>(tilde_b_prime);\n  get<2>(result) =\n      -sin_alpha * get<0>(tilde_b_prime) + cos_alpha * get<2>(tilde_b_prime);\n\n  return result;\n}\n\ntuples::TaggedTuple<Tags::TildePsi> RotatingDipole::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildePsi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(coords, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildePhi> RotatingDipole::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildePhi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(coords, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildeQ> RotatingDipole::variables(\n    const tnsr::I<DataVector, 3>& coords, tmpl::list<Tags::TildeQ> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(coords, 0.0)};\n}\n\nstd::optional<Scalar<DataVector>> RotatingDipole::interior_mask(\n    const tnsr::I<DataVector, 3>& x) {\n  std::optional<Scalar<DataVector>> result{};\n\n  DataVector r_squared = get(dot_product(x, x));\n\n  // NS radius rescaled to 1.0 on grid.\n  const double ns_radius_squared = square(1.0);\n\n  if (min(r_squared) < ns_radius_squared) {\n    // Allocate the mask vector\n    const size_t num_grid_points = get<0>(x).size();\n    result = Scalar<DataVector>{num_grid_points};\n\n    for (size_t i = 0; i < num_grid_points; ++i) {\n      if (r_squared[i] < ns_radius_squared) {\n        // Interior\n        get(result.value())[i] = -1.0;\n      } else {\n        // Exterior\n        get(result.value())[i] = +1.0;\n      }\n    }\n  }\n\n  return result;\n}\n\nbool operator==(const RotatingDipole& lhs, const RotatingDipole& rhs) {\n  return lhs.vector_potential_amplitude_ == rhs.vector_potential_amplitude_ and\n         lhs.varpi0_ == rhs.varpi0_ and lhs.delta_ == rhs.delta_ and\n         lhs.angular_velocity_ == rhs.angular_velocity_ and\n         lhs.tilt_angle_ == rhs.tilt_angle_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const RotatingDipole& lhs, const RotatingDipole& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ForceFree/RotatingDipole.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::AnalyticData {\n\n/*!\n * \\brief The magnetosphere of an isolated rotating star with dipolar initial\n * magnetic field in the flat spacetime. This is a toy model of a pulsar\n * magnetosphere.\n *\n * \\note Coordinate radius of the star is rescaled to 1.0 in code units.\n *\n * The vector potential of the initial magnetic field has the form\n * \\cite Most2022\n *\n * \\begin{equation}\n *  A_\\phi = \\frac{A_0 \\varpi_0 (x^2+y^2)}{(r^2 + \\delta^2)^{3/2}}\n * \\end{equation}\n *\n * where $A_0$ is the vector potential amplitude, $\\varpi_0$ is a constant with\n * the unit of length, $r^2 = x^2 + y^2 + z^2$, and $\\delta$ is a small number\n * for regularization of the dipole magnetic field at the origin ($r=0$).\n *\n * In the Cartesian coordinates, components of densitized magnetic fields are\n * given as\n *\n * \\begin{align}\n *  \\tilde{B}^x & = A_0 \\varpi_0 \\frac{3xz}{(r^2 + \\delta^2)^{5/2}} , \\\\\n *  \\tilde{B}^y & = A_0 \\varpi_0 \\frac{3yz}{(r^2 + \\delta^2)^{5/2}} , \\\\\n *  \\tilde{B}^z & = A_0 \\varpi_0 \\frac{3z^2 - r^2 + 2\\delta^2}{(r^2 +\n *                    \\delta^2)^{5/2}} .\n * \\end{align}\n *\n * Rotation of the star is switched on at $t=0$ with the angular velocity\n * specified in the input file. The grid points inside the star ($x^2 + y^2 +\n * z^2 < 1.0$) are identified as the interior and masked by `interior_mask()`\n * member function. In the masked region we impose the MHD condition\n * $\\mathbf{E} + \\mathbf{v} \\times \\mathbf{B} = 0$, where the velocity field is\n * given by $\\mathbf{v} \\equiv \\Omega \\hat{z} \\times \\mathbf{r}$. By this means,\n * initial dipolar magnetic field is effectively \"anchored\" inside the star\n * while electromagnetic fields are evolved self-consistently outside the star.\n *\n * \\note We impose the MHD condition stated above and $q=0$ inside the masked\n * interior region during the evolution phase. While this initial data class\n * sets both electric field and charge density to zero at $t=0$, those variables\n * are immediately overwritten with proper values ($\\mathbf{E} =\n * -\\mathbf{v}\\times\\mathbf{B}$, $q=0$) once the simulation begins.\n *\n * When the system reaches a stationary state, magnetic field lines far from the\n * star are opening up while the field lines close to the star are corotating.\n * The light cylinder and the Y-point, which marks the boundary between these\n * two regions with different magnetic field topology, are expected to be formed\n * at $r_\\text{LC} = c/\\Omega$.\n *\n * The option `TiltAngle` controls the angle $\\alpha$ between the rotation axis\n * ($z$) and the magnetic axis of initial magnetic field on the $x-z$ plane. An\n * aligned rotator ($\\alpha = 0$) is a common test problem for FFE codes,\n * whereas an oblique rotator ($\\alpha \\neq 0$) is a more realistic model of\n * pulsars \\cite Spitkovsky2006.\n *\n */\nclass RotatingDipole : public evolution::initial_data::InitialData,\n                       public MarkAsAnalyticData {\n public:\n  struct VectorPotentialAmplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The vector potential amplitude A_0\"};\n  };\n\n  struct Varpi0 {\n    using type = double;\n    static constexpr Options::String help = {\"The length constant varpi_0\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Delta {\n    using type = double;\n    static constexpr Options::String help = {\n        \"A small value used to regularize magnetic fields at r=0.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct AngularVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Rotation angular velocity of the star.\"};\n    static type upper_bound() { return 1.0; }\n    static type lower_bound() { return -1.0; }\n  };\n\n  struct TiltAngle {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Angle between the rotation axis (z) and magnetic axis at t = 0.\"};\n    static type upper_bound() { return M_PI; }\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options = tmpl::list<VectorPotentialAmplitude, Varpi0, Delta,\n                             AngularVelocity, TiltAngle>;\n  static constexpr Options::String help{\n      \"Magnetosphere of an isolated rotating star with dipole magnetic field.\"};\n\n  RotatingDipole() = default;\n  RotatingDipole(const RotatingDipole&) = default;\n  RotatingDipole& operator=(const RotatingDipole&) = default;\n  RotatingDipole(RotatingDipole&&) = default;\n  RotatingDipole& operator=(RotatingDipole&&) = default;\n  ~RotatingDipole() override = default;\n\n  RotatingDipole(double vector_potential_amplitude, double varpi0, double delta,\n                 double angular_velocity, double tilt_angle,\n                 const Options::Context& context = {});\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit RotatingDipole(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RotatingDipole);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// @{\n  /// Retrieve the EM variables.\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildeE> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeE>;\n\n  auto variables(const tnsr::I<DataVector, 3>& coords,\n                 tmpl::list<Tags::TildeB> /*meta*/) const\n      -> tuples::TaggedTuple<Tags::TildeB>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildePsi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePsi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildePhi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePhi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& coords,\n                        tmpl::list<Tags::TildeQ> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeQ>;\n  /// @}\n\n  /// Retrieve a collection of EM variables at position x\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n  // Returns the value of NS interior mask\n  static std::optional<Scalar<DataVector>> interior_mask(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x);\n\n  // Returns the value of angular velocity.\n  double angular_velocity() const { return angular_velocity_; };\n\n private:\n  double vector_potential_amplitude_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double varpi0_ = std::numeric_limits<double>::signaling_NaN();\n  double delta_ = std::numeric_limits<double>::signaling_NaN();\n  double angular_velocity_ = std::numeric_limits<double>::signaling_NaN();\n  double tilt_angle_ = std::numeric_limits<double>::signaling_NaN();\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  friend bool operator==(const RotatingDipole& lhs, const RotatingDipole& rhs);\n};\n\nbool operator!=(const RotatingDipole& lhs, const RotatingDipole& rhs);\n\n}  // namespace ForceFree::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GeneralRelativity/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n\nnamespace gr {\n/// Base struct for properties common to all GR analytic data classes\ntemplate <size_t Dim>\nstruct AnalyticDataBase {\n  static constexpr size_t volume_dim = Dim;\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using DerivLapse =\n      ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<volume_dim>, Frame>;\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, volume_dim, Frame>,\n                                   tmpl::size_t<volume_dim>, Frame>;\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, volume_dim, Frame>,\n                    tmpl::size_t<volume_dim>, Frame>;\n\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using tags = tmpl::list<\n      gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,\n      DerivLapse<DataType, Frame>, gr::Tags::Shift<DataType, volume_dim, Frame>,\n      ::Tags::dt<gr::Tags::Shift<DataType, volume_dim, Frame>>,\n      DerivShift<DataType, Frame>,\n      gr::Tags::SpatialMetric<DataType, volume_dim, Frame>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim, Frame>>,\n      DerivSpatialMetric<DataType, Frame>,\n      gr::Tags::SqrtDetSpatialMetric<DataType>,\n      gr::Tags::ExtrinsicCurvature<DataType, volume_dim, Frame>,\n      gr::Tags::InverseSpatialMetric<DataType, volume_dim, Frame>>;\n};\n\n/*!\n * \\ingroup AnalyticDataGroup\n * \\brief Classes which implement analytic data for general relativity\n */\nnamespace AnalyticData {}\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GeneralRelativity/BrillLindquist.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GeneralRelativity/BrillLindquist.hpp\"\n\n#include <array>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr::AnalyticData {\nBrillLindquist::BrillLindquist(const double mass_a, const double mass_b,\n                               const std::array<double, 3>& center_a,\n                               const std::array<double, 3>& center_b,\n                               const Options::Context& context)\n    : mass_a_(mass_a),\n      mass_b_(mass_b),\n      center_a_(center_a),\n      center_b_(center_b) {\n  if (mass_a_ <= 0.0) {\n    PARSE_ERROR(context,\n                \"Mass A must be positive. Given mass: \" << mass_a_);\n  }\n  if (mass_b_ <= 0.0) {\n    PARSE_ERROR(context,\n                \"Mass B must be positive. Given mass: \" << mass_b_);\n  }\n}\n\nBrillLindquist::BrillLindquist(CkMigrateMessage* /*unused*/) {}\n\nvoid BrillLindquist::pup(PUP::er& p) {\n  p | mass_a_;\n  p | mass_b_;\n  p | center_a_;\n  p | center_b_;\n}\n\ntemplate <typename DataType, typename Frame>\nBrillLindquist::IntermediateComputer<DataType, Frame>::IntermediateComputer(\n    const BrillLindquist& analytic_data, const tnsr::I<DataType, 3, Frame>& x)\n    : analytic_data_(analytic_data), x_(x) {}\n\ntemplate <typename DataType, typename Frame>\nvoid BrillLindquist::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center_a,\n    const gsl::not_null<CachedBuffer*> /*cache*/,\n    internal_tags::x_minus_center_a<DataType, Frame> /*meta*/) const {\n  for (size_t i = 0; i < 3; ++i) {\n    x_minus_center_a->get(i) =\n        x_.get(i) - gsl::at(analytic_data_.center_a(), i);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid BrillLindquist::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> r_a,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::r_a<DataType> /*meta*/) const {\n  const auto& x_minus_center_a =\n      cache->get_var(*this, internal_tags::x_minus_center_a<DataType, Frame>{});\n  magnitude(r_a, x_minus_center_a);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid BrillLindquist::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center_b,\n    const gsl::not_null<CachedBuffer*> /*cache*/,\n    internal_tags::x_minus_center_b<DataType, Frame> /*meta*/) const {\n  for (size_t i = 0; i < 3; ++i) {\n    x_minus_center_b->get(i) =\n        x_.get(i) - gsl::at(analytic_data_.center_b(), i);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid BrillLindquist::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> r_b,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::r_b<DataType> /*meta*/) const {\n  const auto& x_minus_center_b =\n      cache->get_var(*this, internal_tags::x_minus_center_b<DataType, Frame>{});\n  magnitude(r_b, x_minus_center_b);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid BrillLindquist::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::conformal_factor<DataType> /*meta*/) const {\n  const auto& r_a = cache->get_var(*this, internal_tags::r_a<DataType>{});\n  const auto& r_b = cache->get_var(*this, internal_tags::r_b<DataType>{});\n  get(*conformal_factor) = 1.0 + 0.5 * analytic_data_.mass_a() / get(r_a) +\n                           0.5 * analytic_data_.mass_b() / get(r_b);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid BrillLindquist::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3, Frame>*> deriv_conformal_factor,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_conformal_factor<DataType, Frame> /*meta*/) const {\n  const auto& r_a = cache->get_var(*this, internal_tags::r_a<DataType>{});\n  const auto& r_b = cache->get_var(*this, internal_tags::r_b<DataType>{});\n  const auto& x_minus_center_a =\n      cache->get_var(*this, internal_tags::x_minus_center_a<DataType, Frame>{});\n  const auto& x_minus_center_b =\n      cache->get_var(*this, internal_tags::x_minus_center_b<DataType, Frame>{});\n  for (size_t i = 0; i < 3; ++i) {\n    deriv_conformal_factor->get(i) =\n        -0.5 * analytic_data_.mass_a() * x_minus_center_a.get(i) /\n            cube(get(r_a)) -\n        0.5 * analytic_data_.mass_b() * x_minus_center_b.get(i) /\n            cube(get(r_b));\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid BrillLindquist::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::SpatialMetric<DataType, 3, Frame> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, internal_tags::conformal_factor<DataType>{});\n  std::fill(spatial_metric->begin(), spatial_metric->end(), 0.);\n  get<0, 0>(*spatial_metric) = pow<4>(get(conformal_factor));\n  get<1, 1>(*spatial_metric) = get<0, 0>(*spatial_metric);\n  get<2, 2>(*spatial_metric) = get<0, 0>(*spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid BrillLindquist::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*> deriv_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    DerivSpatialMetric<DataType, Frame> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, internal_tags::conformal_factor<DataType>{});\n  const auto& deriv_conformal_factor = cache->get_var(\n      *this, internal_tags::deriv_conformal_factor<DataType, Frame>{});\n  std::fill(deriv_spatial_metric->begin(), deriv_spatial_metric->end(), 0.);\n  for (size_t k = 0; k < 3; ++k) {\n    deriv_spatial_metric->get(k, 0, 0) =\n        4.0 * pow<3>(get(conformal_factor)) * deriv_conformal_factor.get(k);\n    deriv_spatial_metric->get(k, 1, 1) = deriv_spatial_metric->get(k, 0, 0);\n    deriv_spatial_metric->get(k, 2, 2) = deriv_spatial_metric->get(k, 0, 0);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType> BrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::Lapse<DataType> /*meta*/) {\n  const auto& r_a = get(get_var(computer, internal_tags::r_a<DataType>{}));\n  return make_with_value<Scalar<DataType>>(r_a, 1.);\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType> BrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/) {\n  const auto& r_a = get(get_var(computer, internal_tags::r_a<DataType>{}));\n  return make_with_value<Scalar<DataType>>(r_a, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::i<DataType, 3, Frame>\nBrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    DerivLapse<DataType, Frame> /*meta*/) {\n  const auto& r_a = get(get_var(computer, internal_tags::r_a<DataType>{}));\n  return make_with_value<tnsr::i<DataType, 3, Frame>>(r_a, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::I<DataType, 3, Frame>\nBrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::Shift<DataType, 3, Frame> /*meta*/) {\n  const auto& r_a = get(get_var(computer, internal_tags::r_a<DataType>{}));\n  return make_with_value<tnsr::I<DataType, 3, Frame>>(r_a, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::I<DataType, 3, Frame>\nBrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>> /*meta*/) {\n  const auto& r_a = get(get_var(computer, internal_tags::r_a<DataType>{}));\n  return make_with_value<tnsr::I<DataType, 3, Frame>>(r_a, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::iJ<DataType, 3, Frame>\nBrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    DerivShift<DataType, Frame> /*meta*/) {\n  const auto& r_a = get(get_var(computer, internal_tags::r_a<DataType>{}));\n  return make_with_value<tnsr::iJ<DataType, 3, Frame>>(r_a, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::ii<DataType, 3, Frame>\nBrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>> /*meta*/) {\n  const auto& r_a = get(get_var(computer, internal_tags::r_a<DataType>{}));\n  return make_with_value<tnsr::ii<DataType, 3, Frame>>(r_a, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType> BrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/) {\n  const auto& conformal_factor =\n      get(get_var(computer, internal_tags::conformal_factor<DataType>{}));\n  return Scalar<DataType>(pow<6>(conformal_factor));\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::II<DataType, 3, Frame>\nBrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::InverseSpatialMetric<DataType, 3, Frame> /*meta*/) {\n  const auto& spatial_metric =\n      get_var(computer, gr::Tags::SpatialMetric<DataType, 3, Frame>{});\n  tnsr::II<DataType, 3, Frame> inverse_spatial_metric{};\n  get<0, 0>(inverse_spatial_metric) = 1.0 / get<0, 0>(spatial_metric);\n  get<1, 1>(inverse_spatial_metric) = 1.0 / get<1, 1>(spatial_metric);\n  get<2, 2>(inverse_spatial_metric) = 1.0 / get<2, 2>(spatial_metric);\n  return inverse_spatial_metric;\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::ii<DataType, 3, Frame>\nBrillLindquist::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::ExtrinsicCurvature<DataType, 3, Frame> /*meta*/) {\n  const auto& r_a = get(get_var(computer, internal_tags::r_a<DataType>{}));\n  return make_with_value<tnsr::ii<DataType, 3, Frame>>(r_a, 0.);\n}\n\nbool operator==(const BrillLindquist& lhs, const BrillLindquist& rhs) {\n  return lhs.mass_a() == rhs.mass_a() and lhs.mass_b() == rhs.mass_b() and\n         lhs.center_a() == rhs.center_a() and lhs.center_b() == rhs.center_b();\n}\n\nbool operator!=(const BrillLindquist& lhs, const BrillLindquist& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template class BrillLindquist::IntermediateVars<DTYPE(data), FRAME(data)>; \\\n  template class BrillLindquist::IntermediateComputer<DTYPE(data), FRAME(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, double),\n                        (::Frame::Inertial, ::Frame::Grid))\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n}  // namespace gr::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GeneralRelativity/BrillLindquist.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GeneralRelativity/AnalyticData.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::AnalyticData {\n/*!\n * \\brief Brill Lindquist data \\cite Brill1963yv corresponding to two black\n * holes momentarily at rest\n *\n * The spatial metric is given by \\f$\\gamma_{ij} = \\psi^4 \\delta_{ij}\\f$\n * where the conformal factor is given by\n * \\f$\\psi = 1 + \\frac{m_A}{2 r_A} + \\frac{m_B}{2 r_B}\\f$ where\n * \\f$m_{A,B}\\f$ are the masses of the black holes and \\f$r_{A,B}\\f$ are the\n * positions of a point relative to the center of each black hole\n *\n * The data is time symmetric (\\f$K_{ij} = 0\\f$) and we arbitrarily choose\n * unit lapse and zero shift.\n */\nclass BrillLindquist : public AnalyticDataBase<3>, public MarkAsAnalyticData {\n public:\n  struct MassA {\n    using type = double;\n    static constexpr Options::String help = {\"Mass of the black hole A\"};\n    static type lower_bound() { return 0.; }\n  };\n  struct MassB {\n    using type = double;\n    static constexpr Options::String help = {\"Mass of the black hole B\"};\n    static type lower_bound() { return 0.; }\n  };\n  struct CenterA {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] center of the black hole A\"};\n  };\n  struct CenterB {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] center of the black hole B\"};\n  };\n  using options = tmpl::list<MassA, MassB, CenterA, CenterB>;\n  static constexpr Options::String help{\n      \"Brill-Lindquist data for two black holes\"};\n\n  BrillLindquist(double mass_a, double mass_b,\n                 const std::array<double, 3>& center_a,\n                 const std::array<double, 3>& center_b,\n                 const Options::Context& context = {});\n  explicit BrillLindquist(CkMigrateMessage* /*unused*/);\n\n  BrillLindquist() = default;\n  BrillLindquist(const BrillLindquist& /*rhs*/) = default;\n  BrillLindquist& operator=(const BrillLindquist& /*rhs*/) = default;\n  BrillLindquist(BrillLindquist&& /*rhs*/) = default;\n  BrillLindquist& operator=(BrillLindquist&& /*rhs*/) = default;\n  ~BrillLindquist() = default;\n\n  /*!\n   * \\brief Computes and returns spacetime quantities for BrillLindquist data at\n   * a specific Cartesian position\n   *\n   * \\param x Cartesian coordinates of the position at which to compute\n   * spacetime quantities\n   */\n  template <typename DataType, typename Frame, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame>& x,\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(\n        tmpl2::flat_all_v<\n            tmpl::list_contains_v<tags<DataType, Frame>, Tags>...>,\n        \"At least one of the requested tags is not supported. The requested \"\n        \"tags are listed as template parameters of the `variables` function.\");\n    IntermediateVars<DataType, Frame> cache(get_size(*x.begin()));\n    IntermediateComputer<DataType, Frame> computer(*this, x);\n    return {cache.get_var(computer, Tags{})...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /*!\n   * \\brief Return the mass of black hole A\n   */\n  SPECTRE_ALWAYS_INLINE double mass_a() const { return mass_a_; }\n  /*!\n   * \\brief Return the mass of black hole B\n   */\n  SPECTRE_ALWAYS_INLINE double mass_b() const { return mass_b_; }\n  /*!\n   * \\brief Return the center of black hole A\n   */\n  SPECTRE_ALWAYS_INLINE const std::array<double, 3>& center_a() const {\n    return center_a_;\n  }\n  /*!\n   * \\brief Return the center of black hole B\n   */\n  SPECTRE_ALWAYS_INLINE const std::array<double, 3>& center_b() const {\n    return center_b_;\n  }\n\n  /*!\n   * \\brief Tags defined for intermediates specific to BrillLindquist data\n   */\n  struct internal_tags {\n    /*!\n     * \\brief Tag for the position of a point relative to the center of\n     * black hole A\n     *\n     * \\details Defined as \\f$X_A^i = \\left(x^i - C_A^i\\right)\\f$, where\n     * \\f$C_A^i\\f$ is the Cartesian coordinates of the center of black hole A\n     * and \\f$x^i\\f$ is the Cartesian coordinates of the point where we're\n     * wanting to compute spacetime quantities.\n     */\n    template <typename DataType, typename Frame>\n    using x_minus_center_a = ::Tags::TempI<0, 3, Frame, DataType>;\n\n    /*!\n     * \\brief Tag for the radius corresponding to the position of a point\n     * relative to the center of black hole A\n     *\n     * \\details Defined as \\f$r_A = \\sqrt{\\delta_{ij} X_A^i X_A^j}\\f$, where\n     * \\f$X_A^i\\f$ is defined by `internal_tags::x_minus_center_a`.\n     */\n    template <typename DataType>\n    using r_a = ::Tags::TempScalar<1, DataType>;\n\n    /*!\n     * \\brief Tag for the position of a point relative to the center of\n     * black hole B\n     *\n     * \\details Defined as \\f$X_B^i = \\left(x^i - C_B^i\\right)\\f$, where\n     * \\f$C_B^i\\f$ is the Cartesian coordinates of the center of black hole B\n     * and \\f$x^i\\f$ is the Cartesian coordinates of the point where we're\n     * wanting to compute spacetime quantities.\n     */\n    template <typename DataType, typename Frame>\n    using x_minus_center_b = ::Tags::TempI<2, 3, Frame, DataType>;\n\n    /*!\n     * \\brief Tag for the radius corresponding to the position of a point\n     * relative to the center of black hole B\n     *\n     * \\details Defined as \\f$r_B = \\sqrt{\\delta_{ij} X_B^i X_B^j}\\f$, where\n     * \\f$X_B^i\\f$ is defined by `internal_tags::x_minus_center_b`.\n     */\n    template <typename DataType>\n    using r_b = ::Tags::TempScalar<3, DataType>;\n    /*!\n     * \\brief Tag for the conformal factor\n     *\n     * \\details Defined as \\f$\\psi = 1 + \\frac{m_A}{2 r_A} + \\frac{m_B}{2\n     * r_B}\\f$ where \\f$m_{A,B}\\f$ are the masses of the black holes and\n     * \\f$r_{A,B}\\f$ are the positions of a point relative to the center of each\n     * black hole\n     */\n    template <typename DataType>\n    using conformal_factor = ::Tags::TempScalar<4, DataType>;\n    /*!\n     * \\brief Tag for the deriatives of the conformal factor\n     *\n     * \\details Defined as \\f$d_i\\psi = -\\frac{m_A X_A^j}{2 r_A^3} \\delta_{ij}\n     *  - \\frac{m_B X_B^j}{2 r_B^3} \\delta_{ij}\\f$ where \\f$m_{A,B}\\f$ are the\n     * masses of the black holes and \\f$r_{A,B}\\f$ are the positions of a point\n     * relative to the center of each black hole.  (Note we are free to\n     * raise/lower coordinate indices with a Eucledian metric)\n     */\n    template <typename DataType, typename Frame>\n    using deriv_conformal_factor = ::Tags::Tempi<5, 3, Frame, DataType>;\n  };\n\n  template <typename DataType, typename Frame>\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, volume_dim, Frame>,\n                    tmpl::size_t<volume_dim>, Frame>;\n\n  /*!\n   * \\brief Buffer for caching computed intermediates and quantities that we do\n   * not want to recompute across the solution's implementation\n   *\n   * \\details See `internal_tags` documentation for details on what quantities\n   * the internal tags represent\n   */\n  template <typename DataType, typename Frame>\n  using CachedBuffer =\n      CachedTempBuffer<internal_tags::x_minus_center_a<DataType, Frame>,\n                       internal_tags::r_a<DataType>,\n                       internal_tags::x_minus_center_b<DataType, Frame>,\n                       internal_tags::r_b<DataType>,\n                       internal_tags::conformal_factor<DataType>,\n                       internal_tags::deriv_conformal_factor<DataType, Frame>,\n                       gr::Tags::SpatialMetric<DataType, 3, Frame>,\n                       DerivSpatialMetric<DataType, Frame>>;\n\n  /*!\n   * \\brief Computes the intermediates and quantities that we do not want to\n   * recompute across the solution's implementation\n   */\n  template <typename DataType, typename Frame>\n  class IntermediateComputer {\n   public:\n    using CachedBuffer = BrillLindquist::CachedBuffer<DataType, Frame>;\n\n    /*!\n     * \\brief Constructs a computer for spacetime quantities of a given\n     * `gr::AnalyticData::BrillLindquist` solution at at a specific\n     * Cartesian position\n     *\n     * \\param analytic_data the given `gr::AnalyticData::BrillLindquist` data\n     * \\param x Cartesian coordinates of the position at which to compute\n     * spacetime quantities\n     */\n    IntermediateComputer(const BrillLindquist& analytic_data,\n                         const tnsr::I<DataType, 3, Frame>& x);\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::x_minus_center_a`\n     */\n    void operator()(\n        gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center_a,\n        gsl::not_null<CachedBuffer*> /*cache*/,\n        internal_tags::x_minus_center_a<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the radius defined by `internal_tags::r_a`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> r_a,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::r_a<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::x_minus_center_b`\n     */\n    void operator()(\n        gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center_b,\n        gsl::not_null<CachedBuffer*> /*cache*/,\n        internal_tags::x_minus_center_b<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the radius defined by `internal_tags::r_b`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> r_b,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::r_b<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::conformal_factor`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> conformal_factor,\n                    gsl::not_null<CachedBuffer*> /*cache*/,\n                    internal_tags::conformal_factor<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::deriv_conformal_factor`\n     */\n    void operator()(\n        gsl::not_null<tnsr::i<DataType, 3, Frame>*> deriv_conformal_factor,\n        gsl::not_null<CachedBuffer*> /*cache*/,\n        internal_tags::deriv_conformal_factor<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the spatial metric\n     *\n     * \\details Computed as \\f$\\gamma_{ij} = \\delta_{ij} \\psi^4\\f$\n     * where \\f$\\psi\\f$ is the conformal factor defined by\n     * `internal_tags::conformal_factor`.\n     */\n    void operator()(gsl::not_null<tnsr::ii<DataType, 3, Frame>*> spatial_metric,\n                    gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::SpatialMetric<DataType, 3, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the spatial derivative of the spatial metric\n     *\n     * \\details Computed as \\f$\\partial_k \\gamma_{ij} = 4 \\psi^3 \\partial_k\n     * \\psi \\delta_{ij} \\f$ where \\f$\\psi\\f$ is the conformal factor defined by\n     * `internal_tags::conformal_factor`.\n     */\n    void operator()(\n        gsl::not_null<tnsr::ijj<DataType, 3, Frame>*> deriv_spatial_metric,\n        gsl::not_null<CachedBuffer*> cache,\n        DerivSpatialMetric<DataType, Frame> /*meta*/) const;\n\n   private:\n    /*!\n     * \\brief The BrillLindquist data\n     */\n    const BrillLindquist& analytic_data_;\n    /*!\n     * \\brief Cartesian coordinates of the position at which to compute\n     * spacetime quantities\n     */\n    const tnsr::I<DataType, 3, Frame>& x_;\n  };\n\n  /*!\n   * \\brief Computes and returns spacetime quantities of interest\n   */\n  template <typename DataType, typename Frame>\n  class IntermediateVars : public CachedBuffer<DataType, Frame> {\n   public:\n    using CachedBuffer = BrillLindquist::CachedBuffer<DataType, Frame>;\n    using CachedBuffer::CachedBuffer;\n    using CachedBuffer::get_var;\n\n    /*!\n     * \\brief Returns the lapse, which is 1\n     */\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::Lapse<DataType> /*meta*/);\n\n    /*!\n     * \\brief Returns the time derivative of the lapse, which is 0\n     */\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/);\n\n    /*!\n     * \\brief Returns the spatial derivative of the lapse, which is 0\n     */\n    tnsr::i<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        DerivLapse<DataType, Frame> /*meta*/);\n\n    /*!\n     * \\brief Returns the shift, which is 0\n     */\n    tnsr::I<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::Shift<DataType, 3, Frame> /*meta*/);\n\n    /*!\n     * \\brief Returns the time derivative of the shift, which is 0\n     */\n    tnsr::I<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>> /*meta*/);\n\n    /*!\n     * \\brief Returns the spatial derivative of the shift, which is 0\n     */\n    tnsr::iJ<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        DerivShift<DataType, Frame> /*meta*/);\n\n    /*!\n     * \\brief Returns the time derivative of the spatial metric, which is 0\n     */\n    tnsr::ii<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>> /*meta*/);\n\n    /*!\n     * \\brief Computes and returns the square root of the determinant of the\n     * spatial metric\n     */\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/);\n\n    /*!\n     * \\brief Computes and returns the inverse spatial metric\n     */\n    tnsr::II<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::InverseSpatialMetric<DataType, 3, Frame> /*meta*/);\n\n    /*!\n     * \\brief Computes and returns the extrinsic curvature which is 0\n     */\n    tnsr::ii<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::ExtrinsicCurvature<DataType, 3, Frame> /*meta*/);\n  };\n\n private:\n  /*!\n   * \\brief Mass of black hole A\n   */\n  double mass_a_{std::numeric_limits<double>::signaling_NaN()};\n  /*!\n   * \\brief Mass of black hole B\n   */\n  double mass_b_{std::numeric_limits<double>::signaling_NaN()};\n  /*!\n   * \\brief Center of black hole A\n   */\n  std::array<double, 3> center_a_ =\n      make_array<3>(std::numeric_limits<double>::signaling_NaN());\n  /*!\n   * \\brief Center of black hole B\n   */\n  std::array<double, 3> center_b_ =\n      make_array<3>(std::numeric_limits<double>::signaling_NaN());\n};\n\n/*!\n * \\brief Return whether two BrillLindquist data are equivalent\n */\nSPECTRE_ALWAYS_INLINE bool operator==(const BrillLindquist& lhs,\n                                      const BrillLindquist& rhs);\n\n/*!\n * \\brief Return whether two BrillLindquist data are not equivalent\n */\nSPECTRE_ALWAYS_INLINE bool operator!=(const BrillLindquist& lhs,\n                                      const BrillLindquist& rhs);\n}  // namespace gr::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GeneralRelativityAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BrillLindquist.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  BrillLindquist.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  GeneralRelativity\n  Interpolation\n  Utilities\n  )\n\nif (TARGET SpEC::Exporter)\n  spectre_target_sources(\n    ${LIBRARY}\n    PRIVATE\n    SpecInitialData.cpp\n    )\n  spectre_target_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n    SpecInitialData.hpp\n    )\n  target_link_libraries(\n    ${LIBRARY}\n    PUBLIC\n    ExternalIO\n    SpEC::Exporter\n    )\nendif()\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GeneralRelativity/SpecInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GeneralRelativity/SpecInitialData.hpp\"\n\n#include <Exporter.hpp>  // The SpEC Exporter\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/External/InterpolateFromSpec.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace gr::AnalyticData {\n\nSpecInitialData::SpecInitialData(std::string data_directory)\n    : data_directory_(std::move(data_directory)),\n      spec_exporter_(std::make_unique<spec::Exporter>(\n          sys::procs_on_node(sys::my_node()), data_directory_,\n          vars_to_interpolate_)) {}\n\nSpecInitialData::SpecInitialData(const SpecInitialData& rhs)\n    : evolution::initial_data::InitialData(rhs) {\n  *this = rhs;\n}\n\nSpecInitialData& SpecInitialData::operator=(const SpecInitialData& rhs) {\n  data_directory_ = rhs.data_directory_;\n  spec_exporter_ =\n      std::make_unique<spec::Exporter>(sys::procs_on_node(sys::my_node()),\n                                       data_directory_, vars_to_interpolate_);\n  return *this;\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nSpecInitialData::get_clone() const {\n  return std::make_unique<SpecInitialData>(*this);\n}\n\nSpecInitialData::SpecInitialData(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid SpecInitialData::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | data_directory_;\n  if (p.isUnpacking()) {\n    spec_exporter_ =\n        std::make_unique<spec::Exporter>(sys::procs_on_node(sys::my_node()),\n                                         data_directory_, vars_to_interpolate_);\n  }\n}\n\nPUP::able::PUP_ID SpecInitialData::my_PUP_ID = 0;\n\ntemplate <typename DataType>\ntuples::tagged_tuple_from_typelist<\n    typename SpecInitialData::interpolated_tags<DataType>>\nSpecInitialData::interpolate_from_spec(const tnsr::I<DataType, 3>& x) const {\n  return io::interpolate_from_spec<interpolated_tags<DataType>>(\n      make_not_null(spec_exporter_.get()), x,\n      static_cast<size_t>(sys::my_local_rank()));\n}\n\ntemplate tuples::tagged_tuple_from_typelist<\n    typename SpecInitialData::interpolated_tags<double>>\nSpecInitialData::interpolate_from_spec(const tnsr::I<double, 3>& x) const;\ntemplate tuples::tagged_tuple_from_typelist<\n    typename SpecInitialData::interpolated_tags<DataVector>>\nSpecInitialData::interpolate_from_spec(const tnsr::I<DataVector, 3>& x) const;\n\n}  // namespace gr::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GeneralRelativity/SpecInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <Exporter.hpp>  // The SpEC Exporter\n#include <memory>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GeneralRelativity/AnalyticData.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace gr::AnalyticData {\n\n/*!\n * \\brief Vacuum initial data generated by SpEC.\n *\n * This class loads numerical data written out by the SpEC initial data solver.\n * It uses the `spec::Exporter` linked in from SpEC to interpolate to arbitrary\n * grid points. The coordinates are assumed to be in SpEC's \"grid\" frame.\n * We interpolate the following quantities:\n *\n * - \"Nid_g\": spatial metric\n * - \"Nid_K\": (lower) extrinsic curvature\n * - \"Nid_N\": lapse\n * - \"Nid_Shift\": (upper) shift\n */\nclass SpecInitialData : public evolution::initial_data::InitialData,\n                        public MarkAsAnalyticData,\n                        public AnalyticDataBase<3> {\n public:\n  template <typename DataType>\n  using tags =\n      tmpl::list<gr::Tags::SpatialMetric<DataType, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataType, 3>,\n                 gr::Tags::Lapse<DataType>,\n                 gr::Tags::Shift<DataType, 3>>;\n\n  struct DataDirectory {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"Path to a directory of data produced by SpEC. The directory is \"\n        \"expected to contain 'GrDomain.input' and 'Vars*.h5' files for all the \"\n        \"subdomains in GrDomain.input.\"};\n  };\n\n  using options = tmpl::list<DataDirectory>;\n\n  static constexpr Options::String help = {\"Initial data generated by SpEC\"};\n\n  SpecInitialData() = default;\n  SpecInitialData(const SpecInitialData& rhs);\n  SpecInitialData& operator=(const SpecInitialData& rhs);\n  SpecInitialData(SpecInitialData&& /*rhs*/) = default;\n  SpecInitialData& operator=(SpecInitialData&& /*rhs*/) = default;\n  ~SpecInitialData() = default;\n\n  SpecInitialData(std::string data_directory);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit SpecInitialData(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SpecInitialData);\n  /// \\endcond\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    auto interpolated_vars = interpolate_from_spec(x);\n    return {std::move(get<Tags>(interpolated_vars))...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  /// These quantities are supported for interpolation from SpEC\n  template <typename DataType>\n  using interpolated_tags =\n      tmpl::list<gr::Tags::SpatialMetric<DataType, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataType, 3>,\n                 gr::Tags::Lapse<DataType>,\n                 gr::Tags::Shift<DataType, 3>>;\n\n  /// These are the names in SpEC datasets corresponding to the quantities above\n  static const inline std::vector<std::string> vars_to_interpolate_{\n      \"Nid_g\", \"Nid_K\", \"Nid_N\", \"Nid_Shift\"};\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<interpolated_tags<DataType>>\n  interpolate_from_spec(const tnsr::I<DataType, 3>& x) const;\n\n  std::string data_directory_{};\n\n  std::unique_ptr<spec::Exporter> spec_exporter_{nullptr};\n};\n\n}  // namespace gr::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GhGrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GhGrMhdAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  InstantiateWrappedGr.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  GeneralRelativitySolutions\n  GrMhdAnalyticData\n  PRIVATE\n  DataStructures\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GhGrMhd/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::grmhd::AnalyticData {\n/// GRMHD analytic data wrapped for GH\n\n/// \\brief List of all analytic data\nusing all_analytic_data = tmpl::list<\n    gh::Solutions::WrappedGr<::grmhd::AnalyticData::BondiHoyleAccretion>,\n    gh::Solutions::WrappedGr<::grmhd::AnalyticData::CcsnCollapse>,\n    gh::Solutions::WrappedGr<::grmhd::AnalyticData::MagnetizedFmDisk>,\n    gh::Solutions::WrappedGr<::grmhd::AnalyticData::MagnetizedTovStar>>;\n}  // namespace gh::grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GhGrMhd/InstantiateWrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.tpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nGENERATE_INSTANTIATIONS(WRAPPED_GR_INSTANTIATE,\n                        (grmhd::AnalyticData::BondiHoyleAccretion,\n                         grmhd::AnalyticData::CcsnCollapse,\n                         grmhd::AnalyticData::MagnetizedFmDisk,\n                         grmhd::AnalyticData::MagnetizedTovStar))\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GhScalarTensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GhScalarTensorAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  InstantiateWrappedGr.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  GeneralRelativitySolutions\n  ScalarTensorAnalyticData\n  PRIVATE\n  DataStructures\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GhScalarTensor/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// ScalarTensor solutions wrapped for GH\nnamespace gh::ScalarTensor::AnalyticData {\n\n/// \\brief List of all analytic solutions\nusing all_analytic_data = tmpl::list<gh::Solutions::WrappedGr<\n    ::ScalarTensor::AnalyticData::KerrSphericalHarmonic>>;\n\n}  // namespace gh::ScalarTensor::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GhScalarTensor/InstantiateWrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nGENERATE_INSTANTIATIONS(WRAPPED_GR_INSTANTIATE,\n                        (ScalarTensor::AnalyticData::KerrSphericalHarmonic))\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n\nnamespace grmhd {\n/// Base struct for properties common to all GRMHD analytic data classes\nstruct AnalyticDataBase {\n  static constexpr size_t volume_dim = 3_st;\n\n  template <typename DataType>\n  using tags =\n      tmpl::push_back<typename gr::AnalyticSolution<3>::template tags<DataType>,\n                      hydro::Tags::RestMassDensity<DataType>,\n                      hydro::Tags::ElectronFraction<DataType>,\n                      hydro::Tags::SpecificInternalEnergy<DataType>,\n                      hydro::Tags::Temperature<DataType>,\n                      hydro::Tags::Pressure<DataType>,\n                      hydro::Tags::SpatialVelocity<DataType, 3>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>,\n                      hydro::Tags::LorentzFactor<DataType>,\n                      hydro::Tags::SpecificEnthalpy<DataType>>;\n};\n\n/*!\n * \\ingroup AnalyticDataGroup\n * \\brief Holds classes implementing analytic data for the GrMhd system.\n */\nnamespace AnalyticData {}\n}  // namespace grmhd\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/BlastWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BlastWave.hpp\"\n\n#include <cmath>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nScalar<DataType> compute_piecewise(\n    const tnsr::I<DataType, 3>& x, const double inner_radius,\n    const double outer_radius, const double inner_value,\n    const double outer_value,\n    const grmhd::AnalyticData::BlastWave::Geometry geometry) {\n  DataType radius{};\n  if (geometry == grmhd::AnalyticData::BlastWave::Geometry::Cylindrical) {\n    radius = sqrt(square(get<0>(x)) + square(get<1>(x)));\n  } else {\n    radius = get(magnitude(x));\n  }\n  auto piecewise_scalar = make_with_value<Scalar<DataType>>(x, 0.0);\n\n  // piecewise_scalar should equal inner_value for r < inner_radius,\n  // should equal outer_value for r > outer_radius, and should transition\n  // in between. Here, use step_function() to compute the result\n  // in each region separately.\n  get(piecewise_scalar) += step_function(inner_radius - radius) * inner_value;\n  get(piecewise_scalar) += step_function(radius - outer_radius) * outer_value;\n  get(piecewise_scalar) +=\n      // Blaze's step_function() at 0.0 is 1.0. So to get a mask that\n      // is the inverse of the masks used above for inner_radius and\n      // outer_radius without double-counting points on the boundary,\n      // use 1.0 - stepfunction(x) instead of stepfunction(-x) here\n      (1.0 - step_function(inner_radius - radius)) *\n      (1.0 - step_function(radius - outer_radius)) *\n      exp(((-1.0 * radius + inner_radius) * log(outer_value) +\n           (radius - outer_radius) * log(inner_value)) /\n          (inner_radius - outer_radius));\n  return piecewise_scalar;\n}\n}  // namespace\n\nnamespace grmhd::AnalyticData {\n\nBlastWave::BlastWave(const double inner_radius, const double outer_radius,\n                     const double inner_density, const double outer_density,\n                     const double inner_pressure, const double outer_pressure,\n                     const std::array<double, 3>& magnetic_field,\n                     const double adiabatic_index, const Geometry geometry,\n                     const Options::Context& context)\n    : inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      inner_density_(inner_density),\n      outer_density_(outer_density),\n      inner_pressure_(inner_pressure),\n      outer_pressure_(outer_pressure),\n      magnetic_field_(magnetic_field),\n      adiabatic_index_(adiabatic_index),\n      geometry_(geometry),\n      equation_of_state_(adiabatic_index) {\n  if (inner_radius >= outer_radius) {\n    PARSE_ERROR(context,\n                \"BlastWave expects InnerRadius < OuterRadius, \"\n                \"but InnerRadius = \"\n                    << inner_radius << \" and OuterRadius = \" << outer_radius);\n  }\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> BlastWave::get_clone()\n    const {\n  return std::make_unique<BlastWave>(*this);\n}\n\nBlastWave::BlastWave(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid BlastWave::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | inner_radius_;\n  p | outer_radius_;\n  p | inner_density_;\n  p | outer_density_;\n  p | inner_pressure_;\n  p | outer_pressure_;\n  p | magnetic_field_;\n  p | adiabatic_index_;\n  p | geometry_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nBlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  return compute_piecewise(x, inner_radius_, outer_radius_, inner_density_,\n                           outer_density_, geometry_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nBlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n  return compute_piecewise(x, inner_radius_, outer_radius_, 0.1, 0.4,\n                           geometry_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nBlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  return {make_with_value<tnsr::I<DataType, 3>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nBlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> BlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return compute_piecewise(x, inner_radius_, outer_radius_, inner_pressure_,\n                           outer_pressure_, geometry_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nBlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  auto magnetic_field = make_with_value<tnsr::I<DataType, 3>>(get<0>(x), 0.0);\n  get<0>(magnetic_field) = gsl::at(magnetic_field_, 0);\n  get<1>(magnetic_field) = gsl::at(magnetic_field_, 1);\n  get<2>(magnetic_field) = gsl::at(magnetic_field_, 2);\n  return magnetic_field;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nBlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>> BlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 1.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nBlastWave::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  using density_tag = hydro::Tags::RestMassDensity<DataType>;\n  using energy_tag = hydro::Tags::SpecificInternalEnergy<DataType>;\n  using pressure_tag = hydro::Tags::Pressure<DataType>;\n  const auto data =\n      variables(x, tmpl::list<density_tag, energy_tag, pressure_tag>{});\n  return hydro::relativistic_specific_enthalpy(\n      get<density_tag>(data), get<energy_tag>(data), get<pressure_tag>(data));\n}\n\nPUP::able::PUP_ID BlastWave::my_PUP_ID = 0;\n\nbool operator==(const BlastWave& lhs, const BlastWave& rhs) {\n  return lhs.inner_radius_ == rhs.inner_radius_ and\n         lhs.outer_radius_ == rhs.outer_radius_ and\n         lhs.inner_density_ == rhs.inner_density_ and\n         lhs.outer_density_ == rhs.outer_density_ and\n         lhs.inner_pressure_ == rhs.inner_pressure_ and\n         lhs.outer_pressure_ == rhs.outer_pressure_ and\n         lhs.magnetic_field_ == rhs.magnetic_field_ and\n         lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.geometry_ == rhs.geometry_;\n}\n\nbool operator!=(const BlastWave& lhs, const BlastWave& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                      \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>                  \\\n      BlastWave::variables(const tnsr::I<DTYPE(data), 3>& x,              \\\n                           tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::DivergenceCleaningField, hydro::Tags::LorentzFactor,\n     hydro::Tags::SpecificEnthalpy))\n\n#define INSTANTIATE_VECTORS(_, data)                                         \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3>>                  \\\n      BlastWave::variables(const tnsr::I<DTYPE(data), 3>& x,                 \\\n                           tmpl::list<TAG(data) < DTYPE(data), 3>> /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace grmhd::AnalyticData\n\ntemplate <>\ngrmhd::AnalyticData::BlastWave::Geometry\nOptions::create_from_yaml<grmhd::AnalyticData::BlastWave::Geometry>::create<\n    void>(const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (\"Cylindrical\" == type_read) {\n    return grmhd::AnalyticData::BlastWave::Geometry::Cylindrical;\n  } else if (\"Spherical\" == type_read) {\n    return grmhd::AnalyticData::BlastWave::Geometry::Spherical;\n  }\n  PARSE_ERROR(\n      options.context(),\n      \"Failed to convert \\\"\"\n          << type_read\n          << \"\\\" to Geometry. Must be one of Cylindrical or Spherical.\");\n}\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/BlastWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for a cylindrical or spherical blast wave.\n *\n * This class implements analytic initial data for a cylindrical blast wave,\n * as described, e.g., in \\cite Kidder2016hev Sec. 6.2.3.\n * A uniform magnetic field threads an ideal fluid. The solution begins with\n * material at fixed (typically high) density and pressure at rest inside a\n * cylinder of radius \\f$r < r_{\\rm in}\\f$ and material at fixed (typically low)\n * density and pressure at rest in a cylindrical shell with radius\n * \\f$r > r_{\\rm out}\\f$. In the region \\f$ r_{\\rm in} < r < r_{\\rm out}\\f$,\n * the solution transitions such that the logarithms of the density and\n * pressure vary linearly. E.g., if \\f$\\rho(r < r_{\\rm in}) = \\rho_{\\rm in}\\f$\n * and \\f$\\rho(r > r_{\\rm out}) = \\rho_{\\rm out}\\f$, then\n * \\f[\n * \\log \\rho = [(r_{\\rm in} - r) \\log(\\rho_{\\rm out})\n *              + (r - r_{\\rm out}) \\log(\\rho_{\\rm in})]\n *              / (r_{\\rm in} - r_{\\rm out}).\n * \\f]\n * Note that the cylinder's axis is the \\f$z\\f$ axis. To evolve this analytic\n * initial data, use a cubic or cylindrical domain with periodic boundary\n * conditions applied to the outer boundaries whose normals are parallel or\n * antiparallel to the z axis. In the transverse (e.g., x and y) dimensions, the\n * domain should be large enough that the blast wave doesn't reach the boundary\n * at the final time. E.g., if `InnerRadius = 0.8`, `OuterRadius = 1.0`, and\n * the final time is 4.0, a good domain extends from `(x,y)=(-6.0, -6.0)` to\n * `(x,y)=(6.0, 6.0)`.\n *\n * An analogous problem with spherical geometry has also been used\n * \\cite CerdaDuran2007 \\cite CerdaDuran2008 \\cite Cipolletta2019. The magnetic\n * field is chosen to be in the z-direction instead of the x-direction.\n */\nclass BlastWave : public evolution::initial_data::InitialData,\n                  public MarkAsAnalyticData,\n                  public AnalyticDataBase,\n                  public hydro::TemperatureInitialization<BlastWave> {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  enum class Geometry { Cylindrical, Spherical };\n\n  /// Inside InnerRadius, density is InnerDensity.\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Inside InnerRadius, density is InnerDensity.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Outside OuterRadius, density is OuterDensity.\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Outside OuterRadius, density is OuterDensity.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Density at radii less than InnerRadius.\n  struct InnerDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Density at radii less than InnerRadius.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Density at radii greater than OuterRadius.\n  struct OuterDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Density at radii greater than OuterRadius.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Pressure at radii less than InnerRadius.\n  struct InnerPressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Pressure at radii less than InnerRadius.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Pressure at radii greater than OuterRadius.\n  struct OuterPressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Pressure at radii greater than OuterRadius.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// The x,y,z components of the uniform magnetic field threading the matter.\n  struct MagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The x,y,z components of the uniform magnetic field.\"};\n  };\n  /// The adiabatic index of the ideal fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the ideal fluid.\"};\n    static type lower_bound() { return 1.0; }\n  };\n  /// The geometry of the blast wave, i.e. Cylindrical or Spherical.\n  struct GeometryOption {\n    static std::string name() { return \"Geometry\"; }\n    using type = Geometry;\n    static constexpr Options::String help = {\n        \"The geometry of the blast wave, i.e. Cylindrical or Spherical.\"};\n  };\n\n  using options = tmpl::list<InnerRadius, OuterRadius, InnerDensity,\n                             OuterDensity, InnerPressure, OuterPressure,\n                             MagneticField, AdiabaticIndex, GeometryOption>;\n\n  static constexpr Options::String help = {\n      \"Cylindrical or spherical blast wave analytic initial data.\"};\n\n  BlastWave() = default;\n  BlastWave(const BlastWave& /*rhs*/) = default;\n  BlastWave& operator=(const BlastWave& /*rhs*/) = default;\n  BlastWave(BlastWave&& /*rhs*/) = default;\n  BlastWave& operator=(BlastWave&& /*rhs*/) = default;\n  ~BlastWave() override = default;\n\n  BlastWave(double inner_radius, double outer_radius, double inner_density,\n            double outer_density, double inner_pressure, double outer_pressure,\n            const std::array<double, 3>& magnetic_field, double adiabatic_index,\n            Geometry geometry, const Options::Context& context = {});\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit BlastWave(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BlastWave);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve the GRMHD variables at a given position.\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n\n  /// @}\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<tmpl::list_contains_v<\n                gr::analytic_solution_tags<3, DataType>, Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n  const EquationsOfState::IdealFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  double inner_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double outer_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double inner_density_ = std::numeric_limits<double>::signaling_NaN();\n  double outer_density_ = std::numeric_limits<double>::signaling_NaN();\n  double inner_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double outer_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  Geometry geometry_ = Geometry::Cylindrical;\n  EquationsOfState::IdealFluid<true> equation_of_state_{};\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  friend bool operator==(const BlastWave& lhs, const BlastWave& rhs);\n\n  friend bool operator!=(const BlastWave& lhs, const BlastWave& rhs);\n};\n\n}  // namespace grmhd::AnalyticData\n\n/// \\cond\ntemplate <>\nstruct Options::create_from_yaml<grmhd::AnalyticData::BlastWave::Geometry> {\n  template <typename Metavariables>\n  static grmhd::AnalyticData::BlastWave::Geometry create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\ngrmhd::AnalyticData::BlastWave::Geometry\nOptions::create_from_yaml<grmhd::AnalyticData::BlastWave::Geometry>::create<\n    void>(const Options::Option& options);\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace grmhd::AnalyticData {\n\nBondiHoyleAccretion::BondiHoyleAccretion(const double bh_mass,\n                                         const double bh_dimless_spin,\n                                         const double rest_mass_density,\n                                         const double flow_speed,\n                                         const double magnetic_field_strength,\n                                         const double polytropic_constant,\n                                         const double polytropic_exponent)\n    : bh_mass_(bh_mass),\n      bh_spin_a_(bh_mass * bh_dimless_spin),\n      rest_mass_density_(rest_mass_density),\n      flow_speed_(flow_speed),\n      magnetic_field_strength_(magnetic_field_strength),\n      polytropic_constant_(polytropic_constant),\n      polytropic_exponent_(polytropic_exponent),\n      equation_of_state_{polytropic_constant_, polytropic_exponent_},\n      background_spacetime_{\n          bh_mass, {{0.0, 0.0, bh_dimless_spin}}, {{0.0, 0.0, 0.0}}},\n      kerr_schild_coords_{bh_mass, bh_dimless_spin} {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nBondiHoyleAccretion::get_clone() const {\n  return std::make_unique<BondiHoyleAccretion>(*this);\n}\n\nBondiHoyleAccretion::BondiHoyleAccretion(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nvoid BondiHoyleAccretion::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | bh_mass_;\n  p | bh_spin_a_;\n  p | rest_mass_density_;\n  p | flow_speed_;\n  p | magnetic_field_strength_;\n  p | polytropic_constant_;\n  p | polytropic_exponent_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n  p | kerr_schild_coords_;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3, Frame::NoFrame> BondiHoyleAccretion::spatial_velocity(\n    const DataType& r_squared, const DataType& cos_theta,\n    const DataType& sin_theta) const {\n  auto result =\n      make_with_value<tnsr::I<DataType, 3, Frame::NoFrame>>(r_squared, 0.0);\n\n  const DataType sigma = r_squared + square(bh_spin_a_) * square(cos_theta);\n  get<0>(result) = flow_speed_ * cos_theta /\n                   sqrt(1.0 + 2.0 * bh_mass_ * sqrt(r_squared) / sigma);\n  get<1>(result) = -flow_speed_ * sin_theta / sqrt(sigma);\n  // get<2>(result) is identically zero\n\n  return result;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3, Frame::NoFrame> BondiHoyleAccretion::magnetic_field(\n    const DataType& r_squared, const DataType& cos_theta,\n    const DataType& sin_theta) const {\n  const double a_squared = bh_spin_a_ * bh_spin_a_;\n  const DataType cos_theta_squared = square(cos_theta);\n  const DataType two_m_r = 2.0 * bh_mass_ * sqrt(r_squared);\n\n  // The square root of the determinant of the spatial metric is proportional to\n  // sin(theta) squared. That factor cancels out after multiplying by Faraday.\n  DataType sigma = r_squared + a_squared * cos_theta_squared;\n  const DataType prefactor =\n      magnetic_field_strength_ / sqrt(sigma * (sigma + two_m_r));\n\n  auto result =\n      make_with_value<tnsr::I<DataType, 3, Frame::NoFrame>>(r_squared, 0.0);\n\n  // Redefine sigma to save an allocation.\n  sigma = 1.0 / square(sigma);\n  get<0>(result) = prefactor *\n                   (r_squared - two_m_r + a_squared +\n                    sigma * two_m_r * (square(r_squared) - square(a_squared))) *\n                   cos_theta;\n\n  get<1>(result) =\n      -prefactor *\n      (sqrt(r_squared) + bh_mass_ * a_squared * sigma *\n                             (r_squared - a_squared * cos_theta_squared) *\n                             (1.0 + cos_theta_squared)) *\n      sin_theta;\n\n  get<2>(result) = bh_spin_a_ * prefactor *\n                   (1.0 + sigma * two_m_r * (r_squared - a_squared)) *\n                   cos_theta;\n\n  return result;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, rest_mass_density_)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n\n  return {make_with_value<Scalar<DataType>>(x, 0.1)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  const DataType r_squared = get(kerr_schild_coords_.r_coord_squared(x));\n  return kerr_schild_coords_.cartesian_from_spherical_ks(\n      spatial_velocity(r_squared, DataType{get<2>(x) / sqrt(r_squared)},\n                       DataType{sqrt((square(get<0>(x)) + square(get<1>(x))) /\n                                     (r_squared + square(bh_spin_a_)))}),\n      x);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Temperature<DataType>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const {\n  return equation_of_state_.temperature_from_density(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return equation_of_state_.pressure_from_density(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  const DataType r_squared = get(kerr_schild_coords_.r_coord_squared(x));\n  return kerr_schild_coords_.cartesian_from_spherical_ks(\n      magnetic_field(r_squared, DataType{get<2>(x) / sqrt(r_squared)},\n                     DataType{sqrt((square(get<0>(x)) + square(get<1>(x))) /\n                                   (r_squared + square(bh_spin_a_)))}),\n      x);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(\n      x, 1.0 / sqrt(1.0 - square(flow_speed_)))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nBondiHoyleAccretion::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  using density_tag = hydro::Tags::RestMassDensity<DataType>;\n  using energy_tag = hydro::Tags::SpecificInternalEnergy<DataType>;\n  using pressure_tag = hydro::Tags::Pressure<DataType>;\n  const auto data =\n      variables(x, tmpl::list<density_tag, energy_tag, pressure_tag>{});\n  return hydro::relativistic_specific_enthalpy(\n      get<density_tag>(data), get<energy_tag>(data), get<pressure_tag>(data));\n}\n\nPUP::able::PUP_ID BondiHoyleAccretion::my_PUP_ID = 0;\n\nbool operator==(const BondiHoyleAccretion& lhs,\n                const BondiHoyleAccretion& rhs) {\n  // There is no comparison operator for the `equation_of_state` and the\n  // `background_spacetime`, but should be okay as the `bh_mass`s,\n  // `bh_dimless_spin`s, `polytropic_exponent`s and `polytropic_constant`s are\n  // compared.\n  return lhs.bh_mass_ == rhs.bh_mass_ and lhs.bh_spin_a_ == rhs.bh_spin_a_ and\n         lhs.rest_mass_density_ == rhs.rest_mass_density_ and\n         lhs.flow_speed_ == rhs.flow_speed_ and\n         lhs.magnetic_field_strength_ == rhs.magnetic_field_strength_ and\n         lhs.polytropic_constant_ == rhs.polytropic_constant_ and\n         lhs.polytropic_exponent_ == rhs.polytropic_exponent_;\n}\n\nbool operator!=(const BondiHoyleAccretion& lhs,\n                const BondiHoyleAccretion& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                     \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>> \\\n      BondiHoyleAccretion::variables(                    \\\n          const tnsr::I<DTYPE(data), 3>& x,              \\\n          tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::DivergenceCleaningField, hydro::Tags::LorentzFactor,\n     hydro::Tags::SpecificEnthalpy, hydro::Tags::Temperature))\n\n#define INSTANTIATE_VECTORS(_, data)                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3>> \\\n      BondiHoyleAccretion::variables(                       \\\n          const tnsr::I<DTYPE(data), 3>& x,                 \\\n          tmpl::list<TAG(data) < DTYPE(data), 3>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrSchildCoords.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for axially symmetric Bondi-Hoyle accretion.\n *\n * In the context of studying Bondi-Hoyle accretion, i.e. non-spherical\n * accretion on to a Kerr black hole moving relative to a gas cloud, this class\n * implements the method proposed by \\cite Font1998 to initialize the GRMHD\n * variables. The fluid quantities are initialized with their (constant) values\n * far from the black hole, e.g. \\f$\\rho = \\rho_\\infty\\f$. Here we assume a\n * polytropic equation of state, so only the rest mass density, as well as the\n * polytropic constant and the polytropic exponent, are provided as inputs.\n * The spatial velocity is initialized using a field that ensures that the\n * injected gas reproduces a continuous parallel wind at large distances.\n * The direction of this flow is chosen to be along the black hole spin.\n * In Kerr (or \"spherical Kerr-Schild\", see gr::KerrSchildCoords) coordinates,\n *\n * \\f{align*}\n * v^r &= \\frac{1}{\\sqrt{\\gamma_{rr}}}v_\\infty \\cos\\theta\\\\\n * v^\\theta &= -\\frac{1}{\\sqrt{\\gamma_{\\theta\\theta}}}v_\\infty \\sin\\theta\\\\\n * v^\\phi &= 0.\n * \\f}\n *\n * where \\f$\\gamma_{ij} = g_{ij}\\f$ is the spatial metric, and \\f$v_\\infty\\f$\n * is the flow speed far from the black hole. Note that\n * \\f$v_\\infty^2 = v_i v^i\\f$. Finally, following the work by \\cite Penner2011,\n * the magnetic field is initialized using Wald's solution to Maxwell's\n * equations in Kerr black hole spacetime. In Kerr (\"spherical\n * Kerr-Schild\") coordinates, the spatial components of the Faraday tensor read\n *\n * \\f{align*}\n * F_{r\\theta} &= a B_0 \\left[1 + \\frac{2Mr}{\\Sigma^2}(r^2 - a^2)\\right]\n * \\sin\\theta\\cos\\theta\\\\\n * F_{\\theta\\phi} &= B_0\\left[\\Delta +\n * \\frac{2Mr}{\\Sigma^2}(r^4 - a^4)\\right]\\sin\\theta\\cos\\theta\\\\\n * F_{\\phi r} &= - B_0\\left[r + \\frac{M a^2}{\\Sigma^2}\n * (r^2 - a^2\\cos^2\\theta)(1 + \\cos^2\\theta)\\right]\\sin^2\\theta.\n * \\f}\n *\n * where \\f$\\Sigma = r^2 + a^2\\cos^2\\theta\\f$ and \\f$\\Delta = r^2 - 2Mr +\n * a^2\\f$. The associated Eulerian magnetic field is\n *\n * \\f{align*}\n * B^r = \\frac{F_{\\theta\\phi}}{\\sqrt\\gamma},\\quad\n * B^\\theta = \\frac{F_{\\phi r}}{\\sqrt\\gamma},\\quad\n * B^\\phi = \\frac{F_{r\\theta}}{\\sqrt\\gamma}.\n * \\f}\n *\n * where \\f$\\gamma = \\text{det}(\\gamma_{ij})\\f$. Wald's solution reproduces a\n * uniform magnetic field far from the black hole.\n */\nclass BondiHoyleAccretion : public virtual evolution::initial_data::InitialData,\n                            public MarkAsAnalyticData,\n                            public AnalyticDataBase {\n public:\n  using equation_of_state_type = EquationsOfState::PolytropicFluid<true>;\n\n  /// The mass of the black hole, \\f$M\\f$.\n  struct BhMass {\n    using type = double;\n    static constexpr Options::String help = {\"The mass of the black hole.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// The dimensionless black hole spin, \\f$a_* = a/M\\f$.\n  struct BhDimlessSpin {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The dimensionless black hole spin.\"};\n    static type lower_bound() { return -1.0; }\n    static type upper_bound() { return 1.0; }\n  };\n  /// The rest mass density of the fluid far from the black hole.\n  struct RestMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The asymptotic rest mass density.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// The magnitude of the spatial velocity far from the black hole.\n  struct FlowSpeed {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The magnitude of the asymptotic flow velocity.\"};\n  };\n  /// The strength of the magnetic field.\n  struct MagFieldStrength {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The strength of the magnetic field.\"};\n  };\n  /// The polytropic constant of the fluid.\n  struct PolytropicConstant {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The polytropic constant of the fluid.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// The polytropic exponent of the fluid.\n  struct PolytropicExponent {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The polytropic exponent of the fluid.\"};\n    static type lower_bound() { return 1.0; }\n  };\n\n  using options =\n      tmpl::list<BhMass, BhDimlessSpin, RestMassDensity, FlowSpeed,\n                 MagFieldStrength, PolytropicConstant, PolytropicExponent>;\n\n  static constexpr Options::String help = {\n      \"Axially symmetric accretion on to a Kerr black hole.\"};\n\n  BondiHoyleAccretion() = default;\n  BondiHoyleAccretion(const BondiHoyleAccretion& /*rhs*/) = default;\n  BondiHoyleAccretion& operator=(const BondiHoyleAccretion& /*rhs*/) = default;\n  BondiHoyleAccretion(BondiHoyleAccretion&& /*rhs*/) = default;\n  BondiHoyleAccretion& operator=(BondiHoyleAccretion&& /*rhs*/) = default;\n  ~BondiHoyleAccretion() override = default;\n\n  BondiHoyleAccretion(double bh_mass, double bh_dimless_spin,\n                      double rest_mass_density, double flow_speed,\n                      double magnetic_field_strength,\n                      double polytropic_constant, double polytropic_exponent);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit BondiHoyleAccretion(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BondiHoyleAccretion);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve hydro variable at `x`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n  /// @}\n\n  /// Retrieve a collection of hydro variables at `x`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables at `x`\n  template <typename DataType, typename Tag,\n            Requires<not tmpl::list_contains_v<\n                tmpl::push_back<hydro::grmhd_tags<DataType>,\n                                hydro::Tags::SpecificEnthalpy<DataType>>,\n                Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return {std::move(get<Tag>(background_spacetime_.variables(\n        x, dummy_time, gr::Solutions::KerrSchild::tags<DataType>{})))};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n  const EquationsOfState::PolytropicFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n private:\n  friend bool operator==(const BondiHoyleAccretion& lhs,\n                         const BondiHoyleAccretion& rhs);\n\n  // compute the spatial velocity in spherical Kerr-Schild coordinates\n  template <typename DataType>\n  tnsr::I<DataType, 3, Frame::NoFrame> spatial_velocity(\n      const DataType& r_squared, const DataType& cos_theta,\n      const DataType& sin_theta) const;\n  // compute the magnetic field in spherical Kerr-Schild coordinates\n  template <typename DataType>\n  tnsr::I<DataType, 3, Frame::NoFrame> magnetic_field(\n      const DataType& r_squared, const DataType& cos_theta,\n      const DataType& sin_theta) const;\n\n  double bh_mass_ = std::numeric_limits<double>::signaling_NaN();\n  double bh_spin_a_ = std::numeric_limits<double>::signaling_NaN();\n  double rest_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double flow_speed_ = std::numeric_limits<double>::signaling_NaN();\n  double magnetic_field_strength_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double polytropic_constant_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent_ = std::numeric_limits<double>::signaling_NaN();\n  EquationsOfState::PolytropicFluid<true> equation_of_state_{};\n  gr::Solutions::KerrSchild background_spacetime_{};\n  gr::KerrSchildCoords kerr_schild_coords_{};\n};\n\nbool operator!=(const BondiHoyleAccretion& lhs, const BondiHoyleAccretion& rhs);\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GrMhdAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BlastWave.cpp\n  BondiHoyleAccretion.cpp\n  CcsnCollapse.cpp\n  KhInstability.cpp\n  MagneticFieldLoop.cpp\n  MagneticRotor.cpp\n  MagnetizedFmDisk.cpp\n  MagnetizedTovStar.cpp\n  OrszagTangVortex.cpp\n  PolarMagnetizedFmDisk.cpp\n  RiemannProblem.cpp\n  SlabJet.cpp\n  SphericalTorus.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  BlastWave.hpp\n  BondiHoyleAccretion.hpp\n  CcsnCollapse.hpp\n  KhInstability.hpp\n  MagneticFieldLoop.hpp\n  MagneticRotor.hpp\n  MagnetizedFmDisk.hpp\n  MagnetizedTovStar.hpp\n  OrszagTangVortex.hpp\n  PolarMagnetizedFmDisk.hpp\n  RiemannProblem.hpp\n  SlabJet.hpp\n  SphericalTorus.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Hydro\n  Options\n  RelativisticEulerSolutions\n  Serialization\n  )\n\nif (TARGET SpEC::Exporter)\n  spectre_target_sources(\n    ${LIBRARY}\n    PRIVATE\n    SpecInitialData.cpp\n    )\n  spectre_target_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n    SpecInitialData.hpp\n    )\n  target_link_libraries(\n    ${LIBRARY}\n    PUBLIC\n    GeneralRelativityAnalyticData\n    SpEC::Exporter\n    )\nendif()\n\nif (TARGET FUKA::Exporter)\n  spectre_target_sources(\n    ${LIBRARY}\n    PRIVATE\n    FukaInitialData.cpp\n    )\n  spectre_target_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n    FukaInitialData.hpp\n    )\n  target_link_libraries(\n    ${LIBRARY}\n    PUBLIC\n    ExternalIO\n    )\nendif()\n\nadd_subdirectory(InitialMagneticFields)\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <fstream>\n#include <limits>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/EosTable.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/Interpolation/PolynomialInterpolation.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::AnalyticData {\nnamespace detail {\nProgenitorProfile::ProgenitorProfile(const std::string& filename) {\n  if (not file_system::check_if_file_exists(filename)) {\n    ERROR(\"Data file not found \" << filename);\n  }\n\n  std::ifstream prog_file(filename);\n  std::string header_line;\n  // Loads header lines (including license information) into header_line.\n  for (int header_index = 0; header_index < 4; header_index++) {\n    std::getline(prog_file, header_line);\n  }\n\n  // Read as integer from file.\n  double num_radial_points = -1.0;\n  double zero_dummy = 0.0;\n\n  // Zero dummy is needed b/c of extra columns in\n  // 1st line of txt profile.\n  prog_file >> num_radial_points >> zero_dummy >> zero_dummy >> zero_dummy >>\n      zero_dummy >> zero_dummy >> zero_dummy;\n\n  ASSERT(num_radial_points > 0.0, \"Must have radial points > 0, not \"\n                                      << num_radial_points\n                                      << \".  Check proper file readin. \\n\");\n\n  num_radial_points_ = static_cast<size_t>(num_radial_points);\n  num_angular_points_ = 1;\n  num_grid_points_ = num_radial_points_;\n\n  radius_.destructive_resize(num_grid_points_);\n  rest_mass_density_.destructive_resize(num_grid_points_);\n  temperature_.destructive_resize(num_grid_points_);\n  fluid_velocity_.destructive_resize(num_grid_points_);\n  electron_fraction_.destructive_resize(num_grid_points_);\n  chi_.destructive_resize(num_grid_points_);\n  metric_potential_.destructive_resize(num_grid_points_);\n\n  // Loop over data stored in text file.\n  for (size_t i = 0; i < num_radial_points_; i++) {\n    prog_file >> radius_[i] >> rest_mass_density_[i] >> temperature_[i] >>\n        fluid_velocity_[i] >> electron_fraction_[i] >> chi_[i] >>\n        metric_potential_[i];\n  }\n  radius_ *= c2g_length_;\n  rest_mass_density_ *= c2g_dens_;\n  // Kelvin to MeV conversion.\n  const double K_to_MeV = 8.621738e-11;\n  temperature_ = temperature_ * K_to_MeV;\n  fluid_velocity_ /= speed_of_light_cgs_;\n  fluid_velocity_ = abs(fluid_velocity_);\n  maximum_radius_ = max(radius_);\n}\n\nvoid ProgenitorProfile::pup(PUP::er& p) {\n  p | maximum_radius_;\n  p | max_density_ratio_for_linear_interpolation_;\n\n  p | num_radial_points_;\n  p | num_angular_points_;\n  p | num_grid_points_;\n\n  p | radius_;\n  p | rest_mass_density_;\n  p | temperature_;\n  p | fluid_velocity_;\n  p | electron_fraction_;\n  p | chi_;\n  p | metric_potential_;\n}\n\nnamespace {\ntemplate <size_t StencilSize>\nvoid hydro_interpolation(const gsl::not_null<double*> target_var,\n                         const double max_var_ratio_for_linear_interpolation,\n                         const double target_coord,\n                         const gsl::span<const double>& var_stencil,\n                         const gsl::span<const double>& coords) {\n  double error_y = 0.0;\n  if (const auto min_max_iters =\n          std::minmax_element(var_stencil.begin(), var_stencil.end());\n      *min_max_iters.second >\n      max_var_ratio_for_linear_interpolation * *min_max_iters.first) {\n    std::array<double, 2> coord_linear{\n        {std::numeric_limits<double>::signaling_NaN(),\n         std::numeric_limits<double>::signaling_NaN()}};\n    std::array<double, 2> var_linear{\n        {std::numeric_limits<double>::signaling_NaN(),\n         std::numeric_limits<double>::signaling_NaN()}};\n    for (size_t i = 0; i < StencilSize - 1; ++i) {\n      // Grab nearest neighbor coordinates and associated variable.\n      if (coords[i] <= target_coord and target_coord <= coords[i + 1]) {\n        coord_linear[0] = coords[i];\n        coord_linear[1] = coords[i + 1];\n        var_linear[0] = gsl::at(var_stencil, i);\n        var_linear[1] = gsl::at(var_stencil, i + 1);\n        break;\n      }\n    }\n    intrp::polynomial_interpolation<1>(\n        target_var, make_not_null(&error_y), target_coord,\n        gsl::make_span(var_linear.data(), var_linear.size()),\n        gsl::make_span(coord_linear.data(), coord_linear.size()));\n  } else {\n    intrp::polynomial_interpolation<StencilSize - 1>(\n        target_var, make_not_null(&error_y), target_coord, var_stencil, coords);\n  }\n}\n}  // namespace\n\n// Input are SpECTRE coords (target_radius & target_cos_theta).\nstd::array<double, 6> ProgenitorProfile::interpolate(\n    const double target_radius, const double target_cos_theta,\n    const bool interpolate_hydro_vars) const {\n  constexpr size_t stencil_size = 4;\n  ASSERT(target_radius >= 0.0,\n         \"The target radius must be positive, not \" << target_radius);\n  if (UNLIKELY(target_radius > maximum_radius_)) {\n    ERROR(\"Requested radius \" << target_radius\n                              << \" is greater than what the  \"\n                                 \"progenitor input file contains, \"\n                              << maximum_radius_);\n  }\n  using std::abs;\n\n  const auto radius_index = static_cast<size_t>(std::distance(\n      radius_.begin(),\n      std::lower_bound(radius_.begin(), radius_.end(), target_radius)));\n\n  if (const size_t grid_index = num_angular_points_ * radius_index;\n      UNLIKELY(grid_index > num_grid_points_)) {\n    ERROR(\"grid_index \" << grid_index\n                        << \" is larger than the number of grid points \"\n                        << num_grid_points_\n                        << \"\\nnumber of angular points: \" << num_angular_points_\n                        << \"\\nnumber of radial points: \" << num_radial_points_\n                        << \"\\nradius index: \" << radius_index);\n  }\n\n  if (UNLIKELY(radius_index >= num_radial_points_)) {\n    ERROR(\n        \"radius_index \" << radius_index\n                        << \" is larger than the number of radial grid points: \"\n                        << num_radial_points_);\n  }\n\n  // The radial index at which the stencil lies on the progenitor grid.\n  const size_t radial_stencil_index = static_cast<size_t>(std::clamp(\n      static_cast<int>(radius_index) - static_cast<int>(stencil_size) / 2, 0,\n      static_cast<int>(num_radial_points_ - stencil_size)));\n\n  const size_t radial_index = radial_stencil_index;\n  // Radial interpolation\n  double error_y = 0.0;\n  // Variables to be output after radial interpolation\n  double target_rest_mass_density{std::numeric_limits<double>::signaling_NaN()};\n  double target_temperature{std::numeric_limits<double>::signaling_NaN()};\n  double target_fluid_velocity{std::numeric_limits<double>::signaling_NaN()};\n  double target_electron_fraction{std::numeric_limits<double>::signaling_NaN()};\n  double target_chi{std::numeric_limits<double>::signaling_NaN()};\n  double target_metric_potential{std::numeric_limits<double>::signaling_NaN()};\n\n  const auto radius_span = gsl::make_span(&radius_[radial_index], stencil_size);\n\n  // Interpolate along the radial stencil.\n  if (interpolate_hydro_vars) {\n    hydro_interpolation<stencil_size>(\n        make_not_null(&target_rest_mass_density),\n        max_density_ratio_for_linear_interpolation_, target_radius,\n        gsl::make_span(&rest_mass_density_[radial_index], stencil_size),\n        radius_span);\n    hydro_interpolation<stencil_size>(\n        make_not_null(&target_temperature),\n        // Note: we use max density ratio for temperature.\n        max_density_ratio_for_linear_interpolation_, target_radius,\n        gsl::make_span(&temperature_[radial_index], stencil_size), radius_span);\n    hydro_interpolation<stencil_size>(\n        make_not_null(&target_fluid_velocity),\n        // Note: we use max density ratio for velocity\n        max_density_ratio_for_linear_interpolation_, target_radius,\n        gsl::make_span(&fluid_velocity_[radial_index], stencil_size),\n        radius_span);\n    hydro_interpolation<stencil_size>(\n        make_not_null(&target_electron_fraction),\n        // Note: we use max density ratio for Ye.\n        max_density_ratio_for_linear_interpolation_, target_radius,\n        gsl::make_span(&electron_fraction_[radial_index], stencil_size),\n        radius_span);\n  }\n\n  // Metric variables\n  intrp::polynomial_interpolation<stencil_size - 1>(\n      make_not_null(&target_chi), make_not_null(&error_y), target_radius,\n      gsl::make_span(&chi_[radial_index], stencil_size), radius_span);\n  intrp::polynomial_interpolation<stencil_size - 1>(\n      make_not_null(&target_metric_potential), make_not_null(&error_y),\n      target_radius,\n      gsl::make_span(&metric_potential_[radial_index], stencil_size),\n      radius_span);\n\n  if (interpolate_hydro_vars) {\n    if (UNLIKELY(target_rest_mass_density < 0.0)) {\n      ERROR(\n          \"Failed to interpolate to (r, cos(theta)) = (\"\n          << target_radius << ',' << target_cos_theta\n          << \") because the resulting density is negative: \"\n          << target_rest_mass_density\n          << \". The interpolation should select linear interpolation to avoid \"\n             \"generating negative densities. Please file an issue so this bug \"\n             \"can get fixed.\");\n    }\n  }\n\n  return {\n      {interpolate_hydro_vars ? target_rest_mass_density\n                              : std::numeric_limits<double>::signaling_NaN(),\n       interpolate_hydro_vars ? target_temperature\n                              : std::numeric_limits<double>::signaling_NaN(),\n       interpolate_hydro_vars ? target_fluid_velocity\n                              : std::numeric_limits<double>::signaling_NaN(),\n       interpolate_hydro_vars ? target_electron_fraction\n                              : std::numeric_limits<double>::signaling_NaN(),\n       target_chi, target_metric_potential}};\n}\n\nnamespace {\ntemplate <typename DataType>\nvoid compute_angular_coordinates(\n    const gsl::not_null<DataType*> radius,\n    const gsl::not_null<DataType*> cos_theta,\n    const gsl::not_null<DataType*> sin_theta,\n    const gsl::not_null<DataType*> phi,\n    const tnsr::I<DataType, 3, Frame::Inertial>& coords) {\n  const size_t num_points = get_size(get<0>(coords));\n  *radius = get(magnitude(coords));\n  if constexpr (not std::is_fundamental_v<DataType>) {\n    cos_theta->destructive_resize(num_points);\n    sin_theta->destructive_resize(num_points);\n    phi->destructive_resize(num_points);\n  }\n  for (size_t i = 0; i < num_points; ++i) {\n    if (get_element(*radius, i) < 1.e-12) {\n      // Need to pick an angle when r~0\n      get_element(*cos_theta, i) = 0.5;\n      get_element(*phi, i) = 0.0;\n    } else {\n      using std::atan2;\n      get_element(*cos_theta, i) =\n          get_element(get<2>(coords), i) / get_element(*radius, i);\n      get_element(*phi, i) =\n          atan2(get_element(get<1>(coords), i), get_element(get<0>(coords), i));\n    }\n  }\n  *sin_theta = sqrt(1.0 - *cos_theta * *cos_theta);\n}\n}  // namespace\n}  // namespace detail\n\nCcsnCollapse::CcsnCollapse(std::string progenitor_filename,\n                           double central_angular_velocity,\n                           double diff_rot_parameter,\n                           double max_dens_ratio_interp,\n                           const std::string& eos_filename,\n                           const std::string& eos_subfilename)\n    : progenitor_filename_(std::move(progenitor_filename)),\n      prog_data_{progenitor_filename_},\n      central_angular_velocity_(central_angular_velocity),\n      inv_diff_rot_parameter_(1.0 / diff_rot_parameter) {\n  CcsnCollapse::prog_data_.set_dens_ratio(max_dens_ratio_interp);\n  const h5::H5File<h5::AccessType::ReadOnly> eos_file{eos_filename};\n  const auto& compose_eos = eos_file.get<h5::EosTable>(eos_subfilename);\n\n  equation_of_state_.initialize(compose_eos);\n}\n\nCcsnCollapse::CcsnCollapse(CkMigrateMessage* msg) : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> CcsnCollapse::get_clone()\n    const {\n  return std::make_unique<CcsnCollapse>(*this);\n}\n\nvoid CcsnCollapse::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | progenitor_filename_;\n  p | prog_data_;\n  p | equation_of_state_;\n  p | central_angular_velocity_;\n  p | inv_diff_rot_parameter_;\n}\n\n// Computes trig quantities based on SpECTRE grid data.\ntemplate <typename DataType>\nCcsnCollapse::IntermediateVariables<DataType>::IntermediateVariables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& in_coords,\n    const double in_delta_r)\n    : coords(in_coords),\n      radius(get_size(get<0>(coords))),\n      phi(get_size(radius)),\n      cos_theta(get_size(radius)),\n      sin_theta(get_size(radius)),\n      delta_r(in_delta_r) {\n  detail::compute_angular_coordinates(\n      make_not_null(&radius), make_not_null(&cos_theta),\n      make_not_null(&sin_theta), make_not_null(&phi), coords);\n}\n\ntemplate <typename DataType>\nCcsnCollapse::IntermediateVariables<DataType>::IntermediateVariables::\n    MetricData::MetricData(const size_t num_points)\n    : chi(make_with_value<DataType>(\n          num_points, std::numeric_limits<double>::signaling_NaN())),\n      metric_potential(make_with_value<DataType>(\n          num_points, std::numeric_limits<double>::signaling_NaN())) {}\n\ntemplate <typename DataType>\nvoid CcsnCollapse::interpolate_vars_if_necessary(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  if (vars->rest_mass_density.has_value()) {\n    return;\n  }\n  const size_t num_points = get_size(vars->radius);\n\n  vars->rest_mass_density = make_with_value<DataType>(num_points, 0.0);\n  vars->temperature = make_with_value<DataType>(num_points, 0.0);\n  vars->fluid_velocity = make_with_value<DataType>(num_points, 0.0);\n  vars->electron_fraction = make_with_value<DataType>(num_points, 0.0);\n  vars->metric_data =\n      typename IntermediateVariables<DataType>::MetricData(num_points);\n  for (size_t i = 0; i < num_points; ++i) {\n    const std::array<double, 6> interpolated_data = prog_data_.interpolate(\n        get_element(vars->radius, i), get_element(vars->cos_theta, i), true);\n\n    // Assign SpECTRE data based on interpolated data (right)\n    get_element(vars->rest_mass_density.value(), i) = interpolated_data[0];\n    get_element(vars->temperature.value(), i) = interpolated_data[1];\n    get_element(vars->fluid_velocity.value(), i) = interpolated_data[2];\n    get_element(vars->electron_fraction.value(), i) = interpolated_data[3];\n    get_element(vars->metric_data->chi, i) = interpolated_data[4];\n    get_element(vars->metric_data->metric_potential, i) = interpolated_data[5];\n  }\n}\n\ntemplate <typename DataType>\nvoid CcsnCollapse::interpolate_deriv_vars_if_necessary(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  if (vars->metric_data_upper.has_value()) {\n    return;\n  }\n  vars->metric_data_upper =\n      std::array<typename IntermediateVariables<DataType>::MetricData, 3>{};\n  vars->metric_data_lower =\n      std::array<typename IntermediateVariables<DataType>::MetricData, 3>{};\n  const double dr = vars->delta_r;\n  const size_t num_points = get_size(get<0>(vars->coords));\n\n  for (size_t d = 0; d < 3; ++d) {\n    auto coord_upper = vars->coords;\n    coord_upper.get(d) += dr;\n    auto coord_lower = vars->coords;\n    coord_lower.get(d) -= dr;\n\n    detail::compute_angular_coordinates(\n        make_not_null(&gsl::at(vars->radius_upper, d)),\n        make_not_null(&gsl::at(vars->cos_theta_upper, d)),\n        make_not_null(&gsl::at(vars->sin_theta_upper, d)),\n        make_not_null(&gsl::at(vars->phi_upper, d)), coord_upper);\n    detail::compute_angular_coordinates(\n        make_not_null(&gsl::at(vars->radius_lower, d)),\n        make_not_null(&gsl::at(vars->cos_theta_lower, d)),\n        make_not_null(&gsl::at(vars->sin_theta_lower, d)),\n        make_not_null(&gsl::at(vars->phi_lower, d)), coord_lower);\n\n    gsl::at(vars->metric_data_upper.value(), d) =\n        typename IntermediateVariables<DataType>::MetricData(num_points);\n    gsl::at(vars->metric_data_lower.value(), d) =\n        typename IntermediateVariables<DataType>::MetricData(num_points);\n\n    for (size_t i = 0; i < num_points; ++i) {\n      const std::array<double, 6> interpolated_data = prog_data_.interpolate(\n          get_element(gsl::at(vars->radius_upper, d), i),\n          get_element(gsl::at(vars->cos_theta_upper, d), i), false);\n      get_element(gsl::at(vars->metric_data_upper.value(), d).chi, i) =\n\n          interpolated_data[4];\n      get_element(gsl::at(vars->metric_data_upper.value(), d).metric_potential,\n                  i) = interpolated_data[5];\n    }\n    for (size_t i = 0; i < num_points; ++i) {\n      const std::array<double, 6> interpolated_data = prog_data_.interpolate(\n          get_element(gsl::at(vars->radius_lower, d), i),\n          get_element(gsl::at(vars->cos_theta_lower, d), i), false);\n      get_element(gsl::at(vars->metric_data_lower.value(), d).chi, i) =\n          interpolated_data[4];\n      get_element(gsl::at(vars->metric_data_lower.value(), d).metric_potential,\n                  i) = interpolated_data[5];\n    }\n  }\n}\n\n// Lapse = exp(metric_potential)\ntemplate <typename DataType>\nScalar<DataType> CcsnCollapse::lapse(const DataType& metric_potential) const {\n  return Scalar<DataType>{exp(metric_potential)};\n}\n\n// Shift = 0 when using Schwarzschild coordinates\ntemplate <typename DataType>\ntnsr::I<DataType, 3, Frame::Inertial> CcsnCollapse::shift(\n    const DataType& radius) const {\n  auto result =\n      make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(radius, 0.0);\n  return result;\n}\n\n// Spatial metric\ntemplate <typename DataType>\ntnsr::ii<DataType, 3, Frame::Inertial> CcsnCollapse::spatial_metric(\n    const DataType& chi, const DataType& cos_theta, const DataType& sin_theta,\n    const DataType& phi) const {\n  auto result =\n      make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(chi, 0.0);\n  for (size_t i = 0; i < get_size(chi); ++i) {\n    // chi2 = grr in RGPS\n    const double chisqrd = square(get_element(chi, i));\n    // cos(theta)\n    const double ct = get_element(cos_theta, i);\n    // sin(theta)\n    const double st = get_element(sin_theta, i);\n    // cos(phi)\n    const double cp = cos(get_element(phi, i));\n    // sin(phi)\n    const double sp = sin(get_element(phi, i));\n\n    // Remapped quantities from M.A. Pajkos thesis, Appendix A.2\n    // xx\n    get_element(get<0, 0>(result), i) =\n        square(sp) + square(cp) * (chisqrd * square(st) + square(ct));\n    // xy\n    get_element(get<0, 1>(result), i) =\n        cp * sp * (-1.0 + chisqrd * square(st) + square(ct));\n    // xz\n    get_element(get<0, 2>(result), i) = cp * ct * st * (chisqrd - 1.0);\n    // yy\n    get_element(get<1, 1>(result), i) =\n        square(cp) + square(sp) * (chisqrd * square(st) + square(ct));\n    // yz\n    get_element(get<1, 2>(result), i) = sp * ct * st * (chisqrd - 1.0);\n    // zz\n    get_element(get<2, 2>(result), i) = chisqrd * square(ct) + square(st);\n  }\n  return result;\n}\n\n// Begin hydro variables.\n// Rest mass density\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nCcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  return {Scalar<DataType>{vars->rest_mass_density.value()}};\n}\n\n// Electron fraction\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nCcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n  return {Scalar<DataType>{vars->electron_fraction.value()}};\n}\n\n// Temperature\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Temperature<DataType>> CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const {\n  return {Scalar<DataType>{vars->temperature.value()}};\n}\n\n// Specific internal energy\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nCcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  const auto rest_mass_density = get<hydro::Tags::RestMassDensity<DataType>>(\n      variables(vars, x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{}));\n  const auto electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataType>>(variables(\n          vars, x, tmpl::list<hydro::Tags::ElectronFraction<DataType>>{}));\n  const auto temperature = get<hydro::Tags::Temperature<DataType>>(\n      variables(vars, x, tmpl::list<hydro::Tags::Temperature<DataType>>{}));\n\n  return {\n      equation_of_state_.specific_internal_energy_from_density_and_temperature(\n          rest_mass_density, temperature, electron_fraction)};\n}\n\n// Pressure\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  const auto rest_mass_density = get<hydro::Tags::RestMassDensity<DataType>>(\n      variables(vars, x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{}));\n  const auto temperature = get<hydro::Tags::Temperature<DataType>>(\n      variables(vars, x, tmpl::list<hydro::Tags::Temperature<DataType>>{}));\n  const auto electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataType>>(variables(\n          vars, x, tmpl::list<hydro::Tags::ElectronFraction<DataType>>{}));\n  return {equation_of_state_.pressure_from_density_and_temperature(\n      rest_mass_density, temperature, electron_fraction)};\n}\n\n// Specific enthalpy\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nCcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  const auto rest_mass_density = get<hydro::Tags::RestMassDensity<DataType>>(\n      variables(vars, x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{}));\n  const auto specific_internal_energy =\n      get<hydro::Tags::SpecificInternalEnergy<DataType>>(variables(\n          vars, x,\n          tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{}));\n  const auto pressure = get<hydro::Tags::Pressure<DataType>>(\n      variables(vars, x, tmpl::list<hydro::Tags::Pressure<DataType>>{}));\n\n  using std::max;\n  return {hydro::relativistic_specific_enthalpy(\n      Scalar<DataType>{DataType{max(1.0e-300, get(rest_mass_density))}},\n      specific_internal_energy, pressure)};\n}\n\n// Velocity (typical spherical coord system rotating around z axis)\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nCcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  auto spatial_velocity = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  // x and y components will have a radial velocity (from file),\n  // and toroidal velocity (from rotation law) component.\n\n  using std::cos;\n  using std::sin;\n  // Omega = omega0/(1 + (r/A)^2)\n  // vphi = distance_from_rotation_axis * Omega\n  // Temporarily store rotational velocity in z velocity component;\n  // will overwrite at the end.\n  get<2>(spatial_velocity) =\n      vars->radius * vars->sin_theta * central_angular_velocity_ /\n      (1.0 + square(vars->radius * inv_diff_rot_parameter_));\n\n  // speed of light check\n  ASSERT(max(get<2>(spatial_velocity)) <= 1.0,\n         \"Spatial velocity \" << max(get<2>(spatial_velocity))\n                             << \" is greater than the speed of light.  Central\"\n                                \" angular velocity is too large. \\n\");\n\n  // vx = vr*sin(theta)*cos(phi) - vphi*sin(phi)\n  get<0>(spatial_velocity) =\n      (-vars->fluid_velocity.value()) * vars->sin_theta * cos(vars->phi) -\n      get<2>(spatial_velocity) * sin(vars->phi);\n\n  // vy = vr*sin(theta)*sin(phi) + vphi*cos(phi)\n  get<1>(spatial_velocity) =\n      (-vars->fluid_velocity.value()) * vars->sin_theta * sin(vars->phi) +\n      get<2>(spatial_velocity) * cos(vars->phi);\n\n  // vz = vr*cos(theta)\n  get<2>(spatial_velocity) =\n      (-vars->fluid_velocity.value()) * sqrt(1.0 - square(vars->sin_theta));\n\n  return {std::move(spatial_velocity)};\n}\n\n// Lorentz\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nCcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n\n  auto lorentz_factor = make_with_value<Scalar<DataType>>(get<0>(x), 0.0);\n  // Compute spatial proper velocity, then Lorentz factor.\n  const auto spatial_metric = get<gr::Tags::SpatialMetric<DataType, 3>>(\n      variables(vars, x, tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{}));\n\n  const auto spatial_velocity =\n      get<hydro::Tags::SpatialVelocity<DataType, 3>>(variables(\n          vars, x, tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>>{}));\n\n  // Spatial velocity squared\n  get(lorentz_factor) =\n      get(dot_product(spatial_velocity, spatial_velocity, spatial_metric));\n\n  get(lorentz_factor) = 1.0 / sqrt(1.0 - get(lorentz_factor));\n  return {std::move(lorentz_factor)};\n}\n\n// B field\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nCcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  return {make_with_value<tnsr::I<DataType, 3>>(x, 0.0)};\n}\n\n// Div cleaning field\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nCcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\n// Lapse\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::Lapse<DataType>> CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  return lapse(vars->metric_data->metric_potential);\n}\n\n// Shift\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::Shift<DataType, 3>> CcsnCollapse::variables(\n    gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::Shift<DataType, 3>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  // 0 in Schwarzschild coords\n  return shift(vars->radius);\n}\n\n// Spatial metric\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, 3>>\nCcsnCollapse::variables(\n    gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::SpatialMetric<DataType, 3>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  return spatial_metric(vars->metric_data->chi, vars->cos_theta,\n                        vars->sin_theta, vars->phi);\n}\n\n// Sqrt_det_spatial_metric\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>\nCcsnCollapse::variables(\n    gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  // Analytically, in the remapped coordinates, the square root of\n  // the determinant of the spatial metric reduces to Chi\n  return {Scalar<DataType>{vars->metric_data->chi}};\n}\n\n// Inverse spatial metric\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::InverseSpatialMetric<DataType, 3>>\nCcsnCollapse::variables(\n    gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<gr::Tags::InverseSpatialMetric<DataType, 3>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n\n  const auto spatial_metric = get<gr::Tags::SpatialMetric<DataType, 3>>(\n      variables(vars, x, tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{}));\n\n  // Determinant is returned first, inverse is returned second\n  return determinant_and_inverse(spatial_metric).second;\n}\n\n// Lapse spatial derivative\ntemplate <typename DataType>\nauto CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<DerivLapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivLapse<DataType>> {\n  interpolate_deriv_vars_if_necessary(vars);\n  // Do 2nd-order FD (upper=upper index, lower = lower index)\n  auto deriv_lapse =\n      make_with_value<tnsr::i<DataType, 3, Frame::Inertial>>(get<0>(x), 0.0);\n  for (size_t d = 0; d < 3; ++d) {\n    const auto lapse_upper =\n        lapse(gsl::at(vars->metric_data_upper.value(), d).metric_potential);\n\n    const auto lapse_lower =\n        lapse(gsl::at(vars->metric_data_lower.value(), d).metric_potential);\n    deriv_lapse.get(d) =\n        (0.5 / vars->delta_r) * (get(lapse_upper) - get(lapse_lower));\n  }\n  return deriv_lapse;\n}\n\n// Shift derivative\ntemplate <typename DataType>\nauto CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<DerivShift<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivShift<DataType>> {\n  auto deriv_shift =\n      make_with_value<tnsr::iJ<DataType, 3, Frame::Inertial>>(get<0>(x), 0.0);\n  return deriv_shift;\n}\n\n// Spatial metric derivative (finite difference)\ntemplate <typename DataType>\nauto CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivSpatialMetric<DataType>> {\n  interpolate_deriv_vars_if_necessary(vars);\n  // Do 2nd-order FD. This isn't super accurate at r=0, but it's fine\n  // everywhere else.\n  auto deriv_spatial_metric =\n      make_with_value<tnsr::ijj<DataType, 3, Frame::Inertial>>(get<0>(x), 0.0);\n  for (size_t d = 0; d < 3; ++d) {\n    const auto spatial_metric_upper = spatial_metric(\n        gsl::at(vars->metric_data_upper.value(), d).chi,\n        gsl::at(vars->cos_theta_upper, d), gsl::at(vars->sin_theta_upper, d),\n        gsl::at(vars->phi_upper, d));\n    const auto spatial_metric_lower = spatial_metric(\n        gsl::at(vars->metric_data_lower.value(), d).chi,\n        gsl::at(vars->cos_theta_lower, d), gsl::at(vars->sin_theta_lower, d),\n        gsl::at(vars->phi_lower, d));\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        deriv_spatial_metric.get(d, i, j) =\n            (0.5 / vars->delta_r) *\n            (spatial_metric_upper.get(i, j) - spatial_metric_lower.get(i, j));\n      }\n    }\n  }\n  return deriv_spatial_metric;\n}\n\n// Extrinsic curvature\ntemplate <typename DataType>\nauto CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, 3>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, 3>> {\n  // 0 in constant spacetime, RGPS\n  return gr::extrinsic_curvature(\n      get<gr::Tags::Lapse<DataType>>(\n          variables(vars, x, tmpl::list<gr::Tags::Lapse<DataType>>{})),\n      get<gr::Tags::Shift<DataType, 3>>(\n          variables(vars, x, tmpl::list<gr::Tags::Shift<DataType, 3>>{})),\n      get<DerivShift<DataType>>(\n          variables(vars, x, tmpl::list<DerivShift<DataType>>{})),\n      get<gr::Tags::SpatialMetric<DataType, 3>>(variables(\n          vars, x, tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{})),\n      make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(x, 0.0),\n      get<DerivSpatialMetric<DataType>>(\n          variables(vars, x, tmpl::list<DerivSpatialMetric<DataType>>{})));\n}\n\n// lapse time derivative\ntemplate <typename DataType>\nauto CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>> {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\n// shift time derivative\ntemplate <typename DataType>\nauto CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, 3>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, 3>>> {\n  return {make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(x, 0.0)};\n}\n\n// spatial metric time derivative\ntemplate <typename DataType>\nauto CcsnCollapse::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>> {\n  return {make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(x, 0.0)};\n}\n\nPUP::able::PUP_ID CcsnCollapse::my_PUP_ID = 0;\n\nbool operator==(const CcsnCollapse& lhs, const CcsnCollapse& rhs) {\n  // The equation of state and progenitor solution aren't explicitly checked.\n  // However, if progenitor_filename_ and the polytropic_exponent_ and\n  // polytropic_constant_ are the same, then the solution and\n  // EOS should be too.\n  return lhs.progenitor_filename_ == rhs.progenitor_filename_ and\n         lhs.equation_of_state_ == rhs.equation_of_state_ and\n         lhs.central_angular_velocity_ == rhs.central_angular_velocity_ and\n         lhs.inv_diff_rot_parameter_ == rhs.inv_diff_rot_parameter_;\n}\n\nbool operator!=(const CcsnCollapse& lhs, const CcsnCollapse& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template class CcsnCollapse::IntermediateVariables<DTYPE(data)>;          \\\n  template Scalar<DTYPE(data)> CcsnCollapse::lapse(const DTYPE(data) &      \\\n                                                   metric_potential) const; \\\n  template tnsr::I<DTYPE(data), 3, Frame::Inertial> CcsnCollapse::shift(    \\\n      const DTYPE(data) & radius) const;                                    \\\n  template tnsr::ii<DTYPE(data), 3, Frame::Inertial>                        \\\n  CcsnCollapse::spatial_metric(                                             \\\n      const DTYPE(data) & chi, const DTYPE(data) & cos_theta,               \\\n      const DTYPE(data) & sin_theta, const DTYPE(data) & phi) const;        \\\n  template void CcsnCollapse::interpolate_vars_if_necessary(                \\\n      const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars) const; \\\n  template void CcsnCollapse::interpolate_deriv_vars_if_necessary(          \\\n      const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef INSTANTIATE\n\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                     \\\n  template tuples::TaggedTuple < TAG(data) < DTYPE(data) >>              \\\n      CcsnCollapse::variables(                                           \\\n          const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars, \\\n          const tnsr::I<DTYPE(data), 3>& x,                              \\\n          tmpl::list < TAG(data) < DTYPE(data) >>                        \\\n          /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::Temperature, hydro::Tags::DivergenceCleaningField,\n     hydro::Tags::LorentzFactor, hydro::Tags::SpecificEnthalpy, gr::Tags::Lapse,\n     gr::Tags::SqrtDetSpatialMetric))\n\n#undef INSTANTIATE_SCALARS\n\n#define INSTANTIATE_MHD_VECTORS(_, data)                                     \\\n  template tuples::TaggedTuple < TAG(data) < DTYPE(data), 3,                 \\\n      Frame::Inertial >>                                                     \\\n          CcsnCollapse::variables(                                           \\\n              const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars, \\\n              const tnsr::I<DTYPE(data), 3>& x,                              \\\n              tmpl::list < TAG(data) < DTYPE(data), 3, Frame::Inertial >>    \\\n              /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_MHD_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef INSTANTIATE_MHD_VECTORS\n\n#define INSTANTIATE_METRIC_TENSORS(_, data)                                   \\\n  template tuples::TaggedTuple < TAG(data) < DTYPE(data),                     \\\n      3 >> CcsnCollapse::variables(                                           \\\n               const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars, \\\n               const tnsr::I<DTYPE(data), 3>& x,                              \\\n               tmpl::list < TAG(data) < DTYPE(data), 3 >>                     \\\n               /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_METRIC_TENSORS, (double, DataVector),\n                        (gr::Tags::Shift, gr::Tags::SpatialMetric,\n                         gr::Tags::InverseSpatialMetric,\n                         gr::Tags::ExtrinsicCurvature))\n\n#undef INSTANTIATE_METRIC_TENSORS\n\n#define INSTANTIATE_METRIC_DT_TENSORS(_, data)                                 \\\n  template tuples::TaggedTuple < ::Tags::dt < TAG(data) < DTYPE(data),         \\\n      3 >>> CcsnCollapse::variables(                                           \\\n                const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars, \\\n                const tnsr::I<DTYPE(data), 3>& x,                              \\\n                tmpl::list < ::Tags::dt < TAG(data) < DTYPE(data), 3 >>>       \\\n                /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_METRIC_DT_TENSORS, (double, DataVector),\n                        (gr::Tags::Shift, gr::Tags::SpatialMetric))\n\n#undef INSTANTIATE_METRIC_DT_TENSORS\n\n#define INSTANTIATE_SCALAR_DT(_, data)                                    \\\n  template tuples::TaggedTuple < ::Tags::dt < TAG(data) < DTYPE(data) >>> \\\n      CcsnCollapse::variables(                                            \\\n          const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars,  \\\n          const tnsr::I<DTYPE(data), 3>& x,                               \\\n          tmpl::list < ::Tags::dt < TAG(data) < DTYPE(data) >>>           \\\n          /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALAR_DT, (double, DataVector),\n                        (gr::Tags::Lapse))\n\n#undef INSTANTIATE_SCALAR_DT\n\n#define INSTANTIATE_METRIC_DERIVS(_, data)                                \\\n  template auto CcsnCollapse::variables(                                  \\\n      const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars,      \\\n      const tnsr::I<DTYPE(data), 3>& x,                                   \\\n      tmpl::list < TAG(data) < DTYPE(data) >>                             \\\n      /*meta*/) const -> tuples::TaggedTuple < TAG(data) < DTYPE(data) >> \\\n      ;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_METRIC_DERIVS, (double, DataVector),\n                        (DerivLapse, DerivShift, DerivSpatialMetric))\n\n#undef INSTANTIATE_METRIC_DERIVS\n\n#undef TAG\n#undef DTYPE\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::AnalyticData {\nnamespace detail {\n/*!\n * \\brief Read a massive star supernova progenitor from file.\n */\nclass ProgenitorProfile {\n public:\n  ProgenitorProfile() = default;\n\n  ProgenitorProfile(const std::string& filename);\n  // interpolate function\n  std::array<double, 6> interpolate(double target_radius,\n                                    double target_cos_theta,\n                                    bool interpolate_hydro_vars) const;\n\n  double max_radius() const { return maximum_radius_; }\n\n  // Change private density ratio based on user-defined value,\n  // accessed in CCSNCollapse() call\n  void set_dens_ratio(double max_dens_ratio) {\n    max_density_ratio_for_linear_interpolation_ = max_dens_ratio;\n  }\n\n  void pup(PUP::er& p);\n\n private:\n  double maximum_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double max_density_ratio_for_linear_interpolation_{\n      std::numeric_limits<double>::signaling_NaN()};\n  size_t num_radial_points_{std::numeric_limits<size_t>::max()};\n  size_t num_angular_points_{std::numeric_limits<size_t>::max()};\n  size_t num_grid_points_{std::numeric_limits<size_t>::max()};\n\n  // Begin constants to translate units from cgs to c=G=M_Sun=(k_Boltzmann=1)=1\n  // Divide cm/s by speed_of_light_cgs_ to get into c=G=M_Sun=1 units\n  static constexpr double speed_of_light_cgs_ =\n      hydro::units::cgs::speed_of_light;\n\n  // Multiply cm by c2g_length_ to get into c=G=M_Sun=1 units\n  // (speed of light)^2 / (Newton's constant * Msun)\n  // c2g_length_ = c^2 / (G*M_Sun)\n  // c2g_length_~6.7706e-6 [1/cm]\n  static constexpr double g_times_m_sun_ =\n      hydro::units::cgs::G_Newton_times_m_sun;\n  static constexpr double c2g_length_ = 1.0 / hydro::units::cgs::length_unit;\n\n  // Multiply g/cm^3 by c2g_dens_ to get into c=G=M_Sun=1 units\n  // c2g_dens_ = (G*M_Sun)^2*G/c^6.\n  // Note G = 6.67430x10^-8.\n  // While G is not known to as many significant figures as G*M_Sun,\n  // it's uncertainty is still less than that of the maximum mass of\n  // a neutron star (~1%~10%), a quantity assumed to be 2.2 M_Sun when\n  // converting to c=G=M_Sun=1 units; see\n  // https://iopscience.iop.org/article/10.3847/2041-8213/aaa401.\n  // c2g_dens_~1.61887e-18 [cm^3/g]\n  static constexpr double newtons_grav_constant_ = hydro::units::cgs::G_Newton;\n  static constexpr double c2g_dens_ =\n      1.0 / hydro::units::cgs::rest_mass_density_unit;\n\n  // Multiply Kelvin by c2g_temp_ to get into c=G=M_Sun=k_Boltzmann=1 units.\n  // c2g_temp_ = (k_B/(M_Sun*c^2))\n  // c2g_temp_=7.72567e-71\n  static constexpr double boltzmann_constant_ = hydro::units::cgs::k_Boltzmann;\n  // Note setting MSun=1.0 is a choice made independent of EoS,\n  // so that there are no unit ambiguities between 2 simulations\n  // ran with different EoSs (e.g., polytropic vs. tabulated)\n  static constexpr double m_sun_ = hydro::units::cgs::mass_unit;\n  static constexpr double c2g_temp_ =\n      boltzmann_constant_ / (m_sun_ * square(speed_of_light_cgs_));\n\n  // readin data\n  DataVector radius_;\n  DataVector rest_mass_density_;\n  DataVector fluid_velocity_;\n  DataVector electron_fraction_;\n  DataVector chi_;\n  DataVector metric_potential_;\n  DataVector temperature_;\n};\n}  // namespace detail\n\n/*!\n * \\brief Evolve a stellar collapse (progenitor) profile through collapse.\n *\n * Read the 1D core-collapse supernova (CCSN) profile from file, assuming\n * the following format of the data:\n * * 1st row - number of radial points in data set, followed by 6 columns of 0s\n * * Subsequent rows contain these column headers:\n *   * radius, rest mass density, specific internal energy, radial velocity,\n *     electron fraction, \\f$X\\f$, \\f$\\Phi\\f$.\n *\n * These columns assume a metric of the form \\f$ds^2 = -e^{2\\Phi} dt^2 +\n * X^2 dr^2 + r^2 d\\theta^2 + r^2 \\sin^2\\theta d\\phi^2\\f$, following\n * \\cite Oconnor2015.\n *\n * Units:\n * The input file is assumed to be in cgs units and\n * will be converted to dimensionless units upon\n * readin, setting \\f$c = G = M_\\odot = 1\\f$.\n *\n * Rotation:\n * Once the stationary progenitor is constructed, an artificial\n * rotation profile can be assigned to the matter.  It takes the form\n * \\f$\\Omega(r) = \\Omega_0/(1 + (r_\\perp/A)^2)\\f$, where \\f$\\Omega_0\\f$ is\n * the central rotation rate, \\f$r_\\perp\\f$ is the distance from the rotation\n * axis, and \\f$A\\f$ is the differential rotation parameter.  Large values\n * of \\f$A\\f$ correspond to more solid-body rotation.  Small values correspond\n * to stronger differential rotation.  The axis of rotation is assumed to\n * be the z axis.\n *\n */\nclass CcsnCollapse : public virtual evolution::initial_data::InitialData,\n                     public MarkAsAnalyticData,\n                     public AnalyticDataBase,\n                     public hydro::TemperatureInitialization<CcsnCollapse> {\n  template <typename DataType>\n  struct IntermediateVariables {\n    IntermediateVariables(\n        const tnsr::I<DataType, 3, Frame::Inertial>& in_coords,\n        double in_delta_r);\n\n    struct MetricData {\n      MetricData() = default;\n      MetricData(size_t num_points);\n      DataType chi;\n      DataType metric_potential;\n    };\n\n    const tnsr::I<DataType, 3, Frame::Inertial>& coords;\n    DataType radius;\n    DataType phi;\n    DataType cos_theta;\n    DataType sin_theta;\n    double delta_r;\n\n    std::optional<DataType> rest_mass_density;\n    std::optional<DataType> fluid_velocity;\n    std::optional<DataType> electron_fraction;\n    std::optional<DataType> temperature;\n    std::optional<MetricData> metric_data;\n    // Data for 2nd-order FD derivatives.\n    // metric info at different indices for finite differences\n    std::optional<std::array<MetricData, 3>> metric_data_upper{};\n    std::optional<std::array<MetricData, 3>> metric_data_lower{};\n\n    std::array<DataType, 3> radius_upper{};\n    std::array<DataType, 3> radius_lower{};\n    std::array<DataType, 3> cos_theta_upper{};\n    std::array<DataType, 3> cos_theta_lower{};\n    std::array<DataType, 3> sin_theta_upper{};\n    std::array<DataType, 3> sin_theta_lower{};\n    std::array<DataType, 3> phi_upper{};\n    std::array<DataType, 3> phi_lower{};\n  };\n\n public:\n  using equation_of_state_type = EquationsOfState::Tabulated3D<true>;\n\n  /// The massive star progenitor data file.\n  ///\n  /// Available in the\n  /// [SXS\n  /// Repo](https://github.com/sxs-collaboration/core_collapse_supernova_files_for_spectre/tree/main/progenitor_files)\n  struct ProgenitorFilename {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The supernova progenitor data file.\"};\n  };\n\n  /// Central angular velocity artificially assigned at readin.\n  ///\n  /// Currently limited by the angular velocity of an object rotating in a\n  /// circle, with radius = 1 cm (well below grid resolution) and\n  /// transverse velocity = the speed of light.\n  ///\n  /// Due to effects from magnetic breaking, more realistic central angular\n  /// velocities for massive stars are of order 0.1 [radians/sec] ~ 4.93e-7\n  /// [code units] (see Figure 1 of \\cite Pajkos2019 based on results from\n  /// \\cite Heger2005, or see \\cite Richers2017 for an exploration of\n  /// different values).\n  struct CentralAngularVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Central angular velocity of progenitor\"};\n\n    static type upper_bound() { return 147670.0; }\n    static type lower_bound() { return -147670.0; }\n  };\n\n  /// Differential rotation parameter for artificially assigned\n  /// rotation profile.\n  struct DifferentialRotationParameter {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Differential rotation parameter (large\"\n        \" indicates solid body, small very differential)\"};\n    // This is ~1 cm, well below simulation resolution.\n    // The lower bound is used to ensure a nonzero divisor.\n    static type lower_bound() { return 6.7706e-6; }\n  };\n\n  /// Maximum density ratio for linear interpolation.\n  ///\n  /// If the ratio of two neighboring densities are greater than this value,\n  /// then interpolation onto the SpECTRE grid reverts to linear, to avoid\n  /// oscillations.  A ratio of 100 is used for the rotating TOV star test\n  /// and a safe value to use if unsure.\n  struct MaxDensityRatioForLinearInterpolation {\n    using type = double;\n    static constexpr Options::String help = {\n        \"If the ratio between neighboring density points is greater\"\n        \" than this parameter, fall back to linear interpolation\"\n        \" onto the SpECTRE grid.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct TableFilename {\n    using type = std::string;\n    static constexpr Options::String help{\"File name of the EOS table\"};\n  };\n\n  struct TableSubFilename {\n    using type = std::string;\n    static constexpr Options::String help{\n        \"Subfile name of the EOS table, e.g., 'dd2'.\"};\n  };\n\n  using options = tmpl::list<\n      ProgenitorFilename, CentralAngularVelocity, DifferentialRotationParameter,\n      MaxDensityRatioForLinearInterpolation, TableFilename, TableSubFilename>;\n  static constexpr Options::String help = {\n      \"Core collapse supernova initial data, read in from a profile containing\"\n      \" hydrodynamic primitives and metric variables.  The data \"\n      \"are read in from disk.\"};\n\n  CcsnCollapse() = default;\n  CcsnCollapse(const CcsnCollapse& /*rhs*/) = default;\n  CcsnCollapse& operator=(const CcsnCollapse& /*rhs*/) = default;\n  CcsnCollapse(CcsnCollapse&& /*rhs*/) = default;\n  CcsnCollapse& operator=(CcsnCollapse&& /*rhs*/) = default;\n  ~CcsnCollapse() override = default;\n\n  CcsnCollapse(std::string progenitor_filename,\n               double central_angular_velocity,\n               double diff_rot_parameter,\n               double max_dens_ratio_interp,\n               const std::string& eos_filename,\n               const std::string& eos_subfilename);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit CcsnCollapse(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CcsnCollapse);\n  /// \\endcond\n\n  /// Retrieve a collection of variables at `(x)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    IntermediateVariables<DataType> intermediate_vars{\n        x, 1.0e-4 * prog_data_.max_radius()};\n    return {get<Tags>(variables(make_not_null(&intermediate_vars), x,\n                                tmpl::list<Tags>{}))...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  const EquationsOfState::Tabulated3D<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n protected:\n  template <typename DataType>\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                                   Frame::Inertial>;\n\n  template <typename DataType>\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, 3>,\n                                   tmpl::size_t<3>, Frame::Inertial>;\n\n  template <typename DataType>\n  using DerivSpatialMetric = ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>,\n                                           tmpl::size_t<3>, Frame::Inertial>;\n\n  template <typename DataType>\n  void interpolate_vars_if_necessary(\n      gsl::not_null<IntermediateVariables<DataType>*> vars) const;\n\n  template <typename DataType>\n  void interpolate_deriv_vars_if_necessary(\n      gsl::not_null<IntermediateVariables<DataType>*> vars) const;\n\n  template <typename DataType>\n  Scalar<DataType> lapse(const DataType& metric_potential) const;\n\n  template <typename DataType>\n  tnsr::I<DataType, 3, Frame::Inertial> shift(const DataType& radius) const;\n\n  template <typename DataType>\n  tnsr::ii<DataType, 3, Frame::Inertial> spatial_metric(\n      const DataType& chi, const DataType& cos_theta, const DataType& sin_theta,\n      const DataType& phi) const;\n\n  template <typename DataType>\n  tnsr::II<DataType, 3, Frame::Inertial> inverse_spatial_metric(\n      const DataType& chi, const DataType& cos_theta, const DataType& sin_theta,\n      const DataType& phi) const;\n\n  template <typename DataType>\n  Scalar<DataType> sqrt_det_spatial_metric(const DataType& chi) const;\n\n  template <typename DataType>\n  auto make_metric_data(size_t num_points) const ->\n      typename IntermediateVariables<DataType>::MetricData;\n\n public:\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      gsl::not_null<IntermediateVariables<DataType>*> vars,\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      gsl::not_null<IntermediateVariables<DataType>*> vars,\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::Shift<DataType, 3>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Shift<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::SpatialMetric<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      gsl::not_null<IntermediateVariables<DataType>*> vars,\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataType, 3>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::InverseSpatialMetric<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<DerivLapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivLapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<DerivShift<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivShift<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/)\n      const -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>>;\n\n  template <typename DataType>\n  auto variables(\n      const gsl::not_null<IntermediateVariables<DataType>*> vars,\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>> /*meta*/)\n      const\n      -> tuples::TaggedTuple<::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>>;\n\n  template <typename DataType>\n  auto variables(const gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, 3>>> /*meta*/)\n      const -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, 3>>>;\n\n  friend bool operator==(const CcsnCollapse& lhs, const CcsnCollapse& rhs);\n\n protected:\n  std::string progenitor_filename_{};\n  detail::ProgenitorProfile prog_data_{};\n  EquationsOfState::Tabulated3D<true> equation_of_state_;\n  double central_angular_velocity_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double inv_diff_rot_parameter_ = std::numeric_limits<double>::signaling_NaN();\n};\nbool operator!=(const CcsnCollapse& lhs, const CcsnCollapse& rhs);\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/FukaInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/FukaInitialData.hpp\"\n\n#include <memory>\n#include <pup.h>\n\nnamespace grmhd::AnalyticData {\n\nFukaInitialData::FukaInitialData(std::string info_filename,\n                                 const double electron_fraction)\n    : info_filename_(std::move(info_filename)),\n      electron_fraction_(electron_fraction) {}\n\nFukaInitialData::FukaInitialData(const FukaInitialData& rhs)\n    : evolution::initial_data::InitialData(rhs) {\n  *this = rhs;\n}\n\nFukaInitialData& FukaInitialData::operator=(const FukaInitialData& rhs) {\n  info_filename_ = rhs.info_filename_;\n  electron_fraction_ = rhs.electron_fraction_;\n  return *this;\n}\n\nFukaInitialData::FukaInitialData(FukaInitialData&& rhs)\n    : evolution::initial_data::InitialData(rhs) {\n  *this = rhs;\n}\n\nFukaInitialData& FukaInitialData::operator=(FukaInitialData&& rhs) {\n  info_filename_ = std::move(rhs.info_filename_);\n  electron_fraction_ = rhs.electron_fraction_;\n  return *this;\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nFukaInitialData::get_clone() const {\n  return std::make_unique<FukaInitialData>(*this);\n}\n\nFukaInitialData::FukaInitialData(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid FukaInitialData::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | info_filename_;\n  p | electron_fraction_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID FukaInitialData::my_PUP_ID = 0;\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/FukaInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <mutex>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/NumericInitialData.hpp\"\n#include \"IO/External/InterpolateFromFuka.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Hydro initial data generated by FUKA.\n *\n * This class loads numerical data written out by the FUKA initial data solver.\n *\n * We choose a constant electron fraction and zero temperature for now because\n * FUKA doesn't export these quantities. We'll have to improve this later, e.g.\n * by constructing an EOS consistent with the FUKA data.\n */\nclass FukaInitialData : public evolution::initial_data::InitialData,\n                        public evolution::NumericInitialData,\n                        public AnalyticDataBase {\n public:\n  struct InfoFilename {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"Path to the '.info' file of data produced by FUKA.\"};\n  };\n\n  struct ElectronFraction {\n    using type = double;\n    static constexpr Options::String help = {\"Constant electron fraction\"};\n  };\n\n  using options = tmpl::list<InfoFilename, ElectronFraction>;\n\n  static constexpr Options::String help = {\"Initial data generated by FUKA\"};\n\n  FukaInitialData() = default;\n  FukaInitialData(const FukaInitialData& rhs);\n  FukaInitialData& operator=(const FukaInitialData& rhs);\n  FukaInitialData(FukaInitialData&& rhs);\n  FukaInitialData& operator=(FukaInitialData&& rhs);\n  ~FukaInitialData() override = default;\n\n  FukaInitialData(std::string info_filename, double electron_fraction);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit FukaInitialData(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(FukaInitialData);\n  /// \\endcond\n\n  template <typename DataType>\n  using tags = tmpl::append<\n      tmpl::list<gr::Tags::SpatialMetric<DataType, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataType, 3>,\n                 gr::Tags::Lapse<DataType>, gr::Tags::Shift<DataType, 3>>,\n      hydro::grmhd_tags<DataType>>;\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 3>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    auto interpolated_vars = io::interpolate_from_fuka<io::FukaIdType::Bns>(\n        make_not_null(&fuka_lock_), info_filename_, x);\n    tuples::TaggedTuple<RequestedTags...> result{};\n    // Move interpolated data into result buffer\n    tmpl::for_each<\n        tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                   gr::Tags::SpatialMetric<DataVector, 3>,\n                   gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                   hydro::Tags::RestMassDensity<DataVector>,\n                   hydro::Tags::SpecificInternalEnergy<DataVector>,\n                   hydro::Tags::Pressure<DataVector>,\n                   hydro::Tags::SpatialVelocity<DataVector, 3>>>(\n        [&result, &interpolated_vars](const auto tag_v) {\n          using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n          get<tag>(result) = std::move(get<tag>(interpolated_vars));\n        });\n    // Compute derived quantities from interpolated data\n    const size_t num_points = x.begin()->size();\n    const auto& rest_mass_density =\n        get<hydro::Tags::RestMassDensity<DataVector>>(result);\n    const auto& specific_internal_energy =\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(result);\n    const auto& pressure = get<hydro::Tags::Pressure<DataVector>>(result);\n    const auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(result);\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(result);\n    if constexpr (tmpl::list_contains_v<\n                      tmpl::list<RequestedTags...>,\n                      hydro::Tags::SpecificEnthalpy<DataVector>>) {\n      // Compute enthalpy from internal energy and pressure if requested\n      auto& specific_enthalpy =\n          get<hydro::Tags::SpecificEnthalpy<DataVector>>(result);\n      get(specific_enthalpy) = DataVector(num_points);\n      for (size_t i = 0; i < num_points; ++i) {\n        const double local_rest_mass_density = get(rest_mass_density)[i];\n        if (equal_within_roundoff(local_rest_mass_density, 0.)) {\n          get(specific_enthalpy)[i] = 1.;\n        } else {\n          get(specific_enthalpy)[i] = get(hydro::relativistic_specific_enthalpy(\n              Scalar<double>(local_rest_mass_density),\n              Scalar<double>(get(specific_internal_energy)[i]),\n              Scalar<double>(get(pressure)[i])));\n        }\n      }\n    }\n    // Constant electron fraction specified by input file\n    auto& electron_fraction =\n        get<hydro::Tags::ElectronFraction<DataVector>>(result);\n    get(electron_fraction) = DataVector(num_points, electron_fraction_);\n    // Zero magnetic field and divergence cleaning field\n    auto& magnetic_field =\n        get<hydro::Tags::MagneticField<DataVector, 3>>(result);\n    get<0>(magnetic_field) = DataVector(num_points, 0.);\n    get<1>(magnetic_field) = DataVector(num_points, 0.);\n    get<2>(magnetic_field) = DataVector(num_points, 0.);\n    auto& div_cleaning_field =\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(result);\n    get(div_cleaning_field) = DataVector(num_points, 0.);\n    // Compute Lorentz factor from spatial velocity\n    auto& lorentz_factor = get<hydro::Tags::LorentzFactor<DataVector>>(result);\n    get(lorentz_factor) =\n        1. / sqrt(1. - get(dot_product(spatial_velocity, spatial_velocity,\n                                       spatial_metric)));\n    // Set temperature to zero for now\n    auto& temperature = get<hydro::Tags::Temperature<DataVector>>(result);\n    get(temperature) = DataVector(num_points, 0.);\n    return result;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  std::string info_filename_{};\n  double electron_fraction_ = std::numeric_limits<double>::signaling_NaN();\n\n  // This lock is used to ensure that only one thread at a time is calling the\n  // FUKA interpolation routines. We make some assumptions here to guarantee\n  // thread-safety:\n  // - This analytic data class exists only once per node (in the global cache).\n  //   This means we don't have to copy or PUP the lock or pass it around\n  //   instances.\n  // - This also allows the lock to be mutable, which is necessary for the\n  //   const-ness of the `variables` function.\n  mutable std::mutex fuka_lock_{};  // NOLINT(spectre-mutable)\n};\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Poloidal.cpp\n  Toroidal.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  InitialMagneticField.hpp\n  Poloidal.hpp\n  Toroidal.hpp\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Poloidal.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Toroidal.hpp\"\n\nnamespace grmhd::AnalyticData::InitialMagneticFields {\n\n/// Typelist of available InitialMagneticFields\nusing initial_magnetic_fields = tmpl::list<Poloidal, Toroidal>;\n\n}  // namespace grmhd::AnalyticData::InitialMagneticFields\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Things related to the initial (seed) magnetic field that can be\n * superposed on GRMHD initial data.\n *\n * In many cases we assign magnetic fields in terms of the vector potential,\n * which can be computed as\n *\n * \\f{align*}{\n *   B^i & = n_a\\epsilon^{aijk}\\partial_jA_k \\\\\n *       & = \\frac{1}{\\sqrt{\\gamma}}[ijk]\\partial_j A_k,\n * \\f}\n *\n * where \\f$[ijk]\\f$ is the total anti-symmetric symbol.\n *\n * For example, in the Cartesian coordinates,\n *\n * \\f{align*}{\n *   B^x & = \\frac{1}{\\sqrt{\\gamma}} (\\partial_y A_z - \\partial_z A_y), \\\\\n *   B^y & = \\frac{1}{\\sqrt{\\gamma}} (\\partial_z A_x - \\partial_x A_z), \\\\\n *   B^z & = \\frac{1}{\\sqrt{\\gamma}} (\\partial_x A_y - \\partial_y A_x).\n * \\f}\n *\n *\n * \\warning The magnetic field classes assume the magnetic field is initialized,\n * both in size and value, before being passed into the `variables` function.\n * This is so that multiple magnetic fields can be superposed. Each magnetic\n * field configuration does a `+=` to make this possible.\n */\nnamespace InitialMagneticFields {\n\n/*!\n * \\brief The abstract base class for initial magnetic field configurations.\n *\n * \\warning This assumes the magnetic field is initialized, both in size and\n * value, before being passed into the `variables` function. This is so that\n * multiple magnetic fields can be superposed. Each magnetic field\n * configuration does a `+=` to make this possible.\n */\nclass InitialMagneticField : public PUP::able {\n protected:\n  InitialMagneticField() = default;\n\n public:\n  ~InitialMagneticField() override = default;\n\n  virtual auto get_clone() const -> std::unique_ptr<InitialMagneticField> = 0;\n\n  virtual void variables(\n      gsl::not_null<tnsr::I<DataVector, 3>*> result,\n      const tnsr::I<DataVector, 3>& coords, const Scalar<DataVector>& pressure,\n      const Scalar<DataVector>& sqrt_det_spatial_metric,\n      const tnsr::i<DataVector, 3>& deriv_pressure) const = 0;\n\n  virtual void variables(gsl::not_null<tnsr::I<double, 3>*> result,\n                         const tnsr::I<double, 3>& coords,\n                         const Scalar<double>& pressure,\n                         const Scalar<double>& sqrt_det_spatial_metric,\n                         const tnsr::i<double, 3>& deriv_pressure) const = 0;\n\n  virtual bool is_equal(const InitialMagneticField& rhs) const = 0;\n\n  /// \\cond\n  explicit InitialMagneticField(CkMigrateMessage* msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(InitialMagneticField);\n  /// \\endcond\n};\n\n}  // namespace InitialMagneticFields\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Poloidal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Poloidal.hpp\"\n\n#include <memory>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace grmhd::AnalyticData::InitialMagneticFields {\n\nstd::unique_ptr<InitialMagneticField> Poloidal::get_clone() const {\n  return std::make_unique<Poloidal>(*this);\n}\n\nPoloidal::Poloidal(CkMigrateMessage* msg) : InitialMagneticField(msg) {}\n\nvoid Poloidal::pup(PUP::er& p) {\n  InitialMagneticField::pup(p);\n  p | pressure_exponent_;\n  p | cutoff_pressure_;\n  p | vector_potential_amplitude_;\n  p | center_;\n  p | max_distance_from_center_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Poloidal::my_PUP_ID = 0;\n\nPoloidal::Poloidal(const size_t pressure_exponent, const double cutoff_pressure,\n                   const double vector_potential_amplitude,\n                   const std::array<double, 3> center,\n                   const double max_distance_from_center)\n    : pressure_exponent_(pressure_exponent),\n      cutoff_pressure_(cutoff_pressure),\n      vector_potential_amplitude_(vector_potential_amplitude),\n      center_(center),\n      max_distance_from_center_(max_distance_from_center) {}\n\nvoid Poloidal::variables(const gsl::not_null<tnsr::I<DataVector, 3>*> result,\n                         const tnsr::I<DataVector, 3>& coords,\n                         const Scalar<DataVector>& pressure,\n                         const Scalar<DataVector>& sqrt_det_spatial_metric,\n                         const tnsr::i<DataVector, 3>& deriv_pressure) const {\n  ASSERT(result->get(0).size() == get(pressure).size(),\n         \"Result must be of size \" << get(pressure).size() << \" but got \"\n                                   << result->get(0).size());\n  variables_impl(result, coords, pressure, sqrt_det_spatial_metric,\n                 deriv_pressure);\n}\n\nvoid Poloidal::variables(const gsl::not_null<tnsr::I<double, 3>*> result,\n                         const tnsr::I<double, 3>& coords,\n                         const Scalar<double>& pressure,\n                         const Scalar<double>& sqrt_det_spatial_metric,\n                         const tnsr::i<double, 3>& deriv_pressure) const {\n  variables_impl(result, coords, pressure, sqrt_det_spatial_metric,\n                 deriv_pressure);\n}\n\nbool Poloidal::is_equal(const InitialMagneticField& rhs) const {\n  const auto& derived_ptr = dynamic_cast<const Poloidal* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <typename DataType>\nvoid Poloidal::variables_impl(\n    const gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n    const tnsr::I<DataType, 3>& coords, const Scalar<DataType>& pressure,\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::i<DataType, 3>& deriv_pressure) const {\n  const size_t num_pts = get_size(get(pressure));\n\n  for (size_t i = 0; i < num_pts; ++i) {\n    const double pressure_i = get_element(get(pressure), i);\n    const double x = get_element(coords.get(0), i) - center_[0];\n    const double y = get_element(coords.get(1), i) - center_[1];\n    const double z = get_element(coords.get(2), i) - center_[2];\n    const double radius = sqrt(x * x + y * y + z * z);\n    if (pressure_i < cutoff_pressure_ or radius > max_distance_from_center_) {\n      continue;\n    }\n\n    // (p - p_c)^{n_s}\n    const double pressure_term = pow(pressure_i - cutoff_pressure_,\n                                     static_cast<int>(pressure_exponent_));\n    // n_s * (p - p_c)^{n_s-1}\n    const double n_times_pressure_to_n_minus_1 =\n        static_cast<double>(pressure_exponent_) *\n        pow(pressure_i - cutoff_pressure_,\n            static_cast<int>(pressure_exponent_) - 1);\n\n    const auto& dp_dx = get_element(deriv_pressure.get(0), i);\n    const auto& dp_dy = get_element(deriv_pressure.get(1), i);\n    const auto& dp_dz = get_element(deriv_pressure.get(2), i);\n\n    // Assign Bx, By, Bz\n    get_element(magnetic_field->get(0), i) +=\n        vector_potential_amplitude_ *\n        (-n_times_pressure_to_n_minus_1 * x * dp_dz) /\n        get_element(get(sqrt_det_spatial_metric), i);\n    get_element(magnetic_field->get(1), i) +=\n        vector_potential_amplitude_ *\n        (-n_times_pressure_to_n_minus_1 * y * dp_dz) /\n        get_element(get(sqrt_det_spatial_metric), i);\n    get_element(magnetic_field->get(2), i) +=\n        vector_potential_amplitude_ *\n        (2.0 * pressure_term +\n         n_times_pressure_to_n_minus_1 * (x * dp_dx + y * dp_dy)) /\n        get_element(get(sqrt_det_spatial_metric), i);\n  }\n}\n\nbool operator==(const Poloidal& lhs, const Poloidal& rhs) {\n  return lhs.pressure_exponent_ == rhs.pressure_exponent_ and\n         lhs.cutoff_pressure_ == rhs.cutoff_pressure_ and\n         lhs.vector_potential_amplitude_ == rhs.vector_potential_amplitude_ and\n         lhs.center_ == rhs.center_ and\n         lhs.max_distance_from_center_ == rhs.max_distance_from_center_;\n}\n\nbool operator!=(const Poloidal& lhs, const Poloidal& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                  \\\n  template void Poloidal::variables_impl<DTYPE(data)>(        \\\n      gsl::not_null<tnsr::I<DTYPE(data), 3>*> magnetic_field, \\\n      const tnsr::I<DTYPE(data), 3>& coords,                  \\\n      const Scalar<DTYPE(data)>& pressure,                    \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric,     \\\n      const tnsr::i<DTYPE(data), 3>& deriv_pressure) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n\n}  // namespace grmhd::AnalyticData::InitialMagneticFields\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Poloidal.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::AnalyticData::InitialMagneticFields {\n\n/*!\n * \\brief %Poloidal magnetic field for GRMHD initial data.\n *\n * The vector potential has the form\n *\n * \\f{align*}{\n *  A_{\\phi} = A_b \\varpi^2 \\max(p-p_{\\mathrm{cut}}, 0)^{n_s} ,\n * \\f}\n *\n * where \\f$A_b\\f$ controls the amplitude of the magnetic field,\n * \\f$\\varpi^2=x^2+y^2=r^2-z^2\\f$ is the cylindrical radius,\n * \\f$n_s\\f$ controls the degree of differentiability, and\n * \\f$p_{\\mathrm{cut}}\\f$ controls the pressure cutoff below which the magnetic\n * field is zero.\n *\n * In Cartesian coordinates the vector potential is:\n *\n * \\f{align*}{\n *   A_x & = -\\frac{y}{\\varpi^2}A_\\phi\n *            = -y A_b\\max(p-p_{\\mathrm{cut}}, 0)^{n_s}, \\\\\n *   A_y & = \\frac{x}{\\varpi^2}A_\\phi\n *            = x A_b\\max(p-p_{\\mathrm{cut}}, 0)^{n_s}, \\\\\n *   A_z & = 0,\n * \\f}\n *\n * On the region where the field is non-zero, the magnetic field is given by:\n *\n * \\f{align*}{\n *   B^x & = -\\frac{A_b n_s}{\\sqrt{\\gamma}}\n *        (p-p_{\\mathrm{cut}})^{n_s-1} x \\partial_z p \\\\\n *   B^x & = -\\frac{A_b n_s}{\\sqrt{\\gamma}}\n *        (p-p_{\\mathrm{cut}})^{n_s-1} y \\partial_z p \\\\\n *   B^z & = \\frac{A_b}{\\sqrt{\\gamma}}\\left[\n *        2(p-p_{\\mathrm{cut}})^{n_s}\n *        + n_s (p-p_{\\mathrm{cut}})^{n_s-1} (x \\partial_x p + y \\partial_y p)\n *        \\right]\n * \\f}\n *\n * Taking the small-\\f$r\\f$ limit gives the magnetic field at the origin:\n *\n * \\f{align*}{\n *   B^x&=0, \\\\\n *   B^y&=0, \\\\\n *   B^z&=\\frac{A_b}{\\sqrt{\\gamma}}\n *        2(p-p_{\\mathrm{cut}})^{n_s}.\n * \\f}\n *\n * Note that the coordinates are relative to the `Center` passed in, so the\n * field can be centered about any arbitrary point. The field is also zero\n * outside of `MaxDistanceFromCenter`, so that compact support can be imposed if\n * necessary.\n *\n * \\warning This assumes the magnetic field is initialized, both in size and\n * value, before being passed into the `variables` function. This is so that\n * multiple magnetic fields can be superposed. Each magnetic field\n * configuration does a `+=` to make this possible.\n */\nclass Poloidal : public InitialMagneticField {\n public:\n  struct PressureExponent {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The exponent n_s controlling the smoothness of the field\"};\n  };\n\n  struct CutoffPressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The pressure below which there is no magnetic field.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct VectorPotentialAmplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The amplitude A_b of the phi-component of the vector potential. This \"\n        \"controls the magnetic field strength.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The center of the magnetic field.\"};\n  };\n\n  struct MaxDistanceFromCenter {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The maximum distance from the center to compute the magnetic field. \"\n        \"Everywhere outside the field is set to zero.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options =\n      tmpl::list<PressureExponent, CutoffPressure, VectorPotentialAmplitude,\n                 Center, MaxDistanceFromCenter>;\n\n  static constexpr Options::String help = {\"Poloidal initial magnetic field\"};\n\n  Poloidal() = default;\n  Poloidal(const Poloidal& /*rhs*/) = default;\n  Poloidal& operator=(const Poloidal& /*rhs*/) = default;\n  Poloidal(Poloidal&& /*rhs*/) = default;\n  Poloidal& operator=(Poloidal&& /*rhs*/) = default;\n  ~Poloidal() override = default;\n\n  Poloidal(size_t pressure_exponent, double cutoff_pressure,\n           double vector_potential_amplitude, std::array<double, 3> center,\n           double max_distance_from_center);\n\n  auto get_clone() const -> std::unique_ptr<InitialMagneticField> override;\n\n  /// \\cond\n  explicit Poloidal(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Poloidal);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// Retrieve magnetic fields at `(x)`\n  void variables(gsl::not_null<tnsr::I<DataVector, 3>*> result,\n                 const tnsr::I<DataVector, 3>& coords,\n                 const Scalar<DataVector>& pressure,\n                 const Scalar<DataVector>& sqrt_det_spatial_metric,\n                 const tnsr::i<DataVector, 3>& deriv_pressure) const override;\n\n  /// Retrieve magnetic fields at `(x)`\n  void variables(gsl::not_null<tnsr::I<double, 3>*> result,\n                 const tnsr::I<double, 3>& coords,\n                 const Scalar<double>& pressure,\n                 const Scalar<double>& sqrt_det_spatial_metric,\n                 const tnsr::i<double, 3>& deriv_pressure) const override;\n\n  bool is_equal(const InitialMagneticField& rhs) const override;\n\n private:\n  template <typename DataType>\n  void variables_impl(gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n                      const tnsr::I<DataType, 3>& coords,\n                      const Scalar<DataType>& pressure,\n                      const Scalar<DataType>& sqrt_det_spatial_metric,\n                      const tnsr::i<DataType, 3>& deriv_pressure) const;\n\n  size_t pressure_exponent_ = std::numeric_limits<size_t>::max();\n  double cutoff_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double vector_potential_amplitude_ =\n      std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> center_{{std::numeric_limits<double>::signaling_NaN(),\n                                 std::numeric_limits<double>::signaling_NaN(),\n                                 std::numeric_limits<double>::signaling_NaN()}};\n  double max_distance_from_center_ =\n      std::numeric_limits<double>::signaling_NaN();\n\n  friend bool operator==(const Poloidal& lhs, const Poloidal& rhs);\n  friend bool operator!=(const Poloidal& lhs, const Poloidal& rhs);\n};\n\n}  // namespace grmhd::AnalyticData::InitialMagneticFields\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Toroidal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Toroidal.hpp\"\n\n#include <memory>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace grmhd::AnalyticData::InitialMagneticFields {\n\nstd::unique_ptr<InitialMagneticField> Toroidal::get_clone() const {\n  return std::make_unique<Toroidal>(*this);\n}\n\nToroidal::Toroidal(CkMigrateMessage* msg) : InitialMagneticField(msg) {}\n\nvoid Toroidal::pup(PUP::er& p) {\n  InitialMagneticField::pup(p);\n  p | pressure_exponent_;\n  p | cutoff_pressure_;\n  p | vector_potential_amplitude_;\n  p | center_;\n  p | max_distance_from_center_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Toroidal::my_PUP_ID = 0;\n\nToroidal::Toroidal(const size_t pressure_exponent, const double cutoff_pressure,\n                   const double vector_potential_amplitude,\n                   const std::array<double, 3> center,\n                   const double max_distance_from_center)\n    : pressure_exponent_(pressure_exponent),\n      cutoff_pressure_(cutoff_pressure),\n      vector_potential_amplitude_(vector_potential_amplitude),\n      center_(center),\n      max_distance_from_center_(max_distance_from_center) {}\n\nvoid Toroidal::variables(const gsl::not_null<tnsr::I<DataVector, 3>*> result,\n                         const tnsr::I<DataVector, 3>& coords,\n                         const Scalar<DataVector>& pressure,\n                         const Scalar<DataVector>& sqrt_det_spatial_metric,\n                         const tnsr::i<DataVector, 3>& deriv_pressure) const {\n  ASSERT(result->get(0).size() == get(pressure).size(),\n         \"Result must be of size \" << get(pressure).size() << \" but got \"\n                                   << result->get(0).size());\n  variables_impl(result, coords, pressure, sqrt_det_spatial_metric,\n                 deriv_pressure);\n}\n\nvoid Toroidal::variables(const gsl::not_null<tnsr::I<double, 3>*> result,\n                         const tnsr::I<double, 3>& coords,\n                         const Scalar<double>& pressure,\n                         const Scalar<double>& sqrt_det_spatial_metric,\n                         const tnsr::i<double, 3>& deriv_pressure) const {\n  variables_impl(result, coords, pressure, sqrt_det_spatial_metric,\n                 deriv_pressure);\n}\n\ntemplate <typename DataType>\nvoid Toroidal::variables_impl(\n    const gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n    const tnsr::I<DataType, 3>& coords, const Scalar<DataType>& pressure,\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::i<DataType, 3>& deriv_pressure) const {\n  const size_t num_pts = get_size(get(pressure));\n\n  for (size_t i = 0; i < num_pts; ++i) {\n    const double pressure_i = get_element(get(pressure), i);\n    const double x = get_element(coords.get(0), i) - center_[0];\n    const double y = get_element(coords.get(1), i) - center_[1];\n    const double z = get_element(coords.get(2), i) - center_[2];\n    const double radius = sqrt(x * x + y * y + z * z);\n    if (pressure_i < cutoff_pressure_ or radius > max_distance_from_center_) {\n      continue;\n    }\n\n    // (p - p_c)^{n_s}\n    const double pressure_term =\n        2.0 * pow(pressure_i - cutoff_pressure_,\n                  static_cast<int>(pressure_exponent_));\n    // n_s * (p - p_c)^{n_s-1}\n    const double n_times_pressure_to_n_minus_1 =\n        static_cast<double>(pressure_exponent_) *\n        pow(pressure_i - cutoff_pressure_,\n            static_cast<int>(pressure_exponent_) - 1);\n\n    const double varpi_squared = square(x) + square(y);\n\n    const auto& dp_dx = get_element(deriv_pressure.get(0), i);\n    const auto& dp_dy = get_element(deriv_pressure.get(1), i);\n\n    // Assign Bx, By\n    get_element(magnetic_field->get(0), i) +=\n        vector_potential_amplitude_ *\n        (y * pressure_term +\n         varpi_squared * n_times_pressure_to_n_minus_1 * dp_dy) /\n        get_element(get(sqrt_det_spatial_metric), i);\n    get_element(magnetic_field->get(1), i) +=\n        vector_potential_amplitude_ *\n        (-(x * pressure_term +\n           varpi_squared * n_times_pressure_to_n_minus_1 * dp_dx)) /\n        get_element(get(sqrt_det_spatial_metric), i);\n  }\n}\n\nbool Toroidal::is_equal(const InitialMagneticField& rhs) const {\n  const auto& derived_ptr = dynamic_cast<const Toroidal* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\nbool operator==(const Toroidal& lhs, const Toroidal& rhs) {\n  return lhs.pressure_exponent_ == rhs.pressure_exponent_ and\n         lhs.cutoff_pressure_ == rhs.cutoff_pressure_ and\n         lhs.vector_potential_amplitude_ == rhs.vector_potential_amplitude_ and\n         lhs.center_ == rhs.center_ and\n         lhs.max_distance_from_center_ == rhs.max_distance_from_center_;\n}\n\nbool operator!=(const Toroidal& lhs, const Toroidal& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                  \\\n  template void Toroidal::variables_impl<DTYPE(data)>(        \\\n      gsl::not_null<tnsr::I<DTYPE(data), 3>*> magnetic_field, \\\n      const tnsr::I<DTYPE(data), 3>& coords,                  \\\n      const Scalar<DTYPE(data)>& pressure,                    \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric,     \\\n      const tnsr::i<DTYPE(data), 3>& deriv_pressure) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n\n}  // namespace grmhd::AnalyticData::InitialMagneticFields\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Toroidal.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::AnalyticData::InitialMagneticFields {\n\n/*!\n * \\brief %Toroidal magnetic field for GRMHD initial data.\n *\n * The vector potential has the form\n *\n * \\f{align*}{\n *  A_x & = A_y = 0 , \\\\\n *  A_z & = A_b \\varpi^2 \\max(p-p_{\\mathrm{cut}}, 0)^{n_s} ,\n * \\f}\n *\n * where \\f$A_b\\f$ controls the amplitude of the magnetic field,\n * \\f$\\varpi^2=x^2+y^2=r^2-z^2\\f$ is the cylindrical radius,\n * \\f$n_s\\f$ controls the degree of differentiability, and\n * \\f$p_{\\mathrm{cut}}\\f$ controls the pressure cutoff below which the magnetic\n * field is zero.\n *\n * On the region where the field is non-zero, the magnetic field is given by:\n *\n * \\f{align*}{\n *   B^x & = \\frac{A_c}{\\sqrt{\\gamma}} \\left[\n *           2y(p-p_{\\mathrm{cut}})^{n_s}\n *           + \\varpi^2 n_s (p-p_{\\mathrm{cut}})^{n_s-1} \\partial_y p \\right],\\\\\n *   B^y & = -\\frac{A_c}{\\sqrt{\\gamma}} \\left[\n *           2x(p-p_{\\mathrm{cut}})^{n_s}\n *           + \\varpi^2 n_s (p-p_{\\mathrm{cut}})^{n_s-1} \\partial_x p \\right],\\\\\n *   B^z & = 0 .\n * \\f}\n *\n * Note that the coordinates are relative to the `Center` passed in, so the\n * field can be centered about any arbitrary point. The field is also zero\n * outside of `MaxDistanceFromCenter`, so that compact support can be imposed if\n * necessary.\n *\n * \\warning This assumes the magnetic field is initialized, both in size and\n * value, before being passed into the `variables` function. This is so that\n * multiple magnetic fields can be superposed. Each magnetic field\n * configuration does a `+=` to make this possible.\n */\nclass Toroidal : public InitialMagneticField {\n public:\n  struct PressureExponent {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"The exponent n_s controlling the smoothness of the field\"};\n  };\n\n  struct CutoffPressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The pressure below which there is no magnetic field.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct VectorPotentialAmplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The amplitude A_b of the vector potential. This controls the magnetic \"\n        \"field strength.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The center of the magnetic field.\"};\n  };\n\n  struct MaxDistanceFromCenter {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The maximum distance from the center to compute the magnetic field. \"\n        \"Everywhere outside the field is set to zero.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options =\n      tmpl::list<PressureExponent, CutoffPressure, VectorPotentialAmplitude,\n                 Center, MaxDistanceFromCenter>;\n\n  static constexpr Options::String help = {\"Toroidal initial magnetic field\"};\n\n  Toroidal() = default;\n  Toroidal(const Toroidal& /*rhs*/) = default;\n  Toroidal& operator=(const Toroidal& /*rhs*/) = default;\n  Toroidal(Toroidal&& /*rhs*/) = default;\n  Toroidal& operator=(Toroidal&& /*rhs*/) = default;\n  ~Toroidal() override = default;\n\n  Toroidal(size_t pressure_exponent, double cutoff_pressure,\n           double vector_potential_amplitude, std::array<double, 3> center,\n           double max_distance_from_center);\n\n  auto get_clone() const -> std::unique_ptr<InitialMagneticField> override;\n\n  /// \\cond\n  explicit Toroidal(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Toroidal);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// Retrieve magnetic fields at `(x)`\n    void variables(gsl::not_null<tnsr::I<DataVector, 3>*> result,\n                 const tnsr::I<DataVector, 3>& coords,\n                 const Scalar<DataVector>& pressure,\n                 const Scalar<DataVector>& sqrt_det_spatial_metric,\n                 const tnsr::i<DataVector, 3>& deriv_pressure) const override;\n\n  /// Retrieve magnetic fields at `(x)`\n  void variables(gsl::not_null<tnsr::I<double, 3>*> result,\n                 const tnsr::I<double, 3>& coords,\n                 const Scalar<double>& pressure,\n                 const Scalar<double>& sqrt_det_spatial_metric,\n                 const tnsr::i<double, 3>& deriv_pressure) const override;\n\n  bool is_equal(const InitialMagneticField& rhs) const override;\n\n private:\n  template <typename DataType>\n  void variables_impl(gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n                      const tnsr::I<DataType, 3>& coords,\n                      const Scalar<DataType>& pressure,\n                      const Scalar<DataType>& sqrt_det_spatial_metric,\n                      const tnsr::i<DataType, 3>& deriv_pressure) const;\n\n  size_t pressure_exponent_ = std::numeric_limits<size_t>::max();\n  double cutoff_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double vector_potential_amplitude_ =\n      std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> center_{{std::numeric_limits<double>::signaling_NaN(),\n                                 std::numeric_limits<double>::signaling_NaN(),\n                                 std::numeric_limits<double>::signaling_NaN()}};\n  double max_distance_from_center_ =\n      std::numeric_limits<double>::signaling_NaN();\n\n  friend bool operator==(const Toroidal& lhs, const Toroidal& rhs);\n};\n\nbool operator!=(const Toroidal& lhs, const Toroidal& rhs);\n\n}  // namespace grmhd::AnalyticData::InitialMagneticFields\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/KhInstability.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/KhInstability.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace grmhd::AnalyticData {\nKhInstability::KhInstability(\n    const double adiabatic_index, const double strip_bimedian_height,\n    const double strip_thickness, const double strip_density,\n    const double strip_velocity, const double background_density,\n    const double background_velocity, const double pressure,\n    const double perturbation_amplitude, const double perturbation_width,\n    const std::array<double, 3>& magnetic_field)\n    : adiabatic_index_(adiabatic_index),\n      strip_bimedian_height_(strip_bimedian_height),\n      strip_half_thickness_(0.5 * strip_thickness),\n      strip_density_(strip_density),\n      strip_velocity_(strip_velocity),\n      background_density_(background_density),\n      background_velocity_(background_velocity),\n      pressure_(pressure),\n      perturbation_amplitude_(perturbation_amplitude),\n      perturbation_width_(perturbation_width),\n      magnetic_field_(magnetic_field),\n      equation_of_state_(adiabatic_index) {\n  ASSERT(strip_density_ > 0.0 and background_density_ > 0.0,\n         \"The mass density must be positive everywhere. Inner \"\n         \"density: \"\n             << strip_density_ << \", Outer density: \" << background_density_\n             << \".\");\n  ASSERT(pressure_ > 0.0, \"The pressure must be positive. The value given was \"\n                              << pressure_ << \".\");\n  ASSERT(perturbation_width_ > 0.0,\n         \"The damping factor must be positive. The value given was \"\n             << perturbation_width_ << \".\");\n  ASSERT(strip_thickness > 0.0,\n         \"The strip thickness must be positive. The value given was \"\n             << strip_thickness << \".\");\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> KhInstability::get_clone()\n    const {\n  return std::make_unique<KhInstability>(*this);\n}\n\nKhInstability::KhInstability(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid KhInstability::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | adiabatic_index_;\n  p | strip_bimedian_height_;\n  p | strip_half_thickness_;\n  p | strip_density_;\n  p | strip_velocity_;\n  p | background_density_;\n  p | background_velocity_;\n  p | pressure_;\n  p | perturbation_amplitude_;\n  p | perturbation_width_;\n  p | magnetic_field_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nKhInstability::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  auto result = make_with_value<Scalar<DataType>>(x, 0.0);\n  const size_t n_pts = get_size(get<0>(x));\n  for (size_t s = 0; s < n_pts; ++s) {\n    get_element(get(result), s) =\n        abs(get_element(get<1>(x), s) - strip_bimedian_height_) <\n                strip_half_thickness_\n            ? strip_density_\n            : background_density_;\n  }\n  return {std::move(result)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nKhInstability::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n\nreturn {make_with_value<Scalar<DataType>>(x, 0.1)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nKhInstability::variables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> KhInstability::variables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return make_with_value<Scalar<DataType>>(x, pressure_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nKhInstability::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  auto result = make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(x, 0.0);\n\n  const size_t n_pts = get_size(get<0>(x));\n  for (size_t s = 0; s < n_pts; ++s) {\n    get_element(get<0>(result), s) =\n        abs(get_element(get<1>(x), s) - strip_bimedian_height_) <\n                strip_half_thickness_\n            ? strip_velocity_\n            : background_velocity_;\n  }\n\n  const double one_over_two_sigma_squared = 0.5 / square(perturbation_width_);\n  const double strip_lower_bound =\n      strip_bimedian_height_ - strip_half_thickness_;\n  const double strip_upper_bound =\n      strip_bimedian_height_ + strip_half_thickness_;\n  get<1>(result) =\n      exp(-one_over_two_sigma_squared * square(get<1>(x) - strip_lower_bound)) +\n      exp(-one_over_two_sigma_squared * square(get<1>(x) - strip_upper_bound));\n  get<1>(result) *= perturbation_amplitude_ * sin(4.0 * M_PI * get<0>(x));\n\n  return {std::move(result)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nKhInstability::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  auto mag_field = make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(\n      get<0>(x), magnetic_field_[0]);\n  get<1>(mag_field) = magnetic_field_[1];\n  get<2>(mag_field) = magnetic_field_[2];\n  return mag_field;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nKhInstability::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return make_with_value<Scalar<DataType>>(x, 0.0);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nKhInstability::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  using velocity_tag = hydro::Tags::SpatialVelocity<DataType, 3>;\n  const auto velocity =\n      get<velocity_tag>(variables(x, tmpl::list<velocity_tag>{}));\n  return {hydro::lorentz_factor(dot_product(velocity, velocity))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nKhInstability::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  return hydro::relativistic_specific_enthalpy(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::SpecificInternalEnergy<DataType>>(variables(\n          x, tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\nPUP::able::PUP_ID KhInstability::my_PUP_ID = 0;\n\nbool operator==(const KhInstability& lhs, const KhInstability& rhs) {\n  // No comparison for equation_of_state_. Comparing adiabatic_index_ should\n  // suffice.\n  return lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.strip_bimedian_height_ == rhs.strip_bimedian_height_ and\n         lhs.strip_half_thickness_ == rhs.strip_half_thickness_ and\n         lhs.strip_density_ == rhs.strip_density_ and\n         lhs.strip_velocity_ == rhs.strip_velocity_ and\n         lhs.background_density_ == rhs.background_density_ and\n         lhs.background_velocity_ == rhs.background_velocity_ and\n         lhs.pressure_ == rhs.pressure_ and\n         lhs.perturbation_amplitude_ == rhs.perturbation_amplitude_ and\n         lhs.perturbation_width_ == rhs.perturbation_width_ and\n         lhs.magnetic_field_ == rhs.magnetic_field_;\n}\n\nbool operator!=(const KhInstability& lhs, const KhInstability& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                         \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>     \\\n      KhInstability::variables(                              \\\n          const tnsr::I<DTYPE(data), 3, Frame::Inertial>& x, \\\n          tmpl::list<TAG(data) < DTYPE(data)>>) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::DivergenceCleaningField, hydro::Tags::SpecificEnthalpy,\n     hydro::Tags::LorentzFactor))\n\n#define INSTANTIATE_VECTORS(_, data)                                         \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3, Frame::Inertial>> \\\n      KhInstability::variables(                                              \\\n          const tnsr::I<DTYPE(data), 3, Frame::Inertial>& x,                 \\\n          tmpl::list<TAG(data) < DTYPE(data), 3, Frame::Inertial>>) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef INSTANTIATE_VECTORS\n#undef INSTANTIATE_SCALARS\n#undef TAG\n#undef DTYPE\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/KhInstability.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for a Kelvin-Helmholtz instability simulation.\n *\n * This is similar to the data from Section 4.7 of \\cite Beckwith2011iy.\n * The initial state consists of a horizontal strip of mass\n * density \\f$\\rho_\\text{in}\\f$ moving with horizontal speed\n * \\f$v_{\\text{in}}\\f$. The rest of the fluid possesses mass density\n * \\f$\\rho_\\text{out}\\f$, and its horizontal velocity is \\f$v_{\\text{out}}\\f$,\n * both constant. Mathematically,\n *\n * \\f{align*}\n * \\rho(x, y) =\n * \\begin{cases}\n * \\rho_\\text{in}, & \\left|y - y_\\text{mid}\\right| < b/2\\\\\n * \\rho_\\text{out}, & \\text{otherwise},\n * \\end{cases}\n * \\f}\n *\n * and\n *\n * \\f{align*}\n * v_x(x, y) =\n * \\begin{cases}\n * v_{\\text{in}}, & \\left|y - y_\\text{mid}\\right| < b/2\\\\\n * v_{\\text{out}}, & \\text{otherwise},\n * \\end{cases}\n * \\f}\n *\n * where \\f$b > 0\\f$ is the thickness of the strip, and \\f$y = y_\\text{mid}\\f$\n * is its horizontal bimedian. The initial pressure is set equal to a constant,\n * and the system is evolved assuming an ideal fluid of known adiabatic index.\n * Finally, in order to excite the instability, the vertical velocity is\n * initialized to\n *\n * \\f{align*}\n * v_y(x, y) = A\\sin(4\\pi x)\n * \\left[\\exp\\left(-\\dfrac{(y - y_\\text{top})^2}{2\\sigma^2}\\right) +\n * \\exp\\left(-\\dfrac{(y - y_\\text{bot})^2}{2\\sigma^2}\\right)\\right],\n * \\f}\n *\n * whose net effect is to perturb the horizontal boundaries of the strip\n * periodically along the \\f$x-\\f$axis. Here \\f$A\\f$ is the amplitude,\n * \\f$\\sigma\\f$ is a characteristic length for the perturbation width,\n * and \\f$y_\\text{top} = y_\\text{mid} + b/2\\f$ and\n * \\f$y_\\text{bot} = y_\\text{mid} - b/2\\f$ are the vertical coordinates\n * of the top and bottom boundaries of the strip, respectively.\n *\n * A uniform magnetic field can be added.\n */\nclass KhInstability : public evolution::initial_data::InitialData,\n                      public MarkAsAnalyticData,\n                      public AnalyticDataBase,\n                      public hydro::TemperatureInitialization<KhInstability> {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  /// The adiabatic index of the fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the fluid.\"};\n  };\n\n  /// The vertical coordinate of the horizontal bimedian of the strip.\n  struct StripBimedianHeight {\n    using type = double;\n    static constexpr Options::String help = {\"The height of the strip center.\"};\n  };\n\n  /// The thickness of the strip.\n  struct StripThickness {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The thickness of the horizontal strip.\"};\n  };\n\n  /// The mass density in the strip\n  struct StripDensity {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The mass density in the horizontal strip.\"};\n  };\n\n  /// The velocity along \\f$x\\f$ in the strip\n  struct StripVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The velocity along x in the horizontal strip.\"};\n  };\n\n  /// The mass density outside of the strip\n  struct BackgroundDensity {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The mass density outside of the strip.\"};\n  };\n\n  /// The velocity along \\f$x\\f$ outside of the strip\n  struct BackgroundVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The velocity along x outside of the strip.\"};\n  };\n\n  /// The initial (constant) pressure of the fluid\n  struct Pressure {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The initial (constant) pressure.\"};\n  };\n\n  /// The amplitude of the perturbation\n  struct PerturbAmplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The amplitude of the perturbation.\"};\n  };\n\n  /// The characteristic length for the width of the perturbation\n  struct PerturbWidth {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The characteristic length for the width of the perturbation.\"};\n  };\n\n  /// The uniform magnetic field\n  struct MagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\"The uniform magnetic field.\"};\n  };\n\n  using options = tmpl::list<AdiabaticIndex, StripBimedianHeight,\n                             StripThickness, StripDensity, StripVelocity,\n                             BackgroundDensity, BackgroundVelocity, Pressure,\n                             PerturbAmplitude, PerturbWidth, MagneticField>;\n\n  static constexpr Options::String help = {\n      \"Initial data to simulate the magnetized KH instability.\"};\n\n  KhInstability() = default;\n  KhInstability(const KhInstability& /*rhs*/) = default;\n  KhInstability& operator=(const KhInstability& /*rhs*/) = default;\n  KhInstability(KhInstability&& /*rhs*/) = default;\n  KhInstability& operator=(KhInstability&& /*rhs*/) = default;\n  ~KhInstability() override = default;\n\n  KhInstability(double adiabatic_index, double strip_bimedian_height,\n                double strip_thickness, double strip_density,\n                double strip_velocity, double background_density,\n                double background_velocity, double pressure,\n                double perturbation_amplitude, double perturbation_width,\n                const std::array<double, 3>& magnetic_field);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit KhInstability(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(KhInstability);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve the GRMHD variables at a given position.\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n  /// @}\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<tmpl::list_contains_v<\n                gr::analytic_solution_tags<3, DataType>, Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n  const EquationsOfState::IdealFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  double strip_bimedian_height_ = std::numeric_limits<double>::signaling_NaN();\n  double strip_half_thickness_ = std::numeric_limits<double>::signaling_NaN();\n  double strip_density_ = std::numeric_limits<double>::signaling_NaN();\n  double strip_velocity_ = std::numeric_limits<double>::signaling_NaN();\n  double background_density_ = std::numeric_limits<double>::signaling_NaN();\n  double background_velocity_ = std::numeric_limits<double>::signaling_NaN();\n  double pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double perturbation_amplitude_ = std::numeric_limits<double>::signaling_NaN();\n  double perturbation_width_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  EquationsOfState::IdealFluid<true> equation_of_state_{};\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  friend bool operator==(const KhInstability& lhs, const KhInstability& rhs);\n\n  friend bool operator!=(const KhInstability& lhs, const KhInstability& rhs);\n};\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/MagneticFieldLoop.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticFieldLoop.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace grmhd::AnalyticData {\n\nMagneticFieldLoop::MagneticFieldLoop(\n    const double pressure, const double rest_mass_density,\n    const double adiabatic_index,\n    const std::array<double, 3>& advection_velocity,\n    const double magnetic_field_magnitude, const double inner_radius,\n    const double outer_radius, const Options::Context& context)\n    : pressure_(pressure),\n      rest_mass_density_(rest_mass_density),\n      adiabatic_index_(adiabatic_index),\n      advection_velocity_(advection_velocity),\n      magnetic_field_magnitude_(magnetic_field_magnitude),\n      inner_radius_(inner_radius),\n      outer_radius_(outer_radius),\n      equation_of_state_{adiabatic_index_} {\n  if (magnitude(advection_velocity) >= 1.0) {\n    PARSE_ERROR(context, \"MagneticFieldLoop: superluminal AdvectionVelocity = \"\n                             << advection_velocity);\n  }\n  if (inner_radius >= outer_radius) {\n    PARSE_ERROR(context, \"MagneticFieldLoop: InnerRadius of \"\n                             << inner_radius\n                             << \" is not less than OuterRadius of \"\n                             << outer_radius);\n  }\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nMagneticFieldLoop::get_clone() const {\n  return std::make_unique<MagneticFieldLoop>(*this);\n}\n\nMagneticFieldLoop::MagneticFieldLoop(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nvoid MagneticFieldLoop::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | pressure_;\n  p | rest_mass_density_;\n  p | adiabatic_index_;\n  p | advection_velocity_;\n  p | magnetic_field_magnitude_;\n  p | inner_radius_;\n  p | outer_radius_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, rest_mass_density_)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n\n  return {make_with_value<Scalar<DataType>>(x, 0.1)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  auto result = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  get<0>(result) = advection_velocity_[0];\n  get<1>(result) = advection_velocity_[1];\n  get<2>(result) = advection_velocity_[2];\n  return {std::move(result)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, pressure_)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  auto result = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  const DataType cylindrical_radius =\n      sqrt(square(get<0>(x)) + square(get<1>(x)));\n  for (size_t s = 0; s < get_size(cylindrical_radius); ++s) {\n    const double current_radius = get_element(cylindrical_radius, s);\n    // The data is ill-posed at the origin, so it is not clear what to do...\n    if (current_radius <= outer_radius_ and current_radius > inner_radius_) {\n      get_element(get<0>(result), s) = -magnetic_field_magnitude_ *\n                                       get_element(get<1>(x), s) /\n                                       current_radius;\n      get_element(get<1>(result), s) = magnetic_field_magnitude_ *\n                                       get_element(get<0>(x), s) /\n                                       current_radius;\n    }\n    if (current_radius <= inner_radius_) {\n      get_element(get<0>(result), s) = -magnetic_field_magnitude_ *\n                                       get_element(get<1>(x), s) /\n                                       inner_radius_;\n      get_element(get<1>(result), s) =\n          magnetic_field_magnitude_ * get_element(get<0>(x), s) / inner_radius_;\n    }\n  }\n  return {std::move(result)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  using velocity_tag = hydro::Tags::SpatialVelocity<DataType, 3>;\n  const auto velocity =\n      get<velocity_tag>(variables(x, tmpl::list<velocity_tag>{}));\n  return {hydro::lorentz_factor(dot_product(velocity, velocity))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nMagneticFieldLoop::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  return hydro::relativistic_specific_enthalpy(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::SpecificInternalEnergy<DataType>>(variables(\n          x, tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\nPUP::able::PUP_ID MagneticFieldLoop::my_PUP_ID = 0;\n\nbool operator==(const MagneticFieldLoop& lhs, const MagneticFieldLoop& rhs) {\n  // there is no comparison operator for the EoS, but should be okay as\n  // the adiabatic_indexs are compared\n  return lhs.pressure_ == rhs.pressure_ and\n         lhs.rest_mass_density_ == rhs.rest_mass_density_ and\n         lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.advection_velocity_ == rhs.advection_velocity_ and\n         lhs.magnetic_field_magnitude_ == rhs.magnetic_field_magnitude_ and\n         lhs.inner_radius_ == rhs.inner_radius_ and\n         lhs.outer_radius_ == rhs.outer_radius_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const MagneticFieldLoop& lhs, const MagneticFieldLoop& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                     \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>> \\\n      MagneticFieldLoop::variables(                      \\\n          const tnsr::I<DTYPE(data), 3>& x,              \\\n          tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::DivergenceCleaningField, hydro::Tags::LorentzFactor,\n     hydro::Tags::SpecificEnthalpy))\n\n#define INSTANTIATE_VECTORS(_, data)                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3>> \\\n      MagneticFieldLoop::variables(                         \\\n          const tnsr::I<DTYPE(data), 3>& x,                 \\\n          tmpl::list<TAG(data) < DTYPE(data), 3>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/MagneticFieldLoop.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for an advecting magnetic field loop.\n *\n * This test, originally proposed in \\cite Gardiner2005hy and presented in a\n * slightly modified form by \\cite Mignone2010br, a region with annular\n * cross section with the specified `InnerRadius` and `OuterRadius` is given a\n * non-zero azimuthal magnetic field of constant magnitude `MagFieldStrength`\n * with zero magnetic field outside the loop.  Inside the `InnerRadius` the\n * magnetic field strength falls to zero quadratically. The loop is embedded in\n * an ideal fluid with the given `AdiabaticIndex`, `RestMassDensity` and\n * `Pressure` with a uniform `AdvectionVelocity`.  The magnetic field loop\n * should advect across the grid, maintaining its shape and strength, as long\n * as the magnetic pressure is negligible compared to the thermal pressure.\n *\n * This test diagnoses how well the evolution scheme preserves the no-monopole\n * condition, as well as the diffusivity of the scheme.\n *\n * The standard test setup is done on \\f$x \\in [-1,1]\\f$, \\f$y \\in [-0.5,\n * 0.5]\\f$, with periodic boundary conditions and with the following values\n * given for the options:\n * -  InnerRadius: 0.06\n * -  OuterRadius: 0.3\n * -  RestMassDensity: 1.0\n * -  Pressure: 1.0\n * -  AdvectionVelocity: [0.08164965809277261, 0.040824829046386304, 0.0]\n * -  MagFieldStrength: 0.001\n * -  AdiabaticIndex: 1.66666666666666667\n *\n */\nclass MagneticFieldLoop\n    : public evolution::initial_data::InitialData,\n      public MarkAsAnalyticData,\n      public AnalyticDataBase,\n      public hydro::TemperatureInitialization<MagneticFieldLoop> {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  /// The pressure throughout the fluid.\n  struct Pressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The constant pressure throughout the fluid.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The rest mass density throughout the fluid.\n  struct RestMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The constant density throughout the fluid.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The adiabatic index for the ideal fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index for the ideal fluid.\"};\n    static type lower_bound() { return 1.0; }\n  };\n\n  /// The fluid velocity.\n  struct AdvectionVelocity {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\"The advection velocity.\"};\n    static type lower_bound() { return {{-1.0, -1.0, -1.0}}; }\n    static type upper_bound() { return {{1.0, 1.0, 1.0}}; }\n  };\n\n  /// The strength of the magnetic field.\n  struct MagFieldStrength {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The magnitude of the magnetic field.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The inner radius of the magnetic loop.\n  struct InnerRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The inner radius of the magnetic loop.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The outer radius of the magnetic loop.\n  struct OuterRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The outer radius of the magnetic loop.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options =\n      tmpl::list<Pressure, RestMassDensity, AdiabaticIndex, AdvectionVelocity,\n                 MagFieldStrength, InnerRadius, OuterRadius>;\n  static constexpr Options::String help = {\n      \"Periodic advection of a magnetic field loop in Minkowski.\"};\n\n  MagneticFieldLoop() = default;\n  MagneticFieldLoop(const MagneticFieldLoop& /*rhs*/) = default;\n  MagneticFieldLoop& operator=(const MagneticFieldLoop& /*rhs*/) = default;\n  MagneticFieldLoop(MagneticFieldLoop&& /*rhs*/) = default;\n  MagneticFieldLoop& operator=(MagneticFieldLoop&& /*rhs*/) = default;\n  ~MagneticFieldLoop() override = default;\n\n  MagneticFieldLoop(double pressure, double rest_mass_density,\n                    double adiabatic_index,\n                    const std::array<double, 3>& advection_velocity,\n                    double magnetic_field_magnitude, double inner_radius,\n                    double outer_radius, const Options::Context& context = {});\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit MagneticFieldLoop(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MagneticFieldLoop);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve the GRMHD variables at a given position.\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n  /// @}\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<tmpl::list_contains_v<\n                gr::analytic_solution_tags<3, DataType>, Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n  const EquationsOfState::IdealFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  double pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double rest_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> advection_velocity_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  double magnetic_field_magnitude_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double inner_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double outer_radius_ = std::numeric_limits<double>::signaling_NaN();\n\n  EquationsOfState::IdealFluid<true> equation_of_state_{};\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  friend bool operator==(const MagneticFieldLoop& lhs,\n                         const MagneticFieldLoop& rhs);\n\n  friend bool operator!=(const MagneticFieldLoop& lhs,\n                         const MagneticFieldLoop& rhs);\n};\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.hpp\"\n\n#include <cmath>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nScalar<DataType> compute_piecewise(const tnsr::I<DataType, 3>& x,\n                                   const double rotor_radius,\n                                   const double rotor_value,\n                                   const double background_value) {\n  const DataType cylindrical_radius =\n      sqrt(square(get<0>(x)) + square(get<1>(x)));\n  return Scalar<DataType>(rotor_value -\n                          (rotor_value - background_value) *\n                              step_function(cylindrical_radius - rotor_radius));\n}\n}  // namespace\n\nnamespace grmhd::AnalyticData {\n\nMagneticRotor::MagneticRotor(\n    const double rotor_radius, const double rotor_density,\n    const double background_density, const double pressure,\n    const double angular_velocity, const std::array<double, 3>& magnetic_field,\n    const double adiabatic_index, const Options::Context& context)\n    : rotor_radius_(rotor_radius),\n      rotor_density_(rotor_density),\n      background_density_(background_density),\n      pressure_(pressure),\n      angular_velocity_(angular_velocity),\n      magnetic_field_(magnetic_field),\n      adiabatic_index_(adiabatic_index),\n      equation_of_state_(adiabatic_index) {\n  if (fabs(rotor_radius * angular_velocity) >= 1.0) {\n    PARSE_ERROR(context,\n                \"MagneticRotor expects RotorRadius * | AngularVelocity | < 1, \"\n                \"but RotorRadius = \"\n                    << rotor_radius\n                    << \" and AngularVelocity = \" << angular_velocity);\n  }\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> MagneticRotor::get_clone()\n    const {\n  return std::make_unique<MagneticRotor>(*this);\n}\n\nMagneticRotor::MagneticRotor(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid MagneticRotor::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | rotor_radius_;\n  p | rotor_density_;\n  p | background_density_;\n  p | pressure_;\n  p | angular_velocity_;\n  p | magnetic_field_;\n  p | adiabatic_index_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nMagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  return compute_piecewise(x, rotor_radius_, rotor_density_,\n                           background_density_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nMagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n\n  return {make_with_value<Scalar<DataType>>(x, 0.1)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nMagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  Scalar<DataType> angular_velocity =\n      compute_piecewise(x, rotor_radius_, angular_velocity_, 0.0);\n  auto spatial_velocity = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  get<0>(spatial_velocity) = -get<1>(x) * get(angular_velocity);\n  get<1>(spatial_velocity) = get<0>(x) * get(angular_velocity);\n  return spatial_velocity;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nMagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> MagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return make_with_value<Scalar<DataType>>(x, pressure_);\n  ;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nMagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  auto magnetic_field = make_with_value<tnsr::I<DataType, 3>>(get<0>(x), 0.0);\n  get<0>(magnetic_field) = gsl::at(magnetic_field_, 0);\n  get<1>(magnetic_field) = gsl::at(magnetic_field_, 1);\n  get<2>(magnetic_field) = gsl::at(magnetic_field_, 2);\n  return magnetic_field;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nMagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nMagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  using velocity_tag = hydro::Tags::SpatialVelocity<DataType, 3>;\n  const auto velocity =\n      get<velocity_tag>(variables(x, tmpl::list<velocity_tag>{}));\n  return {hydro::lorentz_factor(dot_product(velocity, velocity))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nMagneticRotor::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  using density_tag = hydro::Tags::RestMassDensity<DataType>;\n  using energy_tag = hydro::Tags::SpecificInternalEnergy<DataType>;\n  using pressure_tag = hydro::Tags::Pressure<DataType>;\n  const auto data =\n      variables(x, tmpl::list<density_tag, energy_tag, pressure_tag>{});\n  return hydro::relativistic_specific_enthalpy(\n      get<density_tag>(data), get<energy_tag>(data), get<pressure_tag>(data));\n}\n\nPUP::able::PUP_ID MagneticRotor::my_PUP_ID = 0;\n\nbool operator==(const MagneticRotor& lhs, const MagneticRotor& rhs) {\n  // there is no comparison operator for the EoS, but should be okay as\n  // the adiabatic_indexs are compared\n  return lhs.rotor_radius_ == rhs.rotor_radius_ and\n         lhs.rotor_density_ == rhs.rotor_density_ and\n         lhs.background_density_ == rhs.background_density_ and\n         lhs.pressure_ == rhs.pressure_ and\n         lhs.angular_velocity_ == rhs.angular_velocity_ and\n         lhs.magnetic_field_ == rhs.magnetic_field_ and\n         lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const MagneticRotor& lhs, const MagneticRotor& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                          \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>                      \\\n      MagneticRotor::variables(const tnsr::I<DTYPE(data), 3>& x,              \\\n                               tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::DivergenceCleaningField, hydro::Tags::LorentzFactor,\n     hydro::Tags::SpecificEnthalpy))\n\n#define INSTANTIATE_VECTORS(_, data)                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3>> \\\n      MagneticRotor::variables(                             \\\n          const tnsr::I<DTYPE(data), 3>& x,                 \\\n          tmpl::list<TAG(data) < DTYPE(data), 3>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for a magnetic rotor.\n *\n * This is a test first described in \\cite Balsara1999 for classical MHD and\n * later generalised to relativistic MHD in \\cite DelZanna2002rv\n *\n * This effectively 2D test initially consists of an infinitely long cylinder of\n * radius `RotorRadius` rotating about the z-axis with the given\n * `AngularVelocity`. The rest mass density of the fluid inside the rotor,\n * `RotorDensity`, is higher than the `BackgroundDensity` outside of the rotor.\n * The fluid is at a constant `Pressure`.  The rotor is embedded in a constant\n * `MagneticField` (usually taken to be along the x-axis).  The fluid is an\n * ideal fluid with the given `AdiabaticIndex`.  Evolving the initial data,\n * magnetic braking will slow down the rotor, while dragging the magnetic field\n * lines.\n *\n * The standard test setup is done on a unit cube \\f$[-0.5,0.5]^3\\f$ with the\n * following values given for the options:\n * -  RotorRadius: 0.1\n * -  RotorDensity: 10.0\n * -  BackgroundDensity: 1.0\n * -  Pressure: 1.0\n * -  AngularVelocity: 9.95\n * -  MagneticField: [1.0, 0.0, 0.0]\n * -  AdiabaticIndex: 1.66666666666666667\n *\n * Note that \\cite Zanotti2016efficient uses different parameters,\n * -  RotorRadius: 0.1\n * -  RotorDensity: 10.0\n * -  BackgroundDensity: 1.0\n * -  Pressure: 1.0\n * -  AngularVelocity: 9.3\n * -  MagneticField: [1.0, 0.0, 0.0]\n * -  AdiabaticIndex: 1.333333333333333\n *\n * The magnetic field in the disk should rotate by about 90 degrees by t = 0.4.\n */\nclass MagneticRotor : public evolution::initial_data::InitialData,\n                      public MarkAsAnalyticData,\n                      public AnalyticDataBase,\n                      public hydro::TemperatureInitialization<MagneticRotor> {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  /// Radius of the rotor.\n  struct RotorRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial radius of the rotor.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Density inside the rotor.\n  struct RotorDensity {\n    using type = double;\n    static constexpr Options::String help = {\"Density inside RotorRadius.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Density outside the rotor.\n  struct BackgroundDensity {\n    using type = double;\n    static constexpr Options::String help = {\"Density outside RotorRadius.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Uniform pressure inside and outside the rotor.\n  struct Pressure {\n    using type = double;\n    static constexpr Options::String help = {\"Pressure.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Angular velocity inside the rotor.\n  struct AngularVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Angular velocity of matter inside RotorRadius\"};\n  };\n  /// The x,y,z components of the uniform magnetic field threading the matter.\n  struct MagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The x,y,z components of the uniform magnetic field.\"};\n  };\n  /// The adiabatic index of the ideal fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the ideal fluid.\"};\n    static type lower_bound() { return 1.0; }\n  };\n\n  using options =\n      tmpl::list<RotorRadius, RotorDensity, BackgroundDensity, Pressure,\n                 AngularVelocity, MagneticField, AdiabaticIndex>;\n\n  static constexpr Options::String help = {\n      \"Magnetic rotor analytic initial data.\"};\n\n  MagneticRotor() = default;\n  MagneticRotor(const MagneticRotor& /*rhs*/) = default;\n  MagneticRotor& operator=(const MagneticRotor& /*rhs*/) = default;\n  MagneticRotor(MagneticRotor&& /*rhs*/) = default;\n  MagneticRotor& operator=(MagneticRotor&& /*rhs*/) = default;\n  ~MagneticRotor() override = default;\n\n  MagneticRotor(double rotor_radius, double rotor_density,\n                double background_density, double pressure,\n                double angular_velocity,\n                const std::array<double, 3>& magnetic_field,\n                double adiabatic_index, const Options::Context& context = {});\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit MagneticRotor(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MagneticRotor);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve the GRMHD variables at a given position.\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n  /// @}\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<tmpl::list_contains_v<\n                gr::analytic_solution_tags<3, DataType>, Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n  const EquationsOfState::IdealFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  double rotor_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double rotor_density_ = std::numeric_limits<double>::signaling_NaN();\n  double background_density_ = std::numeric_limits<double>::signaling_NaN();\n  double pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double angular_velocity_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  EquationsOfState::IdealFluid<true> equation_of_state_{};\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  friend bool operator==(const MagneticRotor& lhs, const MagneticRotor& rhs);\n\n  friend bool operator!=(const MagneticRotor& lhs, const MagneticRotor& rhs);\n};\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace grmhd::AnalyticData {\n\nMagnetizedFmDisk::MagnetizedFmDisk(\n    const double bh_mass, const double bh_dimless_spin,\n    const double inner_edge_radius, const double max_pressure_radius,\n    const double polytropic_constant, const double polytropic_exponent,\n    const double noise, const double threshold_density,\n    const double inverse_plasma_beta, const size_t normalization_grid_res)\n    : fm_disk_(bh_mass, bh_dimless_spin, inner_edge_radius, max_pressure_radius,\n               polytropic_constant, polytropic_exponent, noise),\n      threshold_density_(threshold_density),\n      inverse_plasma_beta_(inverse_plasma_beta),\n      normalization_grid_res_(normalization_grid_res),\n      kerr_schild_coords_{bh_mass, bh_dimless_spin} {\n  ASSERT(threshold_density_ > 0.0 and threshold_density_ < 1.0,\n         \"The threshold density must be in the range (0, 1). The value given \"\n         \"was \"\n             << threshold_density_ << \".\");\n  ASSERT(inverse_plasma_beta_ >= 0.0,\n         \"The inverse plasma beta must be non-negative. The value given \"\n         \"was \"\n             << inverse_plasma_beta_ << \".\");\n  ASSERT(normalization_grid_res_ >= 4,\n         \"The grid resolution used in the magnetic field normalization must be \"\n         \"at least 4 points. The value given was \"\n             << normalization_grid_res_);\n\n  // The normalization of the magnetic field is determined by the condition\n  //\n  // plasma beta = pgas_max / pmag_max\n  //\n  // where pgas_max and pmag_max are the maximum values of the gas and magnetic\n  // pressure over the domain, respectively. pgas_max is obtained by\n  // evaluating the pressure of the Fishbone-Moncrief solution at the\n  // corresponding radius. In order to compare with other groups, pmag_max is\n  // obtained by precomputing the unnormalized magnetic field and then\n  // finding the maximum of the comoving field squared on a given grid.\n  // Following the CodeComparisonProject files, we choose to find the maximum\n  // on a spherical grid as described below.\n\n  // Set up a Kerr (\"spherical Kerr-Schild\") grid of constant phi = 0,\n  // whose parameterization in Cartesian Kerr-Schild coordinates is\n  // x(r, theta) = r sin_theta,\n  // y(r, theta) = a sin_theta,\n  // z(r, theta) = r cos_theta.\n  const double r_init = fm_disk_.inner_edge_radius_ * fm_disk_.bh_mass_;\n  const double r_fin = fm_disk_.max_pressure_radius_ * fm_disk_.bh_mass_;\n  const double dr = (r_fin - r_init) / normalization_grid_res_;\n  const double dtheta = M_PI / normalization_grid_res_;\n  const double cartesian_n_pts = square(normalization_grid_res_);\n  Index<2> extents(normalization_grid_res_, normalization_grid_res_);\n  auto grid =\n      make_with_value<tnsr::I<DataVector, 3>>(DataVector(cartesian_n_pts), 0.0);\n  for (size_t i = 0; i < normalization_grid_res_; ++i) {\n    double r_i = r_init + static_cast<double>(i) * dr;\n    for (size_t j = 0; j < normalization_grid_res_; ++j) {\n      double sin_theta_j = sin(static_cast<double>(j) * dtheta);\n      auto index = collapsed_index(Index<2>{i, j}, extents);\n      get<0>(grid)[index] = r_i * sin_theta_j;\n      get<1>(grid)[index] = fm_disk_.bh_spin_a_ * sin_theta_j;\n      get<2>(grid)[index] = r_i * cos(static_cast<double>(j) * dtheta);\n    }\n  }\n\n  const auto b_field = unnormalized_magnetic_field(grid);\n  const auto unmagnetized_vars = variables(\n      grid, tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                       hydro::Tags::LorentzFactor<DataVector>,\n                       hydro::Tags::SpatialVelocity<DataVector, 3>>{});\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(unmagnetized_vars);\n\n  const tnsr::I<double, 3> x_max{\n      {{fm_disk_.max_pressure_radius_, 0.0, 0.0}}};\n\n  const double b_squared_max = max(\n      get(dot_product(b_field, b_field, spatial_metric)) /\n          square(get(\n              get<hydro::Tags::LorentzFactor<DataVector>>(unmagnetized_vars))) +\n      square(get(dot_product(\n          b_field,\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(unmagnetized_vars),\n          spatial_metric))));\n  ASSERT(b_squared_max > 0.0, \"Max b squared is zero.\");\n  b_field_normalization_ =\n      sqrt(2.0 *\n           get(get<hydro::Tags::Pressure<double>>(\n               variables(x_max, tmpl::list<hydro::Tags::Pressure<double>>{}))) *\n           inverse_plasma_beta_ / b_squared_max);\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nMagnetizedFmDisk::get_clone() const {\n  return std::make_unique<MagnetizedFmDisk>(*this);\n}\n\nMagnetizedFmDisk::MagnetizedFmDisk(CkMigrateMessage* msg) : fm_disk_(msg) {}\n\nvoid MagnetizedFmDisk::pup(PUP::er& p) {\n  p | fm_disk_;\n  p | threshold_density_;\n  p | inverse_plasma_beta_;\n  p | b_field_normalization_;\n  p | normalization_grid_res_;\n  p | kerr_schild_coords_;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> MagnetizedFmDisk::unnormalized_magnetic_field(\n    const tnsr::I<DataType, 3>& x) const {\n  auto magnetic_field =\n      make_with_value<tnsr::I<DataType, 3, Frame::NoFrame>>(x, 0.0);\n\n  auto x_ks = x;\n\n  // The maximum pressure (and hence the maximum rest mass density) is located\n  // on the ring x^2 + y^2 = r_max^2, z = 0.\n  // Note that `x` may or may not include points on this ring.\n  const tnsr::I<double, 3> x_max{\n      {{fm_disk_.max_pressure_radius_, 0.0, 0.0}}};\n  const double threshold_rest_mass_density =\n      threshold_density_ *\n      get(get<hydro::Tags::RestMassDensity<double>>(variables(\n          x_max, tmpl::list<hydro::Tags::RestMassDensity<double>>{})));\n\n  const double inner_edge_potential =\n      fm_disk_.potential(square(fm_disk_.inner_edge_radius_), 1.0);\n\n  // A_phi \\propto rho - rho_threshold. Normalization comes later.\n  const auto mag_potential = [this, &threshold_rest_mass_density,\n                              &inner_edge_potential](\n                                 const double r,\n                                 const double sin_theta_squared) {\n    // enthalpy = exp(Win - W(r,theta)), as in the Fishbone-Moncrief disk\n    return get(equation_of_state().rest_mass_density_from_enthalpy(\n               Scalar<double>{\n                   exp(inner_edge_potential -\n                       fm_disk_.potential(square(r), sin_theta_squared))})) -\n           threshold_rest_mass_density;\n  };\n\n  // The magnetic field is present only within the disk, where the\n  // rest mass density is greater than the threshold rest mass density.\n  const DataType rest_mass_density =\n      get(get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})));\n  for (size_t s = 0; s < get_size(get<0>(x)); ++s) {\n    if (get_element(rest_mass_density, s) > threshold_rest_mass_density) {\n      // The magnetic field is defined in terms of the Faraday tensor in Kerr\n      // (r, theta, phi) coordinates. We need to get B^r, B^theta, B^phi first,\n      // then we transform to Cartesian coordinates.\n      const double z_squared = square(get_element(get<2>(x), s));\n      double sin_theta_squared =\n          square(get_element(get<0>(x), s)) + square(get_element(get<1>(x), s));\n      double r_squared = sin_theta_squared + z_squared;\n      sin_theta_squared /= r_squared;\n\n      // B^i is proportional to derivatives of the magnetic potential. One has\n      //\n      // B^r = sqrt(gamma) * \\partial_\\theta A_\\phi\n      // B^\\theta = -sqrt(gamma) * \\partial_r A_\\phi\n      //\n      // where sqrt(gamma) = sqrt( sigma (sigma + 2Mr) ) * sin\\theta.\n      const double radius = sqrt(r_squared);\n      const double sin_theta = sqrt(sin_theta_squared);\n      double prefactor =\n          r_squared + square(fm_disk_.bh_spin_a_) * z_squared / r_squared;\n      prefactor *= (prefactor + 2.0 * fm_disk_.bh_mass_ * radius);\n\n      // As done by SpEC and other groups, we approximate the derivatives\n      // with a 2nd-order centered finite difference expression.\n      // For simplicity, we set \\delta r = \\delta\\theta = small number.\n      const double small = 0.0001 * fm_disk_.bh_mass_;\n      prefactor = 2.0 * small * sqrt(prefactor) * sin_theta;\n      prefactor = 1.0 / prefactor;\n\n      // Since the metric, and thus the field, depend on theta through\n      // sin^2(theta) only, we use chain rule in B^\\theta and write\n      //\n      // d/d\\theta = 2 * sin\\theta * cos\\theta * d/d(sin^2(theta)),\n      //\n      // where cos\\theta = z/r.\n      get_element(get<0>(magnetic_field), s) =\n          2.0 * prefactor * sin_theta * get_element(get<2>(x), s) *\n          (mag_potential(radius, sin_theta_squared + small) -\n           mag_potential(radius, sin_theta_squared - small)) /\n          radius;\n      get_element(get<1>(magnetic_field), s) =\n          prefactor * (mag_potential(radius - small, sin_theta_squared) -\n                       mag_potential(radius + small, sin_theta_squared));\n      // phi component of the field vanishes identically.\n\n      // Need x in KS coordinates instead of SKS coordinates\n      // to do transformation below. copy x into x_sks and alter at each\n      // element index s.\n      const double sks_to_ks_factor =\n          sqrt(r_squared + square(fm_disk_.bh_spin_a_)) / radius;\n      get_element(x_ks.get(0), s) = get_element(x.get(0), s) * sks_to_ks_factor;\n      get_element(x_ks.get(1), s) = get_element(x.get(1), s) * sks_to_ks_factor;\n      get_element(x_ks.get(2), s) = get_element(x.get(2), s);\n    }\n  }\n  // magnetic field in KS_coords\n  // change back to SKS_coords\n  const auto magnetic_field_ks =\n      kerr_schild_coords_.cartesian_from_spherical_ks(std::move(magnetic_field),\n                                                      x_ks);\n\n  using inv_jacobian =\n      gr::Solutions::SphericalKerrSchild::internal_tags::inv_jacobian<\n          DataType, Frame::Inertial>;\n\n  FmDisk::IntermediateVariables<DataType> vars(x);\n\n  const auto inv_jacobians =\n      get<inv_jacobian>(fm_disk_.background_spacetime_.variables(\n          x, 0.0, tmpl::list<inv_jacobian>{},\n          make_not_null(&vars.sph_kerr_schild_cache)));\n\n  auto magnetic_field_sks =\n      make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(x, 0.0);\n\n  for (size_t j = 0; j < 3; ++j) {\n    for (size_t i = 0; i < 3; ++i) {\n      magnetic_field_sks.get(j) +=\n          inv_jacobians.get(j, i) * magnetic_field_ks.get(i);\n    }\n  }\n\n  return magnetic_field_sks;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nMagnetizedFmDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/,\n    gsl::not_null<FmDisk::IntermediateVariables<DataType>*> /*vars*/) const {\n  auto result = unnormalized_magnetic_field(x);\n  for (size_t i = 0; i < 3; ++i) {\n    result.get(i) *= b_field_normalization_;\n  }\n  return result;\n}\n\nPUP::able::PUP_ID MagnetizedFmDisk::my_PUP_ID = 0;\n\nbool operator==(const MagnetizedFmDisk& lhs, const MagnetizedFmDisk& rhs) {\n  return lhs.fm_disk_ == rhs.fm_disk_ and\n         lhs.threshold_density_ == rhs.threshold_density_ and\n         lhs.inverse_plasma_beta_ == rhs.inverse_plasma_beta_ and\n         lhs.b_field_normalization_ == rhs.b_field_normalization_ and\n         lhs.normalization_grid_res_ == rhs.normalization_grid_res_;\n}\n\nbool operator!=(const MagnetizedFmDisk& lhs, const MagnetizedFmDisk& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template tuples::TaggedTuple<hydro::Tags::MagneticField<DTYPE(data), 3>>   \\\n  MagnetizedFmDisk::variables(                                               \\\n      const tnsr::I<DTYPE(data), 3>& x,                                      \\\n      tmpl::list<hydro::Tags::MagneticField<DTYPE(data), 3>> /*meta*/,       \\\n      gsl::not_null<                                                         \\\n          FmDisk::FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrSchildCoords.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Magnetized fluid disk orbiting a Kerr black hole.\n *\n * In the context of simulating accretion disks, this class implements a widely\n * used (e.g. \\cite Gammie2003, \\cite Porth2016rfi, \\cite White2015omx)\n * initial setup for the GRMHD variables, consisting of a Fishbone-Moncrief disk\n * \\cite Fishbone1976apj (see also\n * RelativisticEuler::Solutions::FishboneMoncriefDisk),\n * threaded by a weak poloidal magnetic field. The magnetic field is constructed\n * from an axially symmetric toroidal magnetic potential which, in Kerr\n * (\"spherical Kerr-Schild\") coordinates, has the form\n *\n * \\f{align*}\n * A_\\phi(r,\\theta) \\propto \\text{max}(\\rho(r,\\theta) - \\rho_\\text{thresh}, 0),\n * \\f}\n *\n * where \\f$\\rho_\\text{thresh}\\f$ is a user-specified threshold density that\n * confines the magnetic flux to exist inside of the fluid disk only. A commonly\n * used value for this parameter is\n * \\f$\\rho_\\text{thresh} = 0.2\\rho_\\text{max}\\f$, where \\f$\\rho_\\text{max}\\f$\n * is the maximum value of\n * the rest mass density in the disk. Using this potential, the Eulerian\n * magnetic field takes the form\n *\n * \\f{align*}\n * B^r = \\frac{F_{\\theta\\phi}}{\\sqrt{\\gamma}},\\quad\n * B^\\theta = \\frac{F_{\\phi r}}{\\sqrt{\\gamma}},\\quad B^\\phi = 0,\n * \\f}\n *\n * where \\f$F_{ij} = \\partial_i A_j - \\partial_j A_i\\f$ are the spatial\n * components of the Faraday tensor, and \\f$\\gamma\\f$ is the determinant of the\n * spatial metric. The magnetic field is then normalized so that the\n * plasma-\\f$\\beta\\f$ parameter, \\f$\\beta = 2p/b^2\\f$, equals some value\n * specified by the user. Here, \\f$p\\f$ is the fluid pressure, and\n *\n * \\f{align*}\n * b^2 = b^\\mu b_\\mu = \\frac{B_iB^i}{W^2} + (B^iv_i)^2\n * \\f}\n *\n * is the norm of the magnetic field in the fluid frame, with \\f$v_i\\f$ being\n * the spatial velocity, and \\f$W\\f$ the Lorentz factor.\n *\n * \\note When using Kerr-Schild coordinates, the horizon that is at\n * constant \\f$r\\f$ is not spherical, but instead spheroidal. This could make\n * application of boundary condition and computing various fluxes\n * across the horizon more complicated than they need to be.\n * Thus, similar to RelativisticEuler::Solutions::FishboneMoncriefDisk\n * we use Spherical Kerr-Schild coordinates,\n * see gr::Solutions::SphericalKerrSchild, in which constant \\f$r\\f$\n * is spherical. Because we compute variables in Kerr-Schild coordinates,\n * there is a necessary extra step of transforming them back to\n * Spherical Kerr-Schild coordinates.\n *\n * \\warning Spherical Kerr-Schild coordinates and \"spherical Kerr-Schild\"\n * coordinates are not same.\n *\n */\nclass MagnetizedFmDisk : public virtual evolution::initial_data::InitialData,\n                         public MarkAsAnalyticData {\n private:\n  using FmDisk = RelativisticEuler::Solutions::FishboneMoncriefDisk;\n\n public:\n  /// The rest mass density (in units of the maximum rest mass density in the\n  /// disk) below which the matter in the disk is initially unmagetized.\n  struct ThresholdDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Frac. rest mass density below which B-field vanishes.\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n  /// The maximum-magnetic-pressure-to-maximum-fluid-pressure ratio.\n  struct InversePlasmaBeta {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Ratio of max magnetic pressure to max fluid pressure.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// Grid resolution used in magnetic field normalization.\n  struct BFieldNormGridRes {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"Grid Resolution for b-field normalization.\"};\n    static type suggested_value() { return 255; }\n    static type lower_bound() { return 4; }\n  };\n\n  // Unlike the other analytic data classes, we cannot get these from the\n  // `AnalyticDataBase` because this case causes clang-tidy to believe that\n  // there is an ambiguous inheritance problem\n  static constexpr size_t volume_dim = 3_st;\n\n  template <typename DataType>\n  using tags =\n      tmpl::push_back<typename gr::AnalyticSolution<3>::template tags<DataType>,\n                      hydro::Tags::RestMassDensity<DataType>,\n                      hydro::Tags::ElectronFraction<DataType>,\n                      hydro::Tags::SpecificInternalEnergy<DataType>,\n                      hydro::Tags::Temperature<DataType>,\n                      hydro::Tags::Pressure<DataType>,\n                      hydro::Tags::SpatialVelocity<DataType, 3>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>,\n                      hydro::Tags::LorentzFactor<DataType>,\n                      hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  using options = tmpl::push_back<FmDisk::options, ThresholdDensity,\n                                  InversePlasmaBeta, BFieldNormGridRes>;\n\n  static constexpr Options::String help = {\n      \"Magnetized Fishbone-Moncrief disk.\"};\n\n  MagnetizedFmDisk() = default;\n  MagnetizedFmDisk(const MagnetizedFmDisk& /*rhs*/) = default;\n  MagnetizedFmDisk& operator=(const MagnetizedFmDisk& /*rhs*/) = default;\n  MagnetizedFmDisk(MagnetizedFmDisk&& /*rhs*/) = default;\n  MagnetizedFmDisk& operator=(MagnetizedFmDisk&& /*rhs*/) = default;\n  ~MagnetizedFmDisk() override = default;\n\n  MagnetizedFmDisk(\n      double bh_mass, double bh_dimless_spin, double inner_edge_radius,\n      double max_pressure_radius, double polytropic_constant,\n      double polytropic_exponent, double noise, double threshold_density,\n      double inverse_plasma_beta,\n      size_t normalization_grid_res = BFieldNormGridRes::suggested_value());\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit MagnetizedFmDisk(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MagnetizedFmDisk);\n  /// \\endcond\n\n  // Overload the variables function from the base class.\n  using equation_of_state_type = typename FmDisk::equation_of_state_type;\n\n  /// @{\n  /// The variables in Cartesian Kerr-Schild coordinates at `(x, t)`.\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    // Can't store IntermediateVariables as member variable because we\n    // need to be threadsafe.\n    typename FmDisk::IntermediateVariables<DataType> vars(x);\n    return {std::move(get<Tags>([this, &x, &vars]() {\n      if constexpr (std::is_same_v<hydro::Tags::MagneticField<DataType, 3>,\n                                   Tags>) {\n        return variables(x, tmpl::list<Tags>{}, make_not_null(&vars));\n      } else {\n        return fm_disk_.variables(x, tmpl::list<Tags>{}, make_not_null(&vars));\n      }\n    }()))...};\n  }\n\n  template <typename DataType, typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    // Can't store IntermediateVariables as member variable because we need to\n    // be threadsafe.\n    typename FmDisk::IntermediateVariables<DataType> vars(x);\n    if constexpr (std::is_same_v<hydro::Tags::MagneticField<DataType, 3>,\n                                 Tag>) {\n      return variables(x, tmpl::list<Tag>{}, make_not_null(&vars));\n    } else {\n      return fm_disk_.variables(x, tmpl::list<Tag>{}, make_not_null(&vars));\n    }\n  }\n  /// @}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n  const equation_of_state_type& equation_of_state() const {\n    return fm_disk_.equation_of_state();\n  }\n\n private:\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/,\n                 gsl::not_null<FmDisk::IntermediateVariables<DataType>*> vars)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  tnsr::I<DataType, 3> unnormalized_magnetic_field(\n      const tnsr::I<DataType, 3>& x) const;\n\n  friend bool operator==(const MagnetizedFmDisk& lhs,\n                         const MagnetizedFmDisk& rhs);\n\n  RelativisticEuler::Solutions::FishboneMoncriefDisk fm_disk_{};\n  double threshold_density_ = std::numeric_limits<double>::signaling_NaN();\n  double inverse_plasma_beta_ = std::numeric_limits<double>::signaling_NaN();\n  double b_field_normalization_ = std::numeric_limits<double>::signaling_NaN();\n  size_t normalization_grid_res_ = 255;\n  gr::KerrSchildCoords kerr_schild_coords_{};\n};\n\nbool operator!=(const MagnetizedFmDisk& lhs, const MagnetizedFmDisk& rhs);\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace grmhd::AnalyticData {\nMagnetizedTovStar::MagnetizedTovStar() = default;\nMagnetizedTovStar::MagnetizedTovStar(MagnetizedTovStar&& /*rhs*/) = default;\nMagnetizedTovStar& MagnetizedTovStar::operator=(MagnetizedTovStar&& /*rhs*/) =\n    default;\nMagnetizedTovStar::~MagnetizedTovStar() = default;\n\nMagnetizedTovStar::MagnetizedTovStar(\n    const double central_rest_mass_density,\n    std::unique_ptr<MagnetizedTovStar::equation_of_state_type>\n        equation_of_state,\n    const RelativisticEuler::Solutions::TovCoordinates coordinate_system,\n    std::vector<std::unique_ptr<\n        grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField>>\n        magnetic_fields)\n    : tov_star(central_rest_mass_density, std::move(equation_of_state),\n               coordinate_system),\n      magnetic_fields_(std::move(magnetic_fields)) {}\n\nMagnetizedTovStar::MagnetizedTovStar(const MagnetizedTovStar& rhs)\n    : evolution::initial_data::InitialData{rhs},\n      RelativisticEuler::Solutions::TovStar(\n          static_cast<const RelativisticEuler::Solutions::TovStar&>(rhs)),\n      magnetic_fields_(clone_unique_ptrs(rhs.magnetic_fields_)) {}\n\nMagnetizedTovStar& MagnetizedTovStar::operator=(const MagnetizedTovStar& rhs) {\n  if (this == &rhs) {\n    return *this;\n  }\n  static_cast<RelativisticEuler::Solutions::TovStar&>(*this) =\n      static_cast<const RelativisticEuler::Solutions::TovStar&>(rhs);\n  magnetic_fields_ = clone_unique_ptrs(rhs.magnetic_fields_);\n  return *this;\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nMagnetizedTovStar::get_clone() const {\n  return std::make_unique<MagnetizedTovStar>(*this);\n}\n\nMagnetizedTovStar::MagnetizedTovStar(CkMigrateMessage* msg) : tov_star(msg) {}\n\nvoid MagnetizedTovStar::pup(PUP::er& p) {\n  tov_star::pup(p);\n  p | magnetic_fields_;\n}\n\nnamespace magnetized_tov_detail {\ntemplate <typename DataType, StarRegion Region>\nvoid MagnetizedTovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n    const gsl::not_null<Cache*> cache,\n    hydro::Tags::MagneticField<DataType, 3> /*meta*/) const {\n  const Scalar<DataType>& sqrt_det_spatial_metric =\n      cache->get_var(*this, gr::Tags::SqrtDetSpatialMetric<DataType>{});\n  const Scalar<DataType>& pressure =\n      cache->get_var(*this, hydro::Tags::Pressure<DataType>{});\n  const auto& deriv_pressure =\n      cache->get_var(*this, ::Tags::deriv<hydro::Tags::Pressure<DataType>,\n                                          tmpl::size_t<3>, Frame::Inertial>{});\n  set_number_of_grid_points(magnetic_field, get_size(get<0>(coords)));\n  for (size_t i = 0; i < 3; ++i) {\n    magnetic_field->get(i) = 0.0;\n  }\n\n  for (const auto& magnetic_field_compute : magnetic_fields) {\n    magnetic_field_compute->variables(magnetic_field, coords, pressure,\n                                      sqrt_det_spatial_metric, deriv_pressure);\n  }\n}\n}  // namespace magnetized_tov_detail\n\nPUP::able::PUP_ID MagnetizedTovStar::my_PUP_ID = 0;\n\nbool operator==(const MagnetizedTovStar& lhs, const MagnetizedTovStar& rhs) {\n  bool equal =\n      static_cast<const typename MagnetizedTovStar::tov_star&>(lhs) ==\n          static_cast<const typename MagnetizedTovStar::tov_star&>(rhs) and\n      lhs.magnetic_fields_.size() == rhs.magnetic_fields_.size();\n  if (not equal) {\n    return false;\n  }\n  for (size_t i = 0; i < lhs.magnetic_fields_.size(); ++i) {\n    if (not lhs.magnetic_fields_[i]->is_equal(*rhs.magnetic_fields_[i])) {\n      return false;\n    }\n  }\n  return true;\n}\n\nbool operator!=(const MagnetizedTovStar& lhs, const MagnetizedTovStar& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define REGION(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template class magnetized_tov_detail::MagnetizedTovVariables<DTYPE(data), \\\n                                                               REGION(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector),\n                        (magnetized_tov_detail::StarRegion::Center,\n                         magnetized_tov_detail::StarRegion::Interior,\n                         magnetized_tov_detail::StarRegion::Exterior))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef REGION\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\nnamespace magnetized_tov_detail {\n\nusing StarRegion = RelativisticEuler::Solutions::tov_detail::StarRegion;\n\ntemplate <typename DataType, StarRegion Region>\nstruct MagnetizedTovVariables\n    : RelativisticEuler::Solutions::tov_detail::TovVariables<DataType, Region> {\n  static constexpr size_t Dim = 3;\n  using Base =\n      RelativisticEuler::Solutions::tov_detail::TovVariables<DataType, Region>;\n  using Cache = typename Base::Cache;\n  using Base::operator();\n  using Base::coords;\n  using Base::eos;\n  using Base::radial_solution;\n  using Base::radius;\n\n  const std::vector<std::unique_ptr<\n      grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField>>&\n      magnetic_fields;\n\n  MagnetizedTovVariables(\n      const tnsr::I<DataType, 3>& local_x, const DataType& local_radius,\n      const RelativisticEuler::Solutions::TovSolution& local_radial_solution,\n      const EquationsOfState::EquationOfState<true, 1>& local_eos,\n      const std::vector<std::unique_ptr<\n          grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField>>&\n          mag_fields)\n      : Base(local_x, local_radius, local_radial_solution, local_eos),\n        magnetic_fields(mag_fields) {}\n\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n      gsl::not_null<Cache*> cache,\n      hydro::Tags::MagneticField<DataType, 3> /*meta*/) const override;\n};\n\n}  // namespace magnetized_tov_detail\n\n/*!\n * \\brief Magnetized TOV star initial data, where metric terms only account for\n * the hydrodynamics not the magnetic fields.\n *\n * Superposes magnetic fields on top of a TOV solution. These can be any of\n * the classes derived from\n * grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField\n *\n * ### Conversion to CGS units and values for poloidal magnetic field\n *\n * While the amplitude \\f$A_b\\f$ is specified in the code, it is more natural\n * to work with the magnetic field strength, which is given by \\f$\\sqrt{b^2}\\f$\n * (where \\f$b^a\\f$ is the comoving magnetic field), and in CGS units is\n *\n * \\f{align*}{\n *  |B_{\\mathrm{CGS}}|&= \\sqrt{4 \\pi b^2}\n *   \\left(\\frac{c^2}{G M_\\odot}\\right) \\left(\\frac{c}{\\sqrt{4 \\pi \\epsilon_0\n *    G}}\\right) \\\\\n *   &= \\sqrt{b^2} \\times 8.352\\times10^{19}\\mathrm{G} \\,.\n * \\f}\n *\n * We now give values used for standard tests of magnetized stars with a\n * poloidal magnetic field.\n * - \\f$\\rho_c(0)=1.28\\times10^{-3}\\f$\n * - \\f$K=100\\f$\n * - \\f$\\Gamma=2\\f$\n * - %Domain \\f$[-20,20]^3\\f$\n * - Units \\f$M=M_\\odot\\f$\n * - A target final time 20ms means \\f$20\\times10^{-3}/(5\\times10^{-6})=4000M\\f$\n * - The mass of the star is \\f$1.4M_{\\odot}\\f$\n *\n * Parameters for desired magnetic field strength:\n * - For \\f$n_s=0\\f$ and \\f$p_{\\mathrm{cut}}=0.04p_{\\max}\\f$ setting\n *   \\f$A_b=6\\times10^{-5}\\f$ yields a maximum mganetic field strength of\n *   \\f$1.002\\times10^{16}G\\f$.\n * - For \\f$n_s=1\\f$ and \\f$p_{\\mathrm{cut}}=0.04p_{\\max}\\f$ setting\n *   \\f$A_b=0.4\\f$ yields a maximum mganetic field strength of\n *   \\f$1.05\\times10^{16}G\\f$.\n * - For \\f$n_s=2\\f$ and \\f$p_{\\mathrm{cut}}=0.04p_{\\max}\\f$ setting\n *   \\f$A_b=2500\\f$ yields a maximum mganetic field strength of\n *   \\f$1.03\\times10^{16}G\\f$.\n * - For \\f$n_s=3\\f$ and \\f$p_{\\mathrm{cut}}=0.04p_{\\max}\\f$ setting\n *   \\f$A_b=1.65\\times10^{7}\\f$ yields a maximum mganetic field strength of\n *   \\f$1.07\\times10^{16}G\\f$.\n *\n * Note that the magnetic field strength goes as \\f$A_b\\f$ so any desired value\n * can be achieved by a linear scaling.\n */\nclass MagnetizedTovStar : public virtual evolution::initial_data::InitialData,\n                          public MarkAsAnalyticData,\n                          private RelativisticEuler::Solutions::TovStar {\n private:\n  using tov_star = RelativisticEuler::Solutions::TovStar;\n\n public:\n  struct MagneticFields {\n    using type = std::vector<std::unique_ptr<\n        grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField>>;\n    static constexpr Options::String help = {\n        \"Magnetic fields to superpose on the TOV solution.\"};\n  };\n\n  using options = tmpl::push_back<tov_star::options, MagneticFields>;\n\n  static constexpr Options::String help = {\"Magnetized TOV star.\"};\n\n  static constexpr size_t volume_dim = 3_st;\n\n  template <typename DataType>\n  using tags = typename tov_star::template tags<DataType>;\n\n  MagnetizedTovStar();\n  MagnetizedTovStar(const MagnetizedTovStar& rhs);\n  MagnetizedTovStar& operator=(const MagnetizedTovStar& rhs);\n  MagnetizedTovStar(MagnetizedTovStar&& /*rhs*/);\n  MagnetizedTovStar& operator=(MagnetizedTovStar&& /*rhs*/);\n  ~MagnetizedTovStar() override;\n\n  MagnetizedTovStar(\n      double central_rest_mass_density,\n      std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>\n          equation_of_state,\n      RelativisticEuler::Solutions::TovCoordinates coordinate_system,\n      std::vector<std::unique_ptr<\n          grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField>>\n          magnetic_fields);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit MagnetizedTovStar(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MagnetizedTovStar);\n  /// \\endcond\n\n  using tov_star::equation_of_state;\n  using tov_star::equation_of_state_type;\n\n  /// Retrieve a collection of variables at `(x)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    return variables_impl<magnetized_tov_detail::MagnetizedTovVariables>(\n        x, tmpl::list<Tags...>{}, magnetic_fields_);\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const MagnetizedTovStar& lhs,\n                         const MagnetizedTovStar& rhs);\n\n protected:\n  std::vector<std::unique_ptr<\n      grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField>>\n      magnetic_fields_{};\n};\n\nbool operator!=(const MagnetizedTovStar& lhs, const MagnetizedTovStar& rhs);\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/OrszagTangVortex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/OrszagTangVortex.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace grmhd::AnalyticData {\n\nOrszagTangVortex::OrszagTangVortex() = default;\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nOrszagTangVortex::get_clone() const {\n  return std::make_unique<OrszagTangVortex>(*this);\n}\n\nOrszagTangVortex::OrszagTangVortex(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid OrszagTangVortex::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | equation_of_state_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 25. / (36. * M_PI))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.1)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  return {tnsr::I<DataType, 3>{\n      {{-0.5 * sin(2. * M_PI * get<1>(x)), 0.5 * sin(2. * M_PI * get<0>(x)),\n        make_with_value<DataType>(x, 0.)}}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  using density_tag = hydro::Tags::RestMassDensity<DataType>;\n  using pressure_tag = hydro::Tags::Pressure<DataType>;\n  const auto data = variables(x, tmpl::list<density_tag, pressure_tag>{});\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<density_tag>(data), get<pressure_tag>(data));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 5. / (12. * M_PI))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  return {\n      tnsr::I<DataType, 3>{{{-1. / sqrt(4. * M_PI) * sin(2. * M_PI * get<1>(x)),\n                             1. / sqrt(4. * M_PI) * sin(4. * M_PI * get<0>(x)),\n                             make_with_value<DataType>(x, 0.)}}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  using velocity_tag = hydro::Tags::SpatialVelocity<DataType, 3>;\n  const auto velocity =\n      get<velocity_tag>(variables(x, tmpl::list<velocity_tag>{}));\n  return {hydro::lorentz_factor(dot_product(velocity, velocity))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nOrszagTangVortex::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  using density_tag = hydro::Tags::RestMassDensity<DataType>;\n  using energy_tag = hydro::Tags::SpecificInternalEnergy<DataType>;\n  using pressure_tag = hydro::Tags::Pressure<DataType>;\n  const auto data =\n      variables(x, tmpl::list<density_tag, energy_tag, pressure_tag>{});\n  return hydro::relativistic_specific_enthalpy(\n      get<density_tag>(data), get<energy_tag>(data), get<pressure_tag>(data));\n}\n\nPUP::able::PUP_ID OrszagTangVortex::my_PUP_ID = 0;\n\nbool operator==(const OrszagTangVortex& /*lhs*/,\n                const OrszagTangVortex& /*rhs*/) {\n  // there is no comparison operator for the EoS, but it's the same\n  // for every instance of this class.\n  return true;\n}\n\nbool operator!=(const OrszagTangVortex& lhs, const OrszagTangVortex& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                     \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>> \\\n      OrszagTangVortex::variables(                       \\\n          const tnsr::I<DTYPE(data), 3>& x,              \\\n          tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::SpecificInternalEnergy,\n     hydro::Tags::Pressure, hydro::Tags::DivergenceCleaningField,\n     hydro::Tags::LorentzFactor, hydro::Tags::SpecificEnthalpy,\n     hydro::Tags::ElectronFraction))\n\n#define INSTANTIATE_VECTORS(_, data)                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3>> \\\n      OrszagTangVortex::variables(                          \\\n          const tnsr::I<DTYPE(data), 3>& x,                 \\\n          tmpl::list<TAG(data) < DTYPE(data), 3>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/OrszagTangVortex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// for IdealFluid\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for the relativistic Orszag-Tang vortex.\n *\n * The relativistic version of the Orszag-Tang vortex is a\n * 2-dimensional test case for relativistic MHD systems (see, e.g.,\n * \\cite Beckwith2011iy).  It describes the flow of an ideal fluid with\n * adiabatic index \\f$5/3\\f$.  The initial conditions (and hence the\n * states at later times) are periodic in both \\f$x\\f$ and \\f$y\\f$\n * with period 1.  The initial conditions are:\n * \\f{align*}\n * \\rho &= \\frac{25}{36 \\pi} \\\\\n * p &= \\frac{5}{12 \\pi} \\\\\n * v_x &= -\\frac{1}{2} \\sin(2 \\pi y) \\\\\n * v_y &= \\frac{1}{2} \\sin(2 \\pi x) \\\\\n * B_x &= -\\frac{1}{\\sqrt{4 \\pi}} \\sin(2 \\pi y) \\\\\n * B_y &= \\frac{1}{\\sqrt{4 \\pi}} \\sin(4 \\pi x)\n * \\f}\n * with \\f$\\rho\\f$ the rest mass density, \\f$p\\f$ the pressure,\n * \\f$v_i\\f$ the spatial velocity, and \\f$B_i\\f$ the magnetic field.\n *\n * \\parblock\n * \\note We do not currently support 2-dimensional RMHD, so this class\n * provides 3-dimensional data with no \\f$z\\f$-dependence.\n * \\endparblock\n *\n * \\parblock\n * \\note There are multiple errors in the description of this test\n * problem in the original SpECTRE paper \\cite Kidder2016hev and there\n * is a sign error in the velocity in \\cite Beckwith2011iy.  Despite these\n * errors, the actual tests performed for those papers matched the standard\n * problem as presented here.\n * \\endparblock\n */\nclass OrszagTangVortex\n    : public evolution::initial_data::InitialData,\n      public AnalyticDataBase,\n      public hydro::TemperatureInitialization<OrszagTangVortex>,\n      public MarkAsAnalyticData {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  using options = tmpl::list<>;\n\n  static constexpr Options::String help = {\n      \"The relativistic Orszag-Tang vortex\"};\n\n  OrszagTangVortex();\n  OrszagTangVortex(const OrszagTangVortex& /*rhs*/) = default;\n  OrszagTangVortex& operator=(const OrszagTangVortex& /*rhs*/) = default;\n  OrszagTangVortex(OrszagTangVortex&& /*rhs*/) = default;\n  OrszagTangVortex& operator=(OrszagTangVortex&& /*rhs*/) = default;\n  ~OrszagTangVortex() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit OrszagTangVortex(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(OrszagTangVortex);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve hydro variable at `x`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n  /// @}\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<not tmpl::list_contains_v<\n                tmpl::push_back<hydro::grmhd_tags<DataType>,\n                                hydro::Tags::SpecificEnthalpy<DataType>>,\n                Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.;\n    return {std::move(get<Tag>(gr::Solutions::Minkowski<3>{}.variables(\n        x, dummy_time, tmpl::list<Tag>{})))};\n  }\n\n  const equation_of_state_type& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  EquationsOfState::IdealFluid<true> equation_of_state_{5. / 3.};\n};\n\nbool operator==(const OrszagTangVortex& lhs, const OrszagTangVortex& rhs);\n\nbool operator!=(const OrszagTangVortex& lhs, const OrszagTangVortex& rhs);\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/PolarMagnetizedFmDisk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/PolarMagnetizedFmDisk.hpp\"\n\n#include <pup.h>\n#include <utility>\n\n\nnamespace grmhd::AnalyticData {\n\nPolarMagnetizedFmDisk::PolarMagnetizedFmDisk(\n    MagnetizedFmDisk fm_disk, grmhd::AnalyticData::SphericalTorus torus_map)\n    : fm_disk_(std::move(fm_disk)), torus_map_(std::move(torus_map)) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nPolarMagnetizedFmDisk::get_clone() const {\n  return std::make_unique<PolarMagnetizedFmDisk>(*this);\n}\n\nPolarMagnetizedFmDisk::PolarMagnetizedFmDisk(CkMigrateMessage* msg)\n    : fm_disk_(msg) {}\n\nvoid PolarMagnetizedFmDisk::pup(PUP::er& p) {\n  p | fm_disk_;\n  p | torus_map_;\n}\n\nPUP::able::PUP_ID PolarMagnetizedFmDisk::my_PUP_ID = 0; // NOLINT\n\nbool operator==(const PolarMagnetizedFmDisk& lhs,\n                const PolarMagnetizedFmDisk& rhs) {\n  return lhs.fm_disk_ == rhs.fm_disk_ and lhs.torus_map_ == rhs.torus_map_;\n}\n\nbool operator!=(const PolarMagnetizedFmDisk& lhs,\n                const PolarMagnetizedFmDisk& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/PolarMagnetizedFmDisk.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <iterator>\n#include <type_traits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/FrameTransform.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SphericalTorus.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n/*!\n * \\brief Magnetized fluid disk orbiting a Kerr black hole in the Kerr-Schild\n * Cartesian coordinates, but in a toroidal mesh defined from a torus map\n * (see grmhd::AnalyticData::SphericalTorus).\n */\nclass PolarMagnetizedFmDisk\n    : public virtual evolution::initial_data::InitialData,\n      public MarkAsAnalyticData,\n      public AnalyticDataBase {\n public:\n  struct DiskParameters {\n    using type = MagnetizedFmDisk;\n    static constexpr Options::String help = \"Parameters for the disk.\";\n  };\n\n  struct TorusParameters {\n    using type = grmhd::AnalyticData::SphericalTorus;\n    static constexpr Options::String help =\n        \"Parameters for the evolution region.\";\n  };\n\n  using options = tmpl::list<DiskParameters, TorusParameters>;\n\n  static constexpr Options::String help =\n      \"Magnetized Fishbone-Moncrief disk in polar coordinates.\";\n\n  PolarMagnetizedFmDisk() = default;\n  PolarMagnetizedFmDisk(const PolarMagnetizedFmDisk& /*rhs*/) = default;\n  PolarMagnetizedFmDisk& operator=(const PolarMagnetizedFmDisk& /*rhs*/) =\n      default;\n  PolarMagnetizedFmDisk(PolarMagnetizedFmDisk&& /*rhs*/) = default;\n  PolarMagnetizedFmDisk& operator=(PolarMagnetizedFmDisk&& /*rhs*/) = default;\n  ~PolarMagnetizedFmDisk() override = default;\n\n  PolarMagnetizedFmDisk(MagnetizedFmDisk fm_disk,\n                        grmhd::AnalyticData::SphericalTorus torus_map);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit PolarMagnetizedFmDisk(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(PolarMagnetizedFmDisk);\n  /// \\endcond\n\n  /// The grmhd variables.\n  ///\n  /// \\note The functions are optimized for retrieving the hydro variables\n  /// before the metric variables.\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    // In this function, we label the coordinates this solution works\n    // in with Frame::BlockLogical, and the coordinates the wrapped\n    // solution uses Frame::Inertial.  This means the input and output\n    // have to be converted to the correct label.\n\n    const tnsr::I<DataType, 3> observation_coordinates(torus_map_(x));\n\n    using dependencies = tmpl::map<\n        tmpl::pair<gr::AnalyticSolution<3>::DerivShift<DataType>,\n                   gr::Tags::Shift<DataType, 3, Frame::Inertial>>,\n        tmpl::pair<gr::AnalyticSolution<3>::DerivSpatialMetric<DataType>,\n                   gr::Tags::SpatialMetric<DataType, 3, Frame::Inertial>>>;\n    using required_tags = tmpl::remove_duplicates<\n        tmpl::remove<tmpl::list<Tags..., tmpl::at<dependencies, Tags>...>,\n                     tmpl::no_such_type_>>;\n\n    auto observation_data =\n        fm_disk_.variables(observation_coordinates, required_tags{});\n\n    const auto jacobian = torus_map_.jacobian(x);\n    const auto inv_jacobian = torus_map_.inv_jacobian(x);\n\n    const auto change_frame = [this, &inv_jacobian, &jacobian, &x](\n                                  const auto& data, auto tag) {\n      using Tag = decltype(tag);\n      auto result =\n          transform::to_different_frame(get<Tag>(data), jacobian, inv_jacobian);\n\n      if constexpr (std::is_same_v<\n                        Tag, gr::AnalyticSolution<3>::DerivShift<DataType>>) {\n        const auto deriv_inv_jacobian =\n            torus_map_.derivative_of_inv_jacobian(x);\n        const auto& shift =\n            get<gr::Tags::Shift<DataType, 3, Frame::Inertial>>(data);\n        for (size_t i = 0; i < 3; ++i) {\n          for (size_t j = 0; j < 3; ++j) {\n            for (size_t k = 0; k < 3; ++k) {\n              result.get(i, j) +=\n                  deriv_inv_jacobian.get(j, k, i) * shift.get(k);\n            }\n          }\n        }\n      } else if constexpr (std::is_same_v<\n                               Tag, gr::AnalyticSolution<3>::DerivSpatialMetric<\n                                        DataType>>) {\n        const auto hessian = torus_map_.hessian(x);\n        const auto& spatial_metric =\n            get<gr::Tags::SpatialMetric<DataType, 3, Frame::Inertial>>(data);\n        for (size_t i = 0; i < 3; ++i) {\n          for (size_t j = 0; j < 3; ++j) {\n            for (size_t k = j; k < 3; ++k) {\n              for (size_t l = 0; l < 3; ++l) {\n                for (size_t m = 0; m < 3; ++m) {\n                  result.get(i, j, k) +=\n                      (hessian.get(l, j, i) * jacobian.get(m, k) +\n                       hessian.get(l, k, i) * jacobian.get(m, j)) *\n                      spatial_metric.get(l, m);\n                }\n              }\n            }\n          }\n        }\n      } else if constexpr (std::is_same_v<\n                               Tag, gr::Tags::SqrtDetSpatialMetric<DataType>>) {\n        get(result) *= abs(get(determinant(jacobian)));\n      }\n\n      typename Tag::type result_with_replaced_frame{};\n      std::copy(std::move_iterator(result.begin()),\n                std::move_iterator(result.end()),\n                result_with_replaced_frame.begin());\n      return result_with_replaced_frame;\n    };\n\n    return {change_frame(observation_data, Tags{})...};\n  }\n\n  using equation_of_state_type = MagnetizedFmDisk::equation_of_state_type;\n  const equation_of_state_type& equation_of_state() const {\n    return fm_disk_.equation_of_state();\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const PolarMagnetizedFmDisk& lhs,\n                         const PolarMagnetizedFmDisk& rhs);\n\n  MagnetizedFmDisk fm_disk_;\n  grmhd::AnalyticData::SphericalTorus torus_map_;\n};\n\nbool operator!=(const PolarMagnetizedFmDisk& lhs,\n                const PolarMagnetizedFmDisk& rhs);\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/RiemannProblem.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/RiemannProblem.hpp\"\n\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace grmhd::AnalyticData {\n\nRiemannProblem::RiemannProblem(\n    const double adiabatic_index, const double left_rest_mass_density,\n    const double right_rest_mass_density, const double left_pressure,\n    const double right_pressure,\n    const std::array<double, 3>& left_spatial_velocity,\n    const std::array<double, 3>& right_spatial_velocity,\n    const std::array<double, 3>& left_magnetic_field,\n    const std::array<double, 3>& right_magnetic_field, const double lapse,\n    const double shift)\n    : equation_of_state_(adiabatic_index),\n      adiabatic_index_(adiabatic_index),\n      left_rest_mass_density_(left_rest_mass_density),\n      right_rest_mass_density_(right_rest_mass_density),\n      left_pressure_(left_pressure),\n      right_pressure_(right_pressure),\n      left_spatial_velocity_(left_spatial_velocity),\n      right_spatial_velocity_(right_spatial_velocity),\n      left_magnetic_field_(left_magnetic_field),\n      right_magnetic_field_(right_magnetic_field),\n      lapse_(lapse),\n      shift_(shift) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nRiemannProblem::get_clone() const {\n  return std::make_unique<RiemannProblem>(*this);\n}\n\nRiemannProblem::RiemannProblem(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid RiemannProblem::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | equation_of_state_;\n  p | background_spacetime_;\n  p | adiabatic_index_;\n  p | left_rest_mass_density_;\n  p | right_rest_mass_density_;\n  p | left_pressure_;\n  p | right_pressure_;\n  p | left_spatial_velocity_;\n  p | right_spatial_velocity_;\n  p | left_magnetic_field_;\n  p | right_magnetic_field_;\n  p | lapse_;\n  p | shift_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nRiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  auto mass_density = make_with_value<Scalar<DataType>>(x, 0.0);\n  for (size_t i = 0; i < get_size(get<0>(x)); ++i) {\n    if (get_element(get<0>(x), i) <= discontinuity_location_) {\n      get_element(get(mass_density), i) = left_rest_mass_density_;\n    } else {\n      get_element(get(mass_density), i) = right_rest_mass_density_;\n    }\n  }\n  return mass_density;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nRiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n\n    return {make_with_value<Scalar<DataType>>(x, 0.1)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nRiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  auto spatial_velocity =\n      make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(x, 0.0);\n  for (size_t i = 0; i < get_size(get<0>(x)); ++i) {\n    if (get_element(get<0>(x), i) <= discontinuity_location_) {\n      get_element(get<0>(spatial_velocity), i) = left_spatial_velocity_[0];\n      get_element(get<1>(spatial_velocity), i) = left_spatial_velocity_[1];\n      get_element(get<2>(spatial_velocity), i) = left_spatial_velocity_[2];\n    } else {\n      get_element(get<0>(spatial_velocity), i) = right_spatial_velocity_[0];\n      get_element(get<1>(spatial_velocity), i) = right_spatial_velocity_[1];\n      get_element(get<2>(spatial_velocity), i) = right_spatial_velocity_[2];\n    }\n  }\n  return spatial_velocity;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nRiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> RiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  auto pressure = make_with_value<Scalar<DataType>>(x, 0.0);\n  for (size_t i = 0; i < get_size(get<0>(x)); ++i) {\n    if (get_element(get<0>(x), i) <= discontinuity_location_) {\n      get_element(get(pressure), i) = left_pressure_;\n    } else {\n      get_element(get(pressure), i) = right_pressure_;\n    }\n  }\n  return pressure;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nRiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  auto magnetic_field =\n      make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(x, 0.0);\n  for (size_t i = 0; i < get_size(get<0>(x)); ++i) {\n    if (get_element(get<0>(x), i) <= discontinuity_location_) {\n      get_element(get<0>(magnetic_field), i) = left_magnetic_field_[0];\n      get_element(get<1>(magnetic_field), i) = left_magnetic_field_[1];\n      get_element(get<2>(magnetic_field), i) = left_magnetic_field_[2];\n    } else {\n      get_element(get<0>(magnetic_field), i) = right_magnetic_field_[0];\n      get_element(get<1>(magnetic_field), i) = right_magnetic_field_[1];\n      get_element(get<2>(magnetic_field), i) = right_magnetic_field_[2];\n    }\n  }\n  return magnetic_field;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nRiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nRiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  const auto spatial_velocity = get<hydro::Tags::SpatialVelocity<DataType, 3>>(\n      variables(x, tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>>{}));\n  return {\n      hydro::lorentz_factor(dot_product(spatial_velocity, spatial_velocity))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nRiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  return hydro::relativistic_specific_enthalpy(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::SpecificInternalEnergy<DataType>>(variables(\n          x, tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::Lapse<DataType>> RiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, lapse_)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::Shift<DataType, 3>> RiemannProblem::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<gr::Tags::Shift<DataType, 3>> /*meta*/) const {\n  auto shift = make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(x, 0.0);\n  get<0>(shift) = shift_;\n  return {std::move(shift)};\n}\n\nPUP::able::PUP_ID RiemannProblem::my_PUP_ID = 0;\n\nbool operator==(const RiemannProblem& lhs, const RiemannProblem& rhs) {\n  return lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.left_rest_mass_density_ == rhs.left_rest_mass_density_ and\n         lhs.right_rest_mass_density_ == rhs.right_rest_mass_density_ and\n         lhs.left_pressure_ == rhs.left_pressure_ and\n         lhs.right_pressure_ == rhs.right_pressure_ and\n         lhs.left_spatial_velocity_ == rhs.left_spatial_velocity_ and\n         lhs.right_spatial_velocity_ == rhs.right_spatial_velocity_ and\n         lhs.left_magnetic_field_ == rhs.left_magnetic_field_ and\n         lhs.right_magnetic_field_ == rhs.right_magnetic_field_ and\n         lhs.lapse_ == rhs.lapse_ and lhs.shift_ == rhs.shift_;\n}\n\nbool operator!=(const RiemannProblem& lhs, const RiemannProblem& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                           \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>                       \\\n      RiemannProblem::variables(const tnsr::I<DTYPE(data), 3>& x,              \\\n                                tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::SpecificInternalEnergy,\n     hydro::Tags::Pressure, hydro::Tags::DivergenceCleaningField,\n     hydro::Tags::LorentzFactor, hydro::Tags::SpecificEnthalpy,\n     hydro::Tags::ElectronFraction, hydro::Tags::Temperature, gr::Tags::Lapse))\n\n#define INSTANTIATE_VECTORS(_, data)                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3>> \\\n      RiemannProblem::variables(                            \\\n          const tnsr::I<DTYPE(data), 3>& x,                 \\\n          tmpl::list<TAG(data) < DTYPE(data), 3>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n\n// The GR tags have a different template parameter ordering than the rest of the\n// code, so need to instantiate separately.\ntemplate tuples::TaggedTuple<gr::Tags::Shift<double, 3>>\nRiemannProblem::variables(\n    const tnsr::I<double, 3>& x,\n    tmpl::list<gr::Tags::Shift<double, 3>> /*meta*/) const;\ntemplate tuples::TaggedTuple<gr::Tags::Shift<DataVector, 3>>\nRiemannProblem::variables(\n    const tnsr::I<DataVector, 3>& x,\n    tmpl::list<gr::Tags::Shift<DataVector, 3>> /*meta*/) const;\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/RiemannProblem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n/*!\n * \\brief Initial conditions for relativistic MHD Riemann problems\n *\n * The standard problems were first collected in \\cite Balsara2001. A complete\n * Riemann solver for the RMHD equations is presented in \\cite Giacomazzo2006\n * and can be downloaded from https://www.brunogiacomazzo.org/?page_id=395\n *\n * The domain is from \\f$[-0.5,0.5]^3\\f$ with periodic boundary conditions in\n * \\f$y\\f$ and \\f$z\\f$. The problems are usually run at a resolution of 1600\n * finite difference/finite volume grid points and the initial discontinuity is\n * located at \\f$x=0\\f$.\n *\n * While the problems were originally run in Minkowski space, changing the lapse\n * \\f$\\alpha\\f$ to a non-unity constant value and/or the shift \\f$\\beta^x\\f$ to\n * a non-zero constant value allows testing some of the metric terms in the\n * evolution equations in a fairly simple setup.\n *\n * Below are the initial conditions for the 5 different Balsara Riemann\n * problems. Please note that RP5 has a different final time than the rest, and\n * that RP1 has a different adiabatic index than the rest.\n *\n * RP1:\n *  - AdiabaticIndex: 2.0\n *  - LeftDensity: 1.0\n *  - LeftPressure: 1.0\n *  - LeftVelocity: [0.0, 0.0, 0.0]\n *  - LeftMagneticField: [0.5, 1.0, 0.0]\n *  - RightDensity: 0.125\n *  - RightPressure: 0.1\n *  - RightVelocity: [0.0, 0.0, 0.0]\n *  - RightMagneticField: [0.5, -1.0, 0.0]\n *  - Lapse: 1.0\n *  - ShiftX: 0.0\n *  - Final time: 0.4\n *\n * RP2:\n *  - AdiabaticIndex: 1.66666666666666666\n *  - LeftDensity: 1.0\n *  - LeftPressure: 30.0\n *  - LeftVelocity: [0.0, 0.0, 0.0]\n *  - LeftMagneticField: [5.0, 6.0, 6.0]\n *  - RightDensity: 1.0\n *  - RightPressure: 1.0\n *  - RightVelocity: [0.0, 0.0, 0.0]\n *  - RightMagneticField: [5.0, 0.7, 0.7]\n *  - Lapse: 1.0\n *  - ShiftX: 0.0\n *  - Final time: 0.4\n *\n * RP3:\n *  - AdiabaticIndex: 1.66666666666666666\n *  - LeftDensity: 1.0\n *  - LeftPressure: 1000.0\n *  - LeftVelocity: [0.0, 0.0, 0.0]\n *  - LeftMagneticField: [10.0, 7.0, 7.0]\n *  - RightDensity: 1.0\n *  - RightPressure: 0.1\n *  - RightVelocity: [0.0, 0.0, 0.0]\n *  - RightMagneticField: [10.0, 0.7, 0.7]\n *  - Lapse: 1.0\n *  - ShiftX: 0.0\n *  - Final time: 0.4\n *\n * RP4:\n *  - AdiabaticIndex: 1.66666666666666666\n *  - LeftDensity: 1.0\n *  - LeftPressure: 0.1\n *  - LeftVelocity: [0.999, 0.0, 0.0]\n *  - LeftMagneticField: [10.0, 7.0, 7.0]\n *  - RightDensity: 1.0\n *  - RightPressure: 0.1\n *  - RightVelocity: [-0.999, 0.0, 0.0]\n *  - RightMagneticField: [10.0, -7.0, -7.0]\n *  - Lapse: 1.0\n *  - ShiftX: 0.0\n *  - Final time: 0.4\n *\n * RP5:\n *  - AdiabaticIndex: 1.66666666666666666\n *  - LeftDensity: 1.08\n *  - LeftPressure: 0.95\n *  - LeftVelocity: [0.4, 0.3, 0.2]\n *  - LeftMagneticField: [2.0, 0.3, 0.3]\n *  - RightDensity: 1.0\n *  - RightPressure: 1.0\n *  - RightVelocity: [-0.45, -0.2, 0.2]\n *  - RightMagneticField: [2.0, -0.7, 0.5]\n *  - Lapse: 1.0\n *  - ShiftX: 0.0\n *  - Final time: 0.55\n */\nclass RiemannProblem : public evolution::initial_data::InitialData,\n                       public AnalyticDataBase,\n                       public hydro::TemperatureInitialization<RiemannProblem>,\n                       public MarkAsAnalyticData {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the ideal fluid\"};\n    static type lower_bound() { return 1.0; }\n  };\n  struct LeftRestMassDensity {\n    using type = double;\n    static std::string name() { return \"LeftDensity\"; };\n    static constexpr Options::String help = {\n        \"Fluid rest mass density in the left half-domain\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct RightRestMassDensity {\n    using type = double;\n    static std::string name() { return \"RightDensity\"; };\n    static constexpr Options::String help = {\n        \"Fluid rest mass density in the right half-domain\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct LeftPressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fluid pressure in the left half-domain\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct RightPressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fluid pressure in the right half-domain\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct LeftSpatialVelocity {\n    using type = std::array<double, 3>;\n    static std::string name() { return \"LeftVelocity\"; };\n    static constexpr Options::String help = {\n        \"Fluid spatial velocity in the left half-domain\"};\n  };\n  struct RightSpatialVelocity {\n    using type = std::array<double, 3>;\n    static std::string name() { return \"RightVelocity\"; };\n    static constexpr Options::String help = {\n        \"Fluid spatial velocity in the right half-domain\"};\n  };\n  struct LeftMagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Magnetic field in the left half-domain\"};\n  };\n  struct RightMagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Magnetic field in the right half-domain\"};\n  };\n  struct Lapse {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The value of the lapse. Standard is 1.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct ShiftX {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The value of the x-component of the shift, beta^x. Standard is 0.\"};\n  };\n\n  using options =\n      tmpl::list<AdiabaticIndex, LeftRestMassDensity, RightRestMassDensity,\n                 LeftPressure, RightPressure, LeftSpatialVelocity,\n                 RightSpatialVelocity, LeftMagneticField, RightMagneticField,\n                 Lapse, ShiftX>;\n\n  static constexpr Options::String help = {\n      \"Analytic initial data for a GRMHD Riemann problem. The fluid variables \"\n      \"are set homogeneously on either half of the domain left and right of \"\n      \"x=0.\"};\n\n  RiemannProblem() = default;\n  RiemannProblem(const RiemannProblem& /*rhs*/) = default;\n  RiemannProblem& operator=(const RiemannProblem& /*rhs*/) = default;\n  RiemannProblem(RiemannProblem&& /*rhs*/) = default;\n  RiemannProblem& operator=(RiemannProblem&& /*rhs*/) = default;\n  ~RiemannProblem() override = default;\n\n  RiemannProblem(double adiabatic_index, double left_rest_mass_density,\n                 double right_rest_mass_density, double left_pressure,\n                 double right_pressure,\n                 const std::array<double, 3>& left_spatial_velocity,\n                 const std::array<double, 3>& right_spatial_velocity,\n                 const std::array<double, 3>& left_magnetic_field,\n                 const std::array<double, 3>& right_magnetic_field,\n                 double lapse, double shift);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit RiemannProblem(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RiemannProblem);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve the GRMHD variables at a given position.\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::Shift<DataType, 3>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Shift<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n  /// @}\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n  const EquationsOfState::IdealFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  friend bool operator==(const RiemannProblem& lhs, const RiemannProblem& rhs);\n\n  friend bool operator!=(const RiemannProblem& lhs, const RiemannProblem& rhs);\n\n  EquationsOfState::IdealFluid<true> equation_of_state_{};\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  double left_rest_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double right_rest_mass_density_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double left_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double right_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  static constexpr double discontinuity_location_ = 0.0;\n  std::array<double, 3> left_spatial_velocity_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 3> right_spatial_velocity_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 3> left_magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 3> right_magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  double lapse_ = std::numeric_limits<double>::signaling_NaN();\n  double shift_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/SlabJet.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SlabJet.hpp\"\n\n#include <cmath>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nScalar<DataType> compute_piecewise(const tnsr::I<DataType, 3>& x,\n                                   const double inlet_radius,\n                                   const double ambient_value,\n                                   const double jet_value) {\n  auto result = make_with_value<Scalar<DataType>>(x, ambient_value);\n  for (size_t i = 0; i < get_size(get(result)); ++i) {\n    if (get_element(get<0>(x), i) <= 0. and\n        abs(get_element(get<1>(x), i)) <= inlet_radius) {\n      get_element(get(result), i) = jet_value;\n    }\n  }\n  return result;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> compute_piecewise_vector(\n    const tnsr::I<DataType, 3>& x, const double inlet_radius,\n    const std::array<double, 3>& ambient_value,\n    const std::array<double, 3>& jet_value) {\n  auto result = make_with_value<tnsr::I<DataType, 3>>(x, 0.);\n  for (size_t i = 0; i < get_size(get<0>(result)); ++i) {\n    if (get_element(get<0>(x), i) <= 0. and\n        abs(get_element(get<1>(x), i)) <= inlet_radius) {\n      get_element(get<0>(result), i) = jet_value[0];\n      get_element(get<1>(result), i) = jet_value[1];\n      get_element(get<2>(result), i) = jet_value[2];\n    } else {\n      get_element(get<0>(result), i) = ambient_value[0];\n      get_element(get<1>(result), i) = ambient_value[1];\n      get_element(get<2>(result), i) = ambient_value[2];\n    }\n  }\n  return result;\n}\n}  // namespace\n\nnamespace grmhd::AnalyticData {\n\nSlabJet::SlabJet(double adiabatic_index, double ambient_density,\n                 double ambient_pressure, double ambient_electron_fraction,\n                 double jet_density, double jet_pressure,\n                 double jet_electron_fraction,\n                 std::array<double, 3> jet_velocity, double inlet_radius,\n                 std::array<double, 3> magnetic_field)\n    : equation_of_state_(adiabatic_index),\n      ambient_density_(ambient_density),\n      ambient_pressure_(ambient_pressure),\n      ambient_electron_fraction_(ambient_electron_fraction),\n      jet_density_(jet_density),\n      jet_pressure_(jet_pressure),\n      jet_electron_fraction_(jet_electron_fraction),\n      jet_velocity_(jet_velocity),\n      inlet_radius_(inlet_radius),\n      magnetic_field_(magnetic_field) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> SlabJet::get_clone()\n    const {\n  return std::make_unique<SlabJet>(*this);\n}\n\nSlabJet::SlabJet(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid SlabJet::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | equation_of_state_;\n  p | background_spacetime_;\n  p | ambient_density_;\n  p | ambient_pressure_;\n  p | ambient_electron_fraction_;\n  p | jet_density_;\n  p | jet_pressure_;\n  p | jet_electron_fraction_;\n  p | jet_velocity_;\n  p | inlet_radius_;\n  p | magnetic_field_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>> SlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  return compute_piecewise(x, inlet_radius_, ambient_density_, jet_density_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>> SlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n  return compute_piecewise(x, inlet_radius_, ambient_electron_fraction_,\n                           jet_electron_fraction_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nSlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  return compute_piecewise_vector(\n      x, inlet_radius_, std::array<double, 3>{{0., 0., 0.}}, jet_velocity_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nSlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  using density_tag = hydro::Tags::RestMassDensity<DataType>;\n  using pressure_tag = hydro::Tags::Pressure<DataType>;\n  const auto data = variables(x, tmpl::list<density_tag, pressure_tag>{});\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<density_tag>(data), get<pressure_tag>(data));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> SlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return compute_piecewise(x, inlet_radius_, ambient_pressure_, jet_pressure_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>> SlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  auto magnetic_field = make_with_value<tnsr::I<DataType, 3>>(x, 0.);\n  get<0>(magnetic_field) = magnetic_field_[0];\n  get<1>(magnetic_field) = magnetic_field_[1];\n  get<2>(magnetic_field) = magnetic_field_[2];\n  return {std::move(magnetic_field)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nSlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>> SlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  const auto spatial_velocity = get<hydro::Tags::SpatialVelocity<DataType, 3>>(\n      variables(x, tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>>{}));\n  return {\n      hydro::lorentz_factor(dot_product(spatial_velocity, spatial_velocity))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>> SlabJet::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  using density_tag = hydro::Tags::RestMassDensity<DataType>;\n  using energy_tag = hydro::Tags::SpecificInternalEnergy<DataType>;\n  using pressure_tag = hydro::Tags::Pressure<DataType>;\n  const auto data =\n      variables(x, tmpl::list<density_tag, energy_tag, pressure_tag>{});\n  return hydro::relativistic_specific_enthalpy(\n      get<density_tag>(data), get<energy_tag>(data), get<pressure_tag>(data));\n}\n\nPUP::able::PUP_ID SlabJet::my_PUP_ID = 0;\n\nbool operator==(const SlabJet& lhs, const SlabJet& rhs) {\n  return lhs.equation_of_state_ == rhs.equation_of_state_ and\n         lhs.ambient_density_ == rhs.ambient_density_ and\n         lhs.ambient_pressure_ == rhs.ambient_pressure_ and\n         lhs.ambient_electron_fraction_ == rhs.ambient_electron_fraction_ and\n         lhs.jet_density_ == rhs.jet_density_ and\n         lhs.jet_pressure_ == rhs.jet_pressure_ and\n         lhs.jet_electron_fraction_ == rhs.jet_electron_fraction_ and\n         lhs.jet_velocity_ == rhs.jet_velocity_ and\n         lhs.inlet_radius_ == rhs.inlet_radius_ and\n         lhs.magnetic_field_ == rhs.magnetic_field_;\n}\n\nbool operator!=(const SlabJet& lhs, const SlabJet& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                        \\\n  template tuples::TaggedTuple < TAG(data) < DTYPE(data) >> \\\n      SlabJet::variables(const tnsr::I<DTYPE(data), 3>&,    \\\n                         tmpl::list < TAG(data) < DTYPE(data) >>) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::DivergenceCleaningField, hydro::Tags::LorentzFactor,\n     hydro::Tags::SpecificEnthalpy))\n\n#define INSTANTIATE_VECTORS(_, data)                                   \\\n  template tuples::TaggedTuple < TAG(data) < DTYPE(data),              \\\n      3 >> SlabJet::variables(const tnsr::I<DTYPE(data), 3>&,          \\\n                              tmpl::list < TAG(data) < DTYPE(data), 3, \\\n                              Frame::Inertial >>) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/SlabJet.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Analytic initial data for a slab jet\n *\n * This test problem is described in \\cite Komissarov1999, Section 7.4 and\n * Fig. 13. It involves a high Lorentz factor jet injected into an ambient fluid\n * with an initially uniform magnetic field.\n *\n * The parameters used in \\cite Komissarov1999 are:\n *\n * ```yaml\n * AdiabaticIndex: 4. / 3.\n * AmbientDensity: 10.\n * AmbientPressure: 0.01\n * JetDensity: 0.1\n * JetPressure: 0.01\n * # u^i = [20, 0, 0] or W = sqrt(401)\n * JetVelocity: [0.9987523388778445, 0., 0.]\n * InletRadius: 1.\n * MagneticField: [1., 0., 0.]\n * ```\n *\n * In \\cite Komissarov1999 an artificial dissipation of $\\eta_u=0.2$ and\n * $\\eta_b=0.15$ is used, which we don't use here (yet).\n *\n * \\note The inlet is currently modeled as the region of the domain where\n * $x <= 0$ and $|y| <= \\mathrm{inlet_radius}$. Since this class only sets\n * the initial data, no fluid will flow into the domain during the evolution.\n * This has to be modeled as a boundary condition and is not implemented yet.\n */\nclass SlabJet : public evolution::initial_data::InitialData,\n                public MarkAsAnalyticData,\n                public AnalyticDataBase,\n                public hydro::TemperatureInitialization<SlabJet> {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the ideal fluid\"};\n    static double lower_bound() { return 1.; }\n  };\n  struct AmbientDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fluid rest mass density outside the jet\"};\n    static double lower_bound() { return 0.; }\n    static double suggested_value() { return 10.; }\n  };\n  struct AmbientPressure {\n    using type = double;\n    static constexpr Options::String help = {\"Fluid pressure outside the jet\"};\n    static double lower_bound() { return 0.; }\n    static double suggested_value() { return 0.01; }\n  };\n  struct AmbientElectronFraction {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Electron fraction outside the jet\"};\n    static double lower_bound() { return 0.; }\n    static double upper_bound() { return 1.; }\n  };\n  struct JetDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fluid rest mass density of the jet inlet\"};\n    static double lower_bound() { return 0.; }\n    static double suggested_value() { return 0.1; }\n  };\n  struct JetPressure {\n    using type = double;\n    static constexpr Options::String help = {\"Fluid pressure of the jet inlet\"};\n    static double lower_bound() { return 0.; }\n    static double suggested_value() { return 0.01; }\n  };\n  struct JetElectronFraction {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Electron fraction of the jet inlet\"};\n    static double lower_bound() { return 0.; }\n    static double upper_bound() { return 1.; }\n  };\n  struct JetVelocity {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Fluid spatial velocity of the jet inlet\"};\n  };\n  struct InletRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Radius of the jet inlet around y=0\"};\n    static double lower_bound() { return 0.; }\n    static double suggested_value() { return 1.; }\n  };\n  struct MagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Initially uniform magnetic field\"};\n    static std::array<double, 3> suggested_value() { return {{1., 0., 0.}}; }\n  };\n\n  using options =\n      tmpl::list<AdiabaticIndex, AmbientDensity, AmbientPressure,\n                 AmbientElectronFraction, JetDensity, JetPressure,\n                 JetElectronFraction, JetVelocity, InletRadius, MagneticField>;\n\n  static constexpr Options::String help = {\n      \"Analytic initial data for a jet test.\"};\n\n  SlabJet() = default;\n  SlabJet(const SlabJet& /*rhs*/) = default;\n  SlabJet& operator=(const SlabJet& /*rhs*/) = default;\n  SlabJet(SlabJet&& /*rhs*/) = default;\n  SlabJet& operator=(SlabJet&& /*rhs*/) = default;\n  ~SlabJet() = default;\n\n  SlabJet(double adiabatic_index, double ambient_density,\n          double ambient_pressure, double ambient_electron_fraction,\n          double jet_density, double jet_pressure, double jet_electron_fraction,\n          std::array<double, 3> jet_velocity, double inlet_radius,\n          std::array<double, 3> magnetic_field);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit SlabJet(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SlabJet);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve the GRMHD variables at a given position.\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n  /// @}\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<tmpl::list_contains_v<\n                gr::analytic_solution_tags<3, DataType>, Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    constexpr double dummy_time = 0.0;\n    return background_spacetime_.variables(x, dummy_time, tmpl::list<Tag>{});\n  }\n\n  const EquationsOfState::IdealFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  EquationsOfState::IdealFluid<true> equation_of_state_{};\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  double ambient_density_ = std::numeric_limits<double>::signaling_NaN();\n  double ambient_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double ambient_electron_fraction_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double jet_density_ = std::numeric_limits<double>::signaling_NaN();\n  double jet_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double jet_electron_fraction_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> jet_velocity_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  double inlet_radius_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n\n  friend bool operator==(const SlabJet& lhs, const SlabJet& rhs);\n\n  friend bool operator!=(const SlabJet& lhs, const SlabJet& rhs);\n};\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/SpecInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SpecInitialData.hpp\"\n\n#include <Exporter.hpp>  // The SpEC Exporter\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/External/InterpolateFromSpec.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace grmhd::AnalyticData {\n\ntemplate <size_t ThermodynamicDim>\nSpecInitialData<ThermodynamicDim>::SpecInitialData(\n    std::string data_directory,\n    std::unique_ptr<equation_of_state_type> equation_of_state,\n    const double density_cutoff, const double atmosphere_density,\n    const std::optional<double> electron_fraction)\n    : data_directory_(std::move(data_directory)),\n      equation_of_state_(std::move(equation_of_state)),\n      density_cutoff_(density_cutoff),\n      atmosphere_density_(atmosphere_density),\n      electron_fraction_(electron_fraction),\n      spec_exporter_(std::make_unique<spec::Exporter>(\n          sys::procs_on_node(sys::my_node()), data_directory_,\n          vars_to_interpolate_)) {}\n\ntemplate <size_t ThermodynamicDim>\nSpecInitialData<ThermodynamicDim>::SpecInitialData(const SpecInitialData& rhs)\n    : evolution::initial_data::InitialData(rhs) {\n  *this = rhs;\n}\n\ntemplate <size_t ThermodynamicDim>\nSpecInitialData<ThermodynamicDim>& SpecInitialData<ThermodynamicDim>::operator=(\n    const SpecInitialData& rhs) {\n  data_directory_ = rhs.data_directory_;\n  equation_of_state_ = rhs.equation_of_state_->get_clone();\n  density_cutoff_ = rhs.density_cutoff_;\n  atmosphere_density_ = rhs.atmosphere_density_;\n  electron_fraction_ = rhs.electron_fraction_;\n  spec_exporter_ =\n      std::make_unique<spec::Exporter>(sys::procs_on_node(sys::my_node()),\n                                       data_directory_, vars_to_interpolate_);\n  return *this;\n}\n\ntemplate <size_t ThermodynamicDim>\nstd::unique_ptr<evolution::initial_data::InitialData>\nSpecInitialData<ThermodynamicDim>::get_clone() const {\n  return std::make_unique<SpecInitialData>(*this);\n}\n\ntemplate <size_t ThermodynamicDim>\nSpecInitialData<ThermodynamicDim>::SpecInitialData(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\ntemplate <size_t ThermodynamicDim>\nvoid SpecInitialData<ThermodynamicDim>::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | data_directory_;\n  p | equation_of_state_;\n  p | density_cutoff_;\n  p | atmosphere_density_;\n  p | electron_fraction_;\n  if (p.isUnpacking()) {\n    spec_exporter_ =\n        std::make_unique<spec::Exporter>(sys::procs_on_node(sys::my_node()),\n                                         data_directory_, vars_to_interpolate_);\n  }\n}\n\ntemplate <size_t ThermodynamicDim>\nPUP::able::PUP_ID SpecInitialData<ThermodynamicDim>::my_PUP_ID = 0;\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\ntuples::tagged_tuple_from_typelist<typename SpecInitialData<\n    ThermodynamicDim>::template interpolated_tags<DataType>>\nSpecInitialData<ThermodynamicDim>::interpolate_from_spec(\n    const tnsr::I<DataType, 3>& x) const {\n  auto interpolated_data =\n      io::interpolate_from_spec<interpolated_tags<DataType>>(\n          make_not_null(spec_exporter_.get()), x,\n          static_cast<size_t>(sys::my_local_rank()));\n  DataType& rest_mass_density =\n      get(get<hydro::Tags::RestMassDensity<DataType>>(interpolated_data));\n  for (size_t i = 0; i < get_size(rest_mass_density); ++i) {\n    if (get_element(rest_mass_density, i) < density_cutoff_) {\n      get_element(rest_mass_density, i) = atmosphere_density_;\n    }\n  }\n  return interpolated_data;\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> specific_internal_energy,\n    const gsl::not_null<Cache*> cache,\n    hydro::Tags::SpecificInternalEnergy<DataType> /*meta*/) const {\n  const auto& rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataType>>(interpolated_data);\n  const auto& temperature =\n      cache->get_var(*this, hydro::Tags::Temperature<DataType>{});\n  const auto& electron_fraction =\n      cache->get_var(*this, hydro::Tags::ElectronFraction<DataType>{});\n  const size_t num_points = get_size(get(rest_mass_density));\n  for (size_t i = 0; i < num_points; ++i) {\n    const double local_rest_mass_density =\n        get_element(get(rest_mass_density), i);\n    const double local_temperature = get_element(get(temperature), i);\n    const double local_electron_fraction =\n        get_element(get(electron_fraction), i);\n    if constexpr (ThermodynamicDim == 1) {\n      get_element(get(*specific_internal_energy), i) =\n          get(eos.specific_internal_energy_from_density(\n              Scalar<double>(local_rest_mass_density)));\n    } else if constexpr (ThermodynamicDim == 2) {\n      get_element(get(*specific_internal_energy), i) =\n          get(eos.specific_internal_energy_from_density_and_temperature(\n              Scalar<double>(local_rest_mass_density),\n              Scalar<double>(local_temperature)));\n    } else {\n      static_assert(ThermodynamicDim == 3);\n      get_element(get(*specific_internal_energy), i) =\n          get(eos.specific_internal_energy_from_density_and_temperature(\n              Scalar<double>(local_rest_mass_density),\n              Scalar<double>(local_temperature),\n              Scalar<double>(local_electron_fraction)));\n    }\n  }\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> pressure,\n    const gsl::not_null<Cache*> cache,\n    hydro::Tags::Pressure<DataType> /*meta*/) const {\n  const auto& rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataType>>(interpolated_data);\n  const auto& temperature =\n      cache->get_var(*this, hydro::Tags::Temperature<DataType>{});\n  const auto& electron_fraction =\n      cache->get_var(*this, hydro::Tags::ElectronFraction<DataType>{});\n  const size_t num_points = get_size(get(rest_mass_density));\n  for (size_t i = 0; i < num_points; ++i) {\n    const double local_rest_mass_density =\n        get_element(get(rest_mass_density), i);\n    const double local_temperature = get_element(get(temperature), i);\n    const double local_electron_fraction =\n        get_element(get(electron_fraction), i);\n    if constexpr (ThermodynamicDim == 1) {\n      get_element(get(*pressure), i) = get(\n          eos.pressure_from_density(Scalar<double>(local_rest_mass_density)));\n    } else if constexpr (ThermodynamicDim == 2) {\n      get_element(get(*pressure), i) = get(eos.pressure_from_density_and_energy(\n          Scalar<double>(local_rest_mass_density),\n          Scalar<double>(\n              get(eos.specific_internal_energy_from_density_and_temperature(\n                  Scalar<double>(local_rest_mass_density),\n                  Scalar<double>(local_temperature))))));\n    } else {\n      static_assert(ThermodynamicDim == 3);\n      get_element(get(*pressure), i) =\n          get(eos.pressure_from_density_and_temperature(\n              Scalar<double>(local_rest_mass_density),\n              Scalar<double>(local_temperature),\n              Scalar<double>(local_electron_fraction)));\n    }\n  }\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> specific_enthalpy,\n    const gsl::not_null<Cache*> cache,\n    hydro::Tags::SpecificEnthalpy<DataType> /*meta*/) const {\n  const auto& rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataType>>(interpolated_data);\n  const auto& pressure =\n      cache->get_var(*this, hydro::Tags::Pressure<DataType>{});\n  const auto& specific_internal_energy =\n      cache->get_var(*this, hydro::Tags::SpecificInternalEnergy<DataType>{});\n  const size_t num_points = get_size(get(rest_mass_density));\n  for (size_t i = 0; i < num_points; ++i) {\n    const double local_rest_mass_density =\n        get_element(get(rest_mass_density), i);\n    if (local_rest_mass_density <= density_cutoff) {\n      get_element(get(*specific_enthalpy), i) = 1.;\n    } else {\n      get_element(get(*specific_enthalpy), i) =\n          get(hydro::relativistic_specific_enthalpy(\n              Scalar<double>(local_rest_mass_density),\n              Scalar<double>(get_element(get(specific_internal_energy), i)),\n              Scalar<double>(get_element(get(pressure), i))));\n    }\n  }\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> temperature,\n    const gsl::not_null<Cache*> /*cache*/,\n    hydro::Tags::Temperature<DataType> /*meta*/) const {\n  const auto& rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataType>>(interpolated_data);\n  const size_t num_points = get_size(get(rest_mass_density));\n  // Consistent with the use of temperature above we set T=0\n  for (size_t i = 0; i < num_points; ++i) {\n    get_element(get(*temperature), i) = eos.temperature_lower_bound();\n  }\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3>*> inv_spatial_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::InverseSpatialMetric<DataType, 3> /*meta*/) const {\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3>>(interpolated_data);\n  Scalar<DataType> unused_det{};\n  determinant_and_inverse(make_not_null(&unused_det), inv_spatial_metric,\n                          spatial_metric);\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*>\n        lorentz_factor_times_spatial_velocity,\n    const gsl::not_null<Cache*> cache,\n    hydro::Tags::LorentzFactorTimesSpatialVelocity<DataType, 3> /*meta*/)\n    const {\n  const auto& u_i = get<hydro::Tags::LowerSpatialFourVelocity<DataType, 3>>(\n      interpolated_data);\n  const auto& inv_spatial_metric =\n      cache->get_var(*this, gr::Tags::InverseSpatialMetric<DataType, 3>{});\n  raise_or_lower_index(lorentz_factor_times_spatial_velocity, u_i,\n                       inv_spatial_metric);\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lorentz_factor,\n    const gsl::not_null<Cache*> cache,\n    hydro::Tags::LorentzFactor<DataType> /*meta*/) const {\n  const auto& u_i = get<hydro::Tags::LowerSpatialFourVelocity<DataType, 3>>(\n      interpolated_data);\n  const auto& lorentz_factor_times_spatial_velocity = cache->get_var(\n      *this, hydro::Tags::LorentzFactorTimesSpatialVelocity<DataType, 3>{});\n  dot_product(lorentz_factor, u_i, lorentz_factor_times_spatial_velocity);\n  get(*lorentz_factor) = sqrt(1.0 + get(*lorentz_factor));\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> spatial_velocity,\n    const gsl::not_null<Cache*> cache,\n    hydro::Tags::SpatialVelocity<DataType, 3> /*meta*/) const {\n  *spatial_velocity = cache->get_var(\n      *this, hydro::Tags::LorentzFactorTimesSpatialVelocity<DataType, 3>{});\n  const auto& lorentz_factor =\n      cache->get_var(*this, hydro::Tags::LorentzFactor<DataType>{});\n  for (size_t d = 0; d < 3; ++d) {\n    spatial_velocity->get(d) /= get(lorentz_factor);\n  }\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> electron_fraction,\n    const gsl::not_null<Cache*> cache,\n    hydro::Tags::ElectronFraction<DataType> /*meta*/) const {\n  const auto& temperature =\n      cache->get_var(*this, hydro::Tags::Temperature<DataType>{});\n  const auto& rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataType>>(interpolated_data);\n  const size_t num_points = get_size(get(rest_mass_density));\n  if (electron_fraction_value.has_value()) {\n    std::fill(electron_fraction->begin(), electron_fraction->end(),\n              electron_fraction_value.value());\n  } else {\n    for (size_t i = 0; i < num_points; ++i) {\n      const double local_rest_mass_density =\n          get_element(get(rest_mass_density), i);\n      const double local_temperature = get_element(get(temperature), i);\n      get_element(get(*electron_fraction), i) =\n          get(eos.equilibrium_electron_fraction_from_density_temperature(\n              Scalar<double>(local_rest_mass_density),\n              Scalar<double>(local_temperature)));\n    }\n  }\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n    const gsl::not_null<Cache*> /*cache*/,\n    hydro::Tags::MagneticField<DataType, 3> /*meta*/) const {\n  std::fill(magnetic_field->begin(), magnetic_field->end(), 0.);\n}\n\ntemplate <size_t ThermodynamicDim>\ntemplate <typename DataType>\nvoid SpecInitialData<ThermodynamicDim>::VariablesComputer<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> div_cleaning_field,\n    const gsl::not_null<Cache*> /*cache*/,\n    hydro::Tags::DivergenceCleaningField<DataType> /*meta*/) const {\n  get(*div_cleaning_field) = 0.;\n}\n\n#define THERMODIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                                \\\n  template class SpecInitialData<THERMODIM(data)>;                            \\\n  template tuples::tagged_tuple_from_typelist<typename SpecInitialData<       \\\n      THERMODIM(data)>::template interpolated_tags<double>>                   \\\n  SpecInitialData<THERMODIM(data)>::interpolate_from_spec(                    \\\n      const tnsr::I<double, 3>& x) const;                                     \\\n  template tuples::tagged_tuple_from_typelist<typename SpecInitialData<       \\\n      THERMODIM(data)>::template interpolated_tags<DataVector>>               \\\n  SpecInitialData<THERMODIM(data)>::interpolate_from_spec(                    \\\n      const tnsr::I<DataVector, 3>& x) const;                                 \\\n  template class SpecInitialData<THERMODIM(data)>::VariablesComputer<double>; \\\n  template class SpecInitialData<THERMODIM(                                   \\\n      data)>::VariablesComputer<DataVector>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef INSTANTIATION\n#undef THERMODIM\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/SpecInitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <Exporter.hpp>  // The SpEC Exporter\n#include <memory>\n#include <optional>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/NumericInitialData.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/AnalyticData.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n\n/*!\n * \\brief Hydro initial data generated by SpEC.\n *\n * This class loads numerical data written out by the SpEC initial data solver.\n * It uses the `spec::Exporter` linked in from SpEC to interpolate to arbitrary\n * grid points. The coordinates are assumed to be in SpEC's \"grid\" frame.\n * We interpolate the following quantities:\n *\n * - \"g\": spatial metric\n * - \"K\": (lower) extrinsic curvature\n * - \"Lapse\": lapse\n * - \"Shift\": (upper) shift\n * - \"BaryonDensity\": rest mass density\n * - \"u_i\": lower spatial four-velocity\n *\n * The remaining hydro quantities are computed from the interpolated data and\n * the equation of state.\n * The magnetic field is set to zero and the electron fraction is set to a\n * constant read from the input file.\n */\ntemplate <size_t ThermodynamicDim>\nclass SpecInitialData : public evolution::initial_data::InitialData,\n                        public evolution::NumericInitialData,\n                        public AnalyticDataBase {\n private:\n  struct FromEos {};\n\n public:\n  using equation_of_state_type =\n      EquationsOfState::EquationOfState<true, ThermodynamicDim>;\n\n  template <typename DataType>\n  using tags = tmpl::append<\n      tmpl::list<gr::Tags::SpatialMetric<DataType, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataType, 3>,\n                 gr::Tags::Lapse<DataType>, gr::Tags::Shift<DataType, 3>>,\n      hydro::grmhd_tags<DataType>>;\n\n  static std::string name() {\n    return \"SpecInitialData\" + std::to_string(ThermodynamicDim) + \"dEos\";\n  }\n\n  struct DataDirectory {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"Path to a directory of data produced by SpEC. The directory is \"\n        \"expected to contain 'GrDomain.input' and 'Vars*.h5' files for all the \"\n        \"subdomains in GrDomain.input.\"};\n  };\n\n  struct DensityCutoff {\n    using type = double;\n    static constexpr Options::String help =\n        \"Where the density is below this cutoff the fluid variables are set to \"\n        \"vacuum (atmosphere density, atmosphere pressure, atmosphere energy \"\n        \"density/temperature and velocity, unit Lorentz factor and enthalpy). \"\n        \"During the evolution, atmosphere treatment will typically kick in and \"\n        \"fix the value of the fluid variables in these regions. Therefore, \"\n        \"it makes sense to set this density cutoff to the same value as the \"\n        \"atmosphere density cutoff.\";\n  };\n\n  struct AtmosphereDensity {\n    using type = double;\n    static constexpr Options::String help =\n        \"The value to set the density to in atmosphere. This must match what \"\n        \"is done during the evolution.\";\n  };\n\n  struct ElectronFraction {\n    using type = Options::Auto<double, FromEos>;\n    static constexpr Options::String help = {\n        \"Constant electron fraction.\\n\"\n        \"To calculate electron fraction from the Eos, set to 'FromEos'.\"};\n  };\n\n  using options = tmpl::list<\n      DataDirectory,\n      hydro::OptionTags::InitialDataEquationOfState<true, ThermodynamicDim>,\n      DensityCutoff, AtmosphereDensity, ElectronFraction>;\n\n  static constexpr Options::String help = {\"Initial data generated by SpEC\"};\n\n  SpecInitialData() = default;\n  SpecInitialData(const SpecInitialData& rhs);\n  SpecInitialData& operator=(const SpecInitialData& rhs);\n  SpecInitialData(SpecInitialData&& /*rhs*/) = default;\n  SpecInitialData& operator=(SpecInitialData&& /*rhs*/) = default;\n  ~SpecInitialData() override = default;\n\n  SpecInitialData(std::string data_directory,\n                  std::unique_ptr<equation_of_state_type> equation_of_state,\n                  double density_cutoff, double atmosphere_density,\n                  std::optional<double> electron_fraction);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit SpecInitialData(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SpecInitialData);\n  /// \\endcond\n\n  const equation_of_state_type& equation_of_state() const {\n    return *equation_of_state_;\n  }\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    auto interpolated_vars = interpolate_from_spec(x);\n    using requested_tags = tmpl::list<Tags...>;\n    using requested_derived_tags =\n        tmpl::list_difference<requested_tags, interpolated_tags<DataType>>;\n    using requested_interpolated_tags =\n        tmpl::list_difference<requested_tags, requested_derived_tags>;\n    tuples::TaggedTuple<Tags...> result{};\n    // First, compute derived quantities from interpolated data\n    if constexpr (tmpl::size<requested_derived_tags>::value > 0) {\n      VariablesCache<DataType> cache{get_size(get<0>(x))};\n      const VariablesComputer<DataType> computer{\n          interpolated_vars, *equation_of_state_, density_cutoff_,\n          atmosphere_density_, electron_fraction_};\n      tmpl::for_each<requested_derived_tags>(\n          [&result, &cache, &computer](const auto tag_v) {\n            using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n            get<tag>(result) = cache.get_var(computer, tag{});\n          });\n    }\n    // Then, move interpolated data into result buffer\n    tmpl::for_each<requested_interpolated_tags>(\n        [&result, &interpolated_vars](const auto tag_v) {\n          using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n          get<tag>(result) = std::move(get<tag>(interpolated_vars));\n        });\n    return result;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  /// These quantities are supported for interpolation from SpEC\n  template <typename DataType>\n  using interpolated_tags = tmpl::list<\n      // GR quantities\n      gr::Tags::SpatialMetric<DataType, 3>,\n      gr::Tags::ExtrinsicCurvature<DataType, 3>, gr::Tags::Lapse<DataType>,\n      gr::Tags::Shift<DataType, 3>,\n      // Hydro quantities\n      hydro::Tags::RestMassDensity<DataType>,\n      hydro::Tags::LowerSpatialFourVelocity<DataType, 3>>;\n\n  /// These are the names in SpEC datasets corresponding to the quantities above\n  static const inline std::vector<std::string> vars_to_interpolate_{\n      // GR quantities\n      \"g\", \"K\", \"Lapse\", \"Shift\",\n      // Hydro quantities\n      \"BaryonDensity\", \"u_i\"};\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<interpolated_tags<DataType>>\n  interpolate_from_spec(const tnsr::I<DataType, 3>& x) const;\n\n  /// This cache computes all derived quantities from the interpolated\n  /// quantities on demand\n  template <typename DataType>\n  using VariablesCache = cached_temp_buffer_from_typelist<tmpl::push_back<\n      tmpl::list_difference<tags<DataType>, interpolated_tags<DataType>>,\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataType, 3>,\n      gr::Tags::InverseSpatialMetric<DataType, 3>>>;\n\n  /// This is the computer for the `VariablesCache`\n  template <typename DataType>\n  struct VariablesComputer {\n    using Cache = VariablesCache<DataType>;\n\n    const tuples::tagged_tuple_from_typelist<interpolated_tags<DataType>>\n        interpolated_data;\n    const equation_of_state_type& eos;\n    const double density_cutoff;\n    const double atmosphere_density;\n    const std::optional<double>& electron_fraction_value;\n\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> specific_internal_energy,\n        gsl::not_null<Cache*> cache,\n        hydro::Tags::SpecificInternalEnergy<DataType> /*meta*/) const;\n    void operator()(gsl::not_null<Scalar<DataType>*> pressure,\n                    gsl::not_null<Cache*> cache,\n                    hydro::Tags::Pressure<DataType> /*meta*/) const;\n    void operator()(gsl::not_null<Scalar<DataType>*> specific_enthalpy,\n                    gsl::not_null<Cache*> cache,\n                    hydro::Tags::SpecificEnthalpy<DataType> /*meta*/) const;\n    void operator()(gsl::not_null<Scalar<DataType>*> temperature,\n                    gsl::not_null<Cache*> cache,\n                    hydro::Tags::Temperature<DataType> /*meta*/) const;\n    void operator()(gsl::not_null<tnsr::II<DataType, 3>*> inv_spatial_metric,\n                    gsl::not_null<Cache*> cache,\n                    gr::Tags::InverseSpatialMetric<DataType, 3> /*meta*/) const;\n    void operator()(\n        gsl::not_null<tnsr::I<DataType, 3>*>\n            lorentz_factor_times_spatial_velocity,\n        gsl::not_null<Cache*> cache,\n        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataType, 3> /*meta*/)\n        const;\n    void operator()(gsl::not_null<tnsr::I<DataType, 3>*> spatial_velocity,\n                    gsl::not_null<Cache*> cache,\n                    hydro::Tags::SpatialVelocity<DataType, 3> /*meta*/) const;\n    void operator()(gsl::not_null<Scalar<DataType>*> lorentz_factor,\n                    gsl::not_null<Cache*> cache,\n                    hydro::Tags::LorentzFactor<DataType> /*meta*/) const;\n    void operator()(gsl::not_null<Scalar<DataType>*> electron_fraction,\n                    gsl::not_null<Cache*> cache,\n                    hydro::Tags::ElectronFraction<DataType> /*meta*/) const;\n    void operator()(gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n                    gsl::not_null<Cache*> cache,\n                    hydro::Tags::MagneticField<DataType, 3> /*meta*/) const;\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> div_cleaning_field,\n        gsl::not_null<Cache*> cache,\n        hydro::Tags::DivergenceCleaningField<DataType> /*meta*/) const;\n  };\n\n  std::string data_directory_{};\n  std::unique_ptr<equation_of_state_type> equation_of_state_{nullptr};\n  double density_cutoff_ = std::numeric_limits<double>::signaling_NaN();\n  double atmosphere_density_ = std::numeric_limits<double>::signaling_NaN();\n  std::optional<double> electron_fraction_ = std::nullopt;\n\n  std::unique_ptr<spec::Exporter> spec_exporter_{nullptr};\n};\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/SphericalTorus.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SphericalTorus.hpp\"\n\n#include <pup.h>\n\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace grmhd::AnalyticData {\n\nSphericalTorus::SphericalTorus(const double r_min, const double r_max,\n                               const double min_polar_angle,\n                               const double fraction_of_torus,\n                               const double compression_level,\n                               const Options::Context& context)\n    : r_min_(r_min),\n      r_max_(r_max),\n      pi_over_2_minus_theta_min_(M_PI_2 - min_polar_angle),\n      fraction_of_torus_(fraction_of_torus),\n      compression_level_(compression_level) {\n  if (r_min_ <= 0.0) {\n    PARSE_ERROR(context, \"Minimum radius must be positive.\");\n  }\n  if (r_max_ <= r_min_) {\n    PARSE_ERROR(context, \"Maximum radius must be greater than minimum radius.\");\n  }\n  if (pi_over_2_minus_theta_min_ <= 0.0) {\n    PARSE_ERROR(context, \"Minimum polar angle should be less than pi/2.\");\n  }\n  if (pi_over_2_minus_theta_min_ >= M_PI_2) {\n    PARSE_ERROR(context, \"Minimum polar angle should be positive.\");\n  }\n  if (fraction_of_torus_ <= 0.0) {\n    PARSE_ERROR(context, \"Fraction of torus included must be positive.\");\n  }\n  if (fraction_of_torus_ > 1.0) {\n    PARSE_ERROR(context, \"Fraction of torus included must be at most 1.\");\n  }\n  if (compression_level_ < 0.0) {\n    PARSE_ERROR(context, \"Compression level must be non-negative.\");\n  }\n  // a = 1 can lead to 1/0 when computing inv_jacobian so we simply\n  // avoid this issue by restricting to a <1.\n  if (compression_level_ >= 1.0) {\n    PARSE_ERROR(context, \"Compression level must be less than 1.\");\n  }\n}\n\nSphericalTorus::SphericalTorus(const std::array<double, 2>& radial_range,\n                               const double min_polar_angle,\n                               const double fraction_of_torus,\n                               const double compression_level,\n                               const Options::Context& context)\n    : SphericalTorus(radial_range[0], radial_range[1], min_polar_angle,\n                     fraction_of_torus, compression_level, context) {}\n\n//\n//  * r       : radius\n//  * theta   : polar angle is measured from +z axis.\n//  * phi     : azimuthal angle\n//\ntemplate <typename T>\ntnsr::I<T, 3> SphericalTorus::operator()(\n    const tnsr::I<T, 3>& source_coords) const {\n  tnsr::I<T, 3> result;\n  const auto r = radius(get<0>(source_coords));\n  const T theta = M_PI_2 - (pi_over_2_minus_theta_min_ *\n                            cubic_compression(get<1>(source_coords)));\n  const T phi = M_PI * fraction_of_torus_ * get<2>(source_coords);\n  get<0>(result) = r * sin(theta) * cos(phi);\n  get<1>(result) = r * sin(theta) * sin(phi);\n  get<2>(result) = r * cos(theta);\n  return result;\n}\n\ntnsr::I<double, 3> SphericalTorus::inverse(\n    const tnsr::I<double, 3>& target_coords) const {\n  tnsr::I<double, 3> result;\n  const double r = std::hypot(get<0>(target_coords), get<1>(target_coords),\n                              get<2>(target_coords));\n  const double theta =\n      std::atan2(std::hypot(get<0>(target_coords), get<1>(target_coords)),\n                 get<2>(target_coords));\n  const double phi = std::atan2(get<1>(target_coords), get<0>(target_coords));\n  get<0>(result) = radius_inverse(r);\n\n  get<1>(result) = (M_PI_2 - theta) / pi_over_2_minus_theta_min_;\n  // if we are using compression (compression_level_ !=0), there is an extra\n  // step of inverting back to original eta before the cubic compression map.\n  if (compression_level_ != 0.0) {\n    get<1>(result) = cubic_inversion(get<1>(result));\n  }\n  get<2>(result) = phi / (M_PI * fraction_of_torus_);\n  return result;\n}\n\ntemplate <typename T>\nJacobian<T, 3, Frame::BlockLogical, Frame::Inertial> SphericalTorus::jacobian(\n    const tnsr::I<T, 3>& source_coords) const {\n  auto jacobian =\n      make_with_value<Jacobian<T, 3, Frame::BlockLogical, Frame::Inertial>>(\n          get<0>(source_coords), 0.0);\n\n  // In order to reduce number of memory allocations we use some slots of\n  // jacobian for storing temp variables as below.\n\n  auto& sin_theta = get<2, 1>(jacobian);\n  auto& cos_theta = get<2, 0>(jacobian);\n\n  get<2, 2>(jacobian) = M_PI_2 - (pi_over_2_minus_theta_min_ *\n                                  cubic_compression(get<1>(source_coords)));\n  sin_theta = sin(get<2, 2>(jacobian));\n  cos_theta = cos(get<2, 2>(jacobian));\n\n  auto& sin_phi = get<1, 1>(jacobian);\n  auto& cos_phi = get<1, 2>(jacobian);\n  get<2, 2>(jacobian) = M_PI * fraction_of_torus_ * get<2>(source_coords);\n  sin_phi = sin(get<2, 2>(jacobian));\n  cos_phi = cos(get<2, 2>(jacobian));\n\n  auto& r = get<2, 2>(jacobian);\n  radius(make_not_null(&r), get<0>(source_coords));\n\n  // Note : execution order matters here since we are overwriting each temp\n  // variables with jacobian values corresponding to the slot.\n  get<0, 0>(jacobian) = 0.5 * (r_max_ - r_min_) * sin_theta * cos_phi;\n  get<0, 1>(jacobian) = -pi_over_2_minus_theta_min_ * r * cos_theta * cos_phi;\n  get<0, 2>(jacobian) = -M_PI * fraction_of_torus_ * r * sin_theta * sin_phi;\n  get<1, 0>(jacobian) = 0.5 * (r_max_ - r_min_) * sin_theta * sin_phi;\n  get<1, 1>(jacobian) = -pi_over_2_minus_theta_min_ * r * cos_theta * sin_phi;\n  get<1, 2>(jacobian) = M_PI * fraction_of_torus_ * r * sin_theta * cos_phi;\n  get<2, 0>(jacobian) = 0.5 * (r_max_ - r_min_) * cos_theta;\n  get<2, 1>(jacobian) = pi_over_2_minus_theta_min_ * r * sin_theta;\n\n  // an extra factor of partial deriv. from equatorial compression map\n  get<2, 2>(jacobian) =\n      3.0 * compression_level_ * pow<2>(get<1>(source_coords)) +\n      (1.0 - compression_level_);\n  get<0, 1>(jacobian) *= get<2, 2>(jacobian);\n  get<1, 1>(jacobian) *= get<2, 2>(jacobian);\n  get<2, 1>(jacobian) *= get<2, 2>(jacobian);\n  // Set it back to 0\n  get<2, 2>(jacobian) = 0.0;\n  return jacobian;\n}\n\ntemplate <typename T>\nInverseJacobian<T, 3, Frame::BlockLogical, Frame::Inertial>\nSphericalTorus::inv_jacobian(const tnsr::I<T, 3>& source_coords) const {\n  auto inv_jacobian = make_with_value<\n      InverseJacobian<T, 3, Frame::BlockLogical, Frame::Inertial>>(\n      get<0>(source_coords), 0.0);\n\n  // In order to reduce number of memory allocations we use some slots of\n  // jacobian for storing temp variables as below.\n\n  auto& sin_theta = get<1, 2>(inv_jacobian);\n  auto& cos_theta = get<0, 2>(inv_jacobian);\n\n  get<2, 2>(inv_jacobian) = M_PI_2 - (pi_over_2_minus_theta_min_ *\n                                      cubic_compression(get<1>(source_coords)));\n  cos_theta = cos(get<2, 2>(inv_jacobian));\n  sin_theta = sin(get<2, 2>(inv_jacobian));\n\n  auto& sin_phi = get<1, 1>(inv_jacobian);\n  auto& cos_phi = get<2, 1>(inv_jacobian);\n  get<2, 2>(inv_jacobian) = M_PI * fraction_of_torus_ * get<2>(source_coords);\n  cos_phi = cos(get<2, 2>(inv_jacobian));\n  sin_phi = sin(get<2, 2>(inv_jacobian));\n\n  auto& r = get<2, 2>(inv_jacobian);\n  radius(make_not_null(&r), get<0>(source_coords));\n\n  // Note : execution order matters here since we are overwriting each temp\n  // variables with jacobian values corresponding to the slot.\n  get<0, 0>(inv_jacobian) = 2.0 / (r_max_ - r_min_) * sin_theta * cos_phi;\n  get<0, 1>(inv_jacobian) = 2.0 / (r_max_ - r_min_) * sin_theta * sin_phi;\n  get<1, 0>(inv_jacobian) =\n      -(1.0 / pi_over_2_minus_theta_min_) * cos_theta * cos_phi / r;\n  get<2, 0>(inv_jacobian) =\n      -(1.0 / (M_PI * fraction_of_torus_)) * sin_phi / (r * sin_theta);\n  get<1, 1>(inv_jacobian) =\n      -(1.0 / pi_over_2_minus_theta_min_) * cos_theta * sin_phi / r;\n  get<2, 1>(inv_jacobian) =\n      (1.0 / (M_PI * fraction_of_torus_)) * cos_phi / (r * sin_theta);\n  get<0, 2>(inv_jacobian) = 2.0 / (r_max_ - r_min_) * cos_theta;\n  get<1, 2>(inv_jacobian) = (1.0 / pi_over_2_minus_theta_min_) * sin_theta / r;\n\n  // an extra factor of partial deriv. from equatorial compression map.\n  get<2, 2>(inv_jacobian) =\n      (3.0 * compression_level_ * pow<2>(get<1>(source_coords)) +\n       (1.0 - compression_level_));\n  get<1, 0>(inv_jacobian) /= get<2, 2>(inv_jacobian);\n  get<1, 1>(inv_jacobian) /= get<2, 2>(inv_jacobian);\n  get<1, 2>(inv_jacobian) /= get<2, 2>(inv_jacobian);\n\n  // set it back to 0.\n  get<2, 2>(inv_jacobian) = 0.0;\n\n  return inv_jacobian;\n}\n\ntemplate <typename T>\ntnsr::Ijj<T, 3, Frame::NoFrame> SphericalTorus::hessian(\n    const tnsr::I<T, 3>& source_coords) const {\n  auto hessian = make_with_value<tnsr::Ijj<T, 3, Frame::NoFrame>>(\n      get<0>(source_coords), 0.0);\n\n  // In order to reduce number of memory allocations we use some slots of\n  // hessian for storing temp variables as below.\n\n  const double theta_factor = pi_over_2_minus_theta_min_;\n  const double phi_factor = M_PI * fraction_of_torus_;\n\n  // these two slots are zeros (not used)\n  // These two correspond to the derivative and double derivative of the\n  // cubic function we use for the equatorial compression.\n  auto& eta_prime = get<2, 0, 0>(hessian);\n  auto& eta_double_prime = get<2, 0, 2>(hessian);\n  eta_prime = 3 * compression_level_ * pow<2>(get<1>(source_coords)) + 1 -\n              compression_level_;\n  eta_double_prime = 6.0 * compression_level_ * get<1>(source_coords);\n\n  // these two slots are zeros (not used)\n  auto& cos_theta = get<2, 1, 2>(hessian);\n  auto& sin_theta = get<2, 2, 2>(hessian);\n\n  get<0, 0, 0>(hessian) =\n      M_PI_2 - (theta_factor * cubic_compression(get<1>(source_coords)));\n  cos_theta = cos(get<0, 0, 0>(hessian));\n  sin_theta = sin(get<0, 0, 0>(hessian));\n\n  // these two slots are NOT zeros, but will be overwritten with hessian later\n  auto& cos_phi = get<2, 0, 1>(hessian);\n  auto& sin_phi = get<2, 1, 1>(hessian);\n  get<0, 0, 0>(hessian) = phi_factor * get<2>(source_coords);\n  cos_phi = cos(get<0, 0, 0>(hessian));\n  sin_phi = sin(get<0, 0, 0>(hessian));\n\n  // these two slots are zeros (not used)\n  auto& r_factor = get<0, 0, 0>(hessian);\n  auto& r = get<1, 0, 0>(hessian);\n  r_factor = 0.5 * (r_max_ - r_min_);\n  radius(make_not_null(&r), get<0>(source_coords));\n\n  // Note : execution order matters here\n  get<0, 0, 1>(hessian) =\n      -r_factor * theta_factor * cos_theta * cos_phi * eta_prime;\n  get<0, 0, 2>(hessian) = -r_factor * phi_factor * sin_theta * sin_phi;\n  get<0, 1, 1>(hessian) =\n      -square(theta_factor * eta_prime) * r * sin_theta * cos_phi -\n      theta_factor * r * cos_theta * cos_phi * eta_double_prime;\n  get<0, 1, 2>(hessian) =\n      phi_factor * theta_factor * r * cos_theta * sin_phi * eta_prime;\n  get<0, 2, 2>(hessian) = -square(phi_factor) * r * sin_theta * cos_phi;\n  get<1, 0, 1>(hessian) =\n      -r_factor * theta_factor * cos_theta * sin_phi * eta_prime;\n  get<1, 0, 2>(hessian) = r_factor * phi_factor * sin_theta * cos_phi;\n  get<1, 1, 1>(hessian) =\n      -square(theta_factor * eta_prime) * r * sin_theta * sin_phi -\n      theta_factor * r * cos_theta * sin_phi * eta_double_prime;\n  get<1, 1, 2>(hessian) =\n      -phi_factor * theta_factor * r * cos_theta * cos_phi * eta_prime;\n  get<1, 2, 2>(hessian) = -square(phi_factor) * r * sin_theta * sin_phi;\n  get<2, 0, 1>(hessian) = r_factor * theta_factor * sin_theta * eta_prime;\n  get<2, 1, 1>(hessian) = -square(theta_factor * eta_prime) * r * cos_theta +\n                          theta_factor * r * sin_theta * eta_double_prime;\n\n  // remove temp vars and restore to zero\n  get<0, 0, 0>(hessian) = 0.0;\n  get<1, 0, 0>(hessian) = 0.0;\n  get<2, 0, 0>(hessian) = 0.0;\n  get<2, 0, 2>(hessian) = 0.0;\n  get<2, 1, 2>(hessian) = 0.0;\n  get<2, 2, 2>(hessian) = 0.0;\n\n  return hessian;\n}\n\ntemplate <typename T>\ntnsr::Ijk<T, 3, Frame::NoFrame> SphericalTorus::derivative_of_inv_jacobian(\n    const tnsr::I<T, 3>& source_coords) const {\n  auto result = make_with_value<tnsr::Ijk<T, 3, Frame::NoFrame>>(\n      get<0>(source_coords), 0.0);\n\n  // In order to reduce number of memory allocations we use some slots of\n  // `result` for storing temp variables as below.\n\n  const double theta_factor = pi_over_2_minus_theta_min_;\n  const double phi_factor = M_PI * fraction_of_torus_;\n\n  auto& cos_phi = get<1, 2, 2>(result);\n  auto& sin_phi = get<2, 2, 0>(result);\n  get<0, 0, 0>(result) = M_PI * fraction_of_torus_ * get<2>(source_coords);\n  cos_phi = cos(get<0, 0, 0>(result));\n  sin_phi = sin(get<0, 0, 0>(result));\n\n  auto& cos_theta = get<2, 2, 1>(result);\n  auto& sin_theta = get<2, 2, 2>(result);\n\n  get<0, 0, 0>(result) = M_PI_2 - (pi_over_2_minus_theta_min_ *\n                                   cubic_compression(get<1>(source_coords)));\n  cos_theta = cos(get<0, 0, 0>(result));\n  sin_theta = sin(get<0, 0, 0>(result));\n\n  // These two correspond to the derivative and double derivative of the\n  // cubic function we use for the equatorial compression.\n  auto& eta_prime = get<0, 2, 0>(result);\n  auto& eta_double_prime = get<0, 2, 2>(result);\n  eta_prime = 3.0 * compression_level_ * pow<2>(get<1>(source_coords)) + 1.0 -\n              compression_level_;\n  eta_double_prime = 6.0 * compression_level_ * get<1>(source_coords);\n\n  auto& r = get<0, 0, 0>(result);\n  auto& r_factor = get<0, 1, 0>(result);\n  radius(make_not_null(&r), get<0>(source_coords));\n  r_factor = 0.5 * (r_max_ - r_min_);\n\n  get<0, 0, 1>(result) =\n      -theta_factor / r_factor * cos_theta * cos_phi * eta_prime;\n  get<0, 0, 2>(result) = -phi_factor / r_factor * sin_theta * sin_phi;\n  get<0, 1, 1>(result) =\n      -theta_factor / r_factor * cos_theta * sin_phi * eta_prime;\n  get<0, 1, 2>(result) = phi_factor / r_factor * sin_theta * cos_phi;\n  get<0, 2, 1>(result) = theta_factor / r_factor * sin_theta * eta_prime;\n  get<1, 0, 0>(result) =\n      r_factor / theta_factor * cos_theta * cos_phi / (square(r) * eta_prime);\n  get<1, 0, 1>(result) =\n      (-cos_phi / r) * ((sin_theta) - ((cos_theta * eta_double_prime) /\n                                       (square(eta_prime) * theta_factor)));\n  get<1, 0, 2>(result) =\n      phi_factor / theta_factor * cos_theta * sin_phi / (r * eta_prime);\n  get<1, 1, 0>(result) =\n      r_factor / theta_factor * cos_theta * sin_phi / (square(r) * eta_prime);\n  get<1, 1, 1>(result) =\n      (-sin_phi / r) * ((sin_theta) - ((cos_theta * eta_double_prime) /\n                                       (square(eta_prime) * theta_factor)));\n  get<1, 1, 2>(result) =\n      -phi_factor / theta_factor * cos_theta * cos_phi / (r * eta_prime);\n  get<1, 2, 0>(result) =\n      -r_factor / theta_factor * sin_theta / (square(r) * eta_prime);\n  get<1, 2, 1>(result) =\n      (-1.0 / r) * (cos_theta + (sin_theta * eta_double_prime /\n                                 (square(eta_prime) * theta_factor)));\n  get<2, 0, 0>(result) =\n      r_factor / phi_factor * sin_phi / (square(r) * sin_theta);\n  get<2, 0, 1>(result) = -theta_factor / phi_factor * cos_theta * sin_phi *\n                         eta_prime / (r * square(sin_theta));\n  get<2, 0, 2>(result) = -cos_phi / (r * sin_theta);\n  get<2, 1, 0>(result) =\n      -r_factor / phi_factor * cos_phi / (square(r) * sin_theta);\n  get<2, 1, 1>(result) = theta_factor / phi_factor * cos_theta * cos_phi *\n                         eta_prime / (r * square(sin_theta));\n  get<2, 1, 2>(result) = -sin_phi / (r * sin_theta);\n\n  // remove temp vars and restore to zero\n  get<0, 0, 0>(result) = 0.0;\n  get<0, 1, 0>(result) = 0.0;\n  get<0, 2, 0>(result) = 0.0;\n  get<0, 2, 2>(result) = 0.0;\n  get<1, 2, 2>(result) = 0.0;\n  get<2, 2, 0>(result) = 0.0;\n  get<2, 2, 1>(result) = 0.0;\n  get<2, 2, 2>(result) = 0.0;\n\n  return result;\n}\n\nvoid SphericalTorus::pup(PUP::er& p) {\n  size_t version = 1;\n  p | version;\n  // Remember to increment the version number when making changes to this\n  // function. Retain support for unpacking data written by previous versions\n  // whenever possible. See `Domain` docs for details.\n  p | r_min_;\n  p | r_max_;\n  p | pi_over_2_minus_theta_min_;\n  p | fraction_of_torus_;\n  if (version > 0) {\n    p | compression_level_;\n  } else {\n    compression_level_ = 0;\n  }\n}\n\ntemplate <typename T>\nT SphericalTorus::radius(const T& x) const {\n  return 0.5 * r_min_ * (1.0 - x) + 0.5 * r_max_ * (1.0 + x);\n}\n\ntemplate <typename T>\nvoid SphericalTorus::radius(const gsl::not_null<T*> r, const T& x) const {\n  *r = 0.5 * r_min_ * (1.0 - x) + 0.5 * r_max_ * (1.0 + x);\n}\n\ntemplate <typename T>\nT SphericalTorus::radius_inverse(const T& r) const {\n  return ((r - r_min_) - (r_max_ - r)) / (r_max_ - r_min_);\n}\n\nbool operator==(const SphericalTorus& lhs, const SphericalTorus& rhs) {\n  return lhs.r_min_ == rhs.r_min_ and lhs.r_max_ == rhs.r_max_ and\n         lhs.pi_over_2_minus_theta_min_ == rhs.pi_over_2_minus_theta_min_ and\n         lhs.fraction_of_torus_ == rhs.fraction_of_torus_;\n}\n\ntemplate <typename T>\nT SphericalTorus::cubic_compression(const T& x) const {\n  return compression_level_ * pow<3>(x) + (1.0 - compression_level_) * x;\n}\n\ndouble SphericalTorus::cubic_inversion(const double x) const {\n  using std::abs;\n  // using p.228 of Numerical Recipe (cubic equation)\n  const double q = (compression_level_ - 1.0) / (3.0 * compression_level_);\n  const double r = -0.5 * x / (compression_level_);\n  const double a =\n      -sgn(r) * pow(abs(r) + sqrt(square(r) - pow<3>(q)), 1.0 / 3.0);\n  double b = 0.0;\n  if (a != 0.0) {\n    b = q / a;\n  }\n  return a + b;\n}\n\nbool operator!=(const SphericalTorus& lhs, const SphericalTorus& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template tnsr::I<DTYPE(data), 3> SphericalTorus::operator()(                \\\n      const tnsr::I<DTYPE(data), 3>& source_coords) const;                    \\\n  template Jacobian<DTYPE(data), 3, Frame::BlockLogical, Frame::Inertial>     \\\n  SphericalTorus::jacobian(const tnsr::I<DTYPE(data), 3>& source_coords)      \\\n      const;                                                                  \\\n  template InverseJacobian<DTYPE(data), 3, Frame::BlockLogical,               \\\n                           Frame::Inertial>                                   \\\n  SphericalTorus::inv_jacobian(const tnsr::I<DTYPE(data), 3>& source_coords)  \\\n      const;                                                                  \\\n  template tnsr::Ijj<DTYPE(data), 3, Frame::NoFrame> SphericalTorus::hessian( \\\n      const tnsr::I<DTYPE(data), 3>& source_coords) const;                    \\\n  template tnsr::Ijk<DTYPE(data), 3, Frame::NoFrame>                          \\\n  SphericalTorus::derivative_of_inv_jacobian(                                 \\\n      const tnsr::I<DTYPE(data), 3>& source_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/GrMhd/SphericalTorus.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace grmhd::AnalyticData {\n/*!\n * \\brief Torus made by removing two polar cones from a spherical shell\n *\n * Maps source coordinates \\f$(\\xi, \\eta, \\zeta)\\f$ to\n * \\f{align}\n * \\vec{x}(\\xi, \\eta, \\zeta) =\n * \\begin{bmatrix}\n *  r \\sin\\theta\\cos\\phi \\\\\n *  r \\sin\\theta\\sin\\phi \\\\\n *  r \\cos\\theta\n * \\end{bmatrix}\n * \\f}\n *\n * where\n * \\f{align}\n *  r & = r_\\mathrm{min}\\frac{1-\\xi}{2} + r_\\mathrm{max}\\frac{1+\\xi}{2}, \\\\\n *  \\eta_\\mathrm{new} & = a_\\mathrm{comp} \\eta^3 + (1-a_\\mathrm{comp}) \\eta, \\\\\n *  \\theta & = \\pi/2 - (\\pi/2 - \\theta_\\mathrm{min}) \\eta_\\mathrm{new}, \\\\\n *  \\phi   & = f_\\mathrm{torus} \\pi \\zeta.\n * \\f}\n *\n *  - $r_\\mathrm{min}$ and $r_\\mathrm{max}$ are inner and outer radius of torus.\n *  - $\\theta_\\mathrm{min}\\in(0,\\pi/2)$ is the minimum polar angle (measured\n *    from +z axis) of torus, which is equal to the half of the apex angle of\n *    the removed polar cones.\n *  - $f_\\mathrm{torus}\\in(0, 1)$ is azimuthal fraction that the torus covers.\n *  - $a_\\mathrm{comp}\\in[0,1)$ sets the level of equatorial compression\n *    for theta, with zero being none.\n *\n */\nclass SphericalTorus {\n public:\n  static constexpr size_t dim = 3;\n\n  struct RadialRange {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help =\n        \"Radial extent of the torus, \"\n        \"[min_radius, max_radius] \";\n  };\n\n  struct MinPolarAngle {\n    using type = double;\n    static constexpr Options::String help =\n        \"Half of the apex angle of excised polar cones. \"\n        \"Polar angle (measured from +z axis) of torus has range \"\n        \"[MinPolarAngle, pi - MinPolarAngle]\";\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 0.5 * M_PI; }\n  };\n\n  struct FractionOfTorus {\n    using type = double;\n    static constexpr Options::String help =\n        \"Fraction of (azimuthal) orbit covered. Azimuthal angle has range \"\n        \"[- pi * FractionOfTorus, pi * FractionOfTorus].\";\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n\n  struct CompressionLevel {\n    using type = double;\n    static constexpr Options::String help =\n        \"Level of Equatorial Compression for the polar angle.\";\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n\n  static constexpr Options::String help =\n      \"Torus made by removing polar cones from a spherical shell\";\n\n  using options =\n      tmpl::list<RadialRange, MinPolarAngle, FractionOfTorus, CompressionLevel>;\n\n  SphericalTorus(const std::array<double, 2>& radial_range,\n                 double min_polar_angle, double fraction_of_torus,\n                 double compression_level,\n                 const Options::Context& context = {});\n\n  SphericalTorus(double r_min, double r_max, double min_polar_angle,\n                 double fraction_of_torus = 1.0, double compression_level = 0.0,\n                 const Options::Context& context = {});\n\n  SphericalTorus() = default;\n\n  template <typename T>\n  tnsr::I<T, 3> operator()(const tnsr::I<T, 3>& source_coords) const;\n\n  tnsr::I<double, 3> inverse(const tnsr::I<double, 3>& target_coords) const;\n\n  template <typename T>\n  Jacobian<T, 3, Frame::BlockLogical, Frame::Inertial> jacobian(\n      const tnsr::I<T, 3>& source_coords) const;\n\n  template <typename T>\n  InverseJacobian<T, 3, Frame::BlockLogical, Frame::Inertial> inv_jacobian(\n      const tnsr::I<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ijj<T, 3, Frame::NoFrame> hessian(\n      const tnsr::I<T, 3>& source_coords) const;\n\n  template <typename T>\n  tnsr::Ijk<T, 3, Frame::NoFrame> derivative_of_inv_jacobian(\n      const tnsr::I<T, 3>& source_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  bool is_identity() const { return false; }\n\n private:\n  template <typename T>\n  T radius(const T& x) const;\n\n  template <typename T>\n  void radius(gsl::not_null<T*> r, const T& x) const;\n\n  template <typename T>\n  T radius_inverse(const T& r) const;\n\n  template <typename T>\n  T cubic_compression(const T& x) const;\n\n  double cubic_inversion(double x) const;\n\n  friend bool operator==(const SphericalTorus& lhs, const SphericalTorus& rhs);\n\n  double r_min_ = std::numeric_limits<double>::signaling_NaN();\n  double r_max_ = std::numeric_limits<double>::signaling_NaN();\n  double pi_over_2_minus_theta_min_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double fraction_of_torus_ = std::numeric_limits<double>::signaling_NaN();\n  double compression_level_ = std::numeric_limits<double>::signaling_NaN();\n};\n\nbool operator!=(const SphericalTorus& lhs, const SphericalTorus& rhs);\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/NewtonianEuler/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace NewtonianEuler {\n/*!\n * \\ingroup AnalyticDataGroup\n * \\brief Holds classes implementing analytic data for the NewtonianEuler system\n */\nnamespace AnalyticData {}\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/NewtonianEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY NewtonianEulerAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  KhInstability.cpp\n  ShuOsherTube.cpp\n  SodExplosion.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  KhInstability.hpp\n  ShuOsherTube.hpp\n  SodExplosion.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Hydro\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/NewtonianEuler/KhInstability.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/KhInstability.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace NewtonianEuler::AnalyticData {\n\ntemplate <size_t Dim>\nKhInstability<Dim>::KhInstability(\n    const double adiabatic_index, const double strip_bimedian_height,\n    const double strip_thickness, const double strip_density,\n    const double strip_velocity, const double background_density,\n    const double background_velocity, const double pressure,\n    const double perturbation_amplitude, const double perturbation_width)\n    : adiabatic_index_(adiabatic_index),\n      strip_bimedian_height_(strip_bimedian_height),\n      strip_half_thickness_(0.5 * strip_thickness),\n      strip_density_(strip_density),\n      strip_velocity_(strip_velocity),\n      background_density_(background_density),\n      background_velocity_(background_velocity),\n      pressure_(pressure),\n      perturbation_amplitude_(perturbation_amplitude),\n      perturbation_width_(perturbation_width),\n      equation_of_state_(adiabatic_index) {\n  ASSERT(strip_density_ > 0.0 and background_density_ > 0.0,\n         \"The mass density must be positive everywhere. Inner \"\n         \"density: \"\n             << strip_density_ << \", Outer density: \" << background_density_\n             << \".\");\n  ASSERT(pressure_ > 0.0, \"The pressure must be positive. The value given was \"\n                              << pressure_ << \".\");\n  ASSERT(perturbation_width_ > 0.0,\n         \"The damping factor must be positive. The value given was \"\n             << perturbation_width_ << \".\");\n  ASSERT(strip_thickness > 0.0,\n         \"The strip thickness must be positive. The value given was \"\n             << strip_thickness << \".\");\n}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::initial_data::InitialData>\nKhInstability<Dim>::get_clone() const {\n  return std::make_unique<KhInstability>(*this);\n}\n\ntemplate <size_t Dim>\nKhInstability<Dim>::KhInstability(CkMigrateMessage* msg)\n    : evolution::initial_data::InitialData(msg) {}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID KhInstability<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nvoid KhInstability<Dim>::pup(PUP::er& p) {\n  evolution::initial_data::InitialData::pup(p);\n  p | adiabatic_index_;\n  p | strip_bimedian_height_;\n  p | strip_half_thickness_;\n  p | strip_density_;\n  p | strip_velocity_;\n  p | background_density_;\n  p | background_velocity_;\n  p | pressure_;\n  p | perturbation_amplitude_;\n  p | perturbation_width_;\n  p | equation_of_state_;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nKhInstability<Dim>::variables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  auto result = make_with_value<Scalar<DataType>>(x, 0.0);\n  const size_t n_pts = get_size(get<0>(x));\n  for (size_t s = 0; s < n_pts; ++s) {\n    get_element(get(result), s) =\n        abs(get_element(get<Dim - 1>(x), s) - strip_bimedian_height_) <\n                strip_half_thickness_\n            ? strip_density_\n            : background_density_;\n  }\n  return {std::move(result)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<\n    hydro::Tags::SpatialVelocity<DataType, Dim, Frame::Inertial>>\nKhInstability<Dim>::variables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim,\n                                            Frame::Inertial>> /*meta*/) const {\n  auto result =\n      make_with_value<tnsr::I<DataType, Dim, Frame::Inertial>>(x, 0.0);\n\n  const size_t n_pts = get_size(get<0>(x));\n  for (size_t s = 0; s < n_pts; ++s) {\n    get_element(get<0>(result), s) =\n        abs(get_element(get<Dim - 1>(x), s) - strip_bimedian_height_) <\n                strip_half_thickness_\n            ? strip_velocity_\n            : background_velocity_;\n  }\n\n  const double one_over_two_sigma_squared = 0.5 / square(perturbation_width_);\n  const double strip_lower_bound =\n      strip_bimedian_height_ - strip_half_thickness_;\n  const double strip_upper_bound =\n      strip_bimedian_height_ + strip_half_thickness_;\n  get<Dim - 1>(result) = exp(-one_over_two_sigma_squared *\n                             square(get<Dim - 1>(x) - strip_lower_bound)) +\n                         exp(-one_over_two_sigma_squared *\n                             square(get<Dim - 1>(x) - strip_upper_bound));\n  get<Dim - 1>(result) *= perturbation_amplitude_ * sin(4.0 * M_PI * get<0>(x));\n\n  return {std::move(result)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nKhInstability<Dim>::variables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nKhInstability<Dim>::variables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return make_with_value<Scalar<DataType>>(x, pressure_);\n}\n\ntemplate <size_t Dim>\nbool operator==(const KhInstability<Dim>& lhs, const KhInstability<Dim>& rhs) {\n  // No comparison for equation_of_state_. Comparing adiabatic_index_ should\n  // suffice.\n  return lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.strip_bimedian_height_ == rhs.strip_bimedian_height_ and\n         lhs.strip_half_thickness_ == rhs.strip_half_thickness_ and\n         lhs.strip_density_ == rhs.strip_density_ and\n         lhs.strip_velocity_ == rhs.strip_velocity_ and\n         lhs.background_density_ == rhs.background_density_ and\n         lhs.background_velocity_ == rhs.background_velocity_ and\n         lhs.pressure_ == rhs.pressure_ and\n         lhs.perturbation_amplitude_ == rhs.perturbation_amplitude_ and\n         lhs.perturbation_width_ == rhs.perturbation_width_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const KhInstability<Dim>& lhs, const KhInstability<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE_CLASS(_, data)                           \\\n  template class KhInstability<DIM(data)>;                   \\\n  template bool operator==(const KhInstability<DIM(data)>&,  \\\n                           const KhInstability<DIM(data)>&); \\\n  template bool operator!=(const KhInstability<DIM(data)>&,  \\\n                           const KhInstability<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_CLASS, (2, 3))\n\n#define INSTANTIATE_SCALARS(_, data)                                 \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)> >            \\\n      KhInstability<DIM(data)>::variables(                           \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x, \\\n          tmpl::list<TAG(data) < DTYPE(data)> >) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (2, 3), (double, DataVector),\n                        (hydro::Tags::RestMassDensity,\n                         hydro::Tags::SpecificInternalEnergy,\n                         hydro::Tags::Pressure))\n\n#define INSTANTIATE_VELOCITY(_, data)                                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), DIM(data),           \\\n                               Frame::Inertial> >                            \\\n      KhInstability<DIM(data)>::variables(                                   \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x,         \\\n          tmpl::list<TAG(data) < DTYPE(data), DIM(data), Frame::Inertial> >) \\\n          const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VELOCITY, (2, 3), (double, DataVector),\n                        (hydro::Tags::SpatialVelocity))\n\n#undef INSTANTIATE_VELOCITY\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_CLASS\n#undef TAG\n#undef DTYPE\n#undef DIM\n}  // namespace NewtonianEuler::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/NewtonianEuler/KhInstability.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::AnalyticData {\n\n/*!\n * \\brief Initial data to simulate the Kelvin-Helmholtz instability.\n *\n * For comparison purposes, this class implements the planar shear of\n * \\cite Schaal2015, which is evolved using periodic boundary conditions.\n * The initial state consists of a horizontal strip of mass\n * density \\f$\\rho_\\text{in}\\f$ moving with horizontal speed\n * \\f$v_{\\text{in}}\\f$. The rest of the fluid possesses mass density\n * \\f$\\rho_\\text{out}\\f$, and its horizontal velocity is \\f$v_{\\text{out}}\\f$,\n * both constant. Mathematically,\n *\n * \\f{align*}\n * \\rho(x, y) =\n * \\begin{cases}\n * \\rho_\\text{in}, & \\left|y - y_\\text{mid}\\right| < b/2\\\\\n * \\rho_\\text{out}, & \\text{otherwise},\n * \\end{cases}\n * \\f}\n *\n * and\n *\n * \\f{align*}\n * v_x(x, y) =\n * \\begin{cases}\n * v_{\\text{in}}, & \\left|y - y_\\text{mid}\\right| < b/2\\\\\n * v_{\\text{out}}, & \\text{otherwise},\n * \\end{cases}\n * \\f}\n *\n * where \\f$b > 0\\f$ is the thickness of the strip, and \\f$y = y_\\text{mid}\\f$\n * is its horizontal bimedian. The initial pressure is set equal to a constant,\n * and the system is evolved assuming an ideal fluid of known adiabatic index.\n * Finally, in order to excite the instability, the vertical velocity is\n * initialized to\n *\n * \\f{align*}\n * v_y(x, y) = A\\sin(4\\pi x)\n * \\left[\\exp\\left(-\\dfrac{(y - y_\\text{top})^2}{2\\sigma^2}\\right) +\n * \\exp\\left(-\\dfrac{(y - y_\\text{bot})^2}{2\\sigma^2}\\right)\\right],\n * \\f}\n *\n * whose net effect is to perturb the horizontal boundaries of the strip\n * periodically along the \\f$x-\\f$axis. Here \\f$A\\f$ is the amplitude,\n * \\f$\\sigma\\f$ is a characteristic length for the perturbation width,\n * and \\f$y_\\text{top} = y_\\text{mid} + b/2\\f$ and\n * \\f$y_\\text{bot} = y_\\text{mid} - b/2\\f$ are the vertical coordinates\n * of the top and bottom boundaries of the strip, respectively.\n *\n * \\note The periodic form of the perturbation enforces the horizontal\n * extent of the domain to be a multiple of 0.5. The domain chosen in\n * \\cite Schaal2015 is the square \\f$[0, 1]^2\\f$, which covers two wavelengths\n * of the perturbation. In addition, the authors ran their simulations using\n *\n * ```\n * AdiabaticIndex: 1.4\n * StripBimedianHeight: 0.5\n * StripThickness: 0.5\n * StripDensity: 2.0\n * StripVelocity: 0.5\n * BackgroundDensity: 1.0\n * BackgroundVelocity: -0.5\n * Pressure: 2.5\n * PerturbAmplitude: 0.1\n * PerturbWidth: 0.035355339059327376 # 0.05/sqrt(2)\n * ```\n *\n * \\note This class can be used to initialize 2D and 3D data. In 3D, the strip\n * is aligned to the \\f$xy-\\f$plane, and the vertical direction is taken to be\n * the \\f$z-\\f$axis.\n */\ntemplate <size_t Dim>\nclass KhInstability : public evolution::initial_data::InitialData,\n                      public MarkAsAnalyticData {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<false>;\n\n  /// The adiabatic index of the fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the fluid.\"};\n  };\n\n  /// The vertical coordinate of the horizontal bimedian of the strip.\n  struct StripBimedianHeight {\n    using type = double;\n    static constexpr Options::String help = {\"The height of the strip center.\"};\n  };\n\n  /// The thickness of the strip.\n  struct StripThickness {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The thickness of the horizontal strip.\"};\n  };\n\n  /// The mass density in the strip\n  struct StripDensity {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The mass density in the horizontal strip.\"};\n  };\n\n  /// The velocity along \\f$x\\f$ in the strip\n  struct StripVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The velocity along x in the horizontal strip.\"};\n  };\n\n  /// The mass density outside of the strip\n  struct BackgroundDensity {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The mass density outside of the strip.\"};\n  };\n\n  /// The velocity along \\f$x\\f$ outside of the strip\n  struct BackgroundVelocity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The velocity along x outside of the strip.\"};\n  };\n\n  /// The initial (constant) pressure of the fluid\n  struct Pressure {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The initial (constant) pressure.\"};\n  };\n\n  /// The amplitude of the perturbation\n  struct PerturbAmplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The amplitude of the perturbation.\"};\n  };\n\n  /// The characteristic length for the width of the perturbation\n  struct PerturbWidth {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"The characteristic length for the width of the perturbation.\"};\n  };\n\n  using options =\n      tmpl::list<AdiabaticIndex, StripBimedianHeight, StripThickness,\n                 StripDensity, StripVelocity, BackgroundDensity,\n                 BackgroundVelocity, Pressure, PerturbAmplitude, PerturbWidth>;\n\n  static constexpr Options::String help = {\n      \"Initial data to simulate the KH instability.\"};\n\n  KhInstability() = default;\n  KhInstability(const KhInstability& /*rhs*/) = default;\n  KhInstability& operator=(const KhInstability& /*rhs*/) = default;\n  KhInstability(KhInstability&& /*rhs*/) = default;\n  KhInstability& operator=(KhInstability&& /*rhs*/) = default;\n  ~KhInstability() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit KhInstability(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(KhInstability);\n  /// \\endcond\n\n  KhInstability(double adiabatic_index, double strip_bimedian_height,\n                double strip_thickness, double strip_density,\n                double strip_velocity, double background_density,\n                double background_velocity, double pressure,\n                double perturbation_amplitude, double perturbation_width);\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n      tmpl::list<Tags...> /*meta*/) const {\n    return {tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  const EquationsOfState::IdealFluid<false>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  /// @{\n  /// Retrieve hydro variable at `x`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/\n  ) const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<\n                     DataType, Dim, Frame::Inertial>> /*meta*/) const\n      -> tuples::TaggedTuple<\n          hydro::Tags::SpatialVelocity<DataType, Dim, Frame::Inertial>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/\n  ) const -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/\n  ) const -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n  /// @}\n\n  template <size_t SpatialDim>\n  friend bool\n  operator==(  // NOLINT (clang-tidy: readability-redundant-declaration)\n      const KhInstability<SpatialDim>& lhs,\n      const KhInstability<SpatialDim>& rhs);\n\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  double strip_bimedian_height_ = std::numeric_limits<double>::signaling_NaN();\n  double strip_half_thickness_ = std::numeric_limits<double>::signaling_NaN();\n  double strip_density_ = std::numeric_limits<double>::signaling_NaN();\n  double strip_velocity_ = std::numeric_limits<double>::signaling_NaN();\n  double background_density_ = std::numeric_limits<double>::signaling_NaN();\n  double background_velocity_ = std::numeric_limits<double>::signaling_NaN();\n  double pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double perturbation_amplitude_ = std::numeric_limits<double>::signaling_NaN();\n  double perturbation_width_ = std::numeric_limits<double>::signaling_NaN();\n  EquationsOfState::IdealFluid<false> equation_of_state_{};\n};\n\ntemplate <size_t Dim>\nbool operator!=(const KhInstability<Dim>& lhs, const KhInstability<Dim>& rhs);\n\n}  // namespace NewtonianEuler::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/NewtonianEuler/ShuOsherTube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/ShuOsherTube.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace NewtonianEuler::AnalyticData {\nShuOsherTube::ShuOsherTube(const double jump_position,\n                           const double mass_density_l, const double velocity_l,\n                           const double pressure_l, const double velocity_r,\n                           const double pressure_r, const double epsilon,\n                           const double lambda)\n    : mass_density_l_(mass_density_l),\n      velocity_l_(velocity_l),\n      pressure_l_(pressure_l),\n      jump_position_(jump_position),\n      epsilon_(epsilon),\n      lambda_(lambda),\n      velocity_r_(velocity_r),\n      pressure_r_(pressure_r) {\n  ASSERT(mass_density_l_ > 0.0,\n         \"The left mass_density must be greater than 0 but is \"\n             << mass_density_l_);\n  ASSERT(pressure_l_ > 0.0,\n         \"The left pressure must be greater than 0 but is \" << pressure_l_);\n  ASSERT(pressure_r_ > 0.0,\n         \"The right pressure must be greater than 0 but is \" << pressure_r_);\n  ASSERT(epsilon_ > 0.0,\n         \"The sinusoid amplitude must be greater than 0 but is \" << epsilon_);\n  ASSERT(epsilon_ < 1.0,\n         \"The sinusoid amplitude must be less than 1 but is \" << epsilon_);\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> ShuOsherTube::get_clone()\n    const {\n  return std::make_unique<ShuOsherTube>(*this);\n}\n\nShuOsherTube::ShuOsherTube(CkMigrateMessage* msg)\n    : evolution::initial_data::InitialData(msg) {}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID ShuOsherTube::my_PUP_ID = 0;\n\nvoid ShuOsherTube::pup(PUP::er& p) {\n  evolution::initial_data::InitialData::pup(p);\n  p | mass_density_l_;\n  p | velocity_l_;\n  p | pressure_l_;\n  p | jump_position_;\n  p | epsilon_;\n  p | lambda_;\n  p | velocity_r_;\n  p | pressure_r_;\n  p | adiabatic_index_;\n  p | equation_of_state_;\n}\n\nbool operator==(const ShuOsherTube& lhs, const ShuOsherTube& rhs) {\n  // No comparison for equation_of_state_. Comparing adiabatic_index_ should\n  // suffice.\n  return lhs.mass_density_l_ == rhs.mass_density_l_ and\n         lhs.velocity_l_ == rhs.velocity_l_ and\n         lhs.pressure_l_ == rhs.pressure_l_ and\n         lhs.jump_position_ == rhs.jump_position_ and\n         lhs.epsilon_ == rhs.epsilon_ and lhs.lambda_ == rhs.lambda_ and\n         lhs.velocity_r_ == rhs.velocity_r_ and\n         lhs.pressure_r_ == rhs.pressure_r_ and\n         lhs.adiabatic_index_ == rhs.adiabatic_index_;\n}\n\nbool operator!=(const ShuOsherTube& lhs, const ShuOsherTube& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nShuOsherTube::variables(\n    const tnsr::I<DataType, 1, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  auto mass_density = make_with_value<Scalar<DataType>>(x, 0.0);\n  for (size_t s = 0; s < get_size(get<0>(x)); ++s) {\n    const double x_s = get_element(get<0>(x), s);\n    get_element(get(mass_density), s) =\n        x_s < jump_position_ ? mass_density_l_\n                             : 1.0 + epsilon_ * sin(lambda_ * x_s);\n  }\n  return mass_density;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 1, Frame::Inertial>>\nShuOsherTube::variables(const tnsr::I<DataType, 1, Frame::Inertial>& x,\n                        tmpl::list<hydro::Tags::SpatialVelocity<\n                            DataType, 1, Frame::Inertial>> /*meta*/) const {\n  auto velocity =\n      make_with_value<tnsr::I<DataType, 1, Frame::Inertial>>(x, 0.0);\n  for (size_t s = 0; s < get_size(get<0>(x)); ++s) {\n    const double x_s = get_element(get<0>(x), s);\n    get_element(get<0>(velocity), s) =\n        x_s < jump_position_ ? velocity_l_ : velocity_r_;\n  }\n  return velocity;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> ShuOsherTube::variables(\n    const tnsr::I<DataType, 1, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  auto pressure = make_with_value<Scalar<DataType>>(x, 0.0);\n  for (size_t s = 0; s < get_size(get<0>(x)); ++s) {\n    const double x_s = get_element(get<0>(x), s);\n    get_element(get(pressure), s) =\n        x_s < jump_position_ ? pressure_l_ : pressure_r_;\n  }\n  return pressure;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nShuOsherTube::variables(\n    const tnsr::I<DataType, 1, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                         \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>                     \\\n      ShuOsherTube::variables(                                               \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x_shifted, \\\n          tmpl::list<TAG(data) < DTYPE(data)>>) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (1), (double, DataVector),\n                        (hydro::Tags::RestMassDensity, hydro::Tags::Pressure,\n                         hydro::Tags::SpecificInternalEnergy))\n\n#define INSTANTIATE_VELOCITY(_, data)                                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), DIM(data),           \\\n                               Frame::Inertial>>                             \\\n      ShuOsherTube::variables(                                               \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x_shifted, \\\n          tmpl::list<TAG(data) < DTYPE(data), DIM(data), Frame::Inertial>>)  \\\n          const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VELOCITY, (1), (double, DataVector),\n                        (hydro::Tags::SpatialVelocity))\n\n#undef DIM\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VELOCITY\n}  // namespace NewtonianEuler::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/NewtonianEuler/ShuOsherTube.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::AnalyticData {\n/*!\n * \\brief Initial data for the Shu-Osher oscillatory shock tube \\cite Shu1988439\n *\n * The general initial data is given by:\n *\n * \\f{align*}{\n *  \\{\\rho,v^x,p\\}=\n * \\left\\{\n * \\begin{array}{ll}\n *   \\left\\{\\rho_L, v^x_L, p_L\\right\\} &\\mathrm{if} \\;\\;\\; x<\\Delta \\\\\n *   \\left\\{1+\\epsilon\\sin(\\lambda x), v^x_R, p_R\\right\\} &\\mathrm{if} \\;\\;\\;\n *    x\\ge \\Delta\n * \\end{array}\\right.\n * \\f}\n *\n * with the adiabatic index being 1.4.\n *\n * With the standard parameters given below, this is a Mach-3 shock moving into\n * a sinusoidal density profile.\n *\n * \\f{align*}{\n *  \\{\\rho,v^x,p\\}=\n * \\left\\{\n * \\begin{array}{ll}\n *   \\left\\{3.857143, 2.629369, 10.33333\\right\\} &\\mathrm{if} \\;\\;\\; x<-4 \\\\\n *   \\left\\{1+0.2\\sin(5x), 0, 1\\right\\} &\\mathrm{if} \\;\\;\\; x\\ge -4\n * \\end{array}\\right.\n * \\f}\n *\n * With these values the usual final time is 1.8.\n */\nclass ShuOsherTube : public evolution::initial_data::InitialData,\n                     public MarkAsAnalyticData {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<false>;\n\n  /// Initial postition of the discontinuity\n  struct JumpPosition {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial position of the discontinuity.\"};\n  };\n\n  struct LeftMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\"The left mass density.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct LeftVelocity {\n    using type = double;\n    static constexpr Options::String help = {\"The left velocity.\"};\n  };\n\n  struct LeftPressure {\n    using type = double;\n    static constexpr Options::String help = {\"The left pressure.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct RightVelocity {\n    using type = double;\n    static constexpr Options::String help = {\"The right velocity.\"};\n  };\n\n  struct RightPressure {\n    using type = double;\n    static constexpr Options::String help = {\"The right pressure.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Epsilon {\n    using type = double;\n    static constexpr Options::String help = {\"Sinusoid amplitude.\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n\n  struct Lambda {\n    using type = double;\n    static constexpr Options::String help = {\"Sinusoid wavelength.\"};\n  };\n\n  static constexpr Options::String help = {\n      \"1D Shu-Osher oscillatory shock tube.\"};\n\n  using options =\n      tmpl::list<JumpPosition, LeftMassDensity, LeftVelocity, LeftPressure,\n                 RightVelocity, RightPressure, Epsilon, Lambda>;\n\n  ShuOsherTube(double jump_position, double mass_density_l, double velocity_l,\n               double pressure_l, double velocity_r, double pressure_r,\n               double epsilon, double lambda);\n  ShuOsherTube() = default;\n  ShuOsherTube(const ShuOsherTube& /*rhs*/) = default;\n  ShuOsherTube& operator=(const ShuOsherTube& /*rhs*/) = default;\n  ShuOsherTube(ShuOsherTube&& /*rhs*/) = default;\n  ShuOsherTube& operator=(ShuOsherTube&& /*rhs*/) = default;\n  ~ShuOsherTube() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit ShuOsherTube(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ShuOsherTube);\n  /// \\endcond\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, 1, Frame::Inertial>& x,\n      tmpl::list<Tags...> /*meta*/) const {\n    return {tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  const EquationsOfState::IdealFluid<false>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 1, Frame::Inertial>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 1, Frame::Inertial>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<\n                     DataType, 1, Frame::Inertial>> /*meta*/) const\n      -> tuples::TaggedTuple<\n          hydro::Tags::SpatialVelocity<DataType, 1, Frame::Inertial>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 1, Frame::Inertial>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 1, Frame::Inertial>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  friend bool\n  operator==(  // NOLINT (clang-tidy: readability-redundant-declaration)\n      const ShuOsherTube& lhs, const ShuOsherTube& rhs);\n\n  double mass_density_l_ = std::numeric_limits<double>::signaling_NaN();\n  double velocity_l_ = std::numeric_limits<double>::signaling_NaN();\n  double pressure_l_ = std::numeric_limits<double>::signaling_NaN();\n  double jump_position_ = std::numeric_limits<double>::signaling_NaN();\n  double epsilon_ = std::numeric_limits<double>::signaling_NaN();\n  double lambda_ = std::numeric_limits<double>::signaling_NaN();\n  double velocity_r_ = std::numeric_limits<double>::signaling_NaN();\n  double pressure_r_ = std::numeric_limits<double>::signaling_NaN();\n  double adiabatic_index_ = 1.4;\n  EquationsOfState::IdealFluid<false> equation_of_state_{adiabatic_index_};\n};\n\nbool operator!=(const ShuOsherTube& lhs, const ShuOsherTube& rhs);\n}  // namespace NewtonianEuler::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/NewtonianEuler/SodExplosion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/SodExplosion.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace NewtonianEuler::AnalyticData {\ntemplate <size_t Dim>\nSodExplosion<Dim>::SodExplosion(const double initial_radius,\n                                const double inner_mass_density,\n                                const double inner_pressure,\n                                const double outer_mass_density,\n                                const double outer_pressure,\n                                const Options::Context& context)\n    : initial_radius_(initial_radius),\n      inner_mass_density_(inner_mass_density),\n      inner_pressure_(inner_pressure),\n      outer_mass_density_(outer_mass_density),\n      outer_pressure_(outer_pressure),\n      equation_of_state_(1.4) {\n  ASSERT(initial_radius_ > 0.0,\n         \"The initial radius must be positive but is \" << initial_radius_);\n  ASSERT(\n      inner_mass_density_ > 0.0,\n      \"The inner mass density must be positive but is \" << inner_mass_density_);\n  ASSERT(inner_pressure_ > 0.0,\n         \"The inner pressure must be positive but is \" << inner_pressure_);\n  ASSERT(\n      outer_mass_density_ > 0.0,\n      \"The outer mass density must be positive but is \" << outer_mass_density_);\n  ASSERT(outer_pressure_ > 0.0,\n         \"The outer pressure must be positive but is \" << outer_pressure_);\n  if (outer_mass_density_ > inner_mass_density_) {\n    PARSE_ERROR(context, \"The inner mass density (\"\n                             << inner_mass_density_\n                             << \") must be larger than the outer mass density (\"\n                             << outer_mass_density_\n                             << \") so the shock moves radially outward.\");\n  }\n  if (outer_pressure_ > inner_pressure_) {\n    PARSE_ERROR(context, \"The inner pressure (\"\n                             << inner_pressure_\n                             << \") must be larger than the outer pressure (\"\n                             << outer_pressure_\n                             << \") so the shock moves radially outward.\");\n  }\n}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::initial_data::InitialData>\nSodExplosion<Dim>::get_clone() const {\n  return std::make_unique<SodExplosion>(*this);\n}\n\ntemplate <size_t Dim>\nSodExplosion<Dim>::SodExplosion(CkMigrateMessage* msg)\n    : evolution::initial_data::InitialData(msg) {}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID SodExplosion<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nvoid SodExplosion<Dim>::pup(PUP::er& p) {\n  evolution::initial_data::InitialData::pup(p);\n  p | initial_radius_;\n  p | inner_mass_density_;\n  p | inner_pressure_;\n  p | outer_mass_density_;\n  p | outer_pressure_;\n  p | equation_of_state_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const SodExplosion<Dim>& lhs, const SodExplosion<Dim>& rhs) {\n  // No comparison for equation_of_state_. The adiabatic index is hard-coded.\n  return lhs.initial_radius_ == rhs.initial_radius_ and\n         lhs.inner_mass_density_ == rhs.inner_mass_density_ and\n         lhs.inner_pressure_ == rhs.inner_pressure_ and\n         lhs.outer_mass_density_ == rhs.outer_mass_density_ and\n         lhs.outer_pressure_ == rhs.outer_pressure_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const SodExplosion<Dim>& lhs, const SodExplosion<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataVector>>\nSodExplosion<Dim>::variables(\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataVector>> /*meta*/) const {\n  auto mass_density = make_with_value<Scalar<DataVector>>(x, 0.0);\n  std::array<double, Dim> coords{};\n  for (size_t s = 0; s < get_size(get<0>(x)); ++s) {\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(coords, d) = get_element(x.get(d), s);\n    }\n    const double radius = magnitude(coords);\n    get_element(get(mass_density), s) =\n        radius > initial_radius_ ? outer_mass_density_ : inner_mass_density_;\n  }\n  return mass_density;\n}\n\ntemplate <size_t Dim>\ntuples::TaggedTuple<\n    hydro::Tags::SpatialVelocity<DataVector, Dim, Frame::Inertial>>\nSodExplosion<Dim>::variables(\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataVector, Dim,\n                                            Frame::Inertial>> /*meta*/) const {\n  auto velocity = make_with_value<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n      get<0>(x), 0.0);\n  return velocity;\n}\n\ntemplate <size_t Dim>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataVector>>\nSodExplosion<Dim>::variables(\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::Pressure<DataVector>> /*meta*/) const {\n  auto pressure = make_with_value<Scalar<DataVector>>(x, 0.0);\n  std::array<double, Dim> coords{};\n  for (size_t s = 0; s < get_size(get<0>(x)); ++s) {\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(coords, d) = get_element(x.get(d), s);\n    }\n    const double radius = magnitude(coords);\n    get_element(get(pressure), s) =\n        radius > initial_radius_ ? outer_pressure_ : inner_pressure_;\n  }\n  return pressure;\n}\n\ntemplate <size_t Dim>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataVector>>\nSodExplosion<Dim>::variables(\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataVector>> /*meta*/)\n    const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataVector>>(\n          variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataVector>>{})),\n      get<hydro::Tags::Pressure<DataVector>>(\n          variables(x, tmpl::list<hydro::Tags::Pressure<DataVector>>{})));\n}\n\ntemplate class SodExplosion<2>;\ntemplate class SodExplosion<3>;\ntemplate bool operator==(const SodExplosion<2>& lhs,\n                         const SodExplosion<2>& rhs);\ntemplate bool operator==(const SodExplosion<3>& lhs,\n                         const SodExplosion<3>& rhs);\ntemplate bool operator!=(const SodExplosion<2>& lhs,\n                         const SodExplosion<2>& rhs);\ntemplate bool operator!=(const SodExplosion<3>& lhs,\n                         const SodExplosion<3>& rhs);\n}  // namespace NewtonianEuler::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/NewtonianEuler/SodExplosion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::AnalyticData {\n/*!\n * \\brief A cylindrical or spherical Sod explosion \\cite Toro2009 \\cite Sod19781\n *\n * Common initial conditions are:\n *\n * \\f{align*}{\n * (\\rho, v^i, p) =\n * &\\left\\{\n * \\begin{array}{ll}\n *   (1 ,0, 1) & \\mathrm{if} \\; r \\le 0.5 \\\\\n *   (0.125 ,0, 0.1) & \\mathrm{if} \\; r > 0.5\n * \\end{array}\\right.\n * \\f}\n *\n * where \\f$r\\f$ is the cylindrical (2d) or spherical (3d) radius. This test\n * problem uses an adiabatic index of 1.4. A reference solution can be computed\n * in 1d by solving the Newtonian Euler equations in cylindrical or spherical\n * symmetry. Note that the inner and outer density and pressure, as well as\n * where the initial discontinuity is can be chosen arbitrarily.\n */\ntemplate <size_t Dim>\nclass SodExplosion : public evolution::initial_data::InitialData,\n                     public MarkAsAnalyticData {\n public:\n  static_assert(Dim > 1, \"Sod explosion is a 2d and 3d problem.\");\n  using equation_of_state_type = EquationsOfState::IdealFluid<false>;\n\n  /// Initial radius of the discontinuity\n  struct InitialRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial radius of the discontinuity.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct InnerMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\"The inner mass density.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct InnerPressure {\n    using type = double;\n    static constexpr Options::String help = {\"The inner pressure.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct OuterMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\"The outer mass density.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct OuterPressure {\n    using type = double;\n    static constexpr Options::String help = {\"The outer pressure.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options = tmpl::list<InitialRadius, InnerMassDensity, InnerPressure,\n                             OuterMassDensity, OuterPressure>;\n\n  static constexpr Options::String help = {\n      \"Cylindrical or spherical Sod explosion.\"};\n\n  SodExplosion() = default;\n  SodExplosion(const SodExplosion& /*rhs*/) = default;\n  SodExplosion& operator=(const SodExplosion& /*rhs*/) = default;\n  SodExplosion(SodExplosion&& /*rhs*/) = default;\n  SodExplosion& operator=(SodExplosion&& /*rhs*/) = default;\n  ~SodExplosion() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit SodExplosion(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SodExplosion);\n  /// \\endcond\n\n  SodExplosion(double initial_radius, double inner_mass_density,\n               double inner_pressure, double outer_mass_density,\n               double outer_pressure, const Options::Context& context = {});\n\n  /// Retrieve a collection of hydrodynamic variables at position x\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n      tmpl::list<Tags...> /*meta*/) const {\n    return {tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  const equation_of_state_type& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataVector>> variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>> /*meta*/) const;\n\n  tuples::TaggedTuple<\n      hydro::Tags::SpatialVelocity<DataVector, Dim, Frame::Inertial>>\n  variables(const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n            tmpl::list<hydro::Tags::SpatialVelocity<\n                DataVector, Dim, Frame::Inertial>> /*meta*/) const;\n\n  tuples::TaggedTuple<hydro::Tags::Pressure<DataVector>> variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n      tmpl::list<hydro::Tags::Pressure<DataVector>> /*meta*/) const;\n\n  tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataVector>>\n  variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataVector>> /*meta*/)\n      const;\n\n  template <size_t SpatialDim>\n  friend bool\n  operator==(  // NOLINT (clang-tidy: readability-redundant-declaration)\n      const SodExplosion<SpatialDim>& lhs, const SodExplosion<SpatialDim>& rhs);\n\n  double initial_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double inner_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double inner_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double outer_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double outer_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  equation_of_state_type equation_of_state_{};\n};\n\ntemplate <size_t Dim>\nbool operator!=(const SodExplosion<Dim>& lhs, const SodExplosion<Dim>& rhs);\n}  // namespace NewtonianEuler::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY PuncturesAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  MultiplePunctures.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  MultiplePunctures.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Punctures/MultiplePunctures.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/Punctures/MultiplePunctures.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Punctures/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Punctures::AnalyticData {\n\nPuncture::Puncture() = default;\n\nPuncture::Puncture(std::array<double, 3> position_in, double mass_in,\n                   std::array<double, 3> dimensionless_momentum_in,\n                   std::array<double, 3> dimensionless_spin_in)\n    : position(std::move(position_in)),\n      mass(std::move(mass_in)),\n      dimensionless_momentum(dimensionless_momentum_in),\n      dimensionless_spin(dimensionless_spin_in) {}\n\nvoid Puncture::pup(PUP::er& p) {\n  p | position;\n  p | mass;\n  p | dimensionless_momentum;\n  p | dimensionless_spin;\n}\n\nbool operator==(const Puncture& lhs, const Puncture& rhs) {\n  return lhs.position == rhs.position and lhs.mass == rhs.mass and\n         lhs.dimensionless_momentum == rhs.dimensionless_momentum and\n         lhs.dimensionless_spin == rhs.dimensionless_spin;\n}\n\nbool operator!=(const Puncture& lhs, const Puncture& rhs) {\n  return not(lhs == rhs);\n}\n\nnamespace detail {\n\nvoid MultiplePuncturesVariables::operator()(\n    const gsl::not_null<Scalar<DataVector>*> one_over_alpha,\n    const gsl::not_null<Cache*> /*cache*/,\n    detail::Tags::OneOverAlpha /*meta*/) const {\n  const size_t num_points = x.begin()->size();\n  tnsr::I<DataVector, 3> x_centered{num_points};\n  DataVector r{num_points};\n  get(*one_over_alpha) = 0.;\n  for (const auto& puncture : punctures) {\n    x_centered = x;\n    get<0>(x_centered) -= puncture.position[0];\n    get<1>(x_centered) -= puncture.position[1];\n    get<2>(x_centered) -= puncture.position[2];\n    r = get(magnitude(x_centered));\n    // Emit an error when a grid point coincides with a puncture. This is an\n    // error and not an assert because it's a user error and should probably be\n    // fixed in the input file.\n    for (size_t i = 0; i < r.size(); ++i) {\n      if (equal_within_roundoff(r[i], 0.)) {\n        ERROR(\n            \"A grid point coincides with a puncture. Please reposition the \"\n            \"grid or the puncture. Position: \"\n            << puncture.position);\n      }\n    }\n    get(*one_over_alpha) += 0.5 * puncture.mass / r;\n  }\n}\n\nvoid MultiplePuncturesVariables::operator()(\n    const gsl::not_null<Scalar<DataVector>*> alpha,\n    const gsl::not_null<Cache*> cache, Punctures::Tags::Alpha /*meta*/) const {\n  const auto& one_over_alpha =\n      cache->get_var(*this, detail::Tags::OneOverAlpha{});\n  get(*alpha) = 1. / get(one_over_alpha);\n}\n\nvoid MultiplePuncturesVariables::operator()(\n    const gsl::not_null<tnsr::II<DataVector, 3>*>\n        traceless_conformal_extrinsic_curvature,\n    const gsl::not_null<Cache*> /*cache*/,\n    Punctures::Tags::TracelessConformalExtrinsicCurvature /*meta*/) const {\n  const size_t num_points = x.begin()->size();\n  tnsr::I<DataVector, 3> x_centered{num_points};\n  DataVector r{num_points};\n  tnsr::I<DataVector, 3> n{num_points};\n  DataVector n_dot_P{num_points};\n  tnsr::I<DataVector, 3> S_cross_n{num_points};\n  std::fill(traceless_conformal_extrinsic_curvature->begin(),\n            traceless_conformal_extrinsic_curvature->end(), 0.);\n  for (const auto& puncture : punctures) {\n    x_centered = x;\n    get<0>(x_centered) -= puncture.position[0];\n    get<1>(x_centered) -= puncture.position[1];\n    get<2>(x_centered) -= puncture.position[2];\n    r = get(magnitude(x_centered));\n    n = x_centered;\n    get<0>(n) /= r;\n    get<1>(n) /= r;\n    get<2>(n) /= r;\n    n_dot_P = 0.;\n    for (size_t d = 0; d < 3; ++d) {\n      n_dot_P += n.get(d) * gsl::at(puncture.dimensionless_momentum, d);\n    }\n    std::fill(S_cross_n.begin(), S_cross_n.end(), 0.);\n    for (LeviCivitaIterator<3> levi_civita_it; levi_civita_it;\n         ++levi_civita_it) {\n      const auto [i, j, k] = levi_civita_it();\n      S_cross_n.get(i) += levi_civita_it.sign() *\n                          gsl::at(puncture.dimensionless_spin, j) * n.get(k);\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j <= i; ++j) {\n        // Momentum\n        traceless_conformal_extrinsic_curvature->get(i, j) +=\n            1.5 / square(r) * puncture.mass *\n            (gsl::at(puncture.dimensionless_momentum, i) * n.get(j) +\n             gsl::at(puncture.dimensionless_momentum, j) * n.get(i) +\n             n.get(i) * n.get(j) * n_dot_P);\n        // Spin\n        traceless_conformal_extrinsic_curvature->get(i, j) +=\n            3. / cube(r) * square(puncture.mass) *\n            (n.get(i) * S_cross_n.get(j) + n.get(j) * S_cross_n.get(i));\n      }\n      // Diagonal momentum\n      traceless_conformal_extrinsic_curvature->get(i, i) -=\n          1.5 / square(r) * puncture.mass * n_dot_P;\n    }\n  }\n}\n\nvoid MultiplePuncturesVariables::operator()(\n    const gsl::not_null<Scalar<DataVector>*> beta,\n    const gsl::not_null<Cache*> cache, Punctures::Tags::Beta /*meta*/) const {\n  get(*beta) = 0.;\n  const auto& A = cache->get_var(\n      *this, Punctures::Tags::TracelessConformalExtrinsicCurvature{});\n  for (size_t i = 0; i < A.size(); ++i) {\n    get(*beta) += A.multiplicity(i) * square(A[i]);\n  }\n  const auto& alpha = cache->get_var(*this, Punctures::Tags::Alpha{});\n  get(*beta) *= 0.125 * pow<7>(get(alpha));\n}\n\nvoid MultiplePuncturesVariables::operator()(\n    const gsl::not_null<Scalar<DataVector>*> fixed_source_for_field,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<Punctures::Tags::Field> /*meta*/) const {\n  // Initial guess\n  get(*fixed_source_for_field) = 0.;\n}\n\n}  // namespace detail\n\nPUP::able::PUP_ID MultiplePunctures::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace Punctures::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Punctures/MultiplePunctures.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Punctures/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures::AnalyticData {\n\n/// A puncture representing a black hole\nstruct Puncture {\n  struct Position {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help{\"The position C of the puncture\"};\n  };\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help{\"The puncture mass (bare mass) M\"};\n    static double lower_bound() { return 0.; }\n  };\n  struct Momentum {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help{\n        \"The dimensionless linear momentum P / M, where M is the bare mass of \"\n        \"the puncture.\"};\n  };\n  struct Spin {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help{\n        \"The dimensionless angular momentum S / M^2, where M is the bare mass \"\n        \"of the puncture.\"};\n  };\n  using options = tmpl::list<Position, Mass, Momentum, Spin>;\n  static constexpr Options::String help{\"A puncture representing a black hole\"};\n\n  Puncture();\n  Puncture(std::array<double, 3> position_in, double mass_in,\n           std::array<double, 3> dimensionless_momentum_in,\n           std::array<double, 3> dimensionless_spin_in);\n  void pup(PUP::er& p);\n\n  std::array<double, 3> position{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  double mass = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> dimensionless_momentum{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 3> dimensionless_spin{\n      {std::numeric_limits<double>::signaling_NaN()}};\n};\n\nbool operator==(const Puncture& lhs, const Puncture& rhs);\n\nbool operator!=(const Puncture& lhs, const Puncture& rhs);\n\nnamespace detail {\n\nnamespace Tags {\nstruct OneOverAlpha : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace Tags\n\nstruct MultiplePuncturesVariables {\n  static constexpr size_t Dim = 3;\n\n  using Cache =\n      CachedTempBuffer<detail::Tags::OneOverAlpha, Punctures::Tags::Alpha,\n                       Punctures::Tags::TracelessConformalExtrinsicCurvature,\n                       Punctures::Tags::Beta,\n                       ::Tags::FixedSource<Punctures::Tags::Field>>;\n\n  const tnsr::I<DataVector, 3>& x;\n  const std::vector<Puncture>& punctures;\n\n  void operator()(gsl::not_null<Scalar<DataVector>*> one_over_alpha,\n                  gsl::not_null<Cache*> cache,\n                  detail::Tags::OneOverAlpha /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataVector>*> alpha,\n                  gsl::not_null<Cache*> cache,\n                  Punctures::Tags::Alpha /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::II<DataVector, 3>*>\n          traceless_conformal_extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      Punctures::Tags::TracelessConformalExtrinsicCurvature /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataVector>*> beta,\n                  gsl::not_null<Cache*> cache,\n                  Punctures::Tags::Beta /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataVector>*> fixed_source_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::FixedSource<Punctures::Tags::Field> /*meta*/) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief Superposition of multiple punctures\n *\n * This class provides the source fields $\\alpha$ and $\\beta$ for the puncture\n * equation (see \\ref ::Punctures) representing any number of black holes. Each\n * black hole is characterized by its \"puncture mass\" (or \"bare mass\") $M_I$,\n * position $\\mathbf{C}_I$, linear momentum $\\mathbf{P}_I$, and angular momentum\n * $\\mathbf{S}_I$. The corresponding Bowen-York solution to the momentum\n * constraint for the conformal traceless extrinsic curvature is:\n *\n * \\begin{equation}\n * \\bar{A}^{ij} = \\frac{3}{2} \\sum_I \\frac{1}{r_I^2} \\left(\n * 2 P_I^{(i} n_I^{j)} - (\\delta^{ij} - n_I^i n_I^j) P_I^k n_I^k\n * + \\frac{4}{r_I} n_I^{(i} \\epsilon^{j)kl} S_I^k n_I^l\\right)\n * \\end{equation}\n *\n * From it, we compute $\\alpha$ and $\\beta$ as:\n *\n * \\begin{align}\n * \\frac{1}{\\alpha} &= \\sum_I \\frac{M_I}{2 r_I} \\\\\n * \\beta &= \\frac{1}{8} \\alpha^7 \\bar{A}_{ij} \\bar{A}^{ij}\n * \\end{align}\n *\n * \\see ::Punctures\n */\nclass MultiplePunctures : public elliptic::analytic_data::Background,\n                          public elliptic::analytic_data::InitialGuess {\n public:\n  struct Punctures {\n    static constexpr Options::String help =\n        \"Parameters for each puncture, representing black holes\";\n    using type = std::vector<Puncture>;\n  };\n  using options = tmpl::list<Punctures>;\n  static constexpr Options::String help = \"Any number of black holes\";\n\n  MultiplePunctures() = default;\n  MultiplePunctures(const MultiplePunctures&) = default;\n  MultiplePunctures& operator=(const MultiplePunctures&) = default;\n  MultiplePunctures(MultiplePunctures&&) = default;\n  MultiplePunctures& operator=(MultiplePunctures&&) = default;\n  ~MultiplePunctures() = default;\n\n  MultiplePunctures(std::vector<Puncture> punctures)\n      : punctures_(std::move(punctures)) {}\n\n  explicit MultiplePunctures(CkMigrateMessage* m)\n      : elliptic::analytic_data::Background(m),\n        elliptic::analytic_data::InitialGuess(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MultiplePunctures);\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    typename detail::MultiplePuncturesVariables::Cache cache{\n        get_size(*x.begin())};\n    const detail::MultiplePuncturesVariables computer{x, punctures_};\n    return {cache.get_var(computer, RequestedTags{})...};\n  }\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x, const Mesh<3>& /*mesh*/,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& /*inv_jacobian*/,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables(x, tmpl::list<RequestedTags...>{});\n  }\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::Background::pup(p);\n    elliptic::analytic_data::InitialGuess::pup(p);\n    p | punctures_;\n  }\n\n  const std::vector<Puncture>& punctures() const { return punctures_; }\n\n private:\n  std::vector<Puncture> punctures_{};\n};\n\n}  // namespace Punctures::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/RadiationTransport/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY RadiationTransportAnalyticData)\n\nadd_subdirectory(M1Grey)\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\ingroup AnalyticDataGroup\n * \\brief Holds classes implementing analytic data for the M1Grey system.\n */\nnamespace RadiationTransport::M1Grey::AnalyticData {}\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY M1GreyAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  HomogeneousSphere.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  Factory.hpp\n  HomogeneousSphere.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  Hydro\n  Options\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/HomogeneousSphere.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RadiationTransport::M1Grey::AnalyticData {\n/*!\n * \\brief Typelist of all analytic data of M1Grey evolution system\n */\n\nusing all_data = tmpl::list<HomogeneousSphere>;\n}  // namespace RadiationTransport::M1Grey::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/HomogeneousSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/HomogeneousSphere.hpp\"\n\n#include <boost/preprocessor/punctuation/comma_if.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace RadiationTransport::M1Grey::AnalyticData {\nHomogeneousSphere::HomogeneousSphere(const double radius,\n                                     const double emissivity_and_opacity,\n                                     const double outer_opacity,\n                                     const double boundary_roundness)\n    : radius_(radius),\n      emissivity_and_opacity_(emissivity_and_opacity),\n      outer_opacity_(outer_opacity),\n      boundary_roundness_(boundary_roundness) {}\n\nnamespace {\n\n// This function returns the radius squared of the 3-dimensional\n// position vector.\nDataVector radius_squared(const tnsr::I<DataVector, 3>& x) {\n  return square(get<0>(x)) + square(get<1>(x)) + square(get<2>(x));\n}\n}  // namespace\n\nnamespace {\n// This function is used to round the edges of the homogeneous sphere, as\n// opposed to a pure, rectangular step function.\nScalar<DataVector> rounded_step_function(const DataVector& x,\n                                         const double inner_value,\n                                         const double outer_value,\n                                         const double sphere_radius,\n                                         const double boundary_roundness) {\n  return Scalar<DataVector>{\n      (inner_value - outer_value) / M_PI *\n          atan((x - sphere_radius) / -boundary_roundness) +\n      0.5 * (inner_value + outer_value)};\n}\n}  // namespace\n\ntemplate <typename NeutrinoSpecies>\nauto HomogeneousSphere::variables(\n    const tnsr::I<DataVector, 3>& x,\n    tmpl::list<RadiationTransport::M1Grey::Tags::TildeE<\n        Frame::Inertial, NeutrinoSpecies>> /*meta*/) const\n    -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::TildeE<\n        Frame::Inertial, NeutrinoSpecies>> {\n  const DataVector r = sqrt(radius_squared(x));\n\n  const double inner_energy = 1.0;\n  const double outer_energy = 1.0e-12;\n\n  return rounded_step_function(r, inner_energy, outer_energy, radius_,\n                               boundary_roundness_);\n}\n\ntemplate <typename NeutrinoSpecies>\nauto HomogeneousSphere::variables(\n    const tnsr::I<DataVector, 3>& x,\n    tmpl::list<RadiationTransport::M1Grey::Tags::TildeS<\n        Frame::Inertial, NeutrinoSpecies>> /*meta*/) const\n    -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::TildeS<\n        Frame::Inertial, NeutrinoSpecies>> {\n  return {make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(x, 0.0)};\n}\n\ntemplate <typename NeutrinoSpecies>\nauto HomogeneousSphere::variables(\n    const tnsr::I<DataVector, 3>& x,\n    tmpl::list<RadiationTransport::M1Grey::Tags::GreyEmissivity<\n        NeutrinoSpecies>> /*meta*/) const\n    -> tuples::TaggedTuple<\n        RadiationTransport::M1Grey::Tags::GreyEmissivity<NeutrinoSpecies>> {\n  const DataVector r = sqrt(radius_squared(x));\n\n  const double inner_emissivity = emissivity_and_opacity_;\n  const double outer_emissivity = 0.0;\n\n  return rounded_step_function(r, inner_emissivity, outer_emissivity, radius_,\n                               boundary_roundness_);\n}\n\ntemplate <typename NeutrinoSpecies>\nauto HomogeneousSphere::variables(\n    const tnsr::I<DataVector, 3>& x,\n    tmpl::list<RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<\n        NeutrinoSpecies>> /*meta*/) const\n    -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::\n                               GreyAbsorptionOpacity<NeutrinoSpecies>> {\n  const DataVector r = sqrt(radius_squared(x));\n\n  return rounded_step_function(r, emissivity_and_opacity_, outer_opacity_,\n                               radius_, boundary_roundness_);\n}\n\ntemplate <typename NeutrinoSpecies>\nauto HomogeneousSphere::variables(\n    const tnsr::I<DataVector, 3>& x,\n    tmpl::list<RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<\n        NeutrinoSpecies>> /*meta*/) const\n    -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::\n                               GreyScatteringOpacity<NeutrinoSpecies>> {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\nauto HomogeneousSphere::variables(\n    const tnsr::I<DataVector, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataVector>> /*meta*/)\n    -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataVector>> {\n  return {make_with_value<Scalar<DataVector>>(x, 1.0)};\n}\n\nauto HomogeneousSphere::variables(\n    const tnsr::I<DataVector, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3>> /*meta*/)\n    -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataVector, 3>> {\n  return {make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(x, 0.0)};\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nHomogeneousSphere::get_clone() const {\n  return std::make_unique<HomogeneousSphere>(*this);\n}\n\nvoid HomogeneousSphere::pup(PUP::er& p) {\n  evolution::initial_data::InitialData::pup(p);\n  p | radius_;\n  p | emissivity_and_opacity_;\n  p | outer_opacity_;\n  p | boundary_roundness_;\n}\n// NOLINTNEXTLINE\nPUP::able::PUP_ID HomogeneousSphere::my_PUP_ID = 0;\n\nbool operator!=(const HomogeneousSphere& lhs, const HomogeneousSphere& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DERIVED_CLASSES (HomogeneousSphere)\n\n#define DERIVED(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define NTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define EBIN(data) BOOST_PP_TUPLE_ELEM(3, data)\n#define GENERATE_LIST(z, n, _) BOOST_PP_COMMA_IF(n) n\n\n#define INSTANTIATE_M1_FUNCTION_WITH_FRAME(_, data)                            \\\n  template tuples::TaggedTuple<TAG(data) < Frame::Inertial,                    \\\n                               NTYPE(data) < EBIN(data)> >>                    \\\n      DERIVED(data)::variables(                                                \\\n          const tnsr::I<DataVector, 3>& x,                                     \\\n          tmpl::list<TAG(data) < Frame::Inertial, NTYPE(data) < EBIN(data)> >> \\\n          /*meta*/) const;\n\n#define TEMP_LIST \\\n  (BOOST_PP_REPEAT(MAX_NUMBER_OF_NEUTRINO_ENERGY_BINS, GENERATE_LIST, _))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_M1_FUNCTION_WITH_FRAME, DERIVED_CLASSES,\n                        (RadiationTransport::M1Grey::Tags::TildeE,\n                         RadiationTransport::M1Grey::Tags::TildeS),\n                        (neutrinos::ElectronNeutrinos,\n                         neutrinos::ElectronAntiNeutrinos,\n                         neutrinos::HeavyLeptonNeutrinos),\n                        TEMP_LIST)\n\n#undef TEMP_LIST\n#undef INSTANTIATE_M1_FUNCTION_WITH_FRAME\n#undef DERIVED\n#undef TAG\n#undef NTYPE\n#undef EBIN\n#undef GENERATE_LIST\n\n#define DERIVED(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define NTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define EBIN(data) BOOST_PP_TUPLE_ELEM(3, data)\n#define GENERATE_LIST(z, n, _) BOOST_PP_COMMA_IF(n) n\n\n#define INSTANTIATE_M1_FUNCTION(_, data)                                \\\n  template tuples::TaggedTuple<TAG(data) < NTYPE(data) < EBIN(data)> >> \\\n      DERIVED(data)::variables(                                         \\\n          const tnsr::I<DataVector, 3>& x,                              \\\n          tmpl::list<TAG(data) < NTYPE(data) < EBIN(data)> >>           \\\n          /*meta*/) const;\n\n#define TEMP_LIST \\\n  (BOOST_PP_REPEAT(MAX_NUMBER_OF_NEUTRINO_ENERGY_BINS, GENERATE_LIST, _))\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_M1_FUNCTION, DERIVED_CLASSES,\n    (RadiationTransport::M1Grey::Tags::GreyEmissivity,\n     RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity,\n     RadiationTransport::M1Grey::Tags::GreyScatteringOpacity),\n    (neutrinos::ElectronNeutrinos, neutrinos::ElectronAntiNeutrinos,\n     neutrinos::HeavyLeptonNeutrinos),\n    TEMP_LIST)\n\n#undef INSTANTIATE_M1_FUNCTION\n#undef TEMP_LIST\n#undef DERIVED\n#undef TAG\n#undef NTYPE\n#undef EBIN\n#undef GENERATE_LIST\n\n}  // namespace RadiationTransport::M1Grey::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/HomogeneousSphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace RadiationTransport::M1Grey::AnalyticData {\n/*!\n * \\brief Construct a homogeneous sphere of neutrino radiation.\n *\n * We follow the homogeneous sphere test problem in Section 4.5 of\n * \\cite radice2022.  The initial data has radius = 1, with equal emissivity\n * and absorption \\f$\\eta = \\kappa_a = 10\\f$ inside the uniform sphere.\n * Outside of the sphere the absorption is much lower, allowing the neutrinos\n * to stream out.  Initially the neutrino energy density is distributed\n * uniformly inside the sphere. The momentum density is initialized to 0.\n *\n * Note:\n * To avoid sharp discontinuities, we round the edges of the energy profile\n * with an arctangent function, instead of the step function, which has\n * sharper features.\n */\nclass HomogeneousSphere : public virtual evolution::initial_data::InitialData,\n                          public MarkAsAnalyticData {\n public:\n  static constexpr Options::String help = {\n      \"A homogeneous sphere emitting and absorbing neutrinos.\"};\n\n  /// The sphere radius.\n  struct Radius {\n    using type = double;\n    static constexpr Options::String help = \"Sphere radius\";\n  };\n\n  /// The emissivity and absorption opacity.\n  struct EmissivityAndOpacity {\n    using type = double;\n    static constexpr Options::String help = \"Emissivity and absorption opacity\";\n  };\n\n  /// The absorption opacity of the exterior\n  struct OuterOpacity {\n    using type = double;\n    static constexpr Options::String help =\n        \"Opacity of outer absorption region\";\n  };\n\n  /// BoundaryRoundness\n  struct BoundaryRoundness {\n    using type = double;\n    static constexpr Options::String help =\n        \"How rounded the interface is between the sphere radius and outer \"\n        \"region.  Closer to 0 corresponds to sharper (more step like) \"\n        \"interface.\";\n    static type lower_bound() { return 1e-5; }\n  };\n\n  using options =\n      tmpl::list<Radius, EmissivityAndOpacity, OuterOpacity, BoundaryRoundness>;\n\n  HomogeneousSphere() = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit HomogeneousSphere(CkMigrateMessage* /*message*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(HomogeneousSphere);\n  /// \\endcond\n\n  HomogeneousSphere(double radius, double emissivity_and_opacity,\n                    double outer_opacity, double boundary_roundness);\n\n  /// @{\n  /// Retrieve fluid and neutrino variables\n  template <typename NeutrinoSpecies>\n  auto variables(const tnsr::I<DataVector, 3>& x,\n                 tmpl::list<RadiationTransport::M1Grey::Tags::TildeE<\n                     Frame::Inertial, NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::TildeE<\n          Frame::Inertial, NeutrinoSpecies>>;\n\n  template <typename NeutrinoSpecies>\n  auto variables(const tnsr::I<DataVector, 3>& x,\n                 tmpl::list<RadiationTransport::M1Grey::Tags::TildeS<\n                     Frame::Inertial, NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::TildeS<\n          Frame::Inertial, NeutrinoSpecies>>;\n\n  template <typename NeutrinoSpecies>\n  auto variables(const tnsr::I<DataVector, 3>& x,\n                 tmpl::list<RadiationTransport::M1Grey::Tags::GreyEmissivity<\n                     NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<\n          RadiationTransport::M1Grey::Tags::GreyEmissivity<NeutrinoSpecies>>;\n\n  template <typename NeutrinoSpecies>\n  auto variables(\n      const tnsr::I<DataVector, 3>& x,\n      tmpl::list<RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<\n          NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::\n                                 GreyAbsorptionOpacity<NeutrinoSpecies>>;\n\n  template <typename NeutrinoSpecies>\n  auto variables(\n      const tnsr::I<DataVector, 3>& x,\n      tmpl::list<RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<\n          NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::\n                                 GreyScatteringOpacity<NeutrinoSpecies>>;\n\n  static auto variables(\n      const tnsr::I<DataVector, 3>& x,\n      tmpl::list<hydro::Tags::LorentzFactor<DataVector>> /*meta*/)\n      -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataVector>>;\n\n  static auto variables(\n      const tnsr::I<DataVector, 3>& x,\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3>> /*meta*/)\n      -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataVector, 3>>;\n  /// @}\n\n  /// Retrieve the metric variables\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, 3>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    return gr::Solutions::Minkowski<3>{}.variables(x, 0.0, tmpl::list<Tag>{});\n  }\n\n  /// Retrieve a collection of variables\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    return {get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const HomogeneousSphere& lhs,\n                         const HomogeneousSphere& rhs) {\n    return lhs.radius_ == rhs.radius_ and\n           lhs.emissivity_and_opacity_ == rhs.emissivity_and_opacity_ and\n           lhs.outer_opacity_ == rhs.outer_opacity_ and\n           lhs.boundary_roundness_ == rhs.boundary_roundness_;\n  }\n\n  double radius_ = std::numeric_limits<double>::signaling_NaN();\n  double emissivity_and_opacity_ = std::numeric_limits<double>::signaling_NaN();\n  double outer_opacity_ = std::numeric_limits<double>::signaling_NaN();\n  double boundary_roundness_ = std::numeric_limits<double>::signaling_NaN();\n};\n\nbool operator!=(const HomogeneousSphere& lhs, const HomogeneousSphere& rhs);\n\n}  // namespace RadiationTransport::M1Grey::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ScalarTensor/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GeneralRelativity/AnalyticData.hpp\"\n\nnamespace ScalarTensor {\n/// Base struct for properties common to all Scalar Tensor analytic initial data\nstruct AnalyticDataBase {\n  static constexpr size_t volume_dim = 3_st;\n\n  template <typename DataType>\n  using tags = tmpl::push_back<\n      typename gr::AnalyticDataBase<volume_dim>::template tags<DataType>,\n      CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n      CurvedScalarWave::Tags::Phi<volume_dim>>;\n};\n\n/*!\n * \\ingroup AnalyticDataGroup\n * \\brief Holds classes implementing analytic data for the ScalarTensor\n * system.\n */\nnamespace AnalyticData {}\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ScalarTensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarTensorAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  KerrSphericalHarmonic.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  KerrSphericalHarmonic.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  CurvedScalarWave\n  InitialDataUtilities\n  Options\n  Serialization\n  SpinWeightedSphericalHarmonics\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.hpp\"\n\n#include <array>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ScalarTensor::AnalyticData {\n\nKerrSphericalHarmonic::KerrSphericalHarmonic(\n    const double mass, const std::array<double, 3>& dimensionless_spin,\n    const double amplitude, const double radius, const double width,\n    const std::pair<size_t, int> mode) {\n  mass_ = mass;\n  dimensionless_spin_ = dimensionless_spin;\n  amplitude_ = amplitude;\n  radius_ = radius;\n  width_sq_ = square(width);\n  mode_ = mode;\n  background_spacetime_ =\n      gr::Solutions::KerrSchild{mass_, dimensionless_spin_,\n                                // Center\n                                std::array<double, 3>{{0.0, 0.0, 0.0}}};\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nKerrSphericalHarmonic::get_clone() const {\n  return std::make_unique<KerrSphericalHarmonic>(*this);\n}\n\nKerrSphericalHarmonic::KerrSphericalHarmonic(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nvoid KerrSphericalHarmonic::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | mass_;\n  p | dimensionless_spin_;\n  p | amplitude_;\n  p | radius_;\n  p | width_sq_;\n  p | mode_;\n  p | background_spacetime_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<CurvedScalarWave::Tags::Psi>\nKerrSphericalHarmonic::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<CurvedScalarWave::Tags::Psi> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<CurvedScalarWave::Tags::Phi<3_st>>\nKerrSphericalHarmonic::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<CurvedScalarWave::Tags::Phi<3_st>> /*meta*/) const {\n  return {make_with_value<tnsr::i<DataType, 3>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<CurvedScalarWave::Tags::Pi>\nKerrSphericalHarmonic::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<CurvedScalarWave::Tags::Pi> /*meta*/) const {\n  Scalar<DataType> pi =\n      make_with_value<Scalar<DataType>>(x, 0.0);\n  get(pi) += get(magnitude(x)) - radius_;\n  get(pi) = exp(-square(get(pi)) / width_sq_);\n  const Spectral::Swsh::SpinWeightedSphericalHarmonic spherical_harmonic(\n      0, mode_.first, mode_.second);\n  const auto theta = atan2(hypot(x[0], x[1]), x[2]);\n  const auto phi = atan2(x[1], x[0]);\n  get(pi) *= real(spherical_harmonic.evaluate(theta, phi, sin(0.5 * theta),\n                                              cos(0.5 * theta)));\n  get(pi) *= amplitude_;\n  return pi;\n}\n\nPUP::able::PUP_ID KerrSphericalHarmonic::my_PUP_ID = 0;\n\nbool operator==(const KerrSphericalHarmonic& lhs,\n                const KerrSphericalHarmonic& rhs) {\n  return lhs.amplitude_ == rhs.amplitude_ and\n         lhs.dimensionless_spin_ == rhs.dimensionless_spin_ and\n         lhs.radius_ == rhs.radius_ and lhs.width_sq_ == rhs.width_sq_ and\n         lhs.mode_ == rhs.mode_ and lhs.mass_ == rhs.mass_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const KerrSphericalHarmonic& lhs,\n                const KerrSphericalHarmonic& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                        \\\n  template tuples::TaggedTuple<TAG(data)> KerrSphericalHarmonic::variables( \\\n      const tnsr::I<DTYPE(data), 3>& x, tmpl::list<TAG(data)> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (DataVector),\n                        (CurvedScalarWave::Tags::Psi,\n                         CurvedScalarWave::Tags::Pi))\n\n#define INSTANTIATE_VECTORS(_, data)                                        \\\n  template tuples::TaggedTuple<TAG(data)> KerrSphericalHarmonic::variables( \\\n      const tnsr::I<DTYPE(data), 3>& x, tmpl::list<TAG(data)> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (DataVector),\n                        (CurvedScalarWave::Tags::Phi<3_st>))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace ScalarTensor::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ScalarTensor/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarTensor::AnalyticData {\n/*!\n * \\brief Analytic initial data for a pure spherical harmonic in three\n * dimensions in a KerrSchild background.\n *\n * \\details The initial data is taken from \\cite Scheel2003vs , Eqs. 4.1--4.3,\n * and sets the evolved variables of the scalar wave as follows:\n *\n * \\f{align}\n * \\Psi &= 0 \\\\\n * \\Phi_i &= 0 \\\\\n * \\Pi &= \\Pi_0(r, \\theta, \\phi) =  A e^{- (r - r_0)^2 / w^2} Y_{lm}(\\theta,\n * \\phi), \\f}\n *\n * where \\f$A\\f$ is the amplitude of the profile, \\f$r_0\\f$ is its radius and\n * \\f$w\\f$ is its width. This describes a pure spherical harmonic mode\n * \\f$Y_{lm}(\\theta, \\phi)\\f$ truncated by a circular Gaussian window function.\n *\n * When evolved, the scalar field \\f$\\Phi\\f$ will briefly build up around the\n * radius \\f$r_0\\f$ and then disperse. This can be used to study the ringdown\n * behavior and late-time tails in the Kerr spacetime.\n * \\see CurvedScalarWave::AnalyticData::PureSphericalHarmonic and\n * gr::Solutions::KerrSchild.\n */\nclass KerrSphericalHarmonic\n    : public virtual evolution::initial_data::InitialData,\n      public MarkAsAnalyticData,\n      public AnalyticDataBase {\n public:\n  /// The mass of the black hole.\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\"Mass of the black hole.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// The spin of the black hole\n  struct Spin {\n    using type = std::array<double, volume_dim>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] dimensionless spin of the black hole\"};\n  };\n  /// The amplitude of the scalar field\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Amplitude of the constant scalar field\"};\n  };\n  /// The location of the scalar field\n  struct Radius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The radius of the spherical harmonic profile\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// The width of the scalar field\n  struct Width {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The width of the spherical harmonic profile. The width must be \"\n        \"greater than 0.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// The spherical harmonic mode of the scalar field\n  struct Mode {\n    using type = std::pair<size_t, int>;\n    static constexpr Options::String help = {\n        \"The l-mode and m-mode of the spherical harmonic Ylm. The absolute \"\n        \"value of the m_mode must be less than or equal to the \"\n        \"l-mode.\"};\n  };\n\n  using options = tmpl::list<Mass, Spin, Amplitude, Radius, Width, Mode>;\n  static constexpr Options::String help = {\n      \"Initial data for a pure spherical harmonic mode truncated by a circular \"\n      \"Gaussian window funtion. The expression is taken from Scheel(2003), \"\n      \"equations 4.1-4.3.\"};\n\n  KerrSphericalHarmonic() = default;\n  KerrSphericalHarmonic(const KerrSphericalHarmonic& /*rhs*/) = default;\n  KerrSphericalHarmonic& operator=(const KerrSphericalHarmonic& /*rhs*/) =\n      default;\n  KerrSphericalHarmonic(KerrSphericalHarmonic&& /*rhs*/) = default;\n  KerrSphericalHarmonic& operator=(KerrSphericalHarmonic&& /*rhs*/) = default;\n  ~KerrSphericalHarmonic() override = default;\n\n  KerrSphericalHarmonic(double mass,\n                        const std::array<double, 3>& dimensionless_spin,\n                        double amplitude, double radius, double width,\n                        std::pair<size_t, int> mode);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit KerrSphericalHarmonic(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(KerrSphericalHarmonic);\n  /// \\endcond\n\n  // The extra tags below can also be added as common to all scalar tensor\n  // solutions\n  template <typename DataType, typename Frame = Frame::Inertial>\n  using tags = tmpl::flatten<tmpl::list<\n      typename AnalyticDataBase::template tags<DataType>,\n      gr::Tags::DerivDetSpatialMetric<DataType, 3, Frame>,\n      gr::Tags::TraceExtrinsicCurvature<DataType>,\n      gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>,\n      gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>,\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3, Frame>>>;\n\n  /// @{\n  /// Retrieve scalar variable at `x`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<CurvedScalarWave::Tags::Psi> /*meta*/) const\n      -> tuples::TaggedTuple<CurvedScalarWave::Tags::Psi>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<CurvedScalarWave::Tags::Phi<3_st>> /*meta*/) const\n      -> tuples::TaggedTuple<CurvedScalarWave::Tags::Phi<3_st>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<CurvedScalarWave::Tags::Pi> /*meta*/) const\n      -> tuples::TaggedTuple<CurvedScalarWave::Tags::Pi>;\n  /// @}\n\n  /// Retrieve a collection of scalar variables at `x`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag>\n  tuples::TaggedTuple<Tag> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<Tag> /*meta*/) const {\n    // We need to provide a time argument for the background solution\n    return background_spacetime_.variables(x, 0.0, tmpl::list<Tag>{});\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n protected:\n  friend bool operator==(const KerrSphericalHarmonic& lhs,\n                         const KerrSphericalHarmonic& rhs);\n\n  double mass_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, volume_dim> dimensionless_spin_ =\n      make_array<volume_dim>(std::numeric_limits<double>::signaling_NaN());\n  double amplitude_ = std::numeric_limits<double>::signaling_NaN();\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  double width_sq_{std::numeric_limits<double>::signaling_NaN()};\n  std::pair<size_t, int> mode_{std::numeric_limits<size_t>::signaling_NaN(),\n                               std::numeric_limits<int>::signaling_NaN()};\n\n  gr::Solutions::KerrSchild background_spacetime_{};\n};\n\nbool operator!=(const KerrSphericalHarmonic& lhs,\n                const KerrSphericalHarmonic& rhs);\n\n}  // namespace ScalarTensor::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionGroupsGroup\n/// Holds the `OptionTags::AnalyticData` option in the input file\nstruct AnalyticDataGroup {\n  static std::string name() { return \"AnalyticData\"; }\n  static constexpr Options::String help =\n      \"Analytic data used for the initial data\";\n};\n\n/// \\ingroup OptionTagsGroup\n/// The analytic data, with the type of the analytic data set as the template\n/// parameter\ntemplate <typename DataType>\nstruct AnalyticData {\n  static std::string name() { return pretty_type::name<DataType>(); }\n  static constexpr Options::String help = \"Options for the analytic data\";\n  using type = DataType;\n  using group = AnalyticDataGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// The analytic data, with the type of the analytic data set as the\n/// template parameter\ntemplate <typename DataType>\nstruct AnalyticData : db::SimpleTag {\n  using type = DataType;\n  using option_tags = tmpl::list<::OptionTags::AnalyticData<DataType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static DataType create_from_options(const DataType& analytic_solution) {\n    return serialize_and_deserialize<type>(analytic_solution);\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Xcts/AnalyticData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// Analytic data for the \\ref Xcts \"XCTS\" equations, i.e. field configurations\n/// that do not solve the equations but are used as background or initial guess.\nnamespace Xcts::AnalyticData {}\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Xcts/Binary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/Xcts/Binary.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/CommonVariables.tpp\"\n#include \"PointwiseFunctions/Elasticity/Strain.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Xcts::AnalyticData::detail {\n\ntemplate <typename DataType>\nvoid BinaryVariables<DataType>::add_deriv_of_window_function(\n    const gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_conformal_metric)\n    const {\n  if (not falloff_widths.has_value()) {\n    return;\n  }\n  const auto& flat_metric =\n      get<Tags::ConformalMetric<DataType, 3, Frame::Inertial>>(flat_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k <= j; ++k) {\n        for (size_t object_index = 0; object_index < 2; ++object_index) {\n          deriv_conformal_metric->get(i, j, k) -=\n              2. * gsl::at(x_isolated, object_index).get(i) /\n              square(gsl::at(*falloff_widths, object_index)) *\n              gsl::at(windows, object_index) *\n              (get<Tags::ConformalMetric<DataType, 3, Frame::Inertial>>(\n                   gsl::at(isolated_vars, object_index))\n                   .get(j, k) -\n               flat_metric.get(j, k));\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid BinaryVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> shift_background,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::ShiftBackground<DataType, Dim, Frame::Inertial> /*meta*/) const {\n  get<0>(*shift_background) = -angular_velocity * get<1>(x) +\n                              expansion * get<0>(x) + linear_velocity[0];\n  get<1>(*shift_background) =\n      angular_velocity * get<0>(x) + expansion * get<1>(x) + linear_velocity[1];\n  get<2>(*shift_background) = expansion * get<2>(x) + linear_velocity[2];\n}\n\ntemplate <typename DataType>\nvoid BinaryVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, Dim>*> deriv_shift_background,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::ShiftBackground<DataType, Dim, Frame::Inertial>,\n                  tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const {\n  std::fill(deriv_shift_background->begin(), deriv_shift_background->end(), 0.);\n  get<1, 0>(*deriv_shift_background) = -angular_velocity;\n  get<0, 1>(*deriv_shift_background) = angular_velocity;\n  get<0, 0>(*deriv_shift_background) = expansion;\n  get<1, 1>(*deriv_shift_background) = expansion;\n  get<2, 2>(*deriv_shift_background) = expansion;\n}\n\ntemplate class BinaryVariables<DataVector>;\n\n}  // namespace Xcts::AnalyticData::detail\n\ntemplate class Xcts::AnalyticData::CommonVariables<\n    DataVector,\n    typename Xcts::AnalyticData::detail::BinaryVariables<DataVector>::Cache>;\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Xcts/Binary.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <optional>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/CommonVariables.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Flatness.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Xcts::AnalyticData {\n\nnamespace detail {\n\ntemplate <typename DataType>\nusing BinaryVariablesCache = cached_temp_buffer_from_typelist<tmpl::append<\n    common_tags<DataType>,\n    tmpl::list<gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0>,\n               gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0>,\n               gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>, 0>,\n               // For initial guesses\n               Tags::ConformalFactorMinusOne<DataType>,\n               Tags::LapseTimesConformalFactorMinusOne<DataType>,\n               Tags::ShiftExcess<DataType, 3, Frame::Inertial>>,\n    hydro_tags<DataType>>>;\n\ntemplate <typename DataType>\nstruct BinaryVariables\n    : CommonVariables<DataType, BinaryVariablesCache<DataType>> {\n  static constexpr size_t Dim = 3;\n  using Cache = BinaryVariablesCache<DataType>;\n  using Base = CommonVariables<DataType, BinaryVariablesCache<DataType>>;\n  using Base::operator();\n\n  using superposed_tags = tmpl::append<\n      tmpl::list<\n          Tags::ConformalMetric<DataType, Dim, Frame::Inertial>,\n          ::Tags::deriv<Tags::ConformalMetric<DataType, Dim, Frame::Inertial>,\n                        tmpl::size_t<Dim>, Frame::Inertial>,\n          gr::Tags::TraceExtrinsicCurvature<DataType>,\n          ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>>,\n          gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0>,\n          gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0>,\n          gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, Dim>, 0>,\n          Tags::ConformalFactorMinusOne<DataType>,\n          Tags::LapseTimesConformalFactorMinusOne<DataType>,\n          Tags::ShiftExcess<DataType, Dim, Frame::Inertial>>,\n      hydro_tags<DataType>>;\n\n  BinaryVariables(\n      std::optional<std::reference_wrapper<const Mesh<Dim>>> local_mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, Dim, Frame::ElementLogical, Frame::Inertial>>>\n          local_inv_jacobian,\n      const tnsr::I<DataVector, Dim>& local_x,\n      const double local_angular_velocity, const double local_expansion,\n      const std::array<double, 3> local_linear_velocity,\n      std::optional<std::array<double, 2>> local_falloff_widths,\n      std::array<tnsr::I<DataVector, Dim>, 2> local_x_isolated,\n      std::array<DataVector, 2> local_windows,\n      tuples::tagged_tuple_from_typelist<superposed_tags> local_flat_vars,\n      std::array<tuples::tagged_tuple_from_typelist<superposed_tags>, 2>\n          local_isolated_vars)\n      : Base(std::move(local_mesh), std::move(local_inv_jacobian)),\n        x(local_x),\n        angular_velocity(local_angular_velocity),\n        expansion(local_expansion),\n        linear_velocity(local_linear_velocity),\n        falloff_widths(std::move(local_falloff_widths)),\n        x_isolated(std::move(local_x_isolated)),\n        windows(std::move(local_windows)),\n        flat_vars(std::move(local_flat_vars)),\n        isolated_vars(std::move(local_isolated_vars)) {}\n\n  const tnsr::I<DataVector, Dim>& x;\n  const double angular_velocity;\n  const double expansion;\n  const std::array<double, 3> linear_velocity;\n  const std::optional<std::array<double, 2>> falloff_widths;\n  const std::array<tnsr::I<DataVector, Dim>, 2> x_isolated;\n  const std::array<DataVector, 2> windows;\n  const tuples::tagged_tuple_from_typelist<superposed_tags> flat_vars;\n  const std::array<tuples::tagged_tuple_from_typelist<superposed_tags>, 2>\n      isolated_vars;\n\n  template <bool ApplyWindow = true, typename Tag,\n            Requires<tmpl::list_contains_v<superposed_tags, Tag>> = nullptr>\n  void superposition(gsl::not_null<typename Tag::type*> superposed_var,\n                     gsl::not_null<Cache*> /*cache*/, Tag /*meta*/) const {\n    for (size_t i = 0; i < superposed_var->size(); ++i) {\n      if constexpr (ApplyWindow) {\n        (*superposed_var)[i] =\n            get<Tag>(flat_vars)[i] +\n            windows[0] *\n                (get<Tag>(isolated_vars[0])[i] - get<Tag>(flat_vars)[i]) +\n            windows[1] *\n                (get<Tag>(isolated_vars[1])[i] - get<Tag>(flat_vars)[i]);\n      } else {\n        (*superposed_var)[i] = get<Tag>(isolated_vars[0])[i] +\n                               get<Tag>(isolated_vars[1])[i] -\n                               get<Tag>(flat_vars)[i];\n      }\n    }\n  }\n\n  void operator()(\n      const gsl::not_null<tnsr::ii<DataType, Dim>*> conformal_metric,\n      const gsl::not_null<Cache*> cache,\n      Tags::ConformalMetric<DataType, Dim, Frame::Inertial> meta)\n      const override {\n    superposition(conformal_metric, cache, meta);\n  }\n  void operator()(\n      const gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_conformal_metric,\n      const gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ConformalMetric<DataType, Dim, Frame::Inertial>,\n                    tmpl::size_t<Dim>, Frame::Inertial>\n          meta) const override {\n    superposition(deriv_conformal_metric, cache, meta);\n    add_deriv_of_window_function(deriv_conformal_metric);\n  }\n  void operator()(\n      const gsl::not_null<Scalar<DataType>*> extrinsic_curvature_trace,\n      const gsl::not_null<Cache*> cache,\n      gr::Tags::TraceExtrinsicCurvature<DataType> meta) const override {\n    superposition(extrinsic_curvature_trace, cache, meta);\n  }\n  void operator()(\n      const gsl::not_null<Scalar<DataType>*> dt_extrinsic_curvature_trace,\n      const gsl::not_null<Cache*> cache,\n      ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>> meta)\n      const override {\n    superposition(dt_extrinsic_curvature_trace, cache, meta);\n  }\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, Dim>*> shift_background,\n      gsl::not_null<Cache*> cache,\n      Tags::ShiftBackground<DataType, Dim, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::iJ<DataType, Dim>*> deriv_shift_background,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ShiftBackground<DataType, Dim, Frame::Inertial>,\n                    tmpl::size_t<Dim>, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      const gsl::not_null<Scalar<DataType>*> conformal_energy_density,\n      const gsl::not_null<Cache*> cache,\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0> meta) const {\n    superposition<false>(conformal_energy_density, cache, meta);\n  }\n  void operator()(\n      const gsl::not_null<Scalar<DataType>*> conformal_stress_trace,\n      const gsl::not_null<Cache*> cache,\n      gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0> meta) const {\n    superposition<false>(conformal_stress_trace, cache, meta);\n  }\n  void operator()(\n      const gsl::not_null<tnsr::I<DataType, Dim>*> conformal_momentum_density,\n      const gsl::not_null<Cache*> cache,\n      gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, Dim>, 0> meta)\n      const {\n    superposition<false>(conformal_momentum_density, cache, meta);\n  }\n  void operator()(\n      const gsl::not_null<Scalar<DataType>*> conformal_factor_minus_one,\n      const gsl::not_null<Cache*> cache,\n      Tags::ConformalFactorMinusOne<DataType> meta) const {\n    superposition(conformal_factor_minus_one, cache, meta);\n  }\n  void operator()(\n      const gsl::not_null<Scalar<DataType>*>\n          lapse_times_conformal_factor_minus_one,\n      const gsl::not_null<Cache*> cache,\n      Tags::LapseTimesConformalFactorMinusOne<DataType> meta) const {\n    superposition(lapse_times_conformal_factor_minus_one, cache, meta);\n  }\n  void operator()(\n      const gsl::not_null<tnsr::I<DataType, Dim>*> shift_excess,\n      const gsl::not_null<Cache*> cache,\n      Tags::ShiftExcess<DataType, Dim, Frame::Inertial> meta) const {\n    superposition(shift_excess, cache, meta);\n  }\n  void operator()(const gsl::not_null<Scalar<DataType>*> rest_mass_density,\n                  const gsl::not_null<Cache*> cache,\n                  hydro::Tags::RestMassDensity<DataType> meta) const {\n    superposition<false>(rest_mass_density, cache, meta);\n  }\n  void operator()(const gsl::not_null<Scalar<DataType>*> specific_enthalpy,\n                  const gsl::not_null<Cache*> cache,\n                  hydro::Tags::SpecificEnthalpy<DataType> meta) const {\n    superposition<false>(specific_enthalpy, cache, meta);\n  }\n  void operator()(const gsl::not_null<Scalar<DataType>*> pressure,\n                  const gsl::not_null<Cache*> cache,\n                  hydro::Tags::Pressure<DataType> meta) const {\n    superposition<false>(pressure, cache, meta);\n  }\n  void operator()(const gsl::not_null<tnsr::I<DataType, 3>*> spatial_velocity,\n                  const gsl::not_null<Cache*> cache,\n                  hydro::Tags::SpatialVelocity<DataType, 3> meta) const {\n    superposition<false>(spatial_velocity, cache, meta);\n  }\n  void operator()(const gsl::not_null<Scalar<DataType>*> lorentz_factor,\n                  const gsl::not_null<Cache*> cache,\n                  hydro::Tags::LorentzFactor<DataType> meta) const {\n    superposition<false>(lorentz_factor, cache, meta);\n  }\n  void operator()(const gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n                  const gsl::not_null<Cache*> cache,\n                  hydro::Tags::MagneticField<DataType, 3> meta) const {\n    superposition<false>(magnetic_field, cache, meta);\n  }\n\n private:\n  void add_deriv_of_window_function(\n      gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_conformal_metric) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief Binary compact-object data in general relativity, constructed from\n * superpositions of two isolated objects.\n *\n * This class implements background data for the XCTS equations describing two\n * objects in a quasi-equilibrium orbit, i.e. with \\f$\\bar{u}=0\\f$ and\n * \\f$\\partial_t K=0\\f$. Both objects can be chosen from the list of\n * `IsolatedObjectRegistrars`, e.g. they can be black-hole or neutron-star\n * solutions in different coordinates. Most quantities are constructed by\n * superposing the two isolated solutions (see e.g. Eq. (8-9) in\n * \\cite Varma2018sqd or Eq. (45-46) in \\cite Lovelace2008tw):\n *\n * \\f{align}\n * \\bar{\\gamma}_{ij} &= f_{ij} + \\sum_{\\alpha=1}^2\n * e^{-r_\\alpha^2 / w_\\alpha^2}\\left(\\gamma^\\alpha_{ij} - f_{ij}\\right) \\\\\n * K &= \\sum_{\\alpha=1}^2 e^{-r_\\alpha^2 / w_\\alpha^2}K^\\alpha\n * \\f}\n *\n * where \\f$\\gamma^\\alpha_{ij}\\f$ and \\f$K^\\alpha\\f$ denote the spatial metric\n * and extrinsic-curvature trace of the two individual solutions, \\f$r_\\alpha\\f$\n * is the Euclidean coordinate-distance from the center of each object and\n * \\f$w_\\alpha\\f$ are parameters describing the falloff widths of Gaussian\n * window functions. The window functions\n * facilitate that the influence of either of the two objects\n * at the position of the other is strongly damped, and they also avoid\n * logarithmic scaling of the solution at large distances where we would\n * typically employ an inverse-radial coordinate map and asymptotically-flat\n * boundary conditions. The falloff-widths are chosen in terms of the Newtonian\n * Lagrange points of the two objects in \\cite Varma2018sqd and\n * \\cite Lovelace2008tw, and they are input parameters in this implementation.\n * The falloff can be disabled by passing `std::nullopt` to the constructor, or\n * `None` in the input file.\n *\n * \\par Matter sources\n * Matter sources are superposed without the window functions. The analytic\n * matter sources are of\n * limited use anyway, because in a binary setting they don't take the\n * gravitational influence of the other body into account. Therefore, the matter\n * sources should typically be solved-for alongside the gravity sector to impose\n * conditions such as hydrostatic equilibrium. For scenarios where we just want\n * to superpose the isolated matter solutions and compute the resulting gravity,\n * the matter sources are simply added.\n *\n * \\par Orbital motion\n * The remaining quantities that this class implements relate to the orbital\n * motion of the two objects. To obtain initial data in \"co-rotating\"\n * coordinates where the two objects are initially at rest we prescribe the\n * background shift\n *\n * \\f{equation} \\beta^i_\\mathrm{background} = (-\\Omega y, \\Omega x, 0) +\n * \\dot{a}_0 x^i + v^i_0 \\f}\n *\n * where \\f$\\Omega\\f$ is the angular-velocity parameter and \\f$\\dot{a}_0\\f$\n * is an expansion parameter. Both control the eccentricity of the orbit.\n * The parameter \\f$v^i_0\\f$ is a constant velocity that can be used to\n * control the linear momentum of the system (see Eq. (28) in\n * \\cite Ossokine2015yla).\n */\ntemplate <typename IsolatedObjectBase, typename IsolatedObjectClasses>\nclass Binary : public elliptic::analytic_data::Background,\n               public elliptic::analytic_data::InitialGuess {\n public:\n  struct XCoords {\n    static constexpr Options::String help =\n        \"The coordinates on the x-axis where the two objects are placed\";\n    using type = std::array<double, 2>;\n  };\n  struct CenterOfMassOffset {\n    static constexpr Options::String help = {\n        \"Offset in the y and z axes applied to both objects in order to \"\n        \"control the center of mass.\"};\n    using type = std::array<double, 2>;\n  };\n  struct ObjectLeft {\n    static constexpr Options::String help =\n        \"The object placed on the negative x-axis\";\n    using type = std::unique_ptr<IsolatedObjectBase>;\n  };\n  struct ObjectRight {\n    static constexpr Options::String help =\n        \"The object placed on the positive x-axis\";\n    using type = std::unique_ptr<IsolatedObjectBase>;\n  };\n  struct AngularVelocity {\n    static constexpr Options::String help =\n        \"Orbital angular velocity 'Omega0' about the z-axis. Added to the \"\n        \"background shift as a term 'Omega0 x r'.\";\n    using type = double;\n  };\n  struct Expansion {\n    static constexpr Options::String help =\n        \"The expansion parameter 'adot0', which is a radial velocity over \"\n        \"radius. Added to the background shift as a term 'adot0 r^i'\";\n    using type = double;\n  };\n  struct LinearVelocity {\n    static constexpr Options::String help =\n        \"Constant velocity 'v0' added to the background shift to control the \"\n        \"linear momentum of the system.\";\n    using type = std::array<double, 3>;\n  };\n  struct FalloffWidths {\n    static constexpr Options::String help =\n        \"The widths for the window functions around the two objects, or 'None' \"\n        \"to disable the Gaussian falloff.\";\n    using type = Options::Auto<std::array<double, 2>, Options::AutoLabel::None>;\n  };\n  using options =\n      tmpl::list<XCoords, CenterOfMassOffset, ObjectLeft, ObjectRight,\n                 AngularVelocity, Expansion, LinearVelocity, FalloffWidths>;\n  static constexpr Options::String help =\n      \"Binary compact-object data in general relativity, constructed from \"\n      \"superpositions of two isolated objects.\";\n\n  Binary() = default;\n  Binary(const Binary&) = delete;\n  Binary& operator=(const Binary&) = delete;\n  Binary(Binary&&) = default;\n  Binary& operator=(Binary&&) = default;\n  ~Binary() = default;\n\n  Binary(const std::array<double, 2> xcoords,\n         const std::array<double, 2> center_of_mass_offset,\n         std::unique_ptr<IsolatedObjectBase> object_left,\n         std::unique_ptr<IsolatedObjectBase> object_right,\n         const double angular_velocity, const double expansion,\n         const std::array<double, 3> linear_velocity,\n         const std::optional<std::array<double, 2>> falloff_widths,\n         const Options::Context& context = {})\n      : xcoords_(xcoords),\n        y_offset_(center_of_mass_offset[0]),\n        z_offset_(center_of_mass_offset[1]),\n        superposed_objects_({std::move(object_left), std::move(object_right)}),\n        angular_velocity_(angular_velocity),\n        expansion_(expansion),\n        linear_velocity_(linear_velocity),\n        falloff_widths_(falloff_widths) {\n    if (xcoords_[0] >= xcoords_[1]) {\n      PARSE_ERROR(context, \"Specify 'XCoords' ascending from left to right.\");\n    }\n  }\n\n  explicit Binary(CkMigrateMessage* m)\n      : elliptic::analytic_data::Background(m),\n        elliptic::analytic_data::InitialGuess(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Binary);\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables_impl<DataType>(x, std::nullopt, std::nullopt,\n                                    tmpl::list<RequestedTags...>{});\n  }\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x, const Mesh<3>& mesh,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables_impl<DataVector>(x, mesh, inv_jacobian,\n                                      tmpl::list<RequestedTags...>{});\n  }\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::Background::pup(p);\n    elliptic::analytic_data::InitialGuess::pup(p);\n    p | xcoords_;\n    p | y_offset_;\n    p | z_offset_;\n    p | superposed_objects_;\n    p | angular_velocity_;\n    p | expansion_;\n    p | linear_velocity_;\n    p | falloff_widths_;\n  }\n\n  /// Coordinates of the objects, ascending left to right\n  const std::array<double, 2>& x_coords() const { return xcoords_; }\n  /// Offset in y and z coordinates of the objects\n  double y_offset() const { return y_offset_; }\n  double z_offset() const { return z_offset_; }\n  /// The two objects. First entry is the left object, second entry is the right\n  /// object.\n  const std::array<std::unique_ptr<IsolatedObjectBase>, 2>& superposed_objects()\n      const {\n    return superposed_objects_;\n  }\n  double angular_velocity() const { return angular_velocity_; }\n  double expansion() const { return expansion_; }\n  const std::array<double, 3>& linear_velocity() const {\n    return linear_velocity_;\n  }\n  const std::optional<std::array<double, 2>>& falloff_widths() const {\n    return falloff_widths_;\n  }\n\n private:\n  std::array<double, 2> xcoords_{};\n  double y_offset_{};\n  double z_offset_{};\n  std::array<std::unique_ptr<IsolatedObjectBase>, 2> superposed_objects_{};\n  Xcts::Solutions::Flatness flatness_{};\n  double angular_velocity_ = std::numeric_limits<double>::signaling_NaN();\n  double expansion_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> linear_velocity_{};\n  std::optional<std::array<double, 2>> falloff_widths_{};\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables_impl(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      std::optional<std::reference_wrapper<const Mesh<3>>> mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, 3, Frame::ElementLogical, Frame::Inertial>>>\n          inv_jacobian,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    std::array<tnsr::I<DataVector, 3>, 2> x_isolated{{x, x}};\n    const std::array<std::array<double, 3>, 2> coords_isolated{\n        {{{xcoords_[0], y_offset_, z_offset_}},\n         {{xcoords_[1], y_offset_, z_offset_}}}};\n    std::array<DataVector, 2> euclidean_distance{};\n    std::array<DataVector, 2> windows{};\n    // Possible optimization: Only retrieve those superposed tags from the\n    // isolated solutions that are actually needed. This needs some dependency\n    // logic, because some of the non-superposed tags depend on superposed tags.\n    using VarsComputer = detail::BinaryVariables<DataType>;\n    using requested_superposed_tags = typename VarsComputer::superposed_tags;\n    std::array<tuples::tagged_tuple_from_typelist<requested_superposed_tags>, 2>\n        isolated_vars;\n    for (size_t i = 0; i < 2; ++i) {\n      for (size_t dim = 0; dim < 3; dim++) {\n        gsl::at(x_isolated, i).get(dim) -= gsl::at(coords_isolated, i)[dim];\n      }\n      gsl::at(euclidean_distance, i) = get(magnitude(gsl::at(x_isolated, i)));\n      if (falloff_widths_.has_value()) {\n        gsl::at(windows, i) = exp(-square(gsl::at(euclidean_distance, i)) /\n                                  square(gsl::at(*falloff_widths_, i)));\n      } else {\n        gsl::at(windows, i) = make_with_value<DataVector>(x, 1.);\n      }\n      gsl::at(isolated_vars, i) = get_isolated_vars<requested_superposed_tags>(\n          *gsl::at(superposed_objects_, i), gsl::at(x_isolated, i));\n    }\n    auto flat_vars = flatness_.variables(x, requested_superposed_tags{});\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const VarsComputer computer{std::move(mesh),\n                                std::move(inv_jacobian),\n                                x,\n                                angular_velocity_,\n                                expansion_,\n                                linear_velocity_,\n                                falloff_widths_,\n                                std::move(x_isolated),\n                                std::move(windows),\n                                std::move(flat_vars),\n                                std::move(isolated_vars)};\n    return {cache.get_var(computer, RequestedTags{})...};\n  }\n\n  template <typename TagsList, typename... Args>\n  tuples::tagged_tuple_from_typelist<TagsList> get_isolated_vars(\n      const IsolatedObjectBase& isolated_object, const Args&... args) const {\n    return call_with_dynamic_type<tuples::tagged_tuple_from_typelist<TagsList>,\n                                  IsolatedObjectClasses>(\n        &isolated_object, [&args...](const auto* const derived) {\n          return derived->variables(args..., TagsList{});\n        });\n  }\n};\n\n/// \\cond\ntemplate <typename IsolatedObjectBase, typename IsolatedObjectClasses>\nPUP::able::PUP_ID Binary<IsolatedObjectBase, IsolatedObjectClasses>::my_PUP_ID =\n    0;  // NOLINT\n/// \\endcond\n\n}  // namespace Xcts::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY XctsAnalyticData)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Binary.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticData.hpp\n  Binary.hpp\n  CommonVariables.hpp\n  CommonVariables.tpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  GeneralRelativity\n  Hydro\n  LinearOperators\n  Options\n  Parallel\n  Spectral\n  Utilities\n  PRIVATE\n  ElasticityPointwiseFunctions\n  XctsPointwiseFunctions\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Xcts/CommonVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts::AnalyticData {\n\n/// Tags for variables that analytic-data classes can share\ntemplate <typename DataType>\nusing common_tags = tmpl::list<\n    // Background quantities\n    Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n    gr::Tags::TraceExtrinsicCurvature<DataType>,\n    ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>>,\n    Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n    ::Tags::deriv<Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataType, 3,\n                                                            Frame::Inertial>,\n    // Analytic derivatives\n    ::Tags::deriv<Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    // Background quantities derived from the above\n    Tags::InverseConformalMetric<DataType, 3, Frame::Inertial>,\n    Tags::ConformalChristoffelFirstKind<DataType, 3, Frame::Inertial>,\n    Tags::ConformalChristoffelSecondKind<DataType, 3, Frame::Inertial>,\n    Tags::ConformalChristoffelContracted<DataType, 3, Frame::Inertial>,\n    // Fixed sources\n    ::Tags::FixedSource<Tags::ConformalFactorMinusOne<DataType>>,\n    ::Tags::FixedSource<Tags::LapseTimesConformalFactorMinusOne<DataType>>,\n    ::Tags::FixedSource<Tags::ShiftExcess<DataType, 3, Frame::Inertial>>,\n    // These tags require numerical differentiation\n    ::Tags::deriv<\n        Tags::ConformalChristoffelSecondKind<DataType, 3, Frame::Inertial>,\n        tmpl::size_t<3>, Frame::Inertial>,\n    Tags::ConformalRicciTensor<DataType, 3, Frame::Inertial>,\n    Tags::ConformalRicciScalar<DataType>,\n    ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    ::Tags::div<Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n        DataType, 3, Frame::Inertial>>>;\n\n/// Tags for hydro variables that are typically retrieved from a hydro solution\ntemplate <typename DataType>\nusing hydro_tags = tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                              hydro::Tags::SpecificEnthalpy<DataType>,\n                              hydro::Tags::Pressure<DataType>,\n                              hydro::Tags::SpatialVelocity<DataType, 3>,\n                              hydro::Tags::LorentzFactor<DataType>,\n                              hydro::Tags::MagneticField<DataType, 3>>;\n\n/*!\n * \\brief Implementations for variables that analytic-data classes can share\n *\n * Analytic-data classes can derive their variable computers from this class to\n * inherit implementations for the `common_tags`. Note that some variables\n * require a numeric differentiation. To compute those variables, a `mesh` and\n * an `inv_jacobian` must be passed to the constructor. The `mesh` and the\n * `inv_jacobian` can be set to `std::nullopt` if no variables with numeric\n * derivatives are requested.\n *\n * \\tparam DataType `double` or `DataVector`. Must be `DataVector` if variables\n * with numeric derivatives are requested.\n * \\tparam Cache The `CachedTempBuffer` used by the analytic-data class.\n */\ntemplate <typename DataType, typename Cache>\nstruct CommonVariables {\n  static constexpr size_t Dim = 3;\n\n  CommonVariables(const CommonVariables&) = default;\n  CommonVariables& operator=(const CommonVariables&) = default;\n  CommonVariables(CommonVariables&&) = default;\n  CommonVariables& operator=(CommonVariables&&) = default;\n  virtual ~CommonVariables() = default;\n\n  CommonVariables(\n      std::optional<std::reference_wrapper<const Mesh<Dim>>> local_mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, Dim, Frame::ElementLogical, Frame::Inertial>>>\n          local_inv_jacobian)\n      : mesh(std::move(local_mesh)),\n        inv_jacobian(std::move(local_inv_jacobian)) {}\n\n  virtual void operator()(\n      gsl::not_null<tnsr::ii<DataType, Dim>*> conformal_metric,\n      gsl::not_null<Cache*> cache,\n      Tags::ConformalMetric<DataType, Dim, Frame::Inertial> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*> extrinsic_curvature_trace,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*> dt_extrinsic_curvature_trace,\n      gsl::not_null<Cache*> cache,\n      ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/)\n      const = 0;\n  virtual void operator()(\n      gsl::not_null<tnsr::I<DataType, Dim>*> shift_background,\n      gsl::not_null<Cache*> cache,\n      Tags::ShiftBackground<DataType, Dim, Frame::Inertial> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<tnsr::iJ<DataType, Dim>*> deriv_shift_background,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ShiftBackground<DataType, Dim, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<tnsr::II<DataType, Dim>*> longitudinal_shift_background,\n      gsl::not_null<Cache*> cache,\n      Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n          DataType, Dim, Frame::Inertial> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_conformal_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ConformalMetric<DataType, Dim, Frame::Inertial>,\n                    tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<tnsr::II<DataType, Dim>*> inv_conformal_metric,\n      gsl::not_null<Cache*> cache,\n      Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial> /*meta*/)\n      const;\n  virtual void operator()(\n      gsl::not_null<tnsr::ijj<DataType, Dim>*> conformal_christoffel_first_kind,\n      gsl::not_null<Cache*> cache,\n      Tags::ConformalChristoffelFirstKind<DataType, Dim,\n                                          Frame::Inertial> /*meta*/) const;\n  virtual void operator()(gsl::not_null<tnsr::Ijj<DataType, Dim>*>\n                              conformal_christoffel_second_kind,\n                          gsl::not_null<Cache*> cache,\n                          Tags::ConformalChristoffelSecondKind<\n                              DataType, Dim, Frame::Inertial> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::i<DataType, Dim>*> conformal_christoffel_contracted,\n      gsl::not_null<Cache*> cache,\n      Tags::ConformalChristoffelContracted<DataType, Dim,\n                                           Frame::Inertial> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> fixed_source_for_hamiltonian_constraint,\n      gsl::not_null<Cache*> cache,\n      ::Tags::FixedSource<Tags::ConformalFactorMinusOne<DataType>> /*meta*/)\n      const;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> fixed_source_for_lapse_equation,\n      gsl::not_null<Cache*> cache,\n      ::Tags::FixedSource<\n          Tags::LapseTimesConformalFactorMinusOne<DataType>> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, 3>*> fixed_source_momentum_constraint,\n      gsl::not_null<Cache*> cache,\n      ::Tags::FixedSource<\n          Tags::ShiftExcess<DataType, 3, Frame::Inertial>> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::iJkk<DataType, Dim>*>\n          deriv_conformal_christoffel_second_kind,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<\n          Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame::Inertial>,\n          tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::ii<DataType, Dim>*> conformal_ricci_tensor,\n      gsl::not_null<Cache*> cache,\n      Tags::ConformalRicciTensor<DataType, Dim, Frame::Inertial> /*meta*/)\n      const;\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*> conformal_ricci_scalar,\n      gsl::not_null<Cache*> cache,\n      Tags::ConformalRicciScalar<DataType> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::i<DataType, Dim>*> deriv_extrinsic_curvature_trace,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>,\n                    tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::I<DataType, Dim>*> div_longitudinal_shift_background,\n      gsl::not_null<Cache*> cache,\n      ::Tags::div<Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n          DataType, Dim, Frame::Inertial>> /*meta*/) const;\n\n  std::optional<std::reference_wrapper<const Mesh<Dim>>> mesh;\n  std::optional<std::reference_wrapper<const InverseJacobian<\n      DataType, Dim, Frame::ElementLogical, Frame::Inertial>>>\n      inv_jacobian;\n};\n\n}  // namespace Xcts::AnalyticData\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticData/Xcts/CommonVariables.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/Xcts/CommonVariables.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <functional>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/Xcts/LongitudinalOperator.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nnamespace Xcts::AnalyticData {\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::II<DataType, Dim>*> inv_conformal_metric,\n    const gsl::not_null<Cache*> cache,\n    Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial> /*meta*/)\n    const {\n  const auto& conformal_metric = cache->get_var(\n      *this, Tags::ConformalMetric<DataType, Dim, Frame::Inertial>{});\n  Scalar<DataType> unused_det{get_size(*conformal_metric.begin())};\n  determinant_and_inverse(make_not_null(&unused_det), inv_conformal_metric,\n                          conformal_metric);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, Dim>*>\n        conformal_christoffel_first_kind,\n    const gsl::not_null<Cache*> cache,\n    Tags::ConformalChristoffelFirstKind<DataType, Dim,\n                                        Frame::Inertial> /*meta*/) const {\n  const auto& deriv_conformal_metric = cache->get_var(\n      *this, ::Tags::deriv<Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  gr::christoffel_first_kind(conformal_christoffel_first_kind,\n                             deriv_conformal_metric);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::Ijj<DataType, Dim>*>\n        conformal_christoffel_second_kind,\n    const gsl::not_null<Cache*> cache,\n    Tags::ConformalChristoffelSecondKind<DataType, Dim,\n                                         Frame::Inertial> /*meta*/) const {\n  const auto& conformal_christoffel_first_kind = cache->get_var(\n      *this,\n      Tags::ConformalChristoffelFirstKind<DataType, Dim, Frame::Inertial>{});\n  const auto& inv_conformal_metric = cache->get_var(\n      *this, Tags::InverseConformalMetric<DataType, 3, Frame::Inertial>{});\n  raise_or_lower_first_index(conformal_christoffel_second_kind,\n                             conformal_christoffel_first_kind,\n                             inv_conformal_metric);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::i<DataType, Dim>*>\n        conformal_christoffel_contracted,\n    const gsl::not_null<Cache*> cache,\n    Tags::ConformalChristoffelContracted<DataType, Dim,\n                                         Frame::Inertial> /*meta*/) const {\n  const auto& conformal_christoffel_second_kind = cache->get_var(\n      *this,\n      Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame::Inertial>{});\n  for (size_t i = 0; i < Dim; ++i) {\n    conformal_christoffel_contracted->get(i) =\n        conformal_christoffel_second_kind.get(0, i, 0);\n    for (size_t j = 1; j < Dim; ++j) {\n      conformal_christoffel_contracted->get(i) +=\n          conformal_christoffel_second_kind.get(j, i, j);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<Scalar<DataType>*>\n        fixed_source_for_hamiltonian_constraint,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<Tags::ConformalFactorMinusOne<DataType>> /*meta*/)\n    const {\n  std::fill(fixed_source_for_hamiltonian_constraint->begin(),\n            fixed_source_for_hamiltonian_constraint->end(), 0.);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<Scalar<DataType>*> fixed_source_for_lapse_equation,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<\n        Tags::LapseTimesConformalFactorMinusOne<DataType>> /*meta*/) const {\n  std::fill(fixed_source_for_lapse_equation->begin(),\n            fixed_source_for_lapse_equation->end(), 0.);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> fixed_source_momentum_constraint,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<\n        Tags::ShiftExcess<DataType, 3, Frame::Inertial>> /*meta*/) const {\n  std::fill(fixed_source_momentum_constraint->begin(),\n            fixed_source_momentum_constraint->end(), 0.);\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::iJkk<DataType, Dim>*>\n        deriv_conformal_christoffel_second_kind,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<\n        Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame::Inertial>,\n        tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const {\n  ASSERT(mesh.has_value() and inv_jacobian.has_value(),\n         \"Need a mesh and a Jacobian for numeric differentiation.\");\n  if constexpr (std::is_same_v<DataType, DataVector>) {\n    const auto& conformal_christoffel_second_kind = cache->get_var(\n        *this,\n        Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame::Inertial>{});\n    partial_derivative(deriv_conformal_christoffel_second_kind,\n                       conformal_christoffel_second_kind, mesh->get(),\n                       inv_jacobian->get());\n  } else {\n    (void)deriv_conformal_christoffel_second_kind;\n    (void)cache;\n    ERROR(\n        \"Numeric differentiation only works with DataVectors because it needs \"\n        \"a grid.\");\n  }\n}\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__)\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, Dim>*> conformal_ricci_tensor,\n    const gsl::not_null<Cache*> cache,\n    Tags::ConformalRicciTensor<DataType, Dim, Frame::Inertial> /*meta*/) const {\n  const auto& conformal_christoffel_second_kind = cache->get_var(\n      *this,\n      Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame::Inertial>{});\n  const auto& deriv_conformal_christoffel_second_kind = cache->get_var(\n      *this,\n      ::Tags::deriv<\n          Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame::Inertial>,\n          tmpl::size_t<Dim>, Frame::Inertial>{});\n  gr::ricci_tensor(conformal_ricci_tensor, conformal_christoffel_second_kind,\n                   deriv_conformal_christoffel_second_kind);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_ricci_scalar,\n    const gsl::not_null<Cache*> cache,\n    Tags::ConformalRicciScalar<DataType> /*meta*/) const {\n  const auto& conformal_ricci_tensor = cache->get_var(\n      *this, Tags::ConformalRicciTensor<DataType, Dim, Frame::Inertial>{});\n  const auto& inv_conformal_metric = cache->get_var(\n      *this, Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial>{});\n  trace(conformal_ricci_scalar, conformal_ricci_tensor, inv_conformal_metric);\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::i<DataType, Dim>*>\n        deriv_extrinsic_curvature_trace,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>,\n                  tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const {\n  ASSERT(mesh.has_value() and inv_jacobian.has_value(),\n         \"Need a mesh and a Jacobian for numeric differentiation.\");\n  if constexpr (std::is_same_v<DataType, DataVector>) {\n    const auto& extrinsic_curvature_trace =\n        cache->get_var(*this, gr::Tags::TraceExtrinsicCurvature<DataType>{});\n    partial_derivative(deriv_extrinsic_curvature_trace,\n                       extrinsic_curvature_trace, mesh->get(),\n                       inv_jacobian->get());\n  } else {\n    (void)deriv_extrinsic_curvature_trace;\n    (void)cache;\n    ERROR(\n        \"Numeric differentiation only works with DataVectors because it needs \"\n        \"a grid.\");\n  }\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::II<DataType, Dim>*> longitudinal_shift_background,\n    const gsl::not_null<Cache*> cache,\n    Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n        DataType, Dim, Frame::Inertial> /*meta*/) const {\n  const auto& shift_background = cache->get_var(\n      *this, Tags::ShiftBackground<DataType, Dim, Frame::Inertial>{});\n  const auto& deriv_shift_background = cache->get_var(\n      *this,\n      ::Tags::deriv<Tags::ShiftBackground<DataType, Dim, Frame::Inertial>,\n                    tmpl::size_t<Dim>, Frame::Inertial>{});\n  const auto& inv_conformal_metric = cache->get_var(\n      *this, Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial>{});\n  const auto& conformal_christoffel_second_kind = cache->get_var(\n      *this,\n      Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame::Inertial>{});\n  Xcts::longitudinal_operator(longitudinal_shift_background, shift_background,\n                              deriv_shift_background, inv_conformal_metric,\n                              conformal_christoffel_second_kind);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*>\n        div_longitudinal_shift_background,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::div<Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n        DataType, Dim, Frame::Inertial>> /*meta*/) const {\n  ASSERT(mesh.has_value() and inv_jacobian.has_value(),\n         \"Need a mesh and a Jacobian for numeric differentiation.\");\n  if constexpr (std::is_same_v<DataType, DataVector>) {\n    // Copy into a Variables to take the divergence because at this time the\n    // `divergence` function only works with Variables. This won't be used for\n    // anything performance-critical, but adding a `divergence` overload that\n    // takes a Tensor is an obvious optimization here.\n    using tag = Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n        DataType, Dim, Frame::Inertial>;\n    Variables<tmpl::list<tag>> vars{mesh->get().number_of_grid_points()};\n    get<tag>(vars) = cache->get_var(*this, tag{});\n    const auto derivs = divergence(vars, mesh->get(), inv_jacobian->get());\n    *div_longitudinal_shift_background = get<::Tags::div<tag>>(derivs);\n  } else {\n    (void)div_longitudinal_shift_background;\n    (void)cache;\n    ERROR(\n        \"Numeric differentiation only works with DataVectors because it needs \"\n        \"a grid.\");\n  }\n}\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__)\n\n}  // namespace Xcts::AnalyticData\n\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n// note that std::is_convertible is used in the following type aliases as it\n// will not match private base classes (unlike std::is_base_of) and some\n// analytic data privately inherits from an analytic solution\n\n/// \\ingroup AnalyticSolutionsGroup\n/// \\brief Empty base class for marking analytic solutions.\nstruct MarkAsAnalyticSolution {};\n\n/// \\ingroup AnalyticSolutionsGroup\n/// \\brief Check if `T` is an analytic solution\ntemplate <typename T>\nusing is_analytic_solution =\n    typename std::is_convertible<T*, MarkAsAnalyticSolution*>;\n\n/// \\ingroup AnalyticSolutionsGroup\n/// \\brief `true` if `T` is an analytic solution\ntemplate <typename T>\nconstexpr bool is_analytic_solution_v =\n    std::is_convertible_v<T*, MarkAsAnalyticSolution*>;\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/Bump.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Bump.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace Burgers::Solutions {\n\nBump::Bump(const double half_width, const double height, const double center)\n    : half_width_(half_width), height_(height), center_(center) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> Bump::get_clone() const {\n  return std::make_unique<Bump>(*this);\n}\n\nBump::Bump(CkMigrateMessage* msg) : InitialData(msg) {}\n\ntemplate <typename T>\nScalar<T> Bump::u(const tnsr::I<T, 1>& x, double t) const {\n  const T center_distance = get<0>(x) - center_;\n  // Distance from the current peak location divided by the half width\n  // and the time the shock reaches the solution zero.\n  const T reduced_peak_distance =\n      2. * height_ / square(half_width_) * (center_distance - height_ * t);\n\n  const T denom = 1. / (1. + sqrt(1. - 2. * t * reduced_peak_distance));\n\n  return Scalar<T>(2. * denom *\n                   (height_ - center_distance * reduced_peak_distance * denom));\n}\n\ntemplate <typename T>\nScalar<T> Bump::du_dt(const tnsr::I<T, 1>& x, double t) const {\n  const T center_distance = get<0>(x) - center_;\n  // Distance from the current peak location divided by the half width\n  // and the time the shock reaches the solution zero.\n  const T reduced_peak_distance =\n      2. * height_ / square(half_width_) * (center_distance - height_ * t);\n\n  const T denom = 1. / (1. + sqrt(1. - 2. * t * reduced_peak_distance));\n\n  return Scalar<T>(4. * square(height_ / half_width_) * square(denom) *\n                   (denom / (1. - denom) *\n                        (center_distance - 2. * height_ * t) *\n                        (1 - 2. / height_ * denom * center_distance *\n                                 reduced_peak_distance) +\n                    center_distance));\n}\n\ntuples::TaggedTuple<Tags::U> Bump::variables(\n    const tnsr::I<DataVector, 1>& x, double t,\n    tmpl::list<Tags::U> /*meta*/) const {\n  return {u(x, t)};\n}\n\ntuples::TaggedTuple<::Tags::dt<Tags::U>> Bump::variables(\n    const tnsr::I<DataVector, 1>& x, double t,\n    tmpl::list<::Tags::dt<Tags::U>> /*meta*/) const {\n  return {du_dt(x, t)};\n}\n\nvoid Bump::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | half_width_;\n  p | height_;\n  p | center_;\n}\n\nPUP::able::PUP_ID Bump::my_PUP_ID = 0;\n}  // namespace Burgers::Solutions\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                    \\\n  template Scalar<DTYPE(data)> Burgers::Solutions::Bump::u(     \\\n      const tnsr::I<DTYPE(data), 1>& x, double t) const;        \\\n  template Scalar<DTYPE(data)> Burgers::Solutions::Bump::du_dt( \\\n      const tnsr::I<DTYPE(data), 1>& x, double t) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/Bump.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Burgers::Solutions {\n/*!\n * \\brief A solution resembling a bump.\n *\n * At \\f$t=0\\f$, the solution is a parabola:\n * \\f{equation*}\n *  u(x, t) = h \\left(1 - \\left(\\frac{x - c}{w}\\right)^2\\right),\n * \\f}\n * where \\f$h\\f$ is the height, \\f$c\\f$ is the center, and \\f$w\\f$ is\n * the distance from the center to the zeros.  A shock propagates in\n * from infinity and reaches one of the zeros at \\f$t = \\frac{w}{2\n * h}\\f$.\n */\nclass Bump : public evolution::initial_data::InitialData,\n             public MarkAsAnalyticSolution {\n public:\n  struct HalfWidth {\n    using type = double;\n    static constexpr Options::String help{\n        \"The distance from the center to the zero of the bump\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  struct Height {\n    using type = double;\n    static constexpr Options::String help{\"The height of the bump\"};\n  };\n\n  struct Center {\n    using type = double;\n    static constexpr Options::String help{\"The center of the bump\"};\n  };\n\n  using options = tmpl::list<HalfWidth, Height, Center>;\n  static constexpr Options::String help{\"A bump solution\"};\n\n  Bump() = default;\n  Bump(const Bump&) = default;\n  Bump& operator=(const Bump&) = default;\n  Bump(Bump&&) = default;\n  Bump& operator=(Bump&&) = default;\n  ~Bump() override = default;\n\n  Bump(double half_width, double height, double center = 0.);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit Bump(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Bump);\n  /// \\endcond\n\n  template <typename T>\n  Scalar<T> u(const tnsr::I<T, 1>& x, double t) const;\n\n  template <typename T>\n  Scalar<T> du_dt(const tnsr::I<T, 1>& x, double t) const;\n\n  tuples::TaggedTuple<Tags::U> variables(const tnsr::I<DataVector, 1>& x,\n                                         double t,\n                                         tmpl::list<Tags::U> /*meta*/) const;\n\n  tuples::TaggedTuple<::Tags::dt<Burgers::Tags::U>> variables(\n      const tnsr::I<DataVector, 1>& x, double t,\n      tmpl::list<::Tags::dt<Tags::U>> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double half_width_ = std::numeric_limits<double>::signaling_NaN();\n  double height_ = std::numeric_limits<double>::signaling_NaN();\n  double center_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace Burgers::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY BurgersSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Bump.cpp\n  Linear.cpp\n  Step.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Bump.hpp\n  Factory.hpp\n  Linear.hpp\n  Solutions.hpp\n  Step.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Serialization\n  INTERFACE\n  ErrorHandling\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Bump.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Linear.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Step.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers::Solutions {\n/// \\brief List of all analytic solutions\nusing all_solutions = tmpl::list<Bump, Linear, Step>;\n}  // namespace Burgers::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/Linear.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Linear.hpp\"\n\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace Burgers::Solutions {\n\nLinear::Linear(const double shock_time) : shock_time_(shock_time) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> Linear::get_clone()\n    const {\n  return std::make_unique<Linear>(*this);\n}\n\nLinear::Linear(CkMigrateMessage* msg) : InitialData(msg) {}\n\ntemplate <typename T>\nScalar<T> Linear::u(const tnsr::I<T, 1>& x, double t) const {\n  Scalar<T> result(get<0>(x));\n  get(result) /= (t - shock_time_);\n  return result;\n}\n\ntemplate <typename T>\nScalar<T> Linear::du_dt(const tnsr::I<T, 1>& x, double t) const {\n  Scalar<T> result(get<0>(x));\n  get(result) /= -square(t - shock_time_);\n  return result;\n}\n\ntuples::TaggedTuple<Tags::U> Linear::variables(\n    const tnsr::I<DataVector, 1>& x, double t,\n    tmpl::list<Tags::U> /*meta*/) const {\n  return {u(x, t)};\n}\n\ntuples::TaggedTuple<::Tags::dt<Tags::U>> Linear::variables(\n    const tnsr::I<DataVector, 1>& x, double t,\n    tmpl::list<::Tags::dt<Tags::U>> /*meta*/) const {\n  return {du_dt(x, t)};\n}\n\nvoid Linear::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | shock_time_;\n}\n\nPUP::able::PUP_ID Linear::my_PUP_ID = 0;\n}  // namespace Burgers::Solutions\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                      \\\n  template Scalar<DTYPE(data)> Burgers::Solutions::Linear::u(     \\\n      const tnsr::I<DTYPE(data), 1>& x, double t) const;          \\\n  template Scalar<DTYPE(data)> Burgers::Solutions::Linear::du_dt( \\\n      const tnsr::I<DTYPE(data), 1>& x, double t) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/Linear.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Burgers::Solutions {\n/// A solution that is linear in space at all times.\n///\n/// \\f$u(x, t) = x / (t - t_0)\\f$ where \\f$t_0\\f$ is the shock time.\nclass Linear : public evolution::initial_data::InitialData,\n               public MarkAsAnalyticSolution {\n public:\n  struct ShockTime {\n    using type = double;\n    static constexpr Options::String help{\"The time at which a shock forms\"};\n  };\n\n  using options = tmpl::list<ShockTime>;\n  static constexpr Options::String help{\"A spatially linear solution\"};\n\n  Linear() = default;\n  Linear(const Linear&) = default;\n  Linear& operator=(const Linear&) = default;\n  Linear(Linear&&) = default;\n  Linear& operator=(Linear&&) = default;\n  ~Linear() override = default;\n\n  explicit Linear(double shock_time);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit Linear(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Linear);\n  /// \\endcond\n\n  template <typename T>\n  Scalar<T> u(const tnsr::I<T, 1>& x, double t) const;\n\n  template <typename T>\n  Scalar<T> du_dt(const tnsr::I<T, 1>& x, double t) const;\n\n  tuples::TaggedTuple<Tags::U> variables(const tnsr::I<DataVector, 1>& x,\n                                         double t,\n                                         tmpl::list<Tags::U> /*meta*/) const;\n\n  tuples::TaggedTuple<::Tags::dt<Tags::U>> variables(\n      const tnsr::I<DataVector, 1>& x, double t,\n      tmpl::list<::Tags::dt<Tags::U>> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double shock_time_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace Burgers::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines Burgers::Solutions\n\n#pragma once\n\nnamespace Burgers {\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing a solution to the Burgers equation\n * \\f$0 = \\partial_t U + \\partial_x\\left(U^2/2\\right)\\f$.\n */\nnamespace Solutions {}\n}  // namespace Burgers\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/Step.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Step.hpp\"\n\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace Burgers::Solutions {\n\nStep::Step(const double left_value, const double right_value,\n           const double initial_shock_position, const Options::Context& context)\n    : left_value_(left_value),\n      right_value_(right_value),\n      initial_shock_position_(initial_shock_position) {\n  if (left_value <= right_value) {\n    PARSE_ERROR(context, \"Shock solution expects left_value > right_value\");\n  }\n}\n\nStep::Step(CkMigrateMessage* msg) : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> Step::get_clone() const {\n  return std::make_unique<Step>(*this);\n}\n\ntemplate <typename T>\nScalar<T> Step::u(const tnsr::I<T, 1>& x, const double t) const {\n  const double current_shock_position =\n      initial_shock_position_ + 0.5 * (left_value_ + right_value_) * t;\n  return Scalar<T>(left_value_ -\n                   (left_value_ - right_value_) *\n                       step_function(get<0>(x) - current_shock_position));\n}\n\ntemplate <typename T>\nScalar<T> Step::du_dt(const tnsr::I<T, 1>& x, const double /*t*/) const {\n  return make_with_value<Scalar<T>>(x, 0.0);\n}\n\ntuples::TaggedTuple<Tags::U> Step::variables(\n    const tnsr::I<DataVector, 1>& x, const double t,\n    tmpl::list<Tags::U> /*meta*/) const {\n  return {u(x, t)};\n}\n\ntuples::TaggedTuple<::Tags::dt<Tags::U>> Step::variables(\n    const tnsr::I<DataVector, 1>& x, const double t,\n    tmpl::list<::Tags::dt<Tags::U>> /*meta*/) const {\n  return {du_dt(x, t)};\n}\n\nvoid Step::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | left_value_;\n  p | right_value_;\n  p | initial_shock_position_;\n}\n\nPUP::able::PUP_ID Step::my_PUP_ID = 0;\n}  // namespace Burgers::Solutions\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                    \\\n  template Scalar<DTYPE(data)> Burgers::Solutions::Step::u(     \\\n      const tnsr::I<DTYPE(data), 1>& x, double t) const;        \\\n  template Scalar<DTYPE(data)> Burgers::Solutions::Step::du_dt( \\\n      const tnsr::I<DTYPE(data), 1>& x, double t) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Burgers/Step.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Burgers::Solutions {\n/// \\brief A propagating shock between two constant states\n///\n/// The shock propagates left-to-right, with profile\n/// \\f$U(x, t) = U_R + (U_L - U_R) H(-(x - x_0 - v t))\\f$.\n/// Here \\f$U_L\\f$ and \\f$U_R\\f$ are the left and right constant states,\n/// satisfying \\f$U_L > U_R\\f$; \\f$H\\f$ is the Heaviside function; \\f$x_0\\f$ is\n/// the initial (i.e., \\f$t=0\\f$) position of the shock; and\n/// \\f$v = 0.5 (U_L + U_R)\\f$ is the speed of the shock.\n///\n/// \\note At the shock, where \\f$x = x_0 + vt\\f$, we have \\f$U(x, t) = U_L\\f$.\n/// (This is inherited from the Heaviside implementation `step_function`.)\n/// Additionally, the time derivative \\f$\\partial_t u0\\f$ is zero, rather than\n/// the correct delta function.\nclass Step : public evolution::initial_data::InitialData,\n             public MarkAsAnalyticSolution {\n public:\n  struct LeftValue {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help{\"The value of U, left of the shock\"};\n  };\n  struct RightValue {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help{\"The value of U, right of the shock\"};\n  };\n  struct InitialPosition {\n    using type = double;\n    static constexpr Options::String help{\"The shock's position at t==0\"};\n  };\n\n  using options = tmpl::list<LeftValue, RightValue, InitialPosition>;\n  static constexpr Options::String help{\"A propagating shock solution\"};\n\n  Step(double left_value, double right_value, double initial_shock_position,\n       const Options::Context& context = {});\n\n  Step() = default;\n  Step(const Step&) = default;\n  Step& operator=(const Step&) = default;\n  Step(Step&&) = default;\n  Step& operator=(Step&&) = default;\n  ~Step() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit Step(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Step);\n  /// \\endcond\n\n  template <typename T>\n  Scalar<T> u(const tnsr::I<T, 1>& x, double t) const;\n\n  template <typename T>\n  Scalar<T> du_dt(const tnsr::I<T, 1>& x, double t) const;\n\n  tuples::TaggedTuple<Tags::U> variables(const tnsr::I<DataVector, 1>& x,\n                                         double t,\n                                         tmpl::list<Tags::U> /*meta*/) const;\n\n  tuples::TaggedTuple<::Tags::dt<Tags::U>> variables(\n      const tnsr::I<DataVector, 1>& x, double t,\n      tmpl::list<::Tags::dt<Tags::U>> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double left_value_ = std::numeric_limits<double>::signaling_NaN();\n  double right_value_ = std::numeric_limits<double>::signaling_NaN();\n  double initial_shock_position_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace Burgers::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY AnalyticSolutions)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticSolution.hpp\n  Tags.hpp\n  )\n\nadd_subdirectory(Burgers)\nadd_subdirectory(Elasticity)\nadd_subdirectory(ForceFree)\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(GhGrMhd)\nadd_subdirectory(GhRelativisticEuler)\nadd_subdirectory(GrMhd)\nadd_subdirectory(Hydro)\nadd_subdirectory(NewtonianEuler)\nadd_subdirectory(Poisson)\nadd_subdirectory(Punctures)\nadd_subdirectory(RadiationTransport)\nadd_subdirectory(RelativisticEuler)\nadd_subdirectory(ScalarAdvection)\nadd_subdirectory(WaveEquation)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Elasticity/BentBeam.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/BentBeam.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Elasticity::Solutions {\nnamespace detail {\n\ntemplate <typename DataType>\nvoid BentBeamVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 2>*> displacement,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::Displacement<2> /*meta*/) const {\n  const double youngs_modulus = constitutive_relation.youngs_modulus();\n  const double poisson_ratio = constitutive_relation.poisson_ratio();\n  const double prefactor =\n      12. * bending_moment / (youngs_modulus * cube(height));\n  get<0>(*displacement) = -prefactor * get<0>(x) * get<1>(x);\n  get<1>(*displacement) =\n      prefactor / 2. *\n      (square(get<0>(x)) + poisson_ratio * square(get<1>(x)) -\n       square(length) / 4.);\n}\n\ntemplate <typename DataType>\nvoid BentBeamVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 2>*> deriv_displacement,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::Displacement<2>, tmpl::size_t<2>,\n                  Frame::Inertial> /*meta*/) const {\n  const double youngs_modulus = constitutive_relation.youngs_modulus();\n  const double poisson_ratio = constitutive_relation.poisson_ratio();\n  const double prefactor =\n      12. * bending_moment / (youngs_modulus * cube(height));\n  get<0, 0>(*deriv_displacement) = -prefactor * get<1>(x);\n  get<1, 0>(*deriv_displacement) = -prefactor * get<0>(x);\n  get<0, 1>(*deriv_displacement) = prefactor * get<0>(x);\n  get<1, 1>(*deriv_displacement) = prefactor * poisson_ratio * get<1>(x);\n}\n\ntemplate <typename DataType>\nvoid BentBeamVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 2>*> strain,\n    const gsl::not_null<Cache*> cache, Tags::Strain<2> /*meta*/) const {\n  const auto& deriv_displacement = cache->get_var(\n      *this,\n      ::Tags::deriv<Tags::Displacement<2>, tmpl::size_t<2>, Frame::Inertial>{});\n  get<0, 0>(*strain) = get<0, 0>(deriv_displacement);\n  get<1, 1>(*strain) = get<1, 1>(deriv_displacement);\n  get<0, 1>(*strain) = 0.;\n}\n\ntemplate <typename DataType>\nvoid BentBeamVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 2>*> minus_stress,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::MinusStress<2> /*meta*/) const {\n  get<0, 0>(*minus_stress) = -12. * bending_moment / cube(height) * get<1>(x);\n  get<1, 1>(*minus_stress) = 0.;\n  get<0, 1>(*minus_stress) = 0.;\n}\n\ntemplate <typename DataType>\nvoid BentBeamVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> potential_energy_density,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::PotentialEnergyDensity<2> /*meta*/) const {\n  get(*potential_energy_density) = 72. * square(bending_moment) /\n                                   constitutive_relation.youngs_modulus() /\n                                   pow<6>(height) * square(get<1>(x));\n}\n\ntemplate <typename DataType>\nvoid BentBeamVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 2>*> fixed_source_for_displacement,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<Tags::Displacement<2>> /*meta*/) const {\n  std::fill(fixed_source_for_displacement->begin(),\n            fixed_source_for_displacement->end(), 0.);\n}\n\n}  // namespace detail\n\nPUP::able::PUP_ID BentBeam::my_PUP_ID = 0;  // NOLINT\n\nbool operator==(const BentBeam& lhs, const BentBeam& rhs) {\n  return lhs.length() == rhs.length() and lhs.height() == rhs.height() and\n         lhs.bending_moment() == rhs.bending_moment() and\n         lhs.constitutive_relation() == rhs.constitutive_relation();\n}\n\nbool operator!=(const BentBeam& lhs, const BentBeam& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) \\\n  template class detail::BentBeamVariables<DTYPE(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace Elasticity::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Elasticity/BentBeam.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity::Solutions {\n\nnamespace detail {\ntemplate <typename DataType>\nstruct BentBeamVariables {\n  using Cache = CachedTempBuffer<\n      Tags::Displacement<2>,\n      ::Tags::deriv<Tags::Displacement<2>, tmpl::size_t<2>, Frame::Inertial>,\n      Tags::Strain<2>, Tags::MinusStress<2>, Tags::PotentialEnergyDensity<2>,\n      ::Tags::FixedSource<Tags::Displacement<2>>>;\n\n  const tnsr::I<DataType, 2>& x;\n  const double length;\n  const double height;\n  const double bending_moment;\n  const ConstitutiveRelations::IsotropicHomogeneous<2>& constitutive_relation;\n\n  void operator()(gsl::not_null<tnsr::I<DataType, 2>*> displacement,\n                  gsl::not_null<Cache*> cache,\n                  Tags::Displacement<2> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::iJ<DataType, 2>*> deriv_displacement,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<Tags::Displacement<2>, tmpl::size_t<2>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::ii<DataType, 2>*> strain,\n                  gsl::not_null<Cache*> cache, Tags::Strain<2> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::II<DataType, 2>*> minus_stress,\n                  gsl::not_null<Cache*> cache,\n                  Tags::MinusStress<2> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> potential_energy_density,\n                  gsl::not_null<Cache*> cache,\n                  Tags::PotentialEnergyDensity<2> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, 2>*> fixed_source_for_displacement,\n      gsl::not_null<Cache*> cache,\n      ::Tags::FixedSource<Tags::Displacement<2>> /*meta*/) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief A state of pure bending of an elastic beam in 2D\n *\n * \\details This solution describes a 2D slice through an elastic beam of length\n * \\f$L\\f$ and height \\f$H\\f$, centered around (0, 0), that is subject to a\n * bending moment \\f$M=\\int T^{xx}y\\mathrm{d}y\\f$ (see e.g.\n * \\cite ThorneBlandford2017, Eq. 11.41c for a bending moment in 1D). The beam\n * material is characterized by an isotropic and homogeneous constitutive\n * relation \\f$Y^{ijkl}\\f$ in the plane-stress approximation (see\n * `Elasticity::ConstitutiveRelations::IsotropicHomogeneous`). In this scenario,\n * no body-forces \\f$f_\\mathrm{ext}^j\\f$ act on the material, so the\n * \\ref Elasticity equations reduce to \\f$\\nabla_i T^{ij}=0\\f$, but the bending\n * moment \\f$M\\f$ generates the stress\n *\n * \\f{align}\n * T^{xx} &= \\frac{12 M}{H^3} y \\\\\n * T^{xy} &= 0 = T^{yy} \\text{.}\n * \\f}\n *\n * By fixing the rigid-body motions to\n *\n * \\f[\n * \\xi^x(0,y)=0 \\quad \\text{and} \\quad \\xi^y\\left(\\pm \\frac{L}{2},0\\right)=0\n * \\f]\n *\n * we find that this stress is produced by the displacement field\n *\n * \\f{align}\n * \\xi^x&=-\\frac{12 M}{EH^3}xy \\\\\n * \\xi^y&=\\frac{6 M}{EH^3}\\left(x^2+\\nu y^2-\\frac{L^2}{4}\\right)\n * \\f}\n *\n * in terms of the Young's modulus \\f$E\\f$ and the Poisson ration \\f$\\nu\\f$ of\n * the material. The corresponding strain \\f$S_{ij}=\\partial_{(i}\\xi_{j)}\\f$ is\n *\n * \\f{align}\n * S_{xx} &= -\\frac{12 M}{EH^3} y \\\\\n * S_{yy} &= \\frac{12 M}{EH^3} \\nu y \\\\\n * S_{xy} &= S_{yx} = 0\n * \\f}\n *\n * and the potential energy stored in the entire infinitesimal slice is\n *\n * \\f[\n * \\int_{-L/2}^{L/2} \\int_{-H/2}^{H/2} U dy\\,dx = \\frac{6M^2}{EH^3}L \\text{.}\n * \\f]\n */\nclass BentBeam : public elliptic::analytic_data::AnalyticSolution {\n public:\n  using constitutive_relation_type =\n      Elasticity::ConstitutiveRelations::IsotropicHomogeneous<2>;\n\n  struct Length {\n    using type = double;\n    static constexpr Options::String help{\"The beam length\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Height {\n    using type = double;\n    static constexpr Options::String help{\"The beam height\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct BendingMoment {\n    using type = double;\n    static constexpr Options::String help{\n        \"The bending moment applied to the beam\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct Material {\n    using type = constitutive_relation_type;\n    static constexpr Options::String help{\n        \"The material properties of the beam\"};\n  };\n\n  using options = tmpl::list<Length, Height, BendingMoment, Material>;\n  static constexpr Options::String help{\n      \"A 2D slice through an elastic beam which is subject to a bending \"\n      \"moment. The bending moment is applied along the length of the beam, \"\n      \"i.e. the x-axis, so that the beam's left and right ends are bent \"\n      \"towards the positive y-axis. It is measured in units of force.\"};\n\n  BentBeam() = default;\n  BentBeam(const BentBeam&) = default;\n  BentBeam& operator=(const BentBeam&) = default;\n  BentBeam(BentBeam&&) = default;\n  BentBeam& operator=(BentBeam&&) = default;\n  ~BentBeam() override = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<BentBeam>(*this);\n  }\n\n  /// \\cond\n  explicit BentBeam(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BentBeam);  // NOLINT\n  /// \\endcond\n\n  BentBeam(double length, double height, double bending_moment,\n           constitutive_relation_type constitutive_relation)\n      : length_(length),\n        height_(height),\n        bending_moment_(bending_moment),\n        constitutive_relation_(std::move(constitutive_relation)) {}\n\n  double length() const { return length_; }\n  double height() const { return height_; }\n  double bending_moment() const { return bending_moment_; }\n\n  const constitutive_relation_type& constitutive_relation() const {\n    return constitutive_relation_;\n  }\n\n  /// Return potential energy integrated over the whole beam material\n  double potential_energy() const {\n    return 6. * length_ * square(bending_moment_) /\n           (cube(height_) * constitutive_relation_.youngs_modulus());\n  }\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 2>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using VarsComputer = detail::BentBeamVariables<DataType>;\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const VarsComputer computer{x, length_, height_, bending_moment_,\n                                constitutive_relation_};\n    return {cache.get_var(computer, RequestedTags{})...};\n  }\n\n  /// NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::AnalyticSolution::pup(p);\n    p | length_;\n    p | height_;\n    p | bending_moment_;\n    p | constitutive_relation_;\n  }\n\n private:\n  double length_{std::numeric_limits<double>::signaling_NaN()};\n  double height_{std::numeric_limits<double>::signaling_NaN()};\n  double bending_moment_{std::numeric_limits<double>::signaling_NaN()};\n  constitutive_relation_type constitutive_relation_{};\n};\n\nbool operator==(const BentBeam& lhs, const BentBeam& rhs);\nbool operator!=(const BentBeam& lhs, const BentBeam& rhs);\n\n}  // namespace Elasticity::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Elasticity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ElasticitySolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BentBeam.cpp\n  HalfSpaceMirror.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BentBeam.hpp\n  Factory.hpp\n  HalfSpaceMirror.hpp\n  Zero.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ConstitutiveRelations\n  DataStructures\n  Elasticity\n  ErrorHandling\n  Options\n  Serialization\n  Utilities\n  PRIVATE\n  ElasticityPointwiseFunctions\n  GSL::gsl\n  Integration\n  INTERFACE\n  AnalyticData\n  Parallel\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Elasticity/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/BentBeam.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/HalfSpaceMirror.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/Zero.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity::Solutions {\ntemplate <size_t Dim>\nusing all_analytic_solutions = tmpl::append<\n    tmpl::list<Zero<Dim>>,\n    tmpl::conditional_t<Dim == 2, tmpl::list<BentBeam>, tmpl::list<>>,\n    tmpl::conditional_t<Dim == 3, tmpl::list<HalfSpaceMirror>, tmpl::list<>>>;\n}  // namespace Elasticity::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Elasticity/HalfSpaceMirror.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/HalfSpaceMirror.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <gsl/gsl_sf_bessel.h>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Integration/GslQuadAdaptive.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"PointwiseFunctions/Elasticity/PotentialEnergy.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Elasticity::Solutions {\nnamespace detail {\n\nnamespace {\ndouble displacement_r_integrand(const double k, const double r, const double z,\n                                const double beam_width,\n                                const double modulus_term_r) {\n  return gsl_sf_bessel_J1(k * r) * exp(-k * z - square(0.5 * k * beam_width)) *\n         (modulus_term_r + k * z);\n}\n}  // namespace\n\ntemplate <typename DataType>\nvoid HalfSpaceMirrorVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> displacement_r,\n    const gsl::not_null<Cache*> /*cache*/, DisplacementR /*meta*/) const {\n  const double shear_modulus = constitutive_relation.shear_modulus();\n  const double lame_parameter = constitutive_relation.lame_parameter();\n  const auto radius = sqrt(square(get<0>(x)) + square(get<1>(x)));\n\n  const integration::GslQuadAdaptive<\n      integration::GslIntegralType::UpperBoundaryInfinite>\n      integration{integration_intervals};\n  const double lower_boundary = 0.;\n  const size_t num_points = get<0>(x).size();\n\n  const double prefactor = 0.25 / (shear_modulus * M_PI);\n  const double modulus_term_r = 1. - (lame_parameter + 2. * shear_modulus) /\n                                         (lame_parameter + shear_modulus);\n  for (size_t i = 0; i < num_points; i++) {\n    const double z = get<2>(x)[i];\n    const double r = radius[i];\n    try {\n      if (not equal_within_roundoff(r, 0.)) {\n        get(*displacement_r)[i] =\n            prefactor *\n            integration(\n                [&r, &z, &modulus_term_r, this](const double k) {\n                  return displacement_r_integrand(k, r, z, beam_width,\n                                                  modulus_term_r);\n                },\n                lower_boundary, absolute_tolerance, relative_tolerance);\n      }\n    } catch (convergence_error& error) {\n      ERROR(\"The numerical integral failed at r=\"\n            << r << \", z=\" << z << \" (\" << error.what()\n            << \"). Try to increase 'IntegrationIntervals' or make the domain \"\n               \"smaller.\\n\");\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid HalfSpaceMirrorVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> displacement,\n    const gsl::not_null<Cache*> cache, Tags::Displacement<3> /*meta*/) const {\n  const double shear_modulus = constitutive_relation.shear_modulus();\n  const double lame_parameter = constitutive_relation.lame_parameter();\n  const auto radius = sqrt(square(get<0>(x)) + square(get<1>(x)));\n  const auto& displacement_r = get(cache->get_var(*this, DisplacementR{}));\n\n  const integration::GslQuadAdaptive<\n      integration::GslIntegralType::UpperBoundaryInfinite>\n      integration{integration_intervals};\n  const double lower_boundary = 0.;\n  const size_t num_points = get<0>(x).size();\n\n  const double prefactor = 0.25 / (shear_modulus * M_PI);\n  const double modulus_term_z =\n      1. + shear_modulus / (lame_parameter + shear_modulus);\n  for (size_t i = 0; i < num_points; i++) {\n    const double z = get<2>(x)[i];\n    const double r = radius[i];\n    try {\n      if (not equal_within_roundoff(r, 0.)) {\n        // projection on cartesian grid\n        get<0>(*displacement)[i] = get<0>(x)[i] / r * displacement_r[i];\n        get<1>(*displacement)[i] = get<1>(x)[i] / r * displacement_r[i];\n      } else {\n        get<0>(*displacement)[i] = 0.;\n        get<1>(*displacement)[i] = 0.;\n      }\n      const double displacement_z =\n          prefactor *\n          integration(\n              [&r, &z, &modulus_term_z, this](const double k) {\n                return gsl_sf_bessel_J0(k * r) *\n                       exp(-k * z - square(0.5 * k * beam_width)) *\n                       (modulus_term_z + k * z);\n              },\n              lower_boundary, absolute_tolerance, relative_tolerance);\n      get<2>(*displacement)[i] = displacement_z;\n    } catch (convergence_error& error) {\n      ERROR(\"The numerical integral failed at r=\"\n            << r << \", z=\" << z << \" (\" << error.what()\n            << \"). Try to increase 'IntegrationIntervals' or make the domain \"\n               \"smaller.\\n\");\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid HalfSpaceMirrorVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> strain,\n    const gsl::not_null<Cache*> cache, Tags::Strain<3> /*meta*/) const {\n  const double shear_modulus = constitutive_relation.shear_modulus();\n  const double lame_parameter = constitutive_relation.lame_parameter();\n  const auto radius = sqrt(square(get<0>(x)) + square(get<1>(x)));\n  const auto& displacement_r = get(cache->get_var(*this, DisplacementR{}));\n\n  const integration::GslQuadAdaptive<\n      integration::GslIntegralType::UpperBoundaryInfinite>\n      integration{integration_intervals};\n  const double lower_boundary = 0.;\n  const size_t num_points = get<0>(x).size();\n\n  const double prefactor = 0.25 / (shear_modulus * M_PI);\n  const double modulus_term_trace =\n      -2. * shear_modulus / (lame_parameter + shear_modulus);\n  const double modulus_term_zz =\n      shear_modulus / (lame_parameter + shear_modulus);\n  for (size_t i = 0; i < num_points; i++) {\n    const double r = radius[i];\n    const double z = get<2>(x)[i];\n    try {\n      const double trace_term =\n          prefactor *\n          integration(\n              [&r, &z, &modulus_term_trace, this](const double k) {\n                return k * gsl_sf_bessel_J0(k * r) *\n                       exp(-k * z - square(0.5 * k * beam_width)) *\n                       modulus_term_trace;\n              },\n              lower_boundary, absolute_tolerance, relative_tolerance);\n\n      const double strain_zz =\n          -prefactor *\n          integration(\n              [&r, &z, &modulus_term_zz, this](const double k) {\n                return k * gsl_sf_bessel_J0(k * r) *\n                       exp(-k * z - square(0.5 * k * beam_width)) *\n                       (modulus_term_zz + k * z);\n              },\n              lower_boundary, absolute_tolerance, relative_tolerance);\n\n      if (not equal_within_roundoff(r, 0.)) {\n        const double strain_rz =\n            -prefactor *\n            integration(\n                [&r, &z, this](const double k) {\n                  return k * gsl_sf_bessel_J1(k * r) *\n                         exp(-k * z - square(0.5 * k * beam_width)) * (k * z);\n                },\n                lower_boundary, absolute_tolerance, relative_tolerance);\n\n        const double cos_phi = get<0>(x)[i] / r;\n        const double sin_phi = get<1>(x)[i] / r;\n        const double strain_pp = displacement_r[i] / r;\n        const double strain_rr = trace_term - strain_pp - strain_zz;\n        get<0, 0>(*strain)[i] =\n            strain_pp + square(cos_phi) * (strain_rr - strain_pp);\n        get<0, 1>(*strain)[i] = cos_phi * sin_phi * (strain_rr - strain_pp);\n        get<0, 2>(*strain)[i] = cos_phi * strain_rz;\n        get<1, 1>(*strain)[i] =\n            strain_pp + square(sin_phi) * (strain_rr - strain_pp);\n        get<1, 2>(*strain)[i] = sin_phi * strain_rz;\n        get<2, 2>(*strain)[i] = strain_zz;\n      } else {\n        get<0, 0>(*strain)[i] = 0.5 * (trace_term - strain_zz);\n        get<0, 1>(*strain)[i] = 0.;\n        get<0, 2>(*strain)[i] = 0.;\n        get<1, 1>(*strain)[i] = get<0, 0>(*strain)[i];\n        get<1, 2>(*strain)[i] = 0.;\n        get<2, 2>(*strain)[i] = strain_zz;\n      }\n    } catch (convergence_error& error) {\n      ERROR(\"The numerical integral failed at r=\"\n            << r << \", z=\" << z << \" (\" << error.what()\n            << \"). Try to increase 'IntegrationIntervals' or make the domain \"\n               \"smaller.\\n\");\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid HalfSpaceMirrorVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3>*> minus_stress,\n    const gsl::not_null<Cache*> cache, Tags::MinusStress<3> /*meta*/) const {\n  const auto& strain = cache->get_var(*this, Tags::Strain<3>{});\n  constitutive_relation.stress(minus_stress, strain, x);\n  for (auto& component : *minus_stress) {\n    component *= -1.;\n  }\n}\n\ntemplate <typename DataType>\nvoid HalfSpaceMirrorVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> potential_energy_density,\n    const gsl::not_null<Cache*> cache,\n    Tags::PotentialEnergyDensity<3> /*meta*/) const {\n  const auto& strain = cache->get_var(*this, Tags::Strain<3>{});\n  Elasticity::potential_energy_density(potential_energy_density, strain, x,\n                                       constitutive_relation);\n}\n\ntemplate <typename DataType>\nvoid HalfSpaceMirrorVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> fixed_source_for_displacement,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<Tags::Displacement<3>> /*meta*/) const {\n  std::fill(fixed_source_for_displacement->begin(),\n            fixed_source_for_displacement->end(), 0.);\n}\n\n}  // namespace detail\n\nPUP::able::PUP_ID HalfSpaceMirror::my_PUP_ID = 0;  // NOLINT\n\nbool operator==(const HalfSpaceMirror& lhs, const HalfSpaceMirror& rhs) {\n  return lhs.beam_width() == rhs.beam_width() and\n         lhs.constitutive_relation() == rhs.constitutive_relation() and\n         lhs.integration_intervals() == rhs.integration_intervals() and\n         lhs.absolute_tolerance() == rhs.absolute_tolerance() and\n         lhs.relative_tolerance() == rhs.relative_tolerance();\n}\n\nbool operator!=(const HalfSpaceMirror& lhs, const HalfSpaceMirror& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) \\\n  template class detail::HalfSpaceMirrorVariables<DTYPE(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace Elasticity::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Elasticity/HalfSpaceMirror.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity::Solutions {\n\nnamespace detail {\ntemplate <typename DataType>\nstruct HalfSpaceMirrorVariables {\n  struct DisplacementR : db::SimpleTag {\n    using type = Scalar<DataType>;\n  };\n  using Cache =\n      CachedTempBuffer<DisplacementR, Tags::Displacement<3>, Tags::Strain<3>,\n                       Tags::MinusStress<3>, Tags::PotentialEnergyDensity<3>,\n                       ::Tags::FixedSource<Tags::Displacement<3>>>;\n\n  const tnsr::I<DataType, 3>& x;\n  const double beam_width;\n  const ConstitutiveRelations::IsotropicHomogeneous<3>& constitutive_relation;\n  const size_t integration_intervals;\n  const double absolute_tolerance;\n  const double relative_tolerance;\n\n  void operator()(gsl::not_null<Scalar<DataType>*> displacement_r,\n                  gsl::not_null<Cache*> cache, DisplacementR /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, 3>*> displacement,\n                  gsl::not_null<Cache*> cache,\n                  Tags::Displacement<3> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::ii<DataType, 3>*> strain,\n                  gsl::not_null<Cache*> cache, Tags::Strain<3> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::II<DataType, 3>*> minus_stress,\n                  gsl::not_null<Cache*> cache,\n                  Tags::MinusStress<3> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> potential_energy_density,\n                  gsl::not_null<Cache*> cache,\n                  Tags::PotentialEnergyDensity<3> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, 3>*> fixed_source_for_displacement,\n      gsl::not_null<Cache*> cache,\n      ::Tags::FixedSource<Tags::Displacement<3>> /*meta*/) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief The solution for a half-space mirror deformed by a laser beam.\n *\n * \\details This solution is mapping (via the fluctuation dissipation theorem)\n * thermal noise to an elasticity problem where a normally incident and\n * axisymmetric laser beam with a Gaussian beam profile acts on the face of a\n * semi-infinite mirror. Here we assume the face to be at \\f$z = 0\\f$ and the\n * material to extend to \\f$+\\infty\\f$ in the z-direction as well as for the\n * mirror diameter to be comparatively large to the `beam width`. The mirror\n * material is characterized by an isotropic homogeneous constitutive relation\n * \\f$Y^{ijkl}\\f$ (see\n * `Elasticity::ConstitutiveRelations::IsotropicHomogeneous`). In this scenario,\n * the auxiliary elastic problem has an applied pressure distribution equal to\n * the laser beam intensity profile \\f$p(r)\\f$ (see Eq. (11.94) and Eq. (11.95)\n * in \\cite ThorneBlandford2017 with F = 1 and the time dependency dropped)\n *\n * \\f{align}\n * T^{zr} &= T^{rz} = 0 \\\\\n * T^{zz} &= p(r) = \\frac{e^{-\\frac{r^2}{r_0^2}}}{\\pi r_0^2}\\text{.}\n * \\f}\n *\n * in the form of a Neumann boundary condition to the face of the mirror. We\n * find that this stress in cylinder coordinates is produced by the displacement\n * field\n *\n * \\f{align}\n * \\xi_{r} &= \\frac{1}{2 \\mu} \\int_0^{\\infty} dk J_1(kr)e^{(-kz)}\\left(1 -\n * \\frac{\\lambda + 2\\mu}{\\lambda + \\mu} + kz \\right) \\tilde{p}(k) \\\\\n * \\xi_{\\phi} &= 0 \\\\\n * \\xi_{z} &=  \\frac{1}{2 \\mu} \\int_0^{\\infty} dk J_0(kr)e^{(-kz)}\\left(1 +\n * \\frac{\\mu}{\\lambda + \\mu} + kz \\right) \\tilde{p}(k)\n * \\f}\n *\n * and the strain\n *\n * \\f{align}\n * \\Theta &= \\frac{1}{2 \\mu} \\int_0^{\\infty} dk\n * J_0(kr) k e^{(-kz)}\\left(\\frac{-2\\mu}{\\lambda + \\mu}\\right) \\tilde{p}(k) \\\\\n * S_{rr} &= \\Theta - S_{\\phi\\phi} - S_{zz} \\\\\n * S_{\\phi\\phi} &= \\frac{\\xi_{r}}{r} \\\\\n * S_{(rz)} &= -\\frac{1}{2 \\mu} \\int_0^{\\infty} dk J_1(kr) k e^{(-kz)}\\left(kz\n * \\right) \\tilde{p}(k) \\\\\n * S_{zz} &= \\frac{1}{2 \\mu} \\int_0^{\\infty} dk\n * J_0(kr) k e^{(-kz)}\\left(-\\frac{\\mu}{\\lambda + \\mu} - kz \\right) \\tilde{p}(k)\n * \\f}\n *\n * (see Eqs. (11 a) - (11 c) and (13 a) - (13 e), with (13 c) swapped in favor\n * of (12 c) in \\cite Lovelace2007tn), where \\f$\\tilde{p}(k)= \\frac{1}{2\\pi}\n * e^{-(\\frac{kr_0}{2})^2}\\f$ is the Hankel-Transform of the lasers intensity\n * profile and \\f$ \\Theta = \\mathrm{Tr}(S)\\f$ the materials expansion.\n *\n */\nclass HalfSpaceMirror : public elliptic::analytic_data::AnalyticSolution {\n public:\n  using constitutive_relation_type =\n      Elasticity::ConstitutiveRelations::IsotropicHomogeneous<3>;\n\n  struct BeamWidth {\n    using type = double;\n    static constexpr Options::String help{\n        \"The lasers beam width r_0 with FWHM = 2*sqrt(ln 2)*r_0\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Material {\n    using type = constitutive_relation_type;\n    static constexpr Options::String help{\n        \"The material properties of the beam\"};\n  };\n\n  struct IntegrationIntervals {\n    using type = size_t;\n    static constexpr Options::String help{\n        \"Workspace size for numerical integrals. Increase if integrals fail to \"\n        \"reach the prescribed tolerance at large distances relative to the \"\n        \"beam width. The suggested values for workspace size and tolerances \"\n        \"should accommodate distances of up to ~100 beam widths.\"};\n    static type lower_bound() { return 1; }\n    static type suggested_value() { return 350; }\n  };\n\n  struct AbsoluteTolerance {\n    using type = double;\n    static constexpr Options::String help{\n        \"Absolute tolerance for numerical integrals\"};\n    static type lower_bound() { return 0.; }\n    static type suggested_value() { return 1e-12; }\n  };\n\n  struct RelativeTolerance {\n    using type = double;\n    static constexpr Options::String help{\n        \"Relative tolerance for numerical integrals\"};\n    static type lower_bound() { return 0.; }\n    static type upper_bound() { return 1.; }\n    static type suggested_value() { return 1e-10; }\n  };\n\n  using options = tmpl::list<BeamWidth, Material, IntegrationIntervals,\n                             AbsoluteTolerance, RelativeTolerance>;\n  static constexpr Options::String help{\n      \"A semi-infinite mirror on which a laser introduces stress perpendicular \"\n      \"to the mirrors surface.\"};\n\n  HalfSpaceMirror() = default;\n  HalfSpaceMirror(const HalfSpaceMirror&) = default;\n  HalfSpaceMirror& operator=(const HalfSpaceMirror&) = default;\n  HalfSpaceMirror(HalfSpaceMirror&&) = default;\n  HalfSpaceMirror& operator=(HalfSpaceMirror&&) = default;\n  ~HalfSpaceMirror() override = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<HalfSpaceMirror>(*this);\n  }\n\n  /// \\cond\n  explicit HalfSpaceMirror(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(HalfSpaceMirror);  // NOLINT\n  /// \\endcond\n\n  HalfSpaceMirror(double beam_width,\n                  constitutive_relation_type constitutive_relation,\n                  size_t integration_intervals = 350,\n                  double absolute_tolerance = 1e-12,\n                  double relative_tolerance = 1e-10)\n      : beam_width_(beam_width),\n        constitutive_relation_(std::move(constitutive_relation)),\n        integration_intervals_(integration_intervals),\n        absolute_tolerance_(absolute_tolerance),\n        relative_tolerance_(relative_tolerance) {}\n\n  double beam_width() const { return beam_width_; }\n  size_t integration_intervals() const { return integration_intervals_; }\n  double absolute_tolerance() const { return absolute_tolerance_; }\n  double relative_tolerance() const { return relative_tolerance_; }\n\n  const constitutive_relation_type& constitutive_relation() const {\n    return constitutive_relation_;\n  }\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    for (size_t i = 0; i < get_size(get<2>(x)); i++) {\n      if (UNLIKELY(get_element(get<2>(x), i) < 0)) {\n        ERROR(\n            \"The HalfSpaceMirror solution is not defined for negative values \"\n            \"of z.\");\n      }\n    }\n    using VarsComputer = detail::HalfSpaceMirrorVariables<DataType>;\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const VarsComputer computer{x,\n                                beam_width_,\n                                constitutive_relation_,\n                                integration_intervals_,\n                                absolute_tolerance_,\n                                relative_tolerance_};\n    return {cache.get_var(computer, RequestedTags{})...};\n  }\n\n  /// NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::AnalyticSolution::pup(p);\n    p | beam_width_;\n    p | constitutive_relation_;\n    p | integration_intervals_;\n    p | absolute_tolerance_;\n    p | relative_tolerance_;\n  }\n\n private:\n  double beam_width_{std::numeric_limits<double>::signaling_NaN()};\n  constitutive_relation_type constitutive_relation_{};\n  size_t integration_intervals_{std::numeric_limits<size_t>::max()};\n  double absolute_tolerance_{std::numeric_limits<double>::signaling_NaN()};\n  double relative_tolerance_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator==(const HalfSpaceMirror& lhs, const HalfSpaceMirror& rhs);\nbool operator!=(const HalfSpaceMirror& lhs, const HalfSpaceMirror& rhs);\n\n}  // namespace Elasticity::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Elasticity/Zero.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity::Solutions {\n\n/*!\n * \\brief The trivial solution \\f$\\xi^i(x)=0\\f$ of the Elasticity equations.\n * Useful as initial guess.\n */\ntemplate <size_t Dim>\nclass Zero : public elliptic::analytic_data::AnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"The trivial solution, useful as initial guess.\"};\n\n  Zero() = default;\n  Zero(const Zero&) = default;\n  Zero& operator=(const Zero&) = default;\n  Zero(Zero&&) = default;\n  Zero& operator=(Zero&&) = default;\n  ~Zero() override = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<Zero>(*this);\n  }\n\n  /// \\cond\n  explicit Zero(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Zero);  // NOLINT\n  /// \\endcond\n\n  /// Retrieve a collection of variables at coordinates `x`\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using supported_tags =\n        tmpl::list<Tags::Displacement<Dim>,\n                   ::Tags::deriv<Tags::Displacement<Dim>, tmpl::size_t<Dim>,\n                                 Frame::Inertial>,\n                   Tags::Strain<Dim>, Tags::MinusStress<Dim>,\n                   Tags::PotentialEnergyDensity<Dim>,\n                   ::Tags::FixedSource<Tags::Displacement<Dim>>>;\n    static_assert(tmpl::size<tmpl::list_difference<tmpl::list<RequestedTags...>,\n                                                   supported_tags>>::value == 0,\n                  \"The requested tag is not supported\");\n    return {make_with_value<typename RequestedTags::type>(x, 0.)...};\n  }\n};\n\n/// \\cond\ntemplate <size_t Dim>\nPUP::able::PUP_ID Zero<Dim>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\ntemplate <size_t Dim>\nbool operator==(const Zero<Dim>& /*lhs*/, const Zero<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const Zero<Dim>& /*lhs*/, const Zero<Dim>& /*rhs*/) {\n  return false;\n}\n}  // namespace Elasticity::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/AlfvenWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/AlfvenWave.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ForceFree::Solutions {\n\nAlfvenWave::AlfvenWave(const double wave_speed, const Options::Context& context)\n    : wave_speed_(wave_speed) {\n  if (abs(wave_speed_) >= 1.0) {\n    PARSE_ERROR(context,\n                \"The wave speed (\"\n                    << wave_speed_\n                    << \") must be bigger than -1.0 and smaller than 1.0\");\n  }\n  lorentz_factor_ = 1.0 / sqrt(1.0 - square(wave_speed_));\n}\n\nAlfvenWave::AlfvenWave(CkMigrateMessage* msg) : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> AlfvenWave::get_clone()\n    const {\n  return std::make_unique<AlfvenWave>(*this);\n}\n\nvoid AlfvenWave::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | wave_speed_;\n  p | lorentz_factor_;\n  p | background_spacetime_;\n}\n\nPUP::able::PUP_ID AlfvenWave::my_PUP_ID = 0;\n\nDataVector AlfvenWave::wave_profile(const DataVector& x_prime) {\n  // Compute the B_z'(=-E_x') at the rest frame of the wave\n  auto result = make_with_value<DataVector>(x_prime, 1.0);\n  for (size_t i = 0; i < result.size(); ++i) {\n    const double& xi = x_prime[i];\n    if (xi > -0.1) {\n      if (xi <= 0.1) {\n        result[i] = 1.15 + 0.15 * sin(5 * M_PI * xi);\n      } else {\n        result[i] = 1.3;\n      }\n    }\n  }\n  return result;\n}\n\nDataVector AlfvenWave::charge_density(const DataVector& x_prime) {\n  // Compute the charge density q = Div(E) at the rest frame of the wave\n  auto result = make_with_value<DataVector>(x_prime, 0.0);\n  for (size_t i = 0; i < result.size(); ++i) {\n    const double& xi = x_prime[i];\n    if ((xi > -0.1) and (xi <= 0.1)) {\n      result[i] = -0.75 * M_PI * cos(5.0 * M_PI * xi);\n    }\n  }\n  return result;\n}\n\ntuples::TaggedTuple<Tags::TildeE> AlfvenWave::variables(\n    const tnsr::I<DataVector, 3>& x, double t,\n    tmpl::list<Tags::TildeE> /*meta*/) const {\n  const auto& x_coord = get<0>(x);\n  auto electric_field = make_with_value<tnsr::I<DataVector, 3>>(x, 0.0);\n\n  // E_x\n  get<0>(electric_field) =\n      -wave_profile(lorentz_factor_ * (x_coord - wave_speed_ * t));\n  // E_y\n  get<1>(electric_field) =\n      -lorentz_factor_ * wave_speed_ * get<0>(electric_field);\n  // E_z\n  get<2>(electric_field) = lorentz_factor_ * (1.0 - wave_speed_);\n\n  return electric_field;\n}\n\ntuples::TaggedTuple<Tags::TildeB> AlfvenWave::variables(\n    const tnsr::I<DataVector, 3>& x, double t,\n    tmpl::list<Tags::TildeB> /*meta*/) const {\n  const auto& x_coord = get<0>(x);\n  auto magnetic_field = make_with_value<tnsr::I<DataVector, 3>>(x, 1.0);\n  // note) B_x = 1.0\n  // B_y\n  get<1>(magnetic_field) = lorentz_factor_ * (1.0 - wave_speed_);\n  // B_z\n  get<2>(magnetic_field) =\n      lorentz_factor_ *\n      wave_profile(lorentz_factor_ * (x_coord - wave_speed_ * t));\n\n  return magnetic_field;\n}\n\ntuples::TaggedTuple<Tags::TildePsi> AlfvenWave::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildePsi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildePhi> AlfvenWave::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildePhi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildeQ> AlfvenWave::variables(\n    const tnsr::I<DataVector, 3>& x, double t,\n    tmpl::list<Tags::TildeQ> /*meta*/) const {\n  return {Scalar<DataVector>{\n      lorentz_factor_ *\n      charge_density(lorentz_factor_ * (get<0>(x) - wave_speed_ * t))}};\n}\n\nbool operator==(const AlfvenWave& lhs, const AlfvenWave& rhs) {\n  return lhs.wave_speed_ == rhs.wave_speed_ and\n         lhs.lorentz_factor_ == rhs.lorentz_factor_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const AlfvenWave& lhs, const AlfvenWave& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/AlfvenWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::Solutions {\n\n/*!\n * \\brief Alfven wave propagating along \\f$x\\f$ direction in flat spacetime with\n * the wave speed \\f$\\mu\\f$.\n *\n * This test problem was introduced in \\cite Komissarov2004 with $\\mu=0$.\n *\n * In the wave frame (with prime superscript), the stationary solution is given\n * by\n *\n * \\f{align*}\n *  B'_x & = B'_y = 1.0 ,\\\\\n *  B'_z(x') & = \\left\\{\\begin{array}{ll}\n *      1.0         & \\text{if } x' < -0.1   \\\\\n *      1.15 + 0.15 \\sin (5\\pi x') & \\text{if } -0.1 < x' < 0.1 \\\\\n *      1.3         & \\text{if } x' > 0.1 \\end{array}\\right\\} ,\\\\\n *      E'_x & = -B'_z ,\\\\\n *      E'_y & = 0 \\\\\n *      E'_z & = 1.0\n * \\f}\n *\n * and\n *\n * \\f{align*}\n *  q'   & = \\left\\{\\begin{array}{ll}\n *      - 0.75 \\pi \\cos (5 \\pi x')  & \\text{if } -0.1 < x' < 0.1 \\\\\n *      0         & \\text{otherwise}    \\end{array}\\right\\} , \\\\\n *  J_x' & = 0 , \\\\\n *  J_y' & = \\left\\{\\begin{array}{ll}\n *      - 0.75 \\pi \\cos (5 \\pi x')  & \\text{if } -0.1 < x' < 0.1 \\\\\n *      0         & \\text{otherwise} \\end{array}\\right\\} , \\\\\n *  J_z' & = 0 .\n * \\f}\n *\n * Applying the Lorentz transformation, electromagnetic fields and 4-current in\n * the grid frame at \\f$t=0\\f$ are given by\n *\n * \\f{align*}\n *  E_x(x) & = E'_x(\\gamma x) , \\\\\n *  E_y(x) & = \\gamma[E'_y(\\gamma x) + \\mu B'_z(\\gamma x)] , \\\\\n *  E_z(x) & = \\gamma[E'_z(\\gamma x) - \\mu B'_y(\\gamma x)] , \\\\\n *  B_x(x) & = B'_x(\\gamma x), \\\\\n *  B_y(x) & = \\gamma[ B'_y(\\gamma x) - \\mu E'_z(\\gamma x) ] , \\\\\n *  B_z(x) & = \\gamma[ B'_z(\\gamma x) + \\mu E'_y(\\gamma x) ] .\n * \\f}\n *\n * and\n *\n * \\f{align*}\n *  q(x)    & = \\gamma q'(\\gamma x) , \\\\\n *  J_x(x)  & = \\gamma \\mu q'(\\gamma x) , \\\\\n *  J_y(x)  & = J_y'(\\gamma x) , \\\\\n *  J_z(x)  & = 0 .\n * \\f}\n *\n * The wave speed can be chosen any value $-1 < \\mu < 1$, and the solution at\n * time $t$ is $f(x,t) = f(x-\\mu t, 0)$ for any physical quantities.\n *\n */\nclass AlfvenWave : public evolution::initial_data::InitialData,\n                   public MarkAsAnalyticSolution {\n public:\n  /// The wave speed\n  struct WaveSpeed {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The wave speed along x direction\"};\n    static type lower_bound() { return -1.0; }\n    static type upper_bound() { return 1.0; }\n  };\n\n  using options = tmpl::list<WaveSpeed>;\n\n  static constexpr Options::String help{\n      \"Alfven wave propagating along x direction in flat spacetime with the \"\n      \"wave speed mu\"};\n\n  AlfvenWave() = default;\n  AlfvenWave(const AlfvenWave&) = default;\n  AlfvenWave& operator=(const AlfvenWave&) = default;\n  AlfvenWave(AlfvenWave&&) = default;\n  AlfvenWave& operator=(AlfvenWave&&) = default;\n  ~AlfvenWave() override = default;\n\n  AlfvenWave(double wave_speed, const Options::Context& context = {});\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit AlfvenWave(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(AlfvenWave);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// @{\n  /// Retrieve the EM variables at (x,t).\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<Tags::TildeE> /*meta*/) const\n      -> tuples::TaggedTuple<Tags::TildeE>;\n\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<Tags::TildeB> /*meta*/) const\n      -> tuples::TaggedTuple<Tags::TildeB>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildePsi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePsi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildePhi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePhi>;\n\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<Tags::TildeQ> /*meta*/) const\n      -> tuples::TaggedTuple<Tags::TildeQ>;\n  /// @}\n\n  /// Retrieve a collection of EM variables at `(x, t)`\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 3>& x,\n                                         const double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, 3>& x, double t,\n                                     tmpl::list<Tag> /*meta*/) const {\n    return background_spacetime_.variables(x, t, tmpl::list<Tag>{});\n  }\n\n private:\n  static DataVector wave_profile(const DataVector& x_prime);\n  static DataVector charge_density(const DataVector& x_prime);\n  double wave_speed_ = std::numeric_limits<double>::signaling_NaN();\n  double lorentz_factor_ = std::numeric_limits<double>::signaling_NaN();\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  friend bool operator==(const AlfvenWave& lhs, const AlfvenWave& rhs);\n};\n\nbool operator!=(const AlfvenWave& lhs, const AlfvenWave& rhs);\n\n}  // namespace ForceFree::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ForceFreeSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AlfvenWave.cpp\n  ExactWald.cpp\n  FastWave.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AlfvenWave.hpp\n  ExactWald.hpp\n  Factory.hpp\n  FastWave.hpp\n  Solutions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  Options\n  Serialization\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/ExactWald.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/ExactWald.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ForceFree::Solutions {\n\nExactWald::ExactWald(const double magnetic_field_amplitude)\n    : magnetic_field_amplitude_(magnetic_field_amplitude) {}\n\nExactWald::ExactWald(CkMigrateMessage* msg) : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> ExactWald::get_clone()\n    const {\n  return std::make_unique<ExactWald>(*this);\n}\n\nvoid ExactWald::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | background_spacetime_;\n  p | magnetic_field_amplitude_;\n}\n\n// NOLINTNEXTLINE\nPUP::able::PUP_ID ExactWald::my_PUP_ID = 0;\n\ntuples::TaggedTuple<Tags::TildeE> ExactWald::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildeE> /*meta*/) const {\n  auto result = make_with_value<tnsr::I<DataVector, 3>>(x, 0.0);\n\n  DataVector r_squared = get(dot_product(x, x));\n\n  get<0>(result) = -2.0 * magnetic_field_amplitude_ * get<1>(x) / r_squared;\n  get<1>(result) = 2.0 * magnetic_field_amplitude_ * get<0>(x) / r_squared;\n\n  return result;\n}\n\ntuples::TaggedTuple<Tags::TildeB> ExactWald::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildeB> /*meta*/) const {\n  auto tilde_b =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(x, 0.0);\n  get<2>(tilde_b) = magnetic_field_amplitude_;\n\n  return tilde_b;\n}\n\ntuples::TaggedTuple<Tags::TildePsi> ExactWald::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildePsi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildePhi> ExactWald::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildePhi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildeQ> ExactWald::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildeQ> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\nbool operator==(const ExactWald& lhs, const ExactWald& rhs) {\n  return lhs.magnetic_field_amplitude_ == rhs.magnetic_field_amplitude_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const ExactWald& lhs, const ExactWald& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/ExactWald.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrSchildCoords.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::Solutions {\n\n/*!\n * \\brief An exact electrovacuum force-free solution of Maxwell's equations in\n * the Schwarzschild spacetime by Wald \\cite Wald1974.\n *\n * The solution is given in terms of the electromagnetic 4-potential\n *\n * \\begin{equation}\n *  A_\\mu = \\frac{B_0}{2}(\\phi_\\mu + 2a t_\\mu)\n * \\end{equation}\n *\n * where $B_0$ is the vector potential amplitude, $\\phi^\\mu = \\partial_\\phi$,\n * $t^\\mu = \\partial_t$, and $a$ is the (dimensionless) spin of the black hole.\n * The case $a=0$ is force-free outside the horizon.\n *\n * \\note This solution is not force-free inside the horizon; the condition\n * $E_iB^i = 0$ is still satisfied, but $B^2 > E^2$ is not.\n *\n * In the spherical Kerr-Schild coordinates, the only nonzero component of\n * vector potential is\n *\n * \\begin{equation}\n *  A_\\phi = \\frac{B_0}{2}r^2 \\sin^2 \\theta.\n * \\end{equation}\n *\n * Computing magnetic fields,\n * \\begin{align}\n *   \\tilde{B}^r  & = \\partial_\\theta A_\\phi = B_0 r^2 \\sin\\theta\\cos\\theta \\\\\n *   \\tilde{B}^\\theta & = - \\partial_r A_\\phi = -B_0 r \\sin^2 \\theta \\\\\n *   \\tilde{B}^\\phi   &= 0 ,\n * \\end{align}\n *\n * Transformation to the Cartesian coordinates gives\n * \\begin{equation}\n *  \\tilde{B}^x = 0 , \\quad\n *  \\tilde{B}^y = 0 , \\quad\n *  \\tilde{B}^z = B_0 .\n * \\end{equation}\n *\n * Electric fields are given by\n *\n * \\begin{equation}\n *  E_i = F_{ia}n^a = \\frac{1}{\\alpha}(F_{i0} - F_{ij}\\beta^j) .\n * \\end{equation}\n *\n * We omit the derivation and write out results below:\n *  \\begin{equation}\n *    \\tilde{E}^x = - \\frac{2 M B_0 y}{r^2}, \\quad\n *    \\tilde{E}^y =   \\frac{2 M B_0 x}{r^2}, \\quad\n *    \\tilde{E}^z = 0\n *  \\end{equation}\n *\n * Note that $\\tilde{B}^i \\equiv \\sqrt{\\gamma}B^i$, $\\tilde{E}^i \\equiv\n * \\sqrt{\\gamma}E^i$, and $\\gamma = 1 + 2M/r$ in the (Cartesian) Kerr-Schild\n * coordinates.  We use $M=1$ Schwarzschild black hole in the Kerr-Schild\n * coordinates (see the documentation of gr::Solutions::KerrSchild).\n *\n */\nclass ExactWald : public evolution::initial_data::InitialData,\n                  public MarkAsAnalyticSolution {\n public:\n  struct MagneticFieldAmplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Amplitude of magnetic field along z axis.\"};\n  };\n\n  using options = tmpl::list<MagneticFieldAmplitude>;\n\n  static constexpr Options::String help{\n      \"Exact vacuum solution of Maxwell's equations in Schwarzschild BH \"\n      \"spacetime by Wald (1974).\"};\n\n  ExactWald() = default;\n  ExactWald(const ExactWald&) = default;\n  ExactWald& operator=(const ExactWald&) = default;\n  ExactWald(ExactWald&&) = default;\n  ExactWald& operator=(ExactWald&&) = default;\n  ~ExactWald() override = default;\n\n  explicit ExactWald(double magnetic_field_amplitude);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit ExactWald(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ExactWald);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// @{\n  /// Retrieve the EM variables at (x,t).\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<Tags::TildeE> /*meta*/) const\n      -> tuples::TaggedTuple<Tags::TildeE>;\n\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<Tags::TildeB> /*meta*/) const\n      -> tuples::TaggedTuple<Tags::TildeB>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildePsi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePsi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildePhi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePhi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildeQ> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeQ>;\n  /// @}\n\n  /// Retrieve a collection of EM variables at `(x, t)`\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 3>& x,\n                                         const double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, 3>& x, double t,\n                                     tmpl::list<Tag> /*meta*/) const {\n    return background_spacetime_.variables(x, t, tmpl::list<Tag>{});\n  }\n\n private:\n  gr::Solutions::KerrSchild background_spacetime_{\n      1.0, {{0.0, 0.0, 0.0}}, {{0.0, 0.0, 0.0}}};\n  double magnetic_field_amplitude_ =\n      std::numeric_limits<double>::signaling_NaN();\n\n  friend bool operator==(const ExactWald& lhs, const ExactWald& rhs);\n};\n\nbool operator!=(const ExactWald& lhs, const ExactWald& rhs);\n\n}  // namespace ForceFree::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/AlfvenWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/ExactWald.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/FastWave.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::Solutions {\n/*!\n * \\brief Typelist of all analytic solutions of GRFFE evolution system\n */\nusing all_solutions = tmpl::list<AlfvenWave, ExactWald, FastWave>;\n}  // namespace ForceFree::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/FastWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/FastWave.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ForceFree::Solutions {\n\nFastWave::FastWave(CkMigrateMessage* msg) : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> FastWave::get_clone()\n    const {\n  return std::make_unique<FastWave>(*this);\n}\n\nvoid FastWave::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | background_spacetime_;\n}\n\nPUP::able::PUP_ID FastWave::my_PUP_ID = 0;\n\nDataVector FastWave::initial_profile(const DataVector& coords) {\n  // Compute the initial functional form of B_y(=E_z)\n  auto result = make_with_value<DataVector>(coords, 1.0);\n  for (size_t i = 0; i < result.size(); ++i) {\n    const double& x = coords[i];\n    if (x > -0.1) {\n      if (x <= 0.1) {\n        result[i] = -1.5 * x + 0.85;\n      } else {\n        result[i] = 0.7;\n      }\n    }\n  }\n  return result;\n}\n\ntuples::TaggedTuple<Tags::TildeE> FastWave::variables(\n    const tnsr::I<DataVector, 3>& x, double t,\n    tmpl::list<Tags::TildeE> /*meta*/) {\n  auto result = make_with_value<tnsr::I<DataVector, 3>>(x, 0.0);\n  // E_z = -B_y\n  result.get(2) = -initial_profile(x.get(0) - t);\n  return result;\n}\n\ntuples::TaggedTuple<Tags::TildeB> FastWave::variables(\n    const tnsr::I<DataVector, 3>& x, double t,\n    tmpl::list<Tags::TildeB> /*meta*/) {\n  auto result = make_with_value<tnsr::I<DataVector, 3>>(x, 0.0);\n  result.get(0) = 1.0;  // B_x = 1.0\n  result.get(1) = initial_profile(x.get(0) - t);\n  return result;\n}\n\ntuples::TaggedTuple<Tags::TildePsi> FastWave::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildePsi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildePhi> FastWave::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildePhi> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\ntuples::TaggedTuple<Tags::TildeQ> FastWave::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<Tags::TildeQ> /*meta*/) {\n  return {make_with_value<Scalar<DataVector>>(x, 0.0)};\n}\n\nbool operator==(const FastWave& lhs, const FastWave& rhs) {\n  return lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const FastWave& lhs, const FastWave& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ForceFree::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/FastWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ForceFree::Solutions {\n\n/*!\n * \\brief An electromagnetic wave propagating into \\f$+x\\f$ direction in flat\n * spacetime.\n *\n * The initial data is given by \\cite Komissarov2002\n *\n * \\f{align*}{\n *  B^x & = 1.0 , \\\\\n *  B^y & = \\left\\{\\begin{array}{ll}\n *  1.0          & \\text{if } x < -0.1 \\\\\n *  -1.5x + 0.85 & \\text{if } -0.1 \\leq x \\leq 0.1 \\\\\n *  0.7          & \\text{if } x > 0.1 \\\\\n * \\end{array}\\right\\}, \\\\\n *  B^z & = 0 , \\\\\n *  E^x & = E^y = 0 , \\\\\n *  E^z & = -B^y .\n * \\f}\n *\n * The electric and magnetic fields are advected to \\f$+x\\f$ direction with the\n * speed of light (\\f$\\lambda=+1\\f$), and the solution at \\f$(\\vec{x},t)\\f$ is\n *\n * \\f{align*}{\n *  E^i(x,y,z,t) & = E^i(x-t,y,z,0) , \\\\\n *  B^i(x,y,z,t) & = B^i(x-t,y,z,0) .\n * \\f}\n *\n * Electric charge density \\f$q\\f$ is zero everywhere.\n *\n */\nclass FastWave : public evolution::initial_data::InitialData,\n                 public MarkAsAnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A fast mode wave propagating +x direction in flat spacetime\"};\n\n  FastWave() = default;\n  FastWave(const FastWave&) = default;\n  FastWave& operator=(const FastWave&) = default;\n  FastWave(FastWave&&) = default;\n  FastWave& operator=(FastWave&&) = default;\n  ~FastWave() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit FastWave(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(FastWave);\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// @{\n  /// Retrieve the EM variables at (x,t).\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildeE> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeE>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildeB> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeB>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildePsi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePsi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildePhi> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildePhi>;\n\n  static auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                        tmpl::list<Tags::TildeQ> /*meta*/)\n      -> tuples::TaggedTuple<Tags::TildeQ>;\n  /// @}\n\n  /// Retrieve a collection of EM variables at `(x, t)`\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 3>& x,\n                                         const double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, 3>& x, double t,\n                                     tmpl::list<Tag> /*meta*/) const {\n    return background_spacetime_.variables(x, t, tmpl::list<Tag>{});\n  }\n\n private:\n  // Computes the initial profile\n  static DataVector initial_profile(const DataVector& coords);\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  friend bool operator==(const FastWave& lhs, const FastWave& rhs);\n  friend bool operator!=(const FastWave& lhs, const FastWave& rhs);\n};\n\n}  // namespace ForceFree::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ForceFree/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ForceFree {\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing a solution to the GRFFE system.\n *\n */\nnamespace Solutions {}\n}  // namespace ForceFree\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GeneralRelativitySolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  GaugeWave.cpp\n  GaugePlaneWave.cpp\n  HarmonicSchwarzschild.cpp\n  KerrSchild.cpp\n  Minkowski.cpp\n  SphericalKerrSchild.cpp\n  TeukolskyWave.cpp\n  TrumpetSchwarzschild.cpp\n  WrappedGr.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  GaugeWave.hpp\n  GaugePlaneWave.hpp\n  HarmonicSchwarzschild.hpp\n  KerrSchild.hpp\n  Minkowski.hpp\n  Solutions.hpp\n  SphericalKerrSchild.hpp\n  TeukolskyWave.hpp\n  TrumpetSchwarzschild.hpp\n  WrappedGr.hpp\n  WrappedGr.tpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  GeneralRelativity\n  Interpolation\n  Serialization\n  SpecialRelativity\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::Solutions {\n/// \\brief List of all GH analytic solutions\ntemplate <size_t Dim>\nusing all_solutions =\n    tmpl::append<tmpl::list<WrappedGr<gr::Solutions::GaugePlaneWave<Dim>>,\n                            WrappedGr<gr::Solutions::GaugeWave<Dim>>,\n                            WrappedGr<gr::Solutions::Minkowski<Dim>>>,\n                 tmpl::conditional_t<\n                     Dim == 3,\n                     tmpl::list<WrappedGr<gr::Solutions::HarmonicSchwarzschild>,\n                                WrappedGr<gr::Solutions::KerrSchild>,\n                                WrappedGr<gr::Solutions::SphericalKerrSchild>,\n                                WrappedGr<gr::Solutions::TrumpetSchwarzschild>>,\n                     tmpl::list<>>>;\n}  // namespace gh::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gr::Solutions {\ntemplate <size_t Dim>\nGaugePlaneWave<Dim>::GaugePlaneWave(CkMigrateMessage* /*msg*/) {}\n\ntemplate <size_t Dim>\nGaugePlaneWave<Dim>::GaugePlaneWave(\n    const std::array<double, Dim>& wave_vector,\n    std::unique_ptr<MathFunction<1, Frame::Inertial>> profile)\n    : wave_vector_(wave_vector),\n      profile_(std::move(profile)),\n      omega_(magnitude(wave_vector_)) {}\n\ntemplate <size_t Dim>\nGaugePlaneWave<Dim>::GaugePlaneWave(const GaugePlaneWave& other)\n    : wave_vector_(other.wave_vector_),\n      profile_(other.profile_->get_clone()),\n      omega_(magnitude(wave_vector_)) {}\n\ntemplate <size_t Dim>\nGaugePlaneWave<Dim>& GaugePlaneWave<Dim>::operator=(\n    const GaugePlaneWave& other) {\n  wave_vector_ = other.wave_vector_;\n  omega_ = magnitude(wave_vector_);\n  profile_ = other.profile_->get_clone();\n  return *this;\n}\n\ntemplate <size_t Dim>\nvoid GaugePlaneWave<Dim>::pup(PUP::er& p) {\n  p | wave_vector_;\n  p | profile_;\n  p | omega_;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nGaugePlaneWave<Dim>::IntermediateVars<DataType>::IntermediateVars(\n    const std::array<double, Dim>& wave_vector,\n    const std::unique_ptr<MathFunction<1, Frame::Inertial>>& profile,\n    double omega, const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n    double t) {\n  auto u = make_with_value<DataType>(x, -omega * t);\n  for (size_t d = 0; d < Dim; ++d) {\n    u += gsl::at(wave_vector, d) * x.get(d);\n  }\n  h = profile->operator()(u);\n  du_h = profile->first_deriv(u);\n  du_du_h = profile->second_deriv(u);\n  det_gamma = 1.0 + h * square(omega);\n  lapse = 1.0 / sqrt(det_gamma);\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>> {\n  return {Scalar<DataType>{vars.lapse}};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>> {\n  return {Scalar<DataType>{0.5 * cube(omega_) * vars.du_h * cube(vars.lapse)}};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<DerivLapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivLapse<DataType>> {\n  tnsr::i<DataType, volume_dim, Frame::Inertial> d_lapse{\n      -0.5 * square(omega_) * vars.du_h * cube(vars.lapse)};\n  for (size_t i = 0; i < volume_dim; ++i) {\n    d_lapse.get(i) *= gsl::at(wave_vector_, i);\n  }\n  return d_lapse;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::Shift<DataType, volume_dim>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::Shift<DataType, volume_dim>> {\n  tnsr::I<DataType, volume_dim, Frame::Inertial> shift{-omega_ * vars.h /\n                                                       vars.det_gamma};\n  for (size_t i = 0; i < volume_dim; ++i) {\n    shift.get(i) *= gsl::at(wave_vector_, i);\n  }\n  return shift;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> /*meta*/)\n    const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> {\n  tnsr::I<DataType, volume_dim, Frame::Inertial> dt_shift{\n      square(omega_) * vars.du_h / square(vars.det_gamma)};\n  for (size_t i = 0; i < volume_dim; ++i) {\n    dt_shift.get(i) *= gsl::at(wave_vector_, i);\n  }\n  return dt_shift;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<DerivShift<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivShift<DataType>> {\n  tnsr::iJ<DataType, volume_dim, Frame::Inertial> d_shift{\n      -omega_ * vars.du_h / square(vars.det_gamma)};\n  for (size_t i = 0; i < volume_dim; ++i) {\n    for (size_t j = 0; j < volume_dim; ++j) {\n      d_shift.get(i, j) *= gsl::at(wave_vector_, i) * gsl::at(wave_vector_, j);\n    }\n  }\n  return d_shift;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, volume_dim>> {\n  tnsr::ii<DataType, Dim, Frame::Inertial> spatial_metric{vars.h};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      spatial_metric.get(i, j) *=\n          gsl::at(wave_vector_, i) * gsl::at(wave_vector_, j);\n    }\n    spatial_metric.get(i, i) += 1.0;\n  }\n  return spatial_metric;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> /*meta*/)\n    const -> tuples::TaggedTuple<\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> {\n  tnsr::ii<DataType, Dim, Frame::Inertial> dt_spatial_metric{-omega_ *\n                                                             vars.du_h};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      dt_spatial_metric.get(i, j) *=\n          gsl::at(wave_vector_, i) * gsl::at(wave_vector_, j);\n    }\n  }\n  return dt_spatial_metric;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivSpatialMetric<DataType>> {\n  tnsr::ijj<DataType, Dim, Frame::Inertial> d_spatial_metric{vars.du_h};\n  for (size_t k = 0; k < Dim; ++k) {\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = i; j < Dim; ++j) {\n        d_spatial_metric.get(k, i, j) *= gsl::at(wave_vector_, k) *\n                                         gsl::at(wave_vector_, i) *\n                                         gsl::at(wave_vector_, j);\n      }\n    }\n  }\n  return d_spatial_metric;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>> {\n  return {Scalar<DataType>{sqrt(vars.det_gamma)}};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> /*meta*/)\n    const\n    -> tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> {\n  tnsr::ii<DataType, Dim, Frame::Inertial> extrinsic_curvature{\n      -0.5 * omega_ * vars.du_h * vars.lapse};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      extrinsic_curvature.get(i, j) *=\n          gsl::at(wave_vector_, i) * gsl::at(wave_vector_, j);\n    }\n  }\n  return extrinsic_curvature;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugePlaneWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::InverseSpatialMetric<DataType, volume_dim>> /*meta*/)\n    const -> tuples::TaggedTuple<\n        gr::Tags::InverseSpatialMetric<DataType, volume_dim>> {\n  tnsr::II<DataType, Dim, Frame::Inertial> inv_spatial_metric{vars.h /\n                                                              vars.det_gamma};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      inv_spatial_metric.get(i, j) *=\n          -gsl::at(wave_vector_, i) * gsl::at(wave_vector_, j);\n    }\n    inv_spatial_metric.get(i, i) += 1.0;\n  }\n  return inv_spatial_metric;\n}\n\ntemplate <size_t Dim>\nbool operator==(const GaugePlaneWave<Dim>& lhs,\n                const GaugePlaneWave<Dim>& rhs) {\n  return lhs.wave_vector_ == rhs.wave_vector_ and\n         *(lhs.profile_) == *(rhs.profile_) and lhs.omega_ == rhs.omega_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const GaugePlaneWave<Dim>& lhs,\n                const GaugePlaneWave<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>::          \\\n      IntermediateVars(                                                        \\\n          const std::array<double, DIM(data)>& wave_vector,                    \\\n          const std::unique_ptr<MathFunction<1, Frame::Inertial>>& profile,    \\\n          double omega,                                                        \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x,           \\\n          const double t);                                                     \\\n  template tuples::TaggedTuple<gr::Tags::Lapse<DTYPE(data)>>                   \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& /*x*/,           \\\n      const double /*t*/,                                                      \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<gr::Tags::Lapse<DTYPE(data)>> /*meta*/) const;                \\\n  template tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>>       \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>> /*meta*/) const;    \\\n  template tuples::TaggedTuple<::Tags::deriv<                                  \\\n      gr::Tags::Lapse<DTYPE(data)>, tmpl::size_t<DIM(data)>, Frame::Inertial>> \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::Lapse<DTYPE(data)>, tmpl::size_t<DIM(data)>, \\\n                        Frame::Inertial>> /*meta*/) const;                     \\\n  template tuples::TaggedTuple<gr::Tags::Shift<DTYPE(data), DIM(data)>>        \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<gr::Tags::Shift<DTYPE(data), DIM(data)>> /*meta*/) const;     \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::Shift<DTYPE(data), DIM(data)>>>                     \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<                                                              \\\n          ::Tags::dt<gr::Tags::Shift<DTYPE(data), DIM(data)>>> /*meta*/)       \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::deriv<gr::Tags::Shift<DTYPE(data), DIM(data)>,                   \\\n                    tmpl::size_t<DIM(data)>, Frame::Inertial>>                 \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::Shift<DTYPE(data), DIM(data)>,               \\\n                        tmpl::size_t<DIM(data)>, Frame::Inertial>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>                         \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)    \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>>             \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<::Tags::dt<                                                   \\\n          gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>> /*meta*/) const;   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::deriv<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>,           \\\n                    tmpl::size_t<DIM(data)>, Frame::Inertial>>                 \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>,       \\\n                        tmpl::size_t<DIM(data)>, Frame::Inertial>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::InverseSpatialMetric<DTYPE(data), DIM(data)>>                  \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<                                                              \\\n          gr::Tags::InverseSpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)    \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::ExtrinsicCurvature<DTYPE(data), DIM(data)>>                    \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<                                                              \\\n          gr::Tags::ExtrinsicCurvature<DTYPE(data), DIM(data)>> /*meta*/)      \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>>    \\\n  GaugePlaneWave<DIM(data)>::variables(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugePlaneWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,    \\\n      tmpl::list<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector))\n\n#undef INSTANTIATE\n#define INSTANTIATE(_, data)                                      \\\n  template class GaugePlaneWave<DIM(data)>;                       \\\n  template bool operator==(const GaugePlaneWave<DIM(data)>& lhs,  \\\n                           const GaugePlaneWave<DIM(data)>& rhs); \\\n  template bool operator!=(const GaugePlaneWave<DIM(data)>& lhs,  \\\n                           const GaugePlaneWave<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef DIM\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gr::Solutions {\n\n/*!\n * \\brief Gauge plane wave in flat spacetime\n *\n * \\details\n * The spacetime metric is in Kerr-Schild form:\n * \\f{equation}{\n * g_{\\mu\\nu} = \\eta_{\\mu\\nu} + H l_\\mu l_\\nu\n * \\f}\n * where \\f$H\\f$ is a scalar function of the coordinates, \\f$\\eta_{\\mu\\nu}\\f$\n * is the Minkowski metric, and \\f$l^\\mu\\f$ is a null vector. Note that the\n * form of the metric along with the nullness of \\f$l^\\mu\\f$ allows one to\n * raise and lower indices of \\f$l^\\mu\\f$ using \\f$\\eta_{\\mu\\nu}\\f$, and\n * that \\f$l^t l^t = l_t l_t = l^i l_i\\f$.\n * Note also that\n * \\f{equation}{\n * g^{\\mu\\nu}  \\equiv \\eta^{\\mu\\nu} - H l^\\mu l^\\nu,\n * \\f}\n * and that \\f$\\sqrt{-g}=1\\f$.\n * Also, \\f$l_\\mu\\f$ is a geodesic with respect to both the physical metric\n * and the Minkowski metric:\n * \\f{equation}{\n * l^\\mu \\partial_\\mu l_\\nu = l^\\mu\\nabla_\\mu l_\\nu = 0.\n * \\f}\n *\n * For this solution we choose the profile \\f$H\\f$ of the plane wave to be an\n * arbitrary one-dimensional function of \\f$u = \\vec{k} \\cdot \\vec{x} - \\omega\n * t\\f$ with a constant Euclidean wave vector \\f$\\vec{k}\\f$ and frequency\n * \\f$\\omega = ||\\vec{k}||\\f$.  The null covector is chosen to be \\f$l_a =\n * (-\\omega, k_i)\\f$. Thus, if \\f$H = H[u]\\f$, then \\f$\\partial_\\mu H = H'[u]\n * l_\\mu\\f$. Therefore the derivatives of the spacetime metric are:\n * \\f{equation}{\n * \\partial_\\rho g_{\\mu\\nu} = H' l_\\rho l_\\mu l_\\nu,\n * \\f}\n *\n * The 3+1 quantities are\n * \\f{align}{\n * \\alpha & = \\left( 1 + H \\omega^2 \\right)^{-1/2},\\\\\n * \\beta^i & = \\frac{-H \\omega k^i}{1 + H \\omega^2},\\\\\n * \\gamma_{ij} & = \\delta_{ij} + H k_i k_j,\\\\\n * \\gamma & = 1 + H \\omega^2,\\\\\n * \\gamma^{ij} & = \\delta^{ij} - \\frac{H k^i k^j}{1 + H \\omega^2},\\\\\n * \\partial_t \\alpha & = \\frac{\\omega^3 H'}{2 \\left(1 + H \\omega^2\n *    \\right)^{3/2}},\\\\\n * \\partial_i \\alpha & = - \\frac{\\omega^2 H' k_i}{2 \\left(1 + H\n *    \\omega^2 \\right)^{3/2}},\\\\\n * \\partial_t \\beta^i & = \\frac{\\omega^2 H' k^i}{\\left(1 + H \\omega^2\n *    \\right)^2},\\\\\n * \\partial_j \\beta^i & = - \\frac{\\omega H' k_j k^i}{\\left(1 + H\n *    \\omega^2 \\right)^2},\\\\\n * \\partial_t \\gamma_{ij} & = - \\omega H' k_i k_j,\\\\\n * \\partial_k \\gamma_{ij} & = H' k_k k_i k_j,\\\\\n * K_{ij} & =  - \\frac{\\omega H' k_i k_j}{2 \\left(1 + H\n *    \\omega^2 \\right)^{1/2}}.\n * \\f}\n *\n * Note that this solution is a gauge wave as \\f$\\Gamma^a{}_{bc} = \\frac{1}{2}\n * H' l^a l_b l_c\\f$ and thus \\f$R^a{}_{bcd} = 0\\f$.\n *\n * \\tparam Dim the spatial dimension of the solution\n */\ntemplate <size_t Dim>\nclass GaugePlaneWave : public AnalyticSolution<Dim>,\n                       public MarkAsAnalyticSolution {\n public:\n  static constexpr size_t volume_dim = Dim;\n  struct WaveVector {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\n        \"The direction of propagation of the wave.\"};\n  };\n\n  struct Profile {\n    using type = std::unique_ptr<MathFunction<1, Frame::Inertial>>;\n    static constexpr Options::String help = {\"The profile of the wave.\"};\n  };\n\n  using options = tmpl::list<WaveVector, Profile>;\n  static constexpr Options::String help{\"Gauge plane wave in flat spacetime\"};\n\n  GaugePlaneWave() = default;\n  GaugePlaneWave(const std::array<double, Dim>& wave_vector,\n                 std::unique_ptr<MathFunction<1, Frame::Inertial>> profile);\n  GaugePlaneWave(const GaugePlaneWave&);\n  GaugePlaneWave& operator=(const GaugePlaneWave&);\n  GaugePlaneWave(GaugePlaneWave&&) = default;\n  GaugePlaneWave& operator=(GaugePlaneWave&&) = default;\n  ~GaugePlaneWave() = default;\n\n  explicit GaugePlaneWave(CkMigrateMessage* /*msg*/);\n\n  template <typename DataType>\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataType>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  template <typename DataType>\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, volume_dim>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  template <typename DataType>\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, volume_dim>,\n                    tmpl::size_t<volume_dim>, Frame::Inertial>;\n\n  template <typename DataType>\n  struct IntermediateVars {\n    IntermediateVars(\n        const std::array<double, Dim>& wave_vector,\n        const std::unique_ptr<MathFunction<1, Frame::Inertial>>& profile,\n        double omega, const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n        double t);\n    DataType h{};\n    DataType du_h{};\n    DataType du_du_h{};\n    DataType det_gamma{};\n    DataType lapse{};\n  };\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<Tags...> /*meta*/) const {\n    const auto& vars =\n        IntermediateVars<DataType>{wave_vector_, profile_, omega_, x, t};\n    return {get<Tags>(variables(x, t, vars, tmpl::list<Tags>{}))...};\n  }\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"Unrecognized tag requested. See the function parameters \"\n                  \"for the tag.\");\n    return {get<Tags>(variables(x, t, vars, tmpl::list<Tags>{}))...};\n  }\n\n  std::array<double, Dim> get_wave_vector() const { return wave_vector_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/)\n      const -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivLapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivLapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::Shift<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::Shift<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> /*meta*/)\n      const\n      -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivShift<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivShift<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<\n          ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n                 double /*t*/, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n                 double /*t*/, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          gr::Tags::ExtrinsicCurvature<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          gr::Tags::InverseSpatialMetric<DataType, volume_dim>>;\n\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const GaugePlaneWave<LocalDim>& lhs,\n                         const GaugePlaneWave<LocalDim>& rhs);\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator!=(const GaugePlaneWave<LocalDim>& lhs,\n                         const GaugePlaneWave<LocalDim>& rhs);\n\n  std::array<double, Dim> wave_vector_{};\n  std::unique_ptr<MathFunction<1, Frame::Inertial>> profile_;\n  double omega_{};\n};\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// compute gauge wave H\ntemplate <typename DataType, size_t Dim>\nDataType gauge_wave_h(const tnsr::I<DataType, Dim>& x, const double t,\n                      const double amplitude, const double wavelength) {\n  return {-1.0 * amplitude * sin((get<0>(x) - t) * (2.0 * M_PI / wavelength)) +\n          1.0};\n}\n\n// compute gauge wave derivH: \\partial_x H\ntemplate <typename DataType, size_t Dim>\nDataType gauge_wave_deriv_h(const tnsr::I<DataType, Dim>& x, const double t,\n                            const double amplitude, const double wavelength) {\n  return {-1.0 * amplitude * (2.0 * M_PI / wavelength) *\n          cos((get<0>(x) - t) * (2.0 * M_PI / wavelength))};\n}\n}  // namespace\n\nnamespace gr::Solutions {\ntemplate <size_t Dim>\nGaugeWave<Dim>::GaugeWave(CkMigrateMessage* /*msg*/) {}\n\ntemplate <size_t Dim>\nGaugeWave<Dim>::GaugeWave(const double amplitude, const double wavelength,\n                          const Options::Context& context)\n    : amplitude_(amplitude), wavelength_(wavelength) {\n  if (abs(amplitude) >= 1.0) {\n    PARSE_ERROR(context,\n                \"Amplitude must be less than one. Given amplitude: \"\n                << amplitude_);\n  }\n  if (wavelength <= 0.0) {\n    PARSE_ERROR(context,\n                \"Wavelength must be non-negative. Given wavelength: \"\n                << wavelength_);\n  }\n}\n\ntemplate <size_t Dim>\nvoid GaugeWave<Dim>::pup(PUP::er& p) {\n  p | amplitude_;\n  p | wavelength_;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nGaugeWave<Dim>::IntermediateVars<DataType>::IntermediateVars(\n    const double amplitude, const double wavelength,\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double t) {\n  h = gauge_wave_h(x, t, amplitude, wavelength);\n  dx_h = gauge_wave_deriv_h(x, t, amplitude, wavelength);\n  sqrt_h = sqrt(h);\n  dx_h_over_2_sqrt_h = 0.5 * dx_h / sqrt(h);\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>> {\n  return {Scalar<DataType>{vars.sqrt_h}};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>> {\n  return {Scalar<DataType>{-1.0 * vars.dx_h_over_2_sqrt_h}};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<DerivLapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivLapse<DataType>> {\n  // most parts of d_lapse are zero, so make_with_value() here\n  auto d_lapse =\n      make_with_value<tnsr::i<DataType, volume_dim, Frame::Inertial>>(x, 0.0);\n  get<0>(d_lapse) = vars.dx_h_over_2_sqrt_h;\n  return d_lapse;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& /*vars*/,\n    tmpl::list<gr::Tags::Shift<DataType, volume_dim>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::Shift<DataType, volume_dim>> {\n  return {\n      make_with_value<tnsr::I<DataType, volume_dim, Frame::Inertial>>(x, 0.0)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& /*vars*/,\n    tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> /*meta*/)\n    const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> {\n  return {\n      make_with_value<tnsr::I<DataType, volume_dim, Frame::Inertial>>(x, 0.0)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& /*vars*/,\n    tmpl::list<DerivShift<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivShift<DataType>> {\n  return {\n      make_with_value<tnsr::iJ<DataType, volume_dim, Frame::Inertial>>(x, 0.0)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, volume_dim>> {\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataType, volume_dim, Frame::Inertial>>(x, 0.0);\n  get<0, 0>(spatial_metric) = vars.h;\n  for (size_t i = 1; i < volume_dim; ++i) {\n    spatial_metric.get(i, i) = 1.0;\n  }\n  return spatial_metric;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> /*meta*/)\n    const -> tuples::TaggedTuple<\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> {\n  auto dt_spatial_metric =\n      make_with_value<tnsr::ii<DataType, volume_dim, Frame::Inertial>>(x, 0.0);\n  get<0, 0>(dt_spatial_metric) = -1.0 * vars.dx_h;\n  return dt_spatial_metric;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivSpatialMetric<DataType>> {\n  auto d_spatial_metric =\n      make_with_value<tnsr::ijj<DataType, volume_dim, Frame::Inertial>>(x, 0.0);\n  get<0, 0, 0>(d_spatial_metric) = vars.dx_h;\n  return d_spatial_metric;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>> {\n  return {Scalar<DataType>{vars.sqrt_h}};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> /*meta*/)\n    const\n    -> tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> {\n  auto extrinsic_curvature =\n      make_with_value<tnsr::ii<DataType, volume_dim, Frame::Inertial>>(x, 0.0);\n  get<0, 0>(extrinsic_curvature) = vars.dx_h_over_2_sqrt_h;\n  return extrinsic_curvature;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nauto GaugeWave<Dim>::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::InverseSpatialMetric<DataType, volume_dim>> /*meta*/)\n    const -> tuples::TaggedTuple<\n        gr::Tags::InverseSpatialMetric<DataType, volume_dim>> {\n  auto inverse_spatial_metric =\n      make_with_value<tnsr::II<DataType, volume_dim, Frame::Inertial>>(x, 0.0);\n  get<0, 0>(inverse_spatial_metric) = 1.0 / vars.h;\n  for (size_t i = 1; i < volume_dim; ++i) {\n    inverse_spatial_metric.get(i, i) = 1.0;\n  }\n  return inverse_spatial_metric;\n}\n\ntemplate <size_t Dim>\nbool operator==(const GaugeWave<Dim>& lhs, const GaugeWave<Dim>& rhs) {\n  return lhs.amplitude() == rhs.amplitude() and\n         lhs.wavelength() == rhs.wavelength();\n}\n\ntemplate <size_t Dim>\nbool operator!=(const GaugeWave<Dim>& lhs, const GaugeWave<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>::               \\\n      IntermediateVars(                                                        \\\n          const double amplitude, const double wavelength,                     \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x,           \\\n          const double t);                                                     \\\n  template tuples::TaggedTuple<gr::Tags::Lapse<DTYPE(data)>>                   \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& /*x*/,           \\\n      const double /*t*/,                                                      \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<gr::Tags::Lapse<DTYPE(data)>> /*meta*/) const;                \\\n  template tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>>       \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>> /*meta*/) const;    \\\n  template tuples::TaggedTuple<::Tags::deriv<                                  \\\n      gr::Tags::Lapse<DTYPE(data)>, tmpl::size_t<DIM(data)>, Frame::Inertial>> \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::Lapse<DTYPE(data)>, tmpl::size_t<DIM(data)>, \\\n                        Frame::Inertial>> /*meta*/) const;                     \\\n  template tuples::TaggedTuple<gr::Tags::Shift<DTYPE(data), DIM(data)>>        \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<gr::Tags::Shift<DTYPE(data), DIM(data)>> /*meta*/) const;     \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::Shift<DTYPE(data), DIM(data)>>>                     \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          ::Tags::dt<gr::Tags::Shift<DTYPE(data), DIM(data)>>> /*meta*/)       \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::deriv<gr::Tags::Shift<DTYPE(data), DIM(data)>,                   \\\n                    tmpl::size_t<DIM(data)>, Frame::Inertial>>                 \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::Shift<DTYPE(data), DIM(data)>,               \\\n                        tmpl::size_t<DIM(data)>, Frame::Inertial>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>                         \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)    \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>>             \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<::Tags::dt<                                                   \\\n          gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>> /*meta*/) const;   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::deriv<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>,           \\\n                    tmpl::size_t<DIM(data)>, Frame::Inertial>>                 \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>,       \\\n                        tmpl::size_t<DIM(data)>, Frame::Inertial>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::InverseSpatialMetric<DTYPE(data), DIM(data)>>                  \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          gr::Tags::InverseSpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)    \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::ExtrinsicCurvature<DTYPE(data), DIM(data)>>                    \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          gr::Tags::ExtrinsicCurvature<DTYPE(data), DIM(data)>> /*meta*/)      \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>>    \\\n  GaugeWave<DIM(data)>::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const GaugeWave<DIM(data)>::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector))\n\n#undef INSTANTIATE\n#define INSTANTIATE(_, data)                                 \\\n  template class GaugeWave<DIM(data)>;                       \\\n  template bool operator==(const GaugeWave<DIM(data)>& lhs,  \\\n                           const GaugeWave<DIM(data)>& rhs); \\\n  template bool operator!=(const GaugeWave<DIM(data)>& lhs,  \\\n                           const GaugeWave<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef DIM\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gr {\nnamespace Solutions {\n\n/*!\n * \\brief Gauge wave in flat spacetime\n *\n * \\details\n * This solution is Minkowski space in coordinates chosen to contain\n * a gauge wave. The spacetime metric is given by Eq. (4.3) of\n * \\cite Alcubierre2003pc :\n *\n * \\f{equation}{\n * ds^2 = -H dt^2 + H dx^2 + dy^2 + dz^2,\n * \\f}\n *\n * where\n *\n * \\f{equation}{\n * H = H(x-t) = 1 - A \\sin\\left(\\frac{2\\pi(x-t)}{d}\\right).\n * \\f}\n *\n * The gauge wave has amplitude \\f$A\\f$, wavelength \\f$d\\f$, and propagates\n * along the x axis.\n *\n * In these coordinates, the spatial metric \\f$\\gamma_{ij}\\f$ and\n * inverse spatial metric \\f$\\gamma^{ij}\\f$ are diagonal,\n * with the diagonal elements equal to unity except for\n *\n * \\f{align}{\n * \\gamma_{xx} & = H,\\\\\n * \\gamma^{xx} & = 1/H.\n * \\f}\n *\n * The components of the derivatives of \\f$\\gamma_{ij}\\f$ vanish except for\n *\n * \\f{align}{\n * \\partial_t \\gamma_{xx} & = \\partial_t H = - \\partial_x H,\\\\\n * \\partial_x \\gamma_{xx} & = \\partial_x H.\n * \\f}\n *\n * The square root of the spatial metric determinant is\n *\n * \\f{align}{\n * \\sqrt{\\gamma} & = \\sqrt{H}.\n * \\f}\n *\n * The lapse and its derivatives are\n *\n * \\f{align}{\n * \\alpha & = \\sqrt{H},\\\\\n * \\partial_t \\alpha & = -\\frac{\\partial_x H}{2\\sqrt{H}},\\\\\n * \\partial_x \\alpha & = \\frac{\\partial_x H}{2\\sqrt{H}},\\\\\n * \\partial_y \\alpha & = \\partial_z \\alpha = 0.\n * \\f}\n *\n * The shift \\f$\\beta^i\\f$ and its derivatives vanish.\n *\n * The extrinsic curvature's components vanish, except for\n *\n * \\f{align}{\n * K_{xx} & = \\frac{\\partial_x H}{2 \\sqrt{H}}.\n * \\f}\n *\n * The following are input file options that can be specified:\n *  - Amplitude\n *  - Wavelength\n */\ntemplate <size_t Dim>\nclass GaugeWave : public AnalyticSolution<Dim>, public MarkAsAnalyticSolution {\n  template <typename DataType>\n  struct IntermediateVars;\n\n public:\n  static constexpr size_t volume_dim = Dim;\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help = {\"Amplitude of the gauge wave\"};\n    static type upper_bound() { return 1.; }\n    static type lower_bound() { return -1.; }\n  };\n  struct Wavelength {\n    using type = double;\n    static constexpr Options::String help = {\"Wavelength of the gauge wave\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  using options = tmpl::list<Amplitude, Wavelength>;\n  static constexpr Options::String help{\"Gauge wave in flat spacetime\"};\n\n  GaugeWave(double amplitude, double wavelength,\n            const Options::Context& context = {});\n\n  GaugeWave() = default;\n  GaugeWave(const GaugeWave& /*rhs*/) = default;\n  GaugeWave& operator=(const GaugeWave& /*rhs*/) = default;\n  GaugeWave(GaugeWave&& /*rhs*/) = default;\n  GaugeWave& operator=(GaugeWave&& /*rhs*/) = default;\n  ~GaugeWave() = default;\n\n  explicit GaugeWave(CkMigrateMessage* /*msg*/);\n\n  template <typename DataType>\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataType>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  template <typename DataType>\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, volume_dim>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  template <typename DataType>\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, volume_dim>,\n                    tmpl::size_t<volume_dim>, Frame::Inertial>;\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<Tags...> /*meta*/) const {\n    const auto& vars =\n        IntermediateVars<DataType>{amplitude_, wavelength_, x, t};\n    return {get<Tags>(variables(x, t, vars, tmpl::list<Tags>{}))...};\n  }\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"Unrecognized tag requested.  See the function parameters \"\n                  \"for the tag.\");\n    return {get<Tags>(variables(x, t, vars, tmpl::list<Tags>{}))...};\n  }\n\n  template <typename DataType>\n  using tags = tmpl::list<\n      gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,\n      DerivLapse<DataType>, gr::Tags::Shift<DataType, volume_dim>,\n      ::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>, DerivShift<DataType>,\n      gr::Tags::SpatialMetric<DataType, volume_dim>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>,\n      DerivSpatialMetric<DataType>, gr::Tags::SqrtDetSpatialMetric<DataType>,\n      gr::Tags::ExtrinsicCurvature<DataType, volume_dim>,\n      gr::Tags::InverseSpatialMetric<DataType, volume_dim>>;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  SPECTRE_ALWAYS_INLINE double amplitude() const { return amplitude_; }\n  SPECTRE_ALWAYS_INLINE double wavelength() const { return wavelength_; }\n\n private:\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/)\n      const -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivLapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivLapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::Shift<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::Shift<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> /*meta*/)\n      const\n      -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivShift<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivShift<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<\n          ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n                 double /*t*/, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n                 double /*t*/, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          gr::Tags::ExtrinsicCurvature<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          gr::Tags::InverseSpatialMetric<DataType, volume_dim>>;\n\n  template <typename DataType>\n  struct IntermediateVars {\n    IntermediateVars(double amplitude, double wavelength,\n                     const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                     double t);\n    DataType h{};\n    DataType dx_h{};\n    DataType sqrt_h{};\n    DataType dx_h_over_2_sqrt_h{};\n  };\n\n  double amplitude_{1.0};\n  double wavelength_{1.0};\n};\n\ntemplate <size_t Dim>\nbool operator==(const GaugeWave<Dim>& lhs, const GaugeWave<Dim>& rhs);\ntemplate <size_t Dim>\nbool operator!=(const GaugeWave<Dim>& lhs, const GaugeWave<Dim>& rhs);\n}  // namespace Solutions\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr::Solutions {\n\nHarmonicSchwarzschild::HarmonicSchwarzschild(\n    const double mass, const std::array<double, volume_dim>& center,\n    const Options::Context& context)\n    : mass_(mass), center_(center) {\n  if (mass_ < 0.0) {\n    PARSE_ERROR(context, \"Mass must be non-negative. Given mass: \" << mass_);\n  }\n}\n\nHarmonicSchwarzschild::HarmonicSchwarzschild(CkMigrateMessage* /*msg*/) {}\n\nvoid HarmonicSchwarzschild::pup(PUP::er& p) {\n  p | mass_;\n  p | center_;\n}\n\ntemplate <typename DataType, typename Frame>\nHarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::\n    IntermediateComputer(const HarmonicSchwarzschild& solution,\n                         const tnsr::I<DataType, 3, Frame>& x)\n    : solution_(solution), x_(x) {}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center,\n    const gsl::not_null<CachedBuffer*> /*cache*/,\n    internal_tags::x_minus_center<DataType, Frame> /*meta*/) const {\n  for (size_t i = 0; i < 3; ++i) {\n    x_minus_center->get(i) = x_.get(i) - gsl::at(solution_.center(), i);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::r<DataType> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n\n  get(*r) =\n      sqrt(square(get<0>(x_minus_center)) + square(get<1>(x_minus_center)) +\n           square(get<2>(x_minus_center)));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> one_over_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::one_over_r<DataType> /*meta*/) const {\n  const auto& r = cache->get_var(*this, internal_tags::r<DataType>{});\n\n  get(*one_over_r) = 1.0 / get(r);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_over_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::x_over_r<DataType, Frame> /*meta*/) const {\n  const auto& one_over_r =\n      cache->get_var(*this, internal_tags::one_over_r<DataType>{});\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n\n  tenex::evaluate<ti::I>(x_over_r, x_minus_center(ti::I) * one_over_r());\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> m_over_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::m_over_r<DataType> /*meta*/) const {\n  const auto& one_over_r =\n      cache->get_var(*this, internal_tags::one_over_r<DataType>{});\n\n  get(*m_over_r) = solution_.mass() * get(one_over_r);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> sqrt_f_0,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::sqrt_f_0<DataType> /*meta*/) const {\n  const auto& m_over_r =\n      cache->get_var(*this, internal_tags::m_over_r<DataType>{});\n\n  get(*sqrt_f_0) = 1.0 + get(m_over_r);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> f_0,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::f_0<DataType> /*meta*/) const {\n  const auto& sqrt_f_0 =\n      cache->get_var(*this, internal_tags::sqrt_f_0<DataType>{});\n\n  get(*f_0) = square(get(sqrt_f_0));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> two_m_over_m_plus_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::two_m_over_m_plus_r<DataType> /*meta*/) const {\n  const auto& r = cache->get_var(*this, internal_tags::r<DataType>{});\n\n  get(*two_m_over_m_plus_r) =\n      2.0 * solution_.mass() / (solution_.mass() + get(r));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> two_m_over_m_plus_r_squared,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::two_m_over_m_plus_r_squared<DataType> /*meta*/) const {\n  const auto& two_m_over_m_plus_r =\n      cache->get_var(*this, internal_tags::two_m_over_m_plus_r<DataType>{});\n\n  get(*two_m_over_m_plus_r_squared) = square(get(two_m_over_m_plus_r));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> two_m_over_m_plus_r_cubed,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::two_m_over_m_plus_r_cubed<DataType> /*meta*/) const {\n  const auto& two_m_over_m_plus_r =\n      cache->get_var(*this, internal_tags::two_m_over_m_plus_r<DataType>{});\n  const auto& two_m_over_m_plus_r_squared = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_squared<DataType>{});\n\n  get(*two_m_over_m_plus_r_cubed) =\n      get(two_m_over_m_plus_r) * get(two_m_over_m_plus_r_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> spatial_metric_rr,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::spatial_metric_rr<DataType> /*meta*/) const {\n  const auto& two_m_over_m_plus_r =\n      cache->get_var(*this, internal_tags::two_m_over_m_plus_r<DataType>{});\n  const auto& two_m_over_m_plus_r_squared = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_squared<DataType>{});\n  const auto& two_m_over_m_plus_r_cubed = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_cubed<DataType>{});\n\n  get(*spatial_metric_rr) = 1.0 + get(two_m_over_m_plus_r) +\n                            get(two_m_over_m_plus_r_squared) +\n                            get(two_m_over_m_plus_r_cubed);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> one_over_spatial_metric_rr,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::one_over_spatial_metric_rr<DataType> /*meta*/) const {\n  const auto& spatial_metric_rr =\n      cache->get_var(*this, internal_tags::spatial_metric_rr<DataType>{});\n\n  get(*one_over_spatial_metric_rr) = 1.0 / get(spatial_metric_rr);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> spatial_metric_rr_minus_f_0,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::spatial_metric_rr_minus_f_0<DataType> /*meta*/) const {\n  const auto& spatial_metric_rr =\n      cache->get_var(*this, internal_tags::spatial_metric_rr<DataType>{});\n  const auto& f_0 = cache->get_var(*this, internal_tags::f_0<DataType>{});\n\n  get(*spatial_metric_rr_minus_f_0) = get(spatial_metric_rr) - get(f_0);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> d_spatial_metric_rr,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::d_spatial_metric_rr<DataType> /*meta*/) const {\n  const auto& two_m_over_m_plus_r_squared = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_squared<DataType>{});\n  const auto& two_m_over_m_plus_r_cubed = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_cubed<DataType>{});\n\n  get(*d_spatial_metric_rr) =\n      (0.5 * get(two_m_over_m_plus_r_squared) + get(two_m_over_m_plus_r_cubed) +\n       1.5 * square(get(two_m_over_m_plus_r_squared))) /\n      (-solution_.mass());\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> d_f_0,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::d_f_0<DataType> /*meta*/) const {\n  const auto& sqrt_f_0 =\n      cache->get_var(*this, internal_tags::sqrt_f_0<DataType>{});\n  const auto& m_over_r =\n      cache->get_var(*this, internal_tags::m_over_r<DataType>{});\n  const auto& one_over_r =\n      cache->get_var(*this, internal_tags::one_over_r<DataType>{});\n\n  get(*d_f_0) = -2.0 * get(sqrt_f_0) * get(m_over_r) * get(one_over_r);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3, Frame>*> d_f_0_times_x_over_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::d_f_0_times_x_over_r<DataType, Frame> /*meta*/) const {\n  const auto& d_f_0 = cache->get_var(*this, internal_tags::d_f_0<DataType>{});\n  const auto& x_over_r =\n      cache->get_var(*this, internal_tags::x_over_r<DataType, Frame>{});\n\n  get<0>(*d_f_0_times_x_over_r) = get(d_f_0) * get<0>(x_over_r);\n  get<1>(*d_f_0_times_x_over_r) = get(d_f_0) * get<1>(x_over_r);\n  get<2>(*d_f_0_times_x_over_r) = get(d_f_0) * get<2>(x_over_r);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> f_1,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::f_1<DataType> /*meta*/) const {\n  const auto& one_over_r =\n      cache->get_var(*this, internal_tags::one_over_r<DataType>{});\n  const auto& spatial_metric_rr =\n      cache->get_var(*this, internal_tags::spatial_metric_rr<DataType>{});\n  const auto& f_0 = cache->get_var(*this, internal_tags::f_0<DataType>{});\n\n  get(*f_1) = get(one_over_r) * (get(spatial_metric_rr) - get(f_0));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3, Frame>*> f_1_times_x_over_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::f_1_times_x_over_r<DataType, Frame> /*meta*/) const {\n  const auto& f_1 = cache->get_var(*this, internal_tags::f_1<DataType>{});\n  const auto& x_over_r =\n      cache->get_var(*this, internal_tags::x_over_r<DataType, Frame>{});\n\n  get<0>(*f_1_times_x_over_r) = get(f_1) * get<0>(x_over_r);\n  get<1>(*f_1_times_x_over_r) = get(f_1) * get<1>(x_over_r);\n  get<2>(*f_1_times_x_over_r) = get(f_1) * get<2>(x_over_r);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> f_2,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::f_2<DataType> /*meta*/) const {\n  const auto& d_spatial_metric_rr =\n      cache->get_var(*this, internal_tags::d_spatial_metric_rr<DataType>{});\n  const auto& d_f_0 = cache->get_var(*this, internal_tags::d_f_0<DataType>{});\n  const auto& f_1 = cache->get_var(*this, internal_tags::f_1<DataType>{});\n\n  get(*f_2) = get(d_spatial_metric_rr) - get(d_f_0) - 2.0 * get(f_1);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::iii<DataType, 3, Frame>*>\n        f_2_times_xxx_over_r_cubed,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::f_2_times_xxx_over_r_cubed<DataType, Frame> /*meta*/) const {\n  const auto& f_2 = cache->get_var(*this, internal_tags::f_2<DataType>{});\n  const auto& x_over_r =\n      cache->get_var(*this, internal_tags::x_over_r<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = i; j < 3; j++) {\n      for (size_t k = j; k < 3; k++) {\n        f_2_times_xxx_over_r_cubed->get(i, j, k) =\n            get(f_2) * x_over_r.get(i) * x_over_r.get(j) * x_over_r.get(k);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> f_3,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::f_3<DataType> /*meta*/) const {\n  const auto& one_over_r =\n      cache->get_var(*this, internal_tags::one_over_r<DataType>{});\n  const auto& two_m_over_m_plus_r_squared = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_squared<DataType>{});\n  const auto& one_over_spatial_metric_rr = cache->get_var(\n      *this, internal_tags::one_over_spatial_metric_rr<DataType>{});\n\n  get(*f_3) = get(one_over_r) * get(two_m_over_m_plus_r_squared) *\n              get(one_over_spatial_metric_rr);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> f_4,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::f_4<DataType> /*meta*/) const {\n  const auto& f_3 = cache->get_var(*this, internal_tags::f_3<DataType>{});\n  const auto& two_m_over_m_plus_r_squared = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_squared<DataType>{});\n  const auto& two_m_over_m_plus_r_cubed = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_cubed<DataType>{});\n  const auto& d_spatial_metric_rr =\n      cache->get_var(*this, internal_tags::d_spatial_metric_rr<DataType>{});\n  const auto& one_over_spatial_metric_rr = cache->get_var(\n      *this, internal_tags::one_over_spatial_metric_rr<DataType>{});\n\n  get(*f_4) = -get(f_3) -\n              get(two_m_over_m_plus_r_cubed) * get(one_over_spatial_metric_rr) /\n                  solution_.mass() -\n              get(d_spatial_metric_rr) * get(two_m_over_m_plus_r_squared) *\n                  square(get(one_over_spatial_metric_rr));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::Lapse<DataType> /*meta*/) const {\n  const auto& one_over_spatial_metric_rr = cache->get_var(\n      *this, internal_tags::one_over_spatial_metric_rr<DataType>{});\n\n  get(*lapse) = sqrt(get(one_over_spatial_metric_rr));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*>\n        neg_half_lapse_cubed_times_d_spatial_metric_rr,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::neg_half_lapse_cubed_times_d_spatial_metric_rr<\n        DataType> /*meta*/) const {\n  const auto& lapse = cache->get_var(*this, gr::Tags::Lapse<DataType>{});\n  const auto& d_spatial_metric_rr =\n      cache->get_var(*this, internal_tags::d_spatial_metric_rr<DataType>{});\n\n  get(*neg_half_lapse_cubed_times_d_spatial_metric_rr) =\n      -0.5 * cube(get(lapse)) * get(d_spatial_metric_rr);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> shift,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::Shift<DataType, 3, Frame> /*meta*/) const {\n  const auto& two_m_over_m_plus_r_squared = cache->get_var(\n      *this, internal_tags::two_m_over_m_plus_r_squared<DataType>{});\n  const auto& x_over_r =\n      cache->get_var(*this, internal_tags::x_over_r<DataType, Frame>{});\n  const auto& one_over_spatial_metric_rr = cache->get_var(\n      *this, internal_tags::one_over_spatial_metric_rr<DataType>{});\n\n  ::tenex::evaluate<ti::I>(shift, two_m_over_m_plus_r_squared() *\n                                      x_over_r(ti::I) *\n                                      one_over_spatial_metric_rr());\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3, Frame>*> deriv_shift,\n    const gsl::not_null<CachedBuffer*> cache,\n    DerivShift<DataType, Frame> /*meta*/) const {\n  const auto& x_over_r =\n      cache->get_var(*this, internal_tags::x_over_r<DataType, Frame>{});\n  const auto& f_3 = cache->get_var(*this, internal_tags::f_3<DataType>{});\n  const auto& f_4 = cache->get_var(*this, internal_tags::f_4<DataType>{});\n\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t i = k; i < 3; ++i) {\n      if (i != k) {\n        deriv_shift->get(k, i) = get(f_4) * x_over_r.get(i) * x_over_r.get(k);\n        deriv_shift->get(i, k) = deriv_shift->get(k, i);  // symmetry\n      } else {\n        deriv_shift->get(k, i) = get(f_4) * square(x_over_r.get(i)) + get(f_3);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::SpatialMetric<DataType, 3, Frame> /*meta*/) const {\n  const auto& f_0 = cache->get_var(*this, internal_tags::f_0<DataType>{});\n  const auto& spatial_metric_rr_minus_f_0 = cache->get_var(\n      *this, internal_tags::spatial_metric_rr_minus_f_0<DataType>{});\n  const auto& x_over_r =\n      cache->get_var(*this, internal_tags::x_over_r<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      spatial_metric->get(i, j) =\n          get(spatial_metric_rr_minus_f_0) * x_over_r.get(i) * x_over_r.get(j);\n      if (i == j) {\n        spatial_metric->get(i, j) += get(f_0);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*> deriv_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    DerivSpatialMetric<DataType, Frame> /*meta*/) const {\n  const auto& d_f_0_times_x_over_r = cache->get_var(\n      *this, internal_tags::d_f_0_times_x_over_r<DataType, Frame>{});\n  const auto& f_1_times_x_over_r = cache->get_var(\n      *this, internal_tags::f_1_times_x_over_r<DataType, Frame>{});\n  const auto& f_2_times_xxx_over_r_cubed = cache->get_var(\n      *this, internal_tags::f_2_times_xxx_over_r_cubed<DataType, Frame>{});\n\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        deriv_spatial_metric->get(k, i, j) =\n            f_2_times_xxx_over_r_cubed.get(k, i, j);\n        if (i == j) {\n          deriv_spatial_metric->get(k, i, j) += d_f_0_times_x_over_r.get(k);\n        }\n        if (j == k) {\n          deriv_spatial_metric->get(k, i, j) += f_1_times_x_over_r.get(i);\n        }\n        if (i == k) {\n          deriv_spatial_metric->get(k, i, j) += f_1_times_x_over_r.get(j);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> dt_spatial_metric,\n    const gsl::not_null<CachedBuffer*> /*cache*/,\n    ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>> /*meta*/) const {\n  std::fill(dt_spatial_metric->begin(), dt_spatial_metric->end(), 0.);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> det_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::DetSpatialMetric<DataType> /*meta*/) const {\n  const auto& spatial_metric =\n      cache->get_var(*this, gr::Tags::SpatialMetric<DataType, 3, Frame>{});\n\n  *det_spatial_metric = determinant(spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid HarmonicSchwarzschild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> one_over_det_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::one_over_det_spatial_metric<DataType> /*meta*/) const {\n  const auto& det_spatial_metric =\n      cache->get_var(*this, gr::Tags::DetSpatialMetric<DataType>{});\n\n  get(*one_over_det_spatial_metric) = 1.0 / get(det_spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::i<DataType, 3, Frame>\nHarmonicSchwarzschild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    DerivLapse<DataType, Frame> /*meta*/) {\n  const auto& neg_half_lapse_cubed_times_d_spatial_metric_rr = get_var(\n      computer, internal_tags::neg_half_lapse_cubed_times_d_spatial_metric_rr<\n                    DataType>{});\n  const auto& x_over_r =\n      get_var(computer, internal_tags::x_over_r<DataType, Frame>{});\n\n  tnsr::i<DataType, 3, Frame> deriv_lapse{};\n  for (size_t i = 0; i < 3; i++) {\n    deriv_lapse.get(i) =\n        get(neg_half_lapse_cubed_times_d_spatial_metric_rr) * x_over_r.get(i);\n  }\n\n  return deriv_lapse;\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType>\nHarmonicSchwarzschild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/) {\n  const auto& r = get(get_var(computer, internal_tags::r<DataType>{}));\n  return make_with_value<Scalar<DataType>>(r, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::I<DataType, 3, Frame>\nHarmonicSchwarzschild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>> /*meta*/) {\n  const auto& r = get(get_var(computer, internal_tags::r<DataType>{}));\n  return make_with_value<tnsr::I<DataType, 3, Frame>>(r, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType>\nHarmonicSchwarzschild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/) {\n  const auto& det_spatial_metric =\n      get_var(computer, gr::Tags::DetSpatialMetric<DataType>{});\n  return Scalar<DataType>(sqrt(get(det_spatial_metric)));\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::II<DataType, 3, Frame>\nHarmonicSchwarzschild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::InverseSpatialMetric<DataType, 3, Frame> /*meta*/) {\n  const auto& spatial_metric =\n      get_var(computer, gr::Tags::SpatialMetric<DataType, 3, Frame>{});\n  const DataType& spatial_metric_00 = get<0, 0>(spatial_metric);\n  const DataType& spatial_metric_01 = get<0, 1>(spatial_metric);\n  const DataType& spatial_metric_02 = get<0, 2>(spatial_metric);\n  const DataType& spatial_metric_11 = get<1, 1>(spatial_metric);\n  const DataType& spatial_metric_12 = get<1, 2>(spatial_metric);\n  const DataType& spatial_metric_22 = get<2, 2>(spatial_metric);\n  const auto& one_over_det_spatial_metric =\n      get_var(computer, internal_tags::one_over_det_spatial_metric<DataType>{});\n\n  tnsr::II<DataType, 3, Frame> inverse_spatial_metric{};\n  get<0, 0>(inverse_spatial_metric) =\n      (spatial_metric_11 * spatial_metric_22 - square(spatial_metric_12)) *\n      get(one_over_det_spatial_metric);\n  get<0, 1>(inverse_spatial_metric) = (spatial_metric_12 * spatial_metric_02 -\n                                       spatial_metric_22 * spatial_metric_01) *\n                                      get(one_over_det_spatial_metric);\n  get<0, 2>(inverse_spatial_metric) = (spatial_metric_01 * spatial_metric_12 -\n                                       spatial_metric_02 * spatial_metric_11) *\n                                      get(one_over_det_spatial_metric);\n  get<1, 1>(inverse_spatial_metric) = (spatial_metric_22 * spatial_metric_00 -\n                                       spatial_metric_02 * spatial_metric_02) *\n                                      get(one_over_det_spatial_metric);\n  get<1, 2>(inverse_spatial_metric) = (spatial_metric_02 * spatial_metric_01 -\n                                       spatial_metric_00 * spatial_metric_12) *\n                                      get(one_over_det_spatial_metric);\n  get<2, 2>(inverse_spatial_metric) = (spatial_metric_00 * spatial_metric_11 -\n                                       spatial_metric_01 * spatial_metric_01) *\n                                      get(one_over_det_spatial_metric);\n\n  return inverse_spatial_metric;\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::ii<DataType, 3, Frame>\nHarmonicSchwarzschild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::ExtrinsicCurvature<DataType, 3, Frame> /*meta*/) {\n  return gr::extrinsic_curvature(\n      get_var(computer, gr::Tags::Lapse<DataType>{}),\n      get_var(computer, gr::Tags::Shift<DataType, 3, Frame>{}),\n      get_var(computer, DerivShift<DataType, Frame>{}),\n      get_var(computer, gr::Tags::SpatialMetric<DataType, 3, Frame>{}),\n      get_var(computer,\n              ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>{}),\n      get_var(computer, DerivSpatialMetric<DataType, Frame>{}));\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template class HarmonicSchwarzschild::IntermediateVars<DTYPE(data),     \\\n                                                         FRAME(data)>;    \\\n  template class HarmonicSchwarzschild::IntermediateComputer<DTYPE(data), \\\n                                                             FRAME(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, double),\n                        (::Frame::Inertial, ::Frame::Grid))\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\nnamespace Solutions {\n/*!\n * \\brief Schwarzschild black hole in Cartesian coordinates with harmonic gauge\n *\n * \\details\n * This solution represents a Schwarzschild black hole in coordinates that are\n * harmonic in both space and time, as well as horizon-penetrating. Therefore,\n * this solution fulfills the harmonic coordinate conditions Eq. (4.42), (4.44),\n * and (4.45) in \\cite BaumgarteShapiro :\n *\n * \\f{align}\n *     {}^{(4)}\\Gamma^a &= 0\n * \\f}\n *\n * \\f{align}\n *     (\\partial_t - \\beta^j \\partial_j)\\alpha &= -\\alpha^2 K\n * \\f}\n *\n * \\f{align}\n *     (\\partial_t - \\beta^j \\partial_j)\\beta^i &=\n *         -\\alpha^2 (\\gamma^{ij} \\partial_j \\ln \\alpha -\n *                    \\gamma^{jk} \\Gamma^i_{jk})\n * \\f}\n *\n * (Note that Eq. 4.45 in \\cite BaumgarteShapiro is missing a minus sign in\n * front of the \\f$\\Gamma\\f$-contraction term)\n *\n * We implement Eqs. (45)--(50) in \\cite Cook1997qc , which represent the\n * zero-spin limit of the time-harmonic and horizon-penetrating slices of Kerr\n * spacetime presented in the paper. We add the radial transformation\n * \\f$r \\to r + M\\f$ to make the spatial coordinates harmonic as well (see\n * Eq. (43) in \\cite Cook1997qc ), so the coordinates remain harmonic under\n * boosts.\n *\n * Consider a Schwarzschild black hole of mass \\f$M\\f$ and center \\f$C^i\\f$. The\n * spacetime will be specified using Cartesian coordinates \\f$x^i\\f$ that are\n * spatially and temporally harmonic. A radius centered on the black hole is\n *\n * \\f{align}\n *     r &= \\sqrt{\\delta_{ij} \\left(x^i - C^i\\right)\\left(x^j - C^j\\right)}\n * \\f}\n *\n * For computing the spatial metric, we define the following quantities:\n *\n * \\f{align}\n *     \\gamma_{rr} &= 1 + \\frac{2M}{M+r} + \\left(\\frac{2M}{M+r}\\right)^2\n *         + \\left(\\frac{2M}{M+r}\\right)^3,\\\\\n *     \\partial_r \\gamma_{rr} &= -\\frac{1}{2M}\\left(\\frac{2M}{M+r}\\right)^2\n *         -\\frac{1}{M}\\left(\\frac{2M}{M+r}\\right)^3\n *         -\\frac{3}{2M}\\left(\\frac{2M}{M+r}\\right)^4,\\\\\n *     \\frac{X^i}{r} &= \\frac{x^i - C^i}{r},\\\\\n *     X_j &= X^i \\delta_{ij}\n * \\f}\n *\n * From these quantities, the spatial metric and its time derivative are\n * computed as\n *\n * \\f{align}\n *     \\gamma_{ij} &=\n *         \\left(\\gamma_{rr} - \\left(1+\\frac{M}{r}\\right)^2\\right)\n *             \\frac{X_i}{r} \\frac{X_j}{r} +\n *         \\delta_{ij} \\left(1+\\frac{M}{r}\\right)^2,\\\\\n *     \\partial_t \\gamma_{ij} &= 0\n * \\f}\n *\n * The spatial derivative is given in terms of the following quantities:\n *\n * \\f{align}\n *     f_0 &= \\left(1+\\frac{M}{r}\\right)^2\\\\\n *     \\partial_r f_0 &=\n *         2 \\left(1+\\frac{M}{r}\\right)\\left(-\\frac{M}{r^2}\\right),\\\\\n *     f_1 &= \\frac{1}{r} \\left(\\gamma_{rr} - f_0\\right),\\\\\n *     f_2 &= \\partial_r \\gamma_{rr} - \\partial_r f_0 - 2 f_1\n * \\f}\n *\n * In terms of these, the spatial metric's spatial derivative is\n *\n * \\f{align}\n *     \\partial_k \\gamma_{ij} &=\n *         f_2 \\frac{X_i}{r} \\frac{X_j}{r} \\frac{X_k}{r} +\n *         f_1 \\frac{X_j}{r} \\delta_{ik} + f_1 \\frac{X_i}{r} \\delta_{jk} +\n *         \\partial_r f_0 \\frac{X_k}{r} \\delta_{ij}\n * \\f}\n *\n * The lapse and its derivatives are\n *\n * \\f{align}\n *     \\alpha &= \\gamma_{rr}^{-1/2},\\\\\n *     \\partial_t \\alpha &= 0,\\\\\n *     \\partial_i \\alpha &=\n *         -\\frac{1}{2} \\gamma_{rr}^{-3/2} \\partial_r \\gamma_{rr} \\frac{X_i}{r}\n * \\f}\n *\n * The shift and its time derivative are\n *\n * \\f{align}\n *     \\beta^i &= \\left(\\frac{2M}{M+r}\\right)^2 \\frac{X^i}{r}\n *         \\frac{1}{\\gamma_{rr}},\\\\\n *     \\partial_t \\beta^i &= 0\n * \\f}\n *\n * The spatial derivative of the shift is computed in terms of the following\n * quantities:\n *\n * \\f{align}\n *     f_3 &= \\frac{1}{r} \\frac{1}{\\gamma_{rr}} \\left(\\frac{2M}{M+r}\\right)^2,\\\\\n *     f_4 &=\n *         -f_3 -\n *         \\frac{1}{M} \\frac{1}{\\gamma_{rr}}\n *             \\left(\\frac{2M}{M+r}\\right)^3 -\n *         \\partial_r \\gamma_{rr} \\left(\\frac{2 M}{M+r}\n *             \\frac{1}{\\gamma_{rr}}\\right)^2\n * \\f}\n *\n * In terms of these, the shift's spatial derivative is\n *\n * \\f{align}\n *     \\partial_k \\beta^i &= f_4 \\frac{X^i}{r} \\frac{X_k}{r} + \\delta_k^i f_3\n * \\f}\n */\nclass HarmonicSchwarzschild : public AnalyticSolution<3_st>,\n                              public MarkAsAnalyticSolution {\n public:\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\"Mass of the black hole\"};\n    static type lower_bound() { return 0.; }\n  };\n  struct Center {\n    using type = std::array<double, volume_dim>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] center of the black hole\"};\n  };\n  using options = tmpl::list<Mass, Center>;\n  static constexpr Options::String help{\n      \"Schwarzschild black hole in Cartesian coordinates with harmonic gauge\"};\n\n  HarmonicSchwarzschild(double mass,\n                        const std::array<double, volume_dim>& center,\n                        const Options::Context& context = {});\n\n  HarmonicSchwarzschild() = default;\n  HarmonicSchwarzschild(const HarmonicSchwarzschild& /*rhs*/) = default;\n  HarmonicSchwarzschild& operator=(const HarmonicSchwarzschild& /*rhs*/) =\n      default;\n  HarmonicSchwarzschild(HarmonicSchwarzschild&& /*rhs*/) = default;\n  HarmonicSchwarzschild& operator=(HarmonicSchwarzschild&& /*rhs*/) = default;\n  ~HarmonicSchwarzschild() = default;\n\n  explicit HarmonicSchwarzschild(CkMigrateMessage* /*msg*/);\n\n  /*!\n   * \\brief Computes and returns spacetime quantities for a Schwarzschild black\n   * hole with harmonic coordinates at a specific Cartesian position\n   *\n   * \\param x Cartesian coordinates of the position at which to compute\n   * spacetime quantities\n   */\n  template <typename DataType, typename Frame, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame>& x, double /*t*/,\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(\n        tmpl2::flat_all_v<\n            tmpl::list_contains_v<tags<DataType, Frame>, Tags>...>,\n        \"At least one of the requested tags is not supported. The requested \"\n        \"tags are listed as template parameters of the `variables` function.\");\n    IntermediateVars<DataType, Frame> cache(get_size(*x.begin()));\n    IntermediateComputer<DataType, Frame> computer(*this, x);\n    return {cache.get_var(computer, Tags{})...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /*!\n   * \\brief Return the mass of the black hole\n   */\n  SPECTRE_ALWAYS_INLINE double mass() const { return mass_; }\n  /*!\n   * \\brief Return the center of the black hole\n   */\n  SPECTRE_ALWAYS_INLINE const std::array<double, volume_dim>& center() const {\n    return center_;\n  }\n\n  /*!\n   * \\brief Tags defined for intermediates specific to the harmonic\n   * Schwarzschild solution\n   */\n  struct internal_tags {\n    /*!\n     * \\brief Tag for the position of a point relative to the center of the\n     * black hole\n     *\n     * \\details Defined as \\f$X^i = \\left(x^i - C^i\\right)\\f$, where \\f$C^i\\f$\n     * is the Cartesian coordinates of the center of the black hole\n     * and \\f$x^i\\f$ is the Cartesian coordinates of the point where we're\n     * wanting to compute spacetime quantities.\n     */\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using x_minus_center = ::Tags::TempI<0, 3, Frame, DataType>;\n    /*!\n     * \\brief Tag for the radius corresponding to the position of a point\n     * relative to the center of the black hole\n     *\n     * \\details Defined as \\f$r = \\sqrt{\\delta_{ij} X^i X^j}\\f$, where \\f$X^i\\f$\n     * is defined by `internal_tags::x_minus_center`.\n     */\n    template <typename DataType>\n    using r = ::Tags::TempScalar<1, DataType>;\n    /*!\n     * \\brief Tag for one over the radius corresponding to the position of a\n     * point relative to the center of the black hole\n     *\n     * \\details The quantity \\f$r\\f$ is the radius defined by\n     * `internal_tags::r`.\n     */\n    template <typename DataType>\n    using one_over_r = ::Tags::TempScalar<2, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\frac{X^i}{r}\\f$\n     *\n     * \\details The quantity \\f$X^i\\f$ is defined by\n     * `internal_tags::x_minus_center` and \\f$r\\f$ is the radius defined by\n     * `internal_tags::r`.\n     */\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using x_over_r = ::Tags::TempI<3, 3, Frame, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\frac{M}{r}\\f$\n     *\n     * \\details The quantity \\f$M\\f$ is the mass of the black hole and \\f$r\\f$\n     * is the radius defined by `internal_tags::r`.\n     */\n    template <typename DataType>\n    using m_over_r = ::Tags::TempScalar<4, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\sqrt{f_0} = 1 + \\frac{M}{r}\\f$\n     *\n     * \\details The quantity \\f$M\\f$ is the mass of the black hole, \\f$r\\f$ is\n     * the radius defined by `internal_tags::r`, and \\f$f_0\\f$ is defined by\n     * `internal_tags::f_0`.\n     */\n    template <typename DataType>\n    using sqrt_f_0 = ::Tags::TempScalar<5, DataType>;\n    /*!\n     * \\brief Tag for the intermediate\n     * \\f$f_0 = \\left(1 + \\frac{M}{r}\\right)^2\\f$\n     *\n     * \\details The quantity \\f$M\\f$ is the mass of the black hole and \\f$r\\f$\n     * is the radius defined by `internal_tags::r`.\n     */\n    template <typename DataType>\n    using f_0 = ::Tags::TempScalar<6, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\frac{2M}{M+r}\\f$\n     *\n     * \\details The quantity \\f$M\\f$ is the mass of the black hole and \\f$r\\f$\n     * is the radius defined by `internal_tags::r`.\n     */\n    template <typename DataType>\n    using two_m_over_m_plus_r = ::Tags::TempScalar<7, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\left(\\frac{2M}{M+r}\\right)^2\\f$\n     *\n     * \\details The quantity \\f$M\\f$ is the mass of the black hole and \\f$r\\f$\n     * is the radius defined by `internal_tags::r`.\n     */\n    template <typename DataType>\n    using two_m_over_m_plus_r_squared = ::Tags::TempScalar<8, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\left(\\frac{2M}{M+r}\\right)^3\\f$\n     *\n     * \\details The quantity \\f$M\\f$ is the mass of the black hole and \\f$r\\f$\n     * is the radius defined by `internal_tags::r`.\n     */\n    template <typename DataType>\n    using two_m_over_m_plus_r_cubed = ::Tags::TempScalar<9, DataType>;\n    /*!\n     * \\brief Tag for the \\f$\\gamma_{rr}\\f$ component of the spatial metric\n     *\n     * \\details Defined as\n     *\n     * \\f{align}\n     *     \\gamma_{rr} &= 1 + \\frac{2M}{M+r} + \\left(\\frac{2M}{M+r}\\right)^2\n     *         + \\left(\\frac{2M}{M+r}\\right)^3\n     * \\f}\n     *\n     * where \\f$M\\f$ is the mass of the black hole and \\f$r\\f$ is the radius\n     * defined by `internal_tags::r`.\n     */\n    template <typename DataType>\n    using spatial_metric_rr = ::Tags::TempScalar<10, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\frac{1}{\\gamma_{rr}}\\f$\n     *\n     * \\details The quantity \\f$\\gamma_{rr}\\f$ is defined by\n     * `internal_tags::spatial_metric_rr`.\n     */\n    template <typename DataType>\n    using one_over_spatial_metric_rr = ::Tags::TempScalar<11, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\gamma_{rr} - f_0\\f$\n     *\n     * \\details The quantity \\f$\\gamma_{rr}\\f$ is defined by\n     * `internal_tags::spatial_metric_rr` and \\f$f_0\\f$ is defined by\n     * `internal_tags::f_0`.\n     */\n    template <typename DataType>\n    using spatial_metric_rr_minus_f_0 = ::Tags::TempScalar<12, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\partial_r \\gamma_{rr}\\f$\n     *\n     * \\details Defined as\n     *\n     * \\f{align}\n     *     \\partial_r \\gamma_{rr} &= -\\frac{1}{2M}\\left(\\frac{2M}{M+r}\\right)^2\n     *         -\\frac{1}{M}\\left(\\frac{2M}{M+r}\\right)^3\n     *         -\\frac{3}{2M}\\left(\\frac{2M}{M+r}\\right)^4\n     * \\f}\n     *\n     * where \\f$M\\f$ is the mass of the black hole and \\f$r\\f$ is the radius\n     * defined by `internal_tags::r`.\n     */\n    template <typename DataType>\n    using d_spatial_metric_rr = ::Tags::TempScalar<13, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\partial_r f_0\\f$\n     *\n     * \\details Defined as\n     *\n     * \\f{align}\n     *     \\partial_r f_0 &=\n     *         2 \\left(1+\\frac{M}{r}\\right)\\left(-\\frac{M}{r^2}\\right)\n     * \\f}\n     *\n     * where \\f$M\\f$ is the mass of the black hole and \\f$r\\f$ is the radius\n     * defined by `internal_tags::r`.\n     */\n    template <typename DataType>\n    using d_f_0 = ::Tags::TempScalar<14, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$\\partial_r f_0 \\frac{X_i}{r}\\f$\n     *\n     * \\details The quantity \\f$r\\f$ is the radius defined by\n     * `internal_tags::r`, \\f$\\partial_r f_0\\f$ is defined by\n     * `internal_tags::d_f_0`, and \\f$X_j = X^i \\delta_{ij}\\f$ where \\f$X^i\\f$\n     * is defined by `internal_tags::x_minus_center`.\n     */\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using d_f_0_times_x_over_r = ::Tags::Tempi<15, 3, Frame, DataType>;\n    /*!\n     * \\brief Tag for the intermediate\n     * \\f$f_1 = \\frac{1}{r} \\left(\\gamma_{rr} - f_0\\right)\\f$\n     *\n     * \\details The quantity \\f$r\\f$ is the radius defined by\n     * `internal_tags::r`, \\f$\\gamma_{rr}\\f$ is defined by\n     * `internal_tags::spatial_metric_rr`, and \\f$f_0\\f$ is defined by\n     * `internal_tags::f_0`.\n     */\n    template <typename DataType>\n    using f_1 = ::Tags::TempScalar<16, DataType>;\n    /*!\n     * \\brief Tag for the intermediate \\f$f_1 \\frac{X_i}{r}\\f$\n     *\n     * \\details The quantity \\f$r\\f$ is the radius defined by\n     * `internal_tags::r`, \\f$f_1\\f$ is defined by `internal_tags::f_1`, and\n     * \\f$X_j = X^i \\delta_{ij}\\f$ where \\f$X^i\\f$ is defined by\n     * `internal_tags::x_minus_center`.\n     */\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using f_1_times_x_over_r = ::Tags::Tempi<17, 3, Frame, DataType>;\n    /*!\n     * \\brief Tag for the intermediate\n     * \\f$f_2 = \\partial_r \\gamma_{rr} - \\partial_r f_0 - 2 f_1\\f$\n     *\n     * \\details The quantity \\f$\\partial_r \\gamma_{rr}\\f$ is defined by\n     * `internal_tags::d_spatial_metric_rr`, \\f$\\partial_r f_0\\f$ is defined by\n     * `internal_tags::d_f_0`, and \\f$f_1\\f$ is defined by `internal_tags::f_1`.\n     */\n    template <typename DataType>\n    using f_2 = ::Tags::TempScalar<18, DataType>;\n    /*!\n     * \\brief Tag for the intermediate\n     * \\f$f_2 \\frac{X_i}{r} \\frac{X_j}{r} \\frac{X_k}{r}\\f$\n     *\n     * \\details The quantity \\f$r\\f$ is the radius defined by\n     * `internal_tags::r`, \\f$f_2\\f$ is defined by `internal_tags::f_2`, and\n     * \\f$X_j = X^i \\delta_{ij}\\f$ where \\f$X^i\\f$ is defined by\n     * `internal_tags::x_minus_center`.\n     */\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using f_2_times_xxx_over_r_cubed = ::Tags::Tempiii<19, 3, Frame, DataType>;\n    /*!\n     * \\brief Tag for the intermediate\n     * \\f$f_3 = \\frac{1}{r}\\frac{1}{\\gamma_{rr}}\\left(\\frac{2M}{M+r}\\right)^2\\f$\n     *\n     * \\details The quantity \\f$M\\f$ is the mass of the black hole, \\f$r\\f$ is\n     * the radius defined by `internal_tags::r`, and \\f$\\gamma_{rr}\\f$ is\n     * defined by `internal_tags::spatial_metric_rr`.\n     */\n    template <typename DataType>\n    using f_3 = ::Tags::TempScalar<20, DataType>;\n    /*!\n     * \\brief Tag for the intermediate\n     *\n     * \\f{align}\n     *     f_4 &=\n     *         -f_3 -\n     *         \\frac{1}{M} \\frac{1}{\\gamma_{rr}}\n     *             \\left(\\frac{2M}{M+r}\\right)^3 -\n     *         \\partial_r \\gamma_{rr} \\left(\\frac{2 M}{M+r}\n     *             \\frac{1}{\\gamma_{rr}}\\right)^2\n     * \\f}\n     *\n     * \\details The quantity \\f$M\\f$ is the mass of the black hole, \\f$r\\f$ is\n     * the radius defined by `internal_tags::r`, \\f$f_3\\f$ is defined by\n     * `internal_tags::f_3`, \\f$\\gamma_{rr}\\f$ is defined by\n     * `internal_tags::spatial_metric_rr`, and its derivative\n     * \\f$\\partial_r \\gamma_{rr}\\f$ is defined by\n     * `internal_tags::d_spatial_metric_rr`.\n     */\n    template <typename DataType>\n    using f_4 = ::Tags::TempScalar<21, DataType>;\n    /*!\n     * \\brief Tag for one over the determinant of the spatial metric\n     */\n    template <typename DataType>\n    using one_over_det_spatial_metric = ::Tags::TempScalar<22, DataType>;\n    /*!\n     * \\brief Tag for the intermediate\n     * \\f$-\\frac{1}{2} \\gamma_{rr}^{-3/2} \\partial_r \\gamma_{rr}\\f$\n     *\n     * \\details The lapse is defined as \\f$\\alpha = \\gamma_{rr}^{-1/2}\\f$,\n     * \\f$\\gamma_{rr}\\f$ is defined by `internal_tags::spatial_metric_rr` and\n     * its derivative \\f$\\partial_r \\gamma_{rr}\\f$ is defined by\n     * `internal_tags::d_spatial_metric_rr`.\n     */\n    template <typename DataType>\n    using neg_half_lapse_cubed_times_d_spatial_metric_rr =\n        ::Tags::TempScalar<23, DataType>;\n  };\n\n  /*!\n   * \\brief Buffer for caching computed intermediates and quantities that we do\n   * not want to recompute across the solution's implementation\n   *\n   * \\details See `internal_tags` documentation for details on what quantities\n   * the internal tags represent\n   */\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using CachedBuffer = CachedTempBuffer<\n      internal_tags::x_minus_center<DataType, Frame>,\n      internal_tags::r<DataType>, internal_tags::one_over_r<DataType>,\n      internal_tags::x_over_r<DataType, Frame>,\n      internal_tags::m_over_r<DataType>, internal_tags::sqrt_f_0<DataType>,\n      internal_tags::f_0<DataType>,\n      internal_tags::two_m_over_m_plus_r<DataType>,\n      internal_tags::two_m_over_m_plus_r_squared<DataType>,\n      internal_tags::two_m_over_m_plus_r_cubed<DataType>,\n      internal_tags::spatial_metric_rr<DataType>,\n      internal_tags::one_over_spatial_metric_rr<DataType>,\n      internal_tags::spatial_metric_rr_minus_f_0<DataType>,\n      internal_tags::d_spatial_metric_rr<DataType>,\n      internal_tags::d_f_0<DataType>,\n      internal_tags::d_f_0_times_x_over_r<DataType, Frame>,\n      internal_tags::f_1<DataType>,\n      internal_tags::f_1_times_x_over_r<DataType, Frame>,\n      internal_tags::f_2<DataType>,\n      internal_tags::f_2_times_xxx_over_r_cubed<DataType, Frame>,\n      internal_tags::f_3<DataType>, internal_tags::f_4<DataType>,\n      gr::Tags::Lapse<DataType>,\n      internal_tags::neg_half_lapse_cubed_times_d_spatial_metric_rr<DataType>,\n      gr::Tags::Shift<DataType, 3, Frame>, DerivShift<DataType, Frame>,\n      gr::Tags::SpatialMetric<DataType, 3, Frame>,\n      DerivSpatialMetric<DataType, Frame>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>,\n      gr::Tags::DetSpatialMetric<DataType>,\n      internal_tags::one_over_det_spatial_metric<DataType>>;\n\n  /*!\n   * \\brief Computes the intermediates and quantities that we do not want to\n   * recompute across the solution's implementation\n   */\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  class IntermediateComputer {\n   public:\n    using CachedBuffer = HarmonicSchwarzschild::CachedBuffer<DataType, Frame>;\n\n    /*!\n     * \\brief Constructs a computer for spacetime quantities of a given\n     * `gr::Solutions::HarmonicSchwarzschild` solution at at a specific\n     * Cartesian position\n     *\n     * \\param solution the given `gr::Solutions::HarmonicSchwarzschild` solution\n     * \\param x Cartesian coordinates of the position at which to compute\n     * spacetime quantities\n     */\n    IntermediateComputer(const HarmonicSchwarzschild& solution,\n                         const tnsr::I<DataType, 3, Frame>& x);\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::x_minus_center`\n     */\n    void operator()(\n        gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center,\n        gsl::not_null<CachedBuffer*> /*cache*/,\n        internal_tags::x_minus_center<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the radius defined by `internal_tags::r`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> r,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::r<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::one_over_r`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> one_over_r,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::one_over_r<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::x_over_r`\n     */\n    void operator()(gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_over_r,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::x_over_r<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::m_over_r`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> m_over_r,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::m_over_r<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::sqrt_f_0`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> sqrt_f_0,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::sqrt_f_0<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::f_0`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> f_0,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::f_0<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::two_m_over_m_plus_r`\n     */\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> two_m_over_m_plus_r,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::two_m_over_m_plus_r<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::two_m_over_m_plus_r_squared`\n     */\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> two_m_over_m_plus_r_squared,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::two_m_over_m_plus_r_squared<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::two_m_over_m_plus_r_cubed`\n     */\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> two_m_over_m_plus_r_cubed,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::two_m_over_m_plus_r_cubed<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::spatial_metric_rr`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> spatial_metric_rr,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::spatial_metric_rr<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::one_over_spatial_metric_rr`\n     */\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> one_over_spatial_metric_rr,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::one_over_spatial_metric_rr<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::spatial_metric_rr_minus_f_0`\n     */\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> spatial_metric_rr_minus_f_0,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::spatial_metric_rr_minus_f_0<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::d_spatial_metric_rr`\n     */\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> d_spatial_metric_rr,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::d_spatial_metric_rr<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::d_f_0`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> d_f_0,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::d_f_0<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::d_f_0_times_x_over_r`\n     */\n    void operator()(\n        gsl::not_null<tnsr::i<DataType, 3, Frame>*> d_f_0_times_x_over_r,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::d_f_0_times_x_over_r<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::f_1`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> f_1,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::f_1<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::f_1_times_x_over_r`\n     */\n    void operator()(\n        gsl::not_null<tnsr::i<DataType, 3, Frame>*> f_1_times_x_over_r,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::f_1_times_x_over_r<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::f_2`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> f_2,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::f_2<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::f_2_times_xxx_over_r_cubed`\n     */\n    void operator()(\n        gsl::not_null<tnsr::iii<DataType, 3, Frame>*>\n            f_2_times_xxx_over_r_cubed,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::f_2_times_xxx_over_r_cubed<DataType, Frame> /*meta*/)\n        const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::f_3`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> f_3,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::f_3<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by `internal_tags::f_4`\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> f_4,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::f_4<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the lapse\n     *\n     * \\details Computed as\n     *\n     * \\f{align}\n     *     \\alpha &= \\gamma_{rr}^{-1/2}\n     * \\f}\n     *\n     * where \\f$\\gamma_{rr}\\f$ is a component of the spatial metric defined by\n     * `internal_tags::spatial_metric_rr`.\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> lapse,\n                    gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::Lapse<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the intermediate defined by\n     * `internal_tags::neg_half_lapse_cubed_times_d_spatial_metric_rr`\n     */\n    void operator()(\n        gsl::not_null<Scalar<DataType>*>\n            neg_half_lapse_cubed_times_d_spatial_metric_rr,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::neg_half_lapse_cubed_times_d_spatial_metric_rr<\n            DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the shift\n     *\n     * \\details Computed as\n     *\n     * \\f{align}\n     *     \\beta^i &= \\left(\\frac{2M}{M+r}\\right)^2 \\frac{X^i}{r}\n     *         \\frac{1}{\\gamma_{rr}}\n     * \\f}\n     *\n     * where \\f$M\\f$ is the mass of the black hole, \\f$r\\f$ is the radius\n     * defined by `internal_tags::r`, \\f$X^i\\f$ is defined by\n     * `internal_tags::x_minus_center`, and \\f$\\gamma_{rr}\\f$ is defined by\n     * `internal_tags::spatial_metric_rr`.\n     */\n    void operator()(gsl::not_null<tnsr::I<DataType, 3, Frame>*> shift,\n                    gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::Shift<DataType, 3, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the spatial derivative of the shift\n     *\n     * \\details Computed as\n     *\n     * \\f{align}\n     *     \\partial_k \\beta^i &=\n     *         f_4 \\frac{X^i}{r} \\frac{X_k}{r} + \\delta_k^i f_3\n     * \\f}\n     *\n     * where \\f$r\\f$ is the radius defined by `internal_tags::r`, \\f$X^i\\f$ is\n     * defined by `internal_tags::x_minus_center`, \\f$X_j = X^i \\delta_{ij}\\f$,\n     * \\f$f_3\\f$ is defined by `internal_tags::f_3`, and \\f$f_4\\f$ is defined by\n     * `internal_tags::f_4`.\n     */\n    void operator()(gsl::not_null<tnsr::iJ<DataType, 3, Frame>*> deriv_shift,\n                    gsl::not_null<CachedBuffer*> cache,\n                    DerivShift<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the spatial metric\n     *\n     * \\details Computed as\n     *\n     * \\f{align}\n     *     \\gamma_{ij} &=\n     *         \\left(\\gamma_{rr} - \\left(1+\\frac{M}{r}\\right)^2\\right)\n     *             \\frac{X_i}{r} \\frac{X_j}{r} +\n     *         \\delta_{ij} \\left(1+\\frac{M}{r}\\right)^2\n     * \\f}\n     *\n     * where \\f$M\\f$ is the mass of the black hole, \\f$r\\f$ is the radius\n     * defined by `internal_tags::r`, \\f$\\gamma_{rr}\\f$ is defined by\n     * `internal_tags::spatial_metric_rr`, and \\f$X_j = X^i \\delta_{ij}\\f$ where\n     * \\f$X^i\\f$ is defined by `internal_tags::x_minus_center`.\n     */\n    void operator()(gsl::not_null<tnsr::ii<DataType, 3, Frame>*> spatial_metric,\n                    gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::SpatialMetric<DataType, 3, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the spatial derivative of the spatial metric\n     *\n     * \\details Computed as\n     *\n     * \\f{align}\n     *     \\partial_k \\gamma_{ij} &=\n     *         f_2 \\frac{X_i}{r} \\frac{X_j}{r} \\frac{X_k}{r} +\n     *         f_1 \\frac{X_j}{r} \\delta_{ik} + f_1 \\frac{X_i}{r} \\delta_{jk} +\n     *         \\partial_r f_0 \\frac{X_k}{r} \\delta_{ij}\n     * \\f}\n     *\n     * where \\f$r\\f$ is the radius defined by `internal_tags::r`,\n     * \\f$\\partial_r f_0\\f$ is defined by `internal_tags::d_f_0`, \\f$f_1\\f$ is\n     * defined by `internal_tags::f_1`, \\f$f_2\\f$ is defined by\n     * `internal_tags::f_2`, and \\f$X_j = X^i \\delta_{ij}\\f$ where \\f$X^i\\f$ is\n     * defined by `internal_tags::x_minus_center`.\n     */\n    void operator()(\n        gsl::not_null<tnsr::ijj<DataType, 3, Frame>*> deriv_spatial_metric,\n        gsl::not_null<CachedBuffer*> cache,\n        DerivSpatialMetric<DataType, Frame> /*meta*/) const;\n\n    /*!\n     * \\brief Sets the time derivative of the spatial metric to 0\n     */\n    void operator()(\n        gsl::not_null<tnsr::ii<DataType, 3, Frame>*> dt_spatial_metric,\n        gsl::not_null<CachedBuffer*> cache,\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>> /*meta*/) const;\n\n    /*!\n     * \\brief Computes the determinant of the spatial metric\n     */\n    void operator()(gsl::not_null<Scalar<DataType>*> det_spatial_metric,\n                    gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::DetSpatialMetric<DataType> /*meta*/) const;\n\n    /*!\n     * \\brief Computes one over the determinant of the spatial metric\n     */\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> one_over_det_spatial_metric,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::one_over_det_spatial_metric<DataType> /*meta*/) const;\n\n   private:\n    /*!\n     * \\brief The harmonic Schwarzschild solution\n     */\n    const HarmonicSchwarzschild& solution_;\n    /*!\n     * \\brief Cartesian coordinates of the position at which to compute\n     * spacetime quantities\n     */\n    const tnsr::I<DataType, 3, Frame>& x_;\n  };\n\n  /*!\n   * \\brief Computes and returns spacetime quantities of interest\n   */\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  class IntermediateVars : public CachedBuffer<DataType, Frame> {\n   public:\n    using CachedBuffer = HarmonicSchwarzschild::CachedBuffer<DataType, Frame>;\n    using CachedBuffer::CachedBuffer;\n    using CachedBuffer::get_var;\n\n    /*!\n     * \\brief Computes and returns the spatial derivative of the lapse\n     *\n     * \\details Computed as\n     *\n     * \\f{align}\n     *     \\partial_i \\alpha &=\n     *         -\\frac{1}{2} \\gamma_{rr}^{-3/2}\n     *         \\partial_r \\gamma_{rr} \\frac{X_i}{r}\n     * \\f}\n     *\n     * where \\f$r\\f$ is the radius defined by `internal_tags::r`,\n     * \\f$\\gamma_{rr}\\f$ is defined by `internal_tags::spatial_metric_rr`, its\n     * derivative \\f$\\partial_r \\gamma_{rr}\\f$ is defined by\n     * `internal_tags::d_spatial_metric_rr`, and \\f$X_j = X^i \\delta_{ij}\\f$\n     * where \\f$X^i\\f$ is defined by `internal_tags::x_minus_center`.\n     */\n    tnsr::i<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        DerivLapse<DataType, Frame> /*meta*/);\n\n    /*!\n     * \\brief Returns the time derivative of the lapse, which is 0\n     */\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/);\n\n    /*!\n     * \\brief Returns the time derivative of the shift, which is 0\n     */\n    tnsr::I<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>> /*meta*/);\n\n    /*!\n     * \\brief Computes and returns the square root of the determinant of the\n     * spatial metric\n     */\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/);\n\n    /*!\n     * \\brief Computes and returns the inverse spatial metric\n     */\n    tnsr::II<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::InverseSpatialMetric<DataType, 3, Frame> /*meta*/);\n\n    /*!\n     * \\brief Computes and returns the extrinsic curvature\n     */\n    tnsr::ii<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::ExtrinsicCurvature<DataType, 3, Frame> /*meta*/);\n  };\n\n private:\n  /*!\n   * \\brief Mass of the black hole\n   */\n  double mass_{std::numeric_limits<double>::signaling_NaN()};\n  /*!\n   * \\brief Center of the black hole\n   */\n  std::array<double, volume_dim> center_ =\n      make_array<volume_dim>(std::numeric_limits<double>::signaling_NaN());\n};\n\n/*!\n * \\brief Return whether two harmonic Schwarzschild solutions are equivalent\n */\nSPECTRE_ALWAYS_INLINE bool operator==(const HarmonicSchwarzschild& lhs,\n                                      const HarmonicSchwarzschild& rhs) {\n  return lhs.mass() == rhs.mass() and lhs.center() == rhs.center();\n}\n\n/*!\n * \\brief Return whether two harmonic Schwarzschild solutions are not equivalent\n */\nSPECTRE_ALWAYS_INLINE bool operator!=(const HarmonicSchwarzschild& lhs,\n                                      const HarmonicSchwarzschild& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace Solutions\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n\n#include <cmath>\n#include <numeric>\n#include <ostream>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/SpecialRelativity/LorentzBoostMatrix.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace gr::Solutions {\nKerrSchild::KerrSchild(CkMigrateMessage* /*msg*/) {}\n\nKerrSchild::KerrSchild(const double mass,\n                       const std::array<double, 3>& dimensionless_spin,\n                       const std::array<double, 3>& center,\n                       const std::array<double, 3>& boost_velocity,\n                       const Options::Context& context)\n    : mass_(mass),\n      dimensionless_spin_(dimensionless_spin),\n      center_(center),\n      boost_velocity_(boost_velocity),\n      zero_spin_(dimensionless_spin_ == std::array<double, 3>{{0., 0., 0.}}),\n      zero_velocity_(boost_velocity_ == std::array<double, 3>{{0., 0., 0.}}) {\n  const double spin_magnitude = magnitude(dimensionless_spin_);\n  if (spin_magnitude > 1.0) {\n    PARSE_ERROR(context, \"Spin magnitude must be < 1. Given spin: \"\n                             << dimensionless_spin_ << \" with magnitude \"\n                             << spin_magnitude);\n  }\n  if (mass_ < 0.0) {\n    PARSE_ERROR(context, \"Mass must be non-negative. Given mass: \" << mass_);\n  }\n}\n\nvoid KerrSchild::pup(PUP::er& p) {\n  p | mass_;\n  p | dimensionless_spin_;\n  p | center_;\n  p | boost_velocity_;\n  p | zero_spin_;\n  p | zero_velocity_;\n}\n\ntemplate <typename DataType, typename Frame>\nKerrSchild::IntermediateComputer<DataType, Frame>::IntermediateComputer(\n    const KerrSchild& solution, const tnsr::I<DataType, 3, Frame>& x)\n    : solution_(solution), x_(x) {}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center,\n    const gsl::not_null<CachedBuffer*> /*cache*/,\n    internal_tags::x_minus_center_unboosted<DataType, Frame> /*meta*/) const {\n  *x_minus_center = x_;\n  for (size_t d = 0; d < 3; ++d) {\n    x_minus_center->get(d) -= gsl::at(solution_.center(), d);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center_boosted,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::x_minus_center<DataType, Frame> /*meta*/) const {\n  const auto& x_minus_center = cache->get_var(\n      *this, internal_tags::x_minus_center_unboosted<DataType, Frame>{});\n  // Boost the coordinates to the rest frame\n  sr::lorentz_boost(x_minus_center_boosted, x_minus_center, 0.,\n                    solution_.boost_velocity());\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> a_dot_x,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::a_dot_x<DataType> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n\n  const auto spin_a = solution_.dimensionless_spin() * solution_.mass();\n  get(*a_dot_x) = spin_a[0] * get<0>(x_minus_center) +\n                  spin_a[1] * get<1>(x_minus_center) +\n                  spin_a[2] * get<2>(x_minus_center);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> a_dot_x_squared,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::a_dot_x_squared<DataType> /*meta*/) const {\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n\n  get(*a_dot_x_squared) = square(a_dot_x);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> half_xsq_minus_asq,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::half_xsq_minus_asq<DataType> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n\n  const auto spin_a = solution_.dimensionless_spin() * solution_.mass();\n  const auto a_squared =\n      std::inner_product(spin_a.begin(), spin_a.end(), spin_a.begin(), 0.);\n\n  get(*half_xsq_minus_asq) =\n      0.5 * (square(get<0>(x_minus_center)) + square(get<1>(x_minus_center)) +\n             square(get<2>(x_minus_center)) - a_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> r_squared,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::r_squared<DataType> /*meta*/) const {\n  if (solution_.zero_spin()) {\n    const auto& x_minus_center =\n        cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n    dot_product(r_squared, x_minus_center, x_minus_center);\n    return;\n  }\n  const auto& half_xsq_minus_asq =\n      get(cache->get_var(*this, internal_tags::half_xsq_minus_asq<DataType>{}));\n  const auto& a_dot_x_squared =\n      get(cache->get_var(*this, internal_tags::a_dot_x_squared<DataType>{}));\n\n  get(*r_squared) =\n      half_xsq_minus_asq + sqrt(square(half_xsq_minus_asq) + a_dot_x_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::r<DataType> /*meta*/) const {\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n\n  get(*r) = sqrt(r_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> a_dot_x_over_rsquared,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::a_dot_x_over_rsquared<DataType> /*meta*/) const {\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n\n  get(*a_dot_x_over_rsquared) = a_dot_x / r_squared;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> deriv_log_r_denom,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_log_r_denom<DataType> /*meta*/) const {\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n  const auto& half_xsq_minus_asq =\n      get(cache->get_var(*this, internal_tags::half_xsq_minus_asq<DataType>{}));\n\n  get(*deriv_log_r_denom) = 0.5 / (r_squared - half_xsq_minus_asq);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3, Frame>*> deriv_log_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_log_r<DataType, Frame> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& a_dot_x_over_rsquared = get(\n      cache->get_var(*this, internal_tags::a_dot_x_over_rsquared<DataType>{}));\n  const auto& deriv_log_r_denom =\n      get(cache->get_var(*this, internal_tags::deriv_log_r_denom<DataType>{}));\n\n  const auto spin_a = solution_.dimensionless_spin() * solution_.mass();\n  for (size_t i = 0; i < 3; ++i) {\n    deriv_log_r->get(i) =\n        deriv_log_r_denom *\n        (x_minus_center.get(i) + gsl::at(spin_a, i) * a_dot_x_over_rsquared);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> H_denom,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::H_denom<DataType> /*meta*/) const {\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n  const auto& a_dot_x_squared =\n      get(cache->get_var(*this, internal_tags::a_dot_x_squared<DataType>{}));\n\n  get(*H_denom) = 1.0 / (square(r_squared) + a_dot_x_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> H,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::H<DataType> /*meta*/) const {\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n  const auto& H_denom =\n      get(cache->get_var(*this, internal_tags::H_denom<DataType>{}));\n\n  get(*H) = solution_.mass() * r * r_squared * H_denom;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> deriv_H_temp1,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_H_temp1<DataType> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n  const auto& H_denom =\n      get(cache->get_var(*this, internal_tags::H_denom<DataType>{}));\n\n  get(*deriv_H_temp1) = H * (3.0 - 4.0 * square(r_squared) * H_denom);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> deriv_H_temp2,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_H_temp2<DataType> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& H_denom =\n      get(cache->get_var(*this, internal_tags::H_denom<DataType>{}));\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n\n  get(*deriv_H_temp2) = H * (2.0 * H_denom * a_dot_x);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::a<DataType, 3, Frame>*> deriv_H,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_H_unboosted<DataType, Frame> /*meta*/) const {\n  const auto& deriv_log_r =\n      cache->get_var(*this, internal_tags::deriv_log_r<DataType, Frame>{});\n  const auto& deriv_H_temp1 =\n      get(cache->get_var(*this, internal_tags::deriv_H_temp1<DataType>{}));\n  const auto& deriv_H_temp2 =\n      get(cache->get_var(*this, internal_tags::deriv_H_temp2<DataType>{}));\n\n  const auto spin_a = solution_.dimensionless_spin() * solution_.mass();\n  get<0>(*deriv_H) = 0.;\n  for (size_t i = 0; i < 3; ++i) {\n    deriv_H->get(i + 1) =\n        deriv_H_temp1 * deriv_log_r.get(i) - deriv_H_temp2 * gsl::at(spin_a, i);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::a<DataType, 3, Frame>*> deriv_H_boosted,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_H<DataType, Frame> /*meta*/) const {\n  const auto& deriv_H = cache->get_var(\n      *this, internal_tags::deriv_H_unboosted<DataType, Frame>{});\n  sr::lorentz_boost(deriv_H_boosted, deriv_H, -solution_.boost_velocity());\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> denom,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::denom<DataType> /*meta*/) const {\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n\n  const auto spin_a = solution_.dimensionless_spin() * solution_.mass();\n  const auto a_squared =\n      std::inner_product(spin_a.begin(), spin_a.end(), spin_a.begin(), 0.);\n\n  get(*denom) = 1.0 / (r_squared + a_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> a_dot_x_over_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::a_dot_x_over_r<DataType> /*meta*/) const {\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n\n  get(*a_dot_x_over_r) = a_dot_x / r;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::a<DataType, 3, Frame>*> null_form,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::null_form_unboosted<DataType, Frame> /*meta*/) const {\n  const auto& a_dot_x_over_r =\n      get(cache->get_var(*this, internal_tags::a_dot_x_over_r<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& denom =\n      get(cache->get_var(*this, internal_tags::denom<DataType>{}));\n\n  const auto spin_a = solution_.dimensionless_spin() * solution_.mass();\n  get<0>(*null_form) = 1.;\n  for (size_t i = 0; i < 3; ++i) {\n    const size_t cross_product_index_1 = (i + 1) % 3;\n    const size_t cross_product_index_2 = (i + 2) % 3;\n    null_form->get(i + 1) =\n        denom * (r * x_minus_center.get(i) -\n                 gsl::at(spin_a, cross_product_index_1) *\n                     x_minus_center.get(cross_product_index_2) +\n                 gsl::at(spin_a, cross_product_index_2) *\n                     x_minus_center.get(cross_product_index_1) +\n                 a_dot_x_over_r * gsl::at(spin_a, i));\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::a<DataType, 3, Frame>*> null_form_boosted,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::null_form<DataType, Frame> /*meta*/) const {\n  const auto& null_form = cache->get_var(\n      *this, internal_tags::null_form_unboosted<DataType, Frame>{});\n  sr::lorentz_boost(null_form_boosted, null_form, -solution_.boost_velocity());\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ab<DataType, 3, Frame>*> deriv_null_form,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_null_form_unboosted<DataType, Frame> /*meta*/) const {\n  const auto spin_a = solution_.dimensionless_spin() * solution_.mass();\n  const auto& denom =\n      get(cache->get_var(*this, internal_tags::denom<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& null_form = cache->get_var(\n      *this, internal_tags::null_form_unboosted<DataType, Frame>{});\n  const auto& a_dot_x_over_rsquared = get(\n      cache->get_var(*this, internal_tags::a_dot_x_over_rsquared<DataType>{}));\n  const auto& deriv_log_r =\n      cache->get_var(*this, internal_tags::deriv_log_r<DataType, Frame>{});\n\n  for (size_t d = 0; d < 4; ++d) {\n    deriv_null_form->get(d, 0) = 0.;\n    deriv_null_form->get(0, d) = 0.;\n  }\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      deriv_null_form->get(j + 1, i + 1) =\n          denom * (gsl::at(spin_a, i) * gsl::at(spin_a, j) / r +\n                   (x_minus_center.get(i) - 2.0 * r * null_form.get(i + 1) -\n                    a_dot_x_over_rsquared * gsl::at(spin_a, i)) *\n                       deriv_log_r.get(j) * r);\n      if (i == j) {\n        deriv_null_form->get(j + 1, i + 1) += denom * r;\n      } else {  //  add denom*epsilon^ijk a_k\n        size_t k = (j + 1) % 3;\n        if (k == i) {  // j+1 = i (cyclic), so choose minus sign\n          k++;\n          k = k % 3;  // and set k to be neither i nor j\n          deriv_null_form->get(j + 1, i + 1) -= denom * gsl::at(spin_a, k);\n        } else {  // i+1 = j (cyclic), so choose plus sign\n          deriv_null_form->get(j + 1, i + 1) += denom * gsl::at(spin_a, k);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ab<DataType, 3, Frame>*> deriv_null_form_boosted,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_null_form<DataType, Frame> /*meta*/) const {\n  const auto& deriv_null_form = cache->get_var(\n      *this, internal_tags::deriv_null_form_unboosted<DataType, Frame>{});\n  // Inverse-boost the first index, because it represents the derivative which\n  // is given in the boosted frame\n  sr::lorentz_boost(deriv_null_form_boosted, deriv_null_form,\n                    -solution_.boost_velocity());\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse_squared,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::lapse_squared<DataType> /*meta*/) const {\n  if (solution_.zero_spin() and solution_.zero_velocity()) {\n    const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n    get(*lapse_squared) = 1.0 / (1.0 + 2.0 * solution_.mass() / r);\n    return;\n  }\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  get(*lapse_squared) = 1.0 / (1.0 + 2.0 * square(get<0>(null_form)) * H);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::Lapse<DataType> /*meta*/) const {\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n  get(*lapse) = sqrt(lapse_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> deriv_lapse_multiplier,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_lapse_multiplier<DataType> /*meta*/) const {\n  const auto& lapse = get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n  get(*deriv_lapse_multiplier) = -lapse * lapse_squared;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> shift_multiplier,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::shift_multiplier<DataType> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n\n  get(*shift_multiplier) = 2.0 * get<0>(null_form) * H * lapse_squared;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> shift,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::Shift<DataType, 3, Frame> /*meta*/) const {\n  if (solution_.zero_spin() and solution_.zero_velocity()) {\n    const auto& x_minus_center =\n        cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n    const auto& r_squared =\n        get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n    const auto& lapse_squared =\n        get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n    for (size_t i = 0; i < 3; ++i) {\n      shift->get(i) = 2.0 * solution_.mass() * x_minus_center.get(i) *\n                      lapse_squared / r_squared;\n    }\n    return;\n  }\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  const auto& shift_multiplier =\n      get(cache->get_var(*this, internal_tags::shift_multiplier<DataType>{}));\n\n  for (size_t i = 0; i < 3; ++i) {\n    shift->get(i) = shift_multiplier * null_form.get(i + 1);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3, Frame>*> deriv_shift,\n    const gsl::not_null<CachedBuffer*> cache,\n    DerivShift<DataType, Frame> /*meta*/) const {\n  if (solution_.zero_spin() and solution_.zero_velocity()) {\n    const auto& x_minus_center =\n        cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n    const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n    const auto& r_squared =\n        get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n    const auto& lapse_squared =\n        get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        deriv_shift->get(i, j) =\n            (-4.0 * solution_.mass() * x_minus_center.get(i) *\n             x_minus_center.get(j) * lapse_squared *\n             (1 - solution_.mass() * lapse_squared / r)) /\n            square(r_squared);\n      }\n      deriv_shift->get(i, i) +=\n          2.0 * solution_.mass() * lapse_squared / r_squared;\n    }\n    return;\n  }\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n  const auto& deriv_H =\n      cache->get_var(*this, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& deriv_null_form =\n      cache->get_var(*this, internal_tags::deriv_null_form<DataType, Frame>{});\n\n  for (size_t m = 0; m < 3; ++m) {\n    for (size_t i = 0; i < 3; ++i) {\n      deriv_shift->get(m, i) =\n          2.0 * lapse_squared *\n              (get<0>(null_form) * null_form.get(i + 1) * deriv_H.get(m + 1) +\n               H * get<0>(null_form) * deriv_null_form.get(m + 1, i + 1) +\n               H * null_form.get(i + 1) * deriv_null_form.get(m + 1, 0)) -\n          4.0 * H * get<0>(null_form) * null_form.get(i + 1) *\n              square(lapse_squared) *\n              (square(get<0>(null_form)) * deriv_H.get(m + 1) +\n               2. * H * get<0>(null_form) * deriv_null_form.get(m + 1, 0));\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::SpatialMetric<DataType, 3, Frame> /*meta*/) const {\n  if (solution_.zero_spin() and solution_.zero_velocity()) {\n    const auto& x_minus_center =\n        cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n    const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n    const auto& r_squared =\n        get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        spatial_metric->get(i, j) = 2.0 * solution_.mass() *\n                                    x_minus_center.get(i) *\n                                    x_minus_center.get(j) / r_squared / r;\n      }\n      spatial_metric->get(i, i) += 1.0;\n    }\n    return;\n  }\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n\n  std::fill(spatial_metric->begin(), spatial_metric->end(), 0.);\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric->get(i, i) = 1.;\n    for (size_t j = i; j < 3; ++j) {  // Symmetry\n      spatial_metric->get(i, j) +=\n          2.0 * H * null_form.get(i + 1) * null_form.get(j + 1);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3, Frame>*> inverse_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::InverseSpatialMetric<DataType, 3, Frame> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  if (solution_.zero_spin() and solution_.zero_velocity()) {\n    const auto& x_minus_center =\n        cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n    const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n    const auto& r_squared =\n        get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        inverse_spatial_metric->get(i, j) =\n            -2.0 * solution_.mass() * x_minus_center.get(i) *\n            x_minus_center.get(j) /\n            (2 * solution_.mass() * r_squared + r_squared * r);\n      }\n      inverse_spatial_metric->get(i, i) += 1.0;\n    }\n    return;\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {  // Symmetry\n      inverse_spatial_metric->get(i, j) = -2.0 * H * lapse_squared *\n                                          null_form.get(i + 1) *\n                                          null_form.get(j + 1);\n    }\n    inverse_spatial_metric->get(i, i) += 1.;\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*> deriv_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    DerivSpatialMetric<DataType, Frame> /*meta*/) const {\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  const auto& deriv_H =\n      cache->get_var(*this, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& deriv_null_form =\n      cache->get_var(*this, internal_tags::deriv_null_form<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {  // Symmetry\n      for (size_t m = 0; m < 3; ++m) {\n        deriv_spatial_metric->get(m, i, j) =\n            2.0 * null_form.get(i + 1) * null_form.get(j + 1) *\n                deriv_H.get(m + 1) +\n            2.0 * H *\n                (null_form.get(i + 1) * deriv_null_form.get(m + 1, j + 1) +\n                 null_form.get(j + 1) * deriv_null_form.get(m + 1, i + 1));\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> dt_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>> /*meta*/) const {\n  if (solution_.zero_velocity()) {\n    std::fill(dt_spatial_metric->begin(), dt_spatial_metric->end(), 0.);\n    return;\n  }\n  const auto& ex_curvature =\n      cache->get_var(*this, gr::Tags::ExtrinsicCurvature<DataType, 3, Frame>{});\n  const auto& lapse = cache->get_var(*this, gr::Tags::Lapse<DataType>{});\n  const auto& shift =\n      cache->get_var(*this, gr::Tags::Shift<DataType, 3, Frame>{});\n  const auto& deriv_shift =\n      cache->get_var(*this, DerivShift<DataType, Frame>{});\n  const auto& spatial_metric =\n      cache->get_var(*this, gr::Tags::SpatialMetric<DataType, 3, Frame>{});\n  const auto& deriv_spatial_metric =\n      cache->get_var(*this, DerivSpatialMetric<DataType, Frame>{});\n    // Reconstruct from the extrinsic curvature and shift\n    gr::time_derivative_of_spatial_metric(dt_spatial_metric, lapse, shift,\n                                          deriv_shift, spatial_metric,\n                                          deriv_spatial_metric, ex_curvature);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> null_form_dot_deriv_H,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::null_form_dot_deriv_H<DataType> /*meta*/) const {\n  const auto& deriv_H =\n      cache->get_var(*this, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  get(*null_form_dot_deriv_H) = 0.0;\n  for (size_t i = 0; i < 3; ++i) {\n    get(*null_form_dot_deriv_H) += null_form.get(i + 1) * deriv_H.get(i + 1);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3, Frame>*>\n        null_form_dot_deriv_null_form,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::null_form_dot_deriv_null_form<DataType, Frame> /*meta*/)\n    const {\n  const auto& deriv_null_form =\n      cache->get_var(*this, internal_tags::deriv_null_form<DataType, Frame>{});\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  for (size_t j = 0; j < 3; ++j) {\n    null_form_dot_deriv_null_form->get(j) = 0.0;\n    for (size_t i = 0; i < 3; ++i) {\n      null_form_dot_deriv_null_form->get(j) +=\n          null_form.get(i + 1) * deriv_null_form.get(i + 1, j + 1);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> extrinsic_curvature,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::ExtrinsicCurvature<DataType, 3, Frame> /*meta*/) const {\n  const auto& lapse = cache->get_var(*this, gr::Tags::Lapse<DataType>{});\n  const auto& H = cache->get_var(*this, internal_tags::H<DataType>{});\n  const auto& deriv_H =\n      cache->get_var(*this, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& null_form =\n      cache->get_var(*this, internal_tags::null_form<DataType, Frame>{});\n  const auto& deriv_null_form =\n      cache->get_var(*this, internal_tags::deriv_null_form<DataType, Frame>{});\n  const auto& null_form_dot_deriv_H =\n      cache->get_var(*this, internal_tags::null_form_dot_deriv_H<DataType>{});\n  const auto& null_form_dot_deriv_null_form = cache->get_var(\n      *this, internal_tags::null_form_dot_deriv_null_form<DataType, Frame>{});\n  // We use a formula with l^0. However, since we actually use l_0, there is a\n  // sign difference. This can be seen by lowering the index with the\n  // Minkowski metric, which can be done for the null form given the form of the\n  // KerrSchild metric.\n  const double l0_relative_sign = -1.0;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      extrinsic_curvature->get(i, j) =\n          (-1.0 / get(lapse)) *\n              (null_form.get(i + 1) * null_form.get(j + 1) * get<0>(deriv_H) +\n               get(H) *\n                   (null_form.get(i + 1) * deriv_null_form.get(0, j + 1) +\n                    null_form.get(j + 1) * deriv_null_form.get(0, i + 1))) -\n          get(lapse) *\n              (get(H) *\n                   (l0_relative_sign * null_form.get(i + 1) *\n                        deriv_null_form.get(j + 1, 0) +\n                    l0_relative_sign * null_form.get(j + 1) *\n                        deriv_null_form.get(i + 1, 0) +\n                    l0_relative_sign * get<0>(null_form) *\n                        (deriv_null_form.get(i + 1, j + 1) +\n                         deriv_null_form.get(j + 1, i + 1) +\n                         2.0 * get(H) *\n                             (null_form.get(i + 1) *\n                                  null_form_dot_deriv_null_form.get(j) +\n                              null_form.get(j + 1) *\n                                  null_form_dot_deriv_null_form.get(i)) +\n                         2.0 * null_form.get(i + 1) * null_form.get(j + 1) *\n                             get(null_form_dot_deriv_H))) +\n               l0_relative_sign * get<0>(null_form) *\n                   (null_form.get(i + 1) * deriv_H.get(j + 1) +\n                    null_form.get(j + 1) * deriv_H.get(i + 1)));\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*>\n        spatial_christoffel_first_kind,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame> /*meta*/) const {\n  const auto& d_spatial_metric =\n      cache->get_var(*this, DerivSpatialMetric<DataType, Frame>{});\n  gr::christoffel_first_kind<3, Frame, IndexType::Spatial, DataType>(\n      spatial_christoffel_first_kind, d_spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid KerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ijj<DataType, 3, Frame>*>\n        spatial_christoffel_second_kind,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame> /*meta*/) const {\n  const auto& spatial_christoffel_first_kind = cache->get_var(\n      *this, gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>{});\n  const auto& inverse_spatial_metric = cache->get_var(\n      *this, gr::Tags::InverseSpatialMetric<DataType, 3, Frame>{});\n  raise_or_lower_first_index<DataType, SpatialIndex<3, UpLo::Lo, Frame>,\n                             SpatialIndex<3, UpLo::Lo, Frame>>(\n      spatial_christoffel_second_kind, spatial_christoffel_first_kind,\n      inverse_spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::i<DataType, 3, Frame>\nKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    DerivLapse<DataType, Frame> /*meta*/) {\n  if (computer.solution().zero_spin() and computer.solution().zero_velocity()) {\n    const auto& x_minus_center =\n        get_var(computer, internal_tags::x_minus_center<DataType, Frame>{});\n    const auto& r = get(get_var(computer, internal_tags::r<DataType>{}));\n    const auto& r_squared =\n        get(get_var(computer, internal_tags::r_squared<DataType>{}));\n\n    const auto& lapse = get(get_var(computer, gr::Tags::Lapse<DataType>{}));\n    const auto& lapse_squared =\n        get(get_var(computer, internal_tags::lapse_squared<DataType>{}));\n\n    tnsr::i<DataType, 3, Frame> d_lapse(get_size(r));\n    for (size_t i = 0; i < 3; ++i) {\n      d_lapse.get(i) = computer.solution().mass() * x_minus_center.get(i) *\n                       lapse * lapse_squared / r_squared / r;\n    }\n    return d_lapse;\n  }\n  tnsr::i<DataType, 3, Frame> result{};\n  const auto& H = get_var(computer, internal_tags::H<DataType>{});\n  const auto& deriv_H =\n      get_var(computer, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& deriv_lapse_multiplier =\n      get(get_var(computer, internal_tags::deriv_lapse_multiplier<DataType>{}));\n  const auto& null_form =\n      get_var(computer, internal_tags::null_form<DataType, Frame>{});\n  const auto& deriv_null_form =\n      get_var(computer, internal_tags::deriv_null_form<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    result.get(i) =\n        deriv_lapse_multiplier *\n        (square(get<0>(null_form)) * deriv_H.get(i + 1) +\n         2. * get(H) * get<0>(null_form) * deriv_null_form.get(i + 1, 0));\n  }\n  return result;\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType> KerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/) {\n  const auto& H = get(get_var(computer, internal_tags::H<DataType>{}));\n  return make_with_value<Scalar<DataType>>(H, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::I<DataType, 3, Frame>\nKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>> /*meta*/) {\n  const auto& H = get(get_var(computer, internal_tags::H<DataType>()));\n  return make_with_value<tnsr::I<DataType, 3, Frame>>(H, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType> KerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/) {\n  return Scalar<DataType>(1.0 /\n                          get(get_var(computer, gr::Tags::Lapse<DataType>{})));\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::i<DataType, 3, Frame>\nKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::DerivDetSpatialMetric<DataType, 3, Frame> /*meta*/) {\n  const auto& deriv_H =\n      get_var(computer, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& null_form =\n      get_var(computer, internal_tags::null_form<DataType, Frame>{});\n\n  auto result =\n      make_with_value<tnsr::i<DataType, 3, Frame>>(get<0>(deriv_H), 0.);\n  for (size_t i = 0; i < 3; ++i) {\n    result.get(i) = 2.0 * square(get<0>(null_form)) * deriv_H.get(i + 1);\n  }\n\n  return result;\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType> KerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) {\n  if (computer.solution().zero_spin() and computer.solution().zero_velocity()) {\n    const double m = computer.solution().mass();\n    const auto& r = get(get_var(computer, internal_tags::r<DataType>{}));\n    Scalar<DataType> result(get_size(r));\n    get(result) = 1. / ((2. * m + r) * r);\n    get(result) = sqrt(cube(result.get()));\n    get(result) *= 2. * m * (3. * m + r);\n    return result;\n  }\n  return trace(\n      get_var(computer, gr::Tags::ExtrinsicCurvature<DataType, 3, Frame>{}),\n      get_var(computer, gr::Tags::InverseSpatialMetric<DataType, 3, Frame>{}));\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::I<DataType, 3, Frame>\nKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3, Frame> /*meta*/) {\n  if (computer.solution().zero_spin() and computer.solution().zero_velocity()) {\n    const double m = computer.solution().mass();\n    const auto& r = get(get_var(computer, internal_tags::r<DataType>{}));\n    const auto& r_squared =\n        get(get_var(computer, internal_tags::r_squared<DataType>{}));\n    const auto& x_minus_center =\n        get_var(computer, internal_tags::x_minus_center<DataType, Frame>{});\n    DataType factor = m * (8. * m + 3. * r) / square(2. * m + r) / r_squared;\n    tnsr::I<DataType, 3, Frame> result(get_size(r));\n    for (size_t i = 0; i < 3; ++i) {\n      result.get(i) = factor * x_minus_center.get(i);\n    }\n    return result;\n  }\n  const auto& inverse_spatial_metric =\n      get_var(computer, gr::Tags::InverseSpatialMetric<DataType, 3, Frame>{});\n  const auto& spatial_christoffel_second_kind = get_var(\n      computer, gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>{});\n  return trace_last_indices<DataType, SpatialIndex<3, UpLo::Up, Frame>,\n                            SpatialIndex<3, UpLo::Lo, Frame>>(\n      spatial_christoffel_second_kind, inverse_spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::Abb<DataType, 3, Frame>\nKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame> /*meta*/) {\n  ASSERT(\n      computer.solution().zero_velocity(),\n      \"The tag gr::Tags::SpacetimeChristoffelSecondKind is not supported for a \"\n      \"boosted KerrSchild solution.\");\n  const auto& lapse = get_var(computer, gr::Tags::Lapse<DataType>{});\n  const auto& shift = get_var(computer, gr::Tags::Shift<DataType, 3, Frame>{});\n  const auto& spatial_metric =\n      get_var(computer, gr::Tags::SpatialMetric<DataType, 3, Frame>{});\n  const auto& inverse_spatial_metric =\n      get_var(computer, gr::Tags::InverseSpatialMetric<DataType, 3, Frame>{});\n  const auto& dt_lapse =\n      get_var(computer, ::Tags::dt<gr::Tags::Lapse<DataType>>{});\n  const auto& di_lapse = get_var(\n      computer,\n      ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>, Frame>{});\n  const auto& dt_shift =\n      get_var(computer, ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>>{});\n  const auto& di_shift =\n      get_var(computer, ::Tags::deriv<gr::Tags::Shift<DataType, 3, Frame>,\n                                      tmpl::size_t<3>, Frame>{});\n  const auto& dt_spatial_metric = get_var(\n      computer, ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>{});\n  const auto& di_spatial_metric = get_var(\n      computer, ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3, Frame>,\n                              tmpl::size_t<3>, Frame>{});\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto d_spacetime_metric = gr::derivatives_of_spacetime_metric(\n      lapse, dt_lapse, di_lapse, shift, dt_shift, di_shift, spatial_metric,\n      dt_spatial_metric, di_spatial_metric);\n  return gr::christoffel_second_kind(d_spacetime_metric,\n                                     inverse_spacetime_metric);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template class KerrSchild::IntermediateVars<DTYPE(data), FRAME(data)>; \\\n  template class KerrSchild::IntermediateComputer<DTYPE(data), FRAME(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, double),\n                        (::Frame::Grid, ::Frame::Inertial, ::Frame::Distorted))\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\nnamespace Solutions {\n\n/*!\n * \\brief Kerr black hole in Kerr-Schild coordinates\n *\n * \\details\n * The metric is \\f$g_{\\mu\\nu} = \\eta_{\\mu\\nu} + 2 H l_\\mu l_\\nu\\f$,\n * where \\f$\\eta_{\\mu\\nu}\\f$ is the Minkowski metric, \\f$H\\f$ is a scalar\n * function, and \\f$l_\\mu\\f$ is the outgoing null vector.\n * \\f$H\\f$ and \\f$l_\\mu\\f$ are known functions of the coordinates\n * and of the mass and spin vector.\n *\n * The following are input file options that can be specified:\n *  - Mass (default: 1.)\n *  - Center (default: {0,0,0})\n *  - Spin (default: {0,0,0})\n *\n * ## Kerr-Schild Coordinates\n *\n *\n * A Kerr-Schild coordinate system is defined by\n * \\f{equation}{\n * g_{\\mu\\nu}  \\equiv \\eta_{\\mu\\nu} + 2 H l_\\mu l_\\nu,\n * \\f}\n * where \\f$H\\f$ is a scalar function of the coordinates, \\f$\\eta_{\\mu\\nu}\\f$\n * is the Minkowski metric, and \\f$l^\\mu\\f$ is a null vector. Note that the\n * form of the metric along with the nullness of \\f$l^\\mu\\f$ allows one to\n * raise and lower indices of \\f$l^\\mu\\f$ using \\f$\\eta_{\\mu\\nu}\\f$, and\n * that \\f$l^t l^t = l_t l_t = l^i l_i\\f$.\n * Note also that\n * \\f{equation}{\n * g^{\\mu\\nu}  \\equiv \\eta^{\\mu\\nu} - 2 H l^\\mu l^\\nu,\n * \\f}\n * and that \\f$\\sqrt{-g}=1\\f$.\n * Also, \\f$l_\\mu\\f$ is a geodesic with respect to both the physical metric\n * and the Minkowski metric:\n * \\f{equation}{\n * l^\\mu \\partial_\\mu l_\\nu = l^\\mu\\nabla_\\mu l_\\nu = 0.\n * \\f}\n *\n *\n * The corresponding 3+1 quantities are\n * \\f{eqnarray}{\n * \\gamma_{i j}     &=& \\delta_{i j} + 2 H l_i l_j,\\\\\n * \\gamma^{i j}  &=& \\delta^{i j} - {2 H l^i l^j \\over 1+2H l^t l^t},\\\\\n * {\\rm det} \\gamma_{i j}&=& 1+2H l^t l^t,\\\\\n * \\partial_k ({\\rm det} \\gamma_{i j})&=& 2 l^t l^t \\partial_k H,\\\\\n * \\beta^i       &=& - {2 H l^t l^i \\over 1+2H l^t l^t},\\\\\n * N        &=& \\left(1+2 H l^t l^t\\right)^{-1/2},\\quad\\hbox{(lapse)}\\\\\n * \\alpha     &=& \\left(1+2 H l^t l^t\\right)^{-1},\n *                \\quad\\hbox{(densitized lapse)}\\\\\n * K_{i j}  &=& - \\left(1+2 H l^t l^t\\right)^{1/2}\n *       \\left[l_i l_j \\partial_{t} H + 2 H l_{(i} \\partial_{t} l_{j)}\\right]\n *                 \\nonumber \\\\\n *                 &&-2\\left(1+2 H l^t l^t\\right)^{-1/2}\n *                \\left[H l^t \\partial_{(i}l_{j)} + H l_{(i}\\partial_{j)}l^t\n *          + l^t l_{(i}\\partial_{j)} H + 2H^2 l^t l_{(i} l^k\\partial_{k}l_{j)}\n *          + H l^t l_i l_j l^k \\partial_{k} H\\right],\\\\\n * \\partial_{k}\\gamma_{i j}&=& 2 l_i l_j\\partial_{k} H +\n *    4 H l_{(i} \\partial_{k}l_{j)},\\\\\n * \\partial_{k}N   &=& -\\left(1+2 H l^t l^t\\right)^{-3/2}\n *                    \\left(l^tl^t\\partial_{k}H+2Hl^t\\partial_{k}l^t\\right),\\\\\n * \\partial_{k}\\beta^i  &=& - 2\\left(1+2H l^t l^t\\right)^{-1}\n *    \\left(l^tl^i\\partial_{k}H+Hl^t\\partial_{k}l^i+Hl^i\\partial_{k}l^t\\right)\n *                   + 4 H l^t l^i \\left(1+2H l^t l^t\\right)^{-2}\n *                     \\left(l^tl^t\\partial_{k}H+2Hl^t\\partial_{k}l^t\\right),\\\\\n * \\Gamma^k{}_{i j}&=& -\\delta^{k m}\\left(l_i l_j \\partial_{m}H\n *                                    + 2l_{(i} \\partial_{m}l_{j)} \\right)\n *                   + 2 H l_{(i}\\partial_{j)} l^k\n *          \\nonumber \\\\\n *                  &&+\\left(1+2 H l^t l^t\\right)^{-1}\n *                     \\left[2 l^k l_{(i}\\partial_{j)} H\n *                          +2 H l_i l_j l^k l^m \\partial_{m}H\n *                          +2 H l^k \\partial_{(i}l_{j)}\n *                          +4 H^2 l^k l_{(i} (l^m \\partial_{m}l_{j)}\n *                                      -\\partial_{j)} l^t)\n *                     \\right].\n * \\f}\n * Note that \\f$l^i\\f$ is **not** equal to \\f$\\gamma^{i j} l_j\\f$; it is equal\n * to \\f$g^{i \\mu} l_\\mu\\f$.\n *\n * ## Kerr Spacetime\n *\n * ### Spin in the z direction\n *\n * Assume Cartesian coordinates \\f$(t,x,y,z)\\f$. Then for stationary Kerr\n * spacetime with mass \\f$M\\f$ and angular momentum \\f$a M\\f$\n * in the \\f$z\\f$ direction,\n * \\f{eqnarray}{\n * H     &=& {M r^3 \\over r^4 + a^2 z^2},\\\\\n * l_\\mu &=&\n * \\left(1,{rx+ay\\over r^2+a^2},{ry-ax\\over r^2+a^2},{z\\over r}\\right),\n * \\f}\n * where \\f$r\\f$ is defined by\n * \\f{equation}{\n * \\label{eq:rdefinition1}\n * {x^2+y^2\\over a^2+r^2} + {z^2\\over r^2} = 1,\n * \\f}\n * or equivalently,\n * \\f{equation}{\n * r^2 = {1\\over 2}(x^2 + y^2 + z^2 - a^2)\n *      + \\left({1\\over 4}(x^2 + y^2 + z^2 - a^2)^2 + a^2 z^2\\right)^{1/2}.\n * \\f}\n *\n * Possibly useful formula:\n * \\f{equation}{\n * \\partial_{i} r = {x_i + z \\delta_{i z} \\displaystyle {a^2\\over r^2} \\over\n *   2 r\\left(1 - \\displaystyle {x^2 + y^2 + z^2 - a^2\\over 2 r^2}\\right)}.\n * \\f}\n *\n * ### Spin in an arbitrary direction\n *\n * For arbitrary spin direction, let \\f$\\vec{x}\\equiv (x,y,z)\\f$ and\n * \\f$\\vec{a}\\f$ be a flat-space three-vector with magnitude-squared\n * (\\f$\\delta_{ij}\\f$ norm) equal to \\f$a^2\\f$.\n * Then the Kerr-Schild quantities for Kerr spacetime are:\n * \\f{eqnarray}{\n * H       &=& {M r^3 \\over r^4 + (\\vec{a}\\cdot\\vec{x})^2},\\\\\n * \\vec{l} &=& {r\\vec{x}-\\vec{a}\\times\\vec{x}+(\\vec{a}\\cdot\\vec{x})\\vec{a}/r\n *              \\over r^2+a^2 },\\\\\n * l_t     &=& 1,\\\\\n * \\label{eq:rdefinition2}\n * r^2     &=& {1\\over 2}(\\vec{x}\\cdot\\vec{x}-a^2)\n *      + \\left({1\\over 4}(\\vec{x}\\cdot\\vec{x}-a^2)^2\n *              + (\\vec{a}\\cdot\\vec{x})^2\\right)^{1/2},\n * \\f}\n * where \\f$\\vec{l}\\equiv (l_x,l_y,l_z)\\f$, and\n * all dot and cross products are evaluated as flat-space 3-vector operations.\n *\n * Possibly useful formulae:\n * \\f{equation}{\n * \\partial_{i} r = {x_i + (\\vec{a}\\cdot\\vec{x})a_i/r^2 \\over\n *   2 r\\left(1 - \\displaystyle {\\vec{x}\\cdot\\vec{x}-a^2\\over 2 r^2}\\right)},\n * \\f}\n * \\f{equation}{\n * {\\partial_{i} H \\over H} =\n *  {3\\partial_{i}r\\over r} - {4 r^3 \\partial_{i}r +\n *                     2(\\vec{a}\\cdot\\vec{x})\\vec{a}\n *                     \\over r^4 + (\\vec{a}\\cdot\\vec{x})^2},\n * \\f}\n * \\f{equation}{\n * (r^2+a^2)\\partial_{j} l_i =\n *                 (x_i-2 r l_i-(\\vec{a}\\cdot\\vec{x})a_i/r^2)\\partial_{j}r\n *                 + r\\delta_{ij} + a_i a_j/r + \\epsilon^{ijk} a_k.\n * \\f}\n *\n * ## Cartesian and Spherical Coordinates for Kerr\n *\n * The Kerr-Schild coordinates are defined in terms of the Cartesian\n * coordinates \\f$(x,y,z)\\f$.  If one wishes to express Kerr-Schild\n * coordinates in terms of the spherical polar coordinates\n * \\f$(\\tilde{r},\\theta,\\phi)\\f$ then one can make the obvious and\n * usual transformation\n * \\f{equation}{\n * \\label{eq:sphertocartsimple}\n * x=\\tilde{r}\\sin\\theta\\cos\\phi,\\quad\n * y=\\tilde{r}\\sin\\theta\\sin\\phi,\\quad\n * z=\\tilde{r}\\cos\\theta.\n * \\f}\n *\n * This is simple, and has the advantage that in this coordinate system\n * for \\f$M\\to0\\f$, Kerr spacetime becomes Minkowski space in spherical\n * coordinates \\f$(\\tilde{r},\\theta,\\phi)\\f$. However, the disadvantage is\n * that the horizon of a Kerr hole is **not** located at constant\n * \\f$\\tilde{r}\\f$, but is located instead at constant \\f$r\\f$,\n * where \\f$r\\f$ is the radial\n * Boyer-Lindquist coordinate defined in (\\f$\\ref{eq:rdefinition2}\\f$).\n *\n * For spin in the \\f$z\\f$ direction, one could use the transformation\n * \\f{equation}{\n * x=\\sqrt{r^2+a^2}\\sin\\theta\\cos\\phi,\\quad\n * y=\\sqrt{r^2+a^2}\\sin\\theta\\sin\\phi,\\quad\n * z=r\\cos\\theta.\n * \\f}\n * In this case, for \\f$M\\to0\\f$, Kerr spacetime becomes Minkowski space in\n * spheroidal coordinates, but now the horizon is on a constant-coordinate\n * surface.\n *\n * Right now we use (\\f$\\ref{eq:sphertocartsimple}\\f$), but we may\n * wish to use the other transformation in the future.\n *\n * ## Boost of the Kerr-Schild solution\n *\n * We add initial momentum to the solution by applying a Lorentz boost to the\n * metric. Since the Kerr-Schild metric can be expressed covariantly in terms of\n * the Minkowski metric, a scalar function and a one form, we construct the\n * metric in the rest frame of the black hole and then apply an inverse boost to\n * each of the covariant objects individually. Notice that we also need to\n * appropriately boost the coordinates to the to the rest frame before computing\n * the metric.\n *\n * \\warning While technically the boosted Kerr-Schild metric is dependent on\n * both the time and space coordinates, we have implemented it only at $t = 0$\n * as in SpEC. Therefore it is technically not an analytic solution and should\n * not be used to compute errors with respect to it.\n *\n * Moreover, since the boosted solution is intended for use as initial data,\n * we do not compute the time derivatives of the lapse and shift in the boosted\n * frame but set them to zero.\n *\n * Consequently, the gr::Tags::SpacetimeChristoffelSecondKind computed here,\n * corresponds to the boosted Kerr-Schild for the gauge where lapse and shift\n * have vanishing derivatives.\n *\n */\nclass KerrSchild : public AnalyticSolution<3_st>,\n                   public MarkAsAnalyticSolution {\n public:\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\"Mass of the black hole\"};\n    static type lower_bound() { return 0.; }\n  };\n  struct Spin {\n    using type = std::array<double, volume_dim>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] dimensionless spin of the black hole\"};\n  };\n  struct Center {\n    using type = std::array<double, volume_dim>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] center of the black hole\"};\n  };\n  struct Velocity {\n    using type = std::array<double, volume_dim>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] boost velocity of the black hole\"};\n  };\n  using options = tmpl::list<Mass, Spin, Center, Velocity>;\n  static constexpr Options::String help{\n      \"Black hole in Kerr-Schild coordinates\"};\n\n  KerrSchild(double mass, const std::array<double, 3>& dimensionless_spin,\n             const std::array<double, 3>& center,\n             const std::array<double, 3>& boost_velocity = {{0., 0., 0.}},\n             const Options::Context& context = {});\n\n  explicit KerrSchild(CkMigrateMessage* /*msg*/);\n\n  template <typename DataType, typename Frame = Frame::Inertial>\n  using tags = tmpl::flatten<tmpl::list<\n      AnalyticSolution<3_st>::tags<DataType, Frame>,\n      gr::Tags::DerivDetSpatialMetric<DataType, 3, Frame>,\n      gr::Tags::TraceExtrinsicCurvature<DataType>,\n      gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>,\n      gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>,\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3, Frame>,\n      gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame>>>;\n\n  KerrSchild() = default;\n  KerrSchild(const KerrSchild& /*rhs*/) = default;\n  KerrSchild& operator=(const KerrSchild& /*rhs*/) = default;\n  KerrSchild(KerrSchild&& /*rhs*/) = default;\n  KerrSchild& operator=(KerrSchild&& /*rhs*/) = default;\n  ~KerrSchild() = default;\n\n  template <typename DataType, typename Frame, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame>& x, double /*t*/,\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(\n        tmpl2::flat_all_v<\n            tmpl::list_contains_v<tags<DataType, Frame>, Tags>...>,\n        \"At least one of the requested tags is not supported. The requested \"\n        \"tags are listed as template parameters of the `variables` function.\");\n    IntermediateVars<DataType, Frame> cache(get_size(*x.begin()));\n    IntermediateComputer<DataType, Frame> computer(*this, x);\n    return {cache.get_var(computer, Tags{})...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  double mass() const { return mass_; }\n  const std::array<double, volume_dim>& center() const { return center_; }\n  const std::array<double, volume_dim>& dimensionless_spin() const {\n    return dimensionless_spin_;\n  }\n  const std::array<double, volume_dim>& boost_velocity() const {\n    return boost_velocity_;\n  }\n  bool zero_spin() const { return zero_spin_; }\n  bool zero_velocity() const { return zero_velocity_; }\n\n  struct internal_tags {\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using x_minus_center_unboosted = ::Tags::TempI<0, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using x_minus_center = ::Tags::TempI<1, 3, Frame, DataType>;\n    template <typename DataType>\n    using a_dot_x = ::Tags::TempScalar<2, DataType>;\n    template <typename DataType>\n    using a_dot_x_squared = ::Tags::TempScalar<3, DataType>;\n    template <typename DataType>\n    using half_xsq_minus_asq = ::Tags::TempScalar<4, DataType>;\n    template <typename DataType>\n    using r_squared = ::Tags::TempScalar<5, DataType>;\n    template <typename DataType>\n    using r = ::Tags::TempScalar<6, DataType>;\n    template <typename DataType>\n    using a_dot_x_over_rsquared = ::Tags::TempScalar<7, DataType>;\n    template <typename DataType>\n    using deriv_log_r_denom = ::Tags::TempScalar<8, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_log_r = ::Tags::Tempi<9, 3, Frame, DataType>;\n    template <typename DataType>\n    using H_denom = ::Tags::TempScalar<10, DataType>;\n    template <typename DataType>\n    using H = ::Tags::TempScalar<11, DataType>;\n    template <typename DataType>\n    using deriv_H_temp1 = ::Tags::TempScalar<12, DataType>;\n    template <typename DataType>\n    using deriv_H_temp2 = ::Tags::TempScalar<13, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_H_unboosted = ::Tags::Tempa<14, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_H = ::Tags::Tempa<15, 3, Frame, DataType>;\n    template <typename DataType>\n    using denom = ::Tags::TempScalar<16, DataType>;\n    template <typename DataType>\n    using a_dot_x_over_r = ::Tags::TempScalar<17, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using null_form_unboosted = ::Tags::Tempa<18, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using null_form = ::Tags::Tempa<19, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_null_form_unboosted = ::Tags::Tempab<20, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_null_form = ::Tags::Tempab<21, 3, Frame, DataType>;\n    template <typename DataType>\n    using null_form_dot_deriv_H = ::Tags::TempScalar<22, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using null_form_dot_deriv_null_form = ::Tags::Tempi<23, 3, Frame, DataType>;\n    template <typename DataType>\n    using lapse_squared = ::Tags::TempScalar<24, DataType>;\n    template <typename DataType>\n    using deriv_lapse_multiplier = ::Tags::TempScalar<25, DataType>;\n    template <typename DataType>\n    using shift_multiplier = ::Tags::TempScalar<26, DataType>;\n  };\n\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using CachedBuffer = CachedTempBuffer<\n      internal_tags::x_minus_center_unboosted<DataType, Frame>,\n      internal_tags::x_minus_center<DataType, Frame>,\n      internal_tags::a_dot_x<DataType>,\n      internal_tags::a_dot_x_squared<DataType>,\n      internal_tags::half_xsq_minus_asq<DataType>,\n      internal_tags::r_squared<DataType>, internal_tags::r<DataType>,\n      internal_tags::a_dot_x_over_rsquared<DataType>,\n      internal_tags::deriv_log_r_denom<DataType>,\n      internal_tags::deriv_log_r<DataType, Frame>,\n      internal_tags::H_denom<DataType>, internal_tags::H<DataType>,\n      internal_tags::deriv_H_temp1<DataType>,\n      internal_tags::deriv_H_temp2<DataType>,\n      internal_tags::deriv_H_unboosted<DataType, Frame>,\n      internal_tags::deriv_H<DataType, Frame>, internal_tags::denom<DataType>,\n      internal_tags::a_dot_x_over_r<DataType>,\n      internal_tags::null_form_unboosted<DataType, Frame>,\n      internal_tags::null_form<DataType, Frame>,\n      internal_tags::deriv_null_form_unboosted<DataType, Frame>,\n      internal_tags::deriv_null_form<DataType, Frame>,\n      internal_tags::null_form_dot_deriv_H<DataType>,\n      internal_tags::null_form_dot_deriv_null_form<DataType, Frame>,\n      internal_tags::lapse_squared<DataType>, gr::Tags::Lapse<DataType>,\n      internal_tags::deriv_lapse_multiplier<DataType>,\n      internal_tags::shift_multiplier<DataType>,\n      gr::Tags::Shift<DataType, 3, Frame>, DerivShift<DataType, Frame>,\n      gr::Tags::SpatialMetric<DataType, 3, Frame>,\n      gr::Tags::InverseSpatialMetric<DataType, 3, Frame>,\n      DerivSpatialMetric<DataType, Frame>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>,\n      gr::Tags::ExtrinsicCurvature<DataType, 3, Frame>,\n      gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>,\n      gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>>;\n\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  class IntermediateComputer {\n   public:\n    using CachedBuffer = KerrSchild::CachedBuffer<DataType, Frame>;\n\n    IntermediateComputer(const KerrSchild& solution,\n                         const tnsr::I<DataType, 3, Frame>& x);\n\n    const KerrSchild& solution() const { return solution_; }\n\n    void operator()(\n        gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center,\n        gsl::not_null<CachedBuffer*> /*cache*/,\n        internal_tags::x_minus_center_unboosted<DataType, Frame> /*meta*/)\n        const;\n\n    void operator()(\n        gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center_boosted,\n        gsl::not_null<CachedBuffer*> /*cache*/,\n        internal_tags::x_minus_center<DataType, Frame> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> a_dot_x,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::a_dot_x<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> a_dot_x_squared,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::a_dot_x_squared<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> half_xsq_minus_asq,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::half_xsq_minus_asq<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> r_squared,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::r_squared<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> r,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::r<DataType> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> a_dot_x_over_rsquared,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::a_dot_x_over_rsquared<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> deriv_log_r_denom,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::deriv_log_r_denom<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<tnsr::i<DataType, 3, Frame>*> deriv_log_r,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::deriv_log_r<DataType, Frame> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> H_denom,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::H_denom<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> H,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::H<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> deriv_H_temp1,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::deriv_H_temp1<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> deriv_H_temp2,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::deriv_H_temp2<DataType> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::a<DataType, 3, Frame>*> deriv_H,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::deriv_H_unboosted<DataType, Frame> /*meta*/) const;\n\n    void operator()(gsl::not_null<tnsr::a<DataType, 3, Frame>*> deriv_H_boosted,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::deriv_H<DataType, Frame> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> denom,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::denom<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> a_dot_x_over_r,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::a_dot_x_over_r<DataType> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::a<DataType, 3, Frame>*> null_form,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::null_form_unboosted<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::a<DataType, 3, Frame>*> null_form_boosted,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::null_form<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::ab<DataType, 3, Frame>*> deriv_null_form,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::deriv_null_form_unboosted<DataType, Frame> /*meta*/)\n        const;\n\n    void operator()(\n        gsl::not_null<tnsr::ab<DataType, 3, Frame>*> deriv_null_form_boosted,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::deriv_null_form<DataType, Frame> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> lapse_squared,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::lapse_squared<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> lapse,\n                    gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::Lapse<DataType> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> deriv_lapse_multiplier,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::deriv_lapse_multiplier<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<Scalar<DataType>*> shift_multiplier,\n                    gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::shift_multiplier<DataType> /*meta*/) const;\n\n    void operator()(gsl::not_null<tnsr::I<DataType, 3, Frame>*> shift,\n                    gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::Shift<DataType, 3, Frame> /*meta*/) const;\n\n    void operator()(gsl::not_null<tnsr::iJ<DataType, 3, Frame>*> deriv_shift,\n                    gsl::not_null<CachedBuffer*> cache,\n                    DerivShift<DataType, Frame> /*meta*/) const;\n\n    void operator()(gsl::not_null<tnsr::ii<DataType, 3, Frame>*> spatial_metric,\n                    gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::SpatialMetric<DataType, 3, Frame> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::II<DataType, 3, Frame>*> spatial_metric,\n        gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::InverseSpatialMetric<DataType, 3, Frame> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::ijj<DataType, 3, Frame>*> deriv_spatial_metric,\n        gsl::not_null<CachedBuffer*> cache,\n        DerivSpatialMetric<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::ii<DataType, 3, Frame>*> dt_spatial_metric,\n        gsl::not_null<CachedBuffer*> cache,\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<Scalar<DataType>*> null_form_dot_deriv_H,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::null_form_dot_deriv_H<DataType> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::i<DataType, 3, Frame>*>\n            null_form_dot_deriv_null_form,\n        gsl::not_null<CachedBuffer*> cache,\n        internal_tags::null_form_dot_deriv_null_form<DataType, Frame> /*meta*/)\n        const;\n\n    void operator()(\n        gsl::not_null<tnsr::ii<DataType, 3, Frame>*> extrinsic_curvature,\n        gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::ExtrinsicCurvature<DataType, 3, Frame> /*meta*/) const;\n\n    void operator()(\n        gsl::not_null<tnsr::ijj<DataType, 3, Frame>*>\n            spatial_christoffel_first_kind,\n        gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame> /*meta*/)\n        const;\n\n    void operator()(\n        gsl::not_null<tnsr::Ijj<DataType, 3, Frame>*>\n            spatial_christoffel_second_kind,\n        gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame> /*meta*/)\n        const;\n\n   private:\n    const KerrSchild& solution_;\n    const tnsr::I<DataType, 3, Frame>& x_;\n    // Here null_vector_0 is simply -1, but if you have a boosted solution,\n    // then null_vector_0 can be something different, so we leave it coded\n    // in instead of eliminating it.\n    static constexpr double null_vector_0_ = -1.0;\n  };\n\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  class IntermediateVars : public CachedBuffer<DataType, Frame> {\n   public:\n    using CachedBuffer = KerrSchild::CachedBuffer<DataType, Frame>;\n    using CachedBuffer::CachedBuffer;\n    using CachedBuffer::get_var;\n\n    tnsr::i<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        DerivLapse<DataType, Frame> /*meta*/);\n\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/);\n\n    tnsr::I<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>> /*meta*/);\n\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/);\n\n    tnsr::i<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::DerivDetSpatialMetric<DataType, 3, Frame> /*meta*/);\n\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/);\n\n    tnsr::I<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3,\n                                                    Frame> /*meta*/);\n    tnsr::Abb<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame> /*meta*/);\n\n   private:\n    // Here null_vector_0 is simply -1, but if you have a boosted solution,\n    // then null_vector_0 can be something different, so we leave it coded\n    // in instead of eliminating it.\n    static constexpr double null_vector_0_ = -1.0;\n  };\n\n private:\n  double mass_{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, volume_dim> dimensionless_spin_ =\n      make_array<volume_dim>(std::numeric_limits<double>::signaling_NaN());\n  std::array<double, volume_dim> center_ =\n      make_array<volume_dim>(std::numeric_limits<double>::signaling_NaN());\n  std::array<double, volume_dim> boost_velocity_ =\n      make_array<volume_dim>(std::numeric_limits<double>::signaling_NaN());\n  bool zero_spin_{};\n  bool zero_velocity_{};\n};\n\nSPECTRE_ALWAYS_INLINE bool operator==(const KerrSchild& lhs,\n                                      const KerrSchild& rhs) {\n  return lhs.mass() == rhs.mass() and\n         lhs.dimensionless_spin() == rhs.dimensionless_spin() and\n         lhs.center() == rhs.center() and\n         lhs.boost_velocity() == rhs.boost_velocity();\n}\n\nSPECTRE_ALWAYS_INLINE bool operator!=(const KerrSchild& lhs,\n                                      const KerrSchild& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace Solutions\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr::Solutions {\ntemplate <size_t Dim>\nMinkowski<Dim>::Minkowski(CkMigrateMessage* /*msg*/) {}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::Lapse<DataType>> Minkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const {\n  return {Scalar<DataType>(make_with_value<DataType>(x, 1.))};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const {\n  return {Scalar<DataType>(make_with_value<DataType>(x, 0.))};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>,\n                                  Frame::Inertial>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>,\n                             Frame::Inertial>> /*meta*/) const {\n  return {make_with_value<tnsr::i<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::Shift<DataType, Dim>> Minkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::Shift<DataType, Dim>> /*meta*/) const {\n  return {make_with_value<tnsr::I<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, Dim>>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, Dim>>> /*meta*/) const {\n  return {make_with_value<tnsr::I<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::deriv<gr::Tags::Shift<DataType, Dim>,\n                                  tmpl::size_t<Dim>, Frame::Inertial>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<::Tags::deriv<gr::Tags::Shift<DataType, Dim>, tmpl::size_t<Dim>,\n                             Frame::Inertial>> /*meta*/) const {\n  return {make_with_value<tnsr::iJ<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, Dim>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::SpatialMetric<DataType, Dim>> /*meta*/) const {\n  tnsr::ii<DataType, Dim> lower_metric(make_with_value<DataType>(x, 0.));\n  for (size_t i = 0; i < Dim; ++i) {\n    lower_metric.get(i, i) = 1.;\n  }\n  return {std::move(lower_metric)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::dt<gr::Tags::SpatialMetric<DataType, Dim>>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<DataType, Dim>>> /*meta*/)\n    const {\n  return {make_with_value<tnsr::ii<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                                  tmpl::size_t<Dim>, Frame::Inertial>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                             tmpl::size_t<Dim>, Frame::Inertial>> /*meta*/)\n    const {\n  return {make_with_value<tnsr::ijj<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::InverseSpatialMetric<DataType, Dim>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::InverseSpatialMetric<DataType, Dim>> /*meta*/) const {\n  tnsr::II<DataType, Dim> upper_metric(make_with_value<DataType>(x, 0.));\n  for (size_t i = 0; i < Dim; ++i) {\n    upper_metric.get(i, i) = 1.;\n  }\n  return {std::move(upper_metric)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, Dim>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, Dim>> /*meta*/) const {\n  return {make_with_value<tnsr::ii<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 1.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::dt<gr::Tags::SqrtDetSpatialMetric<DataType>>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<::Tags::dt<gr::Tags::SqrtDetSpatialMetric<DataType>>> /*meta*/)\n    const {\n  return {make_with_value<Scalar<DataType>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::DerivDetSpatialMetric<DataType, Dim>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::DerivDetSpatialMetric<DataType, Dim>> /*meta*/) const {\n  return {make_with_value<tnsr::i<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::TraceExtrinsicCurvature<DataType>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::SpatialChristoffelFirstKind<DataType, Dim>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::SpatialChristoffelFirstKind<DataType, Dim>> /*meta*/)\n    const {\n  return {make_with_value<tnsr::ijj<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::SpatialChristoffelSecondKind<DataType, Dim>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<gr::Tags::SpatialChristoffelSecondKind<DataType, Dim>> /*meta*/)\n    const {\n  return {make_with_value<tnsr::Ijj<DataType, Dim>>(x, 0.)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::TraceSpatialChristoffelSecondKind<DataType, Dim>>\nMinkowski<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<\n        gr::Tags::TraceSpatialChristoffelSecondKind<DataType, Dim>> /*meta*/)\n    const {\n  return {make_with_value<tnsr::I<DataType, Dim>>(x, 0.)};\n}\n\n}  // namespace gr::Solutions\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template tuples::TaggedTuple<gr::Tags::Lapse<DTYPE(data)>>                   \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<gr::Tags::Lapse<DTYPE(data)>> /*meta*/) const;                \\\n  template tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>>       \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>> /*meta*/) const;    \\\n  template tuples::TaggedTuple<::Tags::deriv<                                  \\\n      gr::Tags::Lapse<DTYPE(data)>, tmpl::size_t<DIM(data)>, Frame::Inertial>> \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::Lapse<DTYPE(data)>, tmpl::size_t<DIM(data)>, \\\n                        Frame::Inertial>> /*meta*/) const;                     \\\n  template tuples::TaggedTuple<gr::Tags::Shift<DTYPE(data), DIM(data)>>        \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<gr::Tags::Shift<DTYPE(data), DIM(data)>> /*meta*/) const;     \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::Shift<DTYPE(data), DIM(data)>>>                     \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<                                                              \\\n          ::Tags::dt<gr::Tags::Shift<DTYPE(data), DIM(data)>>> /*meta*/)       \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::deriv<gr::Tags::Shift<DTYPE(data), DIM(data)>,                   \\\n                    tmpl::size_t<DIM(data)>, Frame::Inertial>>                 \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::Shift<DTYPE(data), DIM(data)>,               \\\n                        tmpl::size_t<DIM(data)>, Frame::Inertial>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>                         \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)    \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>>             \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<::Tags::dt<                                                   \\\n          gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>> /*meta*/) const;   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::deriv<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>,           \\\n                    tmpl::size_t<DIM(data)>, Frame::Inertial>>                 \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>,       \\\n                        tmpl::size_t<DIM(data)>, Frame::Inertial>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::InverseSpatialMetric<DTYPE(data), DIM(data)>>                  \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<                                                              \\\n          gr::Tags::InverseSpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)    \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::ExtrinsicCurvature<DTYPE(data), DIM(data)>>                    \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<                                                              \\\n          gr::Tags::ExtrinsicCurvature<DTYPE(data), DIM(data)>> /*meta*/)      \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>>    \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>> /*meta*/) const; \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>>>                 \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<                                                              \\\n          ::Tags::dt<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::DerivDetSpatialMetric<DTYPE(data), DIM(data)>>                 \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<                                                              \\\n          gr::Tags::DerivDetSpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::SpatialChristoffelFirstKind<DTYPE(data), DIM(data)>>           \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<gr::Tags::SpatialChristoffelFirstKind<DTYPE(data),            \\\n                                                       DIM(data)>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::SpatialChristoffelSecondKind<DTYPE(data), DIM(data)>>          \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<gr::Tags::SpatialChristoffelSecondKind<DTYPE(data),           \\\n                                                        DIM(data)>> /*meta*/)  \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::TraceSpatialChristoffelSecondKind<DTYPE(data), DIM(data)>>     \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<gr::Tags::TraceSpatialChristoffelSecondKind<                  \\\n          DTYPE(data), DIM(data)>> /*meta*/) const;                            \\\n  template tuples::TaggedTuple<gr::Tags::TraceExtrinsicCurvature<DTYPE(data)>> \\\n  gr::Solutions::Minkowski<DIM(data)>::variables(                              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double /*t*/,                  \\\n      tmpl::list<gr::Tags::TraceExtrinsicCurvature<DTYPE(data)>> /*meta*/)     \\\n      const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector))\n\n#undef INSTANTIATE\n\n#undef DTYPE\n\n#define INSTANTIATE(_, data) template class gr::Solutions::Minkowski<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gr {\nnamespace Solutions {\n\n/*!\n * \\brief The Minkowski solution for flat space in Dim spatial dimensions.\n *\n * \\details The solution has lapse \\f$N(x,t)= 1 \\f$, shift \\f$N^i(x,t) = 0 \\f$\n * and the identity as the spatial metric: \\f$g_{ii} = 1 \\f$\n */\ntemplate <size_t Dim>\nclass Minkowski : public AnalyticSolution<Dim>, public MarkAsAnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Minkowski solution to Einstein's Equations\"};\n\n\n  Minkowski() = default;\n  Minkowski(const Minkowski& /*rhs*/) = default;\n  Minkowski& operator=(const Minkowski& /*rhs*/) = default;\n  Minkowski(Minkowski&& /*rhs*/) = default;\n  Minkowski& operator=(Minkowski&& /*rhs*/) = default;\n  ~Minkowski() = default;\n\n  explicit Minkowski(CkMigrateMessage* /*msg*/);\n\n  template <typename DataType>\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>,\n                                   Frame::Inertial>;\n  template <typename DataType>\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, Dim>,\n                                   tmpl::size_t<Dim>, Frame::Inertial>;\n  template <typename DataType>\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>, tmpl::size_t<Dim>,\n                    Frame::Inertial>;\n\n  template <typename DataType, typename Frame = Frame::Inertial>\n  using tags = tmpl::flatten<tmpl::list<\n      typename AnalyticSolution<Dim>::template tags<DataType, Frame>,\n      gr::Tags::DerivDetSpatialMetric<DataType, Dim, Frame>,\n      gr::Tags::TraceExtrinsicCurvature<DataType>,\n      gr::Tags::SpatialChristoffelFirstKind<DataType, Dim, Frame>,\n      gr::Tags::SpatialChristoffelSecondKind<DataType, Dim, Frame>,\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataType, Dim, Frame>>>;\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, Dim>& x,\n                                         double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(\n        tmpl2::flat_all_v<\n            tmpl::list_contains_v<tags<DataType>, Tags>...>,\n        \"At least one of the requested tags is not supported. The requested \"\n        \"tags are listed as template parameters of the `variables` function.\");\n\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::Lapse<DataType>> variables(\n      const tnsr::I<DataType, Dim>& x, double t,\n      tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>> variables(\n      const tnsr::I<DataType, Dim>& x, double t,\n      tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<::Tags::deriv<gr::Tags::Lapse<DataType>,\n                                    tmpl::size_t<Dim>, Frame::Inertial>>\n  variables(\n      const tnsr::I<DataType, Dim>& x, double t,\n      tmpl::list<::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>,\n                               Frame::Inertial>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::Shift<DataType, Dim>> variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<gr::Tags::Shift<DataType, Dim>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, Dim>>> variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, Dim>>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<::Tags::deriv<gr::Tags::Shift<DataType, Dim>,\n                                    tmpl::size_t<Dim>, Frame::Inertial>>\n  variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<::Tags::deriv<gr::Tags::Shift<DataType, Dim>,\n                               tmpl::size_t<Dim>, Frame::Inertial>> /*meta*/)\n      const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, Dim>> variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<gr::Tags::SpatialMetric<DataType, Dim>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<::Tags::dt<gr::Tags::SpatialMetric<DataType, Dim>>>\n  variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<DataType, Dim>>> /*meta*/)\n      const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                                    tmpl::size_t<Dim>, Frame::Inertial>>\n  variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                               tmpl::size_t<Dim>, Frame::Inertial>> /*meta*/)\n      const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::InverseSpatialMetric<DataType, Dim>> variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataType, Dim>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, Dim>> variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, Dim>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>> variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::DerivDetSpatialMetric<DataType, Dim>> variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<gr::Tags::DerivDetSpatialMetric<DataType, Dim>> /*meta*/)\n      const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<::Tags::dt<gr::Tags::SqrtDetSpatialMetric<DataType>>>\n  variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<::Tags::dt<gr::Tags::SqrtDetSpatialMetric<DataType>>> /*meta*/)\n      const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::TraceExtrinsicCurvature<DataType>> variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::SpatialChristoffelFirstKind<DataType, Dim>>\n  variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<gr::Tags::SpatialChristoffelFirstKind<DataType, Dim>> /*meta*/)\n      const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<gr::Tags::SpatialChristoffelSecondKind<DataType, Dim>>\n  variables(const tnsr::I<DataType, Dim>& x, double /*t*/,\n            tmpl::list<\n                gr::Tags::SpatialChristoffelSecondKind<DataType, Dim>> /*meta*/)\n      const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataType, Dim>>\n  variables(const tnsr::I<DataType, Dim>& x, double /*t*/,\n            tmpl::list<gr::Tags::TraceSpatialChristoffelSecondKind<\n                DataType, Dim>> /*meta*/) const;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\ntemplate <size_t Dim>\ninline constexpr bool operator==(const Minkowski<Dim>& /*lhs*/,\n                                 const Minkowski<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\ninline constexpr bool operator!=(const Minkowski<Dim>& /*lhs*/,\n                                 const Minkowski<Dim>& /*rhs*/) {\n  return false;\n}\n}  // namespace Solutions\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n#include <cstddef>\n#include <exception>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace py = pybind11;\n\nnamespace gr::Solutions::py_bindings {\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n\n  py::class_<KerrSchild>(m, \"KerrSchild\")\n      .def(py::init<double, const std::array<double, 3>&,\n                    const std::array<double, 3>&>(),\n           py::arg(\"mass\"), py::arg(\"dimensionless_spin\"),\n           py::arg(\"center\") = std::array<double, 3>{{0.0, 0.0, 0.0}})\n      .def_property_readonly(\"mass\", &KerrSchild::mass)\n      .def_property_readonly(\"dimensionless_spin\",\n                             &KerrSchild::dimensionless_spin)\n      .def_property_readonly(\"center\", &KerrSchild::center)\n      .def(\n          \"variables\",\n          [](const KerrSchild& solution, const tnsr::I<DataVector, 3>& x,\n             const std::vector<std::string>& requested_quantities) -> py::dict {\n            KerrSchild::IntermediateVars<DataVector, Frame::Inertial> cache(\n                x.begin()->size());\n            KerrSchild::IntermediateComputer<DataVector, Frame::Inertial>\n                computer(solution, x);\n            using available_tags =\n                KerrSchild::tags<DataVector, Frame::Inertial>;\n            py::dict result{};\n            for (const auto& requested_quantity : requested_quantities) {\n              bool found = false;\n              tmpl::for_each<available_tags>([&requested_quantity, &cache,\n                                              &computer, &result,\n                                              &found](const auto tag_v) {\n                if (found) {\n                  return;\n                }\n                using tag = tmpl::type_from<decltype(tag_v)>;\n                if (requested_quantity == db::tag_name<tag>()) {\n                  result[requested_quantity.c_str()] =\n                      cache.get_var(computer, tag{});\n                  found = true;\n                }\n              });\n              if (not found) {\n                std::string available_quantities{};\n                tmpl::for_each<available_tags>(\n                    [&available_quantities](const auto tag_v) {\n                      using tag = tmpl::type_from<decltype(tag_v)>;\n                      available_quantities += db::tag_name<tag>() + \"\\n\";\n                    });\n                throw std::invalid_argument(\n                    \"Requested quantity '\" + requested_quantity +\n                    \"' is not available. \"\n                    // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n                    \"Available quantities are:\\n\" +\n                    available_quantities);\n              }\n            }\n            return result;\n          },\n          py::arg(\"x\"), py::arg(\"requested_quantities\"));\n}\n\n}  // namespace gr::Solutions::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyGrSolutions\")\n\nspectre_python_add_module(\n  GeneralRelativity\n  LIBRARY_NAME ${LIBRARY}\n  MODULE_PATH \"PointwiseFunctions/AnalyticSolutions\"\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  GeneralRelativitySolutions\n  pybind11::module\n  Utilities\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n)\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n\nnamespace gr {\n/// Base struct for properties common to all GR analytic solutions\ntemplate <size_t Dim>\nstruct AnalyticSolution {\n  static constexpr size_t volume_dim = Dim;\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataType>,\n                                   tmpl::size_t<volume_dim>, Frame>;\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, volume_dim, Frame>,\n                                   tmpl::size_t<volume_dim>, Frame>;\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, volume_dim, Frame>,\n                    tmpl::size_t<volume_dim>, Frame>;\n\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using tags = tmpl::list<\n      gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,\n      DerivLapse<DataType, Frame>, gr::Tags::Shift<DataType, volume_dim, Frame>,\n      ::Tags::dt<gr::Tags::Shift<DataType, volume_dim, Frame>>,\n      DerivShift<DataType, Frame>,\n      gr::Tags::SpatialMetric<DataType, volume_dim, Frame>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim, Frame>>,\n      DerivSpatialMetric<DataType, Frame>,\n      gr::Tags::SqrtDetSpatialMetric<DataType>,\n      gr::Tags::ExtrinsicCurvature<DataType, volume_dim, Frame>,\n      gr::Tags::InverseSpatialMetric<DataType, volume_dim, Frame>>;\n};\n\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Classes which implement analytic solutions to Einstein's equations\n */\nnamespace Solutions {}\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n\n#include <cmath>\n#include <numeric>\n#include <ostream>\n#include <typeinfo>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/CrossProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace gr::Solutions {\n\nSphericalKerrSchild::SphericalKerrSchild(\n    const double mass, SphericalKerrSchild::Spin::type dimensionless_spin,\n    SphericalKerrSchild::Center::type center, const Options::Context& context)\n\n    : mass_(mass),\n      // NOLINTNEXTLINE(performance-move-const-arg)\n      dimensionless_spin_(std::move(dimensionless_spin)),\n      // NOLINTNEXTLINE(performance-move-const-arg)\n      center_(std::move(center)) {\n  const double spin_magnitude = magnitude(dimensionless_spin_);\n  if (spin_magnitude > 1.) {\n    PARSE_ERROR(context, \"Spin magnitude must be < 1. Given spin: \"\n                             << dimensionless_spin_ << \" with magnitude \"\n                             << spin_magnitude);\n  }\n  if (mass_ <= 0.) {\n    PARSE_ERROR(context, \"Mass must be > 0. Given mass: \" << mass_);\n  }\n}\n\nSphericalKerrSchild::SphericalKerrSchild(CkMigrateMessage* /*unused*/) {}\n\nvoid SphericalKerrSchild::pup(PUP::er& p) {\n  p | mass_;\n  p | dimensionless_spin_;\n  p | center_;\n}\n\ntemplate <typename DataType, typename Frame>\nSphericalKerrSchild::IntermediateComputer<\n    DataType, Frame>::IntermediateComputer(const SphericalKerrSchild& solution,\n                                           const tnsr::I<DataType, 3, Frame>& x)\n    : solution_(solution), x_(x) {}\n\nnamespace {\nauto dimensionful_spin(const SphericalKerrSchild& solution) {\n  return solution.dimensionless_spin() * solution.mass();\n}\n\nauto spin_a_and_squared(const SphericalKerrSchild& solution) {\n  const auto spin_a = dimensionful_spin(solution);\n  const auto a_squared =\n      std::inner_product(spin_a.begin(), spin_a.end(), spin_a.begin(), 0.);\n  return std::make_tuple(spin_a, a_squared);\n}\n}  // namespace\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center,\n    const gsl::not_null<CachedBuffer*> /*cache*/,\n    internal_tags::x_minus_center<DataType, Frame> /*meta*/) const {\n  *x_minus_center = x_;\n  for (size_t i = 0; i < 3; ++i) {\n    x_minus_center->get(i) -= gsl::at(solution_.center(), i);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> r_squared,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::r_squared<DataType> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n\n  get(*r_squared) = square(get<0>(x_minus_center)) +\n                    square(get<1>(x_minus_center)) +\n                    square(get<2>(x_minus_center));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::r<DataType> /*meta*/) const {\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n\n  get(*r) = sqrt(r_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> rho,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::rho<DataType> /*meta*/) const {\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n  const auto [spin_a, a_squared] = spin_a_and_squared(solution_);\n\n  get(*rho) = sqrt(r_squared + a_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_F,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::helper_matrix_F<DataType, Frame> /*meta*/) const {\n  const auto [spin_a, a_squared] = spin_a_and_squared(solution_);\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      helper_matrix_F->get(i, j) = -1. / (rho * cube(r));\n      if (i == j) {  // Kronecker delta\n        helper_matrix_F->get(i, j) *=\n            (a_squared - gsl::at(spin_a, i) * gsl::at(spin_a, j));\n      } else {\n        helper_matrix_F->get(i, j) *= -gsl::at(spin_a, i) * gsl::at(spin_a, j);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> transformation_matrix_P,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::transformation_matrix_P<DataType, Frame> /*meta*/) const {\n  const auto spin_a = dimensionful_spin(solution_);\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      transformation_matrix_P->get(i, j) =\n          -(gsl::at(spin_a, i) * gsl::at(spin_a, j)) / (r * (rho + r));\n      if (i == j) {  // Kronecker delta\n        transformation_matrix_P->get(i, j) += rho / r;\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> jacobian,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::jacobian<DataType, Frame> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& transformation_matrix_P = cache->get_var(\n      *this, internal_tags::transformation_matrix_P<DataType, Frame>{});\n  const auto& helper_matrix_F =\n      cache->get_var(*this, internal_tags::helper_matrix_F<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      jacobian->get(j, i) = transformation_matrix_P.get(i, j);\n      for (size_t k = 0; k < 3; ++k) {\n        jacobian->get(j, i) += helper_matrix_F.get(i, k) *\n                               x_minus_center.get(k) * x_minus_center.get(j);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_D,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::helper_matrix_D<DataType, Frame> /*meta*/) const {\n  const auto [spin_a, a_squared] = spin_a_and_squared(solution_);\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      helper_matrix_D->get(i, j) = 1. / (r * cube(rho));\n      if (i == j) {  // Kronecker delta\n        helper_matrix_D->get(i, j) *=\n            (a_squared - gsl::at(spin_a, i) * gsl::at(spin_a, j));\n      } else {\n        helper_matrix_D->get(i, j) *= -gsl::at(spin_a, i) * gsl::at(spin_a, j);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_C,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::helper_matrix_C<DataType, Frame> /*meta*/) const {\n  const auto& helper_matrix_F =\n      cache->get_var(*this, internal_tags::helper_matrix_F<DataType, Frame>{});\n  const auto& helper_matrix_D =\n      cache->get_var(*this, internal_tags::helper_matrix_D<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t m = 0; m < 3; ++m) {\n      helper_matrix_C->get(i, m) =\n          helper_matrix_D.get(i, m) - 3. * helper_matrix_F.get(i, m);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::iJk<DataType, 3, Frame>*> deriv_jacobian,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_jacobian<DataType, Frame> /*meta*/) const {\n  const auto& helper_matrix_C =\n      cache->get_var(*this, internal_tags::helper_matrix_C<DataType, Frame>{});\n  const auto& helper_matrix_F =\n      cache->get_var(*this, internal_tags::helper_matrix_F<DataType, Frame>{});\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t i = 0; i < 3; ++i) {\n        deriv_jacobian->get(k, j, i) =\n            helper_matrix_F.get(i, j) * x_minus_center.get(k) +\n            helper_matrix_F.get(i, k) * x_minus_center.get(j);\n        for (size_t m = 0; m < 3; ++m) {\n          if (j == k) {  // Kronecker delta\n            deriv_jacobian->get(k, j, i) +=\n                helper_matrix_F.get(i, m) * x_minus_center.get(m);\n          }\n          deriv_jacobian->get(k, j, i) +=\n              helper_matrix_C.get(i, m) * x_minus_center.get(k) *\n              x_minus_center.get(m) * x_minus_center.get(j) / r_squared;\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> transformation_matrix_Q,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::transformation_matrix_Q<DataType, Frame> /*meta*/) const {\n  const auto spin_a = dimensionful_spin(solution_);\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      transformation_matrix_Q->get(i, j) =\n          (gsl::at(spin_a, i) * gsl::at(spin_a, j)) / ((rho + r) * rho);\n      if (i == j) {  // Kronecker delta\n        transformation_matrix_Q->get(i, j) += r / rho;\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_G1,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::helper_matrix_G1<DataType, Frame> /*meta*/) const {\n  const auto [spin_a, a_squared] = spin_a_and_squared(solution_);\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t m = 0; m < 3; ++m) {\n      helper_matrix_G1->get(i, m) = 1. / (r * square(rho));\n      if (i == m) {  // Kronecker delta\n        helper_matrix_G1->get(i, m) *=\n            (a_squared - gsl::at(spin_a, i) * gsl::at(spin_a, m));\n      } else {\n        helper_matrix_G1->get(i, m) *= -gsl::at(spin_a, i) * gsl::at(spin_a, m);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> a_dot_x,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::a_dot_x<DataType> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto spin_a = dimensionful_spin(solution_);\n\n  get(*a_dot_x) = spin_a[0] * get<0>(x_minus_center) +\n                  spin_a[1] * get<1>(x_minus_center) +\n                  spin_a[2] * get<2>(x_minus_center);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> s_number,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::s_number<DataType> /*meta*/) const {\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n\n  get(*s_number) = r_squared + square(a_dot_x) / r_squared;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_G2,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::helper_matrix_G2<DataType, Frame> /*meta*/) const {\n  const auto& transformation_matrix_Q = cache->get_var(\n      *this, internal_tags::transformation_matrix_Q<DataType, Frame>{});\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& s_number =\n      get(cache->get_var(*this, internal_tags::s_number<DataType>{}));\n\n  for (size_t n = 0; n < 3; ++n) {\n    for (size_t j = 0; j < 3; ++j) {\n      helper_matrix_G2->get(n, j) =\n          square(rho) / (s_number * r) * transformation_matrix_Q.get(n, j);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> G1_dot_x,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::G1_dot_x<DataType, Frame> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& helper_matrix_G1 =\n      cache->get_var(*this, internal_tags::helper_matrix_G1<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    G1_dot_x->get(i) = helper_matrix_G1.get(i, 0) * x_minus_center.get(0);\n    for (size_t m = 1; m < 3; ++m) {\n      G1_dot_x->get(i) += helper_matrix_G1.get(i, m) * x_minus_center.get(m);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3, Frame>*> G2_dot_x,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::G2_dot_x<DataType, Frame> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& helper_matrix_G2 =\n      cache->get_var(*this, internal_tags::helper_matrix_G2<DataType, Frame>{});\n\n  for (size_t j = 0; j < 3; ++j) {\n    G2_dot_x->get(j) = helper_matrix_G2.get(0, j) * x_minus_center.get(0);\n    for (size_t n = 1; n < 3; ++n) {\n      G2_dot_x->get(j) += helper_matrix_G2.get(n, j) * x_minus_center.get(n);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> inv_jacobian,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::inv_jacobian<DataType, Frame> /*meta*/) const {\n  const auto& transformation_matrix_Q = cache->get_var(\n      *this, internal_tags::transformation_matrix_Q<DataType, Frame>{});\n  const auto& G1_dot_x =\n      cache->get_var(*this, internal_tags::G1_dot_x<DataType, Frame>{});\n  const auto& G2_dot_x =\n      cache->get_var(*this, internal_tags::G2_dot_x<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      inv_jacobian->get(j, i) =\n          transformation_matrix_Q.get(i, j) + G1_dot_x.get(i) * G2_dot_x.get(j);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_E1,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::helper_matrix_E1<DataType, Frame> /*meta*/) const {\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto& r_squared =\n      get(cache->get_var(*this, internal_tags::r_squared<DataType>{}));\n  const auto [spin_a, a_squared] = spin_a_and_squared(solution_);\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t m = 0; m < 3; ++m) {\n      helper_matrix_E1->get(i, m) =\n          -(square(rho) + 2. * r_squared) / (r_squared * pow<4>(rho));\n      if (i == m) {  // Kronecker delta\n        helper_matrix_E1->get(i, m) *=\n            (a_squared - gsl::at(spin_a, i) * gsl::at(spin_a, m));\n      } else {\n        helper_matrix_E1->get(i, m) *= -gsl::at(spin_a, i) * gsl::at(spin_a, m);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_E2,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::helper_matrix_E2<DataType, Frame> /*meta*/) const {\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto [spin_a, a_squared] = spin_a_and_squared(solution_);\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n  const auto& s_number =\n      get(cache->get_var(*this, internal_tags::s_number<DataType>{}));\n  const auto& helper_matrix_G2 =\n      cache->get_var(*this, internal_tags::helper_matrix_G2<DataType, Frame>{});\n  const auto& transformation_matrix_P = cache->get_var(\n      *this, internal_tags::transformation_matrix_P<DataType, Frame>{});\n\n  for (size_t n = 0; n < 3; ++n) {\n    for (size_t j = 0; j < 3; ++j) {\n      helper_matrix_E2->get(n, j) =\n          (1. / s_number) * transformation_matrix_P.get(n, j);\n      helper_matrix_E2->get(n, j) +=\n          ((-a_squared / (square(rho) * r)) -\n           (2. / s_number) * (r - (square(a_dot_x) / cube(r)))) *\n          helper_matrix_G2.get(n, j);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::iJk<DataType, 3, Frame>*> deriv_inv_jacobian,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_inv_jacobian<DataType, Frame> /*meta*/) const {\n  const auto spin_a = dimensionful_spin(solution_);\n  const auto& helper_matrix_D =\n      cache->get_var(*this, internal_tags::helper_matrix_D<DataType, Frame>{});\n  const auto& helper_matrix_G1 =\n      cache->get_var(*this, internal_tags::helper_matrix_G1<DataType, Frame>{});\n  const auto& helper_matrix_G2 =\n      cache->get_var(*this, internal_tags::helper_matrix_G2<DataType, Frame>{});\n  const auto& helper_matrix_E1 =\n      cache->get_var(*this, internal_tags::helper_matrix_E1<DataType, Frame>{});\n  const auto& helper_matrix_E2 =\n      cache->get_var(*this, internal_tags::helper_matrix_E2<DataType, Frame>{});\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& G1_dot_x =\n      cache->get_var(*this, internal_tags::G1_dot_x<DataType, Frame>{});\n  const auto& G2_dot_x =\n      cache->get_var(*this, internal_tags::G2_dot_x<DataType, Frame>{});\n  const auto& s_number =\n      get(cache->get_var(*this, internal_tags::s_number<DataType>{}));\n\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t i = 0; i < 3; ++i) {\n        deriv_inv_jacobian->get(k, j, i) =\n            helper_matrix_D.get(i, j) * x_minus_center.get(k) +\n            helper_matrix_G1.get(i, k) * G2_dot_x.get(j) +\n            helper_matrix_G2.get(k, j) * G1_dot_x.get(i) -\n            2. * a_dot_x * gsl::at(spin_a, k) * G1_dot_x.get(i) *\n                G2_dot_x.get(j) / (s_number * square(r));\n        for (size_t m = 0; m < 3; ++m) {\n          deriv_inv_jacobian->get(k, j, i) +=\n              (helper_matrix_E1.get(i, m) * x_minus_center.get(m) *\n                   G2_dot_x.get(j) * x_minus_center.get(k) +\n               G1_dot_x.get(i) * x_minus_center.get(k) * x_minus_center.get(m) *\n                   helper_matrix_E2.get(m, j)) /\n              r;\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> H,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::H<DataType> /*meta*/) const {\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n\n  get(*H) = solution_.mass() * cube(r) / (pow<4>(r) + square(a_dot_x));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> kerr_schild_x,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::kerr_schild_x<DataType, Frame> /*meta*/) const {\n  const auto& x_minus_center =\n      cache->get_var(*this, internal_tags::x_minus_center<DataType, Frame>{});\n  const auto& transformation_matrix_P = cache->get_var(\n      *this, internal_tags::transformation_matrix_P<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    kerr_schild_x->get(i) =\n        transformation_matrix_P.get(i, 0) * x_minus_center.get(0);\n    for (size_t j = 1; j < 3; ++j) {\n      kerr_schild_x->get(i) +=\n          transformation_matrix_P.get(i, j) * x_minus_center.get(j);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> a_cross_x,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::a_cross_x<DataType, Frame> /*meta*/) const {\n  const auto spin_a = dimensionful_spin(solution_);\n  const tnsr::I<DataType, 3, Frame>& kerr_schild_x =\n      cache->get_var(*this, internal_tags::kerr_schild_x<DataType, Frame>{});\n\n  // temp_spin used to convert the spin from type array to type tnsr\n  auto temp_spin = make_with_value<tnsr::I<DataType, 3, Frame>>(\n      get_size(get_element(kerr_schild_x, 0)), 0.);\n\n  for (size_t i = 0; i < 3; ++i) {\n    temp_spin[i] = gsl::at(spin_a, i);\n  }\n  *a_cross_x = cross_product(temp_spin, kerr_schild_x);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> kerr_schild_l,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::kerr_schild_l<DataType, Frame> /*meta*/) const {\n  const auto spin_a = dimensionful_spin(solution_);\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& a_cross_x =\n      cache->get_var(*this, internal_tags::a_cross_x<DataType, Frame>{});\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto& kerr_schild_x =\n      cache->get_var(*this, internal_tags::kerr_schild_x<DataType, Frame>{});\n\n  const auto sqr_inv_rho = 1. / square(rho);\n\n  for (int i = 0; i < 3; ++i) {\n    kerr_schild_l->get(i) =\n        sqr_inv_rho * (r * kerr_schild_x.get(i) +\n                       a_dot_x * gsl::at(spin_a, i) / r - a_cross_x.get(i));\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 4, Frame>*> l_lower,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::l_lower<DataType, Frame> /*meta*/) const {\n  const auto& kerr_schild_l =\n      cache->get_var(*this, internal_tags::kerr_schild_l<DataType, Frame>{});\n  const auto& jacobian =\n      cache->get_var(*this, internal_tags::jacobian<DataType, Frame>{});\n\n  l_lower->get(0) = 1.;  // this is l_t\n\n  for (size_t j = 0; j < 3; ++j) {\n    l_lower->get(j + 1) = jacobian.get(j, 0) * kerr_schild_l.get(0);\n    for (size_t i = 1; i < 3; ++i) {\n      l_lower->get(j + 1) += jacobian.get(j, i) * kerr_schild_l.get(i);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 4, Frame>*> l_upper,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::l_upper<DataType, Frame> /*meta*/) const {\n  const auto& kerr_schild_l =\n      cache->get_var(*this, internal_tags::kerr_schild_l<DataType, Frame>{});\n  const auto& inv_jacobian =\n      cache->get_var(*this, internal_tags::inv_jacobian<DataType, Frame>{});\n\n  l_upper->get(0) = -1.;  // this is l^t\n\n  for (size_t j = 0; j < 3; ++j) {\n    l_upper->get(j + 1) = inv_jacobian.get(0, j) * kerr_schild_l.get(0);\n    for (size_t i = 1; i < 3; ++i) {\n      l_upper->get(j + 1) += inv_jacobian.get(i, j) * kerr_schild_l.get(i);\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> deriv_r,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_r<DataType, Frame> /*meta*/) const {\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n  const auto& kerr_schild_x =\n      cache->get_var(*this, internal_tags::kerr_schild_x<DataType, Frame>{});\n  const auto& H = cache->get_var(*this, internal_tags::H<DataType>{});\n  const auto spin_a = dimensionful_spin(solution_);\n\n  // temp_mass used to convert mass from type double to Scalar\n  const auto temp_mass = make_with_value<Scalar<DataType>>(\n      get_size(get_element(kerr_schild_x, 0)), solution_.mass());\n  const auto deriv_r_denom = get(H) / get(temp_mass);\n\n  for (size_t i = 0; i < 3; ++i) {\n    deriv_r->get(i) =\n        deriv_r_denom *\n        (kerr_schild_x.get(i) + a_dot_x * gsl::at(spin_a, i) / square(r));\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 4, Frame>*> deriv_H,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_H<DataType, Frame> /*meta*/) const {\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n  const auto& H = cache->get_var(*this, internal_tags::H<DataType>{});\n  const auto& jacobian =\n      cache->get_var(*this, internal_tags::jacobian<DataType, Frame>{});\n  const auto spin_a = dimensionful_spin(solution_);\n  const auto& deriv_r =\n      cache->get_var(*this, internal_tags::deriv_r<DataType, Frame>{});\n\n  const auto H_denom = 1. / (pow<4>(r) + square(a_dot_x));\n  const auto factor = 3. / r - 4. * cube(r) * H_denom;\n\n  deriv_H->get(0) = 0.;  // set time component to 0\n  for (size_t i = 0; i < 3; ++i) {\n    deriv_H->get(i + 1) =\n        get(H) *\n        (factor * deriv_r.get(i) - 2. * H_denom * a_dot_x * gsl::at(spin_a, i));\n  }\n\n  // Explicitly copy because we modify components in loop below\n  const auto deriv_H_x = deriv_H->get(1);\n  const auto deriv_H_y = deriv_H->get(2);\n  const auto deriv_H_z = deriv_H->get(3);\n\n  for (size_t j = 0; j < 3; ++j) {\n    deriv_H->get(j + 1) = jacobian.get(j, 0) * deriv_H_x +\n                          jacobian.get(j, 1) * deriv_H_y +\n                          jacobian.get(j, 2) * deriv_H_z;\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ij<DataType, 4, Frame>*> kerr_schild_deriv_l,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::kerr_schild_deriv_l<DataType, Frame> /*meta*/) const {\n  const auto& kerr_schild_x =\n      cache->get_var(*this, internal_tags::kerr_schild_x<DataType, Frame>{});\n  const auto& r = get(cache->get_var(*this, internal_tags::r<DataType>{}));\n  const auto& a_dot_x =\n      get(cache->get_var(*this, internal_tags::a_dot_x<DataType>{}));\n  const auto& rho = get(cache->get_var(*this, internal_tags::rho<DataType>{}));\n  const auto spin_a = dimensionful_spin(solution_);\n  const auto& kerr_schild_l =\n      cache->get_var(*this, internal_tags::kerr_schild_l<DataType, Frame>{});\n  const auto& deriv_r =\n      cache->get_var(*this, internal_tags::deriv_r<DataType, Frame>{});\n\n  const auto sqr_inv_rho = 1. / square(rho);\n\n  kerr_schild_deriv_l->get(0, 0) = 0.;  // set first time component to 0\n  for (size_t i = 0; i < 3; ++i) {      // set remaining time components to 0\n    kerr_schild_deriv_l->get(i + 1, 0) = 0.;\n    kerr_schild_deriv_l->get(0, i + 1) = 0.;\n    for (size_t j = 0; j < 3; ++j) {\n      kerr_schild_deriv_l->get(j + 1, i + 1) =\n          sqr_inv_rho * ((kerr_schild_x.get(i) - 2. * r * kerr_schild_l.get(i) -\n                          a_dot_x * gsl::at(spin_a, i) / square(r)) *\n                             deriv_r.get(j) +\n                         gsl::at(spin_a, i) * gsl::at(spin_a, j) / r);\n      if (i == j) {\n        kerr_schild_deriv_l->get(j + 1, i + 1) += sqr_inv_rho * r;\n      } else {  // add sqr_inv_rho*epsilon^ijk a_k\n        size_t k = (j + 1) % 3;\n        if (k == i) {  // j+1 = i (cyclic), so choose minus sign\n          ++k;\n          k %= 3;  // and set k to be neither i nor j\n          kerr_schild_deriv_l->get(j + 1, i + 1) -=\n              sqr_inv_rho * gsl::at(spin_a, k);\n        } else {  // i+1 = j (cyclic), so choose plus sign\n          kerr_schild_deriv_l->get(j + 1, i + 1) +=\n              sqr_inv_rho * gsl::at(spin_a, k);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ij<DataType, 4, Frame>*> deriv_l,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_l<DataType, Frame> /*meta*/) const {\n  const auto& kerr_schild_l =\n      cache->get_var(*this, internal_tags::kerr_schild_l<DataType, Frame>{});\n  const auto& jacobian =\n      cache->get_var(*this, internal_tags::jacobian<DataType, Frame>{});\n  const auto& deriv_jacobian =\n      cache->get_var(*this, internal_tags::deriv_jacobian<DataType, Frame>{});\n  const auto& kerr_schild_deriv_l = cache->get_var(\n      *this, internal_tags::kerr_schild_deriv_l<DataType, Frame>{});\n\n  deriv_l->get(0, 0) = 0.;          // set first time component to 0\n  for (size_t i = 0; i < 3; ++i) {  // set remaining time components to 0\n    deriv_l->get(i + 1, 0) = 0.;\n    deriv_l->get(0, i + 1) = 0.;\n    for (size_t j = 0; j < 3; ++j) {\n      deriv_l->get(j + 1, i + 1) = 0.;\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          deriv_l->get(j + 1, i + 1) += jacobian.get(i, k) *\n                                        jacobian.get(j, m) *\n                                        kerr_schild_deriv_l.get(m + 1, k + 1);\n        }\n        deriv_l->get(j + 1, i + 1) +=\n            kerr_schild_l.get(k) * deriv_jacobian.get(j, i, k);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse_squared,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::lapse_squared<DataType> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& l_upper =\n      cache->get_var(*this, internal_tags::l_upper<DataType, Frame>{});\n\n  get(*lapse_squared) = 1. / (1. + 2. * square(l_upper.get(0)) * H);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::Lapse<DataType> /*meta*/) const {\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n\n  get(*lapse) = sqrt(lapse_squared);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> deriv_lapse_multiplier,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::deriv_lapse_multiplier<DataType> /*meta*/) const {\n  const auto& lapse = get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n\n  get(*deriv_lapse_multiplier) =\n      -square(null_vector_0_) * lapse * lapse_squared;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<Scalar<DataType>*> shift_multiplier,\n    const gsl::not_null<CachedBuffer*> cache,\n    internal_tags::shift_multiplier<DataType> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n\n  get(*shift_multiplier) = -2. * null_vector_0_ * H * lapse_squared;\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3, Frame>*> shift,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::Shift<DataType, 3, Frame> /*meta*/) const {\n  const auto& l_upper =\n      cache->get_var(*this, internal_tags::l_upper<DataType, Frame>{});\n  const auto& shift_multiplier =\n      get(cache->get_var(*this, internal_tags::shift_multiplier<DataType>{}));\n\n  for (size_t i = 0; i < 3; ++i) {\n    shift->get(i) = shift_multiplier * l_upper.get(i + 1);\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3, Frame>*> deriv_shift,\n    const gsl::not_null<CachedBuffer*> cache,\n    DerivShift<DataType, Frame> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& l_upper =\n      cache->get_var(*this, internal_tags::l_upper<DataType, Frame>{});\n  const auto& l_lower =\n      cache->get_var(*this, internal_tags::l_lower<DataType, Frame>{});\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n  const auto& deriv_H =\n      cache->get_var(*this, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& deriv_l =\n      cache->get_var(*this, internal_tags::deriv_l<DataType, Frame>{});\n  const auto& inv_jacobian =\n      cache->get_var(*this, internal_tags::inv_jacobian<DataType, Frame>{});\n  const auto& deriv_inv_jacobian = cache->get_var(\n      *this, internal_tags::deriv_inv_jacobian<DataType, Frame>{});\n\n  for (int i = 0; i < 3; ++i) {\n    for (int k = 0; k < 3; ++k) {\n      deriv_shift->get(k, i) =\n          4. * H * l_upper.get(0) * l_upper.get(i + 1) * square(lapse_squared) *\n              (square(l_upper.get(0)) * deriv_H.get(k + 1) +\n               2. * H * l_upper.get(0) * deriv_l.get(k + 1, 0)) -\n          2. * lapse_squared *\n              (l_upper.get(0) * l_upper.get(i + 1) * deriv_H.get(k + 1) +\n               H * l_upper.get(i + 1) * deriv_l.get(k + 1, 0));\n      for (int j = 0; j < 3; ++j) {\n        for (int m = 0; m < 3; ++m) {\n          deriv_shift->get(k, i) +=\n              -2. * lapse_squared * H * l_upper.get(0) *\n              (inv_jacobian.get(j, i) * inv_jacobian.get(j, m) *\n                   deriv_l.get(k + 1, m + 1) +\n               inv_jacobian.get(j, i) * l_lower.get(m + 1) *\n                   deriv_inv_jacobian.get(k, j, m) +\n               inv_jacobian.get(j, m) * l_lower.get(m + 1) *\n                   deriv_inv_jacobian.get(k, j, i));\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::SpatialMetric<DataType, 3, Frame> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& l_lower =\n      cache->get_var(*this, internal_tags::l_lower<DataType, Frame>{});\n  const auto& jacobian =\n      cache->get_var(*this, internal_tags::jacobian<DataType, Frame>{});\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {  // Symmetry\n      spatial_metric->get(i, j) =\n          2. * H * l_lower.get(i + 1) * l_lower.get(j + 1);\n      for (size_t m = 0; m < 3; ++m) {\n        spatial_metric->get(i, j) += jacobian.get(i, m) * jacobian.get(j, m);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*> deriv_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    DerivSpatialMetric<DataType, Frame> /*meta*/) const {\n  const auto& l_lower =\n      cache->get_var(*this, internal_tags::l_lower<DataType, Frame>{});\n  const auto& deriv_H =\n      cache->get_var(*this, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& deriv_l =\n      cache->get_var(*this, internal_tags::deriv_l<DataType, Frame>{});\n  const auto& jacobian =\n      cache->get_var(*this, internal_tags::jacobian<DataType, Frame>{});\n  const auto& deriv_jacobian =\n      cache->get_var(*this, internal_tags::deriv_jacobian<DataType, Frame>{});\n\n  for (int k = 0; k < 3; ++k) {\n    for (int i = 0; i < 3; ++i) {\n      for (int j = i; j < 3; ++j) {  // Symmetry\n        deriv_spatial_metric->get(k, i, j) =\n            2. * l_lower.get(i + 1) * l_lower.get(j + 1) * deriv_H.get(k + 1) +\n            2. * H *\n                (l_lower.get(i + 1) * deriv_l.get(k + 1, j + 1) +\n                 l_lower.get(j + 1) * deriv_l.get(k + 1, i + 1));\n        for (int m = 0; m < 3; ++m) {\n          deriv_spatial_metric->get(k, i, j) +=\n              deriv_jacobian.get(k, i, m) * jacobian.get(j, m) +\n              deriv_jacobian.get(k, j, m) * jacobian.get(i, m);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> dt_spatial_metric,\n    const gsl::not_null<CachedBuffer*> /*cache*/,\n    ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>> /*meta*/) const {\n  std::fill(dt_spatial_metric->begin(), dt_spatial_metric->end(), 0.);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3, Frame>*> inverse_spatial_metric,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::InverseSpatialMetric<DataType, 3, Frame> /*meta*/) const {\n  const auto& H = get(cache->get_var(*this, internal_tags::H<DataType>{}));\n  const auto& lapse_squared =\n      get(cache->get_var(*this, internal_tags::lapse_squared<DataType>{}));\n  const auto& l_upper =\n      cache->get_var(*this, internal_tags::l_upper<DataType, Frame>{});\n  const auto& inv_jacobian =\n      cache->get_var(*this, internal_tags::inv_jacobian<DataType, Frame>{});\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {  // Symmetry\n      inverse_spatial_metric->get(i, j) =\n          -2. * H * lapse_squared * l_upper.get(i + 1) * l_upper.get(j + 1);\n      for (size_t m = 0; m < 3; ++m) {\n        inverse_spatial_metric->get(i, j) +=\n            inv_jacobian.get(m, i) * inv_jacobian.get(m, j);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> extrinsic_curvature,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::ExtrinsicCurvature<DataType, 3, Frame> /*meta*/) const {\n  gr::extrinsic_curvature(\n      extrinsic_curvature, cache->get_var(*this, gr::Tags::Lapse<DataType>{}),\n      cache->get_var(*this, gr::Tags::Shift<DataType, 3, Frame>{}),\n      cache->get_var(*this, DerivShift<DataType, Frame>{}),\n      cache->get_var(*this, gr::Tags::SpatialMetric<DataType, 3, Frame>{}),\n      cache->get_var(*this,\n                     ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>{}),\n      cache->get_var(*this, DerivSpatialMetric<DataType, Frame>{}));\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*>\n        spatial_christoffel_first_kind,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame> /*meta*/) const {\n  const auto& d_spatial_metric =\n      cache->get_var(*this, DerivSpatialMetric<DataType, Frame>{});\n  gr::christoffel_first_kind<3, Frame, IndexType::Spatial, DataType>(\n      spatial_christoffel_first_kind, d_spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\nvoid SphericalKerrSchild::IntermediateComputer<DataType, Frame>::operator()(\n    const gsl::not_null<tnsr::Ijj<DataType, 3, Frame>*>\n        spatial_christoffel_second_kind,\n    const gsl::not_null<CachedBuffer*> cache,\n    gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame> /*meta*/) const {\n  const auto& spatial_christoffel_first_kind = cache->get_var(\n      *this, gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>{});\n  const auto& inverse_spatial_metric = cache->get_var(\n      *this, gr::Tags::InverseSpatialMetric<DataType, 3, Frame>{});\n  raise_or_lower_first_index<DataType, SpatialIndex<3, UpLo::Lo, Frame>,\n                             SpatialIndex<3, UpLo::Lo, Frame>>(\n      spatial_christoffel_second_kind, spatial_christoffel_first_kind,\n      inverse_spatial_metric);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::i<DataType, 3, Frame>\nSphericalKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    DerivLapse<DataType, Frame> /*meta*/) {\n  tnsr::i<DataType, 3, Frame> result{};\n  const auto& deriv_H =\n      get_var(computer, internal_tags::deriv_H<DataType, Frame>{});\n  const auto& deriv_lapse_multiplier =\n      get(get_var(computer, internal_tags::deriv_lapse_multiplier<DataType>{}));\n\n  for (size_t i = 0; i < 3; ++i) {\n    result.get(i) = deriv_lapse_multiplier * deriv_H.get(i + 1);\n  }\n  return result;\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType>\nSphericalKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/) {\n  const auto& H = get(get_var(computer, internal_tags::H<DataType>{}));\n\n  return make_with_value<Scalar<DataType>>(H, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::I<DataType, 3, Frame>\nSphericalKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>> /*meta*/) {\n  const auto& H = get(get_var(computer, internal_tags::H<DataType>()));\n\n  return make_with_value<tnsr::I<DataType, 3, Frame>>(H, 0.);\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType>\nSphericalKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/) {\n  const auto& jacobian =\n      get_var(computer, internal_tags::jacobian<DataType, Frame>{});\n\n  return Scalar<DataType>(get(determinant(jacobian)) /\n                          get(get_var(computer, gr::Tags::Lapse<DataType>{})));\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::i<DataType, 3, Frame>\nSphericalKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::DerivDetSpatialMetric<DataType, 3, Frame> /*meta*/) {\n  const auto& deriv_H =\n      get_var(computer, internal_tags::deriv_H<DataType, Frame>{});\n\n  auto result =\n      make_with_value<tnsr::i<DataType, 3, Frame>>(get<0>(deriv_H), 0.);\n  for (size_t i = 0; i < 3; ++i) {\n    result.get(i) = 2. * square(null_vector_0_) * deriv_H.get(i + 1);\n  }\n  return result;\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType>\nSphericalKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) {\n  return trace(\n      get_var(computer, gr::Tags::ExtrinsicCurvature<DataType, 3, Frame>{}),\n      get_var(computer, gr::Tags::InverseSpatialMetric<DataType, 3, Frame>{}));\n}\n\ntemplate <typename DataType, typename Frame>\ntnsr::I<DataType, 3, Frame>\nSphericalKerrSchild::IntermediateVars<DataType, Frame>::get_var(\n    const IntermediateComputer<DataType, Frame>& computer,\n    gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3, Frame> /*meta*/) {\n  const auto& inverse_spatial_metric =\n      get_var(computer, gr::Tags::InverseSpatialMetric<DataType, 3, Frame>{});\n  const auto& spatial_christoffel_second_kind = get_var(\n      computer, gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>{});\n  return trace_last_indices<DataType, SpatialIndex<3, UpLo::Up, Frame>,\n                            SpatialIndex<3, UpLo::Lo, Frame>>(\n      spatial_christoffel_second_kind, inverse_spatial_metric);\n}\n\nbool operator==(const SphericalKerrSchild& lhs,\n                const SphericalKerrSchild& rhs) {\n  return lhs.mass() == rhs.mass() and\n         lhs.dimensionless_spin() == rhs.dimensionless_spin() and\n         lhs.center() == rhs.center();\n}\n\nbool operator!=(const SphericalKerrSchild& lhs,\n                const SphericalKerrSchild& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                            \\\n  template class SphericalKerrSchild::IntermediateVars<DTYPE(data),     \\\n                                                       FRAME(data)>;    \\\n  template class SphericalKerrSchild::IntermediateComputer<DTYPE(data), \\\n                                                           FRAME(data)>;\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, double),\n                        (::Frame::Inertial, ::Frame::Grid))\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\nnamespace Solutions {\n\n/*!\n * \\brief Kerr black hole in Spherical Kerr-Schild coordinates\n *\n * ## Introduction\n *\n * Given a Kerr-Schild (KS) black hole system, we denote the coordinate system\n * using \\f$\\{t,x,y,z\\}\\f$. In the transformed system, Spherical Kerr-Schild\n * (Spherical KS), we denote the coordinate system using\n * \\f$\\{t,\\bar{x},\\bar{y},\\bar{z}\\}\\f$. Further, when considering indexed\n * objects, we will use Greek and Latin indices with the standard convention\n * \\f$(\\mu=0,1,2,3\\f$ and \\f$i=1,2,3\\f$ respectively), but we will use a bar to\n * denote that a given index is in reference to the Spherical KS coordinate\n * system (i.e. \\f$i\\f$ vs. \\f$\\bar{\\imath}\\f$ and \\f$\\mu\\f$ vs.\n * \\f$\\bar{\\mu}\\f$).\n *\n * ## Spin in the z direction\n *\n * ### The Transformation\n *\n * The Boyer-Lindquist radius for KS with its spin in the \\f$z\\f$ direction is\n * defined by\n *\n * \\f{align}{\n *    \\frac{x^2 + y^2}{r^2 + a^2} + \\frac{z^2}{r^2} = 1,\n * \\f}\n *\n * or equivalently,\n *\n * \\f{align}{\n *    r^2 &= \\frac{1}{2}\\left(x^2+y^2+z^2-a^2\\right) +\n *    \\left(\\frac{1}{4}\\left(x^2+y^2+z^2-a^2\\right)^2 + a^2z^2\\right)^{1/2}.\n * \\f}\n *\n * The Spherical KS coordinates\n *\n * \\f{align}{\n *    \\vec{\\bar{x}} = x^{\\bar{\\imath}} = x_{\\bar{\\imath}} =\n *    (\\bar{x},\\bar{y},\\bar{z}),\n * \\f}\n *\n * and KS coordinates\n *\n * \\f{align}{\n *    \\vec{x} = x^{i} = x_{i} = (x,y,z),\n * \\f}\n *\n * are related by\n *\n * \\f{align}{\n *    \\left(\\frac{\\bar{x}}{r},\\frac{\\bar{y}}{r},\\frac{\\bar{z}}{r}\\right) \\equiv\n *    \\left(\\frac{x}{\\rho},\\frac{y}{\\rho},\\frac{z}{r}\\right),\n * \\f}\n *\n * where we have defined\n *\n * \\f{align}{\n *   \\rho^2 \\equiv r^2 + a^2. \\label{eq:rho}\n * \\f}\n *\n * Therefore, we have that \\f$r\\f$ satisfies the equation for a sphere in\n * Spherical KS\n *\n * \\f{align}{\n *    r^2 = \\vec{\\bar{x}}\\cdot\\vec{\\bar{x}} = \\bar{x}^2 + \\bar{y}^2 +\n *    \\bar{z}^2.\n * \\f}\n *\n * It is clear to see that the Spherical KS radius coincides with the\n * Boyer-Lindquist radius.\n *\n * ## Spin in an Arbitrary Direction\n *\n * Given that the remaining important quantities take forms that are easily\n * specialized to the \\f$z\\f$ spin case, we instead focus on the general case\n * for brevity.\n *\n * ### The Transformation\n *\n * The Boyer-Lindquist radius for KS with spin in an arbitrary direction is\n * defined by\n *\n * \\f{align}{\n *    r^2 = \\frac{1}{2}\\left(\\vec{x}\\cdot\\vec{x}-a^2\\right) +\n *    \\left(\\frac{1}{4}\\left(\\vec{x}\\cdot\\vec{x}-a^2\\right)^2 +\n *    \\left(\\vec{a}\\cdot\\vec{x}\\right)^2\\right)^{1/2}.\n * \\f}\n *\n * Then, defining two transformation matrices \\f$Q^{\\bar{\\imath}}{}_{j}\\f$ and\n * \\f$P^{j}{}_{\\bar{\\imath}}\\f$ as\n *\n * \\f{align}{\n *    Q^{\\bar{\\imath}}{}_{j} &= \\frac{r}{\\rho}\\delta^{\\bar{\\imath}}{}_{j} +\n *    \\frac{1}{(\\rho + r)\\rho}a^{\\bar{\\imath}}a_{j}, \\\\\n *    P^{j}{}_{\\bar{\\imath}} &= \\frac{\\rho}{r}\\delta^{j}{}_{\\bar{\\imath}} -\n *    \\frac{1}{(\\rho + r)r}a^{j}a_{\\bar{\\imath}},\n * \\f}\n *\n * where the definition of \\f$\\rho\\f$ is identical to Eq. \\f$(\\ref{eq:rho})\\f$,\n * such that\n *\n * \\f{align}{\n *   Q^{\\bar{\\imath}}{}_{j}x^{j} &= x^{\\bar{\\imath}}, \\\\\n *   P^{j}{}_{\\bar{\\imath}}x^{\\bar{\\imath}} &= x^{j}.\n * \\f}\n *\n * We again recover that \\f$r\\f$ satisfies the equation for a sphere in\n * Spherical KS\n *\n * \\f{align}{\n *   r^2 = \\vec{\\bar{x}}\\cdot\\vec{\\bar{x}} = \\bar{x}^2+\\bar{y}^2+\\bar{z}^2,\n * \\f}\n *\n * and we also have that\n *\n * \\f{align}{\n *   \\vec{a}\\cdot\\vec{\\bar{x}} = \\vec{a}\\cdot\\vec{x}.\n * \\f}\n *\n * Note that \\f$Q^{\\bar{\\imath}}{}_{ j}\\f$ and \\f$P^{j}{}_{\\bar{\\imath}}\\f$\n * satisfy\n *\n * \\f{align}{\n *   P^{i}{}_{\\bar{\\jmath}}\\,Q^{\\bar{\\jmath}}{}_{ k} = \\delta^{i}{}_{ k}.\n * \\f}\n *\n * The Jacobian is then given by\n *\n * \\f{align}{\n *   T^{i}{}_{\\bar{\\jmath}} = \\partial_{\\bar{\\jmath}}\\,x^{i} =\n *   \\frac{\\partial x^i}{\\partial x^{\\bar{\\jmath}}},\n * \\f}\n *\n * while its inverse is given by\n *\n * \\f{align}{\n *   S^{\\bar{\\jmath}}{}_{ i} = \\partial_{i}\\,x^{\\bar{\\jmath}} =\n *   \\frac{\\partial x^{\\bar{\\jmath}}}{\\partial x^i},\n * \\f}\n *\n * which in turn satisfies\n *\n * \\f{align}{\n *   T^{i}{}_{\\bar{\\jmath}}\\,S^{\\bar{\\jmath}}{}_{ k} &= \\delta^i_{\\; k}.\n * \\f}\n *\n * ### The Metric\n *\n * A KS coordinate system is defined by\n *\n * \\f{align}{\n *   g_{\\mu\\nu} = \\eta_{\\mu\\nu} + 2Hl_{\\mu}l_{\\nu},\n * \\f}\n *\n * where \\f$H\\f$ is a scalar function of the coordinates, \\f$\\eta_{\\mu\\nu}\\f$ is\n * the Minkowski metric, and \\f$l^\\mu\\f$ is a null vector. Note that the inverse\n * of the spacetime metric is given by\n *\n * \\f{align}{\n *   g^{\\mu\\nu} = \\eta^{\\mu\\nu} - 2Hl^{\\mu}l^{\\nu}.\n * \\f}\n *\n * The scalar function \\f$H\\f$ takes the form\n *\n * \\f{align}{\n *   H = \\frac{Mr^3}{r^4 + \\left(\\vec{a}\\cdot\\vec{x}\\right)^{2}},\n * \\f}\n *\n * where \\f$M\\f$ is the mass, while the spatial part of the null vector takes\n * the form\n *\n * \\f{align}{\n *   l_{i} = l^{i} = \\frac{r\\vec{x} - \\vec{a}\\times\\vec{x} +\n *   \\frac{(\\vec{a}\\cdot\\vec{x})\\vec{a}}{r}}{\\rho^2}.\n * \\f}\n *\n * Note that the full spacetime form of the null vector is\n *\n * \\f{align}{\n *   l_{\\mu} &= (-1,l_{i}), & l^{\\mu} &= (1,l^{i}).\n * \\f}\n *\n * Transforming the KS spatial metric then yields the following Spherical KS\n * spatial metric\n *\n * \\f{align}{\n *   \\gamma_{\\bar{\\imath}\\bar{\\jmath}} &=\n *   \\gamma_{mn}T^{m}{}_{\\bar{\\imath}}\\,T^{n}{}_{\\bar{\\jmath}}, \\nonumber \\\\\n *   &= \\eta_{mn}T^{m}{}_{\\bar{\\imath}}\\,T^{n}{}_{\\bar{\\jmath}} +\n *   2Hl_{m}l_{n}T^{m}{}_{\\bar{\\imath}}\\,T^{n}{}_{\\bar{\\jmath}}, \\nonumber \\\\\n *   &= \\eta_{\\bar{\\imath}\\bar{\\jmath}} + 2Hl_{\\bar{\\imath}}l_{\\bar{\\jmath}}.\n * \\f}\n *\n * The transformed spacetime Minkowski metric is given by\n *\n * \\f{align}{\n *   \\eta_{\\bar{\\mu}\\bar{\\nu}} = (-1)\\otimes\\eta_{\\bar{\\imath}\\bar{\\jmath}},\n * \\f}\n *\n * and the transformed spacetime null vector is given by\n *\n * \\f{align}{\n *   l_{\\bar{\\mu}} &= (-1,l_{\\bar{\\imath}}), &\n *   l^{\\bar{\\mu}} &= (1,l^{\\bar{\\imath}}).\n * \\f}\n *\n * Therefore, the Spherical KS spacetime metric is\n *\n * \\f{align}{\n *   g_{\\bar{\\mu}\\bar{\\nu}} = \\eta_{\\bar{\\mu}\\bar{\\nu}} +\n *   2Hl_{\\bar{\\mu}}l_{\\bar{\\nu}}.\n * \\f}\n *\n * Further, we have that the lapse in Spherical KS is given by\n *\n * \\f{align}{\n *   \\alpha = \\left(1 + 2H\\right)^{-1/2},\n * \\f}\n *\n * and the shift in Spherical KS by\n *\n * \\f{align}{\n *   \\beta^{\\bar{\\imath}} &= -\\frac{2Hl^{t}l^{\\bar{\\imath}}}{1 + 2Hl^{t}l^{t}} =\n *   -2H\\alpha^{2}l^{t}l^{\\bar{\\imath}}, & \\beta_{\\bar{\\imath}} &=\n *  -2Hl_{t}l_{\\bar{\\imath}}.\n * \\f}\n *\n * ### Derivatives\n *\n * The derivatives of the preceding quantities are\n *\n * \\f{align}{\n *   \\frac{\\partial r}{\\partial x^{i}} &= \\frac{r^{2}x_{i} +\n *   \\left(\\vec{a}\\cdot\\vec{x}\\right)a_{i}}{rs}, \\\\\n *   \\partial_{\\bar{\\imath}}H &= HT^{m}{}_{\\bar{\\imath}}\n *   \\left[\\frac{3}{r}\\frac{\\partial r}{\\partial x^{m}} -\n *   \\frac{4r^{3}\\frac{\\partial r}{\\partial x^{m}} +\n *   2\\left(\\vec{a}\\cdot\\vec{x}\\right)a_{m}}{r^{4} +\n *   \\left(\\vec{a}\\cdot\\vec{x}\\right)^{2}}\\right], \\\\\n *   \\partial_{\\bar{\\jmath}}l^{\\bar{\\imath}} &=\n *   \\partial_{\\bar{\\jmath}}\\left(l_{k}T^{k}{}_{\\bar{\\imath}}\\right),\n *   \\nonumber \\\\\n *   &= T^{k}{}_{\\bar{\\imath}}T^{m}{}_{\\bar{\\jmath}}\\frac{1}{\\rho^{2}}\n *   \\left[\\left(x_{k} - 2rl_{k} -\n *   \\frac{\\left(\\vec{a}\\cdot\\vec{x}\\right)a_{k}}{r^{2}}\\right)\n *   \\frac{\\partial r}{\\partial x^{m}} + r\\delta_{km} + \\frac{a_{k}a_{m}}{r} +\n *   \\epsilon^{kmn}a_{n}\\right] +\n *   l_{k}\\partial_{\\bar{\\jmath}}T^{k}{}_{\\bar{\\imath}}, \\\\\n *   \\partial_{\\bar{k}}\\gamma_{\\bar{\\imath}\\bar{\\jmath}} &=\n *   2l_{\\bar{\\imath}}l_{\\bar{\\jmath}}\\partial_{\\bar{k}}H +\n *   4Hl_{(\\bar{\\imath}}\\partial_{\\bar{k}}l_{\\bar{\\jmath})} +\n *   T^{m}{}_{\\bar{\\jmath}}\\partial_{\\bar{k}}T^{m}{}_{\\bar{\\imath}} +\n *   T^{m}{}_{\\bar{\\imath}}\\partial_{\\bar{k}}T^{m}{}_{\\bar{\\jmath}}, \\\\\n *   \\partial_{\\bar{k}}\\alpha &= -\\left(1+2H\\right)^{-3/2}\\partial_{\\bar{k}}H =\n *   -\\alpha^{3}\\partial_{\\bar{k}}H, \\\\\n *   \\partial_{\\bar{k}}\\beta^{i} &=\n *   2\\alpha^{2}\\left[l^{\\bar{\\imath}}\\partial_{\\bar{k}}H +\n *   H\\left(S^{\\bar{\\imath}}{}_{j}S^{\\bar{m}}{}_{n}\n *   \\delta_{nj}\\partial_{\\bar{k}}l_{\\bar{m}} +\n *   S^{\\bar{\\imath}}{}_{j}l_{\\bar{m}}\\partial_{\\bar{k}}S^{\\bar{m}}{}_{n}\n *   \\delta_{nj} + S^{\\bar{m}}{}_{n}\\delta_{nj}l_{\\bar{m}}\n *   \\partial_{\\bar{k}}S^{\\bar{\\imath}}{}_{j}\\right)\\right] -\n *   4Hl^{\\bar{\\imath}}\\alpha^{4}\\partial_{\\bar{k}}H,\n * \\f}\n *\n * where we have defined \\f$s\\f$ as\n *\n * \\f{align}{\n *   s &\\equiv r^{2} + \\frac{\\left(\\vec{a}\\cdot\\vec{x}\\right)^{2}}{r^{2}}.\n *   \\label{eq: s_number}\n * \\f}\n *\n * ## Code\n *\n * While the previous sections described the relevant physical quantities, the\n * actual files make use of internally defined objects to ease the computation\n * of the Jacobian, the inverse Jacobian, and their corresponding derivatives.\n * Therefore, we now list these intermediary objects as well as how they\n * construct the aforementioned physical quantities with the appropriate\n * definition found in the code (all for arbitrary spin).\n *\n * ### Helper Matrices\n *\n * The intermediary objects used to define the various Jacobian objects, the\n * so-called \"helper matrices\", are defined below.\n *\n * \\f{align}{\n *   F^{i}{}_{\\bar{k}} &\\equiv\n *   -\\frac{1}{\\rho r^{3}}\\left(a^{2}\\delta^{i}{}_{\\bar{k}} -\n *   a^{i}a_{\\bar{k}}\\right), \\\\\n *   \\left(G_1\\right)^{\\bar{\\imath}}{}_{ \\bar{m}} &\\equiv\n *   \\frac{1}{\\rho^{2}r}\\left(a^{2}\\delta^{\\bar{\\imath}}{}_{\\bar{m}} -\n *   a^{\\bar{\\imath}}a_{\\bar{m}}\\right), \\\\\n *   \\left(G_2\\right)^{\\bar{n}}{}_{ j} &\\equiv\n *   \\frac{\\rho^{2}}{sr}Q^{\\bar{n}}{}_{j}, \\\\\n *   D^{i}{}_{\\bar{m}} &\\equiv\n *   \\frac{1}{\\rho^{3}r}\\left(a^{2}\\delta^{i}{}_{\\bar{m}} -\n *   a^{i}a_{\\bar{m}}\\right), \\\\\n *   C^{i}{}_{\\bar{m}} &\\equiv D^{i}{}_{\\bar{m}} - 3F^{i}{}_{\\bar{m}} =\n *   \\frac{1}{\\rho r}\\left(\\frac{1}{\\rho^{2}} +\n *   \\frac{3}{r^{2}}\\right)\\left(a^{2}\\delta^{i}{}_{\\bar{m}} -\n *   a^{i}a_{\\bar{m}}\\right), \\\\\n *   \\left(E_1\\right)^{i}{}_{\\bar{m}} &\\equiv\n *   -\\frac{1}{\\rho^{2}}\\left(\\frac{1}{r^{2}} +\n *   \\frac{2}{\\rho^{2}}\\right)\\left(a^{2}\\delta^{i}{}_{\\bar{m}} -\n *   a^{i}a_{\\bar{m}}\\right), \\nonumber \\\\\n *   &= -\\frac{\\left(\\rho^{2} +\n *   2r^{2}\\right)}{r^{2}\\rho^{4}}\\left(a^{2}\\delta^{i}{}_{\\bar{m}} -\n *   a^{i}a_{\\bar{m}}\\right), \\\\\n *   \\left(E_2\\right)^{\\bar{n}}{}_{ j} &\\equiv \\left[-\\frac{a^{2}}{\\rho^{2}r} -\n *   \\frac{2}{s}\\left(r - \\frac{\\left(\\vec{a}\\cdot\\vec{x}\\right)^{2}}{r^{3}}\n *   \\right)\\right]\\cdot\\left(G_2\\right)^{\\bar{n}}{}_{ j} +\n *   \\frac{1}{s}P^{\\bar{n}}{}_{ j},\n * \\f}\n *\n * where \\f$s\\f$ is defined identically to Eq. \\f$(\\ref{eq: s_number})\\f$.\n *\n * ### Physical Quantities\n *\n * Below are the definitions for how we construct the Jacobian, inverse\n * Jacobian, derivative of the Jacobian, and derivative of the inverse Jacobian\n * in the code using the helper matrices.\n *\n * \\f{align}{\n *   T^{i}{}_{\\bar{\\jmath}} &= P^{i}{}_{\\bar{\\jmath}} +\n *   F^{i}{}_{\\bar{k}}x^{\\bar{k}}x_{\\bar{\\jmath}}, \\\\\n *   S^{\\bar{\\imath}}{}_{ j} &= Q^{\\bar{\\imath}}{}_{ j} +\n *   \\left(G_1\\right)^{\\bar{\\imath}}{}_{\\bar{m}}x^{\\bar{m}}x_{\\bar{n}}\n *   \\left(G_2\\right)^{\\bar{n}}{}_{ j}, \\\\\n *   \\partial_{\\bar{k}}T^{i}{}_{\\bar{\\jmath}} &=\n *   F^{i}{}_{\\bar{\\jmath}}x_{\\bar{k}} + F^{i}{}_{\\bar{k}}x_{\\bar{\\jmath}} +\n *   F^{i}{}_{\\bar{m}}x^{\\bar{m}}\\delta_{jk} +\n *   C^{i}{}_{\\bar{m}}\\frac{x_{\\bar{k}}x^{\\bar{m}}x_{\\bar{\\jmath}}}{r^{2}}, \\\\\n *   \\partial_{\\bar{k}}S^{\\;\\bar{\\imath}}{}_{ j} &=\n *   D^{\\bar{\\imath}}{}_{j}x_{\\bar{k}} +\n *   \\left(G_1\\right)^{\\bar{\\imath}}{}_{\\bar{k}}x_{\\bar{n}}\n *   \\left(G_2\\right)^{\\bar{n}}{}_{j} +\n *   \\left(G_1\\right)^{\\bar{\\imath}}{}_{\\bar{m}}x^{\\bar{m}}\n *   \\left(G_2\\right)^{\\bar{n}}{}_{j}\\delta_{\\bar{n}\\bar{k}} \\nonumber \\\\\n *   &\\quad +\n *   \\left(E_1\\right)^{i}{}_{\\bar{m}}\\frac{x_{\\bar{k}}x^{\\bar{m}}x_{\\bar{n}}}{r}\n *   \\left(G_2\\right)^{\\bar{n}}{}_{j} +\n *   \\left(G_1\\right)^{\\bar{i}}{}_{\\bar{m}}\n *   \\frac{x_{\\bar{k}}x^{\\bar{m}}x_{\\bar{n}}}{r}\\left(E_2\\right)^{\\bar{n}}{}_{j}\n *   - \\left(G_1\\right)^{\\bar{\\imath}}{}_{\\bar{m}}x^{\\bar{m}}x_{\\bar{n}}\n *   \\left(G_2\\right)^{\\bar{n}}{}_{j}\n *   \\frac{2\\left(\\vec{a}\\cdot\\vec{x}\\right)}{sr^{2}}a_{\\bar{k}}.\n * \\f}\n */\nclass SphericalKerrSchild : public AnalyticSolution<3_st>,\n                            public MarkAsAnalyticSolution {\n public:\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\"Mass of the black hole\"};\n    static type lower_bound() { return 0.; }\n  };\n  struct Spin {\n    using type = std::array<double, volume_dim>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] dimensionless spin of the black hole\"};\n  };\n  struct Center {\n    using type = std::array<double, volume_dim>;\n    static constexpr Options::String help = {\n        \"The [x,y,z] center of the black hole\"};\n  };\n  using options = tmpl::list<Mass, Spin, Center>;\n  static constexpr Options::String help{\n      \"Black hole in Spherical Kerr-Schild coordinates\"};\n\n  template <typename DataType, typename Frame = Frame::Inertial>\n  using tags = tmpl::flatten<tmpl::list<\n      AnalyticSolution<3_st>::tags<DataType, Frame>,\n      gr::Tags::DerivDetSpatialMetric<DataType, 3, Frame>,\n      gr::Tags::TraceExtrinsicCurvature<DataType>,\n      gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>,\n      gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>,\n      gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3, Frame>>>;\n\n  SphericalKerrSchild(double mass, Spin::type dimensionless_spin,\n                      Center::type center,\n                      const Options::Context& context = {});\n\n  explicit SphericalKerrSchild(CkMigrateMessage* /*unused*/);\n\n  SphericalKerrSchild() = default;\n  SphericalKerrSchild(const SphericalKerrSchild& /*rhs*/) = default;\n  SphericalKerrSchild& operator=(const SphericalKerrSchild& /*rhs*/) = default;\n  SphericalKerrSchild(SphericalKerrSchild&& /*rhs*/) = default;\n  SphericalKerrSchild& operator=(SphericalKerrSchild&& /*rhs*/) = default;\n  ~SphericalKerrSchild() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  SPECTRE_ALWAYS_INLINE double mass() const { return mass_; }\n  SPECTRE_ALWAYS_INLINE const std::array<double, volume_dim>& center() const {\n    return center_;\n  }\n  SPECTRE_ALWAYS_INLINE const std::array<double, volume_dim>&\n  dimensionless_spin() const {\n    return dimensionless_spin_;\n  }\n\n  struct internal_tags {\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using x_minus_center = ::Tags::TempI<0, 3, Frame, DataType>;\n    template <typename DataType>\n    using r_squared = ::Tags::TempScalar<1, DataType>;\n    template <typename DataType>\n    using r = ::Tags::TempScalar<2, DataType>;\n    template <typename DataType>\n    using rho = ::Tags::TempScalar<3, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using helper_matrix_F = ::Tags::TempIj<4, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using transformation_matrix_P = ::Tags::TempIj<5, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using jacobian = ::Tags::TempIj<6, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using helper_matrix_D = ::Tags::TempIj<7, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using helper_matrix_C = ::Tags::TempIj<8, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_jacobian = ::Tags::TempiJk<9, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using transformation_matrix_Q = ::Tags::TempIj<10, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using helper_matrix_G1 = ::Tags::TempIj<11, 3, Frame, DataType>;\n    template <typename DataType>\n    using a_dot_x = ::Tags::TempScalar<12, DataType>;\n    template <typename DataType>\n    using s_number = ::Tags::TempScalar<13, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using helper_matrix_G2 = ::Tags::TempIj<14, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using G1_dot_x = ::Tags::TempI<15, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using G2_dot_x = ::Tags::Tempi<16, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using inv_jacobian = ::Tags::TempIj<17, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using helper_matrix_E1 = ::Tags::TempIj<18, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using helper_matrix_E2 = ::Tags::TempIj<19, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_inv_jacobian = ::Tags::TempiJk<20, 3, Frame, DataType>;\n    template <typename DataType>\n    using H = ::Tags::TempScalar<21, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using kerr_schild_x = ::Tags::TempI<22, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using a_cross_x = ::Tags::TempI<23, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using kerr_schild_l = ::Tags::TempI<24, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using l_lower = ::Tags::Tempi<25, 4, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using l_upper = ::Tags::TempI<26, 4, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_r = ::Tags::TempI<27, 3, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_H = ::Tags::TempI<28, 4, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using kerr_schild_deriv_l = ::Tags::Tempij<29, 4, Frame, DataType>;\n    template <typename DataType, typename Frame = ::Frame::Inertial>\n    using deriv_l = ::Tags::Tempij<30, 4, Frame, DataType>;\n    template <typename DataType>\n    using lapse_squared = ::Tags::TempScalar<31, DataType>;\n    template <typename DataType>\n    using deriv_lapse_multiplier = ::Tags::TempScalar<32, DataType>;\n    template <typename DataType>\n    using shift_multiplier = ::Tags::TempScalar<33, DataType>;\n  };\n\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  using CachedBuffer = CachedTempBuffer<\n      internal_tags::x_minus_center<DataType, Frame>,\n      internal_tags::r_squared<DataType>, internal_tags::r<DataType>,\n      internal_tags::rho<DataType>,\n      internal_tags::helper_matrix_F<DataType, Frame>,\n      internal_tags::transformation_matrix_P<DataType, Frame>,\n      internal_tags::jacobian<DataType, Frame>,\n      internal_tags::helper_matrix_D<DataType, Frame>,\n      internal_tags::helper_matrix_C<DataType, Frame>,\n      internal_tags::deriv_jacobian<DataType, Frame>,\n      internal_tags::transformation_matrix_Q<DataType, Frame>,\n      internal_tags::helper_matrix_G1<DataType, Frame>,\n      internal_tags::a_dot_x<DataType>, internal_tags::s_number<DataType>,\n      internal_tags::helper_matrix_G2<DataType, Frame>,\n      internal_tags::G1_dot_x<DataType, Frame>,\n      internal_tags::G2_dot_x<DataType, Frame>,\n      internal_tags::inv_jacobian<DataType, Frame>,\n      internal_tags::helper_matrix_E1<DataType, Frame>,\n      internal_tags::helper_matrix_E2<DataType, Frame>,\n      internal_tags::deriv_inv_jacobian<DataType, Frame>,\n      internal_tags::H<DataType>, internal_tags::kerr_schild_x<DataType, Frame>,\n      internal_tags::a_cross_x<DataType, Frame>,\n      internal_tags::kerr_schild_l<DataType, Frame>,\n      internal_tags::l_lower<DataType, Frame>,\n      internal_tags::l_upper<DataType, Frame>,\n      internal_tags::deriv_r<DataType, Frame>,\n      internal_tags::deriv_H<DataType, Frame>,\n      internal_tags::kerr_schild_deriv_l<DataType, Frame>,\n      internal_tags::deriv_l<DataType, Frame>,\n      internal_tags::lapse_squared<DataType>, gr::Tags::Lapse<DataType>,\n      internal_tags::deriv_lapse_multiplier<DataType>,\n      internal_tags::shift_multiplier<DataType>,\n      gr::Tags::Shift<DataType, 3, Frame>, DerivShift<DataType, Frame>,\n      gr::Tags::SpatialMetric<DataType, 3, Frame>,\n      DerivSpatialMetric<DataType, Frame>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>,\n      gr::Tags::ExtrinsicCurvature<DataType, 3, Frame>,\n      gr::Tags::InverseSpatialMetric<DataType, 3, Frame>,\n      gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>,\n      gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>>;\n\n  // forward-declaration needed.\n  template <typename DataType, typename Frame>\n  class IntermediateVars;\n\n  template <typename DataType, typename Frame = Frame::Inertial>\n  using allowed_tags =\n      tmpl::push_back<tags<DataType, Frame>,\n                      typename internal_tags::inv_jacobian<DataType, Frame>>;\n\n  template <typename DataType, typename Frame, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame>& x, double /*t*/,\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(\n        tmpl2::flat_all_v<\n            tmpl::list_contains_v<allowed_tags<DataType, Frame>, Tags>...>,\n        \"At least one of the requested tags is not supported. The requested \"\n        \"tags are listed as template parameters of the `variables` function.\");\n    IntermediateVars<DataType, Frame> cache(get_size(*x.begin()));\n    IntermediateComputer<DataType, Frame> computer(*this, x);\n    return {cache.get_var(computer, Tags{})...};\n  }\n\n  template <typename DataType, typename Frame, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame>& x, double /*t*/,\n      tmpl::list<Tags...> /*meta*/,\n      gsl::not_null<IntermediateVars<DataType, Frame>*> cache) const {\n    static_assert(\n        tmpl2::flat_all_v<\n            tmpl::list_contains_v<allowed_tags<DataType, Frame>, Tags>...>,\n        \"At least one of the requested tags is not supported. The requested \"\n        \"tags are listed as template parameters of the `variables` function.\");\n    if (cache->number_of_grid_points() != get_size(*x.begin())) {\n      *cache = IntermediateVars<DataType, Frame>(get_size(*x.begin()));\n    }\n    IntermediateComputer<DataType, Frame> computer(*this, x);\n    return {cache->get_var(computer, Tags{})...};\n  }\n\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  class IntermediateComputer {\n   public:\n    using CachedBuffer = SphericalKerrSchild::CachedBuffer<DataType, Frame>;\n\n    IntermediateComputer(const SphericalKerrSchild& solution,\n                         const tnsr::I<DataType, 3, Frame>& x);\n\n    // spin_a_and_squared(const SphericalKerrSchild& solution);\n\n    void operator()(\n        const gsl::not_null<tnsr::I<DataType, 3, Frame>*> x_minus_center,\n        const gsl::not_null<CachedBuffer*> /*cache*/,\n        internal_tags::x_minus_center<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> r_squared,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::r_squared<DataType> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> r,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::r<DataType> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> rho,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::rho<DataType> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_F,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::helper_matrix_F<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*>\n            transformation_matrix_P,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::transformation_matrix_P<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> jacobian,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::jacobian<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_D,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::helper_matrix_D<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_C,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::helper_matrix_C<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::iJk<DataType, 3, Frame>*> deriv_jacobian,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::deriv_jacobian<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*>\n            transformation_matrix_Q,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::transformation_matrix_Q<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_G1,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::helper_matrix_G1<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> a_dot_x,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::a_dot_x<DataType> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> s_number,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::s_number<DataType> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_G2,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::helper_matrix_G2<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::I<DataType, 3, Frame>*> G1_dot_x,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::G1_dot_x<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::i<DataType, 3, Frame>*> G2_dot_x,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::G2_dot_x<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> inv_jacobian,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::inv_jacobian<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_E1,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::helper_matrix_E1<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ij<DataType, 3, Frame>*> helper_matrix_E2,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::helper_matrix_E2<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::iJk<DataType, 3, Frame>*> deriv_inv_jacobian,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::deriv_inv_jacobian<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> H,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::H<DataType> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::I<DataType, 3, Frame>*> kerr_schild_x,\n        const gsl::not_null<CachedBuffer*> /*cache*/,\n        internal_tags::kerr_schild_x<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::I<DataType, 3, Frame>*> a_cross_x,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::a_cross_x<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::I<DataType, 3, Frame>*> kerr_schild_l,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::kerr_schild_l<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::i<DataType, 4, Frame>*> l_lower,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::l_lower<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::I<DataType, 4, Frame>*> l_upper,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::l_upper<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::I<DataType, 3, Frame>*> deriv_r,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::deriv_r<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::I<DataType, 4, Frame>*> deriv_H,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::deriv_H<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::ij<DataType, 4, Frame>*> kerr_schild_deriv_l,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::kerr_schild_deriv_l<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::ij<DataType, 4, Frame>*> deriv_l,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::deriv_l<DataType, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> lapse_squared,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::lapse_squared<DataType> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> lapse,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::Lapse<DataType> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<Scalar<DataType>*> deriv_lapse_multiplier,\n        const gsl::not_null<CachedBuffer*> cache,\n        internal_tags::deriv_lapse_multiplier<DataType> /*meta*/) const;\n\n    void operator()(const gsl::not_null<Scalar<DataType>*> shift_multiplier,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    internal_tags::shift_multiplier<DataType> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::I<DataType, 3, Frame>*> shift,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    gr::Tags::Shift<DataType, 3, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::iJ<DataType, 3, Frame>*> deriv_shift,\n        const gsl::not_null<CachedBuffer*> cache,\n        DerivShift<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> spatial_metric,\n        const gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::SpatialMetric<DataType, 3, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::II<DataType, 3, Frame>*>\n            inverse_spatial_metric,\n        const gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::InverseSpatialMetric<DataType, 3, Frame> /*meta*/) const;\n\n    void operator()(const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*>\n                        deriv_spatial_metric,\n                    const gsl::not_null<CachedBuffer*> cache,\n                    DerivSpatialMetric<DataType, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> dt_spatial_metric,\n        const gsl::not_null<CachedBuffer*> cache,\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> extrinsic_curvature,\n        const gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::ExtrinsicCurvature<DataType, 3, Frame> /*meta*/) const;\n\n    void operator()(\n        const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*>\n            christoffel_first_kind,\n        const gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame> /*meta*/)\n        const;\n\n    void operator()(\n        const gsl::not_null<tnsr::Ijj<DataType, 3, Frame>*>\n            christoffel_second_kind,\n        const gsl::not_null<CachedBuffer*> cache,\n        gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame> /*meta*/)\n        const;\n\n   private:\n    const SphericalKerrSchild& solution_;\n    const tnsr::I<DataType, 3, Frame>& x_;\n    // Here null_vector_0 is simply -1, but if you have a boosted solution,\n    // then null_vector_0 can be something different, so we leave it coded\n    // in instead of eliminating it.\n    static constexpr double null_vector_0_ = -1.0;\n  };\n\n  template <typename DataType, typename Frame = ::Frame::Inertial>\n  class IntermediateVars : public CachedBuffer<DataType, Frame> {\n   public:\n    using CachedBuffer = SphericalKerrSchild::CachedBuffer<DataType, Frame>;\n    using CachedBuffer::CachedBuffer;\n    using CachedBuffer::get_var;\n\n    tnsr::i<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        DerivLapse<DataType, Frame> /*meta*/);\n\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/);\n\n    tnsr::I<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame>> /*meta*/);\n\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/);\n\n    tnsr::i<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::DerivDetSpatialMetric<DataType, 3, Frame> /*meta*/);\n\n    Scalar<DataType> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/);\n\n    tnsr::I<DataType, 3, Frame> get_var(\n        const IntermediateComputer<DataType, Frame>& computer,\n        gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3,\n                                                    Frame> /*meta*/);\n\n   private:\n    // Here null_vector_0 is simply -1, but if you have a boosted solution,\n    // then null_vector_0 can be something different, so we leave it coded\n    // in instead of eliminating it.\n    static constexpr double null_vector_0_ = -1.0;\n  };\n\n private:\n  double mass_{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, volume_dim> dimensionless_spin_ =\n      make_array<volume_dim>(std::numeric_limits<double>::signaling_NaN());\n  std::array<double, volume_dim> center_ =\n      make_array<volume_dim>(std::numeric_limits<double>::signaling_NaN());\n};\n\nbool operator==(const SphericalKerrSchild& lhs, const SphericalKerrSchild& rhs);\n\nbool operator!=(const SphericalKerrSchild& lhs, const SphericalKerrSchild& rhs);\n\n}  // namespace Solutions\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TeukolskyWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TeukolskyWave.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/FrameTransform.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace {\n\n// The spherical-coordinate implementation is not valid near the origin.\n// Evaluations at or below this radius from the wave center trigger an error.\nconstexpr double origin_exclusion_radius = 0.1;\n\n// Points within this distance of the z axis (cylindrical_radius == 0) are\n// treated as being exactly on the axis. In this case, the value on the\n// axis is found by averaging the values at two small offsets from the\n// axis in orthogonal directions.\nconstexpr double axis_coordinate_tolerance = 1.0e-14;\n\n// When evaluating the axis limit (cylindrical_radius == 0), approach the\n// axis from a transverse offset of this size from two orthogonal directions\n// and then average the result to get the value on the limit.\nconstexpr double axis_limit_offset = 1.0e-8;\n\ntemplate <typename DataType>\nstd::pair<tnsr::ii<DataType, 3, Frame::Inertial>,\n          tnsr::ii<DataType, 3, Frame::Inertial>>\nperturbation_and_dt_perturbation(const DataType& centered_x,\n                                 const DataType& centered_y,\n                                 const DataType& centered_z, const double t,\n                                 const double amplitude, const int mode,\n                                 const bool even_parity, const bool ingoing,\n                                 const double radius, const double width) {\n  const DataType radius_from_center =\n      sqrt(square(centered_x) + square(centered_y) + square(centered_z));\n  const DataType cylindrical_radius =\n      sqrt(square(centered_x) + square(centered_y));\n\n  const DataType axis_mask =\n      step_function(cylindrical_radius - axis_coordinate_tolerance);\n  const DataType safe_cylindrical_radius =\n      cylindrical_radius + (1.0 - axis_mask);\n\n  const DataType cos_theta = centered_z / radius_from_center;\n  const DataType sin_theta = cylindrical_radius / radius_from_center;\n  const DataType cos_phi =\n      axis_mask * centered_x / safe_cylindrical_radius + (1.0 - axis_mask);\n  const DataType sin_phi = axis_mask * centered_y / safe_cylindrical_radius;\n  const DataType sin_2phi = 2.0 * sin_phi * cos_phi;\n  const DataType cos_2phi = square(cos_phi) - square(sin_phi);\n\n  const DataType y_profile = radius_from_center - radius + (ingoing ? t : -t);\n  const double minus_two_over_width_squared = -2.0 / square(width);\n  const DataType profile = amplitude * exp(-square(y_profile) / square(width));\n  const DataType profile_1 = minus_two_over_width_squared * y_profile * profile;\n  const DataType profile_2 =\n      minus_two_over_width_squared * (profile + y_profile * profile_1);\n  const DataType profile_3 =\n      minus_two_over_width_squared * (2.0 * profile_1 + y_profile * profile_2);\n  const DataType profile_4 =\n      minus_two_over_width_squared * (3.0 * profile_2 + y_profile * profile_3);\n  const DataType profile_5 =\n      minus_two_over_width_squared * (4.0 * profile_3 + y_profile * profile_4);\n\n  auto spherical_metric =\n      make_with_value<tnsr::ii<DataType, 3, Frame::NoFrame>>(centered_x, 0.0);\n  auto dt_spherical_metric =\n      make_with_value<tnsr::ii<DataType, 3, Frame::NoFrame>>(centered_x, 0.0);\n\n  if (even_parity) {\n    auto angular_rr = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_rtheta = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_rphi = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_thetaphi = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_theta_theta_1 = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_theta_theta_2 = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_phi_phi_1 = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_phi_phi_2 = make_with_value<DataType>(centered_x, 0.0);\n    switch (mode) {\n      case -2:\n        angular_rr = square(sin_theta) * sin_2phi;\n        angular_rtheta = sin_theta * cos_theta * sin_2phi;\n        angular_rphi = sin_theta * cos_2phi;\n        angular_theta_theta_1 = (1.0 + square(cos_theta)) * sin_2phi;\n        angular_theta_theta_2 = -sin_2phi;\n        angular_thetaphi = -cos_theta * cos_2phi;\n        angular_phi_phi_1 = -angular_theta_theta_1;\n        angular_phi_phi_2 = square(cos_theta) * sin_2phi;\n        break;\n      case -1:\n        angular_rr = 2.0 * sin_theta * cos_theta * sin_phi;\n        angular_rtheta = (square(cos_theta) - square(sin_theta)) * sin_phi;\n        angular_rphi = cos_theta * cos_phi;\n        angular_theta_theta_1 = -2.0 * sin_theta * cos_theta * sin_phi;\n        angular_thetaphi = sin_theta * cos_phi;\n        angular_phi_phi_1 = -angular_theta_theta_1;\n        angular_phi_phi_2 = -2.0 * sin_theta * cos_theta * sin_phi;\n        break;\n      case 0:\n        angular_rr = 2.0 - 3.0 * square(sin_theta);\n        angular_rtheta = -3.0 * sin_theta * cos_theta;\n        angular_theta_theta_1 = 3.0 * square(sin_theta);\n        angular_theta_theta_2 = -1.0;\n        angular_phi_phi_1 = -angular_theta_theta_1;\n        angular_phi_phi_2 = 3.0 * square(sin_theta) - 1.0;\n        break;\n      case 1:\n        angular_rr = 2.0 * sin_theta * cos_theta * cos_phi;\n        angular_rtheta = (square(cos_theta) - square(sin_theta)) * cos_phi;\n        angular_rphi = -cos_theta * sin_phi;\n        angular_theta_theta_1 = -2.0 * sin_theta * cos_theta * cos_phi;\n        angular_thetaphi = -sin_theta * sin_phi;\n        angular_phi_phi_1 = -angular_theta_theta_1;\n        angular_phi_phi_2 = -2.0 * sin_theta * cos_theta * cos_phi;\n        break;\n      case 2:\n        angular_rr = square(sin_theta) * cos_2phi;\n        angular_rtheta = sin_theta * cos_theta * cos_2phi;\n        angular_rphi = -sin_theta * sin_2phi;\n        angular_theta_theta_1 = (1.0 + square(cos_theta)) * cos_2phi;\n        angular_theta_theta_2 = -cos_2phi;\n        angular_thetaphi = cos_theta * sin_2phi;\n        angular_phi_phi_1 = -angular_theta_theta_1;\n        angular_phi_phi_2 = square(cos_theta) * cos_2phi;\n        break;\n      default:\n        ERROR(\"Unsupported Teukolsky mode\");\n    }\n\n    const DataType radial_a =\n        3.0 *\n        (profile_2 + (-3.0 * profile_1 + 3.0 * profile / radius_from_center) /\n                         radius_from_center) /\n        cube(radius_from_center);\n    const DataType radial_b =\n        -(-profile_3 + (3.0 * profile_2 + (-6.0 * profile_1 +\n                                           6.0 * profile / radius_from_center) /\n                                              radius_from_center) /\n                           radius_from_center) /\n        square(radius_from_center);\n    const DataType radial_c =\n        0.25 *\n        (profile_4 + (-2.0 * profile_3 +\n                      (9.0 * profile_2 + (-21.0 * profile_1 +\n                                          21.0 * profile / radius_from_center) /\n                                             radius_from_center) /\n                          radius_from_center) /\n                         radius_from_center) /\n        radius_from_center;\n\n    get<0, 0>(spherical_metric) = radial_a * angular_rr;\n    get<0, 1>(spherical_metric) =\n        radius_from_center * radial_b * angular_rtheta;\n    get<0, 2>(spherical_metric) =\n        radius_from_center * radial_b * angular_rphi * sin_theta;\n    get<1, 1>(spherical_metric) =\n        square(radius_from_center) *\n        (radial_c * angular_theta_theta_1 + radial_a * angular_theta_theta_2);\n    get<1, 2>(spherical_metric) = square(radius_from_center) *\n                                  (radial_a - 2.0 * radial_c) *\n                                  angular_thetaphi * sin_theta;\n    get<2, 2>(spherical_metric) =\n        square(radius_from_center) *\n        (radial_c * angular_phi_phi_1 + radial_a * angular_phi_phi_2) *\n        square(sin_theta);\n\n    const double propagation_sign = ingoing ? 1.0 : -1.0;\n    const DataType dt_radial_a =\n        propagation_sign * 3.0 *\n        (profile_3 + (-3.0 * profile_2 + 3.0 * profile_1 / radius_from_center) /\n                         radius_from_center) /\n        cube(radius_from_center);\n    const DataType dt_radial_b =\n        -propagation_sign *\n        (-profile_4 +\n         (3.0 * profile_3 +\n          (-6.0 * profile_2 + 6.0 * profile_1 / radius_from_center) /\n              radius_from_center) /\n             radius_from_center) /\n        square(radius_from_center);\n    const DataType dt_radial_c =\n        propagation_sign * 0.25 *\n        (profile_5 +\n         (-2.0 * profile_4 +\n          (9.0 * profile_3 +\n           (-21.0 * profile_2 + 21.0 * profile_1 / radius_from_center) /\n               radius_from_center) /\n              radius_from_center) /\n             radius_from_center) /\n        radius_from_center;\n\n    get<0, 0>(dt_spherical_metric) = dt_radial_a * angular_rr;\n    get<0, 1>(dt_spherical_metric) =\n        radius_from_center * dt_radial_b * angular_rtheta;\n    get<0, 2>(dt_spherical_metric) =\n        radius_from_center * dt_radial_b * angular_rphi * sin_theta;\n    get<1, 1>(dt_spherical_metric) =\n        square(radius_from_center) * (dt_radial_c * angular_theta_theta_1 +\n                                      dt_radial_a * angular_theta_theta_2);\n    get<1, 2>(dt_spherical_metric) = square(radius_from_center) *\n                                     (dt_radial_a - 2.0 * dt_radial_c) *\n                                     angular_thetaphi * sin_theta;\n    get<2, 2>(dt_spherical_metric) =\n        square(radius_from_center) *\n        (dt_radial_c * angular_phi_phi_1 + dt_radial_a * angular_phi_phi_2) *\n        square(sin_theta);\n  } else {\n    auto angular_rtheta = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_rphi = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_theta_theta = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_thetaphi = make_with_value<DataType>(centered_x, 0.0);\n    auto angular_phi_phi = make_with_value<DataType>(centered_x, 0.0);\n    switch (mode) {\n      case -2:\n        angular_rtheta = 4.0 * sin_theta * sin_2phi;\n        angular_rphi = 4.0 * sin_theta * cos_theta * cos_2phi;\n        angular_theta_theta = -2.0 * cos_theta * sin_2phi;\n        angular_thetaphi = -(2.0 - square(sin_theta)) * cos_2phi;\n        angular_phi_phi = 2.0 * cos_theta * sin_2phi;\n        break;\n      case -1:\n        angular_rtheta = -2.0 * cos_theta * sin_phi;\n        angular_rphi = -2.0 * (square(cos_theta) - square(sin_theta)) * cos_phi;\n        angular_theta_theta = -sin_theta * sin_phi;\n        angular_thetaphi = -cos_theta * sin_theta * cos_phi;\n        angular_phi_phi = sin_theta * sin_phi;\n        break;\n      case 0:\n        angular_rphi = -4.0 * cos_theta * sin_theta;\n        angular_thetaphi = -square(sin_theta);\n        break;\n      case 1:\n        angular_rtheta = -2.0 * cos_theta * cos_phi;\n        angular_rphi = 2.0 * (square(cos_theta) - square(sin_theta)) * sin_phi;\n        angular_theta_theta = -sin_theta * cos_phi;\n        angular_thetaphi = cos_theta * sin_theta * sin_phi;\n        angular_phi_phi = sin_theta * cos_phi;\n        break;\n      case 2:\n        angular_rtheta = 4.0 * sin_theta * cos_2phi;\n        angular_rphi = -4.0 * sin_theta * cos_theta * sin_2phi;\n        angular_theta_theta = -2.0 * cos_theta * cos_2phi;\n        angular_thetaphi = (2.0 - square(sin_theta)) * sin_2phi;\n        angular_phi_phi = 2.0 * cos_theta * cos_2phi;\n        break;\n      default:\n        ERROR(\"Unsupported Teukolsky mode\");\n    }\n\n    const DataType radial_k =\n        (profile_2 + (-3.0 * profile_1 + 3.0 * profile / radius_from_center) /\n                         radius_from_center) /\n        square(radius_from_center);\n    const DataType radial_l =\n        (-profile_3 + (2.0 * profile_2 +\n                       (-3.0 * profile_1 + 3.0 * profile / radius_from_center) /\n                           radius_from_center) /\n                          radius_from_center) /\n        radius_from_center;\n\n    get<0, 1>(spherical_metric) =\n        radius_from_center * radial_k * angular_rtheta;\n    get<0, 2>(spherical_metric) =\n        radius_from_center * radial_k * angular_rphi * sin_theta;\n    get<1, 1>(spherical_metric) =\n        square(radius_from_center) * radial_l * angular_theta_theta;\n    get<1, 2>(spherical_metric) =\n        square(radius_from_center) * radial_l * angular_thetaphi * sin_theta;\n    get<2, 2>(spherical_metric) = square(radius_from_center) * radial_l *\n                                  angular_phi_phi * square(sin_theta);\n\n    const double propagation_sign = ingoing ? 1.0 : -1.0;\n    const DataType dt_radial_k =\n        propagation_sign *\n        (profile_3 + (-3.0 * profile_2 + 3.0 * profile_1 / radius_from_center) /\n                         radius_from_center) /\n        square(radius_from_center);\n    const DataType dt_radial_l =\n        propagation_sign *\n        (-profile_4 +\n         (2.0 * profile_3 +\n          (-3.0 * profile_2 + 3.0 * profile_1 / radius_from_center) /\n              radius_from_center) /\n             radius_from_center) /\n        radius_from_center;\n\n    get<0, 1>(dt_spherical_metric) =\n        radius_from_center * dt_radial_k * angular_rtheta;\n    get<0, 2>(dt_spherical_metric) =\n        radius_from_center * dt_radial_k * angular_rphi * sin_theta;\n    get<1, 1>(dt_spherical_metric) =\n        square(radius_from_center) * dt_radial_l * angular_theta_theta;\n    get<1, 2>(dt_spherical_metric) =\n        square(radius_from_center) * dt_radial_l * angular_thetaphi * sin_theta;\n    get<2, 2>(dt_spherical_metric) = square(radius_from_center) * dt_radial_l *\n                                     angular_phi_phi * square(sin_theta);\n  }\n\n  // Jacobian dx^{spherical}/dx^{Cartesian} used by to_different_frame.\n  Jacobian<DataType, 3, Frame::Inertial, Frame::NoFrame> jacobian =\n      make_with_value<Jacobian<DataType, 3, Frame::Inertial, Frame::NoFrame>>(\n          centered_x, 0.0);\n  get<0, 0>(jacobian) = sin_theta * cos_phi;\n  get<0, 1>(jacobian) = sin_theta * sin_phi;\n  get<0, 2>(jacobian) = cos_theta;\n  get<1, 0>(jacobian) = cos_theta * cos_phi / radius_from_center;\n  get<1, 1>(jacobian) = cos_theta * sin_phi / radius_from_center;\n  get<1, 2>(jacobian) = -sin_theta / radius_from_center;\n  const DataType sin_theta_for_phi = sin_theta + (1.0 - axis_mask);\n  get<2, 0>(jacobian) = -sin_phi / (radius_from_center * sin_theta_for_phi);\n  get<2, 1>(jacobian) = cos_phi / (radius_from_center * sin_theta_for_phi);\n\n  auto perturbation = transform::to_different_frame(spherical_metric, jacobian);\n  auto dt_perturbation =\n      transform::to_different_frame(dt_spherical_metric, jacobian);\n  return {std::move(perturbation), std::move(dt_perturbation)};\n}\n\ntemplate <typename DataType>\nstd::pair<tnsr::ii<DataType, 3, Frame::Inertial>,\n          tnsr::ii<DataType, 3, Frame::Inertial>>\nmetric_and_dt_spatial_metric(const DataType& x, const DataType& y,\n                             const DataType& z, const double t,\n                             const double amplitude, const int mode,\n                             const bool even_parity, const bool ingoing,\n                             const std::array<double, 3>& center,\n                             const double radius, const double width,\n                             const bool include_minkowski_background) {\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(x, 0.0);\n  auto dt_spatial_metric =\n      make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(x, 0.0);\n  if (include_minkowski_background) {\n    get<0, 0>(spatial_metric) = 1.0;\n    get<1, 1>(spatial_metric) = 1.0;\n    get<2, 2>(spatial_metric) = 1.0;\n  }\n\n  const DataType centered_x = x - center[0];\n  const DataType centered_y = y - center[1];\n  const DataType centered_z = z - center[2];\n  const DataType radius_from_center =\n      sqrt(square(centered_x) + square(centered_y) + square(centered_z));\n  const double minimum_radius_from_center = min(radius_from_center);\n  if (minimum_radius_from_center <= origin_exclusion_radius) {\n    ERROR(\n        \"TeukolskyWave cannot be evaluated at points with radius <= \"\n        << origin_exclusion_radius\n        << \" from its center because the implementation uses spherical \"\n           \"coordinates, which are singular at the origin. Minimum radius was \"\n        << minimum_radius_from_center << \".\");\n  }\n  const DataType cylindrical_radius =\n      sqrt(square(centered_x) + square(centered_y));\n  const DataType axis_mask =\n      step_function(cylindrical_radius - axis_coordinate_tolerance);\n  const DataType centered_x_axis_limit =\n      centered_x + (1.0 - axis_mask) * axis_limit_offset;\n  const DataType centered_y_axis_limit =\n      centered_y + (1.0 - axis_mask) * axis_limit_offset;\n\n  const auto perturbation_and_dt = perturbation_and_dt_perturbation(\n      centered_x, centered_y, centered_z, t, amplitude, mode, even_parity,\n      ingoing, radius, width);\n  // On the axis, evaluate the smooth Cartesian limit by approaching from two\n  // orthogonal transverse directions instead of fixing an arbitrary azimuth.\n  const auto axis_limit_perturbation_and_dt_x =\n      perturbation_and_dt_perturbation(centered_x_axis_limit, centered_y,\n                                       centered_z, t, amplitude, mode,\n                                       even_parity, ingoing, radius, width);\n  const auto axis_limit_perturbation_and_dt_y =\n      perturbation_and_dt_perturbation(centered_x, centered_y_axis_limit,\n                                       centered_z, t, amplitude, mode,\n                                       even_parity, ingoing, radius, width);\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      const DataType axis_limit_perturbation =\n          0.5 * (axis_limit_perturbation_and_dt_x.first.get(i, j) +\n                 axis_limit_perturbation_and_dt_y.first.get(i, j));\n      const DataType axis_limit_dt_perturbation =\n          0.5 * (axis_limit_perturbation_and_dt_x.second.get(i, j) +\n                 axis_limit_perturbation_and_dt_y.second.get(i, j));\n      spatial_metric.get(i, j) +=\n          axis_mask * perturbation_and_dt.first.get(i, j) +\n          (1.0 - axis_mask) * axis_limit_perturbation;\n      dt_spatial_metric.get(i, j) =\n          axis_mask * perturbation_and_dt.second.get(i, j) +\n          (1.0 - axis_mask) * axis_limit_dt_perturbation;\n    }\n  }\n\n  return {std::move(spatial_metric), std::move(dt_spatial_metric)};\n}\n\n}  // namespace\n\nnamespace gr::Solutions {\n\nTeukolskyWave::TeukolskyWave(CkMigrateMessage* /*msg*/) {}\n\nTeukolskyWave::TeukolskyWave(double amplitude, const int mode,\n                             std::string parity, std::string direction,\n                             std::array<double, 3> center, const double radius,\n                             const double width,\n                             const Options::Context& context)\n    : TeukolskyWave(amplitude, mode, std::move(parity), std::move(direction),\n                    center, radius, width, true, context) {}\n\nTeukolskyWave::TeukolskyWave(double amplitude, const int mode,\n                             std::string parity, std::string direction,\n                             std::array<double, 3> center, const double radius,\n                             const double width,\n                             const bool include_minkowski_background,\n                             const Options::Context& context)\n    : amplitude_(amplitude),\n      mode_(mode),\n      parity_(std::move(parity)),\n      direction_(std::move(direction)),\n      center_(center),\n      radius_(radius),\n      width_(width),\n      include_minkowski_background_(include_minkowski_background) {\n  if (mode_ < -2 or mode_ > 2) {\n    PARSE_ERROR(context, \"Mode must lie between -2 and 2, inclusive.\");\n  }\n  if (parity_ != \"even\" and parity_ != \"odd\") {\n    PARSE_ERROR(context, \"Parity must be either 'even' or 'odd'.\");\n  }\n  if (direction_ != \"outgoing\" and direction_ != \"ingoing\") {\n    PARSE_ERROR(context, \"Direction must be either 'outgoing' or 'ingoing'.\");\n  }\n  if (width_ <= 0.0) {\n    PARSE_ERROR(context, \"Width must be greater than 0.\");\n  }\n}\n\nvoid TeukolskyWave::pup(PUP::er& p) {\n  p | amplitude_;\n  p | mode_;\n  p | parity_;\n  p | direction_;\n  p | center_;\n  p | radius_;\n  p | width_;\n  p | include_minkowski_background_;\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>> {\n  return {Scalar<DataType>{\n      make_with_value<DataType>(x, include_minkowski_background_ ? 1.0 : 0.0)}};\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>> {\n  return {Scalar<DataType>{make_with_value<DataType>(x, 0.0)}};\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    tmpl::list<gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>>\n    /*meta*/) const\n    -> tuples::TaggedTuple<\n        gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>> {\n  return {\n      make_with_value<tnsr::I<DataType, volume_dim, Frame::Inertial>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    tmpl::list<\n        ::Tags::dt<gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>>>\n    /*meta*/) const\n    -> tuples::TaggedTuple<\n        ::Tags::dt<gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>>> {\n  return {\n      make_with_value<tnsr::I<DataType, volume_dim, Frame::Inertial>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double t,\n    tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim, Frame::Inertial>>\n    /*meta*/) const\n    -> tuples::TaggedTuple<\n        gr::Tags::SpatialMetric<DataType, volume_dim, Frame::Inertial>> {\n  return metric_and_dt_spatial_metric(get<0>(x), get<1>(x), get<2>(x), t,\n                                      amplitude_, mode_, parity_ == \"even\",\n                                      direction_ == \"ingoing\", center_, radius_,\n                                      width_, include_minkowski_background_)\n      .first;\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double t,\n    tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<\n        DataType, volume_dim, Frame::Inertial>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<\n        gr::Tags::SpatialMetric<DataType, volume_dim, Frame::Inertial>>> {\n  return metric_and_dt_spatial_metric(get<0>(x), get<1>(x), get<2>(x), t,\n                                      amplitude_, mode_, parity_ == \"even\",\n                                      direction_ == \"ingoing\", center_, radius_,\n                                      width_, include_minkowski_background_)\n      .second;\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double t,\n    tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>> {\n  if (not include_minkowski_background_) {\n    ERROR(\n        \"IncludeMinkowskiBackground must be true to compute \"\n        \"sqrt(det(gamma)).\");\n  }\n  const auto spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, volume_dim>>(variables(\n          x, t, tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>>{}));\n  const auto det_and_inverse = determinant_and_inverse(spatial_metric);\n  auto sqrt_det_spatial_metric = make_with_value<Scalar<DataType>>(x, 0.0);\n  get(sqrt_det_spatial_metric) = sqrt(get(det_and_inverse.first));\n  return sqrt_det_spatial_metric;\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double t,\n    tmpl::list<\n        gr::Tags::ExtrinsicCurvature<DataType, volume_dim, Frame::Inertial>>\n    /*meta*/) const\n    -> tuples::TaggedTuple<\n        gr::Tags::ExtrinsicCurvature<DataType, volume_dim, Frame::Inertial>> {\n  if (not include_minkowski_background_) {\n    ERROR(\n        \"IncludeMinkowskiBackground must be true to compute extrinsic \"\n        \"curvature.\");\n  }\n  auto extrinsic_curvature =\n      make_with_value<tnsr::ii<DataType, volume_dim, Frame::Inertial>>(x, 0.0);\n  const auto dt_spatial_metric = get<\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>>(variables(\n      x, t,\n      tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>>{}));\n  for (size_t i = 0; i < volume_dim; ++i) {\n    for (size_t j = i; j < volume_dim; ++j) {\n      extrinsic_curvature.get(i, j) = -0.5 * dt_spatial_metric.get(i, j);\n    }\n  }\n  return extrinsic_curvature;\n}\n\ntemplate <typename DataType>\nauto TeukolskyWave::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double t,\n    tmpl::list<gr::Tags::InverseSpatialMetric<DataType, volume_dim,\n                                              Frame::Inertial>> /*meta*/) const\n    -> tuples::TaggedTuple<\n        gr::Tags::InverseSpatialMetric<DataType, volume_dim, Frame::Inertial>> {\n  if (not include_minkowski_background_) {\n    ERROR(\n        \"IncludeMinkowskiBackground must be true to compute inverse spatial \"\n        \"metric.\");\n  }\n  const auto spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, volume_dim>>(variables(\n          x, t, tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>>{}));\n  return determinant_and_inverse(spatial_metric).second;\n}\n\nbool operator==(const TeukolskyWave& lhs, const TeukolskyWave& rhs) {\n  return lhs.amplitude() == rhs.amplitude() and lhs.mode() == rhs.mode() and\n         lhs.parity() == rhs.parity() and lhs.direction() == rhs.direction() and\n         lhs.center() == rhs.center() and lhs.radius() == rhs.radius() and\n         lhs.width() == rhs.width() and\n         lhs.include_minkowski_background() ==\n             rhs.include_minkowski_background();\n}\n\nbool operator!=(const TeukolskyWave& lhs, const TeukolskyWave& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template tuples::TaggedTuple<gr::Tags::Lapse<DTYPE(data)>>                   \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t, tmpl::list<gr::Tags::Lapse<DTYPE(data)>> /*meta*/) const;      \\\n  template tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>>       \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t, tmpl::list<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>> /*meta*/) \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<gr::Tags::Shift<                                \\\n      DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>>                \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t,                                                                \\\n      tmpl::list<gr::Tags::Shift<DTYPE(data), TeukolskyWave::volume_dim,       \\\n                                 Frame::Inertial>> /*meta*/) const;            \\\n  template tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<                     \\\n      DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>>>               \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t,                                                                \\\n      tmpl::list<::Tags::dt<gr::Tags::Shift<                                   \\\n          DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>>> /*meta*/) \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<gr::Tags::SpatialMetric<                        \\\n      DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>>                \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t,                                                                \\\n      tmpl::list<gr::Tags::SpatialMetric<                                      \\\n          DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>> /*meta*/)  \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<::Tags::dt<gr::Tags::SpatialMetric<             \\\n      DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>>>               \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t,                                                                \\\n      tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<                           \\\n          DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>>> /*meta*/) \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>>    \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t,                                                                \\\n      tmpl::list<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>> /*meta*/) const; \\\n  template tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<                   \\\n      DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>>                \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t,                                                                \\\n      tmpl::list<gr::Tags::ExtrinsicCurvature<                                 \\\n          DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>> /*meta*/)  \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<gr::Tags::InverseSpatialMetric<                 \\\n      DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>>                \\\n  gr::Solutions::TeukolskyWave::variables(                                     \\\n      const tnsr::I<DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>&  \\\n          x,                                                                   \\\n      double t,                                                                \\\n      tmpl::list<gr::Tags::InverseSpatialMetric<                               \\\n          DTYPE(data), TeukolskyWave::volume_dim, Frame::Inertial>> /*meta*/)  \\\n      const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TeukolskyWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gr::Solutions {\n\n/*!\n * \\brief A perturbative Teukolsky wave (optionally plus Minkowski).\n *\n * \\details This class provides a linearized \\f$l=2\\f$ Teukolsky wave in\n * Cartesian inertial coordinates. The implementation evaluates the spherical\n * formulas point-by-point and then transforms to Cartesian coordinates.\n *\n * This solution does not provide spatial derivatives of the metric, so it\n * does not support the full tag list of a typical GR analytic solution.\n *\n * See Eqs. (5) -- (10) of \\cite Teukolsky1982nz for the equations in\n * spherical coordinates of the perturbed metric implemented here. Those\n * equations contain a freely specifiable radial profile; this implementation\n * chooses a Gaussian in \\f$r - R_0 \\pm t\\f$, where \\f$R_0\\f$ is the\n * `Radius` parameter, with `Width` setting the pulse width.\n *\n * \\note The implementation evaluates spherical-coordinate expressions, so it\n * must not be used too close to the origin about `Center`, where those\n * coordinates are singular. Specifically, evaluating the solution at points\n * with \\f$r \\le 0.1\\f$, where \\f$r\\f$ is the distance from `Center`,\n * triggers an error. This cutoff is unrelated to the option `Radius`,\n * which sets the location of the Gaussian pulse.\n *\n * On the \\f$z\\f$-axis, the spherical coordinate \\f$\\phi\\f$ is not well\n * defined. The implementation here is based on an implementation in SpEC that\n * just assumed the solution is never evaluated on the axis. Here, the\n * z-axis singularity is handled explicitly: on the \\f$z\\f$-axis\n * (cylindrical radius \\f$\\rho = 0\\f$), the Cartesian result is obtained by\n * averaging the smooth limit approached from two orthogonal transverse\n * directions, so the solution is regular there.\n *\n * Optionally, the solution can include a flat Minkowski background as well as\n * the metric perturbation. Note that the tags for the inverse spatial metric,\n * square root of the spatial metric determinant, and extrinsic curvature\n * require the flat background to be included. The background can be excluded\n * when one wants only the perturbation itself (e.g., to add it on top of a\n * different background, such as the metric of a Kerr-Schild black hole).\n */\nclass TeukolskyWave : public MarkAsAnalyticSolution {\n public:\n  static constexpr size_t volume_dim = 3;\n\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help{\"Amplitude of the perturbation\"};\n  };\n\n  struct Mode {\n    using type = int;\n    static constexpr Options::String help{\n        \"Azimuthal mode m of the l=2 Teukolsky wave\"};\n    static type lower_bound() { return -2; }\n    static type upper_bound() { return 2; }\n  };\n\n  struct Parity {\n    using type = std::string;\n    static constexpr Options::String help{\n        \"Parity of the perturbation: 'even' or 'odd'\"};\n  };\n\n  struct Direction {\n    using type = std::string;\n    static constexpr Options::String help{\n        \"Propagation direction: 'outgoing' or 'ingoing'\"};\n  };\n\n  struct Center {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help{\n        \"Center of the Teukolsky wave in inertial coordinates\"};\n  };\n\n  struct Radius {\n    using type = double;\n    static constexpr Options::String help{\n        \"Radius of the center of the Gaussian pulse at t=0\"};\n  };\n\n  struct Width {\n    using type = double;\n    static constexpr Options::String help{\n        \"Width of the Gaussian pulse profile\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options =\n      tmpl::list<Amplitude, Mode, Parity, Direction, Center, Radius, Width>;\n  static constexpr Options::String help{\n      \"A perturbative Teukolsky wave in Cartesian inertial coordinates\"};\n\n  template <typename DataType>\n  using tags = tmpl::list<\n      gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,\n      gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>,\n      ::Tags::dt<gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>>,\n      gr::Tags::SpatialMetric<DataType, volume_dim, Frame::Inertial>,\n      ::Tags::dt<\n          gr::Tags::SpatialMetric<DataType, volume_dim, Frame::Inertial>>,\n      gr::Tags::SqrtDetSpatialMetric<DataType>,\n      gr::Tags::ExtrinsicCurvature<DataType, volume_dim, Frame::Inertial>,\n      gr::Tags::InverseSpatialMetric<DataType, volume_dim, Frame::Inertial>>;\n\n  TeukolskyWave(double amplitude, int mode, std::string parity,\n                std::string direction, std::array<double, 3> center,\n                double radius, double width,\n                const Options::Context& context = {});\n\n  TeukolskyWave(double amplitude, int mode, std::string parity,\n                std::string direction, std::array<double, 3> center,\n                double radius, double width, bool include_minkowski_background,\n                const Options::Context& context = {});\n\n  TeukolskyWave() = default;\n  TeukolskyWave(const TeukolskyWave& /*rhs*/) = default;\n  TeukolskyWave& operator=(const TeukolskyWave& /*rhs*/) = default;\n  TeukolskyWave(TeukolskyWave&& /*rhs*/) = default;\n  TeukolskyWave& operator=(TeukolskyWave&& /*rhs*/) = default;\n  ~TeukolskyWave() = default;\n\n  explicit TeukolskyWave(CkMigrateMessage* /*msg*/);\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    static_assert(tmpl2::flat_all_v<\n                      tmpl::list_contains_v<tags<DataType>, RequestedTags>...>,\n                  \"At least one of the requested tags is not supported.\");\n    return {\n        get<RequestedTags>(variables(x, t, tmpl::list<RequestedTags>{}))...};\n  }\n\n  template <typename RequestedTag, typename DataType>\n  tuples::TaggedTuple<RequestedTag> variable(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n      const double t) const {\n    return variables(x, t, tmpl::list<RequestedTag>{});\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  double amplitude() const { return amplitude_; }\n  int mode() const { return mode_; }\n  const std::string& parity() const { return parity_; }\n  const std::string& direction() const { return direction_; }\n  const std::array<double, 3>& center() const { return center_; }\n  double radius() const { return radius_; }\n  double width() const { return width_; }\n  bool include_minkowski_background() const {\n    return include_minkowski_background_;\n  }\n\n private:\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const\n      -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>>\n      /*meta*/) const\n      -> tuples::TaggedTuple<\n          gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t,\n                 tmpl::list<::Tags::dt<gr::Tags::Shift<\n                     DataType, volume_dim, Frame::Inertial>>> /*meta*/) const\n      -> tuples::TaggedTuple<\n          ::Tags::dt<gr::Tags::Shift<DataType, volume_dim, Frame::Inertial>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t,\n                 tmpl::list<gr::Tags::SpatialMetric<\n                     DataType, volume_dim, Frame::Inertial>> /*meta*/) const\n      -> tuples::TaggedTuple<\n          gr::Tags::SpatialMetric<DataType, volume_dim, Frame::Inertial>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<::Tags::dt<\n          gr::Tags::SpatialMetric<DataType, volume_dim, Frame::Inertial>>>\n      /*meta*/) const\n      -> tuples::TaggedTuple<::Tags::dt<\n          gr::Tags::SpatialMetric<DataType, volume_dim, Frame::Inertial>>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<\n          gr::Tags::ExtrinsicCurvature<DataType, volume_dim, Frame::Inertial>>\n      /*meta*/) const\n      -> tuples::TaggedTuple<\n          gr::Tags::ExtrinsicCurvature<DataType, volume_dim, Frame::Inertial>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t,\n                 tmpl::list<gr::Tags::InverseSpatialMetric<\n                     DataType, volume_dim, Frame::Inertial>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::InverseSpatialMetric<\n          DataType, volume_dim, Frame::Inertial>>;\n\n  double amplitude_{std::numeric_limits<double>::signaling_NaN()};\n  int mode_{0};\n  std::string parity_{};\n  std::string direction_{};\n  std::array<double, 3> center_{};\n  double radius_{std::numeric_limits<double>::signaling_NaN()};\n  double width_{std::numeric_limits<double>::signaling_NaN()};\n  bool include_minkowski_background_{false};\n};\n\nbool operator==(const TeukolskyWave& lhs, const TeukolskyWave& rhs);\nbool operator!=(const TeukolskyWave& lhs, const TeukolskyWave& rhs);\n\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.hpp\"\n\n#include <array>\n#include <boost/math/quadrature/tanh_sinh.hpp>\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/IndefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// All quantities are dimensionless, i.e. in unit of black hole mass M\n\nconstexpr size_t num_of_pts = 20;  // number of LGL pts per DG element\nconstexpr double lapse_threshold = 0.1;\n// the max_isotropic_r works fine for larger value; one just need to add\n// one or two more elements. Very close to the puncture (r<1.e-4)\n// the error is relatively large, and one needs to add more elements.\nconstexpr double max_isotropic_r = 5000;  // in unit of the black hole mass M\n\n// Element placement in isotropic coordinates for interpolation to user grid\n// It is assumed that element_lower_bounds and element_upper_bounds are sorted\n// in asending order, and element_lower_bounds[i] < element_upper_bounds[i]\nconstexpr std::array element_lower_bounds{\n    0., 1.e-4, 1.e-3, 1.e-2, 1.e-1, 0.5,  1.,   2.,    4.,\n    8., 16.,    32.,    64.,    128.,   256., 512., 1024., 2048.};\nconstexpr size_t num_of_elements = element_lower_bounds.size();\nconstexpr auto element_upper_bounds = [] {\n  std::array<double, num_of_elements> upper_bounds{};\n  for (size_t i = 0; i + 1 < num_of_elements; ++i) {\n    upper_bounds.at(i) = element_lower_bounds.at(i + 1);\n  }\n  upper_bounds.at(num_of_elements - 1) = max_isotropic_r;\n  return upper_bounds;\n}();\n\nconst Mesh<1> source_mesh{num_of_pts, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\nusing Affine = domain::CoordinateMaps::Affine;\n\n// Get the critical lapse from eq. (42)\n// for n=2 critical lapse ~0.16\ndouble get_crit_lapse(const double n) {\n  return sqrt((sqrt(4. + 9. * n * n) - 3. * n) /\n              (sqrt(4. + 9. * n * n) + 3. * n));\n}\n\n// Get the critical Schwarzschild radius R_c from eq. (41)\ndouble get_crit_schwarzschild_r(const double n) {\n  return ((3. * n * n + sqrt(4. * pow<2>(n) + 9. * pow<4>(n))) / (4. * n * n));\n}\n\n// Get the value of C(n)^2 in eq. (43); note that eq. (43) is missing a factor\n// of M^4\ndouble get_c_n_squared(const double n) {\n  return pow<3>(3. * n + sqrt(4. + 9. * n * n)) / (128. * pow<3>(n)) *\n         exp(-2. * get_crit_lapse(n) / n);\n}\n\n// Get the function value of the transedental equation eq. (39)\ndouble function_of_lapse_and_schwarzschild_r(const double lapse,\n                                             const double schwarzschild_r,\n                                             const double n) {\n  ASSERT((lapse >= 0.) and (lapse < 1.),\n         \"invalid lapse range!\"\n         \"Required: [0, 1) but given \"\n             << lapse);\n  return (pow<4>(schwarzschild_r) - 2. * pow<3>(schwarzschild_r) +\n          get_c_n_squared(n) * exp(2. * lapse / n) -\n          lapse * lapse * pow<4>(schwarzschild_r));\n}\n\n// Get the Schwarzschild R corresponding to a lapse value by solving the\n// transedental equation eq. (39)\ndouble get_schwarzschild_r_from_lapse(const double n, const double lapse,\n                                      const double crit_lapse,\n                                      const double crit_schwarzschild_r) {\n  ASSERT((lapse >= 0.) and (lapse < 1.),\n         \"invalid lapse range!\"\n         \"Required: [0, 1) but given \"\n             << lapse);\n\n  // if one really needs solution beyond max_isotropic_r, the following needs\n  // to be modified.\n  const double max_schwarzschild_r =\n      std::max(max_isotropic_r, 4. / (1. - pow<2>(lapse)));\n  // the min_schwarzschild_r is NOT the location of the black\n  // hole throat. This is chosen purely for numerical root finding\n  const double min_schwarzschild_r = 0.;\n\n  return RootFinder::toms748(\n      [n, lapse](double schwarzschild_r) -> double {\n        return function_of_lapse_and_schwarzschild_r(lapse, schwarzschild_r, n);\n      },\n      (lapse < crit_lapse) ? min_schwarzschild_r : crit_schwarzschild_r,\n      (lapse < crit_lapse) ? crit_schwarzschild_r : max_schwarzschild_r,\n      1.e-15, 1.e-15);\n}\n\n// Get the derivative of the lapse with respect to the Schwarzschild R\n// Note that at the critical Schwarzschild R, this quantity is undefined\n// since both the numerator and the denominator are zero.\ntemplate <typename DataType>\nDataType get_d_lapse_d_schwarzschild_r_from_lapse(\n    const double n, const DataType lapse, const DataType schwarzschild_r) {\n  return n *\n         (2. * schwarzschild_r - 3. - 2. * schwarzschild_r * pow<2>(lapse)) /\n         (schwarzschild_r *\n          (schwarzschild_r - 2. + n * schwarzschild_r * lapse -\n           schwarzschild_r * pow<2>(lapse)));\n}\n\n// Get the first integral in eq. (56) with upper bound lapse\ndouble first_integral_above_threshold(const double n,\n                                      const double integral_upper_bound,\n                                      const double crit_lapse,\n                                      const double crit_schwarzschild_r) {\n  ASSERT((integral_upper_bound >= lapse_threshold) and\n             (integral_upper_bound <= 1.),\n         \"invalid upper integration bound in eq. (56).\"\n         \"Required: lapse_threshold <= integral_upper_bound\"\n         \" <= 1. but given integral_upper_bound = \"\n             << integral_upper_bound\n             << \" and lapse_threshold = \" << lapse_threshold);\n  const auto integrand = [n, crit_lapse,\n                          crit_schwarzschild_r](const double lapse) {\n    const double schwarzschild_r = get_schwarzschild_r_from_lapse(\n        n, lapse, crit_lapse, crit_schwarzschild_r);\n    return log(schwarzschild_r) / pow<2>(lapse);\n  };\n\n  boost::math::quadrature::tanh_sinh<double> de_integrator{};\n  return de_integrator.integrate(integrand, lapse_threshold,\n                                 integral_upper_bound);\n}\n\n// Get C_0 from eq. (55)\ndouble get_c_0(const double n, const double crit_lapse,\n               const double crit_schwarzschild_r) {\n  return first_integral_above_threshold(n, 1., crit_lapse,\n                                        crit_schwarzschild_r);\n}\n\n// Get the first integral in eq. (54) with lower bound lapse\n// note that this includes the minus sign already\ndouble first_integral_below_threshold(const double n,\n                                      const double integral_lower_bound,\n                                      const double crit_lapse,\n                                      const double crit_schwarzschild_r) {\n  ASSERT(\n      (integral_lower_bound < lapse_threshold) and (integral_lower_bound > 0.),\n      \"invalid lower integration bound in eq. (56).\"\n      \"Required: 0. <= integral_lower_bound\"\n      \" <= lapse_threshold but given integral_lower_bound = \"\n          << integral_lower_bound\n          << \" and lapse_threshold = \" << lapse_threshold);\n  const auto integrand = [n, crit_lapse,\n                          crit_schwarzschild_r](const double lapse) {\n    const double schwarzschild_r = get_schwarzschild_r_from_lapse(\n        n, lapse, crit_lapse, crit_schwarzschild_r);\n    const double d_lapse_d_schwarzschild_r =\n        get_d_lapse_d_schwarzschild_r_from_lapse(n, lapse, schwarzschild_r);\n    return (-1.) / (d_lapse_d_schwarzschild_r * lapse * schwarzschild_r);\n  };\n\n  boost::math::quadrature::tanh_sinh<double> de_integrator{};\n  return de_integrator.integrate(integrand, integral_lower_bound,\n                                 lapse_threshold);\n}\n\n// Get the isotropic r if the corresponding lapse is above\n// lapse threshold\ndouble get_isotropic_r_from_lapse(const double n, const double lapse,\n                                  const double crit_lapse,\n                                  const double crit_schwarzschild_r,\n                                  const double c_0) {\n  ASSERT((lapse >= 0.) and (lapse < 1.),\n         \"invalid lapse range!\"\n         \"Required: [0, 1) but given \"\n             << lapse);\n  if (lapse == 0.) {\n    return 0.;\n  } else if (lapse < lapse_threshold) {\n    return pow(get_schwarzschild_r_from_lapse(n, lapse_threshold, crit_lapse,\n                                              crit_schwarzschild_r),\n               1. / lapse_threshold) *\n           exp(first_integral_below_threshold(n, lapse, crit_lapse,\n                                              crit_schwarzschild_r) -\n               c_0);\n  } else {\n    return pow(get_schwarzschild_r_from_lapse(n, lapse, crit_lapse,\n                                              crit_schwarzschild_r),\n               1. / lapse) *\n           exp(first_integral_above_threshold(n, lapse, crit_lapse,\n                                              crit_schwarzschild_r) -\n               c_0);\n  }\n}\n\n// Get the lapse corresponding to a target isotropic_r by solving\n// r as a function of lapse\ndouble get_lapse_from_isotropic_r(const double n,\n                                  const double target_isotropic_r,\n                                  const double crit_lapse,\n                                  const double crit_schwarzschild_r,\n                                  const double c_0) {\n  ASSERT(target_isotropic_r >= 0.,\n         \"isotropic r must be \"\n         \"non-negative but given target_isotropic_r = \"\n             << target_isotropic_r);\n  ASSERT(target_isotropic_r <= max_isotropic_r,\n         \"we do not support\"\n         \" trumpet initial data with isotropic r greater than \"\n             << max_isotropic_r\n             << \"but given target_isotropic_r = \" << target_isotropic_r);\n\n  if (target_isotropic_r == 0.) {\n    return 0.;\n  } else {\n    // define the upper bound of lapse for root finding\n    const double max_lapse = sqrt(1. - 1. / max_isotropic_r);\n    const auto isotropic_r_minus_target = [&](const double lapse) -> double {\n      return (get_isotropic_r_from_lapse(n, lapse, crit_lapse,\n                                         crit_schwarzschild_r, c_0) -\n              target_isotropic_r);\n    };\n    return RootFinder::toms748(isotropic_r_minus_target, 0., max_lapse, 1.e-15,\n                               1.e-15);\n  }\n}\n\n// Set a DG grid of isotropic_r as a source grid\n// for interpolation to user-specified grid\ntnsr::I<DataVector, 1, Frame::Inertial> set_source_grid() {\n  const DataVector used_for_size(num_of_elements * num_of_pts,\n                                 std::numeric_limits<double>::signaling_NaN());\n  auto source_grid = make_with_value<tnsr::I<DataVector, 1, Frame::Inertial>>(\n      used_for_size, 0.);\n  const auto logical_coords = logical_coordinates(source_mesh);\n\n  for (size_t i = 0; i < num_of_elements; ++i) {\n    const auto coord_map =\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n            Affine{-1., 1., element_lower_bounds.at(i),\n                   element_upper_bounds.at(i)});\n    const auto inertial_coords = coord_map(logical_coords);\n\n    for (size_t j = 0; j < num_of_pts; ++j) {\n      (get<0>(source_grid))[j + i * num_of_pts] = (get<0>(inertial_coords))[j];\n    }\n  }\n  return source_grid;\n}\n\n// Get the lapse corresponding to a source grid of isotropic_r for\n// interpolation to user-specified isotropic_r\nDataVector get_lapse_on_grid(\n    const double n, const tnsr::I<DataVector, 1, Frame::Inertial>& grid,\n    const double crit_lapse, const double crit_schwarzschild_r,\n    const double c_0) {\n  DataVector lapse_on_grid = get<0>(grid);\n  for (size_t i = 0; i < get<0>(grid).size(); ++i) {\n    lapse_on_grid[i] = get_lapse_from_isotropic_r(\n        n, (get<0>(grid))[i], crit_lapse, crit_schwarzschild_r, c_0);\n    ASSERT(abs(lapse_on_grid[i] - crit_lapse) > 1.e-6,\n           \"lapse on a source grid point is too close to the critical \"\n           \"point where the\"\n           \"radial derivative of lapse cannot be calculated directly.\"\n           \"The violating lapse has value \"\n               << lapse_on_grid[i]\n               << \" which is too close to the critical lapse \" << crit_lapse);\n  }\n  return lapse_on_grid;\n}\n\n// Get the Schwarzschild R corresponding to a source gird of\n// isotropic_r for interpolation to user-specified isotropic_r\nDataVector get_schwarzschild_r_on_grid(const double n,\n                                       const DataVector& lapse_on_grid,\n                                       const double crit_lapse,\n                                       const double crit_schwarzschild_r) {\n  DataVector schwarzschild_r_on_grid = lapse_on_grid;\n  for (size_t i = 0; i < lapse_on_grid.size(); ++i) {\n    schwarzschild_r_on_grid[i] = get_schwarzschild_r_from_lapse(\n        n, lapse_on_grid[i], crit_lapse, crit_schwarzschild_r);\n    ASSERT(abs(schwarzschild_r_on_grid[i] - crit_schwarzschild_r) > 1.e-6,\n           \"Schwarzschild R on a source grid point is too close to the \"\n           \"critical point where the\"\n           \"radial derivative of lapse cannot be calculated directly.\"\n           \"The violating Schwarzschild R has value \"\n               << schwarzschild_r_on_grid[i]\n               << \" which is too close to the critical Schwarzschild R \"\n               << crit_schwarzschild_r);\n  }\n  return schwarzschild_r_on_grid;\n}\n\n// Return the first indices in user-defined grid of isotropic r\n// where isotropic r > element_lower_bounds[i] for i=0, ..., num_of_elements.\n// We assume the user_grid is sorted from small to large.\nstd::array<size_t, num_of_elements> get_element_partition_index(\n    const tnsr::I<DataVector, 1, Frame::Inertial>& user_grid) {\n  std::array<size_t, num_of_elements> lower_bound_indices{};\n  for (size_t i = 0; i < num_of_elements; ++i) {\n    const auto iter =\n        std::lower_bound(get<0>(user_grid).begin(), get<0>(user_grid).end(),\n                         element_lower_bounds.at(i));\n    lower_bound_indices.at(i) =\n        static_cast<size_t>(std::distance(get<0>(user_grid).begin(), iter));\n  }\n  return lower_bound_indices;\n}\n\n// Interpolate a tensor on source_grid to user_grid\ntemplate <size_t num_of_data_vectors>\nstd::array<DataVector, num_of_data_vectors> interpolate_to_user_grid(\n    const std::array<DataVector, num_of_data_vectors>& data_on_source_grid,\n    const tnsr::I<DataVector, 1, Frame::Inertial>& user_grid) {\n  std::array<DataVector, num_of_data_vectors> data_on_user_grid;\n\n  // sort the user grid to put user-grid pts into correct elements\n  // for interpolation. Memorize the original index locations\n  // to transform back after interpolation\n  std::vector<size_t> sort_index(get<0>(user_grid).size());\n  std::iota(sort_index.begin(), sort_index.end(), 0);\n  alg::sort(sort_index, [&user_grid](size_t i, size_t j) {\n    return get<0>(user_grid)[i] < get<0>(user_grid)[j];\n  });\n  auto sorted_user_grid = user_grid;\n  for (size_t i = 0; i < get<0>(user_grid).size(); ++i) {\n    get<0>(sorted_user_grid)[i] = get<0>(user_grid)[sort_index[i]];\n  }\n\n  const double requested_max_isotropic_r =\n      get<0>(sorted_user_grid)[get<0>(user_grid).size() - 1];\n  if (requested_max_isotropic_r > max_isotropic_r) {\n    ERROR(\"The max isotropic radius supported is \"\n          << max_isotropic_r << \"M but the max requested is \"\n          << requested_max_isotropic_r << \"M\");\n  }\n\n  const std::array<size_t, num_of_elements> lower_bound_indices =\n      get_element_partition_index(sorted_user_grid);\n\n  for (size_t k = 0; k < num_of_data_vectors; ++k) {\n    data_on_user_grid.at(k) = get<0>(sorted_user_grid);\n  }\n\n  for (size_t i = 0; i < num_of_elements; ++i) {\n    const tnsr::I<DataVector, 1, Frame::Inertial> user_subgrid_view;\n    const size_t num_of_user_pts_in_element =\n        (i == (num_of_elements - 1))\n            ? (get<0>(sorted_user_grid)).size() - lower_bound_indices.at(i)\n            : lower_bound_indices.at(i + 1) - lower_bound_indices.at(i);\n\n    if (num_of_user_pts_in_element > 0) {\n      make_const_view(make_not_null(&get<0>(user_subgrid_view)),\n                      get<0>(sorted_user_grid), lower_bound_indices.at(i),\n                      num_of_user_pts_in_element);\n      // transform user_subgrid_view to logical coordinates for interpolation\n      const auto coord_map =\n          domain::make_coordinate_map<Frame::Inertial, Frame::ElementLogical>(\n              Affine{element_lower_bounds.at(i), element_upper_bounds.at(i),\n                     -1., 1.});\n      const intrp::Irregular interpolant(source_mesh,\n                                         coord_map(user_subgrid_view));\n\n      for (size_t k = 0; k < num_of_data_vectors; ++k) {\n        const DataVector data_on_source_subgrid;\n        make_const_view(make_not_null(&data_on_source_subgrid),\n                        data_on_source_grid.at(k), i * num_of_pts, num_of_pts);\n        DataVector data_on_user_subgrid =\n            interpolant.interpolate(data_on_source_subgrid);\n\n        for (size_t j = 0; j < num_of_user_pts_in_element; ++j) {\n          (data_on_user_grid.at(k))[sort_index[lower_bound_indices.at(i) + j]] =\n              data_on_user_subgrid[j];\n        }\n      }\n    }\n  }\n\n  return data_on_user_grid;\n}\n\ntemplate <size_t num_of_data_vectors>\nstd::array<double, num_of_data_vectors> interpolate_to_user_grid(\n    const std::array<DataVector, num_of_data_vectors>& data_on_source_grid,\n    const tnsr::I<double, 1, Frame::Inertial>& user_grid) {\n  std::array<double, num_of_data_vectors> result{};\n  auto temp_user_grid =\n      make_with_value<tnsr::I<DataVector, 1, Frame::Inertial>>(\n          DataVector(1, std::numeric_limits<double>::signaling_NaN()),\n          std::numeric_limits<double>::signaling_NaN());\n  get<0>(temp_user_grid)[0] = get<0>(user_grid);\n  const auto data_on_user_grid =\n      interpolate_to_user_grid(data_on_source_grid, temp_user_grid);\n\n  for (size_t i = 0; i < num_of_data_vectors; ++i) {\n    result.at(i) = (data_on_user_grid.at(i))[0];\n  }\n  return result;\n}\n\n// Get the isotropic r on user grid\ntemplate <typename DataType>\nconstexpr tnsr::I<DataType, 1, Frame::Inertial> get_isotropic_r_on_grid(\n    const tnsr::I<DataType, gr::Solutions::TrumpetSchwarzschild::volume_dim,\n                  Frame::Inertial>& x,\n    const double mass) {\n  tnsr::I<DataType, 1, Frame::Inertial> isotropic_r_on_grid{\n      sqrt(pow<2>(get<0>(x)) + pow<2>(get<1>(x)) + pow<2>(get<2>(x))) / mass};\n  return isotropic_r_on_grid;\n}\n}  // namespace\n\nnamespace gr::Solutions {\nconst tnsr::I<DataVector, 1, Frame::Inertial>\n    TrumpetSchwarzschild::source_grid_ = set_source_grid();\n\nTrumpetSchwarzschild::TrumpetSchwarzschild(CkMigrateMessage* /*msg*/) {}\n\nTrumpetSchwarzschild::TrumpetSchwarzschild(const double mass, const double n,\n                                           const Options::Context& context)\n    : mass_(mass), n_(n) {\n  if (mass <= 0.) {\n    PARSE_ERROR(context,\n                \"Black hole mass must be positive, but given \" << mass_);\n  }\n  if (n < 0.) {\n    PARSE_ERROR(context, \"Parameter n must be non-negative, but given \" << n);\n  }\n\n  const double crit_lapse = get_crit_lapse(n);\n  const double crit_schwarzschild_r = get_crit_schwarzschild_r(n);\n  const double c_0 = get_c_0(n, crit_lapse, crit_schwarzschild_r);\n\n  data_on_source_grid_.at(0) =\n      get_lapse_on_grid(n, source_grid_, crit_lapse, crit_schwarzschild_r, c_0);\n  data_on_source_grid_.at(1) = get_schwarzschild_r_on_grid(\n      n, data_on_source_grid_.at(0), crit_lapse, crit_schwarzschild_r);\n}\n\nvoid TrumpetSchwarzschild::pup(PUP::er& p) {\n  p | mass_;\n  p | n_;\n  p | data_on_source_grid_;\n}\n\n// We first compute lapse, schwarzchid_r, d_lapse_d_schwarzschild_r\n// on a source isotropic grid before interpolating to the user-\n// specified isotropic grid corresponding to x\ntemplate <typename DataType>\nTrumpetSchwarzschild::IntermediateVars<DataType>::IntermediateVars(\n    const double mass, const double n,\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const std::array<DataVector, 2>& data_on_source_grid)\n    : one_over_mass(1. / mass), one_over_n(1. / n) {\n  auto target_grid = get_isotropic_r_on_grid(x, mass);\n  const auto data_on_user_grid =\n      interpolate_to_user_grid(data_on_source_grid, target_grid);\n\n  d_lapse_d_schwarzschild_r_on_user_grid =\n      get_d_lapse_d_schwarzschild_r_from_lapse(n, data_on_user_grid.at(0),\n                                               data_on_user_grid.at(1));\n  lapse_on_user_grid = std::move(data_on_user_grid.at(0));\n  one_over_schwarzschild_r_on_user_grid = 1. / data_on_user_grid.at(1);\n  schwarzschild_r_on_user_grid = std::move(data_on_user_grid.at(1));\n  one_over_isotropic_r_on_user_grid = 1. / get<0>(target_grid);\n  isotropic_r_on_user_grid = std::move(get<0>(target_grid));\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>> {\n  return {Scalar<DataType>{vars.lapse_on_user_grid}};\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>> {\n  return {make_with_value<Scalar<DataType>>(vars.lapse_on_user_grid, 0.)};\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<DerivLapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivLapse<DataType>> {\n  tnsr::i<DataType, volume_dim, Frame::Inertial> d_lapse;\n  for (size_t i = 0; i < volume_dim; ++i) {\n    // note that we divide by mass_ to get the correct dimension\n    d_lapse.get(i) = vars.d_lapse_d_schwarzschild_r_on_user_grid *\n                     vars.lapse_on_user_grid *\n                     vars.schwarzschild_r_on_user_grid * x.get(i) *\n                     pow<2>(vars.one_over_isotropic_r_on_user_grid) *\n                     pow<2>(vars.one_over_mass);\n  }\n  return d_lapse;\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::Shift<DataType, volume_dim>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::Shift<DataType, volume_dim>> {\n  tnsr::I<DataType, volume_dim, Frame::Inertial> shift;\n  for (size_t i = 0; i < volume_dim; ++i) {\n    shift.get(i) = x.get(i) * vars.one_over_mass * sqrt(get_c_n_squared(n_)) *\n                   exp(vars.lapse_on_user_grid * vars.one_over_n) *\n                   pow<3>(vars.one_over_schwarzschild_r_on_user_grid);\n  }\n  return shift;\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& /*vars*/,\n    tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> /*meta*/)\n    const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> {\n  return {\n      make_with_value<tnsr::I<DataType, volume_dim, Frame::Inertial>>(x, 0.)};\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<DerivShift<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivShift<DataType>> {\n  tnsr::iJ<DataType, volume_dim, Frame::Inertial> d_shift;\n  DataType sqrt_of_lapse_squared_minus_f =\n      sqrt(get_c_n_squared(n_)) *\n      exp(vars.lapse_on_user_grid * vars.one_over_n) *\n      pow<2>(vars.one_over_schwarzschild_r_on_user_grid);\n\n  for (size_t i = 0; i < volume_dim; ++i) {\n    for (size_t j = 0; j < volume_dim; ++j) {\n      DataType lapse_times_xi_xj_over_isotropic_r_squared =\n          vars.lapse_on_user_grid * x.get(i) * x.get(j) *\n          pow<2>(vars.one_over_mass) *\n          pow<2>(vars.one_over_isotropic_r_on_user_grid);\n\n      d_shift.get(i, j) =\n          lapse_times_xi_xj_over_isotropic_r_squared *\n          (1. / sqrt_of_lapse_squared_minus_f *\n               (vars.lapse_on_user_grid *\n                    vars.d_lapse_d_schwarzschild_r_on_user_grid -\n                1. * pow<2>(vars.one_over_schwarzschild_r_on_user_grid)) -\n           sqrt_of_lapse_squared_minus_f *\n               vars.one_over_schwarzschild_r_on_user_grid);\n\n      if (i == j) {\n        d_shift.get(i, j) += sqrt_of_lapse_squared_minus_f *\n                             vars.one_over_schwarzschild_r_on_user_grid;\n      }\n      // divide by mass to restore unit\n      d_shift.get(i, j) *= vars.one_over_mass;\n    }\n  }\n  return d_shift;\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, volume_dim>> {\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataType, volume_dim, Frame::Inertial>>(x, 0.);\n  for (size_t i = 0; i < volume_dim; ++i) {\n    spatial_metric.get(i, i) = pow<2>(vars.schwarzschild_r_on_user_grid *\n                                      vars.one_over_isotropic_r_on_user_grid);\n  }\n  return spatial_metric;\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& /*vars*/,\n    tmpl::list<\n        ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> /*meta*/)\n    const -> tuples::TaggedTuple<\n              ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> {\n  auto dt_spatial_metric =\n      make_with_value<tnsr::ii<DataType, volume_dim, Frame::Inertial>>(x, 0.);\n  return dt_spatial_metric;\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivSpatialMetric<DataType>> {\n  auto d_spatial_metric =\n      make_with_value<tnsr::ijj<DataType, volume_dim, Frame::Inertial>>(x, 0.);\n\n  for (size_t i = 0; i < volume_dim; ++i) {\n    for (size_t j = 0; j < volume_dim; ++j) {\n      d_spatial_metric.get(i, j, j) =\n          2. * pow<2>(vars.schwarzschild_r_on_user_grid) * x.get(i) *\n          pow<4>(vars.one_over_isotropic_r_on_user_grid) *\n          (vars.lapse_on_user_grid - 1.) * pow<2>(vars.one_over_mass);\n    }\n  }\n  return d_spatial_metric;\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n    const double /*t*/, const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>> {\n  return {Scalar<DataType>{pow<3>(vars.schwarzschild_r_on_user_grid *\n                                  vars.one_over_isotropic_r_on_user_grid)}};\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> /*meta*/)\n    const\n    -> tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> {\n  tnsr::ii<DataType, volume_dim, Frame::Inertial> extrinsic_curvature;\n  DataType sqrt_lapse_squared_minus_f =\n      sqrt(get_c_n_squared(n_)) *\n      exp(vars.lapse_on_user_grid * vars.one_over_n) *\n      pow<2>(vars.one_over_schwarzschild_r_on_user_grid);\n  DataType diagonal_common_factor =\n      vars.schwarzschild_r_on_user_grid * sqrt_lapse_squared_minus_f *\n      pow<2>(vars.one_over_isotropic_r_on_user_grid);\n  DataType off_diagonal_indep_term =\n      (pow<2>(vars.schwarzschild_r_on_user_grid) * vars.lapse_on_user_grid *\n           vars.d_lapse_d_schwarzschild_r_on_user_grid -\n       1.) *\n      pow<4>(vars.one_over_isotropic_r_on_user_grid) /\n      (sqrt_lapse_squared_minus_f);\n\n  for (size_t i = 0; i < volume_dim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      extrinsic_curvature.get(i, j) =\n          x.get(i) * x.get(j) * pow<2>(vars.one_over_mass) *\n              off_diagonal_indep_term +\n          diagonal_common_factor *\n              (((i == j) ? 1. : 0.) -\n               x.get(i) * x.get(j) * pow<2>(vars.one_over_mass) *\n                   pow<2>(vars.one_over_isotropic_r_on_user_grid));\n\n      // divide by mass to get the correct dimension\n      extrinsic_curvature.get(i, j) *= vars.one_over_mass;\n    }\n  }\n  return extrinsic_curvature;\n}\n\ntemplate <typename DataType>\nauto TrumpetSchwarzschild::variables(\n    const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, const double /*t*/,\n    const IntermediateVars<DataType>& vars,\n    tmpl::list<gr::Tags::InverseSpatialMetric<DataType, volume_dim>> /*meta*/)\n    const -> tuples::TaggedTuple<\n              gr::Tags::InverseSpatialMetric<DataType, volume_dim>> {\n  auto inverse_spatial_metric =\n      make_with_value<tnsr::II<DataType, volume_dim, Frame::Inertial>>(x, 0.);\n  for (size_t i = 0; i < volume_dim; ++i) {\n    inverse_spatial_metric.get(i, i) =\n        pow<2>(vars.isotropic_r_on_user_grid *\n               vars.one_over_schwarzschild_r_on_user_grid);\n  }\n  return inverse_spatial_metric;\n}\n\nbool operator==(const TrumpetSchwarzschild& lhs,\n                const TrumpetSchwarzschild& rhs) {\n  return lhs.mass() == rhs.mass() and lhs.n() == rhs.n();\n}\n\nbool operator!=(const TrumpetSchwarzschild& lhs,\n                const TrumpetSchwarzschild& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>::               \\\n      IntermediateVars(                                                        \\\n          const double mass, const double n,                                   \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x,           \\\n          const double /*t*/,                                                  \\\n          const std::array<DataVector, 2>& data_on_source_grid);               \\\n  template tuples::TaggedTuple<gr::Tags::Lapse<DTYPE(data)>>                   \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& /*x*/,           \\\n      const double /*t*/,                                                      \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<gr::Tags::Lapse<DTYPE(data)>> /*meta*/) const;                \\\n  template tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>>       \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<::Tags::dt<gr::Tags::Lapse<DTYPE(data)>>> /*meta*/) const;    \\\n  template tuples::TaggedTuple<::Tags::deriv<                                  \\\n      gr::Tags::Lapse<DTYPE(data)>, tmpl::size_t<DIM(data)>, Frame::Inertial>> \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::Lapse<DTYPE(data)>, tmpl::size_t<DIM(data)>, \\\n                        Frame::Inertial>> /*meta*/) const;                     \\\n  template tuples::TaggedTuple<gr::Tags::Shift<DTYPE(data), DIM(data)>>        \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<gr::Tags::Shift<DTYPE(data), DIM(data)>> /*meta*/) const;     \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::Shift<DTYPE(data), DIM(data)>>>                     \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          ::Tags::dt<gr::Tags::Shift<DTYPE(data), DIM(data)>>> /*meta*/)       \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::deriv<gr::Tags::Shift<DTYPE(data), DIM(data)>,                   \\\n                    tmpl::size_t<DIM(data)>, Frame::Inertial>>                 \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::Shift<DTYPE(data), DIM(data)>,               \\\n                        tmpl::size_t<DIM(data)>, Frame::Inertial>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>                         \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)    \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::dt<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>>             \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<::Tags::dt<                                                   \\\n          gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>>> /*meta*/) const;   \\\n  template tuples::TaggedTuple<                                                \\\n      ::Tags::deriv<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>,           \\\n                    tmpl::size_t<DIM(data)>, Frame::Inertial>>                 \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          ::Tags::deriv<gr::Tags::SpatialMetric<DTYPE(data), DIM(data)>,       \\\n                        tmpl::size_t<DIM(data)>, Frame::Inertial>> /*meta*/)   \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::InverseSpatialMetric<DTYPE(data), DIM(data)>>                  \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          gr::Tags::InverseSpatialMetric<DTYPE(data), DIM(data)>> /*meta*/)    \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      gr::Tags::ExtrinsicCurvature<DTYPE(data), DIM(data)>>                    \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double /*t*/,            \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<                                                              \\\n          gr::Tags::ExtrinsicCurvature<DTYPE(data), DIM(data)>> /*meta*/)      \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>>    \\\n  TrumpetSchwarzschild::variables(                                             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& /*x*/, const double /*t*/,        \\\n      const TrumpetSchwarzschild::IntermediateVars<DTYPE(data)>& vars,         \\\n      tmpl::list<gr::Tags::SqrtDetSpatialMetric<DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (3), (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef DIM\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <ostream>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpline.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\n/// \\endcond\n\nnamespace gr::Solutions {\n\n/*!\n * \\brief Trumpet Schwarzschild solution in isotropic coordinates\n *\n * \\details\n * This solution is a trumpet Schwarzschild black hole in the isotropic\n * coordinates. It is a time-independent puncture solution in 1+log slicing.\n * The solution cannot be written down analytically in Schwarzschild\n * coordinates or isotropic coordinates. Refer to \\cite Hannam2008\n * for equations and details.\n *\n * We first set up a source grid in isotropic radial coordinate r, on which\n * we compute the lapse and Schwarzschild radial coordinate R. The lapse\n * is computed by solving eq. (54)-(56) using the toms748 algorithm, where\n * the integrals in eq. (54)-(56) are evaluated by a tanh_sinh integrator.\n *\n * Eq. (54) is for \\f$\\alpha < \\alpha_s=0.1\\f$:\n * \\f{equation*}{\n * r(\\alpha) = R(\\alpha_s)^{(1/\\alpha_s)} \\exp \\left[ -\n * \\int_{\\alpha}^{\\alpha_s} \\frac{1}{\\bar{\\alpha} R(\\bar{\\alpha})}\n * \\frac{dR}{d\\alpha}(\\bar{\\alpha}) \\: d\\bar{\\alpha} - C_0 \\right],\n * \\f}\n * where eq. (55) gives\n * \\f{equation*}{\n * C_0 = \\int_{\\alpha_s}^{1} \\frac{\\ln R(\\alpha)}{\\alpha^2} d\\alpha.\n * \\f}\n * Eq. (56) is for \\f$\\alpha > \\alpha_s=0.1\\f$:\n * \\f{equation*}{\n * r(\\alpha) = R(\\alpha)^{(1/\\alpha)} \\exp \\left[\n * \\int_{\\alpha_s}^{\\alpha} \\frac{\\ln R(\\bar{\\alpha})}{\\bar{\\alpha}^2}\n * d\\bar{\\alpha} - C_0 \\right].\n * \\f}\n *\n * A standard Gauss quadratures will fail due to singular\n * behaviors of the integrands near the integration limits. The Schwarzschild R\n * is then computed by solving eq. (39) using again the toms748 algorithm.\n * \\f{equation*}{\n * \\alpha^2 = 1 - \\frac{2M}{R} + \\frac{C(n)^2 e^{2 \\alpha / n}\n * }{R^4}.\n * \\f}\n * Note that for a value of n near 2 (corresponding to standard 1+log slicing),\n * eq. (39) has two positive roots for a fixed lapse in [0., 1.).\n * See the following plot by Mathematica. We choose\n * \\image html trumpet_two_branches_illustration.png\n * the physical root, i.e. with the correct asymptotic behaviors: R diverges as\n * \\f$\\alpha\\f$ tends to 1, and R tends to the smaller solution as \\f$\\alpha\\f$\n * tends to 0.\n\n * The physical root is numerically selected by finding the\n * critical lapse and critical Schwarzschild R, below which we tell the toms748\n * solver to find a root \\f$R\\in\\f$ [min_schwarzschild_r, crit_schwarzschild_r]\n * or else we find a root\n * \\f$R\\in\\f$ [crit_schwarzschild_r, max_schwarzschild_r]. min_schwarzschild_r\n * is currently selected to be 0., and max_schwarzschild_r is at least as large\n * as max_isotropic_r (currently 5000M), the maximum coordinate radius we\n * support for this initial data. In case the solver is asked to find a\n * Schwarzschild R greater than the max_isotropic_r, we use double the\n * asymptotic solution from eq. (39), i.e. \\f$4/(1-\\alpha^2)\\f$, as solver\n * upper bound. This latter upper bound is necessary since the integrator\n * in eq. (55) needs lapse very close to 1 to converge.\n *\n * After acquiring the lapse and Schwarzschild R, we can assemble all the 3+1\n * quantities in the isotropic coordinates on the source grid.\n *\n * Since the user supplies grid points in the Cartesian version of the\n * isotropic coordinates, we compute a user grid in the isotropic radial\n * coordinate based on the Cartesian grid, compute the lapse and Schwarzschild\n * R on the source grid, interpolate to the user grid, and then assemble all\n * 3+1 quantities on the user grid and transform them back to the Cartesian\n * grid.\n *\n * To insulate our implementation from different mass parameters, we\n * nondimensionalize the above process using the black hole mass until\n * the final step of computing 3+1 quantities on the Cartesian grid,\n * where we restore the correct unit.\n *\n * The following are input file options that can be specified:\n *  - Mass\n *  - N (the parameter n in the slicing condition eq. (36))\n *\n * \\note\n * N=2. is strongly suggested, as this gives a stationary solution\n * in the standard 1+log slicing. The other values of N near 2.\n * should work but have not been tested thoroughly. N outside of\n * [2., 3.] has not been tested at all.\n *\n * Some quantities very close to the puncture (<1.e-4 in isotropic radius)\n * may have larger truncation errors. This is expected since some quantities\n * such as the determiant of the spatial metric diverges at the puncture.\n */\nclass TrumpetSchwarzschild : public MarkAsAnalyticSolution,\n                             public AnalyticSolution<3_st> {\n private:\n  template <typename DataType>\n  struct IntermediateVars;\n\n public:\n  static constexpr size_t volume_dim = 3;\n\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Mass of the Schwarzschild black hole\"};\n    static type lower_bound() { return 0.1; };\n  };\n\n  // currently we have only tested around N=2; for other N eq. (39) may need to\n  // be solved differently\n  struct N {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Parameter of the trumpet solution family. N=2. gives\"\n        \"a stationary solution in standard 1+log slicing.\"\n        \"An N value outside [2., 3.] has not been tested.\"};\n    static type lower_bound() { return 0.; };\n  };\n\n  using options = tmpl::list<Mass, N>;\n  static constexpr Options::String help{\n      \"Schwarzschild solution in trumpet isotropic coordinates\"};\n\n  TrumpetSchwarzschild(double mass, double n,\n                       const Options::Context& context = {});\n\n  TrumpetSchwarzschild() = default;\n  TrumpetSchwarzschild(const TrumpetSchwarzschild& /*rhs*/) = default;\n  TrumpetSchwarzschild& operator=(const TrumpetSchwarzschild& /*rhs*/) =\n      default;\n  TrumpetSchwarzschild(TrumpetSchwarzschild&& /*rhs*/) = default;\n  TrumpetSchwarzschild& operator=(TrumpetSchwarzschild&& /*rhs*/) = default;\n  ~TrumpetSchwarzschild() = default;\n\n  explicit TrumpetSchwarzschild(CkMigrateMessage* /*msg*/);\n\n  template <typename DataType>\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataType>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  template <typename DataType>\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, volume_dim>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  template <typename DataType>\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, volume_dim>,\n                    tmpl::size_t<volume_dim>, Frame::Inertial>;\n\n  template <typename DataType>\n  using tags = tmpl::list<\n      gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,\n      DerivLapse<DataType>, gr::Tags::Shift<DataType, volume_dim>,\n      ::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>, DerivShift<DataType>,\n      gr::Tags::SpatialMetric<DataType, volume_dim>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>,\n      DerivSpatialMetric<DataType>, gr::Tags::SqrtDetSpatialMetric<DataType>,\n      gr::Tags::ExtrinsicCurvature<DataType, volume_dim>,\n      gr::Tags::InverseSpatialMetric<DataType, volume_dim>>;\n\n  // The user input Cartesian \"x\" is in isotropic coordinates\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      tmpl::list<Tags...> /*meta*/) const {\n    const auto& vars =\n        IntermediateVars<DataType>{mass_, n_, x, t, data_on_source_grid_};\n    return {get<Tags>(variables(x, t, vars, tmpl::list<Tags>{}))...};\n  }\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"Unrecognized tag requested.  See the function parameters \"\n                  \"for the tag.\");\n    return {get<Tags>(variables(x, t, vars, tmpl::list<Tags>{}))...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  SPECTRE_ALWAYS_INLINE double mass() const { return mass_; }\n  SPECTRE_ALWAYS_INLINE double n() const { return n_; }\n\n private:\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/)\n      const -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivLapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivLapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::Shift<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::Shift<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>> /*meta*/)\n      const\n      -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, volume_dim>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                 double t, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivShift<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivShift<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::SpatialMetric<DataType, volume_dim>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<\n          ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          ::Tags::dt<gr::Tags::SpatialMetric<DataType, volume_dim>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n                 double /*t*/, const IntermediateVars<DataType>& vars,\n                 tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, volume_dim, Frame::Inertial>& /*x*/,\n                 double /*t*/, const IntermediateVars<DataType>& vars,\n                 tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          gr::Tags::ExtrinsicCurvature<DataType, volume_dim>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, volume_dim, Frame::Inertial>& x, double t,\n      const IntermediateVars<DataType>& vars,\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataType, volume_dim>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          gr::Tags::InverseSpatialMetric<DataType, volume_dim>>;\n\n  template <typename DataType>\n  struct IntermediateVars {\n    IntermediateVars(double mass, double n,\n                     const tnsr::I<DataType, volume_dim, Frame::Inertial>& x,\n                     double /*t*/,\n                     const std::array<DataVector, 2>& data_on_source_grid);\n    // the lapse, Schwarzschild R, and isotropic r on isotropic grid points\n    DataType lapse_on_user_grid{};\n    DataType schwarzschild_r_on_user_grid{};\n    DataType d_lapse_d_schwarzschild_r_on_user_grid{};\n    DataType isotropic_r_on_user_grid{};\n\n    // cached value to avoid division\n    DataType one_over_schwarzschild_r_on_user_grid{};\n    DataType one_over_isotropic_r_on_user_grid{};\n    double one_over_mass;\n    double one_over_n;\n  };\n\n  double mass_{std::numeric_limits<double>::signaling_NaN()};\n  double n_{std::numeric_limits<double>::signaling_NaN()};\n\n  const static tnsr::I<DataVector, 1, Frame::Inertial> source_grid_;\n  std::array<DataVector, 2> data_on_source_grid_;\n};\n\nbool operator==(const TrumpetSchwarzschild& lhs,\n                const TrumpetSchwarzschild& rhs);\nbool operator!=(const TrumpetSchwarzschild& lhs,\n                const TrumpetSchwarzschild& rhs);\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.tpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nGENERATE_INSTANTIATIONS(\n    WRAPPED_GR_INSTANTIATE,\n    (gr::Solutions::HarmonicSchwarzschild, gr::Solutions::GaugeWave<1>,\n     gr::Solutions::GaugeWave<2>, gr::Solutions::GaugeWave<3>,\n     gr::Solutions::GaugePlaneWave<1>, gr::Solutions::GaugePlaneWave<2>,\n     gr::Solutions::GaugePlaneWave<3>, gr::Solutions::Minkowski<1>,\n     gr::Solutions::Minkowski<2>, gr::Solutions::Minkowski<3>,\n     gr::Solutions::KerrSchild, gr::Solutions::SphericalKerrSchild,\n     gr::Solutions::TrumpetSchwarzschild))\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/list/for_each.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace gh {\nnamespace Solutions {\n/*!\n * \\brief A wrapper for general-relativity analytic solutions that loads\n * the analytic solution and then adds a function that returns\n * any combination of the generalized-harmonic evolution variables,\n * specifically `gr::Tags::SpacetimeMetric`, `gh::Tags::Pi`,\n * and `gh::Tags::Phi`\n */\ntemplate <typename SolutionType>\nclass WrappedGr : public virtual evolution::initial_data::InitialData,\n                  public SolutionType {\n public:\n  using SolutionType::SolutionType;\n\n  WrappedGr() = default;\n  WrappedGr(const WrappedGr& /*rhs*/) = default;\n  WrappedGr& operator=(const WrappedGr& /*rhs*/) = default;\n  WrappedGr(WrappedGr&& /*rhs*/) = default;\n  WrappedGr& operator=(WrappedGr&& /*rhs*/) = default;\n  ~WrappedGr() override = default;\n\n  explicit WrappedGr(const SolutionType& wrapped_solution)\n      : SolutionType(wrapped_solution) {}\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit WrappedGr(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(WrappedGr);\n  /// \\endcond\n\n  static constexpr size_t volume_dim = SolutionType::volume_dim;\n  using options = typename SolutionType::options;\n  static constexpr Options::String help = SolutionType::help;\n  static std::string name() {\n    return \"GeneralizedHarmonic(\" + pretty_type::name<SolutionType>() + \")\";\n  }\n\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataVector, volume_dim>,\n                                   tmpl::size_t<volume_dim>, Frame::Inertial>;\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, volume_dim>,\n                    tmpl::size_t<volume_dim>, Frame::Inertial>;\n  using TimeDerivLapse = ::Tags::dt<gr::Tags::Lapse<DataVector>>;\n  using TimeDerivShift = ::Tags::dt<gr::Tags::Shift<DataVector, volume_dim>>;\n  using TimeDerivSpatialMetric =\n      ::Tags::dt<gr::Tags::SpatialMetric<DataVector, volume_dim>>;\n\n  using IntermediateVars = tuples::tagged_tuple_from_typelist<\n      typename SolutionType::template tags<DataVector>>;\n\n  template <typename DataType>\n  using tags = tmpl::push_back<typename SolutionType::template tags<DataType>,\n                               gr::Tags::SpacetimeMetric<DataType, volume_dim>,\n                               gh::Tags::Pi<DataVector, volume_dim>,\n                               gh::Tags::Phi<DataVector, volume_dim>>;\n\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataVector, volume_dim>& x, double t,\n      tmpl::list<Tags...> /*meta*/) const {\n    // Get the underlying solution's variables using the solution's tags list,\n    // store in IntermediateVariables\n    const IntermediateVars& intermediate_vars = SolutionType::variables(\n        x, t, typename SolutionType::template tags<DataVector>{});\n\n    return {\n        get<Tags>(variables(x, t, tmpl::list<Tags>{}, intermediate_vars))...};\n  }\n\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, volume_dim>& x,\n                                     double t, tmpl::list<Tag> /*meta*/) const {\n    const IntermediateVars& intermediate_vars = SolutionType::variables(\n        x, t, typename SolutionType::template tags<DataVector>{});\n    return {get<Tag>(variables(x, t, tmpl::list<Tag>{}, intermediate_vars))};\n  }\n\n  // overloads for wrapping analytic data\n\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataVector, volume_dim>& x,\n      tmpl::list<Tags...> /*meta*/) const {\n    // Get the underlying solution's variables using the solution's tags list,\n    // store in IntermediateVariables\n    const IntermediateVars intermediate_vars = SolutionType::variables(\n        x, typename SolutionType::template tags<DataVector>{});\n\n    return {get<Tags>(variables(x, tmpl::list<Tags>{}, intermediate_vars))...};\n  }\n\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, volume_dim>& x,\n                                     tmpl::list<Tag> /*meta*/) const {\n    const IntermediateVars intermediate_vars = SolutionType::variables(\n        x, typename SolutionType::template tags<DataVector>{});\n    return {get<Tag>(variables(x, tmpl::list<Tag>{}, intermediate_vars))};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  // Preprocessor logic to avoid declaring variables() functions for\n  // tags other than the three the wrapper adds (i.e., other than\n  // gr::Tags::SpacetimeMetric, gh::Tags::Pi, and\n  // GeneralizedHarmonic:Tags::Phi)\n  using TagShift = gr::Tags::Shift<DataVector, volume_dim>;\n  using TagSpatialMetric = gr::Tags::SpatialMetric<DataVector, volume_dim>;\n  using TagInverseSpatialMetric =\n      gr::Tags::InverseSpatialMetric<DataVector, volume_dim>;\n  using TagExCurvature = gr::Tags::ExtrinsicCurvature<DataVector, volume_dim>;\n\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/, double /*t*/,\n      tmpl::list<Tag> /*meta*/,\n      const IntermediateVars& intermediate_vars) const {\n    static_assert(\n        tmpl::list_contains_v<typename SolutionType::template tags<DataVector>,\n                              Tag>);\n    return {get<Tag>(intermediate_vars)};\n  }\n\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/, tmpl::list<Tag> /*meta*/,\n      const IntermediateVars& intermediate_vars) const {\n    static_assert(\n        tmpl::list_contains_v<typename SolutionType::template tags<DataVector>,\n                              Tag>);\n    return {get<Tag>(intermediate_vars)};\n  }\n\n  tuples::TaggedTuple<gr::Tags::SpacetimeMetric<DataVector, volume_dim>>\n  variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, volume_dim>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n  tuples::TaggedTuple<gh::Tags::Pi<DataVector, volume_dim>> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<gh::Tags::Pi<DataVector, volume_dim>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n  tuples::TaggedTuple<gh::Tags::Phi<DataVector, volume_dim>> variables(\n      const tnsr::I<DataVector, volume_dim>& /*x*/,\n      tmpl::list<gh::Tags::Phi<DataVector, volume_dim>> /*meta*/,\n      const IntermediateVars& intermediate_vars) const;\n\n  tuples::TaggedTuple<gr::Tags::SpacetimeMetric<DataVector, volume_dim>>\n  variables(const tnsr::I<DataVector, volume_dim>& x, const double /*t*/,\n            tmpl::list<gr::Tags::SpacetimeMetric<DataVector, volume_dim>> meta,\n            const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n  tuples::TaggedTuple<gh::Tags::Pi<DataVector, volume_dim>> variables(\n      const tnsr::I<DataVector, volume_dim>& x, double /*t*/,\n      tmpl::list<gh::Tags::Pi<DataVector, volume_dim>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n  tuples::TaggedTuple<gh::Tags::Phi<DataVector, volume_dim>> variables(\n      const tnsr::I<DataVector, volume_dim>& x, double /*t*/,\n      tmpl::list<gh::Tags::Phi<DataVector, volume_dim>> meta,\n      const IntermediateVars& intermediate_vars) const {\n    return variables(x, meta, intermediate_vars);\n  }\n};\n\ntemplate <typename SolutionType>\nbool operator==(const WrappedGr<SolutionType>& lhs,\n                const WrappedGr<SolutionType>& rhs);\n\ntemplate <typename SolutionType>\nbool operator!=(const WrappedGr<SolutionType>& lhs,\n                const WrappedGr<SolutionType>& rhs);\n\ntemplate <typename SolutionType>\nWrappedGr(SolutionType solution) -> WrappedGr<SolutionType>;\n}  // namespace Solutions\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gh::Solutions {\ntemplate <typename SolutionType>\nWrappedGr<SolutionType>::WrappedGr(CkMigrateMessage* msg)\n    : InitialData(msg), SolutionType(msg) {}\n\ntemplate <typename SolutionType>\nstd::unique_ptr<evolution::initial_data::InitialData>\nWrappedGr<SolutionType>::get_clone() const {\n  return std::make_unique<WrappedGr<SolutionType>>(*this);\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<gr::Tags::SpacetimeMetric<\n    DataVector, gh::Solutions::WrappedGr<SolutionType>::volume_dim>>\nWrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector,\n                  gh::Solutions::WrappedGr<SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<gr::Tags::SpacetimeMetric<\n        DataVector,\n        gh::Solutions::WrappedGr<SolutionType>::volume_dim>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(intermediate_vars);\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector,\n                          gh::Solutions::WrappedGr<SolutionType>::volume_dim>>(\n          intermediate_vars);\n  const auto& spatial_metric = get<gr::Tags::SpatialMetric<\n      DataVector, gh::Solutions::WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n\n  return {gr::spacetime_metric(lapse, shift, spatial_metric)};\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<gh::Tags::Phi<DataVector,\n    gh::Solutions::WrappedGr<SolutionType>::volume_dim>>\nWrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, gh::Solutions::WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<gh::Tags::Phi<DataVector,\n        gh::Solutions::WrappedGr<SolutionType>::volume_dim>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(intermediate_vars);\n  const auto& deriv_lapse =\n      get<typename WrappedGr<SolutionType>::DerivLapse>(intermediate_vars);\n\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector,\n                          gh::Solutions::WrappedGr<SolutionType>::volume_dim>>(\n          intermediate_vars);\n  const auto& deriv_shift =\n      get<typename WrappedGr<SolutionType>::DerivShift>(intermediate_vars);\n\n  const auto& spatial_metric = get<gr::Tags::SpatialMetric<\n      DataVector, gh::Solutions::WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto& deriv_spatial_metric =\n      get<typename WrappedGr<SolutionType>::DerivSpatialMetric>(\n          intermediate_vars);\n\n  return {gh::phi(lapse, deriv_lapse, shift, deriv_shift,\n                                   spatial_metric, deriv_spatial_metric)};\n}\n\ntemplate <typename SolutionType>\ntuples::TaggedTuple<gh::Tags::Pi<DataVector,\n    gh::Solutions::WrappedGr<SolutionType>::volume_dim>>\nWrappedGr<SolutionType>::variables(\n    const tnsr::I<DataVector, gh::Solutions::WrappedGr<\n                                  SolutionType>::volume_dim>& /*x*/,\n    tmpl::list<gh::Tags::Pi<DataVector,\n        gh::Solutions::WrappedGr<SolutionType>::volume_dim>> /*meta*/,\n    const IntermediateVars& intermediate_vars) const {\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(intermediate_vars);\n  const auto& dt_lapse =\n      get<typename WrappedGr<SolutionType>::TimeDerivLapse>(intermediate_vars);\n  const auto& deriv_lapse =\n      get<typename WrappedGr<SolutionType>::DerivLapse>(intermediate_vars);\n\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector,\n                          gh::Solutions::WrappedGr<SolutionType>::volume_dim>>(\n          intermediate_vars);\n  const auto& dt_shift =\n      get<typename WrappedGr<SolutionType>::TimeDerivShift>(intermediate_vars);\n  const auto& deriv_shift =\n      get<typename WrappedGr<SolutionType>::DerivShift>(intermediate_vars);\n\n  const auto& spatial_metric = get<gr::Tags::SpatialMetric<\n      DataVector, gh::Solutions::WrappedGr<SolutionType>::volume_dim>>(\n      intermediate_vars);\n  const auto& dt_spatial_metric =\n      get<typename WrappedGr<SolutionType>::TimeDerivSpatialMetric>(\n          intermediate_vars);\n  const auto& deriv_spatial_metric =\n      get<typename WrappedGr<SolutionType>::DerivSpatialMetric>(\n          intermediate_vars);\n\n  const auto phi =\n      gh::phi(lapse, deriv_lapse, shift, deriv_shift,\n                               spatial_metric, deriv_spatial_metric);\n\n  return {gh::pi(lapse, dt_lapse, shift, dt_shift,\n                                  spatial_metric, dt_spatial_metric, phi)};\n}\n\ntemplate <typename SolutionType>\nvoid WrappedGr<SolutionType>::pup(PUP::er& p) {\n  InitialData::pup(p);\n  SolutionType::pup(p);\n}\n\ntemplate <typename SolutionType>\nPUP::able::PUP_ID WrappedGr<SolutionType>::my_PUP_ID = 0;\n\ntemplate <typename SolutionType>\nbool operator==(const WrappedGr<SolutionType>& lhs,\n                const WrappedGr<SolutionType>& rhs) {\n  return static_cast<const SolutionType&>(lhs) ==\n         static_cast<const SolutionType&>(rhs);\n}\n\ntemplate <typename SolutionType>\nbool operator!=(const WrappedGr<SolutionType>& lhs,\n                const WrappedGr<SolutionType>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define WRAPPED_GR_SOLUTION_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define WRAPPED_GR_INSTANTIATE(_, data)                      \\\n  template class gh::Solutions::WrappedGr<  \\\n      WRAPPED_GR_SOLUTION_TYPE(data)>;                       \\\n  template bool gh::Solutions::operator==(  \\\n      const WrappedGr<WRAPPED_GR_SOLUTION_TYPE(data)>& lhs,  \\\n      const WrappedGr<WRAPPED_GR_SOLUTION_TYPE(data)>& rhs); \\\n  template bool gh::Solutions::operator!=(  \\\n      const WrappedGr<WRAPPED_GR_SOLUTION_TYPE(data)>& lhs,  \\\n      const WrappedGr<WRAPPED_GR_SOLUTION_TYPE(data)>& rhs);\n}  // namespace gh::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GhGrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GhGrMhdSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  InstantiateWrappedGr.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  GeneralRelativitySolutions\n  GrMhdSolutions\n  PRIVATE\n  DataStructures\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GhGrMhd/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::grmhd::Solutions {\n/// GRMHD solutions wrapped for GH\n\n/// \\brief List of all analytic solutions\nusing all_solutions =\n    tmpl::list<gh::Solutions::WrappedGr<::grmhd::Solutions::BondiMichel>>;\n\n}  // namespace gh::grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GhGrMhd/InstantiateWrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.tpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nGENERATE_INSTANTIATIONS(WRAPPED_GR_INSTANTIATE, (grmhd::Solutions::BondiMichel))\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GhRelativisticEulerSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  InstantiateWrappedGr.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  GeneralRelativitySolutions\n  RelativisticEulerSolutions\n  PRIVATE\n  DataStructures\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh::RelativisticEuler::Solutions {\n/// Relativistic hydro solutions wrapped for GH\n\n/// \\brief List of all analytic solutions\nusing all_solutions = tmpl::list<\n    gh::Solutions::WrappedGr<\n        ::RelativisticEuler::Solutions::FishboneMoncriefDisk>,\n    gh::Solutions::WrappedGr<::RelativisticEuler::Solutions::RotatingStar>,\n    gh::Solutions::WrappedGr<::RelativisticEuler::Solutions::TovStar>>;\n\n}  // namespace gh::RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/InstantiateWrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.tpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nGENERATE_INSTANTIATIONS(WRAPPED_GR_INSTANTIATE,\n                        (RelativisticEuler::Solutions::FishboneMoncriefDisk,\n                         RelativisticEuler::Solutions::RotatingStar,\n                         RelativisticEuler::Solutions::TovStar))\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/CrossProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace grmhd::Solutions {\n\nAlfvenWave::AlfvenWave(const double wavenumber, const double pressure,\n                       const double rest_mass_density,\n                       const double electron_fraction,\n                       const double adiabatic_index,\n                       const std::array<double, 3>& background_magnetic_field,\n                       const std::array<double, 3>& wave_magnetic_field)\n    : wavenumber_(wavenumber),\n      pressure_(pressure),\n      rest_mass_density_(rest_mass_density),\n      electron_fraction_(electron_fraction),\n      adiabatic_index_(adiabatic_index),\n      background_magnetic_field_(background_magnetic_field),\n      wave_magnetic_field_(wave_magnetic_field),\n      equation_of_state_{adiabatic_index_},\n      initial_unit_vector_along_background_magnetic_field_{\n          background_magnetic_field},\n      initial_unit_vector_along_wave_magnetic_field_{wave_magnetic_field},\n      initial_unit_vector_along_wave_electric_field_{\n          cross_product(initial_unit_vector_along_wave_magnetic_field_,\n                        initial_unit_vector_along_background_magnetic_field_)} {\n  magnitude_B0_ =\n      magnitude(initial_unit_vector_along_background_magnetic_field_).get();\n  magnitude_B1_ =\n      magnitude(initial_unit_vector_along_wave_magnetic_field_).get();\n  magnitude_E_ =\n      magnitude(initial_unit_vector_along_wave_electric_field_).get();\n  for (size_t d = 0; d < 3; d++) {\n    initial_unit_vector_along_background_magnetic_field_.get(d) /=\n        magnitude_B0_;\n    initial_unit_vector_along_wave_magnetic_field_.get(d) /= magnitude_B1_;\n    initial_unit_vector_along_wave_electric_field_.get(d) /= magnitude_E_;\n  }\n  ASSERT(equal_within_roundoff(\n             dot_product(initial_unit_vector_along_background_magnetic_field_,\n                         initial_unit_vector_along_wave_magnetic_field_)\n                 .get(),\n             0.0),\n         \"The background and wave magnetic fields must be perpendicular.\");\n  const double auxiliary_speed_b0 =\n      magnitude_B0_ / sqrt((rest_mass_density_ + pressure_ * adiabatic_index_ /\n                                                     (adiabatic_index_ - 1.0)) +\n                           square(magnitude_B0_) + square(magnitude_B1_));\n  const double auxiliary_speed_b1 =\n      magnitude_B1_ * auxiliary_speed_b0 / magnitude_B0_;\n  const double one_over_speed_denominator =\n      1.0 / sqrt(0.5 * (1.0 + sqrt(1.0 - 4.0 * square(auxiliary_speed_b0 *\n                                                      auxiliary_speed_b1))));\n  alfven_speed_ = auxiliary_speed_b0 * one_over_speed_denominator;\n  fluid_speed_ = -auxiliary_speed_b1 * one_over_speed_denominator;\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> AlfvenWave::get_clone()\n    const {\n  return std::make_unique<AlfvenWave>(*this);\n}\n\nAlfvenWave::AlfvenWave(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid AlfvenWave::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | wavenumber_;\n  p | pressure_;\n  p | rest_mass_density_;\n  p | electron_fraction_;\n  p | adiabatic_index_;\n  p | background_magnetic_field_;\n  p | wave_magnetic_field_;\n  p | alfven_speed_;\n  p | fluid_speed_;\n  p | initial_unit_vector_along_background_magnetic_field_;\n  p | initial_unit_vector_along_wave_magnetic_field_;\n  p | initial_unit_vector_along_wave_electric_field_;\n  p | magnitude_B0_;\n  p | magnitude_B1_;\n  p | magnitude_E_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n}\n\ntemplate <typename DataType>\nDataType AlfvenWave::k_dot_x_minus_vt(const tnsr::I<DataType, 3>& x,\n                                      const double t) const {\n  auto result = make_with_value<DataType>(x, -wavenumber_ * alfven_speed_ * t);\n  for (size_t d = 0; d < 3; d++) {\n    result += wavenumber_ * x.get(d) *\n              initial_unit_vector_along_background_magnetic_field_.get(d);\n  }\n  return result;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nAlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, rest_mass_density_)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nAlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, electron_fraction_)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nAlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(\n      x, pressure_ / ((adiabatic_index_ - 1.0) * rest_mass_density_))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> AlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, pressure_)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nAlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double t,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  const DataType phase = k_dot_x_minus_vt(x, t);\n  auto result = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  for (size_t d = 0; d < 3; d++) {\n    result.get(d) =\n        fluid_speed_ *\n        (cos(phase) * initial_unit_vector_along_wave_magnetic_field_[d] -\n         sin(phase) * initial_unit_vector_along_wave_electric_field_[d]);\n  }\n  return {std::move(result)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nAlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double t,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  const DataType phase = k_dot_x_minus_vt(x, t);\n  auto result = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  for (size_t d = 0; d < 3; d++) {\n    result.get(d) =\n        gsl::at(background_magnetic_field_, d) +\n        magnitude_B1_ *\n            (cos(phase) * initial_unit_vector_along_wave_magnetic_field_[d] -\n             sin(phase) * initial_unit_vector_along_wave_electric_field_[d]);\n  }\n  return {std::move(result)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nAlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>> AlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(\n      x, 1.0 / sqrt(1.0 - square(fluid_speed_)))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nAlfvenWave::variables(\n    const tnsr::I<DataType, 3>& x, double t,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  Scalar<DataType> specific_internal_energy = std::move(\n      get<hydro::Tags::SpecificInternalEnergy<DataType>>(variables<DataType>(\n          x, t, tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})));\n  get(specific_internal_energy) *= adiabatic_index_;\n  get(specific_internal_energy) += 1.0;\n  return {std::move(specific_internal_energy)};\n}\n\nPUP::able::PUP_ID AlfvenWave::my_PUP_ID = 0;\n\nbool operator==(const AlfvenWave& lhs, const AlfvenWave& rhs) {\n  // there is no comparison operator for the EoS, but should be okay as\n  // the adiabatic_indexs are compared\n  return lhs.wavenumber_ == rhs.wavenumber_ and\n         lhs.pressure_ == rhs.pressure_ and\n         lhs.rest_mass_density_ == rhs.rest_mass_density_ and\n         lhs.electron_fraction_ == rhs.electron_fraction_ and\n         lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.background_magnetic_field_ == rhs.background_magnetic_field_ and\n         lhs.wave_magnetic_field_ == rhs.wave_magnetic_field_ and\n         lhs.initial_unit_vector_along_background_magnetic_field_ ==\n             rhs.initial_unit_vector_along_background_magnetic_field_ and\n         lhs.initial_unit_vector_along_wave_magnetic_field_ ==\n             rhs.initial_unit_vector_along_wave_magnetic_field_ and\n         lhs.initial_unit_vector_along_wave_electric_field_ ==\n             rhs.initial_unit_vector_along_wave_electric_field_ and\n         lhs.magnitude_B0_ == rhs.magnitude_B0_ and\n         lhs.magnitude_B1_ == rhs.magnitude_B1_ and\n         lhs.magnitude_E_ == rhs.magnitude_E_ and\n         lhs.alfven_speed_ == rhs.alfven_speed_ and\n         lhs.fluid_speed_ == rhs.fluid_speed_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const AlfvenWave& lhs, const AlfvenWave& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                       \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>                   \\\n      AlfvenWave::variables(const tnsr::I<DTYPE(data), 3>& x, double t,    \\\n                            tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::DivergenceCleaningField, hydro::Tags::LorentzFactor,\n     hydro::Tags::SpecificEnthalpy))\n\n#define INSTANTIATE_VECTORS(_, data)                                          \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3>>                   \\\n      AlfvenWave::variables(const tnsr::I<DTYPE(data), 3>& x, double t,       \\\n                            tmpl::list<TAG(data) < DTYPE(data), 3>> /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::Solutions {\n\n/*!\n * \\brief Circularly polarized Alfv&eacute;n wave solution in Minkowski\n * spacetime travelling along a background magnetic field.\n *\n * An analytic solution to the 3-D GRMHD system. The user specifies the\n * wavenumber \\f$k\\f$ of the Alfv&eacute;n wave, the constant pressure\n * throughout the fluid \\f$P\\f$, the constant rest mass density throughout the\n * fluid \\f$\\rho_0\\f$, the adiabatic index for the ideal fluid equation of\n * state \\f$\\gamma\\f$, the magnetic field parallel to the wavevector\n * \\f$\\vec{B}_0\\f$, and the transverse magnetic field vector \\f$\\vec{B}_1\\f$ at\n * \\f$x=y=z=t=0\\f$.\n *\n * We define the auxiliary velocities:\n * \\f[v^2_{B0} = \\frac{B_0^2}{\\rho_0 h + B_0^2 + B_1^2}\\f]\n * \\f[v^2_{B1} = \\frac{B_1^2}{\\rho_0 h + B_0^2 + B_1^2}\\f]\n *\n * The Alfv&eacute;n wave phase speed that solves the GRMHD equations, even for\n * finite amplitudes \\cite DelZanna2007pk, is given by:\n *\n * \\f[v_A^2 = \\frac{2v^2_{B0}}{1 + \\sqrt{1 - 4 v^2_{B0}v^2_{B1}}}\\f]\n *\n * The amplitude of the fluid velocity is given by:\n *\n * \\f[v_f^2 = \\frac{2v^2_{B1}}{1 + \\sqrt{1 - 4 v^2_{B0}v^2_{B1}}}\\f]\n *\n * The electromagnetic field vectors define a set of basis vectors:\n *\n * \\f{align*}{\n * \\hat{b}_0 &= \\vec{B_0}/B_0 \\\\\n * \\hat{b}_1 &= \\vec{B_1}/B_1 \\\\\n * \\hat{e} &= \\hat{b}_1 \\times \\hat{b}_0\n * \\f}\n *\n * We also define the auxiliary variable for the phase \\f$\\phi\\f$:\n * \\f[\\phi = k(\\vec{x}\\cdot\\hat{b}_0 - v_A t)\\f]\n * In Cartesian coordinates \\f$(x, y, z)\\f$, and using\n * dimensionless units, the primitive quantities at a given time \\f$t\\f$ are\n * then\n *\n * \\f{align*}\n * \\rho(\\vec{x},t) &= \\rho_0 \\\\\n * \\vec{v}(\\vec{x},t) &= v_f(-\\hat{b}_1\\cos\\phi\n *  +\\hat{e}\\sin\\phi)\\\\\n * P(\\vec{x},t) &= P, \\\\\n * \\epsilon(\\vec{x}, t) &= \\frac{P}{(\\gamma - 1)\\rho_0}\\\\\n * \\vec{B}(\\vec{x},t) &= B_1(\\hat{b}_1\\cos\\phi\n *  -\\hat{e}\\sin\\phi) + \\vec{B_0}\n * \\f}\n *\n * Note that the phase speed is not the characteristic Alfv&eacute;n speed\n * \\f$c_A\\f$, which is the speed in the limiting case where the total magnetic\n * field is parallel to the direction of propagation \\cite DelZanna2007pk :\n *\n * \\f[c_A^2 = \\frac{b^2}{\\rho_0 h + b^2}\\f]\n *\n * Where \\f$b^2\\f$ is the invariant quantity \\f$B^2 - E^2\\f$, given by:\n *\n * \\f[b^2 = B_0^2 + B_1^2 - B_0^2 v_f^2\\f]\n */\nclass AlfvenWave : public evolution::initial_data::InitialData,\n                   public AnalyticSolution,\n                   public hydro::TemperatureInitialization<AlfvenWave>,\n                   public MarkAsAnalyticSolution {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  /// The wave number of the profile.\n  struct WaveNumber {\n    using type = double;\n    static constexpr Options::String help = {\"The wave number of the profile.\"};\n  };\n\n  /// The constant pressure throughout the fluid.\n  struct Pressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The constant pressure throughout the fluid.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The constant rest mass density throughout the fluid.\n  struct RestMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The constant rest mass density throughout the fluid.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The constant electron fraction throughout the fluid.\n  struct ElectronFraction {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The constant electron fraction throughout the fluid.\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n\n  /// The adiabatic index for the ideal fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index for the ideal fluid.\"};\n    static type lower_bound() { return 1.0; }\n  };\n\n  /// The background static magnetic field vector.\n  struct BackgroundMagneticField {\n    using type = std::array<double, 3>;\n    static std::string name() { return \"BkgdMagneticField\"; }\n    static constexpr Options::String help = {\n        \"The background magnetic field [B0^x, B0^y, B0^z].\"};\n  };\n\n  /// The sinusoidal magnetic field vector associated with\n  /// the Alfv&eacute;n wave, perpendicular to the background\n  /// magnetic field vector.\n  struct WaveMagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"The wave magnetic field [B1^x, B1^y, B1^z].\"};\n  };\n\n  using options =\n      tmpl::list<WaveNumber, Pressure, RestMassDensity, ElectronFraction,\n                 AdiabaticIndex, BackgroundMagneticField, WaveMagneticField>;\n  static constexpr Options::String help = {\n      \"Circularly polarized Alfven wave in Minkowski spacetime.\"};\n\n  AlfvenWave() = default;\n  AlfvenWave(const AlfvenWave& /*rhs*/) = default;\n  AlfvenWave& operator=(const AlfvenWave& /*rhs*/) = default;\n  AlfvenWave(AlfvenWave&& /*rhs*/) = default;\n  AlfvenWave& operator=(AlfvenWave&& /*rhs*/) = default;\n  ~AlfvenWave() override = default;\n\n  AlfvenWave(double wavenumber, double pressure, double rest_mass_density,\n             double electron_fraction, double adiabatic_index,\n             const std::array<double, 3>& background_magnetic_field,\n             const std::array<double, 3>& wave_magnetic_field);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit AlfvenWave(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(AlfvenWave);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve hydro variable at `(x, t)`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x, double t,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x, double /*t*/,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, t, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n  /// @}\n\n  /// Retrieve a collection of hydro variables at `(x, t)`\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x, double t,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, t, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, t, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<tmpl::list_contains_v<\n                gr::analytic_solution_tags<3, DataType>, Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x, double t,\n                                     tmpl::list<Tag> /*meta*/) const {\n    return background_spacetime_.variables(x, t, tmpl::list<Tag>{});\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n  const EquationsOfState::IdealFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n protected:\n  friend bool operator==(const AlfvenWave& lhs, const AlfvenWave& rhs);\n\n  // Computes the phase.\n  template <typename DataType>\n  DataType k_dot_x_minus_vt(const tnsr::I<DataType, 3>& x, double t) const;\n  double wavenumber_ = std::numeric_limits<double>::signaling_NaN();\n  double pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double rest_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double electron_fraction_ = std::numeric_limits<double>::signaling_NaN();\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> background_magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 3> wave_magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  EquationsOfState::IdealFluid<true> equation_of_state_{};\n  tnsr::I<double, 3> initial_unit_vector_along_background_magnetic_field_{};\n  tnsr::I<double, 3> initial_unit_vector_along_wave_magnetic_field_{};\n  tnsr::I<double, 3> initial_unit_vector_along_wave_electric_field_{};\n  double magnitude_B0_ = std::numeric_limits<double>::signaling_NaN();\n  double magnitude_B1_ = std::numeric_limits<double>::signaling_NaN();\n  double magnitude_E_ = std::numeric_limits<double>::signaling_NaN();\n  double alfven_speed_ = std::numeric_limits<double>::signaling_NaN();\n  double fluid_speed_ = std::numeric_limits<double>::signaling_NaN();\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n};\n\nbool operator!=(const AlfvenWave& lhs, const AlfvenWave& rhs);\n\n}  // namespace grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace grmhd::Solutions {\n\nBondiMichel::BondiMichel(const double mass, const double sonic_radius,\n                         const double sonic_density,\n                         const double polytropic_exponent,\n                         const double mag_field_strength)\n    : mass_(mass),\n      sonic_radius_(sonic_radius),\n      sonic_density_(sonic_density),\n      polytropic_exponent_(polytropic_exponent),\n      mag_field_strength_(mag_field_strength),\n      // From Rezzola and Zanotti (2013)\n      // Rezzola and Zanotti (2013) Eq. 11.94(a)\n      sonic_fluid_speed_squared_(0.5 * mass_ / sonic_radius_),\n      // Rezzola and Zanotti (2013) Eq. 11.94(b)\n      sonic_sound_speed_squared_(sonic_fluid_speed_squared_ /\n                                 (1.0 - 3.0 * sonic_fluid_speed_squared_)),\n      // Rezzola and Zanotti (2013) Eq. 11.101\n      mass_accretion_rate_over_four_pi_(square(sonic_radius_) * sonic_density *\n                                        sqrt(sonic_fluid_speed_squared_)),\n      background_spacetime_{mass_, std::array<double, 3>{{0.0, 0.0, 0.0}},\n                            std::array<double, 3>{{0.0, 0.0, 0.0}}} {\n  const double gamma_minus_one = polytropic_exponent_ - 1.0;\n  // Rezzola and Zanotti (2013) Eq. 11.96\n  const double sonic_newtonian_sound_speed_squared =\n      gamma_minus_one * sonic_sound_speed_squared_ /\n      (gamma_minus_one - sonic_sound_speed_squared_);\n  polytropic_constant_ = sonic_newtonian_sound_speed_squared *\n                         pow(sonic_density_, 1.0 - polytropic_exponent_) /\n                         polytropic_exponent_;\n  equation_of_state_ = EquationsOfState::PolytropicFluid<true>{\n      polytropic_constant_, polytropic_exponent_};\n  const double sonic_specific_enthalpy_squared_minus_one =\n      sonic_newtonian_sound_speed_squared / gamma_minus_one;\n\n  // Rezzola and Zanotti (2013) Eq. 11.97\n  bernoulli_constant_squared_minus_one_ =\n      -3.0 * sonic_fluid_speed_squared_ *\n          square(1.0 + sonic_specific_enthalpy_squared_minus_one) +\n      sonic_specific_enthalpy_squared_minus_one *\n          (2.0 + sonic_specific_enthalpy_squared_minus_one);\n\n  // Inverse of Rezzola and Zanotti (2013) Eq 11.99 without truncation\n  const double sound_speed_at_infinity_squared =\n      gamma_minus_one + (sonic_sound_speed_squared_ - gamma_minus_one) *\n                            sqrt(1.0 + 3.0 * sonic_sound_speed_squared_);\n\n  // Rezzola and Zanotti (2013) Eq. 11.102 is a good approximation\n  // only for `sonic_radius` >> 2.0 * mass_, The exact form has a\n  // factor of sqrt(1.0 + 3.0 * sonic_sound_speed_squared_)\n  rest_mass_density_at_infinity_ =\n      sonic_density_ * pow(sound_speed_at_infinity_squared /\n                               (sonic_sound_speed_squared_ *\n                                sqrt(1.0 + 3.0 * sonic_sound_speed_squared_)),\n                           1.0 / gamma_minus_one);\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> BondiMichel::get_clone()\n    const {\n  return std::make_unique<BondiMichel>(*this);\n}\n\nBondiMichel::BondiMichel(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid BondiMichel::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | mass_;\n  p | sonic_radius_;\n  p | sonic_density_;\n  p | polytropic_exponent_;\n  p | mag_field_strength_;\n  p | sonic_fluid_speed_squared_;\n  p | sonic_sound_speed_squared_;\n  p | polytropic_constant_;\n  p | mass_accretion_rate_over_four_pi_;\n  p | bernoulli_constant_squared_minus_one_;\n  p | rest_mass_density_at_infinity_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n}\n\ntemplate <typename DataType>\nBondiMichel::IntermediateVars<DataType>::IntermediateVars(\n    const double rest_mass_density_at_infinity,\n    const double in_mass_accretion_rate_over_four_pi, const double in_mass,\n    const double in_polytropic_constant, const double in_polytropic_exponent,\n    const double in_bernoulli_constant_squared_minus_one,\n    const double in_sonic_radius, const double in_sonic_density,\n    const tnsr::I<DataType, 3>& x, const bool need_spacetime,\n    const gr::Solutions::KerrSchild& background_spacetime)\n    : radius((magnitude(x)).get()),\n      rest_mass_density(make_with_value<DataType>(x, 0.0)),\n      electron_fraction(make_with_value<DataType>(x, 0.0)),\n      mass_accretion_rate_over_four_pi(in_mass_accretion_rate_over_four_pi),\n      mass(in_mass),\n      polytropic_constant(in_polytropic_constant),\n      polytropic_exponent(in_polytropic_exponent),\n      bernoulli_constant_squared_minus_one(\n          in_bernoulli_constant_squared_minus_one),\n      sonic_radius(in_sonic_radius),\n      sonic_density(in_sonic_density) {\n  // NOLINTNEXTLINE(clang-analyzer-core)\n  for (size_t i = 0; i < get_size(rest_mass_density); i++) {\n    const double current_radius = get_element(radius, i);\n    // Near the sonic radius, a second root to the Bernoulli\n    // root function appears. Within the sonic radius, the\n    // upper bound of\n    // `mass_accretion_rate_over_four_pi_ * sqrt(2.0 /\n    // (mass_* cube(current_radius)))` selects the correct one\n    // of two possible roots. Beyond the sonic radius, this\n    // becomes the lower bound provided to the root finder.\n    const double sonic_bound = mass_accretion_rate_over_four_pi *\n                               sqrt(2.0 / (mass * cube(current_radius)));\n    get_element(rest_mass_density, i) =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(\n            [&current_radius, this](const double guess_for_rho) {\n              return bernoulli_root_function(guess_for_rho, current_radius);\n            },\n            current_radius < sonic_radius ? rest_mass_density_at_infinity\n                                          : sonic_bound,\n            current_radius < sonic_radius ? sonic_bound : sonic_density, 1.e-15,\n            1.e-15);\n    get_element(electron_fraction, i) = 0.4;\n  }\n  if (need_spacetime) {\n    kerr_schild_soln = background_spacetime.variables(\n        x, 0.0, gr::Solutions::KerrSchild::tags<DataType>{});\n  }\n}\n\ntemplate <typename DataType>\ndouble BondiMichel::IntermediateVars<DataType>::bernoulli_root_function(\n    const double rest_mass_density_guess, const double current_radius) const {\n  const double gamma_minus_one = polytropic_exponent - 1.0;\n  const double polytropic_index_times_newtonian_sound_speed_squared =\n      (polytropic_exponent * polytropic_constant *\n       pow(rest_mass_density_guess, gamma_minus_one)) /\n      gamma_minus_one;\n  const double specific_enthalpy_squared_minus_one =\n      polytropic_index_times_newtonian_sound_speed_squared *\n      (2.0 + polytropic_index_times_newtonian_sound_speed_squared);\n  const double specific_enthalpy_squared =\n      specific_enthalpy_squared_minus_one + 1.0;\n  // As the bernoulli constant is 1.0 + a small number, it is better numerically\n  // to compute it as (small_number_1) + (1.0 + small_number_1) * small_number_2\n  // as opposed to (1.0 + small_number_1)^2 * (1.0 + small_number_2) - 1.0.\n  // Computing it in this way allows the numerical root-finding method to work\n  // even for large radii.\n  return specific_enthalpy_squared_minus_one +\n         specific_enthalpy_squared *\n             (-2.0 * mass / current_radius +\n              square(mass_accretion_rate_over_four_pi /\n                     (square(current_radius) * rest_mass_density_guess))) -\n         bernoulli_constant_squared_minus_one;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nBondiMichel::variables(\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n    const IntermediateVars<DataType>& vars) const {\n  return {Scalar<DataType>{DataType{vars.rest_mass_density}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nBondiMichel::variables(\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/,\n    const IntermediateVars<DataType>& vars) const {\n  return {Scalar<DataType>{DataType{vars.electron_fraction}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> BondiMichel::variables(\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n    const IntermediateVars<DataType>& vars) const {\n  return {Scalar<DataType>{\n      DataType{polytropic_constant_ *\n               pow(vars.rest_mass_density, polytropic_exponent_)}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Temperature<DataType>> BondiMichel::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/,\n    const IntermediateVars<DataType>& /*vars*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nBondiMichel::variables(\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n    const IntermediateVars<DataType>& vars) const {\n  return {Scalar<DataType>{\n      DataType{polytropic_constant_ *\n               pow(vars.rest_mass_density, polytropic_exponent_ - 1.0) /\n               (polytropic_exponent_ - 1.0)}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nBondiMichel::variables(\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/,\n    const IntermediateVars<DataType>& vars) const {\n  return {Scalar<DataType>{DataType{\n      1.0 + polytropic_exponent_ * polytropic_constant_ *\n                pow(vars.rest_mass_density, polytropic_exponent_ - 1.0) /\n                (polytropic_exponent_ - 1.0)}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nBondiMichel::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/,\n    const IntermediateVars<DataType>& /*vars*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nBondiMichel::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/,\n    const IntermediateVars<DataType>& vars) const {\n  // Rezzola and Zanotti (2013) Eq. 11.79\n  const DataType abs_fluid_four_velocity_u_r =\n      mass_accretion_rate_over_four_pi_ /\n      (square(vars.radius) * vars.rest_mass_density);\n  auto lorentz_factor = make_with_value<Scalar<DataType>>(x, 1.0);\n  // Rezzola and Zanotti (2013) Eq. 11.87\n  for (size_t i = 0; i < get_size(lorentz_factor.get()); i++) {\n    double two_m_over_r = 2.0 * mass_ / get_element(vars.radius, i);\n    get_element(lorentz_factor.get(), i) =\n        ((1.0 + two_m_over_r) *\n             square(get_element(abs_fluid_four_velocity_u_r, i)) +\n         1.0) /\n        ((two_m_over_r * get_element(abs_fluid_four_velocity_u_r, i) +\n          sqrt(square(get_element(abs_fluid_four_velocity_u_r, i)) + 1.0 -\n               two_m_over_r)) *\n         (sqrt(1.0 + two_m_over_r)));\n  }\n  return lorentz_factor;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nBondiMichel::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/,\n    const IntermediateVars<DataType>& vars) const {\n  auto result = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  // Rezzola and Zanotti (2013) Eq. 11.79\n  const DataType abs_fluid_four_velocity_u_r =\n      mass_accretion_rate_over_four_pi_ /\n      (square(vars.radius) * vars.rest_mass_density);\n  auto fluid_four_velocity_u_t = make_with_value<DataType>(x, 0.0);\n  for (size_t i = 0; i < get_size(fluid_four_velocity_u_t); i++) {\n    double two_m_over_r = 2.0 * mass_ / get_element(vars.radius, i);\n    get_element(fluid_four_velocity_u_t, i) =\n        ((1.0 + two_m_over_r) *\n             square(get_element(abs_fluid_four_velocity_u_r, i)) +\n         1.0) /\n        (two_m_over_r * get_element(abs_fluid_four_velocity_u_r, i) +\n         sqrt(square(get_element(abs_fluid_four_velocity_u_r, i)) + 1.0 -\n              two_m_over_r));\n  }\n\n  // Rezzola and Zanotti (2013) Eq. 7.22\n  const DataType eulerian_radial_velocity_over_radius =\n      sqrt(1.0 + 2.0 * mass_ / vars.radius) *\n      (-abs_fluid_four_velocity_u_r / fluid_four_velocity_u_t +\n       2.0 * mass_ / (vars.radius + 2.0 * mass_)) /\n      vars.radius;\n  result.get(0) = eulerian_radial_velocity_over_radius * x.get(0);\n  result.get(1) = eulerian_radial_velocity_over_radius * x.get(1);\n  result.get(2) = eulerian_radial_velocity_over_radius * x.get(2);\n  return result;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nBondiMichel::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/,\n    const IntermediateVars<DataType>& vars) const {\n  auto result = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  const DataType mag_field_strength_factor =\n      mag_field_strength_ * square(mass_) /\n      (cube(vars.radius) * sqrt(1.0 + 2.0 * mass_ / vars.radius));\n  result.get(0) = mag_field_strength_factor * x.get(0);\n  result.get(1) = mag_field_strength_factor * x.get(1);\n  result.get(2) = mag_field_strength_factor * x.get(2);\n  return result;\n}\n\nPUP::able::PUP_ID BondiMichel::my_PUP_ID = 0;\n\nbool operator==(const BondiMichel& lhs, const BondiMichel& rhs) {\n  // there is no comparison operator for the EoS, but should be okay as\n  // the `polytropic_exponent`s and `polytropic_constant`s are compared\n  return lhs.mass_ == rhs.mass_ and lhs.sonic_radius_ == rhs.sonic_radius_ and\n         lhs.sonic_density_ == rhs.sonic_density_ and\n         lhs.polytropic_exponent_ == rhs.polytropic_exponent_ and\n         lhs.mag_field_strength_ == rhs.mag_field_strength_ and\n         lhs.sonic_fluid_speed_squared_ == rhs.sonic_fluid_speed_squared_ and\n         lhs.sonic_sound_speed_squared_ == rhs.sonic_sound_speed_squared_ and\n         lhs.polytropic_constant_ == rhs.polytropic_constant_ and\n         lhs.mass_accretion_rate_over_four_pi_ ==\n             rhs.mass_accretion_rate_over_four_pi_ and\n         lhs.bernoulli_constant_squared_minus_one_ ==\n             rhs.bernoulli_constant_squared_minus_one_ and\n         lhs.rest_mass_density_at_infinity_ ==\n             rhs.rest_mass_density_at_infinity_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const BondiMichel& lhs, const BondiMichel& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                                  \\\n  template class BondiMichel::IntermediateVars<DTYPE(data)>;                  \\\n  template tuples::TaggedTuple<hydro::Tags::RestMassDensity<DTYPE(data)>>     \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::RestMassDensity<DTYPE(data)>> /*meta*/,         \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<hydro::Tags::ElectronFraction<DTYPE(data)>>    \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::ElectronFraction<DTYPE(data)>> /*meta*/,        \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DTYPE(data)>>    \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::SpecificEnthalpy<DTYPE(data)>> meta,            \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<hydro::Tags::Pressure<DTYPE(data)>>            \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::Pressure<DTYPE(data)>> /*meta*/,                \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<                                               \\\n      hydro::Tags::SpecificInternalEnergy<DTYPE(data)>>                       \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DTYPE(data)>> /*meta*/,  \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<hydro::Tags::Temperature<DTYPE(data)>>         \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::Temperature<DTYPE(data)>> /*meta*/,             \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<hydro::Tags::LorentzFactor<DTYPE(data)>>       \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::LorentzFactor<DTYPE(data)>> /*meta*/,           \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DTYPE(data), 3>>  \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::SpatialVelocity<DTYPE(data), 3>> /*meta*/,      \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<hydro::Tags::MagneticField<DTYPE(data), 3>>    \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::MagneticField<DTYPE(data), 3>> /*meta*/,        \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;          \\\n  template tuples::TaggedTuple<                                               \\\n      hydro::Tags::DivergenceCleaningField<DTYPE(data)>>                      \\\n  BondiMichel::variables(                                                     \\\n      const tnsr::I<DTYPE(data), 3>& x,                                       \\\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DTYPE(data)>> /*meta*/, \\\n      const BondiMichel::IntermediateVars<DTYPE(data)>& vars) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::Solutions {\n\n/*!\n * \\brief Bondi-Michel accretion \\cite Michel1972 with superposed magnetic field\n * in Schwarzschild spacetime in Cartesian Kerr-Schild coordinates.\n *\n * An analytic solution to the 3-D GRMHD system. The user specifies the sonic\n * radius \\f$r_c\\f$ and sonic rest mass density \\f$\\rho_c\\f$, which are the\n * radius and rest mass density at the sonic point, the radius at which the\n * fluid's Eulerian velocity as seen by a distant observer overtakes the local\n * sound speed \\f$c_{s,c}\\f$. With a specified polytropic exponent \\f$\\gamma\\f$,\n * these quantities can be related to the sound speed at infinity\n * \\f$c_{s,\\infty}\\f$ using the following relations:\n *\n * \\f{align*}\n * c_{s,c}^2 &= \\frac{M}{2r_c - 3M} \\\\\n * c_{s,\\infty}^2 &= \\gamma - 1 + (c_{s,c}^2 - \\gamma + 1)\\sqrt{1 + 3c_{s,c}^2}\n * \\f}\n *\n * In the case of the interstellar medium, the sound\n * speed is \\f$\\approx 10^{-4}\\f$, which results in a sonic radius of\n * \\f$\\approx 10^8 M\\f$ for \\f$\\gamma \\neq 5/3\\f$ \\cite RezzollaBook.\n *\n * The density is found via root-finding, through the\n * Bernoulli equation. As one approaches the sonic radius, a second root makes\n * an appearance and one must take care to bracket the correct root. This is\n * done by using the upper bound\n * \\f$\\frac{\\dot{M}}{4\\pi}\\sqrt{\\frac{2}{Mr^3}}\\f$.\n *\n * Additionally specified by the user are the polytropic exponent \\f$\\gamma\\f$,\n * and the strength parameter of the magnetic field \\f$B_0\\f$.\n * In Cartesian Kerr-Schild coordinates \\f$(x, y, z)\\f$, where\n * \\f$ r = \\sqrt{x^2 + y^2 + z^2}\\f$, the superposed magnetic field is\n * \\cite Etienne2010ui\n *\n * \\f{align*}\n * B^i(\\vec{x},t) = \\frac{B_0 M^2}{r^3 \\sqrt{\\gamma}}x^i\n *  =\\frac{B_0 M^2}{r^3 \\sqrt{1 + 2M/r}}x^i.\n * \\f}\n *\n * The accretion rate is\n *\n * \\f{align*}{\n * \\dot{M}=4\\pi r^2\\rho u^r,\n * \\f}\n *\n * and at the sonic radius\n *\n * \\f{align*}{\n * \\dot{M}_c=\\sqrt{8}\\pi \\sqrt{M}r_c^{3/2}\\rho_c.\n * \\f}\n *\n * The polytropic constant is given by\n *\n * \\f{align*}{\n * K=\\frac{1}{\\gamma\\rho_c^{\\gamma-1}}\n * \\left[\\frac{M(\\gamma-1)}{(2r_c-3M)(\\gamma-1)-M}\\right].\n * \\f}\n *\n * The density as a function of the sound speed is\n *\n * \\f{align*}{\n * \\rho^{\\gamma-1}=\\frac{(\\gamma-1)c_s^2}{\\gamma K(\\gamma-1-c_s^2)}.\n * \\f}\n *\n * ## Horizon quantities, \\f$\\gamma\\ne5/3\\f$\n *\n * The density at the horizon is given by:\n *\n * \\f{align*}{\n *  \\rho_h\\simeq\\frac{1}{16}\n *  \\left(\\frac{5-3\\gamma}{2}\\right)^{(3\\gamma-5)/[2(\\gamma-1)]}\n *  \\frac{\\rho_\\infty}{c_{s,\\infty}^3}.\n * \\f}\n *\n * Using the Lorentz invariance of \\f$b^2\\f$ we evaluate:\n *\n * \\f{align*}{\n * b^2=\\frac{B^2}{W^2}+(B^i v_i)^2=\n *  B^r B^r(1-\\gamma_{rr}v^r v^r)+B^r B^r v^r v^r\n * =B^r B^r = \\frac{B_0^2 M^4}{r^4},\n * \\f}\n *\n * where \\f$r\\f$ is the Cartesian Kerr-Schild radius, which is equal to the\n * areal radius for a non-spinning black hole. At the horizon we get\n *\n * \\f{align*}{\n * b^2_h=\\frac{B^2_0}{16}.\n * \\f}\n *\n * Finally, we get\n *\n * \\f{align*}{\n * B_0 = 4 \\sqrt{b^2_h} = 4\\sqrt{\\rho_h} \\sqrt{\\frac{b^2_h}{\\rho_h}},\n * \\f}\n *\n * where the last equality is useful for comparison to papers that give\n * \\f$b^2_h/\\rho_h\\f$.\n *\n * To help with comparing to other codes the following script can be used to\n * compute \\f$b^2_h/\\rho_h\\f$:\n *\n * \\code{.py}\n * #!/bin/env python\n *\n * import numpy as np\n *\n * # Input parameters\n * B_0 = 18\n * r_c = 8\n * rho_c = 1 / 16\n * gamma = 4 / 3\n * mass = 1\n *\n * K = 1 / (gamma * rho_c**(gamma - 1)) * ((gamma - 1) * mass) / (\n *     (2 * r_c - 3 * mass) * (gamma - 1) - mass)\n * c_s_c = mass / (2 * r_c - 3 * mass)\n * c_inf = gamma - 1 + (c_s_c - gamma + 1) * np.sqrt(1. + 3. * c_s_c)\n * rho_inf = ((gamma - 1) * c_inf / (gamma * K *\n *                                   (gamma - 1 - c_inf)))**(1. / (gamma - 1.))\n * rho_h = 1. / 16. * (2.5 - 1.5 * gamma)**(\n *     (3 * gamma - 5) / (2 * (gamma - 1))) * rho_inf / (c_inf**1.5)\n *\n * print(\"B_0\", B_0)\n * print(\"r_c: \", r_c)\n * print(\"rho_c\", rho_c)\n * print(\"b_h^2/rho_h: \", B_0**2 / (16. * rho_h))\n * print(\"gamma: \", gamma)\n * \\endcode\n *\n * ## Horizon quantities, \\f$\\gamma=5/3\\f$\n *\n * The density at the horizon is given by:\n *\n * \\f{align*}{\n * \\rho_h\\simeq \\frac{1}{16}\\frac{\\rho_\\infty}{u_h c_{s,\\infty}^3},\n * \\f}\n *\n * which gives \\cite RezzollaBook\n *\n * \\f{align*}{\n * \\rho_h\\simeq 0.08\\frac{\\rho_\\infty}{c_{s,\\infty}^3}.\n * \\f}\n *\n * The magnetic field \\f$b^2\\f$ is the same as the \\f$\\gamma\\ne5/3\\f$.\n */\nclass BondiMichel : public virtual evolution::initial_data::InitialData,\n                    public AnalyticSolution,\n                    public MarkAsAnalyticSolution {\n protected:\n  template <typename DataType>\n  struct IntermediateVars;\n\n public:\n  using equation_of_state_type = EquationsOfState::PolytropicFluid<true>;\n\n  /// The mass of the black hole.\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = {\"Mass of the black hole.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The radius at which the fluid becomes supersonic.\n  struct SonicRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Schwarzschild radius where fluid becomes supersonic.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The rest mass density of the fluid at the sonic radius.\n  struct SonicDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The density of the fluid at the sonic radius.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The polytropic exponent for the polytropic fluid.\n  struct PolytropicExponent {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The polytropic exponent for the polytropic fluid.\"};\n    static type lower_bound() { return 1.0; }\n  };\n\n  /// The strength of the radial magnetic field.\n  struct MagFieldStrength {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The strength of the radial magnetic field.\"};\n  };\n\n  using options = tmpl::list<Mass, SonicRadius, SonicDensity,\n                             PolytropicExponent, MagFieldStrength>;\n  static constexpr Options::String help = {\n      \"Bondi-Michel solution with a radial magnetic field using \\n\"\n      \"the Schwarzschild coordinate system. Quantities prefixed with \\n\"\n      \"`sonic` refer to field quantities evaluated at the radius \\n\"\n      \"where the fluid speed overtakes the sound speed.\"};\n\n  BondiMichel() = default;\n  BondiMichel(const BondiMichel& /*rhs*/) = default;\n  BondiMichel& operator=(const BondiMichel& /*rhs*/) = default;\n  BondiMichel(BondiMichel&& /*rhs*/) = default;\n  BondiMichel& operator=(BondiMichel&& /*rhs*/) = default;\n  ~BondiMichel() override = default;\n\n  BondiMichel(double mass, double sonic_radius, double sonic_density,\n              double polytropic_exponent, double mag_field_strength);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit BondiMichel(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BondiMichel);\n  /// \\endcond\n\n  /// Retrieve a collection of  hydro variables at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         const double /*t*/,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    // non-const so we can move out the metric vars. We assume that no variable\n    // is being retrieved more than once, which would cause problems with\n    // TaggedTuple anyway.\n    auto intermediate_vars = IntermediateVars<DataType>{\n        rest_mass_density_at_infinity_,\n        mass_accretion_rate_over_four_pi_,\n        mass_,\n        polytropic_constant_,\n        polytropic_exponent_,\n        bernoulli_constant_squared_minus_one_,\n        sonic_radius_,\n        sonic_density_,\n        x,\n        tmpl2::flat_any_v<\n            not tmpl::list_contains_v<hydro::grmhd_tags<DataType>, Tags>...>,\n        background_spacetime_};\n    return {get<Tags>(variables(x, tmpl::list<Tags>{}, intermediate_vars))...};\n  }\n\n  template <typename DataType, typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     const double /*t*/,  // NOLINT\n                                     tmpl::list<Tag> /*meta*/) const {\n    return variables(\n        x, tmpl::list<Tag>{},\n        IntermediateVars<DataType>{\n            rest_mass_density_at_infinity_, mass_accretion_rate_over_four_pi_,\n            mass_, polytropic_constant_, polytropic_exponent_,\n            bernoulli_constant_squared_minus_one_, sonic_radius_,\n            sonic_density_, x,\n            not tmpl::list_contains_v<hydro::grmhd_tags<DataType>, Tag>,\n            background_spacetime_});\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n  const EquationsOfState::PolytropicFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n protected:\n  friend bool operator==(const BondiMichel& lhs, const BondiMichel& rhs);\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n                 const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/,\n                 const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n      const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n                 const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/,\n                 const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/,\n                 const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/,\n                 const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/,\n      const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/,\n                 const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/,\n                 const IntermediateVars<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType, typename Tag,\n            Requires<not tmpl::list_contains_v<\n                tmpl::push_back<hydro::grmhd_tags<DataType>,\n                                hydro::Tags::SpecificEnthalpy<DataType>>,\n                Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(\n      const tnsr::I<DataType, 3>& /*x*/, tmpl::list<Tag> /*meta*/,\n      const IntermediateVars<DataType>& vars) const {\n    return {std::move(get<Tag>(vars.kerr_schild_soln))};\n  }\n\n  template <typename DataType>\n  struct IntermediateVars {\n    IntermediateVars(double rest_mass_density_at_infinity,\n                     double in_mass_accretion_rate_over_four_pi, double in_mass,\n                     double in_polytropic_constant,\n                     double in_polytropic_exponent,\n                     double in_bernoulli_constant_squared_minus_one,\n                     double in_sonic_radius, double in_sonic_density,\n                     const tnsr::I<DataType, 3>& x, bool need_spacetime,\n                     const gr::Solutions::KerrSchild& background_spacetime);\n    DataType radius{};\n    DataType rest_mass_density{};\n    DataType electron_fraction{};\n    double mass_accretion_rate_over_four_pi{};\n    double mass{};\n    double polytropic_constant{};\n    double polytropic_exponent{};\n    double bernoulli_constant_squared_minus_one{};\n    double sonic_radius{};\n    double sonic_density{};\n    double bernoulli_root_function(double rest_mass_density_guess,\n                                   double current_radius) const;\n    tuples::tagged_tuple_from_typelist<\n        typename gr::Solutions::KerrSchild::tags<DataType>>\n        kerr_schild_soln{};\n  };\n\n  double mass_ = std::numeric_limits<double>::signaling_NaN();\n  double sonic_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double sonic_density_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent_ = std::numeric_limits<double>::signaling_NaN();\n  double mag_field_strength_ = std::numeric_limits<double>::signaling_NaN();\n  double sonic_fluid_speed_squared_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double sonic_sound_speed_squared_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double polytropic_constant_ = std::numeric_limits<double>::signaling_NaN();\n  double mass_accretion_rate_over_four_pi_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double bernoulli_constant_squared_minus_one_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double rest_mass_density_at_infinity_ =\n      std::numeric_limits<double>::signaling_NaN();\n  EquationsOfState::PolytropicFluid<true> equation_of_state_{};\n  gr::Solutions::KerrSchild background_spacetime_{};\n};\n\nbool operator!=(const BondiMichel& lhs, const BondiMichel& rhs);\n\n}  // namespace grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GrMhdSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AlfvenWave.cpp\n  BondiMichel.cpp\n  KomissarovShock.cpp\n  SmoothFlow.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AlfvenWave.hpp\n  BondiMichel.hpp\n  KomissarovShock.hpp\n  SmoothFlow.hpp\n  Solutions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  Hydro\n  Options\n  RelativisticEulerSolutions\n  Serialization\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.hpp\"\n\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nScalar<DataType> compute_piecewise(const tnsr::I<DataType, 3>& x,\n                                   const double shock_position,\n                                   const double left_value,\n                                   const double right_value) {\n  return Scalar<DataType>(left_value -\n                          (left_value - right_value) *\n                              step_function(get<0>(x) - shock_position));\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> compute_piecewise_vector(\n    const tnsr::I<DataType, 3>& x, const double shock_position,\n    const std::array<double, 3>& left_value,\n    const std::array<double, 3>& right_value) {\n  return tnsr::I<DataType, 3>{\n      {{left_value[0] - (left_value[0] - right_value[0]) *\n                            step_function(get<0>(x) - shock_position),\n        left_value[1] - (left_value[1] - right_value[1]) *\n                            step_function(get<0>(x) - shock_position),\n        left_value[2] - (left_value[2] - right_value[2]) *\n                            step_function(get<0>(x) - shock_position)}}};\n}\n}  // namespace\n\nnamespace grmhd::Solutions {\n\nKomissarovShock::KomissarovShock(\n    const double adiabatic_index, const double left_rest_mass_density,\n    const double right_rest_mass_density, const double left_electron_fraction,\n    const double right_electron_fraction, const double left_pressure,\n    const double right_pressure,\n    const std::array<double, 3>& left_spatial_velocity,\n    const std::array<double, 3>& right_spatial_velocity,\n    const std::array<double, 3>& left_magnetic_field,\n    const std::array<double, 3>& right_magnetic_field, const double shock_speed)\n    : equation_of_state_(adiabatic_index),\n      adiabatic_index_(adiabatic_index),\n      left_rest_mass_density_(left_rest_mass_density),\n      right_rest_mass_density_(right_rest_mass_density),\n      left_electron_fraction_(left_electron_fraction),\n      right_electron_fraction_(right_electron_fraction),\n      left_pressure_(left_pressure),\n      right_pressure_(right_pressure),\n      left_spatial_velocity_(left_spatial_velocity),\n      right_spatial_velocity_(right_spatial_velocity),\n      left_magnetic_field_(left_magnetic_field),\n      right_magnetic_field_(right_magnetic_field),\n      shock_speed_(shock_speed) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nKomissarovShock::get_clone() const {\n  return std::make_unique<KomissarovShock>(*this);\n}\n\nKomissarovShock::KomissarovShock(CkMigrateMessage* msg) : InitialData(msg) {}\n\nvoid KomissarovShock::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | adiabatic_index_;\n  p | left_rest_mass_density_;\n  p | right_rest_mass_density_;\n  p | left_electron_fraction_;\n  p | right_electron_fraction_;\n  p | left_pressure_;\n  p | right_pressure_;\n  p | left_spatial_velocity_;\n  p | right_spatial_velocity_;\n  p | left_magnetic_field_;\n  p | right_magnetic_field_;\n  p | shock_speed_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nKomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double t,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  return compute_piecewise(x, t * shock_speed_, left_rest_mass_density_,\n                           right_rest_mass_density_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nKomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double t,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n  return compute_piecewise(x, t * shock_speed_, left_electron_fraction_,\n                           right_electron_fraction_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nKomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double t,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  return compute_piecewise_vector(x, t * shock_speed_, left_spatial_velocity_,\n                                  right_spatial_velocity_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nKomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double t,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(variables(\n          x, t, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x, t, tmpl::list<hydro::Tags::Pressure<DataType>>{})));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> KomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double t,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return compute_piecewise(x, t * shock_speed_, left_pressure_,\n                           right_pressure_);\n  ;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nKomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double t,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  return compute_piecewise_vector(x, t * shock_speed_, left_magnetic_field_,\n                                  right_magnetic_field_);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nKomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double /*t*/,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nKomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double t,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  const auto spatial_velocity = get<hydro::Tags::SpatialVelocity<DataType, 3>>(\n      variables(x, t, tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>>{}));\n  return {\n      hydro::lorentz_factor(dot_product(spatial_velocity, spatial_velocity))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nKomissarovShock::variables(\n    const tnsr::I<DataType, 3>& x, const double t,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  using density_tag = hydro::Tags::RestMassDensity<DataType>;\n  using energy_tag = hydro::Tags::SpecificInternalEnergy<DataType>;\n  using pressure_tag = hydro::Tags::Pressure<DataType>;\n  const auto data =\n      variables(x, t, tmpl::list<density_tag, energy_tag, pressure_tag>{});\n  return hydro::relativistic_specific_enthalpy(\n      get<density_tag>(data), get<energy_tag>(data), get<pressure_tag>(data));\n}\n\nPUP::able::PUP_ID KomissarovShock::my_PUP_ID = 0;\n\nbool operator==(const KomissarovShock& lhs, const KomissarovShock& rhs) {\n  return lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.left_rest_mass_density_ == rhs.left_rest_mass_density_ and\n         lhs.right_rest_mass_density_ == rhs.right_rest_mass_density_ and\n         lhs.left_electron_fraction_ == rhs.left_electron_fraction_ and\n         lhs.right_electron_fraction_ == rhs.right_electron_fraction_ and\n         lhs.left_pressure_ == rhs.left_pressure_ and\n         lhs.right_pressure_ == rhs.right_pressure_ and\n         lhs.left_spatial_velocity_ == rhs.left_spatial_velocity_ and\n         lhs.right_spatial_velocity_ == rhs.right_spatial_velocity_ and\n         lhs.left_magnetic_field_ == rhs.left_magnetic_field_ and\n         lhs.right_magnetic_field_ == rhs.right_magnetic_field_ and\n         lhs.shock_speed_ == rhs.shock_speed_;\n}\n\nbool operator!=(const KomissarovShock& lhs, const KomissarovShock& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                     \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>                 \\\n      KomissarovShock::variables(const tnsr::I<DTYPE(data), 3>&, double, \\\n                                 tmpl::list<TAG(data) < DTYPE(data)>>) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::DivergenceCleaningField, hydro::Tags::LorentzFactor,\n     hydro::Tags::SpecificEnthalpy))\n\n#define INSTANTIATE_VECTORS(_, data)                                      \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3>>               \\\n      KomissarovShock::variables(const tnsr::I<DTYPE(data), 3>&, double,  \\\n                                 tmpl::list<TAG(data) < DTYPE(data), 3>>) \\\n          const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace grmhd::Solutions {\n\n/*!\n * \\brief A one-dimensional shock solution for an ideal fluid in Minkowski\n * spacetime\n *\n * This solution consists of a left state for \\f$x<0\\f$ and a right state for\n * \\f$x\\ge 0\\f$, each with constant fluid variables. The interface between these\n * states moves with the shock speed \\f$\\mu\\f$ as described in\n * \\cite Komissarov1999.\n *\n * \\note We do not currently support 1D RMHD, so this class provides a 3D\n * solution with \\f$x\\f$-dependence only. Therefore the computational domain can\n * be represented by a single element with periodic boundary conditions in the\n * \\f$y\\f$ and \\f$z\\f$ directions.\n */\nclass KomissarovShock\n    : public evolution::initial_data::InitialData,\n      public AnalyticSolution,\n      public hydro::TemperatureInitialization<KomissarovShock>,\n      public MarkAsAnalyticSolution {\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<true>;\n\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the ideal fluid\"};\n    static type lower_bound() { return 1.0; }\n  };\n  struct LeftRestMassDensity {\n    using type = double;\n    static std::string name() { return \"LeftDensity\"; };\n    static constexpr Options::String help = {\n        \"Fluid rest mass density in the left half-domain\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct RightRestMassDensity {\n    using type = double;\n    static std::string name() { return \"RightDensity\"; };\n    static constexpr Options::String help = {\n        \"Fluid rest mass density in the right half-domain\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct LeftElectronFraction {\n    using type = double;\n    static std::string name() { return \"LeftElectronFraction\"; };\n    static constexpr Options::String help = {\n        \"Fluid electron fraction in the left half-domain\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n  struct RightElectronFraction {\n    using type = double;\n    static std::string name() { return \"RightElectronFraction\"; };\n    static constexpr Options::String help = {\n        \"Fluid electron fraction in the right half-domain\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n  struct LeftPressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fluid pressure in the left half-domain\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct RightPressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fluid pressure in the right half-domain\"};\n    static type lower_bound() { return 0.0; }\n  };\n  struct LeftSpatialVelocity {\n    using type = std::array<double, 3>;\n    static std::string name() { return \"LeftVelocity\"; };\n    static constexpr Options::String help = {\n        \"Fluid spatial velocity in the left half-domain\"};\n  };\n  struct RightSpatialVelocity {\n    using type = std::array<double, 3>;\n    static std::string name() { return \"RightVelocity\"; };\n    static constexpr Options::String help = {\n        \"Fluid spatial velocity in the right half-domain\"};\n  };\n  struct LeftMagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Magnetic field in the left half-domain\"};\n  };\n  struct RightMagneticField {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Magnetic field in the right half-domain\"};\n  };\n  struct ShockSpeed {\n    using type = double;\n    static constexpr Options::String help = {\"Propagation speed of the shock\"};\n  };\n\n  using options =\n      tmpl::list<AdiabaticIndex, LeftRestMassDensity, RightRestMassDensity,\n                 LeftElectronFraction, RightElectronFraction, LeftPressure,\n                 RightPressure, LeftSpatialVelocity, RightSpatialVelocity,\n                 LeftMagneticField, RightMagneticField, ShockSpeed>;\n\n  static constexpr Options::String help = {\n      \"Analytic initial data for a Komissarov shock test. The fluid variables \"\n      \"are set homogeneously on either half of the domain left and right of \"\n      \"x=0.\"};\n\n  KomissarovShock() = default;\n  KomissarovShock(const KomissarovShock& /*rhs*/) = default;\n  KomissarovShock& operator=(const KomissarovShock& /*rhs*/) = default;\n  KomissarovShock(KomissarovShock&& /*rhs*/) = default;\n  KomissarovShock& operator=(KomissarovShock&& /*rhs*/) = default;\n  ~KomissarovShock() override = default;\n\n  KomissarovShock(double adiabatic_index, double left_rest_mass_density,\n                  double right_rest_mass_density, double left_electron_fraction,\n                  double right_electron_fraction, double left_pressure,\n                  double right_pressure,\n                  const std::array<double, 3>& left_spatial_velocity,\n                  const std::array<double, 3>& right_spatial_velocity,\n                  const std::array<double, 3>& left_magnetic_field,\n                  const std::array<double, 3>& right_magnetic_field,\n                  double shock_speed);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit KomissarovShock(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(KomissarovShock);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve the GRMHD variables at a given position.\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x, double t,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x, double t,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double t,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return TemperatureInitialization::variables(\n        x, t, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n  /// @}\n\n  /// Retrieve a collection of hydro variables at `(x, t)`\n  template <typename DataType, typename Tag1, typename Tag2, typename... Tags>\n  tuples::TaggedTuple<Tag1, Tag2, Tags...> variables(\n      const tnsr::I<DataType, 3>& x, double t,\n      tmpl::list<Tag1, Tag2, Tags...> /*meta*/) const {\n    return {tuples::get<Tag1>(variables(x, t, tmpl::list<Tag1>{})),\n            tuples::get<Tag2>(variables(x, t, tmpl::list<Tag2>{})),\n            tuples::get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<tmpl::list_contains_v<\n                gr::analytic_solution_tags<3, DataType>, Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x, double t,\n                                     tmpl::list<Tag> /*meta*/) const {\n    return background_spacetime_.variables(x, t, tmpl::list<Tag>{});\n  }\n\n  const EquationsOfState::IdealFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n protected:\n  EquationsOfState::IdealFluid<true> equation_of_state_{};\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  double left_rest_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double right_rest_mass_density_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double left_electron_fraction_ = std::numeric_limits<double>::signaling_NaN();\n  double right_electron_fraction_ =\n      std::numeric_limits<double>::signaling_NaN();\n  double left_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double right_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> left_spatial_velocity_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 3> right_spatial_velocity_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 3> left_magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 3> right_magnetic_field_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  double shock_speed_ = std::numeric_limits<double>::signaling_NaN();\n\n  friend bool operator==(const KomissarovShock& lhs,\n                         const KomissarovShock& rhs);\n\n  friend bool operator!=(const KomissarovShock& lhs,\n                         const KomissarovShock& rhs);\n};\n\n}  // namespace grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace grmhd::Solutions {\n\nSmoothFlow::SmoothFlow(const std::array<double, 3>& mean_velocity,\n                       const std::array<double, 3>& wavevector,\n                       const double pressure, const double adiabatic_index,\n                       const double perturbation_size)\n    : RelativisticEuler::Solutions::SmoothFlow<3>(mean_velocity, wavevector,\n                                                  pressure, adiabatic_index,\n                                                  perturbation_size) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> SmoothFlow::get_clone()\n    const {\n  return std::make_unique<SmoothFlow>(*this);\n}\n\nSmoothFlow::SmoothFlow(CkMigrateMessage* msg)\n    : RelativisticEuler::Solutions::SmoothFlow<3>(msg) {}\n\nvoid SmoothFlow::pup(PUP::er& p) {\n  RelativisticEuler::Solutions::SmoothFlow<3>::pup(p);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nSmoothFlow::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  return {make_with_value<tnsr::I<DataType, 3>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nSmoothFlow::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\nPUP::able::PUP_ID SmoothFlow::my_PUP_ID = 0;\n\nbool operator==(const SmoothFlow& lhs, const SmoothFlow& rhs) {\n  using smooth_flow = RelativisticEuler::Solutions::SmoothFlow<3>;\n  return *static_cast<const smooth_flow*>(&lhs) ==\n         *static_cast<const smooth_flow*>(&rhs);\n}\n\nbool operator!=(const SmoothFlow& lhs, const SmoothFlow& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)> >                   \\\n      SmoothFlow::variables(const tnsr::I<DTYPE(data), 3>& x, double t,     \\\n                            tmpl::list<TAG(data) < DTYPE(data)> > /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (double, DataVector),\n                        (hydro::Tags::DivergenceCleaningField))\n\n#define INSTANTIATE_VECTORS(_, data)                                           \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3> >                   \\\n      SmoothFlow::variables(const tnsr::I<DTYPE(data), 3>& x, double t,        \\\n                            tmpl::list<TAG(data) < DTYPE(data), 3> > /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::MagneticField))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/Solutions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::Solutions {\n\n/*!\n * \\brief Periodic GrMhd solution in Minkowski spacetime.\n *\n * An analytic solution to the 3-D GrMhd system. The user specifies the mean\n * flow velocity of the fluid, the wavevector of the density profile, and the\n * amplitude \\f$A\\f$ of the density profile. The magnetic field is taken to be\n * zero everywhere. In Cartesian coordinates \\f$(x, y, z)\\f$, and using\n * dimensionless units, the primitive quantities at a given time \\f$t\\f$ are\n * then\n *\n * \\f{align*}\n * \\rho(\\vec{x},t) &= 1 + A \\sin(\\vec{k}\\cdot(\\vec{x} - \\vec{v}t)) \\\\\n * \\vec{v}(\\vec{x},t) &= [v_x, v_y, v_z]^{T},\\\\\n * P(\\vec{x},t) &= P, \\\\\n * \\epsilon(\\vec{x}, t) &= \\frac{P}{(\\gamma - 1)\\rho}\\\\\n * \\vec{B}(\\vec{x},t) &= [0, 0, 0]^{T}\n * \\f}\n */\nclass SmoothFlow : virtual public MarkAsAnalyticSolution,\n                   public RelativisticEuler::Solutions::SmoothFlow<3> {\n  using smooth_flow = RelativisticEuler::Solutions::SmoothFlow<3>;\n\n public:\n  using options = smooth_flow::options;\n\n  static constexpr Options::String help = {\n      \"Periodic smooth flow in Minkowski spacetime with zero magnetic field.\"};\n\n  SmoothFlow() = default;\n  SmoothFlow(const SmoothFlow& /*rhs*/) = default;\n  SmoothFlow& operator=(const SmoothFlow& /*rhs*/) = default;\n  SmoothFlow(SmoothFlow&& /*rhs*/) = default;\n  SmoothFlow& operator=(SmoothFlow&& /*rhs*/) = default;\n  ~SmoothFlow() override = default;\n\n  SmoothFlow(const std::array<double, 3>& mean_velocity,\n             const std::array<double, 3>& wavevector, double pressure,\n             double adiabatic_index, double perturbation_size);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit SmoothFlow(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SmoothFlow);\n  /// \\endcond\n\n  using smooth_flow::equation_of_state;\n  using smooth_flow::equation_of_state_type;\n\n  // Overload the variables function from the base class.\n  using smooth_flow::variables;\n\n  /// @{\n  /// Retrieve hydro variable at `(x, t)`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x, double /*t*/,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n  /// @}\n\n  /// Retrieve a collection of hydro variables at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n protected:\n  friend bool operator==(const SmoothFlow& lhs, const SmoothFlow& rhs);\n};\n\nbool operator!=(const SmoothFlow& lhs, const SmoothFlow& rhs);\n}  // namespace grmhd::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/GrMhd/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n\nnamespace grmhd {\n/// Base struct for properties common to all GRMHD analytic solutions\nstruct AnalyticSolution {\n  static constexpr size_t volume_dim = 3_st;\n\n  template <typename DataType>\n  using tags =\n      tmpl::push_back<typename gr::AnalyticSolution<3>::template tags<DataType>,\n                      hydro::Tags::RestMassDensity<DataType>,\n                      hydro::Tags::ElectronFraction<DataType>,\n                      hydro::Tags::SpecificInternalEnergy<DataType>,\n                      hydro::Tags::Temperature<DataType>,\n                      hydro::Tags::Pressure<DataType>,\n                      hydro::Tags::SpatialVelocity<DataType, 3>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>,\n                      hydro::Tags::LorentzFactor<DataType>,\n                      hydro::Tags::SpecificEnthalpy<DataType>>;\n};\n\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing a solution to the GrMhd system.\n */\nnamespace Solutions {}\n}  // namespace grmhd\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Hydro/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY HydroSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  SmoothFlow.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  SmoothFlow.hpp\n  Solutions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  Hydro\n  Options\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Hydro/SmoothFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Hydro/SmoothFlow.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <numeric>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace hydro::Solutions {\n\ntemplate <size_t Dim, bool IsRelativistic>\nSmoothFlow<Dim, IsRelativistic>::SmoothFlow(\n    const std::array<double, Dim>& mean_velocity,\n    const std::array<double, Dim>& wavevector, const double pressure,\n    const double adiabatic_index, const double perturbation_size)\n    : mean_velocity_(mean_velocity),\n      wavevector_(wavevector),\n      pressure_(pressure),\n      adiabatic_index_(adiabatic_index),\n      perturbation_size_(perturbation_size),\n      k_dot_v_(std::inner_product(mean_velocity_.begin(), mean_velocity_.end(),\n                                  wavevector_.begin(), 0.0)),\n      equation_of_state_{adiabatic_index_} {}\n\ntemplate <size_t Dim, bool IsRelativistic>\nSmoothFlow<Dim, IsRelativistic>::SmoothFlow(CkMigrateMessage* /*unused*/) {}\n\ntemplate <size_t Dim, bool IsRelativistic>\nvoid SmoothFlow<Dim, IsRelativistic>::pup(PUP::er& p) {\n  p | mean_velocity_;\n  p | wavevector_;\n  p | pressure_;\n  p | adiabatic_index_;\n  p | perturbation_size_;\n  p | k_dot_v_;\n  p | equation_of_state_;\n}\n\ntemplate <size_t Dim, bool IsRelativistic>\ntemplate <typename DataType>\nDataType SmoothFlow<Dim, IsRelativistic>::k_dot_x_minus_vt(\n    const tnsr::I<DataType, Dim>& x, const double t) const {\n  auto result = make_with_value<DataType>(x, -k_dot_v_ * t);\n  for (size_t i = 0; i < Dim; i++) {\n    result += gsl::at(wavevector_, i) * x.get(i);\n  }\n  return result;\n}\n\n// Primitive variables.\ntemplate <size_t Dim, bool IsRelativistic>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nSmoothFlow<Dim, IsRelativistic>::variables(\n    const tnsr::I<DataType, Dim>& x, double t,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  const DataType phase = k_dot_x_minus_vt(x, t);\n  return {Scalar<DataType>{DataType{1.0 + perturbation_size_ * sin(phase)}}};\n}\n\ntemplate <size_t Dim, bool IsRelativistic>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nSmoothFlow<Dim, IsRelativistic>::variables(\n    const tnsr::I<DataType, Dim>& x, double t,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  const DataType phase = k_dot_x_minus_vt(x, t);\n  return {\n      Scalar<DataType>{pressure_ / ((adiabatic_index_ - 1.0) *\n                                    (1.0 + perturbation_size_ * sin(phase)))}};\n}\n\ntemplate <size_t Dim, bool IsRelativistic>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nSmoothFlow<Dim, IsRelativistic>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, pressure_)};\n}\n\ntemplate <size_t Dim, bool IsRelativistic>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, Dim>>\nSmoothFlow<Dim, IsRelativistic>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim>> /*meta*/) const {\n  auto result = make_with_value<tnsr::I<DataType, Dim>>(x, 0.0);\n  for (size_t i = 0; i < Dim; ++i) {\n    result.get(i) = gsl::at(mean_velocity_, i);\n  }\n  return {std::move(result)};\n}\n\ntemplate <size_t Dim, bool IsRelativistic>\ntemplate <typename DataType, bool LocalIsRelativistic,\n          Requires<IsRelativistic and IsRelativistic == LocalIsRelativistic>>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nSmoothFlow<Dim, IsRelativistic>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(\n      x,\n      1.0 / sqrt(1.0 - alg::accumulate(\n                           mean_velocity_, 0.0,\n                           funcl::Plus<funcl::Identity, funcl::Square<>>{})))};\n}\n\ntemplate <size_t Dim, bool IsRelativistic>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nSmoothFlow<Dim, IsRelativistic>::variables(\n    const tnsr::I<DataType, Dim>& x, double t,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  Scalar<DataType> specific_internal_energy = std::move(\n      get<hydro::Tags::SpecificInternalEnergy<DataType>>(variables<DataType>(\n          x, t, tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})));\n  get(specific_internal_energy) *= adiabatic_index_;\n  if constexpr (IsRelativistic) {\n    get(specific_internal_energy) += 1.0;\n  }\n  return {std::move(specific_internal_energy)};\n}\n\ntemplate <size_t Dim, bool IsRelativistic>\nbool operator==(const SmoothFlow<Dim, IsRelativistic>& lhs,\n                const SmoothFlow<Dim, IsRelativistic>& rhs) {\n  // there is no comparison operator for the EoS, but should be okay as\n  // the adiabatic_indexs are compared\n  return lhs.mean_velocity_ == rhs.mean_velocity_ and\n         lhs.wavevector_ == rhs.wavevector_ and\n         lhs.pressure_ == rhs.pressure_ and\n         lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.perturbation_size_ == rhs.perturbation_size_ and\n         lhs.k_dot_v_ == rhs.k_dot_v_;\n}\n\ntemplate <size_t Dim, bool IsRelativistic>\nbool operator!=(const SmoothFlow<Dim, IsRelativistic>& lhs,\n                const SmoothFlow<Dim, IsRelativistic>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define IS_RELATIVISTIC(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE_CLASS(_, data)                             \\\n  template class SmoothFlow<DIM(data), IS_RELATIVISTIC(data)>; \\\n  template bool operator==(                                    \\\n      const SmoothFlow<DIM(data), IS_RELATIVISTIC(data)>&,     \\\n      const SmoothFlow<DIM(data), IS_RELATIVISTIC(data)>&);    \\\n  template bool operator!=(                                    \\\n      const SmoothFlow<DIM(data), IS_RELATIVISTIC(data)>&,     \\\n      const SmoothFlow<DIM(data), IS_RELATIVISTIC(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_CLASS, (1, 2, 3), (true, false))\n\n#define INSTANTIATE_SCALARS(_, data)                           \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>       \\\n      SmoothFlow<DIM(data), IS_RELATIVISTIC(data)>::variables( \\\n          const tnsr::I<DTYPE(data), DIM(data)>& x, double t,  \\\n          tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (1, 2, 3), (true, false),\n                        (double, DataVector),\n                        (hydro::Tags::RestMassDensity,\n                         hydro::Tags::SpecificInternalEnergy,\n                         hydro::Tags::Pressure, hydro::Tags::SpecificEnthalpy))\n\n#define INSTANTIATE_LORENTZ_FACTOR(_, data)                             \\\n  template tuples::TaggedTuple<hydro::Tags::LorentzFactor<DTYPE(data)>> \\\n  SmoothFlow<DIM(data), IS_RELATIVISTIC(data)>::variables(              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double t,               \\\n      tmpl::list<hydro::Tags::LorentzFactor<DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_LORENTZ_FACTOR, (1, 2, 3), (true),\n                        (double, DataVector))\n\n#define INSTANTIATE_VECTORS(_, data)                               \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), DIM(data), \\\n                               Frame::Inertial>>                   \\\n      SmoothFlow<DIM(data), IS_RELATIVISTIC(data)>::variables(     \\\n          const tnsr::I<DTYPE(data), DIM(data)>& x, double t,      \\\n          tmpl::list<TAG(data) < DTYPE(data), DIM(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (1, 2, 3), (true, false),\n                        (double, DataVector), (hydro::Tags::SpatialVelocity))\n\n#undef DIM\n#undef IS_RELATIVISTIC\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_CLASS\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_LORENTZ_FACTOR\n#undef INSTANTIATE_VECTORS\n}  // namespace hydro::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Hydro/SmoothFlow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace hydro::Solutions {\n/*!\n * \\brief Smooth sinusoidal density wave.\n *\n * This is the generic infrastructure for a smooth flow solution that can be\n * used by the hydro systems to avoid code duplication. The solution has a\n * constant pressure and uniform spatial velocity provided that the rest mass\n * density satisfies the advection equation\n *\n * \\f{align*}{\n * \\partial_t\\rho + v^i\\partial_i\\rho = 0,\n * \\f}\n *\n * and the specific internal energy is a function of the rest mass density only,\n * \\f$\\epsilon = \\epsilon(\\rho)\\f$. For testing purposes, this class implements\n * this solution for the case where \\f$\\rho\\f$ is a sine wave. The user\n * specifies the mean flow velocity of the fluid, the wavevector of the density\n * profile, and the amplitude \\f$A\\f$ of the density profile. In Cartesian\n * coordinates \\f$(x, y, z)\\f$, and using dimensionless units, the primitive\n * variables at a given time \\f$t\\f$ are then\n *\n * \\f{align*}{\n * \\rho(\\vec{x},t) &= 1 + A \\sin(\\vec{k}\\cdot(\\vec{x} - \\vec{v}t)) \\\\\n * \\vec{v}(\\vec{x},t) &= [v_x, v_y, v_z]^{T},\\\\\n * P(\\vec{x},t) &= P, \\\\\n * \\epsilon(\\vec{x}, t) &= \\frac{P}{(\\gamma - 1)\\rho}\\\\\n * \\f}\n *\n * where we have assumed \\f$\\epsilon\\f$ and \\f$\\rho\\f$ to be related through an\n * equation mathematically equivalent to the equation of state of an ideal gas,\n * where the pressure is held constant.\n */\ntemplate <size_t Dim, bool IsRelativistic>\nclass SmoothFlow : virtual public MarkAsAnalyticSolution {\n public:\n  SmoothFlow() = default;\n  SmoothFlow(const SmoothFlow& /*rhs*/) = default;\n  SmoothFlow& operator=(const SmoothFlow& /*rhs*/) = default;\n  SmoothFlow(SmoothFlow&& /*rhs*/) = default;\n  SmoothFlow& operator=(SmoothFlow&& /*rhs*/) = default;\n  ~SmoothFlow() = default;\n\n  explicit SmoothFlow(CkMigrateMessage* /*unused*/);\n\n  // clang-tidy: no runtime references\n  void pup(PUP::er& /*p*/);  //  NOLINT\n\n protected:\n  using equation_of_state_type = EquationsOfState::IdealFluid<IsRelativistic>;\n\n  /// The mean flow velocity.\n  struct MeanVelocity {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\"The mean flow velocity.\"};\n  };\n\n  /// The wave vector of the profile.\n  struct WaveVector {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\"The wave vector of the profile.\"};\n  };\n\n  /// The constant pressure throughout the fluid.\n  struct Pressure {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The constant pressure throughout the fluid.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  /// The adiabatic index for the ideal fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index for the ideal fluid.\"};\n    static type lower_bound() { return 1.0; }\n  };\n\n  /// The perturbation amplitude of the rest mass density of the fluid.\n  struct PerturbationSize {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The perturbation size of the rest mass density.\"};\n    static type lower_bound() { return -1.0; }\n    static type upper_bound() { return 1.0; }\n  };\n\n  using options = tmpl::list<MeanVelocity, WaveVector, Pressure, AdiabaticIndex,\n                             PerturbationSize>;\n\n  SmoothFlow(const std::array<double, Dim>& mean_velocity,\n             const std::array<double, Dim>& wavevector, double pressure,\n             double adiabatic_index, double perturbation_size);\n\n  /// @{\n  /// Retrieve hydro variable at `(x, t)`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim>& x, double t,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, Dim>& x, double t,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, Dim>& x, double /*t*/,\n      tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, Dim>>;\n\n  template <typename DataType, bool LocalIsRelativistic = IsRelativistic,\n            Requires<IsRelativistic and IsRelativistic == LocalIsRelativistic> =\n                nullptr>\n  auto variables(const tnsr::I<DataType, Dim>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim>& x, double t,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n  /// @}\n\n  const EquationsOfState::IdealFluid<IsRelativistic>& equation_of_state()\n      const {\n    return equation_of_state_;\n  }\n\n private:\n  template <size_t LocalDim, bool LocalIsRelativistic>\n  friend bool\n  operator==(  // NOLINT (clang-tidy: readability-redundant-declaration)\n      const SmoothFlow<LocalDim, LocalIsRelativistic>& lhs,\n      const SmoothFlow<LocalDim, LocalIsRelativistic>& rhs);\n\n  // Computes the phase.\n  template <typename DataType>\n  DataType k_dot_x_minus_vt(const tnsr::I<DataType, Dim>& x, double t) const;\n\n  std::array<double, Dim> mean_velocity_ =\n      make_array<Dim>(std::numeric_limits<double>::signaling_NaN());\n  std::array<double, Dim> wavevector_ =\n      make_array<Dim>(std::numeric_limits<double>::signaling_NaN());\n  double pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  double perturbation_size_ = std::numeric_limits<double>::signaling_NaN();\n  // The angular frequency.\n  double k_dot_v_ = std::numeric_limits<double>::signaling_NaN();\n  EquationsOfState::IdealFluid<IsRelativistic> equation_of_state_{};\n};\n\ntemplate <size_t Dim, bool IsRelativistic>\nbool operator!=(const SmoothFlow<Dim, IsRelativistic>& lhs,\n                const SmoothFlow<Dim, IsRelativistic>& rhs);\n}  // namespace hydro::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Hydro/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace hydro {\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing common portions of solutions to various\n * different (magneto)hydrodynamical systems.\n *\n * For example, an advecting sinusoidal density profile is a solution to\n * Newtonian Euler, relativistic Euler, and GRMHD, so the common parts of the\n * solution is implemented here.\n */\nnamespace Solutions {}\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY NewtonianEulerSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  IsentropicVortex.cpp\n  LaneEmdenStar.cpp\n  RiemannProblem.cpp\n  SmoothFlow.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  IsentropicVortex.hpp\n  LaneEmdenStar.hpp\n  RiemannProblem.hpp\n  SmoothFlow.hpp\n  Solutions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Hydro\n  HydroSolutions\n  Options\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/IsentropicVortex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/IsentropicVortex.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace NewtonianEuler::Solutions {\n\ntemplate <size_t Dim>\nIsentropicVortex<Dim>::IsentropicVortex(\n    const double adiabatic_index, const std::array<double, Dim>& center,\n    const std::array<double, Dim>& mean_velocity, const double strength,\n    const double perturbation_amplitude)\n    : adiabatic_index_(adiabatic_index),\n      center_(center),\n      mean_velocity_(mean_velocity),\n      perturbation_amplitude_(perturbation_amplitude),\n      strength_(strength),\n      // Polytropic constant is set equal to 1.0\n      equation_of_state_(1.0, adiabatic_index) {\n  if (Dim == 2) {\n    ASSERT(\n        abs(perturbation_amplitude_) < std::numeric_limits<double>::epsilon(),\n        \"A nonzero perturbation amplitude only makes sense in 3 dimensions. \"\n        \"The value given was \"\n            << perturbation_amplitude);\n  }\n\n  ASSERT(strength_ >= 0.0,\n         \"The strength must be non-negative. The value given \"\n         \"was \"\n             << strength_ << \".\");\n}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::initial_data::InitialData>\nIsentropicVortex<Dim>::get_clone() const {\n  return std::make_unique<IsentropicVortex>(*this);\n}\n\ntemplate <size_t Dim>\nIsentropicVortex<Dim>::IsentropicVortex(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID IsentropicVortex<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nvoid IsentropicVortex<Dim>::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | adiabatic_index_;\n  p | center_;\n  p | mean_velocity_;\n  p | perturbation_amplitude_;\n  p | strength_;\n  p | equation_of_state_;\n}\n\n// Can be any smooth function of z. For testing purposes, we choose sin(z).\ntemplate <size_t Dim>\ntemplate <typename DataType>\nDataType IsentropicVortex<Dim>::perturbation_profile(const DataType& z) const {\n  return sin(z);\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nDataType IsentropicVortex<Dim>::deriv_of_perturbation_profile(\n    const DataType& z) const {\n  return cos(z);\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\nIsentropicVortex<Dim>::IntermediateVariables<DataType>::IntermediateVariables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x, const double t,\n    const std::array<double, Dim>& center,\n    const std::array<double, Dim>& mean_velocity, const double strength) {\n  x_tilde = get<0>(x) - center[0] - t * mean_velocity[0];\n  y_tilde = get<1>(x) - center[1] - t * mean_velocity[1];\n  profile = 0.5 * strength *\n            exp(0.5 - 0.5 * (square(x_tilde) + square(y_tilde))) / M_PI;\n  if (Dim == 3) {\n    z_coord = get<Dim - 1>(x);\n  }\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nIsentropicVortex<Dim>::variables(\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n    const IntermediateVariables<DataType>& vars) const {\n  const double adiabatic_index_minus_one = adiabatic_index_ - 1.0;\n  return Scalar<DataType>(pow(1.0 - 0.5 * adiabatic_index_minus_one *\n                                        square(vars.profile) / adiabatic_index_,\n                              1.0 / adiabatic_index_minus_one));\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, Dim>>\nIsentropicVortex<Dim>::variables(\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim>> /*meta*/,\n    const IntermediateVariables<DataType>& vars) const {\n  auto velocity = make_with_value<tnsr::I<DataType, Dim, Frame::Inertial>>(\n      vars.y_tilde, 0.0);\n  for (size_t i = 0; i < Dim; ++i) {\n    velocity.get(i) = gsl::at(mean_velocity_, i);\n  }\n  get<0>(velocity) -= vars.y_tilde * vars.profile;\n  get<1>(velocity) += vars.x_tilde * vars.profile;\n  if (Dim == 3) {\n    get<Dim - 1>(velocity) +=\n        perturbation_amplitude_ * perturbation_profile(vars.z_coord);\n  }\n  return velocity;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nIsentropicVortex<Dim>::variables(\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n    const IntermediateVariables<DataType>& vars) const {\n  return equation_of_state_.specific_internal_energy_from_density(\n      get<hydro::Tags::RestMassDensity<DataType>>(variables(\n          tmpl::list<hydro::Tags::RestMassDensity<DataType>>{}, vars)));\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nIsentropicVortex<Dim>::variables(\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n    const IntermediateVariables<DataType>& vars) const {\n  return equation_of_state_.pressure_from_density(\n      get<hydro::Tags::RestMassDensity<DataType>>(variables(\n          tmpl::list<hydro::Tags::RestMassDensity<DataType>>{}, vars)));\n}\n\ntemplate <size_t Dim>\nbool operator==(const IsentropicVortex<Dim>& lhs,\n                const IsentropicVortex<Dim>& rhs) {\n  // No comparison for equation_of_state_. Comparing individual\n  // members should suffice.\n  return lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.center_ == rhs.center_ and\n         lhs.mean_velocity_ == rhs.mean_velocity_ and\n         lhs.perturbation_amplitude_ == rhs.perturbation_amplitude_ and\n         lhs.strength_ == rhs.strength_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const IsentropicVortex<Dim>& lhs,\n                const IsentropicVortex<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE_CLASS(_, data)                              \\\n  template class IsentropicVortex<DIM(data)>;                   \\\n  template bool operator==(const IsentropicVortex<DIM(data)>&,  \\\n                           const IsentropicVortex<DIM(data)>&); \\\n  template bool operator!=(const IsentropicVortex<DIM(data)>&,  \\\n                           const IsentropicVortex<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_CLASS, (2, 3))\n\n#define INSTANTIATE_MEMBERS(_, data)                                           \\\n  template struct IsentropicVortex<DIM(data)>::IntermediateVariables<DTYPE(    \\\n      data)>;                                                                  \\\n  template DTYPE(data)                                                         \\\n      IsentropicVortex<DIM(data)>::perturbation_profile(const DTYPE(data) & z) \\\n          const;                                                               \\\n  template DTYPE(data)                                                         \\\n      IsentropicVortex<DIM(data)>::deriv_of_perturbation_profile(              \\\n          const DTYPE(data) & z) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_MEMBERS, (2, 3), (double, DataVector))\n\n#define INSTANTIATE_SCALARS(_, data)                      \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)> > \\\n      IsentropicVortex<DIM(data)>::variables(             \\\n          tmpl::list<TAG(data) < DTYPE(data)> >,          \\\n          const IntermediateVariables<DTYPE(data)>&) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (2, 3), (double, DataVector),\n                        (hydro::Tags::RestMassDensity,\n                         hydro::Tags::SpecificInternalEnergy,\n                         hydro::Tags::Pressure))\n\n#define INSTANTIATE_VELOCITY(_, data)                                \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), DIM(data)> > \\\n      IsentropicVortex<DIM(data)>::variables(                        \\\n          tmpl::list<TAG(data) < DTYPE(data), DIM(data)> >,          \\\n          const IntermediateVariables<DTYPE(data)>&) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VELOCITY, (2, 3), (double, DataVector),\n                        (hydro::Tags::SpatialVelocity))\n\n#undef DIM\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_CLASS\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VELOCITY\n\n}  // namespace NewtonianEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/IsentropicVortex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::Solutions {\n\n/*!\n * \\brief Newtonian isentropic vortex in Cartesian coordinates\n *\n * The analytic solution to the 2-D Newtonian Euler system\n * representing the slow advection of an incompressible, isentropic\n * vortex \\cite Yee1999. The initial condition is the superposition of a\n * mean uniform flow with a gaussian-profile vortex. When embedded in\n * 3-D space, the isentropic vortex is still a solution to the corresponding 3-D\n * system if the velocity along the third axis is a constant. In Cartesian\n * coordinates \\f$(x, y, z)\\f$, and using dimensionless units, the primitive\n * quantities at a given time \\f$t\\f$ are then\n *\n * \\f{align*}\n * \\rho &= \\left[1 - \\dfrac{(\\gamma - 1)\\beta^2}{8\\gamma\\pi^2}\\exp\\left(\n * 1 - r^2\\right)\\right]^{1/(\\gamma - 1)}, \\\\\n * v_x &= U - \\dfrac{\\beta\\tilde y}{2\\pi}\\exp\\left(\\dfrac{1 - r^2}{2}\\right),\\\\\n * v_y &= V + \\dfrac{\\beta\\tilde x}{2\\pi}\\exp\\left(\\dfrac{1 - r^2}{2}\\right),\\\\\n * v_z &= W,\\\\\n * \\epsilon &= \\frac{\\rho^{\\gamma - 1}}{\\gamma - 1},\n * \\f}\n *\n * with\n *\n * \\f{align*}\n * r^2 &= {\\tilde x}^2 + {\\tilde y}^2,\\\\\n * \\tilde x &= x - X_0 - U t,\\\\\n * \\tilde y &= y - Y_0 - V t,\n * \\f}\n *\n * where \\f$(X_0, Y_0)\\f$ is the position of the vortex on the \\f$(x, y)\\f$\n * plane at \\f$t = 0\\f$, \\f$(U, V, W)\\f$ are the components of the mean flow\n * velocity, \\f$\\beta\\f$ is the vortex strength, and \\f$\\gamma\\f$ is the\n * adiabatic index. The pressure \\f$p\\f$ is then obtained from the dimensionless\n * polytropic relation\n *\n * \\f{align*}\n * p = \\rho^\\gamma.\n * \\f}\n *\n * On the other hand, if the velocity along the \\f$z-\\f$axis is not a constant\n * but a function of the \\f$z\\f$ coordinate, the resulting modified isentropic\n * vortex is still a solution to the Newtonian Euler system, but with source\n * terms that are proportional to \\f$dv_z/dz\\f$. (See\n * NewtonianEuler::Sources::VortexPerturbation.) For testing purposes,\n * we choose to write the velocity as a uniform field plus a periodic\n * perturbation,\n *\n * \\f{align*}\n * v_z(z) = W + \\epsilon \\sin{z},\n * \\f}\n *\n * where \\f$\\epsilon\\f$ is the amplitude of the perturbation. The resulting\n * source for the Newtonian Euler system will then be proportional to\n * \\f$\\epsilon \\cos{z}\\f$.\n */\ntemplate <size_t Dim>\nclass IsentropicVortex : public evolution::initial_data::InitialData,\n                         public MarkAsAnalyticSolution {\n  static_assert(Dim == 2 or Dim == 3,\n                \"IsentropicVortex solution works in 2 and 3 dimensions\");\n\n  template <typename DataType>\n  struct IntermediateVariables;\n\n public:\n  using equation_of_state_type = EquationsOfState::PolytropicFluid<false>;\n\n  /// The adiabatic index of the fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the fluid.\"};\n  };\n\n  /// The position of the center of the vortex at \\f$t = 0\\f$\n  struct Center {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\n        \"The coordinates of the center of the vortex at t = 0.\"};\n  };\n\n  /// The mean flow velocity.\n  struct MeanVelocity {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\"The mean flow velocity.\"};\n  };\n\n  /// The amplitude of the perturbation generating a source term.\n  struct PerturbAmplitude {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The amplitude of the perturbation producing sources.\"};\n  };\n\n  /// The strength of the vortex.\n  struct Strength {\n    using type = double;\n    static constexpr Options::String help = {\"The strength of the vortex.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options = tmpl::conditional_t<\n      Dim == 3,\n      tmpl::list<AdiabaticIndex, Center, MeanVelocity, Strength,\n                 PerturbAmplitude>,\n      tmpl::list<AdiabaticIndex, Center, MeanVelocity, Strength>>;\n\n  static constexpr Options::String help = {\n      \"Newtonian Isentropic Vortex. Works in 2 and 3 dimensions.\"};\n\n  IsentropicVortex() = default;\n  IsentropicVortex(const IsentropicVortex& /*rhs*/) = default;\n  IsentropicVortex& operator=(const IsentropicVortex& /*rhs*/) = default;\n  IsentropicVortex(IsentropicVortex&& /*rhs*/) = default;\n  IsentropicVortex& operator=(IsentropicVortex&& /*rhs*/) = default;\n  ~IsentropicVortex() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit IsentropicVortex(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(IsentropicVortex);\n  /// \\endcond\n\n  IsentropicVortex(double adiabatic_index,\n                   const std::array<double, Dim>& center,\n                   const std::array<double, Dim>& mean_velocity,\n                   double strength, double perturbation_amplitude = 0.0);\n\n  /// Retrieve a collection of hydrodynamic variables at position x and time t\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n      const double t,  // NOLINT\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    const IntermediateVariables<DataType> vars(x, t, center_, mean_velocity_,\n                                               strength_);\n    return {tuples::get<Tags>(variables(tmpl::list<Tags>{}, vars))...};\n  }\n\n  /// Function of `z` coordinate to compute the perturbation generating\n  /// a source term. Public so the corresponding source class can also use it.\n  template <typename DataType>\n  DataType perturbation_profile(const DataType& z) const;\n\n  template <typename DataType>\n  DataType deriv_of_perturbation_profile(const DataType& z) const;\n\n  // To be used by VortexPerturbation source term\n  double perturbation_amplitude() const { return perturbation_amplitude_; }\n\n  const EquationsOfState::PolytropicFluid<false>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  /// @{\n  /// Retrieve hydro variable at `(x, t)`\n  template <typename DataType>\n  auto variables(tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n                 const IntermediateVariables<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim>> /*meta*/,\n      const IntermediateVariables<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, Dim>>;\n\n  template <typename DataType>\n  auto variables(\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n      const IntermediateVariables<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n                 const IntermediateVariables<DataType>& vars) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n  /// @}\n\n  // Intermediate variables needed to compute the primitives\n  template <typename DataType>\n  struct IntermediateVariables {\n    IntermediateVariables(const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n                          double t, const std::array<double, Dim>& center,\n                          const std::array<double, Dim>& mean_velocity,\n                          double strength);\n    DataType x_tilde{};\n    DataType y_tilde{};\n    DataType profile{};\n    // (3D only) z-coordinate to compute perturbation term\n    DataType z_coord{};\n  };\n\n  template <size_t SpatialDim>\n  friend bool\n  operator==(  // NOLINT (clang-tidy: readability-redundant-declaration)\n      const IsentropicVortex<SpatialDim>& lhs,\n      const IsentropicVortex<SpatialDim>& rhs);\n\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, Dim> center_ =\n      make_array<Dim>(std::numeric_limits<double>::signaling_NaN());\n  std::array<double, Dim> mean_velocity_ =\n      make_array<Dim>(std::numeric_limits<double>::signaling_NaN());\n  double perturbation_amplitude_ = 0.0;\n  double strength_ = std::numeric_limits<double>::signaling_NaN();\n\n  // This is an ideal gas undergoing an isentropic process,\n  // so the relation between the pressure and the mass density is polytropic,\n  // where the polytropic exponent corresponds to the adiabatic index.\n  EquationsOfState::PolytropicFluid<false> equation_of_state_{};\n};\n\ntemplate <size_t Dim>\nbool operator!=(const IsentropicVortex<Dim>& lhs,\n                const IsentropicVortex<Dim>& rhs);\n\n}  // namespace NewtonianEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace NewtonianEuler::Solutions {\n\nLaneEmdenStar::LaneEmdenStar(const double central_mass_density,\n                             const double polytropic_constant)\n    : central_mass_density_(central_mass_density),\n      polytropic_constant_(polytropic_constant),\n      equation_of_state_{polytropic_constant_, 2.0} {\n  ASSERT(central_mass_density > 0.0,\n         \"central_mass_density = \" << central_mass_density);\n  ASSERT(polytropic_constant > 0.0,\n         \"polytropic_constant = \" << polytropic_constant);\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> LaneEmdenStar::get_clone()\n    const {\n  return std::make_unique<LaneEmdenStar>(*this);\n}\n\nLaneEmdenStar::LaneEmdenStar(CkMigrateMessage* msg) : InitialData(msg) {}\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID LaneEmdenStar::my_PUP_ID = 0;\n\nvoid LaneEmdenStar::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | central_mass_density_;\n  p | polytropic_constant_;\n  p | equation_of_state_;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> LaneEmdenStar::gravitational_field(\n    const tnsr::I<DataType, 3>& x) const {\n  // Compute alpha for polytrope n==1, units G==1\n  const double alpha = sqrt(0.5 * polytropic_constant_ / M_PI);\n  const double outer_radius = alpha * M_PI;\n  const double mass_scale = 4.0 * M_PI * cube(alpha) * central_mass_density_;\n  // Add tiny offset to avoid divisons by zero\n  const DataType radius = get(magnitude(x)) + 1.e-30 * outer_radius;\n\n  auto enclosed_mass = make_with_value<DataType>(get_size(radius), mass_scale);\n  for (size_t s = 0; s < get_size(radius); ++s) {\n    if (get_element(radius, s) < outer_radius) {\n      const double xi = get_element(radius, s) / alpha;\n      get_element(enclosed_mass, s) *= sin(xi) - xi * cos(xi);\n    } else {\n      get_element(enclosed_mass, s) *= M_PI;\n    }\n  }\n\n  auto gravitational_field_result = x;\n  for (size_t i = 0; i < 3; ++i) {\n    gravitational_field_result.get(i) *= -enclosed_mass / cube(radius);\n  }\n  return gravitational_field_result;\n}\n\ntemplate <typename DataType>\nScalar<DataType> LaneEmdenStar::precompute_mass_density(\n    const tnsr::I<DataType, 3>& x) const {\n  // Compute alpha for polytrope n==1, units G==1\n  const double alpha = sqrt(0.5 * polytropic_constant_ / M_PI);\n  const double outer_radius = alpha * M_PI;\n  // Add tiny offset to avoid divisons by zero\n  const DataType radius = get(magnitude(x)) + 1.e-30 * outer_radius;\n\n  Scalar<DataType> mass_density(get_size(radius));\n  for (size_t s = 0; s < get_size(radius); ++s) {\n    if (get_element(radius, s) < outer_radius) {\n      const double xi = get_element(radius, s) / alpha;\n      get_element(get(mass_density), s) = central_mass_density_ * sin(xi) / xi;\n    } else {\n      get_element(get(mass_density), s) = 0.0;\n    }\n  }\n  return mass_density;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nLaneEmdenStar::variables(\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n    const Scalar<DataType>& mass_density) const {\n  return mass_density;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nLaneEmdenStar::variables(\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/,\n    const Scalar<DataType>& mass_density) const {\n  return make_with_value<tnsr::I<DataType, 3>>(get(mass_density), 0.0);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> LaneEmdenStar::variables(\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n    const Scalar<DataType>& mass_density) const {\n  return equation_of_state_.pressure_from_density(mass_density);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nLaneEmdenStar::variables(\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n    const Scalar<DataType>& mass_density) const {\n  return equation_of_state_.specific_internal_energy_from_density(mass_density);\n}\n\nbool operator==(const LaneEmdenStar& lhs, const LaneEmdenStar& rhs) {\n  // There is no comparison operator for the EoS, but should be okay as\n  // the `polytropic_constant`s are compared.\n  return lhs.central_mass_density_ == rhs.central_mass_density_ and\n         lhs.polytropic_constant_ == rhs.polytropic_constant_;\n}\n\nbool operator!=(const LaneEmdenStar& lhs, const LaneEmdenStar& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template tnsr::I<DTYPE(data), 3> LaneEmdenStar::gravitational_field(       \\\n      const tnsr::I<DTYPE(data), 3>& x) const;                               \\\n  template Scalar<DTYPE(data)> LaneEmdenStar::precompute_mass_density(       \\\n      const tnsr::I<DTYPE(data), 3>& x) const;                               \\\n  template tuples::TaggedTuple<hydro::Tags::RestMassDensity<DTYPE(data)>>    \\\n  LaneEmdenStar::variables(                                                  \\\n      tmpl::list<hydro::Tags::RestMassDensity<DTYPE(data)>> /*meta*/,        \\\n      const Scalar<DTYPE(data)>& mass_density) const;                        \\\n  template tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DTYPE(data), 3>> \\\n  LaneEmdenStar::variables(                                                  \\\n      tmpl::list<hydro::Tags::SpatialVelocity<DTYPE(data), 3>> /*meta*/,     \\\n      const Scalar<DTYPE(data)>& mass_density) const;                        \\\n  template tuples::TaggedTuple<hydro::Tags::Pressure<DTYPE(data)>>           \\\n  LaneEmdenStar::variables(                                                  \\\n      tmpl::list<hydro::Tags::Pressure<DTYPE(data)>> /*meta*/,               \\\n      const Scalar<DTYPE(data)>& mass_density) const;                        \\\n  template tuples::TaggedTuple<                                              \\\n      hydro::Tags::SpecificInternalEnergy<DTYPE(data)>>                      \\\n  LaneEmdenStar::variables(                                                  \\\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DTYPE(data)>> /*meta*/, \\\n      const Scalar<DTYPE(data)>& mass_density) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace NewtonianEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::Solutions {\n\n/*!\n * \\brief A static spherically symmetric star in Newtonian gravity\n *\n * The solution for a static, spherically-symmetric star in 3 dimensions, found\n * by solving the Lane-Emden equation \\cite Chandrasekhar1939\n * \\cite Shapiro1983 .\n * The Lane-Emden equation has closed-form solutions for certain equations of\n * state; this class implements the solution for a polytropic fluid with\n * polytropic exponent \\f$\\Gamma=2\\f$ (i.e., with polytropic index \\f$n=1\\f$).\n * The solution is returned in units where \\f$G=1\\f$, with \\f$G\\f$ the\n * gravitational constant.\n *\n * The radius and mass of the star are determined by the polytropic constant\n * \\f$\\kappa\\f$ and central density \\f$\\rho_c\\f$.\n * The radius is \\f$R = \\pi \\alpha\\f$,\n * and the mass is \\f$M = 4 \\pi^2 \\alpha^3 \\rho_c\\f$,\n * where \\f$\\alpha = \\sqrt{\\kappa / (2 \\pi)}\\f$ and \\f$G=1\\f$.\n */\nclass LaneEmdenStar : public evolution::initial_data::InitialData,\n                      public MarkAsAnalyticSolution {\n public:\n  using equation_of_state_type = EquationsOfState::PolytropicFluid<false>;\n\n  /// The central mass density of the star.\n  struct CentralMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The central mass density of the star.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  /// The polytropic constant of the polytropic fluid.\n  struct PolytropicConstant {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The polytropic constant of the fluid.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  using options = tmpl::list<CentralMassDensity, PolytropicConstant>;\n\n  static constexpr Options::String help = {\n      \"A static, spherically-symmetric star in Newtonian gravity, found by\\n\"\n      \"solving the Lane-Emden equations, with a given central density and\\n\"\n      \"polytropic fluid. The fluid has polytropic index 1, but the polytropic\\n\"\n      \"constant is specifiable\"};\n\n  LaneEmdenStar() = default;\n  LaneEmdenStar(const LaneEmdenStar& /*rhs*/) = default;\n  LaneEmdenStar& operator=(const LaneEmdenStar& /*rhs*/) = default;\n  LaneEmdenStar(LaneEmdenStar&& /*rhs*/) = default;\n  LaneEmdenStar& operator=(LaneEmdenStar&& /*rhs*/) = default;\n  ~LaneEmdenStar() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit LaneEmdenStar(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(LaneEmdenStar);\n  /// \\endcond\n\n  LaneEmdenStar(double central_mass_density, double polytropic_constant);\n\n  /// Retrieve a collection of variables at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         const double /*t*/,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    const auto mass_density = precompute_mass_density(x);\n    return {tuples::get<Tags>(variables(tmpl::list<Tags>{}, mass_density))...};\n  }\n\n  /// \\brief Compute the gravitational field for the corresponding source term,\n  /// LaneEmdenGravitationalField.\n  ///\n  /// The result is the vector-field giving the acceleration due to gravity\n  /// that is felt by a test particle.\n  template <typename DataType>\n  tnsr::I<DataType, 3> gravitational_field(const tnsr::I<DataType, 3>& x) const;\n\n  const EquationsOfState::PolytropicFluid<false>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  template <typename DataType>\n  Scalar<DataType> precompute_mass_density(const tnsr::I<DataType, 3>& x) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>> variables(\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n      const Scalar<DataType>& mass_density) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>> variables(\n      tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/,\n      const Scalar<DataType>& mass_density) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::Pressure<DataType>> variables(\n      tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n      const Scalar<DataType>& mass_density) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>> variables(\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n      const Scalar<DataType>& mass_density) const;\n\n  friend bool operator==(const LaneEmdenStar& lhs, const LaneEmdenStar& rhs);\n\n  double central_mass_density_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_constant_ = std::numeric_limits<double>::signaling_NaN();\n  EquationsOfState::PolytropicFluid<false> equation_of_state_{};\n};\n\nbool operator!=(const LaneEmdenStar& lhs, const LaneEmdenStar& rhs);\n\n}  // namespace NewtonianEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/RiemannProblem.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/RiemannProblem.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace {\n\n// Any of the two functions of the pressure and the initial data\n// in Eqns. (4.6) and (4.7) of Toro.\ntemplate <size_t Dim>\nstruct FunctionOfPressureAndData {\n  FunctionOfPressureAndData(const typename NewtonianEuler::Solutions::\n                                RiemannProblem<Dim>::InitialData& data,\n                            const double adiabatic_index)\n      : state_pressure_(data.pressure_),\n        constant_a_(data.constant_a_),\n        constant_b_(data.constant_b_) {\n    prefactor_ = 2.0 * data.sound_speed_ / (adiabatic_index - 1.0);\n    exponent_ = 0.5 * (adiabatic_index - 1.0) / adiabatic_index;\n  }\n\n  double operator()(const double pressure) const {\n    // Value depends on whether the initial state is a shock\n    // (pressure > pressure of initial state) or a rarefaction wave\n    // (pressure <= pressure of initial state)\n    return pressure > state_pressure_\n               ? (pressure - state_pressure_) *\n                     sqrt(constant_a_ / (pressure + constant_b_))\n               : prefactor_ *\n                     (pow(pressure / state_pressure_, exponent_) - 1.0);\n  }\n\n private:\n  double state_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double constant_a_ = std::numeric_limits<double>::signaling_NaN();\n  double constant_b_ = std::numeric_limits<double>::signaling_NaN();\n\n  // Auxiliary variables for computing the rarefaction wave\n  double prefactor_ = std::numeric_limits<double>::signaling_NaN();\n  double exponent_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n}  // namespace\n\nnamespace NewtonianEuler::Solutions {\n\ntemplate <size_t Dim>\nRiemannProblem<Dim>::RiemannProblem(\n    const double adiabatic_index, const double initial_position,\n    const double left_mass_density,\n    const std::array<double, Dim>& left_velocity, const double left_pressure,\n    const double right_mass_density,\n    const std::array<double, Dim>& right_velocity, const double right_pressure,\n    const double pressure_star_tol)\n    : adiabatic_index_(adiabatic_index),\n      initial_position_(initial_position),\n      left_initial_data_(left_mass_density, left_velocity, left_pressure,\n                         adiabatic_index, propagation_axis_),\n      right_initial_data_(right_mass_density, right_velocity, right_pressure,\n                          adiabatic_index, propagation_axis_),\n      pressure_star_tol_(pressure_star_tol),\n      equation_of_state_(adiabatic_index) {\n  const double delta_u = right_initial_data_.normal_velocity_ -\n                         left_initial_data_.normal_velocity_;\n\n  // The pressure positivity condition must be met (Eqn. 4.40 of Toro).\n  ASSERT(2.0 *\n                     (left_initial_data_.sound_speed_ +\n                      right_initial_data_.sound_speed_) /\n                     (adiabatic_index_ - 1.0) -\n                 delta_u >\n             0.0,\n         \"The pressure positivity condition must be met. Initial data do not \"\n         \"satisfy this criterion.\");\n\n  // Before evaluating the solution at any (x, t), the type of solution\n  // (shock or rarefaction) on each side of the contact discontinuity\n  // must be sorted out. To do so, the first step is to compute the\n  // state variables in the star region by solving a transcendental equation,\n  // which is what all the math in this constructor is about.\n\n  // Compute bracket for root finder according to value of the function whose\n  // root we want (Eqn. 4.39 of Toro.)\n  const FunctionOfPressureAndData<Dim> f_of_p_left(left_initial_data_,\n                                                   adiabatic_index_);\n  const FunctionOfPressureAndData<Dim> f_of_p_right(right_initial_data_,\n                                                    adiabatic_index_);\n  const auto p_minmax =\n      std::minmax(left_initial_data_.pressure_, right_initial_data_.pressure_);\n  const double f_min =\n      f_of_p_left(p_minmax.first) + f_of_p_right(p_minmax.first) + delta_u;\n  const double f_max =\n      f_of_p_left(p_minmax.second) + f_of_p_right(p_minmax.second) + delta_u;\n\n  if (f_min > 0.0 and f_max > 0.0) {\n    const double exponent = 0.5 * (adiabatic_index_ - 1.0) / adiabatic_index_;\n    pressure_star_ = std::pow(\n        (left_initial_data_.sound_speed_ + right_initial_data_.sound_speed_ -\n         0.5 * (adiabatic_index_ - 1.0) * delta_u) /\n            (left_initial_data_.sound_speed_ *\n                 std::pow(left_initial_data_.pressure_, -exponent) +\n             right_initial_data_.sound_speed_ *\n                 std::pow(right_initial_data_.pressure_, -exponent)),\n        1.0 / exponent);\n    ASSERT(std::abs(f_of_p_left(pressure_star_) + f_of_p_right(pressure_star_) +\n                    delta_u) < 1.0e-8,\n           \"Failed to analytically solve correctly.\");\n  } else {\n    double pressure_lower = std::numeric_limits<double>::signaling_NaN();\n    double pressure_upper = std::numeric_limits<double>::signaling_NaN();\n    if (f_min < 0.0 and f_max > 0.0) {\n      pressure_lower = p_minmax.first;\n      pressure_upper = p_minmax.second;\n    } else {\n      pressure_lower = p_minmax.second;\n      pressure_upper = 10.0 * pressure_lower;  // Arbitrary upper bound < \\infty\n    }\n\n    // Now get pressure by solving transcendental equation.\n    const auto f_of_p = [&f_of_p_left, &f_of_p_right,\n                         &delta_u](const double pressure) {\n      // Function of pressure in Eqn. (4.5) of Toro.\n      return f_of_p_left(pressure) + f_of_p_right(pressure) + delta_u;\n    };\n    try {\n      pressure_star_ = RootFinder::toms748(\n          f_of_p, pressure_lower, pressure_upper, pressure_star_tol_, 1.0e-15);\n    } catch (std::exception& exception) {\n      ERROR(\n          \"Failed to find p_* with Newton-Raphson root finder. Got \"\n          \"exception message:\\n\"\n          << exception.what()\n          << \"\\nIf the residual is small you can change the tolerance for the \"\n             \"root finder in the input file.\");\n    }\n  }\n\n  // Calculated p_*, u_* is obtained from Eqn. (4.9) in Toro.\n  velocity_star_ =\n      0.5 * (left_initial_data_.normal_velocity_ +\n             right_initial_data_.normal_velocity_ -\n             f_of_p_left(pressure_star_) + f_of_p_right(pressure_star_));\n}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::initial_data::InitialData>\nRiemannProblem<Dim>::get_clone() const {\n  return std::make_unique<RiemannProblem>(*this);\n}\n\ntemplate <size_t Dim>\nRiemannProblem<Dim>::RiemannProblem(CkMigrateMessage* msg)\n    : evolution::initial_data::InitialData(msg) {}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID RiemannProblem<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nvoid RiemannProblem<Dim>::pup(PUP::er& p) {\n  evolution::initial_data::InitialData::pup(p);\n  p | adiabatic_index_;\n  p | initial_position_;\n  p | left_initial_data_;\n  p | right_initial_data_;\n  p | pressure_star_tol_;\n  p | pressure_star_;\n  p | velocity_star_;\n  p | equation_of_state_;\n}\n\ntemplate <size_t Dim>\nRiemannProblem<Dim>::InitialData::InitialData(\n    const double mass_density, const std::array<double, Dim>& velocity,\n    const double pressure, const double adiabatic_index,\n    const size_t propagation_axis)\n    : mass_density_(mass_density), velocity_(velocity), pressure_(pressure) {\n  ASSERT(mass_density_ > 0.0,\n         \"The mass density must be positive. Value given: \" << mass_density);\n  ASSERT(pressure_ > 0.0,\n         \"The pressure must be positive. Value given: \" << pressure);\n\n  sound_speed_ = sqrt(adiabatic_index * pressure / mass_density);\n  normal_velocity_ = gsl::at(velocity, propagation_axis);\n  constant_a_ = 2.0 / (adiabatic_index + 1.0) / mass_density;\n  constant_b_ = (adiabatic_index - 1.0) * pressure / (adiabatic_index + 1.0);\n}\n\ntemplate <size_t Dim>\nvoid RiemannProblem<Dim>::InitialData::pup(PUP::er& p) {\n  p | mass_density_;\n  p | velocity_;\n  p | pressure_;\n  p | sound_speed_;\n  p | normal_velocity_;\n  p | constant_a_;\n  p | constant_b_;\n}\n\ntemplate <size_t Dim>\nRiemannProblem<Dim>::Wave::Wave(const InitialData& data,\n                                const double pressure_star,\n                                const double velocity_star,\n                                const double adiabatic_index, const Side& side)\n    : pressure_ratio_(pressure_star / data.pressure_),\n      is_shock_(pressure_ratio_ > 1.0),\n      data_(data),\n      shock_(data, pressure_ratio_, adiabatic_index, side),\n      rarefaction_(data, pressure_ratio_, velocity_star, adiabatic_index,\n                   side) {}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Wave::mass_density(const double x_shifted,\n                                               const double t) const {\n  return (is_shock_ ? shock_.mass_density(x_shifted, t, data_)\n                    : rarefaction_.mass_density(x_shifted, t, data_));\n}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Wave::normal_velocity(\n    const double x_shifted, const double t, const double velocity_star) const {\n  return (is_shock_ ? shock_.normal_velocity(x_shifted, t, data_, velocity_star)\n                    : rarefaction_.normal_velocity(x_shifted, t, data_,\n                                                   velocity_star));\n}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Wave::pressure(const double x_shifted,\n                                           const double t,\n                                           const double pressure_star) const {\n  return (is_shock_\n              ? shock_.pressure(x_shifted, t, data_, pressure_star)\n              : rarefaction_.pressure(x_shifted, t, data_, pressure_star));\n}\n\ntemplate <size_t Dim>\nRiemannProblem<Dim>::Shock::Shock(const InitialData& data,\n                                  const double pressure_ratio,\n                                  const double adiabatic_index,\n                                  const Side& side)\n    : direction_(side == Side::Left ? -1.0 : 1.0) {\n  const double gamma_mm = adiabatic_index - 1.0;\n  const double gamma_pp = adiabatic_index + 1.0;\n  const double gamma_mm_over_gamma_pp = gamma_mm / gamma_pp;\n\n  mass_density_star_ = data.mass_density_ *\n                       (pressure_ratio + gamma_mm_over_gamma_pp) /\n                       (pressure_ratio * gamma_mm_over_gamma_pp + 1.0);\n\n  shock_speed_ =\n      data.normal_velocity_ +\n      direction_ * data.sound_speed_ *\n          sqrt(0.5 * (gamma_pp * pressure_ratio + gamma_mm) / adiabatic_index);\n}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Shock::mass_density(const double x_shifted,\n                                                const double t,\n                                                const InitialData& data) const {\n  return mass_density_star_ +\n         (data.mass_density_ - mass_density_star_) *\n             step_function(direction_ * (x_shifted - shock_speed_ * t));\n}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Shock::normal_velocity(\n    const double x_shifted, const double t, const InitialData& data,\n    const double velocity_star) const {\n  return velocity_star +\n         (data.normal_velocity_ - velocity_star) *\n             step_function(direction_ * (x_shifted - shock_speed_ * t));\n}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Shock::pressure(const double x_shifted,\n                                            const double t,\n                                            const InitialData& data,\n                                            const double pressure_star) const {\n  return pressure_star +\n         (data.pressure_ - pressure_star) *\n             step_function(direction_ * (x_shifted - shock_speed_ * t));\n}\n\ntemplate <size_t Dim>\nRiemannProblem<Dim>::Rarefaction::Rarefaction(const InitialData& data,\n                                              const double pressure_ratio,\n                                              const double velocity_star,\n                                              const double adiabatic_index,\n                                              const Side& side)\n    : direction_(side == Side::Left ? -1.0 : 1.0) {\n  gamma_mm_ = adiabatic_index - 1.0;\n  gamma_pp_ = adiabatic_index + 1.0;\n  mass_density_star_ =\n      data.mass_density_ * pow(pressure_ratio, 1.0 / adiabatic_index);\n  sound_speed_star_ =\n      data.sound_speed_ *\n      pow(pressure_ratio, 0.5 * (adiabatic_index - 1.0) / adiabatic_index);\n  head_speed_ = data.normal_velocity_ + direction_ * data.sound_speed_;\n  tail_speed_ = velocity_star + direction_ * sound_speed_star_;\n}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Rarefaction::mass_density(\n    const double x_shifted, const double t, const InitialData& data) const {\n  const double s = (t > 0.0 ? (x_shifted / t) : 0.0);\n  return direction_ * (x_shifted - tail_speed_ * t) < 0.0\n             ? mass_density_star_\n             : (direction_ * (x_shifted - tail_speed_ * t) >= 0.0 and\n                        direction_ * (x_shifted - head_speed_ * t) < 0.0\n                    ? (data.mass_density_ *\n                       pow((2.0 - direction_ * gamma_mm_ *\n                                      (data.normal_velocity_ - s) /\n                                      data.sound_speed_) /\n                               gamma_pp_,\n                           2.0 / gamma_mm_))\n                    : data.mass_density_);\n}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Rarefaction::normal_velocity(\n    const double x_shifted, const double t, const InitialData& data,\n    const double velocity_star) const {\n  const double s = (t > 0.0 ? (x_shifted / t) : 0.0);\n  return direction_ * (x_shifted - tail_speed_ * t) < 0.0\n             ? velocity_star\n             : (direction_ * (x_shifted - tail_speed_ * t) >= 0.0 and\n                        direction_ * (x_shifted - head_speed_ * t) < 0.0\n                    ? (2.0 *\n                       (0.5 * gamma_mm_ * data.normal_velocity_ + s -\n                        direction_ * data.sound_speed_) /\n                       gamma_pp_)\n                    : data.normal_velocity_);\n}\n\ntemplate <size_t Dim>\ndouble RiemannProblem<Dim>::Rarefaction::pressure(\n    const double x_shifted, const double t, const InitialData& data,\n    const double pressure_star) const {\n  const double s = (t > 0.0 ? (x_shifted / t) : 0.0);\n  return direction_ * (x_shifted - tail_speed_ * t) < 0.0\n             ? pressure_star\n             : (direction_ * (x_shifted - tail_speed_ * t) >= 0.0 and\n                        direction_ * (x_shifted - head_speed_ * t) < 0.0\n                    ? (data.pressure_ *\n                       pow((2.0 - direction_ * gamma_mm_ *\n                                      (data.normal_velocity_ - s) /\n                                      data.sound_speed_) /\n                               gamma_pp_,\n                           2.0 * (gamma_mm_ + 1.0) / gamma_mm_))\n                    : data.pressure_);\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nRiemannProblem<Dim>::variables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x_shifted, const double t,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n    const Wave& left, const Wave& right) const {\n  auto mass_density = make_with_value<Scalar<DataType>>(x_shifted, 0.0);\n  const double u_star_times_t = velocity_star_ * t;\n  for (size_t s = 0; s < get_size(get<0>(x_shifted)); ++s) {\n    const double x_shifted_s = get_element(x_shifted.get(propagation_axis_), s);\n    get_element(get(mass_density), s) =\n        (x_shifted_s < u_star_times_t ? left.mass_density(x_shifted_s, t)\n                                      : right.mass_density(x_shifted_s, t));\n  }\n  return mass_density;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, Dim>>\nRiemannProblem<Dim>::variables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x_shifted, const double t,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim>> /*meta*/,\n    const Wave& left, const Wave& right) const {\n  auto velocity = make_with_value<tnsr::I<DataType, Dim, Frame::Inertial>>(\n      get<0>(x_shifted), 0.0);\n\n  const double u_star_times_t = velocity_star_ * t;\n  for (size_t s = 0; s < get_size(get<0>(x_shifted)); ++s) {\n    const double x_shifted_s = get_element(x_shifted.get(propagation_axis_), s);\n\n    size_t index = propagation_axis_ % Dim;\n    get_element(velocity.get(index), s) =\n        (x_shifted_s < u_star_times_t\n             ? left.normal_velocity(x_shifted_s, t, velocity_star_)\n             : right.normal_velocity(x_shifted_s, t, velocity_star_));\n\n    if (Dim > 1) {\n      index = (propagation_axis_ + 1) % Dim;\n      get_element(velocity.get(index), s) =\n          (x_shifted_s < u_star_times_t\n               ? gsl::at(left_initial_data_.velocity_, index)\n               : gsl::at(right_initial_data_.velocity_, index));\n    }\n\n    if (Dim > 2) {\n      index = (propagation_axis_ + 2) % Dim;\n      get_element(velocity.get(index), s) =\n          (x_shifted_s < u_star_times_t\n               ? gsl::at(left_initial_data_.velocity_, index)\n               : gsl::at(right_initial_data_.velocity_, index));\n    }\n  }\n  return velocity;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nRiemannProblem<Dim>::variables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x_shifted, const double t,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/, const Wave& left,\n    const Wave& right) const {\n  auto pressure = make_with_value<Scalar<DataType>>(x_shifted, 0.0);\n  const double u_star_times_t = velocity_star_ * t;\n  for (size_t s = 0; s < get_size(get<0>(x_shifted)); ++s) {\n    const double x_shifted_s = get_element(x_shifted.get(propagation_axis_), s);\n    get_element(get(pressure), s) =\n        (x_shifted_s < u_star_times_t\n             ? left.pressure(x_shifted_s, t, pressure_star_)\n             : right.pressure(x_shifted_s, t, pressure_star_));\n  }\n  return pressure;\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nRiemannProblem<Dim>::variables(\n    const tnsr::I<DataType, Dim, Frame::Inertial>& x_shifted, const double t,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n    const Wave& left, const Wave& right) const {\n  return equation_of_state_.specific_internal_energy_from_density_and_pressure(\n      get<hydro::Tags::RestMassDensity<DataType>>(variables(\n          x_shifted, t, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{},\n          left, right)),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(x_shifted, t, tmpl::list<hydro::Tags::Pressure<DataType>>{},\n                    left, right)));\n}\n\ntemplate <size_t Dim>\nbool operator==(const RiemannProblem<Dim>& lhs,\n                const RiemannProblem<Dim>& rhs) {\n  return lhs.adiabatic_index_ == rhs.adiabatic_index_ and\n         lhs.initial_position_ == rhs.initial_position_ and\n         lhs.left_initial_data_ == rhs.left_initial_data_ and\n         lhs.right_initial_data_ == rhs.right_initial_data_ and\n         lhs.pressure_star_tol_ == rhs.pressure_star_tol_ and\n         lhs.pressure_star_ == rhs.pressure_star_ and\n         lhs.velocity_star_ == rhs.velocity_star_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const RiemannProblem<Dim>& lhs,\n                const RiemannProblem<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE_CLASS(_, data)                            \\\n  template class RiemannProblem<DIM(data)>;                   \\\n  template bool operator==(const RiemannProblem<DIM(data)>&,  \\\n                           const RiemannProblem<DIM(data)>&); \\\n  template bool operator!=(const RiemannProblem<DIM(data)>&,  \\\n                           const RiemannProblem<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_CLASS, (1, 2, 3))\n\n#define INSTANTIATE_SCALARS(_, data)                                         \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)> >                    \\\n      RiemannProblem<DIM(data)>::variables(                                  \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x_shifted, \\\n          const double t, tmpl::list<TAG(data) < DTYPE(data)> >,             \\\n          const Wave& left, const Wave& right) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (1, 2, 3), (double, DataVector),\n                        (hydro::Tags::RestMassDensity, hydro::Tags::Pressure,\n                         hydro::Tags::SpecificInternalEnergy))\n\n#define INSTANTIATE_VELOCITY(_, data)                                        \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), DIM(data)> >         \\\n      RiemannProblem<DIM(data)>::variables(                                  \\\n          const tnsr::I<DTYPE(data), DIM(data), Frame::Inertial>& x_shifted, \\\n          const double t, tmpl::list<TAG(data) < DTYPE(data), DIM(data)> >,  \\\n          const Wave& left, const Wave& right) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VELOCITY, (1, 2, 3), (double, DataVector),\n                        (hydro::Tags::SpatialVelocity))\n\n#undef DIM\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_CLASS\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VELOCITY\n\n}  // namespace NewtonianEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/RiemannProblem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace NewtonianEuler::Solutions {\n\n/*!\n * \\brief Analytic solution to the Riemann Problem\n *\n * This class implements the exact Riemann solver described in detail in\n * Chapter 4 of \\cite Toro2009. We follow the notation there.\n * The algorithm implemented here allows for 1, 2 and 3D wave propagation\n * along any coordinate axis. Typical initial data for test cases\n * (see \\cite Toro2009) include:\n *\n * - Sod's Shock Tube (shock on the right, rarefaction on the left):\n *   - \\f$(\\rho_L, u_L, p_L) = (1.0, 0.0, 1.0)\\f$\n *   - \\f$(\\rho_R, u_R, p_R) = (0.125, 0.0, 0.1)\\f$\n *   - Recommended setup for sample run:\n *     - InitialTimeStep: 0.0001\n *     - Final time: 0.2\n *     - DomainCreator along wave propagation (no AMR):\n *       - Interval of length 1\n *       - InitialRefinement: 8\n *       - InitialGridPoints: 2\n *\n * - \"123\" problem (two symmetric rarefaction waves):\n *   - \\f$(\\rho_L, u_L, p_L) = (1.0, -2.0, 0.4)\\f$\n *   - \\f$(\\rho_R, u_R, p_R) = (1.0, 2.0, 0.4)\\f$\n *   - Recommended setup for sample run:\n *     - InitialTimeStep: 0.0001\n *     - Final time: 0.15\n *     - DomainCreator along wave propagation (no AMR):\n *       - Interval of length 1\n *       - InitialRefinement: 8\n *       - InitialGridPoints: 2\n *\n * - Collision of two blast waves (this test is challenging):\n *   - \\f$(\\rho_L, u_L, p_L) = (5.99924, 19.5975, 460.894)\\f$\n *   - \\f$(\\rho_R, u_R, p_R) = (5.99242, -6.19633, 46.0950)\\f$\n *   - Recommended setup for sample run:\n *     - InitialTimeStep: 0.00001\n *     - Final time: 0.012\n *     - DomainCreator along wave propagation (no AMR):\n *       - Interval of length 1\n *       - InitialRefinement: 8\n *       - InitialGridPoints: 2\n *\n * - Lax problem:\n *   - \\f$(\\rho_L, u_L, p_L) = (0.445, 0.698, 3.528)\\f$\n *   - \\f$(\\rho_R, u_R, p_R) = (0.5, 0.0, 0.571)\\f$\n *   - Recommended setup for sample run:\n *     - InitialTimeStep: 0.00001\n *     - Final time: 0.1\n *     - DomainCreator along wave propagation (no AMR):\n *       - Interval of length 1\n *       - InitialRefinement: 8\n *       - InitialGridPoints: 2\n *\n * where \\f$\\rho\\f$ is the mass density, \\f$p\\f$ is the pressure, and\n * \\f$u\\f$ denotes the normal velocity.\n *\n * \\note Currently the propagation axis must be hard-coded as a `size_t`\n * private member variable `propagation_axis_`, which can take one of\n * the three values `PropagationAxis::X`, `PropagationAxis::Y`, and\n * `PropagationAxis::Z`.\n *\n * \\details The algorithm makes use of the following recipe:\n *\n * - Given the initial data on both sides of the initial interface of the\n *   discontinuity (here called \"left\" and \"right\" sides, where a coordinate\n *   axis points from left to right), we compute the pressure,\n *   \\f$p_*\\f$, and the normal velocity,\n *   \\f$u_*\\f$, in the so-called star region. This is done in the constructor.\n *   Here \"normal\" refers to the normal direction to the initial interface.\n *\n * - Given the pressure and the normal velocity in the star region, two\n *   `Wave` `struct`s are created, which represent the waves propagating\n *   at later times on each side of the contact discontinuity. Each `Wave`\n *   is equipped with two `struct`s named `Shock` and `Rarefaction` which\n *   contain functions that compute the primitive variables depending on whether\n *   the wave is a shock or a rarefaction.\n *\n * - If \\f$p_* > p_K\\f$, the wave is a shock, otherwise the wave is a\n *   rarefaction. Here \\f$K\\f$ stands for \\f$L\\f$ or \\f$R\\f$: the left\n *   and right initial pressure, respectively. Since this comparison can't be\n *   performed at compile time, each `Wave` holds a `bool` member `is_shock_`\n *   which is `true` if it is a shock, and `false` if it is a\n *   rarefaction wave. This variable is used to evaluate the correct functions\n *   at run time.\n *\n * - In order to obtain the primitives at a certain time and spatial location,\n *   we evaluate whether the spatial location is on the left of the propagating\n *   contact discontinuity \\f$(x < u_* t)\\f$ or on the right \\f$(x > u_* t)\\f$,\n *   and we use the corresponding functions for left or right `Wave`s,\n *   respectively.\n *\n * \\note The characterization of each propagating wave will only\n * depend on the normal velocity, while the initial jump in the components of\n * the velocity transverse to the wave propagation will be advected at the\n * speed of the contact discontinuity (\\f$u_*\\f$).\n */\ntemplate <size_t Dim>\nclass RiemannProblem : public evolution::initial_data::InitialData,\n                       public MarkAsAnalyticSolution {\n  enum class Side { Left, Right };\n  enum PropagationAxis { X = 0, Y = 1, Z = 2 };\n\n  struct Wave;\n  struct Shock;\n  struct Rarefaction;\n\n public:\n  using equation_of_state_type = EquationsOfState::IdealFluid<false>;\n\n  /// The adiabatic index of the fluid.\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The adiabatic index of the fluid.\"};\n  };\n\n  /// Initial position of the discontinuity\n  struct InitialPosition {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial position of the discontinuity.\"};\n  };\n\n  /// The mass density on the left of the initial discontinuity\n  struct LeftMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\"The left mass density.\"};\n  };\n\n  /// The velocity on the left of the initial discontinuity\n  struct LeftVelocity {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\"The left velocity.\"};\n  };\n\n  /// The pressure on the left of the initial discontinuity\n  struct LeftPressure {\n    using type = double;\n    static constexpr Options::String help = {\"The left pressure.\"};\n  };\n\n  /// The mass density on the right of the initial discontinuity\n  struct RightMassDensity {\n    using type = double;\n    static constexpr Options::String help = {\"The right mass density.\"};\n  };\n\n  /// The velocity on the right of the initial discontinuity\n  struct RightVelocity {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\"The right velocity.\"};\n  };\n\n  /// The pressure on the right of the initial discontinuity\n  struct RightPressure {\n    using type = double;\n    static constexpr Options::String help = {\"The right pressure.\"};\n  };\n\n  /// The tolerance for solving for \\f$p_*\\f$.\n  struct PressureStarTol {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The tolerance for the numerical solution for p star\"};\n    static type suggested_value() { return 1.e-9; }\n  };\n\n  // Any of the two states that constitute the initial data, including\n  // some derived quantities that are used repeatedly at each evaluation of the\n  // different waves of the solution.\n  /// Holds initial data on a side of the discontinuity and related quantities\n  struct InitialData {\n    InitialData() = default;\n    InitialData(const InitialData& /*rhs*/) = default;\n    InitialData& operator=(const InitialData& /*rhs*/) = default;\n    InitialData(InitialData&& /*rhs*/) = default;\n    InitialData& operator=(InitialData&& /*rhs*/) = default;\n    ~InitialData() = default;\n\n    InitialData(double mass_density, const std::array<double, Dim>& velocity,\n                double pressure, double adiabatic_index,\n                size_t propagation_axis);\n\n    // clang-tidy: no runtime references\n    void pup(PUP::er& /*p*/);  //  NOLINT\n\n    double mass_density_ = std::numeric_limits<double>::signaling_NaN();\n    std::array<double, Dim> velocity_ =\n        make_array<Dim>(std::numeric_limits<double>::signaling_NaN());\n    double pressure_ = std::numeric_limits<double>::signaling_NaN();\n    double sound_speed_ = std::numeric_limits<double>::signaling_NaN();\n    double normal_velocity_ = std::numeric_limits<double>::signaling_NaN();\n\n    // Data-dependent constants A and B in Eqns. (4.8) of Toro.\n    double constant_a_ = std::numeric_limits<double>::signaling_NaN();\n    double constant_b_ = std::numeric_limits<double>::signaling_NaN();\n\n    friend bool operator==(const InitialData& lhs, const InitialData& rhs) {\n      return lhs.mass_density_ == rhs.mass_density_ and\n             lhs.velocity_ == rhs.velocity_ and lhs.pressure_ == rhs.pressure_;\n    }\n  };\n\n  using options = tmpl::list<AdiabaticIndex, InitialPosition, LeftMassDensity,\n                             LeftVelocity, LeftPressure, RightMassDensity,\n                             RightVelocity, RightPressure, PressureStarTol>;\n\n  static constexpr Options::String help = {\n      \"Riemann Problem in 1, 2 or 3D along any coordinate axis.\"};\n\n  RiemannProblem() = default;\n  RiemannProblem(const RiemannProblem& /*rhs*/) = default;\n  RiemannProblem& operator=(const RiemannProblem& /*rhs*/) = default;\n  RiemannProblem(RiemannProblem&& /*rhs*/) = default;\n  RiemannProblem& operator=(RiemannProblem&& /*rhs*/) = default;\n  ~RiemannProblem() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit RiemannProblem(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RiemannProblem);\n  /// \\endcond\n\n  RiemannProblem(double adiabatic_index, double initial_position,\n                 double left_mass_density,\n                 const std::array<double, Dim>& left_velocity,\n                 double left_pressure, double right_mass_density,\n                 const std::array<double, Dim>& right_velocity,\n                 double right_pressure,\n                 double pressure_star_tol = PressureStarTol::suggested_value());\n\n  /// Retrieve a collection of hydrodynamic variables at position `x`\n  /// and time `t`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, Dim, Frame::Inertial>& x, double t,\n      tmpl::list<Tags...> /*meta*/) const {\n    const Wave left(left_initial_data_, pressure_star_, velocity_star_,\n                    adiabatic_index_, Side::Left);\n    const Wave right(right_initial_data_, pressure_star_, velocity_star_,\n                     adiabatic_index_, Side::Right);\n\n    tnsr::I<DataType, Dim, Frame::Inertial> x_shifted(x);\n    x_shifted.get(propagation_axis_) -= initial_position_;\n\n    return {tuples::get<Tags>(\n        variables(x_shifted, t, tmpl::list<Tags>{}, left, right))...};\n  }\n\n  const EquationsOfState::IdealFluid<false>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n  // Retrieve these member variables for testing purposes.\n  constexpr std::array<double, 2> diagnostic_star_region_values() const {\n    return make_array(pressure_star_, velocity_star_);\n  }\n\n private:\n  /// @{\n  /// Retrieve hydro variable at `(x, t)`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim, Frame::Inertial>& x_shifted,\n                 double t,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n                 const Wave& left, const Wave& right) const\n      -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, Dim, Frame::Inertial>& x_shifted, double t,\n      tmpl::list<hydro::Tags::SpatialVelocity<DataType, Dim>> /*meta*/,\n      const Wave& left, const Wave& right) const\n      -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, Dim>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim, Frame::Inertial>& x_shifted,\n                 double t, tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n                 const Wave& left, const Wave& right) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, Dim, Frame::Inertial>& x_shifted, double t,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n      const Wave& left, const Wave& right) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n  /// @}\n\n  // Any of the two waves propagating on each side of the contact discontinuity.\n  // Depending on whether p_* is larger or smaller\n  // than the initial pressure on the corresponding side, the wave is a\n  // shock (larger) or a rarefaction (smaller) wave. Since p_*\n  // is computed at run time, the characterization of the wave\n  // must also be done at run time. Shock and rarefaction waves will provide\n  // different functions to retrieve the primitive variables at a given (x, t).\n  // Here normal velocity means velocity along the wave propagation.\n  struct Wave {\n    Wave(const InitialData& data, double pressure_star, double velocity_star,\n         double adiabatic_index, const Side& side);\n\n    double mass_density(double x_shifted, double t) const;\n\n    double normal_velocity(double x_shifted, double t,\n                           double velocity_star) const;\n\n    double pressure(double x_shifted, double t, double pressure_star) const;\n\n   private:\n    // p_* over initial pressure on the corresponding side\n    double pressure_ratio_ = std::numeric_limits<double>::signaling_NaN();\n    // false if rarefaction wave\n    bool is_shock_ = true;\n\n    InitialData data_{};\n    Shock shock_{};\n    Rarefaction rarefaction_{};\n  };\n\n  struct Shock {\n    Shock(const InitialData& data, double pressure_ratio,\n          double adiabatic_index, const Side& side);\n\n    double mass_density(double x_shifted, double t,\n                        const InitialData& data) const;\n    double normal_velocity(double x_shifted, double t, const InitialData& data,\n                           double velocity_star) const;\n\n    double pressure(double x_shifted, double t, const InitialData& data,\n                    double pressure_star) const;\n\n   private:\n    double direction_ = std::numeric_limits<double>::signaling_NaN();\n    double mass_density_star_ = std::numeric_limits<double>::signaling_NaN();\n    double shock_speed_ = std::numeric_limits<double>::signaling_NaN();\n  };\n\n  struct Rarefaction {\n    Rarefaction(const InitialData& data, double pressure_ratio,\n                double velocity_star, double adiabatic_index, const Side& side);\n\n    double mass_density(double x_shifted, double t,\n                        const InitialData& data) const;\n    double normal_velocity(double x_shifted, double t, const InitialData& data,\n                           double velocity_star) const;\n\n    double pressure(double x_shifted, double t, const InitialData& data,\n                    double pressure_star) const;\n\n   private:\n    double direction_ = std::numeric_limits<double>::signaling_NaN();\n    double gamma_mm_ = std::numeric_limits<double>::signaling_NaN();\n    double gamma_pp_ = std::numeric_limits<double>::signaling_NaN();\n    double mass_density_star_ = std::numeric_limits<double>::signaling_NaN();\n    double sound_speed_star_ = std::numeric_limits<double>::signaling_NaN();\n    double head_speed_ = std::numeric_limits<double>::signaling_NaN();\n    double tail_speed_ = std::numeric_limits<double>::signaling_NaN();\n  };\n\n  template <size_t SpatialDim>\n  friend bool\n  operator==(  // NOLINT (clang-tidy: readability-redundant-declaration)\n      const RiemannProblem<SpatialDim>& lhs,\n      const RiemannProblem<SpatialDim>& rhs);\n\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  double initial_position_ = std::numeric_limits<double>::signaling_NaN();\n  size_t propagation_axis_ = PropagationAxis::X;\n  InitialData left_initial_data_{};\n  InitialData right_initial_data_{};\n\n  double pressure_star_tol_ = std::numeric_limits<double>::signaling_NaN();\n  // the pressure in the star region, p_*\n  double pressure_star_ = std::numeric_limits<double>::signaling_NaN();\n  // the velocity in the star region, u_*\n  double velocity_star_ = std::numeric_limits<double>::signaling_NaN();\n\n  EquationsOfState::IdealFluid<false> equation_of_state_{};\n};\n\ntemplate <size_t Dim>\nbool operator!=(const RiemannProblem<Dim>& lhs, const RiemannProblem<Dim>& rhs);\n\n}  // namespace NewtonianEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <numeric>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace NewtonianEuler::Solutions {\n\ntemplate <size_t Dim>\nSmoothFlow<Dim>::SmoothFlow(const std::array<double, Dim>& mean_velocity,\n                            const std::array<double, Dim>& wavevector,\n                            const double pressure, const double adiabatic_index,\n                            const double perturbation_size)\n    : smooth_flow{mean_velocity, wavevector, pressure, adiabatic_index,\n                  perturbation_size} {}\n\ntemplate <size_t Dim>\nSmoothFlow<Dim>::SmoothFlow(CkMigrateMessage* msg)\n    : evolution::initial_data::InitialData(msg), smooth_flow(msg) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::initial_data::InitialData>\nSmoothFlow<Dim>::get_clone() const {\n  return std::make_unique<SmoothFlow>(*this);\n}\n\ntemplate <size_t Dim>\nvoid SmoothFlow<Dim>::pup(PUP::er& p) {\n  evolution::initial_data::InitialData::pup(p);\n  smooth_flow::pup(p);\n}\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID SmoothFlow<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nbool operator==(const SmoothFlow<Dim>& lhs, const SmoothFlow<Dim>& rhs) {\n  using smooth_flow = hydro::Solutions::SmoothFlow<Dim, false>;\n  return *static_cast<const smooth_flow*>(&lhs) ==\n         *static_cast<const smooth_flow*>(&rhs);\n}\n\ntemplate <size_t Dim>\nbool operator!=(const SmoothFlow<Dim>& lhs, const SmoothFlow<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE_CLASS(_, data)                        \\\n  template class SmoothFlow<DIM(data)>;                   \\\n  template bool operator==(const SmoothFlow<DIM(data)>&,  \\\n                           const SmoothFlow<DIM(data)>&); \\\n  template bool operator!=(const SmoothFlow<DIM(data)>&,  \\\n                           const SmoothFlow<DIM(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_CLASS, (1, 2, 3))\n\n#undef INSTANTIATE_CLASS\n#undef DIM\n}  // namespace NewtonianEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Hydro/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace NewtonianEuler::Solutions {\n/*!\n * \\brief Smooth density wave advecting across the domain.\n *\n * A solution with constant pressure and uniform spatial velocity provided\n * that the rest mass density satisfies the advection equation\n *\n * \\f{align*}{\n * \\partial_t\\rho + v^i\\partial_i\\rho = 0,\n * \\f}\n *\n * and the specific internal energy is a function of the rest mass density only,\n * \\f$\\epsilon = \\epsilon(\\rho)\\f$. For testing purposes, this class implements\n * this solution for the case where \\f$\\rho\\f$ is a sine wave. The user\n * specifies the mean flow velocity of the fluid, the wavevector of the density\n * profile, and the amplitude \\f$A\\f$ of the density profile. In Cartesian\n * coordinates \\f$(x, y, z)\\f$, and using dimensionless units, the primitive\n * variables at a given time \\f$t\\f$ are then\n *\n * \\f{align*}{\n * \\rho(\\vec{x},t) &= 1 + A \\sin(\\vec{k}\\cdot(\\vec{x} - \\vec{v}t)) \\\\\n * \\vec{v}(\\vec{x},t) &= [v_x, v_y, v_z]^{T},\\\\\n * P(\\vec{x},t) &= P, \\\\\n * \\epsilon(\\vec{x}, t) &= \\frac{P}{(\\gamma - 1)\\rho}\\\\\n * \\f}\n *\n * where we have assumed \\f$\\epsilon\\f$ and \\f$\\rho\\f$ to be related through an\n * equation mathematically equivalent to the equation of state of an ideal gas,\n * where the pressure is held constant.\n */\ntemplate <size_t Dim>\nclass SmoothFlow : public evolution::initial_data::InitialData,\n                   virtual public MarkAsAnalyticSolution,\n                   private hydro::Solutions::SmoothFlow<Dim, false> {\n  using smooth_flow = hydro::Solutions::SmoothFlow<Dim, false>;\n\n public:\n  using options = typename smooth_flow::options;\n\n  static constexpr Options::String help = {\n      \"Smooth density wave advecting across a domain.\"};\n\n  SmoothFlow() = default;\n  SmoothFlow(const SmoothFlow& /*rhs*/) = default;\n  SmoothFlow& operator=(const SmoothFlow& /*rhs*/) = default;\n  SmoothFlow(SmoothFlow&& /*rhs*/) = default;\n  SmoothFlow& operator=(SmoothFlow&& /*rhs*/) = default;\n  ~SmoothFlow() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit SmoothFlow(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SmoothFlow);\n  /// \\endcond\n\n  SmoothFlow(const std::array<double, Dim>& mean_velocity,\n             const std::array<double, Dim>& wavevector, double pressure,\n             double adiabatic_index, double perturbation_size);\n\n  using smooth_flow::equation_of_state;\n  using typename smooth_flow::equation_of_state_type;\n\n  // Overload the variables function from the base class.\n  using smooth_flow::variables;\n\n  /// Retrieve a collection of hydro variables at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, Dim>& x,\n                                         const double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {tuples::get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  template <size_t SpatialDim>\n  friend bool\n  operator==(  // NOLINT (clang-tidy: readability-redundant-declaration)\n      const SmoothFlow<SpatialDim>& lhs, const SmoothFlow<SpatialDim>& rhs);\n};\n\ntemplate <size_t Dim>\nbool operator!=(const SmoothFlow<Dim>& lhs, const SmoothFlow<Dim>& rhs);\n}  // namespace NewtonianEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace NewtonianEuler {\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing a solution to the Newtonian Euler system.\n */\nnamespace Solutions {}\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY PoissonSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Lorentzian.cpp\n  MathFunction.cpp\n  Moustache.cpp\n  ProductOfSinusoids.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Lorentzian.hpp\n  MathFunction.hpp\n  Moustache.hpp\n  ProductOfSinusoids.hpp\n  Zero.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  MathFunctions\n  Options\n  Poisson\n  Serialization\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Lorentzian.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/MathFunction.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Moustache.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/ProductOfSinusoids.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Zero.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::Solutions {\ntemplate <size_t Dim, typename DataType = DataVector>\nusing all_analytic_solutions = tmpl::conditional_t<\n    std::is_same_v<DataType, ComplexDataVector>,\n    tmpl::flatten<tmpl::list<\n        // Only a subset of solutions support ComplexDataVector\n        ProductOfSinusoids<Dim, ComplexDataVector>,\n        Zero<Dim, ComplexDataVector>,\n        tmpl::conditional_t<Dim == 3, Lorentzian<Dim, ComplexDataVector>,\n                            tmpl::list<>>>>,\n    tmpl::flatten<tmpl::list<\n        ProductOfSinusoids<Dim, DataType>, Zero<Dim, DataType>,\n        MathFunction<Dim>,\n        tmpl::conditional_t<Dim == 1 or Dim == 2, Moustache<Dim>, tmpl::list<>>,\n        tmpl::conditional_t<Dim == 3, Lorentzian<Dim, DataType>,\n                            tmpl::list<>>>>>;\n}  // namespace Poisson::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/Lorentzian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Lorentzian.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Poisson::Solutions::detail {\n\ntemplate <typename DataType, size_t Dim>\nvoid LorentzianVariables<DataType, Dim>::operator()(\n    const gsl::not_null<Scalar<DataType>*> field,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::Field<DataType> /*meta*/) const {\n  get(*field) = 1. / sqrt(1. + get(dot_product(x, x))) + constant;\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    get(*field) *= std::complex<double>{cos(complex_phase), sin(complex_phase)};\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid LorentzianVariables<DataType, Dim>::operator()(\n    const gsl::not_null<tnsr::i<DataType, Dim>*> field_gradient,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                  Frame::Inertial> /*meta*/) const {\n  DataType prefactor = -1. / cube(sqrt(1. + get(dot_product(x, x))));\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    prefactor *= std::complex<double>{cos(complex_phase), sin(complex_phase)};\n  }\n  get<0>(*field_gradient) = prefactor * get<0>(x);\n  get<1>(*field_gradient) = prefactor * get<1>(x);\n  get<2>(*field_gradient) = prefactor * get<2>(x);\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid LorentzianVariables<DataType, Dim>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                 Frame::Inertial> /*meta*/) const {\n  const auto& field_gradient = cache->get_var(\n      *this, ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                           Frame::Inertial>{});\n  for (size_t d = 0; d < Dim; ++d) {\n    flux_for_field->get(d) = field_gradient.get(d);\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid LorentzianVariables<DataType, Dim>::operator()(\n    const gsl::not_null<Scalar<DataType>*> fixed_source_for_field,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<Tags::Field<DataType>> /*meta*/) const {\n  get(*fixed_source_for_field) = 3. / pow<5>(sqrt(1. + get(dot_product(x, x))));\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    get(*fixed_source_for_field) *=\n        std::complex<double>{cos(complex_phase), sin(complex_phase)};\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data) \\\n  template class LorentzianVariables<DTYPE(data), DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, ComplexDataVector), (3))\n\n#undef DTYPE\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Poisson::Solutions::detail\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/Lorentzian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::Solutions {\n\nnamespace detail {\ntemplate <typename DataType, size_t Dim>\nstruct LorentzianVariables {\n  using Cache = CachedTempBuffer<\n      Tags::Field<DataType>,\n      ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::FixedSource<Tags::Field<DataType>>>;\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)\n  const tnsr::I<DataVector, Dim>& x;\n  double constant;\n  double complex_phase;\n\n  void operator()(gsl::not_null<Scalar<DataType>*> field,\n                  gsl::not_null<Cache*> cache,\n                  Tags::Field<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::i<DataType, Dim>*> field_gradient,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                               Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> fixed_source_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::FixedSource<Tags::Field<DataType>> /*meta*/) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief A Lorentzian solution to the Poisson equation\n *\n * \\details This implements the Lorentzian solution\n * \\f$u(\\boldsymbol{x})=\\left(1+r^2\\right)^{-\\frac{1}{2}}\\f$ to the\n * three-dimensional Poisson equation\n * \\f$-\\Delta u(\\boldsymbol{x})=f(\\boldsymbol{x})\\f$, where\n * \\f$r^2=x^2+y^2+z^2\\f$. The corresponding source is\n * \\f$f(\\boldsymbol{x})=3\\left(1+r^2\\right)^{-\\frac{5}{2}}\\f$.\n *\n * If `DataType` is `ComplexDataVector`, the solution is multiplied by\n * `exp(i * complex_phase)` to rotate it in the complex plane. This allows to\n * use this solution for the complex Poisson equation.\n *\n * \\note Corresponding 1D and 2D solutions are not implemented yet.\n */\ntemplate <size_t Dim, typename DataType = DataVector>\nclass Lorentzian : public elliptic::analytic_data::AnalyticSolution {\n  static_assert(\n      Dim == 3,\n      \"This solution is currently implemented in 3 spatial dimensions only\");\n\n public:\n  struct PlusConstant {\n    using type = double;\n    static constexpr Options::String help{\"Constant added to the solution.\"};\n  };\n\n  struct ComplexPhase {\n    using type = double;\n    static constexpr Options::String help{\n        \"Phase 'phi' of a complex exponential 'exp(i phi)' that rotates the \"\n        \"solution in the complex plane.\"};\n  };\n\n  using options = tmpl::flatten<tmpl::list<\n      PlusConstant,\n      tmpl::conditional_t<std::is_same_v<DataType, ComplexDataVector>,\n                          ComplexPhase, tmpl::list<>>>>;\n  static constexpr Options::String help{\n      \"A Lorentzian solution to the Poisson equation.\"};\n\n  Lorentzian() = default;\n  Lorentzian(const Lorentzian&) = default;\n  Lorentzian& operator=(const Lorentzian&) = default;\n  Lorentzian(Lorentzian&&) = default;\n  Lorentzian& operator=(Lorentzian&&) = default;\n  ~Lorentzian() override = default;\n\n  explicit Lorentzian(const double constant, const double complex_phase = 0.)\n      : constant_(constant), complex_phase_(complex_phase) {\n    ASSERT((std::is_same_v<DataType, ComplexDataVector> or complex_phase == 0.),\n           \"The complex phase is only supported for ComplexDataVector.\");\n  }\n\n  double constant() const { return constant_; }\n  double complex_phase() const { return complex_phase_; }\n\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<Lorentzian>(*this);\n  }\n\n  /// \\cond\n  explicit Lorentzian(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Lorentzian);  // NOLINT\n  /// \\endcond\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using VarsComputer = detail::LorentzianVariables<DataType, Dim>;\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const VarsComputer computer{x, constant_, complex_phase_};\n    return {cache.get_var(computer, RequestedTags{})...};\n  }\n\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::AnalyticSolution::pup(p);\n    p | constant_;\n    p | complex_phase_;\n  }\n\n private:\n  double constant_ = std::numeric_limits<double>::signaling_NaN();\n  double complex_phase_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <size_t Dim, typename DataType>\nPUP::able::PUP_ID Lorentzian<Dim, DataType>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\ntemplate <size_t Dim, typename DataType>\nbool operator==(const Lorentzian<Dim, DataType>& lhs,\n                const Lorentzian<Dim, DataType>& rhs) {\n  return lhs.constant() == rhs.constant() and\n         lhs.complex_phase() == rhs.complex_phase();\n}\n\ntemplate <size_t Dim, typename DataType>\nbool operator!=(const Lorentzian<Dim, DataType>& lhs,\n                const Lorentzian<Dim, DataType>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Poisson::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/MathFunction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/MathFunction.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Poisson::Solutions {\nnamespace detail {\n\ntemplate <typename DataType, size_t Dim>\nvoid MathFunctionVariables<DataType, Dim>::operator()(\n    const gsl::not_null<Scalar<DataType>*> field,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::Field<DataType> /*meta*/) const {\n  *field = math_function(x);\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid MathFunctionVariables<DataType, Dim>::operator()(\n    const gsl::not_null<tnsr::i<DataType, Dim>*> field_gradient,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                  Frame::Inertial> /*meta*/) const {\n  *field_gradient = math_function.first_deriv(x);\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid MathFunctionVariables<DataType, Dim>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                 Frame::Inertial> /*meta*/) const {\n  const auto& field_gradient = cache->get_var(\n      *this, ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                           Frame::Inertial>{});\n  for (size_t d = 0; d < Dim; ++d) {\n    flux_for_field->get(d) = field_gradient.get(d);\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid MathFunctionVariables<DataType, Dim>::operator()(\n    const gsl::not_null<Scalar<DataType>*> fixed_source_for_field,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<Tags::Field<DataType>> /*meta*/) const {\n  const auto second_deriv = math_function.second_deriv(x);\n  get(*fixed_source_for_field) = 0.;\n  for (size_t d = 0; d < Dim; ++d) {\n    get(*fixed_source_for_field) -= second_deriv.get(d, d);\n  }\n}\n\n}  // namespace detail\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID MathFunction<Dim>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <size_t Dim>\nstd::unique_ptr<elliptic::analytic_data::AnalyticSolution>\nMathFunction<Dim>::get_clone() const {\n  return std::make_unique<MathFunction>(math_function_->get_clone());\n}\n\ntemplate <size_t Dim>\nMathFunction<Dim>::MathFunction(\n    std::unique_ptr<::MathFunction<Dim, Frame::Inertial>> math_function)\n    : math_function_(std::move(math_function)) {}\n\ntemplate <size_t Dim>\nMathFunction<Dim>::MathFunction(CkMigrateMessage* m)\n    : elliptic::analytic_data::AnalyticSolution(m) {}\n\ntemplate <size_t Dim>\nvoid MathFunction<Dim>::pup(PUP::er& p) {\n  elliptic::analytic_data::AnalyticSolution::pup(p);\n  p | math_function_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const MathFunction<Dim>& lhs, const MathFunction<Dim>& rhs) {\n  return lhs.math_function() == rhs.math_function();\n}\n\ntemplate <size_t Dim>\nbool operator!=(const MathFunction<Dim>& lhs, const MathFunction<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_VARS(_, data) \\\n  template class detail::MathFunctionVariables<DTYPE(data), DIM(data)>;\n#define INSTANTIATE(_, data)                                    \\\n  template class MathFunction<DIM(data)>;                       \\\n  template bool operator==(const MathFunction<DIM(data)>& lhs,  \\\n                           const MathFunction<DIM(data)>& rhs); \\\n  template bool operator!=(const MathFunction<DIM(data)>& lhs,  \\\n                           const MathFunction<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VARS, (1, 2, 3), (DataVector))\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE_VARS\n#undef INSTANTIATE\n\n}  // namespace Poisson::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/MathFunction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::Solutions {\n\nnamespace detail {\ntemplate <typename DataType, size_t Dim>\nstruct MathFunctionVariables {\n  using Cache = CachedTempBuffer<\n      Tags::Field<DataType>,\n      ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::FixedSource<Tags::Field<DataType>>>;\n\n  const tnsr::I<DataType, Dim>& x;\n  const ::MathFunction<Dim, Frame::Inertial>& math_function;\n\n  void operator()(gsl::not_null<Scalar<DataType>*> field,\n                  gsl::not_null<Cache*> cache,\n                  Tags::Field<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::i<DataType, Dim>*> field_gradient,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                               Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> fixed_source_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::FixedSource<Tags::Field<DataType>> /*meta*/) const;\n};\n}  // namespace detail\n\ntemplate <size_t Dim>\nclass MathFunction : public elliptic::analytic_data::AnalyticSolution {\n public:\n  struct Function {\n    using type = std::unique_ptr<::MathFunction<Dim, Frame::Inertial>>;\n    static constexpr Options::String help = \"The solution function\";\n  };\n\n  using options = tmpl::list<Function>;\n  static constexpr Options::String help{\n      \"Any solution to the Poisson equation given by a MathFunction \"\n      \"implementation, such as a Gaussian.\"};\n\n  MathFunction() = default;\n  MathFunction(const MathFunction&) = delete;\n  MathFunction& operator=(const MathFunction&) = delete;\n  MathFunction(MathFunction&&) = default;\n  MathFunction& operator=(MathFunction&&) = default;\n  ~MathFunction() override = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override;\n\n  MathFunction(\n      std::unique_ptr<::MathFunction<Dim, Frame::Inertial>> math_function);\n\n  const ::MathFunction<Dim, Frame::Inertial>& math_function() const {\n    return *math_function_;\n  }\n\n  /// \\cond\n  explicit MathFunction(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(MathFunction);  // NOLINT\n  /// \\endcond\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using VarsComputer = detail::MathFunctionVariables<DataType, Dim>;\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const VarsComputer computer{x, *math_function_};\n    return {cache.get_var(computer, RequestedTags{})...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::unique_ptr<::MathFunction<Dim, Frame::Inertial>> math_function_;\n};\n\ntemplate <size_t Dim>\nbool operator==(const MathFunction<Dim>& lhs, const MathFunction<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const MathFunction<Dim>& lhs, const MathFunction<Dim>& rhs);\n\n}  // namespace Poisson::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/Moustache.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Moustache.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace Poisson::Solutions::detail {\n\ntemplate <typename DataType, size_t Dim>\nvoid MoustacheVariables<DataType, Dim>::operator()(\n    const gsl::not_null<Scalar<DataType>*> field,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::Field<DataType> /*meta*/) const {\n  std::fill(field->begin(), field->end(), 1.);\n  for (size_t d = 0; d < Dim; d++) {\n    get(*field) *= x.get(d) * (1. - x.get(d));\n  }\n  auto norm_square = make_with_value<DataType>(get<0>(x), 0.);\n  for (size_t d = 0; d < Dim; d++) {\n    norm_square += square(x.get(d) - 0.5);\n  }\n  get(*field) *= pow(norm_square, 3. / 2.);\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid MoustacheVariables<DataType, Dim>::operator()(\n    const gsl::not_null<tnsr::i<DataType, Dim>*> field_gradient,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                  Frame::Inertial> /*meta*/) const {\n  if constexpr (Dim == 1) {\n    const auto& x_d = get<0>(x);\n    get<0>(*field_gradient) =\n        abs(x_d - 0.5) *\n        evaluate_polynomial(std::array<double, 4>{{0.25, -3., 7.5, -5.}}, x_d);\n  } else if constexpr (Dim == 2) {\n    auto norm_square = square(get<0>(x) - 0.5) + square(get<1>(x) - 0.5);\n    for (size_t d = 0; d < 2; d++) {\n      const auto& x_d = x.get(d);\n      const auto& x_p = x.get((d + 1) % 2);\n      field_gradient->get(d) =\n          sqrt(norm_square) * x_p * (1. - x_p) *\n          (evaluate_polynomial(std::array<double, 4>{{0.25, -3.5, 7.5, -5.}},\n                               x_d) +\n           evaluate_polynomial(std::array<double, 3>{{0.25, -1., 1.}}, x_p) +\n           2. * x_d * x_p - 2. * x_d * square(x_p));\n    }\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid MoustacheVariables<DataType, Dim>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                 Frame::Inertial> /*meta*/) const {\n  const auto& field_gradient = cache->get_var(\n      *this, ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                           Frame::Inertial>{});\n  for (size_t d = 0; d < Dim; ++d) {\n    flux_for_field->get(d) = field_gradient.get(d);\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid MoustacheVariables<DataType, Dim>::operator()(\n    const gsl::not_null<Scalar<DataType>*> fixed_source_for_field,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::FixedSource<Tags::Field<DataType>> /*meta*/) const {\n  if constexpr (Dim == 1) {\n    const auto x1 = get<0>(x) - 0.5;\n    // This polynomial is minus the laplacian of the 1D solution\n    get(*fixed_source_for_field) = abs(x1) * (20. * square(x1) - 1.5);\n  } else if constexpr (Dim == 2) {\n    const auto x1 = get<0>(x) - 0.5;\n    const auto x2 = get<1>(x) - 0.5;\n    const auto x1_square = square(x1);\n    const auto x2_square = square(x2);\n    const auto norm_square = x1_square + x2_square;\n    // This polynomial is minus the laplacian of the 2D solution\n    get(*fixed_source_for_field) =\n        sqrt(norm_square) *\n        (-0.5625 + 6.25 * norm_square - 6.125 * square(norm_square) +\n         4.125 * square(x1_square) - 24.75 * x1_square * x2_square +\n         4.125 * square(x2_square));\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data) \\\n  template class MoustacheVariables<DTYPE(data), DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector), (1, 2))\n\n#undef DTYPE\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Poisson::Solutions::detail\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/Moustache.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::Solutions {\n\nnamespace detail {\ntemplate <typename DataType, size_t Dim>\nstruct MoustacheVariables {\n  using Cache = CachedTempBuffer<\n      Tags::Field<DataType>,\n      ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::FixedSource<Tags::Field<DataType>>>;\n\n  const tnsr::I<DataType, Dim>& x;\n\n  void operator()(gsl::not_null<Scalar<DataType>*> field,\n                  gsl::not_null<Cache*> cache,\n                  Tags::Field<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::i<DataType, Dim>*> field_gradient,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                               Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> fixed_source_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::FixedSource<Tags::Field<DataType>> /*meta*/) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief A solution to the Poisson equation with a discontinuous first\n * derivative.\n *\n * \\details This implements the solution \\f$u(x,y)=x\\left(1-x\\right)\n * y\\left(1-y\\right)\\left(\\left(x-\\frac{1}{2}\\right)^2+\\left(y-\n * \\frac{1}{2}\\right)^2\\right)^\\frac{3}{2}\\f$ to the Poisson equation\n * in two dimensions, and\n * \\f$u(x)=x\\left(1-x\\right)\\left|x-\\frac{1}{2}\\right|^3\\f$ in one dimension.\n * Their boundary conditions vanish on the square \\f$[0,1]^2\\f$ or interval\n * \\f$[0,1]\\f$, respectively.\n *\n * The corresponding source \\f$f=-\\Delta u\\f$ has a discontinuous first\n * derivative at \\f$\\frac{1}{2}\\f$. This accomplishes two things:\n *\n * - It makes it useful to test the convergence behaviour of our elliptic DG\n * solver.\n * - It makes it look like a moustache (at least in 1D).\n *\n * This solution is taken from \\cite Stamm2010.\n */\ntemplate <size_t Dim>\nclass Moustache : public elliptic::analytic_data::AnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A solution with a discontinuous first derivative of its source at 1/2 \"\n      \"that also happens to look like a moustache. It vanishes at zero and one \"\n      \"in each dimension\"};\n\n  Moustache() = default;\n  Moustache(const Moustache&) = default;\n  Moustache& operator=(const Moustache&) = default;\n  Moustache(Moustache&&) = default;\n  Moustache& operator=(Moustache&&) = default;\n  ~Moustache() override = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<Moustache>(*this);\n  }\n\n  /// \\cond\n  explicit Moustache(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Moustache);  // NOLINT\n  /// \\endcond\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using VarsComputer = detail::MoustacheVariables<DataType, Dim>;\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const VarsComputer computer{x};\n    return {cache.get_var(computer, RequestedTags{})...};\n  }\n};\n\n/// \\cond\ntemplate <size_t Dim>\nPUP::able::PUP_ID Moustache<Dim>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\ntemplate <size_t Dim>\nconstexpr bool operator==(const Moustache<Dim>& /*lhs*/,\n                          const Moustache<Dim>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim>\nconstexpr bool operator!=(const Moustache<Dim>& lhs,\n                          const Moustache<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Poisson::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/ProductOfSinusoids.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/ProductOfSinusoids.hpp\"\n\n#include <cmath>\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace Poisson::Solutions::detail {\n\ntemplate <typename DataType, size_t Dim>\nvoid ProductOfSinusoidsVariables<DataType, Dim>::operator()(\n    const gsl::not_null<Scalar<DataType>*> field,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::Field<DataType> /*meta*/) const {\n  std::fill(field->begin(), field->end(), 1.);\n  for (size_t d = 0; d < Dim; d++) {\n    get(*field) *= sin(gsl::at(wave_numbers, d) * x.get(d));\n  }\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    get(*field) *= std::complex<double>{cos(complex_phase), sin(complex_phase)};\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid ProductOfSinusoidsVariables<DataType, Dim>::operator()(\n    const gsl::not_null<tnsr::i<DataType, Dim>*> field_gradient,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                  Frame::Inertial> /*meta*/) const {\n  for (size_t d = 0; d < Dim; d++) {\n    field_gradient->get(d) =\n        gsl::at(wave_numbers, d) * cos(gsl::at(wave_numbers, d) * x.get(d));\n    for (size_t other_d = 0; other_d < Dim; other_d++) {\n      if (other_d != d) {\n        field_gradient->get(d) *=\n            sin(gsl::at(wave_numbers, other_d) * x.get(other_d));\n      }\n    }\n    if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n      field_gradient->get(d) *=\n          std::complex<double>{cos(complex_phase), sin(complex_phase)};\n    }\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid ProductOfSinusoidsVariables<DataType, Dim>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                 Frame::Inertial> /*meta*/) const {\n  const auto& field_gradient = cache->get_var(\n      *this, ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                           Frame::Inertial>{});\n  for (size_t d = 0; d < Dim; ++d) {\n    flux_for_field->get(d) = field_gradient.get(d);\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid ProductOfSinusoidsVariables<DataType, Dim>::operator()(\n    const gsl::not_null<Scalar<DataType>*> fixed_source_for_field,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::FixedSource<Tags::Field<DataType>> /*meta*/) const {\n  const auto& field = cache->get_var(*this, Tags::Field<DataType>{});\n  get(*fixed_source_for_field) = get(field) * square(magnitude(wave_numbers));\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data) \\\n  template class ProductOfSinusoidsVariables<DTYPE(data), DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector, ComplexDataVector), (1, 2, 3))\n\n#undef DTYPE\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Poisson::Solutions::detail\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/ProductOfSinusoids.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::Solutions {\n\nnamespace detail {\ntemplate <typename DataType, size_t Dim>\nstruct ProductOfSinusoidsVariables {\n  using Cache = CachedTempBuffer<\n      Tags::Field<DataType>,\n      ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      ::Tags::FixedSource<Tags::Field<DataType>>>;\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)\n  const tnsr::I<DataVector, Dim>& x;\n  std::array<double, Dim> wave_numbers;\n  double complex_phase;\n\n  void operator()(gsl::not_null<Scalar<DataType>*> field,\n                  gsl::not_null<Cache*> cache,\n                  Tags::Field<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::i<DataType, Dim>*> field_gradient,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, Dim>*> flux_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                               Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> fixed_source_for_field,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::FixedSource<Tags::Field<DataType>> /*meta*/) const;\n};\n}  // namespace detail\n\n/*!\n * \\brief A product of sinusoids \\f$u(\\boldsymbol{x}) = \\prod_i \\sin(k_i x_i)\\f$\n *\n * \\details Solves the Poisson equation \\f$-\\Delta u(x)=f(x)\\f$ for a source\n * \\f$f(x)=\\boldsymbol{k}^2\\prod_i \\sin(k_i x_i)\\f$.\n *\n * If `DataType` is `ComplexDataVector`, the solution is multiplied by\n * `exp(i * complex_phase)` to rotate it in the complex plane. This allows to\n * use this solution for the complex Poisson equation.\n */\ntemplate <size_t Dim, typename DataType = DataVector>\nclass ProductOfSinusoids : public elliptic::analytic_data::AnalyticSolution {\n public:\n  struct WaveNumbers {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help{\"The wave numbers of the sinusoids\"};\n  };\n\n  struct ComplexPhase {\n    using type = double;\n    static constexpr Options::String help{\n        \"Phase 'phi' of a complex exponential 'exp(i phi)' that rotates the \"\n        \"solution in the complex plane.\"};\n  };\n\n  using options = tmpl::flatten<tmpl::list<\n      WaveNumbers,\n      tmpl::conditional_t<std::is_same_v<DataType, ComplexDataVector>,\n                          ComplexPhase, tmpl::list<>>>>;\n  static constexpr Options::String help{\n      \"A product of sinusoids that are taken of a wave number times the \"\n      \"coordinate in each dimension.\"};\n\n  ProductOfSinusoids() = default;\n  ProductOfSinusoids(const ProductOfSinusoids&) = default;\n  ProductOfSinusoids& operator=(const ProductOfSinusoids&) = default;\n  ProductOfSinusoids(ProductOfSinusoids&&) = default;\n  ProductOfSinusoids& operator=(ProductOfSinusoids&&) = default;\n  ~ProductOfSinusoids() override = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<ProductOfSinusoids>(*this);\n  }\n\n  /// \\cond\n  explicit ProductOfSinusoids(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ProductOfSinusoids);  // NOLINT\n  /// \\endcond\n\n  explicit ProductOfSinusoids(const std::array<double, Dim>& wave_numbers,\n                              const double complex_phase = 0.)\n      : wave_numbers_(wave_numbers), complex_phase_(complex_phase) {\n    ASSERT((std::is_same_v<DataType, ComplexDataVector> or complex_phase == 0.),\n           \"The complex phase is only supported for ComplexDataVector.\");\n  }\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using VarsComputer = detail::ProductOfSinusoidsVariables<DataType, Dim>;\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const VarsComputer computer{x, wave_numbers_, complex_phase_};\n    return {cache.get_var(computer, RequestedTags{})...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::AnalyticSolution::pup(p);\n    p | wave_numbers_;\n    p | complex_phase_;\n  }\n\n  const std::array<double, Dim>& wave_numbers() const { return wave_numbers_; }\n  double complex_phase() const { return complex_phase_; }\n\n private:\n  std::array<double, Dim> wave_numbers_{\n      {std::numeric_limits<double>::signaling_NaN()}};\n  double complex_phase_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <size_t Dim, typename DataType>\nPUP::able::PUP_ID ProductOfSinusoids<Dim, DataType>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\ntemplate <size_t Dim, typename DataType>\nbool operator==(const ProductOfSinusoids<Dim, DataType>& lhs,\n                const ProductOfSinusoids<Dim, DataType>& rhs) {\n  return lhs.wave_numbers() == rhs.wave_numbers() and\n         lhs.complex_phase() == rhs.complex_phase();\n}\n\ntemplate <size_t Dim, typename DataType>\nbool operator!=(const ProductOfSinusoids<Dim, DataType>& lhs,\n                const ProductOfSinusoids<Dim, DataType>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Poisson::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Poisson/Zero.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::Solutions {\n\n/// The trivial solution \\f$u=0\\f$ of a Poisson equation. Useful as initial\n/// guess.\ntemplate <size_t Dim, typename DataType = DataVector>\nclass Zero : public elliptic::analytic_data::AnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"The trivial solution, useful as initial guess.\"};\n\n  Zero() = default;\n  Zero(const Zero&) = default;\n  Zero& operator=(const Zero&) = default;\n  Zero(Zero&&) = default;\n  Zero& operator=(Zero&&) = default;\n  ~Zero() override = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<Zero>(*this);\n  }\n\n  /// \\cond\n  explicit Zero(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Zero);  // NOLINT\n  /// \\endcond\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using supported_tags = tmpl::list<\n        Tags::Field<DataType>,\n        ::Tags::deriv<Tags::Field<DataType>, tmpl::size_t<Dim>,\n                      Frame::Inertial>,\n        ::Tags::Flux<Tags::Field<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n        ::Tags::FixedSource<Tags::Field<DataType>>>;\n    static_assert(tmpl::size<tmpl::list_difference<tmpl::list<RequestedTags...>,\n                                                   supported_tags>>::value == 0,\n                  \"The requested tag is not supported\");\n    return {make_with_value<typename RequestedTags::type>(x, 0.)...};\n  }\n};\n\n/// \\cond\ntemplate <size_t Dim, typename DataType>\nPUP::able::PUP_ID Zero<Dim, DataType>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n\ntemplate <size_t Dim, typename DataType>\nbool operator==(const Zero<Dim, DataType>& /*lhs*/,\n                const Zero<Dim, DataType>& /*rhs*/) {\n  return true;\n}\n\ntemplate <size_t Dim, typename DataType>\nbool operator!=(const Zero<Dim, DataType>& lhs,\n                const Zero<Dim, DataType>& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Poisson::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY PuncturesSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Flatness.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Flatness.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Options\n  Punctures\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Punctures/Flatness.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Punctures/Flatness.hpp\"\n\nnamespace Punctures::Solutions {\nbool operator==(const Flatness& /*lhs*/, const Flatness& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const Flatness& lhs, const Flatness& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID Flatness::my_PUP_ID = 0;  // NOLINT\n}  // namespace Punctures::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Punctures/Flatness.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Punctures/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures::Solutions {\n\n/// Flat spacetime. Useful as initial guess.\nclass Flatness : public elliptic::analytic_data::AnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Flat spacetime, useful as initial guess.\"};\n\n  Flatness() = default;\n  Flatness(const Flatness&) = default;\n  Flatness& operator=(const Flatness&) = default;\n  Flatness(Flatness&&) = default;\n  Flatness& operator=(Flatness&&) = default;\n  ~Flatness() = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<Flatness>(*this);\n  }\n\n  /// \\cond\n  explicit Flatness(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Flatness);\n  /// \\endcond\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    // These are all zero\n    using supported_tags =\n        tmpl::list<Tags::Field,\n                   ::Tags::deriv<Tags::Field, tmpl::size_t<3>, Frame::Inertial>,\n                   ::Tags::Flux<Tags::Field, tmpl::size_t<3>, Frame::Inertial>,\n                   Punctures::Tags::Alpha,\n                   Punctures::Tags::TracelessConformalExtrinsicCurvature,\n                   Punctures::Tags::Beta,\n                   ::Tags::FixedSource<Punctures::Tags::Field>>;\n    static_assert(\n        std::is_same_v<\n            tmpl::list_difference<tmpl::list<RequestedTags...>, supported_tags>,\n            tmpl::list<>>,\n        \"Not all requested tags are supported. The static_assert lists the \"\n        \"unsupported tags.\");\n    const auto make_value = [&x](auto tag_v) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      return make_with_value<typename tag::type>(x, 0.);\n    };\n    return {make_value(RequestedTags{})...};\n  }\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x, const Mesh<3>& /*mesh*/,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& /*inv_jacobian*/,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables(x, tmpl::list<RequestedTags...>{});\n  }\n};\n\nbool operator==(const Flatness& lhs, const Flatness& rhs);\n\nbool operator!=(const Flatness& lhs, const Flatness& rhs);\n\n}  // namespace Punctures::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(M1Grey)\nadd_subdirectory(MonteCarlo)\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY M1GreySolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ConstantM1.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ConstantM1.hpp\n  Factory.hpp\n  Solutions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  Hydro\n  Options\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/ConstantM1.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/ConstantM1.hpp\"\n\n#include <algorithm>\n#include <boost/preprocessor/punctuation/comma_if.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <cmath>\n#include <numeric>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace RadiationTransport::M1Grey::Solutions {\n\nConstantM1::ConstantM1(CkMigrateMessage* msg) : InitialData(msg) {}\n\nConstantM1::ConstantM1(const std::array<double, 3>& mean_velocity,\n                       const double comoving_energy_density)\n    :  // clang-tidy: do not std::move trivial types.\n      mean_velocity_(std::move(mean_velocity)),  // NOLINT\n      comoving_energy_density_(comoving_energy_density) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> ConstantM1::get_clone()\n    const {\n  return std::make_unique<ConstantM1>(*this);\n}\n\nvoid ConstantM1::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | mean_velocity_;\n  p | comoving_energy_density_;\n  p | background_spacetime_;\n}\n// NOLINTNEXTLINE\nPUP::able::PUP_ID ConstantM1::my_PUP_ID = 0;\n\n// Variables templated on neutrino species.\ntemplate <typename NeutrinoSpecies>\ntuples::TaggedTuple<\n    RadiationTransport::M1Grey::Tags::TildeE<Frame::Inertial, NeutrinoSpecies>>\nConstantM1::variables(const tnsr::I<DataVector, 3>& x, double /*t*/,\n                      tmpl::list<RadiationTransport::M1Grey::Tags::TildeE<\n                          Frame::Inertial, NeutrinoSpecies>> /*meta*/) const {\n  const double W_sqr =\n      1. /\n      (1. - std::inner_product(mean_velocity_.begin(), mean_velocity_.end(),\n                               mean_velocity_.begin(), 0.));\n  return {Scalar<DataVector>{DataVector(\n      get<0>(x).size(), comoving_energy_density_ / 3. * (4. * W_sqr - 1.))}};\n}\n\ntemplate <typename NeutrinoSpecies>\ntuples::TaggedTuple<\n    RadiationTransport::M1Grey::Tags::TildeS<Frame::Inertial, NeutrinoSpecies>>\nConstantM1::variables(const tnsr::I<DataVector, 3>& x, double /*t*/,\n                      tmpl::list<RadiationTransport::M1Grey::Tags::TildeS<\n                          Frame::Inertial, NeutrinoSpecies>> /*meta*/) const {\n  const double W_sqr =\n      1. /\n      (1. - std::inner_product(mean_velocity_.begin(), mean_velocity_.end(),\n                               mean_velocity_.begin(), 0.));\n  const double prefactor = 4. / 3. * comoving_energy_density_ * W_sqr;\n  auto result =\n      make_with_value<tnsr::i<DataVector, 3>>(x, mean_velocity_[0] * prefactor);\n  get<1>(result) = mean_velocity_[1] * prefactor;\n  get<2>(result) = mean_velocity_[2] * prefactor;\n  return {std::move(result)};\n}\n\ntemplate <typename NeutrinoSpecies>\ntuples::TaggedTuple<\n    RadiationTransport::M1Grey::Tags::GreyEmissivity<NeutrinoSpecies>>\nConstantM1::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<RadiationTransport::M1Grey::Tags::GreyEmissivity<\n        NeutrinoSpecies>> /*meta*/) const {\n  return {Scalar<DataVector>{DataVector(get<0>(x).size(), 0.)}};\n}\n\ntemplate <typename NeutrinoSpecies>\ntuples::TaggedTuple<\n    RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<NeutrinoSpecies>>\nConstantM1::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<\n        NeutrinoSpecies>> /*meta*/) const {\n  return {Scalar<DataVector>{DataVector(get<0>(x).size(), 0.)}};\n}\n\ntemplate <typename NeutrinoSpecies>\ntuples::TaggedTuple<\n    RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<NeutrinoSpecies>>\nConstantM1::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<\n        NeutrinoSpecies>> /*meta*/) const {\n  return {Scalar<DataVector>{DataVector(get<0>(x).size(), 0.)}};\n}\n\n// Variables not templated on neutrino species.\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataVector>>\nConstantM1::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::LorentzFactor<DataVector>> /*meta*/) const {\n  const double W =\n      1. /\n      sqrt(1. - std::inner_product(mean_velocity_.begin(), mean_velocity_.end(),\n                                   mean_velocity_.begin(), 0.));\n  return {Scalar<DataVector>{DataVector(get<0>(x).size(), W)}};\n}\n\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataVector, 3>>\nConstantM1::variables(\n    const tnsr::I<DataVector, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3>> /*meta*/) const {\n  auto result = make_with_value<tnsr::I<DataVector, 3>>(x, mean_velocity_[0]);\n  get<1>(result) = mean_velocity_[1];\n  get<2>(result) = mean_velocity_[2];\n  return {std::move(result)};\n}\n\nbool operator==(const ConstantM1& lhs, const ConstantM1& rhs) {\n  return lhs.mean_velocity_ == rhs.mean_velocity_ and\n         lhs.comoving_energy_density_ == rhs.comoving_energy_density_ and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\nbool operator!=(const ConstantM1& lhs, const ConstantM1& rhs) {\n  return not(lhs == rhs);\n}\n\n#define TAG(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define NTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define EBIN(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define GENERATE_LIST(z, n, _) BOOST_PP_COMMA_IF(n) n\n\n#define INSTANTIATE_M1_FUNCTION_WITH_FRAME(_, data)                            \\\n  template tuples::TaggedTuple<TAG(data) < Frame::Inertial,                    \\\n                               NTYPE(data) < EBIN(data)> >>                    \\\n      ConstantM1::variables(                                                   \\\n          const tnsr::I<DataVector, 3>& x, double t,                           \\\n          tmpl::list<TAG(data) < Frame::Inertial, NTYPE(data) < EBIN(data)> >> \\\n          /*meta*/) const;\n\n#define TEMP_LIST \\\n  (BOOST_PP_REPEAT(MAX_NUMBER_OF_NEUTRINO_ENERGY_BINS, GENERATE_LIST, _))\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_M1_FUNCTION_WITH_FRAME,\n                        (RadiationTransport::M1Grey::Tags::TildeE,\n                         RadiationTransport::M1Grey::Tags::TildeS),\n                        (neutrinos::ElectronNeutrinos,\n                         neutrinos::ElectronAntiNeutrinos,\n                         neutrinos::HeavyLeptonNeutrinos),\n                        TEMP_LIST)\n\n#undef TEMP_LIST\n#undef INSTANTIATE_M1_FUNCTION_WITH_FRAME\n#undef TAG\n#undef NTYPE\n#undef EBIN\n#undef GENERATE_LIST\n\n#define TAG(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define NTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define EBIN(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define GENERATE_LIST(z, n, _) BOOST_PP_COMMA_IF(n) n\n\n#define INSTANTIATE_M1_FUNCTION(_, data)                                \\\n  template tuples::TaggedTuple<TAG(data) < NTYPE(data) < EBIN(data)> >> \\\n      ConstantM1::variables(                                            \\\n          const tnsr::I<DataVector, 3>& x, double t,                    \\\n          tmpl::list<TAG(data) < NTYPE(data) < EBIN(data)> >>           \\\n          /*meta*/) const;\n\n#define TEMP_LIST \\\n  (BOOST_PP_REPEAT(MAX_NUMBER_OF_NEUTRINO_ENERGY_BINS, GENERATE_LIST, _))\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_M1_FUNCTION,\n    (RadiationTransport::M1Grey::Tags::GreyEmissivity,\n     RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity,\n     RadiationTransport::M1Grey::Tags::GreyScatteringOpacity),\n    (neutrinos::ElectronNeutrinos, neutrinos::ElectronAntiNeutrinos,\n     neutrinos::HeavyLeptonNeutrinos),\n    TEMP_LIST)\n\n#undef INSTANTIATE_M1_FUNCTION\n#undef TEMP_LIST\n#undef TAG\n#undef NTYPE\n#undef EBIN\n#undef GENERATE_LIST\n\n}  // namespace RadiationTransport::M1Grey::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/ConstantM1.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace RadiationTransport::M1Grey::Solutions {\n\n/*!\n * \\brief Constant solution to M1 equations in Minkowski spacetime.\n *\n * An analytic solution to the 3-D M1 system. The user specifies the mean\n * flow velocity of the fluid and the radiation energy density in the\n * fluid frame J.\n * The radiation is taken to be in equilibrium with the fluid\n * (i.e. comoving, with an isotropic pressure P=J/3)\n *\n */\n\nclass ConstantM1 : public virtual evolution::initial_data::InitialData,\n                   public MarkAsAnalyticSolution {\n public:\n  /// The mean flow velocity.\n  struct MeanVelocity {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\"The mean flow velocity.\"};\n  };\n\n  /// The radiation comoving energy density\n  struct ComovingEnergyDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Comoving energy density of radiation.\"};\n  };\n\n  using options = tmpl::list<MeanVelocity, ComovingEnergyDensity>;\n  static constexpr Options::String help = {\n      \"Constant radiation field in equilibrium with fluid, in Minkowski \"\n      \"spacetime.\"};\n\n  ConstantM1() = default;\n  ConstantM1(const ConstantM1& /*rhs*/) = default;\n  ConstantM1& operator=(const ConstantM1& /*rhs*/) = default;\n  ConstantM1(ConstantM1&& /*rhs*/) = default;\n  ConstantM1& operator=(ConstantM1&& /*rhs*/) = default;\n  ~ConstantM1() = default;\n\n  ConstantM1(const std::array<double, 3>& mean_velocity,\n             double comoving_energy_density);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  explicit ConstantM1(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ConstantM1);\n\n  /// @{\n  /// Retrieve fluid and neutrino variables at `(x, t)`\n  template <typename NeutrinoSpecies>\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<RadiationTransport::M1Grey::Tags::TildeE<\n                     Frame::Inertial, NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::TildeE<\n          Frame::Inertial, NeutrinoSpecies>>;\n\n  template <typename NeutrinoSpecies>\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<RadiationTransport::M1Grey::Tags::TildeS<\n                     Frame::Inertial, NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::TildeS<\n          Frame::Inertial, NeutrinoSpecies>>;\n\n  template <typename NeutrinoSpecies>\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<RadiationTransport::M1Grey::Tags::GreyEmissivity<\n                     NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<\n          RadiationTransport::M1Grey::Tags::GreyEmissivity<NeutrinoSpecies>>;\n\n  template <typename NeutrinoSpecies>\n  auto variables(\n      const tnsr::I<DataVector, 3>& x, double t,\n      tmpl::list<RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<\n          NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::\n                                 GreyAbsorptionOpacity<NeutrinoSpecies>>;\n\n  template <typename NeutrinoSpecies>\n  auto variables(\n      const tnsr::I<DataVector, 3>& x, double t,\n      tmpl::list<RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<\n          NeutrinoSpecies>> /*meta*/) const\n      -> tuples::TaggedTuple<RadiationTransport::M1Grey::Tags::\n                                 GreyScatteringOpacity<NeutrinoSpecies>>;\n\n  auto variables(const tnsr::I<DataVector, 3>& x, double t,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataVector>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataVector>>;\n\n  auto variables(\n      const tnsr::I<DataVector, 3>& x, double t,\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataVector, 3>>;\n  /// @}\n\n  /// Retrieve a collection of fluid and neutrino variables at `(x, t)`\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 3>& x,\n                                         double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataVector, 3>& x, double t,\n                                     tmpl::list<Tag> /*meta*/) const {\n    return background_spacetime_.variables(x, t, tmpl::list<Tag>{});\n  }\n\n  // clang-tidy: no runtime references\n  void pup(PUP::er& /*p*/) override;  //  NOLINT\n\n private:\n  friend bool operator==(const ConstantM1& lhs, const ConstantM1& rhs);\n\n  std::array<double, 3> mean_velocity_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  double comoving_energy_density_ =\n      std::numeric_limits<double>::signaling_NaN();\n  gr::Solutions::Minkowski<3> background_spacetime_{};\n};\n\nbool operator!=(const ConstantM1& lhs, const ConstantM1& rhs);\n}  // namespace RadiationTransport::M1Grey::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/ConstantM1.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RadiationTransport::M1Grey::Solutions {\n/// \\brief List of all analytic solutions\nusing all_solutions = tmpl::list<ConstantM1>;\n}  // namespace RadiationTransport::M1Grey::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace RadiationTransport {\nnamespace M1Grey {\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing a solution to the M1 system.\n */\nnamespace Solutions {}\n}  // namespace M1Grey\n}  // namespace RadiationTransport\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY MonteCarloSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  HomogeneousSphere.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  HomogeneousSphere.hpp\n  Solutions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  Hydro\n  MonteCarlo\n  Options\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/HomogeneousSphere.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RadiationTransport::MonteCarlo::Solutions {\n/// \\brief List of all analytic solutions\nusing all_solutions = tmpl::list<HomogeneousSphere>;\n}  // namespace RadiationTransport::MonteCarlo::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/HomogeneousSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/HomogeneousSphere.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace RadiationTransport::MonteCarlo::Solutions {\n\nnamespace {\ndouble get_mask(const tnsr::I<double, 3>& x, const double radius_bound) {\n  const double radius = get(magnitude(x));\n  return radius > radius_bound ? 1.0 : 0.0;\n}\n\nDataVector get_mask(const tnsr::I<DataVector, 3>& x,\n                    const double radius_bound) {\n  const DataVector radius = get(magnitude(x));\n  DataVector mask = make_with_value<DataVector>(radius, 0.0);\n  for (size_t i = 0; i < mask.size(); i++) {\n    mask[i] = radius[i] > radius_bound ? 1.0 : 0.0;\n  }\n  return mask;\n}\n}  // namespace\n\nHomogeneousSphere::HomogeneousSphere(\n    const double& radius, const std::array<double, 2>& densities,\n    const std::array<double, 2>& temperatures,\n    const std::array<double, 2>& electron_fractions,\n    std::unique_ptr<EquationsOfState::EquationOfState<IsRelativistic, 3>>\n        local_eos)\n    : radius_(radius),\n      densities_(densities),\n      temperatures_(temperatures),\n      electron_fractions_(electron_fractions),\n      equation_of_state_(std::move(local_eos)) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nHomogeneousSphere::get_clone() const {\n  return std::make_unique<HomogeneousSphere>(*this);\n}\n\nHomogeneousSphere::HomogeneousSphere(CkMigrateMessage* /*unused*/) {}\n\nHomogeneousSphere::HomogeneousSphere(const HomogeneousSphere& rhs)\n    : evolution::initial_data::InitialData(rhs),\n      radius_(rhs.radius_),\n      densities_(rhs.densities_),\n      temperatures_(rhs.temperatures_),\n      electron_fractions_(rhs.electron_fractions_),\n      equation_of_state_(rhs.equation_of_state_->get_clone()) {}\n\nHomogeneousSphere& HomogeneousSphere::operator=(const HomogeneousSphere& rhs) {\n  radius_ = rhs.radius_;\n  densities_ = rhs.densities_;\n  temperatures_ = rhs.temperatures_;\n  electron_fractions_ = rhs.electron_fractions_;\n  equation_of_state_ = rhs.equation_of_state_->get_clone();\n  return *this;\n}\n\nvoid HomogeneousSphere::pup(PUP::er& p) {\n  p | radius_;\n  p | densities_;\n  p | temperatures_;\n  p | electron_fractions_;\n  p | equation_of_state_;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n  const DataType mask = get_mask(x, radius_);\n  return {Scalar<DataType>{DataType{electron_fractions_[1] * mask +\n                                    electron_fractions_[0] * (1.0 - mask)}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  const DataType mask = get_mask(x, radius_);\n  return {Scalar<DataType>{\n      DataType{densities_[1] * mask + densities_[0] * (1.0 - mask)}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Temperature<DataType>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const {\n  const DataType mask = get_mask(x, radius_);\n  return {Scalar<DataType>{\n      DataType{temperatures_[1] * mask + temperatures_[0] * (1.0 - mask)}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 1.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double t,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  auto primitives = this->variables<DataType>(\n      x, t,\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::Temperature<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>>{});\n  return equation_of_state_\n      ->specific_internal_energy_from_density_and_temperature(\n          tuples::get<hydro::Tags::RestMassDensity<DataType>>(primitives),\n          tuples::get<hydro::Tags::Temperature<DataType>>(primitives),\n          tuples::get<hydro::Tags::ElectronFraction<DataType>>(primitives));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double t,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  auto primitives = this->variables<DataType>(\n      x, t,\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::Temperature<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>>{});\n  return equation_of_state_->pressure_from_density_and_temperature(\n      tuples::get<hydro::Tags::RestMassDensity<DataType>>(primitives),\n      tuples::get<hydro::Tags::Temperature<DataType>>(primitives),\n      tuples::get<hydro::Tags::ElectronFraction<DataType>>(primitives));\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double t,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  auto primitives = this->variables<DataType>(\n      x, t,\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>>{});\n  return tuples::get<hydro::Tags::Pressure<DataType>>(primitives) /\n             tuples::get<hydro::Tags::RestMassDensity<DataType>>(primitives) +\n         tuples::get<hydro::Tags::SpecificInternalEnergy<DataType>>(\n             primitives) +\n         1.0;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  return {make_with_value<tnsr::I<DataType, 3>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  return {make_with_value<tnsr::I<DataType, 3>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nHomogeneousSphere::variables(\n    const tnsr::I<DataType, 3>& x, double /*t*/,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\nPUP::able::PUP_ID HomogeneousSphere::my_PUP_ID = 0;  // NOLINT\n\nbool operator==(const HomogeneousSphere& lhs, const HomogeneousSphere& rhs) {\n  return (lhs.radius_ == rhs.radius_ && lhs.densities_ == rhs.densities_ &&\n          lhs.temperatures_ == rhs.temperatures_ &&\n          lhs.electron_fractions_ == rhs.electron_fractions_ &&\n          *lhs.equation_of_state_ == *rhs.equation_of_state_);\n}\n\nbool operator!=(const HomogeneousSphere& lhs, const HomogeneousSphere& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                      \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)> > \\\n      HomogeneousSphere::variables(                       \\\n          const tnsr::I<DTYPE(data), 3>& x, double t,     \\\n          tmpl::list<TAG(data) < DTYPE(data)> > /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (double, DataVector),\n                        (hydro::Tags::DivergenceCleaningField,\n                         hydro::Tags::RestMassDensity,\n                         hydro::Tags::ElectronFraction,\n                         hydro::Tags::Temperature, hydro::Tags::LorentzFactor,\n                         hydro::Tags::SpecificInternalEnergy,\n                         hydro::Tags::Pressure))\n\n#define INSTANTIATE_VECTORS(_, data)                         \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3> > \\\n      HomogeneousSphere::variables(                          \\\n          const tnsr::I<DTYPE(data), 3>& x, double t,        \\\n          tmpl::list<TAG(data) < DTYPE(data), 3> > /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_VECTORS, (double, DataVector),\n                        (hydro::Tags::MagneticField,\n                         hydro::Tags::SpatialVelocity))\n\n#undef DTYPE\n#undef TAG\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_VECTORS\n}  // namespace RadiationTransport::MonteCarlo::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/HomogeneousSphere.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace RadiationTransport::MonteCarlo::Solutions {\n\n/*!\n * \\brief Homogeneous sphere as fluid background to MC run\n *\n * Provides background fluid variables for a\n * fluid with constant density, temperature, Ye\n * in Minkowski spacetime.\n *\n */\nclass HomogeneousSphere : public evolution::initial_data::InitialData,\n                          public MarkAsAnalyticSolution {\n public:\n  constexpr static bool IsRelativistic = true;\n  using equation_of_state_type =\n      EquationsOfState::EquationOfState<IsRelativistic, 3>;\n\n  const EquationsOfState::EquationOfState<IsRelativistic, 3>&\n  equation_of_state() const {\n    return *equation_of_state_;\n  }\n\n  static const size_t volume_dim = 3;\n\n  /// The radius of the sphere\n  struct Radius {\n    using type = double;\n    static constexpr Options::String help = {\"The radius of the sphere.\"};\n  };\n  /// The density inside and outside the sphere\n  struct Densities {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\"Density inside and outside.\"};\n  };\n  /// The temperature inside and outside the sphere\n  struct Temperatures {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\"Temperature inside and outside.\"};\n  };\n  /// The electron fraction inside and outside the sphere\n  struct ElectronFractions {\n    using type = std::array<double, 2>;\n    static constexpr Options::String help = {\"Ye inside and outside.\"};\n  };\n\n  using options = tmpl::list<\n      Radius, Densities, Temperatures, ElectronFractions,\n      hydro::OptionTags::InitialDataEquationOfState<IsRelativistic, 3>>;\n  static constexpr Options::String help = {\n      \"Background for uniform sphere with constant rho, T, Ye\"};\n\n  HomogeneousSphere() = default;\n  HomogeneousSphere(const HomogeneousSphere& /*rhs*/);\n  HomogeneousSphere& operator=(const HomogeneousSphere& /*rhs*/);\n  HomogeneousSphere(HomogeneousSphere&& /*rhs*/) = default;\n  HomogeneousSphere& operator=(HomogeneousSphere&& /*rhs*/) = default;\n  ~HomogeneousSphere() override = default;\n\n  HomogeneousSphere(\n      const double& radius, const std::array<double, 2>& densities,\n      const std::array<double, 2>& temperatures,\n      const std::array<double, 2>& electron_fractions,\n      std::unique_ptr<EquationsOfState::EquationOfState<IsRelativistic, 3>>\n          local_eos);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit HomogeneousSphere(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(HomogeneousSphere);\n  /// \\endcond\n\n  /// @{\n  /// Retrieve fluid variables at `(x, t)`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x, double /*t*/,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x, double /*t*/,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n  /// @}\n\n  /// Retrieve a collection of hydro variables at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag,\n            Requires<not tmpl::list_contains_v<\n                tmpl::push_back<hydro::grmhd_tags<DataType>,\n                                hydro::Tags::SpecificEnthalpy<DataType>>,\n                Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     double t, tmpl::list<Tag> /*meta*/) const {\n    return background_spacetime_.variables(x, t, tmpl::list<Tag>{});\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  friend bool operator==(const HomogeneousSphere& lhs,\n                         const HomogeneousSphere& rhs);\n\n  double radius_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 2> densities_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 2> temperatures_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n  std::array<double, 2> electron_fractions_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n\n  std::unique_ptr<equation_of_state_type> equation_of_state_;\n  gh::Solutions::WrappedGr<gr::Solutions::Minkowski<3>>\n      background_spacetime_{};\n};\n\nbool operator!=(const HomogeneousSphere& lhs, const HomogeneousSphere& rhs);\n}  // namespace RadiationTransport::MonteCarlo::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes providing background fluid/metric for MC\n */\nnamespace RadiationTransport::MonteCarlo::Solutions {\n}  // namespace RadiationTransport::MonteCarlo::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY RelativisticEulerSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  FishboneMoncriefDisk.cpp\n  RotatingStar.cpp\n  SmoothFlow.cpp\n  Tov.cpp\n  TovStar.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FishboneMoncriefDisk.hpp\n  RotatingStar.hpp\n  SmoothFlow.hpp\n  Solutions.hpp\n  Tov.hpp\n  TovStar.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  Hydro\n  HydroSolutions\n  Options\n  Serialization\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace RelativisticEuler::Solutions {\n\nFishboneMoncriefDisk::FishboneMoncriefDisk(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nFishboneMoncriefDisk::FishboneMoncriefDisk(const double bh_mass,\n                                           const double bh_dimless_spin,\n                                           const double inner_edge_radius,\n                                           const double max_pressure_radius,\n                                           const double polytropic_constant,\n                                           const double polytropic_exponent,\n                                           const double noise)\n    : bh_mass_(bh_mass),\n      bh_spin_a_(bh_mass * bh_dimless_spin),\n      inner_edge_radius_(bh_mass * inner_edge_radius),\n      max_pressure_radius_(bh_mass * max_pressure_radius),\n      polytropic_constant_(polytropic_constant),\n      polytropic_exponent_(polytropic_exponent),\n      noise_(noise),\n      equation_of_state_{polytropic_constant_, polytropic_exponent_},\n      background_spacetime_{\n          bh_mass_, {{0.0, 0.0, bh_dimless_spin}}, {{0.0, 0.0, 0.0}}},\n      background_pressure_(background_density_ * background_temperature_),\n      background_specific_internal_energy_(background_temperature_ /\n                                           (polytropic_exponent_ - 1.)) {\n  const double sqrt_m = sqrt(bh_mass_);\n  const double a_sqrt_m = bh_spin_a_ * sqrt_m;\n  const double& rmax = max_pressure_radius_;\n  const double sqrt_rmax = sqrt(rmax);\n  const double rmax_sqrt_rmax = rmax * sqrt_rmax;\n  const double rmax_squared = square(rmax);\n  angular_momentum_ =\n      sqrt_m * (rmax_sqrt_rmax + a_sqrt_m) *\n      (square(bh_spin_a_) - 2.0 * a_sqrt_m * sqrt_rmax + rmax_squared) /\n      (2.0 * a_sqrt_m * rmax_sqrt_rmax +\n       (rmax - 3.0 * bh_mass_) * rmax_squared);\n\n  // compute rho_max_ here:\n  const double potential_at_rmax = potential(rmax_squared, 1.0);\n  const double potential_at_rin = potential(square(inner_edge_radius_), 1.0);\n  const double h_at_rmax = exp(potential_at_rin - potential_at_rmax);\n  rho_max_ = get(equation_of_state_.rest_mass_density_from_enthalpy(\n      Scalar<double>{h_at_rmax}));\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nFishboneMoncriefDisk::get_clone() const {\n  return std::make_unique<FishboneMoncriefDisk>(*this);\n}\n\nvoid FishboneMoncriefDisk::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | bh_mass_;\n  p | bh_spin_a_;\n  p | inner_edge_radius_;\n  p | max_pressure_radius_;\n  p | polytropic_constant_;\n  p | polytropic_exponent_;\n  p | angular_momentum_;\n  p | rho_max_;\n  p | noise_;\n  p | equation_of_state_;\n  p | background_spacetime_;\n  p | background_density_;\n  p | background_temperature_;\n  p | background_pressure_;\n  p | background_specific_internal_energy_;\n}\n// Sigma in Fishbone&Moncrief eqn (3.5)\ntemplate <typename DataType>\nDataType FishboneMoncriefDisk::sigma(const DataType& r_sqrd,\n                                     const DataType& sin_theta_sqrd) const {\n  return r_sqrd + square(bh_spin_a_) * (1.0 - sin_theta_sqrd);\n}\n// Inverse of A in Fishbone&Moncrief eqn (3.5)\ntemplate <typename DataType>\nDataType FishboneMoncriefDisk::inv_ucase_a(const DataType& r_sqrd,\n                                           const DataType& sin_theta_sqrd,\n                                           const DataType& delta) const {\n  const double a_sqrd = square(bh_spin_a_);\n  const DataType r_sqrd_plus_a_sqrd = r_sqrd + a_sqrd;\n  return 1.0 / (square(r_sqrd_plus_a_sqrd) - delta * a_sqrd * sin_theta_sqrd);\n}\n\ntemplate <typename DataType>\nDataType FishboneMoncriefDisk::four_velocity_t_sqrd(\n    const DataType& r_sqrd, const DataType& sin_theta_sqrd) const {\n  const DataType delta =\n      r_sqrd - 2.0 * bh_mass_ * sqrt(r_sqrd) + square(bh_spin_a_);\n  const DataType prefactor = 0.5 / (inv_ucase_a(r_sqrd, sin_theta_sqrd, delta) *\n                                    sigma(r_sqrd, sin_theta_sqrd));\n  return prefactor *\n         (1.0 + sqrt(1.0 + square(angular_momentum_) * delta /\n                               (square(prefactor) * sin_theta_sqrd))) /\n         delta;\n}\n\ntemplate <typename DataType>\nDataType FishboneMoncriefDisk::angular_velocity(\n    const DataType& r_sqrd, const DataType& sin_theta_sqrd) const {\n  const DataType r = sqrt(r_sqrd);\n  return inv_ucase_a(\n             r_sqrd, sin_theta_sqrd,\n             DataType{r_sqrd - 2.0 * bh_mass_ * r + square(bh_spin_a_)}) *\n         (angular_momentum_ * sigma(r_sqrd, sin_theta_sqrd) /\n              (four_velocity_t_sqrd(r_sqrd, sin_theta_sqrd) * sin_theta_sqrd) +\n          2.0 * bh_mass_ * r * bh_spin_a_);\n}\n\ntemplate <typename DataType>\nDataType FishboneMoncriefDisk::potential(const DataType& r_sqrd,\n                                         const DataType& sin_theta_sqrd) const {\n  return angular_momentum_ * angular_velocity(r_sqrd, sin_theta_sqrd) -\n         log(sqrt(four_velocity_t_sqrd(r_sqrd, sin_theta_sqrd)));\n}\n\ntemplate <typename DataType>\nFishboneMoncriefDisk::IntermediateVariables<DataType>::IntermediateVariables(\n    const tnsr::I<DataType, 3>& x) {\n  sin_theta_squared = square(get<0>(x)) + square(get<1>(x));\n  r_squared = sin_theta_squared + square(get<2>(x));\n  sin_theta_squared /= r_squared;\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  const auto specific_enthalpy =\n      get<hydro::Tags::SpecificEnthalpy<DataType>>(variables(\n          x, tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>>{}, vars));\n  auto rest_mass_density =\n      make_with_value<Scalar<DataType>>(x, background_density_);\n  variables_impl(vars, [&rest_mass_density, &specific_enthalpy, this](\n                           const size_t s, const double /*potential_at_s*/) {\n    using std::max;\n    get_element(get(rest_mass_density), s) =\n        max((1 / rho_max_) *\n                get(equation_of_state_.rest_mass_density_from_enthalpy(\n                    Scalar<double>{get_element(get(specific_enthalpy), s)})),\n            background_density_);\n  });\n  return {std::move(rest_mass_density)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> /* vars */) const {\n  // Need to add EoS call to get correct electron fraction\n  // when using tables\n\n  auto ye = make_with_value<Scalar<DataType>>(x, 0.1);\n\n  return {std::move(ye)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  const double inner_edge_potential =\n      potential(square(inner_edge_radius_), 1.0);\n  auto specific_enthalpy = make_with_value<Scalar<DataType>>(x, 1.0);\n  variables_impl(vars, [&specific_enthalpy, inner_edge_potential](\n                           const size_t s, const double potential_at_s) {\n    get_element(get(specific_enthalpy), s) =\n        exp(inner_edge_potential - potential_at_s);\n  });\n  return {std::move(specific_enthalpy)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  std::random_device rd;\n  std::mt19937 gen(rd());\n  std::uniform_real_distribution<> dis(-noise_, noise_);\n  const auto rest_mass_density = get<hydro::Tags::RestMassDensity<DataType>>(\n      variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{}, vars));\n  auto pressure = make_with_value<Scalar<DataType>>(x, background_pressure_);\n  variables_impl(vars, [&pressure, &rest_mass_density, &gen, &dis, this](\n                           const size_t s, const double /*potential_at_s*/) {\n    using std::max;\n    get_element(get(pressure), s) =\n        max((1 / rho_max_) * (1 + dis(gen)) *\n                get(equation_of_state_.pressure_from_density(Scalar<double>{\n                    rho_max_ * get_element(get(rest_mass_density), s)})),\n            background_pressure_);\n  });\n  return {std::move(pressure)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  const auto rest_mass_density = get<hydro::Tags::RestMassDensity<DataType>>(\n      variables(x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{}, vars));\n  auto specific_internal_energy = make_with_value<Scalar<DataType>>(\n      x, background_specific_internal_energy_);\n  variables_impl(vars, [&specific_internal_energy, &rest_mass_density, this](\n                           const size_t s, const double /*potential_at_s*/) {\n    using std::max;\n    get_element(get(specific_internal_energy), s) = max(\n        get(equation_of_state_.specific_internal_energy_from_density(\n            Scalar<double>{rho_max_ * get_element(get(rest_mass_density), s)})),\n        background_specific_internal_energy_);\n  });\n  return {std::move(specific_internal_energy)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Temperature<DataType>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  const auto specific_internal_energy =\n      get<hydro::Tags::SpecificInternalEnergy<DataType>>(variables(\n          x, tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{},\n          vars));\n  auto temperature =\n      make_with_value<Scalar<DataType>>(x, background_temperature_);\n  variables_impl(vars, [&temperature, &specific_internal_energy, this](\n                           const size_t s, const double /*potential_at_s*/) {\n    using std::max;\n    get_element(get(temperature), s) =\n        max((polytropic_exponent_ - 1.0) *\n                get_element(get(specific_internal_energy), s),\n            background_temperature_);\n  });\n  return {std::move(temperature)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  auto spatial_velocity = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  using inv_jacobian =\n      gr::Solutions::SphericalKerrSchild::internal_tags::inv_jacobian<\n          DataType, Frame::Inertial>;\n  const auto inv_jacobians = get<inv_jacobian>(background_spacetime_.variables(\n      x, 0.0, tmpl::list<inv_jacobian>{},\n      make_not_null(&vars->sph_kerr_schild_cache)));\n  const auto shifts =\n      get<gr::Tags::Shift<DataType, 3>>(background_spacetime_.variables(\n          x, 0.0, tmpl::list<gr::Tags::Shift<DataType, 3>>{},\n          make_not_null(&vars->sph_kerr_schild_cache)));\n  const auto lapses =\n      get<gr::Tags::Lapse<DataType>>(background_spacetime_.variables(\n          x, 0.0, tmpl::list<gr::Tags::Lapse<DataType>>{},\n          make_not_null(&vars->sph_kerr_schild_cache)));\n  variables_impl(vars, [&spatial_velocity, &vars, &x, &inv_jacobians, &shifts,\n                        &lapses,\n                        this](const size_t s, const double /*potential_at_s*/) {\n    const double ang_velocity =\n        angular_velocity(get_element(vars->r_squared, s),\n                         get_element(vars->sin_theta_squared, s));\n    // We first compute the transport velocity in Kerr-Schild coordinates\n    // and then transform this vector back to Spherical Kerr-Schild coordinates.\n    auto transport_velocity_ks = make_array<3>(0.0);\n    auto transport_velocity_sks = make_array<3>(0.0);\n    const double sks_to_ks_factor =\n        sqrt(square(bh_spin_a_) + get_element(vars->r_squared, s)) /\n        sqrt(get_element(vars->r_squared, s));\n    transport_velocity_ks[0] -=\n        ang_velocity * get_element(x.get(1), s) * sks_to_ks_factor;\n    transport_velocity_ks[1] +=\n        ang_velocity * get_element(x.get(0), s) * sks_to_ks_factor;\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t i = 0; i < 3; ++i) {\n        gsl::at(transport_velocity_sks, j) +=\n            get_element(inv_jacobians.get(j, i), s) *\n            gsl::at(transport_velocity_ks, i);\n      }\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      get_element(spatial_velocity.get(i), s) =\n          (gsl::at(transport_velocity_sks, i) + get_element(shifts.get(i), s)) /\n          get_element(get(lapses), s);\n    }\n  });\n  return {std::move(spatial_velocity)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  const auto spatial_velocity =\n      get<hydro::Tags::SpatialVelocity<DataType, 3>>(variables(\n          x, tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>>{}, vars));\n  Scalar<DataType> lorentz_factor{\n      1.0 /\n      sqrt(1.0 - get(dot_product(\n                     spatial_velocity, spatial_velocity,\n                     get<gr::Tags::SpatialMetric<DataType, 3>>(\n                         background_spacetime_.variables(\n                             x, 0.0,\n                             tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{},\n                             make_not_null(&vars->sph_kerr_schild_cache))))))};\n  return {std::move(lorentz_factor)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> /*vars*/) const {\n  return {make_with_value<tnsr::I<DataType, 3>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nFishboneMoncriefDisk::variables(\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/,\n    gsl::not_null<IntermediateVariables<DataType>*> /*vars*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType, typename Func>\nvoid FishboneMoncriefDisk::variables_impl(\n    gsl::not_null<IntermediateVariables<DataType>*> vars, Func f) const {\n  const DataType& r_squared = vars->r_squared;\n  const DataType& sin_theta_squared = vars->sin_theta_squared;\n  const double inner_edge_potential =\n      potential(square(inner_edge_radius_), 1.0);\n\n  // fill the disk with matter\n  for (size_t s = 0; s < get_size(r_squared); ++s) {\n    const double r_squared_s = get_element(r_squared, s);\n    const double sin_theta_squared_s = get_element(sin_theta_squared, s);\n\n    // the disk won't extend closer to the axis than r sin theta = rin\n    // so no need to evaluate the potential there\n    if (sqrt(r_squared_s * sin_theta_squared_s) >= inner_edge_radius_) {\n      const double potential_s = potential(r_squared_s, sin_theta_squared_s);\n      // the fluid can only be where W(r, theta) < W_in\n      if (potential_s < inner_edge_potential) {\n        f(s, potential_s);\n      }\n    }\n  }\n}\n\nPUP::able::PUP_ID FishboneMoncriefDisk::my_PUP_ID = 0;\n\nbool operator==(const FishboneMoncriefDisk& lhs,\n                const FishboneMoncriefDisk& rhs) {\n  return lhs.bh_mass_ == rhs.bh_mass_ and lhs.bh_spin_a_ == rhs.bh_spin_a_ and\n         lhs.inner_edge_radius_ == rhs.inner_edge_radius_ and\n         lhs.max_pressure_radius_ == rhs.max_pressure_radius_ and\n         lhs.polytropic_constant_ == rhs.polytropic_constant_ and\n         lhs.polytropic_exponent_ == rhs.polytropic_exponent_ and\n         lhs.noise_ == rhs.noise_ and lhs.rho_max_ == rhs.rho_max_;\n}\n\nbool operator!=(const FishboneMoncriefDisk& lhs,\n                const FishboneMoncriefDisk& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template class FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>;     \\\n  template tuples::TaggedTuple<hydro::Tags::RestMassDensity<DTYPE(data)>>      \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::RestMassDensity<DTYPE(data)>> /*meta*/,          \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<hydro::Tags::ElectronFraction<DTYPE(data)>>     \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::ElectronFraction<DTYPE(data)>> /*meta*/,         \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DTYPE(data)>>     \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::SpecificEnthalpy<DTYPE(data)>> /*meta*/,         \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<hydro::Tags::Pressure<DTYPE(data)>>             \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::Pressure<DTYPE(data)>> /*meta*/,                 \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<                                                \\\n      hydro::Tags::SpecificInternalEnergy<DTYPE(data)>>                        \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DTYPE(data)>> /*meta*/,   \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<hydro::Tags::Temperature<DTYPE(data)>>          \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::Temperature<DTYPE(data)>> /*meta*/,              \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<hydro::Tags::MagneticField<DTYPE(data), 3>>     \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::MagneticField<DTYPE(data), 3,                    \\\n                                            Frame::Inertial>> /*meta*/,        \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<                                                \\\n      hydro::Tags::DivergenceCleaningField<DTYPE(data)>>                       \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DTYPE(data)>> /*meta*/,  \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DTYPE(data), 3>>   \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::SpatialVelocity<DTYPE(data), 3>> /*meta*/,       \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template tuples::TaggedTuple<hydro::Tags::LorentzFactor<DTYPE(data)>>        \\\n  FishboneMoncriefDisk::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3>& x,                                        \\\n      tmpl::list<hydro::Tags::LorentzFactor<DTYPE(data)>> /*meta*/,            \\\n      gsl::not_null<FishboneMoncriefDisk::IntermediateVariables<DTYPE(data)>*> \\\n          vars) const;                                                         \\\n  template DTYPE(data) FishboneMoncriefDisk::potential(                        \\\n      const DTYPE(data) & r_sqrd, const DTYPE(data) & sin_theta_sqrd) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace grmhd::AnalyticData {\nclass MagnetizedFmDisk;\n}  // namespace grmhd::AnalyticData\n/// \\endcond\n\nnamespace RelativisticEuler {\nnamespace Solutions {\n\n/*!\n * \\brief Fluid disk orbiting a Kerr black hole\n *\n * The Fishbone-Moncrief solution to the 3D relativistic Euler system\n * \\cite Fishbone1976apj, representing the isentropic flow of a thick fluid disk\n * orbiting a Kerr black hole. In Boyer-Lindquist coordinates\n * \\f$(t, r, \\theta, \\phi)\\f$, the flow is assumed to be purely toroidal,\n *\n * \\f{align*}\n * u^\\mu = (u^t, 0, 0, u^\\phi),\n * \\f}\n *\n * where \\f$u^\\mu\\f$ is the 4-velocity. Then, all the fluid quantities are\n * assumed to share the same symmetries as those of the background spacetime,\n * namely they are stationary (independent of \\f$t\\f$), and axially symmetric\n * (independent of \\f$\\phi\\f$).\n *\n * Self-gravity is neglected, so that the fluid\n * variables are determined as functions of the metric. Following the treatment\n * by Kozlowski et al. \\cite Kozlowski1978aa (but using signature +2)\n * the solution is expressed in terms of the quantities\n *\n * \\f{align*}\n * \\Omega &= \\dfrac{u^\\phi}{u^t},\\\\\n * W &= W_\\text{in} - \\int_{p_\\text{in}}^p\\frac{dp}{e + p},\n * \\f}\n *\n * where \\f$\\Omega\\f$ is the angular velocity, \\f$p\\f$ is the fluid pressure,\n * \\f$e\\f$ is the energy density, and \\f$W\\f$ is an auxiliary quantity\n * interpreted in the Newtonian limit as the total (gravitational + centrifugal)\n * potential. \\f$W_\\text{in}\\f$ and \\f$p_\\text{in}\\f$ are the potential and\n * the pressure at the radius of the inner edge, i.e. the closest edge to the\n * black hole. Here we assume \\f$p_\\text{in} = 0.\\f$ The solution to the Euler\n * equation is then\n *\n * \\f{align*}\n * (u^t)^2 &= \\frac{A}{2\\Delta\\Sigma}\\left(1 +\n * \\sqrt{1 + \\frac{4l^2\\Delta \\Sigma^2}{A^2\\sin^2\\theta}}\\right)\\\\\n * \\Omega &= \\frac{\\Sigma}{A (u^t)^2}\\frac{l}{\\sin^2\\theta} + \\frac{2Mra}{A}\\\\\n * u^\\phi &= \\Omega u^t\\\\\n * W &= l\\Omega - \\ln u^t,\n * \\f}\n *\n * where\n *\n * \\f{align*}\n * \\Sigma = r^2 + a^2\\cos^2\\theta\\qquad\n * \\Delta = r^2 - 2Mr + a^2\\qquad\n * A = (r^2 + a^2)^2 - \\Delta a^2 \\sin^2\\theta\n * \\f}\n *\n * and \\f$l = u_\\phi u^t\\f$ is the so-called angular momentum per unit\n * intertial mass, which is a parameter defining an\n * individual disk. In deriving the solution, an integration constant has been\n * chosen so that \\f$ W\\longrightarrow 0\\f$ as \\f$r\\longrightarrow \\infty\\f$,\n * in accordance with the Newtonian limit. Note that, from its definition,\n * equipotential contours coincide with isobaric contours. Physically, the\n * matter can fill each of the closed surfaces \\f$W = \\text{const}\\f$, giving\n * rise to an orbiting thick disk. For \\f$W > 0\\f$, all equipotentials are open,\n * whereas for \\f$W < 0\\f$, some of them will be closed. Should a disk exist,\n * the pressure reaches a maximum value on the equator at a coordinate radius\n * \\f$r_\\text{max}\\f$ that is related to the angular momentum per unit inertial\n * mass via\n *\n * \\f{align*}\n * l = \\dfrac{M^{1/2}(r_\\text{max}^{3/2} + aM^{1/2})(a^2 - 2aM^{1/2}\n * r_\\text{max}^{1/2} + r_\\text{max}^2)}{2aM^{1/2}r_\\text{max}^{3/2} +\n * (r_\\text{max} - 3M)r_\\text{max}^2}.\n * \\f}\n *\n * Once \\f$W\\f$ is determined, an equation of state is required in order to\n * obtain the thermodynamic variables. If the flow is isentropic, the specific\n * enthalpy can readily be obtained from the first and second laws of\n * thermodynamics: one has\n *\n * \\f{align*}\n * \\frac{dp}{e + p} = \\frac{dh}{h}\n * \\f}\n *\n * so that\n *\n * \\f{align*}\n * h = h_\\text{in}\\exp(W_\\text{in} - W),\n * \\f}\n *\n * and the pressure can be obtained from a thermodynamic relation of the form\n * \\f$h = h(p)\\f$. Here we assume a polytropic relation\n *\n * \\f{align*}\n * p = K\\rho^\\gamma.\n * \\f}\n *\n * Following \\cite Porth2016rfi, the rest mass density is normalized\n * to be 1. at maximum.\n *\n * Once all the variables are known in Boyer-Lindquist (or Kerr) coordinates, it\n * is straightforward to write them in Cartesian Kerr-Schild coordinates. The\n * coordinate transformation in gr::KerrSchildCoords helps read the Jacobian\n * matrix, which, applied to the azimuthal flow of the disk, gives\n *\n * \\f{align*}\n * u_\\text{KS}^\\mu = u^t(1, -y\\Omega, x\\Omega, 0),\n * \\f}\n *\n * where \\f$u^t\\f$ and \\f$\\Omega\\f$ are now understood as functions of the\n * Kerr-Schild coordinates. Finally, the spatial velocity can be readily\n * obtained from its definition,\n *\n * \\f{align*}\n * \\alpha v^i = \\frac{u^i}{u^t} + \\beta^i,\n * \\f}\n *\n * where \\f$\\alpha\\f$ and \\f$\\beta^i\\f$ are the lapse and the shift,\n * respectively.\n *\n * \\note Kozlowski et al. \\cite Kozlowski1978aa denote\n * \\f$l_* = u_\\phi u^t\\f$ in order to\n * distinguish this quantity from their own definition \\f$l = - u_\\phi/u_t\\f$.\n *\n * \\note When using Kerr-Schild coordinates, the horizon that is at\n * constant \\f$r\\f$ is not spherical, but instead spheroidal. This could make\n * application of boundary condition and computing various fluxes\n * across the horizon more complicated than they need to be.\n * Thus, we use Spherical Kerr-Schild coordinates,\n * see gr::Solutions::SphericalKerrSchild, in which constant \\f$r\\f$\n * is spherical. Because we compute variables in Kerr-Schild coordinates,\n * there is a necessary extra step of transforming them back to\n * Spherical Kerr-Schild coordinates.\n *\n */\nclass FishboneMoncriefDisk\n    : public virtual evolution::initial_data::InitialData,\n      public MarkAsAnalyticSolution,\n      public AnalyticSolution<3>,\n      public hydro::TemperatureInitialization<FishboneMoncriefDisk> {\n protected:\n  template <typename DataType>\n  struct IntermediateVariables;\n\n public:\n  using equation_of_state_type = EquationsOfState::PolytropicFluid<true>;\n\n  /// The mass of the black hole, \\f$M\\f$.\n  struct BhMass {\n    using type = double;\n    static constexpr Options::String help = {\"The mass of the black hole.\"};\n    static type lower_bound() { return 0.0; }\n  };\n  /// The dimensionless black hole spin, \\f$\\chi = a/M\\f$.\n  struct BhDimlessSpin {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The dimensionless black hole spin.\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n  /// The radial coordinate of the inner edge of the disk, in units of \\f$M\\f$.\n  struct InnerEdgeRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The radial coordinate of the inner edge of the disk.\"};\n  };\n  /// The radial coordinate of the maximum pressure, in units of \\f$M\\f$.\n  struct MaxPressureRadius {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The radial coordinate of the maximum pressure.\"};\n  };\n  /// The polytropic constant of the fluid.\n  struct PolytropicConstant {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The polytropic constant of the fluid.\"};\n    static type lower_bound() { return 0.; }\n  };\n  /// The polytropic exponent of the fluid.\n  struct PolytropicExponent {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The polytropic exponent of the fluid.\"};\n    static type lower_bound() { return 1.; }\n  };\n  /// The magnitude of noise added to pressure/energy of the Disk\n  /// to drive MRI.\n  struct Noise {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The magnitude of the white noise perturbation added to \"\n        \"pressure to excite MRI in the disk.\"};\n    static type lower_bound() { return 0.; }\n    static type upper_bound() { return 1.; }\n  };\n\n  using options =\n      tmpl::list<BhMass, BhDimlessSpin, InnerEdgeRadius, MaxPressureRadius,\n                 PolytropicConstant, PolytropicExponent, Noise>;\n  static constexpr Options::String help = {\n      \"Fluid disk orbiting a Kerr black hole.\"};\n\n  FishboneMoncriefDisk() = default;\n  FishboneMoncriefDisk(const FishboneMoncriefDisk& /*rhs*/) = default;\n  FishboneMoncriefDisk& operator=(const FishboneMoncriefDisk& /*rhs*/) =\n      default;\n  FishboneMoncriefDisk(FishboneMoncriefDisk&& /*rhs*/) = default;\n  FishboneMoncriefDisk& operator=(FishboneMoncriefDisk&& /*rhs*/) = default;\n  ~FishboneMoncriefDisk() override = default;\n\n  FishboneMoncriefDisk(double bh_mass, double bh_dimless_spin,\n                       double inner_edge_radius, double max_pressure_radius,\n                       double polytropic_constant, double polytropic_exponent,\n                       double noise);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit FishboneMoncriefDisk(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(FishboneMoncriefDisk);\n  /// \\endcond\n\n  // Eventually, if we implement a gr::Solutions::BoyerLindquist\n  // black hole, the following two functions aren't needed, and the algebra\n  // of the three functions after can be simplified by using the corresponding\n  // lapse, shift, and spatial metric.\n  template <typename DataType>\n  DataType sigma(const DataType& r_sqrd, const DataType& sin_theta_sqrd) const;\n\n  template <typename DataType>\n  DataType inv_ucase_a(const DataType& r_sqrd, const DataType& sin_theta_sqrd,\n                       const DataType& delta) const;\n\n  template <typename DataType>\n  DataType four_velocity_t_sqrd(const DataType& r_sqrd,\n                                const DataType& sin_theta_sqrd) const;\n\n  template <typename DataType>\n  DataType angular_velocity(const DataType& r_sqrd,\n                            const DataType& sin_theta_sqrd) const;\n\n  template <typename DataType>\n  DataType potential(const DataType& r_sqrd,\n                     const DataType& sin_theta_sqrd) const;\n\n  template <typename DataType>\n  using tags =\n      tmpl::append<hydro::grmhd_tags<DataType>,\n                   typename gr::Solutions::SphericalKerrSchild::tags<DataType>>;\n\n  /// @{\n  /// The variables in Cartesian Spherical-Kerr-Schild coordinates at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         const double /*t*/,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    // Can't store IntermediateVariables as member variable because we need to\n    // be threadsafe.\n    IntermediateVariables<DataType> vars(x);\n    return {std::move(\n        get<Tags>(variables(x, tmpl::list<Tags>{}, make_not_null(&vars))))...};\n  }\n\n  template <typename DataType, typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, 3>& x,\n                                     const double /*t*/,\n                                     tmpl::list<Tag> /*meta*/) const {\n    // Can't store IntermediateVariables as member variable because we need to\n    // be threadsafe.\n    IntermediateVariables<DataType> vars(x);\n    return variables(x, tmpl::list<Tag>{}, make_not_null(&vars));\n  }\n  /// @}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  const EquationsOfState::PolytropicFluid<true>& equation_of_state() const {\n    return equation_of_state_;\n  }\n\n protected:\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/,\n                 gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/,\n                 gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/,\n                 gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/,\n                 gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/,\n                 gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/,\n      gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/,\n                 gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/,\n                 gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/,\n                 gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/,\n      gsl::not_null<IntermediateVariables<DataType>*> vars) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  // Grab the metric variables\n  template <typename DataType, typename Tag,\n            Requires<not tmpl::list_contains_v<\n                tmpl::push_back<hydro::grmhd_tags<DataType>,\n                                hydro::Tags::SpecificEnthalpy<DataType>,\n                                hydro::Tags::SpatialVelocity<DataType, 3>,\n                                hydro::Tags::LorentzFactor<DataType>>,\n                Tag>> = nullptr>\n  tuples::TaggedTuple<Tag> variables(\n      const tnsr::I<DataType, 3>& x, tmpl::list<Tag> /*meta*/,\n      gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n    return {get<Tag>(background_spacetime_.variables(\n        x, 0.0, tmpl::list<Tag>{},\n        make_not_null(&vars->sph_kerr_schild_cache)))};\n  }\n\n  template <typename DataType, typename Func>\n  void variables_impl(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                      Func f) const;\n\n  // Intermediate variables needed to set several of the Fishbone-Moncrief\n  // solution's variables.\n\n  template <typename DataType>\n  struct IntermediateVariables {\n    explicit IntermediateVariables(const tnsr::I<DataType, 3>& x);\n\n    DataType r_squared{};\n    DataType sin_theta_squared{};\n    gr::Solutions::SphericalKerrSchild::IntermediateVars<DataType,\n                                                         Frame::Inertial>\n        sph_kerr_schild_cache =\n            gr::Solutions::SphericalKerrSchild::IntermediateVars<\n                DataType, Frame::Inertial>(0);\n  };\n\n  friend bool operator==(const FishboneMoncriefDisk& lhs,\n                         const FishboneMoncriefDisk& rhs);\n  friend class grmhd::AnalyticData::MagnetizedFmDisk;\n\n  double bh_mass_ = std::numeric_limits<double>::signaling_NaN();\n  double bh_spin_a_ = std::numeric_limits<double>::signaling_NaN();\n  double inner_edge_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double max_pressure_radius_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_constant_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent_ = std::numeric_limits<double>::signaling_NaN();\n  double angular_momentum_ = std::numeric_limits<double>::signaling_NaN();\n  double rho_max_ = std::numeric_limits<double>::signaling_NaN();\n  double noise_ = std::numeric_limits<double>::signaling_NaN();\n  EquationsOfState::PolytropicFluid<true> equation_of_state_{};\n  gr::Solutions::SphericalKerrSchild background_spacetime_{};\n\n  // Many of the codes on the EHT Code Comparison Project use flooring with\n  // density and pressure. However, in SpECTRE, floorings are applied on\n  // density and temperature.\n  // Previously, the background values outside of the torus were initialized\n  // to 0 and then subsequently modified based on the atmosphere treatment\n  // and flooring options.\n  // However, this meant that we need to apply higher than desired flooring\n  // level on temperature or density in order to match the radial\n  // profile of the pressure as used in the EHT Code Comparison project.\n  // Thus, we instead initialize the background values that most closely\n  // matches the radial profiles of EHT Code Comparison Project initial data\n  // without introducing any weird kinks.\n  double background_density_ = 1.e-7;\n  double background_temperature_ = 2.e-5;\n  double background_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double background_specific_internal_energy_ =\n      std::numeric_limits<double>::signaling_NaN();\n};\n\nbool operator!=(const FishboneMoncriefDisk& lhs,\n                const FishboneMoncriefDisk& rhs);\n}  // namespace Solutions\n}  // namespace RelativisticEuler\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/Tov.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.Interpolation\");\n  py::module_::import(\"spectre.PointwiseFunctions.Hydro.EquationsOfState\");\n  RelativisticEuler::Solutions::py_bindings::bind_tov(m);\n}\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyRelativisticEulerSolutions\")\n\nspectre_python_add_module(\n  RelativisticEuler\n  LIBRARY_NAME ${LIBRARY}\n  MODULE_PATH \"PointwiseFunctions/AnalyticSolutions\"\n  SOURCES\n  Bindings.cpp\n  Tov.cpp\n  PYTHON_FILES\n  __init__.py\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Tov.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  RelativisticEulerSolutions\n  Hydro\n  pybind11::module\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyEquationsOfState\n  PyInterpolation\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/Tov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/Tov.hpp\"\n\n#include <pybind11/numpy.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Tov.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n\nnamespace py = pybind11;\n\nnamespace RelativisticEuler::Solutions::py_bindings {\n\nvoid bind_tov(py::module& m) {\n  py::enum_<TovCoordinates>(m, \"TovCoordinates\")\n      .value(\"Schwarzschild\", TovCoordinates::Schwarzschild)\n      .value(\"Isotropic\", TovCoordinates::Isotropic);\n  py::class_<TovSolution>(m, \"Tov\")\n      .def(py::init<const EquationsOfState::EquationOfState<true, 1>&, double,\n                    TovCoordinates, double, double, double>(),\n           py::arg(\"equation_of_state\"), py::arg(\"central_mass_density\"),\n           py::arg(\"coordinate_system\") = TovCoordinates::Schwarzschild,\n           py::arg(\"log_enthalpy_at_outer_radius\") = 0.,\n           py::arg(\"absolute_tolerance\") = 1.e-14,\n           py::arg(\"relative_tolerance\") = 1.e-14)\n      .def(\"outer_radius\", &TovSolution::outer_radius)\n      .def(\"total_mass\", &TovSolution::total_mass)\n      .def(\"injection_energy\", &TovSolution::injection_energy)\n      .def(\"mass_over_radius\",\n           py::vectorize(&TovSolution::mass_over_radius<double>))\n      .def(\"log_specific_enthalpy\",\n           py::vectorize(&TovSolution::log_specific_enthalpy<double>))\n      .def(\"conformal_factor\",\n           py::vectorize(&TovSolution::conformal_factor<double>))\n      .def_property_readonly(\"mass_over_radius_interpolant\",\n                             &TovSolution::mass_over_radius_interpolant)\n      .def_property_readonly(\"log_specific_enthalpy_interpolant\",\n                             &TovSolution::log_specific_enthalpy_interpolant)\n      .def_property_readonly(\"conformal_factor_interpolant\",\n                             &TovSolution::conformal_factor_interpolant);\n}\n\n}  // namespace RelativisticEuler::Solutions::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/Tov.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace RelativisticEuler::Solutions::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_tov(pybind11::module& m);\n}  // namespace RelativisticEuler::Solutions::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <fstream>\n#include <limits>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Interpolation/PolynomialInterpolation.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace RelativisticEuler::Solutions {\nnamespace detail {\nCstSolution::CstSolution(const std::string& filename, const bool is_polytrope,\n                         const double polytropic_constant = 0.) {\n  if (not file_system::check_if_file_exists(filename)) {\n    ERROR(\"Cannot open file '\" << filename\n                               << \"' in CstSolution/RotatingStar\\n\");\n  }\n  std::ifstream cst_file(filename);\n  std::string header_line;\n  std::getline(cst_file, header_line);\n  // Read as integers from file to be compatible with SpEC. Might not be\n  // necessary.\n  int num_radial_points = -1;\n  int num_angular_points = -1;\n  cst_file >> num_radial_points >> num_angular_points;\n  num_radial_points_ = static_cast<size_t>(num_radial_points);\n  // The RotNS code seems to write out more grid points than it claims. It's\n  // unclear why, but SpEC does the `+ 1`.\n  num_angular_points_ = static_cast<size_t>(num_angular_points) + 1;\n  num_grid_points_ = num_radial_points_ * num_angular_points_;\n  cst_file >> equatorial_radius_ >> polytropic_index_ >>\n      central_angular_speed_ >> rotation_profile_;\n\n  radius_.destructive_resize(num_grid_points_);\n  cos_theta_.destructive_resize(num_grid_points_);\n  rest_mass_density_.destructive_resize(num_grid_points_);\n  fluid_velocity_.destructive_resize(num_grid_points_);\n  alpha_.destructive_resize(num_grid_points_);\n  rho_.destructive_resize(num_grid_points_);\n  gamma_.destructive_resize(num_grid_points_);\n  omega_.destructive_resize(num_grid_points_);\n\n  // Import the data and rescale.\n  // If the `PolytropicConstant` option was specified in the input file,\n  // `is_polytrope` is set to `true` and the data is rescaled according to the\n  // normalization used in equations 15-23 in \\cite{Cook1992}, which uses\n  // geometric units and sets the polytropic constant as a free parameter. If\n  // the user does not want to rescale, they should set `PolytropicConstant` to\n  // be `1.`, which is the value used in RotNS.\n  // If instead `EquationOfState` is specified, `is_polytrope` is set to `false`\n  // and the data is rescaled according to the normalization used in equations\n  // A1-A10 in \\cite{Cook1994}. This uses CGS units, defined by a fundamental\n  // length scale $\\kappa^{1/2}$, defined as $\\kappa = \\frac{c^2}{G\n  // \\epsilon_0}$, where $\\epsilon_0 = 10^{15}$ g cm^-3 is an arbitrarily\n  // defined density scale.\n\n  const double kappa_norm = square(hydro::units::cgs::speed_of_light) /\n                            (1.e15 * hydro::units::cgs::G_Newton);\n\n  for (size_t i = 0; i < num_radial_points_; i++) {\n    for (size_t j = 0; j < num_angular_points_; j++) {\n      // The data is stored in cos(theta) varies fastest\n      //\n      // Note that cos(theta) is between 0 and 1.\n      const size_t index = num_angular_points_ * i + j;\n      cst_file >> radius_[index] >> cos_theta_[index] >>\n          rest_mass_density_[index] >> alpha_[index] >> rho_[index] >>\n          gamma_[index] >> omega_[index] >> fluid_velocity_[index];\n      if (is_polytrope) {\n        radius_[index] *= pow(polytropic_constant, 0.5 * polytropic_index_);\n        rest_mass_density_[index] *=\n            pow(polytropic_constant, -polytropic_index_);\n        omega_[index] *= pow(polytropic_constant, -0.5 * polytropic_index_) /\n                         equatorial_radius_;\n        fluid_velocity_[index] *=\n            pow(polytropic_constant, -0.5 * polytropic_index_) /\n            equatorial_radius_;\n      } else {\n        radius_[index] *= sqrt(kappa_norm) / hydro::units::cgs::length_unit;\n        rest_mass_density_[index] *=\n            1.e15 / hydro::units::cgs::rest_mass_density_unit;\n        omega_[index] *= (hydro::units::cgs::speed_of_light /\n                          (equatorial_radius_ * sqrt(kappa_norm))) *\n                         hydro::units::cgs::time_unit;\n        fluid_velocity_[index] *= (hydro::units::cgs::speed_of_light /\n                                   (equatorial_radius_ * sqrt(kappa_norm))) *\n                                  hydro::units::cgs::time_unit;\n      }\n    }\n  }\n  maximum_radius_ = max(radius_);\n  if (is_polytrope) {\n    central_angular_speed_ *=\n        pow(polytropic_constant, -0.5 * polytropic_index_) / equatorial_radius_;\n    equatorial_radius_ *= pow(polytropic_constant, 0.5 * polytropic_index_);\n  } else {\n    central_angular_speed_ *= (hydro::units::cgs::speed_of_light /\n                               (equatorial_radius_ * sqrt(kappa_norm))) *\n                              hydro::units::cgs::time_unit;\n    equatorial_radius_ *= sqrt(kappa_norm) / hydro::units::cgs::length_unit;\n  }\n}\n\nvoid CstSolution::pup(PUP::er& p) {\n  p | maximum_radius_;\n  p | max_density_ratio_for_linear_interpolation_;\n\n  p | equatorial_radius_;\n  p | polytropic_index_;\n  p | central_angular_speed_;\n  p | rotation_profile_;\n  p | num_radial_points_;\n  p | num_angular_points_;\n  p | num_grid_points_;\n\n  p | radius_;\n  p | cos_theta_;\n  p | rest_mass_density_;\n  p | fluid_velocity_;\n  p | alpha_;\n  p | rho_;\n  p | gamma_;\n  p | omega_;\n}\n\nnamespace {\ntemplate <size_t StencilSize>\nvoid hydro_interpolation(const gsl::not_null<double*> target_var,\n                         const double max_var_ratio_for_linear_interpolation,\n                         const double target_coord,\n                         const gsl::span<const double>& var_stencil,\n                         const gsl::span<const double>& coords) {\n  double error_y = 0.0;\n  if (const auto min_max_iters =\n          std::minmax_element(var_stencil.begin(), var_stencil.end());\n      *min_max_iters.second >\n      max_var_ratio_for_linear_interpolation * *min_max_iters.first) {\n    std::array<double, 2> coord_linear{\n        {std::numeric_limits<double>::signaling_NaN(),\n         std::numeric_limits<double>::signaling_NaN()}};\n    std::array<double, 2> var_linear{\n        {std::numeric_limits<double>::signaling_NaN(),\n         std::numeric_limits<double>::signaling_NaN()}};\n    for (size_t i = 0; i < StencilSize - 1; ++i) {\n      if (coords[i] <= target_coord and target_coord <= coords[i + 1]) {\n        coord_linear[0] = coords[i];\n        coord_linear[1] = coords[i + 1];\n        var_linear[0] = gsl::at(var_stencil, i);\n        var_linear[1] = gsl::at(var_stencil, i + 1);\n        break;\n      }\n    }\n    intrp::polynomial_interpolation<1>(\n        target_var, make_not_null(&error_y), target_coord,\n        gsl::make_span(var_linear.data(), var_linear.size()),\n        gsl::make_span(coord_linear.data(), coord_linear.size()));\n  } else {\n    intrp::polynomial_interpolation<StencilSize - 1>(\n        target_var, make_not_null(&error_y), target_coord, var_stencil, coords);\n  }\n}\n}  // namespace\n\nstd::array<double, 6> CstSolution::interpolate(\n    const double target_radius, const double target_cos_theta,\n    const bool interpolate_hydro_vars) const {\n  constexpr size_t stencil_size = 4;\n  ASSERT(target_radius >= 0.0,\n         \"The target radius must be positive, not \" << target_radius);\n  if (UNLIKELY(target_radius > maximum_radius_)) {\n    ERROR(\"Requested radius \" << target_radius\n                              << \" is greater than what the Cook, Shapiro, \"\n                                 \"Teukolsky input file contains, \"\n                              << maximum_radius_);\n  }\n  using std::abs;\n  const double target_abs_cos_theta = abs(target_cos_theta);\n  // The `lround` centers the stencils and avoids rounding problems.\n  const auto angular_index = static_cast<size_t>(lround(\n      target_abs_cos_theta * static_cast<double>(num_angular_points_ - 1)));\n  const auto radius_index = static_cast<size_t>(\n      lround(static_cast<double>(num_radial_points_) * target_radius /\n             (target_radius + equatorial_radius_)));\n  if (const size_t grid_index =\n          num_angular_points_ * radius_index + angular_index;\n      UNLIKELY(grid_index > num_grid_points_)) {\n    ERROR(\"grid_index \" << grid_index\n                        << \" is larger than the number of grid points \"\n                        << num_grid_points_\n                        << \"\\nnumber of angular points: \" << num_angular_points_\n                        << \"\\nnumber of radial points: \" << num_radial_points_\n                        << \"\\nradius index: \" << radius_index\n                        << \"\\nangular_index: \" << angular_index);\n  }\n  // if (UNLIKELY(radius_[num_angular_points_ * radius_index + angular_index] >\n  //              target_radius)) {\n  //   // This sanity check and adjustment is in SpEC, but Nils Deppe isn't sure\n  //   // under what circumstances it is necessary. Best guess is that because\n  //   // the radius_index is an estimate it could be that you compute an index\n  //   // that's slightly an over-estimate. We could do a binary search to find\n  //   // the exact index if the estimate fails.\n  //   radius_index--;\n  // }\n  if (UNLIKELY(radius_index >= num_radial_points_)) {\n    ERROR(\n        \"radius_index \" << radius_index\n                        << \" is larger than the number of radial grid points: \"\n                        << num_radial_points_);\n  }\n\n  // If the stencil size is an even number, a shift in the stencil may be\n  // necessary. The intention is that the target point is always in the middle\n  // of the stencil, i.e. between the middle 2 points.\n  int radial_stencil_adjust = 0;\n  int angular_stencil_adjust = 0;\n  if constexpr (stencil_size % 2 == 0) {\n    if (target_radius > radius_[radius_index * num_angular_points_]) {\n      radial_stencil_adjust++;\n    }\n    if (target_abs_cos_theta > cos_theta_[angular_index]) {\n      angular_stencil_adjust++;\n    }\n  }\n  const size_t radial_stencil_index = static_cast<size_t>(\n      std::clamp(static_cast<int>(radius_index) -\n                     static_cast<int>(stencil_size) / 2 + radial_stencil_adjust,\n                 0, static_cast<int>(num_radial_points_ - stencil_size)));\n  const size_t angular_stencil_index = static_cast<size_t>(std::clamp(\n      static_cast<int>(angular_index) - static_cast<int>(stencil_size) / 2 +\n          angular_stencil_adjust,\n      0, static_cast<int>(num_angular_points_ - stencil_size)));\n\n  // We first interpolate in cos(theta) and then in radius.\n  std::array<double, stencil_size> rest_mass_density_rad_stencil{};\n  std::array<double, stencil_size> fluid_velocity_rad_stencil{};\n  std::array<double, stencil_size> alpha_rad_stencil{};\n  std::array<double, stencil_size> rho_rad_stencil{};\n  std::array<double, stencil_size> gamma_rad_stencil{};\n  std::array<double, stencil_size> omega_rad_stencil{};\n  // Since the radius is not contiguous, we need to copy the radius into a\n  // contiguous buffer. radius_for_stencil is that buffer.\n  std::array<double, stencil_size> radius_for_stencil{};\n  for (size_t stencil_rad_index = 0; stencil_rad_index < stencil_size;\n       ++stencil_rad_index) {\n    const size_t radial_index =\n        (stencil_rad_index + radial_stencil_index) * num_angular_points_;\n    gsl::at(radius_for_stencil, stencil_rad_index) =\n        radius_[radial_index + angular_stencil_index];\n\n    double error_y = 0.0;\n    const auto cos_theta_span = gsl::make_span(\n        &cos_theta_[radial_index + angular_stencil_index], stencil_size);\n    // At the surface we want to do linear interpolation to avoid unphysical\n    // oscillations that result in negative densities. We could do some sort of\n    // WENO-type approach to do a one-sided interpolation, but that's alot more\n    // involved.\n    if (interpolate_hydro_vars) {\n      const auto density_span = gsl::make_span(\n          &rest_mass_density_[radial_index + angular_stencil_index],\n          stencil_size);\n      hydro_interpolation<stencil_size>(\n          make_not_null(\n              &gsl::at(rest_mass_density_rad_stencil, stencil_rad_index)),\n          max_density_ratio_for_linear_interpolation_, target_abs_cos_theta,\n          density_span, cos_theta_span);\n      hydro_interpolation<stencil_size>(\n          make_not_null(\n              &gsl::at(fluid_velocity_rad_stencil, stencil_rad_index)),\n          // Note: we use max density ratio for velocity\n          max_density_ratio_for_linear_interpolation_, target_abs_cos_theta,\n          gsl::make_span(&fluid_velocity_[radial_index + angular_stencil_index],\n                         stencil_size),\n          cos_theta_span);\n    }\n    intrp::polynomial_interpolation<stencil_size - 1>(\n        make_not_null(&gsl::at(alpha_rad_stencil, stencil_rad_index)),\n        make_not_null(&error_y), target_abs_cos_theta,\n        gsl::make_span(&alpha_[radial_index + angular_stencil_index],\n                       stencil_size),\n        cos_theta_span);\n    intrp::polynomial_interpolation<stencil_size - 1>(\n        make_not_null(&gsl::at(rho_rad_stencil, stencil_rad_index)),\n        make_not_null(&error_y), target_abs_cos_theta,\n        gsl::make_span(&rho_[radial_index + angular_stencil_index],\n                       stencil_size),\n        cos_theta_span);\n    intrp::polynomial_interpolation<stencil_size - 1>(\n        make_not_null(&gsl::at(gamma_rad_stencil, stencil_rad_index)),\n        make_not_null(&error_y), target_abs_cos_theta,\n        gsl::make_span(&gamma_[radial_index + angular_stencil_index],\n                       stencil_size),\n        cos_theta_span);\n    intrp::polynomial_interpolation<stencil_size - 1>(\n        make_not_null(&gsl::at(omega_rad_stencil, stencil_rad_index)),\n        make_not_null(&error_y), target_abs_cos_theta,\n        gsl::make_span(&omega_[radial_index + angular_stencil_index],\n                       stencil_size),\n        cos_theta_span);\n  }\n\n  // Do radial interpolation\n  double error_y = 0.0;\n  double target_rest_mass_density{std::numeric_limits<double>::signaling_NaN()};\n  double target_fluid_velocity{std::numeric_limits<double>::signaling_NaN()};\n  double target_alpha{std::numeric_limits<double>::signaling_NaN()};\n  double target_rho{std::numeric_limits<double>::signaling_NaN()};\n  double target_gamma{std::numeric_limits<double>::signaling_NaN()};\n  double target_omega{std::numeric_limits<double>::signaling_NaN()};\n  const auto radius_span = gsl::make_span(&radius_for_stencil[0], stencil_size);\n  if (interpolate_hydro_vars) {\n    hydro_interpolation<stencil_size>(\n        make_not_null(&target_rest_mass_density),\n        max_density_ratio_for_linear_interpolation_, target_radius,\n        gsl::make_span(&rest_mass_density_rad_stencil[0], stencil_size),\n        radius_span);\n    hydro_interpolation<stencil_size>(\n        make_not_null(&target_fluid_velocity),\n        // Note: we use max density ratio for velocity\n        max_density_ratio_for_linear_interpolation_, target_radius,\n        gsl::make_span(&fluid_velocity_rad_stencil[0], stencil_size),\n        radius_span);\n  }\n  intrp::polynomial_interpolation<stencil_size - 1>(\n      make_not_null(&target_alpha), make_not_null(&error_y), target_radius,\n      gsl::make_span(&alpha_rad_stencil[0], stencil_size), radius_span);\n  intrp::polynomial_interpolation<stencil_size - 1>(\n      make_not_null(&target_rho), make_not_null(&error_y), target_radius,\n      gsl::make_span(&rho_rad_stencil[0], stencil_size), radius_span);\n  intrp::polynomial_interpolation<stencil_size - 1>(\n      make_not_null(&target_gamma), make_not_null(&error_y), target_radius,\n      gsl::make_span(&gamma_rad_stencil[0], stencil_size), radius_span);\n  intrp::polynomial_interpolation<stencil_size - 1>(\n      make_not_null(&target_omega), make_not_null(&error_y), target_radius,\n      gsl::make_span(&omega_rad_stencil[0], stencil_size), radius_span);\n\n  if (interpolate_hydro_vars) {\n    if (UNLIKELY(target_rest_mass_density < 0.0)) {\n      ERROR(\n          \"Failed to interpolate to (r, cos(theta)) = (\"\n          << target_radius << ',' << target_cos_theta\n          << \") because the resulting density is negative: \"\n          << target_rest_mass_density\n          << \". The interpolation should select linear interpolation to avoid \"\n             \"generating negative densities. Please file an issue so this bug \"\n             \"can get fixed.\");\n    }\n  }\n\n  return {\n      {interpolate_hydro_vars ? target_rest_mass_density\n                              : std::numeric_limits<double>::signaling_NaN(),\n       interpolate_hydro_vars ? target_fluid_velocity\n                              : std::numeric_limits<double>::signaling_NaN(),\n       target_alpha, target_rho, target_gamma, target_omega}};\n}\n\nnamespace {\ntemplate <typename DataType>\nvoid compute_angular_coordinates(\n    const gsl::not_null<DataType*> radius,\n    const gsl::not_null<DataType*> cos_theta,\n    const gsl::not_null<DataType*> sin_theta,\n    const gsl::not_null<DataType*> phi,\n    const tnsr::I<DataType, 3, Frame::Inertial>& coords) {\n  const size_t num_points = get_size(get<0>(coords));\n  *radius = get(magnitude(coords));\n  if constexpr (not std::is_fundamental_v<DataType>) {\n    cos_theta->destructive_resize(num_points);\n    sin_theta->destructive_resize(num_points);\n    phi->destructive_resize(num_points);\n  }\n  for (size_t i = 0; i < num_points; ++i) {\n    if (get_element(*radius, i) < 1.e-12) {\n      // cos_theta=0.5 is what SpEC uses. Use that to match.\n      get_element(*cos_theta, i) = 0.5;\n      get_element(*phi, i) = 0.0;\n    } else {\n      using std::atan2;\n      get_element(*cos_theta, i) =\n          get_element(get<2>(coords), i) / get_element(*radius, i);\n      get_element(*phi, i) =\n          atan2(get_element(get<1>(coords), i), get_element(get<0>(coords), i));\n    }\n  }\n  *sin_theta = sqrt(1.0 - *cos_theta * *cos_theta);\n}\n}  // namespace\n}  // namespace detail\n\nRotatingStar::RotatingStar(std::string rot_ns_filename,\n                           double polytropic_constant)\n    : rot_ns_filename_(std::move(rot_ns_filename)),\n      cst_solution_{rot_ns_filename_, true, polytropic_constant},\n      polytropic_constant_(polytropic_constant),\n      polytropic_exponent_{1.0 + 1.0 / cst_solution_.polytropic_index()},\n      is_polytrope_{true} {\n  equation_of_state_ =\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(\n          polytropic_constant_, polytropic_exponent_);\n}\n\nRotatingStar::RotatingStar(\n    std::string rot_ns_filename,\n    std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>\n        equation_of_state)\n    : rot_ns_filename_(std::move(rot_ns_filename)),\n      cst_solution_{rot_ns_filename_, false},\n      equation_of_state_(std::move(equation_of_state)) {}\n\nRotatingStar::RotatingStar(const RotatingStar& rhs)\n    : evolution::initial_data::InitialData(rhs),\n      rot_ns_filename_(rhs.rot_ns_filename_),\n      cst_solution_{rot_ns_filename_, rhs.is_polytrope_,\n                    rhs.polytropic_constant_},\n      polytropic_constant_(rhs.polytropic_constant_),\n      polytropic_exponent_(rhs.polytropic_exponent_),\n      is_polytrope_{rhs.is_polytrope_},\n      equation_of_state_(rhs.equation_of_state_->get_clone()) {}\n\nRotatingStar& RotatingStar::operator=(const RotatingStar& rhs) {\n  rot_ns_filename_ = rhs.rot_ns_filename_;\n  polytropic_constant_ = rhs.polytropic_constant_;\n  cst_solution_ = detail::CstSolution(rhs.rot_ns_filename_, rhs.is_polytrope_,\n                                      rhs.polytropic_constant_);\n  polytropic_exponent_ = rhs.polytropic_exponent_;\n  is_polytrope_ = rhs.is_polytrope_;\n  equation_of_state_ = rhs.equation_of_state_->get_clone();\n  return *this;\n}\n\nRotatingStar::RotatingStar(CkMigrateMessage* msg) : InitialData(msg) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> RotatingStar::get_clone()\n    const {\n  return std::make_unique<RotatingStar>(*this);\n}\n\nvoid RotatingStar::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | rot_ns_filename_;\n  p | cst_solution_;\n  p | polytropic_constant_;\n  p | polytropic_exponent_;\n  p | is_polytrope_;\n  p | equation_of_state_;\n}\n\ntemplate <typename DataType>\nRotatingStar::IntermediateVariables<DataType>::IntermediateVariables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& in_coords,\n    const double in_delta_r)\n    : coords(in_coords),\n      radius(get_size(get<0>(coords))),\n      phi(get_size(radius)),\n      cos_theta(get_size(radius)),\n      sin_theta(get_size(radius)),\n      delta_r(in_delta_r) {\n  detail::compute_angular_coordinates(\n      make_not_null(&radius), make_not_null(&cos_theta),\n      make_not_null(&sin_theta), make_not_null(&phi), coords);\n}\n\ntemplate <typename DataType>\nRotatingStar::IntermediateVariables<DataType>::IntermediateVariables::\n    MetricData::MetricData(const size_t num_points)\n    : alpha(make_with_value<DataType>(\n          num_points, std::numeric_limits<double>::signaling_NaN())),\n      rho(make_with_value<DataType>(\n          num_points, std::numeric_limits<double>::signaling_NaN())),\n      gamma(make_with_value<DataType>(\n          num_points, std::numeric_limits<double>::signaling_NaN())),\n      omega(make_with_value<DataType>(\n          num_points, std::numeric_limits<double>::signaling_NaN())) {}\n\ntemplate <typename DataType>\nvoid RotatingStar::interpolate_vars_if_necessary(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  if (vars->rest_mass_density.has_value()) {\n    return;\n  }\n  const size_t num_points = get_size(vars->radius);\n  vars->rest_mass_density = make_with_value<DataType>(num_points, 0.0);\n  vars->fluid_velocity = make_with_value<DataType>(num_points, 0.0);\n  vars->metric_data =\n      typename IntermediateVariables<DataType>::MetricData(num_points);\n  for (size_t i = 0; i < num_points; ++i) {\n    const std::array<double, 6> interpolated_data = cst_solution_.interpolate(\n        get_element(vars->radius, i), get_element(vars->cos_theta, i), true);\n    get_element(vars->rest_mass_density.value(), i) = interpolated_data[0];\n    get_element(vars->fluid_velocity.value(), i) = interpolated_data[1];\n    get_element(vars->metric_data->alpha, i) = interpolated_data[2];\n    get_element(vars->metric_data->rho, i) = interpolated_data[3];\n    get_element(vars->metric_data->gamma, i) = interpolated_data[4];\n    get_element(vars->metric_data->omega, i) = interpolated_data[5];\n  }\n}\n\ntemplate <typename DataType>\nvoid RotatingStar::interpolate_deriv_vars_if_necessary(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars) const {\n  if (vars->metric_data_upper.has_value()) {\n    return;\n  }\n  vars->metric_data_upper =\n      std::array<typename IntermediateVariables<DataType>::MetricData, 3>{};\n  vars->metric_data_lower =\n      std::array<typename IntermediateVariables<DataType>::MetricData, 3>{};\n  const double dr = vars->delta_r;\n  const size_t num_points = get_size(get<0>(vars->coords));\n  for (size_t d = 0; d < 3; ++d) {\n    auto coord_upper = vars->coords;\n    coord_upper.get(d) += dr;\n    auto coord_lower = vars->coords;\n    coord_lower.get(d) -= dr;\n\n    detail::compute_angular_coordinates(\n        make_not_null(&gsl::at(vars->radius_upper, d)),\n        make_not_null(&gsl::at(vars->cos_theta_upper, d)),\n        make_not_null(&gsl::at(vars->sin_theta_upper, d)),\n        make_not_null(&gsl::at(vars->phi_upper, d)), coord_upper);\n    detail::compute_angular_coordinates(\n        make_not_null(&gsl::at(vars->radius_lower, d)),\n        make_not_null(&gsl::at(vars->cos_theta_lower, d)),\n        make_not_null(&gsl::at(vars->sin_theta_lower, d)),\n        make_not_null(&gsl::at(vars->phi_lower, d)), coord_lower);\n\n    gsl::at(vars->metric_data_upper.value(), d) =\n        typename IntermediateVariables<DataType>::MetricData(num_points);\n    gsl::at(vars->metric_data_lower.value(), d) =\n        typename IntermediateVariables<DataType>::MetricData(num_points);\n\n    for (size_t i = 0; i < num_points; ++i) {\n      const std::array<double, 6> interpolated_data = cst_solution_.interpolate(\n          get_element(gsl::at(vars->radius_upper, d), i),\n          get_element(gsl::at(vars->cos_theta_upper, d), i), false);\n      get_element(gsl::at(vars->metric_data_upper.value(), d).alpha, i) =\n          interpolated_data[2];\n      get_element(gsl::at(vars->metric_data_upper.value(), d).rho, i) =\n          interpolated_data[3];\n      get_element(gsl::at(vars->metric_data_upper.value(), d).gamma, i) =\n          interpolated_data[4];\n      get_element(gsl::at(vars->metric_data_upper.value(), d).omega, i) =\n          interpolated_data[5];\n    }\n    for (size_t i = 0; i < num_points; ++i) {\n      const std::array<double, 6> interpolated_data = cst_solution_.interpolate(\n          get_element(gsl::at(vars->radius_lower, d), i),\n          get_element(gsl::at(vars->cos_theta_lower, d), i), false);\n      get_element(gsl::at(vars->metric_data_lower.value(), d).alpha, i) =\n          interpolated_data[2];\n      get_element(gsl::at(vars->metric_data_lower.value(), d).rho, i) =\n          interpolated_data[3];\n      get_element(gsl::at(vars->metric_data_lower.value(), d).gamma, i) =\n          interpolated_data[4];\n      get_element(gsl::at(vars->metric_data_lower.value(), d).omega, i) =\n          interpolated_data[5];\n    }\n  }\n}\n\ntemplate <typename DataType>\nScalar<DataType> RotatingStar::lapse(const DataType& gamma,\n                                     const DataType& rho) const {\n  return Scalar<DataType>{exp(0.5 * (gamma + rho))};\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3, Frame::Inertial> RotatingStar::shift(\n    const DataType& omega, const DataType& phi, const DataType& radius,\n    const DataType& sin_theta) const {\n  auto result =\n      make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(radius, 0.0);\n  get<0>(result) = sin(phi) * omega * radius * sin_theta;\n  get<1>(result) = -cos(phi) * omega * radius * sin_theta;\n  return result;\n}\n\ntemplate <typename DataType>\ntnsr::ii<DataType, 3, Frame::Inertial> RotatingStar::spatial_metric(\n    const DataType& gamma, const DataType& rho, const DataType& alpha,\n    const DataType& phi) const {\n  auto result =\n      make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(gamma, 0.0);\n  for (size_t i = 0; i < get_size(gamma); ++i) {\n    const double g_rr = exp(2.0 * get_element(alpha, i));\n    const double g_phi_phi = exp(get_element(gamma, i) - get_element(rho, i));\n    const double cos_phi = cos(get_element(phi, i));\n    const double sin_phi = sin(get_element(phi, i));\n    // Note: We analytically simplify the expressions using trig identities but\n    // have left in the full expressions relating to the jacobian (L) for\n    // comparison with SpEC.\n    //\n    // const double g_theta_theta = g_rr;\n    // const double Lrx = get_element(sin_theta, i) * cos(get_element(phi, i));\n    // const double Lry = get_element(sin_theta, i) * sin(get_element(phi, i));\n    // const double Lrz = get_element(cos_theta, i);\n    // const double Ltx = get_element(cos_theta, i) * cos(get_element(phi, i));\n    // const double Lty = get_element(cos_theta, i) * sin(get_element(phi, i));\n    // const double Ltz = -get_element(sin_theta, i);\n    // const double Lpx = -sin(get_element(phi, i));\n    // const double Lpy = cos(get_element(phi, i));\n    // const double Lpz = 0.0;\n\n    // xx\n    get_element(get<0, 0>(result), i) =\n        square(cos_phi) * g_rr + square(sin_phi) * g_phi_phi;\n    // Lrx * Lrx * g_rr + Ltx * Ltx * g_theta_theta + Lpx * Lpx * g_phi_phi;\n\n    // xy\n    get_element(get<0, 1>(result), i) = cos_phi * sin_phi * (g_rr - g_phi_phi);\n    // Lrx * Lry * g_rr + Ltx * Lty * g_theta_theta + Lpx * Lpy * g_phi_phi;\n\n    // xz\n    // gamma_{xz} = 0\n    // get_element(get<0, 2>(result), i) =\n    //     Lrx * Lrz * g_rr + Ltx * Ltz * g_theta_theta + Lpx * Lpz * g_phi_phi;\n\n    // yy\n    get_element(get<1, 1>(result), i) =\n        square(sin_phi) * g_rr + square(cos_phi) * g_phi_phi;\n    // Lry * Lry * g_rr + Lty * Lty * g_theta_theta + Lpy * Lpy * g_phi_phi;\n\n    // yz\n    // gamma_{yz} = 0\n    // get_element(get<1, 2>(result), i) =\n    //     Lrz * Lry * g_rr + Ltz * Lty * g_theta_theta + Lpz * Lpy * g_phi_phi;\n\n    // zz\n    get_element(get<2, 2>(result), i) = g_rr;\n    // Lrz * Lrz * g_rr + Ltz * Ltz * g_theta_theta + Lpz * Lpz * g_phi_phi;\n  }\n  return result;\n}\n\ntemplate <typename DataType>\ntnsr::II<DataType, 3, Frame::Inertial> RotatingStar::inverse_spatial_metric(\n    const DataType& gamma, const DataType& rho, const DataType& alpha,\n    const DataType& phi) const {\n  auto result =\n      make_with_value<tnsr::II<DataType, 3, Frame::Inertial>>(gamma, 0.0);\n  for (size_t i = 0; i < get_size(gamma); ++i) {\n    const double one_over_g_rr = exp(-2.0 * get_element(alpha, i));\n    const double one_over_g_phi_phi =\n        exp(get_element(rho, i) - get_element(gamma, i));\n    const double cos_phi = cos(get_element(phi, i));\n    const double sin_phi = sin(get_element(phi, i));\n    get_element(get<0, 0>(result), i) =\n        square(sin_phi) * one_over_g_phi_phi + square(cos_phi) * one_over_g_rr;\n    get_element(get<0, 1>(result), i) =\n        cos_phi * sin_phi * (one_over_g_rr - one_over_g_phi_phi);\n    get_element(get<1, 1>(result), i) =\n        square(cos_phi) * one_over_g_phi_phi + square(sin_phi) * one_over_g_rr;\n    get_element(get<2, 2>(result), i) = one_over_g_rr;\n  }\n  return result;\n}\n\ntemplate <typename DataType>\nScalar<DataType> RotatingStar::sqrt_det_spatial_metric(\n    const DataType& gamma, const DataType& rho, const DataType& alpha) const {\n  return Scalar<DataType>{exp(2.0 * alpha + 0.5 * (gamma - rho))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>\nRotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  using std::max;\n  return {Scalar<DataType>{\n      DataType{max(atmosphere_floor_, vars->rest_mass_density.value())}}};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>\nRotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/) const {\n  return {hydro::relativistic_specific_enthalpy(\n      get<hydro::Tags::RestMassDensity<DataType>>(variables(\n          vars, x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n      get<hydro::Tags::SpecificInternalEnergy<DataType>>(variables(\n          vars, x,\n          tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})),\n      get<hydro::Tags::Pressure<DataType>>(\n          variables(vars, x, tmpl::list<hydro::Tags::Pressure<DataType>>{})))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Temperature<DataType>> RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const {\n  const auto rest_mass_density = get<hydro::Tags::RestMassDensity<DataType>>(\n      variables(vars, x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{}));\n  return {equation_of_state_->temperature_from_density(rest_mass_density)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::Pressure<DataType>> RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const {\n  return {equation_of_state_->pressure_from_density(\n      get<hydro::Tags::RestMassDensity<DataType>>(variables(\n          vars, x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>\nRotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const {\n  return {equation_of_state_->specific_internal_energy_from_density(\n      get<hydro::Tags::RestMassDensity<DataType>>(variables(\n          vars, x, tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})))};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nRotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /* vars */,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n\n  auto ye = make_with_value<Scalar<DataType>>(x, 0.1);\n\n  return {std::move(ye)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>\nRotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  auto spatial_velocity = make_with_value<tnsr::I<DataType, 3>>(x, 0.0);\n  // temp compute v=(Omega-omega)r\\sin(\\theta) e^{-\\rho}\n  get<0>(spatial_velocity) =\n      (vars->fluid_velocity.value() - vars->metric_data->omega) * vars->radius *\n      vars->sin_theta * exp(-vars->metric_data->rho);\n  get<1>(spatial_velocity) =\n      get<0>(spatial_velocity)  // temp for v\n      * cos(vars->phi) *\n      exp(0.5 * (vars->metric_data->rho - vars->metric_data->gamma));\n  get<0>(spatial_velocity) *=\n      -sin(vars->phi) *\n      exp(0.5 * (vars->metric_data->rho - vars->metric_data->gamma));\n  return {std::move(spatial_velocity)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>\nRotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  auto lorentz_factor = make_with_value<Scalar<DataType>>(get<0>(x), 0.0);\n  // Compute spatial proper velocity, then Lorentz factor.\n  get(lorentz_factor) =\n      (vars->fluid_velocity.value() - vars->metric_data->omega) * vars->radius *\n      vars->sin_theta * exp(-vars->metric_data->rho);\n  get(lorentz_factor) = 1.0 / sqrt(1.0 - square(get(lorentz_factor)));\n  return {std::move(lorentz_factor)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>\nRotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/) const {\n  return {make_with_value<tnsr::I<DataType, 3>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nRotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.0)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::Lapse<DataType>> RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  return lapse(vars->metric_data->gamma, vars->metric_data->rho);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::Shift<DataType, 3>> RotatingStar::variables(\n    gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::Shift<DataType, 3>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  return shift(vars->metric_data->omega, vars->phi, vars->radius,\n               vars->sin_theta);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, 3>>\nRotatingStar::variables(\n    gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::SpatialMetric<DataType, 3>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  return spatial_metric(vars->metric_data->gamma, vars->metric_data->rho,\n                        vars->metric_data->alpha, vars->phi);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>\nRotatingStar::variables(\n    gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  return sqrt_det_spatial_metric(vars->metric_data->gamma,\n                                 vars->metric_data->rho,\n                                 vars->metric_data->alpha);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::InverseSpatialMetric<DataType, 3>>\nRotatingStar::variables(\n    gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& /*x*/,\n    tmpl::list<gr::Tags::InverseSpatialMetric<DataType, 3>> /*meta*/) const {\n  interpolate_vars_if_necessary(vars);\n  return inverse_spatial_metric(vars->metric_data->gamma,\n                                vars->metric_data->rho,\n                                vars->metric_data->alpha, vars->phi);\n}\n\ntemplate <typename DataType>\nauto RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>> {\n  return make_with_value<Scalar<DataType>>(get<0>(x), 0.0);\n}\n\ntemplate <typename DataType>\nauto RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<DerivLapse<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivLapse<DataType>> {\n  interpolate_deriv_vars_if_necessary(vars);\n  // Do 2nd-order FD\n  auto deriv_lapse =\n      make_with_value<tnsr::i<DataType, 3, Frame::Inertial>>(get<0>(x), 0.0);\n  for (size_t d = 0; d < 3; ++d) {\n    const auto lapse_upper =\n        lapse(gsl::at(vars->metric_data_upper.value(), d).gamma,\n              gsl::at(vars->metric_data_upper.value(), d).rho);\n    const auto lapse_lower =\n        lapse(gsl::at(vars->metric_data_lower.value(), d).gamma,\n              gsl::at(vars->metric_data_lower.value(), d).rho);\n    deriv_lapse.get(d) =\n        (0.5 / vars->delta_r) * (get(lapse_upper) - get(lapse_lower));\n  }\n  return deriv_lapse;\n}\n\ntemplate <typename DataType>\nauto RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, 3>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, 3>>> {\n  return make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(get<0>(x), 0.0);\n}\n\ntemplate <typename DataType>\nauto RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<DerivShift<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivShift<DataType>> {\n  interpolate_deriv_vars_if_necessary(vars);\n  // Do 2nd-order FD\n  auto deriv_shift =\n      make_with_value<tnsr::iJ<DataType, 3, Frame::Inertial>>(get<0>(x), 0.0);\n  for (size_t d = 0; d < 3; ++d) {\n    const auto shift_upper =\n        shift(gsl::at(vars->metric_data_upper.value(), d).omega,\n              gsl::at(vars->phi_upper, d), gsl::at(vars->radius_upper, d),\n              gsl::at(vars->sin_theta_upper, d));\n    const auto shift_lower =\n        shift(gsl::at(vars->metric_data_lower.value(), d).omega,\n              gsl::at(vars->phi_lower, d), gsl::at(vars->radius_lower, d),\n              gsl::at(vars->sin_theta_lower, d));\n    for (size_t i = 0; i < 3; ++i) {\n      deriv_shift.get(d, i) =\n          (0.5 / vars->delta_r) * (shift_upper.get(i) - shift_lower.get(i));\n    }\n  }\n  return deriv_shift;\n}\n\ntemplate <typename DataType>\nauto RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> /*vars*/,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>> /*meta*/) const\n    -> tuples::TaggedTuple<::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>> {\n  return make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(get<0>(x),\n                                                                 0.0);\n}\n\ntemplate <typename DataType>\nauto RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n    -> tuples::TaggedTuple<DerivSpatialMetric<DataType>> {\n  interpolate_deriv_vars_if_necessary(vars);\n  // Do 2nd-order FD. This isn't super accurate at r=0, but it's fine everywhere\n  // else.\n  auto deriv_spatial_metric =\n      make_with_value<tnsr::ijj<DataType, 3, Frame::Inertial>>(get<0>(x), 0.0);\n  for (size_t d = 0; d < 3; ++d) {\n    const auto spatial_metric_upper =\n        spatial_metric(gsl::at(vars->metric_data_upper.value(), d).gamma,\n                       gsl::at(vars->metric_data_upper.value(), d).rho,\n                       gsl::at(vars->metric_data_upper.value(), d).alpha,\n                       gsl::at(vars->phi_upper, d));\n    const auto spatial_metric_lower =\n        spatial_metric(gsl::at(vars->metric_data_lower.value(), d).gamma,\n                       gsl::at(vars->metric_data_lower.value(), d).rho,\n                       gsl::at(vars->metric_data_lower.value(), d).alpha,\n                       gsl::at(vars->phi_lower, d));\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        deriv_spatial_metric.get(d, i, j) =\n            (0.5 / vars->delta_r) *\n            (spatial_metric_upper.get(i, j) - spatial_metric_lower.get(i, j));\n      }\n    }\n  }\n  return deriv_spatial_metric;\n}\n\ntemplate <typename DataType>\nauto RotatingStar::variables(\n    const gsl::not_null<IntermediateVariables<DataType>*> vars,\n    const tnsr::I<DataType, 3>& x,\n    tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, 3>> /*meta*/) const\n    -> tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, 3>> {\n  return gr::extrinsic_curvature(\n      get<gr::Tags::Lapse<DataType>>(\n          variables(vars, x, tmpl::list<gr::Tags::Lapse<DataType>>{})),\n      get<gr::Tags::Shift<DataType, 3>>(\n          variables(vars, x, tmpl::list<gr::Tags::Shift<DataType, 3>>{})),\n      get<DerivShift<DataType>>(\n          variables(vars, x, tmpl::list<DerivShift<DataType>>{})),\n      get<gr::Tags::SpatialMetric<DataType, 3>>(variables(\n          vars, x, tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{})),\n      make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(x, 0.0),\n      get<DerivSpatialMetric<DataType>>(\n          variables(vars, x, tmpl::list<DerivSpatialMetric<DataType>>{})));\n}\n\nPUP::able::PUP_ID RotatingStar::my_PUP_ID = 0;\n\nbool operator==(const RotatingStar& lhs, const RotatingStar& rhs) {\n  return lhs.rot_ns_filename_ == rhs.rot_ns_filename_ and\n         lhs.polytropic_constant_ == rhs.polytropic_constant_ and\n         lhs.polytropic_exponent_ == rhs.polytropic_exponent_ and\n         *lhs.equation_of_state_ == *rhs.equation_of_state_;\n}\n\nbool operator!=(const RotatingStar& lhs, const RotatingStar& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template class RotatingStar::IntermediateVariables<DTYPE(data)>;          \\\n  template Scalar<DTYPE(data)> RotatingStar::lapse(                         \\\n      const DTYPE(data) & gamma, const DTYPE(data) & rho) const;            \\\n  template tnsr::I<DTYPE(data), 3, Frame::Inertial> RotatingStar::shift(    \\\n      const DTYPE(data) & omega, const DTYPE(data) & phi,                   \\\n      const DTYPE(data) & radius, const DTYPE(data) & sin_theta) const;     \\\n  template tnsr::ii<DTYPE(data), 3, Frame::Inertial>                        \\\n  RotatingStar::spatial_metric(                                             \\\n      const DTYPE(data) & gamma, const DTYPE(data) & rho,                   \\\n      const DTYPE(data) & alpha, const DTYPE(data) & phi) const;            \\\n  template tnsr::II<DTYPE(data), 3, Frame::Inertial>                        \\\n  RotatingStar::inverse_spatial_metric(                                     \\\n      const DTYPE(data) & gamma, const DTYPE(data) & rho,                   \\\n      const DTYPE(data) & alpha, const DTYPE(data) & phi) const;            \\\n  template Scalar<DTYPE(data)> RotatingStar::sqrt_det_spatial_metric(       \\\n      const DTYPE(data) & gamma, const DTYPE(data) & rho,                   \\\n      const DTYPE(data) & alpha) const;                                     \\\n  template void RotatingStar::interpolate_vars_if_necessary(                \\\n      const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars) const; \\\n  template void RotatingStar::interpolate_deriv_vars_if_necessary(          \\\n      const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef INSTANTIATE\n\n#define TAG(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                                     \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data)>>                 \\\n      RotatingStar::variables(                                           \\\n          const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars, \\\n          const tnsr::I<DTYPE(data), 3>& x,                              \\\n          tmpl::list<TAG(data) < DTYPE(data)>> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(\n    INSTANTIATE_SCALARS, (double, DataVector),\n    (hydro::Tags::RestMassDensity, hydro::Tags::ElectronFraction,\n     hydro::Tags::SpecificInternalEnergy, hydro::Tags::Pressure,\n     hydro::Tags::Temperature, hydro::Tags::DivergenceCleaningField,\n     hydro::Tags::LorentzFactor, hydro::Tags::SpecificEnthalpy, gr::Tags::Lapse,\n     gr::Tags::SqrtDetSpatialMetric))\n\n#undef INSTANTIATE_SCALARS\n\n#define INSTANTIATE_MHD_VECTORS(_, data)                                     \\\n  template tuples::TaggedTuple<TAG(data) < DTYPE(data), 3, Frame::Inertial>> \\\n      RotatingStar::variables(                                               \\\n          const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars,     \\\n          const tnsr::I<DTYPE(data), 3>& x,                                  \\\n          tmpl::list<TAG(data) < DTYPE(data), 3, Frame::Inertial>> /*meta*/) \\\n          const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_MHD_VECTORS, (double, DataVector),\n                        (hydro::Tags::SpatialVelocity,\n                         hydro::Tags::MagneticField))\n\n#undef INSTANTIATE_MHD_VECTORS\n\n#define INSTANTIATE_METRIC_TENSORS(_, data)                                   \\\n  template tuples::TaggedTuple < TAG(data) < DTYPE(data),                     \\\n      3 >> RotatingStar::variables(                                           \\\n               const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars, \\\n               const tnsr::I<DTYPE(data), 3>& x,                              \\\n               tmpl::list < TAG(data) < DTYPE(data), 3 >>                     \\\n               /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_METRIC_TENSORS, (double, DataVector),\n                        (gr::Tags::Shift, gr::Tags::SpatialMetric,\n                         gr::Tags::InverseSpatialMetric,\n                         gr::Tags::ExtrinsicCurvature))\n\n#undef INSTANTIATE_METRIC_TENSORS\n\n#define INSTANTIATE_METRIC_DERIVS(_, data)                                   \\\n  template auto RotatingStar::variables(                                     \\\n      const gsl::not_null<IntermediateVariables<DTYPE(data)>*> vars,         \\\n      const tnsr::I<DTYPE(data), 3>& x, tmpl::list<TAG(data) < DTYPE(data)>> \\\n      /*meta*/) const->tuples::TaggedTuple<TAG(data) < DTYPE(data)>> ;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_METRIC_DERIVS, (double, DataVector),\n                        (DerivLapse, DerivShift, DerivSpatialMetric))\n\n#undef INSTANTIATE_METRIC_DERIVS\n\n#define INSTANTIATE_METRIC_TIME_DERIVS_SCALARS(_, data)                 \\\n  template auto RotatingStar::variables(                                \\\n      const gsl::not_null<IntermediateVariables<DTYPE(data)>*> /*vars*/,\\\n      const tnsr::I<DTYPE(data), 3>& x,                                 \\\n      tmpl::list < ::Tags::dt < TAG(data) < DTYPE(data) >>>             \\\n      /*meta*/) const                                                   \\\n      -> tuples::TaggedTuple < ::Tags::dt < TAG(data) < DTYPE(data) >>> \\\n      ;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_METRIC_TIME_DERIVS_SCALARS,\n                        (double, DataVector), (gr::Tags::Lapse))\n\n#undef INSTANTIATE_METRIC_TIME_DERIVS_SCALARS\n\n#define INSTANTIATE_METRIC_TIME_DERIVS_TENSORS(_, data)              \\\n  template auto RotatingStar::variables(                             \\\n      const gsl::not_null<IntermediateVariables<DTYPE(data)>*>       \\\n      /*vars*/,                                                      \\\n      const tnsr::I<DTYPE(data), 3>& x,                              \\\n      tmpl::list < ::Tags::dt < TAG(data) < DTYPE(data), 3 >>>       \\\n      /*meta*/) const                                                \\\n      -> tuples::TaggedTuple < ::Tags::dt < TAG(data) < DTYPE(data), \\\n      3 >>>                                                          \\\n      ;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_METRIC_TIME_DERIVS_TENSORS,\n                        (double, DataVector),\n                        (gr::Tags::Shift, gr::Tags::SpatialMetric))\n\n#undef INSTANTIATE_METRIC_TIME_DERIVS_TENSORS\n\n#undef TAG\n#undef DTYPE\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RelativisticEuler::Solutions {\nnamespace detail {\n/*!\n * \\brief Read the CST rotating neutron star solution from file, rescaling the\n * solution.  If the EoS in RotNS is set to PT (polytrope), then the user\n * should use `is_polytrope=true`, so that the normalization is done using the\n * method in \\cite Cook1992. In this case, RotNS already uses geometric units,\n * letting \\f$K\\f$ (i.e.  \\f$P=K\\rho^\\gamma\\f$) be a free parameter. RotNS\n * assumes \\f$K=1\\f$ for its calculations. If the user doesn't want to rescale,\n * they can just set `polytropic_constant=1.` and nothing will change.\n *\n * If any other EoS is used, then it's done according to the method in\n * \\cite Cook1994. In this case, RotNS instead normalizes with CGS, so\n * `is_polytrope=false` converts from CGS into geometric units. The user would\n * not want to use this for a PT EoS, even if they don't want to rescale.\n */\nclass CstSolution {\n public:\n  CstSolution() = default;\n  CstSolution(const std::string& filename, bool is_polytrope,\n              double polytropic_constant);\n\n  std::array<double, 6> interpolate(double target_radius,\n                                    double target_cos_theta,\n                                    bool interpolate_hydro_vars) const;\n\n  double polytropic_index() const { return polytropic_index_; }\n\n  double equatorial_radius() const { return equatorial_radius_; }\n\n  void pup(PUP::er& p);\n\n private:\n  double maximum_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double max_density_ratio_for_linear_interpolation_ = 1.0e2;\n\n  double equatorial_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double polytropic_index_{std::numeric_limits<double>::signaling_NaN()};\n  double central_angular_speed_{std::numeric_limits<double>::signaling_NaN()};\n  double rotation_profile_{std::numeric_limits<double>::signaling_NaN()};\n  size_t num_radial_points_{std::numeric_limits<size_t>::max()};\n  size_t num_angular_points_{std::numeric_limits<size_t>::max()};\n  size_t num_grid_points_{std::numeric_limits<size_t>::max()};\n\n  DataVector radius_;\n  DataVector cos_theta_;  // Note that cos(theta) is between 0 and 1.\n  DataVector rest_mass_density_;\n  DataVector fluid_velocity_;\n  DataVector alpha_;\n  DataVector rho_;\n  DataVector gamma_;\n  DataVector omega_;\n};\n}  // namespace detail\n\n/*!\n * \\brief A solution obtained by reading in rotating neutron star initial data\n * from the RotNS code based on \\cite Cook1992 and \\cite Cook1994.\n *\n * The code that generates the initial data is part of a private SXS repository\n * called `RotNS`.\n *\n * The metric in spherical coordinates is given by \\cite Cook1992\n *\n * \\f{align}{\n *   ds^2=-e^{\\gamma+\\rho}dt^2+e^{2\\alpha}(dr^2+r^2d\\theta^2)\n *   +e^{\\gamma-\\rho}r^2\\sin^2(\\theta)(d\\phi-\\omega dt)^2.\n * \\f}\n *\n * We use rotation about the \\f$z\\f$-axis. That is,\n *\n * \\f{align}{\n *   g_{tt}\n *   &=-e^{\\gamma+\\rho} + e^{\\gamma-\\rho}r^2\\sin^2(\\theta)\\omega^2 \\\\\n *   g_{rr}\n *   &=e^{2\\alpha} \\\\\n *   g_{\\theta\\theta}\n *   &=e^{2\\alpha}r^2 \\\\\n *   g_{\\phi\\phi}\n *   &=e^{\\gamma-\\rho}r^2\\sin^2(\\theta) \\\\\n *   g_{t\\phi}\n *   &=-e^{\\gamma-\\rho}r^2\\sin^2(\\theta)\\omega.\n * \\f}\n *\n * We can transform from spherical to Cartesian coordinates using\n *\n * \\f{align}{\n *   \\label{eq:Jacobian}\n *   \\frac{\\partial (r,\\theta,\\phi)}{\\partial (x,y,z)}=\n *   \\begin{pmatrix}\n *     \\cos(\\phi) \\sin(\\theta) & \\sin(\\theta)\\sin(\\phi) & \\cos(\\theta) \\\\\n *     \\tfrac{\\cos(\\phi)\\cos(\\theta)}{r} & \\tfrac{\\sin(\\phi)\\cos(\\theta)}{r}\n *     & -\\tfrac{\\sin(\\theta)}{r} \\\\\n *     -\\tfrac{\\sin(\\phi)}{r\\sin(\\theta)} & \\tfrac{\\cos(\\phi)}{r\\sin(\\theta)}\n *     & 0\n *   \\end{pmatrix}\n * \\f}\n *\n * and\n *\n * \\f{align}{\n *   \\frac{\\partial (x,y,z)}{\\partial (r,\\theta,\\phi)}=\n *   \\begin{pmatrix}\n *     \\cos(\\phi) \\sin(\\theta) & r\\cos(\\phi)\\cos(\\theta) &\n * -r\\sin(\\theta)\\sin(\\phi)\n *     \\\\\n *     \\sin(\\theta)\\sin(\\phi) & r\\sin(\\phi)\\cos(\\theta) &\n * r\\cos(\\phi)\\sin(\\theta)\n *     \\\\\n *     \\cos(\\theta) & -r\\sin(\\theta) & 0\n *   \\end{pmatrix}\n * \\f}\n *\n *\n * We denote the lapse as \\f$N\\f$ since \\f$\\alpha\\f$ is already being used,\n *\n * \\f{align}{\n *   N = e^{(\\gamma+\\rho)/2}.\n * \\f}\n *\n * The shift is\n *\n * \\f{align}{\n *   \\beta^\\phi =& -\\omega\n * \\f}\n *\n * so\n *\n * \\f{align}{\n *   \\beta^x &= \\partial_\\phi x \\beta^\\phi = \\sin(\\phi)\\omega r \\sin(\\theta), \\\\\n *   \\beta^y &= \\partial_\\phi y \\beta^\\phi = -\\cos(\\phi)\\omega r \\sin(\\theta),\n * \\\\ \\beta^z &= 0. \\f}\n *\n * The spatial metric is\n *\n * \\f{align}{\n *   \\gamma_{xx}\n *   &= \\sin^2(\\theta)\\cos^2(\\phi) e^{(2 \\alpha)}\n *      + \\cos^2(\\theta)\\cos^2(\\phi) e^{(2 \\alpha)}\n *      + \\sin^2(\\phi) e^{(\\gamma-\\rho)} \\notag \\\\\n *   &=\\cos^2(\\phi)e^{2\\alpha} + \\sin^2(\\phi) e^{(\\gamma-\\rho)} \\\\\n *   \\gamma_{xy}\n *   &= \\sin^2(\\theta)\\cos(\\phi)\\sin(\\phi) e^{(2 \\alpha)}\n *      + \\cos^2(\\theta)\\cos(\\phi)\\sin(\\phi) e^{(2 \\alpha)}\n *      - \\sin(\\phi)\\cos(\\phi) e^{(\\gamma-\\rho)} \\notag \\\\\n *   &=\\cos(\\phi)\\sin(\\phi)e^{2\\alpha} - \\sin(\\phi)\\cos(\\phi) e^{(\\gamma-\\rho)}\n * \\\\ \\gamma_{yy}\n *   &= \\sin^2(\\theta)\\sin^2(\\phi) e^{(2 \\alpha)}\n *      + \\cos^2(\\theta)\\sin^2(\\phi) e^{(2 \\alpha)}\n *      + \\cos^2(\\phi) e^{(\\gamma-\\rho)} \\notag \\\\\n *   &=\\sin^2(\\phi)e^{2\\alpha} + \\cos^2(\\phi) e^{(\\gamma-\\rho)} \\\\\n *   \\gamma_{xz}\n *   &= \\sin(\\theta)\\cos(\\phi)\\cos(\\theta) e^{2\\alpha}\n *      -\\cos(\\theta)\\cos(\\phi)\\sin(\\theta)e^{2\\alpha} = 0 \\\\\n *   \\gamma_{yz}\n *   &= \\sin(\\theta)\\sin(\\phi)\\cos(\\theta) e^{2\\alpha}\n *      -\\cos(\\theta)\\sin(\\phi)\\sin(\\theta)e^{2\\alpha} = 0 \\\\\n *   \\gamma_{zz}\n *   &=\\cos^2(\\theta)e^{2\\alpha} + \\sin^2(\\theta) e^{2\\alpha}\n *      = e^{2\\alpha}\n * \\f}\n *\n * and its determinant is\n *\n * \\f{align}{\n *   \\gamma = e^{4\\alpha + (\\gamma-\\rho)} = e^{4\\alpha}e^{(\\gamma-\\rho)}.\n * \\f}\n *\n * At \\f$r=0\\f$ we have \\f$2\\alpha=\\gamma-\\rho\\f$ and so the\n * \\f$\\gamma_{xx}=\\gamma_{yy}=\\gamma_{zz} = e^{2\\alpha}\\f$ and all other\n * components are zero. The inverse spatial metric is given by\n *\n * \\f{align}{\n *   \\gamma^{xx}\n *   &= \\frac{\\gamma_{yy}}{e^{2\\alpha}e^{(\\gamma-\\rho)}} =\n *      \\left[\\sin^2(\\phi)e^{2\\alpha} + \\cos^2(\\phi) e^{(\\gamma-\\rho)}\\right]\n *      e^{-2\\alpha} e^{-(\\gamma-\\rho)} \\notag \\\\\n *   &=\\sin^2(\\phi)e^{-(\\gamma-\\rho)} + \\cos^2(\\phi) e^{-2\\alpha} \\\\\n *   \\gamma^{yy}\n *   &= \\frac{\\gamma_{xx}}{e^{2\\alpha}e^{(\\gamma-\\rho)}} =\n *      \\left[\\cos^2(\\phi)e^{2\\alpha} + \\sin^2(\\phi) e^{(\\gamma-\\rho)}\\right]\n *      e^{-2\\alpha} e^{-(\\gamma-\\rho)} \\notag \\\\\n *   &=\\cos^2(\\phi) e^{-(\\gamma-\\rho)} + \\sin^2(\\phi) e^{-2\\alpha} \\\\\n *   \\gamma^{xy}\n *   &=\\frac{-\\gamma_{xy}}{e^{2\\alpha}e^{(\\gamma-\\rho)}} =\n *     -\\left[\\cos(\\phi)\\sin(\\phi)e^{2\\alpha} - \\sin(\\phi)\\cos(\\phi)\n *      e^{(\\gamma-\\rho)}\\right] e^{-2\\alpha} e^{-(\\gamma-\\rho)} \\notag \\\\\n *   &=-\\cos(\\phi)\\sin(\\phi)e^{-(\\gamma-\\rho)} -\n *     \\sin(\\phi)\\cos(\\phi)e^{-2\\alpha} \\notag \\\\\n *   &=\\cos(\\phi)\\sin(\\phi) \\left[e^{-2\\alpha} -\n *     e^{-(\\gamma-\\rho)}\\right] \\\\\n *   \\gamma^{xz}\n *   &= 0 \\\\\n *   \\gamma^{yz}\n *   &= 0 \\\\\n *   \\gamma^{zz} &= e^{-2\\alpha}.\n * \\f}\n *\n * The 4-velocity in spherical coordinates is given by\n *\n * \\f{align}{\n *   u^{\\bar{a}}=\\frac{e^{-(\\rho+\\gamma)/2}}{\\sqrt{1-v^2}}\n *   \\left[1,0,0,\\Omega\\right],\n * \\f}\n *\n * where\n *\n * \\f{align}{\n *   v=(\\Omega-\\omega)r\\sin(\\theta)e^{-\\rho}.\n * \\f}\n *\n * Transforming to Cartesian coordinates we have\n *\n * \\f{align}{\n *   u^t\n *   &=\\frac{e^{-(\\rho+\\gamma)/2}}{\\sqrt{1-v^2}} \\\\\n *   u^x\n *   &=\\partial_\\phi x u^\\phi = -r\\sin(\\theta)\\sin(\\phi) u^t\\Omega \\\\\n *   u^y\n *   &=\\partial_\\phi y u^\\phi = r\\sin(\\theta)\\cos(\\phi) u^t\\Omega \\\\\n *   u^z &= 0.\n * \\f}\n *\n * The Lorentz factor is given by\n *\n * \\f{align}{\n *   W\n *   &=Nu^t=e^{(\\gamma+\\rho)/2}\\frac{e^{-(\\rho+\\gamma)/2}}{\\sqrt{1-v^2}} \\notag\n * \\\\\n *   &=\\frac{1}{\\sqrt{1-v^2}}.\n * \\f}\n *\n * Using\n *\n * \\f{align}{\n *   v^i = \\frac{1}{N}\\left(\\frac{u^i}{u^t} + \\beta^i\\right)\n * \\f}\n *\n * we get\n *\n * \\f{align}{\n *   v^x\n *   &= -e^{-(\\gamma+\\rho)/2}r\\sin(\\theta)\\sin(\\phi)(\\Omega-\\omega)\n *   =-e^{-(\\gamma-\\rho)/2}\\sin(\\phi) v\\\\\n *   v^y\n *   &= e^{-(\\gamma+\\rho)/2}r\\sin(\\theta)\\cos(\\phi)(\\Omega-\\omega)\n *   = e^{-(\\gamma-\\rho)/2}\\cos(\\phi)v \\\\\n *   v^z&=0.\n * \\f}\n *\n * Lowering with the spatial metric we get\n *\n * \\f{align}{\n *   v_x\n *   &=\\gamma_{xx} v^x + \\gamma_{xy} v^y \\notag \\\\\n *   &=-\\left[\\cos^2(\\phi)e^{2\\alpha}+\\sin^2(\\phi)e^{\\gamma-\\rho}\\right]\n *     e^{-(\\gamma-\\rho)/2}\\sin(\\phi) v \\notag \\\\\n *   &+\\left[\\cos(\\phi)\\sin(\\phi)e^{2\\alpha} -\n *     \\sin(\\phi)\\cos(\\phi)e^{\\gamma-\\rho}\\right]\n *     e^{-(\\gamma-\\rho)/2}\\cos(\\phi)v \\notag \\\\\n *   &=-e^{-(\\gamma-\\rho)/2}v\\sin(\\phi)e^{\\gamma-\\rho}\n *     \\left[\\sin^2(\\phi)+\\cos^2(\\phi)\\right] \\notag \\\\\n *   &=-e^{(\\gamma-\\rho)/2}v\\sin(\\phi) \\\\\n *   v_y\n *   &=\\gamma_{yx} v^x + \\gamma_{yy} v^y \\notag \\\\\n *   &=-\\left[\\cos(\\phi)\\sin(\\phi)e^{2\\alpha} - \\sin(\\phi)\\cos(\\phi)\n *     e^{(\\gamma-\\rho)}\\right] e^{-(\\gamma-\\rho)/2}\\sin(\\phi) v \\notag \\\\\n *   &+\\left[\\sin^2(\\phi)e^{2\\alpha} + \\cos^2(\\phi) e^{(\\gamma-\\rho)}\\right]\n *     e^{-(\\gamma-\\rho)/2}\\cos(\\phi)v \\notag \\\\\n *   &=e^{(\\gamma-\\rho)/2}v\\cos(\\phi) \\\\\n *   v_z &= 0.\n * \\f}\n *\n * This is consistent with the Lorentz factor read off from \\f$u^t\\f$ since\n * \\f$v^iv_i=v^2\\f$. For completeness, \\f$u_i=Wv_i\\f$ so\n *\n * \\f{align}{\n *   u_x\n *   &=-\\frac{e^{(\\gamma-\\rho)/2}v\\sin(\\phi)}{\\sqrt{1-v^2}} \\\\\n *   u_y\n *   &=\\frac{e^{(\\gamma-\\rho)/2}v\\cos(\\phi)}{\\sqrt{1-v^2}} \\\\\n *   u_z&=0.\n * \\f}\n *\n * \\warning Near (within `1e-2`) \\f$r=0\\f$ the numerical errors from\n * interpolation and computing the metric derivatives by finite difference no\n * longer cancel out and so the `tilde_s` time derivative only vanishes to\n * roughly `1e-8` rather than machine precision. Computing the Cartesian\n * derivatives from analytic differentiation of the radial and angular\n * polynomial fits might improve the situation but is a decent about of work to\n * implement.\n */\nclass RotatingStar : public virtual evolution::initial_data::InitialData,\n                     public MarkAsAnalyticSolution,\n                     public AnalyticSolution<3>,\n                     public hydro::TemperatureInitialization<RotatingStar> {\n  template <typename DataType>\n  struct IntermediateVariables {\n    IntermediateVariables(\n        const tnsr::I<DataType, 3, Frame::Inertial>& in_coords,\n        double in_delta_r);\n\n    struct MetricData {\n      MetricData() = default;\n      MetricData(size_t num_points);\n      DataType alpha;\n      DataType rho;\n      DataType gamma;\n      DataType omega;\n    };\n\n    const tnsr::I<DataType, 3, Frame::Inertial>& coords;\n    DataType radius;\n    DataType phi;\n    DataType cos_theta;\n    DataType sin_theta;\n    // Data at coords\n    std::optional<DataType> rest_mass_density;\n    std::optional<DataType> fluid_velocity;\n    std::optional<MetricData> metric_data;\n    // Data for 2nd-order FD derivatives.\n    //\n    // Technically we could do full-order accurate derivatives by using\n    // Jacobians to transform quantities, but since we really want the initial\n    // data to be solved natively in SpECTRE and satisfy the Einstein equations\n    // with neutrinos and magnetic fields, doing the 2nd order FD like SpEC\n    // should be fine.\n    //\n    // Note: We guard all the upper/lower values by checking if\n    // metric_data_upper is computed. While not perfect, this simplifies the\n    // code a lot.\n    double delta_r;\n    std::optional<std::array<MetricData, 3>> metric_data_upper{};\n    std::optional<std::array<MetricData, 3>> metric_data_lower{};\n    std::array<DataType, 3> radius_upper{};\n    std::array<DataType, 3> radius_lower{};\n    std::array<DataType, 3> cos_theta_upper{};\n    std::array<DataType, 3> cos_theta_lower{};\n    std::array<DataType, 3> sin_theta_upper{};\n    std::array<DataType, 3> sin_theta_lower{};\n    std::array<DataType, 3> phi_upper{};\n    std::array<DataType, 3> phi_lower{};\n  };\n\n public:\n  /// The path to the RotNS data file.\n  struct RotNsFilename {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The path to the RotNS data file.\"};\n  };\n\n  /// The polytropic constant of the fluid.\n  ///\n  /// The data in the RotNS file will be rescaled.\n  struct PolytropicConstant {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The polytropic constant of the fluid.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  using options = tmpl::list<Options::Alternatives<\n      tmpl::list<RotNsFilename, PolytropicConstant>,\n      tmpl::list<RotNsFilename,\n                 hydro::OptionTags::InitialDataEquationOfState<true, 1>>>>;\n\n  static constexpr Options::String help = {\n      \"Rotating neutron star initial data solved by the RotNS solver. The data \"\n      \"is read in from disk. If RotNS used a polytropic equation of state, use \"\n      \"the PolytropicConstant option, so that the polytropic index may be read \"\n      \"directly from the RotNS output. Otherwise, set the equation of state to \"\n      \"whichever one you would like to use to load the initial data. Note that \"\n      \"if a polytrope is put into the EquationOfState option rather than using \"\n      \"the PolytropicConstant option, the generic unit conversion will be used \"\n      \"instead of the specific polytrope one.\"};\n\n  RotatingStar() = default;\n  RotatingStar(const RotatingStar& /*rhs*/);\n  RotatingStar& operator=(const RotatingStar& /*rhs*/);\n  RotatingStar(RotatingStar&& /*rhs*/) = default;\n  RotatingStar& operator=(RotatingStar&& /*rhs*/) = default;\n  ~RotatingStar() override = default;\n\n  RotatingStar(std::string rot_ns_filename,\n               std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>\n                   equation_of_state);\n  RotatingStar(std::string rot_ns_filename, double polytropic_constant);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit RotatingStar(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RotatingStar);\n  /// \\endcond\n\n  /// Retrieve a collection of variables at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         const double /*t*/,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    IntermediateVariables<DataType> intermediate_vars{\n        x, 1.0e-4 * cst_solution_.equatorial_radius()};\n    return {get<Tags>(variables(make_not_null(&intermediate_vars), x,\n                                tmpl::list<Tags>{}))...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  const EquationsOfState::EquationOfState<true, 1>& equation_of_state() const {\n    return *equation_of_state_;\n  }\n\n  double equatorial_radius() const {\n    return cst_solution_.equatorial_radius();\n  }\n\n protected:\n  template <typename DataType>\n  using DerivLapse = ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                                   Frame::Inertial>;\n\n  template <typename DataType>\n  using DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, 3>,\n                                   tmpl::size_t<3>, Frame::Inertial>;\n\n  template <typename DataType>\n  using DerivSpatialMetric = ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>,\n                                           tmpl::size_t<3>, Frame::Inertial>;\n\n  template <typename DataType>\n  void interpolate_vars_if_necessary(\n      gsl::not_null<IntermediateVariables<DataType>*> vars) const;\n\n  template <typename DataType>\n  void interpolate_deriv_vars_if_necessary(\n      gsl::not_null<IntermediateVariables<DataType>*> vars) const;\n\n  template <typename DataType>\n  Scalar<DataType> lapse(const DataType& gamma, const DataType& rho) const;\n\n  template <typename DataType>\n  tnsr::I<DataType, 3, Frame::Inertial> shift(const DataType& omega,\n                                              const DataType& phi,\n                                              const DataType& radius,\n                                              const DataType& sin_theta) const;\n\n  template <typename DataType>\n  tnsr::ii<DataType, 3, Frame::Inertial> spatial_metric(\n      const DataType& gamma, const DataType& rho, const DataType& alpha,\n      const DataType& phi) const;\n\n  template <typename DataType>\n  tnsr::II<DataType, 3, Frame::Inertial> inverse_spatial_metric(\n      const DataType& gamma, const DataType& rho, const DataType& alpha,\n      const DataType& phi) const;\n\n  template <typename DataType>\n  Scalar<DataType> sqrt_det_spatial_metric(const DataType& gamma,\n                                           const DataType& rho,\n                                           const DataType& alpha) const;\n\n  template <typename DataType>\n  auto make_metric_data(size_t num_points) const ->\n      typename IntermediateVariables<DataType>::MetricData;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpecificEnthalpy<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::Pressure<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      gsl::not_null<IntermediateVariables<DataType>*> vars,\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::SpatialVelocity<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::LorentzFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::LorentzFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<hydro::Tags::MagneticField<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(\n      gsl::not_null<IntermediateVariables<DataType>*> vars,\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::Lapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Lapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::Shift<DataType, 3>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::Shift<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::SpatialMetric<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::SpatialMetric<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::SqrtDetSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      gsl::not_null<IntermediateVariables<DataType>*> vars,\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataType, 3>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::InverseSpatialMetric<DataType, 3>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<::Tags::dt<gr::Tags::Lapse<DataType>>> /*meta*/)\n      const -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Lapse<DataType>>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<DerivLapse<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivLapse<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<::Tags::dt<gr::Tags::Shift<DataType, 3>>> /*meta*/)\n      const -> tuples::TaggedTuple<::Tags::dt<gr::Tags::Shift<DataType, 3>>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<DerivShift<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivShift<DataType>>;\n\n  template <typename DataType>\n  auto variables(\n      gsl::not_null<IntermediateVariables<DataType>*> vars,\n      const tnsr::I<DataType, 3>& x,\n      tmpl::list<::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>> /*meta*/)\n      const\n      -> tuples::TaggedTuple<::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<DerivSpatialMetric<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<DerivSpatialMetric<DataType>>;\n\n  template <typename DataType>\n  auto variables(gsl::not_null<IntermediateVariables<DataType>*> vars,\n                 const tnsr::I<DataType, 3>& x,\n                 tmpl::list<gr::Tags::ExtrinsicCurvature<DataType, 3>> /*meta*/)\n      const -> tuples::TaggedTuple<gr::Tags::ExtrinsicCurvature<DataType, 3>>;\n\n  friend bool operator==(const RotatingStar& lhs, const RotatingStar& rhs);\n\n  std::string rot_ns_filename_{};\n  detail::CstSolution cst_solution_{};\n  double polytropic_constant_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent_ = std::numeric_limits<double>::signaling_NaN();\n  bool is_polytrope_{};\n  std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>\n      equation_of_state_;\n  // Floor value to protect EoS from encountering FPEs when computing state\n  // variables in the atmosphere\n  static constexpr double atmosphere_floor_ = 1.e-50;\n};\n\nbool operator!=(const RotatingStar& lhs, const RotatingStar& rhs);\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/SmoothFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/SmoothFlow.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <numeric>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace RelativisticEuler::Solutions {\n\ntemplate <size_t Dim>\nSmoothFlow<Dim>::SmoothFlow(const std::array<double, Dim>& mean_velocity,\n                            const std::array<double, Dim>& wavevector,\n                            const double pressure, const double adiabatic_index,\n                            const double perturbation_size)\n    : smooth_flow{mean_velocity, wavevector, pressure, adiabatic_index,\n                  perturbation_size} {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::initial_data::InitialData>\nSmoothFlow<Dim>::get_clone() const {\n  return std::make_unique<SmoothFlow<Dim>>(*this);\n}\n\ntemplate <size_t Dim>\nSmoothFlow<Dim>::SmoothFlow(CkMigrateMessage* msg)\n    : InitialData(msg), smooth_flow(msg) {}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::MagneticField<DataType, Dim>>\nSmoothFlow<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<hydro::Tags::MagneticField<DataType, Dim>> /*meta*/) const {\n  return make_with_value<tnsr::I<DataType, Dim>>(get<0>(x), 0.0);\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>\nSmoothFlow<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n  return {make_with_value<Scalar<DataType>>(x, 0.1)};\n}\n\ntemplate <size_t Dim>\ntemplate <typename DataType>\ntuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>>\nSmoothFlow<Dim>::variables(\n    const tnsr::I<DataType, Dim>& x, double /*t*/,\n    tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/) const {\n  return make_with_value<Scalar<DataType>>(get<0>(x), 0.0);\n}\n\ntemplate <size_t Dim>\nvoid SmoothFlow<Dim>::pup(PUP::er& p) {\n  InitialData::pup(p);\n  hydro::Solutions::SmoothFlow<Dim, true>::pup(p);\n  p | background_spacetime_;\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID SmoothFlow<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nbool operator==(const SmoothFlow<Dim>& lhs, const SmoothFlow<Dim>& rhs) {\n  using smooth_flow = hydro::Solutions::SmoothFlow<Dim, true>;\n  return *static_cast<const smooth_flow*>(&lhs) ==\n             *static_cast<const smooth_flow*>(&rhs) and\n         lhs.background_spacetime_ == rhs.background_spacetime_;\n}\n\ntemplate <size_t Dim>\nbool operator!=(const SmoothFlow<Dim>& lhs, const SmoothFlow<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DATA_TYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_CLASS(_, data)                        \\\n  template class SmoothFlow<DIM(data)>;                   \\\n  template bool operator==(const SmoothFlow<DIM(data)>&,  \\\n                           const SmoothFlow<DIM(data)>&); \\\n  template bool operator!=(const SmoothFlow<DIM(data)>&,  \\\n                           const SmoothFlow<DIM(data)>&);\n\n#define INSTANTIATE_FUNCTIONS(_, data)                                         \\\n  template tuples::TaggedTuple<                                                \\\n      hydro::Tags::DivergenceCleaningField<DATA_TYPE(data)>>                   \\\n  SmoothFlow<DIM(data)>::variables(                                            \\\n      const tnsr::I<DATA_TYPE(data), DIM(data)>& x, double /*t*/,              \\\n      tmpl::list<                                                              \\\n          hydro::Tags::DivergenceCleaningField<DATA_TYPE(data)>> /*meta*/)     \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<hydro::Tags::ElectronFraction<DATA_TYPE(data)>> \\\n  SmoothFlow<DIM(data)>::variables(                                            \\\n      const tnsr::I<DATA_TYPE(data), DIM(data)>& x, double /*t*/,              \\\n      tmpl::list<hydro::Tags::ElectronFraction<DATA_TYPE(data)>> /*meta*/)     \\\n      const;                                                                   \\\n  template tuples::TaggedTuple<                                                \\\n      hydro::Tags::MagneticField<DATA_TYPE(data), DIM(data)>>                  \\\n  SmoothFlow<DIM(data)>::variables(                                            \\\n      const tnsr::I<DATA_TYPE(data), DIM(data)>& x, double /*t*/,              \\\n      tmpl::list<                                                              \\\n          hydro::Tags::MagneticField<DATA_TYPE(data), DIM(data)>> /*meta*/)    \\\n      const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_CLASS, (1, 2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATE_FUNCTIONS, (1, 2, 3), (double, DataVector))\n\n#undef INSTANTIATE_CLASS\n#undef INSTANTIATE_FUNCTIONS\n#undef DIM\n#undef DATA_TYPE\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/SmoothFlow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Hydro/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RelativisticEuler::Solutions {\n\n/*!\n * \\brief Smooth wave propagating in Minkowski spacetime.\n *\n * The relativistic Euler equations in Minkowski spacetime accept a\n * solution with constant pressure and uniform spatial velocity provided\n * that the rest mass density satisfies the advection equation\n *\n * \\f{align*}{\n * \\partial_t\\rho + v^i\\partial_i\\rho = 0,\n * \\f}\n *\n * and the specific internal energy is a function of the rest mass density only,\n * \\f$\\epsilon = \\epsilon(\\rho)\\f$. For testing purposes, this class implements\n * this solution for the case where \\f$\\rho\\f$ is a sine wave. The user\n * specifies the mean flow velocity of the fluid, the wavevector of the density\n * profile, and the amplitude \\f$A\\f$ of the density profile. In Cartesian\n * coordinates \\f$(x, y, z)\\f$, and using dimensionless units, the primitive\n * variables at a given time \\f$t\\f$ are then\n *\n * \\f{align*}{\n * \\rho(\\vec{x},t) &= 1 + A \\sin(\\vec{k}\\cdot(\\vec{x} - \\vec{v}t)) \\\\\n * \\vec{v}(\\vec{x},t) &= [v_x, v_y, v_z]^{T},\\\\\n * P(\\vec{x},t) &= P, \\\\\n * \\epsilon(\\vec{x}, t) &= \\frac{P}{(\\gamma - 1)\\rho}\\\\\n * \\f}\n *\n * where we have assumed \\f$\\epsilon\\f$ and \\f$\\rho\\f$ to be related through an\n * equation mathematically equivalent to the equation of state of an ideal gas,\n * where the pressure is held constant.\n */\ntemplate <size_t Dim>\nclass SmoothFlow : public evolution::initial_data::InitialData,\n                   virtual public MarkAsAnalyticSolution,\n                   public AnalyticSolution<Dim>,\n                   public hydro::TemperatureInitialization<SmoothFlow<Dim>>,\n                   private hydro::Solutions::SmoothFlow<Dim, true> {\n  using smooth_flow = hydro::Solutions::SmoothFlow<Dim, true>;\n\n public:\n  using options = typename smooth_flow::options;\n\n  static constexpr Options::String help = {\n      \"Smooth flow in Minkowski spacetime.\"};\n\n  SmoothFlow() = default;\n  SmoothFlow(const SmoothFlow& /*rhs*/) = default;\n  SmoothFlow& operator=(const SmoothFlow& /*rhs*/) = default;\n  SmoothFlow(SmoothFlow&& /*rhs*/) = default;\n  SmoothFlow& operator=(SmoothFlow&& /*rhs*/) = default;\n  ~SmoothFlow() = default;\n\n  SmoothFlow(const std::array<double, Dim>& mean_velocity,\n             const std::array<double, Dim>& wavevector, double pressure,\n             double adiabatic_index, double perturbation_size);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit SmoothFlow(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SmoothFlow);\n  /// \\endcond\n\n  using smooth_flow::equation_of_state;\n  using typename smooth_flow::equation_of_state_type;\n\n  // Overload the variables function from the base class.\n  using smooth_flow::variables;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim>& x, double t,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return hydro::TemperatureInitialization<SmoothFlow<Dim>>::variables(\n        x, t, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, Dim>& x, double /*t*/,\n                 tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>>;\n\n  /// Retrieve a collection of hydro variables at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, Dim>& x,\n                                         const double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// Retrieve the metric variables\n  template <typename DataType, typename Tag>\n  tuples::TaggedTuple<Tag> variables(const tnsr::I<DataType, Dim>& x, double t,\n                                     tmpl::list<Tag> /*meta*/) const {\n    return background_spacetime_.variables(x, t, tmpl::list<Tag>{});\n  }\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, Dim>> variables(\n      const tnsr::I<DataType, Dim>& x, double t,\n      tmpl::list<hydro::Tags::MagneticField<DataType, Dim>> /*meta*/) const;\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::DivergenceCleaningField<DataType>> variables(\n      const tnsr::I<DataType, Dim>& x, double t,\n      tmpl::list<hydro::Tags::DivergenceCleaningField<DataType>> /*meta*/)\n      const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n private:\n  template <size_t SpatialDim>\n  friend bool\n  operator==(  // NOLINT (clang-tidy: readability-redundant-declaration)\n      const SmoothFlow<SpatialDim>& lhs, const SmoothFlow<SpatialDim>& rhs);\n\n  gr::Solutions::Minkowski<Dim> background_spacetime_{};\n};\n\ntemplate <size_t Dim>\nbool operator!=(const SmoothFlow<Dim>& lhs, const SmoothFlow<Dim>& rhs);\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n\nnamespace RelativisticEuler {\n/// Base struct for properties common to all Relativistic Euler analytic\n/// solutions\ntemplate <size_t Dim>\nstruct AnalyticSolution {\n  static constexpr size_t volume_dim = Dim;\n\n  template <typename DataType>\n  using tags = tmpl::push_back<\n      typename gr::AnalyticSolution<Dim>::template tags<DataType>,\n      hydro::Tags::RestMassDensity<DataType>,\n      hydro::Tags::ElectronFraction<DataType>,\n      hydro::Tags::SpecificInternalEnergy<DataType>,\n      hydro::Tags::Temperature<DataType>, hydro::Tags::Pressure<DataType>,\n      hydro::Tags::SpatialVelocity<DataType, Dim>,\n      hydro::Tags::MagneticField<DataType, Dim>,\n      hydro::Tags::DivergenceCleaningField<DataType>,\n      hydro::Tags::LorentzFactor<DataType>,\n      hydro::Tags::SpecificEnthalpy<DataType>>;\n};\n\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing a solution to the relativistic Euler\n * system.\n */\nnamespace Solutions {}\n}  // namespace RelativisticEuler\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Tov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Tov.hpp\"\n\n// Need Boost MultiArray because it is used internally by ODEINT\n#include \"DataStructures/BoostMultiArray.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <boost/numeric/odeint.hpp>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <ostream>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace RelativisticEuler::Solutions {\nstd::ostream& operator<<(std::ostream& os, const TovCoordinates coords) {\n  switch (coords) {\n    case TovCoordinates::Schwarzschild:\n      return os << \"Schwarzschild\";\n    case TovCoordinates::Isotropic:\n      return os << \"Isotropic\";\n    default:\n      ERROR(\"Unknown TovCoordinates\");\n  }\n}\n}  // namespace RelativisticEuler::Solutions\n\ntemplate <>\nRelativisticEuler::Solutions::TovCoordinates\nOptions::create_from_yaml<RelativisticEuler::Solutions::TovCoordinates>::create<\n    void>(const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (\"Schwarzschild\" == type_read) {\n    return RelativisticEuler::Solutions::TovCoordinates::Schwarzschild;\n  } else if (\"Isotropic\" == type_read) {\n    return RelativisticEuler::Solutions::TovCoordinates::Isotropic;\n  }\n  PARSE_ERROR(\n      options.context(),\n      \"Failed to convert '\"\n          << type_read\n          << \"' to RelativisticEuler::Solutions::TovCoordinates. Must be \"\n             \"'Schwarzschild' or 'Isotropic'.\");\n}\n\nnamespace RelativisticEuler::Solutions {\nnamespace {\n\n// In Schwarzschild coords we integrate u=r^2 and v=m/r (2 vars), and in\n// isotropic coords we also integrate w=ln(psi) (3 vars).\ntemplate <TovCoordinates CoordSystem>\nusing TovVars =\n    std::conditional_t<CoordSystem == TovCoordinates::Schwarzschild,\n                       std::array<double, 2>, std::array<double, 3>>;\n\ntemplate <TovCoordinates CoordSystem>\nvoid lindblom_rhs(\n    const gsl::not_null<TovVars<CoordSystem>*> dvars,\n    const TovVars<CoordSystem>& vars, const double log_enthalpy,\n    const EquationsOfState::EquationOfState<true, 1>& equation_of_state) {\n  const double& radius_squared = vars[0];\n  const double& mass_over_radius = vars[1];\n  double& d_radius_squared = (*dvars)[0];\n  double& d_mass_over_radius = (*dvars)[1];\n  const double specific_enthalpy = std::exp(log_enthalpy);\n  const double rest_mass_density =\n      specific_enthalpy == 1.0\n          ? 0.0\n          : get(equation_of_state.rest_mass_density_from_enthalpy(\n                Scalar<double>{specific_enthalpy}));\n  const double pressure = specific_enthalpy == 1.0\n                              ? 0.0\n                              : get(equation_of_state.pressure_from_density(\n                                    Scalar<double>{rest_mass_density}));\n  const double energy_density =\n      specific_enthalpy * rest_mass_density - pressure;\n\n  // At the center of the star: (u,v) = (0,0)\n  if (UNLIKELY((radius_squared < 1.0e-20) and (mass_over_radius < 1.0e-20))) {\n    d_radius_squared = -3.0 / (2.0 * M_PI * (energy_density + 3.0 * pressure));\n    d_mass_over_radius =\n        -2.0 * energy_density / (energy_density + 3.0 * pressure);\n    if constexpr (CoordSystem == TovCoordinates::Isotropic) {\n      double& d_log_conformal_factor = (*dvars)[2];\n      d_log_conformal_factor = -0.25 * d_mass_over_radius;\n    }\n  } else {\n    const double one_minus_two_m_over_r = 1.0 - 2.0 * mass_over_radius;\n    const double denominator =\n        4.0 * M_PI * radius_squared * pressure + mass_over_radius;\n    const double common_factor = one_minus_two_m_over_r / denominator;\n    d_radius_squared = -2.0 * radius_squared * common_factor;\n    d_mass_over_radius =\n        -(4.0 * M_PI * radius_squared * energy_density - mass_over_radius) *\n        common_factor;\n    if constexpr (CoordSystem == TovCoordinates::Isotropic) {\n      double& d_log_conformal_factor = (*dvars)[2];\n      d_log_conformal_factor = sqrt(one_minus_two_m_over_r) /\n                               (1.0 + sqrt(one_minus_two_m_over_r)) *\n                               mass_over_radius / denominator;\n    }\n  }\n}\n\ntemplate <TovCoordinates CoordSystem>\nclass IntegralObserver {\n public:\n  void operator()(const TovVars<CoordSystem>& vars,\n                  const double current_log_enthalpy) {\n    radius.push_back(std::sqrt(vars[0]));\n    mass_over_radius.push_back(vars[1]);\n    if constexpr (CoordSystem == TovCoordinates::Isotropic) {\n      conformal_factor.push_back(exp(vars[2]));\n    }\n    log_enthalpy.push_back(current_log_enthalpy);\n  }\n  std::vector<double> radius;\n  std::vector<double> mass_over_radius;\n  std::vector<double> conformal_factor;\n  std::vector<double> log_enthalpy;\n};\n\n}  // namespace\n\ntemplate <TovCoordinates CoordSystem>\nvoid TovSolution::integrate(\n    const EquationsOfState::EquationOfState<true, 1>& equation_of_state,\n    const double central_mass_density,\n    const double log_enthalpy_at_outer_radius, const double absolute_tolerance,\n    const double relative_tolerance) {\n  using Vars = TovVars<CoordSystem>;\n  Vars vars{};\n  // Initial integration variables at the center of the star\n  vars[0] = 0.;  // u = r^2 = 0\n  vars[1] = 0.;  // v = m / r = 0\n  if constexpr (CoordSystem == TovCoordinates::Isotropic) {\n    vars[2] = 0.;  // w = ln(psi) = 0 (rescaled later)\n  }\n  Vars dvars{};\n  const Scalar<double> central_specific_internal_energy =\n      equation_of_state.specific_internal_energy_from_density(\n          Scalar<double>{central_mass_density});\n  const Scalar<double> central_pressure =\n      equation_of_state.pressure_from_density(\n          Scalar<double>{central_mass_density});\n  const double central_log_enthalpy =\n      std::log(get(hydro::relativistic_specific_enthalpy(\n          Scalar<double>{central_mass_density},\n          central_specific_internal_energy, central_pressure)));\n  lindblom_rhs<CoordSystem>(make_not_null(&dvars), vars, central_log_enthalpy,\n                            equation_of_state);\n  double initial_step =\n      -std::min(std::abs(1.0 / dvars[0]), std::abs(1.0 / dvars[1]));\n  if constexpr (CoordSystem == TovCoordinates::Isotropic) {\n    initial_step = -std::min(std::abs(initial_step), std::abs(1.0 / dvars[2]));\n  }\n  using StateDopri5 = boost::numeric::odeint::runge_kutta_dopri5<Vars>;\n  boost::numeric::odeint::dense_output_runge_kutta<\n      boost::numeric::odeint::controlled_runge_kutta<StateDopri5>>\n      dopri5 = make_dense_output(absolute_tolerance, relative_tolerance,\n                                 StateDopri5{});\n  IntegralObserver<CoordSystem> observer{};\n  boost::numeric::odeint::integrate_adaptive(\n      dopri5,\n      [&equation_of_state](const Vars& local_vars, Vars& local_dvars,\n                           const double local_enthalpy) {\n        return lindblom_rhs<CoordSystem>(&local_dvars, local_vars,\n                                         local_enthalpy, equation_of_state);\n      },\n      vars, central_log_enthalpy, log_enthalpy_at_outer_radius, initial_step,\n      std::ref(observer));\n  outer_radius_ = observer.radius.back();\n  const double total_mass_over_radius = observer.mass_over_radius.back();\n  total_mass_ = total_mass_over_radius * outer_radius_;\n  injection_energy_ = sqrt(1. - 2. * total_mass_ / outer_radius_);\n\n  if constexpr (CoordSystem == TovCoordinates::Isotropic) {\n    // Transform outer radius to isotropic\n    const double outer_areal_radius = outer_radius_;\n    outer_radius_ = 0.5 * (outer_areal_radius - total_mass_ +\n                           sqrt(square(outer_areal_radius) -\n                                2. * total_mass_ * outer_areal_radius));\n\n    // Match conformal factor to exterior solution\n    const double outer_conformal_factor =\n        1.0 + 0.5 * total_mass_ / outer_radius_;\n    const double matching_constant =\n        outer_conformal_factor / observer.conformal_factor.back();\n    const size_t num_points = observer.radius.size();\n    for (size_t i = 0; i < num_points; ++i) {\n      observer.conformal_factor[i] *= matching_constant;\n      // Transform observed radius to isotropic, so we use the isotropic radius\n      // for all interpolations below\n      observer.radius[i] /= square(observer.conformal_factor[i]);\n      // The interpolation is not safe otherwise\n    }\n    observer.radius.back() = outer_radius_;\n    conformal_factor_interpolant_ =\n        intrp::CubicSpline(observer.radius, observer.conformal_factor);\n  }\n\n  mass_over_radius_interpolant_ =\n      intrp::CubicSpline(observer.radius, observer.mass_over_radius);\n  // log_enthalpy(radius) is almost linear so an interpolant of order 3\n  // maximizes precision\n  log_enthalpy_interpolant_ =\n      intrp::CubicSpline(observer.radius, observer.log_enthalpy);\n}\n\nTovSolution::TovSolution(\n    const EquationsOfState::EquationOfState<true, 1>& equation_of_state,\n    const double central_mass_density, const TovCoordinates coordinate_system,\n    const double log_enthalpy_at_outer_radius, const double absolute_tolerance,\n    const double relative_tolerance)\n    : coordinate_system_(coordinate_system) {\n  if (coordinate_system_ == TovCoordinates::Schwarzschild) {\n    integrate<TovCoordinates::Schwarzschild>(\n        equation_of_state, central_mass_density, log_enthalpy_at_outer_radius,\n        absolute_tolerance, relative_tolerance);\n  } else {\n    integrate<TovCoordinates::Isotropic>(\n        equation_of_state, central_mass_density, log_enthalpy_at_outer_radius,\n        absolute_tolerance, relative_tolerance);\n  }\n}\n\ntemplate <typename DataType>\nDataType TovSolution::mass_over_radius(const DataType& r) const {\n  // Possible optimization: Support DataVector in intrp::BarycentricRational\n  auto result = make_with_value<DataType>(r, 0.);\n  for (size_t i = 0; i < get_size(r); ++i) {\n    ASSERT(\n        get_element(r, i) >= 0.0 and get_element(r, i) <= outer_radius_,\n        \"Invalid radius: \" << r << \" not in [0.0, \" << outer_radius_ << \"]\\n\");\n    get_element(result, i) = mass_over_radius_interpolant_(get_element(r, i));\n  }\n  return result;\n}\n\ntemplate <typename DataType>\nDataType TovSolution::log_specific_enthalpy(const DataType& r) const {\n  // Possible optimization: Support DataVector in intrp::BarycentricRational\n  auto result = make_with_value<DataType>(r, 0.);\n  for (size_t i = 0; i < get_size(r); ++i) {\n    ASSERT(\n        get_element(r, i) >= 0.0 and get_element(r, i) <= outer_radius_,\n        \"Invalid radius: \" << r << \" not in [0.0, \" << outer_radius_ << \"]\\n\");\n    get_element(result, i) = log_enthalpy_interpolant_(get_element(r, i));\n  }\n  return result;\n}\n\ntemplate <typename DataType>\nDataType TovSolution::conformal_factor(const DataType& r) const {\n  ASSERT(coordinate_system_ == TovCoordinates::Isotropic,\n         \"The conformal factor is computed only for isotropic coordinates.\");\n  // Possible optimization: Support DataVector in intrp::BarycentricRational\n  auto result = make_with_value<DataType>(r, 0.);\n  for (size_t i = 0; i < get_size(r); ++i) {\n    ASSERT(\n        get_element(r, i) >= 0.0 and get_element(r, i) <= outer_radius_,\n        \"Invalid radius: \" << r << \" not in [0.0, \" << outer_radius_ << \"]\\n\");\n    get_element(result, i) = conformal_factor_interpolant_(get_element(r, i));\n  }\n  return result;\n}\n\nvoid TovSolution::pup(PUP::er& p) {  // NOLINT\n  p | coordinate_system_;\n  p | outer_radius_;\n  p | total_mass_;\n  p | injection_energy_;\n  p | mass_over_radius_interpolant_;\n  p | log_enthalpy_interpolant_;\n  if (coordinate_system_ == TovCoordinates::Isotropic) {\n    p | conformal_factor_interpolant_;\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template DTYPE(data) TovSolution::mass_over_radius(const DTYPE(data) & r) \\\n      const;                                                                \\\n  template DTYPE(data)                                                      \\\n      TovSolution::log_specific_enthalpy(const DTYPE(data) & r) const;      \\\n  template DTYPE(data) TovSolution::conformal_factor(const DTYPE(data) & r) \\\n      const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Tov.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <ostream>\n\n#include \"NumericalAlgorithms/Interpolation/CubicSpline.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n\n/// \\cond\nnamespace Options {\nstruct Option;\ntemplate <typename T>\nstruct create_from_yaml;\n}  // namespace Options\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace RelativisticEuler::Solutions {\n\n/// Radial coordinate of a TOV solution.\nenum class TovCoordinates {\n  /// Areal radial coordinate, in which the exterior TOV solution coincides with\n  /// the Schwarzschild metric in standard Schwarzschild coordinates.\n  Schwarzschild,\n  /// Isotropic radial coordinate, in which the spatial metric is conformally\n  /// flat.\n  Isotropic\n};\n\nstd::ostream& operator<<(std::ostream& os, TovCoordinates coords);\n\n}  // namespace RelativisticEuler::Solutions\n\ntemplate <>\nstruct Options::create_from_yaml<RelativisticEuler::Solutions::TovCoordinates> {\n  template <typename Metavariables>\n  static RelativisticEuler::Solutions::TovCoordinates create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nRelativisticEuler::Solutions::TovCoordinates\nOptions::create_from_yaml<RelativisticEuler::Solutions::TovCoordinates>::create<\n    void>(const Options::Option& options);\n\nnamespace RelativisticEuler::Solutions {\n\n/*!\n * \\brief TOV solver based on Lindblom's method\n *\n * Uses Lindblom's method of integrating the TOV equations from\n * \\cite Lindblom1998dp .\n *\n * Instead of integrating the interior mass \\f$m(r)\\f$ and pressure \\f$p(r)\\f$,\n * Lindblom introduces the variables \\f$u=r^2\\f$ and \\f$v=m/r\\f$. Then, the TOV\n * equations are integrated with the log of the specific enthalpy as the\n * independent variable, \\f$\\ln(h)\\f$, from the center of the star where\n * \\f$h(r=0) = h_c\\f$ to its surface \\f$h(r=R) = 1\\f$. The ODEs being solved are\n * Eq. (A2) and (A3) in \\cite Lindblom1998dp :\n *\n * \\f{align}\n * \\frac{\\mathrm{d}u}{\\mathrm{d}\\ln{h}} &= \\frac{-2u (1 - 2v)}{4\\pi u p + v} \\\\\n * \\frac{\\mathrm{d}v}{\\mathrm{d}\\ln{h}} &=\n *   -(1 - 2v) \\frac{4\\pi u \\rho - v}{4\\pi u p + v}\n * \\f}\n *\n * Note that Lindblom's paper labels the independent variable as \\f$h\\f$.\n * However the \\f$h\\f$ in Lindblom's paper is **not** the specific enthalpy but\n * its logarithm, \\f$\\ln(h)\\f$.\n *\n * The ODEs are solved numerically when this class is constructed, and the\n * quantities \\f$m(r)/r\\f$ and \\f$\\ln(h)\\f$ are interpolated and exposed as\n * member functions. With these quantities the metric can be constructed as:\n *\n * \\f{equation}\n * \\mathrm{d}s^2 = -\\alpha^2 \\mathrm{d}t^2 + (1 - 2m/r)^{-1} \\mathrm{d}r^2\n *   + r^2 \\mathrm{d}\\Omega^2\n * \\f}\n *\n * where the lapse is\n *\n * \\f{equation}\n * \\alpha(r < R) = \\mathcal{E} / h = \\alpha(r=R) / h\n * \\text{,}\n * \\f}\n *\n * with the conserved `injection_energy()` \\f$\\mathcal{E}\\f$, such that the\n * lapse matches the exterior Schwarzschild solution:\n *\n * \\f{equation}\n * \\alpha(r \\geq R) = \\sqrt{1 - \\frac{2M}{r}}\n * \\f}\n *\n * \\par Isotropic radial coordinate\n * This class also supports transforming to an isotropic radial coordinate. When\n * you pass `RelativisticEuler::Solutions::TovCoordinates::Isotropic` to the\n * constructor, an additional ODE is integrated alongside the TOV equations to\n * determine the conformal factor\n *\n * \\f{equation}\n * \\psi^2 = \\frac{r}{\\bar{r}}\n * \\f}\n *\n * where \\f$r\\f$ is the areal (Schwarzschild) radius and \\f$\\bar{r}\\f$ is the\n * isotropic radius. The additional ODE is:\n *\n * \\f{equation}\n * \\frac{\\mathrm{d}\\ln(\\psi)}{\\mathrm{d}\\ln{h}} =\n *   \\frac{\\sqrt{1 - 2v}}{1 + \\sqrt{1 - 2v}} \\frac{v}{4\\pi u p + v}\n * \\f}\n *\n * In isotropic coordinates, the spatial metric is conformally flat:\n *\n * \\f{equation}\n * \\mathrm{d}s^2 = -\\alpha^2 \\mathrm{d}t^2 + \\psi^4 (\\mathrm{d}\\bar{r}^2 +\n *   \\bar{r}^2 \\mathrm{d}\\Omega^2)\n * \\f}\n *\n * When isotropic coordinates are selected, radii returned by member functions\n * or taken as arguments are isotropic. An exception is `mass_over_radius()`,\n * which always returns the quantity \\f$m / r\\f$ because that is the quantity\n * stored internally and hence most numerically precise. See\n * `mass_over_radius()` for details.\n */\nclass TovSolution {\n public:\n  TovSolution(\n      const EquationsOfState::EquationOfState<true, 1>& equation_of_state,\n      double central_mass_density,\n      const TovCoordinates coordinate_system = TovCoordinates::Schwarzschild,\n      double log_enthalpy_at_outer_radius = 0.0,\n      double absolute_tolerance = 1.e-18, double relative_tolerance = 1.0e-14);\n\n  TovSolution() = default;\n  TovSolution(const TovSolution& /*rhs*/) = default;\n  TovSolution& operator=(const TovSolution& /*rhs*/) = default;\n  TovSolution(TovSolution&& /*rhs*/) = default;\n  TovSolution& operator=(TovSolution&& /*rhs*/) = default;\n  ~TovSolution() = default;\n\n  /// The type of radial coordinate.\n  ///\n  /// \\see RelativisticEuler::Solutions::TovCoordinates\n  TovCoordinates coordinate_system() const { return coordinate_system_; }\n\n  /// \\brief The outer radius of the solution.\n  ///\n  /// This is the outer radius in the specified `coordinate_system()`, i.e.,\n  /// areal or isotropic.\n  ///\n  /// \\note This is the radius at which `log_specific_enthalpy` is equal\n  /// to the value of `log_enthalpy_at_outer_radius` that was given when\n  /// constructing this TovSolution\n  double outer_radius() const { return outer_radius_; }\n\n  /// The total mass \\f$m(R)\\f$, where \\f$R\\f$ is the outer radius.\n  double total_mass() const { return total_mass_; }\n\n  /*!\n   * \\brief The injection energy \\f$\\mathcal{E}=\\alpha(r=R)=\\sqrt{1-2M/R}\\f$.\n   *\n   * The injection energy of the TOV solution is\n   *\n   * \\f{equation}\n   * \\mathcal{E} = -h k^a u_a = h \\alpha\n   * \\text{,}\n   * \\f}\n   *\n   * where \\f$\\boldsymbol{k} = \\partial_t\\f$ is a Killing vector of the static\n   * solution, \\f$h\\f$ is the specific enthalpy, \\f$u_a\\f$ is the fluid\n   * four-velocity, and \\f$\\alpha\\f$ is the lapse (see, e.g., Eqs. (2.19) and\n   * (4.2) in \\cite Moldenhauer2014yaa). Since the TOV solution is static, the\n   * injection energy is conserved not only along stream lines but throughout\n   * the star,\n   *\n   * \\f{equation}\n   * \\nabla_a \\mathcal{E} = 0\n   * \\text{.}\n   * \\f}\n   *\n   * Therefore,\n   *\n   * \\f{equation}\n   * \\mathcal{E} = \\alpha(r=R) = \\sqrt{1 - 2M/R}\n   * \\f}\n   *\n   * by evaluating the injection energy at the outer (areal) radius \\f$R\\f$,\n   * where \\f$h=1\\f$ and where we match the lapse to the outer Schwarzschild\n   * solution. The conservation also implies\n   *\n   * \\f{equation}\n   * \\alpha = \\mathcal{E} / h\n   * \\f}\n   *\n   * throughout the star.\n   */\n  double injection_energy() const { return injection_energy_; }\n\n  /// \\brief The mass inside the given radius over the areal radius,\n  /// \\f$\\frac{m(r)}{r}\\f$\n  ///\n  /// The argument to this function is the radius in the `coordinate_system()`,\n  /// i.e., areal (Schwarzschild) or isotropic radius. The denominator \\f$r\\f$\n  /// in the return value is always the areal (Schwarzschild) radius. You can\n  /// use the conformal factor \\f$\\psi=\\sqrt{r / \\bar{r}}\\f$ returned by the\n  /// `conformal_factor()` function to obtain the mass over the isotropic\n  /// radius, or the mass alone. The reason for this choice is that we represent\n  /// the solution internally as the mass over the areal radius, so this is the\n  /// most numerically precise quantity from which other quantities can be\n  /// derived.\n  ///\n  /// \\note `r` should be non-negative and not greater than `outer_radius()`.\n  template <typename DataType>\n  DataType mass_over_radius(const DataType& r) const;\n\n  /// \\brief The log of the specific enthalpy at the given radius\n  ///\n  /// \\note `r` should be non-negative and not greater than `outer_radius()`\n  template <typename DataType>\n  DataType log_specific_enthalpy(const DataType& r) const;\n\n  /// \\brief The conformal factor \\f$\\psi=\\sqrt{r / \\bar{r}}\\f$.\n  ///\n  /// The conformal factor is computed only when the `coordinate_system()` is\n  /// `RelativisticEuler::Solution::TovCoordinates::Isotropic`. Otherwise, it is\n  /// an error to call this function.\n  ///\n  /// \\note `r` should be non-negative and not greater than `outer_radius()`\n  template <typename DataType>\n  DataType conformal_factor(const DataType& r) const;\n\n  const intrp::CubicSpline& mass_over_radius_interpolant() const {\n    return mass_over_radius_interpolant_;\n  }\n  const intrp::CubicSpline& log_specific_enthalpy_interpolant() const {\n    return log_enthalpy_interpolant_;\n  }\n  const intrp::CubicSpline& conformal_factor_interpolant() const {\n    return conformal_factor_interpolant_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <TovCoordinates CoordSystem>\n  void integrate(\n      const EquationsOfState::EquationOfState<true, 1>& equation_of_state,\n      const double central_mass_density,\n      const double log_enthalpy_at_outer_radius,\n      const double absolute_tolerance, const double relative_tolerance);\n\n  TovCoordinates coordinate_system_{};\n  double outer_radius_{std::numeric_limits<double>::signaling_NaN()};\n  double total_mass_{std::numeric_limits<double>::signaling_NaN()};\n  double injection_energy_{std::numeric_limits<double>::signaling_NaN()};\n  intrp::CubicSpline mass_over_radius_interpolant_;\n  intrp::CubicSpline log_enthalpy_interpolant_;\n  intrp::CubicSpline conformal_factor_interpolant_;\n};\n\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Tov.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace RelativisticEuler::Solutions {\n\nTovStar::TovStar(CkMigrateMessage* msg) : InitialData(msg) {}\n\nTovStar::TovStar(\n    const double central_rest_mass_density,\n    std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>\n        equation_of_state,\n    const RelativisticEuler::Solutions::TovCoordinates coordinate_system)\n    : central_rest_mass_density_(central_rest_mass_density),\n      equation_of_state_(std::move(equation_of_state)),\n      coordinate_system_(coordinate_system),\n      radial_solution_(*equation_of_state_, central_rest_mass_density_,\n                       coordinate_system_) {}\n\nTovStar::TovStar(const TovStar& rhs)\n    : evolution::initial_data::InitialData(rhs),\n      central_rest_mass_density_(rhs.central_rest_mass_density_),\n      equation_of_state_(rhs.equation_of_state_->get_clone()),\n      coordinate_system_(rhs.coordinate_system_),\n      radial_solution_(*equation_of_state_, central_rest_mass_density_,\n                       coordinate_system_) {}\n\nTovStar& TovStar::operator=(const TovStar& rhs) {\n  central_rest_mass_density_ = rhs.central_rest_mass_density_;\n  equation_of_state_ = rhs.equation_of_state_->get_clone();\n  coordinate_system_ = rhs.coordinate_system_;\n  radial_solution_ = RelativisticEuler::Solutions::TovSolution(\n      *equation_of_state_, central_rest_mass_density_, coordinate_system_);\n  return *this;\n}\n\nstd::unique_ptr<evolution::initial_data::InitialData> TovStar::get_clone()\n    const {\n  return std::make_unique<TovStar>(*this);\n}\n\nvoid TovStar::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | central_rest_mass_density_;\n  p | equation_of_state_;\n  p | coordinate_system_;\n  p | radial_solution_;\n}\n\nnamespace tov_detail {\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    [[maybe_unused]] const gsl::not_null<Scalar<DataType>*> mass_over_radius,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::MassOverRadius<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    ERROR(\n        \"The mass-over-radius quantity should not be needed in the exterior of \"\n        \"the star.\");\n  } else {\n    get(*mass_over_radius) = radial_solution.mass_over_radius(radius);\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    [[maybe_unused]] const gsl::not_null<Scalar<DataType>*>\n        log_specific_enthalpy,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::LogSpecificEnthalpy<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    ERROR(\n        \"The log-specific-enthalpy quantity should not be needed in the \"\n        \"exterior of the star (just use the specific enthalpy).\");\n  } else {\n    get(*log_specific_enthalpy) = radial_solution.log_specific_enthalpy(radius);\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> specific_enthalpy,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    hydro::Tags::SpecificEnthalpy<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    get(*specific_enthalpy) = 1.;\n  } else {\n    const auto& log_specific_enthalpy =\n        get(cache->get_var(*this, Tags::LogSpecificEnthalpy<DataType>{}));\n    get(*specific_enthalpy) = exp(log_specific_enthalpy);\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::ConformalFactor<DataType> /*meta*/) const {\n  if (radial_solution.coordinate_system() ==\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n    if constexpr (Region == StarRegion::Exterior) {\n      get(*conformal_factor) = 1. + 0.5 * radial_solution.total_mass() / radius;\n    } else {\n      get(*conformal_factor) = radial_solution.conformal_factor(radius);\n    }\n  } else {\n    ERROR(\n        \"The conformal factor should not be needed in Schwarzschild \"\n        \"coordinates.\");\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    [[maybe_unused]] const gsl::not_null<Scalar<DataType>*> dr_conformal_factor,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    Tags::DrConformalFactor<DataType> /*meta*/) const {\n  if (radial_solution.coordinate_system() ==\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n    if constexpr (Region == StarRegion::Exterior) {\n      get(*dr_conformal_factor) =\n          -0.5 * radial_solution.total_mass() / square(radius);\n    } else if constexpr (Region == StarRegion::Center) {\n      ERROR(\"The 'DrConformalFactor' should not be needed at the star center.\");\n    } else {\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Tags::ConformalFactor<DataType>{}));\n      const auto& mass_over_areal_radius =\n          get(cache->get_var(*this, Tags::MassOverRadius<DataType>{}));\n      get(*dr_conformal_factor) = 0.5 * conformal_factor / radius *\n                                  (sqrt(1. - 2. * mass_over_areal_radius) - 1.);\n    }\n  } else {\n    ERROR(\n        \"The conformal factor should not be needed in Schwarzschild \"\n        \"coordinates.\");\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> areal_radius,\n    const gsl::not_null<Cache*> cache,\n    Tags::ArealRadius<DataType> /*meta*/) const {\n  if (radial_solution.coordinate_system() ==\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n    const auto& conformal_factor =\n        get(cache->get_var(*this, Tags::ConformalFactor<DataType>{}));\n    get(*areal_radius) = square(conformal_factor) * radius;\n  } else {\n    ERROR(\n        \"No need to compute the areal radius in Schwarzschild coordinates, \"\n        \"just use 'radius'.\");\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    [[maybe_unused]] const gsl::not_null<Scalar<DataType>*> dr_areal_radius,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    Tags::DrArealRadius<DataType> /*meta*/) const {\n  if (radial_solution.coordinate_system() ==\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n    if constexpr (Region == StarRegion::Exterior) {\n      const auto& areal_radius =\n          get(cache->get_var(*this, Tags::ArealRadius<DataType>{}));\n      get(*dr_areal_radius) =\n          areal_radius / radius *\n          sqrt(1. - 2. * radial_solution.total_mass() / areal_radius);\n    } else if constexpr (Region == StarRegion::Center) {\n      ERROR(\"The 'DrArealRadius' should not be needed at the star center.\");\n    } else {\n      const auto& areal_radius =\n          get(cache->get_var(*this, Tags::ArealRadius<DataType>{}));\n      const auto& mass_over_radius =\n          get(cache->get_var(*this, Tags::MassOverRadius<DataType>{}));\n      get(*dr_areal_radius) =\n          areal_radius / radius * sqrt(1. - 2. * mass_over_radius);\n    }\n  } else {\n    ERROR(\n        \"No need to compute the areal radius in Schwarzschild coordinates, \"\n        \"just use 'radius'.\");\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> rest_mass_density,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    hydro::Tags::RestMassDensity<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    get(*rest_mass_density) = 0.;\n  } else {\n    const auto& specific_enthalpy =\n        cache->get_var(*this, hydro::Tags::SpecificEnthalpy<DataType>{});\n    if (get(specific_enthalpy) == 1.) {\n      get(*rest_mass_density) = 0.;\n    } else {\n      *rest_mass_density =\n          eos.rest_mass_density_from_enthalpy(specific_enthalpy);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> electron_fraction,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    hydro::Tags::ElectronFraction<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    get(*electron_fraction) = 0.45;\n  } else {\n    get(*electron_fraction) = 0.1;\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> pressure,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    hydro::Tags::Pressure<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    get(*pressure) = 0.;\n  } else {\n    const auto& rest_mass_density =\n        cache->get_var(*this, hydro::Tags::RestMassDensity<DataType>{});\n    if (get(rest_mass_density) == 0.) {\n      get(*pressure) = 0.;\n    } else {\n      *pressure = eos.pressure_from_density(rest_mass_density);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> specific_internal_energy,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    hydro::Tags::SpecificInternalEnergy<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    get(*specific_internal_energy) = 0.;\n  } else {\n    const auto& rest_mass_density =\n        cache->get_var(*this, hydro::Tags::RestMassDensity<DataType>{});\n    if (get(rest_mass_density) == 0.) {\n      get(*specific_internal_energy) = 0.;\n    } else {\n      *specific_internal_energy =\n          eos.specific_internal_energy_from_density(rest_mass_density);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> temperature,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    hydro::Tags::Temperature<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    get(*temperature) = 0.;\n  } else {\n    const auto& rest_mass_density =\n        cache->get_var(*this, hydro::Tags::RestMassDensity<DataType>{});\n    if (get(rest_mass_density) == 0.) {\n      get(*temperature) = 0.;\n    } else {\n      *temperature = eos.temperature_from_density(rest_mass_density);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> metric_time_potential,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    Tags::MetricTimePotential<DataType> /*meta*/) const {\n  // Compute phi in Eq. (1.73) in BaumgarteShapiro, which is the logarithm of\n  // the lapse:\n  //   lapse = e^phi\n  if constexpr (Region == StarRegion::Exterior) {\n    const auto& areal_radius =\n        radial_solution.coordinate_system() ==\n                RelativisticEuler::Solutions::TovCoordinates::Isotropic\n            ? get(cache->get_var(*this, Tags::ArealRadius<DataType>{}))\n            : radius;\n    get(*metric_time_potential) =\n        0.5 * log(1. - 2. * radial_solution.total_mass() / areal_radius);\n  } else {\n    const auto& log_specific_enthalpy =\n        get(cache->get_var(*this, Tags::LogSpecificEnthalpy<DataType>{}));\n    const double log_lapse_at_outer_radius =\n        log(radial_solution.injection_energy());\n    get(*metric_time_potential) =\n        log_lapse_at_outer_radius - log_specific_enthalpy;\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> dr_metric_time_potential,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    Tags::DrMetricTimePotential<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    const auto& areal_radius =\n        radial_solution.coordinate_system() ==\n                RelativisticEuler::Solutions::TovCoordinates::Isotropic\n            ? get(cache->get_var(*this, Tags::ArealRadius<DataType>{}))\n            : radius;\n    get(*dr_metric_time_potential) =\n        radial_solution.total_mass() / square(areal_radius) /\n        (1. - 2. * radial_solution.total_mass() / areal_radius);\n    if (radial_solution.coordinate_system() ==\n        RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n      const auto& dr_areal_radius =\n          get(cache->get_var(*this, Tags::DrArealRadius<DataType>{}));\n      get(*dr_metric_time_potential) *= dr_areal_radius;\n    }\n  } else if constexpr (Region == StarRegion::Center) {\n    get(*dr_metric_time_potential) = 0.;\n  } else {\n    // Compute dphi/dr from the TOV equations, e.g. Eq. (1.79) in\n    // BaumgarteShapiro.\n    const auto& mass_over_radius =\n        get(cache->get_var(*this, Tags::MassOverRadius<DataType>{}));\n    const auto& pressure =\n        get(cache->get_var(*this, hydro::Tags::Pressure<DataType>{}));\n    const auto& areal_radius =\n        radial_solution.coordinate_system() ==\n                RelativisticEuler::Solutions::TovCoordinates::Isotropic\n            ? get(cache->get_var(*this, Tags::ArealRadius<DataType>{}))\n            : radius;\n    get(*dr_metric_time_potential) = (mass_over_radius / areal_radius +\n                                      4. * M_PI * pressure * areal_radius) /\n                                     (1. - 2. * mass_over_radius);\n    if (radial_solution.coordinate_system() ==\n        RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n      const auto& dr_areal_radius =\n          get(cache->get_var(*this, Tags::DrArealRadius<DataType>{}));\n      get(*dr_metric_time_potential) *= dr_areal_radius;\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> metric_radial_potential,\n    const gsl::not_null<Cache*> cache,\n    Tags::MetricRadialPotential<DataType> /*meta*/) const {\n  // Compute lambda in Eq. (1.73) in BaumgarteShapiro\n  if (radial_solution.coordinate_system() ==\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n    const auto& conformal_factor =\n        get(cache->get_var(*this, Tags::ConformalFactor<DataType>{}));\n    get(*metric_radial_potential) = 2. * log(conformal_factor);\n  } else {\n    // Schwarzschild coords\n    if constexpr (Region == StarRegion::Exterior) {\n      const auto& metric_time_potential =\n          get(cache->get_var(*this, Tags::MetricTimePotential<DataType>{}));\n      get(*metric_radial_potential) = -metric_time_potential;\n    } else {\n      const auto& mass_over_radius =\n          get(cache->get_var(*this, Tags::MassOverRadius<DataType>{}));\n      // Eq. (1.76) in BaumgarteShapiro (equation in book is missing a sqrt)\n      get(*metric_radial_potential) = -0.5 * log(1. - 2. * mass_over_radius);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> dr_metric_radial_potential,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    Tags::DrMetricRadialPotential<DataType> /*meta*/) const {\n  if (radial_solution.coordinate_system() ==\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n    const auto& conformal_factor =\n        get(cache->get_var(*this, Tags::ConformalFactor<DataType>{}));\n    const auto& dr_conformal_factor =\n        get(cache->get_var(*this, Tags::DrConformalFactor<DataType>{}));\n    get(*dr_metric_radial_potential) =\n        2. / conformal_factor * dr_conformal_factor;\n  } else {\n    if constexpr (Region == StarRegion::Exterior) {\n      const auto& dr_metric_time_potential =\n          get(cache->get_var(*this, Tags::DrMetricTimePotential<DataType>{}));\n      get(*dr_metric_radial_potential) = -dr_metric_time_potential;\n    } else if constexpr (Region == StarRegion::Center) {\n      get(*dr_metric_radial_potential) = 0.;\n    } else {\n      const auto& mass_over_radius =\n          get(cache->get_var(*this, Tags::MassOverRadius<DataType>{}));\n      const auto& specific_enthalpy =\n          get(cache->get_var(*this, hydro::Tags::SpecificEnthalpy<DataType>{}));\n      const auto& rest_mass_density =\n          get(cache->get_var(*this, hydro::Tags::RestMassDensity<DataType>{}));\n      const auto& pressure =\n          get(cache->get_var(*this, hydro::Tags::Pressure<DataType>{}));\n      // Compute dm/dr from the TOV equations, e.g. Eq. (1.77) in\n      // BaumgarteShapiro.\n      get(*dr_metric_radial_potential) =\n          (4. * M_PI * radius *\n               (specific_enthalpy * rest_mass_density - pressure) -\n           mass_over_radius / radius) /\n          (1. - 2. * mass_over_radius);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> metric_angular_potential,\n    const gsl::not_null<Cache*> cache,\n    Tags::MetricAngularPotential<DataType> /*meta*/) const {\n  if (radial_solution.coordinate_system() ==\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n    const auto& metric_radial_potential =\n        cache->get_var(*this, Tags::MetricRadialPotential<DataType>{});\n    *metric_angular_potential = metric_radial_potential;\n  } else {\n    get(*metric_angular_potential) = 0.;\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> dr_metric_angular_potential,\n    const gsl::not_null<Cache*> cache,\n    Tags::DrMetricAngularPotential<DataType> /*meta*/) const {\n  if (radial_solution.coordinate_system() ==\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic) {\n    const auto& dr_metric_radial_potential =\n        cache->get_var(*this, Tags::DrMetricRadialPotential<DataType>{});\n    *dr_metric_angular_potential = dr_metric_radial_potential;\n  } else {\n    get(*dr_metric_angular_potential) = 0.;\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> dr_pressure,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    Tags::DrPressure<DataType> /*meta*/) const {\n  if constexpr (Region == StarRegion::Exterior) {\n    get(*dr_pressure) = 0.;\n  } else {\n    // Compute dP/dr from the TOV equations, e.g. Eq. (1.78) in\n    // BaumgarteShapiro.\n    const auto& dr_metric_time_potential =\n        get(cache->get_var(*this, Tags::DrMetricTimePotential<DataType>{}));\n    const auto& rest_mass_density =\n        get(cache->get_var(*this, hydro::Tags::RestMassDensity<DataType>{}));\n    const auto& specific_enthalpy =\n        get(cache->get_var(*this, hydro::Tags::SpecificEnthalpy<DataType>{}));\n    get(*dr_pressure) =\n        -dr_metric_time_potential * rest_mass_density * specific_enthalpy;\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*> deriv_pressure,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<hydro::Tags::Pressure<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  // This function is not required for solving TOV equations themselves, but\n  // needed for computing magnetic fields in the MagnetizedTov initial data.\n\n  if constexpr (Region == StarRegion::Center or\n                Region == StarRegion::Exterior) {\n    get<0>(*deriv_pressure) = 0.0;\n    get<1>(*deriv_pressure) = 0.0;\n    get<2>(*deriv_pressure) = 0.0;\n  } else {\n    // cache evaluation only gets triggered when it's needed\n    const auto& dr_pressure =\n        get(cache->get_var(*this, Tags::DrPressure<DataType>{}));\n\n    // dp/dx^i = (dr/dx^i)(dp/dr) = (x^i/r)(dp/dr)\n    for (size_t d = 0; d < 3; ++d) {\n      (*deriv_pressure).get(d) = dr_pressure * coords.get(d) / radius;\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lorentz_factor,\n    const gsl::not_null<Cache*> /*cache*/,\n    hydro::Tags::LorentzFactor<DataType> /*meta*/) const {\n  get(*lorentz_factor) = 1.;\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> spatial_velocity,\n    const gsl::not_null<Cache*> /*cache*/,\n    hydro::Tags::SpatialVelocity<DataType, 3> /*meta*/) const {\n  get<0>(*spatial_velocity) = 0.;\n  get<1>(*spatial_velocity) = 0.;\n  get<2>(*spatial_velocity) = 0.;\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n    const gsl::not_null<Cache*> /*cache*/,\n    hydro::Tags::MagneticField<DataType, 3> /*meta*/) const {\n  get<0>(*magnetic_field) = 0.;\n  get<1>(*magnetic_field) = 0.;\n  get<2>(*magnetic_field) = 0.;\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> div_cleaning_field,\n    const gsl::not_null<Cache*> /*cache*/,\n    hydro::Tags::DivergenceCleaningField<DataType> /*meta*/) const {\n  get(*div_cleaning_field) = 0.;\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::Lapse<DataType> /*meta*/) const {\n  const auto& metric_time_potential =\n      get(cache->get_var(*this, Tags::MetricTimePotential<DataType>{}));\n  get(*lapse) = exp(metric_time_potential);\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> dt_lapse,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/) const {\n  get(*dt_lapse) = 0.;\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*> deriv_lapse,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  if constexpr (Region == StarRegion::Center) {\n    get<0>(*deriv_lapse) = 0.;\n    get<1>(*deriv_lapse) = 0.;\n    get<2>(*deriv_lapse) = 0.;\n  } else {\n    const auto& lapse = get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n    const auto& dr_metric_time_potential =\n        get(cache->get_var(*this, Tags::DrMetricTimePotential<DataType>{}));\n    // lapse = exp(phi), so dlapse/dr = lapse * dphi/dr * x/r\n    get<0>(*deriv_lapse) = lapse * dr_metric_time_potential / radius;\n    get<1>(*deriv_lapse) = get<0>(*deriv_lapse);\n    get<2>(*deriv_lapse) = get<0>(*deriv_lapse);\n    for (size_t i = 0; i < 3; ++i) {\n      deriv_lapse->get(i) *= coords.get(i);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> shift,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::Shift<DataType, 3> /*meta*/) const {\n  get<0>(*shift) = 0.;\n  get<1>(*shift) = 0.;\n  get<2>(*shift) = 0.;\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> dt_shift,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::dt<gr::Tags::Shift<DataType, 3>> /*meta*/) const {\n  get<0>(*dt_shift) = 0.;\n  get<1>(*dt_shift) = 0.;\n  get<2>(*dt_shift) = 0.;\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<gr::Tags::Shift<DataType, 3>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  *deriv_shift = make_with_value<tnsr::iJ<DataType, 3>>(coords, 0.);\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::SpatialMetric<DataType, 3> /*meta*/) const {\n  if constexpr (Region == StarRegion::Center) {\n    const auto& metric_angular_potential =\n        get(cache->get_var(*this, Tags::MetricAngularPotential<DataType>{}));\n    get<0, 0>(*spatial_metric) = exp(2. * metric_angular_potential);\n    get<1, 1>(*spatial_metric) = get<0, 0>(*spatial_metric);\n    get<2, 2>(*spatial_metric) = get<0, 0>(*spatial_metric);\n    get<0, 1>(*spatial_metric) = 0.;\n    get<0, 2>(*spatial_metric) = 0.;\n    get<1, 2>(*spatial_metric) = 0.;\n  } else {\n    const auto& metric_radial_potential =\n        get(cache->get_var(*this, Tags::MetricRadialPotential<DataType>{}));\n    const auto& metric_angular_potential =\n        get(cache->get_var(*this, Tags::MetricAngularPotential<DataType>{}));\n    *spatial_metric = tnsr::ii<DataType, 3, Frame::Inertial>{\n        (exp(2. * metric_radial_potential) -\n         exp(2. * metric_angular_potential)) /\n        square(radius)};\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        spatial_metric->get(i, j) *= coords.get(i) * coords.get(j);\n      }\n      spatial_metric->get(i, i) += exp(2. * metric_angular_potential);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> dt_spatial_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>> /*meta*/) const {\n  *dt_spatial_metric = make_with_value<tnsr::ii<DataType, 3>>(coords, 0.);\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3>*> deriv_spatial_metric,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  if constexpr (Region == StarRegion::Center) {\n    *deriv_spatial_metric = make_with_value<tnsr::ijj<DataType, 3>>(coords, 0.);\n  } else {\n    const auto& metric_radial_potential =\n        get(cache->get_var(*this, Tags::MetricRadialPotential<DataType>{}));\n    const auto& dr_metric_radial_potential =\n        get(cache->get_var(*this, Tags::DrMetricRadialPotential<DataType>{}));\n    const auto& metric_angular_potential =\n        get(cache->get_var(*this, Tags::MetricAngularPotential<DataType>{}));\n    const auto& dr_metric_angular_potential =\n        get(cache->get_var(*this, Tags::DrMetricAngularPotential<DataType>{}));\n    *deriv_spatial_metric = tnsr::ijj<DataType, 3, Frame::Inertial>{\n        2.0 *\n        (exp(2.0 * metric_radial_potential) *\n             (dr_metric_radial_potential - 1.0 / radius) -\n         exp(2.0 * metric_angular_potential) *\n             (dr_metric_angular_potential - 1.0 / radius)) /\n        cube(radius)};\n    for (size_t k = 0; k < 3; ++k) {\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = i; j < 3; ++j) {\n          deriv_spatial_metric->get(k, i, j) *=\n              coords.get(k) * coords.get(i) * coords.get(j);\n          if (k == i) {\n            deriv_spatial_metric->get(k, i, j) +=\n                (exp(2.0 * metric_radial_potential) -\n                 exp(2.0 * metric_angular_potential)) /\n                square(radius) * coords.get(j);\n          }\n          if (k == j) {\n            deriv_spatial_metric->get(k, i, j) +=\n                (exp(2.0 * metric_radial_potential) -\n                 exp(2.0 * metric_angular_potential)) /\n                square(radius) * coords.get(i);\n          }\n          if (i == j) {\n            deriv_spatial_metric->get(k, i, j) +=\n                2.0 * exp(2.0 * metric_angular_potential) *\n                dr_metric_angular_potential * coords.get(k) / radius;\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<Scalar<DataType>*> sqrt_det_spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/) const {\n  const auto& metric_radial_potential =\n      get(cache->get_var(*this, Tags::MetricRadialPotential<DataType>{}));\n  const auto& metric_angular_potential =\n      get(cache->get_var(*this, Tags::MetricAngularPotential<DataType>{}));\n  get(*sqrt_det_spatial_metric) =\n      exp(metric_radial_potential + 2.0 * metric_angular_potential);\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3>*> inv_spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::InverseSpatialMetric<DataType, 3> /*meta*/) const {\n  if constexpr (Region == StarRegion::Center) {\n    const auto& metric_angular_potential =\n        get(cache->get_var(*this, Tags::MetricAngularPotential<DataType>{}));\n    get<0, 0>(*inv_spatial_metric) = exp(-2. * metric_angular_potential);\n    get<1, 1>(*inv_spatial_metric) = get<0, 0>(*inv_spatial_metric);\n    get<2, 2>(*inv_spatial_metric) = get<0, 0>(*inv_spatial_metric);\n    get<0, 1>(*inv_spatial_metric) = 0.;\n    get<0, 2>(*inv_spatial_metric) = 0.;\n    get<1, 2>(*inv_spatial_metric) = 0.;\n  } else {\n    const auto& metric_radial_potential =\n        get(cache->get_var(*this, Tags::MetricRadialPotential<DataType>{}));\n    const auto& metric_angular_potential =\n        get(cache->get_var(*this, Tags::MetricAngularPotential<DataType>{}));\n    *inv_spatial_metric = tnsr::II<DataType, 3, Frame::Inertial>{\n        (exp(-2. * metric_radial_potential) -\n         exp(-2. * metric_angular_potential)) /\n        square(radius)};\n    for (size_t d0 = 0; d0 < 3; ++d0) {\n      for (size_t d1 = d0; d1 < 3; ++d1) {\n        inv_spatial_metric->get(d0, d1) *= coords.get(d0) * coords.get(d1);\n      }\n      inv_spatial_metric->get(d0, d0) += exp(-2.0 * metric_angular_potential);\n    }\n  }\n}\n\ntemplate <typename DataType, StarRegion Region>\nvoid TovVariables<DataType, Region>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> extrinsic_curvature,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::ExtrinsicCurvature<DataType, 3> /*meta*/) const {\n  *extrinsic_curvature = make_with_value<tnsr::ii<DataType, 3>>(coords, 0.);\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__)\n}  // namespace tov_detail\n\nPUP::able::PUP_ID TovStar::my_PUP_ID = 0;\n\nbool operator==(const TovStar& lhs, const TovStar& rhs) {\n  return lhs.central_rest_mass_density_ == rhs.central_rest_mass_density_ and\n         lhs.coordinate_system_ == rhs.coordinate_system_ and\n         *lhs.equation_of_state_ == *rhs.equation_of_state_;\n}\n\nbool operator!=(const TovStar& lhs, const TovStar& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define REGION(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define INSTANTIATE(_, data) \\\n  template class tov_detail::TovVariables<DTYPE(data), REGION(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector),\n                        (tov_detail::StarRegion::Center,\n                         tov_detail::StarRegion::Interior,\n                         tov_detail::StarRegion::Exterior))\n\n#undef DTYPE\n#undef REGION\n#undef INSTANTIATE\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Solutions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Tov.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace RelativisticEuler::Solutions {\nnamespace tov_detail {\n\nenum class StarRegion { Center, Interior, Exterior };\n\nnamespace Tags {\ntemplate <typename DataType>\nstruct MassOverRadius : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct LogSpecificEnthalpy : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct ConformalFactor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct DrConformalFactor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct ArealRadius : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct DrArealRadius : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct DrPressure : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct MetricTimePotential : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct DrMetricTimePotential : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct MetricRadialPotential : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct DrMetricRadialPotential : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct MetricAngularPotential : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct DrMetricAngularPotential : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n}  // namespace Tags\n\ntemplate <typename DataType>\nusing TovVariablesCache = cached_temp_buffer_from_typelist<tmpl::list<\n    Tags::MassOverRadius<DataType>, Tags::LogSpecificEnthalpy<DataType>,\n    Tags::ConformalFactor<DataType>, Tags::DrConformalFactor<DataType>,\n    Tags::ArealRadius<DataType>, Tags::DrArealRadius<DataType>,\n    hydro::Tags::SpecificEnthalpy<DataType>,\n    hydro::Tags::RestMassDensity<DataType>,\n    hydro::Tags::ElectronFraction<DataType>, hydro::Tags::Pressure<DataType>,\n    Tags::DrPressure<DataType>,\n    ::Tags::deriv<hydro::Tags::Pressure<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    hydro::Tags::SpecificInternalEnergy<DataType>,\n    hydro::Tags::Temperature<DataType>, Tags::MetricTimePotential<DataType>,\n    Tags::DrMetricTimePotential<DataType>,\n    Tags::MetricRadialPotential<DataType>,\n    Tags::DrMetricRadialPotential<DataType>,\n    Tags::MetricAngularPotential<DataType>,\n    Tags::DrMetricAngularPotential<DataType>,\n    hydro::Tags::LorentzFactor<DataType>,\n    hydro::Tags::SpatialVelocity<DataType, 3>,\n    hydro::Tags::MagneticField<DataType, 3>,\n    hydro::Tags::DivergenceCleaningField<DataType>, gr::Tags::Lapse<DataType>,\n    ::Tags::dt<gr::Tags::Lapse<DataType>>,\n    ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>, Frame::Inertial>,\n    gr::Tags::Shift<DataType, 3>, ::Tags::dt<gr::Tags::Shift<DataType, 3>>,\n    ::Tags::deriv<gr::Tags::Shift<DataType, 3>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    gr::Tags::SpatialMetric<DataType, 3>,\n    ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>,\n    ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    gr::Tags::SqrtDetSpatialMetric<DataType>,\n    gr::Tags::ExtrinsicCurvature<DataType, 3>,\n    gr::Tags::InverseSpatialMetric<DataType, 3>>>;\n\ntemplate <typename DataType, StarRegion Region>\nstruct TovVariables {\n  static constexpr size_t Dim = 3;\n  using Cache = TovVariablesCache<DataType>;\n\n  TovVariables(const TovVariables&) = default;\n  TovVariables& operator=(const TovVariables&) = default;\n  TovVariables(TovVariables&&) = default;\n  TovVariables& operator=(TovVariables&&) = default;\n  virtual ~TovVariables() = default;\n  TovVariables(\n      const tnsr::I<DataType, 3>& local_coords, const DataType& local_radius,\n      const RelativisticEuler::Solutions::TovSolution& local_radial_solution,\n      const EquationsOfState::EquationOfState<true, 1>& local_eos)\n      : coords(local_coords),\n        radius(local_radius),\n        radial_solution(local_radial_solution),\n        eos(local_eos) {}\n\n  const tnsr::I<DataType, 3>& coords;\n  const DataType& radius;\n  const RelativisticEuler::Solutions::TovSolution& radial_solution;\n  const EquationsOfState::EquationOfState<true, 1>& eos;\n\n  void operator()(gsl::not_null<Scalar<DataType>*> mass_over_radius,\n                  gsl::not_null<Cache*> cache,\n                  Tags::MassOverRadius<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> log_specific_enthalpy,\n                  gsl::not_null<Cache*> cache,\n                  Tags::LogSpecificEnthalpy<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> conformal_factor,\n                  gsl::not_null<Cache*> cache,\n                  Tags::ConformalFactor<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> dr_conformal_factor,\n                  gsl::not_null<Cache*> cache,\n                  Tags::DrConformalFactor<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> areal_radius,\n                  gsl::not_null<Cache*> cache,\n                  Tags::ArealRadius<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> dr_areal_radius,\n                  gsl::not_null<Cache*> cache,\n                  Tags::DrArealRadius<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> specific_enthalpy,\n                  gsl::not_null<Cache*> cache,\n                  hydro::Tags::SpecificEnthalpy<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> temperature,\n                  gsl::not_null<Cache*> cache,\n                  hydro::Tags::Temperature<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> rest_mass_density,\n                  gsl::not_null<Cache*> cache,\n                  hydro::Tags::RestMassDensity<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> electron_fraction,\n                  gsl::not_null<Cache*> cache,\n                  hydro::Tags::ElectronFraction<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> pressure,\n                  gsl::not_null<Cache*> cache,\n                  hydro::Tags::Pressure<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> dr_pressure,\n                  gsl::not_null<Cache*> cache,\n                  Tags::DrPressure<DataType> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, 3>*> deriv_pressure,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<hydro::Tags::Pressure<DataType>, tmpl::size_t<3>,\n                    Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> specific_internal_energy,\n                  gsl::not_null<Cache*> cache,\n                  hydro::Tags::SpecificInternalEnergy<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> metric_time_potential,\n                  gsl::not_null<Cache*> cache,\n                  Tags::MetricTimePotential<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> dr_metric_time_potential,\n                  gsl::not_null<Cache*> cache,\n                  Tags::DrMetricTimePotential<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> metric_radial_potential,\n                  gsl::not_null<Cache*> cache,\n                  Tags::MetricRadialPotential<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> dr_metric_radial_potential,\n                  gsl::not_null<Cache*> cache,\n                  Tags::DrMetricRadialPotential<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> metric_angular_potential,\n                  gsl::not_null<Cache*> cache,\n                  Tags::MetricAngularPotential<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> dr_metric_angular_potential,\n                  gsl::not_null<Cache*> cache,\n                  Tags::DrMetricAngularPotential<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> lorentz_factor,\n                  gsl::not_null<Cache*> cache,\n                  hydro::Tags::LorentzFactor<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, 3>*> spatial_velocity,\n                  gsl::not_null<Cache*> cache,\n                  hydro::Tags::SpatialVelocity<DataType, 3> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::I<DataType, 3>*> magnetic_field,\n      gsl::not_null<Cache*> cache,\n      hydro::Tags::MagneticField<DataType, 3> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> div_cleaning_field,\n      gsl::not_null<Cache*> cache,\n      hydro::Tags::DivergenceCleaningField<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> lapse,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Lapse<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> dt_lapse,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::dt<gr::Tags::Lapse<DataType>> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::i<DataType, 3>*> deriv_lapse,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, 3>*> shift,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Shift<DataType, 3> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, 3>*> dt_shift,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::dt<gr::Tags::Shift<DataType, 3>> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<gr::Tags::Shift<DataType, 3>, tmpl::size_t<3>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::ii<DataType, 3>*> spatial_metric,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::SpatialMetric<DataType, 3> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::ii<DataType, 3>*> dt_spatial_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::ijj<DataType, 3>*> deriv_spatial_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>, tmpl::size_t<3>,\n                    Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> sqrt_det_spatial_metric,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::SqrtDetSpatialMetric<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::ii<DataType, 3>*> extrinsic_curvature,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::ExtrinsicCurvature<DataType, 3> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::II<DataType, 3>*> inv_spatial_metric,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::InverseSpatialMetric<DataType, 3> /*meta*/) const;\n};\n}  // namespace tov_detail\n\n/*!\n * \\brief A static spherically symmetric star\n *\n * An analytic solution for a static, spherically-symmetric star found by\n * solving the Tolman-Oppenheimer-Volkoff (TOV) equations.  The equation of\n * state is assumed to be that of a polytropic fluid.\n *\n * If the spherically symmetric metric is written as\n *\n * \\f[\n * ds^2 = - e^{2 \\Phi_t} dt^2 + e^{2 \\Phi_r} dr^2 + e^{2 \\Phi_\\Omega} r^2\n * d\\Omega^2\n * \\f]\n *\n * where \\f$r = \\delta_{mn} x^m x^n\\f$ is the radial coordinate and\n * \\f$\\Phi_t\\f$, \\f$\\Phi_r\\f$, and \\f$\\Phi_\\Omega\\f$ are the metric potentials,\n * then the lapse, shift, and spatial metric in Cartesian coordinates are\n *\n * \\f{align*}\n * \\alpha &= e^{\\Phi_t} \\\\\n * \\beta^i &= 0 \\\\\n * \\gamma_{ij} &= \\delta_{ij} e^{2 \\Phi_\\Omega} + \\delta_{im} \\delta_{jn}\n * \\frac{x^m x^n}{r^2} \\left( e^{2 \\Phi_r} - e^{2 \\Phi_\\Omega} \\right)\n * \\f}\n *\n * We solve the TOV equations with the method implemented in\n * `RelativisticEuler::Solutions::TovSolution`. It provides the areal\n * mass-over-radius \\f$m(r)/r\\f$ and the log of the specific enthalpy\n * \\f$\\log{h}\\f$. In areal (Schwarzschild) coordinates the spatial metric\n * potentials are\n *\n * \\f{align}\n * e^{\\Phi_r} &= \\left(1 - \\frac{2m}{r}\\right)^{-1/2} \\\\\n * e^{\\Phi_\\Omega} &= 1\n * \\f}\n *\n * In isotropic coordinates the spatial metric potentials are\n *\n * \\begin{equation}\n * e^{2\\Phi_r} = e^{2\\Phi_\\Omega} = \\psi^4\n * \\text{,}\n * \\end{equation}\n *\n * where $\\psi = \\sqrt{r / \\bar{r}}$ is the conformal factor, $r$ is the areal\n * (Schwarzschild) radius and $\\bar{r}$ is the isotropic radius. See\n * `RelativisticEuler::Solutions::TovSolution` for details.\n *\n * \\warning Isotropic coordinates should be used because the metric derivatives\n * are smooth. Otherwise the grid will over-compensate with finite difference\n * cells.\n */\n\nclass TovStar : public virtual evolution::initial_data::InitialData,\n                public MarkAsAnalyticSolution,\n                public AnalyticSolution<3> {\n public:\n  using equation_of_state_type = EquationsOfState::EquationOfState<true, 1>;\n\n  /// The central density of the star.\n  struct CentralDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The central density of the star.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  /// Areal (Schwarzschild) or isotropic coordinates\n  struct Coordinates {\n    using type = RelativisticEuler::Solutions::TovCoordinates;\n    static constexpr Options::String help = {\n        \"Areal ('Schwarzschild') or 'Isotropic' coordinates.\"};\n  };\n\n  static constexpr size_t volume_dim = 3_st;\n\n  using options =\n      tmpl::list<CentralDensity,\n                 hydro::OptionTags::InitialDataEquationOfState<true, 1>,\n                 Coordinates>;\n\n  static constexpr Options::String help = {\n      \"A static, spherically-symmetric star found by solving the \\n\"\n      \"Tolman-Oppenheimer-Volkoff (TOV) equations, with a given central \\n\"\n      \"density and equation of state.\"};\n\n  TovStar() = default;\n  TovStar(const TovStar& /*rhs*/);\n  TovStar& operator=(const TovStar& /*rhs*/);\n  TovStar(TovStar&& /*rhs*/) = default;\n  TovStar& operator=(TovStar&& /*rhs*/) = default;\n  ~TovStar() override = default;\n  TovStar(double central_rest_mass_density,\n          std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>\n              equation_of_state,\n          const RelativisticEuler::Solutions::TovCoordinates coordinate_system =\n              RelativisticEuler::Solutions::TovCoordinates::Schwarzschild);\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit TovStar(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TovStar);\n  /// \\endcond\n\n  /// Retrieve a collection of variables at `(x, t)`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, 3>& x,\n                                         const double /*t*/,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    return variables_impl<tov_detail::TovVariables>(x, tmpl::list<Tags...>{});\n  }\n\n  /// NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) override;\n\n  const EquationsOfState::EquationOfState<true, 1>& equation_of_state() const {\n    return *equation_of_state_;\n  }\n\n  /// The radial profile of the star\n  const RelativisticEuler::Solutions::TovSolution& radial_solution() const {\n    return radial_solution_;\n  }\n\n protected:\n  template <template <class, tov_detail::StarRegion> class VarsComputer,\n            typename DataType, typename... Tags, typename... VarsComputerArgs>\n  tuples::TaggedTuple<Tags...> variables_impl(\n      const tnsr::I<DataType, 3>& x, tmpl::list<Tags...> /*meta*/,\n      VarsComputerArgs&&... vars_computer_args) const {\n    const double outer_radius = radial_solution_.outer_radius();\n    const double center_radius_cutoff = 1.e-30 * outer_radius;\n    const DataType radius = get(magnitude(x));\n    // Dispatch interior and exterior regions of the star.\n    // - Include the equality in the conditions below for the outer radius so\n    //   the `DataVector` variants are preferred over the pointwise `double`\n    //   variant when possible.\n    // - Order the conditions so the cheaper exterior solution is preferred over\n    //   the interior.\n    // - A `DataVector` variant for the center of the star is not needed because\n    //   it's only a single point.\n    if (min(radius) >= outer_radius) {\n      // All points are outside the star. This could be replaced by a\n      // Schwarzschild solution.\n      using ExteriorVarsComputer =\n          VarsComputer<DataType, tov_detail::StarRegion::Exterior>;\n      typename ExteriorVarsComputer::Cache cache{get_size(radius)};\n      ExteriorVarsComputer computer{\n          x, radius, radial_solution_, *equation_of_state_,\n          std::forward<VarsComputerArgs>(vars_computer_args)...};\n      return {cache.get_var(computer, Tags{})...};\n    } else if (max(radius) <= outer_radius and\n               min(radius) > center_radius_cutoff) {\n      // All points are in the star interior, but not at the center\n      using InteriorVarsComputer =\n          VarsComputer<DataType, tov_detail::StarRegion::Interior>;\n      typename InteriorVarsComputer::Cache cache{get_size(radius)};\n      InteriorVarsComputer computer{\n          x, radius, radial_solution_, *equation_of_state_,\n          std::forward<VarsComputerArgs>(vars_computer_args)...};\n      return {cache.get_var(computer, Tags{})...};\n    } else {\n      // Points can be at the center, in the interior, or outside the star, so\n      // check each point individually\n      const size_t num_points = get_size(radius);\n      tuples::TaggedTuple<Tags...> vars{typename Tags::type{num_points}...};\n      const auto get_var = [&vars](const size_t point_index, auto& local_cache,\n                                   const auto& local_computer, auto tag_v) {\n        using tag = std::decay_t<decltype(tag_v)>;\n        if constexpr (std::is_same_v<DataType, DataVector>) {\n          using tags_double = typename VarsComputer<\n              double, tov_detail::StarRegion::Exterior>::Cache::tags_list;\n          using tags_dv = typename VarsComputer<\n              DataVector, tov_detail::StarRegion::Exterior>::Cache::tags_list;\n          using tag_double =\n              tmpl::at<tags_double, tmpl::index_of<tags_dv, tag>>;\n          const auto& tensor =\n              local_cache.get_var(local_computer, tag_double{});\n          for (size_t component = 0; component < tensor.size(); ++component) {\n            get<tag>(vars)[component][point_index] = tensor[component];\n          }\n        } else {\n          (void)point_index;\n          get<tag>(vars) = local_cache.get_var(local_computer, tag{});\n        }\n        return '0';\n      };\n      using CenterVarsComputer =\n          VarsComputer<double, tov_detail::StarRegion::Center>;\n      using InteriorVarsComputer =\n          VarsComputer<double, tov_detail::StarRegion::Interior>;\n      using ExteriorVarsComputer =\n          VarsComputer<double, tov_detail::StarRegion::Exterior>;\n      for (size_t i = 0; i < num_points; ++i) {\n        const tnsr::I<double, 3> x_i{\n            {{get_element(get<0>(x), i), get_element(get<1>(x), i),\n              get_element(get<2>(x), i)}}};\n        if (get_element(radius, i) > outer_radius) {\n          typename ExteriorVarsComputer::Cache cache{1};\n          ExteriorVarsComputer computer{\n              x_i, get_element(radius, i), radial_solution_,\n              *equation_of_state_,\n              std::forward<VarsComputerArgs>(vars_computer_args)...};\n          expand_pack(get_var(i, cache, computer, Tags{})...);\n        } else if (get_element(radius, i) > center_radius_cutoff) {\n          typename InteriorVarsComputer::Cache cache{1};\n          InteriorVarsComputer computer{\n              x_i, get_element(radius, i), radial_solution_,\n              *equation_of_state_,\n              std::forward<VarsComputerArgs>(vars_computer_args)...};\n          expand_pack(get_var(i, cache, computer, Tags{})...);\n        } else {\n          typename CenterVarsComputer::Cache cache{1};\n          CenterVarsComputer computer{\n              x_i, get_element(radius, i), radial_solution_,\n              *equation_of_state_,\n              std::forward<VarsComputerArgs>(vars_computer_args)...};\n          expand_pack(get_var(i, cache, computer, Tags{})...);\n        }\n      }\n      return vars;\n    }\n  }\n\n public:\n  template <typename DataType>\n  using tags = tmpl::list_difference<\n      typename tov_detail::TovVariablesCache<DataType>::tags_list,\n      tmpl::list<\n          // Remove internal tags, which may not be available in the full domain\n          tov_detail::Tags::MassOverRadius<DataType>,\n          tov_detail::Tags::LogSpecificEnthalpy<DataType>,\n          tov_detail::Tags::ConformalFactor<DataType>,\n          tov_detail::Tags::DrConformalFactor<DataType>,\n          tov_detail::Tags::ArealRadius<DataType>,\n          tov_detail::Tags::DrArealRadius<DataType>,\n          tov_detail::Tags::DrPressure<DataType>,\n          tov_detail::Tags::MetricTimePotential<DataType>,\n          tov_detail::Tags::DrMetricTimePotential<DataType>,\n          tov_detail::Tags::MetricRadialPotential<DataType>,\n          tov_detail::Tags::DrMetricRadialPotential<DataType>,\n          tov_detail::Tags::MetricAngularPotential<DataType>,\n          tov_detail::Tags::DrMetricAngularPotential<DataType>>>;\n\n private:\n  friend bool operator==(const TovStar& lhs, const TovStar& rhs);\n\n  double central_rest_mass_density_ =\n      std::numeric_limits<double>::signaling_NaN();\n  std::unique_ptr<equation_of_state_type> equation_of_state_;\n  RelativisticEuler::Solutions::TovCoordinates coordinate_system_{};\n  RelativisticEuler::Solutions::TovSolution radial_solution_{};\n};\n\nbool operator!=(const TovStar& lhs, const TovStar& rhs);\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarAdvectionSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Krivodonova.cpp\n  Kuzmin.cpp\n  Sinusoid.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Krivodonova.hpp\n  Kuzmin.hpp\n  Sinusoid.hpp\n  Solutions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC DataStructures\n  INTERFACE ErrorHandling\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Krivodonova.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Kuzmin.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Sinusoid.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection::Solutions {\n/*!\n * \\brief Typelist of all analytic solutions of advection system\n */\ntemplate <size_t Dim>\nusing all_analytic_solutions = tmpl::flatten<tmpl::list<\n    tmpl::conditional_t<Dim == 1,\n                        tmpl::list<ScalarAdvection::Solutions::Sinusoid,\n                                   ScalarAdvection::Solutions::Krivodonova>,\n                        tmpl::list<>>,\n    tmpl::conditional_t<Dim == 2,\n                        tmpl::list<ScalarAdvection::Solutions::Kuzmin>,\n                        tmpl::list<>>>>;\n}  // namespace ScalarAdvection::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Krivodonova.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Krivodonova.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ScalarAdvection::Solutions {\n\nstd::unique_ptr<evolution::initial_data::InitialData> Krivodonova::get_clone()\n    const {\n  return std::make_unique<Krivodonova>(*this);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<ScalarAdvection::Tags::U> Krivodonova::variables(\n    const tnsr::I<DataType, 1>& x, double t,\n    tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const {\n  // map each grid points x(t) back to its initial position within [-1, 1] at\n  // t=0\n  auto x0 = make_with_value<tnsr::I<DataType, 1>>(x, 0.0);\n  get<0>(x0) = get<0>(x) - t;\n  for (size_t i = 0; i < get_size(get<0>(x)); ++i) {\n    auto& xi = get<0>(x0)[i];\n    // Since we are using the periodic boundary condition with the domain\n    // [-1.0, 1.0], we need to do a 'modulo' operation:\n    //\n    //  x - vt = 2.0 * N + x0\n    //\n    // and get the value of x0, where N is an integer and x0 is a real number in\n    // [-1.0, 1.0). Then x0 corresponds to the initial position of the point\n    // (x,t). We use the value of x0 to compute U(x,t) = U(x0,0).\n    xi = xi - 2.0 * floor(0.5 * (xi + 1.0));\n  }\n\n  // parameters for the initial profile (from the Krivodonova paper)\n  const double a{0.5};\n  const double z{-0.7};\n  const double delta{0.005};\n  const double alpha{10.0};\n  const double beta{log(2.0) / (36.0 * square(delta))};\n\n  const auto F = [](const double x_var, const double alpha_var,\n                    const double a_var) {\n    return sqrt(fmax(1.0 - pow(alpha_var, 2.0) * pow(x_var - a_var, 2.0), 0.0));\n  };\n  const auto G = [](const double x_var, const double beta_var,\n                    const double z_var) {\n    return exp(-beta_var * pow(x_var - z_var, 2.0));\n  };\n\n  // evaluate U(x,t) = U(x0,0)\n  auto u_variable = make_with_value<Scalar<DataType>>(x0, 0.0);\n  for (size_t i = 0; i < get_size(get<0>(x0)); ++i) {\n    const auto& xi = get<0>(x0)[i];\n    auto& ui = get(u_variable)[i];\n\n    if ((-0.8 <= xi) and (xi <= -0.6)) {\n      ui = (G(xi, beta, z - delta) + G(xi, beta, z + delta) +\n            4.0 * G(xi, beta, z)) /\n           6.0;\n    } else if ((-0.4 <= xi) and (xi <= -0.2)) {\n      ui = 1.0;\n    } else if ((0.0 <= xi) and (xi <= 0.2)) {\n      ui = 1.0 - fabs(10.0 * xi - 1.0);\n    } else if ((0.4 <= xi) and (xi <= 0.6)) {\n      ui = (F(xi, alpha, a - delta) + F(xi, alpha, a + delta) +\n            4.0 * F(xi, alpha, a)) /\n           6.0;\n    } else {\n      ui = 0.0;\n    }\n  }\n  return u_variable;\n}\n\nvoid Krivodonova::pup(PUP::er& p) { InitialData::pup(p); }\n\nKrivodonova::Krivodonova(CkMigrateMessage* msg) : InitialData(msg) {}\n\nPUP::able::PUP_ID Krivodonova::my_PUP_ID = 0;\n\nbool operator==(const Krivodonova& /*lhs*/, const Krivodonova& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const Krivodonova& lhs, const Krivodonova& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ScalarAdvection::Solutions\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                             \\\n  template tuples::TaggedTuple<ScalarAdvection::Tags::U> \\\n  ScalarAdvection::Solutions::Krivodonova::variables(    \\\n      const tnsr::I<DTYPE(data), 1>& x, double t,        \\\n      tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Krivodonova.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarAdvection {\nnamespace Solutions {\n/*!\n * \\brief Initial data for the 1D scalar advection problem adopted from\n * \\cite Krivodonova2007 and its analytic solution.\n *\n * The initial proﬁle consists of a combination of Gaussians, a square pulse,\n * a sharp triangle, and a combination of half-ellipses.\n *\n * \\f{align*}\n * u(x,t=0) = \\left\\{\\begin{array}{lcl}\n *  (G(x,\\beta,z-\\delta) + G(x,\\beta,z+\\delta) + 4G(x,\\beta,z))/6 & \\text{if} &\n *   -0.8 \\leq x \\leq -0.6 \\\\\n *  1 & \\text{if} & -0.4 \\leq x \\leq -0.2 \\\\\n *  1 - |10(x-0.1)| & \\text{if} & 0 \\leq x \\leq 0.2 \\\\\n *  (F(x,\\alpha,a-\\delta) + F(x,\\alpha,a+\\delta) + 4F(x,\\alpha,a))/6 & \\text{if}\n *  & 0.4 \\leq x \\leq 0.6 \\\\\n *  0 & \\text{otherwise} & \\\\\n *  \\end{array}\\right\\},\n * \\f}\n *\n * where\n *\n * \\f{align*}\n * G(x,\\beta, z)  & = e^{-\\beta(x-z)^2} \\\\\n * F(x,\\alpha, a) & = \\sqrt{\\max(1-\\alpha^2(x-a)^2, 0)}\n * \\f}\n *\n * with \\f$a=0.5, z=-0.7, \\delta=0.005, \\alpha=10, \\text{and }\\beta =\n * \\log2/(36\\delta^2)\\f$.\n *\n * The system is evolved over the 1D domain \\f$[-1, 1]\\f$ with the constant\n * advection velocity field \\f$v(x) = 1.0\\f$ and with the periodic boundary\n * condition. The initial profile is simply advected (translated) to +x\n * direction, going over cycles in the domain every 2.0 time unit.\n */\nclass Krivodonova : public evolution::initial_data::InitialData,\n                    public MarkAsAnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"An advecting 1D profile adopted from Krivodonova2007 paper, periodic \"\n      \"over the interval [-1, 1]\"};\n\n  Krivodonova() = default;\n  Krivodonova(const Krivodonova&) = default;\n  Krivodonova& operator=(const Krivodonova&) = default;\n  Krivodonova(Krivodonova&&) = default;\n  Krivodonova& operator=(Krivodonova&&) = default;\n  ~Krivodonova() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  template <typename DataType>\n  tuples::TaggedTuple<ScalarAdvection::Tags::U> variables(\n      const tnsr::I<DataType, 1>& x, double t,\n      tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// \\cond\n  explicit Krivodonova(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Krivodonova);\n  /// \\endcond\n};\n\nbool operator==(const Krivodonova& /*lhs*/, const Krivodonova& /*rhs*/);\n\nbool operator!=(const Krivodonova& lhs, const Krivodonova& rhs);\n\n}  // namespace Solutions\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Kuzmin.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Kuzmin.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ScalarAdvection::Solutions {\n\nstd::unique_ptr<evolution::initial_data::InitialData> Kuzmin::get_clone()\n    const {\n  return std::make_unique<Kuzmin>(*this);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<ScalarAdvection::Tags::U> Kuzmin::variables(\n    const tnsr::I<DataType, 2>& x, double t,\n    tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const {\n  auto coords_init = make_with_value<tnsr::I<DataType, 2>>(x, 0.0);\n  auto& x0 = get<0>(coords_init);\n  auto& y0 = get<1>(coords_init);\n\n  // Map each grid points [x(t),y(t)] back to its initial position at t=0:\n  // applying 2D rotation to translated coordinates, and translate them back.\n  // Note that in this analytic solution, all the regions with nonzero values of\n  // U lies within the circle centered at (0.5, 0.5) with radius 0.5. Therefore\n  // we do not need to take care of a specific boundary condition and it is\n  // sufficient to simply rotate all the coordinates back to t=0.\n  x0 = (get<0>(x) - 0.5) * cos(t) + (get<1>(x) - 0.5) * sin(t) + 0.5;\n  y0 = -(get<0>(x) - 0.5) * sin(t) + (get<1>(x) - 0.5) * cos(t) + 0.5;\n\n  // parameters and functions for the initial profile (from the Kuzmin paper)\n  const double r0{0.15};\n  const auto r_xy = [&r0](const double x_var, const double x0_var,\n                          const double y_var, const double y0_var) {\n    return sqrt(pow(x_var - x0_var, 2.0) + pow(y_var - y0_var, 2.0)) / r0;\n  };\n\n  double r_cylinder = std::numeric_limits<double>::signaling_NaN();\n  double r_cone = std::numeric_limits<double>::signaling_NaN();\n  double r_hump = std::numeric_limits<double>::signaling_NaN();\n\n  // evaluate u(x,y,t) = u(x0,y0,0)\n  auto u_variable = make_with_value<Scalar<DataType>>(coords_init, 0.0);\n  for (size_t i = 0; i < get_size(get<0>(x)); ++i) {\n    const auto& xi = x0[i];\n    const auto& yi = y0[i];\n    auto& ui = get(u_variable)[i];\n\n    // slotted cylinder centered at (0.5, 0.75)\n    r_cylinder = r_xy(xi, 0.5, yi, 0.75);\n    // cone centered at (0.5, 0.25)\n    r_cone = r_xy(xi, 0.5, yi, 0.25);\n    // hump centered at (0.25, 0.5)\n    r_hump = r_xy(xi, 0.25, yi, 0.5);\n\n    if (r_cylinder <= 1.0) {\n      if ((abs(xi - 0.5) >= 0.025) or (yi >= 0.85)) {\n        ui = 1.0;\n      } else {\n        ui = 0.0;\n      }\n    } else if (r_cone <= 1.0) {\n      ui = 1.0 - r_cone;\n    } else if (r_hump <= 1.0) {\n      ui = 0.25 * (1.0 + cos(M_PI * r_hump));\n    } else {\n      ui = 0.0;\n    }\n  }\n  return u_variable;\n}\n\nvoid Kuzmin::pup(PUP::er& p) { InitialData::pup(p); }\n\nKuzmin::Kuzmin(CkMigrateMessage* msg) : InitialData(msg) {}\n\nPUP::able::PUP_ID Kuzmin::my_PUP_ID = 0;\n\nbool operator==(const Kuzmin& /*lhs*/, const Kuzmin& /*rhs*/) { return true; }\n\nbool operator!=(const Kuzmin& lhs, const Kuzmin& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ScalarAdvection::Solutions\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                             \\\n  template tuples::TaggedTuple<ScalarAdvection::Tags::U> \\\n  ScalarAdvection::Solutions::Kuzmin::variables(         \\\n      const tnsr::I<DTYPE(data), 2>& x, double t,        \\\n      tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Kuzmin.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarAdvection {\nnamespace Solutions {\n/*!\n * \\brief Initial data for the 2D scalar advection problem adopted from\n * \\cite Kuzmin2014 and its analytic solution.\n *\n * Let \\f$r(x,y) = \\sqrt{(x-x_0)^2 + (y-y_0)^2}/r_0\\f$ be the normalized\n * distance from a point \\f$(x_0,y_0)\\f$ within a circle with the radius\n * \\f$r_0=0.15\\f$. The initial proﬁle consists of three bodies:\n *\n * - a slotted cylinder centered at \\f$(0.5, 0.75)\\f$\n * \\f{align*}\n * u(x,y) = \\left\\{\\begin{array}{ll}\n *  1 & \\text{if } |x-x_0| \\geq 0.025 \\text{ or } y \\geq 0.85 \\\\\n *  0 & \\text{otherwise} \\\\\n * \\end{array}\\right\\},\n * \\f}\n *\n * - a cone centered at \\f$(0.5, 0.25)\\f$\n * \\f{align*}\n * u(x,y) = 1 - r(x,y) ,\n * \\f}\n *\n * - and a hump centered at \\f$(0.25, 0.5)\\f$\n * \\f{align*}\n * u(x,y) = \\frac{1 + \\cos(\\pi r(x,y))}{4} .\n * \\f}\n *\n * The system is evolved over the domain \\f$[0,1]\\times[0,1]\\f$ with the\n * advection velocity field \\f$v(x,y) = (0.5-y,-0.5+x)\\f$, which causes a solid\n * rotation about \\f$(x,y)=(0.5,0.5)\\f$ with angular velocity being 1.0. We use\n * the periodic boundary condition for evolving this problem.\n */\nclass Kuzmin : public evolution::initial_data::InitialData,\n               public MarkAsAnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"A rotating 2D scalar advecting problem adopted from Kuzmin2014 paper\"};\n\n  Kuzmin() = default;\n  Kuzmin(const Kuzmin&) = default;\n  Kuzmin& operator=(const Kuzmin&) = default;\n  Kuzmin(Kuzmin&&) = default;\n  Kuzmin& operator=(Kuzmin&&) = default;\n  ~Kuzmin() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  template <typename DataType>\n  tuples::TaggedTuple<ScalarAdvection::Tags::U> variables(\n      const tnsr::I<DataType, 2>& x, double t,\n      tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// \\cond\n  explicit Kuzmin(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Kuzmin);\n  /// \\endcond\n};\n\nbool operator==(const Kuzmin& /*lhs*/, const Kuzmin& /*rhs*/);\n\nbool operator!=(const Kuzmin& lhs, const Kuzmin& rhs);\n\n}  // namespace Solutions\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Sinusoid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Sinusoid.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace ScalarAdvection::Solutions {\n\nstd::unique_ptr<evolution::initial_data::InitialData> Sinusoid::get_clone()\n    const {\n  return std::make_unique<Sinusoid>(*this);\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<ScalarAdvection::Tags::U> Sinusoid::variables(\n    const tnsr::I<DataType, 1>& x, double t,\n    tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const {\n  auto u = make_with_value<Scalar<DataType>>(get<0>(x), 0.0);\n  get(u) = sin(M_PI * (get<0>(x) - t));\n  return u;\n}\n\nvoid Sinusoid::pup(PUP::er& p) { InitialData::pup(p); }\n\nSinusoid::Sinusoid(CkMigrateMessage* msg) : InitialData(msg) {}\n\nPUP::able::PUP_ID Sinusoid::my_PUP_ID = 0;\n\nbool operator==(const Sinusoid& /*lhs*/, const Sinusoid& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const Sinusoid& lhs, const Sinusoid& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ScalarAdvection::Solutions\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                             \\\n  template tuples::TaggedTuple<ScalarAdvection::Tags::U> \\\n  ScalarAdvection::Solutions::Sinusoid::variables(       \\\n      const tnsr::I<DTYPE(data), 1>& x, double t,        \\\n      tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Sinusoid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarAdvection {\nnamespace Solutions {\n/*!\n * \\brief An 1D sinusoidal wave advecting with speed 1.0, periodic over the\n * interval \\f$[-1, 1]\\f$.\n *\n * \\f{align}{\n *   u(x,t)=\\sin \\pi(x-t)\n * \\f}\n *\n */\nclass Sinusoid : public evolution::initial_data::InitialData,\n                 public MarkAsAnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"An advecting 1D sine wave u(x,t) = sin(pi(x-t)), periodic over the \"\n      \"interval [-1, 1]\"};\n\n  Sinusoid() = default;\n  Sinusoid(const Sinusoid&) = default;\n  Sinusoid& operator=(const Sinusoid&) = default;\n  Sinusoid(Sinusoid&&) = default;\n  Sinusoid& operator=(Sinusoid&&) = default;\n  ~Sinusoid() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  template <typename DataType>\n  tuples::TaggedTuple<ScalarAdvection::Tags::U> variables(\n      const tnsr::I<DataType, 1>& x, double t,\n      tmpl::list<ScalarAdvection::Tags::U> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n  /// \\cond\n  explicit Sinusoid(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Sinusoid);\n  /// \\endcond\n};\n\nbool operator==(const Sinusoid& /*lhs*/, const Sinusoid& /*rhs*/);\n\nbool operator!=(const Sinusoid& lhs, const Sinusoid& rhs);\n\n}  // namespace Solutions\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines ScalarAdvection::Solutions\n\n#pragma once\n\nnamespace ScalarAdvection {\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing a solution to the ScalarAdvection equation\n */\nnamespace Solutions {}\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/SubitemTag.hpp\"\n#include \"DataStructures/DataBox/Subitems.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionGroupsGroup\n/// Holds the `OptionTags::AnalyticSolution` option in the input file\nstruct AnalyticSolutionGroup {\n  static std::string name() { return \"AnalyticSolution\"; }\n  static constexpr Options::String help =\n      \"Analytic solution used for the initial data and errors\";\n};\n\n/// \\ingroup OptionTagsGroup\n/// The analytic solution, with the type of the analytic solution set as the\n/// template parameter\ntemplate <typename SolutionType>\nstruct AnalyticSolution {\n  static std::string name() { return pretty_type::name<SolutionType>(); }\n  static constexpr Options::String help = \"Options for the analytic solution\";\n  using type = SolutionType;\n  using group = AnalyticSolutionGroup;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\ingroup OptionTagsGroup\n/// The analytic solution, with the type of the analytic solution set as the\n/// template parameter\ntemplate <typename SolutionType>\nstruct AnalyticSolution : db::SimpleTag {\n  using type = SolutionType;\n  using option_tags = tmpl::list<::OptionTags::AnalyticSolution<SolutionType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static SolutionType create_from_options(\n      const SolutionType& analytic_solution) {\n    return serialize_and_deserialize<type>(analytic_solution);\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\brief Prefix indicating the analytic solution value for a quantity\n///\n/// \\snippet AnalyticSolutions/Test_Tags.cpp analytic_name\ntemplate <typename Tag>\nstruct Analytic : db::PrefixTag, db::SimpleTag {\n  using type = std::optional<typename Tag::type>;\n  using tag = Tag;\n};\n\nnamespace detail {\ntemplate <typename Tag>\nstruct AnalyticImpl : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n}  // namespace detail\n\n/*!\n * \\brief The analytic solution of the `FieldTags`.\n *\n * The `std::optional` is a `nullopt` if there is no analytic solution.\n *\n * The individual `FieldTags` are added as `Tags::Analytic<Tag>` which holds a\n * `std::optional<Tensor>`.\n */\ntemplate <typename FieldTags>\nstruct AnalyticSolutions : db::SimpleTag {\n  using field_tags = FieldTags;\n  using type = std::optional<\n      ::Variables<db::wrap_tags_in<detail::AnalyticImpl, FieldTags>>>;\n};\n\nnamespace detail {\n// Check if the argument is a `::Tags::AnalyticSolutions` template, or derived\n// from it\ntemplate <typename FieldTags>\nconstexpr std::true_type is_analytic_solutions(\n    ::Tags::AnalyticSolutions<FieldTags>&&) {\n  return {};\n}\n\nconstexpr std::false_type is_analytic_solutions(...) { return {}; }\n\ntemplate <typename Tag>\nstatic constexpr bool is_analytic_solutions_v =\n    decltype(is_analytic_solutions(std::declval<Tag>()))::value;\n}  // namespace detail\n\n/*!\n * \\brief The error of the `Tag` defined as `numerical - analytic`.\n *\n * The `std::optional` is a `nullopt` if no error was able to be computed, e.g.\n * if there is no analytic solution to compare to.\n */\ntemplate <typename Tag>\nstruct Error : db::PrefixTag, db::SimpleTag {\n  using type = std::optional<typename Tag::type>;\n  using tag = Tag;\n};\n\nnamespace detail {\ntemplate <typename Tag>\nstruct ErrorImpl : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n}  // namespace detail\n\n/*!\n * \\brief The error of the `FieldTags`, defined as `numerical - analytic`.\n *\n * The `std::optional` is a `nullopt` if no error was able to be computed, e.g.\n * if there is no analytic solution to compare to.\n *\n * The individual `FieldTags` are added as `Tags::Error<Tag>` which holds a\n * `std::optional<Tensor>`.\n */\ntemplate <typename FieldTags>\nstruct Errors : db::SimpleTag {\n  using field_tags = FieldTags;\n  using type = std::optional<\n      ::Variables<db::wrap_tags_in<detail::ErrorImpl, FieldTags>>>;\n};\n\n/// \\cond\ntemplate <typename FieldTagsList>\nstruct ErrorsCompute;\n/// \\endcond\n\n/*!\n * \\brief Compute tag for computing the error from the `Tags::Analytic` of the\n * `FieldTags`.\n *\n * The error is defined as `numerical - analytic`.\n *\n * We use individual `Tensor`s rather than `Variables` of the `FieldTags`\n * because not all `FieldTags` are always stored in the same `Variables`.\n * For example, in the Generalized Harmonic-GRMHD combined system, the analytic\n * variables for the GH system are part of the `evolved_variables_tag` while the\n * GRMHD analytic variables are part of the `primitive_variables_tag`. A similar\n * issue arises in some elliptic systems. The main drawback of having to use the\n * tensor-by-tensor implementation instead of a `Variables` implementation is\n * the added loop complexity. However, this is offset by the reduced code\n * duplication and flexibility.\n */\ntemplate <typename... FieldTags>\nstruct ErrorsCompute<tmpl::list<FieldTags...>>\n    : Errors<tmpl::list<FieldTags...>>, db::ComputeTag {\n  using field_tags = tmpl::list<FieldTags...>;\n  using base = Errors<tmpl::list<FieldTags...>>;\n  using return_type = typename base::type;\n  using argument_tags =\n      tmpl::list<FieldTags..., ::Tags::Analytic<FieldTags>...>;\n  static void function(\n      const gsl::not_null<return_type*> errors,\n      const typename FieldTags::type&... vars,\n      const typename ::Tags::Analytic<FieldTags>::type&... analytic_vars) {\n    if (const auto& first_analytic_var = get_first_argument(analytic_vars...);\n        first_analytic_var.has_value()) {\n      // Construct a Variables of the size of the DataVector of the first\n      // analytic_vars tensor.\n      *errors = typename return_type::value_type{\n          first_analytic_var.value()[0].size()};\n      const auto compute_error = [](const auto error_ptr, const auto& var,\n                                    const auto& analytic_var) {\n        for (size_t tensor_index = 0; tensor_index < var.size();\n             ++tensor_index) {\n          (*error_ptr)[tensor_index] =\n              var[tensor_index] - analytic_var[tensor_index];\n        }\n      };\n      EXPAND_PACK_LEFT_TO_RIGHT(compute_error(\n          make_not_null(\n              &get<::Tags::detail::ErrorImpl<FieldTags>>(errors->value())),\n          vars, analytic_vars.value()));\n    } else {\n      *errors = std::nullopt;\n    }\n  }\n};\n\nnamespace detail {\n// Check if the argument is a `::Tags::Errors` template, or derived\n// from it\ntemplate <typename FieldTags>\nconstexpr std::true_type is_errors(::Tags::Errors<FieldTags>&&) {\n  return {};\n}\n\nconstexpr std::false_type is_errors(...) { return {}; }\n\ntemplate <typename Tag>\nstatic constexpr bool is_errors_v =\n    decltype(is_errors(std::declval<Tag>()))::value;\n}  // namespace detail\n}  // namespace Tags\n\n/// \\cond\nnamespace Tags {\ntemplate <typename Tag, typename ParentTag>\nstruct Subitem<Tag, ParentTag,\n               Requires<detail::is_analytic_solutions_v<ParentTag>>>\n    : Tag, db::ComputeTag {\n  using field_tags = typename ParentTag::field_tags;\n  using base = Tag;\n  using DataType = typename Tag::tag::type::type;\n  using parent_tag = AnalyticSolutions<field_tags>;\n  using argument_tags = tmpl::list<parent_tag>;\n  static void function(const gsl::not_null<typename base::type*> result,\n                       const typename parent_tag::type& vars) {\n    if (vars.has_value()) {\n      result->reset();\n      *result = typename base::type::value_type{};\n      const auto& tensor =\n          get<typename detail::AnalyticImpl<typename Tag::tag>>(*vars);\n      const size_t num_components = tensor.size();\n      for (size_t storage_index = 0; storage_index < num_components;\n           ++storage_index) {\n        (**result)[storage_index].set_data_ref(\n            // NOLINTNEXTLINE\n            make_not_null(&const_cast<DataType&>(tensor[storage_index])));\n      }\n    } else {\n      *result = std::nullopt;\n    }\n  }\n};\n\ntemplate <typename Tag, typename ParentTag>\nstruct Subitem<Tag, ParentTag, Requires<detail::is_errors_v<ParentTag>>>\n    : Tag, db::ComputeTag {\n  using field_tags = typename ParentTag::field_tags;\n  using base = Tag;\n  using DataType = typename Tag::tag::type::type;\n  using parent_tag = Errors<field_tags>;\n  using argument_tags = tmpl::list<parent_tag>;\n  static void function(const gsl::not_null<typename base::type*> result,\n                       const typename parent_tag::type& vars) {\n    if (vars.has_value()) {\n      result->reset();\n      *result = typename base::type::value_type{};\n      const auto& tensor =\n          get<typename detail::ErrorImpl<typename Tag::tag>>(*vars);\n      const size_t num_components = tensor.size();\n      for (size_t storage_index = 0; storage_index < num_components;\n           ++storage_index) {\n        (**result)[storage_index].set_data_ref(\n            // NOLINTNEXTLINE\n            make_not_null(&const_cast<DataType&>(tensor[storage_index])));\n      }\n    } else {\n      *result = std::nullopt;\n    }\n  }\n};\n}  // namespace Tags\n\nnamespace db {\ntemplate <typename Tag>\nstruct Subitems<Tag, Requires<Tags::detail::is_analytic_solutions_v<Tag>>> {\n  using field_tags = typename Tag::field_tags;\n  using type = db::wrap_tags_in<::Tags::Analytic, field_tags>;\n\n  template <typename Subtag>\n  static void create_item(\n      const gsl::not_null<typename Tag::type*> parent_value,\n      const gsl::not_null<typename Subtag::type*> sub_value) {\n    if (parent_value->has_value()) {\n      auto& tensor = get<::Tags::detail::AnalyticImpl<typename Subtag::tag>>(\n          parent_value->value());\n      sub_value->emplace();\n      // Only update the Tensor if the Variables has changed its allocation\n      if constexpr (not is_any_spin_weighted_v<\n                        typename Subtag::tag::type::type>) {\n        if (tensor.begin()->data() != sub_value->value().begin()->data()) {\n          for (auto tensor_component = tensor.begin(),\n                    sub_var_it = sub_value->value().begin();\n               tensor_component != tensor.end();\n               ++tensor_component, ++sub_var_it) {\n            sub_var_it->set_data_ref(make_not_null(&*tensor_component));\n          }\n        }\n      } else {\n        if (tensor.begin()->data().data() !=\n            sub_value->value().begin()->data().data()) {\n          for (auto tensor_component = tensor.begin(),\n                    sub_var_it = sub_value->value().begin();\n               tensor_component != tensor.end();\n               ++tensor_component, ++sub_var_it) {\n            sub_var_it->set_data_ref(make_not_null(&*tensor_component));\n          }\n        }\n      }\n    } else {\n      *sub_value = std::nullopt;\n    }\n  }\n};\n\ntemplate <typename Tag>\nstruct Subitems<Tag, Requires<Tags::detail::is_errors_v<Tag>>> {\n  using field_tags = typename Tag::field_tags;\n  using type = db::wrap_tags_in<::Tags::Error, field_tags>;\n\n  template <typename Subtag>\n  static void create_item(\n      const gsl::not_null<typename Tag::type*> parent_value,\n      const gsl::not_null<typename Subtag::type*> sub_value) {\n    if (parent_value->has_value()) {\n      auto& tensor = get<::Tags::detail::ErrorImpl<typename Subtag::tag>>(\n          parent_value->value());\n      *sub_value = std::decay_t<decltype(tensor)>{};\n      // Only update the Tensor if the Variables has changed its allocation\n      if constexpr (not is_any_spin_weighted_v<\n                        typename Subtag::tag::type::type>) {\n        if (tensor.begin()->data() != sub_value->value().begin()->data()) {\n          for (auto tensor_component = tensor.begin(),\n                    sub_var_it = sub_value->value().begin();\n               tensor_component != tensor.end();\n               ++tensor_component, ++sub_var_it) {\n            sub_var_it->set_data_ref(make_not_null(&*tensor_component));\n          }\n        }\n      } else {\n        if (tensor.begin()->data().data() !=\n            sub_value->value().begin()->data().data()) {\n          for (auto tensor_component = tensor.begin(),\n                    sub_var_it = sub_value->value().begin();\n               tensor_component != tensor.end();\n               ++tensor_component, ++sub_var_it) {\n            sub_var_it->set_data_ref(make_not_null(&*tensor_component));\n          }\n        }\n      }\n    } else {\n      *sub_value = std::nullopt;\n    }\n  }\n};\n}  // namespace db\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY WaveEquationSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  PlaneWave.cpp\n  RegularSphericalWave.cpp\n  SemidiscretizedDg.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  PlaneWave.hpp\n  RegularSphericalWave.hpp\n  SemidiscretizedDg.hpp\n  Solutions.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Options\n  Serialization\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/SemidiscretizedDg.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarWave::Solutions {\n/// \\brief List of all analytic solutions\ntemplate <size_t Dim>\nusing all_solutions = tmpl::append<\n    tmpl::list<PlaneWave<Dim>>,\n    tmpl::conditional_t<Dim == 1, tmpl::list<SemidiscretizedDg>, tmpl::list<>>,\n    tmpl::conditional_t<Dim == 3, tmpl::list<RegularSphericalWave>,\n                        tmpl::list<>>>;\n}  // namespace ScalarWave::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n\n#include <algorithm>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace ScalarWave::Solutions {\n\ntemplate <size_t Dim>\nPlaneWave<Dim>::PlaneWave(\n    std::array<double, Dim> wave_vector, std::array<double, Dim> center,\n    std::unique_ptr<MathFunction<1, Frame::Inertial>> profile)\n    : wave_vector_(std::move(wave_vector)),\n      center_(std::move(center)),\n      profile_(std::move(profile)),\n      omega_(magnitude(wave_vector_)) {}\n\ntemplate <size_t Dim>\nPlaneWave<Dim>::PlaneWave(const PlaneWave& other)\n    : evolution::initial_data::InitialData(other),\n      wave_vector_(other.wave_vector_),\n      center_(other.center_),\n      profile_(other.profile_->get_clone()),\n      omega_(magnitude(wave_vector_)) {}\n\ntemplate <size_t Dim>\nPlaneWave<Dim>& PlaneWave<Dim>::operator=(const PlaneWave& other) {\n  wave_vector_ = other.wave_vector_;\n  center_ = other.center_;\n  omega_ = magnitude(wave_vector_);\n  profile_ = other.profile_->get_clone();\n  return *this;\n}\n\ntemplate <size_t Dim>\nstd::unique_ptr<evolution::initial_data::InitialData>\nPlaneWave<Dim>::get_clone() const {\n  return std::make_unique<PlaneWave<Dim>>(*this);\n}\n\ntemplate <size_t Dim>\nPlaneWave<Dim>::PlaneWave(CkMigrateMessage* msg) : InitialData(msg) {}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nScalar<T> PlaneWave<Dim>::psi(const tnsr::I<T, Dim>& x, const double t) const {\n  return Scalar<T>(profile_->operator()(u(x, t)));\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nScalar<T> PlaneWave<Dim>::dpsi_dt(const tnsr::I<T, Dim>& x,\n                                  const double t) const {\n  return Scalar<T>(-omega_ * profile_->first_deriv(u(x, t)));\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::i<T, Dim> PlaneWave<Dim>::dpsi_dx(const tnsr::I<T, Dim>& x,\n                                        const double t) const {\n  auto result = make_with_value<tnsr::i<T, Dim>>(x, 0.0);\n  const auto du = profile_->first_deriv(u(x, t));\n  for (size_t i = 0; i < Dim; ++i) {\n    result.get(i) = gsl::at(wave_vector_, i) * du;\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nScalar<T> PlaneWave<Dim>::d2psi_dt2(const tnsr::I<T, Dim>& x,\n                                    const double t) const {\n  return Scalar<T>(square(omega_) * profile_->second_deriv(u(x, t)));\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::i<T, Dim> PlaneWave<Dim>::d2psi_dtdx(const tnsr::I<T, Dim>& x,\n                                           const double t) const {\n  auto result = make_with_value<tnsr::i<T, Dim>>(x, 0.0);\n  const auto d2u = profile_->second_deriv(u(x, t));\n  for (size_t i = 0; i < Dim; ++i) {\n    result.get(i) = -omega_ * gsl::at(wave_vector_, i) * d2u;\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::ii<T, Dim> PlaneWave<Dim>::d2psi_dxdx(const tnsr::I<T, Dim>& x,\n                                            const double t) const {\n  auto result = make_with_value<tnsr::ii<T, Dim>>(x, 0.0);\n  const auto d2u = profile_->second_deriv(u(x, t));\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      result.get(i, j) =\n          gsl::at(wave_vector_, i) * gsl::at(wave_vector_, j) * d2u;\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntuples::TaggedTuple<Tags::Psi, Tags::Pi, Tags::Phi<Dim>>\nPlaneWave<Dim>::variables(\n    const tnsr::I<DataVector, Dim>& x, double t,\n    const tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>> /*meta*/) const {\n  tuples::TaggedTuple<Tags::Psi, Tags::Pi, Tags::Phi<Dim>> variables{\n      psi(x, t), dpsi_dt(x, t), dpsi_dx(x, t)};\n  get<Tags::Pi>(variables).get() *= -1.0;\n  return variables;\n}\n\ntemplate <size_t Dim>\ntuples::TaggedTuple<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                    ::Tags::dt<Tags::Phi<Dim>>>\nPlaneWave<Dim>::variables(\n    const tnsr::I<DataVector, Dim>& x, double t,\n    const tmpl::list<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                     ::Tags::dt<Tags::Phi<Dim>>> /*meta*/) const {\n  tuples::TaggedTuple<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                      ::Tags::dt<Tags::Phi<Dim>>>\n      dt_variables{dpsi_dt(x, t), d2psi_dt2(x, t), d2psi_dtdx(x, t)};\n  get<::Tags::dt<Tags::Pi>>(dt_variables).get() *= -1.0;\n  return dt_variables;\n}\n\ntemplate <size_t Dim>\nvoid PlaneWave<Dim>::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | wave_vector_;\n  p | center_;\n  p | profile_;\n  p | omega_;\n}\ntemplate <size_t Dim>\nbool operator==(const PlaneWave<Dim>& lhs, const PlaneWave<Dim>& rhs) {\n  return (lhs.wave_vector_ == rhs.wave_vector_) and\n         (lhs.center_ == rhs.center_) and\n         (*(lhs.profile_) == *(rhs.profile_)) and (lhs.omega_ == rhs.omega_);\n}\n\ntemplate <size_t Dim>\nbool operator!=(const PlaneWave<Dim>& lhs, const PlaneWave<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nT PlaneWave<Dim>::u(const tnsr::I<T, Dim>& x, const double t) const {\n  auto result = make_with_value<T>(x, -omega_ * t);\n  for (size_t d = 0; d < Dim; ++d) {\n    result += gsl::at(wave_vector_, d) * (x.get(d) - gsl::at(center_, d));\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID PlaneWave<Dim>::my_PUP_ID = 0;\n}  // namespace ScalarWave::Solutions\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                   \\\n  template class ScalarWave::Solutions::PlaneWave<DIM(data)>;  \\\n  template bool ScalarWave::Solutions::operator==(             \\\n      const ScalarWave::Solutions::PlaneWave<DIM(data)>& lhs,  \\\n      const ScalarWave::Solutions::PlaneWave<DIM(data)>& rhs); \\\n  template bool ScalarWave::Solutions::operator!=(             \\\n      const ScalarWave::Solutions::PlaneWave<DIM(data)>& lhs,  \\\n      const ScalarWave::Solutions::PlaneWave<DIM(data)>& rhs);\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template Scalar<DTYPE(data)>                                         \\\n  ScalarWave::Solutions::PlaneWave<DIM(data)>::psi(                    \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double t) const;       \\\n  template Scalar<DTYPE(data)>                                         \\\n  ScalarWave::Solutions::PlaneWave<DIM(data)>::dpsi_dt(                \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, double t) const;       \\\n  template tnsr::i<DTYPE(data), DIM(data)>                             \\\n  ScalarWave::Solutions::PlaneWave<DIM(data)>::dpsi_dx(                \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double t) const; \\\n  template Scalar<DTYPE(data)>                                         \\\n  ScalarWave::Solutions::PlaneWave<DIM(data)>::d2psi_dt2(              \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double t) const; \\\n  template tnsr::i<DTYPE(data), DIM(data)>                             \\\n  ScalarWave::Solutions::PlaneWave<DIM(data)>::d2psi_dtdx(             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double t) const; \\\n  template tnsr::ii<DTYPE(data), DIM(data)>                            \\\n  ScalarWave::Solutions::PlaneWave<DIM(data)>::d2psi_dxdx(             \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double t) const; \\\n  template DTYPE(data) ScalarWave::Solutions::PlaneWave<DIM(data)>::u( \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x, const double t) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector))\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines WaveEquationSolutions::PlaneWave\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ScalarWave::Tags {\nstruct Psi;\nstruct Pi;\ntemplate <size_t Dim>\nstruct Phi;\n}  // namespace ScalarWave::Tags\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarWave::Solutions {\n/*!\n * \\brief A plane wave solution to the Euclidean wave equation\n *\n * The solution is given by \\f$\\Psi(\\vec{x},t) = F(u(\\vec{x},t))\\f$\n * where the profile \\f$F\\f$ of the plane wave is an arbitrary one-dimensional\n * function of \\f$u = \\vec{k} \\cdot (\\vec{x} - \\vec{x_o}) - \\omega t\\f$\n * with the wave vector \\f$\\vec{k}\\f$, the frequency \\f$\\omega = ||\\vec{k}||\\f$\n * and initial center of the profile \\f$\\vec{x_o}\\f$.\n *\n * \\tparam Dim the spatial dimension of the solution\n */\ntemplate <size_t Dim>\nclass PlaneWave : public evolution::initial_data::InitialData,\n                  public MarkAsAnalyticSolution {\n public:\n  static constexpr size_t volume_dim = Dim;\n  struct WaveVector {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\n        \"The direction of propagation of the wave.\"};\n  };\n\n  struct Center {\n    using type = std::array<double, Dim>;\n    static constexpr Options::String help = {\n        \"The initial center of the profile of the wave.\"};\n  };\n\n  struct Profile {\n    using type = std::unique_ptr<MathFunction<1, Frame::Inertial>>;\n    static constexpr Options::String help = {\"The profile of the wave.\"};\n  };\n\n  using options = tmpl::list<WaveVector, Center, Profile>;\n\n  static constexpr Options::String help = {\n      \"A plane wave solution of the Euclidean wave equation\"};\n  using tags =\n      tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<3>, ::Tags::dt<Tags::Psi>,\n                 ::Tags::dt<Tags::Pi>, ::Tags::dt<Tags::Phi<Dim>>>;\n\n  PlaneWave() = default;\n  PlaneWave(std::array<double, Dim> wave_vector, std::array<double, Dim> center,\n            std::unique_ptr<MathFunction<1, Frame::Inertial>> profile);\n  PlaneWave(const PlaneWave&);\n  PlaneWave& operator=(const PlaneWave&);\n  PlaneWave(PlaneWave&&) = default;\n  PlaneWave& operator=(PlaneWave&&) = default;\n  ~PlaneWave() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit PlaneWave(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(PlaneWave);\n  /// \\endcond\n\n  /// The value of the scalar field\n  template <typename T>\n  Scalar<T> psi(const tnsr::I<T, Dim>& x, double t) const;\n\n  /// The time derivative of the scalar field\n  template <typename T>\n  Scalar<T> dpsi_dt(const tnsr::I<T, Dim>& x, double t) const;\n\n  /// The spatial derivatives of the scalar field\n  template <typename T>\n  tnsr::i<T, Dim> dpsi_dx(const tnsr::I<T, Dim>& x, double t) const;\n\n  /// The second time derivative of the scalar field\n  template <typename T>\n  Scalar<T> d2psi_dt2(const tnsr::I<T, Dim>& x, double t) const;\n\n  /// The second mixed derivatives of the scalar field\n  template <typename T>\n  tnsr::i<T, Dim> d2psi_dtdx(const tnsr::I<T, Dim>& x, double t) const;\n\n  /// The second spatial derivatives of the scalar field\n  template <typename T>\n  tnsr::ii<T, Dim> d2psi_dxdx(const tnsr::I<T, Dim>& x, double t) const;\n\n  /// Retrieve the evolution variables at time `t` and spatial coordinates `x`\n  tuples::TaggedTuple<Tags::Psi, Tags::Pi, Tags::Phi<Dim>> variables(\n      const tnsr::I<DataVector, Dim>& x, double t,\n      tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<Dim>> /*meta*/) const;\n\n  /// Retrieve the time derivative of the evolution variables at time `t` and\n  /// spatial coordinates `x`\n  ///\n  /// \\note This function's expected use case is setting the past time\n  /// derivative values for Adams-Bashforth-like steppers.\n  tuples::TaggedTuple<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                      ::Tags::dt<Tags::Phi<Dim>>>\n  variables(const tnsr::I<DataVector, Dim>& x, double t,\n            tmpl::list<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                       ::Tags::dt<Tags::Phi<Dim>>> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const PlaneWave<LocalDim>& lhs,\n                         const PlaneWave<LocalDim>& rhs);\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator!=(const PlaneWave<LocalDim>& lhs,\n                         const PlaneWave<LocalDim>& rhs);\n\n  template <typename T>\n  T u(const tnsr::I<T, Dim>& x, double t) const;\n\n  std::array<double, Dim> wave_vector_{};\n  std::array<double, Dim> center_{};\n  std::unique_ptr<MathFunction<1, Frame::Inertial>> profile_;\n  double omega_{};\n};\n}  // namespace ScalarWave::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n\nnamespace ScalarWave::Solutions {\n\nRegularSphericalWave::RegularSphericalWave(\n    std::unique_ptr<MathFunction<1, Frame::Inertial>> profile)\n    : profile_(std::move(profile)) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nRegularSphericalWave::get_clone() const {\n  return std::make_unique<RegularSphericalWave>(*this);\n}\n\nRegularSphericalWave::RegularSphericalWave(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nRegularSphericalWave::RegularSphericalWave(const RegularSphericalWave& other)\n    : evolution::initial_data::InitialData(other),\n      profile_(other.profile_->get_clone()) {}\n\nRegularSphericalWave& RegularSphericalWave::operator=(\n    const RegularSphericalWave& other) {\n  profile_ = other.profile_->get_clone();\n  return *this;\n}\n\ntuples::TaggedTuple<Tags::Psi, Tags::Pi, Tags::Phi<3>>\nRegularSphericalWave::variables(\n    const tnsr::I<DataVector, 3>& x, double t,\n    const tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<3>> /*meta*/) const {\n  const DataVector r = get(magnitude(x));\n  // See class documentation for choice of cutoff\n  const double r_cutoff = cbrt(std::numeric_limits<double>::epsilon());\n  Scalar<DataVector> psi{r.size()};\n  Scalar<DataVector> dpsi_dt{r.size()};\n  tnsr::i<DataVector, 3> dpsi_dx{r.size()};\n  for (size_t i = 0; i < r.size(); i++) {\n    // Testing for r=0 here assumes a scale of order unity\n    if (equal_within_roundoff(r[i], 0., r_cutoff, 1.)) {\n      get(psi)[i] = 2. * profile_->first_deriv(-t);\n      get(dpsi_dt)[i] = -2. * profile_->second_deriv(-t);\n      for (size_t d = 0; d < 3; d++) {\n        dpsi_dx.get(d)[i] = 0.;\n      }\n    } else {\n      const auto F_out = profile_->operator()(r[i] - t);\n      const auto F_in = profile_->operator()(-r[i] - t);\n      const auto dF_out = profile_->first_deriv(r[i] - t);\n      const auto dF_in = profile_->first_deriv(-r[i] - t);\n      get(psi)[i] = (F_out - F_in) / r[i];\n      get(dpsi_dt)[i] = (-dF_out + dF_in) / r[i];\n      const double dpsi_dx_isotropic =\n          (dF_out + dF_in - get(psi)[i]) / square(r[i]);\n      for (size_t d = 0; d < 3; d++) {\n        dpsi_dx.get(d)[i] = dpsi_dx_isotropic * x.get(d)[i];\n      }\n    }\n  }\n  tuples::TaggedTuple<Tags::Psi, Tags::Pi, Tags::Phi<3>> variables{\n      std::move(psi), std::move(dpsi_dt), std::move(dpsi_dx)};\n  get<Tags::Pi>(variables).get() *= -1.0;\n  return variables;\n}\n\ntuples::TaggedTuple<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                    ::Tags::dt<Tags::Phi<3>>>\nRegularSphericalWave::variables(\n    const tnsr::I<DataVector, 3>& x, double t,\n    const tmpl::list<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                     ::Tags::dt<Tags::Phi<3>>> /*meta*/) const {\n  const DataVector r = get(magnitude(x));\n  // See class documentation for choice of cutoff\n  const double r_cutoff = cbrt(std::numeric_limits<double>::epsilon());\n  Scalar<DataVector> dpsi_dt{r.size()};\n  Scalar<DataVector> d2psi_dt2{r.size()};\n  tnsr::i<DataVector, 3> d2psi_dtdx{r.size()};\n  for (size_t i = 0; i < r.size(); i++) {\n    // Testing for r=0 here assumes a scale of order unity\n    if (equal_within_roundoff(r[i], 0., r_cutoff, 1.)) {\n      get(dpsi_dt)[i] = -2. * profile_->second_deriv(-t);\n      get(d2psi_dt2)[i] = 2. * profile_->third_deriv(-t);\n      for (size_t d = 0; d < 3; d++) {\n        d2psi_dtdx.get(d)[i] = 0.;\n      }\n    } else {\n      const auto dF_out = profile_->first_deriv(r[i] - t);\n      const auto dF_in = profile_->first_deriv(-r[i] - t);\n      const auto d2F_out = profile_->second_deriv(r[i] - t);\n      const auto d2F_in = profile_->second_deriv(-r[i] - t);\n      get(dpsi_dt)[i] = (-dF_out + dF_in) / r[i];\n      get(d2psi_dt2)[i] = (d2F_out - d2F_in) / r[i];\n      const double d2psi_dtdx_isotropic =\n          -(d2F_out + d2F_in + get(dpsi_dt)[i]) / square(r[i]);\n      for (size_t d = 0; d < 3; d++) {\n        d2psi_dtdx.get(d)[i] = d2psi_dtdx_isotropic * x.get(d)[i];\n      }\n    }\n  }\n  tuples::TaggedTuple<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                      ::Tags::dt<Tags::Phi<3>>>\n      dt_variables{std::move(dpsi_dt), std::move(d2psi_dt2),\n                   std::move(d2psi_dtdx)};\n  get<::Tags::dt<Tags::Pi>>(dt_variables).get() *= -1.0;\n  return dt_variables;\n}\n\nbool operator==(const RegularSphericalWave& lhs,\n                const RegularSphericalWave& rhs) {\n  return *(lhs.profile_) == *(rhs.profile_);\n}\nbool operator!=(const RegularSphericalWave& lhs,\n                const RegularSphericalWave& rhs) {\n  return not(lhs == rhs);\n}\nvoid RegularSphericalWave::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | profile_;\n}\n\nPUP::able::PUP_ID RegularSphericalWave::my_PUP_ID = 0;\n}  // namespace ScalarWave::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines ScalarWave::Solutions::RegularSphericalWave\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ScalarWave::Tags {\nstruct Pi;\nstruct Psi;\ntemplate <size_t Dim>\nstruct Phi;\n}  // namespace ScalarWave::Tags\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarWave::Solutions {\n/*!\n * \\brief A 3D spherical wave solution to the Euclidean wave equation that is\n * regular at the origin\n *\n * The solution is given by \\f$\\Psi(\\vec{x},t) = \\Psi(r,t) =\n * \\frac{F(r-t)-F(-r-t)}{r}\\f$ describing an outgoing and an ingoing wave\n * with profile \\f$F(u)\\f$. For small \\f$r\\f$ the solution is approximated by\n * its Taylor expansion \\f$\\Psi(r,t)=2 F^\\prime(-t) + \\mathcal{O}(r^2)\\f$. The\n * outgoing and ingoing waves meet at the origin (and cancel each other) when\n * \\f$F^\\prime(-t)=0\\f$.\n *\n * The expansion is employed where \\f$r\\f$ lies within the cubic root of the\n * machine epsilon. Inside this radius we expect the error due to the truncation\n * of the Taylor expansion to be smaller than the numerical error made when\n * evaluating the full \\f$\\Psi(r,t)\\f$. This is because the truncation error\n * scales as \\f$r^2\\f$ (since we keep the zeroth order, and the linear order\n * vanishes as all odd orders do) and the numerical error scales as\n * \\f$\\frac{\\epsilon}{r}\\f$, so they are comparable at\n * \\f$r\\propto\\epsilon^\\frac{1}{3}\\f$.\n *\n * \\requires the profile \\f$F(u)\\f$ to have a length scale of order unity so\n * that \"small\" \\f$r\\f$ means \\f$r\\ll 1\\f$. This is without loss of generality\n * because of the scale invariance of the wave equation. The profile could be a\n * Gausssian centered at 0 with width 1, for instance.\n */\nclass RegularSphericalWave : public evolution::initial_data::InitialData,\n                             public MarkAsAnalyticSolution {\n public:\n  static constexpr size_t volume_dim = 3;\n  struct Profile {\n    using type = std::unique_ptr<MathFunction<1, Frame::Inertial>>;\n    static constexpr Options::String help = {\n        \"The radial profile of the spherical wave.\"};\n  };\n\n  using options = tmpl::list<Profile>;\n\n  static constexpr Options::String help = {\n      \"A spherical wave solution of the Euclidean wave equation that is \"\n      \"regular at the origin\"};\n\n  using tags =\n      tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<3>, ::Tags::dt<Tags::Psi>,\n                 ::Tags::dt<Tags::Pi>, ::Tags::dt<Tags::Phi<3>>>;\n\n  RegularSphericalWave() = default;\n  explicit RegularSphericalWave(\n      std::unique_ptr<MathFunction<1, Frame::Inertial>> profile);\n  RegularSphericalWave(const RegularSphericalWave& other);\n  RegularSphericalWave& operator=(const RegularSphericalWave& other);\n  RegularSphericalWave(RegularSphericalWave&&) = default;\n  RegularSphericalWave& operator=(RegularSphericalWave&&) = default;\n  ~RegularSphericalWave() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit RegularSphericalWave(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RegularSphericalWave);\n  /// \\endcond\n\n  tuples::TaggedTuple<Tags::Psi, Tags::Pi, Tags::Phi<3>> variables(\n      const tnsr::I<DataVector, 3>& x, double t,\n      tmpl::list<Tags::Psi, Tags::Pi, Tags::Phi<3>> /*meta*/) const;\n\n  tuples::TaggedTuple<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                      ::Tags::dt<Tags::Phi<3>>>\n  variables(const tnsr::I<DataVector, 3>& x, double t,\n            tmpl::list<::Tags::dt<Tags::Psi>, ::Tags::dt<Tags::Pi>,\n                       ::Tags::dt<Tags::Phi<3>>> /*meta*/) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const RegularSphericalWave& lhs,\n                         const RegularSphericalWave& rhs);\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator!=(const RegularSphericalWave& lhs,\n                         const RegularSphericalWave& rhs);\n  std::unique_ptr<MathFunction<1, Frame::Inertial>> profile_;\n};\n}  // namespace ScalarWave::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/SemidiscretizedDg.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/SemidiscretizedDg.hpp\"\n\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarWave::Solutions {\nSemidiscretizedDg::SemidiscretizedDg(const int harmonic,\n                                     const std::array<double, 4>& amplitudes)\n    : harmonic_(harmonic), amplitudes_(amplitudes) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData>\nSemidiscretizedDg::get_clone() const {\n  return std::make_unique<SemidiscretizedDg>(*this);\n}\n\nSemidiscretizedDg::SemidiscretizedDg(CkMigrateMessage* msg)\n    : InitialData(msg) {}\n\nnamespace {\nstruct Mode {\n  std::complex<double> frequency;\n  ComplexDataVector pi_coefficients{2};\n  ComplexDataVector phi_coefficients{2};\n};\n\nstd::array<Mode, 4> get_modes(const tnsr::I<DataVector, 1>& x,\n                              const int harmonic) {\n  using namespace std::complex_literals;\n\n  if (get<0>(x).size() != 2) {\n    ERROR(\"SemidiscretizedDg solution only supports linear elements.\");\n  }\n\n  const double element_length = get<0>(x)[1] - get<0>(x)[0];\n  // Phase change across an element\n  const double element_phase = harmonic * element_length;\n\n  std::array<Mode, 4> result;\n  {\n    std::complex<double> one_plus_i_omega =\n        sqrt(2.0 * exp(1.0i * element_phase) - 1.0);\n    {\n      Mode& mode = result[0];\n      // Same as\n      //   mode.frequency = 1.0i * (1.0 - one_plus_i_omega) / element_length\n      // but better behaved as one_plus_i_omega -> 1\n      mode.frequency = 4.0 * sin(element_phase) /\n                       ((1.0 + exp(-1.0i * element_phase)) *\n                        (1.0 + one_plus_i_omega) * element_length);\n      mode.pi_coefficients[0] = sqrt(1.0 / one_plus_i_omega);\n      mode.pi_coefficients[1] = 1.0 / mode.pi_coefficients[0];\n      mode.phi_coefficients = -mode.pi_coefficients;\n    }\n    {\n      Mode& mode = result[1];\n      mode.frequency = 1.0i * (1.0 + one_plus_i_omega) / element_length;\n      mode.pi_coefficients[0] = sqrt(1.0 / one_plus_i_omega);\n      mode.pi_coefficients[1] = -1.0 / mode.pi_coefficients[0];\n      mode.phi_coefficients = -mode.pi_coefficients;\n    }\n  }\n  {\n    std::complex<double> one_plus_i_omega =\n        sqrt(2.0 * exp(-1.0i * element_phase) - 1.0);\n    {\n      Mode& mode = result[2];\n      // Same as\n      //   mode.frequency = 1.0i * (1.0 - one_plus_i_omega) / element_length\n      // but better behaved as one_plus_i_omega -> 1\n      mode.frequency = -4.0 * sin(element_phase) /\n                       ((1.0 + exp(1.0i * element_phase)) *\n                        (1.0 + one_plus_i_omega) * element_length);\n      mode.pi_coefficients[0] = sqrt(one_plus_i_omega);\n      mode.pi_coefficients[1] = 1.0 / mode.pi_coefficients[0];\n      mode.phi_coefficients = mode.pi_coefficients;\n    }\n    {\n      Mode& mode = result[3];\n      mode.frequency = 1.0i * (1.0 + one_plus_i_omega) / element_length;\n      mode.pi_coefficients[0] = sqrt(one_plus_i_omega);\n      mode.pi_coefficients[1] = -1.0 / mode.pi_coefficients[0];\n      mode.phi_coefficients = mode.pi_coefficients;\n    }\n  }\n  return result;\n}\n}  // namespace\n\ntuples::TaggedTuple<Tags::Pi> SemidiscretizedDg::variables(\n    const tnsr::I<DataVector, 1>& x, double t,\n    tmpl::list<Tags::Pi> /*meta*/) const {\n  using namespace std::complex_literals;\n\n  const auto modes = get_modes(x, harmonic_);\n  const double spatial_phase = harmonic_ * get<0>(x)[0];\n  DataVector pi{0.0, 0.0};\n  for (size_t i = 0; i < 4; ++i) {\n    const auto& mode = gsl::at(modes, i);\n    pi += gsl::at(amplitudes_, i) *\n          real(exp(1.0i * (spatial_phase + mode.frequency * t)) *\n               mode.pi_coefficients);\n  }\n  return {Scalar<DataVector>(std::move(pi))};\n}\n\ntuples::TaggedTuple<Tags::Phi<1>> SemidiscretizedDg::variables(\n    const tnsr::I<DataVector, 1>& x, double t,\n    tmpl::list<Tags::Phi<1>> /*meta*/) const {\n  using namespace std::complex_literals;\n\n  const auto modes = get_modes(x, harmonic_);\n  const double spatial_phase = harmonic_ * get<0>(x)[0];\n  DataVector phi{0.0, 0.0};\n  for (size_t i = 0; i < 4; ++i) {\n    const auto& mode = gsl::at(modes, i);\n    phi += gsl::at(amplitudes_, i) *\n           real(exp(1.0i * (spatial_phase + mode.frequency * t)) *\n                mode.phi_coefficients);\n  }\n  return {tnsr::i<DataVector, 1>{{{std::move(phi)}}}};\n}\n\ntuples::TaggedTuple<Tags::Psi> SemidiscretizedDg::variables(\n    const tnsr::I<DataVector, 1>& x, double t,\n    tmpl::list<Tags::Psi> /*meta*/) const {\n  using namespace std::complex_literals;\n\n  // There are two more modes that are just constant offsets of Psi,\n  // but they are boring so we ignore them.\n  const auto modes = get_modes(x, harmonic_);\n  const double spatial_phase = harmonic_ * get<0>(x)[0];\n  DataVector psi{0.0, 0.0};\n  for (size_t i = 0; i < 4; ++i) {\n    const auto& mode = gsl::at(modes, i);\n    if (mode.frequency == 0.0) {\n      psi -= gsl::at(amplitudes_, i) *\n             real(t * exp(1.0i * spatial_phase) * mode.pi_coefficients);\n    } else {\n      psi -= gsl::at(amplitudes_, i) *\n             real(exp(1.0i * (spatial_phase + mode.frequency * t)) /\n                  (1.0i * mode.frequency) * mode.pi_coefficients);\n    }\n  }\n  return {Scalar<DataVector>(std::move(psi))};\n}\n\nvoid SemidiscretizedDg::pup(PUP::er& p) {\n  InitialData::pup(p);\n  p | harmonic_;\n  p | amplitudes_;\n}\n\nPUP::able::PUP_ID SemidiscretizedDg::my_PUP_ID = 0;\n}  // namespace ScalarWave::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/SemidiscretizedDg.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarWave::Solutions {\n/*!\n * \\brief An exact solution to the semidiscretized DG ScalarWave\n * system with an upwind flux\n *\n * This solution takes into account the spatial discretization error,\n * and so should show convergence in time integration accuracy to\n * roundoff at any resolution.\n *\n * \\warning This is not really a pointwise function, as the solution\n * depends on the spatial discretization.  It will only work on a\n * periodic domain of length \\f$2 \\pi\\f$ (or an integer multiple) with\n * equally sized linear elements.\n */\nclass SemidiscretizedDg : public evolution::initial_data::InitialData,\n                          public MarkAsAnalyticSolution {\n public:\n  using tags = tmpl::list<Tags::Pi, Tags::Phi<1>, Tags::Psi>;\n\n  struct Harmonic {\n    using type = int;\n    static constexpr Options::String help =\n        \"Number of wave periods across the domain\";\n  };\n\n  struct Amplitudes {\n    using type = std::array<double, 4>;\n    static constexpr Options::String help =\n        \"Amplitudes of the independent modes of the harmonic\";\n  };\n\n  using options = tmpl::list<Harmonic, Amplitudes>;\n\n  static constexpr Options::String help =\n      \"A solution of the semidiscretized DG system on linear elements\\n\"\n      \"with spatial period 2 pi.\";\n\n  SemidiscretizedDg(int harmonic, const std::array<double, 4>& amplitudes);\n\n  SemidiscretizedDg() = default;\n  ~SemidiscretizedDg() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override;\n\n  /// \\cond\n  explicit SemidiscretizedDg(CkMigrateMessage* msg);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SemidiscretizedDg);\n  /// \\endcond\n\n  /// Retrieve the evolution variables at time `t` and spatial coordinates `x`\n  template <typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataVector, 1>& x,\n                                         double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(\n        tmpl2::flat_all_v<tmpl::list_contains_v<tags, Tags>...>,\n        \"At least one of the requested tags is not supported. The requested \"\n        \"tags are listed as template parameters of the `variables` function.\");\n    return {get<Tags>(variables(x, t, tmpl::list<Tags>{}))...};\n  }\n\n  /// \\cond\n  tuples::TaggedTuple<Tags::Pi> variables(const tnsr::I<DataVector, 1>& x,\n                                          double t,\n                                          tmpl::list<Tags::Pi> /*meta*/) const;\n\n  tuples::TaggedTuple<Tags::Phi<1>> variables(\n      const tnsr::I<DataVector, 1>& x, double t,\n      tmpl::list<Tags::Phi<1>> /*meta*/) const;\n\n  tuples::TaggedTuple<Tags::Psi> variables(\n      const tnsr::I<DataVector, 1>& x, double t,\n      tmpl::list<Tags::Psi> /*meta*/) const;\n  /// \\endcond\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  int harmonic_{std::numeric_limits<int>::max()};\n  std::array<double, 4> amplitudes_{\n      {std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN(),\n       std::numeric_limits<double>::signaling_NaN()}};\n};\n}  // namespace ScalarWave::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/WaveEquation/Solutions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines ScalarWave::Solutions\n\n#pragma once\n\nnamespace ScalarWave {\n/*!\n * \\ingroup AnalyticSolutionsGroup\n * \\brief Holds classes implementing a solution to the Euclidean wave equation\n * \\f$0 = \\frac{\\partial^2 \\Psi}{\\partial t^2} - \\nabla^2 \\Psi\\f$.\n */\nnamespace Solutions {}\n}  // namespace ScalarWave\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY XctsSolutions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ConstantDensityStar.cpp\n  Flatness.cpp\n  Schwarzschild.cpp\n  TovStar.cpp\n  WrappedGr.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CommonVariables.hpp\n  CommonVariables.tpp\n  ConstantDensityStar.hpp\n  Factory.hpp\n  Flatness.hpp\n  Schwarzschild.hpp\n  TovStar.hpp\n  WrappedGr.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Simd\n  RootFinding\n  PUBLIC\n  DataStructures\n  ElasticityPointwiseFunctions\n  ErrorHandling\n  GeneralRelativity\n  GeneralRelativitySolutions\n  GrMhdAnalyticData\n  InitialDataUtilities\n  Options\n  RelativisticEulerSolutions\n  Serialization\n  Utilities\n  Xcts\n  XctsAnalyticData\n  XctsPointwiseFunctions\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/CommonVariables.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts::Solutions {\n\n/// Tags for variables that solutions can share\ntemplate <typename DataType>\nusing common_tags = tmpl::push_back<\n    AnalyticData::common_tags<DataType>,\n    // Solved variables\n    Tags::ConformalFactorMinusOne<DataType>, Tags::ConformalFactor<DataType>,\n    Tags::LapseTimesConformalFactorMinusOne<DataType>,\n    Tags::LapseTimesConformalFactor<DataType>,\n    Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n    // ADM variables\n    gr::Tags::Lapse<DataType>, gr::Tags::Shift<DataType, 3>,\n    gr::Tags::SpatialMetric<DataType, 3>,\n    gr::Tags::InverseSpatialMetric<DataType, 3>,\n    ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    gr::Tags::ExtrinsicCurvature<DataType, 3>,\n    // Derivatives of solved variables\n    ::Tags::deriv<Tags::ConformalFactorMinusOne<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    ::Tags::deriv<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    ::Tags::deriv<Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    Tags::ShiftStrain<DataType, 3, Frame::Inertial>,\n    // Fluxes\n    ::Tags::Flux<Tags::ConformalFactorMinusOne<DataType>, tmpl::size_t<3>,\n                 Frame::Inertial>,\n    ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                 tmpl::size_t<3>, Frame::Inertial>,\n    Tags::LongitudinalShiftExcess<DataType, 3, Frame::Inertial>,\n    // Background quantities for subsets of the XCTS equations\n    Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataType>,\n    Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<DataType>,\n    Tags::ShiftDotDerivExtrinsicCurvatureTrace<DataType>>;\n\n/// Tags for hydro variables that are typically retrieved from a hydro solution\ntemplate <typename DataType>\nusing hydro_tags = AnalyticData::hydro_tags<DataType>;\n\n/// Implementations for variables that solutions can share\ntemplate <typename DataType, typename Cache>\nstruct CommonVariables : AnalyticData::CommonVariables<DataType, Cache> {\n  static constexpr size_t Dim = 3;\n  using Base = AnalyticData::CommonVariables<DataType, Cache>;\n  using Base::Base;\n  using Base::operator();\n\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*> conformal_factor_minus_one,\n      gsl::not_null<Cache*> cache,\n      Tags::ConformalFactorMinusOne<DataType> /*meta*/) const = 0;\n  virtual void operator()(gsl::not_null<Scalar<DataType>*> conformal_factor,\n                          gsl::not_null<Cache*> cache,\n                          Tags::ConformalFactor<DataType> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<Cache*> cache,\n      Tags::LapseTimesConformalFactorMinusOne<DataType> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      Tags::LapseTimesConformalFactor<DataType> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::I<DataType, Dim>*> shift_excess,\n      gsl::not_null<Cache*> cache,\n      Tags::ShiftExcess<DataType, Dim, Frame::Inertial> /*meta*/) const = 0;\n  virtual void operator()(gsl::not_null<Scalar<DataType>*> lapse,\n                          gsl::not_null<Cache*> cache,\n                          gr::Tags::Lapse<DataType> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<tnsr::ii<DataType, Dim>*> spatial_metric,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::SpatialMetric<DataType, Dim> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::II<DataType, Dim>*> inv_spatial_metric,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::InverseSpatialMetric<DataType, Dim> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_spatial_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>, tmpl::size_t<Dim>,\n                    Frame::Inertial> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::i<DataType, Dim>*> deriv_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ConformalFactorMinusOne<DataType>, tmpl::size_t<Dim>,\n                    Frame::Inertial> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<tnsr::i<DataType, Dim>*> deriv_lapse_times_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                    tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<tnsr::iJ<DataType, Dim>*> deriv_shift_excess,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<tnsr::ii<DataType, Dim>*> shift_strain,\n      gsl::not_null<Cache*> cache,\n      Tags::ShiftStrain<DataType, Dim, Frame::Inertial> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::I<DataType, Dim>*> conformal_factor_flux,\n      gsl::not_null<Cache*> cache,\n      ::Tags::Flux<Tags::ConformalFactorMinusOne<DataType>, tmpl::size_t<Dim>,\n                   Frame::Inertial> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::I<DataType, Dim>*> lapse_times_conformal_factor_flux,\n      gsl::not_null<Cache*> cache,\n      ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                   tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::II<DataType, Dim>*> longitudinal_shift_excess,\n      gsl::not_null<Cache*> cache,\n      Tags::LongitudinalShiftExcess<DataType, Dim, Frame::Inertial> /*meta*/)\n      const;\n  virtual void operator()(gsl::not_null<tnsr::I<DataType, Dim>*> shift,\n                          gsl::not_null<Cache*> cache,\n                          gr::Tags::Shift<DataType, Dim> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<tnsr::ii<DataType, Dim>*> extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::ExtrinsicCurvature<DataType, Dim> /*meta*/) const = 0;\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*>\n          longitudinal_shift_minus_dt_conformal_metric_square,\n      gsl::not_null<Cache*> cache,\n      Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataType> /*meta*/)\n      const;\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*>\n          longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n      gsl::not_null<Cache*> cache,\n      Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<\n          DataType> /*meta*/) const;\n  virtual void operator()(\n      gsl::not_null<Scalar<DataType>*>\n          shift_dot_deriv_extrinsic_curvature_trace,\n      gsl::not_null<Cache*> cache,\n      Tags::ShiftDotDerivExtrinsicCurvatureTrace<DataType> /*meta*/) const;\n};\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/CommonVariables.tpp\"\n#include \"PointwiseFunctions/Elasticity/Strain.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Xcts/LongitudinalOperator.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts::Solutions {\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    Tags::ConformalFactor<DataType> /*meta*/) const {\n  const auto& conformal_factor_minus_one =\n      cache->get_var(*this, Tags::ConformalFactorMinusOne<DataType>{});\n  get(*conformal_factor) = get(conformal_factor_minus_one) + 1.;\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    Tags::LapseTimesConformalFactor<DataType> /*meta*/) const {\n  const auto& lapse_times_conformal_factor_minus_one = cache->get_var(\n      *this, Tags::LapseTimesConformalFactorMinusOne<DataType>{});\n  get(*lapse_times_conformal_factor) =\n      get(lapse_times_conformal_factor_minus_one) + 1.;\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> conformal_factor_flux,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::Flux<Tags::ConformalFactorMinusOne<DataType>, tmpl::size_t<Dim>,\n                 Frame::Inertial> /*meta*/) const {\n  const auto& conformal_factor_gradient = cache->get_var(\n      *this, ::Tags::deriv<Tags::ConformalFactorMinusOne<DataType>,\n                           tmpl::size_t<Dim>, Frame::Inertial>{});\n  const auto& inv_conformal_metric = cache->get_var(\n      *this, Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial>{});\n  raise_or_lower_index(conformal_factor_flux, conformal_factor_gradient,\n                       inv_conformal_metric);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, Dim>*> shift_strain,\n    const gsl::not_null<Cache*> cache,\n    Tags::ShiftStrain<DataType, Dim, Frame::Inertial> /*meta*/) const {\n  const auto& shift_excess = cache->get_var(\n      *this, Tags::ShiftExcess<DataType, Dim, Frame::Inertial>{});\n  const auto& deriv_shift_excess = cache->get_var(\n      *this, ::Tags::deriv<Tags::ShiftExcess<DataType, Dim, Frame::Inertial>,\n                           tmpl::size_t<Dim>, Frame::Inertial>{});\n  const auto& conformal_metric = cache->get_var(\n      *this, Xcts::Tags::ConformalMetric<DataType, Dim, Frame::Inertial>{});\n  const auto& deriv_conformal_metric = cache->get_var(\n      *this,\n      ::Tags::deriv<Xcts::Tags::ConformalMetric<DataType, Dim, Frame::Inertial>,\n                    tmpl::size_t<Dim>, Frame::Inertial>{});\n  const auto& conformal_christoffel_first_kind = cache->get_var(\n      *this, Xcts::Tags::ConformalChristoffelFirstKind<DataType, Dim,\n                                                       Frame::Inertial>{});\n  Elasticity::strain(shift_strain, deriv_shift_excess, conformal_metric,\n                     deriv_conformal_metric, conformal_christoffel_first_kind,\n                     shift_excess);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*>\n        lapse_times_conformal_factor_flux,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                 tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const {\n  const auto& lapse_times_conformal_factor_gradient = cache->get_var(\n      *this, ::Tags::deriv<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                           tmpl::size_t<Dim>, Frame::Inertial>{});\n  const auto& inv_conformal_metric = cache->get_var(\n      *this, Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial>{});\n  raise_or_lower_index(lapse_times_conformal_factor_flux,\n                       lapse_times_conformal_factor_gradient,\n                       inv_conformal_metric);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::II<DataType, Dim>*> longitudinal_shift_excess,\n    const gsl::not_null<Cache*> cache,\n    Tags::LongitudinalShiftExcess<DataType, Dim, Frame::Inertial> /*meta*/)\n    const {\n  const auto& shift_excess = cache->get_var(\n      *this, Tags::ShiftExcess<DataType, Dim, Frame::Inertial>{});\n  const auto& deriv_shift_excess = cache->get_var(\n      *this, ::Tags::deriv<Tags::ShiftExcess<DataType, Dim, Frame::Inertial>,\n                           tmpl::size_t<Dim>, Frame::Inertial>{});\n  const auto& inv_conformal_metric = cache->get_var(\n      *this, Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial>{});\n  const auto& conformal_christoffel_second_kind = cache->get_var(\n      *this,\n      Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame::Inertial>{});\n  Xcts::longitudinal_operator(longitudinal_shift_excess, shift_excess,\n                              deriv_shift_excess, inv_conformal_metric,\n                              conformal_christoffel_second_kind);\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> shift,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::Shift<DataType, Dim> /*meta*/) const {\n  *shift = cache->get_var(*this,\n                          Tags::ShiftExcess<DataType, Dim, Frame::Inertial>{});\n  const auto& shift_background = cache->get_var(\n      *this, Tags::ShiftBackground<DataType, Dim, Frame::Inertial>{});\n  for (size_t d = 0; d < Dim; ++d) {\n    shift->get(d) += shift_background.get(d);\n  }\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, Dim>*> spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::SpatialMetric<DataType, Dim> /*meta*/) const {\n  *spatial_metric = cache->get_var(\n      *this, Tags::ConformalMetric<DataType, Dim, Frame::Inertial>{});\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataType>{});\n  for (size_t i = 0; i < spatial_metric->size(); ++i) {\n    (*spatial_metric)[i] *= pow<4>(get(conformal_factor));\n  }\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::II<DataType, Dim>*> inv_spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::InverseSpatialMetric<DataType, Dim> /*meta*/)\n    const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataType>{});\n  *inv_spatial_metric = cache->get_var(\n      *this, Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial>{});\n  for (size_t i = 0; i < inv_spatial_metric->size(); ++i) {\n    (*inv_spatial_metric)[i] /= pow<4>(get(conformal_factor));\n  }\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                  tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const {\n  const auto& conformal_metric = cache->get_var(\n      *this, Tags::ConformalMetric<DataType, Dim, Frame::Inertial>{});\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataType>{});\n  const auto& deriv_conformal_factor = cache->get_var(\n      *this, ::Tags::deriv<Tags::ConformalFactorMinusOne<DataType>,\n                           tmpl::size_t<Dim>, Frame::Inertial>{});\n  *deriv_spatial_metric = cache->get_var(\n      *this,\n      ::Tags::deriv<Tags::ConformalMetric<DataType, Dim, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial>{});\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k <= j; ++k) {\n        deriv_spatial_metric->get(i, j, k) *= pow<4>(get(conformal_factor));\n        deriv_spatial_metric->get(i, j, k) +=\n            4. * pow<3>(get(conformal_factor)) * deriv_conformal_factor.get(i) *\n            conformal_metric.get(j, k);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<Scalar<DataType>*>\n        longitudinal_shift_minus_dt_conformal_metric_square,\n    const gsl::not_null<Cache*> cache,\n    Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataType> /*meta*/)\n    const {\n  const auto& longitudinal_shift_background = cache->get_var(\n      *this, Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n                 DataType, Dim, Frame::Inertial>{});\n  const auto& longitudinal_shift_excess = cache->get_var(\n      *this, Tags::LongitudinalShiftExcess<DataType, Dim, Frame::Inertial>{});\n  const auto& conformal_metric = cache->get_var(\n      *this, Tags::ConformalMetric<DataType, Dim, Frame::Inertial>{});\n  get(*longitudinal_shift_minus_dt_conformal_metric_square) = 0.;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t l = 0; l < 3; ++l) {\n          get(*longitudinal_shift_minus_dt_conformal_metric_square) +=\n              conformal_metric.get(i, k) * conformal_metric.get(j, l) *\n              (longitudinal_shift_background.get(i, j) +\n               longitudinal_shift_excess.get(i, j)) *\n              (longitudinal_shift_background.get(k, l) +\n               longitudinal_shift_excess.get(k, l));\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<Scalar<DataType>*>\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const gsl::not_null<Cache*> cache,\n    Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<\n        DataType> /*meta*/) const {\n  *longitudinal_shift_minus_dt_conformal_metric_over_lapse_square =\n      cache->get_var(\n          *this,\n          Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataType>{});\n  const auto& lapse = cache->get_var(*this, gr::Tags::Lapse<DataType>{});\n  get(*longitudinal_shift_minus_dt_conformal_metric_over_lapse_square) /=\n      square(get(lapse));\n}\n\ntemplate <typename DataType, typename Cache>\nvoid CommonVariables<DataType, Cache>::operator()(\n    const gsl::not_null<Scalar<DataType>*>\n        shift_dot_deriv_extrinsic_curvature_trace,\n    const gsl::not_null<Cache*> cache,\n    Tags::ShiftDotDerivExtrinsicCurvatureTrace<DataType> /*meta*/) const {\n  const auto& shift =\n      cache->get_var(*this, gr::Tags::Shift<DataType, Dim>{});\n  const auto& deriv_extrinsic_curvature_trace = cache->get_var(\n      *this, ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>,\n                           tmpl::size_t<Dim>, Frame::Inertial>{});\n  dot_product(shift_dot_deriv_extrinsic_curvature_trace, shift,\n              deriv_extrinsic_curvature_trace);\n}\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/ConstantDensityStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/ConstantDensityStar.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\n// Find the alpha parameter that corresponds to the weak-field solution,\n// since this is the solution we get when we set \\psi = 1 initially\ndouble compute_alpha(const double density, const double radius) {\n  const double alpha_source = sqrt(2. * M_PI * density / 3.) * radius;\n  return RootFinder::toms748(\n      [alpha_source](const double a) {\n        const double a_square = pow<2>(a);\n        const double pow_2_one_plus_a_square = pow<2>(1. + a_square);\n        return alpha_source - a_square * pow<3>(a) /\n                                  (pow_2_one_plus_a_square * (1. + a_square));\n      },\n      sqrt(5.), 1.0 / alpha_source,\n      // Choose a precision of 14 base-10 digits for no particular reason\n      1.0e-14, 1.0e-15);\n}\n\ntemplate <typename DataType>\nScalar<DataType> compute_piecewise(const Scalar<DataType>& r, double radius,\n                                   double inner_value, double outer_value);\ntemplate <>\nScalar<double> compute_piecewise(const Scalar<double>& r, const double radius,\n                                 const double inner_value,\n                                 const double outer_value) {\n  return Scalar<double>(get(r) < radius ? inner_value : outer_value);\n}\ntemplate <>\nScalar<DataVector> compute_piecewise(const Scalar<DataVector>& r,\n                                     const double radius,\n                                     const double inner_value,\n                                     const double outer_value) {\n  return Scalar<DataVector>(inner_value - (inner_value - outer_value) *\n                                              step_function(get(r) - radius));\n}\n\n}  // namespace\n\nnamespace Xcts::Solutions {\n\nConstantDensityStar::ConstantDensityStar(const double density,\n                                         const double radius,\n                                         const Options::Context& context)\n    : density_(density), radius_(radius) {\n  const double critical_density =\n      3. * pow<5>(5.) / (2. * pow<6>(6.) * M_PI * pow<2>(radius));\n  if (density <= critical_density) {\n    alpha_ = compute_alpha(density, radius);\n  } else {\n    PARSE_ERROR(context,\n                \"A ConstantDensityStar has no solutions for a density below \"\n                \"the critical density (\"\n                    << critical_density << \").\");\n  }\n}\n\nvoid ConstantDensityStar::pup(PUP::er& p) {\n  elliptic::analytic_data::AnalyticSolution::pup(p);\n  p | density_;\n  p | radius_;\n  if (p.isUnpacking()) {\n    alpha_ = compute_alpha(density_, radius_);\n  }\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<Xcts::Tags::ConformalFactor<DataType>>\nConstantDensityStar::variables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& x,\n    tmpl::list<Xcts::Tags::ConformalFactor<DataType>> /*meta*/) const {\n  const DataType r = get(magnitude(x));\n  const double inner_prefactor =\n      sqrt(alpha_ * radius_) / std::pow(2. * M_PI * density_ / 3., 0.25);\n  const double alpha_times_radius_square = square(alpha_ * radius_);\n  const double beta = inner_prefactor / sqrt(1. + square(alpha_)) - radius_;\n  auto conformal_factor = make_with_value<Scalar<DataType>>(r, 0.);\n  for (size_t i = 0; i < get_size(r); i++) {\n    if (get_element(r, i) <= radius_) {\n      get_element(get(conformal_factor), i) =\n          inner_prefactor /\n          sqrt(square(get_element(r, i)) + alpha_times_radius_square);\n    } else {\n      get_element(get(conformal_factor), i) = beta / get_element(r, i) + 1.;\n    }\n  }\n  return {std::move(conformal_factor)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::Initial<Xcts::Tags::ConformalFactor<DataType>>>\nConstantDensityStar::variables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& x,\n    tmpl::list<::Tags::Initial<Xcts::Tags::ConformalFactor<DataType>>> /*meta*/)\n    const {\n  return {make_with_value<Scalar<DataType>>(x, 1.)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::Initial<::Tags::deriv<\n    Xcts::Tags::ConformalFactor<DataType>, tmpl::size_t<3>, Frame::Inertial>>>\nConstantDensityStar::variables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& x,\n    tmpl::list<::Tags::Initial<\n        ::Tags::deriv<Xcts::Tags::ConformalFactor<DataType>, tmpl::size_t<3>,\n                      Frame::Inertial>>> /*meta*/) const {\n  return {make_with_value<tnsr::i<DataType, 3, Frame::Inertial>>(x, 0.)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<::Tags::FixedSource<Xcts::Tags::ConformalFactor<DataType>>>\nConstantDensityStar::variables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& x,\n    tmpl::list<\n        ::Tags::FixedSource<Xcts::Tags::ConformalFactor<DataType>>> /*meta*/)\n    const {\n  return {make_with_value<Scalar<DataType>>(x, 0.)};\n}\n\ntemplate <typename DataType>\ntuples::TaggedTuple<gr::Tags::EnergyDensity<DataType>>\nConstantDensityStar::variables(\n    const tnsr::I<DataType, 3, Frame::Inertial>& x,\n    tmpl::list<gr::Tags::EnergyDensity<DataType>> /*meta*/) const {\n  return {compute_piecewise(magnitude(x), radius_, density_, 0.)};\n}\n\nPUP::able::PUP_ID ConstantDensityStar::my_PUP_ID = 0;  // NOLINT\n\nbool operator==(const ConstantDensityStar& lhs,\n                const ConstantDensityStar& rhs) {\n  return lhs.density() == rhs.density() and lhs.radius() == rhs.radius();\n}\n\nbool operator!=(const ConstantDensityStar& lhs,\n                const ConstantDensityStar& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template tuples::TaggedTuple<Xcts::Tags::ConformalFactor<DTYPE(data)>>      \\\n  ConstantDensityStar::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3, Frame::Inertial>&,                        \\\n      tmpl::list<Xcts::Tags::ConformalFactor<DTYPE(data)>>) const;            \\\n  template tuples::TaggedTuple<                                               \\\n      ::Tags::Initial<Xcts::Tags::ConformalFactor<DTYPE(data)>>>              \\\n  ConstantDensityStar::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3, Frame::Inertial>&,                        \\\n      tmpl::list<::Tags::Initial<Xcts::Tags::ConformalFactor<DTYPE(data)>>>)  \\\n      const;                                                                  \\\n  template tuples::TaggedTuple<                                               \\\n      ::Tags::Initial<::Tags::deriv<Xcts::Tags::ConformalFactor<DTYPE(data)>, \\\n                                    tmpl::size_t<3>, Frame::Inertial>>>       \\\n  ConstantDensityStar::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3, Frame::Inertial>&,                        \\\n      tmpl::list<::Tags::Initial<                                             \\\n          ::Tags::deriv<Xcts::Tags::ConformalFactor<DTYPE(data)>,             \\\n                        tmpl::size_t<3>, Frame::Inertial>>>) const;           \\\n  template tuples::TaggedTuple<                                               \\\n      ::Tags::FixedSource<Xcts::Tags::ConformalFactor<DTYPE(data)>>>          \\\n  ConstantDensityStar::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3, Frame::Inertial>&,                        \\\n      tmpl::list<                                                             \\\n          ::Tags::FixedSource<Xcts::Tags::ConformalFactor<DTYPE(data)>>>)     \\\n      const;                                                                  \\\n  template tuples::TaggedTuple<gr::Tags::EnergyDensity<DTYPE(data)>>          \\\n  ConstantDensityStar::variables(                                             \\\n      const tnsr::I<DTYPE(data), 3, Frame::Inertial>&,                        \\\n      tmpl::list<gr::Tags::EnergyDensity<DTYPE(data)>>) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/ConstantDensityStar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Xcts::Solutions {\n\n/*!\n * \\brief A constant density star in general relativity\n *\n * \\details This solution describes a star with constant density \\f$\\rho_0\\f$\n * that extends to a (conformal) radius \\f$R\\f$. It solves the XCTS Hamiltonian\n * constraint that reduces to the non-linear elliptic equation\n * \\f[\n * \\Delta^2\\psi+2\\pi\\rho\\psi^5=0\n * \\f]\n * for the conformal factor \\f$\\psi\\f$ (see `Xcts`) under the following\n * assumptions \\cite Baumgarte2006ug :\n *\n * - Time-symmetry \\f$K_{ij}=0\\f$\n * - Conformal flatness \\f$\\overline{\\gamma}=\\delta\\f$, so \\f$\\Delta\\f$ is the\n * flat-space Laplacian\n * - Spherical symmetry\n *\n * Imposing boundary conditions\n * \\f[\n * \\frac{\\partial\\psi}{\\partial r}=0 \\quad \\text{for} \\quad r=0\\\\\n * \\psi\\rightarrow 1 \\quad \\text{for} \\quad r\\rightarrow\\infty\n * \\f]\n * and considering the energy density\n * \\f[\n * \\rho(r\\leq R)=\\rho_0 \\quad \\text{and} \\quad \\rho(r>R)=0\n * \\f]\n * of the star the authors of \\cite Baumgarte2006ug find the solution\n * \\f[\n * \\psi(r\\leq R)=C u_\\alpha(r) \\quad \\text{and}\n * \\quad \\psi(r>R)=\\frac{\\beta}{r} + 1\n * \\f]\n * with \\f$C=(2\\pi\\rho_0/3)^{-1/4}\\f$, the Sobolev functions\n * \\f[\n * u_\\alpha(r)=\\sqrt{\\frac{\\alpha R}{r^2+(\\alpha R)^2}}\n * \\f]\n * and real parameters \\f$\\alpha\\f$ and \\f$\\beta\\f$ that are determined by\n * the following relations:\n * \\f[\n * \\rho_0 R^2=\\frac{3}{2\\pi}f^2(\\alpha) \\quad \\text{with}\n * \\quad f(\\alpha)=\\frac{\\alpha^5}{(1+\\alpha^2)^3} \\\\\n * \\frac{\\beta}{R} + 1 = C u_\\alpha(R)\n * \\f]\n *\n * This solution is described in detail in \\cite Baumgarte2006ug , and also in\n * Exercise 3.8 in \\cite BaumgarteShapiro , since it\n * exhibits the non-uniqueness properties that are typical for the XCTS system.\n * In the simple case of the constant-density star the non-uniqueness is\n * apparent from the function \\f$f(\\alpha)\\f$, which has two solutions for any\n * \\f$\\rho_0\\f$ smaller than a critical density\n * \\f[\n * \\rho_\\mathrm{crit}=\\frac{3}{2\\pi R^2}\\frac{5^2}{6^6}\n * \\approx\\frac{0.0320}{R^2} \\text{,}\n * \\f]\n * a unique solution for \\f$\\rho_0=\\rho_\\mathrm{crit}\\f$ and no solutions\n * above the critical density \\cite Baumgarte2006ug . The authors identify the\n * \\f$\\alpha < \\alpha_\\mathrm{crit}=\\sqrt{5}\\f$ and \\f$\\alpha >\n * \\alpha_\\mathrm{crit}\\f$ branches of solutions with the strong-field and\n * weak-field regimes, respectively (see \\cite Baumgarte2006ug for details).\n * In this implementation we compute the weak-field solution by choosing the\n * \\f$\\alpha > \\alpha_\\mathrm{crit}\\f$ that corresponds to the density\n * \\f$\\rho_0\\f$ of the star. Therefore, we supply initial data\n * \\f$\\psi_\\mathrm{init}=1\\f$ so that a nonlinear iterative numerical solver\n * will converge to the same weak-field solution.\n */\nclass ConstantDensityStar : public elliptic::analytic_data::AnalyticSolution {\n public:\n  struct Density {\n    using type = double;\n    static constexpr Options::String help{\n        \"The constant density within the star\"};\n    static double lower_bound() { return 0.; }\n  };\n  struct Radius {\n    using type = double;\n    static constexpr Options::String help{\"The conformal radius of the star\"};\n    static double lower_bound() { return 0.; }\n  };\n\n  using options = tmpl::list<Density, Radius>;\n  static constexpr Options::String help{\n      \"A constant density star in general relativity\"};\n\n  ConstantDensityStar() = default;\n  ConstantDensityStar(const ConstantDensityStar&) = default;\n  ConstantDensityStar& operator=(const ConstantDensityStar&) = default;\n  ConstantDensityStar(ConstantDensityStar&&) = default;\n  ConstantDensityStar& operator=(ConstantDensityStar&&) = default;\n  ~ConstantDensityStar() = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<ConstantDensityStar>(*this);\n  }\n\n  /// \\cond\n  explicit ConstantDensityStar(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ConstantDensityStar);\n  /// \\endcond\n\n  ConstantDensityStar(double density, double radius,\n                      const Options::Context& context = {});\n\n  double density() const { return density_; }\n  double radius() const { return radius_; }\n\n  /// @{\n  /// Retrieve variable at coordinates `x`\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3, Frame::Inertial>& x,\n                 tmpl::list<Xcts::Tags::ConformalFactor<DataType>> /*meta*/)\n      const -> tuples::TaggedTuple<Xcts::Tags::ConformalFactor<DataType>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3, Frame::Inertial>& x,\n                 tmpl::list<::Tags::Initial<\n                     Xcts::Tags::ConformalFactor<DataType>>> /*meta*/) const\n      -> tuples::TaggedTuple<\n          ::Tags::Initial<Xcts::Tags::ConformalFactor<DataType>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3, Frame::Inertial>& x,\n                 tmpl::list<::Tags::Initial<\n                     ::Tags::deriv<Xcts::Tags::ConformalFactor<DataType>,\n                                   tmpl::size_t<3>, Frame::Inertial>>> /*meta*/)\n      const -> tuples::TaggedTuple<\n          ::Tags::Initial<::Tags::deriv<Xcts::Tags::ConformalFactor<DataType>,\n                                        tmpl::size_t<3>, Frame::Inertial>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3, Frame::Inertial>& x,\n                 tmpl::list<::Tags::FixedSource<\n                     Xcts::Tags::ConformalFactor<DataType>>> /*meta*/) const\n      -> tuples::TaggedTuple<\n          ::Tags::FixedSource<Xcts::Tags::ConformalFactor<DataType>>>;\n\n  template <typename DataType>\n  auto variables(const tnsr::I<DataType, 3, Frame::Inertial>& x,\n                 tmpl::list<gr::Tags::EnergyDensity<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<gr::Tags::EnergyDensity<DataType>>;\n  /// @}\n\n  /// Retrieve a collection of variables at coordinates `x`\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {tuples::get<Tags>(variables(x, tmpl::list<Tags>{}))...};\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double density_ = std::numeric_limits<double>::signaling_NaN();\n  double radius_ = std::numeric_limits<double>::signaling_NaN();\n  double alpha_ = std::numeric_limits<double>::signaling_NaN();\n};\n\nbool operator==(const ConstantDensityStar& /*lhs*/,\n                const ConstantDensityStar& /*rhs*/);\n\nbool operator!=(const ConstantDensityStar& lhs, const ConstantDensityStar& rhs);\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Flatness.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/TovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts {\n/// Analytic solutions of the XCTS equations\nnamespace Solutions {\nusing all_analytic_solutions =\n    tmpl::list<Flatness, WrappedGr<gr::Solutions::KerrSchild>,\n               WrappedGr<gr::Solutions::SphericalKerrSchild>, Schwarzschild,\n               WrappedGr<gr::Solutions::HarmonicSchwarzschild>, TovStar,\n               WrappedGrMhd<RelativisticEuler::Solutions::RotatingStar>,\n               // The following are only approximate solutions to the XCTS\n               // equations. We list them here (as opposed to AnalyticData) to\n               // avoid a cyclic dependency with the `WrappedGr` class, and also\n               // for convenience: by treating them as analytic solutions we can\n               // easily measure the difference between the approximate\n               // (analytic) solution and the numerical solution.\n               WrappedGrMhd<grmhd::AnalyticData::CcsnCollapse>,\n               WrappedGrMhd<grmhd::AnalyticData::MagnetizedTovStar>>;\n}  // namespace Solutions\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/Flatness.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Flatness.hpp\"\n\nnamespace Xcts::Solutions {\nbool operator==(const Flatness& /*lhs*/, const Flatness& /*rhs*/) {\n  return true;\n}\n\nbool operator!=(const Flatness& lhs, const Flatness& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID Flatness::my_PUP_ID = 0;  // NOLINT\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/Flatness.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <memory>\n#include <ostream>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Xcts::Solutions {\n\n/// Flat spacetime in general relativity. Useful as initial guess.\nclass Flatness : public elliptic::analytic_data::AnalyticSolution {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\n      \"Flat spacetime, useful as initial guess.\"};\n\n  Flatness() = default;\n  Flatness(const Flatness&) = default;\n  Flatness& operator=(const Flatness&) = default;\n  Flatness(Flatness&&) = default;\n  Flatness& operator=(Flatness&&) = default;\n  ~Flatness() = default;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<Flatness>(*this);\n  }\n\n  /// \\cond\n  explicit Flatness(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Flatness);\n  /// \\endcond\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using supported_tags_zero = tmpl::list<\n        Tags::ConformalFactorMinusOne<DataType>,\n        Tags::LapseTimesConformalFactorMinusOne<DataType>,\n        ::Tags::deriv<Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n                      tmpl::size_t<3>, Frame::Inertial>,\n        ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>,\n        Tags::ConformalChristoffelFirstKind<DataType, 3, Frame::Inertial>,\n        Tags::ConformalChristoffelSecondKind<DataType, 3, Frame::Inertial>,\n        Tags::ConformalChristoffelContracted<DataType, 3, Frame::Inertial>,\n        Tags::ConformalRicciTensor<DataVector, 3, Frame::Inertial>,\n        Tags::ConformalRicciScalar<DataVector>,\n        gr::Tags::TraceExtrinsicCurvature<DataType>,\n        ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>>,\n        ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>,\n                      tmpl::size_t<3>, Frame::Inertial>,\n        ::Tags::deriv<Tags::ConformalFactorMinusOne<DataType>, tmpl::size_t<3>,\n                      Frame::Inertial>,\n        ::Tags::deriv<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                      tmpl::size_t<3>, Frame::Inertial>,\n        ::Tags::deriv<Tags::ConformalFactor<DataType>, tmpl::size_t<3>,\n                      Frame::Inertial>,\n        ::Tags::deriv<Tags::LapseTimesConformalFactor<DataType>,\n                      tmpl::size_t<3>, Frame::Inertial>,\n        Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n        ::Tags::deriv<Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n                      tmpl::size_t<3>, Frame::Inertial>,\n        Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n        ::Tags::deriv<Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                      tmpl::size_t<3>, Frame::Inertial>,\n        Tags::ShiftStrain<DataType, 3, Frame::Inertial>,\n        gr::Tags::Shift<DataType, 3>,\n        ::Tags::Flux<Tags::ConformalFactorMinusOne<DataType>, tmpl::size_t<3>,\n                     Frame::Inertial>,\n        ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                     tmpl::size_t<3>, Frame::Inertial>,\n        Tags::LongitudinalShiftExcess<DataType, 3, Frame::Inertial>,\n        Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n            DataVector, 3, Frame::Inertial>,\n        Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataVector>,\n        Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<\n            DataVector>,\n        ::Tags::div<Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n            DataVector, 3, Frame::Inertial>>,\n        Tags::ShiftDotDerivExtrinsicCurvatureTrace<DataVector>,\n        gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n        gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0>,\n        gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0>,\n        gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>, 0>,\n        gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 6>,\n        gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 6>,\n        gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>, 6>,\n        gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 8>,\n        gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 8>,\n        gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>, 8>,\n        ::Tags::FixedSource<Tags::ConformalFactorMinusOne<DataType>>,\n        ::Tags::FixedSource<Tags::LapseTimesConformalFactorMinusOne<DataType>>,\n        ::Tags::FixedSource<Tags::ShiftExcess<DataType, 3, Frame::Inertial>>,\n        hydro::Tags::RestMassDensity<DataType>, hydro::Tags::Pressure<DataType>,\n        hydro::Tags::SpatialVelocity<DataType, 3>,\n        hydro::Tags::MagneticField<DataType, 3>>;\n    using supported_tags_one =\n        tmpl::list<Tags::ConformalFactor<DataType>,\n                   Tags::LapseTimesConformalFactor<DataType>,\n                   gr::Tags::Lapse<DataType>,\n                   hydro::Tags::SpecificEnthalpy<DataType>,\n                   hydro::Tags::LorentzFactor<DataType>>;\n    using supported_tags_metric =\n        tmpl::list<Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n                   Tags::InverseConformalMetric<DataType, 3, Frame::Inertial>,\n                   gr::Tags::SpatialMetric<DataType, 3>,\n                   gr::Tags::InverseSpatialMetric<DataType, 3>>;\n    using supported_tags = tmpl::append<supported_tags_zero, supported_tags_one,\n                                        supported_tags_metric>;\n    static_assert(\n        std::is_same_v<\n            tmpl::list_difference<tmpl::list<RequestedTags...>, supported_tags>,\n            tmpl::list<>>,\n        \"Not all requested tags are supported. The static_assert lists the \"\n        \"unsupported tags.\");\n    const auto make_value = [&x](auto tag_v) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      if constexpr (tmpl::list_contains_v<supported_tags_zero, tag>) {\n        return make_with_value<typename tag::type>(x, 0.);\n      } else if constexpr (tmpl::list_contains_v<supported_tags_one, tag>) {\n        return make_with_value<typename tag::type>(x, 1.);\n      } else if constexpr (tmpl::list_contains_v<supported_tags_metric, tag>) {\n        auto flat_metric = make_with_value<typename tag::type>(x, 0.);\n        get<0, 0>(flat_metric) = 1.;\n        get<1, 1>(flat_metric) = 1.;\n        get<2, 2>(flat_metric) = 1.;\n        return flat_metric;\n      }\n    };\n    return {make_value(RequestedTags{})...};\n  }\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x, const Mesh<3>& /*mesh*/,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>&\n      /*inv_jacobian*/,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables(x, tmpl::list<RequestedTags...>{});\n  }\n};\n\nbool operator==(const Flatness& /*lhs*/, const Flatness& /*rhs*/);\n\nbool operator!=(const Flatness& lhs, const Flatness& rhs);\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <limits>\n#include <ostream>\n#include <pup.h>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.tpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/Xcts/ExtrinsicCurvature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Simd/Simd.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::Solutions {\n\nstd::ostream& operator<<(std::ostream& os,\n                         const SchwarzschildCoordinates coords) {\n  switch (coords) {\n    case SchwarzschildCoordinates::Isotropic:\n      return os << \"Isotropic\";\n    case SchwarzschildCoordinates::PainleveGullstrand:\n      return os << \"PainleveGullstrand\";\n    case SchwarzschildCoordinates::KerrSchildIsotropic:\n      return os << \"KerrSchildIsotropic\";\n    case SchwarzschildCoordinates::MaximalIsotropic:\n      return os << \"MaximalIsotropic\";\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Unknown SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\n}  // namespace Xcts::Solutions\n\ntemplate <>\nXcts::Solutions::SchwarzschildCoordinates\nOptions::create_from_yaml<Xcts::Solutions::SchwarzschildCoordinates>::create<\n    void>(const Options::Option& options) {\n  const auto type_read = options.parse_as<std::string>();\n  if (\"Isotropic\" == type_read) {\n    return Xcts::Solutions::SchwarzschildCoordinates::Isotropic;\n  } else if (\"PainleveGullstrand\" == type_read) {\n    return Xcts::Solutions::SchwarzschildCoordinates::PainleveGullstrand;\n  } else if (\"KerrSchildIsotropic\" == type_read) {\n    return Xcts::Solutions::SchwarzschildCoordinates::KerrSchildIsotropic;\n  } else if (\"MaximalIsotropic\" == type_read) {\n    return Xcts::Solutions::SchwarzschildCoordinates::MaximalIsotropic;\n  }\n  PARSE_ERROR(options.context(),\n              \"Failed to convert \\\"\"\n                  << type_read\n                  << \"\\\" to Xcts::Solutions::SchwarzschildCoordinates. Must be \"\n                     \"one of 'Isotropic', 'PainleveGullstrand', \"\n                     \"'KerrSchildIsotropic', 'MaximalIsotropic'.\");\n}\n\nnamespace Xcts::Solutions {\nnamespace detail {\n\nnamespace {\ntemplate <typename DataType>\nDataType kerr_schild_isotropic_radius_from_areal(const DataType& areal_radius,\n                                                 const double mass) {\n  // Eq. (7.34) in https://arxiv.org/abs/gr-qc/0510016\n  const auto one_over_lapse = sqrt(1. + 2. * mass / areal_radius);\n  return 0.25 * areal_radius * square(1. + one_over_lapse) *\n         exp(2. - 2. * one_over_lapse);\n}\n\ntemplate <typename DataType>\nDataType kerr_schild_isotropic_radius_from_areal_deriv(\n    const DataType& areal_radius, const double mass) {\n  const auto isotropic_radius =\n      kerr_schild_isotropic_radius_from_areal(areal_radius, mass);\n  const auto one_over_lapse = sqrt(1. + 2. * mass / areal_radius);\n  return isotropic_radius / areal_radius * one_over_lapse;\n}\n\ndouble kerr_schild_areal_radius_from_isotropic(const double isotropic_radius,\n                                               const double mass) {\n  return RootFinder::toms748(\n      [&isotropic_radius, &mass](const double areal_radius) {\n        return kerr_schild_isotropic_radius_from_areal(areal_radius, mass) -\n               isotropic_radius;\n      },\n      isotropic_radius, isotropic_radius + mass, 1.0e-12, 1.0e-15);\n}\n\nDataVector kerr_schild_areal_radius_from_isotropic(\n    const DataVector& isotropic_radius, const double mass) {\n  return RootFinder::toms748<true>(\n      [&isotropic_radius, &mass](const auto areal_radius, const size_t i) {\n        if constexpr (simd::is_batch<\n                          std::decay_t<decltype(areal_radius)>>::value) {\n          return kerr_schild_isotropic_radius_from_areal(areal_radius, mass) -\n                 simd::load_unaligned(&isotropic_radius[i]);\n        } else {\n          return kerr_schild_isotropic_radius_from_areal(areal_radius, mass) -\n                 isotropic_radius[i];\n        }\n      },\n      isotropic_radius, isotropic_radius + mass, 1.0e-12, 1.0e-15);\n}\n\ntemplate <typename DataType>\nDataType maximal_isotropic_radius_from_areal(const DataType& areal_radius,\n                                             const double mass) {\n  return .25 *\n         (2. * areal_radius + mass +\n          sqrt(4. * square(areal_radius) + 4. * areal_radius * mass +\n               3. * square(mass))) *\n         pow((4. + 3. * sqrt(2.)) * (2. * areal_radius - 3. * mass) /\n                 (8. * areal_radius + 6. * mass +\n                  3. * sqrt(8. * square(areal_radius) +\n                            8. * areal_radius * mass + 6. * square(mass))),\n             1. / sqrt(2.));\n}\n\ntemplate <typename DataType>\nDataType maximal_isotropic_radius_from_areal_deriv(\n    const DataType& areal_radius, const DataType& isotropic_radius,\n    const double mass) {\n  // r_isotropic = (A/4) * B^(1/sqrt(2))\n  const DataType S = sqrt(8. * square(areal_radius) + 8. * areal_radius * mass +\n                          6. * square(mass));\n  const double C = 4. + 3. * sqrt(2.);\n  const DataType D = 8. * areal_radius + 6. * mass + 3. * S;\n  const DataType E = 2. * areal_radius - 3. * mass;\n  const DataType F = 2. * areal_radius + mass;\n  const DataType A = F + S / sqrt(2.);\n  const DataType B = C * E / D;\n  const DataType dAdR = 2. + (4. / sqrt(2.)) * F / S;\n  const DataType dBdR = C * (2. * D - E * (8. + 12. * F / S)) / square(D);\n\n  return isotropic_radius * (dAdR / A + dBdR / (B * sqrt(2.)));\n}\n\ntemplate <typename DataType>\nDataType areal_radius_from_maximal_isotropic(const DataType& isotropic_radius,\n                                             const double mass) {\n  const auto residual = [&isotropic_radius, &mass](const auto areal_radius,\n                                                   const size_t i = 0) {\n    if constexpr (simd::is_batch<std::decay_t<decltype(areal_radius)>>::value) {\n      return maximal_isotropic_radius_from_areal(areal_radius, mass) -\n             simd::load_unaligned(&(get_element(isotropic_radius, i)));\n    } else {\n      return maximal_isotropic_radius_from_areal(areal_radius, mass) -\n             get_element(isotropic_radius, i);\n    }\n  };\n  const auto lower_bound =\n      make_with_value<DataType>(isotropic_radius, 1.5 * mass);\n  const auto upper_bound = make_with_value<DataType>(isotropic_radius, 1e9);\n  return RootFinder::toms748<false>(residual, lower_bound, upper_bound, 1.0e-12,\n                                    1.0e-15);\n}\n\n}  // namespace\n\nSchwarzschildImpl::SchwarzschildImpl(\n    const double mass, const SchwarzschildCoordinates coordinate_system)\n    : mass_(mass), coordinate_system_(coordinate_system) {}\n\ndouble SchwarzschildImpl::mass() const { return mass_; }\n\nSchwarzschildCoordinates SchwarzschildImpl::coordinate_system() const {\n  return coordinate_system_;\n}\n\ndouble SchwarzschildImpl::radius_at_horizon() const {\n  switch (coordinate_system_) {\n    case SchwarzschildCoordinates::Isotropic:\n      return 0.5 * mass_;\n    case SchwarzschildCoordinates::PainleveGullstrand:\n      return 2. * mass_;\n    case SchwarzschildCoordinates::KerrSchildIsotropic:\n      return kerr_schild_isotropic_radius_from_areal(2. * mass_, mass_);\n    case SchwarzschildCoordinates::MaximalIsotropic:\n      return maximal_isotropic_radius_from_areal(2. * mass_, mass_);\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\nvoid SchwarzschildImpl::pup(PUP::er& p) {\n  p | mass_;\n  p | coordinate_system_;\n}\n\nbool operator==(const SchwarzschildImpl& lhs, const SchwarzschildImpl& rhs) {\n  return lhs.mass() == rhs.mass() and\n         lhs.coordinate_system() == rhs.coordinate_system();\n}\n\nbool operator!=(const SchwarzschildImpl& lhs, const SchwarzschildImpl& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> radius,\n    const gsl::not_null<Cache*> /*cache*/,\n    detail::Tags::Radius<DataType> /*meta*/) const {\n  magnitude(radius, x);\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> areal_radius,\n    const gsl::not_null<Cache*> cache,\n    detail::Tags::ArealRadius<DataType> /*meta*/) const {\n  const auto& isotropic_radius =\n      cache->get_var(*this, detail::Tags::Radius<DataType>{});\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::KerrSchildIsotropic:\n      get(*areal_radius) =\n          kerr_schild_areal_radius_from_isotropic(get(isotropic_radius), mass);\n      break;\n    case SchwarzschildCoordinates::MaximalIsotropic:\n      get(*areal_radius) =\n          areal_radius_from_maximal_isotropic(get(isotropic_radius), mass);\n      break;\n    default:\n      ERROR(\n          \"The areal radius is only needed for 'KerrSchildIsotropic' \"\n          \"or 'MaximalIsotropic' coordinates.\");\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> conformal_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::ConformalMetric<DataType, 3, Frame::Inertial> /*meta*/) const {\n  get<0, 0>(*conformal_metric) = 1.;\n  get<1, 1>(*conformal_metric) = 1.;\n  get<2, 2>(*conformal_metric) = 1.;\n  get<0, 1>(*conformal_metric) = 0.;\n  get<0, 2>(*conformal_metric) = 0.;\n  get<1, 2>(*conformal_metric) = 0.;\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3>*> inv_conformal_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::InverseConformalMetric<DataType, 3, Frame::Inertial> /*meta*/)\n    const {\n  get<0, 0>(*inv_conformal_metric) = 1.;\n  get<1, 1>(*inv_conformal_metric) = 1.;\n  get<2, 2>(*inv_conformal_metric) = 1.;\n  get<0, 1>(*inv_conformal_metric) = 0.;\n  get<0, 2>(*inv_conformal_metric) = 0.;\n  get<1, 2>(*inv_conformal_metric) = 0.;\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3>*> deriv_conformal_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Xcts::Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  std::fill(deriv_conformal_metric->begin(), deriv_conformal_metric->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> trace_extrinsic_curvature,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      get(*trace_extrinsic_curvature) = 0.;\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      get(*trace_extrinsic_curvature) = 1.5 * sqrt(2. * mass) / pow(r, 1.5);\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      get(*trace_extrinsic_curvature) =\n          2. * mass * cube(lapse) / square(r) * (1. + 3. * mass / r);\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      get(*trace_extrinsic_curvature) = 0.;\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> dt_trace_extrinsic_curvature,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/) const {\n  get(*dt_trace_extrinsic_curvature) = 0.;\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*>\n        trace_extrinsic_curvature_gradient,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      std::fill(trace_extrinsic_curvature_gradient->begin(),\n                trace_extrinsic_curvature_gradient->end(), 0.);\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const DataType isotropic_prefactor =\n          -2.25 * sqrt(2. * mass) / pow(r, 3.5);\n      get<0>(*trace_extrinsic_curvature_gradient) =\n          isotropic_prefactor * get<0>(x);\n      get<1>(*trace_extrinsic_curvature_gradient) =\n          isotropic_prefactor * get<1>(x);\n      get<2>(*trace_extrinsic_curvature_gradient) =\n          isotropic_prefactor * get<2>(x);\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& rbar =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      const auto& K = get(\n          cache->get_var(*this, gr::Tags::TraceExtrinsicCurvature<DataType>{}));\n      const DataType isotropic_prefactor =\n          K * lapse *\n          (3. * mass * square(lapse) / r - 2. - 3. * mass / (r + 3. * mass)) /\n          square(rbar);\n      get<0>(*trace_extrinsic_curvature_gradient) =\n          isotropic_prefactor * get<0>(x);\n      get<1>(*trace_extrinsic_curvature_gradient) =\n          isotropic_prefactor * get<1>(x);\n      get<2>(*trace_extrinsic_curvature_gradient) =\n          isotropic_prefactor * get<2>(x);\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      std::fill(trace_extrinsic_curvature_gradient->begin(),\n                trace_extrinsic_curvature_gradient->end(), 0.);\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor_minus_one,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::ConformalFactorMinusOne<DataType> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      get(*conformal_factor_minus_one) = 0.5 * mass / r;\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      get(*conformal_factor_minus_one) = 0.;\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}));\n      get(*conformal_factor_minus_one) = conformal_factor - 1.;\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}));\n      get(*conformal_factor_minus_one) = conformal_factor - 1.;\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::ConformalFactor<DataType> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      const auto& conformal_factor_minus_one = get(cache->get_var(\n          *this, Xcts::Tags::ConformalFactorMinusOne<DataType>{}));\n      get(*conformal_factor) = conformal_factor_minus_one + 1.;\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      get(*conformal_factor) = 1.;\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      // Eq. (7.35) in https://arxiv.org/abs/gr-qc/0510016\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      get(*conformal_factor) = 2. * exp(1. / lapse - 1.) / (1. + 1. / lapse);\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto r_areal =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& r_iso =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      get(*conformal_factor) = sqrt(r_areal / r_iso);\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*> conformal_factor_gradient,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const DataType isotropic_prefactor = -0.5 * mass / cube(r);\n      get<0>(*conformal_factor_gradient) = isotropic_prefactor * get<0>(x);\n      get<1>(*conformal_factor_gradient) = isotropic_prefactor * get<1>(x);\n      get<2>(*conformal_factor_gradient) = isotropic_prefactor * get<2>(x);\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      std::fill(conformal_factor_gradient->begin(),\n                conformal_factor_gradient->end(), 0.);\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& rbar =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}));\n      const auto one_over_lapse = sqrt(1. + 2. * mass / r);\n      const DataType isotropic_prefactor =\n          -conformal_factor * mass /\n          (r * (1. + one_over_lapse) * one_over_lapse * square(rbar));\n      get<0>(*conformal_factor_gradient) = isotropic_prefactor * get<0>(x);\n      get<1>(*conformal_factor_gradient) = isotropic_prefactor * get<1>(x);\n      get<2>(*conformal_factor_gradient) = isotropic_prefactor * get<2>(x);\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& r_iso =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const auto& r_areal =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n\n      const DataType drdR =\n          maximal_isotropic_radius_from_areal_deriv(r_areal, r_iso, mass);\n      const DataType dPsidR = .5 * sqrt(r_iso / r_areal) *\n                              (1. / r_iso - (r_areal / square(r_iso)) * drdR);\n      const DataType prefactor = dPsidR / (drdR * r_iso);\n      get<0>(*conformal_factor_gradient) = prefactor * get<0>(x);\n      get<1>(*conformal_factor_gradient) = prefactor * get<1>(x);\n      get<2>(*conformal_factor_gradient) = prefactor * get<2>(x);\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      get(*lapse_times_conformal_factor_minus_one) = -0.5 * mass / r;\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      get(*lapse_times_conformal_factor_minus_one) = 0.;\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& lapse_times_conformal_factor = get(cache->get_var(\n          *this, Xcts::Tags::LapseTimesConformalFactor<DataType>{}));\n      get(*lapse_times_conformal_factor_minus_one) =\n          lapse_times_conformal_factor - 1.;\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& lapse_times_conformal_factor = get(cache->get_var(\n          *this, Xcts::Tags::LapseTimesConformalFactor<DataType>{}));\n      get(*lapse_times_conformal_factor_minus_one) =\n          lapse_times_conformal_factor - 1.;\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::LapseTimesConformalFactor<DataType> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      const auto& lapse_times_conformal_factor_minus_one = get(cache->get_var(\n          *this, Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType>{}));\n      get(*lapse_times_conformal_factor) =\n          lapse_times_conformal_factor_minus_one + 1.;\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      get(*lapse_times_conformal_factor) = 1.;\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}));\n      get(*lapse_times_conformal_factor) = lapse * conformal_factor;\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}));\n      get(*lapse_times_conformal_factor) = lapse * conformal_factor;\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::Lapse<DataType> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}));\n      const auto& lapse_times_conformal_factor = get(cache->get_var(\n          *this, Xcts::Tags::LapseTimesConformalFactor<DataType>{}));\n      get(*lapse) = lapse_times_conformal_factor / conformal_factor;\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      get(*lapse) = 1.;\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      get(*lapse) = 1. / sqrt(1. + 2. * mass / r);\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& r_areal =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      get(*lapse) =\n          sqrt(1. - 2. * mass / r_areal +\n               27. * square(square(mass)) / (16. * square(square(r_areal))));\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*> deriv_lapse,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const auto& conformal_factor =\n          cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{});\n      const DataType isotropic_prefactor =\n          mass / square(get(conformal_factor)) / cube(r);\n      get<0>(*deriv_lapse) = isotropic_prefactor * get<0>(x);\n      get<1>(*deriv_lapse) = isotropic_prefactor * get<1>(x);\n      get<2>(*deriv_lapse) = isotropic_prefactor * get<2>(x);\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      std::fill(deriv_lapse->begin(), deriv_lapse->end(), 0.);\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& rbar =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      const DataType isotropic_prefactor =\n          mass * pow<4>(lapse) / (r * square(rbar));\n      get<0>(*deriv_lapse) = isotropic_prefactor * get<0>(x);\n      get<1>(*deriv_lapse) = isotropic_prefactor * get<1>(x);\n      get<2>(*deriv_lapse) = isotropic_prefactor * get<2>(x);\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& r_areal =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& r_iso =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n\n      const DataType drdR =\n          maximal_isotropic_radius_from_areal_deriv(r_areal, r_iso, mass);\n      const DataType dLapsedR =\n          (1. / (2. * lapse)) *\n          (2. * mass / square(r_areal) -\n           27. * square(square(mass)) / (4. * pow(r_areal, 5)));\n      const DataType prefactor = dLapsedR / (drdR * r_iso);\n      get<0>(*deriv_lapse) = prefactor * get<0>(x);\n      get<1>(*deriv_lapse) = prefactor * get<1>(x);\n      get<2>(*deriv_lapse) = prefactor * get<2>(x);\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*>\n        lapse_times_conformal_factor_gradient,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      *lapse_times_conformal_factor_gradient = cache->get_var(\n          *this, ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                               tmpl::size_t<3>, Frame::Inertial>{});\n      get<0>(*lapse_times_conformal_factor_gradient) *= -1.;\n      get<1>(*lapse_times_conformal_factor_gradient) *= -1.;\n      get<2>(*lapse_times_conformal_factor_gradient) *= -1.;\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      std::fill(lapse_times_conformal_factor_gradient->begin(),\n                lapse_times_conformal_factor_gradient->end(), 0.);\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& rbar =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}));\n      const auto& conformal_factor_gradient = cache->get_var(\n          *this, ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                               tmpl::size_t<3>, Frame::Inertial>{});\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      const DataType isotropic_prefactor =\n          conformal_factor * pow<4>(lapse) * mass / r / square(rbar);\n      *lapse_times_conformal_factor_gradient = conformal_factor_gradient;\n      get<0>(*lapse_times_conformal_factor_gradient) *= lapse;\n      get<1>(*lapse_times_conformal_factor_gradient) *= lapse;\n      get<2>(*lapse_times_conformal_factor_gradient) *= lapse;\n      get<0>(*lapse_times_conformal_factor_gradient) +=\n          isotropic_prefactor * get<0>(x);\n      get<1>(*lapse_times_conformal_factor_gradient) +=\n          isotropic_prefactor * get<1>(x);\n      get<2>(*lapse_times_conformal_factor_gradient) +=\n          isotropic_prefactor * get<2>(x);\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& conformal_factor =\n          get(cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}));\n      const auto& conformal_factor_gradient = cache->get_var(\n          *this, ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                               tmpl::size_t<3>, Frame::Inertial>{});\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      const auto& deriv_lapse = cache->get_var(\n          *this, ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                               Frame::Inertial>{});\n      *lapse_times_conformal_factor_gradient = conformal_factor_gradient;\n      get<0>(*lapse_times_conformal_factor_gradient) *= lapse;\n      get<1>(*lapse_times_conformal_factor_gradient) *= lapse;\n      get<2>(*lapse_times_conformal_factor_gradient) *= lapse;\n      get<0>(*lapse_times_conformal_factor_gradient) +=\n          conformal_factor * get<0>(deriv_lapse);\n      get<1>(*lapse_times_conformal_factor_gradient) +=\n          conformal_factor * get<1>(deriv_lapse);\n      get<2>(*lapse_times_conformal_factor_gradient) +=\n          conformal_factor * get<2>(deriv_lapse);\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\n// Set the background shift to zero in the decomposition:\n//   shift = shift_background + shift_excess\n// See docs of Xcts::Tags::ShiftExcess.\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> shift_background,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::ShiftBackground<DataType, 3, Frame::Inertial> /*meta*/) const {\n  std::fill(shift_background->begin(), shift_background->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift_background,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Xcts::Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  std::fill(deriv_shift_background->begin(), deriv_shift_background->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3, Frame::Inertial>*>\n        longitudinal_shift_background_minus_dt_conformal_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n        DataType, 3, Frame::Inertial> /*meta*/) const {\n  std::fill(longitudinal_shift_background_minus_dt_conformal_metric->begin(),\n            longitudinal_shift_background_minus_dt_conformal_metric->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> shift_excess,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::ShiftExcess<DataType, 3, Frame::Inertial> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      std::fill(shift_excess->begin(), shift_excess->end(), 0.);\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const DataType isotropic_prefactor = sqrt(2. * mass) / pow(r, 1.5);\n      *shift_excess = x;\n      get<0>(*shift_excess) *= isotropic_prefactor;\n      get<1>(*shift_excess) *= isotropic_prefactor;\n      get<2>(*shift_excess) *= isotropic_prefactor;\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      const DataType isotropic_prefactor = 2. * mass * lapse / square(r);\n      *shift_excess = x;\n      get<0>(*shift_excess) *= isotropic_prefactor;\n      get<1>(*shift_excess) *= isotropic_prefactor;\n      get<2>(*shift_excess) *= isotropic_prefactor;\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& r_areal =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      get<0>(*shift_excess) =\n          .75 * sqrt(3) * square(mass) * get<0>(x) / pow(r_areal, 3);\n      get<1>(*shift_excess) =\n          .75 * sqrt(3) * square(mass) * get<1>(x) / pow(r_areal, 3);\n      get<2>(*shift_excess) =\n          .75 * sqrt(3) * square(mass) * get<2>(x) / pow(r_areal, 3);\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift_excess,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<Xcts::Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      std::fill(deriv_shift_excess->begin(), deriv_shift_excess->end(), 0.);\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const DataType diagonal_prefactor = sqrt(2. * mass) / pow(r, 1.5);\n      const DataType isotropic_prefactor =\n          -1.5 * diagonal_prefactor / square(r);\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = 0; j < 3; ++j) {\n          deriv_shift_excess->get(i, j) =\n              isotropic_prefactor * x.get(i) * x.get(j);\n        }\n        deriv_shift_excess->get(i, i) += diagonal_prefactor;\n      }\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      const auto& rbar =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& lapse =\n          get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n      const DataType diagonal_prefactor = 2. * mass * lapse / square(r);\n      const DataType isotropic_prefactor = diagonal_prefactor *\n                                           (square(lapse) * mass / r - 2.) *\n                                           lapse / square(rbar);\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = 0; j < 3; ++j) {\n          deriv_shift_excess->get(i, j) =\n              isotropic_prefactor * x.get(i) * x.get(j);\n        }\n        deriv_shift_excess->get(i, i) += diagonal_prefactor;\n      }\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      const auto& r_areal =\n          get(cache->get_var(*this, detail::Tags::ArealRadius<DataType>{}));\n      const auto& r_iso =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const DataType BetaRadial =\n          .75 * sqrt(3) * square(mass) * r_iso / pow(r_areal, 3);\n\n      const DataType drdR =\n          maximal_isotropic_radius_from_areal_deriv(r_areal, r_iso, mass);\n      const DataType dBetaRadialdR =\n          .75 * sqrt(3) * square(mass) *\n          (drdR / pow(r_areal, 3) - 3. * r_iso / pow(r_areal, 4));\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = 0; j < 3; ++j) {\n          deriv_shift_excess->get(i, j) =\n              (dBetaRadialdR / (drdR * square(r_iso)) -\n               BetaRadial / pow(r_iso, 3)) *\n              x.get(i) * x.get(j);\n        }\n        deriv_shift_excess->get(i, i) += BetaRadial / r_iso;\n      }\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> extrinsic_curvature,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::ExtrinsicCurvature<DataType, 3> /*meta*/) const {\n  switch (coordinate_system) {\n    case SchwarzschildCoordinates::Isotropic: {\n      std::fill(extrinsic_curvature->begin(), extrinsic_curvature->end(), 0.);\n      break;\n    }\n    case SchwarzschildCoordinates::PainleveGullstrand: {\n      const auto& r =\n          get(cache->get_var(*this, detail::Tags::Radius<DataType>{}));\n      const DataType diagonal_prefactor = sqrt(2. * mass / cube(r));\n      const DataType isotropic_prefactor =\n          -3. / 2. * diagonal_prefactor / square(r);\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = 0; j <= i; ++j) {\n          extrinsic_curvature->get(i, j) =\n              isotropic_prefactor * x.get(i) * x.get(j);\n        }\n        extrinsic_curvature->get(i, i) += diagonal_prefactor;\n      }\n      break;\n    }\n    case SchwarzschildCoordinates::KerrSchildIsotropic: {\n      // Background shift and \\bar{u} are both zero\n      const auto& longitudinal_shift_minus_dt_conformal_metric = cache->get_var(\n          *this,\n          Xcts::Tags::LongitudinalShiftExcess<DataType, 3, Frame::Inertial>{});\n      Xcts::extrinsic_curvature(\n          extrinsic_curvature,\n          cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}),\n          cache->get_var(*this, gr::Tags::Lapse<DataType>{}),\n          cache->get_var(\n              *this,\n              Xcts::Tags::ConformalMetric<DataType, 3, Frame::Inertial>{}),\n          longitudinal_shift_minus_dt_conformal_metric,\n          cache->get_var(*this, gr::Tags::TraceExtrinsicCurvature<DataType>{}));\n      break;\n    }\n    case SchwarzschildCoordinates::MaximalIsotropic: {\n      // Background shift and \\bar{u} are both zero\n      const auto& longitudinal_shift_minus_dt_conformal_metric = cache->get_var(\n          *this,\n          Xcts::Tags::LongitudinalShiftExcess<DataType, 3, Frame::Inertial>{});\n      Xcts::extrinsic_curvature(\n          extrinsic_curvature,\n          cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{}),\n          cache->get_var(*this, gr::Tags::Lapse<DataType>{}),\n          cache->get_var(\n              *this,\n              Xcts::Tags::ConformalMetric<DataType, 3, Frame::Inertial>{}),\n          longitudinal_shift_minus_dt_conformal_metric,\n          cache->get_var(*this, gr::Tags::TraceExtrinsicCurvature<DataType>{}));\n      break;\n    }\n      // LCOV_EXCL_START\n    default:\n      ERROR(\"Missing case for SchwarzschildCoordinates\");\n      // LCOV_EXCL_END\n  }\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> energy_density,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>,\n                        ConformalMatterScale> /*meta*/) const {\n  std::fill(energy_density->begin(), energy_density->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> stress_trace,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::Conformal<gr::Tags::StressTrace<DataType>,\n                        ConformalMatterScale> /*meta*/) const {\n  std::fill(stress_trace->begin(), stress_trace->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid SchwarzschildVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> momentum_density,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>,\n                        ConformalMatterScale> /*meta*/) const {\n  std::fill(momentum_density->begin(), momentum_density->end(), 0.);\n}\n\ntemplate class SchwarzschildVariables<double>;\ntemplate class SchwarzschildVariables<DataVector>;\n\n}  // namespace detail\n\nPUP::able::PUP_ID Schwarzschild::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace Xcts::Solutions\n\n// Instantiate implementations for common variables\ntemplate class Xcts::Solutions::CommonVariables<\n    double,\n    typename Xcts::Solutions::detail::SchwarzschildVariables<double>::Cache>;\ntemplate class Xcts::Solutions::CommonVariables<\n    DataVector, typename Xcts::Solutions::detail::SchwarzschildVariables<\n                    DataVector>::Cache>;\ntemplate class Xcts::AnalyticData::CommonVariables<\n    double,\n    typename Xcts::Solutions::detail::SchwarzschildVariables<double>::Cache>;\ntemplate class Xcts::AnalyticData::CommonVariables<\n    DataVector, typename Xcts::Solutions::detail::SchwarzschildVariables<\n                    DataVector>::Cache>;\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <memory>\n#include <ostream>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Flatness.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Xcts::Solutions {\n\n/// Various coordinate systems in which to express the Schwarzschild solution\nenum class SchwarzschildCoordinates {\n  /*!\n   * \\brief Isotropic Schwarzschild coordinates\n   *\n   * These arise from the canonical Schwarzschild coordinates by the radial\n   * transformation\n   *\n   * \\f{equation}\n   * r = \\bar{r}\\left(1+\\frac{M}{2\\bar{r}}\\right)^2\n   * \\f}\n   *\n   * (Eq. (1.61) in \\cite BaumgarteShapiro) where \\f$r\\f$ is the canonical\n   * Schwarzschild radius, also referred to as \"areal\" radius because it is\n   * defined such that spheres with constant \\f$r\\f$ have the area \\f$4\\pi\n   * r^2\\f$, and \\f$\\bar{r}\\f$ is the \"isotropic\" radius. In the isotropic\n   * radius the Schwarzschild spatial metric is conformally flat:\n   *\n   * \\f{equation}\n   * \\gamma_{ij}=\\psi^4\\eta_{ij} \\quad \\text{with conformal factor} \\quad\n   * \\psi=1+\\frac{M}{2\\bar{r}}\n   * \\f}\n   *\n   * (Table 2.1 in \\cite BaumgarteShapiro). The lapse in the conformal radius is\n   *\n   * \\f{equation}\n   * \\alpha=\\frac{1-M/(2\\bar{r})}{1+M/(2\\bar{r})}\n   * \\f}\n   *\n   * and the shift vanishes (\\f$\\beta^i=0\\f$) as it does in areal Schwarzschild\n   * coordinates. The solution also remains maximally sliced, i.e. \\f$K=0\\f$.\n   *\n   * The Schwarzschild horizon in these coordinates is at\n   * \\f$\\bar{r}=\\frac{M}{2}\\f$ due to the radial transformation from \\f$r=2M\\f$.\n   */\n  Isotropic,\n  /*!\n   * \\brief Painlevé-Gullstrand coordinates\n   *\n   * In these coordinates the spatial metric is flat and the lapse is trivial,\n   * but contrary to (isotropic) Schwarzschild coordinates the shift is\n   * nontrivial,\n   *\n   * \\begin{align}\n   *   \\gamma_{ij} &= \\eta_{ij} \\\\\n   *   \\alpha &= 1 \\\\\n   *   \\beta^i &= \\sqrt{\\frac{2M}{r}} \\frac{x^i}{r} \\\\\n   *   K &= \\frac{3}{2}\\sqrt{\\frac{2M}{r^3}}\n   * \\end{align}\n   *\n   * (Table 2.1 in \\cite BaumgarteShapiro).\n   */\n  PainleveGullstrand,\n  /*!\n   * \\brief Isotropic Kerr-Schild coordinates\n   *\n   * Kerr-Schild coordinates with a radial transformation such that the spatial\n   * metric is conformally flat.\n   *\n   * The Schwarzschild spacetime in canonical (areal) Kerr-Schild coordinates is\n   *\n   * \\begin{align}\n   *   \\gamma_{ij} &= \\eta_{ij} + \\frac{2M}{r}\\frac{x^i x^j}{r^2} \\\\\n   *   \\alpha &= \\sqrt{1 + \\frac{2M}{r}}^{-1} \\\\\n   *   \\beta^i &= \\frac{2M\\alpha^2}{r} \\frac{x^i}{r} \\\\\n   *   K &= \\frac{2M\\alpha^3}{r^2} \\left(1 + \\frac{3M}{r}\\right)\n   *   \\text{.}\n   * \\end{align}\n   *\n   * (Table 2.1 in \\cite BaumgarteShapiro). Since the Schwarzschild spacetime is\n   * spherically symmetric we can transform to a radial coordinate $\\bar{r}$ in\n   * which it is conformally flat (see, e.g., Sec. 7.4.1 in \\cite Pfeiffer2005zm\n   * for details):\n   *\n   * \\begin{equation}\n   *   {}^{(3)}\\mathrm{d}s^2\n   *     = \\left(1 + \\frac{2M}{r}\\right)\\mathrm{d}r^2 + r^2 \\mathrm{d}\\Omega^2\n   *     = \\psi^4 \\left(\\mathrm{d}\\bar{r}^2 +\n   *       \\bar{r}^2 \\mathrm{d}\\Omega^2\\right)\n   * \\end{equation}\n   *\n   * Therefore, the conformal factor is $\\psi^2 = r / \\bar{r}$ and\n   *\n   * \\begin{equation}\n   *   \\frac{\\mathrm{d}\\bar{r}}{\\mathrm{d}r}\n   *     = \\frac{\\bar{r}}{r} \\sqrt{1 + \\frac{2M}{r}}\n   *     = \\frac{\\bar{r}}{r} \\frac{1}{\\alpha}\n   *   \\text{,}\n   * \\end{equation}\n   *\n   * which has the solution\n   *\n   * \\begin{equation}\n   *   \\bar{r} = \\frac{r}{4} \\left(1 + \\sqrt{1 + \\frac{2M}{r}}\\right)^2\n   *     e^{2 - 2\\sqrt{1 + 2M / r}}\n   * \\end{equation}\n   *\n   * when we impose $\\bar{r} \\rightarrow r$ as $r \\rightarrow \\infty$. We can\n   * invert this transformation law with a numerical root find to obtain the\n   * areal radius $r$ for any isotropic radius $\\bar{r}$.\n   *\n   * In the isotropic radial coordinate $\\bar{r}$ the solution is then:\n   *\n   * \\begin{align}\n   *   \\gamma_{ij} &= \\psi^4 \\eta_{ij} \\\\\n   *   \\psi &= \\sqrt{\\frac{r}{\\bar{r}}}\n   *     = \\frac{2e^{\\sqrt{1 + 2M / r} - 1}}{1 + \\sqrt{1 + 2M / r}} \\\\\n   *   \\alpha &= \\sqrt{1 + \\frac{2M}{r}}^{-1} \\\\\n   *   \\beta^i\n   *     &= \\frac{\\mathrm{d}\\bar{r}}{\\mathrm{d}r} \\beta^r \\frac{x^i}{\\bar{r}}\n   *      = \\frac{2M\\alpha}{r^2} x^i \\\\\n   *   K &= \\frac{2M\\alpha^3}{r^2} \\left(1 + \\frac{3M}{r}\\right)\n   * \\end{align}\n   *\n   * Here, $x^i$ are the (isotropic) Cartesian coordinates from which we compute\n   * the isotropic radius $\\bar{r}$, $r$ is the areal radius we can obtain from\n   * the isotropic radius by a root find, and $\\beta^r$ is the magnitude of the\n   * shift in areal coordinates, as given above.\n   *\n   * The horizon in these coordinates is at (Eq. (7.37) in\n   * \\cite Pfeiffer2005zm):\n   *\n   * \\begin{equation}\n   * \\bar{r}_\\mathrm{AH} / M \\approx 1.2727410334221052\n   * \\end{equation}\n   */\n  KerrSchildIsotropic,\n  /*!\n   * \\brief Maximal Isotropic (Horizon Penetrating) Schwarzschild coordinates\n   *\n   * Schwarzschild coordinates with a radial transformation such that the radius\n   * is isotropic and the coordinates are horizon penetrating.\n   *\n   * These arise from first choosing a family of time-independent, maximal\n   * slicings of the Schwarzschild spacetime and a slicing condition that give a\n   * unique solution with a limiting surface at \\f$R=3M/2\\f$ and horizon at\n   * \\f$R=2M\\f$ \\cite Estabrook1973ue. The latter is then changed by the radial\n   * transformation \\cite Baumgarte2007ht\n   *\n   * \\f{equation}\n   *  r = \\frac{\\left[2R + M + \\sqrt{4R^2 + 4MR + 3M^2}\\right]}{4}\n   *  \\times \\left[\n   *  \\frac{(4 + 3\\sqrt{2})(2R - 3M)}{8R + 6M + 3\\sqrt{8R^2 + 8MR + 6M^2}}\n   *  \\right]^{1/\\sqrt{2}}\n   * \\f}\n   *\n   * where \\f$R\\f$ is the canonical\n   * Schwarzschild radius, also referred to as \"areal\" radius because it is\n   * defined such that spheres with constant \\f$R\\f$ have the area \\f$4\\pi\n   * R^2\\f$, and \\f$r\\f$ is the \"isotropic\" radius. In the isotropic\n   * radius the Schwarzschild spatial metric is conformally flat:\n   *\n   * \\f{equation}\n   * \\gamma_{ij}=\\psi^4\\eta_{ij} \\quad \\text{with conformal factor} \\quad\n   * \\psi=\\sqrt{\\frac{R}{r}}.\n   * \\f}\n   *\n   * The lapse in the conformal radius is\n   *\n   * \\f{equation}\n   * \\alpha = \\left(1 - \\frac{2M}{R} + \\frac{27M^4}{16R^4} \\right)^{1/2}\n   * \\f}\n   *\n   * and the shift is given by\n   *\n   * \\f{equation}\n   * \\beta^r = \\frac{3 \\sqrt{3} M^2}{4} \\frac{r}{R^3}\n   * \\f}\n   *\n   * The solution remains maximally sliced, i.e. \\f$K=0\\f$. And the horizon in\n   * these coordinates is at \\f$r\\approx 0.7793271080557972 M\\f$ due to the\n   * radial transformation from \\f$R=2M\\f$.\n   *\n   */\n  MaximalIsotropic,\n};\n\nstd::ostream& operator<<(std::ostream& os, SchwarzschildCoordinates coords);\n\n}  // namespace Xcts::Solutions\n\ntemplate <>\nstruct Options::create_from_yaml<Xcts::Solutions::SchwarzschildCoordinates> {\n  template <typename Metavariables>\n  static Xcts::Solutions::SchwarzschildCoordinates create(\n      const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nXcts::Solutions::SchwarzschildCoordinates\nOptions::create_from_yaml<Xcts::Solutions::SchwarzschildCoordinates>::create<\n    void>(const Options::Option& options);\n\nnamespace Xcts::Solutions {\n\nnamespace detail {\n\nstruct SchwarzschildImpl {\n  struct Mass {\n    using type = double;\n    static constexpr Options::String help = \"Mass parameter M\";\n  };\n\n  struct CoordinateSystem {\n    static std::string name() { return \"Coordinates\"; }\n    using type = SchwarzschildCoordinates;\n    static constexpr Options::String help =\n        \"The coordinate system used to describe the solution\";\n  };\n\n  using options = tmpl::list<Mass, CoordinateSystem>;\n  static constexpr Options::String help{\n      \"Schwarzschild spacetime in general relativity\"};\n\n  SchwarzschildImpl() = default;\n  SchwarzschildImpl(const SchwarzschildImpl&) = default;\n  SchwarzschildImpl& operator=(const SchwarzschildImpl&) = default;\n  SchwarzschildImpl(SchwarzschildImpl&&) = default;\n  SchwarzschildImpl& operator=(SchwarzschildImpl&&) = default;\n  ~SchwarzschildImpl() = default;\n\n  explicit SchwarzschildImpl(double mass,\n                             SchwarzschildCoordinates coordinate_system);\n\n  /// The mass parameter \\f$M\\f$.\n  double mass() const;\n\n  SchwarzschildCoordinates coordinate_system() const;\n\n  /// The radius of the Schwarzschild horizon in the given coordinates.\n  double radius_at_horizon() const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n protected:\n  double mass_{std::numeric_limits<double>::signaling_NaN()};\n  SchwarzschildCoordinates coordinate_system_{};\n};\n\nbool operator==(const SchwarzschildImpl& lhs, const SchwarzschildImpl& rhs);\n\nbool operator!=(const SchwarzschildImpl& lhs, const SchwarzschildImpl& rhs);\n\nnamespace Tags {\ntemplate <typename DataType>\nstruct Radius : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct ArealRadius : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n}  // namespace Tags\n\ntemplate <typename DataType>\nusing SchwarzschildVariablesCache =\n    cached_temp_buffer_from_typelist<tmpl::push_front<\n        common_tags<DataType>, detail::Tags::Radius<DataType>,\n        detail::Tags::ArealRadius<DataType>,\n        ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                      Frame::Inertial>,\n        gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0>,\n        gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0>,\n        gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>, 0>>>;\n\ntemplate <typename DataType>\nstruct SchwarzschildVariables\n    : CommonVariables<DataType, SchwarzschildVariablesCache<DataType>> {\n  static constexpr size_t Dim = 3;\n  static constexpr int ConformalMatterScale = 0;\n  using Cache = SchwarzschildVariablesCache<DataType>;\n  using Base = CommonVariables<DataType, SchwarzschildVariablesCache<DataType>>;\n  using Base::operator();\n\n  SchwarzschildVariables(\n      std::optional<std::reference_wrapper<const Mesh<Dim>>> local_mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, Dim, Frame::ElementLogical, Frame::Inertial>>>\n          local_inv_jacobian,\n      const tnsr::I<DataType, 3>& local_x, const double local_mass,\n      const SchwarzschildCoordinates local_coordinate_system)\n      : Base(std::move(local_mesh), std::move(local_inv_jacobian)),\n        x(local_x),\n        mass(local_mass),\n        coordinate_system(local_coordinate_system) {}\n\n  const tnsr::I<DataType, 3>& x;\n  double mass;\n  SchwarzschildCoordinates coordinate_system;\n\n  void operator()(gsl::not_null<Scalar<DataType>*> radius,\n                  gsl::not_null<Cache*> cache,\n                  detail::Tags::Radius<DataType> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> areal_radius,\n                  gsl::not_null<Cache*> cache,\n                  detail::Tags::ArealRadius<DataType> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::ii<DataType, 3>*> conformal_metric,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ConformalMetric<DataType, 3, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::II<DataType, 3>*> inv_conformal_metric,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::InverseConformalMetric<DataType, 3, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::ijj<DataType, 3>*> deriv_conformal_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> trace_extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, 3>*> trace_extrinsic_curvature_gradient,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> dt_trace_extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> conformal_factor_minus_one,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ConformalFactorMinusOne<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> conformal_factor,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ConformalFactor<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, 3>*> conformal_factor_gradient,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(gsl::not_null<Scalar<DataType>*> lapse,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Lapse<DataType> /*meta*/) const override;\n  void operator()(gsl::not_null<tnsr::i<DataType, 3>*> deriv_lapse,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::LapseTimesConformalFactor<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, 3>*>\n          lapse_times_conformal_factor_gradient,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, 3>*> shift_background,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ShiftBackground<DataType, 3, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::iJ<DataType, 3, Frame::Inertial>*>\n          deriv_shift_background,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(gsl::not_null<tnsr::II<DataType, 3, Frame::Inertial>*>\n                      longitudinal_shift_background_minus_dt_conformal_metric,\n                  gsl::not_null<Cache*> cache,\n                  Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n                      DataType, 3, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, 3>*> shift_excess,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ShiftExcess<DataType, 3, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift_excess,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::ii<DataType, 3>*> extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::ExtrinsicCurvature<DataType, 3> /*meta*/) const override;\n  void operator()(gsl::not_null<Scalar<DataType>*> energy_density,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>,\n                                      ConformalMatterScale> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> stress_trace,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Conformal<gr::Tags::StressTrace<DataType>,\n                                      ConformalMatterScale> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, 3>*> momentum_density,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>,\n                                      ConformalMatterScale> /*meta*/) const;\n};\n\n}  // namespace detail\n\n/*!\n * \\brief Schwarzschild spacetime in general relativity\n *\n * This class implements the Schwarzschild solution with mass parameter\n * \\f$M\\f$ in various coordinate systems. See the entries of the\n * `Xcts::Solutions::SchwarzschildCoordinates` enum for the available coordinate\n * systems and for the solution variables in the respective coordinates.\n */\nclass Schwarzschild : public elliptic::analytic_data::AnalyticSolution,\n                      public detail::SchwarzschildImpl {\n public:\n  Schwarzschild() = default;\n  Schwarzschild(const Schwarzschild&) = default;\n  Schwarzschild& operator=(const Schwarzschild&) = default;\n  Schwarzschild(Schwarzschild&&) = default;\n  Schwarzschild& operator=(Schwarzschild&&) = default;\n  ~Schwarzschild() = default;\n\n  using SchwarzschildImpl::SchwarzschildImpl;\n\n  /// \\cond\n  explicit Schwarzschild(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Schwarzschild);\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<Schwarzschild>(*this);\n  }\n  /// \\endcond\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables_impl<DataType>(x, std::nullopt, std::nullopt,\n                                    tmpl::list<RequestedTags...>{});\n  }\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x, const Mesh<3>& mesh,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables_impl<DataVector>(x, mesh, inv_jacobian,\n                                      tmpl::list<RequestedTags...>{});\n  }\n\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::AnalyticSolution::pup(p);\n    detail::SchwarzschildImpl::pup(p);\n  }\n\n private:\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables_impl(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      std::optional<std::reference_wrapper<const Mesh<3>>> mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, 3, Frame::ElementLogical, Frame::Inertial>>>\n          inv_jacobian,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using VarsComputer = detail::SchwarzschildVariables<DataType>;\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const VarsComputer computer{std::move(mesh), std::move(inv_jacobian), x,\n                                mass_, coordinate_system_};\n    const auto get_var = [&cache, &computer, &x](auto tag_v) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      if constexpr (tmpl::list_contains_v<hydro_tags<DataType>, tag>) {\n        (void)cache;\n        (void)computer;\n        return get<tag>(Flatness{}.variables(x, tmpl::list<tag>{}));\n      } else {\n        (void)x;\n        return cache.get_var(computer, tag{});\n      }\n    };\n    return {get_var(RequestedTags{})...};\n  }\n};\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/TovStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/TovStar.hpp\"\n\n#include <algorithm>\n#include <ostream>\n#include <pup.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.tpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts::Solutions::tov_detail {\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> conformal_metric,\n    const gsl::not_null<Cache*> /* cache */,\n    Tags::ConformalMetric<DataType, 3, Frame::Inertial> /*meta*/) const {\n  if (tov_star.radial_solution().coordinate_system() ==\n      TovCoordinates::Isotropic) {\n    get<0, 0>(*conformal_metric) = 1.;\n    get<1, 1>(*conformal_metric) = 1.;\n    get<2, 2>(*conformal_metric) = 1.;\n    get<0, 1>(*conformal_metric) = 0.;\n    get<0, 2>(*conformal_metric) = 0.;\n    get<1, 2>(*conformal_metric) = 0.;\n  } else {\n    *conformal_metric = get_tov_var(gr::Tags::SpatialMetric<DataType, 3>{});\n  }\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3>*> inv_conformal_metric,\n    const gsl::not_null<Cache*> /* cache */,\n    Tags::InverseConformalMetric<DataType, 3, Frame::Inertial> /*meta*/) const {\n  if (tov_star.radial_solution().coordinate_system() ==\n      TovCoordinates::Isotropic) {\n    get<0, 0>(*inv_conformal_metric) = 1.;\n    get<1, 1>(*inv_conformal_metric) = 1.;\n    get<2, 2>(*inv_conformal_metric) = 1.;\n    get<0, 1>(*inv_conformal_metric) = 0.;\n    get<0, 2>(*inv_conformal_metric) = 0.;\n    get<1, 2>(*inv_conformal_metric) = 0.;\n  } else {\n    *inv_conformal_metric =\n        get_tov_var(gr::Tags::InverseSpatialMetric<DataType, 3>{});\n  }\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, 3>*> deriv_conformal_metric,\n    const gsl::not_null<Cache*> /* cache */,\n    ::Tags::deriv<Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  if (tov_star.radial_solution().coordinate_system() ==\n      TovCoordinates::Isotropic) {\n    std::fill(deriv_conformal_metric->begin(), deriv_conformal_metric->end(),\n              0.);\n  } else {\n    *deriv_conformal_metric =\n        get_tov_var(::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>,\n                                  tmpl::size_t<3>, Frame::Inertial>{});\n  }\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> extrinsic_curvature,\n    const gsl::not_null<Cache*> /* cache */,\n    gr::Tags::ExtrinsicCurvature<DataType, 3> /*meta*/) const {\n  std::fill(extrinsic_curvature->begin(), extrinsic_curvature->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> trace_extrinsic_curvature,\n    const gsl::not_null<Cache*> /* cache */,\n    gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) const {\n  get(*trace_extrinsic_curvature) = 0.;\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> dt_trace_extrinsic_curvature,\n    const gsl::not_null<Cache*> /* cache */,\n    ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/) const {\n  get(*dt_trace_extrinsic_curvature) = 0.;\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*> deriv_trace_extrinsic_curvature,\n    const gsl::not_null<Cache*> /* cache */,\n    ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  std::fill(deriv_trace_extrinsic_curvature->begin(),\n            deriv_trace_extrinsic_curvature->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor,\n    const gsl::not_null<Cache*> /* cache */,\n    Tags::ConformalFactor<DataType> /*meta*/) const {\n  if (tov_star.radial_solution().coordinate_system() ==\n      TovCoordinates::Isotropic) {\n    *conformal_factor = get_tov_var(\n        RelativisticEuler::Solutions::tov_detail::Tags::ConformalFactor<\n            DataType>{});\n  } else {\n    get(*conformal_factor) = 1.;\n  }\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor_minus_one,\n    const gsl::not_null<Cache*> cache,\n    Tags::ConformalFactorMinusOne<DataType> /*meta*/) const {\n  if (tov_star.radial_solution().coordinate_system() ==\n      TovCoordinates::Isotropic) {\n    const auto& conformal_factor =\n        get(cache->get_var(*this, Tags::ConformalFactor<DataType>{}));\n    get(*conformal_factor_minus_one) = conformal_factor - 1.;\n  } else {\n    get(*conformal_factor_minus_one) = 0.;\n  }\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*> deriv_conformal_factor,\n    const gsl::not_null<Cache*> /* cache */,\n    ::Tags::deriv<Tags::ConformalFactorMinusOne<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  if (tov_star.radial_solution().coordinate_system() ==\n      TovCoordinates::Isotropic) {\n    for (size_t i = 0; i < get_size(radius); ++i) {\n      if (get_element(radius, i) > 1.e-14) {\n        using tag =\n            RelativisticEuler::Solutions::tov_detail::Tags::DrConformalFactor<\n                double>;\n        const tnsr::I<double, 3> x_i{\n            {{get_element(get<0>(x), i), get_element(get<1>(x), i),\n              get_element(get<2>(x), i)}}};\n        const double dr_conformal_factor =\n            get(get<tag>(tov_star.variables(x_i, 0., tmpl::list<tag>{})));\n        get_element(get<0>(*deriv_conformal_factor), i) =\n            dr_conformal_factor / get_element(radius, i);\n      } else {\n        get_element(get<0>(*deriv_conformal_factor), i) = 0.;\n      }\n    }\n    get<1>(*deriv_conformal_factor) = get<0>(*deriv_conformal_factor);\n    get<2>(*deriv_conformal_factor) = get<0>(*deriv_conformal_factor);\n    get<0>(*deriv_conformal_factor) *= get<0>(x);\n    get<1>(*deriv_conformal_factor) *= get<1>(x);\n    get<2>(*deriv_conformal_factor) *= get<2>(x);\n  } else {\n    get<0>(*deriv_conformal_factor) = 0.;\n    get<1>(*deriv_conformal_factor) = 0.;\n    get<2>(*deriv_conformal_factor) = 0.;\n  }\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse,\n    const gsl::not_null<Cache*> /* cache */,\n    gr::Tags::Lapse<DataType> /*meta*/) const {\n  *lapse = get_tov_var(gr::Tags::Lapse<DataType>{});\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*> deriv_lapse,\n    const gsl::not_null<Cache*> /* cache */,\n    ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  *deriv_lapse = get_tov_var(::Tags::deriv<gr::Tags::Lapse<DataType>,\n                                           tmpl::size_t<3>, Frame::Inertial>{});\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    Tags::LapseTimesConformalFactor<DataType> /*meta*/) const {\n  const auto& conformal_factor =\n      get(cache->get_var(*this, Tags::ConformalFactor<DataType>{}));\n  const auto& lapse = get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n  get(*lapse_times_conformal_factor) = lapse * conformal_factor;\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<Cache*> cache,\n    Tags::LapseTimesConformalFactorMinusOne<DataType> /*meta*/) const {\n  const auto& lapse_times_conformal_factor =\n      get(cache->get_var(*this, Tags::LapseTimesConformalFactor<DataType>{}));\n  get(*lapse_times_conformal_factor_minus_one) =\n      lapse_times_conformal_factor - 1.;\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::i<DataType, 3>*>\n        deriv_lapse_times_conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  const auto& conformal_factor =\n      get(cache->get_var(*this, Tags::ConformalFactor<DataType>{}));\n  const auto& lapse = get(cache->get_var(*this, gr::Tags::Lapse<DataType>{}));\n  const auto& deriv_conformal_factor = cache->get_var(\n      *this, ::Tags::deriv<Tags::ConformalFactorMinusOne<DataType>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  const auto& deriv_lapse =\n      cache->get_var(*this, ::Tags::deriv<gr::Tags::Lapse<DataType>,\n                                          tmpl::size_t<3>, Frame::Inertial>{});\n  get<0>(*deriv_lapse_times_conformal_factor) =\n      lapse * get<0>(deriv_conformal_factor) +\n      get<0>(deriv_lapse) * conformal_factor;\n  get<1>(*deriv_lapse_times_conformal_factor) =\n      lapse * get<1>(deriv_conformal_factor) +\n      get<1>(deriv_lapse) * conformal_factor;\n  get<2>(*deriv_lapse_times_conformal_factor) =\n      lapse * get<2>(deriv_conformal_factor) +\n      get<2>(deriv_lapse) * conformal_factor;\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> shift_background,\n    const gsl::not_null<Cache*> /* cache */,\n    Tags::ShiftBackground<DataType, 3, Frame::Inertial> /*meta*/) const {\n  std::fill(shift_background->begin(), shift_background->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift_background,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Xcts::Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  std::fill(deriv_shift_background->begin(), deriv_shift_background->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::II<DataType, 3, Frame::Inertial>*>\n        longitudinal_shift_background_minus_dt_conformal_metric,\n    const gsl::not_null<Cache*> /* cache */,\n    Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n        DataType, 3, Frame::Inertial> /*meta*/) const {\n  std::fill(longitudinal_shift_background_minus_dt_conformal_metric->begin(),\n            longitudinal_shift_background_minus_dt_conformal_metric->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> shift_excess,\n    const gsl::not_null<Cache*> /* cache */,\n    Tags::ShiftExcess<DataType, 3, Frame::Inertial> /*meta*/) const {\n  std::fill(shift_excess->begin(), shift_excess->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift_excess,\n    const gsl::not_null<Cache*> /* cache */,\n    ::Tags::deriv<Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  std::fill(deriv_shift_excess->begin(), deriv_shift_excess->end(), 0.);\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> energy_density,\n    const gsl::not_null<Cache*> /* cache */,\n    gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>,\n                        ConformalMatterScale> /*meta*/) const {\n  *energy_density = get_tov_var(hydro::Tags::RestMassDensity<DataType>{});\n  get(*energy_density) *=\n      get(get_tov_var(hydro::Tags::SpecificEnthalpy<DataType>{}));\n  get(*energy_density) -= get(get_tov_var(hydro::Tags::Pressure<DataType>{}));\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<Scalar<DataType>*> stress_trace,\n    const gsl::not_null<Cache*> /* cache */,\n    gr::Tags::Conformal<gr::Tags::StressTrace<DataType>,\n                        ConformalMatterScale> /*meta*/) const {\n  get(*stress_trace) = 3. * get(get_tov_var(hydro::Tags::Pressure<DataType>{}));\n}\n\ntemplate <typename DataType>\nvoid TovVariables<DataType>::operator()(\n    const gsl::not_null<tnsr::I<DataType, 3>*> momentum_density,\n    const gsl::not_null<Cache*> /* cache */,\n    gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>,\n                        ConformalMatterScale> /*meta*/) const {\n  std::fill(momentum_density->begin(), momentum_density->end(), 0.);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data) template class TovVariables<DTYPE(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace Xcts::Solutions::tov_detail\n\nPUP::able::PUP_ID Xcts::Solutions::TovStar::my_PUP_ID = 0;  // NOLINT\n\n// Instantiate implementations for common variables\ntemplate class Xcts::Solutions::CommonVariables<\n    double, typename Xcts::Solutions::tov_detail::TovVariablesCache<double>>;\ntemplate class Xcts::Solutions::CommonVariables<\n    DataVector,\n    typename Xcts::Solutions::tov_detail::TovVariablesCache<DataVector>>;\ntemplate class Xcts::AnalyticData::CommonVariables<\n    double, typename Xcts::Solutions::tov_detail::TovVariablesCache<double>>;\ntemplate class Xcts::AnalyticData::CommonVariables<\n    DataVector,\n    typename Xcts::Solutions::tov_detail::TovVariablesCache<DataVector>>;\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/TovStar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <ostream>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace Xcts::Solutions {\nnamespace tov_detail {\n\nusing TovCoordinates = RelativisticEuler::Solutions::TovCoordinates;\n\ntemplate <typename DataType>\nusing TovVariablesCache = cached_temp_buffer_from_typelist<tmpl::push_back<\n    common_tags<DataType>,\n    ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>, Frame::Inertial>,\n    gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0>,\n    gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0>,\n    gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>, 0>>>;\n\ntemplate <typename DataType>\nstruct TovVariables : CommonVariables<DataType, TovVariablesCache<DataType>> {\n  static constexpr size_t Dim = 3;\n  static constexpr int ConformalMatterScale = 0;\n  using Cache = TovVariablesCache<DataType>;\n  using Base = CommonVariables<DataType, TovVariablesCache<DataType>>;\n  using Base::operator();\n\n  const tnsr::I<DataType, 3>& x;\n  const DataType& radius;\n  const RelativisticEuler::Solutions::TovStar& tov_star;\n\n  TovVariables(\n      std::optional<std::reference_wrapper<const Mesh<Dim>>> local_mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, Dim, Frame::ElementLogical, Frame::Inertial>>>\n          local_inv_jacobian,\n      const tnsr::I<DataType, 3>& local_x, const DataType& local_radius,\n      const RelativisticEuler::Solutions::TovStar& local_tov_star)\n      : Base(std::move(local_mesh), std::move(local_inv_jacobian)),\n        x(local_x),\n        radius(local_radius),\n        tov_star(local_tov_star) {}\n\n  void operator()(gsl::not_null<tnsr::ii<DataType, 3>*> conformal_metric,\n                  gsl::not_null<Cache*> cache,\n                  Tags::ConformalMetric<DataType, 3, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::II<DataType, 3>*> inv_conformal_metric,\n      gsl::not_null<Cache*> cache,\n      Tags::InverseConformalMetric<DataType, 3, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::ijj<DataType, 3>*> deriv_conformal_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ConformalMetric<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::ii<DataType, 3>*> extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::ExtrinsicCurvature<DataType, 3> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> trace_extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, 3>*> deriv_trace_extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataType>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> dt_trace_extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/)\n      const override;\n  void operator()(gsl::not_null<Scalar<DataType>*> conformal_factor,\n                  gsl::not_null<Cache*> cache,\n                  Tags::ConformalFactor<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> conformal_factor_minus_one,\n      gsl::not_null<Cache*> cache,\n      Tags::ConformalFactorMinusOne<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, 3>*> deriv_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(gsl::not_null<Scalar<DataType>*> lapse,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Lapse<DataType> /*meta*/) const override;\n  void operator()(gsl::not_null<tnsr::i<DataType, 3>*> deriv_lapse,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      Tags::LapseTimesConformalFactor<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<Cache*> cache,\n      Tags::LapseTimesConformalFactorMinusOne<DataType> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, 3>*> deriv_lapse_times_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(gsl::not_null<tnsr::I<DataType, 3>*> shift_background,\n                  gsl::not_null<Cache*> cache,\n                  Tags::ShiftBackground<DataType, 3, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::iJ<DataType, 3, Frame::Inertial>*>\n          deriv_shift_background,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(gsl::not_null<tnsr::II<DataType, 3, Frame::Inertial>*>\n                      longitudinal_shift_background_minus_dt_conformal_metric,\n                  gsl::not_null<Cache*> cache,\n                  Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n                      DataType, 3, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, 3>*> shift_excess,\n      gsl::not_null<Cache*> cache,\n      Tags::ShiftExcess<DataType, 3, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift_excess,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(gsl::not_null<Scalar<DataType>*> energy_density,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>,\n                                      ConformalMatterScale> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataType>*> stress_trace,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Conformal<gr::Tags::StressTrace<DataType>,\n                                      ConformalMatterScale> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, 3>*> momentum_density,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>,\n                                      ConformalMatterScale> /*meta*/) const;\n\n private:\n  template <typename Tag>\n  typename Tag::type get_tov_var(Tag /*meta*/) const {\n    // Possible optimization: Access the cache of the RelEuler::TovStar solution\n    // so its intermediate quantities don't have to be re-computed repeatedly\n    return get<Tag>(tov_star.variables(\n        x, std::numeric_limits<double>::signaling_NaN(), tmpl::list<Tag>{}));\n  }\n};\n\n}  // namespace tov_detail\n\n/*!\n * \\brief TOV solution to the XCTS equations\n *\n * \\see RelativisticEuler::Solutions::TovStar\n * \\see gr::Solutions::TovSolution\n */\nclass TovStar : public elliptic::analytic_data::AnalyticSolution {\n private:\n  using RelEulerTovStar = RelativisticEuler::Solutions::TovStar;\n\n public:\n  using options = RelEulerTovStar::options;\n  static constexpr Options::String help = RelEulerTovStar::help;\n\n  TovStar() = default;\n  TovStar(const TovStar&) = default;\n  TovStar& operator=(const TovStar&) = default;\n  TovStar(TovStar&&) = default;\n  TovStar& operator=(TovStar&&) = default;\n  ~TovStar() = default;\n\n  TovStar(double central_rest_mass_density,\n          std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>\n              equation_of_state,\n          const RelativisticEuler::Solutions::TovCoordinates coordinate_system)\n      : tov_star(central_rest_mass_density, std::move(equation_of_state),\n                 coordinate_system) {}\n\n  const EquationsOfState::EquationOfState<true, 1>& equation_of_state() const {\n    return tov_star.equation_of_state();\n  }\n\n  const RelativisticEuler::Solutions::TovSolution& radial_solution() const {\n    return tov_star.radial_solution();\n  }\n\n  /// \\cond\n  explicit TovStar(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TovStar);\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<TovStar>(*this);\n  }\n  /// \\endcond\n\n  template <typename DataType>\n  using tags = typename tov_detail::TovVariablesCache<DataType>::tags_list;\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables_impl<DataType>(x, std::nullopt, std::nullopt,\n                                    tmpl::list<RequestedTags...>{});\n  }\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x, const Mesh<3>& mesh,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables_impl<DataVector>(x, mesh, inv_jacobian,\n                                      tmpl::list<RequestedTags...>{});\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::AnalyticSolution::pup(p);\n    p | tov_star;\n  }\n\n private:\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables_impl(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      std::optional<std::reference_wrapper<const Mesh<3>>> mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, 3, Frame::ElementLogical, Frame::Inertial>>>\n          inv_jacobian,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    using VarsComputer = tov_detail::TovVariables<DataType>;\n    typename VarsComputer::Cache cache{get_size(*x.begin())};\n    const DataType radius = get(magnitude(x));\n    const VarsComputer computer{std::move(mesh), std::move(inv_jacobian), x,\n                                radius, tov_star};\n    using unrequested_hydro_tags =\n        tmpl::list_difference<hydro_tags<DataType>,\n                              tmpl::list<RequestedTags...>>;\n    using requested_hydro_tags =\n        tmpl::list_difference<hydro_tags<DataType>, unrequested_hydro_tags>;\n    tuples::tagged_tuple_from_typelist<requested_hydro_tags> hydro_vars;\n    if constexpr (not std::is_same_v<requested_hydro_tags, tmpl::list<>>) {\n      hydro_vars =\n          tov_star.variables(x, std::numeric_limits<double>::signaling_NaN(),\n                             requested_hydro_tags{});\n    }\n    const auto get_var = [&cache, &computer, &hydro_vars](auto tag_v) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      if constexpr (tmpl::list_contains_v<hydro_tags<DataType>, tag>) {\n        (void)cache;\n        (void)computer;\n        return get<tag>(hydro_vars);\n      } else {\n        (void)hydro_vars;\n        return cache.get_var(computer, tag{});\n      }\n    };\n    return {get_var(RequestedTags{})...};\n  }\n\n  friend bool operator==(const TovStar& lhs, const TovStar& rhs) {\n    return lhs.tov_star == rhs.tov_star;\n  }\n\n  // Instead of inheriting from the RelEuler::TovStar we use an aggregate\n  // pattern to avoid multiple-inheritance issues.\n  RelativisticEuler::Solutions::TovStar tov_star{};\n};\n\ninline bool operator!=(const TovStar& lhs, const TovStar& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/Options.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.tpp\"\n#include \"PointwiseFunctions/Elasticity/Strain.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/Hydro/ComovingMagneticField.hpp\"\n#include \"PointwiseFunctions/Hydro/StressEnergy.hpp\"\n#include \"PointwiseFunctions/Xcts/LongitudinalOperator.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts::Solutions {\nnamespace detail {\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, Dim>*> spatial_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::SpatialMetric<DataType, Dim> /*meta*/) const {\n  *spatial_metric = get<gr::Tags::SpatialMetric<DataType, Dim>>(gr_solution);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::II<DataType, Dim>*> inv_spatial_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::InverseSpatialMetric<DataType, Dim> /*meta*/) const {\n  *inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataType, Dim>>(gr_solution);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_spatial_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>, tmpl::size_t<Dim>,\n                  Frame::Inertial> /*meta*/) const {\n  *deriv_spatial_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                        tmpl::size_t<Dim>, Frame::Inertial>>(gr_solution);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, Dim>*> conformal_metric,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::ConformalMetric<DataType, Dim, Frame::Inertial> /*meta*/)\n    const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{});\n  *conformal_metric = get<gr::Tags::SpatialMetric<DataType, Dim>>(gr_solution);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      conformal_metric->get(i, j) /= pow<4>(get(conformal_factor));\n    }\n  }\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::II<DataType, Dim>*> inv_conformal_metric,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::InverseConformalMetric<DataType, Dim, Frame::Inertial> /*meta*/)\n    const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{});\n  *inv_conformal_metric =\n      get<gr::Tags::InverseSpatialMetric<DataType, Dim>>(gr_solution);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      inv_conformal_metric->get(i, j) *= pow<4>(get(conformal_factor));\n    }\n  }\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_conformal_metric,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<Xcts::Tags::ConformalMetric<DataType, Dim, Frame::Inertial>,\n                  tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{});\n  const auto& deriv_conformal_factor = cache->get_var(\n      *this, ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                           tmpl::size_t<Dim>, Frame::Inertial>{});\n  const auto& conformal_metric = cache->get_var(\n      *this, Xcts::Tags::ConformalMetric<DataType, Dim, Frame::Inertial>{});\n  *deriv_conformal_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                        tmpl::size_t<Dim>, Frame::Inertial>>(gr_solution);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      for (size_t k = 0; k <= j; ++k) {\n        deriv_conformal_metric->get(i, j, k) /= pow<4>(get(conformal_factor));\n        deriv_conformal_metric->get(i, j, k) -= 4. / get(conformal_factor) *\n                                                conformal_metric.get(j, k) *\n                                                deriv_conformal_factor.get(i);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*> trace_extrinsic_curvature,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) const {\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataType, Dim>>(gr_solution);\n  const auto& inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataType, Dim>>(gr_solution);\n  trace(trace_extrinsic_curvature, extrinsic_curvature, inv_spatial_metric);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*> dt_trace_extrinsic_curvature,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/) const {\n  get(*dt_trace_extrinsic_curvature) = 0.;\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::ConformalFactor<DataType> /*meta*/) const {\n  get(*conformal_factor) = 1.;\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*> conformal_factor_minus_one,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::ConformalFactorMinusOne<DataType> /*meta*/) const {\n  get(*conformal_factor_minus_one) = 0.;\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::i<DataType, Dim>*> conformal_factor_gradient,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                  tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const {\n  std::fill(conformal_factor_gradient->begin(),\n            conformal_factor_gradient->end(), 0.);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::Lapse<DataType> /*meta*/) const {\n  *lapse = get<gr::Tags::Lapse<DataType>>(gr_solution);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::LapseTimesConformalFactor<DataType> /*meta*/) const {\n  *lapse_times_conformal_factor =\n      cache->get_var(*this, gr::Tags::Lapse<DataType>{});\n  const auto& conformal_factor =\n      cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{});\n  get(*lapse_times_conformal_factor) *= get(conformal_factor);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*>\n        lapse_times_conformal_factor_minus_one,\n    const gsl::not_null<Cache*> cache,\n    Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType> /*meta*/) const {\n  *lapse_times_conformal_factor_minus_one =\n      cache->get_var(*this, Xcts::Tags::LapseTimesConformalFactor<DataType>{});\n  get(*lapse_times_conformal_factor_minus_one) -= 1.;\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::i<DataType, Dim>*>\n        lapse_times_conformal_factor_gradient,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                  tmpl::size_t<Dim>, Frame::Inertial> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Xcts::Tags::ConformalFactor<DataType>{});\n  const auto& deriv_conformal_factor = cache->get_var(\n      *this, ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                           tmpl::size_t<Dim>, Frame::Inertial>{});\n  *lapse_times_conformal_factor_gradient =\n      get<::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>,\n                        Frame::Inertial>>(gr_solution);\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(gr_solution);\n  for (size_t i = 0; i < Dim; ++i) {\n    lapse_times_conformal_factor_gradient->get(i) *= get(conformal_factor);\n    lapse_times_conformal_factor_gradient->get(i) +=\n        get(lapse) * deriv_conformal_factor.get(i);\n  }\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> shift_background,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::ShiftBackground<DataType, Dim, Frame::Inertial> /*meta*/)\n    const {\n  std::fill(shift_background->begin(), shift_background->end(), 0.);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, 3>*> deriv_shift_background,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Xcts::Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  std::fill(deriv_shift_background->begin(), deriv_shift_background->end(), 0.);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::II<DataType, Dim, Frame::Inertial>*>\n        longitudinal_shift_background_minus_dt_conformal_metric,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n        DataType, Dim, Frame::Inertial> /*meta*/) const {\n  std::fill(longitudinal_shift_background_minus_dt_conformal_metric->begin(),\n            longitudinal_shift_background_minus_dt_conformal_metric->end(), 0.);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> shift_excess,\n    const gsl::not_null<Cache*> /*cache*/,\n    Xcts::Tags::ShiftExcess<DataType, Dim, Frame::Inertial> /*meta*/) const {\n  *shift_excess = get<gr::Tags::Shift<DataType, Dim>>(gr_solution);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::iJ<DataType, Dim>*> deriv_shift_excess,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  *deriv_shift_excess =\n      get<::Tags::deriv<gr::Tags::Shift<DataType, Dim>, tmpl::size_t<Dim>,\n                        Frame::Inertial>>(gr_solution);\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> extrinsic_curvature,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::ExtrinsicCurvature<DataType, 3> /*meta*/) const {\n  *extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataType, Dim>>(gr_solution);\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsuggest-attribute=noreturn\"\n#endif  // defined(__GNUC__) && !defined(__clang__)\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    [[maybe_unused]] const gsl::not_null<Scalar<DataType>*>\n        magnetic_field_dot_spatial_velocity,\n    const gsl::not_null<Cache*> /*cache*/,\n    hydro::Tags::MagneticFieldDotSpatialVelocity<DataType> /*meta*/) const {\n  if constexpr (HasMhd) {\n    const auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataType, Dim, Frame::Inertial>>(\n            hydro_solution);\n    const auto& magnetic_field =\n        get<hydro::Tags::MagneticField<DataType, Dim, Frame::Inertial>>(\n            hydro_solution);\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataType, Dim>>(gr_solution);\n    tenex::evaluate(magnetic_field_dot_spatial_velocity,\n                    magnetic_field(ti::I) * spatial_velocity(ti::J) *\n                        spatial_metric(ti::i, ti::j));\n  } else {\n    ERROR(\n        \"The 'MagneticFieldDotSpatialVelocity' should not be needed in vacuum \"\n        \"GR.\");\n  }\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    [[maybe_unused]] const gsl::not_null<Scalar<DataType>*>\n        comoving_magnetic_field_squared,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    hydro::Tags::ComovingMagneticFieldSquared<DataType> /*meta*/) const {\n  if constexpr (HasMhd) {\n    const auto& lorentz_factor =\n        get<hydro::Tags::LorentzFactor<DataType>>(hydro_solution);\n    const auto& magnetic_field =\n        get<hydro::Tags::MagneticField<DataType, Dim, Frame::Inertial>>(\n            hydro_solution);\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataType, Dim>>(gr_solution);\n    const auto magnetic_field_squared =\n        tenex::evaluate(magnetic_field(ti::I) * magnetic_field(ti::J) *\n                        spatial_metric(ti::i, ti::j));\n    const auto& magnetic_field_dot_spatial_velocity = cache->get_var(\n        *this, hydro::Tags::MagneticFieldDotSpatialVelocity<DataType>{});\n    hydro::comoving_magnetic_field_squared(\n        comoving_magnetic_field_squared, magnetic_field_squared,\n        magnetic_field_dot_spatial_velocity, lorentz_factor);\n  } else {\n    ERROR(\n        \"The 'ComovingMagneticFieldSquared' should not be needed in vacuum \"\n        \"GR.\");\n  }\n}\n\n#if defined(__GNUC__) && !defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__)\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*> energy_density,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0> /*meta*/) const {\n  if constexpr (HasMhd) {\n    const auto& rest_mass_density =\n        get<hydro::Tags::RestMassDensity<DataType>>(hydro_solution);\n    const auto& specific_enthalpy =\n        get<hydro::Tags::SpecificEnthalpy<DataType>>(hydro_solution);\n    const auto& pressure = get<hydro::Tags::Pressure<DataType>>(hydro_solution);\n    const auto& lorentz_factor =\n        get<hydro::Tags::LorentzFactor<DataType>>(hydro_solution);\n    const auto& magnetic_field_dot_spatial_velocity = cache->get_var(\n        *this, hydro::Tags::MagneticFieldDotSpatialVelocity<DataType>{});\n    const auto& comoving_magnetic_field_squared = cache->get_var(\n        *this, hydro::Tags::ComovingMagneticFieldSquared<DataType>{});\n    hydro::energy_density(energy_density, rest_mass_density, specific_enthalpy,\n                          pressure, lorentz_factor,\n                          magnetic_field_dot_spatial_velocity,\n                          comoving_magnetic_field_squared);\n  } else {\n    std::fill(energy_density->begin(), energy_density->end(), 0.);\n  }\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<Scalar<DataType>*> stress_trace,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0> /*meta*/) const {\n  if constexpr (HasMhd) {\n    const auto& rest_mass_density =\n        get<hydro::Tags::RestMassDensity<DataType>>(hydro_solution);\n    const auto& specific_enthalpy =\n        get<hydro::Tags::SpecificEnthalpy<DataType>>(hydro_solution);\n    const auto& pressure = get<hydro::Tags::Pressure<DataType>>(hydro_solution);\n    const auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataType, Dim, Frame::Inertial>>(\n            hydro_solution);\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataType, Dim>>(gr_solution);\n    const auto spatial_velocity_squared =\n        tenex::evaluate(spatial_velocity(ti::I) * spatial_velocity(ti::J) *\n                        spatial_metric(ti::i, ti::j));\n    const auto& lorentz_factor =\n        get<hydro::Tags::LorentzFactor<DataType>>(hydro_solution);\n    const auto& magnetic_field_dot_spatial_velocity = cache->get_var(\n        *this, hydro::Tags::MagneticFieldDotSpatialVelocity<DataType>{});\n    const auto& comoving_magnetic_field_squared = cache->get_var(\n        *this, hydro::Tags::ComovingMagneticFieldSquared<DataType>{});\n    hydro::stress_trace(stress_trace, rest_mass_density, specific_enthalpy,\n                        pressure, spatial_velocity_squared, lorentz_factor,\n                        magnetic_field_dot_spatial_velocity,\n                        comoving_magnetic_field_squared);\n  } else {\n    std::fill(stress_trace->begin(), stress_trace->end(), 0.);\n  }\n}\n\ntemplate <typename DataType, bool HasMhd>\nvoid WrappedGrVariables<DataType, HasMhd>::operator()(\n    const gsl::not_null<tnsr::I<DataType, Dim>*> momentum_density,\n    [[maybe_unused]] const gsl::not_null<Cache*> cache,\n    gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, Dim>, 0> /*meta*/)\n    const {\n  if constexpr (HasMhd) {\n    const auto& rest_mass_density =\n        get<hydro::Tags::RestMassDensity<DataType>>(hydro_solution);\n    const auto& specific_enthalpy =\n        get<hydro::Tags::SpecificEnthalpy<DataType>>(hydro_solution);\n    const auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataType, Dim, Frame::Inertial>>(\n            hydro_solution);\n    const auto& lorentz_factor =\n        get<hydro::Tags::LorentzFactor<DataType>>(hydro_solution);\n    const auto& magnetic_field =\n        get<hydro::Tags::MagneticField<DataType, Dim, Frame::Inertial>>(\n            hydro_solution);\n    const auto& magnetic_field_dot_spatial_velocity = cache->get_var(\n        *this, hydro::Tags::MagneticFieldDotSpatialVelocity<DataType>{});\n    const auto& comoving_magnetic_field_squared = cache->get_var(\n        *this, hydro::Tags::ComovingMagneticFieldSquared<DataType>{});\n    hydro::momentum_density(momentum_density, rest_mass_density,\n                            specific_enthalpy, spatial_velocity, lorentz_factor,\n                            magnetic_field, magnetic_field_dot_spatial_velocity,\n                            comoving_magnetic_field_squared);\n  } else {\n    std::fill(momentum_density->begin(), momentum_density->end(), 0.);\n  }\n}\n\ntemplate class WrappedGrVariables<double, false>;\ntemplate class WrappedGrVariables<DataVector, false>;\ntemplate class WrappedGrVariables<double, true>;\ntemplate class WrappedGrVariables<DataVector, true>;\n\n}  // namespace detail\n\ntemplate <typename GrSolution, bool HasMhd>\nPUP::able::PUP_ID WrappedGr<GrSolution, HasMhd>::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace Xcts::Solutions\n\n// Instantiate implementations for common variables\ntemplate class Xcts::Solutions::CommonVariables<\n    double,\n    typename Xcts::Solutions::detail::WrappedGrVariables<double, false>::Cache>;\ntemplate class Xcts::Solutions::CommonVariables<\n    DataVector, typename Xcts::Solutions::detail::WrappedGrVariables<\n                    DataVector, false>::Cache>;\ntemplate class Xcts::AnalyticData::CommonVariables<\n    double,\n    typename Xcts::Solutions::detail::WrappedGrVariables<double, false>::Cache>;\ntemplate class Xcts::AnalyticData::CommonVariables<\n    DataVector, typename Xcts::Solutions::detail::WrappedGrVariables<\n                    DataVector, false>::Cache>;\n\n#define STYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE_GR(_, data) \\\n  template class Xcts::Solutions::WrappedGr<STYPE(data), false>;\n#define INSTANTIATE_GRMHD(_, data) \\\n  template class Xcts::Solutions::WrappedGr<STYPE(data), true>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_GR, (gr::Solutions::KerrSchild,\n                                         gr::Solutions::SphericalKerrSchild,\n                                         gr::Solutions::HarmonicSchwarzschild))\nGENERATE_INSTANTIATIONS(INSTANTIATE_GRMHD,\n                        (grmhd::AnalyticData::CcsnCollapse,\n                         grmhd::AnalyticData::MagnetizedTovStar,\n                         RelativisticEuler::Solutions::RotatingStar))\n\n#undef STYPE\n#undef INSTANTIATE_GR\n#undef INSTANTIATE_GRMHD\n"
  },
  {
    "path": "src/PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/CommonVariables.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Flatness.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::Solutions {\nnamespace detail {\n\ntemplate <typename DataType, size_t Dim>\nusing gr_solution_vars =\n    tmpl::list<gr::Tags::SpatialMetric<DataType, Dim>,\n               gr::Tags::InverseSpatialMetric<DataType, Dim>,\n               ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                             tmpl::size_t<Dim>, Frame::Inertial>,\n               gr::Tags::Lapse<DataType>,\n               ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>,\n                             Frame::Inertial>,\n               gr::Tags::Shift<DataType, Dim>,\n               ::Tags::deriv<gr::Tags::Shift<DataType, Dim>, tmpl::size_t<Dim>,\n                             Frame::Inertial>,\n               gr::Tags::ExtrinsicCurvature<DataType, Dim>>;\n\ntemplate <typename DataType>\nusing WrappedGrVariablesCache =\n    cached_temp_buffer_from_typelist<tmpl::push_back<\n        common_tags<DataType>,\n        hydro::Tags::MagneticFieldDotSpatialVelocity<DataType>,\n        hydro::Tags::ComovingMagneticFieldSquared<DataType>,\n        gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0>,\n        gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0>,\n        gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, 3>, 0>>>;\n\ntemplate <typename DataType, bool HasMhd>\nstruct WrappedGrVariables\n    : CommonVariables<DataType, WrappedGrVariablesCache<DataType>> {\n  static constexpr size_t Dim = 3;\n  using Cache = WrappedGrVariablesCache<DataType>;\n  using Base = CommonVariables<DataType, WrappedGrVariablesCache<DataType>>;\n  using Base::operator();\n\n  WrappedGrVariables(\n      std::optional<std::reference_wrapper<const Mesh<Dim>>> local_mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, Dim, Frame::ElementLogical, Frame::Inertial>>>\n          local_inv_jacobian,\n      const tnsr::I<DataType, 3>& local_x,\n      const tuples::tagged_tuple_from_typelist<gr_solution_vars<DataType, Dim>>&\n          local_gr_solution,\n      const tuples::tagged_tuple_from_typelist<hydro_tags<DataType>>&\n          local_hydro_solution)\n      : Base(std::move(local_mesh), std::move(local_inv_jacobian)),\n        x(local_x),\n        gr_solution(local_gr_solution),\n        hydro_solution(local_hydro_solution) {}\n\n  const tnsr::I<DataType, Dim>& x;\n  const tuples::tagged_tuple_from_typelist<gr_solution_vars<DataType, Dim>>&\n      gr_solution;\n  const tuples::tagged_tuple_from_typelist<hydro_tags<DataType>>&\n      hydro_solution;\n\n  void operator()(\n      gsl::not_null<tnsr::ii<DataType, Dim>*> conformal_metric,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ConformalMetric<DataType, Dim, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(gsl::not_null<tnsr::II<DataType, Dim>*> inv_conformal_metric,\n                  gsl::not_null<Cache*> cache,\n                  Xcts::Tags::InverseConformalMetric<\n                      DataType, Dim, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_conformal_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ConformalMetric<DataType, Dim, Frame::Inertial>,\n                    tmpl::size_t<Dim>, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::ii<DataType, Dim>*> spatial_metric,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::SpatialMetric<DataType, Dim> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::II<DataType, Dim>*> inv_spatial_metric,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::InverseSpatialMetric<DataType, Dim> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::ijj<DataType, Dim>*> deriv_spatial_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>, tmpl::size_t<Dim>,\n                    Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> trace_extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::TraceExtrinsicCurvature<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> dt_trace_extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      ::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataType>> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> conformal_factor,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ConformalFactor<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> conformal_factor_minus_one,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ConformalFactorMinusOne<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, Dim>*> conformal_factor_gradient,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataType>,\n                    tmpl::size_t<Dim>, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(gsl::not_null<Scalar<DataType>*> lapse,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Lapse<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor_minus_one,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> lapse_times_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::LapseTimesConformalFactor<DataType> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::i<DataType, Dim>*>\n          lapse_times_conformal_factor_gradient,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::LapseTimesConformalFactorMinusOne<DataType>,\n                    tmpl::size_t<Dim>, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, Dim>*> shift_background,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ShiftBackground<DataType, Dim, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::iJ<DataType, 3, Frame::Inertial>*>\n          deriv_shift_background,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ShiftBackground<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(gsl::not_null<tnsr::II<DataType, Dim, Frame::Inertial>*>\n                      longitudinal_shift_background_minus_dt_conformal_metric,\n                  gsl::not_null<Cache*> cache,\n                  Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n                      DataType, Dim, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::I<DataType, Dim>*> shift_excess,\n      gsl::not_null<Cache*> cache,\n      Xcts::Tags::ShiftExcess<DataType, Dim, Frame::Inertial> /*meta*/)\n      const override;\n  void operator()(\n      gsl::not_null<tnsr::iJ<DataType, Dim>*> deriv_shift_excess,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Xcts::Tags::ShiftExcess<DataType, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<tnsr::ii<DataType, 3>*> extrinsic_curvature,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::ExtrinsicCurvature<DataType, 3> /*meta*/) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> magnetic_field_dot_spatial_velocity,\n      gsl::not_null<Cache*> cache,\n      hydro::Tags::MagneticFieldDotSpatialVelocity<DataType> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> comoving_magnetic_field_squared,\n      gsl::not_null<Cache*> cache,\n      hydro::Tags::ComovingMagneticFieldSquared<DataType> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> energy_density,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataType>, 0> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataType>*> stress_trace,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::Conformal<gr::Tags::StressTrace<DataType>, 0> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataType, Dim>*> momentum_density,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Conformal<gr::Tags::MomentumDensity<DataType, Dim>,\n                                      0> /*meta*/) const;\n};\n\n}  // namespace detail\n\n/*!\n * \\brief XCTS quantities for a solution of the Einstein equations\n *\n * This class computes all XCTS quantities from the `GrSolution`. To do so, it\n * chooses the conformal factor\n *\n * \\f{equation}{\n *   \\psi = 1\n *   \\text{,}\n * \\f}\n *\n * so the spatial metric of the `GrSolution` is used as conformal metric,\n * \\f$\\bar{\\gamma}_{ij = \\gamma_{ij}\\f$. This is particularly useful for\n * superpositions, because it means that the superposed conformal metric of two\n * `WrappedGr` solutions is probably a good conformal background to solve for a\n * binary solution (see Xcts::AnalyticData::Binary).\n *\n * For example, when the `GrSolution` is `gr::Solutions::KerrSchild`, the\n * conformal metric is the spatial Kerr metric in Kerr-Schild coordinates and\n * \\f$\\psi = 1\\f$. It is also possible to\n * choose a different \\f$\\psi\\f$ so the solution is non-trivial in this\n * variable, though that is probably only useful for testing and currently not\n * implemented. It should be noted, however, that the combination of\n * \\f$\\psi=1\\f$ and apparent-horizon boundary conditions poses a hard problem to\n * the nonlinear solver when starting at a flat initial guess. This is because\n * the strongly-nonlinear boundary-conditions couple the variables in such a way\n * that the solution is initially corrected away from \\f$\\psi=1\\f$ and is then\n * unable to recover. A conformal-factor profile such as \\f$\\psi=1 +\n * \\frac{M}{2r}\\f$ (resembling isotropic coordinates) resolves this issue. In\n * production solves this is not an issue because we choose a much better\n * initial guess than flatness, such as a superposition of Kerr solutions for\n * black-hole binary initial data.\n *\n * \\warning\n * The computation of the XCTS matter source terms (energy density $\\rho$,\n * momentum density $S^i$, stress trace $S$) uses GR quantities (lapse $\\alpha$,\n * shift $\\beta^i$, spatial metric $\\gamma_{ij}$), which means these GR\n * quantities are not treated dynamically in the source terms when solving the\n * XCTS equations. If the GR quantities satisfy the Einstein constraints (as is\n * the case if the `GrSolution` is actually a solution to the Einstein\n * equations), then the XCTS solve will reproduce the GR quantities given the\n * fixed sources computed here. However, if the GR quantities don't satisfy the\n * Einstein constraints (e.g. because a magnetic field was added to the solution\n * but ignored in the gravity sector, or because it is a hydrodynamic solution\n * on a fixed background metric) then the XCTS solution will depend on our\n * treatment of the source terms: fixing the source terms (the simple approach\n * taken here) means we're making a choice of $W$ and $u^i$. This is what\n * initial data codes usually do when they iterate back and forth between a\n * hydro solve and an XCTS solve (e.g. see \\cite Tacik2016zal). Alternatively,\n * we could fix $v^i$ and compute $W$ and $u^i$ from $v^i$ and the dynamic\n * metric variables at every step in the XCTS solver algorithm. This requires\n * adding the source terms and their linearization to the XCTS equations, and\n * could be interesting to explore.\n *\n * \\tparam GrSolution Any solution to the Einstein constraint equations\n * \\tparam HasMhd Enable to compute matter source terms. Disable to set matter\n * source terms to zero.\n */\n\ntemplate <typename GrSolution, bool HasMhd = false>\nclass WrappedGr;\n\ntemplate <typename GrSolution, bool HasMhd>\nclass WrappedGr : public elliptic::analytic_data::AnalyticSolution {\n public:\n  static constexpr size_t Dim = 3;\n\n  using options = typename GrSolution::options;\n  static constexpr Options::String help = GrSolution::help;\n  static std::string name() { return pretty_type::name<GrSolution>(); }\n\n  WrappedGr() = default;\n  WrappedGr(const WrappedGr&) = default;\n  WrappedGr& operator=(const WrappedGr&) = default;\n  WrappedGr(WrappedGr&&) = default;\n  WrappedGr& operator=(WrappedGr&&) = default;\n  ~WrappedGr() = default;\n\n  template <typename... Args,\n            Requires<std::is_constructible_v<GrSolution, Args...>> = nullptr>\n  explicit WrappedGr(Args&&... args)\n      : gr_solution_(std::forward<Args>(args)...) {}\n\n  const GrSolution& gr_solution() const { return gr_solution_; }\n\n  /// \\cond\n  explicit WrappedGr(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(WrappedGr);\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<WrappedGr>(*this);\n  }\n  /// \\endcond\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables_impl<DataType>(x, std::nullopt, std::nullopt,\n                                    tmpl::list<RequestedTags...>{});\n  }\n\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x, const Mesh<3>& mesh,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                            Frame::Inertial>& inv_jacobian,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables_impl<DataVector>(x, mesh, inv_jacobian,\n                                      tmpl::list<RequestedTags...>{});\n  }\n\n  void pup(PUP::er& p) override {\n    elliptic::analytic_data::AnalyticSolution::pup(p);\n    p | gr_solution_;\n  }\n\n private:\n  template <typename DataType, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables_impl(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x,\n      std::optional<std::reference_wrapper<const Mesh<3>>> mesh,\n      std::optional<std::reference_wrapper<const InverseJacobian<\n          DataType, 3, Frame::ElementLogical, Frame::Inertial>>>\n          inv_jacobian,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    tuples::tagged_tuple_from_typelist<detail::gr_solution_vars<DataType, Dim>>\n        gr_solution;\n    if constexpr (is_analytic_solution_v<GrSolution>) {\n      gr_solution = gr_solution_.variables(\n          x, std::numeric_limits<double>::signaling_NaN(),\n          detail::gr_solution_vars<DataType, Dim>{});\n    } else {\n      gr_solution =\n          gr_solution_.variables(x, detail::gr_solution_vars<DataType, Dim>{});\n    }\n    tuples::tagged_tuple_from_typelist<hydro_tags<DataType>> hydro_solution;\n    if constexpr (HasMhd) {\n      if constexpr (is_analytic_solution_v<GrSolution>) {\n        hydro_solution = gr_solution_.variables(\n            x, std::numeric_limits<double>::signaling_NaN(),\n            hydro_tags<DataType>{});\n      } else {\n        hydro_solution = gr_solution_.variables(x, hydro_tags<DataType>{});\n      }\n    }\n    using VarsComputer = detail::WrappedGrVariables<DataType, HasMhd>;\n    const size_t num_points = get_size(*x.begin());\n    typename VarsComputer::Cache cache{num_points};\n    VarsComputer computer{mesh, inv_jacobian, x, gr_solution, hydro_solution};\n    const auto get_var = [&cache, &computer, &hydro_solution, &x](auto tag_v) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      if constexpr (tmpl::list_contains_v<hydro_tags<DataType>, tag>) {\n        (void)cache;\n        (void)computer;\n        if constexpr (HasMhd) {\n          (void)x;\n          return get<tag>(hydro_solution);\n        } else {\n          (void)hydro_solution;\n          return get<tag>(Flatness{}.variables(x, tmpl::list<tag>{}));\n        }\n      } else {\n        (void)hydro_solution;\n        (void)x;\n        return cache.get_var(computer, tag{});\n      }\n    };\n    return {get_var(RequestedTags{})...};\n  }\n\n  friend bool operator==(const WrappedGr<GrSolution, HasMhd>& lhs,\n                         const WrappedGr<GrSolution, HasMhd>& rhs) {\n    return lhs.gr_solution_ == rhs.gr_solution_;\n  }\n\n  GrSolution gr_solution_;\n};\n\ntemplate <typename GrSolution, bool HasMhd>\ninline bool operator!=(const WrappedGr<GrSolution, HasMhd>& lhs,\n                       const WrappedGr<GrSolution, HasMhd>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <typename GrMhdSolution>\nusing WrappedGrMhd = WrappedGr<GrMhdSolution, true>;\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(AnalyticData)\nadd_subdirectory(AnalyticSolutions)\nadd_subdirectory(ConstraintDamping)\nadd_subdirectory(Elasticity)\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(Hydro)\nadd_subdirectory(InitialDataUtilities)\nadd_subdirectory(MathFunctions)\nadd_subdirectory(Punctures)\nadd_subdirectory(ScalarTensor)\nadd_subdirectory(SpecialRelativity)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ConstraintDamping)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Constant.cpp\n  GaussianPlusConstant.cpp\n  RegisterDerivedWithCharm.cpp\n  TimeDependentTripleGaussian.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Constant.hpp\n  DampingFunction.hpp\n  GaussianPlusConstant.hpp\n  RegisterDerivedWithCharm.hpp\n  TimeDependentTripleGaussian.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  ErrorHandling\n  Utilities\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/Constant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ConstraintDamping/Constant.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n\nnamespace ConstraintDamping {\ntemplate <size_t VolumeDim, typename Fr>\nConstant<VolumeDim, Fr>::Constant(CkMigrateMessage* msg)\n    : DampingFunction<VolumeDim, Fr>(msg) {}\n\ntemplate <size_t VolumeDim, typename Fr>\nConstant<VolumeDim, Fr>::Constant(const double value) : value_(value) {}\n\ntemplate <size_t VolumeDim, typename Fr>\ntemplate <typename T>\nvoid Constant<VolumeDim, Fr>::apply_call_operator(\n    const gsl::not_null<Scalar<T>*> value_at_x) const {\n  get(*value_at_x) = value_;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nvoid Constant<VolumeDim, Fr>::operator()(\n    const gsl::not_null<Scalar<double>*> value_at_x,\n    const tnsr::I<double, VolumeDim, Fr>& /*x*/, const double /*time*/,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/) const {\n  apply_call_operator(value_at_x);\n}\ntemplate <size_t VolumeDim, typename Fr>\nvoid Constant<VolumeDim, Fr>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> value_at_x,\n    const tnsr::I<DataVector, VolumeDim, Fr>& x, const double /*time*/,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/) const {\n  set_number_of_grid_points(value_at_x, x);\n  apply_call_operator(value_at_x);\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nvoid Constant<VolumeDim, Fr>::pup(PUP::er& p) {\n  DampingFunction<VolumeDim, Fr>::pup(p);\n  p | value_;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nauto Constant<VolumeDim, Fr>::get_clone() const\n    -> std::unique_ptr<DampingFunction<VolumeDim, Fr>> {\n  return std::make_unique<Constant<VolumeDim, Fr>>(*this);\n}\n}  // namespace ConstraintDamping\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define INSTANTIATE(_, data)                                              \\\n  template ConstraintDamping::Constant<DIM(data), FRAME(data)>::Constant( \\\n      CkMigrateMessage* msg);                                             \\\n  template ConstraintDamping::Constant<DIM(data), FRAME(data)>::Constant( \\\n      const double value);                                                \\\n  template void ConstraintDamping::Constant<DIM(data), FRAME(data)>::pup( \\\n      PUP::er& p);                                                        \\\n  template auto                                                           \\\n  ConstraintDamping::Constant<DIM(data), FRAME(data)>::get_clone()        \\\n      const->std::unique_ptr<DampingFunction<DIM(data), FRAME(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial))\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template void                                                    \\\n  ConstraintDamping::Constant<DIM(data), FRAME(data)>::operator()( \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> value_at_x,        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& /*x*/,   \\\n      const double /*time*/,                                       \\\n      const std::unordered_map<                                    \\\n          std::string,                                             \\\n          std::unique_ptr<::domain::FunctionsOfTime::              \\\n                              FunctionOfTime>>& /*functions_of_time*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n#undef FRAME\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/Constant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n/// \\endcond\n\nnamespace ConstraintDamping {\n/*!\n * \\brief A constant function: \\f$f = C\\f$\n *\n * \\details Input file options are: `Value` \\f$C\\f$. The function takes input\n * coordinates of type `tnsr::I<T, VolumeDim, Fr>`, where `T` is e.g. `double`\n * or `DataVector`, `Fr` is a frame (e.g. `Frame::Inertial`), and `VolumeDim` is\n * the dimension of the spatial volume.\n */\ntemplate <size_t VolumeDim, typename Fr>\nclass Constant : public DampingFunction<VolumeDim, Fr> {\n public:\n  struct Value {\n    using type = double;\n    static constexpr Options::String help = {\"The value.\"};\n  };\n  using options = tmpl::list<Value>;\n\n  static constexpr Options::String help = {\"Returns a constant value\"};\n\n  /// \\cond\n  WRAPPED_PUPable_decl_base_template(SINGLE_ARG(DampingFunction<VolumeDim, Fr>),\n                                     Constant);  // NOLINT\n\n  explicit Constant(CkMigrateMessage* msg);\n  /// \\endcond\n\n  Constant(double value);\n\n  Constant() = default;\n  ~Constant() override = default;\n  Constant(const Constant& /*rhs*/) = default;\n  Constant& operator=(const Constant& /*rhs*/) = default;\n  Constant(Constant&& /*rhs*/) = default;\n  Constant& operator=(Constant&& /*rhs*/) = default;\n\n  void operator()(\n      gsl::not_null<Scalar<double>*> value_at_x,\n      const tnsr::I<double, VolumeDim, Fr>& x, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> value_at_x,\n      const tnsr::I<DataVector, VolumeDim, Fr>& x, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const override;\n\n  auto get_clone() const\n      -> std::unique_ptr<DampingFunction<VolumeDim, Fr>> override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const Constant& lhs, const Constant& rhs) {\n    return lhs.value_ == rhs.value_;\n  }\n\n  template <typename T>\n  void apply_call_operator(const gsl::not_null<Scalar<T>*> value_at_x) const;\n\n  double value_ = std::numeric_limits<double>::signaling_NaN();\n};\n\ntemplate <size_t VolumeDim, typename Fr>\nbool operator!=(const Constant<VolumeDim, Fr>& lhs,\n                const Constant<VolumeDim, Fr>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace ConstraintDamping\n\n/// \\cond\ntemplate <size_t VolumeDim, typename Fr>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID ConstraintDamping::Constant<VolumeDim, Fr>::my_PUP_ID =\n    0;  // NOLINT\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/DampingFunction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n/// \\endcond\n\n/// Holds classes implementing DampingFunction (functions \\f$R^n \\to R\\f$).\nnamespace ConstraintDamping {\n/// \\cond\ntemplate <size_t VolumeDim, typename Fr>\nclass Constant;\ntemplate <size_t VolumeDim, typename Fr>\nclass GaussianPlusConstant;\nclass TimeDependentTripleGaussian;\n/// \\endcond\n\n/*!\n * \\brief Base class defining interface for constraint damping functions.\n *\n * Encodes a function \\f$R^n \\to R\\f$ where n is `VolumeDim` that represents\n * a generalized-harmonic constraint-damping parameter (i.e., Gamma0,\n * Gamma1, or Gamma2).\n */\ntemplate <size_t VolumeDim, typename Fr>\nclass DampingFunction : public PUP::able {\n public:\n  using creatable_classes = tmpl::conditional_t<\n      (VolumeDim == 3 and std::is_same<Fr, Frame::Grid>::value),\n      tmpl::list<ConstraintDamping::Constant<VolumeDim, Fr>,\n                 ConstraintDamping::GaussianPlusConstant<VolumeDim, Fr>,\n                 ConstraintDamping::TimeDependentTripleGaussian>,\n      tmpl::list<ConstraintDamping::GaussianPlusConstant<VolumeDim, Fr>,\n                 ConstraintDamping::Constant<VolumeDim, Fr>>>;\n  constexpr static size_t volume_dim = VolumeDim;\n  using frame = Fr;\n\n  WRAPPED_PUPable_abstract(DampingFunction);  // NOLINT\n\n  DampingFunction() = default;\n  DampingFunction(const DampingFunction& /*rhs*/) = default;\n  DampingFunction& operator=(const DampingFunction& /*rhs*/) = default;\n  DampingFunction(DampingFunction&& /*rhs*/) = default;\n  DampingFunction& operator=(DampingFunction&& /*rhs*/) = default;\n  ~DampingFunction() override = default;\n\n  explicit DampingFunction(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  /// @{\n  /// Returns the value of the function at the coordinate 'x'.\n  virtual void operator()(\n      const gsl::not_null<Scalar<double>*> value_at_x,\n      const tnsr::I<double, VolumeDim, Fr>& x, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const = 0;\n  virtual void operator()(\n      const gsl::not_null<Scalar<DataVector>*> value_at_x,\n      const tnsr::I<DataVector, VolumeDim, Fr>& x, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const = 0;\n  /// @}\n\n  virtual auto get_clone() const\n      -> std::unique_ptr<DampingFunction<VolumeDim, Fr>> = 0;\n};\n}  // namespace ConstraintDamping\n\n#include \"PointwiseFunctions/ConstraintDamping/Constant.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/GaussianPlusConstant.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/TimeDependentTripleGaussian.hpp\"\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/GaussianPlusConstant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ConstraintDamping/GaussianPlusConstant.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n\nnamespace ConstraintDamping {\ntemplate <size_t VolumeDim, typename Fr>\nGaussianPlusConstant<VolumeDim, Fr>::GaussianPlusConstant(CkMigrateMessage* msg)\n    : DampingFunction<VolumeDim, Fr>(msg) {}\n\ntemplate <size_t VolumeDim, typename Fr>\nGaussianPlusConstant<VolumeDim, Fr>::GaussianPlusConstant(\n    const double constant, const double amplitude, const double width,\n    const std::array<double, VolumeDim>& center)\n    : constant_(constant),\n      amplitude_(amplitude),\n      inverse_width_(1.0 / width),\n      center_(center) {}\n\ntemplate <size_t VolumeDim, typename Fr>\ntemplate <typename T>\nvoid GaussianPlusConstant<VolumeDim, Fr>::apply_call_operator(\n    const gsl::not_null<Scalar<T>*> value_at_x,\n    const tnsr::I<T, VolumeDim, Fr>& x) const {\n  tnsr::I<T, VolumeDim, Fr> centered_coords{x};\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    centered_coords.get(i) -= gsl::at(center_, i);\n  }\n  dot_product(value_at_x, centered_coords, centered_coords);\n  get(*value_at_x) =\n      constant_ + amplitude_ * exp(-get(*value_at_x) * square(inverse_width_));\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nvoid GaussianPlusConstant<VolumeDim, Fr>::operator()(\n    const gsl::not_null<Scalar<double>*> value_at_x,\n    const tnsr::I<double, VolumeDim, Fr>& x, const double /*time*/,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/) const {\n  apply_call_operator(value_at_x, x);\n}\ntemplate <size_t VolumeDim, typename Fr>\nvoid GaussianPlusConstant<VolumeDim, Fr>::operator()(\n    const gsl::not_null<Scalar<DataVector>*> value_at_x,\n    const tnsr::I<DataVector, VolumeDim, Fr>& x, const double /*time*/,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n    /*functions_of_time*/) const {\n  set_number_of_grid_points(value_at_x, x);\n  apply_call_operator(value_at_x, x);\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nvoid GaussianPlusConstant<VolumeDim, Fr>::pup(PUP::er& p) {\n  DampingFunction<VolumeDim, Fr>::pup(p);\n  p | constant_;\n  p | amplitude_;\n  p | inverse_width_;\n  p | center_;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nauto GaussianPlusConstant<VolumeDim, Fr>::get_clone() const\n    -> std::unique_ptr<DampingFunction<VolumeDim, Fr>> {\n  return std::make_unique<GaussianPlusConstant<VolumeDim, Fr>>(*this);\n}\n}  // namespace ConstraintDamping\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define INSTANTIATE(_, data)                                                   \\\n  template ConstraintDamping::GaussianPlusConstant<                            \\\n      DIM(data), FRAME(data)>::GaussianPlusConstant(CkMigrateMessage* msg);    \\\n  template ConstraintDamping::GaussianPlusConstant<DIM(data), FRAME(data)>::   \\\n      GaussianPlusConstant(const double constant, const double amplitude,      \\\n                           const double width,                                 \\\n                           const std::array<double, DIM(data)>& center);       \\\n  template void                                                                \\\n  ConstraintDamping::GaussianPlusConstant<DIM(data), FRAME(data)>::pup(        \\\n      PUP::er& p);                                                             \\\n  template auto                                                                \\\n  ConstraintDamping::GaussianPlusConstant<DIM(data), FRAME(data)>::get_clone() \\\n      const->std::unique_ptr<DampingFunction<DIM(data), FRAME(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial))\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void                                                                \\\n  ConstraintDamping::GaussianPlusConstant<DIM(data), FRAME(data)>::operator()( \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> value_at_x,                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x,                   \\\n      const double /*time*/,                                                   \\\n      const std::unordered_map<                                                \\\n          std::string,                                                         \\\n          std::unique_ptr<::domain::FunctionsOfTime::                          \\\n                              FunctionOfTime>>& /*functions_of_time*/) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n#undef FRAME\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/GaussianPlusConstant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n/// \\endcond\n\nnamespace ConstraintDamping {\n/*!\n * \\brief A Gaussian plus a constant: \\f$f = C + A\n * \\exp\\left(-\\frac{(x-x_0)^2}{w^2}\\right)\\f$\n *\n * \\details Input file options are: `Constant` \\f$C\\f$, `Amplitude` \\f$A\\f$,\n * `Width` \\f$w\\f$, and `Center`\\f$x_0\\f$. The function takes input coordinates\n * of type `tnsr::I<T, VolumeDim, Fr>`, where `T` is e.g. `double` or\n * `DataVector`, `Fr` is a frame (e.g. `Frame::Inertial`), and `VolumeDim` is\n * the dimension of the spatial volume.\n */\ntemplate <size_t VolumeDim, typename Fr>\nclass GaussianPlusConstant : public DampingFunction<VolumeDim, Fr> {\n public:\n  struct Constant {\n    using type = double;\n    static constexpr Options::String help = {\"The constant.\"};\n  };\n\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help = {\"The amplitude of the Gaussian.\"};\n  };\n\n  struct Width {\n    using type = double;\n    static constexpr Options::String help = {\"The width of the Gaussian.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  struct Center {\n    using type = std::array<double, VolumeDim>;\n    static constexpr Options::String help = {\"The center of the Gaussian.\"};\n  };\n  using options = tmpl::list<Constant, Amplitude, Width, Center>;\n\n  static constexpr Options::String help = {\n      \"Computes a Gaussian plus a constant about an arbitrary coordinate \"\n      \"center with given width and amplitude\"};\n\n  /// \\cond\n  WRAPPED_PUPable_decl_base_template(SINGLE_ARG(DampingFunction<VolumeDim, Fr>),\n                                     GaussianPlusConstant);  // NOLINT\n\n  explicit GaussianPlusConstant(CkMigrateMessage* msg);\n  /// \\endcond\n\n  GaussianPlusConstant(double constant, double amplitude, double width,\n                       const std::array<double, VolumeDim>& center);\n\n  GaussianPlusConstant() = default;\n  ~GaussianPlusConstant() override = default;\n  GaussianPlusConstant(const GaussianPlusConstant& /*rhs*/) = default;\n  GaussianPlusConstant& operator=(const GaussianPlusConstant& /*rhs*/) =\n      default;\n  GaussianPlusConstant(GaussianPlusConstant&& /*rhs*/) = default;\n  GaussianPlusConstant& operator=(GaussianPlusConstant&& /*rhs*/) = default;\n\n  void operator()(\n      gsl::not_null<Scalar<double>*> value_at_x,\n      const tnsr::I<double, VolumeDim, Fr>& x, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> value_at_x,\n      const tnsr::I<DataVector, VolumeDim, Fr>& x, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const override;\n\n  auto get_clone() const\n      -> std::unique_ptr<DampingFunction<VolumeDim, Fr>> override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const GaussianPlusConstant& lhs,\n                         const GaussianPlusConstant& rhs) {\n    return lhs.constant_ == rhs.constant_ and\n           lhs.amplitude_ == rhs.amplitude_ and\n           lhs.inverse_width_ == rhs.inverse_width_ and\n           lhs.center_ == rhs.center_;\n  }\n\n  double constant_ = std::numeric_limits<double>::signaling_NaN();\n  double amplitude_ = std::numeric_limits<double>::signaling_NaN();\n  double inverse_width_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, VolumeDim> center_{};\n\n  template <typename T>\n  void apply_call_operator(gsl::not_null<Scalar<T>*> value_at_x,\n                           const tnsr::I<T, VolumeDim, Fr>& x) const;\n};\n\ntemplate <size_t VolumeDim, typename Fr>\nbool operator!=(const GaussianPlusConstant<VolumeDim, Fr>& lhs,\n                const GaussianPlusConstant<VolumeDim, Fr>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace ConstraintDamping\n\n/// \\cond\ntemplate <size_t VolumeDim, typename Fr>\nPUP::able::PUP_ID\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    ConstraintDamping::GaussianPlusConstant<VolumeDim, Fr>::my_PUP_ID =\n        0;  // NOLINT\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.hpp\"\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace ConstraintDamping {\nnamespace {\ntemplate <size_t Dim, typename Fr>\nvoid register_damping_functions_with_charm() {\n  register_derived_classes_with_charm<DampingFunction<Dim, Fr>>();\n}\n}  // namespace\n\nvoid register_derived_with_charm() {\n  register_damping_functions_with_charm<1, Frame::Grid>();\n  register_damping_functions_with_charm<2, Frame::Grid>();\n  register_damping_functions_with_charm<3, Frame::Grid>();\n  register_damping_functions_with_charm<1, Frame::Inertial>();\n  register_damping_functions_with_charm<2, Frame::Inertial>();\n  register_damping_functions_with_charm<3, Frame::Inertial>();\n}\n}  // namespace ConstraintDamping\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace ConstraintDamping {\nvoid register_derived_with_charm();\n}  // namespace ConstraintDamping\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/TimeDependentTripleGaussian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ConstraintDamping/TimeDependentTripleGaussian.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace ConstraintDamping {\nTimeDependentTripleGaussian::TimeDependentTripleGaussian(CkMigrateMessage* msg)\n    : DampingFunction<3, Frame::Grid>(msg) {}\n\nTimeDependentTripleGaussian::TimeDependentTripleGaussian(\n    const double constant, const double amplitude_1, const double width_1,\n    const std::optional<std::array<double, 3>>& center_1,\n    const double amplitude_2, const double width_2,\n    const std::optional<std::array<double, 3>>& center_2,\n    const double amplitude_3, const double width_3,\n    const std::array<double, 3>& center_3, const std::string& movement_method,\n    const Options::Context& context)\n    : constant_(constant),\n      amplitude_1_(amplitude_1),\n      inverse_width_1_(1.0 / width_1),\n      center_1_(center_1),\n      amplitude_2_(amplitude_2),\n      inverse_width_2_(1.0 / width_2),\n      center_2_(center_2),\n      amplitude_3_(amplitude_3),\n      inverse_width_3_(1.0 / width_3),\n      center_3_(center_3),\n      movement_method_(movement_method == \"ExpansionFactor\"\n                           ? MovementMethods::ExpansionFactor\n                           : MovementMethods::ObjectCenters) {\n  if (movement_method != \"ExpansionFactor\" and\n      movement_method != \"ObjectCenters\") {\n    PARSE_ERROR(\n        context,\n        \"The movement method must be either 'ExpansionFactor' (for BBH \"\n        \"simulations) or 'ObjectCenters' (for BNS simulations) but got '\"\n            << movement_method << \"'\");\n  }\n  if (movement_method == \"ObjectCenters\") {\n    if (center_1_.has_value()) {\n      PARSE_ERROR(context,\n                  \"You cannot set the Center of Gaussian1 when using the \"\n                  \"ObjectCenters movement method. The center is determine from \"\n                  \"the ObjectCenters function of time.\");\n    }\n    if (center_2_.has_value()) {\n      PARSE_ERROR(context,\n                  \"You cannot set the Center of Gaussian2 when using the \"\n                  \"ObjectCenters movement method. The center is determine from \"\n                  \"the ObjectCenters function of time.\");\n    }\n  } else {\n    if (not center_1_.has_value()) {\n      PARSE_ERROR(context,\n                  \"You must set the Center of Gaussian1 when using the \"\n                  \"ExpansionFactor movement method.\");\n    }\n    if (not center_2_.has_value()) {\n      PARSE_ERROR(context,\n                  \"You must set the Center of Gaussian1 when using the \"\n                  \"ExpansionFactor movement method.\");\n    }\n  }\n}\n\ntemplate <typename T>\nvoid TimeDependentTripleGaussian::apply_call_operator(\n    const gsl::not_null<Scalar<T>*> value_at_x,\n    const tnsr::I<T, 3, Frame::Grid>& x, const double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  // Start by setting the result to the constant\n  get(*value_at_x) = constant_;\n\n  // Loop over the three Gaussians, adding each to the result\n  auto centered_coords = make_with_value<tnsr::I<T, 3, Frame::Grid>>(\n      get<0>(x), std::numeric_limits<double>::signaling_NaN());\n\n  const auto add_gauss_to_value_at_x =\n      [&centered_coords, &x, &functions_of_time, time, this, &value_at_x](\n          const double amplitude, const double inverse_width,\n          const std::array<double, 3>& center) {\n        for (size_t i = 0; i < 3; ++i) {\n          centered_coords.get(i) = x.get(i) - gsl::at(center, i);\n        }\n        if (this->movement_method_ == MovementMethods::ExpansionFactor) {\n          ASSERT(functions_of_time.at(function_of_time_for_scaling_)\n                         ->func(time)[0]\n                         .size() == 1,\n                 \"FunctionOfTimeForScaling in TimeDependentTripleGaussian must \"\n                 \"be a scalar FunctionOfTime, not \"\n                     << functions_of_time.at(function_of_time_for_scaling_)\n                            ->func(time)[0]\n                            .size());\n          const double expansion_factor_value =\n              functions_of_time.at(function_of_time_for_scaling_)\n                  ->func(time)[0][0];\n          get(*value_at_x) +=\n              amplitude *\n              exp(-get(dot_product(centered_coords, centered_coords)) *\n                  square(inverse_width * expansion_factor_value));\n        } else {\n          get(*value_at_x) +=\n              amplitude *\n              exp(-get(dot_product(centered_coords, centered_coords)) *\n                  square(inverse_width));\n        }\n      };\n  if (this->movement_method_ == MovementMethods::ExpansionFactor) {\n    add_gauss_to_value_at_x(amplitude_1_, inverse_width_1_, center_1_.value());\n    add_gauss_to_value_at_x(amplitude_2_, inverse_width_2_, center_2_.value());\n  } else {\n    const DataVector centers =\n        functions_of_time.at(function_of_time_for_centers_)->func(time)[0];\n    ASSERT(centers.size() == 6,\n           \"FunctionOfTimeForCenters in TimeDependentTripleGaussian must have \"\n           \"6 components, not \"\n               << functions_of_time.at(function_of_time_for_centers_)\n                      ->func(time)[0]\n                      .size());\n    const std::array center_1{centers[0], centers[1], centers[2]};\n    const std::array center_2{centers[3], centers[4], centers[5]};\n    add_gauss_to_value_at_x(amplitude_1_, inverse_width_1_, center_1);\n    add_gauss_to_value_at_x(amplitude_2_, inverse_width_2_, center_2);\n  }\n  // Gaussian 3 should be the one centered at the origin in a binary simulation.\n  add_gauss_to_value_at_x(amplitude_3_, inverse_width_3_, center_3_);\n}  // namespace gh::ConstraintDamping\n\nvoid TimeDependentTripleGaussian::operator()(\n    const gsl::not_null<Scalar<double>*> value_at_x,\n    const tnsr::I<double, 3, Frame::Grid>& x, const double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  apply_call_operator(value_at_x, x, time, functions_of_time);\n}\nvoid TimeDependentTripleGaussian::operator()(\n    const gsl::not_null<Scalar<DataVector>*> value_at_x,\n    const tnsr::I<DataVector, 3, Frame::Grid>& x, const double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) const {\n  set_number_of_grid_points(value_at_x, x);\n  apply_call_operator(value_at_x, x, time, functions_of_time);\n}\n\nvoid TimeDependentTripleGaussian::pup(PUP::er& p) {\n  DampingFunction<3, Frame::Grid>::pup(p);\n  p | constant_;\n  p | amplitude_1_;\n  p | inverse_width_1_;\n  p | center_1_;\n  p | amplitude_2_;\n  p | inverse_width_2_;\n  p | center_2_;\n  p | amplitude_3_;\n  p | inverse_width_3_;\n  p | center_3_;\n  p | movement_method_;\n}\n\nauto TimeDependentTripleGaussian::get_clone() const\n    -> std::unique_ptr<DampingFunction<3, Frame::Grid>> {\n  return std::make_unique<TimeDependentTripleGaussian>(*this);\n}\n\nbool operator==(const TimeDependentTripleGaussian& lhs,\n                const TimeDependentTripleGaussian& rhs) {\n  return lhs.constant_ == rhs.constant_ and\n         lhs.amplitude_1_ == rhs.amplitude_1_ and\n         lhs.inverse_width_1_ == rhs.inverse_width_1_ and\n         lhs.center_1_ == rhs.center_1_ and\n         lhs.amplitude_2_ == rhs.amplitude_2_ and\n         lhs.inverse_width_2_ == rhs.inverse_width_2_ and\n         lhs.center_2_ == rhs.center_2_ and\n         lhs.amplitude_3_ == rhs.amplitude_3_ and\n         lhs.inverse_width_3_ == rhs.inverse_width_3_ and\n         lhs.center_3_ == rhs.center_3_ and\n         lhs.movement_method_ == rhs.movement_method_;\n}\n\nbool operator!=(const TimeDependentTripleGaussian& lhs,\n                const TimeDependentTripleGaussian& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace ConstraintDamping\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID ConstraintDamping::TimeDependentTripleGaussian::my_PUP_ID =\n    0;  // NOLINT\n"
  },
  {
    "path": "src/PointwiseFunctions/ConstraintDamping/TimeDependentTripleGaussian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n/// \\endcond\n\nnamespace ConstraintDamping {\n/*!\n * \\brief A sum of three Gaussians plus a constant, where the Gaussian widths\n * are scaled by a domain::FunctionsOfTime::FunctionOfTime.\n *\n * \\details The function \\f$f\\f$ is given by\n * \\f{align}{\n * f = C + \\sum_{\\alpha=1}^3\n * A_\\alpha \\exp\\left(-\\frac{(x-(x_0)_\\alpha)^2}{w_\\alpha^2(t)}\\right).\n * \\f}\n * Input file options are: `Constant` \\f$C\\f$, `Amplitude[1-3]`\n * \\f$A_\\alpha\\f$, `Width[1-3]` \\f$w_\\alpha\\f$, and `Center[1-3]\n * `\\f$(x_0)_\\alpha\\f$. The function takes input\n * coordinates \\f$x\\f$ of type `tnsr::I<T, 3, Frame::Grid>`, where `T` is e.g.\n * `double` or `DataVector`; note that this DampingFunction is only defined\n * for three spatial dimensions and for the grid frame. The Gaussian widths\n * \\f$w_\\alpha\\f$ are scaled by the inverse of the value of a scalar\n * domain::FunctionsOfTime::FunctionOfTime \\f$f(t)\\f$: \\f$w_\\alpha(t) = w_\\alpha\n * / f(t)\\f$.\n *\n * You can choose one of two methods for tracking the object\n * centers. `ExpansionFactor` should be used for BBH simulations where the\n * expansion control system is used to track the objects. `ObjectCenters`\n * sholud be used for BNS simulations where the coordinate centers of the two\n * stars is tracked separately and there is no expansion control system.\n */\nclass TimeDependentTripleGaussian : public DampingFunction<3, Frame::Grid> {\n private:\n  enum class MovementMethods { ExpansionFactor, ObjectCenters };\n\n public:\n  template <size_t GaussianNumber>\n  struct Gaussian {\n    static constexpr Options::String help = {\n        \"Parameters for one of the Gaussians.\"};\n    static std::string name() {\n      return \"Gaussian\" + std::to_string(GaussianNumber);\n    };\n  };\n  struct Constant {\n    using type = double;\n    static constexpr Options::String help = {\"The constant.\"};\n  };\n\n  template <typename Group>\n  struct Amplitude {\n    using group = Group;\n    using type = double;\n    static constexpr Options::String help = {\"The amplitude of the Gaussian.\"};\n  };\n\n  template <typename Group>\n  struct Width {\n    using group = Group;\n    using type = double;\n    static constexpr Options::String help = {\n        \"The unscaled width of the Gaussian.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  template <typename Group>\n  struct Center {\n    using group = Group;\n    using type = tmpl::conditional_t<std::is_same_v<Group, Gaussian<3>>,\n                                     std::array<double, 3>,\n                                     Options::Auto<std::array<double, 3>>>;\n    static constexpr Options::String help = {\"The center of the Gaussian.\"};\n  };\n\n  /// \\brief How to track the movement of the compact objects.\n  ///\n  /// - `ExpansionFactor` for BBH simulations.\n  /// - `ObjectCenters` for BNS simulations.\n  struct MovementMethod {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"How to track the movement of the compact objects.\\n\\n\"\n        \"- `ExpansionFactor` for BBH simulations.\\n\"\n        \"- `ObjectCenters` for BNS simulations.\"};\n  };\n\n  using options =\n      tmpl::list<Constant, Amplitude<Gaussian<1>>, Width<Gaussian<1>>,\n                 Center<Gaussian<1>>, Amplitude<Gaussian<2>>,\n                 Width<Gaussian<2>>, Center<Gaussian<2>>,\n                 Amplitude<Gaussian<3>>, Width<Gaussian<3>>,\n                 Center<Gaussian<3>>, MovementMethod>;\n\n  static constexpr Options::String help = {\n      \"Computes a sum of a constant and 3 Gaussians (each with its own \"\n      \"amplitude, width, and coordinate center), with the Gaussian widths \"\n      \"scaled by the inverse of a FunctionOfTime.\"};\n\n  /// \\cond\n  WRAPPED_PUPable_decl_base_template(\n      SINGLE_ARG(DampingFunction<3, Frame::Grid>),\n      TimeDependentTripleGaussian);  // NOLINT\n  /// \\endcond\n\n  explicit TimeDependentTripleGaussian(CkMigrateMessage* msg);\n\n  TimeDependentTripleGaussian(\n      double constant, double amplitude_1, double width_1,\n      const std::optional<std::array<double, 3>>& center_1, double amplitude_2,\n      double width_2, const std::optional<std::array<double, 3>>& center_2,\n      double amplitude_3, double width_3, const std::array<double, 3>& center_3,\n      const std::string& movement_method, const Options::Context& context = {});\n\n  TimeDependentTripleGaussian() = default;\n  ~TimeDependentTripleGaussian() override = default;\n  TimeDependentTripleGaussian(const TimeDependentTripleGaussian& /*rhs*/) =\n      default;\n  TimeDependentTripleGaussian& operator=(\n      const TimeDependentTripleGaussian& /*rhs*/) = default;\n  TimeDependentTripleGaussian(TimeDependentTripleGaussian&& /*rhs*/) = default;\n  TimeDependentTripleGaussian& operator=(\n      TimeDependentTripleGaussian&& /*rhs*/) = default;\n\n  void operator()(\n      gsl::not_null<Scalar<double>*> value_at_x,\n      const tnsr::I<double, 3, Frame::Grid>& x, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const override;\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> value_at_x,\n      const tnsr::I<DataVector, 3, Frame::Grid>& x, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const override;\n\n  auto get_clone() const\n      -> std::unique_ptr<DampingFunction<3, Frame::Grid>> override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  friend bool operator==(const TimeDependentTripleGaussian& lhs,\n                         const TimeDependentTripleGaussian& rhs);\n\n  double constant_ = std::numeric_limits<double>::signaling_NaN();\n  double amplitude_1_ = std::numeric_limits<double>::signaling_NaN();\n  double inverse_width_1_ = std::numeric_limits<double>::signaling_NaN();\n  std::optional<std::array<double, 3>> center_1_{};\n  double amplitude_2_ = std::numeric_limits<double>::signaling_NaN();\n  double inverse_width_2_ = std::numeric_limits<double>::signaling_NaN();\n  std::optional<std::array<double, 3>> center_2_{};\n  double amplitude_3_ = std::numeric_limits<double>::signaling_NaN();\n  double inverse_width_3_ = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> center_3_{};\n  MovementMethods movement_method_{MovementMethods::ExpansionFactor};\n  inline static const std::string function_of_time_for_scaling_{\"Expansion\"};\n  inline static const std::string function_of_time_for_centers_{\"GridCenters\"};\n\n  template <typename T>\n  void apply_call_operator(\n      gsl::not_null<Scalar<T>*> value_at_x, const tnsr::I<T, 3, Frame::Grid>& x,\n      double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n};\n\nbool operator!=(const TimeDependentTripleGaussian& lhs,\n                const TimeDependentTripleGaussian& rhs);\n}  // namespace ConstraintDamping\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ElasticityPointwiseFunctions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  PotentialEnergy.cpp\n  Strain.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  PotentialEnergy.hpp\n  Strain.hpp\n  Stress.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ConstitutiveRelations\n  DataStructures\n  Domain\n  Elasticity\n  Utilities\n  PRIVATE\n  LinearOperators\n  )\n\nadd_subdirectory(ConstitutiveRelations)\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/ConstitutiveRelations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ConstitutiveRelations)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CubicCrystal.cpp\n  IsotropicHomogeneous.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ConstitutiveRelation.hpp\n  CubicCrystal.hpp\n  Factory.hpp\n  IsotropicHomogeneous.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Options\n  Serialization\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Elasticity {\n/*!\n * \\brief Constitutive (stress-strain) relations that characterize the elastic\n * properties of a material\n */\nnamespace ConstitutiveRelations {\n\n/*!\n * \\brief Base class for constitutive (stress-strain) relations that\n * characterize the elastic properties of a material\n *\n * \\details A constitutive relation, in the context of elasticity, relates the\n * Stress \\f$T^{ij}\\f$ and Strain \\f$S_{ij}=\\nabla_{(i}u_{j)}\\f$ within an\n * elastic material (see \\ref Elasticity). For small stresses it is approximated\n * by the linear relation\n *\n * \\f[\n * T^{ij} = -Y^{ijkl}S_{kl}\n * \\f]\n *\n * (Eq. 11.17 in \\cite ThorneBlandford2017) that is referred to as _Hooke's\n * law_. The constitutive relation in this linear approximation is determined by\n * the elasticity (or _Young's_) tensor \\f$Y^{ijkl}=Y^{(ij)(kl)}=Y^{klij}\\f$\n * that generalizes a simple proportionality to a three-dimensional and\n * (possibly) anisotropic material.\n *\n * \\note We assume a Euclidean metric in Cartesian coordinates here (for now).\n */\ntemplate <size_t Dim>\nclass ConstitutiveRelation : public PUP::able {\n public:\n  static constexpr size_t volume_dim = Dim;\n\n  ConstitutiveRelation() = default;\n  ConstitutiveRelation(const ConstitutiveRelation&) = default;\n  ConstitutiveRelation& operator=(const ConstitutiveRelation&) = default;\n  ConstitutiveRelation(ConstitutiveRelation&&) = default;\n  ConstitutiveRelation& operator=(ConstitutiveRelation&&) = default;\n  ~ConstitutiveRelation() override = default;\n\n  WRAPPED_PUPable_abstract(ConstitutiveRelation);  // NOLINT\n\n  /// Returns a `std::unique_ptr` pointing to a copy of the\n  /// `ConstitutiveRelation`.\n  virtual std::unique_ptr<ConstitutiveRelation<Dim>> get_clone() const = 0;\n\n  /// The constitutive relation that characterizes the elastic properties of a\n  /// material\n  virtual void stress(gsl::not_null<tnsr::II<DataVector, Dim>*> stress,\n                      const tnsr::ii<DataVector, Dim>& strain,\n                      const tnsr::I<DataVector, Dim>& x) const = 0;\n};\n\n}  // namespace ConstitutiveRelations\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/ConstitutiveRelations/CubicCrystal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/CubicCrystal.hpp\"\n\n#include <array>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Elasticity::ConstitutiveRelations {\n\nCubicCrystal::CubicCrystal(const double c_11, const double c_12,\n                           const double c_44)\n    : c_11_(c_11), c_12_(c_12), c_44_(c_44) {\n  ASSERT(\n      c_11_ >= c_12_,\n      \"c_11 must be bigger than c_12, but are c_11=\"\n          << c_11 << \" and c_12=\" << c_12\n          << \". This is because the youngs_modulus \"\n             \"must be positive and the poisson ratio smaller or equal to 0.5.\");\n}\n\nstd::unique_ptr<ConstitutiveRelation<3>> CubicCrystal::get_clone() const {\n  return std::make_unique<CubicCrystal>(*this);\n}\n\nvoid CubicCrystal::stress(const gsl::not_null<tnsr::II<DataVector, 3>*> stress,\n                          const tnsr::ii<DataVector, 3>& strain,\n                          const tnsr::I<DataVector, 3>& /*x*/) const {\n  get<2, 2>(*stress) = -get<0, 0>(strain);\n  for (size_t i = 1; i < 3; ++i) {\n    stress->get(2, 2) -= strain.get(i, i);\n  }\n  stress->get(2, 2) *= c_12_;\n  for (size_t i = 0; i < 3; ++i) {\n    stress->get(i, i) = stress->get(2, 2) - (c_11_ - c_12_) * strain.get(i, i);\n    for (size_t j = 0; j < i; ++j) {\n      stress->get(i, j) = -2. * c_44_ * strain.get(i, j);\n    }\n  }\n}\n\ndouble CubicCrystal::c_11() const { return c_11_; }\n\ndouble CubicCrystal::c_12() const { return c_12_; }\n\ndouble CubicCrystal::c_44() const { return c_44_; }\n\n// through \\lambda = c_{12} = \\frac{E\\nu}{(1+\\nu)(1-2\\nu)}\ndouble CubicCrystal::youngs_modulus() const {\n  return (c_11_ + 2. * c_12_) * (c_11_ - c_12_) / (c_11_ + c_12_);\n}\n\ndouble CubicCrystal::poisson_ratio() const {\n  return 1. / (1. + (c_11_ / c_12_));\n}\n\nPUP::able::PUP_ID CubicCrystal::my_PUP_ID = 0;\n\nvoid CubicCrystal::pup(PUP::er& p) {\n  p | c_11_;\n  p | c_12_;\n  p | c_44_;\n}\n\nbool operator==(const CubicCrystal& lhs, const CubicCrystal& rhs) {\n  return lhs.c_11() == rhs.c_11() and lhs.c_12() == rhs.c_12() and\n         lhs.c_44() == rhs.c_44();\n}\n\nbool operator!=(const CubicCrystal& lhs, const CubicCrystal& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace Elasticity::ConstitutiveRelations\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/ConstitutiveRelations/CubicCrystal.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Elasticity::ConstitutiveRelations {\n\n/*!\n * \\brief A cubic crystalline material\n *\n * \\details For a cubic crystalline material the Elasticity tensor in the linear\n * constitutive relation \\f$T^{ij}=-Y^{ijkl}S_{kl}\\f$ reduces to\n *\n * \\f[\n * Y^{ijkl} =\n * \\begin{cases}\n * c_{11} & \\mathrm{for}\\; i=j=k=l \\\\\n * c_{12} & \\mathrm{for}\\; i=j,k=l,i \\neq k \\\\\n * c_{44} & \\mathrm{for}\\; i=k,j=l,i \\neq j \\;\\mathrm{or}\\; i=l,j=k,i\\neq j \\\\\n * \\end{cases}\n * \\f]\n *\n * with the three independent parameters: the \\f$\\mathrm{Lam\\acute{e}}\\f$\n * parameter \\f$\\lambda\\f$, the Shear modulus \\f$\\mu\\f$ and the %Poisson\n * ratio \\f$\\nu\\f$. In the parametrization chosen in this implementation we use\n * the experimental group parameters \\f$c_{11}\\f$, \\f$c_{12}\\f$ and\n * \\f$c_{44}\\f$, related by;\n *\n * \\f[\n * c_{11} = \\frac{1 - \\nu}{\\nu} \\lambda = \\frac{(1 - \\nu)E}{(1 + \\nu)(1 -\n * 2\\nu)}, \\quad\n * c_{12} = \\lambda = \\frac{E\\nu}{(1 + \\nu)(1 - 2\\nu)}, \\quad\n * c_{44} = \\mu\n * \\f]\n *\n * and inversely;\n *\n * \\f[\n * E = \\frac{(c_{11} + 2c_{12})(c_{11} - c_{12})}{c_{11} + c_{12}}, \\quad\n * \\nu = \\left(1 + \\frac{c_{11}}{c_{12}}\\right)^{-1}, \\quad\n * \\mu = c_{44}\n * \\f]\n *\n * The stress-strain relation then reduces to\n *\n * \\f[\n * T^{ij} =\n * \\begin{cases}\n * -(c_{11} - c_{12}) S^{ij} - c_{12} \\mathrm{Tr}(S) & \\mathrm{for}\\; i=j \\\\\n * -2 c_{44} S^{ij} & \\mathrm{for}\\; i \\neq j \\\\\n * \\end{cases}\n * \\f]\n *\n * In the case where the shear modulus satisfies \\f$c_{44} =\n * \\frac{c_{11}-c_{12}}{2}\\f$ the constitutive relation is that of an isotropic\n * material (see `Elasticity::ConstitutiveRelations::IsotropicHomogeneous`).\n */\n\nclass CubicCrystal : public ConstitutiveRelation<3> {\n public:\n  static constexpr size_t volume_dim = 3;\n\n  struct C_11 {\n    using type = double;\n    static constexpr Options::String help = {\n        \"c_11 parameter for a cubic crystal\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct C_12 {\n    using type = double;\n    static constexpr Options::String help = {\n        \"c_12 parameter for a cubic crystal\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct C_44 {\n    using type = double;\n    static constexpr Options::String help = {\n        \"c_44 parameter for a cubic crystal\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options = tmpl::list<C_11, C_12, C_44>;\n\n  static constexpr Options::String help = {\n      \"A constitutive relation that describes a cubic, crystalline material in \"\n      \"terms of the three independent group paremeters. The parameters \"\n      \"are measured in units of stress, typically Pascals.\"};\n\n  CubicCrystal() = default;\n  CubicCrystal(const CubicCrystal&) = default;\n  CubicCrystal& operator=(const CubicCrystal&) = default;\n  CubicCrystal(CubicCrystal&&) = default;\n  CubicCrystal& operator=(CubicCrystal&&) = default;\n  ~CubicCrystal() override = default;\n\n  CubicCrystal(double c_11, double c_12, double c_44);\n\n  std::unique_ptr<ConstitutiveRelation<3>> get_clone() const override;\n\n  /// The constitutive relation that characterizes the elastic properties of a\n  /// material\n  void stress(gsl::not_null<tnsr::II<DataVector, 3>*> stress,\n              const tnsr::ii<DataVector, 3>& strain,\n              const tnsr::I<DataVector, 3>& x) const override;\n\n  /// The 1st group parameter \\f$c_{11} = \\frac{1 - \\nu}{\\nu} \\lambda\\f$\n  double c_11() const;\n  /// The 2nd group parameter; the \\f$\\mathrm{Lam\\acute{e}}\\f$ parameter\n  /// \\f$c_{12} = \\lambda\\f$\n  double c_12() const;\n  /// The 3rd group parameter; the shear modulus (rigidity) \\f$c_{44} = \\mu\\f$\n  double c_44() const;\n  /// The Young's modulus \\f$E\\f$\n  double youngs_modulus() const;\n  /// The %Poisson ratio \\f$\\nu\\f$\n  double poisson_ratio() const;\n\n  // clang-tidy: no runtime references\n  void pup(PUP::er& /*p*/) override;  //  NOLINT\n\n  explicit CubicCrystal(CkMigrateMessage* /*unused*/) {}\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(ConstitutiveRelation<3>), CubicCrystal);\n\n private:\n  double c_11_ = std::numeric_limits<double>::signaling_NaN();\n  double c_12_ = std::numeric_limits<double>::signaling_NaN();\n  double c_44_ = std::numeric_limits<double>::signaling_NaN();\n};  // namespace ConstitutiveRelations\n\nbool operator==(const CubicCrystal& lhs, const CubicCrystal& rhs);\nbool operator!=(const CubicCrystal& lhs, const CubicCrystal& rhs);\n\n}  // namespace Elasticity::ConstitutiveRelations\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/ConstitutiveRelations/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/CubicCrystal.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity::ConstitutiveRelations {\ntemplate <size_t Dim>\nusing standard_constitutive_relations = tmpl::append<\n    tmpl::list<IsotropicHomogeneous<Dim>>,\n    tmpl::conditional_t<Dim == 3, tmpl::list<CubicCrystal>, tmpl::list<>>>;\n}  // namespace Elasticity::ConstitutiveRelations\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n\n#include <array>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Elasticity::ConstitutiveRelations {\n\ntemplate <size_t Dim>\nIsotropicHomogeneous<Dim>::IsotropicHomogeneous(double bulk_modulus,\n                                                double shear_modulus)\n    : bulk_modulus_(bulk_modulus), shear_modulus_(shear_modulus) {}\n\ntemplate <size_t Dim>\nstd::unique_ptr<ConstitutiveRelation<Dim>>\nIsotropicHomogeneous<Dim>::get_clone() const {\n  return std::make_unique<IsotropicHomogeneous>(*this);\n}\n\ntemplate <>\nvoid IsotropicHomogeneous<3>::stress(\n    const gsl::not_null<tnsr::II<DataVector, 3>*> stress,\n    const tnsr::ii<DataVector, 3>& strain,\n    const tnsr::I<DataVector, 3>& /*x*/) const {\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      stress->get(i, j) = -2. * shear_modulus_ * strain.get(i, j);\n    }\n  }\n  auto trace_term = make_with_value<DataVector>(strain, 0.);\n  for (size_t i = 0; i < 3; ++i) {\n    trace_term += strain.get(i, i);\n  }\n  trace_term *= lame_parameter();\n  for (size_t i = 0; i < 3; ++i) {\n    stress->get(i, i) -= trace_term;\n  }\n}\n\ntemplate <>\nvoid IsotropicHomogeneous<2>::stress(\n    const gsl::not_null<tnsr::II<DataVector, 2>*> stress,\n    const tnsr::ii<DataVector, 2>& strain,\n    const tnsr::I<DataVector, 2>& /*x*/) const {\n  for (size_t i = 0; i < 2; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      stress->get(i, j) = -2. * shear_modulus_ * strain.get(i, j);\n    }\n  }\n  auto trace_term = make_with_value<DataVector>(strain, 0.);\n  for (size_t i = 0; i < 2; ++i) {\n    trace_term += strain.get(i, i);\n  }\n  trace_term *= 2. * (3. * bulk_modulus_ - 2. * shear_modulus_) *\n                shear_modulus_ / (3. * bulk_modulus_ + 4. * shear_modulus_);\n  for (size_t i = 0; i < 2; ++i) {\n    stress->get(i, i) -= trace_term;\n  }\n}\n\ntemplate <size_t Dim>\ndouble IsotropicHomogeneous<Dim>::bulk_modulus() const {\n  return bulk_modulus_;\n}\n\ntemplate <size_t Dim>\ndouble IsotropicHomogeneous<Dim>::shear_modulus() const {\n  return shear_modulus_;\n}\n\ntemplate <size_t Dim>\ndouble IsotropicHomogeneous<Dim>::lame_parameter() const {\n  return bulk_modulus_ - 2. * shear_modulus_ / 3.;\n}\n\ntemplate <size_t Dim>\ndouble IsotropicHomogeneous<Dim>::youngs_modulus() const {\n  return 9. * bulk_modulus_ * shear_modulus_ /\n         (3. * bulk_modulus_ + shear_modulus_);\n}\n\ntemplate <size_t Dim>\ndouble IsotropicHomogeneous<Dim>::poisson_ratio() const {\n  return (3. * bulk_modulus_ - 2. * shear_modulus_) /\n         (6. * bulk_modulus_ + 2. * shear_modulus_);\n}\n\ntemplate <size_t Dim>\nvoid IsotropicHomogeneous<Dim>::pup(PUP::er& p) {\n  p | bulk_modulus_;\n  p | shear_modulus_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const IsotropicHomogeneous<Dim>& lhs,\n                const IsotropicHomogeneous<Dim>& rhs) {\n  return lhs.bulk_modulus() == rhs.bulk_modulus() and\n         lhs.shear_modulus() == rhs.shear_modulus();\n}\ntemplate <size_t Dim>\nbool operator!=(const IsotropicHomogeneous<Dim>& lhs,\n                const IsotropicHomogeneous<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                            \\\n  template class IsotropicHomogeneous<DIM(data)>;                       \\\n  template bool operator==(const IsotropicHomogeneous<DIM(data)>& lhs,  \\\n                           const IsotropicHomogeneous<DIM(data)>& rhs); \\\n  template bool operator!=(const IsotropicHomogeneous<DIM(data)>& lhs,  \\\n                           const IsotropicHomogeneous<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n\n}  // namespace Elasticity::ConstitutiveRelations\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace Elasticity {\nnamespace ConstitutiveRelations {\n\n/*!\n * \\brief An isotropic and homogeneous material\n *\n * \\details For an isotropic and homogeneous material the linear constitutive\n * relation \\f$T^{ij}=-Y^{ijkl}S_{kl}\\f$ reduces to\n *\n * \\f[\n * Y^{ijkl} = \\lambda \\delta^{ij}\\delta^{kl} + \\mu\n * \\left(\\delta^{ik}\\delta^{jl} + \\delta^{il}\\delta^{jk}\\right) \\\\\n * \\implies \\quad T^{ij} = -\\lambda \\mathrm{Tr}(S) \\delta^{ij} - 2\\mu S^{ij}\n * \\f]\n *\n * with the _Lamé parameter_ \\f$\\lambda\\f$ and the _shear modulus_ (or\n * _rigidity_) \\f$\\mu\\f$. In the parametrization chosen in this implementation\n * we use the _bulk modulus_ (or _incompressibility_)\n *\n * \\f[\n * K=\\lambda + \\frac{2}{3}\\mu\n * \\f]\n *\n * instead of the Lamé parameter. In this parametrization the\n * stress-strain relation\n *\n * \\f[\n * T^{ij} = -K \\mathrm{Tr}(S) \\delta^{ij} - 2\\mu\\left(S^{ij} -\n * \\frac{1}{3}\\mathrm{Tr}(S)\\delta^{ij}\\right)\n * \\f]\n *\n * decomposes into a scalar and a traceless part (Eq. 11.18 in\n * \\cite ThorneBlandford2017). Parameters also often used in this context are\n * the _Young's modulus_\n *\n * \\f[\n * E=\\frac{9K\\mu}{3K+\\mu}=\\frac{\\mu(3\\lambda+2\\mu)}{\\lambda+\\mu}\n * \\f]\n *\n * and the _Poisson ratio_\n *\n * \\f[\n * \\nu=\\frac{3K-2\\mu}{2(3K+\\mu)}=\\frac{\\lambda}{2(\\lambda+\\mu)}\\text{.}\n * \\f]\n *\n * Inversely, these relations read:\n *\n * \\f[\n * K =\\frac{E}{3(1-2\\nu)}, \\quad\n * \\lambda =\\frac{E\\nu}{(1+\\nu)(1-2\\nu)}, \\quad\n * \\mu =\\frac{E}{2(1+\\nu)}\n * \\f]\n *\n * **In two dimensions** this implementation reduces to the plane-stress\n * approximation. We assume that all stresses apply in the plane of the\n * computational domain, which corresponds to scenarios of in-plane stretching\n * and shearing of thin slabs of material. Since orthogonal stresses vanish as\n * \\f$T^{i3}=0=T^{3i}\\f$ we find \\f$\\mathrm{Tr}(S)=\\frac{2\\mu}{\\lambda +\n * 2\\mu}\\mathrm{Tr}^{(2)}(S)\\f$, where \\f$\\mathrm{Tr}^{(2)}\\f$ denotes that the\n * trace only applies to the two dimensions within the plane. The constitutive\n * relation thus reduces to\n *\n * \\f{align}\n * T^{ij}=&-\\frac{2\\lambda\\mu}{\\lambda + 2\\mu}\\mathrm{Tr}^{(2)}(S)\\delta^{ij} -\n * 2\\mu S^{ij} \\\\\n * =&-\\frac{E\\nu}{1-\\nu^2}\\mathrm{Tr}^{(2)}(S)\\delta^{ij} -\n * \\frac{E}{1+\\nu}S^{ij}\n * \\f}\n *\n * which is non-zero only in the directions of the plane. Since the stresses\n * are also assumed to be constant along the thickness of the plane\n * \\f$\\partial_3T^{ij}=0\\f$ the elasticity problem \\f$-\\partial_i T^{ij}=F^j\\f$\n * reduces to two dimensions.\n */\ntemplate <size_t Dim>\nclass IsotropicHomogeneous : public ConstitutiveRelation<Dim> {\n public:\n  static constexpr size_t volume_dim = Dim;\n\n  struct BulkModulus {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The incompressibility of the material\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct ShearModulus {\n    using type = double;\n    static constexpr Options::String help = {\"The rigidity of the material\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  using options = tmpl::list<BulkModulus, ShearModulus>;\n\n  static constexpr Options::String help = {\n      \"A constitutive relation that describes an isotropic, homogeneous \"\n      \"material in terms of two elastic moduli. These bulk and shear moduli \"\n      \"indicate the material's resistance to volume and shape changes, \"\n      \"respectively. Both are measured in units of stress, typically Pascals.\"};\n\n  IsotropicHomogeneous() = default;\n  IsotropicHomogeneous(const IsotropicHomogeneous&) = default;\n  IsotropicHomogeneous& operator=(const IsotropicHomogeneous&) = default;\n  IsotropicHomogeneous(IsotropicHomogeneous&&) = default;\n  IsotropicHomogeneous& operator=(IsotropicHomogeneous&&) = default;\n  ~IsotropicHomogeneous() override = default;\n\n  IsotropicHomogeneous(double bulk_modulus, double shear_modulus);\n\n  std::unique_ptr<ConstitutiveRelation<Dim>> get_clone() const override;\n\n  /// The constitutive relation that characterizes the elastic properties of a\n  /// material\n  void stress(gsl::not_null<tnsr::II<DataVector, Dim>*> stress,\n              const tnsr::ii<DataVector, Dim>& strain,\n              const tnsr::I<DataVector, Dim>& x) const override;\n\n  /// The bulk modulus (or incompressibility) \\f$K\\f$\n  double bulk_modulus() const;\n  /// The shear modulus (or rigidity) \\f$\\mu\\f$\n  double shear_modulus() const;\n  /// The Lamé parameter \\f$\\lambda\\f$\n  double lame_parameter() const;\n  /// The Young's modulus \\f$E\\f$\n  double youngs_modulus() const;\n  /// The Poisson ratio \\f$\\nu\\f$\n  double poisson_ratio() const;\n\n  // clang-tidy: no runtime references\n  void pup(PUP::er& /*p*/) override;  //  NOLINT\n\n  explicit IsotropicHomogeneous(CkMigrateMessage* /*unused*/) {}\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(ConstitutiveRelation<Dim>), IsotropicHomogeneous);\n\n private:\n  double bulk_modulus_ = std::numeric_limits<double>::signaling_NaN();\n  double shear_modulus_ = std::numeric_limits<double>::signaling_NaN();\n};\n\ntemplate <size_t Dim>\nbool operator==(const IsotropicHomogeneous<Dim>& lhs,\n                const IsotropicHomogeneous<Dim>& rhs);\ntemplate <size_t Dim>\nbool operator!=(const IsotropicHomogeneous<Dim>& lhs,\n                const IsotropicHomogeneous<Dim>& rhs);\n\n/// \\cond\ntemplate <size_t Dim>\nPUP::able::PUP_ID IsotropicHomogeneous<Dim>::my_PUP_ID = 0;\n/// \\endcond\n\n}  // namespace ConstitutiveRelations\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity {\n\nnamespace OptionTags {\ntemplate <size_t Dim>\nstruct ConstitutiveRelation : db::SimpleTag {\n  static std::string name() { return \"Material\"; }\n  static constexpr Options::String help =\n      \"The constitutive relation of the elastic material.\";\n  using type =\n      std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>;\n};\n\ntemplate <size_t Dim>\nstruct ConstitutiveRelationPerBlock {\n  static std::string name() { return \"Material\"; }\n  using ConstRelPtr =\n      std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>;\n  using type = std::variant<ConstRelPtr, std::vector<ConstRelPtr>,\n                            std::unordered_map<std::string, ConstRelPtr>>;\n  static constexpr Options::String help =\n      \"A constitutive relation in every block of the domain.\";\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n\n/*!\n * \\brief The elastic material's constitutive relation.\n *\n * \\see `Elasticity::ConstitutiveRelations::ConstitutiveRelation`\n */\ntemplate <size_t Dim>\nstruct ConstitutiveRelation : db::SimpleTag {\n  using type =\n      std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>;\n\n  using option_tags = tmpl::list<OptionTags::ConstitutiveRelation<Dim>>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) {\n    return serialize_and_deserialize<type>(value);\n  }\n};\n}  // namespace Tags\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/PotentialEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Elasticity/PotentialEnergy.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace Elasticity {\n\ntemplate <size_t Dim>\nvoid potential_energy_density(const gsl::not_null<Scalar<DataVector>*> result,\n                              const tnsr::ii<DataVector, Dim>& strain,\n                              const tnsr::II<DataVector, Dim>& stress) {\n  set_number_of_grid_points(result, strain);\n  get(*result) = 0.;\n  for (size_t i = 0; i < stress.size(); ++i) {\n    get(*result) -= stress.multiplicity(i) * stress[i] * strain[i];\n  }\n  get(*result) *= 0.5;\n}\n\ntemplate <size_t Dim>\nvoid potential_energy_density(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const tnsr::ii<DataVector, Dim>& strain,\n    const tnsr::I<DataVector, Dim>& coordinates,\n    const ConstitutiveRelations::ConstitutiveRelation<Dim>&\n        constitutive_relation) {\n  tnsr::II<DataVector, Dim> stress{coordinates.begin()->size()};\n  constitutive_relation.stress(make_not_null(&stress), strain, coordinates);\n  potential_energy_density(result, strain, stress);\n}\n\ntemplate <size_t Dim>\nScalar<DataVector> potential_energy_density(\n    const tnsr::ii<DataVector, Dim>& strain,\n    const tnsr::I<DataVector, Dim>& coordinates,\n    const ConstitutiveRelations::ConstitutiveRelation<Dim>&\n        constitutive_relation) {\n  Scalar<DataVector> result =\n      make_with_value<Scalar<DataVector>>(coordinates, 0.);\n  potential_energy_density(make_not_null(&result), strain, coordinates,\n                           constitutive_relation);\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                        \\\n  template void potential_energy_density<DIM(data)>(                \\\n      gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const tnsr::ii<DataVector, DIM(data)>& strain,                \\\n      const tnsr::II<DataVector, DIM(data)>& stress);               \\\n  template void potential_energy_density<DIM(data)>(                \\\n      gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const tnsr::ii<DataVector, DIM(data)>& strain,                \\\n      const tnsr::I<DataVector, DIM(data)>& coordinates,            \\\n      const ConstitutiveRelations::ConstitutiveRelation<DIM(data)>& \\\n          constitutive_relation);                                   \\\n  template Scalar<DataVector> potential_energy_density<DIM(data)>(  \\\n      const tnsr::ii<DataVector, DIM(data)>& strain,                \\\n      const tnsr::I<DataVector, DIM(data)>& coordinates,            \\\n      const ConstitutiveRelations::ConstitutiveRelation<DIM(data)>& \\\n          constitutive_relation);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/PotentialEnergy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp\"\n\nnamespace Elasticity {\n\n/// @{\n/*!\n * \\brief The potential energy density \\f$U=-\\frac{1}{2}S_{ij}T^{ij}\\f$ stored\n * in the deformation of the elastic material (see Eq. (11.25) in\n * \\cite ThorneBlandford2017)\n *\n * Note that the two-dimensional instantiation of this function assumes that\n * only the terms of \\f$S_{ij}T^{ij}\\f$ where both \\f$i\\f$ and \\f$j\\f$\n * correspond to one of the computational dimensions contribute to the sum. This\n * is the case for the plane-stress approximation employed in the\n * two-dimensional `Elasticity::ConstitutiveRelations::IsotropicHomogeneous`,\n * for example, where \\f$T^{i3}=0=T^{3i}\\f$.\n */\ntemplate <size_t Dim>\nvoid potential_energy_density(gsl::not_null<Scalar<DataVector>*> result,\n                              const tnsr::ii<DataVector, Dim>& strain,\n                              const tnsr::II<DataVector, Dim>& stress);\n\ntemplate <size_t Dim>\nvoid potential_energy_density(\n    gsl::not_null<Scalar<DataVector>*> result,\n    const tnsr::ii<DataVector, Dim>& strain,\n    const tnsr::I<DataVector, Dim>& coordinates,\n    const ConstitutiveRelations::ConstitutiveRelation<Dim>&\n        constitutive_relation);\n\ntemplate <size_t Dim>\nScalar<DataVector> potential_energy_density(\n    const tnsr::ii<DataVector, Dim>& strain,\n    const tnsr::I<DataVector, Dim>& coordinates,\n    const ConstitutiveRelations::ConstitutiveRelation<Dim>&\n        constitutive_relation);\n/// @}\n\nnamespace Tags {\n\n/// \\brief Computes the energy density stored in the deformation of the elastic\n/// material.\n/// \\see `Elasticity::Tags::PotentialEnergyDensity`\ntemplate <size_t Dim>\nstruct PotentialEnergyDensityCompute\n    : Elasticity::Tags::PotentialEnergyDensity<Dim>,\n      db::ComputeTag {\n  using base = Elasticity::Tags::PotentialEnergyDensity<Dim>;\n  using return_type = Scalar<DataVector>;\n  using argument_tags =\n      tmpl::list<Elasticity::Tags::Strain<Dim>, Elasticity::Tags::Stress<Dim>>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*>, const tnsr::ii<DataVector, Dim>&,\n      const tnsr::II<DataVector, Dim>&)>(&potential_energy_density<Dim>);\n};\n\n}  // namespace Tags\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/Strain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Elasticity/Strain.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Elasticity {\n\ntemplate <typename DataType, size_t Dim>\nvoid strain(const gsl::not_null<tnsr::ii<DataType, Dim>*> strain,\n            const tnsr::iJ<DataType, Dim>& deriv_displacement) {\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      strain->get(i, j) =\n          0.5 * (deriv_displacement.get(i, j) + deriv_displacement.get(j, i));\n    }\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid strain(const gsl::not_null<tnsr::ii<DataType, Dim>*> strain,\n            const tnsr::iJ<DataType, Dim>& deriv_displacement,\n            const tnsr::ii<DataType, Dim>& metric,\n            const tnsr::ijj<DataType, Dim>& deriv_metric,\n            const tnsr::ijj<DataType, Dim>& christoffel_first_kind,\n            const tnsr::I<DataType, Dim>& displacement) {\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      // Unroll k=0 iteration of the loop below to avoid filling the result with\n      // zeros initially\n      strain->get(i, j) =\n          0.5 * (metric.get(j, 0) * deriv_displacement.get(i, 0) +\n                 get<0>(displacement) * deriv_metric.get(i, j, 0) +\n                 metric.get(i, 0) * deriv_displacement.get(j, 0) +\n                 get<0>(displacement) * deriv_metric.get(j, i, 0)) -\n          christoffel_first_kind.get(0, i, j) * get<0>(displacement);\n      for (size_t k = 1; k < Dim; ++k) {\n        strain->get(i, j) +=\n            0.5 * (metric.get(j, k) * deriv_displacement.get(i, k) +\n                   displacement.get(k) * deriv_metric.get(i, j, k) +\n                   metric.get(i, k) * deriv_displacement.get(j, k) +\n                   displacement.get(k) * deriv_metric.get(j, i, k)) -\n            christoffel_first_kind.get(k, i, j) * displacement.get(k);\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid strain(const gsl::not_null<tnsr::ii<DataVector, Dim>*> strain,\n            const tnsr::I<DataVector, Dim>& displacement, const Mesh<Dim>& mesh,\n            const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>& inv_jacobian) {\n  Elasticity::strain(strain,\n                     partial_derivative(displacement, mesh, inv_jacobian));\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void strain<DIM(data)>(                                        \\\n      gsl::not_null<tnsr::ii<DataVector, DIM(data)>*> strain,             \\\n      const tnsr::I<DataVector, DIM(data)>& displacement,                 \\\n      const Mesh<DIM(data)>& mesh,                                        \\\n      const InverseJacobian<DataVector, DIM(data), Frame::ElementLogical, \\\n                            Frame::Inertial>& inv_jacobian);\n\n#define INSTANTIATE_DTYPE(_, data)                                     \\\n  template void strain(                                                \\\n      gsl::not_null<tnsr::ii<DTYPE(data), DIM(data)>*> strain,         \\\n      const tnsr::iJ<DTYPE(data), DIM(data)>& deriv_displacement);     \\\n  template void strain(                                                \\\n      gsl::not_null<tnsr::ii<DTYPE(data), DIM(data)>*> strain,         \\\n      const tnsr::iJ<DTYPE(data), DIM(data)>& deriv_displacement,      \\\n      const tnsr::ii<DTYPE(data), DIM(data)>& metric,                  \\\n      const tnsr::ijj<DTYPE(data), DIM(data)>& deriv_metric,           \\\n      const tnsr::ijj<DTYPE(data), DIM(data)>& christoffel_first_kind, \\\n      const tnsr::I<DTYPE(data), DIM(data)>& displacement);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3))\nGENERATE_INSTANTIATIONS(INSTANTIATE_DTYPE, (2, 3), (double, DataVector))\n\n#undef DIM\n#undef INSTANTIATE\n\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/Strain.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Elasticity {\n\n/*!\n * \\brief The symmetric strain \\f$S_{ij} = \\partial_{(i} \\xi_{j)}\\f$ on a flat\n * background in Cartesian coordinates.\n */\ntemplate <typename DataType, size_t Dim>\nvoid strain(gsl::not_null<tnsr::ii<DataType, Dim>*> strain,\n            const tnsr::iJ<DataType, Dim>& deriv_displacement);\n\n/*!\n * \\brief The symmetric strain \\f$S_{ij} = \\nabla_{(i} \\gamma_{j)k} \\xi^k =\n * \\partial_{(i} \\gamma_{j)k} \\xi^k - \\Gamma_{kij} \\xi^k\\f$ on a\n * background metric \\f$\\gamma_{ij}\\f$.\n */\ntemplate <typename DataType, size_t Dim>\nvoid strain(gsl::not_null<tnsr::ii<DataType, Dim>*> strain,\n            const tnsr::iJ<DataType, Dim>& deriv_displacement,\n            const tnsr::ii<DataType, Dim>& metric,\n            const tnsr::ijj<DataType, Dim>& deriv_metric,\n            const tnsr::ijj<DataType, Dim>& christoffel_first_kind,\n            const tnsr::I<DataType, Dim>& displacement);\n\n/*!\n * \\brief The symmetric strain \\f$S_{ij} = \\partial_{(i} \\xi_{j)}\\f$ on a flat\n * background in Cartesian coordinates.\n *\n * Note that this function involves a numeric differentiation of the\n * displacement vector.\n */\ntemplate <size_t Dim>\nvoid strain(gsl::not_null<tnsr::ii<DataVector, Dim>*> strain,\n            const tnsr::I<DataVector, Dim>& displacement, const Mesh<Dim>& mesh,\n            const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                                  Frame::Inertial>& inv_jacobian);\n\nnamespace Tags {\n\n/*!\n * \\brief The symmetric strain \\f$S_{ij}=\\nabla_{(i} \\xi_{j)}\\f$ in the elastic\n * material.\n *\n * \\see `Elasticity::strain`\n */\ntemplate <size_t Dim>\nstruct StrainCompute : Elasticity::Tags::Strain<Dim>, db::ComputeTag {\n  using base = Elasticity::Tags::Strain<Dim>;\n  using return_type = tnsr::ii<DataVector, Dim>;\n  using argument_tags =\n      tmpl::list<Elasticity::Tags::Displacement<Dim>, domain::Tags::Mesh<Dim>,\n                 domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>>;\n  static constexpr auto function = &strain<Dim>;\n};\n\n}  // namespace Tags\n}  // namespace Elasticity\n"
  },
  {
    "path": "src/PointwiseFunctions/Elasticity/Stress.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/ConstitutiveRelation.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp\"\n\nnamespace Elasticity::Tags {\n\ntemplate <size_t Dim>\nstruct StressCompute : Elasticity::Tags::Stress<Dim>, db::ComputeTag {\n  using base = Elasticity::Tags::Stress<Dim>;\n  using return_type = tnsr::II<DataVector, Dim>;\n  using argument_tags =\n      tmpl::list<Elasticity::Tags::Strain<Dim>,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 Elasticity::Tags::ConstitutiveRelation<Dim>>;\n  static void function(const gsl::not_null<tnsr::II<DataVector, Dim>*> stress,\n                       const tnsr::ii<DataVector, Dim>& strain,\n                       const tnsr::I<DataVector, Dim>& coordinates,\n                       const ConstitutiveRelations::ConstitutiveRelation<Dim>&\n                           constitutive_relation) {\n    constitutive_relation.stress(stress, strain, coordinates);\n  }\n};\n\n}  // namespace Elasticity::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GeneralRelativity)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Christoffel.cpp\n  CubicCurvatureScalars.cpp\n  DerivativeSpatialMetric.cpp\n  DerivativesOfSpacetimeMetric.cpp\n  ExtrinsicCurvature.cpp\n  GeodesicAcceleration.cpp\n  GeodesicEquation.cpp\n  InterfaceNullNormal.cpp\n  InverseSpacetimeMetric.cpp\n  KerrHorizon.cpp\n  KerrSchildCoords.cpp\n  Lapse.cpp\n  ProjectionOperators.cpp\n  Psi4.cpp\n  Psi4Real.cpp\n  QuadraticCurvatureScalars.cpp\n  Ricci.cpp\n  Shift.cpp\n  SpacetimeDerivativeOfGothG.cpp\n  SpacetimeMetric.cpp\n  SpacetimeNormalOneForm.cpp\n  SpacetimeNormalVector.cpp\n  SpatialMetric.cpp\n  TimeDerivativeOfSpacetimeMetric.cpp\n  TimeDerivativeOfSpatialMetric.cpp\n  TortoiseCoordinates.cpp\n  WeylElectric.cpp\n  WeylMagnetic.cpp\n  WeylPropagating.cpp\n  WeylTypeD1.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Christoffel.hpp\n  CubicCurvatureScalars.hpp\n  DerivativeSpatialMetric.hpp\n  DerivativesOfSpacetimeMetric.hpp\n  DetAndInverseSpatialMetric.hpp\n  ExtrinsicCurvature.hpp\n  GeodesicAcceleration.hpp\n  GeodesicEquation.hpp\n  InterfaceNullNormal.hpp\n  InverseSpacetimeMetric.hpp\n  KerrHorizon.hpp\n  KerrSchildCoords.hpp\n  Lapse.hpp\n  ProjectionOperators.hpp\n  Ricci.hpp\n  Psi4.hpp\n  Psi4Real.hpp\n  QuadraticCurvatureScalars.hpp\n  Shift.hpp\n  SpacetimeDerivativeOfGothG.hpp\n  SpacetimeMetric.hpp\n  SpacetimeNormalOneForm.hpp\n  SpacetimeNormalVector.hpp\n  SpatialMetric.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  TimeDerivativeOfSpacetimeMetric.hpp\n  TimeDerivativeOfSpatialMetric.hpp\n  TortoiseCoordinates.hpp\n  WeylElectric.hpp\n  WeylMagnetic.hpp\n  WeylPropagating.hpp\n  WeylTypeD1.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  Utilities\n  PRIVATE\n  RootFinding\n  INTERFACE\n  ErrorHandling\n  )\n\nadd_subdirectory(GeneralizedHarmonic)\nadd_subdirectory(Python)\nadd_subdirectory(Surfaces)\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Christoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nvoid christoffel_first_kind(\n    const gsl::not_null<tnsr::abb<DataType, SpatialDim, Frame, Index>*>\n        christoffel,\n    const tnsr::abb<DataType, SpatialDim, Frame, Index>& d_metric) {\n  constexpr auto dimensionality =\n      Index == IndexType::Spatial ? SpatialDim : SpatialDim + 1;\n  for (size_t k = 0; k < dimensionality; ++k) {\n    for (size_t i = 0; i < dimensionality; ++i) {\n      for (size_t j = i; j < dimensionality; ++j) {\n        christoffel->get(k, i, j) =\n            0.5 * (d_metric.get(i, j, k) + d_metric.get(j, i, k) -\n                   d_metric.get(k, i, j));\n      }\n    }\n  }\n}\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\ntnsr::abb<DataType, SpatialDim, Frame, Index> christoffel_first_kind(\n    const tnsr::abb<DataType, SpatialDim, Frame, Index>& d_metric) {\n  auto christoffel =\n      make_with_value<tnsr::abb<DataType, SpatialDim, Frame, Index>>(d_metric,\n                                                                     0.);\n  christoffel_first_kind(make_not_null(&christoffel), d_metric);\n  return christoffel;\n}\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nvoid christoffel_second_kind(\n    const gsl::not_null<tnsr::Abb<DataType, SpatialDim, Frame, Index>*>\n        christoffel,\n    const tnsr::abb<DataType, SpatialDim, Frame, Index>& d_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame, Index>& inverse_metric) {\n  constexpr auto dimensionality =\n      Index == IndexType::Spatial ? SpatialDim : SpatialDim + 1;\n  for (size_t d = 0; d < dimensionality; ++d) {\n    for (size_t a = 0; a < dimensionality; ++a) {\n      for (size_t b = a; b < dimensionality; ++b) {\n        christoffel->get(d, a, b) =\n            0.5 * inverse_metric.get(0, d) *\n            (d_metric.get(a, b, 0) + d_metric.get(b, a, 0) -\n             d_metric.get(0, a, b));\n        for (size_t c = 1; c < dimensionality; ++c) {\n          christoffel->get(d, a, b) +=\n              0.5 * inverse_metric.get(c, d) *\n              (d_metric.get(a, b, c) + d_metric.get(b, a, c) -\n               d_metric.get(c, a, b));\n        }\n      }\n    }\n  }\n}\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nauto christoffel_second_kind(\n    const tnsr::abb<DataType, SpatialDim, Frame, Index>& d_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame, Index>& inverse_metric)\n    -> tnsr::Abb<DataType, SpatialDim, Frame, Index> {\n  tnsr::Abb<DataType, SpatialDim, Frame, Index> christoffel(\n      get_size(get<0, 0, 0>(d_metric)));\n  christoffel_second_kind(make_not_null(&christoffel), d_metric,\n                          inverse_metric);\n  return christoffel;\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define INDEXTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template tnsr::abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>   \\\n  gr::christoffel_first_kind(                                                \\\n      const tnsr::abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>& \\\n          d_metric);                                                         \\\n  template void gr::christoffel_first_kind(                                  \\\n      const gsl::not_null<                                                   \\\n          tnsr::abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>*>  \\\n          christoffel,                                                       \\\n      const tnsr::abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>& \\\n          d_metric);                                                         \\\n  template tnsr::Abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>   \\\n  gr::christoffel_second_kind(                                               \\\n      const tnsr::abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>& \\\n          d_metric,                                                          \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>&  \\\n          inverse_metric);                                                   \\\n  template void gr::christoffel_second_kind(                                 \\\n      const gsl::not_null<                                                   \\\n          tnsr::Abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>*>  \\\n          christoffel,                                                       \\\n      const tnsr::abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>& \\\n          d_metric,                                                          \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>&  \\\n          inverse_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial,\n                         Frame::Spherical<Frame::Inertial>,\n                         Frame::Spherical<Frame::Distorted>,\n                         Frame::Spherical<Frame::Grid>),\n                        (IndexType::Spatial, IndexType::Spacetime))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INDEXTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Christoffel.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Defines functions to calculate Christoffel symbols\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <class>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes Christoffel symbol of the first kind from derivative of\n * metric\n *\n * \\details Computes Christoffel symbol \\f$\\Gamma_{abc}\\f$ as:\n * \\f$ \\Gamma_{cab} = \\frac{1}{2} ( \\partial_a g_{bc} + \\partial_b g_{ac}\n *  -  \\partial_c g_{ab}) \\f$\n * where \\f$g_{bc}\\f$ is either a spatial or spacetime metric\n */\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nvoid christoffel_first_kind(\n    gsl::not_null<tnsr::abb<DataType, SpatialDim, Frame, Index>*> christoffel,\n    const tnsr::abb<DataType, SpatialDim, Frame, Index>& d_metric);\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\ntnsr::abb<DataType, SpatialDim, Frame, Index> christoffel_first_kind(\n    const tnsr::abb<DataType, SpatialDim, Frame, Index>& d_metric);\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes Christoffel symbol of the second kind from derivative of\n * metric and the inverse metric.\n *\n * \\details Computes Christoffel symbol \\f$\\Gamma^a_{bc}\\f$ as:\n * \\f$ \\Gamma^d_{ab} = \\frac{1}{2} g^{cd} (\\partial_a g_{bc} + \\partial_b g_{ac}\n *  -  \\partial_c g_{ab}) \\f$\n * where \\f$g_{bc}\\f$ is either a spatial or spacetime metric.\n *\n * Avoids the extra memory allocation that occurs by computing the\n * Christoffel symbol of the first kind and then raising the index.\n */\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nvoid christoffel_second_kind(\n    gsl::not_null<tnsr::Abb<DataType, SpatialDim, Frame, Index>*> christoffel,\n    const tnsr::abb<DataType, SpatialDim, Frame, Index>& d_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame, Index>& inverse_metric);\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nauto christoffel_second_kind(\n    const tnsr::abb<DataType, SpatialDim, Frame, Index>& d_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame, Index>& inverse_metric)\n    -> tnsr::Abb<DataType, SpatialDim, Frame, Index>;\n/// @}\n\nnamespace Tags {\n/// Compute item for spatial Christoffel symbols of the first kind\n/// \\f$\\Gamma_{ijk}\\f$ computed from the first derivative of the\n/// spatial metric.\n///\n/// Can be retrieved using `gr::Tags::SpatialChristoffelFirstKind`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpatialChristoffelFirstKindCompute\n    : SpatialChristoffelFirstKind<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataType, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>>;\n\n  using return_type = tnsr::ijj<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<\n          tnsr::abb<DataType, SpatialDim, Frame, IndexType::Spatial>*>,\n      const tnsr::ijj<DataType, SpatialDim, Frame>&)>(\n      &christoffel_first_kind<SpatialDim, Frame, IndexType::Spatial, DataType>);\n\n  using base = SpatialChristoffelFirstKind<DataType, SpatialDim, Frame>;\n};\n\n/// Compute item for spatial Christoffel symbols of the second kind\n/// \\f$\\Gamma^i_{jk}\\f$ computed from the Christoffel symbols of the\n/// first kind and the inverse spatial metric.\n///\n/// Can be retrieved using `gr::Tags::SpatialChristoffelSecondKind`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpatialChristoffelSecondKindCompute\n    : SpatialChristoffelSecondKind<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpatialChristoffelFirstKind<DataType, SpatialDim, Frame>,\n                 InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::Ijj<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::Ijj<DataType, SpatialDim, Frame>*>,\n      const tnsr::ijj<DataType, SpatialDim, Frame>&,\n      const tnsr::II<DataType, SpatialDim, Frame>&)>(\n      &raise_or_lower_first_index<DataType,\n                                  SpatialIndex<SpatialDim, UpLo::Lo, Frame>,\n                                  SpatialIndex<SpatialDim, UpLo::Lo, Frame>>);\n\n  using base = SpatialChristoffelSecondKind<DataType, SpatialDim, Frame>;\n};\n\n/// Compute item for the trace of the spatial Christoffel symbols\n/// of the first kind\n/// \\f$\\Gamma_{i} = \\Gamma_{ijk}\\gamma^{jk}\\f$ computed from the\n/// Christoffel symbols of the first kind and the inverse spatial metric.\n///\n/// Can be retrieved using `gr::Tags::TraceSpatialChristoffelFirstKind`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct TraceSpatialChristoffelFirstKindCompute\n    : TraceSpatialChristoffelFirstKind<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpatialChristoffelFirstKind<DataType, SpatialDim, Frame>,\n                 InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::i<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::i<DataType, SpatialDim, Frame>*>,\n      const tnsr::ijj<DataType, SpatialDim, Frame>&,\n      const tnsr::II<DataType, SpatialDim, Frame>&)>(\n      &trace_last_indices<DataType, SpatialIndex<SpatialDim, UpLo::Lo, Frame>,\n                          SpatialIndex<SpatialDim, UpLo::Lo, Frame>>);\n\n  using base = TraceSpatialChristoffelFirstKind<DataType, SpatialDim, Frame>;\n};\n\n/// Compute item for the trace of the spatial Christoffel symbols\n/// of the second kind\n/// \\f$\\Gamma^{i} = \\Gamma^{i}_{jk}\\gamma^{jk}\\f$ computed from the\n/// Christoffel symbols of the second kind and the inverse spatial metric.\n///\n/// Can be retrieved using `gr::Tags::TraceSpatialChristoffelSecondKind`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct TraceSpatialChristoffelSecondKindCompute\n    : TraceSpatialChristoffelSecondKind<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpatialChristoffelSecondKind<DataType, SpatialDim, Frame>,\n                 InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::I<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::I<DataType, SpatialDim, Frame>*>,\n      const tnsr::Ijj<DataType, SpatialDim, Frame>&,\n      const tnsr::II<DataType, SpatialDim, Frame>&)>(\n      &trace_last_indices<DataType, SpatialIndex<SpatialDim, UpLo::Up, Frame>,\n                          SpatialIndex<SpatialDim, UpLo::Lo, Frame>>);\n\n  using base = TraceSpatialChristoffelSecondKind<DataType, SpatialDim, Frame>;\n};\n\n/// Compute item for spacetime Christoffel symbols of the first kind\n/// \\f$\\Gamma_{abc}\\f$ computed from the first derivative of the\n/// spacetime metric.\n///\n/// Can be retrieved using `gr::Tags::SpacetimeChristoffelFirstKind`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpacetimeChristoffelFirstKindCompute\n    : SpacetimeChristoffelFirstKind<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<DerivativesOfSpacetimeMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type =\n      tnsr::abb<DataType, SpatialDim, Frame, IndexType::Spacetime>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<\n          tnsr::abb<DataType, SpatialDim, Frame, IndexType::Spacetime>*>,\n      const tnsr::abb<DataType, SpatialDim, Frame, IndexType::Spacetime>&)>(\n      &christoffel_first_kind<SpatialDim, Frame, IndexType::Spacetime,\n                              DataType>);\n\n  using base = SpacetimeChristoffelFirstKind<DataType, SpatialDim, Frame>;\n};\n\n/// Compute item for spacetime Christoffel symbols of the second kind\n/// \\f$\\Gamma^a_{bc}\\f$ computed from the Christoffel symbols of the\n/// first kind and the inverse spacetime metric.\n///\n/// Can be retrieved using `gr::Tags::SpacetimeChristoffelSecondKind`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpacetimeChristoffelSecondKindCompute\n    : SpacetimeChristoffelSecondKind<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpacetimeChristoffelFirstKind<DataType, SpatialDim, Frame>,\n                 InverseSpacetimeMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::Abb<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::Abb<DataType, SpatialDim, Frame>*>,\n      const tnsr::abb<DataType, SpatialDim, Frame>&,\n      const tnsr::AA<DataType, SpatialDim, Frame>&)>(\n      &raise_or_lower_first_index<DataType,\n                                  SpacetimeIndex<SpatialDim, UpLo::Lo, Frame>,\n                                  SpacetimeIndex<SpatialDim, UpLo::Lo, Frame>>);\n\n  using base = SpacetimeChristoffelSecondKind<DataType, SpatialDim, Frame>;\n};\n\n/// Compute item for the trace of the spacetime Christoffel symbols\n/// of the first kind\n/// \\f$\\Gamma_{a} = \\Gamma_{abc}g^{bc}\\f$ computed from the\n/// Christoffel symbols of the first kind and the inverse spacetime metric.\n///\n/// Can be retrieved using `gr::Tags::TraceSpacetimeChristoffelFirstKind`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct TraceSpacetimeChristoffelFirstKindCompute\n    : TraceSpacetimeChristoffelFirstKind<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpacetimeChristoffelFirstKind<DataType, SpatialDim, Frame>,\n                 InverseSpacetimeMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::a<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*>,\n      const tnsr::abb<DataType, SpatialDim, Frame>&,\n      const tnsr::AA<DataType, SpatialDim, Frame>&)>(\n      &trace_last_indices<DataType, SpacetimeIndex<SpatialDim, UpLo::Lo, Frame>,\n                          SpacetimeIndex<SpatialDim, UpLo::Lo, Frame>>);\n\n  using base = TraceSpacetimeChristoffelFirstKind<DataType, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/CubicCurvatureScalars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/CubicCurvatureScalars.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid cubic_invariant_real(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric) {\n  const auto e_mixed = tenex::evaluate<ti::I, ti::j>(\n      inverse_spatial_metric(ti::I, ti::K) * weyl_electric(ti::k, ti::j));\n  const auto b_mixed = tenex::evaluate<ti::I, ti::j>(\n      inverse_spatial_metric(ti::I, ti::K) * weyl_magnetic(ti::k, ti::j));\n\n  // -1/6 tr(E^3) + 1/2 tr(E B^2)\n  tenex::evaluate(\n      result, -1.0 / 6.0 * e_mixed(ti::I, ti::j) * e_mixed(ti::J, ti::k) *\n                      e_mixed(ti::K, ti::i) +\n                  1.0 / 2.0 * e_mixed(ti::I, ti::j) * b_mixed(ti::J, ti::k) *\n                      b_mixed(ti::K, ti::i));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> cubic_invariant_real(\n    const tnsr::ii<DataType, Dim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric) {\n  Scalar<DataType> result{get<0, 0>(inverse_spatial_metric)};\n  cubic_invariant_real(make_not_null(&result), weyl_electric, weyl_magnetic,\n                       inverse_spatial_metric);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid cubic_invariant_imag(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric) {\n  const auto e_mixed = tenex::evaluate<ti::I, ti::j>(\n      inverse_spatial_metric(ti::I, ti::K) * weyl_electric(ti::k, ti::j));\n  const auto b_mixed = tenex::evaluate<ti::I, ti::j>(\n      inverse_spatial_metric(ti::I, ti::K) * weyl_magnetic(ti::k, ti::j));\n\n  // 1/6 tr(B^3) - 1/2 tr(B E^2)\n  tenex::evaluate(\n      result, 1.0 / 6.0 * b_mixed(ti::I, ti::j) * b_mixed(ti::J, ti::k) *\n                      b_mixed(ti::K, ti::i) -\n                  1.0 / 2.0 * b_mixed(ti::I, ti::j) * e_mixed(ti::J, ti::k) *\n                      e_mixed(ti::K, ti::i));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> cubic_invariant_imag(\n    const tnsr::ii<DataType, Dim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric) {\n  Scalar<DataType> result{get<0, 0>(inverse_spatial_metric)};\n  cubic_invariant_imag(make_not_null(&result), weyl_electric, weyl_magnetic,\n                       inverse_spatial_metric);\n  return result;\n}\n\n}  // namespace gr\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                             \\\n  template void gr::cubic_invariant_real(                \\\n      const gsl::not_null<Scalar<DTYPE(data)>*>,         \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>&,      \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>&,      \\\n      const tnsr::II<DTYPE(data), 3, FRAME(data)>&);     \\\n  template Scalar<DTYPE(data)> gr::cubic_invariant_real( \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>&,      \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>&,      \\\n      const tnsr::II<DTYPE(data), 3, FRAME(data)>&);     \\\n  template void gr::cubic_invariant_imag(                \\\n      const gsl::not_null<Scalar<DTYPE(data)>*>,         \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>&,      \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>&,      \\\n      const tnsr::II<DTYPE(data), 3, FRAME(data)>&);     \\\n  template Scalar<DTYPE(data)> gr::cubic_invariant_imag( \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>&,      \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>&,      \\\n      const tnsr::II<DTYPE(data), 3, FRAME(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/CubicCurvatureScalars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gr {\n\n/// @{\n/*!\n * Computes the real part of the cubic invariant of the Weyl tensor from the\n * electric and magnetic parts. The cubic invariant is e.g. given in Equation\n * (10) of \\cite Dennison:2012vf\n * \\f[\n *   \\mathcal{J} = \\left(-\\frac{1}{6} E^i_j E^j_k E^k_i + \\frac{1}{2} E^i_j\n *    B^j_k B^k_i\\right) + i\\left(\\frac{1}{6} B^i_j B^j_k B^k_i - \\frac{1}{2}\n *    B^i_j E^j_k E^k_i\\right)\n * \\f]\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid cubic_invariant_real(\n    gsl::not_null<Scalar<DataType>*> result,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> cubic_invariant_real(\n    const tnsr::ii<DataType, Dim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric);\n/// @}\n\n/// @{\n/*!\n * Computes the imaginary part of the cubic invariant of the Weyl tensor from\n * the electric and magnetic parts. The cubic invariant is e.g. given in\n * Equation (10) of \\cite Dennison:2012vf\n * \\f[\n *   \\mathcal{J} = \\left(-\\frac{1}{6} E^i_j E^j_k E^k_i + \\frac{1}{2} E^i_j\n *    B^j_k B^k_i\\right) + i\\left(\\frac{1}{6} B^i_j B^j_k B^k_i - \\frac{1}{2}\n *    B^i_j E^j_k E^k_i\\right)\n * \\f]\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid cubic_invariant_imag(\n    gsl::not_null<Scalar<DataType>*> result,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> cubic_invariant_imag(\n    const tnsr::ii<DataType, Dim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, Dim, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric);\n/// @}\n\n}  // namespace gr\n\nnamespace gr::Tags {\n// Simple and compute tags for cubic invariants\ntemplate <typename DataType>\nstruct CubicInvariantReal : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct CubicInvariantRealCompute : CubicInvariantReal<DataType>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::WeylElectric<DataType, Dim, Frame>,\n                 gr::Tags::WeylMagnetic<DataType, Dim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>>;\n  using return_type = Scalar<DataType>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataType>*>, const tnsr::ii<DataType, Dim, Frame>&,\n      const tnsr::ii<DataType, Dim, Frame>&,\n      const tnsr::II<DataType, Dim, Frame>&)>(\n      &gr::cubic_invariant_real<DataType, Dim, Frame>);\n  using base = CubicInvariantReal<DataType>;\n};\n\ntemplate <typename DataType>\nstruct CubicInvariantImag : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct CubicInvariantImagCompute : CubicInvariantImag<DataType>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::WeylElectric<DataType, Dim, Frame>,\n                 gr::Tags::WeylMagnetic<DataType, Dim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>>;\n  using return_type = Scalar<DataType>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataType>*>, const tnsr::ii<DataType, Dim, Frame>&,\n      const tnsr::ii<DataType, Dim, Frame>&,\n      const tnsr::II<DataType, Dim, Frame>&)>(\n      &gr::cubic_invariant_imag<DataType, Dim, Frame>);\n  using base = CubicInvariantImag<DataType>;\n};\n}  // namespace gr::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid deriv_inverse_spatial_metric(\n    const gsl::not_null<tnsr::iJJ<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& d_spatial_metric) {\n  set_number_of_grid_points(result, inverse_spatial_metric);\n  for (auto& component : *result) {\n    component = 0.0;\n  }\n\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      for (size_t k = 0; k < Dim; ++k) {\n        for (size_t m = 0; m < Dim; ++m) {\n          for (size_t n = 0; n < Dim; ++n) {\n            (*result).get(k, i, j) -= inverse_spatial_metric.get(i, n) *\n                                      inverse_spatial_metric.get(m, j) *\n                                      d_spatial_metric.get(k, n, m);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::iJJ<DataType, Dim, Frame> deriv_inverse_spatial_metric(\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& d_spatial_metric) {\n  tnsr::iJJ<DataType, Dim, Frame> result{};\n  deriv_inverse_spatial_metric(make_not_null(&result), inverse_spatial_metric,\n                               d_spatial_metric);\n  return result;\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void gr::deriv_inverse_spatial_metric(                              \\\n      const gsl::not_null<tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          result,                                                              \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& d_spatial_metric); \\\n  template tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>                      \\\n  gr::deriv_inverse_spatial_metric(                                            \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& d_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n#undef FRAME\n#undef DIM\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the spatial derivative of the inverse spatial metric from the\n * inverse spatial metric and the spatial derivative of the spatial metric.\n *\n * \\details Computes the derivative as:\n * \\f{align}\n *     \\partial_k \\gamma^{ij} &= -\\gamma^{in} \\gamma^{mj}\n *                 \\partial_k \\gamma_{nm}\n * \\f}\n * where \\f$\\gamma^{ij}\\f$ and \\f$\\partial_k \\gamma_{ij}\\f$ are the inverse\n * spatial metric and spatial derivative of the spatial metric, respectively.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid deriv_inverse_spatial_metric(\n    gsl::not_null<tnsr::iJJ<DataType, Dim, Frame>*> result,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& d_spatial_metric);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::iJJ<DataType, Dim, Frame> deriv_inverse_spatial_metric(\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& d_spatial_metric);\n/// @}\n\nnamespace Tags {\n\n/// \\brief Compute item for the spatial derivative of the inverse spatial\n/// metric.\n/// \\see deriv_inverse_spatial_metric\ntemplate <size_t Dim, typename Frame>\nstruct DerivInverseSpatialMetricCompute\n    : ::Tags::deriv<InverseSpatialMetric<DataVector, Dim, Frame>,\n                    tmpl::size_t<Dim>, Frame>,\n      db::ComputeTag {\n  using base = ::Tags::deriv<InverseSpatialMetric<DataVector, Dim, Frame>,\n                             tmpl::size_t<Dim>, Frame>;\n  using argument_tags =\n      tmpl::list<InverseSpatialMetric<DataVector, Dim, Frame>,\n                 ::Tags::deriv<SpatialMetric<DataVector, Dim, Frame>,\n                               tmpl::size_t<Dim>, Frame>>;\n  using return_type = tnsr::iJJ<DataVector, Dim, Frame>;\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<tnsr::iJJ<DataVector, Dim, Frame>*>,\n                           const tnsr::II<DataVector, Dim, Frame>&,\n                           const tnsr::ijj<DataVector, Dim, Frame>&)>(\n          &gr::deriv_inverse_spatial_metric<DataVector, Dim, Frame>);\n};\n\n}  // namespace Tags\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::abb<DataType, Dim, Frame> derivatives_of_spacetime_metric(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::i<DataType, Dim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::I<DataType, Dim, Frame>& dt_shift,\n    const tnsr::iJ<DataType, Dim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& dt_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& deriv_spatial_metric) {\n  tnsr::abb<DataType, Dim, Frame> spacetime_deriv_spacetime_metric{};\n  derivatives_of_spacetime_metric(\n      make_not_null(&spacetime_deriv_spacetime_metric), lapse, dt_lapse,\n      deriv_lapse, shift, dt_shift, deriv_shift, spatial_metric,\n      dt_spatial_metric, deriv_spatial_metric);\n  return spacetime_deriv_spacetime_metric;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid derivatives_of_spacetime_metric(\n    const gsl::not_null<tnsr::abb<DataType, Dim, Frame>*>\n        spacetime_deriv_spacetime_metric,\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::i<DataType, Dim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::I<DataType, Dim, Frame>& dt_shift,\n    const tnsr::iJ<DataType, Dim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& dt_spatial_metric,\n    const tnsr::ijj<DataType, Dim, Frame>& deriv_spatial_metric) {\n  set_number_of_grid_points(spacetime_deriv_spacetime_metric, lapse);\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t i = 0; i < Dim; ++i) {\n      spacetime_deriv_spacetime_metric->get(a, 0, i + 1) = 0.0;\n    }\n  }\n\n  get<0, 0, 0>(*spacetime_deriv_spacetime_metric) =\n      -2.0 * get(lapse) * get(dt_lapse);\n\n  for (size_t m = 0; m < Dim; ++m) {\n    for (size_t n = 0; n < Dim; ++n) {\n      get<0, 0, 0>(*spacetime_deriv_spacetime_metric) +=\n          dt_spatial_metric.get(m, n) * shift.get(m) * shift.get(n) +\n          2.0 * spatial_metric.get(m, n) * shift.get(m) * dt_shift.get(n);\n    }\n  }\n\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t m = 0; m < Dim; ++m) {\n      spacetime_deriv_spacetime_metric->get(0, 0, i + 1) +=\n          dt_spatial_metric.get(m, i) * shift.get(m) +\n          spatial_metric.get(m, i) * dt_shift.get(m);\n    }\n    for (size_t j = i; j < Dim; ++j) {\n      spacetime_deriv_spacetime_metric->get(0, i + 1, j + 1) =\n          dt_spatial_metric.get(i, j);\n    }\n  }\n\n  for (size_t k = 0; k < Dim; ++k) {\n    spacetime_deriv_spacetime_metric->get(k + 1, 0, 0) =\n        -2.0 * get(lapse) * deriv_lapse.get(k);\n    for (size_t m = 0; m < Dim; ++m) {\n      for (size_t n = 0; n < Dim; ++n) {\n        spacetime_deriv_spacetime_metric->get(k + 1, 0, 0) +=\n            deriv_spatial_metric.get(k, m, n) * shift.get(m) * shift.get(n) +\n            2.0 * spatial_metric.get(m, n) * shift.get(m) *\n                deriv_shift.get(k, n);\n      }\n    }\n\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t m = 0; m < Dim; ++m) {\n        spacetime_deriv_spacetime_metric->get(k + 1, 0, i + 1) +=\n            deriv_spatial_metric.get(k, m, i) * shift.get(m) +\n            spatial_metric.get(m, i) * deriv_shift.get(k, m);\n      }\n      for (size_t j = i; j < Dim; ++j) {\n        spacetime_deriv_spacetime_metric->get(k + 1, i + 1, j + 1) =\n            deriv_spatial_metric.get(k, i, j);\n      }\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void gr::derivatives_of_spacetime_metric(                          \\\n      const gsl::not_null<tnsr::abb<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          spacetime_deriv_spacetime_metric,                                   \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse,  \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& deriv_lapse,        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,              \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,           \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,       \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric, \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          deriv_spatial_metric);                                              \\\n  template tnsr::abb<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gr::derivatives_of_spacetime_metric(                                        \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse,  \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& deriv_lapse,        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,              \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,           \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,       \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric, \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          deriv_spatial_metric);\n}  // namespace gr\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes spacetime derivative of spacetime metric from spatial metric,\n * lapse, shift, and their space and time derivatives.\n *\n * \\details Computes the derivatives as:\n * \\f{align}\n *     \\partial_\\mu g_{tt} &= - 2 \\alpha \\partial_\\mu \\alpha\n *                 + 2 \\gamma_{mn} \\beta^m \\partial_\\mu \\beta^n\n *                 + \\beta^m \\beta^n \\partial_\\mu \\gamma_{mn} \\\\\n *     \\partial_\\mu g_{ti} &= \\gamma_{mi} \\partial_\\mu \\beta^m\n *                 + \\beta^m \\partial_\\mu \\gamma_{mi} \\\\\n *     \\partial_\\mu g_{ij} &= \\partial_\\mu \\gamma_{ij}\n * \\f}\n * where \\f$ \\alpha, \\beta^i, \\gamma_{ij} \\f$ are the lapse, shift, and spatial\n * metric respectively.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid derivatives_of_spacetime_metric(\n    gsl::not_null<tnsr::abb<DataType, SpatialDim, Frame>*>\n        spacetime_deriv_spacetime_metric,\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::abb<DataType, SpatialDim, Frame> derivatives_of_spacetime_metric(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get spacetime derivative of spacetime metric from\n * spatial metric, lapse, shift, and their space and time derivatives.\n *\n * \\details See `derivatives_of_spacetime_metric()`. Can be retrieved using\n * `gr::Tags::DerivativesOfSpacetimeMetric`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct DerivativesOfSpacetimeMetricCompute\n    : gr::Tags::DerivativesOfSpacetimeMetric<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gr::Tags::Lapse<DataVector>, ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<SpatialDim>,\n                    Frame>,\n      gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n      ::Tags::dt<gr::Tags::Shift<DataVector, SpatialDim, Frame>>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>>,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>>;\n\n  using return_type = tnsr::abb<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::abb<DataVector, SpatialDim, Frame>*>\n          spacetime_deriv_spacetime_metric,\n      const Scalar<DataVector>&, const Scalar<DataVector>&,\n      const tnsr::i<DataVector, SpatialDim, Frame>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::iJ<DataVector, SpatialDim, Frame>&,\n      const tnsr::ii<DataVector, SpatialDim, Frame>&,\n      const tnsr::ii<DataVector, SpatialDim, Frame>&,\n      const tnsr::ijj<DataVector, SpatialDim, Frame>&)>(\n      &gr::derivatives_of_spacetime_metric<DataVector, SpatialDim, Frame>);\n\n  using base =\n      gr::Tags::DerivativesOfSpacetimeMetric<DataVector, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n\nnamespace Tags {\n/*!\n * \\brief Compute item for spatial metric determinant \\f$\\gamma\\f$ and inverse\n * \\f$\\gamma^{ij}\\f$ in terms of the spatial metric \\f$\\gamma_{ij}\\f$.\n *\n * \\details Can be retrieved using `gr::Tags::DetSpatialMetric` and\n * `gr::Tags::InverseSpatialMetric`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct DetAndInverseSpatialMetricCompute\n    : ::Tags::Variables<\n          tmpl::list<DetSpatialMetric<DataType>,\n                     InverseSpatialMetric<DataType, SpatialDim, Frame>>>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<SpatialMetric<DataType, SpatialDim, Frame>>;\n  using base = ::Tags::Variables<\n      tmpl::list<DetSpatialMetric<DataType>,\n                 InverseSpatialMetric<DataType, SpatialDim, Frame>>>;\n  using return_type = typename base::type;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<return_type*>,\n      const Tensor<DataType, tmpl::integral_list<std::int32_t, 1, 1>,\n                   tmpl::list<SpatialIndex<SpatialDim, UpLo::Lo, Frame>,\n                              SpatialIndex<SpatialDim, UpLo::Lo, Frame>>>&)>(\n      &determinant_and_inverse);\n};\n\n/*!\n * \\brief Compute item to get the square root of the determinant of the spatial\n * metric \\f$\\sqrt{\\gamma}\\f$ via `gr::Tags::DetAndInverseSpatialMetric`.\n *\n * \\details Can be retrieved using `gr::Tags::SqrtDetSpatialMetric`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SqrtDetSpatialMetricCompute : SqrtDetSpatialMetric<DataType>,\n                                     db::ComputeTag {\n  using argument_tags = tmpl::list<DetSpatialMetric<DataType>>;\n\n  using return_type = Scalar<DataType>;\n\n  static void function(const gsl::not_null<Scalar<DataType>*> result,\n                       const Scalar<DataType>& det_spatial_metric) {\n    get(*result) = sqrt(get(det_spatial_metric));\n  }\n\n  using base = SqrtDetSpatialMetric<DataType>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid extrinsic_curvature(\n    const gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> ex_curvature,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric) {\n  const DataType half_over_lapse = 0.5 / get(lapse);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {  // Symmetry\n      ex_curvature->get(i, j) = -dt_spatial_metric.get(i, j);\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        ex_curvature->get(i, j) +=\n            shift.get(k) * deriv_spatial_metric.get(k, i, j) +\n            spatial_metric.get(k, i) * deriv_shift.get(j, k) +\n            spatial_metric.get(k, j) * deriv_shift.get(i, k);\n      }\n      ex_curvature->get(i, j) *= half_over_lapse;\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> extrinsic_curvature(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric) {\n  tnsr::ii<DataType, SpatialDim, Frame> ex_curvature{};\n  extrinsic_curvature(make_not_null(&ex_curvature), lapse, shift, deriv_shift,\n                      spatial_metric, dt_spatial_metric, deriv_spatial_metric);\n  return ex_curvature;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid covariant_derivative_of_extrinsic_curvature(\n    const gsl::not_null<tnsr::ijj<DataType, SpatialDim, Frame>*> grad_ex_curv,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& d_ex_curv,\n    const tnsr::ii<DataType, SpatialDim, Frame>& ex_curv,\n    const tnsr::Ijj<DataType, SpatialDim, Frame>&\n        spatial_christoffel_second_kind) {\n  set_number_of_grid_points(grad_ex_curv, ex_curv);\n  tenex::evaluate<ti::i, ti::j, ti::k>(\n      grad_ex_curv, d_ex_curv(ti::i, ti::j, ti::k) -\n                        spatial_christoffel_second_kind(ti::L, ti::i, ti::j) *\n                            ex_curv(ti::l, ti::k) -\n                        spatial_christoffel_second_kind(ti::L, ti::i, ti::k) *\n                            ex_curv(ti::j, ti::l));\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ijj<DataType, SpatialDim, Frame>\ncovariant_derivative_of_extrinsic_curvature(\n    const tnsr::ijj<DataType, SpatialDim, Frame>& d_ex_curv,\n    const tnsr::ii<DataType, SpatialDim, Frame>& ex_curv,\n    const tnsr::Ijj<DataType, SpatialDim, Frame>&\n        spatial_christoffel_second_kind) {\n  tnsr::ijj<DataType, SpatialDim, Frame> grad_ex_curv{};\n  covariant_derivative_of_extrinsic_curvature(make_not_null(&grad_ex_curv),\n                                              d_ex_curv, ex_curv,\n                                              spatial_christoffel_second_kind);\n  return grad_ex_curv;\n}\n\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void gr::extrinsic_curvature(                                      \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          ex_curvature,                                                       \\\n      const Scalar<DTYPE(data)>& lapse,                                       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,              \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,       \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric, \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          deriv_spatial_metric);                                              \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>                      \\\n  gr::extrinsic_curvature(                                                    \\\n      const Scalar<DTYPE(data)>& lapse,                                       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,              \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,       \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric, \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          deriv_spatial_metric);                                              \\\n  template void gr::covariant_derivative_of_extrinsic_curvature(              \\\n      const gsl::not_null<tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          grad_ex_curv,                                                       \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& d_ex_curv,        \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& ex_curv,           \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          spatial_christoffel_second_kind);                                   \\\n  template tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gr::covariant_derivative_of_extrinsic_curvature(                            \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>& d_ex_curv,        \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& ex_curv,           \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          spatial_christoffel_second_kind);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief  Computes extrinsic curvature from metric and derivatives.\n * \\details Uses the ADM evolution equation for the spatial metric,\n * \\f[ K_{ij} = \\frac{1}{2 \\alpha} \\left ( -\\partial_0 \\gamma_{ij}\n * + \\beta^k \\partial_k \\gamma_{ij} + \\gamma_{ki} \\partial_j \\beta^k\n * + \\gamma_{kj} \\partial_i \\beta^k \\right ) \\f]\n * where \\f$K_{ij}\\f$ is the extrinsic curvature, \\f$\\alpha\\f$ is the lapse,\n * \\f$\\beta^i\\f$ is the shift, and \\f$\\gamma_{ij}\\f$ is the spatial metric. In\n * terms of the Lie derivative of the spatial metric with respect to a unit\n * timelike vector \\f$n^a\\f$ normal to the spatial slice, this corresponds to\n * the sign convention\n * \\f[ K_{ab} = - \\frac{1}{2} \\mathcal{L}_{\\mathbf{n}} \\gamma_{ab} \\f]\n * where \\f$\\gamma_{ab}\\f$ is the spatial metric. See Eq. (2.53) in\n * \\cite BaumgarteShapiro.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> extrinsic_curvature(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid extrinsic_curvature(\n    gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> ex_curvature,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric);\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief  Computes the spatial covariant derivative of the extrinsic curvature.\n *  \\details The spatial covariant derivative is computed as\n * \\f[ D_k K_{ij} = \\partial_k K_{ij} - {^{(3)}\\Gamma^{l}_{ki}} K_{lj}\n * - {^{(3)}\\Gamma^{l}_{kj}}K_{il} \\f]\n * where \\f$ {^{(3)}\\Gamma^{k}_{ij}} \\f$ is the spatial Christoffel symbol of\n * the second kind.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ijj<DataType, SpatialDim, Frame>\ncovariant_derivative_of_extrinsic_curvature(\n    const tnsr::ijj<DataType, SpatialDim, Frame>& d_ex_curv,\n    const tnsr::ii<DataType, SpatialDim, Frame>& ex_curv,\n    const tnsr::Ijj<DataType, SpatialDim, Frame>&\n        spatial_christoffel_second_kind);\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid covariant_derivative_of_extrinsic_curvature(\n    gsl::not_null<tnsr::ijj<DataType, SpatialDim, Frame>*> grad_ex_curv,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& d_ex_curv,\n    const tnsr::ii<DataType, SpatialDim, Frame>& ex_curv,\n    const tnsr::Ijj<DataType, SpatialDim, Frame>&\n        spatial_christoffel_second_kind);\n/// @}\n\nnamespace Tags {\n/// \\copydoc covariant_derivative_of_extrinsic_curvature\ntemplate <size_t SpatialDim, typename Frame>\nstruct CovariantDerivativeOfExtrinsicCurvatureCompute\n    : gr::Tags::CovariantDerivativeOfExtrinsicCurvature<DataVector, SpatialDim,\n                                                        Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<\n      ::Tags::deriv<gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::ijj<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::ijj<DataVector, SpatialDim, Frame>*>,\n      const tnsr::ijj<DataVector, SpatialDim, Frame>&,\n      const tnsr::ii<DataVector, SpatialDim, Frame>&,\n      const tnsr::Ijj<DataVector, SpatialDim, Frame>&\n          spatial_christoffel_second_kind)>(\n      &covariant_derivative_of_extrinsic_curvature<DataVector, SpatialDim,\n                                                   Frame>);\n\n  using base =\n      gr::Tags::CovariantDerivativeOfExtrinsicCurvature<DataVector, SpatialDim,\n                                                        Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Christoffel.cpp\n  CovariantDerivOfExtrinsicCurvature.cpp\n  DerivSpatialMetric.cpp\n  Expansion1D.cpp\n  ExtrinsicCurvature.cpp\n  GaugeSource.cpp\n  Phi.cpp\n  Pi.cpp\n  Ricci.cpp\n  SecondTimeDerivOfSpacetimeMetric.cpp\n  SpacetimeDerivOfDetSpatialMetric.cpp\n  SpacetimeDerivOfNormOfShift.cpp\n  SpacetimeDerivativeOfSpacetimeMetric.cpp\n  SpatialDerivOfLapse.cpp\n  SpatialDerivOfShift.cpp\n  TimeDerivOfLapse.cpp\n  TimeDerivOfLowerShift.cpp\n  TimeDerivOfShift.cpp\n  TimeDerivOfSpatialMetric.cpp\n  TimeDerivativeOfSpacetimeMetric.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Christoffel.hpp\n  ConstraintDampingTags.hpp\n  ConstraintGammas.hpp\n  CovariantDerivOfExtrinsicCurvature.hpp\n  DerivSpatialMetric.hpp\n  Expansion1D.hpp\n  ExtrinsicCurvature.hpp\n  GaugeSource.hpp\n  Phi.hpp\n  Pi.hpp\n  Ricci.hpp\n  SecondTimeDerivOfSpacetimeMetric.hpp\n  SpacetimeDerivOfDetSpatialMetric.hpp\n  SpacetimeDerivOfNormOfShift.hpp\n  SpacetimeDerivativeOfSpacetimeMetric.hpp\n  SpatialDerivOfLapse.hpp\n  SpatialDerivOfShift.hpp\n  TimeDerivOfLapse.hpp\n  TimeDerivOfLowerShift.hpp\n  TimeDerivOfShift.hpp\n  TimeDerivOfSpatialMetric.hpp\n  TimeDerivativeOfSpacetimeMetric.hpp\n)\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid christoffel_second_kind(\n    const gsl::not_null<tnsr::Ijj<DataType, SpatialDim, Frame>*> christoffel,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inv_metric) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      for (size_t m = 0; m < SpatialDim; ++m) {\n        christoffel->get(m, i, j) =\n            0.5 * inv_metric.get(m, 0) *\n            (phi.get(i, j + 1, 1) + phi.get(j, i + 1, 1) -\n             phi.get(0, i + 1, j + 1));\n        for (size_t k = 1; k < SpatialDim; ++k) {\n          christoffel->get(m, i, j) +=\n              0.5 * inv_metric.get(m, k) *\n              (phi.get(i, j + 1, k + 1) + phi.get(j, i + 1, k + 1) -\n               phi.get(k, i + 1, j + 1));\n        }\n      }\n    }\n  }\n}\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nauto christoffel_second_kind(\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inv_metric)\n    -> tnsr::Ijj<DataType, SpatialDim, Frame> {\n  tnsr::Ijj<DataType, SpatialDim, Frame> christoffel(\n      get_size(get<0, 0, 0>(phi)));\n  christoffel_second_kind(make_not_null(&christoffel), phi, inv_metric);\n  return christoffel;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> trace_christoffel(\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  auto trace = make_with_value<tnsr::a<DataType, SpatialDim, Frame>>(pi, 0.0);\n  trace_christoffel<DataType, SpatialDim, Frame>(\n      &trace, spacetime_normal_one_form, spacetime_normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi);\n  return trace;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid trace_christoffel(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> trace,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  set_number_of_grid_points(trace, spacetime_normal_one_form);\n  get<0>(*trace) = 0.0;\n  // Compute common terms between components.\n  for (size_t b = 0; b < SpatialDim + 1; ++b) {\n    get<0>(*trace) -= 0.5 * pi.get(b, b) * inverse_spacetime_metric.get(b, b);\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      get<0>(*trace) -= 0.5 * spacetime_normal_vector.get(i + 1) *\n                        phi.get(i, b, b) * inverse_spacetime_metric.get(b, b);\n    }\n    for (size_t c = b + 1; c < SpatialDim + 1; ++c) {\n      get<0>(*trace) -= pi.get(b, c) * inverse_spacetime_metric.get(b, c);\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        get<0>(*trace) -= spacetime_normal_vector.get(i + 1) *\n                          phi.get(i, b, c) * inverse_spacetime_metric.get(b, c);\n      }\n    }\n  }\n\n  // Compute spatial components\n  for (size_t a = 1; a < SpatialDim + 1; ++a) {\n    trace->get(a) = spacetime_normal_one_form.get(a) * get<0>(*trace);\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        trace->get(a) +=\n            inverse_spatial_metric.get(i, j) * phi.get(i, j + 1, a);\n      }\n    }\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      trace->get(a) += spacetime_normal_vector.get(b) * pi.get(b, a);\n      // a is always > 0 in this case so delta^i_a is taken care of.\n      trace->get(a) -=\n          0.5 * phi.get(a - 1, b, b) * inverse_spacetime_metric.get(b, b);\n      for (size_t c = b + 1; c < SpatialDim + 1; ++c) {\n        trace->get(a) -=\n            phi.get(a - 1, b, c) * inverse_spacetime_metric.get(b, c);\n      }\n    }\n  }\n  // Now set 0 component\n  get<0>(*trace) *= get<0>(spacetime_normal_one_form);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      get<0>(*trace) += inverse_spatial_metric.get(i, j) * phi.get(i, j + 1, 0);\n    }\n  }\n  for (size_t b = 0; b < SpatialDim + 1; ++b) {\n    get<0>(*trace) += spacetime_normal_vector.get(b) * pi.get(b, 0);\n  }\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void gh::christoffel_second_kind(                                   \\\n      const gsl::not_null<tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          christoffel,                                                         \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,               \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>& inv_metric);        \\\n  template tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>                      \\\n  gh::christoffel_second_kind(                                                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,               \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>& inv_metric);        \\\n  template void gh::trace_christoffel(                                         \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*>       \\\n          trace,                                                               \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                      \\\n          spacetime_normal_one_form,                                           \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                      \\\n          spacetime_normal_vector,                                             \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          inverse_spacetime_metric,                                            \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);              \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)> gh::trace_christoffel( \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                      \\\n          spacetime_normal_one_form,                                           \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                      \\\n          spacetime_normal_vector,                                             \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          inverse_spacetime_metric,                                            \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial,\n                         Frame::Spherical<Frame::Inertial>,\n                         Frame::Spherical<Frame::Grid>))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes spatial Christoffel symbol of the 2nd kind from the\n * the generalized harmonic spatial derivative variable and the\n * inverse spatial metric.\n *\n * \\details\n * If \\f$ \\Phi_{kab} \\f$ is the generalized harmonic spatial derivative\n * variable \\f$ \\Phi_{kab} = \\partial_k g_{ab}\\f$ and \\f$\\gamma^{ij}\\f$ is the\n * inverse spatial metric, the Christoffel symbols are\n * \\f[\n *   \\Gamma^m_{ij} = \\frac{1}{2}\\gamma^{mk}(\\Phi_{ijk}+\\Phi_{jik}-\\Phi_{kij}).\n * \\f]\n *\n * In the not_null version, no memory allocations are performed if the\n * output tensor already has the correct size.\n *\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid christoffel_second_kind(\n    const gsl::not_null<tnsr::Ijj<DataType, SpatialDim, Frame>*> christoffel,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inv_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nauto christoffel_second_kind(\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::II<DataType, SpatialDim, Frame>& inv_metric)\n    -> tnsr::Ijj<DataType, SpatialDim, Frame>;\n/// @}\n\n/// @{\n/*!\n * \\brief Compute \\f$\\Gamma_a\\f$ from the generalized harmonic evolved\n * variables.\n *\n * Starting from Eq. (40) of \\cite Lindblom2005qh we get\n *\n * \\f{align*}{\n *  \\Gamma_a &= \\gamma^{ij}\\Phi_{ija} + n^b \\Pi_{ab} -\n *   \\frac{1}{2}\\gamma^{i}{}_ag^{bc}\\Phi_{ibc} - \\frac{1}{2}n_ag^{bc}\\Pi_{bc}\n *   \\\\\n *   \\Gamma_a &= \\gamma^{ij}\\Phi_{ija} + n^b \\Pi_{ab} -\\frac{1}{2}\n *               n_a g^{bc} \\left(n^i \\Phi_{ibc} + \\Pi_{bc}\\right)\n *               - \\frac{1}{2} \\delta^i_a g^{bc} \\Phi_{ibc}\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> trace_christoffel(\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid trace_christoffel(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> trace,\n    const tnsr::a<DataType, SpatialDim, Frame>& spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n\n/// \\cond\nnamespace gh::OptionTags {\nstruct Group;\n}  // namespace gh::OptionTags\n/// \\endcond\n\nnamespace gh {\nnamespace OptionTags {\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma0 {\n  using type =\n      std::unique_ptr<::ConstraintDamping::DampingFunction<VolumeDim, Fr>>;\n  static constexpr Options::String help{\n      \"DampingFunction for damping parameter gamma0\"};\n  using group = gh::OptionTags::Group;\n};\n\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma1 {\n  using type =\n      std::unique_ptr<::ConstraintDamping::DampingFunction<VolumeDim, Fr>>;\n  static constexpr Options::String help{\n      \"DampingFunction for damping parameter gamma1\"};\n  using group = gh::OptionTags::Group;\n};\n\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma2 {\n  using type =\n      std::unique_ptr<::ConstraintDamping::DampingFunction<VolumeDim, Fr>>;\n  static constexpr Options::String help{\n      \"DampingFunction for damping parameter gamma2\"};\n  using group = gh::OptionTags::Group;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief Constraint dammping parameter \\f$\\gamma_0\\f$ for the generalized\n * harmonic system (cf. \\cite Lindblom2005qh).\n */\nstruct ConstraintGamma0 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Constraint dammping parameter \\f$\\gamma_1\\f$ for the generalized\n * harmonic system (cf. \\cite Lindblom2005qh).\n */\nstruct ConstraintGamma1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Constraint dammping parameter \\f$\\gamma_2\\f$ for the generalized\n * harmonic system (cf. \\cite Lindblom2005qh).\n */\nstruct ConstraintGamma2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief A DampingFunction to compute the constraint damping parameter\n * \\f$\\gamma_0\\f$.\n */\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma0 : db::SimpleTag {\n  using DampingFunctionType =\n      ::ConstraintDamping::DampingFunction<VolumeDim, Fr>;\n  using type = std::unique_ptr<DampingFunctionType>;\n  using option_tags =\n      tmpl::list<::gh::OptionTags::DampingFunctionGamma0<VolumeDim, Fr>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& damping_function) {\n    return damping_function->get_clone();\n  }\n};\n\n/*!\n * \\brief A DampingFunction to compute the constraint damping parameter\n * \\f$\\gamma_0\\f$.\n */\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma1 : db::SimpleTag {\n  using DampingFunctionType =\n      ::ConstraintDamping::DampingFunction<VolumeDim, Fr>;\n  using type = std::unique_ptr<DampingFunctionType>;\n  using option_tags =\n      tmpl::list<::gh::OptionTags::DampingFunctionGamma1<VolumeDim, Fr>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& damping_function) {\n    return damping_function->get_clone();\n  }\n};\n\n/*!\n * \\brief A DampingFunction to compute the constraint damping parameter\n * \\f$\\gamma_0\\f$.\n */\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma2 : db::SimpleTag {\n  using DampingFunctionType =\n      ::ConstraintDamping::DampingFunction<VolumeDim, Fr>;\n  using type = std::unique_ptr<DampingFunctionType>;\n  using option_tags =\n      tmpl::list<::gh::OptionTags::DampingFunctionGamma2<VolumeDim, Fr>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& damping_function) {\n    return damping_function->get_clone();\n  }\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace Tags\n}  // namespace domain\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\nnamespace gh::Tags {\n/*!\n * \\brief Computes the constraint damping parameter \\f$\\gamma_0\\f$ from the\n * coordinates and a DampingFunction.\n *\n * \\details Can be retrieved using\n * `gh::Tags::ConstraintGamma0`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct ConstraintGamma0Compute : ConstraintGamma0, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<DampingFunctionGamma0<SpatialDim, Frame>,\n                 domain::Tags::Coordinates<SpatialDim, Frame>, ::Tags::Time,\n                 ::domain::Tags::FunctionsOfTime>;\n  using return_type = Scalar<DataVector>;\n\n  static constexpr void function(\n      const gsl::not_null<Scalar<DataVector>*> gamma,\n      const ::ConstraintDamping::DampingFunction<SpatialDim, Frame>&\n          damping_function,\n      const tnsr::I<DataVector, SpatialDim, Frame>& coords, const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    damping_function(gamma, coords, time, functions_of_time);\n  }\n\n  using base = ConstraintGamma0;\n};\n\n/*!\n * \\brief Computes the constraint damping parameter \\f$\\gamma_1\\f$ from the\n * coordinates and a DampingFunction.\n *\n * \\details Can be retrieved using\n * `gh::Tags::ConstraintGamma1`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct ConstraintGamma1Compute : ConstraintGamma1, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<DampingFunctionGamma1<SpatialDim, Frame>,\n                 domain::Tags::Coordinates<SpatialDim, Frame>, ::Tags::Time,\n                 ::domain::Tags::FunctionsOfTime>;\n  using return_type = Scalar<DataVector>;\n\n  static constexpr void function(\n      const gsl::not_null<Scalar<DataVector>*> gamma1,\n      const ::ConstraintDamping::DampingFunction<SpatialDim, Frame>&\n          damping_function,\n      const tnsr::I<DataVector, SpatialDim, Frame>& coords, const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    damping_function(gamma1, coords, time, functions_of_time);\n  }\n\n  using base = ConstraintGamma1;\n};\n\n/*!\n * \\brief Computes the constraint damping parameter \\f$\\gamma_2\\f$ from the\n * coordinates and a DampingFunction.\n *\n * \\details Can be retrieved using\n * `gh::Tags::ConstraintGamma2`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct ConstraintGamma2Compute : ConstraintGamma2, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<DampingFunctionGamma2<SpatialDim, Frame>,\n                 domain::Tags::Coordinates<SpatialDim, Frame>, ::Tags::Time,\n                 ::domain::Tags::FunctionsOfTime>;\n  using return_type = Scalar<DataVector>;\n\n  static constexpr void function(\n      const gsl::not_null<Scalar<DataVector>*> gamma,\n      const ::ConstraintDamping::DampingFunction<SpatialDim, Frame>&\n          damping_function,\n      const tnsr::I<DataVector, SpatialDim, Frame>& coords, const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    damping_function(gamma, coords, time, functions_of_time);\n  }\n\n  using base = ConstraintGamma2;\n};\n}  // namespace gh::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/CovariantDerivOfExtrinsicCurvature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/CovariantDerivOfExtrinsicCurvature.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ijj<DataType, SpatialDim, Frame> covariant_deriv_of_extrinsic_curvature(\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal_vector,\n    const tnsr::Ijj<DataType, SpatialDim, Frame>&\n        spatial_christoffel_second_kind,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi) {\n  tnsr::ijj<DataType, SpatialDim, Frame> d_extrinsic_curvature(\n      get_size(get<0>(spacetime_unit_normal_vector)));\n  gh::covariant_deriv_of_extrinsic_curvature<DataType, SpatialDim, Frame>(\n      make_not_null(&d_extrinsic_curvature), extrinsic_curvature,\n      spacetime_unit_normal_vector, spatial_christoffel_second_kind,\n      inverse_spacetime_metric, phi, d_pi, d_phi);\n  return d_extrinsic_curvature;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid covariant_deriv_of_extrinsic_curvature(\n    const gsl::not_null<tnsr::ijj<DataType, SpatialDim, Frame>*>\n        d_extrinsic_curvature,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal_vector,\n    const tnsr::Ijj<DataType, SpatialDim, Frame>&\n        spatial_christoffel_second_kind,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi) {\n  // Ordinary derivative first\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        d_extrinsic_curvature->get(k, i, j) = d_pi.get(k, i + 1, j + 1);\n        for (size_t a = 0; a <= SpatialDim; ++a) {\n          d_extrinsic_curvature->get(k, i, j) +=\n              (d_phi.get(k, i, j + 1, a) + d_phi.get(k, j, i + 1, a)) *\n              spacetime_unit_normal_vector.get(a);\n          for (size_t b = 0; b <= SpatialDim; ++b) {\n            for (size_t c = 0; c <= SpatialDim; ++c) {\n              d_extrinsic_curvature->get(k, i, j) -=\n                  (phi.get(i, j + 1, a) + phi.get(j, i + 1, a)) *\n                  spacetime_unit_normal_vector.get(b) *\n                  (inverse_spacetime_metric.get(c, a) +\n                   0.5 * spacetime_unit_normal_vector.get(c) *\n                       spacetime_unit_normal_vector.get(a)) *\n                  phi.get(k, c, b);\n            }\n          }\n        }\n      }\n    }\n  }\n\n  // Now add gamma terms\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        d_extrinsic_curvature->get(k, i, j) =\n            0.5 * d_extrinsic_curvature->get(k, i, j) -\n            spatial_christoffel_second_kind.get(0, i, k) *\n                extrinsic_curvature.get(0, j) -\n            spatial_christoffel_second_kind.get(0, j, k) *\n                extrinsic_curvature.get(0, i);\n        for (size_t l = 1; l < SpatialDim; ++l) {\n          d_extrinsic_curvature->get(k, i, j) -=\n              spatial_christoffel_second_kind.get(l, i, k) *\n                  extrinsic_curvature.get(l, j) +\n              spatial_christoffel_second_kind.get(l, j, k) *\n                  extrinsic_curvature.get(l, i);\n        }\n      }\n    }\n  }\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                         \\\n  template tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>            \\\n  gh::covariant_deriv_of_extrinsic_curvature(                        \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&           \\\n          extrinsic_curvature,                                       \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&            \\\n          spacetime_unit_normal_vector,                              \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&          \\\n          spatial_christoffel_second_kind,                           \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&           \\\n          inverse_spacetime_metric,                                  \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,     \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,    \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi); \\\n  template void gh::covariant_deriv_of_extrinsic_curvature(          \\\n      gsl::not_null<tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          d_extrinsic_curvature,                                     \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&           \\\n          extrinsic_curvature,                                       \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&            \\\n          spacetime_unit_normal_vector,                              \\\n      const tnsr::Ijj<DTYPE(data), DIM(data), FRAME(data)>&          \\\n          spatial_christoffel_second_kind,                           \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&           \\\n          inverse_spacetime_metric,                                  \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,     \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& d_pi,    \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& d_phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/CovariantDerivOfExtrinsicCurvature.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the covariant derivative of extrinsic curvature from\n * generalized harmonic variables and the spacetime normal vector.\n *\n * \\details If \\f$ \\Pi_{ab} \\f$ and \\f$ \\Phi_{iab} \\f$ are the generalized\n * harmonic conjugate momentum and spatial derivative variables, and if\n * \\f$n^a\\f$ is the spacetime normal vector, then the extrinsic curvature\n * can be written as\n * \\f{equation}\\label{eq:kij}\n *     K_{ij} = \\frac{1}{2} \\Pi_{ij} + \\Phi_{(ij)a} n^a,\n * \\f}\n * and its covariant derivative as\n * \\f{equation}\\label{eq:covkij}\n * \\nabla_k K_{ij} = \\partial_k K_{ij} - \\Gamma^l{}_{ik} K_{lj}\n *                                     - \\Gamma^l{}_{jk} K_{li},\n * \\f}\n * where \\f$\\Gamma^k{}_{ij}\\f$ are Christoffel symbols of the second kind.\n * The partial derivatives of extrinsic curvature can be computed as\n * \\f{equation}\\label{eq:pdkij}\n * \\partial_k K_{ij} =\n *     \\frac{1}{2}\\left(\\partial_k \\Pi_{ij} +\n *                      \\left(\\partial_k \\Phi_{ija} +\n *                            \\partial_k \\Phi_{jia}\\right) n^a +\n *                      \\left(\\Phi_{ija} + \\Phi_{jia}\\right) \\partial_k n^a\n *               \\right),\n * \\f}\n * where we have access to all terms except the spatial derivatives of the\n * spacetime unit normal vector \\f$\\partial_k n^a\\f$. Given that\n * \\f$n^a=(1/\\alpha, -\\beta^i /\\alpha)\\f$, the temporal portion of\n * \\f$\\partial_k n^a\\f$ can be computed as:\n * \\f{align}\n * \\partial_k n^0 =& -\\frac{1}{\\alpha^2} \\partial_k \\alpha, \\nonumber \\\\\n *                =& -\\frac{1}{\\alpha^2} (-\\alpha/2) n^a \\Phi_{kab} n^b,\n *                   \\nonumber \\\\\n *                =& \\frac{1}{2\\alpha} n^a \\Phi_{kab} n^b, \\nonumber \\\\\n *                =& \\frac{1}{2} n^0 n^a \\Phi_{kab} n^b, \\nonumber \\\\\n *                =& -\\left(g^{0a} +\n *                          \\frac{1}{2}n^0 n^a\\right) \\Phi_{kab} n^b,\n * \\f}\n * where we use the expression for \\f$\\partial_k \\alpha\\f$ from\n * \\ref spatial_deriv_of_lapse; while the spatial portion of the same can be\n * computed as:\n * \\f{align}\n * \\partial_k n^i =& -\\partial_k (\\beta^i/\\alpha)\n *                 = -\\frac{1}{\\alpha}\\partial_k \\beta^i\n *                   + \\frac{\\beta^i}{\\alpha^2}\\partial_k \\alpha ,\\nonumber \\\\\n *                =& -\\frac{1}{2}\\frac{\\beta^i}{\\alpha} n^a\\Phi_{kab}n^b\n *                   -\\left(g^{ia}\n *                          + n^i n^a\\right) \\Phi_{kab} n^b, \\nonumber\\\\\n *                =& -\\left(g^{ia} + \\frac{1}{2}n^i n^a\\right) \\Phi_{kab}n^b,\n * \\f}\n * where we use the expression for \\f$\\partial_k \\beta^i\\f$ from\n * \\ref spatial_deriv_of_shift. Combining the last two equations, we find that\n * \\f{equation}\n * \\partial_k n^a = -\\left(g^{ab} + \\frac{1}{2}n^a n^b\\right)\\Phi_{kbc}n^c,\n * \\f}\n * and using Eq.(\\f$\\ref{eq:covkij}\\f$) and Eq.(\\f$\\ref{eq:pdkij}\\f$) with this,\n * we can compute the covariant derivative of the extrinsic curvature as:\n * \\f{equation}\n * \\nabla_k K_{ij} =\n *     \\frac{1}{2}\\left(\\partial_k \\Pi_{ij} +\n *                      \\left(\\partial_k \\Phi_{ija} +\n *                            \\partial_k \\Phi_{jia}\\right) n^a -\n *                      \\left(\\Phi_{ija} + \\Phi_{jia}\\right)\n *                      \\left(g^{ab} + \\frac{1}{2}n^a n^b\\right)\n *                      \\Phi_{kbc}n^c\n *                \\right) - \\Gamma^l{}_{ik} K_{lj} - \\Gamma^l{}_{jk} K_{li} \\f}.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ijj<DataType, SpatialDim, Frame> covariant_deriv_of_extrinsic_curvature(\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal_vector,\n    const tnsr::Ijj<DataType, SpatialDim, Frame>&\n        spatial_christoffel_second_kind,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid covariant_deriv_of_extrinsic_curvature(\n    gsl::not_null<tnsr::ijj<DataType, SpatialDim, Frame>*>\n        d_extrinsic_curvature,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal_vector,\n    const tnsr::Ijj<DataType, SpatialDim, Frame>&\n        spatial_christoffel_second_kind,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& d_pi,\n    const tnsr::ijaa<DataType, SpatialDim, Frame>& d_phi);\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid deriv_spatial_metric(\n    const gsl::not_null<tnsr::ijj<DataType, SpatialDim, Frame>*>\n        d_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  if (UNLIKELY(get_size(get<0, 0, 0>(*d_spatial_metric)) !=\n               get_size(get<0, 0, 0>(phi)))) {\n    *d_spatial_metric =\n        tnsr::ijj<DataType, SpatialDim, Frame>(get_size(get<0, 0, 0>(phi)));\n  }\n  for (size_t k = 0; k < SpatialDim; ++k) {\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = i; j < SpatialDim; ++j) {\n        d_spatial_metric->get(k, i, j) = phi.get(k, i + 1, j + 1);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ijj<DataType, SpatialDim, Frame> deriv_spatial_metric(\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  tnsr::ijj<DataType, SpatialDim, Frame> d_spatial_metric{};\n  gh::deriv_spatial_metric<DataType, SpatialDim, Frame>(\n      make_not_null(&d_spatial_metric), phi);\n  return d_spatial_metric;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void gh::deriv_spatial_metric(                                  \\\n      const gsl::not_null<tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          d_spatial_metric,                                                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);          \\\n  template tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::deriv_spatial_metric(                                                \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes spatial derivatives of the spatial metric from\n *        the generalized harmonic spatial derivative variable.\n *\n * \\details If \\f$ \\Phi_{kab} \\f$ is the generalized\n * harmonic spatial derivative variable, then the derivatives of the\n * spatial metric are\n * \\f[\n *      \\partial_k \\gamma_{ij} = \\Phi_{kij}\n * \\f]\n *\n * This quantity is needed for computing spatial Christoffel symbols.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid deriv_spatial_metric(\n    gsl::not_null<tnsr::ijj<DataType, SpatialDim, Frame>*> d_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ijj<DataType, SpatialDim, Frame> deriv_spatial_metric(\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get spatial derivatives of the spatial metric from\n *        the generalized harmonic spatial derivative variable.\n *\n * \\details See `deriv_spatial_metric()`. Can be retrieved using\n * `gr::Tags::SpatialMetric` wrapped in `::Tags::deriv`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct DerivSpatialMetricCompute\n    : ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<Phi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::ijj<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ijj<DataVector, SpatialDim, Frame>*>,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &deriv_spatial_metric<DataVector, SpatialDim, Frame>);\n\n  using base =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Expansion1D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Expansion1D.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, typename Frame>\nvoid expansion1D(const gsl::not_null<Scalar<DataType>*> expansion,\n                 const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n                 const tnsr::ijj<DataType, 3, Frame>& deriv_spatial_metric,\n                 const tnsr::ii<DataType, 3, Frame>& ext_curvature,\n                 const tnsr::I<DataType, 3, Frame>& coords) {\n  expansion->get() = (2.0 * spatial_metric.get(2, 2) +\n                      get<0>(coords) * deriv_spatial_metric.get(0, 2, 2)) /\n                         (get<0>(coords) * spatial_metric.get(2, 2) *\n                          sqrt(spatial_metric.get(0, 0))) -\n                     2.0 * ext_curvature.get(2, 2) / spatial_metric.get(2, 2);\n}\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType> expansion1D(\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const tnsr::ijj<DataType, 3, Frame>& deriv_spatial_metric,\n    const tnsr::ii<DataType, 3, Frame>& ext_curvature,\n    const tnsr::I<DataType, 3, Frame>& coords) {\n  Scalar<DataType> var_exp_1D{};\n  gh::expansion1D<DataType, Frame>(make_not_null(&var_exp_1D), spatial_metric,\n                                   deriv_spatial_metric, ext_curvature, coords);\n  return var_exp_1D;\n}\n}  // namespace gh\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void gh::expansion1D(                                          \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> var_exp_1D,               \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& spatial_metric,        \\\n      const tnsr::ijj<DTYPE(data), 3, FRAME(data)>& deriv_spatial_metric, \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& ext_curvature,         \\\n      const tnsr::I<DTYPE(data), 3, FRAME(data)>& coords);                \\\n  template Scalar<DTYPE(data)> gh::expansion1D(                           \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& spatial_metric,        \\\n      const tnsr::ijj<DTYPE(data), 3, FRAME(data)>& deriv_spatial_metric, \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& ext_curvature,         \\\n      const tnsr::I<DTYPE(data), 3, FRAME(data)>& coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Expansion1D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the expansion \\f$\\Theta\\f$ for 1D apparent horizon finding.\n *\n * \\details Calculate the expansion to find apparent horizon for 1D\n * cartoon domain. Implements Eq. (7.41) of \\cite BaumgarteShapiro,\n * \\f$\\Theta\\ = -\\frac{1}{\\sqrt{2}} m^{ij} \\Big( s_k \\Gamma^k_{ij}\n * + K_{ij} \\Big) \\f$, assuming the calculations are performed on the Cartesian\n * x axis.\n */\ntemplate <typename DataType, typename Frame>\nvoid expansion1D(gsl::not_null<Scalar<DataType>*> expansion,\n                 const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n                 const tnsr::ijj<DataType, 3, Frame>& deriv_spatial_metric,\n                 const tnsr::ii<DataType, 3, Frame>& ext_curvature,\n                 const tnsr::I<DataType, 3, Frame>& coords);\n\ntemplate <typename DataType, typename Frame>\nScalar<DataType> expansion1D(\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const tnsr::ijj<DataType, 3, Frame>& deriv_spatial_metric,\n    const tnsr::ii<DataType, 3, Frame>& ext_curvature,\n    const tnsr::I<DataType, 3, Frame>& coords);\n/// @}\n\nnamespace Tags {\ntemplate <typename DataType>\nstruct Expansion1D : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief Compute item for the 1D expansion.\n *\n * \\details Calculate the expansion for spherically symmetric cartoon horizon\n * finding.  See `expansion1D()`. Can be retrieved using\n * `gh::Tags::Expansion1D`.\n */\ntemplate <typename Frame>\nstruct Expansion1DCompute : Expansion1D<DataVector>, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::SpatialMetric<DataVector, 3, Frame>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3, Frame>,\n                               tmpl::size_t<3>, Frame>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame>,\n                 domain::Tags::Coordinates<3, Frame> >;\n\n  using return_type = Scalar<DataVector>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*>, const tnsr::ii<DataVector, 3, Frame>&,\n      const tnsr::ijj<DataVector, 3, Frame>&,\n      const tnsr::ii<DataVector, 3, Frame>&,\n      const tnsr::I<DataVector, 3, Frame>&)>(&expansion1D<DataVector, Frame>);\n  using base = Expansion1D<DataVector>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid extrinsic_curvature(\n    const gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> ex_curv,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  set_number_of_grid_points(ex_curv, spacetime_normal_vector);\n  for (auto& component : *ex_curv) {\n    component = 0.0;\n  }\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      for (size_t a = 0; a <= SpatialDim; ++a) {\n        ex_curv->get(i, j) += 0.5 *\n                              (phi.get(i, j + 1, a) + phi.get(j, i + 1, a)) *\n                              spacetime_normal_vector.get(a);\n      }\n      ex_curv->get(i, j) += 0.5 * pi.get(i + 1, j + 1);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> extrinsic_curvature(\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  tnsr::ii<DataType, SpatialDim, Frame> ex_curv{};\n  extrinsic_curvature(make_not_null(&ex_curv), spacetime_normal_vector, pi,\n                      phi);\n  return ex_curv;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void gh::extrinsic_curvature(                                  \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          ex_curv,                                                        \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          spacetime_normal_vector,                                        \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,            \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);         \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::extrinsic_curvature(                                                \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          spacetime_normal_vector,                                        \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,            \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace Tags\n}  // namespace domain\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes extrinsic curvature from generalized harmonic variables\n *        and the spacetime normal vector.\n *\n * \\details If \\f$ \\Pi_{ab} \\f$ and \\f$ \\Phi_{iab} \\f$ are the generalized\n * harmonic conjugate momentum and spatial derivative variables, and if\n * \\f$n^a\\f$ is the spacetime normal vector, then the extrinsic curvature\n * is computed as\n * \\f{align}\n *     K_{ij} &= \\frac{1}{2} \\Pi_{ij} + \\Phi_{(ij)a} n^a\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid extrinsic_curvature(\n    gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> ex_curv,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> extrinsic_curvature(\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_normal_vector,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get extrinsic curvature from generalized harmonic\n * variables and the spacetime normal vector.\n *\n * \\details See `extrinsic_curvature()`. Can be retrieved using\n * `gr::Tags::ExtrinsicCurvature`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct ExtrinsicCurvatureCompute\n    : gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n                 Pi<DataVector, SpatialDim, Frame>,\n                 Phi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::ii<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ii<DataVector, SpatialDim, Frame>*>,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &extrinsic_curvature<DataVector, SpatialDim, Frame>);\n\n  using base = gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim, Frame>;\n};\n\n/*!\n * \\brief Compute item to get the trace of extrinsic curvature from generalized\n * harmonic variables and the spacetime normal vector.\n *\n * \\details See `extrinsic_curvature()` for how the extrinsic curvature\n * \\f$ K_{ij}\\f$ is computed. Its trace is taken as\n * \\f{align}\n *     tr(K) &= g^{ij} K_{ij}.\n * \\f}\n *\n * Can be retrieved using `gr::Tags::TraceExtrinsicCurvature`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct TraceExtrinsicCurvatureCompute\n    : gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, Frame>>;\n\n  using return_type = Scalar<DataVector>;\n\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<Scalar<DataVector>*>,\n                           const tnsr::ii<DataVector, SpatialDim, Frame>&,\n                           const tnsr::II<DataVector, SpatialDim, Frame>&)>(\n          &trace);\n\n  using base = gr::Tags::TraceExtrinsicCurvature<DataVector>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid gauge_source(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> gauge_source_h,\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const Scalar<DataType>& trace_extrinsic_curvature,\n    const tnsr::i<DataType, SpatialDim, Frame>&\n        trace_christoffel_last_indices) {\n  set_number_of_grid_points(gauge_source_h, lapse);\n  for (auto& component : *gauge_source_h) {\n    component = 0.0;\n  }\n  DataType one_over_lapse = 1.0 / get(lapse);\n\n  // Temporary to avoid more nested loops.\n  auto temp = dt_shift;\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t k = 0; k < SpatialDim; ++k) {\n      temp.get(i) -= shift.get(k) * deriv_shift.get(k, i);\n    }\n  }\n\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t k = 0; k < SpatialDim; ++k) {\n      gauge_source_h->get(i + 1) += spatial_metric.get(i, k) * temp.get(k);\n    }\n    gauge_source_h->get(i + 1) *= square(one_over_lapse);\n  }\n\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    gauge_source_h->get(i + 1) += one_over_lapse * deriv_lapse.get(i) -\n                                  trace_christoffel_last_indices.get(i);\n  }\n\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    get<0>(*gauge_source_h) +=\n        shift.get(i) *\n        (gauge_source_h->get(i + 1) + deriv_lapse.get(i) * one_over_lapse);\n  }\n  get<0>(*gauge_source_h) -= one_over_lapse * get(dt_lapse) +\n                             get(lapse) * get(trace_extrinsic_curvature);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> gauge_source(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const Scalar<DataType>& trace_extrinsic_curvature,\n    const tnsr::i<DataType, SpatialDim, Frame>&\n        trace_christoffel_last_indices) {\n  tnsr::a<DataType, SpatialDim, Frame> gauge_source_h{};\n  gauge_source(make_not_null(&gauge_source_h), lapse, dt_lapse, deriv_lapse,\n               shift, dt_shift, deriv_shift, spatial_metric,\n               trace_extrinsic_curvature, trace_christoffel_last_indices);\n  return gauge_source_h;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void gh::gauge_source(                                            \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          gauge_source_h,                                                    \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse, \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& deriv_lapse,       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,             \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,          \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,      \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,   \\\n      const Scalar<DTYPE(data)>& trace_extrinsic_curvature,                  \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          trace_christoffel_last_indices);                                   \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)> gh::gauge_source(    \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse, \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& deriv_lapse,       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,             \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,          \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,      \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,   \\\n      const Scalar<DTYPE(data)>& trace_extrinsic_curvature,                  \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          trace_christoffel_last_indices);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief  Computes generalized harmonic gauge source function.\n * \\details If \\f$\\alpha, \\beta^i, \\gamma_{ij}, \\Gamma_{ijk}, K\\f$ are the\n * lapse, shift, spatial metric, spatial Christoffel symbols, and trace of the\n * extrinsic curvature, then we compute\n * \\f{align}\n * H_l &=\n * \\alpha^{-2} \\gamma_{il}(\\partial_t \\beta^i - \\beta^k \\partial_k \\beta^i)\n * + \\alpha^{-1} \\partial_l \\alpha - \\gamma^{km}\\Gamma_{lkm} \\\\\n * H_0 &= -\\alpha^{-1} \\partial_t \\alpha + \\alpha^{-1} \\beta^k\\partial_k \\alpha\n * + \\beta^k H_k - \\alpha K\n * \\f}\n * See Eqs. 8 and 9 of \\cite Lindblom2005qh\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid gauge_source(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> gauge_source_h,\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const Scalar<DataType>& trace_extrinsic_curvature,\n    const tnsr::i<DataType, SpatialDim, Frame>& trace_christoffel_last_indices);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> gauge_source(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const Scalar<DataType>& trace_extrinsic_curvature,\n    const tnsr::i<DataType, SpatialDim, Frame>& trace_christoffel_last_indices);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief  Compute item to get the implicit gauge source function from 3 + 1\n * quantities.\n *\n * \\details See `gauge_source()`. Can be retrieved using\n * `gh::Tags::GaugeH`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct GaugeHImplicitFrom3p1QuantitiesCompute\n    : GaugeH<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                               tmpl::size_t<SpatialDim>, Frame>,\n                 gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                 ::Tags::dt<gr::Tags::Shift<DataVector, SpatialDim, Frame>>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                               tmpl::size_t<SpatialDim>, Frame>,\n                 gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>,\n                 gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                 gr::Tags::TraceSpatialChristoffelFirstKind<DataVector,\n                                                            SpatialDim, Frame>>;\n\n  using return_type = tnsr::a<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*>,\n      const Scalar<DataVector>&, const Scalar<DataVector>&,\n      const tnsr::i<DataVector, SpatialDim, Frame>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::iJ<DataVector, SpatialDim, Frame>&,\n      const tnsr::ii<DataVector, SpatialDim, Frame>&, const Scalar<DataVector>&,\n      const tnsr::i<DataVector, SpatialDim, Frame>&)>(\n      &gauge_source<DataVector, SpatialDim, Frame>);\n\n  using base = GaugeH<DataVector, SpatialDim, Frame>;\n};\n\n/*!\n * \\brief  Compute item to get spacetime derivative of the gauge source function\n * from its spatial and time derivatives.\n *\n * \\details Can be retrieved using\n * `gh::Tags::SpacetimeDerivGaugeH`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct SpacetimeDerivGaugeHCompute\n    : SpacetimeDerivGaugeH<DataVector, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<::Tags::dt<gh::Tags::GaugeH<DataVector, SpatialDim, Frame>>,\n                 ::Tags::deriv<gh::Tags::GaugeH<DataVector, SpatialDim, Frame>,\n                               tmpl::size_t<SpatialDim>, Frame>>;\n\n  using return_type = tnsr::ab<DataVector, SpatialDim, Frame>;\n\n  static constexpr void function(\n      const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame>*>\n          spacetime_deriv_gauge_source,\n      const tnsr::a<DataVector, SpatialDim, Frame>& time_deriv_gauge_source,\n      const tnsr::ia<DataVector, SpatialDim, Frame>& deriv_gauge_source) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      spacetime_deriv_gauge_source->get(0, b) = time_deriv_gauge_source.get(b);\n      for (size_t a = 1; a < SpatialDim + 1; ++a) {\n        spacetime_deriv_gauge_source->get(a, b) =\n            deriv_gauge_source.get(a - 1, b);\n      }\n    }\n  }\n\n  using base = SpacetimeDerivGaugeH<DataVector, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid phi(const gsl::not_null<tnsr::iaa<DataType, SpatialDim, Frame>*> phi,\n         const Scalar<DataType>& lapse,\n         const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n         const tnsr::I<DataType, SpatialDim, Frame>& shift,\n         const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n         const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n         const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric) {\n  if (UNLIKELY(get_size(get<0, 0, 0>(*phi)) != get_size(get(lapse)))) {\n    *phi = tnsr::iaa<DataType, SpatialDim, Frame>(get_size(get(lapse)));\n  }\n  for (size_t k = 0; k < SpatialDim; ++k) {\n    phi->get(k, 0, 0) = -2. * get(lapse) * deriv_lapse.get(k);\n    for (size_t m = 0; m < SpatialDim; ++m) {\n      for (size_t n = 0; n < SpatialDim; ++n) {\n        phi->get(k, 0, 0) +=\n            deriv_spatial_metric.get(k, m, n) * shift.get(m) * shift.get(n) +\n            2. * spatial_metric.get(m, n) * shift.get(m) *\n                deriv_shift.get(k, n);\n      }\n    }\n\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      phi->get(k, 0, i + 1) = 0.;\n      for (size_t m = 0; m < SpatialDim; ++m) {\n        phi->get(k, 0, i + 1) +=\n            deriv_spatial_metric.get(k, m, i) * shift.get(m) +\n            spatial_metric.get(m, i) * deriv_shift.get(k, m);\n      }\n      for (size_t j = i; j < SpatialDim; ++j) {\n        phi->get(k, i + 1, j + 1) = deriv_spatial_metric.get(k, i, j);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::iaa<DataType, SpatialDim, Frame> phi(\n    const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric) {\n  tnsr::iaa<DataType, SpatialDim, Frame> var_phi{};\n  gh::phi<DataType, SpatialDim, Frame>(make_not_null(&var_phi), lapse,\n                                       deriv_lapse, shift, deriv_shift,\n                                       spatial_metric, deriv_spatial_metric);\n  return var_phi;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void gh::phi(                                                   \\\n      const gsl::not_null<tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          var_phi,                                                         \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& deriv_lapse,     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          deriv_spatial_metric);                                           \\\n  template tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)> gh::phi(         \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& deriv_lapse,     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          deriv_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace Tags\n}  // namespace domain\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the auxiliary variable \\f$\\Phi_{iab}\\f$ used by the\n * generalized harmonic formulation of Einstein's equations.\n *\n * \\details If \\f$ \\alpha, \\beta^i\\f$ and \\f$ \\gamma_{ij} \\f$ are the lapse,\n * shift and spatial metric respectively, then \\f$\\Phi_{iab} \\f$ is computed as\n *\n * \\f{align}\n *     \\Phi_{ktt} &= - 2 \\alpha \\partial_k \\alpha\n *                 + 2 \\gamma_{mn} \\beta^m \\partial_k \\beta^n\n *                 + \\beta^m \\beta^n \\partial_k \\gamma_{mn} \\\\\n *     \\Phi_{kti} &= \\gamma_{mi} \\partial_k \\beta^m\n *                 + \\beta^m \\partial_k \\gamma_{mi} \\\\\n *     \\Phi_{kij} &= \\partial_k \\gamma_{ij}\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid phi(gsl::not_null<tnsr::iaa<DataType, SpatialDim, Frame>*> phi,\n         const Scalar<DataType>& lapse,\n         const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n         const tnsr::I<DataType, SpatialDim, Frame>& shift,\n         const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n         const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n         const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::iaa<DataType, SpatialDim, Frame> phi(\n    const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, SpatialDim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item for the auxiliary variable \\f$\\Phi_{iab}\\f$ used by the\n * generalized harmonic formulation of Einstein's equations.\n *\n * \\details See `phi()`. Can be retrieved using\n * `gh::Tags::Phi`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct PhiCompute : Phi<DataVector, SpatialDim, Frame>, db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gr::Tags::Lapse<DataVector>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<SpatialDim>,\n                    Frame>,\n      gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>>;\n\n  using return_type = tnsr::iaa<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::iaa<DataVector, SpatialDim, Frame>*>,\n      const Scalar<DataVector>&, const tnsr::i<DataVector, SpatialDim, Frame>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::iJ<DataVector, SpatialDim, Frame>&,\n      const tnsr::ii<DataVector, SpatialDim, Frame>&,\n      const tnsr::ijj<DataVector, SpatialDim, Frame>&)>(\n      &phi<DataVector, SpatialDim, Frame>);\n\n  using base = Phi<DataVector, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid pi(const gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*> pi,\n        const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n        const tnsr::I<DataType, SpatialDim, Frame>& shift,\n        const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n        const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n        const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n        const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  if (UNLIKELY(get_size(get<0, 0>(*pi)) != get_size(get(lapse)))) {\n    *pi = tnsr::aa<DataType, SpatialDim, Frame>(get_size(get(lapse)));\n  }\n\n  get<0, 0>(*pi) = -2. * get(lapse) * get(dt_lapse);\n\n  for (size_t m = 0; m < SpatialDim; ++m) {\n    for (size_t n = 0; n < SpatialDim; ++n) {\n      get<0, 0>(*pi) +=\n          dt_spatial_metric.get(m, n) * shift.get(m) * shift.get(n) +\n          2. * spatial_metric.get(m, n) * shift.get(m) * dt_shift.get(n);\n    }\n  }\n\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    pi->get(0, i + 1) = 0.;\n    for (size_t m = 0; m < SpatialDim; ++m) {\n      pi->get(0, i + 1) += dt_spatial_metric.get(m, i) * shift.get(m) +\n                           spatial_metric.get(m, i) * dt_shift.get(m);\n    }\n    for (size_t j = i; j < SpatialDim; ++j) {\n      pi->get(i + 1, j + 1) = dt_spatial_metric.get(i, j);\n    }\n  }\n  for (size_t mu = 0; mu < SpatialDim + 1; ++mu) {\n    for (size_t nu = mu; nu < SpatialDim + 1; ++nu) {\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        pi->get(mu, nu) -= shift.get(i) * phi.get(i, mu, nu);\n      }\n      // Division by `lapse` here is somewhat more efficient (in Release mode)\n      // than pre-computing `one_over_lapse` outside the loop for DataVectors\n      // of `size` up to `50`. This is why we the next line is as it is.\n      pi->get(mu, nu) /= -get(lapse);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> pi(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  tnsr::aa<DataType, SpatialDim, Frame> pi{};\n  gh::pi<DataType, SpatialDim, Frame>(make_not_null(&pi), lapse, dt_lapse,\n                                      shift, dt_shift, spatial_metric,\n                                      dt_spatial_metric, phi);\n  return pi;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void gh::pi(                                                       \\\n      const gsl::not_null<tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          var_pi,                                                             \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse,  \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,              \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,           \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric, \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);             \\\n  template tnsr::aa<DTYPE(data), DIM(data), FRAME(data)> gh::pi(              \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse,  \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,              \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,           \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric, \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace Tags\n}  // namespace domain\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the conjugate momentum \\f$\\Pi_{ab}\\f$ of the spacetime metric\n * \\f$ g_{ab} \\f$.\n *\n * \\details If \\f$ \\alpha, \\beta^i\\f$ are the lapse and shift respectively, and\n * \\f$ \\Phi_{iab} = \\partial_i g_{ab} \\f$ then\n * \\f$\\Pi_{\\mu\\nu} = -\\frac{1}{\\alpha} ( \\partial_t g_{\\mu\\nu}  -\n *      \\beta^m \\Phi_{m\\mu\\nu}) \\f$ where \\f$ \\partial_t g_{ab} \\f$ is computed\n * as\n *\n * \\f{align}\n *     \\partial_t g_{tt} &= - 2 \\alpha \\partial_t \\alpha\n *                 + 2 \\gamma_{mn} \\beta^m \\partial_t \\beta^n\n *                 + \\beta^m \\beta^n \\partial_t \\gamma_{mn} \\\\\n *     \\partial_t g_{ti} &= \\gamma_{mi} \\partial_t \\beta^m\n *                 + \\beta^m \\partial_t \\gamma_{mi} \\\\\n *     \\partial_t g_{ij} &= \\partial_t \\gamma_{ij}\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid pi(gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*> pi,\n        const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n        const tnsr::I<DataType, SpatialDim, Frame>& shift,\n        const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n        const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n        const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n        const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> pi(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item the conjugate momentum \\f$\\Pi_{ab}\\f$ of the spacetime\n * metric \\f$ g_{ab} \\f$.\n *\n * \\details See `pi()`. Can be retrieved using `gh::Tags::Pi`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct PiCompute : Pi<DataVector, SpatialDim, Frame>, db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gr::Tags::Lapse<DataVector>, ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n      gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n      ::Tags::dt<gr::Tags::Shift<DataVector, SpatialDim, Frame>>,\n      gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>,\n      ::Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>>,\n      Phi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::aa<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::aa<DataVector, SpatialDim, Frame>*>,\n      const Scalar<DataVector>&, const Scalar<DataVector>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::ii<DataVector, SpatialDim, Frame>&,\n      const tnsr::ii<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &pi<DataVector, SpatialDim, Frame>);\n\n  using base = Pi<DataVector, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/CovariantDerivOfExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfDetSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfNormOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLowerShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace GeneralizedHarmonic::py_bindings {\n\nnamespace {\ntemplate <size_t Dim>\nvoid bind_impl(py::module& m) {  // NOLINT\n  m.def(\n      \"christoffel_second_kind\",\n      static_cast<tnsr::Ijj<DataVector, Dim> (*)(\n          const tnsr::iaa<DataVector, Dim>&, const tnsr::II<DataVector, Dim>&)>(\n          &::gh::christoffel_second_kind),\n      py::arg(\"phi\"), py::arg(\"inv_metric\"));\n\n  m.def(\n      \"covariant_deriv_of_extrinsic_curvature\",\n      static_cast<tnsr::ijj<DataVector, Dim> (*)(\n          const tnsr::ii<DataVector, Dim>&, const tnsr::A<DataVector, Dim>&,\n          const tnsr::Ijj<DataVector, Dim>&, const tnsr::AA<DataVector, Dim>&,\n          const tnsr::iaa<DataVector, Dim>&, const tnsr::iaa<DataVector, Dim>&,\n          const tnsr::ijaa<DataVector, Dim>&)>(\n          &::gh::covariant_deriv_of_extrinsic_curvature),\n      py::arg(\"extrinsic_curvature\"), py::arg(\"spacetime_unit_normal_vector\"),\n      py::arg(\"spatial_christoffel_second_kind\"),\n      py::arg(\"inverse_spacetime_metric\"), py::arg(\"phi\"), py::arg(\"d_pi\"),\n      py::arg(\"d_phi\"));\n\n  m.def(\"deriv_spatial_metric\",\n        static_cast<tnsr::ijj<DataVector, Dim> (*)(\n            const tnsr::iaa<DataVector, Dim>&)>(&::gh::deriv_spatial_metric),\n        py::arg(\"phi\"));\n\n  m.def(\"extrinsic_curvature\",\n        static_cast<tnsr::ii<DataVector, Dim> (*)(\n            const tnsr::A<DataVector, Dim>&, const tnsr::aa<DataVector, Dim>&,\n            const tnsr::iaa<DataVector, Dim>&)>(&::gh::extrinsic_curvature),\n        py::arg(\"spacetime_normal_vector\"), py::arg(\"pi\"), py::arg(\"phi\"));\n\n  m.def(\"gauge_source\",\n        static_cast<tnsr::a<DataVector, Dim> (*)(\n            const Scalar<DataVector>&, const Scalar<DataVector>&,\n            const tnsr::i<DataVector, Dim>&, const tnsr::I<DataVector, Dim>&,\n            const tnsr::I<DataVector, Dim>&, const tnsr::iJ<DataVector, Dim>&,\n            const tnsr::ii<DataVector, Dim>&, const Scalar<DataVector>&,\n            const tnsr::i<DataVector, Dim>&)>(&::gh::gauge_source),\n        py::arg(\"lapse\"), py::arg(\"dt_lapse\"), py::arg(\"deriv_lapse\"),\n        py::arg(\"shift\"), py::arg(\"dt_shift\"), py::arg(\"deriv_shift\"),\n        py::arg(\"spatial_metric\"), py::arg(\"trace_extrinsic_curvature\"),\n        py::arg(\"trace_christoffel_last_indices\"));\n\n  m.def(\n      \"phi\",\n      static_cast<tnsr::iaa<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::i<DataVector, Dim>&,\n          const tnsr::I<DataVector, Dim>&, const tnsr::iJ<DataVector, Dim>&,\n          const tnsr::ii<DataVector, Dim>&, const tnsr::ijj<DataVector, Dim>&)>(\n          &::gh::phi),\n      py::arg(\"lapse\"), py::arg(\"deriv_lapse\"), py::arg(\"shift\"),\n      py::arg(\"deriv_shift\"), py::arg(\"spatial_metric\"),\n      py::arg(\"deriv_spatial_metric\"));\n\n  m.def(\"pi\",\n        static_cast<tnsr::aa<DataVector, Dim> (*)(\n            const Scalar<DataVector>&, const Scalar<DataVector>&,\n            const tnsr::I<DataVector, Dim>&, const tnsr::I<DataVector, Dim>&,\n            const tnsr::ii<DataVector, Dim>&, const tnsr::ii<DataVector, Dim>&,\n            const tnsr::iaa<DataVector, Dim>&)>(&::gh::pi),\n        py::arg(\"lapse\"), py::arg(\"dt_lapse\"), py::arg(\"shift\"),\n        py::arg(\"dt_shift\"), py::arg(\"spatial_metric\"),\n        py::arg(\"dt_spatial_metric\"), py::arg(\"phi\"));\n\n  m.def(\n      \"spacetime_deriv_of_det_spatial_metric\",\n      static_cast<tnsr::a<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::II<DataVector, Dim>&,\n          const tnsr::ii<DataVector, Dim>&, const tnsr::iaa<DataVector, Dim>&)>(\n          &::gh::spacetime_deriv_of_det_spatial_metric),\n      py::arg(\"sqrt_det_spatial_metric\"), py::arg(\"inverse_spatial_metric\"),\n      py::arg(\"dt_spatial_metric\"), py::arg(\"phi\"));\n\n  m.def(\n      \"spacetime_deriv_of_norm_of_shift\",\n      static_cast<tnsr::a<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n          const tnsr::ii<DataVector, Dim>&, const tnsr::II<DataVector, Dim>&,\n          const tnsr::AA<DataVector, Dim>&, const tnsr::A<DataVector, Dim>&,\n          const tnsr::iaa<DataVector, Dim>&, const tnsr::aa<DataVector, Dim>&)>(\n          &::gh::spacetime_deriv_of_norm_of_shift),\n      py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"spatial_metric\"),\n      py::arg(\"inverse_spatial_metric\"), py::arg(\"inverse_spacetime_metric\"),\n      py::arg(\"spacetime_unit_normal\"), py::arg(\"phi\"), py::arg(\"pi\"));\n\n  m.def(\"spatial_deriv_of_lapse\",\n        static_cast<tnsr::i<DataVector, Dim> (*)(\n            const Scalar<DataVector>&, const tnsr::A<DataVector, Dim>&,\n            const tnsr::iaa<DataVector, Dim>&)>(&::gh::spatial_deriv_of_lapse),\n        py::arg(\"lapse\"), py::arg(\"spacetime_unit_normal\"), py::arg(\"phi\"));\n\n  m.def(\n      \"time_deriv_of_lower_shift\",\n      static_cast<tnsr::i<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n          const tnsr::ii<DataVector, Dim>&, const tnsr::A<DataVector, Dim>&,\n          const tnsr::iaa<DataVector, Dim>&, const tnsr::aa<DataVector, Dim>&)>(\n          &::gh::time_deriv_of_lower_shift),\n      py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"spatial_metric\"),\n      py::arg(\"spacetime_unit_normal\"), py::arg(\"phi\"), py::arg(\"pi\"));\n\n  m.def(\n      \"spatial_deriv_of_shift\",\n      static_cast<tnsr::iJ<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::AA<DataVector, Dim>&,\n          const tnsr::A<DataVector, Dim>&, const tnsr::iaa<DataVector, Dim>&)>(\n          &::gh::spatial_deriv_of_shift),\n      py::arg(\"lapse\"), py::arg(\"inverse_spacetime_metric\"),\n      py::arg(\"spacetime_unit_normal\"), py::arg(\"phi\"));\n\n  m.def(\n      \"spatial_ricci_tensor\",\n      static_cast<tnsr::ii<DataVector, Dim> (*)(\n          const tnsr::iaa<DataVector, Dim>&, const tnsr::ijaa<DataVector, Dim>&,\n          const tnsr::II<DataVector, Dim>&)>(&::gh::spatial_ricci_tensor),\n      py::arg(\"phi\"), py::arg(\"deriv_phi\"), py::arg(\"inverse_spatial_metric\"));\n\n  m.def(\"time_deriv_of_lapse\",\n        static_cast<Scalar<DataVector> (*)(\n            const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n            const tnsr::A<DataVector, Dim>&, const tnsr::iaa<DataVector, Dim>&,\n            const tnsr::aa<DataVector, Dim>&)>(&::gh::time_deriv_of_lapse),\n        py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"spacetime_unit_normal\"),\n        py::arg(\"phi\"), py::arg(\"pi\"));\n\n  m.def(\n      \"time_deriv_of_shift\",\n      static_cast<tnsr::I<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n          const tnsr::II<DataVector, Dim>&, const tnsr::A<DataVector, Dim>&,\n          const tnsr::iaa<DataVector, Dim>&, const tnsr::aa<DataVector, Dim>&)>(\n          &::gh::time_deriv_of_shift),\n      py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"inverse_spatial_metric\"),\n      py::arg(\"spacetime_unit_normal\"), py::arg(\"phi\"), py::arg(\"pi\"));\n\n  m.def(\n      \"time_deriv_of_spatial_metric\",\n      static_cast<tnsr::ii<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n          const tnsr::iaa<DataVector, Dim>&, const tnsr::aa<DataVector, Dim>&)>(\n          &::gh::time_deriv_of_spatial_metric),\n      py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"phi\"), py::arg(\"pi\"));\n\n  m.def(\n      \"time_derivative_of_spacetime_metric\",\n      static_cast<tnsr::aa<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n          const tnsr::aa<DataVector, Dim>&, const tnsr::iaa<DataVector, Dim>&)>(\n          &::gh::time_derivative_of_spacetime_metric),\n      py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"pi\"), py::arg(\"phi\"));\n\n  m.def(\n      \"trace_christoffel\",\n      static_cast<tnsr::a<DataVector, Dim> (*)(\n          const tnsr::a<DataVector, Dim>&, const tnsr::A<DataVector, Dim>&,\n          const tnsr::II<DataVector, Dim>&, const tnsr::AA<DataVector, Dim>&,\n          const tnsr::aa<DataVector, Dim>&, const tnsr::iaa<DataVector, Dim>&)>(\n          &::gh::trace_christoffel),\n      py::arg(\"spacetime_normal_one_form\"), py::arg(\"spacetime_normal_vector\"),\n      py::arg(\"inverse_spatial_metric\"), py::arg(\"inverse_spacetime_metric\"),\n      py::arg(\"pi\"), py::arg(\"phi\"));\n}\n}  // namespace\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py_bindings::bind_impl<1>(m);\n  py_bindings::bind_impl<2>(m);\n  py_bindings::bind_impl<3>(m);\n}\n}  // namespace GeneralizedHarmonic::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyGeneralizedHarmonic\")\n\nspectre_python_add_module(\n    GeneralizedHarmonic\n    LIBRARY_NAME ${LIBRARY}\n    MODULE_PATH \"PointwiseFunctions/GeneralRelativity\"\n    SOURCES\n    Bindings.cpp\n    PYTHON_FILES\n    __init__.py\n)\n\nspectre_python_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n)\n\nspectre_python_link_libraries(\n    ${LIBRARY}\n    PRIVATE\n    DataStructures\n    GeneralizedHarmonic\n    pybind11::module\n    Utilities\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Ricci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Ricci.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid spatial_ricci_tensor(\n    const gsl::not_null<tnsr::ii<DataType, VolumeDim, Frame>*> ricci,\n    const tnsr::iaa<DataType, VolumeDim, Frame>& phi,\n    const tnsr::ijaa<DataType, VolumeDim, Frame>& deriv_phi,\n    const tnsr::II<DataType, VolumeDim, Frame>& inverse_spatial_metric) {\n  set_number_of_grid_points(ricci, phi);\n\n  TempBuffer<tmpl::list<::Tags::TempIjj<0, VolumeDim, Frame, DataType>,\n                        ::Tags::TempijK<0, VolumeDim, Frame, DataType>,\n                        ::Tags::Tempi<0, VolumeDim, Frame, DataType>,\n                        ::Tags::Tempi<1, VolumeDim, Frame, DataType>,\n                        ::Tags::TempI<0, VolumeDim, Frame, DataType>,\n                        ::Tags::TempI<1, VolumeDim, Frame, DataType>>>\n      local_buffer(get_size(get<0, 0>(inverse_spatial_metric)));\n\n  // Note that this is (1/2) times \\Phi^i_{jk} (without the trace)\n  auto& half_spatial_phi_Ijj =\n      get<::Tags::TempIjj<0, VolumeDim, Frame, DataType>>(local_buffer);\n  for (size_t k = 0; k < VolumeDim; ++k) {\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      for (size_t j = i; j < VolumeDim; ++j) {  // Symmetry\n        half_spatial_phi_Ijj.get(k, i, j) =\n            0.5 * inverse_spatial_metric.get(k, 0) * phi.get(0, 1 + i, 1 + j);\n        for (size_t l = 1; l < VolumeDim; ++l) {\n          half_spatial_phi_Ijj.get(k, i, j) +=\n              0.5 * inverse_spatial_metric.get(k, l) * phi.get(l, 1 + i, 1 + j);\n        }\n      }\n    }\n  }\n\n  // Note that this is (1/2) times \\Phi_{ij}^k (without the trace)\n  auto& half_spatial_phi_ijK =\n      get<::Tags::TempijK<0, VolumeDim, Frame, DataType>>(local_buffer);\n  for (size_t k = 0; k < VolumeDim; ++k) {\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      for (size_t j = 0; j < VolumeDim; ++j) {\n        half_spatial_phi_ijK.get(i, j, k) =\n            0.5 * inverse_spatial_metric.get(k, 0) * phi.get(i, 1 + j, 1);\n        for (size_t l = 1; l < VolumeDim; ++l) {\n          half_spatial_phi_ijK.get(i, j, k) +=\n              0.5 * inverse_spatial_metric.get(k, l) * phi.get(i, 1 + j, 1 + l);\n        }\n      }\n    }\n  }\n\n  // Note that these traces are actually (1/2) times d_k and b_k respectively\n  auto& half_spatial_phi_trace_second_third_indices =\n      get<::Tags::Tempi<0, VolumeDim, Frame, DataType>>(local_buffer);\n  auto& half_spatial_phi_trace_first_second_indices =\n      get<::Tags::Tempi<1, VolumeDim, Frame, DataType>>(local_buffer);\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    half_spatial_phi_trace_second_third_indices.get(i) =\n        half_spatial_phi_ijK.get(i, 0, 0);\n    half_spatial_phi_trace_first_second_indices.get(i) =\n        half_spatial_phi_Ijj.get(0, 0, i);\n    for (size_t j = 1; j < VolumeDim; ++j) {\n      half_spatial_phi_trace_second_third_indices.get(i) +=\n          half_spatial_phi_ijK.get(i, j, j);\n      half_spatial_phi_trace_first_second_indices.get(i) +=\n          half_spatial_phi_Ijj.get(j, j, i);\n    }\n  }\n\n  // Again, these traces are actually (1/2) times d^k and b^k respectively\n  auto& half_spatial_phi_trace_second_third_indices_I =\n      get<::Tags::TempI<0, VolumeDim, Frame, DataType>>(local_buffer);\n  auto& half_spatial_phi_trace_first_second_indices_I =\n      get<::Tags::TempI<1, VolumeDim, Frame, DataType>>(local_buffer);\n\n  raise_or_lower_index(\n      make_not_null(&half_spatial_phi_trace_second_third_indices_I),\n      half_spatial_phi_trace_second_third_indices, inverse_spatial_metric);\n  raise_or_lower_index(\n      make_not_null(&half_spatial_phi_trace_first_second_indices_I),\n      half_spatial_phi_trace_first_second_indices, inverse_spatial_metric);\n\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = i; j < VolumeDim; ++j) {  // Symmetry\n      ricci->get(i, j) = 0.;\n      for (size_t k = 0; k < VolumeDim; ++k) {\n        ricci->get(i, j) +=\n            0.5 *\n            (phi.get(i, 1 + j, 1 + k) + phi.get(j, 1 + i, 1 + k) -\n             phi.get(k, 1 + i, 1 + j)) *\n            (half_spatial_phi_trace_second_third_indices_I.get(k) -\n             2.0 * half_spatial_phi_trace_first_second_indices_I.get(k));\n        for (size_t l = 0; l < VolumeDim; ++l) {\n          ricci->get(i, j) += 0.25 * inverse_spatial_metric.get(k, l) *\n                                  (deriv_phi.get(j, l, 1 + k, 1 + i) +\n                                   deriv_phi.get(i, l, 1 + k, 1 + j) -\n                                   deriv_phi.get(j, i, 1 + k, 1 + l) -\n                                   deriv_phi.get(i, j, 1 + k, 1 + l) +\n                                   deriv_phi.get(k, i, 1 + l, 1 + j) +\n                                   deriv_phi.get(k, j, 1 + l, 1 + i) -\n                                   2.0 * deriv_phi.get(l, k, 1 + i, 1 + j)) +\n                              half_spatial_phi_ijK.get(i, k, l) *\n                                  half_spatial_phi_ijK.get(j, l, k) +\n                              2.0 * half_spatial_phi_Ijj.get(k, i, l) *\n                                  half_spatial_phi_ijK.get(k, j, l) -\n                              2.0 * half_spatial_phi_Ijj.get(k, l, i) *\n                                  half_spatial_phi_Ijj.get(l, k, j);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::ii<DataType, VolumeDim, Frame> spatial_ricci_tensor(\n    const tnsr::iaa<DataType, VolumeDim, Frame>& phi,\n    const tnsr::ijaa<DataType, VolumeDim, Frame>& deriv_phi,\n    const tnsr::II<DataType, VolumeDim, Frame>& inverse_spatial_metric) {\n  tnsr::ii<DataType, VolumeDim, Frame> ricci{};\n  gh::spatial_ricci_tensor(make_not_null(&ricci), phi, deriv_phi,\n                           inverse_spatial_metric);\n  return ricci;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void gh::spatial_ricci_tensor(                                 \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          ricci,                                                          \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,          \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& deriv_phi,   \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_spatial_metric);                                        \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::spatial_ricci_tensor(                                               \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,          \\\n      const tnsr::ijaa<DTYPE(data), DIM(data), FRAME(data)>& deriv_phi,   \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Ricci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Declares function templates to calculate the Ricci tensor\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute spatial Ricci tensor using evolved variables and\n * their first derivatives.\n *\n * \\details Lets write the Christoffel symbols of the first kind as\n * \\f{align}\n * \\Gamma_{kij} = \\frac{1}{2}(\\partial_i \\gamma_{jk} +\n *                             \\partial_j \\gamma_{ik} -\n *                             \\partial_k \\gamma_{ij})\n *              = (\\Phi_{(ij)k} - \\frac{1}{2}\\Phi_{kij})\n * \\f}\n * substituting \\f$\\partial_k \\gamma_{ij}\\rightarrow{}\\Phi_{kij}\\f$ by\n * subtracting out the three-index constraint\n * \\f$C_{kij}=\\partial_{k}\\gamma_{ij}-\\Phi_{kij}\\f$ from every term. We also\n * define contractions \\f$d_k=\\frac{1}{2}\\gamma^{ij}\\Phi_{kij}\\f$ and\n * \\f$b_k=\\frac{1}{2}\\gamma^{ij}\\Phi_{ijk}\\f$. This allows us to rewrite the\n * spatial Ricci tensor as:\n * \\f{align}\n * R_{i j} =& \\partial_k \\Gamma^{k}_{ij} - \\partial_i \\Gamma^{k}_{kj}\n *            + \\Gamma^{k}_{kl}\\Gamma^{l}_{ij}\n *            - \\Gamma^{l}_{ki}\\Gamma^{k}_{lj},\\\\\n *\n *         =& \\gamma^{kl}\\left(\\partial_{k}\\Phi_{(ij)l} -\n *                        \\frac{1}{2}\\partial_{k}\\Phi_{lij}\\right)\n *            - b^{l} (\\Phi_{(ij)l} - \\frac{1}{2}\\Phi_{lij})\\nonumber\\\\\n *          & - \\gamma^{kl}\\left(\\partial_{i}\\Phi_{(kj)l}\n *                          - \\frac{1}{2}\\partial_{i}\\Phi_{lkj}\\right)\n *            - \\Phi_{i}{}^{kl}\\left(\\Phi_{(kj)l}\n *                                   - \\frac{1}{2}\\Phi_{lkj}\n *                                   \\right)\\nonumber\\\\\n *          & + \\gamma^{km}\\left(\\Phi_{(kl)m} - \\frac{1}{2}\\Phi_{mkl}\\right)\n *              \\gamma^{ln}\\left(\\Phi_{(ij)n} - \\frac{1}{2}\\Phi_{nij}\\right)\n *              \\nonumber\\\\\n *\n *          & - \\gamma^{km}\\left(\\Phi_{(il)m} - \\frac{1}{2}\\Phi_{mil}\\right)\n *              \\gamma^{ln}\\left(\\Phi_{(jk)n} - \\frac{1}{2}\\Phi_{njk}\\right).\n * \\f}\n * Gathering all terms with second derivatives:\n * \\f{align}\n * R_{i j} =& \\frac{1}{2} \\gamma^{k l} \\left(\\partial_k\\Phi_{ijl}\n *                                      + \\partial_k\\Phi_{jil}\n *                                      - \\partial_k\\Phi_{lij}\n *                                      + \\partial_i\\Phi_{lkj}\n *                                      - \\partial_i\\Phi_{kjl}\n *                                      - \\partial_i\\Phi_{jkl}\\right)\n *          + \\mathcal{O}(\\Phi), \\nonumber\\\\\n *         =& \\frac{1}{2} \\gamma^{kl} \\left(\\partial_{(j}\\Phi_{lki)}\n *                                     - \\partial_{(j}\\Phi_{i)kl}\n *                                     + \\partial_k \\Phi_{(ij)l}\n *                                     - \\partial_l \\Phi_{kij} \\right)\n *          + \\mathcal{O}(\\Phi),\n * \\f}\n * where we use the four-index constraint\n * \\f$C_{klij}=\\partial_k\\Phi_{lij}-\\partial_l\\Phi_{kij}=0\\f$ to swap the\n * first and second derivatives of the spatial metric, and symmetrize\n * \\f$R_{ij} = R_{(ij)}\\f$. Similarly gathering the remaining terms and\n * using the four-index constraint we get:\n * \\f{align}\n * R_{i j} =& - b^k\\left(\\Phi_{ijk} + \\Phi_{jik} - \\Phi_{kij}\\right)\n *            -\\frac{1}{2} \\Phi_i{}^{kl} \\left(\\Phi_{jkl} + \\Phi_{kjl}\n *                                            - \\Phi_{lkj}\\right)\\nonumber\\\\\n *\n *         &+ \\frac{1}{2} d^k \\left(\\Phi_{ijk} + \\Phi_{jik} - \\Phi_{kij}\\right)\n *         - \\left(\\Phi_{(il)}{}^k - \\frac{1}{2} \\Phi^k{}_{il}\\right)\n *           \\left(\\Phi_{(kj)}{}^l - \\frac{1}{2} \\Phi^l{}_{kj}\\right)\n *         + \\mathcal{O}(\\partial\\Phi) \\\\\n *\n *         =& \\frac{1}{2} \\left(\\Phi_{ijk} + \\Phi_{jik} - \\Phi_{kij}\\right)\n *            (d^k - 2 b^k)\n *         + \\frac{1}{4} \\Phi_{ik}{}^l \\Phi_{jl}{}^k\n *         + \\frac{1}{2} \\left(\\Phi^k{}_{il} \\Phi_{kj}{}^l\n *                             - \\Phi^k{}_{li} \\Phi^l{}_{kj}\\right)\n *         + \\mathcal{O}(\\partial\\Phi).\n * \\f}\n * Gathering everything together, we compute the spatial Ricci tensor as:\n * \\f{eqnarray}\n * R_{i j} &=& \\frac{1}{2} \\gamma^{kl} \\left(\\partial_{(j|}\\Phi_{lk|i)}\n *                                     - \\partial_{(j}\\Phi_{i)kl}\n *                                     + \\partial_k \\Phi_{(ij)l}\n *                                     - \\partial_l \\Phi_{kij}\\right)\\nonumber\\\\\n *         &+& \\frac{1}{2} \\left(\\Phi_{ijk} + \\Phi_{jik} - \\Phi_{kij}\\right)\n *            (d^k - 2 b^k)\n *          + \\frac{1}{4} \\Phi_{ik}{}^l \\Phi_{jl}{}^k\n *          + \\frac{1}{2} \\left(\\Phi^k{}_{il} \\Phi_{kj}{}^l\n *                              - \\Phi^k{}_{li} \\Phi^l{}_{kj}\\right).\n * \\label{eq:rij}\n * \\f}\n * This follows from equations (2.13) - (2.20) of \\cite Kidder2001tz .\n *\n * Note that, in code, the mixed-index variables \\f$\\Phi_{ij}{}^k\\f$ and\n * \\f$\\Phi^i{}_{jk}\\f$ in Eq.(\\f$\\ref{eq:rij}\\f$) are computed with a factor of\n * \\f$1/2\\f$ and so the last 3 terms in the same equation that are quadratic in\n * these terms occur multiplied by a factor of \\f$4\\f$.\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid spatial_ricci_tensor(\n    gsl::not_null<tnsr::ii<DataType, VolumeDim, Frame>*> ricci,\n    const tnsr::iaa<DataType, VolumeDim, Frame>& phi,\n    const tnsr::ijaa<DataType, VolumeDim, Frame>& deriv_phi,\n    const tnsr::II<DataType, VolumeDim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::ii<DataType, VolumeDim, Frame> spatial_ricci_tensor(\n    const tnsr::iaa<DataType, VolumeDim, Frame>& phi,\n    const tnsr::ijaa<DataType, VolumeDim, Frame>& deriv_phi,\n    const tnsr::II<DataType, VolumeDim, Frame>& inverse_spatial_metric);\n/// @}\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SecondTimeDerivOfSpacetimeMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SecondTimeDerivOfSpacetimeMetric.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid second_time_deriv_of_spacetime_metric(\n    gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*> d2t2_spacetime_metric,\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& dt_phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& dt_pi) {\n  if (UNLIKELY(get_size(get<0, 0>(*d2t2_spacetime_metric)) !=\n               get_size(get<0, 0>(pi)))) {\n    *d2t2_spacetime_metric =\n        tnsr::aa<DataType, SpatialDim, Frame>(get_size(get<0, 0>(pi)));\n  }\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = a; b < SpatialDim + 1; ++b) {\n      d2t2_spacetime_metric->get(a, b) = -get(dt_lapse) * pi.get(a, b);\n      d2t2_spacetime_metric->get(a, b) -= get(lapse) * dt_pi.get(a, b);\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        d2t2_spacetime_metric->get(a, b) += dt_shift.get(i) * phi.get(i, a, b);\n        d2t2_spacetime_metric->get(a, b) += shift.get(i) * dt_phi.get(i, a, b);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> second_time_deriv_of_spacetime_metric(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& dt_phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& dt_pi) {\n  tnsr::aa<DataType, SpatialDim, Frame> d2t2_spacetime_metric{};\n  gh::second_time_deriv_of_spacetime_metric(\n      make_not_null(&d2t2_spacetime_metric), lapse, dt_lapse, shift, dt_shift,\n      phi, dt_phi, pi, dt_pi);\n  return d2t2_spacetime_metric;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void gh::second_time_deriv_of_spacetime_metric(                   \\\n      const gsl::not_null<tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          d2t2_spacetime_metric,                                             \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse, \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,             \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,          \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,             \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& dt_phi,          \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,               \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& dt_pi);           \\\n  template tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gh::second_time_deriv_of_spacetime_metric(                                 \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse, \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,             \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,          \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,             \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& dt_phi,          \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,               \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& dt_pi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SecondTimeDerivOfSpacetimeMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the second time derivative of the spacetime metric from the\n * generalized harmonic variables, lapse, shift, and the spacetime unit normal\n * 1-form.\n *\n * \\details Let the generalized harmonic conjugate momentum and spatial\n * derivative variables be \\f$\\Pi_{ab} = -n^c \\partial_c g_{ab} \\f$ and\n * \\f$\\Phi_{iab} = \\partial_i g_{ab} \\f$.\n *\n * Using eq.(35) of \\cite Lindblom2005qh (with \\f$\\gamma_1 = -1\\f$) the first\n * time derivative of the spacetime metric may be expressed in terms of the\n * above variables:\n *\n * \\f[\n * \\partial_0 g_{ab} = - \\alpha \\Pi_{ab} + \\beta^k \\Phi_{kab}\n * \\f]\n *\n * As such, its second time derivative is simply the following:\n *\n * \\f[\n * \\partial^2_0 g_{ab}\n *   = - (\\partial_0 \\alpha) \\Pi_{ab} - \\alpha \\partial_0 \\Pi_{ab}\n *     + (\\partial_0 \\beta^k) \\Phi_{kab} + \\beta^k \\partial_0 \\Phi_{kab}\n * \\f]\n *\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid second_time_deriv_of_spacetime_metric(\n    gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*> d2t2_spacetime_metric,\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& dt_phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& dt_pi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> second_time_deriv_of_spacetime_metric(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& dt_phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& dt_pi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get second time derivative of the spacetime metric\n *        from generalized harmonic and geometric variables\n *\n * \\details See `second_time_deriv_of_spacetime_metric()`. Can be retrieved\n * using `gr::Tags::SpacetimeMetric` wrapped in `Tags::dt<Tags::dt>>`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct SecondTimeDerivOfSpacetimeMetricCompute\n    : ::Tags::dt<\n          ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, SpatialDim, Frame>>>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n                 gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                 ::Tags::dt<gr::Tags::Shift<DataVector, SpatialDim, Frame>>,\n                 Phi<DataVector, SpatialDim, Frame>,\n                 ::Tags::dt<Phi<DataVector, SpatialDim, Frame>>,\n                 Pi<DataVector, SpatialDim, Frame>,\n                 ::Tags::dt<Pi<DataVector, SpatialDim, Frame>>>;\n\n  using base = ::Tags::dt<\n      ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, SpatialDim, Frame>>>;\n  using return_type = typename base::type;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::aa<DataVector, SpatialDim, Frame>*>,\n      const Scalar<DataVector>&, const Scalar<DataVector>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&)>(\n      &second_time_deriv_of_spacetime_metric<DataVector, SpatialDim, Frame>);\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfDetSpatialMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfDetSpatialMetric.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh {\nnamespace {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct D4gBuffer;\n\ntemplate <size_t SpatialDim, typename Frame>\nstruct D4gBuffer<double, SpatialDim, Frame> {\n  explicit D4gBuffer(const size_t /*size*/) {}\n\n  tnsr::ijj<double, SpatialDim, Frame> deriv_of_g{};\n  Scalar<double> det_spatial_metric{};\n};\n\ntemplate <size_t SpatialDim, typename Frame>\nstruct D4gBuffer<DataVector, SpatialDim, Frame> {\n private:\n  // We make one giant allocation so that we don't thrash the heap.\n  Variables<tmpl::list<::Tags::Tempijj<0, SpatialDim, Frame, DataVector>,\n                       ::Tags::TempScalar<1, DataVector>>>\n      buffer_;\n\n public:\n  explicit D4gBuffer(const size_t size)\n      : buffer_(size),\n        deriv_of_g(\n            get<::Tags::Tempijj<0, SpatialDim, Frame, DataVector>>(buffer_)),\n        det_spatial_metric(get<::Tags::TempScalar<1, DataVector>>(buffer_)) {}\n\n  tnsr::ijj<DataVector, SpatialDim, Frame>& deriv_of_g;\n  Scalar<DataVector>& det_spatial_metric;\n};\n}  // namespace\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_deriv_of_det_spatial_metric(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*>\n        d4_det_spatial_metric,\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  if (UNLIKELY(get_size(get<0>(*d4_det_spatial_metric)) !=\n               get_size(get(sqrt_det_spatial_metric)))) {\n    *d4_det_spatial_metric = tnsr::a<DataType, SpatialDim, Frame>(\n        get_size(get(sqrt_det_spatial_metric)));\n  }\n  auto& d4_g = *d4_det_spatial_metric;\n  // Use a Variables to reduce total number of allocations. This is especially\n  // important in a multithreaded environment.\n  D4gBuffer<DataType, SpatialDim, Frame> buffer(\n      get_size(get(sqrt_det_spatial_metric)));\n  deriv_spatial_metric<DataType, SpatialDim, Frame>(\n      make_not_null(&buffer.deriv_of_g), phi);\n  get(buffer.det_spatial_metric) = square(get(sqrt_det_spatial_metric));\n  // \\f$ \\partial_0 g = g g^{jk} \\partial_0 g_{jk}\\f$\n  get<0>(d4_g) = inverse_spatial_metric.get(0, 0) * dt_spatial_metric.get(0, 0);\n  for (size_t j = 0; j < SpatialDim; ++j) {\n    for (size_t k = 0; k < SpatialDim; ++k) {\n      if (LIKELY(j != 0 or k != 0)) {\n        get<0>(d4_g) +=\n            inverse_spatial_metric.get(j, k) * dt_spatial_metric.get(j, k);\n      }\n    }\n  }\n  get<0>(d4_g) *= get(buffer.det_spatial_metric);\n  // \\f$ \\partial_i g = g g^{jk} \\partial_i g_{jk}\\f$\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    d4_g.get(i + 1) =\n        inverse_spatial_metric.get(0, 0) * buffer.deriv_of_g.get(i, 0, 0);\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        if (LIKELY(j != 0 or k != 0)) {\n          d4_g.get(i + 1) +=\n              inverse_spatial_metric.get(j, k) * buffer.deriv_of_g.get(i, j, k);\n        }\n      }\n    }\n    d4_g.get(i + 1) *= get(buffer.det_spatial_metric);\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> spacetime_deriv_of_det_spatial_metric(\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  tnsr::a<DataType, SpatialDim, Frame> d4_det_spatial_metric{};\n  gh::spacetime_deriv_of_det_spatial_metric(\n      make_not_null(&d4_det_spatial_metric), sqrt_det_spatial_metric,\n      inverse_spatial_metric, dt_spatial_metric, phi);\n  return d4_det_spatial_metric;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void gh::spacetime_deriv_of_det_spatial_metric(                    \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*>      \\\n          d4_det_spatial_metric,                                              \\\n      const Scalar<DTYPE(data)>& det_spatial_metric,                          \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric, \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);             \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)>                       \\\n  gh::spacetime_deriv_of_det_spatial_metric(                                  \\\n      const Scalar<DTYPE(data)>& det_spatial_metric,                          \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          inverse_spatial_metric,                                             \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric, \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfDetSpatialMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes spacetime derivatives of the determinant of spatial metric,\n *        using the generalized harmonic variables, spatial metric, and its\n *        time derivative.\n *\n * \\details Using the relation\n * \\f$ \\partial_a \\gamma = \\gamma \\gamma^{jk} \\partial_a \\gamma_{jk} \\f$\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_deriv_of_det_spatial_metric(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> d4_det_spatial_metric,\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> spacetime_deriv_of_det_spatial_metric(\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfNormOfShift.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfNormOfShift.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLowerShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh {\nnamespace {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct D4NormOfShiftBuffer;\n\ntemplate <size_t SpatialDim, typename Frame>\nstruct D4NormOfShiftBuffer<double, SpatialDim, Frame> {\n  explicit D4NormOfShiftBuffer(const size_t /*size*/) {}\n\n  tnsr::i<double, SpatialDim, Frame> lower_shift{};\n  tnsr::iJ<double, SpatialDim, Frame> deriv_shift{};\n  tnsr::i<double, SpatialDim, Frame> dt_lower_shift{};\n  tnsr::I<double, SpatialDim, Frame> dt_shift{};\n};\n\ntemplate <size_t SpatialDim, typename Frame>\nstruct D4NormOfShiftBuffer<DataVector, SpatialDim, Frame> {\n private:\n  // We make one giant allocation so that we don't thrash the heap.\n  Variables<tmpl::list<::Tags::Tempi<0, SpatialDim, Frame, DataVector>,\n                       ::Tags::TempiJ<1, SpatialDim, Frame, DataVector>,\n                       ::Tags::Tempi<2, SpatialDim, Frame, DataVector>,\n                       ::Tags::TempI<3, SpatialDim, Frame, DataVector>>>\n      buffer_;\n\n public:\n  explicit D4NormOfShiftBuffer(const size_t size)\n      : buffer_(size),\n        lower_shift(\n            get<::Tags::Tempi<0, SpatialDim, Frame, DataVector>>(buffer_)),\n        deriv_shift(\n            get<::Tags::TempiJ<1, SpatialDim, Frame, DataVector>>(buffer_)),\n        dt_lower_shift(\n            get<::Tags::Tempi<2, SpatialDim, Frame, DataVector>>(buffer_)),\n        dt_shift(\n            get<::Tags::TempI<3, SpatialDim, Frame, DataVector>>(buffer_)) {}\n\n  tnsr::i<DataVector, SpatialDim, Frame>& lower_shift;\n  tnsr::iJ<DataVector, SpatialDim, Frame>& deriv_shift;\n  tnsr::i<DataVector, SpatialDim, Frame>& dt_lower_shift;\n  tnsr::I<DataVector, SpatialDim, Frame>& dt_shift;\n};\n}  // namespace\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_deriv_of_norm_of_shift(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> d4_norm_of_shift,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  if (UNLIKELY(get_size(get<0>(*d4_norm_of_shift)) != get_size(get(lapse)))) {\n    *d4_norm_of_shift =\n        tnsr::a<DataType, SpatialDim, Frame>(get_size(get(lapse)));\n  }\n  // Use a Variables to reduce total number of allocations. This is especially\n  // important in a multithreaded environment.\n  D4NormOfShiftBuffer<DataType, SpatialDim, Frame> buffer(get_size(get(lapse)));\n\n  raise_or_lower_index(make_not_null(&buffer.lower_shift), shift,\n                       spatial_metric);\n  spatial_deriv_of_shift(make_not_null(&buffer.deriv_shift), lapse,\n                         inverse_spacetime_metric, spacetime_unit_normal, phi);\n  time_deriv_of_lower_shift(make_not_null(&buffer.dt_lower_shift), lapse, shift,\n                            spatial_metric, spacetime_unit_normal, phi, pi);\n  time_deriv_of_shift(make_not_null(&buffer.dt_shift), lapse, shift,\n                      inverse_spatial_metric, spacetime_unit_normal, phi, pi);\n  // first term for component 0\n  get<0>(*d4_norm_of_shift) =\n      shift.get(0) * buffer.dt_lower_shift.get(0) +\n      buffer.lower_shift.get(0) * buffer.dt_shift.get(0);\n  for (size_t i = 1; i < SpatialDim; ++i) {\n    get<0>(*d4_norm_of_shift) +=\n        shift.get(i) * buffer.dt_lower_shift.get(i) +\n        buffer.lower_shift.get(i) * buffer.dt_shift.get(i);\n  }\n  // second term for components 1,2,3\n  for (size_t j = 0; j < SpatialDim; ++j) {\n    d4_norm_of_shift->get(1 + j) =\n        shift.get(0) * phi.get(j, 0, 1) +\n        buffer.lower_shift.get(0) * buffer.deriv_shift.get(j, 0);\n    for (size_t i = 1; i < SpatialDim; ++i) {\n      d4_norm_of_shift->get(1 + j) +=\n          shift.get(i) * phi.get(j, 0, i + 1) +\n          buffer.lower_shift.get(i) * buffer.deriv_shift.get(j, i);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> spacetime_deriv_of_norm_of_shift(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  tnsr::a<DataType, SpatialDim, Frame> d4_norm_of_shift{};\n  gh::spacetime_deriv_of_norm_of_shift(\n      make_not_null(&d4_norm_of_shift), lapse, shift, spatial_metric,\n      inverse_spatial_metric, inverse_spacetime_metric, spacetime_unit_normal,\n      phi, pi);\n  return d4_norm_of_shift;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void gh::spacetime_deriv_of_norm_of_shift(                      \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*>   \\\n          d4_norm_of_shift,                                                \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_spatial_metric,                                          \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_spacetime_metric,                                        \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          spacetime_unit_normal,                                           \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);            \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)>                    \\\n  gh::spacetime_deriv_of_norm_of_shift(                                    \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_spatial_metric,                                          \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          inverse_spacetime_metric,                                        \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          spacetime_unit_normal,                                           \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfNormOfShift.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes spacetime derivatives of the norm of the shift vector.\n *\n * \\details The same is computed as:\n * \\f{align*}\n * \\partial_a (\\beta^i \\beta_i) =\n *     (\\beta_i \\partial_0 \\beta^i + \\beta^i \\partial_0 \\beta_i,\n *      \\beta_i \\partial_j \\beta^i + \\beta^i \\partial_j \\beta_i)\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_deriv_of_norm_of_shift(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> d4_norm_of_shift,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> spacetime_deriv_of_norm_of_shift(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n/// @}\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_derivative_of_spacetime_metric(\n    const gsl::not_null<tnsr::abb<DataType, SpatialDim, Frame>*>\n        da_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = a; b < SpatialDim + 1; ++b) {\n      da_spacetime_metric->get(0, a, b) = -pi.get(a, b) * get(lapse);\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        da_spacetime_metric->get(0, a, b) += shift.get(i) * phi.get(i, a, b);\n        da_spacetime_metric->get(i + 1, a, b) = phi.get(i, a, b);\n      }\n    }\n  }\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void gh::spacetime_derivative_of_spacetime_metric(              \\\n      const gsl::not_null<tnsr::abb<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          dt_spacetime_metric,                                             \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,             \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the spacetime derivative of the spacetime metric,\n * \\f$\\partial_a g_{bc}\\f$\n *\n * \\f{align*}{\n * \\partial_t g_{ab}&=-\\alpha \\Pi_{ab} + \\beta^i \\Phi_{iab} \\\\\n * \\partial_i g_{ab}&=\\Phi_{iab}\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_derivative_of_spacetime_metric(\n    gsl::not_null<tnsr::abb<DataType, SpatialDim, Frame>*> da_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spatial_deriv_of_lapse(\n    const gsl::not_null<tnsr::i<DataType, SpatialDim, Frame>*> deriv_lapse,\n    const Scalar<DataType>& lapse,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  if (UNLIKELY(get_size(get<0>(*deriv_lapse)) != get_size(get(lapse)))) {\n    *deriv_lapse = tnsr::i<DataType, SpatialDim, Frame>(get_size(get(lapse)));\n  }\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    deriv_lapse->get(i) =\n        phi.get(i, 0, 0) * square(get<0>(spacetime_unit_normal));\n    for (size_t a = 0; a < SpatialDim + 1; ++a) {\n      for (size_t b = 0; b < SpatialDim + 1; ++b) {\n        if (LIKELY(a != 0 or b != 0)) {\n          deriv_lapse->get(i) += phi.get(i, a, b) *\n                                 spacetime_unit_normal.get(a) *\n                                 spacetime_unit_normal.get(b);\n        }\n      }\n    }\n    deriv_lapse->get(i) *= -0.5 * get(lapse);\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::i<DataType, SpatialDim, Frame> spatial_deriv_of_lapse(\n    const Scalar<DataType>& lapse,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  tnsr::i<DataType, SpatialDim, Frame> deriv_lapse{};\n  gh::spatial_deriv_of_lapse(make_not_null(&deriv_lapse), lapse,\n                             spacetime_unit_normal, phi);\n  return deriv_lapse;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template void gh::spatial_deriv_of_lapse(                              \\\n      const gsl::not_null<tnsr::i<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          deriv_lapse,                                                   \\\n      const Scalar<DTYPE(data)>& lapse,                                  \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          spacetime_unit_normal,                                         \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);        \\\n  template tnsr::i<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::spatial_deriv_of_lapse(                                            \\\n      const Scalar<DTYPE(data)>& lapse,                                  \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          spacetime_unit_normal,                                         \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes spatial derivatives of lapse (\\f$\\alpha\\f$) from the\n *        generalized harmonic variables and spacetime unit normal 1-form.\n *\n * \\details If the generalized harmonic conjugate momentum and spatial\n * derivative variables are \\f$\\Pi_{ab} = -n^c \\partial_c g_{ab} \\f$ and\n * \\f$\\Phi_{iab} = \\partial_i g_{ab} \\f$, the spatial derivatives of\n * \\f$\\alpha\\f$ can be obtained from:\n *\n * \\f{align*}\n *      n^a n^b \\Phi_{iab} =\n *          -\\frac{1}{2\\alpha} [\\partial_i (-\\alpha^2 + \\beta_j\\beta^j)-\n *                              2 \\beta^j \\partial_i \\beta_j\n *                              + \\beta^j \\beta^k \\partial_i \\gamma_{jk}]\n *                         = -\\frac{2}{\\alpha} \\partial_i \\alpha,\n * \\f}\n *\n * since\n *\n * \\f[\n * \\partial_i (\\beta_j\\beta^j) =\n *     2\\beta^j \\partial_i \\beta_j - \\beta^j \\beta^k \\partial_i \\gamma_{jk}.\n * \\f]\n *\n * \\f[\n *     \\Longrightarrow \\partial_i \\alpha = -(\\alpha/2) n^a \\Phi_{iab} n^b\n * \\f]\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spatial_deriv_of_lapse(\n    gsl::not_null<tnsr::i<DataType, SpatialDim, Frame>*> deriv_lapse,\n    const Scalar<DataType>& lapse,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::i<DataType, SpatialDim, Frame> spatial_deriv_of_lapse(\n    const Scalar<DataType>& lapse,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get spatial derivatives of lapse from the\n * generalized harmonic variables and spacetime unit normal one-form.\n *\n * \\details See `spatial_deriv_of_lapse()`. Can be retrieved using\n * `gr::Tags::Lapse` wrapped in `::Tags::deriv`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct DerivLapseCompute : ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                         tmpl::size_t<SpatialDim>, Frame>,\n                           db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n                 Phi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::i<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame>*>,\n      const Scalar<DataVector>&, const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &spatial_deriv_of_lapse<DataVector, SpatialDim, Frame>);\n\n  using base = ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                             tmpl::size_t<SpatialDim>, Frame>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spatial_deriv_of_shift(\n    const gsl::not_null<tnsr::iJ<DataType, SpatialDim, Frame>*> deriv_shift,\n    const Scalar<DataType>& lapse,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  if (UNLIKELY(get_size(get<0, 0>(*deriv_shift)) != get_size(get(lapse)))) {\n    *deriv_shift = tnsr::iJ<DataType, SpatialDim, Frame>(get_size(get(lapse)));\n  }\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      deriv_shift->get(i, j) =\n          (inverse_spacetime_metric.get(j + 1, 0) +\n           spacetime_unit_normal.get(j + 1) * spacetime_unit_normal.get(0)) *\n          spacetime_unit_normal.get(0) * phi.get(i, 0, 0);\n      for (size_t a = 0; a < SpatialDim + 1; ++a) {\n        for (size_t b = 0; b < SpatialDim + 1; ++b) {\n          if (a != 0 or b != 0) {\n            deriv_shift->get(i, j) += (inverse_spacetime_metric.get(j + 1, a) +\n                                       spacetime_unit_normal.get(j + 1) *\n                                           spacetime_unit_normal.get(a)) *\n                                      spacetime_unit_normal.get(b) *\n                                      phi.get(i, a, b);\n          }\n        }\n      }\n      deriv_shift->get(i, j) *= get(lapse);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::iJ<DataType, SpatialDim, Frame> spatial_deriv_of_shift(\n    const Scalar<DataType>& lapse,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  tnsr::iJ<DataType, SpatialDim, Frame> deriv_shift{};\n  gh::spatial_deriv_of_shift(make_not_null(&deriv_shift), lapse,\n                             inverse_spacetime_metric, spacetime_unit_normal,\n                             phi);\n  return deriv_shift;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void gh::spatial_deriv_of_shift(                               \\\n      const gsl::not_null<tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          deriv_shift,                                                    \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_spacetime_metric,                                       \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          spacetime_unit_normal,                                          \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);         \\\n  template tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::spatial_deriv_of_shift(                                             \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_spacetime_metric,                                       \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          spacetime_unit_normal,                                          \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes spatial derivatives of the shift vector from\n *        the generalized harmonic and geometric variables\n *\n * \\details Spatial derivatives of the shift vector \\f$\\beta^i\\f$ can be derived\n * from the following steps:\n * \\f{align*}\n * \\partial_i \\beta^j\n *  =& \\gamma^{jl} \\gamma_{kl} \\partial_i \\beta^k \\\\\n *  =& \\gamma^{jl} (\\beta^k \\partial_i \\gamma_{lk}\n *         + \\gamma_{kl}\\partial_i \\beta^k - \\beta^k \\partial_i \\gamma_{kl}) \\\\\n *  =& \\gamma^{jl} (\\partial_i \\beta_l - \\beta^k \\partial_i \\gamma_{lk})\n *         (\\because \\gamma^{j0} = 0) \\\\\n *  =& \\gamma^{ja} (\\partial_i g_{a0} - \\beta^k \\partial _i g_{ak}) \\\\\n *  =& \\alpha \\gamma^{ja} n^b \\partial_i g_{ab} \\\\\n *  =& (\\gamma^{ja} - n^j n^a) \\alpha n^b \\Phi_{iab}\n *         - 2 n^j \\partial_i \\alpha \\\\\n *  =& g^{ja} \\alpha n^b \\Phi_{iab} - 2 n^j \\partial_i \\alpha \\\\\n *  =& \\alpha (g^{ja} + n^j n^a) n^b \\Phi_{iab}.\n * \\f}\n * where we used the equation from spatial_deriv_of_lapse() for\n * \\f$\\partial_i \\alpha\\f$.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spatial_deriv_of_shift(\n    gsl::not_null<tnsr::iJ<DataType, SpatialDim, Frame>*> deriv_shift,\n    const Scalar<DataType>& lapse,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::iJ<DataType, SpatialDim, Frame> spatial_deriv_of_shift(\n    const Scalar<DataType>& lapse,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get spatial derivatives of the shift vector from\n *        generalized harmonic and geometric variables\n *\n * \\details See `spatial_deriv_of_shift()`. Can be retrieved using\n * `gr::Tags::Shift` wrapped in `::Tags::deriv`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct DerivShiftCompute\n    : ::Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                    tmpl::size_t<SpatialDim>, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gr::Tags::Lapse<DataVector>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, SpatialDim, Frame>,\n      gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n      Phi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::iJ<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::iJ<DataVector, SpatialDim, Frame>*>,\n      const Scalar<DataVector>&, const tnsr::AA<DataVector, SpatialDim, Frame>&,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&)>(\n      &spatial_deriv_of_shift<DataVector, SpatialDim, Frame>);\n\n  using base = ::Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                             tmpl::size_t<SpatialDim>, Frame>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_deriv_of_lapse(\n    const gsl::not_null<Scalar<DataType>*> dt_lapse,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  if (UNLIKELY(get_size(get(*dt_lapse)) != get_size(get(lapse)))) {\n    *dt_lapse = Scalar<DataType>(get_size(get(lapse)));\n  }\n  get(*dt_lapse) =\n      get(lapse) * get<0, 0>(pi) * square(get<0>(spacetime_unit_normal));\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      // first term\n      if (LIKELY(a != 0 or b != 0)) {\n        get(*dt_lapse) += get(lapse) * pi.get(a, b) *\n                          spacetime_unit_normal.get(a) *\n                          spacetime_unit_normal.get(b);\n      }\n      // second term\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        get(*dt_lapse) -= shift.get(i) * phi.get(i, a, b) *\n                          spacetime_unit_normal.get(a) *\n                          spacetime_unit_normal.get(b);\n      }\n    }\n  }\n  get(*dt_lapse) *= 0.5 * get(lapse);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> time_deriv_of_lapse(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  Scalar<DataType> dt_lapse{};\n  gh::time_deriv_of_lapse(make_not_null(&dt_lapse), lapse, shift,\n                          spacetime_unit_normal, phi, pi);\n  return dt_lapse;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                     \\\n  template void gh::time_deriv_of_lapse(                         \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> dt_lapse,        \\\n      const Scalar<DTYPE(data)>& lapse,                          \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift, \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&        \\\n          spacetime_unit_normal,                                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi, \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);  \\\n  template Scalar<DTYPE(data)> gh::time_deriv_of_lapse(          \\\n      const Scalar<DTYPE(data)>& lapse,                          \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift, \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&        \\\n          spacetime_unit_normal,                                 \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi, \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace Tags\n}  // namespace domain\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes time derivative of lapse (\\f$\\alpha\\f$) from the generalized\n *        harmonic variables, lapse, shift and the spacetime unit normal 1-form.\n *\n * \\details Let the generalized harmonic conjugate momentum and spatial\n * derivative variables be \\f$\\Pi_{ab} = -n^c \\partial_c g_{ab} \\f$ and\n * \\f$\\Phi_{iab} = \\partial_i g_{ab} \\f$, and the operator\n * \\f$D := \\partial_0 - \\beta^k \\partial_k \\f$. The time derivative of\n * \\f$\\alpha\\f$ is then:\n *\n * \\f{align*}\n *  \\frac{1}{2} \\alpha^2 n^a n^b \\Pi_{ab}\n *       - \\frac{1}{2} \\alpha \\beta^i n^a n^b \\Phi_{iab}\n *  =& \\frac{1}{2} \\alpha^2 n^a n^b n^c \\partial_c g_{ab}\n *       - \\frac{1}{2} \\alpha \\beta^i (-(2/\\alpha) \\partial_i \\alpha) \\\\\n *  =& \\frac{1}{2} \\alpha^2 [ \\\\\n *       &-(1/\\alpha^3) D[\\gamma_{jk} \\beta^j \\beta^k - \\alpha^2] \\\\\n *       &- (\\beta^j \\beta^k / \\alpha^3)D[\\gamma_{jk}] \\\\\n *       &+ 2 (\\beta^j / \\alpha^3) D[\\gamma_{jk} \\beta^k] \\\\\n *       &+ (2 / \\alpha^2)(\\beta^i \\partial_i \\alpha)]] \\\\\n *  =& \\frac{1}{2\\alpha} [-D[\\gamma_{jk}\\beta^j\\beta^k - \\alpha^2]\n *       - \\beta^j\\beta^k D[\\gamma_{jk}] + 2\\alpha \\beta^k\\partial_k \\alpha\n *       + 2\\beta^j D[\\gamma_{jk}\\beta^k]] \\\\\n *  =& D[\\alpha] + \\beta^k\\partial_k \\alpha \\\\\n *  =& \\partial_0 \\alpha\n * \\f}\n *\n * where the simplification done for \\f$\\partial_i \\alpha\\f$ is used to\n * substitute for the second term (\\f$\\frac{1}{2} \\alpha \\beta^i n^a n^b\n * \\Phi_{iab}\\f$).\n *\n * Thus,\n *\n * \\f[\n *  \\partial_0 \\alpha =\n *      (\\alpha/2)(\\alpha n^a n^b \\Pi_{ab} - \\beta^i n^a n^b \\Phi_{iab})\n * \\f]\n *\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_deriv_of_lapse(\n    gsl::not_null<Scalar<DataType>*> dt_lapse, const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> time_deriv_of_lapse(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get time derivative of lapse (\\f$\\alpha\\f$) from the\n *        generalized harmonic variables, lapse, shift and the spacetime unit\n *        normal 1-form.\n *\n * \\details See `time_deriv_of_lapse()`. Can be retrieved using\n * `gr::Tags::Lapse` wrapped in `Tags::dt`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct TimeDerivLapseCompute : ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n                               db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                 gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n                 Phi<DataVector, SpatialDim, Frame>,\n                 Pi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = Scalar<DataVector>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*>, const Scalar<DataVector>&,\n      const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&)>(\n      &time_deriv_of_lapse<DataVector, SpatialDim, Frame>);\n\n  using base = ::Tags::dt<gr::Tags::Lapse<DataVector>>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLowerShift.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gh {\nnamespace {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct D0LowerShiftBuffer;\n\ntemplate <size_t SpatialDim, typename Frame>\nstruct D0LowerShiftBuffer<double, SpatialDim, Frame> {\n  explicit D0LowerShiftBuffer(const size_t /*size*/) {}\n\n  tnsr::I<double, SpatialDim, Frame> dt_shift{};\n  tnsr::ii<double, SpatialDim, Frame> dt_spatial_metric{};\n};\n\ntemplate <size_t SpatialDim, typename Frame>\nstruct D0LowerShiftBuffer<DataVector, SpatialDim, Frame> {\n private:\n  // We make one giant allocation so that we don't thrash the heap.\n  Variables<tmpl::list<::Tags::TempI<0, SpatialDim, Frame, DataVector>,\n                       ::Tags::Tempii<1, SpatialDim, Frame, DataVector>>>\n      buffer_;\n\n public:\n  explicit D0LowerShiftBuffer(const size_t size)\n      : buffer_(size),\n        dt_shift(get<::Tags::TempI<0, SpatialDim, Frame, DataVector>>(buffer_)),\n        dt_spatial_metric(\n            get<::Tags::Tempii<1, SpatialDim, Frame, DataVector>>(buffer_)) {}\n\n  tnsr::I<DataVector, SpatialDim, Frame>& dt_shift;\n  tnsr::ii<DataVector, SpatialDim, Frame>& dt_spatial_metric;\n};\n}  // namespace\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_deriv_of_lower_shift(\n    const gsl::not_null<tnsr::i<DataType, SpatialDim, Frame>*> dt_lower_shift,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  if (UNLIKELY(get_size(get<0>(*dt_lower_shift)) != get_size(get(lapse)))) {\n    *dt_lower_shift =\n        tnsr::i<DataType, SpatialDim, Frame>(get_size(get(lapse)));\n  }\n  // Use a Variables to reduce total number of allocations. This is especially\n  // important in a multithreaded environment.\n  D0LowerShiftBuffer<DataType, SpatialDim, Frame> buffer(get_size(get(lapse)));\n  // get \\partial_0 N^j\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  gh::time_deriv_of_shift<DataType, SpatialDim, Frame>(\n      make_not_null(&buffer.dt_shift), lapse, shift, inverse_spatial_metric,\n      spacetime_unit_normal, phi, pi);\n  gh::time_deriv_of_spatial_metric<DataType, SpatialDim, Frame>(\n      make_not_null(&buffer.dt_spatial_metric), lapse, shift, phi, pi);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    dt_lower_shift->get(i) = spatial_metric.get(i, 0) * buffer.dt_shift.get(0) +\n                             shift.get(0) * buffer.dt_spatial_metric.get(i, 0);\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      if (j != 0) {\n        dt_lower_shift->get(i) +=\n            spatial_metric.get(i, j) * buffer.dt_shift.get(j) +\n            shift.get(j) * buffer.dt_spatial_metric.get(i, j);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::i<DataType, SpatialDim, Frame> time_deriv_of_lower_shift(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  tnsr::i<DataType, SpatialDim, Frame> dt_lower_shift{};\n  gh::time_deriv_of_lower_shift(make_not_null(&dt_lower_shift), lapse, shift,\n                                spatial_metric, spacetime_unit_normal, phi, pi);\n  return dt_lower_shift;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template void gh::time_deriv_of_lower_shift(                             \\\n      const gsl::not_null<tnsr::i<DTYPE(data), DIM(data), FRAME(data)>*>   \\\n          dt_lower_shift,                                                  \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          spacetime_unit_normal,                                           \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);            \\\n  template tnsr::i<DTYPE(data), DIM(data), FRAME(data)>                    \\\n  gh::time_deriv_of_lower_shift(                                           \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          spacetime_unit_normal,                                           \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLowerShift.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes time derivative of index lowered shift from generalized\n *        harmonic variables, spatial metric and its time derivative.\n *\n * \\details The time derivative of \\f$ \\beta_i \\f$ is given by:\n * \\f{align*}\n *  \\partial_0 \\beta_i =\n *      \\gamma_{ij} \\partial_0 \\beta^j + \\beta^j \\partial_0 \\gamma_{ij}\n * \\f}\n * where the first term is obtained from `time_deriv_of_shift()`, and the latter\n * is a user input.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_deriv_of_lower_shift(\n    gsl::not_null<tnsr::i<DataType, SpatialDim, Frame>*> dt_lower_shift,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::i<DataType, SpatialDim, Frame> time_deriv_of_lower_shift(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n/// @}\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_deriv_of_shift(\n    const gsl::not_null<tnsr::I<DataType, SpatialDim, Frame>*> dt_shift,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  if (UNLIKELY(get_size(get<0>(*dt_shift)) != get_size(get(lapse)))) {\n    *dt_shift = tnsr::I<DataType, SpatialDim, Frame>(get_size(get(lapse)));\n  }\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    dt_shift->get(i) = -get(lapse) * pi.get(1, 0) *\n                       spacetime_unit_normal.get(0) *\n                       inverse_spatial_metric.get(i, 0);\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t a = 0; a < SpatialDim + 1; ++a) {\n        if (a != 0 or j != 0) {\n          dt_shift->get(i) -= get(lapse) * pi.get(j + 1, a) *\n                              spacetime_unit_normal.get(a) *\n                              inverse_spatial_metric.get(i, j);\n        }\n        for (size_t k = 0; k < SpatialDim; ++k) {\n          dt_shift->get(i) += shift.get(j) * spacetime_unit_normal.get(a) *\n                              phi.get(j, k + 1, a) *\n                              inverse_spatial_metric.get(i, k);\n        }\n      }\n    }\n    dt_shift->get(i) *= get(lapse);\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::I<DataType, SpatialDim, Frame> time_deriv_of_shift(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  tnsr::I<DataType, SpatialDim, Frame> dt_shift{};\n  gh::time_deriv_of_shift(make_not_null(&dt_shift), lapse, shift,\n                          inverse_spatial_metric, spacetime_unit_normal, phi,\n                          pi);\n  return dt_shift;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template void gh::time_deriv_of_shift(                                 \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          dt_shift,                                                      \\\n      const Scalar<DTYPE(data)>& lapse,                                  \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,         \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          inverse_spatial_metric,                                        \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          spacetime_unit_normal,                                         \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,         \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);          \\\n  template tnsr::I<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::time_deriv_of_shift(                                               \\\n      const Scalar<DTYPE(data)>& lapse,                                  \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,         \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          inverse_spatial_metric,                                        \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          spacetime_unit_normal,                                         \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,         \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes time derivative of the shift vector from\n *        the generalized harmonic and geometric variables\n *\n * \\details The time derivative of \\f$ \\beta^i \\f$ can be derived from the\n * following steps:\n * \\f{align*}\n * \\partial_0 \\beta^i\n *  =& \\gamma^{ik} \\partial_0 (\\gamma_{kj} \\beta^j)\n *         - \\beta^j \\gamma^{ik} \\partial_0 \\gamma_{kj} \\\\\n *  =& \\alpha \\gamma^{ik} n^b \\partial_0 g_{kb} \\\\\n *  =& \\alpha \\gamma^{ik} n^b (\\partial_0 - \\beta^j\\partial_j) g_{kb}\n *                  + \\alpha \\gamma^{ik} n^b \\beta^j\\partial_j g_{kb} \\\\\n *  =& -\\alpha^2 n^b\\Pi_{kb} \\gamma^{ik}\n *         + \\alpha \\beta^j n^b\\Phi_{jkb} \\gamma^{ik} \\\\\n *  =& -\\alpha \\gamma^{ik} n^b (\\alpha \\Pi_{kb} - \\beta^j \\Phi_{jkb}) \\\\\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_deriv_of_shift(\n    gsl::not_null<tnsr::I<DataType, SpatialDim, Frame>*> dt_shift,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::I<DataType, SpatialDim, Frame> time_deriv_of_shift(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::A<DataType, SpatialDim, Frame>& spacetime_unit_normal,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get time derivative of the shift vector from\n *        the generalized harmonic and geometric variables\n *\n * \\details See `time_deriv_of_shift()`. Can be retrieved using\n * `gr::Tags::Shift` wrapped in `Tags::dt`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct TimeDerivShiftCompute\n    : ::Tags::dt<gr::Tags::Shift<DataVector, SpatialDim, Frame>>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, Frame>,\n                 gr::Tags::SpacetimeNormalVector<DataVector, SpatialDim, Frame>,\n                 Phi<DataVector, SpatialDim, Frame>,\n                 Pi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::I<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::I<DataVector, SpatialDim, Frame>*>,\n      const Scalar<DataVector>&, const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::II<DataVector, SpatialDim, Frame>&,\n      const tnsr::A<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&)>(\n      &time_deriv_of_shift<DataVector, SpatialDim, Frame>);\n\n  using base = ::Tags::dt<gr::Tags::Shift<DataVector, SpatialDim, Frame>>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_deriv_of_spatial_metric(\n    const gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*>\n        dt_spatial_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  if (UNLIKELY(get_size(get<0, 0>(*dt_spatial_metric)) !=\n               get_size(get(lapse)))) {\n    *dt_spatial_metric =\n        tnsr::ii<DataType, SpatialDim, Frame>(get_size(get(lapse)));\n  }\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      dt_spatial_metric->get(i, j) = -get(lapse) * pi.get(i + 1, j + 1);\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        dt_spatial_metric->get(i, j) += shift.get(k) * phi.get(k, i + 1, j + 1);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> time_deriv_of_spatial_metric(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi) {\n  tnsr::ii<DataType, SpatialDim, Frame> dt_spatial_metric{};\n  gh::time_deriv_of_spatial_metric(make_not_null(&dt_spatial_metric), lapse,\n                                   shift, phi, pi);\n  return dt_spatial_metric;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void gh::time_deriv_of_spatial_metric(                         \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          dt_spatial_metric,                                              \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,          \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,          \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);           \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::time_deriv_of_spatial_metric(                                       \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,          \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi,          \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes time derivative of the spatial metric.\n *\n * \\details Let the generalized harmonic conjugate momentum and spatial\n * derivative variables be \\f$\\Pi_{ab} = -n^c \\partial_c g_{ab} \\f$ and\n * \\f$\\Phi_{iab} = \\partial_i g_{ab} \\f$. As \\f$ n_i \\equiv 0 \\f$. The time\n * derivative of the spatial metric is given by the time derivative of the\n * spatial sector of the spacetime metric, i.e.\n * \\f$ \\partial_0 \\gamma_{ij} = \\partial_0 g_{ij} \\f$.\n *\n * To compute the latter, we use the evolution equation for \\f$ g_{ij} \\f$,\n * c.f. eq.(35) of \\cite Lindblom2005qh (with \\f$\\gamma_1 = -1\\f$):\n *\n * \\f[\n * \\partial_0 g_{ab} = - \\alpha \\Pi_{ab} + \\beta^k \\Phi_{kab}\n * \\f]\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_deriv_of_spatial_metric(\n    gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> dt_spatial_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> time_deriv_of_spatial_metric(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item to get time derivative of the spatial metric from\n *        generalized harmonic and geometric variables\n *\n * \\details See `time_deriv_of_spatial_metric()`. Can be retrieved using\n * `gr::Tags::SpatialMetric` wrapped in `Tags::dt`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct TimeDerivSpatialMetricCompute\n    : ::Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, SpatialDim, Frame>,\n                 Phi<DataVector, SpatialDim, Frame>,\n                 Pi<DataVector, SpatialDim, Frame>>;\n\n  using return_type = tnsr::ii<DataVector, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ii<DataVector, SpatialDim, Frame>*>,\n      const Scalar<DataVector>&, const tnsr::I<DataVector, SpatialDim, Frame>&,\n      const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n      const tnsr::aa<DataVector, SpatialDim, Frame>&)>(\n      &time_deriv_of_spatial_metric<DataVector, SpatialDim, Frame>);\n\n  using base =\n      ::Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim, Frame>>;\n};\n}  // namespace Tags\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_derivative_of_spacetime_metric(\n    const gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*>\n        dt_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = a; b < SpatialDim + 1; ++b) {\n      dt_spacetime_metric->get(a, b) = -pi.get(a, b) * get(lapse);\n      for (size_t i = 0; i < SpatialDim; ++i) {\n        dt_spacetime_metric->get(a, b) += shift.get(i) * phi.get(i, a, b);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> time_derivative_of_spacetime_metric(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi) {\n  tnsr::aa<DataType, SpatialDim, Frame> dt_spacetime_metric{\n      get_size(get(lapse))};\n  time_derivative_of_spacetime_metric(make_not_null(&dt_spacetime_metric),\n                                      lapse, shift, pi, phi);\n  return dt_spacetime_metric;\n}\n}  // namespace gh\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gh::time_derivative_of_spacetime_metric(                                \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,          \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,            \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);         \\\n  template void gh::time_derivative_of_spacetime_metric(                  \\\n      const gsl::not_null<tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          dt_spacetime_metric,                                            \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,          \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& pi,            \\\n      const tnsr::iaa<DTYPE(data), DIM(data), FRAME(data)>& phi);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gh {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the time derivative of the spacetime metric from the\n * generalized harmonic quantities \\f$\\Pi_{a b}\\f$, \\f$\\Phi_{i a b}\\f$, and the\n * lapse \\f$\\alpha\\f$ and shift \\f$\\beta^i\\f$.\n *\n * \\details Computes the derivative as:\n *\n * \\f{align}{\n * \\partial_t g_{a b} = \\beta^i \\Phi_{i a b} - \\alpha \\Pi_{a b}.\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_derivative_of_spacetime_metric(\n    gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*> dt_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> time_derivative_of_spacetime_metric(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataType, SpatialDim, Frame>& phi);\n/// @}\n}  // namespace gh\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t Dim>\nvoid geodesic_acceleration(\n    gsl::not_null<tnsr::I<DataType, Dim>*> acceleration,\n    const tnsr::I<DataType, Dim>& velocity,\n    const tnsr::Abb<DataType, Dim>& christoffel_second_kind) {\n  for (size_t i = 0; i < Dim; ++i) {\n    acceleration->get(i) =\n        velocity.get(i) * christoffel_second_kind.get(0, 0, 0) -\n        christoffel_second_kind.get(i + 1, 0, 0);\n    for (size_t j = 0; j < Dim; ++j) {\n      acceleration->get(i) +=\n          2. * velocity.get(j) *\n          (velocity.get(i) * christoffel_second_kind.get(0, j + 1, 0) -\n           christoffel_second_kind.get(i + 1, j + 1, 0));\n      for (size_t k = 0; k < Dim; ++k) {\n        acceleration->get(i) +=\n            velocity.get(j) * velocity.get(k) *\n            (velocity.get(i) * christoffel_second_kind.get(0, j + 1, k + 1) -\n             christoffel_second_kind.get(i + 1, j + 1, k + 1));\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\ntnsr::I<DataType, Dim> geodesic_acceleration(\n    const tnsr::I<DataType, Dim>& velocity,\n    const tnsr::Abb<DataType, Dim>& christoffel_second_kind) {\n  tnsr::I<DataType, Dim> acceleration{get_size(get<0>(velocity))};\n  geodesic_acceleration(make_not_null(&acceleration), velocity,\n                        christoffel_second_kind);\n  return acceleration;\n}\n\n}  // namespace gr\ntemplate void gr::geodesic_acceleration(\n    const gsl::not_null<tnsr::I<double, 3>*> acceleration,\n    const tnsr::I<double, 3>& velocity,\n    const tnsr::Abb<double, 3>& christoffel_second_kind);\ntemplate tnsr::I<double, 3> gr::geodesic_acceleration(\n    const tnsr::I<double, 3>& velocity,\n    const tnsr::Abb<double, 3>& christoffel_second_kind);\ntemplate void gr::geodesic_acceleration(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> acceleration,\n    const tnsr::I<DataVector, 3>& velocity,\n    const tnsr::Abb<DataVector, 3>& christoffel_second_kind);\ntemplate tnsr::I<DataVector, 3> gr::geodesic_acceleration(\n    const tnsr::I<DataVector, 3>& velocity,\n    const tnsr::Abb<DataVector, 3>& christoffel_second_kind);\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\n/// @{\n\n/*!\n * \\brief Computes the coordinate geodesic acceleration in the inertial frame.\n *\n * \\details The geodesic acceleration in coordinate form is given by\n * \\f{equation}\n *  \\frac{d^2 x^i}{d t^2} = (v^i \\Gamma^0_{00} - \\Gamma^i_{00} )  + 2 v^j (v^i\n * \\Gamma^0_{j0} - \\Gamma^i_{j0} ) + v^j v^k (v^i \\Gamma^0_{jk} - \\Gamma^i_{jk}\n * ), \\f}\n * where \\f$v^i\\f$ is the coordinate velocity, \\f$\\Gamma^\\mu_{\\nu \\rho}\\f$ are\n * the spacetime Christoffel symbols of the second kind, and all latin indices\n * are spatial.\n */\ntemplate <typename DataType, size_t Dim>\nvoid geodesic_acceleration(\n    gsl::not_null<tnsr::I<DataType, Dim>*> acceleration,\n    const tnsr::I<DataType, Dim>& velocity,\n    const tnsr::Abb<DataType, Dim>& christoffel_second_kind);\n\ntemplate <typename DataType, size_t Dim>\ntnsr::I<DataType, Dim> geodesic_acceleration(\n    const tnsr::I<DataType, Dim>& velocity,\n    const tnsr::Abb<DataType, Dim>& christoffel_second_kind);\n/// @}\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeodesicEquation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicEquation.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid geodesic_equation(\n    // Output time derivs\n    const gsl::not_null<tnsr::I<DataType, Dim, Frame>*> dt_x,\n    const gsl::not_null<tnsr::i<DataType, Dim, Frame>*> dt_pi,\n    const gsl::not_null<Scalar<DataType>*> dt_lnp0,\n    // Current state\n    const tnsr::I<DataType, Dim, Frame>& /*x*/,\n    const tnsr::i<DataType, Dim, Frame>& pi, const Scalar<DataType>& /*lnp0*/,\n    // Background spacetime\n    const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, Dim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::iJ<DataType, Dim, Frame>& deriv_shift,\n    const tnsr::II<DataType, Dim, Frame>& inv_spatial_metric,\n    const tnsr::iJJ<DataType, Dim, Frame>& deriv_inv_spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& extrinsic_curvature) {\n  {\n    // Scope where we reuse allocation of dt_x as upper_pi\n    auto& upper_pi = *dt_x;\n    raise_or_lower_index(make_not_null(&upper_pi), pi, inv_spatial_metric);\n    tenex::evaluate(dt_lnp0, -deriv_lapse(ti::i) * upper_pi(ti::I) +\n                                 lapse() * extrinsic_curvature(ti::i, ti::j) *\n                                     upper_pi(ti::I) * upper_pi(ti::J));\n  }\n  // Complete computation of dt_x\n  tenex::update<ti::I>(dt_x, lapse() * (*dt_x)(ti::I)-shift(ti::I));\n  // Compute dt_pi\n  tenex::evaluate<ti::i>(\n      dt_pi, -deriv_lapse(ti::i) - (*dt_lnp0)() * pi(ti::i) +\n                 deriv_shift(ti::i, ti::K) * pi(ti::k) -\n                 0.5 * lapse() * deriv_inv_spatial_metric(ti::i, ti::J, ti::K) *\n                     pi(ti::j) * pi(ti::k));\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void geodesic_equation(                                             \\\n      gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*> dt_x,       \\\n      gsl::not_null<tnsr::i<DTYPE(data), DIM(data), FRAME(data)>*> dt_pi,      \\\n      gsl::not_null<Scalar<DTYPE(data)>*> dt_lnp0,                             \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x,                   \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& pi,                  \\\n      const Scalar<DTYPE(data)>& lnp0, const Scalar<DTYPE(data)>& lapse,       \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& deriv_lapse,         \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,               \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,        \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>& inv_spatial_metric, \\\n      const tnsr::iJJ<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          deriv_inv_spatial_metric,                                            \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          extrinsic_curvature);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double), (3), (Frame::Inertial))\n\n#undef DTYPE\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/GeodesicEquation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\n\n/*!\n * \\brief First-order formulation of the geodesic equation for null geodesics\n * that is suitable for ray tracing.\n *\n * This is the formulation of the geodesic equation that is originally used for\n * ray tracing in \\cite Bohn:2014xxa (Eq. (4)) and \\cite Bohn:2016afc (Eq. (6)).\n *\n * For null rays with position $x^\\mu$, proper time (or affine geodesic\n * parameter) $\\tau$, and four-momentum $p^\\mu = dx^\\mu / d\\tau$, we first\n * define the momentum variable\n * \\begin{equation}\n *   \\Pi_i = \\frac{p_i}{\\alpha p^0} = \\frac{p_i}{\\sqrt{\\gamma^{jk} p_j p_k}}\n *   \\text{.}\n * \\end{equation}\n * Here we work in a 3+1 decomposition of spacetime with time coordinate $t$,\n * lapse $\\alpha$, shift $\\beta^i$, spatial metric $\\gamma_{ij}$, and\n * extrinsic curvature $K_{ij}$.\n * Then, the geodesic equation is given by\n * \\begin{align}\n *   \\frac{d \\Pi_i}{d t} &= -\\partial_i \\alpha + (\\Pi^j \\partial_j \\alpha\n *     -\\alpha K_{jk}\\Pi^j\\Pi^k) \\Pi_i) + \\Pi_k \\partial_i \\beta^k\n *     - \\frac{1}{2} \\alpha \\Pi_j\\Pi_k \\partial_i \\gamma^{jk} \\\\\n *   \\frac{d x^i}{d t} &= \\alpha \\Pi^i - \\beta^i\n *   \\text{.}\n * \\end{align}\n *\n * This function also computes the evolution of the additional redshift variable\n * $\\ln(\\alpha p^0)$ (Eq. (5) in \\cite Bohn:2014xxa) as\n * \\begin{equation}\n *   \\frac{d \\ln(\\alpha p^0)}{d t} = -\\Pi^i \\partial_i \\alpha\n *     + \\alpha K_{ij} \\Pi^i \\Pi^j\n *   \\text{.}\n * \\end{equation}\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid geodesic_equation(\n    // Output time derivs\n    gsl::not_null<tnsr::I<DataType, Dim, Frame>*> dt_x,\n    gsl::not_null<tnsr::i<DataType, Dim, Frame>*> dt_pi,\n    gsl::not_null<Scalar<DataType>*> dt_lnp0,\n    // Current state\n    const tnsr::I<DataType, Dim, Frame>& x,\n    const tnsr::i<DataType, Dim, Frame>& pi, const Scalar<DataType>& lnp0,\n    // Background spacetime\n    const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, Dim, Frame>& deriv_lapse,\n    const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::iJ<DataType, Dim, Frame>& deriv_shift,\n    const tnsr::II<DataType, Dim, Frame>& inv_spatial_metric,\n    const tnsr::iJJ<DataType, Dim, Frame>& deriv_inv_spatial_metric,\n    const tnsr::ii<DataType, Dim, Frame>& extrinsic_curvature);\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/InterfaceNullNormal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/InterfaceNullNormal.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::a<DataType, VolumeDim, Frame> interface_null_normal(\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift, const double sign) {\n  ASSERT((sign == 1.) or (sign == -1.),\n         \"Calculation of interface null normal accepts only +1/-1 to indicate \"\n         \"whether the outgoing/incoming normal is needed.\");\n  tnsr::a<DataType, VolumeDim, Frame> null_one_form(\n      get_size(get<0>(spacetime_normal_one_form)));\n  interface_null_normal(make_not_null(&null_one_form),\n                        spacetime_normal_one_form,\n                        interface_unit_normal_one_form, shift, sign);\n  return null_one_form;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid interface_null_normal(\n    gsl::not_null<tnsr::a<DataType, VolumeDim, Frame>*> null_one_form,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift, const double sign) {\n  ASSERT((sign == 1.) or (sign == -1.),\n         \"Calculation of interface null normal accepts only +1/-1 to indicate \"\n         \"whether the outgoing/incoming normal is needed.\");\n\n  const auto interface_unit_normal_time_component =\n      dot_product(interface_unit_normal_one_form, shift);\n  const auto& interface_unit_normal_time =\n      get(interface_unit_normal_time_component);\n  const double one_by_sqrt_2 = 1. / sqrt(2.);\n  get<0>(*null_one_form) = one_by_sqrt_2 * (get<0>(spacetime_normal_one_form) +\n                                            sign * interface_unit_normal_time);\n  for (size_t a = 1; a < VolumeDim + 1; ++a) {\n    null_one_form->get(a) =\n        one_by_sqrt_2 * (spacetime_normal_one_form.get(a) +\n                         sign * interface_unit_normal_one_form.get(a - 1));\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::A<DataType, VolumeDim, Frame> interface_null_normal(\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector,\n    const double sign) {\n  ASSERT((sign == 1.) or (sign == -1.),\n         \"Calculation of interface null normal accepts only +1/-1 to indicate \"\n         \"whether the outgoing/incoming normal is needed.\");\n  tnsr::A<DataType, VolumeDim, Frame> null_vector(\n      get_size(get<0>(spacetime_normal_vector)));\n  interface_null_normal(make_not_null(&null_vector), spacetime_normal_vector,\n                        interface_unit_normal_vector, sign);\n  return null_vector;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid interface_null_normal(\n    gsl::not_null<tnsr::A<DataType, VolumeDim, Frame>*> null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector,\n    const double sign) {\n  ASSERT((sign == 1.) or (sign == -1.),\n         \"Calculation of interface null normal accepts only +1/-1 to indicate \"\n         \"whether the outgoing/incoming normal is needed.\");\n\n  const double one_by_sqrt_2 = 1. / sqrt(2.);\n  get<0>(*null_vector) = one_by_sqrt_2 * get<0>(spacetime_normal_vector);\n  for (size_t a = 1; a < VolumeDim + 1; ++a) {\n    null_vector->get(a) =\n        one_by_sqrt_2 * (spacetime_normal_vector.get(a) +\n                         sign * interface_unit_normal_vector.get(a - 1));\n  }\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gr::interface_null_normal(                                             \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          spacetime_normal_one_form,                                     \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          interface_unit_normal_one_form,                                \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,         \\\n      const double sign);                                                \\\n  template void gr::interface_null_normal(                               \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          null_one_form,                                                 \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          spacetime_normal_one_form,                                     \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          interface_unit_normal_one_form,                                \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,         \\\n      const double sign);                                                \\\n  template tnsr::A<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gr::interface_null_normal(                                             \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          spacetime_normal_vector,                                       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          interface_unit_normal_vector,                                  \\\n      const double sign);                                                \\\n  template void gr::interface_null_normal(                               \\\n      const gsl::not_null<tnsr::A<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          null_vector,                                                   \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          spacetime_normal_vector,                                       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          interface_unit_normal_vector,                                  \\\n      const double sign);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/InterfaceNullNormal.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute null normal one-form to the boundary of a closed\n * region in a spatial slice of spacetime.\n *\n * \\details Consider an \\f$n-1\\f$-dimensional boundary \\f$S\\f$ of a closed\n * region in an \\f$n\\f$-dimensional spatial hypersurface \\f$\\Sigma\\f$. Let\n * \\f$s^a\\f$ be the unit spacelike vector orthogonal to \\f$S\\f$ in \\f$\\Sigma\\f$,\n * and \\f$n^a\\f$ be the timelike unit vector orthogonal to \\f$\\Sigma\\f$.\n * This function returns the null one-form that is outgoing/incoming on \\f$S\\f$:\n *\n * \\f{align*}\n * k_a = \\frac{1}{\\sqrt{2}}\\left(n_a \\pm s_a\\right).\n * \\f}\n *\n * Here \\f$n_a = g_{ab} n^b\\f$ and \\f$s_a = g_{ab} s^b\\f$ are the spacetime\n * one-forms corresponding to the spacetime vectors \\f$n^a\\f$ and \\f$s^a\\f$.\n *\n * If \\f$t^a=(1,0,0,0)\\f$ is a vector in the time direction, then the unit\n * normal to the spatial slice \\f$n^a\\f$ is determined by the relation\n * \\f$t^a = \\alpha n^a + \\beta^a\\f$ (e.g. Eq. (2.98) of \\cite BaumgarteShapiro),\n * where \\f$\\alpha\\f$ is the lapse, \\f$\\beta^a = (0, \\beta^i)\\f$, and\n * \\f$\\beta^i\\f$ is the shift. Solving for \\f$n^a\\f$ then gives\n * \\f$n^a = \\alpha^{-1}(t^a - \\beta^a)\\f$. Then since \\f$n_a = g_{ab} n^b\\f$,\n * the normal one-form is given by\n * \\f$n_a = g_{ab} n^b = \\alpha^{-1}(g_{at} - g_{ab} \\beta^b)\\f$. This implies\n * \\f$n_i = \\alpha^{-1}(g_{it} - g_{ij} \\beta^j)\\f$, or\n * \\f$ n_i = -\\alpha^{-1}(\\beta_i - \\beta_i) = 0\\f$. Only \\f$n_t\\f$ is nonzero:\n * it is \\f$n_t = \\alpha^{-1}(g_{tt} - g_{tj} \\beta^j)\\f$, or\n * \\f$n_t = \\alpha^{-1}(-\\alpha^2 + \\beta_j \\beta^j - \\beta_j \\beta^j)\\f$, or\n * \\f$n_t = -\\alpha\\f$. Note that \\f$n^a\\f$ is a unit timelike vector, since\n * \\f$n^a n_a = n^t n_t = \\alpha^{-1}(-\\alpha) = -1\\f$.\n *\n * The unit normal to the boundary \\f$s^a\\f$ is orthogonal to \\f$n^a\\f$ and has\n * components \\f$s^a = (0, s^i)\\f$, where \\f$s^i\\f$ is the spatial unit normal\n * vector to the boundary. Since it is a spacelike unit normal vector whose\n * time component vanishes, \\f$s^a s_a = s^i s_i = 1\\f$.\n * Note that \\f$s_a = g_{ab} s^b = g_{aj} s^j\\f$, so\n * \\f$s_i = g_{ij} s^j = \\gamma_{ij} s^j\\f$, where \\f$\\gamma_{ij}\\f$ is the\n * spatial metric, while \\f$s_t = g_{tj} s^j = \\beta_j s^j = \\beta^j s_j\\f$.\n * Thus \\f$n^a\\f$ and \\f$s_a\\f$ are orthogonal, since\n * \\f$n^a s_a = \\alpha^{-1}(t^a s_a - \\beta^a s_a)\\f$, or\n * \\f$n^a s_a = \\alpha^{-1}(\\beta^j s_j - \\beta^j s_j) = 0\\f$.\n *\n * This function computes \\f$s_a\\f$ from the inputs \\f$s_i\\f$ (provided as\n * `interface_unit_normal_one_form`) and \\f$\\beta^i\\f$ (provided as `shift`).\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::a<DataType, VolumeDim, Frame> interface_null_normal(\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift, double sign);\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid interface_null_normal(\n    gsl::not_null<tnsr::a<DataType, VolumeDim, Frame>*> null_one_form,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift, double sign);\n/// @}\n\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute null normal vector to the boundary of a closed\n * region in a spatial slice of spacetime.\n *\n * \\details Consider an \\f$n-1\\f$-dimensional boundary \\f$S\\f$ of a closed\n * region in an \\f$n\\f$-dimensional spatial hypersurface \\f$\\Sigma\\f$. Let\n * \\f$s^a\\f$ be the unit spacelike vector orthogonal to \\f$S\\f$ in \\f$\\Sigma\\f$,\n * and \\f$n^a\\f$ be the timelike unit vector orthogonal to \\f$\\Sigma\\f$.\n * This function returns the null vector that is outgoing/ingoing on \\f$S\\f$:\n *\n * \\f{align*}\n * k^a = \\frac{1}{\\sqrt{2}}\\left(n^a \\pm s^a\\right).\n * \\f}\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::A<DataType, VolumeDim, Frame> interface_null_normal(\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector,\n    double sign);\n\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute null normal vector to the boundary of a closed\n * region in a spatial slice of spacetime.\n *\n * \\details Consider an \\f$n-1\\f$-dimensional boundary \\f$S\\f$ of a closed\n * region in an \\f$n\\f$-dimensional spatial hypersurface \\f$\\Sigma\\f$. Let\n * \\f$s^a\\f$ be the unit spacelike vector orthogonal to \\f$S\\f$ in \\f$\\Sigma\\f$,\n * and \\f$n^a\\f$ be the timelike unit vector orthogonal to \\f$\\Sigma\\f$.\n * This function returns the null vector that is outgoing/ingoing on \\f$S\\f$:\n *\n * \\f{align*}\n * k^a = \\frac{1}{\\sqrt{2}}\\left(n^a \\pm s^a\\right).\n * \\f}\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid interface_null_normal(\n    gsl::not_null<tnsr::A<DataType, VolumeDim, Frame>*> null_vector,\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector,\n    double sign);\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::AA<DataType, Dim, Frame> inverse_spacetime_metric(\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric) {\n  tnsr::AA<DataType, Dim, Frame> inv_spacetime_metric{};\n  inverse_spacetime_metric(make_not_null(&inv_spacetime_metric), lapse, shift,\n                           inverse_spatial_metric);\n  return inv_spacetime_metric;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid inverse_spacetime_metric(\n    const gsl::not_null<tnsr::AA<DataType, Dim, Frame>*>\n        inverse_spacetime_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric) {\n  get<0, 0>(*inverse_spacetime_metric) = -1.0 / (get(lapse) * get(lapse));\n\n  const auto& minus_one_over_lapse_sqrd = get<0, 0>(*inverse_spacetime_metric);\n\n  for (size_t i = 0; i < Dim; ++i) {\n    inverse_spacetime_metric->get(0, i + 1) =\n        -shift.get(i) * minus_one_over_lapse_sqrd;\n  }\n\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      inverse_spacetime_metric->get(i + 1, j + 1) =\n          inverse_spatial_metric.get(i, j) +\n          shift.get(i) * shift.get(j) * minus_one_over_lapse_sqrd;\n    }\n  }\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void gr::inverse_spacetime_metric(                             \\\n      const gsl::not_null<tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          inv_spacetime_metric,                                           \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,          \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_spatial_metric);                                        \\\n  template tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gr::inverse_spacetime_metric(                                           \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,          \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute inverse spacetime metric from inverse spatial metric, lapse\n * and shift\n *\n * \\details The inverse spacetime metric \\f$ g^{ab} \\f$ is calculated as\n * \\f{align}\n *    g^{tt} &= -  1/\\alpha^2 \\\\\n *    g^{ti} &= \\beta^i / \\alpha^2 \\\\\n *    g^{ij} &= \\gamma^{ij} - \\beta^i \\beta^j / \\alpha^2\n * \\f}\n * where \\f$ \\alpha, \\beta^i\\f$ and \\f$ \\gamma^{ij}\\f$ are the lapse, shift and\n * inverse spatial metric respectively\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid inverse_spacetime_metric(\n    gsl::not_null<tnsr::AA<DataType, SpatialDim, Frame>*>\n        inverse_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::AA<DataType, SpatialDim, Frame> inverse_spacetime_metric(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item for inverse spacetime metric \\f$g^{ab}\\f$ in terms of the\n * lapse \\f$\\alpha\\f$, shift \\f$\\beta^i\\f$, and inverse spatial metric\n * \\f$\\gamma^{ij}\\f$.\n *\n * \\details Can be retrieved using `gr::Tags::InverseSpacetimeMetric`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct InverseSpacetimeMetricCompute\n    : InverseSpacetimeMetric<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<Lapse<DataType>, Shift<DataType, SpatialDim, Frame>,\n                 InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::AA<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::AA<DataType, SpatialDim, Frame>*>,\n      const Scalar<DataType>&, const tnsr::I<DataType, SpatialDim, Frame>&,\n      const tnsr::II<DataType, SpatialDim, Frame>&)>(\n      &inverse_spacetime_metric<DataType, SpatialDim, Frame>);\n\n  using base = InverseSpacetimeMetric<DataType, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/KerrHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace gr::Solutions {\n\ntemplate <typename DataType>\nScalar<DataType> kerr_horizon_radius(\n    const std::array<DataType, 2>& theta_phi, const double mass,\n    const std::array<double, 3>& dimensionless_spin) {\n  return kerr_schild_radius_from_boyer_lindquist(\n      mass * (1.0 + sqrt(1.0 - square(magnitude(dimensionless_spin)))),\n      theta_phi, mass, dimensionless_spin);\n}\n\ntemplate Scalar<DataVector> kerr_horizon_radius(\n    const std::array<DataVector, 2>& theta_phi, const double mass,\n    const std::array<double, 3>& dimensionless_spin);\n\ntemplate Scalar<double> kerr_horizon_radius(\n    const std::array<double, 2>& theta_phi, const double mass,\n    const std::array<double, 3>& dimensionless_spin);\n\ntemplate <typename DataType>\nScalar<DataType> kerr_schild_radius_from_boyer_lindquist(\n    const double boyer_lindquist_radius,\n    const std::array<DataType, 2>& theta_phi, const double mass,\n    const std::array<double, 3>& dimensionless_spin) {\n  const double spin_magnitude_squared = square(magnitude(dimensionless_spin));\n  const double mass_squared = square(mass);\n\n  const auto& theta = theta_phi[0];\n  const auto& phi = theta_phi[1];\n  const DataType sin_theta = sin(theta);\n  const DataType cos_theta = cos(theta);\n  const DataType sin_phi = sin(phi);\n  const DataType cos_phi = cos(phi);\n  const DataType spin_dot_unit = dimensionless_spin[0] * sin_theta * cos_phi +\n                                 dimensionless_spin[1] * sin_theta * sin_phi +\n                                 dimensionless_spin[2] * cos_theta;\n\n  return Scalar<DataType>{boyer_lindquist_radius *\n                          sqrt(square(boyer_lindquist_radius) +\n                               mass_squared * spin_magnitude_squared) /\n                          sqrt(square(boyer_lindquist_radius) +\n                               mass_squared * square(spin_dot_unit))};\n}\n\ntemplate Scalar<DataVector> kerr_schild_radius_from_boyer_lindquist(\n    const double boyer_lindquist_radius,\n    const std::array<DataVector, 2>& theta_phi, const double mass,\n    const std::array<double, 3>& dimensionless_spin);\n\ntemplate Scalar<double> kerr_schild_radius_from_boyer_lindquist(\n    const double boyer_lindquist_radius, const std::array<double, 2>& theta_phi,\n    const double mass, const std::array<double, 3>& dimensionless_spin);\n\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\nnamespace gr::Solutions {\n\n/*!\n * \\brief The Kerr-Schild radius corresponding to a Boyer-Lindquist radius.\n *\n * \\details Computes the radius of a surface of constant Boyer-Lindquist radius\n * as a function of angles.  The input argument `theta_phi` is typically the\n * output of the `theta_phi_points()` method of a `ylm::Spherepack` object;\n * i.e., a std::array of two DataVectors containing the values of theta and\n * phi at each point on a Strahlkorper.\n *\n *\n * Derivation:\n *\n * Define spherical coordinates \\f$(r,\\theta,\\phi)\\f$ in the usual way\n * from the Cartesian Kerr-Schild coordinates \\f$(x,y,z)\\f$\n * (i.e. \\f$x = r \\sin\\theta \\cos\\phi\\f$ and so on).\n * Then the relationship between \\f$r\\f$ and the radial\n * Boyer-Lindquist coordinate \\f$r_{BL}\\f$ is\n * \\f[\n * r_{BL}^2 = \\frac{1}{2}(r^2 - a^2)\n *     + \\left(\\frac{1}{4}(r^2-a^2)^2 +\n *             r^2(\\vec{a}\\cdot \\hat{x})^2\\right)^{1/2},\n * \\f]\n * where \\f$\\vec{a}\\f$ is the Kerr spin vector (with units of mass),\n * \\f$\\hat{x}\\f$ means \\f$(x/r,y/r,z/r)\\f$, and the dot product is\n * taken as in flat space.\n *\n * We solve the above equation for \\f$r^2\\f$ as a function of angles,\n * yielding\n * \\f[\n *     r^2 = \\frac{r_{BL}^2 (r_{BL}^2 + a^2)}\n                  {r_{BL}^2+(\\vec{a}\\cdot \\hat{x})^2},\n * \\f]\n * where the angles are encoded in \\f$\\hat x\\f$ and everything else on the\n * right-hand side is constant.\n */\ntemplate <typename DataType>\nScalar<DataType> kerr_schild_radius_from_boyer_lindquist(\n    const double boyer_lindquist_radius,\n    const std::array<DataType, 2>& theta_phi, double mass,\n    const std::array<double, 3>& dimensionless_spin);\n/*!\n * \\brief The Kerr-Schild radius corresponding to a Kerr horizon.\n *\n * \\details `kerr_horizon_radius` evaluates \\f$r\\f$ using the above equation in\n * the documentation for `kerr_schild_radius_from_boyer_lindquist`, and\n * using the standard expression for the Boyer-Lindquist radius of the\n * Kerr horizon:\n * \\f[\n *   r_{BL} = r_+ = M + \\sqrt{M^2-a^2}.\n * \\f]\n *\n * \\note If the spin is nearly extremal, this function has accuracy\n *       limited to roughly \\f$10^{-8}\\f$, because of roundoff amplification\n *       from computing \\f$M + \\sqrt{M^2-a^2}\\f$.\n */\ntemplate <typename DataType>\nScalar<DataType> kerr_horizon_radius(\n    const std::array<DataType, 2>& theta_phi, double mass,\n    const std::array<double, 3>& dimensionless_spin);\n\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/KerrSchildCoords.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/KerrSchildCoords.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <ostream>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\n\nKerrSchildCoords::KerrSchildCoords(const double bh_mass,\n                                   const double bh_dimless_spin)\n    : spin_a_(bh_mass * bh_dimless_spin) {\n  ASSERT(bh_mass > 0,\n         \"The mass must be positive. The value given was \" << bh_mass << \".\");\n  ASSERT(bh_dimless_spin > -1.0 and bh_dimless_spin < 1.0,\n         \"The dimensionless spin must be in the range (-1, 1). The value given \"\n         \"was \"\n             << bh_dimless_spin << \".\");\n}\n\nvoid KerrSchildCoords::pup(PUP::er& p) { p | spin_a_; }\n\ntemplate <typename DataType>\ntnsr::Ij<DataType, 3, Frame::NoFrame> KerrSchildCoords::jacobian_matrix(\n    const DataType& x, const DataType& y, const DataType& z) const {\n  auto result = make_with_value<tnsr::Ij<DataType, 3, Frame::NoFrame>>(x, 0.0);\n\n  const double a_squared = square(spin_a_);\n  DataType prefactor = 0.5 * (square(x) + square(y) + square(z) - a_squared);\n  prefactor += sqrt(square(prefactor) + a_squared * square(z));\n  const DataType r = sqrt(prefactor);\n  prefactor += a_squared;\n  const DataType prefactor_dth = sqrt(prefactor / (square(x) + square(y))) / r;\n  prefactor = 1.0 / prefactor;\n\n  get<0, 0>(result) = prefactor * (x * r + spin_a_ * y);\n  get<0, 1>(result) = z * prefactor_dth * x;\n  get<0, 2>(result) = -y;\n  get<1, 0>(result) = prefactor * (y * r - spin_a_ * x);\n  get<1, 1>(result) = z * prefactor_dth * y;\n  get<1, 2>(result) = x;\n  get<2, 0>(result) = z / r;\n  get<2, 1>(result) = -1.0 / prefactor_dth;\n  // get<2, 2>(result) vanishes identically\n\n  return result;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3, Frame::Inertial>\nKerrSchildCoords::cartesian_from_spherical_ks(\n    const tnsr::I<DataType, 3, Frame::NoFrame>& spatial_vector,\n    const tnsr::I<DataType, 3, Frame::Inertial>& cartesian_coords) const {\n  auto result = make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(\n      cartesian_coords, 0.0);\n\n  for (size_t s = 0; s < get_size(get<0>(cartesian_coords)); ++s) {\n    const double& x = get_element(get<0>(cartesian_coords), s);\n    const double& y = get_element(get<1>(cartesian_coords), s);\n    const double& z = get_element(get<2>(cartesian_coords), s);\n\n    // For a point off the z-axis, transforming with Jacobian is well defined.\n    if (LIKELY(not(equal_within_roundoff(x, 0.0) and\n                   equal_within_roundoff(y, 0.0)))) {\n      const auto jacobian = jacobian_matrix(x, y, z);\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = 0; j < 3; ++j) {\n          get_element(result.get(i), s) +=\n              jacobian.get(i, j) * get_element(spatial_vector.get(j), s);\n        }\n      }\n    } else {\n      // On the z-axis, the Jacobian provides the multivalued transformation\n      //\n      // v^x = (+-)(r cos\\phi + a sin\\phi) v^\\theta\n      // v^y = (+-)(r sin\\phi - a cos\\phi) v^\\theta\n      // v^z = (+-) v^r\n      //\n      // where +/- accounts for points z > 0 or z < 0, respectively.\n      // A well defined transformation is possible if v^\\theta vanishes at the\n      // z-axis, in which case the transformed vector points along the z-axis.\n      ASSERT(equal_within_roundoff(get_element(get<1>(spatial_vector), s), 0.0),\n             \"The input vector must have a vanishing theta component on the \"\n             \"z-axis in order to perform a single-valued transformation. The \"\n             \"vector passed has v^theta = \"\n                 << get_element(get<1>(spatial_vector), s) << \" at z =  \" << z\n                 << \" on the z-axis.\");\n      get_element(get<2>(result), s) =\n          ((z > 0.0) ? get_element(get<0>(spatial_vector), s)\n                     : -1.0 * get_element(get<0>(spatial_vector), s));\n    }\n  }\n  return result;\n}\n\ntemplate <typename DataType>\nScalar<DataType> KerrSchildCoords::r_coord_squared(\n    const tnsr::I<DataType, 3, Frame::Inertial>& cartesian_coords) const {\n  const double a_squared = square(spin_a_);\n  const DataType temp =\n      0.5 * (get(dot_product(cartesian_coords, cartesian_coords)) - a_squared);\n  return Scalar<DataType>{\n      temp + sqrt(square(temp) + a_squared * square(get<2>(cartesian_coords)))};\n}\n\nbool operator==(const KerrSchildCoords& lhs, const KerrSchildCoords& rhs) {\n  return lhs.spin_a_ == rhs.spin_a_;\n}\n\nbool operator!=(const KerrSchildCoords& lhs, const KerrSchildCoords& rhs) {\n  return not(lhs == rhs);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template Scalar<DTYPE(data)> KerrSchildCoords::r_coord_squared(              \\\n      const tnsr::I<DTYPE(data), 3, Frame::Inertial>& cartesian_coords) const; \\\n  template tnsr::I<DTYPE(data), 3, Frame::Inertial>                            \\\n  KerrSchildCoords::cartesian_from_spherical_ks(                               \\\n      const tnsr::I<DTYPE(data), 3, Frame::NoFrame>& spatial_vector,           \\\n      const tnsr::I<DTYPE(data), 3, Frame::Inertial>& cartesian_coords) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/KerrSchildCoords.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace gr {\n/*!\n * \\brief Contains helper functions for transforming tensors in Kerr\n * spacetime to Kerr-Schild coordinates.\n *\n * Consider the Kerr-Schild form of the Kerr metric in Cartesian\n * coordinates \\f$ (t, x, y, z)\\f$,\n *\n * \\f{align*}\n * ds^2 = -dt^2 + dx^2 + dy^2 + dz^2 + \\dfrac{2Mr^3}{r^4 + a^2 z^2}\n * \\left[dt + \\dfrac{r(x\\,dx + y\\,dy)}{r^2 + a^2} +\n * \\dfrac{a(y\\,dx - x\\,dy)}{r^2 + a^2} + \\dfrac{z}{r}dz\\right]^2,\n * \\f}\n *\n * where \\f$r\\f$ is the function defined through\n *\n * \\f{align*}\n * r^2(x, y, z) = \\frac{x^2 + y^2 + z^2 - a^2}{2} +\n * \\sqrt{\\left(\\frac{x^2 + y^2 + z^2 - a^2}{2}\\right)^2 + a^2 z^2}.\n * \\f}\n *\n * Depending on the physical context, the following coordinate transformations\n * are usually adopted:\n *\n * - \\em Spherical \\em coordinates, defined by the usual transformation\n *\n *   \\f{align*}\n *   x &= r_\\text{sph}\\sin\\theta\\cos\\phi,\\\\\n *   y &= r_\\text{sph}\\sin\\theta\\sin\\phi,\\\\\n *   z &= r_\\text{sph}\\cos\\theta.\n *   \\f}\n *\n *   Note that \\f$r_\\text{sph} \\neq r\\f$, so the\n *   horizon of a Kerr black hole is \\em not at constant \\f$r_\\text{sph}\\f$.\n *\n * - \\em Oblate \\em spheroidal \\em coordinates,\n *\n *   \\f{align*}\n *   x &= \\sqrt{r^2 + a^2}\\sin\\vartheta\\cos\\phi,\\\\\n *   y &= \\sqrt{r^2 + a^2}\\sin\\vartheta\\sin\\phi,\\\\\n *   z &= r\\cos\\vartheta.\n *   \\f}\n *\n *   Notice that \\f$r\\f$ is that defined in the Kerr-Schild form of the Kerr\n *   metric, so the horizon is at constant \\f$r\\f$. (As it will be noted below,\n *   \\f$r\\f$ corresponds to the Boyer-Lindquist radial coordinate.) Also, note\n *   that the azimuthal angle \\f$\\phi\\f$ is the same as the corresponding\n *   angle in spherical coordinates, whereas the zenithal angle\n *   \\f$\\vartheta\\f$ differs from the corresponding angle \\f$\\theta\\f$.\n *\n * - \\em Kerr \\em coordinates (sometimes also referred to as\n *   \"Kerr-Schild coordinates\"), defined by\n *\n *   \\f{align*}\n *   x &= (r \\cos\\varphi - a \\sin\\varphi)\\sin\\vartheta =\n *   \\sqrt{r^2 + a^2}\\sin\\vartheta\\cos\\left(\\varphi +\n *   \\tan^{-1}(a/ r)\\right),\\\\\n *   y &= (r \\sin\\varphi + a \\cos\\varphi)\\sin\\vartheta =\n *   \\sqrt{r^2 + a^2}\\sin\\vartheta\\sin\\left(\\varphi +\n *   \\tan^{-1}(a/ r)\\right),\\\\\n *   z &= r \\cos\\vartheta.\n *   \\f}\n *\n *   These are the coordinates used in the gr::KerrSchildCoords class.\n *   Notice that \\f$r\\f$ and \\f$\\vartheta\\f$ are the same as those in\n *   spheroidal coordinates, whereas the azimuthal angle\n *   \\f$\\varphi \\neq \\phi\\f$.\n *\n *   \\note Kerr's original \"advanced Eddington-Finkelstein\" form of his metric\n *   is retrieved by performing the additional transformation \\f$t = u - r\\f$.\n *   Note that Kerr's choice of the letter \\f$u\\f$ for the time coordinate\n *   is not consistent with the convention of denoting the advanced time\n *   \\f$v = t + r\\f$ (in flat space), and the retarded time \\f$u = t - r\\f$\n *   (in flat space). Finally, note that Kerr's original metric has the sign\n *   of \\f$a\\f$ flipped.\n *\n *   The Kerr coordinates have been used in the hydro community to evolve the\n *   GRMHD equations. They are the intermediate step in getting the Kerr metric\n *   in Boyer-Lindquist coordinates. The relation between both is\n *\n *   \\f{align*}\n *   dt_\\text{BL} &=\n *   dt - \\frac{2M r\\,dr}{r^2 - 2Mr + a^2}, \\\\\n *   dr_\\text{BL} &= dr,\\\\\n *   d\\theta_\\text{BL} &= d\\vartheta,\\\\\n *   d\\phi_\\text{BL} &= d\\varphi - \\frac{a\\,dr}{r^2-2Mr + a^2}.\n *   \\f}\n *\n *   The above transformation makes explicit that \\f$r\\f$ is the Boyer-Lindquist\n *   radial coodinate, \\f$ r = r_\\text{BL}\\f$. Likewise,\n *   \\f$\\vartheta = \\theta_\\text{BL}\\f$.\n *\n *   \\note Sometimes (especially in the GRMHD community), the Kerr coordinates\n *   as defined above are referred to as \"spherical Kerr-Schild\" coordinates.\n *   These should not be confused with the true spherical coordinates defined\n *   in this documentation.\n *\n *   The Kerr metric in Kerr coodinates is sometimes used to write analytic\n *   initial data for hydro simulations. In this coordinate system, the\n *   metric takes the form\n *\n *   \\f{align*}\n *   ds^2 &= -(1 - B)\\,dt^2 + (1 + B)\\,dr^2 + \\Sigma\\,d\\vartheta^2 +\n *   \\left(r^2 + a^2 + B a^2\\sin^2\\vartheta\\right)\\sin^2\\vartheta\\,d\\varphi^2\\\\\n *   &\\quad + 2B\\,dt\\,dr - 2aB\\sin^2\\vartheta\\,dt\\,d\\varphi -\n *   2a\\left(1 + B\\right)\\sin^2\\vartheta\\,dr\\,d\\varphi,\n *   \\f}\n *\n *   where \\f$B = 2M r/ \\Sigma\\f$ and \\f$\\Sigma = r^2 + a^2\\cos^2\\vartheta\\f$.\n *   Using the additional relations\n *\n *   \\f{align*}\n *   \\sin\\vartheta &= \\sqrt{\\frac{x^2 + y^2}{r^2 + a^2}},\\quad\n *   \\cos\\vartheta = \\frac{z}{r},\\\\\n *   \\sin\\varphi &= \\frac{y r - x a}{\\sqrt{(x^2 + y^2)(r^2 + a^2)}},\\quad\n *   \\cos\\varphi = \\frac{x r + y a}{\\sqrt{(x^2 + y^2)(r^2 + a^2)}},\n *   \\f}\n *\n *   the Jacobian of the transformation to Cartesian Kerr-Schild coordinates is\n *\n *   \\f{align*}\n *   \\dfrac{\\partial(x, y, z)}{\\partial(r, \\vartheta, \\varphi)} &=\n *   \\left(\\begin{array}{ccc}\n *   \\dfrac{xr + ya}{r^2 + a^2} &\n *   \\dfrac{xz}{r}\\sqrt{\\dfrac{r^2 + a^2}{x^2 + y^2}} & -y\\\\\n *   \\dfrac{yr - ax}{r^2 + a^2} &\n *   \\dfrac{yz}{r}\\sqrt{\\dfrac{r^2 + a^2}{x^2 + y^2}} & x\\\\\n *   \\dfrac{z}{r} & - r\\sqrt{\\dfrac{x^2 + y^2}{r^2 + a^2}} & 0\n *   \\end{array}\\right),\n *   \\f}\n *\n *   which can be used to transform tensors between both coordinate systems.\n */\nclass KerrSchildCoords {\n public:\n  KerrSchildCoords() = default;\n  KerrSchildCoords(const KerrSchildCoords& /*rhs*/) = default;\n  KerrSchildCoords& operator=(const KerrSchildCoords& /*rhs*/) = default;\n  KerrSchildCoords(KerrSchildCoords&& /*rhs*/) = default;\n  KerrSchildCoords& operator=(KerrSchildCoords&& /*rhs*/) = default;\n  ~KerrSchildCoords() = default;\n\n  KerrSchildCoords(double bh_mass, double bh_dimless_spin);\n\n  /// Transforms a spatial vector from Kerr (or \"spherical Kerr-Schild\")\n  /// coordinates to Cartesian Kerr-Schild coordinates. If applied on points\n  /// on the z-axis, the vector to transform must have a vanishing\n  /// \\f$v^\\vartheta\\f$ in order for the transformation to be single-valued.\n  template <typename DataType>\n  tnsr::I<DataType, 3, Frame::Inertial> cartesian_from_spherical_ks(\n      const tnsr::I<DataType, 3, Frame::NoFrame>& spatial_vector,\n      const tnsr::I<DataType, 3, Frame::Inertial>& cartesian_coords) const;\n\n  /// Kerr-Schild \\f$r^2\\f$ in terms of the Cartesian coordinates.\n  template <typename DataType>\n  Scalar<DataType> r_coord_squared(\n      const tnsr::I<DataType, 3, Frame::Inertial>& cartesian_coords) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/);\n\n private:\n  friend bool operator==(const KerrSchildCoords& lhs,\n                         const KerrSchildCoords& rhs);\n\n  // The spatial components of the Jacobian of the transformation from\n  // Kerr coordinates to Cartesian coordinates (x, y, z).\n  template <typename DataType>\n  tnsr::Ij<DataType, 3, Frame::NoFrame> jacobian_matrix(\n      const DataType& x, const DataType& y, const DataType& z) const;\n\n  double spin_a_ = std::numeric_limits<double>::signaling_NaN();\n};\n\nbool operator!=(const KerrSchildCoords& lhs, const KerrSchildCoords& rhs);\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Lapse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> lapse(\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric) {\n  Scalar<DataType> local_lapse{get_size(get<0, 0>(spacetime_metric))};\n  lapse(make_not_null(&local_lapse), shift, spacetime_metric);\n  return local_lapse;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid lapse(const gsl::not_null<Scalar<DataType>*> lapse,\n           const tnsr::I<DataType, SpatialDim, Frame>& shift,\n           const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric) {\n  get(*lapse) = -get<0, 0>(spacetime_metric);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    get(*lapse) += shift.get(i) * spacetime_metric.get(i + 1, 0);\n  }\n  get(*lapse) = sqrt(get(*lapse));\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template Scalar<DTYPE(data)> gr::lapse(                                     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,              \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& spacetime_metric); \\\n  template void gr::lapse(                                                    \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> lapse,                        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,              \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& spacetime_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Lapse.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute lapse from shift and spacetime metric\n *\n * \\details Computes\n * \\f{align}\n *    \\alpha &= \\sqrt{\\beta^i g_{it}-g_{tt}}\n * \\f}\n * where \\f$ \\alpha \\f$, \\f$ \\beta^i\\f$, and \\f$g_{ab}\\f$ are the lapse, shift,\n * and spacetime metric.\n * This can be derived, e.g., from Eqs. 2.121--2.122 of Baumgarte & Shapiro.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> lapse(\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid lapse(gsl::not_null<Scalar<DataType>*> lapse,\n           const tnsr::I<DataType, SpatialDim, Frame>& shift,\n           const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item for lapse \\f$\\alpha\\f$ from the spacetime metric\n * \\f$g_{ab}\\f$ and the shift \\f$\\beta^i\\f$.\n *\n * \\details Can be retrieved using `gr::Tags::Lapse`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct LapseCompute : Lapse<DataType>, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<Shift<DataType, SpatialDim, Frame>,\n                 SpacetimeMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function =\n      static_cast<void (*)(const gsl::not_null<Scalar<DataType>*> lapse,\n                           const tnsr::I<DataType, SpatialDim, Frame>&,\n                           const tnsr::aa<DataType, SpatialDim, Frame>&)>(\n          &lapse<DataType, SpatialDim, Frame>);\n\n  using base = Lapse<DataType>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/ProjectionOperators.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/ProjectionOperators.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::II<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::II<DataType, VolumeDim, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataType, VolumeDim, Frame>& normal_vector) {\n  tnsr::II<DataType, VolumeDim, Frame> projection_tensor(\n      get_size(get<0>(normal_vector)));\n  transverse_projection_operator(make_not_null(&projection_tensor),\n                                 inverse_spatial_metric, normal_vector);\n  return projection_tensor;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    const gsl::not_null<tnsr::II<DataType, VolumeDim, Frame>*>\n        projection_tensor,\n    const tnsr::II<DataType, VolumeDim, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataType, VolumeDim, Frame>& normal_vector) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = i; j < VolumeDim; ++j) {\n      projection_tensor->get(i, j) =\n          inverse_spatial_metric.get(i, j) -\n          normal_vector.get(i) * normal_vector.get(j);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::ii<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::ii<DataType, VolumeDim, Frame>& spatial_metric,\n    const tnsr::i<DataType, VolumeDim, Frame>& normal_one_form) {\n  tnsr::ii<DataType, VolumeDim, Frame> projection_tensor(\n      get_size(get<0>(normal_one_form)));\n  transverse_projection_operator(make_not_null(&projection_tensor),\n                                 spatial_metric, normal_one_form);\n  return projection_tensor;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    const gsl::not_null<tnsr::ii<DataType, VolumeDim, Frame>*>\n        projection_tensor,\n    const tnsr::ii<DataType, VolumeDim, Frame>& spatial_metric,\n    const tnsr::i<DataType, VolumeDim, Frame>& normal_one_form) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = i; j < VolumeDim; ++j) {\n      projection_tensor->get(i, j) =\n          spatial_metric.get(i, j) -\n          normal_one_form.get(i) * normal_one_form.get(j);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::Ij<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::I<DataType, VolumeDim, Frame>& normal_vector,\n    const tnsr::i<DataType, VolumeDim, Frame>& normal_one_form) {\n  tnsr::Ij<DataType, VolumeDim, Frame> projection_tensor(\n      get_size(get<0>(normal_vector)));\n  transverse_projection_operator(make_not_null(&projection_tensor),\n                                 normal_vector, normal_one_form);\n  return projection_tensor;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    const gsl::not_null<tnsr::Ij<DataType, VolumeDim, Frame>*>\n        projection_tensor,\n    const tnsr::I<DataType, VolumeDim, Frame>& normal_vector,\n    const tnsr::i<DataType, VolumeDim, Frame>& normal_one_form) {\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      projection_tensor->get(i, j) =\n          -normal_vector.get(i) * normal_one_form.get(j);\n    }\n    projection_tensor->get(i, i) += 1.;\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::AA<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::AA<DataType, VolumeDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector) {\n  tnsr::AA<DataType, VolumeDim, Frame> projection_tensor(\n      get_size(get<0>(spacetime_normal_vector)));\n  transverse_projection_operator(\n      make_not_null(&projection_tensor), inverse_spacetime_metric,\n      spacetime_normal_vector, interface_unit_normal_vector);\n  return projection_tensor;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    const gsl::not_null<tnsr::AA<DataType, VolumeDim, Frame>*>\n        projection_tensor,\n    const tnsr::AA<DataType, VolumeDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector) {\n  for (size_t a = 0, b = 0; b < VolumeDim + 1; ++b) {\n    projection_tensor->get(a, b) =\n        inverse_spacetime_metric.get(a, b) +\n        spacetime_normal_vector.get(a) * spacetime_normal_vector.get(b);\n  }\n  for (size_t a = 1; a < VolumeDim + 1; ++a) {\n    for (size_t b = a; b < VolumeDim + 1; ++b) {\n      projection_tensor->get(a, b) =\n          inverse_spacetime_metric.get(a, b) +\n          spacetime_normal_vector.get(a) * spacetime_normal_vector.get(b) -\n          interface_unit_normal_vector.get(a - 1) *\n              interface_unit_normal_vector.get(b - 1);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::aa<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::aa<DataType, VolumeDim, Frame>& spacetime_metric,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift) {\n  tnsr::aa<DataType, VolumeDim, Frame> projection_tensor{\n      get_size(get<0>(spacetime_normal_one_form))};\n  transverse_projection_operator(make_not_null(&projection_tensor),\n                                 spacetime_metric, spacetime_normal_one_form,\n                                 interface_unit_normal_one_form, shift);\n  return projection_tensor;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    const gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame>*>\n        projection_tensor,\n    const tnsr::aa<DataType, VolumeDim, Frame>& spacetime_metric,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift) {\n  const auto interface_unit_normal_time_component =\n      dot_product(interface_unit_normal_one_form, shift);\n  const auto& interface_unit_normal_time =\n      get(interface_unit_normal_time_component);\n\n  for (size_t a = 0; a < VolumeDim + 1; ++a) {\n    const auto& interface_unit_normal_a =\n        (a == 0 ? interface_unit_normal_time\n                : interface_unit_normal_one_form.get(a - 1));\n    for (size_t b = a; b < VolumeDim + 1; ++b) {\n      const auto& interface_unit_normal_b =\n          (b == 0 ? interface_unit_normal_time\n                  : interface_unit_normal_one_form.get(b - 1));\n      projection_tensor->get(a, b) =\n          spacetime_metric.get(a, b) +\n          spacetime_normal_one_form.get(a) * spacetime_normal_one_form.get(b) -\n          interface_unit_normal_a * interface_unit_normal_b;\n    }\n  }\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::Ab<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift) {\n  tnsr::Ab<DataType, VolumeDim, Frame> projection_tensor{\n      get_size(get<0>(spacetime_normal_vector))};\n  transverse_projection_operator(\n      make_not_null(&projection_tensor), spacetime_normal_vector,\n      spacetime_normal_one_form, interface_unit_normal_vector,\n      interface_unit_normal_one_form, shift);\n  return projection_tensor;\n}\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    const gsl::not_null<tnsr::Ab<DataType, VolumeDim, Frame>*>\n        projection_tensor,\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift) {\n  const auto interface_unit_normal_time_component =\n      dot_product(interface_unit_normal_one_form, shift);\n  const auto& interface_unit_normal_time =\n      get(interface_unit_normal_time_component);\n  const auto interface_unit_normal_vector_time =\n      make_with_value<DataType>(interface_unit_normal_vector.get(0), 0.0);\n\n  for (size_t a = 0; a < VolumeDim + 1; ++a) {\n    const auto& interface_unit_normal_a =\n        (a == 0 ? interface_unit_normal_vector_time\n                : interface_unit_normal_vector.get(a - 1));\n    for (size_t b = 0; b < VolumeDim + 1; ++b) {\n      const auto& interface_unit_normal_b =\n          (b == 0 ? interface_unit_normal_time\n                  : interface_unit_normal_one_form.get(b - 1));\n      projection_tensor->get(a, b) =\n          spacetime_normal_vector.get(a) * spacetime_normal_one_form.get(b) -\n          interface_unit_normal_a * interface_unit_normal_b;\n      if (a == b) {\n        projection_tensor->get(a, b) += 1.;\n      }\n    }\n  }\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template tnsr::II<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gr::transverse_projection_operator(                                        \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spatial_metric,                                            \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& normal_vector);    \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gr::transverse_projection_operator(                                        \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,   \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& normal_one_form);  \\\n  template tnsr::Ij<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gr::transverse_projection_operator(                                        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& normal_vector,     \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& normal_one_form);  \\\n  template void gr::transverse_projection_operator(                          \\\n      const gsl::not_null<tnsr::II<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          projection_tensor,                                                 \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spatial_metric,                                            \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& normal_vector);    \\\n  template void gr::transverse_projection_operator(                          \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          projection_tensor,                                                 \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,   \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& normal_one_form);  \\\n  template void gr::transverse_projection_operator(                          \\\n      const gsl::not_null<tnsr::Ij<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          projection_tensor,                                                 \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& normal_vector,     \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& normal_one_form);  \\\n  template tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gr::transverse_projection_operator(                                        \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spacetime_metric,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_normal_vector,                                           \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          interface_unit_normal_vector);                                     \\\n  template tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gr::transverse_projection_operator(                                        \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& spacetime_metric, \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_normal_one_form,                                         \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          interface_unit_normal_one_form,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift);            \\\n  template tnsr::Ab<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  gr::transverse_projection_operator(                                        \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_normal_vector,                                           \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_normal_one_form,                                         \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          interface_unit_normal_vector,                                      \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          interface_unit_normal_one_form,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift);            \\\n  template void gr::transverse_projection_operator(                          \\\n      const gsl::not_null<tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          projection_tensor,                                                 \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spacetime_metric,                                          \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_normal_vector,                                           \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          interface_unit_normal_vector);                                     \\\n  template void gr::transverse_projection_operator(                          \\\n      const gsl::not_null<tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          projection_tensor,                                                 \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& spacetime_metric, \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_normal_one_form,                                         \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          interface_unit_normal_one_form,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift);            \\\n  template void gr::transverse_projection_operator(                          \\\n      const gsl::not_null<tnsr::Ab<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          projection_tensor,                                                 \\\n      const tnsr::A<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_normal_vector,                                           \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          spacetime_normal_one_form,                                         \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          interface_unit_normal_vector,                                      \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          interface_unit_normal_one_form,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/ProjectionOperators.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute projection operator onto an interface\n *\n * \\details Returns the operator \\f$P^{ij} = \\gamma^{ij} - n^i n^j\\f$,\n * where \\f$\\gamma^{ij}\\f$ is the inverse spatial metric, and\n * \\f$n^i\\f$ is the normal vector to the interface in question.\n *\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::II<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::II<DataType, VolumeDim, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataType, VolumeDim, Frame>& normal_vector);\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    gsl::not_null<tnsr::II<DataType, VolumeDim, Frame>*> projection_tensor,\n    const tnsr::II<DataType, VolumeDim, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataType, VolumeDim, Frame>& normal_vector);\n/// @}\n\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute projection operator onto an interface\n *\n * \\details Returns the operator \\f$P_{ij} = \\gamma_{ij} - n_i n_j\\f$,\n * where \\f$ \\gamma_{ij}\\f$ is the spatial metric, and \\f$ n_i\\f$ is\n * the normal one-form to the interface in question.\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::ii<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::ii<DataType, VolumeDim, Frame>& spatial_metric,\n    const tnsr::i<DataType, VolumeDim, Frame>& normal_one_form);\n\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute projection operator onto an interface\n *\n * \\details Returns the operator \\f$P_{ij} = \\gamma_{ij} - n_i n_j\\f$,\n * where \\f$ \\gamma_{ij}\\f$ is the spatial metric, and \\f$ n_i\\f$ is\n * the normal one-form to the interface in question.\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    gsl::not_null<tnsr::ii<DataType, VolumeDim, Frame>*> projection_tensor,\n    const tnsr::ii<DataType, VolumeDim, Frame>& spatial_metric,\n    const tnsr::i<DataType, VolumeDim, Frame>& normal_one_form);\n\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute projection operator onto an interface\n *\n * \\details Returns the operator \\f$P^{i}_{j} = \\delta^{i}_{j} - n^i n_j\\f$,\n * where \\f$n^i\\f$ and \\f$n_i\\f$ are the normal vector and normal one-form\n * to the interface in question.\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::Ij<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::I<DataType, VolumeDim, Frame>& normal_vector,\n    const tnsr::i<DataType, VolumeDim, Frame>& normal_one_form);\n\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute projection operator onto an interface\n *\n * \\details Returns the operator \\f$P^{i}_{j} = \\delta^{i}_{j} - n^i n_j\\f$,\n * where \\f$n^i\\f$ and \\f$n_i\\f$ are the normal vector and normal one-form\n * to the interface in question.\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    gsl::not_null<tnsr::Ij<DataType, VolumeDim, Frame>*> projection_tensor,\n    const tnsr::I<DataType, VolumeDim, Frame>& normal_vector,\n    const tnsr::i<DataType, VolumeDim, Frame>& normal_one_form);\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute spacetime projection operator onto an interface\n *\n * \\details Consider a \\f$d-1\\f$-dimensional surface \\f$S\\f$ in a\n * \\f$d\\f$-dimensional spatial hypersurface \\f$\\Sigma\\f$. Let \\f$s_a\\f$\n * be the unit spacelike one-form orthogonal to \\f$S\\f$ in \\f$\\Sigma\\f$,\n * and \\f$n_a\\f$ be the timelike unit vector orthogonal to \\f$\\Sigma\\f$.\n * This function returns the projection operator onto \\f$S\\f$ for\n * \\f$d+1\\f$ dimensional quantities:\n *\n * \\f{align*}\n * P_{ab} = g_{ab} + n_a n_b - s_a s_b = \\gamma_{ab} - s_a s_b.\n * \\f}\n *\n * Here \\f$n_a = g_{ab} n^b\\f$ and \\f$s_a = g_{ab} s^b\\f$ are the spacetime\n * one-forms corresponding to the spacetime vectors \\f$n^a\\f$ and \\f$s^a\\f$.\n *\n * If \\f$t^a=(1,0,0,0)\\f$ is a vector in the time direction, then the unit\n * normal to the spatial slice \\f$n^a\\f$ is determined by the relation\n * \\f$t^a = \\alpha n^a + \\beta^a\\f$ (e.g. Eq. (2.98) of \\cite BaumgarteShapiro),\n * where \\f$\\alpha\\f$ is the lapse, \\f$\\beta^a = (0, \\beta^i)\\f$, and\n * \\f$\\beta^i\\f$ is the shift. Solving for \\f$n^a\\f$ then gives\n * \\f$n^a = \\alpha^{-1}(t^a - \\beta^a)\\f$. Then since \\f$n_a = g_{ab} n^b\\f$,\n * the normal one-form is given by\n * \\f$n_a = g_{ab} n^b = \\alpha^{-1}(g_{at} - g_{ab} \\beta^b)\\f$. This implies\n * \\f$n_i = \\alpha^{-1}(g_{it} - g_{ij} \\beta^j)\\f$, or\n * \\f$ n_i = -\\alpha^{-1}(\\beta_i - \\beta_i) = 0\\f$. Only \\f$n_t\\f$ is nonzero:\n * it is \\f$n_t = \\alpha^{-1}(g_{tt} - g_{tj} \\beta^j)\\f$, or\n * \\f$n_t = \\alpha^{-1}(-\\alpha^2 + \\beta_j \\beta^j - \\beta_j \\beta^j)\\f$, or\n * \\f$n_t = -\\alpha\\f$. Note that \\f$n^a\\f$ is a unit timelike vector, since\n * \\f$n^a n_a = n^t n_t = \\alpha^{-1}(-\\alpha) = -1\\f$.\n *\n * The unit normal to the boundary \\f$s^a\\f$ is orthogonal to \\f$n^a\\f$ and has\n * components \\f$s^a = (0, s^i)\\f$, where \\f$s^i\\f$ is the spatial unit normal\n * vector to the boundary. Since it is a spacelike unit normal vector whose\n * time component vanishes, \\f$s^a s_a = s^i s_i = 1\\f$.\n * Note that \\f$s_a = g_{ab} s^b = g_{aj} s^j\\f$, so\n * \\f$s_i = g_{ij} s^j = \\gamma_{ij} s^j\\f$, where \\f$\\gamma_{ij}\\f$ is the\n * spatial metric, while \\f$s_t = g_{tj} s^j = \\beta_j s^j = \\beta^j s_j\\f$.\n * Thus \\f$n^a\\f$ and \\f$s_a\\f$ are orthogonal, since\n * \\f$n^a s_a = \\alpha^{-1}(t^a s_a - \\beta^a s_a)\\f$, or\n * \\f$n^a s_a = \\alpha^{-1}(\\beta^j s_j - \\beta^j s_j) = 0\\f$.\n *\n * This function computes $s_a$ from the inputs $s_i$ (provided as\n * `interface_unit_normal_one_form`) and \\f$\\beta^i\\f$ (provided as `shift`).\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::aa<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::aa<DataType, VolumeDim, Frame>& spacetime_metric,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift);\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    gsl::not_null<tnsr::aa<DataType, VolumeDim, Frame>*> projection_tensor,\n    const tnsr::aa<DataType, VolumeDim, Frame>& spacetime_metric,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift);\n/// @}\n\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute spacetime projection operator onto an interface\n *\n * \\details Consider a \\f$d-1\\f$-dimensional surface \\f$S\\f$ in a\n * \\f$d\\f$-dimensional spatial hypersurface \\f$\\Sigma\\f$. Let \\f$s^a\\f$\n * be the unit spacelike vector orthogonal to \\f$S\\f$ in \\f$\\Sigma\\f$,\n * and \\f$n^a\\f$ be the timelike unit vector orthogonal to \\f$\\Sigma\\f$.\n * This function returns the projection operator onto \\f$S\\f$ for\n * \\f$d+1\\f$ dimensional quantities:\n *\n * \\f{align*}\n * P^{ab} = g^{ab} + n^a n^b - s^a s^b = \\gamma_{ab} - s_a s_b.\n * \\f}\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::AA<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::AA<DataType, VolumeDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector);\n\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute spacetime projection operator onto an interface\n *\n * \\details Consider a \\f$d-1\\f$-dimensional surface \\f$S\\f$ in a\n * \\f$d\\f$-dimensional spatial hypersurface \\f$\\Sigma\\f$. Let \\f$s^a\\f$\n * be the unit spacelike vector orthogonal to \\f$S\\f$ in \\f$\\Sigma\\f$,\n * and \\f$n^a\\f$ be the timelike unit vector orthogonal to \\f$\\Sigma\\f$.\n * This function returns the projection operator onto \\f$S\\f$ for\n * \\f$d+1\\f$ dimensional quantities:\n *\n * \\f{align*}\n * P^{ab} = g^{ab} + n^a n^b - s^a s^b = \\gamma_{ab} - s_a s_b.\n * \\f}\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    gsl::not_null<tnsr::AA<DataType, VolumeDim, Frame>*> projection_tensor,\n    const tnsr::AA<DataType, VolumeDim, Frame>& inverse_spacetime_metric,\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector);\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute spacetime projection operator onto an interface\n *\n * \\details Consider a \\f$d-1\\f$-dimensional surface \\f$S\\f$ in a\n * \\f$d\\f$-dimensional spatial hypersurface \\f$\\Sigma\\f$. Let \\f$s^a\\f$\n * \\f$(s_a)\\f$ be the unit spacelike vector (one-form) orthogonal\n * to \\f$S\\f$ in \\f$\\Sigma\\f$, and \\f$n^a\\f$ \\f$(n_a)\\f$ be the timelike\n * unit vector (one-form) orthogonal to \\f$\\Sigma\\f$. This function\n * returns the projection operator onto \\f$S\\f$ for \\f$d+1\\f$ dimensional\n * quantities:\n *\n * \\f{align*}\n * P^a_b = \\delta^a_b + n^a n_b - s^a s_b.\n * \\f}\n *\n * Here \\f$n_a = g_{ab} n^b\\f$ and \\f$s_a = g_{ab} s^b\\f$ are the spacetime\n * one-forms corresponding to the spacetime vectors \\f$n^a\\f$ and \\f$s^a\\f$.\n *\n * If \\f$t^a=(1,0,0,0)\\f$ is a vector in the time direction, then the unit\n * normal to the spatial slice \\f$n^a\\f$ is determined by the relation\n * \\f$t^a = \\alpha n^a + \\beta^a\\f$ (e.g. Eq. (2.98) of \\cite BaumgarteShapiro),\n * where \\f$\\alpha\\f$ is the lapse, \\f$\\beta^a = (0, \\beta^i)\\f$, and\n * \\f$\\beta^i\\f$ is the shift. Solving for \\f$n^a\\f$ then gives\n * \\f$n^a = \\alpha^{-1}(t^a - \\beta^a)\\f$. Then since \\f$n_a = g_{ab} n^b\\f$,\n * the normal one-form is given by\n * \\f$n_a = g_{ab} n^b = \\alpha^{-1}(g_{at} - g_{ab} \\beta^b)\\f$. This implies\n * \\f$n_i = \\alpha^{-1}(g_{it} - g_{ij} \\beta^j)\\f$, or\n * \\f$ n_i = -\\alpha^{-1}(\\beta_i - \\beta_i) = 0\\f$. Only \\f$n_t\\f$ is nonzero:\n * it is \\f$n_t = \\alpha^{-1}(g_{tt} - g_{tj} \\beta^j)\\f$, or\n * \\f$n_t = \\alpha^{-1}(-\\alpha^2 + \\beta_j \\beta^j - \\beta_j \\beta^j)\\f$, or\n * \\f$n_t = -\\alpha\\f$. Note that \\f$n^a\\f$ is a unit timelike vector, since\n * \\f$n^a n_a = n^t n_t = \\alpha^{-1}(-\\alpha) = -1\\f$.\n *\n * The unit normal to the boundary \\f$s^a\\f$ is orthogonal to \\f$n^a\\f$ and has\n * components \\f$s^a = (0, s^i)\\f$, where \\f$s^i\\f$ is the spatial unit normal\n * vector to the boundary. Since it is a spacelike unit normal vector whose\n * time component vanishes, \\f$s^a s_a = s^i s_i = 1\\f$.\n * Note that \\f$s_a = g_{ab} s^b = g_{aj} s^j\\f$, so\n * \\f$s_i = g_{ij} s^j = \\gamma_{ij} s^j\\f$, where \\f$\\gamma_{ij}\\f$ is the\n * spatial metric, while \\f$s_t = g_{tj} s^j = \\beta_j s^j = \\beta^j s_j\\f$.\n * Thus \\f$n^a\\f$ and \\f$s_a\\f$ are orthogonal, since\n * \\f$n^a s_a = \\alpha^{-1}(t^a s_a - \\beta^a s_a)\\f$, or\n * \\f$n^a s_a = \\alpha^{-1}(\\beta^j s_j - \\beta^j s_j) = 0\\f$.\n *\n * This function computes \\f$s_a\\f$ from the inputs \\f$s_i\\f$ (provided as\n * `interface_unit_normal_one_form`) and \\f$\\beta^i\\f$ (provided as `shift`),\n * and it computes \\f$s^a\\f$ from \\f$s^i\\f$ (since \\f$s^t=0\\f$).\n */\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\ntnsr::Ab<DataType, VolumeDim, Frame> transverse_projection_operator(\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift);\n\ntemplate <typename DataType, size_t VolumeDim, typename Frame>\nvoid transverse_projection_operator(\n    gsl::not_null<tnsr::Ab<DataType, VolumeDim, Frame>*> projection_tensor,\n    const tnsr::A<DataType, VolumeDim, Frame>& spacetime_normal_vector,\n    const tnsr::a<DataType, VolumeDim, Frame>& spacetime_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& interface_unit_normal_vector,\n    const tnsr::i<DataType, VolumeDim, Frame>& interface_unit_normal_one_form,\n    const tnsr::I<DataType, VolumeDim, Frame>& shift);\n/// @}\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Psi4.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Psi4.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Expressions/Evaluate.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ProjectionOperators.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylPropagating.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename Frame>\nvoid psi_4(const gsl::not_null<Scalar<ComplexDataVector>*> psi_4_result,\n           const tnsr::ii<DataVector, 3, Frame>& spatial_ricci,\n           const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n           const tnsr::ijj<DataVector, 3, Frame>& cov_deriv_extrinsic_curvature,\n           const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n           const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n           const tnsr::I<DataVector, 3, Frame>& inertial_coords) {\n  Variables<tmpl::list<::Tags::TempScalar<0>, ::Tags::TempI<0, 3, Frame>,\n                       ::Tags::TempI<1, 3, Frame>, ::Tags::TempI<2, 3, Frame>,\n                       ::Tags::TempI<3, 3, Frame>, ::Tags::Tempi<0, 3, Frame>,\n                       ::Tags::Tempij<0, 3, Frame>, ::Tags::Tempii<0, 3, Frame>,\n                       ::Tags::Tempii<1, 3, Frame>, ::Tags::TempIj<0, 3, Frame>,\n                       ::Tags::TempII<0, 3, Frame>>>\n      temp_buffer{get<0>(inertial_coords).size()};\n  auto& magnitude_cartesian = get<::Tags::TempScalar<0>>(temp_buffer);\n  magnitude(make_not_null(&magnitude_cartesian), inertial_coords,\n            spatial_metric);\n  auto& r_hat = get<::Tags::TempI<0, 3, Frame>>(temp_buffer);\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t i = 0; i < get(magnitude_cartesian).size(); i++) {\n      if (magnitude_cartesian.get()[i] != 0.0) {\n        r_hat.get(j)[i] =\n            inertial_coords.get(j)[i] / magnitude_cartesian.get()[i];\n      } else {\n        r_hat.get(j)[i] = 0.0;\n      }\n    }\n  }\n  auto& lower_r_hat = get<::Tags::Tempi<0, 3, Frame>>(temp_buffer);\n  tenex::evaluate<ti::i>(make_not_null(&lower_r_hat),\n                         r_hat(ti::J) * spatial_metric(ti::i, ti::j));\n  auto& projection_tensor = get<::Tags::Tempii<0, 3, Frame>>(temp_buffer);\n  transverse_projection_operator(make_not_null(&projection_tensor),\n                                 spatial_metric, lower_r_hat);\n  auto& inverse_projection_tensor =\n      get<::Tags::TempII<0, 3, Frame>>(temp_buffer);\n  transverse_projection_operator(make_not_null(&inverse_projection_tensor),\n                                 inverse_spatial_metric, r_hat);\n  auto& projection_up_lo = get<::Tags::TempIj<0, 3, Frame>>(temp_buffer);\n  tenex::evaluate<ti::K, ti::i>(\n      make_not_null(&projection_up_lo),\n      projection_tensor(ti::i, ti::j) * inverse_spatial_metric(ti::K, ti::J));\n\n  auto& u8_plus = get<::Tags::Tempii<1, 3, Frame>>(temp_buffer);\n  gr::weyl_propagating(\n      make_not_null(&u8_plus), spatial_ricci, extrinsic_curvature,\n      inverse_spatial_metric, cov_deriv_extrinsic_curvature, r_hat,\n      inverse_projection_tensor, projection_tensor, projection_up_lo, 1.0);\n\n  // Gram-Schmidt x_hat, a unit vector that's orthogonal to r_hat\n  auto& x_coord = get<::Tags::TempI<1, 3, Frame>>(temp_buffer);\n  x_coord.get(0) = 1.0;\n  x_coord.get(1) = x_coord.get(2) = 0.0;\n  auto& x_component = get<::Tags::TempScalar<0>>(temp_buffer);\n  dot_product(make_not_null(&x_component), x_coord, r_hat, spatial_metric);\n  auto& x_hat = get<::Tags::TempI<2, 3, Frame>>(temp_buffer);\n  tenex::evaluate<ti::I>(make_not_null(&x_hat),\n                         x_coord(ti::I) - (x_component() * r_hat(ti::I)));\n  auto& magnitude_x = get<::Tags::TempScalar<0>>(temp_buffer);\n  magnitude(make_not_null(&magnitude_x), x_hat, spatial_metric);\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t i = 0; i < get(magnitude_x).size(); i++) {\n      if (magnitude_x.get()[i] != 0.0) {\n        x_hat.get(j)[i] /= magnitude_x.get()[i];\n      } else {\n        x_hat.get(j)[i] = 0.0;\n      }\n    }\n  }\n\n  Variables<tmpl::list<::Tags::TempI<0, 3, Frame, ComplexDataVector>,\n                       ::Tags::TempI<1, 3, Frame, ComplexDataVector>>>\n      y_hat_buffer{get<0>(inertial_coords).size()};\n\n  // Grad-Schmidt y_hat, a unit vector orthogonal to r_hat and x_hat\n  auto& y_coord = get<::Tags::TempI<1, 3, Frame>>(temp_buffer);\n  y_coord.get(1) = 1.0;\n  y_coord.get(0) = y_coord.get(2) = 0.0;\n  auto& y_component = get<::Tags::TempScalar<0>>(temp_buffer);\n  dot_product(make_not_null(&y_component), y_coord, r_hat, spatial_metric);\n  auto& y_hat_not_complex = get<::Tags::TempI<3, 3, Frame>>(temp_buffer);\n  tenex::evaluate<ti::I>(make_not_null(&y_hat_not_complex),\n                         y_coord(ti::I) - (y_component() * r_hat(ti::I)));\n  dot_product(make_not_null(&y_component), y_coord, x_hat, spatial_metric);\n  tenex::evaluate<ti::I>(\n      make_not_null(&y_hat_not_complex),\n      y_hat_not_complex(ti::I) - (y_component() * x_hat(ti::I)));\n  auto& magnitude_y = get<::Tags::TempScalar<0>>(temp_buffer);\n  magnitude(make_not_null(&magnitude_y), y_hat_not_complex, spatial_metric);\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t i = 0; i < get(magnitude_y).size(); i++) {\n      if (magnitude_y.get()[i] != 0.0) {\n        y_hat_not_complex.get(j)[i] /= magnitude_y.get()[i];\n      } else {\n        y_hat_not_complex.get(j)[i] = 0.0;\n      }\n    }\n  }\n  const std::complex<double> imag = std::complex<double>(0.0, 1.0);\n  auto& y_hat =\n      get<::Tags::TempI<0, 3, Frame, ComplexDataVector>>(y_hat_buffer);\n  tenex::evaluate<ti::I>(make_not_null(&y_hat),\n                         imag * y_hat_not_complex(ti::I));\n\n  auto& m_bar =\n      get<::Tags::TempI<1, 3, Frame, ComplexDataVector>>(y_hat_buffer);\n  tenex::evaluate<ti::I>(make_not_null(&m_bar), x_hat(ti::I) - y_hat(ti::I));\n\n  tenex::evaluate(psi_4_result,\n                  -0.5 * u8_plus(ti::i, ti::j) * m_bar(ti::I) * m_bar(ti::J));\n}\n\ntemplate <typename Frame>\nScalar<ComplexDataVector> psi_4(\n    const tnsr::ii<DataVector, 3, Frame>& spatial_ricci,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::ijj<DataVector, 3, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame>& inertial_coords) {\n  auto psi_4_result = make_with_value<Scalar<ComplexDataVector>>(\n      get<0, 0>(inverse_spatial_metric),\n      std::numeric_limits<double>::signaling_NaN());\n  psi_4(make_not_null(&psi_4_result), spatial_ricci, extrinsic_curvature,\n        cov_deriv_extrinsic_curvature, spatial_metric, inverse_spatial_metric,\n        inertial_coords);\n  return psi_4_result;\n}\n}  // namespace gr\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template Scalar<ComplexDataVector> gr::psi_4(                           \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_ricci,          \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature,    \\\n      const tnsr::ijj<DataVector, 3, FRAME(data)>&                        \\\n          cov_deriv_extrinsic_curvature,                                  \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_metric,         \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& inverse_spatial_metric, \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& inertial_coords);        \\\n  template void gr::psi_4(                                                \\\n      const gsl::not_null<Scalar<ComplexDataVector>*> psi_4_result,       \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_ricci,          \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature,    \\\n      const tnsr::ijj<DataVector, 3, FRAME(data)>&                        \\\n          cov_deriv_extrinsic_curvature,                                  \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_metric,         \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& inverse_spatial_metric, \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& inertial_coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Frame::Grid, Frame::Inertial))\n\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Psi4.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes Newman Penrose quantity \\f$\\Psi_4\\f$ using the characteristic\n * field U\\f$^{8+}\\f$ and complex vector \\f$\\bar{m}^i\\f$.\n *\n * \\details Computes \\f$\\Psi_4\\f$ as: \\f$\\Psi_4 =\n * U^{8+}_{ij}\\bar{m}^i\\bar{m}^j\\f$ with the characteristic field\n * \\f$U^{8+} = (P^{(a}_i P^{b)}_j - \\frac{1}{2}P_{ij}P^{ab})\n * (E_{ab} - \\epsilon_a^{cd}n_dB_{cb}\\f$)\n * and \\f$\\bar{m}^i\\f$ = \\f$\\frac{(x^i + iy^i)}{\\sqrt{2}}\\f$. \\f$x^i\\f$ and\n * \\f$y^i\\f$ are normalized unit vectors in the frame Frame.\n *\n */\ntemplate <typename Frame>\nvoid psi_4(gsl::not_null<Scalar<ComplexDataVector>*> psi_4_result,\n           const tnsr::ii<DataVector, 3, Frame>& spatial_ricci,\n           const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n           const tnsr::ijj<DataVector, 3, Frame>& cov_deriv_extrinsic_curvature,\n           const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n           const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n           const tnsr::I<DataVector, 3, Frame>& inertial_coords);\n\ntemplate <typename Frame>\nScalar<ComplexDataVector> psi_4(\n    const tnsr::ii<DataVector, 3, Frame>& spatial_ricci,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::ijj<DataVector, 3, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame>& inertial_coords);\n/// @}\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Psi4Real.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Psi4Real.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\n\ntemplate <typename Frame>\nvoid psi_4_real(\n    const gsl::not_null<Scalar<DataVector>*> psi_4_real_result,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_ricci,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::ijj<DataVector, 3, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame>& inertial_coords) {\n  get(*psi_4_real_result) = real(get(\n      psi_4(spatial_ricci, extrinsic_curvature, cov_deriv_extrinsic_curvature,\n            spatial_metric, inverse_spatial_metric, inertial_coords)));\n}\n\ntemplate <typename Frame>\nScalar<DataVector> psi_4_real(\n    const tnsr::ii<DataVector, 3, Frame>& spatial_ricci,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::ijj<DataVector, 3, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame>& inertial_coords) {\n  auto psi_4_real_result = make_with_value<Scalar<DataVector>>(\n      get<0, 0>(inverse_spatial_metric),\n      std::numeric_limits<double>::signaling_NaN());\n  psi_4_real(make_not_null(&psi_4_real_result), spatial_ricci,\n             extrinsic_curvature, cov_deriv_extrinsic_curvature, spatial_metric,\n             inverse_spatial_metric, inertial_coords);\n  return psi_4_real_result;\n}\n}  // namespace gr\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template Scalar<DataVector> gr::psi_4_real(                             \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_ricci,          \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature,    \\\n      const tnsr::ijj<DataVector, 3, FRAME(data)>&                        \\\n          cov_deriv_extrinsic_curvature,                                  \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_metric,         \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& inverse_spatial_metric, \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& inertial_coords);        \\\n  template void gr::psi_4_real(                                           \\\n      const gsl::not_null<Scalar<DataVector>*> psi_4_real_result,         \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_ricci,          \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature,    \\\n      const tnsr::ijj<DataVector, 3, FRAME(data)>&                        \\\n          cov_deriv_extrinsic_curvature,                                  \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_metric,         \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& inverse_spatial_metric, \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& inertial_coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Frame::Grid, Frame::Inertial))\n\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Psi4Real.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\n/// \\cond\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the real part of the Newman Penrose quantity \\f$\\Psi_4\\f$\n * using  \\f$\\Psi_4[Real] = -0.5*U^{8+}_{ij}*(x^ix^j - y^iy^j)\\f$.\n */\ntemplate <typename Frame>\nvoid psi_4_real(\n    gsl::not_null<Scalar<DataVector>*> psi_4_real_result,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_ricci,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::ijj<DataVector, 3, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame>& inertial_coords);\n\ntemplate <typename Frame>\nScalar<DataVector> psi_4_real(\n    const tnsr::ii<DataVector, 3, Frame>& spatial_ricci,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::ijj<DataVector, 3, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n    const tnsr::I<DataVector, 3, Frame>& inertial_coords);\n\nnamespace Tags {\n/// Computes the real part of the Newman Penrose quantity \\f$\\Psi_4\\f$ using\n/// \\f$\\Psi_4[Real] = -0.5*U^{8+}_{ij}*(x^ix^j - y^iy^j)\\f$.\n///\n/// Can be retrieved using `gr::Tags::Psi4Real`\ntemplate <typename Frame>\nstruct Psi4RealCompute : Psi4Real<DataVector>, db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gr::Tags::SpatialRicci<DataVector, 3, Frame>,\n      gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame>,\n      ::Tags::deriv<gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame>,\n                    tmpl::size_t<3>, Frame>,\n      gr::Tags::SpatialMetric<DataVector, 3, Frame>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3, Frame>,\n      domain::Tags::Coordinates<3, Frame>>;\n\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*>, const tnsr::ii<DataVector, 3, Frame>&,\n      const tnsr::ii<DataVector, 3, Frame>&,\n      const tnsr::ijj<DataVector, 3, Frame>&,\n      const tnsr::ii<DataVector, 3, Frame>&,\n      const tnsr::II<DataVector, 3, Frame>&,\n      const tnsr::I<DataVector, 3, Frame>&)>(&psi_4_real<Frame>);\n  using base = Psi4Real<DataVector>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InterfaceNullNormal.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ProjectionOperators.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4Real.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TortoiseCoordinates.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylMagnetic.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylPropagating.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylTypeD1.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace GeneralRelativity::py_bindings {\n\nnamespace {\ntemplate <size_t Dim, IndexType Index>\nvoid bind_spacetime_impl(py::module& m) {  // NOLINT\n  m.def(\"christoffel_first_kind\",\n        static_cast<tnsr::abb<DataVector, Dim, Frame::Inertial, Index> (*)(\n            const tnsr::abb<DataVector, Dim, Frame::Inertial, Index>&)>(\n            &::gr::christoffel_first_kind),\n        py::arg(\"d_metric\"));\n\n  m.def(\"christoffel_second_kind\",\n        static_cast<tnsr::Abb<DataVector, Dim, Frame::Inertial, Index> (*)(\n            const tnsr::abb<DataVector, Dim, Frame::Inertial, Index>&,\n            const tnsr::AA<DataVector, Dim, Frame::Inertial, Index>&)>(\n            &::gr::christoffel_second_kind),\n        py::arg(\"d_metric\"), py::arg(\"inverse_metric\"));\n\n  m.def(\"ricci_scalar\",\n        static_cast<Scalar<DataVector> (*)(\n            const tnsr::aa<DataVector, Dim, Frame::Inertial, Index>&,\n            const tnsr::AA<DataVector, Dim, Frame::Inertial, Index>&)>(\n            &::gr::ricci_scalar),\n        py::arg(\"ricci_tensor\"), py::arg(\"inverse_metric\"));\n\n  m.def(\"ricci_tensor\",\n        static_cast<tnsr::aa<DataVector, Dim, Frame::Inertial, Index> (*)(\n            const tnsr::Abb<DataVector, Dim, Frame::Inertial, Index>&,\n            const tnsr::aBcc<DataVector, Dim, Frame::Inertial, Index>&)>(\n            &::gr::ricci_tensor),\n        py::arg(\"christoffel_2nd_kind\"), py::arg(\"d_christoffel_2nd_kind\"));\n}\n\ntemplate <size_t Dim>\nvoid bind_impl(py::module& m) {  // NOLINT\n  m.def(\n      \"deriv_inverse_spatial_metric\",\n      static_cast<tnsr::iJJ<DataVector, Dim> (*)(\n          const tnsr::II<DataVector, Dim>&, const tnsr::ijj<DataVector, Dim>&)>(\n          &::gr::deriv_inverse_spatial_metric),\n      py::arg(\"inverse_spatial_metric\"), py::arg(\"d_spatial_metric\"));\n\n  m.def(\n      \"extrinsic_curvature\",\n      py::overload_cast<\n          const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n          const tnsr::iJ<DataVector, Dim>&, const tnsr::ii<DataVector, Dim>&,\n          const tnsr::ii<DataVector, Dim>&, const tnsr::ijj<DataVector, Dim>&>(\n          &gr::extrinsic_curvature<DataVector, Dim, Frame::Inertial>),\n      py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"deriv_shift\"),\n      py::arg(\"spatial_metric\"), py::arg(\"dt_spatial_metric\"),\n      py::arg(\"deriv_spatial_metric\"));\n\n  m.def(\"interface_null_normal\",\n        static_cast<tnsr::a<DataVector, Dim> (*)(\n            const tnsr::a<DataVector, Dim>&, const tnsr::i<DataVector, Dim>&,\n            const tnsr::I<DataVector, Dim>&, const double)>(\n            &::gr::interface_null_normal),\n        py::arg(\"spacetime_normal_one_form\"),\n        py::arg(\"interface_unit_normal_one_form\"), py::arg(\"shift\"),\n        py::arg(\"sign\"));\n\n  m.def(\"inverse_spacetime_metric\",\n        static_cast<tnsr::AA<DataVector, Dim> (*)(\n            const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n            const tnsr::II<DataVector, Dim>&)>(&::gr::inverse_spacetime_metric),\n        py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"inverse_spatial_metric\"));\n\n  m.def(\"lapse\",\n        static_cast<Scalar<DataVector> (*)(const tnsr::I<DataVector, Dim>&,\n                                           const tnsr::aa<DataVector, Dim>&)>(\n            &::gr::lapse),\n        py::arg(\"shift\"), py::arg(\"spacetime_metric\"));\n\n  m.def(\"derivatives_of_spacetime_metric\",\n        static_cast<tnsr::abb<DataVector, Dim> (*)(\n            const Scalar<DataVector>&, const Scalar<DataVector>&,\n            const tnsr::i<DataVector, Dim>&, const tnsr::I<DataVector, Dim>&,\n            const tnsr::I<DataVector, Dim>&, const tnsr::iJ<DataVector, Dim>&,\n            const tnsr::ii<DataVector, Dim>&, const tnsr::ii<DataVector, Dim>&,\n            const tnsr::ijj<DataVector, Dim>&)>(\n            &::gr::derivatives_of_spacetime_metric),\n        py::arg(\"lapse\"), py::arg(\"dt_lapse\"), py::arg(\"deriv_lapse\"),\n        py::arg(\"shift\"), py::arg(\"dt_shift\"), py::arg(\"deriv_shift\"),\n        py::arg(\"spatial_metric\"), py::arg(\"dt_spatial_metric\"),\n        py::arg(\"deriv_spatial_metric\"));\n\n  m.def(\"spacetime_metric\",\n        static_cast<tnsr::aa<DataVector, Dim> (*)(\n            const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n            const tnsr::ii<DataVector, Dim>&)>(&::gr::spacetime_metric),\n        py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"spatial_metric\"));\n\n  m.def(\"spatial_metric\",\n        static_cast<tnsr::ii<DataVector, Dim> (*)(\n            const tnsr::aa<DataVector, Dim>&)>(&::gr::spatial_metric),\n        py::arg(\"spacetime_metric\"));\n\n  m.def(\n      \"time_derivative_of_spacetime_metric\",\n      static_cast<tnsr::aa<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const Scalar<DataVector>&,\n          const tnsr::I<DataVector, Dim>&, const tnsr::I<DataVector, Dim>&,\n          const tnsr::ii<DataVector, Dim>&, const tnsr::ii<DataVector, Dim>&)>(\n          &::gr::time_derivative_of_spacetime_metric),\n      py::arg(\"lapse\"), py::arg(\"dt_lapse\"), py::arg(\"shift\"),\n      py::arg(\"dt_shift\"), py::arg(\"spatial_metric\"),\n      py::arg(\"dt_spatial_metric\"));\n\n  m.def(\n      \"time_derivative_of_spatial_metric\",\n      static_cast<tnsr::ii<DataVector, Dim> (*)(\n          const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&,\n          const tnsr::iJ<DataVector, Dim>&, const tnsr::ii<DataVector, Dim>&,\n          const tnsr::ijj<DataVector, Dim>&, const tnsr::ii<DataVector, Dim>&)>(\n          &::gr::time_derivative_of_spatial_metric),\n      py::arg(\"lapse\"), py::arg(\"shift\"), py::arg(\"deriv_shift\"),\n      py::arg(\"spatial_metric\"), py::arg(\"deriv_spatial_metric\"),\n      py::arg(\"extrinsic_curvature\"));\n\n  m.def(\"transverse_projection_operator\",\n        static_cast<tnsr::II<DataVector, Dim> (*)(\n            const tnsr::II<DataVector, Dim>&, const tnsr::I<DataVector, Dim>&)>(\n            &::gr::transverse_projection_operator),\n        py::arg(\"inverse_spatial_metric\"), py::arg(\"normal_vector\"));\n\n  m.def(\"transverse_projection_operator\",\n        static_cast<tnsr::ii<DataVector, Dim> (*)(\n            const tnsr::ii<DataVector, Dim>&, const tnsr::i<DataVector, Dim>&)>(\n            &::gr::transverse_projection_operator),\n        py::arg(\"spatial_metric\"), py::arg(\"normal_one_form\"));\n\n  m.def(\"transverse_projection_operator\",\n        static_cast<tnsr::Ij<DataVector, Dim> (*)(\n            const tnsr::I<DataVector, Dim>&, const tnsr::i<DataVector, Dim>&)>(\n            &::gr::transverse_projection_operator),\n        py::arg(\"normal_vector\"), py::arg(\"normal_one_form\"));\n\n  m.def(\"transverse_projection_operator\",\n        static_cast<tnsr::aa<DataVector, Dim> (*)(\n            const tnsr::aa<DataVector, Dim>&, const tnsr::a<DataVector, Dim>&,\n            const tnsr::i<DataVector, Dim>&, const tnsr::I<DataVector, Dim>&)>(\n            &::gr::transverse_projection_operator),\n        py::arg(\"spacetime_metric\"), py::arg(\"spacetime_normal_one_form\"),\n        py::arg(\"interface_unit_normal_one_form\"), py::arg(\"shift\"));\n\n  m.def(\"transverse_projection_operator\",\n        static_cast<tnsr::AA<DataVector, Dim> (*)(\n            const tnsr::AA<DataVector, Dim>&, const tnsr::A<DataVector, Dim>&,\n            const tnsr::I<DataVector, Dim>&)>(\n            &::gr::transverse_projection_operator),\n        py::arg(\"inverse_spacetime_metric\"), py::arg(\"spacetime_normal_vector\"),\n        py::arg(\"interface_unit_normal_vector\"));\n\n  m.def(\"transverse_projection_operator\",\n        static_cast<tnsr::Ab<DataVector, Dim> (*)(\n            const tnsr::A<DataVector, Dim>&, const tnsr::a<DataVector, Dim>&,\n            const tnsr::I<DataVector, Dim>&, const tnsr::i<DataVector, Dim>&,\n            const tnsr::I<DataVector, Dim>&)>(\n            &::gr::transverse_projection_operator),\n        py::arg(\"spacetime_normal_vector\"),\n        py::arg(\"spacetime_normal_one_form\"),\n        py::arg(\"interface_unit_normal_vector\"),\n        py::arg(\"interface_unit_normal_one_form\"), py::arg(\"shift\"));\n\n  m.def(\n      \"shift\",\n      static_cast<tnsr::I<DataVector, Dim> (*)(\n          const tnsr::aa<DataVector, Dim>&, const tnsr::II<DataVector, Dim>&)>(\n          &::gr::shift),\n      py::arg(\"spacetime_metric\"), py::arg(\"inverse_spatial_metric\"));\n\n  m.def(\"spacetime_normal_one_form\",\n        static_cast<tnsr::a<DataVector, Dim> (*)(const Scalar<DataVector>&)>(\n            &::gr::spacetime_normal_one_form),\n        py::arg(\"lapse\"));\n\n  m.def(\"spacetime_normal_vector\",\n        static_cast<tnsr::A<DataVector, Dim> (*)(\n            const Scalar<DataVector>&, const tnsr::I<DataVector, Dim>&)>(\n            &::gr::spacetime_normal_vector),\n        py::arg(\"lapse\"), py::arg(\"shift\"));\n\n  m.def(\"weyl_electric\",\n        static_cast<tnsr::ii<DataVector, Dim> (*)(\n            const tnsr::ii<DataVector, Dim>&, const tnsr::ii<DataVector, Dim>&,\n            const tnsr::II<DataVector, Dim>&)>(&::gr::weyl_electric),\n        py::arg(\"spatial_ricci\"), py::arg(\"extrinsic_curvature\"),\n        py::arg(\"inverse_spatial_metric\"));\n\n  m.def(\"weyl_electric_scalar\",\n        static_cast<Scalar<DataVector> (*)(const tnsr::ii<DataVector, Dim>&,\n                                           const tnsr::II<DataVector, Dim>&)>(\n            &::gr::weyl_electric_scalar),\n        py::arg(\"weyl_electric\"), py::arg(\"inverse_spatial_metric\"));\n\n  m.def(\"weyl_magnetic\",\n        static_cast<tnsr::ii<DataVector, 3, Frame::Inertial> (*)(\n            const tnsr::ijj<DataVector, 3, Frame::Inertial>&,\n            const tnsr::ii<DataVector, 3, Frame::Inertial>&,\n            const Scalar<DataVector>&)>(&gr::weyl_magnetic),\n        py::arg(\"grad_extrinsic_curvature\"), py::arg(\"spatial_metric\"),\n        py::arg(\"sqrt_det_spatial_metric\"));\n\n  m.def(\"weyl_magnetic_scalar\",\n        static_cast<Scalar<DataVector> (*)(\n            const tnsr::ii<DataVector, 3, Frame::Inertial>&,\n            const tnsr::II<DataVector, 3, Frame::Inertial>&)>(\n            &gr::weyl_magnetic_scalar),\n        py::arg(\"weyl_magnetic\"), py::arg(\"inverse_spatial_metric\"));\n\n  m.def(\"weyl_propagating\",\n        py::overload_cast<\n            const tnsr::ii<DataVector, Dim>&, const tnsr::ii<DataVector, Dim>&,\n            const tnsr::II<DataVector, Dim>&, const tnsr::ijj<DataVector, Dim>&,\n            const tnsr::I<DataVector, Dim>&, const tnsr::II<DataVector, Dim>&,\n            const tnsr::ii<DataVector, Dim>&, const tnsr::Ij<DataVector, Dim>&,\n            const double>(\n            &gr::weyl_propagating<DataVector, Dim, Frame::Inertial>),\n        py::arg(\"ricci\"), py::arg(\"extrinsic_curvature\"),\n        py::arg(\"inverse_spatial_metric\"),\n        py::arg(\"cov_deriv_extrinsic_curvature\"),\n        py::arg(\"unit_interface_normal_vector\"), py::arg(\"projection_IJ\"),\n        py::arg(\"projection_ij\"), py::arg(\"projection_Ij\"), py::arg(\"sign\"));\n\n  m.def(\n      \"weyl_type_D1\",\n      static_cast<tnsr::ii<DataVector, 3, Frame::Inertial> (*)(\n          const tnsr::ii<DataVector, 3, Frame::Inertial>&,\n          const tnsr::ii<DataVector, 3, Frame::Inertial>&,\n          const tnsr::II<DataVector, 3, Frame::Inertial>&)>(&gr::weyl_type_D1),\n      py::arg(\"weyl_electric\"), py::arg(\"spatial_metric\"),\n      py::arg(\"inverse_spatial_metric\"));\n\n  m.def(\"weyl_type_D1_scalar\",\n        static_cast<Scalar<DataVector> (*)(\n            const tnsr::ii<DataVector, 3, Frame::Inertial>&,\n            const tnsr::II<DataVector, 3, Frame::Inertial>&)>(\n            &gr::weyl_type_D1_scalar),\n        py::arg(\"weyl_type_D1\"), py::arg(\"inverse_spatial_metric\"));\n}\n}  // namespace\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py::module_::import(\"spectre.Spectral\");\n  py_bindings::bind_spacetime_impl<1, IndexType::Spatial>(m);\n  py_bindings::bind_spacetime_impl<2, IndexType::Spatial>(m);\n  py_bindings::bind_spacetime_impl<3, IndexType::Spatial>(m);\n  py_bindings::bind_spacetime_impl<1, IndexType::Spacetime>(m);\n  py_bindings::bind_spacetime_impl<2, IndexType::Spacetime>(m);\n  py_bindings::bind_spacetime_impl<3, IndexType::Spacetime>(m);\n  py_bindings::bind_impl<1>(m);\n  py_bindings::bind_impl<2>(m);\n  py_bindings::bind_impl<3>(m);\n  m.def(\"psi4real\",\n        static_cast<Scalar<DataVector> (*)(\n            const tnsr::ii<DataVector, 3>&, const tnsr::ii<DataVector, 3>&,\n            const tnsr::ijj<DataVector, 3>&, const tnsr::ii<DataVector, 3>&,\n            const tnsr::II<DataVector, 3>&, const tnsr::I<DataVector, 3>&)>(\n            &::gr::psi_4_real),\n        py::arg(\"spatial_ricci\"), py::arg(\"extrinsic_curvature\"),\n        py::arg(\"cov_deriv_extrinsic_curvature\"), py::arg(\"spatial_metric\"),\n        py::arg(\"inverse_spatial_metric\"), py::arg(\"inertial_coords\"));\n  m.def(\"tortoise_radius_from_boyer_lindquist_minus_r_plus\",\n        &::gr::tortoise_radius_from_boyer_lindquist_minus_r_plus<double>,\n        py::arg(\"r_minus_r_plus\"), py::arg(\"mass\"),\n        py::arg(\"dimensionless_spin\"));\n  m.def(\"tortoise_radius_from_boyer_lindquist_minus_r_plus\",\n        &::gr::tortoise_radius_from_boyer_lindquist_minus_r_plus<DataVector>,\n        py::arg(\"r_minus_r_plus\"), py::arg(\"mass\"),\n        py::arg(\"dimensionless_spin\"));\n  m.def(\"boyer_lindquist_radius_minus_r_plus_from_tortoise\",\n        &::gr::boyer_lindquist_radius_minus_r_plus_from_tortoise<double>,\n        py::arg(\"r_star\"), py::arg(\"mass\"), py::arg(\"dimensionless_spin\"));\n  m.def(\"boyer_lindquist_radius_minus_r_plus_from_tortoise\",\n        &::gr::boyer_lindquist_radius_minus_r_plus_from_tortoise<DataVector>,\n        py::arg(\"r_star\"), py::arg(\"mass\"), py::arg(\"dimensionless_spin\"));\n}\n}  // namespace GeneralRelativity::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyGeneralRelativity\")\n\nspectre_python_add_module(\n    GeneralRelativity\n    LIBRARY_NAME ${LIBRARY}\n    MODULE_PATH \"PointwiseFunctions\"\n    SOURCES\n    Bindings.cpp\n    PYTHON_FILES\n    __init__.py\n)\n\nspectre_python_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n)\n\nspectre_python_link_libraries(\n    ${LIBRARY}\n    PRIVATE\n    DataStructures\n    GeneralRelativity\n    pybind11::module\n    Utilities\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PySpectral\n  PyTensor\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/QuadraticCurvatureScalars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/QuadraticCurvatureScalars.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\n\ntemplate <typename Frame>\nvoid pontryagin_scalar_in_vacuum(\n    const gsl::not_null<Scalar<DataVector>*> pontryagin_scalar,\n    const tnsr::ii<DataVector, 3, Frame>& weyl_electric,\n    const tnsr::ii<DataVector, 3, Frame>& weyl_magnetic,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric) {\n  tenex::evaluate(pontryagin_scalar, -16.0 * weyl_electric(ti::i, ti::j) *\n                                         inverse_spatial_metric(ti::J, ti::K) *\n                                         weyl_magnetic(ti::k, ti::l) *\n                                         inverse_spatial_metric(ti::L, ti::I));\n}\n\ntemplate <typename Frame>\nScalar<DataVector> pontryagin_scalar_in_vacuum(\n    const tnsr::ii<DataVector, 3, Frame>& weyl_electric,\n    const tnsr::ii<DataVector, 3, Frame>& weyl_magnetic,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric) {\n  Scalar<DataVector> pontryagin_scalar{get<0, 0>(weyl_electric).size()};\n  pontryagin_scalar_in_vacuum(make_not_null(&pontryagin_scalar), weyl_electric,\n                              weyl_magnetic, inverse_spatial_metric);\n  return pontryagin_scalar;\n}\n\nvoid gauss_bonnet_scalar_in_vacuum(\n    const gsl::not_null<Scalar<DataVector>*> gb_scalar,\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar) {\n  // Compute the Kretschmann scalar in vacuum\n  gb_scalar->get() =\n      8.0 * (weyl_electric_scalar.get() - weyl_magnetic_scalar.get());\n}\n\nScalar<DataVector> gauss_bonnet_scalar_in_vacuum(\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar) {\n  Scalar<DataVector> gb_scalar{get(weyl_electric_scalar).size()};\n  gauss_bonnet_scalar_in_vacuum(make_not_null(&gb_scalar), weyl_electric_scalar,\n                                weyl_magnetic_scalar);\n  return gb_scalar;\n}\n\n}  // namespace gr\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template Scalar<DataVector> gr::pontryagin_scalar_in_vacuum(             \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& weyl_electric,           \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& weyl_magnetic,           \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& inverse_spatial_metric); \\\n  template void gr::pontryagin_scalar_in_vacuum(                            \\\n      const gsl::not_null<Scalar<DataVector>*> pontryagin_scalar,          \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& weyl_electric,           \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& weyl_magnetic,           \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& inverse_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Frame::Grid, Frame::Inertial))\n\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/QuadraticCurvatureScalars.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n\n/// @{\n/*!\n * \\brief Computes the Pontryagin scalar in vacuum.\n *\n * \\details The Pontryagin scalar in vacuum is given by\n * \\begin{align}\n *   \\mathcal{P} &\\equiv {^{\\star} C}_{abcd} C^{abcd} \\\\\n *    &= - 16 E_{ab} B^{ab} ~,\n * \\end{align}\n * where $ C_{abcd} $ it the Weyl tensor (with dual $ {^\\star} C}_{abcd} $) in 4\n * spacetime dimensions. Here it is computed in terms of the electric ($\n * E_{ab} $) and magnetic ($ B_{ab} $) parts of the Weyl scalar, with the\n * conventions used here for ($ \\{E_{ab}, B_{ab}\\} $).\n *\n * \\see `gr::Tags::WeylMagnetic` and `gr::Tags::WeylElectric`\n *\n */\ntemplate <typename Frame>\nvoid pontryagin_scalar_in_vacuum(\n    gsl::not_null<Scalar<DataVector>*> pontryagin_scalar,\n    const tnsr::ii<DataVector, 3, Frame>& weyl_electric,\n    const tnsr::ii<DataVector, 3, Frame>& weyl_magnetic,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric);\n\ntemplate <typename Frame>\nScalar<DataVector> pontryagin_scalar_in_vacuum(\n    const tnsr::ii<DataVector, 3, Frame>& weyl_electric,\n    const tnsr::ii<DataVector, 3, Frame>& weyl_magnetic,\n    const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric);\n/// @}\n\n/// @{\n/*!\n * \\brief Computes Gauss-Bonnet scalar in vacuum.\n *\n * \\details The Gauss-Bonnet scalar in vacuum is given by\n * \\begin{align}\n *   \\mathcal{G} &\\equiv R_{abcd} R^{abcd} - 4 R_{ab} R^{ab} + R^2\n *    &= C_{abcd} C^{abcd}\n *    &= 8 (E_{ab} E^{ab} - B_{ab} B^{ab}) ~,\n * \\end{align}\n * where $ R_{abcd} $, $ R_{ab} $, $ R $ $ C_{abcd} $ are the Riemann tensor,\n * Ricci tensor, Ricci scalar and Weyl tensor in 4 spacetime dimensions. The\n * Gauss-Bonnet scalar in vacuum can be computed in terms of the electric ($\n * E_{ab} $) and magnetic ($ B_{ab} $) parts of the Weyl tensor.\n *\n * \\see `gr::Tags::WeylMagnetic` and `gr::Tags::WeylElectric`\n *\n */\nvoid gauss_bonnet_scalar_in_vacuum(\n    gsl::not_null<Scalar<DataVector>*> gb_scalar,\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar);\n\nScalar<DataVector> gauss_bonnet_scalar_in_vacuum(\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar);\n/// @}\n\n}  // namespace gr\n\nnamespace gr::Tags {\n/// @{\n/*!\n * \\brief Tags for the PontryaginScalar in vacuum.\n *\n * The tags are tested in Test_CurvatureScalarComputeTags.cpp\n */\ntemplate <typename DataType>\nstruct PontryaginScalar : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct PontryaginScalarCompute : PontryaginScalar<DataType>, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::WeylElectric<DataType, Dim, Frame>,\n                 gr::Tags::WeylMagnetic<DataType, Dim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>>;\n  using return_type = Scalar<DataType>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataType>*>, const tnsr::ii<DataType, Dim, Frame>&,\n      const tnsr::ii<DataType, Dim, Frame>&,\n      const tnsr::II<DataType, Dim, Frame>&)>(\n      &gr::pontryagin_scalar_in_vacuum<Frame>);\n  using base = PontryaginScalar<DataType>;\n};\n/// @{\n\n/// @{\n/*!\n * \\brief Tags for the Gauss Bonnet Scalar in vacuum.\n *\n * The tags are tested in Test_CurvatureScalarComputeTags.cpp\n */\ntemplate <typename DataType>\nstruct GaussBonnetScalar : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType>\nstruct GaussBonnetScalarCompute : GaussBonnetScalar<DataType>, db::ComputeTag {\n  using argument_tags = tmpl::list<gr::Tags::WeylElectricScalar<DataType>,\n                                   gr::Tags::WeylMagneticScalar<DataType>>;\n  using return_type = Scalar<DataType>;\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<Scalar<DataType>*>,\n                           const Scalar<DataType>&, const Scalar<DataType>&)>(\n          &gr::gauss_bonnet_scalar_in_vacuum);\n  using base = GaussBonnetScalar<DataType>;\n};\n/// @}\n}  // namespace gr::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Ricci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gr {\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nvoid ricci_tensor(\n    const gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame, Index>*> result,\n    const tnsr::Abb<DataType, SpatialDim, Frame, Index>& christoffel_2nd_kind,\n    const tnsr::aBcc<DataType, SpatialDim, Frame, Index>&\n        d_christoffel_2nd_kind) {\n  set_number_of_grid_points(result, christoffel_2nd_kind);\n  for (auto& component : *result) {\n    component = 0.0;\n  }\n  const auto dimensionality = index_dim<0>(*result);\n  for (size_t i = 0; i < dimensionality; ++i) {\n    for (size_t j = i; j < dimensionality; ++j) {\n      for (size_t m = 0; m < dimensionality; ++m) {\n        result->get(i, j) += d_christoffel_2nd_kind.get(m, m, i, j) -\n                             0.5 * (d_christoffel_2nd_kind.get(i, m, m, j) +\n                                    d_christoffel_2nd_kind.get(j, m, m, i));\n\n        for (size_t n = 0; n < dimensionality; ++n) {\n          result->get(i, j) += christoffel_2nd_kind.get(m, i, j) *\n                                   christoffel_2nd_kind.get(n, n, m) -\n                               christoffel_2nd_kind.get(m, i, n) *\n                                   christoffel_2nd_kind.get(n, m, j);\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\ntnsr::aa<DataType, SpatialDim, Frame, Index> ricci_tensor(\n    const tnsr::Abb<DataType, SpatialDim, Frame, Index>& christoffel_2nd_kind,\n    const tnsr::aBcc<DataType, SpatialDim, Frame, Index>&\n        d_christoffel_2nd_kind) {\n  tnsr::aa<DataType, SpatialDim, Frame, Index> result{};\n  ricci_tensor(make_not_null(&result), christoffel_2nd_kind,\n               d_christoffel_2nd_kind);\n  return result;\n}\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nvoid ricci_scalar(\n    const gsl::not_null<Scalar<DataType>*> ricci_scalar_result,\n    const tnsr::aa<DataType, SpatialDim, Frame, Index>& ricci_tensor,\n    const tnsr::AA<DataType, SpatialDim, Frame, Index>& inverse_metric) {\n  trace(ricci_scalar_result, ricci_tensor, inverse_metric);\n}\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nScalar<DataType> ricci_scalar(\n    const tnsr::aa<DataType, SpatialDim, Frame, Index>& ricci_tensor,\n    const tnsr::AA<DataType, SpatialDim, Frame, Index>& inverse_metric) {\n  auto ricci_scalar_result =\n      make_with_value<Scalar<DataType>>(get<0, 0>(inverse_metric), 0.0);\n  ricci_scalar(make_not_null(&ricci_scalar_result), ricci_tensor,\n               inverse_metric);\n  return ricci_scalar_result;\n}\n} // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define INDEXTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template void gr::ricci_tensor(                                             \\\n      const gsl::not_null<                                                    \\\n          tnsr::aa<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>*>    \\\n          result,                                                             \\\n      const tnsr::Abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>&  \\\n          christoffel_2nd_kind,                                               \\\n      const tnsr::aBcc<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>& \\\n          d_christoffel_2nd_kind);                                            \\\n  template tnsr::aa<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>     \\\n  gr::ricci_tensor(                                                           \\\n      const tnsr::Abb<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>&  \\\n          christoffel_2nd_kind,                                               \\\n      const tnsr::aBcc<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>& \\\n          d_christoffel_2nd_kind);                                            \\\n  template Scalar<DTYPE(data)> gr::ricci_scalar(                              \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>&   \\\n          ricci_tensor,                                                       \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>&   \\\n          inverse_metric);                                                    \\\n  template void gr::ricci_scalar(                                             \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> ricci_scalar_result,          \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>&   \\\n          ricci_tensor,                                                       \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data), INDEXTYPE(data)>&   \\\n          inverse_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial),\n                        (IndexType::Spatial, IndexType::Spacetime))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INDEXTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Ricci.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Declares function templates to calculate the Ricci tensor\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes Ricci tensor from the (spatial or spacetime)\n * Christoffel symbol of the second kind and its derivative.\n *\n * \\details Computes Ricci tensor \\f$R_{ab}\\f$ as:\n * \\f$ R_{ab} = \\partial_c \\Gamma^{c}_{ab} - \\partial_{(b} \\Gamma^{c}_{a)c}\n * + \\Gamma^{d}_{ab}\\Gamma^{c}_{cd} - \\Gamma^{d}_{ac} \\Gamma^{c}_{bd} \\f$\n * where \\f$\\Gamma^{a}_{bc}\\f$ is the Christoffel symbol of the second kind.\n */\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nvoid ricci_tensor(\n    gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame, Index>*> result,\n    const tnsr::Abb<DataType, SpatialDim, Frame, Index>& christoffel_2nd_kind,\n    const tnsr::aBcc<DataType, SpatialDim, Frame, Index>&\n        d_christoffel_2nd_kind);\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\ntnsr::aa<DataType, SpatialDim, Frame, Index> ricci_tensor(\n    const tnsr::Abb<DataType, SpatialDim, Frame, Index>& christoffel_2nd_kind,\n    const tnsr::aBcc<DataType, SpatialDim, Frame, Index>&\n        d_christoffel_2nd_kind);\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the Ricci Scalar from the (spatial or spacetime) Ricci Tensor\n * and inverse metrics.\n *\n * \\details Computes Ricci scalar using the inverse metric (spatial or\n * spacetime) and Ricci tensor \\f$R = g^{ab}R_{ab}\\f$\n */\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nvoid ricci_scalar(\n    gsl::not_null<Scalar<DataType>*> ricci_scalar_result,\n    const tnsr::aa<DataType, SpatialDim, Frame, Index>& ricci_tensor,\n    const tnsr::AA<DataType, SpatialDim, Frame, Index>& inverse_metric);\n\ntemplate <size_t SpatialDim, typename Frame, IndexType Index, typename DataType>\nScalar<DataType> ricci_scalar(\n    const tnsr::aa<DataType, SpatialDim, Frame, Index>& ricci_tensor,\n    const tnsr::AA<DataType, SpatialDim, Frame, Index>& inverse_metric);\n/// @}\n\nnamespace Tags {\n/// Compute item for spatial Ricci tensor \\f$R_{ij}\\f$\n/// computed from SpatialChristoffelSecondKind and its spatial derivatives.\n///\n/// Can be retrieved using `gr::Tags::SpatialRicci`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpatialRicciCompute : SpatialRicci<DataType, SpatialDim, Frame>,\n                             db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gr::Tags::SpatialChristoffelSecondKind<DataType, SpatialDim, Frame>,\n      ::Tags::deriv<\n          gr::Tags::SpatialChristoffelSecondKind<DataType, SpatialDim, Frame>,\n          tmpl::size_t<SpatialDim>, Frame>>;\n\n  using return_type = tnsr::ii<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*>,\n      const tnsr::Ijj<DataType, SpatialDim, Frame>&,\n      const tnsr::iJkk<DataType, SpatialDim, Frame>&)>(\n      &ricci_tensor<SpatialDim, Frame, IndexType::Spatial, DataType>);\n\n  using base = SpatialRicci<DataType, SpatialDim, Frame>;\n};\n\n/// Computes the spatial Ricci scalar using the spatial Ricci tensor and the\n/// inverse spatial metric.\n///\n/// Can be retrieved using `gr::Tags::SpatialRicciScalar`\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpatialRicciScalarCompute : SpatialRicciScalar<DataType>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::SpatialRicci<DataType, SpatialDim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<Scalar<DataType>*>,\n                           const tnsr::ii<DataType, SpatialDim, Frame>&,\n                           const tnsr::II<DataType, SpatialDim, Frame>&)>(\n          &ricci_scalar<SpatialDim, Frame, IndexType::Spatial, DataType>);\n\n  using base = SpatialRicciScalar<DataType>;\n};\n}  // namespace Tags\n} // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Shift.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::I<DataType, SpatialDim, Frame> shift(\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  tnsr::I<DataType, SpatialDim, Frame> local_shift{\n      get_size(get<0, 0>(spacetime_metric))};\n  shift(make_not_null(&local_shift), spacetime_metric, inverse_spatial_metric);\n  return local_shift;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid shift(\n    const gsl::not_null<tnsr::I<DataType, SpatialDim, Frame>*> shift,\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    shift->get(i) =\n        inverse_spatial_metric.get(i, 0) * get<1, 0>(spacetime_metric);\n    for (size_t j = 1; j < SpatialDim; ++j) {\n      shift->get(i) +=\n          inverse_spatial_metric.get(i, j) * spacetime_metric.get(j + 1, 0);\n    }\n  }\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template tnsr::I<DTYPE(data), DIM(data), FRAME(data)> gr::shift(           \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& spacetime_metric, \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spatial_metric);                                           \\\n  template void gr::shift(                                                   \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          shift,                                                             \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& spacetime_metric, \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Shift.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute shift from spacetime metric and inverse spatial metric.\n *\n * \\details Computes\n * \\f{align}\n *    \\beta^i &= \\gamma^{ij} g_{jt}\n * \\f}\n * where \\f$ \\beta^i\\f$, \\f$ \\gamma^{ij}\\f$, and \\f$g_{ab}\\f$ are the shift,\n * inverse spatial metric, and spacetime metric.\n * This can be derived, e.g., from Eqs. 2.121--2.122 of Baumgarte & Shapiro.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::I<DataType, SpatialDim, Frame> shift(\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid shift(gsl::not_null<tnsr::I<DataType, SpatialDim, Frame>*> shift,\n           const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric,\n           const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item for shift \\f$\\beta^i\\f$ from the spacetime metric\n * \\f$g_{ab}\\f$ and the inverse spatial metric \\f$\\gamma^{ij}\\f$.\n *\n * \\details Can be retrieved using `gr::Tags::Shift`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct ShiftCompute : Shift<DataType, SpatialDim, Frame>, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpacetimeMetric<DataType, SpatialDim, Frame>,\n                 InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::I<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::I<DataType, SpatialDim, Frame>*> shift,\n      const tnsr::aa<DataType, SpatialDim, Frame>&,\n      const tnsr::II<DataType, SpatialDim, Frame>&)>(\n      &shift<DataType, SpatialDim, Frame>);\n\n  using base = Shift<DataType, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpacetimeDerivativeOfGothG.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeDerivativeOfGothG.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_deriv_of_goth_g(\n    gsl::not_null<tnsr::aBB<DataType, SpatialDim, Frame>*> da_goth_g,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::abb<DataType, SpatialDim, Frame>& da_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::a<DataType, SpatialDim, Frame>& da_lapse,\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& da_det_spatial_metric) {\n  set_number_of_grid_points(da_goth_g, da_spacetime_metric);\n\n  tenex::evaluate<ti::a, ti::B, ti::C>(da_goth_g,\n    (da_lapse(ti::a) * sqrt_det_spatial_metric()\n        + 0.5 * lapse() * da_det_spatial_metric(ti::a)\n        / sqrt_det_spatial_metric()) * inverse_spacetime_metric(ti::B, ti::C)\n     - lapse() * sqrt_det_spatial_metric()\n                                    * inverse_spacetime_metric(ti::B, ti::D)\n                                    * inverse_spacetime_metric(ti::C, ti::E)\n                                    * da_spacetime_metric(ti::a, ti::d, ti::e));\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aBB<DataType, SpatialDim, Frame> spacetime_deriv_of_goth_g(\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::abb<DataType, SpatialDim, Frame>& da_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::a<DataType, SpatialDim, Frame>& da_lapse,\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& da_det_spatial_metric) {\n  tnsr::aBB<DataType, SpatialDim, Frame> da_goth_g{};\n  gr::spacetime_deriv_of_goth_g(make_not_null(&da_goth_g),\n          inverse_spacetime_metric, da_spacetime_metric, lapse, da_lapse,\n                   sqrt_det_spatial_metric, da_det_spatial_metric);\n  return da_goth_g;\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void gr::spacetime_deriv_of_goth_g(                            \\\n      const gsl::not_null<tnsr::aBB<DTYPE(data), DIM(data), FRAME(data)>*>\\\n          da_goth_g,                                                      \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_spacetime_metric,                                       \\\n      const tnsr::abb<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          da_spacetime_metric,                                            \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& da_lapse,       \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric,                 \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          da_det_spatial_metric);                                         \\\n  template tnsr::aBB<DTYPE(data), DIM(data), FRAME(data)>                 \\\n  gr::spacetime_deriv_of_goth_g(                                          \\\n      const tnsr::AA<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          inverse_spacetime_metric,                                       \\\n      const tnsr::abb<DTYPE(data), DIM(data), FRAME(data)>&               \\\n          da_spacetime_metric,                                            \\\n      const Scalar<DTYPE(data)>& lapse,                                   \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& da_lapse,       \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric,                 \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          da_det_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpacetimeDerivativeOfGothG.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes spacetime derivative of\n * \\f$ \\mathfrak{g}^{ab}\\equiv (-g)^{1/2} g^{ab} \\f$.\n *\n * \\details Computes the spacetime derivative of\n * \\f$ \\mathfrak{g}^{ab}\\equiv (-g)^{1/2} g^{ab} \\f$, defined\n * in \\cite Misner1973. Using \\f$ (-g)^{1/2} = \\alpha (\\gamma)^{1/2} \\f$, where\n * \\f$ \\alpha \\f$ is the lapse and \\f$ \\gamma \\f$ is the determinant of the\n * spatial metric (\\cite BaumgarteShapiro), the derivative of\n * \\f$ \\mathfrak{g}^{ab} \\f$ expands out to\n * \\f{align}{\n *   \\partial_c \\mathfrak{g}^{ab} &= \\left[\\partial_c \\alpha \\gamma^{1/2}\n *   + \\frac{1}{2} \\alpha \\partial_c \\gamma \\gamma^{-1/2} \\right] g^{ab}\n *   - \\alpha \\gamma^{1/2} g^{ad} g^{be} \\partial_c g_{de}.\n * \\f}\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_deriv_of_goth_g(\n    gsl::not_null<tnsr::aBB<DataType, SpatialDim, Frame>*> da_goth_g,\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::abb<DataType, SpatialDim, Frame>& da_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::a<DataType, SpatialDim, Frame>& da_lapse,\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& da_det_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aBB<DataType, SpatialDim, Frame> spacetime_deriv_of_goth_g(\n    const tnsr::AA<DataType, SpatialDim, Frame>& inverse_spacetime_metric,\n    const tnsr::abb<DataType, SpatialDim, Frame>& da_spacetime_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::a<DataType, SpatialDim, Frame>& da_lapse,\n    const Scalar<DataType>& sqrt_det_spatial_metric,\n    const tnsr::a<DataType, SpatialDim, Frame>& da_det_spatial_metric);\n/// @}\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpacetimeMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid spacetime_metric(\n    const gsl::not_null<tnsr::aa<DataType, Dim, Frame>*> spacetime_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric) {\n  if (UNLIKELY(get_size(get<0, 0>(*spacetime_metric)) !=\n               get_size(get(lapse)))) {\n    *spacetime_metric = tnsr::aa<DataType, Dim, Frame>(get_size(get(lapse)));\n  }\n\n  get<0, 0>(*spacetime_metric) = -square(get(lapse));\n\n  for (size_t m = 0; m < Dim; ++m) {\n    get<0, 0>(*spacetime_metric) +=\n        spatial_metric.get(m, m) * square(shift.get(m));\n    for (size_t n = 0; n < m; ++n) {\n      get<0, 0>(*spacetime_metric) +=\n          2. * spatial_metric.get(m, n) * shift.get(m) * shift.get(n);\n    }\n  }\n\n  for (size_t i = 0; i < Dim; ++i) {\n    spacetime_metric->get(0, i + 1) = 0.;\n    for (size_t m = 0; m < Dim; ++m) {\n      spacetime_metric->get(0, i + 1) +=\n          spatial_metric.get(m, i) * shift.get(m);\n    }\n    for (size_t j = i; j < Dim; ++j) {\n      spacetime_metric->get(i + 1, j + 1) = spatial_metric.get(i, j);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::aa<DataType, Dim, Frame> spacetime_metric(\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric) {\n  tnsr::aa<DataType, Dim, Frame> spacetime_metric{};\n  gr::spacetime_metric(make_not_null(&spacetime_metric), lapse, shift,\n                       spatial_metric);\n  return spacetime_metric;\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void gr::spacetime_metric(                                          \\\n      const gsl::not_null<tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>*>      \\\n          spacetime_metric,                                                    \\\n      const Scalar<DTYPE(data)>& lapse,                                        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,               \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric);    \\\n  template tnsr::aa<DTYPE(data), DIM(data), FRAME(data)> gr::spacetime_metric( \\\n      const Scalar<DTYPE(data)>& lapse,                                        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,               \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the spacetime metric from the spatial metric, lapse, and\n * shift.\n * \\details The spacetime metric \\f$ g_{ab} \\f$ is calculated as\n * \\f{align}{\n *   g_{tt} &= - \\alpha^2 + \\beta^m \\beta^n \\gamma_{mn} \\\\\n *   g_{ti} &= \\gamma_{mi} \\beta^m  \\\\\n *   g_{ij} &= \\gamma_{ij}\n * \\f}\n * where \\f$ \\alpha, \\beta^i\\f$ and \\f$ \\gamma_{ij}\\f$ are the lapse, shift and\n * spatial metric respectively\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid spacetime_metric(\n    gsl::not_null<tnsr::aa<DataType, Dim, Frame>*> spacetime_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Frame>& shift,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> spacetime_metric(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item for spacetime metric \\f$g_{ab}\\f$ from the lapse\n * \\f$\\alpha\\f$, shift \\f$\\beta^i\\f$, and spatial metric \\f$\\gamma_{ij}\\f$.\n *\n * \\details Can be retrieved using `gr::Tags::SpacetimeMetric`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpacetimeMetricCompute : SpacetimeMetric<DataType, SpatialDim, Frame>,\n                                db::ComputeTag {\n  using argument_tags =\n      tmpl::list<Lapse<DataType>, Shift<DataType, SpatialDim, Frame>,\n                 SpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::aa<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*>,\n      const Scalar<DataType>&, const tnsr::I<DataType, SpatialDim, Frame>&,\n      const tnsr::ii<DataType, SpatialDim, Frame>&)>(\n      &spacetime_metric<DataType, SpatialDim, Frame>);\n\n  using base = SpacetimeMetric<DataType, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_normal_one_form(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> normal_one_form,\n    const Scalar<DataType>& lapse) {\n  set_number_of_grid_points(normal_one_form, lapse);\n  for (auto& component : *normal_one_form) {\n    component = 0.0;\n  }\n  get<0>(*normal_one_form) = -get(lapse);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> spacetime_normal_one_form(\n    const Scalar<DataType>& lapse) {\n  tnsr::a<DataType, SpatialDim, Frame> normal_one_form{};\n  spacetime_normal_one_form(make_not_null(&normal_one_form), lapse);\n  return normal_one_form;\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template tnsr::a<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gr::spacetime_normal_one_form(const Scalar<DTYPE(data)>& lapse);       \\\n  template void gr::spacetime_normal_one_form(                           \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          normal_one_form,                                               \\\n      const Scalar<DTYPE(data)>& lapse);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\brief Computes spacetime normal one-form from lapse.\n *\n * \\details If \\f$\\alpha\\f$ is the lapse, then\n *\n * \\f{align}\n *     n_t &= - \\alpha \\\\\n *     n_i &= 0\n * \\f}\n *\n * is computed.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_normal_one_form(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> normal_one_form,\n    const Scalar<DataType>& lapse);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> spacetime_normal_one_form(\n    const Scalar<DataType>& lapse);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item for spacetime normal oneform \\f$n_a\\f$ from\n * the lapse \\f$\\alpha\\f$.\n *\n * \\details Can be retrieved using `gr::Tags::SpacetimeNormalOneForm`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpacetimeNormalOneFormCompute\n    : SpacetimeNormalOneForm<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags = tmpl::list<Lapse<DataType>>;\n\n  using return_type = tnsr::a<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*>,\n                           const Scalar<DataType>&)>(\n          &spacetime_normal_one_form<DataType, SpatialDim, Frame>);\n\n  using base = SpacetimeNormalOneForm<DataType, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::A<DataType, SpatialDim, Frame> spacetime_normal_vector(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift) {\n  tnsr::A<DataType, SpatialDim, Frame> local_spacetime_normal_vector{\n      get_size(get(lapse))};\n  spacetime_normal_vector(make_not_null(&local_spacetime_normal_vector), lapse,\n                          shift);\n  return local_spacetime_normal_vector;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_normal_vector(\n    const gsl::not_null<tnsr::A<DataType, SpatialDim, Frame>*>\n        spacetime_normal_vector,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift) {\n  get<0>(*spacetime_normal_vector) = 1. / get(lapse);\n  for (size_t i = 0; i < SpatialDim; i++) {\n    spacetime_normal_vector->get(i + 1) =\n        -shift.get(i) * get<0>(*spacetime_normal_vector);\n  }\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                             \\\n  template tnsr::A<DTYPE(data), DIM(data), FRAME(data)>                  \\\n  gr::spacetime_normal_vector(                                           \\\n      const Scalar<DTYPE(data)>& lapse,                                  \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift);        \\\n  template void gr::spacetime_normal_vector(                             \\\n      const gsl::not_null<tnsr::A<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          spacetime_normal_vector,                                       \\\n      const Scalar<DTYPE(data)>& lapse,                                  \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief  Computes spacetime normal vector from lapse and shift.\n *\n * \\details If \\f$\\alpha, \\beta^i\\f$ are the lapse and shift respectively, then\n *\n * \\f{align} n^t &= 1/\\alpha \\\\\n * n^i &= -\\frac{\\beta^i}{\\alpha} \\f}\n *\n * is computed.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::A<DataType, SpatialDim, Frame> spacetime_normal_vector(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spacetime_normal_vector(\n    gsl::not_null<tnsr::A<DataType, SpatialDim, Frame>*>\n        spacetime_normal_vector,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item for spacetime normal vector \\f$n^a\\f$ from\n * the lapse \\f$\\alpha\\f$ and the shift \\f$\\beta^i\\f$.\n *\n * \\details Can be retrieved using `gr::Tags::SpacetimeNormalVector`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpacetimeNormalVectorCompute\n    : SpacetimeNormalVector<DataType, SpatialDim, Frame>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<Lapse<DataType>, Shift<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::A<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::A<DataType, SpatialDim, Frame>*>,\n      const Scalar<DataType>&, const tnsr::I<DataType, SpatialDim, Frame>&)>(\n      &spacetime_normal_vector<DataType, SpatialDim, Frame>);\n\n  using base = SpacetimeNormalVector<DataType, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpatialMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> spatial_metric(\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric) {\n  tnsr::ii<DataType, SpatialDim, Frame> local_spatial_metric{\n      get_size(get<0, 0>(spacetime_metric))};\n  spatial_metric(make_not_null(&local_spatial_metric), spacetime_metric);\n  return local_spatial_metric;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spatial_metric(\n    const gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> spatial_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      spatial_metric->get(i, j) = spacetime_metric.get(i + 1, j + 1);\n    }\n  }\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)> gr::spatial_metric(  \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& spacetime_metric); \\\n  template void gr::spatial_metric(                                           \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>     \\\n          spatial_metric,                                                     \\\n      const tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>& spacetime_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Compute spatial metric from spacetime metric.\n * \\details Simply pull out the spatial components.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> spatial_metric(\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid spatial_metric(\n    gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> spatial_metric,\n    const tnsr::aa<DataType, SpatialDim, Frame>& spacetime_metric);\n/// @}\n\nnamespace Tags {\n/*!\n * \\brief Compute item for spatial metric \\f$\\gamma_{ij}\\f$ from the\n * spacetime metric \\f$g_{ab}\\f$.\n *\n * \\details Can be retrieved using `gr::Tags::SpatialMetric`.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct SpatialMetricCompute : SpatialMetric<DataType, SpatialDim, Frame>,\n                              db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpacetimeMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::ii<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*>,\n      const tnsr::aa<DataType, SpatialDim, Frame>&)>(\n      &spatial_metric<DataType, SpatialDim, Frame>);\n\n  using base = SpatialMetric<DataType, SpatialDim, Frame>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/AreaElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/AreaElement.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid area_element(const gsl::not_null<Scalar<DataVector>*> result,\n                  const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n                  const ylm::Tags::aliases::Jacobian<Frame>& jacobian,\n                  const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n                  const Scalar<DataVector>& radius,\n                  const tnsr::i<DataVector, 3, Frame>& r_hat) {\n  auto cap_theta = make_with_value<tnsr::I<DataVector, 3, Frame>>(r_hat, 0.0);\n  auto cap_phi = make_with_value<tnsr::I<DataVector, 3, Frame>>(r_hat, 0.0);\n\n  for (size_t i = 0; i < 3; ++i) {\n    cap_theta.get(i) = jacobian.get(i, 0);\n    cap_phi.get(i) = jacobian.get(i, 1);\n    for (size_t j = 0; j < 3; ++j) {\n      cap_theta.get(i) += r_hat.get(i) *\n                          (r_hat.get(j) - normal_one_form.get(j)) *\n                          jacobian.get(j, 0);\n      cap_phi.get(i) += r_hat.get(i) * (r_hat.get(j) - normal_one_form.get(j)) *\n                        jacobian.get(j, 1);\n    }\n  }\n\n  get(*result) = square(get(radius));\n  get(*result) *=\n      sqrt(get(dot_product(cap_theta, cap_theta, spatial_metric)) *\n               get(dot_product(cap_phi, cap_phi, spatial_metric)) -\n           square(get(dot_product(cap_theta, cap_phi, spatial_metric))));\n}\n\ntemplate <typename Frame>\nScalar<DataVector> area_element(\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const ylm::Tags::aliases::Jacobian<Frame>& jacobian,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& r_hat) {\n  Scalar<DataVector> result{};\n  area_element(make_not_null(&result), spatial_metric, jacobian,\n               normal_one_form, radius, r_hat);\n  return result;\n}\n\ntemplate <typename Frame>\nvoid euclidean_area_element(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const ylm::Tags::aliases::Jacobian<Frame>& jacobian,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& r_hat) {\n  auto cap_theta = make_with_value<tnsr::I<DataVector, 3, Frame>>(r_hat, 0.0);\n  auto cap_phi = make_with_value<tnsr::I<DataVector, 3, Frame>>(r_hat, 0.0);\n\n  for (size_t i = 0; i < 3; ++i) {\n    cap_theta.get(i) = jacobian.get(i, 0);\n    cap_phi.get(i) = jacobian.get(i, 1);\n    for (size_t j = 0; j < 3; ++j) {\n      cap_theta.get(i) += r_hat.get(i) *\n                          (r_hat.get(j) - normal_one_form.get(j)) *\n                          jacobian.get(j, 0);\n      cap_phi.get(i) += r_hat.get(i) * (r_hat.get(j) - normal_one_form.get(j)) *\n                        jacobian.get(j, 1);\n    }\n  }\n\n  get(*result) = square(get(radius));\n  get(*result) *= sqrt(get(dot_product(cap_theta, cap_theta)) *\n                           get(dot_product(cap_phi, cap_phi)) -\n                       square(get(dot_product(cap_theta, cap_phi))));\n}\n\ntemplate <typename Frame>\nScalar<DataVector> euclidean_area_element(\n    const ylm::Tags::aliases::Jacobian<Frame>& jacobian,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& r_hat) {\n  Scalar<DataVector> result{};\n  euclidean_area_element(make_not_null(&result), jacobian, normal_one_form,\n                         radius, r_hat);\n  return result;\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                           \\\n  template void gr::surfaces::area_element<FRAME(data)>(               \\\n      const gsl::not_null<Scalar<DataVector>*> result,                 \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_metric,      \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& jacobian,       \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& normal_one_form,      \\\n      const Scalar<DataVector>& radius,                                \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat);               \\\n  template Scalar<DataVector> gr::surfaces::area_element<FRAME(data)>( \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_metric,      \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& jacobian,       \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& normal_one_form,      \\\n      const Scalar<DataVector>& radius,                                \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat);               \\\n  template void gr::surfaces::euclidean_area_element<FRAME(data)>(     \\\n      const gsl::not_null<Scalar<DataVector>*> result,                 \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& jacobian,       \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& normal_one_form,      \\\n      const Scalar<DataVector>& radius,                                \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat);               \\\n  template Scalar<DataVector>                                          \\\n  gr::surfaces::euclidean_area_element<FRAME(data)>(                   \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& jacobian,       \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& normal_one_form,      \\\n      const Scalar<DataVector>& radius,                                \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/AreaElement.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TagsTypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/// @{\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Area element of a 2D `Strahlkorper`.\n *\n * \\details Implements Eq. (D.13), using Eqs. (D.4) and (D.5),\n * of \\cite Baumgarte1996hh. Specifically, computes\n * \\f$\\sqrt{(\\Theta^i\\Theta_i)(\\Phi^j\\Phi_j)-(\\Theta^i\\Phi_i)^2}\\f$,\n * \\f$\\Theta^i=\\left(n^i(n_j-s_j) r J^j_\\theta + r J^i_\\theta\\right)\\f$,\n * \\f$\\Phi^i=\\left(n^i(n_j-s_j)r J^j_\\phi + r J^i_\\phi\\right)\\f$,\n * and \\f$\\Theta^i\\f$ and \\f$\\Phi^i\\f$ are lowered by the\n * 3D spatial metric \\f$g_{ij}\\f$. Here \\f$J^i_\\alpha\\f$, \\f$s_j\\f$,\n * \\f$r\\f$, and \\f$n^i=n_i\\f$ correspond to the input arguments\n * `jacobian`, `normal_one_form`, `radius`, and `r_hat`, respectively;\n * these input arguments depend only on the Strahlkorper, not on the\n * metric, and can be computed from a Strahlkorper using ComputeItems\n * in `ylm::Tags`. Note that this does not include the factor\n * of \\f$\\sin\\theta\\f$, i.e., this returns \\f$r^2\\f$ for a spherical\n * `Strahlkorper` in flat space.\n * This choice makes the area element returned here compatible with\n * `definite_integral` defined in `YlmSpherePack.hpp`.\n */\ntemplate <typename Frame>\nvoid area_element(gsl::not_null<Scalar<DataVector>*> result,\n                  const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n                  const ylm::Tags::aliases::Jacobian<Frame>& jacobian,\n                  const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n                  const Scalar<DataVector>& radius,\n                  const tnsr::i<DataVector, 3, Frame>& r_hat);\n\ntemplate <typename Frame>\nScalar<DataVector> area_element(\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const ylm::Tags::aliases::Jacobian<Frame>& jacobian,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& r_hat);\n/// @}\n\n/// @{\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Euclidean area element of a 2D `Strahlkorper`.\n *\n * This is useful for computing a flat-space integral over an\n * arbitrarily-shaped `Strahlkorper`.\n *\n * \\details Implements Eq. (D.13), using Eqs. (D.4) and (D.5),\n * of \\cite Baumgarte1996hh. Specifically, computes\n * \\f$\\sqrt{(\\Theta^i\\Theta_i)(\\Phi^j\\Phi_j)-(\\Theta^i\\Phi_i)^2}\\f$,\n * \\f$\\Theta^i=\\left(n^i(n_j-s_j) r J^j_\\theta + r J^i_\\theta\\right)\\f$,\n * \\f$\\Phi^i=\\left(n^i(n_j-s_j)r J^j_\\phi + r J^i_\\phi\\right)\\f$,\n * and \\f$\\Theta^i\\f$ and \\f$\\Phi^i\\f$ are lowered by the\n * Euclidean spatial metric. Here \\f$J^i_\\alpha\\f$, \\f$s_j\\f$,\n * \\f$r\\f$, and \\f$n^i=n_i\\f$ correspond to the input arguments\n * `jacobian`, `normal_one_form`, `radius`, and `r_hat`, respectively;\n * these input arguments depend only on the Strahlkorper, not on the\n * metric, and can be computed from a Strahlkorper using ComputeItems\n * in `ylm::Tags`. Note that this does not include the factor\n * of \\f$\\sin\\theta\\f$, i.e., this returns \\f$r^2\\f$ for a spherical\n * `Strahlkorper`.\n * This choice makes the area element returned here compatible with\n * `definite_integral` defined in `YlmSpherePack.hpp`.\n */\ntemplate <typename Frame>\nvoid euclidean_area_element(\n    gsl::not_null<Scalar<DataVector>*> result,\n    const ylm::Tags::aliases::Jacobian<Frame>& jacobian,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& r_hat);\n\ntemplate <typename Frame>\nScalar<DataVector> euclidean_area_element(\n    const ylm::Tags::aliases::Jacobian<Frame>& jacobian,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& r_hat);\n/// @}\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY GrSurfaces)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AreaElement.cpp\n  Expansion.cpp\n  ExtrinsicCurvature.cpp\n  GradUnitNormalOneForm.cpp\n  InverseSurfaceMetric.cpp\n  Mass.cpp\n  RadialDistance.cpp\n  RicciScalar.cpp\n  Spin.cpp\n  SurfaceIntegralOfScalar.cpp\n  SurfaceIntegralOfVector.cpp\n  UnitNormalOneForm.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AreaElement.hpp\n  ComputeItems.hpp\n  ExtrinsicCurvature.hpp\n  GradUnitNormalOneForm.hpp\n  InverseSurfaceMetric.hpp\n  Mass.hpp\n  RadialDistance.hpp\n  RicciScalar.hpp\n  Spin.hpp\n  SurfaceIntegralOfScalar.hpp\n  SurfaceIntegralOfVector.hpp\n  Expansion.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  UnitNormalOneForm.hpp\n)\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  SphericalHarmonics\n  PRIVATE\n  ErrorHandling\n  GeneralRelativity\n  LinearAlgebra\n  Utilities\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/ComputeItems.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gr::surfaces::Tags {\n/// @{\n/// These ComputeItems are different from those used in\n/// GeneralizedHarmonic evolution because these live only on the\n/// intrp::Actions::ApparentHorizon DataBox, not in the volume\n/// DataBox.  And these ComputeItems can do fewer allocations than the\n/// volume ones, because (for example) Lapse, SpaceTimeNormalVector,\n/// etc.  can be inlined instead of being allocated as a separate\n/// ComputeItem.\ntemplate <size_t Dim, typename Frame>\nstruct InverseSpatialMetricCompute\n    : gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>,\n      db::ComputeTag {\n  using return_type = tnsr::II<DataVector, Dim, Frame>;\n  static void function(\n      const gsl::not_null<tnsr::II<DataVector, Dim, Frame>*> result,\n      const tnsr::aa<DataVector, Dim, Frame>& psi) {\n    *result = determinant_and_inverse(gr::spatial_metric(psi)).second;\n  };\n  using argument_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim, Frame>>;\n  using base = gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>;\n};\ntemplate <size_t Dim, typename Frame>\nstruct ExtrinsicCurvatureCompute\n    : gr::Tags::ExtrinsicCurvature<DataVector, Dim, Frame>,\n      db::ComputeTag {\n  using return_type = tnsr::ii<DataVector, Dim, Frame>;\n  static void function(\n      const gsl::not_null<tnsr::ii<DataVector, Dim, Frame>*> result,\n      const tnsr::aa<DataVector, Dim, Frame>& psi,\n      const tnsr::aa<DataVector, Dim, Frame>& pi,\n      const tnsr::iaa<DataVector, Dim, Frame>& phi,\n      const tnsr::II<DataVector, Dim, Frame>& inv_g) {\n    const auto shift = gr::shift(psi, inv_g);\n    set_number_of_grid_points(result, psi);\n    gh::extrinsic_curvature(\n        result, gr::spacetime_normal_vector(gr::lapse(shift, psi), shift), pi,\n        phi);\n  }\n  using argument_tags =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim, Frame>,\n                 gh::Tags::Pi<DataVector, Dim, Frame>,\n                 gh::Tags::Phi<DataVector, Dim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>>;\n  using base = gr::Tags::ExtrinsicCurvature<DataVector, Dim, Frame>;\n};\ntemplate <size_t Dim, typename Frame>\nstruct SpatialChristoffelSecondKindCompute\n    : ::gr::Tags::SpatialChristoffelSecondKind<DataVector, Dim, Frame>,\n      db::ComputeTag {\n  using return_type = tnsr::Ijj<DataVector, Dim, Frame>;\n  static void function(\n      const gsl::not_null<tnsr::Ijj<DataVector, Dim, Frame>*> result,\n      const tnsr::iaa<DataVector, Dim, Frame>& phi,\n      const tnsr::II<DataVector, Dim, Frame>& inv_g) {\n    set_number_of_grid_points(result, phi);\n    raise_or_lower_first_index(\n        result, gr::christoffel_first_kind(gh::deriv_spatial_metric(phi)),\n        inv_g);\n  }\n  using argument_tags =\n      tmpl::list<gh::Tags::Phi<DataVector, Dim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>>;\n  using base = ::gr::Tags::SpatialChristoffelSecondKind<DataVector, Dim, Frame>;\n};\n/// @}\n}  // namespace gr::surfaces::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Expansion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Expansion.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid expansion(const gsl::not_null<Scalar<DataVector>*> result,\n               const tnsr::ii<DataVector, 3, Frame>& grad_normal,\n               const tnsr::II<DataVector, 3, Frame>& inverse_surface_metric,\n               const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature) {\n  // If you want the future *ingoing* null expansion,\n  // the formula is the same as here except you\n  // change the sign on grad_normal just before you\n  // subtract the extrinsic curvature.\n  // That is, if GsBar is the value of grad_normal\n  // at this point in the code, and S^i is the unit\n  // spatial normal to the surface,\n  // the outgoing expansion is\n  // (g^ij - S^i S^j) (GsBar_ij - K_ij)\n  // and the ingoing expansion is\n  // (g^ij - S^i S^j) (-GsBar_ij - K_ij)\n  set_number_of_grid_points(result, grad_normal);\n  get(*result) = 0.0;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      get(*result) += inverse_surface_metric.get(i, j) *\n                      (grad_normal.get(i, j) - extrinsic_curvature.get(i, j));\n    }\n  }\n}\n\ntemplate <typename Frame>\nScalar<DataVector> expansion(\n    const tnsr::ii<DataVector, 3, Frame>& grad_normal,\n    const tnsr::II<DataVector, 3, Frame>& inverse_surface_metric,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature) {\n  Scalar<DataVector> result{};\n  expansion(make_not_null(&result), grad_normal, inverse_surface_metric,\n            extrinsic_curvature);\n  return result;\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                              \\\n  template void gr::surfaces::expansion<FRAME(data)>(                     \\\n      const gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& grad_normal,            \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& inverse_surface_metric, \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature);   \\\n  template Scalar<DataVector> gr::surfaces::expansion<FRAME(data)>(       \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& grad_normal,            \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& inverse_surface_metric, \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Expansion.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/// @{\n/// \\ingroup SurfacesGroup\n/// \\brief Expansion of a `Strahlkorper`. Should be zero on apparent horizons.\n///\n/// \\details Implements Eq. (5) in \\cite Baumgarte1996hh.  The input argument\n/// `grad_normal` is the quantity returned by\n/// `gr::surfaces::grad_unit_normal_one_form`, and `inverse_surface_metric`\n/// is the quantity returned by `gr::surfaces::inverse_surface_metric`.\ntemplate <typename Frame>\nvoid expansion(gsl::not_null<Scalar<DataVector>*> result,\n               const tnsr::ii<DataVector, 3, Frame>& grad_normal,\n               const tnsr::II<DataVector, 3, Frame>& inverse_surface_metric,\n               const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature);\n\ntemplate <typename Frame>\nScalar<DataVector> expansion(\n    const tnsr::ii<DataVector, 3, Frame>& grad_normal,\n    const tnsr::II<DataVector, 3, Frame>& inverse_surface_metric,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature);\n/// @}\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/ExtrinsicCurvature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/ExtrinsicCurvature.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid extrinsic_curvature(\n    const gsl::not_null<tnsr::ii<DataVector, 3, Frame>*> result,\n    const tnsr::ii<DataVector, 3, Frame>& grad_normal,\n    const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector) {\n  Scalar<DataVector> nI_nJ_gradnij(get<0, 0>(grad_normal).size(), 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      get(nI_nJ_gradnij) += unit_normal_vector.get(i) *\n                            unit_normal_vector.get(j) * grad_normal.get(i, j);\n    }\n  }\n\n  *result = grad_normal;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      result->get(i, j) += unit_normal_one_form.get(i) *\n                           unit_normal_one_form.get(j) * get(nI_nJ_gradnij);\n      for (size_t k = 0; k < 3; ++k) {\n        result->get(i, j) -=\n            unit_normal_vector.get(k) *\n            (unit_normal_one_form.get(i) * grad_normal.get(j, k) +\n             unit_normal_one_form.get(j) * grad_normal.get(i, k));\n      }\n    }\n  }\n}\n\ntemplate <typename Frame>\ntnsr::ii<DataVector, 3, Frame> extrinsic_curvature(\n    const tnsr::ii<DataVector, 3, Frame>& grad_normal,\n    const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector) {\n  tnsr::ii<DataVector, 3, Frame> result{};\n  extrinsic_curvature(make_not_null(&result), grad_normal, unit_normal_one_form,\n                      unit_normal_vector);\n  return result;\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                             \\\n  template void gr::surfaces::extrinsic_curvature<FRAME(data)>(          \\\n      const gsl::not_null<tnsr::ii<DataVector, 3, FRAME(data)>*> result, \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& grad_normal,           \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& unit_normal_one_form,   \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& unit_normal_vector);    \\\n  template tnsr::ii<DataVector, 3, FRAME(data)>                          \\\n  gr::surfaces::extrinsic_curvature<FRAME(data)>(                        \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& grad_normal,           \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& unit_normal_one_form,   \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& unit_normal_vector);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/ExtrinsicCurvature.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/// @{\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Extrinsic curvature of a 2D `Strahlkorper` embedded in a 3D space.\n *\n * \\details Implements Eq. (D.43) of Carroll's Spacetime and Geometry text.\n * Specifically,\n * \\f$ K_{ij} = P^k_i P^l_j \\nabla_{(k} S_{l)} \\f$, where\n * \\f$ P^k_i = \\delta^k_i - S^k S_i \\f$,\n * `grad_normal` is the quantity \\f$ \\nabla_k S_l \\f$ returned by\n * `gr::surfaces::grad_unit_normal_one_form`, and `unit_normal_vector` is\n * \\f$S^i = g^{ij} S_j\\f$ where \\f$S_j\\f$ is the unit normal one form.\n * Not to be confused with the extrinsic curvature of a 3D spatial slice\n * embedded in 3+1 spacetime.\n * Because gr::surfaces::grad_unit_normal_one_form is symmetric, this\n * can be expanded into\n * \\f$ K_{ij} = \\nabla_{i}S_{j} - 2 S^k S_{(i}\\nabla_{j)}S_k\n * + S_i S_j S^k S^l \\nabla_{k} n_{l}\\f$.\n */\ntemplate <typename Frame>\nvoid extrinsic_curvature(\n    gsl::not_null<tnsr::ii<DataVector, 3, Frame>*> result,\n    const tnsr::ii<DataVector, 3, Frame>& grad_normal,\n    const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector);\n\ntemplate <typename Frame>\ntnsr::ii<DataVector, 3, Frame> extrinsic_curvature(\n    const tnsr::ii<DataVector, 3, Frame>& grad_normal,\n    const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector);\n/// @}\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/GradUnitNormalOneForm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/GradUnitNormalOneForm.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid grad_unit_normal_one_form(\n    const gsl::not_null<tnsr::ii<DataVector, 3, Frame>*> result,\n    const tnsr::i<DataVector, 3, Frame>& r_hat,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form,\n    const tnsr::ii<DataVector, 3, Frame>& d2x_radius,\n    const DataVector& one_over_one_form_magnitude,\n    const tnsr::Ijj<DataVector, 3, Frame>& christoffel_2nd_kind) {\n  const DataVector one_over_radius = 1.0 / get(radius);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {  // symmetry\n      result->get(i, j) = -one_over_one_form_magnitude *\n                          (r_hat.get(i) * r_hat.get(j) * one_over_radius +\n                           d2x_radius.get(i, j));\n      for (size_t k = 0; k < 3; ++k) {\n        result->get(i, j) -=\n            unit_normal_one_form.get(k) * christoffel_2nd_kind.get(k, i, j);\n      }\n    }\n    result->get(i, i) += one_over_radius * one_over_one_form_magnitude;\n  }\n}\n\ntemplate <typename Frame>\ntnsr::ii<DataVector, 3, Frame> grad_unit_normal_one_form(\n    const tnsr::i<DataVector, 3, Frame>& r_hat,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form,\n    const tnsr::ii<DataVector, 3, Frame>& d2x_radius,\n    const DataVector& one_over_one_form_magnitude,\n    const tnsr::Ijj<DataVector, 3, Frame>& christoffel_2nd_kind) {\n  tnsr::ii<DataVector, 3, Frame> result{};\n  grad_unit_normal_one_form(make_not_null(&result), r_hat, radius,\n                            unit_normal_one_form, d2x_radius,\n                            one_over_one_form_magnitude, christoffel_2nd_kind);\n  return result;\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                              \\\n  template void gr::surfaces::grad_unit_normal_one_form<FRAME(data)>(     \\\n      gsl::not_null<tnsr::ii<DataVector, 3, FRAME(data)>*> result,        \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat,                   \\\n      const Scalar<DataVector>& radius,                                   \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& unit_normal_one_form,    \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& d2x_radius,             \\\n      const DataVector& one_over_one_form_magnitude,                      \\\n      const tnsr::Ijj<DataVector, 3, FRAME(data)>& christoffel_2nd_kind); \\\n  template tnsr::ii<DataVector, 3, FRAME(data)>                           \\\n  gr::surfaces::grad_unit_normal_one_form<FRAME(data)>(                   \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& r_hat,                   \\\n      const Scalar<DataVector>& radius,                                   \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& unit_normal_one_form,    \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& d2x_radius,             \\\n      const DataVector& one_over_one_form_magnitude,                      \\\n      const tnsr::Ijj<DataVector, 3, FRAME(data)>& christoffel_2nd_kind);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/GradUnitNormalOneForm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/// @{\n/// \\ingroup SurfacesGroup\n/// \\brief Computes 3-covariant gradient \\f$D_i S_j\\f$ of a\n/// Strahlkorper's normal.\n///\n/// \\details See Eqs. (1--9) of \\cite Baumgarte1996hh.\n/// Here \\f$S_j\\f$ is the (normalized) unit one-form to the surface,\n/// and \\f$D_i\\f$ is the spatial covariant derivative.  Note that this\n/// object is symmetric, even though this is not obvious from the\n/// definition.  The input arguments `r_hat`, `radius`, and\n/// `d2x_radius` depend on the Strahlkorper but not on the metric, and\n/// can be computed from a Strahlkorper using ComputeItems in\n/// `ylm::Tags`.  The input argument\n/// `one_over_one_form_magnitude` is \\f$1/\\sqrt{g^{ij}n_i n_j}\\f$,\n/// where \\f$n_i\\f$ is `ylm::Tags::NormalOneForm` (i.e.  the\n/// unnormalized one-form to the Strahlkorper); it can be computed\n/// using (one over) the `magnitude` function.  The input argument\n/// `unit_normal_one_form` is \\f$S_j\\f$,the normalized one-form.\ntemplate <typename Frame>\nvoid grad_unit_normal_one_form(\n    gsl::not_null<tnsr::ii<DataVector, 3, Frame>*> result,\n    const tnsr::i<DataVector, 3, Frame>& r_hat,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form,\n    const tnsr::ii<DataVector, 3, Frame>& d2x_radius,\n    const DataVector& one_over_one_form_magnitude,\n    const tnsr::Ijj<DataVector, 3, Frame>& christoffel_2nd_kind);\n\ntemplate <typename Frame>\ntnsr::ii<DataVector, 3, Frame> grad_unit_normal_one_form(\n    const tnsr::i<DataVector, 3, Frame>& r_hat,\n    const Scalar<DataVector>& radius,\n    const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form,\n    const tnsr::ii<DataVector, 3, Frame>& d2x_radius,\n    const DataVector& one_over_one_form_magnitude,\n    const tnsr::Ijj<DataVector, 3, Frame>& christoffel_2nd_kind);\n/// @}\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/InverseSurfaceMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/InverseSurfaceMetric.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid inverse_surface_metric(\n    const gsl::not_null<tnsr::II<DataVector, 3, Frame>*> result,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n    const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric) {\n  *result = upper_spatial_metric;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {  // Symmetry\n      result->get(i, j) -=\n          unit_normal_vector.get(i) * unit_normal_vector.get(j);\n    }\n  }\n}\n\ntemplate <typename Frame>\ntnsr::II<DataVector, 3, Frame> inverse_surface_metric(\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n    const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric) {\n  tnsr::II<DataVector, 3, Frame> result{};\n  inverse_surface_metric(make_not_null(&result), unit_normal_vector,\n                         upper_spatial_metric);\n  return result;\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                             \\\n  template void gr::surfaces::inverse_surface_metric<FRAME(data)>(       \\\n      const gsl::not_null<tnsr::II<DataVector, 3, FRAME(data)>*> result, \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& unit_normal_vector,     \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& upper_spatial_metric); \\\n  template tnsr::II<DataVector, 3, FRAME(data)>                          \\\n  gr::surfaces::inverse_surface_metric<FRAME(data)>(                     \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& unit_normal_vector,     \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& upper_spatial_metric);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/InverseSurfaceMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/// @{\n/// \\ingroup SurfacesGroup\n/// \\brief Computes inverse 2-metric \\f$g^{ij}-S^i S^j\\f$ of a Strahlkorper.\n///\n/// \\details See Eqs. (1--9) of \\cite Baumgarte1996hh.\n/// Here \\f$S^i\\f$ is the (normalized) unit vector to the surface,\n/// and \\f$g^{ij}\\f$ is the 3-metric.  This object is expressed in the\n/// usual 3-d Cartesian basis, so it is written as a 3-dimensional tensor.\n/// But because it is orthogonal to \\f$S_i\\f$, it has only 3 independent\n/// degrees of freedom, and could be expressed as a 2-d tensor with an\n/// appropriate choice of basis. The input argument `unit_normal_vector` is\n/// \\f$S^i = g^{ij} S_j\\f$, where \\f$S_j\\f$ is the unit normal one form.\ntemplate <typename Frame>\nvoid inverse_surface_metric(\n    gsl::not_null<tnsr::II<DataVector, 3, Frame>*> result,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n    const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric);\n\ntemplate <typename Frame>\ntnsr::II<DataVector, 3, Frame> inverse_surface_metric(\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n    const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric);\n/// @}\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Mass.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Mass.hpp\"\n\n#include <cmath>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace gr::surfaces {\ndouble irreducible_mass(const double area) {\n  ASSERT(area > 0.0,\n         \"The area of the horizon must be greater than zero but is \" << area);\n  return sqrt(area / (16.0 * M_PI));\n}\n\ndouble christodoulou_mass(const double dimensionful_spin_magnitude,\n                          const double irreducible_mass) {\n  return sqrt(square(irreducible_mass) + (square(dimensionful_spin_magnitude) /\n                                          (4.0 * square(irreducible_mass))));\n}\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Mass.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace gr::surfaces {\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Irreducible mass of a 2D `Strahlkorper`.\n *\n * \\details See Eqs. (15.38) \\cite Hartle2003gravity. This function computes the\n * irreducible mass from the area of a horizon. Specifically, computes\n * \\f$M_\\mathrm{irr}=\\sqrt{\\frac{A}{16\\pi}}\\f$.\n */\ndouble irreducible_mass(double area);\n\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Christodoulou Mass of a 2D `Strahlkorper`.\n *\n * \\details See e.g. Eq. (1) of \\cite Lovelace2016uwp.\n * This function computes the Christodoulou mass from the dimensionful\n * spin angular momentum \\f$S\\f$ and the irreducible mass \\f$M_{irr}\\f$\n * of a black hole horizon. Specifically, computes\n *\\f$M=\\sqrt{M_{irr}^2+\\frac{S^2}{4M_{irr}^2}}\\f$\n */\ndouble christodoulou_mass(double dimensionful_spin_magnitude,\n                          double irreducible_mass);\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace gr::surfaces::py_bindings {\n\ntemplate <typename Frame>\nvoid bind_horizon_quantities_impl(py::module& m) {\n  static constexpr size_t Dim = 3;\n  m.def(\n      \"horizon_quantities\",\n      [](ylm::Strahlkorper<Frame> horizon,\n         tnsr::ii<DataVector, Dim, Frame> spatial_metric,\n         tnsr::II<DataVector, Dim, Frame> inv_spatial_metric,\n         tnsr::ii<DataVector, Dim, Frame> extrinsic_curvature,\n         tnsr::Ijj<DataVector, Dim, Frame> spatial_christoffel_second_kind,\n         tnsr::ii<DataVector, Dim, Frame> spatial_ricci) -> py::dict {\n        const auto box = db::create<\n            tmpl::append<tmpl::list<ylm::Tags::Strahlkorper<Frame>>,\n                         ::ah::vars_to_interpolate_to_target<Dim, Frame>>,\n            tmpl::push_back<::ah::compute_items_on_target<Dim, Frame>,\n                            gr::surfaces::Tags::DimensionlessSpinVectorCompute<\n                                Frame, Frame>>>(\n            std::move(horizon), std::move(spatial_metric),\n            std::move(inv_spatial_metric), std::move(extrinsic_curvature),\n            std::move(spatial_christoffel_second_kind),\n            std::move(spatial_ricci));\n        py::dict result{};\n        result[\"Area\"] = db::get<gr::surfaces::Tags::Area>(box);\n        result[\"IrreducibleMass\"] =\n            db::get<gr::surfaces::Tags::IrreducibleMass>(box);\n        result[\"MaxRicciScalar\"] = db::get<ylm::Tags::MaxRicciScalar>(box);\n        result[\"MinRicciScalar\"] = db::get<ylm::Tags::MinRicciScalar>(box);\n        result[\"ChristodoulouMass\"] =\n            db::get<gr::surfaces::Tags::ChristodoulouMass>(box);\n        result[\"DimensionlessSpinMagnitude\"] =\n            db::get<gr::surfaces::Tags::DimensionlessSpinMagnitude<Frame>>(box);\n        result[\"DimensionlessSpinVector\"] =\n            db::get<gr::surfaces::Tags::DimensionlessSpinVector<Frame>>(box);\n        return result;\n      },\n      py::arg(\"horizon\"), py::arg(\"spatial_metric\"),\n      py::arg(\"inv_spatial_metric\"), py::arg(\"extrinsic_curvature\"),\n      py::arg(\"spatial_christoffel_second_kind\"), py::arg(\"spatial_ricci\"));\n}\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  py::module_::import(\"spectre.SphericalHarmonics\");\n  bind_horizon_quantities_impl<Frame::Inertial>(m);\n}\n\n}  // namespace gr::surfaces::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyGrSurfaces\")\n\nspectre_python_add_module(\n  Surfaces\n  LIBRARY_NAME ${LIBRARY}\n  MODULE_PATH \"PointwiseFunctions/GeneralRelativity\"\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n)\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ErrorHandling\n  GrSurfaces\n  pybind11::module\n  SphericalHarmonics\n  Utilities\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PySphericalHarmonics\n  PyTensor\n)\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/RadialDistance.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/RadialDistance.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid radial_distance(const gsl::not_null<Scalar<DataVector>*> radial_distance,\n                     const ylm::Strahlkorper<Frame>& strahlkorper_a,\n                     const ylm::Strahlkorper<Frame>& strahlkorper_b) {\n  if (strahlkorper_a.expansion_center() != strahlkorper_b.expansion_center()) {\n    ERROR(\n        \"Currently computing the radial distance between two Strahlkorpers \"\n        \"is only supported if they have the same centers, but the \"\n        \"strahlkorpers provided have centers \"\n        << strahlkorper_a.expansion_center() << \" and \"\n        << strahlkorper_b.expansion_center());\n  }\n  const auto physical_size_a = strahlkorper_a.ylm_spherepack().physical_size();\n  const auto physical_size_b = strahlkorper_b.ylm_spherepack().physical_size();\n  if (strahlkorper_a.l_max() == strahlkorper_b.l_max() and\n      strahlkorper_a.m_max() == strahlkorper_b.m_max()) {\n    if (UNLIKELY(get(*radial_distance).size() != physical_size_a)) {\n      ERROR(\"radial_distance has size \"\n            << get(*radial_distance).size()\n            << \" but both Strahlkorpers have physical_size \" << physical_size_a\n            << \". Provide an output of the correct size.\");\n    }\n    get(*radial_distance) =\n        get(ylm::radius(strahlkorper_a)) - get(ylm::radius(strahlkorper_b));\n  } else if (strahlkorper_a.l_max() > strahlkorper_b.l_max() or\n             (strahlkorper_a.l_max() == strahlkorper_b.l_max() and\n              strahlkorper_a.m_max() > strahlkorper_b.m_max())) {\n    if (UNLIKELY(get(*radial_distance).size() != physical_size_a)) {\n      ERROR(\"radial_distance has size \"\n            << get(*radial_distance).size()\n            << \" but Strahlkorper a has higher resolution with physical_size \"\n            << physical_size_a\n            << \". Provide an output matching the higher-resolution surface.\");\n    }\n    get(*radial_distance) =\n        get(ylm::radius(strahlkorper_a)) -\n        get(ylm::radius(ylm::Strahlkorper<Frame>(\n            strahlkorper_a.l_max(), strahlkorper_a.m_max(), strahlkorper_b)));\n  } else {\n    if (UNLIKELY(get(*radial_distance).size() != physical_size_b)) {\n      ERROR(\"radial_distance has size \"\n            << get(*radial_distance).size()\n            << \" but Strahlkorper b has higher resolution with physical_size \"\n            << physical_size_b\n            << \". Provide an output matching the higher-resolution surface.\");\n    }\n    get(*radial_distance) =\n        -get(ylm::radius(strahlkorper_b)) +\n        get(ylm::radius(ylm::Strahlkorper<Frame>(\n            strahlkorper_b.l_max(), strahlkorper_b.m_max(), strahlkorper_a)));\n  };\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                    \\\n  template void gr::surfaces::radial_distance<FRAME(data)>(     \\\n      const gsl::not_null<Scalar<DataVector>*> radial_distance, \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper_a,     \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper_b);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/RadialDistance.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Radial distance between two `Strahlkorper`s.\n *\n * \\details Computes the pointwise radial distance \\f$r_a-r_b\\f$ between two\n * Strahlkorpers `strahlkorper_a` and `strahlkorper_b` that have the same\n * center. If the Strahlkorpers' resolutions are unequal, the lower-resolution\n * Strahlkorper is internally prolonged to match the higher-resolution\n * Strahlkorper. The `radial_distance` DataVector must already have size\n * consistent with the higher-resolution Strahlkorper's resolution (or the\n * common size when the resolutions match); a mismatch triggers an error.\n */\ntemplate <typename Frame>\nvoid radial_distance(gsl::not_null<Scalar<DataVector>*> radial_distance,\n                     const ylm::Strahlkorper<Frame>& strahlkorper_a,\n                     const ylm::Strahlkorper<Frame>& strahlkorper_b);\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/RicciScalar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/RicciScalar.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid ricci_scalar(const gsl::not_null<Scalar<DataVector>*> result,\n                  const tnsr::ii<DataVector, 3, Frame>& spatial_ricci_tensor,\n                  const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n                  const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n                  const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric) {\n  trace(result, spatial_ricci_tensor, upper_spatial_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      get(*result) -= 2.0 * spatial_ricci_tensor.get(i, j) *\n                      unit_normal_vector.get(i) * unit_normal_vector.get(j);\n\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t l = 0; l < 3; ++l) {\n          // K^{ij} K_{ij} = g^{ik} g^{jl} K_{kl} K_{ij}\n          get(*result) -=\n              upper_spatial_metric.get(i, k) * upper_spatial_metric.get(j, l) *\n              extrinsic_curvature.get(k, l) * extrinsic_curvature.get(i, j);\n        }\n      }\n    }\n  }\n  get(*result) += square(get(trace(extrinsic_curvature, upper_spatial_metric)));\n}\n\ntemplate <typename Frame>\nScalar<DataVector> ricci_scalar(\n    const tnsr::ii<DataVector, 3, Frame>& spatial_ricci_tensor,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric) {\n  Scalar<DataVector> result{};\n  ricci_scalar(make_not_null(&result), spatial_ricci_tensor, unit_normal_vector,\n               extrinsic_curvature, upper_spatial_metric);\n  return result;\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                             \\\n  template void gr::surfaces::ricci_scalar<FRAME(data)>(                 \\\n      const gsl::not_null<Scalar<DataVector>*> result,                   \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_ricci_tensor,  \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& unit_normal_vector,     \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature,   \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& upper_spatial_metric); \\\n  template Scalar<DataVector> gr::surfaces::ricci_scalar<FRAME(data)>(   \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_ricci_tensor,  \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& unit_normal_vector,     \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature,   \\\n      const tnsr::II<DataVector, 3, FRAME(data)>& upper_spatial_metric);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/RicciScalar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/// @{\n/// \\ingroup SurfacesGroup\n/// \\brief Intrinsic Ricci scalar of a 2D `Strahlkorper`.\n///\n/// \\details Implements Eq. (D.51) of\n/// Sean Carroll's Spacetime and Geometry textbook (except correcting\n/// sign errors: both extrinsic curvature terms are off by a minus sign\n/// in Carroll's text but correct in Carroll's errata).\n/// \\f$ \\hat{R}=R - 2 R_{ij} S^i S^j + K^2-K^{ij}K_{ij}.\\f$\n/// Here \\f$\\hat{R}\\f$ is the intrinsic Ricci scalar curvature of\n/// the Strahlkorper, \\f$R\\f$ and \\f$R_{ij}\\f$ are the Ricci scalar and\n/// Ricci tensor of the 3D space that contains the Strahlkorper,\n/// \\f$ K_{ij} \\f$ the output of gr::surfaces::extrinsic_curvature,\n/// \\f$ K \\f$ is the trace of \\f$K_{ij}\\f$,\n/// and `unit_normal_vector` is\n/// \\f$S^i = g^{ij} S_j\\f$ where \\f$S_j\\f$ is the unit normal one form.\ntemplate <typename Frame>\nvoid ricci_scalar(gsl::not_null<Scalar<DataVector>*> result,\n                  const tnsr::ii<DataVector, 3, Frame>& spatial_ricci_tensor,\n                  const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n                  const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n                  const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric);\n\ntemplate <typename Frame>\nScalar<DataVector> ricci_scalar(\n    const tnsr::ii<DataVector, 3, Frame>& spatial_ricci_tensor,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature,\n    const tnsr::II<DataVector, 3, Frame>& upper_spatial_metric);\n/// @}\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Spin.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Spin.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/LinearAlgebra/FindGeneralizedEigenvalues.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\n// Functions used by gr::surfaces::dimensionful_spin_magnitude\nnamespace {\n// Find the 2D surface metric by inserting the tangents \\f$\\partial_\\theta\\f$\n// and \\f$\\partial_\\phi\\f$ into the slots of the 3D spatial metric\ntemplate <typename Fr>\ntnsr::ii<DataVector, 2, Frame::Spherical<Fr>> get_surface_metric(\n    const tnsr::ii<DataVector, 3, Fr>& spatial_metric,\n    const ylm::Tags::aliases::Jacobian<Fr>& tangents,\n    const Scalar<DataVector>& sin_theta) {\n  auto surface_metric =\n      make_with_value<tnsr::ii<DataVector, 2, Frame::Spherical<Fr>>>(\n          get<0, 0>(spatial_metric), 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      get<0, 1>(surface_metric) += spatial_metric.get(i, j) *\n                                   tangents.get(i, 0) * tangents.get(j, 1) *\n                                   get(sin_theta);\n    }\n    // Use symmetry to sum over fewer terms for the 0,0 and 1,1 components\n    get<0, 0>(surface_metric) +=\n        spatial_metric.get(i, i) * square(tangents.get(i, 0));\n    get<1, 1>(surface_metric) += spatial_metric.get(i, i) *\n                                 square(tangents.get(i, 1)) *\n                                 square(get(sin_theta));\n    for (size_t j = i + 1; j < 3; ++j) {\n      get<0, 0>(surface_metric) += 2.0 * spatial_metric.get(i, j) *\n                                   tangents.get(i, 0) * tangents.get(j, 0);\n      get<1, 1>(surface_metric) += 2.0 * spatial_metric.get(i, j) *\n                                   tangents.get(i, 1) * tangents.get(j, 1) *\n                                   square(get(sin_theta));\n    }\n  }\n  return surface_metric;\n}\n\n// Compute the trace of Christoffel 2nd kind on the horizon\ntemplate <typename Fr>\ntnsr::I<DataVector, 2, Frame::Spherical<Fr>> get_trace_christoffel_second_kind(\n    const tnsr::ii<DataVector, 2, Frame::Spherical<Fr>>& surface_metric,\n    const tnsr::II<DataVector, 2, Frame::Spherical<Fr>>& inverse_surface_metric,\n    const Scalar<DataVector>& sin_theta, const ylm::Spherepack& ylm) {\n  const Scalar<DataVector> cos_theta{cos(ylm.theta_phi_points()[0])};\n\n  // Because the surface metric components are not representable in terms\n  // of scalar spherical harmonics, you can't naively take first derivatives.\n  // To avoid potentially large numerical errors, actually differentiate\n  // square(sin_theta) * the metric component, then\n  // compute from that the gradient of just the metric component itself.\n  //\n  // Note: the method implemented here works with ylm::Spherepack but will\n  // fail for other expansions that, unlike Spherepack, include a collocation\n  // point at theta = 0. Before switching to such an expansion, first\n  // reimplement this code to avoid dividing by sin(theta).\n  //\n  // Note: ylm::Spherepack gradients are flat-space Pfaffian derivatives.\n  auto grad_surface_metric_theta_theta =\n      ylm.gradient(square(get(sin_theta)) * get<0, 0>(surface_metric));\n  get<0>(grad_surface_metric_theta_theta) /= square(get(sin_theta));\n  get<1>(grad_surface_metric_theta_theta) /= square(get(sin_theta));\n  get<0>(grad_surface_metric_theta_theta) -=\n      2.0 * get<0, 0>(surface_metric) * get(cos_theta) / get(sin_theta);\n\n  auto grad_surface_metric_theta_phi =\n      ylm.gradient(get(sin_theta) * get<0, 1>(surface_metric));\n\n  get<0>(grad_surface_metric_theta_phi) /= get(sin_theta);\n  get<1>(grad_surface_metric_theta_phi) /= get(sin_theta);\n  get<0>(grad_surface_metric_theta_phi) -=\n      get<0, 1>(surface_metric) * get(cos_theta) / get(sin_theta);\n\n  auto grad_surface_metric_phi_phi = ylm.gradient(get<1, 1>(surface_metric));\n\n  auto deriv_surface_metric =\n      make_with_value<tnsr::ijj<DataVector, 2, Frame::Spherical<Fr>>>(\n          get<0, 0>(surface_metric), 0.0);\n  // Get the partial derivative of the metric from the Pfaffian derivative\n  get<0, 0, 0>(deriv_surface_metric) = get<0>(grad_surface_metric_theta_theta);\n  get<1, 0, 0>(deriv_surface_metric) =\n      get(sin_theta) * get<1>(grad_surface_metric_theta_theta);\n  get<0, 0, 1>(deriv_surface_metric) = get<0>(grad_surface_metric_theta_phi);\n  get<1, 0, 1>(deriv_surface_metric) =\n      get(sin_theta) * get<1>(grad_surface_metric_theta_phi);\n  get<0, 1, 1>(deriv_surface_metric) = get<0>(grad_surface_metric_phi_phi);\n  get<1, 1, 1>(deriv_surface_metric) =\n      get(sin_theta) * get<1>(grad_surface_metric_phi_phi);\n\n  return trace_last_indices(\n      raise_or_lower_first_index(\n          gr::christoffel_first_kind(deriv_surface_metric),\n          inverse_surface_metric),\n      inverse_surface_metric);\n}\n\n// I'm going to solve a general eigenvalue problem of the form\n// A x = lambda B x, where A and B are NxN, where N is the\n// number of elements with l > 0 and l < ntheta - 2,\n// i.e. l < l_max + 1 - 2 = l_max - 1. This function computes N.\nsize_t get_matrix_dimension(const ylm::Spherepack& ylm) {\n  // If l_max == m_max, there are square(l_max+1) Ylms total\n  size_t matrix_dimension = square(ylm.l_max() + 1);\n  // If l_max > m_max, there are\n  // (l_max - m_max) * (l_max - m_max + 1) fewer Ylms total\n  matrix_dimension -=\n      (ylm.l_max() - ylm.m_max()) * (ylm.l_max() - ylm.m_max() + 1);\n  // The actual matrix dimension is smaller, because we do not count\n  // Ylms with l == 0, l == l_max, or l == l_max - 1.\n  matrix_dimension -= 4 * ylm.m_max() + 3;\n  if (ylm.l_max() == ylm.m_max()) {\n    matrix_dimension += 2;\n  }\n  return matrix_dimension;\n}\n\n// Get left matrix A and right matrix B for eigenproblem A x = lambda B x.\ntemplate <typename Fr>\nvoid get_left_and_right_eigenproblem_matrices(\n    const gsl::not_null<Matrix*> left_matrix,\n    const gsl::not_null<Matrix*> right_matrix,\n    const tnsr::II<DataVector, 2, Frame::Spherical<Fr>>& inverse_surface_metric,\n    const tnsr::I<DataVector, 2, Frame::Spherical<Fr>>&\n        trace_christoffel_second_kind,\n    const Scalar<DataVector>& sin_theta, const Scalar<DataVector>& ricci_scalar,\n    const ylm::Spherepack& ylm) {\n  const auto grad_ricci_scalar = ylm.gradient(get(ricci_scalar));\n  // loop over all terms with 0<l<l_max-1: each makes a column of\n  // the matrices for the eigenvalue problem\n  size_t column = 0;  // number which column of the matrix we are filling\n  for (auto iter_i = ylm::SpherepackIterator(ylm.l_max(), ylm.m_max()); iter_i;\n       ++iter_i) {\n    if (iter_i.l() > 0 and iter_i.l() < ylm.l_max() - 1 and\n        iter_i.m() <= iter_i.l()) {\n      // Make a spectral vector that's all zeros except for one element,\n      // which is 1. This corresponds to the ith Ylm, which I call yi.\n      DataVector yi_spectral(ylm.spectral_size(), 0.0);\n      yi_spectral[iter_i()] = 1.0;\n\n      // Transform column vector corresponding to\n      // a specific Y_lm to physical space.\n      const DataVector yi_physical = ylm.spec_to_phys(yi_spectral);\n\n      // In physical space, numerically compute the\n      // linear differential operators acting on the\n      // ith Y_lm.\n\n      // \\nabla^2 Y_lm\n      const auto derivs_yi = ylm.first_and_second_derivative(yi_physical);\n      auto laplacian_yi =\n          make_with_value<Scalar<DataVector>>(ricci_scalar, 0.0);\n      get(laplacian_yi) +=\n          get<0, 0>(derivs_yi.second) * get<0, 0>(inverse_surface_metric);\n      get(laplacian_yi) += 2.0 * get<1, 0>(derivs_yi.second) *\n                           get<1, 0>(inverse_surface_metric) * get(sin_theta);\n      get(laplacian_yi) += get<1, 1>(derivs_yi.second) *\n                           get<1, 1>(inverse_surface_metric) *\n                           square(get(sin_theta));\n      get(laplacian_yi) -=\n          get<0>(derivs_yi.first) * get<0>(trace_christoffel_second_kind);\n      get(laplacian_yi) -= get<1>(derivs_yi.first) * get(sin_theta) *\n                           get<1>(trace_christoffel_second_kind);\n\n      // \\nabla^4 Y_lm\n      const auto derivs_laplacian_yi =\n          ylm.first_and_second_derivative(get(laplacian_yi));\n      auto laplacian_squared_yi =\n          make_with_value<Scalar<DataVector>>(ricci_scalar, 0.0);\n      get(laplacian_squared_yi) += get<0, 0>(derivs_laplacian_yi.second) *\n                                   get<0, 0>(inverse_surface_metric);\n      get(laplacian_squared_yi) += 2.0 * get<1, 0>(derivs_laplacian_yi.second) *\n                                   get<1, 0>(inverse_surface_metric) *\n                                   get(sin_theta);\n      get(laplacian_squared_yi) += get<1, 1>(derivs_laplacian_yi.second) *\n                                   get<1, 1>(inverse_surface_metric) *\n                                   square(get(sin_theta));\n      get(laplacian_squared_yi) -= get<0>(derivs_laplacian_yi.first) *\n                                   get<0>(trace_christoffel_second_kind);\n      get(laplacian_squared_yi) -= get<1>(derivs_laplacian_yi.first) *\n                                   get(sin_theta) *\n                                   get<1>(trace_christoffel_second_kind);\n\n      // \\nabla R \\cdot \\nabla Y_lm\n      auto grad_ricci_scalar_dot_grad_yi =\n          make_with_value<Scalar<DataVector>>(ricci_scalar, 0.0);\n      get(grad_ricci_scalar_dot_grad_yi) += get<0>(derivs_yi.first) *\n                                            get<0>(grad_ricci_scalar) *\n                                            get<0, 0>(inverse_surface_metric);\n      get(grad_ricci_scalar_dot_grad_yi) +=\n          get<0>(derivs_yi.first) * get<1>(grad_ricci_scalar) *\n          get<1, 0>(inverse_surface_metric) * get(sin_theta);\n      get(grad_ricci_scalar_dot_grad_yi) +=\n          get<1>(derivs_yi.first) * get<0>(grad_ricci_scalar) *\n          get<1, 0>(inverse_surface_metric) * get(sin_theta);\n      get(grad_ricci_scalar_dot_grad_yi) +=\n          get<1>(derivs_yi.first) * get<1>(grad_ricci_scalar) *\n          get<1, 1>(inverse_surface_metric) * square(get(sin_theta));\n\n      // Assemble the operator making up the eigenproblem's left-hand-side\n      auto left_matrix_yi_physical =\n          make_with_value<Scalar<DataVector>>(ricci_scalar, 0.0);\n      get(left_matrix_yi_physical) = get(laplacian_squared_yi) +\n                                     get(ricci_scalar) * get(laplacian_yi) +\n                                     get(grad_ricci_scalar_dot_grad_yi);\n\n      // Transform back to spectral space, to get one column each for the left\n      // and right matrices for the eigenvalue problem.\n      const DataVector left_matrix_yi_spectral =\n          ylm.phys_to_spec(get(left_matrix_yi_physical));\n      const DataVector right_matrix_yi_spectral =\n          ylm.phys_to_spec(get(laplacian_yi));\n\n      // Set the current column of the left and right matrices\n      // for the eigenproblem.\n      size_t row = 0;\n      for (auto iter_j = ylm::SpherepackIterator(ylm.l_max(), ylm.m_max());\n           iter_j; ++iter_j) {\n        if (iter_j.l() > 0 and iter_j.l() < ylm.l_max() - 1) {\n          (*left_matrix)(row, column) = left_matrix_yi_spectral[iter_j()];\n          (*right_matrix)(row, column) = right_matrix_yi_spectral[iter_j()];\n          ++row;\n        }\n      }  // loop over rows\n      ++column;\n    }\n  }  // loop over columns\n}\n\n// Find the eigenvectors corresponding to the three smallest-magnitude\n// eigenvalues.\n// Note: uses the fact that eigenvalues should be real\nstd::array<DataVector, 3> get_eigenvectors_for_3_smallest_magnitude_eigenvalues(\n    const DataVector& eigenvalues_real_part, const Matrix& eigenvectors,\n    const ylm::Spherepack& ylm) {\n  size_t index_smallest = 0;\n  size_t index_second_smallest = 0;\n  size_t index_third_smallest = 0;\n\n  // Simple algorithm that loops over all elements to\n  // find indexes of 3 smallest-magnitude eigenvalues\n  for (size_t i = 1; i < eigenvalues_real_part.size(); ++i) {\n    if (abs(eigenvalues_real_part[i]) <\n        abs(eigenvalues_real_part[index_smallest])) {\n      index_third_smallest = index_second_smallest;\n      index_second_smallest = index_smallest;\n      index_smallest = i;\n    } else if (i < 2 or abs(eigenvalues_real_part[i]) <\n                            abs(eigenvalues_real_part[index_second_smallest])) {\n      index_third_smallest = index_second_smallest;\n      index_second_smallest = i;\n    } else if (i < 3 or abs(eigenvalues_real_part[i]) <\n                            abs(eigenvalues_real_part[index_third_smallest])) {\n      index_third_smallest = i;\n    }\n  }\n\n  DataVector smallest_eigenvector(ylm.spectral_size(), 0.0);\n  DataVector second_smallest_eigenvector(ylm.spectral_size(), 0.0);\n  DataVector third_smallest_eigenvector(ylm.spectral_size(), 0.0);\n\n  size_t row = 0;\n\n  for (auto iter_i = ylm::SpherepackIterator(ylm.l_max(), ylm.m_max()); iter_i;\n       ++iter_i) {\n    if (iter_i.l() > 0 and iter_i.l() < ylm.l_max() - 1) {\n      smallest_eigenvector[iter_i()] = eigenvectors(row, index_smallest);\n      second_smallest_eigenvector[iter_i()] =\n          eigenvectors(row, index_second_smallest);\n      third_smallest_eigenvector[iter_i()] =\n          eigenvectors(row, index_third_smallest);\n      ++row;\n    }\n  }\n\n  return {{smallest_eigenvector, second_smallest_eigenvector,\n           third_smallest_eigenvector}};\n}\n\n// This function converts the three eigenvectors with smallest-magnitude\n// eigenvalues to physical space to get the spin potentials corresponding to\n// the approximate Killing vectors. The potentials are normalized using the\n// \"Kerr normalization:\" the integral of (potential - the potential average)^2\n// is set to (horizon area)^3/(48*pi), as it is for Kerr.\nstd::array<DataVector, 3> get_normalized_spin_potentials(\n    const std::array<DataVector, 3>& eigenvectors_for_potentials,\n    const ylm::Spherepack& ylm, const Scalar<DataVector>& area_element) {\n  const double area = ylm.definite_integral(get(area_element).data());\n\n  std::array<DataVector, 3> potentials;\n\n  DataVector temp_integrand(get(area_element));\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(potentials, i) =\n        ylm.spec_to_phys(gsl::at(eigenvectors_for_potentials, i));\n\n    temp_integrand = gsl::at(potentials, i) * get(area_element);\n    const double potential_average =\n        ylm.definite_integral(temp_integrand.data()) / area;\n\n    temp_integrand =\n        square(gsl::at(potentials, i) - potential_average) * get(area_element);\n    const double potential_norm = ylm.definite_integral(temp_integrand.data());\n    gsl::at(potentials, i) *=\n        sqrt(cube(area) / (48.0 * square(M_PI) * potential_norm));\n  }\n  return potentials;\n}\n\n// Get the spin magnitude. There are three potentials, each corresponding to an\n// approximate Killing vector. The spin for each potential is the surface\n// integral of the potential times the spin function. The spin magnitude\n// is the Euclidean norm of the spin for each potential.\ndouble get_spin_magnitude(const std::array<DataVector, 3>& potentials,\n                          const Scalar<DataVector>& spin_function,\n                          const Scalar<DataVector>& area_element,\n                          const ylm::Spherepack& ylm) {\n  double spin_magnitude_squared = 0.0;\n\n  DataVector spin_density(get(area_element));\n  for (size_t i = 0; i < 3; ++i) {\n    spin_density =\n        gsl::at(potentials, i) * get(spin_function) * get(area_element);\n    spin_magnitude_squared +=\n        square(ylm.definite_integral(spin_density.data()) / (8.0 * M_PI));\n  }\n  return sqrt(spin_magnitude_squared);\n}\n}  // namespace\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid spin_function(const gsl::not_null<Scalar<DataVector>*> result,\n                   const ylm::Tags::aliases::Jacobian<Frame>& tangents,\n                   const ylm::Strahlkorper<Frame>& strahlkorper,\n                   const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n                   const Scalar<DataVector>& area_element,\n                   const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature) {\n  set_number_of_grid_points(result, area_element);\n  for (auto& component : *result) {\n    component = 0.0;\n  }\n\n  auto extrinsic_curvature_theta_normal_sin_theta =\n      make_with_value<Scalar<DataVector>>(area_element, 0.0);\n  auto extrinsic_curvature_phi_normal =\n      make_with_value<Scalar<DataVector>>(area_element, 0.0);\n\n  // using result as temporary\n  DataVector& extrinsic_curvature_dot_normal = get(*result);\n  for (size_t i = 0; i < 3; ++i) {\n    extrinsic_curvature_dot_normal =\n        extrinsic_curvature.get(i, 0) * get<0>(unit_normal_vector);\n    for (size_t j = 1; j < 3; ++j) {\n      extrinsic_curvature_dot_normal +=\n          extrinsic_curvature.get(i, j) * unit_normal_vector.get(j);\n    }\n\n    // Note: I must multiply by sin_theta because\n    // I take the phi derivative of this term by using\n    // the spherepack gradient, which includes a\n    // sin_theta in the denominator of the phi derivative.\n    // Will do this outside the i,j loops.\n    get(extrinsic_curvature_theta_normal_sin_theta) +=\n        extrinsic_curvature_dot_normal * tangents.get(i, 0);\n\n    // Note: I must multiply by sin_theta because tangents.get(i,1)\n    // actually contains \\partial_\\phi / sin(theta), but I want just\n    //\\partial_\\phi. Will do this outside the i,j loops.\n    get(extrinsic_curvature_phi_normal) +=\n        extrinsic_curvature_dot_normal * tangents.get(i, 1);\n  }\n\n  // using result as temporary\n  DataVector& sin_theta = get(*result);\n  sin_theta = sin(strahlkorper.ylm_spherepack().theta_phi_points()[0]);\n  get(extrinsic_curvature_theta_normal_sin_theta) *= sin_theta;\n  get(extrinsic_curvature_phi_normal) *= sin_theta;\n\n  // now computing actual result\n  get(*result) = (get<0>(strahlkorper.ylm_spherepack().gradient(\n                      get(extrinsic_curvature_phi_normal))) -\n                  get<1>(strahlkorper.ylm_spherepack().gradient(\n                      get(extrinsic_curvature_theta_normal_sin_theta)))) /\n                 (sin_theta * get(area_element));\n}\n\ntemplate <typename Frame>\nScalar<DataVector> spin_function(\n    const ylm::Tags::aliases::Jacobian<Frame>& tangents,\n    const ylm::Strahlkorper<Frame>& strahlkorper,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n    const Scalar<DataVector>& area_element,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature) {\n  Scalar<DataVector> result{};\n  spin_function(make_not_null(&result), tangents, strahlkorper,\n                unit_normal_vector, area_element, extrinsic_curvature);\n  return result;\n}\n\ntemplate <typename Frame>\nvoid dimensionful_spin_magnitude(\n    const gsl::not_null<double*> result, const Scalar<DataVector>& ricci_scalar,\n    const Scalar<DataVector>& spin_function,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const ylm::Tags::aliases::Jacobian<Frame>& tangents,\n    const ylm::Strahlkorper<Frame>& strahlkorper,\n    const Scalar<DataVector>& area_element) {\n  const Scalar<DataVector> sin_theta{\n      sin(strahlkorper.ylm_spherepack().theta_phi_points()[0])};\n\n  const auto surface_metric =\n      get_surface_metric(spatial_metric, tangents, sin_theta);\n  const auto inverse_surface_metric =\n      determinant_and_inverse(surface_metric).second;\n  const auto trace_christoffel_second_kind = get_trace_christoffel_second_kind(\n      surface_metric, inverse_surface_metric, sin_theta,\n      strahlkorper.ylm_spherepack());\n\n  const size_t matrix_dimension =\n      get_matrix_dimension(strahlkorper.ylm_spherepack());\n  Matrix left_matrix(matrix_dimension, matrix_dimension, 0.0);\n  Matrix right_matrix(matrix_dimension, matrix_dimension, 0.0);\n  get_left_and_right_eigenproblem_matrices(\n      &left_matrix, &right_matrix, inverse_surface_metric,\n      trace_christoffel_second_kind, sin_theta, ricci_scalar,\n      strahlkorper.ylm_spherepack());\n\n  DataVector eigenvalues_real_part(matrix_dimension, 0.0);\n  DataVector eigenvalues_im_part(matrix_dimension, 0.0);\n  Matrix eigenvectors(matrix_dimension, matrix_dimension, 0.0);\n  find_generalized_eigenvalues(&eigenvalues_real_part, &eigenvalues_im_part,\n                               &eigenvectors, left_matrix, right_matrix);\n\n  const std::array<DataVector, 3> smallest_eigenvectors =\n      get_eigenvectors_for_3_smallest_magnitude_eigenvalues(\n          eigenvalues_real_part, eigenvectors, strahlkorper.ylm_spherepack());\n\n  // Get normalized potentials (Kerr normalization) corresponding to the\n  // eigenvectors with three smallest-magnitude eigenvalues.\n  const auto potentials = get_normalized_spin_potentials(\n      smallest_eigenvectors, strahlkorper.ylm_spherepack(), area_element);\n\n  *result = get_spin_magnitude(potentials, spin_function, area_element,\n                               strahlkorper.ylm_spherepack());\n}\n\ntemplate <typename Frame>\ndouble dimensionful_spin_magnitude(\n    const Scalar<DataVector>& ricci_scalar,\n    const Scalar<DataVector>& spin_function,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const ylm::Tags::aliases::Jacobian<Frame>& tangents,\n    const ylm::Strahlkorper<Frame>& strahlkorper,\n    const Scalar<DataVector>& area_element) {\n  double result{};\n  dimensionful_spin_magnitude(make_not_null(&result), ricci_scalar,\n                              spin_function, spatial_metric, tangents,\n                              strahlkorper, area_element);\n  return result;\n}\n\nvoid dimensionless_spin_magnitude(const gsl::not_null<double*> result,\n                                  const double dimensionful_spin_magnitude,\n                                  const double christodoulou_mass) {\n  *result = dimensionful_spin_magnitude / square(christodoulou_mass);\n}\n\ndouble dimensionless_spin_magnitude(const double dimensionful_spin_magnitude,\n                                    const double christodoulou_mass) {\n  double result{};\n  dimensionless_spin_magnitude(make_not_null(&result),\n                               dimensionful_spin_magnitude, christodoulou_mass);\n  return result;\n}\n\ntemplate <typename MetricDataFrame, typename MeasurementFrame>\nvoid spin_vector(\n    const gsl::not_null<std::array<double, 3>*> result, double spin_magnitude,\n    const Scalar<DataVector>& area_element,\n    const Scalar<DataVector>& ricci_scalar,\n    const Scalar<DataVector>& spin_function,\n    const ylm::Strahlkorper<MetricDataFrame>& strahlkorper,\n    const tnsr::I<DataVector, 3, MeasurementFrame>& measurement_frame_coords) {\n  const auto& ylm = strahlkorper.ylm_spherepack();\n\n  // Assert that the DataVectors in area_element, ricci_scalar, and\n  // spin_function have the same size as the ylm size.\n\n  // get the ylm's physical size as a variable to reuse\n  const size_t ylm_physical_size = ylm.physical_size();\n  ASSERT(get(area_element).size() == ylm_physical_size,\n         \"area_element size doesn't match ylm physical size: \"\n             << get(area_element).size() << \" vs \" << ylm_physical_size);\n  ASSERT(get(ricci_scalar).size() == ylm_physical_size,\n         \"ricci_scalar size doesn't match ylm physical size: \"\n             << get(ricci_scalar).size() << \" vs \" << ylm_physical_size);\n  ASSERT(get(spin_function).size() == ylm_physical_size,\n         \"spin_function size doesn't match ylm physical size: \"\n             << get(spin_function).size() << \" vs \" << ylm_physical_size);\n\n  // Compute very rough center of the measurement frame by simply\n  // averaging measurement_frame_coords.  It is ok for this center to\n  // be very rough, since it will be corrected below.\n  const auto measurement_frame_center =\n      [&measurement_frame_coords]() -> std::array<double, 3> {\n    std::array<double, 3> center{};\n    for (size_t d = 0; d < 3; ++d) {\n      gsl::at(center, d) =\n          std::accumulate(measurement_frame_coords.get(d).begin(),\n                          measurement_frame_coords.get(d).end(), 0.0) /\n          measurement_frame_coords.get(d).size();\n    }\n    return center;\n  }();\n\n  std::array<double, 3> spin_vector =\n      make_array<3>(std::numeric_limits<double>::signaling_NaN());\n  auto integrand = make_with_value<Scalar<DataVector>>(get(area_element), 0.0);\n\n  for (size_t i = 0; i < 3; ++i) {\n    get(integrand) = get(area_element) * get(ricci_scalar) *\n                     (measurement_frame_coords.get(i) -\n                      gsl::at(measurement_frame_center, i));\n    get(integrand) =\n        ylm.definite_integral(get(integrand).data()) / (-8.0 * M_PI);\n    // integrand in the above line is -x^i_R from the paper.\n\n    get(integrand) +=\n        measurement_frame_coords.get(i) - gsl::at(measurement_frame_center, i);\n    get(integrand) *= get(area_element) * get(spin_function);\n    gsl::at(spin_vector, i) = ylm.definite_integral(get(integrand).data());\n  }\n\n  // Normalize spin_vector so its magnitude is the magnitude of the spin.\n  *result = spin_vector * (spin_magnitude / magnitude(spin_vector));\n}\n\ntemplate <typename MetricDataFrame, typename MeasurementFrame>\nstd::array<double, 3> spin_vector(\n    double spin_magnitude, const Scalar<DataVector>& area_element,\n    const Scalar<DataVector>& ricci_scalar,\n    const Scalar<DataVector>& spin_function,\n    const ylm::Strahlkorper<MetricDataFrame>& strahlkorper,\n    const tnsr::I<DataVector, 3, MeasurementFrame>& measurement_frame_coords) {\n  std::array<double, 3> result{};\n  spin_vector(make_not_null(&result), spin_magnitude, area_element,\n              ricci_scalar, spin_function, strahlkorper,\n              measurement_frame_coords);\n  return result;\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                              \\\n  template void gr::surfaces::spin_function<FRAME(data)>(                 \\\n      const gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& tangents,          \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper,                 \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& unit_normal_vector,      \\\n      const Scalar<DataVector>& area_element,                             \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature);   \\\n  template Scalar<DataVector> gr::surfaces::spin_function<FRAME(data)>(   \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& tangents,          \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper,                 \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& unit_normal_vector,      \\\n      const Scalar<DataVector>& area_element,                             \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& extrinsic_curvature);   \\\n  template void gr::surfaces::dimensionful_spin_magnitude<FRAME(data)>(   \\\n      const gsl::not_null<double*> result,                                \\\n      const Scalar<DataVector>& ricci_scalar,                             \\\n      const Scalar<DataVector>& spin_function,                            \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_metric,         \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& tangents,          \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper,                 \\\n      const Scalar<DataVector>& area_element);                            \\\n  template double gr::surfaces::dimensionful_spin_magnitude<FRAME(data)>( \\\n      const Scalar<DataVector>& ricci_scalar,                             \\\n      const Scalar<DataVector>& spin_function,                            \\\n      const tnsr::ii<DataVector, 3, FRAME(data)>& spatial_metric,         \\\n      const ylm::Tags::aliases::Jacobian<FRAME(data)>& tangents,          \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper,                 \\\n      const Scalar<DataVector>& area_element);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n\n#define METRICFRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define MEASUREMENTFRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define INSTANTIATE(_, data)                                               \\\n  template void                                                            \\\n  gr::surfaces::spin_vector<METRICFRAME(data), MEASUREMENTFRAME(data)>(    \\\n      const gsl::not_null<std::array<double, 3>*> result,                  \\\n      const double spin_magnitude, const Scalar<DataVector>& area_element, \\\n      const Scalar<DataVector>& ricci_scalar,                              \\\n      const Scalar<DataVector>& spin_function,                             \\\n      const ylm::Strahlkorper<METRICFRAME(data)>& strahlkorper,            \\\n      const tnsr::I<DataVector, 3, MEASUREMENTFRAME(data)>&                \\\n          measurement_frame_coords);                                       \\\n  template std::array<double, 3>                                           \\\n  gr::surfaces::spin_vector<METRICFRAME(data), MEASUREMENTFRAME(data)>(    \\\n      const double spin_magnitude, const Scalar<DataVector>& area_element, \\\n      const Scalar<DataVector>& ricci_scalar,                              \\\n      const Scalar<DataVector>& spin_function,                             \\\n      const ylm::Strahlkorper<METRICFRAME(data)>& strahlkorper,            \\\n      const tnsr::I<DataVector, 3, MEASUREMENTFRAME(data)>&                \\\n          measurement_frame_coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef MEASUREMENTFRAME\n#undef METRICFRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Spin.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TagsTypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/// @{\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Spin function of a 2D `Strahlkorper`.\n *\n * \\details See Eqs. (2) and (10)\n * of \\cite Owen2017yaj. This function computes the\n * \"spin function,\" which is an ingredient for horizon surface integrals that\n * measure quasilocal spin. This function is proportional to the imaginary part\n * of the horizon's complex scalar curvature. For Kerr black holes, the spin\n * function is proportional to the horizon vorticity. It is also useful for\n * visualizing the direction of a black hole's spin.\n * Specifically, this function computes\n * \\f$\\Omega = \\epsilon^{AB}\\nabla_A\\omega_B\\f$,\n * where capital indices index the tangent bundle of the surface and\n * where \\f$\\omega_\\mu=(K_{\\rho\\nu}-K g_{\\rho\\nu})h_\\mu^\\rho s^\\nu\\f$ is\n * the curl of the angular momentum density of the surface,\n * \\f$h^\\rho_\\mu = \\delta_\\mu^\\rho + u_\\mu u^\\rho - s_\\mu s^\\rho\\f$\n * is the projector tangent to the 2-surface,\n * \\f$g_{\\rho\\nu} = \\psi_{\\rho\\nu} + u_\\rho u_\\nu\\f$ is the spatial\n * metric of the spatial slice, \\f$u^\\rho\\f$ is the unit normal to the\n * spatial slice, and \\f$s^\\nu\\f$ is the unit normal vector to the surface.\n * Because the tangent basis vectors \\f$e_A^\\mu\\f$ are\n * orthogonal to both \\f$u^\\mu\\f$ and \\f$s^\\mu\\f$, it is straightforward\n * to show that \\f$\\Omega = \\epsilon^{AB} \\nabla_A K_{B\\mu}s^\\mu\\f$.\n * This function uses the tangent vectors of the `Strahlkorper` to\n * compute \\f$K_{B\\mu}s^\\mu\\f$ and then numerically computes the\n * components of its gradient. The argument `area_element`\n * can be computed via `gr::surfaces::area_element`.\n * The argument `unit_normal_vector` can be found by raising the\n * index of the one-form returned by `gr::surfaces::unit_normal_oneform`.\n * The argument `tangents` is a Tangents that can be obtained from the\n * StrahlkorperDataBox using the `ylm::Tags::Tangents` tag.\n */\ntemplate <typename Frame>\nvoid spin_function(gsl::not_null<Scalar<DataVector>*> result,\n                   const ylm::Tags::aliases::Jacobian<Frame>& tangents,\n                   const ylm::Strahlkorper<Frame>& strahlkorper,\n                   const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n                   const Scalar<DataVector>& area_element,\n                   const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature);\n\ntemplate <typename Frame>\nScalar<DataVector> spin_function(\n    const ylm::Tags::aliases::Jacobian<Frame>& tangents,\n    const ylm::Strahlkorper<Frame>& strahlkorper,\n    const tnsr::I<DataVector, 3, Frame>& unit_normal_vector,\n    const Scalar<DataVector>& area_element,\n    const tnsr::ii<DataVector, 3, Frame>& extrinsic_curvature);\n/// @}\n\n/// @{\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Spin magnitude measured on a 2D `Strahlkorper`.\n *\n * \\details Measures the quasilocal spin magnitude of a Strahlkorper, by\n * inserting \\f$\\alpha=1\\f$ into Eq. (10) of \\cite Owen2009sb\n * and dividing by \\f$8\\pi\\f$ to yield the spin magnitude. The\n * spin magnitude is a Euclidean norm of surface integrals over the horizon\n * \\f$S = \\frac{1}{8\\pi}\\oint z \\Omega dA\\f$,\n * where \\f$\\Omega\\f$ is obtained via `gr::surfaces::spin_function()`,\n * \\f$dA\\f$ is the area element, and \\f$z\\f$ (the \"spin potential\") is a\n * solution of a generalized eigenproblem given by Eq. (9) of\n * \\cite Owen2009sb. Specifically,\n * \\f$\\nabla^4 z + \\nabla \\cdot (R\\nabla z) = \\lambda \\nabla^2 z\\f$, where\n * \\f$R\\f$ is obtained via `gr::surfaces::ricci_scalar()`. The spin\n * magnitude is the Euclidean norm of the three values of \\f$S\\f$ obtained from\n * the eigenvectors \\f$z\\f$ with the 3 smallest-magnitude\n * eigenvalues \\f$\\lambda\\f$. Note that this formulation of the eigenproblem\n * uses the \"Owen\" normalization, Eq. (A9) and surrounding discussion in\n * \\cite Lovelace2008tw.\n * The eigenvectors are normalized  with the \"Kerr normalization\",\n * Eq. (A22) of \\cite Lovelace2008tw.\n * The argument `spatial_metric` is the metric of the 3D spatial slice\n * evaluated on the `Strahlkorper`.\n * The argument `tangents` can be obtained from the StrahlkorperDataBox\n * using the `ylm::Tags::Tangents` tag, and the argument\n * `unit_normal_vector` can\n * be found by raising the index of the one-form returned by\n * `gr::surfaces::unit_normal_one_form`.\n * The argument `strahlkorper` is the surface on which the spin magnitude is\n * computed.\n * The argument `area_element`\n * can be computed via `gr::surfaces::area_element`.\n */\ntemplate <typename Frame>\nvoid dimensionful_spin_magnitude(\n    gsl::not_null<double*> result, const Scalar<DataVector>& ricci_scalar,\n    const Scalar<DataVector>& spin_function,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const ylm::Tags::aliases::Jacobian<Frame>& tangents,\n    const ylm::Strahlkorper<Frame>& strahlkorper,\n    const Scalar<DataVector>& area_element);\n\ntemplate <typename Frame>\ndouble dimensionful_spin_magnitude(\n    const Scalar<DataVector>& ricci_scalar,\n    const Scalar<DataVector>& spin_function,\n    const tnsr::ii<DataVector, 3, Frame>& spatial_metric,\n    const ylm::Tags::aliases::Jacobian<Frame>& tangents,\n    const ylm::Strahlkorper<Frame>& strahlkorper,\n    const Scalar<DataVector>& area_element);\n/// @}\n\n/// @{\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Dimensionless spin magnitude of a `Strahlkorper`.\n *\n * \\details\n * This function computes the dimensionless spin magnitude \\f$\\chi\\f$\n * from the dimensionful spin magnitude \\f$S\\f$ and the christodoulou\n * mass \\f$M\\f$ of a black hole. Specifically, computes\n * \\f$\\chi = \\frac{S}{M^2}\\f$.\n */\n\nvoid dimensionless_spin_magnitude(const gsl::not_null<double*> result,\n                                  const double dimensionful_spin_magnitude,\n                                  const double christodoulou_mass);\n\ndouble dimensionless_spin_magnitude(const double dimensionful_spin_magnitude,\n                                    const double christodoulou_mass);\n/// @}\n\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Spin vector of a 2D `Strahlkorper`.\n *\n * \\details Computes the spin vector of a `Strahlkorper` in a\n * `MeasurementFrame`, such as `Frame::Inertial`. The result is a\n * `std::array<double, 3>` containing the Cartesian components (in\n * `MeasurementFrame`) of the spin vector whose magnitude is\n * `spin_magnitude`. `spin_vector` will return the dimensionless spin\n * components if `spin_magnitude` is the dimensionless spin magnitude,\n * and it will return the dimensionful spin components if\n * `spin_magnitude` is the dimensionful spin magnitude. The spin\n * vector is given by\n * a surface integral over the horizon \\f$\\mathcal{H}\\f$ [Eq. (25) of\n * \\cite Owen2017yaj]:\n * \\f$S^i = \\frac{S}{N} \\oint_\\mathcal{H} dA \\Omega (x^i - x^i_0 - x^i_R) \\f$,\n * where \\f$S\\f$ is the spin magnitude,\n * \\f$N\\f$ is a normalization factor enforcing \\f$\\delta_{ij}S^iS^j = S\\f$,\n * \\f$dA\\f$ is the area element (via `gr::surfaces::area_element`),\n * \\f$\\Omega\\f$ is the \"spin function\" (via `gr::surfaces::spin_function`),\n * \\f$x^i\\f$ are the `MeasurementFrame` coordinates of points on\n * the `Strahlkorper`,\n * \\f$x^i_0\\f$ are the `MeasurementFrame` coordinates of the center\n * of the Strahlkorper,\n * \\f$x^i_R = \\frac{1}{8\\pi}\\oint_\\mathcal{H} dA (x^i - x^i_0) R \\f$,\n * and \\f$R\\f$ is the intrinsic Ricci scalar of the `Strahlkorper`\n * (via `gr::surfaces::ricci_scalar`).\n * Note that measuring positions on the horizon relative to\n * \\f$x^i_0 + x^i_R\\f$ instead of \\f$x^i_0\\f$ ensures that the mass dipole\n * moment vanishes.\n *\n * \\param result The computed spin vector in `MeasurementFrame`.\n * \\param spin_magnitude The spin magnitude.\n * \\param area_element The area element on `strahlkorper`'s\n *        collocation points.\n * \\param ricci_scalar The intrinsic ricci scalar on `strahlkorper`'s\n *        collocation points.\n * \\param spin_function The spin function on `strahlkorper`'s\n *        collocation points.\n * \\param strahlkorper The Strahlkorper in the `MetricDataFrame` frame.\n * \\param measurement_frame_coords The Cartesian coordinates of `strahlkorper`'s\n * collocation points, mapped to `MeasurementFrame`.\n *\n * Note that `spin_vector` uses two frames: the Strahlkorper and all of the\n * metric quantities are in `MetricDataFrame` and are used for doing integrals,\n * but the `measurement_frame_coordinates` are in `MeasurementFrame` and are\n * used for making sure the result is in the appropriate frame.  The two frames\n * `MeasurementFrame` and `MetricDataFrame` may or may not be the same.\n * In principle, spin_vector could be written using only a single frame\n * (`MeasurementFrame`) but that would require that the metric quantities\n * are known on the collocation points of a Strahlkorper in `MeasurementFrame`,\n * which would involve more interpolation.\n */\ntemplate <typename MetricDataFrame, typename MeasurementFrame>\nvoid spin_vector(\n    const gsl::not_null<std::array<double, 3>*> result, double spin_magnitude,\n    const Scalar<DataVector>& area_element,\n    const Scalar<DataVector>& ricci_scalar,\n    const Scalar<DataVector>& spin_function,\n    const ylm::Strahlkorper<MetricDataFrame>& strahlkorper,\n    const tnsr::I<DataVector, 3, MeasurementFrame>& measurement_frame_coords);\n\ntemplate <typename MetricDataFrame, typename MeasurementFrame>\nstd::array<double, 3> spin_vector(\n    double spin_magnitude, const Scalar<DataVector>& area_element,\n    const Scalar<DataVector>& ricci_scalar,\n    const Scalar<DataVector>& spin_function,\n    const ylm::Strahlkorper<MetricDataFrame>& strahlkorper,\n    const tnsr::I<DataVector, 3, MeasurementFrame>& measurement_frame_coords);\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfScalar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfScalar.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\ndouble surface_integral_of_scalar(\n    const Scalar<DataVector>& area_element, const Scalar<DataVector>& scalar,\n    const ylm::Strahlkorper<Frame>& strahlkorper) {\n  const DataVector integrand = get(area_element) * get(scalar);\n  return strahlkorper.ylm_spherepack().definite_integral(integrand.data());\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                \\\n  template double gr::surfaces::surface_integral_of_scalar( \\\n      const Scalar<DataVector>& area_element,               \\\n      const Scalar<DataVector>& scalar,                     \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfScalar.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\n/// \\endcond\n\nnamespace gr::surfaces {\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Surface integral of a scalar on a 2D `Strahlkorper`\n *\n * \\details Computes the surface integral \\f$\\oint dA f\\f$ for a scalar \\f$f\\f$\n * on a `Strahlkorper` with area element \\f$dA\\f$. The area element can be\n * computed via `gr::surfaces::area_element()`.\n */\ntemplate <typename Frame>\ndouble surface_integral_of_scalar(const Scalar<DataVector>& area_element,\n                                  const Scalar<DataVector>& scalar,\n                                  const ylm::Strahlkorper<Frame>& strahlkorper);\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfVector.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\ndouble euclidean_surface_integral_of_vector(\n    const Scalar<DataVector>& area_element,\n    const tnsr::I<DataVector, 3, Frame>& vector,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const ylm::Strahlkorper<Frame>& strahlkorper) {\n  const DataVector integrand =\n      get(area_element) * get(dot_product(vector, normal_one_form)) /\n      sqrt(get(dot_product(normal_one_form, normal_one_form)));\n  return strahlkorper.ylm_spherepack().definite_integral(integrand.data());\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                          \\\n  template double gr::surfaces::euclidean_surface_integral_of_vector( \\\n      const Scalar<DataVector>& area_element,                         \\\n      const tnsr::I<DataVector, 3, FRAME(data)>& vector,              \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& normal_one_form,     \\\n      const ylm::Strahlkorper<FRAME(data)>& strahlkorper);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace ylm {\ntemplate <typename Frame>\nclass Strahlkorper;\n}  // namespace ylm\n/// \\endcond\n\nnamespace gr::surfaces {\n/*!\n * \\ingroup SurfacesGroup\n * \\brief Euclidean surface integral of a vector on a 2D `Strahlkorper`\n *\n * \\details Computes the surface integral\n * \\f$\\oint V^i s_i (s_j s_k \\delta^{jk})^{-1/2} d^2S\\f$ for a\n * vector \\f$V^i\\f$ on a `Strahlkorper` with area element \\f$d^2S\\f$ and\n * normal one-form \\f$s_i\\f$.  Here \\f$\\delta^{ij}\\f$ is the Euclidean\n * metric (i.e. the Kronecker delta). Note that the input `normal_one_form`\n * is not assumed to be normalized; the denominator of the integrand\n * effectively normalizes it using the Euclidean metric.\n * The area element can be computed via\n * `gr::surfaces::euclidean_area_element()`.\n */\ntemplate <typename Frame>\ndouble euclidean_surface_integral_of_vector(\n    const Scalar<DataVector>& area_element,\n    const tnsr::I<DataVector, 3, Frame>& vector,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const ylm::Strahlkorper<Frame>& strahlkorper);\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <deque>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TagsDeclarations.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TagsTypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/AreaElement.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/GradUnitNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Mass.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/RicciScalar.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Spin.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfScalar.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/UnitNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup SurfacesGroup\n/// Holds tags and ComputeItems associated with a `ylm::Strahlkorper`.\nnamespace ylm::Tags {\n/// The OneOverOneFormMagnitude is the reciprocal of the magnitude of the\n/// one-form perpendicular to the horizon\nstruct OneOverOneFormMagnitude : db::SimpleTag {\n  using type = DataVector;\n};\n\n/// Computes the reciprocal of the magnitude of the one form perpendicular to\n/// the horizon\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct OneOverOneFormMagnitudeCompute : db::ComputeTag,\n                                        OneOverOneFormMagnitude {\n  using base = OneOverOneFormMagnitude;\n  using return_type = DataVector;\n  static void function(\n      const gsl::not_null<DataVector*> one_over_magnitude,\n      const tnsr::II<DataType, Dim, Frame>& inverse_spatial_metric,\n      const tnsr::i<DataType, Dim, Frame>& normal_one_form) {\n    *one_over_magnitude =\n        1.0 / get(magnitude(normal_one_form, inverse_spatial_metric));\n  }\n  using argument_tags =\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>,\n                 NormalOneForm<Frame>>;\n};\n\n/// The unit normal one-form \\f$s_j\\f$ to the horizon.\ntemplate <typename Frame>\nstruct UnitNormalOneForm : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Frame>;\n};\n/// Computes the unit one-form perpendicular to the horizon\ntemplate <typename Frame>\nstruct UnitNormalOneFormCompute : UnitNormalOneForm<Frame>, db::ComputeTag {\n  using base = UnitNormalOneForm<Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::i<DataVector, 3, Frame>*>,\n      const tnsr::i<DataVector, 3, Frame>&, const DataVector&)>(\n      &::gr::surfaces::unit_normal_one_form<Frame>);\n  using argument_tags =\n      tmpl::list<ylm::Tags::NormalOneForm<Frame>, OneOverOneFormMagnitude>;\n  using return_type = tnsr::i<DataVector, 3, Frame>;\n};\n\n/// UnitNormalVector is defined as \\f$S^i = \\gamma^{ij} S_j\\f$,\n/// where \\f$S_j\\f$ is the unit normal one form\n/// and \\f$\\gamma^{ij}\\f$ is the inverse spatial metric.\ntemplate <typename Frame>\nstruct UnitNormalVector : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame>;\n};\n/// Computes the UnitNormalVector perpendicular to the horizon.\ntemplate <typename Frame>\nstruct UnitNormalVectorCompute : UnitNormalVector<Frame>, db::ComputeTag {\n  using base = UnitNormalVector<Frame>;\n  static void function(\n      gsl::not_null<tnsr::I<DataVector, 3, Frame>*> result,\n      const tnsr::II<DataVector, 3, Frame>& inverse_spatial_metric,\n      const tnsr::i<DataVector, 3, Frame>& unit_normal_one_form) {\n    raise_or_lower_index(result, unit_normal_one_form, inverse_spatial_metric);\n  }\n  using argument_tags =\n      tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, 3, Frame>,\n                 UnitNormalOneForm<Frame>>;\n  using return_type = tnsr::I<DataVector, 3, Frame>;\n};\n\n/// The 3-covariant gradient \\f$D_i S_j\\f$ of a Strahlkorper's normal\ntemplate <typename Frame>\nstruct GradUnitNormalOneForm : db::SimpleTag {\n  using type = tnsr::ii<DataVector, 3, Frame>;\n};\n/// Computes 3-covariant gradient \\f$D_i S_j\\f$ of a Strahlkorper's normal\ntemplate <typename Frame>\nstruct GradUnitNormalOneFormCompute : GradUnitNormalOneForm<Frame>,\n                                      db::ComputeTag {\n  using base = GradUnitNormalOneForm<Frame>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::ii<DataVector, 3, Frame>*>,\n      const tnsr::i<DataVector, 3, Frame>&, const Scalar<DataVector>&,\n      const tnsr::i<DataVector, 3, Frame>&,\n      const tnsr::ii<DataVector, 3, Frame>&, const DataVector&,\n      const tnsr::Ijj<DataVector, 3, Frame>&)>(\n      &gr::surfaces::grad_unit_normal_one_form<Frame>);\n  using argument_tags =\n      tmpl::list<Rhat<Frame>, Radius<Frame>, UnitNormalOneForm<Frame>,\n                 D2xRadius<Frame>, OneOverOneFormMagnitude,\n                 gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, Frame>>;\n  using return_type = tnsr::ii<DataVector, 3, Frame>;\n};\n\n/// Extrinsic curvature of a 2D Strahlkorper embedded in a 3D space.\ntemplate <typename Frame>\nstruct ExtrinsicCurvature : db::SimpleTag {\n  using type = tnsr::ii<DataVector, 3, Frame>;\n};\n/// Calculates the Extrinsic curvature of a 2D Strahlkorper embedded in a 3D\n/// space.\ntemplate <typename Frame>\nstruct ExtrinsicCurvatureCompute : ExtrinsicCurvature<Frame>, db::ComputeTag {\n  using base = ExtrinsicCurvature<Frame>;\n  static constexpr auto function =\n      static_cast<void (*)(const gsl::not_null<tnsr::ii<DataVector, 3, Frame>*>,\n                           const tnsr::ii<DataVector, 3, Frame>&,\n                           const tnsr::i<DataVector, 3, Frame>&,\n                           const tnsr::I<DataVector, 3, Frame>&)>(\n          &gr::surfaces::extrinsic_curvature<Frame>);\n  using argument_tags =\n      tmpl::list<GradUnitNormalOneForm<Frame>, UnitNormalOneForm<Frame>,\n                 UnitNormalVector<Frame>>;\n  using return_type = tnsr::ii<DataVector, 3, Frame>;\n};\n\n/// Ricci scalar is the two-dimensional intrinsic Ricci scalar curvature\n/// of a Strahlkorper\nstruct RicciScalar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// Computes the two-dimensional intrinsic Ricci scalar of a Strahlkorper\ntemplate <typename Frame>\nstruct RicciScalarCompute : RicciScalar, db::ComputeTag {\n  using base = RicciScalar;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*>, const tnsr::ii<DataVector, 3, Frame>&,\n      const tnsr::I<DataVector, 3, Frame>&,\n      const tnsr::ii<DataVector, 3, Frame>&,\n      const tnsr::II<DataVector, 3, Frame>&)>(\n      &gr::surfaces::ricci_scalar<Frame>);\n  using argument_tags =\n      tmpl::list<gr::Tags::SpatialRicci<DataVector, 3, Frame>,\n                 UnitNormalVector<Frame>, ExtrinsicCurvature<Frame>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3, Frame>>;\n  using return_type = Scalar<DataVector>;\n};\n\n/// The pointwise maximum of the Strahlkorper's intrinsic Ricci scalar\n/// curvature.\nstruct MaxRicciScalar : db::SimpleTag {\n  using type = double;\n};\n\n/// Computes the pointwise maximum of the Strahlkorper's intrinsic Ricci\n/// scalar curvature.\nstruct MaxRicciScalarCompute : MaxRicciScalar, db::ComputeTag {\n  using base = MaxRicciScalar;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> max_ricci_scalar,\n                       const Scalar<DataVector>& ricci_scalar) {\n    *max_ricci_scalar = max(get(ricci_scalar));\n  }\n  using argument_tags = tmpl::list<RicciScalar>;\n};\n\n/// The pointwise minimum of the Strahlkorper’s intrinsic Ricci scalar\n/// curvature.\nstruct MinRicciScalar : db::SimpleTag {\n  using type = double;\n};\n\n/// Computes the pointwise minimum of the Strahlkorper’s intrinsic Ricci\n/// scalar curvature.\nstruct MinRicciScalarCompute : MinRicciScalar, db::ComputeTag {\n  using base = MinRicciScalar;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> min_ricci_scalar,\n                       const Scalar<DataVector>& ricci_scalar) {\n    *min_ricci_scalar = min(get(ricci_scalar));\n  }\n  using argument_tags = tmpl::list<RicciScalar>;\n};\n\n/// @{\n/// Computes the Euclidean area element on a Strahlkorper.\n/// Useful for flat space integrals.\ntemplate <typename Frame>\nstruct EuclideanAreaElement : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <typename Frame>\nstruct EuclideanAreaElementCompute : EuclideanAreaElement<Frame>,\n                                     db::ComputeTag {\n  using base = EuclideanAreaElement<Frame>;\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*>,\n      const ylm::Tags::aliases::Jacobian<Frame>&,\n      const tnsr::i<DataVector, 3, Frame>&, const Scalar<DataVector>&,\n      const tnsr::i<DataVector, 3, Frame>&)>(\n      &::gr::surfaces::euclidean_area_element<Frame>);\n  using argument_tags =\n      tmpl::list<ylm::Tags::Jacobian<Frame>, ylm::Tags::NormalOneForm<Frame>,\n                 ylm::Tags::Radius<Frame>, ylm::Tags::Rhat<Frame>>;\n};\n/// @}\n\n/// @{\n/// Computes the flat-space integral of a scalar over a Strahlkorper.\ntemplate <typename IntegrandTag, typename Frame>\nstruct EuclideanSurfaceIntegral : db::SimpleTag {\n  static std::string name() {\n    return \"EuclideanSurfaceIntegral(\" + db::tag_name<IntegrandTag>() + \")\";\n  }\n  using type = double;\n};\n\ntemplate <typename IntegrandTag, typename Frame>\nstruct EuclideanSurfaceIntegralCompute\n    : EuclideanSurfaceIntegral<IntegrandTag, Frame>,\n      db::ComputeTag {\n  using base = EuclideanSurfaceIntegral<IntegrandTag, Frame>;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> surface_integral,\n                       const Scalar<DataVector>& euclidean_area_element,\n                       const Scalar<DataVector>& integrand,\n                       const ylm::Strahlkorper<Frame>& strahlkorper) {\n    *surface_integral = ::gr::surfaces::surface_integral_of_scalar<Frame>(\n        euclidean_area_element, integrand, strahlkorper);\n  }\n  using argument_tags = tmpl::list<EuclideanAreaElement<Frame>, IntegrandTag,\n                                   ylm::Tags::Strahlkorper<Frame>>;\n};\n/// @}\n\n/// @{\n/// Computes the Euclidean-space integral of a vector over a\n/// Strahlkorper, \\f$\\oint V^i s_i (s_j s_k \\delta^{jk})^{-1/2} d^2 S\\f$,\n/// where \\f$s_i\\f$ is the Strahlkorper surface unit normal and\n/// \\f$\\delta^{ij}\\f$ is the Kronecker delta.  Note that \\f$s_i\\f$ is\n/// not assumed to be normalized; the denominator of the integrand\n/// effectively normalizes it using the Euclidean metric.\ntemplate <typename IntegrandTag, typename Frame>\nstruct EuclideanSurfaceIntegralVector : db::SimpleTag {\n  static std::string name() {\n    return \"EuclideanSurfaceIntegralVector(\" + db::tag_name<IntegrandTag>() +\n           \")\";\n  }\n  using type = double;\n};\n\ntemplate <typename IntegrandTag, typename Frame>\nstruct EuclideanSurfaceIntegralVectorCompute\n    : EuclideanSurfaceIntegralVector<IntegrandTag, Frame>,\n      db::ComputeTag {\n  using base = EuclideanSurfaceIntegralVector<IntegrandTag, Frame>;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> surface_integral,\n                       const Scalar<DataVector>& euclidean_area_element,\n                       const tnsr::I<DataVector, 3, Frame>& integrand,\n                       const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n                       const ylm::Strahlkorper<Frame>& strahlkorper) {\n    *surface_integral =\n        ::gr::surfaces::euclidean_surface_integral_of_vector<Frame>(\n            euclidean_area_element, integrand, normal_one_form, strahlkorper);\n  }\n  using argument_tags = tmpl::list<EuclideanAreaElement<Frame>, IntegrandTag,\n                                   ylm::Tags::NormalOneForm<Frame>,\n                                   ylm::Tags::Strahlkorper<Frame>>;\n};\n/// @}\n}  // namespace ylm::Tags\n\nnamespace gr::surfaces {\n/// \\ingroup SurfacesGroup\n/// Holds tags and ComputeItems associated with a `ylm::Strahlkorper` that\n/// also need a metric.\nnamespace Tags {\n\n/// @{\n/// Computes the area element on a Strahlkorper. Useful for integrals.\ntemplate <typename Frame>\nstruct AreaElement : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <typename Frame>\nstruct AreaElementCompute : AreaElement<Frame>, db::ComputeTag {\n  using base = AreaElement<Frame>;\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*>, const tnsr::ii<DataVector, 3, Frame>&,\n      const ylm::Tags::aliases::Jacobian<Frame>&,\n      const tnsr::i<DataVector, 3, Frame>&, const Scalar<DataVector>&,\n      const tnsr::i<DataVector, 3, Frame>&)>(&area_element<Frame>);\n  using argument_tags =\n      tmpl::list<gr::Tags::SpatialMetric<DataVector, 3, Frame>,\n                 ylm::Tags::Jacobian<Frame>, ylm::Tags::NormalOneForm<Frame>,\n                 ylm::Tags::Radius<Frame>, ylm::Tags::Rhat<Frame>>;\n};\n/// @}\n\n/// @{\n/// Computes the integral of a scalar over a Strahlkorper.\ntemplate <typename IntegrandTag, typename Frame>\nstruct SurfaceIntegral : db::SimpleTag {\n  static std::string name() {\n    return \"SurfaceIntegral(\" + db::tag_name<IntegrandTag>() + \")\";\n  }\n  using type = double;\n};\n\ntemplate <typename IntegrandTag, typename Frame>\nstruct SurfaceIntegralCompute : SurfaceIntegral<IntegrandTag, Frame>,\n                                db::ComputeTag {\n  using base = SurfaceIntegral<IntegrandTag, Frame>;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> surface_integral,\n                       const Scalar<DataVector>& area_element,\n                       const Scalar<DataVector>& integrand,\n                       const ylm::Strahlkorper<Frame>& strahlkorper) {\n    *surface_integral = ::gr::surfaces::surface_integral_of_scalar<Frame>(\n        area_element, integrand, strahlkorper);\n  }\n  using argument_tags = tmpl::list<AreaElement<Frame>, IntegrandTag,\n                                   ylm::Tags::Strahlkorper<Frame>>;\n};\n/// @}\n\n/// Tag representing the surface area of a Strahlkorper\nstruct Area : db::SimpleTag {\n  using type = double;\n};\n\n/// Computes the surface area of a Strahlkorer, \\f$A = \\oint_S dA\\f$ given an\n/// AreaElement \\f$dA\\f$ and a Strahlkorper \\f$S\\f$.\ntemplate <typename Frame>\nstruct AreaCompute : Area, db::ComputeTag {\n  using base = Area;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> result,\n                       const ylm::Strahlkorper<Frame>& strahlkorper,\n                       const Scalar<DataVector>& area_element) {\n    *result = strahlkorper.ylm_spherepack().definite_integral(\n        get(area_element).data());\n  }\n  using argument_tags =\n      tmpl::list<ylm::Tags::Strahlkorper<Frame>, AreaElement<Frame>>;\n};\n\n/// The Irreducible (areal) mass of an apparent horizon\nstruct IrreducibleMass : db::SimpleTag {\n  using type = double;\n};\n\n/// Computes the Irreducible mass of an apparent horizon from its area\ntemplate <typename Frame>\nstruct IrreducibleMassCompute : IrreducibleMass, db::ComputeTag {\n  using base = IrreducibleMass;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> result, const double area) {\n    *result = ::gr::surfaces::irreducible_mass(area);\n  }\n\n  using argument_tags = tmpl::list<Area>;\n};\n\n/// The spin function is proportional to the imaginary part of the\n/// Strahlkorper’s complex scalar curvature.\n\nstruct SpinFunction : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/// Calculates the spin function which is proportional to the imaginary part of\n/// the Strahlkorper’s complex scalar curvature.\ntemplate <typename Frame>\nstruct SpinFunctionCompute : SpinFunction, db::ComputeTag {\n  using base = SpinFunction;\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataVector>*>,\n      const ylm::Tags::aliases::Jacobian<Frame>&,\n      const ylm::Strahlkorper<Frame>&, const tnsr::I<DataVector, 3, Frame>&,\n      const Scalar<DataVector>&, const tnsr::ii<DataVector, 3, Frame>&)>(\n      &gr::surfaces::spin_function<Frame>);\n  using argument_tags =\n      tmpl::list<ylm::Tags::Tangents<Frame>, ylm::Tags::Strahlkorper<Frame>,\n                 ylm::Tags::UnitNormalVector<Frame>, AreaElement<Frame>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame>>;\n  using return_type = Scalar<DataVector>;\n};\n\n/// The approximate-Killing-Vector quasilocal spin magnitude of a Strahlkorper\n/// (see Sec. 2.2 of \\cite Boyle2019kee and references therein).\nstruct DimensionfulSpinMagnitude : db::SimpleTag {\n  using type = double;\n};\n\n/// Computes the approximate-Killing-Vector quasilocal spin magnitude of a\n/// Strahlkorper\ntemplate <typename Frame>\nstruct DimensionfulSpinMagnitudeCompute : DimensionfulSpinMagnitude,\n                                          db::ComputeTag {\n  using base = DimensionfulSpinMagnitude;\n  using return_type = double;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<double*>, const Scalar<DataVector>&,\n      const Scalar<DataVector>&, const tnsr::ii<DataVector, 3, Frame>&,\n      const ylm::Tags::aliases::Jacobian<Frame>&,\n      const ylm::Strahlkorper<Frame>&, const Scalar<DataVector>&)>(\n      &gr::surfaces::dimensionful_spin_magnitude<Frame>);\n  using argument_tags =\n      tmpl::list<ylm::Tags::RicciScalar, SpinFunction,\n                 gr::Tags::SpatialMetric<DataVector, 3, Frame>,\n                 ylm::Tags::Tangents<Frame>, ylm::Tags::Strahlkorper<Frame>,\n                 AreaElement<Frame>>;\n};\n\n/// The dimensionful spin angular momentum vector.\ntemplate <typename Frame>\nstruct DimensionfulSpinVector : db::SimpleTag {\n  using type = std::array<double, 3>;\n};\n\n/// Computes the dimensionful spin angular momentum vector.\ntemplate <typename MeasurementFrame, typename MetricDataFrame>\nstruct DimensionfulSpinVectorCompute : DimensionfulSpinVector<MeasurementFrame>,\n                                       db::ComputeTag {\n  using base = DimensionfulSpinVector<MeasurementFrame>;\n  using return_type = std::array<double, 3>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<std::array<double, 3>*>, double,\n      const Scalar<DataVector>&, const Scalar<DataVector>&,\n      const Scalar<DataVector>&, const ylm::Strahlkorper<MetricDataFrame>&,\n      const tnsr::I<DataVector, 3, MeasurementFrame>&)>(\n      &gr::surfaces::spin_vector<MetricDataFrame, MeasurementFrame>);\n  using argument_tags =\n      tmpl::list<DimensionfulSpinMagnitude, AreaElement<MetricDataFrame>,\n                 ylm::Tags::RicciScalar, SpinFunction,\n                 ylm::Tags::Strahlkorper<MetricDataFrame>,\n                 ylm::Tags::CartesianCoords<MeasurementFrame>>;\n};\n\n/// The Christodoulou mass, which is a function of the dimensionful spin\n/// angular momentum and the irreducible mass of a Strahlkorper.\nstruct ChristodoulouMass : db::SimpleTag {\n  using type = double;\n};\n\n/// Computes the Christodoulou mass from the dimensionful spin angular momentum\n/// and the irreducible mass of a Strahlkorper.\ntemplate <typename Frame>\nstruct ChristodoulouMassCompute : ChristodoulouMass, db::ComputeTag {\n  using base = ChristodoulouMass;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> result,\n                       const double dimensionful_spin_magnitude,\n                       const double irreducible_mass) {\n    *result = ::gr::surfaces::christodoulou_mass(dimensionful_spin_magnitude,\n                                                 irreducible_mass);\n  }\n\n  using argument_tags = tmpl::list<DimensionfulSpinMagnitude, IrreducibleMass>;\n};\n\n/// The dimensionless spin magnitude of a `Strahlkorper`.\ntemplate <typename Frame>\nstruct DimensionlessSpinMagnitude : db::SimpleTag {\n  using type = double;\n};\n\n/// Computes the dimensionless spin magnitude \\f$\\chi = \\frac{S}{M^2}\\f$\n/// from the dimensionful spin magnitude \\f$S\\f$ and the christodoulou\n/// mass \\f$M\\f$ of a black hole.\ntemplate <typename Frame>\nstruct DimensionlessSpinMagnitudeCompute : DimensionlessSpinMagnitude<Frame>,\n                                           db::ComputeTag {\n  using base = DimensionlessSpinMagnitude<Frame>;\n  using return_type = double;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<double*>, const double, const double)>(\n      &gr::surfaces::dimensionless_spin_magnitude);\n  using argument_tags =\n      tmpl::list<DimensionfulSpinMagnitude, ChristodoulouMass>;\n};\n\n/// The dimensionless spin angular momentum vector\ntemplate <typename Frame>\nstruct DimensionlessSpinVector : db::SimpleTag {\n  using type = std::array<double, 3>;\n};\n\n/// Computes the dimensionless spin angular momentum vector\ntemplate <typename MeasurementFrame, typename MetricDataFrame>\nstruct DimensionlessSpinVectorCompute\n    : DimensionlessSpinVector<MeasurementFrame>,\n      db::ComputeTag {\n  using base = DimensionlessSpinVector<MeasurementFrame>;\n  using argument_tags =\n      tmpl::list<DimensionlessSpinMagnitude<MeasurementFrame>,\n                 AreaElement<MetricDataFrame>, ylm::Tags::RicciScalar,\n                 SpinFunction, ylm::Tags::Strahlkorper<MetricDataFrame>,\n                 ylm::Tags::CartesianCoords<MeasurementFrame>>;\n  using return_type = std::array<double, 3>;\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<std::array<double, 3>*>, double,\n      const Scalar<DataVector>&, const Scalar<DataVector>&,\n      const Scalar<DataVector>&, const ylm::Strahlkorper<MetricDataFrame>&,\n      const tnsr::I<DataVector, 3, MeasurementFrame>&)>(\n      &gr::surfaces::spin_vector<MetricDataFrame, MeasurementFrame>);\n};\n\n}  // namespace Tags\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\nnamespace ylm::Tags {\ntemplate <typename Frame>\nstruct EuclideanAreaElement;\ntemplate <typename Frame>\nstruct EuclideanAreaElementCompute;\ntemplate <typename IntegrandTag, typename Frame>\nstruct EuclideanSurfaceIntegral;\nstruct OneOverOneFormMagnitude;\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct OneOverOneFormMagnitudeCompute;\ntemplate <typename Frame>\nstruct UnitNormalOneForm;\ntemplate <typename Frame>\nstruct UnitNormalOneFormCompute;\ntemplate <typename Frame>\nstruct UnitNormalVector;\ntemplate <typename Frame>\nstruct UnitNormalVectorCompute;\ntemplate <typename Frame>\nstruct GradUnitNormalOneForm;\ntemplate <typename Frame>\nstruct GradUnitNormalOneFormCompute;\ntemplate <typename Frame>\nstruct ExtrinsicCurvature;\ntemplate <typename Frame>\nstruct ExtrinsicCurvatureCompute;\nstruct RicciScalar;\ntemplate <typename Frame>\nstruct RicciScalarCompute;\nstruct MaxRicciScalar;\nstruct MaxRicciScalarCompute;\nstruct MinRicciScalar;\nstruct MinRicciScalarCompute;\n}  // namespace ylm::Tags\n\nnamespace gr::surfaces::Tags {\ntemplate <typename Frame>\nstruct AreaElement;\ntemplate <typename Frame>\nstruct AreaElementCompute;\ntemplate <typename IntegrandTag, typename Frame>\nstruct SurfaceIntegral;\nstruct Area;\ntemplate <typename Frame>\nstruct AreaCompute;\nstruct IrreducibleMass;\ntemplate <typename Frame>\nstruct IrreducibleMassCompute;\nstruct SpinFunction;\ntemplate <typename Frame>\nstruct SpinFunctionCompute;\nstruct DimensionfulSpinMagnitude;\ntemplate <typename Frame>\nstruct DimensionfulSpinMagnitudeCompute;\nstruct ChristodoulouMass;\ntemplate <typename Frame>\nstruct ChristodoulouMassCompute;\ntemplate <typename Frame>\nstruct DimensionlessSpinMagnitude;\ntemplate <typename Frame>\nstruct DimensionlessSpinMagnitudeCompute;\ntemplate <typename Frame>\nstruct DimensionfulSpinVector;\ntemplate <typename MeasurementFrame, typename MetricDataFrame>\nstruct DimensionfulSpinVectorCompute;\ntemplate <typename Frame>\nstruct DimensionlessSpinVector;\ntemplate <typename MeasurementFrame, typename MetricDataFrame>\nstruct DimensionlessSpinVectorCompute;\n}  // namespace gr::surfaces::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/UnitNormalOneForm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/UnitNormalOneForm.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr::surfaces {\ntemplate <typename Frame>\nvoid unit_normal_one_form(\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame>*> result,\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const DataVector& one_over_one_form_magnitude) {\n  *result = normal_one_form;\n  for (size_t i = 0; i < 3; ++i) {\n    result->get(i) *= one_over_one_form_magnitude;\n  }\n}\n\ntemplate <typename Frame>\ntnsr::i<DataVector, 3, Frame> unit_normal_one_form(\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const DataVector& one_over_one_form_magnitude) {\n  tnsr::i<DataVector, 3, Frame> result{};\n  unit_normal_one_form(make_not_null(&result), normal_one_form,\n                       one_over_one_form_magnitude);\n  return result;\n}\n}  // namespace gr::surfaces\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATE(_, data)                                            \\\n  template void gr::surfaces::unit_normal_one_form<FRAME(data)>(        \\\n      const gsl::not_null<tnsr::i<DataVector, 3, FRAME(data)>*> result, \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& normal_one_form,       \\\n      const DataVector& one_over_one_form_magnitude);                   \\\n  template tnsr::i<DataVector, 3, FRAME(data)>                          \\\n  gr::surfaces::unit_normal_one_form<FRAME(data)>(                      \\\n      const tnsr::i<DataVector, 3, FRAME(data)>& normal_one_form,       \\\n      const DataVector& one_over_one_form_magnitude);\nGENERATE_INSTANTIATIONS(INSTANTIATE,\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n#undef INSTANTIATE\n#undef FRAME\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Surfaces/UnitNormalOneForm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr::surfaces {\n/// @{\n/// \\ingroup SurfacesGroup\n/// \\brief Computes normalized unit normal one-form to a Strahlkorper.\n///\n/// \\details The input argument `normal_one_form` \\f$n_i\\f$ is the\n/// unnormalized surface one-form; it depends on a Strahlkorper but\n/// not on a metric.  The input argument `one_over_one_form_magnitude`\n/// is \\f$1/\\sqrt{g^{ij}n_i n_j}\\f$, which can be computed using (one\n/// over) the `magnitude` function.\ntemplate <typename Frame>\nvoid unit_normal_one_form(gsl::not_null<tnsr::i<DataVector, 3, Frame>*> result,\n                          const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n                          const DataVector& one_over_one_form_magnitude);\n\ntemplate <typename Frame>\ntnsr::i<DataVector, 3, Frame> unit_normal_one_form(\n    const tnsr::i<DataVector, 3, Frame>& normal_one_form,\n    const DataVector& one_over_one_form_magnitude);\n/// @}\n}  // namespace gr::surfaces\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Conformal.hpp\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace gr {\nnamespace Tags {\n/*!\n * \\brief The quantity `Tag` scaled by a conformal factor to the given `Power`\n */\ntemplate <typename Tag, int Power>\nstruct Conformal : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n  static constexpr int conformal_factor_power = Power;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n\nnamespace gr {\nnamespace Tags {\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpacetimeMetric : db::SimpleTag {\n  using type = tnsr::aa<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct InverseSpacetimeMetric : db::SimpleTag {\n  using type = tnsr::AA<DataType, Dim, Frame>;\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpatialMetric : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n/*!\n * \\brief Inverse of the spatial metric.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct InverseSpatialMetric : db::SimpleTag {\n  using type = tnsr::II<DataType, Dim, Frame>;\n};\n/*!\n * \\brief Determinant of the spatial metric.\n */\ntemplate <typename DataType>\nstruct DetSpatialMetric : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct SqrtDetSpatialMetric : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n/*!\n * \\brief Derivative of the determinant of the spatial metric.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct DerivDetSpatialMetric : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n/*!\n * \\brief Spatial derivative of the inverse of the spatial metric.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct DerivInverseSpatialMetric : db::SimpleTag {\n  using type = tnsr::iJJ<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Shift : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\ntemplate <typename DataType>\nstruct Lapse : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n/*!\n * \\brief Spacetime derivatives of the spacetime metric\n *\n * \\details Spacetime derivatives of the spacetime metric\n * \\f$\\partial_a g_{bc}\\f$ assembled from the spatial and temporal\n * derivatives of evolved 3+1 variables.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct DerivativesOfSpacetimeMetric : db::SimpleTag {\n  using type = tnsr::abb<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpacetimeChristoffelFirstKind : db::SimpleTag {\n  using type = tnsr::abb<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpacetimeChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::Abb<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpatialChristoffelFirstKind : db::SimpleTag {\n  using type = tnsr::ijj<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpatialChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::Ijj<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpacetimeNormalOneForm : db::SimpleTag {\n  using type = tnsr::a<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpacetimeNormalVector : db::SimpleTag {\n  using type = tnsr::A<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct TraceSpacetimeChristoffelFirstKind : db::SimpleTag {\n  using type = tnsr::a<DataType, Dim, Frame>;\n};\n/*!\n * \\brief Trace of the spacetime Christoffel symbols of the second kind\n * \\f$\\Gamma^{i} = \\Gamma^i_{jk}g^{jk}\\f$, where \\f$\\Gamma^i_{jk}\\f$ are\n * Christoffel symbols of the second kind and \\f$g^{jk}\\f$ is the\n * inverse spacetime metric.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct TraceSpacetimeChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::A<DataType, Dim, Frame>;\n};\n/*!\n * \\brief Trace of the spatial Christoffel symbols of the first kind\n * \\f$\\Gamma_{i} = \\Gamma_{ijk}\\gamma^{jk}\\f$, where \\f$\\Gamma_{ijk}\\f$ are\n * Christoffel symbols of the first kind and \\f$\\gamma^{jk}\\f$ is the\n * inverse spatial metric.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct TraceSpatialChristoffelFirstKind : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct TraceSpatialChristoffelSecondKind : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n/// Contraction of the first two indices of the spatial Christoffel symbols:\n/// \\f$\\Gamma^i_{ij}\\f$. Useful for covariant divergences.\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpatialChristoffelSecondKindContracted : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ExtrinsicCurvature : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\ntemplate <typename DataType>\nstruct TraceExtrinsicCurvature : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct CovariantDerivativeOfExtrinsicCurvature : db::SimpleTag {\n  using type = tnsr::ijj<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Holds a quantity that's similar to the shift, but isn't the shift.\n *\n * \\details This holds\n *\n * \\f{equation}{\n * \\beta^i \\frac{\\partial x^\\hat{i}}{\\partial x^i} =\n * \\hat{beta}^\\hat{i} + \\frac{\\partial x^\\hat{i}}{\\partial t}\n * \\f}\n *\n * where hatted quantities are in the distorted frame and non-hatted quantities\n * are in the grid frame.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct ShiftyQuantity : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Computes the spatial Ricci tensor from the spatial\n * Christoffel symbol of the second kind and its derivative.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct SpatialRicci : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Simple tag for the spatial Ricci scalar\n */\ntemplate <typename DataType>\nstruct SpatialRicciScalar : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief Computes the real part of \\f$\\Psi_4\\f$\n */\ntemplate <typename DataType>\nstruct Psi4Real : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The energy density \\f$E=n_a n_b T^{ab}\\f$, where \\f$n_a\\f$ denotes the\n * normal to the spatial hypersurface\n */\ntemplate <typename DataType>\nstruct EnergyDensity : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The trace of the spatial stress-energy tensor\n * \\f$S=\\gamma^{ij}\\gamma_{ia}\\gamma_{jb}T^{ab}\\f$\n */\ntemplate <typename DataType>\nstruct StressTrace : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The spatial momentum density \\f$S^i=-\\gamma^{ij}n^aT_{aj}\\f$, where\n * \\f$n_a\\f$ denotes the normal to the spatial hypersurface\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct MomentumDensity : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/// The ADM Hamiltonian constraint\n/// \\f$\\frac{1}{2} \\left(R + K^2 - K_{ij} K^{ij}\\right) - 8 \\pi \\rho\\f$\n/// (see e.g. Eq. (2.132) in \\cite BaumgarteShapiro).\n///\n/// \\note We include a factor of \\f$1/2\\f$ in the Hamiltonian constraint for\n/// consistency with SpEC, and so the matter terms in the Hamiltonian and\n/// momentum constraints are both scaled by $8\\pi$.\ntemplate <typename DataType>\nstruct HamiltonianConstraint : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The ADM momentum constraint\n/// \\f$\\nabla_j (K^{ij} - \\gamma^{ij} K) - 8 \\pi S^i\\f$, where\n/// \\f$\\nabla\\f$ denotes the covariant derivative associated with the spatial\n/// metric \\f$\\gamma_{ij}\\f$ (see e.g. Eq. (2.133) in \\cite BaumgarteShapiro).\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct MomentumConstraint : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Computes the electric part of the Weyl tensor in vacuum\n * as: \\f$ E_{ij} = R_{ij} + KK_{ij} - K^m_{i}K_{mj}\\f$ where \\f$R_{ij}\\f$ is\n * the spatial Ricci tensor, \\f$K_{ij}\\f$ is the extrinsic curvature, and\n * \\f$K\\f$ is the trace of \\f$K_{ij}\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct WeylElectric : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief The magnetic part of the Weyl tensor in vacuum \\f$B_{ij}\\f$.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct WeylMagnetic : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Frame>;\n};\n\n/*!\n * \\brief Computes a quantity measuring how far from type D spacetime is,\n * using measure D1 [Eq. (8)] of \\cite Bhagwat2017tkm.\n */\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct WeylTypeD1 : db::SimpleTag {\n  using type = tnsr::ii<DataType, 3, Frame>;\n};\n\n/*!\n * \\brief Computes the scalar \\f$E_{ij} E^{ij}\\f$ from the electric part of the\n * Weyl tensor \\f$E_{ij}\\f$ and the inverse spatial metric \\f$\\gamma^{ij}\\f$,\n * i.e. \\f$E_{ij} E^{ij} = \\gamma^{ik}\\gamma^{jl}E_{ij}E_{kl}\\f$.\n */\ntemplate <typename DataType>\nstruct WeylElectricScalar : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief The square \\f$B_{ij} B^{ij}\\f$ of the magnetic part of the Weyl tensor\n * \\f$B_{ij}\\f$.\n */\ntemplate <typename DataType>\nstruct WeylMagneticScalar : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/*!\n * \\brief Computes the scalar \\f$D_{ij} D^{ij}\\f$ (Eq. (8) of\n * \\cite Bhagwat2017tkm) from \\f$D_{ij}\\f$ and the inverse spatial metric\n * \\f$\\gamma^{ij}\\f$, i.e. \\f$D = \\gamma^{ik}\\gamma^{jl}D_{ij}D_{kl}\\f$.\n */\ntemplate <typename DataType>\nstruct WeylTypeD1Scalar : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n}  // namespace Tags\n\n/// GR Tags commonly needed for the evolution of hydro systems\ntemplate <size_t Dim, typename DataType>\nusing tags_for_hydro =\n    tmpl::list<gr::Tags::Lapse<DataType>, gr::Tags::Shift<DataType, Dim>,\n               gr::Tags::SpatialMetric<DataType, Dim>,\n               gr::Tags::InverseSpatialMetric<DataType, Dim>,\n               gr::Tags::SqrtDetSpatialMetric<DataType>,\n               ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>,\n                             Frame::Inertial>,\n               ::Tags::deriv<gr::Tags::Shift<DataType, Dim>, tmpl::size_t<Dim>,\n                             Frame::Inertial>,\n               ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                             tmpl::size_t<Dim>, Frame::Inertial>,\n               gr::Tags::ExtrinsicCurvature<DataType, Dim>>;\n\n/// The tags for the variables returned by GR analytic solutions.\ntemplate <size_t Dim, typename DataType>\nusing analytic_solution_tags =\n    tmpl::list<gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,\n               ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<Dim>,\n                             Frame::Inertial>,\n               gr::Tags::Shift<DataType, Dim>,\n               ::Tags::dt<gr::Tags::Shift<DataType, Dim>>,\n               ::Tags::deriv<gr::Tags::Shift<DataType, Dim>, tmpl::size_t<Dim>,\n                             Frame::Inertial>,\n               gr::Tags::SpatialMetric<DataType, Dim>,\n               ::Tags::dt<gr::Tags::SpatialMetric<DataType, Dim>>,\n               ::Tags::deriv<gr::Tags::SpatialMetric<DataType, Dim>,\n                             tmpl::size_t<Dim>, Frame::Inertial>,\n               gr::Tags::SqrtDetSpatialMetric<DataType>,\n               gr::Tags::ExtrinsicCurvature<DataType, Dim>,\n               gr::Tags::InverseSpatialMetric<DataType, Dim>>;\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n\nclass DataVector;\n\nnamespace gr {\nnamespace Tags {\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpacetimeMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct InverseSpacetimeMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpatialMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct DetAndInverseSpatialMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct InverseSpatialMetric;\ntemplate <typename DataType>\nstruct DetSpatialMetric;\ntemplate <typename DataType>\nstruct SqrtDetSpatialMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct DerivDetSpatialMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct DerivInverseSpatialMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct Shift;\ntemplate <typename DataType>\nstruct Lapse;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct DerivativesOfSpacetimeMetric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpacetimeChristoffelFirstKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpacetimeChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpatialChristoffelFirstKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpatialChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpacetimeNormalOneForm;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpacetimeNormalVector;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct TraceSpacetimeChristoffelFirstKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct TraceSpacetimeChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct TraceSpatialChristoffelFirstKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct TraceSpatialChristoffelSecondKind;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpatialChristoffelSecondKindContracted;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct ExtrinsicCurvature;\ntemplate <typename DataType>\nstruct TraceExtrinsicCurvature;\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct CovariantDerivativeOfExtrinsicCurvature;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct SpatialRicci;\ntemplate <typename DataType>\nstruct SpatialRicciScalar;\ntemplate <typename DataType>\nstruct Psi4Real;\ntemplate <typename DataType>\nstruct EnergyDensity;\ntemplate <typename DataType>\nstruct StressTrace;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct MomentumDensity;\ntemplate <typename DataType>\nstruct HamiltonianConstraint;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct MomentumConstraint;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct WeylElectric;\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nstruct WeylMagnetic;\ntemplate <typename DataType>\nstruct WeylElectricScalar;\ntemplate <typename DataType>\nstruct WeylMagneticScalar;\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct WeylTypeD1;\ntemplate <typename Datatype>\nstruct WeylTypeD1Scalar;\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpacetimeMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpacetimeMetric.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_derivative_of_spacetime_metric(\n    const gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*>\n        dt_spacetime_metric,\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric) {\n  get<0, 0>(*dt_spacetime_metric) = -2.0 * get(lapse) * get(dt_lapse);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      get<0, 0>(*dt_spacetime_metric) +=\n          dt_spatial_metric.get(i, j) * shift.get(i) * shift.get(j) +\n          2.0 * spatial_metric.get(i, j) * shift.get(i) * dt_shift.get(j);\n    }\n  }\n\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    dt_spacetime_metric->get(0, i + 1) =\n        dt_spatial_metric.get(i, 0) * get<0>(shift) +\n        spatial_metric.get(i, 0) * get<0>(dt_shift);\n    for (size_t j = 1; j < SpatialDim; ++j) {\n      dt_spacetime_metric->get(0, i + 1) +=\n          dt_spatial_metric.get(i, j) * shift.get(j) +\n          spatial_metric.get(i, j) * dt_shift.get(j);\n    }\n  }\n\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      dt_spacetime_metric->get(i + 1, j + 1) = dt_spatial_metric.get(i, j);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> time_derivative_of_spacetime_metric(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric) {\n  tnsr::aa<DataType, SpatialDim, Frame> dt_spacetime_metric{\n      get_size(get(lapse))};\n  time_derivative_of_spacetime_metric(make_not_null(&dt_spacetime_metric),\n                                      lapse, dt_lapse, shift, dt_shift,\n                                      spatial_metric, dt_spatial_metric);\n  return dt_spacetime_metric;\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>                       \\\n  gr::time_derivative_of_spacetime_metric(                                     \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse,   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,               \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,            \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,     \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric); \\\n  template void gr::time_derivative_of_spacetime_metric(                       \\\n      const gsl::not_null<tnsr::aa<DTYPE(data), DIM(data), FRAME(data)>*>      \\\n          dt_spacetime_metric,                                                 \\\n      const Scalar<DTYPE(data)>& lapse, const Scalar<DTYPE(data)>& dt_lapse,   \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,               \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& dt_shift,            \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,     \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& dt_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpacetimeMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup GeneralRelativityGroup\n/// Holds functions related to general relativity.\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the time derivative of the spacetime metric from spatial\n * metric, lapse, shift, and their time derivatives.\n *\n * \\details Computes the derivative as:\n *\n * \\f{align}{\n * \\partial_t g_{tt} &= - 2 \\alpha \\partial_t \\alpha\n * - 2 \\gamma_{i j} \\beta^i \\partial_t \\beta^j\n * + \\beta^i \\beta^j \\partial_t \\gamma_{i j}\\\\\n * \\partial_t g_{t i} &= \\gamma_{j i} \\partial_t \\beta^j\n * + \\beta^j \\partial_t \\gamma_{j i}\\\\\n * \\partial_t g_{i j} &= \\partial_t \\gamma_{i j},\n * \\f}\n *\n * where \\f$\\alpha, \\beta^i, \\gamma_{ij}\\f$ are the lapse, shift, and spatial\n * metric respectively.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_derivative_of_spacetime_metric(\n    gsl::not_null<tnsr::aa<DataType, SpatialDim, Frame>*> dt_spacetime_metric,\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::aa<DataType, SpatialDim, Frame> time_derivative_of_spacetime_metric(\n    const Scalar<DataType>& lapse, const Scalar<DataType>& dt_lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::I<DataType, SpatialDim, Frame>& dt_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& dt_spatial_metric);\n/// @}\nnamespace Tags {}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_derivative_of_spatial_metric(\n    const gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*>\n        dt_spatial_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature) {\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      dt_spatial_metric->get(i, j) =\n          -2. * get(lapse) * extrinsic_curvature.get(i, j);\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        dt_spatial_metric->get(i, j) +=\n            shift.get(k) * deriv_spatial_metric.get(k, i, j) +\n            spatial_metric.get(k, i) * deriv_shift.get(j, k) +\n            spatial_metric.get(k, j) * deriv_shift.get(i, k);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> time_derivative_of_spatial_metric(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature) {\n  tnsr::ii<DataType, SpatialDim, Frame> dt_spatial_metric{get_size(get(lapse))};\n  time_derivative_of_spatial_metric(make_not_null(&dt_spatial_metric), lapse,\n                                    shift, deriv_shift, spatial_metric,\n                                    deriv_spatial_metric, extrinsic_curvature);\n  return dt_spatial_metric;\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>                   \\\n  gr::time_derivative_of_spatial_metric(                                   \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          deriv_spatial_metric,                                            \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          extrinsic_curvature);                                            \\\n  template void gr::time_derivative_of_spatial_metric(                     \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>  \\\n          dt_spatial_metric,                                               \\\n      const Scalar<DTYPE(data)>& lapse,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,           \\\n      const tnsr::iJ<DTYPE(data), DIM(data), FRAME(data)>& deriv_shift,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric, \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                \\\n          deriv_spatial_metric,                                            \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                 \\\n          extrinsic_curvature);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the time derivative of the spatial metric from extrinsic\n * curvature, lapse, shift, and their time derivatives.\n *\n * \\details Computes the derivative as (see e.g. \\cite BaumgarteShapiro, Eq.\n * (2.134)):\n *\n * \\f{equation}\n * \\partial_t \\gamma_{ij} = -2 \\alpha K_{ij} + 2 \\nabla_{(i} \\beta_{j)}\n *   = -2 \\alpha K_{ij} + \\beta^k \\partial_k \\gamma_{ij}\n *     + \\gamma_{ik} \\partial_j \\beta^k + \\gamma_{jk} \\partial_i \\beta^k\n * \\f}\n *\n * where \\f$\\alpha\\f$ is the lapse, \\f$\\beta^i\\f$ is the shift,\n * \\f$\\gamma_{ij}\\f$ is the spatial metric and \\f$K_{ij}\\f$ is the extrinsic\n * curvature.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid time_derivative_of_spatial_metric(\n    gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> dt_spatial_metric,\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> time_derivative_of_spatial_metric(\n    const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, SpatialDim, Frame>& shift,\n    const tnsr::iJ<DataType, SpatialDim, Frame>& deriv_shift,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& deriv_spatial_metric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature);\n/// @}\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/TortoiseCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/TortoiseCoordinates.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Simd/Simd.hpp\"\n\nnamespace gr {\n\ntemplate <typename DataType>\nDataType tortoise_radius_from_boyer_lindquist_minus_r_plus(\n    const DataType& r_minus_r_plus, const double mass,\n    const double dimensionless_spin) {\n  const double r_plus = 1.0 + sqrt(1.0 - square(dimensionless_spin));\n  return r_minus_r_plus + r_plus * mass +\n         (r_plus * log(0.5 * r_minus_r_plus / mass) +\n          (r_plus - 2.0) * log(0.5 * r_minus_r_plus / mass + r_plus - 1.0)) *\n             mass / (r_plus - 1.0);\n}\n\ntemplate <typename DataType>\nDataType boyer_lindquist_radius_minus_r_plus_from_tortoise(\n    const DataType& r_star, const double mass,\n    const double dimensionless_spin) {\n  const auto residual = [&r_star, &mass, &dimensionless_spin](\n                            const auto r_minus_r_plus, const size_t i = 0) {\n    if constexpr (simd::is_batch<\n                      std::decay_t<decltype(r_minus_r_plus)>>::value) {\n      return tortoise_radius_from_boyer_lindquist_minus_r_plus(\n                 r_minus_r_plus, 1.0, dimensionless_spin) -\n             simd::load_unaligned(&(get_element(r_star, i))) / mass;\n    } else {\n      return tortoise_radius_from_boyer_lindquist_minus_r_plus(\n                 r_minus_r_plus, 1.0, dimensionless_spin) -\n             get_element(r_star, i) / mass;\n    }\n  };\n  // Possible performance optimization: tighten these bounds, e.g. by treating\n  // small and large tortoise radii separately.\n  const auto lower_bound = make_with_value<DataType>(r_star, 1e-14);\n  DataType upper_bound = blaze::max(r_star / mass, 5.0);\n  upper_bound =\n      RootFinder::toms748(residual, lower_bound, upper_bound, 0.0, 1e-14) *\n      mass;\n  return upper_bound;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template DTYPE(data) tortoise_radius_from_boyer_lindquist_minus_r_plus( \\\n      const DTYPE(data) & r_minus_r_plus, double mass,                    \\\n      double dimensionless_spin);                                         \\\n  template DTYPE(data) boyer_lindquist_radius_minus_r_plus_from_tortoise( \\\n      const DTYPE(data) & r_star, double mass, double dimensionless_spin);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/TortoiseCoordinates.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace gr {\n\n/*!\n * \\brief Computes the tortoise coordinates radius from the Boyer-Lindquist\n * radius.\n *\n * This function evaluates the transformation from tortoise coordinates $r_*$ to\n * Boyer-Lindquist radius $r$:\n * \\begin{equation}\n * r_* = r + \\frac{2 M}{r_+ - r_-}\\left(\n *   r_+ \\ln(\\frac{r - r_+}{2 M}) - r_- \\ln(\\frac{r - r_-}{2 M}) \\right)\n * \\end{equation}\n * where $r_\\pm = M \\pm \\sqrt{M^2 - a^2}$.\n *\n * This transformation has the Jacobian\n * \\begin{equation}\n *   \\frac{dr_*}{dr} = \\frac{r^2 + a^2}{\\Delta}\n * \\end{equation}\n * with $\\Delta = r^2 - 2 M r + a^2$.\n *\n * \\param r_minus_r_plus Boyer-Lindquist radius minus $r_+$: $r - r_+$.\n * \\param mass Kerr mass parameter $M$.\n * \\param dimensionless_spin Kerr dimensionless spin parameter $\\chi=a/M$.\n * \\return Tortoise coordinates radius $r_*$.\n */\ntemplate <typename DataType>\nDataType tortoise_radius_from_boyer_lindquist_minus_r_plus(\n    const DataType& r_minus_r_plus, double mass, double dimensionless_spin);\n\n/*!\n * \\brief Computes the Boyer-Lindquist radius from tortoise coordinates.\n *\n * This function inverts the transformation from tortoise coordinates radius\n * $r_*$ to Boyer-Lindquist radius $r$:\n * \\begin{equation}\n * r_* = r + \\frac{2 M}{r_+ - r_-}\\left(\n *   r_+ \\ln(\\frac{r - r_+}{2 M}) - r_- \\ln(\\frac{r - r_-}{2 M}) \\right)\n * \\end{equation}\n * where $r_\\pm = M \\pm \\sqrt{M^2 - a^2}$.\n *\n * It performs a numerical rootfind to invert the above equation.\n *\n * \\param r_star Tortoise coordinate $r_*$.\n * \\param mass Kerr mass parameter $M$.\n * \\param dimensionless_spin Kerr dimensionless spin parameter $\\chi=a/M$.\n * \\return Boyer-Lindquist radius minus $r_+$: $r - r_+$.\n */\ntemplate <typename DataType>\nDataType boyer_lindquist_radius_minus_r_plus_from_tortoise(\n    const DataType& r_star, double mass, double dimensionless_spin);\n\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/WeylElectric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> weyl_electric(\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  tnsr::ii<DataType, SpatialDim, Frame> weyl_electric_part{};\n  weyl_electric<DataType, SpatialDim, Frame>(make_not_null(&weyl_electric_part),\n                                             spatial_ricci, extrinsic_curvature,\n                                             inverse_spatial_metric);\n  return weyl_electric_part;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_electric(\n    const gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*>\n        weyl_electric_part,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  *weyl_electric_part = spatial_ricci;\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        for (size_t l = 0; l < SpatialDim; ++l) {\n          weyl_electric_part->get(i, j) +=\n              inverse_spatial_metric.get(k, l) *\n              (extrinsic_curvature.get(k, l) * extrinsic_curvature.get(i, j) -\n               extrinsic_curvature.get(i, l) * extrinsic_curvature.get(k, j));\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_electric_scalar(\n    const gsl::not_null<Scalar<DataType>*> weyl_electric_scalar_result,\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_electric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  *weyl_electric_scalar_result =\n      make_with_value<Scalar<DataType>>(get<0, 0>(inverse_spatial_metric), 0.0);\n\n  auto weyl_electric_up_down =\n      make_with_value<tnsr::Ij<DataType, SpatialDim, Frame>>(\n          get<0, 0>(inverse_spatial_metric), 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        weyl_electric_up_down.get(j, k) +=\n            weyl_electric.get(i, k) * inverse_spatial_metric.get(i, j);\n      }\n    }\n  }\n  for (size_t j = 0; j < SpatialDim; ++j) {\n    for (size_t k = 0; k < SpatialDim; ++k) {\n      if (UNLIKELY(j == 0 and k == 0)) {\n        get(*weyl_electric_scalar_result) =\n            weyl_electric_up_down.get(j, k) * weyl_electric_up_down.get(k, j);\n      } else {\n        get(*weyl_electric_scalar_result) +=\n            weyl_electric_up_down.get(j, k) * weyl_electric_up_down.get(k, j);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> weyl_electric_scalar(\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_electric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  Scalar<DataType> weyl_electric_scalar_result{};\n  weyl_electric_scalar<DataType, SpatialDim, Frame>(\n      make_not_null(&weyl_electric_scalar_result), weyl_electric,\n      inverse_spatial_metric);\n  return weyl_electric_scalar_result;\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)> gr::weyl_electric(  \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_ricci,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          extrinsic_curvature,                                               \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spatial_metric);                                           \\\n  template void gr::weyl_electric(                                           \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          weyl_electric_part,                                                \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_ricci,    \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          extrinsic_curvature,                                               \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spatial_metric);                                           \\\n  template Scalar<DTYPE(data)> gr::weyl_electric_scalar(                     \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& weyl_electric,    \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spatial_metric);                                           \\\n  template void gr::weyl_electric_scalar(                                    \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> weyl_electric_scalar_result, \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& weyl_electric,    \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          inverse_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/WeylElectric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the electric part of the Weyl tensor in vacuum.\n *\n * \\details Computes the electric part of the Weyl tensor in vacuum \\f$E_{ij}\\f$\n * as: \\f$ E_{ij} = R_{ij} + KK_{ij} - K^m_{i}K_{mj}\\f$ where \\f$R_{ij}\\f$ is\n * the spatial Ricci tensor, \\f$K_{ij}\\f$ is the extrinsic curvature, and\n * \\f$K\\f$ is the trace of \\f$K_{ij}\\f$. An additional definition is \\f$E_{ij} =\n * n^a n^b C_{a i b j}\\f$, where \\f$n\\f$ is the unit-normal to the hypersurface\n * and \\f$C\\f$ is the Weyl tensor consistent with the conventions\n * in \\cite Boyle2019kee.\n * \\note This needs additional terms for computations in a non-vacuum.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> weyl_electric(\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_electric(\n    gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> weyl_electric_part,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the scalar \\f$E_{ij} E^{ij}\\f$ from the electric\n * part of the Weyl tensor \\f$E_{ij}\\f$.\n *\n * \\details Computes the scalar \\f$E_{ij} E^{ij}\\f$ from the electric part\n * of the Weyl tensor \\f$E_{ij}\\f$ and the inverse spatial metric\n * \\f$\\gamma^{ij}\\f$, i.e. \\f$E_{ij} = \\gamma^{ik}\\gamma^{jl}E_{ij}E_{kl}\\f$.\n *\n * \\note The electric part of the Weyl tensor in vacuum is available via\n * gr::weyl_electric(). The electric part of the Weyl tensor needs additional\n * terms for matter.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> weyl_electric_scalar(\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_electric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_electric_scalar(\n    gsl::not_null<Scalar<DataType>*> weyl_electric_scalar_result,\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_electric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n/// @}\n\nnamespace Tags {\n/// Compute item for the electric part of the weyl tensor in vacuum\n/// Computed from the SpatialRicci, ExtrinsicCurvature, and InverseSpatialMetric\n///\n/// Can be retrieved using gr::Tags::WeylElectric\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct WeylElectricCompute : WeylElectric<DataType, SpatialDim, Frame>,\n                             db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::SpatialRicci<DataType, SpatialDim, Frame>,\n                 gr::Tags::ExtrinsicCurvature<DataType, SpatialDim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::ii<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*>,\n      const tnsr::ii<DataType, SpatialDim, Frame>&,\n      const tnsr::ii<DataType, SpatialDim, Frame>&,\n      const tnsr::II<DataType, SpatialDim, Frame>&)>(\n      &weyl_electric<DataType, SpatialDim, Frame>);\n\n  using base = WeylElectric<DataType, SpatialDim, Frame>;\n};\n\n/// Can be retrieved using gr::Tags::WeylElectricScalar\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct WeylElectricScalarCompute : WeylElectricScalar<DataType>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::WeylElectric<DataType, SpatialDim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<Scalar<DataType>*>,\n                           const tnsr::ii<DataType, SpatialDim, Frame>&,\n                           const tnsr::II<DataType, SpatialDim, Frame>&)>(\n          &gr::weyl_electric_scalar<DataType, SpatialDim, Frame>);\n\n  using base = WeylElectricScalar<DataType>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/WeylMagnetic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/WeylMagnetic.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gr {\ntemplate <typename Frame, typename DataType>\ntnsr::ii<DataType, 3, Frame> weyl_magnetic(\n    const tnsr::ijj<DataType, 3, Frame>& grad_extrinsic_curvature,\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const Scalar<DataType>& sqrt_det_spatial_metric) {\n  auto weyl_magnetic_part = make_with_value<tnsr::ii<DataType, 3, Frame>>(\n      get<0, 0>(spatial_metric), 0.0);\n  weyl_magnetic<Frame, DataType>(make_not_null(&weyl_magnetic_part),\n                                 grad_extrinsic_curvature, spatial_metric,\n                                 sqrt_det_spatial_metric);\n\n  return weyl_magnetic_part;\n}\n\ntemplate <typename Frame, typename DataType>\nvoid weyl_magnetic(\n    const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> weyl_magnetic_part,\n    const tnsr::ijj<DataType, 3, Frame>& grad_extrinsic_curvature,\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const Scalar<DataType>& sqrt_det_spatial_metric) {\n  auto grad_extrinsic_curvature_cross_spatial_metric =\n      make_with_value<tnsr::ij<DataType, 3, Frame>>(get<0, 0>(spatial_metric),\n                                                    0.0);\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (LeviCivitaIterator<3> it; it; ++it) {\n        grad_extrinsic_curvature_cross_spatial_metric.get(i, j) +=\n            it.sign() * grad_extrinsic_curvature.get(it[2], it[1], i) *\n            spatial_metric.get(j, it[0]);\n      }\n    }\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      weyl_magnetic_part->get(i, j) =\n          (grad_extrinsic_curvature_cross_spatial_metric.get(i, j) +\n           grad_extrinsic_curvature_cross_spatial_metric.get(j, i)) *\n          (0.5 / get(sqrt_det_spatial_metric));\n    }\n  }\n}\n\ntemplate <typename Frame, typename DataType>\nvoid weyl_magnetic_scalar(\n    const gsl::not_null<Scalar<DataType>*> weyl_magnetic_scalar_result,\n    const tnsr::ii<DataType, 3, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, 3, Frame>& inverse_spatial_metric) {\n  auto weyl_magnetic_up_down = make_with_value<tnsr::Ij<DataType, 3, Frame>>(\n      get<0, 0>(inverse_spatial_metric), 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        weyl_magnetic_up_down.get(j, k) +=\n            weyl_magnetic.get(i, k) * inverse_spatial_metric.get(i, j);\n      }\n    }\n  }\n  for (size_t j = 0; j < 3; ++j) {\n    for (size_t k = 0; k < 3; ++k) {\n      if (UNLIKELY(j == 0 and k == 0)) {\n        get(*weyl_magnetic_scalar_result) =\n            weyl_magnetic_up_down.get(j, k) * weyl_magnetic_up_down.get(k, j);\n      } else {\n        get(*weyl_magnetic_scalar_result) +=\n            weyl_magnetic_up_down.get(j, k) * weyl_magnetic_up_down.get(k, j);\n      }\n    }\n  }\n}\n\ntemplate <typename Frame, typename DataType>\nScalar<DataType> weyl_magnetic_scalar(\n    const tnsr::ii<DataType, 3, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, 3, Frame>& inverse_spatial_metric) {\n  Scalar<DataType> weyl_magnetic_scalar_result{\n      get<0, 0>(inverse_spatial_metric)};\n  weyl_magnetic_scalar<Frame, DataType>(\n      make_not_null(&weyl_magnetic_scalar_result), weyl_magnetic,\n      inverse_spatial_metric);\n  return weyl_magnetic_scalar_result;\n}\n}  // namespace gr\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                  \\\n  template tnsr::ii<DTYPE(data), 3, FRAME(data)> gr::weyl_magnetic(           \\\n      const tnsr::ijj<DTYPE(data), 3, FRAME(data)>& grad_extrinsic_curvature, \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& spatial_metric,            \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric);                    \\\n  template void gr::weyl_magnetic(                                            \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), 3, FRAME(data)>*>             \\\n          weyl_magnetic_part,                                                 \\\n      const tnsr::ijj<DTYPE(data), 3, FRAME(data)>& grad_extrinsic_curvature, \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& spatial_metric,            \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric);                    \\\n  template Scalar<DTYPE(data)> gr::weyl_magnetic_scalar(                      \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& weyl_magnetic,             \\\n      const tnsr::II<DTYPE(data), 3, FRAME(data)>& inverse_spatial_metric);   \\\n  template void gr::weyl_magnetic_scalar(                                     \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> weyl_magnetic_scalar_result,  \\\n      const tnsr::ii<DTYPE(data), 3, FRAME(data)>& weyl_magnetic,             \\\n      const tnsr::II<DTYPE(data), 3, FRAME(data)>& inverse_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/WeylMagnetic.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the magnetic part of the Weyl tensor.\n *\n * \\details Computes the magnetic part of the Weyl tensor \\f$B_{ij}\\f$\n * as:\n *\n * \\f{align}{\n * B_{ij} =\n * \\left(1/\\sqrt{\\det\\gamma}\\right)D_{k}K_{l(i}\\gamma_{j)m}\\epsilon^{mlk} \\f}\n *\n * where \\f$\\epsilon^{ijk}\\f$ is the spatial Levi-Civita symbol,\n * \\f$K_{ij}\\f$\n * is the extrinsic curvature, \\f$\\gamma_{jm} \\f$ is the spatial metric,\n * and \\f$D_i\\f$ is spatial covariant derivative.\n */\ntemplate <typename Frame, typename DataType>\ntnsr::ii<DataType, 3, Frame> weyl_magnetic(\n    const tnsr::ijj<DataType, 3, Frame>& grad_extrinsic_curvature,\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const Scalar<DataType>& sqrt_det_spatial_metric);\n\ntemplate <typename Frame, typename DataType>\nvoid weyl_magnetic(\n    gsl::not_null<tnsr::ii<DataType, 3, Frame>*> weyl_magnetic_part,\n    const tnsr::ijj<DataType, 3, Frame>& grad_extrinsic_curvature,\n    const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n    const Scalar<DataType>& sqrt_det_spatial_metric);\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the scalar \\f$B_{ij} B^{ij}\\f$ from the magnetic\n * part of the Weyl tensor \\f$B_{ij}\\f$.\n *\n * \\details Computes the scalar \\f$B_{ij} B^{ij}\\f$ from the magnetic part\n * of the Weyl tensor \\f$B_{ij}\\f$ and the inverse spatial metric\n * \\f$\\gamma^{ij}\\f$, i.e. \\f$B_{ij} = \\gamma^{ik}\\gamma^{jl}B_{ij}B_{kl}\\f$.\n *\n * \\note The magnetic part of the Weyl tensor in vacuum is available via\n * `gr::weyl_magnetic()`. The magnetic part of the Weyl tensor needs additional\n * terms for matter.\n */\ntemplate <typename Frame, typename DataType>\nScalar<DataType> weyl_magnetic_scalar(\n    const tnsr::ii<DataType, 3, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, 3, Frame>& inverse_spatial_metric);\n\ntemplate <typename Frame, typename DataType>\nvoid weyl_magnetic_scalar(\n    gsl::not_null<Scalar<DataType>*> weyl_magnetic_scalar_result,\n    const tnsr::ii<DataType, 3, Frame>& weyl_magnetic,\n    const tnsr::II<DataType, 3, Frame>& inverse_spatial_metric);\n/// @}\n\nnamespace Tags {\n/// Compute item for the magnetic part of the weyl tensor in vacuum\n/// Computed from the `ExtrinsicCurvature` and `SpatialMetric`\n///\n/// Can be retrieved using gr::Tags::WeylMagnetic\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct WeylMagneticCompute : WeylMagnetic<DataType, Dim, Frame>,\n                             db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gr::Tags::CovariantDerivativeOfExtrinsicCurvature<DataType, Dim, Frame>,\n      gr::Tags::SpatialMetric<DataType, Dim, Frame>,\n      gr::Tags::SqrtDetSpatialMetric<DataType>>;\n\n  using return_type = tnsr::ii<DataType, Dim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ii<DataType, Dim, Frame>*>,\n      const tnsr::ijj<DataType, Dim, Frame>&,\n      const tnsr::ii<DataType, Dim, Frame>&, const Scalar<DataType>&)>(\n      &weyl_magnetic<Frame, DataType>);\n\n  using base = WeylMagnetic<DataType, Dim, Frame>;\n};\n\n/// Can be retrieved using gr::Tags::`WeylMagneticScalar`\n/// Computes magnetic part of the Weyl tensor\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct WeylMagneticScalarCompute : WeylMagneticScalar<DataType>,\n                                   db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::WeylMagnetic<DataType, Dim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, Dim, Frame>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataType>*>, const tnsr::ii<DataType, Dim, Frame>&,\n      const tnsr::II<DataType, Dim, Frame>&)>(\n      &gr::weyl_magnetic_scalar<Frame, DataType>);\n\n  using base = WeylMagneticScalar<DataType>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/WeylPropagating.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/WeylPropagating.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> weyl_propagating(\n    const tnsr::ii<DataType, SpatialDim, Frame>& ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::I<DataType, SpatialDim, Frame>& unit_interface_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& projection_IJ,\n    const tnsr::ii<DataType, SpatialDim, Frame>& projection_ij,\n    const tnsr::Ij<DataType, SpatialDim, Frame>& projection_Ij,\n    const double sign) {\n  tnsr::ii<DataType, SpatialDim, Frame> weyl_prop(\n      get_size(get<0>(unit_interface_normal_vector)));\n  weyl_propagating<DataType, SpatialDim, Frame>(\n      make_not_null(&weyl_prop), ricci, extrinsic_curvature,\n      inverse_spatial_metric, cov_deriv_extrinsic_curvature,\n      unit_interface_normal_vector, projection_IJ, projection_ij, projection_Ij,\n      sign);\n  return weyl_prop;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_propagating(\n    const gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> weyl_prop_u8,\n    const tnsr::ii<DataType, SpatialDim, Frame>& ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::I<DataType, SpatialDim, Frame>& unit_interface_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& projection_IJ,\n    const tnsr::ii<DataType, SpatialDim, Frame>& projection_ij,\n    const tnsr::Ij<DataType, SpatialDim, Frame>& projection_Ij,\n    const double sign) {\n  ASSERT((sign == 1.) or (sign == -1.),\n         \"Calculation of weyl propagating modes accepts only +1/-1 to indicate \"\n         \"which of U8+/- is needed.\");\n  set_number_of_grid_points(weyl_prop_u8, unit_interface_normal_vector);\n\n  TempBuffer<tmpl::list<::Tags::Tempii<0, SpatialDim, Frame, DataType>>>\n      unprojected_weyl_prop_u8_vars(\n          get_size(get<0>(unit_interface_normal_vector)));\n\n  auto& unprojected_weyl_prop_u8 =\n      get<::Tags::Tempii<0, SpatialDim, Frame, DataType>>(\n          unprojected_weyl_prop_u8_vars);\n\n  gr::weyl_electric(make_not_null(&unprojected_weyl_prop_u8), ricci,\n                    extrinsic_curvature, inverse_spatial_metric);\n\n  // Compute the portion that the projections act on\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {  // Symmetry\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        unprojected_weyl_prop_u8.get(i, j) -=\n            sign * unit_interface_normal_vector.get(k) *\n            (cov_deriv_extrinsic_curvature.get(k, i, j) -\n             0.5 * (cov_deriv_extrinsic_curvature.get(j, i, k) +\n                    cov_deriv_extrinsic_curvature.get(i, j, k)));\n      }\n    }\n  }\n\n  // Now project\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = i; j < SpatialDim; ++j) {  // Symmetry\n      weyl_prop_u8->get(i, j) = 0.;\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        for (size_t l = 0; l < SpatialDim; ++l) {\n          weyl_prop_u8->get(i, j) +=\n              (projection_Ij.get(k, i) * projection_Ij.get(l, j) -\n               0.5 * projection_IJ.get(k, l) * projection_ij.get(i, j)) *\n              unprojected_weyl_prop_u8.get(k, l);\n        }\n      }\n    }\n  }\n}\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)> gr::weyl_propagating( \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& ricci,              \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          extrinsic_curvature,                                                 \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          cov_deriv_extrinsic_curvature,                                       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                      \\\n          unit_interface_normal_vector,                                        \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>& projection_IJ,      \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& projection_ij,      \\\n      const tnsr::Ij<DTYPE(data), DIM(data), FRAME(data)>& projection_Ij,      \\\n      const double sign);                                                      \\\n  template void gr::weyl_propagating(                                          \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>      \\\n          weyl_prop_u8,                                                        \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& ricci,              \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          extrinsic_curvature,                                                 \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                     \\\n          inverse_spatial_metric,                                              \\\n      const tnsr::ijj<DTYPE(data), DIM(data), FRAME(data)>&                    \\\n          cov_deriv_extrinsic_curvature,                                       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>&                      \\\n          unit_interface_normal_vector,                                        \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>& projection_IJ,      \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& projection_ij,      \\\n      const tnsr::Ij<DTYPE(data), DIM(data), FRAME(data)>& projection_Ij,      \\\n      const double sign);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/WeylPropagating.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the propagating modes of the Weyl tensor\n *\n * \\details The Weyl tensor evolution system in vacuum has six characteristic\n * fields, of which two (\\f$ U^{8\\pm}\\f$) are proportional to the\n * Newman-Penrose components of the Weyl tensor \\f$\\Psi_4\\f$ and \\f$\\Psi_0\\f$.\n * These represent the true gravitational-wave degrees of freedom, and\n * can be written down in terms of \\f$3+1\\f$ quantities as\n * \\cite Kidder2004rw (see Eq. 75):\n *\n * \\f{align}\n * U^{8\\pm}_{ij} &= \\left(P^{k}_i P^{l}_j - \\frac{1}{2} P_{ij} P^{kl}\\right)\n *                  \\left(R_{kl} + K K_{kl} - K_k^m K_{ml}\n *                       \\mp n^m \\nabla_m K_{kl} \\pm n^m \\nabla_{(k}K_{l)m}\n *                       \\right),\\\\\n *               &= \\left(P^{k}_i P^{l}_j - \\frac{1}{2} P_{ij} P^{kl}\\right)\n *                  \\left(E_{kl} \\mp n^m \\nabla_m K_{kl}\n *                        \\pm n^m \\nabla_{(k}K_{l)m}\\right),\n * \\f}\n *\n * where \\f$R_{ij}\\f$ is the spatial Ricci tensor, \\f$K_{ij}\\f$ is the\n * extrinsic curvature, \\f$K\\f$ is the trace of \\f$K_{ij}\\f$, \\f$E_{ij}\\f$ is\n * the electric part of the Weyl tensor in vacuum, \\f$n^i\\f$ is the outward\n * directed unit normal vector to the interface, \\f$\\nabla_i\\f$ denotes the\n * covariant derivative, and \\f$P^{ij}\\f$ and its index-raised and lowered forms\n * project tensors transverse to \\f$n^i\\f$.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> weyl_propagating(\n    const tnsr::ii<DataType, SpatialDim, Frame>& ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::I<DataType, SpatialDim, Frame>& unit_interface_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& projection_IJ,\n    const tnsr::ii<DataType, SpatialDim, Frame>& projection_ij,\n    const tnsr::Ij<DataType, SpatialDim, Frame>& projection_Ij, double sign);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_propagating(\n    gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> weyl_prop_u8,\n    const tnsr::ii<DataType, SpatialDim, Frame>& ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::I<DataType, SpatialDim, Frame>& unit_interface_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& projection_IJ,\n    const tnsr::ii<DataType, SpatialDim, Frame>& projection_ij,\n    const tnsr::Ij<DataType, SpatialDim, Frame>& projection_Ij, double sign);\n/// @}\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/WeylTypeD1.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/GeneralRelativity/WeylTypeD1.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::Ij<DataType, SpatialDim, Frame> up_down_from_lower_rank_2_tensor(\n    const tnsr::ii<DataType, SpatialDim, Frame>& lower_rank_2_tensor,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  auto result = make_with_value<tnsr::Ij<DataType, SpatialDim, Frame>>(\n      get<0, 0>(inverse_spatial_metric), 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        result.get(j, k) +=\n            lower_rank_2_tensor.get(i, k) * inverse_spatial_metric.get(i, j);\n      }\n    }\n  }\n  return result;\n}\n\n}  // namespace\n\nnamespace gr {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_type_D1(\n    const gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> weyl_type_D1,\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  auto weyl_electric_up_down =\n      up_down_from_lower_rank_2_tensor(weyl_electric, inverse_spatial_metric);\n\n  auto upper_weyl_electric =\n      make_with_value<tnsr::II<DataType, SpatialDim, Frame>>(\n          get<0, 0>(inverse_spatial_metric), 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t l = i; l < SpatialDim; ++l) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        upper_weyl_electric.get(i, l) +=\n            weyl_electric_up_down.get(i, j) * inverse_spatial_metric.get(j, l);\n      }\n    }\n  }\n\n  // compute factor a = 16.0 * E_{ij} E^{ij}:\n  Scalar<DataType> a =\n      weyl_electric_scalar(weyl_electric, inverse_spatial_metric);\n  get(a) *= 16.0;\n\n  // Compute factor b = -64 E^k_i E^{ij} E_{jk}:\n  auto b =\n      make_with_value<Scalar<DataType>>(get<0, 0>(inverse_spatial_metric), 0.0);\n\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        get(b) += weyl_electric_up_down.get(k, i) *\n                  upper_weyl_electric.get(i, j) * weyl_electric.get(j, k);\n      }\n    }\n  }\n  get(b) *= -64.0;\n\n  // compute deviation from type D using measure D1:\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      weyl_type_D1->get(i, j) = (get(a) / 12.0) * spatial_metric.get(i, j) -\n                                (get(b) / get(a) * weyl_electric.get(i, j));\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        weyl_type_D1->get(i, j) -=\n            4.0 * (weyl_electric_up_down.get(k, i) * weyl_electric.get(j, k));\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> weyl_type_D1(\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  tnsr::ii<DataType, SpatialDim, Frame> weyl_type_D1_result{};\n  weyl_type_D1<DataType, SpatialDim, Frame>(\n        make_not_null(&weyl_type_D1_result), weyl_electric, spatial_metric,\n        inverse_spatial_metric);\n  return weyl_type_D1_result;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_type_D1_scalar(\n    const gsl::not_null<Scalar<DataType>*> weyl_type_D1_scalar_result,\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_type_D1,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  *weyl_type_D1_scalar_result =\n      make_with_value<Scalar<DataType>>(get<0, 0>(inverse_spatial_metric), 0.0);\n\n  auto weyl_type_D1_up_down =\n      up_down_from_lower_rank_2_tensor(weyl_type_D1, inverse_spatial_metric);\n\n  for (size_t j = 0; j < SpatialDim; ++j) {\n    for (size_t k = 0; k < SpatialDim; ++k) {\n      if (UNLIKELY(j == 0 and k == 0)) {\n        get(*weyl_type_D1_scalar_result) =\n            weyl_type_D1_up_down.get(j, k) * weyl_type_D1_up_down.get(k, j);\n      } else {\n        get(*weyl_type_D1_scalar_result) +=\n            weyl_type_D1_up_down.get(j, k) * weyl_type_D1_up_down.get(k, j);\n      }\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> weyl_type_D1_scalar(\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_type_D1,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric) {\n  Scalar<DataType> weyl_type_D1_scalar_result{};\n  weyl_type_D1_scalar<DataType, SpatialDim, Frame>(\n      make_not_null(&weyl_type_D1_scalar_result), weyl_type_D1,\n      inverse_spatial_metric);\n  return weyl_type_D1_scalar_result;\n}\n\n}  // namespace gr\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)> gr::weyl_type_D1(  \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& weyl_electric,   \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,  \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          inverse_spatial_metric);                                          \\\n  template void gr::weyl_type_D1(                                           \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>   \\\n          weyl_type_D1,                                                     \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& weyl_electric,   \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& spatial_metric,  \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          inverse_spatial_metric);                                          \\\n  template void gr::weyl_type_D1_scalar(                                    \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> weyl_type_D1_scalar_result, \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& weyl_type_D1,    \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          inverse_spatial_metric);                                          \\\n  template Scalar<DTYPE(data)> gr::weyl_type_D1_scalar(                     \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& weyl_type_D1,    \\\n      const tnsr::II<DTYPE(data), DIM(data), FRAME(data)>&                  \\\n          inverse_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/GeneralRelativity/WeylTypeD1.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace gr {\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes a quantity measuring how far from type D spacetime is.\n *\n * \\details Computes a quantity measuring how far from type D spacetime is,\n * using measure D1 [Eq. (8) of \\cite Bhagwat2017tkm]:\n *\n * \\f{align}{\n * \\frac{a}{12} \\gamma_{ij} - \\frac{b}{a} E_{ij} - 4\n * E_{i}^{k} E_{jk} = 0 \\f}\n *\n * where \\f$\\gamma_{ij}\\f$ is the spatial metric and \\f$E_{ij}\\f$ is the\n * electric part ofthe Weyl tensor.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> weyl_type_D1(\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_type_D1(\n    gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*> weyl_type_D1,\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_electric,\n    const tnsr::ii<DataType, SpatialDim, Frame>& spatial_metric,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n/// @}\n\n/// @{\n/*!\n * \\ingroup GeneralRelativityGroup\n * \\brief Computes the scalar \\f$D_{ij} D^{ij}\\f$ , a measure of a spacetime's\n * devitation from type D.\n *\n * \\details Computes the scalar \\f$D_{ij} D^{ij}\\f$ from \\f$D_{ij}\\f$ (Eq. (8)\n * of \\cite Bhagwat2017tkm] and the inverse spatial metric \\f$\\gamma^{ij}\\f$,\n * i.e. \\f$D = \\gamma^{ik}\\gamma^{jl}D_{ij}D_{kl}\\f$.\n *\n * \\note The Weyl Type D1 \\f$D_{ij}\\f$ is available via gr::weyl_type_D1.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid weyl_type_D1_scalar(\n    gsl::not_null<Scalar<DataType>*> weyl_type_D1_scalar_result,\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_type_D1,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> weyl_type_D1_scalar(\n    const tnsr::ii<DataType, SpatialDim, Frame>& weyl_type_D1,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric);\n\n/// @}\n\nnamespace Tags {\n/// Compute item for WeylTypeD1\n/// Computed from WeylElectric, SpatialMetric, and InverseSpatialMetric\n///\n/// Can be retrieved using gr::Tags::WeylTypeD1\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct WeylTypeD1Compute : WeylTypeD1<DataType, SpatialDim, Frame>,\n                           db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::WeylElectric<DataType, SpatialDim, Frame>,\n                 gr::Tags::SpatialMetric<DataType, SpatialDim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = tnsr::ii<DataType, SpatialDim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::ii<DataType, SpatialDim, Frame>*>,\n      const tnsr::ii<DataType, SpatialDim, Frame>&,\n      const tnsr::ii<DataType, SpatialDim, Frame>&,\n      const tnsr::II<DataType, SpatialDim, Frame>&)>(\n      &weyl_type_D1<DataType, SpatialDim, Frame>);\n\n  using base = WeylTypeD1<DataType, SpatialDim, Frame>;\n};\n\n/// Can be retrieved using gr::Tags::WeylTypeD1Scalar\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nstruct WeylTypeD1ScalarCompute : WeylTypeD1Scalar<DataType>, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<gr::Tags::WeylTypeD1<DataType, SpatialDim, Frame>,\n                 gr::Tags::InverseSpatialMetric<DataType, SpatialDim, Frame>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<Scalar<DataType>*>,\n                           const tnsr::ii<DataType, SpatialDim, Frame>&,\n                           const tnsr::II<DataType, SpatialDim, Frame>&)>(\n          &gr::weyl_type_D1_scalar<DataType, SpatialDim, Frame>);\n\n  using base = WeylTypeD1Scalar<DataType>;\n};\n}  // namespace Tags\n}  // namespace gr\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Hydro)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ComovingMagneticField.cpp\n  InversePlasmaBeta.cpp\n  LorentzFactor.cpp\n  LowerSpatialFourVelocity.cpp\n  MagneticFieldTreatment.cpp\n  MassFlux.cpp\n  MassWeightedFluidItems.cpp\n  QuadrupoleFormula.cpp\n  SoundSpeedSquared.cpp\n  SpecificEnthalpy.cpp\n  StressEnergy.cpp\n  TransportVelocity.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ComovingMagneticField.hpp\n  InversePlasmaBeta.hpp\n  LorentzFactor.hpp\n  LowerSpatialFourVelocity.hpp\n  MagneticFieldTreatment.hpp\n  MassFlux.hpp\n  MassWeightedFluidItems.hpp\n  QuadrupoleFormula.hpp\n  SoundSpeedSquared.hpp\n  SpecificEnthalpy.hpp\n  StressEnergy.hpp\n  Tags.hpp\n  TagsDeclarations.hpp\n  Temperature.hpp\n  TransportVelocity.hpp\n  Units.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  H5\n  GeneralRelativity\n  Options\n  Serialization\n  INTERFACE\n  )\n\nadd_subdirectory(EquationsOfState)\nadd_subdirectory(InitialData)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/ComovingMagneticField.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/ComovingMagneticField.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace hydro {\n\ntemplate <typename DataType>\nvoid comoving_magnetic_field(\n    const gsl::not_null<tnsr::A<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& spatial_velocity,\n    const tnsr::I<DataType, 3>& magnetic_field,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const tnsr::I<DataType, 3>& shift,\n    const Scalar<DataType>& lapse) {\n  get<0>(*result) = get(lorentz_factor) *\n                    get(magnetic_field_dot_spatial_velocity) / get(lapse);\n\n  for (size_t i = 0; i < 3; ++i) {\n    result->get(i + 1) =\n        (magnetic_field.get(i) / get(lorentz_factor)) +\n        get<0>(*result) * (get(lapse) * spatial_velocity.get(i) - shift.get(i));\n  }\n}\n\ntemplate <typename DataType>\ntnsr::A<DataType, 3> comoving_magnetic_field(\n    const tnsr::I<DataType, 3>& spatial_velocity,\n    const tnsr::I<DataType, 3>& magnetic_field,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const tnsr::I<DataType, 3>& shift,\n    const Scalar<DataType>& lapse) {\n  tnsr::A<DataType, 3> result{};\n  comoving_magnetic_field(make_not_null(&result), spatial_velocity,\n                          magnetic_field, magnetic_field_dot_spatial_velocity,\n                          lorentz_factor, shift, lapse);\n  return result;\n}\n\ntemplate <typename DataType>\nvoid comoving_magnetic_field_one_form(\n    const gsl::not_null<tnsr::a<DataType, 3>*> result,\n    const tnsr::i<DataType, 3>& spatial_velocity_one_form,\n    const tnsr::i<DataType, 3>& magnetic_field_one_form,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const tnsr::I<DataType, 3>& shift,\n    const Scalar<DataType>& lapse) {\n  get<0>(*result) = -get(lapse) * get(lorentz_factor) *\n                    get(magnetic_field_dot_spatial_velocity);\n  for (size_t i = 0; i < 3; ++i) {\n    result->get(i + 1) = magnetic_field_one_form.get(i) / get(lorentz_factor) +\n                         get(lorentz_factor) *\n                             get(magnetic_field_dot_spatial_velocity) *\n                             spatial_velocity_one_form.get(i);\n    get<0>(*result) += shift.get(i) * result->get(i + 1);\n  }\n}\n\ntemplate <typename DataType>\ntnsr::a<DataType, 3> comoving_magnetic_field_one_form(\n    const tnsr::i<DataType, 3>& spatial_velocity_one_form,\n    const tnsr::i<DataType, 3>& magnetic_field_one_form,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const tnsr::I<DataType, 3>& shift,\n    const Scalar<DataType>& lapse) {\n  tnsr::a<DataType, 3> result{};\n  comoving_magnetic_field_one_form(\n      make_not_null(&result), spatial_velocity_one_form,\n      magnetic_field_one_form, magnetic_field_dot_spatial_velocity,\n      lorentz_factor, shift, lapse);\n  return result;\n}\n\ntemplate <typename DataType>\nvoid comoving_magnetic_field_squared(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& magnetic_field_squared,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor) {\n  get(*result) = get(magnetic_field_squared) / square(get(lorentz_factor)) +\n                 square(get(magnetic_field_dot_spatial_velocity));\n}\n\ntemplate <typename DataType>\nScalar<DataType> comoving_magnetic_field_squared(\n    const Scalar<DataType>& magnetic_field_squared,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor) {\n  Scalar<DataType> result{};\n  comoving_magnetic_field_squared(\n      make_not_null(&result), magnetic_field_squared,\n      magnetic_field_dot_spatial_velocity, lorentz_factor);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                               \\\n  template void comoving_magnetic_field(                                     \\\n      const gsl::not_null<tnsr::A<DTYPE(data), 3>*>                          \\\n          comoving_magnetic_field_result,                                    \\\n      const tnsr::I<DTYPE(data), 3>&, const tnsr::I<DTYPE(data), 3>&,        \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,                \\\n      const tnsr::I<DTYPE(data), 3>&, const Scalar<DTYPE(data)>&);           \\\n  template tnsr::A<DTYPE(data), 3> comoving_magnetic_field(                  \\\n      const tnsr::I<DTYPE(data), 3>&, const tnsr::I<DTYPE(data), 3>&,        \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,                \\\n      const tnsr::I<DTYPE(data), 3>&, const Scalar<DTYPE(data)>&);           \\\n  template void comoving_magnetic_field_one_form(                            \\\n      const gsl::not_null<tnsr::a<DTYPE(data), 3>*>                          \\\n          comoving_magnetic_field_one_form_result,                           \\\n      const tnsr::i<DTYPE(data), 3>&, const tnsr::i<DTYPE(data), 3>&,        \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,                \\\n      const tnsr::I<DTYPE(data), 3>&, const Scalar<DTYPE(data)>&);           \\\n  template tnsr::a<DTYPE(data), 3> comoving_magnetic_field_one_form(         \\\n      const tnsr::i<DTYPE(data), 3>&, const tnsr::i<DTYPE(data), 3>&,        \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,                \\\n      const tnsr::I<DTYPE(data), 3>&, const Scalar<DTYPE(data)>&);           \\\n  template void comoving_magnetic_field_squared(                             \\\n      const gsl::not_null<Scalar<DTYPE(data)>*>, const Scalar<DTYPE(data)>&, \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&);               \\\n  template Scalar<DTYPE(data)> comoving_magnetic_field_squared(              \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,                \\\n      const Scalar<DTYPE(data)>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATION\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/ComovingMagneticField.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace hydro {\n\n/// @{\n/*!\n * \\brief The comoving magnetic field vector $b^\\mu$ and one-form $b_\\mu$\n *\n * The components of the comoving magnetic field vector are:\n *\n * \\begin{align}\n * b^0 &= W B^j v^k \\gamma_{j k} / \\alpha \\\\\n * b^i &= B^i / W + B^j v^k \\gamma_{j k} u^i\n * \\end{align}\n *\n * where $u^i = W(v^i - \\beta^i/\\alpha)$.\n *\n * Using the spacetime metric, the corresponding one-form components are:\n *\n * \\begin{align}\n * b_0 &= - \\alpha W v^i B_i + \\beta^i b_i \\\\\n * b_i &= B_i / W + B^j v^k \\gamma_{j k} W v_i\n * \\end{align}\n *\n * The square of the vector is:\n *\n * \\begin{equation}\n * b^2 = B^i B^j \\gamma_{i j} / W^2 + (B^i v^j \\gamma_{i j})^2\n * \\end{equation}\n *\n * See also Eq. (5.173) in \\cite BaumgarteShapiro, with the difference that we\n * work in Heaviside-Lorentz units where the magnetic field is rescaled by\n * $1/\\sqrt{4\\pi}$, following \\cite Moesta2013dna .\n */\ntemplate <typename DataType>\nvoid comoving_magnetic_field(\n    gsl::not_null<tnsr::A<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& spatial_velocity,\n    const tnsr::I<DataType, 3>& magnetic_field,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const tnsr::I<DataType, 3>& shift,\n    const Scalar<DataType>& lapse);\n\ntemplate <typename DataType>\ntnsr::A<DataType, 3> comoving_magnetic_field(\n    const tnsr::I<DataType, 3>& spatial_velocity,\n    const tnsr::I<DataType, 3>& magnetic_field,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const tnsr::I<DataType, 3>& shift,\n    const Scalar<DataType>& lapse);\n\ntemplate <typename DataType>\nvoid comoving_magnetic_field_one_form(\n    gsl::not_null<tnsr::a<DataType, 3>*> result,\n    const tnsr::i<DataType, 3>& spatial_velocity_one_form,\n    const tnsr::i<DataType, 3>& magnetic_field_one_form,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const tnsr::I<DataType, 3>& shift,\n    const Scalar<DataType>& lapse);\n\ntemplate <typename DataType>\ntnsr::a<DataType, 3> comoving_magnetic_field_one_form(\n    const tnsr::i<DataType, 3>& spatial_velocity_one_form,\n    const tnsr::i<DataType, 3>& magnetic_field_one_form,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const tnsr::I<DataType, 3>& shift,\n    const Scalar<DataType>& lapse);\n\ntemplate <typename DataType>\nvoid comoving_magnetic_field_squared(\n    gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& magnetic_field_squared,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor);\n\ntemplate <typename DataType>\nScalar<DataType> comoving_magnetic_field_squared(\n    const Scalar<DataType>& magnetic_field_squared,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& lorentz_factor);\n/// @}\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace EquationsOfState {\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename ColdEos>,\n                                     Barotropic2D<ColdEos>, double, 2)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename ColdEos>,\n                                     Barotropic2D<ColdEos>, DataVector, 2)\n\ntemplate <typename ColdEos>\nstd::unique_ptr<EquationOfState<ColdEos::is_relativistic, 2>>\nBarotropic2D<ColdEos>::get_clone() const {\n  auto clone = std::make_unique<Barotropic2D<ColdEos>>(*this);\n  return std::unique_ptr<EquationOfState<is_relativistic, 2>>(std::move(clone));\n}\n\ntemplate <typename ColdEos>\nstd::unique_ptr<EquationOfState<ColdEos::is_relativistic, 3>>\nBarotropic2D<ColdEos>::promote_to_3d_eos() const {\n  return std::make_unique<Barotropic3D<ColdEos>>(underlying_eos_);\n}\n\ntemplate <typename ColdEos>\nbool Barotropic2D<ColdEos>::is_equal(\n    const EquationOfState<ColdEos::is_relativistic, 2>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const Barotropic2D<ColdEos>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <typename ColdEos>\nvoid Barotropic2D<ColdEos>::pup(PUP::er& p) {\n  EquationOfState<ColdEos::is_relativistic, 2>::pup(p);\n  p | underlying_eos_;\n}\ntemplate <typename ColdEos>\nBarotropic2D<ColdEos>::Barotropic2D(CkMigrateMessage* msg)\n    : EquationOfState<ColdEos::is_relativistic, 2>(msg) {}\n\ntemplate <typename ColdEos>\nbool Barotropic2D<ColdEos>::operator==(const Barotropic2D<ColdEos>& rhs) const {\n  return this->underlying_eos_ == rhs.underlying_eos_;\n}\ntemplate <typename ColdEos>\nbool Barotropic2D<ColdEos>::operator!=(const Barotropic2D<ColdEos>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <typename ColdEos>\ntemplate <class DataType>\nScalar<DataType> Barotropic2D<ColdEos>::pressure_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& /*specific_internal_energy*/) const {\n  return underlying_eos_.pressure_from_density(rest_mass_density);\n}\n\ntemplate <typename ColdEos>\ntemplate <class DataType>\nScalar<DataType> Barotropic2D<ColdEos>::pressure_from_density_and_enthalpy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& /*specific_enthalpy*/) const {\n  return underlying_eos_.pressure_from_density(rest_mass_density);\n}\n\ntemplate <typename ColdEos>\ntemplate <class DataType>\nScalar<DataType>\nBarotropic2D<ColdEos>::specific_internal_energy_from_density_and_pressure_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& /*pressure*/) const {\n  return underlying_eos_.specific_internal_energy_from_density(\n      rest_mass_density);\n}\n\ntemplate <typename ColdEos>\ntemplate <class DataType>\nScalar<DataType>\nBarotropic2D<ColdEos>::temperature_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& /*specific_internal_energy*/) const {\n  return make_with_value<Scalar<DataType>>(rest_mass_density, 0.0);\n}\n\ntemplate <typename ColdEos>\ntemplate <class DataType>\nScalar<DataType> Barotropic2D<ColdEos>::\n    specific_internal_energy_from_density_and_temperature_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& /*temperature*/) const {\n  return underlying_eos_.specific_internal_energy_from_density(\n      rest_mass_density);\n}\n\ntemplate <typename ColdEos>\ntemplate <class DataType>\nScalar<DataType> Barotropic2D<ColdEos>::chi_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& /*specific_internal_energy*/) const {\n  return underlying_eos_.chi_from_density(rest_mass_density);\n}\n\ntemplate <typename ColdEos>\ntemplate <class DataType>\nScalar<DataType> Barotropic2D<ColdEos>::\n    kappa_times_p_over_rho_squared_from_density_and_energy_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& /*specific_internal_energy*/) const {\n  return underlying_eos_.kappa_times_p_over_rho_squared_from_density(\n      rest_mass_density);\n}\n\ntemplate class Barotropic2D<EquationsOfState::PolytropicFluid<true>>;\ntemplate class Barotropic2D<EquationsOfState::PolytropicFluid<false>>;\ntemplate class Barotropic2D<PiecewisePolytropicFluid<true>>;\ntemplate class Barotropic2D<PiecewisePolytropicFluid<false>>;\ntemplate class Barotropic2D<Spectral>;\ntemplate class Barotropic2D<Enthalpy<PolytropicFluid<true>>>;\ntemplate class Barotropic2D<Enthalpy<Spectral>>;\ntemplate class Barotropic2D<Enthalpy<Enthalpy<Spectral>>>;\ntemplate class Barotropic2D<Enthalpy<Enthalpy<Enthalpy<Spectral>>>>;\n\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief A 2D equation of state representing a barotropic fluid.\n *\n *\n * The equation of state takes the form\n *\n * \\f[\n * p = p (\\rho , T, Y_e) = p(\\rho, 0, Y_e= Y_{e, \\beta})\n * \\f]\n *\n * where \\f$\\rho\\f$ is the rest mass density, \\f$T\\f$  the\n * temperature , and \\f$Y_e\\f$ the electron fraction. The temperature and\n * electron fraction are not used, so evaluating this EoS at any arbtirary\n * temeperature or electron fraction is equivalent to evaluating it at\n * zero temperature and in beta equalibrium.\n */\ntemplate <typename ColdEos>\nclass Barotropic2D : public EquationOfState<ColdEos::is_relativistic, 2> {\n public:\n  static constexpr size_t thermodynamic_dim = 2;\n  static constexpr bool is_relativistic = ColdEos::is_relativistic;\n\n  static std::string name() {\n    return \"Barotropic2D(\" + pretty_type::name<ColdEos>() + \")\";\n  }\n  static constexpr Options::String help = {\n      \"A 2D EoS which is independent of electron fraction and temperature. \"\n      \"Contains an underlying 1D EoS which is dependent only \"\n      \"on rest mass density.\"};\n  struct UnderlyingEos {\n    using type = ColdEos;\n    static std::string name() { return pretty_type::short_name<ColdEos>(); }\n    static constexpr Options::String help{\n        \"The underlying EoS which is being represented as a 2D EoS.  Must be a \"\n        \"1D EoS\"};\n  };\n\n  using options = tmpl::list<UnderlyingEos>;\n\n  Barotropic2D() = default;\n  Barotropic2D(const Barotropic2D&) = default;\n  Barotropic2D& operator=(const Barotropic2D&) = default;\n  Barotropic2D(Barotropic2D&&) = default;\n  Barotropic2D& operator=(Barotropic2D&&) = default;\n  ~Barotropic2D() override = default;\n\n  explicit Barotropic2D(const ColdEos& underlying_eos)\n      : underlying_eos_(underlying_eos){};\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(Barotropic2D, 2)\n\n  std::unique_ptr<EquationOfState<ColdEos::is_relativistic, 2>> get_clone()\n      const override;\n\n  std::unique_ptr<EquationOfState<ColdEos::is_relativistic, 3>>\n  promote_to_3d_eos() const override;\n\n  bool is_equal(\n      const EquationOfState<ColdEos::is_relativistic, 2>& rhs) const override;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  bool is_barotropic() const override { return true; }\n\n  bool operator==(const Barotropic2D<ColdEos>& rhs) const;\n\n  bool operator!=(const Barotropic2D<ColdEos>& rhs) const;\n  /// @{\n  /*!\n   * Computes the electron fraction in beta-equilibrium \\f$Y_e^{\\rm eq}\\f$ from\n   * the rest mass density \\f$\\rho\\f$ and the temperature \\f$T\\f$.\n   */\n  Scalar<double> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<double>& rest_mass_density,\n      const Scalar<double>& temperature) const override {\n    return underlying_eos_\n        .equilibrium_electron_fraction_from_density_temperature(\n            rest_mass_density, temperature);\n  }\n\n  Scalar<DataVector> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& temperature) const override {\n    return underlying_eos_\n        .equilibrium_electron_fraction_from_density_temperature(\n            rest_mass_density, temperature);\n  }\n  /// @}\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<ColdEos::is_relativistic, 2>), Barotropic2D);\n\n  /// The lower bound of the electron fraction that is valid for this EOS\n  double electron_fraction_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the electron fraction that is valid for this EOS\n  double electron_fraction_upper_bound() const override { return 1.0; }\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override {\n    return underlying_eos_.rest_mass_density_lower_bound();\n  }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return underlying_eos_.rest_mass_density_upper_bound();\n  }\n\n  /// The lower bound of the temperature that is valid for this EOS\n  double temperature_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the temperature that is valid for this EOS\n  double temperature_upper_bound() const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$ and electron fraction \\f$Y_e\\f$\n  double specific_internal_energy_lower_bound(\n      const double /*rest_mass_density*/) const override {\n    return underlying_eos_.specific_internal_energy_lower_bound();\n  }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_upper_bound(\n      const double /*rest_mass_density*/) const override {\n    return underlying_eos_.specific_internal_energy_upper_bound();\n  }\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override {\n    return underlying_eos_.specific_enthalpy_lower_bound();\n  }\n\n  /// The baryon mass for this EoS\n  double baryon_mass() const override { return underlying_eos_.baryon_mass(); }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(2)\n  ColdEos underlying_eos_;\n};\n/// \\cond\ntemplate <typename ColdEos>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID EquationsOfState::Barotropic2D<ColdEos>::my_PUP_ID = 0;\n/// \\endcond\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace EquationsOfState {\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename ColdEquilEos>,\n                                     Barotropic3D<ColdEquilEos>, double, 3)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename ColdEquilEos>,\n                                     Barotropic3D<ColdEquilEos>, DataVector, 3)\n\ntemplate <typename ColdEquilEos>\nstd::unique_ptr<EquationOfState<ColdEquilEos::is_relativistic, 3>>\nBarotropic3D<ColdEquilEos>::get_clone() const {\n  auto clone = std::make_unique<Barotropic3D<ColdEquilEos>>(*this);\n  return std::unique_ptr<EquationOfState<is_relativistic, 3>>(std::move(clone));\n}\n\ntemplate <typename ColdEquilEos>\nbool Barotropic3D<ColdEquilEos>::is_equal(\n    const EquationOfState<ColdEquilEos::is_relativistic, 3>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const Barotropic3D<ColdEquilEos>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <typename ColdEquilEos>\nvoid Barotropic3D<ColdEquilEos>::pup(PUP::er& p) {\n  EquationOfState<ColdEquilEos::is_relativistic, 3>::pup(p);\n  p | underlying_eos_;\n}\ntemplate <typename ColdEquilEos>\nBarotropic3D<ColdEquilEos>::Barotropic3D(CkMigrateMessage* msg)\n    : EquationOfState<ColdEquilEos::is_relativistic, 3>(msg) {}\n\ntemplate <typename ColdEquilEos>\nbool Barotropic3D<ColdEquilEos>::operator==(\n    const Barotropic3D<ColdEquilEos>& rhs) const {\n  return this->underlying_eos_ == rhs.underlying_eos_;\n}\ntemplate <typename ColdEquilEos>\nbool Barotropic3D<ColdEquilEos>::operator!=(\n    const Barotropic3D<ColdEquilEos>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <typename ColdEquilEos>\ntemplate <class DataType>\nScalar<DataType>\nBarotropic3D<ColdEquilEos>::pressure_from_density_and_temperature_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& /*temperature*/,\n    const Scalar<DataType>& /*electron_fraction*/) const {\n  return underlying_eos_.pressure_from_density(rest_mass_density);\n}\ntemplate <typename ColdEquilEos>\ntemplate <class DataType>\nScalar<DataType>\nBarotropic3D<ColdEquilEos>::pressure_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& /*specific_internal_energy*/,\n    const Scalar<DataType>& /*electron_fraction*/) const {\n  return underlying_eos_.pressure_from_density(rest_mass_density);\n}\ntemplate <typename ColdEquilEos>\ntemplate <class DataType>\nScalar<DataType>\nBarotropic3D<ColdEquilEos>::temperature_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& /*specific_internal_energy*/,\n    const Scalar<DataType>& /*electron_fraction*/) const {\n  return make_with_value<Scalar<DataType>>(rest_mass_density, 0.0);\n}\n\ntemplate <typename ColdEquilEos>\ntemplate <class DataType>\nScalar<DataType> Barotropic3D<ColdEquilEos>::\n    specific_internal_energy_from_density_and_temperature_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& /*temperature*/,\n        const Scalar<DataType>& /*electron_fraction*/) const {\n  return underlying_eos_.specific_internal_energy_from_density(\n      rest_mass_density);\n}\ntemplate <typename ColdEquilEos>\ntemplate <class DataType>\nScalar<DataType> Barotropic3D<ColdEquilEos>::\n    sound_speed_squared_from_density_and_temperature_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& /*temperature*/,\n        const Scalar<DataType>& /*electron_fraction*/) const {\n  // We have to do this to avoid dividing by zero\n  const DataType enthalpy_density =\n      get(underlying_eos_.pressure_from_density(rest_mass_density)) +\n      (1.0 + get(underlying_eos_.specific_internal_energy_from_density(\n                 rest_mass_density))) *\n          get(rest_mass_density);\n  return Scalar<DataType>{\n      get(rest_mass_density) *\n      get(underlying_eos_.chi_from_density(rest_mass_density)) /\n      enthalpy_density};\n}\ntemplate class Barotropic3D<EquationsOfState::PolytropicFluid<true>>;\ntemplate class Barotropic3D<EquationsOfState::PolytropicFluid<false>>;\ntemplate class Barotropic3D<PiecewisePolytropicFluid<true>>;\ntemplate class Barotropic3D<PiecewisePolytropicFluid<false>>;\ntemplate class Barotropic3D<Spectral>;\ntemplate class Barotropic3D<Enthalpy<PolytropicFluid<true>>>;\ntemplate class Barotropic3D<Enthalpy<Spectral>>;\ntemplate class Barotropic3D<Enthalpy<Enthalpy<Spectral>>>;\ntemplate class Barotropic3D<Enthalpy<Enthalpy<Enthalpy<Spectral>>>>;\n\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief A 3D equation of state representing a barotropic fluid.\n *\n *\n * The equation of state takes the form\n *\n * \\f[\n * p = p (\\rho , T, Y_e) = p(\\rho, 0, Y_e= Y_{e, \\beta})\n * \\f]\n *\n * where \\f$\\rho\\f$ is the rest mass density, \\f$T\\f$  the\n * temperature , and \\f$Y_e\\f$ the electron fraction. The temperature and\n * electron fraction are not used, so evaluating this EoS at any arbtirary\n * temeperature or electron fraction is equivalent to evaluating it at\n * zero temperature and in beta equalibrium.\n */\ntemplate <typename ColdEquilEos>\nclass Barotropic3D : public EquationOfState<ColdEquilEos::is_relativistic, 3> {\n public:\n  static constexpr size_t thermodynamic_dim = 3;\n  static constexpr bool is_relativistic = ColdEquilEos::is_relativistic;\n\n  static std::string name() {\n    return \"Barotropic3D(\" + pretty_type::name<ColdEquilEos>() + \")\";\n  }\n  static constexpr Options::String help = {\n      \"An 3D EoS which is independent of electron fraction and temperature. \"\n      \"Contains an underlying 1D EoS which is dependent only \"\n      \"on rest mass density.\"};\n  struct UnderlyingEos {\n    using type = ColdEquilEos;\n    static std::string name() {\n      return pretty_type::short_name<ColdEquilEos>();\n    }\n    static constexpr Options::String help{\n        \"The underlying Eos which is being represented as a \"\n        \"3D Eos.  Must be a 1D EoS\"};\n  };\n\n  using options = tmpl::list<UnderlyingEos>;\n\n  Barotropic3D() = default;\n  Barotropic3D(const Barotropic3D&) = default;\n  Barotropic3D& operator=(const Barotropic3D&) = default;\n  Barotropic3D(Barotropic3D&&) = default;\n  Barotropic3D& operator=(Barotropic3D&&) = default;\n  ~Barotropic3D() override = default;\n\n  explicit Barotropic3D(const ColdEquilEos& underlying_eos)\n      : underlying_eos_(underlying_eos){};\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(Barotropic3D, 3)\n\n  std::unique_ptr<EquationOfState<ColdEquilEos::is_relativistic, 3>> get_clone()\n      const override;\n\n  bool is_equal(const EquationOfState<ColdEquilEos::is_relativistic, 3>& rhs)\n      const override;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  bool is_barotropic() const override { return true; }\n\n  /// \\brief Returns `true` if the EOS is in beta-equilibrium\n  bool is_equilibrium() const override { return false; }\n\n  bool operator==(const Barotropic3D<ColdEquilEos>& rhs) const;\n\n  bool operator!=(const Barotropic3D<ColdEquilEos>& rhs) const;\n  /// @{\n  /*!\n   * Computes the electron fraction in beta-equilibrium \\f$Y_e^{\\rm eq}\\f$ from\n   * the rest mass density \\f$\\rho\\f$ and the temperature \\f$T\\f$.\n   */\n  Scalar<double> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<double>& rest_mass_density,\n      const Scalar<double>& temperature) const override {\n    return underlying_eos_\n        .equilibrium_electron_fraction_from_density_temperature(\n            rest_mass_density, temperature);\n  }\n\n  Scalar<DataVector> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& temperature) const override {\n    return underlying_eos_\n        .equilibrium_electron_fraction_from_density_temperature(\n            rest_mass_density, temperature);\n  }\n  /// @}\n  //\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<ColdEquilEos::is_relativistic, 3>),\n      Barotropic3D);\n\n  /// The lower bound of the electron fraction that is valid for this EOS\n  double electron_fraction_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the electron fraction that is valid for this EOS\n  double electron_fraction_upper_bound() const override { return 1.0; }\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override {\n    return underlying_eos_.rest_mass_density_lower_bound();\n  }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return underlying_eos_.rest_mass_density_upper_bound();\n  }\n\n  /// The lower bound of the temperature that is valid for this EOS\n  double temperature_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the temperature that is valid for this EOS\n  double temperature_upper_bound() const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$ and electron fraction \\f$Y_e\\f$\n  double specific_internal_energy_lower_bound(\n      const double /*rest_mass_density*/,\n      const double /*electron_fraction*/) const override {\n    return underlying_eos_.specific_internal_energy_lower_bound();\n  }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_upper_bound(\n      const double /*rest_mass_density*/,\n      const double /*electron_fraction*/) const override {\n    return underlying_eos_.specific_internal_energy_upper_bound();\n  }\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override {\n    return underlying_eos_.specific_enthalpy_lower_bound();\n  }\n\n  /// The baryon mass for this EoS\n  double baryon_mass() const override { return underlying_eos_.baryon_mass(); }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(3)\n  ColdEquilEos underlying_eos_;\n};\n/// \\cond\ntemplate <typename ColdEquilEos>\nPUP::able::PUP_ID EquationsOfState::Barotropic3D<ColdEquilEos>::my_PUP_ID = 0;\n/// \\endcond\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Barotropic2D.cpp\n  Barotropic3D.cpp\n  DarkEnergyFluid.cpp\n  Enthalpy.cpp\n  Equilibrium3D.cpp\n  HybridEos.cpp\n  IdealFluid.cpp\n  PiecewisePolytropicFluid.cpp\n  PolytropicFluid.cpp\n  RegisterDerivedWithCharm.cpp\n  Spectral.cpp\n  Tabulated3d.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Barotropic2D.hpp\n  Barotropic3D.hpp\n  DarkEnergyFluid.hpp\n  Enthalpy.hpp\n  Equilibrium3D.hpp\n  EquationOfState.hpp\n  Factory.hpp\n  HybridEos.hpp\n  IdealFluid.hpp\n  PiecewisePolytropicFluid.hpp\n  PolytropicFluid.hpp\n  RegisterDerivedWithCharm.hpp\n  Spectral.hpp\n  Tabulated3d.hpp\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/DarkEnergyFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/DarkEnergyFluid.hpp\"\n\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic>\nDarkEnergyFluid<IsRelativistic>::DarkEnergyFluid(const double parameter_w)\n    : parameter_w_(parameter_w) {\n  if (parameter_w_ <= 0.0 or parameter_w_ > 1.0) {\n    ERROR(\"The w(z) parameter must be positive, but less than one\");\n  }\n}\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     DarkEnergyFluid<IsRelativistic>, double, 2)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     DarkEnergyFluid<IsRelativistic>,\n                                     DataVector, 2)\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 2>>\nDarkEnergyFluid<IsRelativistic>::get_clone() const {\n  auto clone = std::make_unique<DarkEnergyFluid<IsRelativistic>>(*this);\n  return std::unique_ptr<EquationOfState<IsRelativistic, 2>>(std::move(clone));\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 3>>\nDarkEnergyFluid<IsRelativistic>::promote_to_3d_eos() const {\n  return std::make_unique<Equilibrium3D<DarkEnergyFluid<IsRelativistic>>>(\n      *this);\n}\n\ntemplate <bool IsRelativistic>\nbool DarkEnergyFluid<IsRelativistic>::is_equal(\n    const EquationOfState<IsRelativistic, 2>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const DarkEnergyFluid<IsRelativistic>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <bool IsRelativistic>\nbool DarkEnergyFluid<IsRelativistic>::operator==(\n    const DarkEnergyFluid<IsRelativistic>& rhs) const {\n  return parameter_w_ == rhs.parameter_w_;\n}\n\ntemplate <bool IsRelativistic>\nbool DarkEnergyFluid<IsRelativistic>::operator!=(\n    const DarkEnergyFluid<IsRelativistic>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <bool IsRelativistic>\nDarkEnergyFluid<IsRelativistic>::DarkEnergyFluid(CkMigrateMessage* msg)\n    : EquationOfState<IsRelativistic, 2>(msg) {}\n\ntemplate <bool IsRelativistic>\nvoid DarkEnergyFluid<IsRelativistic>::pup(PUP::er& p) {\n  EquationOfState<IsRelativistic, 2>::pup(p);\n  p | parameter_w_;\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nDarkEnergyFluid<IsRelativistic>::pressure_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{parameter_w_ * get(rest_mass_density) *\n                          (1.0 + get(specific_internal_energy))};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nDarkEnergyFluid<IsRelativistic>::pressure_from_density_and_enthalpy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_enthalpy) const {\n  return Scalar<DataType>{(parameter_w_ / (parameter_w_ + 1.0)) *\n                          get(rest_mass_density) * get(specific_enthalpy)};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> DarkEnergyFluid<IsRelativistic>::\n    specific_internal_energy_from_density_and_pressure_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& pressure) const {\n  return Scalar<DataType>{\n      get(pressure) / (parameter_w_ * get(rest_mass_density)) - 1.0};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nDarkEnergyFluid<IsRelativistic>::temperature_from_density_and_energy_impl(\n    const Scalar<DataType>& /*rest_mass_density*/,\n    const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{parameter_w_ * get(specific_internal_energy)};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> DarkEnergyFluid<IsRelativistic>::\n    specific_internal_energy_from_density_and_temperature_impl(\n        const Scalar<DataType>& /*rest_mass_density*/,\n        const Scalar<DataType>& temperature) const {\n  return Scalar<DataType>{get(temperature) / parameter_w_};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nDarkEnergyFluid<IsRelativistic>::chi_from_density_and_energy_impl(\n    const Scalar<DataType>& /*rest_mass_density*/,\n    const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{parameter_w_ * (1.0 + get(specific_internal_energy))};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> DarkEnergyFluid<IsRelativistic>::\n    kappa_times_p_over_rho_squared_from_density_and_energy_impl(\n        const Scalar<DataType>& /*rest_mass_density*/,\n        const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{square(parameter_w_) *\n                          (1.0 + get(specific_internal_energy))};\n}\n}  // namespace EquationsOfState\n\ntemplate class EquationsOfState::DarkEnergyFluid<true>;\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/DarkEnergyFluid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Equation of state for a dark energy fluid\n *\n * A dark energy fluid equation of state:\n *\n * \\f[\n * p = w(z) \\rho ( 1.0 + \\epsilon)\n * \\f]\n *\n * where \\f$\\rho\\f$ is the rest mass density, \\f$\\epsilon\\f$ is the specific\n * internal energy, and \\f$w(z) > 0\\f$ is a parameter depending on the redshift\n * \\f$z\\f$.\n *\n * The temperature \\f$T\\f$ is defined as\n *\n * \\f[\n * T = w(z) \\epsilon\n * \\f]\n */\ntemplate <bool IsRelativistic>\nclass DarkEnergyFluid : public EquationOfState<IsRelativistic, 2> {\n public:\n  static constexpr size_t thermodynamic_dim = 2;\n  static constexpr bool is_relativistic = IsRelativistic;\n  static_assert(is_relativistic,\n                \"Dark energy fluid equation of state only makes sense in a \"\n                \"relativistic setting.\");\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 2>> get_clone()\n      const override;\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 3>> promote_to_3d_eos()\n      const override;\n\n  bool is_equal(const EquationOfState<IsRelativistic, 2>& rhs) const override;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  bool is_barotropic() const override { return false; }\n\n  bool operator==(const DarkEnergyFluid<IsRelativistic>& rhs) const;\n\n  bool operator!=(const DarkEnergyFluid<IsRelativistic>& rhs) const;\n\n  struct ParameterW {\n    using type = double;\n    static constexpr Options::String help = {\"Parameter w(z)\"};\n    static double lower_bound() { return 0.0; }\n    static double upper_bound() { return 1.0; }\n  };\n\n  static constexpr Options::String help = {\n      \"A dark energy fluid equation of state.\\n\"\n      \"The pressure is related to the rest mass density by \"\n      \"p = w(z) * rho * (1 + epsilon), where p is the pressure, rho is the \"\n      \"rest mass density, epsilon is the specific internal energy, and w(z) is \"\n      \"a parameter.\\n\"\n      \"The temperature T is defined as T=w(z) epsilon.\"};\n\n  using options = tmpl::list<ParameterW>;\n\n  DarkEnergyFluid() = default;\n  DarkEnergyFluid(const DarkEnergyFluid&) = default;\n  DarkEnergyFluid& operator=(const DarkEnergyFluid&) = default;\n  DarkEnergyFluid(DarkEnergyFluid&&) = default;\n  DarkEnergyFluid& operator=(DarkEnergyFluid&&) = default;\n  ~DarkEnergyFluid() override = default;\n\n  explicit DarkEnergyFluid(double parameter_w);\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(DarkEnergyFluid, 2)\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<IsRelativistic, 2>), DarkEnergyFluid);\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_lower_bound(\n      const double /* rest_mass_density */) const override {\n    return -1.0;\n  }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_upper_bound(\n      const double /* rest_mass_density */) const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override { return 0.0; }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(2)\n\n  double parameter_w_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <bool IsRelativistic>\nPUP::able::PUP_ID EquationsOfState::DarkEnergyFluid<IsRelativistic>::my_PUP_ID =\n    0;\n/// \\endcond\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <numeric>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"NumericalAlgorithms/Spectral/Clenshaw.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\ndouble evaluate_cosine_series(const std::vector<double>& coefficients,\n                              const double& cosx) {\n  // cos(nx) = cos((n-1)*x)*2*cosx - 1* cos((n-2)*x)\n  return Spectral::evaluate_clenshaw(coefficients, 2.0 * cosx, -1.0, cosx,\n                                     2.0 * cosx * cosx - 1.0);\n}\ndouble evaluate_sine_series(const std::vector<double>& coefficients, double x,\n                            const double& cosx) {\n  // sin(nx) = sin((n-1)*x) * 2*cos(x) - 1 * sin((n-2)x)\n  auto sinx = sin(x);\n  return Spectral::evaluate_clenshaw(coefficients, 2.0 * cosx, -1.0, sinx,\n                                     2.0 * cosx * sinx);\n}\n}  // namespace\n\nnamespace EquationsOfState {\ntemplate <typename LowDensityEoS>\nEnthalpy<LowDensityEoS>::Coefficients::Coefficients(\n    std::vector<double> in_polynomial_coefficients,\n    std::vector<double> in_sin_coefficients,\n    std::vector<double> in_cos_coefficients, double in_trig_scale,\n    double in_reference_density, double in_exponential_constant)\n    : polynomial_coefficients(std::move(in_polynomial_coefficients)),\n      sin_coefficients(std::move(in_sin_coefficients)),\n      cos_coefficients(std::move(in_cos_coefficients)),\n      trig_scale(in_trig_scale),\n      reference_density(in_reference_density) {\n  if (not std::isnan(in_exponential_constant)) {\n    // used to construct the pressuure coefficient\n    has_exponential_prefactor = true;\n    exponential_external_constant = in_exponential_constant;\n  } else {\n    has_exponential_prefactor = false;\n    exponential_external_constant =\n        std::numeric_limits<double>::signaling_NaN();\n  }\n}\ntemplate <typename LowDensityEoS>\nbool Enthalpy<LowDensityEoS>::Coefficients::operator==(\n    const Coefficients& rhs) const {\n  return polynomial_coefficients == rhs.polynomial_coefficients and\n         sin_coefficients == rhs.sin_coefficients and\n         cos_coefficients == rhs.cos_coefficients and\n         trig_scale == rhs.trig_scale and\n         reference_density == rhs.reference_density and\n         has_exponential_prefactor == rhs.has_exponential_prefactor and\n         (has_exponential_prefactor ? exponential_external_constant ==\n                                          rhs.exponential_external_constant\n                                    : true);\n}\nnamespace {\nstd::vector<double> operator-(const std::vector<double>& lhs,\n                              const std::vector<double>& rhs) {\n  ASSERT(lhs.size() == rhs.size(), \"Incorrect Sizes in vector addition\");\n  std::vector<double> result(lhs.size());\n  for (size_t index = 0; index < lhs.size(); index++) {\n    result[index] = lhs[index] - rhs[index];\n  }\n  return result;\n}\n}  // namespace\ntemplate <typename LowDensityEoS>\ntypename Enthalpy<LowDensityEoS>::Coefficients\nEnthalpy<LowDensityEoS>::compute_pressure_coefficients(\n    const typename Enthalpy<LowDensityEoS>::Coefficients& enthalpy,\n    const typename Enthalpy<LowDensityEoS>::Coefficients& energy_density) {\n  // precompute the coefficients of p = rho * h  - e\n\n  auto polynomial_coefficients =\n      enthalpy.polynomial_coefficients - energy_density.polynomial_coefficients;\n  auto sin_coefficients =\n      enthalpy.sin_coefficients - energy_density.sin_coefficients;\n  auto cos_coefficients =\n      enthalpy.cos_coefficients - energy_density.cos_coefficients;\n  auto trig_scale = enthalpy.trig_scale;\n  auto reference_density = enthalpy.reference_density;\n  auto exponential_external_constant =\n      -energy_density.exponential_external_constant;\n  return Enthalpy<LowDensityEoS>::Coefficients(\n      polynomial_coefficients, sin_coefficients, cos_coefficients, trig_scale,\n      reference_density, exponential_external_constant);\n}\n\n// Given an expansion h(x) = sum_i f_i(x) , compute int_a^x sum_i f_i(x) e^x +\n// F(a) where f_i(x) could be one of the basis functions  used, i.e. x^i/i!,\n// sin(ikx) or cos(ikx)\ntemplate <typename LowDensityEoS>\ntypename Enthalpy<LowDensityEoS>::Coefficients\nEnthalpy<LowDensityEoS>::Coefficients::compute_exponential_integral(\n    const std::pair<double, double>& initial_condition,\n    const double minimum_density) {\n  // This is used to compute the energy density coefficients\n  if (has_exponential_prefactor) {\n    // No code currently calls this\n    ERROR(\n        \"Attempting to exponentially integrate an EoS decomposition series \"\n        \"with an exponential prefactor!  This is not yet implemented, if you \"\n        \"need this, please file an issue to get it added.\");\n  }\n  std::vector<double> integral_poly_coeffs(polynomial_coefficients.size(), 0.0);\n  std::vector<double> integral_sin_coeffs(sin_coefficients.size(), 0.0);\n  std::vector<double> integral_cos_coeffs(cos_coefficients.size(), 0.0);\n  Enthalpy::Coefficients exponential_integral_coefficients = *this;\n  // Preprocessing to put coefficients in better basis c_i z^i  = c_i' z^i/i!\n  std::vector<double> taylor_series_coefficients(\n      polynomial_coefficients.size());\n  for (size_t i = 0; i < integral_poly_coeffs.size(); i++) {\n    taylor_series_coefficients[i] =\n        polynomial_coefficients[i] * static_cast<double>(factorial(i));\n  }\n  // i indexes terms of the integrand, each of which contributes i+1 terms (of\n  // degree r <= i) to the integral indexed by r.  Therefore, r indexes terms\n  // of the integral.\n  for (size_t i = 0; i < taylor_series_coefficients.size(); i++) {\n    for (size_t r = 0; r <= i; r++) {\n      integral_poly_coeffs[r] +=\n          ((i - r) % 2 == 0 ? 1.0 : -1.0) * taylor_series_coefficients[i];\n    }\n  }\n  // restore the default normalization for the coefficients\n  for (size_t r = 0; r < integral_poly_coeffs.size(); r++) {\n    integral_poly_coeffs[r] /= static_cast<double>(factorial(r));\n  }\n\n  // note again sum starts from 0, basis functions are sin([j+1]kx)\n  for (size_t j = 0; j < sin_coefficients.size(); j++) {\n    // contribution from the sine terms\n    double k = trig_scale;\n    integral_sin_coeffs[j] +=\n        1.0 / (square(j + 1) * square(k) + 1.0) * sin_coefficients[j];\n    integral_cos_coeffs[j] -= (static_cast<double>(j + 1) * k) /\n                              (square(j + 1) * square(k) + 1.0) *\n                              sin_coefficients[j];\n    // contribution from the cosine terms\n    integral_cos_coeffs[j] +=\n        1.0 / (square(j + 1) * square(k) + 1.0) * cos_coefficients[j];\n    integral_sin_coeffs[j] += (static_cast<double>(j + 1) * k) /\n                              (square(j + 1) * square(k) + 1.0) *\n                              cos_coefficients[j];\n  }\n  exponential_integral_coefficients.has_exponential_prefactor = true;\n  exponential_integral_coefficients.exponential_external_constant = 0.0;\n  exponential_integral_coefficients.polynomial_coefficients =\n      std::move(integral_poly_coeffs);\n  exponential_integral_coefficients.sin_coefficients =\n      std::move(integral_sin_coeffs);\n  exponential_integral_coefficients.cos_coefficients =\n      std::move(integral_cos_coeffs);\n  const double new_constant =\n      initial_condition.second -\n      evaluate_coefficients(exponential_integral_coefficients,\n                            initial_condition.first, minimum_density);\n  exponential_integral_coefficients.exponential_external_constant =\n      new_constant;\n  return exponential_integral_coefficients;\n}\ntemplate <typename LowDensityEoS>\ntypename Enthalpy<LowDensityEoS>::Coefficients\nEnthalpy<LowDensityEoS>::Coefficients::compute_derivative() {\n  std::vector<double> derivative_poly_coeffs(polynomial_coefficients.size());\n  std::vector<double> derivative_sin_coeffs(sin_coefficients.size());\n  std::vector<double> derivative_cos_coeffs(cos_coefficients.size());\n  Enthalpy::Coefficients derivative_coefficients = *this;\n  // d/dz e^z \\sum_i a_i f(z) = e^z \\sum_i a_i f_i(z)  + e^z \\sum_i a_i f_i'(z)\n  if (has_exponential_prefactor) {\n    // Currently unused, but may be useful in the future\n    ERROR(\n        \"This branch is untested, it may be \"\n        \"used to compute derivatives of internal\"\n        \"energy (or related quantities) in the future. \");\n    derivative_poly_coeffs = polynomial_coefficients;\n    derivative_sin_coeffs = sin_coefficients;\n    derivative_cos_coeffs = cos_coefficients;\n    derivative_poly_coeffs[polynomial_coefficients.size() - 1] = 0.0;\n    for (size_t i = 0; i < polynomial_coefficients.size() - 1; i++) {\n      derivative_poly_coeffs[i] +=\n          polynomial_coefficients[i + 1] * static_cast<double>(i + 1);\n    }\n    // Again sum starts from 0\n    for (size_t j = 0; j < sin_coefficients.size(); j++) {\n      derivative_cos_coeffs[j] +=\n          sin_coefficients[j] * (static_cast<double>(j + 1) * trig_scale);\n      derivative_sin_coeffs[j] +=\n          -cos_coefficients[j] * (static_cast<double>(j + 1) * trig_scale);\n    }\n  } else {  // There is no exponential prefactor\n    // The final coefficient will be zero because nothing differentiates to it\n    derivative_poly_coeffs[polynomial_coefficients.size() - 1] = 0.0;\n    for (size_t i = 0; i < polynomial_coefficients.size() - 1; i++) {\n      derivative_poly_coeffs[i] =\n          polynomial_coefficients[i + 1] * static_cast<double>(i + 1);\n    }\n    for (size_t j = 0; j < sin_coefficients.size(); j++) {\n      derivative_cos_coeffs[j] =\n          sin_coefficients[j] * (static_cast<double>(j + 1) * trig_scale);\n      derivative_sin_coeffs[j] =\n          -cos_coefficients[j] * (static_cast<double>(j + 1) * trig_scale);\n    }\n  }\n  derivative_coefficients.polynomial_coefficients =\n      std::move(derivative_poly_coeffs);\n  derivative_coefficients.sin_coefficients = std::move(derivative_sin_coeffs);\n  derivative_coefficients.cos_coefficients = std::move(derivative_cos_coeffs);\n  return derivative_coefficients;\n}\n\ntemplate <typename LowDensityEoS>\nvoid Enthalpy<LowDensityEoS>::Coefficients::pup(PUP::er& p) {\n  p | polynomial_coefficients;\n  p | sin_coefficients;\n  p | cos_coefficients;\n  p | trig_scale;\n  p | reference_density;\n  p | has_exponential_prefactor;\n  p | exponential_external_constant;\n}\ntemplate <typename LowDensityEoS>\nEnthalpy<LowDensityEoS>::Enthalpy(\n    const double reference_density, const double max_density,\n    const double min_density, const double trig_scale,\n    const std::vector<double>& polynomial_coefficients,\n    const std::vector<double>& sin_coefficients,\n    const std::vector<double>& cos_coefficients,\n    const LowDensityEoS& low_density_eos, const double transition_delta_epsilon)\n    : reference_density_(reference_density),\n      minimum_density_(min_density),\n      maximum_density_(max_density),\n      low_density_eos_(low_density_eos),\n      coefficients_(polynomial_coefficients, sin_coefficients, cos_coefficients,\n                    trig_scale, reference_density)\n\n{\n  minimum_enthalpy_ = specific_enthalpy_from_density(minimum_density_);\n  // Compute based on low density behavior\n  double min_energy_density =\n      minimum_density_ +\n      get(low_density_eos_.specific_internal_energy_from_density(\n          Scalar<double>(minimum_density_))) *\n          minimum_density_ +\n      transition_delta_epsilon;\n  exponential_integral_coefficients_ =\n      coefficients_.compute_exponential_integral(\n          {x_from_density(min_density), min_energy_density}, minimum_density_);\n  derivative_coefficients_ = coefficients_.compute_derivative();\n  pressure_coefficients_ = compute_pressure_coefficients(\n      coefficients_, exponential_integral_coefficients_);\n}\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename LowDensityEoS>,\n                                     Enthalpy<LowDensityEoS>, double, 1)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename LowDensityEoS>,\n                                     Enthalpy<LowDensityEoS>, DataVector, 1)\n\ntemplate <typename LowDensityEoS>\nstd::unique_ptr<EquationOfState<true, 1>> Enthalpy<LowDensityEoS>::get_clone()\n    const {\n  auto clone = std::make_unique<Enthalpy>(*this);\n  return std::unique_ptr<EquationOfState<true, 1>>(std::move(clone));\n}\n\ntemplate <typename LowDensityEoS>\nstd::unique_ptr<EquationOfState<true, 3>>\nEnthalpy<LowDensityEoS>::promote_to_3d_eos() const {\n  return std::make_unique<Barotropic3D<Enthalpy<LowDensityEoS>>>(*this);\n}\n\ntemplate <typename LowDensityEoS>\nstd::unique_ptr<EquationOfState<true, 2>>\nEnthalpy<LowDensityEoS>::promote_to_2d_eos() const {\n  return std::make_unique<Barotropic2D<Enthalpy<LowDensityEoS>>>(*this);\n}\n\ntemplate <typename LowDensityEoS>\nbool Enthalpy<LowDensityEoS>::is_equal(\n    const EquationOfState<true, 1>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const Enthalpy<LowDensityEoS>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\ntemplate <typename LowDensityEoS>\nbool Enthalpy<LowDensityEoS>::operator==(\n    const Enthalpy<LowDensityEoS>& rhs) const {\n  return low_density_eos_ == rhs.low_density_eos_ and\n         coefficients_ == rhs.coefficients_ and\n         exponential_integral_coefficients_ ==\n             rhs.exponential_integral_coefficients_;\n  // Don't need to check the derivative coefficients\n}\ntemplate <typename LowDensityEoS>\nbool Enthalpy<LowDensityEoS>::operator!=(\n    const Enthalpy<LowDensityEoS>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <typename LowDensityEoS>\nEnthalpy<LowDensityEoS>::Enthalpy(CkMigrateMessage* msg)\n    : EquationOfState<true, 1>(msg) {}\n\ntemplate <typename LowDensityEoS>\nvoid Enthalpy<LowDensityEoS>::pup(PUP::er& p) {\n  EquationOfState<true, 1>::pup(p);\n  p | reference_density_;\n  p | maximum_density_;\n  p | minimum_density_;\n  p | minimum_enthalpy_;\n  p | low_density_eos_;\n  p | coefficients_;\n  p | exponential_integral_coefficients_;\n  p | derivative_coefficients_;\n  p | pressure_coefficients_;\n}\n\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::x_from_density(\n    const double rest_mass_density) const {\n  ASSERT(rest_mass_density > 0.0, \"Density must be greater than zero\");\n  return log(rest_mass_density / reference_density_);\n}\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::density_from_x(const double x) const {\n  return reference_density_ * exp(x);\n}\n// Only works for rho > rho_min\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::energy_density_from_log_density(\n    const double x, const double rest_mass_density) const {\n  return evaluate_coefficients(exponential_integral_coefficients_, x,\n                               rest_mass_density);\n}\n\n// Evaluate the function represented by the coefficinets at x  = log(rho/rho_0)\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::evaluate_coefficients(\n    const Enthalpy<LowDensityEoS>::Coefficients& coefficients, const double x,\n    const double exponential_prefactor) {\n  const double k_times_x = coefficients.trig_scale * x;\n  const double polynomial_contribution =\n      evaluate_polynomial(coefficients.polynomial_coefficients, x);\n  double value = polynomial_contribution;\n  // A couple of edge cases\n  switch (coefficients.sin_coefficients.size()) {\n    case 0:\n      break;\n    case 1: {\n      // One sine and one cosine term\n      value += (coefficients.sin_coefficients[0] * sin(k_times_x) +\n                coefficients.cos_coefficients[0] * cos(k_times_x));\n      break;\n    }\n    default: {\n      // Use Clenshaw's method to evaluate\n      const double coskx = cos(k_times_x);\n      const double sin_contribution =\n          evaluate_sine_series(coefficients.sin_coefficients, k_times_x, coskx);\n      const double cos_contribution =\n          evaluate_cosine_series(coefficients.cos_coefficients, coskx);\n      value += (sin_contribution + cos_contribution);\n      break;\n    }\n  }\n  if (coefficients.has_exponential_prefactor) {\n    // multiply by some constant, typically rho(x) = rho_0 * exp(x)\n    // add an additional constant external to the previous prefactor\n    return value * exponential_prefactor +\n           coefficients.exponential_external_constant;\n  }\n  return value;\n}\ntemplate <typename LowDensityEoS>\ntemplate <typename DataType>\nScalar<DataType> Enthalpy<LowDensityEoS>::pressure_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    return Scalar<double>{pressure_from_density(get(rest_mass_density))};\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto result = make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n    for (size_t i = 0; i < get(result).size(); ++i) {\n      get(result)[i] = pressure_from_density(get(rest_mass_density)[i]);\n    }\n    return result;\n  }\n}\ntemplate <typename LowDensityEoS>\ntemplate <class DataType>\nScalar<DataType> Enthalpy<LowDensityEoS>::rest_mass_density_from_enthalpy_impl(\n    const Scalar<DataType>& specific_enthalpy) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    return Scalar<double>{\n        rest_mass_density_from_enthalpy(get(specific_enthalpy))};\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto result = make_with_value<Scalar<DataVector>>(specific_enthalpy, 0.0);\n    for (size_t i = 0; i < get(result).size(); ++i) {\n      get(result)[i] =\n          rest_mass_density_from_enthalpy(get(specific_enthalpy)[i]);\n    }\n    return result;\n  }\n}\n\ntemplate <typename LowDensityEoS>\ntemplate <class DataType>\nScalar<DataType>\nEnthalpy<LowDensityEoS>::specific_internal_energy_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    return Scalar<double>{\n        specific_internal_energy_from_density(get(rest_mass_density))};\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto result = make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n    for (size_t i = 0; i < get(result).size(); ++i) {\n      get(result)[i] =\n          specific_internal_energy_from_density(get(rest_mass_density)[i]);\n    }\n    return result;\n  }\n}\n\ntemplate <typename LowDensityEoS>\ntemplate <class DataType>\nScalar<DataType> Enthalpy<LowDensityEoS>::chi_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    return Scalar<double>{chi_from_density(get(rest_mass_density))};\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto result = make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n    for (size_t i = 0; i < get(result).size(); ++i) {\n      get(result)[i] = chi_from_density(get(rest_mass_density)[i]);\n    }\n    return result;\n  }\n}\n\ntemplate <typename LowDensityEoS>\ntemplate <class DataType>\nScalar<DataType>\nEnthalpy<LowDensityEoS>::kappa_times_p_over_rho_squared_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  return make_with_value<Scalar<DataType>>(get(rest_mass_density), 0.0);\n}\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::chi_from_density(\n    const double rest_mass_density) const {\n  if (Enthalpy::in_low_density_domain(rest_mass_density)) {\n    return get(\n        low_density_eos_.chi_from_density(Scalar<double>(rest_mass_density)));\n  } else {\n    const double x = x_from_density(rest_mass_density);\n    return evaluate_coefficients(derivative_coefficients_, x);\n  }\n}\n\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::specific_internal_energy_from_density(\n    const double rest_mass_density) const {\n  if (Enthalpy::in_low_density_domain(rest_mass_density)) {\n    return get(low_density_eos_.specific_internal_energy_from_density(\n        Scalar<double>(rest_mass_density)));\n  } else {\n    return 1.0 / rest_mass_density *\n               energy_density_from_log_density(\n                   x_from_density(rest_mass_density), rest_mass_density) -\n           1.0;\n  }\n}\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::specific_enthalpy_from_density(\n    const double rest_mass_density) const {\n  if (Enthalpy::in_low_density_domain(rest_mass_density)) {\n    return get(hydro::relativistic_specific_enthalpy(\n        Scalar<double>(rest_mass_density),\n        low_density_eos_.specific_internal_energy_from_density(\n            Scalar<double>(rest_mass_density)),\n        low_density_eos_.pressure_from_density(\n            Scalar<double>(rest_mass_density))));\n  } else {\n    return evaluate_coefficients(coefficients_,\n                                 x_from_density(rest_mass_density));\n  }\n}\n\n// P(x) = rho(x)h(x) - e(x) with h the specific enthalpy, and e\n// the energy density.\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::pressure_from_log_density(\n    const double x, const double density) const {\n  return evaluate_coefficients(pressure_coefficients_, x, density);\n}\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::pressure_from_density(\n    const double rest_mass_density) const {\n  if (in_low_density_domain(rest_mass_density)) {\n    return get(low_density_eos_.pressure_from_density(\n        Scalar<double>(rest_mass_density)));\n  } else {\n    const double x = x_from_density(rest_mass_density);\n    return pressure_from_log_density(x, rest_mass_density);\n  }\n}\n\n// Solve for h(rho)=h0, which requires rootfinding for this EoS\ntemplate <typename LowDensityEoS>\ndouble Enthalpy<LowDensityEoS>::rest_mass_density_from_enthalpy(\n    const double specific_enthalpy) const {\n  if (specific_enthalpy <= minimum_enthalpy_) {\n    return get(low_density_eos_.rest_mass_density_from_enthalpy(\n        Scalar<double>(specific_enthalpy)));\n  } else {\n    // Root-finding appropriate between reference density and maximum density\n    // We can use x=0 and x=x_max as bounds\n    const auto f = [this, &specific_enthalpy](const double density) {\n      const auto x = x_from_density(density);\n      return evaluate_coefficients(coefficients_, x) - specific_enthalpy;\n    };\n    return RootFinder::toms748(f, minimum_density_, maximum_density_, 1.0e-14,\n                               1.0e-15);\n  }\n}\ntemplate <typename LowDensityEoS>\nPUP::able::PUP_ID EquationsOfState::Enthalpy<LowDensityEoS>::my_PUP_ID = 0;\n\ntemplate class EquationsOfState::Enthalpy<Spectral>;\ntemplate class EquationsOfState::Enthalpy<PolytropicFluid<true>>;\ntemplate class EquationsOfState::Enthalpy<EquationsOfState::Enthalpy<Spectral>>;\ntemplate class EquationsOfState::Enthalpy<\n    EquationsOfState::Enthalpy<EquationsOfState::Enthalpy<Spectral>>>;\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <limits>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n\n/// \\endcond\n\nnamespace EquationsOfState {\n\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief An equation of state given by parametrized enthalpy\n *\n * This equation of state is determined as a function of \\f$x =\n * \\ln(\\rho/\\rho_0)\\f$ where \\f$\\rho\\f$ is the rest mass density and\n * \\f$\\rho_0\\f$ is the provided reference density.\n * The pseudo-enthalpy \\f$h \\equiv (p + rho  + u)/rho\\f$\n * is expanded as\n *\n * \\f{equation}\n * h(x) = \\sum_i a_i x^i + \\sum_j b_j \\sin(jkx) + c_j \\cos(jkx)\n * \\f}\n *\n * This form allows for convenient calculation of thermodynamic\n * quantities for a cold equation of state. For example\n *\n * \\f{equation}\n * h(x) = \\frac{d e} {d \\rho} |_{x = \\log(\\rho/\\rho_0)}\n * \\f}\n *\n * where \\f$e\\f$ is the total energy density.  At the same time \\f$ dx =\n * d\\rho/\\rho \\f$ so \\f$ \\rho_0 e^x dx = d \\rho \\f$ Therefore,\n *\n * \\f{equation}\n * e(x) - e(x_0) = \\int_{x_0}^x h(x') e^{x'} dx '\n * \\f}\n *\n * This can be computed analytically because\n *\n * \\f{equation}\n *  \\int a_i \\frac{x^i}{i!} e^{x} dx = \\sum_{j \\leq i} a_i (-1)^{i-j}\n * \\frac{(x)^{j}}{j!}\n * + C \\f}\n *\n * and\n *\n * \\f{equation}\n * \\int b_j \\sin(j k x) e^x dx = b_j e^x \\frac{\\sin(jkx) - j k \\cos(jkx)}{j^2\n * k^2 + 1} \\f}\n *\n * \\f{equation}\n * \\int c_j \\cos(j k x) e^x dx = b_j e^x \\frac{\\cos(jkx) + j k \\sin(jkx)}{j^2\n * k^2 + 1} \\f}\n *\n * From this most other thermodynamic quantities can be computed\n * analytically\n *\n * The internal energy density\n * \\f{equation}\n * \\epsilon(x)\\rho(x) = e(x)  - \\rho(x)\n * \\f}\n *\n * The pressure\n * \\f{equation}\n * p(x) = \\rho(x) h(x) - e(x)\n * \\f}\n *\n * The derivative of the pressure with respect to the rest mass density\n * \\f{equation}\n * \\chi(x) = \\frac{dp}{d\\rho} |_{x = x(\\rho)} = \\frac{dh}{dx}\n * \\f}\n *\n * Below the minimum density, a spectral parameterization\n * is used.\n *\n *\n *\n */\ntemplate <typename LowDensityEoS>\nclass Enthalpy : public EquationOfState<true, 1> {\n private:\n  struct Coefficients {\n    std::vector<double> polynomial_coefficients;\n    std::vector<double> sin_coefficients;\n    std::vector<double> cos_coefficients;\n    double trig_scale;\n    double reference_density;\n    bool has_exponential_prefactor;\n    double exponential_external_constant;\n    Coefficients() = default;\n    ~Coefficients() = default;\n    Coefficients(const Coefficients& coefficients) = default;\n    Coefficients(std::vector<double> in_polynomial_coefficients,\n                 std::vector<double> in_sin_coefficients,\n                 std::vector<double> in_cos_coefficients, double in_trig_scale,\n                 double in_reference_density,\n                 double in_exponential_constant =\n                     std::numeric_limits<double>::quiet_NaN());\n    bool operator==(const Coefficients& rhs) const;\n\n    Enthalpy<LowDensityEoS>::Coefficients compute_exponential_integral(\n        const std::pair<double, double>& initial_condition,\n        const double minimum_density);\n    Enthalpy<LowDensityEoS>::Coefficients compute_derivative();\n    void pup(PUP::er& p);\n  };\n\n public:\n  static constexpr size_t thermodynamic_dim = 1;\n  static constexpr bool is_relativistic = true;\n\n  static std::string name() {\n    return \"Enthalpy(\" + pretty_type::name<LowDensityEoS>() + \")\";\n  }\n\n  struct ReferenceDensity {\n    using type = double;\n    static constexpr Options::String help = {\"Reference density rho_0\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  struct MinimumDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Minimum valid density rho_min,\"\n        \" for this parametrization\"};\n    static double lower_bound() { return 0.0; }\n  };\n  struct MaximumDensity {\n    using type = double;\n    static constexpr Options::String help = {\"Maximum density for this EoS\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  struct PolynomialCoefficients {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\"Polynomial coefficients a_i\"};\n  };\n\n  struct TrigScaling {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Fundamental wavenumber of trig \"\n        \"functions, k\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  struct SinCoefficients {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\"Sine coefficients b_j\"};\n  };\n  struct CosCoefficients {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\"Cosine coefficients c_j\"};\n  };\n  struct StitchedLowDensityEoS {\n    using type = LowDensityEoS;\n    static std::string name() {\n      return pretty_type::short_name<LowDensityEoS>();\n    }\n    static constexpr Options::String help = {\n        \"Low density EoS stitched at the MinimumDensity\"};\n  };\n\n  struct TransitionDeltaEpsilon {\n    using type = double;\n    static constexpr Options::String help = {\n        \"the change in internal energy across the low-\"\n        \"to-high-density transition, generically 0.0\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  static constexpr Options::String help = {\n      \"An EoS with a parametrized value h(log(rho/rho_0)) with h the specific \"\n      \"enthalpy and rho the baryon rest mass density.  The enthalpy is \"\n      \"expanded as a sum of polynomial terms and trigonometric corrections. \"\n      \"let x = log(rho/rho_0) in\"\n      \"h(x) = \\\\sum_i a_ix^i + \\\\sum_j b_jsin(k * j * x) + c_jcos(k * j * x) \"\n      \"Note that rho(x)(1+epsilon(x)) = int_0^x e^x' h((x') dx' can be \"\n      \"computed \"\n      \"analytically, and therefore so can \"\n      \"P(x) = rho(x) * (h(x) - (1 + epsilon(x))) \"};\n\n  using options =\n      tmpl::list<ReferenceDensity, MaximumDensity, MinimumDensity, TrigScaling,\n                 PolynomialCoefficients, SinCoefficients, CosCoefficients,\n                 StitchedLowDensityEoS, TransitionDeltaEpsilon>;\n\n  Enthalpy() = default;\n  Enthalpy(const Enthalpy&) = default;\n  Enthalpy& operator=(const Enthalpy&) = default;\n  Enthalpy(Enthalpy&&) = default;\n  Enthalpy& operator=(Enthalpy&&) = default;\n  ~Enthalpy() override = default;\n\n  Enthalpy(double reference_density, double max_density, double min_density,\n           double trig_scale,\n           const std::vector<double>& polynomial_coefficients,\n           const std::vector<double>& sin_coefficients,\n           const std::vector<double>& cos_coefficients,\n           const LowDensityEoS& low_density_eos,\n           const double transition_delta_epsilon);\n\n  std::unique_ptr<EquationOfState<true, 1>> get_clone() const override;\n\n  std::unique_ptr<EquationOfState<true, 3>> promote_to_3d_eos() const override;\n\n  std::unique_ptr<EquationOfState<true, 2>> promote_to_2d_eos() const override;\n\n  bool is_equal(const EquationOfState<true, 1>& rhs) const override;\n\n  bool operator==(const Enthalpy<LowDensityEoS>& rhs) const;\n\n  bool operator!=(const Enthalpy<LowDensityEoS>& rhs) const;\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(Enthalpy, 1)\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<true, 1>), Enthalpy);\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override { return 1.0; }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  double specific_internal_energy_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  double specific_internal_energy_upper_bound() const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The vacuum baryon mass for this EoS\n  double baryon_mass() const override { return low_density_eos_.baryon_mass(); }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(1)\n\n  SPECTRE_ALWAYS_INLINE\n  bool in_low_density_domain(const double rest_mass_density) const {\n    return rest_mass_density < minimum_density_;\n  }\n\n  double x_from_density(const double rest_mass_density) const;\n  double density_from_x(const double x) const;\n  double energy_density_from_log_density(const double x,\n                                         const double rest_mass_density) const;\n  static double evaluate_coefficients(\n      const Enthalpy::Coefficients& coefficients, const double x,\n      const double exponential_prefactor =\n          std::numeric_limits<double>::signaling_NaN());\n  static Enthalpy::Coefficients compute_pressure_coefficients(\n      const typename Enthalpy::Coefficients& enthalpy,\n      const typename Enthalpy::Coefficients& energy_density);\n\n  double chi_from_density(const double rest_mass_density) const;\n  double specific_internal_energy_from_density(\n      const double rest_mass_density) const;\n  double specific_enthalpy_from_density(const double rest_mass_density) const;\n  double pressure_from_density(const double rest_mass_density) const;\n  double pressure_from_log_density(const double x,\n                                   const double rest_mass_density) const;\n  double rest_mass_density_from_enthalpy(const double specific_enthalpy) const;\n\n  double reference_density_ = std::numeric_limits<double>::signaling_NaN();\n  double minimum_density_ = std::numeric_limits<double>::signaling_NaN();\n  double maximum_density_ = std::numeric_limits<double>::signaling_NaN();\n  double minimum_enthalpy_ = std::numeric_limits<double>::signaling_NaN();\n\n  LowDensityEoS low_density_eos_;\n  Coefficients coefficients_;\n  Coefficients exponential_integral_coefficients_;\n  Coefficients derivative_coefficients_;\n  Coefficients pressure_coefficients_;\n};\n\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/sub.hpp>\n#include <boost/preprocessor/list/for_each.hpp>\n#include <boost/preprocessor/punctuation/comma_if.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/enum.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\nnamespace EquationsOfState {\ntemplate <typename ColdEos>\nclass Barotropic2D;\ntemplate <typename ColdEquilEos>\nclass Barotropic3D;\ntemplate <bool IsRelativistic>\nclass DarkEnergyFluid;\ntemplate <typename EquilEos>\nclass Equilibrium3D;\ntemplate <typename ColdEquationOfState>\nclass HybridEos;\ntemplate <bool IsRelativistic>\nclass IdealFluid;\ntemplate <bool IsRelativistic>\nclass PolytropicFluid;\ntemplate <bool IsRelativistic>\nclass PiecewisePolytropicFluid;\nclass Spectral;\ntemplate <typename LowDensityEoS>\nclass Enthalpy;\ntemplate <bool IsRelativistic>\nclass Tabulated3D;\n}  // namespace EquationsOfState\n/// \\endcond\n\n/// Contains all equations of state, including base class\nnamespace EquationsOfState {\n\nnamespace detail {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nstruct DerivedClasses {};\n\ntemplate <>\nstruct DerivedClasses<true, 1> {\n  using type = tmpl::list<\n      Enthalpy<Enthalpy<Enthalpy<Spectral>>>, Enthalpy<Enthalpy<Spectral>>,\n      Enthalpy<Spectral>, Enthalpy<PolytropicFluid<true>>,\n      PiecewisePolytropicFluid<true>, PolytropicFluid<true>, Spectral>;\n};\n\ntemplate <>\nstruct DerivedClasses<false, 1> {\n  using type =\n      tmpl::list<PiecewisePolytropicFluid<false>, PolytropicFluid<false>>;\n};\n\ntemplate <>\nstruct DerivedClasses<true, 2> {\n  using type =\n      tmpl::list<Barotropic2D<PolytropicFluid<true>>, Barotropic2D<Spectral>,\n                 Barotropic2D<Enthalpy<Spectral>>,\n                 Barotropic2D<PiecewisePolytropicFluid<true>>,\n                 Barotropic2D<Enthalpy<Enthalpy<Spectral>>>,\n                 Barotropic2D<Enthalpy<Enthalpy<Enthalpy<Spectral>>>>,\n                 DarkEnergyFluid<true>, IdealFluid<true>,\n                 HybridEos<PolytropicFluid<true>>, HybridEos<Spectral>,\n                 HybridEos<Enthalpy<Spectral>>>;\n};\n\ntemplate <>\nstruct DerivedClasses<false, 2> {\n  using type = tmpl::list<Barotropic2D<PolytropicFluid<false>>,\n                          Barotropic2D<PiecewisePolytropicFluid<false>>,\n                          IdealFluid<false>, HybridEos<PolytropicFluid<false>>>;\n};\n\ntemplate <>\nstruct DerivedClasses<true, 3> {\n  using type =\n      tmpl::list<Tabulated3D<true>, Barotropic3D<PolytropicFluid<true>>,\n                 Barotropic3D<Spectral>, Barotropic3D<Enthalpy<Spectral>>,\n                 Barotropic3D<PiecewisePolytropicFluid<true>>,\n                 Barotropic3D<Enthalpy<Enthalpy<Spectral>>>,\n                 Barotropic3D<Enthalpy<Enthalpy<Enthalpy<Spectral>>>>,\n                 Equilibrium3D<HybridEos<PolytropicFluid<true>>>,\n                 Equilibrium3D<HybridEos<Spectral>>,\n                 Equilibrium3D<HybridEos<Enthalpy<Spectral>>>,\n                 Equilibrium3D<DarkEnergyFluid<true>>,\n                 Equilibrium3D<IdealFluid<true>>>;\n};\n\ntemplate <>\nstruct DerivedClasses<false, 3> {\n  using type = tmpl::list<Tabulated3D<false>, Equilibrium3D<IdealFluid<false>>,\n                          Barotropic3D<PiecewisePolytropicFluid<false>>,\n                          Equilibrium3D<HybridEos<PolytropicFluid<false>>>,\n                          Barotropic3D<PolytropicFluid<false>>>;\n};\n\n}  // namespace detail\n\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Base class for equations of state depending on whether or not the\n * system is relativistic, and the number of independent thermodynamic variables\n * (`ThermodynamicDim`) needed to determine the pressure.\n *\n * The template parameter `IsRelativistic` is `true` for relativistic equations\n * of state and `false` for non-relativistic equations of state.\n */\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n\ntemplate <typename T>\nstruct get_eos_base_impl {\n  using type = EquationsOfState::EquationOfState<T::is_relativistic,\n                                                 T::thermodynamic_dim>;\n};\n\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nstruct get_eos_base_impl<\n    EquationsOfState::EquationOfState<IsRelativistic, ThermodynamicDim>> {\n  using type =\n      EquationsOfState::EquationOfState<IsRelativistic, ThermodynamicDim>;\n};\n\ntemplate <typename T>\nusing get_eos_base = typename get_eos_base_impl<T>::type;\n\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Base class for equations of state which need one thermodynamic\n * variable in order to determine the pressure.\n *\n * The template parameter `IsRelativistic` is `true` for relativistic equations\n * of state and `false` for non-relativistic equations of state.\n */\ntemplate <bool IsRelativistic>\nclass EquationOfState<IsRelativistic, 1> : public PUP::able {\n public:\n  static constexpr bool is_relativistic = IsRelativistic;\n  static constexpr size_t thermodynamic_dim = 1;\n  using creatable_classes =\n      typename detail::DerivedClasses<IsRelativistic, 1>::type;\n\n  EquationOfState() = default;\n  EquationOfState(const EquationOfState&) = default;\n  EquationOfState& operator=(const EquationOfState&) = default;\n  EquationOfState(EquationOfState&&) = default;\n  EquationOfState& operator=(EquationOfState&&) = default;\n  ~EquationOfState() override = default;\n\n  explicit EquationOfState(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  WRAPPED_PUPable_abstract(EquationOfState);  // NOLINT\n\n  virtual std::unique_ptr<EquationOfState<IsRelativistic, 1>> get_clone()\n      const = 0;\n\n  virtual bool is_equal(\n      const EquationOfState<IsRelativistic, 1>& rhs) const = 0;\n\n  /// Create a 3D EOS from the 1D EOS\n  virtual std::unique_ptr<EquationOfState<IsRelativistic, 3>>\n  promote_to_3d_eos() const = 0;\n\n  /// Create a 2D EOS from the 1D EOS\n  virtual std::unique_ptr<EquationOfState<IsRelativistic, 2>>\n  promote_to_2d_eos() const = 0;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  bool is_barotropic() const { return true; }\n  /// @{\n  /*!\n   * Computes the electron fraction in beta-equilibrium \\f$Y_e^{\\rm eq}\\f$ from\n   * the rest mass density \\f$\\rho\\f$.\n   */\n  virtual Scalar<double> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<double>& rest_mass_density,\n      const Scalar<double>& /*temperature*/) const {\n    return make_with_value<Scalar<double>>(rest_mass_density, 0.1);\n  }\n\n  virtual Scalar<DataVector>\n  equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& /*temperature*/) const {\n    return make_with_value<Scalar<DataVector>>(rest_mass_density, 0.1);\n  }\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the pressure \\f$p\\f$ from the rest mass density \\f$\\rho\\f$.\n   */\n  virtual Scalar<double> pressure_from_density(\n      const Scalar<double>& /*rest_mass_density*/) const = 0;\n  virtual Scalar<DataVector> pressure_from_density(\n      const Scalar<DataVector>& /*rest_mass_density*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the rest mass density \\f$\\rho\\f$ from the specific enthalpy\n   * \\f$h\\f$.\n   */\n  virtual Scalar<double> rest_mass_density_from_enthalpy(\n      const Scalar<double>& /*specific_enthalpy*/) const = 0;\n  virtual Scalar<DataVector> rest_mass_density_from_enthalpy(\n      const Scalar<DataVector>& /*specific_enthalpy*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the specific internal energy \\f$\\epsilon\\f$ from the rest mass\n   * density \\f$\\rho\\f$.\n   */\n  virtual Scalar<double> specific_internal_energy_from_density(\n      const Scalar<double>& /*rest_mass_density*/) const = 0;\n  virtual Scalar<DataVector> specific_internal_energy_from_density(\n      const Scalar<DataVector>& /*rest_mass_density*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the temperature \\f$T\\f$ from the rest mass\n   * density \\f$\\rho\\f$.\n   */\n  virtual Scalar<double> temperature_from_density(\n      const Scalar<double>& /*rest_mass_density*/) const {\n    return Scalar<double>{0.0};\n  }\n  virtual Scalar<DataVector> temperature_from_density(\n      const Scalar<DataVector>& rest_mass_density) const {\n    return make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n  }\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the temperature \\f$\\T\\f$ from the specific internal energy\n   * \\f$\\epsilon\\f$.\n   */\n  virtual Scalar<double> temperature_from_specific_internal_energy(\n      const Scalar<double>& /*specific_internal_energy*/) const {\n    return Scalar<double>{0.0};\n  }\n  virtual Scalar<DataVector> temperature_from_specific_internal_energy(\n      const Scalar<DataVector>& specific_internal_energy) const {\n    return make_with_value<Scalar<DataVector>>(specific_internal_energy, 0.0);\n  }\n  /// @}\n\n  /// @{\n  /*!\n   * Computes \\f$\\chi=\\partial p / \\partial \\rho\\f$ from \\f$\\rho\\f$, where\n   * \\f$p\\f$ is the pressure and \\f$\\rho\\f$ is the rest mass density.\n   */\n  virtual Scalar<double> chi_from_density(\n      const Scalar<double>& /*rest_mass_density*/) const = 0;\n  virtual Scalar<DataVector> chi_from_density(\n      const Scalar<DataVector>& /*rest_mass_density*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes \\f$\\kappa p/\\rho^2=(p/\\rho^2)\\partial p / \\partial \\epsilon\\f$\n   * from \\f$\\rho\\f$, where \\f$p\\f$ is the pressure, \\f$\\rho\\f$ is the rest mass\n   * density, and \\f$\\epsilon\\f$ is the specific internal energy.\n   *\n   * The reason for not returning just\n   * \\f$\\kappa=\\partial p / \\partial \\epsilon\\f$ is to avoid division by zero\n   * for small values of \\f$\\rho\\f$ when assembling the speed of sound with\n   * some equations of state.\n   */\n  virtual Scalar<double> kappa_times_p_over_rho_squared_from_density(\n      const Scalar<double>& /*rest_mass_density*/) const = 0;\n  virtual Scalar<DataVector> kappa_times_p_over_rho_squared_from_density(\n      const Scalar<DataVector>& /*rest_mass_density*/) const = 0;\n\n  /// The lower bound of the electron fraction that is valid for this EOS\n  virtual double electron_fraction_lower_bound() const { return 0.0; }\n\n  /// The upper bound of the electron fraction that is valid for this EOS\n  virtual double electron_fraction_upper_bound() const { return 1.0; }\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  virtual double rest_mass_density_lower_bound() const = 0;\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  virtual double rest_mass_density_upper_bound() const = 0;\n\n  /// The lower bound of the temperature that is valid for this EOS\n  virtual double temperature_lower_bound() const { return 0.0; };\n\n  /// The upper bound of the temperature that is valid for this EOS\n  virtual double temperature_upper_bound() const {\n    return std::numeric_limits<double>::max();\n  };\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  virtual double specific_internal_energy_lower_bound() const { return 0.0; };\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  virtual double specific_internal_energy_upper_bound() const {\n    return std::numeric_limits<double>::max();\n  };\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  virtual double specific_enthalpy_lower_bound() const = 0;\n\n  /// The vacuum mass of a baryon for this EOS\n  virtual double baryon_mass() const {\n    return hydro::units::geometric::default_baryon_mass;\n  }\n};\n\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Base class for equations of state which need two independent\n * thermodynamic variables in order to determine the pressure.\n *\n * The template parameter `IsRelativistic` is `true` for relativistic equations\n * of state and `false` for non-relativistic equations of state.\n */\ntemplate <bool IsRelativistic>\nclass EquationOfState<IsRelativistic, 2> : public PUP::able {\n public:\n  static constexpr bool is_relativistic = IsRelativistic;\n  static constexpr size_t thermodynamic_dim = 2;\n  using creatable_classes =\n      typename detail::DerivedClasses<IsRelativistic, 2>::type;\n\n  EquationOfState() = default;\n  EquationOfState(const EquationOfState&) = default;\n  EquationOfState& operator=(const EquationOfState&) = default;\n  EquationOfState(EquationOfState&&) = default;\n  EquationOfState& operator=(EquationOfState&&) = default;\n  ~EquationOfState() override = default;\n\n  explicit EquationOfState(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  WRAPPED_PUPable_abstract(EquationOfState);  // NOLINT\n\n  virtual inline std::unique_ptr<EquationOfState<IsRelativistic, 2>> get_clone()\n      const = 0;\n\n  virtual bool is_equal(\n      const EquationOfState<IsRelativistic, 2>& rhs) const = 0;\n\n  virtual std::unique_ptr<EquationOfState<IsRelativistic, 2>>\n  promote_to_2d_eos() const {\n    return this->get_clone();\n  }\n\n  virtual std::unique_ptr<EquationOfState<IsRelativistic, 3>>\n  promote_to_3d_eos() const = 0;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  virtual bool is_barotropic() const = 0;\n\n  /// \\brief Returns `true` if the EOS is in beta-equilibrium\n  virtual bool is_equilibrium() const {\n    return true;\n  }\n\n  /// @{\n  /*!\n   * Computes the electron fraction in beta-equilibrium \\f$Y_e^{\\rm eq}\\f$ from\n   * the rest mass density \\f$\\rho\\f$ and the temperature \\f$T\\f$.\n   */\n  virtual Scalar<double> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<double>& rest_mass_density,\n      const Scalar<double>& /*temperature*/) const {\n    return make_with_value<Scalar<double>>(rest_mass_density, 0.1);\n  }\n\n  virtual Scalar<DataVector>\n  equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& /*temperature*/) const {\n    return make_with_value<Scalar<DataVector>>(rest_mass_density, 0.1);\n  }\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the pressure \\f$p\\f$ from the rest mass density \\f$\\rho\\f$ and the\n   * specific internal energy \\f$\\epsilon\\f$.\n   */\n  virtual Scalar<double> pressure_from_density_and_energy(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*specific_internal_energy*/) const = 0;\n  virtual Scalar<DataVector> pressure_from_density_and_energy(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*specific_internal_energy*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the pressure \\f$p\\f$ from the rest mass density \\f$\\rho\\f$ and the\n   * specific enthalpy \\f$h\\f$.\n   */\n  virtual Scalar<double> pressure_from_density_and_enthalpy(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*specific_enthalpy*/) const = 0;\n  virtual Scalar<DataVector> pressure_from_density_and_enthalpy(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*specific_enthalpy*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the specific internal energy \\f$\\epsilon\\f$ from the rest mass\n   * density \\f$\\rho\\f$ and the pressure \\f$p\\f$.\n   */\n  virtual Scalar<double> specific_internal_energy_from_density_and_pressure(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*pressure*/) const = 0;\n  virtual Scalar<DataVector> specific_internal_energy_from_density_and_pressure(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*pressure*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the temperature \\f$T\\f$ from the rest mass\n   * density \\f$\\rho\\f$ and the specific internal energy \\f$\\epsilon\\f$.\n   */\n  virtual Scalar<double> temperature_from_density_and_energy(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*specific_internal_energy*/) const = 0;\n  virtual Scalar<DataVector> temperature_from_density_and_energy(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*specific_internal_energy*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the specific internal energy \\f$\\epsilon\\f$ from the rest mass\n   * density \\f$\\rho\\f$ and the temperature \\f$T\\f$.\n   */\n  virtual Scalar<double> specific_internal_energy_from_density_and_temperature(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*temperature*/) const = 0;\n  virtual Scalar<DataVector>\n  specific_internal_energy_from_density_and_temperature(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*temperature*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes \\f$\\chi=\\partial p / \\partial \\rho |_{\\epsilon}\\f$ from the\n   * \\f$\\rho\\f$ and \\f$\\epsilon\\f$, where \\f$p\\f$ is the pressure, \\f$\\rho\\f$ is\n   * the rest mass density, and \\f$\\epsilon\\f$ is the specific internal energy.\n   */\n  virtual Scalar<double> chi_from_density_and_energy(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*specific_internal_energy*/) const = 0;\n  virtual Scalar<DataVector> chi_from_density_and_energy(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*specific_internal_energy*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes \\f$\\kappa p/\\rho^2=(p/\\rho^2)\\partial p / \\partial \\epsilon\n   * |_{\\rho}\\f$ from \\f$\\rho\\f$ and \\f$\\epsilon\\f$, where \\f$p\\f$ is the\n   * pressure, \\f$\\rho\\f$ is the rest mass density, and \\f$\\epsilon\\f$ is the\n   * specific internal energy.\n   *\n   * The reason for not returning just\n   * \\f$\\kappa=\\partial p / \\partial \\epsilon\\f$ is to avoid division by zero\n   * for small values of \\f$\\rho\\f$ when assembling the speed of sound with\n   * some equations of state.\n   */\n  virtual Scalar<double> kappa_times_p_over_rho_squared_from_density_and_energy(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*specific_internal_energy*/) const = 0;\n  virtual Scalar<DataVector>\n  kappa_times_p_over_rho_squared_from_density_and_energy(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*specific_internal_energy*/) const = 0;\n  /// @}\n\n  /// The lower bound of the electron fraction that is valid for this EOS\n  virtual double electron_fraction_lower_bound() const { return 0.0; }\n\n  /// The upper bound of the electron fraction that is valid for this EOS\n  virtual double electron_fraction_upper_bound() const { return 1.0; }\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  virtual double rest_mass_density_lower_bound() const = 0;\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  virtual double rest_mass_density_upper_bound() const = 0;\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  virtual double specific_internal_energy_lower_bound(\n      const double rest_mass_density) const = 0;\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  virtual double specific_internal_energy_upper_bound(\n      const double rest_mass_density) const = 0;\n\n  /// The lower bound of the temperature that is valid for this EOS\n  virtual double temperature_lower_bound() const { return 0.0; };\n\n  /// The upper bound of the temperature that is valid for this EOS\n  virtual double temperature_upper_bound() const {\n    return std::numeric_limits<double>::max();\n  };\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  virtual double specific_enthalpy_lower_bound() const = 0;\n\n  /// The vacuum mass of a baryon for this EOS\n  virtual double baryon_mass() const {\n    return hydro::units::geometric::default_baryon_mass;\n  }\n};\n\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Base class for equations of state which need three independent\n * thermodynamic variables in order to determine the pressure.\n *\n * The template parameter `IsRelativistic` is `true` for relativistic equations\n * of state and `false` for non-relativistic equations of state.\n */\ntemplate <bool IsRelativistic>\nclass EquationOfState<IsRelativistic, 3> : public PUP::able {\n public:\n  static constexpr bool is_relativistic = IsRelativistic;\n  static constexpr size_t thermodynamic_dim = 3;\n  using creatable_classes =\n      typename detail::DerivedClasses<IsRelativistic, 3>::type;\n\n  EquationOfState() = default;\n  EquationOfState(const EquationOfState&) = default;\n  EquationOfState& operator=(const EquationOfState&) = default;\n  EquationOfState(EquationOfState&&) = default;\n  EquationOfState& operator=(EquationOfState&&) = default;\n  ~EquationOfState() override = default;\n\n  explicit EquationOfState(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n  WRAPPED_PUPable_abstract(EquationOfState);  // NOLINT\n\n  virtual inline std::unique_ptr<EquationOfState<IsRelativistic, 3>> get_clone()\n      const = 0;\n\n  virtual bool is_equal(\n      const EquationOfState<IsRelativistic, 3>& rhs) const = 0;\n\n  virtual std::unique_ptr<EquationOfState<IsRelativistic, 3>>\n  promote_to_3d_eos() const {\n    return this->get_clone();\n  }\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  virtual bool is_barotropic() const = 0;\n\n  /// \\brief Returns `true` if the EOS is in beta-equilibrium\n  virtual bool is_equilibrium() const = 0;\n\n  /// @{\n  /*!\n   * Computes the electron fraction in beta-equilibrium \\f$Y_e^{\\rm eq}\\f$ from\n   * the rest mass density \\f$\\rho\\f$ and the temperature \\f$T\\f$.\n   */\n  virtual Scalar<double> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*temperature*/) const = 0;\n\n  virtual Scalar<DataVector>\n  equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*temperature*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the pressure \\f$p\\f$ from the rest mass density \\f$\\rho\\f$, the\n   * specific internal energy \\f$\\epsilon\\f$ and electron fraction \\f$Y_e\\f$.\n   */\n  virtual Scalar<double> pressure_from_density_and_energy(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*specific_internal_energy*/,\n      const Scalar<double>& /*electron_fraction*/) const = 0;\n  virtual Scalar<DataVector> pressure_from_density_and_energy(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*specific_internal_energy*/,\n      const Scalar<DataVector>& /*electron_fraction*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the pressure \\f$p\\f$ from the rest mass density \\f$\\rho\\f$, the\n   * temperature \\f$T\\f$, and electron fraction \\f$Y_e\\f$.\n   */\n  virtual Scalar<double> pressure_from_density_and_temperature(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*temperature*/,\n      const Scalar<double>& /*electron_fraction*/) const = 0;\n  virtual Scalar<DataVector> pressure_from_density_and_temperature(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*temperature*/,\n      const Scalar<DataVector>& /*electron_fraction*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the temperature \\f$T\\f$ from the rest mass\n   * density \\f$\\rho\\f$, the specific internal energy \\f$\\epsilon\\f$,\n   * and electron fraction \\f$Y_e\\f$.\n   */\n  virtual Scalar<double> temperature_from_density_and_energy(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*specific_internal_energy*/,\n      const Scalar<double>& /*electron_fraction*/) const = 0;\n  virtual Scalar<DataVector> temperature_from_density_and_energy(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*specific_internal_energy*/,\n      const Scalar<DataVector>& /*electron_fraction*/) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes the specific internal energy \\f$\\epsilon\\f$ from the rest mass\n   * density \\f$\\rho\\f$, the temperature \\f$T\\f$, and electron fraction\n   * \\f$Y_e\\f$.\n   */\n  virtual Scalar<double> specific_internal_energy_from_density_and_temperature(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*temperature*/,\n      const Scalar<double>& /*electron_fraction*/\n  ) const = 0;\n  virtual Scalar<DataVector>\n  specific_internal_energy_from_density_and_temperature(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*temperature*/,\n      const Scalar<DataVector>& /*electron_fraction*/\n  ) const = 0;\n  /// @}\n\n  /// @{\n  /*!\n   * Computes adiabatic sound speed squared\n   * \\f[\n   * c_s^2  \\equiv \\frac{\\partial p}{\\partial e} |_{s, Y_e} =\n   * \\frac{\\rho}{h}\\frac{\\partial p}{\\partial \\rho} |_{e, Y_e} +\n   * \\frac{\\partial p}{\\partial e}|_{\\rho, Y_e}\n   * \\f].\n   * With \\f$p, e\\f$ the pressure and energy density respectively,\n   * \\f$s\\f$ the entropy density, \\f$Y_e\\f$ the electron fraction\n   * \\f$\\rho\\f$ the rest-mass density, and \\f$h\\f$ the enthalpy density.\n   * Note that \\f$e\\f$ is the total energy density and not the internal energy,\n   * therefore\n   * \\f[\n   * \\frac{\\partial p}{\\partial \\rho} |_{e, Y_e} \\neq \\chi \\equiv \\frac{\\partial\n   * p}{\\partial \\rho} |_{\\epsilon, Y_e}\n   * \\f]\n   * as defined in the 2-d EoS above. By definition\n   * \\f$ e = (1+\\epsilon) \\rho \\f$ so holding \\f$e\\f$ constant\n   * \\f[\n   *  0 = \\frac{d e}{d \\rho} = \\frac{\\partial e}{\\partial \\rho} +\n   *     \\frac{\\partial e}{\\partial \\epsilon} \\frac{\\partial \\epsilon}{\\rho}.\n   * \\f]\n   * (where we have suppressed \\f$ Y_e\\f$ dependence)\n   * So \\f$ \\partial \\epsilon /  \\partial \\rho |_{e} = (1 + \\epsilon)/\\rho \\f$\n   * and we can expand\n   * \\f[\n   * \\frac{\\partial p}{\\partial \\rho} |_{e, Y_e} = \\frac{\\partial e}{\\partial\n   * \\rho}_{\\epsilon, Y_e} + \\frac{(1 + \\epsilon)}{\\rho} \\frac{\\partial\n   * e}{\\partial \\epsilon}|_{\\rho, Y_e}\n   * \\f]\n   *  Finally, we can rewrite the entire sound speed using only the rest-mass\n   * density, specific internal energy, and electron fraction as variables,\n   * by using \\f$ \\frac{\\partial e}{\\partial \\epsilon}|_{\\rho, Y_e} = 1 \\f$\n   * \\f[\n   * c_s^2   =\n   * \\frac{\\rho}{h}\\frac{\\partial p}{\\partial \\rho} |_{\\epsilon, Y_e} +\n   * \\frac{1}{\\rho} \\frac{\\partial p}{\\partial \\epsilon}|_{\\rho, Y_e} \\left(\n   * 1 - \\frac{(1 + \\epsilon)\\rho}{h}\\right)\n   * \\f]\n   * Which reduces to our preferred form\n   * \\f[\n   * c_s^2 =\n   * \\frac{\\rho}{h}(\\chi + \\kappa)\n   * \\f]\n   *\n   * Computed as a function of temperature, rest-mass density and electron\n   * fraction. Note that this will break thermodynamic consistency if the\n   * pressure and internal energy interpolated separately. The precise impact of\n   * this will depend on the EoS and numerical scheme used for the evolution.\n   */\n  virtual Scalar<double> sound_speed_squared_from_density_and_temperature(\n      const Scalar<double>& /*rest_mass_density*/,\n      const Scalar<double>& /*temperature*/,\n      const Scalar<double>& /*electron_fraction*/) const = 0;\n  virtual Scalar<DataVector> sound_speed_squared_from_density_and_temperature(\n      const Scalar<DataVector>& /*rest_mass_density*/,\n      const Scalar<DataVector>& /*temperature*/,\n      const Scalar<DataVector>& /*electron_fraction*/) const = 0;\n  /// @}\n\n  /// The lower bound of the electron fraction that is valid for this EOS\n  virtual double electron_fraction_lower_bound() const = 0;\n\n  /// The upper bound of the electron fraction that is valid for this EOS\n  virtual double electron_fraction_upper_bound() const = 0;\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  virtual double rest_mass_density_lower_bound() const = 0;\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  virtual double rest_mass_density_upper_bound() const = 0;\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$ and electron fraction \\f$Y_e\\f$.\n  virtual double specific_internal_energy_lower_bound(\n      const double rest_mass_density, const double electron_fraction) const = 0;\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$ and electron fraction \\f$Y_e\\f$.\n  virtual double specific_internal_energy_upper_bound(\n      const double rest_mass_density, const double electron_fraction) const = 0;\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  virtual double specific_enthalpy_lower_bound() const = 0;\n\n  /// The lower bound of the temperature that is valid for this EOS\n  virtual double temperature_lower_bound() const = 0;\n\n  /// The upper bound of the temperature that is valid for this EOS\n  virtual double temperature_upper_bound() const = 0;\n\n  /// The vacuum mass of a baryon for this EOS\n  virtual double baryon_mass() const {\n    return hydro::units::geometric::default_baryon_mass;\n  }\n};\n\n/// Compare two equations of state for equality\ntemplate <bool IsRelLhs, bool IsRelRhs, size_t ThermoDimLhs,\n          size_t ThermoDimRhs>\nbool operator==(const EquationOfState<IsRelLhs, ThermoDimLhs>& lhs,\n                const EquationOfState<IsRelRhs, ThermoDimRhs>& rhs) {\n  if constexpr (IsRelLhs == IsRelRhs and ThermoDimLhs == ThermoDimRhs) {\n    return typeid(lhs) == typeid(rhs) and lhs.is_equal(rhs);\n  } else {\n    return false;\n  }\n}\ntemplate <bool IsRelLhs, bool IsRelRhs, size_t ThermoDimLhs,\n          size_t ThermoDimRhs>\nbool operator!=(const EquationOfState<IsRelLhs, ThermoDimLhs>& lhs,\n                const EquationOfState<IsRelRhs, ThermoDimRhs>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace EquationsOfState\n\n/// \\cond\n#define EQUATION_OF_STATE_FUNCTIONS_1D                      \\\n  (pressure_from_density, rest_mass_density_from_enthalpy,  \\\n   specific_internal_energy_from_density, chi_from_density, \\\n   kappa_times_p_over_rho_squared_from_density)\n\n#define EQUATION_OF_STATE_FUNCTIONS_2D                                   \\\n  (pressure_from_density_and_energy, pressure_from_density_and_enthalpy, \\\n   specific_internal_energy_from_density_and_pressure,                   \\\n   temperature_from_density_and_energy,                                  \\\n   specific_internal_energy_from_density_and_temperature,                \\\n   chi_from_density_and_energy,                                          \\\n   kappa_times_p_over_rho_squared_from_density_and_energy)\n\n#define EQUATION_OF_STATE_FUNCTIONS_3D                                      \\\n  (pressure_from_density_and_energy, pressure_from_density_and_temperature, \\\n   temperature_from_density_and_energy,                                     \\\n   specific_internal_energy_from_density_and_temperature,                   \\\n   sound_speed_squared_from_density_and_temperature)\n\n#define EQUATION_OF_STATE_ARGUMENTS_EXPAND(z, n, type) \\\n  BOOST_PP_COMMA_IF(n) const Scalar<type>&\n\n#define EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS_HELPER(r, DIM,        \\\n                                                         FUNCTION_NAME) \\\n  Scalar<double> FUNCTION_NAME(BOOST_PP_REPEAT(                         \\\n      DIM, EQUATION_OF_STATE_ARGUMENTS_EXPAND, double)) const override; \\\n  Scalar<DataVector> FUNCTION_NAME(BOOST_PP_REPEAT(                     \\\n      DIM, EQUATION_OF_STATE_ARGUMENTS_EXPAND, DataVector)) const override;\n\n/// \\endcond\n\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Macro used to generate forward declarations of member functions in\n * derived classes\n */\n#define EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(DERIVED, DIM)            \\\n  BOOST_PP_LIST_FOR_EACH(                                                  \\\n      EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS_HELPER, DIM,               \\\n      BOOST_PP_TUPLE_TO_LIST(BOOST_PP_TUPLE_ELEM(                          \\\n          BOOST_PP_SUB(DIM, 1),                                            \\\n          (EQUATION_OF_STATE_FUNCTIONS_1D, EQUATION_OF_STATE_FUNCTIONS_2D, \\\n           EQUATION_OF_STATE_FUNCTIONS_3D))))                              \\\n                                                                           \\\n  /* clang-tidy: do not use non-const references */                        \\\n  void pup(PUP::er& p) override; /* NOLINT */                              \\\n                                                                           \\\n  explicit DERIVED(CkMigrateMessage* msg);\n\n/// \\cond\n#define EQUATION_OF_STATE_FORWARD_ARGUMENTS(z, n, unused) \\\n  BOOST_PP_COMMA_IF(n) arg##n\n\n#define EQUATION_OF_STATE_ARGUMENTS_EXPAND_NAMED(z, n, type) \\\n  BOOST_PP_COMMA_IF(n) const Scalar<type>& arg##n\n\n#define EQUATION_OF_STATE_MEMBER_DEFINITIONS_HELPER(                        \\\n    TEMPLATE, DERIVED, DATA_TYPE, DIM, FUNCTION_NAME)                       \\\n  TEMPLATE                                                                  \\\n  Scalar<DATA_TYPE> DERIVED::FUNCTION_NAME(BOOST_PP_REPEAT(                 \\\n      DIM, EQUATION_OF_STATE_ARGUMENTS_EXPAND_NAMED, DATA_TYPE)) const {    \\\n    return FUNCTION_NAME##_impl(                                            \\\n        BOOST_PP_REPEAT(DIM, EQUATION_OF_STATE_FORWARD_ARGUMENTS, UNUSED)); \\\n  }\n\n#define EQUATION_OF_STATE_MEMBER_DEFINITIONS_HELPER_2(r, ARGS, FUNCTION_NAME) \\\n  EQUATION_OF_STATE_MEMBER_DEFINITIONS_HELPER(                                \\\n      BOOST_PP_TUPLE_ELEM(0, ARGS), BOOST_PP_TUPLE_ELEM(1, ARGS),             \\\n      BOOST_PP_TUPLE_ELEM(2, ARGS), BOOST_PP_TUPLE_ELEM(3, ARGS),             \\\n      FUNCTION_NAME)\n/// \\endcond\n\n#define EQUATION_OF_STATE_MEMBER_DEFINITIONS(TEMPLATE, DERIVED, DATA_TYPE, \\\n                                             DIM)                          \\\n  BOOST_PP_LIST_FOR_EACH(                                                  \\\n      EQUATION_OF_STATE_MEMBER_DEFINITIONS_HELPER_2,                       \\\n      (TEMPLATE, DERIVED, DATA_TYPE, DIM),                                 \\\n      BOOST_PP_TUPLE_TO_LIST(BOOST_PP_TUPLE_ELEM(                          \\\n          BOOST_PP_SUB(DIM, 1),                                            \\\n          (EQUATION_OF_STATE_FUNCTIONS_1D, EQUATION_OF_STATE_FUNCTIONS_2D, \\\n           EQUATION_OF_STATE_FUNCTIONS_3D))))\n\n/// \\cond\n#define EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS_HELPER(r, DIM,        \\\n                                                              FUNCTION_NAME) \\\n  template <class DataType>                                                  \\\n  Scalar<DataType> FUNCTION_NAME##_impl(BOOST_PP_REPEAT(                     \\\n      DIM, EQUATION_OF_STATE_ARGUMENTS_EXPAND, DataType)) const;\n/// \\endcond\n\n#define EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(DIM)                \\\n  BOOST_PP_LIST_FOR_EACH(                                                  \\\n      EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS_HELPER, DIM,          \\\n      BOOST_PP_TUPLE_TO_LIST(BOOST_PP_TUPLE_ELEM(                          \\\n          BOOST_PP_SUB(DIM, 1),                                            \\\n          (EQUATION_OF_STATE_FUNCTIONS_1D, EQUATION_OF_STATE_FUNCTIONS_2D, \\\n           EQUATION_OF_STATE_FUNCTIONS_3D))))\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/DarkEnergyFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/HybridEos.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace EquationsOfState {\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename EquilEos>,\n                                     Equilibrium3D<EquilEos>, double, 3)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename EquilEos>,\n                                     Equilibrium3D<EquilEos>, DataVector, 3)\ntemplate <typename EquilEos>\nvoid Equilibrium3D<EquilEos>::pup(PUP::er& p) {\n  EquationOfState<EquilEos::is_relativistic, 3>::pup(p);\n  p | underlying_eos_;\n}\ntemplate <typename EquilEos>\nEquilibrium3D<EquilEos>::Equilibrium3D(CkMigrateMessage* msg)\n    : EquationOfState<EquilEos::is_relativistic, 3>(msg) {}\n\ntemplate <typename EquilEos>\nstd::unique_ptr<EquationOfState<EquilEos::is_relativistic, 3>>\nEquilibrium3D<EquilEos>::get_clone() const {\n  auto clone = std::make_unique<Equilibrium3D<EquilEos>>(*this);\n  return std::unique_ptr<EquationOfState<is_relativistic, 3>>(std::move(clone));\n}\n\ntemplate <typename EquilEos>\nbool Equilibrium3D<EquilEos>::is_equal(\n    const EquationOfState<EquilEos::is_relativistic, 3>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const Equilibrium3D<EquilEos>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <typename EquilEos>\nbool Equilibrium3D<EquilEos>::operator==(\n    const Equilibrium3D<EquilEos>& rhs) const {\n  return this->underlying_eos_ == rhs.underlying_eos_;\n}\ntemplate <typename EquilEos>\nbool Equilibrium3D<EquilEos>::operator!=(\n    const Equilibrium3D<EquilEos>& rhs) const {\n  return !(*this == rhs);\n}\n\ntemplate <typename EquilEos>\ntemplate <class DataType>\nScalar<DataType>\nEquilibrium3D<EquilEos>::pressure_from_density_and_temperature_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& temperature,\n    const Scalar<DataType>& /*electron_fraction*/) const {\n  return underlying_eos_.pressure_from_density_and_energy(\n      rest_mass_density,\n      underlying_eos_.specific_internal_energy_from_density_and_temperature(\n          rest_mass_density, temperature));\n}\ntemplate <typename EquilEos>\ntemplate <class DataType>\nScalar<DataType> Equilibrium3D<EquilEos>::pressure_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& /*electron_fraction*/) const {\n  return underlying_eos_.pressure_from_density_and_energy(\n      rest_mass_density, specific_internal_energy);\n}\ntemplate <typename EquilEos>\ntemplate <class DataType>\nScalar<DataType>\nEquilibrium3D<EquilEos>::temperature_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& /*electron_fraction*/) const {\n  return underlying_eos_.temperature_from_density_and_energy(\n      rest_mass_density, specific_internal_energy);\n}\n\ntemplate <typename EquilEos>\ntemplate <class DataType>\nScalar<DataType> Equilibrium3D<EquilEos>::\n    specific_internal_energy_from_density_and_temperature_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& temperature,\n        const Scalar<DataType>& /*electron_fraction*/) const {\n  return underlying_eos_.specific_internal_energy_from_density_and_temperature(\n      rest_mass_density, temperature);\n}\n\ntemplate <typename EquilEos>\ntemplate <class DataType>\nScalar<DataType>\nEquilibrium3D<EquilEos>::sound_speed_squared_from_density_and_temperature_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& temperature,\n    const Scalar<DataType>& /*electron_fraction*/) const {\n  const Scalar<DataType> specific_internal_energy =\n      underlying_eos_.specific_internal_energy_from_density_and_temperature(\n          rest_mass_density, temperature);\n  const DataType pressure =\n      get(underlying_eos_.pressure_from_density_and_energy(\n          rest_mass_density, specific_internal_energy));\n  const DataType enthalpy_density =\n      pressure + (1.0 + get(specific_internal_energy)) * get(rest_mass_density);\n  // Cold part plus temperature dependent part, see the 3D EoS documentation for\n  // expression.\n  return Scalar<DataType>{\n      get(rest_mass_density) / enthalpy_density *\n      (get(underlying_eos_.chi_from_density_and_energy(\n           rest_mass_density, specific_internal_energy)) +\n       get(underlying_eos_\n               .kappa_times_p_over_rho_squared_from_density_and_energy(\n                   rest_mass_density, specific_internal_energy)))};\n}\n\ntemplate class Equilibrium3D<HybridEos<PolytropicFluid<true>>>;\ntemplate class Equilibrium3D<HybridEos<PolytropicFluid<false>>>;\ntemplate class Equilibrium3D<HybridEos<Spectral>>;\ntemplate class Equilibrium3D<HybridEos<Enthalpy<Spectral>>>;\ntemplate class Equilibrium3D<DarkEnergyFluid<true>>;\ntemplate class Equilibrium3D<IdealFluid<true>>;\ntemplate class Equilibrium3D<IdealFluid<false>>;\n\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief A 3D equation of state representing a fluid in compositional\n * equalibrium.\n *\n *\n * The equation of state takes the form\n *\n * \\f[\n * p = p (T, rho, Y_e) = p(T, rho, Y_e= Y_{e, \\beta})\n * \\f]\n *\n * where \\f$\\rho\\f$ is the rest mass density and  \\f$T\\f$ is the\n * temperaturee;   \\f$Y_e\\f$  the electron fraction,  is not\n * used, and therefore this evaluating this EoS at any arbtirary\n * electron fraction is equivalent to evaluating it  in beta equalibrium\n *\n */\ntemplate <typename EquilEos>\nclass Equilibrium3D : public EquationOfState<EquilEos::is_relativistic, 3> {\n public:\n  static constexpr size_t thermodynamic_dim = 3;\n  static constexpr bool is_relativistic = EquilEos::is_relativistic;\n  static std::string name() {\n    return \"Equilibrium3D(\" + pretty_type::name<EquilEos>() + \")\";\n  }\n  static constexpr Options::String help = {\n      \"An 3D EoS which is independent of electron fraction. \"\n      \"Contains an underlying 2D EoS which is dependent only \"\n      \"on rest mass density and temperature/internal energy.\"};\n\n  struct UnderlyingEos {\n    using type = EquilEos;\n    static std::string name() { return pretty_type::short_name<EquilEos>(); }\n    static constexpr Options::String help{\n        \"The underlying Eos which is being represented as a \"\n        \"3D Eos.  Must be a 2D EoS\"};\n  };\n\n  using options = tmpl::list<UnderlyingEos>;\n\n  Equilibrium3D() = default;\n  Equilibrium3D(const Equilibrium3D&) = default;\n  Equilibrium3D& operator=(const Equilibrium3D&) = default;\n  Equilibrium3D(Equilibrium3D&&) = default;\n  Equilibrium3D& operator=(Equilibrium3D&&) = default;\n  ~Equilibrium3D() override = default;\n\n  explicit Equilibrium3D(const EquilEos& underlying_eos)\n      : underlying_eos_(underlying_eos){};\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(Equilibrium3D, 3)\n\n  std::unique_ptr<EquationOfState<EquilEos::is_relativistic, 3>> get_clone()\n      const override;\n\n  bool is_equal(\n      const EquationOfState<EquilEos::is_relativistic, 3>& rhs) const override;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  bool is_barotropic() const override { return false; }\n\n  /// \\brief Returns `true` if the EOS is in beta-equilibrium\n  bool is_equilibrium() const override { return true; }\n\n  bool operator==(const Equilibrium3D<EquilEos>& rhs) const;\n\n  bool operator!=(const Equilibrium3D<EquilEos>& rhs) const;\n  /// @{\n  /*!\n   * Computes the electron fraction in beta-equilibrium \\f$Y_e^{\\rm eq}\\f$ from\n   * the rest mass density \\f$\\rho\\f$ and the temperature \\f$T\\f$.\n   */\n  Scalar<double> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<double>& rest_mass_density,\n      const Scalar<double>& temperature) const override{\n    return underlying_eos_\n        .equilibrium_electron_fraction_from_density_temperature(\n            rest_mass_density, temperature);\n  }\n\n  Scalar<DataVector> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& temperature) const override {\n    return underlying_eos_\n        .equilibrium_electron_fraction_from_density_temperature(\n            rest_mass_density, temperature);\n  }\n  /// @}\n  //\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<EquilEos::is_relativistic, 3>), Equilibrium3D);\n\n  /// The lower bound of the electron fraction that is valid for this EOS\n  double electron_fraction_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the electron fraction that is valid for this EOS\n  double electron_fraction_upper_bound() const override { return 1.0; }\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override {\n    return underlying_eos_.rest_mass_density_lower_bound();\n  }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return underlying_eos_.rest_mass_density_upper_bound();\n  }\n\n  /// The lower bound of the temperature that is valid for this EOS\n  double temperature_lower_bound() const override {\n    return underlying_eos_.temperature_lower_bound();\n  }\n\n  /// The upper bound of the temperature that is valid for this EOS\n  double temperature_upper_bound() const override {\n    return underlying_eos_.temperature_upper_bound();\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$ and electron fraction \\f$Y_e\\f$\n  double specific_internal_energy_lower_bound(\n      const double rest_mass_density,\n      const double /*electron_fraction*/) const override {\n    return underlying_eos_.specific_internal_energy_lower_bound(\n        rest_mass_density);\n  }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_upper_bound(\n      const double rest_mass_density,\n      const double /*electron_fraction*/) const override {\n    return underlying_eos_.specific_internal_energy_upper_bound(\n        rest_mass_density);\n  }\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override {\n    return underlying_eos_.specific_enthalpy_lower_bound();\n  }\n\n  /// The baryon mass for this EoS\n  double baryon_mass() const override { return underlying_eos_.baryon_mass(); }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(3)\n  EquilEos underlying_eos_;\n};\n/// \\cond\ntemplate <typename EquilEos>\nPUP::able::PUP_ID EquationsOfState::Equilibrium3D<EquilEos>::my_PUP_ID = 0;\n/// \\endcond\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/DarkEnergyFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/HybridEos.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/HybridEos.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/HybridEos.hpp\"\n\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions//Hydro/EquationsOfState/Enthalpy.hpp\"\n#include \"PointwiseFunctions//Hydro/EquationsOfState/Spectral.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace EquationsOfState {\ntemplate <typename ColdEquationOfState>\nHybridEos<ColdEquationOfState>::HybridEos(ColdEquationOfState cold_eos,\n                                          const double thermal_adiabatic_index)\n    : cold_eos_(std::move(cold_eos)),\n      thermal_adiabatic_index_(thermal_adiabatic_index) {}\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename ColdEquationOfState>,\n                                     HybridEos<ColdEquationOfState>, double, 2)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <typename ColdEquationOfState>,\n                                     HybridEos<ColdEquationOfState>, DataVector,\n                                     2)\n\ntemplate <typename ColdEquationOfState>\nstd::unique_ptr<\n    EquationOfState<HybridEos<ColdEquationOfState>::is_relativistic, 2>>\nHybridEos<ColdEquationOfState>::get_clone() const {\n  auto clone = std::make_unique<HybridEos<ColdEquationOfState>>(*this);\n  return std::unique_ptr<EquationOfState<is_relativistic, 2>>(std::move(clone));\n}\n\ntemplate <typename ColdEquationOfState>\nstd::unique_ptr<\n    EquationOfState<HybridEos<ColdEquationOfState>::is_relativistic, 3>>\nHybridEos<ColdEquationOfState>::promote_to_3d_eos() const {\n  return std::make_unique<Equilibrium3D<HybridEos<ColdEquationOfState>>>(\n      Equilibrium3D(*this));\n}\n\ntemplate <typename ColdEquationOfState>\nbool HybridEos<ColdEquationOfState>::operator==(\n    const HybridEos<ColdEquationOfState>& rhs) const {\n  return cold_eos_ == rhs.cold_eos_ and\n         thermal_adiabatic_index_ == rhs.thermal_adiabatic_index_;\n}\n\ntemplate <typename ColdEquationOfState>\nbool HybridEos<ColdEquationOfState>::operator!=(\n    const HybridEos<ColdEquationOfState>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <typename ColdEquationOfState>\nbool HybridEos<ColdEquationOfState>::is_equal(\n    const EquationOfState<is_relativistic, 2>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const HybridEos<ColdEquationOfState>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <typename ColdEquationOfState>\nHybridEos<ColdEquationOfState>::HybridEos(CkMigrateMessage* msg)\n    : EquationOfState<is_relativistic, 2>(msg) {}\n\ntemplate <typename ColdEquationOfState>\nvoid HybridEos<ColdEquationOfState>::pup(PUP::er& p) {\n  EquationOfState<is_relativistic, 2>::pup(p);\n  p | cold_eos_;\n  p | thermal_adiabatic_index_;\n}\n\ntemplate <typename ColdEquationOfState>\ntemplate <class DataType>\nScalar<DataType>\nHybridEos<ColdEquationOfState>::pressure_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy) const {\n  using std::max;\n  return Scalar<DataType>{\n      get(cold_eos_.pressure_from_density(rest_mass_density)) +\n      get(rest_mass_density) * (thermal_adiabatic_index_ - 1.0) *\n          max((get(specific_internal_energy) -\n               get(cold_eos_.specific_internal_energy_from_density(\n                   rest_mass_density))),\n              0.0)};\n}\n\ntemplate <typename ColdEquationOfState>\ntemplate <class DataType>\nScalar<DataType>\nHybridEos<ColdEquationOfState>::pressure_from_density_and_enthalpy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_enthalpy) const {\n  using std::max;\n  if constexpr (ColdEquationOfState::is_relativistic) {\n    return Scalar<DataType>{\n        (get(cold_eos_.pressure_from_density(rest_mass_density)) +\n         get(rest_mass_density) * (thermal_adiabatic_index_ - 1.0) *\n             max((get(specific_enthalpy) - 1.0 -\n                  get(cold_eos_.specific_internal_energy_from_density(\n                      rest_mass_density))), 0.0)) /\n        thermal_adiabatic_index_};\n  } else {\n    return Scalar<DataType>{\n        (get(cold_eos_.pressure_from_density(rest_mass_density)) +\n         get(rest_mass_density) * (thermal_adiabatic_index_ - 1.0) *\n             max((get(specific_enthalpy) -\n                  get(cold_eos_.specific_internal_energy_from_density(\n                      rest_mass_density))), 0.0)) /\n        thermal_adiabatic_index_};\n  }\n}\n\ntemplate <typename ColdEquationOfState>\ntemplate <class DataType>\nScalar<DataType> HybridEos<ColdEquationOfState>::\n    specific_internal_energy_from_density_and_pressure_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& pressure) const {\n  using std::max;\n  return Scalar<DataType>{\n      get(cold_eos_.specific_internal_energy_from_density(rest_mass_density)) +\n      1.0 / (thermal_adiabatic_index_ - 1.0) *\n          max((get(pressure) -\n               get(cold_eos_.pressure_from_density(rest_mass_density))), 0.0) /\n          get(rest_mass_density)};\n}\n\ntemplate <typename ColdEquationOfState>\ntemplate <class DataType>\nScalar<DataType>\nHybridEos<ColdEquationOfState>::temperature_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy) const {\n  using std::max;\n  return Scalar<DataType>{(thermal_adiabatic_index_ - 1.0) *\n                          max((get(specific_internal_energy) -\n                           get(cold_eos_.specific_internal_energy_from_density(\n                               rest_mass_density))), 0.0)};\n}\n\ntemplate <typename ColdEquationOfState>\ntemplate <class DataType>\nScalar<DataType> HybridEos<ColdEquationOfState>::\n    specific_internal_energy_from_density_and_temperature_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& temperature) const {\n  return Scalar<DataType>{\n      get(cold_eos_.specific_internal_energy_from_density(rest_mass_density)) +\n      get(temperature) / (thermal_adiabatic_index_ - 1.0)};\n}\n\ntemplate <typename ColdEquationOfState>\ntemplate <class DataType>\nScalar<DataType>\nHybridEos<ColdEquationOfState>::chi_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{\n      get(cold_eos_.chi_from_density(rest_mass_density)) +\n      (thermal_adiabatic_index_ - 1.0) *\n          (get(specific_internal_energy) -\n           get(cold_eos_.specific_internal_energy_from_density(\n               rest_mass_density)) -\n           get(cold_eos_.pressure_from_density(rest_mass_density)) /\n               get(rest_mass_density))};\n}\n\ntemplate <typename ColdEquationOfState>\ntemplate <class DataType>\nScalar<DataType> HybridEos<ColdEquationOfState>::\n    kappa_times_p_over_rho_squared_from_density_and_energy_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& specific_internal_energy) const {\n  using std::max;\n  return Scalar<DataType>{\n      (thermal_adiabatic_index_ - 1.0) *\n          get(cold_eos_.pressure_from_density(rest_mass_density)) /\n          get(rest_mass_density) +\n      square(thermal_adiabatic_index_ - 1.0) *\n          max((get(specific_internal_energy) -\n               get(cold_eos_.specific_internal_energy_from_density(\n                   rest_mass_density))), 0.0)};\n}\n}  // namespace EquationsOfState\n\ntemplate class EquationsOfState::HybridEos<\n    EquationsOfState::PolytropicFluid<true>>;\ntemplate class EquationsOfState::HybridEos<\n    EquationsOfState::PolytropicFluid<false>>;\ntemplate class EquationsOfState::HybridEos<EquationsOfState::Spectral>;\ntemplate class EquationsOfState::HybridEos<\n    EquationsOfState::Enthalpy<EquationsOfState::Spectral>>;\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/HybridEos.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n *\n * \\brief Hybrid equation of state combining a barotropic EOS for cold\n * (zero-temperature) part with a simple thermal part\n *\n * The hybrid equation of state:\n *\n * \\f[\n * p = p_{cold}(\\rho) + \\rho (\\Gamma_{th}-1) (\\epsilon - \\epsilon_{cold}(\\rho))\n * \\f]\n *\n * where \\f$p\\f$ is the pressure, \\f$\\rho\\f$ is the rest mass density,\n * \\f$\\epsilon\\f$ is the specific internal energy, \\f$p_{cold}\\f$ and\n * \\f$\\epsilon_{cold}\\f$ are the pressure and specific internal energy evaluated\n * using the cold EOS, and \\f$\\Gamma_{th}\\f$ is the adiabatic index for the\n * thermal part.\n *\n * The temperature \\f$T\\f$ is defined as\n *\n * \\f[\n * T = (\\Gamma_{th} - 1) (\\epsilon - \\epsilon_{cold})\n * \\f]\n *\n * This is the amount of internal energy above that of the cold EOS.\n *\n * For a hybrid EOS with the cold EOS being a polytrope we have\n *\n * \\f{align}\n * p &= \\kappa \\rho^\\Gamma + \\rho T\n *     =\\kappa\\rho^\\Gamma+\\rho\n *     (\\Gamma-1)\\left(\\epsilon-\\frac{K\\rho^{\\Gamma-1}}{\\Gamma-1}\\right), \\\\\n * T&=(\\Gamma-1)\\left(\\epsilon-\\frac{K\\rho^{\\Gamma-1}}{\\Gamma-1}\\right), \\\\\n * \\epsilon &= \\frac{1}{(\\Gamma-1)}\\frac{p}{\\rho}\n * \\f}\n */\ntemplate <typename ColdEquationOfState>\nclass HybridEos\n    : public EquationOfState<ColdEquationOfState::is_relativistic, 2> {\n public:\n  static constexpr size_t thermodynamic_dim = 2;\n  static constexpr bool is_relativistic = ColdEquationOfState::is_relativistic;\n\n  struct ColdEos {\n    using type = ColdEquationOfState;\n    static constexpr Options::String help = {\"Cold equation of state\"};\n    static std::string name() {\n      return pretty_type::short_name<ColdEquationOfState>();\n    }\n  };\n\n  struct ThermalAdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\"Adiabatic index Gamma_th\"};\n  };\n\n  static constexpr Options::String help = {\n      \"A hybrid equation of state combining a cold EOS with a simple thermal \"\n      \"part.  The pressure is related to the rest mass density by \"\n      \" p = p_cold(rho) + rho * (Gamma_th - 1) * (epsilon - \"\n      \"epsilon_cold(rho)), where p is the pressure, rho is the rest mass \"\n      \"density, epsilon is the specific internal energy, p_cold and \"\n      \"epsilon_cold are the pressure and specific internal energy evaluated \"\n      \"using the cold EOS and Gamma_th is the adiabatic index for the thermal \"\n      \"part.\"};\n\n  using options = tmpl::list<ColdEos, ThermalAdiabaticIndex>;\n\n  HybridEos() = default;\n  HybridEos(const HybridEos&) = default;\n  HybridEos& operator=(const HybridEos&) = default;\n  HybridEos(HybridEos&&) = default;\n  HybridEos& operator=(HybridEos&&) = default;\n  ~HybridEos() override = default;\n\n  HybridEos(ColdEquationOfState cold_eos, double thermal_adiabatic_index);\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(HybridEos, 2)\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<is_relativistic, 2>), HybridEos);\n\n  std::unique_ptr<EquationOfState<is_relativistic, 2>> get_clone()\n      const override;\n\n  std::unique_ptr<EquationOfState<is_relativistic, 3>> promote_to_3d_eos()\n      const override;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  bool is_barotropic() const override { return false; }\n\n  bool operator==(const HybridEos<ColdEquationOfState>& rhs) const;\n\n  bool operator!=(const HybridEos<ColdEquationOfState>& rhs) const;\n\n  bool is_equal(const EquationOfState<is_relativistic, 2>& rhs) const override;\n\n  static std::string name() {\n    return \"HybridEos(\" + pretty_type::name<ColdEquationOfState>() + \")\";\n  }\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override {\n    return cold_eos_.rest_mass_density_lower_bound();\n  }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return cold_eos_.rest_mass_density_upper_bound();\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_lower_bound(\n      const double rest_mass_density) const override {\n    return get(cold_eos_.specific_internal_energy_from_density(\n        Scalar<double>{rest_mass_density}));\n  }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_upper_bound(\n      const double /*rest_mass_density*/) const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override {\n    return cold_eos_.specific_enthalpy_lower_bound();\n  }\n\n  /// The vacuum baryon mass for this EoS\n  double baryon_mass() const override { return cold_eos_.baryon_mass(); }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(2)\n\n  ColdEquationOfState cold_eos_;\n  double thermal_adiabatic_index_ =\n      std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <typename ColdEquationOfState>\nPUP::able::PUP_ID EquationsOfState::HybridEos<ColdEquationOfState>::my_PUP_ID =\n    0;\n/// \\endcond\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic>\nIdealFluid<IsRelativistic>::IdealFluid(const double adiabatic_index,\n                                       const double min_temperature)\n    : adiabatic_index_(adiabatic_index), min_temperature_(min_temperature) {}\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     IdealFluid<IsRelativistic>, double, 2)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     IdealFluid<IsRelativistic>, DataVector, 2)\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 2>>\nIdealFluid<IsRelativistic>::get_clone() const {\n  auto clone = std::make_unique<IdealFluid<IsRelativistic>>(*this);\n  return std::unique_ptr<EquationOfState<IsRelativistic, 2>>(std::move(clone));\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 3>>\nIdealFluid<IsRelativistic>::promote_to_3d_eos() const {\n  return std::make_unique<Equilibrium3D<IdealFluid<IsRelativistic>>>(\n      Equilibrium3D(*this));\n}\n\ntemplate <bool IsRelativistic>\nbool IdealFluid<IsRelativistic>::is_equal(\n    const EquationOfState<IsRelativistic, 2>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const IdealFluid<IsRelativistic>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <bool IsRelativistic>\nbool IdealFluid<IsRelativistic>::operator==(\n    const IdealFluid<IsRelativistic>& rhs) const {\n  return (adiabatic_index_ == rhs.adiabatic_index_) and\n         (min_temperature_ == rhs.min_temperature_);\n}\n\ntemplate <bool IsRelativistic>\nbool IdealFluid<IsRelativistic>::operator!=(\n    const IdealFluid<IsRelativistic>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <bool IsRelativistic>\nIdealFluid<IsRelativistic>::IdealFluid(CkMigrateMessage* msg)\n    : EquationOfState<IsRelativistic, 2>(msg) {}\n\ntemplate <bool IsRelativistic>\nvoid IdealFluid<IsRelativistic>::pup(PUP::er& p) {\n  EquationOfState<IsRelativistic, 2>::pup(p);\n  p | adiabatic_index_;\n  p | min_temperature_;\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nIdealFluid<IsRelativistic>::pressure_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{get(rest_mass_density) *\n                          get(specific_internal_energy) *\n                          (adiabatic_index_ - 1.0)};\n}\n\ntemplate <>\ntemplate <class DataType>\nScalar<DataType> IdealFluid<true>::pressure_from_density_and_enthalpy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_enthalpy) const {\n  return Scalar<DataType>{get(rest_mass_density) *\n                          (get(specific_enthalpy) - 1.0) *\n                          (adiabatic_index_ - 1.0) / adiabatic_index_};\n}\n\ntemplate <>\ntemplate <class DataType>\nScalar<DataType> IdealFluid<false>::pressure_from_density_and_enthalpy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_enthalpy) const {\n  return Scalar<DataType>{get(rest_mass_density) * get(specific_enthalpy) *\n                          (adiabatic_index_ - 1.0) / adiabatic_index_};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> IdealFluid<IsRelativistic>::\n    specific_internal_energy_from_density_and_pressure_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& pressure) const {\n  return Scalar<DataType>{1.0 / (adiabatic_index_ - 1.0) * get(pressure) /\n                          get(rest_mass_density)};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nIdealFluid<IsRelativistic>::temperature_from_density_and_energy_impl(\n    const Scalar<DataType>& /*rest_mass_density*/,\n    const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{(adiabatic_index_ - 1.0) *\n                          get(specific_internal_energy)};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> IdealFluid<IsRelativistic>::\n    specific_internal_energy_from_density_and_temperature_impl(\n        const Scalar<DataType>& /*rest_mass_density*/,\n        const Scalar<DataType>& temperature) const {\n  return Scalar<DataType>{get(temperature) / (adiabatic_index_ - 1.0)};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> IdealFluid<IsRelativistic>::chi_from_density_and_energy_impl(\n    const Scalar<DataType>& /*rest_mass_density*/,\n    const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{get(specific_internal_energy) *\n                          (adiabatic_index_ - 1.0)};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> IdealFluid<IsRelativistic>::\n    kappa_times_p_over_rho_squared_from_density_and_energy_impl(\n        const Scalar<DataType>& /*rest_mass_density*/,\n        const Scalar<DataType>& specific_internal_energy) const {\n  return Scalar<DataType>{square(adiabatic_index_ - 1.0) *\n                          get(specific_internal_energy)};\n}\n\ntemplate <bool IsRelativistic>\ndouble IdealFluid<IsRelativistic>::specific_internal_energy_upper_bound(\n    const double /* rest_mass_density */) const {\n  // this bound comes from the dominant energy condition which implies\n  // that the pressure is bounded by the total energy density,\n  // i.e. p < e = rho * (1 + eps)\n  if (IsRelativistic and adiabatic_index_ > 2.0) {\n    return 1.0 / (adiabatic_index_ - 2.0);\n  }\n  return std::numeric_limits<double>::max();\n}\n}  // namespace EquationsOfState\n\ntemplate class EquationsOfState::IdealFluid<true>;\ntemplate class EquationsOfState::IdealFluid<false>;\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Equation of state for an ideal fluid\n *\n * An ideal fluid equation of state:\n *\n * \\f[\n * p = \\rho \\epsilon(\\gamma-1)\n * \\f]\n *\n * where \\f$\\rho\\f$ is the rest mass density, \\f$\\epsilon\\f$ is the specific\n * internal energy, and \\f$\\gamma\\f$ is the adiabatic index.\n *\n * The temperature \\f$T\\f$ is defined as\n *\n * \\f[\n * T = (\\gamma - 1) \\epsilon\n * \\f]\n */\ntemplate <bool IsRelativistic>\nclass IdealFluid : public EquationOfState<IsRelativistic, 2> {\n public:\n  static constexpr size_t thermodynamic_dim = 2;\n  static constexpr bool is_relativistic = IsRelativistic;\n\n  struct AdiabaticIndex {\n    using type = double;\n    static constexpr Options::String help = {\"Adiabatic index gamma\"};\n  };\n\n  struct MinTemperature {\n    using type = double;\n    static type lower_bound() { return 0.0; }\n    static constexpr Options::String help = {\n        \"Minimum temperature. \"\n        \"This value must be non-negative.\"};\n  };\n\n  static constexpr Options::String help = {\n      \"An ideal fluid equation of state.\\n\"\n      \"The pressure is related to the rest mass density by p = rho * epsilon * \"\n      \"(gamma - 1), where p is the pressure, rho is the rest mass density, \"\n      \"epsilon is the specific internal energy, and gamma is the adiabatic \"\n      \"index.\\n\"\n      \"The temperature T is defined as T=(gamma-1) * epsilon.\"};\n\n  using options = tmpl::list<AdiabaticIndex, MinTemperature>;\n\n  IdealFluid() = default;\n  IdealFluid(const IdealFluid&) = default;\n  IdealFluid& operator=(const IdealFluid&) = default;\n  IdealFluid(IdealFluid&&) = default;\n  IdealFluid& operator=(IdealFluid&&) = default;\n  ~IdealFluid() override = default;\n\n  explicit IdealFluid(double adiabatic_index, double min_temperature = 0.0);\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(IdealFluid, 2)\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 2>> get_clone()\n      const override;\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 3>> promote_to_3d_eos()\n      const override;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  bool is_barotropic() const override { return false; }\n\n  bool operator==(const IdealFluid<IsRelativistic>& rhs) const;\n\n  bool operator!=(const IdealFluid<IsRelativistic>& rhs) const;\n\n  bool is_equal(const EquationOfState<IsRelativistic, 2>& rhs) const override;\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<IsRelativistic, 2>), IdealFluid);\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  /// If non-zero lower bound for temperature is provided, then the lower bound\n  /// for specific internal energy is also non-zero accordingly.\n  double specific_internal_energy_lower_bound(\n      const double /* rest_mass_density */) const override {\n    return (min_temperature_) / (adiabatic_index_ - 1.0);\n  }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_upper_bound(\n      double rest_mass_density) const override;\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  /// If non-zero lower bound for temperature is provided, then the lower bound\n  /// for specific internal enthalpy is also non-zero accordingly.\n  double specific_enthalpy_lower_bound() const override {\n    return IsRelativistic ? 1.0 + (adiabatic_index_ * min_temperature_) /\n                                      (adiabatic_index_ - 1.0)\n                          : (adiabatic_index_ * min_temperature_) /\n                                (adiabatic_index_ - 1.0);\n  }\n\n  /// The lower bound of the temperature that is valid for this EOS.\n  /// Non-zero lower bound could be set to impose floor on the specific\n  /// internal energy.\n  double temperature_lower_bound() const override { return min_temperature_; }\n\n  /// The vacuum baryon mass for this EoS\n  double baryon_mass() const override {\n    return hydro::units::geometric::default_baryon_mass;\n  }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(2)\n\n  double adiabatic_index_ = std::numeric_limits<double>::signaling_NaN();\n  double min_temperature_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <bool IsRelativistic>\nPUP::able::PUP_ID EquationsOfState::IdealFluid<IsRelativistic>::my_PUP_ID = 0;\n/// \\endcond\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.hpp\"\n\n#include <cmath>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\n// Assign polytropic exponents & constants based on primitive values\nnamespace {\n\nvoid choose_polytropic_properties(\n    const gsl::not_null<double*> polytropic_constant,\n    const gsl::not_null<double*> polytropic_exponent,\n    const double primitive_vector, const double primitive_comparison,\n    const double hi_constant, const double lo_constant,\n    const double hi_exponent, const double lo_exponent) {\n  // Assumes >= for high density material\n  if (primitive_vector < primitive_comparison) {\n    *polytropic_constant = lo_constant;\n    *polytropic_exponent = lo_exponent;\n  } else {\n    *polytropic_constant = hi_constant;\n    *polytropic_exponent = hi_exponent;\n  }\n}\n}  // namespace\n\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic>\nPiecewisePolytropicFluid<IsRelativistic>::PiecewisePolytropicFluid(\n    double transition_density, double polytropic_constant_lo,\n    double polytropic_exponent_lo, double polytropic_exponent_hi)\n    : transition_density_(transition_density),\n      transition_pressure_(polytropic_constant_lo *\n                           pow(transition_density_, polytropic_exponent_lo)),\n      transition_spec_eint_(\n          polytropic_constant_lo / (polytropic_exponent_lo - 1.0) *\n          pow(transition_density_, polytropic_exponent_lo - 1.0)),\n      polytropic_constant_lo_(polytropic_constant_lo),\n      polytropic_exponent_lo_(polytropic_exponent_lo),\n      polytropic_constant_hi_(\n          polytropic_constant_lo *\n          pow(transition_density,\n              polytropic_exponent_lo - polytropic_exponent_hi)),\n      polytropic_exponent_hi_(polytropic_exponent_hi) {}\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     PiecewisePolytropicFluid<IsRelativistic>,\n                                     double, 1)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     PiecewisePolytropicFluid<IsRelativistic>,\n                                     DataVector, 1)\n\ntemplate <bool IsRelativistic>\nbool PiecewisePolytropicFluid<IsRelativistic>::operator==(\n    const PiecewisePolytropicFluid<IsRelativistic>& rhs) const {\n  return (transition_density_ == rhs.transition_density_) and\n         (polytropic_constant_lo_ == rhs.polytropic_constant_lo_) and\n         (polytropic_exponent_lo_ == rhs.polytropic_exponent_lo_) and\n         (polytropic_constant_hi_ == rhs.polytropic_constant_hi_) and\n         (polytropic_exponent_hi_ == rhs.polytropic_exponent_hi_);\n}\n\ntemplate <bool IsRelativistic>\nbool PiecewisePolytropicFluid<IsRelativistic>::operator!=(\n    const PiecewisePolytropicFluid<IsRelativistic>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <bool IsRelativistic>\nbool PiecewisePolytropicFluid<IsRelativistic>::is_equal(\n    const EquationOfState<IsRelativistic, 1>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const PiecewisePolytropicFluid<IsRelativistic>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 1>>\nPiecewisePolytropicFluid<IsRelativistic>::get_clone() const {\n  auto clone =\n      std::make_unique<PiecewisePolytropicFluid<IsRelativistic>>(*this);\n  return std::unique_ptr<EquationOfState<IsRelativistic, 1>>(std::move(clone));\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 3>>\nPiecewisePolytropicFluid<IsRelativistic>::promote_to_3d_eos() const {\n  return std::make_unique<\n      Barotropic3D<PiecewisePolytropicFluid<IsRelativistic>>>(*this);\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 2>>\nPiecewisePolytropicFluid<IsRelativistic>::promote_to_2d_eos() const {\n  return std::make_unique<\n      Barotropic2D<PiecewisePolytropicFluid<IsRelativistic>>>(*this);\n}\n\ntemplate <bool IsRelativistic>\nPiecewisePolytropicFluid<IsRelativistic>::PiecewisePolytropicFluid(\n    CkMigrateMessage* msg)\n    : EquationOfState<IsRelativistic, 1>(msg) {}\n\ntemplate <bool IsRelativistic>\nvoid PiecewisePolytropicFluid<IsRelativistic>::pup(PUP::er& p) {\n  EquationOfState<IsRelativistic, 1>::pup(p);\n  p | transition_density_;\n  p | transition_pressure_;\n  p | transition_spec_eint_;\n  p | polytropic_constant_lo_;\n  p | polytropic_exponent_lo_;\n  p | polytropic_constant_hi_;\n  p | polytropic_exponent_hi_;\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nPiecewisePolytropicFluid<IsRelativistic>::pressure_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  double polytropic_constant = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent = std::numeric_limits<double>::signaling_NaN();\n\n  auto result = make_with_value<Scalar<DataType>>(rest_mass_density, 0.0);\n\n  for (size_t i = 0; i < get_size(get(rest_mass_density)); ++i) {\n    const double density = get_element(get(rest_mass_density), i);\n    // select high or low polytropic constant & exponent\n    choose_polytropic_properties(\n        &polytropic_constant, &polytropic_exponent, density,\n        transition_density_, polytropic_constant_hi_, polytropic_constant_lo_,\n        polytropic_exponent_hi_, polytropic_exponent_lo_);\n    // calculate pressure from density\n    get_element(get(result), i) =\n        polytropic_constant * pow(density, polytropic_exponent);\n  }\n  return result;\n}\n\n// Relativistic specific enthalpy\ntemplate <>\ntemplate <class DataType>\nScalar<DataType>\nPiecewisePolytropicFluid<true>::rest_mass_density_from_enthalpy_impl(\n    const Scalar<DataType>& specific_enthalpy) const {\n  const double transition_spec_enthalpy =\n      1.0 + transition_spec_eint_ + transition_pressure_ / transition_density_;\n\n  double polytropic_constant = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent = std::numeric_limits<double>::signaling_NaN();\n\n  auto result = make_with_value<Scalar<DataType>>(specific_enthalpy, 0.0);\n\n  for (size_t i = 0; i < get_size(get(specific_enthalpy)); ++i) {\n    const double spec_enthalpy = get_element(get(specific_enthalpy), i);\n    // select high or low polytropic constant & exponent\n    choose_polytropic_properties(\n        &polytropic_constant, &polytropic_exponent, spec_enthalpy,\n        transition_spec_enthalpy, polytropic_constant_hi_,\n        polytropic_constant_lo_, polytropic_exponent_hi_,\n        polytropic_exponent_lo_);\n    // calculate density from relativistic specific enthalpy\n    get_element(get(result), i) =\n        pow(((polytropic_exponent - 1.0) /\n             (polytropic_constant * polytropic_exponent)) *\n                (spec_enthalpy - 1.0 -\n                 (polytropic_exponent - polytropic_exponent_lo_) /\n                     ((polytropic_exponent_hi_ - 1.0) *\n                      (polytropic_exponent_lo_ - 1.0)) *\n                     polytropic_constant_lo_ *\n                     pow(transition_density_, polytropic_exponent_lo_ - 1.0)),\n            1.0 / (polytropic_exponent - 1.0));\n  }\n  return result;\n}\n\n// Newtonian specific enthalpy\ntemplate <>\ntemplate <class DataType>\nScalar<DataType>\nPiecewisePolytropicFluid<false>::rest_mass_density_from_enthalpy_impl(\n    const Scalar<DataType>& specific_enthalpy) const {\n  const double transition_spec_enthalpy =\n      transition_spec_eint_ + transition_pressure_ / transition_density_;\n\n  double polytropic_constant = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent = std::numeric_limits<double>::signaling_NaN();\n\n  auto result = make_with_value<Scalar<DataType>>(specific_enthalpy, 0.0);\n\n  for (size_t i = 0; i < get_size(get(specific_enthalpy)); ++i) {\n    const double spec_enthalpy = get_element(get(specific_enthalpy), i);\n    // select high or low polytropic constant & exponent\n    choose_polytropic_properties(\n        &polytropic_constant, &polytropic_exponent, spec_enthalpy,\n        transition_spec_enthalpy, polytropic_constant_hi_,\n        polytropic_constant_lo_, polytropic_exponent_hi_,\n        polytropic_exponent_lo_);\n    // calculate density from Newtonian specific enthalpy\n    get_element(get(result), i) =\n        pow(((polytropic_exponent - 1.0) /\n             (polytropic_constant * polytropic_exponent)) *\n                (spec_enthalpy -\n                 (polytropic_exponent - polytropic_exponent_lo_) /\n                     ((polytropic_exponent_hi_ - 1.0) *\n                      (polytropic_exponent_lo_ - 1.0)) *\n                     polytropic_constant_lo_ *\n                     pow(transition_density_, polytropic_exponent_lo_ - 1.0)),\n            1.0 / (polytropic_exponent - 1.0));\n  }\n  return result;\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> PiecewisePolytropicFluid<IsRelativistic>::\n    specific_internal_energy_from_density_impl(\n        const Scalar<DataType>& rest_mass_density) const {\n  double polytropic_constant = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent = std::numeric_limits<double>::signaling_NaN();\n\n  auto result = make_with_value<Scalar<DataType>>(rest_mass_density, 0.0);\n\n  for (size_t i = 0; i < get_size(get(rest_mass_density)); ++i) {\n    const double density = get_element(get(rest_mass_density), i);\n    // select high or low polytropic constant & exponent\n    choose_polytropic_properties(\n        &polytropic_constant, &polytropic_exponent, density,\n        transition_density_, polytropic_constant_hi_, polytropic_constant_lo_,\n        polytropic_exponent_hi_, polytropic_exponent_lo_);\n    // calculate specific internal energy from density\n    get_element(get(result), i) =\n        polytropic_constant / (polytropic_exponent - 1.0) *\n            pow(density, polytropic_exponent - 1.0) +\n        (polytropic_exponent - polytropic_exponent_lo_) /\n            ((polytropic_exponent_hi_ - 1.0) *\n             (polytropic_exponent_lo_ - 1.0)) *\n            polytropic_constant_lo_ *\n            pow(transition_density_, (polytropic_exponent_lo_ - 1.0));\n  }\n  return result;\n}\n\n// Chi = dP/drho\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nPiecewisePolytropicFluid<IsRelativistic>::chi_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  double polytropic_constant = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent = std::numeric_limits<double>::signaling_NaN();\n\n  auto result = make_with_value<Scalar<DataType>>(rest_mass_density, 0.0);\n\n  for (size_t i = 0; i < get_size(get(rest_mass_density)); ++i) {\n    const double density = get_element(get(rest_mass_density), i);\n    // select high or low polytropic constant & exponent\n    choose_polytropic_properties(\n        &polytropic_constant, &polytropic_exponent, density,\n        transition_density_, polytropic_constant_hi_, polytropic_constant_lo_,\n        polytropic_exponent_hi_, polytropic_exponent_lo_);\n    // calculate chi from density\n    get_element(get(result), i) = polytropic_constant * polytropic_exponent *\n                                  pow(density, polytropic_exponent - 1.0);\n  }\n  return result;\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> PiecewisePolytropicFluid<IsRelativistic>::\n    kappa_times_p_over_rho_squared_from_density_impl(\n        const Scalar<DataType>& rest_mass_density) const {\n  return make_with_value<Scalar<DataType>>(get(rest_mass_density), 0.0);\n}\n\ntemplate <bool IsRelativistic>\ndouble PiecewisePolytropicFluid<IsRelativistic>::rest_mass_density_upper_bound()\n    const {\n  // this bound comes from the dominant energy condition which implies\n  // that the pressure is bounded by the total energy density,\n  // i.e. p < e = rho * (1 + eps)\n  if (IsRelativistic and polytropic_exponent_hi_ > 2.0) {\n    const double eint_boundary_constant =\n        (polytropic_exponent_hi_ - polytropic_exponent_lo_) *\n        polytropic_constant_lo_ /\n        ((polytropic_exponent_hi_ - 1.0) * (polytropic_exponent_lo_ - 1.0)) *\n        pow(transition_density_, polytropic_exponent_lo_ - 1.0);\n    return pow((polytropic_exponent_hi_ - 1.0) /\n                   (polytropic_constant_hi_ * (polytropic_exponent_hi_ - 2.0)) *\n                   (1.0 + eint_boundary_constant),\n               1.0 / (polytropic_exponent_hi_ - 1.0));\n  }\n  return std::numeric_limits<double>::max();\n}\n\ntemplate <bool IsRelativistic>\ndouble PiecewisePolytropicFluid<\n    IsRelativistic>::specific_internal_energy_upper_bound() const {\n  // this bound comes from the dominant energy condition which implies\n  // that the pressure is bounded by the total energy density,\n  // i.e. p < e = rho * (1 + eps)\n  if (IsRelativistic and polytropic_exponent_hi_ > 2.0) {\n    const double eint_boundary_constant =\n        (polytropic_exponent_hi_ - polytropic_exponent_lo_) *\n        polytropic_constant_lo_ /\n        ((polytropic_exponent_hi_ - 1.0) * (polytropic_exponent_lo_ - 1.0)) *\n        pow(transition_density_, polytropic_exponent_lo_ - 1.0);\n    return (1.0 + (polytropic_exponent_hi_ - 1.0) * eint_boundary_constant) /\n           (polytropic_exponent_hi_ - 2.0);\n  }\n  return std::numeric_limits<double>::max();\n}\n}  // namespace EquationsOfState\n\ntemplate class EquationsOfState::PiecewisePolytropicFluid<true>;\ntemplate class EquationsOfState::PiecewisePolytropicFluid<false>;\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Equation of state for a piecewise polytropic fluid\n *\n * A piecewise polytropic equation of state \\f$p=K_i\\rho^{\\Gamma_i}\\f$ where\n *  \\f$K_i\\f$ is the polytropic constant and \\f$\\Gamma_i\\f$ is the polytropic\n * exponent. Here the subscript \\f$i\\f$ indicates two pairs of constants and\n *  exponents which characterize `the stiffness' of the matter at low and high\n *  densities.  For a given density, the polytropic exponent is related to the\n *  polytropic index \\f$N_p\\f$ by \\f$N_p=1/(\\Gamma-1)\\f$.  For posterity,\n *  this two piece polytrope has been used in toy models of CCSNe (e.g.,\n *  \\cite Dimmelmeier2001 ) and could be extended to a general \"M\" number of\n * parts for simplified equations of state for neutron stars (e.g.,\n *  \\cite OBoyle2020 ). For a reference to a general piecewise polytrope, see\n * Section 2.4.7 of \\cite RezzollaBook.\n */\ntemplate <bool IsRelativistic>\nclass PiecewisePolytropicFluid : public EquationOfState<IsRelativistic, 1> {\n public:\n  static constexpr size_t thermodynamic_dim = 1;\n  static constexpr bool is_relativistic = IsRelativistic;\n\n  /// The density demarcating the high and low density descriptions of the\n  /// fluid.\n  struct PiecewisePolytropicTransitionDensity {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Density below (above) which, the matter is described by a low (high) \"\n        \"density polytropic fluid.\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  /// The constant \\f$K\\f$ scaling the low density material\n  /// \\f$p=K\\rho^{\\Gamma}\\f$.\n  ///\n  /// Note, by enforcing pressure continuity at the transition density\n  /// \\f$\\bar{\\rho}\\f$, the high density constant \\f$K_{high}\\f$ is given\n  /// as \\f$K_{high} = K_{low} (\\bar{\\rho})^{\\Gamma_{low} - \\Gamma_{high}}\\f$.\n  struct PolytropicConstantLow {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Polytropic constant K for lower\"\n        \" density material\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  /// The exponent \\f$\\Gamma\\f$, scaling the low density material\n  /// \\f$p=K\\rho^{\\Gamma}\\f$.\n  struct PolytropicExponentLow {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Polytropic exponent for lower\"\n        \" density material.\"};\n    static double lower_bound() { return 1.0; }\n  };\n\n  /// The exponent \\f$\\Gamma\\f$, scaling the high density material\n  /// \\f$p=K\\rho^{\\Gamma}\\f$.\n  struct PolytropicExponentHigh {\n    using type = double;\n    static constexpr Options::String help = {\n        \"Polytropic exponent for higher\"\n        \" density material.\"};\n    static double lower_bound() { return 1.0; }\n  };\n\n  static constexpr Options::String help = {\n      \"A piecewise polytropic fluid equation of state.\\n\"\n      \"The pressure is related to the rest mass density by p = K_i rho ^ \"\n      \"Gamma_i, \"\n      \"where p is the pressure, rho is the rest mass density, K_i is the \"\n      \"polytropic constant either describing the low or high density material, \"\n      \"and Gamma_i is the polytropic exponent for the low or high density \"\n      \"material. The polytropic index N_i is defined as Gamma_i = 1 + 1 / N_i.\"\n      \"  The subscript `i' refers to different pairs of Gamma and K that can\"\n      \" describe either low or high density material.\"};\n\n  using options =\n      tmpl::list<PiecewisePolytropicTransitionDensity, PolytropicConstantLow,\n                 PolytropicExponentLow, PolytropicExponentHigh>;\n\n  PiecewisePolytropicFluid() = default;\n  PiecewisePolytropicFluid(const PiecewisePolytropicFluid&) = default;\n  PiecewisePolytropicFluid& operator=(const PiecewisePolytropicFluid&) =\n      default;\n  PiecewisePolytropicFluid(PiecewisePolytropicFluid&&) = default;\n  PiecewisePolytropicFluid& operator=(PiecewisePolytropicFluid&&) = default;\n  ~PiecewisePolytropicFluid() override = default;\n\n  PiecewisePolytropicFluid(double transition_density,\n                           double polytropic_constant_lo,\n                           double polytropic_exponent_lo,\n                           double polytropic_exponent_hi);\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 1>> get_clone()\n      const override;\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 3>> promote_to_3d_eos()\n      const override;\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 2>> promote_to_2d_eos()\n      const override;\n\n  bool is_equal(const EquationOfState<IsRelativistic, 1>& rhs) const override;\n\n  bool operator==(const PiecewisePolytropicFluid<IsRelativistic>& rhs) const;\n\n  bool operator!=(const PiecewisePolytropicFluid<IsRelativistic>& rhs) const;\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(PiecewisePolytropicFluid, 1)\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<IsRelativistic, 1>), PiecewisePolytropicFluid);\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override;\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override {\n    return IsRelativistic ? 1.0 : 0.0;\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  double specific_internal_energy_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  double specific_internal_energy_upper_bound() const override;\n\n  /// The vacuum baryon mass for this EoS\n  double baryon_mass() const override {\n    return hydro::units::geometric::default_baryon_mass;\n  }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(1)\n\n  double transition_density_ = std::numeric_limits<double>::signaling_NaN();\n  double transition_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  double transition_spec_eint_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_constant_lo_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent_lo_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_constant_hi_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent_hi_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <bool IsRelativistic>\nPUP::able::PUP_ID\n    EquationsOfState::PiecewisePolytropicFluid<IsRelativistic>::my_PUP_ID = 0;\n/// \\endcond\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic>\nPolytropicFluid<IsRelativistic>::PolytropicFluid(\n    const double polytropic_constant, const double polytropic_exponent)\n    : polytropic_constant_(polytropic_constant),\n      polytropic_exponent_(polytropic_exponent) {}\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     PolytropicFluid<IsRelativistic>, double, 1)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     PolytropicFluid<IsRelativistic>,\n                                     DataVector, 1)\n\ntemplate <bool IsRelativistic>\nbool PolytropicFluid<IsRelativistic>::operator==(\n    const PolytropicFluid<IsRelativistic>& rhs) const {\n  return (polytropic_constant_ == rhs.polytropic_constant_) and\n         (polytropic_exponent_ == rhs.polytropic_exponent_);\n}\n\ntemplate <bool IsRelativistic>\nbool PolytropicFluid<IsRelativistic>::operator!=(\n    const PolytropicFluid<IsRelativistic>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <bool IsRelativistic>\nbool PolytropicFluid<IsRelativistic>::is_equal(\n    const EquationOfState<IsRelativistic, 1>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const PolytropicFluid<IsRelativistic>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 1>>\nPolytropicFluid<IsRelativistic>::get_clone() const {\n  auto clone = std::make_unique<PolytropicFluid<IsRelativistic>>(*this);\n  return std::unique_ptr<EquationOfState<IsRelativistic, 1>>(std::move(clone));\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 3>>\nPolytropicFluid<IsRelativistic>::promote_to_3d_eos() const {\n  return std::make_unique<Barotropic3D<PolytropicFluid<IsRelativistic>>>(*this);\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 2>>\nPolytropicFluid<IsRelativistic>::promote_to_2d_eos() const {\n  return std::make_unique<Barotropic2D<PolytropicFluid<IsRelativistic>>>(*this);\n}\n\ntemplate <bool IsRelativistic>\nPolytropicFluid<IsRelativistic>::PolytropicFluid(CkMigrateMessage* msg)\n    : EquationOfState<IsRelativistic, 1>(msg) {}\n\ntemplate <bool IsRelativistic>\nvoid PolytropicFluid<IsRelativistic>::pup(PUP::er& p) {\n  EquationOfState<IsRelativistic, 1>::pup(p);\n  p | polytropic_constant_;\n  p | polytropic_exponent_;\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> PolytropicFluid<IsRelativistic>::pressure_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  return Scalar<DataType>{polytropic_constant_ *\n                          pow(get(rest_mass_density), polytropic_exponent_)};\n}\n\ntemplate <>\ntemplate <class DataType>\nScalar<DataType> PolytropicFluid<true>::rest_mass_density_from_enthalpy_impl(\n    const Scalar<DataType>& specific_enthalpy) const {\n  return Scalar<DataType>{pow(((polytropic_exponent_ - 1.0) /\n                               (polytropic_constant_ * polytropic_exponent_)) *\n                                  (get(specific_enthalpy) - 1.0),\n                              1.0 / (polytropic_exponent_ - 1.0))};\n}\n\ntemplate <>\ntemplate <class DataType>\nScalar<DataType> PolytropicFluid<false>::rest_mass_density_from_enthalpy_impl(\n    const Scalar<DataType>& specific_enthalpy) const {\n  return Scalar<DataType>{pow(((polytropic_exponent_ - 1.0) /\n                               (polytropic_constant_ * polytropic_exponent_)) *\n                                  get(specific_enthalpy),\n                              1.0 / (polytropic_exponent_ - 1.0))};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nPolytropicFluid<IsRelativistic>::specific_internal_energy_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  return Scalar<DataType>{\n      polytropic_constant_ / (polytropic_exponent_ - 1.0) *\n      pow(get(rest_mass_density), polytropic_exponent_ - 1.0)};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> PolytropicFluid<IsRelativistic>::chi_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  return Scalar<DataType>{\n      polytropic_constant_ * polytropic_exponent_ *\n      pow(get(rest_mass_density), polytropic_exponent_ - 1.0)};\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> PolytropicFluid<IsRelativistic>::\n    kappa_times_p_over_rho_squared_from_density_impl(\n        const Scalar<DataType>& rest_mass_density) const {\n  return make_with_value<Scalar<DataType>>(get(rest_mass_density), 0.0);\n}\n\ntemplate <bool IsRelativistic>\ndouble PolytropicFluid<IsRelativistic>::rest_mass_density_upper_bound() const {\n  // this bound comes from the dominant energy condition which implies\n  // that the pressure is bounded by the total energy density,\n  // i.e. p < e = rho * (1 + eps)\n  if (IsRelativistic and polytropic_exponent_ > 2.0) {\n    return pow((polytropic_exponent_ - 1.0) /\n                   (polytropic_constant_ * (polytropic_exponent_ - 2.0)),\n               1.0 / (polytropic_exponent_ - 1.0));\n  }\n  return std::numeric_limits<double>::max();\n}\n\ntemplate <bool IsRelativistic>\ndouble PolytropicFluid<IsRelativistic>::specific_internal_energy_upper_bound()\n    const {\n  // this bound comes from the dominant energy condition which implies\n  // that the pressure is bounded by the total energy density,\n  // i.e. p < e = rho * (1 + eps)\n  if (IsRelativistic and polytropic_exponent_ > 2.0) {\n    return 1.0 / (polytropic_exponent_ - 2.0);\n  }\n  return std::numeric_limits<double>::max();\n}\n}  // namespace EquationsOfState\n\ntemplate class EquationsOfState::PolytropicFluid<true>;\ntemplate class EquationsOfState::PolytropicFluid<false>;\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Equation of state for a polytropic fluid\n *\n * A polytropic equation of state \\f$p=K\\rho^{\\Gamma}\\f$ where \\f$K\\f$ is the\n * polytropic constant and \\f$\\Gamma\\f$ is the polytropic exponent. The\n * polytropic exponent is related to the polytropic index \\f$N_p\\f$ by\n * \\f$N_p=1/(\\Gamma-1)\\f$.\n *\n * We also have\n *\n * \\f{align}{\n * \\epsilon&=\\frac{K\\rho^{\\Gamma-1}}{\\Gamma-1}\\\\\n * h&=1+\\epsilon+\\frac{p}{\\rho}=1+\\frac{K\\Gamma}{\\Gamma-1}\\rho^{\\Gamma-1} \\\\\n * T&=0 \\\\\n * c_s^2&=\\frac{\\Gamma p}{\\rho h}\n * =\\frac{\\Gamma(\\Gamma-1)p}{\\rho(\\Gamma-1)+\\Gamma p}\n * =\\left(\\frac{1}{\\Gamma K\\rho^{\\Gamma-1}}+\\frac{1}{\\Gamma-1}\\right)^{-1}\n * \\f}\n */\ntemplate <bool IsRelativistic>\nclass PolytropicFluid : public EquationOfState<IsRelativistic, 1> {\n public:\n  static constexpr size_t thermodynamic_dim = 1;\n  static constexpr bool is_relativistic = IsRelativistic;\n\n  struct PolytropicConstant {\n    using type = double;\n    static constexpr Options::String help = {\"Polytropic constant K\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  struct PolytropicExponent {\n    using type = double;\n    static constexpr Options::String help = {\"Polytropic exponent Gamma\"};\n    static double lower_bound() { return 1.0; }\n  };\n\n  static constexpr Options::String help = {\n      \"A polytropic fluid equation of state.\\n\"\n      \"The pressure is related to the rest mass density by p = K rho ^ Gamma, \"\n      \"where p is the pressure, rho is the rest mass density, K is the \"\n      \"polytropic constant, and Gamma is the polytropic exponent. The \"\n      \"polytropic index N is defined as Gamma = 1 + 1 / N.\"};\n\n  using options = tmpl::list<PolytropicConstant, PolytropicExponent>;\n\n  PolytropicFluid() = default;\n  PolytropicFluid(const PolytropicFluid&) = default;\n  PolytropicFluid& operator=(const PolytropicFluid&) = default;\n  PolytropicFluid(PolytropicFluid&&) = default;\n  PolytropicFluid& operator=(PolytropicFluid&&) = default;\n  ~PolytropicFluid() override = default;\n\n  PolytropicFluid(double polytropic_constant, double polytropic_exponent);\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 1>> get_clone()\n      const override;\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 3>> promote_to_3d_eos()\n      const override;\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 2>> promote_to_2d_eos()\n      const override;\n\n  bool is_equal(const EquationOfState<IsRelativistic, 1>& rhs) const override;\n\n  bool operator==(const PolytropicFluid<IsRelativistic>& rhs) const;\n\n  bool operator!=(const PolytropicFluid<IsRelativistic>& rhs) const;\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(PolytropicFluid, 1)\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<IsRelativistic, 1>), PolytropicFluid);\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override;\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override {\n    return IsRelativistic ? 1.0 : 0.0;\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  double specific_internal_energy_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  double specific_internal_energy_upper_bound() const override;\n\n  /// The vacuum baryon mass for this EoS\n  double baryon_mass() const override {\n    return hydro::units::geometric::default_baryon_mass;\n  }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(1)\n\n  double polytropic_constant_ = std::numeric_limits<double>::signaling_NaN();\n  double polytropic_exponent_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <bool IsRelativistic>\nPUP::able::PUP_ID EquationsOfState::PolytropicFluid<IsRelativistic>::my_PUP_ID =\n    0;\n/// \\endcond\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/Enthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/PiecewisePolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/Spectral.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace EquationsOfState {\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  // Abstract base class\n  py_bindings::bind_equation_of_state(m);\n  // Derived classes\n  py_bindings::bind_enthalpy(m);\n  py_bindings::bind_piecewisepolytropic_fluid(m);\n  py_bindings::bind_polytropic_fluid(m);\n  py_bindings::bind_spectral(m);\n}\n\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyEquationsOfState\")\n\nspectre_python_add_module(\n  EquationsOfState\n  LIBRARY_NAME ${LIBRARY}\n  MODULE_PATH \"PointwiseFunctions/Hydro\"\n  SOURCES\n  Bindings.cpp\n  EquationOfState.cpp\n  Enthalpy.cpp\n  PiecewisePolytropicFluid.cpp\n  PolytropicFluid.cpp\n  Spectral.cpp\n  PYTHON_FILES\n  __init__.py\n  )\n\nspectre_python_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  EquationOfState.hpp\n  Enthalpy.hpp\n  PiecewisePolytropicFluid.hpp\n  PolytropicFluid.hpp\n  Spectral.hpp\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Hydro\n  pybind11::module\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/Enthalpy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/Enthalpy.hpp\"\n\n#include <pybind11/numpy.h>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n// low_density_eos uses Spectral and Polytropic Fluid type EoS.\n\nnamespace py = pybind11;\n\nnamespace EquationsOfState::py_bindings {\n\ntemplate <typename LowDensityEoS>\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_enthalpy_impl(py::module& m, const std::string& name) {\n  py::class_<Enthalpy<LowDensityEoS>, EquationOfState<true, 1>>(\n    m, name.c_str()).def(\n      py::init<double, double, double, double, std::vector<double>,\n               std::vector<double>, std::vector<double>, LowDensityEoS,\n               double>(),\n      py::arg(\"reference_density\"), py::arg(\"max_density\"),\n      py::arg(\"min_density\"), py::arg(\"trig_scale\"),\n      py::arg(\"polynomial_coefficients\"), py::arg(\"sin_coefficients\"),\n      py::arg(\"cos_coefficients\"), py::arg(\"low_density_eos\"),\n      py::arg(\"transition_delta_epsilon\"));\n}\n\nvoid bind_enthalpy(py::module& m) {\n  bind_enthalpy_impl<PolytropicFluid<true>>(m, \"EnthalpyPolytropicFluid\");\n  bind_enthalpy_impl<Spectral>(m, \"EnthalpySpectral\");\n  bind_enthalpy_impl<Enthalpy<Spectral>>(m, \"EnthalpyEnthalpySpectral\");\n  bind_enthalpy_impl<Enthalpy<Enthalpy<Spectral>>>(\n      m, \"EnthalpyEnthalpyEnthalpySpectral\");\n}\n\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/Enthalpy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace EquationsOfState::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_enthalpy(pybind11::module& m);\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/EquationOfState.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/EquationOfState.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n\nnamespace py = pybind11;\n\nnamespace EquationsOfState::py_bindings {\n\nvoid bind_equation_of_state(py::module& m) {\n  using EosType = EquationOfState<true, 1>;\n  py::class_<EosType>(m, \"RelativisticEquationOfState1D\")\n      .def(\n          \"pressure_from_density\",\n          [](const EosType& eos, const Scalar<DataVector>& rest_mass_density) {\n            return eos.pressure_from_density(rest_mass_density);\n          },\n          py::arg(\"rest_mass_density\"))\n      .def(\n          \"rest_mass_density_from_enthalpy\",\n          [](const EosType& eos, const Scalar<DataVector>& specific_enthalpy) {\n            return eos.rest_mass_density_from_enthalpy(specific_enthalpy);\n          },\n          py::arg(\"specific_enthalpy\"))\n      .def(\n          \"specific_internal_energy_from_density\",\n          [](const EosType& eos, const Scalar<DataVector>& rest_mass_density) {\n            return eos.specific_internal_energy_from_density(rest_mass_density);\n          },\n          py::arg(\"rest_mass_density\"))\n      .def(\n          \"temperature_from_density\",\n          [](const EosType& eos, const Scalar<DataVector>& rest_mass_density) {\n            return eos.temperature_from_density(rest_mass_density);\n          },\n          py::arg(\"rest_mass_density\"))\n      .def(\n          \"temperature_from_specific_internal_energy\",\n          [](const EosType& eos,\n             const Scalar<DataVector>& specific_internal_energy) {\n            return eos.temperature_from_specific_internal_energy(\n                specific_internal_energy);\n          },\n          py::arg(\"specific_internal_energy\"))\n      .def(\n          \"chi_from_density\",\n          [](const EosType& eos, const Scalar<DataVector>& rest_mass_density) {\n            return eos.chi_from_density(rest_mass_density);\n          },\n          py::arg(\"rest_mass_density\"))\n      .def(\n          \"kappa_times_p_over_rho_squared_from_density\",\n          [](const EosType& eos, const Scalar<DataVector>& rest_mass_density) {\n            return eos.kappa_times_p_over_rho_squared_from_density(\n                rest_mass_density);\n          },\n          py::arg(\"rest_mass_density\"));\n}\n\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/EquationOfState.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace EquationsOfState::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_equation_of_state(pybind11::module& m);\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/PiecewisePolytropicFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/PiecewisePolytropicFluid.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.hpp\"\n\nnamespace py = pybind11;\n\nnamespace EquationsOfState::py_bindings {\n\nvoid bind_piecewisepolytropic_fluid(py::module& m) {\n  py::class_<PiecewisePolytropicFluid<true>, EquationOfState<true, 1>>(\n      m, \"RelativisticPiecewisePolytropicFluid\")\n      .def(py::init<double, double, double, double>(),\n           py::arg(\"transition_density\"), py::arg(\"polytropic_constant_lo\"),\n           py::arg(\"polytropic_exponent_lo\"),\n           py::arg(\"polytropic_exponent_hi\"));\n}\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/PiecewisePolytropicFluid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace EquationsOfState::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_piecewisepolytropic_fluid(pybind11::module& m);\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/PolytropicFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/PolytropicFluid.hpp\"\n\n#include <pybind11/pybind11.h>\n#include <string>\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n\nnamespace py = pybind11;\n\nnamespace EquationsOfState::py_bindings {\n\nvoid bind_polytropic_fluid(py::module& m) {\n  py::class_<PolytropicFluid<true>, EquationOfState<true, 1>>(\n      m, \"RelativisticPolytropicFluid\")\n      .def(py::init<double, double>(), py::arg(\"polytropic_constant\"),\n           py::arg(\"polytropic_exponent\"));\n}\n\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/PolytropicFluid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace EquationsOfState::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_polytropic_fluid(pybind11::module& m);\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/Spectral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Python/Spectral.hpp\"\n\n#include <pybind11/numpy.h>\n#include <pybind11/operators.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n#include <vector>\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n\nnamespace py = pybind11;\n\nnamespace EquationsOfState::py_bindings {\n\nvoid bind_spectral(py::module& m) {\n  py::class_<Spectral, EquationOfState<true, 1>>(\n      m, \"Spectral\")\n      .def(py::init<double, double, std::vector<double>, double>(),\n           py::arg(\"reference_density\"), py::arg(\"reference_pressure\"),\n           py::arg(\"spectral_coefficients\"),\n           py::arg(\"upper_density\"));\n}\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/Spectral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pybind11/pybind11.h>\n\nnamespace EquationsOfState::py_bindings {\n// NOLINTNEXTLINE(google-runtime-references)\nvoid bind_spectral(pybind11::module& m);\n}  // namespace EquationsOfState::py_bindings\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\nnamespace EquationsOfState {\n\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nvoid register_derived_subset_with_charm() {\n  using derived_equations_of_state =\n      typename EquationsOfState::detail::DerivedClasses<IsRelativistic,\n                                                        ThermodynamicDim>::type;\n  register_classes_with_charm(derived_equations_of_state{});\n}\n\nvoid register_derived_with_charm() {\n  register_derived_subset_with_charm<true, 1>();\n  register_derived_subset_with_charm<false, 1>();\n  register_derived_subset_with_charm<true, 2>();\n  register_derived_subset_with_charm<false, 2>();\n  register_derived_subset_with_charm<true, 3>();\n  register_derived_subset_with_charm<false, 3>();\n}\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace EquationsOfState {\nvoid register_derived_with_charm();\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Spectral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n\n#include <cmath>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\nstd::vector<double> compute_integral_coefficients(\n    const std::vector<double>& gamma_coefficients) {\n  std::vector<double> result = gamma_coefficients;\n  for (size_t i = 0; i < result.size(); ++i) {\n    result[i] /= (1.0 + i);\n  }\n  return result;\n}\n}  // namespace\n\nnamespace EquationsOfState {\nSpectral::Spectral(const double reference_density,\n                   const double reference_pressure,\n                   std::vector<double> coefficients, const double upper_density)\n    : reference_density_(reference_density),\n      reference_pressure_(reference_pressure),\n      integral_coefficients_(compute_integral_coefficients(coefficients)),\n      gamma_coefficients_(std::move(coefficients)),\n      x_max_(log(upper_density / reference_density)),\n      gamma_of_x_max_(gamma(x_max_)),\n      integral_of_gamma_of_x_max_(integral_of_gamma(x_max_)) {\n  // Setup table of specific energies\n  // Use 6th order Gauss-Legendre quadrature.\n  // Weights and collocation points can be calculated following\n  // e.g. the gauleg method from Numerical Recipes\n  // (Sec. 4.6.1 in 3rd edition). We only need to consider\n  // collocation points with x>0, as the other points are\n  // just symmetric with respect to the origin.\n  quadrature_weights_ = {0.3607615730481386, 0.4679139345726910,\n                         0.1713244923791704};\n  quadrature_points_ = {0.6612093864662645, 0.2386191860831969,\n                        0.9324695142031521};\n  number_of_quadrature_coefs_ = quadrature_weights_.size();\n  const auto n_points_epsilon = static_cast<size_t>(ceil(2.0 * x_max_) + 1.0);\n  const double delta_x = x_max_ / (n_points_epsilon - 1.0);\n  table_of_specific_energies_.resize(n_points_epsilon);\n  table_of_specific_energies_[0] =\n      reference_pressure_ / reference_density_ / (gamma_coefficients_[0] - 1.0);\n  for (size_t i = 1; i < n_points_epsilon; i++) {\n    table_of_specific_energies_[i] = table_of_specific_energies_[i - 1];\n    // Use Gaussian quadrature to calculate the specific internal\n    // energy at the next point, separated from the old point\n    // by delta_x in log(density)\n    // From the 1st law of thermodynamics,\n    // d(epsilon)/dx = (Pressure)/(reference_density)*exp(-x)\n    const double x0 = (i - 1.) * delta_x;\n    for (size_t q = 0; q < number_of_quadrature_coefs_; q++) {\n      const double xp = x0 + (1.0 + quadrature_points_[q]) * (delta_x / 2.0);\n      const double xm = x0 + (1.0 - quadrature_points_[q]) * (delta_x / 2.0);\n      table_of_specific_energies_[i] +=\n          quadrature_weights_[q] * delta_x / (2.0 * reference_density) *\n          (exp(-xp) * pressure_from_log_density(xp) +\n           exp(-xm) * pressure_from_log_density(xm));\n    }\n  }\n}\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(, Spectral, double, 1)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(, Spectral, DataVector, 1)\n\nstd::unique_ptr<EquationOfState<true, 1>> Spectral::get_clone() const {\n  auto clone = std::make_unique<Spectral>(*this);\n  return std::unique_ptr<EquationOfState<true, 1>>(std::move(clone));\n}\n\nstd::unique_ptr<EquationOfState<true, 3>> Spectral::promote_to_3d_eos() const {\n  return std::make_unique<Barotropic3D<Spectral>>(*this);\n}\n\nstd::unique_ptr<EquationOfState<true, 2>> Spectral::promote_to_2d_eos() const {\n  return std::make_unique<Barotropic2D<Spectral>>(*this);\n}\n\nbool Spectral::operator==(const Spectral& rhs) const {\n  return reference_density_ == rhs.reference_density_ and\n         reference_pressure_ == rhs.reference_pressure_;\n}\n\nbool Spectral::operator!=(const Spectral& rhs) const {\n  return not(*this == rhs);\n}\n\nbool Spectral::is_equal(const EquationOfState<true, 1>& rhs) const {\n  const auto& derived_ptr = dynamic_cast<const Spectral* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\nSpectral::Spectral(CkMigrateMessage* msg) : EquationOfState<true, 1>(msg) {}\n\nvoid Spectral::pup(PUP::er& p) {\n  EquationOfState<true, 1>::pup(p);\n  p | reference_density_;\n  p | reference_pressure_;\n  p | integral_coefficients_;\n  p | gamma_coefficients_;\n  p | x_max_;\n  p | gamma_of_x_max_;\n  p | integral_of_gamma_of_x_max_;\n  p | number_of_quadrature_coefs_;\n  p | quadrature_weights_;\n  p | quadrature_points_;\n  p | table_of_specific_energies_;\n}\n\n// this evaluates the power series\n// Int_0^x Gamma(xx) dxx = Sum_{n=0}^N \\frac{gamma_n}{n+1} xx^{n+1}\ndouble Spectral::integral_of_gamma(const double x) const {\n  return evaluate_polynomial(integral_coefficients_, x) * x;\n}\n\ntemplate <typename DataType>\nScalar<DataType> Spectral::pressure_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    return Scalar<double>{pressure_from_density(get(rest_mass_density))};\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto result = make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n    for (size_t i = 0; i < get(result).size(); ++i) {\n      get(result)[i] = pressure_from_density(get(rest_mass_density)[i]);\n    }\n    return result;\n  }\n}\n\ntemplate <class DataType>\nScalar<DataType> Spectral::rest_mass_density_from_enthalpy_impl(\n    const Scalar<DataType>& specific_enthalpy) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    return Scalar<double>{\n        rest_mass_density_from_enthalpy(get(specific_enthalpy))};\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto result = make_with_value<Scalar<DataVector>>(specific_enthalpy, 0.0);\n    for (size_t i = 0; i < get(result).size(); ++i) {\n      get(result)[i] =\n          rest_mass_density_from_enthalpy(get(specific_enthalpy)[i]);\n    }\n    return result;\n  }\n}\n\ntemplate <class DataType>\nScalar<DataType> Spectral::specific_internal_energy_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    return Scalar<double>{\n        specific_internal_energy_from_density(get(rest_mass_density))};\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto result = make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n    for (size_t i = 0; i < get(result).size(); ++i) {\n      get(result)[i] =\n          specific_internal_energy_from_density(get(rest_mass_density)[i]);\n    }\n    return result;\n  }\n}\n\ntemplate <class DataType>\nScalar<DataType> Spectral::chi_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    return Scalar<double>{chi_from_density(get(rest_mass_density))};\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto result = make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0);\n    for (size_t i = 0; i < get(result).size(); ++i) {\n      get(result)[i] = chi_from_density(get(rest_mass_density)[i]);\n    }\n    return result;\n  }\n}\n\ntemplate <class DataType>\nScalar<DataType> Spectral::kappa_times_p_over_rho_squared_from_density_impl(\n    const Scalar<DataType>& rest_mass_density) const {\n  return make_with_value<Scalar<DataType>>(get(rest_mass_density), 0.0);\n}\n\n// this evaluates the power series\n// Gamma(x) = Sum_{n=0}^N gamma_n x^n\ndouble Spectral::gamma(const double x) const {\n  return evaluate_polynomial(gamma_coefficients_, x);\n}\n\ndouble Spectral::chi_from_density(const double rest_mass_density) const {\n  const double x = log(rest_mass_density / reference_density_);\n  const double P = pressure_from_log_density(x);\n  const double chi = P / rest_mass_density *\n                     (x <= 0.0 ? integral_coefficients_[0]\n                               : (x < x_max_ ? gamma(x) : gamma(x_max_)));\n  return chi;\n}\n\ndouble Spectral::specific_internal_energy_from_density(\n    const double rest_mass_density) const {\n  const double x = log(rest_mass_density / reference_density_);\n  if (x <= 0.) {\n    return reference_pressure_ / reference_density_ /\n           (gamma_coefficients_[0] - 1.0) *\n           exp((gamma_coefficients_[0] - 1.0) * x);\n  }\n  const size_t n_points_epsilon = table_of_specific_energies_.size();\n  const double delta_x = x_max_ / (n_points_epsilon - 1.0);\n  double specific_energy = 0.0;\n  if (x >= x_max_) {\n    // Analytic integral at constant gamma_coefficient above x_max_\n    specific_energy = table_of_specific_energies_[n_points_epsilon - 1];\n    specific_energy += pressure_from_log_density(x_max_) / reference_density_ *\n                       exp(-x_max_) *\n                       std::expm1((gamma_of_x_max_ - 1.0) * (x - x_max_)) /\n                       (gamma_of_x_max_ - 1.0);\n    return specific_energy;\n  } else {\n    const auto table_index = static_cast<size_t>(floor(x / delta_x));\n    const double x0 = delta_x * table_index;\n    specific_energy = table_of_specific_energies_[table_index];\n    // Gaussian quadrature integral\n    for (size_t q = 0; q < number_of_quadrature_coefs_; q++) {\n      const double xp = (1.0 + quadrature_points_[q]) * (x - x0) / 2. + x0;\n      const double xm = (1.0 - quadrature_points_[q]) * (x - x0) / 2. + x0;\n      const double Pp = pressure_from_log_density(xp);\n      const double Pm = pressure_from_log_density(xm);\n      specific_energy += quadrature_weights_[q] * (x - x0) / 2. *\n                         (Pp * exp(-xp) + Pm * exp(-xm)) / reference_density_;\n    }\n    return specific_energy;\n  }\n}\n\ndouble Spectral::specific_enthalpy_from_density(\n    const double rest_mass_density) const {\n  return 1.0 + pressure_from_density(rest_mass_density) / rest_mass_density +\n         specific_internal_energy_from_density(rest_mass_density);\n}\n\n// P = P_0 exp(Int_0^x Gamma(xx) dxx)\n// where\n// Gamma(x) = gamma_0                        for  x < 0\n//            Sum_{n=0}^N gamma_n x^n             0 < x < x_{max}\n//            Sum_{n=0}^N gamma_n x_{max}^n       x > x_{max}\ndouble Spectral::pressure_from_log_density(const double x) const {\n  const double integral_of_gamma_of_x =\n      x <= 0.0 ? integral_coefficients_[0] * x\n               : (x < x_max_ ? integral_of_gamma(x)\n                             : integral_of_gamma_of_x_max_ +\n                                   gamma_of_x_max_ * (x - x_max_));\n  return reference_pressure_ * exp(integral_of_gamma_of_x);\n}\n\ndouble Spectral::pressure_from_density(const double rest_mass_density) const {\n  const double x = log(rest_mass_density / reference_density_);\n  return pressure_from_log_density(x);\n}\n\n// Solve for h(rho)=h0, which requires rootfinding for this EoS\ndouble Spectral::rest_mass_density_from_enthalpy(\n    const double specific_enthalpy) const {\n  const double reference_enthalpy =\n      specific_enthalpy_from_density(reference_density_);\n  const double upper_density = reference_density_ * exp(x_max_);\n  const double enthalpy_of_x_max_ =\n      specific_enthalpy_from_density(upper_density);\n  if (specific_enthalpy <= reference_enthalpy) {\n    double rest_mass_density =\n        (specific_enthalpy - 1.0) * (gamma_coefficients_[0] - 1.0) /\n        gamma_coefficients_[0] *\n        pow(reference_density_, gamma_coefficients_[0]) / reference_pressure_;\n    rest_mass_density =\n        pow(rest_mass_density, 1.0 / (gamma_coefficients_[0] - 1.0));\n    return rest_mass_density;\n  } else if (specific_enthalpy >= enthalpy_of_x_max_) {\n    // Above maximum density, we also have an analytical expression\n    // (h-1-eps_max)*(rho_max/P_max)+1/(Gamma_max-1) = Gamma_max / (Gamma_max-1)\n    // * exp((Gamma_max-1)*(x-x_max)) [Can be derived by combining expressions\n    // for epsilon(x) and P(x) with x = log(rho)]\n    const size_t n_points_epsilon = table_of_specific_energies_.size();\n    const double specific_internal_energy_of_x_max_ =\n        table_of_specific_energies_[n_points_epsilon - 1];\n    const double pressure_of_x_max_ = pressure_from_log_density(x_max_);\n    double x_target =\n        ((specific_enthalpy - 1.0 - specific_internal_energy_of_x_max_) *\n             upper_density / pressure_of_x_max_ +\n         1.0 / (gamma_of_x_max_ - 1.0)) *\n        (gamma_of_x_max_ - 1.0) / gamma_of_x_max_;\n    x_target = log(x_target) / (gamma_of_x_max_ - 1.0) + x_max_;\n    return reference_density_ * exp(x_target);\n  } else {\n    // Root-finding appropriate between reference density and maximum density\n    // We can use x=0 and x=x_max as bounds\n    const auto f = [this, &specific_enthalpy](const double density) {\n      return this->specific_enthalpy_from_density(density) - specific_enthalpy;\n    };\n    const auto root_from_lambda = RootFinder::toms748(\n        f, reference_density_, upper_density, 1.0e-14, 1.0e-15);\n    return root_from_lambda;\n  }\n}\n\nPUP::able::PUP_ID EquationsOfState::Spectral::my_PUP_ID = 0;\n\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief A spectral equation of state\n *\n * This equation of state is determined as a function of \\f$x =\n * \\ln(\\rho/\\rho_0)\\f$ where \\f$\\rho\\f$ is the rest mass density and\n * \\f$\\rho_0\\f$ is the provided reference density.  The adiabatic\n * index \\f$\\Gamma(x)\\f$ is defined such that\n * \\f{equation}{\n * \\frac{d \\ln p}{dx} = \\Gamma(x) = \\sum_{n=0}^N\n * \\gamma_n x^n\n * \\f}\n *\n * for the set of spectral coefficinets \\f$\\gamma_n\\f$ when\n * \\f$0 < x < x_u = \\ln(\\rho_u/\\rho_0)\\f$, where \\f$\\rho_u\\f$ is the provided\n * upper density.\n *\n * For \\f$ x < 0 \\f$, \\f$ \\Gamma(x) = \\gamma_0 \\f$.\n *\n * For \\f$ x > x_u \\f$, \\f$ \\Gamma(x) = \\Gamma(x_u) \\f$\n *\n *\n */\nclass Spectral : public EquationOfState<true, 1> {\n public:\n  static constexpr size_t thermodynamic_dim = 1;\n  static constexpr bool is_relativistic = true;\n\n  struct ReferenceDensity {\n    using type = double;\n    static constexpr Options::String help = {\"Reference density rho_0\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  struct ReferencePressure {\n    using type = double;\n    static constexpr Options::String help = {\"Reference pressure p_0\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  struct Coefficients {\n    using type = std::vector<double>;\n    static constexpr Options::String help = {\"Spectral coefficients gamma_i\"};\n  };\n\n  struct UpperDensity {\n    using type = double;\n    static constexpr Options::String help = {\"Upper density rho_u\"};\n    static double lower_bound() { return 0.0; }\n  };\n\n  static constexpr Options::String help = {\n      \"A spectral equation of state.  Defining x = log(rho/rho_0), Gamma(x) = \"\n      \"Sum_i gamma_i x^i, then the pressure is determined from d(log P)/dx = \"\n      \"Gamma(x) for x > 0.  For x < 0 the EOS is a polytrope with \"\n      \"Gamma(x)=Gamma(0).  For x > x_u = log(rho_u/rho_0), Gamma(x) = \"\n      \"Gamma(x_u).\\n\"\n      \"To get smooth equations of state, it is recommended that the second \"\n      \"and third supplied coefficient should be 0. It is up to the user to \"\n      \"choose coefficients that are physically reasonable, e.g. that \"\n      \"satisfy causality.\"};\n\n  using options = tmpl::list<ReferenceDensity, ReferencePressure, Coefficients,\n                             UpperDensity>;\n\n  Spectral() = default;\n  Spectral(const Spectral&) = default;\n  Spectral& operator=(const Spectral&) = default;\n  Spectral(Spectral&&) = default;\n  Spectral& operator=(Spectral&&) = default;\n  ~Spectral() override = default;\n\n  Spectral(double reference_density, double reference_pressure,\n           std::vector<double> coefficients, double upper_density);\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(Spectral, 1)\n\n  std::unique_ptr<EquationOfState<true, 1>> get_clone() const override;\n\n  std::unique_ptr<EquationOfState<true, 3>> promote_to_3d_eos() const override;\n\n  std::unique_ptr<EquationOfState<true, 2>> promote_to_2d_eos() const override;\n\n  bool operator==(const Spectral& rhs) const;\n\n  bool operator!=(const Spectral& rhs) const;\n\n  bool is_equal(const EquationOfState<true, 1>& rhs) const override;\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<true, 1>), Spectral);\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override { return 1.0; }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  double specific_internal_energy_lower_bound() const override { return 0.0; }\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  double specific_internal_energy_upper_bound() const override {\n    return std::numeric_limits<double>::max();\n  }\n\n  /// The vacuum baryon mass for this EoS\n  double baryon_mass() const override {\n    return hydro::units::geometric::default_baryon_mass;\n  }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(1)\n\n  double gamma(const double x) const;\n  double integral_of_gamma(const double x) const;\n  double chi_from_density(const double density) const;\n  double specific_internal_energy_from_density(const double density) const;\n  double specific_enthalpy_from_density(const double density) const;\n  double pressure_from_density(const double density) const;\n  double pressure_from_log_density(const double x) const;\n  double rest_mass_density_from_enthalpy(const double specific_enthalpy) const;\n\n  double reference_density_ = std::numeric_limits<double>::signaling_NaN();\n  double reference_pressure_ = std::numeric_limits<double>::signaling_NaN();\n  std::vector<double> integral_coefficients_{};\n  std::vector<double> gamma_coefficients_{};\n  double x_max_ = std::numeric_limits<double>::signaling_NaN();\n  double gamma_of_x_max_ = std::numeric_limits<double>::signaling_NaN();\n  double integral_of_gamma_of_x_max_ =\n      std::numeric_limits<double>::signaling_NaN();\n  std::vector<double> table_of_specific_energies_{};\n  // Information for Gaussian quadrature\n  size_t number_of_quadrature_coefs_ =\n      std::numeric_limits<size_t>::signaling_NaN();\n  std::vector<double> quadrature_weights_{};\n  std::vector<double> quadrature_points_{};\n};\n\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace EquationsOfState {\n\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     Tabulated3D<IsRelativistic>, double, 3)\nEQUATION_OF_STATE_MEMBER_DEFINITIONS(template <bool IsRelativistic>,\n                                     Tabulated3D<IsRelativistic>, DataVector, 3)\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> Tabulated3D<IsRelativistic>::\n    equilibrium_electron_fraction_from_density_temperature_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& temperature) const {\n  Scalar<DataType> electron_fraction = make_with_value<Scalar<DataType>>(\n      rest_mass_density, electron_fraction_lower_bound());\n\n  Scalar<DataType> converted_electron_fraction;\n  Scalar<DataType> log_rest_mass_density;\n  Scalar<DataType> log_temperature;\n\n  convert_to_table_quantities(\n      make_not_null(&converted_electron_fraction),\n      make_not_null(&log_rest_mass_density), make_not_null(&log_temperature),\n      electron_fraction, rest_mass_density, temperature);\n\n  // Compute free-streaming beta-eq. electron fraction (from DeltaMu==0)\n\n  if constexpr (std::is_same_v<DataType, double>) {\n    const auto& log_rho = get(log_rest_mass_density);\n    const auto& log_T = get(log_temperature);\n\n    const auto f = [this, log_rho, log_T](const double ye) {\n      const auto weights = interpolator_.get_weights(log_T, log_rho, ye);\n      const auto interpolated_values =\n          interpolator_.template interpolate<DeltaMu>(weights);\n\n      return interpolated_values[0];\n    };\n\n    const auto root_from_lambda =\n        RootFinder::toms748(f, electron_fraction_lower_bound(),\n                            electron_fraction_upper_bound(), 1.0e-14, 1.0e-15);\n\n    get(converted_electron_fraction) = root_from_lambda;\n\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    for (size_t s = 0; s < get(electron_fraction).size(); ++s) {\n      const auto& log_rho = get(log_rest_mass_density)[s];\n      const auto& log_T = get(log_temperature)[s];\n\n      const auto f = [this, log_rho, log_T](const double ye) {\n        const auto weights = interpolator_.get_weights(log_T, log_rho, ye);\n        const auto interpolated_values =\n            interpolator_.template interpolate<DeltaMu>(weights);\n\n        return interpolated_values[0];\n      };\n\n      const auto root_from_lambda = RootFinder::toms748(\n          f, electron_fraction_lower_bound(), electron_fraction_upper_bound(),\n          1.0e-14, 1.0e-15);\n\n      get(converted_electron_fraction)[s] = root_from_lambda;\n    }\n  }\n  return converted_electron_fraction;\n}\n\ntemplate <bool IsRelativistic>\nstd::unique_ptr<EquationOfState<IsRelativistic, 3>>\nTabulated3D<IsRelativistic>::get_clone() const {\n  auto clone = std::make_unique<Tabulated3D<IsRelativistic>>(*this);\n  return std::unique_ptr<EquationOfState<IsRelativistic, 3>>(std::move(clone));\n}\n\ntemplate <bool IsRelativistic>\nvoid Tabulated3D<IsRelativistic>::initialize(const h5::EosTable& spectre_eos) {\n  // STEP 0: Allocate intermediate data structures for initialization\n\n  auto setup_index_variable = [&spectre_eos](const std::string& name) {\n    auto& available_names = spectre_eos.independent_variable_names();\n    size_t i = 0;\n    for (i = 0; i < available_names.size(); ++i) {\n      if (name == available_names[i]) {\n        break;\n      }\n    }\n\n    auto bounds = spectre_eos.independent_variable_bounds()[i];\n    if (spectre_eos.independent_variable_uses_log_spacing()[i]) {\n      for (auto& b : bounds) {\n        b = std::log(b);\n      }\n    }\n    size_t num_points = spectre_eos.independent_variable_number_of_points()[i];\n\n    std::vector<double> index_variable(num_points);\n\n    for (i = 0; i < num_points; ++i) {\n      index_variable[i] =\n          bounds[0] + (bounds[1] - bounds[0]) / (double(num_points - 1)) * i;\n    }\n\n    return index_variable;\n  };\n\n  std::vector<double> electron_fraction =\n      setup_index_variable(\"electron fraction\");\n  std::vector<double> log_density = setup_index_variable(\"number density\");\n  std::vector<double> log_temperature = setup_index_variable(\"temperature\");\n\n  // Get size of table\n  size_t size =\n      electron_fraction.size() * log_density.size() * log_temperature.size();\n\n  std::vector<double> table_data(size * NumberOfVars);\n\n  // Need to setup index variables\n\n  auto pressure = spectre_eos.read_quantity(\"pressure\");\n  auto eps = spectre_eos.read_quantity(\"specific internal energy\");\n  auto cs2 = spectre_eos.read_quantity(\"sound speed squared\");\n\n  auto mu_l = spectre_eos.read_quantity(\"lepton chemical potential\");\n  //  WILL BE NEEDED FOR FUTURE PR\n  //  auto mu_q = spectre_eos.read_quantity(\"charge chemical potential\");\n  //  auto mu_b = spectre_eos.read_quantity(\"baryon chemical potential\");\n\n  double enthalpy_minimum = 1.e99;\n  double eps_min = 1.e99;\n\n  for (size_t s = 0; s < size; ++s) {\n    eps_min = std::min(eps_min, eps[s]);\n  }\n\n  double energy_shift = (eps_min < 0) ? 2. * eps_min : 0.;\n\n  // STEP 3: Fun with indices\n\n  // nb is in units of 1/fm^3\n  // convert to geometrical units\n  // assuming an effective mass scale\n  // set by the neutron mass\n\n  constexpr double nb_fm3_to_geom = hydro::units::nuclear::neutron_mass /\n                                    hydro::units::nuclear::pressure_unit;\n\n  for (double& log_density_i : log_density) {\n    log_density_i += std::log(nb_fm3_to_geom);\n  }\n\n  // Convert table\n  for (size_t iR = 0; iR < log_density.size(); ++iR) {\n    for (size_t iT = 0; iT < log_temperature.size(); ++iT) {\n      for (size_t iY = 0; iY < electron_fraction.size(); ++iY) {\n        // Index spectre table\n        // Ye varies fastest\n        size_t index_spectre =\n            iY + electron_fraction.size() * (iR + log_density.size() * iT);\n        // Local index\n        // T varies fastest\n        size_t index_tab3D =\n            iT + log_temperature.size() * (iR + log_density.size() * iY);\n\n        constexpr double press_MeV_to_geom =\n            1.0 / hydro::units::nuclear::pressure_unit;\n\n        double* table_point = &(table_data[index_tab3D * NumberOfVars]);\n\n        table_point[Pressure] =\n            std::log(press_MeV_to_geom * pressure[index_spectre]);\n        table_point[Epsilon] = std::log(eps[index_spectre] - energy_shift);\n        table_point[CsSquared] = cs2[index_spectre];\n        table_point[DeltaMu] = mu_l[index_spectre];\n\n        // Determine specific enthalpy minimum\n        double h = 1. + table_point[Epsilon] +\n                   table_point[Pressure] / std::exp(log_density[iR]);\n        enthalpy_minimum = std::min(enthalpy_minimum, h);\n      }\n    }\n  }\n\n  initialize(electron_fraction, log_density, log_temperature, table_data,\n             energy_shift, enthalpy_minimum);\n}\n\ntemplate <bool IsRelativistic>\nvoid Tabulated3D<IsRelativistic>::initialize(\n    std::vector<double> electron_fraction, std::vector<double> log_density,\n    std::vector<double> log_temperature, std::vector<double> table_data,\n    double energy_shift, double enthalpy_minimum) {\n  energy_shift_ = energy_shift;\n  enthalpy_minimum_ = enthalpy_minimum;\n  table_electron_fraction_ = std::move(electron_fraction);\n  table_log_density_ = std::move(log_density);\n  table_log_temperature_ = std::move(log_temperature);\n  table_data_ = std::move(table_data);\n\n  initialize_interpolator();\n}\n\ntemplate <bool IsRelativistic>\nvoid Tabulated3D<IsRelativistic>::initialize_interpolator() {\n  Index<3> num_x_points;\n\n  // The order is T, rho, Ye\n  num_x_points[0] = table_log_temperature_.size();\n  num_x_points[1] = table_log_density_.size();\n  num_x_points[2] = table_electron_fraction_.size();\n\n  std::array<gsl::span<double const>, 3> independent_data_view;\n\n  independent_data_view[0] =\n      gsl::span<double const>{table_log_temperature_.data(), num_x_points[0]};\n\n  independent_data_view[1] =\n      gsl::span<double const>{table_log_density_.data(), num_x_points[1]};\n\n  independent_data_view[2] =\n      gsl::span<double const>{table_electron_fraction_.data(), num_x_points[2]};\n\n  interpolator_ = intrp::UniformMultiLinearSpanInterpolation<3, NumberOfVars>(\n      independent_data_view, {table_data_.data(), table_data_.size()},\n      num_x_points);\n}\n\ntemplate <bool IsRelativistic>\nbool Tabulated3D<IsRelativistic>::is_equal(\n    const EquationOfState<IsRelativistic, 3>& rhs) const {\n  const auto& derived_ptr =\n      dynamic_cast<const Tabulated3D<IsRelativistic>* const>(&rhs);\n  return derived_ptr != nullptr and *derived_ptr == *this;\n}\n\ntemplate <bool IsRelativistic>\nbool Tabulated3D<IsRelativistic>::operator==(\n    const Tabulated3D<IsRelativistic>& rhs) const {\n  bool result = true;\n  result &= (rhs.enthalpy_minimum_ == this->enthalpy_minimum_);\n  result &= (rhs.energy_shift_ == this->energy_shift_);\n  result &= (rhs.table_electron_fraction_ == this->table_electron_fraction_);\n  result &= (rhs.table_log_density_ == this->table_log_density_);\n  result &= (rhs.table_log_temperature_ == this->table_log_temperature_);\n  result &= (rhs.table_data_ == this->table_data_);\n\n  return result;\n}\n\ntemplate <bool IsRelativistic>\nbool Tabulated3D<IsRelativistic>::operator!=(\n    const Tabulated3D<IsRelativistic>& rhs) const {\n  return not(*this == rhs);\n}\n\ntemplate <bool IsRelativistic>\nTabulated3D<IsRelativistic>::Tabulated3D(CkMigrateMessage* msg)\n    : EquationOfState<IsRelativistic, 3>(msg) {}\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nvoid Tabulated3D<IsRelativistic>::enforce_physicality(\n    Scalar<DataType>& electron_fraction, Scalar<DataType>& rest_mass_density,\n    Scalar<DataType>& temperature) const {\n  if constexpr (std::is_same_v<DataType, double>) {\n    get(rest_mass_density) = std::max(\n        std::min(get(rest_mass_density), rest_mass_density_upper_bound()),\n        rest_mass_density_lower_bound());\n\n    get(electron_fraction) = std::max(\n        std::min(get(electron_fraction), electron_fraction_upper_bound()),\n        electron_fraction_lower_bound());\n\n    get(temperature) =\n        std::max(std::min(get(temperature), temperature_upper_bound()),\n                 temperature_lower_bound());\n\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    for (size_t s = 0; s < get(electron_fraction).size(); ++s) {\n      get(rest_mass_density)[s] = std::max(\n          std::min(get(rest_mass_density)[s], rest_mass_density_upper_bound()),\n          rest_mass_density_lower_bound());\n\n      get(electron_fraction)[s] = std::max(\n          std::min(get(electron_fraction)[s], electron_fraction_upper_bound()),\n          electron_fraction_lower_bound());\n\n      get(temperature)[s] =\n          std::max(std::min(get(temperature)[s], temperature_upper_bound()),\n                   temperature_lower_bound());\n    }\n  }\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nTabulated3D<IsRelativistic>::pressure_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& electron_fraction) const {\n  auto temperature = temperature_from_density_and_energy_impl(\n      rest_mass_density, specific_internal_energy, electron_fraction);\n\n  return pressure_from_density_and_temperature_impl(\n      rest_mass_density, temperature, electron_fraction);\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nTabulated3D<IsRelativistic>::pressure_from_density_and_temperature_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& temperature,\n    const Scalar<DataType>& electron_fraction) const {\n  Scalar<DataType> converted_electron_fraction;\n  Scalar<DataType> log_rest_mass_density;\n  Scalar<DataType> log_temperature;\n\n  convert_to_table_quantities(\n      make_not_null(&converted_electron_fraction),\n      make_not_null(&log_rest_mass_density), make_not_null(&log_temperature),\n      electron_fraction, rest_mass_density, temperature);\n\n  Scalar<DataType> pressure =\n      make_with_value<Scalar<DataType>>(get(rest_mass_density), 0.0);\n\n  if constexpr (std::is_same_v<DataType, double>) {\n    auto weights = interpolator_.get_weights(get(log_temperature),\n                                             get(log_rest_mass_density),\n                                             get(converted_electron_fraction));\n    auto interpolated_state =\n        interpolator_.template interpolate<Pressure>(weights);\n    get(pressure) = std::exp(interpolated_state[0]);\n\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    for (size_t s = 0; s < get(electron_fraction).size(); ++s) {\n      auto weights = interpolator_.get_weights(\n          get(log_temperature)[s], get(log_rest_mass_density)[s],\n          get(converted_electron_fraction)[s]);\n      auto interpolated_state =\n          interpolator_.template interpolate<Pressure>(weights);\n      get(pressure)[s] = std::exp(interpolated_state[0]);\n    }\n  }\n\n  return pressure;\n}\n\ntemplate <bool IsRelativistic>\nvoid Tabulated3D<IsRelativistic>::pup(PUP::er& p) {\n  EquationOfState<IsRelativistic, 3>::pup(p);\n  p | energy_shift_;\n  p | enthalpy_minimum_;\n  p | table_electron_fraction_;\n  p | table_log_density_;\n  p | table_log_temperature_;\n  p | table_data_;\n\n  if (p.isUnpacking()) {\n    initialize_interpolator();\n  }\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType>\nTabulated3D<IsRelativistic>::temperature_from_density_and_energy_impl(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& electron_fraction) const {\n  Scalar<DataType> converted_electron_fraction;\n  Scalar<DataType> log_rest_mass_density;\n\n  Scalar<DataType> log_temperature;\n  Scalar<DataType> temperature;\n\n  temperature = make_with_value<Scalar<DataType>>(rest_mass_density,\n                                                  temperature_lower_bound());\n\n  convert_to_table_quantities(\n      make_not_null(&converted_electron_fraction),\n      make_not_null(&log_rest_mass_density), make_not_null(&log_temperature),\n      electron_fraction, rest_mass_density, temperature);\n\n  // Check bounds on eps, note that eps may be negative\n  Scalar<DataType> log_specific_internal_energy = specific_internal_energy;\n\n  if constexpr (std::is_same_v<DataType, double>) {\n    get(log_specific_internal_energy) =\n        std::max(std::min(get(specific_internal_energy),\n                          specific_internal_energy_upper_bound(\n                              get(rest_mass_density), get(electron_fraction))),\n                 specific_internal_energy_lower_bound(get(rest_mass_density),\n                                                      get(electron_fraction)));\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    for (size_t s = 0; s < get(electron_fraction).size(); ++s) {\n      get(log_specific_internal_energy)[s] = std::max(\n          std::min(get(specific_internal_energy)[s],\n                   specific_internal_energy_upper_bound(\n                       get(rest_mass_density)[s], get(electron_fraction)[s])),\n          specific_internal_energy_lower_bound(get(rest_mass_density)[s],\n                                               get(electron_fraction)[s]));\n    }\n  }\n\n  // Correct for negative eps\n  get(log_specific_internal_energy) -= energy_shift_;\n  get(log_specific_internal_energy) = log(get(log_specific_internal_energy));\n\n  if constexpr (std::is_same_v<DataType, double>) {\n    const auto& log_eps = get(log_specific_internal_energy);\n    const auto& log_rho = get(log_rest_mass_density);\n    const auto& ye = get(converted_electron_fraction);\n\n    // Root-finding appropriate between reference density and maximum density\n    // We can use x=0 and x=x_max as bounds\n    const auto f = [this, log_eps, log_rho, ye](const double log_T) {\n      const auto weights = interpolator_.get_weights(log_T, log_rho, ye);\n      const auto interpolated_values =\n          interpolator_.template interpolate<Epsilon>(weights);\n\n      return log_eps - interpolated_values[0];\n    };\n\n    bool need_root_finding = true;\n    double root_from_lambda = table_log_temperature_.front();\n    if (fabs(f(table_log_temperature_.front())) <= 1.0e-14) {\n      need_root_finding = false;\n    }\n\n    if (fabs(f(upper_bound_tolerance_ * table_log_temperature_.back())) <=\n        1.0e-14) {\n      root_from_lambda = table_log_temperature_.back();\n      need_root_finding = false;\n    }\n\n    if (need_root_finding) {\n      root_from_lambda = RootFinder::toms748(\n          f, table_log_temperature_.front(),\n          upper_bound_tolerance_ * table_log_temperature_.back(), 1.0e-14,\n          1.0e-15);\n    }\n\n    get(temperature) = exp(root_from_lambda);\n\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    for (size_t s = 0; s < get(electron_fraction).size(); ++s) {\n      const auto& log_eps = get(log_specific_internal_energy)[s];\n      const auto& log_rho = get(log_rest_mass_density)[s];\n      const auto& ye = get(converted_electron_fraction)[s];\n\n      // Root-finding appropriate between reference density and maximum density\n      // We can use x=0 and x=x_max as bounds\n      const auto f = [this, log_eps, log_rho, ye](const double log_T) {\n        const auto weights = interpolator_.get_weights(log_T, log_rho, ye);\n        const auto interpolated_values =\n            interpolator_.template interpolate<Epsilon>(weights);\n\n        return log_eps - interpolated_values[0];\n      };\n\n      // Check bounds to avoid error in TOMS748 if bracket is zero\n      bool need_root_finding = true;\n      double root_from_lambda = table_log_temperature_.front();\n      if (fabs(f(table_log_temperature_.front())) <= 1.0e-14) {\n        need_root_finding = false;\n      }\n\n      if (fabs(f(upper_bound_tolerance_ * table_log_temperature_.back())) <=\n          1.0e-14) {\n        root_from_lambda = table_log_temperature_.back();\n        need_root_finding = false;\n      }\n      if (need_root_finding) {\n        root_from_lambda = RootFinder::toms748(\n            f, table_log_temperature_.front(),\n            upper_bound_tolerance_ * table_log_temperature_.back(), 1.0e-14,\n            1.0e-15);\n      }\n\n      get(temperature)[s] = exp(root_from_lambda);\n    }\n  }\n  return temperature;\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> Tabulated3D<IsRelativistic>::\n    specific_internal_energy_from_density_and_temperature_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& temperature,\n        const Scalar<DataType>& electron_fraction) const {\n  Scalar<DataType> converted_electron_fraction;\n  Scalar<DataType> log_rest_mass_density;\n  Scalar<DataType> log_temperature;\n\n  convert_to_table_quantities(\n      make_not_null(&converted_electron_fraction),\n      make_not_null(&log_rest_mass_density), make_not_null(&log_temperature),\n      electron_fraction, rest_mass_density, temperature);\n\n  Scalar<DataType> specific_internal_energy =\n      make_with_value<Scalar<DataType>>(get(rest_mass_density), 0.0);\n\n  if constexpr (std::is_same_v<DataType, double>) {\n    auto weights = interpolator_.get_weights(get(log_temperature),\n                                             get(log_rest_mass_density),\n                                             get(converted_electron_fraction));\n    auto interpolated_state =\n        interpolator_.template interpolate<Epsilon>(weights);\n    get(specific_internal_energy) =\n        std::exp(interpolated_state[0]) + energy_shift_;\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    for (size_t s = 0; s < get(electron_fraction).size(); ++s) {\n      auto weights = interpolator_.get_weights(\n          get(log_temperature)[s], get(log_rest_mass_density)[s],\n          get(converted_electron_fraction)[s]);\n      auto interpolated_state =\n          interpolator_.template interpolate<Epsilon>(weights);\n      get(specific_internal_energy)[s] =\n          std::exp(interpolated_state[0]) + energy_shift_;\n    }\n  }\n\n  return specific_internal_energy;\n}\n\ntemplate <bool IsRelativistic>\ntemplate <class DataType>\nScalar<DataType> Tabulated3D<IsRelativistic>::\n    sound_speed_squared_from_density_and_temperature_impl(\n        const Scalar<DataType>& rest_mass_density,\n        const Scalar<DataType>& temperature,\n        const Scalar<DataType>& electron_fraction) const {\n  Scalar<DataType> converted_electron_fraction;\n  Scalar<DataType> log_rest_mass_density;\n  Scalar<DataType> log_temperature;\n\n  convert_to_table_quantities(\n      make_not_null(&converted_electron_fraction),\n      make_not_null(&log_rest_mass_density), make_not_null(&log_temperature),\n      electron_fraction, rest_mass_density, temperature);\n\n  Scalar<DataType> cs2 =\n      make_with_value<Scalar<DataType>>(get(rest_mass_density), 0.0);\n\n  if constexpr (std::is_same_v<DataType, double>) {\n    auto weights = interpolator_.get_weights(get(log_temperature),\n                                             get(log_rest_mass_density),\n                                             get(converted_electron_fraction));\n    auto interpolated_state =\n        interpolator_.template interpolate<CsSquared>(weights);\n    get(cs2) = interpolated_state[0];\n\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    for (size_t s = 0; s < get(electron_fraction).size(); ++s) {\n      auto weights = interpolator_.get_weights(\n          get(log_temperature)[s], get(log_rest_mass_density)[s],\n          get(converted_electron_fraction)[s]);\n      auto interpolated_state =\n          interpolator_.template interpolate<CsSquared>(weights);\n      get(cs2)[s] = interpolated_state[0];\n    }\n  }\n\n  return cs2;\n}\n\ntemplate <bool IsRelativistic>\ndouble Tabulated3D<IsRelativistic>::specific_internal_energy_lower_bound(\n    const double rest_mass_density, const double electron_fraction) const {\n  double converted_electron_fraction =\n      std::min(std::max(electron_fraction_lower_bound(), electron_fraction),\n               electron_fraction_upper_bound());\n\n  double log_rest_mass_density =\n      std::min(std::max(rest_mass_density_lower_bound(), rest_mass_density),\n               rest_mass_density_upper_bound());\n\n  log_rest_mass_density = log(log_rest_mass_density);\n\n  auto weights = interpolator_.get_weights(log(temperature_lower_bound()),\n                                           log_rest_mass_density,\n                                           converted_electron_fraction);\n  auto interpolated_state =\n      interpolator_.template interpolate<Epsilon>(weights);\n\n  return exp(interpolated_state[0]) + energy_shift_;\n}\n\ntemplate <bool IsRelativistic>\ndouble Tabulated3D<IsRelativistic>::specific_internal_energy_upper_bound(\n    const double rest_mass_density, const double electron_fraction) const {\n  double converted_electron_fraction =\n      std::min(std::max(electron_fraction_lower_bound(), electron_fraction),\n               electron_fraction_upper_bound());\n\n  double log_rest_mass_density =\n      std::min(std::max(rest_mass_density_lower_bound(), rest_mass_density),\n               rest_mass_density_upper_bound());\n\n  log_rest_mass_density = log(log_rest_mass_density);\n\n  auto weights = interpolator_.get_weights(\n      log(upper_bound_tolerance_ * temperature_upper_bound()),\n      log_rest_mass_density, converted_electron_fraction);\n  auto interpolated_state =\n      interpolator_.template interpolate<Epsilon>(weights);\n\n  return exp(interpolated_state[0]) + energy_shift_;\n}\n\ntemplate <bool IsRelativistic>\nTabulated3D<IsRelativistic>::\n    Tabulated3D(  // NOLINTNEXTLINE(performance-unnecessary-value-param)\n        std::vector<double> electron_fraction,\n        // NOLINTNEXTLINE(performance-unnecessary-value-param)\n        std::vector<double> log_density,\n        // NOLINTNEXTLINE(performance-unnecessary-value-param)\n        std::vector<double> log_temperature,\n        // NOLINTNEXTLINE(performance-unnecessary-value-param)\n        std::vector<double> table_data, double energy_shift,\n        double enthalpy_minimum) {\n  initialize(std::move(electron_fraction), std::move(log_density),\n             std::move(log_temperature), std::move(table_data), energy_shift,\n             enthalpy_minimum);\n}\n\ntemplate <bool IsRelativistic>\nTabulated3D<IsRelativistic>::Tabulated3D(const h5::EosTable& spectre_eos) {\n  initialize(spectre_eos);\n}\n\ntemplate <bool IsRelativistic>\nTabulated3D<IsRelativistic>::Tabulated3D(const std::string& filename,\n                                         const std::string& subfilename) {\n  h5::H5File<h5::AccessType::ReadOnly> eos_file{filename};\n  const auto& spectre_eos = eos_file.get<h5::EosTable>(\"/\" + subfilename);\n\n  initialize(spectre_eos);\n}\n\n}  // namespace EquationsOfState\n\ntemplate class EquationsOfState::Tabulated3D<true>;\ntemplate class EquationsOfState::Tabulated3D<false>;\n\ntemplate Scalar<double> EquationsOfState::Tabulated3D<true>::\n    equilibrium_electron_fraction_from_density_temperature_impl(\n        Tensor<double, brigand::list<>, brigand::list<>> const&,\n        Tensor<double, brigand::list<>, brigand::list<>> const&) const;\ntemplate Scalar<DataVector> EquationsOfState::Tabulated3D<true>::\n    equilibrium_electron_fraction_from_density_temperature_impl(\n        Tensor<DataVector, brigand::list<>, brigand::list<>> const&,\n        Tensor<DataVector, brigand::list<>, brigand::list<>> const&) const;\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/preprocessor/arithmetic/dec.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"IO/H5/EosTable.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/Interpolation/MultiLinearSpanInterpolation.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace EquationsOfState {\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Nuclear matter equation of state in tabulated form.\n *\n * The equation of state takes the form\n *\n * \\f[\n * p = p (T, rho, Y_e)\n * \\f]\n *\n * where \\f$\\rho\\f$ is the rest mass density, \\f$T\\f$ is the\n * temperature, and \\f$Y_e\\f$ is the electron fraction.\n * The temperature is given in units of MeV.\n */\ntemplate <bool IsRelativistic>\nclass Tabulated3D : public EquationOfState<IsRelativistic, 3> {\n public:\n  static constexpr size_t thermodynamic_dim = 3;\n  static constexpr bool is_relativistic = IsRelativistic;\n\n  static constexpr Options::String help = {\n      \"A tabulated three-dimensional equation of state.\\n\"\n      \"The energy density, pressure and sound speed \"\n      \"are tabulated as a function of density, electron_fraction and \"\n      \"temperature.\"};\n\n  struct TableFilename {\n    using type = std::string;\n    static constexpr Options::String help{\"File name of the EOS table\"};\n  };\n\n  struct TableSubFilename {\n    using type = std::string;\n    static constexpr Options::String help{\n        \"Subfile name of the EOS table, e.g., 'dd2'.\"};\n  };\n\n  using options = tmpl::list<TableFilename, TableSubFilename>;\n\n  /// Fields stored in the table\n  enum : size_t { Epsilon = 0, Pressure, CsSquared, DeltaMu, NumberOfVars };\n\n  Tabulated3D() = default;\n  Tabulated3D(const Tabulated3D&) = default;\n  Tabulated3D& operator=(const Tabulated3D&) = default;\n  Tabulated3D(Tabulated3D&&) = default;\n  Tabulated3D& operator=(Tabulated3D&&) = default;\n  ~Tabulated3D() override = default;\n\n  explicit Tabulated3D(const std::string& filename,\n                       const std::string& subfilename);\n\n  explicit Tabulated3D(std::vector<double> electron_fraction,\n                       std::vector<double> log_density,\n                       std::vector<double> log_temperature,\n                       std::vector<double> table_data, double energy_shift,\n                       double enthalpy_minimum);\n\n  explicit Tabulated3D(const h5::EosTable& spectre_eos);\n\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBERS(Tabulated3D, 3)\n\n  template <class DataType>\n  void convert_to_table_quantities(\n      const gsl::not_null<Scalar<DataType>*> converted_electron_fraction,\n      const gsl::not_null<Scalar<DataType>*> log_rest_mass_density,\n      const gsl::not_null<Scalar<DataType>*> log_temperature,\n      const Scalar<DataType>& electron_fraction,\n      const Scalar<DataType>& rest_mass_density,\n      const Scalar<DataType>& temperature) const {\n    get(*converted_electron_fraction) = get(electron_fraction);\n    get(*log_rest_mass_density) = get(rest_mass_density);\n    get(*log_temperature) = get(temperature);\n\n    // Enforce physicality of input\n    // We reuse the same variables here, these are not log yet.\n    enforce_physicality(*converted_electron_fraction, *log_rest_mass_density,\n                        *log_temperature);\n\n    // Table uses log T and log rho\n    get(*log_rest_mass_density) = log(get(*log_rest_mass_density));\n    get(*log_temperature) = log(get(*log_temperature));\n  }\n\n  std::unique_ptr<EquationOfState<IsRelativistic, 3>> get_clone()\n      const override;\n\n  void initialize(std::vector<double> electron_fraction,\n                  std::vector<double> log_density,\n                  std::vector<double> log_temperature,\n                  std::vector<double> table_data, double energy_shift,\n                  double enthalpy_minimum);\n\n\n  void initialize(const h5::EosTable& spectre_eos);\n\n  bool is_equal(const EquationOfState<IsRelativistic, 3>& rhs) const override;\n\n  /// \\brief Returns `true` if the EOS is barotropic\n  bool is_barotropic() const override { return false; }\n\n  /// \\brief Returns `true` if the EOS is in beta-equilibrium\n  bool is_equilibrium() const override { return false; }\n\n  bool operator==(const Tabulated3D<IsRelativistic>& rhs) const;\n\n  bool operator!=(const Tabulated3D<IsRelativistic>& rhs) const;\n\n  template <class DataType>\n  Scalar<DataType> equilibrium_electron_fraction_from_density_temperature_impl(\n      const Scalar<DataType>& rest_mass_density,\n      const Scalar<DataType>& temperature) const;\n\n  /// @{\n  /*!\n   * Computes the electron fraction in beta-equilibrium \\f$Y_e^{\\rm eq}\\f$ from\n   * the rest mass density \\f$\\rho\\f$ and the temperature \\f$T\\f$.\n   */\n  Scalar<double> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<double>& rest_mass_density,\n      const Scalar<double>& temperature) const override {\n    return equilibrium_electron_fraction_from_density_temperature_impl<double>(\n        rest_mass_density, temperature);\n  }\n\n  Scalar<DataVector> equilibrium_electron_fraction_from_density_temperature(\n      const Scalar<DataVector>& rest_mass_density,\n      const Scalar<DataVector>& temperature) const override {\n    return equilibrium_electron_fraction_from_density_temperature_impl<\n        DataVector>(rest_mass_density, temperature);\n  }\n  /// @}\n  //\n\n  template <typename DataType>\n  void enforce_physicality(Scalar<DataType>& electron_fraction,\n                           Scalar<DataType>& density,\n                           Scalar<DataType>& temperature) const;\n\n  WRAPPED_PUPable_decl_base_template(  // NOLINT\n      SINGLE_ARG(EquationOfState<IsRelativistic, 3>), Tabulated3D);\n\n  /// The lower bound of the electron fraction that is valid for this EOS\n  double electron_fraction_lower_bound() const override {\n    return table_electron_fraction_.front();\n  }\n\n  /// The upper bound of the electron fraction that is valid for this EOS\n  double electron_fraction_upper_bound() const override {\n    return table_electron_fraction_.back();\n  }\n\n  /// The lower bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_lower_bound() const override {\n    return std::exp((table_log_density_.front()));\n  }\n\n  /// The upper bound of the rest mass density that is valid for this EOS\n  double rest_mass_density_upper_bound() const override {\n    return std::exp((table_log_density_.back()));\n  }\n\n  /// The lower bound of the temperature that is valid for this EOS\n  double temperature_lower_bound() const override {\n    return std::exp((table_log_temperature_.front()));\n  }\n\n  /// The upper bound of the temperature that is valid for this EOS\n  double temperature_upper_bound() const override {\n    return std::exp((table_log_temperature_.back()));\n  }\n\n  /// The lower bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$ and electron fraction \\f$Y_e\\f$\n  double specific_internal_energy_lower_bound(\n      const double rest_mass_density,\n      const double electron_fraction) const override;\n\n  /// The upper bound of the specific internal energy that is valid for this EOS\n  /// at the given rest mass density \\f$\\rho\\f$\n  double specific_internal_energy_upper_bound(\n      const double rest_mass_density,\n      const double electron_fraction) const override;\n\n  /// The lower bound of the specific enthalpy that is valid for this EOS\n  double specific_enthalpy_lower_bound() const override {\n    return enthalpy_minimum_;\n  }\n\n  /// The baryon mass for this EoS\n  double baryon_mass() const override {\n    return hydro::units::geometric::neutron_mass;\n  }\n\n private:\n  EQUATION_OF_STATE_FORWARD_DECLARE_MEMBER_IMPLS(3)\n\n  void initialize_interpolator();\n\n  /// Energy shift used to account for negative specific internal energies,\n  /// which are only stored logarithmically\n  double energy_shift_ = 0.;\n\n  /// Enthalpy minium  across the table\n  double enthalpy_minimum_ = 1.;\n\n  /// Main interpolator for the EoS.\n  /// The ordering is  \\f$(\\log T. \\log \\rho, Y_e)\\f$.\n  /// Assumed to be sorted in ascending order.\n  intrp::UniformMultiLinearSpanInterpolation<3, NumberOfVars> interpolator_{};\n  /// Electron fraction\n  std::vector<double> table_electron_fraction_{};\n  /// Logarithmic rest-mass denisty\n  std::vector<double> table_log_density_{};\n  /// Logarithmic temperature\n  std::vector<double> table_log_temperature_{};\n  /// Tabulate data. Entries are stated in the enum\n  std::vector<double> table_data_{};\n\n  /// Tolerance on upper bound for root finding\n  static constexpr double upper_bound_tolerance_ = 0.9999;\n};\n\n/// \\cond\ntemplate <bool IsRelativistic>\nPUP::able::PUP_ID EquationsOfState::Tabulated3D<IsRelativistic>::my_PUP_ID = 0;\n/// \\endcond\n\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/InitialData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  IrrotationalBns.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  IrrotationalBns.hpp\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/InitialData/IrrotationalBns.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/InitialData/IrrotationalBns.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorExpression.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace hydro::initial_data::irrotational_bns {\ntemplate <typename DataType>\nvoid rotational_shift(\n    const gsl::not_null<tnsr::I<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& shift,\n    const tnsr::I<DataType, 3>& spatial_rotational_killing_vector) {\n  ::tenex::evaluate<ti::I>(\n      result, shift(ti::I) + spatial_rotational_killing_vector(ti::I));\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> rotational_shift(\n    const tnsr::I<DataType, 3>& shift,\n    const tnsr::I<DataType, 3>& spatial_rotational_killing_vector) {\n  tnsr::I<DataType, 3> buffer{};\n  rotational_shift(make_not_null(&buffer), shift,\n                   spatial_rotational_killing_vector);\n  return buffer;\n}\n\ntemplate <typename DataType>\nvoid rotational_shift_stress(const gsl::not_null<tnsr::II<DataType, 3>*> result,\n                             const tnsr::I<DataType, 3>& rotational_shift,\n                             const Scalar<DataType>& lapse) {\n  ::tenex::evaluate<ti::I, ti::J>(\n      result,\n      rotational_shift(ti::I) * rotational_shift(ti::J) / square(lapse()));\n}\n\ntemplate <typename DataType>\ntnsr::II<DataType, 3> rotational_shift_stress(\n    const tnsr::I<DataType, 3>& rotational_shift,\n    const Scalar<DataType>& lapse) {\n  tnsr::II<DataType, 3> buffer{};\n  rotational_shift_stress(make_not_null(&buffer), rotational_shift, lapse);\n  return buffer;\n}\n\ntemplate <typename DataType>\nvoid derivative_rotational_shift_over_lapse_squared(\n    const gsl::not_null<tnsr::iJ<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& rotational_shift,\n    const tnsr::iJ<DataType, 3>& deriv_of_shift, const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, 3>& deriv_of_lapse,\n    const tnsr::iJ<DataType, 3>& deriv_of_spatial_rotational_killing_vector) {\n  ::tenex::evaluate<ti::i, ti::J>(\n      result, (deriv_of_shift(ti::i, ti::J) +\n               deriv_of_spatial_rotational_killing_vector(ti::i, ti::J)) /\n                      square(lapse()) -\n                  2.0 * rotational_shift(ti::J) * deriv_of_lapse(ti::i) /\n                      cube(lapse()));\n}\n\ntemplate <typename DataType>\ntnsr::iJ<DataType, 3> derivative_rotational_shift_over_lapse_squared(\n    const tnsr::I<DataType, 3>& rotational_shift,\n    const tnsr::iJ<DataType, 3>& deriv_of_shift, const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, 3>& deriv_of_lapse,\n    const tnsr::iJ<DataType, 3>& deriv_of_spatial_rotational_killing_vector) {\n  tnsr::iJ<DataType, 3> buffer{};\n  derivative_rotational_shift_over_lapse_squared(\n      make_not_null(&buffer), rotational_shift, deriv_of_shift, lapse,\n      deriv_of_lapse, deriv_of_spatial_rotational_killing_vector);\n  return buffer;\n}\n\ntemplate <typename DataType>\nvoid specific_enthalpy_squared(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const tnsr::I<DataType, 3>& rotational_shift, const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, 3>& velocity_potential_gradient,\n    const tnsr::II<DataType, 3>& inverse_spatial_metric,\n    const double euler_enthalpy_constant) {\n  return tenex::evaluate<>(\n      result,\n      square((euler_enthalpy_constant +\n              rotational_shift(ti::I) * velocity_potential_gradient(ti::i)) /\n             lapse()) -\n          velocity_potential_gradient(ti::i) *\n              velocity_potential_gradient(ti::j) *\n              inverse_spatial_metric(ti::I, ti::J));\n}\n\ntemplate <typename DataType>\nScalar<DataType> specific_enthalpy_squared(\n    const tnsr::I<DataType, 3>& rotational_shift, const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, 3>& velocity_potential_gradient,\n    const tnsr::II<DataType, 3>& inverse_spatial_metric,\n    const double euler_enthalpy_constant) {\n  Scalar<DataType> buffer{};\n  specific_enthalpy_squared(make_not_null(&buffer), rotational_shift, lapse,\n                            velocity_potential_gradient, inverse_spatial_metric,\n                            euler_enthalpy_constant);\n  return buffer;\n}\n\ntemplate <typename DataType>\nvoid spatial_rotational_killing_vector(\n    const gsl::not_null<tnsr::I<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& x, const double orbital_angular_velocity,\n    const Scalar<DataType>& sqrt_det_spatial_metric) {\n  // Cross product involves volume element in arbitrary coordinates\n  set_number_of_grid_points(result, sqrt_det_spatial_metric);\n  get<0>(*result) =\n      -get(sqrt_det_spatial_metric) * get<1>(x) * orbital_angular_velocity;\n  get<1>(*result) =\n      get(sqrt_det_spatial_metric) * get<0>(x) * orbital_angular_velocity;\n  get<2>(*result) = 0.0;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> spatial_rotational_killing_vector(\n    const tnsr::I<DataType, 3>& x, const double orbital_angular_velocity,\n    const Scalar<DataType>& sqrt_det_spatial_metric) {\n  tnsr::I<DataType, 3> buffer{};\n  spatial_rotational_killing_vector(make_not_null(&buffer), x,\n                                    orbital_angular_velocity,\n                                    sqrt_det_spatial_metric);\n  return buffer;\n}\n\ntemplate <typename DataType>\nvoid divergence_spatial_rotational_killing_vector(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const tnsr::I<DataType, 3>& x, const double /*orbital_angular_velocity*/,\n    const Scalar<DataType>& /*sqrt_det_spatial_metric*/) {\n  set_number_of_grid_points(result, x);\n  std::fill(result->begin(), result->end(), 0.0);\n}\n\ntemplate <typename DataType>\nScalar<DataType> divergence_spatial_rotational_killing_vector(\n    const tnsr::I<DataType, 3>& x, const double orbital_angular_velocity,\n    const Scalar<DataType>& sqrt_det_spatial_metric) {\n  Scalar<DataType> buffer{};\n  divergence_spatial_rotational_killing_vector(make_not_null(&buffer), x,\n                                               orbital_angular_velocity,\n                                               sqrt_det_spatial_metric);\n  return buffer;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                               \\\n  template void rotational_shift(                                            \\\n      gsl::not_null<tnsr::I<DTYPE(data), 3>*> result,                        \\\n      const tnsr::I<DTYPE(data), 3>& shift,                                  \\\n      const tnsr::I<DTYPE(data), 3>& spatial_rotational_killing_vector);     \\\n  template tnsr::I<DTYPE(data), 3> rotational_shift(                         \\\n      const tnsr::I<DTYPE(data), 3>& shift,                                  \\\n      const tnsr::I<DTYPE(data), 3>& spatial_rotational_killing_vector);     \\\n  template void rotational_shift_stress(                                     \\\n      gsl::not_null<tnsr::II<DTYPE(data), 3>*> result,                       \\\n      const tnsr::I<DTYPE(data), 3>& rotational_shift,                       \\\n      const Scalar<DTYPE(data)>& lapse);                                     \\\n  template tnsr::II<DTYPE(data), 3> rotational_shift_stress(                 \\\n      const tnsr::I<DTYPE(data), 3>& rotational_shift,                       \\\n      const Scalar<DTYPE(data)>& lapse);                                     \\\n  template void derivative_rotational_shift_over_lapse_squared(              \\\n      gsl::not_null<tnsr::iJ<DTYPE(data), 3>*> result,                       \\\n      const tnsr::I<DTYPE(data), 3>& rotational_shift,                       \\\n      const tnsr::iJ<DTYPE(data), 3>& deriv_of_shift,                        \\\n      const Scalar<DTYPE(data)>& lapse,                                      \\\n      const tnsr::i<DTYPE(data), 3>& deriv_of_lapse,                         \\\n      const tnsr::iJ<DTYPE(data), 3>&                                        \\\n          deriv_of_spatial_rotational_killing_vector);                       \\\n  template tnsr::iJ<DTYPE(data), 3>                                          \\\n  derivative_rotational_shift_over_lapse_squared(                            \\\n      const tnsr::I<DTYPE(data), 3>& rotational_shift,                       \\\n      const tnsr::iJ<DTYPE(data), 3>& deriv_of_shift,                        \\\n      const Scalar<DTYPE(data)>& lapse,                                      \\\n      const tnsr::i<DTYPE(data), 3>& deriv_of_lapse,                         \\\n      const tnsr::iJ<DTYPE(data), 3>&                                        \\\n          deriv_of_spatial_rotational_killing_vector);                       \\\n  template void specific_enthalpy_squared(                                   \\\n      gsl::not_null<Scalar<DTYPE(data)>*> result,                            \\\n      const tnsr::I<DTYPE(data), 3>& rotational_shift,                       \\\n      const Scalar<DTYPE(data)>& lapse,                                      \\\n      const tnsr::i<DTYPE(data), 3>& velocity_potential_gradient,            \\\n      const tnsr::II<DTYPE(data), 3>& inverse_spatial_metric,                \\\n      double euler_enthalpy_constant);                                       \\\n  template Scalar<DTYPE(data)> specific_enthalpy_squared(                    \\\n      const tnsr::I<DTYPE(data), 3>& rotational_shift,                       \\\n      const Scalar<DTYPE(data)>& lapse,                                      \\\n      const tnsr::i<DTYPE(data), 3>& velocity_potential_gradient,            \\\n      const tnsr::II<DTYPE(data), 3>& inverse_spatial_metric,                \\\n      double euler_enthalpy_constant);                                       \\\n  template void spatial_rotational_killing_vector(                           \\\n      gsl::not_null<tnsr::I<DTYPE(data), 3>*> result,                        \\\n      const tnsr::I<DTYPE(data), 3>& x, double orbital_angular_velocity,     \\\n      const Scalar<DTYPE(data)>& determinant_spatial_metric);                \\\n  template tnsr::I<DTYPE(data), 3> spatial_rotational_killing_vector(        \\\n      const tnsr::I<DTYPE(data), 3>& x, double orbital_angular_velocity,     \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric);                   \\\n  template void divergence_spatial_rotational_killing_vector(                \\\n      gsl::not_null<Scalar<DTYPE(data)>*> result,                            \\\n      const tnsr::I<DTYPE(data), 3>& x, double orbital_angular_velocity,     \\\n      const Scalar<DTYPE(data)>& determinant_spatial_metric);                \\\n  template Scalar<DTYPE(data)> divergence_spatial_rotational_killing_vector( \\\n      const tnsr::I<DTYPE(data), 3>& x, double orbital_angular_velocity,     \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATION\n\n}  // namespace hydro::initial_data::irrotational_bns\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/InitialData/IrrotationalBns.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/*!\n * \\brief Items related to solving for irrotational bns initial data\n * See e.g. \\cite BaumgarteShapiro Ch. 15 (P. 523)\n */\nnamespace hydro::initial_data::irrotational_bns {\n/// @{\n/// \\brief Compute the  shift plus a spatial vector \\f$ k^i\\f$ representing\n/// the local binary rotation \\f$B^i = \\beta^i + k^i\\f$\n///\ntemplate <typename DataType>\nvoid rotational_shift(\n    gsl::not_null<tnsr::I<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& shift,\n    const tnsr::I<DataType, 3>& spatial_rotational_killing_vector);\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> rotational_shift(\n    const tnsr::I<DataType, 3>& shift,\n    const tnsr::I<DataType, 3>& spatial_rotational_killing_vector);\n/// @}\n\n/// @{\n/// \\brief Compute the  stress-energy corresponding to the rotation shift.\n/// (this has no corresponding equation number in \\cite BaumgarteShapiro, it\n/// is defined for convenience in evaluating fluxes and sources for the DG\n/// scheme.)\n///\n///\n/// \\f[\\Sigma^{ij} = \\frac{B^iB^j}{\\alpha^2}\\f]\n///\ntemplate <typename DataType>\nvoid rotational_shift_stress(gsl::not_null<tnsr::II<DataType, 3>*> result,\n                             const tnsr::I<DataType, 3>& rotational_shift,\n                             const Scalar<DataType>& lapse);\ntemplate <typename DataType>\ntnsr::II<DataType, 3> rotational_shift_stress(\n    const tnsr::I<DataType, 3>& rotational_shift,\n    const Scalar<DataType>& lapse);\n/// @}\n\n/// @{\n/// \\brief  Compute derivative  \\f$ \\partial_i (B^j / \\alpha^2) \\f$\n///\n/// Here \\f$ \\partial_i \\f$ is the spatial partial derivative, \\f$ \\alpha\\f$ is\n/// the lapse and \\f$B^i\\f$ the rotational shift).  The derivatives passed as\n/// arguments should be spatial partial derivatives.\ntemplate <typename DataType>\nvoid derivative_rotational_shift_over_lapse_squared(\n    gsl::not_null<tnsr::iJ<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& rotational_shift,\n    const tnsr::iJ<DataType, 3>& deriv_of_shift, const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, 3>& deriv_of_lapse,\n    const tnsr::iJ<DataType, 3>& deriv_of_spatial_rotational_killing_vector);\ntemplate <typename DataType>\ntnsr::iJ<DataType, 3> derivative_rotational_shift_over_lapse_squared(\n    const tnsr::I<DataType, 3>& rotational_shift,\n    const tnsr::iJ<DataType, 3>& deriv_of_shift, const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, 3>& deriv_of_lapse,\n    const tnsr::iJ<DataType, 3>& deriv_of_spatial_rotational_killing_vector);\n/// @}\n\n/// @{\n/// \\brief Compute the specific enthalpy squared from other hydro variables and\n/// the spacetime\n///\n/// The eqn. is identical in content to \\cite BaumgarteShapiro 15.76, it\n/// computes the specific enthalpy \\f$ h \\f$\n/*!\n   \\f[\n   h^2 = \\frac{1}{\\alpha^2} \\left(C + B^i D_i \\Phi\\right)^2 - D_i\n   \\Phi D^i \\Phi\n   \\f]\n*/\n/// Where \\f$\\Phi \\f$ is the velocity potential, and \\f$C\\f$ is the\n/// Euler-constant, which in a slowly rotating, slowly orbiting configuration\n/// becomes the central specific enthalpy times the central lapse\ntemplate <typename DataType>\nvoid specific_enthalpy_squared(\n    gsl::not_null<Scalar<DataType>*> result,\n    const tnsr::I<DataType, 3>& rotational_shift, const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, 3>& velocity_potential_gradient,\n    const tnsr::II<DataType, 3>& inverse_spatial_metric,\n    double euler_enthalpy_constant);\ntemplate <typename DataType>\nScalar<DataType> specific_enthalpy_squared(\n    const tnsr::I<DataType, 3>& rotational_shift, const Scalar<DataType>& lapse,\n    const tnsr::i<DataType, 3>& velocity_potential_gradient,\n    const tnsr::II<DataType, 3>& inverse_spatial_metric,\n    double euler_enthalpy_constant);\n/// @}\n\n/// @{\n/// \\brief Compute the spatial rotational killing vector associated with uniform\n/// rotation around the z-axis.\n///\n/// Taking \\f$\\Omega_j\\f$ to be the uniform rotation axis (assumed in the\n/// z-direction) and \\f$ \\epsilon^{ijk}\\f$ to be the Levi-Civita tensor\n/// (\\f$\\epsilon_{ijk} = \\sqrt{\\gamma} e_{ijk}\\f$, with \\f$e_{ijk}\\f$ totally\n/// antisymmetric with \\f$ e_{123} = 1\\f$) , then\n/// the killing vector is given by  (\\cite BaumgarteShapiro 15.13) :\n/*!\n  \\f[ k^i = \\epsilon^{ijk}\\Omega_j x_k \\f]\n */\ntemplate <typename DataType>\nvoid spatial_rotational_killing_vector(\n    gsl::not_null<tnsr::I<DataType, 3>*> result, const tnsr::I<DataType, 3>& x,\n    double orbital_angular_velocity,\n    const Scalar<DataType>& sqrt_det_spatial_metric);\ntemplate <typename DataType>\ntnsr::I<DataType, 3> spatial_rotational_killing_vector(\n    const tnsr::I<DataType, 3>& x, double orbital_angular_velocity,\n    const Scalar<DataType>& sqrt_det_spatial_metric);\n/// @}\n\n/// @{\n/// \\brief The spatial derivative of the spatial rotational killing vector\n///\n/// As for `spatial_rotational_killing_vector`, assumes uniform rotation around\n/// the z-axis\ntemplate <typename DataType>\nvoid divergence_spatial_rotational_killing_vector(\n    gsl::not_null<Scalar<DataType>*> result, const tnsr::I<DataType, 3>& x,\n    double orbital_angular_velocity,\n    const Scalar<DataType>& sqrt_det_spatial_metric);\ntemplate <typename DataType>\nScalar<DataType> divergence_spatial_rotational_killing_vector(\n    const tnsr::I<DataType, 3>& x, double orbital_angular_velocity,\n    const Scalar<DataType>& sqrt_det_spatial_metric);\n}  // namespace hydro::initial_data::irrotational_bns\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/InversePlasmaBeta.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/InversePlasmaBeta.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace hydro {\ntemplate <typename DataType>\nvoid inverse_plasma_beta(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& comoving_magnetic_field_magnitude,\n    const Scalar<DataType>& fluid_pressure) {\n  get(*result) =\n      0.5*square(get(comoving_magnetic_field_magnitude))/get(fluid_pressure);\n}\n\ntemplate <typename DataType>\nScalar<DataType> inverse_plasma_beta(\n    const Scalar<DataType>& comoving_magnetic_field_magnitude,\n    const Scalar<DataType>& fluid_pressure) {\n  Scalar<DataType> result{};\n  inverse_plasma_beta(make_not_null(&result), comoving_magnetic_field_magnitude,\n                      fluid_pressure);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                      \\\n  template void inverse_plasma_beta(                              \\\n      gsl::not_null<Scalar<DTYPE(data)>*> result,                 \\\n      const Scalar<DTYPE(data)>& comoving_magnetic_field_magnitude, \\\n      const Scalar<DTYPE(data)>& fluid_pressure);                 \\\n  template Scalar<DTYPE(data)> inverse_plasma_beta(               \\\n      const Scalar<DTYPE(data)>& comoving_magnetic_field_magnitude, \\\n      const Scalar<DTYPE(data)>& fluid_pressure);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef INSTANTIATE\n#undef DTYPE\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/InversePlasmaBeta.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n///// \\endcond\n\nnamespace hydro {\n/// @{\n/*!\n * \\brief Computes the inverse plasma beta\n *\n * The inverse plasma beta \\f$\\beta^{-1} = b^2 / (2 p)\\f$, where\n * \\f$b^2\\f$ is the square of the comoving magnetic field amplitude\n * and \\f$p\\f$ is the fluid pressure.\n */\ntemplate <typename DataType>\nvoid inverse_plasma_beta(\n    gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& comoving_magnetic_field_magnitude,\n    const Scalar<DataType>& fluid_pressure);\n\ntemplate <typename DataType>\nScalar<DataType> inverse_plasma_beta(\n    const Scalar<DataType>& comoving_magnetic_field_magnitude,\n    const Scalar<DataType>& fluid_pressure);\n/// @}\n\nnamespace Tags {\n/// Can be retrieved using `hydro::Tags::InversePlasmaBeta`\ntemplate <typename DataType>\nstruct InversePlasmaBetaCompute : InversePlasmaBeta<DataType>, db::ComputeTag {\n  using base = InversePlasmaBeta<DataType>;\n  using return_type = Scalar<DataType>;\n\n  using argument_tags =\n      tmpl::list<ComovingMagneticFieldMagnitude<DataType>, Pressure<DataType>>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataType>*>, const Scalar<DataType>&,\n      const Scalar<DataType>&)>(&inverse_plasma_beta<DataType>);\n};\n}  // namespace Tags\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/LorentzFactor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace hydro {\ntemplate <typename DataType>\nvoid lorentz_factor(const gsl::not_null<Scalar<DataType>*> result,\n                    const Scalar<DataType>& spatial_velocity_squared) {\n  get(*result) = 1.0 / sqrt(1.0 - get(spatial_velocity_squared));\n}\n\ntemplate <typename DataType>\nScalar<DataType> lorentz_factor(\n    const Scalar<DataType>& spatial_velocity_squared) {\n  Scalar<DataType> result{};\n  lorentz_factor(make_not_null(&result), spatial_velocity_squared);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid lorentz_factor(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n    const tnsr::i<DataType, Dim, Frame>& spatial_velocity_form) {\n  set_number_of_grid_points(result, spatial_velocity);\n  lorentz_factor(result, dot_product(spatial_velocity, spatial_velocity_form));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> lorentz_factor(\n    const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n    const tnsr::i<DataType, Dim, Frame>& spatial_velocity_form) {\n  Scalar<DataType> result{};\n  lorentz_factor(make_not_null(&result), spatial_velocity,\n                 spatial_velocity_form);\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template void lorentz_factor(                                             \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result,                     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& spatial_velocity, \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          spatial_velocity_form);                                           \\\n  template Scalar<DTYPE(data)> lorentz_factor(                              \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& spatial_velocity, \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>&                   \\\n          spatial_velocity_form);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\ntemplate void lorentz_factor(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const Scalar<DataVector>& spatial_velocity_squared);\ntemplate Scalar<DataVector> lorentz_factor(\n    const Scalar<DataVector>& spatial_velocity_squared);\ntemplate void lorentz_factor(const gsl::not_null<Scalar<double>*> result,\n                             const Scalar<double>& spatial_velocity_squared);\ntemplate Scalar<double> lorentz_factor(\n    const Scalar<double>& spatial_velocity_squared);\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/LorentzFactor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace hydro {\n/// @{\n/// Computes the Lorentz factor \\f$W=1/\\sqrt{1 - v^i v_i}\\f$\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid lorentz_factor(gsl::not_null<Scalar<DataType>*> result,\n                    const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n                    const tnsr::i<DataType, Dim, Frame>& spatial_velocity_form);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> lorentz_factor(\n    const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n    const tnsr::i<DataType, Dim, Frame>& spatial_velocity_form);\n\ntemplate <typename DataType>\nvoid lorentz_factor(gsl::not_null<Scalar<DataType>*> result,\n                    const Scalar<DataType>& spatial_velocity_squared);\n\ntemplate <typename DataType>\nScalar<DataType> lorentz_factor(\n    const Scalar<DataType>& spatial_velocity_squared);\n/// @}\n\nnamespace Tags {\n/// Compute item for Lorentz factor \\f$W\\f$.\n///\n/// Can be retrieved using `hydro::Tags::LorentzFactor`\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct LorentzFactorCompute : LorentzFactor<DataType>, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpatialVelocity<DataType, Dim, Frame>,\n                 SpatialVelocityOneForm<DataType, Dim, Frame>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataType>*>, const tnsr::I<DataType, Dim, Frame>&,\n      const tnsr::i<DataType, Dim, Frame>&)>(\n      &lorentz_factor<DataType, Dim, Frame>);\n\n  using base = LorentzFactor<DataType>;\n};\n}  // namespace Tags\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/LowerSpatialFourVelocity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/LowerSpatialFourVelocity.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\nnamespace hydro::Tags {\nvoid LowerSpatialFourVelocityCompute::function(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> result,\n    const tnsr::I<DataVector, 3>& spatial_velocity,\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const Scalar<DataVector>& lorentz_factor) {\n  raise_or_lower_index(result, spatial_velocity, spatial_metric);\n  for (size_t d = 0; d < 3; ++d) {\n    result->get(d) *= get(lorentz_factor);\n  }\n}\n\n}  // namespace hydro::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/LowerSpatialFourVelocity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace hydro::Tags {\n\n/*!\n * \\brief Computes $u_i=W \\gamma_{ij} v^j$, where $W$ is the Lorentz factor,\n * $\\gamma_{ij}$ is the spatial metric, and $v^j$ is the spatial velocity.\n *\n */\nstruct LowerSpatialFourVelocityCompute\n    : hydro::Tags::LowerSpatialFourVelocity<DataVector, 3, Frame::Inertial>,\n      db::ComputeTag {\n  using base =\n      hydro::Tags::LowerSpatialFourVelocity<DataVector, 3, Frame::Inertial>;\n  using argument_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>>;\n  static void function(const gsl::not_null<tnsr::i<DataVector, 3>*> result,\n                       const tnsr::I<DataVector, 3>& spatial_velocity,\n                       const tnsr::ii<DataVector, 3>& spatial_metric,\n                       const Scalar<DataVector>& lorentz_factor);\n};\n\n}  // namespace hydro::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/MagneticFieldTreatment.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/MagneticFieldTreatment.hpp\"\n\n#include <ostream>\n#include <string>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace hydro {\nstd::ostream& operator<<(std::ostream& os, const MagneticFieldTreatment t) {\n  switch (t) {\n    case MagneticFieldTreatment::AssumeZero:\n      return os << \"AssumeZero\";\n    case MagneticFieldTreatment::CheckIfZero:\n      return os << \"CheckIfZero\";\n    case MagneticFieldTreatment::AssumeNonZero:\n      return os << \"AssumeNonZero\";\n    default:\n      ERROR(\"Unknown value for MagneticFieldTreatment \" << static_cast<int>(t));\n  };\n}\n}  // namespace hydro\n\ntemplate <>\nhydro::MagneticFieldTreatment\nOptions::create_from_yaml<hydro::MagneticFieldTreatment>::create<void>(\n    const Options::Option& options) {\n  const auto recons_method = options.parse_as<std::string>();\n  if (recons_method == get_output(type::AssumeZero)) {\n    return type::AssumeZero;\n  } else if (recons_method == get_output(type::CheckIfZero)) {\n    return type::CheckIfZero;\n  } else if (recons_method == get_output(type::AssumeNonZero)) {\n    return type::AssumeNonZero;\n  } else {\n    PARSE_ERROR(options.context(),\n                \"MagneticFieldTreatment must be '\"\n                    << get_output(type::AssumeZero) << \"', '\"\n                    << get_output(type::CheckIfZero) << \"', or '\"\n                    << get_output(type::AssumeNonZero) << \"'\");\n  }\n}\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/MagneticFieldTreatment.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\n#include \"Options/Options.hpp\"\n\nnamespace hydro {\n/// \\brief Used to specify how to handle the magnetic field.\nenum MagneticFieldTreatment {\n  /// Assume the magnetic field is zero\n  AssumeZero,\n  /// Check if the magnetic field is zero\n  CheckIfZero,\n  /// Assume the magnetic field is non-zero\n  AssumeNonZero\n};\n\nstd::ostream& operator<<(std::ostream& os, MagneticFieldTreatment t);\n}  // namespace hydro\n\n/// \\cond\ntemplate <>\nstruct Options::create_from_yaml<hydro::MagneticFieldTreatment> {\n  using type = hydro::MagneticFieldTreatment;\n  template <typename Metavariables>\n  static type create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nhydro::MagneticFieldTreatment\nOptions::create_from_yaml<hydro::MagneticFieldTreatment>::create<void>(\n    const Options::Option& options);\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/MassFlux.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"MassFlux.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace hydro {\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid mass_flux(const gsl::not_null<tnsr::I<DataType, Dim, Frame>*> result,\n               const Scalar<DataType>& rest_mass_density,\n               const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n               const Scalar<DataType>& lorentz_factor,\n               const Scalar<DataType>& lapse,\n               const tnsr::I<DataType, Dim, Frame>& shift,\n               const Scalar<DataType>& sqrt_det_spatial_metric) {\n  for (size_t i = 0; i < Dim; ++i) {\n    result->get(i) = get(rest_mass_density) * get(lorentz_factor) *\n                     get(sqrt_det_spatial_metric) *\n                     (get(lapse) * spatial_velocity.get(i) - shift.get(i));\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::I<DataType, Dim, Frame> mass_flux(\n    const Scalar<DataType>& rest_mass_density,\n    const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, Dim, Frame>& shift,\n    const Scalar<DataType>& sqrt_det_spatial_metric) {\n  tnsr::I<DataType, Dim, Frame> result{};\n  mass_flux(make_not_null(&result), rest_mass_density, spatial_velocity,\n            lorentz_factor, lapse, shift, sqrt_det_spatial_metric);\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define INSTANTIATE(_, data)                                                \\\n  template void mass_flux(                                                  \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          result,                                                           \\\n      const Scalar<DTYPE(data)>& rest_mass_density,                         \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& spatial_velocity, \\\n      const Scalar<DTYPE(data)>& lorentz_factor,                            \\\n      const Scalar<DTYPE(data)>& lapse,                                     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,            \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric);                  \\\n  template tnsr::I<DTYPE(data), DIM(data), FRAME(data)> mass_flux(          \\\n      const Scalar<DTYPE(data)>& rest_mass_density,                         \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& spatial_velocity, \\\n      const Scalar<DTYPE(data)>& lorentz_factor,                            \\\n      const Scalar<DTYPE(data)>& lapse,                                     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift,            \\\n      const Scalar<DTYPE(data)>& sqrt_det_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/MassFlux.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace hydro {\n/// @{\n/// Computes the vector \\f$J^i\\f$ in \\f$\\dot{M} = -\\int J^i s_i d^2S\\f$,\n/// representing the mass flux through a surface with normal \\f$s_i\\f$.\n///\n/// Note that the integral is understood\n/// as a flat-space integral: all metric factors are included in \\f$J^i\\f$.\n/// In particular, if the integral is done over a Strahlkorper, the\n/// `gr::surfaces::euclidean_area_element` of the Strahlkorper should be used,\n/// and \\f$s_i\\f$ is\n/// the normal one-form to the Strahlkorper normalized with the flat metric,\n/// \\f$s_is_j\\delta^{ij}=1\\f$.\n///\n/// The formula is\n/// \\f$ J^i = \\rho W \\sqrt{\\gamma}(\\alpha v^i-\\beta^i)\\f$,\n/// where \\f$\\rho\\f$ is the mass density, \\f$W\\f$ is the Lorentz factor,\n/// \\f$v^i\\f$ is the spatial velocity of the fluid,\n/// \\f$\\gamma\\f$ is the determinant of the 3-metric \\f$\\gamma_{ij}\\f$,\n/// \\f$\\alpha\\f$ is the lapse, and \\f$\\beta^i\\f$ is the shift.\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid mass_flux(gsl::not_null<tnsr::I<DataType, Dim, Frame>*> result,\n               const Scalar<DataType>& rest_mass_density,\n               const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n               const Scalar<DataType>& lorentz_factor,\n               const Scalar<DataType>& lapse,\n               const tnsr::I<DataType, Dim, Frame>& shift,\n               const Scalar<DataType>& sqrt_det_spatial_metric);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\ntnsr::I<DataType, Dim, Frame> mass_flux(\n    const Scalar<DataType>& rest_mass_density,\n    const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n    const Scalar<DataType>& lorentz_factor, const Scalar<DataType>& lapse,\n    const tnsr::I<DataType, Dim, Frame>& shift,\n    const Scalar<DataType>& sqrt_det_spatial_metric);\n/// @}\n\nnamespace Tags {\n/// Compute item for mass flux vector \\f$J^i\\f$.\n///\n/// Can be retrieved using `hydro::Tags::MassFlux`\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct MassFluxCompute : MassFlux<DataType, Dim, Frame>,\n                               db::ComputeTag {\n  using argument_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim, Frame>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 ::gr::Tags::Lapse<DataType>,\n                 ::gr::Tags::Shift<DataType, Dim, Frame>,\n                 ::gr::Tags::SqrtDetSpatialMetric<DataType>>;\n\n  using return_type = tnsr::I<DataType, Dim, Frame>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<tnsr::I<DataType, Dim, Frame>*>, const Scalar<DataType>&,\n      const tnsr::I<DataType, Dim, Frame>&, const Scalar<DataType>&,\n      const Scalar<DataType>&, const tnsr::I<DataType, Dim, Frame>&,\n      const Scalar<DataType>&)>(&mass_flux<DataType, Dim, Frame>);\n\n  using base = MassFlux<DataType, Dim, Frame>;\n};\n}  // namespace Tags\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/MassWeightedFluidItems.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"MassWeightedFluidItems.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace hydro {\n\nstd::ostream& operator<<(std::ostream& os, HalfPlaneIntegralMask mask) {\n  switch (mask) {\n    case HalfPlaneIntegralMask::None:\n      return os << \"None\";\n    case HalfPlaneIntegralMask::PositiveXOnly:\n      return os << \"PositiveXOnly\";\n    case HalfPlaneIntegralMask::NegativeXOnly:\n      return os << \"NegativeXOnly\";\n    default:\n      ERROR(\"Unknown HalfPlaneIntegralMask!\");\n  }\n}\n\nstd::string name(const HalfPlaneIntegralMask mask){\n  return MakeString{} << mask;\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid u_lower_t(const gsl::not_null<Scalar<DataType>*> result,\n               const Scalar<DataType>& lorentz_factor,\n               const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n               const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n               const Scalar<DataType>& lapse,\n               const tnsr::I<DataType, Dim, Frame>& shift) {\n  dot_product(result, spatial_velocity, shift, spatial_metric);\n  result->get() = get(lorentz_factor) * (get(lapse) * (-1.0) + result->get());\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> u_lower_t(\n    const Scalar<DataType>& lorentz_factor,\n    const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Frame>& shift) {\n  auto result = make_with_value<Scalar<DataType>>(lorentz_factor, 0.0);\n  u_lower_t(make_not_null(&result), lorentz_factor, spatial_velocity,\n            spatial_metric, lapse, shift);\n  return result;\n}\n\ntemplate <typename DataType>\nvoid mass_weighted_internal_energy(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& tilde_d,\n    const Scalar<DataType>& specific_internal_energy) {\n  result->get() = get(tilde_d) * get(specific_internal_energy);\n}\n\ntemplate <typename DataType>\nScalar<DataType> mass_weighted_internal_energy(\n    const Scalar<DataType>& tilde_d,\n    const Scalar<DataType>& specific_internal_energy) {\n  auto result = make_with_value<Scalar<DataType>>(tilde_d, 0.0);\n  mass_weighted_internal_energy(make_not_null(&result), tilde_d,\n                                specific_internal_energy);\n  return result;\n}\n\ntemplate <typename DataType>\nvoid mass_weighted_kinetic_energy(const gsl::not_null<Scalar<DataType>*> result,\n                                  const Scalar<DataType>& tilde_d,\n                                  const Scalar<DataType>& lorentz_factor) {\n  result->get() = get(tilde_d) * (get(lorentz_factor) - 1.0);\n}\n\ntemplate <typename DataType>\nScalar<DataType> mass_weighted_kinetic_energy(\n    const Scalar<DataType>& tilde_d, const Scalar<DataType>& lorentz_factor) {\n  auto result = make_with_value<Scalar<DataType>>(tilde_d, 0.0);\n  mass_weighted_kinetic_energy(make_not_null(&result), tilde_d, lorentz_factor);\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid tilde_d_unbound_ut_criterion(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& tilde_d, const Scalar<DataType>& lorentz_factor,\n    const tnsr::I<DataType, Dim, Fr>& spatial_velocity,\n    const tnsr::ii<DataType, Dim, Fr>& spatial_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Fr>& shift) {\n  u_lower_t(result, lorentz_factor, spatial_velocity, spatial_metric, lapse,\n            shift);\n  result->get() = get(tilde_d) * step_function(-1.0 - result->get());\n}\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> tilde_d_unbound_ut_criterion(\n    const Scalar<DataType>& tilde_d, const Scalar<DataType>& lorentz_factor,\n    const tnsr::I<DataType, Dim, Fr>& spatial_velocity,\n    const tnsr::ii<DataType, Dim, Fr>& spatial_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Fr>& shift) {\n  auto result = make_with_value<Scalar<DataType>>(tilde_d, 0.0);\n  tilde_d_unbound_ut_criterion(make_not_null(&result), tilde_d, lorentz_factor,\n                               spatial_velocity, spatial_metric, lapse, shift);\n  return result;\n}\n\ntemplate <HalfPlaneIntegralMask IntegralMask, typename DataType, size_t Dim>\nvoid tilde_d_in_half_plane(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords) {\n  get(*result) = get(tilde_d);\n  switch (IntegralMask) {\n    case HalfPlaneIntegralMask::PositiveXOnly:\n      get(*result) *= step_function(get<0>(grid_coords));\n      break;\n    case HalfPlaneIntegralMask::NegativeXOnly:\n      get(*result) *= step_function(get<0>(grid_coords) * (-1.0));\n      break;\n    default:\n      break;\n  }\n}\n\ntemplate <HalfPlaneIntegralMask IntegralMask, typename DataType, size_t Dim>\nScalar<DataType> tilde_d_in_half_plane(\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords) {\n  auto result = make_with_value<Scalar<DataType>>(tilde_d, 0.0);\n  tilde_d_in_half_plane<IntegralMask>(make_not_null(&result), tilde_d,\n                                      grid_coords);\n  return result;\n}\n\ntemplate <HalfPlaneIntegralMask IntegralMask, typename DataType, size_t Dim,\n          typename Fr>\nvoid mass_weighted_coords(\n    const gsl::not_null<tnsr::I<DataType, Dim, Fr>*> result,\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords,\n    const tnsr::I<DataType, Dim, Fr>& compute_coords) {\n  for (size_t i = 0; i < Dim; i++) {\n    result->get(i) = get(tilde_d) * (compute_coords.get(i));\n    switch (IntegralMask) {\n      case HalfPlaneIntegralMask::PositiveXOnly:\n        result->get(i) *= step_function(get<0>(grid_coords));\n        break;\n      case HalfPlaneIntegralMask::NegativeXOnly:\n        result->get(i) *= step_function(get<0>(grid_coords) * (-1.0));\n        break;\n      default:\n        break;\n    }\n  }\n}\n\ntemplate <HalfPlaneIntegralMask IntegralMask, typename DataType, size_t Dim,\n          typename Fr>\ntnsr::I<DataType, Dim, Fr> mass_weighted_coords(\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords,\n    const tnsr::I<DataType, Dim, Fr>& compute_coords) {\n  auto result = make_with_value<tnsr::I<DataType, Dim, Fr>>(tilde_d, 0.0);\n  mass_weighted_coords<IntegralMask>(make_not_null(&result), tilde_d,\n                                     grid_coords, compute_coords);\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template void u_lower_t(                                                     \\\n      const gsl::not_null<Scalar<DataVector>*> result,                         \\\n      const Scalar<DataVector>& lorentz_factor,                                \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& spatial_velocity, \\\n      const tnsr::ii<DataVector, DIM(data), Frame::Inertial>& spatial_metric,  \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift);           \\\n  template Scalar<DataVector> u_lower_t(                                       \\\n      const Scalar<DataVector>& lorentz_factor,                                \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& spatial_velocity, \\\n      const tnsr::ii<DataVector, DIM(data), Frame::Inertial>& spatial_metric,  \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift);           \\\n  template void tilde_d_unbound_ut_criterion(                                  \\\n      const gsl::not_null<Scalar<DataVector>*> result,                         \\\n      const Scalar<DataVector>& tilde_d,                                       \\\n      const Scalar<DataVector>& lorentz_factor,                                \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& spatial_velocity, \\\n      const tnsr::ii<DataVector, DIM(data), Frame::Inertial>& spatial_metric,  \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift);           \\\n  template Scalar<DataVector> tilde_d_unbound_ut_criterion(                    \\\n      const Scalar<DataVector>& tilde_d,                                       \\\n      const Scalar<DataVector>& lorentz_factor,                                \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& spatial_velocity, \\\n      const tnsr::ii<DataVector, DIM(data), Frame::Inertial>& spatial_metric,  \\\n      const Scalar<DataVector>& lapse,                                         \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& shift);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define HALFPLANE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template void mass_weighted_coords<HALFPLANE(data)>(                      \\\n      const gsl::not_null<tnsr::I<DataVector, DIM(data), Frame::Inertial>*> \\\n          result,                                                           \\\n      const Scalar<DataVector>& tilde_d,                                    \\\n      const tnsr::I<DataVector, DIM(data), Frame::Grid>& dg_grid_coords,    \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& dg_coords);    \\\n  template tnsr::I<DataVector, DIM(data), Frame::Inertial>                  \\\n  mass_weighted_coords<HALFPLANE(data)>(                                    \\\n      const Scalar<DataVector>& tilde_d,                                    \\\n      const tnsr::I<DataVector, DIM(data), Frame::Grid>& dg_grid_coords,    \\\n      const tnsr::I<DataVector, DIM(data), Frame::Inertial>& dg_coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3),\n                        (HalfPlaneIntegralMask::None,\n                         HalfPlaneIntegralMask::PositiveXOnly,\n                         HalfPlaneIntegralMask::NegativeXOnly))\n\n#undef DIM\n#undef OBJECT\n#undef INSTANTIATE\n\n// For tilde_d_in_half_plane, we require limiting the integrand to a half\n// plane -> Do not instantiate the function for None\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define HALFPLANE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void tilde_d_in_half_plane<HALFPLANE(data)>(                   \\\n      const gsl::not_null<Scalar<DataVector>*> result,                    \\\n      const Scalar<DataVector>& tilde_d,                                  \\\n      const tnsr::I<DataVector, DIM(data), Frame::Grid>& dg_grid_coords); \\\n  template Scalar<DataVector> tilde_d_in_half_plane<HALFPLANE(data)>(     \\\n      const Scalar<DataVector>& tilde_d,                                  \\\n      const tnsr::I<DataVector, DIM(data), Frame::Grid>& dg_grid_coords);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3),\n                        (HalfPlaneIntegralMask::PositiveXOnly,\n                         HalfPlaneIntegralMask::NegativeXOnly))\n\n#undef DIM\n#undef OBJECT\n#undef INSTANTIATE\n\ntemplate void mass_weighted_internal_energy(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const Scalar<DataVector>& tilde_d,\n    const Scalar<DataVector>& specific_internal_energy);\ntemplate Scalar<DataVector> mass_weighted_internal_energy(\n    const Scalar<DataVector>& tilde_d,\n    const Scalar<DataVector>& specific_internal_energy);\ntemplate void mass_weighted_kinetic_energy(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const Scalar<DataVector>& tilde_d,\n    const Scalar<DataVector>& lorentz_factor);\ntemplate Scalar<DataVector> mass_weighted_kinetic_energy(\n    const Scalar<DataVector>& tilde_d,\n    const Scalar<DataVector>& lorentz_factor);\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/MassWeightedFluidItems.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace hydro {\n\nenum class HalfPlaneIntegralMask { None, PositiveXOnly, NegativeXOnly };\n\nstd::ostream& operator<<(std::ostream& os, HalfPlaneIntegralMask mask);\n\nstd::string name(HalfPlaneIntegralMask mask);\n\n/// Tag containing TildeD * SpecificInternalEnergy\n/// Useful as a diagnostics tool, as input to volume\n/// integral.\nnamespace Tags {\ntemplate <typename DataType>\nstruct MassWeightedInternalEnergy : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// Tag containing TildeD * (LorentzFactor - 1.0)\n/// Useful as a diagnostics tool, as input to volume\n/// integral.\ntemplate <typename DataType>\nstruct MassWeightedKineticEnergy : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// Contains TildeD restricted to regions marked as\n/// unbound, using the u_t < -1 criterion.\ntemplate <typename DataType>\nstruct TildeDUnboundUtCriterion : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// Contains TildeD restricted to x>0 or x<0. This provides the\n/// normalization factor for integrals over the half plane\n/// weighted by tildeD\ntemplate <typename DataType, HalfPlaneIntegralMask IntegralMask>\nstruct TildeDInHalfPlane : db::SimpleTag {\n  using type = Scalar<DataType>;\n  static std::string name() {\n    return \"TildeDMask(\" + ::hydro::name(IntegralMask) + \")\";\n  }\n};\n\n/// Contains TildeD * (coordinates in frame Fr).\n/// IntegralMask allows us to restrict the data to the x>0 or x<0\n/// plane in grid coordinates (useful for NSNS).\ntemplate <typename DataType, size_t Dim, HalfPlaneIntegralMask IntegralMask,\n          typename Fr = Frame::Inertial>\nstruct MassWeightedCoords : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Fr>;\n  static std::string name() {\n    return \"MassWeightedCoordsMask(\" + ::hydro::name(IntegralMask) + \")\";\n  }\n};\n}  // namespace Tags\n\n/// @{\n/// Compute $u_t=$\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid u_lower_t(gsl::not_null<Scalar<DataType>*> result,\n               const Scalar<DataType>& lorentz_factor,\n               const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n               const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n               const Scalar<DataType>& lapse,\n               const tnsr::I<DataType, Dim, Frame>& shift);\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nScalar<DataType> u_lower_t(\n    const Scalar<DataType>& lorentz_factor,\n    const tnsr::I<DataType, Dim, Frame>& spatial_velocity,\n    const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Frame>& shift);\n/// @}\n\n/// @{\n/// Compute tilde_d * specific_internal_energy\n/// Result of the calculation stored in result.\ntemplate <typename DataType>\nvoid mass_weighted_internal_energy(\n    gsl::not_null<Scalar<DataType>*> result, const Scalar<DataType>& tilde_d,\n    const Scalar<DataType>& specific_internal_energy);\n\ntemplate <typename DataType>\nScalar<DataType> mass_weighted_internal_energy(\n    const Scalar<DataType>& tilde_d,\n    const Scalar<DataType>& specific_internal_energy);\n/// @}\n\n/// @{\n/// Compute tilde_d * (lorentz_factor - 1.0)\n/// Result of the calculation stored in result.\ntemplate <typename DataType>\nvoid mass_weighted_kinetic_energy(gsl::not_null<Scalar<DataType>*> result,\n                                  const Scalar<DataType>& tilde_d,\n                                  const Scalar<DataType>& lorentz_factor);\ntemplate <typename DataType>\nScalar<DataType> mass_weighted_kinetic_energy(\n    const Scalar<DataType>& tilde_d, const Scalar<DataType>& lorentz_factor);\n/// @}\n\n/// @{\n/// Returns tilde_d in regions where u_t < -1 and 0 in regions where\n/// u_t > -1 (approximate criteria for unbound matter, theoretically\n/// valid for particles following geodesics of a time-independent metric).\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nvoid tilde_d_unbound_ut_criterion(\n    gsl::not_null<Scalar<DataType>*> result, const Scalar<DataType>& tilde_d,\n    const Scalar<DataType>& lorentz_factor,\n    const tnsr::I<DataType, Dim, Fr>& spatial_velocity,\n    const tnsr::ii<DataType, Dim, Fr>& spatial_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Fr>& shift);\n\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nScalar<DataType> tilde_d_unbound_ut_criterion(\n    const Scalar<DataType>& tilde_d, const Scalar<DataType>& lorentz_factor,\n    const tnsr::I<DataType, Dim, Fr>& spatial_velocity,\n    const tnsr::ii<DataType, Dim, Fr>& spatial_metric,\n    const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Fr>& shift);\n/// @}\n\n/// @{\n/// Returns tilde_d in one half plane and zero in the other\n/// IntegralMask allows us to restrict the data to the x>0 or x<0\n/// plane in grid coordinates (useful for NSNS).\ntemplate <HalfPlaneIntegralMask IntegralMask, typename DataType, size_t Dim>\nvoid tilde_d_in_half_plane(\n    gsl::not_null<Scalar<DataType>*> result, const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords);\n\ntemplate <HalfPlaneIntegralMask IntegralMask, typename DataType, size_t Dim>\nScalar<DataType> tilde_d_in_half_plane(\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords);\n/// @}\n\n/// @{\n/// Returns tilde_d * compute_coords\n/// IntegralMask allows us to restrict the data to the x>0 or x<0\n/// plane in grid coordinates (useful for NSNS).\ntemplate <HalfPlaneIntegralMask IntegralMask, typename DataType, size_t Dim,\n          typename Fr = Frame::Inertial>\nvoid mass_weighted_coords(\n    gsl::not_null<tnsr::I<DataType, Dim, Fr>*> result,\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords,\n    const tnsr::I<DataType, Dim, Fr>& compute_coords);\n\ntemplate <HalfPlaneIntegralMask IntegralMask, typename DataType, size_t Dim,\n          typename Fr = Frame::Inertial>\ntnsr::I<DataType, Dim, Fr> mass_weighted_coords(\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords,\n    const tnsr::I<DataType, Dim, Fr>& compute_coords);\n/// @}\n\nnamespace Tags {\n/// Compute item for mass-weighted internal energy\n///\n/// Can be retrieved using `hydro::Tags::MassWeightedInternalEnergy'\ntemplate <typename DataType>\nstruct MassWeightedInternalEnergyCompute : MassWeightedInternalEnergy<DataType>,\n                                           db::ComputeTag {\n  using argument_tags = tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                                   SpecificInternalEnergy<DataType>>;\n\n  using return_type = Scalar<DataType>;\n\n  using base = MassWeightedInternalEnergy<DataType>;\n\n  static constexpr auto function =\n      static_cast<void (*)(const gsl::not_null<Scalar<DataType>*> result,\n                           const Scalar<DataType>& tilde_d,\n                           const Scalar<DataType>& specific_internal_energy)>(\n          &mass_weighted_internal_energy<DataType>);\n};\n\n/// Compute item for mass-weighted internal energy\n///\n/// Can be retrieved using `hydro::Tags::MassWeightedKineticEnergy'\ntemplate <typename DataType>\nstruct MassWeightedKineticEnergyCompute : MassWeightedKineticEnergy<DataType>,\n                                          db::ComputeTag {\n  using argument_tags = tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                                   LorentzFactor<DataType>>;\n\n  using return_type = Scalar<DataType>;\n\n  using base = MassWeightedKineticEnergy<DataType>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<Scalar<DataType>*> result,\n      const Scalar<DataType>& tilde_d, const Scalar<DataType>& lorentz_factor)>(\n      &mass_weighted_kinetic_energy<DataType>);\n};\n\n/// Compute item for TildeD limited to unbound material (u_t<-1 criteria)\n///\n/// Can be retrieved using `hydro::Tags::TildeDUnboundUtCriterion'\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct TildeDUnboundUtCriterionCompute : TildeDUnboundUtCriterion<DataType>,\n                                         db::ComputeTag {\n  using argument_tags =\n      tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD, LorentzFactor<DataType>,\n                 SpatialVelocity<DataType, Dim, Fr>,\n                 gr::Tags::SpatialMetric<DataType, Dim, Fr>,\n                 gr::Tags::Lapse<DataType>, gr::Tags::Shift<DataType, Dim, Fr>>;\n\n  using return_type = Scalar<DataType>;\n\n  using base = TildeDUnboundUtCriterion<DataType>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<Scalar<DataType>*> result,\n      const Scalar<DataType>& tilde_d, const Scalar<DataType>& lorentz_factor,\n      const tnsr::I<DataType, Dim, Fr>& spatial_velocity,\n      const tnsr::ii<DataType, Dim, Fr>& spacial_metric,\n      const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Fr>& shift)>(\n      &tilde_d_unbound_ut_criterion<DataType, Dim, Fr>);\n};\n\n/// Compute tag for TildeD limited to the x>0 or x<0 half plane\ntemplate <typename DataType, size_t Dim, HalfPlaneIntegralMask IntegralMask,\n          typename GridCoordsTag>\nstruct TildeDInHalfPlaneCompute : TildeDInHalfPlane<DataType, IntegralMask>,\n                                  db::ComputeTag {\n  using argument_tags =\n      tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD, GridCoordsTag>;\n\n  using return_type = Scalar<DataType>;\n\n  using base = TildeDInHalfPlane<DataType, IntegralMask>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<Scalar<DataType>*> result,\n      const Scalar<DataType>& tilde_d,\n      const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords)>(\n        &tilde_d_in_half_plane<IntegralMask, DataType, Dim>);\n};\n\n/// Compute item for TildeD * (coordinates in frame Fr).\n/// IntegralMask allows us to restrict the data to the x>0 or x<0\n/// plane in grid coordinates (useful for NSNS).\n///\n/// Can be retrieved using `hydro::Tags::MassWeightedCoords'\ntemplate <typename DataType, size_t Dim, HalfPlaneIntegralMask IntegralMask,\n          typename GridCoordsTag, typename OutputCoordsTag,\n          typename Fr = Frame::Inertial>\nstruct MassWeightedCoordsCompute : MassWeightedCoords<DataType, Dim,\n                                   IntegralMask>, db::ComputeTag {\n  using argument_tags = tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                                   GridCoordsTag, OutputCoordsTag>;\n\n  using return_type = tnsr::I<DataType, Dim, Fr>;\n\n  using base = MassWeightedCoords<DataType, Dim, IntegralMask, Fr>;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::I<DataType, Dim, Fr>*> result,\n      const Scalar<DataType>& tilde_d,\n      const tnsr::I<DataType, Dim, Frame::Grid>& grid_coords,\n      const tnsr::I<DataType, Dim, Fr>& compute_coords)>(\n      &mass_weighted_coords<IntegralMask, DataType, Dim, Fr>);\n};\n}  // namespace Tags\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n#include <pybind11/pybind11.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/ComovingMagneticField.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/LowerSpatialFourVelocity.hpp\"\n#include \"PointwiseFunctions/Hydro/MassFlux.hpp\"\n#include \"PointwiseFunctions/Hydro/MassWeightedFluidItems.hpp\"\n#include \"PointwiseFunctions/Hydro/SoundSpeedSquared.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/StressEnergy.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace {\ntemplate <typename DataType>\nvoid bind_comoving_magnetic_field_impl(py::module& m) {\n  // Wrapper for calculating Co-moving magnetic fields\n  m.def(\"comoving_magnetic_field\",\n        static_cast<tnsr::A<DataType, 3> (*)(\n            const tnsr::I<DataType, 3>&, const tnsr::I<DataType, 3>&,\n            const Scalar<DataType>&, const Scalar<DataType>&,\n            const tnsr::I<DataType, 3>&, const Scalar<DataType>&)>(\n            &hydro::comoving_magnetic_field<DataType>),\n        py::arg(\"spatial_velocity\"),\n        py::arg(\"magnetic_field\"),\n        py::arg(\"magnetic_field_dot_spatial_velocity\"),\n        py::arg(\"lorentz_factor\"), py::arg(\"shift\"), py::arg(\"lapse\"));\n  m.def(\"comoving_magnetic_field_one_form\",\n        static_cast<tnsr::a<DataType, 3> (*)(\n            const tnsr::i<DataType, 3>&, const tnsr::i<DataType, 3>&,\n            const Scalar<DataType>&, const Scalar<DataType>&,\n            const tnsr::I<DataType, 3>&, const Scalar<DataType>&)>(\n            &hydro::comoving_magnetic_field_one_form<DataType>),\n        py::arg(\"spatial_velocity_one_form\"),\n        py::arg(\"magnetic_field_one_form\"),\n        py::arg(\"magnetic_field_dot_spatial_velocity\"),\n        py::arg(\"lorentz_factor\"), py::arg(\"shift\"), py::arg(\"lapse\"));\n  m.def(\"comoving_magnetic_field_squared\",\n        static_cast<Scalar<DataType> (*)(const Scalar<DataType>&,\n                                         const Scalar<DataType>&,\n                                         const Scalar<DataType>&)>(\n            &hydro::comoving_magnetic_field_squared<DataType>),\n        py::arg(\"magnetic_field_squared\"),\n        py::arg(\"magnetic_field_dot_spatial_velocity\"),\n        py::arg(\"lorentz_factor\"));\n}\n\ntemplate <typename DataType>\nvoid bind_lorentz_factor_impl(py::module& m) {\n  m.def(\"lorentz_factor\",\n        static_cast<Scalar<DataType> (*)(const Scalar<DataType>&)>(\n            &hydro::lorentz_factor<DataType>),\n        py::arg(\"spatial_velocity_squared\"));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid bind_lorentz_factor_impl(py::module& m) {\n  m.def(\"lorentz_factor\",\n        static_cast<Scalar<DataType> (*)(const tnsr::I<DataType, Dim, Frame>&,\n                                         const tnsr::i<DataType, Dim, Frame>&)>(\n            &hydro::lorentz_factor<DataType, Dim, Frame>),\n        py::arg(\"spatial_velocity\"), py::arg(\"spatial_velocity_form\"));\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid bind_mass_flux_impl(py::module& m) {\n  m.def(\"mass_flux\",\n        static_cast<tnsr::I<DataType, Dim, Frame> (*)(\n            const Scalar<DataType>&, const tnsr::I<DataType, Dim, Frame>&,\n            const Scalar<DataType>&, const Scalar<DataType>&,\n            const tnsr::I<DataType, Dim, Frame>&, const Scalar<DataType>&)>(\n            &hydro::mass_flux<DataType, Dim, Frame>),\n        py::arg(\"rest_mass_density\"), py::arg(\"spatial_velocity\"),\n        py::arg(\"lorentz_factor\"), py::arg(\"lapse\"), py::arg(\"shift\"),\n        py::arg(\"sqrt_det_spatial_metric\"));\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid bind_mass_weighted_impl(py::module& m) {\n  m.def(\n      \"u_lower_t\",\n      [](const Scalar<DataType>& lorentz_factor,\n         const tnsr::I<DataType, Dim>& spatial_velocity,\n         const tnsr::ii<DataType, Dim>& spatial_metric,\n         const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim>& shift) {\n        return hydro::u_lower_t(lorentz_factor, spatial_velocity,\n                                spatial_metric, lapse, shift);\n      },\n      py::arg(\"lorentz_factor\"), py::arg(\"spatial_velocity\"),\n      py::arg(\"spatial_metric\"), py::arg(\"lapse\"), py::arg(\"shift\"));\n  m.def(\n      \"tilde_d_unbound_ut_criterion\",\n      [](const Scalar<DataType>& tilde_d,\n         const Scalar<DataType>& lorentz_factor,\n         const tnsr::I<DataType, Dim>& spatial_velocity,\n         const tnsr::ii<DataType, Dim>& spatial_metric,\n         const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim>& shift) {\n        return hydro::tilde_d_unbound_ut_criterion(\n            tilde_d, lorentz_factor, spatial_velocity, spatial_metric, lapse,\n            shift);\n      },\n      py::arg(\"tilde_d\"), py::arg(\"lorentz_factor\"),\n      py::arg(\"spatial_velocity\"), py::arg(\"spatial_metric\"), py::arg(\"lapse\"),\n      py::arg(\"shift\"));\n\n  if constexpr (Dim == 1) {\n    m.def(\n        \"mass_weighted_internal_energy\",\n        [](const Scalar<DataType>& tilde_d,\n           const Scalar<DataType>& specific_internal_energy) {\n          return hydro::mass_weighted_internal_energy(tilde_d,\n                                                      specific_internal_energy);\n        },\n        py::arg(\"tilde_d\"), py::arg(\"specific_internal_energy\"));\n    m.def(\n        \"mass_weighted_kinetic_energy\",\n        [](const Scalar<DataType>& tilde_d,\n           const Scalar<DataType>& lorentz_factor) {\n          return hydro::mass_weighted_kinetic_energy(tilde_d, lorentz_factor);\n        },\n        py::arg(\"tilde_d\"), py::arg(\"lorentz_factor\"));\n  }\n}\n\ntemplate <typename DataType, size_t ThermodynamicDim>\nvoid bind_sound_speed_impl(py::module& m) {\n  m.def(\"sound_speed_squared\",\n        static_cast<Scalar<DataType> (*)(\n            const Scalar<DataType>&, const Scalar<DataType>&,\n            const Scalar<DataType>&,\n            const EquationsOfState::EquationOfState<true, ThermodynamicDim>&)>(\n            &hydro::sound_speed_squared<DataType, ThermodynamicDim>),\n        py::arg(\"rest_mass_density\"), py::arg(\"specific_internal_energy\"),\n        py::arg(\"specific_enthalpy\"), py::arg(\"equation_of_state\"));\n}\n\ntemplate <typename DataType>\nvoid bind_specific_enthalpy_impl(py::module& m) {\n  m.def(\"relativistic_specific_enthalpy\",\n        static_cast<Scalar<DataType> (*)(const Scalar<DataType>&,\n                                         const Scalar<DataType>&,\n                                         const Scalar<DataType>&)>(\n            &hydro::relativistic_specific_enthalpy<DataType>),\n        py::arg(\"rest_mass_density\"), py::arg(\"specific_internal_energy\"),\n        py::arg(\"pressure\"));\n}\n\ntemplate <typename DataType>\nvoid bind_stress_energy_impl(py::module& m) {\n  m.def(\"energy_density\", &hydro::energy_density<DataType>, py::arg(\"result\"),\n        py::arg(\"rest_mass_density\"), py::arg(\"specific_enthalpy\"),\n        py::arg(\"pressure\"), py::arg(\"lorentz_factor\"),\n        py::arg(\"magnetic_field_dot_spatial_velocity\"),\n        py::arg(\"comoving_magnetic_field_squared\"));\n  m.def(\"momentum_density\", &hydro::momentum_density<DataType>,\n        py::arg(\"result\"), py::arg(\"rest_mass_density\"),\n        py::arg(\"specific_enthalpy\"), py::arg(\"spatial_velocity\"),\n        py::arg(\"lorentz_factor\"), py::arg(\"magnetic_field\"),\n        py::arg(\"magnetic_field_dot_spatial_velocity\"),\n        py::arg(\"comoving_magnetic_field_squared\"));\n  m.def(\"stress_trace\", &hydro::stress_trace<DataType>, py::arg(\"result\"),\n        py::arg(\"rest_mass_density\"), py::arg(\"specific_enthalpy\"),\n        py::arg(\"pressure\"), py::arg(\"spatial_velocity_squared\"),\n        py::arg(\"lorentz_factor\"),\n        py::arg(\"magnetic_field_dot_spatial_velocity\"),\n        py::arg(\"comoving_magnetic_field_squared\"));\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid bind_impl(py::module& m) {\n  bind_mass_flux_impl<DataType, Dim, Frame::Grid>(m);\n  bind_mass_flux_impl<DataType, Dim, Frame::Inertial>(m);\n  bind_lorentz_factor_impl<DataType, Dim, Frame::Inertial>(m);\n  if constexpr (std::is_same_v<DataType, DataVector>) {\n    bind_mass_weighted_impl<DataType, Dim>(m);\n  }\n}\n\ntemplate <typename DataType>\nvoid bind_impl(py::module& m) {\n  bind_impl<DataType, 1>(m);\n  bind_impl<DataType, 2>(m);\n  bind_impl<DataType, 3>(m);\n\n  bind_comoving_magnetic_field_impl<DataType>(m);\n  bind_lorentz_factor_impl<DataType>(m);\n  // Here the dims are the thermodynamic dims, not spatial dim.\n  bind_sound_speed_impl<DataType, 1>(m);\n  bind_sound_speed_impl<DataType, 2>(m);\n\n  bind_specific_enthalpy_impl<DataType>(m);\n  bind_stress_energy_impl<DataType>(m);\n}\n\n}  // namespace\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.Spectral\");\n  bind_impl<DataVector>(m);\n  m.def(\"lower_spatial_four_velocity\",\n        &hydro::Tags::LowerSpatialFourVelocityCompute::function,\n        py::arg(\"result\"), py::arg(\"spatial_velocity\"),\n        py::arg(\"spatial_metric\"), py::arg(\"lorentz_factor\"));\n}\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyHydro\")\n\nspectre_python_add_module(\n  Hydro\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n  MODULE_PATH \"PointwiseFunctions\"\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Hydro\n  pybind11::module\n  Utilities\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/QuadrupoleFormula.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"QuadrupoleFormula.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace hydro {\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid quadrupole_moment(\n    const gsl::not_null<tnsr::ii<DataType, Dim, Fr>*> result,\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Fr>& coordinates) {\n  set_number_of_grid_points(result, tilde_d);\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = i; j < Dim; j++) {\n      result->get(i, j) = get(tilde_d) * coordinates.get(i) *\n                          coordinates.get(j);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid quadrupole_moment_derivative(\n    const gsl::not_null<tnsr::ii<DataType, Dim, Fr>*> result,\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Fr>& coordinates,\n    const tnsr::I<DataType, Dim, Fr>& spatial_velocity) {\n  set_number_of_grid_points(result, tilde_d);\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = i; j < Dim; j++) {\n      result->get(i, j) = get(tilde_d) *\n                          (spatial_velocity.get(i) * coordinates.get(j) +\n                          coordinates.get(i) * spatial_velocity.get(j));\n    }\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void quadrupole_moment<DTYPE(data), DIM(data), FRAME(data)>(      \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          result,                                                            \\\n      const Scalar<DTYPE(data)>& tilde_d,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& spatial_position); \\\n  template void quadrupole_moment_derivative<DTYPE(data), DIM(data),         \\\n          FRAME(data)>(                                                      \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          result,                                                            \\\n      const Scalar<DTYPE(data)>& tilde_d,                                    \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& spatial_position,  \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& spatial_velocity);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (DataVector),\n                        (Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/QuadrupoleFormula.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace hydro {\n\nnamespace Tags {\n/// \\brief Tag containing the quadrupole moment.\n///\n/// The quadrupole moment is defined as \\f$\\tilde{D} x^i x^j\\f$\n/// (equation 21 of \\cite Shibata2003), with\n/// \\f$\\tilde{D}=\\sqrt{\\gamma}\\rho W\\f$, $W$ being the Lorentz factor,\n/// $\\gamma$ being the determinant of the spatial metric, and\n/// \\f$x\\f$ the coordinates in Frame Fr.\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct QuadrupoleMoment : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Fr>;\n};\n\n/// \\brief Tag containing the first time derivative of the quadrupole moment.\n///\n/// For the first derivative of the quadrupole moment, we use\n/// \\f$\\tilde{D} (v^i x^j + x^i v^j)\\f$ (equation 23 of \\cite Shibata2003),\n/// with \\f$\\tilde{D}=\\sqrt{\\gamma}\\rho W\\f$, $W$ being the Lorentz factor,\n/// $\\gamma$ being the determinant of the spatial metric, \\f$x\\f$ the\n/// coordinates in Frame Fr, and \\f$v\\f$ the corresponding spatial velocity.\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct QuadrupoleMomentDerivative : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim, Fr>;\n};\n}  // namespace Tags\n\n/// \\brief Function computing the quadrupole moment.\n///\n/// Computes the quadrupole moment, using\n/// \\f$\\tilde{D} x^i x^j\\f$ (equation 21 of \\cite Shibata2003),\n/// with \\f$\\tilde{D}=\\sqrt{\\gamma}\\rho W\\f$, $W$ being the Lorentz factor,\n/// $\\gamma$ being the determinant of the spatial metric, and\n/// \\f$x\\f$ the coordinates in Frame Fr. Result of calculation stored in result.\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nvoid quadrupole_moment(\n    const gsl::not_null<tnsr::ii<DataType, Dim, Fr>*> result,\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Fr>& coordinates);\n\n/// \\brief Function computing the first time derivative of the\n/// quadrupole moment.\n///\n/// Computes the first time derivative of the quadrupole moment, using\n/// \\f$\\tilde{D} (v^i x^j + x^i v^j)\\f$ (equation 23 of \\cite Shibata2003),\n/// with \\f$\\tilde{D}=\\sqrt{\\gamma}\\rho W\\f$, $W$ being the Lorentz factor,\n/// $\\gamma$ being the determinant of the spatial metric, \\f$x\\f$ the\n/// coordinates in Frame Fr, and \\f$v\\f$ the corresponding spatial velocity.\n/// Result of calculation stored in result.\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nvoid quadrupole_moment_derivative(\n    const gsl::not_null<tnsr::ii<DataType, Dim, Fr>*> result,\n    const Scalar<DataType>& tilde_d,\n    const tnsr::I<DataType, Dim, Fr>& coordinates,\n    const tnsr::I<DataType, Dim, Fr>& spatial_velocity);\n\n} // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/SoundSpeedSquared.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/SoundSpeedSquared.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace hydro {\n\ntemplate <typename DataType, size_t ThermodynamicDim>\nvoid sound_speed_squared(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& specific_enthalpy,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) {\n  if constexpr (ThermodynamicDim == 1) {\n    get(*result) =\n        get(equation_of_state.chi_from_density(rest_mass_density)) +\n        get(equation_of_state.kappa_times_p_over_rho_squared_from_density(\n            rest_mass_density));\n  } else if constexpr (ThermodynamicDim == 2) {\n    get(*result) =\n        get(equation_of_state.chi_from_density_and_energy(\n            rest_mass_density, specific_internal_energy)) +\n        get(equation_of_state\n                .kappa_times_p_over_rho_squared_from_density_and_energy(\n                    rest_mass_density, specific_internal_energy));\n  }\n  get(*result) /= get(specific_enthalpy);\n}\n\ntemplate <typename DataType, size_t ThermodynamicDim>\nScalar<DataType> sound_speed_squared(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& specific_enthalpy,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state) {\n  Scalar<DataType> result{};\n  sound_speed_squared(make_not_null(&result), rest_mass_density,\n                      specific_internal_energy, specific_enthalpy,\n                      equation_of_state);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define THERMO_DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                           \\\n  template void sound_speed_squared(                                   \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result,                \\\n      const Scalar<DTYPE(data)>& rest_mass_density,                    \\\n      const Scalar<DTYPE(data)>& specific_internal_energy,             \\\n      const Scalar<DTYPE(data)>& specific_enthalpy,                    \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& \\\n          equation_of_state);                                          \\\n  template Scalar<DTYPE(data)> sound_speed_squared(                    \\\n      const Scalar<DTYPE(data)>& rest_mass_density,                    \\\n      const Scalar<DTYPE(data)>& specific_internal_energy,             \\\n      const Scalar<DTYPE(data)>& specific_enthalpy,                    \\\n      const EquationsOfState::EquationOfState<true, THERMO_DIM(data)>& \\\n          equation_of_state);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector), (1, 2))\n\n#undef DTYPE\n#undef THERMO_DIM\n#undef INSTANTIATE\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/SoundSpeedSquared.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace EquationsOfState {\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nclass EquationOfState;\n}  // namespace EquationsOfState\n/// \\endcond\n\nnamespace hydro {\n/// @{\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Computes the relativistic sound speed squared\n *\n * The relativistic sound speed squared is given by\n * \\f$c_s^2 = \\left(\\chi + p\\kappa / \\rho^2\\right)/h\\f$, where\n * \\f$p\\f$ is the fluid pressure, \\f$\\rho\\f$ is the rest mass density,\n * \\f$h = 1 + \\epsilon + p / \\rho\\f$ is the specific enthalpy\n * \\f$\\chi = (\\partial p/\\partial\\rho)_\\epsilon\\f$ and\n * \\f$\\kappa = (\\partial p/ \\partial \\epsilon)_\\rho\\f$, where\n * \\f$\\epsilon\\f$ is the specific internal energy.\n */\ntemplate <typename DataType, size_t ThermodynamicDim>\nvoid sound_speed_squared(\n    gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& specific_enthalpy,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state);\n\ntemplate <typename DataType, size_t ThermodynamicDim>\nScalar<DataType> sound_speed_squared(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& specific_enthalpy,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>&\n        equation_of_state);\n/// @}\n\nnamespace Tags {\n/// Compute item for the sound speed squared \\f$c_s^2\\f$.\n/// \\see hydro::sound_speed_squared\n///\n/// Can be retrieved using `hydro::Tags::SoundSpeedSquared`\ntemplate <typename DataType, bool IsRelativistic, size_t ThermoDim>\nstruct SoundSpeedSquaredCompute : SoundSpeedSquared<DataType>, db::ComputeTag {\n  using argument_tags = typename tmpl::list<\n      RestMassDensity<DataType>, SpecificInternalEnergy<DataType>,\n      SpecificEnthalpy<DataType>,\n      hydro::Tags::EquationOfState<IsRelativistic, ThermoDim>>;\n\n  using return_type = Scalar<DataType>;\n\n  template <typename EquationOfStateType>\n  static void function(const gsl::not_null<Scalar<DataType>*> result,\n                       const Scalar<DataType>& rest_mass_density,\n                       const Scalar<DataType>& specific_internal_energy,\n                       const Scalar<DataType>& specific_enthalpy,\n                       const EquationOfStateType& equation_of_state) {\n    sound_speed_squared(result, rest_mass_density, specific_internal_energy,\n                        specific_enthalpy, equation_of_state);\n  }\n\n  using base = SoundSpeedSquared<DataType>;\n};\n}  // namespace Tags\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/SpecificEnthalpy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace hydro {\ntemplate <typename DataType>\nvoid relativistic_specific_enthalpy(\n    const gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& pressure) {\n  get(*result) = 1.0 + get(specific_internal_energy) +\n                 get(pressure) / get(rest_mass_density);\n}\n\ntemplate <typename DataType>\nScalar<DataType> relativistic_specific_enthalpy(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& pressure) {\n  Scalar<DataType> result{};\n  relativistic_specific_enthalpy(make_not_null(&result), rest_mass_density,\n                                 specific_internal_energy, pressure);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                   \\\n  template void relativistic_specific_enthalpy(                \\\n      const gsl::not_null<Scalar<DTYPE(data)>*> result,        \\\n      const Scalar<DTYPE(data)>& rest_mass_density,            \\\n      const Scalar<DTYPE(data)>& specific_internal_energy,     \\\n      const Scalar<DTYPE(data)>& pressure);                    \\\n  template Scalar<DTYPE(data)> relativistic_specific_enthalpy( \\\n      const Scalar<DTYPE(data)>& rest_mass_density,            \\\n      const Scalar<DTYPE(data)>& specific_internal_energy,     \\\n      const Scalar<DTYPE(data)>& pressure);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/SpecificEnthalpy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace hydro {\n/// @{\n/*!\n * \\ingroup EquationsOfStateGroup\n * \\brief Computes the relativistic specific enthalpy \\f$h\\f$ as:\n * \\f$ h = 1 + \\epsilon + \\frac{p}{\\rho} \\f$\n * where \\f$\\epsilon\\f$ is the specific internal energy, \\f$p\\f$\n * is the pressure, and \\f$\\rho\\f$ is the rest mass density.\n */\ntemplate <typename DataType>\nvoid relativistic_specific_enthalpy(\n    gsl::not_null<Scalar<DataType>*> result,\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& pressure);\n\ntemplate <typename DataType>\nScalar<DataType> relativistic_specific_enthalpy(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& pressure);\n/// @}\n\nnamespace Tags {\n/// Compute item for the relativistic specific enthalpy \\f$h\\f$.\n///\n/// Can be retrieved using `hydro::Tags::SpecificEnthalpy`\ntemplate <typename DataType>\nstruct SpecificEnthalpyCompute : SpecificEnthalpy<DataType>, db::ComputeTag {\n  using argument_tags =\n      tmpl::list<RestMassDensity<DataType>, SpecificInternalEnergy<DataType>,\n                 Pressure<DataType>>;\n\n  using return_type = Scalar<DataType>;\n\n  static constexpr auto function = static_cast<void (*)(\n      gsl::not_null<Scalar<DataType>*>, const Scalar<DataType>&,\n      const Scalar<DataType>&, const Scalar<DataType>&)>(\n      &relativistic_specific_enthalpy<DataType>);\n\n  using base = SpecificEnthalpy<DataType>;\n};\n}  // namespace Tags\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/StressEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Hydro/StressEnergy.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/Hydro/ComovingMagneticField.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nvoid four_velocity(const gsl::not_null<tnsr::A<DataType, 3>*> result,\n                   const tnsr::I<DataType, 3>& spatial_velocity,\n                   const tnsr::I<DataType, 3>& shift,\n                   const Scalar<DataType>& lorentz_factor,\n                   const Scalar<DataType>& lapse) {\n  get<0>(*result) = get(lorentz_factor) / get(lapse);\n  for (size_t i = 0; i < 3; ++i) {\n    result->get(i + 1) =\n        get<0>(*result) * (get(lapse) * spatial_velocity.get(i) - shift.get(i));\n  }\n}\n\ntemplate <typename DataType>\ntnsr::A<DataType, 3> four_velocity(const tnsr::I<DataType, 3>& spatial_velocity,\n                                   const tnsr::I<DataType, 3>& shift,\n                                   const Scalar<DataType>& lorentz_factor,\n                                   const Scalar<DataType>& lapse) {\n  tnsr::A<DataType, 3> result{};\n  four_velocity(make_not_null(&result), spatial_velocity, shift, lorentz_factor,\n                lapse);\n  return result;\n}\n}  // namespace\n\nnamespace hydro {\n\ntemplate <typename DataType>\nvoid energy_density(gsl::not_null<Scalar<DataType>*> result,\n                    const Scalar<DataType>& rest_mass_density,\n                    const Scalar<DataType>& specific_enthalpy,\n                    const Scalar<DataType>& pressure,\n                    const Scalar<DataType>& lorentz_factor,\n                    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n                    const Scalar<DataType>& comoving_magnetic_field_squared) {\n  *result = rest_mass_density;\n  get(*result) *= get(specific_enthalpy);\n  get(*result) += get(comoving_magnetic_field_squared);\n  get(*result) -= square(get(magnetic_field_dot_spatial_velocity));\n  get(*result) *= square(get(lorentz_factor));\n  get(*result) -= get(pressure);\n  get(*result) -= 0.5 * get(comoving_magnetic_field_squared);\n}\n\ntemplate <typename DataType>\nvoid momentum_density(\n    gsl::not_null<tnsr::I<DataType, 3>*> result,\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_enthalpy,\n    const tnsr::I<DataType, 3>& spatial_velocity,\n    const Scalar<DataType>& lorentz_factor,\n    const tnsr::I<DataType, 3>& magnetic_field,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& comoving_magnetic_field_squared) {\n  get<0>(*result) = (get(rest_mass_density) * get(specific_enthalpy) +\n                     get(comoving_magnetic_field_squared) -\n                     square(get(magnetic_field_dot_spatial_velocity))) *\n                    square(get(lorentz_factor));\n  get<1>(*result) = get<0>(*result);\n  get<2>(*result) = get<0>(*result);\n  for (size_t d = 0; d < 3; ++d) {\n    result->get(d) *= spatial_velocity.get(d);\n    result->get(d) -=\n        get(magnetic_field_dot_spatial_velocity) * magnetic_field.get(d);\n  }\n}\n\ntemplate <typename DataType>\nvoid stress_trace(gsl::not_null<Scalar<DataType>*> result,\n                  const Scalar<DataType>& rest_mass_density,\n                  const Scalar<DataType>& specific_enthalpy,\n                  const Scalar<DataType>& pressure,\n                  const Scalar<DataType>& spatial_velocity_squared,\n                  const Scalar<DataType>& lorentz_factor,\n                  const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n                  const Scalar<DataType>& comoving_magnetic_field_squared) {\n  get(*result) =\n      3. * get(pressure) +\n      get(rest_mass_density) * get(specific_enthalpy) *\n          (square(get(lorentz_factor)) - 1.) +\n      get(comoving_magnetic_field_squared) *\n          (square(get(lorentz_factor)) * get(spatial_velocity_squared) + 0.5) -\n      square(get(magnetic_field_dot_spatial_velocity)) *\n          (square(get(lorentz_factor)) * get(spatial_velocity_squared) + 1.);\n}\n\ntemplate <typename DataType>\nvoid stress_energy_tensor(\n    const gsl::not_null<tnsr::AA<DataType, 3>*> result,\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& pressure, const Scalar<DataType>& lorentz_factor,\n    const Scalar<DataType>& lapse,\n    const Scalar<DataType>& comoving_magnetic_field_magnitude,\n    const tnsr::I<DataType, 3>& spatial_velocity,\n    const tnsr::I<DataType, 3>& shift,\n    const tnsr::I<DataType, 3>& magnetic_field,\n    const tnsr::ii<DataType, 3>& spatial_metric,\n    const tnsr::II<DataType, 3>& inverse_spatial_metric) {\n  // Preallocate to minimize number of allocations.\n  TempBuffer<tmpl::list<::Tags::TempScalar<0, DataType>,\n                        ::Tags::TempScalar<1, DataType>,\n                        ::Tags::TempScalar<2, DataType>,\n                        ::Tags::TempA<3, 3, Frame::Inertial, DataType>,\n                        ::Tags::TempA<4, 3, Frame::Inertial, DataType>,\n                        ::Tags::TempAA<5, 3, Frame::Inertial, DataType>>>\n      buffer(get_size(get(rest_mass_density)));\n\n  auto& magnetic_field_dot_spatial_velocity =\n      get<::Tags::TempScalar<0, DataType>>(buffer);\n  auto& rho_h_star = get<::Tags::TempScalar<1, DataType>>(buffer);\n  auto& p_star = get<::Tags::TempScalar<2, DataType>>(buffer);\n  auto& comoving_magnetic_field_v =\n      get<::Tags::TempA<3, 3, Frame::Inertial, DataType>>(buffer);\n  auto& four_velocity_v =\n      get<::Tags::TempA<4, 3, Frame::Inertial, DataType>>(buffer);\n  auto& inverse_spacetime_metric_v =\n      get<::Tags::TempAA<5, 3, Frame::Inertial, DataType>>(buffer);\n\n  gr::inverse_spacetime_metric(make_not_null(&inverse_spacetime_metric_v),\n                               lapse, shift, inverse_spatial_metric);\n\n  dot_product(make_not_null(&magnetic_field_dot_spatial_velocity),\n              magnetic_field, spatial_velocity, spatial_metric);\n\n  comoving_magnetic_field(make_not_null(&comoving_magnetic_field_v),\n                          spatial_velocity, magnetic_field,\n                          magnetic_field_dot_spatial_velocity, lorentz_factor,\n                          shift, lapse);\n\n  four_velocity(make_not_null(&four_velocity_v), spatial_velocity, shift,\n                lorentz_factor, lapse);\n\n  get(rho_h_star) = (get(rest_mass_density) +\n                     get(rest_mass_density) * get(specific_internal_energy)) +\n                    get(pressure) +\n                    square(get(comoving_magnetic_field_magnitude));\n\n  get(p_star) =\n      get(pressure) + 0.5 * square(get(comoving_magnetic_field_magnitude));\n\n  for (size_t i = 0; i < 4; ++i) {\n    for (size_t j = i; j < 4; ++j) {\n      result->get(i, j) =\n          get(rho_h_star) * four_velocity_v.get(i) * four_velocity_v.get(j) +\n          get(p_star) * inverse_spacetime_metric_v.get(i, j) -\n          comoving_magnetic_field_v.get(i) * comoving_magnetic_field_v.get(j);\n    }\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define INSTANTIATION(r, data)                                              \\\n  template void energy_density(                                             \\\n      gsl::not_null<Scalar<DTYPE(data)>*>, const Scalar<DTYPE(data)>&,      \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,               \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,               \\\n      const Scalar<DTYPE(data)>&);                                          \\\n  template void momentum_density(                                           \\\n      gsl::not_null<tnsr::I<DTYPE(data), 3>*>, const Scalar<DTYPE(data)>&,  \\\n      const Scalar<DTYPE(data)>&, const tnsr::I<DTYPE(data), 3>&,           \\\n      const Scalar<DTYPE(data)>&, const tnsr::I<DTYPE(data), 3>&,           \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&);              \\\n  template void stress_trace(                                               \\\n      gsl::not_null<Scalar<DTYPE(data)>*>, const Scalar<DTYPE(data)>&,      \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,               \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,               \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&);              \\\n  template void stress_energy_tensor(                                       \\\n      gsl::not_null<tnsr::AA<DTYPE(data), 3>*>, const Scalar<DTYPE(data)>&, \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,               \\\n      const Scalar<DTYPE(data)>&, const Scalar<DTYPE(data)>&,               \\\n      const Scalar<DTYPE(data)>&, const tnsr::I<DTYPE(data), 3>&,           \\\n      const tnsr::I<DTYPE(data), 3>&, const tnsr::I<DTYPE(data), 3>&,       \\\n      const tnsr::ii<DTYPE(data), 3>&, const tnsr::II<DTYPE(data), 3>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATION\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/StressEnergy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace hydro {\n\n/*!\n * \\brief The total mass-energy density measured by a normal observer, $E = n_a\n * n_b T^{ab}$\n *\n * This quantity sources the gravitational field equations in the 3+1\n * decomposition (see Eq. (2.138) in \\cite BaumgarteShapiro).\n *\n * Perfect fluid contribution (Eq. (5.33) in \\cite BaumgarteShapiro):\n *\n * \\begin{equation}\n *   E_\\mathrm{fluid} = \\rho h W^2 - p\n * \\end{equation}\n *\n * Magnetic field contribution (Eq. (5.152) in \\cite BaumgarteShapiro):\n *\n * \\begin{equation}\n *   E_\\mathrm{em} = b^2 \\left(W^2 - \\frac{1}{2}\\right) - (\\alpha b^t)^2\n * \\end{equation}\n *\n * where $\\alpha b^t = W B^k v_k$.\n *\n * \\param result Output buffer. Will be resized if needed.\n * \\param rest_mass_density $\\rho$\n * \\param specific_enthalpy $h$\n * \\param pressure $p$\n * \\param lorentz_factor $W$\n * \\param magnetic_field_dot_spatial_velocity $B^k v_k$\n * \\param comoving_magnetic_field_squared $b^2$\n *\n * \\see gr::Tags::EnergyDensity\n */\ntemplate <typename DataType>\nvoid energy_density(gsl::not_null<Scalar<DataType>*> result,\n                    const Scalar<DataType>& rest_mass_density,\n                    const Scalar<DataType>& specific_enthalpy,\n                    const Scalar<DataType>& pressure,\n                    const Scalar<DataType>& lorentz_factor,\n                    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n                    const Scalar<DataType>& comoving_magnetic_field_squared);\n\n/*!\n * \\brief The spatial momentum density $S^i = -\\gamma^{ij} n^a T_{aj}$\n *\n * This quantity sources the gravitational field equations in the 3+1\n * decomposition (see Eq. (2.138) in \\cite BaumgarteShapiro).\n *\n * Perfect fluid contribution (Eq. (5.34) in \\cite BaumgarteShapiro):\n *\n * \\begin{equation}\n *   S^i_\\mathrm{fluid} = \\rho h W^2 v^i\n * \\end{equation}\n *\n * Magnetic field contribution (Eq. (5.153) in \\cite BaumgarteShapiro):\n *\n * \\begin{equation}\n *   S^i_\\mathrm{em} = b^2 W^2 v^i - \\alpha b^t \\gamma^{ij} b_j\n * \\end{equation}\n *\n * where $\\alpha b^t \\gamma^{ij} b_j = B^k v_k B^i + (B^k v_k)^2 W^2 v^i$.\n *\n * \\param result Output buffer. Will be resized if needed.\n * \\param rest_mass_density $\\rho$\n * \\param specific_enthalpy $h$\n * \\param spatial_velocity $v^i$\n * \\param lorentz_factor $W$\n * \\param magnetic_field $B^i$\n * \\param magnetic_field_dot_spatial_velocity $B^k v_k$\n * \\param comoving_magnetic_field_squared $b^2$\n *\n * \\see gr::Tags::MomentumDensity\n */\ntemplate <typename DataType>\nvoid momentum_density(\n    gsl::not_null<tnsr::I<DataType, 3>*> result,\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_enthalpy,\n    const tnsr::I<DataType, 3>& spatial_velocity,\n    const Scalar<DataType>& lorentz_factor,\n    const tnsr::I<DataType, 3>& magnetic_field,\n    const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n    const Scalar<DataType>& comoving_magnetic_field_squared);\n\n/*!\n * \\brief The trace of the spatial stress tensor, $S =\n * \\gamma^{ij}\\gamma_{ia}\\gamma_{jb}T^{ab}$\n *\n * This quantity sources the gravitational field equations in the 3+1\n * decomposition (see Eq. (2.138) in \\cite BaumgarteShapiro).\n *\n * Perfect fluid contribution (Eq. (5.36) in \\cite BaumgarteShapiro):\n *\n * \\begin{equation}\n *   S_\\mathrm{fluid} = 3 p + \\rho h (W^2 - 1)\n * \\end{equation}\n *\n * Magnetic field contribution (Eq. (5.155) in \\cite BaumgarteShapiro):\n *\n * \\begin{equation}\n *   S_\\mathrm{em} = b^2 (W^2 v^2 + \\frac{3}{2}) - \\gamma^{ij} b_i b_j\n * \\end{equation}\n *\n * where $\\gamma^{ij} b_i b_j = b^2 + (B^k v_k)^2 (W^2 v^2 + 1)$.\n *\n * \\param result Output buffer. Will be resized if needed.\n * \\param rest_mass_density $\\rho$\n * \\param specific_enthalpy $h$\n * \\param pressure $p$\n * \\param spatial_velocity_squared $v^2 = \\gamma_{ij} v^i v^j$\n * \\param lorentz_factor $W$\n * \\param magnetic_field_dot_spatial_velocity $B^k v_k$\n * \\param comoving_magnetic_field_squared $b^2$\n *\n * \\see gr::Tags::StressTrace\n */\ntemplate <typename DataType>\nvoid stress_trace(gsl::not_null<Scalar<DataType>*> result,\n                  const Scalar<DataType>& rest_mass_density,\n                  const Scalar<DataType>& specific_enthalpy,\n                  const Scalar<DataType>& pressure,\n                  const Scalar<DataType>& spatial_velocity_squared,\n                  const Scalar<DataType>& lorentz_factor,\n                  const Scalar<DataType>& magnetic_field_dot_spatial_velocity,\n                  const Scalar<DataType>& comoving_magnetic_field_squared);\n\n/*!\n * \\brief Stress Energy Tesnor, $T^{ab}=\n * (\\rho h)^{*} u^a u ^b + p^{*} g^{ab} - b^{a} b^{b}$,\n *\n * where $(\\rho h)^{*} = \\rho h + b^{2}$ and $p^{*} = p + b^{2}/2$\n * are the enthalpy density and fluid pressure augmented by contributions of\n * magnetic pressure $p_{mag}$ = b^{2}/2, respectively.\n *\n * $b$ refers to magnetic field measured in the comoving frame of the fluid\n * $b^{a} = ^{*}F^{ab} u_{b}$.\n */\ntemplate <typename DataType>\nvoid stress_energy_tensor(\n    gsl::not_null<tnsr::AA<DataType, 3>*> result,\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& pressure, const Scalar<DataType>& lorentz_factor,\n    const Scalar<DataType>& lapse,\n    const Scalar<DataType>& comoving_magnetic_field_magnitude,\n    const tnsr::I<DataType, 3>& spatial_velocity,\n    const tnsr::I<DataType, 3>& shift,\n    const tnsr::I<DataType, 3>& magnetic_field,\n    const tnsr::ii<DataType, 3>& spatial_metric,\n    const tnsr::II<DataType, 3>& inverse_spatial_metric);\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/TypeTraits.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n/// \\ingroup EvolutionSystemsGroup\n/// \\brief Items related to hydrodynamic systems.\nnamespace hydro {\n\n/// %Tags for options of hydrodynamic systems.\nnamespace OptionTags {\n/// The equation of state of the fluid.\ntemplate <bool IsRelativistic, size_t ThermoDim>\nstruct InitialDataEquationOfState {\n  using type = std::unique_ptr<\n      EquationsOfState::EquationOfState<IsRelativistic, ThermoDim>>;\n  static std::string name() { return \"EquationOfState\"; }\n  static constexpr Options::String help = {\n      \"Options for the equation of state used for the initial data.\"};\n};\n\n/// The equation of state of the fluid.\ntemplate <bool IsRelativistic, size_t ThermoDim>\nstruct EquationOfState {\n  struct FromInitialData {};\n  using type = Options::Auto<std::unique_ptr<EquationsOfState::EquationOfState<\n                                 IsRelativistic, ThermoDim>>,\n                             FromInitialData>;\n\n  static constexpr Options::String help = {\n      \"The equation of state to use during the evolution.\"};\n};\n\nstruct GrmhdEquationOfState {\n  struct FromInitialData {};\n  using type =\n      Options::Auto<std::unique_ptr<EquationsOfState::EquationOfState<true, 3>>,\n                    FromInitialData>;\n  static std::string name() { return \"EquationOfState\"; }\n  static constexpr Options::String help = {\n      \"Options for the equation of state used for relativistic\"\n      \"hydro simulations using GRMHD executables.\"};\n};\n}  // namespace OptionTags\n\n/// %Tags for hydrodynamic systems.\nnamespace Tags {\n\n/// The Alfvén speed squared \\f$v_A^2\\f$.\ntemplate <typename DataType>\nstruct AlfvenSpeedSquared : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The magnetic field \\f$b^\\mu = u_\\nu {}^\\star\\!F^{\\mu \\nu}\\f$\n/// measured by an observer comoving with the fluid with 4-velocity\n/// \\f$u_\\nu\\f$ where \\f${}^\\star\\!F^{\\mu \\nu}\\f$\n/// is the dual of the Faraday tensor.  Note that \\f$b^\\mu\\f$ has a\n/// time component (that is, \\f$b^\\mu n_\\mu \\neq 0\\f$, where \\f$n_\\mu\\f$ is\n/// the normal to the spacelike hypersurface).\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct ComovingMagneticField : db::SimpleTag {\n  using type = tnsr::A<DataType, Dim, Fr>;\n  static std::string name() {\n    return Frame::prefix<Fr>() + \"ComovingMagneticField\";\n  }\n};\n\n/// The square of the comoving magnetic field, \\f$b^\\mu b_\\mu\\f$\ntemplate <typename DataType>\nstruct ComovingMagneticFieldSquared : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The magnitude of the comoving magnetic field, \\f$\\sqrt{b^\\mu b_\\mu}\\f$\ntemplate <typename DataType>\nstruct ComovingMagneticFieldMagnitude : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The divergence-cleaning field \\f$\\Phi\\f$.\ntemplate <typename DataType>\nstruct DivergenceCleaningField : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The electron fraction \\f$Y_e\\f$.\ntemplate <typename DataType>\nstruct ElectronFraction : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The equation of state retrieved from the analytic solution / data in the\n/// input file\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nstruct EquationOfState : db::SimpleTag {\n  using type = std::unique_ptr<\n      EquationsOfState::EquationOfState<IsRelativistic, ThermodynamicDim>>;\n\n  template <typename Metavariables>\n  using option_tags =\n      tmpl::list<OptionTags::EquationOfState<IsRelativistic, ThermodynamicDim>,\n                 ::evolution::initial_data::OptionTags::InitialData>;\n  static constexpr bool pass_metavariables = true;\n\n  template <typename Metavariables>\n  static type create_from_options(\n      const std::optional<type>& eos,\n      const std::unique_ptr<::evolution::initial_data::InitialData>&\n          initial_data) {\n    if (eos.has_value()) {\n      return eos.value()->get_clone();\n    } else {\n      return call_with_dynamic_type<\n          type,\n          tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                   ::evolution::initial_data::InitialData>>(\n          initial_data.get(), [](const auto* const derived_initial_data) {\n            if constexpr (::evolution::is_numeric_initial_data_v<\n                              std::decay_t<decltype(*derived_initial_data)>>) {\n              ERROR(\n                  \"Equation of State cannot currently be parsed from numeric\"\n                  \"initial data, please explicitly specify the equation of \"\n                  \"state for the evolution in the input file.\");\n              if constexpr (ThermodynamicDim == 1) {\n                return std::make_unique<\n                    EquationsOfState::PolytropicFluid<IsRelativistic>>(100.0,\n                                                                       2.0);\n              } else if constexpr (ThermodynamicDim == 2) {\n                return std::make_unique<\n                    EquationsOfState::IdealFluid<IsRelativistic>>(2.0);\n              } else if constexpr (ThermodynamicDim == 3) {\n                return std::make_unique<EquationsOfState::Barotropic3D<\n                    EquationsOfState::PolytropicFluid<IsRelativistic>>>(\n                    EquationsOfState::PolytropicFluid<IsRelativistic>(100.0,\n                                                                      2.0));\n              }\n            } else {\n              if constexpr (ThermodynamicDim == 3) {\n                return derived_initial_data->equation_of_state()\n                    .promote_to_3d_eos();\n              } else if constexpr (ThermodynamicDim == 2) {\n                return derived_initial_data->equation_of_state()\n                    .promote_to_2d_eos();\n              } else {\n                return derived_initial_data->equation_of_state().get_clone();\n              }\n            }\n          });\n    }\n  }\n};\n\n/// The inverse plasma beta \\f$\\beta^{-1} = b^2 / (2 p)\\f$, where\n///// \\f$b^2\\f$ is the square of the comoving magnetic field amplitude\n///// and \\f$p\\f$ is the fluid pressure.\ntemplate <typename DataType>\nstruct InversePlasmaBeta : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The Lorentz factor \\f$W = (1-v^iv_i)^{-1/2}\\f$, where \\f$v^i\\f$ is\n/// the spatial velocity of the fluid.\ntemplate <typename DataType>\nstruct LorentzFactor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The square of the Lorentz factor \\f$W^2\\f$.\ntemplate <typename DataType>\nstruct LorentzFactorSquared : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The magnetic field \\f$B^i = n_\\mu {}^\\star\\!F^{i \\mu}\\f$ measured by an\n/// Eulerian observer, where \\f$n_\\mu\\f$ is the normal to the spatial\n/// hypersurface and \\f${}^\\star\\!F^{\\mu \\nu}\\f$ is the dual of the\n/// Faraday tensor.  Note that \\f$B^i\\f$ is purely spatial, and it\n/// can be lowered using the spatial metric.\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct MagneticField : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Fr>;\n  static std::string name() { return Frame::prefix<Fr>() + \"MagneticField\"; }\n};\n\n/// The magnetic field dotted into the spatial velocity, \\f$B^iv_i\\f$ where\n/// \\f$v_i\\f$ is the spatial velocity one-form.\ntemplate <typename DataType>\nstruct MagneticFieldDotSpatialVelocity : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The one-form of the magnetic field.  Note that \\f$B^i\\f$ is raised\n/// and lowered with the spatial metric.\n/// \\see hydro::Tags::MagneticField\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct MagneticFieldOneForm : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Fr>;\n  static std::string name() {\n    return Frame::prefix<Fr>() + \"MagneticFieldOneForm\";\n  }\n};\n\n/// The square of the magnetic field, \\f$B^iB_i\\f$\ntemplate <typename DataType>\nstruct MagneticFieldSquared : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The magnetic pressure \\f$p_m\\f$.\ntemplate <typename DataType>\nstruct MagneticPressure : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The fluid pressure \\f$p\\f$.\ntemplate <typename DataType>\nstruct Pressure : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The rest-mass density \\f$\\rho\\f$.\ntemplate <typename DataType>\nstruct RestMassDensity : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The sound speed squared \\f$c_s^2\\f$.\ntemplate <typename DataType>\nstruct SoundSpeedSquared : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The spatial velocity \\f$v^i\\f$ of the fluid,\n/// where \\f$v^i=u^i/W + \\beta^i/\\alpha\\f$.\n/// Here \\f$u^i\\f$ is the spatial part of the 4-velocity of the fluid,\n/// \\f$W\\f$ is the Lorentz factor, \\f$\\beta^i\\f$ is the shift vector,\n/// and \\f$\\alpha\\f$ is the lapse function. Note that \\f$v^i\\f$ is raised\n/// and lowered with the spatial metric.\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct SpatialVelocity : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Fr>;\n  static std::string name() { return Frame::prefix<Fr>() + \"SpatialVelocity\"; }\n};\n\n/// The spatial velocity one-form \\f$v_i\\f$, where \\f$v_i\\f$ is raised\n/// and lowered with the spatial metric.\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct SpatialVelocityOneForm : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Fr>;\n  static std::string name() {\n    return Frame::prefix<Fr>() + \"SpatialVelocityOneForm\";\n  }\n};\n\n/// The spatial velocity squared \\f$v^2 = v_i v^i\\f$.\ntemplate <typename DataType>\nstruct SpatialVelocitySquared : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The relativistic specific enthalpy \\f$h\\f$.\ntemplate <typename DataType>\nstruct SpecificEnthalpy : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The specific internal energy \\f$\\epsilon\\f$.\ntemplate <typename DataType>\nstruct SpecificInternalEnergy : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// The temperature \\f$T\\f$ of the fluid.\ntemplate <typename DataType>\nstruct Temperature : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n/// \\brief Tag containing the transport velocity.\n///\n/// The transport velocity is defined as \\f$v_t^i=\\alpha v^i-\\beta^i\\f$,\n/// with $v^i$ being the spatial velocity, $\\alpha$ the lapse, and\n/// $\\beta^i$ the shift.\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct TransportVelocity : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Fr>;\n};\n\n/// The spatial components of the four-velocity one-form \\f$u_i\\f$.\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct LowerSpatialFourVelocity : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Fr>;\n};\n\n/// The Lorentz factor \\f$W\\f$ times the spatial velocity \\f$v^i\\f$.\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct LorentzFactorTimesSpatialVelocity : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Fr>;\n};\n\n/// The vector \\f$J^i\\f$ in \\f$\\dot{M} = -\\int J^i s_i d^2S\\f$,\n/// representing the mass flux through a surface with normal \\f$s_i\\f$.\n///\n/// Note that the integral is understood\n/// as a flat-space integral: all metric factors are included in \\f$J^i\\f$.\n/// In particular, if the integral is done over a Strahlkorper, the\n/// `gr::surfaces::euclidean_area_element` of the Strahlkorper should be used,\n/// and \\f$s_i\\f$ is\n/// the normal one-form to the Strahlkorper normalized with the flat metric,\n/// \\f$s_is_j\\delta^{ij}=1\\f$.\n///\n/// The formula is\n/// \\f$ J^i = \\rho W \\sqrt{\\gamma}(\\alpha v^i-\\beta^i)\\f$,\n/// where \\f$\\rho\\f$ is the mass density, \\f$W\\f$ is the Lorentz factor,\n/// \\f$v^i\\f$ is the spatial velocity of the fluid,\n/// \\f$\\gamma\\f$ is the determinant of the 3-metric \\f$\\gamma_{ij}\\f$,\n/// \\f$\\alpha\\f$ is the lapse, and \\f$\\beta^i\\f$ is the shift.\ntemplate <typename DataType, size_t Dim, typename Fr>\nstruct MassFlux : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Fr>;\n  static std::string name() { return Frame::prefix<Fr>() + \"MassFlux\"; }\n};\n\n/// The equation of state retrieved from the analytic solution / data in the\n/// input file\nstruct GrmhdEquationOfState : db::SimpleTag {\n  using type = std::unique_ptr<EquationsOfState::EquationOfState<true, 3>>;\n\n  template <typename Metavariables>\n  using option_tags =\n      tmpl::list<OptionTags::GrmhdEquationOfState,\n                 ::evolution::initial_data::OptionTags::InitialData>;\n  static constexpr bool pass_metavariables = true;\n\n  template <typename Metavariables>\n  static type create_from_options(\n      const std::optional<type>& eos,\n      const std::unique_ptr<::evolution::initial_data::InitialData>&\n          initial_data) {\n    if (eos.has_value()) {\n      return eos.value()->get_clone();\n    } else {\n      return call_with_dynamic_type<\n          type,\n          tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                   ::evolution::initial_data::InitialData>>(\n          initial_data.get(), [](const auto* const derived_initial_data) {\n            if constexpr (::evolution::is_numeric_initial_data_v<\n                              std::decay_t<decltype(*derived_initial_data)>>) {\n              ERROR(\n                  \"Equation of State cannot currently be parsed from numeric\"\n                  \"initial data, please explicitly specify the equation of \"\n                  \"state for the evolution in the input file.\");\n              return std::make_unique<EquationsOfState::Barotropic3D<\n                  EquationsOfState::PolytropicFluid<true>>>(\n                  EquationsOfState::PolytropicFluid<true>(100.0, 2.0));\n            } else {\n              return (derived_initial_data->equation_of_state()\n                          .promote_to_3d_eos());\n            }\n          });\n    }\n  }\n};\n}  // namespace Tags\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/TagsDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nclass DataVector;\n\nnamespace hydro {\nnamespace Tags {\n\ntemplate <typename DataType>\nstruct AlfvenSpeedSquared;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct ComovingMagneticField;\ntemplate <typename DataType>\nstruct ComovingMagneticFieldSquared;\ntemplate <typename DataType>\nstruct ComovingMagneticFieldMagnitude;\ntemplate <typename DataType>\nstruct DivergenceCleaningField;\ntemplate <typename DataType>\nstruct ElectronFraction;\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nstruct EquationOfState;\nstruct GrmhdEquationOfState;\ntemplate <typename DataType>\nstruct InversePlasmaBeta;\ntemplate <typename DataType>\nstruct LorentzFactor;\ntemplate <typename DataType>\nstruct LorentzFactorSquared;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct MagneticField;\ntemplate <typename DataType>\nstruct MagneticFieldDotSpatialVelocity;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct MagneticFieldOneForm;\ntemplate <typename DataType>\nstruct MagneticFieldSquared;\ntemplate <typename DataType>\nstruct MagneticPressure;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct MassFlux;\ntemplate <typename DataType>\nstruct Pressure;\ntemplate <typename DataType>\nstruct RestMassDensity;\ntemplate <typename DataType>\nstruct SoundSpeedSquared;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct SpatialVelocity;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct SpatialVelocityOneForm;\ntemplate <typename DataType>\nstruct SpatialVelocitySquared;\ntemplate <typename DataType>\nstruct SpecificEnthalpy;\ntemplate <typename DataType>\nstruct SpecificInternalEnergy;\ntemplate <typename DataType>\nstruct Temperature;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct LowerSpatialFourVelocity;\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct LorentzFactorTimesSpatialVelocity;\n}  // namespace Tags\n/// \\endcond\n\n/// The tags for the primitive variables for GRMHD.\ntemplate <typename DataType>\nusing grmhd_tags = tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                              hydro::Tags::ElectronFraction<DataType>,\n                              hydro::Tags::SpecificInternalEnergy<DataType>,\n                              hydro::Tags::SpatialVelocity<DataType, 3>,\n                              hydro::Tags::MagneticField<DataType, 3>,\n                              hydro::Tags::DivergenceCleaningField<DataType>,\n                              hydro::Tags::LorentzFactor<DataType>,\n                              hydro::Tags::Pressure<DataType>,\n                              hydro::Tags::Temperature<DataType> >;\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/Temperature.hpp",
    "content": "\n// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace hydro {\n\n/// @{\n/*!\n * \\brief Wrapper to add temperature variable to initial data\n * providing only density and or energy_density initialization.\n */\n\ntemplate <typename DerivedSolution>\nclass TemperatureInitialization {\n private:\n  template <typename DataType, size_t Dim, typename... Args>\n  auto variables_impl(const tnsr::I<DataType, Dim>& x,\n                      tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/,\n                      Args&... extra_args) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    const auto* derived = static_cast<DerivedSolution const*>(this);\n    const auto& eos = derived->equation_of_state();\n    if constexpr (std::decay_t<decltype(eos)>::thermodynamic_dim == 1) {\n      return eos.temperature_from_density(\n          get<hydro::Tags::RestMassDensity<DataType>>(derived->variables(\n              x, extra_args...,\n              tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})));\n    } else if constexpr (std::decay_t<decltype(eos)>::thermodynamic_dim == 2) {\n      return eos.temperature_from_density_and_energy(\n          get<hydro::Tags::RestMassDensity<DataType>>(derived->variables(\n              x, extra_args...,\n              tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n          get<hydro::Tags::SpecificInternalEnergy<DataType>>(derived->variables(\n              x, extra_args...,\n              tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})));\n    } else {\n      return eos.temperature_from_density_and_energy(\n          get<hydro::Tags::RestMassDensity<DataType>>(derived->variables(\n              x, extra_args...,\n              tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n          get<hydro::Tags::SpecificInternalEnergy<DataType>>(derived->variables(\n              x, extra_args...,\n              tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})),\n          get<hydro::Tags::ElectronFraction<DataType>>(derived->variables(\n              x, extra_args...,\n              tmpl::list<hydro::Tags::ElectronFraction<DataType>>{})));\n    }\n  }\n\n public:\n  template <typename DataType, size_t Dim>\n  auto variables(const tnsr::I<DataType, Dim>& x,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return variables_impl(x, tmpl::list<hydro::Tags::Temperature<DataType>>{});\n  }\n\n  template <typename DataType, size_t Dim>\n  auto variables(const tnsr::I<DataType, Dim>& x, const double t,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    return variables_impl(x, tmpl::list<hydro::Tags::Temperature<DataType>>{},\n                          t);\n  }\n\n  template <typename ExtraVars, typename DataType, size_t Dim, typename... Args>\n  auto variables(ExtraVars& extra_variables, const tnsr::I<DataType, Dim>& x,\n                 Args&... extra_args,\n                 tmpl::list<hydro::Tags::Temperature<DataType>> /*meta*/) const\n      -> tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> {\n    const auto* derived = static_cast<DerivedSolution const*>(this);\n    const auto& eos = derived->equation_of_state();\n    if constexpr (std::decay_t<decltype(eos)>::thermodynamic_dim == 1) {\n      return eos.temperature_from_density(\n          get<hydro::Tags::RestMassDensity<DataType>>(derived->variables(\n              extra_variables, x, extra_args...,\n              tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})));\n    } else if constexpr (std::decay_t<decltype(eos)>::thermodynamic_dim == 2) {\n      return eos.temperature_from_density_and_energy(\n          get<hydro::Tags::RestMassDensity<DataType>>(derived->variables(\n              extra_variables, x, extra_args...,\n              tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n          get<hydro::Tags::SpecificInternalEnergy<DataType>>(derived->variables(\n              extra_variables, x, extra_args...,\n              tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})));\n    } else {\n      return eos.temperature_from_density_and_energy(\n          get<hydro::Tags::RestMassDensity<DataType>>(derived->variables(\n              extra_variables, x, extra_args...,\n              tmpl::list<hydro::Tags::RestMassDensity<DataType>>{})),\n          get<hydro::Tags::SpecificInternalEnergy<DataType>>(derived->variables(\n              extra_variables, x, extra_args...,\n              tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>>{})),\n          get<hydro::Tags::ElectronFraction<DataType>>(derived->variables(\n              extra_variables, x, extra_args...,\n              tmpl::list<hydro::Tags::ElectronFraction<DataType>>{})));\n    }\n  }\n};\n/// @}\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/TransportVelocity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"TransportVelocity.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace hydro {\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nvoid transport_velocity(const gsl::not_null<tnsr::I<DataType, Dim, Fr>*> result,\n                        const tnsr::I<DataType, Dim, Fr>& spatial_velocity,\n                        const Scalar<DataType>& lapse,\n                        const tnsr::I<DataType, Dim, Fr>& shift) {\n  set_number_of_grid_points(result, lapse);\n  for (size_t i = 0; i < Dim; i++) {\n    result->get(i) = spatial_velocity.get(i) * get(lapse) - shift.get(i);\n  }\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template void transport_velocity<DTYPE(data), DIM(data), FRAME(data)>(    \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*>    \\\n          result,                                                           \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& spatial_velocity, \\\n      const Scalar<DTYPE(data)>& lapse,                                     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& shift);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/TransportVelocity.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename>\nstruct not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace hydro {\n\n/// \\brief Function computing the transport velocity.\n///\n/// Computes the transport velocity, using\n/// \\f$v_t^i=\\alpha v^i-\\beta^i\\f$, with\n/// $v^i$ being the spatial velocity, $\\alpha$ the lapse, and\n/// $\\beta^i$ the shift.\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nvoid transport_velocity(gsl::not_null<tnsr::I<DataType, Dim, Fr>*> result,\n                        const tnsr::I<DataType, Dim, Fr>& spatial_velocity,\n                        const Scalar<DataType>& lapse,\n                        const tnsr::I<DataType, Dim, Fr>& shift);\n\nnamespace Tags {\n/// \\brief Compute tag for the transport velocity.\n///\n/// Compute item for the transport velocity, using\n/// \\f$v_t^i=\\alpha v^i-\\beta^i\\f$, with\n/// $v^i$ being the spatial velocity, $\\alpha$ the lapse, and\n/// $\\beta^i$ the shift.\ntemplate <typename DataType, size_t Dim, typename Fr = Frame::Inertial>\nstruct TransportVelocityCompute\n    : hydro::Tags::TransportVelocity<DataType, Dim, Fr>,\n      db::ComputeTag {\n  using argument_tags =\n      tmpl::list<SpatialVelocity<DataType, Dim, Fr>,\n                 ::gr::Tags::Lapse<DataType>,\n                 ::gr::Tags::Shift<DataType, Dim, Fr>>;\n\n  using base = hydro::Tags::TransportVelocity<DataType, Dim, Fr>;\n  using return_type = typename base::type;\n\n  static constexpr auto function = static_cast<void (*)(\n      const gsl::not_null<tnsr::I<DataType, Dim, Fr>*> result,\n      const tnsr::I<DataType, Dim, Fr>& spatial_velocity,\n      const Scalar<DataType>& lapse, const tnsr::I<DataType, Dim, Fr>& shift)>(\n      &hydro::transport_velocity<DataType, Dim, Fr>);\n};\n\n}  // namespace Tags\n\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/Hydro/Units.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace hydro {\n/*!\n * \\brief Functions, constants, and classes for converting between different\n * units.\n *\n * In SpECTRE we prefer to use geometric units where \\f$G=c=M_{\\odot}=1\\f$, as\n * is standard in numerical relativity. However, in order to interface with\n * other codes, we need to sometimes convert units. This namespace is designed\n * to hold the various conversion factors and functions. A unit library/system\n * would be nice, but is a non-trivial amount of work.\n */\nnamespace units {\n/*!\n * \\brief Entities for converting between geometric units where\n * \\f$G=c=M_{\\odot}=1\\f$ and CGS units.\n *\n */\nnamespace cgs {\n\n/// The speed of light in cm/s (This is exact)\nconstexpr double speed_of_light = 29979245800.0;\n/// Newton's gravitational constant as given by\n/// https://journals.aps.org/rmp/abstract/10.1103/RevModPhys.93.025010.\n/// G is likely to vary between sources, even beyond the reported uncertainty\nconstexpr double G_Newton = 6.67430e-8;\n/// Note G*M_Sun = 1.32712440042x10^26 +/- 1e16 [1/cm]\n/// G*M_Sun factor from https://doi.org/10.1063/1.4921980\nstatic constexpr double G_Newton_times_m_sun = 1.32712440042e26;\n/// k_B factor in erg/K from\n/// https://journals.aps.org/rmp/pdf/10.1103/RevModPhys.93.025010.\n/// (This is exact)\nstatic constexpr double k_Boltzmann = 1.380649e-16;\n\n/// `mass_cgs = mass_geometric * mass_unit`\n/// Heuristically the mass of the sun in grams\nconstexpr double mass_unit = G_Newton_times_m_sun / G_Newton;\n/// `length_cgs = length_geometric * length_unit`\n/// Heuristically, half the schwarzschild radius of the sun\n/// in cm\nconstexpr double length_unit = G_Newton_times_m_sun / square(speed_of_light);\n/// `time_cgs = time_geometric * time_unit`\n/// Heuristically, half the light crossing time of a\n/// solar schwarzschild radius in seconds\nconstexpr double time_unit = length_unit / speed_of_light;\n/// `rho_cgs = rho_geometric * rho_unit`\n/// Heuristically, the density in g/cm^3 of matter ~2200 times the density of\n/// atomic nuclei.  Note this is much larger than any density realized in the\n/// universe.\nconstexpr double rest_mass_density_unit =\n    mass_unit / (length_unit * length_unit * length_unit);\n/// `pressure_cgs = pressure_geometric * pressure_unit`\n/// Heuristically, the quantity above but times the speed of light squared in\n/// cgs\nconstexpr double pressure_unit =\n    mass_unit / (length_unit * time_unit * time_unit);\n\n// Extra constants, which are known to greatest precision in SI / (equvialently\n// CGS)\n/// The accepted value for the atomic mass unit in g, uncertainty +-30e-10\nconstexpr double atomic_mass_unit = 1.66053906660e-24;\n/// The neutron mass, given in grams, uncertainty at 5.7 x 10^-10 level\nconstexpr double neutron_mass = 1.67492749804e-24;\n/// The proton mass, given in grams. Uncertainty at 2e-8 level\nconstexpr double proton_mass = 1.672621898e-24;\n/// The electron-volt (eV) given in ergs, which is known exactly in SI/cgs\nconstexpr double electron_volt = 1.602176634e-12;\n\n/*!\n * \\brief CGS unit of Gauss.\n *\n * Equals `mass_unit^(1/2) * length_unit^(-1/2) * time_unit^(-1)`.\n *\n * Conversion rule for electromagnetic fields is\n * ```\n * magnetic_field_cgs = magnetic_field_geometric * gauss_unit\n * ```\n * or\n * ```\n * electric_field_cgs = electric_field_geometric * gauss_unit\n * ```\n *\n * \\warning Before changing units using this value, make sure the unit systems\n * you are converting values between are using the same unit convention for\n * electromagnetic variables as well. Gaussian and Heaviside-Lorentz unit\n * convention (in electromagnetism) have a factor of \\f$\\sqrt{4\\pi}\\f$\n * difference in variables. See\n * <a href=\"https://en.wikipedia.org/wiki/Heaviside%E2%80%93Lorentz_units\">this\n * Wikipedia page</a> for how to convert between different unit systems in\n * electromagnetism.\n *\n * Note that ForceFree and grmhd::ValenciaDivClean evolution systems are\n * adopting the geometrized Heaviside-Lorentz unit for magnetic fields.\n *\n * e.g. Suppose magnetic field has value \\f$10^{-5}\\f$ with the code unit in the\n * ValenciaDivClean evolution system. This corresponds to\n * ```\n * 10^(-5) * gauss_unit = 2.3558985e+14 Gauss\n * ```\n * in the CGS Heaviside-Lorentz unit.\n *\n * If one wants to convert it to the usual CGS Gaussian unit, extra factor of\n * \\f$\\sqrt{4\\pi}\\f$ needs to be multiplied:\n * ```\n * 10^(-5) * gauss_unit * sqrt(4pi) = 8.35144274e+14 Gauss\n * ```\n */\nconstexpr double gauss_unit = 2.3558985e+19;\n}  // namespace cgs\n\n/*!\n * \\brief The defining quantities of the nuclear fm-MeV/c^2-fm/c unit system\n *\n * Constants are defined in terms of cgs units where possible, which are, in\n * many cases known exactly due to their relation to SI.\n */\nnamespace nuclear {\n/// `mass_nuclear = mass_unit * mass_geometric`\n/// Heuristically a solar mass in MeV/c^2\nconstexpr double mass_unit =\n     cgs::mass_unit /\n    (1.0e6 * cgs::electron_volt / square(cgs::speed_of_light));\n/// `length_nuclear = length_unit * length_geometric`\n/// Heuristically GM_sun/c^2 in fm\nconstexpr double length_unit =  cgs::length_unit / (1.0e-13);\n/// `time_nuclear = time_unit * time_geometric`\n/// Heuristically GM_sun/c^3 in fm/c\nconstexpr double time_unit =\n     cgs::time_unit / (1.0e-13 / cgs::speed_of_light);\n/// `pressure_nuclear = pressure_geometric * pressure_geometric`\n/// Heuristically the rest-mass energy density of\n/// ~2200 the density of atomic nucli expressed in MeV/fm^3\nconstexpr double pressure_unit = mass_unit / (length_unit * square(time_unit));\n/// `rest_mass_density_nuclear = rest_mass_density_geometric *\n/// rest_mass_density_geometric` Heuristically ~2200 the density of atomic nucli\n/// expressed in MeV/c^2/fm^3\nconstexpr double rest_mass_density_unit = mass_unit / cube(length_unit);\nconstexpr double neutron_mass =\n    cgs::neutron_mass /\n    (1.0e6 * cgs::electron_volt / square(cgs::speed_of_light));\nconstexpr double atomic_mass_unit =\n    cgs::atomic_mass_unit /\n    (1.0e6 * cgs::electron_volt / square(cgs::speed_of_light));\n/// The proton mass in MeV, uncertainty at 5.8eV.\nconstexpr double proton_mass =\n    cgs::proton_mass /\n    (1.0e6 * cgs::electron_volt / square(cgs::speed_of_light));\n\n/// The saturation number density of baryons in nuclear matter, in\n/// units of 1/fm^3.  This is a standard value, consistent with  e.g.\n/// https://journals.aps.org/prc/abstract/10.1103/PhysRevC.102.044321\nconstexpr double saturation_number_density = 0.16;\n\n}  // namespace nuclear\n/*!\n * \\brief Quantities given in terms of geometric units G = c = M_odot =\n * 1\n *\n * All quantities are given in terms of unit systems which are related exactly\n * to SI units.  The limiting factor in conversions is the poorly known value\n * of G; which leads to large uncertainties in the relative value of SI-derived\n * masses to the solar mass.  For this reason, it is best to avoid using\n * geometric units in calculations except where conversion is explicitly needed.\n */\nnamespace geometric {\n/// Neutron mass in G = c = M_sun = 1.0 units\nconstexpr double neutron_mass = cgs::neutron_mass / cgs::mass_unit;\n/// The rest mass density of matter at the saturation point\nconstexpr double default_saturation_rest_mass_density =\n    1 / nuclear::rest_mass_density_unit *\n    (nuclear::saturation_number_density * nuclear::atomic_mass_unit);\n/// The default baryon mass in geometric units.\n/// Accepted value of atomic mass unit as expressed in\n/// solar masses\n/// Limited by precision of knowledge of solar mass.\nconstexpr double default_baryon_mass = cgs::atomic_mass_unit / cgs::mass_unit;\n}  // namespace geometric\n\n}  // namespace units\n}  // namespace hydro\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace elliptic::analytic_data {\n/*!\n * \\brief Subclasses represent analytic solutions of elliptic systems.\n *\n * Subclasses must define the compile-time interfaces of both\n * `elliptic::analytic_data::InitialGuess` and\n * `elliptic::analytic_data::Background`.\n *\n * The combined set of system variables and background fields must solve the\n * elliptic PDEs. Subclasses must list all additional requirements needed so\n * they solve the elliptic PDEs in the class documentation and in their option\n * help-string.\n */\nclass AnalyticSolution : public elliptic::analytic_data::InitialGuess,\n                         public elliptic::analytic_data::Background {\n protected:\n  AnalyticSolution() = default;\n\n public:\n  ~AnalyticSolution() override = default;\n\n  /// \\cond\n  explicit AnalyticSolution(CkMigrateMessage* msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(AnalyticSolution);\n  /// \\endcond\n\n  virtual std::unique_ptr<AnalyticSolution> get_clone() const = 0;\n};\n}  // namespace elliptic::analytic_data\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/Background.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace elliptic::analytic_data {\n/*!\n * \\brief Subclasses supply variable-independent background data for an elliptic\n * solve.\n *\n * Examples for background fields are a background metric, associated curvature\n * quantities, matter sources such as a mass-density in the XCTS equations, or\n * just a source function \\f$f(x)\\f$ in a Poisson equation \\f$\\Delta u =\n * f(x)\\f$.\n *\n * Subclasses must define the following compile-time interface:\n *\n * - They are option-creatable.\n * - They define a `variables` function that provides the fixed-sources in the\n *   elliptic equations (see `elliptic::protocols::FirstOrderSystem`). The\n *   function must have this signature:\n *\n *   \\snippet Test_InitializeFixedSources.cpp background_vars_fct\n *\n *   It must support being called with a `tmpl::list` of all system-variable\n *   tags prefixed with `::Tags::FixedSource`.\n * - They define a `variables` function that provides data for all background\n *   quantities, if any are listed in the `background_fields` of the system (see\n *   `elliptic::protocols::FirstOrderSystem`). The function must have this\n *   signature:\n *\n *   \\snippet Test_SubdomainOperator.cpp background_vars_fct_derivs\n *\n *   It must support being called with a `tmpl::list` of all background tags. It\n *   may use the `mesh` and `inv_jacobian` to compute numerical derivatives.\n */\nclass Background : public virtual PUP::able {\n protected:\n  Background() = default;\n\n public:\n  ~Background() override = default;\n\n  /// \\cond\n  explicit Background(CkMigrateMessage* msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(Background);\n  /// \\endcond\n};\n}  // namespace elliptic::analytic_data\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY InitialDataUtilities)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  NumericData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AnalyticSolution.hpp\n  Background.hpp\n  InitialData.hpp\n  InitialGuess.hpp\n  NumericData.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Exporter\n  Options\n  Serialization\n  Spectral\n  )\n\nadd_subdirectory(Tags)\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/InitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace evolution {\n/*!\n * \\brief Namespace for things related to initial data used for evolution\n * systems.\n */\nnamespace initial_data {\n/*!\n * \\brief The abstract base class for initial data of evolution systems. All\n * analytic solutions and analytic data must virtually inherit from this class.\n */\nclass InitialData : public PUP::able {\n protected:\n  InitialData() = default;\n\n public:\n  ~InitialData() override = default;\n\n  virtual auto get_clone() const -> std::unique_ptr<InitialData> = 0;\n\n  /// \\cond\n  explicit InitialData(CkMigrateMessage* msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(InitialData);\n  /// \\endcond\n};\n}  // namespace initial_data\n}  // namespace evolution\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// Items related to pointwise analytic data for elliptic solves, such as\n/// initial guesses, analytic solutions, and background quantities in elliptic\n/// PDEs\nnamespace elliptic::analytic_data {\n/*!\n * \\brief Subclasses represent an initial guess for an elliptic solve.\n *\n * Subclasses must define the following compile-time interface:\n *\n * - They are option-creatable.\n * - They define a `variables` function that can provide data for all variables\n *   that are being solved for. Specifically the function must have this\n *   signature:\n *\n *   \\snippet Test_InitializeFields.cpp initial_guess_vars_fct\n *\n *   It must support being called with a `tmpl::list` of all tags that are being\n *   solved for. For this purpose it can be convenient to template the function\n *   on the set of requested tags.\n */\nclass InitialGuess : public virtual PUP::able {\n protected:\n  InitialGuess() = default;\n\n public:\n  ~InitialGuess() override = default;\n\n  /// \\cond\n  explicit InitialGuess(CkMigrateMessage* msg) : PUP::able(msg) {}\n  WRAPPED_PUPable_abstract(InitialGuess);\n  /// \\endcond\n};\n}  // namespace elliptic::analytic_data\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/NumericData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/InitialDataUtilities/NumericData.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <utility>\n\nNumericData::NumericData(std::string file_glob, std::string subgroup,\n                         int observation_step, bool extrapolate_into_excisions)\n    : file_glob_(std::move(file_glob)),\n      subgroup_(std::move(subgroup)),\n      observation_step_(observation_step),\n      extrapolate_into_excisions_(extrapolate_into_excisions) {}\n\nbool operator==(const NumericData& lhs, const NumericData& rhs) {\n  return lhs.file_glob() == rhs.file_glob() and\n         lhs.subgroup() == rhs.subgroup() and\n         lhs.observation_step() == rhs.observation_step() and\n         lhs.extrapolate_into_excisions() == rhs.extrapolate_into_excisions();\n}\n\nbool operator!=(const NumericData& lhs, const NumericData& rhs) {\n  return not(lhs == rhs);\n}\n\nvoid NumericData::pup(PUP::er& p) {\n  p | file_glob_;\n  p | subgroup_;\n  p | observation_step_;\n  p | extrapolate_into_excisions_;\n}\n\nnamespace elliptic::analytic_data {\n\nNumericData::NumericData(CkMigrateMessage* m)\n    : elliptic::analytic_data::Background(m),\n      elliptic::analytic_data::InitialGuess(m) {}\n\nvoid NumericData::pup(PUP::er& p) {\n  elliptic::analytic_data::Background::pup(p);\n  elliptic::analytic_data::InitialGuess::pup(p);\n  ::NumericData::pup(p);\n}\n\nbool operator==(const NumericData& lhs, const NumericData& rhs) {\n  return static_cast<const ::NumericData&>(lhs) ==\n         static_cast<const ::NumericData&>(rhs);\n}\n\nbool operator!=(const NumericData& lhs, const NumericData& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID NumericData::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace elliptic::analytic_data\n\nnamespace evolution::initial_data {\n\nNumericData::NumericData(CkMigrateMessage* m)\n    : evolution::initial_data::InitialData(m) {}\n\nstd::unique_ptr<evolution::initial_data::InitialData> NumericData::get_clone()\n    const {\n  return std::make_unique<NumericData>(*this);\n}\n\nvoid NumericData::pup(PUP::er& p) {\n  evolution::initial_data::InitialData::pup(p);\n  ::NumericData::pup(p);\n}\n\nbool operator==(const NumericData& lhs, const NumericData& rhs) {\n  return static_cast<const ::NumericData&>(lhs) ==\n         static_cast<const ::NumericData&>(rhs);\n}\n\nbool operator!=(const NumericData& lhs, const NumericData& rhs) {\n  return not(lhs == rhs);\n}\n\nPUP::able::PUP_ID NumericData::my_PUP_ID = 0;  // NOLINT\n\n}  // namespace evolution::initial_data\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/NumericData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/*!\n * \\brief Load numeric data from volume data files\n *\n * This class loads all requested tags from volume data files using\n * `spectre::Exporter::interpolate_to_points`. This is an easy and useful\n * alternative to `importers::ElementDataReader` to load numeric data with the\n * following advantages:\n *\n * - No need to work with parallel components, actions, or phases to load\n *   numeric data. Also, no need to parse numeric data options separately from\n *   analytic data options. Just include this class in the list of analytic data\n *   classes for the executable.\n * - The data is interpolated and returned in serial, just like analytic data.\n * - The data can be re-interpolated to any set of points on request, which is\n *   an easy way to handle AMR or other domain changes.\n *\n * However, it also comes with the following caveats:\n *\n * - The volume data files must have datasets with the same names as the tags\n *   being loaded. For example, if the tag being loaded is `gr::Tags::Shift`,\n *   then the volume data files must have datasets named `Shift_x`, `Shift_y`,\n *   and `Shift_z`. If you need processing of the data before it can be used,\n *   e.g. to load some datasets and compute other derived quantities from them,\n *   then consider writing a custom class or use `importers::ElementDataReader`.\n * - Reading in data on the same grid that it was written on may not give you\n *   the same data back, at least not on Gauss-Lobatto grids. This is because\n *   grid points on element boundaries are disambiguated by\n *   `block_logical_coordinates` and `element_logical_coordinates`, so a\n *   boundary point may be written by one element but read back in from its\n *   neighbor. To avoid this, consider using `importers::ElementDataReader`\n *   which has functionality to read in data on the same grid that it was\n *   written on. This is not possible with this class because it is not aware of\n *   the element structure (it operates in a pointwise manner).\n * - Large datasets may not fit in memory. Each element will open the H5 files\n *   and interpolate data from them, so this may not fit in memory if the\n *   datasets are large and/or many elements are doing this at the same time. To\n *   avoid this, consider using `importers::ElementDataReader` which reads one\n *   H5 file at a time on the node level.\n */\nclass NumericData {\n public:\n  struct FileGlob {\n    static constexpr Options::String help =\n        \"Path or glob pattern to the data file\";\n    using type = std::string;\n  };\n  struct Subgroup {\n    static constexpr Options::String help = {\n        \"The subgroup within the file, excluding extensions\"};\n    using type = std::string;\n  };\n  struct ObservationStep {\n    static constexpr Options::String help =\n        \"The observation step at which to read data\";\n    using type = int;\n  };\n  struct ExtrapolateIntoExcisions {\n    static constexpr Options::String help = {\n        \"Whether to extrapolate data into excised regions\"};\n    using type = bool;\n  };\n  using options =\n      tmpl::list<FileGlob, Subgroup, ObservationStep, ExtrapolateIntoExcisions>;\n  static constexpr Options::String help =\n      \"Numeric data loaded from volume data files\";\n\n  NumericData() = default;\n  NumericData(const NumericData&) = default;\n  NumericData& operator=(const NumericData&) = default;\n  NumericData(NumericData&&) = default;\n  NumericData& operator=(NumericData&&) = default;\n  ~NumericData() = default;\n\n  NumericData(std::string file_glob, std::string subgroup, int observation_step,\n              bool extrapolate_into_excisions);\n\n  const std::string& file_glob() const { return file_glob_; }\n\n  const std::string& subgroup() const { return subgroup_; }\n\n  int observation_step() const { return observation_step_; }\n\n  bool extrapolate_into_excisions() const {\n    return extrapolate_into_excisions_;\n  }\n\n  template <typename DataType, size_t Dim, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataType, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return spectre::Exporter::interpolate_to_points<\n        tmpl::list<RequestedTags...>>(\n        file_glob_, subgroup_,\n        spectre::Exporter::ObservationStep{observation_step_}, x,\n        extrapolate_into_excisions_);\n  }\n\n  template <size_t Dim, typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, Dim>& x, const Mesh<Dim>& /*mesh*/,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>& /*inv_jacobian*/,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return variables(x, tmpl::list<RequestedTags...>{});\n  }\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p);\n\n protected:\n  std::string file_glob_{};\n  std::string subgroup_{};\n  int observation_step_{};\n  bool extrapolate_into_excisions_{};\n};\n\nbool operator==(const NumericData& lhs, const NumericData& rhs);\nbool operator!=(const NumericData& lhs, const NumericData& rhs);\n\nnamespace elliptic::analytic_data {\n\n/*!\n * \\brief Load numeric data from volume data files\n *\n * \\see ::NumericData\n */\nclass NumericData : public elliptic::analytic_data::Background,\n                    public elliptic::analytic_data::InitialGuess,\n                    public ::NumericData {\n public:\n  using ::NumericData::NumericData;\n\n  explicit NumericData(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NumericData);\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override;\n};\n\nbool operator==(const NumericData& lhs, const NumericData& rhs);\nbool operator!=(const NumericData& lhs, const NumericData& rhs);\n\n}  // namespace elliptic::analytic_data\n\nnamespace evolution::initial_data {\n\n/*!\n * \\brief Load numeric data from volume data files\n *\n * \\see ::NumericData\n */\nclass NumericData : public evolution::initial_data::InitialData,\n                    public ::NumericData {\n public:\n  using ::NumericData::NumericData;\n\n  explicit NumericData(CkMigrateMessage* m);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NumericData);\n\n  std::unique_ptr<evolution::initial_data::InitialData> get_clone()\n      const override;\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override;\n};\n\nbool operator==(const NumericData& lhs, const NumericData& rhs);\nbool operator!=(const NumericData& lhs, const NumericData& rhs);\n\n}  // namespace evolution::initial_data\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InitialData.hpp\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::initial_data {\n/*!\n * \\brief Option tags for initial data of evolution system.\n */\nnamespace OptionTags {\n/*!\n * \\ingroup OptionTagsGroup\n * \\brief Class holding options for initial data of evolution system.\n */\nstruct InitialData {\n  static constexpr Options::String help =\n      \"Options for initial data of evolution system\";\n  using type = std::unique_ptr<evolution::initial_data::InitialData>;\n};\n}  // namespace OptionTags\n\n/*!\n * \\brief Tags for initial data of evolution system.\n */\nnamespace Tags {\n/*!\n * \\brief The global cache tag for the initial data type\n */\nstruct InitialData : db::SimpleTag {\n  using type = std::unique_ptr<evolution::initial_data::InitialData>;\n  using option_tags =\n      tmpl::list<evolution::initial_data::OptionTags::InitialData>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& value) {\n    return serialize_and_deserialize<type>(value);\n  }\n};\n}  // namespace Tags\n}  // namespace evolution::initial_data\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY MathFunctions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Gaussian.cpp\n  PowX.cpp\n  RegisterDerivedWithCharm.cpp\n  Sinusoid.cpp\n  TensorProduct.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Factory.hpp\n  Gaussian.hpp\n  MathFunction.hpp\n  PowX.hpp\n  RegisterDerivedWithCharm.hpp\n  Sinusoid.hpp\n  TensorProduct.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  ErrorHandling\n  Options\n  Serialization\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace MathFunctions {\nnamespace Factory_detail {\ntemplate <size_t VolumeDim, typename Fr>\nstruct all_math_functions {\n  using type = tmpl::list<MathFunctions::Gaussian<VolumeDim, Fr>>;\n};\n\ntemplate <typename Fr>\nstruct all_math_functions<1, Fr> {\n  using type =\n      tmpl::list<MathFunctions::Gaussian<1, Fr>, MathFunctions::PowX<1, Fr>,\n                 MathFunctions::Sinusoid<1, Fr>>;\n};\n}  // namespace Factory_detail\n\ntemplate <size_t VolumeDim, typename Fr>\nusing all_math_functions =\n    typename Factory_detail::all_math_functions<VolumeDim, Fr>::type;\n}  // namespace MathFunctions\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/Gaussian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace MathFunctions {\n\ntemplate <typename Fr>\nGaussian<1, Fr>::Gaussian(const double amplitude, const double width,\n                          const double center)\n    : amplitude_(amplitude), inverse_width_(1.0 / width), center_(center) {}\n\ntemplate <typename Fr>\nGaussian<1, Fr>::Gaussian(double amplitude, double width,\n                          const std::array<double, 1>& center)\n    : Gaussian(amplitude, width, center[0]) {}\n\ntemplate <typename Fr>\nstd::unique_ptr<MathFunction<1, Fr>> Gaussian<1, Fr>::get_clone() const {\n  return std::make_unique<Gaussian<1, Fr>>(*this);\n}\n\ntemplate <typename Fr>\ndouble Gaussian<1, Fr>::operator()(const double& x) const {\n  return apply_call_operator(x);\n}\n\ntemplate <typename Fr>\nDataVector Gaussian<1, Fr>::operator()(const DataVector& x) const {\n  return apply_call_operator(x);\n}\n\ntemplate <typename Fr>\ndouble Gaussian<1, Fr>::first_deriv(const double& x) const {\n  return apply_first_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector Gaussian<1, Fr>::first_deriv(const DataVector& x) const {\n  return apply_first_deriv(x);\n}\n\ntemplate <typename Fr>\ndouble Gaussian<1, Fr>::second_deriv(const double& x) const {\n  return apply_second_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector Gaussian<1, Fr>::second_deriv(const DataVector& x) const {\n  return apply_second_deriv(x);\n}\n\ntemplate <typename Fr>\ndouble Gaussian<1, Fr>::third_deriv(const double& x) const {\n  return apply_third_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector Gaussian<1, Fr>::third_deriv(const DataVector& x) const {\n  return apply_third_deriv(x);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT Gaussian<1, Fr>::apply_call_operator(const T& x) const {\n  return amplitude_ * exp(-square((x - center_) * inverse_width_));\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT Gaussian<1, Fr>::apply_first_deriv(const T& x) const {\n  return (-2.0 * amplitude_ * square(inverse_width_)) * (x - center_) *\n         exp(-square((x - center_) * inverse_width_));\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT Gaussian<1, Fr>::apply_second_deriv(const T& x) const {\n  return (-2.0 * amplitude_ * square(inverse_width_)) *\n         (1.0 - 2.0 * square(x - center_) * square(inverse_width_)) *\n         exp(-square((x - center_) * inverse_width_));\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT Gaussian<1, Fr>::apply_third_deriv(const T& x) const {\n  return 4.0 * amplitude_ * pow<4>(inverse_width_) * (x - center_) *\n         (3.0 - 2.0 * square((x - center_) * inverse_width_)) *\n         exp(-square((x - center_) * inverse_width_));\n}\n\ntemplate <typename Fr>\nvoid Gaussian<1, Fr>::pup(PUP::er& p) {\n  MathFunction<1, Fr>::pup(p);\n  p | amplitude_;\n  p | inverse_width_;\n  p | center_;\n}\n\ntemplate <typename Fr>\nbool Gaussian<1, Fr>::operator==(const MathFunction<1, Fr>& other) const {\n  const auto* derived_other = dynamic_cast<const Gaussian<1, Fr>*>(&other);\n  return derived_other != nullptr and (*this == *derived_other);\n}\n\ntemplate <typename Fr>\nbool Gaussian<1, Fr>::operator!=(const MathFunction<1, Fr>& other) const {\n  return not(*this == other);\n}\n\ntemplate <typename Fr>\nbool Gaussian<1, Fr>::operator==(const Gaussian<1, Fr>& other) const {\n  return (this->amplitude_ == other.amplitude_) and\n         (this->inverse_width_ == other.inverse_width_) and\n         (this->center_ == other.center_);\n}\n\ntemplate <typename Fr>\nbool Gaussian<1, Fr>::operator!=(const Gaussian<1, Fr>& other) const {\n  return not(*this == other);\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nGaussian<VolumeDim, Fr>::Gaussian(const double amplitude, const double width,\n                                  const std::array<double, VolumeDim>& center)\n    : amplitude_(amplitude), inverse_width_(1.0 / width), center_(center) {}\n\ntemplate <size_t VolumeDim, typename Fr>\nstd::unique_ptr<MathFunction<VolumeDim, Fr>>\nGaussian<VolumeDim, Fr>::get_clone() const {\n  return std::make_unique<Gaussian<VolumeDim, Fr>>(*this);\n}\n\ntemplate <size_t VolumeDim, typename Fr>\ntemplate <typename T>\ntnsr::I<T, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::centered_coordinates(\n    const tnsr::I<T, VolumeDim, Fr>& x) const {\n  tnsr::I<T, VolumeDim, Fr> centered_coords = x;\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    centered_coords.get(i) -= gsl::at(center_, i);\n  }\n  return centered_coords;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\ntemplate <typename T>\nScalar<T> Gaussian<VolumeDim, Fr>::apply_call_operator(\n    const tnsr::I<T, VolumeDim, Fr>& centered_coords) const {\n  Scalar<T> result = dot_product(centered_coords, centered_coords);\n  get(result) = amplitude_ * exp(-get(result) * square(inverse_width_));\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\ntemplate <typename T>\ntnsr::i<T, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::apply_first_deriv(\n    const tnsr::I<T, VolumeDim, Fr>& centered_coords,\n    const Scalar<T>& gaussian) const {\n  auto result = make_with_value<tnsr::i<T, VolumeDim, Fr>>(get(gaussian), 0.0);\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    result.get(i) =\n        -2.0 * square(inverse_width_) * get(gaussian) * centered_coords.get(i);\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\ntemplate <typename T>\ntnsr::ii<T, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::apply_second_deriv(\n    const tnsr::I<T, VolumeDim, Fr>& centered_coords, const Scalar<T>& gaussian,\n    const tnsr::i<T, VolumeDim, Fr>& d_gaussian) const {\n  auto result = make_with_value<tnsr::ii<T, VolumeDim, Fr>>(get(gaussian), 0.0);\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = i; j < VolumeDim; ++j) {\n      result.get(i, j) = centered_coords.get(i) * d_gaussian.get(j);\n      if (i == j) {\n        result.get(i, i) += get(gaussian);\n      }\n      result.get(i, j) *= -2.0 * square(inverse_width_);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\ntemplate <typename T>\ntnsr::iii<T, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::apply_third_deriv(\n    const tnsr::I<T, VolumeDim, Fr>& centered_coords, const Scalar<T>& gaussian,\n    const tnsr::i<T, VolumeDim, Fr>& d_gaussian,\n    const tnsr::ii<T, VolumeDim, Fr>& d2_gaussian) const {\n  auto result =\n      make_with_value<tnsr::iii<T, VolumeDim, Fr>>(get(gaussian), 0.0);\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = i; j < VolumeDim; ++j) {\n      for (size_t k = j; k < VolumeDim; ++k) {\n        result.get(i, j, k) = centered_coords.get(j) * d2_gaussian.get(i, k);\n        if (j == k) {\n          result.get(i, j, k) += d_gaussian.get(i);\n        }\n        if (i == j) {\n          result.get(i, j, k) += d_gaussian.get(k);\n        }\n        result.get(i, j, k) *= -2.0 * square(inverse_width_);\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nScalar<double> Gaussian<VolumeDim, Fr>::operator()(\n    const tnsr::I<double, VolumeDim, Fr>& x) const {\n  return apply_call_operator(centered_coordinates(x));\n}\ntemplate <size_t VolumeDim, typename Fr>\nScalar<DataVector> Gaussian<VolumeDim, Fr>::operator()(\n    const tnsr::I<DataVector, VolumeDim, Fr>& x) const {\n  return apply_call_operator(centered_coordinates(x));\n}\n\ntemplate <size_t VolumeDim, typename Fr>\ntnsr::i<double, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::first_deriv(\n    const tnsr::I<double, VolumeDim, Fr>& x) const {\n  const tnsr::I<double, VolumeDim, Fr>& centered_coords =\n      centered_coordinates(x);\n  return apply_first_deriv(centered_coords,\n                           apply_call_operator(centered_coords));\n}\ntemplate <size_t VolumeDim, typename Fr>\ntnsr::i<DataVector, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::first_deriv(\n    const tnsr::I<DataVector, VolumeDim, Fr>& x) const {\n  const tnsr::I<DataVector, VolumeDim, Fr>& centered_coords =\n      centered_coordinates(x);\n  return apply_first_deriv(centered_coords,\n                           apply_call_operator(centered_coords));\n}\n\ntemplate <size_t VolumeDim, typename Fr>\ntnsr::ii<double, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::second_deriv(\n    const tnsr::I<double, VolumeDim, Fr>& x) const {\n  const tnsr::I<double, VolumeDim, Fr> centered_coords =\n      centered_coordinates(x);\n  const Scalar<double>& gauss = apply_call_operator(centered_coords);\n  return apply_second_deriv(centered_coords, gauss,\n                            apply_first_deriv(centered_coords, gauss));\n}\ntemplate <size_t VolumeDim, typename Fr>\ntnsr::ii<DataVector, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::second_deriv(\n    const tnsr::I<DataVector, VolumeDim, Fr>& x) const {\n  const tnsr::I<DataVector, VolumeDim, Fr> centered_coords =\n      centered_coordinates(x);\n  const Scalar<DataVector>& gauss = apply_call_operator(centered_coords);\n  return apply_second_deriv(centered_coords, gauss,\n                            apply_first_deriv(centered_coords, gauss));\n}\n\ntemplate <size_t VolumeDim, typename Fr>\ntnsr::iii<double, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::third_deriv(\n    const tnsr::I<double, VolumeDim, Fr>& x) const {\n  const tnsr::I<double, VolumeDim, Fr> centered_coords =\n      centered_coordinates(x);\n  const Scalar<double>& gauss = apply_call_operator(centered_coords);\n  const tnsr::i<double, VolumeDim, Fr> d_gauss =\n      apply_first_deriv(centered_coords, gauss);\n  return apply_third_deriv(centered_coords, gauss, d_gauss,\n                           apply_second_deriv(centered_coords, gauss, d_gauss));\n}\ntemplate <size_t VolumeDim, typename Fr>\ntnsr::iii<DataVector, VolumeDim, Fr> Gaussian<VolumeDim, Fr>::third_deriv(\n    const tnsr::I<DataVector, VolumeDim, Fr>& x) const {\n  const tnsr::I<DataVector, VolumeDim, Fr> centered_coords =\n      centered_coordinates(x);\n  const Scalar<DataVector>& gauss = apply_call_operator(centered_coords);\n  const tnsr::i<DataVector, VolumeDim, Fr> d_gauss =\n      apply_first_deriv(centered_coords, gauss);\n  return apply_third_deriv(centered_coords, gauss, d_gauss,\n                           apply_second_deriv(centered_coords, gauss, d_gauss));\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nbool Gaussian<VolumeDim, Fr>::operator==(\n    const MathFunction<VolumeDim, Fr>& other) const {\n  const auto* derived_other =\n      dynamic_cast<const Gaussian<VolumeDim, Fr>*>(&other);\n  return derived_other != nullptr and (*this == *derived_other);\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nbool Gaussian<VolumeDim, Fr>::operator!=(\n    const MathFunction<VolumeDim, Fr>& other) const {\n  return not(*this == other);\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nbool Gaussian<VolumeDim, Fr>::operator==(\n    const Gaussian<VolumeDim, Fr>& other) const {\n  return this->amplitude_ == other.amplitude_ and\n         this->inverse_width_ == other.inverse_width_ and\n         this->center_ == other.center_;\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nbool Gaussian<VolumeDim, Fr>::operator!=(\n    const Gaussian<VolumeDim, Fr>& other) const {\n  return not(*this == other);\n}\n\ntemplate <size_t VolumeDim, typename Fr>\nvoid Gaussian<VolumeDim, Fr>::pup(PUP::er& p) {\n  MathFunction<VolumeDim, Fr>::pup(p);\n  p | amplitude_;\n  p | inverse_width_;\n  p | center_;\n}\n}  // namespace MathFunctions\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define INSTANTIATE(_, data) \\\n  template class MathFunctions::Gaussian<DIM(data), FRAME(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef FRAME\n#undef INSTANTIATE\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                        \\\n  template DTYPE(data)                                              \\\n      MathFunctions::Gaussian<1, FRAME(data)>::apply_call_operator( \\\n          const DTYPE(data) & x) const;                             \\\n  template DTYPE(data)                                              \\\n      MathFunctions::Gaussian<1, FRAME(data)>::apply_first_deriv(   \\\n          const DTYPE(data) & x) const;                             \\\n  template DTYPE(data)                                              \\\n      MathFunctions::Gaussian<1, FRAME(data)>::apply_second_deriv(  \\\n          const DTYPE(data) & x) const;                             \\\n  template DTYPE(data)                                              \\\n      MathFunctions::Gaussian<1, FRAME(data)>::apply_third_deriv(   \\\n          const DTYPE(data) & x) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template tnsr::I<DTYPE(data), DIM(data), FRAME(data)>                      \\\n  MathFunctions::Gaussian<DIM(data), FRAME(data)>::centered_coordinates(     \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x) const;          \\\n  template Scalar<DTYPE(data)>                                               \\\n  MathFunctions::Gaussian<DIM(data), FRAME(data)>::apply_call_operator(      \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x) const;          \\\n  template tnsr::i<DTYPE(data), DIM(data), FRAME(data)>                      \\\n  MathFunctions::Gaussian<DIM(data), FRAME(data)>::apply_first_deriv(        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x,                 \\\n      const Scalar<DTYPE(data)>& gaussian) const;                            \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>                     \\\n  MathFunctions::Gaussian<DIM(data), FRAME(data)>::apply_second_deriv(       \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x,                 \\\n      const Scalar<DTYPE(data)>& gaussian,                                   \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& d_gaussian) const; \\\n  template tnsr::iii<DTYPE(data), DIM(data), FRAME(data)>                    \\\n  MathFunctions::Gaussian<DIM(data), FRAME(data)>::apply_third_deriv(        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x,                 \\\n      const Scalar<DTYPE(data)>& gaussian,                                   \\\n      const tnsr::i<DTYPE(data), DIM(data), FRAME(data)>& d_gaussian,        \\\n      const tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>& d2_gaussian) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (2, 3), (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/Gaussian.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines MathFunctions::Gaussian.\n\n#pragma once\n\n#include <array>\n#include <memory>\n#include <pup.h>\n\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace MathFunctions {\ntemplate <size_t VolumeDim, typename Fr>\nclass Gaussian;\n\n/*!\n *  \\ingroup MathFunctionsGroup\n *  \\brief 1D Gaussian \\f$f = A \\exp\\left(-\\frac{(x-x_0)^2}{w^2}\\right)\\f$\n *\n *  \\details Input file options are: Amplitude, Width, and Center. The function\n *  takes input of type `double` or `DataVector` and returns\n *  the same type as the input type.\n */\ntemplate <typename Fr>\nclass Gaussian<1, Fr> : public MathFunction<1, Fr> {\n public:\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help = {\"The amplitude.\"};\n  };\n\n  struct Width {\n    using type = double;\n    static constexpr Options::String help = {\"The width.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  struct Center {\n    using type = double;\n    static constexpr Options::String help = {\"The center.\"};\n  };\n  using options = tmpl::list<Amplitude, Width, Center>;\n\n  static constexpr Options::String help = {\n      \"Computes a Gaussian about an arbitrary coordinate center with given \"\n      \"width and amplitude\"};\n\n  WRAPPED_PUPable_decl_base_template(SINGLE_ARG(MathFunction<1, Fr>),\n                                     Gaussian);  // NOLINT\n\n  explicit Gaussian(CkMigrateMessage* /*unused*/) {}\n\n  Gaussian(double amplitude, double width, double center);\n  Gaussian(double amplitude, double width, const std::array<double, 1>& center);\n\n  Gaussian() = default;\n  std::unique_ptr<MathFunction<1, Fr>> get_clone() const override;\n\n  double operator()(const double& x) const override;\n  DataVector operator()(const DataVector& x) const override;\n  using MathFunction<1, Fr>::operator();\n\n  double first_deriv(const double& x) const override;\n  DataVector first_deriv(const DataVector& x) const override;\n  using MathFunction<1, Fr>::first_deriv;\n\n  double second_deriv(const double& x) const override;\n  DataVector second_deriv(const DataVector& x) const override;\n  using MathFunction<1, Fr>::second_deriv;\n\n  double third_deriv(const double& x) const override;\n  DataVector third_deriv(const DataVector& x) const override;\n  using MathFunction<1, Fr>::third_deriv;\n\n  bool operator==(const MathFunction<1, Fr>& other) const override;\n  bool operator!=(const MathFunction<1, Fr>& other) const override;\n\n  bool operator==(const Gaussian<1, Fr>& other) const;\n  bool operator!=(const Gaussian<1, Fr>& other) const;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double amplitude_{};\n  double inverse_width_{};\n  double center_{};\n\n  template <typename T>\n  T apply_call_operator(const T& x) const;\n  template <typename T>\n  T apply_first_deriv(const T& x) const;\n  template <typename T>\n  T apply_second_deriv(const T& x) const;\n  template <typename T>\n  T apply_third_deriv(const T& x) const;\n};\n\n/*!\n * \\ingroup MathFunctionsGroup\n * \\brief Gaussian \\f$f = A \\exp\\left(-\\frac{(x-x_0)^2}{w^2}\\right)\\f$\n *\n * \\details Input file options are: Amplitude, Width, and Center. The function\n * takes input coordinates of type `tnsr::I<T, VolumeDim, Fr>`, where `T` is\n * e.g. `double` or `DataVector`, `Fr` is a frame (e.g. `Frame::Inertial`), and\n * `VolumeDim` is the dimension of the spatial volume, i.e. 2 or 3. (The case of\n * VolumeDim == 1 is handled specially by Gaussian<1, T, Frame::Inertial>.)\n */\ntemplate <size_t VolumeDim, typename Fr>\nclass Gaussian : public MathFunction<VolumeDim, Fr> {\n public:\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help = {\"The amplitude.\"};\n  };\n\n  struct Width {\n    using type = double;\n    static constexpr Options::String help = {\"The width.\"};\n    static type lower_bound() { return 0.; }\n  };\n\n  struct Center {\n    using type = std::array<double, VolumeDim>;\n    static constexpr Options::String help = {\"The center.\"};\n  };\n  using options = tmpl::list<Amplitude, Width, Center>;\n\n  static constexpr Options::String help = {\n      \"Computes a Gaussian about an arbitrary coordinate center with given \"\n      \"width and amplitude\"};\n\n  WRAPPED_PUPable_decl_base_template(SINGLE_ARG(MathFunction<VolumeDim, Fr>),\n                                     Gaussian);  // NOLINT\n\n  explicit Gaussian(CkMigrateMessage* /*unused*/) {}\n\n  Gaussian(double amplitude, double width,\n           const std::array<double, VolumeDim>& center);\n  Gaussian() = default;\n  std::unique_ptr<MathFunction<VolumeDim, Fr>> get_clone() const override;\n\n  Scalar<double> operator()(\n      const tnsr::I<double, VolumeDim, Fr>& x) const override;\n  Scalar<DataVector> operator()(\n      const tnsr::I<DataVector, VolumeDim, Fr>& x) const override;\n\n  tnsr::i<double, VolumeDim, Fr> first_deriv(\n      const tnsr::I<double, VolumeDim, Fr>& x) const override;\n  tnsr::i<DataVector, VolumeDim, Fr> first_deriv(\n      const tnsr::I<DataVector, VolumeDim, Fr>& x) const override;\n\n  tnsr::ii<double, VolumeDim, Fr> second_deriv(\n      const tnsr::I<double, VolumeDim, Fr>& x) const override;\n  tnsr::ii<DataVector, VolumeDim, Fr> second_deriv(\n      const tnsr::I<DataVector, VolumeDim, Fr>& x) const override;\n\n  tnsr::iii<double, VolumeDim, Fr> third_deriv(\n      const tnsr::I<double, VolumeDim, Fr>& x) const override;\n  tnsr::iii<DataVector, VolumeDim, Fr> third_deriv(\n      const tnsr::I<DataVector, VolumeDim, Fr>& x) const override;\n\n  bool operator==(const MathFunction<VolumeDim, Fr>& other) const override;\n  bool operator!=(const MathFunction<VolumeDim, Fr>& other) const override;\n\n  bool operator==(const Gaussian<VolumeDim, Fr>& other) const;\n  bool operator!=(const Gaussian<VolumeDim, Fr>& other) const;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double amplitude_{};\n  double inverse_width_{};\n  std::array<double, VolumeDim> center_{};\n\n  template <typename T>\n  tnsr::I<T, VolumeDim, Fr> centered_coordinates(\n      const tnsr::I<T, VolumeDim, Fr>& x) const;\n  template <typename T>\n  Scalar<T> apply_call_operator(\n      const tnsr::I<T, VolumeDim, Fr>& centered_coords) const;\n  template <typename T>\n  tnsr::i<T, VolumeDim, Fr> apply_first_deriv(\n      const tnsr::I<T, VolumeDim, Fr>& centered_coords,\n      const Scalar<T>& gaussian) const;\n  template <typename T>\n  tnsr::ii<T, VolumeDim, Fr> apply_second_deriv(\n      const tnsr::I<T, VolumeDim, Fr>& centered_coords,\n      const Scalar<T>& gaussian,\n      const tnsr::i<T, VolumeDim, Fr>& d_gaussian) const;\n  template <typename T>\n  tnsr::iii<T, VolumeDim, Fr> apply_third_deriv(\n      const tnsr::I<T, VolumeDim, Fr>& centered_coords,\n      const Scalar<T>& gaussian, const tnsr::i<T, VolumeDim, Fr>& d_gaussian,\n      const tnsr::ii<T, VolumeDim, Fr>& d2_gaussian) const;\n};\n\ntemplate <size_t VolumeDim, typename Fr>\nbool operator!=(const Gaussian<VolumeDim, Fr>& lhs,\n                const Gaussian<VolumeDim, Fr>& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace MathFunctions\n\n/// \\cond\ntemplate <size_t VolumeDim, typename Fr>\nPUP::able::PUP_ID MathFunctions::Gaussian<VolumeDim, Fr>::my_PUP_ID =\n    0;  // NOLINT\n\ntemplate <typename Fr>\nPUP::able::PUP_ID MathFunctions::Gaussian<1, Fr>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/MathFunction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines base-class MathFunction.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\ingroup MathFunctionsGroup\n/// Holds classes implementing MathFunction (functions \\f$R^n \\to R\\f$).\nnamespace MathFunctions {}\n\n/*!\n * \\ingroup MathFunctionsGroup\n * Encodes a function \\f$R^n \\to R\\f$ where n is `VolumeDim`.\n */\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction;\n\n/*!\n * \\ingroup MathFunctionsGroup\n * Encodes a function \\f$R^n \\to R\\f$ where n is `VolumeDim` and where the\n * function input (i.e., the spatial coordinates) is given as a rank-1 tensor.\n */\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction : public PUP::able {\n public:\n  constexpr static size_t volume_dim = VolumeDim;\n  using frame = Fr;\n\n  WRAPPED_PUPable_abstract(MathFunction);  // NOLINT\n  MathFunction() = default;\n  virtual std::unique_ptr<MathFunction> get_clone() const = 0;\n\n  /// @{\n  /// Returns the value of the function at the coordinate 'x'.\n  virtual Scalar<double> operator()(\n      const tnsr::I<double, VolumeDim, Fr>& x) const = 0;\n  virtual Scalar<DataVector> operator()(\n      const tnsr::I<DataVector, VolumeDim, Fr>& x) const = 0;\n  /// @}\n\n  /// @{\n  /// Returns the first partial derivatives of the function at 'x'.\n  virtual tnsr::i<double, VolumeDim, Fr> first_deriv(\n      const tnsr::I<double, VolumeDim, Fr>& x) const = 0;\n  virtual tnsr::i<DataVector, VolumeDim, Fr> first_deriv(\n      const tnsr::I<DataVector, VolumeDim, Fr>& x) const = 0;\n  /// @}\n\n  /// @{\n  /// Returns the second partial derivatives of the function at 'x'.\n  virtual tnsr::ii<double, VolumeDim, Fr> second_deriv(\n      const tnsr::I<double, VolumeDim, Fr>& x) const = 0;\n  virtual tnsr::ii<DataVector, VolumeDim, Fr> second_deriv(\n      const tnsr::I<DataVector, VolumeDim, Fr>& x) const = 0;\n  /// @}\n\n  /// @{\n  /// Returns the third partial derivatives of the function at 'x'.\n  virtual tnsr::iii<double, VolumeDim, Fr> third_deriv(\n      const tnsr::I<double, VolumeDim, Fr>& x) const = 0;\n  virtual tnsr::iii<DataVector, VolumeDim, Fr> third_deriv(\n      const tnsr::I<DataVector, VolumeDim, Fr>& x) const = 0;\n  /// @}\n\n  virtual bool operator==(const MathFunction<VolumeDim, Fr>& other) const = 0;\n  virtual bool operator!=(const MathFunction<VolumeDim, Fr>& other) const = 0;\n};\n\n/*!\n * \\ingroup MathFunctionsGroup\n * Partial template specialization of MathFunction which encodes a\n * function \\f$R \\to R\\f$. In this 1D specialization, the input and output can\n * be `Tensors`, `doubles`, or `DataVectors`.\n */\ntemplate <typename Fr>\nclass MathFunction<1, Fr> : public PUP::able {\n public:\n  constexpr static size_t volume_dim = 1;\n  using frame = Fr;\n\n  WRAPPED_PUPable_abstract(MathFunction);  // NOLINT\n  MathFunction() = default;\n  virtual std::unique_ptr<MathFunction> get_clone() const = 0;\n\n  /// Returns the function value at the coordinate 'x'\n  virtual double operator()(const double& x) const = 0;\n  virtual DataVector operator()(const DataVector& x) const = 0;\n  Scalar<double> operator()(const tnsr::I<double, 1, Fr>& x) const {\n    return Scalar<double>{{{operator()(get<0>(x))}}};\n  }\n  Scalar<DataVector> operator()(const tnsr::I<DataVector, 1, Fr>& x) const {\n    return Scalar<DataVector>{{{operator()(get<0>(x))}}};\n  }\n\n  /// Returns the first derivative at 'x'\n  virtual double first_deriv(const double& x) const = 0;\n  virtual DataVector first_deriv(const DataVector& x) const = 0;\n  tnsr::i<double, 1, Fr> first_deriv(const tnsr::I<double, 1, Fr>& x) const {\n    auto result = make_with_value<tnsr::i<double, 1, Fr>>(get<0>(x), 0.0);\n    get<0>(result) = first_deriv(get<0>(x));\n    return result;\n  }\n  tnsr::i<DataVector, 1, Fr> first_deriv(\n      const tnsr::I<DataVector, 1, Fr>& x) const {\n    auto result = make_with_value<tnsr::i<DataVector, 1, Fr>>(get<0>(x), 0.0);\n    get<0>(result) = first_deriv(get<0>(x));\n    return result;\n  }\n\n  /// Returns the second derivative at 'x'\n  virtual double second_deriv(const double& x) const = 0;\n  virtual DataVector second_deriv(const DataVector& x) const = 0;\n  tnsr::ii<double, 1, Fr> second_deriv(const tnsr::I<double, 1, Fr>& x) const {\n    auto result = make_with_value<tnsr::ii<double, 1, Fr>>(get<0>(x), 0.0);\n    get<0, 0>(result) = second_deriv(get<0>(x));\n    return result;\n  }\n  tnsr::ii<DataVector, 1, Fr> second_deriv(\n      const tnsr::I<DataVector, 1, Fr>& x) const {\n    auto result = make_with_value<tnsr::ii<DataVector, 1, Fr>>(get<0>(x), 0.0);\n    get<0, 0>(result) = second_deriv(get<0>(x));\n    return result;\n  }\n\n  /// Returns the third derivative at 'x'\n  virtual double third_deriv(const double& x) const = 0;\n  virtual DataVector third_deriv(const DataVector& x) const = 0;\n  tnsr::iii<double, 1, Fr> third_deriv(const tnsr::I<double, 1, Fr>& x) const {\n    auto result = make_with_value<tnsr::iii<double, 1, Fr>>(get<0>(x), 0.0);\n    get<0, 0, 0>(result) = third_deriv(get<0>(x));\n    return result;\n  }\n  tnsr::iii<DataVector, 1, Fr> third_deriv(\n      const tnsr::I<DataVector, 1, Fr>& x) const {\n    auto result = make_with_value<tnsr::iii<DataVector, 1, Fr>>(get<0>(x), 0.0);\n    get<0, 0, 0>(result) = third_deriv(get<0>(x));\n    return result;\n  }\n  virtual bool operator==(const MathFunction<1, Fr>& other) const = 0;\n  virtual bool operator!=(const MathFunction<1, Fr>& other) const = 0;\n};\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/PowX.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace MathFunctions {\n\ntemplate <typename Fr>\nPowX<1, Fr>::PowX(const int power) : power_(power) {}\n\ntemplate <typename Fr>\nstd::unique_ptr<MathFunction<1, Fr>> PowX<1, Fr>::get_clone() const {\n  return std::make_unique<PowX<1, Fr>>(*this);\n}\n\ntemplate <typename Fr>\ndouble PowX<1, Fr>::operator()(const double& x) const {\n  return apply_call_operator(x);\n}\n\ntemplate <typename Fr>\nDataVector PowX<1, Fr>::operator()(const DataVector& x) const {\n  return apply_call_operator(x);\n}\n\ntemplate <typename Fr>\ndouble PowX<1, Fr>::first_deriv(const double& x) const {\n  return apply_first_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector PowX<1, Fr>::first_deriv(const DataVector& x) const {\n  return apply_first_deriv(x);\n}\n\ntemplate <typename Fr>\ndouble PowX<1, Fr>::second_deriv(const double& x) const {\n  return apply_second_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector PowX<1, Fr>::second_deriv(const DataVector& x) const {\n  return apply_second_deriv(x);\n}\n\ntemplate <typename Fr>\ndouble PowX<1, Fr>::third_deriv(const double& x) const {\n  return apply_third_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector PowX<1, Fr>::third_deriv(const DataVector& x) const {\n  return apply_third_deriv(x);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT PowX<1, Fr>::apply_call_operator(const T& x) const {\n  return pow(x, power_);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT PowX<1, Fr>::apply_first_deriv(const T& x) const {\n  return 0 == power_ ? make_with_value<T>(x, 0.0) : power_ * pow(x, power_ - 1);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT PowX<1, Fr>::apply_second_deriv(const T& x) const {\n  return 0 == power_ or 1 == power_\n             ? make_with_value<T>(x, 0.0)\n             : power_ * (power_ - 1) * pow(x, power_ - 2);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT PowX<1, Fr>::apply_third_deriv(const T& x) const {\n  return 0 == power_ or 1 == power_ or 2 == power_\n             ? make_with_value<T>(x, 0.0)\n             : power_ * (power_ - 1) * (power_ - 2) * pow(x, power_ - 3);\n}\n\ntemplate <typename Fr>\nbool PowX<1, Fr>::operator==(const MathFunction<1, Fr>& other) const {\n  const auto* derived_other = dynamic_cast<const PowX<1, Fr>*>(&other);\n  return derived_other != nullptr and (*this == *derived_other);\n}\n\ntemplate <typename Fr>\nbool PowX<1, Fr>::operator!=(const MathFunction<1, Fr>& other) const {\n  return not(*this == other);\n}\n\ntemplate <typename Fr>\nbool PowX<1, Fr>::operator==(const PowX<1, Fr>& other) const {\n  return this->power_ == other.power_;\n}\n\ntemplate <typename Fr>\nbool PowX<1, Fr>::operator!=(const PowX<1, Fr>& other) const {\n  return not(*this == other);\n}\n\ntemplate <typename Fr>\nvoid PowX<1, Fr>::pup(PUP::er& p) {\n  MathFunction<1, Fr>::pup(p);\n  p | power_;\n}\n\ntemplate class MathFunctions::PowX<1, Frame::Grid>;\ntemplate class MathFunctions::PowX<1, Frame::Inertial>;\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                   \\\n  template DTYPE(data)                                                         \\\n      MathFunctions::PowX<1, FRAME(data)>::apply_call_operator(                \\\n          const DTYPE(data) & x) const;                                        \\\n  template DTYPE(data) MathFunctions::PowX<1, FRAME(data)>::apply_first_deriv( \\\n      const DTYPE(data) & x) const;                                            \\\n  template DTYPE(data)                                                         \\\n      MathFunctions::PowX<1, FRAME(data)>::apply_second_deriv(                 \\\n          const DTYPE(data) & x) const;                                        \\\n  template DTYPE(data) MathFunctions::PowX<1, FRAME(data)>::apply_third_deriv( \\\n      const DTYPE(data) & x) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n}  // namespace MathFunctions\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/PowX.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines MathFunctions::PowX.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace MathFunctions {\ntemplate <size_t VolumeDim, typename Fr>\nclass PowX;\n\n/*!\n * \\ingroup MathFunctionsGroup\n * \\brief Power of X \\f$f(x)=x^X\\f$\n */\ntemplate <typename Fr>\nclass PowX<1, Fr> : public MathFunction<1, Fr> {\n public:\n  struct Power {\n    using type = int;\n    static constexpr Options::String help = {\n        \"The power that the double is raised to.\"};\n  };\n  using options = tmpl::list<Power>;\n\n  static constexpr Options::String help = {\n      \"Raises the input value to a given power\"};\n  PowX() = default;\n\n  WRAPPED_PUPable_decl_base_template(SINGLE_ARG(MathFunction<1, Fr>),\n                                     PowX);  // NOLINT\n  std::unique_ptr<MathFunction<1, Fr>> get_clone() const override;\n\n  explicit PowX(int power);\n\n  explicit PowX(CkMigrateMessage* /*unused*/) {}\n\n  double operator()(const double& x) const override;\n  DataVector operator()(const DataVector& x) const override;\n\n  double first_deriv(const double& x) const override;\n  DataVector first_deriv(const DataVector& x) const override;\n\n  double second_deriv(const double& x) const override;\n  DataVector second_deriv(const DataVector& x) const override;\n\n  double third_deriv(const double& x) const override;\n  DataVector third_deriv(const DataVector& x) const override;\n\n  bool operator==(const MathFunction<1, Fr>& other) const override;\n  bool operator!=(const MathFunction<1, Fr>& other) const override;\n\n  bool operator==(const PowX<1, Fr>& other) const;\n  bool operator!=(const PowX<1, Fr>& other) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double power_{};\n\n  template <typename T>\n  T apply_call_operator(const T& x) const;\n  template <typename T>\n  T apply_first_deriv(const T& x) const;\n  template <typename T>\n  T apply_second_deriv(const T& x) const;\n  template <typename T>\n  T apply_third_deriv(const T& x) const;\n};\n\n}  // namespace MathFunctions\n\n/// \\cond\ntemplate <typename Fr>\nPUP::able::PUP_ID MathFunctions::PowX<1, Fr>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/RegisterDerivedWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/MathFunctions/RegisterDerivedWithCharm.hpp\"\n\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace MathFunctions {\nvoid register_derived_with_charm() {\n  register_classes_with_charm<Gaussian<1, Frame::Inertial>,\n                              PowX<1, Frame::Inertial>,\n                              Sinusoid<1, Frame::Inertial>>();\n}\n}  // namespace MathFunctions\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/RegisterDerivedWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace MathFunctions {\nvoid register_derived_with_charm();\n}  // namespace MathFunctions\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/Sinusoid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace MathFunctions {\ntemplate <typename Fr>\nSinusoid<1, Fr>::Sinusoid(const double amplitude, const double wavenumber,\n                          const double phase)\n    : amplitude_(amplitude), wavenumber_(wavenumber), phase_(phase) {}\n\ntemplate <typename Fr>\nstd::unique_ptr<MathFunction<1, Fr>> Sinusoid<1, Fr>::get_clone() const {\n  return std::make_unique<Sinusoid<1, Fr>>(*this);\n}\n\ntemplate <typename Fr>\ndouble Sinusoid<1, Fr>::operator()(const double& x) const {\n  return apply_call_operator(x);\n}\n\ntemplate <typename Fr>\nDataVector Sinusoid<1, Fr>::operator()(const DataVector& x) const {\n  return apply_call_operator(x);\n}\n\ntemplate <typename Fr>\ndouble Sinusoid<1, Fr>::first_deriv(const double& x) const {\n  return apply_first_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector Sinusoid<1, Fr>::first_deriv(const DataVector& x) const {\n  return apply_first_deriv(x);\n}\n\ntemplate <typename Fr>\ndouble Sinusoid<1, Fr>::second_deriv(const double& x) const {\n  return apply_second_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector Sinusoid<1, Fr>::second_deriv(const DataVector& x) const {\n  return apply_second_deriv(x);\n}\n\ntemplate <typename Fr>\ndouble Sinusoid<1, Fr>::third_deriv(const double& x) const {\n  return apply_third_deriv(x);\n}\n\ntemplate <typename Fr>\nDataVector Sinusoid<1, Fr>::third_deriv(const DataVector& x) const {\n  return apply_third_deriv(x);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT Sinusoid<1, Fr>::apply_call_operator(const T& x) const {\n  return amplitude_ * sin(wavenumber_ * x + phase_);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT Sinusoid<1, Fr>::apply_first_deriv(const T& x) const {\n  return wavenumber_ * amplitude_ * cos(wavenumber_ * x + phase_);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT Sinusoid<1, Fr>::apply_second_deriv(const T& x) const {\n  return -amplitude_ * square(wavenumber_) * sin(wavenumber_ * x + phase_);\n}\n\ntemplate <typename Fr>\ntemplate <typename T>\nT Sinusoid<1, Fr>::apply_third_deriv(const T& x) const {\n  return -amplitude_ * cube(wavenumber_) * cos(wavenumber_ * x + phase_);\n}\n\ntemplate <typename Fr>\nbool Sinusoid<1, Fr>::operator==(const MathFunction<1, Fr>& other) const {\n  const auto* derived_other = dynamic_cast<const Sinusoid<1, Fr>*>(&other);\n  if (derived_other != nullptr) {\n    return (this->amplitude_ == derived_other->amplitude_) and\n           (this->wavenumber_ == derived_other->wavenumber_) and\n           (this->phase_ == derived_other->phase_);\n  }\n  return false;\n}\n\ntemplate <typename Fr>\nbool Sinusoid<1, Fr>::operator!=(const MathFunction<1, Fr>& other) const {\n  return not(*this == other);\n}\n\ntemplate <typename Fr>\nbool Sinusoid<1, Fr>::operator==(const Sinusoid<1, Fr>& other) const {\n  return (this->amplitude_ == other.amplitude_) and\n         (this->wavenumber_ == other.wavenumber_) and\n         (this->phase_ == other.phase_);\n}\n\ntemplate <typename Fr>\nbool Sinusoid<1, Fr>::operator!=(const Sinusoid<1, Fr>& other) const {\n  return not(*this == other);\n}\n\ntemplate <typename Fr>\nvoid Sinusoid<1, Fr>::pup(PUP::er& p) {\n  MathFunction<1, Fr>::pup(p);\n  p | amplitude_;\n  p | wavenumber_;\n  p | phase_;\n}\n}  // namespace MathFunctions\n\ntemplate class MathFunctions::Sinusoid<1, Frame::Grid>;\ntemplate class MathFunctions::Sinusoid<1, Frame::Inertial>;\n\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                        \\\n  template DTYPE(data)                                              \\\n      MathFunctions::Sinusoid<1, FRAME(data)>::apply_call_operator( \\\n          const DTYPE(data) & x) const;                             \\\n  template DTYPE(data)                                              \\\n      MathFunctions::Sinusoid<1, FRAME(data)>::apply_first_deriv(   \\\n          const DTYPE(data) & x) const;                             \\\n  template DTYPE(data)                                              \\\n      MathFunctions::Sinusoid<1, FRAME(data)>::apply_second_deriv(  \\\n          const DTYPE(data) & x) const;                             \\\n  template DTYPE(data)                                              \\\n      MathFunctions::Sinusoid<1, FRAME(data)>::apply_third_deriv(   \\\n          const DTYPE(data) & x) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Frame::Grid, Frame::Inertial),\n                        (double, DataVector))\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/Sinusoid.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines MathFunctions::Sinusoid.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace MathFunctions {\ntemplate <size_t VolumeDim, typename Fr>\nclass Sinusoid;\n\n/*!\n *  \\ingroup MathFunctionsGroup\n *  \\brief Sinusoid \\f$f = A \\sin\\left(k x + \\delta \\right)\\f$.\n *\n *  \\details Input file options are: Amplitude, Phase, and Wavenumber\n */\ntemplate <typename Fr>\nclass Sinusoid<1, Fr> : public MathFunction<1, Fr> {\n public:\n  struct Amplitude {\n    using type = double;\n    static constexpr Options::String help = {\"The amplitude.\"};\n  };\n\n  struct Wavenumber {\n    using type = double;\n    static constexpr Options::String help = {\"The wavenumber.\"};\n  };\n\n  struct Phase {\n    using type = double;\n    static constexpr Options::String help = {\"The phase shift.\"};\n  };\n  using options = tmpl::list<Amplitude, Wavenumber, Phase>;\n\n  static constexpr Options::String help = {\n      \"Applies a Sinusoid function to the input value\"};\n\n  Sinusoid(double amplitude, double wavenumber, double phase);\n  Sinusoid() = default;\n  std::unique_ptr<MathFunction<1, Fr>> get_clone() const override;\n\n  WRAPPED_PUPable_decl_base_template(SINGLE_ARG(MathFunction<1, Fr>),\n                                     Sinusoid);  // NOLINT\n\n  explicit Sinusoid(CkMigrateMessage* /*unused*/) {}\n\n  double operator()(const double& x) const override;\n  DataVector operator()(const DataVector& x) const override;\n\n  double first_deriv(const double& x) const override;\n  DataVector first_deriv(const DataVector& x) const override;\n\n  double second_deriv(const double& x) const override;\n  DataVector second_deriv(const DataVector& x) const override;\n\n  double third_deriv(const double& x) const override;\n  DataVector third_deriv(const DataVector& x) const override;\n\n  bool operator==(const MathFunction<1, Fr>& other) const override;\n  bool operator!=(const MathFunction<1, Fr>& other) const override;\n\n  bool operator==(const Sinusoid<1, Fr>& other) const;\n  bool operator!=(const Sinusoid<1, Fr>& other) const;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double amplitude_{};\n  double wavenumber_{};\n  double phase_{};\n\n  template <typename T>\n  T apply_call_operator(const T& x) const;\n  template <typename T>\n  T apply_first_deriv(const T& x) const;\n  template <typename T>\n  T apply_second_deriv(const T& x) const;\n  template <typename T>\n  T apply_third_deriv(const T& x) const;\n};\n\n}  // namespace MathFunctions\n\n/// \\cond\ntemplate <typename Fr>\nPUP::able::PUP_ID MathFunctions::Sinusoid<1, Fr>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/TensorProduct.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/MathFunctions/TensorProduct.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace MathFunctions {\n\ntemplate <size_t Dim>\nTensorProduct<Dim>::TensorProduct(\n    double scale,\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, Dim>&&\n        functions)\n    : scale_(scale), functions_(std::move(functions)) {}\n\ntemplate <size_t Dim>\nTensorProduct<Dim>::TensorProduct(const TensorProduct<Dim>& other)\n    : scale_(other.scale_) {\n  for (size_t i = 0; i < Dim; ++i) {\n    functions_.at(i) = other.functions_.at(i)->get_clone();\n  }\n}\n\ntemplate <size_t Dim>\nTensorProduct<Dim>& TensorProduct<Dim>::operator=(\n    const TensorProduct<Dim>& other) {\n  scale_ = other.scale_;\n  for (size_t i = 0; i < Dim; ++i) {\n    functions_.at(i) = other.functions_.at(i)->get_clone();\n  }\n  return *this;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\nScalar<T> TensorProduct<Dim>::operator()(const tnsr::I<T, Dim>& x) const {\n  auto result = make_with_value<Scalar<T>>(x, scale_);\n  for (size_t d = 0; d < Dim; ++d) {\n    result.get() *= gsl::at(functions_, d)->operator()(x.get(d));\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::i<T, Dim> TensorProduct<Dim>::first_derivatives(\n    const tnsr::I<T, Dim>& x) const {\n  auto result = make_with_value<tnsr::i<T, Dim>>(x, scale_);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t d = 0; d < Dim; ++d) {\n      if (i == d) {\n        result.get(i) *= gsl::at(functions_, d)->first_deriv(x.get(d));\n      } else {\n        result.get(i) *= gsl::at(functions_, d)->operator()(x.get(d));\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\ntemplate <typename T>\ntnsr::ii<T, Dim> TensorProduct<Dim>::second_derivatives(\n    const tnsr::I<T, Dim>& x) const {\n  auto result = make_with_value<tnsr::ii<T, Dim>>(x, scale_);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t d = 0; d < Dim; ++d) {\n      if (i == d) {\n        result.get(i, i) *= gsl::at(functions_, d)->second_deriv(x.get(d));\n      } else {\n        result.get(i, i) *= gsl::at(functions_, d)->operator()(x.get(d));\n      }\n    }\n    for (size_t j = i + 1; j < Dim; ++j) {\n      for (size_t d = 0; d < Dim; ++d) {\n        if (i == d or j == d) {\n          result.get(i, j) *= gsl::at(functions_, d)->first_deriv(x.get(d));\n        } else {\n          result.get(i, j) *= gsl::at(functions_, d)->operator()(x.get(d));\n        }\n      }\n    }\n  }\n  return result;\n}\ntemplate <size_t Dim>\nbool operator==(const TensorProduct<Dim>& lhs, const TensorProduct<Dim>& rhs) {\n  bool are_equal = lhs.scale_ == rhs.scale_;\n  for (size_t i = 0; i < Dim; ++i) {\n    are_equal = are_equal and *lhs.functions_.at(i) == *rhs.functions_.at(i);\n  }\n  return are_equal;\n}\ntemplate <size_t Dim>\nbool operator!=(const TensorProduct<Dim>& lhs, const TensorProduct<Dim>& rhs) {\n  return !(lhs == rhs);\n}\n\n}  // namespace MathFunctions\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                   \\\n  template Scalar<DTYPE(data)>                                 \\\n  MathFunctions::TensorProduct<DIM(data)>::operator()(         \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x) const;         \\\n  template tnsr::i<DTYPE(data), DIM(data)>                     \\\n  MathFunctions::TensorProduct<DIM(data)>::first_derivatives(  \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x) const;         \\\n  template tnsr::ii<DTYPE(data), DIM(data)>                    \\\n  MathFunctions::TensorProduct<DIM(data)>::second_derivatives( \\\n      const tnsr::I<DTYPE(data), DIM(data)>& x) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector))\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                               \\\n  template class MathFunctions::TensorProduct<DIM(data)>;  \\\n  template bool MathFunctions::operator==(                 \\\n      const MathFunctions::TensorProduct<DIM(data)>& lhs,  \\\n      const MathFunctions::TensorProduct<DIM(data)>& rhs); \\\n  template bool MathFunctions::operator!=(                 \\\n      const MathFunctions::TensorProduct<DIM(data)>& lhs,  \\\n      const MathFunctions::TensorProduct<DIM(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/MathFunctions/TensorProduct.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines a tensor product of one-dimensional MathFunctions\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n\nnamespace MathFunctions {\n\n/// \\ingroup MathFunctionsGroup\n/// \\brief a tensor product of one-dimensional MathFunctions\ntemplate <size_t Dim>\nclass TensorProduct {\n public:\n  TensorProduct(double scale,\n                std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>,\n                           Dim>&& functions);\n  TensorProduct() = default;\n  TensorProduct(const TensorProduct& other);\n  TensorProduct(TensorProduct&&) = default;\n  TensorProduct& operator=(const TensorProduct& other);\n  TensorProduct& operator=(TensorProduct&&) = default;\n  ~TensorProduct() = default;\n\n  /// The value of the function\n  template <typename T>\n  Scalar<T> operator()(const tnsr::I<T, Dim>& x) const;\n\n  /// The partial derivatives of the function\n  template <typename T>\n  tnsr::i<T, Dim> first_derivatives(const tnsr::I<T, Dim>& x) const;\n\n  /// The second partial derivatives of the function\n  template <typename T>\n  tnsr::ii<T, Dim> second_derivatives(const tnsr::I<T, Dim>& x) const;\n\n private:\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator==(const TensorProduct<LocalDim>& lhs,\n                         const TensorProduct<LocalDim>& rhs);\n  template <size_t LocalDim>\n  // NOLINTNEXTLINE(readability-redundant-declaration)\n  friend bool operator!=(const TensorProduct<LocalDim>& lhs,\n                         const TensorProduct<LocalDim>& rhs);\n  double scale_{1.0};\n  std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, Dim> functions_;\n};\n}  // namespace MathFunctions\n"
  },
  {
    "path": "src/PointwiseFunctions/Punctures/AdmIntegrals.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Punctures/AdmIntegrals.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Punctures {\n\nvoid adm_mass_integrand(const gsl::not_null<Scalar<DataVector>*> result,\n                        const Scalar<DataVector>& field,\n                        const Scalar<DataVector>& alpha,\n                        const Scalar<DataVector>& beta) {\n  get(*result) = get(alpha) * (get(field) + 1.) + 1.;\n  get(*result) = pow<7>(get(*result));\n  get(*result) = 0.5 / M_PI * get(beta) / get(*result);\n}\n\nScalar<DataVector> adm_mass_integrand(const Scalar<DataVector>& field,\n                                      const Scalar<DataVector>& alpha,\n                                      const Scalar<DataVector>& beta) {\n  Scalar<DataVector> result{get(field).size()};\n  adm_mass_integrand(make_not_null(&result), field, alpha, beta);\n  return result;\n}\n\n}  // namespace Punctures\n"
  },
  {
    "path": "src/PointwiseFunctions/Punctures/AdmIntegrals.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Punctures/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures {\n\n/// @{\n/*!\n * \\brief The volume integrand for the ADM mass $M_\\mathrm{ADM}$\n *\n * The ADM mass for Punctures is (Eq. (12.56) in \\cite BaumgarteShapiro)\n *\n * \\begin{equation}\n * M_\\mathrm{ADM} = \\sum_I M_I + \\frac{1}{2\\pi} \\int\n *   \\beta \\left(\\alpha \\left(1 + u\\right) + 1\\right)^{-7}\n * \\end{equation}\n *\n * \\see Punctures\n */\nvoid adm_mass_integrand(const gsl::not_null<Scalar<DataVector>*> result,\n                        const Scalar<DataVector>& field,\n                        const Scalar<DataVector>& alpha,\n                        const Scalar<DataVector>& beta);\n\nScalar<DataVector> adm_mass_integrand(const Scalar<DataVector>& field,\n                                      const Scalar<DataVector>& alpha,\n                                      const Scalar<DataVector>& beta);\n/// @}\n\nnamespace Tags {\n\n/// @{\nstruct AdmMassIntegrand : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief The volume integrand for the ADM mass $M_\\mathrm{ADM}$\n *\n * \\see adm_mass_integrand\n */\nstruct AdmMassIntegrandCompute : AdmMassIntegrand, db::ComputeTag {\n  using base = AdmMassIntegrand;\n  using argument_tags = tmpl::list<Field, Alpha, Beta>;\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<Scalar<DataVector>*>,\n                           const Scalar<DataVector>&, const Scalar<DataVector>&,\n                           const Scalar<DataVector>&)>(&adm_mass_integrand);\n};\n/// @}\n\n}  // namespace Tags\n}  // namespace Punctures\n"
  },
  {
    "path": "src/PointwiseFunctions/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY PuncturesPointwiseFunctions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AdmIntegrals.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AdmIntegrals.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Punctures\n  Utilities\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/PointwiseFunctions/Punctures/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <pybind11/pybind11.h>\n\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/Punctures/AdmIntegrals.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nnamespace Punctures {\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module_::import(\"spectre.DataStructures\");\n  py::module_::import(\"spectre.DataStructures.Tensor\");\n  m.def(\"adm_mass_integrand\",\n        py::overload_cast<const Scalar<DataVector>&, const Scalar<DataVector>&,\n                          const Scalar<DataVector>&>(&adm_mass_integrand),\n        py::arg(\"field\"), py::arg(\"alpha\"), py::arg(\"beta\"));\n}\n\n}  // namespace Punctures\n"
  },
  {
    "path": "src/PointwiseFunctions/Punctures/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyPuncturesPointwiseFunctions\")\n\nspectre_python_add_module(\n  Punctures\n  MODULE_PATH \"PointwiseFunctions\"\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n)\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  PuncturesPointwiseFunctions\n  pybind11::module\n)\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyDataStructures\n  PyTensor\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/Punctures/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarTensorPointwise)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  RampUpFunction.cpp\n  ScalarCharge.cpp\n  ScalarSource.cpp\n  StressEnergy.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ConstraintDampingTags.hpp\n  ConstraintGammas.hpp\n  RampUpFunction.hpp\n  ScalarCharge.hpp\n  SourceTags.hpp\n  ScalarSource.hpp\n  StressEnergy.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  INTERFACE\n  ErrorHandling\n  )\n\nadd_subdirectory(ScalarGaussBonnet)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ConstraintDampingTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n\n/// \\cond\nnamespace ScalarTensor::OptionTags {\nstruct Group;\n}  // namespace ScalarTensor::OptionTags\n/// \\endcond\n\nnamespace ScalarTensor {\nnamespace OptionTags {\n/*!\n * \\brief A DampingFunction to compute the constraint damping parameter\n * \\f$\\gamma_1\\f$.\n */\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma1 {\n  using type =\n      std::unique_ptr<::ConstraintDamping::DampingFunction<VolumeDim, Fr>>;\n  static constexpr Options::String help{\n      \"DampingFunction for damping parameter gamma1\"};\n  using group = ::ScalarTensor::OptionTags::Group;\n};\n\n/*!\n * \\brief A DampingFunction to compute the constraint damping parameter\n * \\f$\\gamma_2\\f$.\n */\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma2 {\n  using type =\n      std::unique_ptr<::ConstraintDamping::DampingFunction<VolumeDim, Fr>>;\n  static constexpr Options::String help{\n      \"DampingFunction for damping parameter gamma2\"};\n  using group = ::ScalarTensor::OptionTags::Group;\n};\n}  // namespace OptionTags\n\nnamespace Tags {\n/// \\copydoc ScalarTensor::OptionTags::DampingFunctionGamma1\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma1 : db::SimpleTag {\n  using DampingFunctionType =\n      ::ConstraintDamping::DampingFunction<VolumeDim, Fr>;\n  using type = std::unique_ptr<DampingFunctionType>;\n  using option_tags = tmpl::list<\n      ::ScalarTensor::OptionTags::DampingFunctionGamma1<VolumeDim, Fr>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& damping_function) {\n    return damping_function->get_clone();\n  }\n};\n\n/// \\copydoc ScalarTensor::OptionTags::DampingFunctionGamma2\ntemplate <size_t VolumeDim, typename Fr>\nstruct DampingFunctionGamma2 : db::SimpleTag {\n  using DampingFunctionType =\n      ::ConstraintDamping::DampingFunction<VolumeDim, Fr>;\n  using type = std::unique_ptr<DampingFunctionType>;\n  using option_tags = tmpl::list<\n      ::ScalarTensor::OptionTags::DampingFunctionGamma2<VolumeDim, Fr>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& damping_function) {\n    return damping_function->get_clone();\n  }\n};\n}  // namespace Tags\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ConstraintGammas.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ConstraintDampingTags.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\nnamespace domain::Tags {\ntemplate <size_t Dim, typename Frame>\nstruct Coordinates;\n}  // namespace domain::Tags\nclass DataVector;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n/// \\endcond\n\nnamespace ScalarTensor::Tags {\n/*!\n * \\brief Computes the constraint damping parameter \\f$\\gamma_1\\f$ from the\n * coordinates and a DampingFunction.\n *\n * \\details Can be retrieved using\n * `CurvedScalarWave::Tags::ConstraintGamma1`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct ConstraintGamma1Compute : ::CurvedScalarWave::Tags::ConstraintGamma1,\n                                 db::ComputeTag {\n  using argument_tags =\n      tmpl::list<DampingFunctionGamma1<SpatialDim, Frame>,\n                 domain::Tags::Coordinates<SpatialDim, Frame>, ::Tags::Time,\n                 ::domain::Tags::FunctionsOfTime>;\n  using return_type = Scalar<DataVector>;\n\n  static constexpr void function(\n      const gsl::not_null<Scalar<DataVector>*> gamma,\n      const ::ConstraintDamping::DampingFunction<SpatialDim, Frame>&\n          damping_function,\n      const tnsr::I<DataVector, SpatialDim, Frame>& coords, const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    damping_function(gamma, coords, time, functions_of_time);\n  }\n\n  using base = ::CurvedScalarWave::Tags::ConstraintGamma1;\n};\n\n/*!\n * \\brief Computes the constraint damping parameter \\f$\\gamma_2\\f$ from the\n * coordinates and a DampingFunction.\n *\n * \\details Can be retrieved using\n * `CurvedScalarWave::::Tags::ConstraintGamma2`.\n */\ntemplate <size_t SpatialDim, typename Frame>\nstruct ConstraintGamma2Compute : ::CurvedScalarWave::Tags::ConstraintGamma2,\n                                 db::ComputeTag {\n  using argument_tags =\n      tmpl::list<DampingFunctionGamma2<SpatialDim, Frame>,\n                 domain::Tags::Coordinates<SpatialDim, Frame>, ::Tags::Time,\n                 ::domain::Tags::FunctionsOfTime>;\n  using return_type = Scalar<DataVector>;\n\n  static constexpr void function(\n      const gsl::not_null<Scalar<DataVector>*> gamma,\n      const ::ConstraintDamping::DampingFunction<SpatialDim, Frame>&\n          damping_function,\n      const tnsr::I<DataVector, SpatialDim, Frame>& coords, const double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) {\n    damping_function(gamma, coords, time, functions_of_time);\n  }\n\n  using base = ::CurvedScalarWave::Tags::ConstraintGamma2;\n};\n}  // namespace ScalarTensor::Tags\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/RampUpFunction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ScalarTensor/RampUpFunction.hpp\"\n\n#include \"Utilities/Math.hpp\"\n\ndouble ScalarTensor::nonic_ramp_function(const double time,\n                                         const double start_time,\n                                         const double ramp_time) {\n  // Ramping on with ramp function given in Eq. B4 of 1911.02588\n  return smoothstep<4>(start_time, start_time + ramp_time, time);\n}\n\ndouble ScalarTensor::nonic_ramp_function(\n    const double time, const std::pair<double, double> start_and_ramp_times) {\n  const double start_time = start_and_ramp_times.first;\n  const double ramp_time = start_and_ramp_times.second;\n  return ScalarTensor::nonic_ramp_function(time, start_time, ramp_time);\n}\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/RampUpFunction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <utility>\n\nnamespace ScalarTensor {\n/// @{\n/*!\n * \\brief Function to smoothly turn on the coupling terms.\n *\n * \\details Implements Eq. B4 of \\cite Okounkova:2019zjf :\n * \\begin{align}\n *    t_*  &\\equiv (t - t_\\mathrm{s})/t_\\mathrm{ramp} \\,, \\\\\n *    F(t) &= t_*^5  (126 + t_* (-420 + t_* (540 + t_* (-315 + 70 t_*))))~,\n * \\end{align}\n * where $ t_\\mathrm{s} $ and $ t_\\mathrm{ramp} $ are parameters that\n * control the start time and duration of the turn-on period.\n * Note that $F(t)$ is `smoothstep<4>`.\n */\ndouble nonic_ramp_function(double time, double start_time, double ramp_time);\n\ndouble nonic_ramp_function(double time,\n                             std::pair<double, double> start_and_ramp_times);\n/// @}\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarCharge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ScalarTensor/ScalarCharge.hpp\"\n\n#include <cmath>\n\n#include \"Utilities/Gsl.hpp\"\n\nvoid ScalarTensor::scalar_charge_integrand(\n    const gsl::not_null<Scalar<DataVector>*> result,\n    const tnsr::i<DataVector, 3>& phi,\n    const tnsr::I<DataVector, 3>& unit_normal_vector) {\n  // Project the scalar gradient on the normal vector\n  tenex::evaluate(result, phi(ti::i) * unit_normal_vector(ti::I));\n  // Multiply by integral prefactor\n  get(*result) /= -4.0 * M_PI;\n}\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarCharge.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarTensor {\n\n/// @{\n/*!\n * \\brief The scalar charge per unit solid angle.\n *\n * \\details This function calculates the integrand of:\n * \\f{align*}\n * q = - \\dfrac{1}{4 \\pi} \\oint dA \\Phi_i n^{i},\n * \\f}\n * where \\f$ n^{i} \\f$ is the unit (outward) normal of the surface.\n *\n * For a spherically symmetric scalar, this value will coincide with the value\n * as extracted from the $r^{-1}$ decay in\n * \\f[ \\Psi \\sim \\phi_\\infty + q / r + \\cdots~. \\f]\n */\nvoid scalar_charge_integrand(const gsl::not_null<Scalar<DataVector>*> result,\n                             const tnsr::i<DataVector, 3>& phi,\n                             const tnsr::I<DataVector, 3>& unit_normal_vector);\n/// @}\n\n} // namespace ScalarTensor\n\nnamespace ScalarTensor::StrahlkorperScalar::Tags {\n\n/*!\n * \\brief The scalar charge per unit area.\n *\n * \\details This tag holds the integrand of:\n * \\f{align*}\n * q = - \\dfrac{1}{4 \\pi} \\oint dA \\Phi_i n^{i},\n * \\f}\n * where \\f$ n^{i} \\f$ is the unit (outward) normal of the surface.\n *\n * For a spherically symmetric scalar, this value will coincide with the value\n * as extracted from the $r^{-1}$ decay in\n * \\f[ \\Psi \\sim \\phi_\\infty + q / r + \\cdots~. \\f]\n */\nstruct ScalarChargeIntegrand : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n/*!\n * \\brief Compute tag for the scalar charge per unit area.\n *\n * \\see ScalarTensor::StrahlkorperScalar::Tags::ScalarChargeIntegrand\n */\nstruct ScalarChargeIntegrandCompute : ScalarChargeIntegrand, db::ComputeTag {\n  static constexpr size_t Dim = 3;\n  using base = ScalarChargeIntegrand;\n  static constexpr auto function = &ScalarTensor::scalar_charge_integrand;\n  using argument_tags =\n      tmpl::list<CurvedScalarWave::Tags::Phi<Dim>,\n                 ylm::Tags::UnitNormalVector<Frame::Inertial>>;\n  using return_type = Scalar<DataVector>;\n};\n\n}  // namespace ScalarTensor::StrahlkorperTags\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarGaussBonnetPointwise)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  CouplingParameters.cpp\n  ScalarSource.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CouplingParameters.hpp\n  ScalarSource.hpp\n  Tags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ScalarTensor\n  DataStructures\n  INTERFACE\n  ErrorHandling\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/CouplingParameters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/CouplingParameters.hpp\"\n\n#include <pup.h>\n\nnamespace ScalarTensor {\n\nCouplingParameterOptions::CouplingParameterOptions(const double linear_in,\n                                                   const double quadratic_in,\n                                                   const double quartic_in)\n    : linear(linear_in), quadratic(quadratic_in), quartic(quartic_in) {}\n\nvoid CouplingParameterOptions::pup(PUP::er& p) {\n  p | linear;\n  p | quadratic;\n  p | quartic;\n}\n\nbool operator==(const CouplingParameterOptions& lhs,\n                const CouplingParameterOptions& rhs) {\n  return lhs.linear == rhs.linear and lhs.quadratic == rhs.quadratic and\n         lhs.quartic == rhs.quartic;\n}\n\nbool operator!=(const CouplingParameterOptions& lhs,\n                const CouplingParameterOptions& rhs) {\n  return not(lhs == rhs);\n}\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/CouplingParameters.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace ScalarTensor {\n\n/*!\n * \\brief Linear, quadratic and quartic coupling parameters to curvature.\n */\nstruct CouplingParameterOptions {\n  static constexpr Options::String help = {\n      \"Options for coupling parameters to curvature.\"};\n\n  struct Linear {\n    using type = double;\n    static constexpr Options::String help = \"Linear coupling parameter.\";\n  };\n\n  struct Quadratic {\n    using type = double;\n    static constexpr Options::String help = \"Quadratic coupling parameter.\";\n  };\n\n  struct Quartic {\n    using type = double;\n    static constexpr Options::String help = \"Quartic coupling parameter.\";\n  };\n\n  using options = tmpl::list<Linear, Quadratic, Quartic>;\n\n  CouplingParameterOptions() = default;\n  CouplingParameterOptions(double linear_in, double quadratic_in,\n                           double quartic_in);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  double linear{std::numeric_limits<double>::signaling_NaN()};\n  double quadratic{std::numeric_limits<double>::signaling_NaN()};\n  double quartic{std::numeric_limits<double>::signaling_NaN()};\n};\n\nbool operator==(const CouplingParameterOptions& lhs,\n                const CouplingParameterOptions& rhs);\nbool operator!=(const CouplingParameterOptions& lhs,\n                const CouplingParameterOptions& rhs);\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/ScalarSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/ScalarSource.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/QuadraticCurvatureScalars.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/RampUpFunction.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace ScalarTensor {\n\nvoid gauss_bonnet_scalar_source(\n    const gsl::not_null<Scalar<DataVector>*> scalar_source,\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar,\n    const Scalar<DataVector>& psi,\n    const CouplingParameterOptions& coupling_parameters, const double mass_psi,\n    const std::pair<double, double> start_and_ramp_times, const double time) {\n  // Compute the Riemann squared scalar in vacuum\n  gr::gauss_bonnet_scalar_in_vacuum(scalar_source, weyl_electric_scalar,\n                                    weyl_magnetic_scalar);\n  // Multiply by the source coupling function\n  multiply_by_negative_deriv_of_coupling_func(\n      scalar_source, psi, coupling_parameters, start_and_ramp_times, time);\n  // Add mass term\n  scalar_source->get() += square(mass_psi) * get(psi);\n}\n\nScalar<DataVector> gauss_bonnet_scalar_source(\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar,\n    const Scalar<DataVector>& psi,\n    const CouplingParameterOptions& coupling_parameters, const double mass_psi,\n    const std::pair<double, double> start_and_ramp_times, const double time) {\n  Scalar<DataVector> result{};\n  gauss_bonnet_scalar_source(make_not_null(&result), weyl_electric_scalar,\n                             weyl_magnetic_scalar, psi, coupling_parameters,\n                             mass_psi, start_and_ramp_times, time);\n  return result;\n}\n\nvoid multiply_by_negative_deriv_of_coupling_func(\n    const gsl::not_null<Scalar<DataVector>*> scalar_source,\n    const Scalar<DataVector>& psi,\n    const CouplingParameterOptions& coupling_parameters,\n    const std::pair<double, double> start_and_ramp_times, const double time) {\n  const auto ones_scalar = make_with_value<Scalar<DataVector>>(psi, 1.0);\n\n  // Ramp up factor\n  const double ramp_factor = nonic_ramp_function(time, start_and_ramp_times);\n\n  const double linear_coupling_psi_over_four =\n      0.25 * ramp_factor * coupling_parameters.linear;\n  const double first_coupling_psi_over_four =\n      0.25 * ramp_factor * coupling_parameters.quadratic;\n  const double second_coupling_psi_over_four =\n      0.25 * ramp_factor * coupling_parameters.quartic;\n\n  *scalar_source->get() *= -linear_coupling_psi_over_four * ones_scalar.get() -\n                           first_coupling_psi_over_four * psi.get() -\n                           second_coupling_psi_over_four * cube(psi.get());\n}\n\nvoid multiply_by_negative_second_deriv_of_coupling_func(\n    const gsl::not_null<Scalar<DataVector>*> scalar_source,\n    const Scalar<DataVector>& psi,\n    const CouplingParameterOptions& coupling_parameters,\n    const std::pair<double, double> start_and_ramp_times, const double time) {\n  const auto ones_scalar = make_with_value<Scalar<DataVector>>(psi, 1.0);\n\n  // Ramp up factor\n  const double ramp_factor = nonic_ramp_function(time, start_and_ramp_times);\n\n  // Linear coupling drops out here\n  const double first_coupling_psi_over_four =\n      0.25 * ramp_factor * coupling_parameters.quadratic;\n  const double second_coupling_psi_over_four =\n      0.25 * ramp_factor * coupling_parameters.quartic;\n\n  *scalar_source->get() *=\n      -first_coupling_psi_over_four * ones_scalar.get() -\n      (3.0 * second_coupling_psi_over_four) * square(psi.get());\n}\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/ScalarSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/CouplingParameters.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/Tags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/SourceTags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarTensor {\n/// @{\n/*!\n * \\brief Computes the source term given by the coupling of the scalar to\n * curvature.\n *\n * \\details For a scalar field with mass parameter $ m_\\Psi $,\n * the wave equation takes the form\n * \\begin{align}\n *   \\Box \\Psi = \\mathcal{S} ~,\n * \\end{align}\n *\n * where the source is given by\n * \\begin{align}\n *   \\mathcal{S} \\equiv m^2_\\Psi \\Psi - f'(\\Psi) \\mathcal{G}~,\n * \\end{align}\n * where\n * \\begin{align}\n *   \\mathcal{G} \\equiv 8 (E_{ab} E^{ab} - B_{ab} B^{ab}) ~,\n * \\end{align}\n * is the Gauss-Bonnet scalar and the coupling function is given by\n * \\begin{align}\n *   f(\\Psi) \\equiv \\lambda \\Psi\n *      + \\dfrac{1}{16} \\left( \\eta \\Psi^2 + 2 \\zeta \\Psi^4 \\right) ~,\n * \\end{align}\n * Here the Gauss-Bonnet scalar (in vacuum) is given in terms of the electric\n * ($ E_{ab} $) and magnetic ($ B_{ab} $) parts of the Weyl scalar.\n *\n */\nvoid gauss_bonnet_scalar_source(\n    gsl::not_null<Scalar<DataVector>*> scalar_source,\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar,\n    const Scalar<DataVector>& psi,\n    const CouplingParameterOptions& coupling_parameters, double mass_psi,\n    std::pair<double, double> start_and_ramp_times, double time);\n\nScalar<DataVector> gauss_bonnet_scalar_source(\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar,\n    const Scalar<DataVector>& psi,\n    const CouplingParameterOptions& coupling_parameters, double mass_psi,\n    std::pair<double, double> start_and_ramp_times, double time);\n/// @}\n\n/*!\n * \\brief Multiplies by the coupling function.\n *\n * \\details Multiply by the first derivative of the coupling function given by\n * \\begin{align}\n *   f(\\Psi) \\equiv\n *      + \\dfrac{1}{16} \\left( 4 \\lambda \\Psi + 2 \\eta \\Psi^2 + \\zeta \\Psi^4\n * \\right) ~.\n * \\end{align}\n *\n */\nvoid multiply_by_negative_deriv_of_coupling_func(\n    gsl::not_null<Scalar<DataVector>*> scalar_source,\n    const Scalar<DataVector>& psi,\n    const CouplingParameterOptions& coupling_parameters,\n    std::pair<double, double> start_and_ramp_times, double time);\n\n/*!\n * \\brief Multiplies by the coupling function.\n *\n * \\details Multiply by the second derivative of the coupling function given by\n * \\begin{align}\n *   f(\\Psi) \\equiv\n *      + \\dfrac{1}{16} \\left( 4 \\lambda \\Psi + 2 \\eta \\Psi^2 + \\zeta \\Psi^4\n * \\right) ~.\n * \\end{align}\n *\n */\nvoid multiply_by_negative_second_deriv_of_coupling_func(\n    gsl::not_null<Scalar<DataVector>*> scalar_source,\n    const Scalar<DataVector>& psi,\n    const CouplingParameterOptions& coupling_parameters,\n    std::pair<double, double> start_and_ramp_times, double time);\n\nnamespace Tags {\n/*!\n * \\copydoc ScalarTensor::gauss_bonnet_scalar_source\n */\nstruct ScalarSourceCompute : ScalarSource, db::ComputeTag {\n  using argument_tags = tmpl::list<\n      gr::Tags::WeylElectricScalar<DataVector>,\n      gr::Tags::WeylMagneticScalar<DataVector>, CurvedScalarWave::Tags::Psi,\n      ScalarTensor::Tags::CouplingParameters, ScalarTensor::Tags::ScalarMass,\n      ScalarTensor::Tags::RampUpParameters, ::Tags::Time>;\n  using return_type = Scalar<DataVector>;\n  static constexpr void (*function)(\n      const gsl::not_null<Scalar<DataVector>*> /* scalar_source */,\n      const Scalar<DataVector>& /* weyl_electric_scalar */,\n      const Scalar<DataVector>& /* weyl_magnetic_scalar */,\n      const Scalar<DataVector>& /* psi */,\n      const CouplingParameterOptions& /* coupling_parameters */,\n      const double /* mass_psi */,\n      const std::pair<double, double> /* start_and_ramp_times */,\n      const double /* time */) = &gauss_bonnet_scalar_source;\n  using base = ScalarSource;\n};\n}  // namespace Tags\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/CouplingParameters.hpp\"\n\n/// \\cond\nnamespace ScalarTensor::OptionTags {\nstruct Group;\n}  // namespace ScalarTensor::OptionTags\n/// \\endcond\n\nnamespace ScalarTensor {\nnamespace OptionTags {\n/*!\n * \\brief Linear coupling parameters to curvature.\n */\nstruct CouplingParameters {\n  static constexpr Options::String help = {\"Coupling parameters to curvature.\"};\n  using type = ScalarTensor::CouplingParameterOptions;\n  using group = ScalarTensor::OptionTags::Group;\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief Linear, quadratic and quartic coupling parameters to curvature.\n */\nstruct CouplingParameters : db::SimpleTag {\n  using type = ScalarTensor::CouplingParameterOptions;\n  using option_tags = tmpl::list<OptionTags::CouplingParameters>;\n  static constexpr bool pass_metavariables = false;\n  static ScalarTensor::CouplingParameterOptions create_from_options(\n      const ScalarTensor::CouplingParameterOptions& coupling_parameters) {\n    return coupling_parameters;\n  }\n};\n}  // namespace Tags\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ScalarTensor/ScalarSource.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/QuadraticCurvatureScalars.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/RampUpFunction.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace ScalarTensor {\n\nvoid add_scalar_source_to_dt_pi_scalar(\n    gsl::not_null<Scalar<DataVector>*> dt_pi_scalar,\n    const Scalar<DataVector>& scalar_source, const Scalar<DataVector>& lapse) {\n  get(*dt_pi_scalar) += get(lapse) * get(scalar_source);\n}\n\nvoid mass_source(const gsl::not_null<Scalar<DataVector>*> scalar_source,\n                 const Scalar<DataVector>& psi, const double mass_psi) {\n  get(*scalar_source) = square(mass_psi) * get(psi);\n}\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/ScalarSource.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarTensor {\n\n/*!\n * \\brief Add in the source term to the \\f$\\Pi\\f$\n * evolved variable of the ::CurvedScalarWave system.\n *\n * \\details The only source term in the wave equation\n * \\f[\n *  \\Box \\Psi = \\mathcal{S} ~,\n *  \\f]\n *\n * is in the equation for \\f$\\Pi\\f$:\n * \\f[\n *  \\partial_t \\Pi + \\text{\\{spatial derivative\n * terms\\}} = \\alpha \\mathcal{S}\n * ~,\n * \\f]\n *\n * where \\f$\\mathcal{S}\\f$ is the source term (e. g. in the Klein-Gordon\n * equation, the source term is the derivative of the scalar potential\n * \\f$\\mathcal{S} \\equiv \\partial V / \\partial \\Psi \\f$.)\n *\n * This function adds that contribution to the existing value of `dt_pi_scalar`.\n * The wave equation terms in the scalar equation should be computed before\n * passing the `dt_pi_scalar` to this function for updating.\n *\n * \\param dt_pi_scalar Time derivative terms of $\\Pi$. The sourceless part\n * should be computed before with ::CurvedScalarWave::TimeDerivative.\n * \\param scalar_source Source term $\\mathcal{S}$ for the scalar equation.\n * \\param lapse Lapse $\\alpha$.\n *\n * \\see `CurvedScalarWave::TimeDerivative` for details about the source-less\n * part of the time derivative calculation.\n */\nvoid add_scalar_source_to_dt_pi_scalar(\n    gsl::not_null<Scalar<DataVector>*> dt_pi_scalar,\n    const Scalar<DataVector>& scalar_source, const Scalar<DataVector>& lapse);\n\n/*!\n * \\brief Computes the source term given by the mass of the scalar.\n *\n * \\details For a scalar field with mass parameter \\f$ m_\\Psi \\f$,\n * the wave equation takes the form\n * \\f[\n *   \\Box \\Psi = \\mathcal{S} ~,\n * \\f]\n *\n * where the source is given by\n * \\f[\n *   \\mathcal{S} \\equiv m^2_\\Psi \\Psi~.\n * \\f]\n *\n * Here the mass parameter value is an option that needs to be specified in the\n * input file.\n *\n * \\param scalar_source Source term $\\mathcal{S}$ for the scalar equation.\n * \\param psi Scalar field $\\Psi$.\n * \\param mass_psi Mass of the scalar field $m_\\Psi$.\n *\n * \\see `ScalarTensor::Tags::ScalarMass` for details about the mass.\n */\nvoid mass_source(gsl::not_null<Scalar<DataVector>*> scalar_source,\n                 const Scalar<DataVector>& psi, double mass_psi);\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/SourceTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n/// \\cond\nnamespace ScalarTensor::OptionTags {\nstruct Group;\n}  // namespace ScalarTensor::OptionTags\n/// \\endcond\n\nnamespace ScalarTensor {\nnamespace OptionTags {\n/*!\n * \\brief Start time for ramp up function.\n */\nstruct RampUpStart {\n  static std::string name() { return \"RampUpStart\"; }\n  using type = double;\n  static constexpr Options::String help{\"Start time for ramp up function\"};\n  using group = ::ScalarTensor::OptionTags::Group;\n};\n\n/*!\n * \\brief Start time for ramp up function.\n */\nstruct RampUpDuration {\n  static std::string name() { return \"RampUpDuration\"; }\n  using type = double;\n  static constexpr Options::String help{\"Duration time for ramp up function\"};\n  using group = ::ScalarTensor::OptionTags::Group;\n  static double lower_bound() { return 0.0; }\n};\n\n}  // namespace OptionTags\n\nnamespace Tags {\n/*!\n * \\brief Start and duration time for ramp up function.\n */\nstruct RampUpParameters : db::SimpleTag {\n  using type = std::pair<double, double>;\n  using option_tags =\n      tmpl::list<OptionTags::RampUpStart, OptionTags::RampUpDuration>;\n  static constexpr bool pass_metavariables = false;\n  static std::pair<double, double> create_from_options(\n      const double start_time, const double duration_time) {\n    if (duration_time <= 0.0) {\n      ERROR(\"Ramp up duration time must be greater than zero, but is \"\n            << duration_time);\n    }\n    return std::pair<double, double>{start_time, duration_time};\n  }\n};\n\n}  // namespace Tags\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/StressEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ScalarTensor/StressEnergy.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarTensor {\n\nvoid add_stress_energy_term_to_dt_pi(\n    const gsl::not_null<tnsr::aa<DataVector, 3_st>*> dt_pi,\n    const tnsr::aa<DataVector, 3_st>& trace_reversed_stress_energy,\n    const Scalar<DataVector>& lapse) {\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      dt_pi->get(a, b) -=\n          16.0 * M_PI * get(lapse) * trace_reversed_stress_energy.get(a, b);\n    }\n  }\n}\n\nvoid trace_reversed_stress_energy(\n    const gsl::not_null<tnsr::aa<DataVector, 3_st>*> stress_energy,\n    const Scalar<DataVector>& pi_scalar,\n    const tnsr::i<DataVector, 3_st>& phi_scalar,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3_st>& shift) {\n  // 00-component\n  get<0, 0>(*stress_energy) = square(get(lapse) * get(pi_scalar));\n\n  for (size_t i = 0; i < 3; ++i) {\n    // 00-component\n    get<0, 0>(*stress_energy) +=\n        -2.0 * get(lapse) * get(pi_scalar) * shift.get(i) * phi_scalar.get(i);\n    // 0i-component\n    stress_energy->get(0, i + 1) =\n        -get(lapse) * get(pi_scalar) * phi_scalar.get(i);\n\n    for (size_t j = 0; j < 3; ++j) {\n      // 00-component\n      get<0, 0>(*stress_energy) +=\n          shift.get(i) * shift.get(j) * phi_scalar.get(i) * phi_scalar.get(j);\n      // 0i-component\n      stress_energy->get(0, i + 1) +=\n          shift.get(j) * phi_scalar.get(j) * phi_scalar.get(i);\n    }\n\n    for (size_t j = i; j < 3; ++j) {\n      // ij-component\n      stress_energy->get(i + 1, j + 1) = phi_scalar.get(i) * phi_scalar.get(j);\n    }\n  }\n}\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/StressEnergy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ScalarTensor {\n\n/*!\n * \\brief Add in the trace-reversed stress-energy source term to the \\f$\\Pi\\f$\n * evolved variable of the ::gh system.\n *\n * \\details The only stress energy source term in the Generalized Harmonic\n * evolution equations is in the equation for \\f$\\Pi_{a b}\\f$:\n * \\f[\n * \\partial_t \\Pi_{ab} + \\text{\\{spatial derivative terms\\}} =\n * \\text{\\{GH source terms\\}}\n * - 16 \\pi \\alpha (T^{(\\Psi)}_{ab} - \\frac{1}{2} g_{a b} g^{cd}T^{(\\Psi)}_{cd})\n * ~.\n * \\f]\n *\n * (note that this function takes as argument the trace-reversed stress-energy\n * tensor)\n *\n * This function adds that contribution to the existing value of `dt_pi`. The\n * spacetime terms in the GH equation should be computed before passing the\n * `dt_pi` to this function for updating.\n *\n * \\param dt_pi Time derivative of the $\\Pi_{ab}$ variable in the ::gh system.\n * The vacuum part should be computed before with ::gh::TimeDerivative\n * \\param trace_reversed_stress_energy Trace-reversed stress energy tensor of\n * the scalar $T^{(\\Psi), \\text{TR}}_{a b} \\equiv T^{(\\Psi)}_{ab} - \\frac{1}{2}\n *  g_{a b} g^{cd}T^{(\\Psi)}_{cd} = \\partial_a \\Psi \\partial_b \\Psi $.\n * \\param lapse Lapse $\\alpha$.\n *\n * \\see `gh::TimeDerivative` for details about the spacetime\n * part of the time derivative calculation.\n */\nvoid add_stress_energy_term_to_dt_pi(\n    gsl::not_null<tnsr::aa<DataVector, 3_st>*> dt_pi,\n    const tnsr::aa<DataVector, 3_st>& trace_reversed_stress_energy,\n    const Scalar<DataVector>& lapse);\n\n/*!\n * \\brief Compute the trace-reversed stress-energy tensor of the scalar field.\n *\n * \\details The trace-reversed stress energy tensor is needed to compute the\n * backreaction of the scalar to the spacetime evolution and is given by\n * \\f{align*}{\n * T^{(\\Psi), \\text{TR}}_{a b} &\\equiv T^{(\\Psi)}_{ab} - \\frac{1}{2}\n *  g_{a b} g^{cd}T^{(\\Psi)}_{cd} \\\\\n *  &= \\partial_a \\Psi \\partial_b \\Psi ~,\n * \\f}\n *\n * where \\f$T^{(\\Psi)}_{ab}\\f$ is the standard stress-energy tensor of the\n * scalar.\n *\n * In terms of the evolved variables of the scalar,\n * \\f{align*}{\n    T_{00} &= \\alpha^2 \\Pi^{2} - 2 \\alpha \\Pi \\beta^{i} \\Phi_{i}\n     + \\beta^{i} \\beta^{j} \\Phi_{i} \\Phi{j}~,                     \\\\\n    T_{0k} &= - \\alpha \\Pi \\Phi_{k} + \\beta^{i} \\Phi_{i} \\Phi_{k}~,          \\\\\n    T_{ij} &= \\Phi_{i} \\Phi{j}~.\n * \\f}\n *\n * where \\f$\\alpha\\f$ is the lapse.\n *\n * \\param stress_energy Trace-reversed stress energy tensor of\n * the scalar $T^{(\\Psi), \\text{TR}}_{a b} \\equiv T^{(\\Psi)}_{ab} - \\frac{1}{2}\n *  g_{a b} g^{cd}T^{(\\Psi)}_{cd} = \\partial_a \\Psi \\partial_b \\Psi $.\n * \\param pi_scalar Scalar evolution variable $\\Pi$.\n * \\param phi_scalar Scalar evolution variable $\\Phi_i$.\n * \\param lapse Lapse $\\alpha$.\n * \\param shift Shift $\\beta^{i}$.\n */\nvoid trace_reversed_stress_energy(\n    gsl::not_null<tnsr::aa<DataVector, 3_st>*> stress_energy,\n    const Scalar<DataVector>& pi_scalar,\n    const tnsr::i<DataVector, 3_st>& phi_scalar,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, 3_st>& shift);\n\nnamespace Tags {\n\n/*!\n * \\brief Compute tag for the trace reversed stress energy tensor.\n *\n * \\details Compute using ScalarTensor::trace_reversed_stress_energy.\n */\nstruct TraceReversedStressEnergyCompute\n    : TraceReversedStressEnergy<DataVector, 3_st, Frame::Inertial>,\n      db::ComputeTag {\n  static constexpr size_t Dim = 3;\n  using argument_tags =\n      tmpl::list<CurvedScalarWave::Tags::Pi, CurvedScalarWave::Tags::Phi<Dim>,\n                 gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, 3, Frame::Inertial>>;\n  using return_type = tnsr::aa<DataVector, Dim, Frame::Inertial>;\n  static constexpr void (*function)(\n      const gsl::not_null<tnsr::aa<DataVector, Dim>*> result,\n      const Scalar<DataVector>&, const tnsr::i<DataVector, Dim>&,\n      const Scalar<DataVector>&,\n      const tnsr::I<DataVector, Dim>&) = &trace_reversed_stress_energy;\n  using base = TraceReversedStressEnergy<DataVector, Dim, Frame::Inertial>;\n};\n}  // namespace Tags\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ScalarTensorXcts)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ScalarMomentum.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ScalarMomentum.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  CurvedScalarWave\n  DataStructures\n  GeneralRelativity\n  Utilities\n  ScalarGaussBonnet\n  Xcts\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/Xcts/ScalarMomentum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/ScalarTensor/Xcts/ScalarMomentum.hpp\"\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/Tags.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace sgb {\n\nvoid scalar_momentum(const gsl::not_null<Scalar<DataVector>*> result,\n                     const tnsr::i<DataVector, 3, Frame::Inertial>& deriv,\n                     const tnsr::I<DataVector, 3>& shift,\n                     const Scalar<DataVector>& lapse) {\n  dot_product(result, shift, deriv);\n  get(*result) /= get(lapse);\n}\n\nScalar<DataVector> scalar_momentum(\n    const tnsr::i<DataVector, 3, Frame::Inertial>& deriv,\n    const tnsr::I<DataVector, 3>& shift, const Scalar<DataVector>& lapse) {\n  Scalar<DataVector> result{get(lapse).size()};\n  scalar_momentum(make_not_null(&result), deriv, shift, lapse);\n  return result;\n}\n\n}  // namespace sgb\n\ntemplate struct sgb::Tags::PiCompute<gr::Tags::Shift<DataVector, 3>,\n                                     CurvedScalarWave::Tags::Pi>;\n\ntemplate struct sgb::Tags::PiCompute<sgb::Tags::RolledOffShift,\n                                     sgb::Tags::PiWithRolledOffShift>;\n"
  },
  {
    "path": "src/PointwiseFunctions/ScalarTensor/Xcts/ScalarMomentum.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace sgb {\n/// @{\n/*!\n * \\brief Compute the momentum $\\Pi$ assuming quasi-stationarity in sGB gravity.\n *\n * This expression can be obtained by simply demanding $\\partial_t \\Psi = 0$,\n * yielding\n *\n * \\begin{equation}\n * \\Pi \\equiv -n^{a} \\partial_a = \\alpha^{-1} \\beta^{i} \\partial_i \\Psi\n * \\end{equation}\n */\n\nvoid scalar_momentum(gsl::not_null<Scalar<DataVector>*> result,\n                     const tnsr::i<DataVector, 3, Frame::Inertial>& deriv,\n                     const tnsr::I<DataVector, 3>& shift,\n                     const Scalar<DataVector>& lapse);\n\nScalar<DataVector> scalar_momentum(\n    const tnsr::i<DataVector, 3, Frame::Inertial>& deriv,\n    const tnsr::I<DataVector, 3>& shift, const Scalar<DataVector>& lapse);\n/// @{\n\nnamespace Tags {\n\n/*!\n * \\brief Re-compute the momentum Pi, assuming quasi-stationarity.\n */\ntemplate <typename ShiftTag, typename OutputTag>\nstruct PiCompute : OutputTag, db::ComputeTag {\n public:\n  using base = OutputTag;\n  using return_type = typename base::type;\n  static constexpr auto function =\n      static_cast<void (*)(gsl::not_null<return_type*>,\n                           const tnsr::i<DataVector, 3, Frame::Inertial>&,\n                           const tnsr::I<DataVector, 3>&,\n                           const Scalar<DataVector>&)>(&scalar_momentum);\n  using argument_tags =\n      tmpl::list<::Tags::deriv<Psi, tmpl::size_t<3>, Frame::Inertial>, ShiftTag,\n                 gr::Tags::Lapse<DataVector>>;\n};\n\n}  // namespace Tags\n}  // namespace sgb\n"
  },
  {
    "path": "src/PointwiseFunctions/SpecialRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SpecialRelativity)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  LorentzBoostMatrix.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  LorentzBoostMatrix.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Utilities\n  PUBLIC\n  DataStructures\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/SpecialRelativity/LorentzBoostMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/SpecialRelativity/LorentzBoostMatrix.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace sr {\ntemplate <size_t SpatialDim>\ntnsr::Ab<double, SpatialDim, Frame::NoFrame> lorentz_boost_matrix(\n    const tnsr::I<double, SpatialDim, Frame::NoFrame>& velocity) {\n  auto boost_matrix =\n      make_with_value<tnsr::Ab<double, SpatialDim, Frame::NoFrame>>(\n          get<0>(velocity), std::numeric_limits<double>::signaling_NaN());\n  lorentz_boost_matrix<SpatialDim>(&boost_matrix, velocity);\n  return boost_matrix;\n}\n\ntemplate <size_t SpatialDim>\nvoid lorentz_boost_matrix(\n    gsl::not_null<tnsr::Ab<double, SpatialDim, Frame::NoFrame>*> boost_matrix,\n    const tnsr::I<double, SpatialDim, Frame::NoFrame>& velocity) {\n  const double velocity_squared{get(dot_product(velocity, velocity))};\n  const double lorentz_factor{1.0 / sqrt(1.0 - velocity_squared)};\n\n  // For the spatial-spatial terms of the boost matrix, we need to compute\n  // a prefactor, which is essentially kinetic energy per mass per velocity\n  // squared. Specifically, the prefactor is\n  //\n  // kinetic_energy_per_v_squared = (lorentz_factor-1.0)/velocity^2\n  //\n  // This is algebraically equivalent to\n  //\n  // kinetic_energy_per_v_squared = lorentz_factor / ((1 + sqrt(1-velocity^2))),\n  //\n  // a form that avoids division by zero as v->0.\n\n  double kinetic_energy_per_v_squared{square(lorentz_factor) /\n                                      (1.0 + lorentz_factor)};\n\n  get<0, 0>(*boost_matrix) = lorentz_factor;\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    (*boost_matrix).get(0, i + 1) = velocity.get(i) * lorentz_factor;\n    (*boost_matrix).get(i + 1, 0) = velocity.get(i) * lorentz_factor;\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      (*boost_matrix).get(i + 1, j + 1) =\n          velocity.get(i) * velocity.get(j) * kinetic_energy_per_v_squared;\n    }\n    (*boost_matrix).get(i + 1, i + 1) += 1.0;\n  }\n}\n\ntemplate <size_t SpatialDim>\ntnsr::Ab<double, SpatialDim, Frame::NoFrame> lorentz_boost_matrix(\n    const std::array<double, SpatialDim>& velocity) {\n  tnsr::I<double, SpatialDim, Frame::NoFrame> velocity_as_tensor(velocity);\n  return lorentz_boost_matrix(velocity_as_tensor);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid lorentz_boost(\n    const gsl::not_null<tnsr::I<DataType, SpatialDim, Frame>*> result,\n    const tnsr::I<DataType, SpatialDim, Frame>& vector,\n    const double vector_component_0,\n    const std::array<double, SpatialDim>& velocity) {\n  if (velocity == make_array<SpatialDim>(0.)) {\n    *result = vector;\n    return;\n  }\n  // Inverse matrix with respect to the boost applied to one forms\n  const auto boost_matrix = lorentz_boost_matrix(-velocity);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    result->get(i) = boost_matrix.get(i + 1, 0) * vector_component_0;\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      result->get(i) += boost_matrix.get(i + 1, j + 1) * vector.get(j);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid lorentz_boost(\n    const gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> result,\n    const tnsr::a<DataType, SpatialDim, Frame>& one_form,\n    const std::array<double, SpatialDim>& velocity) {\n  if (velocity == make_array<SpatialDim>(0.)) {\n    *result = one_form;\n    return;\n  }\n  const auto boost_matrix = lorentz_boost_matrix(velocity);\n  for (size_t i = 0; i < SpatialDim + 1; ++i) {\n    result->get(i) = 0.;\n    for (size_t j = 0; j < SpatialDim + 1; ++j) {\n      result->get(i) += boost_matrix.get(i, j) * one_form.get(j);\n    }\n  }\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid lorentz_boost(\n    const gsl::not_null<tnsr::ab<DataType, SpatialDim, Frame>*> result,\n    const tnsr::ab<DataType, SpatialDim, Frame>& tensor,\n    const std::array<double, SpatialDim>& velocity) {\n  if (velocity == make_array<SpatialDim>(0.)) {\n    *result = tensor;\n    return;\n  }\n  const auto boost_matrix = lorentz_boost_matrix(velocity);\n  for (size_t i = 0; i < SpatialDim + 1; ++i) {\n    for (size_t k = 0; k < SpatialDim + 1; ++k) {\n      result->get(i, k) = 0.;\n      for (size_t j = 0; j < SpatialDim + 1; ++j) {\n        for (size_t l = 0; l < SpatialDim + 1; ++l) {\n          result->get(i, k) += boost_matrix.get(i, j) * boost_matrix.get(k, l) *\n                               tensor.get(j, l);\n        }\n      }\n    }\n  }\n}\n\n}  // namespace sr\n\n// Explicit Instantiations\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template tnsr::Ab<double, DIM(data), Frame::NoFrame>             \\\n  sr::lorentz_boost_matrix(                                        \\\n      const tnsr::I<double, DIM(data), Frame::NoFrame>& velocity); \\\n  template void sr::lorentz_boost_matrix(                          \\\n      gsl::not_null<tnsr::Ab<double, DIM(data), Frame::NoFrame>*>  \\\n          boost_matrix,                                            \\\n      const tnsr::I<double, DIM(data), Frame::NoFrame>& velocity); \\\n  template tnsr::Ab<double, DIM(data), Frame::NoFrame>             \\\n  sr::lorentz_boost_matrix(const std::array<double, DIM(data)>& velocity);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void sr::lorentz_boost(                                        \\\n      const gsl::not_null<tnsr::I<DTYPE(data), DIM(data), FRAME(data)>*>  \\\n          result,                                                         \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& one_form,       \\\n      const double one_form_component_0,                                  \\\n      const std::array<double, DIM(data)>& velocity);                     \\\n  template void sr::lorentz_boost(                                        \\\n      const gsl::not_null<tnsr::a<DTYPE(data), DIM(data), FRAME(data)>*>  \\\n          result,                                                         \\\n      const tnsr::a<DTYPE(data), DIM(data), FRAME(data)>& one_form,       \\\n      const std::array<double, DIM(data)>& velocity);                     \\\n  template void sr::lorentz_boost(                                        \\\n      const gsl::not_null<tnsr::ab<DTYPE(data), DIM(data), FRAME(data)>*> \\\n          result,                                                         \\\n      const tnsr::ab<DTYPE(data), DIM(data), FRAME(data)>& tensor,        \\\n      const std::array<double, DIM(data)>& velocity);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector),\n                        (Frame::Grid, Frame::Distorted, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INSTANTIATE\n"
  },
  {
    "path": "src/PointwiseFunctions/SpecialRelativity/LorentzBoostMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n\n/// \\endcond\n\n/// \\ingroup SpecialRelativityGroup\n/// Holds functions related to special relativity.\nnamespace sr {\n/// @{\n/*!\n * \\ingroup SpecialRelativityGroup\n * \\brief Computes the matrix for a Lorentz boost from a single\n * velocity vector (i.e., not a velocity field).\n *\n * \\details Given a spatial velocity vector \\f$v^i\\f$ (with \\f$c=1\\f$),\n * compute the matrix \\f$\\Lambda^{a}{}_{\\bar{a}}\\f$ for a Lorentz boost with\n * that velocity [e.g. Eq. (2.38) of \\cite ThorneBlandford2017]:\n *\n * \\f{align}{\n * \\Lambda^t{}_{\\bar{t}} &= \\gamma, \\\\\n * \\Lambda^t{}_{\\bar{i}} = \\Lambda^i{}_{\\bar{t}} &= \\gamma v^i, \\\\\n * \\Lambda^i{}_{\\bar{j}} = \\Lambda^j{}_{\\bar{i}} &= [(\\gamma - 1)/v^2] v^i v^j\n *                                              + \\delta^{ij}.\n * \\f}\n *\n * Here \\f$v = \\sqrt{\\delta_{ij} v^i v^j}\\f$, \\f$\\gamma = 1/\\sqrt{1-v^2}\\f$,\n * and \\f$\\delta^{ij}\\f$ is the Kronecker delta. Note that this matrix boosts\n * a one-form from the unbarred to the barred frame, and its inverse\n * (obtained via \\f$v \\rightarrow -v\\f$) boosts a vector from the barred to\n * the unbarred frame.\n *\n * Note that while the Lorentz boost matrix is symmetric, the returned\n * boost matrix is of type `tnsr::Ab`, because `Tensor` does not support\n * symmetric tensors unless both indices have the same valence.\n */\ntemplate <size_t SpatialDim>\ntnsr::Ab<double, SpatialDim, Frame::NoFrame> lorentz_boost_matrix(\n    const tnsr::I<double, SpatialDim, Frame::NoFrame>& velocity);\n\ntemplate <size_t SpatialDim>\nvoid lorentz_boost_matrix(\n    gsl::not_null<tnsr::Ab<double, SpatialDim, Frame::NoFrame>*> boost_matrix,\n    const tnsr::I<double, SpatialDim, Frame::NoFrame>& velocity);\n\ntemplate <size_t SpatialDim>\ntnsr::Ab<double, SpatialDim, Frame::NoFrame> lorentz_boost_matrix(\n    const std::array<double, SpatialDim>& velocity);\n/// @}\n\n/// @{\n/*!\n * \\ingroup SpecialRelativityGroup\n * \\brief Apply a Lorentz boost to the spatial part of a vector.\n * \\details This requires passing the 0th component of the vector as an\n * additional argument.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid lorentz_boost(gsl::not_null<tnsr::I<DataType, SpatialDim, Frame>*> result,\n                   const tnsr::I<DataType, SpatialDim, Frame>& vector,\n                   double vector_component_0,\n                   const std::array<double, SpatialDim>& velocity);\n\n/*!\n * \\brief Apply a Lorentz boost to a one form.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid lorentz_boost(\n    gsl::not_null<tnsr::a<DataType, SpatialDim, Frame>*> result,\n    const tnsr::a<DataType, SpatialDim, Frame>& one_form,\n    const std::array<double, SpatialDim>& velocity);\n\n/*!\n * \\brief Apply a Lorentz boost to each component of a rank-2 tensor with\n * lower or covariant indices.\n * \\note In the future we might want to write a single function capable to boost\n * a tensor of arbitrary rank.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid lorentz_boost(gsl::not_null<tnsr::ab<DataType, SpatialDim, Frame>*> result,\n                   const tnsr::ab<DataType, SpatialDim, Frame>& tensor,\n                   const std::array<double, SpatialDim>& velocity);\n/// @}\n}  // namespace sr\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/AdmMass.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Xcts/AdmMass.hpp\"\n\nnamespace Xcts {\n\nvoid adm_mass_surface_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted) {\n  tenex::evaluate<ti::I>(\n      result, 1. / (16. * M_PI) *\n                  (inv_conformal_metric(ti::J, ti::K) *\n                       conformal_christoffel_second_kind(ti::I, ti::j, ti::k) -\n                   inv_conformal_metric(ti::I, ti::J) *\n                       conformal_christoffel_contracted(ti::j) -\n                   8. * inv_conformal_metric(ti::I, ti::J) *\n                       deriv_conformal_factor(ti::j)));\n}\n\ntnsr::I<DataVector, 3> adm_mass_surface_integrand(\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted) {\n  tnsr::I<DataVector, 3> result;\n  adm_mass_surface_integrand(\n      make_not_null(&result), deriv_conformal_factor, inv_conformal_metric,\n      conformal_christoffel_second_kind, conformal_christoffel_contracted);\n  return result;\n}\n\nvoid adm_mass_volume_integrand(\n    gsl::not_null<Scalar<DataVector>*> result,\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& conformal_ricci_scalar,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::iJK<DataVector, 3>& deriv_inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::iJkk<DataVector, 3>& deriv_conformal_christoffel_second_kind) {\n  tenex::evaluate(\n      result,\n      1. / (16. * M_PI) *\n          (deriv_inv_conformal_metric(ti::i, ti::J, ti::K) *\n               conformal_christoffel_second_kind(ti::I, ti::j, ti::k) +\n           inv_conformal_metric(ti::J, ti::K) *\n               deriv_conformal_christoffel_second_kind(ti::i, ti::I, ti::j,\n                                                       ti::k) +\n           conformal_christoffel_contracted(ti::l) *\n               inv_conformal_metric(ti::J, ti::K) *\n               conformal_christoffel_second_kind(ti::L, ti::j, ti::k) -\n           deriv_inv_conformal_metric(ti::i, ti::I, ti::J) *\n               conformal_christoffel_contracted(ti::j) -\n           inv_conformal_metric(ti::I, ti::J) *\n               deriv_conformal_christoffel_second_kind(ti::i, ti::K, ti::k,\n                                                       ti::j) -\n           conformal_christoffel_contracted(ti::l) *\n               inv_conformal_metric(ti::L, ti::J) *\n               conformal_christoffel_contracted(ti::j) -\n           conformal_factor() * conformal_ricci_scalar() -\n           2. / 3. * pow<5>(conformal_factor()) *\n               square(trace_extrinsic_curvature()) +\n           pow<5>(conformal_factor()) / 4. *\n              longitudinal_shift_minus_dt_conformal_metric_over_lapse_square() +\n           16. * M_PI * pow<5>(conformal_factor()) * energy_density()));\n}\n\nScalar<DataVector> adm_mass_volume_integrand(\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& conformal_ricci_scalar,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::iJK<DataVector, 3>& deriv_inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::iJkk<DataVector, 3>& deriv_conformal_christoffel_second_kind) {\n  Scalar<DataVector> result;\n  adm_mass_volume_integrand(\n      make_not_null(&result), conformal_factor, conformal_ricci_scalar,\n      trace_extrinsic_curvature,\n      longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n      energy_density, inv_conformal_metric, deriv_inv_conformal_metric,\n      conformal_christoffel_second_kind, conformal_christoffel_contracted,\n      deriv_conformal_christoffel_second_kind);\n  return result;\n}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/AdmMass.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts {\n\n/// @{\n/*!\n * \\brief Surface integrand for the ADM mass calculation.\n *\n * We define the ADM mass integral as (see Eq. 3.139 in \\cite BaumgarteShapiro):\n *\n * \\begin{equation}\n *   M_\\text{ADM} = \\frac{1}{16\\pi}\n *                  \\oint_{S_\\infty} \\Big(\n *                     \\bar\\gamma^{jk} \\bar\\Gamma^i_{jk}\n *                     - \\bar\\gamma^{ij} \\bar\\Gamma_{j}\n *                     - 8 \\bar\\gamma^{ij} \\partial_j \\psi\n *                  \\Big) d\\bar{S}_i.\n * \\end{equation}\n *\n * \\note We don't use the other versions presented in \\cite BaumgarteShapiro of\n * this integral because they make assumptions like $\\bar\\gamma = 1$,\n * $\\bar\\Gamma^i_{ij} = 0$ and faster fall-off of the conformal metric.\n *\n * \\note For consistency with `adm_mass_volume_integrand`, this integrand needs\n * to be contracted with the conformal face normal and integrated with the\n * conformal area element.\n *\n * \\param result output pointer\n * \\param deriv_conformal_factor the gradient of the conformal factor\n * $\\partial_i \\psi$\n * \\param inv_conformal_metric the inverse conformal metric $\\bar\\gamma^{ij}$\n * \\param conformal_christoffel_second_kind the conformal christoffel symbol\n * $\\bar\\Gamma^i_{jk}$\n * \\param conformal_christoffel_contracted the conformal christoffel symbol\n * contracted in its first two indices $\\bar\\Gamma_{i} = \\bar\\Gamma^j_{ij}$\n */\nvoid adm_mass_surface_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted);\n\n/// Return-by-value overload\ntnsr::I<DataVector, 3> adm_mass_surface_integrand(\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted);\n/// @}\n\n/// @{\n/*!\n * \\brief Volume integrand for the ADM mass calculation.\n *\n * We cast the ADM mass as an infinite volume integral by applying Gauss' law on\n * the surface integral defined in `adm_mass_surface_integrand`:\n *\n * \\begin{equation}\n *   M_\\text{ADM} = \\frac{1}{16\\pi}\n *                  \\int_{V_\\infty} \\Big(\n *                    \\partial_i \\bar\\gamma^{jk} \\bar\\Gamma^i_{jk}\n *                    + \\bar\\gamma^{jk} \\partial_i \\bar\\Gamma^i_{jk}\n *                    + \\bar\\Gamma_l \\bar\\gamma^{jk} \\bar\\Gamma^l_{jk}\n *                    - \\partial_i \\bar\\gamma^{ij} \\bar\\Gamma_j\n *                    - \\bar\\gamma^{ij} \\partial_i \\bar\\Gamma_j\n *                    - \\bar\\Gamma_l \\bar\\gamma^{lj} \\bar\\Gamma_j\n *                    - 8 \\bar D^2 \\psi\n *                  \\Big) d\\bar{V},\n * \\end{equation}\n *\n * where we can use the Hamiltonian constraint (Eq. 3.37 in\n * \\cite BaumgarteShapiro) to replace $8 \\bar D^2 \\psi$ with\n *\n * \\begin{equation}\n *   8 \\bar D^2 \\psi = \\psi \\bar R + \\frac{2}{3} \\psi^5 K^2\n *                     - \\frac{1}{4} \\psi^5 \\frac{1}{\\alpha^2}\n *                         \\Big[ (\\bar L \\beta)_{ij} - \\bar u_{ij} \\Big]\n *                         \\Big[ (\\bar L \\beta)^{ij} - \\bar u^{ij} \\Big]\n *                     - 16\\pi \\psi^5 \\rho.\n * \\end{equation}\n *\n * \\note This is similar to Eq. 3.149 in \\cite BaumgarteShapiro, except that\n * here we don't assume $\\bar\\gamma = 1$.\n *\n * \\note For consistency with `adm_mass_surface_integrand`, this integrand needs\n * to be integrated with the conformal volume element.\n *\n * \\param result output pointer\n * \\param conformal_factor the conformal factor\n * \\param conformal_ricci_scalar the conformal Ricci scalar $\\bar R$\n * \\param trace_extrinsic_curvature the extrinsic curvature trace $K$\n * \\param longitudinal_shift_minus_dt_conformal_metric_over_lapse_square the\n * quantity computed in\n * `Xcts::Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare`\n * \\param energy_density the energy density $\\rho$\n * \\param inv_conformal_metric the inverse conformal metric $\\bar\\gamma^{ij}$\n * \\param deriv_inv_conformal_metric the gradient of the inverse conformal\n * metric $\\partial_i \\bar\\gamma^{jk}$\n * \\param conformal_christoffel_second_kind the conformal christoffel symbol\n * $\\bar\\Gamma^i_{jk}$\n * \\param conformal_christoffel_contracted the conformal christoffel symbol\n * contracted in its first two indices $\\bar\\Gamma_{i} = \\bar\\Gamma^j_{ij}$\n * \\param deriv_conformal_christoffel_second_kind the gradient of the conformal\n * christoffel symbol $\\partial_i \\bar\\Gamma^j_{kl}$\n */\nvoid adm_mass_volume_integrand(\n    gsl::not_null<Scalar<DataVector>*> result,\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& conformal_ricci_scalar,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::iJK<DataVector, 3>& deriv_inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::iJkk<DataVector, 3>& deriv_conformal_christoffel_second_kind);\n\n/// Return-by-value overload\nScalar<DataVector> adm_mass_volume_integrand(\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& conformal_ricci_scalar,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>&\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::iJK<DataVector, 3>& deriv_inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted,\n    const tnsr::iJkk<DataVector, 3>& deriv_conformal_christoffel_second_kind);\n/// @}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/AdmMomentum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Xcts/AdmMomentum.hpp\"\n\nnamespace Xcts {\n\nvoid adm_linear_momentum_surface_integrand(\n    gsl::not_null<tnsr::II<DataVector, 3>*> result,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_spatial_metric,\n    const tnsr::II<DataVector, 3>& inv_extrinsic_curvature,\n    const Scalar<DataVector>& trace_extrinsic_curvature) {\n  tenex::evaluate<ti::I, ti::J>(\n      result,\n      1. / (8. * M_PI) * pow<10>(conformal_factor()) *\n          (inv_extrinsic_curvature(ti::I, ti::J) -\n           trace_extrinsic_curvature() * inv_spatial_metric(ti::I, ti::J)));\n}\n\ntnsr::II<DataVector, 3> adm_linear_momentum_surface_integrand(\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_spatial_metric,\n    const tnsr::II<DataVector, 3>& inv_extrinsic_curvature,\n    const Scalar<DataVector>& trace_extrinsic_curvature) {\n  tnsr::II<DataVector, 3> result;\n  adm_linear_momentum_surface_integrand(\n      make_not_null(&result), conformal_factor, inv_spatial_metric,\n      inv_extrinsic_curvature, trace_extrinsic_curvature);\n  return result;\n}\n\nvoid adm_linear_momentum_volume_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const tnsr::II<DataVector, 3>& surface_integrand,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted) {\n  // Note: we can ignore the $1/(8\\pi)$ term below because it is already\n  // included in `surface_integrand`.\n  tenex::evaluate<ti::I>(\n      result,\n      -(conformal_christoffel_second_kind(ti::I, ti::j, ti::k) *\n            surface_integrand(ti::J, ti::K) +\n        conformal_christoffel_contracted(ti::k) *\n            surface_integrand(ti::I, ti::K) -\n        2. * conformal_metric(ti::j, ti::k) * surface_integrand(ti::J, ti::K) *\n            inv_conformal_metric(ti::I, ti::L) * deriv_conformal_factor(ti::l) /\n            conformal_factor()));\n}\n\ntnsr::I<DataVector, 3> adm_linear_momentum_volume_integrand(\n    const tnsr::II<DataVector, 3>& surface_integrand,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted) {\n  tnsr::I<DataVector, 3> result;\n  adm_linear_momentum_volume_integrand(\n      make_not_null(&result), surface_integrand, conformal_factor,\n      deriv_conformal_factor, conformal_metric, inv_conformal_metric,\n      conformal_christoffel_second_kind, conformal_christoffel_contracted);\n  return result;\n}\n\nvoid adm_angular_momentum_z_surface_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const tnsr::II<DataVector, 3>& linear_momentum_surface_integrand,\n    const tnsr::I<DataVector, 3>& coords) {\n  // Note: we can ignore the $1/(8\\pi)$ term below because it is already\n  // included in `linear_momentum_surface_integrand`.\n  for (int I = 0; I < 3; I++) {\n    result->get(I) =\n        get<0>(coords) * linear_momentum_surface_integrand.get(1, I) -\n        get<1>(coords) * linear_momentum_surface_integrand.get(0, I);\n  }\n}\n\ntnsr::I<DataVector, 3> adm_angular_momentum_z_surface_integrand(\n    const tnsr::II<DataVector, 3>& linear_momentum_surface_integrand,\n    const tnsr::I<DataVector, 3>& coords) {\n  tnsr::I<DataVector, 3> result;\n  adm_angular_momentum_z_surface_integrand(\n      make_not_null(&result), linear_momentum_surface_integrand, coords);\n  return result;\n}\n\nvoid adm_angular_momentum_z_volume_integrand(\n    gsl::not_null<Scalar<DataVector>*> result,\n    const tnsr::I<DataVector, 3>& linear_momentum_volume_integrand,\n    const tnsr::I<DataVector, 3>& coords) {\n  // Note: we can ignore the $-1/(8\\pi)$ term below because it is already\n  // included in `linear_momentum_volume_integrand`.\n  result->get() = get<0>(coords) * get<1>(linear_momentum_volume_integrand) -\n                  get<1>(coords) * get<0>(linear_momentum_volume_integrand);\n}\n\nScalar<DataVector> adm_angular_momentum_z_volume_integrand(\n    const tnsr::I<DataVector, 3>& linear_momentum_volume_integrand,\n    const tnsr::I<DataVector, 3>& coords) {\n  Scalar<DataVector> result;\n  adm_angular_momentum_z_volume_integrand(\n      make_not_null(&result), linear_momentum_volume_integrand, coords);\n  return result;\n}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/AdmMomentum.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts {\n\n/// @{\n/*!\n * \\brief Surface integrand for the ADM linear momentum calculation.\n *\n * We define the ADM linear momentum integral as (see Eqs. 19-20 in\n * \\cite Ossokine2015yla):\n *\n * \\begin{equation}\n *   P_\\text{ADM}^i = \\frac{1}{8\\pi}\n *                    \\oint_{S_\\infty} \\psi^{10} \\Big(\n *                      K^{ij} - K \\gamma^{ij}\n *                    \\Big) \\, dS_j.\n * \\end{equation}\n *\n * \\note For consistency with `adm_linear_momentum_volume_integrand`, this\n * integrand needs to be contracted with the Euclidean face normal and\n * integrated with the Euclidean area element.\n *\n * \\param result output pointer\n * \\param conformal_factor the conformal factor $\\psi$\n * \\param inv_spatial_metric the inverse spatial metric $\\gamma^{ij}$\n * \\param inv_extrinsic_curvature the inverse extrinsic curvature $K^{ij}$\n * \\param trace_extrinsic_curvature the trace of the extrinsic curvature $K$\n */\nvoid adm_linear_momentum_surface_integrand(\n    gsl::not_null<tnsr::II<DataVector, 3>*> result,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_spatial_metric,\n    const tnsr::II<DataVector, 3>& inv_extrinsic_curvature,\n    const Scalar<DataVector>& trace_extrinsic_curvature);\n\n/// Return-by-value overload\ntnsr::II<DataVector, 3> adm_linear_momentum_surface_integrand(\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::II<DataVector, 3>& inv_spatial_metric,\n    const tnsr::II<DataVector, 3>& inv_extrinsic_curvature,\n    const Scalar<DataVector>& trace_extrinsic_curvature);\n/// @}\n\n/// @{\n/*!\n * \\brief Volume integrand for ADM linear momentum calculation defined as (see\n * Eq. 20 in \\cite Ossokine2015yla):\n *\n * \\begin{equation}\n *   P_\\text{ADM}^i = - \\frac{1}{8\\pi}\n *                      \\int_{V_\\infty} \\Big(\n *                        \\bar\\Gamma^i_{jk} P^{jk}\n *                        + \\bar\\Gamma^j_{jk} P^{jk}\n *                        - 2 \\bar\\gamma_{jk} P^{jk} \\bar\\gamma^{il}\n *                                                   \\partial_l(\\ln\\psi)\n *                      \\Big) \\, dV,\n * \\end{equation}\n *\n * where $1/(8\\pi) P^{jk}$ is the result from\n * `adm_linear_momentum_surface_integrand`.\n *\n * \\note For consistency with `adm_linear_momentum_surface_integrand`, this\n * integrand needs to be integrated with the Euclidean volume element.\n *\n * \\param result output pointer\n * \\param surface_integrand the quantity $1/(8\\pi) P^{ij}$ (result of\n * `adm_linear_momentum_surface_integrand`)\n * \\param conformal_factor the conformal factor $\\psi$\n * \\param deriv_conformal_factor the gradient of the conformal factor\n * $\\partial_i\\psi$\n * \\param conformal_metric the conformal metric $\\bar\\gamma_{ij}$\n * \\param inv_conformal_metric the inverse conformal metric $\\bar\\gamma^{ij}$\n * \\param conformal_christoffel_second_kind the conformal christoffel symbol\n * $\\bar\\Gamma^i_{jk}$\n * \\param conformal_christoffel_contracted the contracted conformal christoffel\n * symbol $\\bar\\Gamma_i$\n */\nvoid adm_linear_momentum_volume_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const tnsr::II<DataVector, 3>& surface_integrand,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted);\n\n/// Return-by-value overload\ntnsr::I<DataVector, 3> adm_linear_momentum_volume_integrand(\n    const tnsr::II<DataVector, 3>& surface_integrand,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3>& deriv_conformal_factor,\n    const tnsr::ii<DataVector, 3>& conformal_metric,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind,\n    const tnsr::i<DataVector, 3>& conformal_christoffel_contracted);\n/// @}\n\n/// @{\n/*!\n * \\brief Surface integrand for the z-component of the ADM angular momentum.\n *\n * We define the ADM angular momentum surface integral as (see Eq. 23 in\n * \\cite Ossokine2015yla):\n *\n * \\begin{equation}\n *   J_\\text{ADM}^z = \\frac{1}{8\\pi}\n *                    \\oint_{S_\\infty} \\Big(\n *                      x P^{yj} - y P^{xj}\n *                    \\Big) \\, dS_j,\n * \\end{equation}\n *\n * where $1/(8\\pi) P^{jk}$ is the result from\n * `adm_linear_momentum_surface_integrand`.\n *\n * \\note For consistency with `adm_angular_momentum_z_volume_integrand`, this\n * integrand needs to be contracted with the Euclidean face normal and\n * integrated with the Euclidean area element.\n *\n * \\param result output pointer\n * \\param linear_momentum_surface_integrand the quantity $1/(8\\pi) P^{ij}$\n * (result of `adm_linear_momentum_surface_integrand`)\n * \\param coords the inertial coordinates $x^i$\n */\nvoid adm_angular_momentum_z_surface_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const tnsr::II<DataVector, 3>& linear_momentum_surface_integrand,\n    const tnsr::I<DataVector, 3>& coords);\n\n/// Return-by-value overload\ntnsr::I<DataVector, 3> adm_angular_momentum_z_surface_integrand(\n    const tnsr::II<DataVector, 3>& linear_momentum_surface_integrand,\n    const tnsr::I<DataVector, 3>& coords);\n/// @}\n\n/// @{\n/*!\n * \\brief Volume integrand for the z-component of the ADM angular momentum.\n *\n * We define the ADM angular momentum volume integral as (see Eq. 23 in\n * \\cite Ossokine2015yla):\n *\n * \\begin{equation}\n *   J_\\text{ADM}^z = - \\frac{1}{8\\pi}\n *                      \\int_{V_\\infty} \\Big(\n *                        x G^y - y G^x\n *                      \\Big) \\, dV,\n * \\end{equation}\n *\n * where $-1/(8\\pi) G^i$ is the result from\n * `adm_linear_momentum_volume_integrand`.\n *\n * \\note For consistency with `adm_angular_momentum_z_surface_integrand`, this\n * integrand needs to be integrated with the Euclidean volume element.\n *\n * \\param result output pointer\n * \\param linear_momentum_volume_integrand the quantity $-1/(8\\pi) G^i$ (result\n * of `adm_linear_momentum_volume_integrand`)\n * \\param coords the inertial coordinates $x^i$\n */\nvoid adm_angular_momentum_z_volume_integrand(\n    gsl::not_null<Scalar<DataVector>*> result,\n    const tnsr::I<DataVector, 3>& linear_momentum_volume_integrand,\n    const tnsr::I<DataVector, 3>& coords);\n\n/// Return-by-value overload\nScalar<DataVector> adm_angular_momentum_z_volume_integrand(\n    const tnsr::I<DataVector, 3>& linear_momentum_volume_integrand,\n    const tnsr::I<DataVector, 3>& coords);\n/// @}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY XctsPointwiseFunctions)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AdmMass.cpp\n  AdmMomentum.cpp\n  CenterOfMass.cpp\n  ExtrinsicCurvature.cpp\n  LongitudinalOperator.cpp\n  SpacetimeQuantities.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AdmMass.hpp\n  AdmMomentum.hpp\n  CenterOfMass.hpp\n  ExtrinsicCurvature.hpp\n  LongitudinalOperator.hpp\n  SpacetimeQuantities.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  GeneralRelativity\n  LinearOperators\n  Serialization\n  Spectral\n  Utilities\n  INTERFACE\n  Domain\n  PRIVATE\n  ElasticityPointwiseFunctions\n  )\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/CenterOfMass.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Xcts/CenterOfMass.hpp\"\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n\nnamespace Xcts {\n\nvoid center_of_mass_surface_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& coords) {\n  const auto euclidean_radius = magnitude(coords);\n  tenex::evaluate<ti::I>(result,\n                         3. / (8. * M_PI) *\n                             (pow<4>(conformal_factor_minus_one()) +\n                              4. * pow<3>(conformal_factor_minus_one()) +\n                              6. * pow<2>(conformal_factor_minus_one()) +\n                              4. * conformal_factor_minus_one()) *\n                             coords(ti::I) / euclidean_radius());\n}\n\ntnsr::I<DataVector, 3> center_of_mass_surface_integrand(\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& coords) {\n  tnsr::I<DataVector, 3> result;\n  center_of_mass_surface_integrand(make_not_null(&result),\n                                   conformal_factor_minus_one, coords);\n  return result;\n}\n\nvoid center_of_mass_volume_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& deriv_conformal_factor,\n    const tnsr::I<DataVector, 3>& coords) {\n  const auto euclidean_radius = magnitude(coords);\n  tenex::evaluate<ti::I>(\n      result,\n      3. / (4. * M_PI * pow<2>(euclidean_radius())) *\n          (2. * pow<3>(conformal_factor()) * deriv_conformal_factor(ti::j) *\n               coords(ti::I) * coords(ti::J) +\n           pow<4>(conformal_factor()) * coords(ti::I)));\n}\n\ntnsr::I<DataVector, 3> center_of_mass_volume_integrand(\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& deriv_conformal_factor,\n    const tnsr::I<DataVector, 3>& coords) {\n  tnsr::I<DataVector, 3> result;\n  center_of_mass_volume_integrand(make_not_null(&result), conformal_factor,\n                                  deriv_conformal_factor, coords);\n  return result;\n}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/CenterOfMass.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts {\n\n/// @{\n/*!\n * \\brief Surface integrand for the center of mass calculation.\n *\n * We define the center of mass integral as\n *\n * \\begin{equation}\n *   C_\\text{CoM}^i = \\frac{3}{8 \\pi M_\\text{ADM}} \\oint_{S_\\infty} \\Big[\n *                      (\\psi - 1)^4 + 4 (\\psi - 1)^3\n *                      + 6 (\\psi - 1)^2 + 4 (\\psi - 1)\n *                    \\Big] n^i \\, dA,\n * \\end{equation}\n *\n * where $n^i = x^i / r$ and $r = \\sqrt{x^2 + y^2 + z^2}$. We use $\\psi-1$\n * instead of $\\psi$ because it is the variable solved for in the XCTS system.\n * Expanding the integrand, we see that this is identical to\n *\n * \\begin{equation}\n *   C_\\text{CoM}^i = \\frac{3}{8 \\pi M_\\text{ADM}}\n *               \\oint_{S_\\infty} (\\psi^4 - 1) n^i \\, dA,\n * \\end{equation}\n *\n * Analytically, this is equivalent to the definition in Eq. (25) of\n * \\cite Ossokine2015yla because\n * \\begin{equation}\n *   \\oint_{S_\\infty} n^i \\, dA = 0.\n * \\end{equation}\n * Numerically, we have found that subtracting $1$ from $\\psi^4$ results in less\n * round-off errors, leading to a more accurate center of mass.\n *\n * One way to interpret this integral is that we are summing over the unit\n * vectors $n^i$, rescaled by $\\psi^4$, in all directions. If $\\psi(\\vec r)$ is\n * constant, no rescaling happens and all the unit vectors cancel out. If\n * $\\psi(\\vec r)$ is not constant, then $\\vec C_\\text{CoM}$ will emerge from the\n * difference of large numbers (sum of rescaled $n^i$ in each subdomain). With\n * larger and larger numbers being involved in this cancellation (i.e. with\n * increasing radius of $S_\\infty$), we loose numerical accuracy. In other\n * words, we are seeking the subdominant terms. Since $\\psi^4 \\to 1$ in\n * conformal flatness, subtracting $1$ from it in the integrand makes the\n * numbers involved in this cancellation smaller, reducing this issue.\n *\n * \\note We don't include the ADM mass $M_{ADM}$ in this integrand. After\n * integrating the result of this function, you have to divide by $M_{ADM}$.\n *\n * \\note For consistency with `center_of_mass_volume_integrand`, this\n * integrand needs to be integrated with the Euclidean area element.\n *\n * \\see `Xcts::adm_mass_surface_integrand`\n *\n * \\warning This integral assumes that the conformal metric falls off to\n * flatness faster than $1/r^2$. That means that it cannot be directly used\n * with the Kerr-Schild metric, which falls off as $1/r$. This is not a problem\n * for XCTS with Superposed Kerr-Schild (SKS) because of the exponential\n * fall-off terms.\n *\n * \\param result output pointer\n * \\param conformal_factor_minus_one the conformal factor $\\psi - 1$\n * \\param coords the inertial coordinates $x^i$\n */\nvoid center_of_mass_surface_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& coords);\n\n/// Return-by-value overload\ntnsr::I<DataVector, 3> center_of_mass_surface_integrand(\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& coords);\n/// @}\n\n/// @{\n/*!\n * \\brief Volume integrand for the center of mass calculation.\n *\n * We cast the center of mass as an infinite volume integral by applying Gauss'\n * law on the surface integral defined in `center_of_mass_surface_integrand`:\n *\n * \\begin{equation}\n *   C_\\text{CoM}^i = \\frac{3}{8 \\pi M_\\text{ADM}}\n *                    \\int_{V_\\infty} \\Big(\n *                      4 \\psi^3 \\partial_j \\psi n^i n^j\n *                      + \\frac{2}{r} \\psi^4 n^i\n *                    \\Big) dV\n *                  = \\frac{3}{4 \\pi M_\\text{ADM}}\n *                    \\int_{V_\\infty} \\frac{1}{r^2} \\Big(\n *                      2 \\psi^3 \\partial_j \\psi x^i x^j\n *                      + \\psi^4 x^i\n *                    \\Big) dV,\n * \\end{equation}\n *\n * where $n^i = x^i / r$ and $r = \\sqrt{x^2 + y^2 + z^2}$.\n *\n * \\note For consistency with `center_of_mass_surface_integrand`, this\n * integrand needs to be integrated with the Euclidean volume element.\n *\n * \\see `center_of_mass_surface_integrand`\n *\n * \\warning This integral currently suffers from significant round-off errors.\n * It is recommended to use only the surface integral if possible.\n *\n * \\param result output pointer\n * \\param conformal_factor the conformal factor $\\psi$\n * \\param deriv_conformal_factor the gradient of the conformal factor\n * $\\partial_i \\psi$\n * \\param coords the inertial coordinates $x^i$\n */\nvoid center_of_mass_volume_integrand(\n    gsl::not_null<tnsr::I<DataVector, 3>*> result,\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& deriv_conformal_factor,\n    const tnsr::I<DataVector, 3>& coords);\n\n/// Return-by-value overload\ntnsr::I<DataVector, 3> center_of_mass_volume_integrand(\n    const Scalar<DataVector>& conformal_factor,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& deriv_conformal_factor,\n    const tnsr::I<DataVector, 3>& coords);\n/// @}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/ExtrinsicCurvature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Xcts/ExtrinsicCurvature.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts {\n\ntemplate <typename DataType>\nvoid extrinsic_curvature(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> result,\n    const Scalar<DataType>& conformal_factor, const Scalar<DataType>& lapse,\n    const tnsr::ii<DataType, 3>& conformal_metric,\n    const tnsr::II<DataType, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataType>& trace_extrinsic_curvature) {\n  tenex::evaluate<ti::i, ti::j>(\n      result,\n      pow<4>(conformal_factor()) *\n          (conformal_metric(ti::i, ti::k) * conformal_metric(ti::j, ti::l) *\n               longitudinal_shift_minus_dt_conformal_metric(ti::K, ti::L) /\n               (2. * lapse()) +\n           conformal_metric(ti::i, ti::j) * trace_extrinsic_curvature() / 3.));\n}\n\ntemplate <typename DataType>\ntnsr::ii<DataType, 3> extrinsic_curvature(\n    const Scalar<DataType>& conformal_factor, const Scalar<DataType>& lapse,\n    const tnsr::ii<DataType, 3>& conformal_metric,\n    const tnsr::II<DataType, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataType>& trace_extrinsic_curvature) {\n  tnsr::ii<DataType, 3> result{get_size(get(conformal_factor))};\n  extrinsic_curvature(\n      make_not_null(&result), conformal_factor, lapse, conformal_metric,\n      longitudinal_shift_minus_dt_conformal_metric, trace_extrinsic_curvature);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                 \\\n  template void extrinsic_curvature(                         \\\n      const gsl::not_null<tnsr::ii<DTYPE(data), 3>*> result, \\\n      const Scalar<DTYPE(data)>& conformal_factor,           \\\n      const Scalar<DTYPE(data)>& lapse,                      \\\n      const tnsr::ii<DTYPE(data), 3>& conformal_metric,      \\\n      const tnsr::II<DTYPE(data), 3>&                        \\\n          longitudinal_shift_minus_dt_conformal_metric,      \\\n      const Scalar<DTYPE(data)>& trace_extrinsic_curvature); \\\n  template tnsr::ii<DTYPE(data), 3> extrinsic_curvature(     \\\n      const Scalar<DTYPE(data)>& conformal_factor,           \\\n      const Scalar<DTYPE(data)>& lapse,                      \\\n      const tnsr::ii<DTYPE(data), 3>& conformal_metric,      \\\n      const tnsr::II<DTYPE(data), 3>&                        \\\n          longitudinal_shift_minus_dt_conformal_metric,      \\\n      const Scalar<DTYPE(data)>& trace_extrinsic_curvature);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/ExtrinsicCurvature.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts {\n\n/// @{\n/*!\n * \\brief Extrinsic curvature computed from the conformal decomposition used in\n * the XCTS system.\n *\n * The extrinsic curvature decomposition is (see Eq. 3.113 in\n * \\cite BaumgarteShapiro):\n *\n * \\begin{equation}\n *   K_{ij} = A_{ij} + \\frac{1}{3}\\gamma_{ij}K\n *     = \\frac{\\psi^4}{2\\lapse}\\left((\\bar{L}\\beta)_{ij} - \\bar{u}_{ij}\\right)\n *       + \\frac{\\psi^4}{3} \\bar{\\gamma}_{ij} K\n * \\end{equation}\n *\n * \\param result output buffer for the extrinsic curvature\n * \\param conformal_factor the conformal factor $\\psi$\n * \\param lapse the lapse $\\alpha$\n * \\param conformal_metric the conformal metric $\\bar{\\gamma}_{ij}$\n * \\param longitudinal_shift_minus_dt_conformal_metric the term\n * $(\\bar{L}\\beta)^{ij} - \\bar{u}^{ij}$. Note that $(\\bar{L}\\beta)^{ij}$ is the\n * _conformal_ longitudinal shift, and $(\\bar{L}\\beta)^{ij}=\\psi^4(L\\beta)^{ij}$\n * (Eq. 3.98 in \\cite BaumgarteShapiro). See also Xcts::longitudinal_operator.\n * \\param trace_extrinsic_curvature the trace of the extrinsic curvature,\n * $K=\\gamma^{ij}K_{ij}$. Note that it is a conformal invariant, $K=\\bar{K}$ (by\n * choice).\n */\ntemplate <typename DataType>\nvoid extrinsic_curvature(\n    const gsl::not_null<tnsr::ii<DataType, 3>*> result,\n    const Scalar<DataType>& conformal_factor, const Scalar<DataType>& lapse,\n    const tnsr::ii<DataType, 3>& conformal_metric,\n    const tnsr::II<DataType, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataType>& trace_extrinsic_curvature);\n\n/// Return-by-value overload\ntemplate <typename DataType>\ntnsr::ii<DataType, 3> extrinsic_curvature(\n    const Scalar<DataType>& conformal_factor, const Scalar<DataType>& lapse,\n    const tnsr::ii<DataType, 3>& conformal_metric,\n    const tnsr::II<DataType, 3>& longitudinal_shift_minus_dt_conformal_metric,\n    const Scalar<DataType>& trace_extrinsic_curvature);\n/// @}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/LongitudinalOperator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Xcts/LongitudinalOperator.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts {\n\ntemplate <typename DataType>\nvoid longitudinal_operator(const gsl::not_null<tnsr::II<DataType, 3>*> result,\n                           const tnsr::ii<DataType, 3>& strain,\n                           const tnsr::II<DataType, 3>& inv_metric) {\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      // Unroll first iteration of the loop over `k` to avoid filling the result\n      // with zero initially. This first assignment is the k=0, l=0 iteration:\n      result->get(i, j) =\n          (2. * inv_metric.get(i, 0) * inv_metric.get(j, 0) -\n           2. / 3. * inv_metric.get(i, j) * get<0, 0>(inv_metric)) *\n          get<0, 0>(strain);\n      // These are the remaining contributions of the k=0 iteration:\n      for (size_t l = 1; l < 3; ++l) {\n        result->get(i, j) +=\n            (2. * inv_metric.get(i, 0) * inv_metric.get(j, l) -\n             2. / 3. * inv_metric.get(i, j) * inv_metric.get(0, l)) *\n            strain.get(0, l);\n      }\n      // This is the loop from which the k=0 iteration is unrolled above:\n      for (size_t k = 1; k < 3; ++k) {\n        for (size_t l = 0; l < 3; ++l) {\n          result->get(i, j) +=\n              (2. * inv_metric.get(i, k) * inv_metric.get(j, l) -\n               2. / 3. * inv_metric.get(i, j) * inv_metric.get(k, l)) *\n              strain.get(k, l);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid longitudinal_operator(\n    const gsl::not_null<tnsr::II<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& shift, const tnsr::iJ<DataType, 3>& deriv_shift,\n    const tnsr::II<DataType, 3>& inv_metric,\n    const tnsr::Ijj<DataType, 3>& christoffel_second_kind) {\n  set_number_of_grid_points(result, shift);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      result->get(i, j) = 0.;\n      for (size_t k = 0; k < 3; ++k) {\n        result->get(i, j) +=\n            inv_metric.get(i, k) * deriv_shift.get(k, j) +\n            inv_metric.get(j, k) * deriv_shift.get(k, i) -\n            2. / 3. * inv_metric.get(i, j) * deriv_shift.get(k, k);\n        for (size_t l = 0; l < 3; ++l) {\n          result->get(i, j) +=\n              inv_metric.get(i, k) * christoffel_second_kind.get(j, k, l) *\n                  shift.get(l) +\n              inv_metric.get(j, k) * christoffel_second_kind.get(i, k, l) *\n                  shift.get(l) -\n              2. / 3. * inv_metric.get(i, j) *\n                  christoffel_second_kind.get(k, k, l) * shift.get(l);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid longitudinal_operator_flat_cartesian(\n    const gsl::not_null<tnsr::II<DataType, 3>*> result,\n    const tnsr::ii<DataType, 3>& strain) {\n  // Compute trace term in 2-2 component of the result\n  get<2, 2>(*result) = get<0, 0>(strain);\n  for (size_t d = 1; d < 3; ++d) {\n    get<2, 2>(*result) += strain.get(d, d);\n  }\n  get<2, 2>(*result) *= -2. / 3.;\n  for (size_t i = 0; i < 3; ++i) {\n    // Copy trace term to other diagonal components and complete diagonal\n    // components with non-trace contribution\n    result->get(i, i) = get<2, 2>(*result) + 2. * strain.get(i, i);\n    // Compute off-diagonal contributions\n    for (size_t j = 0; j < i; ++j) {\n      result->get(i, j) = 2. * strain.get(i, j);\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid longitudinal_operator_flat_cartesian(\n    const gsl::not_null<tnsr::II<DataType, 3>*> result,\n    const tnsr::iJ<DataType, 3>& deriv_shift) {\n  // Compute trace term in 2-2 component of the result\n  get<2, 2>(*result) = get<0, 0>(deriv_shift);\n  for (size_t d = 1; d < 3; ++d) {\n    get<2, 2>(*result) += deriv_shift.get(d, d);\n  }\n  get<2, 2>(*result) *= -2. / 3.;\n  for (size_t i = 0; i < 3; ++i) {\n    // Copy trace term to other diagonal components and complete diagonal\n    // components with non-trace contribution\n    result->get(i, i) = get<2, 2>(*result) + 2. * deriv_shift.get(i, i);\n    // Compute off-diagonal contributions\n    for (size_t j = 0; j < i; ++j) {\n      result->get(i, j) = deriv_shift.get(i, j) + deriv_shift.get(j, i);\n    }\n  }\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                     \\\n  template void longitudinal_operator(                           \\\n      gsl::not_null<tnsr::II<DTYPE(data), 3>*> result,           \\\n      const tnsr::ii<DTYPE(data), 3>& strain,                    \\\n      const tnsr::II<DTYPE(data), 3>& inv_metric);               \\\n  template void longitudinal_operator(                           \\\n      gsl::not_null<tnsr::II<DTYPE(data), 3>*> result,           \\\n      const tnsr::I<DTYPE(data), 3>& shift,                      \\\n      const tnsr::iJ<DTYPE(data), 3>& deriv_shift,               \\\n      const tnsr::II<DTYPE(data), 3>& inv_metric,                \\\n      const tnsr::Ijj<DTYPE(data), 3>& christoffel_second_kind); \\\n  template void longitudinal_operator_flat_cartesian(            \\\n      gsl::not_null<tnsr::II<DTYPE(data), 3>*> result,           \\\n      const tnsr::ii<DTYPE(data), 3>& strain);                   \\\n  template void longitudinal_operator_flat_cartesian(            \\\n      gsl::not_null<tnsr::II<DTYPE(data), 3>*> result,           \\\n      const tnsr::iJ<DTYPE(data), 3>& deriv_shift);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/LongitudinalOperator.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts {\n/// @{\n/*!\n * \\brief The longitudinal operator, or vector gradient, \\f$(L\\beta)^{ij}\\f$\n *\n * Computes the longitudinal operator\n *\n * \\f{equation}\n * (L\\beta)^{ij} = \\nabla^i \\beta^j + \\nabla^j \\beta^i -\n * \\frac{2}{3}\\gamma^{ij}\\nabla_k\\beta^k\n * \\f}\n *\n * of a vector field \\f$\\beta^i\\f$, where \\f$\\nabla\\f$ denotes the covariant\n * derivative w.r.t. the metric \\f$\\gamma\\f$ (see e.g. Eq. (3.50) in\n * \\cite BaumgarteShapiro). Note that in the XCTS equations the longitudinal\n * operator is typically applied to conformal quantities and w.r.t. the\n * conformal metric \\f$\\bar{\\gamma}\\f$.\n *\n * In terms of the symmetric \"strain\" quantity\n * \\f$B_{ij}=\\nabla_{(i}\\gamma_{j)k}\\beta^k\\f$ the longitudinal operator is:\n *\n * \\f{equation}\n * (L\\beta)^{ij} = 2\\left(\\gamma^{ik}\\gamma^{jl} -\n * \\frac{1}{3} \\gamma^{ij}\\gamma^{kl}\\right) B_{kl}\n * \\f}\n *\n * Note that the strain can be computed with `Elasticity::strain`.\n */\ntemplate <typename DataType>\nvoid longitudinal_operator(gsl::not_null<tnsr::II<DataType, 3>*> result,\n                           const tnsr::ii<DataType, 3>& strain,\n                           const tnsr::II<DataType, 3>& inv_metric);\n\ntemplate <typename DataType>\nvoid longitudinal_operator(\n    gsl::not_null<tnsr::II<DataType, 3>*> result,\n    const tnsr::I<DataType, 3>& shift, const tnsr::iJ<DataType, 3>& deriv_shift,\n    const tnsr::II<DataType, 3>& inv_metric,\n    const tnsr::Ijj<DataType, 3>& christoffel_second_kind);\n/// @}\n\n/// @{\n/*!\n * \\brief The conformal longitudinal operator \\f$(L\\beta)^{ij}\\f$ on a flat\n * conformal metric in Cartesian coordinates \\f$\\gamma_{ij}=\\delta_{ij}\\f$\n *\n * \\see `Xcts::longitudinal_operator`\n */\ntemplate <typename DataType>\nvoid longitudinal_operator_flat_cartesian(\n    gsl::not_null<tnsr::II<DataType, 3>*> result,\n    const tnsr::ii<DataType, 3>& strain);\n\ntemplate <typename DataType>\nvoid longitudinal_operator_flat_cartesian(\n    gsl::not_null<tnsr::II<DataType, 3>*> result,\n    const tnsr::iJ<DataType, 3>& deriv_shift);\n/// @}\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/SpacetimeQuantities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/Xcts/SpacetimeQuantities.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Elasticity/Strain.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Xcts/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/Xcts/LongitudinalOperator.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Xcts {\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<Scalar<DataVector>*> conformal_factor,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::ConformalFactor<DataVector> /*meta*/) const {\n  get(*conformal_factor) = get(conformal_factor_minus_one) + 1.;\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor,\n    const gsl::not_null<Cache*> /*cache*/,\n    Tags::LapseTimesConformalFactor<DataVector> /*meta*/) const {\n  get(*lapse_times_conformal_factor) =\n      get(lapse_times_conformal_factor_minus_one) + 1.;\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::SpatialMetric<DataVector, 3> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  *spatial_metric = conformal_metric;\n  for (auto& spatial_metric_component : *spatial_metric) {\n    spatial_metric_component *= pow<4>(get(conformal_factor));\n  }\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::II<DataVector, 3>*> inv_spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::InverseSpatialMetric<DataVector, 3> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  *inv_spatial_metric = inv_conformal_metric;\n  for (auto& inv_spatial_metric_component : *inv_spatial_metric) {\n    inv_spatial_metric_component /= pow<4>(get(conformal_factor));\n  }\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::iJJ<DataVector, 3>*> deriv_inv_spatial_metric,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  const auto& deriv_conformal_factor =\n      cache->get_var(*this, ::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                          tmpl::size_t<3>, Frame::Inertial>{});\n  gr::deriv_inverse_spatial_metric(\n      deriv_inv_spatial_metric, inv_conformal_metric, deriv_conformal_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = j; k < 3; ++k) {\n        auto& deriv_inv_spatial_metric_component =\n            deriv_inv_spatial_metric->get(i, j, k);\n        deriv_inv_spatial_metric_component /= pow<4>(get(conformal_factor));\n        deriv_inv_spatial_metric_component +=\n            -4. * deriv_conformal_factor.get(i) /\n            pow<5>(get(conformal_factor)) * inv_conformal_metric.get(j, k);\n      }\n    }\n  }\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> deriv_conformal_factor,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::ConformalFactor<DataVector>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  partial_derivative(deriv_conformal_factor, conformal_factor_minus_one, mesh,\n                     inv_jacobian);\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::Ijj<DataVector, 3>*>\n        spatial_christoffel_second_kind,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::SpatialChristoffelSecondKind<DataVector, 3> /*meta*/) const {\n  // Eq. (3.7) in Baumgarte/Shapiro\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  const auto& deriv_conformal_factor =\n      cache->get_var(*this, ::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                          tmpl::size_t<3>, Frame::Inertial>{});\n  *spatial_christoffel_second_kind = conformal_christoffel_second_kind;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k <= j; ++k) {\n        if (i == j) {\n          spatial_christoffel_second_kind->get(i, j, k) +=\n              2. * deriv_conformal_factor.get(k) / get(conformal_factor);\n        }\n        if (i == k) {\n          spatial_christoffel_second_kind->get(i, j, k) +=\n              2. * deriv_conformal_factor.get(j) / get(conformal_factor);\n        }\n        for (size_t l = 0; l < 3; ++l) {\n          spatial_christoffel_second_kind->get(i, j, k) -=\n              2. * conformal_metric.get(j, k) * inv_conformal_metric.get(i, l) *\n              deriv_conformal_factor.get(l) / get(conformal_factor);\n        }\n      }\n    }\n  }\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::ij<DataVector, 3>*> deriv2_conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                tmpl::size_t<3>, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  const auto& deriv_conformal_factor =\n      cache->get_var(*this, ::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                          tmpl::size_t<3>, Frame::Inertial>{});\n  // Possible optimization: store in symmetric tensor\n  partial_derivative(deriv2_conformal_factor, deriv_conformal_factor, mesh,\n                     inv_jacobian);\n  // Add Christoffel symbol terms for covariant derivative\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        deriv2_conformal_factor->get(i, j) -=\n            conformal_christoffel_second_kind.get(k, i, j) *\n            deriv_conformal_factor.get(k);\n      }\n    }\n  }\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<Scalar<DataVector>*>\n        conformal_laplacian_of_conformal_factor,\n    const gsl::not_null<Cache*> cache,\n    detail::ConformalLaplacianOfConformalFactor<DataVector> /*meta*/) const {\n  const auto& deriv2_conformal_factor = cache->get_var(\n      *this, ::Tags::deriv<::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                         tmpl::size_t<3>, Frame::Inertial>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  tenex::evaluate(conformal_laplacian_of_conformal_factor,\n                  deriv2_conformal_factor(ti::i, ti::j) *\n                      inv_conformal_metric(ti::I, ti::J));\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> spatial_ricci,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::SpatialRicci<DataVector, 3> /*meta*/) const {\n  // Eq. (3.10) in Baumgarte/Shapiro\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  const auto& deriv_conformal_factor =\n      cache->get_var(*this, ::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                          tmpl::size_t<3>, Frame::Inertial>{});\n  const auto& deriv2_conformal_factor = cache->get_var(\n      *this, ::Tags::deriv<::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                         tmpl::size_t<3>, Frame::Inertial>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  const auto& conformal_laplacian_of_conformal_factor = cache->get_var(\n      *this, detail::ConformalLaplacianOfConformalFactor<DataVector>{});\n  *spatial_ricci = conformal_ricci;\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j <= i; ++j) {\n      spatial_ricci->get(i, j) -=\n          2. *\n          (deriv2_conformal_factor.get(i, j) +\n           conformal_metric.get(i, j) *\n               get(conformal_laplacian_of_conformal_factor)) /\n          get(conformal_factor);\n      spatial_ricci->get(i, j) += 6. * deriv_conformal_factor.get(i) *\n                                  deriv_conformal_factor.get(j) /\n                                  square(get(conformal_factor));\n      for (size_t l = 0; l < 3; ++l) {\n        for (size_t m = 0; m < 3; ++m) {\n          spatial_ricci->get(i, j) -=\n              2. * conformal_metric.get(i, j) * inv_conformal_metric.get(l, m) *\n              deriv_conformal_factor.get(l) * deriv_conformal_factor.get(m) /\n              square(get(conformal_factor));\n        }\n      }\n    }\n  }\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::i<DataVector, 3>*>\n        deriv_lapse_times_conformal_factor,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::LapseTimesConformalFactor<DataVector>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  partial_derivative(deriv_lapse_times_conformal_factor,\n                     lapse_times_conformal_factor_minus_one, mesh,\n                     inv_jacobian);\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<Scalar<DataVector>*> lapse,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::Lapse<DataVector> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  const auto& lapse_times_conformal_factor =\n      cache->get_var(*this, Tags::LapseTimesConformalFactor<DataVector>{});\n  get(*lapse) = get(lapse_times_conformal_factor) / get(conformal_factor);\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::i<DataVector, 3>*> deriv_lapse,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  const auto& lapse = cache->get_var(*this, gr::Tags::Lapse<DataVector>{});\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  const auto& deriv_conformal_factor =\n      cache->get_var(*this, ::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                          tmpl::size_t<3>, Frame::Inertial>{});\n  const auto& deriv_lapse_times_conformal_factor = cache->get_var(\n      *this, ::Tags::deriv<Tags::LapseTimesConformalFactor<DataVector>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  tenex::evaluate<ti::i>(\n      deriv_lapse,\n      deriv_lapse_times_conformal_factor(ti::i) / conformal_factor() -\n          lapse() / conformal_factor() * deriv_conformal_factor(ti::i));\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift,\n    const gsl::not_null<Cache*> /*cache*/,\n    gr::Tags::Shift<DataVector, 3> /*meta*/) const {\n  for (size_t i = 0; i < 3; ++i) {\n    shift->get(i) = shift_excess.get(i) + shift_background.get(i);\n  }\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::iJ<DataVector, 3>*> deriv_shift_excess,\n    const gsl::not_null<Cache*> /*cache*/,\n    ::Tags::deriv<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial> /*meta*/) const {\n  // It's important to avoid computing a numeric derivative of the full shift\n  // (background + excess), since the background shift may increase linearly\n  // with distance (it can have `Omega x r` and `a_dot * r` terms). Instead, the\n  // derivative of the background shift is known analytically and included in\n  // `longitudinal_shift_background_minus_dt_conformal_metric`.\n  partial_derivative(deriv_shift_excess, shift_excess, mesh, inv_jacobian);\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::iJ<DataVector, 3>*> deriv_shift,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                  Frame::Inertial> /*meta*/) const {\n  const auto& deriv_shift_excess = cache->get_var(\n      *this, ::Tags::deriv<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  tenex::evaluate<ti::i, ti::J>(\n      deriv_shift,\n      deriv_shift_excess(ti::i, ti::J) + deriv_shift_background(ti::i, ti::J));\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> shift_strain,\n    const gsl::not_null<Cache*> cache,\n    Tags::ShiftStrain<DataVector, 3, Frame::Inertial> /*meta*/) const {\n  const auto& deriv_shift_excess = cache->get_var(\n      *this, ::Tags::deriv<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  Elasticity::strain(shift_strain, deriv_shift_excess, conformal_metric,\n                     deriv_conformal_metric, conformal_christoffel_first_kind,\n                     shift_excess);\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n    const gsl::not_null<Cache*> cache,\n    Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial> /*meta*/)\n    const {\n  const auto& deriv_shift_excess = cache->get_var(\n      *this, ::Tags::deriv<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  Xcts::longitudinal_operator(longitudinal_shift_excess, shift_excess,\n                              deriv_shift_excess, inv_conformal_metric,\n                              conformal_christoffel_second_kind);\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> div_longitudinal_shift_excess,\n    const gsl::not_null<Cache*> cache,\n    ::Tags::div<\n        Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>> /*meta*/)\n    const {\n  // Copy into a Variables to take the divergence because currently (Mar 2022)\n  // the `divergence` function only works with Variables. This won't be used\n  // for anything performance-critical, but adding a `divergence` overload\n  // that takes a Tensor is an obvious optimization here.\n  using tag = Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>;\n  Variables<tmpl::list<tag>> vars{mesh.number_of_grid_points()};\n  get<tag>(vars) = cache->get_var(*this, tag{});\n  const auto derivs = divergence(vars, mesh, inv_jacobian);\n  *div_longitudinal_shift_excess = get<::Tags::div<tag>>(derivs);\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::II<DataVector, 3>*>\n        longitudinal_shift_minus_dt_conformal_metric,\n    const gsl::not_null<Cache*> cache,\n    detail::LongitudinalShiftMinusDtConformalMetric<DataVector> /*meta*/)\n    const {\n  const auto& longitudinal_shift_excess = cache->get_var(\n      *this, Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>{});\n  tenex::evaluate<ti::I, ti::J>(\n      longitudinal_shift_minus_dt_conformal_metric,\n      longitudinal_shift_excess(ti::I, ti::J) +\n          longitudinal_shift_background_minus_dt_conformal_metric(ti::I,\n                                                                  ti::J));\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::ii<DataVector, 3>*> extrinsic_curvature,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::ExtrinsicCurvature<DataVector, 3> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  const auto& lapse = cache->get_var(*this, gr::Tags::Lapse<DataVector>{});\n  const auto& longitudinal_shift_minus_dt_conformal_metric = cache->get_var(\n      *this, detail::LongitudinalShiftMinusDtConformalMetric<DataVector>{});\n  Xcts::extrinsic_curvature(\n      extrinsic_curvature, conformal_factor, lapse, conformal_metric,\n      longitudinal_shift_minus_dt_conformal_metric, trace_extrinsic_curvature);\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::HamiltonianConstraint<DataVector> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  const auto& conformal_laplacian_of_conformal_factor = cache->get_var(\n      *this, detail::ConformalLaplacianOfConformalFactor<DataVector>{});\n  const auto& inv_spatial_metric =\n      cache->get_var(*this, gr::Tags::InverseSpatialMetric<DataVector, 3>{});\n  const auto& extrinsic_curvature =\n      cache->get_var(*this, gr::Tags::ExtrinsicCurvature<DataVector, 3>{});\n  // Eq. 3.12 in BaumgarteShapiro, divided by 2 for consistency with SpEC\n  tenex::evaluate(hamiltonian_constraint,\n                  4. * conformal_laplacian_of_conformal_factor() -\n                      0.5 * (conformal_factor() * conformal_ricci_scalar() +\n                             pow<5>(conformal_factor()) *\n                                 (square(trace_extrinsic_curvature()) -\n                                  inv_spatial_metric(ti::I, ti::K) *\n                                      inv_spatial_metric(ti::J, ti::L) *\n                                      extrinsic_curvature(ti::i, ti::j) *\n                                      extrinsic_curvature(ti::k, ti::l) -\n                                  16. * M_PI * energy_density())));\n}\n\nvoid SpacetimeQuantitiesComputer::operator()(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n    const gsl::not_null<Cache*> cache,\n    gr::Tags::MomentumConstraint<DataVector, 3> /*meta*/) const {\n  const auto& conformal_factor =\n      cache->get_var(*this, Tags::ConformalFactor<DataVector>{});\n  const auto& lapse_times_conformal_factor =\n      cache->get_var(*this, Tags::LapseTimesConformalFactor<DataVector>{});\n  const auto& deriv_conformal_factor =\n      cache->get_var(*this, ::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                          tmpl::size_t<3>, Frame::Inertial>{});\n  const auto& deriv_lapse_times_conformal_factor = cache->get_var(\n      *this, ::Tags::deriv<Tags::LapseTimesConformalFactor<DataVector>,\n                           tmpl::size_t<3>, Frame::Inertial>{});\n  const auto& div_longitudinal_shift_excess = cache->get_var(\n      *this,\n      ::Tags::div<\n          Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>>{});\n  const auto& longitudinal_shift_minus_dt_conformal_metric = cache->get_var(\n      *this, detail::LongitudinalShiftMinusDtConformalMetric<DataVector>{});\n  // Eq. 3.109 in BaumgarteShapiro\n  tenex::evaluate<ti::I>(\n      momentum_constraint,\n      0.5 * (div_longitudinal_shift_excess(ti::I) +\n             div_longitudinal_shift_background_minus_dt_conformal_metric(\n                 ti::I) +\n             conformal_christoffel_second_kind(ti::I, ti::j, ti::k) *\n                 longitudinal_shift_minus_dt_conformal_metric(ti::J, ti::K) +\n             conformal_christoffel_contracted(ti::j) *\n                 longitudinal_shift_minus_dt_conformal_metric(ti::I, ti::J) -\n             longitudinal_shift_minus_dt_conformal_metric(ti::I, ti::J) *\n                 (deriv_lapse_times_conformal_factor(ti::j) /\n                      lapse_times_conformal_factor() -\n                  7. * deriv_conformal_factor(ti::j) / conformal_factor()) -\n             4. / 3. * lapse_times_conformal_factor() / conformal_factor() *\n                 inv_conformal_metric(ti::I, ti::J) *\n                 deriv_trace_extrinsic_curvature(ti::j)) -\n          8. * M_PI * lapse_times_conformal_factor() *\n              cube(conformal_factor()) * momentum_density(ti::I));\n}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PointwiseFunctions/Xcts/SpacetimeQuantities.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts {\n\nnamespace detail {\ntemplate <typename DataType>\nstruct ConformalLaplacianOfConformalFactor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType>\nstruct LongitudinalShiftMinusDtConformalMetric : db::SimpleTag {\n  using type = tnsr::II<DataType, 3>;\n};\n}  // namespace detail\n\n/// General-relativistic 3+1 quantities computed from XCTS variables.\nusing SpacetimeQuantities = CachedTempBuffer<\n    Tags::ConformalFactor<DataVector>,\n    Tags::LapseTimesConformalFactor<DataVector>,\n    // Derivatives of XCTS variables\n    ::Tags::deriv<Tags::ConformalFactor<DataVector>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    // Note: this is the _covariant_ second derivative of the conformal factor\n    // (includes Christoffel symbols)\n    ::Tags::deriv<::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                tmpl::size_t<3>, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    detail::ConformalLaplacianOfConformalFactor<DataVector>,\n    ::Tags::deriv<Tags::LapseTimesConformalFactor<DataVector>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    ::Tags::deriv<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    Xcts::Tags::ShiftStrain<DataVector, 3, Frame::Inertial>,\n    Xcts::Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>,\n    ::Tags::div<Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>>,\n    detail::LongitudinalShiftMinusDtConformalMetric<DataVector>,\n    // ADM quantities\n    gr::Tags::SpatialMetric<DataVector, 3>,\n    gr::Tags::InverseSpatialMetric<DataVector, 3>,\n    ::Tags::deriv<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    gr::Tags::Lapse<DataVector>,\n    ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    gr::Tags::Shift<DataVector, 3>,\n    ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n    gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>,\n    gr::Tags::SpatialRicci<DataVector, 3>,\n    // Constraints\n    gr::Tags::HamiltonianConstraint<DataVector>,\n    gr::Tags::MomentumConstraint<DataVector, 3>>;\n\n/// `CachedTempBuffer` computer class for 3+1 quantities from XCTS variables.\n/// See `Xcts::SpacetimeQuantities`.\nstruct SpacetimeQuantitiesComputer {\n  using Cache = SpacetimeQuantities;\n\n  void operator()(gsl::not_null<Scalar<DataVector>*> conformal_factor,\n                  gsl::not_null<Cache*> cache,\n                  Tags::ConformalFactor<DataVector> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*> lapse_times_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      Tags::LapseTimesConformalFactor<DataVector> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::ii<DataVector, 3>*> spatial_metric,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::SpatialMetric<DataVector, 3> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::II<DataVector, 3>*> inv_spatial_metric,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::InverseSpatialMetric<DataVector, 3> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::iJJ<DataVector, 3>*> deriv_inv_spatial_metric,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::i<DataVector, 3>*> deriv_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ConformalFactor<DataVector>, tmpl::size_t<3>,\n                    Frame::Inertial> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::Ijj<DataVector, 3>*> spatial_christoffel_second_kind,\n      gsl::not_null<Cache*> cache,\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::ij<DataVector, 3>*> deriv2_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<::Tags::deriv<Tags::ConformalFactor<DataVector>,\n                                  tmpl::size_t<3>, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const;\n  void operator()(\n      gsl::not_null<Scalar<DataVector>*>\n          conformal_laplacian_of_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      detail::ConformalLaplacianOfConformalFactor<DataVector> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::ii<DataVector, 3>*> spatial_ricci,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::SpatialRicci<DataVector, 3> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::i<DataVector, 3>*> deriv_lapse_times_conformal_factor,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::LapseTimesConformalFactor<DataVector>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataVector>*> lapse,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Lapse<DataVector> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::i<DataVector, 3>*> deriv_lapse,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataVector, 3>*> shift,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::Shift<DataVector, 3> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::iJ<DataVector, 3>*> deriv_shift_excess,\n      gsl::not_null<Cache*> cache,\n      ::Tags::deriv<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::iJ<DataVector, 3>*> deriv_shift,\n                  gsl::not_null<Cache*> cache,\n                  ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                                Frame::Inertial> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::ii<DataVector, 3>*> shift_strain,\n      gsl::not_null<Cache*> cache,\n      Tags::ShiftStrain<DataVector, 3, Frame::Inertial> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::II<DataVector, 3>*> longitudinal_shift_excess,\n      gsl::not_null<Cache*> cache,\n      Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial> /*meta*/)\n      const;\n  void operator()(\n      gsl::not_null<tnsr::I<DataVector, 3>*> div_longitudinal_shift_excess,\n      gsl::not_null<Cache*> cache,\n      ::Tags::div<Tags::LongitudinalShiftExcess<\n          DataVector, 3, Frame::Inertial>> /*meta*/) const;\n  void operator()(\n      gsl::not_null<tnsr::II<DataVector, 3>*>\n          longitudinal_shift_minus_dt_conformal_metric,\n      gsl::not_null<Cache*> cache,\n      detail::LongitudinalShiftMinusDtConformalMetric<DataVector> /*meta*/)\n      const;\n  void operator()(gsl::not_null<tnsr::ii<DataVector, 3>*> extrinsic_curvature,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::ExtrinsicCurvature<DataVector, 3> /*meta*/) const;\n  void operator()(gsl::not_null<Scalar<DataVector>*> hamiltonian_constraint,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::HamiltonianConstraint<DataVector> /*meta*/) const;\n  void operator()(gsl::not_null<tnsr::I<DataVector, 3>*> momentum_constraint,\n                  gsl::not_null<Cache*> cache,\n                  gr::Tags::MomentumConstraint<DataVector, 3> /*meta*/) const;\n\n  // NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members)\n  // XCTS variables\n  const Scalar<DataVector>& conformal_factor_minus_one;\n  const Scalar<DataVector>& lapse_times_conformal_factor_minus_one;\n  const tnsr::I<DataVector, 3>& shift_excess;\n  // Background\n  const tnsr::ii<DataVector, 3>& conformal_metric;\n  const tnsr::II<DataVector, 3>& inv_conformal_metric;\n  const tnsr::ijj<DataVector, 3>& deriv_conformal_metric;\n  const tnsr::ijj<DataVector, 3>& conformal_christoffel_first_kind;\n  const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind;\n  const tnsr::i<DataVector, 3>& conformal_christoffel_contracted;\n  const tnsr::ii<DataVector, 3>& conformal_ricci;\n  const Scalar<DataVector>& conformal_ricci_scalar;\n  const Scalar<DataVector>& trace_extrinsic_curvature;\n  const tnsr::i<DataVector, 3>& deriv_trace_extrinsic_curvature;\n  const tnsr::I<DataVector, 3>& shift_background;\n  const tnsr::iJ<DataVector, 3>& deriv_shift_background;\n  const tnsr::II<DataVector, 3>&\n      longitudinal_shift_background_minus_dt_conformal_metric;\n  const tnsr::I<DataVector, 3>&\n      div_longitudinal_shift_background_minus_dt_conformal_metric;\n  const Scalar<DataVector>& energy_density;\n  const tnsr::I<DataVector, 3>& momentum_density;\n  // Grid\n  const Mesh<3>& mesh;\n  const InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>&\n      inv_jacobian;\n  // NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members)\n};\n\nnamespace Tags {\n/// Compute tag for the 3+1 quantities `Tags` from XCTS variables. The `Tags`\n/// can be any subset of the tags supported by `Xcts::SpacetimeQuantities`.\ntemplate <typename Tags>\nstruct SpacetimeQuantitiesCompute : ::Tags::Variables<Tags>, db::ComputeTag {\n  using base = ::Tags::Variables<Tags>;\n  using argument_tags = tmpl::list<\n      domain::Tags::Mesh<3>, ConformalFactorMinusOne<DataVector>,\n      LapseTimesConformalFactorMinusOne<DataVector>,\n      ShiftExcess<DataVector, 3, Frame::Inertial>,\n      ConformalMetric<DataVector, 3, Frame::Inertial>,\n      InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n      ::Tags::deriv<ConformalMetric<DataVector, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial>,\n      ConformalChristoffelFirstKind<DataVector, 3, Frame::Inertial>,\n      ConformalChristoffelSecondKind<DataVector, 3, Frame::Inertial>,\n      ConformalChristoffelContracted<DataVector, 3, Frame::Inertial>,\n      ConformalRicciTensor<DataVector, 3, Frame::Inertial>,\n      ConformalRicciScalar<DataVector>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                    tmpl::size_t<3>, Frame::Inertial>,\n      ShiftBackground<DataVector, 3, Frame::Inertial>,\n      ::Tags::deriv<ShiftBackground<DataVector, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial>,\n      LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,\n                                                        Frame::Inertial>,\n      ::Tags::div<LongitudinalShiftBackgroundMinusDtConformalMetric<\n          DataVector, 3, Frame::Inertial>>,\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>, 0>,\n      gr::Tags::Conformal<gr::Tags::MomentumDensity<DataVector, 3>, 0>,\n      domain::Tags::Mesh<3>,\n      domain::Tags::InverseJacobian<3, Frame::ElementLogical, Frame::Inertial>>;\n  template <typename... Args>\n  static void function(const gsl::not_null<typename base::type*> result,\n                       const Mesh<3>& mesh, const Args&... args) {\n    const size_t num_points = mesh.number_of_grid_points();\n    if (result->number_of_grid_points() != num_points) {\n      result->initialize(num_points);\n    }\n    SpacetimeQuantities spacetime_quantities{num_points};\n    const SpacetimeQuantitiesComputer computer{args...};\n    tmpl::for_each<Tags>(\n        [&spacetime_quantities, &computer, &result](const auto tag_v) {\n          using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n          get<tag>(*result) = spacetime_quantities.get_var(computer, tag{});\n        });\n  }\n};\n}  // namespace Tags\n\n}  // namespace Xcts\n"
  },
  {
    "path": "src/PythonBindings/BoundChecks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <stdexcept>\n#include <string>\n\n#include \"Utilities/PrettyType.hpp\"\n\nnamespace py_bindings {\n/*!\n * \\ingroup PythonBindingsGroup\n * \\brief Check if a vector-like object access is in bounds. Throws\n * std::runtime_error if it is not.\n */\ntemplate <typename T>\nvoid bounds_check(const T& t, const size_t i) {\n  if (i >= t.size()) {\n    throw std::runtime_error{\"Out of bounds access (\" + std::to_string(i) +\n                             \") into \" + pretty_type::name<T>() + \" of size \" +\n                             std::to_string(t.size())};\n  }\n}\n/*!\n * \\ingroup PythonBindingsGroup\n * \\brief Check if a matrix-like object access is in bounds. Throws\n * std::runtime_error if it is not.\n */\ntemplate <typename T>\nvoid matrix_bounds_check(const T& matrix, const size_t row,\n                         const size_t column) {\n  if (row >= matrix.rows() or column >= matrix.columns()) {\n    throw std::runtime_error{\"Out of bounds access (\" + std::to_string(row) +\n                             \", \" + std::to_string(column) +\n                             \") into Matrix of size (\" +\n                             std::to_string(matrix.rows()) + \", \" +\n                             std::to_string(matrix.columns()) + \")\"};\n  }\n}\n}  // namespace py_bindings\n"
  },
  {
    "path": "src/PythonBindings/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (${BUILD_PYTHON_BINDINGS})\n  set(LIBRARY PythonBindings)\n\n  add_spectre_library(${LIBRARY})\n\n  spectre_target_sources(\n    ${LIBRARY}\n    PRIVATE\n    CharmCompatibility.cpp\n    FormalineCompatibility.cpp\n    InfoAtLinkCompatibility.cpp\n    )\n\n  spectre_target_headers(\n    ${LIBRARY}\n    INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n    HEADERS\n    BoundChecks.hpp\n    )\n\n  target_link_libraries(\n    ${LIBRARY}\n    PRIVATE\n    Informer\n    PUBLIC\n    Utilities\n    )\nendif()\n"
  },
  {
    "path": "src/PythonBindings/CharmCompatibility.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Charm looks for this function but since we build without a main function or\n// main module we just have it be empty\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmissing-declarations\"\nextern \"C\" void CkRegisterMainModule(void) {}\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "src/PythonBindings/FormalineCompatibility.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/Formaline.hpp\"\n\nnamespace formaline {\nstd::vector<char> get_archive() {\n  return {'N', 'o', 't', ' ', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd'};\n}\n\nstd::string get_environment_variables() {\n  return \"Not supported in Python\";\n}\n\nstd::string get_build_info() {\n  return \"Not supported in Python\";\n}\n\nstd::string get_paths() { return \"Not supported in Python.\"; }\n}  // namespace formaline\n"
  },
  {
    "path": "src/PythonBindings/InfoAtLinkCompatibility.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Informer/InfoFromBuild.hpp\"\n\n#include <string>\n\nstd::string link_date() { return \"Unavailable in Python bindings\"; }\n\nstd::string executable_name() { return \"Unavailable in Python bindings\"; }\n\nstd::string git_description() { return \"Unavailable in Python bindings\"; }\n\nstd::string git_branch() { return \"Unavailable in Python bindings\"; }\n"
  },
  {
    "path": "src/Time/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  SelfStartActions.hpp\n  )\n"
  },
  {
    "path": "src/Time/Actions/SelfStartActions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nclass TimeStepper;\nnamespace TimeSteppers::Tags {\nstruct FixedOrder;\n}  // namespace TimeSteppers::Tags\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// Definition of the integrator self-starting procedure.\n///\n/// The self-start procedure generates \\f$N\\f$ function values of\n/// accuracy \\f$O\\left((\\Delta t)^N\\right)\\f$, where \\f$N\\f$ is the\n/// negative of the initial slab number.  To generate values, it\n/// requires the time stepper to be a multistep integrator that will\n/// produce an order-\\f$k\\f$-accurate result given \\f$k-1\\f$ history\n/// values.\n///\n/// If the integrator is started from analytic history data or\n/// requires no history (such as for a substep integrator), then the\n/// initial slab number can be set to zero and no self-start steps\n/// will be taken.\n///\n/// \\details\n/// To self-start a multistep integrator, the function is integrated\n/// repeatedly with increasing accuracy.  A first order integrator\n/// (Euler's method) requires no history values, so it can be used,\n/// starting from the initial point, to generate a\n/// first-order-accurate value at a later time.  We then reset to the\n/// start conditions and use the new \"history\" value (at a discarded\n/// point later in time) to take two steps with a second-order method.\n/// These values are second-order-accurate despite the history only\n/// being first-order because the calculation of the change in the\n/// value multiplies the previous derivatives by a factor of\n/// \\f$\\Delta t\\f$.  The time and value are then again reset to their\n/// starting values and we start again at third order, and so on.\n///\n/// The choice of performing the low-order integrations in the same\n/// direction as the main integration makes this a _forward_\n/// self-start procedure, as opposed to a _backward_ procedure that\n/// produces values for times before the start time.  The primary\n/// advantage of the forward version is that the solution is\n/// guaranteed to exist after the start time, but not before.  It also\n/// makes bookkeeping easier, as the reset after each order increase\n/// is to the initial state, rather than to a time one step further\n/// back for each order.  It does have the disadvantage, however, of\n/// leaving a non-monotonic history at the end of the procedure, which\n/// the main evolution loop must be able to handle.\n///\n/// Each time the state is reset the slab number is increased by one.\n/// This ensures that the evaluations are considered to be ordered in\n/// their evaluation order, even though they are not monotonic in\n/// time.  When the slab number reaches zero the initialization\n/// procedure is complete and history appropriate for use for an\n/// integrator of order \\f$N+1\\f$ has been generated.\n///\n/// The self-start procedure performs all its evaluations before the\n/// end of the first time step of the main evolution.  It is important\n/// that none of the early steps fall at the same time as the\n/// self-start history values, so the main evolution should not\n/// decrease its step size on the first step after the procedure.\n/// Additionally, the history times will not be monotonicly increasing\n/// until \\f$N\\f$ steps have been taken.  The local-time-stepping\n/// calculations require monotonic time, so local time-stepping should\n/// not be initiated until the self-start values have expired from the\n/// history.  These restrictions on step-size changing are checked in\n/// the TimeStepper::can_change_step_size method.\nnamespace SelfStart {\n/// Self-start tags\nnamespace Tags {\n/// \\ingroup TimeGroup\n/// The initial value of a quantity.  The contents are stored in a\n/// tuple to avoid putting duplicate tensors into the DataBox.\ntemplate <typename Tag>\nstruct InitialValue : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = std::tuple<typename Tag::type>;\n};\n}  // namespace Tags\n\n/// Self-start actions\nnamespace Actions {\nnamespace detail {\ntemplate <typename System, bool HasPrimitives>\nstruct vars_to_save_impl {\n  using type = tmpl::flatten<tmpl::list<typename System::variables_tag>>;\n};\n\ntemplate <typename System>\nstruct vars_to_save_impl<System, true> {\n  using type =\n      tmpl::flatten<tmpl::list<typename System::variables_tag,\n                               typename System::primitive_variables_tag>>;\n};\n\ntemplate <typename System>\nusing vars_to_save = typename vars_to_save_impl<\n    System, System::has_primitive_and_conservative_vars>::type;\n}  // namespace detail\n\n/// \\ingroup ActionsGroup\n/// \\ingroup TimeGroup\n/// Prepares the evolution for time-stepper self-starting.\n///\n/// Stores the initial values of the variables and time step and sets\n/// an appropriate step for self-starting.\n///\n/// \\details The self-start procedure must take place within one slab,\n/// and we want to avoid the end of the slab so that we don't have to\n/// deal with another action advancing the slab on us.  There will be\n/// problems if the main evolution tries to evaluate at a time that\n/// the self-start procedure used before the self-start version falls\n/// out of the history, so we have to make sure that does not happen.\n/// We can't do that by making sure our steps are large enough to keep\n/// ahead because of the slab restriction, so instead we have to make\n/// the self-start step smaller to ensure no collisions.  The easiest\n/// way to do that is to fit the entire procedure before the first\n/// real step, so we pick an initialization time step of\n/// \\f$\\Delta t/(N+1)\\f$, for \\f$\\Delta t\\f$ the initial evolution\n/// time step and \\f$N\\f$ the number of history points to be\n/// generated.\n///\n/// The original `Tags::TimeStep` is temporarily stored separately\n/// during the self-start procedure, and restored to its original\n/// value at the conclusion of the self-start procedure in preparation\n/// for the main evolution, so that the initial time steps during the\n/// evolution are appropriately set according to `Initialization`\n/// phase values.\n///\n/// Uses:\n/// - GlobalCache: nothing\n/// - DataBox:\n///   - Tags::TimeStepId\n///   - Tags::TimeStep\n///   - variables_tag\n///   - primitive_variables_tag if the system has primitives\n///\n/// DataBox changes:\n/// - Adds:\n///   - SelfStart::Tags::InitialValue<Tags::TimeStep>\n///   - SelfStart::Tags::InitialValue<variables_tag>\n///   - SelfStart::Tags::InitialValue<primitive_variables_tag> if the system\n///     has primitives\n/// - Removes: nothing\n/// - Modifies: Tags::TimeStep\ntemplate <typename System,\n          template <typename> typename CacheTagPrefix = std::type_identity_t>\nstruct Initialize {\n private:\n  template <typename TagsToSave>\n  struct StoreInitialValues;\n\n  template <typename... TagsToSave>\n  struct StoreInitialValues<tmpl::list<TagsToSave...>> {\n    using simple_tags = tmpl::list<Tags::InitialValue<TagsToSave>...>;\n\n    template <typename Box>\n    static void apply(Box& box) {\n      ::Initialization::mutate_assign<simple_tags>(\n          make_not_null(&box), std::make_tuple(db::get<TagsToSave>(box))...);\n    }\n  };\n\n public:\n  using simple_tags = typename StoreInitialValues<tmpl::push_back<\n      detail::vars_to_save<System>, ::Tags::TimeStep>>::simple_tags;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const TimeDelta initial_step = db::get<::Tags::TimeStep>(box);\n    // The slab number increments each time a new point is generated\n    // until it reaches zero.  The multistep integrators all need\n    // `order` control points at distinct times, even if some of those\n    // are not counted in the reported required past steps.\n    // (Predictor stages can't line up with history points.)  This\n    // doesn't count the value as the initial time, hence the \"- 1\".\n    const auto values_needed =\n        db::get<::Tags::Next<::Tags::TimeStepId>>(box).slab_number() == 0\n            ? 0\n            : get<TimeSteppers::Tags::FixedOrder>(\n                  db::get<CacheTagPrefix<::Tags::TimeStepper<TimeStepper>>>(box)\n                      .order()) -\n                  1;\n\n    // Decrease the step so that the generated history will be\n    // entirely before the first step.  This ensures we will not\n    // generate any duplicate times in the history as we start the\n    // real evolution and that the starting procedure does not require\n    // any more information (such as function-of-time values) than the\n    // first real step.\n    const TimeDelta self_start_step = initial_step / (values_needed + 1);\n\n    StoreInitialValues<tmpl::push_back<detail::vars_to_save<System>,\n                                       ::Tags::TimeStep>>::apply(box);\n    db::mutate<::Tags::TimeStep>(\n        [&self_start_step](const gsl::not_null<::TimeDelta*> time_step) {\n          *time_step = self_start_step;\n        },\n        make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\ingroup ActionsGroup\n/// \\ingroup TimeGroup\n/// Resets the state for the next iteration if the current order is\n/// complete, and exits the self-start loop if the required order has\n/// been reached.\n///\n/// Uses:\n/// - GlobalCache: nothing\n/// - DataBox: Tags::Next<Tags::TimeStepId>\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: nothing\ntemplate <typename ExitTag, typename System>\nstruct CheckForCompletion {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const bool done_with_order =\n        db::get<::Tags::Next<::Tags::TimeStepId>>(box).is_at_slab_boundary();\n\n    if (done_with_order) {\n      tmpl::for_each<detail::vars_to_save<System>>([&box](auto tag) {\n        using Tag = tmpl::type_from<decltype(tag)>;\n        db::mutate<Tag>(\n            [](const gsl::not_null<typename Tag::type*> value,\n               const std::tuple<typename Tag::type>& initial_value) {\n              *value = get<0>(initial_value);\n            },\n            make_not_null(&box), db::get<Tags::InitialValue<Tag>>(box));\n      });\n    }\n\n    // The self start procedure begins with slab number\n    // -number_of_past_steps and counts up.  When we reach 0 we should\n    // start the evolution proper.  The first thing the evolution loop\n    // will do is update the time, so here we need to check if the\n    // next time should be the first real step.\n    if (db::get<::Tags::Next<::Tags::TimeStepId>>(box).slab_number() == 0) {\n      return {Parallel::AlgorithmExecution::Continue,\n              tmpl::index_of<ActionList, ::Actions::Label<ExitTag>>::value};\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\ingroup ActionsGroup\n/// \\ingroup TimeGroup\n/// If we have taken enough steps for this order, set the next time to\n/// the start time and increment the slab number\n///\n/// Uses:\n/// - GlobalCache: nothing\n/// - DataBox:\n///   - Tags::HistoryEvolvedVariables\n///   - Tags::TimeStepId\n///   - Tags::TimeStep\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies: Tags::Next<Tags::TimeStepId> if there is an order increase\nstruct CheckForOrderIncrease {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {  // NOLINT const\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    const auto& time = time_step_id.step_time();\n    const auto& time_step = db::get<::Tags::TimeStep>(box);\n    using history_tags = ::Tags::get_all_history_tags<DbTags>;\n    const size_t history_integration_order =\n        db::get<tmpl::front<history_tags>>(box).integration_order();\n\n    // This is correct for both AdamsBashforth and AdamsMoultonPc.\n    // The latter doesn't need as many history values, but it can't\n    // use the first step of the previous order because it would line\n    // up with the predictor stage of the first step for the current\n    // order.\n    const Time required_time =\n        (time_step.is_positive() ? time.slab().start() : time.slab().end()) +\n        history_integration_order * time_step;\n    const bool done_with_order =\n        time_step_id.substep() == 0 and time == required_time;\n\n    if (done_with_order) {\n      db::mutate<::Tags::Next<::Tags::TimeStepId>>(\n          [](const gsl::not_null<::TimeStepId*> next_time_id,\n             const ::TimeStepId& current_time_id) {\n            const Slab slab = current_time_id.step_time().slab();\n            *next_time_id =\n                TimeStepId(current_time_id.time_runs_forward(),\n                           current_time_id.slab_number() + 1,\n                           current_time_id.time_runs_forward() ? slab.start()\n                                                               : slab.end());\n          },\n          make_not_null(&box), db::get<::Tags::TimeStepId>(box));\n      tmpl::for_each<history_tags>([&box,\n                                    &history_integration_order](auto tag_v) {\n        using history_tag = typename decltype(tag_v)::type;\n        db::mutate<history_tag>(\n            [&history_integration_order](\n                const gsl::not_null<typename history_tag::type*>\n                    mutable_history) {\n              ASSERT(mutable_history->integration_order() ==\n                         history_integration_order,\n                     \"Using multiple histories of different integration \"\n                     \"orders. When using multiple histories sharing the same \"\n                     \"time stepper, all histories must maintain identical \"\n                     \"integration order.\");\n              mutable_history->integration_order(\n                  mutable_history->integration_order() + 1);\n            },\n            make_not_null(&box));\n      });\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n/// \\ingroup ActionsGroup\n/// \\ingroup TimeGroup\n/// Cleans up after the self-start procedure\n///\n/// Resets the time step to that requested for the evolution and\n/// removes temporary self-start data.\n///\n/// Uses:\n/// - GlobalCache: nothing\n/// - DataBox: SelfStart::Tags::InitialValue<Tags::TimeStep>\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes:\n///   - All SelfStart::Tags::InitialValue tags\n/// - Modifies: Tags::TimeStep\nstruct Cleanup {\n private:\n  template <typename T>\n  struct is_a_initial_value : tt::is_a<Tags::InitialValue, T> {};\n\n  using initial_step_tag = Tags::InitialValue<::Tags::TimeStep>;\n\n public:\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Reset the time step to the value requested by the user.  The\n    // variables were reset in CheckForCompletion.\n    db::mutate<::Tags::TimeStep>(\n        [](const gsl::not_null<::TimeDelta*> time_step,\n           const std::tuple<::TimeDelta>& initial_step) {\n          *time_step = get<0>(initial_step);\n        },\n        make_not_null(&box), db::get<initial_step_tag>(box));\n    using remove_tags = tmpl::filter<DbTags, is_a_initial_value<tmpl::_1>>;\n    // reset each tag to default constructed values to reduce memory usage (Data\n    // structures like `DataVector`s and `Tensor`s have negligible memory usage\n    // when default-constructed). This tends to be better for build time than\n    // constructing more DataBox types with and without this handful of tags.\n    tmpl::for_each<remove_tags>([&box](auto tag_v) {\n      using tag = typename decltype(tag_v)::type;\n      db::mutate<tag>(\n          [](auto tag_value) {\n            *tag_value = std::decay_t<decltype(*tag_value)>{};\n          },\n          make_not_null(&box));\n    });\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n\nnamespace detail {\nstruct PhaseStart;\nstruct PhaseEnd;\n}  // namespace detail\n\n/// \\ingroup TimeGroup\n/// The list of actions required to self-start an integrator.\n///\n/// \\tparam StepActions List of actions computing and recording the\n/// system derivative and updating the evolved variables (but not the\n/// time).\n///\n/// \\see SelfStart\ntemplate <typename StepActions, typename System,\n          template <typename> typename CacheTagPrefix = std::type_identity_t>\nusing self_start_procedure = tmpl::flatten<tmpl::list<\n// clang-format off\n    SelfStart::Actions::Initialize<System, CacheTagPrefix>,\n    ::Actions::Label<detail::PhaseStart>,\n    SelfStart::Actions::CheckForCompletion<detail::PhaseEnd, System>,\n    ::Actions::MutateApply<AdvanceTime<CacheTagPrefix>>,\n    SelfStart::Actions::CheckForOrderIncrease,\n    StepActions,\n    ::Actions::Goto<detail::PhaseStart>,\n    ::Actions::Label<detail::PhaseEnd>,\n    SelfStart::Actions::Cleanup,\n    ::Actions::MutateApply<AdvanceTime<CacheTagPrefix>>,\n    Parallel::Actions::TerminatePhase>>;\n// clang-format on\n}  // namespace SelfStart\n"
  },
  {
    "path": "src/Time/AdaptiveSteppingDiagnostics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n\n#include <pup.h>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nAdaptiveSteppingDiagnostics& AdaptiveSteppingDiagnostics::operator+=(\n    const AdaptiveSteppingDiagnostics& other) {\n  ASSERT(number_of_slabs == other.number_of_slabs,\n         \"Unequal number of slabs(\" << number_of_slabs << \",\"\n                                    << other.number_of_slabs << \")\");\n  ASSERT(number_of_slab_size_changes == other.number_of_slab_size_changes,\n         \"Unequal number of slab_size_changes(\"\n             << number_of_slab_size_changes << \",\"\n             << other.number_of_slab_size_changes << \")\");\n  number_of_steps += other.number_of_steps;\n  number_of_step_fraction_changes += other.number_of_step_fraction_changes;\n  number_of_step_rejections += other.number_of_step_rejections;\n  return *this;\n}\n\nvoid AdaptiveSteppingDiagnostics::pup(PUP::er& p) {\n  p | number_of_slabs;\n  p | number_of_slab_size_changes;\n  p | number_of_steps;\n  p | number_of_step_fraction_changes;\n  p | number_of_step_rejections;\n}\n\nbool operator==(const AdaptiveSteppingDiagnostics& a,\n                const AdaptiveSteppingDiagnostics& b) {\n  return a.number_of_slabs == b.number_of_slabs and\n         a.number_of_slab_size_changes == b.number_of_slab_size_changes and\n         a.number_of_steps == b.number_of_steps and\n         a.number_of_step_fraction_changes ==\n             b.number_of_step_fraction_changes and\n         a.number_of_step_rejections == b.number_of_step_rejections;\n}\n\nbool operator!=(const AdaptiveSteppingDiagnostics& a,\n                const AdaptiveSteppingDiagnostics& b) {\n  return not(a == b);\n}\n"
  },
  {
    "path": "src/Time/AdaptiveSteppingDiagnostics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nstruct AdaptiveSteppingDiagnostics {\n  uint64_t number_of_slabs = 0;\n  uint64_t number_of_slab_size_changes = 0;\n  uint64_t number_of_steps = 0;\n  uint64_t number_of_step_fraction_changes = 0;\n  uint64_t number_of_step_rejections = 0;\n\n  AdaptiveSteppingDiagnostics& operator+=(\n      const AdaptiveSteppingDiagnostics& other);\n\n  void pup(PUP::er& p);\n};\n\nbool operator==(const AdaptiveSteppingDiagnostics& a,\n                const AdaptiveSteppingDiagnostics& b);\n\nbool operator!=(const AdaptiveSteppingDiagnostics& a,\n                const AdaptiveSteppingDiagnostics& b);\n"
  },
  {
    "path": "src/Time/AdvanceTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/AdvanceTime.hpp\"\n\n#include <cstdint>\n\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace AdvanceTime_detail {\nvoid apply(const gsl::not_null<TimeStepId*> time_id,\n           const gsl::not_null<TimeStepId*> next_time_id,\n           const gsl::not_null<TimeDelta*> time_step,\n           const gsl::not_null<double*> time,\n           const gsl::not_null<uint64_t*> step_number_within_slab,\n           const gsl::not_null<AdaptiveSteppingDiagnostics*> diags,\n           const TimeStepper& time_stepper, const bool using_error_control) {\n  const bool new_step = next_time_id->substep() == 0;\n  if (time_id->slab_number() != next_time_id->slab_number()) {\n    *step_number_within_slab = 0;\n    ++diags->number_of_slabs;\n    // Put this here instead of unconditionally doing the next\n    // check because on the first call time_id doesn't have a\n    // valid slab so comparing the times will FPE.\n    ++diags->number_of_steps;\n  } else if (new_step) {\n    ++(*step_number_within_slab);\n    ++diags->number_of_steps;\n  }\n\n  *time_id = *next_time_id;\n  *time_step = time_step->with_slab(time_id->step_time().slab());\n\n  if (using_error_control) {\n    *next_time_id =\n        time_stepper.next_time_id_for_error(*next_time_id, *time_step);\n  } else {\n    *next_time_id = time_stepper.next_time_id(*next_time_id, *time_step);\n  }\n  *time = time_id->substep_time();\n}\n}  // namespace AdvanceTime_detail\n"
  },
  {
    "path": "src/Time/AdvanceTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass AdaptiveSteppingDiagnostics;\nclass TimeDelta;\nclass TimeStepId;\nclass TimeStepper;\nnamespace Tags {\nstruct AdaptiveSteppingDiagnostics;\ntemplate <typename Tag>\nstruct Next;\nstruct StepperErrorEstimatesEnabled;\nstruct StepNumberWithinSlab;\nstruct Time;\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace AdvanceTime_detail {\nvoid apply(gsl::not_null<TimeStepId*> time_id,\n           gsl::not_null<TimeStepId*> next_time_id,\n           gsl::not_null<TimeDelta*> time_step, gsl::not_null<double*> time,\n           gsl::not_null<uint64_t*> step_number_within_slab,\n           gsl::not_null<AdaptiveSteppingDiagnostics*> diags,\n           const TimeStepper& time_stepper, bool using_error_control);\n}  // namespace AdvanceTime_detail\n\n/// \\ingroup TimeGroup\n/// \\brief Advance time one substep\n///\n/// Replaces the time state with the `Tags::Next` values, advances the\n/// `Tags::Next` values, and sets `Tags::Time` to the new substep time.\ntemplate <template <typename> typename CacheTagPrefix = std::type_identity_t>\nstruct AdvanceTime {\n  using return_tags =\n      tmpl::list<Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n                 Tags::Time, Tags::StepNumberWithinSlab,\n                 Tags::AdaptiveSteppingDiagnostics>;\n  using argument_tags =\n      tmpl::list<CacheTagPrefix<Tags::TimeStepper<TimeStepper>>,\n                 Tags::StepperErrorEstimatesEnabled>;\n\n  static constexpr auto apply = &AdvanceTime_detail::apply;\n};\n"
  },
  {
    "path": "src/Time/ApproximateTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cmath>\n\n#include \"Time/ApproximateTime.hpp\"\n\n#define DEFINE_OP(lprefix, rprefix, ret, op, lhs, rhs)            \\\n  ret operator op(const lprefix##lhs& a, const rprefix##rhs& b) { \\\n    return {a.value() op b.value()};                              \\\n  }\n\n#define DEFINE_ALL_OPS(lprefix, rprefix)                                     \\\n  DEFINE_OP(lprefix, rprefix, bool, ==, Time, Time)                          \\\n  DEFINE_OP(lprefix, rprefix, bool, !=, Time, Time)                          \\\n  DEFINE_OP(lprefix, rprefix, bool, <, Time, Time)                           \\\n  DEFINE_OP(lprefix, rprefix, bool, >, Time, Time)                           \\\n  DEFINE_OP(lprefix, rprefix, bool, <=, Time, Time)                          \\\n  DEFINE_OP(lprefix, rprefix, bool, >=, Time, Time)                          \\\n  DEFINE_OP(lprefix, rprefix, bool, ==, TimeDelta, TimeDelta)                \\\n  DEFINE_OP(lprefix, rprefix, bool, !=, TimeDelta, TimeDelta)                \\\n  DEFINE_OP(lprefix, rprefix, bool, <, TimeDelta, TimeDelta)                 \\\n  DEFINE_OP(lprefix, rprefix, bool, >, TimeDelta, TimeDelta)                 \\\n  DEFINE_OP(lprefix, rprefix, bool, <=, TimeDelta, TimeDelta)                \\\n  DEFINE_OP(lprefix, rprefix, bool, >=, TimeDelta, TimeDelta)                \\\n  DEFINE_OP(lprefix, rprefix, ApproximateTimeDelta, -, Time, Time)           \\\n  DEFINE_OP(lprefix, rprefix, ApproximateTime, +, Time, TimeDelta)           \\\n  DEFINE_OP(lprefix, rprefix, ApproximateTime, +, TimeDelta, Time)           \\\n  DEFINE_OP(lprefix, rprefix, ApproximateTime, -, Time, TimeDelta)           \\\n  DEFINE_OP(lprefix, rprefix, ApproximateTimeDelta, +, TimeDelta, TimeDelta) \\\n  DEFINE_OP(lprefix, rprefix, ApproximateTimeDelta, -, TimeDelta, TimeDelta) \\\n  DEFINE_OP(lprefix, rprefix, double, /, TimeDelta, TimeDelta)\n\n#ifdef __clang__\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wbraced-scalar-init\"\n#endif  /* __clang__ */\nDEFINE_ALL_OPS(Approximate, )\nDEFINE_ALL_OPS(, Approximate)\nDEFINE_ALL_OPS(Approximate, Approximate)\n#ifdef __clang__\n#pragma GCC diagnostic pop\n#endif  /* __clang__ */\n\n#undef DEFINE_ALL_OPS\n#undef DEFINE_OP\n\nApproximateTimeDelta operator+(const ApproximateTimeDelta& a) { return a; }\nApproximateTimeDelta operator-(const ApproximateTimeDelta& a) {\n  return {-a.value()};\n}\n\nApproximateTimeDelta operator*(const ApproximateTimeDelta& a,\n                               const TimeDelta::rational_t& b) {\n  return {a.value() * b.value()};\n}\nApproximateTimeDelta operator*(const TimeDelta::rational_t& a,\n                               const ApproximateTimeDelta& b) {\n  return {a.value() * b.value()};\n}\nApproximateTimeDelta operator/(const ApproximateTimeDelta& a,\n                               const TimeDelta::rational_t& b) {\n  return {a.value() * b.inverse().value()};\n}\n\nstd::ostream& operator<<(std::ostream& os, const ApproximateTime& t) {\n  return os << t.value();\n}\n\nApproximateTimeDelta abs(const ApproximateTimeDelta& t) {\n  return {std::abs(t.value())};\n}\n\nstd::ostream& operator<<(std::ostream& os, const ApproximateTimeDelta& dt) {\n  return os << dt.value();\n}\n"
  },
  {
    "path": "src/Time/ApproximateTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <ostream>\n\n#include \"Time/Time.hpp\"\n\n/// \\ingroup TimeGroup\n/// Time-like interface to a double for use with dense output\nstruct ApproximateTime {\n  double time = std::numeric_limits<double>::signaling_NaN();\n  double value() const { return time; }\n};\n\n/// \\ingroup TimeGroup\n/// TimeDelta-like interface to a double for use with dense output\nstruct ApproximateTimeDelta {\n  double delta = std::numeric_limits<double>::signaling_NaN();\n  double value() const { return delta; }\n  bool is_positive() const { return delta > 0.; }\n};\n\n// Duplicate most of the interface for Time and TimeDelta.  Leave out\n// the mutation functions, as these are only supposed to be used for\n// temporary calculations.\n\n#define DECLARE_OP(lprefix, rprefix, ret, op, lhs, rhs) \\\n  ret operator op(const lprefix##lhs& a, const rprefix##rhs& b);\n\n#define DECLARE_ALL_OPS(lprefix, rprefix)                                     \\\n  DECLARE_OP(lprefix, rprefix, bool, ==, Time, Time)                          \\\n  DECLARE_OP(lprefix, rprefix, bool, !=, Time, Time)                          \\\n  DECLARE_OP(lprefix, rprefix, bool, <, Time, Time)                           \\\n  DECLARE_OP(lprefix, rprefix, bool, >, Time, Time)                           \\\n  DECLARE_OP(lprefix, rprefix, bool, <=, Time, Time)                          \\\n  DECLARE_OP(lprefix, rprefix, bool, >=, Time, Time)                          \\\n  DECLARE_OP(lprefix, rprefix, bool, ==, TimeDelta, TimeDelta)                \\\n  DECLARE_OP(lprefix, rprefix, bool, !=, TimeDelta, TimeDelta)                \\\n  DECLARE_OP(lprefix, rprefix, bool, <, TimeDelta, TimeDelta)                 \\\n  DECLARE_OP(lprefix, rprefix, bool, >, TimeDelta, TimeDelta)                 \\\n  DECLARE_OP(lprefix, rprefix, bool, <=, TimeDelta, TimeDelta)                \\\n  DECLARE_OP(lprefix, rprefix, bool, >=, TimeDelta, TimeDelta)                \\\n  DECLARE_OP(lprefix, rprefix, ApproximateTimeDelta, -, Time, Time)           \\\n  DECLARE_OP(lprefix, rprefix, ApproximateTime, +, Time, TimeDelta)           \\\n  DECLARE_OP(lprefix, rprefix, ApproximateTime, +, TimeDelta, Time)           \\\n  DECLARE_OP(lprefix, rprefix, ApproximateTime, -, Time, TimeDelta)           \\\n  DECLARE_OP(lprefix, rprefix, ApproximateTimeDelta, +, TimeDelta, TimeDelta) \\\n  DECLARE_OP(lprefix, rprefix, ApproximateTimeDelta, -, TimeDelta, TimeDelta) \\\n  DECLARE_OP(lprefix, rprefix, double, /, TimeDelta, TimeDelta)\n\nDECLARE_ALL_OPS(Approximate, )\nDECLARE_ALL_OPS(, Approximate)\nDECLARE_ALL_OPS(Approximate, Approximate)\n\n#undef DECLARE_ALL_OPS\n#undef DECLARE_OP\n\nApproximateTimeDelta operator+(const ApproximateTimeDelta& a);\nApproximateTimeDelta operator-(const ApproximateTimeDelta& a);\n\nApproximateTimeDelta operator*(const ApproximateTimeDelta& a,\n                               const TimeDelta::rational_t& b);\nApproximateTimeDelta operator*(const TimeDelta::rational_t& a,\n                               const ApproximateTimeDelta& b);\nApproximateTimeDelta operator/(const ApproximateTimeDelta& a,\n                               const TimeDelta::rational_t& b);\n\nstd::ostream& operator<<(std::ostream& os, const ApproximateTime& t);\n\nApproximateTimeDelta abs(const ApproximateTimeDelta& t);\n\nstd::ostream& operator<<(std::ostream& os, const ApproximateTimeDelta& dt);\n"
  },
  {
    "path": "src/Time/BoundaryHistory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/container/static_vector.hpp>\n#include <cstddef>\n#include <iosfwd>\n#include <optional>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/CircularDeque.hpp\"\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StlBoilerplate.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace TimeSteppers {\nnamespace BoundaryHistory_detail {\ntemplate <typename Data>\nstruct StepData {\n  struct Entry {\n    TimeStepId id;\n    Data data;\n\n    void pup(PUP::er& p);\n  };\n\n  size_t integration_order;\n  // Unlike in History, the full step is the first entry, so we need\n  // one more element.\n  boost::container::static_vector<Entry, history_max_substeps + 1> substeps;\n\n  void pup(PUP::er& p);\n};\n}  // namespace BoundaryHistory_detail\n\n/// \\ingroup TimeSteppersGroup\n/// Access to the list of `TimeStepId`s in a `BoundaryHistory`.\n///\n/// For simplicity of implementation, iterable-container access is not\n/// provided for substeps within a step, but is instead provided\n/// through additional methods on this class.\n/// @{\nclass ConstBoundaryHistoryTimes\n    : public stl_boilerplate::RandomAccessSequence<ConstBoundaryHistoryTimes,\n                                                   const TimeStepId, false> {\n protected:\n  ~ConstBoundaryHistoryTimes() = default;\n\n public:\n  virtual size_t size() const = 0;\n  virtual const TimeStepId& operator[](size_t n) const = 0;\n  virtual const TimeStepId& operator[](\n      const std::pair<size_t, size_t>& step_and_substep) const = 0;\n  virtual size_t integration_order(size_t n) const = 0;\n  virtual size_t integration_order(const TimeStepId& id) const = 0;\n  virtual size_t number_of_substeps(size_t n) const = 0;\n  /// This returns the same value for any substep of the same step.\n  virtual size_t number_of_substeps(const TimeStepId& id) const = 0;\n};\n\nclass MutableBoundaryHistoryTimes : public ConstBoundaryHistoryTimes {\n protected:\n  ~MutableBoundaryHistoryTimes() = default;\n\n public:\n  /// Remove the earliest step and its substeps.\n  virtual void pop_front() const = 0;\n  virtual void clear() const = 0;\n  /// Remove all substeps for step \\p n except for the step itself.\n  virtual void clear_substeps(size_t n) const = 0;\n};\n/// @}\n\n/// \\ingroup TimeSteppersGroup\n/// Type erased base class for evaluating BoundaryHistory couplings.\n///\n/// The results are cached in the `BoundaryHistory` class.\ntemplate <typename UntypedCouplingResult>\nclass BoundaryHistoryEvaluator {\n public:\n  virtual const UntypedCouplingResult& operator()(\n      const TimeStepId& local_id, const TimeStepId& remote_id) const = 0;\n\n protected:\n  ~BoundaryHistoryEvaluator() = default;\n};\n\n/// \\ingroup TimeSteppersGroup\n/// History data used by a TimeStepper for boundary integration.\n///\n/// \\tparam LocalData local data passed to the boundary coupling\n/// \\tparam RemoteData remote data passed to the boundary coupling\n/// \\tparam UntypedCouplingResult math_wrapper_type of cached boundary couplings\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nclass BoundaryHistory {\n  static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>,\n                                      UntypedCouplingResult>);\n\n public:\n  BoundaryHistory() = default;\n  BoundaryHistory(const BoundaryHistory& other) = default;\n  BoundaryHistory(BoundaryHistory&&) = default;\n  BoundaryHistory& operator=(const BoundaryHistory& other) = default;\n  BoundaryHistory& operator=(BoundaryHistory&&) = default;\n  ~BoundaryHistory() = default;\n\n  // Factored out of ConstSideAccess so that the base classes of\n  // MutableSideAccess can have protected destructors.\n  template <bool Local, bool Mutable>\n  class SideAccessCommon\n      : public tmpl::conditional_t<Mutable, MutableBoundaryHistoryTimes,\n                                   ConstBoundaryHistoryTimes> {\n   public:\n    using MutableData = tmpl::conditional_t<Local, LocalData, RemoteData>;\n    using Data = tmpl::conditional_t<Mutable, MutableData, const MutableData>;\n\n    size_t size() const override;\n    static constexpr size_t max_size() {\n      return std::remove_cvref_t<decltype(std::declval<ConstSideAccess<Local>>()\n                                              .parent_data())>::max_size();\n    }\n\n    const TimeStepId& operator[](size_t n) const override;\n    const TimeStepId& operator[](\n        const std::pair<size_t, size_t>& step_and_substep) const override;\n\n    size_t integration_order(size_t n) const override;\n    size_t integration_order(const TimeStepId& id) const override;\n\n    size_t number_of_substeps(size_t n) const override;\n    size_t number_of_substeps(const TimeStepId& id) const override;\n\n    /// Access the data stored on the side.  When performed through a\n    /// `MutableSideAccess`, these allow modification of the data.\n    /// Performing such modifications likely invalidates the coupling\n    /// cache for the associated `BoundaryHistory` object, which\n    /// should be cleared.\n    /// @{\n    Data& data(size_t n) const;\n    Data& data(const TimeStepId& id) const;\n    /// @}\n\n    /// Apply \\p func to each entry.\n    ///\n    /// The function \\p func must accept two arguments, one of type\n    /// `const TimeStepId&` and a second of either type `const Data&`\n    /// or `gsl::not_null<Data*>`, with the `not_null` version only\n    /// available if this is a `MutableSideAccess`.  If \\p func takes\n    /// a `not_null`, it must return a `bool` indicating if it\n    /// modified the entry.  If any entries are modified, the coupling\n    /// cache of parent `BoundaryHistory` will be cleared.\n    template <typename Func>\n    void for_each(Func&& func) const;\n\n   protected:\n    ~SideAccessCommon() = default;\n\n    tmpl::conditional_t<\n        Mutable, CircularDeque<BoundaryHistory_detail::StepData<MutableData>>&,\n        const CircularDeque<BoundaryHistory_detail::StepData<MutableData>>&>\n    parent_data() const;\n\n    auto& step_data(const TimeStepId& id) const;\n\n    auto& entry(const TimeStepId& id) const;\n\n    auto& entry(const std::pair<size_t, size_t>& step_and_substep) const;\n\n    using StoredHistory =\n        tmpl::conditional_t<Mutable, BoundaryHistory, const BoundaryHistory>;\n    explicit SideAccessCommon(gsl::not_null<StoredHistory*> parent);\n\n    gsl::not_null<StoredHistory*> parent_;\n  };\n\n  template <bool Local>\n  class MutableSideAccess final : public SideAccessCommon<Local, true> {\n   public:\n    using Data = tmpl::conditional_t<Local, LocalData, RemoteData>;\n\n    void pop_front() const override;\n    void clear() const override;\n    void clear_substeps(size_t n) const override;\n\n    void insert(const TimeStepId& id, size_t integration_order,\n                Data data) const;\n\n    void insert_initial(const TimeStepId& id, size_t integration_order,\n                        Data data) const;\n\n   private:\n    friend class BoundaryHistory;\n    explicit MutableSideAccess(gsl::not_null<BoundaryHistory*> parent);\n  };\n\n  template <bool Local>\n  class ConstSideAccess final : public SideAccessCommon<Local, false> {\n   private:\n    friend class BoundaryHistory;\n    explicit ConstSideAccess(gsl::not_null<const BoundaryHistory*> parent);\n  };\n\n  MutableSideAccess<true> local();\n  ConstSideAccess<true> local() const;\n\n  MutableSideAccess<false> remote();\n  ConstSideAccess<false> remote() const;\n\n private:\n  template <typename Coupling>\n  class EvaluatorImpl final\n      : public BoundaryHistoryEvaluator<UntypedCouplingResult> {\n   public:\n    const UntypedCouplingResult& operator()(\n        const TimeStepId& local_id, const TimeStepId& remote_id) const override;\n\n   private:\n    friend class BoundaryHistory;\n\n    EvaluatorImpl(const gsl::not_null<const BoundaryHistory*> parent,\n                  Coupling coupling)\n        : parent_(parent), coupling_(std::move(coupling)) {}\n\n    gsl::not_null<const BoundaryHistory*> parent_;\n    Coupling coupling_;\n  };\n\n public:\n  /// Obtain an object that can evaluate type-erased boundary\n  /// couplings.\n  ///\n  /// The passed functor must take objects of types `LocalData` and\n  /// `RemoteData` and return an object with math_wrapper_type\n  /// `UntypedCouplingResult`.  Results are cached, so different calls\n  /// to this function should pass equivalent couplings.\n  template <typename Coupling>\n  auto evaluator(Coupling&& coupling) const {\n    return EvaluatorImpl<Coupling>(this, std::forward<Coupling>(coupling));\n  }\n\n  /// Clear the cached values.\n  ///\n  /// This is required after existing history entries that have been\n  /// used in coupling calculations are mutated.\n  void clear_coupling_cache();\n\n  void pup(PUP::er& p);\n\n  template <bool IncludeData>\n  std::ostream& print(std::ostream& os, size_t padding_size = 0) const;\n\n private:\n  void insert_local(const TimeStepId& id, size_t integration_order,\n                    LocalData data);\n  void insert_remote(const TimeStepId& id, size_t integration_order,\n                     RemoteData data);\n\n  void insert_initial_local(const TimeStepId& id, size_t integration_order,\n                            LocalData data);\n  void insert_initial_remote(const TimeStepId& id, size_t integration_order,\n                             RemoteData data);\n\n  void pop_local();\n  void pop_remote();\n\n  void clear_substeps_local(size_t n);\n  void clear_substeps_remote(size_t n);\n\n  std::tuple<std::optional<UntypedCouplingResult>&, const LocalData&,\n             const RemoteData&>\n  find_cache_entry(const TimeStepId& local_id,\n                   const TimeStepId& remote_id) const;\n\n  CircularDeque<BoundaryHistory_detail::StepData<LocalData>> local_data_{};\n  CircularDeque<BoundaryHistory_detail::StepData<RemoteData>> remote_data_{};\n\n  template <typename Data>\n  using CouplingSubsteps =\n      boost::container::static_vector<Data, history_max_substeps + 1>;\n\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable CircularDeque<CouplingSubsteps<\n      CircularDeque<CouplingSubsteps<std::optional<UntypedCouplingResult>>>>>\n      couplings_;\n};\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\ntemplate <typename Func>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::for_each(Func&& func) const {\n  bool entries_changed = false;\n  for (auto& step : parent_data()) {\n    for (auto& substep : step.substeps) {\n      if constexpr (std::is_invocable_v<Func&, const TimeStepId&,\n                                        const Data&>) {\n        func(std::as_const(substep.id), std::as_const(substep.data));\n      } else {\n        static_assert(Mutable,\n                      \"Cannot perform mutating for_each on a ConstSideAccess\");\n        if (func(std::as_const(substep.id), make_not_null(&substep.data))) {\n          entries_changed = true;\n        }\n      }\n    }\n  }\n  if constexpr (Mutable) {\n    if (entries_changed) {\n      // A minor optimization would be to only clear the cache entries\n      // that have actually been invalidated, but most things that\n      // modify the history modify all the entries.\n      parent_->clear_coupling_cache();\n    }\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <typename Coupling>\nauto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    EvaluatorImpl<Coupling>::operator()(const TimeStepId& local_id,\n                                        const TimeStepId& remote_id) const\n    -> const UntypedCouplingResult& {\n  const auto [coupling_entry, local_data, remote_data] =\n      parent_->find_cache_entry(local_id, remote_id);\n  if (not coupling_entry.has_value()) {\n    auto new_entry = coupling_(local_data, remote_data);\n    static_assert(std::is_same_v<math_wrapper_type<decltype(new_entry)>,\n                                 UntypedCouplingResult>);\n    coupling_entry.emplace(into_math_wrapper_type(std::move(new_entry)));\n  }\n  return *coupling_entry;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nstd::ostream& operator<<(std::ostream& os,\n                         const BoundaryHistory<LocalData, RemoteData,\n                                               UntypedCouplingResult>& history);\n}  // namespace TimeSteppers\n\n// Documentation for macro defined in the tpp file\n#ifdef SPECTRE_DOXYGEN_INVOKED\n/// \\ingroup TimeSteppersGroup\n/// Explicitly instantiate BoundaryHistory and helpers.  Should be\n/// called with the template arguments for `BoundaryHistory`.\n#define INSTANTIATE_BOUNDARY_HISTORY(...) UNSPECIFIED\n#endif  // SPECTRE_DOXYGEN_INVOKED\n"
  },
  {
    "path": "src/Time/BoundaryHistory.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Time/BoundaryHistory.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/CircularDeque.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/PupBoost.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\nnamespace BoundaryHistory_detail {\ntemplate <typename Data>\nvoid StepData<Data>::Entry::pup(PUP::er& p) {\n  p | id;\n  p | data;\n}\n\ntemplate <typename Data>\nvoid StepData<Data>::pup(PUP::er& p) {\n  p | integration_order;\n  p | substeps;\n}\n\ntemplate <typename Data>\nbool operator<(const StepData<Data>& a, const StepData<Data>& b) {\n  return a.substeps.front().id < b.substeps.front().id;\n}\n\ntemplate <typename Data>\nbool operator<(const TimeStepId& a, const StepData<Data>& b) {\n  return a < b.substeps.front().id;\n}\n\ntemplate <typename Data>\nbool operator<(const StepData<Data>& a, const TimeStepId& b) {\n  return a.substeps.front().id < b;\n}\n}  // namespace BoundaryHistory_detail\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nsize_t BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::size() const {\n  return parent_data().size();\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nconst TimeStepId&\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::SideAccessCommon<\n    Local, Mutable>::operator[](const size_t n) const {\n  return (*this)[{n, 0}];\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nconst TimeStepId&\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::SideAccessCommon<\n    Local, Mutable>::operator[](const std::pair<size_t, size_t>&\n                                    step_and_substep) const {\n  return entry(step_and_substep).id;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nsize_t BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::integration_order(const size_t n) const {\n  return parent_data()[n].integration_order;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nsize_t\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::SideAccessCommon<\n    Local, Mutable>::integration_order(const TimeStepId& id) const {\n  return step_data(id).integration_order;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nsize_t BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::number_of_substeps(const size_t n) const {\n  return parent_data()[n].substeps.size();\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nsize_t\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::SideAccessCommon<\n    Local, Mutable>::number_of_substeps(const TimeStepId& id) const {\n  return step_data(id).substeps.size();\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nauto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::data(const size_t n) const -> Data& {\n  return parent_data()[n].substeps.front().data;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nauto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::data(const TimeStepId& id) const\n    -> Data& {\n  return entry(id).data;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nauto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::parent_data() const\n    -> tmpl::conditional_t<\n        Mutable, CircularDeque<BoundaryHistory_detail::StepData<MutableData>>&,\n        const CircularDeque<BoundaryHistory_detail::StepData<MutableData>>&> {\n  if constexpr (Local) {\n    return parent_->local_data_;\n  } else {\n    return parent_->remote_data_;\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nauto& BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::step_data(const TimeStepId& id) const {\n  auto entry = std::upper_bound(parent_data().begin(), parent_data().end(), id);\n  ASSERT(entry != parent_data().begin(), \"Id \" << id << \" not present.\");\n  --entry;\n  ASSERT(id.substep() < entry->substeps.size() and\n             entry->substeps[id.substep()].id == id,\n         \"Id \" << id << \" not present.\");\n  return *entry;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nauto& BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::entry(const TimeStepId& id) const {\n  // Bounds and consistency are checked in step_data()\n  return step_data(id).substeps[id.substep()];\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nauto& BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    SideAccessCommon<Local, Mutable>::entry(\n        const std::pair<size_t, size_t>& step_and_substep) const {\n  ASSERT(step_and_substep.first < parent_data().size(),\n         \"Step out of range: \" << step_and_substep.first);\n  auto& substeps = parent_data()[step_and_substep.first].substeps;\n  ASSERT(step_and_substep.second < substeps.size(),\n         \"Substep out of range: \" << step_and_substep.second);\n  return substeps[step_and_substep.second];\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local, bool Mutable>\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::SideAccessCommon<\n    Local, Mutable>::SideAccessCommon(const gsl::not_null<StoredHistory*>\n                                          parent)\n    : parent_(parent) {}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local>\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    MutableSideAccess<Local>::MutableSideAccess(\n        const gsl::not_null<BoundaryHistory*> parent)\n    : SideAccessCommon<Local, true>(parent) {}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local>\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::ConstSideAccess<\n    Local>::ConstSideAccess(const gsl::not_null<const BoundaryHistory*> parent)\n    : SideAccessCommon<Local, false>(parent) {}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nauto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::local()\n    -> MutableSideAccess<true> {\n  return MutableSideAccess<true>(this);\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nauto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::local()\n    const -> ConstSideAccess<true> {\n  return ConstSideAccess<true>(this);\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nauto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::remote()\n    -> MutableSideAccess<false> {\n  return MutableSideAccess<false>(this);\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nauto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::remote()\n    const -> ConstSideAccess<false> {\n  return ConstSideAccess<false>(this);\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    MutableSideAccess<Local>::pop_front() const {\n  if constexpr (Local) {\n    this->parent_->pop_local();\n  } else {\n    this->parent_->pop_remote();\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local>\nvoid BoundaryHistory<LocalData, RemoteData,\n                     UntypedCouplingResult>::MutableSideAccess<Local>::clear()\n    const {\n  while (not this->empty()) {\n    pop_front();\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    MutableSideAccess<Local>::clear_substeps(const size_t n) const {\n  if constexpr (Local) {\n    this->parent_->clear_substeps_local(n);\n  } else {\n    this->parent_->clear_substeps_remote(n);\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    MutableSideAccess<Local>::insert(const TimeStepId& id,\n                                     const size_t integration_order,\n                                     Data data) const {\n  ASSERT(this->parent_data().empty() or\n             id > this->parent_data().back().substeps.back().id,\n         \"New data not newer than current data.\");\n  if constexpr (Local) {\n    this->parent_->insert_local(id, integration_order, std::move(data));\n  } else {\n    this->parent_->insert_remote(id, integration_order, std::move(data));\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool Local>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    MutableSideAccess<Local>::insert_initial(const TimeStepId& id,\n                                             const size_t integration_order,\n                                             Data data) const {\n  ASSERT(id.substep() == 0, \"Cannot insert_initial with substeps.\");\n  ASSERT(this->parent_data().empty() or\n             id < this->parent_data().front().substeps.front().id,\n         \"New data not older than current data.\");\n  if constexpr (Local) {\n    this->parent_->insert_initial_local(id, integration_order, std::move(data));\n  } else {\n    this->parent_->insert_initial_remote(id, integration_order,\n                                         std::move(data));\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData,\n                     UntypedCouplingResult>::clear_coupling_cache() {\n  for (auto& remote_step : couplings_) {\n    for (auto& remote_substep : remote_step) {\n      for (auto& local_step : remote_substep) {\n        for (auto& local_substep : local_step) {\n          local_substep.reset();\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::pup(\n    PUP::er& p) {\n  p | local_data_;\n  p | remote_data_;\n  p | couplings_;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\ntemplate <bool IncludeData>\nstd::ostream&\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::print(\n    std::ostream& os, const size_t padding_size) const {\n  const std::string pad(padding_size, ' ');\n  using ::operator<<;\n  const auto do_print = [&os, &pad](const auto& times) {\n    for (size_t step = 0; step < times.size(); ++step) {\n      const size_t number_of_substeps = times.number_of_substeps(step);\n      for (size_t substep = 0; substep < number_of_substeps; ++substep) {\n        const auto id = times[{step, substep}];\n        os << pad << \" Time: \" << id;\n        if (substep == 0) {\n          os << \" (order \" << times.integration_order(step) << \")\";\n        }\n        os << \"\\n\";\n        if constexpr (IncludeData) {\n          os << pad << \"  Data: \";\n          // os << times.data(id) fails to compile on gcc-11\n          print_stl(os, times.data(id));\n          os << \"\\n\";\n        }\n      }\n    }\n  };\n  os << pad << \"Local Data:\\n\";\n  do_print(local());\n  os << pad << \"Remote Data:\\n\";\n  do_print(remote());\n  return os;\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    insert_local(const TimeStepId& id, const size_t integration_order,\n                 LocalData data) {\n  if (id.substep() == 0) {\n    local_data_.push_back({integration_order, {}});\n  } else {\n    ASSERT(integration_order == local_data_.back().integration_order,\n           \"Cannot change integration order during a step.\");\n  }\n  local_data_.back().substeps.push_back({id, std::move(data)});\n  for (auto& remote_step : couplings_) {\n    for (auto& remote_substep : remote_step) {\n      if (id.substep() == 0) {\n        remote_substep.emplace_back(1_st);\n      } else {\n        remote_substep.back().emplace_back();\n      }\n    }\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    insert_remote(const TimeStepId& id, const size_t integration_order,\n                  RemoteData data) {\n  if (id.substep() == 0) {\n    remote_data_.push_back({integration_order, {}});\n  } else {\n    ASSERT(integration_order == remote_data_.back().integration_order,\n           \"Cannot change integration order during a step.\");\n  }\n  remote_data_.back().substeps.push_back({id, std::move(data)});\n  if (id.substep() == 0) {\n    couplings_.emplace_back(1_st);\n  } else {\n    couplings_.back().emplace_back();\n  }\n  for (const auto& local_step : local_data_) {\n    couplings_.back().back().emplace_back(local_step.substeps.size());\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    insert_initial_local(const TimeStepId& id, const size_t integration_order,\n                         LocalData data) {\n  local_data_.push_front({integration_order, {}});\n  local_data_.front().substeps.push_back({id, std::move(data)});\n  for (auto& remote_step : couplings_) {\n    for (auto& remote_substep : remote_step) {\n      remote_substep.emplace_front(1_st);\n    }\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    insert_initial_remote(const TimeStepId& id, const size_t integration_order,\n                          RemoteData data) {\n  remote_data_.push_front({integration_order, {}});\n  remote_data_.front().substeps.push_back({id, std::move(data)});\n  couplings_.emplace_front(1_st);\n  for (const auto& local_step : local_data_) {\n    couplings_.front().back().emplace_back(local_step.substeps.size());\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData,\n                     UntypedCouplingResult>::pop_local() {\n  local_data_.pop_front();\n  for (auto& remote_step : couplings_) {\n    for (auto& remote_substep : remote_step) {\n      remote_substep.pop_front();\n    }\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData,\n                     UntypedCouplingResult>::pop_remote() {\n  remote_data_.pop_front();\n  couplings_.pop_front();\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    clear_substeps_local(const size_t n) {\n  local_data_[n].substeps.erase(local_data_[n].substeps.begin() + 1,\n                                local_data_[n].substeps.end());\n  for (auto& remote_step : couplings_) {\n    for (auto& remote_substep : remote_step) {\n      auto& local_step = remote_substep[n];\n      local_step.erase(local_step.begin() + 1, local_step.end());\n    }\n  }\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nvoid BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::\n    clear_substeps_remote(const size_t n) {\n  remote_data_[n].substeps.erase(remote_data_[n].substeps.begin() + 1,\n                                 remote_data_[n].substeps.end());\n  auto& remote_step = couplings_[n];\n  remote_step.erase(remote_step.begin() + 1, remote_step.end());\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nstd::tuple<std::optional<UntypedCouplingResult>&, const LocalData&,\n           const RemoteData&>\nBoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::find_cache_entry(\n    const TimeStepId& local_id, const TimeStepId& remote_id) const {\n  auto local_entry =\n      std::upper_bound(local_data_.begin(), local_data_.end(), local_id);\n  ASSERT(local_entry != local_data_.begin(), \"local_id not present\");\n  --local_entry;\n  ASSERT(local_id.substep() < local_entry->substeps.size() and\n             local_entry->substeps[local_id.substep()].id == local_id,\n         \"local_id not present\");\n  const auto local_step_offset =\n      static_cast<size_t>(local_entry - local_data_.begin());\n\n  auto remote_entry =\n      std::upper_bound(remote_data_.begin(), remote_data_.end(), remote_id);\n  ASSERT(remote_entry != remote_data_.begin(), \"remote_id not present\");\n  --remote_entry;\n  ASSERT(remote_id.substep() < remote_entry->substeps.size() and\n             remote_entry->substeps[remote_id.substep()].id == remote_id,\n         \"remote_id not present\");\n  const auto remote_step_offset =\n      static_cast<size_t>(remote_entry - remote_data_.begin());\n\n  return {couplings_[remote_step_offset][remote_id.substep()][local_step_offset]\n                    [local_id.substep()],\n          local_entry->substeps[local_id.substep()].data,\n          remote_entry->substeps[remote_id.substep()].data};\n}\n\ntemplate <typename LocalData, typename RemoteData,\n          typename UntypedCouplingResult>\nstd::ostream& operator<<(\n    std::ostream& os,\n    const BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>&\n        history) {\n  return history.template print<true>(os);\n}\n}  // namespace TimeSteppers\n\n#define INSTANTIATE_BOUNDARY_HISTORY(...)                                      \\\n  template class TimeSteppers::BoundaryHistory<__VA_ARGS__>;                   \\\n  template class TimeSteppers::BoundaryHistory<__VA_ARGS__>::ConstSideAccess<  \\\n      false>;                                                                  \\\n  template class TimeSteppers::BoundaryHistory<__VA_ARGS__>::ConstSideAccess<  \\\n      true>;                                                                   \\\n  template class TimeSteppers::BoundaryHistory<                                \\\n      __VA_ARGS__>::MutableSideAccess<false>;                                  \\\n  template class TimeSteppers::BoundaryHistory<                                \\\n      __VA_ARGS__>::MutableSideAccess<true>;                                   \\\n  template class TimeSteppers::BoundaryHistory<__VA_ARGS__>::SideAccessCommon< \\\n      false, false>;                                                           \\\n  template class TimeSteppers::BoundaryHistory<__VA_ARGS__>::SideAccessCommon< \\\n      false, true>;                                                            \\\n  template class TimeSteppers::BoundaryHistory<__VA_ARGS__>::SideAccessCommon< \\\n      true, false>;                                                            \\\n  template class TimeSteppers::BoundaryHistory<__VA_ARGS__>::SideAccessCommon< \\\n      true, true>;                                                             \\\n  template std::ostream&                                                       \\\n  TimeSteppers::BoundaryHistory<__VA_ARGS__>::print<false>(                    \\\n      std::ostream & os, const size_t padding_size) const;                     \\\n  template std::ostream&                                                       \\\n  TimeSteppers::BoundaryHistory<__VA_ARGS__>::print<true>(                     \\\n      std::ostream & os, const size_t padding_size) const;\n"
  },
  {
    "path": "src/Time/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Time)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AdaptiveSteppingDiagnostics.cpp\n  AdvanceTime.cpp\n  ApproximateTime.cpp\n  ChooseLtsStepSize.cpp\n  History.cpp\n  LargestStepperError.cpp\n  SelfStart.cpp\n  Slab.cpp\n  SlabRoundingError.cpp\n  StepperErrorEstimate.cpp\n  StepperErrorTolerances.cpp\n  Time.cpp\n  TimeSequence.cpp\n  TimeStepId.cpp\n  TimeStepRequest.cpp\n  TimeStepRequestProcessor.cpp\n  VariableOrderAlgorithm.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AdaptiveSteppingDiagnostics.hpp\n  AdvanceTime.hpp\n  ApproximateTime.hpp\n  BoundaryHistory.hpp\n  BoundaryHistory.tpp\n  ChangeStepSize.hpp\n  ChangeTimeStepperOrder.hpp\n  ChangeTimeStepperOrder.tpp\n  ChooseLtsStepSize.hpp\n  CleanHistory.hpp\n  CleanHistory.tpp\n  EvolutionOrdering.hpp\n  History.hpp\n  LargestStepperError.hpp\n  RecordTimeStepperData.hpp\n  RecordTimeStepperData.tpp\n  RequestsStepperErrorTolerances.hpp\n  SelfStart.hpp\n  Slab.hpp\n  SlabRoundingError.hpp\n  StepperErrorEstimate.hpp\n  StepperErrorTolerances.hpp\n  Time.hpp\n  TimeSequence.hpp\n  TimeStepId.hpp\n  TimeStepRequest.hpp\n  TimeStepRequestProcessor.hpp\n  UpdateU.hpp\n  UpdateU.tpp\n  VariableOrderAlgorithm.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Boost::boost\n  DataStructures\n  Domain\n  ErrorHandling\n  EventsAndTriggers\n  Options\n  Serialization\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(ChangeSlabSize)\nadd_subdirectory(OptionTags)\nadd_subdirectory(StepChoosers)\nadd_subdirectory(Tags)\nadd_subdirectory(TimeSteppers)\nadd_subdirectory(Triggers)\n"
  },
  {
    "path": "src/Time/ChangeSlabSize/Action.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <map>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Time/ChangeSlabSize/ChangeSlabSize.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/MinimumTimeStep.hpp\"\n#include \"Time/TimeStepRequestProcessor.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace Tags {\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace tuples {\ntemplate <class... Tags>\nclass TaggedTuple;\n}  // namespace tuples\n/// \\endcond\n\nnamespace Actions {\n/// \\ingroup ActionsGroup\n/// \\ingroup TimeGroup\n/// Adjust the slab size based on previous executions of\n/// Events::ChangeSlabSize\n///\n/// Uses:\n/// - DataBox:\n///   - Tags::HistoryEvolvedVariables\n///   - Tags::TimeStep\n///   - Tags::TimeStepId\n///   - Tags::TimeStepper<TimeStepper>\n///\n/// DataBox changes:\n/// - Adds: nothing\n/// - Removes: nothing\n/// - Modifies:\n///   - Tags::Next<Tags::TimeStepId>\n///   - Tags::TimeStep\n///   - Tags::TimeStepId\nstruct ChangeSlabSize {\n  using const_global_cache_tags = tmpl::list<::Tags::MinimumTimeStep>;\n  using simple_tags =\n      tmpl::list<::Tags::ChangeSlabSize::NewSlabSize,\n                 ::Tags::ChangeSlabSize::NumberOfExpectedMessages>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    if (not time_step_id.is_at_slab_boundary()) {\n      return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    }\n\n    TimeStepRequestProcessor step_requests(time_step_id.time_runs_forward());\n\n    const auto slab_number = time_step_id.slab_number();\n    const auto& expected_messages_map =\n        db::get<::Tags::ChangeSlabSize::NumberOfExpectedMessages>(box);\n    if (not expected_messages_map.empty() and\n        expected_messages_map.begin()->first == slab_number) {\n      const size_t expected_messages = expected_messages_map.begin()->second;\n      ASSERT(expected_messages > 0,\n             \"Should only create map entries when sending messages.\");\n\n      const auto& slab_size_messages =\n          db::get<::Tags::ChangeSlabSize::NewSlabSize>(box);\n      if (slab_size_messages.empty()) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n      const int64_t first_received_change_slab =\n          slab_size_messages.begin()->first;\n\n      ASSERT(first_received_change_slab >= slab_number,\n             \"Received data for a change at slab \" << first_received_change_slab\n             << \" but it is already slab \" << slab_number);\n      if (first_received_change_slab != slab_number) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n\n      const auto& received_changes = slab_size_messages.begin()->second;\n      ASSERT(expected_messages >= received_changes.size(),\n             \"Received \" << received_changes.size()\n                         << \" size change messages at slab \" << slab_number\n                         << \", but only expected \" << expected_messages);\n      if (received_changes.size() != expected_messages) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n\n      // We have all the data we need.\n\n      step_requests =\n          alg::accumulate(slab_size_messages.begin()->second, step_requests);\n\n      db::mutate<::Tags::ChangeSlabSize::NumberOfExpectedMessages,\n                 ::Tags::ChangeSlabSize::NewSlabSize>(\n          [](const gsl::not_null<std::map<int64_t, size_t>*> expected,\n             const gsl::not_null<\n                 std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                 sizes) {\n            expected->erase(expected->begin());\n            sizes->erase(sizes->begin());\n          },\n          make_not_null(&box));\n    }\n    const double new_slab_end = step_requests.step_end(\n        time_step_id.step_time().value(),\n        db::get<::Tags::ChangeSlabSize::SlabSizeGoal>(box));\n\n    if (const auto new_goal = step_requests.new_step_size_goal();\n        new_goal.has_value()) {\n      db::mutate<::Tags::ChangeSlabSize::SlabSizeGoal>(\n          [&](const gsl::not_null<double*> slab_size_goal) {\n            *slab_size_goal = *new_goal;\n          },\n          make_not_null(&box));\n    }\n\n    const TimeStepper& time_stepper =\n        db::get<::Tags::TimeStepper<TimeStepper>>(box);\n\n    // Sometimes time steppers need to run with a fixed step size.\n    // This is generally at the start of an evolution when the history\n    // is in an unusual state.\n    if (tmpl::as_pack<::Tags::get_all_history_tags<DbTags>>(\n            [&]<typename... HistoryTags>(tmpl::type_<HistoryTags>... /*meta*/) {\n              return (... and time_stepper.can_change_step_size(\n                                  time_step_id, db::get<HistoryTags>(box)));\n            })) {\n      change_slab_size(make_not_null(&box), new_slab_end);\n    }\n\n    step_requests.error_on_hard_limit(\n        db::get<::Tags::TimeStep>(box).value(),\n        (time_step_id.step_time() + db::get<::Tags::TimeStep>(box)).value());\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n"
  },
  {
    "path": "src/Time/ChangeSlabSize/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Event.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Action.hpp\n  ChangeSlabSize.hpp\n  Event.hpp\n  Tags.hpp\n  )\n"
  },
  {
    "path": "src/Time/ChangeSlabSize/ChangeSlabSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct AdaptiveSteppingDiagnostics;\nstruct MinimumTimeStep;\ntemplate <typename Tag>\nstruct Next;\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nclass TimeStepper;\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// Change the slab size, updating all quantities in the DataBox\n/// depending on it.\n///\n/// The end of the slab (in the appropriate direction for the\n/// evolution's flow of time) will be set to \\p new_slab_end.  The\n/// time step is set to the same fraction of the new slab as it was of\n/// the old.\ntemplate <typename DbTags>\nvoid change_slab_size(const gsl::not_null<db::DataBox<DbTags>*> box,\n                      const double new_slab_end) {\n  const TimeStepId& old_time_step_id = db::get<Tags::TimeStepId>(*box);\n\n  ASSERT(old_time_step_id.is_at_slab_boundary(),\n         \"Cannot change slab size in middle of slab: \" << old_time_step_id);\n\n  const Slab old_slab = old_time_step_id.step_time().slab();\n  const double old_slab_end = old_time_step_id.time_runs_forward()\n                                  ? old_slab.end().value()\n                                  : old_slab.start().value();\n\n  if (new_slab_end == old_slab_end) {\n    return;\n  }\n\n  const TimeDelta& old_time_step = db::get<Tags::TimeStep>(*box);\n\n  const Slab new_slab = old_time_step_id.time_runs_forward()\n                            ? Slab(old_slab.start().value(), new_slab_end)\n                            : Slab(new_slab_end, old_slab.end().value());\n  // We are at a slab boundary, so the substep is 0.\n  const TimeStepId new_time_step_id(\n      old_time_step_id.time_runs_forward(), old_time_step_id.slab_number(),\n      old_time_step_id.step_time().with_slab(new_slab));\n  const auto new_time_step = old_time_step.with_slab(new_slab);\n\n  if (std::abs(new_time_step.value()) <\n      db::get<::Tags::MinimumTimeStep>(*box)) {\n    ERROR_NO_TRACE(\n        \"Chosen step size \"\n        << new_time_step.value() << \" is smaller than the MinimumTimeStep of \"\n        << db::get<::Tags::MinimumTimeStep>(*box)\n        << \" while changing the slab size to \" << new_slab.duration().value()\n        << \".\\n\"\n           \"\\n\"\n           \"This can indicate a flaw in the step chooser, the grid, or a \"\n           \"simulation instability that an error-based stepper is naively \"\n           \"attempting to resolve. A possible issue is an aliasing-driven \"\n           \"instability that could be cured by more aggressive filtering if \"\n           \"you are using DG.\");\n  }\n\n  const auto new_next_time_step_id =\n      db::get<Tags::TimeStepper<TimeStepper>>(*box).next_time_id(\n          new_time_step_id, new_time_step);\n\n  db::mutate_apply<\n      tmpl::push_front<Tags::get_all_history_tags<DbTags>,\n                       ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n                       ::Tags::TimeStepId, ::Tags::AdaptiveSteppingDiagnostics>,\n      tmpl::list<>>(\n      [&new_next_time_step_id, &new_time_step, &new_time_step_id](\n          const gsl::not_null<TimeStepId*> next_time_step_id,\n          const gsl::not_null<TimeDelta*> time_step,\n          const gsl::not_null<TimeStepId*> local_time_step_id,\n          const gsl::not_null<AdaptiveSteppingDiagnostics*> diags,\n          const auto... histories) {\n        const auto update_history = [&](const auto history) {\n          if (not history->empty() and\n              history->back().time_step_id == *local_time_step_id) {\n            ASSERT(history->at_step_start(),\n                   \"Cannot change step size with substep data.\");\n            history->back().time_step_id = new_time_step_id;\n          }\n          return 0;\n        };\n        expand_pack(update_history(histories)...);\n\n        *next_time_step_id = new_next_time_step_id;\n        *time_step = new_time_step;\n        *local_time_step_id = new_time_step_id;\n        ++diags->number_of_slab_size_changes;\n      },\n      box);\n}\n"
  },
  {
    "path": "src/Time/ChangeSlabSize/Event.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/ChangeSlabSize/Event.hpp\"\n\nnamespace Events {\nPUP::able::PUP_ID ChangeSlabSize::my_PUP_ID = 0;  // NOLINT\n}  // namespace Events\n"
  },
  {
    "path": "src/Time/ChangeSlabSize/Event.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <cstdint>\n#include <limits>\n#include <map>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Time/TimeStepRequestProcessor.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct DataBox;\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Events {\nnamespace ChangeSlabSize_detail {\nstruct StoreNewSlabSize {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const int64_t slab_number,\n                    const TimeStepRequestProcessor& requests) {\n    db::mutate<::Tags::ChangeSlabSize::NewSlabSize>(\n        [&](const gsl::not_null<\n            std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                sizes) { (*sizes)[slab_number].emplace_back(requests); },\n        make_not_null(&box));\n  }\n};\n}  // namespace ChangeSlabSize_detail\n\n/// \\ingroup TimeGroup\n/// %Trigger a slab size change.\n///\n/// The new size will be the minimum suggested by any of the provided\n/// step choosers on any element.  This requires a global reduction,\n/// so it is possible to delay the change until a later slab to avoid\n/// a global synchronization.  The actual change is carried out by\n/// Actions::ChangeSlabSize.\n///\n/// When running with global time-stepping, the slab size and step\n/// size are the same, so this adjusts the step size used by the time\n/// integration.  With local time-stepping this controls the interval\n/// between times when the sequences of steps on all elements are\n/// forced to align.\nclass ChangeSlabSize : public Event {\n  using ReductionData = Parallel::ReductionData<\n      Parallel::ReductionDatum<int64_t, funcl::AssertEqual<>>,\n      Parallel::ReductionDatum<TimeStepRequestProcessor, funcl::Plus<>>>;\n\n public:\n  /// \\cond\n  explicit ChangeSlabSize(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ChangeSlabSize);  // NOLINT\n  /// \\endcond\n\n  struct StepChoosers {\n    static constexpr Options::String help = \"Limits on slab size\";\n    using type =\n        std::vector<std::unique_ptr<StepChooser<StepChooserUse::Slab>>>;\n    static size_t lower_bound_on_size() { return 1; }\n  };\n\n  struct DelayChange {\n    static constexpr Options::String help = \"Slabs to wait before changing\";\n    using type = uint64_t;\n  };\n\n  using options = tmpl::list<StepChoosers, DelayChange>;\n  static constexpr Options::String help =\n      \"Trigger a slab size change chosen by the provided step choosers.\\n\"\n      \"The actual changing of the slab size can be delayed until a later\\n\"\n      \"slab to improve parallelization.\";\n\n  ChangeSlabSize() = default;\n  ChangeSlabSize(std::vector<std::unique_ptr<StepChooser<StepChooserUse::Slab>>>\n                     step_choosers,\n                 const uint64_t delay_change, const Options::Context& context)\n      : step_choosers_(std::move(step_choosers)), delay_change_(delay_change) {\n    if (delay_change != 0) {\n      for (const auto& chooser : step_choosers_) {\n        if (not chooser->can_be_delayed()) {\n          // The runtime name might not be exactly the same as the one\n          // used by the factory, but hopefully it's close enough that\n          // the user can figure it out.\n          PARSE_ERROR(context,\n                      \"The \" << pretty_type::get_runtime_type_name(*chooser)\n                      << \" StepChooser cannot be applied with a delay.\");\n        }\n      }\n    }\n  }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  // Need a const version of the full box for the step choosers, but\n  // can't get a const version while mutating other tags, so request a\n  // mutable version.\n  using return_tags = tmpl::list<::Tags::DataBox>;\n  using argument_tags = tmpl::list<::Tags::TimeStepId>;\n\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(const gsl::not_null<db::DataBox<DbTags>*> box,\n                  const TimeStepId& time_step_id,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& array_index,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    const auto next_changable_slab = time_step_id.is_at_slab_boundary()\n                                         ? time_step_id.slab_number()\n                                         : time_step_id.slab_number() + 1;\n    const auto slab_to_change =\n        next_changable_slab + static_cast<int64_t>(delay_change_);\n    const auto slab_start = time_step_id.step_time();\n    const auto current_slab_size = (time_step_id.time_runs_forward() ? 1 : -1) *\n                                   slab_start.slab().duration();\n\n    TimeStepRequestProcessor step_requests(time_step_id.time_runs_forward());\n    bool synchronization_required = false;\n    for (const auto& step_chooser : step_choosers_) {\n      step_requests.process(\n          step_chooser->desired_step(current_slab_size.value(), *box));\n      // We must synchronize if any step chooser requires it, not just\n      // the limiting one, because choosers requiring synchronization\n      // can be limiting on some processors and not others.\n      if (not synchronization_required) {\n        synchronization_required = step_chooser->uses_local_data();\n      }\n    }\n\n    db::mutate<::Tags::ChangeSlabSize::NumberOfExpectedMessages>(\n        [&](const gsl::not_null<std::map<int64_t, size_t>*> expected) {\n          ++(*expected)[slab_to_change];\n        },\n        box);\n\n    if constexpr (Parallel::is_dg_element_collection_v<ParallelComponent>) {\n      ERROR(\n          \"Slab changing is not yet implemented for the DgElementCollection \"\n          \"parallel component. Specifically, the ability to do reductions \"\n          \"using Charm++-style reductions has not been implemented.\");\n    } else {\n      const auto& component_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      const auto& self_proxy = component_proxy[array_index];\n      if (synchronization_required) {\n        Parallel::contribute_to_reduction<\n            ChangeSlabSize_detail::StoreNewSlabSize>(\n            ReductionData(slab_to_change, step_requests), self_proxy,\n            component_proxy);\n      } else {\n        db::mutate<::Tags::ChangeSlabSize::NewSlabSize>(\n            [&](const gsl::not_null<\n                std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                    sizes) {\n              (*sizes)[slab_to_change].push_back(step_requests);\n            },\n            box);\n      }\n    }\n    (void)cache, (void)array_index;\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override {\n    // This depends on the chosen StepChoosers, but they don't have a\n    // way to report this information so we just return true to be\n    // safe.\n    return true;\n  }\n\n  template <typename F>\n  void for_each_step_chooser(F&& f) const {\n    for (const auto& step_chooser : step_choosers_) {\n      f(*step_chooser);\n    }\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | step_choosers_;\n    p | delay_change_;\n  }\n\n private:\n  std::vector<std::unique_ptr<StepChooser<StepChooserUse::Slab>>>\n      step_choosers_;\n  uint64_t delay_change_ = std::numeric_limits<uint64_t>::max();\n};\n}  // namespace Events\n"
  },
  {
    "path": "src/Time/ChangeSlabSize/Tags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <map>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/TimeStepRequestProcessor.hpp\"\n\nnamespace Tags::ChangeSlabSize {\n/// Sizes requested for each slab by ChangeSlabSize events.\nstruct NewSlabSize : db::SimpleTag {\n  using type = std::map<int64_t, std::vector<TimeStepRequestProcessor>>;\n};\n\n/// Number of ChangeSlabSize events changing the size at each slab.\nstruct NumberOfExpectedMessages : db::SimpleTag {\n  using type = std::map<int64_t, size_t>;\n};\n\n/// Long-term desired slab size.  Used as the default size if nothing\n/// chooses a smaller one.\nstruct SlabSizeGoal : db::SimpleTag {\n  using type = double;\n};\n}  // namespace Tags::ChangeSlabSize\n"
  },
  {
    "path": "src/Time/ChangeStepSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <bit>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/ChooseLtsStepSize.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/LtsStepChoosers.hpp\"\n#include \"Time/Tags/MinimumTimeStep.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Time/TimeStepRequestProcessor.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nstruct AllStepChoosers;\nnamespace Tags {\nstruct AdaptiveSteppingDiagnostics;\nstruct DataBox;\nstruct FixedLtsRatio;\ntemplate <typename Tag>\nstruct Next;\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\n/// \\endcond\n\n/// \\brief Adjust the step size for local time stepping.\n///\n/// \\details\n/// Usually, the new step size is chosen by calling the StepChoosers from\n/// `Tags::LtsStepChoosers`, restricted based on the allowed step sizes at the\n/// current time, and limits from history initialization.\n///\n/// If `Tags::FixedLtsRatio` is present in the DataBox and not empty, the\n/// StepChoosers are not called and instead the desired step is taken to be the\n/// slab size over that value.  Early in the evolution, the actual chosen step\n/// may differ from this because of restrictions on the allowed step, but all\n/// such restrictions are global and will not result in different decisions for\n/// different elements with the same desired fixed ratio.\n///\n/// The optional template parameter `StepChoosersToUse` may be used to\n/// indicate a subset of the constructable step choosers to use for the current\n/// application of `ChangeStepSize`. Passing `AllStepChoosers` (default)\n/// indicates that any constructible step chooser may be used. This option is\n/// used when multiple components need to invoke `ChangeStepSize` with step\n/// choosers that may not be compatible with all components.\ntemplate <typename StepChoosersToUse = AllStepChoosers,\n          template <typename> typename CacheTagPrefix = std::type_identity_t>\nstruct ChangeStepSize {\n  using const_global_cache_tags =\n      tmpl::list<CacheTagPrefix<Tags::MinimumTimeStep>,\n                 CacheTagPrefix<Tags::LtsStepChoosers>>;\n\n  using return_tags = tmpl::list<Tags::DataBox>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename DbTags>\n  static void apply(const gsl::not_null<db::DataBox<DbTags>*> box) {\n    const auto& time_step_id = db::get<Tags::TimeStepId>(*box);\n    if (time_step_id.substep() != 0) {\n      return;\n    }\n\n    const LtsTimeStepper& time_stepper =\n        db::get<CacheTagPrefix<Tags::TimeStepper<LtsTimeStepper>>>(*box);\n    const auto& step_choosers =\n        db::get<CacheTagPrefix<Tags::LtsStepChoosers>>(*box);\n\n    using history_tags = ::Tags::get_all_history_tags<DbTags>;\n    bool can_change_step_size = true;\n    tmpl::for_each<history_tags>([&box, &can_change_step_size, &time_stepper,\n                                  &time_step_id](auto tag_v) {\n      if (not can_change_step_size) {\n        return;\n      }\n      using tag = typename decltype(tag_v)::type;\n      const auto& history = db::get<tag>(*box);\n      can_change_step_size =\n          time_stepper.can_change_step_size(time_step_id, history);\n    });\n\n    const auto current_step = db::get<Tags::TimeStep>(*box);\n\n    std::optional<size_t> fixed_lts_ratio{};\n    if constexpr (db::tag_is_retrievable_v<Tags::FixedLtsRatio,\n                                           db::DataBox<DbTags>>) {\n      fixed_lts_ratio = db::get<Tags::FixedLtsRatio>(*box);\n    }\n\n    TimeStepRequestProcessor step_requests(time_step_id.time_runs_forward());\n    if (fixed_lts_ratio.has_value()) {\n      ASSERT(std::popcount(*fixed_lts_ratio) == 1,\n             \"fixed_lts_ratio must be a power of 2, not \" << *fixed_lts_ratio);\n      step_requests.process(TimeStepRequest{\n          .size_goal =\n              (current_step.slab().duration() / *fixed_lts_ratio).value()});\n    } else {\n      const double last_step_size = current_step.value();\n      for (const auto& step_chooser : step_choosers) {\n        const auto step_request =\n            step_chooser->template desired_step<StepChoosersToUse>(\n                last_step_size, *box);\n        step_requests.process(step_request);\n      }\n    }\n\n    if (not can_change_step_size) {\n      step_requests.error_on_hard_limit(\n          current_step.value(),\n          (time_step_id.step_time() + current_step).value());\n      return;\n    }\n\n    const double desired_step = step_requests.step_size(\n        time_step_id.step_time().value(), current_step.value());\n\n    const auto& minimum_time_step =\n        db::get<CacheTagPrefix<::Tags::MinimumTimeStep>>(*box);\n\n    // We do this check twice, first on the desired value, and then on\n    // the actual chosen value, which is probably slightly smaller.\n    if (std::abs(desired_step) < minimum_time_step) {\n      ERROR_NO_TRACE(\n          \"Chosen step size \"\n          << desired_step << \" is smaller than the MinimumTimeStep of \"\n          << minimum_time_step\n          << \".\\n\"\n             \"\\n\"\n             \"This can indicate a flaw in the step chooser, the grid, or a \"\n             \"simulation instability that an error-based stepper is naively \"\n             \"attempting to resolve. A possible issue is an aliasing-driven \"\n             \"instability that could be cured by more aggressive filtering if \"\n             \"you are using DG.\");\n    }\n\n    const auto new_step =\n        choose_lts_step_size(time_step_id.step_time(), desired_step);\n    step_requests.error_on_hard_limit(\n        new_step.value(), (time_step_id.step_time() + new_step).value());\n\n    if (new_step == current_step) {\n      return;\n    }\n\n    if (std::abs(new_step.value()) < minimum_time_step) {\n      ERROR_NO_TRACE(\n          \"Chosen step size after conversion to a fraction of a slab \"\n          << new_step << \" is smaller than the MinimumTimeStep of \"\n          << minimum_time_step\n          << \".\\n\"\n             \"\\n\"\n             \"This can indicate a flaw in the step chooser, the grid, or a \"\n             \"simulation instability that an error-based stepper is naively \"\n             \"attempting to resolve. A possible issue is an aliasing-driven \"\n             \"instability that could be cured by more aggressive filtering if \"\n             \"you are using DG.\");\n    }\n\n    db::mutate<Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n               Tags::AdaptiveSteppingDiagnostics>(\n        [&](const gsl::not_null<TimeStepId*> local_next_time_id,\n            const gsl::not_null<TimeDelta*> time_step,\n            const gsl::not_null<AdaptiveSteppingDiagnostics*> diags) {\n          *time_step = new_step;\n          *local_next_time_id =\n              time_stepper.next_time_id(time_step_id, *time_step);\n          ++diags->number_of_step_fraction_changes;\n        },\n        box);\n  }\n};\n"
  },
  {
    "path": "src/Time/ChangeTimeStepperOrder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <optional>\n#include <type_traits>\n\n#include \"Time/Tags/VariableOrderAlgorithm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nstruct StepperErrorEstimate;\nclass TimeStepId;\nclass TimeStepper;\nclass VariableOrderAlgorithm;\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\ntemplate <typename Tag>\nstruct Next;\ntemplate <typename Tag>\nstruct StepperErrors;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace TimeSteppers {\ntemplate <typename Vars>\nclass History;\n}  // namespace TimeSteppers\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/*!\n * \\ingroup TimeGroup\n * \\brief Adjust the step order for local time-stepping\n *\n * \\details See VariableOrderAlgorithm for descriptions of the\n * algorithms.  This mutator wraps that class, checking that the order\n * is not changed out of the valid range for the time stepper.\n */\n/// @{\ntemplate <typename System,\n          template <typename> typename CacheTagPrefix = std::type_identity_t,\n          typename = tmpl::conditional_t<\n              tt::is_a_v<tmpl::list, typename System::variables_tag>,\n              typename System::variables_tag,\n              tmpl::list<typename System::variables_tag>>>\nstruct ChangeTimeStepperOrder;\n\ntemplate <typename System, template <typename> typename CacheTagPrefix,\n          typename... VariablesTags>\nstruct ChangeTimeStepperOrder<System, CacheTagPrefix,\n                              tmpl::list<VariablesTags...>> {\n  using const_global_cache_tags =\n      tmpl::list<CacheTagPrefix<Tags::VariableOrderAlgorithm>>;\n  using return_tags =\n      tmpl::list<Tags::HistoryEvolvedVariables<VariablesTags>...>;\n  using argument_tags =\n      tmpl::list<CacheTagPrefix<Tags::TimeStepper<TimeStepper>>,\n                 CacheTagPrefix<Tags::VariableOrderAlgorithm>,\n                 Tags::Next<Tags::TimeStepId>,\n                 Tags::StepperErrors<VariablesTags>...>;\n\n  static void apply(\n      const gsl::not_null<\n          TimeSteppers::History<typename VariablesTags::type>*>... histories,\n      const TimeStepper& time_stepper,\n      const VariableOrderAlgorithm& order_algorithm,\n      const TimeStepId& next_time_step_id,\n      const typename tmpl::has_type<\n          VariablesTags,\n          std::array<std::optional<StepperErrorEstimate>, 2>>::type&... errors);\n};\n/// @}\n"
  },
  {
    "path": "src/Time/ChangeTimeStepperOrder.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/VariableOrderAlgorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <typename System, template <typename> typename CacheTagPrefix,\n          typename... VariablesTags>\nvoid ChangeTimeStepperOrder<System, CacheTagPrefix,\n                            tmpl::list<VariablesTags...>>::\n    apply(\n        const gsl::not_null<\n            TimeSteppers::History<typename VariablesTags::type>*>... histories,\n        const TimeStepper& time_stepper,\n        const VariableOrderAlgorithm& order_algorithm,\n        const TimeStepId& next_time_step_id,\n        const typename tmpl::has_type<\n            VariablesTags, std::array<std::optional<StepperErrorEstimate>,\n                                      2>>::type&... errors) {\n  if (next_time_step_id.substep() != 0) {\n    return;\n  }\n\n  const auto order_variant = time_stepper.order();\n  const auto* stepper_order =\n      variants::get_if<TimeSteppers::Tags::VariableOrder>(&order_variant);\n  if (stepper_order == nullptr) {\n    // Running at fixed order.\n    return;\n  }\n\n  const size_t new_order =\n      std::clamp(order_algorithm.template choose_order<VariablesTags...>(\n                     *histories..., errors...),\n                 stepper_order->minimum, stepper_order->maximum);\n\n  expand_pack((histories->integration_order(new_order), 0)...);\n}\n"
  },
  {
    "path": "src/Time/ChooseLtsStepSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/ChooseLtsStepSize.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n\n#include \"Time/Time.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nTimeDelta choose_lts_step_size(const Time& time, const double desired_step) {\n  ASSERT((time.fraction().denominator() &\n          (time.fraction().denominator() - 1)) == 0,\n         \"Not at a binary-fraction time within slab: \" << time.fraction());\n  const TimeDelta full_slab =\n      desired_step > 0.0 ? time.slab().duration() : -time.slab().duration();\n  const double desired_step_count = full_slab.value() / desired_step;\n  // log2 is a slowly increasing function, so log2(2^n + eps) may give\n  // n because of floating-point truncation.  The inner ceil call\n  // avoids this problem.\n  const auto desired_step_power =\n      desired_step_count == 0.0\n          ? 0\n          : static_cast<size_t>(\n                std::ceil(std::log2(std::ceil(desired_step_count))));\n\n  const auto max_denominator =\n      std::numeric_limits<decltype(time.fraction().denominator())>::max() / 2 +\n      1;\n  ASSERT((max_denominator & (max_denominator - 1)) == 0,\n         \"Calculated max denominator not a power of 2.\");\n  if (two_to_the(desired_step_power) > static_cast<size_t>(max_denominator)) {\n    ERROR_NO_TRACE(\n        \"Chosen step \"\n        << desired_step\n        << \" cannot be represented as a fraction of a slab of size \"\n        << time.slab().duration().value()\n        << \" without integer overflow.  The smallest representable step is \"\n        << time.slab().duration().value() / max_denominator << \".\");\n  }\n\n  // Ensure we will hit the slab boundary if we continue taking\n  // constant-sized steps.\n  const auto step_count =\n      std::max(static_cast<decltype(time.fraction().denominator())>(\n                   two_to_the(desired_step_power)),\n               time.fraction().denominator());\n\n  return full_slab / step_count;\n}\n"
  },
  {
    "path": "src/Time/ChooseLtsStepSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\cond\nclass Time;\nclass TimeDelta;\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// Convert an arbitrary desired step to a valid LTS step size.\nTimeDelta choose_lts_step_size(const Time& time, const double desired_step);\n"
  },
  {
    "path": "src/Time/CleanHistory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nclass TimeStepper;\nnamespace Tags {\ntemplate <typename StepperInterface>\nstruct TimeStepper;\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\n}  // namespace Tags\nnamespace TimeSteppers {\ntemplate <typename Vars>\nclass History;\n}  // namespace TimeSteppers\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// \\brief Clean time stepper history after a substep\n/// @{\ntemplate <typename System,\n          template <typename> typename CacheTagPrefix = std::type_identity_t,\n          typename = tmpl::conditional_t<\n              tt::is_a_v<tmpl::list, typename System::variables_tag>,\n              typename System::variables_tag,\n              tmpl::list<typename System::variables_tag>>>\nstruct CleanHistory;\n\ntemplate <typename System, template <typename> typename CacheTagPrefix,\n          typename... VariablesTags>\nstruct CleanHistory<System, CacheTagPrefix, tmpl::list<VariablesTags...>> {\n  using return_tags =\n      tmpl::list<Tags::HistoryEvolvedVariables<VariablesTags>...>;\n  using argument_tags =\n      tmpl::list<CacheTagPrefix<Tags::TimeStepper<TimeStepper>>>;\n\n  static void apply(\n      const gsl::not_null<\n          TimeSteppers::History<typename VariablesTags::type>*>... histories,\n      const TimeStepper& time_stepper);\n};\n/// @}\n"
  },
  {
    "path": "src/Time/CleanHistory.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Time/CleanHistory.hpp\"\n\n#include \"Time/History.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <typename System, template <typename> typename CacheTagPrefix,\n          typename... VariablesTags>\nvoid CleanHistory<System, CacheTagPrefix, tmpl::list<VariablesTags...>>::apply(\n    const gsl::not_null<\n        TimeSteppers::History<typename VariablesTags::type>*>... histories,\n    const TimeStepper& time_stepper) {\n  expand_pack((time_stepper.clean_history(histories), 0)...);\n}\n"
  },
  {
    "path": "src/Time/EvolutionOrdering.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <functional>\n#include <limits>\n#include <pup.h>\n#include <type_traits>\n#include <utility>\n\n/// \\ingroup TimeGroup\n/// Implementation of \\ref evolution_less, \\ref evolution_greater,\n/// \\ref evolution_less_equal, and \\ref evolution_greater_equal.\n///\n/// This should only be used through the named aliases, but provides\n/// the documentation of the members.\n/// @{\ntemplate <typename T, template <typename> typename Comparator>\nstruct evolution_comparator {\n  bool time_runs_forward = true;\n  constexpr bool operator()(const T& x, const T& y) const {\n    return time_runs_forward ? Comparator<T>{}(x, y) : Comparator<T>{}(y, x);\n  }\n\n  /// Provides an infinite (in the sense of\n  /// std::numeric_limits::infinity()) value that compares greater\n  /// than any other value in the evolution ordering.\n  template <typename U = T>\n  constexpr U infinity() const {\n    static_assert(std::numeric_limits<U>::has_infinity);\n    return time_runs_forward ? std::numeric_limits<U>::infinity()\n                             : -std::numeric_limits<U>::infinity();\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | time_runs_forward; }\n};\n\ntemplate <template <typename> typename Comparator>\nstruct evolution_comparator<void, Comparator> {\n  bool time_runs_forward = true;\n\n  template <typename T, typename U>\n  constexpr decltype(auto) operator()(T&& t, U&& u) const {\n    static_assert(std::is_same_v<decltype(Comparator<void>{}(\n                                     std::forward<T>(t), std::forward<U>(u))),\n                                 decltype(Comparator<void>{}(\n                                     std::forward<U>(u), std::forward<T>(t)))>,\n                  \"The return types of operators used in evolution comparators \"\n                  \"must be symmetric in their arguments.\");\n    return time_runs_forward\n               ? Comparator<void>{}(std::forward<T>(t), std::forward<U>(u))\n               : Comparator<void>{}(std::forward<U>(u), std::forward<T>(t));\n  }\n\n  /// \\copydoc evolution_comparator::infinity\n  template <typename U>\n  constexpr U infinity() const {\n    static_assert(std::numeric_limits<U>::has_infinity);\n    return time_runs_forward ? std::numeric_limits<U>::infinity()\n                             : -std::numeric_limits<U>::infinity();\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | time_runs_forward; }\n};\n/// @}\n\n/// \\ingroup TimeGroup\n/// Ordering functors that reverse their order when time runs\n/// backwards.  See evolution_comparator and\n/// evolution_comparator<void,Comparator> for the provided interface.\n///\n/// \\see std::less\ntemplate <typename T = void>\nusing evolution_less = evolution_comparator<T, std::less>;\n\n/// \\ingroup TimeGroup\n/// \\copydoc evolution_less\ntemplate <typename T = void>\nusing evolution_greater = evolution_comparator<T, std::greater>;\n\n/// \\ingroup TimeGroup\n/// \\copydoc evolution_less\ntemplate <typename T = void>\nusing evolution_less_equal = evolution_comparator<T, std::less_equal>;\n\n/// \\ingroup TimeGroup\n/// \\copydoc evolution_less\ntemplate <typename T = void>\nusing evolution_greater_equal = evolution_comparator<T, std::greater_equal>;\n"
  },
  {
    "path": "src/Time/History.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/History.hpp\"\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace TimeSteppers {\ntemplate <typename T>\nbool operator==(const UntypedStepRecord<T>& a, const UntypedStepRecord<T>& b) {\n  return a.time_step_id == b.time_step_id and a.value == b.value and\n         a.derivative == b.derivative;\n}\n\ntemplate <typename T>\nbool operator!=(const UntypedStepRecord<T>& a, const UntypedStepRecord<T>& b) {\n  return not(a == b);\n}\n\ntemplate <typename T>\nsize_t ConstUntypedHistory<T>::UntypedSubsteps::size() const {\n  return history_->substep_values().size();\n}\n\ntemplate <typename T>\nsize_t ConstUntypedHistory<T>::UntypedSubsteps::max_size() const {\n  return history_->substep_values().max_size();\n}\n\ntemplate <typename T>\nauto ConstUntypedHistory<T>::UntypedSubsteps::operator[](\n    const size_t index) const -> const UntypedStepRecord<T>& {\n  ASSERT(index < size(),\n         \"Requested substep \" << index << \" but only have \" << size());\n  return history_->substep_values()[index];\n}\n\ntemplate <typename T>\nConstUntypedHistory<T>::UntypedSubsteps::UntypedSubsteps(\n    const ConstUntypedHistory& history)\n    : history_(&history) {}\n\ntemplate <typename T>\nauto ConstUntypedHistory<T>::substeps() const -> UntypedSubsteps {\n  return UntypedSubsteps(*this);\n}\n\nnamespace History_detail {\ntemplate <typename UntypedBase>\nsize_t UntypedAccessCommon<UntypedBase>::integration_order() const {\n  return integration_order_;\n}\n\ntemplate <typename UntypedBase>\nsize_t UntypedAccessCommon<UntypedBase>::size() const {\n  return step_values_.size();\n}\n\ntemplate <typename UntypedBase>\nsize_t UntypedAccessCommon<UntypedBase>::max_size() const {\n  return history_max_past_steps + 2;\n}\n\ntemplate <typename UntypedBase>\nauto UntypedAccessCommon<UntypedBase>::operator[](const size_t index) const\n    -> const UntypedStepRecord<WrapperType>& {\n  ASSERT(index < size(),\n         \"Requested step \" << index << \" but only have \" << size());\n  return step_values_[index];\n}\n\ntemplate <typename UntypedBase>\nauto UntypedAccessCommon<UntypedBase>::operator[](const TimeStepId& id) const\n    -> const UntypedStepRecord<WrapperType>& {\n  return find_record(*this, id);\n}\n\ntemplate <typename UntypedBase>\nbool UntypedAccessCommon<UntypedBase>::at_step_start() const {\n  return substep_values_.empty() or\n         substep_values_.back().time_step_id < this->back().time_step_id;\n}\n\ntemplate <typename UntypedBase>\nauto UntypedAccessCommon<UntypedBase>::substep_values() const\n    -> const boost::container::static_vector<UntypedStepRecord<WrapperType>,\n                                             history_max_substeps>& {\n  return substep_values_;\n}\n}  // namespace History_detail\n\n#define MATH_WRAPPER_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                   \\\n  template bool operator==(                                    \\\n      const UntypedStepRecord<MATH_WRAPPER_TYPE(data)>& a,     \\\n      const UntypedStepRecord<MATH_WRAPPER_TYPE(data)>& b);    \\\n  template bool operator!=(                                    \\\n      const UntypedStepRecord<MATH_WRAPPER_TYPE(data)>& a,     \\\n      const UntypedStepRecord<MATH_WRAPPER_TYPE(data)>& b);    \\\n  template class ConstUntypedHistory<MATH_WRAPPER_TYPE(data)>; \\\n  template class History_detail::UntypedAccessCommon<          \\\n      ConstUntypedHistory<MATH_WRAPPER_TYPE(data)>>;           \\\n  template class History_detail::UntypedAccessCommon<          \\\n      MutableUntypedHistory<MATH_WRAPPER_TYPE(data)>>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (MATH_WRAPPER_TYPES))\n\n#undef INSTANTIATE\n#undef MATH_WRAPPER_TYPE\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/History.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/container/static_vector.hpp>\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"DataStructures/StaticDeque.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ContainsAllocations.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupBoost.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/StlBoilerplate.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n\n/// Largest number of past steps supported by the time stepper\n/// `History`.  Corresponds to the `number_of_past_steps()` method of\n/// `TimeStepper`.  AdamsBashforth with order 8 has the largest\n/// requirement.\nconstexpr size_t history_max_past_steps = 7;\n/// Largest number of substeps supported by the time stepper\n/// `History`.  Corresponds to the `number_of_substeps()` and\n/// `number_of_substeps_for_error()` methods of `TimeStepper`.\n/// DormandPrince5 with an error estimate and Rk5Owren have the\n/// largest requirement.\nconstexpr size_t history_max_substeps = 6;\n\n/// \\ingroup TimeSteppersGroup\n/// Entry in the time-stepper history, in type-erased form.\n///\n/// The history access classes do not provide mutable references to\n/// these structs, so they cannot be used to modify history data.\n///\n/// This struct mirrors the typed `StepRecord` struct.  See that for\n/// details.\n///\n/// \\tparam T One of the types in \\ref MATH_WRAPPER_TYPES\ntemplate <typename T>\nstruct UntypedStepRecord {\n  TimeStepId time_step_id;\n  std::optional<T> value;\n  T derivative;\n\n  static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>, T>);\n};\n\ntemplate <typename T>\nbool operator==(const UntypedStepRecord<T>& a, const UntypedStepRecord<T>& b);\n\ntemplate <typename T>\nbool operator!=(const UntypedStepRecord<T>& a, const UntypedStepRecord<T>& b);\n\n#if defined(__GNUC__) and not defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\n#endif  // defined(__GNUC__) and not defined(__clang__)\n/// \\ingroup TimeSteppersGroup\n/// Access to the history data used by a TimeStepper in type-erased\n/// form.  Obtain an instance with `History::untyped()`.\n///\n/// The methods mirror similar ones in `History`.  See that class for\n/// details.\n///\n/// \\tparam T One of the types in \\ref MATH_WRAPPER_TYPES\ntemplate <typename T>\nclass ConstUntypedHistory\n    : public stl_boilerplate::RandomAccessSequence<\n          ConstUntypedHistory<T>, const UntypedStepRecord<T>, false> {\n  static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>, T>);\n\n protected:\n  ConstUntypedHistory() = default;\n  ConstUntypedHistory(const ConstUntypedHistory&) = default;\n  ConstUntypedHistory(ConstUntypedHistory&&) = default;\n  ConstUntypedHistory& operator=(const ConstUntypedHistory&) = default;\n  ConstUntypedHistory& operator=(ConstUntypedHistory&&) = default;\n  ~ConstUntypedHistory() = default;\n\n public:\n  using WrapperType = T;\n\n  /// \\cond\n  class UntypedSubsteps\n      : public stl_boilerplate::RandomAccessSequence<\n            UntypedSubsteps, const UntypedStepRecord<T>, true> {\n   public:\n    UntypedSubsteps() = delete;\n    UntypedSubsteps(const UntypedSubsteps&) = delete;\n    UntypedSubsteps(UntypedSubsteps&&) = default;\n    UntypedSubsteps& operator=(const UntypedSubsteps&) = delete;\n    UntypedSubsteps& operator=(UntypedSubsteps&&) = default;\n    ~UntypedSubsteps() = default;\n\n    using WrapperType = T;\n\n    size_t size() const;\n    size_t max_size() const;\n\n    const UntypedStepRecord<T>& operator[](const size_t index) const;\n\n   private:\n    friend ConstUntypedHistory;\n    explicit UntypedSubsteps(const ConstUntypedHistory& history);\n\n    gsl::not_null<const ConstUntypedHistory*> history_;\n  };\n  /// \\endcond\n\n  virtual size_t integration_order() const = 0;\n\n  virtual size_t size() const = 0;\n  virtual size_t max_size() const = 0;\n\n  virtual const UntypedStepRecord<T>& operator[](size_t index) const = 0;\n\n  virtual const UntypedStepRecord<T>& operator[](\n      const TimeStepId& id) const = 0;\n\n  virtual bool at_step_start() const = 0;\n\n  UntypedSubsteps substeps() const;\n\n private:\n  friend UntypedSubsteps;\n  virtual const boost::container::static_vector<UntypedStepRecord<WrapperType>,\n                                                history_max_substeps>&\n  substep_values() const = 0;\n};\n\n/// \\ingroup TimeSteppersGroup\n/// Mutable access to the history data used by a TimeStepper in\n/// type-erased form.  Obtain an instance with `History::untyped()`.\n///\n/// Data cannot be inserted or modified through the type-erased\n/// interface.  The only mutability exposed is the ability to delete\n/// data.\n///\n/// The methods mirror similar ones in `History`.  See that class for\n/// details.\n///\n/// \\tparam T One of the types in \\ref MATH_WRAPPER_TYPES\ntemplate <typename T>\nclass MutableUntypedHistory : public ConstUntypedHistory<T> {\n  static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>, T>);\n\n protected:\n  MutableUntypedHistory() = default;\n  MutableUntypedHistory(const MutableUntypedHistory&) = default;\n  MutableUntypedHistory(MutableUntypedHistory&&) = default;\n  MutableUntypedHistory& operator=(const MutableUntypedHistory&) = default;\n  MutableUntypedHistory& operator=(MutableUntypedHistory&&) = default;\n  ~MutableUntypedHistory() = default;\n\n public:\n  virtual void discard_value(const TimeStepId& id_to_discard) const = 0;\n\n  virtual void pop_front() const = 0;\n\n  virtual void clear_substeps() const = 0;\n};\n#if defined(__GNUC__) and not defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) and not defined(__clang__)\n\n/// \\ingroup TimeSteppersGroup\n/// Data in an entry of the time-stepper history.\n///\n/// The `value` field may be empty if the value has been discarded to\n/// save memory.  See `History::discard_value` and\n/// `ConstUntypedHistory::discard_value`.\ntemplate <typename Vars>\nstruct StepRecord {\n  using DerivVars = db::prefix_variables<::Tags::dt, Vars>;\n\n  TimeStepId time_step_id;\n  std::optional<Vars> value;\n  DerivVars derivative;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  std::ostream& print(std::ostream& os) const;\n};\n\ntemplate <typename Vars>\nvoid StepRecord<Vars>::pup(PUP::er& p) {\n  p | time_step_id;\n  p | value;\n  p | derivative;\n}\n\ntemplate <typename Vars>\nstd::ostream& StepRecord<Vars>::print(std::ostream& os) const {\n  using ::operator<<;\n  os << \"TimeStepId: \" << time_step_id << \"\\n\";\n  os << \"Value: \" << value << \"\\n\";\n  os << \"Derivative: \" << derivative << \"\\n\";\n  return os;\n}\n\ntemplate <typename Vars>\nbool operator==(const StepRecord<Vars>& a, const StepRecord<Vars>& b) {\n  return a.time_step_id == b.time_step_id and a.value == b.value and\n         a.derivative == b.derivative;\n}\n\ntemplate <typename Vars>\nbool operator!=(const StepRecord<Vars>& a, const StepRecord<Vars>& b) {\n  return not(a == b);\n}\n\n/// \\cond\ntemplate <typename Vars>\nclass History;\n/// \\endcond\n\nnamespace History_detail {\n// Find a record from either the steps or substeps.  This takes the\n// arguments it does so it can be used to find both const and\n// non-const values from both typed and untyped histories.\ntemplate <typename History>\ndecltype(auto) find_record(History&& history, const TimeStepId& id) {\n  const size_t substep = id.substep();\n  if (substep == 0) {\n    for (size_t i = 0; i < history.size(); ++i) {\n      auto& record = history[i];\n      if (record.time_step_id == id) {\n        return record;\n      }\n    }\n    ERROR(id << \" not present\");\n  } else {\n    ASSERT(substep - 1 < history.substeps().size(), id << \" not present\");\n    auto& record = history.substeps()[substep - 1];\n    ASSERT(record.time_step_id == id, id << \" not present\");\n    return record;\n  }\n}\n\n#if defined(__GNUC__) and not defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\n#endif  // defined(__GNUC__) and not defined(__clang__)\ntemplate <typename UntypedBase>\nclass UntypedAccessCommon : public UntypedBase {\n protected:\n  UntypedAccessCommon() = delete;\n  UntypedAccessCommon(const UntypedAccessCommon&) = delete;\n  UntypedAccessCommon(UntypedAccessCommon&&) = default;\n  UntypedAccessCommon& operator=(const UntypedAccessCommon&) = delete;\n  // Can't move-assign non-owning DataVectors.\n  UntypedAccessCommon& operator=(UntypedAccessCommon&&) = delete;\n  ~UntypedAccessCommon() = default;\n\n public:\n  using WrapperType = typename UntypedBase::WrapperType;\n\n  size_t integration_order() const override;\n\n  size_t size() const override;\n  size_t max_size() const override;\n\n  const UntypedStepRecord<WrapperType>& operator[](\n      const size_t index) const override;\n  const UntypedStepRecord<WrapperType>& operator[](\n      const TimeStepId& id) const override;\n\n  bool at_step_start() const override;\n\n protected:\n  template <typename Vars>\n  UntypedAccessCommon(const History<Vars>& history) {\n    reinitialize(history);\n  }\n\n  template <typename Vars>\n  void reinitialize(const History<Vars>& history) const {\n    // This class is presenting a view.  Regenerating its internal\n    // representation after a change to the parent structure is an\n    // implementation detail.\n    auto* const mutable_this = const_cast<UntypedAccessCommon*>(this);\n    mutable_this->integration_order_ = history.integration_order();\n    mutable_this->step_values_.clear();\n    for (const StepRecord<Vars>& record : history) {\n      mutable_this->step_values_.push_back(make_untyped(record));\n    }\n    mutable_this->substep_values_.clear();\n    for (const StepRecord<Vars>& record : history.substeps()) {\n      mutable_this->substep_values_.push_back(make_untyped(record));\n    }\n  }\n\n private:\n  const boost::container::static_vector<UntypedStepRecord<WrapperType>,\n                                        history_max_substeps>&\n  substep_values() const override;\n\n  template <typename Vars>\n  static UntypedStepRecord<WrapperType> make_untyped(\n      const StepRecord<Vars>& record) {\n    // This class only exposes these records as const references, so\n    // it is OK if we break non-allocating references to the original\n    // data by moving out of the MathWrappers since you can't modify\n    // through them.\n    return {record.time_step_id,\n            record.value.has_value() ? std::optional{const_cast<WrapperType&&>(\n                                           *make_math_wrapper(*record.value))}\n                                     : std::nullopt,\n            const_cast<WrapperType&&>(*make_math_wrapper(record.derivative))};\n  }\n\n  size_t integration_order_{};\n  // static_vector never reallocates, so storing non-owning\n  // DataVectors is safe.\n  boost::container::static_vector<UntypedStepRecord<WrapperType>,\n                                  history_max_past_steps + 2>\n      step_values_{};\n  boost::container::static_vector<UntypedStepRecord<WrapperType>,\n                                  history_max_substeps>\n      substep_values_{};\n};\n#if defined(__GNUC__) and not defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) and not defined(__clang__)\n}  // namespace History_detail\n\n/// \\ingroup TimeSteppersGroup\n/// The past-time data used by TimeStepper classes to update the\n/// evolved variables.\n///\n/// This class exposes an STL-like container interface for accessing\n/// the step (not substep) `StepRecord` data.  The records can be\n/// freely modified through this interface, although modifying the\n/// `time_step_id` field is generally inadvisable.\n///\n/// This class is designed to minimize the number of memory\n/// allocations performed during a step, and so caches discarded\n/// entries for reuse if they contain dynamic allocations.  During\n/// steady-state operation, this class will perform no heap\n/// allocations.  If `Vars` and the associated `DerivVars` do not\n/// allocate internally, then this class will perform no heap\n/// allocations under any circumstances.\ntemplate <typename Vars>\nclass History\n    : public stl_boilerplate::RandomAccessSequence<History<Vars>,\n                                                   StepRecord<Vars>, false> {\n public:\n  using DerivVars = db::prefix_variables<::Tags::dt, Vars>;\n\n  History() = default;\n  explicit History(const size_t integration_order)\n      : integration_order_(integration_order) {}\n  History(const History& other);\n  History(History&&) = default;\n  History& operator=(const History& other);\n  History& operator=(History&&) = default;\n\n  /// The wrapped type presented by the type-erased history.  One of\n  /// the types in \\ref MATH_WRAPPER_TYPES.\n  using UntypedVars = math_wrapper_type<Vars>;\n\n  /// \\cond\n  // This wrapper around UntypedAccessCommon exists because we want\n  // the special members of that to be protected, and we want this to\n  // be final.\n  class ConstUntypedAccess final : public History_detail::UntypedAccessCommon<\n                                       ConstUntypedHistory<UntypedVars>> {\n   public:\n    ConstUntypedAccess() = delete;\n    ConstUntypedAccess(const ConstUntypedAccess&) = delete;\n    ConstUntypedAccess(ConstUntypedAccess&&) = default;\n    ConstUntypedAccess& operator=(const ConstUntypedAccess&) = delete;\n    ConstUntypedAccess& operator=(ConstUntypedAccess&&) = default;\n    ~ConstUntypedAccess() = default;\n\n   private:\n    friend History;\n\n    explicit ConstUntypedAccess(const History& history)\n        : History_detail::UntypedAccessCommon<ConstUntypedHistory<UntypedVars>>(\n              history) {}\n  };\n\n  class MutableUntypedAccess final : public History_detail::UntypedAccessCommon<\n                                         MutableUntypedHistory<UntypedVars>> {\n   public:\n    MutableUntypedAccess() = delete;\n    MutableUntypedAccess(const MutableUntypedAccess&) = delete;\n    MutableUntypedAccess(MutableUntypedAccess&&) = default;\n    MutableUntypedAccess& operator=(const MutableUntypedAccess&) = delete;\n    MutableUntypedAccess& operator=(MutableUntypedAccess&&) = default;\n    ~MutableUntypedAccess() = default;\n\n    void discard_value(const TimeStepId& id_to_discard) const override {\n      history_->discard_value(id_to_discard);\n      this->reinitialize(*history_);\n    }\n\n    void pop_front() const override {\n      history_->pop_front();\n      this->reinitialize(*history_);\n    }\n\n    void clear_substeps() const override {\n      history_->clear_substeps();\n      this->reinitialize(*history_);\n    }\n\n   private:\n    friend History;\n\n    explicit MutableUntypedAccess(const gsl::not_null<History*> history)\n        : History_detail::UntypedAccessCommon<\n              MutableUntypedHistory<UntypedVars>>(*history),\n          history_(history) {}\n\n   private:\n    gsl::not_null<History*> history_;\n  };\n\n  class ConstSubsteps : public stl_boilerplate::RandomAccessSequence<\n                            ConstSubsteps, const StepRecord<Vars>, true> {\n   public:\n    ConstSubsteps() = delete;\n    ConstSubsteps(const ConstSubsteps&) = delete;\n    ConstSubsteps(ConstSubsteps&&) = default;\n    ConstSubsteps& operator=(const ConstSubsteps&) = delete;\n    ConstSubsteps& operator=(ConstSubsteps&&) = default;\n    ~ConstSubsteps() = default;\n\n    size_t size() const { return history_->substep_values_.size(); }\n    static constexpr size_t max_size() { return history_max_substeps; }\n\n    const StepRecord<Vars>& operator[](const size_t index) const {\n      ASSERT(index < size(),\n             \"Requested substep \" << index << \" but only have \" << size());\n      return history_->substep_values_[index];\n    }\n\n   private:\n    friend History;\n    explicit ConstSubsteps(const History& history) : history_(&history) {}\n\n    gsl::not_null<const History*> history_;\n  };\n\n  class MutableSubsteps\n      : public stl_boilerplate::RandomAccessSequence<MutableSubsteps,\n                                                     StepRecord<Vars>, true> {\n   public:\n    MutableSubsteps() = delete;\n    MutableSubsteps(const MutableSubsteps&) = delete;\n    MutableSubsteps(MutableSubsteps&&) = default;\n    MutableSubsteps& operator=(const MutableSubsteps&) = delete;\n    MutableSubsteps& operator=(MutableSubsteps&&) = default;\n    ~MutableSubsteps() = default;\n\n    size_t size() const { return history_->substep_values_.size(); }\n    static constexpr size_t max_size() { return history_max_substeps; }\n\n    StepRecord<Vars>& operator[](const size_t index) const {\n      ASSERT(index < size(),\n             \"Requested substep \" << index << \" but only have \" << size());\n      return history_->substep_values_[index];\n    }\n\n   private:\n    friend History;\n    explicit MutableSubsteps(const gsl::not_null<History*> history)\n        : history_(history) {}\n\n    gsl::not_null<History*> history_;\n  };\n  /// \\endcond\n\n  /// Immutable, type-erased access to the history.  This method\n  /// returns a class derived from `ConstUntypedHistory<UntypedVars>`.\n  /// Any modifications to the History class invalidate the object\n  /// returned by this function.\n  ConstUntypedAccess untyped() const { return ConstUntypedAccess(*this); }\n\n  /// Mutable, type-erased access to the history.  This method returns\n  /// a class derived from `MutableUntypedHistory<UntypedVars>`.  Any\n  /// modifications to the History class invalidate the object\n  /// returned by this function, except for modifications performed\n  /// through the object itself.\n  MutableUntypedAccess untyped() { return MutableUntypedAccess(this); }\n\n  /// Get or set the order the time stepper is running at.  Many time\n  /// steppers expect this to have a particular value.  This has no\n  /// effect on the storage of past data.\n  /// @{\n  size_t integration_order() const { return integration_order_; }\n  void integration_order(const size_t new_integration_order) {\n    integration_order_ = new_integration_order;\n  }\n  /// @}\n\n  /// Type and value used to indicate that a record is to be created\n  /// without the `value` field set.\n  /// @{\n  struct NoValue {};\n  static constexpr NoValue no_value{};\n  /// @}\n\n  /// Insert a new entry into the history.  It will be inserted as a\n  /// step or substep as appropriate.\n  ///\n  /// The supplied `time_step_id` must be later than the current\n  /// latest entry, and if the substep of `time_step_id` is nonzero,\n  /// the id must be consistent with the existing data for the current\n  /// step.\n  ///\n  /// If the constant `History::no_value` is passed, the created\n  /// record will not have its `value` field set.  This is useful for\n  /// histories other than the history of the primary evolution\n  /// integration, such as the implicit portion of an IMEX evolution.\n  /// These other uses adjust the result of the main history, and so\n  /// do not require values (which are only used for the zeroth-order\n  /// terms).\n  ///\n  /// The `value` and `derivative` data will be copied into cached\n  /// allocations, if any are available.  That is, only a copy, not a\n  /// memory allocation, will be performed when possible.\n  /// @{\n  void insert(const TimeStepId& time_step_id, const Vars& value,\n              const DerivVars& derivative);\n  void insert(const TimeStepId& time_step_id, NoValue /*unused*/,\n              const DerivVars& derivative);\n  /// @}\n\n  /// Insert a new entry in the history by modifying the fields of the\n  /// record directly, instead of passing a value to copy.\n  ///\n  /// The (optional) \\p value_inserter must be a functor callable with\n  /// single argument of type `gsl::not_null<Vars*>` and the \\p\n  /// derivative_inserter must be callable with a single argument of\n  /// type `gsl::not_null<DerivVars*>`.  The passed objects will be\n  /// created from a cached memory allocation if one is available, and\n  /// will otherwise be default-constructed.\n  ///\n  /// All the restrictions on valid values \\p time_step_id for the\n  /// `insert` method apply here as well.\n  /// @{\n  template <typename ValueFunc, typename DerivativeFunc>\n  void insert_in_place(const TimeStepId& time_step_id,\n                       ValueFunc&& value_inserter,\n                       DerivativeFunc&& derivative_inserter);\n  template <typename DerivativeFunc>\n  void insert_in_place(const TimeStepId& time_step_id, NoValue /*unused*/,\n                       DerivativeFunc&& derivative_inserter);\n  /// @}\n\n  /// Insert data at the start of the history, similar to `push_front`\n  /// on some STL containers.  This can be useful when initializing a\n  /// multistep integrator.\n  ///\n  /// This should only be used for initialization, and cannot be used\n  /// for substep data.  The supplied `time_step_id` must be earlier\n  /// than the current first entry.\n  /// @{\n  void insert_initial(TimeStepId time_step_id, Vars value,\n                      DerivVars derivative);\n  void insert_initial(TimeStepId time_step_id, NoValue /*unused*/,\n                      DerivVars derivative);\n  /// @}\n\n  /// The number of stored step (not substep) entries.\n  size_t size() const { return step_values_.size(); }\n  /// The maximum number of step (not substep) entries that can be\n  /// stored in the history.  This number is not very large, but will\n  /// be sufficient for running a time stepper requiring\n  /// `history_max_past_steps` past step values.\n  static constexpr size_t max_size() {\n    // In addition to the past steps, we must store the start and end\n    // of the current step.\n    return history_max_past_steps + 2;\n  }\n\n  /// Access the `StepRecord` for a step (not substep).\n  /// @{\n  const StepRecord<Vars>& operator[](const size_t index) const {\n    return step_values_[index];\n  }\n  StepRecord<Vars>& operator[](const size_t index) {\n    return step_values_[index];\n  }\n  /// @}\n\n  /// Access the `StepRecord` for a step or substep with a given\n  /// TimeStepId.  It is an error if there is no entry with that id.\n  /// @{\n  const StepRecord<Vars>& operator[](const TimeStepId& id) const {\n    return History_detail::find_record(*this, id);\n  }\n  StepRecord<Vars>& operator[](const TimeStepId& id) {\n    return History_detail::find_record(*this, id);\n  }\n  /// @}\n\n  /// Get the value at the latest step or substep in the history, even\n  /// if the value in that record has been discarded.  This is not\n  /// available if `undo_latest` has been called since the last\n  /// insertion.\n  ///\n  /// This function exists to allow access to the previous value after\n  /// a potentially bad step has been taken without restricting how\n  /// the time steppers can manage their history.\n  const Vars& latest_value() const;\n\n  /// Get the record for the start of the step containing the passed\n  /// time.  If the time matches a step time exactly, the record for\n  /// that step is returned.\n  const StepRecord<Vars>& step_start(double time) const;\n\n  /// Check whether we are at the start of a step, i.e, the most\n  /// recent entry in the history is not a substep.\n  bool at_step_start() const;\n\n  /// Container view of the `StepRecord`s for the history substeps.\n  /// These methods return classes providing an STL-like container\n  /// interface to the substep data.  These containers are\n  /// zero-indexed, so `substeps()[0]` will have a substep value of 1.\n  ///\n  /// These containers do not have methods to modify their sizes.  Use\n  /// the methods on the `History` class for those operations.\n  /// @{\n  ConstSubsteps substeps() const { return ConstSubsteps(*this); }\n  MutableSubsteps substeps() { return MutableSubsteps(this); }\n  /// @}\n\n  /// Clear the `value` in the indicated record.  It is an error if\n  /// there is no record with the passed TimeStepId.  Any memory\n  /// allocations will be cached for future reuse.\n  void discard_value(const TimeStepId& id_to_discard);\n\n  /// Drop the oldest step (not substep) entry in the history.  Any\n  /// memory allocations will be cached for future reuse.\n  void pop_front();\n\n  /// Drop the newest step or substep entry in the history.  Any\n  /// memory allocations will be cached for future reuse.\n  void undo_latest();\n\n  /// Remove all substep entries from the history.  Any memory\n  /// allocations will be cached for future reuse.\n  void clear_substeps();\n\n  /// Remove all (step and substep) entries from the history.  Any\n  /// memory allocations will be cached for future reuse.\n  void clear();\n\n  /// Release any cached memory allocations.\n  void shrink_to_fit();\n\n  /// Apply \\p func to `make_not_null(&e)` for `e` every `derivative`\n  /// and valid `*value` in records held by the history.\n  template <typename F>\n  void map_entries(F&& func);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  std::ostream& print(std::ostream& os) const;\n\n private:\n  void discard_value(gsl::not_null<std::optional<Vars>*> value);\n  void cache_allocations(gsl::not_null<StepRecord<Vars>*> record);\n\n  template <typename ValueFunc, typename DerivativeFunc>\n  void insert_impl(const TimeStepId& time_step_id, ValueFunc&& value_inserter,\n                   DerivativeFunc&& derivative_inserter);\n\n  template <typename InsertedVars>\n  void insert_initial_impl(TimeStepId time_step_id, InsertedVars value,\n                           DerivVars derivative);\n\n  size_t integration_order_{0};\n\n  StaticDeque<StepRecord<Vars>, max_size()> step_values_{};\n  boost::container::static_vector<StepRecord<Vars>, history_max_substeps>\n      substep_values_{};\n\n  // If the algorithm undoes a substep, it may want to reset the\n  // variables to the previous value.  We don't want the time stepper\n  // to have to keep track of this when discarding values, so we hang\n  // onto the last value if it gets discarded.\n  std::optional<Vars> latest_value_if_discarded_{};\n\n  // Memory allocations available for reuse.\n  boost::container::static_vector<Vars, max_size() + history_max_substeps>\n      vars_allocation_cache_{};\n  boost::container::static_vector<DerivVars, max_size() + history_max_substeps>\n      deriv_vars_allocation_cache_{};\n};\n\n// Don't copy the allocation caches.\ntemplate <typename Vars>\nHistory<Vars>::History(const History& other)\n    : integration_order_(other.integration_order_),\n      step_values_(other.step_values_),\n      substep_values_(other.substep_values_),\n      latest_value_if_discarded_(other.latest_value_if_discarded_) {}\n\n// Don't copy the allocation caches.\ntemplate <typename Vars>\nHistory<Vars>& History<Vars>::operator=(const History& other) {\n  integration_order_ = other.integration_order_;\n  step_values_ = other.step_values_;\n  substep_values_ = other.substep_values_;\n  latest_value_if_discarded_ = other.latest_value_if_discarded_;\n  return *this;\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::insert(const TimeStepId& time_step_id, const Vars& value,\n                           const DerivVars& derivative) {\n  insert_impl(\n      time_step_id,\n      [&](const gsl::not_null<Vars*> record_value) { *record_value = value; },\n      [&](const gsl::not_null<DerivVars*> record_derivative) {\n        *record_derivative = derivative;\n      });\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::insert(const TimeStepId& time_step_id, NoValue /*unused*/,\n                           const DerivVars& derivative) {\n  insert_impl(time_step_id, NoValue{},\n              [&](const gsl::not_null<DerivVars*> record_derivative) {\n                *record_derivative = derivative;\n              });\n}\n\ntemplate <typename Vars>\ntemplate <typename ValueFunc, typename DerivativeFunc>\nvoid History<Vars>::insert_in_place(const TimeStepId& time_step_id,\n                                    ValueFunc&& value_inserter,\n                                    DerivativeFunc&& derivative_inserter) {\n  insert_impl(time_step_id, std::forward<ValueFunc>(value_inserter),\n              std::forward<DerivativeFunc>(derivative_inserter));\n}\n\ntemplate <typename Vars>\ntemplate <typename DerivativeFunc>\nvoid History<Vars>::insert_in_place(const TimeStepId& time_step_id,\n                                    NoValue /*unused*/,\n                                    DerivativeFunc&& derivative_inserter) {\n  insert_impl(time_step_id, NoValue{},\n              std::forward<DerivativeFunc>(derivative_inserter));\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::insert_initial(TimeStepId time_step_id, Vars value,\n                                   DerivVars derivative) {\n  insert_initial_impl(std::move(time_step_id), std::move(value),\n                      std::move(derivative));\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::insert_initial(TimeStepId time_step_id, NoValue /*unused*/,\n                                   DerivVars derivative) {\n  insert_initial_impl(std::move(time_step_id), NoValue{},\n                      std::move(derivative));\n}\n\ntemplate <typename Vars>\nconst Vars& History<Vars>::latest_value() const {\n  const auto& latest_record =\n      at_step_start() ? this->back() : substeps().back();\n  if (latest_record.value.has_value()) {\n    return *latest_record.value;\n  } else {\n    ASSERT(latest_value_if_discarded_.has_value(),\n           \"Latest value unavailable.  The latest insertion was undone.\");\n    return *latest_value_if_discarded_;\n  }\n}\n\ntemplate <typename Vars>\nconst StepRecord<Vars>& History<Vars>::step_start(const double time) const {\n  // Search starting at the end to handle self-start correctly (and\n  // because the result under usual use is one of the last two\n  // entries).\n  const auto first_step = this->begin();\n  auto step = this->end();\n  ASSERT(step != first_step, \"History is empty\");\n  --step;\n  const evolution_less<double> before{step->time_step_id.time_runs_forward()};\n  while (before(time, step->time_step_id.step_time().value())) {\n    ASSERT(step != first_step,\n           \"Start of step at time \" << time << \" is before start of history \"\n           << first_step->time_step_id);\n    --step;\n  }\n  return *step;\n}\n\ntemplate <typename Vars>\nbool History<Vars>::at_step_start() const {\n  ASSERT(not this->empty(), \"History is empty\");\n  return substep_values_.empty() or\n         substep_values_.back().time_step_id < this->back().time_step_id;\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::discard_value(const TimeStepId& id_to_discard) {\n  auto& latest_record = at_step_start() ? this->back() : substeps().back();\n  if (latest_record.time_step_id == id_to_discard) {\n    discard_value(&latest_value_if_discarded_);\n    latest_value_if_discarded_ = std::move(latest_record.value);\n    latest_record.value.reset();\n  } else {\n    discard_value(&History_detail::find_record(*this, id_to_discard).value);\n  }\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::pop_front() {\n  ASSERT(not this->empty(), \"History is empty\");\n  ASSERT(substeps().empty() or substeps().front().time_step_id.step_time() !=\n                                   this->front().time_step_id.step_time(),\n         \"Cannot remove a step with substeps.  Call clear_substeps() first.\");\n  cache_allocations(&step_values_.front());\n  step_values_.pop_front();\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::undo_latest() {\n  if (at_step_start()) {\n    cache_allocations(&step_values_.back());\n    step_values_.pop_back();\n  } else {\n    cache_allocations(&substep_values_.back());\n    substep_values_.pop_back();\n  }\n  discard_value(&latest_value_if_discarded_);\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::clear_substeps() {\n  for (auto& record : substep_values_) {\n    cache_allocations(&record);\n  }\n  substep_values_.clear();\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::clear() {\n  clear_substeps();\n  while (not this->empty()) {\n    pop_front();\n  }\n  discard_value(&latest_value_if_discarded_);\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::shrink_to_fit() {\n  vars_allocation_cache_.clear();\n  vars_allocation_cache_.shrink_to_fit();\n  deriv_vars_allocation_cache_.clear();\n  deriv_vars_allocation_cache_.shrink_to_fit();\n}\n\ntemplate <typename Vars>\ntemplate <typename F>\nvoid History<Vars>::map_entries(F&& func) {\n  for (auto& record : *this) {\n    func(make_not_null(&record.derivative));\n    if (record.value.has_value()) {\n      func(make_not_null(&*record.value));\n    }\n  }\n  for (auto& record : this->substeps()) {\n    func(make_not_null(&record.derivative));\n    if (record.value.has_value()) {\n      func(make_not_null(&*record.value));\n    }\n  }\n}\n\ntemplate <typename Vars>\nvoid History<Vars>::pup(PUP::er& p) {\n  p | integration_order_;\n  p | step_values_;\n  p | substep_values_;\n  p | latest_value_if_discarded_;\n\n  // Don't serialize the allocation cache.\n}\n\ntemplate <typename Vars>\nstd::ostream& History<Vars>::print(std::ostream& os) const {\n  using ::operator<<;\n  os << \"Integration order: \" << integration_order_ << \"\\n\";\n  os << \"Step values:\\n\";\n  for (auto& record : *this) {\n    record.print(os);\n  }\n  os << \"Substep values:\\n\";\n  for (auto& record : substep_values_) {\n    record.print(os);\n  }\n  os << \"Latest value if discarded: \" << latest_value_if_discarded_ << \"\\n\";\n  return os;\n}\n\n// Doxygen is confused by this function for some reason.\n/// \\cond\ntemplate <typename Vars>\nvoid History<Vars>::discard_value(\n    const gsl::not_null<std::optional<Vars>*> value) {\n  if (not value->has_value()) {\n    return;\n  }\n  // If caching doesn't save anything, don't allocate memory for the cache.\n  if (contains_allocations(**value)) {\n    vars_allocation_cache_.emplace_back(std::move(**value));\n  }\n  value->reset();\n}\n/// \\endcond\n\ntemplate <typename Vars>\nvoid History<Vars>::cache_allocations(\n    const gsl::not_null<StepRecord<Vars>*> record) {\n  discard_value(&record->value);\n  // If caching doesn't save anything, don't allocate memory for the cache.\n  if (contains_allocations(record->derivative)) {\n    deriv_vars_allocation_cache_.emplace_back(std::move(record->derivative));\n  }\n}\n\ntemplate <typename Vars>\ntemplate <typename ValueFunc, typename DerivativeFunc>\nvoid History<Vars>::insert_impl(const TimeStepId& time_step_id,\n                                ValueFunc&& value_inserter,\n                                DerivativeFunc&& derivative_inserter) {\n  ASSERT(this->empty() or time_step_id > this->back().time_step_id,\n         \"New entry at \" << time_step_id\n         << \" must be later than previous entry at \"\n         << this->back().time_step_id);\n  discard_value(&latest_value_if_discarded_);\n  StepRecord<Vars> record{};\n  record.time_step_id = time_step_id;\n  if constexpr (not std::is_same_v<ValueFunc, NoValue>) {\n    if (vars_allocation_cache_.empty()) {\n      record.value.emplace();\n    } else {\n      record.value.emplace(std::move(vars_allocation_cache_.back()));\n      vars_allocation_cache_.pop_back();\n    }\n    std::forward<ValueFunc>(value_inserter)(make_not_null(&*record.value));\n  }\n  if (not deriv_vars_allocation_cache_.empty()) {\n    record.derivative = std::move(deriv_vars_allocation_cache_.back());\n    deriv_vars_allocation_cache_.pop_back();\n  }\n  std::forward<DerivativeFunc>(derivative_inserter)(\n      make_not_null(&record.derivative));\n\n  const size_t substep = time_step_id.substep();\n  if (substep == 0) {\n    step_values_.push_back(std::move(record));\n  } else {\n    ASSERT(not this->empty(), \"Cannot insert substep into empty history.\");\n    ASSERT(time_step_id.step_time() == this->back().time_step_id.step_time(),\n           \"Cannot insert substep \" << time_step_id << \" of different step \"\n           << this->back().time_step_id);\n    ASSERT(substep == substeps().size() + 1,\n           \"Cannot insert substep \" << substep << \" following \"\n           << substeps().size());\n    ASSERT(substep_values_.size() < substep_values_.max_size(),\n           \"Cannot insert new substep because the History is full.\");\n    substep_values_.push_back(std::move(record));\n  }\n}\n\ntemplate <typename Vars>\ntemplate <typename InsertedVars>\nvoid History<Vars>::insert_initial_impl(TimeStepId time_step_id,\n                                        InsertedVars value,\n                                        DerivVars derivative) {\n  ASSERT(vars_allocation_cache_.empty(),\n         \"insert_initial should only be used for initialization\");\n  ASSERT(deriv_vars_allocation_cache_.empty(),\n         \"insert_initial should only be used for initialization\");\n  ASSERT(time_step_id.substep() == 0, \"Cannot use insert_initial for substeps\");\n  ASSERT(this->empty() or time_step_id < this->front().time_step_id,\n         \"New initial entry at \" << time_step_id\n         << \" must be earlier than previous entry at \"\n         << this->front().time_step_id);\n\n  if constexpr (std::is_same_v<InsertedVars, Vars>) {\n    step_values_.push_front(StepRecord<Vars>{\n        std::move(time_step_id), std::move(value), std::move(derivative)});\n  } else {\n    static_assert(std::is_same_v<InsertedVars, NoValue>);\n    (void)value;\n    step_values_.push_front(StepRecord<Vars>{\n        std::move(time_step_id), std::nullopt, std::move(derivative)});\n  }\n}\n\ntemplate <typename Vars>\nbool operator==(const History<Vars>& a, const History<Vars>& b) {\n  return a.integration_order() == b.integration_order() and\n         a.size() == b.size() and std::equal(a.begin(), a.end(), b.begin()) and\n         a.substeps() == b.substeps();\n}\n\ntemplate <typename Vars>\nbool operator!=(const History<Vars>& a, const History<Vars>& b) {\n  return not(a == b);\n}\n\ntemplate <typename Vars>\nstd::ostream& operator<<(std::ostream& os, const History<Vars>& history) {\n  return history.print(os);\n}\n\n/// \\ingroup TimeSteppersGroup\n/// Initialize a History object based on the contents of another,\n/// applying a transformation to each value and derivative.\n///\n/// The transformation functions can either take a value from the\n/// source history and return a value for the destination history or\n/// take a `gsl::not_null` value from the destination history and a\n/// value from the source history to initialize it with.  For the sake\n/// of implementation simplicity, either both transformers must mutate\n/// or both must produce values.\n///\n/// An overload applying the same transformation to the values and\n/// derivatives is provided for convenience.\n///\n/// \\see transform_mutate\n/// @{\ntemplate <typename DestVars, typename SourceVars, typename ValueTransformer,\n          typename DerivativeTransformer>\nvoid transform(const gsl::not_null<History<DestVars>*> dest,\n               const History<SourceVars>& source,\n               ValueTransformer&& value_transformer,\n               DerivativeTransformer&& derivative_transformer) {\n  dest->clear_substeps();\n  dest->clear();\n  dest->integration_order(source.integration_order());\n  if (source.empty()) {\n    return;\n  }\n  auto pre_substep_end = source.end();\n  if (not source.substeps().empty() and\n      source.back().time_step_id > source.substeps().back().time_step_id) {\n    --pre_substep_end;\n  }\n\n  const auto transform_record =\n      [&derivative_transformer, &dest, &value_transformer](\n          const typename History<SourceVars>::value_type& record) {\n        if constexpr (std::is_invocable_v<ValueTransformer,\n                                          gsl::not_null<DestVars*>,\n                                          const SourceVars&>) {\n          if (record.value.has_value()) {\n            dest->insert_in_place(\n                record.time_step_id,\n                [&](const auto result) {\n                  value_transformer(result, *record.value);\n                },\n                [&](const auto result) {\n                  derivative_transformer(result, record.derivative);\n                });\n          } else {\n            dest->insert_in_place(\n                record.time_step_id, History<DestVars>::no_value,\n                [&](const auto result) {\n                  derivative_transformer(result, record.derivative);\n                });\n          }\n        } else {\n          static_assert(\n              std::is_invocable_v<ValueTransformer, const SourceVars&>,\n              \"Transform function must either be callable to mutate entries \"\n              \"or return the transformed state by value.\");\n          if (record.value.has_value()) {\n            dest->insert(record.time_step_id, value_transformer(*record.value),\n                         derivative_transformer(record.derivative));\n          } else {\n            dest->insert(record.time_step_id, History<DestVars>::no_value,\n                         derivative_transformer(record.derivative));\n          }\n        }\n      };\n\n  auto copying_step = source.begin();\n  for (; copying_step != pre_substep_end; ++copying_step) {\n    transform_record(*copying_step);\n  }\n  for (const auto& record : source.substeps()) {\n    transform_record(record);\n  }\n  if (pre_substep_end != source.end()) {\n    transform_record(*pre_substep_end);\n  }\n}\n\ntemplate <typename DestVars, typename SourceVars, typename Transformer>\nvoid transform(const gsl::not_null<History<DestVars>*> dest,\n               const History<SourceVars>& source, Transformer&& transformer) {\n  transform(dest, source, transformer, transformer);\n}\n/// @}\n\n/// \\ingroup TimeSteppersGroup\n/// Combine two History objects by applying a transformation to each\n/// value and derivative.\n///\n/// The transformers must accept a `gsl::not_null` value from the \\p\n/// dest history and a const reference to value from the \\p source\n/// history.  They will be applied to each corresponding (non-nullopt)\n/// pair of values from the two histories.  It is an error if the\n/// entries in the two histories do not match.\n///\n/// An overload applying the same transformation to the values and\n/// derivatives is provided for convenience.\n///\n/// \\see transform\n/// @{\ntemplate <typename DestVars, typename SourceVars, typename ValueTransformer,\n          typename DerivativeTransformer>\nvoid transform_mutate(const gsl::not_null<History<DestVars>*> dest,\n                      const History<SourceVars>& source,\n                      ValueTransformer&& value_transformer,\n                      DerivativeTransformer&& derivative_transformer) {\n  ASSERT(dest->integration_order() == source.integration_order(),\n         \"Attempting to combine histories with integration orders \"\n             << dest->integration_order() << \" and \"\n             << source.integration_order());\n\n  const auto transform_record =\n      [&derivative_transformer, &value_transformer](\n          typename History<DestVars>::value_type& dest_record,\n          const typename History<SourceVars>::value_type& source_record) {\n        ASSERT(dest_record.time_step_id == source_record.time_step_id,\n               \"Entries to combine do not match: \"\n                   << dest_record.time_step_id << \" \"\n                   << source_record.time_step_id);\n        ASSERT(dest_record.value.has_value() == source_record.value.has_value(),\n               \"Only one of the entries to combine has a value at \"\n                   << dest_record.time_step_id);\n        if (dest_record.value.has_value()) {\n          value_transformer(make_not_null(&*dest_record.value),\n                            *source_record.value);\n        }\n        derivative_transformer(make_not_null(&dest_record.derivative),\n                               source_record.derivative);\n      };\n\n  ASSERT(dest->size() == source.size(),\n         \"Attempting to combine histories with sizes \"\n             << dest->size() << \" and \" << source.size());\n  {\n    auto dest_entry = dest->begin();\n    auto source_entry = source.begin();\n    while (dest_entry != dest->end()) {\n      transform_record(*dest_entry, *source_entry);\n      ++dest_entry;\n      ++source_entry;\n    }\n  }\n\n  {\n    auto dest_substeps = dest->substeps();\n    const auto source_substeps = source.substeps();\n    ASSERT(dest_substeps.size() == source_substeps.size(),\n           \"Attempting to combine histories with substep sizes \"\n               << dest_substeps.size() << \" and \" << source_substeps.size());\n    auto dest_entry = dest_substeps.begin();\n    auto source_entry = source_substeps.begin();\n    while (dest_entry != dest_substeps.end()) {\n      transform_record(*dest_entry, *source_entry);\n      ++dest_entry;\n      ++source_entry;\n    }\n  }\n}\n\ntemplate <typename DestVars, typename SourceVars, typename Transformer>\nvoid transform_mutate(const gsl::not_null<History<DestVars>*> dest,\n                      const History<SourceVars>& source,\n                      Transformer&& transformer) {\n  transform_mutate(dest, source, transformer, transformer);\n}\n/// @}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/LargestStepperError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/LargestStepperError.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <complex>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\ntemplate <typename T>\ndouble largest_stepper_error(const T& values, const T& errors,\n                             const StepperErrorTolerances& tolerances) {\n  using std::abs;\n  // Outer call to max() may be from blaze or an identity function\n  // from ContainerHelpers.hpp for doubles.\n  using ::max;\n  // Inner max() is either blaze or std::max.\n  using std::max;\n  return max(abs(errors) /\n             (tolerances.absolute +\n              tolerances.relative * max(abs(values), abs(values + errors))));\n}\n\n#define TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                \\\n  template double largest_stepper_error(                    \\\n      const TYPE(data) & values, const TYPE(data) & errors, \\\n      const StepperErrorTolerances& tolerances);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, std::complex<double>, DataVector,\n                                      ComplexDataVector))\n\n#undef INSTANTIATE\n#undef TYPE\n"
  },
  {
    "path": "src/Time/LargestStepperError.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n\n/// \\cond\nclass ComplexDataVector;\nclass DataVector;\nclass StepperErrorTolerances;\n/// \\endcond\n\n/*!\n * \\ingroup TimeGroup\n * \\brief Calculate the pointwise worst error.\n *\n * \\details For a `double`, or `std::complex<double>`, calculates\n *\n * \\f{equation}\n *   \\frac{e}{a + r \\max(|v|, |v + e|)}\n * \\f}\n *\n * where $v$ is \\p values, $e$ is \\p errors, and $a$ and $r$ are the\n * tolerances from \\p tolerances.  For vector types, calculates the\n * largest error over all the points.  Only the `absolute` and\n * `relative` fields from \\p tolerances are used.\n *\n * Instantiated for `double`, `std::complex<double>`, `DataVector`,\n * and `ComplexDataVector`.\n */\ntemplate <typename T>\ndouble largest_stepper_error(const T& values, const T& errors,\n                             const StepperErrorTolerances& tolerances);\n"
  },
  {
    "path": "src/Time/OptionTags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  InitialSlabSize.hpp\n  InitialTime.hpp\n  InitialTimeStep.hpp\n  LtsStepChoosers.hpp\n  MinimumTimeStep.hpp\n  TimeStepper.hpp\n  VariableOrderAlgorithm.hpp\n  )\n"
  },
  {
    "path": "src/Time/OptionTags/InitialSlabSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief The initial slab size\nstruct InitialSlabSize {\n  using type = double;\n  static constexpr Options::String help = \"The initial slab size\";\n  static type lower_bound() { return 0.; }\n  using group = evolution::OptionTags::Group;\n};\n}  // namespace OptionTags\n"
  },
  {
    "path": "src/Time/OptionTags/InitialTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief The time at which to start the simulation\nstruct InitialTime {\n  using type = double;\n  static constexpr Options::String help = {\n      \"The time at which the evolution is started.\"};\n  static type suggested_value() { return 0.0; }\n  using group = evolution::OptionTags::Group;\n};\n}  // namespace OptionTags\n"
  },
  {
    "path": "src/Time/OptionTags/InitialTimeStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief The initial time step taken by the time stepper. This may be\n/// overridden by an adaptive stepper\nstruct InitialTimeStep {\n  using type = double;\n  static constexpr Options::String help =\n      \"The initial time step, before local stepping adjustment\";\n  using group = evolution::OptionTags::Group;\n};\n}  // namespace OptionTags\n"
  },
  {
    "path": "src/Time/OptionTags/LtsStepChoosers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <vector>\n\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup TimeGroup\nstruct LtsStepChoosers {\n  static constexpr Options::String help{\n      \"Limits on the LTS step size.  If the list is empty, the step:slab \"\n      \"ratio will not be changed.\"};\n  using type =\n      std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>;\n  using group = evolution::OptionTags::Group;\n};\n}  // namespace OptionTags\n"
  },
  {
    "path": "src/Time/OptionTags/MinimumTimeStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief The minimum step size without triggering an error\nstruct MinimumTimeStep {\n  using type = double;\n  static constexpr Options::String help =\n      \"The minimum step size without triggering an error\";\n  static type lower_bound() { return 0.; }\n  using group = evolution::OptionTags::Group;\n};\n}  // namespace OptionTags\n"
  },
  {
    "path": "src/Time/OptionTags/TimeStepper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup TimeGroup\ntemplate <typename StepperType>\nstruct TimeStepper {\n  static std::string name() { return \"TimeStepper\"; }\n  static constexpr Options::String help{\"The time stepper\"};\n  using type = std::unique_ptr<StepperType>;\n  using group = evolution::OptionTags::Group;\n};\n}  // namespace OptionTags\n"
  },
  {
    "path": "src/Time/OptionTags/VariableOrderAlgorithm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Evolution/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/VariableOrderAlgorithm.hpp\"\n\nnamespace OptionTags {\n/// \\ingroup OptionTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Algorithm for changing the time-stepper order in a\n/// variable-order evolution.\n/// \\see ChangeTimeStepperOrder\nstruct VariableOrderAlgorithm {\n  using type = ::VariableOrderAlgorithm;\n  static constexpr Options::String help =\n      \"Algorithm for changing the time-stepper order in a variable-order \"\n      \"evolution.\";\n  using group = evolution::OptionTags::Group;\n};\n}  // namespace OptionTags\n"
  },
  {
    "path": "src/Time/RecordTimeStepperData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nclass TimeStepId;\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\nstruct TimeStepId;\n}  // namespace Tags\nnamespace TimeSteppers {\ntemplate <typename Vars>\nclass History;\n}  // namespace TimeSteppers\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// Records the variables and their time derivatives in the time stepper\n/// history.\n/// @{\ntemplate <typename System,\n          typename = tmpl::conditional_t<\n              tt::is_a_v<tmpl::list, typename System::variables_tag>,\n              typename System::variables_tag,\n              tmpl::list<typename System::variables_tag>>>\nstruct RecordTimeStepperData;\n\ntemplate <typename System, typename... VariablesTags>\nstruct RecordTimeStepperData<System, tmpl::list<VariablesTags...>> {\n  using return_tags =\n      tmpl::list<Tags::HistoryEvolvedVariables<VariablesTags>...>;\n  using argument_tags =\n      tmpl::list<Tags::TimeStepId, VariablesTags...,\n                 db::add_tag_prefix<Tags::dt, VariablesTags>...>;\n\n  static void apply(\n      const gsl::not_null<\n          TimeSteppers::History<typename VariablesTags::type>*>... histories,\n      const TimeStepId& time_step_id,\n      const typename VariablesTags::type&... vars,\n      const typename db::add_tag_prefix<Tags::dt,\n                                        VariablesTags>::type&... dt_vars);\n};\n/// @}\n"
  },
  {
    "path": "src/Time/RecordTimeStepperData.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Time/RecordTimeStepperData.hpp\"\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <typename System, typename... VariablesTags>\nvoid RecordTimeStepperData<System, tmpl::list<VariablesTags...>>::apply(\n    const gsl::not_null<\n        TimeSteppers::History<typename VariablesTags::type>*>... histories,\n    const TimeStepId& time_step_id, const typename VariablesTags::type&... vars,\n    const typename db::add_tag_prefix<Tags::dt,\n                                      VariablesTags>::type&... dt_vars) {\n  expand_pack((histories->insert(time_step_id, vars, dt_vars), 0)...);\n}\n"
  },
  {
    "path": "src/Time/RequestsStepperErrorTolerances.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <typeindex>\n#include <unordered_map>\n\n#include \"Time/StepperErrorTolerances.hpp\"\n\n/// \\ingroup TimeGroup\n/// Base class for requesting time stepper error tolerances.\nstruct RequestsStepperErrorTolerances {\n public:\n  /// A map from the type of a variables tag to the tolerances for\n  /// that variable.\n  virtual std::unordered_map<std::type_index, StepperErrorTolerances>\n  tolerances() const = 0;\n\n protected:\n  RequestsStepperErrorTolerances() = default;\n  ~RequestsStepperErrorTolerances() = default;\n};\n"
  },
  {
    "path": "src/Time/SelfStart.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/SelfStart.hpp\"\n\n#include \"Time/TimeStepId.hpp\"\n\nnamespace SelfStart {\nbool is_self_starting(const TimeStepId& time_id) {\n  return time_id.slab_number() < 0;\n}\n\nbool step_unused(const TimeStepId& time_id, const TimeStepId& next_time_id) {\n  return time_id.slab_number() < 0 and\n         time_id.slab_number() != next_time_id.slab_number();\n}\n}  // namespace SelfStart\n"
  },
  {
    "path": "src/Time/SelfStart.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Time/TimeStepId.hpp\"\n\nnamespace SelfStart {\n/// Reports whether the `time_id` is during self start\n///\n/// This currently assumes that the slab number of the `time_id` will be\n/// negative if and only if self-start is in progress. If self start is\n/// modified to alter that behavior, this utility must also be modified.\nbool is_self_starting(const TimeStepId& time_id);\n\n/// Returns whether the result of the step from \\p time_id to \\p\n/// next_time_id is unused because of a self-start reset.\nbool step_unused(const TimeStepId& time_id, const TimeStepId& next_time_id);\n}  // namespace SelfStart\n"
  },
  {
    "path": "src/Time/Slab.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Slab.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <ostream>\n#include <pup.h>\n\n#include \"Time/Time.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nSlab::Slab(const double start, const double end) : start_(start), end_(end) {\n  ASSERT(start_ < end_, \"Backwards Slab: \" << start_ << \" >= \" << end_);\n}\n\nSlab Slab::with_duration_from_start(const double start, const double duration) {\n  return {start, start + duration};\n}\n\nSlab Slab::with_duration_to_end(const double end, const double duration) {\n  return {end - duration, end};\n}\n\nTime Slab::start() const { return {*this, 0}; }\n\nTime Slab::end() const { return {*this, 1}; }\n\nTimeDelta Slab::duration() const { return {*this, 1}; }\n\nSlab Slab::advance() const { return {end_, end_ + (end_ - start_)}; }\n\nSlab Slab::retreat() const { return {start_ - (end_ - start_), start_}; }\n\nSlab Slab::advance_towards(const TimeDelta& dt) const {\n  ASSERT(dt.is_positive() or (-dt).is_positive(),\n         \"Can't advance along a zero time vector\");\n  return dt.is_positive() ? advance() : retreat();\n}\n\nSlab Slab::with_duration_from_start(const double duration) const {\n  return {start_, start_ + duration};\n}\n\nSlab Slab::with_duration_to_end(const double duration) const {\n  return {end_ - duration, end_};\n}\n\nbool Slab::is_followed_by(const Slab& other) const {\n  return end_ == other.start_;\n}\n\nbool Slab::is_preceeded_by(const Slab& other) const {\n  return other.is_followed_by(*this);\n}\n\nbool Slab::overlaps(const Slab& other) const {\n  return not(end_ <= other.start_ or start_ >= other.end_);\n}\n\nvoid Slab::pup(PUP::er& p) {\n  p | start_;\n  p | end_;\n}\n\nbool operator==(const Slab& a, const Slab& b) {\n  return a.start_ == b.start_ and a.end_ == b.end_;\n}\n\nbool operator!=(const Slab& a, const Slab& b) { return not(a == b); }\n\nbool operator<(const Slab& a, const Slab& b) {\n  ASSERT(a == b or a.end_ <= b.start_ or a.start_ >= b.end_,\n         \"Cannot compare overlapping slabs \" << a << \" and \" << b);\n  return a.end_ <= b.start_;\n}\n\nbool operator>(const Slab& a, const Slab& b) { return b < a; }\n\nbool operator<=(const Slab& a, const Slab& b) { return not(a > b); }\n\nbool operator>=(const Slab& a, const Slab& b) { return not(a < b); }\n\nstd::ostream& operator<<(std::ostream& os, const Slab& s) {\n  return os << \"Slab[\" << s.start().value() << \",\" << s.end().value() << \"]\";\n}\n\nsize_t hash_value(const Slab& s) {\n  size_t h = 0;\n  boost::hash_combine(h, s.start().value());\n  boost::hash_combine(h, s.end().value());\n  return h;\n}\n\n// clang-tidy: do not modify std namespace (okay for hash)\nnamespace std {  // NOLINT\nsize_t hash<Slab>::operator()(const Slab& s) const {\n  return boost::hash<Slab>{}(s);\n}\n}  // namespace std\n"
  },
  {
    "path": "src/Time/Slab.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class Slab\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <iosfwd>\n#include <limits>\n\nclass Time;\nclass TimeDelta;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\n/// \\ingroup TimeGroup\n///\n/// A chunk of time.  Every element must reach slab boundaries\n/// exactly, no matter how it actually takes time steps to get there.\n/// The simulation can only be assumed to have global data available\n/// at slab boundaries.\nclass Slab {\n public:\n  /// Default constructor gives an invalid Slab.\n  Slab() = default;\n\n  /// Construct a slab running between two times (exactly).\n  Slab(double start, double end);\n\n  /// Construct a slab with a given start time and duration.  The\n  /// actual duration may differ by roundoff from the supplied value.\n  static Slab with_duration_from_start(double start, double duration);\n\n  /// Construct a slab with a given end time and duration.  The\n  /// actual duration may differ by roundoff from the supplied value.\n  static Slab with_duration_to_end(double end, double duration);\n\n  Time start() const;\n  Time end() const;\n  TimeDelta duration() const;\n\n  /// Create a new slab immediately following this one with the same\n  /// (up to roundoff) duration.\n  Slab advance() const;\n\n  /// Create a new slab immediately preceeding this one with the same\n  /// (up to roundoff) duration.\n  Slab retreat() const;\n\n  /// Create a slab adjacent to this one in the direction indicated by\n  /// the argument, as with advance() or retreat().\n  Slab advance_towards(const TimeDelta& dt) const;\n\n  /// Create a new slab with the same start time as this one with the\n  /// given duration (up to roundoff).\n  Slab with_duration_from_start(double duration) const;\n\n  /// Create a new slab with the same end time as this one with the\n  /// given duration (up to roundoff).\n  Slab with_duration_to_end(double duration) const;\n\n  /// Check if this slab is immediately followed by the other slab.\n  bool is_followed_by(const Slab& other) const;\n\n  /// Check if this slab is immediately preceeded by the other slab.\n  bool is_preceeded_by(const Slab& other) const;\n\n  /// Check if slabs overlap.  Abutting slabs do not overlap.\n  bool overlaps(const Slab& other) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  double start_ = std::numeric_limits<double>::signaling_NaN();\n  double end_ = std::numeric_limits<double>::signaling_NaN();\n\n  friend class Time;\n  friend class TimeDelta;\n\n  friend bool operator==(const Slab& a, const Slab& b);\n  friend bool operator<(const Slab& a, const Slab& b);\n  friend bool operator==(const Time& a, const Time& b);\n};\n\nbool operator!=(const Slab& a, const Slab& b);\n\n/// Slab comparison operators give the time ordering.  Overlapping\n/// unequal slabs should not be compared (and will trigger an\n/// assertion).\n/// @{\n// NOLINTNEXTLINE(readability-redundant-declaration) redeclared for docs\nbool operator<(const Slab& a, const Slab& b);\nbool operator>(const Slab& a, const Slab& b);\nbool operator<=(const Slab& a, const Slab& b);\nbool operator>=(const Slab& a, const Slab& b);\n/// @}\n\nstd::ostream& operator<<(std::ostream& os, const Slab& s);\n\nsize_t hash_value(const Slab& s);\n\nnamespace std {\ntemplate <>\nstruct hash<Slab> {\n  size_t operator()(const Slab& s) const;\n};\n}  // namespace std\n\n// Needs to be included so various return types are complete, but\n// cannot be included at the top because Time.hpp includes this file.\n#include \"Time/Time.hpp\"\n"
  },
  {
    "path": "src/Time/SlabRoundingError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/SlabRoundingError.hpp\"\n\n#include <cmath>\n#include <limits>\n\n#include \"Time/Time.hpp\"\n\ndouble slab_rounding_error(const Time& time) {\n  return 4.0 * std::numeric_limits<double>::epsilon() *\n         (std::abs(time.value()) + std::abs(time.slab().duration().value()));\n}\n"
  },
  {
    "path": "src/Time/SlabRoundingError.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\cond\nclass Time;\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// Scale of the roundoff error incurred from inexact slab operations\n/// near the given time.\ndouble slab_rounding_error(const Time& time);\n"
  },
  {
    "path": "src/Time/StepChoosers/ByBlock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepChoosers/ByBlock.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Structure/Element.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace StepChoosers {\ntemplate <size_t Dim>\nByBlock<Dim>::ByBlock(std::vector<double> sizes) : sizes_(std::move(sizes)) {}\n\ntemplate <size_t Dim>\nTimeStepRequest ByBlock<Dim>::operator()(const Element<Dim>& element,\n                                         const double last_step) const {\n  const size_t block = element.id().block_id();\n  if (block >= sizes_.size()) {\n    ERROR(\"Step size not specified for block \" << block);\n  }\n  return {.size_goal = std::copysign(sizes_[block], last_step)};\n}\n\ntemplate <size_t Dim>\nbool ByBlock<Dim>::uses_local_data() const {\n  return true;\n}\n\ntemplate <size_t Dim>\nbool ByBlock<Dim>::can_be_delayed() const {\n  return true;\n}\n\ntemplate <size_t Dim>\nvoid ByBlock<Dim>::pup(PUP::er& p) {\n  StepChooser<StepChooserUse::Slab>::pup(p);\n  StepChooser<StepChooserUse::LtsStep>::pup(p);\n  p | sizes_;\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID ByBlock<Dim>::my_PUP_ID = 0;  // NOLINT\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) template class ByBlock<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/ByBlock.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <vector>\n\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Element;\nnamespace domain {\nnamespace Tags {\ntemplate <size_t VolumeDim>\nstruct Element;\n}  // namespace Tags\n}  // namespace domain\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace StepChoosers {\n/// Sets a goal specified per-block.\n///\n/// \\note This debugging StepChooser is not included in the\n/// `standard_step_choosers` list, but can be added to the\n/// `factory_creation` struct in the metavariables.\ntemplate <size_t Dim>\nclass ByBlock : public StepChooser<StepChooserUse::Slab>,\n                public StepChooser<StepChooserUse::LtsStep> {\n public:\n  /// \\cond\n  ByBlock() = default;\n  explicit ByBlock(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ByBlock);  // NOLINT\n  /// \\endcond\n\n  struct Sizes {\n    using type = std::vector<double>;\n    static constexpr Options::String help{\n        \"Step sizes, indexed by block number\"};\n  };\n\n  static constexpr Options::String help{\"Sets a goal specified per-block.\"};\n  using options = tmpl::list<Sizes>;\n\n  explicit ByBlock(std::vector<double> sizes);\n\n  using argument_tags = tmpl::list<domain::Tags::Element<Dim>>;\n\n  TimeStepRequest operator()(const Element<Dim>& element,\n                             double last_step) const;\n\n  bool uses_local_data() const override;\n  bool can_be_delayed() const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<double> sizes_;\n};\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ByBlock.cpp\n  Constant.cpp\n  FixedLtsRatio.cpp\n  LimitIncrease.cpp\n  Maximum.cpp\n  Random.cpp\n  StepToTimes.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ByBlock.hpp\n  Cfl.hpp\n  Constant.hpp\n  ElementSizeCfl.hpp\n  ErrorControl.hpp\n  Factory.hpp\n  FixedLtsRatio.hpp\n  LimitIncrease.hpp\n  Maximum.hpp\n  PreventRapidIncrease.hpp\n  Random.hpp\n  StepChooser.hpp\n  StepToTimes.hpp\n  )\n"
  },
  {
    "path": "src/Time/StepChoosers/Cfl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/MinimumGridSpacing.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim, typename Frame>\nstruct MinimumGridSpacing;\n}  // namespace Tags\n}  // namespace domain\n\n/// \\endcond\n\nnamespace StepChoosers {\n/// Sets a goal based on the CFL stability criterion.\ntemplate <typename Frame, typename System>\nclass Cfl : public StepChooser<StepChooserUse::Slab>,\n            public StepChooser<StepChooserUse::LtsStep> {\n public:\n  /// \\cond\n  Cfl() = default;\n  explicit Cfl(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Cfl);  // NOLINT\n  /// \\endcond\n\n  struct SafetyFactor {\n    using type = double;\n    static constexpr Options::String help{\"Multiplier for computed step\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  static constexpr Options::String help{\n      \"Sets a goal based on the CFL stability criterion.\"};\n  using options = tmpl::list<SafetyFactor>;\n\n  explicit Cfl(const double safety_factor) : safety_factor_(safety_factor) {}\n\n  using argument_tags =\n      tmpl::list<domain::Tags::MinimumGridSpacing<System::volume_dim, Frame>,\n                 ::Tags::TimeStepper<TimeStepper>,\n                 typename System::compute_largest_characteristic_speed::base>;\n\n  using compute_tags = tmpl::list<\n      domain::Tags::MinimumGridSpacingCompute<System::volume_dim, Frame>,\n      typename System::compute_largest_characteristic_speed>;\n\n  TimeStepRequest operator()(const double minimum_grid_spacing,\n                             const TimeStepper& time_stepper,\n                             const double speed, const double last_step) const {\n    const double time_stepper_stability_factor = time_stepper.stable_step();\n    const double step_size = safety_factor_ * time_stepper_stability_factor *\n                             minimum_grid_spacing /\n                             (speed * System::volume_dim);\n    return {.size_goal = std::copysign(step_size, last_step)};\n  }\n\n  bool uses_local_data() const override { return true; }\n  bool can_be_delayed() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    StepChooser<StepChooserUse::Slab>::pup(p);\n    StepChooser<StepChooserUse::LtsStep>::pup(p);\n    p | safety_factor_;\n  }\n\n private:\n  double safety_factor_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <typename Frame, typename System>\nPUP::able::PUP_ID Cfl<Frame, System>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/Constant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepChoosers/Constant.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n\nnamespace StepChoosers {\nConstant::Constant(const double value) : value_(value) {}\n\nTimeStepRequest Constant::operator()(const double last_step) const {\n  return {.size_goal = std::copysign(value_, last_step)};\n}\n\nbool Constant::uses_local_data() const { return false; }\n\nbool Constant::can_be_delayed() const { return true; }\n\nvoid Constant::pup(PUP::er& p) {\n  StepChooser<StepChooserUse::Slab>::pup(p);\n  StepChooser<StepChooserUse::LtsStep>::pup(p);\n  p | value_;\n}\n\nPUP::able::PUP_ID Constant::my_PUP_ID = 0;  // NOLINT\n}  // namespace StepChoosers\n\ntemplate <>\nStepChoosers::Constant\nOptions::create_from_yaml<StepChoosers::Constant>::create<void>(\n    const Options::Option& options) {\n  return StepChoosers::Constant{options.parse_as<double>()};\n}\n"
  },
  {
    "path": "src/Time/StepChoosers/Constant.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace StepChoosers {\n\n/// Sets a constant goal.\nclass Constant : public StepChooser<StepChooserUse::Slab>,\n                 public StepChooser<StepChooserUse::LtsStep> {\n public:\n  /// \\cond\n  Constant() = default;\n  explicit Constant(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Constant);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help{\"Sets a constant goal.\"};\n\n  explicit Constant(double value);\n\n  using argument_tags = tmpl::list<>;\n\n  TimeStepRequest operator()(double last_step) const;\n\n  bool uses_local_data() const override;\n  bool can_be_delayed() const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double value_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace StepChoosers\n\ntemplate <>\nstruct Options::create_from_yaml<StepChoosers::Constant> {\n  template <typename Metavariables>\n  static StepChoosers::Constant create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nStepChoosers::Constant\nOptions::create_from_yaml<StepChoosers::Constant>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Time/StepChoosers/ElementSizeCfl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace domain {\nnamespace Tags {\ntemplate <size_t Dim>\nstruct SizeOfElement;\n}  // namespace Tags\n}  // namespace domain\n\n/// \\endcond\n\nnamespace StepChoosers {\n/// Sets a goal based on the CFL stability criterion, but uses the full\n/// size of the element as the length scale in question.\n///\n/// This is useful as a coarse estimate for slabs, or to place a ceiling on\n/// another dynamically-adjusted step chooser.\ntemplate <size_t Dim, typename System>\nclass ElementSizeCfl : public StepChooser<StepChooserUse::Slab>,\n                       public StepChooser<StepChooserUse::LtsStep> {\n public:\n  /// \\cond\n  ElementSizeCfl() = default;\n  explicit ElementSizeCfl(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ElementSizeCfl);  // NOLINT\n  /// \\endcond\n\n  struct SafetyFactor {\n    using type = double;\n    static constexpr Options::String help{\"Multiplier for computed step\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  static constexpr Options::String help{\n      \"Sets a goal based on the CFL stability criterion, but in which \"\n      \"the entire size of the element is used as the spacing in the \"\n      \"computation. This is useful primarily for placing a ceiling on another \"\n      \"dynamically-adjusted step chooser\"};\n  using options = tmpl::list<SafetyFactor>;\n\n  explicit ElementSizeCfl(const double safety_factor)\n      : safety_factor_(safety_factor) {}\n\n  using argument_tags =\n      tmpl::list<::Tags::TimeStepper<TimeStepper>,\n                 domain::Tags::SizeOfElement<Dim>,\n                 typename System::compute_largest_characteristic_speed::base>;\n  using compute_tags =\n      tmpl::list<domain::Tags::SizeOfElementCompute<Dim>,\n                 typename System::compute_largest_characteristic_speed>;\n\n  TimeStepRequest operator()(const TimeStepper& time_stepper,\n                             const std::array<double, Dim>& element_size,\n                             const double speed, const double last_step) const {\n    double min_size_of_element = std::numeric_limits<double>::infinity();\n    for (auto face_to_face_dimension : element_size) {\n      if (face_to_face_dimension < min_size_of_element) {\n        min_size_of_element = face_to_face_dimension;\n      }\n    }\n    const double time_stepper_stability_factor = time_stepper.stable_step();\n    const double step_size = safety_factor_ * time_stepper_stability_factor *\n                             min_size_of_element / (speed * Dim);\n    return {.size_goal = std::copysign(step_size, last_step)};\n  }\n\n  bool uses_local_data() const override { return true; }\n  bool can_be_delayed() const override { return true; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    StepChooser<StepChooserUse::Slab>::pup(p);\n    StepChooser<StepChooserUse::LtsStep>::pup(p);\n    p | safety_factor_;\n  }\n\n private:\n  double safety_factor_ = std::numeric_limits<double>::signaling_NaN();\n};\n\n/// \\cond\ntemplate <size_t Dim, typename System>\nPUP::able::PUP_ID ElementSizeCfl<Dim, System>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/ErrorControl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cmath>\n#include <limits>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <typeindex>\n#include <typeinfo>\n#include <unordered_map>\n\n#include \"Options/String.hpp\"\n#include \"Time/RequestsStepperErrorTolerances.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nstruct NoSuchType;\n/// \\endcond\n\nnamespace StepChoosers {\n/*!\n * \\brief Sets a goal based on time-stepper truncation error.\n *\n * \\details The suggested step is calculated via a simple specialization of the\n * scheme suggested in \\cite Hairer1993. We first compute the aggregated error\n * measure from the stepper error:\n *\n * \\f[\n * E = \\max_i(|E_i| / sc_i),\n * \\f]\n *\n * where \\f$E_i\\f$ is the ODE error reported for each individual grid point,\n * reported by the time stepper, and \\f$sc_i\\f$ is the step control measure\n * determined by the tolerances:\n *\n * \\f[\n * sc_i = Atol_i + \\max(|y_i|,|y_i + E_i|) Rtol_i,\n * \\f]\n *\n * and \\f$y_i\\f$ is the value of the function at the previous step at\n * grid point \\f$i\\f$.  (The estimate is more commonly done comparing\n * with the current step, but using the previous step avoids a memory\n * allocation in the TimeStepper and should not have a major effect on\n * the result.)\n *\n * When choosing a step size for LTS or when no record of previous\n * error is available, the step has size:\n *\n * \\f[\n * h_{\\text{new}} = h \\cdot \\min\\left(F_{\\text{max}},\n * \\max\\left(F_{\\text{min}},\n * \\frac{F_{\\text{safety}}}{E^{1/(q + 1)}}\\right)\\right),\n * \\f]\n *\n * where \\f$h_{\\text{new}}\\f$ is the new suggested step size \\f$h\\f$ is the\n * previous step size, \\f$F_{\\text{max}}\\f$ is the maximum factor by which we\n * allow the step to increase, \\f$F_{\\text{min}}\\f$ is the minimum factor by\n * which we allow the step to decrease. \\f$F_{\\text{safety}}\\f$ is the safety\n * factor on the computed error -- this forces the step size slightly lower\n * than we would naively compute so that the result of the step will likely be\n * within the target error. \\f$q\\f$ is the order of the stepper error\n * calculation. Intuitively, we should change the step less drastically for a\n * higher order stepper.\n *\n * When controlling slab size, after the first error calculation, the\n * error \\f$E\\f$ is recorded in the \\ref DataBoxGroup \"DataBox\", and\n * subsequent error calculations use a simple PI scheme suggested in\n * \\cite NumericalRecipes section 17.2.1:\n *\n * \\f[\n * h_{\\text{new}} = h \\cdot \\min\\left(F_{\\text{max}},\n * \\max\\left(F_{\\text{min}},\n * F_{\\text{safety}} E^{-0.7 / (q + 1)}\n * E_{\\text{prev}}^{0.4 / (q + 1)}\\right)\\right),\n * \\f]\n *\n * where \\f$E_{\\text{prev}}\\f$ is the error computed in the previous\n * step.  This method is never used for choosing an LTS step because\n * the restriction of step size changes to factors of two was found to\n * interfere with the more gradual increase chosen by the PI\n * controller.\n *\n * \\note The template parameter `ErrorControlSelector` is used to disambiguate\n * in the input-file options between `ErrorControl` step choosers that are\n * based on different variables. This is needed if multiple systems are evolved\n * in the same executable. The name used for the input file includes\n * `ErrorControlSelector::name()` if it is provided.\n */\ntemplate <typename StepChooserUse, typename EvolvedVariableTag,\n          typename ErrorControlSelector = NoSuchType>\nclass ErrorControl : public StepChooser<StepChooserUse>,\n                     public RequestsStepperErrorTolerances {\n public:\n  /// \\cond\n  ErrorControl() = default;\n  explicit ErrorControl(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ErrorControl);  // NOLINT\n  /// \\endcond\n\n  static std::string name() {\n    if constexpr (std::is_same_v<ErrorControlSelector, NoSuchType>) {\n      return \"ErrorControl\";\n    } else {\n      return \"ErrorControl(\" + ErrorControlSelector::name() + \")\";\n    }\n  }\n\n  struct AbsoluteTolerance {\n    using type = double;\n    static constexpr Options::String help{\"Target absolute tolerance\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct RelativeTolerance {\n    using type = double;\n    static constexpr Options::String help{\"Target relative tolerance\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct MaxFactor {\n    using type = double;\n    static constexpr Options::String help{\n        \"Maximum factor to increase the step by\"};\n    static type lower_bound() { return 1.0; }\n  };\n\n  struct MinFactor {\n    using type = double;\n    static constexpr Options::String help{\n        \"Minimum factor to increase the step by\"};\n    static type lower_bound() { return 0.0; }\n    static type upper_bound() { return 1.0; }\n  };\n\n  struct SafetyFactor {\n    using type = double;\n    static constexpr Options::String help{\n        \"Extra factor to apply to step estimate; can be used to decrease step \"\n        \"size to improve step acceptance rate.\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  static constexpr Options::String help{\n      \"Sets a goal based on time-stepper truncation error.\"};\n  using options = tmpl::list<AbsoluteTolerance, RelativeTolerance, MaxFactor,\n                             MinFactor, SafetyFactor>;\n\n  ErrorControl(const double absolute_tolerance, const double relative_tolerance,\n               const double max_factor, const double min_factor,\n               const double safety_factor)\n      : absolute_tolerance_{absolute_tolerance},\n        relative_tolerance_{relative_tolerance},\n        max_factor_{max_factor},\n        min_factor_{min_factor},\n        safety_factor_{safety_factor} {}\n\n  using argument_tags = tmpl::list<::Tags::StepperErrors<EvolvedVariableTag>>;\n\n  TimeStepRequest operator()(\n      const typename ::Tags::StepperErrors<EvolvedVariableTag>::type& errors,\n      const double /*previous_step*/) const {\n    // Do not request that the step size be changed if there isn't a new error\n    // estimate\n    if (not errors[1].has_value()) {\n      return {};\n    }\n    double new_step;\n    if (std::is_same_v<StepChooserUse, ::StepChooserUse::LtsStep> or\n        not errors[0].has_value() or errors[0]->order != errors[1]->order) {\n      new_step =\n          errors[1]->step_size.value() *\n          std::clamp(safety_factor_ *\n                         pow(1.0 / std::max(errors[1]->step_error(), 1e-14),\n                             1.0 / (errors[1]->order + 1)),\n                     min_factor_, max_factor_);\n    } else {\n      // From simple advice from Numerical Recipes 17.2.1 regarding a heuristic\n      // for PI step control.\n      const double alpha_factor = 0.7 / (errors[1]->order + 1);\n      const double beta_factor = 0.4 / (errors[0]->order + 1);\n      new_step =\n          errors[1]->step_size.value() *\n          std::clamp(\n              safety_factor_ *\n                  pow(1.0 / std::max(errors[1]->step_error(), 1e-14),\n                      alpha_factor) *\n                  pow(std::max(errors[0]->step_error(), 1e-14), beta_factor),\n              min_factor_, max_factor_);\n    }\n    return ::TimeStepRequest{.size_goal = new_step};\n  }\n\n  bool uses_local_data() const override { return true; }\n  bool can_be_delayed() const override { return true; }\n\n  std::unordered_map<std::type_index, StepperErrorTolerances> tolerances()\n      const override {\n    return {{typeid(EvolvedVariableTag),\n             {.estimates = StepperErrorTolerances::Estimates::StepperOrder,\n              .absolute = absolute_tolerance_,\n              .relative = relative_tolerance_}}};\n  }\n\n  void pup(PUP::er& p) override {  // NOLINT\n    StepChooser<StepChooserUse>::pup(p);\n    p | absolute_tolerance_;\n    p | relative_tolerance_;\n    p | min_factor_;\n    p | max_factor_;\n    p | safety_factor_;\n  }\n\n private:\n  double absolute_tolerance_ = std::numeric_limits<double>::signaling_NaN();\n  double relative_tolerance_ = std::numeric_limits<double>::signaling_NaN();\n  double max_factor_ = std::numeric_limits<double>::signaling_NaN();\n  double min_factor_ = std::numeric_limits<double>::signaling_NaN();\n  double safety_factor_ = std::numeric_limits<double>::signaling_NaN();\n};\n/// \\cond\ntemplate <typename StepChooserUse, typename EvolvedVariableTag,\n          typename ErrorControlSelector>\nPUP::able::PUP_ID ErrorControl<StepChooserUse, EvolvedVariableTag,\n                               ErrorControlSelector>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Time/StepChoosers/Cfl.hpp\"\n#include \"Time/StepChoosers/Constant.hpp\"\n#include \"Time/StepChoosers/ElementSizeCfl.hpp\"\n#include \"Time/StepChoosers/ErrorControl.hpp\"\n#include \"Time/StepChoosers/LimitIncrease.hpp\"\n#include \"Time/StepChoosers/Maximum.hpp\"\n#include \"Time/StepChoosers/PreventRapidIncrease.hpp\"\n#include \"Time/StepChoosers/StepToTimes.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace StepChoosers {\nnamespace Factory_detail {\ntemplate <typename System, bool HasCharSpeedFunctions>\nusing common_step_choosers = tmpl::push_back<\n    tmpl::conditional_t<\n        HasCharSpeedFunctions,\n        tmpl::list<StepChoosers::Cfl<Frame::Inertial, System>,\n                   StepChoosers::ElementSizeCfl<System::volume_dim, System>>,\n        tmpl::list<>>,\n    StepChoosers::Constant, StepChoosers::LimitIncrease, StepChoosers::Maximum>;\ntemplate <typename Use, typename System>\nusing step_choosers_for_step_only = tmpl::list<\n    StepChoosers::PreventRapidIncrease<typename System::variables_tag>,\n    StepChoosers::ErrorControl<Use, typename System::variables_tag>>;\nusing step_choosers_for_slab_only = tmpl::list<StepChoosers::StepToTimes>;\n\ntemplate <typename System, bool HasCharSpeedFunctions>\nusing lts_slab_choosers =\n    tmpl::append<common_step_choosers<System, HasCharSpeedFunctions>,\n                 step_choosers_for_slab_only>;\n}  // namespace Factory_detail\n\ntemplate <typename System, bool HasCharSpeedFunctions = true>\nusing standard_step_choosers = tmpl::append<\n    Factory_detail::common_step_choosers<System, HasCharSpeedFunctions>,\n    Factory_detail::step_choosers_for_step_only<StepChooserUse::LtsStep,\n                                                System>>;\n\ntemplate <typename System, bool LocalTimeStepping,\n          bool HasCharSpeedFunctions = true>\nusing standard_slab_choosers = tmpl::conditional_t<\n    LocalTimeStepping,\n    Factory_detail::lts_slab_choosers<System, HasCharSpeedFunctions>,\n    tmpl::append<\n        Factory_detail::lts_slab_choosers<System, HasCharSpeedFunctions>,\n        Factory_detail::step_choosers_for_step_only<StepChooserUse::Slab,\n                                                    System>>>;\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/FixedLtsRatio.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n#include <vector>\n\n#include \"Time/StepChoosers/StepChooser.hpp\"\n\nnamespace StepChoosers {\nFixedLtsRatio::FixedLtsRatio(\n    std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>\n        step_choosers)\n    : step_choosers_(std::move(step_choosers)) {}\n\nbool FixedLtsRatio::uses_local_data() const { return true; }\nbool FixedLtsRatio::can_be_delayed() const { return true; }\n\nvoid FixedLtsRatio::pup(PUP::er& p) {\n  StepChooser<StepChooserUse::Slab>::pup(p);\n  p | step_choosers_;\n}\n\nPUP::able::PUP_ID FixedLtsRatio::my_PUP_ID = 0;  // NOLINT\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/FixedLtsRatio.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\nstruct FixedLtsRatio;\nstruct TimeStep;\n}  // namespace Tags\n/// \\endcond\n\nnamespace StepChoosers {\n/// Requests a slab size based on the desired step in regions with a\n/// fixed slab fraction.\n///\n/// \\note This StepChooser is not included in the\n/// `standard_step_choosers` list.  Executables using the feature must\n/// include it explicitly in the `factory_creation` struct and add the\n/// `::Tags::FixedLtsRatio` tag to the element DataBox.\nclass FixedLtsRatio : public StepChooser<StepChooserUse::Slab> {\n public:\n  /// \\cond\n  FixedLtsRatio() = default;\n  explicit FixedLtsRatio(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(FixedLtsRatio);  // NOLINT\n  /// \\endcond\n\n  struct StepChoosers {\n    using type =\n        std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>;\n    static constexpr Options::String help{\"LTS step choosers to test\"};\n  };\n\n  static constexpr Options::String help{\n      \"Requests a slab size based on the desired step in regions with a fixed \"\n      \"slab fraction.\"};\n  using options = tmpl::list<StepChoosers>;\n\n  explicit FixedLtsRatio(\n      std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>\n          step_choosers);\n\n  using argument_tags = tmpl::list<::Tags::DataBox>;\n\n  template <typename DbTags>\n  TimeStepRequest operator()(const db::DataBox<DbTags>& box,\n                             const double /*last_step*/) const {\n    const auto& step_ratio = db::get<::Tags::FixedLtsRatio>(box);\n    if (not step_ratio.has_value()) {\n      return {};\n    }\n\n    const auto& current_step = db::get<::Tags::TimeStep>(box);\n    const evolution_less<double> less{current_step.is_positive()};\n\n    std::optional<double> size_goal{};\n    std::optional<double> size{};\n    for (const auto& step_chooser : step_choosers_) {\n      const auto step_request =\n          step_chooser->desired_step(current_step.value(), box);\n\n      if (step_request.size_goal.has_value()) {\n        if (size_goal.has_value()) {\n          *size_goal = std::min(*size_goal, *step_request.size_goal, less);\n        } else {\n          size_goal = step_request.size_goal;\n        }\n      }\n      if (step_request.size.has_value()) {\n        if (size.has_value()) {\n          *size = std::min(*size, *step_request.size, less);\n        } else {\n          size = step_request.size;\n        }\n      }\n\n      // As of writing (Oct. 2024), no StepChooserUse::LtsStep chooser\n      // sets these.\n      ASSERT(not(step_request.end.has_value() or\n                 step_request.size_hard_limit.has_value() or\n                 step_request.end_hard_limit.has_value()),\n             \"Unhandled field set by StepChooser.  Please file a bug \"\n             \"containing the options passed to FixedLtsRatio.\");\n    }\n\n    if (size_goal.has_value()) {\n      *size_goal *= *step_ratio;\n    }\n    if (size.has_value()) {\n      *size *= *step_ratio;\n      if (size_goal.has_value() and less(*size_goal, *size)) {\n        // Not allowed to request a goal and a bigger step.\n        size.reset();\n      }\n    }\n\n    return {.size_goal = size_goal, .size = size};\n  }\n\n  bool uses_local_data() const override;\n  bool can_be_delayed() const override;\n\n  template <typename F>\n  void for_each_step_chooser(F&& f) const {\n    for (const auto& step_chooser : step_choosers_) {\n      f(*step_chooser);\n    }\n  }\n\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>\n      step_choosers_;\n};\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/LimitIncrease.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepChoosers/LimitIncrease.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n\nnamespace StepChoosers {\nLimitIncrease::LimitIncrease(const double factor) : factor_(factor) {}\n\nTimeStepRequest LimitIncrease::operator()(const double last_step) const {\n  return {.size = last_step * factor_};\n}\n\nbool LimitIncrease::uses_local_data() const { return false; }\n\nbool LimitIncrease::can_be_delayed() const { return true; }\n\nvoid LimitIncrease::pup(PUP::er& p) {\n  StepChooser<StepChooserUse::Slab>::pup(p);\n  StepChooser<StepChooserUse::LtsStep>::pup(p);\n  p | factor_;\n}\n\nPUP::able::PUP_ID LimitIncrease::my_PUP_ID = 0;  // NOLINT\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/LimitIncrease.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace StepChoosers {\n/// Limits step increase to a constant ratio.\nclass LimitIncrease : public StepChooser<StepChooserUse::Slab>,\n                      public StepChooser<StepChooserUse::LtsStep> {\n public:\n  /// \\cond\n  LimitIncrease() = default;\n  explicit LimitIncrease(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(LimitIncrease);  // NOLINT\n  /// \\endcond\n\n  struct Factor {\n    using type = double;\n    static constexpr Options::String help{\"Factor to allow increase by\"};\n    static type lower_bound() { return 1.0; }\n  };\n\n  static constexpr Options::String help{\n      \"Limits step increase to a constant ratio.\"};\n  using options = tmpl::list<Factor>;\n\n  explicit LimitIncrease(double factor);\n\n  using argument_tags = tmpl::list<>;\n\n  TimeStepRequest operator()(double last_step) const;\n\n  bool uses_local_data() const override;\n  bool can_be_delayed() const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double factor_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/Maximum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepChoosers/Maximum.hpp\"\n\n#include <cmath>\n#include <pup.h>\n\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n\nnamespace StepChoosers {\nMaximum::Maximum(const double value) : value_(value) {}\n\nTimeStepRequest Maximum::operator()(const double last_step) const {\n  return {.size = std::copysign(value_, last_step)};\n}\n\nbool Maximum::uses_local_data() const { return false; }\n\nbool Maximum::can_be_delayed() const { return true; }\n\nvoid Maximum::pup(PUP::er& p) {\n  StepChooser<StepChooserUse::Slab>::pup(p);\n  StepChooser<StepChooserUse::LtsStep>::pup(p);\n  p | value_;\n}\n\nPUP::able::PUP_ID Maximum::my_PUP_ID = 0;  // NOLINT\n}  // namespace StepChoosers\n\ntemplate <>\nStepChoosers::Maximum\nOptions::create_from_yaml<StepChoosers::Maximum>::create<void>(\n    const Options::Option& options) {\n  return StepChoosers::Maximum{options.parse_as<double>()};\n}\n"
  },
  {
    "path": "src/Time/StepChoosers/Maximum.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n#include \"Options/Options.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nnamespace StepChoosers {\n\n/// Limits the step size to a constant.\nclass Maximum : public StepChooser<StepChooserUse::Slab>,\n                public StepChooser<StepChooserUse::LtsStep> {\n public:\n  /// \\cond\n  Maximum() = default;\n  explicit Maximum(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Maximum);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help{\"Limits the step size to a constant.\"};\n\n  explicit Maximum(double value);\n\n  using argument_tags = tmpl::list<>;\n\n  TimeStepRequest operator()(double last_step) const;\n\n  bool uses_local_data() const override;\n  bool can_be_delayed() const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double value_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace StepChoosers\n\ntemplate <>\nstruct Options::create_from_yaml<StepChoosers::Maximum> {\n  template <typename Metavariables>\n  static StepChoosers::Maximum create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\n\ntemplate <>\nStepChoosers::Maximum\nOptions::create_from_yaml<StepChoosers::Maximum>::create<void>(\n    const Options::Option& options);\n"
  },
  {
    "path": "src/Time/StepChoosers/PreventRapidIncrease.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <optional>\n#include <pup.h>\n\n#include \"Options/String.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/SlabRoundingError.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace StepChoosers {\n/// Limits the time step to prevent multistep integrator instabilities.\n///\n/// Avoids instabilities due to rapid increases in the step size by\n/// preventing the step size from increasing if any step in the\n/// time-stepper history increased.  If there have been recent step\n/// size increases, the new size bound is the size of the most recent\n/// step, otherwise no restriction is imposed.\ntemplate <typename VariablesTag>\nclass PreventRapidIncrease : public StepChooser<StepChooserUse::Slab>,\n                             public StepChooser<StepChooserUse::LtsStep> {\n public:\n  /// \\cond\n  PreventRapidIncrease() = default;\n  explicit PreventRapidIncrease(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(PreventRapidIncrease);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help{\n      \"Limits the time step to prevent multistep integrator instabilities.\"};\n  using options = tmpl::list<>;\n\n  using argument_tags =\n      tmpl::list<::Tags::HistoryEvolvedVariables<VariablesTag>>;\n\n  TimeStepRequest operator()(\n      const ::TimeSteppers::History<typename VariablesTag::type>& history,\n      const double last_step) const {\n    if (history.size() < 2) {\n      return {};\n    }\n\n    const double sloppiness =\n        slab_rounding_error(history.front().time_step_id.step_time());\n    std::optional<Time> previous_time{};\n    double newer_step = abs(last_step);\n    for (auto record = history.rbegin(); record != history.rend(); ++record) {\n      const Time time = record->time_step_id.step_time();\n      if (previous_time.has_value()) {\n        const double this_step = abs(*previous_time - time).value();\n        // Potential roundoff error comes from the inability to make\n        // slabs exactly the same length.\n        if (this_step < newer_step - sloppiness) {\n          return {.size = last_step};\n        }\n        newer_step = this_step;\n      }\n      previous_time.emplace(time);\n    }\n    return {};\n  }\n\n  bool uses_local_data() const override { return false; }\n  bool can_be_delayed() const override { return true; }\n\n  void pup(PUP::er& p) override {\n    StepChooser<StepChooserUse::Slab>::pup(p);\n    StepChooser<StepChooserUse::LtsStep>::pup(p);\n  }\n};\n\n/// \\cond\ntemplate <typename VariablesTag>\nPUP::able::PUP_ID PreventRapidIncrease<VariablesTag>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/Random.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepChoosers/Random.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n#include <random>\n\n#include \"Domain/Structure/Element.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace StepChoosers {\n\ntemplate <size_t VolumeDim>\nRandom<VolumeDim>::Random() = default;\n\ntemplate <size_t VolumeDim>\nRandom<VolumeDim>::Random(CkMigrateMessage* /*unused*/) {}\n\ntemplate <size_t VolumeDim>\nRandom<VolumeDim>::Random(const double minimum, const double maximum,\n                          const size_t seed, const Options::Context& context)\n    : minimum_(minimum), maximum_(maximum), seed_(seed) {\n  if (minimum_ >= maximum_) {\n    PARSE_ERROR(context, \"Must have Minimum < Maximum\");\n  }\n}\n\ntemplate <size_t VolumeDim>\nTimeStepRequest Random<VolumeDim>::operator()(const Element<VolumeDim>& element,\n                                              const TimeStepId& time_step_id,\n                                              const double last_step) const {\n  size_t local_seed = seed_;\n  boost::hash_combine(local_seed, element.id());\n  boost::hash_combine(local_seed, time_step_id);\n  std::mt19937_64 rng(local_seed);\n  std::uniform_real_distribution<> dist(log(minimum_), log(maximum_));\n  for (;;) {\n    const double step = exp(dist(rng));\n    // Don't produce out-of-range values because of roundoff.\n    if (step >= minimum_ and step <= maximum_) {\n      return {.size_goal = std::copysign(step, last_step)};\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\nbool Random<VolumeDim>::uses_local_data() const {\n  return true;\n}\n\ntemplate <size_t VolumeDim>\nbool Random<VolumeDim>::can_be_delayed() const {\n  return true;\n}\n\ntemplate <size_t VolumeDim>\nvoid Random<VolumeDim>::pup(PUP::er& p) {\n  StepChooser<StepChooserUse::Slab>::pup(p);\n  StepChooser<StepChooserUse::LtsStep>::pup(p);\n  p | minimum_;\n  p | maximum_;\n  p | seed_;\n}\n\ntemplate <size_t VolumeDim>\nPUP::able::PUP_ID Random<VolumeDim>::my_PUP_ID = 0;  // NOLINT\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data) template class Random<DIM(data)>;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n#undef DIM\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/Random.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Element;\nclass TimeStepId;\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\nnamespace domain::Tags {\ntemplate <size_t VolumeDim>\nstruct Element;\n}  // namespace domain::Tags\n/// \\endcond\n\nnamespace StepChoosers {\n/// Changes the step size pseudo-randomly.  Values are distributed\n/// uniformly in $\\log(dt)$.  The current step is always accepted.\n///\n/// \\note This debugging StepChooser is not included in the\n/// `standard_step_choosers` list, but can be added to the\n/// `factory_creation` struct in the metavariables.\ntemplate <size_t VolumeDim>\nclass Random : public StepChooser<StepChooserUse::Slab>,\n               public StepChooser<StepChooserUse::LtsStep> {\n public:\n  /// \\cond\n  Random();\n  explicit Random(CkMigrateMessage* /*unused*/);\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Random);  // NOLINT\n  /// \\endcond\n\n  struct Minimum {\n    using type = double;\n    static constexpr Options::String help{\"Minimum value to suggest\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Maximum {\n    using type = double;\n    static constexpr Options::String help{\"Maximum value to suggest\"};\n    static type lower_bound() { return 0.0; }\n  };\n\n  struct Seed {\n    using type = size_t;\n    static constexpr Options::String help{\"RNG seed\"};\n  };\n\n  static constexpr Options::String help =\n      \"Changes the step size pseudo-randomly.\";\n  using options = tmpl::list<Minimum, Maximum, Seed>;\n\n  Random(double minimum, double maximum, size_t seed,\n         const Options::Context& context = {});\n\n  using argument_tags =\n      tmpl::list<domain::Tags::Element<VolumeDim>, Tags::TimeStepId>;\n\n  TimeStepRequest operator()(const Element<VolumeDim>& element,\n                             const TimeStepId& time_step_id,\n                             double last_step) const;\n\n  bool uses_local_data() const override;\n  bool can_be_delayed() const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  double minimum_ = std::numeric_limits<double>::signaling_NaN();\n  double maximum_ = std::numeric_limits<double>::signaling_NaN();\n  size_t seed_ = 0;\n};\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/StepChooser.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n\n/// The intended use for a step chooser.  This is used to control the\n/// classes via factories.\nnamespace StepChooserUse {\nstruct Slab;\nstruct LtsStep;\n}  // namespace StepChooserUse\n\n/// \\cond\ntemplate <typename StepChooserUse>\nclass StepChooser;\n/// \\endcond\n\n/// \\ingroup TimeGroup\n///\n/// Holds all the StepChoosers\nnamespace StepChoosers {\n\nnamespace detail {\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(compute_tags)\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(simple_tags)\n\ntemplate <typename Metavariables, bool UsingLts>\nusing all_step_choosers = tmpl::join<tmpl::remove<\n    tmpl::list<\n        tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                 StepChooser<StepChooserUse::Slab>>,\n        tmpl::conditional_t<\n            UsingLts,\n            tmpl::at<typename Metavariables::factory_creation::factory_classes,\n                     StepChooser<StepChooserUse::LtsStep>>,\n            tmpl::no_such_type_>>,\n    tmpl::no_such_type_>>;\n}  // namespace detail\n\ntemplate <typename Metavariables, bool UsingLts>\nusing step_chooser_compute_tags = tmpl::remove_duplicates<tmpl::join<\n    tmpl::transform<detail::all_step_choosers<Metavariables, UsingLts>,\n                    detail::get_compute_tags_or_default<\n                        tmpl::_1, tmpl::pin<tmpl::list<>>>>>>;\n\ntemplate <typename Metavariables, bool UsingLts>\nusing step_chooser_simple_tags = tmpl::remove_duplicates<tmpl::join<\n    tmpl::transform<detail::all_step_choosers<Metavariables, UsingLts>,\n                    detail::get_simple_tags_or_default<\n                        tmpl::_1, tmpl::pin<tmpl::list<>>>>>>;\n}  // namespace StepChoosers\n\n/// A placeholder type to indicate that all constructible step choosers should\n/// be used in step chooser utilities that permit a list of choosers to be\n/// specified.\nstruct AllStepChoosers {};\n\n/// \\ingroup TimeGroup\n///\n/// \\brief StepChoosers suggest upper bounds on step sizes.  See\n/// `TimeStepRequest` for details on how the results are used.\n///\n/// Concrete StepChoosers should define `operator()` returning the\n/// desired step and taking the `last_step` and arguments specified by\n/// the class's `argument_tags` type alias.\n///\n/// Derived classes must indicate whether the chooser is usable as a\n/// step chooser, slab chooser, or both by inheriting from StepChooser\n/// with the appropriate `StepChooserUse` template argument.\ntemplate <typename StepChooserUse>\nclass StepChooser : public virtual PUP::able {\n protected:\n  /// \\cond HIDDEN_SYMBOLS\n  StepChooser() = default;\n  StepChooser(const StepChooser&) = default;\n  StepChooser(StepChooser&&) = default;\n  StepChooser& operator=(const StepChooser&) = default;\n  StepChooser& operator=(StepChooser&&) = default;\n  /// \\endcond\n\n public:\n  ~StepChooser() override = default;\n\n  WRAPPED_PUPable_abstract(StepChooser);  // NOLINT\n\n  /// Whether the result can differ on different elements, so\n  /// requiring communication to synchronize the result across the\n  /// domain.  This is ignored for LTS step changing.\n  ///\n  /// \\note As this is only used for slab-size changing, the\n  /// `last_step` passed to the call operator is *not* considered\n  /// local data.\n  virtual bool uses_local_data() const = 0;\n\n  /// Whether the result can be applied with a delay.\n  ///\n  /// StepChoosers setting the `.end` or `.end_hard_limit` fields of\n  /// `TimeStepRequest` must return false here.\n  virtual bool can_be_delayed() const = 0;\n\n  /// The `last_step` parameter describes the step size to be\n  /// adjusted.  It may be the step size or the slab size, or may be\n  /// infinite if the appropriate size cannot be determined.\n  ///\n  /// The optional template parameter `StepChoosersToUse` may be used to\n  /// indicate a subset of the constructable step choosers to use for the\n  /// current application of `ChangeStepSize`. Passing `AllStepChoosers`\n  /// (default) indicates that any constructible step chooser may be used. This\n  /// option is used when multiple components need to invoke `ChangeStepSize`\n  /// with step choosers that may not be compatible with all components.\n  template <typename StepChoosersToUse = AllStepChoosers, typename DbTags>\n  TimeStepRequest desired_step(const double last_step,\n                               const db::DataBox<DbTags>& box) const {\n    using factory_classes =\n        typename std::decay_t<decltype(db::get<Parallel::Tags::Metavariables>(\n            box))>::factory_creation::factory_classes;\n    using step_choosers =\n        tmpl::conditional_t<std::is_same_v<StepChoosersToUse, AllStepChoosers>,\n                            tmpl::at<factory_classes, StepChooser>,\n                            StepChoosersToUse>;\n    const auto result = call_with_dynamic_type<TimeStepRequest, step_choosers>(\n        this, [&last_step, &box](const auto* const chooser) {\n          return db::apply(*chooser, box, last_step);\n        });\n    ASSERT(not result.size_goal.has_value() or\n               (*result.size_goal > 0.) == (last_step > 0.),\n           \"Step size changed sign: \" << *result.size_goal);\n    ASSERT(\n        not result.size.has_value() or (*result.size > 0.) == (last_step > 0.),\n        \"Step size changed sign: \" << *result.size);\n    ASSERT(not result.size_hard_limit.has_value() or\n               (*result.size_hard_limit > 0.) == (last_step > 0.),\n           \"Step size changed sign: \" << *result.size_hard_limit);\n    ASSERT(not(result.end.has_value() and can_be_delayed()),\n           \"Delayable end limits are not allowed.\");\n    ASSERT(not(result.end_hard_limit.has_value() and can_be_delayed()),\n           \"Delayable end limits are not allowed.\");\n    return result;\n  }\n};\n"
  },
  {
    "path": "src/Time/StepChoosers/StepToTimes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepChoosers/StepToTimes.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace StepChoosers {\nStepToTimes::StepToTimes(std::unique_ptr<TimeSequence<double>> times)\n    : times_(std::move(times)) {}\n\nTimeStepRequest StepToTimes::operator()(const double now,\n                                        const double last_step) const {\n  const auto goal_times = times_->times_near(now);\n  if (not goal_times[1].has_value()) {\n    // No times requested.\n    return {};\n  }\n\n  const evolution_greater<double> after{last_step > 0.0};\n  const auto next_time = after(*goal_times[1], now)\n                             ? goal_times[1]\n                             : gsl::at(goal_times, last_step > 0.0 ? 2 : 0);\n  if (not next_time.has_value()) {\n    // We've passed all the times.  No restriction.\n    return {};\n  }\n\n  // The calling code can ignore one part of the request if it can\n  // fulfill the other part exactly, so this will either step\n  // exactly to *next_time or step at most 2/3 of the way there.\n  // This attempts to balance out the steps to avoid a step almost\n  // to *next_time followed by a very small step.  This will work\n  // poorly if there are two copies of this StepChooser targeting\n  // times very close together, but hopefully people don't do that.\n  return {.size = 2.0 / 3.0 * (*next_time - now),\n          .end = *next_time,\n          .end_hard_limit = *next_time};\n}\n\nbool StepToTimes::uses_local_data() const { return false; }\nbool StepToTimes::can_be_delayed() const { return false; }\n\nvoid StepToTimes::pup(PUP::er& p) {\n  StepChooser<StepChooserUse::Slab>::pup(p);\n  p | times_;\n}\n\nPUP::able::PUP_ID StepToTimes::my_PUP_ID = 0;  // NOLINT\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepChoosers/StepToTimes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\n#include \"Options/String.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace StepChoosers {\n/// Suggests step sizes to place steps at specific times.\n///\n/// The suggestion provided depends on the current time, so it should\n/// be applied immediately, rather than delayed several slabs.\nclass StepToTimes : public StepChooser<StepChooserUse::Slab> {\n public:\n  /// \\cond\n  StepToTimes() = default;\n  explicit StepToTimes(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(StepToTimes);  // NOLINT\n  /// \\endcond\n\n  struct Times {\n    using type = std::unique_ptr<TimeSequence<double>>;\n    static constexpr Options::String help{\"Times to force steps at\"};\n  };\n\n  static constexpr Options::String help =\n      \"Suggests step sizes to place steps at specific times.\\n\"\n      \"\\n\"\n      \"The suggestion provided depends on the current time, so it should\\n\"\n      \"be applied immediately, rather than delayed several slabs.\";\n  using options = tmpl::list<Times>;\n\n  explicit StepToTimes(std::unique_ptr<TimeSequence<double>> times);\n\n  using argument_tags = tmpl::list<::Tags::Time>;\n\n  TimeStepRequest operator()(double now, double last_step) const;\n\n  bool uses_local_data() const override;\n  bool can_be_delayed() const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::unique_ptr<TimeSequence<double>> times_;\n};\n}  // namespace StepChoosers\n"
  },
  {
    "path": "src/Time/StepperErrorEstimate.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepperErrorEstimate.hpp\"\n\n#include <pup.h>\n#include <pup_stl.h>\n\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nStepperErrorEstimate::StepperErrorEstimate(const Time step_time_in,\n                                           const TimeDelta step_size_in,\n                                           const size_t order_in,\n                                           const double step_error_in)\n    : step_time(step_time_in), step_size(step_size_in), order(order_in) {\n  gsl::at(errors, order).emplace(step_error_in);\n}\n\ndouble StepperErrorEstimate::step_error() const {\n  return gsl::at(errors, order).value();\n}\n\nvoid StepperErrorEstimate::pup(PUP::er& p) {\n  p | step_time;\n  p | step_size;\n  p | order;\n  p | errors;\n}\n"
  },
  {
    "path": "src/Time/StepperErrorEstimate.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"Time/Time.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// Estimate of the TimeStepper truncation error.\nstruct StepperErrorEstimate {\n  /// Start of the step the estimate is for.\n  Time step_time{};\n  /// Size of the step the estimate is for.\n  TimeDelta step_size{};\n  /// Order of accuracy of the primary estimate.\n  size_t order{};\n  /// Error estimates.  The entry `errors[order]`, with `order` the\n  /// previous member of this struct, is the estimated error of the\n  /// time step, and has a convenience accessor `step_error()`.  Other\n  /// entries are estimations of the error if the order of a\n  /// variable-order stepper were to be changed.  The error\n  /// `errors[i]` should scale approximately as $(\\Delta t)^{i + 1}$.\n  std::array<std::optional<double>, 8> errors{};\n\n  StepperErrorEstimate() = default;\n  StepperErrorEstimate(Time step_time_in, TimeDelta step_size_in,\n                       size_t order_in, double step_error_in);\n\n  /// Convenience accessor for `errors[order].value()`.\n  double step_error() const;\n\n  void pup(PUP::er& p);\n};\n"
  },
  {
    "path": "src/Time/StepperErrorTolerances.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/StepperErrorTolerances.hpp\"\n\n#include <pup.h>\n#include <pup_stl.h>  // defines pup for enum\n\nvoid StepperErrorTolerances::pup(PUP::er& p) {\n  p | estimates;\n  p | absolute;\n  p | relative;\n}\n\nbool operator==(const StepperErrorTolerances& a,\n                const StepperErrorTolerances& b) {\n  return (a.estimates == StepperErrorTolerances::Estimates::None and\n          b.estimates == StepperErrorTolerances::Estimates::None) or\n         (a.estimates == b.estimates and a.absolute == b.absolute and\n          a.relative == b.relative);\n}\n\nbool operator!=(const StepperErrorTolerances& a,\n                const StepperErrorTolerances& b) {\n  return not(a == b);\n}\n"
  },
  {
    "path": "src/Time/StepperErrorTolerances.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// Tolerances used for time step error control\nstruct StepperErrorTolerances {\n  enum class Estimates { None, StepperOrder, AllOrders };\n\n  /// Which estimates the time stepper should generate.\n  Estimates estimates = Estimates::None;\n  double absolute = std::numeric_limits<double>::signaling_NaN();\n  double relative = std::numeric_limits<double>::signaling_NaN();\n\n  void pup(PUP::er& p);\n};\n\nbool operator==(const StepperErrorTolerances& a,\n                const StepperErrorTolerances& b);\nbool operator!=(const StepperErrorTolerances& a,\n                const StepperErrorTolerances& b);\n"
  },
  {
    "path": "src/Time/Tags/AdaptiveSteppingDiagnostics.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n\nnamespace Tags {\nstruct AdaptiveSteppingDiagnostics : db::SimpleTag {\n  using type = ::AdaptiveSteppingDiagnostics;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  StepperErrorTolerancesCompute.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AdaptiveSteppingDiagnostics.hpp\n  FixedLtsRatio.hpp\n  HistoryEvolvedVariables.hpp\n  LtsStepChoosers.hpp\n  MinimumTimeStep.hpp\n  StepNumberWithinSlab.hpp\n  StepperErrorEstimatesEnabled.hpp\n  StepperErrorTolerances.hpp\n  StepperErrorTolerancesCompute.hpp\n  StepperErrors.hpp\n  Time.hpp\n  TimeAndPrevious.hpp\n  TimeStep.hpp\n  TimeStepId.hpp\n  TimeStepper.hpp\n  VariableOrderAlgorithm.hpp\n  )\n"
  },
  {
    "path": "src/Time/Tags/FixedLtsRatio.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace Tags {\n/// \\ingroup TimeGroup\n/// \\brief Tag forcing a constant step size over a region in an LTS evolution.\nstruct FixedLtsRatio : db::SimpleTag {\n  using type = std::optional<size_t>;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/HistoryEvolvedVariables.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/History.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// Tag for the TimeStepper history\n///\n/// \\tparam Tag tag for the variables\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables : db::SimpleTag {\n  using type = TimeSteppers::History<typename Tag::type>;\n};\n\n/// \\ingroup TimeGroup\n/// From a list of tags `TagList`, extract all tags that are template\n/// specializations of `HistoryEvolvedVariables`.\ntemplate <typename TagList>\nusing get_all_history_tags =\n    tmpl::filter<TagList, tt::is_a<::Tags::HistoryEvolvedVariables, tmpl::_1>>;\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/LtsStepChoosers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup_stl.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/OptionTags/LtsStepChoosers.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Tag for a vector of ::StepChooser%s\nstruct LtsStepChoosers : db::SimpleTag {\n  using type =\n      std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>;\n  using option_tags = tmpl::list<::OptionTags::LtsStepChoosers>;\n  static constexpr bool is_overlayable = true;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& step_choosers) {\n    return serialize_and_deserialize<type>(step_choosers);\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/MinimumTimeStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/OptionTags/MinimumTimeStep.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief The minimum step size without triggering an error\nstruct MinimumTimeStep : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<::OptionTags::MinimumTimeStep>;\n  static constexpr bool is_overlayable = true;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type option) { return option; }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/StepNumberWithinSlab.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Number of time step taken within a Slab\nstruct StepNumberWithinSlab : db::SimpleTag {\n  using type = uint64_t;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/StepperErrorEstimatesEnabled.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n\nnamespace Tags {\n/// \\ingroup TimeGroup\n/// \\brief A tag that is true if the time stepper should be run in\n/// error estimation mode.\n///\n/// \\details Estimates will not actually be produced for a variable\n/// unless `StepperErrorTolerances` provides tolerances, but in\n/// split-variable systems some time steppers require extra steps if\n/// any variable requires error estimates.\nstruct StepperErrorEstimatesEnabled : db::SimpleTag {\n  using type = bool;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/StepperErrorTolerances.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Tag for the stepper error tolerances.\ntemplate <typename Tag>\nstruct StepperErrorTolerances : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    return \"StepperErrorTolerances(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = ::StepperErrorTolerances;\n  using tag = Tag;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/StepperErrorTolerancesCompute.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Tags/StepperErrorTolerancesCompute.hpp\"\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <typeindex>\n#include <typeinfo>\n#include <vector>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"Time/ChangeSlabSize/Event.hpp\"\n#include \"Time/RequestsStepperErrorTolerances.hpp\"\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/VariableOrderAlgorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Tags {\nnamespace {\ntemplate <typename StepChooserUse>\nbool requests_any_tolerances(const StepChooser<StepChooserUse>& step_chooser) {\n  const auto* const tolerance_request =\n      dynamic_cast<const RequestsStepperErrorTolerances*>(&step_chooser);\n  return tolerance_request != nullptr and\n         not tolerance_request->tolerances().empty();\n}\n}  // namespace\n\nnamespace StepperErrorEstimatesEnabledCompute_detail {\nvoid lts_function(\n    const gsl::not_null<bool*> error_estimates_enabled,\n    const ::EventsAndTriggers& events_and_triggers,\n    const std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>&\n        step_choosers) {\n  *error_estimates_enabled = false;\n\n  events_and_triggers.for_each_event([&](const auto& event) {\n    if (*error_estimates_enabled) {\n      return;\n    }\n    if (const auto* const change_slab_size =\n            dynamic_cast<const ::Events::ChangeSlabSize*>(&event)) {\n      change_slab_size->for_each_step_chooser(\n          [&](const StepChooser<StepChooserUse::Slab>& step_chooser) {\n            if (*error_estimates_enabled) {\n              return;\n            }\n            if (const auto* const fixed_ratio =\n                    dynamic_cast<const ::StepChoosers::FixedLtsRatio*>(\n                        &step_chooser);\n                fixed_ratio != nullptr) {\n              fixed_ratio->for_each_step_chooser(\n                  [&](const StepChooser<StepChooserUse::LtsStep>&\n                          sub_step_chooser) {\n                    if (*error_estimates_enabled) {\n                      return;\n                    }\n                    if (requests_any_tolerances(sub_step_chooser)) {\n                      *error_estimates_enabled = true;\n                    }\n                  });\n            }\n          });\n    }\n  });\n\n  for (const auto& step_chooser : step_choosers) {\n    if (requests_any_tolerances(*step_chooser)) {\n      *error_estimates_enabled = true;\n      return;\n    }\n  }\n}\n\nvoid gts_function(const gsl::not_null<bool*> error_estimates_enabled,\n                  const ::EventsAndTriggers& events_and_triggers) {\n  // In principle the slab size could be changed based on a dense\n  // trigger, but it's not clear that there is ever a good reason to\n  // do so, and it wouldn't make sense to use error control in that\n  // context in any case.\n  *error_estimates_enabled = false;\n  events_and_triggers.for_each_event([&](const auto& event) {\n    if (*error_estimates_enabled) {\n      return;\n    }\n    if (const auto* const change_slab_size =\n            dynamic_cast<const ::Events::ChangeSlabSize*>(&event)) {\n      change_slab_size->for_each_step_chooser(\n          [&](const StepChooser<StepChooserUse::Slab>& step_chooser) {\n            if (*error_estimates_enabled) {\n              return;\n            }\n            if (requests_any_tolerances(step_chooser)) {\n              *error_estimates_enabled = true;\n            }\n          });\n    }\n  });\n}\n}  // namespace StepperErrorEstimatesEnabledCompute_detail\n\nnamespace StepperErrorTolerancesCompute_detail {\nnamespace {\ntemplate <typename StepChooserUse>\nvoid set_tolerances_if_requested(\n    const gsl::not_null<::StepperErrorTolerances*> tolerances,\n    const StepChooser<StepChooserUse>& step_chooser,\n    const std::type_index& tag_type, const std::string& tag_name) {\n  if (const auto* const tolerance_request =\n          dynamic_cast<const RequestsStepperErrorTolerances*>(&step_chooser);\n      tolerance_request != nullptr) {\n    const auto tolerances_map = tolerance_request->tolerances();\n    if (const auto this_tolerances = tolerances_map.find(tag_type);\n        this_tolerances != tolerances_map.end()) {\n      if (tolerances->estimates != ::StepperErrorTolerances::Estimates::None and\n          *tolerances != this_tolerances->second) {\n        ERROR_NO_TRACE(\"All ErrorControl events for \"\n                       << tag_name << \" must use the same tolerances.\");\n      }\n      *tolerances = this_tolerances->second;\n    }\n  }\n}\n}  // namespace\n\nvoid lts_impl(\n    const gsl::not_null<::StepperErrorTolerances*> tolerances,\n    const ::EventsAndTriggers& events_and_triggers,\n    const std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>&\n        step_choosers,\n    const ::TimeStepper& time_stepper,\n    const ::VariableOrderAlgorithm& variable_order_algorithm,\n    const std::type_index& tag_type, const std::string& tag_name) {\n  *tolerances = ::StepperErrorTolerances{};\n\n  events_and_triggers.for_each_event([&](const auto& event) {\n    if (const auto* const change_slab_size =\n            dynamic_cast<const ::Events::ChangeSlabSize*>(&event)) {\n      change_slab_size->for_each_step_chooser(\n          [&](const StepChooser<StepChooserUse::Slab>& step_chooser) {\n            if (const auto* const fixed_ratio =\n                    dynamic_cast<const ::StepChoosers::FixedLtsRatio*>(\n                        &step_chooser);\n                fixed_ratio != nullptr) {\n              fixed_ratio->for_each_step_chooser(\n                  [&](const StepChooser<StepChooserUse::LtsStep>&\n                          sub_step_chooser) {\n                    set_tolerances_if_requested(tolerances, sub_step_chooser,\n                                                tag_type, tag_name);\n                  });\n            }\n          });\n    }\n  });\n\n  for (const auto& step_chooser : step_choosers) {\n    set_tolerances_if_requested(tolerances, *step_chooser, tag_type, tag_name);\n  }\n\n  // Error-based variable-order requires some variable to be\n  // controlled using error control, but in a split-variable system\n  // a different variable might be controlled, so no control on this\n  // variable is not an error.\n  if (tolerances->estimates != ::StepperErrorTolerances::Estimates::None and\n      variants::holds_alternative<TimeSteppers::Tags::VariableOrder>(\n          time_stepper.order())) {\n    tolerances->estimates = std::max(\n        tolerances->estimates, variable_order_algorithm.required_estimates());\n  }\n}\n\nvoid gts_impl(const gsl::not_null<::StepperErrorTolerances*> tolerances,\n              const ::EventsAndTriggers& events_and_triggers,\n              const std::type_index& tag_type, const std::string& tag_name) {\n  *tolerances = ::StepperErrorTolerances{};\n  // In principle the slab size could be changed based on a dense\n  // trigger, but it's not clear that there is ever a good reason to\n  // do so, and it wouldn't make sense to use error control in that\n  // context in any case.\n  events_and_triggers.for_each_event([&](const auto& event) {\n    if (const auto* const change_slab_size =\n            dynamic_cast<const ::Events::ChangeSlabSize*>(&event)) {\n      change_slab_size->for_each_step_chooser(\n          [&](const StepChooser<StepChooserUse::Slab>& step_chooser) {\n            set_tolerances_if_requested(tolerances, step_chooser, tag_type,\n                                        tag_name);\n          });\n    }\n  });\n}\n}  // namespace StepperErrorTolerancesCompute_detail\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/StepperErrorTolerancesCompute.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <typeindex>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/Tags/StepperErrorTolerances.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass EventsAndTriggers;\ntemplate <typename StepChooserUse>\nclass StepChooser;\nstruct StepperErrorTolerances;\nclass TimeStepper;\nclass VariableOrderAlgorithm;\nnamespace StepChooserUse {\nstruct LtsStep;\n}  // namespace StepChooserUse\nnamespace Tags {\ntemplate <Triggers::WhenToCheck WhenToCheck>\nstruct EventsAndTriggers;\nstruct LtsStepChoosers;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\nstruct VariableOrderAlgorithm;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Tags {\nnamespace StepperErrorEstimatesEnabledCompute_detail {\nvoid lts_function(\n    gsl::not_null<bool*> error_estimates_enabled,\n    const ::EventsAndTriggers& events_and_triggers,\n    const std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>&\n        step_choosers);\n\nvoid gts_function(gsl::not_null<bool*> error_estimates_enabled,\n                  const ::EventsAndTriggers& events_and_triggers);\n}  // namespace StepperErrorEstimatesEnabledCompute_detail\n\n/// \\ingroup TimeGroup\n/// \\brief Searches the StepChoosers for any requesting error estimates.\ntemplate <bool LocalTimeStepping,\n          template <typename> typename CacheTagPrefix = std::type_identity_t>\nstruct StepperErrorEstimatesEnabledCompute : db::ComputeTag,\n                                             StepperErrorEstimatesEnabled {\n  using base = StepperErrorEstimatesEnabled;\n  using return_type = type;\n  using argument_tags = tmpl::conditional_t<\n      LocalTimeStepping,\n      tmpl::list<::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n                 CacheTagPrefix<::Tags::LtsStepChoosers>>,\n      tmpl::list<::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>>;\n\n  static constexpr auto function = []() {\n    if constexpr (LocalTimeStepping) {\n      return &StepperErrorEstimatesEnabledCompute_detail::lts_function;\n    } else {\n      return &StepperErrorEstimatesEnabledCompute_detail::gts_function;\n    }\n  }();\n};\n\nnamespace StepperErrorTolerancesCompute_detail {\nvoid lts_impl(\n    gsl::not_null<::StepperErrorTolerances*> tolerances,\n    const ::EventsAndTriggers& events_and_triggers,\n    const std::vector<std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>&\n        step_choosers,\n    const ::TimeStepper& time_stepper,\n    const ::VariableOrderAlgorithm& variable_order_algorithm,\n    const std::type_index& tag_type, const std::string& tag_name);\n\nvoid gts_impl(gsl::not_null<::StepperErrorTolerances*> tolerances,\n              const ::EventsAndTriggers& events_and_triggers,\n              const std::type_index& tag_type, const std::string& tag_name);\n}  // namespace StepperErrorTolerancesCompute_detail\n\n/// \\ingroup TimeGroup\n/// \\brief A tag that contains the error tolerances if any StepChooser\n/// requests an error estimate for the variable.\ntemplate <typename EvolvedVariableTag, bool LocalTimeStepping,\n          template <typename> typename CacheTagPrefix = std::type_identity_t>\nstruct StepperErrorTolerancesCompute\n    : db::ComputeTag,\n      StepperErrorTolerances<EvolvedVariableTag> {\n  using base = StepperErrorTolerances<EvolvedVariableTag>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::conditional_t<\n      LocalTimeStepping,\n      tmpl::list<::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n                 CacheTagPrefix<::Tags::LtsStepChoosers>,\n                 CacheTagPrefix<::Tags::TimeStepper<::TimeStepper>>,\n                 CacheTagPrefix<::Tags::VariableOrderAlgorithm>>,\n      tmpl::list<::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>>;\n\n  // local time stepping\n  static void function(\n      const gsl::not_null<::StepperErrorTolerances*> tolerances,\n      const ::EventsAndTriggers& events_and_triggers,\n      const std::vector<\n          std::unique_ptr<::StepChooser<StepChooserUse::LtsStep>>>&\n          step_choosers,\n      const ::TimeStepper& time_stepper,\n      const ::VariableOrderAlgorithm& variable_order_algorithm) {\n    StepperErrorTolerancesCompute_detail::lts_impl(\n        tolerances, events_and_triggers, step_choosers, time_stepper,\n        variable_order_algorithm, typeid(EvolvedVariableTag),\n        db::tag_name<EvolvedVariableTag>());\n  }\n\n  // global time stepping\n  static void function(\n      const gsl::not_null<::StepperErrorTolerances*> tolerances,\n      const ::EventsAndTriggers& events_and_triggers) {\n    StepperErrorTolerancesCompute_detail::gts_impl(\n        tolerances, events_and_triggers, typeid(EvolvedVariableTag),\n        db::tag_name<EvolvedVariableTag>());\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/StepperErrors.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Tag for the stepper error measures.\ntemplate <typename Tag>\nstruct StepperErrors : db::PrefixTag, db::SimpleTag {\n  static std::string name() {\n    return \"StepperErrors(\" + db::tag_name<Tag>() + \")\";\n  }\n  using type = std::array<std::optional<StepperErrorEstimate>, 2>;\n  using tag = Tag;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/Time.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/OptionTags/InitialTime.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Tag for the current time as a double\n///\n/// The meaning of \"current time\" varies during the algorithm, but\n/// generally is whatever time is appropriate for the calculation\n/// being run.  Usually this is the substep time, but things such as\n/// dense-output calculations may temporarily change the value.\nstruct Time : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTags::InitialTime>;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double initial_time) {\n    return initial_time;\n  }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/TimeAndPrevious.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\nstruct PreviousTriggerTime;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Tags {\n\n/// @{\n/// \\ingroup TimeGroup\n/// \\brief Tag for the current and previous time as doubles\n///\n/// \\warning The previous time is calculated via the value of the\n/// ::Tags::PreviousTriggerTime. Therefore, this tag can only be\n/// used in the context of dense triggers as that is where the\n/// ::Tags::PreviousTriggerTime tag is set. Any Events that request\n/// this tag in their `argument_tags` type alias, must be triggered by a\n/// DenseTrigger.\n///\n/// \\note The Index is just so we can have multiple of this tag in the same\n/// DataBox.\ntemplate <size_t Index>\nstruct TimeAndPrevious : db::SimpleTag {\n  static constexpr size_t index = Index;\n  using type = LinkedMessageId<double>;\n  static std::string name() { return \"TimeAndPrevious\" + get_output(Index); }\n};\n\ntemplate <size_t Index>\nstruct TimeAndPreviousCompute : TimeAndPrevious<Index>, db::ComputeTag {\n  using argument_tags = tmpl::list<::Tags::Time, ::Tags::PreviousTriggerTime>;\n  using base = TimeAndPrevious<Index>;\n  using return_type = LinkedMessageId<double>;\n\n  static void function(\n      gsl::not_null<LinkedMessageId<double>*> time_and_previous,\n      const double time, const std::optional<double>& previous_time) {\n    time_and_previous->id = time;\n    time_and_previous->previous = previous_time;\n  }\n};\n/// @}\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/TimeStep.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/Time.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Tag for step size\nstruct TimeStep : db::SimpleTag {\n  using type = ::TimeDelta;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/TimeStepId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Tag for ::TimeStepId for the algorithm state\nstruct TimeStepId : db::SimpleTag {\n  using type = ::TimeStepId;\n  template <typename Tag>\n  using step_prefix = typename Tags::dt<Tag>;\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Tags/TimeStepper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Time/OptionTags/TimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass LtsTimeStepper;\n/// \\endcond\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// The evolution TimeStepper.  The template parameter should be one\n/// of the time stepper base classes, such as `TimeStepper` or\n/// `LtsTimeStepper`.\n///\n/// For the contained object to be used, the reference tags listed in\n/// `time_stepper_ref_tags<StepperType>` will also need to be added to\n/// the DataBox.\ntemplate <typename StepperType>\nstruct ConcreteTimeStepper : db::SimpleTag {\n  using type = std::unique_ptr<StepperType>;\n  using option_tags = tmpl::list<::OptionTags::TimeStepper<StepperType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::unique_ptr<StepperType> create_from_options(\n      const std::unique_ptr<StepperType>& time_stepper) {\n    if (not std::is_same_v<StepperType, LtsTimeStepper> and\n        variants::holds_alternative<TimeSteppers::Tags::VariableOrder>(\n            time_stepper->order())) {\n      ERROR_NO_TRACE(\n          \"Variable-order TimeSteppers are only supported in evolutions with \"\n          \"local time-stepping.\");\n    }\n    return serialize_and_deserialize<type>(time_stepper);\n  }\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// Access to a time stepper through the `StepperInterface` interface\n/// (such as `TimeStepper` or `LtsTimeStepper`).\n///\n/// \\details This tag cannot be added directly to the DataBox of\n/// GlobalCache because it contains an abstract type, but can only be\n/// used for retrieving the time stepper.  Instead, the\n/// `ConcreteTimeStepper` tag should be added, along with the\n/// reference tags given by `time_stepper_ref_tags`.\ntemplate <typename StepperInterface>\nstruct TimeStepper : db::SimpleTag {\n  using type = StepperInterface;\n};\n\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// Reference tag to provide access to the time stepper through its\n/// provided interfaces, such as `Tags::TimeStepper<TimeStepper>` and\n/// `Tags::TimeStepper<LtsTimeStepper>`.  Usually added through the\n/// `time_stepper_ref_tags` alias.\ntemplate <typename StepperInterface, typename StepperType>\nstruct TimeStepperRef : TimeStepper<StepperInterface>, db::ReferenceTag {\n  using base = TimeStepper<StepperInterface>;\n  using argument_tags = tmpl::list<ConcreteTimeStepper<StepperType>>;\n  static const StepperInterface& get(const StepperType& stepper) {\n    return stepper;\n  }\n};\n}  // namespace Tags\n\n/// \\ingroup TimeGroup\n/// List of Tags::TimeStepperRef specializations needed when adding a\n/// Tags::ConcreteTimeStepper.\ntemplate <typename StepperType>\nusing time_stepper_ref_tags = tmpl::transform<\n    typename StepperType::provided_time_stepper_interfaces,\n    tmpl::bind<::Tags::TimeStepperRef, tmpl::_1, tmpl::pin<StepperType>>>;\n"
  },
  {
    "path": "src/Time/Tags/VariableOrderAlgorithm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/OptionTags/VariableOrderAlgorithm.hpp\"\n#include \"Time/VariableOrderAlgorithm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\n/// \\ingroup DataBoxTagsGroup\n/// \\ingroup TimeGroup\n/// \\brief Algorithm for changing the time-stepper order in a\n/// variable-order evolution.\n/// \\see ChangeTimeStepperOrder\nstruct VariableOrderAlgorithm : db::SimpleTag {\n  using type = ::VariableOrderAlgorithm;\n  using option_tags = tmpl::list<::OptionTags::VariableOrderAlgorithm>;\n  static constexpr bool is_overlayable = true;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n}  // namespace Tags\n"
  },
  {
    "path": "src/Time/Time.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Time.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <ostream>\n#include <pup.h>\n#include <utility>\n\n#include \"Time/Slab.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n\n// Time implementations\n\nTime::Time(Slab slab, rational_t fraction)\n    // clang-tidy: move trivially copyable type\n    : slab_(std::move(slab)), fraction_(std::move(fraction)) {  // NOLINT\n  range_check();\n  compute_value();\n}\n\nTime Time::with_slab(const Slab& new_slab) const {\n  if (new_slab == slab_) {\n    return *this;\n  } else if (is_at_slab_start()) {\n    if (slab_.start_ == new_slab.start_) {\n      return new_slab.start();\n    } else {\n      ASSERT(slab_.start() == new_slab.end(),\n             \"Can't move \" << fraction_ << \" \" << slab_ << \" to slab \"\n             << new_slab);\n      return new_slab.end();\n    }\n  } else {\n    ASSERT(is_at_slab_end(), \"Can't move \" << fraction_ << \" \" << slab_\n                                           << \" to slab \" << new_slab);\n    if (slab_.end_ == new_slab.end_) {\n      return new_slab.end();\n    } else {\n      ASSERT(slab_.end() == new_slab.start(),\n             \"Can't move \" << fraction_ << \" \" << slab_ << \" to slab \"\n             << new_slab);\n      return new_slab.start();\n    }\n  }\n}\n\nTime& Time::operator+=(const TimeDelta& delta) {\n  *this = this->with_slab(delta.slab_);\n  fraction_ += delta.fraction_;\n  range_check();\n  compute_value();\n  return *this;\n}\n\nTime& Time::operator-=(const TimeDelta& delta) {\n  *this = this->with_slab(delta.slab_);\n  fraction_ -= delta.fraction_;\n  range_check();\n  compute_value();\n  return *this;\n}\n\nbool Time::is_at_slab_start() const { return fraction_ == 0; }\nbool Time::is_at_slab_end() const { return fraction_ == 1; }\nbool Time::is_at_slab_boundary() const {\n  return is_at_slab_start() or is_at_slab_end();\n}\n\nvoid Time::pup(PUP::er& p) {\n  p | slab_;\n  p | fraction_;\n  if (p.isUnpacking()) {\n    // If the serialized object was uninitialized, this calculation\n    // can be garbage.\n    const ScopedFpeState disable_fpes(false);\n    compute_value();\n  }\n}\n\nbool Time::StructuralCompare::operator()(const Time& a, const Time& b) const {\n  if (a.fraction().numerator() != b.fraction().numerator()) {\n    return a.fraction().numerator() < b.fraction().numerator();\n  }\n  if (a.fraction().denominator() != b.fraction().denominator()) {\n    return a.fraction().denominator() < b.fraction().denominator();\n  }\n  return a.slab() < b.slab();\n}\n\nvoid Time::compute_value() {\n  value_ =\n      (1 - fraction_).value() * slab_.start_ + fraction_.value() * slab_.end_;\n}\n\nvoid Time::range_check() const {\n  ASSERT(fraction_ >= 0 and fraction_ <= 1,\n         \"Out of range slab fraction: \" << fraction_);\n}\n\n// TimeDelta implementations\n\nTimeDelta::TimeDelta(Slab slab, rational_t fraction)\n    // clang-tidy: move trivially copyable type\n    : slab_(std::move(slab)), fraction_(std::move(fraction)) {}  // NOLINT\n\nTimeDelta TimeDelta::with_slab(const Slab& new_slab) const {\n  return {new_slab, fraction()};\n}\n\ndouble TimeDelta::value() const {\n  return (slab_.end_ - slab_.start_) * fraction_.value();\n}\n\nbool TimeDelta::is_positive() const { return fraction_ > 0; }\n\nTimeDelta& TimeDelta::operator+=(const TimeDelta& other) {\n  ASSERT(slab_ == other.slab_, \"Can't add TimeDeltas from different slabs\");\n  fraction_ += other.fraction_;\n  return *this;\n}\n\nTimeDelta& TimeDelta::operator-=(const TimeDelta& other) {\n  ASSERT(slab_ == other.slab_,\n         \"Can't subtract TimeDeltas from different slabs\");\n  fraction_ -= other.fraction_;\n  return *this;\n}\n\nTimeDelta TimeDelta::operator+() const { return *this; }\n\nTimeDelta TimeDelta::operator-() const { return {slab_, -fraction_}; }\n\nTimeDelta& TimeDelta::operator*=(const rational_t& mult) {\n  fraction_ *= mult;\n  return *this;\n}\n\nTimeDelta& TimeDelta::operator/=(const rational_t& div) {\n  fraction_ /= div;\n  return *this;\n}\n\nvoid TimeDelta::pup(PUP::er& p) {\n  p | slab_;\n  p | fraction_;\n}\n\n// Free function implementations\n\n// Time <cmp> Time\n\nbool operator==(const Time& a, const Time& b) {\n  if (a.slab() == b.slab()) {\n    return a.fraction() == b.fraction();\n  } else {\n    return (a.slab().is_followed_by(b.slab()) and\n            a.is_at_slab_end() and\n            b.is_at_slab_start()) or\n           (a.slab().is_preceeded_by(b.slab()) and\n            a.is_at_slab_start() and\n            b.is_at_slab_end()) or\n           (a.slab().start_ == b.slab().start_ and\n            a.is_at_slab_start() and\n            b.is_at_slab_start()) or\n           (a.slab().end_ == b.slab().end_ and\n            a.is_at_slab_end() and\n            b.is_at_slab_end());\n  }\n}\n\nbool operator!=(const Time& a, const Time& b) { return not(a == b); }\n\nbool operator<(const Time& a, const Time& b) {\n  if (a.slab() == b.slab()) {\n    return a.fraction() < b.fraction();\n  }\n  if (not a.slab().overlaps(b.slab())) {\n    return a != b and a.slab() < b.slab();\n  }\n  if ((a.slab().start() == b.slab().start() and a.is_at_slab_start()) or\n      (a.slab().end() == b.slab().end() and a.is_at_slab_end())) {\n    return a.with_slab(b.slab()) < b;\n  } else {\n    return a < b.with_slab(a.slab());\n  }\n}\n\nbool operator>(const Time& a, const Time& b) { return b < a; }\n\nbool operator<=(const Time& a, const Time& b) { return not(a > b); }\n\nbool operator>=(const Time& a, const Time& b) { return not(a < b); }\n\n// TimeDelta <cmp> TimeDelta\n\nbool operator==(const TimeDelta& a, const TimeDelta& b) {\n  return a.slab() == b.slab() and a.fraction() == b.fraction();\n}\n\nbool operator!=(const TimeDelta& a, const TimeDelta& b) { return not(a == b); }\n\nbool operator<(const TimeDelta& a, const TimeDelta& b) {\n  ASSERT(a.slab() == b.slab(), \"Can't check cross-slab TimeDelta inequalities\");\n  return a.fraction() < b.fraction();\n}\n\nbool operator>(const TimeDelta& a, const TimeDelta& b) { return b < a; }\n\nbool operator<=(const TimeDelta& a, const TimeDelta& b) { return not(a > b); }\n\nbool operator>=(const TimeDelta& a, const TimeDelta& b) { return not(a < b); }\n\n// Time <op> Time\n\nTimeDelta operator-(const Time& a, const Time& b) {\n  if (a.slab() == b.slab()) {\n    return {a.slab(), a.fraction() - b.fraction()};\n  } else if (a.slab().is_followed_by(b.slab())) {\n    if (a.is_at_slab_end()) {\n      return {b.slab(), -b.fraction()};\n    } else {\n      ASSERT(b.is_at_slab_start(),\n             \"Can't subtract times from different slabs: \" << a << \" - \" << b);\n      return {a.slab(), a.fraction() - 1};\n    }\n  } else {\n    ASSERT(a.slab().is_preceeded_by(b.slab()),\n           \"Can't subtract times from different slabs: \" << a << \" - \" << b);\n    if (a.is_at_slab_start()) {\n      return {b.slab(), 1 - b.fraction()};\n    } else {\n      ASSERT(b.is_at_slab_end(),\n             \"Can't subtract times from different slabs: \" << a << \" - \" << b);\n      return {a.slab(), a.fraction()};\n    }\n  }\n}\n\n// Time <op> TimeDelta, TimeDelta <op> Time\nTime operator+(Time a, const TimeDelta& b) {\n  a += b;\n  return a;\n}\n\nTime operator+(const TimeDelta& a, Time b) {\n  b += a;\n  return b;\n}\n\nTime operator-(Time a, const TimeDelta& b) {\n  a -= b;\n  return a;\n}\n\n// TimeDelta <op> TimeDelta\n\nTimeDelta operator+(TimeDelta a, const TimeDelta& b) {\n  a += b;\n  return a;\n}\n\nTimeDelta operator-(TimeDelta a, const TimeDelta& b) {\n  a -= b;\n  return a;\n}\n\ndouble operator/(const TimeDelta& a, const TimeDelta& b) {\n  // We need to use double/double here because this is the\n  // implementation of TimeDelta/TimeDelta.\n  return (a.fraction() / b.fraction()).value() *\n         (a.slab().duration().value() / b.slab().duration().value());\n}\n\n// rational <op> TimeDelta, TimeDelta <op> rational\nTimeDelta operator*(TimeDelta a, const TimeDelta::rational_t& b) {\n  a *= b;\n  return a;\n}\n\nTimeDelta operator*(const TimeDelta::rational_t& a, TimeDelta b) {\n  b *= a;\n  return b;\n}\n\nTimeDelta operator/(TimeDelta a, const TimeDelta::rational_t& b) {\n  a /= b;\n  return a;\n}\n\n// Miscellaneous other functions for Time\n\nstd::ostream& operator<<(std::ostream& os, const Time& t) {\n  return os << t.slab() << \":\" << t.fraction();\n}\n\nsize_t hash_value(const Time& t) {\n  // We need equal Times to have equal hashes.  Times at matching ends\n  // of slabs are equal, so we can't use the slab and fraction in that\n  // case.\n  if (t.is_at_slab_boundary()) {\n    return boost::hash<double>{}(t.value());\n  } else {\n    size_t h = 0;\n    boost::hash_combine(h, t.slab());\n    boost::hash_combine(h, t.fraction());\n    return h;\n  }\n}\n\n// clang-tidy: do not modify std namespace (okay for hash)\nnamespace std {  // NOLINT\nsize_t hash<Time>::operator()(const Time& t) const {\n  return boost::hash<Time>{}(t);\n}\n}  // namespace std\n\n// Miscellaneous other functions for TimeDelta\n\nTimeDelta abs(TimeDelta t) {\n  if (not t.is_positive()) {\n    t *= -1;\n  }\n  return t;\n}\n\nstd::ostream& operator<<(std::ostream& os, const TimeDelta& dt) {\n  return os << dt.slab() << \":\" << dt.fraction();\n}\n\nsize_t hash_value(const TimeDelta& t) {\n  size_t h = 0;\n  boost::hash_combine(h, t.slab());\n  boost::hash_combine(h, t.fraction());\n  return h;\n}\n\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nnamespace std {\nsize_t hash<TimeDelta>::operator()(const TimeDelta& dt) const {\n  return boost::hash<TimeDelta>{}(dt);\n}\n}  // namespace std\n"
  },
  {
    "path": "src/Time/Time.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines Time and TimeDelta\n\n#pragma once\n\n#include <cstddef>\n#include <functional>\n#include <iosfwd>\n#include <limits>\n\n#include \"Time/Slab.hpp\"\n#include \"Utilities/Rational.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nclass TimeDelta;\n/// \\endcond\n\n/// \\ingroup TimeGroup\n///\n/// The time in a simulation.  Times can be safely compared for exact\n/// equality as long as they do not belong to overlapping unequal\n/// slabs.\nclass Time {\n public:\n  using rational_t = Rational;\n\n  /// Default constructor gives an invalid Time.\n  Time() = default;\n\n  /// A time a given fraction of the way through the given slab.\n  Time(Slab slab, rational_t fraction);\n\n  /// Move the time to a different slab.  The time must be at an end\n  /// of the current slab and the new slab must share that endpoint.\n  Time with_slab(const Slab& new_slab) const;\n\n  /// Approximate numerical value of the Time.\n  double value() const { return value_; }\n  const Slab& slab() const { return slab_; }\n  const rational_t& fraction() const { return fraction_; }\n\n  Time& operator+=(const TimeDelta& delta);\n  Time& operator-=(const TimeDelta& delta);\n\n  bool is_at_slab_start() const;\n  bool is_at_slab_end() const;\n  bool is_at_slab_boundary() const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  /// A comparison operator that compares Times structurally, i.e.,\n  /// just looking at the class members.  This is only intended for\n  /// use as the comparator in a map.  The returned ordering does not\n  /// match the time ordering and opposite sides of slab boundaries do\n  /// not compare equal.  It is, however, much faster to compute than\n  /// the temporal ordering, so it is useful when an ordering is\n  /// required, but the ordering does not have to be physically\n  /// meaningful.\n  struct StructuralCompare {\n    bool operator()(const Time& a, const Time& b) const;\n  };\n\n private:\n  Slab slab_;\n  rational_t fraction_;\n  double value_ = std::numeric_limits<double>::signaling_NaN();\n\n  // The value is precomputed so that we can avoid doing the rational\n  // math repeatedly.  The value of a Time should almost always be\n  // needed at some point.\n  void compute_value();\n\n  void range_check() const;\n\n  friend class TimeDelta;\n};\n\n/// \\ingroup TimeGroup\n///\n/// Represents an interval of time within a single slab.\nclass TimeDelta {\n public:\n  using rational_t = Time::rational_t;\n\n  /// Default constructor gives an invalid TimeDelta.\n  TimeDelta() = default;\n\n  /// An interval covering a given fraction of the slab.\n  TimeDelta(Slab slab, rational_t fraction);\n\n  /// Move the interval to a different slab.  The resulting interval\n  /// will in general not be the same length, but will take up the\n  /// same fraction of its slab.\n  TimeDelta with_slab(const Slab& new_slab) const;\n\n  Slab slab() const { return slab_; }\n  rational_t fraction() const { return fraction_; }\n\n  /// Approximate numerical length of the interval.\n  double value() const;\n\n  /// Test if the interval is oriented towards larger time.\n  bool is_positive() const;\n\n  TimeDelta& operator+=(const TimeDelta& other);\n  TimeDelta& operator-=(const TimeDelta& other);\n  TimeDelta operator+() const;\n  TimeDelta operator-() const;\n  TimeDelta& operator*=(const rational_t& mult);\n  TimeDelta& operator/=(const rational_t& div);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  Slab slab_;\n  rational_t fraction_;\n\n  friend class Time;\n};\n\n// Time <cmp> Time\n// clang-tidy: clang-tidy wants this removed in favor of friend\n// declaration in different header.\nbool operator==(const Time& a, const Time& b);  // NOLINT\nbool operator!=(const Time& a, const Time& b);\nbool operator<(const Time& a, const Time& b);\nbool operator>(const Time& a, const Time& b);\nbool operator<=(const Time& a, const Time& b);\nbool operator>=(const Time& a, const Time& b);\n\n// TimeDelta <cmp> TimeDelta\nbool operator==(const TimeDelta& a, const TimeDelta& b);\nbool operator!=(const TimeDelta& a, const TimeDelta& b);\nbool operator<(const TimeDelta& a, const TimeDelta& b);\nbool operator>(const TimeDelta& a, const TimeDelta& b);\nbool operator<=(const TimeDelta& a, const TimeDelta& b);\nbool operator>=(const TimeDelta& a, const TimeDelta& b);\n\n// Time <op> Time\nTimeDelta operator-(const Time& a, const Time& b);\n\n// Time <op> TimeDelta, TimeDelta <op> Time\nTime operator+(Time a, const TimeDelta& b);\nTime operator+(const TimeDelta& a, Time b);\nTime operator-(Time a, const TimeDelta& b);\n\n// TimeDelta <op> TimeDelta\nTimeDelta operator+(TimeDelta a, const TimeDelta& b);\nTimeDelta operator-(TimeDelta a, const TimeDelta& b);\n\n// This returns a double rather than a rational so we can compare dt\n// in different slabs.\ndouble operator/(const TimeDelta& a, const TimeDelta& b);\n\n// rational <op> TimeDelta, TimeDelta <op> rational\nTimeDelta operator*(TimeDelta a, const TimeDelta::rational_t& b);\nTimeDelta operator*(const TimeDelta::rational_t& a, TimeDelta b);\nTimeDelta operator/(TimeDelta a, const TimeDelta::rational_t& b);\n\n// Miscellaneous other functions for Time\n\nstd::ostream& operator<<(std::ostream& os, const Time& t);\n\nsize_t hash_value(const Time& t);\n\nnamespace std {\ntemplate <>\nstruct hash<Time> {\n  size_t operator()(const Time& t) const;\n};\n}  // namespace std\n\n// Miscellaneous other functions for TimeDelta\n\nTimeDelta abs(TimeDelta t);\n\nstd::ostream& operator<<(std::ostream& os, const TimeDelta& dt);\n\nsize_t hash_value(const TimeDelta& dt);\n\nnamespace std {\ntemplate <>\nstruct hash<TimeDelta> {\n  size_t operator()(const TimeDelta& dt) const;\n};\n}  // namespace std\n"
  },
  {
    "path": "src/Time/TimeSequence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSequence.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstdint>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Options/ParseError.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace TimeSequences {\ntemplate <typename T>\nEvenlySpaced<T>::EvenlySpaced(const T interval, const T offset,\n                              const Options::Context& context)\n    : interval_(static_cast<SignedT>(interval)),\n      offset_(static_cast<SignedT>(offset)) {\n  if (interval_ == 0) {\n    PARSE_ERROR(context, \"Interval must be positive.\");\n  }\n}\n\ntemplate <typename T>\nstd::array<std::optional<T>, 3> EvenlySpaced<T>::times_near(\n    const T time) const {\n  auto sequence_number =\n      std::floor((time + 0.5 * interval_ - offset_) / interval_);\n\n  if (not std::is_signed_v<T> and offset_ + interval_ * sequence_number < 0) {\n    ++sequence_number;\n  }\n\n  const auto previous = offset_ + interval_ * (sequence_number - 1);\n  const auto current = offset_ + interval_ * sequence_number;\n  const auto next = offset_ + interval_ * (sequence_number + 1);\n\n  ASSERT(std::is_signed_v<T> or current >= 0,\n         \"Generated a negative value for an unsigned sequence.\");\n\n  return {{std::is_signed_v<T> or previous >= 0 ? previous : std::optional<T>{},\n           current, next}};\n}\n\ntemplate <typename T>\nvoid EvenlySpaced<T>::pup(PUP::er& p) {\n  TimeSequence<T>::pup(p);\n  p | interval_;\n  p | offset_;\n}\n\ntemplate <typename T>\nPUP::able::PUP_ID EvenlySpaced<T>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <typename T>\nSpecified<T>::Specified(std::vector<T> values) : values_(std::move(values)) {\n  std::sort(values_.begin(), values_.end());\n  const auto new_end = std::unique(values_.begin(), values_.end());\n  values_.erase(new_end, values_.end());\n}\n\ntemplate <typename T>\nstd::array<std::optional<T>, 3> Specified<T>::times_near(const T time) const {\n  if (values_.empty()) {\n    return {};\n  }\n\n  auto closest_time = std::lower_bound(values_.begin(), values_.end(), time);\n  if (closest_time == values_.end() or\n      (closest_time != values_.begin() and\n       time - *(closest_time - 1) < *closest_time - time)) {\n    --closest_time;\n  }\n\n  const auto next = closest_time + 1 == values_.end() ? std::optional<T>{}\n                                                      : *(closest_time + 1);\n  const auto previous = closest_time == values_.begin() ? std::optional<T>{}\n                                                        : *(closest_time - 1);\n  return {{previous, *closest_time, next}};\n}\n\ntemplate <typename T>\nvoid Specified<T>::pup(PUP::er& p) {\n  TimeSequence<T>::pup(p);\n  p | values_;\n}\n\ntemplate <typename T>\nPUP::able::PUP_ID Specified<T>::my_PUP_ID = 0;  // NOLINT\n\n// For time values\ntemplate class EvenlySpaced<double>;\ntemplate class Specified<double>;\n// For slabs\ntemplate class EvenlySpaced<std::uint64_t>;\ntemplate class Specified<std::uint64_t>;\n}  // namespace TimeSequences\n"
  },
  {
    "path": "src/Time/TimeSequence.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <optional>\n#include <pup.h>\n#include <vector>\n\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Represents a sequence of times.\n///\n/// The template parameter \\p T can either be `double` for a sequence\n/// of simulation times or std::uint64_t for a sequence of slab\n/// numbers.\ntemplate <typename T>\nclass TimeSequence : public PUP::able {\n protected:\n  /// \\cond HIDDEN_SYMBOLS\n  TimeSequence() = default;\n  TimeSequence(const TimeSequence&) = default;\n  TimeSequence(TimeSequence&&) = default;\n  TimeSequence& operator=(const TimeSequence&) = default;\n  TimeSequence& operator=(TimeSequence&&) = default;\n  /// \\endcond\n\n public:\n  ~TimeSequence() override = default;\n\n  WRAPPED_PUPable_abstract(TimeSequence);  // NOLINT\n\n  /// Returns the time in the sequence nearest to \\p time, the time\n  /// before that, and the time after, in numerical order.  These\n  /// values allow a consumer to find the times in the sequence\n  /// bracketing the given time and give enough additional information\n  /// so that exact or roundoff matches can be handled however the\n  /// consumer wishes.  Any time that does not exist (because the\n  /// sequence terminates) is returned as an empty std::optional.  The\n  /// central std::optional will be populated unless the sequence is\n  /// empty.\n  virtual std::array<std::optional<T>, 3> times_near(T time) const = 0;\n};\n\n/// \\ingroup TimeGroup\n///\n/// Holds all the TimeSequences\nnamespace TimeSequences {\n/*!\n * \\brief A sequence of evenly spaced times.\n *\n * The sequence of times\n * \\f{equation}{\n *   n \\times \\mathrm{Interval} + \\mathrm{Offset}\n * \\f}\n * for all integers $n$.\n */\ntemplate <typename T>\nclass EvenlySpaced : public TimeSequence<T> {\n public:\n  /// \\cond\n  EvenlySpaced() = default;\n  explicit EvenlySpaced(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(EvenlySpaced);  // NOLINT\n  /// \\endcond\n\n  struct Interval {\n    static constexpr Options::String help = \"Spacing between times\";\n    using type = T;\n    static constexpr T lower_bound() { return 0; }\n  };\n\n  struct Offset {\n    static constexpr Options::String help = \"Offset of sequence\";\n    using type = T;\n  };\n\n  static constexpr Options::String help =\n      \"The sequence of times  n * Interval + Offset  for all integers n.\";\n  using options = tmpl::list<Interval, Offset>;\n\n  explicit EvenlySpaced(T interval, T offset = 0,\n                        const Options::Context& context = {});\n\n  std::array<std::optional<T>, 3> times_near(T time) const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  // This is\n  //   tmpl::conditional<std::is_integral_v<T>, std::make_signed<T>,\n  //                     tmpl::identity<T>>::type\n  // except avoiding instantiating std::make_signed<double> because,\n  // depending on the standard version, that is either undefined\n  // behavior or makes the program ill-formed.\n  using SignedT = tmpl::apply<tmpl::apply<\n      tmpl::if_<std::is_integral<tmpl::pin<T>>,\n                tmpl::defer<tmpl::bind<std::make_signed_t, tmpl::pin<T>>>,\n                tmpl::defer<tmpl::always<tmpl::pin<T>>>>>>;\n\n  SignedT interval_{};\n  SignedT offset_{};\n};\n\n/// An explicitly specified sequence of times.\ntemplate <typename T>\nclass Specified : public TimeSequence<T> {\n public:\n  /// \\cond\n  Specified() = default;\n  explicit Specified(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Specified);  // NOLINT\n  /// \\endcond\n\n  struct Values {\n    static constexpr Options::String help = \"The times in the sequence\";\n    using type = std::vector<T>;\n  };\n\n  static constexpr Options::String help =\n      \"An explicitly specified sequence of times.\";\n  using options = tmpl::list<Values>;\n\n  explicit Specified(std::vector<T> values);\n\n  std::array<std::optional<T>, 3> times_near(T time) const override;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::vector<T> values_;\n};\n\ntemplate <typename T>\nusing all_time_sequences = tmpl::list<EvenlySpaced<T>, Specified<T>>;\n}  // namespace TimeSequences\n"
  },
  {
    "path": "src/Time/TimeStepId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeStepId.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <limits>\n#include <ostream>\n#include <pup.h>\n\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nTimeStepId::TimeStepId(const bool time_runs_forward, const int64_t slab_number,\n                       const Time& time)\n    : slab_number_(slab_number),\n      step_time_(time),\n      step_size_(time_runs_forward ? 1 : -1),\n      substep_time_(time.value()) {\n  canonicalize();\n}\n\nTimeStepId::TimeStepId(const bool time_runs_forward, const int64_t slab_number,\n                       const Time& step_time, const uint64_t substep,\n                       const TimeDelta& step_size, const double substep_time)\n    : slab_number_(slab_number),\n      step_time_(step_time),\n      substep_(substep),\n      step_size_(substep == 0 ? (time_runs_forward ? 1 : -1)\n                              : step_size.fraction()),\n      substep_time_(substep_time) {\n  ASSERT(substep <= std::numeric_limits<decltype(substep_)>::max(),\n         \"Overflow in substep: \" << substep);\n  ASSERT(substep_ != 0 or step_time_.value() == substep_time_,\n         \"Initial substep must align with the step.\");\n  ASSERT(substep_ == 0 or step_time.slab() == step_size.slab(),\n         \"Time and step have different slabs.\");\n  ASSERT(substep == 0 or time_runs_forward == step_size.is_positive(),\n         \"Step size has wrong sign: \" << step_size);\n  if (time_runs_forward) {\n    ASSERT(substep_time_ >= step_time_.value(),\n           \"Substep must be within the step.\");\n  } else {\n    ASSERT(substep_time_ <= step_time_.value(),\n           \"Substep must be within the step.\");\n  }\n  canonicalize();\n}\n\nbool TimeStepId::time_runs_forward() const { return step_size_ > 0; }\n\nTimeDelta TimeStepId::step_size() const {\n  ASSERT(substep_ != 0, \"Step size not available at substep 0.\");\n  return {step_time_.slab(), step_size_};\n}\n\nbool TimeStepId::is_at_slab_boundary() const {\n  return substep_ == 0 and step_time_.is_at_slab_boundary();\n}\n\nTimeStepId TimeStepId::next_step(const TimeDelta& step_size) const {\n  ASSERT(substep_ == 0 or step_size == this->step_size(),\n         \"Step size inconsistent: \" << this->step_size() << \" \" << step_size);\n  return {time_runs_forward(), slab_number_, step_time_ + step_size};\n}\n\nTimeStepId TimeStepId::next_substep(const TimeDelta& step_size,\n                                    const double step_fraction) const {\n  ASSERT(substep_ == 0 or step_size == this->step_size(),\n         \"Step size inconsistent: \" << this->step_size() << \" \" << step_size);\n  ASSERT(step_fraction >= 0.0 and step_fraction <= 1.0,\n         \"Substep must be within the step.\");\n  const double new_time = (1.0 - step_fraction) * step_time_.value() +\n                          step_fraction * (step_time_ + step_size).value();\n  return {time_runs_forward(), slab_number_, step_time_, substep() + 1,\n          step_size, new_time};\n}\n\nvoid TimeStepId::canonicalize() {\n  if (time_runs_forward() ? step_time_.is_at_slab_end()\n                          : step_time_.is_at_slab_start()) {\n    ASSERT(substep_ == 0,\n           \"Time needs to be advanced, but step already started\");\n    const Slab new_slab = time_runs_forward() ? step_time_.slab().advance()\n                                              : step_time_.slab().retreat();\n    ++slab_number_;\n    step_time_ = step_time_.with_slab(new_slab);\n    ASSERT(substep_time_ == step_time_.value(),\n           \"Time changed during canonicalization\");\n  }\n}\n\nvoid TimeStepId::pup(PUP::er& p) {\n  p | slab_number_;\n  p | step_time_;\n  p | substep_;\n  p | step_size_;\n  p | substep_time_;\n}\n\nbool operator==(const TimeStepId& a, const TimeStepId& b) {\n  ASSERT(a.time_runs_forward() == b.time_runs_forward(),\n         \"Time is not running in a consistent direction\");\n  bool equal = a.slab_number() == b.slab_number() and\n               a.step_time() == b.step_time() and a.substep() == b.substep();\n  if (equal and a.substep() != 0) {\n    equal = a.step_size() == b.step_size();\n  }\n  ASSERT(not equal or a.substep_time() == b.substep_time(),\n         \"IDs at same step and substep but different times\");\n  return equal;\n}\n\nbool operator!=(const TimeStepId& a, const TimeStepId& b) {\n  return not(a == b);\n}\n\nbool operator<(const TimeStepId& a, const TimeStepId& b) {\n  ASSERT(a.time_runs_forward() == b.time_runs_forward(),\n         \"Time is not running in a consistent direction\");\n  if (a.slab_number() != b.slab_number()) {\n    return a.slab_number() < b.slab_number();\n  }\n  if (a.step_time() != b.step_time()) {\n    return evolution_less<Time>{a.time_runs_forward()}(a.step_time(),\n                                                       b.step_time());\n  }\n  if (a.substep() != b.substep()) {\n    return a.substep() < b.substep();\n  }\n  if (a.substep() == 0) {\n    // Objects are equal.\n    return false;\n  }\n  // Arbitrary, but need a total ordering of TimeStepId to use it as a\n  // std::map key.\n  return a.step_size() < b.step_size();\n}\nbool operator<=(const TimeStepId& a, const TimeStepId& b) { return not(b < a); }\nbool operator>(const TimeStepId& a, const TimeStepId& b) { return b < a; }\nbool operator>=(const TimeStepId& a, const TimeStepId& b) { return not(a < b); }\n\nstd::ostream& operator<<(std::ostream& s, const TimeStepId& id) {\n  return s << id.slab_number() << ':' << id.step_time() << ':' << id.substep()\n           << ':' << id.substep_time();\n}\n\nsize_t hash_value(const TimeStepId& id) {\n  size_t h = 0;\n  boost::hash_combine(h, id.slab_number());\n  boost::hash_combine(h, id.step_time());\n  boost::hash_combine(h, id.substep());\n  if (id.substep() != 0) {\n    boost::hash_combine(h, id.step_size());\n    boost::hash_combine(h, id.substep_time());\n  }\n  return h;\n}\n\n// clang-tidy: do not modify std namespace (okay for hash)\nnamespace std {  // NOLINT\nsize_t hash<TimeStepId>::operator()(const TimeStepId& id) const {\n  return boost::hash<TimeStepId>{}(id);\n}\n}  // namespace std\n"
  },
  {
    "path": "src/Time/TimeStepId.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class TimeStepId.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <iosfwd>\n#include <limits>\n\n#include \"Time/Time.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\n/// \\ingroup TimeGroup\n///\n/// A unique identifier for the temporal state of an integrated\n/// system.\nclass TimeStepId {\n public:\n  TimeStepId() = default;\n  /// Create a TimeStepId at the start of a step.  If that step is at the\n  /// (evolution-defined) end of the slab the TimeStepId will be advanced\n  /// to the next slab.\n  TimeStepId(bool time_runs_forward, int64_t slab_number, const Time& time);\n  /// Create a TimeStepId at a substep at time `substep_time` in a step\n  /// starting at time `step_time`.\n  TimeStepId(bool time_runs_forward, int64_t slab_number, const Time& step_time,\n             uint64_t substep, const TimeDelta& step_size, double substep_time);\n\n  bool time_runs_forward() const;\n  int64_t slab_number() const { return slab_number_; }\n  /// Time at the start of the current step\n  const Time& step_time() const { return step_time_; }\n  uint64_t substep() const { return substep_; }\n  /// Current step size.  Only available when the substep is nonzero,\n  /// because on the full step the state is valid for any step size.\n  TimeDelta step_size() const;\n  /// Time of the current substep\n  double substep_time() const { return substep_time_; }\n\n  bool is_at_slab_boundary() const;\n\n  /// Returns a new TimeStepId representing the start of the next step.\n  TimeStepId next_step(const TimeDelta& step_size) const;\n\n  /// Returns a new TimeStepId representing the next substep, given\n  /// the position of the substep within the step.\n  TimeStepId next_substep(const TimeDelta& step_size,\n                          double step_fraction) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  void canonicalize();\n\n  int64_t slab_number_{std::numeric_limits<int64_t>::lowest()};\n  Time step_time_{};\n  uint8_t substep_{0};\n  Rational step_size_{};\n  double substep_time_{};\n};\n\n/// These comparisons define a total order on all TimeStepIds with the\n/// same direction of time flow.  For any two objects representing\n/// states of the same element, the ordering will match the\n/// computational ordering of those states.  For others, (e.g.,\n/// overlapping steps) it is arbitrary.\n/// @{\nbool operator==(const TimeStepId& a, const TimeStepId& b);\nbool operator!=(const TimeStepId& a, const TimeStepId& b);\nbool operator<(const TimeStepId& a, const TimeStepId& b);\nbool operator<=(const TimeStepId& a, const TimeStepId& b);\nbool operator>(const TimeStepId& a, const TimeStepId& b);\nbool operator>=(const TimeStepId& a, const TimeStepId& b);\n/// @}\n\nstd::ostream& operator<<(std::ostream& s, const TimeStepId& id);\n\nsize_t hash_value(const TimeStepId& id);\n\nnamespace std {\ntemplate <>\nstruct hash<TimeStepId> {\n  size_t operator()(const TimeStepId& id) const;\n};\n}  // namespace std\n"
  },
  {
    "path": "src/Time/TimeStepRequest.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeStepRequest.hpp\"\n\n#include <pup.h>\n\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nvoid TimeStepRequest::pup(PUP::er& p) {\n  p | size_goal;\n  p | size;\n  p | end;\n  p | size_hard_limit;\n  p | end_hard_limit;\n}\n\nbool operator==(const TimeStepRequest& a, const TimeStepRequest& b) {\n  return a.size_goal == b.size_goal and a.size == b.size and a.end == b.end and\n         a.size_hard_limit == b.size_hard_limit and\n         a.end_hard_limit == b.end_hard_limit;\n}\n\nbool operator!=(const TimeStepRequest& a, const TimeStepRequest& b) {\n  return not(a == b);\n}\n"
  },
  {
    "path": "src/Time/TimeStepRequest.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// Information on a requested time-step size returned by `StepChooser`s.\n///\n/// Requests made using this struct control most of the changing of\n/// slab and local-time-stepping step sizes.  Slab-size changing is\n/// more complex, as it is controlled using the `ChangeSlabSize`\n/// event, which may run zero or more times per slab, and may have the\n/// application of its result delayed.  The step size for LTS is\n/// adjusted using a constant list of `StepChoosers` on every step.\n///\n/// For slab-size adjustment (including the global time step when not\n/// using LTS), a \"goal\" step size is maintained from step to step.\n/// It is initially the initial slab size from the input file, and can\n/// be adjusted by any request setting the `size_goal` field of this\n/// struct.  If multiple requests set new goals at the same time, the\n/// smallest value is used.  At the start of each slab, the size is\n/// chosen to be the smallest of the current goal and the temporary\n/// limits imposed by any requests processed at that time.  For the\n/// LTS step size, the current step size is used as the goal if no\n/// request sets a new one.\n///\n/// Any request with either the `size` or `end` fields set introduces\n/// a temporary step size limit.  If only one of the two is set, the\n/// chosen step size will be limited to be not larger than `*size`, or\n/// not go beyond the time `*end`.  If both are set, the tighter limit\n/// may be ignored if the looser limit is chosen as the actual step,\n/// but the step will not be chosen to be intermediate between two\n/// temporary limits from the same request object.\n///\n/// The hard limits are not used in setting the step size, but if the\n/// actual step does not satisfy all hard limits from all requests the\n/// evolution will be terminated with an error.\n///\n/// \\note All step sizes are subject to roundoff error in either\n/// direction except for two cases:\n/// * Slab sizes imposed by the `end` limit of a request will always\n///   end precisely at the requested time.\n/// * LTS step sizes within a single slab will always be considered to\n///   have size ratios that are exactly powers of two for the purposes\n///   of the `size_goal` and `size` limits.\nstruct TimeStepRequest {\n  std::optional<double> size_goal{};\n\n  std::optional<double> size{};\n  std::optional<double> end{};\n\n  std::optional<double> size_hard_limit{};\n  std::optional<double> end_hard_limit{};\n\n  void pup(PUP::er& p);\n};\n\nbool operator==(const TimeStepRequest& a, const TimeStepRequest& b);\nbool operator!=(const TimeStepRequest& a, const TimeStepRequest& b);\n"
  },
  {
    "path": "src/Time/TimeStepRequestProcessor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeStepRequestProcessor.hpp\"\n\n#include <algorithm>\n#include <optional>\n\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nTimeStepRequestProcessor::TimeStepRequestProcessor(const bool time_runs_forward)\n    : time_runs_forward_(time_runs_forward) {\n  const double inf = evolution_less<double>{time_runs_forward}.infinity();\n  step_size_request_ = inf;\n  step_end_request_ = inf;\n  next_step_size_request_ = inf;\n  next_step_end_request_ = inf;\n  step_size_hard_limit_ = inf;\n  step_end_hard_limit_ = inf;\n}\n\nstd::optional<double> TimeStepRequestProcessor::new_step_size_goal() const {\n  return step_size_goal_;\n}\n\ndouble TimeStepRequestProcessor::step_size(const double step_start,\n                                           const double step_size_goal) const {\n  const evolution_less<double> smaller{time_runs_forward_};\n\n  const double goal = step_size_goal_.value_or(step_size_goal);\n  const double size_from_end = step_end_request_ - step_start;\n\n  if (can_use_larger_limit_) {\n    // Both limits are from the same request\n    const double request = std::max(step_size_request_, size_from_end, smaller);\n    if (not(smaller(goal, request) or\n            smaller(next_step_size_request_, request) or\n            smaller(next_step_end_request_ - step_start, request))) {\n      return request;\n    }\n  }\n\n  return std::min({goal, step_size_request_, size_from_end}, smaller);\n}\n\ndouble TimeStepRequestProcessor::step_end(const double step_start,\n                                          const double step_size_goal) const {\n  const evolution_less<double> before{time_runs_forward_};\n\n  const double goal = step_start + step_size_goal_.value_or(step_size_goal);\n  const double end_from_size = step_start + step_size_request_;\n\n  if (can_use_larger_limit_) {\n    // Both limits are from the same request\n    const double request = std::max(step_end_request_, end_from_size, before);\n    if (not(before(goal, request) or\n            before(step_start + next_step_size_request_, request) or\n            before(next_step_end_request_, request))) {\n      return request;\n    }\n  }\n\n  return std::min({goal, step_end_request_, end_from_size}, before);\n}\n\nvoid TimeStepRequestProcessor::process(const TimeStepRequest& request) {\n  // Earlier-time and smaller-step are the same operation on doubles,\n  // but it's easier to read if they have different names.\n  const evolution_less<double> before{time_runs_forward_};\n  const evolution_less<double> smaller{time_runs_forward_};\n\n  ASSERT(not(request.size_goal.has_value() and\n             request.size.has_value() and\n             smaller(*request.size_goal, *request.size)),\n         \"Requested a step size goal of \" << *request.size_goal\n         << \", but a particular step size larger than that goal: \"\n         << *request.size);\n\n  TimeStepRequestProcessor request_postprocessor(time_runs_forward_);\n  request_postprocessor.step_size_goal_ = request.size_goal;\n\n  request_postprocessor.step_size_request_ =\n      request.size.value_or(smaller.infinity());\n  request_postprocessor.step_end_request_ =\n      request.end.value_or(before.infinity());\n\n  request_postprocessor.can_use_larger_limit_ =\n      request.size.has_value() and request.end.has_value();\n\n  request_postprocessor.step_size_hard_limit_ =\n      request.size_hard_limit.value_or(smaller.infinity());\n  request_postprocessor.step_end_hard_limit_ =\n      request.end_hard_limit.value_or(before.infinity());\n\n  *this += request_postprocessor;\n}\n\nTimeStepRequestProcessor& TimeStepRequestProcessor::operator+=(\n    const TimeStepRequestProcessor& other) {\n  ASSERT(time_runs_forward_ == other.time_runs_forward_,\n         \"Inconsistent time directions.\");\n\n  // Earlier-time and smaller-step are the same operation on doubles,\n  // but it's easier to read if they have different names.\n  const evolution_less<double> before{time_runs_forward_};\n  const evolution_less<double> smaller{time_runs_forward_};\n\n  if (smaller(other.step_size_goal_.value_or(smaller.infinity()),\n              step_size_goal_.value_or(smaller.infinity()))) {\n    step_size_goal_ = other.step_size_goal_;\n  }\n\n  if (smaller(step_size_request_, other.step_size_request_)) {\n    next_step_size_request_ =\n        std::min(next_step_size_request_, other.step_size_request_, smaller);\n  } else if (smaller(other.step_size_request_, step_size_request_)) {\n    next_step_size_request_ =\n        std::min(step_size_request_, other.next_step_size_request_, smaller);\n  } else {\n    next_step_size_request_ = std::min(next_step_size_request_,\n                                       other.next_step_size_request_, smaller);\n  }\n  if (before(step_end_request_, other.step_end_request_)) {\n    next_step_end_request_ =\n        std::min(next_step_end_request_, other.step_end_request_, before);\n  } else if (before(other.step_end_request_, step_end_request_)) {\n    next_step_end_request_ =\n        std::min(step_end_request_, other.next_step_end_request_, before);\n  } else {\n    next_step_end_request_ =\n        std::min(next_step_end_request_, other.next_step_end_request_, before);\n  }\n\n  const double new_size_request =\n      std::min(step_size_request_, other.step_size_request_, smaller);\n  const double new_end_request =\n      std::min(step_end_request_, other.step_end_request_, before);\n  if ((new_size_request == step_size_request_) ==\n          (new_end_request == step_end_request_) and\n      (new_size_request == other.step_size_request_) ==\n          (new_end_request == other.step_end_request_)) {\n    if (new_size_request == step_size_request_) {\n      if (new_size_request == other.step_size_request_) {\n        can_use_larger_limit_ =\n            can_use_larger_limit_ and other.can_use_larger_limit_;\n      } else {\n        // use existing value\n      }\n    } else {\n      if (new_size_request == other.step_size_request_) {\n        can_use_larger_limit_ = other.can_use_larger_limit_;\n      } else {\n        can_use_larger_limit_ = false;\n      }\n    }\n  } else {\n    can_use_larger_limit_ = false;\n  }\n  step_size_request_ = new_size_request;\n  step_end_request_ = new_end_request;\n\n  step_size_hard_limit_ =\n      std::min(step_size_hard_limit_, other.step_size_hard_limit_, smaller);\n  step_end_hard_limit_ =\n      std::min(step_end_hard_limit_, other.step_end_hard_limit_, before);\n\n  return *this;\n}\n\nvoid TimeStepRequestProcessor::error_on_hard_limit(const double size,\n                                                   const double end) const {\n  // Earlier-time and smaller-step are the same operation on doubles,\n  // but it's easier to read if they have different names.\n  const evolution_less<double> before{time_runs_forward_};\n  const evolution_less<double> smaller{time_runs_forward_};\n\n  if (smaller(step_size_hard_limit_, size)) {\n    ERROR(\"Could not adjust step below \"\n          << size << \" to meet maximum step size \" << step_size_hard_limit_);\n  }\n  if (before(step_end_hard_limit_, end)) {\n    ERROR(\"Could not adjust step to before \"\n          << end << \" to avoid exceeding time \" << step_end_hard_limit_);\n  }\n}\n\nvoid TimeStepRequestProcessor::pup(PUP::er& p) {\n  p | time_runs_forward_;\n  p | step_size_goal_;\n  p | step_size_request_;\n  p | step_end_request_;\n  p | can_use_larger_limit_;\n  p | next_step_size_request_;\n  p | next_step_end_request_;\n  p | step_size_hard_limit_;\n  p | step_end_hard_limit_;\n}\n\nbool operator==(const TimeStepRequestProcessor& a,\n                const TimeStepRequestProcessor& b) {\n  return a.time_runs_forward_ == b.time_runs_forward_ and\n         a.step_size_goal_ == b.step_size_goal_ and\n         a.step_size_request_ == b.step_size_request_ and\n         a.step_end_request_ == b.step_end_request_ and\n         a.can_use_larger_limit_ == b.can_use_larger_limit_ and\n         a.next_step_size_request_ == b.next_step_size_request_ and\n         a.next_step_end_request_ == b.next_step_end_request_ and\n         a.step_size_hard_limit_ == b.step_size_hard_limit_ and\n         a.step_end_hard_limit_ == b.step_end_hard_limit_;\n}\n\nbool operator!=(const TimeStepRequestProcessor& a,\n                const TimeStepRequestProcessor& b) {\n  return not(a == b);\n}\n\nTimeStepRequestProcessor operator+(const TimeStepRequestProcessor& a,\n                                   const TimeStepRequestProcessor& b) {\n  auto result = a;\n  result += b;\n  return result;\n}\n"
  },
  {
    "path": "src/Time/TimeStepRequestProcessor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <optional>\n\n/// \\cond\nstruct TimeStepRequest;\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// Combine TimeStepRequest objects to find a consensus step.  See\n/// that class for details.\nclass TimeStepRequestProcessor {\n public:\n  TimeStepRequestProcessor() = default;\n  explicit TimeStepRequestProcessor(bool time_runs_forward);\n\n  /// The new goal, if one was given.\n  std::optional<double> new_step_size_goal() const;\n\n  /// The new step.  Two versions are provided, as neither computing\n  /// the step size from the end or the end from the size is\n  /// guaranteed to be an exact operation on floating-point values,\n  /// and which quantity is fundamental varies between consumers.  The\n  /// \\p step_size_goal is ignored if any requests have set a new\n  /// goal.\n  /// @{\n  double step_size(double step_start, double step_size_goal) const;\n  double step_end(double step_start, double step_size_goal) const;\n  /// @}\n\n  void process(const TimeStepRequest& request);\n\n  /// Merge the results from another object.\n  TimeStepRequestProcessor& operator+=(const TimeStepRequestProcessor& other);\n\n  /// ERROR if \\p size and \\p end do not satisfy the hard limits.\n  void error_on_hard_limit(double size, double end) const;\n\n  void pup(PUP::er& p);\n\n private:\n  friend bool operator==(const TimeStepRequestProcessor& a,\n                         const TimeStepRequestProcessor& b);\n\n  bool time_runs_forward_ = false;\n\n  std::optional<double> step_size_goal_{};\n\n  double step_size_request_ = std::numeric_limits<double>::signaling_NaN();\n  double step_end_request_ = std::numeric_limits<double>::signaling_NaN();\n\n  bool can_use_larger_limit_ = false;\n  double next_step_size_request_ = std::numeric_limits<double>::signaling_NaN();\n  double next_step_end_request_ = std::numeric_limits<double>::signaling_NaN();\n\n  double step_size_hard_limit_ = std::numeric_limits<double>::signaling_NaN();\n  double step_end_hard_limit_ = std::numeric_limits<double>::signaling_NaN();\n};\n\nbool operator!=(const TimeStepRequestProcessor& a,\n                const TimeStepRequestProcessor& b);\n\n/// Combine the limits from two TimeStepRequestProcessor objects.\nTimeStepRequestProcessor operator+(const TimeStepRequestProcessor& a,\n                                   const TimeStepRequestProcessor& b);\n"
  },
  {
    "path": "src/Time/TimeSteppers/AdamsBashforth.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <cstdint>\n#include <iterator>\n#include <limits>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Time/ApproximateTime.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/LargestStepperError.hpp\"\n#include \"Time/SelfStart.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsCoefficients.hpp\"\n#include \"Time/TimeSteppers/AdamsLts.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TimeSteppers {\n\n// Don't include AdamsCoefficients.hpp in the header just to get one\n// constant.\nstatic_assert(adams_coefficients::maximum_order ==\n              AdamsBashforth::maximum_order);\n\nAdamsBashforth::AdamsBashforth(const std::optional<size_t>& order,\n                               const Options::Context& context)\n    : order_(order) {\n  if (order_.has_value() and (*order_ < 1 or *order_ > maximum_order)) {\n    PARSE_ERROR(context,\n                \"The order for Adams-Bashforth Nth order must be 1 <= order <= \"\n                << maximum_order);\n  }\n}\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nAdamsBashforth::order() const {\n  if (order_.has_value()) {\n    return variants::TaggedVariant<Tags::FixedOrder>(*order_);\n  } else {\n    return variants::TaggedVariant<Tags::VariableOrder>(minimum_order,\n                                                        maximum_order);\n  }\n}\n\nuint64_t AdamsBashforth::number_of_substeps() const { return 1; }\n\nuint64_t AdamsBashforth::number_of_substeps_for_error() const { return 1; }\n\nsize_t AdamsBashforth::number_of_past_steps() const {\n  return order_.value_or(1) - 1;\n}\n\ndouble AdamsBashforth::stable_step() const {\n  if (not order_.has_value()) {\n    ERROR_NO_TRACE(\n        \"Variable-order time stepper does not have well-defined stable step.\");\n  }\n\n  if (*order_ == 1) {\n    return 1.;\n  }\n\n  // This is the condition that the characteristic polynomial of the\n  // recurrence relation defined by the method has the correct sign at\n  // -1.  It is not clear whether this is sufficient for all orders,\n  // but it is for the ones we support.\n  const auto& coefficients =\n      adams_coefficients::constant_adams_bashforth_coefficients(*order_);\n  double invstep = 0.;\n  for (const auto coef : coefficients) {\n    invstep = coef - invstep;\n  }\n  return 1. / invstep;\n}\n\nbool AdamsBashforth::monotonic() const { return true; }\n\nTimeStepId AdamsBashforth::next_time_id(const TimeStepId& current_id,\n                                        const TimeDelta& time_step) const {\n  ASSERT(current_id.substep() == 0, \"Adams-Bashforth should not have substeps\");\n  return current_id.next_step(time_step);\n}\n\nTimeStepId AdamsBashforth::next_time_id_for_error(\n    const TimeStepId& current_id, const TimeDelta& time_step) const {\n  return next_time_id(current_id, time_step);\n}\n\nbool AdamsBashforth::neighbor_data_required(\n    const TimeStepId& next_substep_id,\n    const TimeStepId& neighbor_data_id) const {\n  return neighbor_data_id < next_substep_id;\n}\n\nbool AdamsBashforth::neighbor_data_required(\n    const double dense_output_time, const TimeStepId& neighbor_data_id) const {\n  return evolution_less<double>{neighbor_data_id.time_runs_forward()}(\n      neighbor_data_id.substep_time(), dense_output_time);\n}\n\nvoid AdamsBashforth::pup(PUP::er& p) {\n  LtsTimeStepper::pup(p);\n  p | order_;\n}\n\nnamespace {\ntemplate <typename T>\ndouble evaluate_error(\n    const gsl::not_null<T*> scratch, const ConstUntypedHistory<T>& history,\n    const StepperErrorTolerances& tolerances,\n    const adams_coefficients::OrderVector<double>& higher_order_coefficients,\n    const adams_coefficients::OrderVector<double>& lower_order_coefficients) {\n  auto error_coefficients = higher_order_coefficients;\n  for (size_t i = 0; i < lower_order_coefficients.size(); ++i) {\n    error_coefficients[i + 1] -= lower_order_coefficients[i];\n  }\n\n  auto coefficient = error_coefficients.rbegin();\n  auto history_entry = history.rbegin();\n  *scratch = *coefficient * history_entry->derivative;\n  for (;;) {\n    ++history_entry;\n    ++coefficient;\n    if (coefficient == error_coefficients.rend()) {\n      break;\n    }\n    *scratch += *coefficient * history_entry->derivative;\n  }\n\n  return largest_stepper_error(*history.back().value, *scratch, tolerances);\n}\n}  // namespace\n\ntemplate <bool DenseOutput, typename T>\nstd::optional<StepperErrorEstimate> AdamsBashforth::update_u_common(\n    const gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n    const tmpl::conditional_t<DenseOutput, ApproximateTime, Time>& time,\n    const StepperErrorTolerances& tolerances) const {\n  ASSERT(not DenseOutput or\n             tolerances.estimates == StepperErrorTolerances::Estimates::None,\n         \"Can't compute errors in dense output.\");\n  ASSERT(history.size() >= history.integration_order(),\n         \"Incorrect data to take an order-\" << history.integration_order()\n         << \" step.  Have \" << history.size() << \" times, need at least \"\n         << history.integration_order());\n  ASSERT(history.integration_order() <= order_.value_or(maximum_order),\n         \"Requested integration order higher than integrator order\");\n\n  const auto& step_start = history.back().time_step_id.step_time();\n  const auto history_start =\n      history.end() -\n      static_cast<typename ConstUntypedHistory<T>::difference_type>(\n          history.integration_order());\n  adams_coefficients::OrderVector<Time> control_times{};\n  std::transform(history_start, history.end(),\n                 std::back_inserter(control_times),\n                 [](const auto& r) { return r.time_step_id.step_time(); });\n  const auto update_coefficients = adams_coefficients::coefficients(\n      control_times.begin(), control_times.end(), step_start, time);\n\n  std::optional<StepperErrorEstimate> error{};\n  if constexpr (not DenseOutput) {\n    if (tolerances.estimates != StepperErrorTolerances::Estimates::None) {\n      auto lower_order_coefficients = adams_coefficients::coefficients(\n          control_times.begin() + 1, control_times.end(), step_start, time);\n      error.emplace(step_start, time - step_start,\n                    history.integration_order() - 1,\n                    evaluate_error(u, history, tolerances, update_coefficients,\n                                   lower_order_coefficients));\n\n      if (tolerances.estimates ==\n          StepperErrorTolerances::Estimates::AllOrders) {\n        for (size_t error_order = history.integration_order() - 2;\n             error_order != std::numeric_limits<size_t>::max();\n             --error_order) {\n          const auto higher_order_coefficients = lower_order_coefficients;\n          lower_order_coefficients = adams_coefficients::coefficients(\n              control_times.end() -\n                  static_cast<decltype(control_times)::difference_type>(\n                      error_order),\n              control_times.end(), step_start, time);\n          gsl::at(error->errors, error_order)\n              .emplace(evaluate_error(u, history, tolerances,\n                                      higher_order_coefficients,\n                                      lower_order_coefficients));\n        }\n      }\n    }\n\n    // Dense output adds to the existing value, but the main step overwrites.\n    *u = *history.back().value;\n  }\n\n  auto coefficient = update_coefficients.begin();\n  for (auto history_entry = history_start;\n       history_entry != history.end();\n       ++history_entry, ++coefficient) {\n    *u += *coefficient * history_entry->derivative;\n  }\n  return error;\n}\n\ntemplate <typename T>\nstd::optional<StepperErrorEstimate> AdamsBashforth::update_u_impl(\n    const gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n    const TimeDelta& time_step,\n    const StepperErrorTolerances& tolerances) const {\n  const Time next_time = history.back().time_step_id.step_time() + time_step;\n  return update_u_common<false>(u, history, next_time, tolerances);\n}\n\ntemplate <typename T>\nvoid AdamsBashforth::clean_history_impl(\n    const MutableUntypedHistory<T>& history) const {\n  while (history.size() >= history.integration_order()) {\n    history.pop_front();\n  }\n  if (history.size() > 1) {\n    history.discard_value(history[history.size() - 2].time_step_id);\n  }\n}\n\ntemplate <typename T>\nbool AdamsBashforth::dense_update_u_impl(const gsl::not_null<T*> u,\n                                         const ConstUntypedHistory<T>& history,\n                                         const double time) const {\n  update_u_common<true>(u, history, ApproximateTime{time},\n                        StepperErrorTolerances{});\n  return true;\n}\n\ntemplate <typename T>\nbool AdamsBashforth::can_change_step_size_impl(\n    const TimeStepId& time_id, const ConstUntypedHistory<T>& history) const {\n  // We need to prevent the next step from occurring at the same time\n  // as one already in the history.  The self-start code ensures this\n  // can't happen during self-start, and it clearly can't happen\n  // during normal evolution where the steps are monotonic, but during\n  // the transition between them we have to worry about a step being\n  // placed on a self-start time.  The self-start algorithm guarantees\n  // the final state is safe for constant-time-step evolution, so we\n  // just force that until we've passed all the self-start times.\n  const evolution_less_equal<Time> less_equal{time_id.time_runs_forward()};\n  return not ::SelfStart::is_self_starting(time_id) and\n         alg::all_of(history, [&](const auto& record) {\n           return less_equal(record.time_step_id.step_time(),\n                             time_id.step_time());\n         });\n}\n\ntemplate <typename T>\nvoid AdamsBashforth::add_boundary_delta_impl(\n    const gsl::not_null<T*> result,\n    const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n    const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n    const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n    const TimeDelta& time_step) const {\n  for (size_t i = 0; i < local_times.size(); ++i) {\n    ASSERT(not order_.has_value() or\n               local_times.integration_order(i) == *order_ or\n               ::SelfStart::is_self_starting(local_times[i]),\n           \"Incorrect local order \" << local_times.integration_order(i)\n           << \" at time \" << local_times[i]);\n  }\n  for (size_t i = 0; i < remote_times.size(); ++i) {\n    ASSERT(not order_.has_value() or\n               remote_times.integration_order(i) == *order_ or\n               ::SelfStart::is_self_starting(remote_times[i]),\n           \"Incorrect remote order \" << remote_times.integration_order(i)\n           << \" at time \" << remote_times[i]);\n  }\n\n  const auto step_start = local_times.back().step_time();\n  const auto lts_coefficients = adams_lts::lts_coefficients(\n      local_times, remote_times, step_start, step_start + time_step,\n      adams_lts::SchemeType::Explicit, adams_lts::SchemeType::Explicit, 0, 0);\n  adams_lts::apply_coefficients(result, lts_coefficients, coupling);\n}\n\nvoid AdamsBashforth::clean_boundary_history_impl(\n    const TimeSteppers::MutableBoundaryHistoryTimes& local_times,\n    const TimeSteppers::MutableBoundaryHistoryTimes& remote_times) const {\n  // Unlike for the volume terms, we need to keep an extra point in\n  // case the order changes.  This is because the order stored in the\n  // volume history represents, when the cleanup is run, the order of\n  // the next step, while the orders in the boundary history are\n  // stored per-step, so we have the order for the step we've just\n  // taken, which may be one lower than what we need.\n  const size_t local_order =\n      local_times.integration_order(local_times.size() - 1);\n  size_t needed_local_values = local_order - 1;\n  if (local_order < maximum_order and not order_.has_value()) {\n    ++needed_local_values;\n  }\n  while (local_times.size() > needed_local_values) {\n    local_times.pop_front();\n  }\n\n  const size_t needed_remote_values =\n      remote_times.integration_order(remote_times.size() - 1);\n  // We're guaranteed to have a new local value inserted before the\n  // next use, but not a new remote value, so we need to keep one more\n  // of these.  This also takes care of the extra point needed for\n  // variable-order, since the order can't change unless we get a new\n  // value.\n  while (remote_times.size() > needed_remote_values) {\n    remote_times.pop_front();\n  }\n}\n\ntemplate <typename T>\nvoid AdamsBashforth::boundary_dense_output_impl(\n    const gsl::not_null<T*> result,\n    const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n    const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n    const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n    const double time) const {\n  for (size_t i = 0; i < local_times.size(); ++i) {\n    ASSERT(not order_.has_value() or\n               local_times.integration_order(i) == *order_ or\n               ::SelfStart::is_self_starting(local_times[i]),\n           \"Incorrect local order \" << local_times.integration_order(i)\n           << \" at time \" << local_times[i]);\n  }\n  for (size_t i = 0; i < remote_times.size(); ++i) {\n    ASSERT(not order_.has_value() or\n               remote_times.integration_order(i) == *order_ or\n               ::SelfStart::is_self_starting(remote_times[i]),\n           \"Incorrect remote order \" << remote_times.integration_order(i)\n           << \" at time \" << remote_times[i]);\n  }\n\n  if (local_times.back().step_time().value() == time) {\n    // Nothing to do.  The requested time is the start of the step,\n    // which is the input value of `result`.\n    return;\n  }\n  const auto small_step_start =\n      std::max(local_times.back(), remote_times.back()).step_time();\n  const auto lts_coefficients =\n      adams_lts::lts_coefficients(\n          local_times, remote_times, local_times.back().step_time(),\n          small_step_start, adams_lts::SchemeType::Explicit,\n          adams_lts::SchemeType::Explicit, 0, 0) +\n      adams_lts::lts_coefficients(local_times, remote_times, small_step_start,\n                                  ApproximateTime{time},\n                                  adams_lts::SchemeType::Explicit,\n                                  adams_lts::SchemeType::Explicit, 0, 0);\n  adams_lts::apply_coefficients(result, lts_coefficients, coupling);\n}\n\nbool operator==(const AdamsBashforth& lhs, const AdamsBashforth& rhs) {\n  return lhs.order_ == rhs.order_;\n}\n\nbool operator!=(const AdamsBashforth& lhs, const AdamsBashforth& rhs) {\n  return not(lhs == rhs);\n}\n\nTIME_STEPPER_DEFINE_OVERLOADS(AdamsBashforth)\nLTS_TIME_STEPPER_DEFINE_OVERLOADS(AdamsBashforth)\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::AdamsBashforth::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/AdamsBashforth.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nstruct ApproximateTime;\nclass TimeDelta;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace TimeSteppers {\ntemplate <typename T>\nclass BoundaryHistoryEvaluator;\nclass ConstBoundaryHistoryTimes;\ntemplate <typename T>\nclass ConstUntypedHistory;\nclass MutableBoundaryHistoryTimes;\ntemplate <typename T>\nclass MutableUntypedHistory;\n}  // namespace TimeSteppers\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace TimeSteppers {\n\n/*!\n * \\ingroup TimeSteppersGroup\n *\n * An Nth order Adams-Bashforth time stepper.\n *\n * The stable step size factors for different orders are given by:\n *\n * <table class=\"doxtable\">\n *  <tr>\n *    <th> %Order </th>\n *    <th> CFL Factor </th>\n *  </tr>\n *  <tr>\n *    <td> 1 </td>\n *    <td> 1 </td>\n *  </tr>\n *  <tr>\n *    <td> 2 </td>\n *    <td> 1 / 2 </td>\n *  </tr>\n *  <tr>\n *    <td> 3 </td>\n *    <td> 3 / 11 </td>\n *  </tr>\n *  <tr>\n *    <td> 4 </td>\n *    <td> 3 / 20 </td>\n *  </tr>\n *  <tr>\n *    <td> 5 </td>\n *    <td> 45 / 551 </td>\n *  </tr>\n *  <tr>\n *    <td> 6 </td>\n *    <td> 5 / 114 </td>\n *  </tr>\n *  <tr>\n *    <td> 7 </td>\n *    <td> 945 / 40663 </td>\n *  </tr>\n *  <tr>\n *    <td> 8 </td>\n *    <td> 945 / 77432 </td>\n *  </tr>\n * </table>\n *\n * \\section lts Local time stepping calculation\n *\n * \\f$\\newcommand\\tL{t^L}\\newcommand\\tR{t^R}\\newcommand\\tU{\\tilde{t}\\!}\n * \\newcommand\\mat{\\mathbf}\\f$\n *\n * Suppose the local and remote sides of the interface are evaluated\n * at times \\f$\\ldots, \\tL_{-1}, \\tL_0, \\tL_1, \\ldots\\f$ and\n * \\f$\\ldots, \\tR_{-1}, \\tR_0, \\tR_1, \\ldots\\f$, respectively, with\n * the starting location of the numbering arbitrary in each case.\n * Let the step we wish to calculate the effect of be the step from\n * \\f$\\tL_{m_S}\\f$ to \\f$\\tL_{m_S+1}\\f$.  We call the sequence\n * produced from the union of the local and remote time sequences\n * \\f$\\ldots, \\tU_{-1}, \\tU_0, \\tU_1, \\ldots\\f$.  For example, one\n * possible sequence of times is:\n * \\f{equation}\n *   \\begin{aligned}\n *     \\text{Local side:} \\\\ \\text{Union times:} \\\\ \\text{Remote side:}\n *   \\end{aligned}\n *   \\cdots\n *   \\begin{gathered}\n *     \\, \\\\ \\tU_1 \\\\ \\tR_5\n *   \\end{gathered}\n *   \\leftarrow \\Delta \\tU_1 \\rightarrow\n *   \\begin{gathered}\n *     \\tL_4 \\\\ \\tU_2 \\\\ \\,\n *   \\end{gathered}\n *   \\leftarrow \\Delta \\tU_2 \\rightarrow\n *   \\begin{gathered}\n *     \\, \\\\ \\tU_3 \\\\ \\tR_6\n *   \\end{gathered}\n *   \\leftarrow \\Delta \\tU_3 \\rightarrow\n *   \\begin{gathered}\n *    \\, \\\\ \\tU_4 \\\\ \\tR_7\n *   \\end{gathered}\n *   \\leftarrow \\Delta \\tU_4 \\rightarrow\n *   \\begin{gathered}\n *     \\tL_5 \\\\ \\tU_5 \\\\ \\,\n *   \\end{gathered}\n *   \\cdots\n * \\f}\n * We call the indices of the step's start and end times in the\n * union time sequence \\f$n_S\\f$ and \\f$n_E\\f$, respectively.  We\n * define \\f$n^L_m\\f$ to be the union-time index corresponding to\n * \\f$\\tL_m\\f$ and \\f$m^L_n\\f$ to be the index of the last local\n * time not later than \\f$\\tU_n\\f$ and similarly for the remote\n * side.  So for the above example, \\f$n^L_4 = 2\\f$ and \\f$m^R_2 =\n * 5\\f$, and if we wish to compute the step from \\f$\\tL_4\\f$ to\n * \\f$\\tL_5\\f$ we would have \\f$m_S = 4\\f$, \\f$n_S = 2\\f$, and\n * \\f$n_E = 5\\f$.\n *\n * If we wish to evaluate the change over this step to \\f$k\\f$th\n * order, we can write the change in the value as a linear\n * combination of the values of the coupling between the elements at\n * unequal times:\n * \\f{equation}\n *   \\mat{F}_{m_S} =\n *   \\mspace{-10mu}\n *   \\sum_{q^L = m_S-(k-1)}^{m_S}\n *   \\,\n *   \\sum_{q^R = m^R_{n_S}-(k-1)}^{m^R_{n_E-1}}\n *   \\mspace{-10mu}\n *   \\mat{D}_{q^Lq^R}\n *   I_{q^Lq^R},\n * \\f}\n * where \\f$\\mat{D}_{q^Lq^R}\\f$ is the coupling function evaluated\n * between data from \\f$\\tL_{q^L}\\f$ and \\f$\\tR_{q^R}\\f$.  The\n * coefficients can be written as the sum of three terms,\n * \\f{equation}\n *   I_{q^Lq^R} = I^E_{q^Lq^R} + I^R_{q^Lq^R} + I^L_{q^Lq^R},\n * \\f}\n * which can be interpreted as a contribution from equal-time\n * evaluations and contributions related to the remote and local\n * evaluation times.  These are given by\n * \\f{align}\n *   I^E_{q^Lq^R} &=\n *   \\mspace{-10mu}\n *   \\sum_{n=n_S}^{\\min\\left\\{n_E, n^L+k\\right\\}-1}\n *   \\mspace{-10mu}\n *   \\tilde{\\alpha}_{n,n-n^L} \\Delta \\tU_n\n *   &&\\text{if $\\tL_{q^L} = \\tR_{q^R}$, otherwise 0}\n *   \\\\\n *   I^R_{q^Lq^R} &=\n *   \\ell_{q^L - m_S + k}\\!\\left(\n *     \\tU_{n^R}; \\tL_{m_S - (k-1)}, \\ldots, \\tL_{m_S}\\right)\n *   \\mspace{-10mu}\n *   \\sum_{n=\\max\\left\\{n_S, n^R\\right\\}}\n *       ^{\\min\\left\\{n_E, n^R+k\\right\\}-1}\n *   \\mspace{-10mu}\n *   \\tilde{\\alpha}_{n,n-n^R} \\Delta \\tU_n\n *   &&\\text{if $\\tR_{q^R}$ is not in $\\{\\tL_{\\vphantom{|}\\cdots}\\}$,\n *     otherwise 0}\n *   \\\\\n *   I^L_{q^Lq^R} &=\n *   \\mspace{-10mu}\n *   \\sum_{n=\\max\\left\\{n_S, n^R\\right\\}}\n *       ^{\\min\\left\\{n_E, n^L+k, n^R_{q^R+k}\\right\\}-1}\n *   \\mspace{-10mu}\n *   \\ell_{q^R - m^R_n + k}\\!\\left(\\tU_{n^L};\n *     \\tR_{m^R_n - (k-1)}, \\ldots, \\tR_{m^R_n}\\right)\n *   \\tilde{\\alpha}_{n,n-n^L} \\Delta \\tU_n\n *   &&\\text{if $\\tL_{q^L}$ is not in $\\{\\tR_{\\vphantom{|}\\cdots}\\}$,\n *     otherwise 0,}\n * \\f}\n * where for brevity we write \\f$n^L = n^L_{q^L}\\f$ and \\f$n^R =\n * n^R_{q^R}\\f$, and where \\f$\\ell_a(t; x_1, \\ldots, x_k)\\f$ a\n * Lagrange interpolating polynomial and \\f$\\tilde{\\alpha}_{nj}\\f$\n * is the \\f$j\\f$th coefficient for an Adams-Bashforth step over the\n * union times from step \\f$n\\f$ to step \\f$n+1\\f$.\n */\nclass AdamsBashforth : public LtsTimeStepper {\n public:\n  static constexpr const size_t minimum_order = 1;\n  static constexpr const size_t maximum_order = 8;\n\n  struct Order {\n    using type = Options::Auto<size_t>;\n    static constexpr Options::String help = {\"Convergence order\"};\n  };\n  using options = tmpl::list<Order>;\n  static constexpr Options::String help = {\n      \"An Adams-Bashforth Nth order time-stepper.\"};\n\n  AdamsBashforth() = default;\n  explicit AdamsBashforth(const std::optional<size_t>& order,\n                          const Options::Context& context = {});\n  AdamsBashforth(const AdamsBashforth&) = default;\n  AdamsBashforth& operator=(const AdamsBashforth&) = default;\n  AdamsBashforth(AdamsBashforth&&) = default;\n  AdamsBashforth& operator=(AdamsBashforth&&) = default;\n  ~AdamsBashforth() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  uint64_t number_of_substeps() const override;\n\n  uint64_t number_of_substeps_for_error() const override;\n\n  size_t number_of_past_steps() const override;\n\n  double stable_step() const override;\n\n  bool monotonic() const override;\n\n  TimeStepId next_time_id(const TimeStepId& current_id,\n                          const TimeDelta& time_step) const override;\n\n  TimeStepId next_time_id_for_error(const TimeStepId& current_id,\n                                    const TimeDelta& time_step) const override;\n\n  bool neighbor_data_required(\n      const TimeStepId& next_substep_id,\n      const TimeStepId& neighbor_data_id) const override;\n\n  bool neighbor_data_required(\n      double dense_output_time,\n      const TimeStepId& neighbor_data_id) const override;\n\n  WRAPPED_PUPable_decl_template(AdamsBashforth);  // NOLINT\n\n  explicit AdamsBashforth(CkMigrateMessage* /*unused*/) {}\n\n  // clang-tidy: do not pass by non-const reference\n  void pup(PUP::er& p) override;  // NOLINT\n\n private:\n  friend bool operator==(const AdamsBashforth& lhs, const AdamsBashforth& rhs);\n\n  template <bool DenseOutput, typename T>\n  std::optional<StepperErrorEstimate> update_u_common(\n      gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n      const tmpl::conditional_t<DenseOutput, ApproximateTime, Time>& time,\n      const StepperErrorTolerances& tolerances) const;\n\n  template <typename T>\n  std::optional<StepperErrorEstimate> update_u_impl(\n      gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n      const TimeDelta& time_step,\n      const StepperErrorTolerances& tolerances = {}) const;\n\n  template <typename T>\n  void clean_history_impl(const MutableUntypedHistory<T>& history) const;\n\n  template <typename T>\n  bool dense_update_u_impl(gsl::not_null<T*> u,\n                           const ConstUntypedHistory<T>& history,\n                           double time) const;\n\n  template <typename T>\n  bool can_change_step_size_impl(const TimeStepId& time_id,\n                                 const ConstUntypedHistory<T>& history) const;\n\n  template <typename T>\n  void add_boundary_delta_impl(\n      gsl::not_null<T*> result,\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n      const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n      const TimeDelta& time_step) const;\n\n  void clean_boundary_history_impl(\n      const TimeSteppers::MutableBoundaryHistoryTimes& local_times,\n      const TimeSteppers::MutableBoundaryHistoryTimes& remote_times)\n      const override;\n\n  template <typename T>\n  void boundary_dense_output_impl(\n      gsl::not_null<T*> result,\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n      const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n      const double time) const;\n\n  TIME_STEPPER_DECLARE_OVERLOADS\n  LTS_TIME_STEPPER_DECLARE_OVERLOADS\n\n  std::optional<size_t> order_{};\n};\n\nbool operator!=(const AdamsBashforth& lhs, const AdamsBashforth& rhs);\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/AdamsCoefficients.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/AdamsCoefficients.hpp\"\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nnamespace TimeSteppers::adams_coefficients {\nOrderVector<double> constant_adams_bashforth_coefficients(const size_t order) {\n  switch (order) {\n    case 0:\n      return {};\n    case 1:\n      return {1.};\n    case 2:\n      return {-0.5, 1.5};\n    case 3:\n      return {5.0 / 12.0, -4.0 / 3.0, 23.0 / 12.0};\n    case 4:\n      return {-3.0 / 8.0, 37.0 / 24.0, -59.0 / 24.0, 55.0 / 24.0};\n    case 5:\n      return {251.0 / 720.0, -637.0 / 360.0, 109.0 / 30.0, -1387.0 / 360.0,\n              1901.0 / 720.0};\n    case 6:\n      return {-95.0 / 288.0,  959.0 / 480.0,   -3649.0 / 720.0,\n              4991.0 / 720.0, -2641.0 / 480.0, 4277.0 / 1440.0};\n    case 7:\n      return {19087.0 / 60480.0, -5603.0 / 2520.0,   135713.0 / 20160.0,\n              -10754.0 / 945.0,  235183.0 / 20160.0, -18637.0 / 2520.0,\n              198721.0 / 60480.0};\n    case 8:\n      return {-5257.0 / 17280.0,     32863.0 / 13440.0,   -115747.0 / 13440.0,\n              2102243.0 / 120960.0,  -296053.0 / 13440.0, 242653.0 / 13440.0,\n              -1152169.0 / 120960.0, 16083.0 / 4480.0};\n    default:\n      ERROR(\"Bad order: \" << order);\n  }\n}\n\nOrderVector<double> constant_adams_moulton_coefficients(const size_t order) {\n  switch (order) {\n    case 0:\n      return {};\n    case 1:\n      return {1.0};\n    case 2:\n      return {0.5, 0.5};\n    case 3:\n      return {-1.0 / 12.0, 2.0 / 3.0, 5.0 / 12.0};\n    case 4:\n      return {1.0 / 24.0, -5.0 / 24.0, 19.0 / 24.0, 3.0 / 8.0};\n    case 5:\n      return {-19.0 / 720.0, 53.0 / 360.0, -11.0 / 30.0, 323.0 / 360.0,\n              251.0 / 720.0};\n    case 6:\n      return {3.0 / 160.0,    -173.0 / 1440.0, 241.0 / 720.0,\n              -133.0 / 240.0, 1427.0 / 1440.0, 95.0 / 288.0};\n    case 7:\n      return {-863.0 / 60480.0, 263.0 / 2520.0,     -6737.0 / 20160.0,\n              586.0 / 945.0,    -15487.0 / 20160.0, 2713.0 / 2520.0,\n              19087.0 / 60480.0};\n    case 8:\n      return {275.0 / 24192.0,     -11351.0 / 120960.0, 1537.0 / 4480.0,\n              -88547.0 / 120960.0, 123133.0 / 120960.0, -4511.0 / 4480.0,\n              139849.0 / 120960.0, 5257.0 / 17280.0};\n    default:\n      ERROR(\"Bad order: \" << order);\n  }\n}\n\ntemplate <typename T>\nOrderVector<T> variable_coefficients(OrderVector<T> control_times,\n                                     const T& step_start, const T& step_end) {\n  // The coefficients are, for each j,\n  // \\int_{step_start}^{step_end} dt ell_j(t; control_times),\n\n  // Shift the step to be near zero to minimize the roundoff error\n  // from the final polynomial evaluations.\n  alg::for_each(control_times, [&](T& t) { t -= step_start; });\n\n  const size_t order = control_times.size();\n  OrderVector<T> result;\n  for (size_t j = 0; j < order; ++j) {\n    // Calculate coefficients of the Lagrange interpolating polynomials,\n    // in the standard a_0 + a_1 t + a_2 t^2 + ... form.\n    OrderVector<T> poly(order, 0);\n\n    poly[0] = 1;\n\n    for (size_t m = 0; m < order; ++m) {\n      if (m == j) {\n        continue;\n      }\n      const T denom = 1 / (control_times[j] - control_times[m]);\n      for (size_t i = m < j ? m + 1 : m; i > 0; --i) {\n        poly[i] = (poly[i - 1] - poly[i] * control_times[m]) * denom;\n      }\n      poly[0] *= -control_times[m] * denom;\n    }\n\n    // Integrate p(t), term by term.  We choose the constant of\n    // integration so the indefinite integral is zero at t=0.  We do\n    // not adjust the indexing, so the t^n term in the integral is in\n    // the (n-1)th entry of the vector (as opposed to the nth entry\n    // before integrating).\n    for (size_t m = 0; m < order; ++m) {\n      poly[m] /= m + 1;\n    }\n    result.push_back((step_end - step_start) *\n                     evaluate_polynomial(poly, step_end - step_start));\n  }\n  return result;\n}\n\n#define TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                \\\n  template OrderVector<TYPE(data)> variable_coefficients(                   \\\n      OrderVector<TYPE(data)> control_times, const TYPE(data) & step_start, \\\n      const TYPE(data) & step_end);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, Rational))\n\n#undef INSTANTIATE\n#undef TYPE\n}  // namespace TimeSteppers::adams_coefficients\n"
  },
  {
    "path": "src/Time/TimeSteppers/AdamsCoefficients.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/container/static_vector.hpp>\n#include <cmath>\n#include <cstddef>\n#include <iterator>\n#include <type_traits>\n\n#include \"Time/SlabRoundingError.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n\n/// \\cond\nstruct ApproximateTime;\n/// \\endcond\n\n/// Helpers for calculating Adams coefficients\nnamespace TimeSteppers::adams_coefficients {\nconstexpr size_t maximum_order = 8;\n\n/// A vector holding one entry per order of integration.\ntemplate <typename T>\nusing OrderVector = boost::container::static_vector<T, maximum_order>;\n\n/// The standard Adams-Bashforth coefficients for constant step size,\n/// ordered from oldest to newest time, as one would find in a\n/// reference table (except likely in the opposite order).\nOrderVector<double> constant_adams_bashforth_coefficients(size_t order);\n\n/// The standard Adams-Moulton coefficients for constant step size,\n/// ordered from oldest to newest time, as one would find in a\n/// reference table (except likely in the opposite order).\nOrderVector<double> constant_adams_moulton_coefficients(size_t order);\n\n/// \\brief Generate coefficients for an Adams step.\n///\n/// The coefficients are for a step using derivatives at \\p\n/// control_times, with the entries in the result vector corresponding\n/// to the passed times in order.  The result includes the overall\n/// factor of step size, so, for example, the coefficients for Euler's\n/// method (`control_times = {0}, step_start=0, step_end=dt`) would be\n/// `{dt}`, not `{1}`.\n///\n/// No requirements are imposed on \\p control_times, except that the\n/// entries are all distinct.\n///\n/// Only `T = double` is used by the time steppers, but `T = Rational`\n/// can be used to generate coefficient tables.\ntemplate <typename T>\nOrderVector<T> variable_coefficients(OrderVector<T> control_times,\n                                     const T& step_start, const T& step_end);\n\n/// \\brief Get coefficients for a time step.\n///\n/// Arguments are an iterator pair to past times (of type `Time`),\n/// with the most recent last, and the start and end of the time step\n/// to take, with the end a `Time` or `ApproximateTime`.  This\n/// performs the same calculation as `variable_coefficients`, except\n/// that it works with `Time`s and will detect and optimize the\n/// constant-step-size case.\ntemplate <typename Iterator, typename TimeType>\nOrderVector<double> coefficients(const Iterator& times_begin,\n                                 const Iterator& times_end,\n                                 const Time& step_start,\n                                 const TimeType& step_end) {\n  static_assert(std::is_same_v<TimeType, Time> or\n                std::is_same_v<TimeType, ApproximateTime>);\n  if (times_begin == times_end) {\n    return {};\n  }\n  const double step_size = (step_end - step_start).value();\n  bool constant_step_size = true;\n  // We shift the control times to be near zero, which gives smaller\n  // errors from the variable_coefficients function.\n  OrderVector<double> control_times{0.0};\n  Time previous_time = *times_begin;\n  for (auto t = std::next(times_begin); t != times_end; ++t) {\n    const Time this_time = *t;\n    const double this_step = (this_time - previous_time).value();\n    control_times.push_back(control_times.back() + this_step);\n    if (constant_step_size and\n        std::abs(this_step - step_size) > slab_rounding_error(this_time)) {\n      constant_step_size = false;\n    }\n    previous_time = this_time;\n  }\n  if (constant_step_size and step_start == previous_time) {\n    auto result = constant_adams_bashforth_coefficients(control_times.size());\n    alg::for_each(result, [&](double& coef) { coef *= step_size; });\n    return result;\n  } else if (constant_step_size and step_end == previous_time) {\n    auto result = constant_adams_moulton_coefficients(control_times.size());\n    alg::for_each(result, [&](double& coef) { coef *= step_size; });\n    return result;\n  }\n\n  return variable_coefficients(\n      control_times,\n      control_times.back() + (step_start - previous_time).value(),\n      control_times.back() + (step_end - previous_time).value());\n}\n}  // namespace TimeSteppers::adams_coefficients\n"
  },
  {
    "path": "src/Time/TimeSteppers/AdamsLts.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/AdamsLts.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LagrangePolynomial.hpp\"\n#include \"Time/ApproximateTime.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsCoefficients.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TimeSteppers::adams_lts {\nTime exact_substep_time(const TimeStepId& id) {\n  const Time result =\n      id.substep() == 0 ? id.step_time() : id.step_time() + id.step_size();\n  ASSERT(result.value() == id.substep_time(), \"Substep not at expected time.\");\n  return result;\n}\n\nnamespace {\ntemplate <typename T>\nusing OrderVector = adams_coefficients::OrderVector<T>;\n\ntemplate <typename Op>\nLtsCoefficients& add_assign_impl(LtsCoefficients& a, const LtsCoefficients& b,\n                                 const Op& op) {\n  const auto key_equal = [](const LtsCoefficients::value_type& l,\n                            const LtsCoefficients::value_type& r) {\n    return get<0>(l) == get<0>(r) and get<1>(l) == get<1>(r);\n  };\n  const auto key_less = [](const LtsCoefficients::value_type& l,\n                           const LtsCoefficients::value_type& r) {\n    return get<0>(l) < get<0>(r) or\n           (get<0>(l) == get<0>(r) and get<1>(l) < get<1>(r));\n  };\n\n  // Three passes: first the common entries, then the new entries, and\n  // then remove zeros.\n  size_t common_entries = 0;\n  {\n    auto a_it = a.begin();\n    auto b_it = b.begin();\n    while (a_it != a.end() and b_it != b.end()) {\n      if (key_less(*a_it, *b_it)) {\n        ++a_it;\n      } else if (key_less(*b_it, *a_it)) {\n        ++b_it;\n      } else {\n        ++common_entries;\n        get<2>(*a_it) += op(get<2>(*b_it));\n        // Zero any values that are just roundoff.  They will be\n        // removed below.\n        constexpr double tolerance =\n            100.0 * std::numeric_limits<double>::epsilon();\n        if (std::abs(get<2>(*a_it)) < tolerance * std::abs(get<2>(*b_it))) {\n          get<2>(*a_it) = 0.0;\n        }\n        ++a_it;\n        ++b_it;\n      }\n    }\n  }\n  a.resize(a.size() + b.size() - common_entries);\n  {\n    auto write = a.rbegin();\n    auto a_it = write + static_cast<LtsCoefficients::difference_type>(\n                            b.size() - common_entries);\n    auto b_it = b.rbegin();\n    while (b_it != b.rend()) {\n      if (a_it == a.rend() or key_less(*a_it, *b_it)) {\n        *write = *b_it;\n        get<2>(*write) = op(get<2>(*write));\n        ++b_it;\n      } else {\n        if (key_equal(*a_it, *b_it)) {\n          ++b_it;\n        }\n        *write = *a_it;\n        ++a_it;\n      }\n      ++write;\n    }\n  }\n  {\n    auto write = a.begin();\n    for (const auto& entry : a) {\n      if (get<2>(entry) != 0.0) {\n        *write++ = entry;\n      }\n    }\n    a.erase(write, a.end());\n  }\n  return a;\n}\n}  // namespace\n\nLtsCoefficients& operator+=(LtsCoefficients& a, const LtsCoefficients& b) {\n  return add_assign_impl(a, b, [](const double x) { return x; });\n}\nLtsCoefficients& operator-=(LtsCoefficients& a, const LtsCoefficients& b) {\n  return add_assign_impl(a, b, [](const double x) { return -x; });\n}\n\nLtsCoefficients operator+(LtsCoefficients&& a, LtsCoefficients&& b) {\n  return std::move(a += b);\n}\nLtsCoefficients operator+(LtsCoefficients&& a, const LtsCoefficients& b) {\n  return std::move(a += b);\n}\nLtsCoefficients operator+(const LtsCoefficients& a, LtsCoefficients&& b) {\n  return std::move(b += a);\n}\nLtsCoefficients operator+(const LtsCoefficients& a, const LtsCoefficients& b) {\n  auto a2 = a;\n  return std::move(a2 += b);\n}\n\nLtsCoefficients operator-(LtsCoefficients&& a, LtsCoefficients&& b) {\n  return std::move(a -= b);\n}\nLtsCoefficients operator-(LtsCoefficients&& a, const LtsCoefficients& b) {\n  return std::move(a -= b);\n}\nLtsCoefficients operator-(const LtsCoefficients& a, const LtsCoefficients& b) {\n  auto a2 = a;\n  return std::move(a2 -= b);\n}\nLtsCoefficients operator-(const LtsCoefficients& a, LtsCoefficients&& b) {\n  alg::for_each(b, [](auto& coef) { get<2>(coef) *= -1.0; });\n  return std::move(b += a);\n}\n\ntemplate <typename T>\nvoid apply_coefficients(const gsl::not_null<T*> result,\n                        const LtsCoefficients& coefficients,\n                        const BoundaryHistoryEvaluator<T>& coupling) {\n  for (const auto& term : coefficients) {\n    *result += get<2>(term) * coupling(get<0>(term), get<1>(term));\n  }\n}\n\nnamespace {\n// Collect the ids used for interpolating during a step to `end_time`\n// from `times`.\n//\n// For implicit schemes, the step containing (or ending at) `end_time`\n// must have predictor data available.\ntemplate <typename TimeType>\nOrderVector<TimeStepId> find_relevant_ids(\n    const ConstBoundaryHistoryTimes& times, const TimeType& end_time,\n    const SchemeType scheme_type, const int order_offset) {\n  OrderVector<TimeStepId> ids{};\n  using difference_type = std::iterator_traits<\n      ConstBoundaryHistoryTimes::const_iterator>::difference_type;\n  const evolution_less<> less{times.front().time_runs_forward()};\n  // Can't do a binary search because times are not sorted during self-start.\n  auto used_range_end = times.end();\n  for (;;) {\n    ASSERT(used_range_end != times.begin(),\n           \"All times in history were after \" << end_time);\n    if (less((used_range_end - 1)->step_time(), end_time)) {\n      break;\n    }\n    --used_range_end;\n  }\n  const auto last_step =\n      static_cast<size_t>(used_range_end - times.begin() - 1);\n  const auto order =\n      static_cast<difference_type>(times.integration_order(last_step)) +\n      order_offset;\n  const auto number_of_past_steps =\n      order - (scheme_type == SchemeType::Implicit ? 1 : 0);\n  ASSERT(used_range_end - times.begin() >= number_of_past_steps,\n         \"Insufficient past data.\");\n  std::copy(used_range_end - number_of_past_steps, used_range_end,\n            std::back_inserter(ids));\n  if (scheme_type == SchemeType::Implicit) {\n    ASSERT(times.number_of_substeps(last_step) == 2,\n           \"Must have substep data for implicit stepping.\");\n    ids.push_back(times[{last_step, 1}]);\n  }\n  return ids;\n}\n\n// Choose the relevant times from `local` and `remote` for defining\n// the small steps, using the specified schemes for interpolation on\n// the local and remote sides.\n//\n// This is the `num_steps` most recent values from the union of the\n// `local` and `remote` times, excluding any values that should only\n// be used for interpolation.\nOrderVector<Time> merge_to_small_steps(const OrderVector<Time>& local,\n                                       const OrderVector<Time>& remote,\n                                       const evolution_less<Time>& less,\n                                       const SchemeType local_scheme,\n                                       const SchemeType remote_scheme,\n                                       const size_t num_steps) {\n  OrderVector<Time> small_steps(num_steps);\n  auto local_it = local.rbegin();\n  auto remote_it = remote.rbegin();\n\n  ASSERT(not(local_scheme == SchemeType::Implicit and\n             remote_scheme == SchemeType::Explicit and\n             less(*local_it, *remote_it)),\n         \"Explicit time \" << *remote_it << \" after implicit \" << *local_it);\n  ASSERT(not(remote_scheme == SchemeType::Implicit and\n             local_scheme == SchemeType::Explicit and\n             less(*remote_it, *local_it)),\n         \"Explicit time \" << *local_it << \" after implicit \" << *remote_it);\n\n  if (local_scheme == SchemeType::Implicit and\n      remote_scheme == SchemeType::Implicit) {\n    // If both the interpolation schemes are implicit, we will get\n    // two times after the small step we are working on, one from\n    // each.  One of them (if they are different) belongs to a later\n    // small step, and we should ignore it.  If they are the same,\n    // ignoring one of them is harmless.\n    if (less(*local_it, *remote_it)) {\n      ++remote_it;\n    } else {\n      ++local_it;\n    }\n  }\n\n  for (auto out = small_steps.rbegin(); out != small_steps.rend(); ++out) {\n    if (local_it == local.rend()) {\n      ASSERT(remote_it != remote.rend(), \"Ran out of data\");\n      *out = *remote_it;\n      ++remote_it;\n    } else if (remote_it == remote.rend()) {\n      *out = *local_it;\n      ++local_it;\n    } else {\n      *out = std::max(*local_it, *remote_it, less);\n      if (*local_it == *out) {\n        ++local_it;\n      }\n      if (*remote_it == *out) {\n        ++remote_it;\n      }\n    }\n  }\n  return small_steps;\n}\n\n// Evaluate the Lagrange interpolating polynomials with the given\n// `control_times` at `time`.  The returned vector contains the values\n// of all the Lagrange polynomials.\ntemplate <typename TimeType>\nOrderVector<double> interpolation_coefficients(\n    const OrderVector<Time>& control_times, const TimeType& time) {\n  if constexpr (std::is_same_v<TimeType, Time>) {\n    // Skip the Lagrange polynomial calculations if we are evaluating\n    // at a control time.  This should be common.\n    for (size_t i = 0; i < control_times.size(); ++i) {\n      if (control_times[i] == time) {\n        OrderVector<double> coefficients(control_times.size(), 0.0);\n        coefficients[i] = 1.0;\n        return coefficients;\n      }\n    }\n  }\n\n  OrderVector<double> control_times_fp(control_times.size());\n  alg::transform(control_times, control_times_fp.begin(),\n                 [](const Time& t) { return t.value(); });\n\n  OrderVector<double> coefficients{};\n  for (size_t i = 0; i < control_times.size(); ++i) {\n    coefficients.push_back(lagrange_polynomial(\n        i, time.value(), control_times_fp.begin(), control_times_fp.end()));\n  }\n  return coefficients;\n}\n\ntemplate <typename TimeType>\nLtsCoefficients lts_coefficients_for_gts(\n    const OrderVector<TimeStepId>& control_ids, const Time& start_time,\n    const TimeType& end_time) {\n  // The sides are stepping at the same rate, so no LTS is happening\n  // at this boundary.\n  OrderVector<Time> control_times(control_ids.size());\n  alg::transform(control_ids, control_times.begin(), exact_substep_time);\n\n  const OrderVector<double> gts_coefficients = adams_coefficients::coefficients(\n      control_times.begin(), control_times.end(), start_time, end_time);\n  LtsCoefficients lts_coefficients{};\n  for (size_t step = 0; step < gts_coefficients.size(); ++step) {\n    lts_coefficients.emplace_back(control_ids[step], control_ids[step],\n                                  gts_coefficients[step]);\n  }\n  return lts_coefficients;\n}\n}  // namespace\n\ntemplate <typename TimeType>\nLtsCoefficients lts_coefficients(const ConstBoundaryHistoryTimes& local_times,\n                                 const ConstBoundaryHistoryTimes& remote_times,\n                                 const Time& start_time,\n                                 const TimeType& end_time,\n                                 const SchemeType local_scheme,\n                                 const SchemeType remote_scheme,\n                                 const int local_order_offset,\n                                 const int remote_order_offset) {\n  ASSERT(local_order_offset == 0 or local_order_offset == -1,\n         \"Must be 0 or -1, not \" << local_order_offset);\n  ASSERT(remote_order_offset == 0 or remote_order_offset == -1,\n         \"Must be 0 or -1, not \" << remote_order_offset);\n  if (start_time == end_time) {\n    return {};\n  }\n  const int integration_order_offset =\n      std::max(local_order_offset, remote_order_offset);\n  const evolution_less<Time> time_less{local_times.front().time_runs_forward()};\n\n  LtsCoefficients step_coefficients{};\n\n  TimeType small_step_end = end_time;\n  for (;;) {\n    const OrderVector<TimeStepId> local_ids = find_relevant_ids(\n        local_times, small_step_end, local_scheme, local_order_offset);\n    const OrderVector<TimeStepId> remote_ids = find_relevant_ids(\n        remote_times, small_step_end, remote_scheme, remote_order_offset);\n\n    // Check is the there is actually local time-stepping happening at\n    // this boundary.  Only check for the latest small step, before we\n    // have generated any coefficients.\n    if (step_coefficients.empty() and local_scheme == remote_scheme and\n        local_ids == remote_ids) {\n      return lts_coefficients_for_gts(local_ids, start_time, end_time);\n    }\n\n    OrderVector<Time> local_control_times(local_ids.size());\n    alg::transform(local_ids, local_control_times.begin(), exact_substep_time);\n    OrderVector<Time> remote_control_times(remote_ids.size());\n    alg::transform(remote_ids, remote_control_times.begin(),\n                   exact_substep_time);\n\n    const size_t local_order =\n        local_ids.size() - static_cast<size_t>(local_order_offset);\n    const size_t remote_order =\n        remote_ids.size() - static_cast<size_t>(remote_order_offset);\n    const size_t integration_order =\n        std::max(local_order, remote_order) +\n        static_cast<size_t>(integration_order_offset);\n\n    const OrderVector<Time> small_step_times = merge_to_small_steps(\n        local_control_times, remote_control_times, time_less, local_scheme,\n        remote_scheme, integration_order);\n    const Time current_small_step =\n        small_step_times[small_step_times.size() -\n                         (local_scheme == SchemeType::Implicit or\n                                  remote_scheme == SchemeType::Implicit\n                              ? 2\n                              : 1)];\n    ASSERT(not time_less(current_small_step, start_time),\n           \"Reached time \" << current_small_step\n           << \" without hitting start time \" << start_time\n           << \" while iterating over small steps.  Most likely the supplied \"\n              \"start time was not a step boundary.\");\n\n    const OrderVector<double> small_step_coefficients =\n        adams_coefficients::coefficients(small_step_times.begin(),\n                                         small_step_times.end(),\n                                         current_small_step, small_step_end);\n\n    for (size_t contributing_small_step = 0;\n         contributing_small_step < small_step_times.size();\n         ++contributing_small_step) {\n      const OrderVector<double> local_interpolation_coefficients =\n          interpolation_coefficients(local_control_times,\n                                     small_step_times[contributing_small_step]);\n      const OrderVector<double> remote_interpolation_coefficients =\n          interpolation_coefficients(remote_control_times,\n                                     small_step_times[contributing_small_step]);\n      for (size_t local_step_index = 0;\n           local_step_index < local_interpolation_coefficients.size();\n           ++local_step_index) {\n        if (local_interpolation_coefficients[local_step_index] == 0.0) {\n          continue;\n        }\n        for (size_t remote_step_index = 0;\n             remote_step_index < remote_interpolation_coefficients.size();\n             ++remote_step_index) {\n          if (remote_interpolation_coefficients[remote_step_index] == 0.0) {\n            continue;\n          }\n          step_coefficients.emplace_back(\n              local_ids[local_step_index], remote_ids[remote_step_index],\n              small_step_coefficients[contributing_small_step] *\n                  local_interpolation_coefficients[local_step_index] *\n                  remote_interpolation_coefficients[remote_step_index]);\n        }\n      }\n    }\n    if (current_small_step == start_time) {\n      break;\n    }\n\n    if constexpr (std::is_same_v<TimeType, Time>) {\n      // We're iterating backwards, so the next step temporally is the\n      // one we just did.\n      small_step_end = current_small_step;\n    } else {\n      ERROR(\n          \"Multiple-small-step dense output is not supported.  Calculate \"\n          \"coefficients as the sum of the exact update for the complete small \"\n          \"steps and a dense update for a single partial small step.\");\n    }\n  }\n\n  // Combine duplicate entries\n  ASSERT(not step_coefficients.empty(),\n         \"Generated no coefficients.  This an algorithmic bug.  Please file \"\n         \"an issue.\");\n  alg::sort(step_coefficients);\n  auto unique_entry = step_coefficients.begin();\n  for (auto generated_entry = std::next(step_coefficients.begin());\n       generated_entry != step_coefficients.end();\n       ++generated_entry) {\n    if (get<0>(*generated_entry) == get<0>(*unique_entry) and\n        get<1>(*generated_entry) == get<1>(*unique_entry)) {\n      get<2>(*unique_entry) += get<2>(*generated_entry);\n    } else {\n      *++unique_entry = *generated_entry;\n    }\n  }\n  step_coefficients.erase(std::next(unique_entry), step_coefficients.end());\n  return step_coefficients;\n}\n\n#define MATH_WRAPPER_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                          \\\n  template void apply_coefficients(                   \\\n      gsl::not_null<MATH_WRAPPER_TYPE(data)*> result, \\\n      const LtsCoefficients& coefficients,            \\\n      const BoundaryHistoryEvaluator<MATH_WRAPPER_TYPE(data)>& coupling);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (MATH_WRAPPER_TYPES))\n#undef INSTANTIATE\n#undef MATH_WRAPPER_TYPE\n\n#define TIME_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template LtsCoefficients lts_coefficients(                                 \\\n      const ConstBoundaryHistoryTimes& local_times,                          \\\n      const ConstBoundaryHistoryTimes& remote_times, const Time& start_time, \\\n      const TIME_TYPE(data) & end_time, SchemeType local_scheme,             \\\n      SchemeType remote_scheme, int local_order_offset,                      \\\n      int remote_order_offset);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (Time, ApproximateTime))\n#undef INSTANTIATE\n#undef TIME_TYPE\n}  // namespace TimeSteppers::adams_lts\n"
  },
  {
    "path": "src/Time/TimeSteppers/AdamsLts.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/container/small_vector.hpp>\n#include <cstddef>\n#include <tuple>\n\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsCoefficients.hpp\"\n\n/// \\cond\nclass Time;\nnamespace TimeSteppers {\ntemplate <typename T>\nclass BoundaryHistoryEvaluator;\nclass ConstBoundaryHistoryTimes;\n}  // namespace TimeSteppers\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// Shared LTS implementation for the two Adams-based methods.\nnamespace TimeSteppers::adams_lts {\n/// Get the time of a substep as a `Time`, assuming substeps are at\n/// the end of the step.\nTime exact_substep_time(const TimeStepId& id);\n\n// For order-k 2:1 stepping, in each small step, half the points will\n// require interpolation (k entries each) and the others will not (1\n// entry each).  So (2 small steps) * ((k/2 interpolations) * (from k\n// points) + (k/2 non-interpolations)) = k (k + 1).  (The small steps\n// round k/2 in different directions and the effect cancels out.)\nconstexpr size_t lts_coefficients_static_size =\n    adams_coefficients::maximum_order * (adams_coefficients::maximum_order + 1);\n\n/// Storage for LTS coefficients that should not allocate in typical\n/// cases.  Each entry is a tuple of (local id, remote id,\n/// coefficient).  The contents should be kept sorted, as some\n/// functions assume that.\nstruct LtsCoefficients\n    : boost::container::small_vector<std::tuple<TimeStepId, TimeStepId, double>,\n                                     lts_coefficients_static_size> {\n  using boost::container::small_vector<\n      std::tuple<TimeStepId, TimeStepId, double>,\n      lts_coefficients_static_size>::small_vector;\n};\n\nLtsCoefficients& operator+=(LtsCoefficients& a, const LtsCoefficients& b);\nLtsCoefficients& operator-=(LtsCoefficients& a, const LtsCoefficients& b);\n\nLtsCoefficients operator+(LtsCoefficients&& a, LtsCoefficients&& b);\nLtsCoefficients operator+(LtsCoefficients&& a, const LtsCoefficients& b);\nLtsCoefficients operator+(const LtsCoefficients& a, LtsCoefficients&& b);\nLtsCoefficients operator+(const LtsCoefficients& a, const LtsCoefficients& b);\n\nLtsCoefficients operator-(LtsCoefficients&& a, LtsCoefficients&& b);\nLtsCoefficients operator-(LtsCoefficients&& a, const LtsCoefficients& b);\nLtsCoefficients operator-(const LtsCoefficients& a, LtsCoefficients&& b);\nLtsCoefficients operator-(const LtsCoefficients& a, const LtsCoefficients& b);\n\n/// Add the LTS boundary terms for to \\p result for the given set of\n/// coefficients.\ntemplate <typename T>\nvoid apply_coefficients(gsl::not_null<T*> result,\n                        const LtsCoefficients& coefficients,\n                        const BoundaryHistoryEvaluator<T>& coupling);\n\n/// Type of coefficients used for an Adams scheme\nenum class SchemeType { Explicit, Implicit };\n\n/*!\n * Calculate the nonzero terms in an Adams LTS boundary contribution.\n *\n * The coefficients are generated for a step from \\p start_time to \\p\n * end_time.  Interpolation from the elements is performed using\n * polynomials appropriate for the given \\p local_scheme and \\p\n * remote_scheme.  The orders for the interpolations are taken from\n * the \\p local_times and \\p remote_times, adjusted by \\p\n * local_order_offset and \\p remote_order_offset, which can be either\n * 0 or -1.  The small-step integration is performed with an implicit\n * method if either \\p local_scheme or \\p remote_scheme is implicit,\n * and the order is the larger of the integration orders on the two\n * sides, reduced by one if both sides have offset -1.\n *\n * This function returns the sum of the coefficients for the small\n * steps, i.e., the steps between all the times either side updates\n * between the time bounds.  Each small step is calculated as\n *\n * \\f{equation}\n * \\Delta \\tilde{y}_n = \\Delta \\tilde{t}_n \\sum_{ij} D(y^L_{n,-i}, y^R_{n,-j})\n * \\sum_q \\tilde{\\alpha}_{nq}\n * \\ell_i(\\tilde{t}_{n-q}; t^L_\\cdots, \\ldots)\n * \\ell_j(\\tilde{t}_{n-q}; t^R_\\cdots, \\ldots),\n * \\f}\n *\n * where $y^L_{n,0}$ and $y^R_{n,0}$ are the values of the local and\n * remote variables at the start of the step containing small step\n * $n$, $y^L_{n,1}$ and $y^R_{n,1}$ are the predictor values for those\n * steps, and other index values count backwards in the history of\n * that side.  The range of $i$ and choice of the control times\n * $t^L_\\cdots$ are determined by \\p local_scheme and the local order,\n * $j$ and $t^R_\\cdots$ are determined by \\p remote_scheme and the\n * remote order, and the Adams coefficients $\\tilde{\\alpha}_{nq}$\n * correspond to the larger of the two orders, and are implicit if\n * either set of interpolation coefficients is.\n *\n * When called for dense output, the arguments must represent a\n * single small step, i.e., the step cannot cross any of the control\n * times.  Any additional terms can be generated by a second call\n * treating the remainder of the step as non-dense.\n *\n * \\tparam TimeType The type `Time` for a step aligned with the\n * control times or `ApproximateTime` for dense output.\n */\ntemplate <typename TimeType>\nLtsCoefficients lts_coefficients(\n    const ConstBoundaryHistoryTimes& local_times,\n    const ConstBoundaryHistoryTimes& remote_times, const Time& start_time,\n    const TimeType& end_time, SchemeType local_scheme, SchemeType remote_scheme,\n    int local_order_offset, int remote_order_offset);\n}  // namespace TimeSteppers::adams_lts\n"
  },
  {
    "path": "src/Time/TimeSteppers/AdamsMoultonPc.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/AdamsMoultonPc.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Time/ApproximateTime.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/LargestStepperError.hpp\"\n#include \"Time/SelfStart.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/TimeSteppers/AdamsCoefficients.hpp\"\n#include \"Time/TimeSteppers/AdamsLts.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TimeSteppers {\n\n// Don't include AdamsCoefficients.hpp in the header just to get one\n// constant.\nstatic_assert(adams_coefficients::maximum_order ==\n              AdamsMoultonPc<false>::maximum_order);\n\ntemplate <bool Monotonic>\nAdamsMoultonPc<Monotonic>::AdamsMoultonPc(const std::optional<size_t>& order,\n                                          const Options::Context& context)\n    : order_(order) {\n  if (order_.has_value() and (*order_ < 2 or *order_ > maximum_order)) {\n    PARSE_ERROR(context,\n                \"The order for Adams-Moulton Nth order must be 2 <= order <= \"\n                << maximum_order);\n  }\n}\n\ntemplate <bool Monotonic>\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nAdamsMoultonPc<Monotonic>::order() const {\n  if (order_.has_value()) {\n    return variants::TaggedVariant<Tags::FixedOrder>(*order_);\n  } else {\n    return variants::TaggedVariant<Tags::VariableOrder>(minimum_order,\n                                                        maximum_order);\n  }\n}\n\ntemplate <bool Monotonic>\nuint64_t AdamsMoultonPc<Monotonic>::number_of_substeps() const {\n  return 2;\n}\n\ntemplate <bool Monotonic>\nuint64_t AdamsMoultonPc<Monotonic>::number_of_substeps_for_error() const {\n  return number_of_substeps();\n}\n\ntemplate <bool Monotonic>\nsize_t AdamsMoultonPc<Monotonic>::number_of_past_steps() const {\n  return order_.value_or(2) - 2;\n}\n\ntemplate <bool Monotonic>\ndouble AdamsMoultonPc<Monotonic>::stable_step() const {\n  if (not order_.has_value()) {\n    ERROR_NO_TRACE(\n        \"Variable-order time stepper does not have well-defined stable step.\");\n  }\n\n  switch (*order_) {\n    case 2:\n      return 1.0;\n    case 3:\n      return 0.981297;\n    case 4:\n      return 0.794227;\n    case 5:\n      return 0.612340;\n    case 6:\n      return 0.464542;\n    case 7:\n      return 0.350596;\n    case 8:\n      return 0.264373;\n    default:\n      ERROR(\"Bad order\");\n  }\n}\n\ntemplate <bool Monotonic>\nbool AdamsMoultonPc<Monotonic>::monotonic() const {\n  return Monotonic;\n}\n\ntemplate <bool Monotonic>\nTimeStepId AdamsMoultonPc<Monotonic>::next_time_id(\n    const TimeStepId& current_id, const TimeDelta& time_step) const {\n  switch (current_id.substep()) {\n    case 0:\n      return current_id.next_substep(time_step, 1.0);\n    case 1:\n      return current_id.next_step(time_step);\n    default:\n      ERROR(\"Bad id: \" << current_id);\n  }\n}\n\ntemplate <bool Monotonic>\nTimeStepId AdamsMoultonPc<Monotonic>::next_time_id_for_error(\n    const TimeStepId& current_id, const TimeDelta& time_step) const {\n  return next_time_id(current_id, time_step);\n}\n\ntemplate <bool Monotonic>\nbool AdamsMoultonPc<Monotonic>::neighbor_data_required(\n    const TimeStepId& next_substep_id,\n    const TimeStepId& neighbor_data_id) const {\n  // Because of self-start, step times may not be monotonic across\n  // slabs, so check that first.\n  if (neighbor_data_id.slab_number() != next_substep_id.slab_number()) {\n    return neighbor_data_id.slab_number() < next_substep_id.slab_number();\n  }\n\n  const evolution_less<Time> before{neighbor_data_id.time_runs_forward()};\n\n  if constexpr (Monotonic) {\n    const auto next_time = adams_lts::exact_substep_time(next_substep_id);\n    const auto neighbor_time = adams_lts::exact_substep_time(neighbor_data_id);\n    return before(neighbor_time, next_time) or\n           (neighbor_time == next_time and neighbor_data_id.substep() == 1 and\n            next_substep_id.substep() == 0);\n  } else {\n    if (next_substep_id.substep() == 1) {\n      // predictor\n      return before(neighbor_data_id.step_time(),\n                    next_substep_id.step_time()) or\n             (neighbor_data_id.step_time() == next_substep_id.step_time() and\n              neighbor_data_id.substep() == 0);\n    } else {\n      // corrector\n      return before(neighbor_data_id.step_time(), next_substep_id.step_time());\n    }\n  }\n}\n\ntemplate <bool Monotonic>\nbool AdamsMoultonPc<Monotonic>::neighbor_data_required(\n    const double dense_output_time, const TimeStepId& neighbor_data_id) const {\n  const evolution_less<double> before{neighbor_data_id.time_runs_forward()};\n  if constexpr (Monotonic) {\n    return not before(dense_output_time,\n                      adams_lts::exact_substep_time(neighbor_data_id).value());\n  } else {\n    return before(neighbor_data_id.step_time().value(), dense_output_time);\n  }\n}\n\ntemplate <bool Monotonic>\nvoid AdamsMoultonPc<Monotonic>::pup(PUP::er& p) {\n  LtsTimeStepper::pup(p);\n  p | order_;\n}\n\nnamespace {\ntemplate <typename T>\ndouble evaluate_error(\n    const gsl::not_null<T*> scratch, const ConstUntypedHistory<T>& history,\n    const StepperErrorTolerances& tolerances,\n    const adams_coefficients::OrderVector<double>& corrector_coefficients,\n    const adams_coefficients::OrderVector<double>& predictor_coefficients) {\n  // We can't use the predictor value from the history because it\n  // might have been modified by variable fixing and filtering and\n  // such.\n  auto error_coefficients = corrector_coefficients;\n  for (size_t i = 0; i < predictor_coefficients.size(); ++i) {\n    error_coefficients[i] -= predictor_coefficients[i];\n  }\n\n  auto coefficient = error_coefficients.rbegin();\n  *scratch = *coefficient * history.substeps().front().derivative;\n  ++coefficient;\n  auto history_entry = history.rbegin();\n  while (coefficient != error_coefficients.rend()) {\n    *scratch += *coefficient * history_entry->derivative;\n    ++coefficient;\n    ++history_entry;\n  }\n\n  return largest_stepper_error(*history.back().value, *scratch, tolerances);\n}\n}  // namespace\n\ntemplate <bool Monotonic>\ntemplate <bool DenseOutput, typename T>\nstd::optional<StepperErrorEstimate> AdamsMoultonPc<Monotonic>::update_u_common(\n    const gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n    const tmpl::conditional_t<DenseOutput, ApproximateTime, Time>& time,\n    const bool corrector, const StepperErrorTolerances& tolerances) const {\n  ASSERT(not DenseOutput or\n             tolerances.estimates == StepperErrorTolerances::Estimates::None,\n         \"Can't compute errors in dense output.\");\n  ASSERT(history.size() >= history.integration_order() - 1,\n         \"Insufficient history\");\n  // Pass in whether to run the predictor or corrector even though we\n  // can compute it as a sanity check.\n  ASSERT(corrector != history.substeps().empty(),\n         \"Applying predictor or corrector when expecting the other.\");\n  ASSERT(corrector != history.at_step_start(), \"Unexpected new data\");\n\n  const auto& step_start = history.back().time_step_id.step_time();\n  const auto history_start =\n      history.end() -\n      static_cast<typename ConstUntypedHistory<T>::difference_type>(\n          history.integration_order() - 1);\n  adams_coefficients::OrderVector<Time> control_times{};\n  std::transform(history_start, history.end(),\n                 std::back_inserter(control_times),\n                 [](const auto& r) { return r.time_step_id.step_time(); });\n  if (corrector) {\n    control_times.push_back(\n        step_start + history.substeps().front().time_step_id.step_size());\n  }\n  const auto update_coefficients = adams_coefficients::coefficients(\n      control_times.begin(), control_times.end(), step_start, time);\n\n  std::optional<StepperErrorEstimate> error{};\n  if constexpr (not DenseOutput) {\n    if (corrector and\n        tolerances.estimates != StepperErrorTolerances::Estimates::None) {\n      const auto predictor_coefficients = adams_coefficients::coefficients(\n          control_times.begin(), control_times.end() - 1, step_start, time);\n      error.emplace(step_start, time - step_start,\n                    history.integration_order() - 1,\n                    evaluate_error(u, history, tolerances, update_coefficients,\n                                   predictor_coefficients));\n\n      if (tolerances.estimates ==\n          StepperErrorTolerances::Estimates::AllOrders) {\n        for (size_t error_order = history.integration_order() - 2;\n             error_order != std::numeric_limits<size_t>::max();\n             --error_order) {\n          const auto error_corrector = adams_coefficients::coefficients(\n              control_times.end() -\n                  static_cast<decltype(control_times)::difference_type>(\n                      error_order + 1),\n              control_times.end(), step_start, time);\n          const auto error_predictor = adams_coefficients::coefficients(\n              control_times.end() -\n                  static_cast<decltype(control_times)::difference_type>(\n                      error_order + 1),\n              control_times.end() - 1, step_start, time);\n          gsl::at(error->errors, error_order)\n              .emplace(evaluate_error(u, history, tolerances, error_corrector,\n                                      error_predictor));\n        }\n      }\n    }\n\n    // Dense output adds to the existing value, but the main step overwrites.\n    *u = *history.back().value;\n  }\n\n  auto coefficient = update_coefficients.begin();\n  for (auto history_entry = history_start;\n       history_entry != history.end();\n       ++history_entry, ++coefficient) {\n    *u += *coefficient * history_entry->derivative;\n  }\n  if (corrector) {\n    *u += update_coefficients.back() * history.substeps().front().derivative;\n  }\n\n  return error;\n}\n\ntemplate <bool Monotonic>\ntemplate <typename T>\nstd::optional<StepperErrorEstimate> AdamsMoultonPc<Monotonic>::update_u_impl(\n    const gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n    const TimeDelta& time_step,\n    const StepperErrorTolerances& tolerances) const {\n  const Time next_time = history.back().time_step_id.step_time() + time_step;\n  return update_u_common<false>(u, history, next_time,\n                                not history.at_step_start(), tolerances);\n}\n\ntemplate <bool Monotonic>\ntemplate <typename T>\nvoid AdamsMoultonPc<Monotonic>::clean_history_impl(\n    const MutableUntypedHistory<T>& history) const {\n  if (not history.at_step_start()) {\n    ASSERT(history.integration_order() > 1, \"Cannot run below second order.\");\n    const auto required_points = history.integration_order() - 2;\n    history.clear_substeps();\n    while (history.size() > required_points) {\n      history.pop_front();\n    }\n    if (not history.empty()) {\n      history.discard_value(history.back().time_step_id);\n    }\n  }\n}\n\ntemplate <bool Monotonic>\ntemplate <typename T>\nbool AdamsMoultonPc<Monotonic>::dense_update_u_impl(\n    const gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n    const double time) const {\n  if constexpr (Monotonic) {\n    if (not history.at_step_start()) {\n      return false;\n    }\n    update_u_common<true>(u, history, ApproximateTime{time}, false,\n                          StepperErrorTolerances{});\n    return true;\n  } else {\n    if (history.at_step_start()) {\n      return false;\n    }\n    update_u_common<true>(u, history, ApproximateTime{time}, true,\n                          StepperErrorTolerances{});\n    return true;\n  }\n}\n\ntemplate <bool Monotonic>\ntemplate <typename T>\nbool AdamsMoultonPc<Monotonic>::can_change_step_size_impl(\n    const TimeStepId& time_id, const ConstUntypedHistory<T>& history) const {\n  // We need to prevent the next step from occurring at the same time\n  // as one already in the history.  The self-start code ensures this\n  // can't happen during self-start, and it clearly can't happen\n  // during normal evolution where the steps are monotonic, but during\n  // the transition between them we have to worry about a step being\n  // placed on a self-start time.  The self-start algorithm guarantees\n  // the final state is safe for constant-time-step evolution, so we\n  // just force that until we've passed all the self-start times.\n  const evolution_less_equal<Time> less_equal{time_id.time_runs_forward()};\n  return not ::SelfStart::is_self_starting(time_id) and\n         alg::all_of(history, [&](const auto& record) {\n           return less_equal(record.time_step_id.step_time(),\n                             time_id.step_time());\n         });\n}\n\ntemplate <bool Monotonic>\ntemplate <typename T>\nvoid AdamsMoultonPc<Monotonic>::add_boundary_delta_impl(\n    const gsl::not_null<T*> result,\n    const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n    const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n    const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n    const TimeDelta& time_step) const {\n  ASSERT(not local_times.empty(), \"No local data provided.\");\n  ASSERT(not remote_times.empty(), \"No remote data provided.\");\n  for (size_t i = 0; i < local_times.size(); ++i) {\n    ASSERT(not order_.has_value() or\n               local_times.integration_order(i) == *order_ or\n               ::SelfStart::is_self_starting(local_times[i]),\n           \"Incorrect local order \" << local_times.integration_order(i)\n           << \" at time \" << local_times[i]);\n  }\n  for (size_t i = 0; i < remote_times.size(); ++i) {\n    ASSERT(not order_.has_value() or\n               remote_times.integration_order(i) == *order_ or\n               ::SelfStart::is_self_starting(remote_times[i]),\n           \"Incorrect remote order \" << remote_times.integration_order(i)\n           << \" at time \" << remote_times[i]);\n  }\n\n  if constexpr (Monotonic) {\n    const auto step_start = local_times.back().step_time();\n    const auto step_end = step_start + time_step;\n    const auto small_step_start =\n        std::max(local_times.back(), remote_times.back()).step_time();\n    const auto synchronization_time =\n        std::min(local_times.back(), remote_times.back()).step_time();\n    const auto is_synchronization_time = [&](const TimeStepId& id) {\n      return id.step_time() == synchronization_time;\n    };\n    ASSERT(alg::any_of(local_times, is_synchronization_time) and\n               alg::any_of(remote_times, is_synchronization_time),\n           \"Only nested step patterns (N:1) are supported.\");\n\n    if (local_times.number_of_substeps(local_times.size() - 1) == 1) {\n      // Predictor\n      auto lts_coefficients = adams_lts::lts_coefficients(\n          local_times, remote_times, small_step_start, step_end,\n          adams_lts::SchemeType::Explicit, adams_lts::SchemeType::Explicit, -1,\n          -1);\n      if (not is_synchronization_time(remote_times.back())) {\n        lts_coefficients += adams_lts::lts_coefficients(\n            local_times, remote_times, synchronization_time, small_step_start,\n            adams_lts::SchemeType::Explicit, adams_lts::SchemeType::Implicit,\n            -1, 0);\n      }\n      adams_lts::apply_coefficients(result, lts_coefficients, coupling);\n    } else {\n      // Corrector\n      if (remote_times.number_of_substeps(remote_times.size() - 1) == 2) {\n        // Aligned corrector\n        ASSERT(adams_lts::exact_substep_time(\n                   remote_times[{remote_times.size() - 1, 1}]) == step_end,\n               \"Have remote substep data, but it isn't aligned with the local \"\n               \"data.\");\n        const auto lts_coefficients =\n            adams_lts::lts_coefficients(local_times, remote_times,\n                                        synchronization_time, step_end,\n                                        adams_lts::SchemeType::Implicit,\n                                        adams_lts::SchemeType::Implicit, 0, 0) -\n            adams_lts::lts_coefficients(local_times, remote_times,\n                                        synchronization_time, step_start,\n                                        adams_lts::SchemeType::Implicit,\n                                        adams_lts::SchemeType::Explicit, 0, -1);\n        adams_lts::apply_coefficients(result, lts_coefficients, coupling);\n      } else {\n        // Unaligned corrector\n        ASSERT(step_start == small_step_start,\n               \"Trying to take unaligned step, but remote side is smaller.\");\n        const auto lts_coefficients = adams_lts::lts_coefficients(\n            local_times, remote_times, step_start, step_end,\n            adams_lts::SchemeType::Implicit, adams_lts::SchemeType::Explicit, 0,\n            -1);\n        adams_lts::apply_coefficients(result, lts_coefficients, coupling);\n      }\n    }\n  } else {\n    adams_lts::SchemeType scheme = adams_lts::SchemeType::Implicit;\n    int order_offset = 0;\n\n    if (local_times.number_of_substeps(local_times.size() - 1) == 1) {\n      // Predictor\n      scheme = adams_lts::SchemeType::Explicit;\n      order_offset = -1;\n      ASSERT(remote_times.back() <= local_times.back(),\n             \"Unexpected remote values available.\");\n    }\n\n    const auto lts_coefficients = adams_lts::lts_coefficients(\n        local_times, remote_times, local_times.back().step_time(),\n        local_times.back().step_time() + time_step, scheme, scheme,\n        order_offset, order_offset);\n    adams_lts::apply_coefficients(result, lts_coefficients, coupling);\n  }\n}\n\ntemplate <bool Monotonic>\nvoid AdamsMoultonPc<Monotonic>::clean_boundary_history_impl(\n    const TimeSteppers::MutableBoundaryHistoryTimes& local_times,\n    const TimeSteppers::MutableBoundaryHistoryTimes& remote_times) const {\n  if (local_times.empty() or\n      local_times.number_of_substeps(local_times.size() - 1) != 2) {\n    return;\n  }\n  ASSERT(not remote_times.empty(), \"No remote data available.\");\n\n  const bool synchronized =\n      remote_times.number_of_substeps(remote_times.size() - 1) == 2 and\n      adams_lts::exact_substep_time(local_times[{local_times.size() - 1, 1}]) ==\n          adams_lts::exact_substep_time(\n              remote_times[{remote_times.size() - 1, 1}]);\n\n  if (Monotonic and not synchronized) {\n    return;\n  }\n\n  // Unlike for the volume terms, we need to keep an extra point in\n  // case the order changes.  This is because the order stored in the\n  // volume history represents, when the cleanup is run, the order of\n  // the next step, while the orders in the boundary history are\n  // stored per-step, so we have the order for the step we've just\n  // taken, which may be one lower than what we need.\n  const auto local_order =\n      local_times.integration_order(local_times.size() - 1);\n  auto required_local_points = local_order - 2;\n  if (local_order < maximum_order and not order_.has_value()) {\n    ++required_local_points;\n  }\n\n  while (local_times.size() > required_local_points) {\n    local_times.pop_front();\n  }\n  for (size_t i = 0; i < local_times.size(); ++i) {\n    local_times.clear_substeps(i);\n  }\n\n  // If the sides are not aligned, then we are in the middle of the\n  // remote step, so still need its data.\n  if (synchronized) {\n    const auto remote_order =\n        remote_times.integration_order(remote_times.size() - 1);\n    auto required_remote_points = remote_order - 2;\n    if (remote_order < maximum_order and not order_.has_value()) {\n      ++required_remote_points;\n    }\n\n    while (remote_times.size() > required_remote_points) {\n      remote_times.pop_front();\n    }\n    for (size_t i = 0; i < remote_times.size(); ++i) {\n      remote_times.clear_substeps(i);\n    }\n  }\n}\n\ntemplate <bool Monotonic>\ntemplate <typename T>\nvoid AdamsMoultonPc<Monotonic>::boundary_dense_output_impl(\n    const gsl::not_null<T*> result,\n    const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n    const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n    const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n    const double time) const {\n  for (size_t i = 0; i < local_times.size(); ++i) {\n    ASSERT(not order_.has_value() or\n               local_times.integration_order(i) == *order_ or\n               ::SelfStart::is_self_starting(local_times[i]),\n           \"Incorrect local order \" << local_times.integration_order(i)\n           << \" at time \" << local_times[i]);\n  }\n  for (size_t i = 0; i < remote_times.size(); ++i) {\n    ASSERT(not order_.has_value() or\n               remote_times.integration_order(i) == *order_ or\n               ::SelfStart::is_self_starting(remote_times[i]),\n           \"Incorrect remote order \" << remote_times.integration_order(i)\n           << \" at time \" << remote_times[i]);\n  }\n\n  if constexpr (Monotonic) {\n    ASSERT(local_times.number_of_substeps(local_times.size() - 1) == 1,\n           \"Dense output must be done before predictor evaluation.\");\n\n    const auto small_step_start =\n        std::max(local_times.back(), remote_times.back()).step_time();\n    const auto synchronization_time =\n        std::min(local_times.back(), remote_times.back()).step_time();\n    const auto is_synchronization_time = [&](const TimeStepId& id) {\n      return id.step_time() == synchronization_time;\n    };\n    ASSERT(alg::any_of(local_times, is_synchronization_time) and\n               alg::any_of(remote_times, is_synchronization_time),\n           \"Only nested step patterns (N:1) are supported.\");\n\n    auto lts_coefficients = adams_lts::lts_coefficients(\n        local_times, remote_times, small_step_start, ApproximateTime{time},\n        adams_lts::SchemeType::Explicit, adams_lts::SchemeType::Explicit, -1,\n        -1);\n    if (not is_synchronization_time(remote_times.back())) {\n      lts_coefficients += adams_lts::lts_coefficients(\n          local_times, remote_times, synchronization_time, small_step_start,\n          adams_lts::SchemeType::Explicit, adams_lts::SchemeType::Implicit, -1,\n          0);\n    }\n    adams_lts::apply_coefficients(result, lts_coefficients, coupling);\n  } else {\n    if (local_times.back().step_time().value() == time) {\n      // Nothing to do.  The requested time is the start of the step,\n      // which is the input value of `result`.\n      return;\n    }\n    ASSERT(local_times.number_of_substeps(local_times.size() - 1) == 2,\n           \"Dense output must be done after predictor evaluation.\");\n\n    const auto small_step_start =\n        std::max(local_times.back(), remote_times.back()).step_time();\n    const auto lts_coefficients =\n        adams_lts::lts_coefficients(\n            local_times, remote_times, local_times.back().step_time(),\n            small_step_start, adams_lts::SchemeType::Implicit,\n            adams_lts::SchemeType::Implicit, 0, 0) +\n        adams_lts::lts_coefficients(local_times, remote_times, small_step_start,\n                                    ApproximateTime{time},\n                                    adams_lts::SchemeType::Implicit,\n                                    adams_lts::SchemeType::Implicit, 0, 0);\n    adams_lts::apply_coefficients(result, lts_coefficients, coupling);\n  }\n}\n\ntemplate <bool Monotonic>\nbool operator==(const AdamsMoultonPc<Monotonic>& lhs,\n                const AdamsMoultonPc<Monotonic>& rhs) {\n  return lhs.order() == rhs.order();\n}\n\ntemplate <bool Monotonic>\nbool operator!=(const AdamsMoultonPc<Monotonic>& lhs,\n                const AdamsMoultonPc<Monotonic>& rhs) {\n  return not(lhs == rhs);\n}\n\nTIME_STEPPER_DEFINE_OVERLOADS_TEMPLATED(AdamsMoultonPc<Monotonic>,\n                                        bool Monotonic)\nLTS_TIME_STEPPER_DEFINE_OVERLOADS_TEMPLATED(AdamsMoultonPc<Monotonic>,\n                                            bool Monotonic)\n\ntemplate <bool Monotonic>\nPUP::able::PUP_ID AdamsMoultonPc<Monotonic>::my_PUP_ID = 0;  // NOLINT\n\n#define MONOTONIC(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                            \\\n  template class AdamsMoultonPc<MONOTONIC(data)>;                       \\\n  template bool operator==(const AdamsMoultonPc<MONOTONIC(data)>& lhs,  \\\n                           const AdamsMoultonPc<MONOTONIC(data)>& rhs); \\\n  template bool operator!=(const AdamsMoultonPc<MONOTONIC(data)>& lhs,  \\\n                           const AdamsMoultonPc<MONOTONIC(data)>& rhs);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (false, true))\n#undef INSTANTIATE\n#undef MONOTONIC\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/AdamsMoultonPc.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nstruct ApproximateTime;\nclass TimeDelta;\nclass TimeStepId;\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace TimeSteppers {\ntemplate <typename T>\nclass BoundaryHistoryEvaluator;\nclass ConstBoundaryHistoryTimes;\ntemplate <typename T>\nclass ConstUntypedHistory;\nclass MutableBoundaryHistoryTimes;\ntemplate <typename T>\nclass MutableUntypedHistory;\n}  // namespace TimeSteppers\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n *\n * An $N$th order Adams-Moulton predictor-corrector method using an\n * $(N - 1)$th order Adams-Bashforth predictor.\n *\n * If \\p Monotonic is true, dense output is performed using the\n * predictor stage, otherwise the corrector is used.  The corrector\n * results are more accurate (but still formally the same order), but\n * require a RHS evaluation at the end of the step before dense output\n * can be performed.\n *\n * The stable step size factors for different orders are (to\n * approximately 4-5 digits):\n *\n * <table class=\"doxtable\">\n *  <tr>\n *    <th> %Order </th>\n *    <th> CFL Factor </th>\n *  </tr>\n *  <tr>\n *    <td> 2 </td>\n *    <td> 1 </td>\n *  </tr>\n *  <tr>\n *    <td> 3 </td>\n *    <td> 0.981297 </td>\n *  </tr>\n *  <tr>\n *    <td> 4 </td>\n *    <td> 0.794227 </td>\n *  </tr>\n *  <tr>\n *    <td> 5 </td>\n *    <td> 0.612340 </td>\n *  </tr>\n *  <tr>\n *    <td> 6 </td>\n *    <td> 0.464542 </td>\n *  </tr>\n *  <tr>\n *    <td> 7 </td>\n *    <td> 0.350596 </td>\n *  </tr>\n *  <tr>\n *    <td> 8 </td>\n *    <td> 0.264373 </td>\n *  </tr>\n * </table>\n */\ntemplate <bool Monotonic>\nclass AdamsMoultonPc : public LtsTimeStepper {\n public:\n  static std::string name() {\n    return Monotonic ? \"AdamsMoultonPcMonotonic\" : \"AdamsMoultonPc\";\n  }\n\n  static constexpr size_t minimum_order = 2;\n  static constexpr size_t maximum_order = 8;\n\n  struct Order {\n    using type = Options::Auto<size_t>;\n    static constexpr Options::String help = {\"Convergence order\"};\n  };\n  using options = tmpl::list<Order>;\n  static constexpr Options::String help =\n      Monotonic\n          ? \"An Adams-Moulton predictor-corrector time-stepper with monotonic \"\n            \"dense output.\"\n          : \"An Adams-Moulton predictor-corrector time-stepper.\";\n\n  AdamsMoultonPc() = default;\n  explicit AdamsMoultonPc(const std::optional<size_t>& order,\n                          const Options::Context& context = {});\n  AdamsMoultonPc(const AdamsMoultonPc&) = default;\n  AdamsMoultonPc& operator=(const AdamsMoultonPc&) = default;\n  AdamsMoultonPc(AdamsMoultonPc&&) = default;\n  AdamsMoultonPc& operator=(AdamsMoultonPc&&) = default;\n  ~AdamsMoultonPc() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  uint64_t number_of_substeps() const override;\n\n  uint64_t number_of_substeps_for_error() const override;\n\n  size_t number_of_past_steps() const override;\n\n  double stable_step() const override;\n\n  bool monotonic() const override;\n\n  TimeStepId next_time_id(const TimeStepId& current_id,\n                          const TimeDelta& time_step) const override;\n\n  TimeStepId next_time_id_for_error(const TimeStepId& current_id,\n                                    const TimeDelta& time_step) const override;\n\n  bool neighbor_data_required(\n      const TimeStepId& next_substep_id,\n      const TimeStepId& neighbor_data_id) const override;\n\n  bool neighbor_data_required(\n      double dense_output_time,\n      const TimeStepId& neighbor_data_id) const override;\n\n  WRAPPED_PUPable_decl_template(AdamsMoultonPc);  // NOLINT\n\n  explicit AdamsMoultonPc(CkMigrateMessage* /*unused*/) {}\n\n  void pup(PUP::er& p) override;\n\n private:\n  template <bool DenseOutput, typename T>\n  std::optional<StepperErrorEstimate> update_u_common(\n      gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n      const tmpl::conditional_t<DenseOutput, ApproximateTime, Time>& time,\n      bool corrector, const StepperErrorTolerances& tolerances) const;\n\n  template <typename T>\n  std::optional<StepperErrorEstimate> update_u_impl(\n      gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n      const TimeDelta& time_step,\n      const StepperErrorTolerances& tolerances = {}) const;\n\n  template <typename T>\n  void clean_history_impl(const MutableUntypedHistory<T>& history) const;\n\n  template <typename T>\n  bool dense_update_u_impl(gsl::not_null<T*> u,\n                           const ConstUntypedHistory<T>& history,\n                           double time) const;\n\n  template <typename T>\n  bool can_change_step_size_impl(const TimeStepId& time_id,\n                                 const ConstUntypedHistory<T>& history) const;\n\n  template <typename T>\n  void add_boundary_delta_impl(\n      gsl::not_null<T*> result,\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n      const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n      const TimeDelta& time_step) const;\n\n  void clean_boundary_history_impl(\n      const TimeSteppers::MutableBoundaryHistoryTimes& local_times,\n      const TimeSteppers::MutableBoundaryHistoryTimes& remote_times)\n      const override;\n\n  template <typename T>\n  void boundary_dense_output_impl(\n      gsl::not_null<T*> result,\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n      const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n      double time) const;\n\n  TIME_STEPPER_DECLARE_OVERLOADS\n  LTS_TIME_STEPPER_DECLARE_OVERLOADS\n\n  std::optional<size_t> order_{};\n};\n\ntemplate <bool Monotonic>\nbool operator==(const AdamsMoultonPc<Monotonic>& lhs,\n                const AdamsMoultonPc<Monotonic>& rhs);\ntemplate <bool Monotonic>\nbool operator!=(const AdamsMoultonPc<Monotonic>& lhs,\n                const AdamsMoultonPc<Monotonic>& rhs);\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AdamsBashforth.cpp\n  AdamsCoefficients.cpp\n  AdamsLts.cpp\n  AdamsMoultonPc.cpp\n  ClassicalRungeKutta4.cpp\n  DormandPrince5.cpp\n  Heun2.cpp\n  ImexRungeKutta.cpp\n  Rk3HesthavenSsp.cpp\n  Rk3Kennedy.cpp\n  Rk3Owren.cpp\n  Rk3Pareschi.cpp\n  Rk4Kennedy.cpp\n  Rk4Owren.cpp\n  Rk5Owren.cpp\n  Rk5Tsitouras.cpp\n  RungeKutta.cpp\n  TimeStepper.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AdamsBashforth.hpp\n  AdamsCoefficients.hpp\n  AdamsLts.hpp\n  AdamsMoultonPc.hpp\n  ClassicalRungeKutta4.hpp\n  DormandPrince5.hpp\n  Factory.hpp\n  Heun2.hpp\n  ImexRungeKutta.hpp\n  ImexTimeStepper.hpp\n  LtsTimeStepper.hpp\n  Rk3HesthavenSsp.hpp\n  Rk3Kennedy.hpp\n  Rk3Owren.hpp\n  Rk4Kennedy.hpp\n  Rk4Owren.hpp\n  Rk5Owren.hpp\n  Rk3Pareschi.hpp\n  Rk5Tsitouras.hpp\n  RungeKutta.hpp\n  TimeStepper.hpp\n  )\n"
  },
  {
    "path": "src/Time/TimeSteppers/ClassicalRungeKutta4.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/ClassicalRungeKutta4.hpp\"\n\n#include <utility>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nClassicalRungeKutta4::order() const {\n  return variants::TaggedVariant<Tags::FixedOrder>(4);\n}\n\n// The growth function for RK4 is (e.g. page 60 of\n// http://www.staff.science.uu.nl/~frank011/Classes/numwisk/ch10.pdf\n//\n//   g = 1 + mu + mu^2 / 2 + mu^3 / 6 + mu^4 / 24,\n//\n// where mu = lambda * dt. The equation dy/dt = -lambda * y evolves\n// stably if |g| < 1. For lambda=-2, chosen so the stable_step() for\n// RK1 (i.e. forward Euler) would be 1, RK4 has a stable step\n// determined by inserting mu->-2 dt into the above equation. Finding the\n// solutions with a numerical root find yields a stable step of about 1.39265.\ndouble ClassicalRungeKutta4::stable_step() const { return 1.3926467817026411; }\n\nconst RungeKutta::ButcherTableau& ClassicalRungeKutta4::butcher_tableau()\n    const {\n  // See (17.1.3) of Numerical Recipes 3rd Edition\n  static const ButcherTableau tableau{\n      // Substep times\n      {1.0 / 2.0, 1.0 / 2.0, 1.0, 3.0 / 4.0},\n      // Substep coefficients\n      {{1.0 / 2.0},\n       {0.0, 1.0 / 2.0},\n       {0.0, 0.0, 1.0},\n       {5.0 / 32.0, 7.0 / 32.0, 13.0 / 32.0, -1.0 / 32.0}},\n      // Result coefficients\n      {1.0 / 6.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 6.0},\n      // Coefficients for the embedded method for generating an error measure.\n      {-1.0 / 2.0, 7.0 / 3.0, 7.0 / 3.0, 13.0 / 6.0, -16.0 / 3.0},\n      // Dense output coefficient polynomials.  Numerical Recipes\n      // Eq. (17.2.15). This implements cubic interpolation throughout\n      // the step.\n      {{0.0, 1.0, -3.0 / 2.0, 2.0 / 3.0},\n       {0.0, 0.0, 1.0, -2.0 / 3.0},\n       {0.0, 0.0, 1.0, -2.0 / 3.0},\n       {0.0, 0.0, 1.0 / 2.0, -1.0 / 3.0},\n       {},\n       {0.0, 0.0, -1.0, 1.0}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::ClassicalRungeKutta4::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/ClassicalRungeKutta4.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace TimeSteppers {\ntemplate <typename T>\nclass UntypedHistory;\n}  // namespace TimeSteppers\n/// \\endcond\n\nnamespace TimeSteppers {\n\n/*!\n * \\ingroup TimeSteppersGroup\n *\n * The standard 4th-order Runge-Kutta method, given e.g. in\n * https://en.wikipedia.org/wiki/Runge-Kutta_methods\n * that solves equations of the form\n *\n * \\f{eqnarray}{\n * \\frac{du}{dt} & = & \\mathcal{L}(t,u).\n * \\f}\n * Given a solution \\f$u(t^n)=u^n\\f$, this stepper computes\n * \\f$u(t^{n+1})=u^{n+1}\\f$ using the following equations:\n *\n * \\f{eqnarray}{\n * v^{(1)} & = & u^n + dt\\cdot \\mathcal{L}(t^n, u^n)/2,\\\\\n * v^{(2)} & = & u^n + dt\\cdot \\mathcal{L}(t^n + dt/2, v^{(1)})/2,\\\\\n * v^{(3)} & = & u^n + dt\\cdot \\mathcal{L}(t^n + dt/2, v^{(2)}),\\\\\n * v^{(4)} & = & u^n + dt\\cdot \\mathcal{L}(t^n + dt, v^{(3)}),\\\\\n * u^{n+1} & = & (2v^{(1)} + 4v^{(2)} + 2v^{(3)} + v^{(4)} - 3 u^n)/6.\n * \\f}\n *\n * Note that in the implementation, the expression for \\f$u^{n+1}\\f$ is\n * computed simultaneously with \\f$v^{(4)}\\f$, so that there are\n * actually only four substeps per step.\n *\n * The CFL factor/stable step size is 1.3926467817026411.\n */\nclass ClassicalRungeKutta4 : public RungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"The standard fourth-order Runge-Kutta time-stepper.\"};\n\n  ClassicalRungeKutta4() = default;\n  ClassicalRungeKutta4(const ClassicalRungeKutta4&) = default;\n  ClassicalRungeKutta4& operator=(const ClassicalRungeKutta4&) = default;\n  ClassicalRungeKutta4(ClassicalRungeKutta4&&) = default;\n  ClassicalRungeKutta4& operator=(ClassicalRungeKutta4&&) = default;\n  ~ClassicalRungeKutta4() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  WRAPPED_PUPable_decl_template(ClassicalRungeKutta4);  // NOLINT\n\n  explicit ClassicalRungeKutta4(CkMigrateMessage* /*unused*/) {}\n\n  const ButcherTableau& butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const ClassicalRungeKutta4& /*lhs*/,\n                                 const ClassicalRungeKutta4& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const ClassicalRungeKutta4& /*lhs*/,\n                                 const ClassicalRungeKutta4& /*rhs*/) {\n  return false;\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/DormandPrince5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/DormandPrince5.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nDormandPrince5::order() const {\n  return variants::TaggedVariant<Tags::FixedOrder>(5);\n}\n\n// The growth function for DP5 is\n//\n//   g = mu^6 / 600 + \\sum_{n=0}^5 mu^n / n!,\n//\n// where mu = lambda * dt. The equation dy/dt = -lambda * y evolves\n// stably if |g| < 1. For lambda=-2, chosen so the stable_step() for\n// RK1 (i.e. forward Euler) would be 1, DP5 has a stable step\n// determined by inserting mu->-2 dt into the above equation. Finding the\n// solutions with a numerical root find yields a stable step of about 1.653.\ndouble DormandPrince5::stable_step() const { return 1.6532839463174733; }\n\nconst RungeKutta::ButcherTableau& DormandPrince5::butcher_tableau() const {\n  // Coefficients from the Dormand-Prince 5 Butcher tableau\n  // (e.g. Sec. 17.2 of \\cite NumericalRecipes).\n  static const ButcherTableau tableau{\n      // Substep times\n      {1.0 / 5.0, 3.0 / 10.0, 4.0 / 5.0, 8.0 / 9.0, 1.0, 1.0},\n      // Substep coefficients\n      {{1.0 / 5.0},\n       {3.0 / 40.0, 9.0 / 40.0},\n       {44.0 / 45.0, -56.0 / 15.0, 32.0 / 9.0},\n       {19372.0 / 6561.0, -25360.0 / 2187.0, 64448.0 / 6561.0, -212.0 / 729.0},\n       {9017.0 / 3168.0, -355.0 / 33.0, 46732.0 / 5247.0, 49.0 / 176.0,\n        -5103.0 / 18656.0},\n       {35.0 / 384.0, 0.0, 500.0 / 1113.0, 125.0 / 192.0, -2187.0 / 6784.0,\n        11.0 / 84.0}},\n      // Result coefficients\n      {35.0 / 384.0, 0.0, 500.0 / 1113.0, 125.0 / 192.0, -2187.0 / 6784.0,\n       11.0 / 84.0},\n      // Coefficients for the embedded method for generating an error measure.\n      {5179.0 / 57600.0, 0.0, 7571.0 / 16695.0, 393.0 / 640.0,\n       -92097.0 / 339200.0, 187.0 / 2100.0, 1.0 / 40.0},\n      // Dense output coefficient polynomials\n      {{0.0, 1.0, -8048581381.0 / 2820520608.0, 8663915743.0 / 2820520608.0,\n        -12715105075.0 / 11282082432.0},\n       {},\n       {0.0, 0.0, 131558114200.0 / 32700410799.0,\n        -68118460800.0 / 10900136933.0, 87487479700.0 / 32700410799.0},\n       {0.0, 0.0, -1754552775.0 / 470086768.0, 14199869525.0 / 1410260304.0,\n        -10690763975.0 / 1880347072.0},\n       {0.0, 0.0, 127303824393.0 / 49829197408.0,\n        -318862633887.0 / 49829197408.0, 701980252875.0 / 199316789632.0},\n       {0.0, 0.0, -282668133.0 / 205662961.0, 2019193451.0 / 616988883.0,\n        -1453857185.0 / 822651844.0},\n       {},\n       {0.0, 0.0, 40617522.0 / 29380423.0, -110615467.0 / 29380423.0,\n        69997945.0 / 29380423.0}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::DormandPrince5::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/DormandPrince5.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class DormandPrince5.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n\n/*!\n * \\ingroup TimeSteppersGroup\n *\n * The standard 5th-order Dormand-Prince time stepping method, given e.g. in\n * Sec. 17.2 of \\cite NumericalRecipes.\n *\n * \\f{eqnarray}{\n * \\frac{du}{dt} & = & \\mathcal{L}(t,u).\n * \\f}\n * Given a solution \\f$u(t^n)=u^n\\f$, this stepper computes\n * \\f$u(t^{n+1})=u^{n+1}\\f$ using the following equations:\n *\n * \\f{align}{\n * k^{(1)} & = dt \\mathcal{L}(t^n, u^n),\\\\\n * k^{(i)} & = dt \\mathcal{L}(t^n + c_i dt,\n *                              u^n + \\sum_{j=1}^{i-1} a_{ij} k^{(j)}),\n *                              \\mbox{ } 2 \\leq i \\leq 6,\\\\\n * u^{n+1} & = u^n + \\sum_{i=1}^{6} b_i k^{(i)}.\n * \\f}\n *\n * Here the coefficients \\f$a_{ij}\\f$, \\f$b_i\\f$, and \\f$c_i\\f$ are given\n * in e.g. Sec. 17.2 of \\cite NumericalRecipes. Note that \\f$c_1 = 0\\f$.\n *\n * The CFL factor/stable step size is 1.6532839463174733.\n */\nclass DormandPrince5 : public RungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"The standard Dormand-Prince 5th-order time stepper.\"};\n\n  DormandPrince5() = default;\n  DormandPrince5(const DormandPrince5&) = default;\n  DormandPrince5& operator=(const DormandPrince5&) = default;\n  DormandPrince5(DormandPrince5&&) = default;\n  DormandPrince5& operator=(DormandPrince5&&) = default;\n  ~DormandPrince5() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  WRAPPED_PUPable_decl_template(DormandPrince5);  // NOLINT\n\n  explicit DormandPrince5(CkMigrateMessage* /*unused*/) {}\n\n  const ButcherTableau& butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const DormandPrince5& /*lhs*/,\n                                 const DormandPrince5& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const DormandPrince5& /*lhs*/,\n                                 const DormandPrince5& /*rhs*/) {\n  return false;\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Factory.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/AdamsMoultonPc.hpp\"\n#include \"Time/TimeSteppers/ClassicalRungeKutta4.hpp\"\n#include \"Time/TimeSteppers/DormandPrince5.hpp\"\n#include \"Time/TimeSteppers/Heun2.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Time/TimeSteppers/Rk3Kennedy.hpp\"\n#include \"Time/TimeSteppers/Rk3Owren.hpp\"\n#include \"Time/TimeSteppers/Rk3Pareschi.hpp\"\n#include \"Time/TimeSteppers/Rk4Kennedy.hpp\"\n#include \"Time/TimeSteppers/Rk4Owren.hpp\"\n#include \"Time/TimeSteppers/Rk5Owren.hpp\"\n#include \"Time/TimeSteppers/Rk5Tsitouras.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/// Typelist of available TimeSteppers\nusing time_steppers =\n    tmpl::list<AdamsBashforth, AdamsMoultonPc<false>, AdamsMoultonPc<true>,\n               ClassicalRungeKutta4, DormandPrince5, Heun2, Rk3HesthavenSsp,\n               Rk3Kennedy, Rk3Owren, Rk3Pareschi, Rk4Kennedy, Rk4Owren,\n               Rk5Owren, Rk5Tsitouras>;\n\n/// Typelist of available LtsTimeSteppers\nusing lts_time_steppers =\n    tmpl::list<AdamsBashforth, AdamsMoultonPc<false>, AdamsMoultonPc<true>>;\n\n/// Typelist of available ImexTimeSteppers\nusing imex_time_steppers =\n    tmpl::list<Heun2, Rk3Kennedy, Rk3Pareschi, Rk4Kennedy>;\n\n/// Typelist of TimeSteppers whose substep times are strictly increasing\nusing increasing_substep_time_steppers =\n    tmpl::list<AdamsBashforth, Rk3Owren, Rk4Owren>;\n\n/// Typelist of LtsTimeSteppers with monotonic() true, i.e., those\n/// that work with control systems.\nusing monotonic_lts_time_steppers =\n    tmpl::list<AdamsBashforth, AdamsMoultonPc<true>>;\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Heun2.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Heun2.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> Heun2::order()\n    const {\n  return variants::TaggedVariant<Tags::FixedOrder>(2);\n}\n\n// The stability polynomial is\n//\n//   p(z) = \\sum_{n=0}^{stages-1} alpha_n z^n / n!,\n//\n// alpha_n=1.0 for n=1...(order-1). It is the same as for forward Euler.\ndouble Heun2::stable_step() const { return 1.0; }\n\nsize_t Heun2::imex_order() const { return 2; }\n\nsize_t Heun2::implicit_stage_order() const { return 2; }\n\nconst RungeKutta::ButcherTableau& Heun2::butcher_tableau() const {\n  static const ButcherTableau tableau{\n      // Substep times\n      {1.0},\n      // Substep coefficients\n      {{1.0}},\n      // Result coefficients\n      {0.5, 0.5},\n      // Coefficients for the embedded method for generating an error measure.\n      {1.0, 0.0},\n      // Dense output coefficient polynomials\n      {{0.0, 0.5},\n       {0.0, 0.5}}};\n  return tableau;\n}\n\nconst ImexRungeKutta::ImplicitButcherTableau&\nHeun2::implicit_butcher_tableau() const {\n  static const ImplicitButcherTableau tableau{{{0.5, 0.5}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Heun2::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Heun2.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/ImexRungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * \\brief A second order continuous-extension RK method that provides 2nd-order\n * dense output.\n *\n * \\f{eqnarray}{\n * \\frac{du}{dt} & = & \\mathcal{L}(t,u).\n * \\f}\n * Given a solution \\f$u(t^n)=u^n\\f$, this stepper computes\n * \\f$u(t^{n+1})=u^{n+1}\\f$ using the following equations:\n *\n * \\f{align}{\n * k^{(i)} & = \\mathcal{L}(t^n + c_i \\Delta t,\n *                         u^n + \\Delta t \\sum_{j=1}^{i-1} a_{ij} k^{(j)}),\n *                              \\mbox{ } 1 \\leq i \\leq s,\\\\\n * u^{n+1}(t^n + \\theta \\Delta t) & = u^n + \\Delta t \\sum_{i=1}^{s} b_i(\\theta)\n * k^{(i)}. \\f}\n *\n * Here the coefficients \\f$a_{ij}\\f$, \\f$b_i\\f$, and \\f$c_i\\f$ are given\n * in \\cite Gassner20114232. Note that \\f$c_1 = 0\\f$, \\f$s\\f$ is the number\n * of stages, and \\f$\\theta\\f$ is the fraction of the step.\n *\n * When used as an IMEX method, the implicit portion uses the\n * trapezoid rule, which is stiffly accurate and A-stable.\n *\n * The CFL factor/stable step size is 1.0.\n */\nclass Heun2 : public ImexRungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"Heun's method, a 2nd order Runge-Kutta method.\"};\n\n  Heun2() = default;\n  Heun2(const Heun2&) = default;\n  Heun2& operator=(const Heun2&) = default;\n  Heun2(Heun2&&) = default;\n  Heun2& operator=(Heun2&&) = default;\n  ~Heun2() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  size_t imex_order() const override;\n\n  size_t implicit_stage_order() const override;\n\n  WRAPPED_PUPable_decl_template(Heun2);  // NOLINT\n\n  explicit Heun2(CkMigrateMessage* /*unused*/) {}\n\n  const ButcherTableau& butcher_tableau() const override;\n\n  const ImplicitButcherTableau& implicit_butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const Heun2& /*lhs*/, const Heun2& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Heun2& /*lhs*/, const Heun2& /*rhs*/) {\n  return false;\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/ImexRungeKutta.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/ImexRungeKutta.hpp\"\n\n#include \"Time/History.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace TimeSteppers {\n\nnamespace {\ntemplate <typename T>\nvoid apply_explicit_coefficients(const gsl::not_null<T*> u,\n                                 const ConstUntypedHistory<T>& implicit_history,\n                                 const std::vector<double>& coefficients,\n                                 const size_t number_of_coefficients_to_apply,\n                                 const double time_step) {\n  if (number_of_coefficients_to_apply > 0) {\n    if (coefficients[0] != 0.0) {\n      *u += coefficients[0] * time_step * implicit_history.back().derivative;\n    }\n    for (size_t i = 1; i < number_of_coefficients_to_apply; ++i) {\n      if (coefficients[i] != 0.0) {\n        *u += coefficients[i] * time_step *\n              implicit_history.substeps()[i - 1].derivative;\n      }\n    }\n  }\n}\n}  // namespace\n\ntemplate <typename T>\nvoid ImexRungeKutta::add_inhomogeneous_implicit_terms_impl(\n    const gsl::not_null<T*> u, const ConstUntypedHistory<T>& implicit_history,\n    const TimeDelta& time_step) const {\n  ASSERT(implicit_history.integration_order() == get<Tags::FixedOrder>(order()),\n         \"Fixed-order stepper cannot run at order \"\n             << implicit_history.integration_order());\n\n  auto substep =\n      implicit_history.at_step_start() ? 0 : implicit_history.substeps().size();\n\n  ASSERT(number_of_substeps() == number_of_substeps_for_error(),\n         \"The current interface does not provide enough information to \"\n         \"determine whether error estimation is active in IMEX functions.\");\n  if (substep == number_of_substeps() - 1) {\n    const auto& coefficients = butcher_tableau().result_coefficients;\n    apply_explicit_coefficients(u, implicit_history, coefficients,\n                                coefficients.size(), time_step.value());\n  } else {\n    ASSERT(substep < implicit_butcher_tableau().substep_coefficients.size(),\n           \"Tableau too short: \" << substep << \"/\"\n           << implicit_butcher_tableau().substep_coefficients.size());\n    const auto& coefficients =\n        implicit_butcher_tableau().substep_coefficients[substep];\n    apply_explicit_coefficients(u, implicit_history, coefficients,\n                                std::min(substep + 1, coefficients.size()),\n                                time_step.value());\n  }\n}\n\ntemplate <typename T>\ndouble ImexRungeKutta::implicit_weight_impl(\n    const ConstUntypedHistory<T>& implicit_history,\n    const TimeDelta& time_step) const {\n  const auto substep =\n      implicit_history.at_step_start() ? 0 : implicit_history.substeps().size();\n  const auto& coefficients = implicit_butcher_tableau().substep_coefficients;\n  if (coefficients.size() > substep and\n      coefficients[substep].size() == substep + 2) {\n    return time_step.value() * coefficients[substep][substep + 1];\n  } else {\n    return 0.0;\n  }\n}\n\nIMEX_TIME_STEPPER_DEFINE_OVERLOADS(ImexRungeKutta)\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/ImexRungeKutta.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <vector>\n\n#include \"Time/TimeSteppers/ImexTimeStepper.hpp\"\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass TimeDelta;\nnamespace TimeSteppers {\ntemplate <typename T>\nclass ConstUntypedHistory;\n}  // namespace TimeSteppers\n/// \\endcond\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * Intermediate base class implementing a generic IMEX Runge-Kutta\n * scheme.\n *\n * Implements most of the virtual methods of ImexTimeStepper for a\n * generic Runge-Kutta method.  Derived classes must implement the\n * requirements of the `RungeKutta` base class, as well as\n * `imex_order()`, `implicit_stage_order()`, and\n * `implicit_butcher_tableau()`.\n */\nclass ImexRungeKutta : public virtual RungeKutta,\n                       public virtual ImexTimeStepper {\n public:\n  /// Implicit part of the Butcher tableau.  Most parts of the tableau\n  /// must be the same as the explicit part, and so are omitted.\n  struct ImplicitButcherTableau {\n    /*!\n     * The coefficient matrix of the substeps.  We can only reasonably\n     * support EDIRK methods (including special cases such as ESDIRK\n     * and QESDIRK, see \\cite Kennedy2016), so the tableau must be\n     * lower-triangular with an empty first row.  As with the explicit\n     * tableau, the initial blank row should be omitted.  For a\n     * stiffly-accurate method, the final row must be the same as the\n     * result coefficients in the explicit tableau.\n     *\n     * More general DIRK methods can be implemented inefficiently by\n     * adding an unused initial substep to convert them to EDIRK form.\n     */\n    std::vector<std::vector<double>> substep_coefficients;\n  };\n\n  /*!\n   * Smallest order of the intermediate result at any substep.  For\n   * the methods supported by this class, this cannot exceed 2.\n   */\n  virtual size_t implicit_stage_order() const = 0;\n\n  virtual const ImplicitButcherTableau& implicit_butcher_tableau() const = 0;\n\n private:\n  template <typename T>\n  void add_inhomogeneous_implicit_terms_impl(\n      gsl::not_null<T*> u, const ConstUntypedHistory<T>& implicit_history,\n      const TimeDelta& time_step) const;\n\n  template <typename T>\n  double implicit_weight_impl(const ConstUntypedHistory<T>& implicit_history,\n                              const TimeDelta& time_step) const;\n\n  IMEX_TIME_STEPPER_DECLARE_OVERLOADS\n};\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/ImexTimeStepper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\cond\n#define IMEX_TIME_STEPPER_WRAPPED_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define IMEX_TIME_STEPPER_DERIVED_CLASS(data) BOOST_PP_TUPLE_ELEM(1, data)\n/// \\endcond\n\n/*!\n * \\ingroup TimeSteppersGroup\n *\n * Base class for TimeSteppers with IMEX support, derived from\n * TimeStepper.\n *\n * All supported time-stepping algorithms (both implicit and\n * explicit) consist of substeps that add linear combinations of\n * derivative values.  For implicit substeps, it is convenient to\n * split out the contribution of the final implicit term from\n * contributions from the history.  We therefore write an implicit\n * substep as\n *\n * \\f{equation}{\n *   Y_{n+1} =\n *   Y_{n,\\text{explicit}} + Y_{n,\\text{inhomogeneous}} + w_n S(Y_{n+1})\n * \\f}\n *\n * Here \\f$Y_{n,\\text{explicit}}\\f$ is the value of the evolved\n * variables before the implicit substep is applied,\n * \\f$Y_{n,\\text{inhomogeneous}}\\f$ is the contribution of the past\n * values from the implicit derivative history, and \\f$S(\\cdot)\\f$ is\n * the implicit portion of the right-hand-side (generally source\n * terms for PDEs).  We call \\f$w_n\\f$ the *implicit weight*.\n * This split form is convenient for most consumers of this class, so\n * we present methods for calculating\n * \\f$Y_{n,\\text{inhomogeneous}}\\f$ and \\f$w_n\\f$ individually\n * instead of a method to perform a full step update.\n *\n * History cleanup is the same for the explicit and implicit parts,\n * and should be done together.\n *\n * Dense output formulae are the same for the explicit and implicit\n * parts of any conservative IMEX stepper.  To evaluate dense output,\n * call `dense_update_u` with the implicit history after a successful\n * call to `dense_update_u` with the explicit history.\n *\n * Several of the member functions of this class are templated and\n * perform type erasure before forwarding their arguments to the\n * derived classes.  This is implemented using the macros \\ref\n * IMEX_TIME_STEPPER_DECLARE_OVERLOADS, which must be placed in a\n * private section of the class body, and\n * IMEX_TIME_STEPPER_DEFINE_OVERLOADS(derived_class), which must be\n * placed in the cpp file.\n */\nclass ImexTimeStepper : public virtual TimeStepper {\n public:\n  static constexpr bool imex = true;\n  using provided_time_stepper_interfaces =\n      tmpl::list<ImexTimeStepper, TimeStepper>;\n\n  WRAPPED_PUPable_abstract(ImexTimeStepper);  // NOLINT\n\n/// \\cond\n#define IMEX_TIME_STEPPER_DECLARE_VIRTUALS_IMPL(_, data)                      \\\n  virtual void add_inhomogeneous_implicit_terms_forward(                      \\\n      gsl::not_null<IMEX_TIME_STEPPER_WRAPPED_TYPE(data)*> u,                 \\\n      const TimeSteppers::ConstUntypedHistory<IMEX_TIME_STEPPER_WRAPPED_TYPE( \\\n          data)>& implicit_history,                                           \\\n      const TimeDelta& time_step) const = 0;                                  \\\n  virtual double implicit_weight_forward(                                     \\\n      const TimeSteppers::ConstUntypedHistory<IMEX_TIME_STEPPER_WRAPPED_TYPE( \\\n          data)>& implicit_history,                                           \\\n      const TimeDelta& time_step) const = 0;\n\n  GENERATE_INSTANTIATIONS(IMEX_TIME_STEPPER_DECLARE_VIRTUALS_IMPL,\n                          (MATH_WRAPPER_TYPES))\n#undef IMEX_TIME_STEPPER_DECLARE_VIRTUALS_IMPL\n  /// \\endcond\n\n  /// Convergence order of the integrator when used in IMEX mode.\n  virtual size_t imex_order() const = 0;\n\n  /// Add the change for the current implicit substep,\n  /// \\f$Y_{n,\\text{inhomogeneous}}\\f$, to u, given a past history of\n  /// the implicit derivatives.\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// void add_inhomogeneous_implicit_terms_impl(\n  ///     gsl::not_null<T*> u,\n  ///     const ConstUntypedHistory<T>& implicit_history,\n  ///     const TimeDelta& time_step) const;\n  /// ```\n  ///\n  /// \\note\n  /// Unlike the `update_u` methods, which overwrite their result\n  /// arguments, this function adds the result to the existing value.\n  template <typename Vars>\n  void add_inhomogeneous_implicit_terms(\n      const gsl::not_null<Vars*> u,\n      const TimeSteppers::History<Vars>& implicit_history,\n      const TimeDelta& time_step) const {\n    return add_inhomogeneous_implicit_terms_forward(\n        &*make_math_wrapper(u), implicit_history.untyped(), time_step);\n  }\n\n  /// The coefficient \\f$w_n\\f$ of the implicit derivative for the\n  /// current substep.  For a Runge-Kutta method, this is the\n  /// coefficient on the diagonal of the Butcher tableau.\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// double implicit_weight_impl(\n  ///     const ConstUntypedHistory<T>& implicit_history,\n  ///     const TimeDelta& time_step) const;\n  /// ```\n  template <typename Vars>\n  double implicit_weight(const TimeSteppers::History<Vars>& implicit_history,\n                         const TimeDelta& time_step) const {\n    return implicit_weight_forward(implicit_history.untyped(), time_step);\n  }\n};\n\n/// \\cond\n#define IMEX_TIME_STEPPER_DECLARE_OVERLOADS_IMPL(_, data)                     \\\n  void add_inhomogeneous_implicit_terms_forward(                              \\\n      gsl::not_null<IMEX_TIME_STEPPER_WRAPPED_TYPE(data)*> u,                 \\\n      const TimeSteppers::ConstUntypedHistory<IMEX_TIME_STEPPER_WRAPPED_TYPE( \\\n          data)>& implicit_history,                                           \\\n      const TimeDelta& time_step) const override;                             \\\n  double implicit_weight_forward(                                             \\\n      const TimeSteppers::ConstUntypedHistory<IMEX_TIME_STEPPER_WRAPPED_TYPE( \\\n          data)>& implicit_history,                                           \\\n      const TimeDelta& time_step) const override;\n\n#define IMEX_TIME_STEPPER_DEFINE_OVERLOADS_IMPL(_, data)                      \\\n  void IMEX_TIME_STEPPER_DERIVED_CLASS(data)::                                \\\n      add_inhomogeneous_implicit_terms_forward(                               \\\n          const gsl::not_null<IMEX_TIME_STEPPER_WRAPPED_TYPE(data)*> u,       \\\n          const TimeSteppers::ConstUntypedHistory<                            \\\n              IMEX_TIME_STEPPER_WRAPPED_TYPE(data)>& implicit_history,        \\\n          const TimeDelta& time_step) const {                                 \\\n    return add_inhomogeneous_implicit_terms_impl(u, implicit_history,         \\\n                                                 time_step);                  \\\n  }                                                                           \\\n  double IMEX_TIME_STEPPER_DERIVED_CLASS(data)::implicit_weight_forward(      \\\n      const TimeSteppers::ConstUntypedHistory<IMEX_TIME_STEPPER_WRAPPED_TYPE( \\\n          data)>& implicit_history,                                           \\\n      const TimeDelta& time_step) const {                                     \\\n    return implicit_weight_impl(implicit_history, time_step);                 \\\n  }\n/// \\endcond\n\n/// \\ingroup TimeSteppersGroup\n/// Macro declaring overloaded detail methods in classes derived from\n/// ImexTimeStepper.  Must be placed in a private section of the class\n/// body.\n#define IMEX_TIME_STEPPER_DECLARE_OVERLOADS                         \\\n  GENERATE_INSTANTIATIONS(IMEX_TIME_STEPPER_DECLARE_OVERLOADS_IMPL, \\\n                          (MATH_WRAPPER_TYPES))\n\n/// \\ingroup TimeSteppersGroup\n/// Macro defining overloaded detail methods in classes derived from\n/// ImexTimeStepper.  Must be placed in the cpp file for the derived\n/// class.\n#define IMEX_TIME_STEPPER_DEFINE_OVERLOADS(derived_class)          \\\n  GENERATE_INSTANTIATIONS(IMEX_TIME_STEPPER_DEFINE_OVERLOADS_IMPL, \\\n                          (MATH_WRAPPER_TYPES), (derived_class))\n"
  },
  {
    "path": "src/Time/TimeSteppers/LtsTimeStepper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\cond\nclass TimeDelta;\nclass TimeStepId;\n/// \\endcond\n\n/// \\cond\n#define LTS_TIME_STEPPER_WRAPPED_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define LTS_TIME_STEPPER_DERIVED_CLASS(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define LTS_TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data) \\\n  BOOST_PP_TUPLE_ELEM(2, data)\n/// \\endcond\n\n/// \\ingroup TimeSteppersGroup\n///\n/// Base class for TimeSteppers with local time-stepping support,\n/// derived from TimeStepper.\n///\n/// Several of the member functions of this class are templated and\n/// perform type erasure before forwarding their arguments to the\n/// derived classes.  This is implemented using the macros \\ref\n/// LTS_TIME_STEPPER_DECLARE_OVERLOADS, which must be placed in a\n/// private section of the class body, and\n/// LTS_TIME_STEPPER_DEFINE_OVERLOADS(derived_class), which must be\n/// placed in the cpp file.\nclass LtsTimeStepper : public virtual TimeStepper {\n public:\n  static constexpr bool local_time_stepping = true;\n  using provided_time_stepper_interfaces =\n      tmpl::list<LtsTimeStepper, TimeStepper>;\n\n  WRAPPED_PUPable_abstract(LtsTimeStepper);  // NOLINT\n\n  // These two are defined as separate type aliases to keep the\n  // doxygen page width somewhat under control.\n  template <typename LocalVars, typename RemoteVars, typename Coupling>\n  using BoundaryHistoryType = TimeSteppers::BoundaryHistory<\n      LocalVars, RemoteVars,\n      math_wrapper_type<\n          std::invoke_result_t<const Coupling&, LocalVars, RemoteVars>>>;\n\n  /// Return type of boundary-related functions.  The coupling returns\n  /// the derivative of the variables, but this is multiplied by the\n  /// time step so the return type should not have `dt` prefixes.\n  template <typename LocalVars, typename RemoteVars, typename Coupling>\n  using BoundaryReturn = db::unprefix_variables<\n      std::invoke_result_t<const Coupling&, LocalVars, RemoteVars>>;\n\n/// \\cond\n#define LTS_TIME_STEPPER_DECLARE_VIRTUALS_IMPL(_, data)            \\\n  virtual void add_boundary_delta_forward(                         \\\n      gsl::not_null<LTS_TIME_STEPPER_WRAPPED_TYPE(data)*> result,  \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,  \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times, \\\n      const TimeSteppers::BoundaryHistoryEvaluator<                \\\n          LTS_TIME_STEPPER_WRAPPED_TYPE(data)>& coupling,          \\\n      const TimeDelta& time_step) const = 0;                       \\\n  virtual void boundary_dense_output_forward(                      \\\n      gsl::not_null<LTS_TIME_STEPPER_WRAPPED_TYPE(data)*> result,  \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,  \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times, \\\n      const TimeSteppers::BoundaryHistoryEvaluator<                \\\n          LTS_TIME_STEPPER_WRAPPED_TYPE(data)>& coupling,          \\\n      double time) const = 0;\n\n  GENERATE_INSTANTIATIONS(LTS_TIME_STEPPER_DECLARE_VIRTUALS_IMPL,\n                          (MATH_WRAPPER_TYPES))\n#undef LTS_TIME_STEPPER_DECLARE_VIRTUALS_IMPL\n  /// \\endcond\n\n  /// \\brief Check whether a neighbor record is needed for boundary\n  /// output.\n  ///\n  /// In order to perform boundary output, all records from the\n  /// neighbor with `TimeStepId`s for which this method returns true\n  /// should have been added to the history.  Versions are provided\n  /// for a substep and for dense output.\n  /// @{\n  virtual bool neighbor_data_required(\n      const TimeStepId& next_substep_id,\n      const TimeStepId& neighbor_data_id) const = 0;\n\n  virtual bool neighbor_data_required(\n      double dense_output_time, const TimeStepId& neighbor_data_id) const = 0;\n  /// @}\n\n  /// \\brief Compute the change in a boundary quantity due to the\n  /// coupling on the interface.\n  ///\n  /// The coupling function `coupling` should take the local and\n  /// remote flux data and compute the derivative of the boundary\n  /// quantity.  These values may be used to form a linear combination\n  /// internally, so the result should have appropriate mathematical\n  /// operators defined to allow that.\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// void add_boundary_delta_impl(\n  ///     gsl::not_null<T*> result,\n  ///     const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n  ///     const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n  ///     const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n  ///     const TimeDelta& time_step) const;\n  /// ```\n  ///\n  /// \\note\n  /// Unlike the `update_u` methods, which overwrite the `result`\n  /// argument, this function adds the result to the existing value.\n  template <typename LocalVars, typename RemoteVars, typename Coupling>\n  void add_boundary_delta(\n      const gsl::not_null<BoundaryReturn<LocalVars, RemoteVars, Coupling>*>\n          result,\n      const BoundaryHistoryType<LocalVars, RemoteVars, Coupling>& history,\n      const TimeDelta& time_step, const Coupling& coupling) const {\n    return add_boundary_delta_forward(&*make_math_wrapper(result),\n                                      history.local(), history.remote(),\n                                      history.evaluator(coupling), time_step);\n  }\n\n  /// Remove old entries from the history.\n  ///\n  /// This should be called after update_u and dense output.\n  /// Afterward, the history will generally require a new local entry\n  /// to be added before it can be used by the TimeStepper.\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// void clean_boundary_history_impl(\n  ///     const TimeSteppers::MutableBoundaryHistoryTimes& local_times,\n  ///     const TimeSteppers::MutableBoundaryHistoryTimes& remote_times)\n  ///     const override;\n  /// ```\n  template <typename LocalVars, typename RemoteVars, typename CouplingResult>\n  void clean_boundary_history(\n      const gsl::not_null<\n          TimeSteppers::BoundaryHistory<LocalVars, RemoteVars, CouplingResult>*>\n          history) const {\n    return clean_boundary_history_impl(history->local(), history->remote());\n  }\n\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// void boundary_dense_output_impl(\n  ///     gsl::not_null<T*> result,\n  ///     const TimeSteppers::ConstBoundaryHistoryTimes& local_times,\n  ///     const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,\n  ///     const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,\n  ///     double time) const;\n  /// ```\n  template <typename LocalVars, typename RemoteVars, typename Coupling>\n  void boundary_dense_output(\n      const gsl::not_null<BoundaryReturn<LocalVars, RemoteVars, Coupling>*>\n          result,\n      const BoundaryHistoryType<LocalVars, RemoteVars, Coupling>& history,\n      const double time, const Coupling& coupling) const {\n    return boundary_dense_output_forward(&*make_math_wrapper(result),\n                                         history.local(), history.remote(),\n                                         history.evaluator(coupling), time);\n  }\n\n private:\n  virtual void clean_boundary_history_impl(\n      const TimeSteppers::MutableBoundaryHistoryTimes& local_times,\n      const TimeSteppers::MutableBoundaryHistoryTimes& remote_times) const = 0;\n};\n\n/// \\cond\n#define LTS_TIME_STEPPER_DECLARE_OVERLOADS_IMPL(_, data)           \\\n  void add_boundary_delta_forward(                                 \\\n      gsl::not_null<LTS_TIME_STEPPER_WRAPPED_TYPE(data)*> result,  \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,  \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times, \\\n      const TimeSteppers::BoundaryHistoryEvaluator<                \\\n          LTS_TIME_STEPPER_WRAPPED_TYPE(data)>& coupling,          \\\n      const TimeDelta& time_step) const override;                  \\\n  void boundary_dense_output_forward(                              \\\n      gsl::not_null<LTS_TIME_STEPPER_WRAPPED_TYPE(data)*> result,  \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,  \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times, \\\n      const TimeSteppers::BoundaryHistoryEvaluator<                \\\n          LTS_TIME_STEPPER_WRAPPED_TYPE(data)>& coupling,          \\\n      double time) const override;\n\n#define LTS_TIME_STEPPER_DEFINE_OVERLOADS_IMPL(_, data)                     \\\n  LTS_TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data)                             \\\n  void LTS_TIME_STEPPER_DERIVED_CLASS(data)::add_boundary_delta_forward(    \\\n      const gsl::not_null<LTS_TIME_STEPPER_WRAPPED_TYPE(data)*> result,     \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,           \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,          \\\n      const TimeSteppers::BoundaryHistoryEvaluator<                         \\\n          LTS_TIME_STEPPER_WRAPPED_TYPE(data)>& coupling,                   \\\n      const TimeDelta& time_step) const {                                   \\\n    return add_boundary_delta_impl(result, local_times, remote_times,       \\\n                                   coupling, time_step);                    \\\n  }                                                                         \\\n  LTS_TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data)                             \\\n  void LTS_TIME_STEPPER_DERIVED_CLASS(data)::boundary_dense_output_forward( \\\n      const gsl::not_null<LTS_TIME_STEPPER_WRAPPED_TYPE(data)*> result,     \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& local_times,           \\\n      const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,          \\\n      const TimeSteppers::BoundaryHistoryEvaluator<                         \\\n          LTS_TIME_STEPPER_WRAPPED_TYPE(data)>& coupling,                   \\\n      const double time) const {                                            \\\n    return boundary_dense_output_impl(result, local_times, remote_times,    \\\n                                      coupling, time);                      \\\n  }\n/// \\endcond\n\n/// \\ingroup TimeSteppersGroup\n/// Macro declaring overloaded detail methods in classes derived from\n/// TimeStepper.  Must be placed in a private section of the class\n/// body.\n#define LTS_TIME_STEPPER_DECLARE_OVERLOADS                         \\\n  GENERATE_INSTANTIATIONS(LTS_TIME_STEPPER_DECLARE_OVERLOADS_IMPL, \\\n                          (MATH_WRAPPER_TYPES))\n\n/// \\ingroup TimeSteppersGroup\n/// Macro defining overloaded detail methods in classes derived from\n/// TimeStepper.  Must be placed in the cpp file for the derived\n/// class.\n/// @{\n#define LTS_TIME_STEPPER_DEFINE_OVERLOADS(derived_class)          \\\n  GENERATE_INSTANTIATIONS(LTS_TIME_STEPPER_DEFINE_OVERLOADS_IMPL, \\\n                          (MATH_WRAPPER_TYPES), (derived_class), ())\n#define LTS_TIME_STEPPER_DEFINE_OVERLOADS_TEMPLATED(derived_class, \\\n                                                    template_args) \\\n  GENERATE_INSTANTIATIONS(LTS_TIME_STEPPER_DEFINE_OVERLOADS_IMPL,  \\\n                          (MATH_WRAPPER_TYPES), (derived_class),   \\\n                          (template <template_args>))\n/// @}\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk3HesthavenSsp.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n\n#include <cmath>\n#include <optional>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/LargestStepperError.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace TimeSteppers {\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nRk3HesthavenSsp::order() const {\n  return variants::TaggedVariant<Tags::FixedOrder>(3);\n}\n\ndouble Rk3HesthavenSsp::stable_step() const {\n  // This is the condition for  y' = -k y  to go to zero.\n  return 0.5 * (1. + cbrt(4. + sqrt(17.)) - 1. / cbrt(4. + sqrt(17.)));\n}\n\nbool Rk3HesthavenSsp::monotonic() const { return false; }\n\nuint64_t Rk3HesthavenSsp::number_of_substeps() const { return 3; }\n\nuint64_t Rk3HesthavenSsp::number_of_substeps_for_error() const { return 3; }\n\nsize_t Rk3HesthavenSsp::number_of_past_steps() const { return 0; }\n\nTimeStepId Rk3HesthavenSsp::next_time_id(const TimeStepId& current_id,\n                                         const TimeDelta& time_step) const {\n  switch (current_id.substep()) {\n    case 0:\n      return current_id.next_substep(time_step, 1.0);\n    case 1:\n      return current_id.next_substep(time_step, 0.5);\n    case 2:\n      return current_id.next_step(time_step);\n    default:\n      ERROR(\"Bad id: \" << current_id);\n  }\n}\n\nTimeStepId Rk3HesthavenSsp::next_time_id_for_error(\n    const TimeStepId& current_id, const TimeDelta& time_step) const {\n  return next_time_id(current_id, time_step);\n}\n\ntemplate <typename T>\nstd::optional<StepperErrorEstimate> Rk3HesthavenSsp::update_u_impl(\n    gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n    const TimeDelta& time_step,\n    const StepperErrorTolerances& tolerances) const {\n  ASSERT(history.integration_order() == get<Tags::FixedOrder>(order()),\n         \"Fixed-order stepper cannot run at order \"\n             << history.integration_order());\n\n  const auto substep = history.at_step_start() ? 0 : history.substeps().size();\n\n  std::optional<StepperErrorEstimate> error{};\n  if (substep == 2 and\n      tolerances.estimates != StepperErrorTolerances::Estimates::None) {\n    ASSERT(\n        tolerances.estimates == StepperErrorTolerances::Estimates::StepperOrder,\n        \"Lower-order error estimates not provided\");\n    *u = -(1.0 / 6.0) * *history.back().value +\n         (2.0 / 3.0) * *history.substeps()[1].value +\n         (2.0 / 3.0) * time_step.value() * history.substeps()[1].derivative -\n         0.5 * *history.substeps()[0].value -\n         0.5 * time_step.value() * history.substeps()[0].derivative;\n    error.emplace(history.back().time_step_id.step_time(), time_step, 2,\n                  largest_stepper_error(*history.back().value, *u, tolerances));\n  }\n\n  switch (substep) {\n    case 0:\n      *u =\n          *history.back().value + time_step.value() * history.back().derivative;\n      break;\n    case 1:\n      *u = 0.25 * (3.0 * *history.back().value + *history.substeps()[0].value +\n                   time_step.value() * history.substeps()[0].derivative);\n      break;\n    case 2:\n      *u = (1.0 / 3.0) *\n           (*history.back().value + 2.0 * *history.substeps()[1].value +\n            2.0 * time_step.value() * history.substeps()[1].derivative);\n      break;\n    default:\n      ERROR(\"Bad substep: \" << history.substeps().size());\n  }\n\n  return error;\n}\n\ntemplate <typename T>\nvoid Rk3HesthavenSsp::clean_history_impl(\n    const MutableUntypedHistory<T>& history) const {\n  if (history.at_step_start()) {\n    history.clear_substeps();\n  }\n  if (history.size() > 1) {\n    history.pop_front();\n  }\n  ASSERT(history.size() == 1, \"Too much history supplied.\");\n}\n\ntemplate <typename T>\nbool Rk3HesthavenSsp::dense_update_u_impl(const gsl::not_null<T*> u,\n                                          const ConstUntypedHistory<T>& history,\n                                          const double time) const {\n  const double step_start = history.front().time_step_id.step_time().value();\n  if (time == step_start) {\n    return true;\n  }\n  if (not history.at_step_start()) {\n    return false;\n  }\n  const double step_end = history.back().time_step_id.step_time().value();\n  const evolution_less<double> before{step_end > step_start};\n  if (history.size() == 1 or not before(time, step_end)) {\n    return false;\n  }\n  const double step_size = step_end - step_start;\n  const double output_fraction = (time - step_start) / step_size;\n  ASSERT(output_fraction >= 0.0, \"Attempting dense output at time \"\n                                     << time << \", but already progressed past \"\n                                     << step_start);\n\n  *u += -(2.0 / 3.0) * square(output_fraction) * *history.front().value +\n        output_fraction * (1.0 - output_fraction) * step_size *\n            history.front().derivative +\n        (2.0 / 3.0) * square(output_fraction) *\n            (*history.substeps()[1].value +\n             step_size * history.substeps()[1].derivative);\n\n  return true;\n}\n\ntemplate <typename T>\nbool Rk3HesthavenSsp::can_change_step_size_impl(\n    const TimeStepId& /*time_id*/,\n    const ConstUntypedHistory<T>& /*history*/) const {\n  return true;\n}\n\nTIME_STEPPER_DEFINE_OVERLOADS(Rk3HesthavenSsp)\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Rk3HesthavenSsp::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk3HesthavenSsp.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nclass TimeDelta;\nnamespace TimeSteppers {\ntemplate <typename T>\nclass ConstUntypedHistory;\ntemplate <typename T>\nclass MutableUntypedHistory;\n}  // namespace TimeSteppers\n/// \\endcond\n\nnamespace TimeSteppers {\n\n/// \\ingroup TimeSteppersGroup\n///\n/// A \"strong stability-preserving\" 3rd-order Runge-Kutta\n/// time-stepper, as described in \\cite HesthavenWarburton section\n/// 5.7.\n///\n/// The CFL factor/stable step size is 1.25637266330916.\n///\n/// \\note The time stepper is only strong-stability-preserving for\n/// time steps not exceeding 1.0, i.e., slightly less than 0.8 times\n/// the stable step.\nclass Rk3HesthavenSsp : public TimeStepper {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"A third-order strong stability-preserving Runge-Kutta time-stepper.\"};\n\n  Rk3HesthavenSsp() = default;\n  Rk3HesthavenSsp(const Rk3HesthavenSsp&) = default;\n  Rk3HesthavenSsp& operator=(const Rk3HesthavenSsp&) = default;\n  Rk3HesthavenSsp(Rk3HesthavenSsp&&) = default;\n  Rk3HesthavenSsp& operator=(Rk3HesthavenSsp&&) = default;\n  ~Rk3HesthavenSsp() override = default;\n\n  WRAPPED_PUPable_decl_template(Rk3HesthavenSsp);  // NOLINT\n\n  explicit Rk3HesthavenSsp(CkMigrateMessage* /*unused*/) {}\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  bool monotonic() const override;\n\n  uint64_t number_of_substeps() const override;\n\n  uint64_t number_of_substeps_for_error() const override;\n\n  size_t number_of_past_steps() const override;\n\n  TimeStepId next_time_id(const TimeStepId& current_id,\n                          const TimeDelta& time_step) const override;\n\n  TimeStepId next_time_id_for_error(const TimeStepId& current_id,\n                                    const TimeDelta& time_step) const override;\n\n private:\n  template <typename T>\n  std::optional<StepperErrorEstimate> update_u_impl(\n      gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n      const TimeDelta& time_step,\n      const StepperErrorTolerances& tolerances = {}) const;\n\n  template <typename T>\n  void clean_history_impl(const MutableUntypedHistory<T>& history) const;\n\n  template <typename T>\n  bool dense_update_u_impl(gsl::not_null<T*> u,\n                           const ConstUntypedHistory<T>& history,\n                           double time) const;\n\n  template <typename T>\n  bool can_change_step_size_impl(const TimeStepId& time_id,\n                                 const ConstUntypedHistory<T>& history) const;\n\n  TIME_STEPPER_DECLARE_OVERLOADS\n};\n\ninline bool constexpr operator==(const Rk3HesthavenSsp& /*lhs*/,\n                                 const Rk3HesthavenSsp& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Rk3HesthavenSsp& /*lhs*/,\n                                 const Rk3HesthavenSsp& /*rhs*/) {\n  return false;\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk3Kennedy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Rk3Kennedy.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nRk3Kennedy::order() const {\n  return variants::TaggedVariant<Tags::FixedOrder>(3);\n}\n\ndouble Rk3Kennedy::stable_step() const { return 1.832102281377816; }\n\nsize_t Rk3Kennedy::imex_order() const { return 3; }\n\nsize_t Rk3Kennedy::implicit_stage_order() const { return 2; }\n\n// The numbers in the tableaus are not exact.  Despite being written\n// as rationals, the true values are irrational numbers.\nconst RungeKutta::ButcherTableau& Rk3Kennedy::butcher_tableau() const {\n  static const ButcherTableau tableau{\n      // Substep times\n      {1767732205903.0 / 2027836641118.0, 3.0 / 5.0, 1.0},\n      // Substep coefficients\n      {{1767732205903.0 / 2027836641118.0},\n       {5535828885825.0 / 10492691773637.0, 788022342437.0 / 10882634858940.0},\n       {6485989280629.0 / 16251701735622.0, -4246266847089.0 / 9704473918619.0,\n        10755448449292.0 / 10357097424841.0}},\n      // Result coefficients\n      {1471266399579.0 / 7840856788654.0, -4482444167858.0 / 7529755066697.0,\n       11266239266428.0 / 11593286722821.0, 1767732205903.0 / 4055673282236.0},\n      // Coefficients for the embedded method for generating an error measure.\n      {2756255671327.0 / 12835298489170.0, -10771552573575.0 / 22201958757719.0,\n       9247589265047.0 / 10645013368117.0, 2193209047091.0 / 5459859503100.0},\n      // Dense output coefficient polynomials\n      {{0.0, 4655552711362.0 / 22874653954995.0,\n        -215264564351.0 / 13552729205753.0},\n       {0.0, -18682724506714.0 / 9892148508045.0,\n        17870216137069.0 / 13817060693119.0},\n       {0.0, 34259539580243.0 / 13192909600954.0,\n        -28141676662227.0 / 17317692491321.0},\n       {0.0, 584795268549.0 / 6622622206610.0,\n        2508943948391.0 / 7218656332882.0}}};\n  return tableau;\n}\n\nconst ImexRungeKutta::ImplicitButcherTableau&\nRk3Kennedy::implicit_butcher_tableau() const {\n  static const ImplicitButcherTableau tableau{\n      {{1767732205903.0 / 4055673282236.0, 1767732205903.0 / 4055673282236.0},\n       {2746238789719.0 / 10658868560708.0, -640167445237.0 / 6845629431997.0,\n        1767732205903.0 / 4055673282236.0},\n       {1471266399579.0 / 7840856788654.0, -4482444167858.0 / 7529755066697.0,\n        11266239266428.0 / 11593286722821.0,\n        1767732205903.0 / 4055673282236.0}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Rk3Kennedy::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk3Kennedy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/ImexRungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * \\brief A third-order Runge-Kutta method with IMEX support.\n *\n * The coefficients are given as ARK3(2)4L[2]SA in \\cite Kennedy2003.\n *\n * The implicit part is stiffly accurate and L-stable.\n *\n * The CFL factor/stable step size is 1.832102281377816.\n */\nclass Rk3Kennedy : public ImexRungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"A 3rd-order Runge-Kutta scheme devised by Kennedy and Carpenter.\"};\n\n  Rk3Kennedy() = default;\n  Rk3Kennedy(const Rk3Kennedy&) = default;\n  Rk3Kennedy& operator=(const Rk3Kennedy&) = default;\n  Rk3Kennedy(Rk3Kennedy&&) = default;\n  Rk3Kennedy& operator=(Rk3Kennedy&&) = default;\n  ~Rk3Kennedy() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  size_t imex_order() const override;\n\n  size_t implicit_stage_order() const override;\n\n  WRAPPED_PUPable_decl_template(Rk3Kennedy);  // NOLINT\n\n  explicit Rk3Kennedy(CkMigrateMessage* /*unused*/) {}\n\n  const ButcherTableau& butcher_tableau() const override;\n\n  const ImplicitButcherTableau& implicit_butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const Rk3Kennedy& /*lhs*/,\n                                 const Rk3Kennedy& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Rk3Kennedy& lhs, const Rk3Kennedy& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk3Owren.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Rk3Owren.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> Rk3Owren::order()\n    const {\n  return variants::TaggedVariant<Tags::FixedOrder>(3);\n}\n\n// The stability polynomial is\n//\n//   p(z) = \\sum_{n=0}^{stages-1} alpha_n z^n / n!,\n//\n// alpha_n=1.0 for n=1...(order-1).\ndouble Rk3Owren::stable_step() const { return 1.2563726633091645; }\n\nconst RungeKutta::ButcherTableau& Rk3Owren::butcher_tableau() const {\n  static const ButcherTableau tableau{\n      // Substep times\n      {12.0 / 23.0, 4.0 / 5.0},\n      // Substep coefficients\n      {{12.0 / 23.0},\n       {-68.0 / 375.0, 368.0 / 375.0}},\n      // Result coefficients\n      {31.0 / 144.0, 529.0 / 1152.0, 125.0 / 384.0},\n      // Coefficients for the embedded method for generating an error measure.\n      {1.0 / 24.0, 23.0 / 24.0, 0.0},\n      // Dense output coefficient polynomials\n      {{0.0, 1.0, -65.0 / 48.0, 41.0 / 72.0},\n       {0.0, 0.0, 529.0 / 384.0, -529.0 / 576.0},\n       {0.0, 0.0, 125.0 / 128.0, -125.0 / 192.0},\n       {0.0, 0.0, -1.0, 1.0}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Rk3Owren::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk3Owren.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * \\brief A third order continuous-extension RK method that provides 3rd-order\n * dense output.\n *\n * \\f{eqnarray}{\n * \\frac{du}{dt} & = & \\mathcal{L}(t,u).\n * \\f}\n * Given a solution \\f$u(t^n)=u^n\\f$, this stepper computes\n * \\f$u(t^{n+1})=u^{n+1}\\f$ using the following equations:\n *\n * \\f{align}{\n * k^{(i)} & = \\mathcal{L}(t^n + c_i \\Delta t,\n *                         u^n + \\Delta t \\sum_{j=1}^{i-1} a_{ij} k^{(j)}),\n *                              \\mbox{ } 1 \\leq i \\leq s,\\\\\n * u^{n+1}(t^n + \\theta \\Delta t) & = u^n + \\Delta t \\sum_{i=1}^{s} b_i(\\theta)\n * k^{(i)}. \\f}\n *\n * Here the coefficients \\f$a_{ij}\\f$, \\f$b_i\\f$, and \\f$c_i\\f$ are given\n * in \\cite Owren1992 and \\cite Gassner20114232. Note that \\f$c_1 = 0\\f$,\n * \\f$s\\f$ is the number of stages, and \\f$\\theta\\f$ is the fraction of the\n * step. This is an FSAL stepper.\n *\n * The CFL factor/stable step size is 1.2563726633091645.\n */\nclass Rk3Owren : public RungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"A 3rd-order continuous extension Runge-Kutta method.\"};\n\n  Rk3Owren() = default;\n  Rk3Owren(const Rk3Owren&) = default;\n  Rk3Owren& operator=(const Rk3Owren&) = default;\n  Rk3Owren(Rk3Owren&&) = default;\n  Rk3Owren& operator=(Rk3Owren&&) = default;\n  ~Rk3Owren() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  WRAPPED_PUPable_decl_template(Rk3Owren);  // NOLINT\n\n  explicit Rk3Owren(CkMigrateMessage* /*unused*/) {}\n\n  const ButcherTableau& butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const Rk3Owren& /*lhs*/,\n                                 const Rk3Owren& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Rk3Owren& /*lhs*/,\n                                 const Rk3Owren& /*rhs*/) {\n  return false;\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk3Pareschi.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Rk3Pareschi.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nRk3Pareschi::order() const {\n  return variants::TaggedVariant<Tags::FixedOrder>(3);\n}\n\ndouble Rk3Pareschi::stable_step() const { return 1.25637; }\n\nsize_t Rk3Pareschi::imex_order() const { return 3; }\n\nsize_t Rk3Pareschi::implicit_stage_order() const { return 0; }\n\nnamespace {\nconst double alpha = 0.24169426078821;\nconst double beta = 0.06042356519705;\nconst double eta = 0.12915286960590;\n}  // namespace\n\nconst RungeKutta::ButcherTableau& Rk3Pareschi::butcher_tableau() const {\n  static const ButcherTableau tableau{\n      // Substep times\n      {0.0, 0.0, 1.0, 0.5},\n      // Substep coefficients\n      {{0.0},\n       {0.0},\n       {0.0, 0.0, 1.0},\n       {0.0, 0.0, 1.0 / 4.0, 1.0 / 4.0}},\n      // Result coefficients\n      {0.0, 0.0, 1.0 / 6.0, 1.0 / 6.0, 2.0 / 3.0},\n      // Coefficients for the embedded method for generating an error measure.\n      //\n      // Not given in the reference.  Any set of coefficients of the form\n      // (0, 0, x, x, 1 - 2 x) works.\n      {0.0, 0.0, 0.25, 0.25, 0.5},\n      // Dense output coefficient polynomials\n      {{},\n       {0.0, 1.0, -1.0},\n       {0.0, 0.0, 1.0 / 6.0},\n       {0.0, 0.0, 1.0 / 6.0},\n       {0.0, 0.0, 2.0 / 3.0}}};\n  return tableau;\n}\n\nconst ImexRungeKutta::ImplicitButcherTableau&\nRk3Pareschi::implicit_butcher_tableau() const {\n  static const ImplicitButcherTableau tableau{\n      {{0.0, alpha},  // This stage is zeroth-order\n       {0.0, -alpha, alpha},\n       {0.0, 0.0, 1 - alpha, alpha},\n       {0.0, beta, eta, 0.5 - beta - eta - alpha, alpha}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Rk3Pareschi::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk3Pareschi.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/ImexRungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * \\brief A third-order Runge-Kutta method with IMEX support.\n *\n * The method as published has four stages, but is implemented with\n * five as a way to convert it to an EDIRK method.\n *\n * The coefficients are given as IMEX-SSP3(4,3,3) in \\cite Pareschi2005.\n *\n * While this method can be implemented so that the explicit part is\n * strong-stability-preserving, the presentation in \\cite Pareschi2005\n * is not, and this implementation follows that presentation.  See\n * \\cite HesthavenWarburton section 5.7 for details.\n *\n * Using this time stepper in a non-IMEX simulation is not\n * recommended, as it performs two unused RHS evaluations.  When using\n * IMEX it performs one extra evaluation because there are more\n * implicit steps than explicit.\n *\n * The implicit portion is L-stable.\n *\n * The CFL factor/stable step size is 1.25637.\n */\nclass Rk3Pareschi : public ImexRungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"A 3rd-order 4 stage Runge-Kutta scheme devised by Pareschi and Russo.\"};\n\n  Rk3Pareschi() = default;\n  Rk3Pareschi(const Rk3Pareschi&) = default;\n  Rk3Pareschi& operator=(const Rk3Pareschi&) = default;\n  Rk3Pareschi(Rk3Pareschi&&) = default;\n  Rk3Pareschi& operator=(Rk3Pareschi&&) = default;\n  ~Rk3Pareschi() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  size_t imex_order() const override;\n\n  size_t implicit_stage_order() const override;\n\n  WRAPPED_PUPable_decl_template(Rk3Pareschi);  // NOLINT\n\n  explicit Rk3Pareschi(CkMigrateMessage* /*unused*/) {}\n\n  const ButcherTableau& butcher_tableau() const override;\n\n  const ImplicitButcherTableau& implicit_butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const Rk3Pareschi& /*lhs*/,\n                                 const Rk3Pareschi& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Rk3Pareschi& lhs,\n                                 const Rk3Pareschi& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk4Kennedy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Rk4Kennedy.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nRk4Kennedy::order() const {\n  return variants::TaggedVariant<Tags::FixedOrder>(4);\n}\n\ndouble Rk4Kennedy::stable_step() const { return 2.1172491998184686; }\n\nsize_t Rk4Kennedy::imex_order() const { return 4; }\n\nsize_t Rk4Kennedy::implicit_stage_order() const { return 2; }\n\n// The numbers in the tableaus are not exact.  Despite being written\n// as rationals, the true values are irrational numbers.\nconst RungeKutta::ButcherTableau& Rk4Kennedy::butcher_tableau() const {\n  static const ButcherTableau tableau{\n      // Substep times\n      {1.0 / 2.0, 83.0 / 250.0, 31.0 / 50.0, 17.0 / 20.0, 1.0},\n      // Substep coefficients\n      {{1.0 / 2.0},\n       {13861.0 / 62500.0, 6889.0 / 62500.0},\n       {-116923316275.0 / 2393684061468.0, -2731218467317.0 / 15368042101831.0,\n        9408046702089.0 / 11113171139209.0},\n       {-451086348788.0 / 2902428689909.0, -2682348792572.0 / 7519795681897.0,\n        12662868775082.0 / 11960479115383.0,\n        3355817975965.0 / 11060851509271.0},\n       {647845179188.0 / 3216320057751.0, 73281519250.0 / 8382639484533.0,\n        552539513391.0 / 3454668386233.0, 3354512671639.0 / 8306763924573.0,\n        4040.0 / 17871.0}},\n      // Result coefficients\n      {82889.0 / 524892.0, 0.0, 15625.0 / 83664.0, 69875.0 / 102672.0,\n       -2260.0 / 8211.0, 1.0 / 4.0},\n      // Coefficients for the embedded method for generating an error measure.\n      {4586570599.0 / 29645900160.0, 0.0, 178811875.0 / 945068544.0,\n       814220225.0 / 1159782912.0, -3700637.0 / 11593932.0, 61727.0 / 225920.0},\n      // Dense output coefficient polynomials\n      {{0.0, 6943876665148.0 / 7220017795957.0, -54480133.0 / 30881146.0,\n        6818779379841.0 / 7100303317025.0},\n       {},\n       {0.0, 7640104374378.0 / 9702883013639.0, -11436875.0 / 14766696.0,\n        2173542590792.0 / 12501825683035.0},\n       {0.0, -20649996744609.0 / 7521556579894.0, 174696575.0 / 18121608.0,\n        -31592104683404.0 / 5083833661969.0},\n       {0.0, 8854892464581.0 / 2390941311638.0, -12120380.0 / 966161.0,\n        61146701046299.0 / 7138195549469.0},\n       {0.0, -11397109935349.0 / 6675773540249.0, 3843.0 / 706.0,\n        -17219254887155.0 / 4939391667607.0}}};\n  return tableau;\n}\n\nconst ImexRungeKutta::ImplicitButcherTableau&\nRk4Kennedy::implicit_butcher_tableau() const {\n  static const ImplicitButcherTableau tableau{\n      {{1.0 / 4.0, 1.0 / 4.0},\n       {8611.0 / 62500.0, -1743.0 / 31250.0, 1.0 / 4.0},\n       {5012029.0 / 34652500.0, -654441.0 / 2922500.0, 174375.0 / 388108.0,\n        1.0 / 4.0},\n       {15267082809.0 / 155376265600.0, -71443401.0 / 120774400.0,\n        730878875.0 / 902184768.0, 2285395.0 / 8070912.0, 1.0 / 4.0},\n       {82889.0 / 524892.0, 0.0, 15625.0 / 83664.0, 69875.0 / 102672.0,\n        -2260.0 / 8211.0, 1.0 / 4.0}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Rk4Kennedy::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk4Kennedy.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/ImexRungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * \\brief A fourth-order Runge-Kutta method with IMEX support.\n *\n * The coefficients are given as ARK4(3)6L[2]SA in \\cite Kennedy2003.\n *\n * The implicit part is stiffly accurate and L-stable.\n *\n * The CFL factor/stable step size is 2.1172491998184686.\n */\nclass Rk4Kennedy : public ImexRungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"A 4th-order Runge-Kutta scheme devised by Kennedy and Carpenter.\"};\n\n  Rk4Kennedy() = default;\n  Rk4Kennedy(const Rk4Kennedy&) = default;\n  Rk4Kennedy& operator=(const Rk4Kennedy&) = default;\n  Rk4Kennedy(Rk4Kennedy&&) = default;\n  Rk4Kennedy& operator=(Rk4Kennedy&&) = default;\n  ~Rk4Kennedy() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  size_t imex_order() const override;\n\n  size_t implicit_stage_order() const override;\n\n  WRAPPED_PUPable_decl_template(Rk4Kennedy);  // NOLINT\n\n  explicit Rk4Kennedy(CkMigrateMessage* /*unused*/) {}\n\n  const ButcherTableau& butcher_tableau() const override;\n\n  const ImplicitButcherTableau& implicit_butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const Rk4Kennedy& /*lhs*/,\n                                 const Rk4Kennedy& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Rk4Kennedy& lhs, const Rk4Kennedy& rhs) {\n  return not(lhs == rhs);\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk4Owren.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Rk4Owren.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\nRk4Owren::Rk4Owren(CkMigrateMessage* /*msg*/) {}\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> Rk4Owren::order()\n    const {\n  return variants::TaggedVariant<Tags::FixedOrder>(4);\n}\n\n// The stability polynomial is\n//\n//   p(z) = \\sum_{n=0}^{stages-1} alpha_n z^n / n!,\n//\n// alpha_n=1.0 for n=1...(order-1). For the fourth order method:\n//  alpha_5 = 5 (1 - 2 c_3) c_4\n// The stability limit as compared to a forward Euler method is given by finding\n// the root for |p(-2 z)|-1=0. For forward Euler this is 1.0.\ndouble Rk4Owren::stable_step() const { return 1.4367588951002057; }\n\nconst RungeKutta::ButcherTableau& Rk4Owren::butcher_tableau() const {\n  static const ButcherTableau tableau{\n      // Substep times\n      {1.0 / 6.0, 11.0 / 37.0, 11.0 / 17.0, 13.0 / 15.0},\n      // Substep coefficients\n      {{1.0 / 6.0},\n       {44.0 / 1369.0, 363.0 / 1369.0},\n       {3388.0 / 4913.0, -8349.0 / 4913.0, 8140.0 / 4913.0},\n       {-36764.0 / 408375.0, 767.0 / 1125.0, -32708.0 / 136125.0,\n        210392.0 / 408375.0}},\n      // Result coefficients\n      {1697.0 / 18876.0, 0.0, 50653.0 / 116160.0, 299693.0 / 1626240.0,\n       3375.0 / 11648.0},\n      // Coefficients for the embedded method for generating an error measure.\n      {101.0 / 363.0, 0.0, -1369.0 / 14520.0, 11849.0 / 14520.0, 0.0},\n      // Dense output coefficient polynomials\n      {{0.0, 1.0, -104217.0 / 37466.0, 1806901.0 / 618189.0,\n        -866577.0 / 824252.0},\n       {},\n       {0.0, 0.0, 861101.0 / 230560.0, -2178079.0 / 380424.0,\n        12308679.0 / 5072320.0},\n       {0.0, 0.0, -63869.0 / 293440.0, 6244423.0 / 5325936.0,\n        -7816583.0 / 10144640.0},\n       {0.0, 0.0, -1522125.0 / 762944.0, 982125.0 / 190736.0,\n        -624375.0 / 217984.0},\n       {0.0, 0.0, 165.0 / 131.0, -461.0 / 131.0, 296.0 / 131.0}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Rk4Owren::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk4Owren.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * \\brief A fourth order continuous-extension RK method that provides 4th-order\n * dense output.\n *\n * \\f{eqnarray}{\n * \\frac{du}{dt} & = & \\mathcal{L}(t,u).\n * \\f}\n * Given a solution \\f$u(t^n)=u^n\\f$, this stepper computes\n * \\f$u(t^{n+1})=u^{n+1}\\f$ using the following equations:\n *\n * \\f{align}{\n * k^{(i)} & = \\mathcal{L}(t^n + c_i \\Delta t,\n *                         u^n + \\Delta t \\sum_{j=1}^{i-1} a_{ij} k^{(j)}),\n *                              \\mbox{ } 1 \\leq i \\leq s,\\\\\n * u^{n+1}(t^n + \\theta \\Delta t) & = u^n + \\Delta t \\sum_{i=1}^{s} b_i(\\theta)\n * k^{(i)}. \\f}\n *\n * Here the coefficients \\f$a_{ij}\\f$, \\f$b_i\\f$, and \\f$c_i\\f$ are given\n * in \\cite Owren1992 and \\cite Gassner20114232. Note that \\f$c_1 = 0\\f$,\n * \\f$s\\f$ is the number of stages, and \\f$\\theta\\f$ is the fraction of the\n * step. This is an FSAL stepper.\n *\n * The CFL factor/stable step size is 1.4367588951002057.\n */\nclass Rk4Owren : public RungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"A 4th-order continuous extension Runge-Kutta time stepper.\"};\n\n  Rk4Owren() = default;\n  Rk4Owren(const Rk4Owren&) = default;\n  Rk4Owren& operator=(const Rk4Owren&) = default;\n  Rk4Owren(Rk4Owren&&) = default;\n  Rk4Owren& operator=(Rk4Owren&&) = default;\n  ~Rk4Owren() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  WRAPPED_PUPable_decl_template(Rk4Owren);  // NOLINT\n\n  explicit Rk4Owren(CkMigrateMessage* /*msg*/);\n\n  const ButcherTableau& butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const Rk4Owren& /*lhs*/,\n                                 const Rk4Owren& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Rk4Owren& /*lhs*/,\n                                 const Rk4Owren& /*rhs*/) {\n  return false;\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk5Owren.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Rk5Owren.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\nRk5Owren::Rk5Owren(CkMigrateMessage* /*msg*/) {}\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> Rk5Owren::order()\n    const {\n  return variants::TaggedVariant<Tags::FixedOrder>(5);\n}\n\n// The stability polynomial is\n//\n//   p(z) = \\sum_{n=0}^{stages-1} alpha_n z^n / n!,\n//\n// alpha_n=1.0 for n=1...(order-1). For the fifth order method:\n//  alpha_6 = 6 (-5 c3**2 + 2 c3) - 2 c6 beta\n//  alpha_7 = 14 c3 c6 beta\n// where\n//   beta = 20 c3**2 - 15 c3 + 3\n// The stability limit as compared to a forward Euler method is given by finding\n// the root for |p(-2 z)|-1=0. For forward Euler this is 1.0.\ndouble Rk5Owren::stable_step() const { return 1.5961737362090775; }\n\nconst RungeKutta::ButcherTableau& Rk5Owren::butcher_tableau() const {\n  static const ButcherTableau tableau{\n      // Substep times\n      {1.0 / 6.0, 1.0 / 4.0, 1.0 / 2.0, 1.0 / 2.0, 9.0 / 14.0, 7.0 / 8.0},\n      // Substep coefficients\n      {{1.0 / 6.0},\n       {1.0 / 16.0, 3.0 / 16.0},\n       {0.25, -0.75, 1.0},\n       {-0.75, 15.0 / 4.0, -3.0, 0.5},\n       {369.0 / 1372.0, -243.0 / 343.0, 297.0 / 343.0, 1485.0 / 9604.0,\n        297.0 / 4802.0},\n       {-133.0 / 4512.0, 1113.0 / 6016.0, 7945.0 / 16544.0, -12845.0 / 24064.0,\n        -315.0 / 24064.0, 156065.0 / 198528.0}},\n      // Result coefficients\n      {83.0 / 945.0, 0.0, 248.0 / 825.0, 41.0 / 180.0, 1.0 / 36.0,\n       2401.0 / 38610.0, 6016.0 / 20475.0},\n      // Coefficients for the embedded method for generating an error measure.\n      {-1.0 / 9.0, 0.0, 40.0 / 33.0, -7.0 / 4.0, -1.0 / 12.0, 343.0 / 198.0,\n       0.0},\n      // Dense output coefficient polynomials\n      {{0.0, 1.0, -3292.0 / 819.0, 17893.0 / 2457.0, -4969.0 / 819.0,\n        596.0 / 315.0},\n       {},\n       {0.0, 0.0, 5112.0 / 715.0, -43568.0 / 2145.0, 1344.0 / 65.0,\n        -1984.0 / 275.0},\n       {0.0, 0.0, -123.0 / 52.0, 3161.0 / 234.0, -1465.0 / 78.0, 118.0 / 15.0},\n       {0.0, 0.0, -63.0 / 52.0, 1061.0 / 234.0, -413.0 / 78.0, 2.0},\n       {0.0, 0.0, -40817.0 / 33462.0, 60025.0 / 50193.0, 2401.0 / 1521.0,\n        -9604.0 / 6435.0},\n       {0.0, 0.0, 18048.0 / 5915.0, -637696.0 / 53235.0, 96256.0 / 5915.0,\n        -48128.0 / 6825.0},\n       {0.0, 0.0, -18.0 / 13.0, 75.0 / 13.0, -109.0 / 13.0, 4.0}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Rk5Owren::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk5Owren.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * \\brief A fifth order continuous-extension RK method that provides 5th-order\n * dense output.\n *\n * \\f{eqnarray}{\n * \\frac{du}{dt} & = & \\mathcal{L}(t,u).\n * \\f}\n * Given a solution \\f$u(t^n)=u^n\\f$, this stepper computes\n * \\f$u(t^{n+1})=u^{n+1}\\f$ using the following equations:\n *\n * \\f{align}{\n * k^{(i)} & = \\mathcal{L}(t^n + c_i \\Delta t,\n *                         u^n + \\Delta t \\sum_{j=1}^{i-1} a_{ij} k^{(j)}),\n *                              \\mbox{ } 1 \\leq i \\leq s,\\\\\n * u^{n+1}(t^n + \\theta \\Delta t) & = u^n + \\Delta t \\sum_{i=1}^{s} b_i(\\theta)\n * k^{(i)}. \\f}\n *\n * Here the coefficients \\f$a_{ij}\\f$, \\f$b_i\\f$, and \\f$c_i\\f$ are given\n * in \\cite Owren1992 and \\cite Gassner20114232. Note that \\f$c_1 = 0\\f$,\n * \\f$s\\f$ is the number of stages, and \\f$\\theta\\f$ is the fraction of the\n * step. This is an FSAL stepper.\n *\n * The CFL factor/stable step size is 1.5961737362090775.\n */\nclass Rk5Owren : public RungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"A 5th-order continuous extension Runge-Kutta time stepper.\"};\n\n  Rk5Owren() = default;\n  Rk5Owren(const Rk5Owren&) = default;\n  Rk5Owren& operator=(const Rk5Owren&) = default;\n  Rk5Owren(Rk5Owren&&) = default;\n  Rk5Owren& operator=(Rk5Owren&&) = default;\n  ~Rk5Owren() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  WRAPPED_PUPable_decl_template(Rk5Owren);  // NOLINT\n\n  explicit Rk5Owren(CkMigrateMessage* /*msg*/);\n\n  const ButcherTableau& butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const Rk5Owren& /*lhs*/,\n                                 const Rk5Owren& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Rk5Owren& /*lhs*/,\n                                 const Rk5Owren& /*rhs*/) {\n  return false;\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk5Tsitouras.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/Rk5Tsitouras.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n\nnamespace TimeSteppers {\nRk5Tsitouras::Rk5Tsitouras(CkMigrateMessage* /*msg*/) {}\n\nvariants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder>\nRk5Tsitouras::order() const {\n  return variants::TaggedVariant<Tags::FixedOrder>(5);\n}\n\ndouble Rk5Tsitouras::stable_step() const { return 1.7534234969024887; }\n\nconst RungeKutta::ButcherTableau& Rk5Tsitouras::butcher_tableau() const {\n  static const ButcherTableau tableau{\n      // Substep times\n      {0.161, 0.327, 0.9, 0.9800255409045097, 1.0, 1.0},\n      // Substep coefficients\n      {{0.161},\n       {-0.008480655492356992, 0.3354806554923570},\n       {2.897153057105495, -6.359448489975075, 4.362295432869581},\n       {5.32586482843926, -11.74888356406283, 7.495539342889836,\n        -0.09249506636175525},\n       {5.86145544294642, -12.92096931784711, 8.159367898576159,\n        -0.07158497328140100, -0.02826905039406838},\n       {0.09646076681806523, 0.01, 0.4798896504144996, 1.379008574103742,\n        -3.290069515436081, 2.324710524099774}},\n      // Result coefficients\n      {0.09646076681806523, 0.01, 0.4798896504144996, 1.379008574103742,\n       -3.290069515436081, 2.324710524099774},\n      // Coefficients for the embedded method for generating an error\n      // measure.  The values given in the reference are actually the\n      // differences between these and the result coefficients, and\n      // the last value has a sign error.\n      {0.09824077787029123, 0.010816434459657, 0.4720087724042376,\n       1.5237195812770048, -3.872426680888636, 2.7827926300289607, -1.0 / 66.0},\n      // Dense output coefficient polynomials\n      {{0.0, 1.0, -2.763706197274826, 2.9132554618219126, -1.0530884977290216},\n       {0.0, 0.0, 0.1317, -0.2234, 0.1017},\n       {0.0, 0.0, 3.930296236894751, -5.941033872131505, 2.490627285651252793},\n       {0.0, 0.0, -12.411077166933676, 30.33818863028232,\n        -16.54810288924490272},\n       {0.0, 0.0, 37.50931341651104, -88.1789048947664, 47.37952196281928122},\n       {0.0, 0.0, -27.89652628919729, 65.09189467479368, -34.87065786149660974},\n       {},\n       {0.0, 0.0, 1.5, -4.0, 2.5}}};\n  return tableau;\n}\n}  // namespace TimeSteppers\n\nPUP::able::PUP_ID TimeSteppers::Rk5Tsitouras::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "src/Time/TimeSteppers/Rk5Tsitouras.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * \\brief A fifth order RK method constructed with fewer restrictions\n * on its coefficients than is common.  On a standard test suite, it\n * was found to be roughly 10% more efficient than\n * DormandPrince5.\\cite Tsitouras2011.\n *\n * The CFL stable step size is 1.7534234969024887.\n */\nclass Rk5Tsitouras : public RungeKutta {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\n      \"An efficient 5th-order Runge-Kutta time stepper.\"};\n\n  Rk5Tsitouras() = default;\n  Rk5Tsitouras(const Rk5Tsitouras&) = default;\n  Rk5Tsitouras& operator=(const Rk5Tsitouras&) = default;\n  Rk5Tsitouras(Rk5Tsitouras&&) = default;\n  Rk5Tsitouras& operator=(Rk5Tsitouras&&) = default;\n  ~Rk5Tsitouras() override = default;\n\n  variants::TaggedVariant<Tags::FixedOrder, Tags::VariableOrder> order()\n      const override;\n\n  double stable_step() const override;\n\n  WRAPPED_PUPable_decl_template(Rk5Tsitouras);  // NOLINT\n\n  explicit Rk5Tsitouras(CkMigrateMessage* /*msg*/);\n\n  const ButcherTableau& butcher_tableau() const override;\n};\n\ninline bool constexpr operator==(const Rk5Tsitouras& /*lhs*/,\n                                 const Rk5Tsitouras& /*rhs*/) {\n  return true;\n}\n\ninline bool constexpr operator!=(const Rk5Tsitouras& /*lhs*/,\n                                 const Rk5Tsitouras& /*rhs*/) {\n  return false;\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/RungeKutta.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n\n#include <algorithm>\n#include <optional>\n\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/LargestStepperError.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace TimeSteppers {\n\nuint64_t RungeKutta::number_of_substeps() const {\n  return butcher_tableau().result_coefficients.size();\n}\n\nuint64_t RungeKutta::number_of_substeps_for_error() const {\n  return std::max(butcher_tableau().result_coefficients.size(),\n                  butcher_tableau().error_coefficients.size());\n}\n\nsize_t RungeKutta::number_of_past_steps() const { return 0; }\n\nbool RungeKutta::monotonic() const { return false; }\n\nnamespace {\nTimeStepId next_time_id_from_substeps(\n    const TimeStepId& current_id, const TimeDelta& time_step,\n    const std::vector<double>& substep_times,\n    const size_t number_of_substeps) {\n  ASSERT(substep_times.size() + 1 >= number_of_substeps,\n         \"More result coefficients than substeps\");\n  const auto substep = current_id.substep();\n\n  if (substep >= number_of_substeps) {\n    ERROR(\"In substep should be less than the number of steps, not \"\n          << substep << \"/\" << number_of_substeps);\n  } else if (substep == number_of_substeps - 1) {\n    return current_id.next_step(time_step);\n  } else {\n    return current_id.next_substep(time_step, substep_times[substep]);\n  }\n}\n}  // namespace\n\nTimeStepId RungeKutta::next_time_id(const TimeStepId& current_id,\n                                    const TimeDelta& time_step) const {\n  return next_time_id_from_substeps(current_id, time_step,\n                                    butcher_tableau().substep_times,\n                                    number_of_substeps());\n}\n\nTimeStepId RungeKutta::next_time_id_for_error(\n    const TimeStepId& current_id, const TimeDelta& time_step) const {\n  return next_time_id_from_substeps(current_id, time_step,\n                                    butcher_tableau().substep_times,\n                                    number_of_substeps_for_error());\n}\n\nnamespace {\ntemplate <typename T>\nvoid compute_substep(const gsl::not_null<T*> u,\n                     const ConstUntypedHistory<T>& history, const double dt,\n                     const std::vector<double>& substep_coefficients) {\n  *u = *history.back().value;\n  if (substep_coefficients[0] != 0.0) {\n    *u += substep_coefficients[0] * dt * history.back().derivative;\n  }\n  for (size_t i = 1; i < substep_coefficients.size(); ++i) {\n    if (substep_coefficients[i] != 0.0) {\n      *u += substep_coefficients[i] * dt * history.substeps()[i - 1].derivative;\n    }\n  }\n}\n\ntemplate <typename T>\nvoid step_error(const gsl::not_null<T*> u_error,\n                const ConstUntypedHistory<T>& history, const double dt,\n                const RungeKutta::ButcherTableau& tableau) {\n  const auto& coefficients = tableau.result_coefficients;\n  const auto& error_coefficients = tableau.error_coefficients;\n\n  *u_error = (coefficients[0] - error_coefficients[0]) * dt *\n             history.back().derivative;\n  const size_t num_coefficients =\n      std::max(coefficients.size(), error_coefficients.size());\n  for (size_t i = 1; i < num_coefficients; ++i) {\n    const double coefficient =\n        (i < coefficients.size() ? coefficients[i] : 0.0) -\n        (i < error_coefficients.size() ? error_coefficients[i] : 0.0);\n    if (coefficient != 0.0) {\n      *u_error += coefficient * dt * history.substeps()[i - 1].derivative;\n    }\n  }\n}\n\ntemplate <typename T>\nvoid update_u_impl_with_tableau(const gsl::not_null<T*> u,\n                                const ConstUntypedHistory<T>& history,\n                                const TimeDelta& time_step,\n                                const RungeKutta::ButcherTableau& tableau,\n                                const size_t number_of_substeps) {\n  const double dt = time_step.value();\n\n  const auto substep = history.at_step_start() ? 0 : history.substeps().size();\n  if (substep == number_of_substeps - 1) {\n    compute_substep(u, history, dt, tableau.result_coefficients);\n  } else if (substep < number_of_substeps - 1) {\n    compute_substep(u, history, dt, tableau.substep_coefficients[substep]);\n  } else {\n    ERROR(\"Substep should be less than \" << number_of_substeps << \", not \"\n                                         << substep);\n  }\n}\n}  // namespace\n\ntemplate <typename T>\nvoid RungeKutta::update_u_impl(const gsl::not_null<T*> u,\n                               const ConstUntypedHistory<T>& history,\n                               const TimeDelta& time_step) const {\n  ASSERT(history.integration_order() == get<Tags::FixedOrder>(order()),\n         \"Fixed-order stepper cannot run at order \"\n             << history.integration_order());\n  return update_u_impl_with_tableau(u, history, time_step, butcher_tableau(),\n                                    number_of_substeps());\n}\n\ntemplate <typename T>\nstd::optional<StepperErrorEstimate> RungeKutta::update_u_impl(\n    gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n    const TimeDelta& time_step,\n    const StepperErrorTolerances& tolerances) const {\n  ASSERT(history.integration_order() == get<Tags::FixedOrder>(order()),\n         \"Fixed-order stepper cannot run at order \"\n             << history.integration_order());\n\n  const auto& tableau = butcher_tableau();\n  const auto number_of_substeps = number_of_substeps_for_error();\n  const size_t substep =\n      history.at_step_start() ? 0 : history.substeps().size();\n  std::optional<StepperErrorEstimate> error{};\n  if (substep == number_of_substeps - 1 and\n      tolerances.estimates != StepperErrorTolerances::Estimates::None) {\n    ASSERT(\n        tolerances.estimates == StepperErrorTolerances::Estimates::StepperOrder,\n        \"Lower-order error estimates not provided\");\n    const double dt = time_step.value();\n    step_error(u, history, dt, tableau);\n    error.emplace(history.back().time_step_id.step_time(), time_step,\n                  get<Tags::FixedOrder>(order()) - 1,\n                  largest_stepper_error(*history.back().value, *u, tolerances));\n  }\n\n  update_u_impl_with_tableau(u, history, time_step, tableau,\n                             number_of_substeps);\n  return error;\n}\n\ntemplate <typename T>\nvoid RungeKutta::clean_history_impl(\n    const MutableUntypedHistory<T>& history) const {\n  if (history.at_step_start()) {\n    history.clear_substeps();\n    if (history.size() > 1) {\n      history.pop_front();\n    }\n  } else {\n    history.discard_value(history.substeps().back().time_step_id);\n  }\n  ASSERT(history.size() == 1, \"Have more than one step after cleanup.\");\n}\n\ntemplate <typename T>\nbool RungeKutta::dense_update_u_impl(const gsl::not_null<T*> u,\n                                     const ConstUntypedHistory<T>& history,\n                                     const double time) const {\n  const double step_start = history.front().time_step_id.step_time().value();\n  if (time == step_start) {\n    return true;\n  }\n  if (not history.at_step_start()) {\n    return false;\n  }\n  const double step_end = history.back().time_step_id.step_time().value();\n  const evolution_less<double> before{step_end > step_start};\n  if (history.size() == 1 or not before(time, step_end)) {\n    return false;\n  }\n  const double step_size = step_end - step_start;\n  const double output_fraction = (time - step_start) / step_size;\n  ASSERT(output_fraction >= 0.0, \"Attempting dense output at time \"\n                                     << time << \", but already progressed past \"\n                                     << step_start);\n\n  const auto& tableau = butcher_tableau();\n\n  const auto number_of_dense_coefficients = tableau.dense_coefficients.size();\n  const size_t number_of_substep_terms = std::min(\n      tableau.result_coefficients.size(), number_of_dense_coefficients);\n  for (size_t i = 0; i < number_of_substep_terms; ++i) {\n    const double coef =\n        evaluate_polynomial(tableau.dense_coefficients[i], output_fraction);\n    if (coef != 0.0) {\n      *u += coef * step_size *\n            (i == 0 ? history.front() : history.substeps()[i - 1]).derivative;\n    }\n  }\n\n  if (number_of_dense_coefficients > number_of_substep_terms) {\n    // We use the derivative at the end of the step.\n    const double coef =\n        evaluate_polynomial(tableau.dense_coefficients.back(), output_fraction);\n    if (coef != 0.0) {\n      *u += coef * step_size * history.back().derivative;\n    }\n  }\n\n  return true;\n}\n\ntemplate <typename T>\nbool RungeKutta::can_change_step_size_impl(\n    const TimeStepId& /*time_id*/,\n    const ConstUntypedHistory<T>& /*history*/) const {\n  return true;\n}\n\nTIME_STEPPER_DEFINE_OVERLOADS(RungeKutta)\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/RungeKutta.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <vector>\n\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nstruct StepperErrorTolerances;\nclass TimeDelta;\nnamespace TimeSteppers {\ntemplate <typename T>\nclass ConstUntypedHistory;\ntemplate <typename T>\nclass MutableUntypedHistory;\n}  // namespace TimeSteppers\n/// \\endcond\n\nnamespace TimeSteppers {\n/*!\n * \\ingroup TimeSteppersGroup\n * Intermediate base class implementing a generic Runge-Kutta scheme.\n *\n * Implements most of the virtual methods of TimeStepper for a generic\n * Runge-Kutta method.  From the TimeStepper interface, derived\n * classes need only implement `order` and `stable_step`, and should\n * not include the `TIME_STEPPER_*` macros.  All other methods are\n * implemented in terms of a Butcher tableau returned by the\n * `butcher_tableau` function.\n */\nclass RungeKutta : public virtual TimeStepper {\n public:\n  struct ButcherTableau {\n    /*!\n     * The times of the substeps, excluding the initial time step.\n     * Often called \\f$c\\f$ in the literature.\n     */\n    std::vector<double> substep_times;\n    /*!\n     * The coefficient matrix of the substeps.  Do not include the\n     * initial empty row or the coefficients for the full step.  Often\n     * called \\f$A\\f$ in the literature.\n     */\n    std::vector<std::vector<double>> substep_coefficients;\n    /*!\n     * The coefficients for the final result.  Often called \\f$b\\f$ in\n     * the literature.\n     *\n     * If the number of coefficients is smaller than the number of\n     * substeps defined in `substep_coefficients`, the extra substeps\n     * will only be performed when an error estimate is requested.\n     */\n    std::vector<double> result_coefficients;\n    /*!\n     * The coefficients for an error estimate.  Often called\n     * \\f$b^*\\f$ or \\f$\\hat{b}\\f$ in the literature.  This is assumed\n     * to be one order less accurate than the main method.\n     */\n    std::vector<double> error_coefficients;\n    /*!\n     * Coefficient polynomials for dense output.  Each entry is the\n     * coefficients of a polynomial that will be evaluated with the\n     * fraction of the way through the step at which output is\n     * desired:\n     *\n     * \\f{equation}{\n     *   y = y_0 + \\sum_i y'_i \\sum_j p_{ij} x^j \\qquad 0 \\le x \\le 1\n     * \\f}\n     *\n     * The derivative at the start of the next step is available as an\n     * additional substep after the final real substep.  Trailing zero\n     * polynomials can be omitted.\n     */\n    std::vector<std::vector<double>> dense_coefficients;\n  };\n\n  uint64_t number_of_substeps() const override;\n\n  uint64_t number_of_substeps_for_error() const override;\n\n  size_t number_of_past_steps() const override;\n\n  bool monotonic() const override;\n\n  TimeStepId next_time_id(const TimeStepId& current_id,\n                          const TimeDelta& time_step) const override;\n\n  TimeStepId next_time_id_for_error(const TimeStepId& current_id,\n                                    const TimeDelta& time_step) const override;\n\n  virtual const ButcherTableau& butcher_tableau() const = 0;\n\n private:\n  template <typename T>\n  void update_u_impl(gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n                     const TimeDelta& time_step) const;\n\n  template <typename T>\n  std::optional<StepperErrorEstimate> update_u_impl(\n      gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n      const TimeDelta& time_step,\n      const StepperErrorTolerances& tolerances) const;\n\n  template <typename T>\n  void clean_history_impl(const MutableUntypedHistory<T>& history) const;\n\n  template <typename T>\n  bool dense_update_u_impl(gsl::not_null<T*> u,\n                           const ConstUntypedHistory<T>& history,\n                           double time) const;\n\n  template <typename T>\n  bool can_change_step_size_impl(const TimeStepId& time_id,\n                                 const ConstUntypedHistory<T>& history) const;\n\n  TIME_STEPPER_DECLARE_OVERLOADS\n};\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/TimeStepper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n\nnamespace TimeSteppers {\nbool operator==(const VariableOrder& a, const VariableOrder& b) {\n  return a.minimum == b.minimum && a.maximum == b.maximum;\n}\n\nbool operator!=(const VariableOrder& a, const VariableOrder& b) {\n  return not(a == b);\n}\n}  // namespace TimeSteppers\n"
  },
  {
    "path": "src/Time/TimeSteppers/TimeStepper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\n/// \\cond\nstruct StepperErrorTolerances;\nclass TimeDelta;\nclass TimeStepId;\n/// \\endcond\n\n/// \\ingroup TimeSteppersGroup\n///\n/// Holds classes that take time steps.\nnamespace TimeSteppers {}\n\n/// \\cond\n#define TIME_STEPPER_WRAPPED_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define TIME_STEPPER_DERIVED_CLASS(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data) BOOST_PP_TUPLE_ELEM(2, data)\n/// \\endcond\n\nnamespace TimeSteppers {\n/// Minimum and maximum orders of a variable-order TimeStepper.\nstruct VariableOrder {\n  size_t minimum;\n  size_t maximum;\n\n  VariableOrder(const size_t minimum_in, const size_t maximum_in)\n      : minimum(minimum_in), maximum(maximum_in) {}\n};\n\nbool operator==(const VariableOrder& a, const VariableOrder& b);\nbool operator!=(const VariableOrder& a, const VariableOrder& b);\n\nnamespace Tags {\n/// Order of a fixed-order TimeStepper.\nstruct FixedOrder {\n  using type = size_t;\n};\n\n/// Minimum and maximum orders of a variable-order TimeStepper.\nstruct VariableOrder {\n  using type = TimeSteppers::VariableOrder;\n};\n}  // namespace Tags\n}  // namespace TimeSteppers\n\n/// \\ingroup TimeSteppersGroup\n///\n/// Abstract base class for TimeSteppers.\n///\n/// Several of the member functions of this class are templated and\n/// perform type erasure before forwarding their arguments to the\n/// derived classes.  This is implemented using the macros \\ref\n/// TIME_STEPPER_DECLARE_OVERLOADS, which must be placed in a private\n/// section of the class body, and\n/// TIME_STEPPER_DEFINE_OVERLOADS(derived_class), which must be placed\n/// in the cpp file.\nclass TimeStepper : public PUP::able {\n public:\n  static constexpr bool local_time_stepping = false;\n  static constexpr bool imex = false;\n  using provided_time_stepper_interfaces = tmpl::list<TimeStepper>;\n\n  WRAPPED_PUPable_abstract(TimeStepper);  // NOLINT\n\n  /// \\cond\n#define TIME_STEPPER_DECLARE_VIRTUALS_IMPL(_, data)                        \\\n  virtual void update_u_forward(                                           \\\n      const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,             \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(   \\\n          data)>& history,                                                 \\\n      const TimeDelta& time_step) const = 0;                               \\\n  virtual std::optional<StepperErrorEstimate> update_u_forward(            \\\n      const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,             \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(   \\\n          data)>& history,                                                 \\\n      const TimeDelta& time_step,                                          \\\n      const StepperErrorTolerances& tolerances) const = 0;                 \\\n  virtual void clean_history_forward(                                      \\\n      const TimeSteppers::MutableUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \\\n          data)>& history) const = 0;                                      \\\n  virtual bool dense_update_u_forward(                                     \\\n      const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,             \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(   \\\n          data)>& history,                                                 \\\n      double time) const = 0;                                              \\\n  virtual bool can_change_step_size_forward(                               \\\n      const TimeStepId& time_id,                                           \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(   \\\n          data)>& history) const = 0;\n\n  GENERATE_INSTANTIATIONS(TIME_STEPPER_DECLARE_VIRTUALS_IMPL,\n                          (MATH_WRAPPER_TYPES))\n#undef TIME_STEPPER_DECLARE_VIRTUALS_IMPL\n  /// \\endcond\n\n  /// Set \\p u to the value at the end of the current substep.\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// void update_u_impl(gsl::not_null<T*> u,\n  ///                    const ConstUntypedHistory<T>& history,\n  ///                    const TimeDelta& time_step) const;\n  /// ```\n  template <typename Vars>\n  void update_u(const gsl::not_null<Vars*> u,\n                const TimeSteppers::History<Vars>& history,\n                const TimeDelta& time_step) const {\n    return update_u_forward(&*make_math_wrapper(u), history.untyped(),\n                            time_step);\n  }\n\n  /// Set \\p u to the value at the end of the current substep; report the error\n  /// measure when available.\n  ///\n  /// For a substep method, the error measure will only be available on full\n  /// steps. For a multistep method, the error measure will only be available\n  /// when a sufficient number of steps are available in the `history` to\n  /// compare two orders of step. Whenever the error measure is unavailable,\n  /// the return value is empty\n  ///\n  /// If \\p tolerances requests no estimates (e.g., a\n  /// default-constructed object), no error measures are calculated,\n  /// but any additional substeps necessary for error estimation are\n  /// still taken.  This is useful when a system integrates multiple\n  /// variables in separate calls to the TimeStepper, but only uses\n  /// estimates from some of them.\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// std::optional<StepperErrorEstimate> update_u_impl(\n  ///     gsl::not_null<T*> u,\n  ///     const ConstUntypedHistory<T>& history,\n  ///     const TimeDelta& time_step,\n  ///     const StepperErrorTolerances& tolerances) const;\n  /// ```\n  template <typename Vars>\n  std::optional<StepperErrorEstimate> update_u(\n      const gsl::not_null<Vars*> u, const TimeSteppers::History<Vars>& history,\n      const TimeDelta& time_step,\n      const StepperErrorTolerances& tolerances) const {\n    return update_u_forward(&*make_math_wrapper(u), history.untyped(),\n                            time_step, tolerances);\n  }\n\n  /// Remove old entries from the history.\n  ///\n  /// This should be called after update_u and dense output.\n  /// Afterward, the history will generally require a new entry to be\n  /// added before it can be used by the TimeStepper.\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// void clean_history_impl(const MutableUntypedHistory<T>& history) const;\n  /// ```\n  template <typename Vars>\n  void clean_history(\n      const gsl::not_null<TimeSteppers::History<Vars>*> history) const {\n    return clean_history_forward(history->untyped());\n  }\n\n  /// Compute the solution value at a time between steps.  To evaluate\n  /// at a time within a given step, call this method at the start of\n  /// the step containing the time.  The function returns true on\n  /// success, otherwise the call should be retried after the next\n  /// substep.\n  ///\n  /// The change from the partial step will be added to the initial\n  /// value, so \\p u should generally be initialized to\n  /// `*history.step_start(time).value`.  (TimeStepper\n  /// implementations are required to keep this value in the history.)\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// bool dense_update_u_impl(\n  ///     gsl::not_null<T*> u, const ConstUntypedHistory<T>& history,\n  ///     double time) const;\n  /// ```\n  template <typename Vars>\n  bool dense_update_u(const gsl::not_null<Vars*> u,\n                      const TimeSteppers::History<Vars>& history,\n                      const double time) const {\n    return dense_update_u_forward(&*make_math_wrapper(u), history.untyped(),\n                                  time);\n  }\n\n  /// The convergence order of the stepper\n  virtual variants::TaggedVariant<TimeSteppers::Tags::FixedOrder,\n                                  TimeSteppers::Tags::VariableOrder>\n  order() const = 0;\n\n  /// Number of substeps in this TimeStepper\n  virtual uint64_t number_of_substeps() const = 0;\n\n  /// Number of substeps in this TimeStepper when providing an error measure for\n  /// adaptive time-stepping\n  ///\n  /// \\details Certain substep methods (e.g. embedded RK4(3)) require additional\n  /// steps when providing an error measure of the integration.\n  virtual uint64_t number_of_substeps_for_error() const = 0;\n\n  /// Number of past time steps needed for multi-step method\n  virtual size_t number_of_past_steps() const = 0;\n\n  /// Rough estimate of the maximum step size this method can take\n  /// stably as a multiple of the step for Euler's method.\n  virtual double stable_step() const = 0;\n\n  /// Whether computational and temporal orderings of operations\n  /// match.\n  ///\n  /// If this method returns true, then, for two time-stepper\n  /// operations occurring at different simulation times, the\n  /// temporally earlier operation will be performed first.  These\n  /// operations include RHS evaluation, dense output, and neighbor\n  /// communication.  In particular, dense output never requires\n  /// performing a RHS evaluation later than the output time, so\n  /// control systems measurements cannot cause deadlocks.\n  ///\n  /// \\warning This guarantee only holds if the time steps themselves\n  /// are monotonic, which can be violated during initialization.\n  virtual bool monotonic() const = 0;\n\n  /// The TimeStepId after the current substep\n  virtual TimeStepId next_time_id(const TimeStepId& current_id,\n                                  const TimeDelta& time_step) const = 0;\n\n  /// The TimeStepId after the current substep when providing an error measure\n  /// for adaptive time-stepping.\n  ///\n  /// Certain substep methods (e.g. embedded RK4(3)) require additional\n  /// steps when providing an error measure of the integration.\n  virtual TimeStepId next_time_id_for_error(\n      const TimeStepId& current_id, const TimeDelta& time_step) const = 0;\n\n  /// Whether a change in the step size is allowed before taking\n  /// a step.  Step sizes can never be changed on a substep.\n  ///\n  /// Derived classes must implement this as a function with signature\n  ///\n  /// ```\n  /// template <typename T>\n  /// bool can_change_step_size_impl(\n  ///     const TimeStepId& time_id,\n  ///     const ConstUntypedHistory<T>& history) const;\n  /// ```\n  template <typename Vars>\n  bool can_change_step_size(const TimeStepId& time_id,\n                            const TimeSteppers::History<Vars>& history) const {\n    return can_change_step_size_forward(time_id, history.untyped());\n  }\n};\n\n/// \\cond\n#define TIME_STEPPER_DECLARE_OVERLOADS_IMPL(_, data)                           \\\n  void update_u_forward(                                                       \\\n      gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,                       \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(       \\\n          data)>& history,                                                     \\\n      const TimeDelta& time_step) const override;                              \\\n  std::optional<StepperErrorEstimate> update_u_forward(                        \\\n      gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,                       \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(       \\\n          data)>& history,                                                     \\\n      const TimeDelta& time_step,                                              \\\n      const StepperErrorTolerances& tolerances) const override;                \\\n  void clean_history_forward(                                                  \\\n      const TimeSteppers::MutableUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(     \\\n          data)>& history) const override;                                     \\\n  bool dense_update_u_forward(                                                 \\\n      gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,                       \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(       \\\n          data)>& history,                                                     \\\n      double time) const override;                                             \\\n  bool can_change_step_size_forward(                                           \\\n      const TimeStepId& time_id,                                               \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(       \\\n          data)>& history) const override;\n\n#define TIME_STEPPER_DEFINE_OVERLOADS_IMPL(_, data)                        \\\n  TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data)                                \\\n  void TIME_STEPPER_DERIVED_CLASS(data)::update_u_forward(                 \\\n      const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,             \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(   \\\n          data)>& history,                                                 \\\n      const TimeDelta& time_step) const {                                  \\\n    update_u_impl(u, history, time_step);                                  \\\n  }                                                                        \\\n  TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data)                                \\\n  std::optional<StepperErrorEstimate>                                      \\\n  TIME_STEPPER_DERIVED_CLASS(data)::update_u_forward(                      \\\n      const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,             \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(   \\\n          data)>& history,                                                 \\\n      const TimeDelta& time_step,                                          \\\n      const StepperErrorTolerances& tolerances) const {                    \\\n    return update_u_impl(u, history, time_step, tolerances);               \\\n  }                                                                        \\\n  TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data)                                \\\n  void TIME_STEPPER_DERIVED_CLASS(data)::clean_history_forward(            \\\n      const TimeSteppers::MutableUntypedHistory<TIME_STEPPER_WRAPPED_TYPE( \\\n          data)>& history) const {                                         \\\n    return clean_history_impl(history);                                    \\\n  }                                                                        \\\n  TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data)                                \\\n  bool TIME_STEPPER_DERIVED_CLASS(data)::dense_update_u_forward(           \\\n      const gsl::not_null<TIME_STEPPER_WRAPPED_TYPE(data)*> u,             \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(   \\\n          data)>& history,                                                 \\\n      const double time) const {                                           \\\n    return dense_update_u_impl(u, history, time);                          \\\n  }                                                                        \\\n  TIME_STEPPER_DERIVED_CLASS_TEMPLATE(data)                                \\\n  bool TIME_STEPPER_DERIVED_CLASS(data)::can_change_step_size_forward(     \\\n      const TimeStepId& time_id,                                           \\\n      const TimeSteppers::ConstUntypedHistory<TIME_STEPPER_WRAPPED_TYPE(   \\\n          data)>& history) const {                                         \\\n    return can_change_step_size_impl(time_id, history);                    \\\n  }\n/// \\endcond\n\n/// \\ingroup TimeSteppersGroup\n/// Macro declaring overloaded detail methods in classes derived from\n/// TimeStepper.  Must be placed in a private section of the class\n/// body.\n#define TIME_STEPPER_DECLARE_OVERLOADS                         \\\n  GENERATE_INSTANTIATIONS(TIME_STEPPER_DECLARE_OVERLOADS_IMPL, \\\n                          (MATH_WRAPPER_TYPES))\n\n/// \\ingroup TimeSteppersGroup\n/// Macro defining overloaded detail methods in classes derived from\n/// TimeStepper.  Must be placed in the cpp file for the derived\n/// class.\n/// @{\n#define TIME_STEPPER_DEFINE_OVERLOADS(derived_class)          \\\n  GENERATE_INSTANTIATIONS(TIME_STEPPER_DEFINE_OVERLOADS_IMPL, \\\n                          (MATH_WRAPPER_TYPES), (derived_class), ())\n#define TIME_STEPPER_DEFINE_OVERLOADS_TEMPLATED(derived_class, template_args) \\\n  GENERATE_INSTANTIATIONS(TIME_STEPPER_DEFINE_OVERLOADS_IMPL,                 \\\n                          (MATH_WRAPPER_TYPES), (derived_class),              \\\n                          (template <template_args>))\n/// @}\n"
  },
  {
    "path": "src/Time/Triggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  NearTimes.cpp\n  OnSubsteps.cpp\n  SlabCompares.cpp\n  Slabs.cpp\n  StepsWithinSlab.cpp\n  TimeCompares.cpp\n  Times.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  NearTimes.hpp\n  OnSubsteps.hpp\n  SlabCompares.hpp\n  Slabs.hpp\n  StepsWithinSlab.hpp\n  TimeCompares.hpp\n  TimeTriggers.hpp\n  Times.hpp\n  )\n"
  },
  {
    "path": "src/Time/Triggers/NearTimes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Triggers/NearTimes.hpp\"\n\n#include \"Options/ParseError.hpp\"\n\nnamespace Triggers {\nbool NearTimes::operator()(const double now, const TimeDelta& time_step) const {\n  const bool time_runs_forward = time_step.is_positive();\n\n  double range_code_units = range_;\n  if (unit_ == Unit::Slab) {\n    range_code_units *= time_step.slab().duration().value();\n  } else if (unit_ == Unit::Step) {\n    range_code_units *= std::abs(time_step.value());\n  }\n\n  if (not time_runs_forward) {\n    range_code_units = -range_code_units;\n  }\n\n  // Interval around now to look for trigger times in.\n  auto trigger_range = std::make_pair(\n      direction_ == Direction::Before ? now : now - range_code_units,\n      direction_ == Direction::After ? now : now + range_code_units);\n\n  if (not time_runs_forward) {\n    std::swap(trigger_range.first, trigger_range.second);\n  }\n\n  const auto nearby_times = times_->times_near(trigger_range.first);\n  for (const auto& time : nearby_times) {\n    if (time and *time >= trigger_range.first and\n        *time <= trigger_range.second) {\n      return true;\n    }\n  }\n  return false;\n}\n\nvoid NearTimes::pup(PUP::er& p) {\n  p | times_;\n  p | range_;\n  p | unit_;\n  p | direction_;\n}\n\nPUP::able::PUP_ID NearTimes::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/NearTimes.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cmath>\n#include <limits>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <utility>\n\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\nstruct TimeStep;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Triggers {\nnamespace NearTimes_enums {\nenum class Unit { Time, Slab, Step };\nenum class Direction { Before, After, Both };\n}  // namespace NearTimes_enums\n\n/// \\ingroup EventsAndTriggersGroup\n/// \\ingroup TimeGroup\n/// Trigger in intervals surrounding particular times.\n///\n/// When using adaptive time stepping, intervals specified in terms of\n/// slabs or steps are approximate.\n///\n/// \\see Times\nclass NearTimes : public Trigger {\n public:\n  /// \\cond\n  NearTimes() = default;\n  explicit NearTimes(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(NearTimes);  // NOLINT\n  /// \\endcond\n\n  using Unit = NearTimes_enums::Unit;\n  using Direction = NearTimes_enums::Direction;\n\n  struct OptionTags {\n    struct Times {\n      using type = std::unique_ptr<TimeSequence<double>>;\n      static constexpr Options::String help = \"Times to trigger at\";\n    };\n\n    struct Range {\n      using type = double;\n      static type lower_bound() { return 0.0; }\n      static constexpr Options::String help =\n          \"Maximum time difference to trigger at\";\n    };\n\n    struct Unit {\n      using type = NearTimes::Unit;\n      static constexpr Options::String help =\n          \"Interpret Range as 'Time', 'Step's, or 'Slab's\";\n    };\n\n    struct Direction {\n      using type = NearTimes::Direction;\n      static constexpr Options::String help =\n          \"Trigger 'Before', 'After', or 'Both' from the times\";\n    };\n  };\n\n  static constexpr Options::String help =\n      \"Trigger in intervals surrounding particular times.\";\n  using options =\n      tmpl::list<typename OptionTags::Times, typename OptionTags::Range,\n                 typename OptionTags::Unit, typename OptionTags::Direction>;\n\n  NearTimes(std::unique_ptr<TimeSequence<double>> times, const double range,\n            const Unit unit, const Direction direction)\n      : times_(std::move(times)),\n        range_(range),\n        unit_(unit),\n        direction_(direction) {}\n\n  using argument_tags = tmpl::list<Tags::Time, Tags::TimeStep>;\n\n  bool operator()(const double now, const TimeDelta& time_step) const;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override;\n\n private:\n  std::unique_ptr<TimeSequence<double>> times_{};\n  double range_ = std::numeric_limits<double>::signaling_NaN();\n  Unit unit_{};\n  Direction direction_{};\n};\n}  // namespace Triggers\n\ntemplate <>\nstruct Options::create_from_yaml<Triggers::NearTimes_enums::Unit> {\n  using type = Triggers::NearTimes_enums::Unit;\n  template <typename Metavariables>\n  static type create(const Options::Option& options) {\n    const auto unit = options.parse_as<std::string>();\n    if (unit == \"Time\") {\n      return type::Time;\n    } else if (unit == \"Step\") {\n      return type::Step;\n    } else if (unit == \"Slab\") {\n      return type::Slab;\n    } else {\n      PARSE_ERROR(options.context(), \"Unit must be 'Time', 'Step', or 'Slab'\");\n    }\n  }\n};\n\ntemplate <>\nstruct Options::create_from_yaml<\n    typename Triggers::NearTimes_enums::Direction> {\n  using type = Triggers::NearTimes_enums::Direction;\n  template <typename Metavariables>\n  static type create(const Options::Option& options) {\n    const auto unit = options.parse_as<std::string>();\n    if (unit == \"Before\") {\n      return type::Before;\n    } else if (unit == \"After\") {\n      return type::After;\n    } else if (unit == \"Both\") {\n      return type::Both;\n    } else {\n      PARSE_ERROR(options.context(),\n                  \"Direction must be 'Before', 'After', or 'Both'\");\n    }\n  }\n};\n"
  },
  {
    "path": "src/Time/Triggers/OnSubsteps.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Triggers/OnSubsteps.hpp\"\n\nnamespace Triggers {\nPUP::able::PUP_ID OnSubsteps::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/OnSubsteps.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\nnamespace Triggers {\n/*!\n * \\ingroup EventsAndTriggersGroup\n * \\ingroup TimeGroup\n * Check a trigger on substeps, as well as full steps.  Primarily for\n * debugging.\n *\n * In LTS mode, only substeps of the first step in each slab will be\n * checked. Such substeps may not be aligned across the domain.\n *\n * The observation value on a substep is set to the start time of the\n * step plus $10^6$ times the substep number.\n *\n * This trigger can be used as:\n *\n * ```yaml\n * - Trigger:\n *     OnSubsteps:\n *       Always:\n * ```\n */\nclass OnSubsteps : public Trigger {\n public:\n  /// \\cond\n  OnSubsteps() = default;\n  explicit OnSubsteps(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(OnSubsteps);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help =\n      \"Check a trigger on substeps in addition to steps.\";\n\n  explicit OnSubsteps(std::unique_ptr<Trigger> trigger)\n      : trigger_(std::move(trigger)) {}\n\n  using argument_tags = tmpl::list<Tags::DataBox>;\n\n  template <typename DbTags>\n  bool operator()(const db::DataBox<DbTags>& box) const {\n    // This trigger doesn't actually do anything.  All the special\n    // logic is in the RunEventsAndTriggers action.  Just forward\n    // along.\n    return trigger_->is_triggered(box);\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override { p | trigger_; }\n\n private:\n  std::unique_ptr<Trigger> trigger_;\n};\n}  // namespace Triggers\n\ntemplate <>\nstruct Options::create_from_yaml<Triggers::OnSubsteps> {\n  template <typename Metavariables>\n  static Triggers::OnSubsteps create(const Option& options) {\n    return Triggers::OnSubsteps(\n        options.parse_as<std::unique_ptr<Trigger>, Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/Time/Triggers/SlabCompares.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Triggers/SlabCompares.hpp\"\n\nnamespace Triggers {\nPUP::able::PUP_ID SlabCompares::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/SlabCompares.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <pup.h>\n\n#include \"Options/Comparator.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// \\ingroup TimeGroup\n/// Trigger based on a comparison with the slab number.\nclass SlabCompares : public Trigger {\n public:\n  /// \\cond\n  SlabCompares() = default;\n  explicit SlabCompares(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SlabCompares);  // NOLINT\n  /// \\endcond\n\n  struct Comparison {\n    using type = Options::Comparator;\n    constexpr static Options::String help = \"Comparison type\";\n  };\n\n  struct Value {\n    using type = uint64_t;\n    constexpr static Options::String help = \"Value to compare to\";\n  };\n\n  using options = tmpl::list<Comparison, Value>;\n  static constexpr Options::String help{\n      \"Trigger based on a comparison with the slab number.\"};\n\n  explicit SlabCompares(const Options::Comparator comparator,\n                        const uint64_t slab_number)\n      : comparator_(comparator), slab_number_(slab_number) {}\n\n  using argument_tags = tmpl::list<Tags::TimeStepId>;\n\n  bool operator()(const TimeStepId& time_step_id) const {\n    return comparator_(static_cast<uint64_t>(time_step_id.slab_number()),\n                       slab_number_);\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | comparator_;\n    p | slab_number_;\n  }\n\n private:\n  Options::Comparator comparator_{};\n  uint64_t slab_number_{};\n};\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/Slabs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Triggers/Slabs.hpp\"\n\nnamespace Triggers {\nPUP::able::PUP_ID Slabs::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/Slabs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// \\ingroup TimeGroup\n/// Trigger at specified numbers of slabs after the simulation start.\nclass Slabs : public Trigger {\n public:\n  /// \\cond\n  Slabs() = default;\n  explicit Slabs(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Slabs);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help{\n    \"Trigger at specified numbers of slabs after the simulation start.\"};\n\n  explicit Slabs(std::unique_ptr<TimeSequence<uint64_t>> slabs)\n      : slabs_(std::move(slabs)) {}\n\n  using argument_tags = tmpl::list<Tags::TimeStepId>;\n\n  bool operator()(const TimeStepId& time_id) const {\n    const auto unsigned_slab =\n        static_cast<std::uint64_t>(time_id.slab_number());\n    return slabs_->times_near(unsigned_slab)[1] == unsigned_slab;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override { p | slabs_; }\n\n private:\n  std::unique_ptr<TimeSequence<uint64_t>> slabs_{};\n};\n}  // namespace Triggers\n\ntemplate <>\nstruct Options::create_from_yaml<Triggers::Slabs> {\n  template <typename Metavariables>\n  static Triggers::Slabs create(const Option& options) {\n    return Triggers::Slabs(\n        options.parse_as<std::unique_ptr<TimeSequence<uint64_t>>,\n                         Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/Time/Triggers/StepsWithinSlab.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Triggers/StepsWithinSlab.hpp\"\n\nnamespace Triggers {\nPUP::able::PUP_ID StepsWithinSlab::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/StepsWithinSlab.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct StepNumberWithinSlab;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// \\ingroup TimeGroup\n/// Trigger at specified steps within each slab.\nclass StepsWithinSlab : public Trigger {\n public:\n  /// \\cond\n  StepsWithinSlab() = default;\n  explicit StepsWithinSlab(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(StepsWithinSlab);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help{\n      \"Trigger at specified steps within each slab.\"};\n\n  explicit StepsWithinSlab(\n      std::unique_ptr<TimeSequence<uint64_t>> steps_within_slab)\n      : steps_within_slab_(std::move(steps_within_slab)) {}\n\n  using argument_tags = tmpl::list<Tags::StepNumberWithinSlab>;\n\n  bool operator()(const uint64_t step_within_slab) const {\n    return steps_within_slab_->times_near(step_within_slab)[1] ==\n           step_within_slab;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override { p | steps_within_slab_; }\n\n private:\n  std::unique_ptr<TimeSequence<uint64_t>> steps_within_slab_{};\n};\n}  // namespace Triggers\n\ntemplate <>\nstruct Options::create_from_yaml<Triggers::StepsWithinSlab> {\n  template <typename Metavariables>\n  static Triggers::StepsWithinSlab create(const Option& options) {\n    return Triggers::StepsWithinSlab(\n        options.parse_as<std::unique_ptr<TimeSequence<uint64_t>>,\n                         Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/Time/Triggers/TimeCompares.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Triggers/TimeCompares.hpp\"\n\nnamespace Triggers {\nPUP::able::PUP_ID TimeCompares::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/TimeCompares.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <pup.h>\n\n#include \"Options/Comparator.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// \\ingroup TimeGroup\n/// Trigger based on a comparison with the time.\nclass TimeCompares : public Trigger {\n public:\n  /// \\cond\n  TimeCompares() = default;\n  explicit TimeCompares(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TimeCompares);  // NOLINT\n  /// \\endcond\n\n  struct Comparison {\n    using type = Options::Comparator;\n    constexpr static Options::String help = \"Comparison type\";\n  };\n\n  struct Value {\n    using type = double;\n    constexpr static Options::String help = \"Value to compare to\";\n  };\n\n  using options = tmpl::list<Comparison, Value>;\n  static constexpr Options::String help{\n      \"Trigger based on a comparison with the time.\"};\n\n  explicit TimeCompares(const Options::Comparator comparator, const double time)\n      : comparator_(comparator), time_(time) {}\n\n  using argument_tags = tmpl::list<Tags::Time>;\n\n  bool operator()(const double& time) const { return comparator_(time, time_); }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | comparator_;\n    p | time_;\n  }\n\n private:\n  Options::Comparator comparator_{};\n  double time_{};\n};\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/TimeTriggers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Time/Triggers/NearTimes.hpp\"\n#include \"Time/Triggers/OnSubsteps.hpp\"\n#include \"Time/Triggers/SlabCompares.hpp\"\n#include \"Time/Triggers/Slabs.hpp\"\n#include \"Time/Triggers/TimeCompares.hpp\"\n#include \"Time/Triggers/Times.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// Typelist of Time triggers\nusing time_triggers =\n    tmpl::list<NearTimes, OnSubsteps, SlabCompares, Slabs, TimeCompares, Times>;\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/Times.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/Triggers/Times.hpp\"\n\nnamespace Triggers {\nPUP::able::PUP_ID Times::my_PUP_ID = 0;  // NOLINT\n}  // namespace Triggers\n"
  },
  {
    "path": "src/Time/Triggers/Times.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace Triggers {\n/// \\ingroup EventsAndTriggersGroup\n/// \\ingroup TimeGroup\n/// Trigger at particular times.\n///\n/// \\warning This trigger will only fire if it is actually checked at\n/// the times specified.  The StepToTimes StepChooser can be useful\n/// for this.\nclass Times : public Trigger {\n public:\n  /// \\cond\n  Times() = default;\n  explicit Times(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Times);  // NOLINT\n  /// \\endcond\n\n  static constexpr Options::String help{\"Trigger at particular times.\"};\n\n  explicit Times(std::unique_ptr<TimeSequence<double>> times)\n      : times_(std::move(times)) {}\n\n  using argument_tags = tmpl::list<Tags::Time>;\n\n  bool operator()(const double now) const {\n    return times_->times_near(now)[1] == std::optional(now);\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override { p | times_; }\n\n private:\n  std::unique_ptr<TimeSequence<double>> times_;\n};\n}  // namespace Triggers\n\ntemplate <>\nstruct Options::create_from_yaml<Triggers::Times> {\n  template <typename Metavariables>\n  static Triggers::Times create(const Option& options) {\n    return Triggers::Times(\n        options\n            .parse_as<std::unique_ptr<TimeSequence<double>>, Metavariables>());\n  }\n};\n"
  },
  {
    "path": "src/Time/UpdateU.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <optional>\n#include <type_traits>\n\n#include \"Time/Tags/StepperErrorTolerancesCompute.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nstruct StepperErrorEstimate;\nstruct StepperErrorTolerances;\nclass TimeDelta;\nclass TimeStepId;\nclass TimeStepper;\nnamespace Tags {\ntemplate <typename Tag>\nstruct HistoryEvolvedVariables;\ntemplate <typename Tag>\nstruct Next;\nstruct StepperErrorEstimatesEnabled;\ntemplate <typename Tag>\nstruct StepperErrorTolerances;\nstruct TimeStep;\nstruct TimeStepId;\ntemplate <typename StepperInterface>\nstruct TimeStepper;\n}  // namespace Tags\nnamespace TimeSteppers {\ntemplate <typename Vars>\nclass History;\n}  // namespace TimeSteppers\nnamespace gsl {\ntemplate <class T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// \\ingroup TimeGroup\n/// \\brief Perform variable updates for one substep\n/// @{\ntemplate <typename System, bool LocalTimeStepping,\n          template <typename> typename CacheTagPrefix = std::type_identity_t,\n          typename = tmpl::conditional_t<\n              tt::is_a_v<tmpl::list, typename System::variables_tag>,\n              typename System::variables_tag,\n              tmpl::list<typename System::variables_tag>>>\nstruct UpdateU;\n\ntemplate <typename System, bool LocalTimeStepping,\n          template <typename> typename CacheTagPrefix,\n          typename... VariablesTags>\nstruct UpdateU<System, LocalTimeStepping, CacheTagPrefix,\n               tmpl::list<VariablesTags...>> {\n  using simple_tags = tmpl::list<::Tags::StepperErrors<VariablesTags>...>;\n  using compute_tags =\n      tmpl::list<Tags::StepperErrorEstimatesEnabledCompute<LocalTimeStepping,\n                                                           CacheTagPrefix>,\n                 Tags::StepperErrorTolerancesCompute<\n                     VariablesTags, LocalTimeStepping, CacheTagPrefix>...>;\n\n  using return_tags =\n      tmpl::list<VariablesTags..., Tags::StepperErrors<VariablesTags>...>;\n  using argument_tags =\n      tmpl::list<CacheTagPrefix<Tags::TimeStepper<TimeStepper>>,\n                 Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n                 Tags::StepperErrorEstimatesEnabled,\n                 Tags::HistoryEvolvedVariables<VariablesTags>...,\n                 Tags::StepperErrorTolerances<VariablesTags>...>;\n\n  static void apply(\n      const gsl::not_null<typename VariablesTags::type*>... vars,\n      const typename tmpl::has_type<\n          VariablesTags,\n          gsl::not_null<std::array<std::optional<StepperErrorEstimate>, 2>*>>::\n          type... errors,\n      const TimeStepper& time_stepper, const TimeStepId& time_step_id,\n      const TimeStepId& next_time_step_id, const TimeDelta& time_step,\n      bool error_estimates_enabled,\n      const TimeSteppers::History<typename VariablesTags::type>&... histories,\n      const typename tmpl::has_type<\n          VariablesTags, StepperErrorTolerances>::type&... tolerances);\n};\n/// @}\n"
  },
  {
    "path": "src/Time/UpdateU.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Time/UpdateU.hpp\"\n\n#include <array>\n#include <optional>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/SelfStart.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <typename System, bool LocalTimeStepping,\n          template <typename> typename CacheTagPrefix,\n          typename... VariablesTags>\nvoid UpdateU<System, LocalTimeStepping, CacheTagPrefix,\n             tmpl::list<VariablesTags...>>::\n    apply(\n        const gsl::not_null<typename VariablesTags::type*>... vars,\n        const typename tmpl::has_type<\n            VariablesTags,\n            gsl::not_null<std::array<std::optional<StepperErrorEstimate>,\n                                     2>*>>::type... errors,\n        const TimeStepper& time_stepper, const TimeStepId& time_step_id,\n        const TimeStepId& next_time_step_id, const TimeDelta& time_step,\n        const bool error_estimates_enabled,\n        const TimeSteppers::History<typename VariablesTags::type>&... histories,\n        const typename tmpl::has_type<\n            VariablesTags, StepperErrorTolerances>::type&... tolerances) {\n  if (::SelfStart::step_unused(time_step_id, next_time_step_id)) {\n    return;\n  }\n\n  if (error_estimates_enabled) {\n    const auto update_one_variables =\n        [&time_stepper, &time_step]<typename Vars>(\n            const gsl::not_null<Vars*> var,\n            const gsl::not_null<\n                std::array<std::optional<StepperErrorEstimate>, 2>*>\n                error,\n            const TimeSteppers::History<Vars>& history,\n            StepperErrorTolerances tolerance) {\n          // If we are doing variable-order h-refinement we need\n          // low-order error estimates to restart elements.\n          // Figuring out whether we are actually doing that is\n          // hard, but since it can only happen at LTS slab\n          // boundaries it's not a big deal if we do a little extra\n          // work.  As of this writing, we do not use variable-order\n          // steppers in GTS.\n          if (tolerance.estimates != StepperErrorTolerances::Estimates::None and\n              variants::holds_alternative<TimeSteppers::Tags::VariableOrder>(\n                  time_stepper.order()) and\n              (history.back().time_step_id.step_time() + time_step)\n                  .is_at_slab_boundary()) {\n            tolerance.estimates = StepperErrorTolerances::Estimates::AllOrders;\n          }\n          const auto new_error =\n              time_stepper.update_u(var, history, time_step, tolerance);\n          if (new_error.has_value()) {\n            // Save the previous error if there was one, but not if this is a\n            // retry of the same step.\n            if ((*error)[1].has_value() and\n                (*error)[1]->step_time != new_error->step_time) {\n              (*error)[0] = (*error)[1];\n            }\n            (*error)[1].emplace(*new_error);\n          }\n          return 0;\n        };\n    expand_pack(update_one_variables(vars, errors, histories, tolerances)...);\n  } else {\n    const auto update_one_variables =\n        [&time_stepper, &time_step]<typename Vars>(\n            const gsl::not_null<Vars*> var,\n            const TimeSteppers::History<Vars>& history) {\n          time_stepper.update_u(var, history, time_step);\n          return 0;\n        };\n    expand_pack(update_one_variables(vars, histories)...);\n  }\n}\n"
  },
  {
    "path": "src/Time/VariableOrderAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Time/VariableOrderAlgorithm.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <pup.h>\n\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nVariableOrderAlgorithm::VariableOrderAlgorithm() = default;\n\nVariableOrderAlgorithm::VariableOrderAlgorithm(const size_t goal_order)\n    : goal_order_(goal_order) {}\n\nVariableOrderAlgorithm::VariableOrderAlgorithm(const double order_falloff)\n    : order_falloff_(order_falloff) {}\n\nStepperErrorTolerances::Estimates VariableOrderAlgorithm::required_estimates()\n    const {\n  return order_falloff_.has_value()\n             ? StepperErrorTolerances::Estimates::AllOrders\n             : StepperErrorTolerances::Estimates::None;\n}\n\nvoid VariableOrderAlgorithm::pup(PUP::er& p) {\n  p | goal_order_;\n  p | order_falloff_;\n}\n\nsize_t VariableOrderAlgorithm::choose_order_goal(\n    const size_t current_order) const {\n  if (current_order == goal_order_.value()) {\n    return current_order;\n  } else if (current_order < goal_order_.value()) {\n    return current_order + 1;\n  } else {\n    return current_order - 1;\n  }\n}\n\nnamespace {\ntemplate <size_t NVars>\ndouble largest_error(\n    const std::array<const std::optional<StepperErrorEstimate>*, NVars>& errors,\n    const size_t order) {\n  double order_error = -std::numeric_limits<double>::infinity();\n  for (const auto* error : errors) {\n    if (error->has_value()) {\n      order_error = std::max(order_error,\n                             gsl::at(error->value().errors, order - 1).value());\n    }\n  }\n\n  if (order_error == -std::numeric_limits<double>::infinity()) {\n    ERROR_NO_TRACE(\n        \"OrderFalloff only implemented with error-based adaptive time \"\n        \"stepping.\");\n  }\n\n  return order_error;\n}\n}  // namespace\n\ntemplate <size_t NVars>\nsize_t VariableOrderAlgorithm::choose_order_falloff(\n    const size_t current_order,\n    const std::array<const std::optional<StepperErrorEstimate>*, NVars>& errors)\n    const {\n  double prev_error = largest_error(errors, 1);\n  double best_convergence = 1.0;\n  for (size_t order = 2; order < current_order; ++order) {\n    const double error = largest_error(errors, order);\n    const double convergence = error / prev_error;\n    if (convergence > std::pow(best_convergence, order_falloff_.value())) {\n      return current_order - 1;\n    }\n    prev_error = error;\n    best_convergence = std::min(best_convergence, convergence);\n  }\n\n  const double current_error = largest_error(errors, current_order);\n\n  const double current_convergence = current_error / prev_error;\n  if (current_convergence >\n      std::pow(best_convergence, order_falloff_.value())) {\n    return current_order;\n  }\n\n  return current_order + 1;\n}\n\nbool operator==(const VariableOrderAlgorithm& a,\n                const VariableOrderAlgorithm& b) {\n  return a.goal_order_ == b.goal_order_ and\n         a.order_falloff_ == b.order_falloff_;\n}\n\n#define NVARS(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                       \\\n  template size_t VariableOrderAlgorithm::choose_order_falloff(    \\\n      size_t current_order,                                        \\\n      const std::array<const std::optional<StepperErrorEstimate>*, \\\n                       NVARS(data)>& errors) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2))\n\n#undef INSTATNTIATE\n#undef NVARS\n"
  },
  {
    "path": "src/Time/VariableOrderAlgorithm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Tests for the VariableOrderAlgorithm class are performed in\n// Test_ChangeTimeStepperOrder.cpp.  The class is split into this file\n// to avoid include loops with the associated tags.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"Options/String.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Options {\ntemplate <typename... AlternativeLists>\nstruct Alternatives;\n}  // namespace Options\n/// \\endcond\n\n/*!\n * \\ingroup TimeGroup\n * \\brief Class encapsulating the time-stepper order changing algorithms\n *\n * \\details Supports two modes: driving the order to a specified constant, and\n * measuring the convergence of the error estimates.\n *\n * When driving to a constant, the order is changed by one towards the\n * goal if it is not at the goal.\n *\n * When measuring convergence, let the relative error estimate from\n * the time stepper for order-$k$ integration be $e_k$, and $\\lambda\n * \\le 1$ be the falloff this class was constructed with.  Then, we\n * decrease the order by one if, for any $k \\ge 2$ and less than the\n * current integration order,\n *\n * \\f{equation}\n *   \\frac{e_k}{e_{k-1}} >\n *   \\left(\\min_{2 \\le j < k} \\frac{e_j}{e_{j-1}}\\right)^\\lambda,\n * \\f}\n *\n * with the right-hand side taken to be $1$ for $k = 2$.  If the order\n * is not decreased, it is kept the same if the above condition holds\n * for $k$ equal to the current integration order, and is increased by\n * one otherwise.  If the current integration order is $1$ (not\n * possible for predictor-corrector methods), it is always increased.\n * This algorithm will almost never prefer an integration order less\n * than three.\n *\n * \\note This is currently all implemented in one class for simplicity\n * with dealing with templates.  If we add more algorithms splitting\n * into a base class with implementations would be appropriate.\n */\nclass VariableOrderAlgorithm {\n public:\n  struct GoalOrder {\n    using type = size_t;\n    static constexpr Options::String help = \"Order to drive the integrator to.\";\n  };\n\n  struct OrderFalloff {\n    using type = double;\n    static constexpr Options::String help =\n        \"Threshold for changing time-stepper order, as a logarithmic \"\n        \"fraction of the best order-to-order improvement.\";\n  };\n\n  using options = tmpl::list<\n      Options::Alternatives<tmpl::list<GoalOrder>, tmpl::list<OrderFalloff>>>;\n  static constexpr Options::String help =\n      \"Algorithm for choosing the time-stepper order in a variable-order \"\n      \"evolution.\";\n\n  VariableOrderAlgorithm();\n  explicit VariableOrderAlgorithm(size_t goal_order);\n  explicit VariableOrderAlgorithm(double order_falloff);\n\n  StepperErrorTolerances::Estimates required_estimates() const;\n\n  template <typename... VariablesTags>\n  size_t choose_order(\n      const TimeSteppers::History<typename VariablesTags::type>&... histories,\n      const typename tmpl::has_type<\n          VariablesTags,\n          std::array<std::optional<StepperErrorEstimate>, 2>>::type&... errors)\n      const {\n    const auto history_order =\n        get_first_argument(histories...).integration_order();\n    ASSERT((... and (history_order == histories.integration_order())),\n           \"Multiple histories with different integration orders.\");\n\n    if (goal_order_.has_value()) {\n      ASSERT(not order_falloff_.has_value(),\n             \"Internal error: should not have multiple algorithms active\");\n      return choose_order_goal(history_order);\n    } else {\n      ASSERT(order_falloff_.has_value(),\n             \"VariableOrderAlgorithm not initialized\");\n      return choose_order_falloff(history_order, std::array{&errors[1]...});\n    }\n  }\n\n  void pup(PUP::er& p);\n\n private:\n  size_t choose_order_goal(size_t current_order) const;\n\n  template <size_t NVars>\n  size_t choose_order_falloff(\n      size_t current_order,\n      const std::array<const std::optional<StepperErrorEstimate>*, NVars>&\n          errors) const;\n\n  friend bool operator==(const VariableOrderAlgorithm& a,\n                         const VariableOrderAlgorithm& b);\n\n  std::optional<size_t> goal_order_{};\n  std::optional<double> order_falloff_{};\n};\n"
  },
  {
    "path": "src/Utilities/Algorithm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n#pragma once\n\n#include <algorithm>\n#include <functional>\n#include <iterator>\n\n#include \"Utilities/Gsl.hpp\"\n\nnamespace cpp20 {\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::swap that is constexpr;\n * taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/type_traits\n */\ntemplate <class T>\nconstexpr void swap(T& a, T& b) {\n  T c(std::move(a));\n  a = std::move(b);\n  b = std::move(c);\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::iter_swap that is constexpr;\n * taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/type_traits\n */\ntemplate <class ForwardIt1, class ForwardIt2>\nconstexpr void iter_swap(ForwardIt1 a, ForwardIt2 b) {\n  swap(*a, *b);\n}\n\nnamespace detail {\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::reverse that is constexpr, for bidirectional\n * iterators; taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/algorithm\n */\ntemplate <class BidirIt>\nconstexpr void reverse(BidirIt first, BidirIt last,\n                       std::bidirectional_iterator_tag /* unused */) {\n  while (first != last) {\n    if (first == --last) {\n      break;\n    }\n    cpp20::iter_swap(first, last);\n    ++first;\n  }\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::reverse that is constexpr, for random access\n * iterators; taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/algorithm\n */\ntemplate <class RandomAccessIterator>\nconstexpr void reverse(RandomAccessIterator first, RandomAccessIterator last,\n                       std::random_access_iterator_tag /* unused */) {\n  if (first != last) {\n    for (; first < --last; ++first) {\n      cpp20::iter_swap(first, last);\n    }\n  }\n}\n}  // namespace detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::reverse that is constexpr;\n * taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/algorithm\n */\ntemplate <class BidirectionalIterator>\nconstexpr void reverse(BidirectionalIterator first,\n                       BidirectionalIterator last) {\n  cpp20::detail::reverse(first, last,\n                         typename std::iterator_traits<\n                             BidirectionalIterator>::iterator_category());\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::next_permutation that is constexpr,\n * for a generic comparator; taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/algorithm\n */\ntemplate <class Compare, class BidirectionalIterator>\nconstexpr bool next_permutation(BidirectionalIterator first,\n                                BidirectionalIterator last, Compare comp) {\n  BidirectionalIterator i = last;\n  if (first == last || first == --i) {\n    return false;\n  }\n  while (true) {\n    BidirectionalIterator ip1 = i;\n    if (comp(*--i, *ip1)) {\n      BidirectionalIterator j = last;\n      while (!comp(*i, *--j)) {\n      }\n      cpp20::swap(*i, *j);\n      cpp20::reverse(ip1, last);\n      return true;\n    }\n    if (i == first) {\n      cpp20::reverse(first, last);\n      return false;\n    }\n  }\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::next_permutation that is constexpr,\n * with less as the comparator; taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/algorithm\n */\ntemplate <class BidirectionalIterator>\nconstexpr bool next_permutation(BidirectionalIterator first,\n                                BidirectionalIterator last) {\n  return cpp20::next_permutation(\n      first, last,\n      std::less<\n          typename std::iterator_traits<BidirectionalIterator>::value_type>());\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::find that is constexpr\n */\ntemplate <class InputIt, class T>\nconstexpr InputIt find(InputIt first, InputIt last, const T& value) {\n  for (; first != last; ++first) {\n    if (*first == value) {\n      return first;\n    }\n  }\n  return last;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::find_if that is constexpr\n */\ntemplate <class InputIt, class UnaryPredicate>\nconstexpr InputIt find_if(InputIt first, InputIt last, UnaryPredicate p) {\n  for (; first != last; ++first) {\n    if (p(*first)) {\n      return first;\n    }\n  }\n  return last;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::find_if_not that is constexpr\n */\ntemplate <class InputIt, class UnaryPredicate>\nconstexpr InputIt find_if_not(InputIt first, InputIt last, UnaryPredicate q) {\n  for (; first != last; ++first) {\n    if (!q(*first)) {\n      return first;\n    }\n  }\n  return last;\n}\n}  // namespace cpp20\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Utility functions wrapping STL algorithms and additional algorithms.\n */\nnamespace alg {\n/// Convenience wrapper around std::all_of\ntemplate <class Container, class UnaryPredicate>\ndecltype(auto) all_of(const Container& c, UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return std::all_of(begin(c), end(c),\n                     std::forward<UnaryPredicate>(unary_predicate));\n}\n\n/// Convenience wrapper around std::any_of\ntemplate <class Container, class UnaryPredicate>\ndecltype(auto) any_of(const Container& c, UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return std::any_of(begin(c), end(c),\n                     std::forward<UnaryPredicate>(unary_predicate));\n}\n\n/// Convenience wrapper around std::none_of\ntemplate <class Container, class UnaryPredicate>\ndecltype(auto) none_of(const Container& c, UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return std::none_of(begin(c), end(c),\n                      std::forward<UnaryPredicate>(unary_predicate));\n}\n\n/// Convenience wrapper around std::count\ntemplate <class Container, class T>\ndecltype(auto) count(const Container& c, const T& value) {\n  using std::begin;\n  using std::end;\n  return std::count(begin(c), end(c), value);\n}\n\n/// Convenience wrapper around std::count_if\ntemplate <class Container, class UnaryPredicate>\ndecltype(auto) count_if(const Container& c, UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return std::count_if(begin(c), end(c),\n                       std::forward<UnaryPredicate>(unary_predicate));\n}\n\n/// Convenience wrapper around constexpr reimplementation of std::find\ntemplate <class Container, class T>\nconstexpr decltype(auto) find(Container&& c, const T& value) {\n  using std::begin;\n  using std::end;\n  return cpp20::find(begin(std::forward<Container>(c)),\n                     end(std::forward<Container>(c)), value);\n}\n\n/// Convenience wrapper around constexpr reimplementation of std::find_if\ntemplate <class Container, class UnaryPredicate>\nconstexpr decltype(auto) find_if(Container&& c,\n                                 UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return cpp20::find_if(begin(std::forward<Container>(c)),\n                        end(std::forward<Container>(c)),\n                        std::forward<UnaryPredicate>(unary_predicate));\n}\n\n/// Convenience wrapper around constexpr reimplementation of std::find_if_not\ntemplate <class Container, class UnaryPredicate>\nconstexpr decltype(auto) find_if_not(Container&& c,\n                                     UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return cpp20::find_if_not(begin(std::forward<Container>(c)),\n                            end(std::forward<Container>(c)),\n                            std::forward<UnaryPredicate>(unary_predicate));\n}\n\n/// Convenience wrapper around constexpr reimplementation of std::find, returns\n/// `true` if `value` is in `c`.\ntemplate <class Container, class T>\nconstexpr bool found(const Container& c, const T& value) {\n  using std::begin;\n  using std::end;\n  return cpp20::find(begin(c), end(c), value) != end(c);\n}\n\n/// Convenience wrapper around constexpr reimplementation of std::find_if,\n/// returns `true` if the result of `cpp20::find_if` is not equal to `end(c)`.\ntemplate <class Container, class UnaryPredicate>\nconstexpr bool found_if(const Container& c, UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return cpp20::find_if(begin(c), end(c),\n                        std::forward<UnaryPredicate>(unary_predicate)) !=\n         end(c);\n}\n\n/// Convenience wrapper around constexpr reimplementation of std::find_if_not,\n/// returns `true` if the result of `cpp20::find_if_not` is not equal to\n/// `end(c)`.\ntemplate <class Container, class UnaryPredicate>\nconstexpr bool found_if_not(const Container& c,\n                            UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return cpp20::find_if_not(begin(c), end(c),\n                            std::forward<UnaryPredicate>(unary_predicate)) !=\n         end(c);\n}\n\n/// @{\n/// Convenience wrapper around std::for_each, returns the result of\n/// `std::for_each(begin(c), end(c), f)`.\ntemplate <class Container, class UnaryFunction>\ndecltype(auto) for_each(const Container& c, UnaryFunction&& f) {\n  using std::begin;\n  using std::end;\n  return std::for_each(begin(c), end(c), std::forward<UnaryFunction>(f));\n}\n\ntemplate <class Container, class UnaryFunction>\ndecltype(auto) for_each(Container& c, UnaryFunction&& f) {\n  using std::begin;\n  using std::end;\n  return std::for_each(begin(c), end(c), std::forward<UnaryFunction>(f));\n}\n/// @}\n\n/// Convenience wrapper around std::equal, assumes containers `lhs` has at least\n/// as many elements as `rhs`.\ntemplate <class Container, class Container2>\ndecltype(auto) equal(const Container& lhs, const Container2& rhs) {\n  using std::begin;\n  using std::end;\n  return std::equal(begin(lhs), end(lhs), begin(rhs));\n}\n\n/// Convenience wrapper around std::equal, assumes containers `lhs` has at least\n/// as many elements as `rhs`.\ntemplate <class Container, class Container2, class BinaryPredicate>\ndecltype(auto) equal(const Container& lhs, const Container2& rhs,\n                     BinaryPredicate&& p) {\n  using std::begin;\n  using std::end;\n  return std::equal(begin(lhs), end(lhs), begin(rhs),\n                    std::forward<BinaryPredicate>(p));\n}\n\n/// Convenience wrapper around std::max_element\ntemplate <class Container>\nconstexpr decltype(auto) max_element(const Container& c) {\n  using std::begin;\n  using std::end;\n  return std::max_element(begin(c), end(c));\n}\n\n/// Convenience wrapper around std::max_element\ntemplate <class Container, class Compare>\nconstexpr decltype(auto) max_element(const Container& c, Compare&& comp) {\n  using std::begin;\n  using std::end;\n  return std::max_element(begin(c), end(c), std::forward<Compare>(comp));\n}\n\n/// Convenience wrapper around std::min_element\ntemplate <class Container>\nconstexpr decltype(auto) min_element(const Container& c) {\n  using std::begin;\n  using std::end;\n  return std::min_element(begin(c), end(c));\n}\n\n/// Convenience wrapper around std::min_element\ntemplate <class Container, class Compare>\nconstexpr decltype(auto) min_element(const Container& c, Compare&& comp) {\n  using std::begin;\n  using std::end;\n  return std::min_element(begin(c), end(c), std::forward<Compare>(comp));\n}\n\n/// Convenience wrapper around std::minmax_element\ntemplate <class Container>\nconstexpr decltype(auto) minmax_element(const Container& c) {\n  using std::begin;\n  using std::end;\n  return std::minmax_element(begin(c), end(c));\n}\n\n/// Convenience wrapper around std::minmax_element\ntemplate <class Container, class Compare>\nconstexpr decltype(auto) minmax_element(const Container& c, Compare&& comp) {\n  using std::begin;\n  using std::end;\n  return std::minmax_element(begin(c), end(c), std::forward<Compare>(comp));\n}\n\n/// Convenience wrapper around std::remove\ntemplate <class Container, class T>\ndecltype(auto) remove(Container& c, const T& value) {\n  using std::begin;\n  using std::end;\n  return std::remove(begin(c), end(c), value);\n}\n\n/// Convenience wrapper around std::remove_if\ntemplate <class Container, class UnaryPredicate>\ndecltype(auto) remove_if(Container& c, UnaryPredicate&& unary_predicate) {\n  using std::begin;\n  using std::end;\n  return std::remove_if(begin(c), end(c),\n                        std::forward<UnaryPredicate>(unary_predicate));\n}\n\n/// Convience wrapper around std::sample\ntemplate <class Container, class OutputIt, class Distance, class URBG>\ndecltype(auto) sample(Container& c, OutputIt out, Distance n, URBG&& g) {\n  using std::begin;\n  using std::end;\n  return std::sample(begin(c), end(c), out, n, g);\n}\n\n/// Convenience wrapper around std::sort\ntemplate <class Container>\ndecltype(auto) sort(Container& c) {\n  using std::begin;\n  using std::end;\n  return std::sort(begin(c), end(c));\n}\n\n/// Convenience wrapper around std::sort\ntemplate <class Container, class Compare>\ndecltype(auto) sort(Container& c, Compare&& comp) {\n  using std::begin;\n  using std::end;\n  return std::sort(begin(c), end(c), std::forward<Compare>(comp));\n}\n\n/// Convenience wrapper around std::transform\ntemplate <class Container, class OutputIt, class UnaryOp>\ndecltype(auto) transform(const Container& c, OutputIt output_first,\n                         UnaryOp&& unary_op) {\n  using std::begin;\n  using std::end;\n  return std::transform(begin(c), end(c), output_first,\n                        std::forward<UnaryOp>(unary_op));\n}\n\n/// Convenience wrapper around std::transform\ntemplate <class Container1, class Container2, class OutputIt, class BinaryOp>\ndecltype(auto) transform(const Container1& c1, const Container2& c2,\n                         OutputIt output_first, BinaryOp&& binary_op) {\n  using std::begin;\n  using std::end;\n  return std::transform(begin(c1), end(c1), begin(c2), output_first,\n                        std::forward<BinaryOp>(binary_op));\n}\n}  // namespace alg\n"
  },
  {
    "path": "src/Utilities/Array.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <iterator>\n#include <ostream>\n#include <utility>\n\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace cpp20 {\nnamespace detail {\ntemplate <typename T, size_t Size, size_t... Is>\nconstexpr std::array<T, Size> convert_to_array(\n    // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n    const T (&t)[Size], std::index_sequence<Is...> /*meta*/) {\n  return {{t[Is]...}};\n}\n}  // namespace detail\n\n/// \\brief A `std::array` implementation with partial C++20 support\n///\n/// \\warning This is not a standard-compliant C++20 `std::array` implementation.\n/// We provide this implementation because we need the `constexpr operator==`\n/// from C++20. Note that other C++20 changes, such as the replacement of the\n/// other comparison operators in favor of `operator<=>`, are not implemented.\n/// This class can be removed when we support C++20.\ntemplate <typename T, size_t Size>\nstruct array {\n  using value_type = T;\n  using reference = value_type&;\n  using const_reference = const value_type&;\n  using iterator = value_type*;\n  using const_iterator = const value_type*;\n  using pointer = value_type*;\n  using const_pointer = const value_type*;\n  using size_type = size_t;\n  using difference_type = std::ptrdiff_t;\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n\n  // clang-tidy: mark explicit. We want implicit conversion\n  constexpr operator std::array<T, Size>() const {  // NOLINT\n    return detail::convert_to_array(data_, std::make_index_sequence<Size>{});\n  }\n\n  constexpr iterator begin() {\n    return iterator(data_);  // NOLINT\n  }\n  constexpr const_iterator begin() const {\n    return const_iterator(data_);  // NOLINT\n  }\n  constexpr iterator end() {\n    return iterator(data_ + Size);  // NOLINT\n  }\n  constexpr const_iterator end() const {\n    return const_iterator(data_ + Size);  // NOLINT\n  }\n\n  constexpr const_iterator cbegin() const { return begin(); }\n  constexpr const_iterator cend() const { return end(); }\n\n  constexpr size_type size() const { return Size; }\n  constexpr size_type max_size() const { return Size; }\n  constexpr bool empty() const { return Size == 0; }\n\n  constexpr reference operator[](const size_type i) {\n    return data_[i];  // NOLINT\n  }\n  constexpr const_reference operator[](const size_type i) const {\n    return data_[i];  // NOLINT\n  }\n\n  constexpr reference at(const size_type i) {\n    return data_[i];  // NOLINT\n  }\n  constexpr const_reference at(const size_type i) const {\n    return data_[i];  // NOLINT\n  }\n\n  constexpr reference front() { return data_[0]; }\n  constexpr const_reference front() const { return data_[0]; }\n  constexpr reference back() { return data_[Size > 0 ? Size - 1 : 0]; }\n  constexpr const_reference back() const {\n    return data_[Size > 0 ? Size - 1 : 0];\n  }\n\n  constexpr value_type* data() { return data_; }\n  constexpr const value_type* data() const { return data_; }\n\n  // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n  value_type data_[Size > 0 ? Size : 1];\n};\nnamespace detail {\ntemplate <typename T = void>\nstruct Equal {\n  constexpr bool inline operator()(const T& lhs, const T& rhs) const {\n    return lhs == rhs;\n  }\n};\n\ntemplate <>\nstruct Equal<void> {\n  template <class T0, class T1>\n  constexpr bool inline operator()(T0&& lhs, T1&& rhs) const {\n    return std::forward<T0>(lhs) == std::forward<T1>(rhs);\n  }\n};\n}  // namespace detail\n\ntemplate <typename InputIter1, typename InputIter2,\n          typename BinaryPred = detail::Equal<>>\nconstexpr bool equal(InputIter1 first1, InputIter1 last1, InputIter2 first2,\n                     BinaryPred pred = detail::Equal<>{}) {\n  for (; first1 != last1; ++first1, ++first2) {\n    if (not pred(*first1, *first2)) {\n      return false;\n    }\n  }\n  return true;\n}\n\ntemplate <typename InputIter1, typename InputIter2,\n          typename BinaryPred = std::less_equal<>>\nconstexpr bool lexicographical_compare(InputIter1 first1, InputIter1 last1,\n                                       InputIter2 first2, InputIter2 last2,\n                                       BinaryPred pred = std::less_equal<>{}) {\n  for (; first2 != last2; ++first1, ++first2) {\n    if (first1 == last1 or pred(*first1, *first2)) {\n      return true;\n    } else if (pred(*first2, *first1)) {\n      return false;\n    }\n  }\n  return false;\n}\n\ntemplate <class T, size_t Size>\ninline constexpr bool operator==(const array<T, Size>& x,\n                                 const array<T, Size>& y) {\n  return equal(x.data_, x.data_ + Size, y.data_);  // NOLINT\n}\n\ntemplate <class T, size_t Size>\ninline constexpr bool operator!=(const array<T, Size>& lhs,\n                                 const array<T, Size>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <class T, size_t Size>\ninline constexpr bool operator<(const array<T, Size>& lhs,\n                                const array<T, Size>& rhs) {\n  return lexicographical_compare(lhs.__elems_, lhs.__elems_ + Size,\n                                 rhs.__elems_, rhs.__elems_ + Size);\n}\n\ntemplate <class T, size_t Size>\ninline constexpr bool operator>(const array<T, Size>& lhs,\n                                const array<T, Size>& rhs) {\n  return rhs < lhs;\n}\n\ntemplate <class T, size_t Size>\ninline constexpr bool operator<=(const array<T, Size>& lhs,\n                                 const array<T, Size>& rhs) {\n  return not(rhs < lhs);\n}\n\ntemplate <class T, size_t Size>\ninline constexpr bool operator>=(const array<T, Size>& lhs,\n                                 const array<T, Size>& rhs) {\n  return not(lhs < rhs);\n}\n\ntemplate <typename T>\ninline std::ostream& operator<<(std::ostream& os, const array<T, 0>& /*a*/) {\n  return os << \"()\";\n}\n\ntemplate <typename T, size_t N>\ninline std::ostream& operator<<(std::ostream& os, const array<T, N>& a) {\n  os << '(';\n  for (size_t i = 0; i < N - 1; ++i) {\n    os << a[i] << ',';\n  }\n  if (N > 0) {\n    os << a[N - 1];\n  }\n  os << ')';\n  return os;\n}\n}  // namespace cpp20\n\nnamespace detail {\ntemplate <typename List, size_t... indices,\n          Requires<not tt::is_a_v<tmpl::list, tmpl::front<List>>> = nullptr>\ninline constexpr auto make_cpp20_array_from_list_helper(\n    std::integer_sequence<size_t, indices...> /*meta*/)\n    -> cpp20::array<std::decay_t<decltype(tmpl::front<List>::value)>,\n                    tmpl::size<List>::value> {\n  return cpp20::array<std::decay_t<decltype(tmpl::front<List>::value)>,\n                      tmpl::size<List>::value>{\n      {tmpl::at<List, tmpl::size_t<indices>>::value...}};\n}\n}  // namespace detail\n\n/// \\ingroup ConstantExpressionsGroup\n/// Make an array from a typelist that holds std::integral_constant's all of\n/// which have the same `value_type`\n///\n/// \\tparam List the typelist of std::integral_constant's\n/// \\return array of integral values from the typelist\ntemplate <typename List,\n          Requires<not tt::is_a_v<tmpl::list, tmpl::front<List>>> = nullptr>\ninline constexpr auto make_cpp20_array_from_list()\n    -> cpp20::array<std::decay_t<decltype(tmpl::front<List>::value)>,\n                    tmpl::size<List>::value> {\n  return detail::make_cpp20_array_from_list_helper<List>(\n      std::make_integer_sequence<size_t, tmpl::size<List>::value>{});\n}\n\ntemplate <typename TypeForZero,\n          Requires<not tt::is_a_v<tmpl::list, TypeForZero>> = nullptr>\ninline constexpr cpp20::array<std::decay_t<TypeForZero>, 0>\nmake_cpp20_array_from_list() {\n  return cpp20::array<std::decay_t<TypeForZero>, 0>{{}};\n}\n\nnamespace detail {\ntemplate <typename T, size_t Size, size_t... Is>\ninline constexpr cpp20::array<T, Size> convert_to_cpp20_array_impl(\n    const std::array<T, Size>& t, std::index_sequence<Is...> /*meta*/) {\n  return {{t[Is]...}};\n}\n}  // namespace detail\n\ntemplate <typename T, size_t Size, size_t... Is>\ninline constexpr cpp20::array<T, Size> convert_to_cpp20_array(\n    const std::array<T, Size>& t) {\n  return detail::convert_to_cpp20_array_impl(t,\n                                             std::make_index_sequence<Size>{});\n}\n"
  },
  {
    "path": "src/Utilities/Autodiff/Autodiff.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#ifdef SPECTRE_AUTODIFF\n\n#include <autodiff/common/numbertraits.hpp>\n#include <autodiff/forward/dual.hpp>\n#include <autodiff/reverse/var.hpp>\n\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Simd/Simd.hpp\"\n\nnamespace autodiff {\nusing BatchType = simd::batch<double>;\nusing SecondOrderDual = autodiff::HigherOrderDual<2, BatchType>;\nusing SecondOrderDualNum = autodiff::HigherOrderDual<2, double>;\n\nnamespace detail {\n/// Template specialization for simd::batch<double> to treat it as arithmetic.\n// The major difficulty we have with DataVector working with autodiff is\n// DataVector does not have a scalar broadcast constructor, which is expected\n// in the seed function for autodiff dual type.\ntemplate <>\nstruct ArithmeticTraits<simd::batch<double>> {\n  static constexpr bool isArithmetic = true;\n};\n}  // namespace detail\n}  // namespace autodiff\n\nnamespace MakeWithValueImpls {\ntemplate <typename T>\nstruct MakeWithValueImpl<autodiff::HigherOrderDual<2, double>, T> {\n  static SPECTRE_ALWAYS_INLINE autodiff::HigherOrderDual<2, double> apply(\n      const T& /* input */, const double value) {\n    return {value};\n  }\n};\n\ntemplate <typename T>\nstruct MakeWithValueImpl<autodiff::HigherOrderDual<2, simd::batch<double>>, T> {\n  static SPECTRE_ALWAYS_INLINE autodiff::HigherOrderDual<2, simd::batch<double>>\n  apply(const T& /* input */, const double value) {\n    return {simd::batch<double>::broadcast(value)};\n  }\n};\n}  // namespace MakeWithValueImpls\n\n#endif  // SPECTRE_AUTODIFF\n"
  },
  {
    "path": "src/Utilities/Autodiff/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Having a wrapper target will make it easier to replace autodiff\n# with another auto-differentiation library if we ever need to.\n\nset(LIBRARY Autodiff)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Autodiff.hpp\n  )\n\nif (SPECTRE_AUTODIFF)\n  target_link_libraries(\n    ${LIBRARY}\n    INTERFACE\n    autodiff::autodiff\n    Simd\n    )\n\n  target_compile_definitions(\n    ${LIBRARY}\n    INTERFACE\n    SPECTRE_AUTODIFF\n    )\n\n  message(STATUS \"Enabling autodiff support\")\nendif()\n"
  },
  {
    "path": "src/Utilities/Base64.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/Base64.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nconst std::array<char, 64> alphabet{\n    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',\n    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\n    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};\n\nconst char padding_char = '=';\n}  // namespace\n\nstd::string base64_encode(const std::vector<std::byte>& data) {\n  const size_t encoded_size = (data.size() + 2) / 3 * 4;\n  std::string encoded{};\n  encoded.reserve(encoded_size);\n\n  auto octet = data.begin();\n  while (octet != data.end()) {\n    size_t bits = 0;\n    size_t octets_in_group = 0;\n    while (octets_in_group < 3 and octet != data.end()) {\n      bits |= static_cast<size_t>(*octet) << 8 * (2 - octets_in_group);\n      ++octet;\n      ++octets_in_group;\n    }\n\n    const size_t characters_to_output = octets_in_group + 1;\n\n    for (size_t i = 0; i < characters_to_output; ++i) {\n      const auto hextet = 0x3F & (bits >> 6 * (3 - i));\n      encoded.push_back(gsl::at(alphabet, hextet));\n    }\n\n    if (characters_to_output < 4) {\n      encoded.push_back(padding_char);\n      if (characters_to_output < 3) {\n        encoded.push_back(padding_char);\n      }\n      ASSERT(\n          characters_to_output >= 2,\n          \"Output an invalid number of characters: \" << characters_to_output);\n    }\n  }\n\n  ASSERT(encoded.size() == encoded_size,\n         \"Expected \" << encoded_size << \" characters for \" << data.size()\n                     << \" bytes, but wrote \" << encoded.size());\n  return encoded;\n}\n\nstd::vector<std::byte> base64_decode(const std::string& encoded) {\n  static const std::array<std::byte, 256> reverse_alphabet = []() {\n    std::array<std::byte, 256> local_reverse_alphabet{};\n    std::fill(local_reverse_alphabet.begin(), local_reverse_alphabet.end(),\n              std::byte{255});\n    for (size_t i = 0; i < alphabet.size(); ++i) {\n      gsl::at(local_reverse_alphabet,\n              static_cast<size_t>(gsl::at(alphabet, i))) =\n          static_cast<std::byte>(i);\n    }\n    gsl::at(local_reverse_alphabet, static_cast<size_t>(padding_char)) =\n        std::byte{0};\n    return local_reverse_alphabet;\n  }();\n\n  if (encoded.size() % 4 != 0) {\n    ERROR(\"base64 encoded data must have a multiple of 4 characters, not \"\n          << encoded.size());\n  }\n\n  // Might be slightly too large.\n  const size_t decoded_size_bound = encoded.size() / 4 * 3;\n  std::vector<std::byte> decoded{};\n  decoded.reserve(decoded_size_bound);\n\n  auto encoded_char = encoded.begin();\n  while (encoded_char != encoded.end()) {\n    size_t bits = 0;\n    int padding = 0;\n    for (size_t i = 0; i < 4; ++i) {\n      const auto hextet =\n          gsl::at(reverse_alphabet, static_cast<size_t>(*encoded_char));\n      if (hextet == std::byte{255}) {\n        ERROR(\"Invalid character in base64 encoded string: '\" << *encoded_char\n                                                              << \"'\");\n      }\n      bits <<= 6;\n      bits |= static_cast<size_t>(hextet);\n      if (padding > 0 and *encoded_char != padding_char) {\n        ERROR(\"Misplaced padding character at position \"\n              << (encoded_char - encoded.begin() - padding)\n              << \" of base64 encoded string.\");\n      }\n      if (*encoded_char == padding_char) {\n        ++padding;\n      }\n      ++encoded_char;\n    }\n\n    if (padding > 2 or (padding != 0 and encoded_char != encoded.end())) {\n      ERROR(\"Misplaced padding character at position \"\n            << (encoded_char - encoded.begin() - padding)\n            << \" of base64 encoded string.\");\n    }\n\n    const int octets_to_output = 3 - padding;\n    for (int i = 0; i < octets_to_output; ++i) {\n      const auto octet = 0xFF & (bits >> 8 * (2 - i));\n      decoded.push_back(static_cast<std::byte>(octet));\n    }\n  }\n\n  ASSERT(decoded.size() <= decoded_size_bound and\n             decoded.size() + 3 > decoded_size_bound,\n         \"Expected approximately \"\n             << decoded_size_bound << \" bytes for \" << encoded.size()\n             << \" characters, but wrote \" << decoded.size());\n  return decoded;\n}\n"
  },
  {
    "path": "src/Utilities/Base64.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n/// Encode and decode data using the RFC4648 base64 format.\n/// @{\nstd::string base64_encode(const std::vector<std::byte>& data);\nstd::vector<std::byte> base64_decode(const std::string& encoded);\n/// @}\n"
  },
  {
    "path": "src/Utilities/Blas.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/Blas.hpp\"\n\nextern \"C\" {\n#ifdef DISABLE_OPENBLAS_MULTITHREADING\n// Declaring this ourselves instead of including cblas.h because our `Blas`\n// library does not provide include directories, so cblas.h might not be\n// available.\nvoid openblas_set_num_threads(int num_threads);\n#endif  // DISABLE_OPENBLAS_MULTITHREADING\n}  // extern \"C\"\n\nvoid disable_openblas_multithreading() {\n#ifdef DISABLE_OPENBLAS_MULTITHREADING\n  openblas_set_num_threads(1);\n#endif  // DISABLE_OPENBLAS_MULTITHREADING\n}\n"
  },
  {
    "path": "src/Utilities/Blas.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares the interfaces for the BLAS used.\n///\n/// Wrappers are defined to perform casts from different integer types\n/// when the natural type in C++ differs from the BLAS argument.\n\n#pragma once\n\n#include <complex>\n\n#ifndef SPECTRE_DEBUG\n#include <libxsmm.h>\n#endif  // ifndef SPECTRE_DEBUG\n#include <gsl/gsl_cblas.h>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace blas_detail {\nextern \"C\" {\ndouble ddot_(const int& N, const double* X, const int& INCX, const double* Y,\n             const int& INCY);\n\n// The final two arguments are the \"hidden\" lengths of the first two.\n// https://gcc.gnu.org/onlinedocs/gfortran/Argument-passing-conventions.html\nvoid dgemm_(const char& TRANSA, const char& TRANSB, const int& M, const int& N,\n            const int& K, const double& ALPHA, const double* A, const int& LDA,\n            const double* B, const int& LDB, const double& BETA,\n            const double* C, const int& LDC, size_t, size_t);\nvoid zgemm_(const char& TRANSA, const char& TRANSB, const int& M, const int& N,\n            const int& K, const std::complex<double>& ALPHA,\n            const std::complex<double>* A, const int& LDA,\n            const std::complex<double>* B, const int& LDB,\n            const std::complex<double>& BETA, const std::complex<double>* C,\n            const int& LDC, size_t, size_t);\n\n// The final argument is the \"hidden\" length of the first one.\n// https://gcc.gnu.org/onlinedocs/gfortran/Argument-passing-conventions.html\nvoid dgemv_(const char& TRANS, const int& M, const int& N, const double& ALPHA,\n            const double* A, const int& LDA, const double* X, const int& INCX,\n            const double& BETA, double* Y, const int& INCY, size_t);\n}  // extern \"C\"\n}  // namespace blas_detail\n\n/*!\n * \\brief Disable OpenBLAS multithreading since it conflicts with Charm++\n * parallelism\n *\n * Add this function to the `charm_init_node_funcs` of any executable that uses\n * BLAS routines.\n *\n * Details: https://github.com/xianyi/OpenBLAS/wiki/Faq#multi-threaded\n */\nvoid disable_openblas_multithreading();\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * The dot product of two vectors.\n *\n * \\param N the length of the vectors.\n * \\param X a pointer to the first element of the first vector.\n * \\param INCX the stride for the elements of the first vector.\n * \\param Y a pointer to the first element of the second vector.\n * \\param INCY the stride for the elements of the second vector.\n * \\return the dot product of the given vectors.\n */\ninline double ddot_(const size_t& N, const double* X, const size_t& INCX,\n                    const double* Y, const size_t& INCY) {\n  // INCX and INCY are allowed to be negative by BLAS, but we never\n  // use them that way.  If needed, they can be changed here, but then\n  // code providing values will also have to be changed to int to\n  // avoid warnings.\n  return blas_detail::ddot_(gsl::narrow_cast<int>(N), X,\n                            gsl::narrow_cast<int>(INCX), Y,\n                            gsl::narrow_cast<int>(INCY));\n}\n/// The unconjugated complex dot product $x \\cdot y$. See `zdotc_` for the\n/// conjugated complex dot product, which is the standard dot product on the\n/// vector space of complex numbers.\ninline std::complex<double> zdotu_(const size_t& N,\n                                   const std::complex<double>* X,\n                                   const size_t& INCX,\n                                   const std::complex<double>* Y,\n                                   const size_t& INCY) {\n  // The complex result of the BLAS zdot* functions is sometimes returned by\n  // value and sometimes returned by reference, depending on the Fortran\n  // compiler settings. By using the cblas interface we ensure a consistent\n  // behavior.\n  std::complex<double> result;\n  cblas_zdotu_sub(gsl::narrow_cast<int>(N), X, gsl::narrow_cast<int>(INCX), Y,\n                  gsl::narrow_cast<int>(INCY), &result);\n  return result;\n}\n/// The conjugated complex dot product $\\bar{x} \\cdot y$. This is the standard\n/// dot product on the vector space of complex numbers.\ninline std::complex<double> zdotc_(const size_t& N,\n                                   const std::complex<double>* X,\n                                   const size_t& INCX,\n                                   const std::complex<double>* Y,\n                                   const size_t& INCY) {\n  std::complex<double> result;\n  cblas_zdotc_sub(gsl::narrow_cast<int>(N), X, gsl::narrow_cast<int>(INCX), Y,\n                  gsl::narrow_cast<int>(INCY), &result);\n  return result;\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Perform a matrix-matrix multiplication\n *\n * Perform the matrix-matrix multiplication\n * \\f[\n * C = \\alpha \\mathrm{op}(A) \\mathrm{op}(B) + \\beta \\mathrm{op}(C)\n * \\f]\n *\n * where \\f$\\mathrm{op}(A)\\f$ represents either \\f$A\\f$ or \\f$A^{T}\\f$\n * (transpose of \\f$A\\f$).\n *\n * LIBXSMM, which is much faster than BLAS for small matrices, can be called\n * instead of BLAS by passing the template parameter `true`.\n *\n * \\param TRANSA either 'N', 'T' or 'C', transposition of matrix A\n * \\param TRANSB either 'N', 'T' or 'C', transposition of matrix B\n * \\param M Number of rows in \\f$\\mathrm{op}(A)\\f$\n * \\param N Number of columns in \\f$\\mathrm{op}(B)\\f$ and \\f$\\mathrm{op}(C)\\f$\n * \\param K Number of columns in \\f$\\mathrm{op}(A)\\f$\n * \\param ALPHA specifies \\f$\\alpha\\f$\n * \\param A Matrix \\f$A\\f$\n * \\param LDA Specifies first dimension of \\f$\\mathrm{op}(A)\\f$\n * \\param B Matrix \\f$B\\f$\n * \\param LDB Specifies first dimension of \\f$\\mathrm{op}(B)\\f$\n * \\param BETA specifies \\f$\\beta\\f$\n * \\param C Matrix \\f$C\\f$\n * \\param LDC Specifies first dimension of \\f$\\mathrm{op}(C)\\f$\n * \\tparam UseLibXsmm if `true` then use LIBXSMM\n */\ntemplate <bool UseLibXsmm = false>\ninline void dgemm_(const char& TRANSA, const char& TRANSB, const size_t& M,\n                   const size_t& N, const size_t& K, const double& ALPHA,\n                   const double* A, const size_t& LDA, const double* B,\n                   const size_t& LDB, const double& BETA, double* C,\n                   const size_t& LDC) {\n  ASSERT('N' == TRANSA or 'n' == TRANSA or 'T' == TRANSA or 't' == TRANSA or\n             'C' == TRANSA or 'c' == TRANSA,\n         \"TRANSA must be upper or lower case N, T, or C. See the BLAS \"\n         \"documentation for help.\");\n  ASSERT('N' == TRANSB or 'n' == TRANSB or 'T' == TRANSB or 't' == TRANSB or\n             'C' == TRANSB or 'c' == TRANSB,\n         \"TRANSB must be upper or lower case N, T, or C. See the BLAS \"\n         \"documentation for help.\");\n\n  // On some BLAS implementations (e.g. Accelerate.framework on macOS) a call\n  // with a zero-sized dimension aborts instead of acting as a no-op.  Treat\n  // these cases explicitly so we behave consistently across platforms.\n  if (M == 0 or N == 0 or K == 0) {\n    return;\n  }\n\n  blas_detail::dgemm_(\n      TRANSA, TRANSB, gsl::narrow_cast<int>(M), gsl::narrow_cast<int>(N),\n      gsl::narrow_cast<int>(K), ALPHA, A, gsl::narrow_cast<int>(LDA), B,\n      gsl::narrow_cast<int>(LDB), BETA, C, gsl::narrow_cast<int>(LDC), 1, 1);\n}\ntemplate <bool UseLibXsmm = false>\ninline void zgemm_(const char& TRANSA, const char& TRANSB, const size_t& M,\n                   const size_t& N, const size_t& K,\n                   const std::complex<double>& ALPHA,\n                   const std::complex<double>* A, const size_t& LDA,\n                   const std::complex<double>* B, const size_t& LDB,\n                   const std::complex<double>& BETA, std::complex<double>* C,\n                   const size_t& LDC) {\n  ASSERT('N' == TRANSA or 'n' == TRANSA or 'T' == TRANSA or 't' == TRANSA or\n             'C' == TRANSA or 'c' == TRANSA,\n         \"TRANSA must be upper or lower case N, T, or C. See the BLAS \"\n         \"documentation for help.\");\n  ASSERT('N' == TRANSB or 'n' == TRANSB or 'T' == TRANSB or 't' == TRANSB or\n             'C' == TRANSB or 'c' == TRANSB,\n         \"TRANSB must be upper or lower case N, T, or C. See the BLAS \"\n         \"documentation for help.\");\n\n  // On some BLAS implementations (e.g. Accelerate.framework on macOS) a call\n  // with a zero-sized dimension aborts instead of acting as a no-op.  Treat\n  // these cases explicitly so we behave consistently across platforms.\n  if (M == 0 or N == 0 or K == 0) {\n    return;\n  }\n\n  blas_detail::zgemm_(\n      TRANSA, TRANSB, gsl::narrow_cast<int>(M), gsl::narrow_cast<int>(N),\n      gsl::narrow_cast<int>(K), ALPHA, A, gsl::narrow_cast<int>(LDA), B,\n      gsl::narrow_cast<int>(LDB), BETA, C, gsl::narrow_cast<int>(LDC), 1, 1);\n}\n\n// libxsmm is disabled in DEBUG builds because backtraces (from, for\n// example, FPEs) do not work when the error occurs in libxsmm code.\n#ifndef SPECTRE_DEBUG\ntemplate <>\ninline void dgemm_<true>(const char& TRANSA, const char& TRANSB,\n                         const size_t& M, const size_t& N, const size_t& K,\n                         const double& ALPHA, const double* A,\n                         const size_t& LDA, const double* B, const size_t& LDB,\n                         const double& BETA, double* C, const size_t& LDC) {\n  ASSERT('N' == TRANSA or 'n' == TRANSA or 'T' == TRANSA or 't' == TRANSA or\n             'C' == TRANSA or 'c' == TRANSA,\n         \"TRANSA must be upper or lower case N, T, or C. See the BLAS \"\n         \"documentation for help.\");\n  ASSERT('N' == TRANSB or 'n' == TRANSB or 'T' == TRANSB or 't' == TRANSB or\n             'C' == TRANSB or 'c' == TRANSB,\n         \"TRANSB must be upper or lower case N, T, or C. See the BLAS \"\n         \"documentation for help.\");\n\n  // On some BLAS implementations (e.g. Accelerate.framework on macOS) a call\n  // with a zero-sized dimension aborts instead of acting as a no-op.  Treat\n  // these cases explicitly so we behave consistently across platforms.\n  if (M == 0 or N == 0 or K == 0) {\n    return;\n  }\n\n  const auto m = gsl::narrow_cast<int>(M);\n  const auto n = gsl::narrow_cast<int>(N);\n  const auto k = gsl::narrow_cast<int>(K);\n  const auto lda = gsl::narrow_cast<int>(LDA);\n  const auto ldb = gsl::narrow_cast<int>(LDB);\n  const auto ldc = gsl::narrow_cast<int>(LDC);\n  libxsmm_dgemm(&TRANSA, &TRANSB, &m, &n, &k, &ALPHA, A, &lda, B, &ldb, &BETA,\n                C, &ldc);\n}\n#endif  // ifndef SPECTRE_DEBUG\n/// @}\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Perform a matrix-vector multiplication\n *\n * \\f[\n * y = \\alpha \\mathrm{op}(A) x + \\beta y\n * \\f]\n *\n * where \\f$\\mathrm{op}(A)\\f$ represents either \\f$A\\f$ or \\f$A^{T}\\f$\n * (transpose of \\f$A\\f$).\n *\n * \\param TRANS either 'N', 'T' or 'C', transposition of matrix A\n * \\param M Number of rows in \\f$\\mathrm{op}(A)\\f$\n * \\param N Number of columns in \\f$\\mathrm{op}(A)\\f$\n * \\param ALPHA specifies \\f$\\alpha\\f$\n * \\param A Matrix \\f$A\\f$\n * \\param LDA Specifies first dimension of \\f$\\mathrm{op}(A)\\f$\n * \\param X Vector \\f$x\\f$\n * \\param INCX Specifies the increment for the elements of \\f$x\\f$\n * \\param BETA Specifies \\f$\\beta\\f$\n * \\param Y Vector \\f$y\\f$\n * \\param INCY Specifies the increment for the elements of \\f$y\\f$\n */\ninline void dgemv_(const char& TRANS, const size_t& M, const size_t& N,\n                   const double& ALPHA, const double* A, const size_t& LDA,\n                   const double* X, const size_t& INCX, const double& BETA,\n                   double* Y, const size_t& INCY) {\n  ASSERT('N' == TRANS or 'n' == TRANS or 'T' == TRANS or 't' == TRANS or\n             'C' == TRANS or 'c' == TRANS,\n         \"TRANS must be upper or lower case N, T, or C. See the BLAS \"\n         \"documentation for help.\");\n\n  // On some BLAS implementations (e.g. Accelerate.framework on macOS) a call\n  // with a zero-sized dimension aborts instead of acting as a no-op.  Treat\n  // these cases explicitly so we behave consistently across platforms.\n  if (M == 0 or N == 0) {\n    return;\n  }\n\n  // INCX and INCY are allowed to be negative by BLAS, but we never\n  // use them that way.  If needed, they can be changed here, but then\n  // code providing values will also have to be changed to int to\n  // avoid warnings.\n  blas_detail::dgemv_(TRANS, gsl::narrow_cast<int>(M), gsl::narrow_cast<int>(N),\n                      ALPHA, A, gsl::narrow_cast<int>(LDA), X,\n                      gsl::narrow_cast<int>(INCX), BETA, Y,\n                      gsl::narrow_cast<int>(INCY), 1);\n}\n/// @}\n"
  },
  {
    "path": "src/Utilities/BlazeExceptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <csignal>\n\n// Blaze 3.8.2 has a missing include that causes nvcc to fail\n// (fixed in https://bitbucket.org/blaze-lib/blaze/pull-requests/63).\n// We just include it here to fix the issue.\n// NOLINTBEGIN\n#include <blaze/util/EnableIf.h>\n#include <blaze/math/traits/ReduceTrait.h>\n// NOLINTEND\n\n#ifdef __CUDA_ARCH__\n// When building for Nvidia GPUs we need to disable the use of vector\n// intrinsics.\n#define BLAZE_USE_VECTORIZATION 0\n#endif\n\n#ifdef SPECTRE_DEBUG\n#define BLAZE_THROW(EXCEPTION)           \\\n  struct sigaction handler {};           \\\n  handler.sa_handler = SIG_IGN;          \\\n  handler.sa_flags = 0;                  \\\n  sigemptyset(&handler.sa_mask);         \\\n  sigaction(SIGTRAP, &handler, nullptr); \\\n  raise(SIGTRAP);                        \\\n  throw EXCEPTION\n#else  // SPECTRE_DEBUG\n#define BLAZE_THROW(EXCEPTION)\n#endif  // SPECTRE_DEBUG\n"
  },
  {
    "path": "src/Utilities/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\nadd_subdirectory(Autodiff)\nadd_subdirectory(Kokkos)\nadd_subdirectory(Simd)\n\nset(LIBRARY Utilities)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Base64.cpp\n  Blas.cpp\n  FileSystem.cpp\n  Formaline.cpp\n  MemoryHelpers.cpp\n  OptimizerHacks.cpp\n  PrettyType.cpp\n  Rational.cpp\n  SnakeCase.cpp\n  UtcTime.cpp\n  WrapText.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Algorithm.hpp\n  Array.hpp\n  Base64.hpp\n  Blas.hpp\n  BlazeExceptions.hpp\n  CachedFunction.hpp\n  CallWithDynamicType.hpp\n  CartesianProduct.hpp\n  CleanupRoutine.hpp\n  CloneUniquePtrs.hpp\n  ConstantExpressions.hpp\n  ContainerHelpers.hpp\n  ContainsAllocations.hpp\n  DereferenceWrapper.hpp\n  EqualWithinRoundoff.hpp\n  FileSystem.hpp\n  ForceInline.hpp\n  Formaline.hpp\n  FractionUtilities.hpp\n  Functional.hpp\n  GenerateInstantiations.hpp\n  GetOutput.hpp\n  Gsl.hpp\n  Literals.hpp\n  MakeArray.hpp\n  MakeSignalingNan.hpp\n  MakeString.hpp\n  MakeVector.hpp\n  MakeWithValue.hpp\n  Math.hpp\n  MemoryHelpers.hpp\n  NoSuchType.hpp\n  Numeric.hpp\n  OptimizerHacks.hpp\n  OptionalHelpers.hpp\n  Overloader.hpp\n  PrettyType.hpp\n  PrintHelpers.hpp\n  ProtocolHelpers.hpp\n  Rational.hpp\n  Registration.hpp\n  Requires.hpp\n  SetNumberOfGridPoints.hpp\n  SnakeCase.hpp\n  Spherepack.hpp\n  SplitTuple.hpp\n  StaticCache.hpp\n  StdArrayHelpers.hpp\n  StdHelpers.hpp\n  StlBoilerplate.hpp\n  StlStreamDeclarations.hpp\n  TMPL.hpp\n  TmplDebugging.hpp\n  TmplDigraph.hpp\n  Tuple.hpp\n  TupleSlice.hpp\n  TypeTraits.hpp\n  UtcTime.hpp\n  VectorAlgebra.hpp\n  WrapText.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Autodiff\n  BLAS::BLAS\n  Blaze\n  Boost::boost\n  Brigand\n  ErrorHandling\n  SpectreKokkos\n  PRIVATE\n  Printf\n  )\n\n# Libxsmm is disabled in Debug mode (see Blas.hpp).\n# As of Sep 1, 2023 libxsmm has an issue with lldb on ARM Macs (detecting\n# the CPU architecture raises SIGILL signals when loading the libxsmm dynamic\n# library). Just not linking libxsmm in Debug mode works around that.\nif (NOT (SPECTRE_DEBUG AND APPLE))\n  target_link_libraries(\n    ${LIBRARY}\n    PUBLIC\n    Libxsmm\n    )\nendif()\n\nadd_subdirectory(ErrorHandling)\nadd_subdirectory(Protocols)\nadd_subdirectory(StdHelpers)\nadd_subdirectory(System)\nadd_subdirectory(Serialization)\nadd_subdirectory(TypeTraits)\n\nif (NOT USE_FORMALINE)\n  set_source_files_properties(\n    Formaline.cpp\n    PROPERTIES\n    COMPILE_DEFINITIONS SPECTRE_NO_FORMALINE)\nendif (NOT USE_FORMALINE)\n"
  },
  {
    "path": "src/Utilities/CachedFunction.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class CachedFunction\n\n#pragma once\n\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n/// A function wrapper that caches function values.\ntemplate <typename Function, typename Map>\nclass CachedFunction {\n public:\n  using input = typename Map::key_type;\n  using output = typename Map::mapped_type;\n\n  template <typename... MapArgs>\n  explicit CachedFunction(Function function, MapArgs... map_args)\n      : function_(std::move(function)),\n        cache_(std::forward<MapArgs>(map_args)...) {}\n\n  /// Obtain the function result\n  const output& operator()(const input& x) {\n    auto it = cache_.find(x);\n    if (it == cache_.end()) {\n      it = cache_.emplace(x, function_(x)).first;\n    }\n    return it->second;\n  }\n\n  /// Clear the cache entries\n  void clear() { cache_.clear(); }\n\n private:\n  Function function_;\n  Map cache_;\n};\n\n/// Construct a CachedFunction wrapping the given function\n///\n/// \\example\n/// \\snippet Test_CachedFunction.cpp make_cached_function_example\n///\n/// \\tparam Input function argument type\n/// \\tparam Map class template to use as the map holding the cache\n/// \\param function the function\n/// \\param map_args arguments to pass to the map constructor\ntemplate <typename Input, template <typename...> class Map = std::unordered_map,\n          typename... MapArgs, typename Function, typename... PassedMapArgs>\nauto make_cached_function(Function function, PassedMapArgs... map_args) {\n  using output = std::invoke_result_t<Function&, const Input&>;\n  return CachedFunction<Function, Map<std::decay_t<Input>,\n                                      std::decay_t<output>,\n                                      MapArgs...>>(\n      std::move(function), std::forward<PassedMapArgs>(map_args)...);\n}\n"
  },
  {
    "path": "src/Utilities/CallWithDynamicType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n#include <typeinfo>\n#include <utility>\n\n#include \"TypeTraits/FastPointerCast.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Call a functor with the derived type of a base class pointer.\n///\n/// \\details Calls functor with obj cast to type `T*` where T is the\n/// dynamic type of `*obj`.  The decay type of `T` must be in the\n/// provided list of classes.\n///\n/// Extra arguments can be passed by `const&` to the functor.\n///\n/// \\tparam Result the return type\n/// \\tparam Classes the typelist of derived classes\ntemplate <typename Result, typename Classes, typename Base, typename Callable,\n          typename... Args>\nResult call_with_dynamic_type(Base* const obj, Callable&& f, Args&&... args) {\n  if constexpr (tmpl::size<Classes>::value != 0) {\n    using Derived = tmpl::front<Classes>;\n    using DerivedPointer = tmpl::conditional_t<std::is_const<Base>::value,\n                                               Derived const*, Derived*>;\n    return typeid(*obj) == typeid(Derived)\n               ? std::forward<Callable>(f)(\n                     tt::fast_pointer_cast<DerivedPointer>(obj),\n                     std::forward<Args>(args)...)\n               : call_with_dynamic_type<Result, tmpl::pop_front<Classes>>(\n                     obj, std::forward<Callable>(f),\n                     std::forward<Args>(args)...);\n  } else {\n    ERROR(\"Class \" << pretty_type::get_runtime_type_name(*obj)\n                   << \" is not registered with \"\n                   << pretty_type::get_name<std::remove_const_t<Base>>());\n  }\n}\n"
  },
  {
    "path": "src/Utilities/CartesianProduct.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <iterator>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n// This implementation is from:\n// https://stackoverflow.com/questions/16686942/how-to-iterate-over-two-stl-like-containers-cartesian-product\n\nnamespace detail {\n\n// the lambda is fully bound with one element from each of the ranges\ntemplate <class Op>\nvoid insert_tuples(Op op) {\n  // evaluating the lambda will insert the currently bound tuple\n  op();\n}\n\n// \"peel off\" the first range from the remaining tuple of ranges\ntemplate <class Op, class InputIterator1, class... InputIterator2>\nvoid insert_tuples(Op op, std::pair<InputIterator1, InputIterator1> head,\n                   std::pair<InputIterator2, InputIterator2>... tail) {\n  // \"peel off\" the elements from the first of the remaining ranges\n  // NOTE: the recursion will effectively generate the multiple nested for-loops\n  for (auto it = head.first; it != head.second; ++it) {\n    // bind the first free variable in the lambda, and\n    // keep one free variable for each of the remaining ranges\n    detail::insert_tuples(\n        [&op, &it](InputIterator2... elems) { op(it, elems...); }, tail...);\n  }\n}\n\n}  // namespace detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Fill the `result` iterator with the Cartesian product of a sequence of\n * iterators\n *\n * The `result` will hold all possible combinations of the input iterators.\n * The last dimension varies fastest.\n */\ntemplate <class OutputIterator, class... InputIterator>\nvoid cartesian_product(OutputIterator result,\n                       std::pair<InputIterator, InputIterator>... dimensions) {\n  detail::insert_tuples(\n      [&result](InputIterator... elems) {\n        *result++ = std::make_tuple(*elems...);\n      },\n      dimensions...);\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief The Cartesian product of a sequence of arrays\n *\n * Returns a `std::array` with all possible combinations of the input arrays.\n * The last dimension varies fastest.\n *\n * \\example\n * Here's an example using this function to replace a nested for loop:\n * \\snippet Test_Wedge3D.cpp cartesian_product_loop\n */\ntemplate <typename... Ts, size_t... Lens>\nstd::array<std::tuple<Ts...>, (... * Lens)> cartesian_product(\n    const std::array<Ts, Lens>&... dimensions) {\n  std::array<std::tuple<Ts...>, (... * Lens)> result{};\n  cartesian_product(result.begin(),\n                    std::make_pair(dimensions.begin(), dimensions.end())...);\n  return result;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief The Cartesian product of several containers\n *\n * Returns a `std::vector` with all possible combinations of the values of the\n * input containers. The value of the last container varies fastest.\n */\ntemplate <typename... Containers>\nstd::vector<std::tuple<typename Containers::value_type...>> cartesian_product(\n    const Containers&... containers) {\n  std::vector<std::tuple<typename Containers::value_type...>> result{};\n  cartesian_product(\n      std::back_inserter(result),\n      std::make_pair(std::begin(containers), std::end(containers))...);\n  return result;\n}\n"
  },
  {
    "path": "src/Utilities/CleanupRoutine.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <utility>\n\n/// An object that calls a functor when it goes out of scope.\n///\n/// \\snippet Test_CleanupRoutine.cpp cleanup_routine\ntemplate <typename F>\nclass CleanupRoutine {\n public:\n  CleanupRoutine(F routine) : routine_(std::move(routine)) {}\n\n  // Copying/moving don't make sense, and it's not clear what a\n  // moved-out-of instance would be expected to do.\n  CleanupRoutine(const CleanupRoutine&) = delete;\n  CleanupRoutine(CleanupRoutine&&) = delete;\n  CleanupRoutine& operator=(const CleanupRoutine&) = delete;\n  CleanupRoutine& operator=(CleanupRoutine&&) = delete;\n\n  ~CleanupRoutine() { routine_(); }\n\n private:\n  F routine_;\n};\n"
  },
  {
    "path": "src/Utilities/CloneUniquePtrs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <unordered_map>\n#include <vector>\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Given a map of `std::unique_ptr` returns a copy of the map by\n/// invoking `get_clone()` on each element of the input map.\ntemplate <typename KeyType, typename T>\nstd::unordered_map<KeyType, std::unique_ptr<T>> clone_unique_ptrs(\n    const std::unordered_map<KeyType, std::unique_ptr<T>>& map) {\n  std::unordered_map<KeyType, std::unique_ptr<T>> result{};\n  for (const auto& kv : map) {\n    result[kv.first] = kv.second->get_clone();\n  }\n  return result;\n}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Given a vector of `std::unique_ptr` returns a copy of the vector by\n/// invoking `get_clone()` on each element of the input vector.\ntemplate <typename T>\nstd::vector<std::unique_ptr<T>> clone_unique_ptrs(\n    const std::vector<std::unique_ptr<T>>& vector) {\n  std::vector<std::unique_ptr<T>> result{vector.size()};\n  for (size_t i = 0; i < vector.size(); ++i) {\n    result[i] = vector[i]->get_clone();\n  }\n  return result;\n}\n"
  },
  {
    "path": "src/Utilities/ConstantExpressions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Define simple functions for constant expressions.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cassert>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <initializer_list>\n#include <utility>\n\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsInteger.hpp\"\n\n/// \\ingroup ConstantExpressionsGroup\n/// Compute 2 to the n for integral types.\n///\n/// \\param n the power of two to compute.\n/// \\return 2^n\ntemplate <typename T,\n          Requires<tt::is_integer_v<T> and std::is_unsigned_v<T>> = nullptr>\nSPECTRE_ALWAYS_INLINE constexpr T two_to_the(T n) {\n  return T(1) << n;\n}\n\n/// \\ingroup ConstantExpressionsGroup\n/// Get the nth bit from the right, counting from zero.\n///\n/// \\param i the value to be acted on.\n/// \\param N which place to extract the bit\n/// \\return the value of the bit at that place.\nSPECTRE_ALWAYS_INLINE constexpr size_t get_nth_bit(const size_t i,\n                                                   const size_t N) {\n  return (i >> N) % 2;\n}\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Compute the square of `x`\ntemplate <typename T>\n// NOLINTNEXTLINE(readability-const-return-type)\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) square(const T& x) {\n  return x * x;\n}\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Compute the cube of `x`\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) cube(const T& x) {\n  return x * x * x;\n}\n\n/*!\n * \\ingroup ConstantExpressionsGroup\n * \\brief Compute the falling factorial of \\f$(x)_{n}\\f$\n *\n * \\details The falling factorial \\f$(x_)_n = x (x - 1) \\ldots (x - n - 1) =\n * \\frac{x!}{(x-n)!}\\f$\n *\n * \\note The largest representable factorial is 20!. It is\n * up to the user to ensure this is satisfied\n */\nconstexpr uint64_t falling_factorial(const uint64_t x, const uint64_t n) {\n  // clang-tidy: don't warn about STL internals, I can't fix them\n  assert(n <= x);  // NOLINT\n  uint64_t r = 1;\n  for (uint64_t k = 0; k < n; ++k) {\n    r *= (x - k);\n  }\n  return r;\n}\n\n/*!\n * \\ingroup ConstantExpressionsGroup\n * \\brief Compute the factorial of \\f$n!\\f$\n */\nconstexpr uint64_t factorial(const uint64_t n) {\n  assert(n <= 20);  // NOLINT\n  return falling_factorial(n, n);\n}\n\n/*!\n * \\ingroup ConstantExpressionsGroup\n * \\brief Compute the binomial coefficient $\\binom{n}{k}$\n *\n * \\details The binomial coefficient is defined as\n * $\\binom{n}{k} = \\frac{n!}{k!(n-k)!}$\n */\nconstexpr uint64_t binomial(const uint64_t n, const uint64_t k) {\n  assert(n < 63);  // NOLINT\n  assert(k <= n);  // NOLINT\n  uint64_t result = 1;\n  for (uint64_t j = 1; j <= std::min(k, n - k); ++j) {\n    result *= n + 1 - j;\n    result /= j;\n  }\n  return result;\n}\n\n/// \\ingroup ConstantExpressionsGroup\nnamespace ConstantExpressions_detail {\n\n// Implementation functions for the pow template function below which computes\n// optimized powers where the exponent is a compile-time integer\n\n// base case power 0: Returns 1.0. This will need to be overloaded for types for\n// which the simple return is inappropriate.\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow_impl(\n    const T& /*t*/, std::integral_constant<int, 0> /*meta*/,\n    std::bool_constant<true> /*exponent_was_positive*/) {\n  return static_cast<tt::get_fundamental_type_t<T>>(1.0);\n}\n\n// special case power 1: acts as a direct identity function for efficiency\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow_impl(\n    const T& t, std::integral_constant<int, 1> /*meta*/,\n    std::bool_constant<true> /*exponent_was_positive*/) {\n  return t;\n}\n\n// general case for positive powers: return the power via recursive inline call,\n// which expands to a series of multiplication operations.\ntemplate <int N, typename T>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow_impl(\n    const T& t, std::integral_constant<int, N> /*meta*/,\n    std::bool_constant<true> /*exponent_was_positive*/) {\n  return t * pow_impl(t, std::integral_constant<int, N - 1>{},\n                      std::bool_constant<true>{});\n}\n\n// general case for negative powers: return the multiplicative inverse of the\n// result from the recursive inline call, which expands to a series of\n// multiplication operations. This assumes that division is supported with\n// tt::get_fundamental_type_t<T> and T. If not, this utility will need further\n// specialization.\ntemplate <int N, typename T>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow_impl(\n    const T& t, std::integral_constant<int, N> /*meta*/,\n    std::bool_constant<false> /*exponent_was_positive*/) {\n  return static_cast<tt::get_fundamental_type_t<T>>(1) /\n         (pow_impl(t, std::integral_constant<int, -N>{},\n                   std::bool_constant<true>{}));\n}\n}  // namespace ConstantExpressions_detail\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Compute t^N where N is an integer (positive or negative)\n///\n/// \\warning If passing an integer this will do integer arithmetic, e.g.\n/// `pow<-10>(2) == 0` evaluates to `true`\n///\n/// \\warning For optimization, it is assumed that the `pow<0>` of a vector type\n/// (e.g. `DataVector`) will not be used for initialization or directly indexed,\n/// so `pow<0>` returns simply `1.0`. In the case of use for initialization, a\n/// constructor should be used instead, and in the case of a direct index, the\n/// expression may be simplifyable to avoid the use of `pow<0>` altogether. If a\n/// more complete treatment of `pow<0>` is required, further overloads may be\n/// added to the `ConstantExpressions_detail` namespace.\n///\n/// \\tparam N the integer power being raised to in \\f$t^N\\f$\n/// \\param t the value being exponentiated\n/// \\return value \\f$t^N\\f$ determined via repeated multiplication\ntemplate <int N, typename T>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) pow(const T& t) {\n  return ConstantExpressions_detail::pow_impl(\n      t, std::integral_constant<int, N>{}, std::bool_constant<(N >= 0)>{});\n}\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Compute the absolute value of of its argument\n///\n/// The argument must be comparable to an int and must be negatable.\ntemplate <typename T, Requires<tt::is_integer_v<T> or\n                               std::is_floating_point_v<T>> = nullptr>\nSPECTRE_ALWAYS_INLINE constexpr T ce_abs(const T& x) {\n  return x < 0 ? -x : x;\n}\n\n/// \\cond\ntemplate <>\nSPECTRE_ALWAYS_INLINE constexpr double ce_abs(const double& x) {\n  return __builtin_fabs(x);\n}\n\ntemplate <>\nSPECTRE_ALWAYS_INLINE constexpr float ce_abs(const float& x) {\n  return __builtin_fabsf(x);\n}\n/// \\endcond\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Compute the absolute value of its argument\nconstexpr SPECTRE_ALWAYS_INLINE double ce_fabs(const double x) {\n  return ce_abs(x);\n}\n\nconstexpr SPECTRE_ALWAYS_INLINE float ce_fabs(const float x) {\n  return ce_abs(x);\n}\n\nnamespace ConstantExpressions_detail {\nstruct CompareByMagnitude {\n  template <typename T>\n  constexpr bool operator()(const T& a, const T& b) {\n    return ce_abs(a) < ce_abs(b);\n  }\n};\n}  // namespace ConstantExpressions_detail\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Return the argument with the largest magnitude\n///\n/// Magnitude is determined by constexpr_abs.  In case of a tie,\n/// returns the leftmost of the tied values.\n/// @{\ntemplate <typename T>\nconstexpr const T& max_by_magnitude(const T& a, const T& b) {\n  return std::max(a, b, ConstantExpressions_detail::CompareByMagnitude{});\n}\n\ntemplate <typename T>\nconstexpr T max_by_magnitude(std::initializer_list<T> ilist) {\n  return std::max(std::move(ilist),\n                  ConstantExpressions_detail::CompareByMagnitude{});\n}\n/// @}\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Return the argument with the smallest magnitude\n///\n/// Magnitude is determined by constexpr_abs.  In case of a tie,\n/// returns the leftmost of the tied values.\n/// @{\ntemplate <typename T>\nconstexpr const T& min_by_magnitude(const T& a, const T& b) {\n  return std::min(a, b, ConstantExpressions_detail::CompareByMagnitude{});\n}\n\ntemplate <typename T>\nconstexpr T min_by_magnitude(std::initializer_list<T> ilist) {\n  return std::min(std::move(ilist),\n                  ConstantExpressions_detail::CompareByMagnitude{});\n}\n/// @}\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Returns `f(ic<0>{}) + f(ic<1>{}) + ... + f(ic<NumTerms-1>{})`\n/// where `ic<N>` stands for `std::integral_constant<size_t, N>`.\n/// This function allows the result types of each operation to be\n/// different, and so works efficiently with expression templates.\n/// \\note When summing expression templates one must be careful of\n/// referring to temporaries in `f`.\ntemplate <size_t NumTerms, typename Function, Requires<NumTerms == 1> = nullptr>\nconstexpr decltype(auto) constexpr_sum(Function&& f) {\n  return f(std::integral_constant<size_t, 0>{});\n}\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <size_t NumTerms, typename Function,\n          Requires<(NumTerms > 1)> = nullptr>\nconstexpr decltype(auto) constexpr_sum(Function&& f) {\n  return constexpr_sum<NumTerms - 1>(f) +\n         f(std::integral_constant<size_t, NumTerms - 1>{});\n}\n/// \\endcond\n\nnamespace detail {\ntemplate <typename List, size_t... indices,\n          Requires<not tt::is_a_v<tmpl::list, tmpl::front<List>>> = nullptr>\ninline constexpr std::array<std::decay_t<decltype(tmpl::front<List>::value)>,\n                            tmpl::size<List>::value>\n    make_array_from_list_helper(\n        std::integer_sequence<size_t, indices...> /*meta*/) {\n  return std::array<std::decay_t<decltype(tmpl::front<List>::value)>,\n                    tmpl::size<List>::value>{\n      {tmpl::at<List, tmpl::size_t<indices>>::value...}};\n}\n}  // namespace detail\n\n/// \\ingroup ConstantExpressionsGroup\n/// Make an array from a typelist that holds std::integral_constant's all of\n/// which have the same `value_type`\n///\n/// \\tparam List the typelist of std::integral_constant's\n/// \\return array of integral values from the typelist\ntemplate <typename List,\n          Requires<not tt::is_a_v<tmpl::list, tmpl::front<List>>> = nullptr>\ninline constexpr auto make_array_from_list()\n    -> std::array<std::decay_t<decltype(tmpl::front<List>::value)>,\n                  tmpl::size<List>::value> {\n  return detail::make_array_from_list_helper<List>(\n      std::make_integer_sequence<size_t, tmpl::size<List>::value>{});\n}\n\ntemplate <typename TypeForZero,\n          Requires<not tt::is_a_v<tmpl::list, TypeForZero>> = nullptr>\ninline constexpr std::array<std::decay_t<TypeForZero>, 0>\nmake_array_from_list() {\n  return std::array<std::decay_t<TypeForZero>, 0>{{}};\n}\n\nnamespace detail {\n/// \\cond\ntemplate <typename List, size_t... indices,\n          Requires<tt::is_a<tmpl::list, tmpl::front<List>>::value> = nullptr>\ninline constexpr std::array<\n    std::decay_t<\n        decltype(make_array_from_list<tmpl::at<List, tmpl::size_t<0>>>())>,\n    tmpl::size<List>::value>\n    make_array_from_list_helper(\n        std::integer_sequence<size_t, indices...> /*unused*/) {\n  return std::array<std::decay_t<decltype(make_array_from_list<\n                                          tmpl::at<List, tmpl::size_t<0>>>())>,\n                    tmpl::size<List>::value>{\n      {make_array_from_list_helper<tmpl::at<List, tmpl::size_t<indices>>>(\n          std::make_integer_sequence<\n              size_t,\n              tmpl::size<tmpl::at<List, tmpl::size_t<indices>>>::value>{})...}};\n}\n/// \\endcond\n}  // namespace detail\n\n/// \\ingroup ConstantExpressionsGroup\n///\n/// Make an array of arrays from a typelist that holds typelists of\n/// std::integral_constant's all of which have the same `value_type`\n///\n/// \\tparam List the typelist of typelists of std::integral_constant's\n/// \\return array of arrays of integral values from the typelists\ntemplate <typename List,\n          Requires<tt::is_a_v<tmpl::list, tmpl::front<List>>> = nullptr>\ninline constexpr auto make_array_from_list() {\n  return detail::make_array_from_list_helper<List>(\n      std::make_integer_sequence<size_t, tmpl::size<List>::value>{});\n}\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Compute the length of a const char* at compile time\nSPECTRE_ALWAYS_INLINE constexpr size_t cstring_length(const char* str) {\n  // clang-tidy: do not use pointer arithmetic\n  return *str != 0 ? 1 + cstring_length(str + 1) : 0;  // NOLINT\n}\n\n/// \\ingroup ConstantExpressionsGroup\n/// \\brief Compute a hash of a const char* at compile time\nSPECTRE_ALWAYS_INLINE constexpr size_t cstring_hash(const char* str) {\n  // clang-tidy: do not use pointer arithmetic\n  return *str != 0\n             ? (cstring_hash(str + 1) * 33) ^  // NOLINT\n                   static_cast<size_t>(*str)\n             : 5381;\n}\n\nnamespace ConstantExpression_detail {\ntemplate <typename T, size_t Size, size_t... I, size_t... J>\ninline constexpr std::array<std::decay_t<T>, Size> replace_at_helper(\n    const std::array<T, Size>& arr, const T& value, const size_t i,\n    std::integer_sequence<size_t, I...> /*unused*/,\n    std::integer_sequence<size_t, J...> /*unused*/) {\n  // clang-tidy: Cannot use gsl::at because we want constexpr evaluation and\n  // Parallel::abort violates this\n  return std::array<std::decay_t<T>, Size>{\n      {arr[I]..., value, arr[i + J]...}};  // NOLINT\n}\n}  // namespace ConstantExpression_detail\n\n/// \\ingroup ConstantExpressionsGroup\n/// Replace at compile time the `I`th entry in the array with `value`\ntemplate <size_t I, typename T, size_t Size>\ninline constexpr std::array<std::decay_t<T>, Size> replace_at(\n    const std::array<T, Size>& arr, T value) {\n  return ConstantExpression_detail::replace_at_helper(\n      arr, std::forward<T>(value), I + 1,\n      std::make_integer_sequence<size_t, I>{},\n      std::make_integer_sequence<size_t, Size - I - 1>{});\n}\n\n/// \\ingroup ConstantExpressionsGroup\n/// Check at compile time if two `std::array`s are equal\ntemplate <typename T, typename S, size_t size>\ninline constexpr bool array_equal(const std::array<T, size>& lhs,\n                                  const std::array<S, size>& rhs,\n                                  const size_t i = 0) {\n  // clang-tidy: Cannot use gsl::at because we want constexpr evaluation and\n  // Parallel::abort violates this\n  return i < size ? (lhs[i] == rhs[i]  // NOLINT\n                     and array_equal(lhs, rhs, i + 1))\n                  : true;\n}\n"
  },
  {
    "path": "src/Utilities/ContainerHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <type_traits>\n\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/IsComplexOfFundamental.hpp\"\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Callable struct which retrieves the `t.size()` for operand `t`. This\n/// will cause a compiler error if no such function exists.\nstruct GetContainerSize {\n  template <typename T>\n  SPECTRE_ALWAYS_INLINE constexpr decltype(auto) operator()(const T& t) const {\n    return t.size();\n  }\n};\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Callable struct for the subscript operator. Returns `t[i]`\nstruct GetContainerElement {\n  template <typename T>\n  SPECTRE_ALWAYS_INLINE constexpr decltype(auto) operator()(\n      T& t, const size_t i) const {\n    return t[i];\n  }\n};\n\nnamespace ContainerHelpers_detail {\n// implementation struct for get_element and get_size\ntemplate <bool IsFundamentalOrComplexOfFundamental>\nstruct ContainerImpls;\n\ntemplate <>\nstruct ContainerImpls<true> {\n  template <typename T, typename SubscriptFunction>\n  static SPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_element(\n      T& t, const size_t /*i*/, const SubscriptFunction /*at*/) {\n    return t;\n  }\n\n  template <typename T, typename SizeFunction>\n  static SPECTRE_ALWAYS_INLINE constexpr size_t get_size(\n      const T& /*t*/, const SizeFunction /*size*/) {\n    return 1;\n  }\n};\n\ntemplate <>\nstruct ContainerImpls<false> {\n  template <typename T, typename SubscriptFunction>\n  static SPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_element(\n      T& t, const size_t i, SubscriptFunction at) {\n    return at(t, i);\n  }\n\n  template <typename T, typename SizeFunction>\n  static SPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_size(\n      const T& t, SizeFunction size) {\n    return size(t);\n  }\n};\n}  // namespace ContainerHelpers_detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Returns the `i`th element if `T` has a subscript operator, otherwise\n * if `T` is fundamental or a `std::complex` of a fundamental type,  returns\n * `t`.\n *\n * \\details This function also optionally takes the user-defined subscript\n * function `at`, which can be used to specify a custom indexing function. For\n * instance, for a type which is a `std::array` of a `std::array`s, the indexing\n * function could be the below callable struct:\n * \\snippet Test_ContainerHelpers.cpp get_element_example_indexing_callable\n *\n * which would index the data structure in a manner in which the outer array\n * index varies fastest. The indexing function must take as arguments the\n * applicable container and a `size_t` index, in that order. This follows the\n * convention of `gsl::at`.\n * \\note `std::complex` are regarded as non-indexable (despite a predictable\n * memory layout), so this function acts as the identity on `std::complex` of\n * fundamental types\n */\ntemplate <typename T, typename SubscriptFunction = GetContainerElement>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_element(\n    T& t, const size_t i, SubscriptFunction at = GetContainerElement{}) {\n  return ContainerHelpers_detail::ContainerImpls<(\n      tt::is_complex_of_fundamental_v<std::remove_cv_t<T>> or\n      std::is_fundamental_v<std::remove_cv_t<T>>)>::get_element(t, i, at);\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Retrieve the size of `t` if `t.size()` is a valid expression,\n * otherwise if `T` is fundamental or a `std::complex` of a fundamental type,\n * returns 1.\n *\n * \\details This function also optionally takes the user-defined `size`\n * function, which can be used to specify a custom size function. For\n * instance, for a type which is a `std::array` of a `std::array`, the size\n * function could be the below callable struct:\n * \\snippet Test_ContainerHelpers.cpp get_size_example_size_callable\n *\n * The `size` function must take the single argument of the applicable\n * container, and should return a `size_t`. This follows the convention of\n * `std::size()` as of C++17.\n * \\note `std::complex` are regarded as non-indexable (despite a predictable\n * memory layout), so this function will return 1 for a `std::complex` of a\n * fundamental type\n */\ntemplate <typename T, typename SizeFunction = GetContainerSize>\nSPECTRE_ALWAYS_INLINE constexpr decltype(auto) get_size(\n    const T& t, SizeFunction size = GetContainerSize{}) {\n  return ContainerHelpers_detail::ContainerImpls<(\n      tt::is_complex_of_fundamental_v<std::remove_cv_t<T>> or\n      std::is_fundamental_v<std::remove_cv_t<T>>)>::get_size(t, size);\n}\n\n/// Fall-back that allows using `min(x)` where `x` can be a vector or a double\nSPECTRE_ALWAYS_INLINE constexpr double min(const double val) { return val; }\n\n/// Fall-back that allows using `max(x)` where `x` can be a vector or a double\nSPECTRE_ALWAYS_INLINE constexpr double max(const double val) { return val; }\n"
  },
  {
    "path": "src/Utilities/ContainsAllocations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n\n/// Check whether an object contains memory allocations.  Classes can\n/// add overloads as needed.\n/// @{\ninline bool contains_allocations(const double /*value*/) { return false; }\ninline bool contains_allocations(const std::complex<double> /*value*/) {\n  return false;\n}\n/// @}\n"
  },
  {
    "path": "src/Utilities/DereferenceWrapper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function dereference_wrapper\n\n#pragma once\n\n#include <complex>\n#include <functional>\n#include <utility>\n\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Returns the reference object held by a reference wrapper, if a\n/// non-reference_wrapper type is passed in then the object is returned\ntemplate <typename T>\ndecltype(auto) dereference_wrapper(T&& t) {\n  return std::forward<T>(t);\n}\n\n/// \\cond\ntemplate <typename T>\nT& dereference_wrapper(const std::reference_wrapper<T>& t) {\n  return t.get();\n}\ntemplate <typename T>\nT& dereference_wrapper(std::reference_wrapper<T>& t) {\n  return t.get();\n}\ntemplate <typename T>\nT&& dereference_wrapper(const std::reference_wrapper<T>&& t) {\n  return t.get();\n}\ntemplate <typename T>\nT&& dereference_wrapper(std::reference_wrapper<T>&& t) {\n  return t.get();\n}\n/// \\endcond\n\n/// \\cond\n// Add overloads of math functions for reference_wrapper.\n// This is necessary because if a class, say DataVector, inherits from\n// blaze vector types and does not specify the math operators specifically for\n// DataVector then the implicit cast from reference_wrapper<DataVector> to\n// DataVector does not result in finding the math operators.\n//\n// We use forwarding references to resolve ambiguity errors with\n// DVecScalarMultExpr and DVecScalarDivExpr, with a std::reference_wrapper\n// The forwarding references match everything perfectly and so the functions\n// here will (almost) always win in overload selection.\n#define UNARY_REF_WRAP_OP(OP)               \\\n  template <typename T>                     \\\n  SPECTRE_ALWAYS_INLINE decltype(auto) OP(  \\\n      const std::reference_wrapper<T>& t) { \\\n    return OP(t.get());                     \\\n  }\n#define BINARY_REF_WRAP_FUNCTION_OP(OP)                                    \\\n  template <                                                               \\\n      typename T0, typename T1,                                            \\\n      Requires<not tt::is_a_v<std::reference_wrapper, std::decay_t<T1>>> = \\\n          nullptr>                                                         \\\n  SPECTRE_ALWAYS_INLINE decltype(auto) OP(                                 \\\n      const std::reference_wrapper<T0>& t0, T1&& t1) {                     \\\n    return OP(t0.get(), t1);                                               \\\n  }                                                                        \\\n  template <                                                               \\\n      typename T0, typename T1,                                            \\\n      Requires<not tt::is_a_v<std::reference_wrapper, std::decay_t<T0>>> = \\\n          nullptr>                                                         \\\n  SPECTRE_ALWAYS_INLINE decltype(auto) OP(                                 \\\n      T0&& t0, const std::reference_wrapper<T1>& t1) {                     \\\n    return OP(t0, t1.get());                                               \\\n  }                                                                        \\\n  template <typename T0, typename T1>                                      \\\n  SPECTRE_ALWAYS_INLINE decltype(auto) OP(                                 \\\n      const std::reference_wrapper<T0>& t0,                                \\\n      const std::reference_wrapper<T1>& t1) {                              \\\n    return OP(t0.get(), t1.get());                                         \\\n  }\n\n#define BINARY_REF_WRAP_OP(OP)                                             \\\n  template <                                                               \\\n      typename T0, typename T1,                                            \\\n      Requires<not tt::is_a_v<std::reference_wrapper, std::decay_t<T1>>> = \\\n          nullptr>                                                         \\\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator OP(                        \\\n      const std::reference_wrapper<T0>& t0, T1&& t1) {                     \\\n    return t0.get() OP t1;                                                 \\\n  }                                                                        \\\n  template <                                                               \\\n      typename T0, typename T1,                                            \\\n      Requires<not tt::is_a_v<std::reference_wrapper, std::decay_t<T0>>> = \\\n          nullptr>                                                         \\\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator OP(                        \\\n      T0&& t0, const std::reference_wrapper<T1>& t1) {                     \\\n    return t0 OP t1.get();                                                 \\\n  }                                                                        \\\n  template <typename T0, typename T1>                                      \\\n  SPECTRE_ALWAYS_INLINE decltype(auto) operator OP(                        \\\n      const std::reference_wrapper<T0>& t0,                                \\\n      const std::reference_wrapper<T1>& t1) {                              \\\n    return t0.get() OP t1.get();                                           \\\n  }\n\nUNARY_REF_WRAP_OP(abs)\nUNARY_REF_WRAP_OP(acos)\nUNARY_REF_WRAP_OP(acosh)\nUNARY_REF_WRAP_OP(asin)\nUNARY_REF_WRAP_OP(asinh)\nUNARY_REF_WRAP_OP(atan)\nBINARY_REF_WRAP_FUNCTION_OP(atan2)\nUNARY_REF_WRAP_OP(atanh)\nUNARY_REF_WRAP_OP(cbrt)\nUNARY_REF_WRAP_OP(conj)\nUNARY_REF_WRAP_OP(cos)\nUNARY_REF_WRAP_OP(cosh)\nUNARY_REF_WRAP_OP(erf)\nUNARY_REF_WRAP_OP(erfc)\nUNARY_REF_WRAP_OP(exp)\nUNARY_REF_WRAP_OP(exp2)\nUNARY_REF_WRAP_OP(exp10)\nUNARY_REF_WRAP_OP(fabs)\nBINARY_REF_WRAP_FUNCTION_OP(hypot)\nUNARY_REF_WRAP_OP(imag)\nUNARY_REF_WRAP_OP(invcbrt)\nUNARY_REF_WRAP_OP(invsqrt)\nUNARY_REF_WRAP_OP(log)\nUNARY_REF_WRAP_OP(log2)\nUNARY_REF_WRAP_OP(log10)\nUNARY_REF_WRAP_OP(max)\nUNARY_REF_WRAP_OP(min)\nBINARY_REF_WRAP_FUNCTION_OP(pow)\nUNARY_REF_WRAP_OP(real)\nUNARY_REF_WRAP_OP(sin)\nUNARY_REF_WRAP_OP(sinh)\nUNARY_REF_WRAP_OP(sqrt)\nUNARY_REF_WRAP_OP(step_function)\nUNARY_REF_WRAP_OP(tan)\nUNARY_REF_WRAP_OP(tanh)\n\nBINARY_REF_WRAP_OP(+)\nBINARY_REF_WRAP_OP(-)\nBINARY_REF_WRAP_OP(*)\nBINARY_REF_WRAP_OP(/)\nBINARY_REF_WRAP_OP(==)\n\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE decltype(auto) operator-(\n    const std::reference_wrapper<T>& t) {\n  return -t.get();\n}\n\n#undef UNARY_REF_WRAP_OP\n#undef BINARY_REF_WRAP_OP\n#undef BINARY_REF_WRAP_FUNCTION_OP\n/// \\endcond\n"
  },
  {
    "path": "src/Utilities/EqualWithinRoundoff.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <limits>\n#include <type_traits>\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/TypeTraits/IsIterable.hpp\"\n#include \"Utilities/TypeTraits/IsMaplike.hpp\"\n\nnamespace EqualWithinRoundoffImpls {\n/*!\n * \\brief Specialize this class to add support for the `equal_within_roundoff`\n * function.\n *\n * Ensure the `Lhs` and `Rhs` are symmetric. A specialization must implement a\n * static `apply` function with this signature:\n *\n * ```cpp\n * static bool apply(const Lhs& lhs, const Rhs& rhs, const double eps,\n *                   const double scale);\n * ```\n *\n * It can be helpful to invoke the `equal_within_roundoff` function for floating\n * points from within your specialization.\n */\ntemplate <typename Lhs, typename Rhs, typename = std::nullptr_t>\nstruct EqualWithinRoundoffImpl;\n}  // namespace EqualWithinRoundoffImpls\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Checks if two values `lhs` and `rhs` are equal within roundoff, by\n * comparing `abs(lhs - rhs) < (max(abs(lhs), abs(rhs)) + scale) * eps`.\n *\n * The two values can be floating-point numbers, or any types for which\n * `EqualWithinRoundoffImpls::EqualWithinRoundoffImpl` has been specialized. For\n * example, a default implementation exists for the case where `lhs`, `rhs`, or\n * both, are iterable, and compares the values point-wise.\n */\ntemplate <typename Lhs, typename Rhs>\nconstexpr SPECTRE_ALWAYS_INLINE bool equal_within_roundoff(\n    const Lhs& lhs, const Rhs& rhs,\n    const double eps = std::numeric_limits<double>::epsilon() * 100.0,\n    const double scale = 1.0) {\n  return EqualWithinRoundoffImpls::EqualWithinRoundoffImpl<Lhs, Rhs>::apply(\n      lhs, rhs, eps, scale);\n}\n\n/// Specializations of `EqualWithinRoundoffImpl` for custom types, to add\n/// support for the `equal_within_roundoff` function.\nnamespace EqualWithinRoundoffImpls {\n\n// Compare two floating points\ntemplate <typename Floating>\nstruct EqualWithinRoundoffImpl<Floating, Floating,\n                               Requires<std::is_floating_point_v<Floating>>> {\n  static constexpr SPECTRE_ALWAYS_INLINE bool apply(const Floating& lhs,\n                                                    const Floating& rhs,\n                                                    const double eps,\n                                                    const double scale) {\n    return ce_fabs(lhs - rhs) <=\n           (std::max(ce_fabs(lhs), ce_fabs(rhs)) + scale) * eps;\n  }\n};\n\n// Compare a complex number to a floating point, interpreting the latter as a\n// real number\ntemplate <typename Floating>\nstruct EqualWithinRoundoffImpl<std::complex<Floating>, Floating,\n                               Requires<std::is_floating_point_v<Floating>>> {\n  static SPECTRE_ALWAYS_INLINE bool apply(const std::complex<Floating>& lhs,\n                                          const Floating& rhs, const double eps,\n                                          const double scale) {\n    return equal_within_roundoff(lhs.real(), rhs, eps, scale) and\n           equal_within_roundoff(lhs.imag(), 0., eps, scale);\n  }\n};\n\n// Compare a floating point to a complex number, interpreting the former as a\n// real number\ntemplate <typename Floating>\nstruct EqualWithinRoundoffImpl<Floating, std::complex<Floating>,\n                               Requires<std::is_floating_point_v<Floating>>> {\n  static SPECTRE_ALWAYS_INLINE bool apply(const std::complex<Floating>& lhs,\n                                          const Floating& rhs, const double eps,\n                                          const double scale) {\n    return equal_within_roundoff(rhs, lhs, eps, scale);\n  }\n};\n\n// Compare two complex numbers\ntemplate <typename Floating>\nstruct EqualWithinRoundoffImpl<std::complex<Floating>, std::complex<Floating>,\n                               Requires<std::is_floating_point_v<Floating>>> {\n  static SPECTRE_ALWAYS_INLINE bool apply(const std::complex<Floating>& lhs,\n                                          const std::complex<Floating>& rhs,\n                                          const double eps,\n                                          const double scale) {\n    return equal_within_roundoff(lhs.real(), rhs.real(), eps, scale) and\n           equal_within_roundoff(lhs.imag(), rhs.imag(), eps, scale);\n  }\n};\n\n// Compare an iterable to a floating point\ntemplate <typename Lhs, typename Rhs>\nstruct EqualWithinRoundoffImpl<\n    Lhs, Rhs,\n    Requires<tt::is_iterable_v<Lhs> and not tt::is_maplike_v<Lhs> and\n             std::is_floating_point_v<Rhs>>> {\n  static SPECTRE_ALWAYS_INLINE bool apply(const Lhs& lhs, const Rhs& rhs,\n                                          const double eps,\n                                          const double scale) {\n    return alg::all_of(lhs, [&rhs, &eps, &scale](const auto& lhs_element) {\n      return equal_within_roundoff(lhs_element, rhs, eps, scale);\n    });\n  }\n};\n\n// Compare a floating point to an iterable\ntemplate <typename Lhs, typename Rhs>\nstruct EqualWithinRoundoffImpl<\n    Lhs, Rhs,\n    Requires<tt::is_iterable_v<Rhs> and not tt::is_maplike_v<Rhs> and\n             std::is_floating_point_v<Lhs>>> {\n  static SPECTRE_ALWAYS_INLINE bool apply(const Lhs& lhs, const Rhs& rhs,\n                                          const double eps,\n                                          const double scale) {\n    return equal_within_roundoff(rhs, lhs, eps, scale);\n  }\n};\n\n// Compare two iterables\ntemplate <typename Lhs, typename Rhs>\nstruct EqualWithinRoundoffImpl<\n    Lhs, Rhs,\n    Requires<tt::is_iterable_v<Lhs> and not tt::is_maplike_v<Lhs> and\n             tt::is_iterable_v<Rhs> and not tt::is_maplike_v<Rhs>>> {\n  static bool apply(const Lhs& lhs, const Rhs& rhs, const double eps,\n                    const double scale) {\n    auto lhs_it = lhs.begin();\n    auto rhs_it = rhs.begin();\n    while (lhs_it != lhs.end() and rhs_it != rhs.end()) {\n      if (not equal_within_roundoff(*lhs_it, *rhs_it, eps, scale)) {\n        return false;\n      }\n      ++lhs_it;\n      ++rhs_it;\n    }\n    ASSERT(lhs_it == lhs.end() and rhs_it == rhs.end(),\n           \"Can't compare lhs and rhs because they have different lengths.\");\n    return true;\n  }\n};\n\n}  // namespace EqualWithinRoundoffImpls\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/AbortWithErrorMessage.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/ErrorHandling/AbortWithErrorMessage.hpp\"\n\n#include <boost/stacktrace.hpp>\n#include <charm++.h>\n#include <cstdlib>\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <stdexcept>\n\n#include \"Utilities/ErrorHandling/Breakpoint.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/ErrorHandling/FormatStacktrace.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace {\ntemplate <bool ShowTrace, typename ExceptionTypeToThrow>\n[[noreturn]] void abort_with_error_message_impl(const char* file,\n                                                const int line,\n                                                const char* pretty_function,\n                                                const std::string& message) {\n  std::ostringstream os;\n  os.precision(std::numeric_limits<double>::max_digits10 - 1);\n  os << \"\\n\"\n     << \"############ ERROR ############\\n\";\n  if constexpr (ShowTrace) {\n    os << \"Stack trace:\\n\\n\" << boost::stacktrace::stacktrace() << \"\\n\";\n  }\n  os << \"Wall time: \" << sys::pretty_wall_time() << \"\\n\"\n     << \"Node: \" << sys::my_node() << \" Proc: \" << sys::my_proc() << \"\\n\"\n     << abbreviated_symbol_name(std::string{pretty_function}) << \" in \" << file\n     << \":\" << line << \"\\n\"\n     << \"\\n\"\n     << message << \"\\n\";\n  print_captures_for_error(os);\n  os << \"############ ERROR ############\\n\"\n     << \"\\n\";\n  breakpoint();\n  throw ExceptionTypeToThrow(os.str());\n}\n}  // namespace\n\nvoid abort_with_error_message(const char* expression, const char* file,\n                              const int line, const char* pretty_function,\n                              const std::string& message) {\n  std::ostringstream os;\n  os.precision(std::numeric_limits<double>::max_digits10 - 1);\n  os << \"\\n\"\n     << \"############ ASSERT FAILED ############\\n\"\n     << \"Stack trace:\\n\\n\"\n     << boost::stacktrace::stacktrace() << \"\\n\"\n     << \"Wall time: \" << sys::pretty_wall_time() << \"\\n\"\n     << \"Node: \" << sys::my_node() << \" Proc: \" << sys::my_proc() << \"\\n\"\n     << abbreviated_symbol_name(std::string{pretty_function}) << \" in \" << file\n     << \":\" << line << \"\\n\"\n     << \"\\n\"\n     << \"'\" << expression << \"' violated!\\n\"\n     << message << \"\\n\";\n  print_captures_for_error(os);\n  os << \"############ ASSERT FAILED ############\\n\"\n     << \"\\n\";\n  breakpoint();\n  throw SpectreAssert(os.str());\n}\n\ntemplate <typename ExceptionTypeToThrow>\nvoid abort_with_error_message(const char* file, const int line,\n                              const char* pretty_function,\n                              const std::string& message) {\n  abort_with_error_message_impl<true, ExceptionTypeToThrow>(\n      file, line, pretty_function, message);\n}\n\nvoid abort_with_error_message_no_trace(const char* file, const int line,\n                                       const char* pretty_function,\n                                       const std::string& message) {\n  abort_with_error_message_impl<false, SpectreError>(file, line,\n                                                     pretty_function, message);\n}\n\n#define GET_EX_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                       \\\n  template void abort_with_error_message<GET_EX_TYPE(data)>(         \\\n      const char* file, const int line, const char* pretty_function, \\\n      const std::string& message);\n\n// STL exceptions first, group as on\n// https://en.cppreference.com/w/cpp/error/exception\n//\n// Then SpECTRE exception classes.\nGENERATE_INSTANTIATIONS(\n    INSTANTIATION, (std::logic_error, std::invalid_argument, std::domain_error,\n                    std::length_error, std::out_of_range,\n\n                    std::runtime_error, std::range_error, std::overflow_error,\n                    std::underflow_error,\n\n                    std::ios_base::failure,\n\n                    SpectreError, SpectreAssert, SpectreFpe, convergence_error))\n\n#undef INSTANTIATION\n#undef GET_EX_TYPE\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/AbortWithErrorMessage.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares function abort_with_error_message\n\n#pragma once\n\n#include <string>\n\n/// \\ingroup ErrorHandlingGroup\n/// Compose an error message with an expression and a backtrace, then abort the\n/// program.\n///\n/// We try to demangle and format the backtrace. Long symbol names are\n/// abbreviated, unless you set the `SPECTRE_SHOW_FULL_BACKTRACE_SYMBOLS`\n/// environment variable to a non-empty value (e.g. \"1\").\n[[noreturn]] void abort_with_error_message(const char* expression,\n                                           const char* file, int line,\n                                           const char* pretty_function,\n                                           const std::string& message);\n\n/// \\ingroup ErrorHandlingGroup\n/// Compose an error message including a backtrace and abort the program.\n///\n/// We try to demangle and format the backtrace. Long symbol names are\n/// abbreviated, unless you set the `SPECTRE_SHOW_FULL_BACKTRACE_SYMBOLS`\n/// environment variable to a non-empty value (e.g. \"1\").\ntemplate <typename ExceptionTypeToThrow>\n[[noreturn]] void abort_with_error_message(const char* file, int line,\n                                           const char* pretty_function,\n                                           const std::string& message);\n\n/// \\ingroup ErrorHandlingGroup\n/// Compose an error message without a backtrace and abort the program.\n///\n/// \\note This always throws `SpectreError` as the exception type.\n[[noreturn]] void abort_with_error_message_no_trace(const char* file, int line,\n                                                    const char* pretty_function,\n                                                    const std::string& message);\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/Assert.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines macro ASSERT.\n\n#pragma once\n\n#include <iomanip>\n#include <sstream>\n#include <string>\n\n#include \"Utilities/ErrorHandling/AbortWithErrorMessage.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n\n/*!\n * \\ingroup ErrorHandlingGroup\n * \\brief Assert that an expression should be true.\n *\n * If the preprocessor macro SPECTRE_DEBUG is defined and the expression is\n * false, an error message is printed to the standard error stream, and the\n * program aborts. ASSERT should be used to catch coding errors as it does\n * nothing in production code.\n * \\param a the expression that must be true\n * \\param m the error message as an ostream\n */\n#ifdef SPECTRE_DEBUG\n// isocpp.org recommends using an `if (true)` instead of a `do\n// while(false)` for macros because the latter can mess with inlining\n// in some (old?) compilers:\n// https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-multi-stmts\n// https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-if\n// However, Intel's reachability analyzer (as of version 16.0.3\n// 20160415) can't figure out that the else branch and everything\n// after it is unreachable, causing warnings (and possibly suboptimal\n// code generation).\n#define ASSERT(a, m)                                                           \\\n  do {                                                                         \\\n    if (!(a)) { /* NOLINT(readability-simplify-boolean-expr) */                \\\n      const ScopedFpeState disable_fpes_ASSERT(false);                         \\\n      std::ostringstream avoid_name_collisions_ASSERT;                         \\\n      /* clang-tidy: macro arg in parentheses */                               \\\n      avoid_name_collisions_ASSERT << std::setprecision(18) << std::scientific \\\n                                   << m; /* NOLINT */                          \\\n      abort_with_error_message(#a, __FILE__, __LINE__,                         \\\n                               static_cast<const char*>(__PRETTY_FUNCTION__),  \\\n                               avoid_name_collisions_ASSERT.str());            \\\n    }                                                                          \\\n  } while (false)\n#else\n#define ASSERT(a, m)                                                           \\\n  do {                                                                         \\\n    if (false) {                                                               \\\n      static_cast<void>(a);                                                    \\\n      const ScopedFpeState disable_fpes_ASSERT(false);                         \\\n      std::ostringstream avoid_name_collisions_ASSERT;                         \\\n      /* clang-tidy: macro arg in parentheses */                               \\\n      avoid_name_collisions_ASSERT << std::setprecision(18) << std::scientific \\\n                                   << m; /* NOLINT */                          \\\n      static_cast<void>(avoid_name_collisions_ASSERT);                         \\\n    }                                                                          \\\n  } while (false)\n#endif\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/Breakpoint.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/ErrorHandling/Breakpoint.hpp\"\n\n#include <csignal>\n\nvoid breakpoint() {\n  // We send ourselves a SIGTRAP and ignore it.  If we're not being\n  // traced (e.g. being run in a debugger), that doesn't do much, but if we are\n  // then the tracer (debugger) can see the signal delivery.  We don't reset the\n  // signal handler afterwards in case this is called on multiple threads; we\n  // don't want one thread reenabling the default handler just before another\n  // calls raise().\n\n  struct sigaction handler {};\n  handler.sa_handler = SIG_IGN;  // NOLINT\n  handler.sa_flags = 0;\n  sigemptyset(&handler.sa_mask);\n  sigaction(SIGTRAP, &handler, nullptr);\n  raise(SIGTRAP);\n}\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/Breakpoint.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief Raise `SIGTRAP` so that debuggers will trap.\n */\nvoid breakpoint();\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY ErrorHandling)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  AbortWithErrorMessage.cpp\n  Breakpoint.cpp\n  CaptureForError.cpp\n  FloatingPointExceptions.cpp\n  FormatStacktrace.cpp\n  SegfaultHandler.cpp\n  Strerror.cpp\n  StrerrorWrapper.c\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  AbortWithErrorMessage.hpp\n  Assert.hpp\n  Breakpoint.hpp\n  CaptureForError.hpp\n  Error.hpp\n  Exceptions.hpp\n  ExpectsAndEnsures.hpp\n  FloatingPointExceptions.hpp\n  FormatStacktrace.hpp\n  SegfaultHandler.hpp\n  StaticAssert.hpp\n  Strerror.hpp\n  StrerrorWrapper.h\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  Charmxx::charmxx\n  PUBLIC\n  SystemUtilities\n  )\n\nset_source_files_properties(\n  StrerrorWrapper.c\n  PROPERTIES\n  SKIP_PRECOMPILE_HEADERS ON\n  )\n\n# Try to use libbacktrace for boost::stacktrace so it displays line numbers.\n# The build configuration is explained here:\n# https://www.boost.org/doc/libs/1_78_0/doc/html/stacktrace/configuration_and_build.html\n# Note that we don't try to use Boost's addr2line mode because it takes a long\n# time to run and doesn't work well:\n# https://github.com/boostorg/stacktrace/issues/97\n# On some systems (Debian) libbacktrace comes preinstalled with GCC (see notes\n# in the Boost docs linked above).\nfind_library(\n  BACKTRACE_LIB\n  NAMES backtrace\n  PATH_SUFFIXES lib64 lib\n  HINTS ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})\nfind_path(\n  BACKTRACE_HEADER_DIR\n  NAMES backtrace.h\n  PATH_SUFFIXES include\n  HINTS ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})\nif (NOT BACKTRACE_LIB STREQUAL \"BACKTRACE_LIB-NOTFOUND\"\n    AND NOT BACKTRACE_HEADER_DIR STREQUAL \"BACKTRACE_HEADER_DIR-NOTFOUND\")\n  message(STATUS \"Using libbacktrace for stack traces: ${BACKTRACE_LIB}\")\n  set(BACKTRACE_HEADER \"<${BACKTRACE_HEADER_DIR}/backtrace.h>\")\n  target_compile_definitions(${LIBRARY} PRIVATE\n    BOOST_STACKTRACE_USE_BACKTRACE\n    BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE=${BACKTRACE_HEADER})\n  target_link_libraries(${LIBRARY} PRIVATE ${BACKTRACE_LIB})\nelse()\n  # Fall back to \"basic\" mode. It needs no configuration because it's Boost's\n  # default.\n  message(STATUS \"Using basic mode for stack traces. Install libbacktrace \"\n    \"(https://github.com/ianlancetaylor/libbacktrace) to obtain more useful \"\n    \"traces in errors and asserts.\")\nendif()\n# All modes use libdl (see https://www.boost.org/doc/libs/1_78_0/doc/html/stacktrace/configuration_and_build.html)\n# CMake provides the correct way to link with libdl.\ntarget_link_libraries(${LIBRARY} PRIVATE ${CMAKE_DL_LIBS})\n\n# Boost::stacktrace needs the `_Unwind_Backtrace` function to be available.\n# On macOS it is available without defining `_GNU_SOURCE`, so Boost wants us\n# to define `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED`. Otherwise we get a\n# compiler error telling us to define this.\nif (APPLE)\n  target_compile_definitions(${LIBRARY} PRIVATE\n    BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED)\nendif()\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/CaptureForError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n\n#include <ostream>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace CaptureForError_detail {\nCaptureList& CaptureList::the_list() {\n  thread_local CaptureList singleton{};\n  return singleton;\n}\n\nvoid CaptureList::register_self(const CaptureForErrorBase* const capture) {\n  captures_.push_back(capture);\n}\n\nvoid CaptureList::deregister_self(const CaptureForErrorBase* const capture) {\n  ASSERT(not captures_.empty() and captures_.back() == capture,\n         \"Deregistering wrong capture.\");\n  captures_.pop_back();\n}\n\nvoid CaptureList::print(std::ostream& stream) {\n  if (currently_printing_) {\n    // Error during an error.  Don't recurse.\n    return;\n  }\n  try {\n    currently_printing_ = true;\n    if (not captures_.empty()) {\n      stream << \"\\nCaptured variables:\\n\";\n    }\n    for (const auto& capture : captures_) {\n      capture->print(stream);\n    }\n  } catch (...) {\n    currently_printing_ = false;\n    throw;\n  }\n  currently_printing_ = false;\n}\n\nCaptureList::CaptureList() = default;\n\nCaptureList::~CaptureList() noexcept(false) {\n  ASSERT(captures_.empty(), \"Not all captures deregistered.\");\n}\n}  // namespace CaptureForError_detail\n\nvoid print_captures_for_error(std::ostream& stream) {\n  CaptureForError_detail::CaptureList::the_list().print(stream);\n}\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/CaptureForError.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n#include <vector>\n\nnamespace CaptureForError_detail {\nclass CaptureForErrorBase {\n protected:\n  CaptureForErrorBase() = default;\n  ~CaptureForErrorBase() = default;\n\n public:\n  CaptureForErrorBase(CaptureForErrorBase&&) = delete;\n  CaptureForErrorBase(const CaptureForErrorBase&) = delete;\n  CaptureForErrorBase& operator=(CaptureForErrorBase&&) = delete;\n  CaptureForErrorBase& operator=(const CaptureForErrorBase&) = delete;\n\n  virtual void print(std::ostream& stream) const = 0;\n};\n\nclass CaptureList {\n public:\n  static CaptureList& the_list();\n\n  void register_self(const CaptureForErrorBase* capture);\n  void deregister_self(const CaptureForErrorBase* capture);\n\n  void print(std::ostream& stream);\n\n  CaptureList(CaptureList&&) = delete;\n  CaptureList(const CaptureList&) = delete;\n  CaptureList& operator=(CaptureList&&) = delete;\n  CaptureList& operator=(const CaptureList&) = delete;\n\n private:\n  CaptureList();\n  ~CaptureList() noexcept(false);\n\n  std::vector<const CaptureForErrorBase*> captures_{};\n  bool currently_printing_ = false;\n};\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wnon-virtual-dtor\"\ntemplate <typename T>\n// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor)\nclass CaptureForError : public CaptureForErrorBase {\n public:\n  CaptureForError() = delete;\n  CaptureForError(CaptureForError&&) = delete;\n  CaptureForError(const CaptureForError&) = delete;\n  CaptureForError& operator=(CaptureForError&&) = delete;\n  CaptureForError& operator=(const CaptureForError&) = delete;\n\n  CaptureForError(const char* const name, const T& capture)\n      : name_(name), capture_(capture) {\n    CaptureList::the_list().register_self(this);\n  }\n\n  // Capturing an rvalue would lead to a dangling reference.\n  CaptureForError(const char* /*name*/, const T&& /*capture*/) = delete;\n\n  ~CaptureForError() noexcept(false) {\n    CaptureList::the_list().deregister_self(this);\n  }\n\n  void print(std::ostream& stream) const override {\n    stream << name_ << \" = \" << capture_ << \"\\n\";\n  }\n\n private:\n  const char* name_;\n  const T& capture_;\n};\n#pragma GCC diagnostic pop\n}  // namespace CaptureForError_detail\n\n/// \\cond\n#define CAPTURE_FOR_ERROR_NAME2(line) capture_for_error_impl##line\n#define CAPTURE_FOR_ERROR_NAME(line) CAPTURE_FOR_ERROR_NAME2(line)\n/// \\endcond\n\n/*!\n * \\brief Capture a variable to be printed on ERROR or ASSERT.\n *\n * The argument will be printed using its stream operator if an error\n * occurs before the end of the current scope.  The object is captured\n * by reference, so it must live until the end of the current scope,\n * and any subsequent changes to it will be reflected in the error\n * message.\n */\n#define CAPTURE_FOR_ERROR(var)                                          \\\n  const CaptureForError_detail::CaptureForError CAPTURE_FOR_ERROR_NAME( \\\n      __LINE__)(#var, var)\n\n/*!\n * \\brief Stream all objects currently captured by CaptureForError.\n */\nvoid print_captures_for_error(std::ostream& stream);\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/Error.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines macro ERROR.\n\n#pragma once\n\n#include <iomanip>\n#include <string>\n\n#include \"Utilities/ErrorHandling/AbortWithErrorMessage.hpp\"\n#include \"Utilities/ErrorHandling/Breakpoint.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/System/Abort.hpp\"\n\nnamespace Error_detail {\n// You can't use ScopedFpeState (a non-literal type) in a constexpr\n// function, but you can call another function that uses it.\ntemplate <typename ExceptionTypeToThrow, typename F>\n[[noreturn]] SPECTRE_ALWAYS_INLINE void abort_without_fpes(\n    const char* file, const int line, const char* const pretty_function,\n    F&& message) {\n  const ScopedFpeState disable_fpes(false);\n  abort_with_error_message<ExceptionTypeToThrow>(file, line, pretty_function,\n                                                 message());\n}\n}  // namespace Error_detail\n\n/*!\n * \\ingroup ErrorHandlingGroup\n * \\brief prints an error message to the standard error stream and aborts the\n * program.\n *\n * ERROR should not be used for coding errors, but instead for user errors\n * or failure modes of numerical algorithms. An acceptable use for error is also\n * in the default case of a switch statement.\n *\n * \\details\n * The implementation is specialized so that in compile time contexts, a short\n * error message will be thrown, but in runtime contexts, a more verbose error\n * will be printed. This specialization of throwing a short error at compile\n * time greatly reduces the compile time and memory consumption during debug\n * builds of deep and heavily inlined `TensorExpression` tree traversals.\n *\n * To accomplish this, `__builtin_is_constant_evaluated()` is used directly\n * instead of calling a wrapper function because calling a wrapper was found to\n * slightly increase the compile time and memory usage of large\n * `TensorExpression`s when compiling in debug mode.\n *\n * \\param m an arbitrary output stream.\n */\n// isocpp.org recommends using an `if (true)` instead of a `do\n// while(false)` for macros because the latter can mess with inlining\n// in some (old?) compilers:\n// https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-multi-stmts\n// https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-if\n// However, Intel's reachability analyzer (as of version 16.0.3\n// 20160415) can't figure out that the else branch and everything\n// after it is unreachable, causing warnings (and possibly suboptimal\n// code generation).\n#ifdef __CUDA_ARCH__\n#if defined(__clang__) && defined(__CUDA__)\n#define ERROR(m)                                              \\\n  printf(\"Error in file %s on line %d.\", __FILE__, __LINE__); \\\n  __builtin_trap();\n#else\n#define ERROR(m)                                              \\\n  printf(\"Error in file %s on line %d.\", __FILE__, __LINE__); \\\n  __trap();\n#endif\n#else\n#define ERROR(m)                                                             \\\n  do {                                                                       \\\n    if (__builtin_is_constant_evaluated()) {                                 \\\n      throw std::runtime_error(\"Failed\");                                    \\\n    } else {                                                                 \\\n      Error_detail::abort_without_fpes<SpectreError>(                        \\\n          __FILE__, __LINE__, static_cast<const char*>(__PRETTY_FUNCTION__), \\\n          [&]() -> std::string {                                             \\\n            return MakeString{} << std::setprecision(18) << std::scientific  \\\n                                << m;                                        \\\n          });                                                                \\\n    }                                                                        \\\n  } while (false)\n#endif\n\n/*!\n * \\ingroup ErrorHandlingGroup\n * \\brief Same as ERROR but will throw `EXCEPTION_TYPE` instead of\n * `SpectreError`.\n *\n * \\note Any exception types used must have an associated explicit\n * instantiation in `Utilities/ErrorHandling/AbortWithErrorMessage.cpp`\n */\n#ifdef __CUDA_ARCH__\n#if defined(__clang__) && defined(__CUDA__)\n#define ERROR_AS(m, EXCEPTION_TYPE)                           \\\n  printf(\"Error in file %s on line %d.\", __FILE__, __LINE__); \\\n  __builtin_trap();\n#else\n#define ERROR_AS(m, EXCEPTION_TYPE)                           \\\n  printf(\"Error in file %s on line %d.\", __FILE__, __LINE__); \\\n  __trap();\n#endif\n#else\n#define ERROR_AS(m, EXCEPTION_TYPE)                                          \\\n  do {                                                                       \\\n    if (__builtin_is_constant_evaluated()) {                                 \\\n      throw std::runtime_error(\"Failed\");                                    \\\n    } else {                                                                 \\\n      Error_detail::abort_without_fpes<EXCEPTION_TYPE>(                      \\\n          __FILE__, __LINE__, static_cast<const char*>(__PRETTY_FUNCTION__), \\\n          [&]() -> std::string {                                             \\\n            return MakeString{} << std::setprecision(18) << std::scientific  \\\n                                << m;                                        \\\n          });                                                                \\\n    }                                                                        \\\n  } while (false)\n#endif\n\n/*!\n * \\ingroup ErrorHandlingGroup\n * \\brief Same as ERROR but does not print a backtrace. Intended to be used for\n * user errors, such as incorrect values in an input file.\n */\n#ifdef __CUDA_ARCH__\n#if defined(__clang__) && defined(__CUDA__)\n#define ERROR_NO_TRACE(m)                                     \\\n  printf(\"Error in file %s on line %d.\", __FILE__, __LINE__); \\\n  __builtin_trap();\n#else\n#define ERROR_NO_TRACE(m)                                     \\\n  printf(\"Error in file %s on line %d.\", __FILE__, __LINE__); \\\n  __trap();\n#endif\n#else\n#define ERROR_NO_TRACE(m)                                                    \\\n  do {                                                                       \\\n    if (__builtin_is_constant_evaluated()) {                                 \\\n      throw std::runtime_error(\"Failed\");                                    \\\n    } else {                                                                 \\\n      const ScopedFpeState disable_fpes_ERROR(false);                        \\\n      abort_with_error_message_no_trace(                                     \\\n          __FILE__, __LINE__, static_cast<const char*>(__PRETTY_FUNCTION__), \\\n          MakeString{} << std::setprecision(18) << std::scientific << m);    \\\n    }                                                                        \\\n  } while (false)\n#endif\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/Exceptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <stdexcept>\n\n/// \\ingroup ErrorHandlingGroup\n/// Exception indicating an ASSERT failed\nclass SpectreAssert : public std::runtime_error {\n public:\n  explicit SpectreAssert(const std::string& message) : runtime_error(message) {}\n};\n\n/// \\ingroup ErrorHandlingGroup\n/// Exception indicating an ERROR was triggered\nclass SpectreError : public std::runtime_error {\n public:\n  explicit SpectreError(const std::string& message) : runtime_error(message) {}\n};\n\n/// \\ingroup ErrorHandlingGroup\n/// Exception indicating an ERROR was triggered because of an FPE\n///\n/// \\note You cannot rely on catching this exception for recovering from\n/// FPEs because not all compilers and hardware properly support throwing\n/// exceptions on FPEs.\nclass SpectreFpe : public std::runtime_error {\n public:\n  explicit SpectreFpe(const std::string& message) : runtime_error(message) {}\n};\n\n/// \\ingroup ErrorHandlingGroup\n/// Exception indicating convergence failure\nclass convergence_error : public std::runtime_error {\n public:\n  explicit convergence_error(const std::string& message)\n      : runtime_error(message) {}\n};\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/ExpectsAndEnsures.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines macros Expects and Ensures\n\n#pragma once\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n// part of GSL, but GSL depends on Expects and Ensures...\n#ifndef UNLIKELY\n#if defined(__clang__) || defined(__GNUC__)\n#define UNLIKELY(x) __builtin_expect(!!(x), 0)\n#else\n#define UNLIKELY(x) (x)\n#endif\n#endif\n\n/*!\n * \\ingroup ErrorHandlingGroup\n * \\brief check expectation of pre-conditions of a function\n *\n * The Expects macro sets the preconditions to a function's arguments, it is a\n * contract (C++20) that must be satisfied. See the CppCoreGuidelines for\n * details.\n * \\param cond the expression that is expected to be true\n */\n#if defined(SPECTRE_DEBUG) || defined(EXPECTS_ENSURES)\n#define Expects(cond)      \\\n  if (UNLIKELY(!(cond))) { \\\n    ERROR(#cond);          \\\n  } else                   \\\n    static_cast<void>(0)\n#else\n#define Expects(cond)        \\\n  if (false) {               \\\n    static_cast<void>(cond); \\\n  } else                     \\\n    static_cast<void>(0)\n#endif\n\n/*!\n * \\ingroup ErrorHandlingGroup\n * \\brief Check that a post-condition of a function is true\n *\n * The Ensures macro sets the postconditions of function, it is a contract\n * (C++20) that must be satisfied. See the CppCoreGuidelines for details.\n * \\param cond the expression that is expected to be true\n */\n#if defined(SPECTRE_DEBUG) || defined(EXPECTS_ENSURES)\n#define Ensures(cond)      \\\n  if (UNLIKELY(!(cond))) { \\\n    ERROR(#cond);          \\\n  } else                   \\\n    static_cast<void>(0)\n#else\n#define Ensures(cond)        \\\n  if (false) {               \\\n    static_cast<void>(cond); \\\n  } else                     \\\n    static_cast<void>(0)\n#endif\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/FloatingPointExceptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n\n#include <csignal>\n\n#if SPECTRE_FPE_CSR\n#include <xmmintrin.h>\n#elif SPECTRE_FPE_FENV\n#include <cfenv>\n#endif\n\nnamespace {\n\n#if SPECTRE_FPE_CSR\nconst unsigned int disabled_mask = _mm_getcsr();\nconst unsigned int exception_flags =\n    _MM_MASK_MASK & ~(_MM_MASK_OVERFLOW | _MM_MASK_INVALID | _MM_MASK_DIV_ZERO);\n#elif SPECTRE_FPE_FENV\nconst int exception_flags = FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW;\n#endif\n\n[[noreturn]] void fpe_signal_handler(int /*signal*/) {\n#if SPECTRE_FPE_CSR\n  _mm_setcsr(exception_flags);\n#elif SPECTRE_FPE_FENV\n  feenableexcept(exception_flags);\n#endif\n  ERROR_AS(\"Floating point exception!\", SpectreFpe);\n}\n}  // namespace\n\nvoid enable_floating_point_exceptions() {\n#if SPECTRE_FPE_CSR\n  _mm_setcsr(exception_flags);\n#elif SPECTRE_FPE_FENV\n  feenableexcept(exception_flags);\n#endif\n\n  struct sigaction handler {};\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)\n  handler.sa_handler = fpe_signal_handler;\n  handler.sa_flags = SA_NODEFER;\n  sigemptyset(&handler.sa_mask);\n  sigaction(SIGFPE, &handler, nullptr);\n}\n\nvoid disable_floating_point_exceptions() {\n#if SPECTRE_FPE_CSR\n  _mm_setcsr(disabled_mask);\n#elif SPECTRE_FPE_FENV\n  fedisableexcept(exception_flags);\n#endif\n}\n\nScopedFpeState::~ScopedFpeState() { restore_exceptions(); }\n\nScopedFpeState::ScopedFpeState() { save_exceptions(); }\n\nScopedFpeState::ScopedFpeState(const bool exceptions_enabled)\n    : ScopedFpeState() {\n  set_exceptions(exceptions_enabled);\n}\n\nScopedFpeState::ScopedFpeState(ScopedFpeState::DoNotSave /*meta*/) {}\n\nvoid ScopedFpeState::set_exceptions(const bool exceptions_enabled) const {\n  ASSERT(original_state_.has_value(), \"FPE state not saved.\");\n  if (exceptions_enabled) {\n    enable_floating_point_exceptions();\n  } else {\n    disable_floating_point_exceptions();\n  }\n}\n\nvoid ScopedFpeState::save_exceptions() {\n  ASSERT(not original_state_.has_value(), \"FPE state already saved.\");\n#if SPECTRE_FPE_CSR\n  original_state_.emplace(_mm_getcsr());\n#elif SPECTRE_FPE_FENV\n  original_state_.emplace(fegetexcept());\n#else\n  original_state_.emplace();\n#endif\n}\n\nvoid ScopedFpeState::restore_exceptions() {\n  if (not original_state_.has_value()) {\n    return;\n  }\n#if SPECTRE_FPE_CSR\n  _mm_setcsr(*original_state_);\n#elif SPECTRE_FPE_FENV\n  fedisableexcept(exception_flags);\n  feenableexcept(*original_state_);\n#endif\n  original_state_.reset();\n}\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/FloatingPointExceptions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Functions to enable/disable termination on floating point exceptions\n\n#pragma once\n\n#include <optional>\n\n/// \\cond\n#ifdef __APPLE__\n#ifndef __arm64__\n#define SPECTRE_FPE_CSR 1\n#endif\n#elif not defined(__aarch64__)\n#define SPECTRE_FPE_FENV 1\n#endif\n/// \\endcond\n\n/// \\ingroup ErrorHandlingGroup\n/// After a call to this function, the code will terminate with a floating\n/// point exception on overflow, divide-by-zero, and invalid operations.\nvoid enable_floating_point_exceptions();\n\n/// \\ingroup ErrorHandlingGroup\n/// After a call to this function, the code will NOT terminate with a floating\n/// point exception on overflow, divide-by-zero, and invalid operations.\n///\n/// \\warning Do not use this function to temporarily disable FPEs,\n/// because it will not interact correctly with C++ exceptions.  Use\n/// `ScopedFpeState` instead.\nvoid disable_floating_point_exceptions();\n\n/// \\ingroup ErrorHandlingGroup\n/// An RAII object to temporarily modify the handling of floating\n/// point exceptions.\nclass ScopedFpeState {\n public:\n  ScopedFpeState(const ScopedFpeState&) = delete;\n  ScopedFpeState(ScopedFpeState&&) = delete;\n  ScopedFpeState& operator=(const ScopedFpeState&) = delete;\n  ScopedFpeState& operator=(ScopedFpeState&&) = delete;\n  ~ScopedFpeState();\n\n  /// Start a scope that will be restored, without changing the\n  /// current state.\n  ScopedFpeState();\n  /// Start a scope with the specified exception state.  This is\n  /// equivalent to calling the default constructor followed by\n  /// `set_exceptions`.\n  explicit ScopedFpeState(bool exceptions_enabled);\n\n  struct DoNotSave {};\n  /// Start a scope without saving the current state.  The only valid\n  /// method call from this state is `save_exceptions`.\n  explicit ScopedFpeState(DoNotSave /*meta*/);\n\n  /// Enable or disable floating point exceptions.  It is an error if\n  /// the exception state is not currently saved.\n  void set_exceptions(bool exceptions_enabled) const;\n\n  /// Save the current exception handling state after it has been\n  /// cleared by `restore_exceptions`.  It will be restored by a later\n  /// call to `restore_exceptions`.  It is an error to call this if\n  /// a state is already saved.\n  void save_exceptions();\n\n  /// Restore the FPE handling to the internally saved state if\n  /// present and clear that state.  This is called automatically by\n  /// the destructor if it is not called manually.\n  void restore_exceptions();\n\n private:\n#if SPECTRE_FPE_CSR\n  std::optional<unsigned int> original_state_;\n#elif SPECTRE_FPE_FENV\n  std::optional<int> original_state_;\n#else\n  // FPEs not supported, but this is still used to check method calls\n  // are valid.\n  struct DummyState {};\n  std::optional<DummyState> original_state_;\n#endif\n};\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/FormatStacktrace.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/ErrorHandling/FormatStacktrace.hpp\"\n\n#include <array>\n#include <boost/core/demangle.hpp>\n#include <boost/stacktrace.hpp>\n#include <cstddef>\n#include <cstdlib>     // For std::getenv\n#include <execinfo.h>  // For backtrace_symbols\n// link.h is not available on all platforms\n#if __has_include(<link.h>)\n#include <link.h>\n#endif\n#include <memory>\n#include <sstream>\n#include <string>\n\nstd::string abbreviated_symbol_name(const std::string& symbol_name) {\n  // We display the first `abbrev_length_from_start` characters of the symbol\n  // name, then \" [...] \", and then the last `abbrev_length_from_end` characters\n  // of the symbol name.\n  constexpr size_t abbrev_length_from_start = 300;\n  constexpr size_t abbrev_length_from_end = 100;\n  // Length of \" [...] \"\n  constexpr size_t abbrev_length_separator = 7;\n  if (symbol_name.size() <= abbrev_length_from_start + abbrev_length_from_end +\n                                abbrev_length_separator) {\n    return symbol_name;\n  }\n  // Allow an environment variable to toggle the abbreviation\n  const char* show_full_symbol_env =\n      std::getenv(\"SPECTRE_SHOW_FULL_BACKTRACE_SYMBOLS\");\n  if (show_full_symbol_env != nullptr and\n      not std::string{show_full_symbol_env}.empty()) {\n    return symbol_name;\n  }\n  return symbol_name.substr(0, abbrev_length_from_start) + \" [...] \" +\n         symbol_name.substr(symbol_name.size() - abbrev_length_from_end,\n                            abbrev_length_from_end);\n}\n\nnamespace {\n\nstd::string get_stack_frame(void* addr) {\n  std::unique_ptr<char*, decltype(free)*> stack_syms{\n      backtrace_symbols(&addr, 1), free};\n  return stack_syms.get()[0];\n}\n\n#if __has_include(<link.h>)\nstd::string addr2line_command(const void* addr) {\n  std::stringstream ss;\n  ss << \"addr2line -fCpe \";\n  // Convert the address to the virtual memory address inside the\n  // library/executable. This is the address that addr2line and llvm-addr2line\n  // expect.\n  Dl_info info;\n  link_map* link_map = nullptr;\n  dladdr1(addr, &info,\n          reinterpret_cast<void**>(&link_map),  // NOLINT\n          RTLD_DL_LINKMAP);\n  ss << info.dli_fname;\n  ss << \" \";\n  // NOLINTNEXTLINE\n  ss << reinterpret_cast<void*>(reinterpret_cast<size_t>(addr) -\n                                link_map->l_addr);\n  return ss.str();\n}\n#endif  // __has_include(<link.h>)\n\n}  // namespace\n\nstd::ostream& operator<<(std::ostream& os,\n                         const boost::stacktrace::stacktrace& backtrace) {\n  const std::streamsize standard_width = os.width();\n  const size_t frames = backtrace.size();\n  for (size_t i = 0; i < frames; ++i) {\n    const auto& frame = backtrace[i];\n    const std::string& symbol_name = frame.name();\n    const std::string& source_file = frame.source_file();\n    const size_t source_line = frame.source_line();\n    // Enumerate frame number\n    os.width(3);\n    os << i;\n    os.width(standard_width);\n    os << \". \";\n    if (frame.empty()) {\n      os << \"[empty]\";\n      continue;\n    }\n    // Skip frames originating in error handling code\n    if (symbol_name.find(\"abort_with_error_message\") != std::string::npos or\n        source_file.find(\"src/Utilities/ErrorHandling/\") != std::string::npos or\n        symbol_name.find(\"boost::stacktrace\") != std::string::npos) {\n      os << \"[error handling]\\n\";\n      continue;\n    }\n    if (symbol_name.empty()) {\n      // Boost was unable to get the symbol name (probably because dladdr\n      // can't find it either). Fall back to printing the default stack\n      // frame.\n      const std::string stack_frame =\n          get_stack_frame(const_cast<void*>(frame.address()));  // NOLINT\n      if (stack_frame.find(\"ErrorHandling\") != std::string::npos or\n          stack_frame.find(\"segfault_signal_handler\") != std::string::npos) {\n        os << \"[error handling]\\n\";\n        continue;\n      }\n      os << stack_frame;\n    } else {\n      // Print symbol name. Abbreviate if necessary to avoid filling the\n      // screen with templates.\n      os << abbreviated_symbol_name(symbol_name);\n    }\n    if (source_line != 0) {\n      // Print location in source file if available\n      os << \" in \" << source_file << \":\" << source_line;\n    } else {\n#if __has_include(<link.h>)\n      // Print addr2line information otherwise\n      os << \" - Resolve source file and line with: \"\n         << addr2line_command(frame.address());\n#endif\n    }\n    os << '\\n';\n  }\n\n  return os;\n}\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/FormatStacktrace.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Formatter for Boost stacktrace, following these docs:\n/// https://www.boost.org/doc/libs/1_78_0/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.global_control_over_stacktrace_o\n\n#pragma once\n\n#include <boost/stacktrace/stacktrace_fwd.hpp>\n#include <ostream>\n\nstd::string abbreviated_symbol_name(const std::string& symbol_name);\n\nstd::ostream& operator<<(std::ostream& os,\n                         const boost::stacktrace::stacktrace& backtrace);\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/SegfaultHandler.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n#include <csignal>\n\nnamespace {\n[[noreturn]] void segfault_signal_handler(int /*signal*/) {\n  ERROR(\"Segmentation fault!\");\n}\n}  // namespace\n\nvoid enable_segfault_handler() {\n  std::signal(SIGSEGV, segfault_signal_handler);\n}\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/SegfaultHandler.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Functions to enable handling segmentation faults\n\n#pragma once\n\n/// \\ingroup ErrorHandlingGroup\n/// After a call to this function, the code will handle `SIGSEGV` segmentation\n/// faults by printing an error with a stacktrace.\nvoid enable_segfault_handler();\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/StaticAssert.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines macro DEBUG_STATIC_ASSERT.\n\n#pragma once\n\n/*!\n * \\ingroup ErrorHandlingGroup\n * \\brief A `static_assert` that is only checked in Debug builds\n */\n#ifdef SPECTRE_DEBUG\n#define DEBUG_STATIC_ASSERT(...) static_assert(__VA_ARGS__)\n#else  // ifdef  SPECTRE_DEBUG\n#define DEBUG_STATIC_ASSERT(...) static_assert(true)\n#endif  // ifdef  SPECTRE_DEBUG\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/Strerror.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/ErrorHandling/Strerror.hpp\"\n\n#include <array>\n#include <string>\n\n#include \"Utilities/ErrorHandling/StrerrorWrapper.h\"\n\nstd::string strerror_threadsafe(const int errnum) {\n  // The documentation gives no guidance on how much space to reserve,\n  // but a typical English result is \"No such file or directory\".\n  std::array<char, 1000> message{};\n  if (spectre_strerror_r(errnum, message.data(), message.size()) != 0) {\n    return \"strerror failed\";\n  }\n  return {message.data()};\n}\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/Strerror.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\nstd::string strerror_threadsafe(int errnum);\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/StrerrorWrapper.c",
    "content": "/*\n * Distributed under the MIT License.\n * See LICENSE.txt for details.\n */\n\n#ifdef __linux__\n\n// Force selection of XSI-compliant strerror_r by glibc\n// Taken from https://groups.google.com/g/golang-checkins/c/cd1OSJoE20c?pli=1\n#undef _POSIX_C_SOURCE\n// POSIX_C_SOURCE must be >= 200112L and _GNU_SOURCE not defined\n// as per strerror_r man page (https://linux.die.net/man/3/strerror_r)\n#define _POSIX_C_SOURCE 200112L  // NOLINT\n#undef _GNU_SOURCE\n\n#endif /* __linux__ */\n\n#include \"Utilities/ErrorHandling/StrerrorWrapper.h\"\n\n#include <stddef.h>\n#include <string.h>\n\nint spectre_strerror_r(const int errnum, char* const buf, const size_t buflen) {\n  return strerror_r(errnum, buf, buflen);\n}\n"
  },
  {
    "path": "src/Utilities/ErrorHandling/StrerrorWrapper.h",
    "content": "/*\n * Distributed under the MIT License.\n * See LICENSE.txt for details.\n */\n\n#pragma once\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n/*!\n * \\brief Same as the POSIX strerror_r.\n *\n * \\details C wrapper to avoid visibility issues caused by GNU.  The\n * POSIX-compliant function strerror_r is only defined by glibc if\n * _GNU_SOURCE is not defined, but libstdc++ doesn't work without\n * _GNU_SOURCE defined.\n */\nint spectre_strerror_r(int errnum, char* buf, size_t buflen);\n#ifdef __cplusplus\n} /* extern \"C\" */\n#endif /* __cplusplus */\n"
  },
  {
    "path": "src/Utilities/FileSystem.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/FileSystem.hpp\"\n\n#include <algorithm>\n#include <cerrno>\n#include <chrono>\n#include <cmath>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <filesystem>\n#include <glob.h>\n#include <libgen.h>\n#include <memory>\n#include <regex>\n#include <sstream>\n#include <thread>\n\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace file_system {\n\nvoid copy(const std::string& from, const std::string& to) {\n  std::filesystem::copy(from, to);\n}\n\nstd::string cwd() {\n  double wait_time = 1;\n  std::string current_path = std::filesystem::current_path();\n  while (current_path.empty()) {\n    // It's not clear how to test this code since we can't make the file system\n    // be slow\n    // LCOV_EXCL_START\n    std::this_thread::sleep_for(std::chrono::duration<double>(wait_time));\n    wait_time += 10;\n    try {\n      current_path = std::filesystem::current_path();\n    } catch (const std::exception& e) {\n      if (wait_time > 61) {\n        ERROR(\n            \"Could not get the current directory. This is typically related to \"\n            \"filesystem issues. Exception message: \"\n            << e.what());\n      }\n    }\n    // LCOV_EXCL_STOP\n  }\n  return current_path;\n}\n\nvoid create_directory(const std::string& dir, const double wait_time,\n                      const size_t num_tries) {\n  // each time we fail to create a directory, we increase the wait_time by this\n  // factor\n  static constexpr double wait_time_increase_factor = 1.1;\n  if (dir.empty()) {\n    ERROR(\"Cannot create a directory that has no name\");\n  }\n  if (std::string::npos == dir.find_first_not_of('/')) {\n    return;  // trying to create directory '/'\n  }\n\n  // Try multiple times for slow filesystems\n  for (size_t number_of_failures = 0; number_of_failures < num_tries;\n       ++number_of_failures) {\n    try {\n      std::filesystem::create_directories(dir);\n      return;\n    } catch (const std::exception& e) {\n      // LCOV_EXCL_START\n      Parallel::printf(\n          \"create_directory: mkdir(%s) failed %d time(s). Error: %s\\n\", dir,\n          number_of_failures + 1, e.what());\n      std::this_thread::sleep_for(std::chrono::duration<double>(\n          wait_time *\n          std::pow(wait_time_increase_factor, number_of_failures + 1)));\n      // LCOV_EXCL_STOP\n    }\n  }\n  // LCOV_EXCL_START\n  ERROR(\"Unable to mkdir '\" << dir << \"'. Giving up after \" << num_tries\n                            << \" tries\\n\");\n  // LCOV_EXCL_STOP\n}\n\nbool check_if_dir_exists(const std::string& dir) {\n  return std::filesystem::exists(dir) and std::filesystem::is_directory(dir);\n}\n\nbool check_if_file_exists(const std::string& file) {\n  return std::filesystem::exists(file) and\n         std::filesystem::is_regular_file(file);\n}\n\nsize_t file_size(const std::string& file) {\n  if (not check_if_file_exists(file)) {\n    ERROR(\"Cannot get size of file '\"\n          << file\n          << \"' because it cannot be accessed. Either it does not \"\n             \"exist or you do not have the appropriate permissions.\");\n  }\n  return static_cast<size_t>(std::filesystem::file_size(file));\n}\n\nstd::string get_absolute_path(const std::string& rel_path) {\n  return std::filesystem::canonical(rel_path);\n}\n\nstd::string get_file_name(const std::string& file_path) {\n  if (file_path.empty()) {\n    ERROR(\"Received an empty path\");\n  }\n  if (file_path.find('/') == std::string::npos) {\n    // Handle file names such as 'dummy.txt' or '.dummy.txt'\n    return file_path;\n  }\n  std::smatch match{};\n  std::regex file_name_pattern{R\"(^.*/([^/]+))\"};\n  auto regex_matched = std::regex_search(file_path, match, file_name_pattern);\n  if (not regex_matched) {\n    ERROR(\"Failed to find a file in the given path: '\" << file_path << \"'\");\n  }\n  return match[1];\n}\n\nstd::string get_parent_path(const std::string& path) {\n  std::vector<char> file_path(path.length() + 1);\n  strncpy(file_path.data(), path.c_str(), path.length() + 1);\n  // The pointer from ::dirname is not freed since it aliases with file_path\n  char* parent_dir_name = ::dirname(file_path.data());\n  std::string return_name(parent_dir_name);\n  return return_name;\n}\n\nstd::vector<std::string> glob(const std::string& pattern) {\n  glob_t buffer;\n  const int return_value =\n      ::glob(pattern.c_str(), GLOB_TILDE, nullptr, &buffer);\n  if (return_value != 0) {\n    ERROR(\"Unable to resolve glob '\" + pattern + \"': \" + std::strerror(errno));\n  }\n  std::vector<std::string> file_names(\n      buffer.gl_pathv,\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      buffer.gl_pathv + buffer.gl_pathc);\n  globfree(&buffer);\n  return file_names;\n}\n\nbool is_file(const std::string& path) {\n  if (not std::filesystem::exists(path)) {\n    ERROR(\n        \"Failed to check if path points to a file because the path is invalid. \"\n        \"Given path is: \"\n        << path);\n  }\n  return std::filesystem::is_regular_file(path);\n}\n\nstd::vector<std::string> ls(const std::string& dir_name) {\n  std::vector<std::string> contents;\n  for (auto const& dir_entry : std::filesystem::directory_iterator{dir_name}) {\n    contents.push_back(dir_entry.path().filename());\n  }\n  return contents;\n}\n\nvoid rm(const std::string& path, bool recursive) {\n  if (recursive) {\n    std::filesystem::remove_all(path);\n  } else {\n    std::filesystem::remove(path);\n  }\n}\n}  // namespace file_system\n"
  },
  {
    "path": "src/Utilities/FileSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares functions to do file system manipulations\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief A light-weight file system library based on POSIX.\n *\n * We use this library instead of a subprocess based library because OpenMPI\n * does not support forking of processes on all systems. Since the\n * parallelization library we use may be implemented on top of OpenMPI we\n * take the safe route and use POSIX.\n */\nnamespace file_system {\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Copies files or directories.\n *\n * Wrapper around `std::file_system::copy()`.\n */\nvoid copy(const std::string& from, const std::string& to);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Returns the current working directory, resolving symlinks\n */\nstd::string cwd();\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Creates a directory, including any parents that don't exist. If the\n * directory exists `create_directory` does nothing.\n *\n * \\requires permissions to create `dir` on the filesystem\n * \\effects creates the directory `dir` on the filesystem\n *\n * \\param dir the path where to create the directory\n * \\param wait_time time to wait in seconds between failures\n * \\param num_tries number of attempts to create directory (for slow\n * filesystems)\n */\nvoid create_directory(const std::string& dir, double wait_time = 1,\n                      size_t num_tries = 40);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Returns true if the directory exists\n *\n * \\returns `true` if the directory exists\n */\nbool check_if_dir_exists(const std::string& dir);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Returns true if the regular file or link to the regular file exists.\n *\n * \\note See the stat(2) documentation, e.g. at\n * http://man7.org/linux/man-pages/man2/stat.2.html for details.\n *\n * \\returns `true` if the file exists\n */\nbool check_if_file_exists(const std::string& file);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Returns the file size in bytes\n *\n * \\requires `file` is a valid file on the filesystem\n * \\returns size of `file` in bytes\n */\nsize_t file_size(const std::string& file);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Get the absolute path, resolving symlinks\n *\n * \\requires `rel_path` is a valid path on the filesystem\n * \\returns the absolute path\n */\nstd::string get_absolute_path(const std::string& rel_path);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Given a path to a file returns the file name\n *\n * \\example\n * \\snippet Test_FileSystem.cpp get_file_name\n *\n * \\requires `file_path` is a valid path on the filesystem\n * \\returns the file name\n */\nstd::string get_file_name(const std::string& file_path);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Wraps the dirname function to get the pathname of the parent directory\n *\n * See the opengroup documentation:\n * http://pubs.opengroup.org/onlinepubs/9699919799/functions/dirname.html\n *\n * \\example\n * \\snippet Test_FileSystem.cpp get_parent_path\n *\n * \\requires `path` is a valid path on the filesystem\n * \\returns the path to the parent directory\n */\nstd::string get_parent_path(const std::string& path);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Get a list of files matching the given glob pattern\n */\nstd::vector<std::string> glob(const std::string& pattern);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Returns true if the path points to a regular file or a link to a\n * regular file.\n *\n * \\note See the stat(2) documentation, e.g. at\n * http://man7.org/linux/man-pages/man2/stat.2.html for details.\n *\n * \\requires `path` is a valid path on the filesystem\n * \\returns `true` if `file` is a file, not a directory\n */\nbool is_file(const std::string& path);\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Gets a list of files in a directory\n *\n * \\returns vector of all files and directories inside `dir_name`\n */\nstd::vector<std::string> ls(const std::string& dir_name = \"./\");\n\n/*!\n * \\ingroup FileSystemGroup\n * \\brief Deletes a file or directory.\n *\n * \\requires `path` be a valid path on the filesystem\n * \\effects deletes `path` from the filesystem, if `recursive` is `true` then\n * behaves like `rm -r`, otherwise like `rm` but will delete an empty directory\n */\nvoid rm(const std::string& path, bool recursive);\n}  // namespace file_system\n"
  },
  {
    "path": "src/Utilities/ForceInline.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines macro to always inline a function.\n\n#pragma once\n\n#if SPECTRE_USE_ALWAYS_INLINE && defined(__GNUC__)\n/// \\ingroup UtilitiesGroup\n/// Always inline a function. Only use this if you benchmarked the code.\n#define SPECTRE_ALWAYS_INLINE __attribute__((always_inline)) inline\n#else\n/// \\ingroup UtilitiesGroup\n/// Always inline a function. Only use this if you benchmarked the code.\n#define SPECTRE_ALWAYS_INLINE inline\n#endif\n"
  },
  {
    "path": "src/Utilities/Formaline.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/Formaline.hpp\"\n\n#include <cstdio>\n#include <ostream>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\n// NOTE: The definitions of the functions `formaline::get_archive`,\n// `formaline::get_environment_variables`, `formaline::get_build_info`,\n// and `formaline::get_paths` are generated at link time and defined in the\n// script `tools/Formaline.sh` for non-macOS builds.\nnamespace formaline {\nvoid write_to_file(const std::string& filename_without_extension) {\n  std::FILE* outfile = nullptr;\n  const auto archive = get_archive();\n\n  const std::string filename = filename_without_extension + \".tar.gz\";\n  outfile = std::fopen(filename.c_str(), \"w\");\n  if (outfile == nullptr) {\n    ERROR(\"Failed to open file '\" << filename << \"' for Formaline output\");\n  }\n  std::fwrite(archive.data(), sizeof(char), archive.size(), outfile);\n  std::fclose(outfile);\n}\n\n#ifdef SPECTRE_NO_FORMALINE\nstd::vector<char> get_archive() {\n  return {'N', 'o', 't', ' ', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd'};\n}\n\nstd::string get_environment_variables() { return \"Not supported on macOS\"; }\n\nstd::string get_build_info() { return \"Not supported on macOS\"; }\n\nstd::string get_paths() { return \"Not supported on macOS\"; }\n#endif  // defined(__APPLE__)\n}  // namespace formaline\n"
  },
  {
    "path": "src/Utilities/Formaline.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Functions for retrieving system and source tree information\n */\nnamespace formaline {\n/*!\n * \\brief Returns a byte stream of the source tree at the time the executable\n * was compiled.\n */\nstd::vector<char> get_archive();\n\n/*!\n * \\brief Returns the environment variables at link time.\n */\nstd::string get_environment_variables();\n\n/*!\n * \\brief Returns the contents of SpECTRE's BuildInfo.txt file.\n */\nstd::string get_build_info();\n\n/*!\n * \\brief Returns the PATH, CPATH, LD_LIBRARY_PATH, LIBRARY_PATH, and\n * CMAKE_PREFIX_PATH at time of compilation.\n */\nstd::string get_paths();\n\n/*!\n * \\brief Write the source tree archive to the file\n * `filename_without_extension.tar.gz`\n */\nvoid write_to_file(const std::string& filename_without_extension);\n}  // namespace formaline\n"
  },
  {
    "path": "src/Utilities/FractionUtilities.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cmath>\n#include <cstdint>\n#include <limits>\n#include <type_traits>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// Type trait to check if a type looks like a fraction (specifically,\n/// if it has numerator and denominator methods)\n/// @{\ntemplate <typename T, typename = std::void_t<>>\nstruct is_fraction : std::false_type {};\ntemplate <typename T>\nstruct is_fraction<T, std::void_t<decltype(std::declval<T>().numerator()),\n                                  decltype(std::declval<T>().denominator())>>\n    : std::true_type {};\n\ntemplate <typename T>\nconstexpr bool is_fraction_v = is_fraction<T>::value;\n/// @}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Compute the continued fraction representation of a number\n///\n/// The template argument may be a fraction type or a floating point\n/// type.  If the expansion is computed exactly then it will be the\n/// shorter of the expansions for the given value.\ntemplate <class T>\nclass ContinuedFraction {\n  template <typename U, typename = typename is_fraction<U>::type>\n  struct value_type_helper;\n\n  template <typename U>\n  struct value_type_helper<U, std::false_type> {\n    using type = int64_t;\n  };\n\n  template <typename U>\n  struct value_type_helper<U, std::true_type> {\n    using type = std::decay_t<decltype(std::declval<U>().numerator())>;\n  };\n\n public:\n  /// The decayed return type of T::numerator() for a fraction,\n  /// int64_t for other types.\n  using value_type = typename value_type_helper<T>::type;\n\n  explicit ContinuedFraction(const T& value)\n      : term_(ifloor(value)),\n        remainder_(value - term_),\n        error_([this, &value]() {\n          using std::abs;\n          using std::max;\n          // For any non-fundamental type, epsilon() returns a\n          // default-constructed value, which should be zero for\n          // fractions.\n          return max(abs(value), abs(remainder_)) *\n                 std::numeric_limits<T>::epsilon();\n        }()) {}\n\n  /// Obtain the current element in the expansion\n  value_type operator*() const { return term_; }\n\n  /// Check if the expansion is incomplete.  If T is a fraction type\n  /// this will be true until the full exact representation of the\n  /// fraction is produced.  If T is a floating point type this will\n  /// be true until the estimated numerical error is larger than the\n  /// remaining error in the representation.\n  explicit operator bool() const { return not done_; }\n\n  /// Advance to the next element in the expansion\n  ContinuedFraction& operator++() {\n    // Terminate when remainder_ is consistent with zero.\n    if (remainder_ == 0 or error_ > remainder_) {\n      done_ = true;\n      return *this;\n    }\n    remainder_ = 1 / remainder_;\n    error_ *= square(remainder_);\n    term_ = ifloor(remainder_);\n    remainder_ -= term_;\n    return *this;\n  }\n\n private:\n  template <typename U>\n  static value_type ifloor(const U& x) {\n    if constexpr (is_fraction_v<U>) {\n      return static_cast<value_type>(x.numerator() / x.denominator());\n    } else {\n      return static_cast<value_type>(std::floor(x));\n    }\n  }\n\n  value_type term_;\n  T remainder_;\n  // Estimate of error in the term.\n  T error_;\n  bool done_{false};\n};\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Sum a continued fraction\n///\n/// \\tparam Fraction the result type, which must be a fraction type\ntemplate <class Fraction>\nclass ContinuedFractionSummer {\n public:\n  using Term_t = std::decay_t<decltype(std::declval<Fraction>().numerator())>;\n\n  /// Sum of the supplied continued fraction terms.  If the exact sum\n  /// cannot be represented by the `Fraction` type, a semiconvergent\n  /// may be returned instead.\n  Fraction value() const { return Fraction(numerator_, denominator_); }\n\n  /// Insert a new term.  Terms should be supplied from most to least\n  /// significant.\n  void insert(Term_t term) {\n    if (overflowed_) {\n      return;\n    }\n\n    Term_t new_numerator{};\n    Term_t new_denominator{};\n    if (__builtin_mul_overflow(term, numerator_, &new_numerator) or\n        __builtin_add_overflow(new_numerator, prev_numerator_,\n                               &new_numerator) or\n        __builtin_mul_overflow(term, denominator_, &new_denominator) or\n        __builtin_add_overflow(new_denominator, prev_denominator_,\n                               &new_denominator)) {\n      overflowed_ = true;\n\n      // We can't add this term exactly, but we can try to return a\n      // semiconvergent.\n      using std::abs;\n      const Term_t representable_semiconvergent_index =\n          std::min((std::numeric_limits<Term_t>::max() - abs(prev_numerator_)) /\n                       abs(numerator_),\n                   (std::numeric_limits<Term_t>::max() - prev_denominator_) /\n                       denominator_);\n      // There is an edge case where the two sides of this inequality\n      // are equal, where we don't have enough information to\n      // determine whether the semiconvergent approximation is better\n      // than the current convergent.  We assume it's not.\n      if (2 * representable_semiconvergent_index <= term) {\n        return;\n      }\n\n      new_numerator =\n          representable_semiconvergent_index * numerator_ + prev_numerator_;\n      new_denominator =\n          representable_semiconvergent_index * denominator_ + prev_denominator_;\n    }\n\n    prev_numerator_ = numerator_;\n    numerator_ = new_numerator;\n    prev_denominator_ = denominator_;\n    denominator_ = new_denominator;\n  }\n\n private:\n  Term_t numerator_{1};\n  Term_t denominator_{0};\n  Term_t prev_numerator_{0};\n  Term_t prev_denominator_{1};\n  bool overflowed_{false};\n};\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Find the fraction in the supplied interval with the\n/// smallest denominator\n///\n/// The endpoints are considered to be in the interval.  The order of\n/// the arguments is not significant.  The answer is unique as long as\n/// the interval has length less than 1; for longer intervals, an\n/// integer in the range will be returned.\ntemplate <typename Fraction, typename T1, typename T2>\nFraction simplest_fraction_in_interval(const T1& end1, const T2& end2) {\n  ContinuedFractionSummer<Fraction> result;\n  using Term_t = typename decltype(result)::Term_t;\n  ContinuedFraction<T1> cf1(end1);\n  ContinuedFraction<T2> cf2(end2);\n  using InputTerm_t = std::common_type_t<typename decltype(cf1)::value_type,\n                                         typename decltype(cf2)::value_type>;\n  InputTerm_t term1 = *cf1;\n  InputTerm_t term2 = *cf2;\n  for (; cf1 and cf2; term1 = *++cf1, term2 = *++cf2) {\n    if (term1 != term2) {\n      if (++cf1) {\n        ++term1;\n      }\n      if (++cf2) {\n        ++term2;\n      }\n      using std::min;\n      result.insert(gsl::narrow<Term_t>(min(term1, term2)));\n      return result.value();\n    }\n    result.insert(gsl::narrow<Term_t>(term1));\n  }\n  return result.value();\n}\n"
  },
  {
    "path": "src/Utilities/Functional.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/StaticAssert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Math.hpp\"\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Higher order function objects similar to `std::plus`, etc.\n *\n * \\details\n * These chaining function objects can be used to represent highly general\n * mathematical operations\n * 1. as types, which can be passed around in template arguments, and\n * 2. such that any time they can be evaluated at compile time, they will be.\n *\n * As an illustrative example, consider the definition of a general sinusoid\n * function object type :\n * \\snippet Utilities/Test_Functional.cpp using_sinusoid\n * which then gives a type which when instantiated and evaluated will give the\n * answer \\f$ a\\times\\sin(b + c \\times d)\\f$ from calling `Sinusoid{}(a,b,c,d)`\n *\n * As a more creative example, we can take advantage of literals to make, for\n * instance, distributions. Let's make a Gaussian with mean at 5.0 and unity\n * variance\n * \\snippet Utilities/Test_Functional.cpp using_gaussian\n *\n * This gives us a function object whose call operator takes one argument that\n * gives the value of the desired Gaussian distribution \\f$ e^{-(x - 5.0)^2}\n * \\f$\n */\nnamespace funcl {\n// using for overload resolution with blaze\nusing std::max;\nusing std::min;\n\n/// \\cond\ntemplate <size_t Arity>\nstruct Functional {\n  static constexpr size_t arity = Arity;\n\n protected:\n  template <class C, size_t Offset, class... Ts, size_t... Is>\n  static constexpr decltype(auto) helper(const std::tuple<Ts...>& t,\n                                         std::index_sequence<Is...> /*meta*/) {\n    return C{}(std::get<Offset + Is>(t)...);\n  }\n};\n\nstruct Identity;\n/// \\endcond\n\n/// Functional that asserts that the function object `C` applied to the first\n/// and second arguments are equal and returns the function object C applied to\n/// the first argument\ntemplate <class C = Identity>\nstruct AssertEqual : Functional<2> {\n  template <class T>\n  const T& operator()(const T& t0, const T& t1) {\n    DEBUG_STATIC_ASSERT(\n        C::arity == 1,\n        \"The arity of the functional passed to AssertEqual must be 1\");\n    ASSERT(C{}(t0) == C{}(t1), \"Values are not equal in funcl::AssertEqual \"\n           << C{}(t0) << \" and \" << C{}(t1));\n    return C{}(t0);\n  }\n};\n\n#define MAKE_BINARY_FUNCTIONAL(NAME, OPERATOR)                                 \\\n  /** Functional for computing `OPERATOR` from two objects */                  \\\n  template <class C0 = Identity, class C1 = C0>                                \\\n  struct NAME : Functional<C0::arity + C1::arity> {                            \\\n    using base = Functional<C0::arity + C1::arity>;                            \\\n    template <class... Ts>                                                     \\\n    constexpr auto operator()(const Ts&... ts) {                               \\\n      return OPERATOR(                                                         \\\n          base::template helper<C0, 0>(std::tuple<const Ts&...>(ts...),        \\\n                                       std::make_index_sequence<C0::arity>{}), \\\n          base::template helper<C1, C0::arity>(                                \\\n              std::tuple<const Ts&...>(ts...),                                 \\\n              std::make_index_sequence<C1::arity>{}));                         \\\n    }                                                                          \\\n  };                                                                           \\\n  /** \\cond */                                                                 \\\n  template <class C1>                                                          \\\n  struct NAME<Identity, C1> : Functional<1 + C1::arity> {                      \\\n    template <class T0, class... Ts>                                           \\\n    constexpr auto operator()(const T0& t0, const Ts&... ts) {                 \\\n      return OPERATOR(t0, C1{}(ts...));                                        \\\n    }                                                                          \\\n  };                                                                           \\\n  template <>                                                                  \\\n  struct NAME<Identity, Identity> : Functional<2> {                            \\\n    template <class T0, class T1>                                              \\\n    constexpr auto operator()(const T0& t0, const T1& t1) {                    \\\n      return OPERATOR(t0, t1);                                                 \\\n    }                                                                          \\\n  } /** \\endcond */\n\n#define MAKE_BINARY_INPLACE_OPERATOR(NAME, OPERATOR)               \\\n  /** Functional for computing `OPERATOR` of two objects */        \\\n  template <class C0 = Identity, class C1 = C0>                    \\\n  struct NAME : Functional<C0::arity + C1::arity> {                \\\n    using base = Functional<C0::arity + C1::arity>;                \\\n    template <class... Ts>                                         \\\n    constexpr decltype(auto) operator()(Ts&... ts) {               \\\n      return base::template helper<C0, 0>(                         \\\n          std::tuple<const Ts&...>(ts...),                         \\\n          std::make_index_sequence<C0::arity>{})                   \\\n          OPERATOR base::template helper<C1, C0::arity>(           \\\n              std::tuple<const Ts&...>(ts...),                     \\\n              std::make_index_sequence<C1::arity>{});              \\\n    }                                                              \\\n  };                                                               \\\n  /** \\cond */                                                     \\\n  template <class C1>                                              \\\n  struct NAME<Identity, C1> : Functional<1 + C1::arity> {          \\\n    template <class T0, class... Ts>                               \\\n    constexpr decltype(auto) operator()(T0& t0, const Ts&... ts) { \\\n      return t0 OPERATOR C1{}(ts...);                              \\\n    }                                                              \\\n  };                                                               \\\n  template <>                                                      \\\n  struct NAME<Identity, Identity> : Functional<2> {                \\\n    static constexpr size_t arity = 2;                             \\\n    template <class T0, class T1>                                  \\\n    constexpr decltype(auto) operator()(T0& t0, const T1& t1) {    \\\n      return t0 OPERATOR t1;                                       \\\n    }                                                              \\\n  } /** \\endcond */\n\n#define MAKE_BINARY_OPERATOR(NAME, OPERATOR)                   \\\n  /** Functional for computing `OPERATOR` of two objects */    \\\n  template <class C0 = Identity, class C1 = C0>                \\\n  struct NAME : Functional<C0::arity + C1::arity> {            \\\n    using base = Functional<C0::arity + C1::arity>;            \\\n    template <class... Ts>                                     \\\n    constexpr auto operator()(const Ts&... ts) {               \\\n      return base::template helper<C0, 0>(                     \\\n          std::tuple<const Ts&...>(ts...),                     \\\n          std::make_index_sequence<C0::arity>{})               \\\n          OPERATOR base::template helper<C1, C0::arity>(       \\\n              std::tuple<const Ts&...>(ts...),                 \\\n              std::make_index_sequence<C1::arity>{});          \\\n    }                                                          \\\n  };                                                           \\\n  /** \\cond */                                                 \\\n  template <class C1>                                          \\\n  struct NAME<Identity, C1> : Functional<1 + C1::arity> {      \\\n    template <class T0, class... Ts>                           \\\n    constexpr auto operator()(const T0& t0, const Ts&... ts) { \\\n      return t0 OPERATOR C1{}(ts...);                          \\\n    }                                                          \\\n  };                                                           \\\n  template <>                                                  \\\n  struct NAME<Identity, Identity> : Functional<2> {            \\\n    static constexpr size_t arity = 2;                         \\\n    template <class T0, class T1>                              \\\n    constexpr auto operator()(const T0& t0, const T1& t1) {    \\\n      return t0 OPERATOR t1;                                   \\\n    }                                                          \\\n  } /** \\endcond */\n\n#define MAKE_LITERAL_VAL(NAME, VAL)                                    \\\n  /** Functional literal for `VAL` */                                  \\\n  struct Literal##NAME : Functional<0> {                               \\\n    constexpr double operator()() { return static_cast<double>(VAL); } \\\n  }\n\n#define MAKE_UNARY_FUNCTIONAL(NAME, OPERATOR)             \\\n  /** Functional for computing `OPERATOR` on an object */ \\\n  template <typename C0 = Identity>                       \\\n  struct NAME;                                            \\\n  /** \\cond */                                            \\\n  template <typename C0>                                  \\\n  struct NAME : Functional<C0::arity> {                   \\\n    template <class... Ts>                                \\\n    constexpr auto operator()(const Ts&... ts) {          \\\n      return OPERATOR(C0{}(ts...));                       \\\n    }                                                     \\\n  };                                                      \\\n  template <>                                             \\\n  struct NAME<Identity> : Functional<1> {                 \\\n    template <class T0>                                   \\\n    constexpr auto operator()(const T0& t0) {             \\\n      return OPERATOR(t0);                                \\\n    }                                                     \\\n  } /** \\endcond */\n\n/// Functional to retrieve the `ArgumentIndex`th argument\ntemplate <size_t Arity, size_t ArgumentIndex = 0, class C = Identity>\nstruct GetArgument : Functional<Arity> {\n  template <class... Ts>\n  constexpr decltype(auto) operator()(const Ts&... ts) {\n    static_assert(Arity == sizeof...(Ts),\n                  \"The arity passed to GetArgument must be the same as the \"\n                  \"actually arity of the function.\");\n    return C{}(std::get<ArgumentIndex>(std::tuple<const Ts&...>(ts...)));\n  }\n};\n\n/// The identity higher order function object\nstruct Identity : Functional<1> {\n  template <class T>\n  SPECTRE_ALWAYS_INLINE constexpr const T& operator()(const T& t) {\n    return t;\n  }\n};\n\ntemplate <int val, typename Type = double>\nstruct Literal : Functional<0> {\n  constexpr Type operator()() { return static_cast<Type>(val); }\n};\n\nMAKE_BINARY_INPLACE_OPERATOR(DivAssign, /=);\nMAKE_BINARY_INPLACE_OPERATOR(MinusAssign, -=);\nMAKE_BINARY_INPLACE_OPERATOR(MultAssign, *=);\nMAKE_BINARY_INPLACE_OPERATOR(PlusAssign, +=);\n\nMAKE_BINARY_OPERATOR(Divides, /);\nMAKE_BINARY_OPERATOR(Minus, -);\nMAKE_BINARY_OPERATOR(Multiplies, *);\nMAKE_BINARY_OPERATOR(Plus, +);\nMAKE_BINARY_OPERATOR(And, and);\nMAKE_BINARY_OPERATOR(Or, or);\n\nMAKE_BINARY_FUNCTIONAL(Atan2, atan2);\nMAKE_BINARY_FUNCTIONAL(Hypot, hypot);\nMAKE_BINARY_FUNCTIONAL(Max, max);\nMAKE_BINARY_FUNCTIONAL(Min, min);\nMAKE_BINARY_FUNCTIONAL(Pow, pow);\n\nMAKE_LITERAL_VAL(Pi, M_PI);\nMAKE_LITERAL_VAL(E, M_E);\n\nMAKE_UNARY_FUNCTIONAL(Abs, abs);\nMAKE_UNARY_FUNCTIONAL(Acos, acos);\nMAKE_UNARY_FUNCTIONAL(Acosh, acosh);\nMAKE_UNARY_FUNCTIONAL(Asin, asin);\nMAKE_UNARY_FUNCTIONAL(Asinh, asinh);\nMAKE_UNARY_FUNCTIONAL(Atan, atan);\nMAKE_UNARY_FUNCTIONAL(Atanh, atanh);\nMAKE_UNARY_FUNCTIONAL(Cbrt, cbrt);\nMAKE_UNARY_FUNCTIONAL(Conj, conj);\nMAKE_UNARY_FUNCTIONAL(Cos, cos);\nMAKE_UNARY_FUNCTIONAL(Cosh, cosh);\nMAKE_UNARY_FUNCTIONAL(Erf, erf);\nMAKE_UNARY_FUNCTIONAL(Exp, exp);\nMAKE_UNARY_FUNCTIONAL(Exp2, exp2);\nMAKE_UNARY_FUNCTIONAL(Fabs, fabs);\nMAKE_UNARY_FUNCTIONAL(Imag, imag);\nMAKE_UNARY_FUNCTIONAL(InvCbrt, invcbrt);\nMAKE_UNARY_FUNCTIONAL(InvSqrt, invsqrt);\nMAKE_UNARY_FUNCTIONAL(Log, log);\nMAKE_UNARY_FUNCTIONAL(Log10, log10);\nMAKE_UNARY_FUNCTIONAL(Log2, log2);\nMAKE_UNARY_FUNCTIONAL(Real, real);\nMAKE_UNARY_FUNCTIONAL(Sin, sin);\nMAKE_UNARY_FUNCTIONAL(Sinh, sinh);\nMAKE_UNARY_FUNCTIONAL(Sqrt, sqrt);\nMAKE_UNARY_FUNCTIONAL(StepFunction, step_function);\nMAKE_UNARY_FUNCTIONAL(Tan, tan);\nMAKE_UNARY_FUNCTIONAL(Tanh, tanh);\nMAKE_UNARY_FUNCTIONAL(Negate, -);\n\n/// Function for computing an integer power, forwards to template pow<N>()\ntemplate <int N, typename C0 = Identity>\nstruct UnaryPow;\n\n/// \\cond\ntemplate <int N, typename C0>\nstruct UnaryPow : Functional<C0::arity> {\n  template <class... Ts>\n  constexpr auto operator()(const Ts&... ts) {\n    return pow<N>(C0{}(ts...));\n  }\n};\n\ntemplate <int N>\nstruct UnaryPow<N, Identity> : Functional<1> {\n  template <class T0>\n  constexpr auto operator()(const T0& t0) {\n    return pow<N>(t0);\n  }\n};\n/// \\endcond\n\n/// Function for squaring a quantity\ntemplate <class C = Identity>\nstruct Square : Functional<C::arity> {\n  template <class... Ts>\n  constexpr auto operator()(const Ts&... ts) {\n    decltype(auto) result = C{}(ts...);\n    return result * result;\n  }\n};\n\n/// Function that applies `C` to every element of the operands. This function is\n/// currently only tested for `std::vector` operands. Operands other than the\n/// first may be a single value, which is applied element-wise to the vector. If\n/// needed, this function can be generalized further.\ntemplate <typename C>\nstruct ElementWise : Functional<C::arity> {\n  template <typename T0, typename... Ts>\n  auto operator()(const T0& t0, const Ts&... ts) {\n    const size_t size = get_size(t0);\n    ASSERT(((get_size(ts) == size or get_size(ts) == 1) and ...),\n           \"Sizes must be the same but got \"\n               << (std::vector<size_t>{size, get_size(ts)...}));\n    T0 result(size);\n    for (size_t i = 0; i < size; ++i) {\n      get_element(result, i) = C{}(get_element(t0, i), get_element(ts, i)...);\n    }\n    return result;\n  }\n};\n\n/// Function that merges two containers using the `merge` method of the first\n/// container. Can be used to collect data in a `std::map` in a reduction.\ntemplate <typename C0 = Identity, typename C1 = C0>\nstruct Merge : Functional<2> {\n  template <typename T>\n  T operator()(const T& t0, const T& t1) {\n    auto result = C0{}(t0);\n    auto to_merge = C1{}(t1);\n    result.merge(to_merge);\n    return result;\n  }\n};\n\n#undef MAKE_BINARY_FUNCTIONAL\n#undef MAKE_BINARY_INPLACE_OPERATOR\n#undef MAKE_BINARY_OPERATOR\n#undef MAKE_UNARY_FUNCTIONAL\n}  // namespace funcl\n"
  },
  {
    "path": "src/Utilities/GenerateInstantiations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/parameter/name.hpp>\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/control/iif.hpp>\n#include <boost/preprocessor/control/while.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/list/fold_left.hpp>\n#include <boost/preprocessor/list/fold_right.hpp>\n#include <boost/preprocessor/list/for_each_product.hpp>\n#include <boost/preprocessor/list/size.hpp>\n#include <boost/preprocessor/list/to_tuple.hpp>\n#include <boost/preprocessor/list/transform.hpp>\n#include <boost/preprocessor/logical/bitand.hpp>\n#include <boost/preprocessor/logical/bool.hpp>\n#include <boost/preprocessor/logical/compl.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/tuple/elem.hpp>\n#include <boost/preprocessor/tuple/reverse.hpp>\n#include <boost/preprocessor/tuple/size.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <boost/preprocessor/variadic/elem.hpp>\n#include <boost/preprocessor/variadic/to_list.hpp>\n\n/// \\cond\n#define GENERATE_INSTANTIATIONS_DO_PRODUCT(INSTANTIATION_MACRO, LIST_OF_LISTS) \\\n  BOOST_PP_LIST_FOR_EACH_PRODUCT(INSTANTIATION_MACRO,                          \\\n                                 BOOST_PP_LIST_SIZE(LIST_OF_LISTS),            \\\n                                 BOOST_PP_LIST_TO_TUPLE(LIST_OF_LISTS))\n\n#define GENERATE_INSTANTIATION_TUPLES_TO_LISTS(d, _, elem) \\\n  BOOST_PP_TUPLE_TO_LIST(BOOST_PP_TUPLE_SIZE(elem), elem)\n/// \\endcond\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Macro useful for generating many explicit instantiations of function\n * or class templates\n *\n * It is often necessary to generate explicit instantiations of function or\n * class templates. Since the total number of explicit instantiations scales as\n * the product of the number of possible number of parameter values of each\n * template parameter, this quickly becomes tedious. This macro allows you to\n * easily generate hundreds of explicit instantiations.\n *\n * The first argument to the macro is a macro that takes two arguments and is\n * described below. The remaining arguments are macro-tuples, e.g. `(1, 2, 3)`.\n * The Cartesian product of the macro-tuples is then computed and each term is\n * passed as a tuple as the second argument to the `INSTANTIATION_MACRO`. The\n * first argument to the `INSTANTIATION_MACRO` is a Boost.Preprocessor internal\n * variable so just make it `_`. The `INSTANTIATION(_, data)` macro below serves\n * as an example. A concrete example is generating explicit instantiations of\n * the class `Index<Dim>` for `Dim = 0,1,2,3`, which you would do as follows:\n *\n * \\code\n * #define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n *\n * #define INSTANTIATION(_, data)         \\\n *   template class Index<GET_DIM(data)>;\n *\n * GENERATE_INSTANTIATIONS(INSTANTIATION, (0, 1, 2, 3))\n *\n * #undef GET_DIM\n * #undef INSTANTIATION\n * \\endcode\n *\n * This will generate:\n *\n * \\code\n * template class Index<0>;\n * template class Index<1>;\n * template class Index<2>;\n * template class Index<3>;\n * \\endcode\n *\n * It is also possible to generate explicit instantiations for multiple classes\n * or functions in a single call to `GENERATE_INSTANTIATIONS`. For example, the\n * (in)equivalence operators can be generated using:\n *\n * \\code\n * #define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n * #define GEN_OP(op, dim)                            \\\n *   template bool operator op(const Index<dim>& lhs, \\\n *                             const Index<dim>& rhs);\n * #define INSTANTIATION(_, data)         \\\n *   template class Index<GET_DIM(data)>; \\\n *   GEN_OP(==, GET_DIM(data))            \\\n *   GEN_OP(!=, GET_DIM(data))\n *\n * GENERATE_INSTANTIATIONS(INSTANTIATION, (0, 1, 2, 3))\n *\n * #undef GET_DIM\n * #undef GEN_OP\n * #undef INSTANTIATION\n * \\endcode\n *\n * which will result in the instantiations:\n *\n * \\code\n * template class Index<0>;\n * template bool operator==(const Index<0>& lhs, const Index<0>& rhs);\n * template bool operator!=(const Index<0>& lhs, const Index<0>& rhs);\n * template class Index<1>;\n * template bool operator==(const Index<1>& lhs, const Index<1>& rhs);\n * template bool operator!=(const Index<1>& lhs, const Index<1>& rhs);\n * template class Index<2>;\n * template bool operator==(const Index<2>& lhs, const Index<2>& rhs);\n * template bool operator!=(const Index<2>& lhs, const Index<2>& rhs);\n * template class Index<3>;\n * template bool operator==(const Index<3>& lhs, const Index<3>& rhs);\n * template bool operator!=(const Index<3>& lhs, const Index<3>& rhs);\n * \\endcode\n *\n * Now let's look at generating instantiations of member function templates of\n * class templates, which will be a common use case. In this example we generate\n * explicit instantiations of all the member function templates of the class\n * `ScalarWave::Solutions::PlaneWave`. In total, for `Dim = 1,2,3` and types\n * `double` and `DataVector` this is about 42 explicit instantiations, which\n * would be extremely annoying to write by hand. The macro code is surprisingly\n * simple:\n *\n * \\code\n * #define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n * #define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n *\n * #define INSTANTIATE(_, data)                                              \\\n *   template Scalar<DTYPE(data)>                                            \\\n *   ScalarWave::Solutions::PlaneWave<DIM(data)>::psi(                       \\\n *       const tnsr::I<DTYPE(data), DIM(data)>& x, double t) const; \\\n *   template Scalar<DTYPE(data)>                                            \\\n *   ScalarWave::Solutions::PlaneWave<DIM(data)>::dpsi_dt(                   \\\n *       const tnsr::I<DTYPE(data), DIM(data)>& x, double t) const;\n *\n * GENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (double, DataVector))\n *\n * #undef DIM\n * #undef DTYPE\n * #undef INSTANTIATE\n * \\endcode\n *\n * We don't show the result from preprocessor since for all of the member\n * functions of `PlaneWave` the total output is approximately 150 lines, but you\n * can hopefully see the benefits of generating explicit instantiations using\n * the `GENERATE_INSTANTIATIONS` way.\n *\n * One thing that can be difficult is debugging metaprograms (be they template\n * or macro-based). To this end we provide a make target `DebugPreprocessor`\n * which prints the output of running the preprocessor on the file\n * `src/Executables/DebugPreprocessor/DebugPreprocessor.cpp`.\n * Note that the output of the `GENERATE_INSTANTIATIONS` macro will be on a\n * single line, so it often proves useful to copy-paste the output into an\n * editor and run clang-format over the code so it's easier to reason about.\n */\n#define GENERATE_INSTANTIATIONS(INSTANTIATION_MACRO, ...)                \\\n  GENERATE_INSTANTIATIONS_DO_PRODUCT(                                    \\\n      INSTANTIATION_MACRO,                                               \\\n      BOOST_PP_LIST_TRANSFORM(GENERATE_INSTANTIATION_TUPLES_TO_LISTS, _, \\\n                              BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__)))\n"
  },
  {
    "path": "src/Utilities/GetOutput.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <sstream>\n#include <string>\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Get the streamed output of `t` as a `std::string`\n */\ntemplate <typename T>\nstd::string get_output(const T& t) {\n  std::ostringstream os;\n  os << t;\n  return os.str();\n}\n"
  },
  {
    "path": "src/Utilities/Gsl.hpp",
    "content": "\n/// \\file\n/// Defines functions and classes from the GSL\n\n#pragma once\n\n#pragma GCC system_header\n\n// The code in this file is adapted from Microsoft's GSL that can be found at\n// https://github.com/Microsoft/GSL\n// The original license and copyright are:\n///////////////////////////////////////////////////////////////////////////////\n//\n// Copyright (c) 2015 Microsoft Corporation. All rights reserved.\n//\n// This code is licensed under the MIT License (MIT).\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n///////////////////////////////////////////////////////////////////////////////\n// The code changes are because SpECTRE is not allowed to throw under any\n// circumstances that cannot be guaranteed to be caught and so all throw's\n// are replaced by hard errors (ERROR).\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <stdexcept>\n#include <type_traits>\n#include <utility>\n\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/ErrorHandling/ExpectsAndEnsures.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n#if defined(__clang__) || defined(__GNUC__)\n\n/*!\n * \\ingroup UtilitiesGroup\n * The if statement is expected to evaluate true most of the time\n */\n#define LIKELY(x) __builtin_expect(!!(x), 1)\n\n/*!\n * \\ingroup UtilitiesGroup\n * The if statement is expected to evaluate false most of the time\n */\n#define UNLIKELY(x) __builtin_expect(!!(x), 0)\n\n#else\n/*!\n * \\ingroup UtilitiesGroup\n * The if statement is expected to evaluate true most of the time\n */\n#define LIKELY(x) (x)\n\n/*!\n * \\ingroup UtilitiesGroup\n * The if statement is expected to evaluate false most of the time\n */\n#define UNLIKELY(x) (x)\n#endif\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Implementations from the Guideline Support Library\n */\nnamespace gsl {\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Cast `u` to a type `T` where the cast may result in narrowing\n */\ntemplate <class T, class U>\nSPECTRE_ALWAYS_INLINE constexpr T narrow_cast(U&& u) {\n  return static_cast<T>(std::forward<U>(u));\n}\n\nnamespace gsl_detail {\ntemplate <class T, class U>\nstruct is_same_signedness\n    : public std::integral_constant<bool, std::is_signed<T>::value ==\n                                              std::is_signed<U>::value> {};\n}  // namespace gsl_detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief A checked version of narrow_cast() that ERRORs if the cast changed\n * the value\n */\ntemplate <class T, class U>\nSPECTRE_ALWAYS_INLINE T narrow(U u) {\n  T t = narrow_cast<T>(u);\n  if (static_cast<U>(t) != u) {\n    ERROR(\"Failed to cast \" << u << \" of type \" << pretty_type::get_name<U>()\n                            << \" to type \" << pretty_type::get_name<T>());\n  }\n  if (not gsl_detail::is_same_signedness<T, U>::value and\n      ((t < T{}) != (u < U{}))) {\n    ERROR(\"Failed to cast \" << u << \" of type \" << pretty_type::get_name<U>()\n                            << \" to type \" << pretty_type::get_name<T>());\n  }\n  return t;\n}\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Retrieve a entry from a container, with checks in Debug mode that\n * the index being retrieved is valid.\n */\ntemplate <class T, std::size_t N, typename Size>\nSPECTRE_ALWAYS_INLINE constexpr T& at(std::array<T, N>& arr, Size index) {\n  Expects(index >= 0 and index < narrow_cast<Size>(N));\n  return arr[static_cast<std::size_t>(index)];\n}\n\ntemplate <class T, std::size_t N, typename Size>\nSPECTRE_ALWAYS_INLINE constexpr T& at(cpp20::array<T, N>& arr, Size index) {\n  Expects(index >= 0 and index < narrow_cast<Size>(N));\n  return arr[static_cast<std::size_t>(index)];\n}\n\ntemplate <class T, std::size_t N, typename Size>\nSPECTRE_ALWAYS_INLINE constexpr const T& at(const cpp20::array<T, N>& arr,\n                                            Size index) {\n  Expects(index >= 0 and index < narrow_cast<Size>(N));\n  return arr[static_cast<std::size_t>(index)];\n}\n\ntemplate <class Cont, typename Size>\nSPECTRE_ALWAYS_INLINE constexpr const typename Cont::value_type& at(\n    const Cont& cont, Size index) {\n  Expects(index >= 0 and index < narrow_cast<Size>(cont.size()));\n  return cont[static_cast<typename Cont::size_type>(index)];\n}\n\ntemplate <class T, typename Size>\nSPECTRE_ALWAYS_INLINE constexpr const T& at(std::initializer_list<T> cont,\n                                            Size index) {\n  Expects(index >= 0 and index < narrow_cast<Size>(cont.size()));\n  return *(cont.begin() + index);\n}\n/// @}\n\nnamespace detail {\ntemplate <class T>\nstruct owner_impl {\n  static_assert(std::is_same<T, const owner_impl<int*>&>::value,\n                \"You should not have an owning raw pointer, instead you should \"\n                \"use std::unique_ptr or, sparingly, std::shared_ptr. If \"\n                \"clang-tidy told you to use gsl::owner, then you should still \"\n                \"use std::unique_ptr instead.\");\n  using type = T;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Mark a raw pointer as owning its data\n *\n * \\warning You should never actually use `gsl::owner`. Instead you should use\n * `std::unique_ptr`, and if shared ownership is required, `std::shared_ptr`.\n */\ntemplate <class T, Requires<std::is_pointer<T>::value> = nullptr>\nusing owner = typename detail::owner_impl<T>::type;\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Require a pointer to not be a `nullptr`\n *\n * Restricts a pointer or smart pointer to only hold non-null values.\n *\n * Has zero size overhead over `T`.\n *\n * If `T` is a pointer (i.e. `T == U*`) then\n * - allow construction from `U*`\n * - disallow construction from `nullptr_t`\n * - disallow default construction\n * - ensure construction from null `U*` fails\n * - allow implicit conversion to `U*`\n */\ntemplate <class T>\nclass not_null {\n public:\n  static_assert(std::is_assignable<T&, std::nullptr_t>::value,\n                \"T cannot be assigned nullptr.\");\n\n  template <typename U, Requires<std::is_convertible<U, T>::value> = nullptr>\n  constexpr not_null(U&& u) : ptr_(std::forward<U>(u)) {\n    Expects(ptr_ != nullptr);\n  }\n\n  template <typename U, Requires<std::is_convertible<U, T>::value> = nullptr>\n  constexpr not_null(const not_null<U>& other) : not_null(other.get()) {}\n\n  not_null(const not_null& other) = default;\n  not_null& operator=(const not_null& other) = default;\n\n  constexpr T get() const {\n    Ensures(ptr_ != nullptr);\n    return ptr_;\n  }\n\n  constexpr operator T() const { return get(); }\n  constexpr T operator->() const { return get(); }\n  constexpr decltype(auto) operator*() const { return *get(); }\n\n  // prevents compilation when someone attempts to assign a null pointer\n  // constant\n  not_null(std::nullptr_t) = delete;\n  not_null& operator=(std::nullptr_t) = delete;\n\n  // unwanted operators...pointers only point to single objects!\n  not_null& operator++() = delete;\n  not_null& operator--() = delete;\n  not_null operator++(int) = delete;\n  not_null operator--(int) = delete;\n  not_null& operator+=(std::ptrdiff_t) = delete;\n  not_null& operator-=(std::ptrdiff_t) = delete;\n  void operator[](std::ptrdiff_t) const = delete;\n\n private:\n  T ptr_;\n};\n\ntemplate <class T>\nstd::ostream& operator<<(std::ostream& os, const not_null<T>& val) {\n  os << val.get();\n  return os;\n}\n\ntemplate <class T, class U>\nauto operator==(const not_null<T>& lhs, const not_null<U>& rhs)\n    -> decltype(lhs.get() == rhs.get()) {\n  return lhs.get() == rhs.get();\n}\n\ntemplate <class T, class U>\nauto operator!=(const not_null<T>& lhs, const not_null<U>& rhs)\n    -> decltype(lhs.get() != rhs.get()) {\n  return lhs.get() != rhs.get();\n}\n\ntemplate <class T, class U>\nauto operator<(const not_null<T>& lhs, const not_null<U>& rhs)\n    -> decltype(lhs.get() < rhs.get()) {\n  return lhs.get() < rhs.get();\n}\n\ntemplate <class T, class U>\nauto operator<=(const not_null<T>& lhs, const not_null<U>& rhs)\n    -> decltype(lhs.get() <= rhs.get()) {\n  return lhs.get() <= rhs.get();\n}\n\ntemplate <class T, class U>\nauto operator>(const not_null<T>& lhs, const not_null<U>& rhs)\n    -> decltype(lhs.get() > rhs.get()) {\n  return lhs.get() > rhs.get();\n}\n\ntemplate <class T, class U>\nauto operator>=(const not_null<T>& lhs, const not_null<U>& rhs)\n    -> decltype(lhs.get() >= rhs.get()) {\n  return lhs.get() >= rhs.get();\n}\n\n// more unwanted operators\ntemplate <class T, class U>\nstd::ptrdiff_t operator-(const not_null<T>&, const not_null<U>&) = delete;\ntemplate <class T>\nnot_null<T> operator-(const not_null<T>&, std::ptrdiff_t) = delete;\ntemplate <class T>\nnot_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;\ntemplate <class T>\nnot_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;\n\n// GCC 7 does not like the signed unsigned missmatch (size_t ptrdiff_t)\n// While there is a conversion from signed to unsigned, it happens at\n// compile time, so the compiler wouldn't have to warn indiscriminently, but\n// could check if the source value actually doesn't fit into the target type\n// and only warn in those cases.\n#if __GNUC__ > 6\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"\n#endif  // __GNUC__ > 6\n\n// [views.constants], constants\nconstexpr const std::ptrdiff_t dynamic_extent = -1;\n\ntemplate <class ElementType, std::ptrdiff_t Extent = dynamic_extent>\nclass span;\n\n// implementation details\nnamespace detail {\ntemplate <class T>\nstruct is_span_oracle : std::false_type {};\n\ntemplate <class ElementType, std::ptrdiff_t Extent>\nstruct is_span_oracle<gsl::span<ElementType, Extent>> : std::true_type {};\n\ntemplate <class T>\nstruct is_span : public is_span_oracle<std::remove_cv_t<T>> {};\n\ntemplate <class T>\nstruct is_std_array_oracle : std::false_type {};\n\ntemplate <class ElementType, std::size_t Extent>\nstruct is_std_array_oracle<std::array<ElementType, Extent>> : std::true_type {};\n\ntemplate <class T>\nstruct is_std_array : public is_std_array_oracle<std::remove_cv_t<T>> {};\n\ntemplate <std::ptrdiff_t From, std::ptrdiff_t To>\nstruct is_allowed_extent_conversion\n    : public std::integral_constant<bool, From == To ||\n                                              From == gsl::dynamic_extent ||\n                                              To == gsl::dynamic_extent> {};\n\ntemplate <class From, class To>\nstruct is_allowed_element_type_conversion\n    : public std::integral_constant<\n          bool, std::is_convertible<From (*)[], To (*)[]>::value> {};\n\ntemplate <class Span, bool IsConst>\nclass span_iterator {\n  using element_type_ = typename Span::element_type;\n\n public:\n  using iterator_category = std::random_access_iterator_tag;\n  using value_type = std::remove_cv_t<element_type_>;\n  using difference_type = typename Span::index_type;\n\n  using reference =\n      tmpl::conditional_t<IsConst, const element_type_, element_type_>&;\n  using pointer = std::add_pointer_t<reference>;\n\n  span_iterator() = default;\n\n  constexpr span_iterator(const Span* span, typename Span::index_type idx)\n      : span_(span), index_(idx) {}\n\n  friend span_iterator<Span, true>;\n  template <bool B, Requires<!B && IsConst> = nullptr>\n  constexpr span_iterator(const span_iterator<Span, B>& other)\n      : span_iterator(other.span_, other.index_) {}\n\n  constexpr reference operator*() const {\n    Expects(index_ != span_->size());\n    return *(span_->data() + index_);\n  }\n\n  constexpr pointer operator->() const {\n    Expects(index_ != span_->size());\n    return span_->data() + index_;\n  }\n\n  constexpr span_iterator& operator++() {\n    Expects(0 <= index_ && index_ != span_->size());\n    ++index_;\n    return *this;\n  }\n\n  constexpr span_iterator operator++(int) {\n    auto ret = *this;\n    ++(*this);\n    return ret;\n  }\n\n  constexpr span_iterator& operator--() {\n    Expects(index_ != 0 && index_ <= span_->size());\n    --index_;\n    return *this;\n  }\n\n  constexpr span_iterator operator--(int) {\n    auto ret = *this;\n    --(*this);\n    return ret;\n  }\n\n  constexpr span_iterator operator+(difference_type n) const {\n    auto ret = *this;\n    return ret += n;\n  }\n\n  friend constexpr span_iterator operator+(difference_type n,\n                                           span_iterator const& rhs) {\n    return rhs + n;\n  }\n\n  constexpr span_iterator& operator+=(difference_type n) {\n    Expects((index_ + n) >= 0 && (index_ + n) <= span_->size());\n    index_ += n;\n    return *this;\n  }\n\n  constexpr span_iterator operator-(difference_type n) const {\n    auto ret = *this;\n    return ret -= n;\n  }\n\n  constexpr span_iterator& operator-=(difference_type n) { return *this += -n; }\n\n  constexpr difference_type operator-(span_iterator rhs) const {\n    Expects(span_ == rhs.span_);\n    return index_ - rhs.index_;\n  }\n\n  constexpr reference operator[](difference_type n) const {\n    return *(*this + n);\n  }\n\n  constexpr friend bool operator==(span_iterator lhs, span_iterator rhs) {\n    return lhs.span_ == rhs.span_ && lhs.index_ == rhs.index_;\n  }\n\n  constexpr friend bool operator!=(span_iterator lhs, span_iterator rhs) {\n    return !(lhs == rhs);\n  }\n\n  constexpr friend bool operator<(span_iterator lhs, span_iterator rhs) {\n    return lhs.index_ < rhs.index_;\n  }\n\n  constexpr friend bool operator<=(span_iterator lhs, span_iterator rhs) {\n    return !(rhs < lhs);\n  }\n\n  constexpr friend bool operator>(span_iterator lhs, span_iterator rhs) {\n    return rhs < lhs;\n  }\n\n  constexpr friend bool operator>=(span_iterator lhs, span_iterator rhs) {\n    return !(rhs > lhs);\n  }\n\n protected:\n  const Span* span_ = nullptr;\n  std::ptrdiff_t index_ = 0;\n};\n\ntemplate <std::ptrdiff_t Ext>\nclass extent_type {\n public:\n  using index_type = std::ptrdiff_t;\n\n  static_assert(Ext >= 0, \"A fixed-size span must be >= 0 in size.\");\n\n  constexpr extent_type() {}\n\n  template <index_type Other>\n  constexpr extent_type(extent_type<Other> ext) {\n    static_assert(\n        Other == Ext || Other == dynamic_extent,\n        \"Mismatch between fixed-size extent and size of initializing data.\");\n    Expects(ext.size() == Ext);\n  }\n\n  constexpr extent_type(index_type size) { Expects(size == Ext); }\n\n  constexpr index_type size() const { return Ext; }\n};\n\ntemplate <>\nclass extent_type<dynamic_extent> {\n public:\n  using index_type = std::ptrdiff_t;\n\n  template <index_type Other>\n  explicit constexpr extent_type(extent_type<Other> ext) : size_(ext.size()) {}\n\n  explicit constexpr extent_type(index_type size) : size_(size) {\n    Expects(size >= 0);\n  }\n\n  constexpr index_type size() const { return size_; }\n\n private:\n  index_type size_;\n};\n\ntemplate <class ElementType, std::ptrdiff_t Extent, std::ptrdiff_t Offset,\n          std::ptrdiff_t Count>\nstruct calculate_subspan_type {\n  using type =\n      span<ElementType,\n           Count != dynamic_extent\n               ? Count\n               : (Extent != dynamic_extent ? Extent - Offset : Extent)>;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Create a span/view on a range, which is cheap to copy (one pointer).\n */\ntemplate <class ElementType, std::ptrdiff_t Extent>\nclass span {\n public:\n  // constants and types\n  using element_type = ElementType;\n  using value_type = std::remove_cv_t<ElementType>;\n  using index_type = size_t;\n  using pointer = element_type*;\n  using reference = element_type&;\n\n  using iterator = detail::span_iterator<span<ElementType, Extent>, false>;\n  using const_iterator = detail::span_iterator<span<ElementType, Extent>, true>;\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n\n  using size_type = index_type;\n\n  static constexpr index_type extent{Extent};\n\n  // [span.cons], span constructors, copy, assignment, and destructor\n  template <bool Dependent = false,\n            // \"Dependent\" is needed to make \"Requires<Dependent ||\n            // Extent <= 0>\" SFINAE, since \"Requires<Extent <= 0>\" is\n            // ill-formed when Extent is greater than 0.\n            Requires<(Dependent || Extent <= 0)> = nullptr>\n  constexpr span() : storage_(nullptr, detail::extent_type<0>()) {}\n\n  constexpr span(pointer ptr, index_type count) : storage_(ptr, count) {}\n\n  constexpr span(pointer firstElem, pointer lastElem)\n      : storage_(firstElem, std::distance(firstElem, lastElem)) {}\n\n  template <std::size_t N>\n  constexpr span(element_type (&arr)[N])\n      : storage_(KnownNotNull{std::addressof(arr[0])},\n                 detail::extent_type<N>()) {}\n\n  template <std::size_t N, Requires<(N > 0)> = nullptr>\n  constexpr span(std::array<std::remove_const_t<element_type>, N>& arr)\n      : storage_(KnownNotNull{arr.data()}, detail::extent_type<N>()) {}\n\n  constexpr span(std::array<std::remove_const_t<element_type>, 0>&)\n      : storage_(static_cast<pointer>(nullptr), detail::extent_type<0>()) {}\n\n  template <std::size_t N, Requires<(N > 0)> = nullptr>\n  constexpr span(const std::array<std::remove_const_t<element_type>, N>& arr)\n      : storage_(KnownNotNull{arr.data()}, detail::extent_type<N>()) {}\n\n  constexpr span(const std::array<std::remove_const_t<element_type>, 0>&)\n      : storage_(static_cast<pointer>(nullptr), detail::extent_type<0>()) {}\n\n  // NB: the SFINAE here uses .data() as a incomplete/imperfect proxy for the\n  // requirement on Container to be a contiguous sequence container.\n  template <\n      class Container,\n      Requires<\n          !detail::is_span<Container>::value &&\n          !detail::is_std_array<Container>::value &&\n          std::is_convertible<typename Container::pointer, pointer>::value &&\n          std::is_convertible<\n              typename Container::pointer,\n              decltype(std::declval<Container>().data())>::value> = nullptr>\n  constexpr span(Container& cont)\n      : span(cont.data(), narrow<index_type>(cont.size())) {}\n\n  template <\n      class Container,\n      Requires<\n          std::is_const<element_type>::value &&\n          !detail::is_span<Container>::value &&\n          std::is_convertible<typename Container::pointer, pointer>::value &&\n          std::is_convertible<\n              typename Container::pointer,\n              decltype(std::declval<Container>().data())>::value> = nullptr>\n  constexpr span(const Container& cont)\n      : span(cont.data(), narrow<index_type>(cont.size())) {}\n\n  constexpr span(const span& other) = default;\n\n  template <class OtherElementType, std::ptrdiff_t OtherExtent,\n            Requires<detail::is_allowed_extent_conversion<OtherExtent,\n                                                          Extent>::value &&\n                     detail::is_allowed_element_type_conversion<\n                         OtherElementType, element_type>::value> = nullptr>\n  constexpr span(const span<OtherElementType, OtherExtent>& other)\n      : storage_(other.data(), detail::extent_type<OtherExtent>(other.size())) {\n  }\n\n  ~span() = default;\n  constexpr span& operator=(const span& other) = default;\n\n  // [span.sub], span subviews\n  template <std::ptrdiff_t Count>\n  constexpr span<element_type, Count> first() const {\n    Expects(Count >= 0 && Count <= size());\n    return {data(), Count};\n  }\n\n  template <std::ptrdiff_t Count>\n  constexpr span<element_type, Count> last() const {\n    Expects(Count >= 0 && size() - Count >= 0);\n    return {data() + (size() - Count), Count};\n  }\n\n  template <std::ptrdiff_t Offset, std::ptrdiff_t Count = dynamic_extent>\n  constexpr auto subspan() const ->\n      typename detail::calculate_subspan_type<ElementType, Extent, Offset,\n                                              Count>::type {\n    Expects(\n        (Offset >= 0 && size() - Offset >= 0) &&\n        (Count == dynamic_extent || (Count >= 0 && Offset + Count <= size())));\n\n    return {data() + Offset, Count == dynamic_extent ? size() - Offset : Count};\n  }\n\n  constexpr span<element_type, dynamic_extent> first(index_type count) const {\n    Expects(count >= 0 && count <= size());\n    return {data(), count};\n  }\n\n  constexpr span<element_type, dynamic_extent> last(index_type count) const {\n    return make_subspan(size() - count, dynamic_extent,\n                        subspan_selector<Extent>{});\n  }\n\n  constexpr span<element_type, dynamic_extent> subspan(\n      index_type offset, index_type count = dynamic_extent) const {\n    return make_subspan(offset, count, subspan_selector<Extent>{});\n  }\n\n  // [span.obs], span observers\n  constexpr index_type size() const { return storage_.size(); }\n  constexpr index_type size_bytes() const {\n    return size() * narrow_cast<index_type>(sizeof(element_type));\n  }\n  constexpr bool empty() const { return size() == 0; }\n\n  // [span.elem], span element access\n  constexpr reference operator[](index_type idx) const {\n    Expects(CheckRange(idx, storage_.size()));\n    return data()[idx];\n  }\n\n  constexpr reference at(index_type idx) const { return this->operator[](idx); }\n  constexpr reference operator()(index_type idx) const {\n    return this->operator[](idx);\n  }\n  constexpr pointer data() const { return storage_.data(); }\n\n  // [span.iter], span iterator support\n  constexpr iterator begin() const { return {this, 0}; }\n  constexpr iterator end() const { return {this, size()}; }\n\n  constexpr const_iterator cbegin() const { return {this, 0}; }\n  constexpr const_iterator cend() const { return {this, size()}; }\n\n  constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; }\n  constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; }\n\n  constexpr const_reverse_iterator crbegin() const {\n    return const_reverse_iterator{cend()};\n  }\n  constexpr const_reverse_iterator crend() const {\n    return const_reverse_iterator{cbegin()};\n  }\n\n private:\n  static bool CheckRange(index_type idx, index_type size) {\n    // Optimization:\n    //\n    // idx >= 0 && idx < size\n    // =>\n    // static_cast<size_t>(idx) < static_cast<size_t>(size)\n    //\n    // because size >=0 by span construction, and negative idx will\n    // wrap around to a value always greater than size when casted.\n\n    // check if we have enough space to wrap around\n    if (sizeof(index_type) <= sizeof(size_t)) {\n      return narrow_cast<size_t>(idx) < narrow_cast<size_t>(size);\n    } else {\n      return idx >= 0 && idx < size;\n    }\n  }\n\n  // Needed to remove unnecessary null check in subspans\n  struct KnownNotNull {\n    pointer p;\n  };\n\n  // this implementation detail class lets us take advantage of the\n  // empty base class optimization to pay for only storage of a single\n  // pointer in the case of fixed-size spans\n  template <class ExtentType>\n  class storage_type : public ExtentType {\n   public:\n    // KnownNotNull parameter is needed to remove unnecessary null check\n    // in subspans and constructors from arrays\n    template <class OtherExtentType>\n    constexpr storage_type(KnownNotNull data, OtherExtentType ext)\n        : ExtentType(ext), data_(data.p) {\n      Expects(ExtentType::size() >= 0);\n    }\n\n    template <class OtherExtentType>\n    constexpr storage_type(pointer data, OtherExtentType ext)\n        : ExtentType(ext), data_(data) {\n      Expects(ExtentType::size() >= 0);\n      Expects(data || ExtentType::size() == 0);\n    }\n\n    constexpr pointer data() const { return data_; }\n\n   private:\n    pointer data_;\n  };\n\n  storage_type<detail::extent_type<Extent>> storage_;\n\n  // The rest is needed to remove unnecessary null check\n  // in subspans and constructors from arrays\n  constexpr span(KnownNotNull ptr, index_type count) : storage_(ptr, count) {}\n\n  template <std::ptrdiff_t CallerExtent>\n  class subspan_selector {};\n\n  template <std::ptrdiff_t CallerExtent>\n  span<element_type, dynamic_extent> make_subspan(\n      index_type offset, index_type count,\n      subspan_selector<CallerExtent>) const {\n    const span<element_type, dynamic_extent> tmp(*this);\n    return tmp.subspan(offset, count);\n  }\n\n  span<element_type, dynamic_extent> make_subspan(\n      index_type offset, index_type count,\n      subspan_selector<dynamic_extent>) const {\n    Expects(offset >= 0 && size() - offset >= 0);\n\n    if (count == dynamic_extent) {\n      return {KnownNotNull{data() + offset}, size() - offset};\n    }\n\n    Expects(count >= 0 && size() - offset >= count);\n    return {KnownNotNull{data() + offset}, count};\n  }\n};\n\n// [span.comparison], span comparison operators\ntemplate <class ElementType, std::ptrdiff_t FirstExtent,\n          std::ptrdiff_t SecondExtent>\nconstexpr bool operator==(span<ElementType, FirstExtent> l,\n                          span<ElementType, SecondExtent> r) {\n  return std::equal(l.begin(), l.end(), r.begin(), r.end());\n}\n\ntemplate <class ElementType, std::ptrdiff_t Extent>\nconstexpr bool operator!=(span<ElementType, Extent> l,\n                          span<ElementType, Extent> r) {\n  return !(l == r);\n}\n\ntemplate <class ElementType, std::ptrdiff_t Extent>\nconstexpr bool operator<(span<ElementType, Extent> l,\n                         span<ElementType, Extent> r) {\n  return std::lexicographical_compare(l.begin(), l.end(), r.begin(), r.end());\n}\n\ntemplate <class ElementType, std::ptrdiff_t Extent>\nconstexpr bool operator<=(span<ElementType, Extent> l,\n                          span<ElementType, Extent> r) {\n  return !(l > r);\n}\n\ntemplate <class ElementType, std::ptrdiff_t Extent>\nconstexpr bool operator>(span<ElementType, Extent> l,\n                         span<ElementType, Extent> r) {\n  return r < l;\n}\n\ntemplate <class ElementType, std::ptrdiff_t Extent>\nconstexpr bool operator>=(span<ElementType, Extent> l,\n                          span<ElementType, Extent> r) {\n  return !(l < r);\n}\n\n/// @{\n/// \\ingroup UtilitiesGroup\n/// Utility function for creating spans\ntemplate <class ElementType>\nconstexpr span<ElementType> make_span(\n    ElementType* ptr, typename span<ElementType>::index_type count) {\n  return span<ElementType>(ptr, count);\n}\n\ntemplate <class ElementType>\nconstexpr span<ElementType> make_span(ElementType* firstElem,\n                                      ElementType* lastElem) {\n  return span<ElementType>(firstElem, lastElem);\n}\n\ntemplate <class ElementType, std::size_t N>\nconstexpr span<ElementType, N> make_span(ElementType (&arr)[N]) {\n  return span<ElementType, N>(arr);\n}\n\ntemplate <class Container>\nconstexpr span<typename Container::value_type> make_span(Container& cont) {\n  return span<typename Container::value_type>(cont);\n}\n\ntemplate <class Container>\nconstexpr span<const typename Container::value_type> make_span(\n    const Container& cont) {\n  return span<const typename Container::value_type>(cont);\n}\n\ntemplate <class Ptr>\nconstexpr span<typename Ptr::element_type> make_span(Ptr& cont,\n                                                     std::ptrdiff_t count) {\n  return span<typename Ptr::element_type>(cont, count);\n}\n\ntemplate <class Ptr>\nconstexpr span<typename Ptr::element_type> make_span(Ptr& cont) {\n  return span<typename Ptr::element_type>(cont);\n}\n/// @}\n\n// Specialization of gsl::at for span\ntemplate <class ElementType, std::ptrdiff_t Extent>\nconstexpr ElementType& at(span<ElementType, Extent> s,\n                          typename span<ElementType, Extent>::index_type i) {\n  // No bounds checking here because it is done in span::operator[] called below\n  return s[i];\n}\n\n#if __GNUC__ > 6\n#pragma GCC diagnostic pop\n#endif  // __GNUC__ > 6\n\ntemplate <class ElementType, std::ptrdiff_t Extent>\nstd::ostream& operator<<(std::ostream& os, const span<ElementType, Extent> t) {\n  os << \"(\";\n  auto it = t.cbegin();\n  if (it != t.cend()) {\n    os << *it;\n    ++it;\n    for (; it != t.cend(); ++it) {\n      os << \",\";\n      os << *it;\n    }\n  }\n  return os << \")\";\n}\n}  // namespace gsl\n\n// The remainder of this file is\n// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// Construct a not_null from a pointer.  Often this will be done as\n/// an implicit conversion, but it may be necessary to perform the\n/// conversion explicitly when type deduction is desired.\n///\n/// \\note This is not a standard GSL function, and so is not in the\n/// gsl namespace.\ntemplate <typename T>\nconstexpr gsl::not_null<T*> make_not_null(T* ptr) {\n  return gsl::not_null<T*>(ptr);\n}\n"
  },
  {
    "path": "src/Utilities/Kokkos/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SpectreKokkos)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  KokkosCore.hpp\n)\n\nif(TARGET Kokkos::kokkos)\n  target_link_libraries(\n    ${LIBRARY}\n    INTERFACE\n    Kokkos::kokkos\n  )\nendif()\n"
  },
  {
    "path": "src/Utilities/Kokkos/KokkosCore.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#if __has_include(<Kokkos_Core.hpp>)\n#include <Kokkos_Core.hpp>\n\n/// \\brief If defined then SpECTRE is using Kokkos\n#define SPECTRE_KOKKOS 1\n\n#else  // #if __has_include(<Kokkos_Core.hpp>)\n#define KOKKOS_FUNCTION\n#define KOKKOS_INLINE_FUNCTION\n#endif  // #if __has_include(<Kokkos_Core.hpp>)\n"
  },
  {
    "path": "src/Utilities/Literals.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines useful literals\n\n#pragma once\n\n#include <string>\n\nusing namespace std::literals::string_literals;  // NOLINT\n\n/// \\ingroup UtilitiesGroup\n/// Defines the _st size_t suffix\ninline constexpr size_t operator\"\"_st(const unsigned long long n) {  // NOLINT\n  return static_cast<size_t>(n);\n}\n"
  },
  {
    "path": "src/Utilities/MakeArray.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function make_array.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <utility>\n\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n// Much of this is taken from\n// http://stackoverflow.com/questions/1065774/initialization-of\n//     -a-normal-array-with-one-default-value\n// with some modifications.\nnamespace MakeArray_detail {\n// We handle the zero size case separately below because both for size zero\n// and size one arrays the index_sequence is empty.\n// We use the index_sequence to be able to fill the first Size-1 (which is\n// sizeof...(Is)) via constructor calls `T(args...)`. The final element is\n// forwarded to avoid a possible copy if an rvalue reference is passed to\n// make_array. The generic implementation handles both the make_array(T&&) case\n// and the make_array(Args&&...) case below.\n// The (void)Is cast is to avoid any potential trouble with overloaded comma\n// operators.\ntemplate <bool SizeZero>\nstruct MakeArray {\n  template <typename T, typename... Args, size_t... Is>\n  static SPECTRE_ALWAYS_INLINE constexpr std::array<T, sizeof...(Is) + 1> apply(\n      std::index_sequence<Is...> /* unused */, Args&&... args) {\n    return {{((void)Is, T(args...))..., T(std::forward<Args>(args)...)}};\n  }\n};\n\ntemplate <>\nstruct MakeArray<true> {\n  template <typename T, typename... Args>\n  static SPECTRE_ALWAYS_INLINE constexpr std::array<T, 0> apply(\n      std::index_sequence<> /* unused */, Args&&... args) {\n#ifndef HAVE_BROKEN_ARRAY0\n    expand_pack(args...);  // Used in other preprocessor branch\n    return std::array<T, 0>{{}};\n#else  // HAVE_BROKEN_ARRAY0\n    // https://bugs.llvm.org/show_bug.cgi?id=35491\n    return {{T(std::forward<Args>(args)...)}};\n#endif  // HAVE_BROKEN_ARRAY0\n  }\n};\n}  // namespace MakeArray_detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Create a `std::array<T, Size>{{T(args...), T(args...), ...}}`\n * \\tparam Size the size of the array\n * \\tparam T the type of the element in the array\n */\ntemplate <size_t Size, typename T, typename... Args>\nSPECTRE_ALWAYS_INLINE constexpr std::array<T, Size> make_array(Args&&... args) {\n  return MakeArray_detail::MakeArray<Size == 0>::template apply<T>(\n      std::make_index_sequence<(Size == 0 ? Size : Size - 1)>{},\n      std::forward<Args>(args)...);\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Create a `std::array<std::decay_t<T>, Size>{{t, t, ...}}`\n * \\tparam Size the size of the array\n */\ntemplate <size_t Size, typename T>\nSPECTRE_ALWAYS_INLINE constexpr auto make_array(T&& t)\n    -> std::array<std::decay_t<T>, Size> {\n  return MakeArray_detail::MakeArray<Size == 0>::template apply<\n      std::decay_t<T>>(\n      std::make_index_sequence<(Size == 0 ? Size : Size - 1)>{},\n      std::forward<T>(t));\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Helper function to initialize a std::array with varying number of\n * arguments\n */\ntemplate <typename T, typename... V, Requires<(sizeof...(V) > 0)> = nullptr>\nSPECTRE_ALWAYS_INLINE constexpr auto make_array(T&& t, V&&... values)\n    -> std::array<typename std::decay_t<T>, sizeof...(V) + 1> {\n  static_assert(\n      tmpl2::flat_all_v<std::is_same_v<std::decay_t<T>, std::decay_t<V>>...>,\n      \"all types to make_array(...) must be the same\");\n  return std::array<std::decay_t<T>, sizeof...(V) + 1>{\n      {std::forward<T>(t), std::forward<V>(values)...}};\n}\n\nnamespace MakeArray_detail {\ntemplate <typename Seq, typename T,\n          Requires<std::is_rvalue_reference_v<Seq>> = nullptr>\nconstexpr T&& forward_element(T& t) {\n  return std::move(t);\n}\ntemplate <typename Seq, typename T,\n          Requires<not std::is_rvalue_reference_v<Seq>> = nullptr>\nconstexpr T& forward_element(T& t) {\n  return t;\n}\n\ntemplate <typename T, size_t size, typename Seq, size_t... indexes>\nconstexpr std::array<T, size> make_array_from_iterator_impl(\n    Seq&& s, std::integer_sequence<size_t, indexes...> /*meta*/) {\n  // clang-tidy: do not use pointer arithmetic\n  return std::array<T, size>{\n      {forward_element<decltype(s)>(*(std::begin(s) + indexes))...}};  // NOLINT\n}\n}  // namespace MakeArray_detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Create an `std::array<T, size>` from the first `size` values of `seq`\n *\n * \\requires `Seq` has a `begin` function\n * \\tparam T the type held by the array\n * \\tparam size the size of the created array\n */\ntemplate <typename T, size_t size, typename Seq>\nconstexpr std::array<T, size> make_array(Seq&& seq) {\n  return MakeArray_detail::make_array_from_iterator_impl<T, size>(\n      std::forward<Seq>(seq), std::make_index_sequence<size>{});\n}\n"
  },
  {
    "path": "src/Utilities/MakeSignalingNan.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n\n/// @{\n/// \\ingroup UtilitiesGroup\n/// \\brief Returns an appropriate signaling NaN for fundamantal or multi-field\n/// types (such as `std::complex`).\ntemplate <typename T>\nT make_signaling_NaN(const T& /*meta*/) {\n  return std::numeric_limits<T>::signaling_NaN();\n}\n\ntemplate <typename T>\nstd::complex<T> make_signaling_NaN(const std::complex<T>& /*meta*/) {\n  return {std::numeric_limits<T>::signaling_NaN(),\n          std::numeric_limits<T>::signaling_NaN()};\n}\n\ntemplate <typename T>\nT make_signaling_NaN() {\n  return make_signaling_NaN(static_cast<T>(0));\n}\n/// @}\n"
  },
  {
    "path": "src/Utilities/MakeString.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <ostream>\n#include <sstream>\n#include <string>\n\n#include \"Utilities/StlStreamDeclarations.hpp\"\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Make a string by streaming into object.\n *\n * \\snippet Test_MakeString.cpp make_string\n */\nclass MakeString {\n public:\n  MakeString() = default;\n  MakeString(const MakeString&) = delete;\n  MakeString& operator=(const MakeString&) = delete;\n  MakeString(MakeString&&) = default;\n  MakeString& operator=(MakeString&&) = delete;\n  ~MakeString() = default;\n\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  operator std::string() const { return stream_.str(); }\n\n  template <class T>\n  friend MakeString operator<<(MakeString&& ms, const T& t) {\n    // clang-tidy: can get unintentional pointer casts\n    ms.stream_ << t;  // NOLINT\n    return std::move(ms);\n  }\n\n  template <class T>\n  friend MakeString& operator<<(MakeString& ms, const T& t) {\n    // clang-tidy: can get unintentional pointer casts\n    ms.stream_ << t;  // NOLINT\n    return ms;\n  }\n\n private:\n  std::stringstream stream_{};\n};\n\ninline std::ostream& operator<<(std::ostream& os, const MakeString& t) {\n  return os << std::string{t};\n}\n"
  },
  {
    "path": "src/Utilities/MakeVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <initializer_list>\n#include <utility>\n#include <vector>\n\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/*!\n * \\brief Constructs a `std::vector` containing arguments passed in.\n *\n * This is useful as it allows in-place construction of a vector of non-copyable\n * objects.\n *\n * \\example\n * \\snippet Utilities/Test_MakeVector.cpp make_vector_example\n */\ntemplate <class ValueType = void, class Arg0, class... Args>\nauto make_vector(Arg0&& arg_0, Args&&... remaining_args) {\n  std::vector<\n      tmpl::conditional_t<std::is_same_v<ValueType, void>, Arg0, ValueType>>\n      return_vector;\n  return_vector.reserve(sizeof...(Args) + 1);\n  return_vector.emplace_back(std::forward<Arg0>(arg_0));\n  (void)std::initializer_list<int>{\n      (((void)return_vector.emplace_back(std::forward<Args>(remaining_args))),\n       0)...};\n  return return_vector;\n}\n\ntemplate <class T>\nstd::vector<T> make_vector() {\n  return {};\n}\n"
  },
  {
    "path": "src/Utilities/MakeWithValue.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Defines make_with_value\n\n#pragma once\n\n#include <array>\n#include <complex>\n#include <functional>\n#include <type_traits>\n#include <vector>\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\n/// \\ingroup DataStructuresGroup\n/// Implementations of make_with_value.\nnamespace MakeWithValueImpls {\n/// Defines a method for determining the number of points represented\n/// by an object.  This allows the object to appear as the input to\n/// make_with_value.\n///\n/// The MakeWithValueImpls::number_of_points convenience wrapper is\n/// provided to simplify calling this.\ntemplate <typename T, typename = std::nullptr_t>\nstruct NumberOfPoints {\n  /// The default implementation will produce a compile-time error.\n  [[noreturn]] static SPECTRE_ALWAYS_INLINE size_t apply(const T& /*input*/) {\n    static_assert(typename tmpl::has_type<T, std::false_type>::type{},\n                  \"Do not know how to obtain a size from this type.  Either \"\n                  \"implement NumberOfPoints or specialize MakeWithValueImpl \"\n                  \"for the type you are trying to create.\");\n  }\n};\n\n/// The number of points represented by an object.\ntemplate <typename T>\nsize_t number_of_points(const T& input) {\n  return NumberOfPoints<T>::apply(input);\n}\n\n/// Defines a method for producing an object representing a given\n/// number of points.\n///\n/// Do not call these functions directly.  Use make_with_value\n/// instead, which can take a size as its first argument.\ntemplate <typename R, typename = std::nullptr_t>\nstruct MakeWithSize {\n  /// The default implementation will produce a compile-time error.\n  /// In specializations, the \\p value parameter need not be a template.\n  template <typename T>\n  [[noreturn]] static SPECTRE_ALWAYS_INLINE R apply(const size_t /*size*/,\n                                                    const T& /*value*/) {\n    static_assert(typename tmpl::has_type<R, std::false_type>::type{},\n                  \"Do not know how to create a sized object of this type.  \"\n                  \"Either implement MakeWithSize or specialize \"\n                  \"MakeWithValueImpl for the type you are trying to create.\");\n  }\n};\n\ntemplate <typename R, typename T, typename = std::nullptr_t>\nstruct MakeWithValueImpl {\n  /// The default implementation uses \\ref number_of_points and MakeWithSize.\n  template <typename ValueType>\n  static SPECTRE_ALWAYS_INLINE R apply(const T& input, const ValueType value) {\n    return MakeWithSize<R>::apply(number_of_points(input), value);\n  }\n};\n}  // namespace MakeWithValueImpls\n\n/// \\ingroup DataStructuresGroup\n/// \\brief Given an object of type `T`, create an object of type `R` whose\n/// elements are initialized to `value`.\n///\n/// \\details This function is useful in function templates in order to\n/// initialize the return type of a function template with `value` for functions\n/// that can be called either at a single grid-point or to fill a data structure\n/// at the same set of grid-points as the `input`\n\n/// \\tparam ValueType The type of `value`. For most containers, this will be\n/// `double`.\n///\n/// \\see MakeWithValueImpls, set_number_of_grid_points\ntemplate <typename R, typename T, typename ValueType>\nSPECTRE_ALWAYS_INLINE std::remove_const_t<R> make_with_value(\n    const T& input, const ValueType& value) {\n  return MakeWithValueImpls::MakeWithValueImpl<std::remove_const_t<R>,\n                                               T>::apply(input, value);\n}\n\nnamespace MakeWithValueImpls {\ntemplate <>\nstruct NumberOfPoints<size_t> {\n  static SPECTRE_ALWAYS_INLINE size_t apply(const size_t& input) {\n    return input;\n  }\n};\n\n/// \\brief Returns a double initialized to `value` (`input` is ignored)\ntemplate <typename T>\nstruct MakeWithValueImpl<double, T> {\n  static SPECTRE_ALWAYS_INLINE double apply(const T& /* input */,\n                                            const double value) {\n    return value;\n  }\n};\n\ntemplate <typename T>\nstruct MakeWithValueImpl<std::complex<double>, T> {\n  static SPECTRE_ALWAYS_INLINE std::complex<double> apply(\n      const T& /* input */, const std::complex<double> value) {\n    return value;\n  }\n};\n\n/// \\brief Makes a `std::array`; each element of the `std::array`\n/// must be `make_with_value`-creatable from a `InputType`.\ntemplate <size_t Size, typename T, typename InputType>\nstruct MakeWithValueImpl<std::array<T, Size>, InputType> {\n  template <typename ValueType>\n  static SPECTRE_ALWAYS_INLINE std::array<T, Size> apply(\n      const InputType& input, const ValueType value) {\n    return make_array<Size>(make_with_value<T>(input, value));\n  }\n};\n\ntemplate <size_t Size, typename T>\nstruct NumberOfPoints<std::array<T, Size>> {\n  static SPECTRE_ALWAYS_INLINE size_t apply(const std::array<T, Size>& input) {\n    static_assert(Size > 0);\n    // size_t is interpreted as the number of points in other\n    // contexts, but that doesn't make sense here.\n    static_assert(not std::is_same_v<T, size_t>,\n                  \"Cannot get size from non-vector.\");\n    const size_t points = number_of_points(input[0]);\n    ASSERT(\n        alg::all_of(input,\n                    [&](const T& t) { return number_of_points(t) == points; }),\n        \"Inconsistent number of points in array entries.\");\n    return points;\n  }\n};\n\ntemplate <typename T>\nstruct NumberOfPoints<std::vector<T>> {\n  static SPECTRE_ALWAYS_INLINE size_t apply(const std::vector<T>& input) {\n    // size_t is interpreted as the number of points in other\n    // contexts, but that doesn't make sense here.\n    static_assert(not std::is_same_v<T, size_t>,\n                  \"Cannot get number_of_points from non-vector.\");\n    ASSERT(not input.empty(),\n           \"Cannot get number of points from empty std::vector.\");\n    const size_t points = number_of_points(input[0]);\n    ASSERT(\n        alg::all_of(input,\n                    [&](const T& t) { return number_of_points(t) == points; }),\n        \"Inconsistent number of points in vector entries.\");\n    return points;\n  }\n};\n\ntemplate <typename T>\nstruct NumberOfPoints<std::reference_wrapper<T>> {\n  static SPECTRE_ALWAYS_INLINE size_t apply(\n      const std::reference_wrapper<T>& input) {\n    return number_of_points(input.get());\n  }\n};\n\n}  // namespace MakeWithValueImpls\n"
  },
  {
    "path": "src/Utilities/Math.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <numeric>\n#include <type_traits>\n#include <vector>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsInteger.hpp\"\n\n// using for overload resolution with blaze\n// clang-tidy doesn't want these in the global namespace\nusing std::conj;  // NOLINT\nusing std::imag;  // NOLINT\nusing std::real;  // NOLINT\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Returns the number of digits in an integer number\n */\ntemplate <typename T>\nSPECTRE_ALWAYS_INLINE T number_of_digits(const T number) {\n  static_assert(tt::is_integer_v<std::decay_t<T>>,\n                \"Must call number_of_digits with an integer number\");\n  return number == 0 ? 1\n                     : static_cast<decltype(number)>(\n                           std::ceil(std::log10(std::abs(number) + 1)));\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Evaluate a polynomial \\f$\\sum_{p=0}^N c_p x^p\\f$ with Horner's rule\n *\n * \\param coeffs The polynomial coefficients \\f$c_p\\f$ ordered from constant to\n * largest power\n * \\param x The polynomial variable \\f$x\\f$\n *\n * \\tparam CoeffsIterable The type of the polynomial coefficients \\p coeffs. Can\n * be a `std::vector<double>` or `std::array<double>`, which means the\n * coefficients are constant for all values in \\p x. Each coefficient can also\n * be a vector type of typically the same size as \\p x, which means the\n * coefficients vary with the elements in \\p x.\n * \\tparam DataType The type of the polynomial variable \\p x. Must support\n * `make_with_value<DataType, DataType>`, as well as (elementwise) addition with\n * `CoeffsIterable::value_type` and multiplication with `DataType`.\n */\ntemplate <typename CoeffsIterable, typename DataType>\nDataType evaluate_polynomial(const CoeffsIterable& coeffs, const DataType& x) {\n  return std::accumulate(coeffs.rbegin(), coeffs.rend(),\n                         make_with_value<DataType>(x, 0.),\n                         [&x](const DataType& state, const auto& element) {\n                           return state * x + element;\n                         });\n}\n\n/// \\ingroup UtilitiesGroup\n\n/// \\brief Defines the Heaviside step function \\f$\\Theta\\f$ for arithmetic\n/// types.  \\f$\\Theta(0) = 1\\f$.\ntemplate <typename T, Requires<std::is_arithmetic<T>::value> = nullptr>\nconstexpr T step_function(const T& arg) {\n  return static_cast<T>((arg >= static_cast<T>(0)) ? 1 : 0);\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Smoothly interpolates from 0 to 1 between `lower_edge` and\n * `upper_edge` with Hermite interpolation of polynomial degree `2 * N + 1`.\n *\n * The smoothstep function is\n *\n * \\begin{align*}\n * S_N(x) = \\begin{cases}\n * 0 &\\quad \\text{for} \\quad x\\leq x_0 \\\\\n * \\tilde{S}_N((x - x_0) / (x_1 - x_0))\n * &\\quad \\text{for} \\quad x_0 \\leq x\\leq x_1 \\\\\n * 1 &\\quad \\text{for} \\quad x_1\\leq x \\\\\n * \\end{cases}\n * \\end{align*}\n *\n * where \\f$x_0\\f$ is `lower_edge` and \\f$x_1\\f$ is `upper_edge`. The general\n * form of the polynomial \\f$\\tilde{S}_N(x)\\f$ is:\n *\n * \\begin{equation}\n * \\tilde{S}_N(x) = x^{N+1} \\sum_{k=0}^{N} \\binom{N+k}{k}\n *   \\binom{2N+1}{N-k} (-x)^k\n * \\end{equation}\n *\n * The first few polynomials are:\n *\n * \\begin{align*}\n * \\tilde{S}_0(x) &= x \\\\\n * \\tilde{S}_1(x) &= 3x^2 - 2x^3 \\\\\n * \\tilde{S}_2(x) &= 10x^3 - 15x^4 + 6x^5 \\\\\n * \\tilde{S}_3(x) &= 35x^4 - 84x^5 + 70x^6 - 20x^7\n * \\text{.}\n * \\end{align*}\n *\n * This function is $C^N$ continuous at the edges, i.e. the first $N$\n * derivatives are continuous at the edges.\n *\n * If `lower_edge` and `upper_edge` are equal, this function reduces to the\n * step function $\\Theta(x - x_0)$.\n */\ntemplate <size_t N, typename DataType>\nDataType smoothstep(const double lower_edge, const double upper_edge,\n                    const DataType& arg) {\n  if (lower_edge == upper_edge) {\n    return step_function(arg - lower_edge);\n  }\n  ASSERT(lower_edge < upper_edge,\n         \"Requires lower_edge < upper_edge, but lower_edge=\"\n             << lower_edge << \" and upper_edge=\" << upper_edge);\n  constexpr auto coeffs = []() {\n    std::array<double, 2 * N + 2> result{};\n    for (size_t k = 0; k <= N; ++k) {\n      result[N + 1 + k] = (k % 2 == 0 ? 1. : -1.) * binomial(2 * N + 1, N - k) *\n                          binomial(N + k, k);\n    }\n    return result;\n  }();\n  using std::clamp;\n  const DataType x = clamp(\n      static_cast<DataType>((arg - lower_edge) / (upper_edge - lower_edge)), 0.,\n      1.);\n  return evaluate_polynomial(coeffs, x);\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Derivative of the $N$-th order smoothstep function\n *\n * The general form of the derivative of the smoothstep function is:\n *\n * \\begin{equation}\n * \\tilde{S}_N'(x) = (2N + 1) \\binom{2N}{N} (x - x^2)^N\n * \\end{equation}\n *\n * Since the smoothstep function is $C^N$ continuous at the edges, this\n * function is $C^{N-1}$ continuous at the edges.\n *\n * If `lower_edge` and `upper_edge` are equal, this function reduces to the\n * derivative of the step function, which is zero everywhere except at the edge\n * where it is not defined. We define it to be zero in this case as well.\n */\ntemplate <size_t N, typename DataType>\nDataType smoothstep_deriv(const double lower_edge, const double upper_edge,\n                          const DataType& arg) {\n  if (lower_edge == upper_edge) {\n    return make_with_value<DataType>(arg, 0.);\n  }\n  ASSERT(lower_edge < upper_edge,\n         \"Requires lower_edge < upper_edge, but lower_edge=\"\n             << lower_edge << \" and upper_edge=\" << upper_edge);\n  using std::clamp;\n  const DataType x = clamp(\n      static_cast<DataType>((arg - lower_edge) / (upper_edge - lower_edge)), 0.,\n      1.);\n  constexpr auto coeff = (2 * N + 1) * binomial(2 * N, N);\n  return coeff * pow<N>(x - square(x)) / (upper_edge - lower_edge);\n}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Defines the inverse square-root (\\f$1/\\sqrt{x}\\f$) for arithmetic\n/// and complex types\ntemplate <typename T, Requires<std::is_arithmetic<T>::value or\n                               tt::is_a_v<std::complex, T>> = nullptr>\nauto invsqrt(const T& arg) {\n  return static_cast<T>(1.0) / sqrt(arg);\n}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Defines the inverse cube-root (\\f$1/\\sqrt[3]{x}\\f$) for arithmetic\n/// types\ntemplate <typename T, Requires<std::is_arithmetic<T>::value> = nullptr>\nauto invcbrt(const T& arg) {\n  return static_cast<T>(1.0) / cbrt(arg);\n}\n\nnamespace sgn_detail {\ntemplate <typename T>\nconstexpr T sgn(const T& val, std::true_type /*is_signed*/) {\n  return static_cast<T>(static_cast<T>(0) < val) -\n         static_cast<T>(val < static_cast<T>(0));\n}\n\ntemplate <typename T>\nconstexpr T sgn(const T& val, std::false_type /*is_signed*/) {\n  return static_cast<T>(static_cast<T>(0) < val);\n}\n}  // namespace sgn_detail\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Compute the sign function of `val` defined as `1` if `val > 0`, `0`\n/// if `val == 0`, and `-1` if `val < 0`.\ntemplate <typename T>\nconstexpr T sgn(const T& val) {\n  return sgn_detail::sgn(val, std::is_signed<T>{});\n}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Raises a double to the integer power n.\ninline double integer_pow(const double x, const int e) {\n  ASSERT(e >= 0, \"Negative powers are not implemented\");\n  int ecount = e;\n  int bitcount = 1;\n  while (ecount >>= 1) {\n    ++bitcount;\n  }\n  double result = 1.;\n  while (bitcount) {\n    result *= result;\n    if ((e >> --bitcount) & 0x1) {\n      result *= x;\n    }\n  }\n  return result;\n}\n"
  },
  {
    "path": "src/Utilities/MemoryHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/MemoryHelpers.hpp\"\n\n#include <new>\n\n#include \"ErrorHandling/Error.hpp\"\n\nnamespace {\n[[noreturn]] void report_failure() {\n  // Make sure we don't go into an infinite loop if we fail to\n  // allocate in this function.\n  std::set_new_handler(nullptr);\n\n  ERROR(\"Failed to allocate memory.\");\n}\n}  // namespace\n\nvoid setup_memory_allocation_failure_reporting() {\n  std::set_new_handler(report_failure);\n}\n"
  },
  {
    "path": "src/Utilities/MemoryHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <type_traits>\n\nnamespace cpp20 {\nnamespace detail {\ntemplate <typename T>\nstruct unique_type {\n  using single_object = std::unique_ptr<T>;\n};\n\ntemplate <typename T>\nstruct unique_type<T[]> {\n  using array = std::unique_ptr<T[]>;\n};\n\ntemplate<typename T, size_t Bound>\nstruct unique_type<T[Bound]> { struct invalid_type { }; };\n}  // namespace detail\n\ntemplate <typename T, typename... Args>\ntypename detail::unique_type<T>::single_object make_unique_for_overwrite() {\n  return std::unique_ptr<T>(new T);\n}\n\ntemplate <typename T>\ntypename detail::unique_type<T>::array make_unique_for_overwrite(\n    const size_t num) {\n  return std::unique_ptr<T>(new std::remove_extent_t<T>[num]);\n}\n\ntemplate <typename T, typename... Args>\ntypename detail::unique_type<T>::invalid_type make_unique_for_overwrite(\n    Args&&...) = delete;\n}  // namespace cpp20\n\n/// Install a memory allocation failure handler that calls ERROR()\n/// instead of throwing an exception.\nvoid setup_memory_allocation_failure_reporting();\n"
  },
  {
    "path": "src/Utilities/NoSuchType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Used to mark \"no type\" or \"bad state\" for metaprogramming\n */\nstruct NoSuchType {};\n"
  },
  {
    "path": "src/Utilities/Numeric.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iterator>\n#include <numeric>\n\n#include \"Utilities/Gsl.hpp\"\n\n/// C++ STL code present in C++2b.\nnamespace cpp2b {\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::iota that is constexpr;\n * taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/numeric\n */\ntemplate <class ForwardIterator, class T>\nconstexpr void iota(ForwardIterator first, ForwardIterator last, T value) {\n  for (; first != last; ++first, (void)++value) {\n    *first = value;\n  }\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * Reimplementation of std::accumulate that is constexpr;\n * taken from the LLVM source at\n * https://github.com/llvm-mirror/libcxx/blob/master/include/numeric\n */\ntemplate <class InputIt, class T>\nconstexpr T accumulate(InputIt first, InputIt last, T init) {\n  for (; first != last; ++first) {\n    init = std::move(init) + *first;  // std::move since C++20\n  }\n  return init;\n}\n}  // namespace cpp2b\n\nnamespace alg {\ntemplate <class Container, class T>\nconstexpr decltype(auto) iota(Container&& c, T value) {\n  for (auto& t : c) {\n    t = value;\n    ++value;\n  }\n  return std::forward<Container>(c);\n}\n\n/// Convenience wrapper around std::accumulate, returns\n/// `std::accumulate(begin(c), end(c), init)`.\ntemplate <class Container, class T>\ndecltype(auto) accumulate(const Container& c, T init) {\n  using std::begin;\n  using std::end;\n  return std::accumulate(begin(c), end(c), std::move(init));\n}\n\n/// Convenience wrapper around std::accumulate, returns\n/// `std::accumulate(begin(c), end(c), init, f)`.\ntemplate <class Container, class T, class BinaryFunction>\ndecltype(auto) accumulate(const Container& c, T init, BinaryFunction&& f) {\n  using std::begin;\n  using std::end;\n  return std::accumulate(begin(c), end(c), std::move(init),\n                         std::forward<BinaryFunction>(f));\n}\n}  // namespace alg\n"
  },
  {
    "path": "src/Utilities/OptimizerHacks.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/OptimizerHacks.hpp\"\n\n#if defined(__clang__) && __clang__ < 11\nnamespace optimizer_hacks {\nvoid indicate_value_can_be_changed(void* /*variable*/) {}\n}  // namespace optimizer_hacks\n#endif  /* defined(__clang__) && __clang__ < 11 */\n\n"
  },
  {
    "path": "src/Utilities/OptimizerHacks.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#if defined(__clang__) && __clang__ < 11\n/// \\ingroup PeoGroup\n/// Workarounds for optimizer bugs\nnamespace optimizer_hacks {\n/// \\ingroup PeoGroup\n/// Produce a situation where the value of `*variable` could have been\n/// changed without the optimizer's knowledge, without actually\n/// changing the variable.\nvoid indicate_value_can_be_changed(void* variable);\n}  // namespace optimizer_hacks\n\n#define VARIABLE_CAUSES_CLANG_FPE(var) \\\n  ::optimizer_hacks::indicate_value_can_be_changed(&(var))\n#else  /* defined(__clang__) && __clang__ < 11 */\n/// \\ingroup PeoGroup\n/// Clang's optimizer has a known bug that sometimes produces spurious\n/// FPEs.  This indicates that the variable `var` can trigger that bug\n/// and prevents some optimizations.  This is fixed upstream in Clang\n/// 11.\n#define VARIABLE_CAUSES_CLANG_FPE(var) ((void)(var))\n#endif  /* defined(__clang__) && __clang__ < 11 */\n"
  },
  {
    "path": "src/Utilities/OptionalHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Returns `t.has_value()` if `t` is a `std::optional` otherwise returns\n * `true`.\n */\ntemplate <typename T>\nconstexpr bool has_value(const T& /*t*/) {\n  return true;\n}\n\ntemplate <typename T>\nconstexpr bool has_value(const std::optional<T>& t) {\n  return t.has_value();\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Returns `t.value()` if `t` is a `std::optional` otherwise returns\n * `t`.\n */\ntemplate <typename T>\nconstexpr T& value(T& t) {\n  return t;\n}\n\ntemplate <typename T>\nconstexpr const T& value(const T& t) {\n  return t;\n}\n\ntemplate <typename T>\nconstexpr const T& value(const std::optional<T>& t) {\n  return t.value();\n}\n\ntemplate <typename T>\nconstexpr T& value(std::optional<T>& t) {\n  return t.value();\n}\n/// @}\n"
  },
  {
    "path": "src/Utilities/Overloader.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Used for overloading lambdas, useful for lambda-SFINAE\n *\n * \\snippet Utilities/Test_Overloader.cpp overloader_example\n */\ntemplate <class... Fs>\nstruct Overloader : Fs... {\n  using Fs::operator()...;\n};\n\ntemplate <class... Fs>\nOverloader(Fs...) -> Overloader<Fs...>;\n"
  },
  {
    "path": "src/Utilities/PrettyType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/PrettyType.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <string_view>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace pretty_type::detail {\nnamespace {\n// Remove characters until a character in target is found.  This is\n// used to skip characters we know we don't care about or do not\n// understand.\nvoid remove_until(const gsl::not_null<std::string_view*> remainder,\n                  const char* targets) {\n  const auto next_target = remainder->find_first_of(targets);\n  ASSERT(next_target != remainder->npos,\n         \"Overran string \" << *remainder << \" looking for \" << targets);\n  remainder->remove_prefix(next_target);\n}\n\nstd::string_view process_type_or_value(\n    gsl::not_null<std::string_view*> remainder);\nvoid process_literal(gsl::not_null<std::string_view*> remainder);\nstd::string_view process_item_sequence(\n    gsl::not_null<std::string_view*> remainder);\nvoid process_substitution(gsl::not_null<std::string_view*> remainder);\nstd::string_view process_identifier(gsl::not_null<std::string_view*> remainder);\n\n// An entity of undetermined type.\nstd::string_view process_type_or_value(\n    const gsl::not_null<std::string_view*> remainder) {\n  remove_until(remainder, \"0123456789LNS\");\n  std::string_view result = \"\";\n  switch ((*remainder)[0]) {\n    // Note that the first two cases return and the second two break.\n    case 'L':\n      process_literal(remainder);\n      return \"\";\n    case 'N':\n      return process_item_sequence(remainder);\n    case 'S':\n      process_substitution(remainder);\n      break;\n    default:\n      result = process_identifier(remainder);\n      break;\n  }\n  // The last two cases can be templates, and so may be followed by\n  // template parameters.\n  if (not remainder->empty() and (*remainder)[0] == 'I') {\n    process_item_sequence(remainder);\n  }\n  return result;\n}\n\n// A value (such as for a non-type template parameter) encoded as\n// L<type><value>E or LDnE (nullptr)\nvoid process_literal(const gsl::not_null<std::string_view*> remainder) {\n  remainder->remove_prefix(1);\n  if (not ('a' <= (*remainder)[0] and (*remainder)[0] <= 'z')) {\n    if ((*remainder)[0] == 'D') {\n      // decltype(something).  The only valid value of this form\n      // should be nullptr.  The Itanium ABI standard says nullptr is\n      // LDnE, but many compilers mangle it as LDn0E instead.\n      ASSERT(\n          (remainder->size() >= 3 and (*remainder)[1] == 'n' and\n           (*remainder)[2] == 'E') or\n              (remainder->size() >= 4 and (*remainder)[1] == 'n' and\n               (*remainder)[2] == '0' and (*remainder)[3] == 'E'),\n          \"Expected nullptr constant (DnE or Dn0E) at start of \" << *remainder);\n    } else {\n      // A named enum class\n      process_type_or_value(remainder);\n    }\n  }\n  remove_until(remainder, \"E\");\n  remainder->remove_prefix(1);\n}\n\n// A sequence of items without separators, such as namespaces or\n// template parameters.\nstd::string_view process_item_sequence(\n    const gsl::not_null<std::string_view*> remainder) {\n  remainder->remove_prefix(1);\n  std::string_view result;\n  for (;;) {\n    // List from process_type_or_value with J and E added\n    remove_until(remainder, \"0123456789LNSJE\");\n    if ((*remainder)[0] == 'E') {\n      // E = end\n      remainder->remove_prefix(1);\n      return result;\n    } else if ((*remainder)[0] == 'J') {\n      // A parameter pack.  Only allowed immediately inside a list of\n      // template parameters, so doesn't have to be handled anywhere\n      // else in the processing.\n      process_item_sequence(remainder);\n    } else {\n      result = process_type_or_value(remainder);\n    }\n  }\n  return result;\n}\n\n// A \"substitution\" (i.e., an abbreviation of some sort).  There are\n// three types:\n//   St          - \"std::\", which will be followed by more parts\n//   S[a-z]      - some other standard library thing\n//   S[0-9A-Z]*_ - a back-reference to some previously seen entity\n// All cases where these could occur outside of template parameters\n// were handled as special cases at the start, so we don't care about\n// any of them.\nvoid process_substitution(const gsl::not_null<std::string_view*> remainder) {\n  remainder->remove_prefix(1);\n  if ((*remainder)[0] == 't') {\n    remainder->remove_prefix(1);\n    process_type_or_value(remainder);\n  } else if ('a' <= (*remainder)[0] and (*remainder)[0] <= 'z') {\n    remainder->remove_prefix(1);\n  } else {\n    remove_until(remainder, \"_\");\n    remainder->remove_prefix(1);\n  }\n}\n\n// Identifier coded as <name length><name>\nstd::string_view process_identifier(\n    const gsl::not_null<std::string_view*> remainder) {\n  size_t length_chars = 0;\n  const size_t identifier_chars =\n      static_cast<size_t>(std::stoi(std::string(*remainder), &length_chars));\n  std::string_view identifier =\n      remainder->substr(length_chars, identifier_chars);\n  remainder->remove_prefix(length_chars + identifier_chars);\n  return identifier;\n}\n}  // namespace\n\nstd::string extract_short_name(const std::string& name) {\n  // This ignores some of the less common language features (e.g., C\n  // array types) and a lot of things that are not relevant to types.\n\n  ASSERT(not name.empty(), \"Cannot get type from an empty string\");\n\n  // Special-case a few standard library types.  These can be encoded\n  // either using a special notation or using the standard namespace\n  // notation, and some of them are type aliases in the latter case.\n  // This makes them consistent.\n  if (name == typeid(std::string).name()) {\n    return \"string\";\n  } else if (name == typeid(std::istream).name()) {\n    return \"istream\";\n  } else if (name == typeid(std::ostream).name()) {\n    return \"ostream\";\n  } else if (name == typeid(std::iostream).name()) {\n    return \"iostream\";\n  }\n\n  // Short circuit for fundamentals and some standard library types.\n  // These can occur in complicated types, but never as the component\n  // we care about (which is always a struct or struct template name).\n  if (name.size() == 1) {\n    switch (name[0]) {\n      case 'v': return \"void\";\n      case 'b': return \"bool\";\n      case 'c': return \"char\";\n      case 'a': return \"signed char\";\n      case 'h': return \"unsigned char\";\n      case 's': return \"short\";\n      case 't': return \"unsigned short\";\n      case 'i': return \"int\";\n      case 'j': return \"unsigned int\";\n      case 'l': return \"long\";\n      case 'm': return \"unsigned long\";\n      case 'x': return \"long long\";\n      case 'y': return \"unsigned long long\";\n      case 'f': return \"float\";\n      case 'd': return \"double\";\n      case 'e': return \"long double\";\n      default: ERROR(\"Builtin type \" << name << \" not handled\");\n    }\n  } else if (name[0] == 'S') {\n    // More possible standard library special cases, but with template\n    // parameters.\n    switch (name[1]) {\n      case 'a': return \"allocator\";\n      case 'b': return \"basic_string\";  // but not a std::string\n      default:;\n    }\n  }\n\n  std::string_view name_view(name);\n  if (name[0] == 'S' and name[1] == 't') {\n    // \"St\" is an abbreviation for the std:: prefix.  We don't care\n    // about namespaces, so we can just drop it.\n    name_view.remove_prefix(2);\n  }\n\n  std::string result(process_type_or_value(&name_view));\n  ASSERT(name_view.empty(), \"Type name was not fully processed\");\n  return result;\n}\n}  // namespace pretty_type::detail\n"
  },
  {
    "path": "src/Utilities/PrettyType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Contains a pretty_type library to write types in a \"pretty\" format\n\n#pragma once\n\n#include <boost/core/demangle.hpp>\n#include <deque>\n#include <forward_list>\n#include <list>\n#include <map>\n#include <memory>\n#include <queue>\n#include <set>\n#include <sstream>\n#include <stack>\n#include <string>\n#include <type_traits>\n#include <typeinfo>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/ArraySize.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsStdArray.hpp\"\n\n/// \\cond\n#define PRETTY_TYPE_USE_BOOST\n/// \\endcond\n\n/*!\n * \\ingroup PrettyTypeGroup\n * \\brief Contains all functions that are part of PrettyType, used for printing\n * types in a pretty manner.\n */\nnamespace pretty_type {\nnamespace detail {\nusing str_const = const char* const;\n\ntemplate <typename T>\nstruct Type;\n\ntemplate <>\nstruct Type<char> {\n  using type = char;\n  static constexpr str_const type_name = {\"char\"};\n};\n\ntemplate <>\nstruct Type<signed char> {\n  using type = signed char;\n  static constexpr str_const type_name = {\"signed char\"};\n};\n\ntemplate <>\nstruct Type<unsigned char> {\n  using type = unsigned char;\n  static constexpr str_const type_name = {\"unsigned char\"};\n};\n\ntemplate <>\nstruct Type<wchar_t> {\n  using type = wchar_t;\n  static constexpr str_const type_name = {\"wchar_t\"};\n};\n\ntemplate <>\nstruct Type<char16_t> {\n  using type = char16_t;\n  static constexpr str_const type_name = {\"char16_t\"};\n};\n\ntemplate <>\nstruct Type<char32_t> {\n  using type = char32_t;\n  static constexpr str_const type_name = {\"char32_t\"};\n};\n\ntemplate <>\nstruct Type<int> {\n  using type = int;\n  static constexpr str_const type_name = {\"int\"};\n};\n\ntemplate <>\nstruct Type<unsigned int> {\n  using type = unsigned int;\n  static constexpr str_const type_name = {\"unsigned int\"};\n};\n\ntemplate <>\nstruct Type<long> {\n  using type = long;\n  static constexpr str_const type_name = {\"long\"};\n};\n\ntemplate <>\nstruct Type<unsigned long> {\n  using type = unsigned long;\n  static constexpr str_const type_name = {\"unsigned long\"};\n};\n\ntemplate <>\nstruct Type<long long> {\n  using type = long long;\n  static constexpr str_const type_name = {\"long long\"};\n};\n\ntemplate <>\nstruct Type<unsigned long long> {\n  using type = unsigned long long;\n  static constexpr str_const type_name = {\"unsigned long long\"};\n};\n\ntemplate <>\nstruct Type<short> {\n  using type = short;\n  static constexpr str_const type_name = {\"short\"};\n};\n\ntemplate <>\nstruct Type<unsigned short> {\n  using type = unsigned short;\n  static constexpr str_const type_name = {\"unsigned short\"};\n};\n\ntemplate <>\nstruct Type<float> {\n  using type = float;\n  static constexpr str_const type_name = {\"float\"};\n};\n\ntemplate <>\nstruct Type<double> {\n  using type = double;\n  static constexpr str_const type_name = {\"double\"};\n};\n\ntemplate <>\nstruct Type<long double> {\n  using type = long double;\n  static constexpr str_const type_name = {\"long double\"};\n};\n\ntemplate <>\nstruct Type<bool> {\n  using type = bool;\n  static constexpr str_const type_name = {\"bool\"};\n};\n\ntemplate <>\nstruct Type<void> {\n  using type = void;\n  static constexpr str_const type_name = {\"void\"};\n};\n\ntemplate <>\nstruct Type<std::string> {\n  using type = std::string;\n  static constexpr str_const type_name = {\"std::string\"};\n};\n\ntemplate <typename... T>\nusing TemplateMap_t = tmpl::map<tmpl::pair<T, Type<T>>...>;\n\ntemplate <typename T>\nstd::string add_qualifiers() {\n  std::stringstream ss;\n  if (std::is_pointer<T>::value) {\n    if (std::is_const<std::remove_pointer_t<T>>::value) {\n      ss << \" const\";\n    }\n    if (std::is_volatile<std::remove_pointer_t<T>>::value) {\n      ss << \" volatile\";\n    }\n    ss << \"*\";\n  }\n  if (std::is_const<std::remove_reference_t<T>>::value) {\n    ss << \" const\";\n  }\n  if (std::is_volatile<std::remove_reference_t<T>>::value) {\n    ss << \" volatile\";\n  }\n  if (std::is_reference<T>::value) {\n    ss << \"&\";\n  }\n  return ss.str();\n}\n\n/*!\n * \\ingroup PrettyTypeGroup\n * Used to construct the name of a container\n *\n * \\tparam T the type whose name to print\n * \\tparam M the map of the basic types to print\n * \\tparam KT the struct holding the template alias template_list which is a\n * list of known specializations of construct_name for those containers\n */\ntemplate <typename T, typename M, typename KT, typename = std::nullptr_t>\nstruct construct_name;\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tmpl::has_key<M, std::decay_t<std::remove_pointer_t<T>>>::value ==\n             1>> {\n  static std::string get() {\n    constexpr str_const t =\n        tmpl::at<M, std::decay_t<std::remove_pointer_t<T>>>::type_name;\n    std::stringstream ss;\n    ss << t << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<\n        tmpl::has_key<M, std::decay_t<std::remove_reference_t<\n                             std::remove_pointer_t<T>>>>::value == 0 and\n        std::is_same<\n            tmpl::list<>,\n            tmpl::find<typename KT::template template_list<std::decay_t<\n                           std::remove_reference_t<std::remove_pointer_t<T>>>>,\n                       std::is_base_of<std::true_type, tmpl::_1>>>::value>> {\n  static std::string get() {\n    std::stringstream ss;\n#if defined(PRETTY_TYPE_USE_BOOST)\n    ss << boost::core::demangle(typeid(T).name());\n#else\n    ss << typeid(T).name();\n#endif\n    return ss.str();\n  }\n};\n\n// STL Sequences\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::vector, std::decay_t<std::remove_reference_t<\n                                         std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_pointer_t<T>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::vector<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::value_type>>, M,\n              KT>::get();\n    ss << add_qualifiers<typename type::value_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_std_array_v<\n        std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::array<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::value_type>>, M,\n              KT>::get();\n    ss << add_qualifiers<typename type::value_type>();\n    ss << \", \" << tt::array_size<type>::value << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::deque, std::decay_t<std::remove_reference_t<\n                                        std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_pointer_t<T>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::deque<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::value_type>>, M,\n              KT>::get();\n    ss << add_qualifiers<typename type::value_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::forward_list, std::decay_t<std::remove_reference_t<\n                                               std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_pointer_t<T>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::forward_list<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::value_type>>, M,\n              KT>::get();\n    ss << add_qualifiers<typename type::value_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::list, std::decay_t<std::remove_reference_t<\n                                       std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_pointer_t<T>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::list<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::value_type>>, M,\n              KT>::get();\n    ss << add_qualifiers<typename type::value_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\n// STL Associative containers\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::map, std::decay_t<std::remove_reference_t<\n                                      std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::map<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::key_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::key_type>() << \", \"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::mapped_type>>,\n              M, KT>::get()\n       << add_qualifiers<typename type::mapped_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::multimap, std::decay_t<std::remove_reference_t<\n                                           std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::multimap<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::key_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::key_type>() << \", \"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::mapped_type>>,\n              M, KT>::get()\n       << add_qualifiers<typename type::mapped_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::multiset, std::decay_t<std::remove_reference_t<\n                                           std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::multiset<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::key_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::key_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::set, std::decay_t<std::remove_reference_t<\n                                      std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::set<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::key_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::key_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\n// STL Unordered associative containers\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<\n        std::unordered_map,\n        std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::unordered_map<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::key_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::key_type>() << \", \"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::mapped_type>>,\n              M, KT>::get()\n       << add_qualifiers<typename type::mapped_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<\n        std::unordered_multimap,\n        std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::unordered_multimap<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::key_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::key_type>() << \", \"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::mapped_type>>,\n              M, KT>::get()\n       << add_qualifiers<typename type::mapped_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<\n        std::unordered_multiset,\n        std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::unordered_multiset<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::key_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::key_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<\n        std::unordered_set,\n        std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::unordered_set<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::key_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::key_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\n// STL Container adaptors\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<\n        std::priority_queue,\n        std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::priority_queue<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::value_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::value_type>() << \", \"\n       << construct_name<std::decay_t<std::remove_pointer_t<\n                             typename type::container_type>>,\n                         M, KT>::get()\n       << add_qualifiers<typename type::container_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::queue, std::decay_t<std::remove_reference_t<\n                                        std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::queue<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::value_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::value_type>() << \", \"\n       << construct_name<std::decay_t<std::remove_pointer_t<\n                             typename type::container_type>>,\n                         M, KT>::get()\n       << add_qualifiers<typename type::container_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::stack, std::decay_t<std::remove_reference_t<\n                                        std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::stack<\"\n       << construct_name<\n              std::decay_t<std::remove_pointer_t<typename type::value_type>>, M,\n              KT>::get()\n       << add_qualifiers<typename type::value_type>() << \", \"\n       << construct_name<std::decay_t<std::remove_pointer_t<\n                             typename type::container_type>>,\n                         M, KT>::get()\n       << add_qualifiers<typename type::container_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\n// STL Smart pointers\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::unique_ptr, std::decay_t<std::remove_reference_t<\n                                             std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::unique_ptr<\"\n       << construct_name<std::decay_t<std::remove_pointer_t<decltype(\n                             *std::declval<type>())>>,\n                         M, KT>::get()\n       << add_qualifiers<\n              std::remove_reference_t<decltype(*std::declval<type>())>>()\n       << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::shared_ptr, std::decay_t<std::remove_reference_t<\n                                             std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::shared_ptr<\"\n       << construct_name<std::decay_t<std::remove_pointer_t<decltype(\n                             *std::declval<type>())>>,\n                         M, KT>::get()\n       << add_qualifiers<\n              std::remove_reference_t<decltype(*std::declval<type>())>>()\n       << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n\ntemplate <typename T, typename M, typename KT>\nstruct construct_name<\n    T, M, KT,\n    Requires<tt::is_a_v<std::weak_ptr, std::decay_t<std::remove_reference_t<\n                                           std::remove_pointer_t<T>>>>>> {\n  using type = std::decay_t<std::remove_reference_t<std::remove_pointer_t<T>>>;\n  using element_type = typename type::element_type;\n  static std::string get() {\n    std::stringstream ss;\n    ss << \"std::weak_ptr<\"\n       << construct_name<std::decay_t<std::remove_pointer_t<element_type>>, M,\n                         KT>::get()\n       << add_qualifiers<element_type>() << \">\";\n    ss << add_qualifiers<T>();\n    return ss.str();\n  }\n};\n}  // namespace detail\n\n/*!\n * \\ingroup PrettyTypeGroup\n * \\brief typelist of basic types that can be pretty printed\n *\n * These are specializations of tt::Type<T>\n */\nusing basics_map =\n    detail::TemplateMap_t<char, signed char, unsigned char, wchar_t, char16_t,\n                          char32_t, int, unsigned int, long, unsigned long,\n                          long long, unsigned long long, short, unsigned short,\n                          float, double, long double, bool, std::string>;\n\n/*!\n * \\ingroup PrettyTypeGroup\n * \\brief A list of type traits to check if something is an STL member\n *\n * Contains a template alias with the name template_list of type traits that\n * identify STL containers that can be pretty printed\n */\nstruct stl_templates {\n  /// List of known STL classes that can be pretty printed\n  template <typename X>\n  using template_list = tmpl::list<\n      tt::is_a<std::vector, X>, tt::is_std_array<X>, tt::is_a<std::deque, X>,\n      tt::is_a<std::forward_list, X>, tt::is_a<std::list, X>,\n      tt::is_a<std::map, X>, tt::is_a<std::set, X>, tt::is_a<std::multiset, X>,\n      tt::is_a<std::multimap, X>, tt::is_a<std::unordered_map, X>,\n      tt::is_a<std::unordered_multimap, X>,\n      tt::is_a<std::unordered_multiset, X>, tt::is_a<std::unordered_set, X>,\n      tt::is_a<std::priority_queue, X>, tt::is_a<std::queue, X>,\n      tt::is_a<std::stack, X>, tt::is_a<std::unique_ptr, X>,\n      tt::is_a<std::shared_ptr, X>, tt::is_a<std::weak_ptr, X>>;\n};\n\n/*!\n * \\ingroup PrettyTypeGroup\n *  \\brief Returns a string with the prettiest typename known for the type T.\n *\n *  Example usage: auto name = get_name<T>();\n *\n *  \\tparam T the type to print\n *  \\tparam Map a tmpl::map of basic types (non-containers) and their Type<T>\n *  specializations that determine how to print the type name in a pretty form\n *  \\tparam KnownTemplates struct hold template alias tmpl::list of is_... that\n *  are known how to be printed pretty\n *  \\return std::string containing the typename\n */\ntemplate <typename T, typename Map = basics_map,\n          typename KnownTemplates = stl_templates>\nstd::string get_name() {\n  return detail::construct_name<T, Map, KnownTemplates>::get();\n}\n\n/*!\n * \\ingroup PrettyTypeGroup\n * \\brief Returns a string with the prettiest typename known for the runtime\n * type of x.\n *\n * The result will generally not be as pretty as the result of\n * get_name, but this function will report the derived type of a class\n * when only given a base class reference, which get_type cannot do.\n */\ntemplate <typename T>\nstd::string get_runtime_type_name(const T& x) {\n  return boost::core::demangle(typeid(x).name());\n}\n\nnamespace detail {\nstd::string extract_short_name(const std::string& name);\n}  // namespace detail\n\n/*!\n * \\ingroup PrettyTypeGroup\n * \\brief Return the \"short name\" of a class, that is, the name\n * without template parameters or scopes.\n */\ntemplate <typename T>\nstd::string short_name() {\n  return detail::extract_short_name(typeid(T).name());\n}\n\nnamespace detail {\ntemplate <typename T, typename = std::void_t<>>\nstruct name_helper {\n  static std::string name() { return pretty_type::short_name<T>(); }\n};\n\ntemplate <typename T>\nstruct name_helper<T, std::void_t<decltype(T::name())>> {\n  static std::string name() { return T::name(); }\n};\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup PrettyTypeGroup\n * \\brief Return the result of the `name()` member of a class. If a class\n * doesn't have a `name()` member, call `pretty_type::short_name<T>()` instead.\n *\n * \\warning Do not use this inside the `name()` member of struct. This\n * can lead to recursion as `pretty_type::name<Tag>()` will call the `name()`\n * member of the struct.\n */\ntemplate <typename T>\nstd::string name() {\n  return detail::name_helper<T>::name();\n}\n\ntemplate <typename T>\nstd::string name(const T& /*unused*/) {\n  return name<T>();\n}\n/// @}\n\n/*!\n * \\ingroup PrettyTypeGroup\n * \\brief Return a comma separated list of the `pretty_type::name` of every type\n * in a `tmpl::list`.\n *\n * \\note The `tmpl::list` must be flattened.\n */\ntemplate <typename List>\nstd::string list_of_names() {\n  static_assert(tt::is_a_v<tmpl::list, List>);\n  std::stringstream ss{};\n  bool first_element = true;\n  tmpl::for_each<List>([&first_element, &ss](auto v) {\n    using type = tmpl::type_from<decltype(v)>;\n    static_assert(not tt::is_a_v<tmpl::list, type>,\n                  \"The tmpl::list provided to pretty_type::list_of_names must \"\n                  \"be flattened.\");\n    if (not first_element) {\n      ss << \", \";\n    } else {\n      first_element = false;\n    }\n\n    ss << pretty_type::name<type>();\n  });\n\n  return ss.str();\n}\n\n/*!\n * \\ingroup PrettyTypeGroup\n * \\brief Return a vector of the `pretty_type::get_name` of every type\n * in a `tmpl::list`.\n */\ntemplate <typename... Types>\nstd::vector<std::string> vector_of_get_names(tmpl::list<Types...> /*meta*/) {\n  return {pretty_type::get_name<Types>()...};\n}\n}  // namespace pretty_type\n"
  },
  {
    "path": "src/Utilities/PrintHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <iterator>\n#include <sstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits/IsStreamable.hpp\"\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Either streams `value` or the string \"UNSTREAMABLE\"\ntemplate <typename StreamType, typename T>\nStreamType& print_value(StreamType& os, const T& value) {\n  if constexpr (tt::is_streamable_v<StreamType, T>) {\n    os << value;\n  } else {\n    os << \"UNSTREAMABLE\";\n  }\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Applies the function f(out, it) to each item from begin to\n * end, separated by commas and surrounded by parens.\n */\ntemplate <typename ForwardIt, typename Func>\nvoid sequence_print_helper(std::ostream& out, ForwardIt begin,\n                           const ForwardIt& end, Func f) {\n  out << \"(\";\n  if (begin != end) {\n    while (true) {\n      f(out, *begin++);\n      if (begin == end) {\n        break;\n      }\n      out << \",\";\n    }\n  }\n  out << \")\";\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Prints all the items as a comma separated list surrounded by parens.\n */\ntemplate <typename ForwardIt>\nvoid sequence_print_helper(std::ostream& out, ForwardIt begin,\n                           const ForwardIt& end) {\n  sequence_print_helper(\n      out, std::move(begin), end,\n      [](std::ostream& os,\n         const typename std::iterator_traits<ForwardIt>::value_type& it) {\n        print_value(os, it);\n      });\n}\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * Like sequence_print_helper, but sorts the string representations.\n */\ntemplate <typename ForwardIt, typename Func>\nvoid unordered_print_helper(std::ostream& out, ForwardIt begin,\n                            const ForwardIt& end, Func f) {\n  std::vector<std::string> entries;\n  while (begin != end) {\n    std::ostringstream ss;\n    f(ss, *begin++);\n    entries.push_back(ss.str());\n  }\n  std::sort(entries.begin(), entries.end());\n  sequence_print_helper(out, entries.begin(), entries.end());\n}\n\ntemplate <typename ForwardIt>\nvoid unordered_print_helper(std::ostream& out, ForwardIt begin,\n                            const ForwardIt& end) {\n  unordered_print_helper(\n      out, std::move(begin), end,\n      [](std::ostream& os,\n         const typename std::iterator_traits<ForwardIt>::value_type& it) {\n        print_value(os, it);\n      });\n}\n/// @}\n"
  },
  {
    "path": "src/Utilities/ProtocolHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace tt {\n\n/*!\n * \\ingroup ProtocolsGroup\n * \\brief Indicate a class conforms to the `Protocol`.\n *\n * (Publicly) inherit classes from this class to indicate they conform to the\n * `Protocol`.\n *\n * \\see Documentation on \\ref protocols\n */\ntemplate <typename Protocol>\nstruct ConformsTo {};\n\n// Note that std::is_convertible is used in the following type aliases as it\n// will not match private base classes (unlike std::is_base_of)\n\n/// @{\n/*!\n * \\ingroup ProtocolsGroup\n * \\brief Checks if the `ConformingType` conforms to the `Protocol`.\n *\n * This metafunction is SFINAE-friendly. See `tt::assert_conforms_to` for a\n * metafunction that is not SFINAE-friendly but that triggers static asserts\n * with diagnostic messages to understand why the `ConformingType` does not\n * conform to the `Protocol`.\n *\n * This metafunction only checks if the class derives off the protocol to reduce\n * compile time. Protocol conformance is tested rigorously in the unit tests\n * instead.\n *\n * \\see Documentation on \\ref protocols\n * \\see tt::assert_conforms_to\n */\ntemplate <typename ConformingType, typename Protocol>\nstruct conforms_to\n    : std::is_convertible<ConformingType*, ConformsTo<Protocol>*> {};\ntemplate <typename ConformingType, typename Protocol>\nconstexpr bool conforms_to_v = conforms_to<ConformingType, Protocol>::value;\n/// @}\n\n/// @{\n/*!\n * \\ingroup ProtocolsGroup\n * \\brief Assert that the `ConformingType` conforms to the `Protocol`.\n *\n * Similar to `tt::conforms_to`, but not SFINAE-friendly. Instead, triggers\n * static asserts with diagnostic messages to understand why the\n * `ConformingType` fails to conform to the `Protocol`.\n *\n * \\see Documentation on \\ref protocols\n * \\see tt::conforms_to\n */\ntemplate <typename ConformingType, typename Protocol>\nstruct assert_conforms_to : std::true_type {\n  static_assert(\n      tt::conforms_to_v<ConformingType, Protocol>,\n      \"The type does not indicate it conforms to the protocol. The type is \"\n      \"listed as the first template parameter to `assert_conforms_to` \"\n      \"and the protocol is listed as the second template parameter. \"\n      \"Have you forgotten to (publicly) inherit the type from \"\n      \"tt::ConformsTo<Protocol>?\");\n  // Implicitly instantiate Protocol::test in order to test conformance\n  static_assert(\n      not std::is_same_v<\n          decltype(typename Protocol::template test<ConformingType>{}), void>);\n};\n\ntemplate <typename ConformingType, typename Protocol>\nstatic constexpr bool assert_conforms_to_v =\n    assert_conforms_to<ConformingType, Protocol>::value;\n/// @}\n\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  StaticReturnApplyable.hpp\n  )\n"
  },
  {
    "path": "src/Utilities/Protocols/StaticReturnApplyable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace protocols {\n/// Protocol for a struct with a static `apply` function returning and\n/// taking arguments based on tags in `return_tags` and\n/// `argument_tags` type aliases.\n///\n/// \\snippet Test_StaticReturnApplyable.cpp StaticReturnApplyable\nstruct StaticReturnApplyable {\n  template <typename ConformingType>\n  struct test {\n    using argument_tags = typename ConformingType::argument_tags;\n    using return_tags = typename ConformingType::return_tags;\n    static_assert(tt::is_a_v<tmpl::list, argument_tags>);\n    static_assert(tt::is_a_v<tmpl::list, return_tags>);\n\n    static_assert(\n        std::is_same_v<return_tags, tmpl::remove_duplicates<return_tags>>,\n        \"return_tags should not contain duplicates.\");\n    // Duplicates in argument_tags are OK.\n  };\n};\n\n}  // namespace protocols\n"
  },
  {
    "path": "src/Utilities/Rational.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/Rational.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <boost/integer/common_factor_rt.hpp>\n#include <ostream>\n#include <pup.h>\n#include <tuple>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nnamespace {\n// This is needed a lot, so define a shorter name.\nstd::int64_t to64(const std::int32_t n) { return static_cast<std::int64_t>(n); }\n\ntemplate <typename IntType>\nstd::tuple<std::int32_t, std::int32_t> reduce(IntType numerator,\n                                              IntType denominator) {\n  const IntType common_factor = boost::integer::gcd(numerator, denominator);\n  numerator /= common_factor;\n  denominator /= common_factor;\n  if (denominator < 0) {\n    numerator = -numerator;\n    denominator = -denominator;\n  }\n  ASSERT(static_cast<std::int32_t>(numerator) == numerator and\n         static_cast<std::int32_t>(denominator) == denominator,\n         \"Rational overflow: \" << numerator << \"/\" << denominator);\n  return std::make_tuple(numerator, denominator);\n}\n}  // namespace\n\nRational::Rational(const std::int32_t numerator,\n                   const std::int32_t denominator) {\n  ASSERT(denominator != 0, \"Division by zero\");\n  std::tie(numerator_, denominator_) = reduce(numerator, denominator);\n}\n\ndouble Rational::value() const {\n  return static_cast<double>(numerator_) / static_cast<double>(denominator_);\n}\n\nRational Rational::inverse() const {\n  // Default construct to avoid the reduce() call.\n  ASSERT(*this != 0, \"Division by zero\");\n  Rational ret;\n  ret.numerator_ = denominator_;\n  ret.denominator_ = numerator_;\n  if (ret.denominator_ < 0) {\n    ret.numerator_ = -ret.numerator_;\n    ret.denominator_ = -ret.denominator_;\n  }\n  return ret;\n}\n\nRational& Rational::operator+=(const Rational& other) {\n  std::tie(numerator_, denominator_) =\n      reduce(to64(numerator_) * to64(other.denominator_) +\n             to64(denominator_) * to64(other.numerator_),\n             to64(denominator_) * to64(other.denominator_));\n  return *this;\n}\nRational& Rational::operator-=(const Rational& other) {\n  return *this += -other;\n}\nRational& Rational::operator*=(const Rational& other) {\n  std::tie(numerator_, denominator_) =\n      reduce(to64(numerator_) * to64(other.numerator()),\n             to64(denominator_) * to64(other.denominator()));\n  return *this;\n}\nRational& Rational::operator/=(const Rational& other) {\n  return *this *= other.inverse();\n}\n\nvoid Rational::pup(PUP::er& p) {\n  p | numerator_;\n  p | denominator_;\n}\n\nRational operator-(Rational r) {\n  // No reduced-form check needed\n  r.numerator_ = -r.numerator_;\n  return r;\n}\n\nRational operator+(const Rational& a, const Rational& b) {\n  Rational ret = a;\n  ret += b;\n  return ret;\n}\nRational operator-(const Rational& a, const Rational& b) {\n  Rational ret = a;\n  ret -= b;\n  return ret;\n}\nRational operator*(const Rational& a, const Rational& b) {\n  Rational ret = a;\n  ret *= b;\n  return ret;\n}\nRational operator/(const Rational& a, const Rational& b) {\n  Rational ret = a;\n  ret /= b;\n  return ret;\n}\n\nbool operator==(const Rational& a, const Rational& b) {\n  return a.numerator() == b.numerator() and a.denominator() == b.denominator();\n}\nbool operator!=(const Rational& a, const Rational& b) { return not(a == b); }\nbool operator<(const Rational& a, const Rational& b) {\n  return to64(a.numerator()) * to64(b.denominator()) <\n         to64(b.numerator()) * to64(a.denominator());\n}\nbool operator>(const Rational& a, const Rational& b) { return b < a; }\nbool operator<=(const Rational& a, const Rational& b) { return not(b < a); }\nbool operator>=(const Rational& a, const Rational& b) { return not(a < b); }\n\nRational abs(const Rational& r) { return r.numerator() >= 0 ? r : -r; }\n\nstd::ostream& operator<<(std::ostream& os, const Rational& r) {\n  return os << r.numerator() << '/' << r.denominator();\n}\n\nsize_t hash_value(const Rational& r) {\n  size_t h = 0;\n  boost::hash_combine(h, r.numerator());\n  boost::hash_combine(h, r.denominator());\n  return h;\n}\n\n// clang-tidy: do not modify std namespace (okay for hash)\nnamespace std {  // NOLINT\nsize_t hash<Rational>::operator()(const Rational& r) const {\n  return boost::hash<Rational>{}(r);\n}\n}  // namespace std\n"
  },
  {
    "path": "src/Utilities/Rational.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <iosfwd>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits/IsInteger.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\n/// \\ingroup UtilitiesGroup\n/// A rational number\n///\n/// This serves as a faster replacement for\n/// `boost::rational<std::int32_t>`.  As of Boost 1.65.0, arithmetic\n/// operators average about twice as fast, and ordering operators are\n/// about eight times as fast.\nclass Rational {\n public:\n  Rational() = default;\n  Rational(std::int32_t numerator, std::int32_t denominator);\n\n  // Allow implicit conversion of integers to Rationals, but don't\n  // allow doubles to implicitly convert to an integer and then to a\n  // Rational.\n  template <typename T, Requires<tt::is_integer_v<T>> = nullptr>\n  // NOLINTNEXTLINE(google-explicit-constructor,readability-avoid-const-params-in-decls)\n  Rational(const T integral_value) : Rational(integral_value, 1) {}\n\n  std::int32_t numerator() const { return numerator_; }\n  std::int32_t denominator() const { return denominator_; }\n\n  double value() const;\n\n  Rational inverse() const;\n\n  Rational& operator+=(const Rational& other);\n  Rational& operator-=(const Rational& other);\n  Rational& operator*=(const Rational& other);\n  Rational& operator/=(const Rational& other);\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n  friend Rational operator-(Rational r);\n\n private:\n  std::int32_t numerator_{0};\n  std::int32_t denominator_{1};\n};\n\nRational operator+(const Rational& a, const Rational& b);\nRational operator-(const Rational& a, const Rational& b);\nRational operator*(const Rational& a, const Rational& b);\nRational operator/(const Rational& a, const Rational& b);\n\nbool operator==(const Rational& a, const Rational& b);\nbool operator!=(const Rational& a, const Rational& b);\nbool operator<(const Rational& a, const Rational& b);\nbool operator>(const Rational& a, const Rational& b);\nbool operator<=(const Rational& a, const Rational& b);\nbool operator>=(const Rational& a, const Rational& b);\n\nRational abs(const Rational& r);\n\nstd::ostream& operator<<(std::ostream& os, const Rational& r);\n\nsize_t hash_value(const Rational& r);\n\nnamespace std {\ntemplate <>\nstruct hash<Rational> {\n  size_t operator()(const Rational& r) const;\n};\n}  // namespace std\n\nnamespace MakeWithValueImpls {\ntemplate <typename T>\nstruct MakeWithValueImpl<Rational, T> {\n  static Rational apply(const T& /*input*/, double value) {\n    ASSERT(static_cast<std::int32_t>(value) == value,\n           \"Only integer-valued Rationals can be created with MakeWithValue.\");\n    return Rational(static_cast<std::int32_t>(value));\n  }\n};\n}  // namespace MakeWithValueImpls\n"
  },
  {
    "path": "src/Utilities/Registration.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Helpers for derived class registration\n///\n/// SpECTRE's factory mechanism requires that the base class contain a\n/// list of all derived classes (in the special nested type alias\n/// `creatable_classes`).  This can be problematic when the desired\n/// list of derived classes can vary with use of the base class.\n/// These helpers provide a method to simplify handling these\n/// compile-time registrations.\n///\n/// Each optional derived class defines a registrar helper and both\n/// the base class and derived classes are templated on the list of\n/// registrars.  The typical structure of these classes is:\n/// \\snippet Test_Registration.cpp registrar_structure\n/// A concrete base class type with a specific set of derived classes\n/// is constructed like\n/// \\snippet Test_Registration.cpp registrar_use\n///\n/// It is frequently useful to default the registrar list in a derived\n/// class to the registrar for that class.  This ensures that any\n/// methods in the base class using the registrar list (such as those\n/// using `call_with_dynamic_type()`) work\n/// as expected on an explicitly constructed derived class with the\n/// list omitted.\nnamespace Registration {\n/// A template for defining a registrar.\n///\n/// A registrar for a class can be defined by making a type alias to\n/// this struct, filling in the registrant.\n/// \\snippet Test_Registration.cpp registrar\n/// In more complex cases (such as with non-type template\n/// parameters) defining a registrar manually may be necessary.\n/// \\snippet Test_Registration.cpp custom_registrar\ntemplate <template <typename...> class Registrant, typename... Args>\nstruct Registrar {\n  // Final registrant type, specialized with input Args... and the\n  // RegistrarList it was extracted from.  This will be a full\n  // specialization of a derived class that uses the registration\n  // framework.\n  template <typename RegistrarList>\n  using f = Registrant<Args..., RegistrarList>;\n};\n\nnamespace detail {\ntemplate <typename RegistrarList, typename Registrar>\nstruct registrant {\n  using type = typename Registrar::template f<RegistrarList>;\n};\n}  // namespace detail\n\n/// Transform a list of registrars into the list of associated\n/// registrants.  This is usually used to define the\n/// `creatable_classes` type list.\ntemplate <typename RegistrarList>\nusing registrants = tmpl::transform<\n    tmpl::remove_duplicates<RegistrarList>,\n    detail::registrant<tmpl::pin<tmpl::remove_duplicates<RegistrarList>>,\n                       tmpl::_1>>;\n}  // namespace Registration\n"
  },
  {
    "path": "src/Utilities/Requires.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\file\n/// Defines the type alias Requires\n\n#include <cstddef>\n\nnamespace Requires_detail {\ntemplate <bool B>\nstruct requires_impl {\n  using template_error_type_failed_to_meet_requirements_on_template_parameters =\n      std::nullptr_t;\n};\n\ntemplate <>\nstruct requires_impl<false> {};\n}  // namespace Requires_detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Express requirements on the template parameters of a function or\n * class, replaces `std::enable_if_t`\n *\n * Replacement for `std::enable_if_t` and Concepts for expressing requirements\n * on template parameters. This does not require merging of the Concepts\n * TS (whose merit is debatable) and provides an \"error message\" if substitution\n * of a template parameter failed. Specifically, the compiler error will contain\n * \"template_error_type_failed_to_meet_requirements_on_template_parameters\",\n * aiding the user of a function or class in tracking down the list of\n * requirements on the deduced type.\n *\n * For example, if a function `foo` is defined as:\n * \\snippet Utilities/Test_Requires.cpp foo_definition\n * then calling the function with a list, `foo(std::list<double>{});` results in\n * the following compilation error from clang:\n * \\code\n * ./tests/Unit/Utilities/Test_Requires.cpp:29:3: error: no matching function\n *    for call to 'foo'\n *   foo(std::list<double>{});\n *   ^~~\n * ./tests/Unit/Utilities/Test_Requires.cpp:15:13: note: candidate\n *     template ignored: substitution failure [with T = std::__1::list<double,\n *     std::__1::allocator<double> >]: no type named\n *     'template_error_type_failed_to_meet_requirements_on_template_parameters'\n *     in 'Requires_detail::requires_impl<false>'\n * std::string foo(const T&) {\n *             ^\n * 1 error generated.\n * \\endcode\n *\n * Here is an example of how write function overloads using `Requires` or to\n * express constraints on the template parameters:\n * \\snippet Utilities/Test_Requires.cpp function_definitions\n *\n * \\note\n * Using `Requires` is safer than using `std::enable_if_t` because the\n * nested type alias is of type `std::nullptr_t` and so usage is always:\n * \\code\n * template <typename T, Requires<(bool depending on T)> = nullptr>\n * \\endcode\n */\ntemplate <bool B>\nusing Requires = typename Requires_detail::requires_impl<\n    B>::template_error_type_failed_to_meet_requirements_on_template_parameters;\n"
  },
  {
    "path": "src/Utilities/Serialization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Serialization)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\n# spectre_target_sources(\n#   ${LIBRARY}\n#   PUBLIC\n#   )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CharmPupable.hpp\n  PupBoost.hpp\n  PupStlCpp11.hpp\n  PupStlCpp17.hpp\n  RegisterDerivedClassesWithCharm.hpp\n  Serialize.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Boost::boost\n  Charmxx::pup\n  Utilities\n  )\n"
  },
  {
    "path": "src/Utilities/Serialization/CharmPupable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines macros to allow serialization of abstract template base classes\n\n#pragma once\n\n#include <pup.h>\n\n#ifdef __GNUC__\n#pragma GCC system_header\n#endif\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Mark derived classes as serializable\n *\n * Any class that inherits from an abstract base class where the base class is\n * not a template class must contain this macro if it is to be serialized.\n */\n#define WRAPPED_PUPable_decl_template(className) \\\n  PUPable_decl_template(SINGLE_ARG(className))  // NOLINT\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Mark derived template classes as serializable\n *\n * Any class that inherits from an abstract base class where the base class is\n * a template class must contain this macro if it is to be serialized.\n */\n#define WRAPPED_PUPable_decl_base_template(baseClassName, className) \\\n  PUPable_decl_base_template(SINGLE_ARG(baseClassName), /* NOLINT */ \\\n                             SINGLE_ARG(className))     // NOLINT\n\n/// Wraps the Charm++ macro, see the Charm++ documentation\n#define WRAPPED_PUPable_decl(className) \\\n  PUPable_decl(SINGLE_ARG(className))  // NOLINT\n\n/// Wraps the Charm++ macro, see the Charm++ documentation\n#define WRAPPED_PUPable_abstract(className) \\\n  PUPable_abstract(SINGLE_ARG(className))  // NOLINT\n"
  },
  {
    "path": "src/Utilities/Serialization/PupBoost.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines helper functions for working with boost.\n\n#pragma once\n\n#include <boost/container/small_vector.hpp>\n#include <boost/container/static_vector.hpp>\n#include <boost/math/quaternion.hpp>\n#include <boost/rational.hpp>\n#include <pup.h>\n\nnamespace PUP {\n/// @{\n/// \\ingroup ParallelGroup\n/// Serialization of boost::rational for Charm++\ntemplate <class T>\nvoid pup(PUP::er& p, boost::rational<T>& var) {  // NOLINT\n  if (p.isUnpacking()) {\n    typename boost::rational<T>::int_type n, d;\n    p | n;\n    p | d;\n    var.assign(n, d);\n  } else {\n    typename boost::rational<T>::int_type n = var.numerator();\n    typename boost::rational<T>::int_type d = var.denominator();\n    p | n;\n    p | d;\n  }\n}\n\ntemplate <typename T>\ninline void operator|(PUP::er& p, boost::rational<T>& var) {  // NOLINT\n  pup(p, var);\n}\n/// @}\n\ntemplate <class T, size_t N>\nvoid pup(PUP::er& p, boost::container::static_vector<T, N>& v) {\n  auto size = v.size();\n  p | size;\n  v.resize(size);\n  if (PUP::as_bytes<T>::value) {\n    PUParray(p, v.data(), size);\n  } else {\n    for (auto& x : v) {\n      p | x;\n    }\n  }\n}\n\ntemplate <class T, size_t N>\nvoid operator|(PUP::er& p, boost::container::static_vector<T, N>& v) {\n  pup(p, v);\n}\n\ntemplate <class T, size_t N>\nvoid pup(PUP::er& p, boost::container::small_vector<T, N>& v) {\n  auto size = v.size();\n  p | size;\n  v.resize(size);\n  if (PUP::as_bytes<T>::value) {\n    PUParray(p, v.data(), size);\n  } else {\n    for (auto& x : v) {\n      p | x;\n    }\n  }\n}\n\ntemplate <class T, size_t N>\nvoid operator|(PUP::er& p, boost::container::small_vector<T, N>& v) {\n  pup(p, v);\n}\n\ntemplate <typename T>\nvoid pup(PUP::er& p, boost::math::quaternion<T>& quaternion) {\n  T component_1 = quaternion.R_component_1();\n  T component_2 = quaternion.R_component_2();\n  T component_3 = quaternion.R_component_3();\n  T component_4 = quaternion.R_component_4();\n  p | component_1;\n  p | component_2;\n  p | component_3;\n  p | component_4;\n  if (p.isUnpacking()) {\n    quaternion = boost::math::quaternion<T>(component_1, component_2,\n                                            component_3, component_4);\n  }\n}\n\ntemplate <typename T>\nvoid operator|(PUP::er& p, boost::math::quaternion<T>& quaternion) {\n  pup(p, quaternion);\n}\n}  // namespace PUP\n"
  },
  {
    "path": "src/Utilities/Serialization/PupStlCpp11.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// PUP routines for new C+11 STL containers and other standard\n/// library objects Charm does not provide implementations for\n\n#pragma once\n\n#include <cstddef>\n#include <map>\n#include <pup.h>\n#include <pup_stl.h>\n#include <utility>\n\nnamespace PUP {\n\n/// \\ingroup ParallelGroup\n/// Serialization of std::atomic for Charm++\ntemplate <typename T>\nvoid pup(PUP::er& p, std::atomic<T>& d) {  // NOLINT\n  T local_d;\n  if (p.isUnpacking()) {\n    p | local_d;\n    d.store(std::move(local_d));\n  } else {\n    local_d = d.load();\n    p | local_d;\n  }\n}\n\n/// \\ingroup ParallelGroup\n/// Serialization of std::atomic for Charm++\ntemplate <typename T>\nvoid operator|(er& p, std::atomic<T>& d) {  // NOLINT\n  pup(p, d);\n}\n}  // namespace PUP\n\n/// \\ingroup ParallelGroup\n/// Temporary serialization function for a `std::map` with a comparator to\n/// circumvent a charm bug.\ntemplate <typename K, typename T, typename C>\nvoid pup_override(PUP::er& p, std::map<K, T, C>& m) {\n  if (p.isUnpacking()) {\n    size_t size;\n    std::pair<K, T> pair;\n    p | size;\n    for (size_t i = 0; i < size; ++i) {\n      p | pair;\n      m.insert(pair);\n    }\n  } else {\n    size_t size = m.size();\n    p | size;\n    for (auto& val : m) {\n      p | val;\n    }\n  }\n}\n"
  },
  {
    "path": "src/Utilities/Serialization/PupStlCpp17.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cassert>\n#include <optional>\n#include <pup_stl.h>\n#include <utility>\n#include <variant>\n\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\n/// \\ingroup ParallelGroup\n/// Serialization of std::optional for Charm++\ntemplate <typename T>\nvoid pup(er& p, std::optional<T>& t) {  // NOLINT\n  bool valid = false;\n  if (p.isUnpacking()) {\n    p | valid;\n    if (valid) {\n      // default construct in a manner that doesn't require a copy.\n      // This is necessary when holding objects like pair<const Key, unique_ptr>\n      // in unordered map-type structures. The reason this case is tricky is\n      // because the const Key is non-movable while the unique_ptr is\n      // non-copyable. Our only option is in-place construction.\n      t.emplace();\n      p | *t;\n    } else {\n      t.reset();\n    }\n  } else {\n    valid = t.has_value();\n    p | valid;\n    if (valid) {\n      p | *t;\n    }\n  }\n}\n\n/// \\ingroup ParallelGroup\n/// Serialization of std::optional for Charm++\ntemplate <typename T>\nvoid operator|(er& p, std::optional<T>& t) {  // NOLINT\n  pup(p, t);\n}\n\n/// \\ingroup ParallelGroup\n/// Serialization of std::variant for Charm++\ntemplate <typename... Ts>\nvoid pup(er& p, std::variant<Ts...>& t) {  // NOLINT\n  // clang tidy: complains about pointer decay, I don't understand the error\n  // since nothing here is doing anything with pointers.\n  assert(not t.valueless_by_exception());  // NOLINT\n\n  size_t current_index = 0;\n  size_t send_index = t.index();\n  p | send_index;\n\n  const auto pup_helper = [&current_index, &p, send_index, &t](auto type_v) {\n    using type = tmpl::type_from<decltype(type_v)>;\n    if (UNLIKELY(current_index == send_index)) {\n      if (p.isUnpacking()) {\n        type temp{};\n        p | temp;\n        t = std::move(temp);\n      } else {\n        p | std::get<type>(t);\n      }\n    }\n    ++current_index;\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(pup_helper(tmpl::type_<Ts>{}));\n}\n\n/// \\ingroup ParallelGroup\n/// Serialization of std::variant for Charm++\ntemplate <typename... Ts>\nvoid operator|(er& p, std::variant<Ts...>& t) {  // NOLINT\n  pup(p, t);\n}\n}  // namespace PUP\n"
  },
  {
    "path": "src/Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Functions for serializing factory-created classes\n\n#pragma once\n\n#include <boost/algorithm/string.hpp>\n#include <pup.h>\n#include <typeinfo>\n\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\brief String representation of a type that is somewhat stable\n *\n * This string representation is used to identify the type in serialized data,\n * including data written to disk. Therefore, the registration name must be\n * somewhat reliable across invocations of the same program, across different\n * compilers, and across different executables. C++ provides no standard tool\n * for this. In particular, the name returned by `typeid().name()` explicitly\n * provides no guarantees, though for GCC and Clang it returns the mangled name\n * which should be ABI-stable. See docs:\n * https://en.cppreference.com/w/cpp/types/type_info/name\n *\n * To obtain a somewhat reliable string representation we use\n * `pretty_type::get_name` (which actually demangles the `typeid().name()`). It\n * also doesn't make any guarantees, but at least it gives a human-readable\n * string representation close to the actual source code. We remove whitespaces\n * from the name because we found inconsistent whitespace on different machines,\n * likely because the compiler's internal demangling routine got updated:\n * https://github.com/sxs-collaboration/spectre/issues/4944\n *\n * To improve reliability further in the future we may call a dedicated function\n * on `T`, such as `static std::string registration_name()`, which is expected\n * to return a unique name (including any template parameters etc). However,\n * adding this requirement to all PUP::able classes requires quite a lot of code\n * on our part (but is ultimately the safest option).\n *\n * \\warning Renaming classes or namespaces will change the registration name and\n * hence break compatibility with data written by older versions of the code, as\n * does changing the implementation of this function. This means we can't\n * deserialize coordinate maps written in H5 files for interpolation of volume\n * data.\n */\ntemplate <typename T>\nstd::string registration_name() {\n  std::string result = pretty_type::get_name<T>();\n  boost::algorithm::erase_all(result, \" \");\n  return result;\n}\n\n/// Register specified classes.  This function can either take classes\n/// to register as template arguments or take a `tmpl::list` of\n/// classes as a function argument.\ntemplate <typename... Registrants>\nvoid register_classes_with_charm(\n    const tmpl::list<Registrants...> /*meta*/ = {}) {\n  const auto helper = [](auto class_v) {\n    using class_to_register = typename decltype(class_v)::type;\n    // We use PUPable_reg2 because this takes as a second argument the name of\n    // the class (as a `const char*`), while PUPable_reg converts the argument\n    // verbatim to a string using the `#` preprocessor operator.\n    PUPable_reg2(class_to_register,\n                 registration_name<class_to_register>().c_str());\n  };\n  (void)helper;\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(tmpl::type_<Registrants>{}));\n}\n\n/// Register derived classes of the `Base` class\ntemplate <typename Base>\nvoid register_derived_classes_with_charm() {\n  register_classes_with_charm(typename Base::creatable_classes{});\n}\n\n/// Register all classes in Metavariables::factory_classes\ntemplate <typename Metavariables>\nvoid register_factory_classes_with_charm() {\n  register_classes_with_charm(\n      tmpl::filter<\n          tmpl::flatten<tmpl::values_as_sequence<\n              typename Metavariables::factory_creation::factory_classes>>,\n          std::is_base_of<PUP::able, tmpl::_1>>{});\n}\n"
  },
  {
    "path": "src/Utilities/Serialization/Serialize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines the serialize and deserialize functions.\n\n#pragma once\n\n#include <pup.h>\n#include <type_traits>\n#include <vector>\n\n#include \"Utilities/Gsl.hpp\"\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Serialize an object using PUP.\n *\n * The type to serialize as must be explicitly specified.  We require\n * this because a mismatch between the serialize and deserialize calls\n * causes undefined behavior and we do not want this to depend on\n * inferred types for safety.\n *\n * \\tparam T type to serialize\n */\ntemplate <typename T>\nstd::vector<char> serialize(const T& obj) {\n  const T& typed_obj = obj;\n  // pup routine is non-const, but shouldn't modify anything in serialization\n  // mode.\n  // clang-tidy: do not use const_cast\n  auto& mut_obj = const_cast<T&>(typed_obj);  // NOLINT\n\n  PUP::sizer sizer;\n  sizer | mut_obj;\n  std::vector<char> data(sizer.size());\n  PUP::toMem writer(data.data());\n  writer | mut_obj;\n\n  return data;\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Deserialize an object using PUP.\n *\n * \\tparam T the type to deserialize to\n */\ntemplate <typename T>\nT deserialize(const void* const data) {  // NOLINT\n  // clang-tidy: no const in forward decl (this is a definition)\n  PUP::fromMem reader(data);\n  T result{};\n  reader | result;\n  return result;\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Deserialize an object using PUP.\n *\n * \\tparam T the type to deserialize to.\n */\ntemplate <typename T>\nvoid deserialize(const gsl::not_null<T*> result,\n                 const void* const data) {  // NOLINT\n  // clang-tidy: no const in forward decl (this is a definition)\n  PUP::fromMem reader(data);\n  reader | *result;\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Returns the size of an object in bytes\n */\ntemplate <typename T>\nsize_t size_of_object_in_bytes(const T& obj) {\n  PUP::sizer sizer;\n  sizer | const_cast<T&>(obj);  // NOLINT\n  return sizer.size();\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Serializes and deserializes an object `t` of type `T`\n *\n * Performs a PUP round-trip on `t`, returning the deserialized copy.\n * Useful for testing that a class is correctly serializable, and for\n * copying objects that store state in a way only accessible via PUP.\n *\n * \\requires `T` to be default constructible.\n */\ntemplate <typename T>\nT serialize_and_deserialize(const T& t) {\n  static_assert(\n      std::is_default_constructible_v<T>,\n      \"Cannot use serialize_and_deserialize if a class is not default \"\n      \"constructible.\");\n  return deserialize<T>(serialize<T>(t).data());\n}\n\n/*!\n * \\ingroup ParallelGroup\n * \\brief Serializes and deserializes an object `t` of type `T` into `result`\n *\n * Performs a PUP round-trip on `t`, writing the deserialized value into\n * the pre-existing object pointed to by `result`.\n *\n * Unlike the value-returning overload, `T` does not need to be default\n * constructible, since the result is written into the pre-existing `*result`.\n */\ntemplate <typename T>\nvoid serialize_and_deserialize(const gsl::not_null<T*> result, const T& t) {\n  deserialize<T>(result, serialize<T>(t).data());\n}\n"
  },
  {
    "path": "src/Utilities/SetNumberOfGridPoints.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <type_traits>\n#include <vector>\n\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Implementations of \\link set_number_of_grid_points \\endlink\n///\n/// Specializations may be trivial, meaning the object is similar to a\n/// `double` and doesn't actually represent grid data.  The only\n/// requirement for trivial specializations is defining `is_trivial`\n/// as `true`.\n///\n/// Non-trivial specializations must define `is_trivial` to `false`,\n/// and must define a static `apply` function with the signature shown\n/// in the below example.  Non-trivial specializations must also be\n/// accompanied by a specialization of the\n/// MakeWithValueImpls::NumberOfPoints struct.  The `apply` function\n/// will only be called if the current size is incorrect, so\n/// implementations should not check the current size.\n///\n/// \\snippet Test_SetNumberOfGridPoints.cpp SetNumberOfGridPointsImpl\nnamespace SetNumberOfGridPointsImpls {\n/// Default implementation is not defined.\ntemplate <typename T, typename = std::nullptr_t>\nstruct SetNumberOfGridPointsImpl;\n}  // namespace SetNumberOfGridPointsImpls\n\n/// \\ingroup DataStructuresGroup\n/// Change the number of grid points in an object.\n///\n/// Change the number of points stored in \\p result to a given value\n/// or to match another object.  If \\p pattern is a `size_t` it will\n/// be used as the number of points, otherwise it will be interpreted\n/// as data on a collection of grid points.\n///\n/// \\see SetNumberOfGridPointsImpls, make_with_value\ntemplate <typename T, typename U>\nvoid set_number_of_grid_points(const gsl::not_null<T*> result,\n                               const U& pattern) {\n  (void)result;\n  (void)pattern;\n  if constexpr (not SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n                    T>::is_trivial) {\n    const size_t size = MakeWithValueImpls::number_of_points(pattern);\n    if (UNLIKELY(MakeWithValueImpls::number_of_points(*result) != size)) {\n      SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<T>::apply(result,\n                                                                      size);\n    }\n  }\n}\n\ntemplate <>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<double> {\n  static constexpr bool is_trivial = true;\n};\n\ntemplate <>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n    std::complex<double>> {\n  static constexpr bool is_trivial = true;\n};\n\ntemplate <typename T, size_t N>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<std::array<T, N>> {\n  static constexpr bool is_trivial =\n      N == 0 or SetNumberOfGridPointsImpl<T>::is_trivial;\n  static void apply(const gsl::not_null<std::array<T, N>*> result,\n                    const size_t size) {\n    for (auto& entry : *result) {\n      set_number_of_grid_points(make_not_null(&entry), size);\n    }\n  }\n};\n\ntemplate <typename T>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<std::vector<T>> {\n  static constexpr bool is_trivial = SetNumberOfGridPointsImpl<T>::is_trivial;\n  static void apply(const gsl::not_null<std::vector<T>*> result,\n                    const size_t size) {\n    for (auto& entry : *result) {\n      set_number_of_grid_points(make_not_null(&entry), size);\n    }\n  }\n};\n\n"
  },
  {
    "path": "src/Utilities/Simd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# If XSIMD exists, set up a wrapper target.\n#\n# Having a wrapper target will make it easier to replace XSIMD\n# with another SIMD library if we ever need to.\nset(LIBRARY Simd)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Simd.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  xsimd\n  )\n\n  message(STATUS \"Enabling simd support\")\n"
  },
  {
    "path": "src/Utilities/Simd/Simd.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#ifdef SPECTRE_USE_XSIMD\n#include <limits>\n#include <xsimd/xsimd.hpp>\n\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n\n/// Namespace containing SIMD functions based on XSIMD.\nnamespace simd = xsimd;\n\nnamespace MakeWithValueImpls {\ntemplate <typename U, typename T, typename Arch>\nstruct MakeWithValueImpl<xsimd::batch<U, Arch>, T> {\n  static SPECTRE_ALWAYS_INLINE xsimd::batch<U, Arch> apply(const T& /* input */,\n                                                           const U value) {\n    return xsimd::batch<U, Arch>(value);\n  }\n};\n}  // namespace MakeWithValueImpls\n\nnamespace xsimd {\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(value_type)\n\nnamespace detail {\ntemplate <typename T>\nstruct size_impl : std::integral_constant<size_t, 1> {};\n\ntemplate <typename T, typename A>\nstruct size_impl<batch<T, A>>\n    : std::integral_constant<size_t, batch<T, A>::size> {};\n}  // namespace detail\n\ntemplate <typename T>\nconstexpr size_t size() {\n  return detail::size_impl<T>::value;\n}\n\nnamespace detail {\ntemplate <typename T, size_t... Is>\nT make_sequence_impl(std::index_sequence<Is...> /*meta*/) {\n  return T{static_cast<typename T::value_type>(Is)...};\n}\n}  // namespace detail\n\ntemplate <typename T>\nT make_sequence() {\n  return detail::make_sequence_impl<T>(std::make_index_sequence<size<T>()>{});\n}\n}  // namespace xsimd\n\nnamespace std {\ntemplate <typename T, typename Arch>\nclass numeric_limits<::xsimd::batch<T, Arch>> : public numeric_limits<T> {\n public:\n  static constexpr bool is_iec559 = false;\n\n  static ::xsimd::batch<T, Arch> min() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::min());\n  }\n  static ::xsimd::batch<T, Arch> lowest() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::lowest());\n  }\n  static ::xsimd::batch<T, Arch> max() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::max());\n  }\n  static ::xsimd::batch<T, Arch> epsilon() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::epsilon());\n  }\n  static ::xsimd::batch<T, Arch> round_error() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::round_error());\n  }\n  static ::xsimd::batch<T, Arch> infinity() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::infinity());\n  }\n  static ::xsimd::batch<T, Arch> quiet_NaN() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::quiet_NaN());\n  }\n  static ::xsimd::batch<T, Arch> signaling_NaN() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::signaling_NaN());\n  }\n  static ::xsimd::batch<T, Arch> denorm_min() {\n    return ::xsimd::batch<T, Arch>(numeric_limits<T>::denorm_min());\n  }\n};\n}  // namespace std\n#else  // no xsimd\n\n#include <cmath>\n#include <complex>\n#include <type_traits>\n\n#include \"Utilities/Requires.hpp\"\n\nnamespace simd {\ntemplate <typename T, typename A = void>\nclass batch;\ntemplate <typename T, typename A = void>\nclass batch_bool;\n\ntemplate <typename T>\nstruct scalar_type {\n  using type = T;\n};\ntemplate <typename T>\nusing scalar_type_t = typename scalar_type<T>::type;\n\ntemplate <typename T>\nstruct mask_type {\n  using type = bool;\n};\ntemplate <typename T>\nusing mask_type_t = typename mask_type<T>::type;\n\ntemplate <typename T>\nstruct is_batch : std::false_type {};\n\ntemplate <typename T, typename A>\nstruct is_batch<batch<T, A>> : std::true_type {};\n\nnamespace detail {\ntemplate <typename T>\nstruct size_impl : std::integral_constant<size_t, 1> {};\n}  // namespace detail\n\ntemplate <typename T>\nconstexpr size_t size() {\n  return detail::size_impl<T>::value;\n}\n\nnamespace detail {\ntemplate <typename T, size_t... Is>\nT make_sequence_impl(std::index_sequence<Is...> /*meta*/) {\n  return T{static_cast<typename T::value_type>(Is)...};\n}\n}  // namespace detail\n\ntemplate <typename T>\nT make_sequence() {\n  return detail::make_sequence_impl<T>(std::make_index_sequence<size<T>()>{});\n}\n\n// NOLINTBEGIN(misc-unused-using-decls)\nusing std::abs;\nusing std::acos;\nusing std::acosh;\nusing std::arg;\nusing std::asin;\nusing std::asinh;\nusing std::atan;\nusing std::atan2;\nusing std::atanh;\nusing std::cbrt;\nusing std::ceil;\nusing std::conj;\nusing std::copysign;\nusing std::cos;\nusing std::cosh;\nusing std::erf;\nusing std::erfc;\nusing std::exp;\nusing std::exp2;\nusing std::expm1;\nusing std::fabs;\nusing std::fdim;\nusing std::floor;\nusing std::fmax;\nusing std::fmin;\nusing std::fmod;\nusing std::hypot;\nusing std::isfinite;\nusing std::isinf;\nusing std::isnan;\nusing std::ldexp;\nusing std::lgamma;\nusing std::log;\nusing std::log10;\nusing std::log1p;\nusing std::log2;\nusing std::max;\nusing std::min;\nusing std::modf;\nusing std::nearbyint;\nusing std::nextafter;\nusing std::norm;\nusing std::polar;\nusing std::proj;\nusing std::remainder;\nusing std::rint;\nusing std::round;\nusing std::sin;\nusing std::sinh;\nusing std::sqrt;\nusing std::tan;\nusing std::tanh;\nusing std::tgamma;\nusing std::trunc;\n// NOLINTEND(misc-unused-using-decls)\n\ninline bool all(const bool mask) { return mask; }\n\ninline bool any(const bool mask) { return mask; }\n\ninline bool none(const bool mask) { return not mask; }\n\ntemplate <typename T, Requires<std::is_scalar_v<T>> = nullptr>\nT clip(const T& val, const T& low, const T& hi) {\n  assert(low <= hi && \"ordered clipping bounds\");\n  return low > val ? low : (hi < val ? hi : val);\n}\n\n#if defined(__GLIBC__)\ninline float exp10(const float& x) { return ::exp10f(x); }\ninline double exp10(const double& x) { return ::exp10(x); }\n#else\ninline float exp10(const float& x) {\n  const float ln10 = std::log(10.f);\n  return std::exp(ln10 * x);\n}\ninline double exp10(const double& x) {\n  const double ln10 = std::log(10.);\n  return std::exp(ln10 * x);\n}\n#endif\n\ninline double sign(const bool& v) { return static_cast<double>(v); }\n\ntemplate <typename T>\nT sign(const T& v) {\n  return v < static_cast<T>(0)    ? static_cast<T>(-1.)\n         : v == static_cast<T>(0) ? static_cast<T>(0.)\n                                  : static_cast<T>(1.);\n}\n\ntemplate <typename T>\nT select(const bool cond, const T true_branch, const T false_branch) {\n  return cond ? true_branch : false_branch;\n}\n\ninline std::pair<float, float> sincos(const float val) {\n  // The nvcc compiler's built-in __sincos is for GPU code, not CPU code. In\n  // the case that we are running on a GPU (__CUDA_ARCH__ is defined) or we\n  // are not using nvcc then use the builtin, otherwise call sin and cos\n  // separately.\n#if (defined(__CUDACC__) && defined(__CUDA_ARCH__)) or (not defined(__CUDACC__))\n  float result_sin{};\n  float result_cos{};\n  __sincosf(val, &result_sin, &result_cos);\n  return std::pair{result_sin, result_cos};\n#else\n  return std::pair{sin(val), cos(val)};\n#endif\n}\n\ninline std::pair<double, double> sincos(const double val) {\n  // The nvcc compiler's built-in __sincos is for GPU code, not CPU code. In\n  // the case that we are running on a GPU (__CUDA_ARCH__ is defined) or we\n  // are not using nvcc then use the builtin, otherwise call sin and cos\n  // separately.\n#if (defined(__CUDACC__) && defined(__CUDA_ARCH__)) or (not defined(__CUDACC__))\n  double result_sin{};\n  double result_cos{};\n  __sincos(val, &result_sin, &result_cos);\n  return std::pair{result_sin, result_cos};\n#else\n  return std::pair{sin(val), cos(val)};\n#endif\n}\n\ntemplate <typename T, Requires<std::is_integral_v<T>> = nullptr>\nT fma(const T a, const T b, const T c) {\n  return a * b + c;\n}\n\ntemplate <typename T, Requires<std::is_floating_point_v<T>> = nullptr>\nT fma(const T a, const T b, const T c) {\n  return std::fma(a, b, c);\n}\n\ntemplate <typename T, Requires<std::is_integral_v<T>> = nullptr>\nT fms(const T& a, const T& b, const T& c) {\n  return a * b - c;\n}\n\ntemplate <typename T, Requires<std::is_floating_point_v<T>> = nullptr>\nT fms(const T a, const T b, const T c) {\n  return std::fma(a, b, -c);\n}\n\ntemplate <typename T, Requires<std::is_integral_v<T>> = nullptr>\nT fnma(const T a, const T b, const T c) {\n  return -(a * b) + c;\n}\n\ntemplate <typename T, Requires<std::is_floating_point_v<T>> = nullptr>\nT fnma(const T a, const T b, const T c) {\n  return std::fma(-a, b, c);\n}\n\ntemplate <typename T, Requires<std::is_integral_v<T>> = nullptr>\nT fnms(const T a, const T b, const T c) {\n  return -(a * b) - c;\n}\n\ntemplate <typename T, Requires<std::is_floating_point_v<T>> = nullptr>\nT fnms(const T a, const T b, const T c) {\n  return -std::fma(a, b, c);\n}\n\ntemplate <typename Arch = void, typename From>\nFrom load(From* mem) {\n  static_assert(std::is_arithmetic_v<From>);\n  return *mem;\n}\n\ntemplate <typename Arch = void, typename T>\nvoid store(T* mem, const T& val) {\n  static_assert(std::is_arithmetic_v<T>);\n  *mem = val;\n}\n\ntemplate <typename Arch = void, typename From>\nFrom load_unaligned(From* mem) {\n  static_assert(std::is_arithmetic_v<From>);\n  return *mem;\n}\n\ntemplate <typename Arch = void, typename T>\nvoid store_unaligned(T* mem, const T& val) {\n  static_assert(std::is_arithmetic_v<T>);\n  *mem = val;\n}\n}  // namespace simd\n#endif\n"
  },
  {
    "path": "src/Utilities/SnakeCase.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/SnakeCase.hpp\"\n\n#include <string>\n\nstd::string camel_case_to_snake_case(const std::string& input) {\n  std::string output;\n  for (const char c : input) {\n    if (static_cast<bool>(std::isupper(c)) and not output.empty()) {\n      output += '_';\n    }\n    output += static_cast<char>(std::tolower(c));\n  }\n  return output;\n}\n\nstd::string snake_case_to_camel_case(const std::string& input) {\n  std::string output;\n  bool to_upper = true;\n  for (const char c : input) {\n    if (c == '_') {\n      to_upper = true;\n    } else {\n      output += to_upper ? static_cast<char>(std::toupper(c)) : c;\n      to_upper = false;\n    }\n  }\n  return output;\n}\n"
  },
  {
    "path": "src/Utilities/SnakeCase.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n/// Transform a CamelCase string to snake_case\nstd::string camel_case_to_snake_case(const std::string& input);\n\n/// Transform a snake_case string to CamelCase\nstd::string snake_case_to_camel_case(const std::string& input);\n"
  },
  {
    "path": "src/Utilities/Spherepack.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\file\n/// Provides a C++ interface to the Fortran SPHEREPACK library.\n\nextern \"C\" {\nvoid shagsi_(const int&, const int&, double*, const int&, double*, const int&,\n             double*, const int&, gsl::not_null<int*>);\nvoid shags_(const int&, const int&, const int&, const int&, const int&,\n            const int&, const int&, const int&, const double*, const int&,\n            const int&, double*, double*, const int&, const int&, const int&,\n            const int&, const double*, const int&, double*, const int&,\n            gsl::not_null<int*>);\nvoid shsgsi_(const int&, const int&, double*, const int&, double*, const int&,\n             double*, const int&, gsl::not_null<int*>);\nvoid shsgs_(const int&, const int&, const int&, const int&, const int&,\n            const int&, const int&, const int&, double*, const int&, const int&,\n            const double*, const double*, const int&, const int&, const int&,\n            const int&, const double*, const int&, double*, const int&,\n            gsl::not_null<int*>);\nvoid vhagsi_(const int&, const int&, double*, const int&, double*, const int&,\n             gsl::not_null<int*>);\nvoid vhags_(const int&, const int&, const int&, const int&, const int&,\n            const int&, const int&, const int&, const double*, const double*,\n            const int&, const int&, double*, double*, double*, double*,\n            const int&, const int&, const double*, const int&, double*,\n            const int&, gsl::not_null<int*>);\nvoid vhsgsi_(const int&, const int&, double*, const int&, double*, const int&,\n             gsl::not_null<int*>);\nvoid vhsgs_(const int&, const int&, const int&, const int&, const int&,\n            const int&, const int&, const int&, double*, double*, const int&,\n            const int&, const double*, const double*, const double*,\n            const double*, const int&, const int&, const double*, const int&,\n            double*, const int&, gsl::not_null<int*>);\nvoid slapgs_(const int&, const int&, const int&, const int&, const int&,\n             const int&, const int&, const int&, double*, const int&,\n             const int&, const double*, const double*, const int&, const int&,\n             const double*, const int&, double*, const int&,\n             gsl::not_null<int*>);\nvoid gradgs_(const int&, const int&, const int&, const int&, const int&,\n             const int&, const int&, const int&, double*, double*, const int&,\n             const int&, const double*, const double*, const int&, const int&,\n             const double*, const int&, double*, const int&,\n             gsl::not_null<int*>);\nvoid divgs_(const int&, const int&, const int&, const int&, const int&,\n            const int&, const int&, const int&, double*, const int&, const int&,\n            const double*, const double*, const int&, const int&, const double*,\n            const int&, double*, const int&, gsl::not_null<int*>);\nvoid vrtgs_(const int&, const int&, const int&, const int&, const int&,\n            const int&, const int&, const int&, double*, const int&, const int&,\n            const double*, const double*, const int&, const int&, const double*,\n            const int&, double*, const int&, gsl::not_null<int*>);\nvoid vtsgsi_(const int&, const int&, double*, const int&, double*, const int&,\n             double*, const int&, gsl::not_null<int*>);\nvoid vtsgs_(const int&, const int&, const int&, const int&, const int&,\n            const int&, const int&, const int&, double*, double*, const int&,\n            const int&, const double*, const double*, const double*,\n            const double*, const int&, const int&, const double*, const int&,\n            double*, const int&, gsl::not_null<int*>);\nvoid alfk_(const int&, const int&, double*);\nvoid lfin_(const int&, const double*, const int&, const int&, const int&,\n           double*, const int&, double*);\nvoid gaqd_(const int&, double*, double*, double*, const int&,\n           gsl::not_null<int*>);\n}\n"
  },
  {
    "path": "src/Utilities/SplitTuple.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace split_tuple_detail {\ntemplate <size_t Offset, size_t... Indices, typename... TupleTypes>\nconstexpr std::tuple<\n    std::tuple_element_t<Offset + Indices, std::tuple<TupleTypes...>>...>\nextract_subset(\n    std::index_sequence<Indices...> /*meta*/,\n    [[maybe_unused]] const gsl::not_null<std::tuple<TupleTypes...>*> tuple) {\n  return {std::forward<\n      std::tuple_element_t<Offset + Indices, std::tuple<TupleTypes...>>>(\n      std::get<Offset + Indices>(*tuple))...};\n}\n\ntemplate <typename... Offsets, typename... Sizes, typename... TupleTypes>\nconstexpr auto impl(tmpl::list<Offsets...> /*meta*/,\n                    tmpl::list<Sizes...> /*meta*/,\n                    [[maybe_unused]] std::tuple<TupleTypes...> tuple) {\n  static_assert((0 + ... + Sizes::value) == sizeof...(TupleTypes),\n                \"Tuple size does not match output sizes.\");\n  return std::make_tuple(extract_subset<Offsets::value>(\n      std::make_index_sequence<Sizes::value>{}, make_not_null(&tuple))...);\n}\n}  // namespace split_tuple_detail\n\n/// \\ingroup UtilitiesGroup\n/// Split a `std::tuple` into multiple tuples\n///\n/// \\note There are two functions with this name, but doxygen can't\n/// figure that out.  They have signatures\n/// ```\n/// template <typename SizeList, typename... TupleTypes>\n/// constexpr auto split_tuple(std::tuple<TupleTypes...> tuple);\n/// template <size_t... Sizes, typename... TupleTypes>\n/// constexpr auto split_tuple(std::tuple<TupleTypes...> tuple);\n/// ```\n///\n/// Given a list of sizes, either directly as template parameters or\n/// as a typelist of integral constant types, split the passed tuple\n/// into pieces containing the specified number of entries.  The\n/// passed sizes must sum to the size of the tuple.\n///\n/// \\returns a `std::tuple` of `std::tuple`s\n///\n/// \\see std::tuple_cat for the inverse operation.\n///\n/// \\snippet Utilities/Test_SplitTuple.cpp split_tuple\n/// @{\ntemplate <typename SizeList, typename... TupleTypes>\nconstexpr auto split_tuple(std::tuple<TupleTypes...> tuple) {\n  using offsets = tmpl::pop_back<\n      tmpl::fold<SizeList, tmpl::list<tmpl::size_t<0>>,\n                 tmpl::bind<tmpl::push_back, tmpl::_state,\n                            tmpl::plus<tmpl::bind<tmpl::back, tmpl::_state>,\n                                       tmpl::_element>>>>;\n  return split_tuple_detail::impl(offsets{}, SizeList{}, std::move(tuple));\n}\n\ntemplate <size_t... Sizes, typename... TupleTypes>\nconstexpr auto split_tuple(std::tuple<TupleTypes...> tuple) {\n  return split_tuple<tmpl::list<tmpl::size_t<Sizes>...>>(std::move(tuple));\n}\n/// @}\n"
  },
  {
    "path": "src/Utilities/StaticCache.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsInteger.hpp\"\n\n/// \\ingroup UtilitiesGroup\n/// Range of integral values for StaticCache indices.  The `Start` is inclusive\n/// and the `End` is exclusive.  The range must not be empty.\ntemplate <auto Start, auto End>\nstruct CacheRange {\n  static_assert(std::is_same_v<decltype(Start), decltype(End)>);\n  static_assert(Start < End, \"CacheRange must include at least one value\");\n  constexpr static auto start = Start;\n  constexpr static auto end = End;\n  constexpr static auto size = end - start;\n  using value_type = std::remove_cv_t<decltype(start)>;\n};\n\n/// \\ingroup UtilitiesGroup\n/// Possible enumeration values for the StaticCache. Only values specified here\n/// are retrievable.\n///\n/// \\note The `EnumerationType` must be streamable.\ntemplate <typename EnumerationType, EnumerationType... Enums>\nstruct CacheEnumeration {\n  constexpr static size_t size = sizeof...(Enums);\n  using value_type = EnumerationType;\n  static constexpr std::array<value_type, size> values{Enums...};\n  using value_list = tmpl::integral_list<EnumerationType, Enums...>;\n};\n\n/// \\ingroup UtilitiesGroup\n/// A cache of objects intended to be stored in a static variable.\n///\n/// Objects can be accessed via a combination of several `size_t` and `enum`\n/// arguments. The range of each integral argument is specified via a template\n/// parameter of type `CacheRange<start, end>`, giving the first and\n/// one-past-last values for the range. Each `enum` argument is specified by a\n/// template parameter of type `CacheEnumeration<EnumerationType, Members...>`\n/// giving the enumeration type and an explicit set of every enum member to be\n/// cached.\n///\n/// \\example\n/// A cache with only numeric indices:\n/// \\snippet Test_StaticCache.cpp static_cache\n///\n/// \\example\n/// A cache with enumeration indices:\n/// \\snippet Test_StaticCache.cpp static_cache_with_enum\n///\n/// \\example\n/// A cache with mixed numeric and enumeration indices:\n/// \\snippet Test_StaticCache.cpp static_cache_with_enum_and_numeric\n///\n/// \\example\n/// A cache with no arguments at all (caching only a single object)\n/// \\snippet Test_StaticCache.cpp static_cache_no_args\n///\n/// \\see make_static_cache\n///\n/// \\tparam T type held in the cache\n/// \\tparam Ranges ranges of valid indices\ntemplate <typename Generator, typename T, typename... Ranges>\nclass StaticCache {\n public:\n  template <typename Gen>\n  // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)\n  explicit StaticCache(Gen&& generator)\n      : generator_{std::forward<Gen>(generator)} {}\n\n  template <typename... Args>\n  const T& operator()(const Args... parameters) const {\n    static_assert(sizeof...(parameters) == sizeof...(Ranges),\n                  \"Number of arguments must match number of ranges.\");\n    return unwrap_cache_combined(generate_tuple<Ranges>(parameters)...);\n  }\n\n private:\n  template <typename Range, typename T1>\n  auto generate_tuple(const T1 parameter) const {\n    if constexpr (std::is_enum<T1>::value) {\n      static_assert(\n          std::is_same<typename Range::value_type, std::remove_cv_t<T1>>::value,\n          \"Mismatched enum parameter type and cached type.\");\n      size_t array_location = std::numeric_limits<size_t>::max();\n      static const std::array<typename Range::value_type, Range::size> values{\n          Range::values};\n      for (size_t i = 0; i < Range::size; ++i) {\n        if (parameter == gsl::at(values, i)) {\n          array_location = i;\n          break;\n        }\n      }\n      if (UNLIKELY(array_location == std::numeric_limits<size_t>::max())) {\n        ERROR(\"Uncached enumeration value: \" << parameter);\n      }\n      return std::tuple{array_location, typename Range::value_list{}};\n    } else {\n      static_assert(\n          tt::is_integer_v<std::remove_cv_t<T1>>,\n          \"The parameter passed for a CacheRange must be an integer type.\");\n\n      // Check range here because the nested range checks in the unwrap_cache\n      // function cause significant compile time overhead.\n      if (UNLIKELY(Range::start >\n                       static_cast<decltype(Range::start)>(parameter) or\n                   static_cast<decltype(Range::start)>(parameter) >=\n                       Range::start +\n                           static_cast<decltype(Range::start)>(Range::size))) {\n        ERROR(\"Index out of range: \"\n              << Range::start << \" <= \" << parameter << \" < \"\n              << Range::start +\n                     static_cast<decltype(Range::start)>(Range::size));\n      }\n      return std::tuple{\n          // unsigned cast is safe since this is an index into an array\n          static_cast<size_t>(\n              static_cast<typename Range::value_type>(parameter) -\n              Range::start),\n          tmpl::make_sequence<\n              tmpl::integral_constant<typename Range::value_type, Range::start>,\n              Range::size>{}};\n    }\n  }\n\n  // Compilation time notes:\n  //\n  // - The separate peeling of different number of arguments is the\n  //   fastest implementation Nils Deppe has found so far.\n  // - The second fastest is using a Cartesian product on the lists of\n  //   possible values, followed by a for_each over that list to set the\n  //   function pointers in the array. Note that having the for_each function\n  //   be marked `constexpr` resulted in a 6x reduction in compilation time\n  //   for clang 17 compared to the not constexpr version, but still 40%\n  //   slower compilation compared to the pattern matching below.\n  template <typename... IntegralConstantValues>\n  const T& unwrap_cache_combined() const {\n    static const T cached_object = generator_(IntegralConstantValues::value...);\n    return cached_object;\n  }\n\n  // NVCC v12.6 can't handle the unwrapping code below, so we use a simpler\n  // version for CUDA. Note that we don't actually need to use StaticCache on\n  // device at all, so the main issue with NVCC is that it tries to compile this\n  // code for CUDA at all.\n#if defined(__NVCC__) && defined(__CUDA_ARCH__)\n  template <typename... IntegralConstantValues, typename... IntegralConstants,\n            typename... Args>\n  const T& unwrap_cache_combined(\n      std::tuple<size_t, tmpl::list<IntegralConstants...>> parameter0,\n      Args... parameters) const {\n    // note that the act of assigning to the specified function pointer type\n    // fixes the template arguments that need to be inferred.\n    static const std::array<\n        const T& (StaticCache<Generator, T, Ranges...>::*)(Args...) const,\n        sizeof...(IntegralConstants)>\n        cache{{&StaticCache<Generator, T, Ranges...>::unwrap_cache_combined<\n            IntegralConstantValues..., IntegralConstants>...}};\n    // The array `cache` holds pointers to member functions, so we dereference\n    // the pointer and invoke it on `this`.\n    return (this->*gsl::at(cache, std::get<0>(parameter0)))(parameters...);\n  }\n#else  // defined(__NVCC__) && defined(__CUDA_ARCH__)\n  template <typename... IntegralConstantValues, typename... IntegralConstants>\n  const T& unwrap_cache_combined(\n      std::tuple<size_t, tmpl::list<IntegralConstants...>> parameter0) const {\n    // note that the act of assigning to the specified function pointer type\n    // fixes the template arguments that need to be inferred.\n    static const std::array<const T& (StaticCache::*)() const,\n                            sizeof...(IntegralConstants)>\n        cache{{&StaticCache::unwrap_cache_combined<IntegralConstantValues...,\n                                                   IntegralConstants>...}};\n    // The array `cache` holds pointers to member functions, so we dereference\n    // the pointer and invoke it on `this`.\n    return (this->*gsl::at(cache, std::get<0>(parameter0)))();\n  }\n\n  template <typename... IntegralConstantValues, typename... IntegralConstants0,\n            typename... IntegralConstants1>\n  const T& unwrap_cache_combined(\n      std::tuple<size_t, tmpl::list<IntegralConstants0...>> parameter0,\n      std::tuple<size_t, tmpl::list<IntegralConstants1...>> parameter1) const {\n    constexpr size_t num0 = sizeof...(IntegralConstants0);\n    constexpr size_t num1 = sizeof...(IntegralConstants1);\n    constexpr size_t total_size = num0 * num1;\n    // note that the act of assigning to the specified function pointer type\n    // fixes the template arguments that need to be inferred.\n    static const std::array<const T& (StaticCache::*)() const, total_size>\n        cache = []() {\n          std::array<const T& (StaticCache::*)() const, total_size> result;\n          size_t counter1 = 0;\n          const auto helper1 = [&counter1,\n                                &result]<typename IntegralConstant1>() {\n            size_t counter0 = 0;\n            const auto helper0 = [&counter0, &counter1,\n                                  &result]<typename IntegralConstant0>() {\n              result[counter0 + num0 * counter1] =\n                  &StaticCache::unwrap_cache_combined<IntegralConstantValues...,\n                                                      IntegralConstant0,\n                                                      IntegralConstant1>;\n              ++counter0;\n            };\n            EXPAND_PACK_LEFT_TO_RIGHT(\n                helper0.template operator()<IntegralConstants0>());\n            ++counter1;\n          };\n          EXPAND_PACK_LEFT_TO_RIGHT(\n              helper1.template operator()<IntegralConstants1>());\n          return result;\n        }();\n\n    // The array `cache` holds pointers to member functions, so we dereference\n    // the pointer and invoke it on `this`.\n    return (this->*gsl::at(cache, std::get<0>(parameter0) +\n                                      num0 * std::get<0>(parameter1)))();\n  }\n\n  template <typename... IntegralConstantValues, typename... IntegralConstants0,\n            typename... IntegralConstants1, typename... IntegralConstants2>\n  const T& unwrap_cache_combined(\n      std::tuple<size_t, tmpl::list<IntegralConstants0...>> parameter0,\n      std::tuple<size_t, tmpl::list<IntegralConstants1...>> parameter1,\n      std::tuple<size_t, tmpl::list<IntegralConstants2...>> parameter2) const {\n    constexpr size_t num0 = sizeof...(IntegralConstants0);\n    constexpr size_t num1 = sizeof...(IntegralConstants1);\n    constexpr size_t num2 = sizeof...(IntegralConstants2);\n    constexpr size_t total_size = num0 * num1 * num2;\n    // note that the act of assigning to the specified function pointer type\n    // fixes the template arguments that need to be inferred.\n    static const std::array<const T& (StaticCache::*)() const, total_size>\n        cache = []() {\n          std::array<const T& (StaticCache::*)() const, total_size> result;\n          size_t counter2 = 0;\n          const auto helper2 = [&counter2,\n                                &result]<typename IntegralConstant2>() {\n            size_t counter1 = 0;\n            const auto helper1 = [&counter1, &counter2,\n                                  &result]<typename IntegralConstant1>() {\n              size_t counter0 = 0;\n              const auto helper0 = [&counter0, &counter1, &counter2,\n                                    &result]<typename IntegralConstant0>() {\n                result[counter0 + num0 * (counter1 + num1 * counter2)] =\n                    &StaticCache::unwrap_cache_combined<\n                        IntegralConstantValues..., IntegralConstant0,\n                        IntegralConstant1, IntegralConstant2>;\n                ++counter0;\n              };\n              EXPAND_PACK_LEFT_TO_RIGHT(\n                  helper0.template operator()<IntegralConstants0>());\n              ++counter1;\n            };\n            EXPAND_PACK_LEFT_TO_RIGHT(\n                helper1.template operator()<IntegralConstants1>());\n            ++counter2;\n          };\n          EXPAND_PACK_LEFT_TO_RIGHT(\n              helper2.template operator()<IntegralConstants2>());\n          return result;\n        }();\n\n    // The array `cache` holds pointers to member functions, so we dereference\n    // the pointer and invoke it on `this`.\n    return (\n        this->*gsl::at(cache, std::get<0>(parameter0) +\n                                  num0 * (std::get<0>(parameter1) +\n                                          num1 * std::get<0>(parameter2))))();\n  }\n\n  template <typename... IntegralConstantValues, typename... IntegralConstants0,\n            typename... IntegralConstants1, typename... IntegralConstants2,\n            typename... IntegralConstants3>\n  const T& unwrap_cache_combined(\n      std::tuple<size_t, tmpl::list<IntegralConstants0...>> parameter0,\n      std::tuple<size_t, tmpl::list<IntegralConstants1...>> parameter1,\n      std::tuple<size_t, tmpl::list<IntegralConstants2...>> parameter2,\n      std::tuple<size_t, tmpl::list<IntegralConstants3...>> parameter3) const {\n    constexpr size_t num0 = sizeof...(IntegralConstants0);\n    constexpr size_t num1 = sizeof...(IntegralConstants1);\n    constexpr size_t num2 = sizeof...(IntegralConstants2);\n    constexpr size_t num3 = sizeof...(IntegralConstants3);\n    constexpr size_t total_size = num0 * num1 * num2 * num3;\n    // note that the act of assigning to the specified function pointer type\n    // fixes the template arguments that need to be inferred.\n    static const std::array<const T& (StaticCache::*)() const, total_size>\n        cache = []() {\n          std::array<const T& (StaticCache::*)() const, total_size> result;\n          size_t counter3 = 0;\n          const auto helper3 = [&counter3,\n                                &result]<typename IntegralConstant3>() {\n            size_t counter2 = 0;\n            const auto helper2 = [&counter2, &counter3,\n                                  &result]<typename IntegralConstant2>() {\n              size_t counter1 = 0;\n              const auto helper1 = [&counter1, &counter2, &counter3,\n                                    &result]<typename IntegralConstant1>() {\n                size_t counter0 = 0;\n                const auto helper0 = [&counter0, &counter1, &counter2,\n                                      &counter3,\n                                      &result]<typename IntegralConstant0>() {\n                  result[counter0 +\n                         num0 *\n                             (counter1 + num1 * (counter2 + num2 * counter3))] =\n                      &StaticCache::unwrap_cache_combined<\n                          IntegralConstantValues..., IntegralConstant0,\n                          IntegralConstant1, IntegralConstant2,\n                          IntegralConstant3>;\n                  ++counter0;\n                };\n                EXPAND_PACK_LEFT_TO_RIGHT(\n                    helper0.template operator()<IntegralConstants0>());\n                ++counter1;\n              };\n              EXPAND_PACK_LEFT_TO_RIGHT(\n                  helper1.template operator()<IntegralConstants1>());\n              ++counter2;\n            };\n            EXPAND_PACK_LEFT_TO_RIGHT(\n                helper2.template operator()<IntegralConstants2>());\n            ++counter3;\n          };\n          EXPAND_PACK_LEFT_TO_RIGHT(\n              helper3.template operator()<IntegralConstants3>());\n          return result;\n        }();\n\n    // The array `cache` holds pointers to member functions, so we\n    // dereference the pointer and invoke it on `this`.\n    return (\n        this->*gsl::at(cache,\n                       std::get<0>(parameter0) +\n                           num0 * (std::get<0>(parameter1) +\n                                   num1 * (std::get<0>(parameter2) +\n                                           num2 * std::get<0>(parameter3)))))();\n  }\n\n  template <typename... IntegralConstantValues, typename... IntegralConstants0,\n            typename... IntegralConstants1, typename... IntegralConstants2,\n            typename... IntegralConstants3, typename... IntegralConstants4,\n            typename... Args>\n  const T& unwrap_cache_combined(\n      std::tuple<size_t, tmpl::list<IntegralConstants0...>> parameter0,\n      std::tuple<size_t, tmpl::list<IntegralConstants1...>> parameter1,\n      std::tuple<size_t, tmpl::list<IntegralConstants2...>> parameter2,\n      std::tuple<size_t, tmpl::list<IntegralConstants3...>> parameter3,\n      std::tuple<size_t, tmpl::list<IntegralConstants4...>> parameter4,\n      const Args&... parameters) const {\n    constexpr size_t num0 = sizeof...(IntegralConstants0);\n    constexpr size_t num1 = sizeof...(IntegralConstants1);\n    constexpr size_t num2 = sizeof...(IntegralConstants2);\n    constexpr size_t num3 = sizeof...(IntegralConstants3);\n    constexpr size_t num4 = sizeof...(IntegralConstants4);\n    constexpr size_t total_size = num0 * num1 * num2 * num3 * num4;\n    // note that the act of assigning to the specified function pointer type\n    // fixes the template arguments that need to be inferred.\n    static const std::array<const T& (StaticCache::*)(Args...) const,\n                            total_size>\n        cache = []() {\n          std::array<const T& (StaticCache::*)(Args...) const, total_size>\n              result;\n          size_t counter4 = 0;\n          const auto helper4 = [&counter4,\n                                &result]<typename IntegralConstant4>() {\n            size_t counter3 = 0;\n            const auto helper3 = [&counter3, &counter4,\n                                  &result]<typename IntegralConstant3>() {\n              size_t counter2 = 0;\n              const auto helper2 = [&counter2, &counter3, &counter4,\n                                    &result]<typename IntegralConstant2>() {\n                size_t counter1 = 0;\n                const auto helper1 = [&counter1, &counter2, &counter3,\n                                      &counter4,\n                                      &result]<typename IntegralConstant1>() {\n                  size_t counter0 = 0;\n                  const auto helper0 = [&counter0, &counter1, &counter2,\n                                        &counter3, &counter4,\n                                        &result]<typename IntegralConstant0>() {\n                    result[counter0 +\n                           num0 *\n                               (counter1 +\n                                num1 * (counter2 +\n                                        num2 * (counter3 + num3 * counter4)))] =\n                        &StaticCache::unwrap_cache_combined<\n                            IntegralConstantValues..., IntegralConstant0,\n                            IntegralConstant1, IntegralConstant2,\n                            IntegralConstant3, IntegralConstant4>;\n                    ++counter0;\n                  };\n                  EXPAND_PACK_LEFT_TO_RIGHT(\n                      helper0.template operator()<IntegralConstants0>());\n                  ++counter1;\n                };\n                EXPAND_PACK_LEFT_TO_RIGHT(\n                    helper1.template operator()<IntegralConstants1>());\n                ++counter2;\n              };\n              EXPAND_PACK_LEFT_TO_RIGHT(\n                  helper2.template operator()<IntegralConstants2>());\n              ++counter3;\n            };\n            EXPAND_PACK_LEFT_TO_RIGHT(\n                helper3.template operator()<IntegralConstants3>());\n            ++counter4;\n          };\n          EXPAND_PACK_LEFT_TO_RIGHT(\n              helper4.template operator()<IntegralConstants4>());\n          return result;\n        }();\n\n    // The array `cache` holds pointers to member functions, so we dereference\n    // the pointer and invoke it on `this`.\n    return (\n        this->*gsl::at(\n                   cache,\n                   std::get<0>(parameter0) +\n                       num0 *\n                           (std::get<0>(parameter1) +\n                            num1 * (std::get<0>(parameter2) +\n                                    num2 * (std::get<0>(parameter3) +\n                                            num3 * std::get<0>(parameter4))))))(\n        parameters...);\n  }\n#endif  // defined(__NVCC__) && defined(__CUDA_ARCH__)\n\n  const Generator generator_;\n};\n\n/// \\ingroup UtilitiesGroup\n/// Create a StaticCache, inferring the cached type from the generator.\ntemplate <typename... Ranges, typename Generator>\nauto make_static_cache(Generator&& generator) {\n  using CachedType = std::remove_cvref_t<decltype(generator(\n      std::declval<typename Ranges::value_type>()...))>;\n  return StaticCache<std::remove_cvref_t<Generator>, CachedType, Ranges...>(\n      std::forward<Generator>(generator));\n}\n"
  },
  {
    "path": "src/Utilities/StdArrayHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines arithmetic operators for std::array and other helpful functions.\n\n#pragma once\n\n#include <array>\n#include <cassert>\n#include <cmath>\n#include <cstddef>\n#include <type_traits>\n#include <utility>\n\n#include \"Utilities/Gsl.hpp\"\n\n// Arithmetic operators for std::array<T, Dim>\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr std::array<T, Dim>& operator+=(std::array<T, Dim>& lhs,\n                                                const std::array<U, Dim>& rhs) {\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(lhs, i) += gsl::at(rhs, i);\n  }\n  return lhs;\n}\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr auto operator+(const std::array<T, Dim>& lhs,\n                                const std::array<U, Dim>& rhs)\n    -> std::array<decltype(lhs[0] + rhs[0]), Dim> {\n  std::array<decltype(lhs[0] + rhs[0]), Dim> result{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(result, i) = gsl::at(lhs, i) + gsl::at(rhs, i);\n  }\n  return result;\n}\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr std::array<T, Dim>& operator-=(std::array<T, Dim>& lhs,\n                                                const std::array<U, Dim>& rhs) {\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(lhs, i) -= gsl::at(rhs, i);\n  }\n  return lhs;\n}\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr auto operator-(const std::array<T, Dim>& lhs,\n                                const std::array<U, Dim>& rhs)\n    -> std::array<decltype(lhs[0] - rhs[0]), Dim> {\n  std::array<decltype(lhs[0] - rhs[0]), Dim> result{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(result, i) = gsl::at(lhs, i) - gsl::at(rhs, i);\n  }\n  return result;\n}\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr std::array<T, Dim>& operator*=(std::array<T, Dim>& lhs,\n                                                const U& scale) {\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(lhs, i) *= scale;\n  }\n  return lhs;\n}\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr std::array<T, Dim> operator*(const std::array<T, Dim>& lhs,\n                                              const U& scale) {\n  std::array<T, Dim> result = lhs;\n  result *= scale;\n  return result;\n}\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr std::array<T, Dim> operator*(const U& scale,\n                                              const std::array<T, Dim>& rhs) {\n  return rhs * scale;\n}\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr std::array<T, Dim>& operator/=(std::array<T, Dim>& lhs,\n                                                const U& scale) {\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(lhs, i) /= scale;\n  }\n  return lhs;\n}\n\ntemplate <size_t Dim, typename T, typename U>\ninline constexpr std::array<T, Dim> operator/(const std::array<T, Dim>& lhs,\n                                              const U& scale) {\n  std::array<T, Dim> result = lhs;\n  result /= scale;\n  return result;\n}\n\ntemplate <size_t Dim, typename T>\ninline constexpr std::array<T, Dim> operator-(const std::array<T, Dim>& rhs) {\n  std::array<T, Dim> result{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(result, i) = -gsl::at(rhs, i);\n  }\n  return result;\n}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Construct an array from an existing array omitting one element\ntemplate <typename T, size_t Dim>\ninline constexpr std::array<T, Dim - 1> all_but_specified_element_of(\n    const std::array<T, Dim>& a, const size_t element_to_remove) {\n  assert(element_to_remove < Dim);\n  std::array<T, Dim - 1> result{};\n  for (size_t i = 0; i < element_to_remove; ++i) {\n    gsl::at(result, i) = gsl::at(a, i);\n  }\n  for (size_t i = element_to_remove + 1; i < Dim; ++i) {\n    gsl::at(result, i - 1) = gsl::at(a, i);\n  }\n  return result;\n}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Construct an array from an existing array adding one element\ntemplate <typename T, size_t Dim>\ninline constexpr std::array<T, Dim + 1> insert_element(\n    std::array<T, Dim> a, const size_t element_to_add, T value) {\n  assert(element_to_add <= Dim);\n  std::array<T, Dim + 1> result{};\n  for (size_t i = 0; i < element_to_add; ++i) {\n    gsl::at(result, i) = std::move(gsl::at(a, i));\n  }\n  gsl::at(result, element_to_add) = std::move(value);\n  for (size_t i = element_to_add; i < Dim; ++i) {\n    gsl::at(result, i + 1) = std::move(gsl::at(a, i));\n  }\n  return result;\n}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Construct an array from an existing array prepending a value\ntemplate <typename T, size_t Dim>\ninline constexpr std::array<T, Dim + 1> prepend(const std::array<T, Dim>& a,\n                                                T value) {\n  std::array<T, Dim + 1> result{};\n  gsl::at(result, 0) = std::move(value);\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(result, i + 1) = gsl::at(a, i);\n  }\n  return result;\n}\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Construct an array from the contents of two other arrays.\ntemplate <typename T, size_t Dim1, size_t Dim2>\nconstexpr std::array<T, Dim1 + Dim2> concatenate(std::array<T, Dim1> a,\n                                                 std::array<T, Dim2> b) {\n  std::array<T, Dim1 + Dim2> result{};\n  for (size_t i = 0; i < Dim1; ++i) {\n    gsl::at(result, i) = std::move(gsl::at(a, i));\n  }\n  for (size_t i = 0; i < Dim2; ++i) {\n    gsl::at(result, i + Dim1) = std::move(gsl::at(b, i));\n  }\n  return result;\n}\n\n/// @{\n/// \\ingroup UtilitiesGroup\n/// \\brief Euclidean magnitude of the elements of the array.\n///\n/// \\details If T is a container the magnitude is computed separately for each\n/// element of the container.\n///\n/// \\requires If T is a container, T must have following mathematical operators:\n/// abs(), sqrt(), and element-wise addition and multiplication.  In addition,\n/// each T in the array must have the same size.\ntemplate <typename T>\nconstexpr decltype(auto) magnitude(const std::array<T, 1>& a) {\n  using std::abs;\n  return abs(a[0]);\n}\n\ntemplate <typename T>\nconstexpr decltype(auto) magnitude(const std::array<T, 2>& a) {\n  return sqrt(a[0] * a[0] + a[1] * a[1]);\n}\n\ntemplate <typename T>\nconstexpr decltype(auto) magnitude(const std::array<T, 3>& a) {\n  return sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);\n}\n/// @}\n/// @{\n/// \\ingroup UtilitiesGroup\n/// \\brief Dot product between two arrays\n///\n/// \\details This also works elementwise if T is a container and R is a float or\n/// the other way round. The return type will always be the same as the return\n/// type of the multiplication which may be a blaze expression template.\n\ntemplate <typename T, typename R>\nconstexpr decltype(auto) dot(const std::array<T, 1>& first,\n                             const std::array<R, 1>& second) {\n  return first[0] * second[0];\n}\n\ntemplate <typename T, typename R>\nconstexpr decltype(auto) dot(const std::array<T, 2>& first,\n                             const std::array<R, 2>& second) {\n  return first[0] * second[0] + first[1] * second[1];\n}\n\ntemplate <typename T, typename R>\nconstexpr decltype(auto) dot(const std::array<T, 3>& first,\n                             const std::array<R, 3>& second) {\n  return first[0] * second[0] + first[1] * second[1] + first[2] * second[2];\n}\n/// @}\n\nnamespace std_array_helpers_detail {\ntemplate <typename T, size_t Dim, typename F, size_t... Indices>\nauto map_array_impl(const std::array<T, Dim>& array, const F& f,\n                    const std::index_sequence<Indices...> /*meta*/) {\n  return std::array<std::decay_t<decltype(f(std::declval<T>()))>, Dim>{\n      {f(array[Indices])...}};\n}\n}  // namespace std_array_helpers_detail\n\n/// \\ingroup UtilitiesGroup\n/// Applies a function to each element of an array, producing a new\n/// array of the results.  The elements of the new array are\n/// constructed in place, so they need not be default constructible.\ntemplate <typename T, size_t Dim, typename F>\nauto map_array(const std::array<T, Dim>& array, const F& f) {\n  return std_array_helpers_detail::map_array_impl(\n      array, f, std::make_index_sequence<Dim>{});\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Declares a binary function on an array, intended for binary\n * operators such as `+`\n *\n * \\param RESULT_TYPE the `value_type` that is the result of the operation\n * (e.g. `A` for resulting `std::array<A,2>`)\n *\n * \\param LTYPE the `value_type` of the first argument of the function (so the\n * left value if the function is an operator overload)\n *\n * \\param RTYPE the `value_type` of the second argument of the function\n *\n * \\param OP_FUNCTION_NAME the function which should be declared\n * (e.g. `operator+`)\n *\n * \\param BINARY_OP the binary function which should be applied elementwise to\n * the pair of arrays. (e.g. `std::plus<>()`)\n */\n// clang-tidy: complaints about uninitialized member, but the transform will set\n// data\n#define DEFINE_STD_ARRAY_BINOP(RESULT_TYPE, LTYPE, RTYPE, OP_FUNCTION_NAME,   \\\n                               BINARY_OP)                                     \\\n  template <size_t Dim>                                                       \\\n  std::array<RESULT_TYPE, Dim> OP_FUNCTION_NAME(                              \\\n      const std::array<LTYPE, Dim>& lhs, const std::array<RTYPE, Dim>& rhs) { \\\n    std::array<RESULT_TYPE, Dim> result; /*NOLINT*/                           \\\n    std::transform(lhs.begin(), lhs.end(), rhs.begin(), result.begin(),       \\\n                   BINARY_OP);                                                \\\n    return result;                                                            \\\n  }\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Declares an in-place binary function on an array, intended for\n * operations such as `+=`\n *\n * \\param LTYPE the `value_type` of the first argument of the function which is\n * also the result `value_tye` of the operation (so the left value if the\n * function is an operator overload)\n *\n * \\param RTYPE the `value_type` of the second argument of the function\n *\n * \\param OP_FUNCTION_NAME the function which should be declared\n * (e.g. `operator+=`)\n *\n * \\param BINARY_OP the binary function which should be applied elementwise to\n * the pair of arrays. (e.g. `std::plus<>()`)\n */\n#define DEFINE_STD_ARRAY_INPLACE_BINOP(LTYPE, RTYPE, OP_FUNCTION_NAME,  \\\n                                       BINARY_OP)                       \\\n  template <size_t Dim>                                                 \\\n  std::array<LTYPE, Dim>& OP_FUNCTION_NAME(                             \\\n      std::array<LTYPE, Dim>& lhs, const std::array<RTYPE, Dim>& rhs) { \\\n    std::transform(lhs.begin(), lhs.end(), rhs.begin(), lhs.begin(),    \\\n                   BINARY_OP);                                          \\\n    return lhs;                                                         \\\n  }\n"
  },
  {
    "path": "src/Utilities/StdHelpers/Bit.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <bit>\n\n#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L\n// std::bit_floor and std::has_single_bit are defined in <bit>\n#else\n// std::bit_floor and std::has_single_bit are not defined in <bit> and\n// therefore we provide the definitions\n#include <cstdint>\n#include <limits>\n\n#include \"Utilities/Requires.hpp\"\n\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nnamespace std {\ntemplate <typename T,\n          Requires<std::is_same_v<T, uint8_t> or std::is_same_v<T, uint16_t> or\n                   std::is_same_v<T, uint32_t> or std::is_same_v<T, uint64_t> or\n                   std::is_same_v<T, size_t> > = nullptr>\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nconstexpr T bit_floor(T x) noexcept {\n  if (x != 0) {\n    return T(1) << (std::numeric_limits<T>::digits - std::countl_zero(x) - 1);\n  }\n  return 0;\n}\n\ntemplate <typename T,\n          Requires<std::is_same_v<T, uint8_t> or std::is_same_v<T, uint16_t> or\n                   std::is_same_v<T, uint32_t> or std::is_same_v<T, uint64_t> or\n                   std::is_same_v<T, size_t> > = nullptr>\n// NOLINTNEXTLINE(cert-dcl58-cpp)\nconstexpr bool has_single_bit(T x) noexcept {\n  return std::popcount(x) == 1;\n}\n}  // namespace std\n#endif\n"
  },
  {
    "path": "src/Utilities/StdHelpers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY StdHelpers)\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Bit.hpp\n  RetrieveUniquePtr.hpp\n  )\n"
  },
  {
    "path": "src/Utilities/StdHelpers/RetrieveUniquePtr.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n\nnamespace StdHelpers {\n/// @{\n/// \\brief Dereference a `std::unique_ptr` or just get the value back.\ntemplate <typename T, typename Deleter>\nconst T& retrieve(const std::unique_ptr<T, Deleter>& t) {\n  return *t;\n}\n\ntemplate <typename T>\nconst T& retrieve(const T& t) {\n  return t;\n}\n\ntemplate <typename T, typename Deleter>\nT& retrieve(std::unique_ptr<T, Deleter>& t) {\n  return *t;\n}\n\ntemplate <typename T>\nT& retrieve(T& t) {\n  return t;\n}\n/// @}\n}  // namespace StdHelpers\n"
  },
  {
    "path": "src/Utilities/StdHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines helper functions for the standard library.\n\n#pragma once\n\n#include <array>\n#include <chrono>\n#include <cstdio>\n#include <ctime>\n#include <deque>\n#include <list>\n#include <map>\n#include <memory>\n#include <optional>\n#include <set>\n#include <sstream>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Utilities/PrintHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/StlStreamDeclarations.hpp\"\n#include \"Utilities/TypeTraits/IsStreamable.hpp\"\n\nnamespace StdHelpers_detail {\n\n// Helper classes for operator<< for tuples\ntemplate <size_t N>\nstruct TuplePrinter {\n  template <typename... Args>\n  static std::ostream& print(std::ostream& os, const std::tuple<Args...>& t) {\n    TuplePrinter<N - 1>::print(os, t);\n    os << \",\";\n    print_value(os, std::get<N - 1>(t));\n    return os;\n  }\n};\n\ntemplate <>\nstruct TuplePrinter<1> {\n  template <typename... Args>\n  static std::ostream& print(std::ostream& os, const std::tuple<Args...>& t) {\n    print_value(os, std::get<0>(t));\n    return os;\n  }\n};\n\ntemplate <>\nstruct TuplePrinter<0> {\n  template <typename... Args>\n  static std::ostream& print(std::ostream& os,\n                             const std::tuple<Args...>& /*t*/) {\n    return os;\n  }\n};\n}  // namespace StdHelpers_detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output the items of a std::list\n */\ntemplate <typename T>\ninline std::ostream& operator<<(std::ostream& os, const std::list<T>& v) {\n  sequence_print_helper(os, std::begin(v), std::end(v));\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output the items of a std::vector\n */\ntemplate <typename T>\ninline std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {\n  sequence_print_helper(os, std::begin(v), std::end(v));\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output the items of a std::deque\n */\ntemplate <typename T>\ninline std::ostream& operator<<(std::ostream& os, const std::deque<T>& v) {\n  sequence_print_helper(os, std::begin(v), std::end(v));\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output the items of a std::array\n */\ntemplate <typename T, size_t N>\ninline std::ostream& operator<<(std::ostream& os, const std::array<T, N>& a) {\n  sequence_print_helper(os, begin(a), end(a));\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Stream operator for tuples\n */\ntemplate <typename... Args>\ninline std::ostream& operator<<(std::ostream& os,\n                                const std::tuple<Args...>& t) {\n  os << \"(\";\n  StdHelpers_detail::TuplePrinter<sizeof...(Args)>::print(os, t);\n  os << \")\";\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output all the key, value pairs of a std::unordered_map\n */\ntemplate <typename K, typename V, typename H>\ninline std::ostream& operator<<(std::ostream& os,\n                                const std::unordered_map<K, V, H>& m) {\n  unordered_print_helper(\n      os, begin(m), end(m),\n      [](std::ostream& out,\n         const typename std::unordered_map<K, V, H>::value_type& value) {\n        out << \"[\";\n        print_value(out, value.first);\n        out << \",\";\n        print_value(out, value.second);\n        out << \"]\";\n      });\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output all the key, value pairs of a std::map\n */\ntemplate <typename K, typename V, typename C>\ninline std::ostream& operator<<(std::ostream& os, const std::map<K, V, C>& m) {\n  sequence_print_helper(\n      os, begin(m), end(m),\n      [](std::ostream& out,\n         const typename std::map<K, V, C>::value_type& value) {\n        out << \"[\";\n        print_value(out, value.first);\n        out << \",\";\n        print_value(out, value.second);\n        out << \"]\";\n      });\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output the items of a std::unordered_set\n */\ntemplate <typename T, typename H>\ninline std::ostream& operator<<(std::ostream& os,\n                                const std::unordered_set<T, H>& v) {\n  unordered_print_helper(os, std::begin(v), std::end(v));\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output the items of a std::unordered_multiset\n */\ntemplate <typename T, typename H>\ninline std::ostream& operator<<(std::ostream& os,\n                                const std::unordered_multiset<T, H>& v) {\n  unordered_print_helper(os, std::begin(v), std::end(v));\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Output the items of a std::set\n */\ntemplate <typename T, typename C>\ninline std::ostream& operator<<(std::ostream& os, const std::set<T, C>& v) {\n  sequence_print_helper(os, std::begin(v), std::end(v));\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Stream operator for std::unique_ptr\n */\ntemplate <typename T, Requires<tt::is_streamable<std::ostream, T>::value>>\ninline std::ostream& operator<<(std::ostream& os, const std::unique_ptr<T>& t) {\n  return os << *t;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Stream operator for std::shared_ptr\n */\ntemplate <typename T, Requires<tt::is_streamable<std::ostream, T>::value>>\ninline std::ostream& operator<<(std::ostream& os, const std::shared_ptr<T>& t) {\n  return os << *t;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Stream operator for std::pair\n */\ntemplate <typename T, typename U>\ninline std::ostream& operator<<(std::ostream& os, const std::pair<T, U>& t) {\n  os << \"(\";\n  print_value(os, t.first);\n  os << \", \";\n  print_value(os, t.second);\n  os << \")\";\n  return os;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Stream operator for std::optional\n */\ntemplate <typename T>\ninline std::ostream& operator<<(std::ostream& os, const std::optional<T>& t) {\n  // Match boost::optional behavior and print \"--\" when invalid\n  if (t.has_value()) {\n    print_value(os, t.value());\n    return os;\n  } else {\n    return os << \"--\";\n  }\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Equivalent to `os << t`.\n *\n * For some reason this sometimes works when trying to stream directly\n * doesn't find our STL container stream operators.\n */\ntemplate <typename T>\nvoid print_stl(std::ostream& os, const T& t) {\n  using ::operator<<;\n  os << t;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Construct a string containing the keys of a std::unordered_map\n */\ntemplate <typename K, typename V, typename H>\ninline std::string keys_of(const std::unordered_map<K, V, H>& m) {\n  std::ostringstream os;\n  unordered_print_helper(\n      os, begin(m), end(m),\n      [](std::ostream& out,\n         const typename std::unordered_map<K, V, H>::value_type& value) {\n        print_value(out, value.first);\n      });\n  return os.str();\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Construct a string containing the keys of a std::map\n */\ntemplate <typename K, typename V, typename C>\ninline std::string keys_of(const std::map<K, V, C>& m) {\n  std::ostringstream os;\n  sequence_print_helper(\n      os, begin(m), end(m),\n      [](std::ostream& out,\n         const typename std::map<K, V, C>::value_type& value) {\n        print_value(out, value.first);\n      });\n  return os.str();\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Format a string like printf\n *\n * Given a formatting string and arguments this returns the corresponding\n * string. Similar to printf but using std::strings.\n */\ntemplate <typename... Args>\nstd::string formatted_string(const std::string& fmt, Args... args) {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wformat-nonliteral\"\n  // clang-tidy: do not use snprintf\n  auto requiredBytes = static_cast<size_t>(std::snprintf(  // NOLINT\n                           nullptr, 0, fmt.c_str(), args...)) +\n                       1;\n  std::string rtn;\n  rtn.resize(requiredBytes);\n  // clang-tidy: do not use snprintf\n  std::snprintf(&rtn[0], requiredBytes, fmt.c_str(), args...);  // NOLINT\n#pragma GCC diagnostic pop\n  if (rtn[rtn.size() - 1] == '\\0') {\n    rtn.resize(rtn.size() - 1);\n  }\n  return rtn;\n}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Get the current date and time\n */\ninline std::string current_date_and_time() {\n  const auto now =\n      std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());\n  return std::ctime(&now);\n}\n"
  },
  {
    "path": "src/Utilities/StlBoilerplate.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <stdexcept>\n#include <type_traits>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\n/// Classes to generate repetitive code needed for STL compatibility.\nnamespace stl_boilerplate {\n/// Base class to generate methods for a random-access iterator.  This\n/// class takes the derived class and the (optionally `const`)\n/// `value_type` of the iterator.  The exposed `value_type` alias will\n/// always be non-const, as required by the standard, but the template\n/// parameter's constness will affect the `reference` and `pointer`\n/// aliases.\n///\n/// The derived class must implement `operator*`, `operator+=`,\n/// `operator-` (of two iterators), and `operator==`.\n///\n/// \\snippet Test_StlBoilerplate.cpp RandomAccessIterator\n///\n/// This class is inspired by Boost's `iterator_facade`, except that\n/// that has a lot of special cases, some of which work poorly because\n/// they predate C++11.\ntemplate <typename Iter, typename ValueType>\nclass RandomAccessIterator {\n protected:\n  RandomAccessIterator() = default;\n  RandomAccessIterator(const RandomAccessIterator&) = default;\n  RandomAccessIterator(RandomAccessIterator&&) = default;\n  RandomAccessIterator& operator=(const RandomAccessIterator&) = default;\n  RandomAccessIterator& operator=(RandomAccessIterator&&) = default;\n  ~RandomAccessIterator() = default;\n\n public:\n  using iterator_category = std::random_access_iterator_tag;\n  using value_type = std::remove_const_t<ValueType>;\n  using reference = ValueType&;\n  using pointer = ValueType*;\n  using difference_type = std::ptrdiff_t;\n\n  pointer operator->() const { return &**dthis(); }\n\n  Iter& operator++() { return *dthis() += 1; }\n  Iter operator++(int) {\n    const auto ret = *dthis();\n    ++*dthis();\n    return ret;\n  }\n  Iter& operator--() { return *dthis() += -1; }\n  Iter operator--(int) {\n    const auto ret = *dthis();\n    --*dthis();\n    return ret;\n  }\n\n  Iter& operator-=(const difference_type n) { return *dthis() += -n; }\n\n  reference operator[](const difference_type n) const {\n    auto temp = *dthis();\n    temp += n;\n    return *temp;\n  }\n\n private:\n  // derived this\n  Iter* dthis() { return static_cast<Iter*>(this); }\n  const Iter* dthis() const { return static_cast<const Iter*>(this); }\n};\n\ntemplate <typename Iter, typename ValueType>\nbool operator!=(const RandomAccessIterator<Iter, ValueType>& a,\n                const RandomAccessIterator<Iter, ValueType>& b) {\n  return not(static_cast<const Iter&>(a) == static_cast<const Iter&>(b));\n}\n\ntemplate <typename Iter, typename ValueType>\nbool operator<(const RandomAccessIterator<Iter, ValueType>& a,\n               const RandomAccessIterator<Iter, ValueType>& b) {\n  return static_cast<const Iter&>(b) - static_cast<const Iter&>(a) > 0;\n}\n\ntemplate <typename Iter, typename ValueType>\nbool operator>(const RandomAccessIterator<Iter, ValueType>& a,\n               const RandomAccessIterator<Iter, ValueType>& b) {\n  return b < a;\n}\n\ntemplate <typename Iter, typename ValueType>\nbool operator<=(const RandomAccessIterator<Iter, ValueType>& a,\n                const RandomAccessIterator<Iter, ValueType>& b) {\n  return not(a > b);\n}\n\ntemplate <typename Iter, typename ValueType>\nbool operator>=(const RandomAccessIterator<Iter, ValueType>& a,\n                const RandomAccessIterator<Iter, ValueType>& b) {\n  return not(a < b);\n}\n\ntemplate <typename Iter, typename ValueType>\nIter operator+(\n    const RandomAccessIterator<Iter, ValueType>& a,\n    const typename RandomAccessIterator<Iter, ValueType>::difference_type& n) {\n  auto result = static_cast<const Iter&>(a);\n  result += n;\n  return result;\n}\n\ntemplate <typename Iter, typename ValueType>\nIter operator+(\n    const typename RandomAccessIterator<Iter, ValueType>::difference_type& n,\n    const RandomAccessIterator<Iter, ValueType>& a) {\n  return a + n;\n}\n\ntemplate <typename Iter, typename ValueType>\nIter operator-(\n    const RandomAccessIterator<Iter, ValueType>& a,\n    const typename RandomAccessIterator<Iter, ValueType>::difference_type& n) {\n  auto result = static_cast<const Iter&>(a);\n  result -= n;\n  return result;\n}\n\n/// Base class to generate methods for a random-access sequence,\n/// similar to a `std::array`.\n///\n/// This class takes the derived class and the `value_type` of the\n/// sequence as template parameters.  The third template parameter\n/// specifies whether to define comparison operators.  Comparisons are\n/// required by the standard, but for derived classes with additional\n/// functionality the autogenerated ones can give incorrect results.\n///\n/// The derived class must implement `size` and `operator[]`.\n///\n/// \\snippet Test_StlBoilerplate.cpp RandomAccessSequence\n///\n/// This class provides methods for accessing and modifying elements\n/// of the sequence, such as `front` and `begin`, as well as iterators\n/// and reverse iterators.  Methods modifying the sequence itself,\n/// such as `insert`, require explicit implementations.\ntemplate <typename Sequence, typename ValueType, bool DefineComparisons>\nclass RandomAccessSequence {\n protected:\n  RandomAccessSequence() = default;\n  RandomAccessSequence(const RandomAccessSequence&) = default;\n  RandomAccessSequence(RandomAccessSequence&&) = default;\n  RandomAccessSequence& operator=(const RandomAccessSequence&) = default;\n  RandomAccessSequence& operator=(RandomAccessSequence&&) = default;\n  ~RandomAccessSequence() = default;\n\n public:\n  using value_type = ValueType;\n  using reference = value_type&;\n  using const_reference = const value_type&;\n  using pointer = ValueType*;\n  using const_pointer = const ValueType*;\n\n  class const_iterator\n      : public RandomAccessIterator<const_iterator, const value_type> {\n   public:\n    const_iterator() = default;\n\n    const value_type& operator*() const { return (*container_)[offset_]; }\n    const_iterator& operator+=(const std::ptrdiff_t n) {\n      offset_ += static_cast<size_t>(n);\n      return *this;\n    }\n\n   private:\n    friend bool operator==(const const_iterator& a, const const_iterator& b) {\n      return a.container_ == b.container_ and a.offset_ == b.offset_;\n    }\n\n    friend std::ptrdiff_t operator-(const const_iterator& a,\n                                    const const_iterator& b) {\n      ASSERT(a.container_ == b.container_, \"Subtracting unrelated iterators\");\n      return static_cast<std::ptrdiff_t>(a.offset_) -\n             static_cast<std::ptrdiff_t>(b.offset_);\n    }\n\n    friend RandomAccessSequence;\n    const_iterator(const gsl::not_null<const Sequence*> container,\n                   const size_t offset)\n        : container_(container), offset_(offset) {}\n\n    const Sequence* container_{nullptr};\n    size_t offset_{0};\n  };\n\n  class iterator : public RandomAccessIterator<iterator, value_type> {\n   public:\n    iterator() = default;\n\n    value_type& operator*() const { return (*container_)[offset_]; }\n    iterator& operator+=(const std::ptrdiff_t n) {\n      offset_ += static_cast<size_t>(n);\n      return *this;\n    }\n\n    operator const_iterator() const {\n      return container_->cbegin() + static_cast<std::ptrdiff_t>(offset_);\n    }\n\n   private:\n    friend bool operator==(const iterator& a, const iterator& b) {\n      return a.container_ == b.container_ and a.offset_ == b.offset_;\n    }\n\n    friend std::ptrdiff_t operator-(const iterator& a, const iterator& b) {\n      ASSERT(a.container_ == b.container_, \"Subtracting unrelated iterators\");\n      return static_cast<std::ptrdiff_t>(a.offset_) -\n             static_cast<std::ptrdiff_t>(b.offset_);\n    }\n\n    friend RandomAccessSequence;\n    iterator(const gsl::not_null<Sequence*> container, const size_t offset)\n        : container_(container), offset_(offset) {}\n\n    Sequence* container_{nullptr};\n    size_t offset_{0};\n  };\n\n  using reverse_iterator = std::reverse_iterator<iterator>;\n  using const_reverse_iterator = std::reverse_iterator<const_iterator>;\n  using difference_type =\n      typename std::iterator_traits<const_iterator>::difference_type;\n  using size_type = size_t;\n\n  iterator begin() & { return {dthis(), 0}; }\n  const_iterator begin() const& { return {dthis(), 0}; }\n  const_iterator cbegin() const& { return begin(); }\n  iterator end() & { return {dthis(), dthis()->size()}; }\n  const_iterator end() const& { return {dthis(), dthis()->size()}; }\n  const_iterator cend() const& { return end(); }\n\n  reverse_iterator rbegin() & { return reverse_iterator(end()); }\n  const_reverse_iterator rbegin() const& {\n    return const_reverse_iterator(end());\n  }\n  const_reverse_iterator crbegin() const& { return rbegin(); }\n  reverse_iterator rend() & { return reverse_iterator(begin()); }\n  const_reverse_iterator rend() const& {\n    return const_reverse_iterator(begin());\n  }\n  const_reverse_iterator crend() const& { return rend(); }\n\n  /// Not part of the standard, but any attempt to store the results\n  /// of a call that would match one of these would be undefined\n  /// behavior.  Valid uses would be: use as temporaries, but that can\n  /// usually be better done through the normal container access; and\n  /// use in unevaluated contexts, but those should really be\n  /// explicitly requesting lvalue references if that's what they're\n  /// checking.  Either of these can be easily worked around.  If\n  /// these things happen in code outside our control we may have to\n  /// reconsider.\n  /// @{\n  void begin() && = delete;\n  void begin() const&& = delete;\n  void cbegin() && = delete;\n  void cbegin() const&& = delete;\n  void end() && = delete;\n  void end() const&& = delete;\n  void cend() && = delete;\n  void cend() const&& = delete;\n  void rbegin() && = delete;\n  void rbegin() const&& = delete;\n  void crbegin() && = delete;\n  void crbegin() const&& = delete;\n  void rend() && = delete;\n  void rend() const&& = delete;\n  void crend() && = delete;\n  void crend() const&& = delete;\n  /// @}\n\n  size_type max_size() const { return std::numeric_limits<size_type>::max(); }\n  bool empty() const { return begin() == end(); }\n\n  reference front() { return *begin(); }\n  const_reference front() const { return *begin(); }\n  reference back() { return *(end() - 1); }\n  const_reference back() const { return *(end() - 1); }\n\n  reference at(const size_type n) {\n    if (n >= dthis()->size()) {\n      throw std::out_of_range(MakeString{} << \"RandomAccessSequence::at \" << n\n                                           << \" >= \" << dthis()->size());\n    }\n    return (*dthis())[n];\n  }\n  const_reference at(const size_type n) const {\n    if (n >= dthis()->size()) {\n      throw std::out_of_range(MakeString{} << \"RandomAccessSequence::at \" << n\n                                           << \" >= \" << dthis()->size());\n    }\n    return (*dthis())[n];\n  }\n\n private:\n  // derived this\n  Sequence* dthis() { return static_cast<Sequence*>(this); }\n  const Sequence* dthis() const { return static_cast<const Sequence*>(this); }\n};\n\ntemplate <typename Sequence, typename ValueType>\nbool operator==(const RandomAccessSequence<Sequence, ValueType, true>& a,\n                const RandomAccessSequence<Sequence, ValueType, true>& b) {\n  return static_cast<const Sequence&>(a).size() ==\n             static_cast<const Sequence&>(b).size() and\n         std::equal(a.begin(), a.end(), b.begin());\n}\n\ntemplate <typename Sequence, typename ValueType>\nbool operator!=(const RandomAccessSequence<Sequence, ValueType, true>& a,\n                const RandomAccessSequence<Sequence, ValueType, true>& b) {\n  return not(a == b);\n}\n\ntemplate <typename Sequence, typename ValueType>\nbool operator<(const RandomAccessSequence<Sequence, ValueType, true>& a,\n               const RandomAccessSequence<Sequence, ValueType, true>& b) {\n  return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end());\n}\n\ntemplate <typename Sequence, typename ValueType>\nbool operator>(const RandomAccessSequence<Sequence, ValueType, true>& a,\n               const RandomAccessSequence<Sequence, ValueType, true>& b) {\n  return b < a;\n}\n\ntemplate <typename Sequence, typename ValueType>\nbool operator<=(const RandomAccessSequence<Sequence, ValueType, true>& a,\n                const RandomAccessSequence<Sequence, ValueType, true>& b) {\n  return not(a > b);\n}\n\ntemplate <typename Sequence, typename ValueType>\nbool operator>=(const RandomAccessSequence<Sequence, ValueType, true>& a,\n                const RandomAccessSequence<Sequence, ValueType, true>& b) {\n  return not(a < b);\n}\n}  // namespace stl_boilerplate\n"
  },
  {
    "path": "src/Utilities/StlStreamDeclarations.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Declares stream operators for STL containers\n\n#pragma once\n\n// Suppress warnings from this header since they can occur from not including\n// StdHelpers later. This header is used only in TypeTraits.hpp so that\n// is_streamable works correctly for STL types that have stream operators\n// defined.\n#ifdef __GNUC__\n#pragma GCC system_header\n#endif\n\n#include <array>\n#include <deque>\n#include <list>\n#include <map>\n#include <memory>\n#include <optional>\n#include <set>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Utilities/Requires.hpp\"\n\nnamespace tt {\ntemplate <typename S, typename T, typename>\nstruct is_streamable;\n}  // namespace tt\n\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os, const std::list<T>& v);\n\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os, const std::vector<T>& v);\n\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os, const std::deque<T>& v);\n\ntemplate <typename T, size_t N>\nstd::ostream& operator<<(std::ostream& os, const std::array<T, N>& a);\n\ntemplate <typename... Args>\nstd::ostream& operator<<(std::ostream& os, const std::tuple<Args...>& t);\n\ntemplate <typename K, typename V, typename H>\ninline std::ostream& operator<<(std::ostream& os,\n                                const std::unordered_map<K, V, H>& m);\n\ntemplate <typename K, typename V, typename C>\ninline std::ostream& operator<<(std::ostream& os, const std::map<K, V, C>& m);\n\ntemplate <typename T, typename H>\nstd::ostream& operator<<(std::ostream& os, const std::unordered_set<T, H>& v);\n\ntemplate <typename T, typename H>\nstd::ostream& operator<<(std::ostream& os,\n                         const std::unordered_multiset<T, H>& v);\n\ntemplate <typename T, typename C>\ninline std::ostream& operator<<(std::ostream& os, const std::set<T, C>& v);\n\ntemplate <typename T,\n          Requires<tt::is_streamable<std::ostream, T, void>::value> = nullptr>\nstd::ostream& operator<<(std::ostream& os, const std::unique_ptr<T>& t);\n\ntemplate <typename T,\n          Requires<tt::is_streamable<std::ostream, T, void>::value> = nullptr>\nstd::ostream& operator<<(std::ostream& os, const std::shared_ptr<T>& t);\n\ntemplate <typename T, typename U>\nstd::ostream& operator<<(std::ostream& os, const std::pair<T, U>& t);\n\ntemplate <typename T>\nstd::ostream& operator<<(std::ostream& os, const std::optional<T>& t);\n"
  },
  {
    "path": "src/Utilities/System/Abort.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/System/Abort.hpp\"\n\n#include <charm++.h>\n#include <exception>\n#include <string>\n\nnamespace sys {\nvoid abort(const std::string& message) {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wformat-security\"\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg)\n  CkPrintf(\"%s\\n\", message.c_str());\n#pragma GCC diagnostic pop\n  CkExit(1);\n  // the following call is never reached, but suppresses the warning that\n  // a 'noreturn' function does return\n  std::terminate();  // LCOV_EXCL_LINE\n}\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/Abort.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\nnamespace sys {\n/// \\ingroup ErrorHandlingGroup\n/// Abort the program with an error message.\n///\n/// \\details This function calls CkExit with a non-zero argument to indicate a\n/// failure, unless the SPECTRE_TRAP_ON_ERROR environmental variable is set, in\n/// which case it raises SIGTRAP.\n[[noreturn]] void abort(const std::string& message);\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/AttachDebugger.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/System/AttachDebugger.hpp\"\n\n#include <chrono>\n#include <cstdlib>\n#include <iostream>\n#include <thread>\n#include <unistd.h>\n\nnamespace sys {\nvoid attach_debugger() {\n  const char* env_enable_parallel_debug =\n      // NOLINTNEXTLINE(concurrency-mt-unsafe)\n      std::getenv(\"SPECTRE_ATTACH_DEBUGGER\");\n  if (env_enable_parallel_debug != nullptr) {\n    // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n    char hostname[2048];\n    gethostname(static_cast<char*>(hostname), sizeof(hostname));\n\n    const std::string output_info =\n        std::string{\"pid:\"} + std::to_string(getpid()) + std::string{\":host:\"} +\n        std::string{static_cast<char*>(hostname)} + \"\\n\";\n    std::cout << output_info << std::flush;\n    // NOLINTNEXTLINE(misc-const-correctness)\n    volatile int i = 10;\n    while (i == 10) {\n      using namespace std::chrono_literals;\n      std::this_thread::sleep_for(std::chrono::seconds{i});\n    }\n  }\n}\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/AttachDebugger.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace sys {\n/*!\n * \\brief Provide an infinite loop to attach a debugger during startup. Useful\n * for debugging MPI runs.\n *\n * Each MPI rank writes a file name `spectre_pid_#_host_NAME` to the working\n * directory. This allows you to attach GDB to the running process using\n * `gdb --pid=PID`, once for each MPI rank. You must then halt the program\n * using `C-c` and then call `set var i = 7` inside GDB. Once you've done this\n * on each MPI rank, you can have each MPI rank `continue`.\n *\n * To add support for attaching to a debugger in an executable, you must add\n * `sys::attach_debugger` to the\n * `Parallel::charmxx::register_init_node_and_proc` init node functions. Then,\n * when you launch the executable launch it as\n * ```shell\n * SPECTRE_ATTACH_DEBUGGER=1 mpirun -np N ...\n * ```\n * The environment variable `SPECTRE_ATTACH_DEBUGGER` being set tells the code\n * to allow attaching from a debugger.\n */\nvoid attach_debugger();\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY SystemUtilities)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  Abort.cpp\n  AttachDebugger.cpp\n  Exit.cpp\n  ParallelInfo.cpp\n  Prefetch.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  Abort.hpp\n  AttachDebugger.hpp\n  Exit.hpp\n  ParallelInfo.hpp\n  Prefetch.hpp\n  System.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Charmxx::charmxx\n  )\n"
  },
  {
    "path": "src/Utilities/System/Exit.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/System/Exit.hpp\"\n\n#include <charm++.h>\n#include <exception>\n\nnamespace sys {\n\n[[noreturn]] void exit(const int exit_code) {\n  CkExit(exit_code);\n  // the following call is never reached, but suppresses the warning that\n  // a 'noreturn' function does return\n  std::terminate();  // LCOV_EXCL_LINE\n}\n\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/Exit.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\nnamespace sys {\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Exit the program normally.\n/// This should only be called once over all processors.\n[[noreturn]] void exit(int exit_code = 0);\n\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/ParallelInfo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <charm++.h>\n#include <iomanip>\n#include <sstream>\n\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace sys {\nint number_of_procs() { return CkNumPes(); }\n\nint my_proc() { return CkMyPe(); }\n\nint number_of_nodes() { return CkNumNodes(); }\n\nint my_node() { return CkMyNode(); }\n\nint procs_on_node([[maybe_unused]] const int node_index) {\n  return CkNodeSize(node_index);\n}\n\nint my_local_rank() { return CkMyRank(); }\n\nint first_proc_on_node([[maybe_unused]] const int node_index) {\n  return CkNodeFirst(node_index);\n}\n\nint node_of([[maybe_unused]] const int proc_index) {\n  return CkNodeOf(proc_index);\n}\n\nint local_rank_of([[maybe_unused]] const int proc_index) {\n  return CkRankOf(proc_index);\n}\n\ndouble wall_time() { return CkWallTimer(); }\n\nstd::string pretty_wall_time(const double total_seconds) {\n  // Subseconds don't really matter so just ignore them. This gives nice round\n  // numbers.\n  int total = static_cast<int>(total_seconds);\n  const int day = total / (24 * 3600);\n\n  total %= (24 * 3600);\n  const int hour = total / 3600;\n\n  total %= 3600;\n  const int minutes = total / 60;\n\n  total %= 60;\n  const int seconds = total;\n\n  std::stringstream ss{};\n  ss << std::setfill('0');\n  if (day > 0) {\n    ss << std::setw(2) << day << \"-\";\n  }\n\n  // std::setw() isn't sticky so it has to be used for every insertion\n  ss << std::setw(2) << hour << \":\";\n  ss << std::setw(2) << minutes << \":\";\n  ss << std::setw(2) << seconds;\n  return ss.str();\n}\n\nstd::string pretty_wall_time() { return pretty_wall_time(sys::wall_time()); }\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/ParallelInfo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions that provide low-level system information such as number\n/// of nodes and processors.  When working with distributed objects, one should\n/// use the corresponding functions in Parallel/Info.hpp instead of the\n/// low-level functions here.\n\n#pragma once\n\n#include <string>\n\nnamespace sys {\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Number of processing elements.\n */\nint number_of_procs();\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief %Index of my processing element.\n */\nint my_proc();\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Number of nodes.\n */\nint number_of_nodes();\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief %Index of my node.\n */\nint my_node();\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Number of processing elements on the given node.\n */\nint procs_on_node(int node_index);\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief The local index of my processing element on my node.\n * This is in the interval 0, ..., procs_on_node(my_node()) - 1.\n */\nint my_local_rank();\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief %Index of first processing element on the given node.\n */\nint first_proc_on_node(int node_index);\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief %Index of the node for the given processing element.\n */\nint node_of(int proc_index);\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief The local index for the given processing element on its node.\n */\nint local_rank_of(int proc_index);\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief The elapsed wall time in seconds.\n */\ndouble wall_time();\n\n/// @{\n/// \\ingroup UtilitiesGroup\n/// \\brief Format the wall time in DD-HH:MM:SS format.\n///\n/// If the walltime is shorter than a day, omit the `DD-` part.\nstd::string pretty_wall_time(double total_seconds);\n\nstd::string pretty_wall_time();\n/// @}\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/Prefetch.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/System/Prefetch.hpp\"\n\n#include <ostream>\n#include <stdexcept>\n\nnamespace sys {\nstd::ostream& operator<<(std::ostream& os, const PrefetchTo cache_location) {\n  switch (cache_location) {\n    case PrefetchTo::L1Cache:\n      return os << \"L1Cache\";\n    case PrefetchTo::L2Cache:\n      return os << \"L2Cache\";\n    case PrefetchTo::L3Cache:\n      return os << \"L3Cache\";\n    case PrefetchTo::NonTemporal:\n      return os << \"NonTemporal\";\n    case PrefetchTo::WriteL1Cache:\n      return os << \"WriteL1Cache\";\n    case PrefetchTo::WriteL2Cache:\n      return os << \"WriteL2Cache\";\n    default:\n      throw std::runtime_error(\"Unknown value of cache_location\");\n  };\n}\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/Prefetch.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\nnamespace sys {\n/// The cache location to prefetch data to.\nenum class PrefetchTo : int {\n  /// Prefetch into L1 data cache\n  ///\n  /// Typically the fastest CPU cache, about 32 KB in size.\n  L1Cache = 3,\n  /// Prefetch into L2 data cache\n  ///\n  /// Typically the second fastest CPU cache, about 128 or 256 KB in size.\n  L2Cache = 2,\n  /// Prefetch into L3 data cache\n  ///\n  /// Typically the slowest CPU cache and is shared among multiple cores with\n  /// sizes varying by factors of several.\n  L3Cache = 1,\n  /// Non-temporal is an element that is unlikely to be re-used. E.g., read\n  /// once, written but never read.\n  NonTemporal = 0,\n  /// Prefetch to L1 data cache for writing\n  WriteL1Cache = 7,\n  /// Prefetch to L2 data cache for writing\n  WriteL2Cache = 6\n};\n\n/// \\brief Prefetch data into a specific level of data cache.\ntemplate <PrefetchTo CacheLocation>\n#if defined(__GNUC__)\n__attribute__((always_inline)) inline\n#endif\nvoid prefetch(const void* address_to_prefetch) {\n  // The enum values are bit flags where the lowest two bits (right-most)\n  // control the cache level and the 3rd bit controls whether it is a write-only\n  // or read-write operation.\n  __builtin_prefetch(address_to_prefetch,\n                     (static_cast<int>(CacheLocation) >> 2) & 1,\n                     static_cast<int>(CacheLocation) & 0x3);\n}\n\nstd::ostream& operator<<(std::ostream& os, PrefetchTo cache_location);\n}  // namespace sys\n"
  },
  {
    "path": "src/Utilities/System/System.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/*!\n * \\brief Utilities for hardware and system interop.\n */\nnamespace sys {}\n"
  },
  {
    "path": "src/Utilities/TMPL.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Wraps the template metaprogramming library used (brigand)\n\n#pragma once\n\n// Since this header only wraps brigand and several additions to it we mark\n// it as a system header file so that clang-tidy ignores it.\n#ifdef __GNUC__\n#pragma GCC system_header\n#endif\n\n/// \\cond NEVER\n#define BRIGAND_NO_BOOST_SUPPORT\n/// \\endcond\n#include <brigand/brigand.hpp>\n\n#include <cstddef>\n#include <initializer_list>\n#include <type_traits>\n#include <variant>\n\n#include \"Utilities/TmplDigraph.hpp\"\n\nnamespace brigand {\n/// \\cond\nnamespace detail {\n\n// specializations to catch attempts to transform a non-sequence\ntemplate <typename NotSeq, class Func>\nstruct transform<0, NotSeq, Func> {\n  static_assert(\n      std::is_same_v<list<>, NotSeq>,\n      \"Cannot transform a non-sequence (the second argument of is_same_v).\");\n  using type = std::void_t<>;\n};\n\ntemplate <typename NotSeq, template <class...> class Seq, class... T,\n          class Func>\nstruct transform<1, NotSeq, Seq<T...>, Func> {\n  static_assert(\n      std::is_same_v<list<>, NotSeq>,\n      \"Cannot transform a non-sequence (the second argument of is_same_v).\");\n  using type = std::void_t<>;\n};\n\ntemplate <template <class...> class Seq, class... T, typename NotSeq,\n          class Func>\nstruct transform<1, Seq<T...>, NotSeq, Func> {\n  static_assert(\n      std::is_same_v<list<>, NotSeq>,\n      \"Cannot transform a non-sequence (the second argument of is_same_v).\");\n  using type = std::void_t<>;\n};\n\ntemplate <typename NotSeq1, typename NotSeq2, class Func>\nstruct transform<1, NotSeq1, NotSeq2, Func> {\n  static_assert(\n      std::is_same_v<list<>, NotSeq1>,\n      \"Cannot transform a non-sequence (the second argument of is_same_v).\");\n  using type = std::void_t<>;\n};\n\ntemplate <bool b, typename O, typename L, std::size_t I, typename R,\n          typename U = void>\nstruct replace_at_impl;\n\ntemplate <template <typename...> class S, typename... Os, typename... Ts,\n          typename R>\nstruct replace_at_impl<false, S<Os...>, S<Ts...>, 0, R> {\n  using type = S<Os..., Ts...>;\n};\n\ntemplate <template <typename...> class S, typename... Os, typename... Ts,\n          typename T, typename R>\nstruct replace_at_impl<false, S<Os...>, S<T, Ts...>, 1, R>\n    : replace_at_impl<false, S<Os..., R>, S<Ts...>, 0, R> {};\n\ntemplate <template <typename...> class S, typename... Os, typename T,\n          typename... Ts, std::size_t I, typename R>\nstruct replace_at_impl<false, S<Os...>, S<T, Ts...>, I, R,\n                       typename std::enable_if<(I > 1)>::type>\n    : replace_at_impl<false, S<Os..., T>, S<Ts...>, (I - 1), R> {};\n\ntemplate <template <typename...> class S, typename... Os, typename T1,\n          typename T2, typename T3, typename T4, typename T5, typename T6,\n          typename T7, typename T8, typename T9, typename T10, typename T11,\n          typename T12, typename T13, typename T14, typename T15, typename T16,\n          typename... Ts, std::size_t I, typename R>\nstruct replace_at_impl<true, S<Os...>,\n                       S<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,\n                         T14, T15, T16, Ts...>,\n                       I, R>\n    : replace_at_impl<((I - 16) > 16),\n                      S<Os..., T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,\n                        T12, T13, T14, T15, T16>,\n                      S<Ts...>, (I - 16), R> {};\n\ntemplate <typename L, typename I, typename R>\nstruct call_replace_at_impl\n    : replace_at_impl<(I::value > 15), brigand::clear<L>, L, I::value + 1, R> {\n};\n}  // namespace detail\n/// \\endcond\n\nnamespace lazy {\ntemplate <typename L, typename I, typename R>\nusing replace_at = ::brigand::detail::call_replace_at_impl<L, I, R>;\n}  // namespace lazy\ntemplate <typename L, typename I, typename R>\nusing replace_at = typename ::brigand::lazy::replace_at<L, I, R>::type;\n}  // namespace brigand\n\nnamespace brigand {\nnamespace detail {\ntemplate <typename List, typename Ind1, typename Ind2>\nstruct swap_at_impl {\n  using type = ::brigand::replace_at<\n      ::brigand::replace_at<List, Ind1, ::brigand::at<List, Ind2>>, Ind2,\n      ::brigand::at<List, Ind1>>;\n};\n}  // namespace detail\n\ntemplate <typename List, typename Ind1, typename Ind2>\nusing swap_at =\n    typename ::brigand::detail::swap_at_impl<List, Ind1, Ind2>::type;\n}  // namespace brigand\n\nnamespace brigand {\ntemplate <typename V>\nusing abs = std::integral_constant<typename V::value_type,\n                                   (V::value < 0 ? -V::value : V::value)>;\n\ntemplate <typename V>\nusing sign =\n    std::integral_constant<typename V::value_type, (V::value < 0 ? -1 : 1)>;\n\ntemplate <int T>\nusing int_ = std::integral_constant<int, T>;\n\ntemplate <typename V, typename N>\nstruct power\n    : std::integral_constant<\n          typename V::value_type,\n          V::value * power<V, std::integral_constant<typename N::value_type,\n                                                     N::value - 1>>::value> {};\n\ntemplate <typename V, typename T>\nstruct power<V, std::integral_constant<T, 0>>\n    : std::integral_constant<typename V::value_type, 1> {};\n\ntemplate <typename T>\nstruct factorial : times<T, factorial<uint64_t<T::value - 1>>> {};\ntemplate <>\nstruct factorial<uint64_t<1>> : uint64_t<1> {};\n}  // namespace brigand\n\nnamespace brigand {\nnamespace detail {\ntemplate <typename List, std::size_t Size = size<List>::value>\nstruct permutations_impl {\n  template <typename T, typename List1>\n  struct helper {\n    using type = ::brigand::transform<\n        typename permutations_impl<::brigand::remove<List1, T>>::type,\n        ::brigand::lazy::push_front<_state, T>>;\n  };\n\n  using type = ::brigand::fold<\n      List, list<>,\n      ::brigand::lazy::append<::brigand::_state, helper<::brigand::_element,\n                                                        ::brigand::pin<List>>>>;\n};\n\ntemplate <typename List>\nstruct permutations_impl<List, 1> {\n  using type = list<List>;\n};\n}  // namespace detail\n\nnamespace lazy {\ntemplate <typename List>\nusing permutations = detail::permutations_impl<List>;\n}  // namespace lazy\n\ntemplate <typename List>\nusing permutations = typename lazy::permutations<List>::type;\n}  // namespace brigand\n\nnamespace brigand {\nnamespace detail {\ntemplate <typename List, std::size_t Size = ::brigand::size<List>::value>\nstruct generic_permutations_impl {\n  template <typename Lc, typename List1>\n  struct helper {\n    using type = ::brigand::transform<\n        typename generic_permutations_impl<::brigand::erase<List1, Lc>>::type,\n        ::brigand::lazy::push_front<::brigand::_state,\n                                    ::brigand::at<List1, Lc>>>;\n  };\n  using type = ::brigand::fold<\n      ::brigand::make_sequence<brigand::uint32_t<0>, Size>, ::brigand::list<>,\n      ::brigand::lazy::append<::brigand::_state, helper<::brigand::_element,\n                                                        ::brigand::pin<List>>>>;\n};\n\ntemplate <typename List>\nstruct generic_permutations_impl<List, 1> {\n  using type = ::brigand::list<List>;\n};\n}  // namespace detail\n\nnamespace lazy {\ntemplate <typename List>\nusing generic_permutations = detail::generic_permutations_impl<List>;\n}  // namespace lazy\n\ntemplate <typename List>\nusing generic_permutations = typename lazy::generic_permutations<List>::type;\n}  // namespace brigand\n\nnamespace brigand {\nnamespace detail {\ntemplate <typename List, typename Number = uint32_t<1>>\nstruct combinations_impl_helper {\n  using split_list = split_at<List, Number>;\n  using type =\n      fold<back<split_list>, list<>,\n           lazy::append<\n               _state,\n               bind<list, bind<push_back, pin<front<split_list>>, _element>>>>;\n};\n\ntemplate <typename List, typename OutSize, typename = void>\nstruct combinations_impl {\n  using type =\n      append<list<>,\n             typename combinations_impl_helper<List, prev<OutSize>>::type,\n             typename combinations_impl<pop_front<List>, OutSize>::type>;\n};\ntemplate <typename List, typename OutSize>\nstruct combinations_impl<\n    List, OutSize,\n    typename std::enable_if<OutSize::value == size<List>::value>::type> {\n  using type = typename combinations_impl_helper<List, prev<OutSize>>::type;\n};\n}  // namespace detail\n\nnamespace lazy {\ntemplate <typename List, typename OutSize = uint32_t<2>>\nusing combinations = detail::combinations_impl<List, OutSize>;\n}  // namespace lazy\n\ntemplate <typename List, typename OutSize = uint32_t<2>>\nusing combinations = typename lazy::combinations<List, OutSize>::type;\n}  // namespace brigand\n\nnamespace brigand {\nnamespace detail {\ntemplate <typename Seq, typename T>\nstruct equal_members_helper\n    : std::is_same<count_if<Seq, std::is_same<T, _1>>, size_t<1>> {};\n}  // namespace detail\n\ntemplate <typename List1, typename List2>\nusing equal_members = and_<\n    fold<List1, bool_<true>,\n         and_<_state, detail::equal_members_helper<pin<List2>, _element>>>,\n    fold<List2, bool_<true>,\n         and_<_state, detail::equal_members_helper<pin<List1>, _element>>>>;\n}  // namespace brigand\n\nnamespace brigand {\nnamespace detail {\ntemplate <typename Functor, typename State, typename I, typename Sequence>\nstruct enumerated_fold_impl {\n  using type = State;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0>\nstruct enumerated_fold_impl<Functor, State, I, Sequence<T0>> {\n  using type = brigand::apply<Functor, State, T0, I>;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0, typename T1>\nstruct enumerated_fold_impl<Functor, State, I, Sequence<T0, T1>> {\n  using type = brigand::apply<Functor, brigand::apply<Functor, State, T0, I>,\n                              T1, brigand::next<I>>;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0, typename T1,\n          typename T2>\nstruct enumerated_fold_impl<Functor, State, I, Sequence<T0, T1, T2>> {\n  using type = brigand::apply<\n      Functor,\n      brigand::apply<Functor, brigand::apply<Functor, State, T0, I>, T1,\n                     brigand::next<I>>,\n      T2, brigand::plus<I, brigand::int32_t<2>>>;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0, typename T1,\n          typename T2, typename T3>\nstruct enumerated_fold_impl<Functor, State, I, Sequence<T0, T1, T2, T3>> {\n  using type = brigand::apply<\n      Functor,\n      brigand::apply<\n          Functor,\n          brigand::apply<Functor, brigand::apply<Functor, State, T0, I>, T1,\n                         brigand::next<I>>,\n          T2, brigand::plus<I, brigand::int32_t<2>>>,\n      T3, brigand::plus<I, brigand::int32_t<3>>>;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0, typename T1,\n          typename T2, typename T3, typename T4>\nstruct enumerated_fold_impl<Functor, State, I, Sequence<T0, T1, T2, T3, T4>> {\n  using type = brigand::apply<\n      Functor,\n      brigand::apply<\n          Functor,\n          brigand::apply<\n              Functor,\n              brigand::apply<Functor, brigand::apply<Functor, State, T0, I>, T1,\n                             brigand::next<I>>,\n              T2, brigand::plus<I, brigand::int32_t<2>>>,\n          T3, brigand::plus<I, brigand::int32_t<3>>>,\n      T4, brigand::plus<I, brigand::int32_t<4>>>;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0, typename T1,\n          typename T2, typename T3, typename T4, typename T5>\nstruct enumerated_fold_impl<Functor, State, I,\n                            Sequence<T0, T1, T2, T3, T4, T5>> {\n  using type = brigand::apply<\n      Functor,\n      brigand::apply<\n          Functor,\n          brigand::apply<\n              Functor,\n              brigand::apply<\n                  Functor,\n                  brigand::apply<Functor, brigand::apply<Functor, State, T0, I>,\n                                 T1, brigand::next<I>>,\n                  T2, brigand::plus<I, brigand::int32_t<2>>>,\n              T3, brigand::plus<I, brigand::int32_t<3>>>,\n          T4, brigand::plus<I, brigand::int32_t<4>>>,\n      T5, brigand::plus<I, brigand::int32_t<5>>>;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0, typename T1,\n          typename T2, typename T3, typename T4, typename T5, typename T6>\nstruct enumerated_fold_impl<Functor, State, I,\n                            Sequence<T0, T1, T2, T3, T4, T5, T6>> {\n  using type = brigand::apply<\n      Functor,\n      brigand::apply<\n          Functor,\n          brigand::apply<\n              Functor,\n              brigand::apply<\n                  Functor,\n                  brigand::apply<\n                      Functor,\n                      brigand::apply<Functor,\n                                     brigand::apply<Functor, State, T0, I>, T1,\n                                     brigand::next<I>>,\n                      T2, brigand::plus<I, brigand::int32_t<2>>>,\n                  T3, brigand::plus<I, brigand::int32_t<3>>>,\n              T4, brigand::plus<I, brigand::int32_t<4>>>,\n          T5, brigand::plus<I, brigand::int32_t<5>>>,\n      T6, brigand::plus<I, brigand::int32_t<6>>>;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0, typename T1,\n          typename T2, typename T3, typename T4, typename T5, typename T6,\n          typename T7>\nstruct enumerated_fold_impl<Functor, State, I,\n                            Sequence<T0, T1, T2, T3, T4, T5, T6, T7>> {\n  using type = brigand::apply<\n      Functor,\n      brigand::apply<\n          Functor,\n          brigand::apply<\n              Functor,\n              brigand::apply<\n                  Functor,\n                  brigand::apply<\n                      Functor,\n                      brigand::apply<\n                          Functor,\n                          brigand::apply<Functor,\n                                         brigand::apply<Functor, State, T0, I>,\n                                         T1, brigand::next<I>>,\n                          T2, brigand::plus<I, brigand::int32_t<2>>>,\n                      T3, brigand::plus<I, brigand::int32_t<3>>>,\n                  T4, brigand::plus<I, brigand::int32_t<4>>>,\n              T5, brigand::plus<I, brigand::int32_t<5>>>,\n          T6, brigand::plus<I, brigand::int32_t<6>>>,\n      T7, brigand::plus<I, brigand::int32_t<7>>>;\n};\n\ntemplate <typename Functor, typename State, typename I,\n          template <typename...> class Sequence, typename T0, typename T1,\n          typename T2, typename T3, typename T4, typename T5, typename T6,\n          typename T7, typename... T>\nstruct enumerated_fold_impl<Functor, State, I,\n                            Sequence<T0, T1, T2, T3, T4, T5, T6, T7, T...>>\n    : enumerated_fold_impl<\n          Functor,\n          brigand::apply<\n              Functor,\n              brigand::apply<\n                  Functor,\n                  brigand::apply<\n                      Functor,\n                      brigand::apply<\n                          Functor,\n                          brigand::apply<\n                              Functor,\n                              brigand::apply<\n                                  Functor,\n                                  brigand::apply<\n                                      Functor,\n                                      brigand::apply<Functor, State, T0, I>, T1,\n                                      brigand::next<I>>,\n                                  T2, brigand::plus<I, brigand::int32_t<2>>>,\n                              T3, brigand::plus<I, brigand::int32_t<3>>>,\n                          T4, brigand::plus<I, brigand::int32_t<4>>>,\n                      T5, brigand::plus<I, brigand::int32_t<5>>>,\n                  T6, brigand::plus<I, brigand::int32_t<6>>>,\n              T7, brigand::plus<I, brigand::int32_t<7>>>,\n          brigand::plus<I, brigand::int32_t<8>>, Sequence<T...>> {};\n}  // namespace detail\n\nnamespace lazy {\ntemplate <typename Sequence, typename State, typename Functor,\n          typename I = brigand::int32_t<0>>\nusing enumerated_fold =\n    typename detail::enumerated_fold_impl<Functor, State, I, Sequence>;\n}  // namespace lazy\n\ntemplate <typename Sequence, typename State, typename Functor,\n          typename I = brigand::int32_t<0>>\nusing enumerated_fold =\n    typename lazy::enumerated_fold<Sequence, State, Functor, I>::type;\n}  // namespace brigand\n\nnamespace brigand {\ntemplate <bool>\nstruct conditional;\n\ntemplate <>\nstruct conditional<true> {\n  template <typename T, typename F>\n  using type = T;\n};\n\ntemplate <>\nstruct conditional<false> {\n  template <typename T, typename F>\n  using type = F;\n};\n\ntemplate <bool B, typename T, typename F>\nusing conditional_t = typename conditional<B>::template type<T, F>;\n}  // namespace brigand\n\nnamespace brigand {\ntemplate <bool>\nstruct branch_if;\n\ntemplate <>\nstruct branch_if<true> {\n  template <typename T, typename F>\n  using type = typename detail::apply<T>::type;\n};\n\ntemplate <>\nstruct branch_if<false> {\n  template <typename T, typename F>\n  using type = typename detail::apply<F>::type;\n};\n\ntemplate <bool B, typename T, typename F>\nusing branch_if_t = typename branch_if<B>::template type<T, F>;\n}  // namespace brigand\n\nnamespace tmpl = brigand;\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Metaprogramming things that are not planned to be submitted to Brigand\n */\nnamespace tmpl2 {\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief A compile-time list of values of the same type\n */\ntemplate <class T, T...>\nstruct value_list {};\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief A non-short-circuiting logical AND between bools 'B\"\"\n *\n * Useful when arbitrarily large parameter packs need to be evaluated, since\n * std::conjunction and std::disjunction use recursion\n */\ntemplate <bool... Bs>\nusing flat_all =\n    std::is_same<value_list<bool, Bs...>,\n                 value_list<bool, (static_cast<void>(Bs), true)...>>;\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief A non-short-circuiting logical AND between bools 'B\"\"\n *\n * Useful when arbitrarily large parameter packs need to be evaluated, since\n * std::conjunction and std::disjunction use recursion\n */\ntemplate <bool... Bs>\nconstexpr bool flat_all_v = flat_all<Bs...>::value;\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief A non-short-circuiting logical OR between bools 'B\"\"\n *\n * Useful when arbitrarily large parameter packs need to be evaluated, since\n * std::conjunction and std::disjunction use recursion\n */\ntemplate <bool... Bs>\nusing flat_any = std::integral_constant<\n    bool, not std::is_same<\n              value_list<bool, Bs...>,\n              value_list<bool, (static_cast<void>(Bs), false)...>>::value>;\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief A non-short-circuiting logical OR between bools 'B\"\"\n *\n * Useful when arbitrarily large parameter packs need to be evaluated, since\n * std::conjunction and std::disjunction use recursion\n */\ntemplate <bool... Bs>\nconstexpr bool flat_any_v = flat_any<Bs...>::value;\n}  // namespace tmpl2\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Allows zero-cost unordered expansion of a parameter\n *\n * \\details\n * Expands a parameter pack, typically useful for runtime evaluation via a\n * Callable such as a lambda, function, or function object. For example,\n * an unordered transform of a std::tuple can be implemented as:\n * \\snippet Utilities/Test_TMPL.cpp expand_pack_example\n *\n * \\see tuple_fold tuple_counted_fold tuple_transform std::tuple\n * EXPAND_PACK_LEFT_TO_RIGHT\n */\ntemplate <typename... Ts>\nconstexpr void expand_pack(Ts&&... /*unused*/) {}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Expand a parameter pack evaluating the terms from left to right.\n *\n * The parameter pack inside the argument to the macro must not be expanded\n * since the macro will do the expansion correctly for you. In the below example\n * a parameter pack of `std::integral_constant<size_t, I>` is passed to the\n * function. The closure `lambda` is used to sum up the values of all the `Ts`.\n * Note that the `Ts` passed to `EXPAND_PACK_LEFT_TO_RIGHT` is not expanded.\n *\n * \\snippet Utilities/Test_TMPL.cpp expand_pack_left_to_right\n *\n * \\see tuple_fold tuple_counted_fold tuple_transform std::tuple expand_pack\n */\n#define EXPAND_PACK_LEFT_TO_RIGHT(...) \\\n  (void)std::initializer_list<char> { ((void)(__VA_ARGS__), '0')... }\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Returns the first argument of a parameter pack\n */\ntemplate <typename T, typename... Ts>\nconstexpr decltype(auto) get_first_argument(T&& t, Ts&&... /*rest*/) {\n  return t;\n}\n\nnamespace brigand {\nnamespace lazy {\n/// Check if a typelist contains an item.\ntemplate <typename Sequence, typename Item>\nstruct list_contains;\n\n/// \\cond\ntemplate <template <typename...> typename L, typename... Items, typename Item>\nstruct list_contains<L<Items...>, Item>\n    : bool_<(... or std::is_same_v<Items, Item>)> {};\n/// \\endcond\n}  // namespace lazy\n\n/// Check if a typelist contains an item.\n/// @{\ntemplate <typename Sequence, typename Item>\nusing list_contains = typename lazy::list_contains<Sequence, Item>::type;\n\ntemplate <typename Sequence, typename Item>\nconstexpr bool list_contains_v = lazy::list_contains<Sequence, Item>::value;\n/// @}\n\n/// Obtain the elements of `Sequence1` that are not in `Sequence2`.\ntemplate <typename Sequence1, typename Sequence2>\nusing list_difference =\n    fold<Sequence2, Sequence1, lazy::remove<_state, _element>>;\n\ntemplate <typename List>\nusing remove_duplicates = fold<List, clear<List>,\n                               if_<lazy::list_contains<_state, _element>,\n                                   _state, bind<push_back, _state, _element>>>;\n\nnamespace detail {\ntemplate <typename List>\nstruct as_pack_impl;\n\ntemplate <template <typename...> typename L, typename... Args>\nstruct as_pack_impl<L<Args...>> {\n  template <typename F>\n  static constexpr decltype(auto) apply(F&& f) {\n    return std::forward<F>(f)(type_<Args>{}...);\n  }\n};\n}  // namespace detail\n\n/// Call a functor with the types from a list.\n///\n/// Given a typelist `List = tmpl::list<A, B, ...>` (not necessarily\n/// with head `tmpl::list`), calls \\p f as `f(tmpl::type_<A>{},\n/// tmpl::type_<B>{}, ...)` and returns the result.\n///\n/// This is useful for converting a typelist into a parameter pack.\n///\n/// \\snippet Utilities/Test_TMPL.cpp as_pack\ntemplate <typename List, typename F>\nconstexpr decltype(auto) as_pack(F&& f) {\n  return detail::as_pack_impl<List>::apply(std::forward<F>(f));\n}\n\nnamespace detail {\ntemplate <typename Sequence>\nstruct make_std_variant_over_impl;\n\ntemplate <template <typename...> class Sequence, typename... Ts>\nstruct make_std_variant_over_impl<Sequence<Ts...>> {\n  static_assert(((not std::is_same_v<std::decay_t<std::remove_pointer_t<Ts>>,\n                                     void>)&&...),\n                \"Cannot create a std::variant with a 'void' type.\");\n  using type = std::variant<Ts...>;\n};\n}  // namespace detail\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Create a std::variant with all all the types inside the typelist\n * Sequence\n *\n * \\metareturns std::variant of all types inside `Sequence`\n */\ntemplate <typename Sequence>\nusing make_std_variant_over = typename detail::make_std_variant_over_impl<\n    tmpl::remove_duplicates<Sequence>>::type;\n}  // namespace brigand\n"
  },
  {
    "path": "src/Utilities/TmplDebugging.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines class TypeDisplayer\n\n#pragma once\n\n#include \"Utilities/TMPL.hpp\"\n\n/*!\n * \\ingroup UtilitiesGroup TypeTraitsGroup\n * \\brief Get compiler error with type of template parameter\n *\n * The compiler error generated when using an object of type\n * `TypeDisplayer<...>` contains the types of the template parameters. This\n * effectively provides printf-debugging for metaprogramming. For example,\n * \\code\n * TypeDisplayer<std::vector<double>> some_random_name;\n * \\endcode\n * will produce a compiler error that contains the type `std::vector<double,\n * std::allocator...>`. TypeDisplayer is extremely useful when debugging\n * template metaprograms.\n *\n * \\note The TypeDisplayer header should only be included during testing\n * and debugging.\n *\n * \\see make_list\n */\ntemplate <typename...>\nstruct TypeDisplayer;\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Metafunction to turn a parameter pack into a typelist\n *\n * This metafunction is really only useful for debugging metaprograms. For\n * example, the desired algorithm might be:\n *\n * \\code\n * using variables_tags_from_single_tags = tmpl::filter<\n *     extracted_from_variables,\n *     tmpl::bind<tmpl::found, tmpl::pin<mutated_tags_list>,\n *                tmpl::bind<std::is_same, tmpl::_1, tmpl::parent<tmpl::_1>>>>;\n * \\endcode\n *\n * However, getting the `tmpl::pin`, `tmpl::parent`, and `tmpl::bind` calls\n * right can be extremely frustrating with little help as to what is going on.\n * Let's introduce an error by pinning `tmpl::_1`:\n *\n * \\code\n * using variables_tags_from_single_tags = tmpl::filter<\n *     extracted_from_variables,\n *     tmpl::bind<tmpl::found, tmpl::pin<mutated_tags_list>,\n *                tmpl::bind<std::is_same, tmpl::pin<tmpl::_1>,\n *                tmpl::parent<tmpl::_1>>>>;\n * \\endcode\n *\n * The result is comparing all values in `extracted_from_variables` to\n * themselves. To find this out, replace `tmpl::filter` and `tmpl::found` with\n * `tmpl::transform`, and the metafunction `std::is_same` to `make_list`.\n * You will then get back a \"backtrace\" of what the algorithm did, which is\n * invaluable for getting the `tmpl::pin` and `tmpl::parent` right. That is,\n *\n * \\code\n * using variables_tags_from_single_tags2 = tmpl::transform<\n *     extracted_from_variables,\n *     tmpl::bind<tmpl::transform, tmpl::pin<mutated_tags_list>,\n *                tmpl::bind<make_list, tmpl::_1, tmpl::parent<tmpl::_1>>>>;\n *\n * TypeDisplayer<variables_tags_from_single_tags2> aeou;\n * \\endcode\n *\n * You will get an output along the lines of:\n *\n * \\code\n * src/DataStructures/DataBox.hpp:1181:40: error: implicit instantiation of\n * undefined template\n * 'TypeDisplayer<brigand::list<brigand::list<\n *     brigand::list<test_databox_tags::ScalarTag,\n *     test_databox_tags::Tag0>, brigand::list<test_databox_tags::ScalarTag,\n *     Tags::Variables<brigand::list<test_databox_tags::ScalarTag,\n *                     test_databox_tags::VectorTag> > > >,\n *     brigand::list<brigand::list<test_databox_tags::VectorTag,\n *                   test_databox_tags::Tag0>,\n *     brigand::list<test_databox_tags::VectorTag,\n *                   Tags::Variables<\n *                   brigand::list<test_databox_tags::ScalarTag,\n *                   test_databox_tags::VectorTag> > > > > >'\n * \\endcode\n *\n * \\see TypeDisplayer\n */\ntemplate <class... Ts>\nstruct make_list {\n  using type = tmpl::list<Ts...>;\n};\n"
  },
  {
    "path": "src/Utilities/TmplDigraph.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <brigand/brigand.hpp>\n#include <type_traits>\n\n#include \"Utilities/Requires.hpp\"\n\nnamespace brigand {\n\ntemplate <typename Source, typename Destination, typename Weight = int32_t<1>>\nstruct edge {\n  using source = Source;\n  using destination = Destination;\n  using weight = Weight;\n};\n\ntemplate <int Source, int Destination, int Weight = 1>\nusing edge_ = edge<int32_t<Source>, int32_t<Destination>, int32_t<Weight>>;\n\ntemplate <typename T>\nstruct get_source;\ntemplate <typename Source, typename Destination, typename Weight>\nstruct get_source<edge<Source, Destination, Weight>> {\n  using type = Source;\n};\n\ntemplate <typename T>\nstruct get_destination;\ntemplate <typename Source, typename Destination, typename Weight>\nstruct get_destination<edge<Source, Destination, Weight>> {\n  using type = Destination;\n};\n\ntemplate <typename T>\nstruct get_weight;\ntemplate <typename Source, typename Destination, typename Weight>\nstruct get_weight<edge<Source, Destination, Weight>> {\n  using type = Weight;\n};\n\ntemplate <typename State, typename Element>\nstruct add_unique_vertex;\n\ntemplate <typename State, typename Source, typename Destination,\n          typename Weight>\nstruct add_unique_vertex<State, edge<Source, Destination, Weight>> {\n  using source = typename if_<found<State, std::is_same<pin<Source>, _1>>,\n                              list<>, list<Source>>::type;\n  using destination =\n      typename if_<found<State, std::is_same<pin<Destination>, _1>>, list<>,\n                   list<Destination>>::type;\n  using type = append<State, source, destination>;\n};\n\ntemplate <class E, class S, class = std::nullptr_t>\nstruct has_source : std::false_type {};\ntemplate <class E, class S>\nstruct has_source<E, S, Requires<std::is_same<typename E::source, S>::value>>\n    : std::true_type {};\n\ntemplate <class E, class D, class = void>\nstruct has_destination : std::false_type {};\ntemplate <class E, class D>\nstruct has_destination<\n    E, D, Requires<std::is_same<typename E::destination, D>::value>>\n    : std::true_type {};\n\ntemplate <class E, class S, class D>\nstruct has_source_and_destination : std::false_type {};\ntemplate <template <class...> class E, class S, class D, class W>\nstruct has_source_and_destination<E<S, D, W>, S, D> : std::true_type {};\n\ntemplate <class edgeList>\nstruct digraph;\n\nnamespace detail {\ntemplate <class Graph, class S, class D>\nstruct get_edge_impl;\ntemplate <class S, class D, class edgeList>\nstruct get_edge_impl<digraph<edgeList>, S, D> {\n  using type = find<edgeList, has_source_and_destination<_1, pin<S>, pin<D>>>;\n};\n}  // namespace detail\n\nnamespace detail {\ntemplate <class Graph, class S, class D>\nstruct has_edge_impl;\ntemplate <class S, class D, class edgeList>\nstruct has_edge_impl<digraph<edgeList>, S, D>\n    : found<edgeList, has_source_and_destination<_1, pin<S>, pin<D>>> {};\n}  // namespace detail\n\ntemplate <class Graph>\nstruct outgoing_edges_impl;\ntemplate <class edgeList>\nstruct outgoing_edges_impl<digraph<edgeList>> {\n  using type = typename digraph<edgeList>::adjacency_list;\n};\n\ntemplate <class Graph>\nstruct ingoing_edges_impl;\ntemplate <class edgeList>\nstruct ingoing_edges_impl<digraph<edgeList>> {\n  using type = typename digraph<edgeList>::ingoing_list;\n};\n\n// This is what we would like to do, but Intel cannot handle it...\ntemplate <class T, template <class...> class F, class... Es>\nstruct compute_adjacency_list;\ntemplate <template <class...> class VertexSeq, class... Vertices, class... Es,\n          template <class...> class F>\nstruct compute_adjacency_list<VertexSeq<Vertices...>, F, Es...> {\n  using type = brigand::list<\n      brigand::filter<brigand::list<Es...>, F<brigand::_1, pin<Vertices>>>...>;\n};\n\ntemplate <template <class...> class List, class... edges>\nstruct digraph<List<edges...>> {\n public:\n  using edge_list = list<edges...>;\n  static_assert(is_set<edge_list>::value,\n                \"Cannot have repeated edges in a digraph\");\n  using unique_vertex_list =\n      fold<edge_list, list<>, add_unique_vertex<_state, _element>>;\n  using vertex_count = size<unique_vertex_list>;\n  using edge_count = uint32_t<sizeof...(edges)>;\n\n  using adjacency_list =\n      compute_adjacency_list<unique_vertex_list, has_source, edges...>;\n\n  using ingoing_list =\n      compute_adjacency_list<unique_vertex_list, has_destination, edges...>;\n\n  template <class E>\n  static digraph<::brigand::remove<\n      list<edges...>,\n      detail::get_edge_impl<digraph<List<edges...>>, typename E::source,\n                            typename E::destination>>>\n      erase(brigand::type_<E>);\n\n  template <class E>\n  static digraph<push_back<\n      remove<list<edges...>,\n             detail::get_edge_impl<digraph<List<edges...>>, typename E::source,\n                                   typename E::destination>>,\n      E>>\n      insert(::brigand::type_<E>);\n};\n\nnamespace lazy {\ntemplate <class Graph, class S, class D>\nusing has_edge = ::brigand::detail::has_edge_impl<Graph, S, D>;\n}  // namespace lazy\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Check if a digraph has an edge with source `S` and destination `D`\n *\n *\n */\ntemplate <class Graph, class S, class D>\nusing has_edge = typename ::brigand::lazy::has_edge<Graph, S, D>::type;\n\nnamespace lazy {\ntemplate <class Graph, class S, class D>\nusing get_edge = ::brigand::detail::get_edge_impl<Graph, S, D>;\n}  // namespace lazy\n\ntemplate <class Graph, class S, class D>\nusing get_edge = typename ::brigand::lazy::get_edge<Graph, S, D>;\n\ntemplate <class Graph, class Vertex>\nusing outgoing_edges = at<typename Graph::adjacency_list::type,\n                          index_of<typename Graph::unique_vertex_list, Vertex>>;\n\ntemplate <class Graph, class Vertex>\nusing ingoing_edges = at<typename Graph::ingoing_list::type,\n                         index_of<typename Graph::unique_vertex_list, Vertex>>;\n\ntemplate <class Graph>\nusing vertex_count = size<typename Graph::unique_vertex_list>;\n\ntemplate <class Graph>\nusing edge_count = size<typename Graph::edge_list>;\n}  // namespace brigand\n"
  },
  {
    "path": "src/Utilities/Tuple.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions for manipulating tuples\n\n#pragma once\n\n#include <tuple>\n#include <utility>\n\nnamespace tuple_impl_detail {\ntemplate <bool ReverseIteration, typename... Elements, typename N_aryOp,\n          typename... Args, size_t... Is>\nconstexpr inline void tuple_fold_impl(const std::tuple<Elements...>& tupull,\n                                      N_aryOp&& op,\n                                      std::index_sequence<Is...> /*meta*/,\n                                      Args&... args) {\n  constexpr size_t tuple_size = sizeof...(Elements);\n  static_cast<void>(std::initializer_list<char>{\n      (static_cast<void>(\n           op(std::get<(ReverseIteration ? tuple_size - 1 - Is : Is)>(tupull),\n              args...)),\n       '0')...});\n}\n\ntemplate <bool ReverseIteration, typename... Elements, typename N_aryOp,\n          typename... Args, size_t... Is>\nconstexpr inline void tuple_counted_fold_impl(\n    const std::tuple<Elements...>& tupull, N_aryOp&& op,\n    std::index_sequence<Is...> /*meta*/, Args&... args) {\n  constexpr size_t tuple_size = sizeof...(Elements);\n  static_cast<void>(std::initializer_list<char>{\n      (static_cast<void>(\n           op(std::get<(ReverseIteration ? tuple_size - 1 - Is : Is)>(tupull),\n              (ReverseIteration ? tuple_size - 1 - Is : Is), args...)),\n       '0')...});\n}\n\ntemplate <bool ReverseIteration, typename... Elements, typename N_aryOp,\n          typename... Args, size_t... Is>\nconstexpr inline void tuple_transform_impl(\n    const std::tuple<Elements...>& tupull, N_aryOp&& op,\n    std::index_sequence<Is...> /*meta*/, Args&... args) {\n  constexpr size_t tuple_size = sizeof...(Elements);\n  static_cast<void>(std::initializer_list<char>{(\n      static_cast<void>(op(\n          std::get<(ReverseIteration ? tuple_size - 1 - Is : Is)>(tupull),\n          std::integral_constant<size_t, (ReverseIteration ? tuple_size - 1 - Is\n                                                           : Is)>{},\n          args...)),\n      '0')...});\n}\n}  // namespace tuple_impl_detail\n\n/// @{\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Perform a fold over a std::tuple\n *\n * \\details\n * Iterates over the elements in a std::tuple `tuple` from left to right\n * (left fold) calling `op(element, args...)` on each element in `tuple`. A\n * right fold can be done by explicitly setting the first template parameter\n * to true. Folds are easily implemented using `tuple_fold` by updating\n * one of the `args...` at each iteration. If you need the index of the current\n * element you can use the `tuple_counted_fold` variant. `tuple_counted_fold`\n * passes the current index as the second argument to the Callable `op`. That\n * is, `op(element, index, args...)`.\n *\n * \\example\n * The sum of a std::tuple of Arithmetics can be computed in several ways.\n * First, you can use a lambda:\n * \\snippet Utilities/Test_Tuple.cpp tuple_fold_lambda\n * You'll notice that `state` is taken by reference and mutated.\n *\n * You can do the same thing with a struct defined as\n * \\snippet Utilities/Test_Tuple.cpp tuple_fold_struct_defn\n * and then using an instance of the struct\n * \\snippet Utilities/Test_Tuple.cpp tuple_fold_struct\n *\n * \\note You are not able to pass a function pointer to `tuple_fold` or\n * `tuple_counted_fold` because you cannot pass a pointer to a function\n * template, only a function.\n *\n * \\see expand_pack tuple_transform tuple_fold tuple_counted_fold std::tuple\n */\ntemplate <bool ReverseIteration = false, typename... Elements, typename N_aryOp,\n          typename... Args>\nconstexpr inline void tuple_fold(const std::tuple<Elements...>& tuple,\n                                 N_aryOp&& op, Args&&... args) {\n  tuple_impl_detail::tuple_fold_impl<ReverseIteration>(\n      tuple, std::forward<N_aryOp>(op),\n      std::make_index_sequence<sizeof...(Elements)>{}, args...);\n}\n\ntemplate <bool ReverseIteration = false, typename... Elements, typename N_aryOp,\n          typename... Args>\nconstexpr inline void tuple_counted_fold(const std::tuple<Elements...>& tuple,\n                                         N_aryOp&& op, Args&&... args) {\n  tuple_impl_detail::tuple_counted_fold_impl<ReverseIteration>(\n      tuple, std::forward<N_aryOp>(op),\n      std::make_index_sequence<sizeof...(Elements)>{}, args...);\n}\n/// @}\n\n/*!\n * \\ingroup UtilitiesGroup\n * \\brief Perform a transform over a std::tuple\n *\n * \\details\n * Iterates over the elements in a std::tuple `tuple` from left to right\n * calling `op.operator()(element, index, args...)` on each element\n * in `tuple`. A right-to-left transform can be done by explicitly setting\n * the first template parameter to true. The second argument of the invokable\n * will be a deduced `std::integral_constant<size_t, value>`, from which the\n * current index can be extracted by using `decltype(index)::%value`.\n * For a function object the `decltype(index)` can be replaced by the deduced\n * type of `index`. For example,\n * \\snippet Utilities/Test_Tuple.cpp tuple_transform_negate\n *\n * Using `tuple_transform` with a generic lambda goes as follows,\n * \\snippet Utilities/Test_Tuple.cpp tuple_transform\n *\n * \\see expand_pack tuple_fold tuple_counted_fold std::tuple\n */\ntemplate <bool ReverseIteration = false, typename... Elements, typename N_aryOp,\n          typename... Args>\nconstexpr inline void tuple_transform(const std::tuple<Elements...>& tuple,\n                                      N_aryOp&& op, Args&&... args) {\n  tuple_impl_detail::tuple_transform_impl<ReverseIteration>(\n      tuple, std::forward<N_aryOp>(op),\n      std::make_index_sequence<sizeof...(Elements)>{}, args...);\n}\n"
  },
  {
    "path": "src/Utilities/TupleSlice.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions for slicing tuples\n\n#pragma once\n\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\nnamespace tuple_impl_detail {\ntemplate <size_t Start, typename Tuple, size_t... Is>\nconstexpr std::tuple<std::tuple_element_t<Is + Start, std::decay_t<Tuple>>...>\nslice_impl(Tuple&& tuple, std::index_sequence<Is...> /*meta*/) {\n  return {std::get<Is + Start>(std::forward<Tuple>(tuple))...};\n}\n}  // namespace tuple_impl_detail\n\n/// \\ingroup UtilitiesGroup\n/// The subset of elements in `tuple` from index `Start` to (excluding) `Stop`\ntemplate <size_t Start, size_t Stop, typename Tuple>\nconstexpr auto tuple_slice(Tuple&& tuple) {\n  constexpr size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;\n  static_assert(\n      Start <= Stop and Stop <= tuple_size,\n      \"'Start' and 'Stop' must satisfy Start <= Stop <= size of 'Tuple'\");\n  return tuple_impl_detail::slice_impl<Start>(\n      std::forward<Tuple>(tuple), std::make_index_sequence<Stop - Start>{});\n}\n\n/// \\ingroup UtilitiesGroup\n/// The first `Size` elements in `tuple`\ntemplate <size_t Size, typename Tuple>\nconstexpr auto tuple_head(Tuple&& tuple) {\n  constexpr size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;\n  static_assert(Size <= tuple_size,\n                \"'Size' must not exceed the size of 'Tuple'\");\n  return tuple_slice<0, Size>(std::forward<Tuple>(tuple));\n}\n\n/// \\ingroup UtilitiesGroup\n/// The last `Size` elements in `tuple`\ntemplate <size_t Size, typename Tuple>\nconstexpr auto tuple_tail(Tuple&& tuple) {\n  constexpr size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;\n  static_assert(Size <= tuple_size,\n                \"'Size' must not exceed the size of 'Tuple'\");\n  return tuple_slice<tuple_size - Size, tuple_size>(std::forward<Tuple>(tuple));\n}\n"
  },
  {
    "path": "src/Utilities/TypeTraits/ArraySize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <type_traits>\n\nnamespace tt {\nnamespace TypeTraits_detail {\ntemplate <typename T, std::size_t N>\nstd::integral_constant<std::size_t, N> array_size_impl(\n    const std::array<T, N>& /*array*/);\n}  // namespace TypeTraits_detail\n\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Get the size of a std::array as a std::integral_constant\n///\n/// \\details\n/// Given a std::array, `Array`, returns a std::integral_constant that has the\n/// size of the array as its value\n///\n/// \\usage\n/// For a std::array `T`\n/// \\code\n/// using result = tt::array_size<T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::integral_constant<std::size_t>\n///\n/// \\semantics\n/// For a type `T`,\n/// \\code\n/// using tt::array_size<std::array<T, N>> = std::integral_constant<std::size_t,\n/// N>;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_ArraySize.cpp array_size_example\n/// \\tparam Array the whose size should be stored in value of array_size\ntemplate <typename Array>\nusing array_size =\n    decltype(TypeTraits_detail::array_size_impl(std::declval<const Array&>()));\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  ArraySize.hpp\n  CanBeCopyConstructed.hpp\n  CreateGetStaticMemberVariableOrDefault.hpp\n  CreateGetTypeAliasOrDefault.hpp\n  CreateHasStaticMemberVariable.hpp\n  CreateHasTypeAlias.hpp\n  CreateIsCallable.hpp\n  FastPointerCast.hpp\n  FunctionInfo.hpp\n  GetFundamentalType.hpp\n  HasEquivalence.hpp\n  HasInequivalence.hpp\n  IsA.hpp\n  IsCallable.hpp\n  IsComplexOfFundamental.hpp\n  IsInteger.hpp\n  IsIterable.hpp\n  IsMaplike.hpp\n  IsStdArray.hpp\n  IsStdArrayOfSize.hpp\n  IsStreamable.hpp\n  RemoveReferenceWrapper.hpp\n  TypeTraits.hpp\n  )\n"
  },
  {
    "path": "src/Utilities/TypeTraits/CanBeCopyConstructed.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Check if `T` is copy constructible\n *\n * The STL `std::is_copy_constructible` does not work as expected with some\n * types, such as `std::unordered_map`. This is because\n * `std::is_copy_constructible` only checks that the copy construction call is\n * well-formed, not that it could actually be done in practice. To get around\n * this for containers we check that `T::value_type` is also copy constructible.\n */\ntemplate <typename T, typename = void>\nstruct can_be_copy_constructed : std::is_copy_constructible<T> {};\n\n/// \\cond\ntemplate <typename T>\nstruct can_be_copy_constructed<T, std::void_t<typename T::value_type>>\n    : std::bool_constant<std::is_copy_constructible_v<T> and\n                         std::is_copy_constructible_v<typename T::value_type>> {\n};\n/// \\endcond\n\ntemplate <typename T>\nconstexpr bool can_be_copy_constructed_v = can_be_copy_constructed<T>::value;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/CreateGetStaticMemberVariableOrDefault.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Generate a metafunction that will retrieve the specified\n * `static constexpr` member variable, or, if not present, a default.\n *\n * \\warning Please use defaults responsibly.  In many cases it is\n * better to require the static member variable is present so the\n * functionality isn't hidden.  Also make sure to document the\n * compile-time interface, e.g. with a \\ref protocols \"protocol\".\n */\n#define CREATE_GET_STATIC_MEMBER_VARIABLE_OR_DEFAULT(VARIABLE_NAME)          \\\n  template <typename CheckingType, auto Default, typename = std::void_t<>>   \\\n  struct get_##VARIABLE_NAME##_or_default {                                  \\\n    static constexpr auto value = Default;                                   \\\n  };                                                                         \\\n  template <typename CheckingType, auto Default>                             \\\n  struct get_##VARIABLE_NAME##_or_default<                                   \\\n      CheckingType, Default,                                                 \\\n      std::void_t<decltype(CheckingType::VARIABLE_NAME)>> {                  \\\n    static_assert(                                                           \\\n        std::is_same_v<std::decay_t<decltype(Default)>,                      \\\n                       std::decay_t<decltype(CheckingType::VARIABLE_NAME)>>, \\\n        \"Types of default and found value are not the same.\");               \\\n    static constexpr auto value = CheckingType::VARIABLE_NAME;               \\\n  };                                                                         \\\n  template <typename CheckingType, auto Default>                             \\\n  constexpr auto get_##VARIABLE_NAME##_or_default_v =                        \\\n      get_##VARIABLE_NAME##_or_default<CheckingType, Default>::value;\n"
  },
  {
    "path": "src/Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Generate a metafunction that will retrieve the specified type alias,\n * or if not present, assign a default type.\n *\n * \\warning Please use defaults responsibly.  In many cases it is\n * better to require the type alias is present so the\n * functionality isn't hidden.  Also make sure to document the\n * compile-time interface, e.g. with a \\ref protocols \"protocol\".\n */\n#define CREATE_GET_TYPE_ALIAS_OR_DEFAULT(ALIAS_NAME)                           \\\n  template <typename CheckingType, typename Default, typename = std::void_t<>> \\\n  struct get_##ALIAS_NAME##_or_default {                                       \\\n    using type = Default;                                                      \\\n  };                                                                           \\\n  template <typename CheckingType, typename Default>                           \\\n  struct get_##ALIAS_NAME##_or_default<                                        \\\n      CheckingType, Default, std::void_t<typename CheckingType::ALIAS_NAME>> { \\\n    using type = typename CheckingType::ALIAS_NAME;                            \\\n  };                                                                           \\\n  template <typename CheckingType, typename Default>                           \\\n  using get_##ALIAS_NAME##_or_default_t =                                      \\\n      typename get_##ALIAS_NAME##_or_default<CheckingType, Default>::type;\n"
  },
  {
    "path": "src/Utilities/TypeTraits/CreateHasStaticMemberVariable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/NoSuchType.hpp\"\n\n/// @{\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Generate a type trait to check if a class has a `static constexpr`\n * variable, optionally also checking its type.\n *\n * To generate the corresponding `_v` metafunction, call\n * `CREATE_HAS_STATIC_MEMBER_VARIABLE` first and then\n * `CREATE_HAS_STATIC_MEMBER_VARIABLE_V`.\n *\n * \\example\n * \\snippet Test_CreateHasStaticMemberVariable.cpp CREATE_HAS_EXAMPLE\n *\n * \\see `CREATE_IS_CALLABLE`\n */\n// Use `NoSuchType*****` to represent any `VariableType`, i.e. not checking the\n// variable type at all. If someone has that many pointers to a thing that isn't\n// useful, it's their fault...\n#define CREATE_HAS_STATIC_MEMBER_VARIABLE(CONSTEXPR_NAME)                     \\\n  template <typename CheckingType, typename VariableType = NoSuchType*****,   \\\n            typename = std::void_t<>>                                         \\\n  struct has_##CONSTEXPR_NAME : std::false_type {};                           \\\n                                                                              \\\n  template <typename CheckingType, typename VariableType>                     \\\n  struct has_##CONSTEXPR_NAME<CheckingType, VariableType,                     \\\n                              std::void_t<std::remove_const_t<decltype(       \\\n                                  CheckingType::CONSTEXPR_NAME)>>>            \\\n      : std::bool_constant<std::is_same_v<VariableType, NoSuchType*****> or   \\\n                           std::is_same_v<std::remove_const_t<decltype(       \\\n                                              CheckingType::CONSTEXPR_NAME)>, \\\n                                          VariableType>> {};\n\n// Separate macros to avoid compiler warnings about unused variables\n#define CREATE_HAS_STATIC_MEMBER_VARIABLE_V(CONSTEXPR_NAME)                 \\\n  template <typename CheckingType, typename VariableType = NoSuchType*****> \\\n  static constexpr const bool has_##CONSTEXPR_NAME##_v =                    \\\n      has_##CONSTEXPR_NAME<CheckingType, VariableType>::value;\n/// @}\n"
  },
  {
    "path": "src/Utilities/TypeTraits/CreateHasTypeAlias.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/NoSuchType.hpp\"\n\n/// @{\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Generate a type trait to check if a class has a type alias with a\n * particular name, optionally also checking its type.\n *\n * To generate the corresponding `_v` metafunction, call\n * `CREATE_HAS_TYPE_ALIAS` first and then `CREATE_HAS_TYPE_ALIAS_V`.\n *\n * \\example\n * \\snippet Test_CreateHasTypeAlias.cpp CREATE_HAS_TYPE_ALIAS\n *\n * \\see `CREATE_IS_CALLABLE`\n */\n// Use `NoSuchType*****` to represent any `AliasType`, i.e. not checking the\n// alias type at all. If someone has that many pointers to a thing that isn't\n// useful, it's their fault...\n#define CREATE_HAS_TYPE_ALIAS(ALIAS_NAME)                                 \\\n  template <typename CheckingType, typename AliasType = NoSuchType*****,  \\\n            typename = std::void_t<>>                                     \\\n  struct has_##ALIAS_NAME : std::false_type {};                           \\\n                                                                          \\\n  template <typename CheckingType, typename AliasType>                    \\\n  struct has_##ALIAS_NAME<CheckingType, AliasType,                        \\\n                          std::void_t<typename CheckingType::ALIAS_NAME>> \\\n      : std::bool_constant<                                               \\\n            std::is_same_v<AliasType, NoSuchType*****> or                 \\\n            std::is_same_v<typename CheckingType::ALIAS_NAME, AliasType>> {};\n// Separate macros to avoid compiler warnings about unused variables\n#define CREATE_HAS_TYPE_ALIAS_V(ALIAS_NAME)                              \\\n  template <typename CheckingType, typename AliasType = NoSuchType*****> \\\n  static constexpr const bool has_##ALIAS_NAME##_v =                     \\\n      has_##ALIAS_NAME<CheckingType, AliasType>::value;\n/// @}\n"
  },
  {
    "path": "src/Utilities/TypeTraits/CreateIsCallable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n/// @{\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Generate a type trait to check if a class has a member function that\n * can be invoked with arguments of type `TArgs...`\n *\n * The usage of the type trait is identical to the usage of the\n * `tt::is_callable` type trait. The name of the type trait is\n * `is_METHOD_NAME_callable` and is not placed in\n * the `tt` namespace. To avoid collisions it is highly recommended that type\n * traits generated with this macro are generated into `_detail` namespaces.\n * This will reduce redefinition compilation errors.\n *\n * Note that variable templates with `_r` and `_t` suffixes that follow the\n * standard library's naming convention are also generated. To generate\n * corresponding `_v` metafunctions, call `CREATE_IS_CALLABLE` first and then\n * `CREATE_IS_CALLABLE_V` and/or `CREATE_IS_CALLABLE_R_V`.\n *\n * \\example\n * \\snippet Test_CreateIsCallable.cpp CREATE_IS_CALLABLE_EXAMPLE\n *\n * \\see tt::is_callable\n */\n#define CREATE_IS_CALLABLE(METHOD_NAME)                                        \\\n  struct AnyReturnType##METHOD_NAME {};                                        \\\n                                                                               \\\n  template <typename ReturnType, typename TT, typename... TArgs>               \\\n  class is_##METHOD_NAME##_callable_r {                                        \\\n   private:                                                                    \\\n    struct NotCallable {};                                                     \\\n    template <typename T, typename... Args>                                    \\\n    static auto test_callable(int)                                             \\\n        -> decltype(std::declval<T>().METHOD_NAME(std::declval<Args>()...));   \\\n    template <typename, typename...>                                           \\\n    static auto test_callable(...) -> NotCallable;                             \\\n                                                                               \\\n   public:                                                                     \\\n    static constexpr bool value =                                              \\\n        (std::is_same_v<ReturnType, AnyReturnType##METHOD_NAME> and            \\\n         not std::is_same_v<decltype(test_callable<TT, TArgs...>(0)),          \\\n                            NotCallable>) or                                   \\\n        std::is_same_v<decltype(test_callable<TT, TArgs...>(0)), ReturnType>;  \\\n    using type = std::integral_constant<bool, value>;                          \\\n  };                                                                           \\\n  template <typename ReturnType, typename T, typename... Args>                 \\\n  using is_##METHOD_NAME##_callable_r_t =                                      \\\n      typename is_##METHOD_NAME##_callable_r<ReturnType, T, Args...>::type;    \\\n  template <typename TT, typename... TArgs>                                    \\\n  using is_##METHOD_NAME##_callable =                                          \\\n      is_##METHOD_NAME##_callable_r<AnyReturnType##METHOD_NAME, TT, TArgs...>; \\\n  template <typename TT, typename... TArgs>                                    \\\n  using is_##METHOD_NAME##_callable_t =                                        \\\n      is_##METHOD_NAME##_callable_r_t<AnyReturnType##METHOD_NAME, TT,          \\\n                                      TArgs...>;\n\n// Separate macros to avoid compiler warnings about unused variables\n#define CREATE_IS_CALLABLE_R_V(METHOD_NAME)                    \\\n  template <typename ReturnType, typename T, typename... Args> \\\n  constexpr bool is_##METHOD_NAME##_callable_r_v =             \\\n      is_##METHOD_NAME##_callable_r<ReturnType, T, Args...>::value;\n#define CREATE_IS_CALLABLE_V(METHOD_NAME)        \\\n  template <typename T, typename... Args>        \\\n  constexpr bool is_##METHOD_NAME##_callable_v = \\\n      is_##METHOD_NAME##_callable<T, Args...>::value;\n/// @}\n"
  },
  {
    "path": "src/Utilities/TypeTraits/FastPointerCast.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n#include <utility>\n\n#include \"Utilities/Requires.hpp\"\n\nnamespace tt {\nnamespace detail {\ntemplate <typename T, typename U, typename = void>\nstruct can_static_cast : std::false_type {};\n\ntemplate <typename T, typename U>\nstruct can_static_cast<T, U,\n                       std::void_t<decltype(static_cast<U>(std::declval<T>()))>>\n    : std::true_type {};\n\ntemplate <typename T, typename U>\nstatic constexpr bool can_static_cast_v = can_static_cast<T, U>::value;\n}  // namespace detail\n\n/*!\n * \\brief Cast `t` which is of type `T*` to a `U`. If a `static_cast<U>(t)` is\n * possible, use that, otherwise use `dynamic_cast<U>(t)`.\n */\ntemplate <typename U, typename T>\nU fast_pointer_cast(T* t) {\n  if constexpr (detail::can_static_cast_v<T*, U>) {\n    return static_cast<U>(t);\n  } else {\n    return dynamic_cast<U>(t);\n  }\n}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/FunctionInfo.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/TMPL.hpp\"\n\nnamespace tt {\n/// \\cond\nnamespace detail {\ntemplate <typename T>\nstruct function_info_impl;\n\ntemplate <typename Ret, typename... Args>\nstruct function_info_impl<Ret(Args...)> {\n  using return_type = Ret;\n  using argument_types = tmpl::list<Args...>;\n  using class_type = void;\n};\n\ntemplate <typename Ret, typename... Args>\nstruct function_info_impl<Ret(Args...) noexcept> {\n  using return_type = Ret;\n  using argument_types = tmpl::list<Args...>;\n  using class_type = void;\n};\n\n#define FUNCTION_INFO_IMPL_FUNCTION_PTR(MODIFIERS, NOEXCEPT_STATUS)        \\\n  template <typename Ret,                                                  \\\n            typename... Args> /* NOLINTNEXTLINE(misc-macro-parentheses) */ \\\n  struct function_info_impl<Ret (*MODIFIERS)(Args...) NOEXCEPT_STATUS> {   \\\n    using return_type = Ret;                                               \\\n    using argument_types = tmpl::list<Args...>;                            \\\n    using class_type = void;                                               \\\n  }\n\nFUNCTION_INFO_IMPL_FUNCTION_PTR(, );\nFUNCTION_INFO_IMPL_FUNCTION_PTR(, noexcept);\nFUNCTION_INFO_IMPL_FUNCTION_PTR(const, );\nFUNCTION_INFO_IMPL_FUNCTION_PTR(const, noexcept);\nFUNCTION_INFO_IMPL_FUNCTION_PTR(volatile, );\nFUNCTION_INFO_IMPL_FUNCTION_PTR(volatile, noexcept);\nFUNCTION_INFO_IMPL_FUNCTION_PTR(const volatile, );\nFUNCTION_INFO_IMPL_FUNCTION_PTR(const volatile, noexcept);\n#undef FUNCTION_INFO_IMPL_FUNCTION_PTR\n\n#define FUNCTION_INFO_IMPL_CLASS(MODIFIERS)                                \\\n  template <typename Ret, typename Class,                                  \\\n            typename... Args> /* NOLINTNEXTLINE(misc-macro-parentheses) */ \\\n  struct function_info_impl<Ret (Class::*)(Args...) MODIFIERS> {           \\\n    using return_type = Ret;                                               \\\n    using argument_types = tmpl::list<Args...>;                            \\\n    using class_type = Class;                                              \\\n  }\n\nFUNCTION_INFO_IMPL_CLASS();\nFUNCTION_INFO_IMPL_CLASS(const);\nFUNCTION_INFO_IMPL_CLASS(noexcept);\nFUNCTION_INFO_IMPL_CLASS(volatile);\nFUNCTION_INFO_IMPL_CLASS(const noexcept);\nFUNCTION_INFO_IMPL_CLASS(const volatile);\nFUNCTION_INFO_IMPL_CLASS(const volatile noexcept);\nFUNCTION_INFO_IMPL_CLASS(volatile noexcept);\n#undef FUNCTION_INFO_IMPL_CLASS\n}  // namespace detail\n/// \\endcond\n\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Returns a struct that contains the return type, argument types, and\n * the class type if the `F` is a non-static member function\n *\n * The return class has member type aliases:\n * - `return_type` The return type of the function\n * - `argument_types` A `tmpl::list` of the arguments types of the function\n * - `class_type` The type of the class if the function is a non-static member\n * function, otherwise `void`\n *\n * \\note For static member variables the class will be `void` because they are\n * effectively free functions.\n */\ntemplate <typename F>\nusing function_info = detail::function_info_impl<F>;\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/GetFundamentalType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <type_traits>\n\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n#include \"Utilities/TypeTraits/IsComplexOfFundamental.hpp\"\n\nnamespace tt {\nnamespace detail {\n// NOLINTBEGIN(clang-diagnostic-unused-const-variable)\nCREATE_HAS_TYPE_ALIAS(ElementType)\nCREATE_HAS_TYPE_ALIAS_V(ElementType)\nCREATE_HAS_TYPE_ALIAS(value_type)\nCREATE_HAS_TYPE_ALIAS_V(value_type)\n// NOLINTEND(clang-diagnostic-unused-const-variable)\n}  // namespace detail\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Extracts the fundamental type for a container\n///\n/// \\details  Designates a type alias `get_fundamental_type::type`\n///  as `T` when `T` itself is an appropriate fundamental type, and the\n/// contained type of a container which specifies a `value_type`.\n///\n/// `get_fundamental_type_t<T>` is provided as a type alias to\n/// `type` from `get_fundamental_type<T>`\n///\n/// \\snippet Test_GetFundamentalType.cpp get_fundamental_type\ntemplate <typename T, typename = std::nullptr_t>\nstruct get_fundamental_type {\n  using type = tmpl::conditional_t<std::is_fundamental_v<T>, T, NoSuchType>;\n};\n\n/// \\cond\n// Specialization for Blaze expressions\ntemplate <typename T>\nstruct get_fundamental_type<T, Requires<detail::has_ElementType_v<T>>> {\n  using type = typename get_fundamental_type<typename T::ElementType>::type;\n};\n// Specialization for containers\ntemplate <typename T>\nstruct get_fundamental_type<T, Requires<detail::has_value_type_v<T> and\n                                        not detail::has_ElementType_v<T>>> {\n  using type = typename get_fundamental_type<typename T::value_type>::type;\n};\n/// \\endcond\n\ntemplate <typename T>\nusing get_fundamental_type_t = typename get_fundamental_type<T>::type;\n/// @}\n\ntemplate <typename T, typename = std::nullptr_t>\nstruct get_complex_or_fundamental_type {\n  using type =\n      std::conditional_t<tt::is_complex_or_fundamental_v<T>, T, NoSuchType>;\n};\n\n/// \\cond\n// Specialization for Blaze expressions\ntemplate <typename T>\nstruct get_complex_or_fundamental_type<T,\n                                       Requires<detail::has_ElementType_v<T>>> {\n  using type =\n      typename get_complex_or_fundamental_type<typename T::ElementType>::type;\n};\n// Specialization for containers\ntemplate <typename T>\nstruct get_complex_or_fundamental_type<\n    T, Requires<detail::has_value_type_v<T> and\n                not detail::has_ElementType_v<T> and\n                not tt::is_complex_or_fundamental_v<T>>> {\n  using type =\n      typename get_complex_or_fundamental_type<typename T::value_type>::type;\n};\n/// \\endcond\n\ntemplate <typename T>\nusing get_complex_or_fundamental_type_t =\n    typename get_complex_or_fundamental_type<T>::type;\n\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/HasEquivalence.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if type `T` has operator== defined.\n///\n/// \\details\n/// Inherits from std::true_type if the type `T` has operator== defined,\n/// otherwise inherits from std::false_type\n///\n/// \\usage\n/// For any type `T`,\n/// \\code\n/// using result = tt::has_equivalence<T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If the type `T` has operator== defined, then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_HasEquivalence.cpp has_equivalence_example\n/// \\see has_inequivalence\n/// \\tparam T the type we want to know if it has operator==\ntemplate <typename T, typename = std::void_t<>>\nstruct has_equivalence : std::false_type {};\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T>\nstruct has_equivalence<\n    T, std::void_t<decltype(std::declval<T>() == std::declval<T>())>>\n    : std::true_type {};\n/// \\endcond\n\n/// \\see has_equivalence\ntemplate <typename T>\nconstexpr bool has_equivalence_v = has_equivalence<T>::value;\n\n/// \\see has_equivalence\ntemplate <typename T>\nusing has_equivalence_t = typename has_equivalence<T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/HasInequivalence.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if type `T` has operator!= defined.\n///\n/// \\details\n/// Inherits from std::true_type if the type `T` has operator!= defined,\n/// otherwise inherits from std::false_type\n///\n/// \\usage\n/// For any type `T`,\n/// \\code\n/// using result = tt::has_inequivalence<T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If the type `T` has operator!= defined, then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_HasInequivalence.cpp has_inequivalence_example\n/// \\see has_equivalence\n/// \\tparam T the type we want to know if it has operator!=\ntemplate <typename T, typename U = void>\nstruct has_inequivalence : std::false_type {};\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T>\nstruct has_inequivalence<\n    T, std::void_t<decltype(std::declval<T>() != std::declval<T>())>>\n    : std::true_type {};\n/// \\endcond\n\n/// \\see has_inequivalence\ntemplate <typename T>\nconstexpr bool has_inequivalence_v = has_inequivalence<T>::value;\n\n/// \\see has_inequivalence\ntemplate <typename T>\nusing has_inequivalence_t = typename has_inequivalence<T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsA.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\nnamespace tt {\nnamespace detail {\ntemplate <template <typename...> class U, typename T>\nstruct is_a : std::false_type {};\n\ntemplate <template <typename...> class U, typename... Args>\nstruct is_a<U, U<Args...>> : std::true_type {};\n\ntemplate <template <typename...> class U>\nstruct is_a_wrapper;\n\ntemplate <typename U, typename T>\nstruct wrapped_is_a;\n\ntemplate <template <typename...> class U, typename T>\nstruct wrapped_is_a<is_a_wrapper<U>, T> : detail::is_a<U, T> {};\n}  // namespace detail\n\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if type `T` is a template specialization of `U`\n///\n/// \\requires `U` is a class template\n/// \\effects If `T` is a template specialization of `U`, then inherits from\n/// std::true_type, otherwise inherits from std::false_type\n///\n/// \\usage\n/// For any type `T` and class template `U`\n/// \\code\n/// using result = tt::is_a<U, T>;\n/// \\endcode\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If the type `T` is a template specialization of the type `U`, then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_IsA.cpp is_a_example\n/// \\see is_std_array\n/// \\tparam T type to check\n/// \\tparam U the type that T might be a template specialization of\ntemplate <template <typename...> class U, typename T>\nusing is_a = detail::wrapped_is_a<detail::is_a_wrapper<U>, T>;\n\n/// \\see is_a\ntemplate <template <typename...> class U, typename T>\nconstexpr bool is_a_v = is_a<U, T>::value;\n\n/// \\see is_a\ntemplate <template <typename...> class U, typename T>\nusing is_a_t = typename is_a<U, T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsCallable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if a type `T` is callable, i.e. `T(Args...)` is evaluable.\n///\n/// \\details\n/// Inherits from std::true_type if `TT` has the call operator, operator()\n/// defined with arguments `TArgs...`, otherwise inherits from std::false_type.\n///\n/// \\usage\n/// For any type `TT` and types `TArgs_i`,\n/// \\code\n/// using result = tt::is_callable<TT, TArgs_0, TArgs_1, TArgs_2>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If the type `TT` defines operator() with arguments `TArgs...`, then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_IsCallable.cpp is_callable_example\n/// \\see std::is_callable\n/// \\tparam TT the class to check\n/// \\tparam TArgs the args passed to operator()\ntemplate <typename TT, typename... TArgs>\nclass is_callable {\n  // The reason we have private before public here is that we have static member\n  // functions and since this is meant to be a super lightweight helper class\n  // it's better to break convention than increase code size.\n private:\n  /// \\cond\n  // We pass an int here to disambiguate the two possible templates and have the\n  // compiler prefer the first one. If it cannot be used because there's no\n  // call operator, then it uses the second one.\n  template <typename T, typename... Args>\n  static auto test_callable(int)\n      -> decltype(std::declval<T>()(std::declval<Args>()...), std::true_type());\n\n  template <typename, typename...>\n  static auto test_callable(...) -> std::false_type;\n  /// \\endcond\n\n public:\n  /// `true` if callable, `false` otherwise\n  static constexpr bool value = decltype(test_callable<TT, TArgs...>(0))::value;\n  /// `std::true_type` if callable, `std::false_type` otherwise\n  using type = std::integral_constant<bool, value>;\n};\n/// \\see is_callable\ntemplate <typename T, typename... Args>\nconstexpr bool is_callable_v = is_callable<T, Args...>::value;\n\n/// \\see is_callable\ntemplate <typename T, typename... Args>\nusing is_callable_t = typename is_callable<T, Args...>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsComplexOfFundamental.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Determines if a type `T` is a `std::complex` of a fundamental type,\n/// is a `std::true_type` if so, and otherwise is a `std::false_type`\n///\n/// \\snippet Test_IsComplexOfFundamental.cpp is_complex_of_fundamental\ntemplate <typename T, typename = std::bool_constant<true>>\nstruct is_complex_of_fundamental : std::false_type {};\n\n/// \\cond\n// this version will only pattern match if `T` is both complex and a fundamental\n// type\ntemplate <typename T>\nstruct is_complex_of_fundamental<std::complex<T>,\n                                 std::bool_constant<std::is_fundamental_v<T>>>\n    : std::true_type {};\n/// \\endcond\n/// @}\n\ntemplate <typename T>\nconstexpr bool is_complex_of_fundamental_v =\n    is_complex_of_fundamental<T>::value;\n\n/// \\ingroup TypeTraitsGroup\n/// \\brief Evaluates to `true` if type `T` is a `std::complex` of a fundamental\n/// type or if `T` is a fundamental type.\ntemplate <typename T>\nconstexpr bool is_complex_or_fundamental_v =\n    is_complex_of_fundamental_v<T> or std::is_fundamental_v<T>;\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsInteger.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Check if `I` is an integer type (non-bool, non-character), unlike\n * std::is_integral\n *\n * \\details\n * Inherits from `std::true_type` if `I` is a `short`, `unsigned short`,\n * `int`, `unsigned int`, `long`, `unsigned long`, `long long`, or\n * `unsigned long long`, otherwise inherits from `std::false_type`.\n *\n * \\usage\n * For any type `I`,\n * \\code\n * using result = tt::is_integer<I>;\n * \\endcode\n * \\metareturns\n * std::bool_constant\n *\n * \\example\n * \\snippet Test_IsInteger.cpp is_integer_example\n * \\see std::is_integral std::is_arithmetic std::is_floating_point\n */\ntemplate <typename I>\nstruct is_integer : std::false_type {};\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <>\nstruct is_integer<short> : std::true_type {};\n\ntemplate <>\nstruct is_integer<unsigned short> : std::true_type {};\n\ntemplate <>\nstruct is_integer<int> : std::true_type {};\n\ntemplate <>\nstruct is_integer<unsigned int> : std::true_type {};\n\ntemplate <>\nstruct is_integer<long> : std::true_type {};\n\ntemplate <>\nstruct is_integer<unsigned long> : std::true_type {};\n\ntemplate <>\nstruct is_integer<long long> : std::true_type {};\n\ntemplate <>\nstruct is_integer<unsigned long long> : std::true_type {};\n/// \\endcond\n\n/// \\see is_integer\ntemplate <typename T>\nconstexpr bool is_integer_v = is_integer<T>::value;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsIterable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if type T has a begin() and end() function\n///\n/// \\details\n/// Given a type `T` inherits from std::true_type if `T` has member functions\n/// `begin()` and `end()`, otherwise inherits from std::false_type\n///\n/// \\usage\n/// For any type `T`\n/// \\code\n/// using result = tt::is_iterable<T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If `T` has member function `begin()` and `end()` then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_IsIterable.cpp is_iterable_example\n/// \\tparam T the type to check\ntemplate <typename T, typename = std::void_t<>>\nstruct is_iterable : std::false_type {};\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T>\nstruct is_iterable<T, std::void_t<decltype(std::declval<T>().begin(),\n                                           std::declval<T>().end())>>\n    : std::true_type {};\n/// \\endcond\n\n/// \\see is_iterable\ntemplate <typename T>\nconstexpr bool is_iterable_v = is_iterable<T>::value;\n\n/// \\see is_iterable\ntemplate <typename T>\nusing is_iterable_t = typename is_iterable<T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsMaplike.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits/IsIterable.hpp\"\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if type `T` is like a std::map or std::unordored_map\n///\n/// \\details\n/// Inherits from std::true_type if the type `T` has a type alias `key_type`,\n/// type alias `mapped_type`, and `operator[](const typename T::key_type&)`\n/// defined, otherwise inherits from std::false_type\n///\n/// \\usage\n/// For any type `T`,\n/// \\code\n/// using result = tt::is_maplike<T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If the type `T` has a type alias `key_type`,\n/// type alias `mapped_type`, and `operator[](const typename T::key_type&)`\n/// defined, then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_IsMaplike.cpp is_maplike_example\n/// \\see std::map std::unordered_map is_a\n/// \\tparam T the type to check\ntemplate <typename T, typename = std::void_t<>>\nstruct is_maplike : std::false_type {};\n\n/// \\cond\ntemplate <typename T>\nstruct is_maplike<T, std::void_t<typename T::key_type, typename T::mapped_type,\n                                 decltype(std::declval<T&>()[std::declval<\n                                     const typename T::key_type&>()]),\n                                 Requires<tt::is_iterable_v<T>>>>\n    : std::true_type {};\n/// \\endcond\n\n/// \\see is_maplike\ntemplate <typename T>\nconstexpr bool is_maplike_v = is_maplike<T>::value;\n\n/// \\see is_maplike\ntemplate <typename T>\nusing is_maplike_t = typename is_maplike<T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsStdArray.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if type T is a std::array\n///\n/// \\details\n/// Given a type `T` derives from std::true_type if `T` is a std::array and from\n/// std::false_type if `T` is not a std::array.\n///\n/// \\usage\n/// For any type `T`\n/// \\code\n/// using result = tt::is_std_array<T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If `T` is a std::array then\n/// \\code\n/// typename result::type = std::bool_constant<true>;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::bool_constant<false>;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_IsStdArray.cpp is_std_array_example\n/// \\see is_a is_std_array_of_size\n/// \\tparam T the type to check\ntemplate <typename T>\nstruct is_std_array : std::false_type {};\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T, size_t N>\nstruct is_std_array<std::array<T, N>> : std::true_type {};\n/// \\endcond\n\n/// \\see is_std_array\ntemplate <typename T>\nconstexpr bool is_std_array_v = is_std_array<T>::value;\n\n/// \\see is_std_array\ntemplate <typename T>\nusing is_std_array_t = typename is_std_array<T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsStdArrayOfSize.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <type_traits>\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if type T is a std::array of a given size\n///\n/// \\details\n/// Given a size_t `N` and type `T` derives from std::true_type if `T`\n/// is a std::array of size `N` and from std::false_type otherwise.\n///\n/// \\usage\n/// For any type `T`\n/// \\code\n/// using result = tt::is_std_array_of_size<N, T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If `T` is a std::array of size `N` then\n/// \\code\n/// typename result::type = std::bool_constant<true>;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::bool_constant<false>;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_IsStdArrayOfSize.cpp is_std_array_of_size_example\n/// \\see is_std_array\n/// \\tparam T the type to check\ntemplate <size_t N, typename T>\nstruct is_std_array_of_size : std::false_type {};\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <size_t N, typename T>\nstruct is_std_array_of_size<N, std::array<T, N>> : std::true_type {};\n/// \\endcond\n\n/// \\see is_std_array_of_size\ntemplate <size_t N, typename T>\nconstexpr bool is_std_array_of_size_v = is_std_array_of_size<N, T>::value;\n\n/// \\see is_std_array_of_size\ntemplate <size_t N, typename T>\nusing is_std_array_of_size_t = typename is_std_array_of_size<N, T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/IsStreamable.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/StlStreamDeclarations.hpp\"\n\nnamespace tt {\n/// @{\n/// \\ingroup TypeTraitsGroup\n/// \\brief Check if type `T` has operator<<(`S`, `T`) defined.\n///\n/// \\details\n/// Inherits from std::true_type if the type `T` has operator<<(`S`, `T`)\n/// defined for a stream `S`, otherwise inherits from std::false_type\n///\n/// \\usage\n/// For any type `T` and stream type `S`,\n/// \\code\n/// using result = tt::is_streamable<S, T>;\n/// \\endcode\n///\n/// \\metareturns\n/// std::bool_constant\n///\n/// \\semantics\n/// If the type `T` has operator<<(`S`, `T`) defined for stream `S`, then\n/// \\code\n/// typename result::type = std::true_type;\n/// \\endcode\n/// otherwise\n/// \\code\n/// typename result::type = std::false_type;\n/// \\endcode\n///\n/// \\example\n/// \\snippet Test_IsStreamable.cpp is_streamable_example\n/// \\see std::cout std::ifstream std::sstream std::ostream\n/// \\tparam S the stream type, e.g. std::stringstream or std::ostream\n/// \\tparam T the type we want to know if it has operator<<\ntemplate <typename S, typename T, typename = std::void_t<>>\nstruct is_streamable : std::false_type {};\n\n/// \\cond\ntemplate <typename S, typename T>\nstruct is_streamable<\n    S, T,\n    std::void_t<decltype(std::declval<std::add_lvalue_reference_t<S>>()\n                         << std::declval<T>()),\n                Requires<not std::is_same<S, T>::value>>> : std::true_type {};\n/// \\endcond\n\n/// \\see is_streamable\ntemplate <typename S, typename T>\nconstexpr bool is_streamable_v = is_streamable<S, T>::value;\n\n/// \\see is_streamable\ntemplate <typename S, typename T>\nusing is_streamable_t = typename is_streamable<S, T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/RemoveReferenceWrapper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <functional>\n\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace tt {\n/// @{\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Gets the underlying type if the type is a std::reference_wrapper,\n * otherwise returns the type itself\n *\n * \\usage\n * For any type `I`,\n * \\code\n * using result = tt::remove_reference_wrapper<I>;\n * \\endcode\n * \\metareturns\n * either `I::type` if `I` is a std::reference_wrapper, else returns I\n *\n * \\example\n * \\snippet Test_RemoveReferenceWrapper.cpp remove_reference_wrapper_example\n * \\see std::reference_wrapper\n */\ntemplate <typename T>\nstruct remove_reference_wrapper {\n  using type = T;\n};\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T>\nstruct remove_reference_wrapper<std::reference_wrapper<T>> {\n  using type = T;\n};\n\ntemplate <typename T>\nstruct remove_reference_wrapper<const std::reference_wrapper<T>> {\n  using type = T;\n};\n\ntemplate <typename T>\nstruct remove_reference_wrapper<volatile std::reference_wrapper<T>> {\n  using type = T;\n};\n\ntemplate <typename T>\nstruct remove_reference_wrapper<const volatile std::reference_wrapper<T>> {\n  using type = T;\n};\n/// \\endcond\n\ntemplate <typename T>\nusing remove_reference_wrapper_t = typename remove_reference_wrapper<T>::type;\n/// @}\n\n/// @{\n/*!\n * \\ingroup TypeTraitsGroup\n * \\brief Removes std::reference_wrapper, references, and cv qualifiers.\n *\n * \\example\n * \\snippet Test_RemoveReferenceWrapper.cpp remove_cvref_wrap\n * \\see std::reference_wrapper remove_reference_wrapper std::remove_cvref\n */\ntemplate <typename T>\nstruct remove_cvref_wrap {\n  using type = cpp20::remove_cvref_t<tt::remove_reference_wrapper_t<T>>;\n};\n\ntemplate <typename T>\nusing remove_cvref_wrap_t = typename remove_cvref_wrap<T>::type;\n/// @}\n}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits/TypeTraits.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\ingroup TypeTraitsGroup\n/// A collection of useful type traits\nnamespace tt {}  // namespace tt\n"
  },
  {
    "path": "src/Utilities/TypeTraits.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <type_traits>\n\n/// \\ingroup TypeTraitsGroup\n/// C++ STL code present in C++20\nnamespace cpp20 {\n/// \\ingroup TypeTraitsGroup\ntemplate <class T>\nstruct remove_cvref {\n  // clang-tidy use using instead of typedef\n  typedef std::remove_cv_t<std::remove_reference_t<T>> type;  // NOLINT\n};\n\n/// \\ingroup TypeTraitsGroup\ntemplate <class T>\nusing remove_cvref_t = typename remove_cvref<T>::type;\n}  // namespace cpp20\n"
  },
  {
    "path": "src/Utilities/UtcTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/UtcTime.hpp\"\n\n#include <sstream>\n\n#include <boost/date_time/posix_time/posix_time.hpp>\n#include <boost/date_time/posix_time/posix_time_io.hpp>\n\nstd::string utc_time() {\n  static const std::string datetime_format = \"%Y-%m-%d %H:%M:%S UTC\";\n  // Use this needlessly complicated implementation with Boost because the\n  // needed features from C++20 aren't available yet in all compilers.\n  const auto now_utc = boost::posix_time::second_clock::universal_time();\n  auto* time_facet = new boost::posix_time::time_facet(datetime_format.c_str());\n  std::ostringstream oss;\n  oss.imbue(std::locale(std::locale::classic(), time_facet));\n  oss << now_utc;\n  return oss.str();\n}\n"
  },
  {
    "path": "src/Utilities/UtcTime.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n/*!\n * \\brief Get the current date and time in UTC.\n *\n * The date and time are formatted as \"YYYY-MM-DD HH:MM:SS UTC\".\n */\nstd::string utc_time();\n"
  },
  {
    "path": "src/Utilities/VectorAlgebra.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <blaze/math/traits/MultTrait.h>\n\n#include \"Utilities/Gsl.hpp\"\n\n/// @{\n/// \\ingroup UtilitiesGroup\n/// \\brief Computes the outer product between two vectors.\n/// \\details For vectors \\f$A\\f$ and \\f$B\\f$, the resulting outer product is\n/// \\f$\\{A_1 B_1,\\, A_2 B_1\\, \\dots\\, A_N B_1,\\, A_1 B_2\\, \\dots\\, A_N B_M\\}\\f$.\n/// This is useful for generating separable volume data from its constituent\n/// inputs.\ntemplate <typename LhsVectorType, typename RhsVectorType,\n          typename ResultVectorType =\n              typename blaze::MultTrait<LhsVectorType, RhsVectorType>::Type>\nvoid outer_product(const gsl::not_null<ResultVectorType*> result,\n                   const LhsVectorType& lhs, const RhsVectorType& rhs) {\n  result->destructive_resize(lhs.size() * rhs.size());\n  for (size_t i = 0; i < rhs.size(); ++i) {\n    ResultVectorType view{result->data() + i * lhs.size(), lhs.size()};\n    view = rhs[i] * lhs;\n  }\n}\n\ntemplate <typename LhsVectorType, typename RhsVectorType,\n          typename ResultVectorType =\n              typename blaze::MultTrait<LhsVectorType, RhsVectorType>::Type>\nResultVectorType outer_product(const LhsVectorType& lhs,\n                               const RhsVectorType& rhs) {\n  auto result = ResultVectorType{lhs.size() * rhs.size()};\n  outer_product(make_not_null(&result), lhs, rhs);\n  return result;\n}\n/// @}\n\n/// @{\n/// \\ingroup UtilitiesGroup\n/// \\brief Creates or fills a vector with data from `to_repeat` copied\n/// `times_to_repeat`  times in sequence.\n///\n/// \\details This can be useful for generating data that consists of the same\n/// block of values duplicated a number of times. For instance, this can be used\n/// to create a vector representing three-dimensional volume data from a\n/// corresponding two-dimensional vector data, if the two-dimensional data\n/// corresponds to the two fastest-varying directions of the desired\n/// three-dimensional representation. The result would then be uniform in the\n/// slowest-varying direction of the three dimensional grid.\ntemplate <typename VectorType>\nvoid fill_with_n_copies(\n    // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n    const gsl::not_null<VectorType*> result, const VectorType& to_copy,\n    // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n    const size_t times_to_copy) {\n  result->destructive_resize(to_copy.size() * times_to_copy);\n  for (size_t i = 0; i < times_to_copy; ++i) {\n    VectorType view{result->data() + i * to_copy.size(), to_copy.size()};\n    view = to_copy;\n  }\n}\n\n// clang-tidy incorrectly believes this to be a forward-declaration\ntemplate <typename VectorType>\nVectorType create_vector_of_n_copies(const VectorType& to_copy,\n                                     const size_t times_to_copy) {  // NOLINT\n  auto result = VectorType{to_copy.size() * times_to_copy};\n  fill_with_n_copies(make_not_null(&result), to_copy, times_to_copy);\n  return result;\n}\n/// @}\n"
  },
  {
    "path": "src/Utilities/WrapText.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/WrapText.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nstd::string wrap_text(std::string str, const size_t line_length,\n                      const std::string& indentation) {\n  str = indentation + str;\n  ASSERT(indentation.size() < line_length,\n         \"The indentation must be shorter than the line length. Indentation \"\n         \"length is: \"\n             << indentation.size() << \" while line length is: \" << line_length);\n\n  // Insert indentation at newlines in the string first to ensure all new lines\n  // are indented correctly.\n  if (not indentation.empty()) {\n    size_t newline_location = str.find('\\n');\n    while (newline_location != std::string::npos) {\n      str.insert(newline_location + 1, indentation);\n      newline_location = str.find('\\n', newline_location + 1);\n    }\n  }\n\n  // Wrap the string to the set length of characters\n  for (size_t i = 0; i + line_length < str.size();) {\n    // Find the last newline, and split there if it is within the next\n    // line_length characters.\n    if (const size_t last_newline = str.rfind('\\n', i + line_length);\n        last_newline != std::string::npos and last_newline > i) {\n      i = last_newline + 1;\n    } else if (const size_t last_space = str.rfind(' ', i + line_length);\n               last_space <= i + indentation.size() or\n               last_space == std::string::npos or\n               (i == 0 and str.substr(i, last_space + 1) ==\n                               std::string(last_space + 1, ' '))) {\n      // The last 'or' condition of the `if` above handles the edge case where\n      // the first space in the first line is precedes only spaces.\n      const size_t insert_location = i + line_length - 1;\n      str.insert(insert_location, \"-\\n\" + indentation);\n      i = insert_location + 2;\n    } else {\n      str.at(last_space) = '\\n';\n      str.insert(last_space + 1, indentation);\n      i = last_space + 1;\n    }\n  }\n  return str;\n}\n"
  },
  {
    "path": "src/Utilities/WrapText.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n/// \\ingroup UtilitiesGroup\n/// \\brief Wrap the string `str` so that it is no longer than `line_length` and\n/// indent each new line with `indentation`. The first line is also indented.\n///\n/// Single words longer than `line_length` are hyphenated.\nstd::string wrap_text(std::string str, size_t line_length,\n                      const std::string& indentation = \"\");\n"
  },
  {
    "path": "src/Visualization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "src/Visualization/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyVisualization\")\n\nspectre_python_add_module(\n  Visualization\n  PYTHON_FILES\n  __init__.py\n  ElementIdToParaview.py\n  GenerateTetrahedralConnectivity.py\n  GenerateXdmf.py\n  Plot.py\n  PlotAlongLine.py\n  PlotCce.py\n  PlotControlSystem.py\n  PlotDatFile.py\n  PlotEccentricityControl.py\n  PlotEllipticConvergence.py\n  PlotMemoryMonitors.py\n  PlotPowerMonitors.py\n  PlotSizeControl.py\n  PlotSlice.py\n  PlotTrajectories.py\n  plots.mplstyle\n  ReadInputFile.py\n  Render1D.py\n)\n\nadd_subdirectory(Render3D)\n"
  },
  {
    "path": "src/Visualization/Python/ElementIdToParaview.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport re\n\nimport click\n\nfrom spectre.Domain import ElementId\n\n\ndef _detect_dimension(element_id_str: str) -> int:\n    \"\"\"Detect the spatial dimension from an element ID string.\n\n    Counts the number of 'LxIy' segments to determine the dimension.\n    \"\"\"\n    segments = re.findall(r\"L\\d+I\\d+\", element_id_str)\n    dim = len(segments)\n    if dim < 1 or dim > 3:\n        raise click.ClickException(\n            f\"Cannot determine dimension from '{element_id_str}'. \"\n            f\"Expected 1-3 'LxIy' segments, found {dim}.\"\n        )\n    return dim\n\n\ndef element_id_to_short_id(element_id_str: str) -> int:\n    \"\"\"Convert an element ID string to a compact short ID.\n\n    Auto-detects the dimension from the number of 'LxIy' segments in the\n    string.\n    \"\"\"\n    dim = _detect_dimension(element_id_str)\n    return ElementId[dim](element_id_str).to_short_id()\n\n\n@click.command(name=\"element-id-to-paraview\")\n@click.argument(\"element_ids\", nargs=-1, required=True)\ndef element_id_to_paraview_command(element_ids):\n    \"\"\"Convert SpECTRE element ID strings to compact numeric IDs.\n\n    These short IDs strip block_id, grid_index, and direction bits, keeping\n    only the segment ID portion. They are useful for filtering or selecting\n    elements in ParaView.\n\n    Example usage:\n\n        spectre element-id-to-paraview \"[B2,(L2I3,L1I0,L1I1)]\"\n    \"\"\"\n    dims = {eid: _detect_dimension(eid) for eid in element_ids}\n    unique_dims = set(dims.values())\n    if len(unique_dims) > 1:\n        mismatches = \"\\n\".join(f\"  {eid} -> {d}D\" for eid, d in dims.items())\n        raise click.ClickException(\n            \"All element IDs must have the same dimension, but got \"\n            f\"dimensions {sorted(unique_dims)}:\\n{mismatches}\"\n        )\n    for eid in element_ids:\n        click.echo(element_id_to_short_id(eid))\n"
  },
  {
    "path": "src/Visualization/Python/GenerateTetrahedralConnectivity.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom typing import Optional\n\nimport click\nimport h5py\nimport numpy as np\nimport rich\nimport scipy.spatial as spatial\n\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.support.Logging import configure_logging\nfrom spectre.Visualization.GenerateXdmf import available_subfiles\n\nlogger = logging.getLogger(__name__)\n\n\ndef generate_tetrahedral_connectivity(\n    h5file,\n    subfile_name: str,\n    relative_paths: bool = True,\n    start_time: Optional[float] = None,\n    stop_time: Optional[float] = None,\n    stride: int = 1,\n    coordinates: str = \"InertialCoordinates\",\n    force: bool = False,\n):\n    \"\"\"Generate tetrahedral connectivity using scipy.spatial.Delaunay\n\n    Given the coordinates, this generates a tetrahedral connectivity that can\n    be read in by ParaView or other visualization software (e.g. VisIt or yt).\n    It uses the scipy.spatial.Delaunay class to generate the connectivity, which\n    uses the Qhull library (github.com/qhull/qhull/), which uses the quickhull\n    algorithm and scales as O(n^2) in the worst case. Thus, generating the\n    connectivity can take several minutes per temporal ID. Unfortunately,\n    depending on the particular grid point distribution, generating the\n    connectivity may even fail in qhull, which is unfortunately difficult\n    to fix.\n\n    You should combine the volume HDF5 files into one before running this in\n    order to get a fully connected domain.\n\n    Note that while ParaView has a Delaunay3D filter, it is much slower than\n    qhull, needs to be rerun every time ParaView is opened while the qhull\n    output is stored, and the Delaunay3D filter sometimes produces nonsense\n    connectivity that is very difficult to debug and fix.\n\n    After the tetrahedral connectivity has been written you can run\n    'generate-xdmf' with the flag '--use-tetrahedral-connectivity'. ParaView\n    volume renderings are sometimes nicer with tetrahedral connectivity and this\n    can be used to fill gaps between finite-difference or Gauss elements.\n\n    If this algorithm is too slow, one possible improvement is to apply\n    qhull to each block of the domain and then connect the blocks to each\n    other separately. This keeps the number of grid points lower for each\n    invocation of qhull, which likely reduces total runtime and may also\n    reduce or eliminate failure cases. In the ideal case we would apply\n    qhull to each element and then connect elements that are using FD or\n    Gauss points to their neighbors.\n\n    \\f\n    Arguments:\n      h5file: The HDF5 file on which to run.\n      subfile_name: Volume data subfile in the H5 files.\n      start_time: Optional. The earliest time at which to start visualizing. The\n        start-time value is included.\n      stop_time: Optional. The time at which to stop visualizing. The stop-time\n        value is not included.\n      stride: Optional. View only every stride'th time step.\n      coordinates: Optional. Name of coordinates dataset. Default:\n        \"InertialCoordinates\".\n      force: Optional. Overwrite the existing tetrahedral connectivity.\n        Default: False\n    \"\"\"\n    filename = h5file\n    h5file = h5py.File(filename, \"a\")\n\n    if not subfile_name:\n        subfiles = available_subfiles(h5file, extension=\".vol\")\n        if len(subfiles) == 1:\n            subfile_name = subfiles[0]\n            logger.info(\n                f\"Selected subfile {subfile_name} (the only available one).\"\n            )\n        else:\n            raise RequiredChoiceError(\n                (\n                    \"Specify '--subfile-name' / '-d' to select a\"\n                    \" subfile containing volume data.\"\n                ),\n                choices=subfiles,\n            )\n\n    if not subfile_name.endswith(\".vol\"):\n        subfile_name += \".vol\"\n\n    # Open subfile\n    try:\n        vol_subfile = h5file[subfile_name]\n    except KeyError as err:\n        raise ValueError(\n            f\"Could not open subfile name '{subfile_name}' in\"\n            f\" '{filename}'. Available subfiles: \"\n            + str(available_subfiles(h5file, extension=\".vol\"))\n        ) from err\n\n    # Sort timesteps by time\n    temporal_ids_and_values = sorted(\n        [\n            (key, vol_subfile[key].attrs[\"observation_value\"])\n            for key in vol_subfile.keys()\n        ],\n        key=lambda key_and_time: key_and_time[1],\n    )\n\n    # Stride through timesteps\n    for temporal_id, time in temporal_ids_and_values[::stride]:\n        # Filter by start and end time\n        if start_time is not None and time < start_time:\n            continue\n        if stop_time is not None and time > stop_time:\n            break\n\n        data_at_id = vol_subfile[temporal_id]\n\n        if force and (\"tetrahedral_connectivity\" in data_at_id):\n            del data_at_id[\"tetrahedral_connectivity\"]\n\n        x_coords = np.asarray(data_at_id[coordinates + \"_x\"])\n        y_coords = np.asarray(data_at_id[coordinates + \"_y\"])\n        if (coordinates + \"_z\") in data_at_id:\n            z_coords = np.asarray(data_at_id[coordinates + \"_z\"])\n            coords = np.column_stack((x_coords, y_coords, z_coords))\n        else:\n            coords = np.column_stack((x_coords, y_coords))\n\n        logger.info(\n            \"Generating tetrahedral connectivity at\"\n            f\" {temporal_id}/{time}. This may take a few minutes and\"\n            \" may even fail depending on the grid structure.\"\n        )\n        delaunay = spatial.Delaunay(coords)\n        data_at_id.create_dataset(\n            \"tetrahedral_connectivity\",\n            data=delaunay.simplices.flatten(),\n        )\n\n    h5file.close()\n\n\n@click.command(\n    name=\"generate-tetrahedral-connectivity\",\n    help=generate_tetrahedral_connectivity.__doc__,\n)\n@click.argument(\n    \"h5file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=1,\n    required=True,\n)\n@click.option(\n    \"--subfile-name\",\n    \"-d\",\n    help=(\n        \"Name of the volume data subfile in the H5 files. A '.vol' extension is\"\n        \" added if needed. If unspecified, and the first H5 file contains only\"\n        \" a single '.vol' subfile, choose that. Otherwise, list all '.vol'\"\n        \" subfiles and exit.\"\n    ),\n)\n@click.option(\n    \"--stride\", default=1, type=int, help=\"View only every stride'th time step\"\n)\n@click.option(\n    \"--start-time\",\n    type=float,\n    help=(\n        \"The earliest time at which to start visualizing. The start-time \"\n        \"value is included.\"\n    ),\n)\n@click.option(\n    \"--stop-time\",\n    type=float,\n    help=(\n        \"The time at which to stop visualizing. The stop-time value is \"\n        \"included.\"\n    ),\n)\n@click.option(\n    \"--coordinates\",\n    default=\"InertialCoordinates\",\n    show_default=True,\n    help=\"The coordinates to use for visualization\",\n)\n@click.option(\n    \"--force\",\n    is_flag=True,\n    default=False,\n    help=\"Overwrite existing tetrahedral connectivity.\",\n)\ndef generate_tetrahedral_connectivity_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    generate_tetrahedral_connectivity(**kwargs)\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.INFO)\n    generate_tetrahedral_connectivity_command(\n        help_option_names=[\"-h\", \"--help\"]\n    )\n"
  },
  {
    "path": "src/Visualization/Python/GenerateXdmf.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\"\"\"Tools to generate XDMF files that ParaView and VisIt can read.\n\nThe XDMF file format is documented here:\nhttps://xdmf.org/index.php/XDMF_Model_and_Format\n\"\"\"\n\nimport logging\nimport os\nimport sys\nimport xml.etree.ElementTree as ET\nfrom typing import List, Optional, Sequence, Tuple\n\nimport click\nimport h5py\nimport numpy as np\nimport rich\n\nfrom spectre.IO.H5.ReadH5 import available_subfiles\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.support.Logging import configure_logging\n\nlogger = logging.getLogger(__name__)\n\n\ndef _list_tensor_components(observation):\n    components = list(observation.keys())\n    components.remove(\"connectivity\")\n    if \"pole_connectivity\" in components:\n        components.remove(\"pole_connectivity\")\n    if \"tetrahedral_connectivity\" in components:\n        components.remove(\"tetrahedral_connectivity\")\n    components.remove(\"total_extents\")\n    components.remove(\"grid_names\")\n    components.remove(\"bases\")\n    components.remove(\"quadratures\")\n    if \"domain\" in components:\n        components.remove(\"domain\")\n    if \"functions_of_time\" in components:\n        components.remove(\"functions_of_time\")\n    # New mixed-topology datasets — handled as cell-centered attributes\n    if \"ElementId\" in components:\n        components.remove(\"ElementId\")\n    if \"BlockId\" in components:\n        components.remove(\"BlockId\")\n    return components\n\n\ndef _xmf_dtype(dtype: type):\n    assert dtype in [\n        np.dtype(\"float32\"),\n        np.dtype(\"float64\"),\n    ], f\"Data type must be either a 32-bit or 64-bit float but got {dtype}.\"\n    return \"Double\" if dtype == np.dtype(\"float64\") else \"Float\"\n\n\ndef _xmf_topology(\n    observation, topology_type: str, connectivity_name: str, grid_path: str\n) -> ET.Element:\n    num_vertices = {\n        \"Hexahedron\": 8,\n        \"Quadrilateral\": 4,\n        \"Tetrahedron\": 4,\n        \"Triangle\": 3,\n        \"Wedge\": 6,\n    }[topology_type]\n    num_cells = len(observation[connectivity_name]) // num_vertices\n    xmf_topology = ET.Element(\n        \"Topology\",\n        TopologyType=topology_type,\n        NumberOfElements=str(num_cells),\n    )\n    xmf_data_item = ET.SubElement(\n        xmf_topology,\n        \"DataItem\",\n        Dimensions=f\"{num_cells} {num_vertices}\",\n        NumberType=\"Int\",\n        Format=\"HDF5\",\n    )\n    xmf_data_item.text = os.path.join(grid_path, connectivity_name)\n    return xmf_topology\n\n\ndef _count_cells_in_mixed_connectivity(connectivity):\n    \"\"\"Count cells in an XDMF mixed-topology connectivity array.\"\"\"\n    # Map from XDMF type integer to number of vertices per cell\n    verts_per_type = {2: 2, 4: 3, 5: 4, 8: 6, 9: 8}\n    number_of_cells = 0\n    i = 0\n    while i < len(connectivity):\n        type_tag = int(connectivity[i])\n        n_verts = verts_per_type.get(type_tag)\n        if n_verts is None:\n            raise ValueError(f\"Unknown XDMF topology type tag: {type_tag}\")\n        i += 1 + n_verts\n        number_of_cells += 1\n    return number_of_cells\n\n\ndef _xmf_mixed_topology(\n    observation, connectivity_name: str, grid_path: str\n) -> ET.Element:\n    \"\"\"Build an XDMF Mixed topology element for a new-format connectivity.\"\"\"\n    connectivity = observation[connectivity_name][:]\n    number_of_cells = _count_cells_in_mixed_connectivity(connectivity)\n    xmf_topology = ET.Element(\n        \"Topology\",\n        TopologyType=\"Mixed\",\n        NumberOfElements=str(number_of_cells),\n    )\n    xmf_data_item = ET.SubElement(\n        xmf_topology,\n        \"DataItem\",\n        Dimensions=str(len(connectivity)),\n        NumberType=\"Int\",\n        Format=\"HDF5\",\n    )\n    xmf_data_item.text = os.path.join(grid_path, connectivity_name)\n    return xmf_topology\n\n\ndef _xmf_cell_attribute(\n    observation, name: str, number_of_cells: int, grid_path: str\n) -> ET.Element:\n    \"\"\"Build an XDMF Attribute element for a cell-centered uint64 dataset.\"\"\"\n    xmf_attribute = ET.Element(\n        \"Attribute\",\n        Name=name,\n        AttributeType=\"Scalar\",\n        Center=\"Cell\",\n    )\n    xmf_data_item = ET.SubElement(\n        xmf_attribute,\n        \"DataItem\",\n        Dimensions=str(number_of_cells),\n        NumberType=\"UInt\",\n        Precision=\"8\",\n        Format=\"HDF5\",\n    )\n    xmf_data_item.text = os.path.join(grid_path, name)\n    return xmf_attribute\n\n\ndef _xmf_geometry(\n    observation, coordinates: str, dim: int, num_points: int, grid_path: str\n) -> ET.Element:\n    # The X_Y_Z and X_Y means that the x, y, and z coordinates are stored in\n    # separate datasets, rather than something like interleaved.\n    xmf_geometry = ET.Element(\n        \"Geometry\", GeometryType=\"X_Y_Z\" if dim == 3 else \"X_Y\"\n    )\n    for xyz in \"xyz\"[:dim]:\n        component_name = coordinates + \"_\" + xyz\n        xmf_data_item = ET.SubElement(\n            xmf_geometry,\n            \"DataItem\",\n            Dimensions=str(num_points),\n            NumberType=_xmf_dtype(observation[component_name].dtype),\n            Precision=\"8\",\n            Format=\"HDF5\",\n        )\n        xmf_data_item.text = os.path.join(grid_path, component_name)\n    return xmf_geometry\n\n\ndef _xmf_scalar(\n    observation, name: str, num_points: int, grid_path: str\n) -> ET.Element:\n    xmf_attribute = ET.Element(\n        \"Attribute\",\n        Name=name,\n        AttributeType=\"Scalar\",\n        Center=\"Node\",\n    )\n    xmf_data_item = ET.SubElement(\n        xmf_attribute,\n        \"DataItem\",\n        Dimensions=str(num_points),\n        NumberType=_xmf_dtype(observation[name].dtype),\n        Precision=\"8\",\n        Format=\"HDF5\",\n    )\n    xmf_data_item.text = os.path.join(grid_path, name)\n    return xmf_attribute\n\n\ndef _xmf_vector(\n    observation, name: str, dim: int, num_points: int, grid_path: str\n) -> ET.Element:\n    # Write a vector using the three components that make up the vector (i.e.\n    # v_x, v_y, v_z)\n    xmf_attribute = ET.Element(\n        \"Attribute\",\n        Name=name,\n        AttributeType=\"Vector\",\n        Center=\"Node\",\n    )\n    xmf_function = ET.SubElement(\n        xmf_attribute,\n        \"DataItem\",\n        Dimensions=f\"{num_points} 3\",\n        ItemType=\"Function\",\n        # In 2d we still need a 3d dataset to have a vector because ParaView\n        # only supports 3d vectors. We deal with this by making the z-component\n        # all zeros.\n        Function=(\"JOIN($0,$1,$2)\" if dim == 3 else \"JOIN($0,$1, 0 * $1)\"),\n    )\n    for xyz in \"xyz\"[:dim]:\n        component_name = name + \"_\" + xyz\n        xmf_data_item = ET.SubElement(\n            xmf_function,\n            \"DataItem\",\n            Dimensions=str(num_points),\n            NumberType=_xmf_dtype(observation[component_name].dtype),\n            Precision=\"8\",\n            Format=\"HDF5\",\n        )\n        xmf_data_item.text = os.path.join(grid_path, component_name)\n    return xmf_attribute\n\n\ndef _xmf_grid(\n    observation,\n    topo_dim: int,\n    filename: str,\n    subfile_name: str,\n    temporal_id: str,\n    coordinates: str,\n    filling_poles: bool = False,\n    use_tetrahedral_connectivity: bool = False,\n) -> ET.Element:\n    # Make sure the coordinates are found in the file. We assume there should\n    # always be an x-coordinate.\n    assert coordinates + \"_x\" in observation, (\n        f\"No '{coordinates}_x' dataset found in '{filename}'. Existing\"\n        \" datasets with 'Coordinates' in their name: \"\n        + str(\n            [\n                dataset_name[:-2]\n                for dataset_name in observation\n                if \"Coordinates\" in dataset_name and dataset_name.endswith(\"_x\")\n            ]\n        )\n    )\n\n    # Determine dimension of embedding space by counting the number of\n    # coordinate components\n    dim = sum((coordinates + \"_\" + xyz) in observation for xyz in \"xyz\")\n\n    if filling_poles:\n        assert \"pole_connectivity\" in observation and topo_dim == 2 and dim == 3\n\n    xmf_grid = ET.Element(\"Grid\", Name=filename, GridType=\"Uniform\")\n\n    # Extents in the logical directions for each element in the dataset. The\n    # extents are stored in one long list with the dimension varying fast and\n    # the element index varying slow.\n    total_extents = observation[\"total_extents\"]\n    num_elements = len(total_extents) // topo_dim\n    extents = np.reshape(total_extents, (num_elements, topo_dim), order=\"C\")\n    num_points = np.sum(np.prod(extents, axis=1))\n\n    # Configure grid location in the H5 file\n    grid_path = filename + \":/\" + subfile_name + \"/\" + temporal_id + \"/\"\n\n    # Detect new mixed-topology format by presence of 'element_id' dataset\n    is_new_format = \"ElementId\" in observation\n\n    # Write topology\n    if topo_dim == 2 and dim == 3:\n        # 2D surface embedded in 3D space\n        if filling_poles:\n            if is_new_format:\n                xmf_topology = _xmf_mixed_topology(\n                    observation,\n                    connectivity_name=\"pole_connectivity\",\n                    grid_path=grid_path,\n                )\n            else:\n                xmf_topology = _xmf_topology(\n                    observation,\n                    topology_type=\"Triangle\",\n                    connectivity_name=\"pole_connectivity\",\n                    grid_path=grid_path,\n                )\n        elif is_new_format:\n            xmf_topology = _xmf_mixed_topology(\n                observation,\n                connectivity_name=\"connectivity\",\n                grid_path=grid_path,\n            )\n        else:\n            xmf_topology = _xmf_topology(\n                observation,\n                topology_type=\"Quadrilateral\",\n                connectivity_name=\"connectivity\",\n                grid_path=grid_path,\n            )\n    else:\n        # Cover volume\n        if use_tetrahedral_connectivity:\n            topology_type = {3: \"Tetrahedron\", 2: \"Triangle\"}[topo_dim]\n            xmf_topology = _xmf_topology(\n                observation,\n                topology_type=topology_type,\n                connectivity_name=\"tetrahedral_connectivity\",\n                grid_path=grid_path,\n            )\n        elif is_new_format:\n            xmf_topology = _xmf_mixed_topology(\n                observation,\n                connectivity_name=\"connectivity\",\n                grid_path=grid_path,\n            )\n        else:\n            topology_type = {3: \"Hexahedron\", 2: \"Quadrilateral\"}[topo_dim]\n            xmf_topology = _xmf_topology(\n                observation,\n                topology_type=topology_type,\n                connectivity_name=\"connectivity\",\n                grid_path=grid_path,\n            )\n    xmf_grid.append(xmf_topology)\n\n    # Write geometry\n    xmf_grid.append(\n        _xmf_geometry(\n            observation,\n            coordinates=coordinates,\n            dim=dim,\n            num_points=num_points,\n            grid_path=grid_path,\n        )\n    )\n\n    # Write the tensors that are to be visualized\n    for component in _list_tensor_components(observation):\n        if component in [coordinates + \"_\" + xyz for xyz in \"xyz\"[:dim]]:\n            # Skip coordinates\n            continue\n        elif component.endswith(\"_x\"):\n            # Vectors\n            xmf_grid.append(\n                _xmf_vector(\n                    observation,\n                    name=component[:-2],\n                    dim=dim,\n                    num_points=num_points,\n                    grid_path=grid_path,\n                )\n            )\n        elif component.endswith(\"_y\") or component.endswith(\"_z\"):\n            # Skip other vector components since they're processed above\n            continue\n        else:\n            # Treat everything else as scalars\n            xmf_grid.append(\n                _xmf_scalar(\n                    observation,\n                    name=component,\n                    num_points=num_points,\n                    grid_path=grid_path,\n                )\n            )\n\n    # For new-format volume data, add cell-centered element_id and block_id\n    # attributes (not for the pole-filling grid, which uses pole_connectivity).\n    if is_new_format and not filling_poles:\n        number_of_cells = _count_cells_in_mixed_connectivity(\n            observation[\"connectivity\"][:]\n        )\n        for attr_name in [\"ElementId\", \"BlockId\"]:\n            if attr_name in observation:\n                xmf_grid.append(\n                    _xmf_cell_attribute(\n                        observation, attr_name, number_of_cells, grid_path\n                    )\n                )\n\n    return xmf_grid\n\n\ndef get_files_with_subfile(\n    h5file_names: Sequence[str], subfile_name: str\n) -> List[Tuple[h5py.File, str]]:\n    \"\"\"Get the h5files and their name that contain a subfile with the name\n    subfile_name\n\n        \\f\n    Arguments:\n      h5file_names: List of H5 file names of files to open and check if they\n        have the subfile.\n      subfile_name: The name of the subfile to check for.\n    \"\"\"\n    result = list()\n    for filename in h5file_names:\n        h5file = h5py.File(filename, \"r\")\n        if subfile_name in h5file:\n            result.append((h5file, filename))\n    return result\n\n\ndef generate_xdmf(\n    h5files,\n    output: str,\n    subfile_name: str,\n    relative_paths: bool = True,\n    start_time: Optional[float] = None,\n    stop_time: Optional[float] = None,\n    stride: int = 1,\n    coordinates: str = \"InertialCoordinates\",\n    use_tetrahedral_connectivity: bool = False,\n):\n    \"\"\"Generate an XDMF file for ParaView and VisIt\n\n    Read volume data from the 'H5FILES' and generate an XDMF file. The XDMF file\n    points into the 'H5FILES' files so ParaView and VisIt can load the volume\n    data. To process multiple files suffixed with the node number and from\n    multiple segments specify a glob like 'Segment*/VolumeData*.h5'.\n\n    To load the XDMF file in ParaView you must choose the 'Xdmf3 Reader', NOT\n    'Xdmf Reader'.\n\n    \\f\n    Arguments:\n      h5files: List of H5 volume data files.\n      output: Output filename. A '.xmf' extension is added if not present.\n      subfile_name: Volume data subfile in the H5 files.\n      relative_paths: If True, use relative paths in the XDMF file (default). If\n        False, use absolute paths.\n      start_time: Optional. The earliest time at which to start visualizing. The\n        start-time value is included.\n      stop_time: Optional. The time at which to stop visualizing. The stop-time\n        value is not included.\n      stride: Optional. View only every stride'th time step.\n      coordinates: Optional. Name of coordinates dataset. Default:\n        \"InertialCoordinates\".\n      use_tetrahedral_connectivity: Optional. Use \"tetrahedral_connectivity\".\n        Default: False\n    \"\"\"\n    h5file_names = h5files\n\n    if not subfile_name:\n        subfiles = available_subfiles(\n            h5file_names,\n            extension=\".vol\",\n        )\n        if len(subfiles) == 1:\n            subfile_name = subfiles[0]\n            logger.info(\n                f\"Selected subfile {subfile_name} (the only available one).\"\n            )\n        else:\n            raise RequiredChoiceError(\n                (\n                    \"Specify '--subfile-name' / '-d' to select a\"\n                    \" subfile containing volume data.\"\n                ),\n                choices=subfiles,\n            )\n\n    if not subfile_name.endswith(\".vol\"):\n        subfile_name += \".vol\"\n\n    h5files = get_files_with_subfile(h5file_names, subfile_name)\n\n    if len(h5files) == 0:\n        raise ValueError(\n            f\"Could not open subfile name '{subfile_name}' in any h5 \"\n            f\"files, {h5file_names}. Available subfiles: \"\n            + str(\n                available_subfiles(\n                    h5file_names,\n                    extension=\".vol\",\n                )\n            )\n        )\n\n    # Prepare XDMF document by building up an XML tree\n    xmf_root = ET.Element(\"Xdmf\", Version=\"3.0\")\n    xmf_domain = ET.SubElement(xmf_root, \"Domain\")\n    xmf_timesteps = ET.SubElement(\n        xmf_domain,\n        \"Grid\",\n        Name=\"Evolution\",\n        GridType=\"Collection\",\n        CollectionType=\"Temporal\",\n    )\n    # Collect timestep records from all input files so stride can be applied\n    # globally rather than independently per file.\n    timesteps = dict()\n\n    for h5file, filename in h5files:\n        # Open subfile\n        try:\n            vol_subfile = h5file[subfile_name]\n        except KeyError as err:\n            raise ValueError(\n                f\"Could not open subfile name '{subfile_name}' in '{filename}'.\"\n                \" Available subfiles: \"\n                + str(available_subfiles(h5file, extension=\".vol\"))\n            ) from err\n        topo_dim = int(vol_subfile.attrs[\"dimension\"])\n        if topo_dim == 1:\n            raise ValueError(\n                \"The spatial dimension of the data in subfile\"\n                f\" {subfile_name} of HDF5 file {filename} is 1d \"\n                \"but generate-xdmf only works on 2d and 3d data.\"\n            )\n\n        # Use paths relative to the output file or absolute paths\n        filename_in_output = (\n            os.path.relpath(\n                filename, os.path.dirname(output) if output else None\n            )\n            if relative_paths\n            else os.path.abspath(filename)\n        )\n\n        # Sort timesteps by time\n        temporal_ids_and_values = sorted(\n            [\n                (key, vol_subfile[key].attrs[\"observation_value\"])\n                for key in vol_subfile.keys()\n                if key.startswith(\"ObservationId\")\n            ],\n            key=lambda key_and_time: key_and_time[1],\n        )\n\n        for temporal_id, time in temporal_ids_and_values:\n            timestep_key = (time, temporal_id)\n            if timestep_key not in timesteps:\n                timesteps[timestep_key] = []\n            timesteps[timestep_key].append(\n                (vol_subfile, topo_dim, filename_in_output)\n            )\n\n    # Sort timesteps globally by time and apply stride to the global sequence.\n    sorted_timestep_items = sorted(\n        timesteps.items(), key=lambda item: (item[0][0], item[0][1])\n    )\n    for (time, temporal_id), timestep_records in sorted_timestep_items[\n        ::stride\n    ]:\n        # Filter by start and end time\n        if start_time is not None and time < start_time:\n            continue\n        if stop_time is not None and time > stop_time:\n            break\n\n        xmf_timestep_grid = ET.SubElement(\n            xmf_timesteps, \"Grid\", Name=\"Grids\", GridType=\"Collection\"\n        )\n        # The time is stored as a `Time` tag in the grid collection\n        ET.SubElement(xmf_timestep_grid, \"Time\", Value=f\"{time:.14e}\")\n\n        for vol_subfile, topo_dim, filename_in_output in timestep_records:\n            # Construct the grid for this observation\n            observation = vol_subfile[temporal_id]\n            xmf_timestep_grid.append(\n                _xmf_grid(\n                    observation,\n                    topo_dim=topo_dim,\n                    filename=filename_in_output,\n                    subfile_name=subfile_name,\n                    temporal_id=temporal_id,\n                    coordinates=coordinates,\n                    use_tetrahedral_connectivity=use_tetrahedral_connectivity,\n                )\n            )\n            # Backwards compatibility: old files have a separate\n            # 'pole_connectivity' dataset with Triangle cells to fill the poles.\n            if \"pole_connectivity\" in observation:\n                xmf_timestep_grid.append(\n                    _xmf_grid(\n                        observation,\n                        topo_dim=topo_dim,\n                        filename=filename_in_output,\n                        subfile_name=subfile_name,\n                        temporal_id=temporal_id,\n                        coordinates=coordinates,\n                        filling_poles=True,\n                        use_tetrahedral_connectivity=(\n                            use_tetrahedral_connectivity\n                        ),\n                    )\n                )\n\n    for h5file in h5files:\n        h5file[0].close()\n\n    # Pretty-print XML\n    try:\n        # Added in Py 3.9\n        ET.indent(xmf_root)\n    except AttributeError:\n        pass\n\n    # Output XML (XDMF 3.0 does not use the DTD declaration)\n    xmf_document = '<?xml version=\"1.0\" ?>\\n'\n    xmf_document += ET.tostring(xmf_root, encoding=\"unicode\")\n    xmf_document += \"\\n\"\n    if output:\n        if not output.endswith(\".xmf\"):\n            output += \".xmf\"\n        with open(output, \"w\") as open_output_file:\n            open_output_file.write(xmf_document)\n    else:\n        sys.stdout.write(xmf_document)\n\n\n@click.command(name=\"generate-xdmf\", help=generate_xdmf.__doc__)\n@click.argument(\n    \"h5files\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=-1,\n    required=True,\n)\n@click.option(\n    \"--output\",\n    \"-o\",\n    type=click.Path(writable=True),\n    help=(\n        \"Output file name. A '.xmf' extension will be added if not present. \"\n        \"If unspecified, the output will be written to stdout.\"\n    ),\n)\n@click.option(\n    \"--subfile-name\",\n    \"-d\",\n    help=(\n        \"Name of the volume data subfile in the H5 files. A '.vol' extension is\"\n        \" added if needed. If unspecified, and the first H5 file contains only\"\n        \" a single '.vol' subfile, choose that. Otherwise, list all '.vol'\"\n        \" subfiles and exit.\"\n    ),\n)\n@click.option(\n    \"--relative-paths/--absolute-paths\",\n    default=True,\n    show_default=True,\n    help=\"Use relative paths or absolute paths in the XDMF file.\",\n)\n@click.option(\n    \"--stride\", default=1, type=int, help=\"View only every stride'th time step\"\n)\n@click.option(\n    \"--start-time\",\n    type=float,\n    help=(\n        \"The earliest time at which to start visualizing. The start-time \"\n        \"value is included.\"\n    ),\n)\n@click.option(\n    \"--stop-time\",\n    type=float,\n    help=(\n        \"The time at which to stop visualizing. The stop-time value is \"\n        \"included.\"\n    ),\n)\n@click.option(\n    \"--coordinates\",\n    default=\"InertialCoordinates\",\n    show_default=True,\n    help=\"The coordinates to use for visualization\",\n)\n@click.option(\n    \"--use-tetrahedral-connectivity\",\n    is_flag=True,\n    default=False,\n    help=(\n        \"Use a tetrahedral connectivity called tetrahedral_connectivity in \"\n        \"the HDF5 file. See the generate-tetrahedral-connectivity CLI for \"\n        \"information on how to generate tetrahedral connectivity and what it \"\n        \"can be useful for.\"\n    ),\n)\ndef generate_xdmf_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    generate_xdmf(**kwargs)\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.INFO)\n    generate_xdmf_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/Plot.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport functools\nimport logging\nimport os\n\nimport click\nimport matplotlib.animation\nimport matplotlib.pyplot as plt\nimport rich.progress\n\nlogger = logging.getLogger(__name__)\n\nDEFAULT_MPL_STYLESHEET = os.path.join(\n    os.path.dirname(__file__), \"plots.mplstyle\"\n)\n\n\ndef apply_stylesheet_command():\n    \"\"\"Add a CLI option to apply a stylesheet for plotting\"\"\"\n\n    def decorator(f):\n        @click.option(\n            \"--stylesheet\",\n            \"-s\",\n            envvar=\"SPECTRE_MPL_STYLESHEET\",\n            help=(\n                \"Select a matplotlib stylesheet for customization of the plot,\"\n                \" such as linestyle cycles, linewidth, fontsize, legend, etc.\"\n                \" Specify a filename or one of the built-in styles. See\"\n                \" https://matplotlib.org/gallery/style_sheets/style_sheets_reference\"\n                \" for a list of built-in styles, e.g. 'seaborn-dark'. The\"\n                \" stylesheet can also be set with the 'SPECTRE_MPL_STYLESHEET'\"\n                \" environment variable.\"\n            ),\n        )\n        # Preserve the original function's name and docstring\n        @functools.wraps(f)\n        def command(stylesheet, **kwargs):\n            # Apply the default stylesheet and the user-provided one\n            stylesheets = [DEFAULT_MPL_STYLESHEET]\n            if stylesheet is not None:\n                stylesheets.append(stylesheet)\n            plt.style.use(stylesheets)\n            return f(**kwargs)\n\n        return command\n\n    return decorator\n\n\ndef show_or_save_plot_command():\n    \"\"\"Add an 'output' CLI option and show or save the plot accordingly\n\n    Apply this decorator to a CLI command that generates a plot or animation.\n    Return the `matplotlib.Figure` or `matplotlib.animation.Animation` from your\n    command. At the end of the command, the plot or animation is either shown\n    interactively or saved to a file, depending on the user-specified 'output'\n    option.\n    \"\"\"\n\n    def decorator(f):\n        @click.option(\n            \"--output\",\n            \"-o\",\n            type=click.Path(file_okay=True, dir_okay=False, writable=True),\n            help=(\n                \"Name of the output plot file. If unspecified, the plot is \"\n                \"shown interactively, which only works on machines with a \"\n                \"window server. If a filename is specified, its extension \"\n                \"determines the file format, e.g. 'plot.png' or 'plot.pdf' for \"\n                \"static plots and 'animation.gif' or 'animation.mp4' (requires \"\n                \"ffmpeg) for animations. \"\n                \"If no extension is given, the file format depends on the \"\n                \"system settings (see matplotlib.pyplot.savefig docs).\"\n            ),\n        )\n        # Preserve the original function's name and docstring\n        @functools.wraps(f)\n        def command(output, **kwargs):\n            # Call the original function\n            fig_or_anim = f(**kwargs)\n            # Show or save the plot\n            if output:\n                if isinstance(fig_or_anim, matplotlib.animation.Animation):\n                    progress = rich.progress.Progress(\n                        rich.progress.TextColumn(\n                            \"[progress.description]{task.description}\"\n                        ),\n                        rich.progress.BarColumn(),\n                        rich.progress.MofNCompleteColumn(),\n                        rich.progress.TimeRemainingColumn(),\n                    )\n                    task_id = progress.add_task(\"Rendering frames\", total=None)\n                    with progress:\n                        fig_or_anim.save(\n                            output,\n                            progress_callback=lambda i, n: progress.update(\n                                task_id, completed=i + 1, total=n\n                            ),\n                        )\n                else:\n                    plt.savefig(output)\n                    plt.close()\n            else:\n                if not os.environ.get(\"DISPLAY\"):\n                    logger.warning(\n                        \"No 'DISPLAY' environment variable is configured so\"\n                        \" plotting interactively is unlikely to work. Write the\"\n                        \" plot to a file with the --output/-o option.\"\n                    )\n                plt.show()\n\n        return command\n\n    return decorator\n"
  },
  {
    "path": "src/Visualization/Python/PlotAlongLine.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom typing import Sequence\n\nimport click\nimport matplotlib.animation\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nfrom spectre.IO.Exporter import ObservationId, interpolate_to_points\nfrom spectre.IO.H5 import (\n    list_observations,\n    open_volfiles,\n    open_volfiles_command,\n    parse_point,\n)\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef points_on_line(\n    line_start: Sequence[float], line_end: Sequence[float], num_points: int\n) -> np.ndarray:\n    \"\"\"Returns points on a line.\n\n    Parameters:\n      line_start: Start point of the line.\n      line_end: End point of the line.\n      num_points: Number of points to return.\n\n    Returns: An array of shape (dim, num_points) with uniformly spaced points\n      on the line.\n    \"\"\"\n    line_start = np.array(line_start)\n    line_end = np.array(line_end)\n    line_parameter = np.linspace(0, 1, num_points)\n    normal = line_end - line_start\n    return line_start[:, np.newaxis] + np.outer(normal, line_parameter)\n\n\ndef plot_along_line(\n    h5_files,\n    subfile_name,\n    obs_id,\n    obs_time,\n    vars,\n    line_start,\n    line_end,\n    extrapolate_into_excisions=False,\n    num_samples=200,\n    num_threads=None,\n    x_logscale=False,\n    y_logscale=False,\n    y_bounds=None,\n    animate=False,\n    interval=100,\n):\n    \"\"\"Plot variables along a line through volume data\n\n    Interpolates the volume data in the H5_FILES to a line and plots the\n    selected variables. You choose the line by specifying the start and end\n    points.\n\n    Either select a specific observation in the volume data with '--step' or\n    '--time', or specify '--animate' to produce an animation over all\n    observations.\n    \"\"\"\n    if animate == (obs_id is not None):\n        raise click.UsageError(\n            \"Specify an observation '--step' or '--time', or specify\"\n            \" '--animate' (but not both).\"\n        )\n    if animate:\n        obs_ids, obs_times = list_observations(\n            open_volfiles(h5_files, subfile_name)\n        )\n\n    # Determine the line coordinates\n    if line_start is None or line_end is None:\n        raise click.UsageError(\n            f\"Specify '--line-start' / '-A' and '--line-end' / '-B'.\"\n        )\n    if len(line_start) != len(line_end):\n        raise click.UsageError(\n            \"'--line-start' / '-A' and '--line-end' / '-B' must have\"\n            \" the same dimension.\"\n        )\n    target_coords = points_on_line(line_start, line_end, num_samples)\n\n    # Set x-axis for plot. For plotting along an axis, use the axis coordinate.\n    # Otherwise, use the line parameter.\n    normal = np.asarray(line_end) - np.asarray(line_start)\n    nonzero_entries = np.nonzero(normal)[0]\n    coord_axis = nonzero_entries[0] if len(nonzero_entries) == 1 else None\n    if coord_axis is not None:\n        x = target_coords[coord_axis]\n        x_label = \"xyz\"[coord_axis]\n    else:\n        x = np.linspace(0, 1, num_samples)\n        A_label = \", \".join(f\"{x:g}\" for x in line_start)\n        B_label = \", \".join(f\"{x:g}\" for x in line_end)\n        x_label = f\"$({A_label})$ to $({B_label})$\"\n\n    # Select plotting parameters. Any further customization of the plotting\n    # style can be done with a stylesheet.\n    plot_kwargs = dict(\n        color=\"black\" if len(vars) == 1 else None,\n    )\n\n    # Set up the figure\n    fig, ax = plt.figure(), plt.gca()\n    plt.xlim(x[0], x[-1])\n    if x_logscale:\n        plt.xscale(\"log\")\n    if y_logscale:\n        plt.yscale(\"log\")\n    if y_bounds:\n        plt.ylim(*y_bounds)\n    plt.xlabel(x_label)\n    # Keep track of plot lines to update in animation\n    lines = [\n        plt.plot([], [], label=var_name, **plot_kwargs)[0] for var_name in vars\n    ]\n    plt.legend()\n    time_label = plt.annotate(\n        \"\",\n        xy=(0, 0),\n        xycoords=\"axes fraction\",\n        xytext=(4, 3),\n        textcoords=\"offset points\",\n        ha=\"left\",\n        va=\"bottom\",\n        fontsize=9,\n    )\n\n    def update_plot(obs_id, obs_time):\n        vars_on_line = interpolate_to_points(\n            h5_files,\n            subfile_name=subfile_name,\n            observation=ObservationId(obs_id),\n            tensor_components=vars,\n            target_points=target_coords,\n            extrapolate_into_excisions=extrapolate_into_excisions,\n            num_threads=num_threads,\n        )\n        for y, var_name, line in zip(vars_on_line, vars, lines):\n            line.set_data(x, np.asarray(y))\n        time_label.set_text(f\"t = {obs_time:g}\")\n        ax.relim()\n        ax.autoscale_view()\n\n    # Animate or plot\n    if animate:\n        return matplotlib.animation.FuncAnimation(\n            fig,\n            lambda i: update_plot(obs_ids[i], obs_times[i]),\n            init_func=list,\n            frames=len(obs_ids),\n            interval=interval,\n            blit=False,\n        )\n    else:\n        update_plot(obs_id, obs_time)\n        return fig\n\n\n@click.command(name=\"along-line\", help=plot_along_line.__doc__)\n@open_volfiles_command(obs_id_required=False, multiple_vars=True)\n# Line options\n# These aren't marked \"required\" so the user can omit them when using options\n# like '--list-vars'.\n@click.option(\n    \"--line-start\",\n    \"-A\",\n    callback=parse_point,\n    help=(\n        \"Coordinates of the start of the line through the volume data. \"\n        \"Specify as comma-separated list, e.g. '0,0,0'.  [required]\"\n    ),\n)\n@click.option(\n    \"--line-end\",\n    \"-B\",\n    callback=parse_point,\n    help=(\n        \"Coordinates of the end of the line through the volume data. \"\n        \"Specify as comma-separated list, e.g. '1,0,0'.  [required]\"\n    ),\n)\n@click.option(\n    \"--extrapolate-into-excisions\",\n    is_flag=True,\n    help=(\n        \"Enables extrapolation into excision regions of the domain. \"\n        \"This can be useful to fill the excision region with \"\n        \"(constraint-violating but smooth) data so it can be imported into \"\n        \"moving puncture codes.\"\n    ),\n)\n@click.option(\n    \"--num-samples\",\n    \"-N\",\n    type=int,\n    default=200,\n    show_default=True,\n    help=(\n        \"Number of uniformly spaced samples along the line to which volume \"\n        \"data is interpolated.\"\n    ),\n)\n@click.option(\n    \"--num-threads\",\n    \"-j\",\n    type=int,\n    show_default=\"all available cores\",\n    help=(\n        \"Number of threads to use for interpolation. Only available if compiled\"\n        \" with OpenMP. Parallelization is over volume data files, so this only\"\n        \" has an effect if multiple files are specified.\"\n    ),\n)\n@click.option(\"--x-logscale\", is_flag=True, help=\"Set the x-axis to log scale.\")\n@click.option(\"--y-logscale\", is_flag=True, help=\"Set the y-axis to log scale.\")\n@click.option(\n    \"--y-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the y-axis.\",\n)\n# Animation options\n@click.option(\"--animate\", is_flag=True, help=\"Animate over all observations.\")\n@click.option(\n    \"--interval\",\n    default=100,\n    type=float,\n    help=\"Delay between frames in milliseconds. Only used for animations.\",\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_along_line_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    return plot_along_line(**kwargs)\n\n\nif __name__ == \"__main__\":\n    plot_along_line_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotCce.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom typing import Optional, Sequence\n\nimport click\nimport h5py\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\nfrom spectre.IO.H5 import available_subfiles, to_dataframe\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef _parse_modes(ctx, param, all_modes):\n    result = []\n    for mode in all_modes:\n        l_and_m = mode.split(\",\")\n        if len(l_and_m) != 2:\n            raise click.BadParameter(\n                f\"The mode {mode} must be specified as 'l,m'\"\n            )\n        result.append(f\"Real Y_{mode}\")\n        result.append(f\"Imag Y_{mode}\")\n    return result\n\n\ndef plot_cce(\n    h5_filename: str,\n    modes: Sequence[str],\n    real: bool = False,\n    imag: bool = False,\n    extraction_radius: Optional[int] = None,\n    list_extraction_radii: bool = False,\n    backward_cce_group: Optional[str] = None,\n    x_bounds: Optional[Sequence[float]] = None,\n    x_label: Optional[str] = None,\n    title: Optional[str] = None,\n    fig: Optional[plt.Figure] = None,\n):\n    \"\"\"\n    Plot the Strain, News, and Psi0-Psi4 from the output of a SpECTRE CCE run.\n\n    The data must be in a SpECTRE Cce subfile with a '.cce' extension. Multiple\n    modes can be plotted at once along with your choice of plotting both real,\n    imaginary, or both.\n\n    IMPORTANT: These plots are *NOT* in the correct BMS frame. This tool is only\n    meant to plot the raw data produced by the SpECTRE CCE module.\n    \"\"\"\n\n    if real and imag:\n        raise click.UsageError(\n            \"Only specify one of '--real'/'--imag'. If you want to plot both\"\n            \" real and imaginary modes, specify neither.\"\n        )\n\n    # Filter out modes we aren't plotting\n    if real:\n        modes = [mode for mode in modes if \"Real\" in mode]\n    elif imag:\n        modes = [mode for mode in modes if \"Imag\" in mode]\n\n    plot_quantities = [\"Strain\", \"News\", \"Psi0\", \"Psi1\", \"Psi2\", \"Psi3\", \"Psi4\"]\n\n    with h5py.File(h5_filename, \"r\") as h5file:\n        cce_subfiles = available_subfiles(h5file, extension=\".cce\")\n\n        # If we're only listing subfiles, print them and exit\n        if list_extraction_radii:\n            import rich\n            import rich.columns\n\n            rich.print(rich.columns.Columns(cce_subfiles))\n            return\n\n        # If there aren't any cce subfiles, raise an error (unless this is the\n        # old CCE data format)\n        if len(cce_subfiles) == 0 and backward_cce_group is None:\n            raise click.UsageError(\n                f\"Could not find any Cce subfiles in H5 file {h5_filename}. Cce\"\n                \" subfiles must end with the extension '.cce'\"\n            )\n\n        if (\n            len(cce_subfiles) == 1\n            and extraction_radius is not None\n            and f\"SpectreR{extraction_radius:04}.cce\" not in cce_subfiles[0]\n        ):\n            raise click.UsageError(\n                f\"The extraction radius passed in ({extraction_radius}) does\"\n                \" not match the single Cce subfile that was found\"\n                f\" ({cce_subfiles[0]}). Either specify the correct extraction\"\n                \" radius, or remove the option altogether.\"\n            )\n\n        if backward_cce_group is not None:\n            cce_subfiles = [backward_cce_group]\n\n        # If there is more than one cce subfile, but we didn't specify an\n        # extraction radius, then error\n        if len(cce_subfiles) > 1 and extraction_radius is None:\n            raise click.UsageError(\n                f\"The H5 file {h5_filename} has {len(cce_subfiles)} Cce\"\n                \" subfiles, but you did not specify an extraction radius.\"\n                \" Please specify an extraction radius with\"\n                \" '--extraction-radius'/'-r'.\"\n            )\n\n        # If we didn't specify an extraction radius, the subfile name is just\n        # the one listed in the file. If the extraction radius was specified\n        # (and we've now guaranteed there is more than one subfile) use that as\n        # the subfile name\n        cce_subfile_name = (\n            cce_subfiles[0]\n            if (extraction_radius is None or backward_cce_group is not None)\n            else f\"SpectreR{extraction_radius:04}.cce\"\n        )\n        cce_subfile = h5file.get(cce_subfile_name)\n        if cce_subfile is None:\n            raise RequiredChoiceError(\n                (\n                    f\"Could not find Cce subfile {cce_subfile} in H5 file\"\n                    f\" {h5_filename}.\"\n                ),\n                choices=cce_subfiles,\n            )\n\n        suffix = \".dat\" if backward_cce_group is not None else \"\"\n\n        # Only take the columns that we need and prefix the columns with their\n        # quantity so we can have just one DataFrame\n        data = pd.concat(\n            [\n                to_dataframe(cce_subfile.get(quantity + suffix))\n                .set_index(\"time\")[modes]\n                .add_prefix(quantity)\n                for quantity in plot_quantities\n            ],\n            axis=1,\n        )\n\n    # Restrict to x-bounds if we have any\n    if x_bounds:\n        data = data[(data.index >= x_bounds[0]) & (data.index <= x_bounds[1])]\n\n    # Set up the plots\n    if fig is None:\n        fig = plt.figure(figsize=(8, 2 * len(plot_quantities)))\n    axes = list(fig.subplots(len(plot_quantities), 1, sharex=True))\n    # Make the legend look a bit nicer\n    divisor = 1 if (real or imag) else 2\n    num_col = min(4, len(modes) / divisor)\n    cycle = plt.rcParams[\"axes.prop_cycle\"].by_key()[\"color\"]\n\n    # Plot quantities and configure the axes and legend\n    for i, quantity in enumerate(plot_quantities):\n        ax = axes[i]\n        for j, mode in enumerate(modes):\n            # If we are plotting both real and imaginary modes, make real solid\n            # and imaginary dashed. If we are plotting only real or only\n            # imaginary, then the lines are solid\n            linestyle = (\n                \"solid\" if (real or imag or \"Real\" in mode) else \"dashed\"\n            )\n            # If we are plotting both real and imaginary modes, make the\n            # real/imag lines for the same mode the same color. If we are\n            # plotting only real or only imaginary, then just cycle regularly\n            # through the colors\n            cycle_idx = (\n                j % len(cycle) if (real or imag) else (j // 2) % len(cycle)\n            )\n            ax.plot(\n                data.index,\n                data[f\"{quantity}{mode}\"],\n                color=cycle[cycle_idx],\n                linestyle=linestyle,\n                label=mode if i == 0 else None,\n            )\n        ax.set_ylabel(quantity)\n        # Legend only above top plot\n        if i == 0:\n            ax.legend(\n                loc=\"lower center\", bbox_to_anchor=(0.5, 1.0), ncol=num_col\n            )\n\n    # If we have an x-label, goes on the bottom plot\n    if x_label:\n        axes[-1].set_xlabel(x_label)\n\n    # Plot needs to be fairly big\n    if title:\n        plt.suptitle(title)\n    return fig\n\n\n@click.command(name=\"cce\", help=plot_cce.__doc__)\n@click.argument(\n    \"h5_filename\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=1,\n)\n@click.option(\n    \"--modes\",\n    \"-m\",\n    multiple=True,\n    callback=_parse_modes,\n    required=True,\n    help=(\n        \"Which mode to plot. Specified as 'l,m' (e.g. '--modes 2,2'). Will plot\"\n        \" both real and imaginary components unless '--real' or '--imag' are\"\n        \" specified. Can be specified multiple times.\"\n    ),\n)\n@click.option(\n    \"--real\",\n    is_flag=True,\n    default=False,\n    help=\"Plot only real modes. Mutually exclusive with '--imag'.\",\n)\n@click.option(\n    \"--imag\",\n    is_flag=True,\n    default=False,\n    help=\"Plot only imaginary modes. Mutually exclusive with '--real'.\",\n)\n@click.option(\n    \"--extraction-radius\",\n    \"-r\",\n    type=int,\n    help=(\n        \"Extraction radius of data to plot as an int. If there is only one Cce\"\n        \" subfile, that one will be used and this option does not need to be\"\n        \" specified. The expected form of the Cce subfile is 'SpectreRXXXX.cce'\"\n        \" where XXXX is the zero-padded integer extraction radius. This option\"\n        \" is ignored if the backwards compatibility option '--cce-group'/'-d'\"\n        \" is specified.\"\n    ),\n)\n@click.option(\n    \"--list-extraction-radii\",\n    \"-l\",\n    is_flag=True,\n    default=False,\n    help=\"List Cce subfiles in the 'h5_filename' and exit.\",\n)\n@click.option(\n    \"--cce-group\",\n    \"-d\",\n    \"backward_cce_group\",\n    help=(\n        \"Option for backwards compatibility with an old version of CCE data.\"\n        \" This is the group name of the CCE data in the 'h5_filename'\"\n        \" (typically Cce). This option should only be used if your CCE data was\"\n        \" produced with a version of SpECTRE prior to this Pull Request:\"\n        \" https://github.com/sxs-collaboration/spectre/pull/5985.\"\n    ),\n)\n# Plotting options\n@click.option(\n    \"--x-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the x-axis.\",\n)\n@click.option(\n    \"--x-label\",\n    help=\"The label on the x-axis.\",\n)\n@click.option(\n    \"--title\",\n    \"-t\",\n    help=\"Title of the graph.\",\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_cce_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    plot_cce(**kwargs)\n\n\nif __name__ == \"__main__\":\n    plot_cce_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotControlSystem.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom typing import Optional, Sequence\n\nimport click\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom spectre.IO.H5 import available_subfiles, to_dataframe\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef plot_control_system(\n    reduction_files: Sequence[str],\n    with_shape: bool = True,\n    show_all_m: bool = False,\n    shape_l_max: int = 2,\n    x_bounds: Optional[Sequence[float]] = None,\n    x_label: Optional[str] = None,\n    title: Optional[str] = None,\n):\n    \"\"\"\n    Plot diagnostic information regarding all control systems except size\n    control. If you want size control diagnostics use\n    `spectre plot size-control`.\n\n    This tool assumes there are subfiles in each of the \"reduction-files\" with\n    the path `/ControlSystems/{Name}/*.dat`, where `{NAME}` is the name of the\n    control system and `*.dat` are all the components of that control system.\n\n    Shape control is a bit special because it has a large number of components.\n    Control whether or not you plot shape, and how many of these components you\n    plot, with the `--with-shape/--without-shape`, `--shape-l_max`, and\n    `--show-all-m` options.\n    \"\"\"\n\n    # Given an h5 file, make sure that the \"ControlSystems\" group exists. Then\n    # return the list of control systems in that group, excluding size control\n    def check_control_system_dir(h5_filename: str):\n        h5file = h5py.File(h5_filename, \"r\")\n        control_system_dir = h5file.get(\"ControlSystems\")\n        if control_system_dir is None:\n            raise RequiredChoiceError(\n                (\n                    \"Unable to open group 'ControlSystems' from h5 file\"\n                    f\" {h5_filename}.\"\n                ),\n                choices=available_subfiles(h5file, extension=\".dat\"),\n            )\n\n        # No size control\n        return [key for key in control_system_dir.keys() if \"Size\" not in key]\n\n    # Given an h5 file and a subfile, make sure that the subfile exists inside\n    # the h5 file. Then return the subfile as a DataFrame (with \"Time\" as the\n    # index)\n    def check_control_system_file(\n        h5_filename: str,\n        h5file: h5py.File,\n        control_system_name: str,\n        component_name: str,\n    ):\n        subfile_path = (\n            f\"/ControlSystems/{control_system_name}/{component_name}.dat\"\n        )\n        subfile = h5file.get(subfile_path)\n        if subfile_path is None:\n            raise RequiredChoiceError(\n                (\n                    f\"Unable to open control system subfile '{subfile_path}'\"\n                    f\" from h5 file {h5_filename}.\"\n                ),\n                choices=available_subfiles(h5file, extension=\".dat\"),\n            )\n\n        return to_dataframe(subfile).set_index(\"Time\")\n\n    # Given an h5 file name and a control system name, return a list of all\n    # components for that control system\n    def get_control_system_components(\n        h5_filename: str, control_system_name: str\n    ):\n        h5file = h5py.File(h5_filename, \"r\")\n        control_system_group_name = f\"/ControlSystems/{control_system_name}\"\n        control_system_group = h5file.get(control_system_group_name)\n\n        # Only want the component name without the '.dat' extension\n        keys = [key.split(\"/\")[-1][:-4] for key in control_system_group.keys()]\n\n        if \"Shape\" in control_system_name:\n            return [\n                key\n                for key in keys\n                if 2 <= int(key.split(\"l\")[1].split(\"m\")[0]) <= shape_l_max\n            ]\n        else:\n            return keys\n\n    relevant_columns = [\"ControlError\", \"DampingTimescale\"]\n\n    # We are only plotting the most relevant columns for now. More can be added\n    # later if it's useful for debugging\n    def extract_relevant_columns(\n        df: pd.DataFrame, name: str, component_name: str\n    ):\n        return df[relevant_columns].add_prefix(name + component_name)\n\n    # Get a list of all control systems (excluding size) that we have from the\n    # first reductions file and a map to all of their components\n    control_systems = check_control_system_dir(reduction_files[0])\n    control_system_components = {\n        system: get_control_system_components(reduction_files[0], system)\n        for system in control_systems\n        if (\n            (\"Shape\" not in system or with_shape)\n            and not system.endswith(\".dat\")\n        )\n    }\n\n    # Open every h5file. For each h5file, turn each component of each control\n    # system into a DataFrame and concat it with the large data frame\n    data = pd.DataFrame()\n    for reduction_file in reduction_files:\n        h5file = h5py.File(reduction_file)\n        file_df = pd.DataFrame()\n\n        for system in control_system_components:\n            for component in control_system_components[system]:\n                tmp_data = check_control_system_file(\n                    reduction_file, h5file, system, component\n                )\n                tmp_data = extract_relevant_columns(tmp_data, system, component)\n                # When we concat DataFrames from within an H5 file together, we\n                # assume they have the same indexes (times) so we concat along\n                # axis=1\n                file_df = pd.concat([file_df, tmp_data], axis=1)\n\n            if \"Shape\" not in system or show_all_m:\n                continue\n\n            # If this is the shape system and we don't want to show all m, we\n            # take the L2 norm over all m for a given l\n            for l in range(2, shape_l_max + 1):\n                component_prefix = f\"l{l}m\"\n                for column in relevant_columns:\n                    components_to_norm = [\n                        f\"{system}{component_prefix}{m}{column}\"\n                        for m in range(-l, l + 1)\n                    ]\n\n                    file_df[f\"{system}{component_prefix}{column}\"] = np.sqrt(\n                        np.square(file_df[components_to_norm].to_numpy()).sum(\n                            axis=1\n                        )\n                    )\n\n        # When concating the large DataFrames from each H5 file together, we\n        # assume all the columns are the same so we concat along axis=0\n        data = pd.concat([data, file_df])\n\n    # If we aren't showing all m for shape control, modify the shape components\n    # that are being plotted\n    if with_shape and not show_all_m:\n        for system in control_systems:\n            if \"Shape\" not in system:\n                continue\n\n            # Overwrite existing components with empty list\n            control_system_components[system] = []\n            for l in range(2, shape_l_max + 1):\n                control_system_components[system].append(f\"l{l}m\")\n\n    # Restrict data to bounds\n    if x_bounds:\n        data = data[(data.index >= x_bounds[0]) & (data.index <= x_bounds[1])]\n\n    # Set up plots\n    fig, axes = plt.subplots(2, 1, sharex=True)\n\n    axes = list(axes)\n\n    # Plot all components for all systems. The name for the legend differs\n    # slightly from the name in the DataFrame, just to make it prettier for the\n    # user. Since both figures will have the same lines, we only show one legend\n    line_styles = [\"-\", \"--\", \":\", \"-.\"]\n    for i, system in enumerate(control_system_components):\n        for component in control_system_components[system]:\n            df_name = f\"{system}{component}\"\n            legend_name = (\n                system\n                if len(control_system_components[system]) == 1\n                else f\"{system} {component}\"\n            )\n\n            axes[0].plot(\n                data.index,\n                np.abs(data[f\"{df_name}ControlError\"]),\n                linestyle=line_styles[i % len(line_styles)],\n            )\n            axes[1].plot(\n                data.index,\n                np.abs(data[f\"{df_name}DampingTimescale\"]),\n                label=legend_name,\n                linestyle=line_styles[i % len(line_styles)],\n            )\n\n    # Configure the axes and legend\n    fig.set_size_inches(10, len(axes) * 4)\n    axes[0].set_ylabel(\"Control Error\")\n    axes[1].set_ylabel(\"Damping Time\")\n    for ax in axes:\n        ax.grid()\n        ax.set_yscale(\"log\")\n    if x_label:\n        # Bottom plot gets x label\n        axes[1].set_xlabel(x_label)\n    if title:\n        fig.suptitle(title)\n    axes[1].legend(loc=\"center left\", bbox_to_anchor=(1.01, 1.1))\n    return fig\n\n\n@click.command(name=\"control-system\", help=plot_control_system.__doc__)\n@click.argument(\n    \"reduction_files\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=-1,\n    required=True,\n)\n@click.option(\n    \"--with-shape/--without-shape\",\n    default=True,\n    show_default=True,\n    help=\"Wether or not to plot shape control.\",\n)\n@click.option(\n    \"--shape-l_max\",\n    \"-l\",\n    type=int,\n    default=2,\n    show_default=True,\n    help=(\n        \"The max number of spherical harmonics to show on the plot. Since\"\n        \" higher ell can have a lot of components, it may be desirable to show\"\n        \" fewer components. Never plots l=0,1 since we don't control these\"\n        \" components. Only used if '--with-shape'.\"\n    ),\n)\n@click.option(\n    \"--show-all-m\",\n    is_flag=True,\n    default=False,\n    show_default=True,\n    help=(\n        \"When plotting shape control, for a given ell, plot all m components.\"\n        \" Default is, for a given ell, to plot the L2 norm over all the m\"\n        \" components. Only used if '--with-shape'.\"\n    ),\n)\n# Plotting options\n@click.option(\n    \"--x-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the x-axis.\",\n)\n@click.option(\n    \"--x-label\",\n    help=\"The label on the x-axis.\",\n)\n@click.option(\n    \"--title\",\n    \"-t\",\n    help=\"Title of the graph.\",\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_control_system_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    return plot_control_system(**kwargs)\n\n\nif __name__ == \"__main__\":\n    plot_control_system_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotDatFile.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\n\nimport click\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport rich\n\nfrom spectre.IO.H5 import available_subfiles\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef parse_functions(ctx, param, all_values):\n    \"\"\"Parse function names and their labels\n\n    Functions and their labels can be specified as key-value pairs such as\n    'Error(ScalarField)=$L_2(\\\\phi)$'. Remember to wrap the key-value pair in\n    quotes on the command line to avoid issues with special characters or\n    spaces.\n    \"\"\"\n    if all_values is None:\n        return {}\n    functions_and_labels = {}\n    for value in all_values:\n        key_and_value = value.split(\"=\")\n        if len(key_and_value) == 1:\n            # No label specified, use name of function as label\n            functions_and_labels[key_and_value[0]] = key_and_value[0]\n        elif len(key_and_value) == 2:\n            functions_and_labels[key_and_value[0]] = key_and_value[1]\n        else:\n            raise click.BadParameter(\n                f\"The value of '{value}' could not be parsed as a key-value \"\n                \"pair. It should have a single '=' or none.\"\n            )\n    return functions_and_labels\n\n\n@click.command(name=\"dat\")\n@click.argument(\n    \"h5_file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.option(\n    \"--subfile-name\",\n    \"-d\",\n    help=\"The dat subfile to read.  [required]\",\n)\n@click.option(\n    \"--legend-only\",\n    \"-l\",\n    is_flag=True,\n    help=\"Print out the available quantities and exit.\",\n)\n@click.option(\n    \"--function\",\n    \"-y\",\n    \"functions\",\n    multiple=True,\n    callback=parse_functions,\n    help=(\n        \"The quantity to plot. Can be specified multiple times to plot several\"\n        \" quantities on a single figure. If unspecified, list all available\"\n        \" quantities and exit. Labels of quantities can be specified as\"\n        \" key-value pairs such as 'Error(Psi)=$L_2(\\\\psi)$'. Remember to wrap\"\n        \" the key-value pair in quotes on the command line to avoid issues with\"\n        \" special characters or spaces.\"\n    ),\n)\n@click.option(\n    \"--x-axis\",\n    \"-x\",\n    help=\"Select the column in the dat file uses as the x-axis in the plot.\",\n    show_default=\"first column in the dat file\",\n)\n# Plotting options\n@click.option(\n    \"--x-label\",\n    help=\"The label on the x-axis.\",\n    show_default=\"name of the x-axis column\",\n)\n@click.option(\n    \"--y-label\",\n    required=False,\n    help=\"The label on the y-axis.\",\n    show_default=\"no label\",\n)\n@click.option(\"--x-logscale\", is_flag=True, help=\"Set the x-axis to log scale.\")\n@click.option(\"--y-logscale\", is_flag=True, help=\"Set the y-axis to log scale.\")\n@click.option(\n    \"--x-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the x-axis.\",\n)\n@click.option(\n    \"--y-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the y-axis.\",\n)\n@click.option(\n    \"--title\", \"-t\", help=\"Title of the graph.\", show_default=\"subfile name\"\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_dat_command(\n    h5_file,\n    subfile_name,\n    legend_only,\n    functions,\n    x_axis,\n    x_label,\n    y_label,\n    x_logscale,\n    y_logscale,\n    x_bounds,\n    y_bounds,\n    title,\n):\n    \"\"\"Plot columns in '.dat' datasets in H5 files\"\"\"\n    with h5py.File(h5_file, \"r\") as h5file:\n        # Print available subfiles and exit\n        if not subfile_name:\n            raise RequiredChoiceError(\n                (\n                    \"Specify '--subfile-name' / '-d' to select a\"\n                    \" subfile containing data to plot.\"\n                ),\n                choices=available_subfiles(h5file, extension=\".dat\"),\n            )\n\n        # Open subfile\n        if not subfile_name.endswith(\".dat\"):\n            subfile_name += \".dat\"\n        dat_file = h5file.get(subfile_name)\n        if dat_file is None:\n            raise RequiredChoiceError(\n                f\"Unable to open dat subfile '{subfile_name}'.\",\n                choices=available_subfiles(h5file, extension=\".dat\"),\n            )\n\n        # Read legend from subfile\n        legend = list(dat_file.attrs[\"Legend\"])\n\n        # Select x-axis\n        if x_axis is None:\n            x_axis = legend[0]\n        elif x_axis not in legend:\n            raise RequiredChoiceError(\n                f\"Unknown x-axis '{x_axis}'.\", choices=legend\n            )\n\n        # Print legend and exit if requested\n        if legend_only or not functions:\n            import rich.table\n\n            rich.print(f\"{len(legend)} columns x {len(dat_file)} data points:\")\n            table = rich.table.Table(show_header=False, box=None)\n            for i, function in enumerate(legend):\n                table.add_row(\n                    # Is the quantity selected as x or y?\n                    (\n                        \"x\"\n                        if function == x_axis\n                        else \"y\" if function in functions else \"\"\n                    ),\n                    # Name of the quantity\n                    function,\n                    # Value bounds\n                    f\"[{np.min(dat_file[:, i]):g}, {np.max(dat_file[:, i]):g}]\",\n                    # Highlight selected quantities\n                    style=(\n                        \"bold\"\n                        if function == x_axis or function in functions\n                        else None\n                    ),\n                )\n            rich.print(table)\n            return\n\n        # Select plotting parameters. Any further customization of the plotting\n        # style can be done with a stylesheet.\n        plot_kwargs = dict(\n            color=\"black\" if len(functions) == 1 else None,\n            marker=\".\" if len(dat_file) < 20 else None,\n        )\n\n        # Plot the selected quantities\n        for function, label in functions.items():\n            if function not in legend:\n                raise RequiredChoiceError(\n                    f\"Unknown function '{function}'.\", choices=legend\n                )\n\n            plt.plot(\n                dat_file[:, legend.index(x_axis)],\n                dat_file[:, legend.index(function)],\n                label=label,\n                **plot_kwargs,\n            )\n\n    # Configure the axes\n    if y_logscale:\n        plt.yscale(\"log\")\n    if x_logscale:\n        plt.xscale(\"log\")\n    plt.xlabel(x_label if x_label else x_axis)\n    if y_label:\n        plt.ylabel(y_label)\n    plt.legend()\n    if x_bounds:\n        plt.xlim(*x_bounds)\n    if y_bounds:\n        plt.ylim(*y_bounds)\n    plt.title(title if title else subfile_name[:-4])\n\n\nif __name__ == \"__main__\":\n    plot_dat_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotEccentricityControl.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\nfrom typing import Sequence, Union\n\nimport click\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport yaml\nfrom matplotlib.colors import LogNorm\nfrom scipy.interpolate import CloughTocher2DInterpolator\n\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef plot_eccentricity_control(\n    ecc_params_files: Sequence[Union[str, Path]],\n):\n    \"\"\"Plot eccentricity control iterations.\n\n    \\f\n    Arguments:\n      ecc_params_files: YAML files containing the output of\n        'eccentricity_control_params()'.\n\n    Returns:\n      The figure containing the plot.\n    \"\"\"\n    # Load the eccentricity parameters\n    ecc_params = []\n    for ecc_params_file in ecc_params_files:\n        with open(ecc_params_file, \"r\") as open_file:\n            ecc_params.append(yaml.safe_load(open_file))\n    ecc_params = pd.DataFrame(ecc_params)\n\n    # Set up the figure\n    fig = plt.figure(figsize=(4.5, 4))\n    plt.xlabel(r\"Angular velocity $\\Omega_0$\")\n    plt.ylabel(r\"Expansion $\\dot{a}_0$\")\n    plt.grid(zorder=0)\n    plt.ticklabel_format(axis=\"both\", style=\"sci\", scilimits=(0, 0))\n\n    # Plot measured eccentricity contours\n    ecc_norm = LogNorm(vmin=1e-4, vmax=1)\n    if len(ecc_params) > 2:\n        ecc_intrp = CloughTocher2DInterpolator(\n            ecc_params[[\"Omega0\", \"Adot0\"]],\n            np.log10(ecc_params[\"Eccentricity\"]),\n            rescale=True,\n        )\n        x, y = np.meshgrid(\n            np.linspace(*ecc_params[\"Omega0\"].agg([\"min\", \"max\"]), 200),\n            np.linspace(*ecc_params[\"Adot0\"].agg([\"min\", \"max\"]), 200),\n        )\n        z = ecc_intrp(x, y)\n        ecc_contours = plt.contourf(\n            x,\n            y,\n            10**z,\n            levels=10.0 ** np.arange(-4, 1),\n            norm=ecc_norm,\n            cmap=\"Blues\",\n            zorder=10,\n        )\n    else:\n        ecc_contours = plt.cm.ScalarMappable(\n            norm=ecc_norm,\n            cmap=\"Blues\",\n        )\n    plt.colorbar(ecc_contours, label=\"Eccentricity\", ax=plt.gca())\n    cbar_ax = fig.axes[1]\n    plt.gca().use_sticky_edges = False\n\n    # Plot the path through parameter space\n    plt.plot(\n        ecc_params[\"Omega0\"],\n        ecc_params[\"Adot0\"],\n        color=\"black\",\n        zorder=20,\n    )\n\n    # Place a marker at each iteration\n    scatter_kwargs = dict(\n        color=\"white\",\n        marker=\"o\",\n        linewidth=1,\n        edgecolor=\"black\",\n        s=100,\n        zorder=25,\n    )\n    annotate_kwargs = dict(\n        textcoords=\"offset points\",\n        xytext=(0, -0.5),\n        ha=\"center\",\n        va=\"center\",\n        fontsize=8,\n        zorder=30,\n    )\n    plt.scatter(ecc_params[\"Omega0\"], ecc_params[\"Adot0\"], **scatter_kwargs)\n    cbar_ax.scatter(\n        np.repeat(0.5, len(ecc_params)),\n        ecc_params[\"Eccentricity\"],\n        **scatter_kwargs,\n    )\n    for i in range(len(ecc_params)):\n        plt.annotate(\n            f\"{i}\",\n            ecc_params.iloc[i][[\"Omega0\", \"Adot0\"]],\n            **annotate_kwargs,\n        )\n        cbar_ax.annotate(\n            f\"{i}\", (0.5, ecc_params[\"Eccentricity\"][i]), **annotate_kwargs\n        )\n\n    # Plot location of the next iteration\n    plt.plot(\n        ecc_params.iloc[-1][[\"Omega0\", \"NewOmega0\"]],\n        ecc_params.iloc[-1][[\"Adot0\", \"NewAdot0\"]],\n        color=\"black\",\n        linestyle=\"dotted\",\n        zorder=20,\n    )\n    plt.scatter(\n        ecc_params.iloc[-1][\"NewOmega0\"],\n        ecc_params.iloc[-1][\"NewAdot0\"],\n        color=\"black\",\n        marker=\"o\",\n        s=30,\n        zorder=40,\n    )\n    return fig\n\n\n@click.command(\n    name=\"eccentricity-control\", help=plot_eccentricity_control.__doc__\n)\n@click.argument(\n    \"ecc_params_files\",\n    nargs=-1,\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_eccentricity_control_command(**kwargs):\n    return plot_eccentricity_control(**kwargs)\n\n\nif __name__ == \"__main__\":\n    plot_eccentricity_control_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotEllipticConvergence.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport datetime\nimport logging\nfrom typing import List\n\nimport click\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nfrom matplotlib.ticker import MaxNLocator\n\nfrom spectre.IO.H5 import available_subfiles, to_dataframe\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef split_iteration_sequence(data: pd.DataFrame) -> List[pd.DataFrame]:\n    \"\"\"Split a dataframe where its index is not strictly increasing.\n\n    Returns a list of dataframes, where each has a strictly increasing index.\n    \"\"\"\n    split_indices = np.split(\n        np.arange(len(data)), np.nonzero(np.diff(data.index) < 1)[0] + 1\n    )\n    return [data.iloc[split_index] for split_index in split_indices]\n\n\ndef plot_elliptic_convergence(\n    h5_file,\n    fig=None,\n    linear_residuals_subfile_name=\"GmresResiduals.dat\",\n    nonlinear_residuals_subfile_name=\"NewtonRaphsonResiduals.dat\",\n):\n    \"\"\"Plot elliptic solver convergence\n\n    Arguments:\n      h5_file: The H5 reductions file.\n      fig: Optional. The matplotlib figure to plot in.\n      linear_residuals_subfile_name: The name of the subfile containing the\n        linear solver residuals.\n      nonlinear_residuals_subfile_name: The name of the subfile containing the\n        nonlinear solver residuals.\n    \"\"\"\n    with h5py.File(h5_file, \"r\") as open_h5file:\n        if not linear_residuals_subfile_name in open_h5file:\n            all_subfiles = available_subfiles(open_h5file, extension=\".dat\")\n            raise ValueError(\n                \"Could not find the linear residuals subfile\"\n                f\" '{linear_residuals_subfile_name}' in the H5 file. Available\"\n                f\" subfiles: {all_subfiles}\"\n            )\n        linear_residuals = split_iteration_sequence(\n            to_dataframe(open_h5file[linear_residuals_subfile_name]).set_index(\n                \"Iteration\"\n            )\n        )\n        nonlinear_residuals = (\n            split_iteration_sequence(\n                to_dataframe(\n                    open_h5file[nonlinear_residuals_subfile_name]\n                ).set_index(\"Iteration\")\n            )\n            if nonlinear_residuals_subfile_name in open_h5file\n            else None\n        )\n    cumulative_linsolv_iterations = [0] + list(\n        np.cumsum([len(l) - 1 for l in linear_residuals])\n    )\n    norm = (\n        nonlinear_residuals\n        if nonlinear_residuals is not None\n        else linear_residuals\n    )[0][\"Residual\"].iloc[0]\n    # Plot nonlinear solver residuals\n    if fig is None:\n        fig = plt.figure()\n    ax_residual, ax_time = fig.subplots(\n        nrows=2, ncols=1, sharex=True, gridspec_kw={\"height_ratios\": [3, 1]}\n    )\n    if nonlinear_residuals is not None:\n        m = 0\n        for i, residuals in enumerate(nonlinear_residuals):\n            ax_residual.plot(\n                cumulative_linsolv_iterations[m : m + len(residuals)],\n                residuals[\"Residual\"] / norm,\n                color=\"black\",\n                ls=\"dotted\",\n                marker=\".\",\n                label=\"Nonlinear solver\" if i == 0 else None,\n            )\n            if \"Walltime\" in residuals:\n                ax_time.plot(\n                    cumulative_linsolv_iterations[m : m + len(residuals) - 1],\n                    np.diff(residuals[\"Walltime\"]),\n                    color=\"black\",\n                    ls=\"dotted\",\n                    marker=\".\",\n                )\n            m += len(residuals) - 1\n    # Plot linear solver residuals\n    for i, residuals in enumerate(linear_residuals):\n        ax_residual.plot(\n            residuals.index + cumulative_linsolv_iterations[i],\n            residuals[\"Residual\"] / norm,\n            color=\"black\",\n            label=\"Linear solver\" if i == 0 else None,\n            marker=\".\" if len(residuals) < 20 else None,\n        )\n        if \"Walltime\" in residuals:\n            ax_time.plot(\n                residuals.index[:-1] + cumulative_linsolv_iterations[i],\n                np.diff(residuals[\"Walltime\"]),\n                color=\"black\",\n                marker=\".\" if len(residuals) < 20 else None,\n            )\n    # Annotate time of last linear solver iteration\n    if \"Walltime\" in residuals:\n        last_time = linear_residuals[-1].iloc[-1][\"Walltime\"]\n        ax_time.annotate(\n            f\"Walltime: {datetime.timedelta(seconds=round(last_time))}\",\n            xy=(1, 1),\n            xycoords=\"axes fraction\",\n            xytext=(0, 3),\n            textcoords=\"offset points\",\n            ha=\"right\",\n            va=\"bottom\",\n            fontsize=9,\n        )\n    else:\n        ax_time.annotate(\n            \"No walltime data\",\n            xy=(1, 1),\n            xycoords=\"axes fraction\",\n            xytext=(0, 3),\n            textcoords=\"offset points\",\n            ha=\"right\",\n            va=\"bottom\",\n            fontsize=9,\n        )\n\n    # Configure the axes\n    ax_residual.set_yscale(\"log\")\n    ax_residual.grid()\n    ax_residual.legend()\n    ax_residual.set_ylabel(\"Relative residual\")\n    ax_time.set_yscale(\"log\")\n    ax_time.grid()\n    ax_time.set_ylabel(\"Walltime per iteration [s]\")\n    ax_time.set_xlabel(\"Cumulative linear solver iteration\")\n    # Allow only integer ticks for the x-axis\n    ax_time.xaxis.set_major_locator(MaxNLocator(integer=True))\n    return fig\n\n\n@click.command(name=\"elliptic-convergence\")\n@click.argument(\n    \"h5_file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.option(\n    \"--linear-residuals-subfile-name\",\n    help=\"The name of the subfile containing the linear solver residuals\",\n    default=\"GmresResiduals.dat\",\n    show_default=True,\n)\n@click.option(\n    \"--nonlinear-residuals-subfile-name\",\n    help=\"The name of the subfile containing the nonlinear solver residuals\",\n    default=\"NewtonRaphsonResiduals.dat\",\n    show_default=True,\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_elliptic_convergence_command(**kwargs):\n    \"\"\"Plot elliptic solver convergence\"\"\"\n    _rich_traceback_guard = True  # Hide traceback until here\n    return plot_elliptic_convergence(**kwargs)\n\n\nif __name__ == \"__main__\":\n    plot_elliptic_convergence_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotMemoryMonitors.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\nimport logging\nimport os\nfrom math import inf\nfrom typing import Optional, Sequence\n\nimport click\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom spectre.IO.H5 import available_subfiles, to_dataframe\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\n@click.command(name=\"memory-monitors\")\n@click.argument(\n    \"reduction_files\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=-1,\n    required=True,\n)\n# Plotting options\n@click.option(\n    \"--use-mb/--use-gb\",\n    default=False,\n    show_default=True,\n    help=\"Plot the y-axis in Megabytes or Gigabytes\",\n)\n@click.option(\n    \"--x-label\",\n    help=\"The label on the x-axis.\",\n    show_default=\"name of the x-axis column\",\n)\n@click.option(\n    \"--x-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the x-axis.\",\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_memory_monitors_command(\n    reduction_files: Sequence[str],\n    use_mb: bool,\n    x_label: Optional[str],\n    x_bounds: Optional[Sequence[float]],\n):\n    \"\"\"\n    Plot the memory usage of a simulation from the MemoryMonitors data in the\n    Reductions H5 file.\n\n    This tool assumes there is a group in each of the \"reduction-files\" with the\n    path \"/MemoryMonitors/\" that holds dat files for each parallel component\n    that was monitored.\n\n    Note that the totals plotted here are not necessary the total memory usage\n    of the simulation. The memory monitors only capture what is inside\n    'pup'functions. Any memory that cannot be captured by a 'pup' function will\n    not by represented by this plot.\n    \"\"\"\n\n    # Given an h5 file, make sure that the \"MemoryMonitors\" group exists. Then\n    # return the list of subfiles in that group\n    def check_memory_monitor_dir(h5_filename: str):\n        h5file = h5py.File(h5_filename, \"r\")\n        memory_monitor_dir = h5file.get(\"MemoryMonitors\")\n        if memory_monitor_dir is None:\n            raise RequiredChoiceError(\n                (\n                    \"Unable to open group 'MemoryMonitors' from h5 file\"\n                    f\" {h5_filename}.\"\n                ),\n                choices=available_subfiles(h5file, extension=\".dat\"),\n            )\n\n        return list(memory_monitor_dir.keys())\n\n    # Given an h5 file and a subfile, make sure that the subfile exists inside\n    # the h5 file. Then return the subfile as a DataFrame (with \"Time\" as the\n    # index)\n    def check_memory_monitor_file(\n        h5_filename: str, h5file: h5py.File, subfile_name: str\n    ):\n        subfile_path = f\"/MemoryMonitors/{subfile_name}\"\n        subfile = h5file.get(subfile_path)\n        if subfile_path is None:\n            raise RequiredChoiceError(\n                (\n                    f\"Unable to open memory subfile '{subfile_path}'\"\n                    f\" from h5 file {h5_filename}.\"\n                ),\n                choices=available_subfiles(h5file, extension=\".dat\"),\n            )\n\n        return to_dataframe(subfile).set_index(\"Time\")\n\n    # Given a DataFrame, sum all the columns that list totals for each node\n    def total_over_nodes(df: pd.DataFrame):\n        cols_to_sum = [\n            col\n            for col in df.columns\n            if col == \"Size (MB)\" or \"Size on node\" in col\n        ]\n        return df[cols_to_sum].sum(axis=1)\n\n    # Get a list of all components that we have monitored from the first\n    # reductions file\n    memory_filenames = check_memory_monitor_dir(reduction_files[0])\n\n    # Open every h5file. For each h5file, turn each subfile into a DataFrame.\n    # Then concat all DataFrames together into one that's indexed by the\n    # subfile/component name.\n    totals_df = pd.DataFrame()\n    for reduction_file in reduction_files:\n        h5file = h5py.File(reduction_file)\n        local_totals_df = pd.DataFrame()\n\n        for subfile_name in memory_filenames:\n            df = check_memory_monitor_file(reduction_file, h5file, subfile_name)\n            local_totals_df[subfile_name] = total_over_nodes(df)\n\n        totals_df = pd.concat([totals_df, local_totals_df])\n\n    # Restrict to x-bounds if there are any\n    for subfile in memory_filenames:\n        if x_bounds is not None:\n            totals_df[subfile] = totals_df[subfile][\n                (totals_df.index >= x_bounds[0])\n                & (totals_df.index <= x_bounds[1])\n            ]\n\n    # Need .dat because all other components have that extension\n    the_rest_str = \"The Rest.dat\"\n\n    # Sum all the columns that list totals for each node to get a grand total\n    # memory usage. Also see if any columns are less than 1% of this total. If\n    # they are, group them in \"the rest\".\n    grand_total = totals_df.sum(axis=1)\n    the_rest_threshold = 0.01 * grand_total.mean()\n    components_to_plot = list(\n        totals_df.columns[totals_df.max() > the_rest_threshold]\n    )\n    the_rest_columns = list(set(totals_df.columns) - set(components_to_plot))\n    # Only plot the rest of we actually have it\n    if len(the_rest_columns) != 0:\n        totals_df[the_rest_str] = totals_df[the_rest_columns].sum(axis=1)\n        components_to_plot.append(the_rest_str)\n\n    # For plotting in MB vs GB\n    divisor = 1.0 if use_mb else 1000.0\n\n    # Start plotting\n    fig = plt.figure()\n    ax = fig.add_subplot(111)\n\n    # Plot the total\n    ax.plot(\n        totals_df.index,\n        grand_total / divisor,\n        color=\"black\",\n        label=\"Total\",\n    )\n\n    # Determine plotting order\n    maxes = list(totals_df[components_to_plot].max())\n    components_to_plot = np.array(components_to_plot)[np.argsort(maxes)[::-1]]\n\n    # Plot the individual components\n    for component in components_to_plot:\n        ax.plot(\n            totals_df.index,\n            totals_df[component] / divisor,\n            linewidth=0.2,\n            # Remove .dat extension\n            label=component[:-4],\n        )\n\n    gb_or_mb = \"MB\" if use_mb else \"GB\"\n    plt.title(f\"Total Memory Usage ({gb_or_mb})\")\n    if x_label is not None:\n        ax.set_xlabel(x_label)\n    # The lines in the legend are a bit small because of the plot linewidth,\n    # so make the legend lines a bit bigger\n    leg = plt.legend(\n        loc=\"center left\",\n        bbox_to_anchor=(1, 0.5),\n    )\n    for line in leg.get_lines():\n        line.set_linewidth(1.0)\n\n\nif __name__ == \"__main__\":\n    plot_memory_monitors_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotPowerMonitors.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom itertools import cycle\nfrom typing import Iterable, Optional, Sequence, Tuple, Union\n\nimport click\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom matplotlib.colors import LinearSegmentedColormap\nfrom matplotlib.ticker import MaxNLocator\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.Domain import Domain, deserialize_domain\nfrom spectre.IO.H5 import open_volfiles, open_volfiles_command, parse_point\nfrom spectre.IO.H5.IterElements import iter_elements, stripped_element_name\nfrom spectre.NumericalAlgorithms.LinearOperators import power_monitors\nfrom spectre.Spectral import Basis\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef find_block_or_group(\n    block_id: int,\n    block_or_group_names: Sequence[str],\n    domain: Union[Domain[1], Domain[2], Domain[3]],\n) -> Optional[int]:\n    \"\"\"Find entry in 'block_or_group_names' that corresponds to the 'block_id'\"\"\"\n    block_name = domain.blocks[block_id].name\n    for i, name in enumerate(block_or_group_names):\n        if name == block_name:\n            return i\n        if (\n            name in domain.block_groups\n            and block_name in domain.block_groups[name]\n        ):\n            return i\n    return None\n\n\ndef plot_power_monitors(\n    volfiles: Union[spectre_h5.H5Vol, Iterable[spectre_h5.H5Vol]],\n    obs_id: Optional[int],\n    tensor_components: Sequence[str],\n    block_or_group_names: Sequence[str],\n    domain: Union[Domain[1], Domain[2], Domain[3]],\n    dimension_labels: Sequence[str] = [r\"$\\xi$\", r\"$\\eta$\", r\"$\\zeta$\"],\n    element_patterns: Optional[Sequence[str]] = None,\n    skip_filtered_modes: int = 0,\n    figsize: Optional[Tuple[float, float]] = None,\n):\n    plot_over_time = obs_id is None\n    # One column per block or group\n    num_cols = len(block_or_group_names)\n    # One row per dimension if plotted over time to declutter the plots\n    num_rows = domain.dim if plot_over_time else 1\n    fig, axes = plt.subplots(\n        nrows=num_rows,\n        ncols=num_cols,\n        figsize=figsize or (num_cols * 4, num_rows * 4),\n        sharey=True,\n        sharex=True,\n        squeeze=False,\n    )\n\n    # Evaluate property cycles (by default this is just 'color'). We do multiple\n    # plotting commands (at least one per element), so we don't want matplotlib\n    # to cycle through the properties at every plotting command.\n    prop_cycle = {\n        key: cycle(values)\n        for key, values in plt.rcParams[\"axes.prop_cycle\"].by_key().items()\n    }\n    props_dim = {\n        d: {key: next(values) for key, values in prop_cycle.items()}\n        for d in range(domain.dim)\n    }\n\n    # Collect data for each subplot\n    if plot_over_time:\n        all_mode_time_series = {\n            subplot_index: dict() for subplot_index in range(num_cols)\n        }\n    else:\n        num_elements = np.zeros(num_cols, dtype=int)\n        max_error = np.zeros((num_cols, domain.dim))\n\n    shown_dtype_warning_once = False\n    for element, tensor_data in iter_elements(\n        volfiles, obs_id, tensor_components, element_patterns=element_patterns\n    ):\n        # Skip FD elements because we can't compute power monitors for them\n        if any(\n            basis == Basis.FiniteDifference for basis in element.mesh.basis()\n        ):\n            continue\n\n        # Find the subplot for this element's block, or skip the element if its\n        # block wasn't selected\n        subplot_index = find_block_or_group(\n            element.id.block_id, block_or_group_names, domain\n        )\n        if subplot_index is None:\n            continue\n\n        # Compute power monitors and take L2 norm over tensor components\n        all_modes = [\n            np.zeros(element.mesh.extents(d) - skip_filtered_modes)\n            for d in range(element.dim)\n        ]\n        if tensor_data.dtype != np.float64:\n            if not shown_dtype_warning_once:\n                logger.warning(\n                    \"Tensor data is not double precision. Power monitors\"\n                    \" will be inaccurate below the precision of the data.\"\n                )\n                shown_dtype_warning_once = True\n            tensor_data = tensor_data.astype(np.float64)\n        for component in tensor_data:\n            modes = power_monitors(DataVector(component), element.mesh)\n            for d, modes_dim in enumerate(modes):\n                num_modes = len(modes_dim) - skip_filtered_modes\n                all_modes[d] += np.array(modes_dim)[:num_modes] ** 2\n        for d in range(element.dim):\n            all_modes[d] = np.sqrt(all_modes[d])\n\n        if plot_over_time:\n            # Collect time series of modes\n            all_mode_time_series[subplot_index].setdefault(\n                element.id, []\n            ).append((element.time, all_modes))\n        else:\n            # Plot modes directly\n            ax = axes[0][subplot_index]\n            for d, modes_dim in enumerate(all_modes):\n                ax.semilogy(modes_dim, **props_dim[d], zorder=30 + d)\n                ax.scatter(\n                    len(modes_dim) - 1,\n                    modes_dim[-1],\n                    marker=\".\",\n                    color=props_dim[d].get(\"color\", \"black\"),\n                    zorder=30 + d,\n                )\n                # Collect reduction data\n                # - We estimate the truncation error by just taking the highest\n                #   mode. This won't work well with filtering and should be\n                #   improved on the C++ side.\n                max_error[subplot_index][d] = max(\n                    max_error[subplot_index][d], all_modes[d][-1]\n                )\n            num_elements[subplot_index] += 1\n\n    if plot_over_time:\n        # Plot mode timeseries\n        max_num_modes = np.max(\n            np.array(\n                [\n                    [len(modes) for modes in all_modes]\n                    for subplot_index in range(num_cols)\n                    for mode_time_series in all_mode_time_series[\n                        subplot_index\n                    ].values()\n                    for _, all_modes in mode_time_series\n                ]\n            ),\n            axis=0,\n        )\n        mode_cmap = [\n            LinearSegmentedColormap.from_list(\n                \"Modes\",\n                [\"black\", props_dim[d].get(\"color\", \"black\")],\n                N=max_num_modes[d],\n            )\n            for d in range(domain.dim)\n        ]\n        for subplot_index in range(num_cols):\n            for element_id, mode_time_series in all_mode_time_series[\n                subplot_index\n            ].items():\n                times = np.array([time for time, _ in mode_time_series])\n                for d in range(domain.dim):\n                    ax = axes[d][subplot_index]\n                    for mode in range(max_num_modes[d]):\n                        mode_time_series_i = np.array(\n                            [\n                                (\n                                    all_modes[d][mode]\n                                    if len(all_modes[d]) > mode\n                                    else np.nan\n                                )\n                                for _, all_modes in mode_time_series\n                            ]\n                        )\n                        color = mode_cmap[d](mode / (max_num_modes[d] - 1))\n                        ax.semilogy(\n                            times,\n                            mode_time_series_i,\n                            color=color,\n                            zorder=30 + d,\n                        )\n        # Plot colorbars as legend\n        import matplotlib.cm\n        import matplotlib.colors\n\n        for d in range(domain.dim):\n            colorbar = plt.colorbar(\n                matplotlib.cm.ScalarMappable(\n                    norm=matplotlib.colors.Normalize(0, max_num_modes[d]),\n                    cmap=mode_cmap[d],\n                ),\n                ax=axes[d],\n                ticks=list(range(max_num_modes[d])),\n                label=dimension_labels[d] + \" Mode\",\n            )\n            colorbar.ax.invert_yaxis()\n    else:\n        # Annotate the max truncation error. Also serves as a legend.\n        for subplot_index, ax in enumerate(axes[0]):\n            for d in range(domain.dim):\n                ax.axhline(\n                    max_error[subplot_index][d], **props_dim[d], zorder=20 + d\n                )\n                ax.annotate(\n                    dimension_labels[d],\n                    xy=(0, max_error[subplot_index][d]),\n                    xytext=((2 * d + 0.5) * plt.rcParams[\"font.size\"], 0),\n                    textcoords=\"offset points\",\n                    ha=\"left\",\n                    va=\"center\",\n                    bbox=dict(\n                        fc=\"white\",\n                        ec=props_dim[d].get(\"color\", \"black\"),\n                        pad=2.0,\n                    ),\n                    zorder=40 + d,\n                )\n\n    # Set plot titles\n    for subplot_index, ax in enumerate(axes[0]):\n        ax.set_title(block_or_group_names[subplot_index], loc=\"left\")\n        num_elements_i = (\n            len(all_mode_time_series[subplot_index])\n            if plot_over_time\n            else num_elements[subplot_index]\n        )\n        ax.set_title(\n            f\"{num_elements_i} element\" + (\"\" if num_elements_i == 1 else \"s\"),\n            loc=\"right\",\n        )\n\n    for axes_row in axes:\n        for ax in axes_row:\n            # Draw grid lines\n            ax.grid(which=\"both\", zorder=0)\n            # Allow only integer ticks for modes\n            if not plot_over_time:\n                ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n\n    # Add y-labels to the leftmost subplots\n    if plot_over_time:\n        for d, ax in enumerate(axes):\n            ax[0].set_ylabel(\n                r\"Power monitors $P_{q_\" + dimension_labels[d].strip(\"$\") + \"}$\"\n            )\n    else:\n        axes[0][0].set_ylabel(r\"Power monitors $P_{q_{\\hat{\\imath}}}$\")\n\n    # Add x-label spanning all subplots\n    ax_colspan = fig.add_subplot(111, frameon=False)\n    ax_colspan.tick_params(\n        labelcolor=\"none\", top=False, bottom=False, left=False, right=False\n    )\n    ax_colspan.grid(False)\n    ax_colspan.set_xlabel(\"Time\" if plot_over_time else \"Mode number\")\n\n\n@click.command(name=\"power-monitors\")\n@open_volfiles_command(obs_id_required=False, multiple_vars=True)\n@click.option(\n    \"--list-blocks\",\n    is_flag=True,\n    help=\"Print available blocks and block groups and exit.\",\n)\n@click.option(\n    \"--block\",\n    \"-b\",\n    \"block_or_group_names\",\n    multiple=True,\n    help=(\n        \"Name of block or block group to analyze. \"\n        \"Can be specified multiple times to plot several block(groups) at once.\"\n    ),\n)\n@click.option(\n    \"--elements\",\n    \"-e\",\n    \"element_patterns\",\n    multiple=True,\n    help=(\n        \"Include only elements that match the specified glob \"\n        \"pattern, like 'B*,(L1I*,L0I0,L0I0)'. \"\n        \"Can be specified multiple times, in which case elements \"\n        \"are included that match _any_ of the specified \"\n        \"patterns. If unspecified, include all elements in the blocks.\"\n    ),\n)\n@click.option(\n    \"--list-elements\",\n    is_flag=True,\n    help=(\n        \"List all elements in the specified blocks subject to \"\n        \"'--elements' / '-e' patterns.\"\n    ),\n)\n@click.option(\n    \"--over-time\", \"-T\", is_flag=True, help=\"Plot power monitors over time.\"\n)\n@click.option(\n    \"--skip-filtered-modes\",\n    type=int,\n    default=0,\n    help=(\n        \"Skip this number of highest modes. Useful if the highest modes are\"\n        \" filtered, zeroing them out.\"\n    ),\n)\n# Plotting options\n@click.option(\"--figsize\", nargs=2, type=float, help=\"Figure size in inches.\")\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_power_monitors_command(\n    h5_files,\n    subfile_name,\n    obs_id,\n    obs_time,\n    vars,\n    list_blocks,\n    block_or_group_names,\n    list_elements,\n    element_patterns,\n    over_time,\n    **kwargs,\n):\n    \"\"\"Plot power monitors from volume data\n\n    Reads volume data in the 'H5_FILES' and computes power monitors, which are\n    essentially the spectral modes in each dimension of the grid. They give an\n    indication how well the spectral expansion resolves fields on the grid.\n    Power monitors are computed for all tensor components selected with the\n    '--var' / '-y' option, and combined as an L2 norm.\n\n    One subplot is created for every selected '--block' / '-b'. This can be a\n    single block name, or a block group defined by the domain (such as all six\n    wedges in a spherical shell). The power monitors in every logical direction\n    of the grid are plotted for all elements in the block or block group. The\n    logical directions are labeled \"xi\", \"eta\" and \"zeta\", and their orientation\n    is defined by the coordinate maps in the domain. For example, see the\n    documentation of the 'Wedge' map to understand which logical direction is\n    radial in spherical shells.\n    \"\"\"\n    if over_time == (obs_id is not None):\n        raise click.UsageError(\n            \"Specify an observation '--step' or '--time', or specify\"\n            \" '--over-time' (but not both).\"\n        )\n\n    # Print available blocks and groups\n    open_h5_file = spectre_h5.H5File(h5_files[0], \"r\")\n    volfile = open_h5_file.get_vol(subfile_name)\n    dim = volfile.get_dimension()\n    domain = deserialize_domain[dim](volfile.get_domain())\n    all_block_groups = list(domain.block_groups.keys())\n    all_block_names = [block.name for block in domain.blocks]\n    if list_blocks:\n        import rich.columns\n\n        rich.print(rich.columns.Columns(all_block_groups + all_block_names))\n        return\n    elif not block_or_group_names:\n        raise RequiredChoiceError(\n            (\n                \"Specify '--block' / '-b' to select (possibly multiple) blocks\"\n                \" or block groups to analyze.\"\n            ),\n            choices=all_block_groups + all_block_names,\n        )\n    # Validate block and group names\n    for name in block_or_group_names:\n        if not (name in all_block_groups or name in all_block_names):\n            raise RequiredChoiceError(\n                f\"'{name}' matches no block or block group.\",\n                choices=all_block_groups + all_block_names,\n            )\n\n    # Print available elements IDs\n    if not element_patterns:\n        # Don't apply any filters when no element patterns were specified\n        element_patterns = None\n    if list_elements:\n        all_element_ids = sorted(\n            set(\n                element.id\n                for element in iter_elements(\n                    open_volfiles(h5_files, subfile_name, obs_id),\n                    obs_id,\n                    element_patterns=element_patterns,\n                )\n            )\n        )\n        # Print grouped by block\n        import rich.console\n\n        console = rich.console.Console()\n        for i, block_name in enumerate(block_or_group_names):\n            element_ids = [\n                stripped_element_name(element_id)\n                for element_id in all_element_ids\n                if find_block_or_group(\n                    element_id.block_id, block_or_group_names, domain\n                )\n                == i\n            ]\n            console.rule(\n                f\"[bold]{block_name}[/bold] ({len(element_ids)} elements)\"\n            )\n            console.print(rich.columns.Columns(element_ids))\n        return\n\n    # Close the H5 file because we're done with preprocessing\n    open_h5_file.close()\n\n    # Plot!\n    import rich.progress\n\n    progress = rich.progress.Progress(\n        rich.progress.TextColumn(\"[progress.description]{task.description}\"),\n        rich.progress.BarColumn(),\n        rich.progress.MofNCompleteColumn(),\n        rich.progress.TimeRemainingColumn(),\n        disable=(len(h5_files) == 1),\n    )\n    task_id = progress.add_task(\"Processing files\", total=len(h5_files))\n    volfiles_progress = progress.track(\n        open_volfiles(h5_files, subfile_name, obs_id), task_id=task_id\n    )\n    with progress:\n        plot_power_monitors(\n            volfiles_progress,\n            obs_id=obs_id,\n            tensor_components=vars,\n            domain=domain,\n            block_or_group_names=block_or_group_names,\n            element_patterns=element_patterns,\n            **kwargs,\n        )\n        progress.update(task_id, completed=len(h5_files))\n\n\nif __name__ == \"__main__\":\n    plot_power_monitors_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotSizeControl.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom typing import Optional, Sequence\n\nimport click\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom spectre.IO.H5 import available_subfiles, to_dataframe\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef plot_size_control(\n    reduction_files: Sequence[str],\n    object_label: str,\n    x_bounds: Optional[Sequence[float]] = None,\n    x_label: Optional[str] = None,\n    title: Optional[str] = None,\n):\n    \"\"\"\n    Plot diagnostic information regarding the Size control system.\n\n    This tool assumes there is a subfile in each of the \"reduction-files\" with\n    the path `/ControlSystems/Size{LABEL}/Diagnostics.dat`, where `{LABEL}` is\n    replaced with the \"object-label\" input option.\n    \"\"\"\n    object_label = (\n        \"\" if object_label.lower() == \"none\" else object_label.upper()\n    )\n\n    def check_diagnostics_file(h5_filename):\n        with h5py.File(h5_filename, \"r\") as h5file:\n            # See src/ControlSystem/ControlErrors/Size.cpp for path\n            diagnostics_file_name = (\n                f\"ControlSystems/Size{object_label}/Diagnostics.dat\"\n            )\n            diagnostics_data = h5file.get(diagnostics_file_name)\n            if diagnostics_data is None:\n                raise RequiredChoiceError(\n                    (\n                        \"Unable to open diagnostic file\"\n                        f\" '{diagnostics_file_name}' from h5 file\"\n                        f\" {h5_filename}.\"\n                    ),\n                    choices=available_subfiles(h5file, extension=\".dat\"),\n                )\n\n            return to_dataframe(diagnostics_data)\n\n    data = pd.concat(\n        check_diagnostics_file(reduction_file)\n        for reduction_file in reduction_files\n    )\n\n    # Restrict data to bounds\n    if x_bounds:\n        data = data[\n            (data[\"Time\"] >= x_bounds[0]) & (data[\"Time\"] <= x_bounds[1])\n        ]\n\n    # Set up plots\n    fig, axes = plt.subplots(7, 1, sharex=True)\n\n    # Plot the selected quantities. Groupings are based both on relationship\n    # of quantities and also relative scales of quantities\n    times = data[\"Time\"]\n    axes = list(axes)\n    # Damping times are sort of the odd ones out. Put it with control error but\n    # on a different y-axis\n    top1 = axes[0].plot(\n        times, np.abs(data[\"ControlError\"]), label=\"Control Error\"\n    )\n    axes.append(axes[0].twinx())\n    top2 = axes[-1].plot(\n        times, data[\"DampingTime\"], color=\"C1\", label=\"Damping time\"\n    )\n    axes[0].set_yscale(\"log\")\n    axes[-1].set_yscale(\"log\")\n    # Support an older version of control system output that didn't have the\n    # smoother timescale\n    if \"SmootherTimescale\" in data.columns:\n        top3 = axes[-1].plot(\n            times,\n            data[\"SmootherTimescale\"],\n            color=\"C1\",\n            linestyle=\"dashed\",\n            label=\"Smooth damping time\",\n        )\n        top_lines = top1 + top2 + top3\n    else:\n        top_lines = top1 + top2\n\n    top_labels = [l.get_label() for l in top_lines]\n    axes[0].tick_params(axis=\"y\", labelcolor=\"C0\")\n    axes[-1].tick_params(axis=\"y\", labelcolor=\"C1\")\n\n    # State\n    axes[1].plot(times, data[\"StateNumber\"], label=\"State\")\n    axes[1].set_ylim(0, 5)\n\n    # All delta R's\n    axes[2].plot(\n        times,\n        data[\"MinRelativeDeltaR\"],\n        label=\"Min relative Delta R\",\n        color=\"C0\",\n    )\n    axes[2].plot(\n        times,\n        data[\"AvgRelativeDeltaR\"],\n        label=\"Average relative Delta R\",\n        color=\"C1\",\n    )\n    axes[2].plot(\n        times,\n        data[\"MinDeltaR\"],\n        label=\"Min Delta R\",\n        color=\"C0\",\n        linestyle=\"dashed\",\n    )\n    axes[2].plot(\n        times,\n        data[\"AvgDeltaR\"],\n        label=\"Average Delta R\",\n        color=\"C1\",\n        linestyle=\"dashed\",\n    )\n\n    # Char speeds\n    axes[3].plot(times, data[\"MinCharSpeed\"], label=\"Min char speed\")\n    axes[3].plot(\n        times, data[\"MinComovingCharSpeed\"], label=\"Min comoving char speed\"\n    )\n    axes[3].plot(times, data[\"TargetCharSpeed\"], label=\"Target char speed\")\n\n    # Zero crossing predictors\n    axes[4].plot(\n        times, data[\"CharSpeedCrossingTime\"], label=\"Char speed crossing time\"\n    )\n    axes[4].plot(\n        times,\n        data[\"ComovingCharSpeedCrossingTime\"],\n        label=\"Comoving char speed crossing time\",\n    )\n    axes[4].plot(\n        times, data[\"DeltaRCrossingTime\"], label=\"Delta R crossing time\"\n    )\n    axes[4].set_yscale(\"log\")\n\n    # Lambda and horizon\n    axes[5].plot(times, data[\"FunctionOfTime\"], label=\"Function of time\")\n    axes[5].plot(times, data[\"HorizonCoef00\"], label=\"Horizon coef 00\")\n\n    # Time deriv of lambda and horizon\n    axes[6].plot(times, data[\"DtFunctionOfTime\"], label=\"dtFunction of time\")\n    axes[6].plot(\n        times, data[\"RawDtHorizonCoef00\"], label=\"Raw dtHorizon coef 00\"\n    )\n    axes[6].plot(\n        times, data[\"AveragedDtHorizonCoef00\"], label=\"Avg dtHorizon coef 00\"\n    )\n\n    # Configure the axes and legends\n    fig.set_size_inches(10, len(axes) * 2)\n    if x_label:\n        # Bottom plot. -1 is the second y-axis for top plot\n        axes[-2].set_xlabel(x_label)\n    if title:\n        plt.title(title)\n    for i in range(len(axes) - 1):\n        axes[i].grid()\n        if i == 0:\n            axes[i].legend(\n                top_lines,\n                top_labels,\n                loc=\"center left\",\n                bbox_to_anchor=(1.03, 0.5),\n            )\n        else:\n            axes[i].legend(loc=\"center left\", bbox_to_anchor=(1, 0.5))\n    return fig\n\n\n@click.command(name=\"size-control\", help=plot_size_control.__doc__)\n@click.argument(\n    \"reduction_files\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=-1,\n    required=True,\n)\n@click.option(\n    \"--object-label\",\n    \"-d\",\n    required=True,\n    help=(\n        \"Which object to plot. This is either 'A', 'B', or 'None'. 'None' is\"\n        \" used when there is only one black hole in the simulation.\"\n    ),\n)\n# Plotting options\n@click.option(\n    \"--x-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the x-axis.\",\n)\n@click.option(\n    \"--x-label\",\n    help=\"The label on the x-axis.\",\n)\n@click.option(\n    \"--title\",\n    \"-t\",\n    help=\"Title of the graph.\",\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_size_control_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    return plot_size_control(**kwargs)\n\n\nif __name__ == \"__main__\":\n    plot_size_control_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotSlice.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom typing import Sequence, Union\n\nimport click\nimport matplotlib.animation\nimport matplotlib.cm\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport rich\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.Exporter import ObservationId, interpolate_to_points\nfrom spectre.IO.H5 import (\n    list_observations,\n    open_volfiles,\n    open_volfiles_command,\n    parse_point,\n)\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef points_on_slice(\n    slice_origin: Sequence[float],\n    slice_extent: Sequence[float],\n    slice_normal: Sequence[float],\n    slice_up: Sequence[float],\n    num_points: Sequence[int],\n):\n    \"\"\"Returns points on a rectangular slice in 3D.\n\n    Parameters:\n      slice_origin: Coordinates of the center of the slice.\n      slice_extent: Coordinate extent in both directions of the slice (two\n        numbers).\n      slice_normal: Direction of the normal of the slice.\n      slice_up: Up-direction of the slice.\n      num_points: Number of points to return in each dimension (two numbers).\n\n    Returns: An array of shape (3, num_points[0], num_points[1]) with uniformly\n      spaced points on the slice.\n    \"\"\"\n    assert np.isclose(\n        np.dot(slice_up, slice_normal), 0.0\n    ), \"Up and normal vectors must be orthogonal\"\n    assert (\n        len(num_points) == 2\n    ), \"Specify two numbers for num_points (one per dimension of the slice)\"\n    assert (\n        len(slice_extent) == 2\n    ), \"Specify two numbers for slice_extent (one per dimension of the slice)\"\n    n = slice_normal / np.linalg.norm(slice_normal)\n    u = slice_up / np.linalg.norm(slice_up)\n    v = np.cross(u, n) * slice_extent[0]\n    u *= slice_extent[1]\n    lower_left = slice_origin - 0.5 * (u + v)\n    xx, yy = np.meshgrid(\n        np.linspace(0, 1, num_points[0]),\n        np.linspace(0, 1, num_points[1]),\n        indexing=\"ij\",\n    )\n    return (\n        lower_left[:, np.newaxis, np.newaxis]\n        + v[:, np.newaxis, np.newaxis] * xx[np.newaxis, :, :]\n        + u[:, np.newaxis, np.newaxis] * yy[np.newaxis, :, :]\n    )\n\n\ndef plot_slice(\n    h5_files,\n    subfile_name,\n    obs_id,\n    obs_time,\n    var_name,\n    slice_origin,\n    slice_extent,\n    slice_normal,\n    slice_up,\n    extrapolate_into_excisions=False,\n    num_samples=[200, 200],\n    num_threads=None,\n    title=None,\n    data_bounds=None,\n    animate=False,\n    interval=100,\n):\n    \"\"\"Plot variables on a slice through volume data\n\n    Interpolates the volume data in the H5_FILES to a slice and plots the\n    selected variables. You choose the slice by specifying its center, extents,\n    normal, and up direction.\n\n    Either select a specific observation in the volume data with '--step' or\n    '--time', or specify '--animate' to produce an animation over all\n    observations.\n    \"\"\"\n    if animate == (obs_id is not None):\n        raise click.UsageError(\n            \"Specify an observation '--step' or '--time', or specify\"\n            \" '--animate' (but not both).\"\n        )\n    if animate:\n        obs_ids, obs_times = list_observations(\n            open_volfiles(h5_files, subfile_name)\n        )\n\n    # Determine the slice coordinates\n    if (\n        slice_origin is None\n        or slice_extent is None\n        or slice_normal is None\n        or slice_up is None\n    ):\n        raise click.UsageError(\n            f\"Specify all slice parameters: '--slice-[origin,extent,normal,up]'\"\n        )\n    target_coords = points_on_slice(\n        slice_origin, slice_extent, slice_normal, slice_up, num_samples\n    )\n    dim = target_coords.shape[0]\n    assert dim == 3, \"Only 3D slices are supported\"\n\n    # Set axes for plot. For plotting along an axis, use the axis coordinate.\n    # Otherwise, use the affine parameter.\n    def coord_axis(vec):\n        nonzero_entries = np.nonzero(vec)[0]\n        if len(nonzero_entries) == 1:\n            return nonzero_entries[0]\n        return None\n\n    y_axis = coord_axis(slice_up)\n    x_axis = coord_axis(np.cross(slice_up, slice_normal))\n    if x_axis is not None:\n        x = target_coords[x_axis]\n        x_label = \"xyz\"[x_axis]\n    else:\n        x = np.linspace(0, 1, num_samples[0])\n        x_label = None\n    if y_axis is not None:\n        y = target_coords[y_axis]\n        y_label = \"xyz\"[y_axis]\n    else:\n        y = np.linspace(0, 1, num_samples[1])\n        y_label = None\n\n    # Determine global data bounds. Only needed for an animation.\n    if data_bounds is None and animate:\n        data_bounds = [np.inf, -np.inf]\n        for volfile in open_volfiles(h5_files, subfile_name):\n            for obs_id in volfile.list_observation_ids():\n                data = volfile.get_tensor_component(obs_id, var_name).data\n                data_bounds[0] = min(data_bounds[0], data.min())\n                data_bounds[1] = max(data_bounds[1], data.max())\n        logger.info(\n            f\"Determined data bounds for '{var_name}':\"\n            f\" [{data_bounds[0]:g}, {data_bounds[1]:g}]\"\n        )\n    norm = (\n        plt.Normalize(vmin=data_bounds[0], vmax=data_bounds[1])\n        if data_bounds\n        else None\n    )\n    levels = (\n        np.linspace(data_bounds[0], data_bounds[1], 10) if data_bounds else 10\n    )\n\n    # Set up the figure\n    fig, ax = plt.figure(), plt.gca()\n    plt.title(title or var_name)\n    if np.isclose(slice_extent[0], slice_extent[1]):\n        plt.gca().set_aspect(\"equal\")\n    plt.xlabel(x_label)\n    plt.ylabel(y_label)\n    cbar = plt.colorbar(matplotlib.cm.ScalarMappable(norm=norm), ax=ax)\n    time_label = plt.annotate(\n        \"\",\n        xy=(0, 0),\n        xycoords=\"axes fraction\",\n        xytext=(4, 3),\n        textcoords=\"offset points\",\n        ha=\"left\",\n        va=\"bottom\",\n        fontsize=9,\n    )\n\n    # Interpolate data and plot the slice\n    def plot_slice(obs_id, time):\n        data = np.array(\n            interpolate_to_points(\n                h5_files,\n                subfile_name=subfile_name,\n                observation=ObservationId(obs_id),\n                tensor_components=[var_name],\n                target_points=target_coords.reshape(3, np.prod(num_samples)),\n                extrapolate_into_excisions=extrapolate_into_excisions,\n                num_threads=num_threads,\n            )[0]\n        ).reshape(num_samples)\n        contours_filled = plt.contourf(\n            x, y, data, levels=levels, norm=norm, extend=\"both\"\n        )\n        contours = plt.contour(\n            contours_filled, colors=\"white\", linewidths=0.5, alpha=0.6\n        )\n        time_label.set_text(f\"t = {time:g}\")\n        if norm is None:\n            cbar.update_normal(contours_filled)\n        try:\n            # Before matplotlib 3.8:\n            return contours_filled.collections + contours.collections\n        except AttributeError:\n            # Since matplotlib 3.8:\n            return [contours_filled, contours]\n\n    # Plot a static slice and return early\n    if not animate:\n        plot_slice(obs_id, obs_time)\n        return fig\n\n    # Animate the slice\n    # Keep track of old artists to clear before plotting new ones\n    artists = []\n\n    def update_plot(frame):\n        nonlocal artists\n        # Clear old artists\n        for artist in artists:\n            artist.remove()\n        # Plot new slice\n        artists = plot_slice(obs_ids[frame], obs_times[frame])\n\n    return matplotlib.animation.FuncAnimation(\n        fig,\n        update_plot,\n        init_func=list,\n        frames=len(obs_ids),\n        interval=interval,\n        blit=False,\n    )\n\n\n@click.command(name=\"slice\", help=plot_slice.__doc__)\n@open_volfiles_command(obs_id_required=False, multiple_vars=False)\n# Slice options\n# These aren't marked \"required\" so the user can omit them when using options\n# like '--list-vars'.\n@click.option(\n    \"--slice-origin\",\n    \"--slice-center\",\n    \"-C\",\n    callback=parse_point,\n    help=(\n        \"Coordinates of the center of the slice through the volume \"\n        \"data. Specify as comma-separated list, e.g. '0,0,0'.  [required]\"\n    ),\n)\n@click.option(\n    \"--slice-extent\",\n    \"-X\",\n    type=float,\n    nargs=2,\n    help=(\n        \"Extent in both directions of the slice through the volume data, e.g. \"\n        \"'-X 10 10' for a 10x10 slice in the coordinates of the volume data.\"\n        \"  [required]\"\n    ),\n)\n@click.option(\n    \"--slice-normal\",\n    \"-n\",\n    callback=parse_point,\n    help=(\n        \"Direction of the normal of the slice through the volume \"\n        \"data. Specify as comma-separated list, e.g. '0,0,1' for a slice \"\n        \"in the xy-plane.  [required]\"\n    ),\n)\n@click.option(\n    \"--slice-up\",\n    \"-u\",\n    callback=parse_point,\n    help=(\n        \"Up-direction of the slice through the volume \"\n        \"data. Specify as comma-separated list, e.g. '0,1,0' so the y-axis \"\n        \"is the vertical axis of the plot.  [required]\"\n    ),\n)\n@click.option(\n    \"--extrapolate-into-excisions\",\n    is_flag=True,\n    help=(\n        \"Enables extrapolation into excision regions of the domain. \"\n        \"This can be useful to fill the excision region with \"\n        \"(constraint-violating but smooth) data so it can be imported into \"\n        \"moving puncture codes.\"\n    ),\n)\n@click.option(\n    \"--num-samples\",\n    \"-N\",\n    type=int,\n    nargs=2,\n    default=(200, 200),\n    show_default=True,\n    help=(\n        \"Number of uniformly spaced samples along each direction of the slice \"\n        \"to which volume data is interpolated.\"\n    ),\n)\n@click.option(\n    \"--num-threads\",\n    \"-j\",\n    type=int,\n    show_default=\"all available cores\",\n    help=(\n        \"Number of threads to use for interpolation. Only available if compiled\"\n        \" with OpenMP. Parallelization is over volume data files, so this only\"\n        \" has an effect if multiple files are specified.\"\n    ),\n)\n@click.option(\n    \"--title\",\n    \"-t\",\n    help=\"Title for the plot.\",\n    show_default=\"name of the variable\",\n)\n@click.option(\n    \"--y-bounds\",\n    \"--data-bounds\",\n    \"data_bounds\",\n    type=float,\n    nargs=2,\n    help=\"Lower and upper bounds for the color scale of the plot.\",\n)\n# Animation options\n@click.option(\"--animate\", is_flag=True, help=\"Animate over all observations.\")\n@click.option(\n    \"--interval\",\n    default=100,\n    type=float,\n    help=\"Delay between frames in milliseconds. Only used for animations.\",\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_slice_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    return plot_slice(**kwargs)\n\n\nif __name__ == \"__main__\":\n    plot_slice_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/PlotTrajectories.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n# Plot trajectory from inspiral run, concatenating all segments\n\nimport logging\n\nimport click\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\n\nlogger = logging.getLogger(__name__)\n\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\n\n\ndef import_A_and_B(\n    filenames,\n    subfile_name_aha=\"ApparentHorizons/ControlSystemAhA_Centers.dat\",\n    subfile_name_ahb=\"ApparentHorizons/ControlSystemAhB_Centers.dat\",\n):\n    A_data = []\n    B_data = []\n\n    for filename in filenames:\n        with h5py.File(filename, \"r\") as file:\n            A_data.append(\n                np.array(\n                    file[subfile_name_aha][:, [0, 4, 5, 6]]\n                )  # 0 ->time,  4 -> x, 5 -> y, 6 -> z\n            )\n            B_data.append(\n                np.array(\n                    file[subfile_name_ahb][:, [0, 4, 5, 6]]\n                )  # 0 ->time,  4 -> x, 5 -> y, 6 -> z\n            )\n\n    if A_data and B_data:\n        A = np.concatenate(A_data)\n        B = np.concatenate(B_data)\n        min_length = min(len(A), len(B))\n        return A[:min_length], B[:min_length]\n    else:\n        return None, None\n\n\ndef plot_trajectory(AhA: np.ndarray, AhB: np.ndarray, fig=None):\n    \"\"\"\n    Plot concatenated trajectories in inspiral simulation.\n\n    Outputs a 2x2 figure with 4 plots:\n    1st row: A and B trajectories in 3D, coordinate separation in 3D\n    2nd row: A and B trajectories in 2D xy plane, coordinate separation in 2D\n    xy plane. The 3D plots use a subsampled version of the data, with default\n    value of 15, to speed up the plots and avoid memory error.\n\n    Arguments:\n    AhA: Array of shape (num_points, 4) with the time and coordinates of\n      the first object.\n    AhB: Array of shape (num_points, 4) with the time and coordinates of\n      the second object.\n    fig: Matplotlib figure object used for plotting. Subplots will be added to\n      this figure. If None, a new figure will be created.\n    \"\"\"\n    if fig is None:\n        fig = plt.figure(figsize=(10, 10))\n\n    # Plot 3D trajectories\n    ax1 = fig.add_subplot(2, 2, 1, projection=\"3d\")\n    ax1.plot(*AhA[:, 1:].T, color=\"C0\", label=\"AhA\")\n    ax1.scatter(*AhA[-1, 1:], color=\"C0\")\n    ax1.plot(*AhB[:, 1:].T, color=\"C1\", label=\"AhB\")\n    ax1.scatter(*AhB[-1, 1:], color=\"C1\")\n    ax1.set_xlabel(\"X\")\n    ax1.set_ylabel(\"Y\")\n    ax1.set_zlabel(\"Z\")\n    ax1.set_title(\"3D Trajectories\")\n    ax1.legend()\n\n    # Calculate coordinate separation in 3D\n    separation_3d = AhA[:, 1:] - AhB[:, 1:]\n\n    # Plot 3D coordinate separation\n    ax2 = fig.add_subplot(2, 2, 2, projection=\"3d\")\n    ax2.plot(*separation_3d.T, color=\"black\")\n    ax2.scatter(*separation_3d[-1], color=\"black\")\n    ax2.set_xlabel(\"X diff\")\n    ax2.set_ylabel(\"Y diff\")\n    ax2.set_zlabel(\"Z diff\")\n    ax2.set_title(\"Coordinate separation (3D)\")\n\n    # Plot 2D trajectories\n    ax3 = fig.add_subplot(2, 2, 3)\n    ax3.plot(*AhA[:, 1:3].T, label=\"AhA\", color=\"C0\")\n    ax3.scatter(*AhA[-1, 1:3], color=\"C0\")\n    ax3.plot(*AhB[:, 1:3].T, label=\"AhB\", color=\"C1\")\n    ax3.scatter(*AhB[-1, 1:3], color=\"C1\")\n    ax3.set_xlabel(\"x\")\n    ax3.set_ylabel(\"y\")\n    ax3.legend()\n    ax3.set_title(\"Trajectories (2D)\")\n    ax3.set_aspect(\"equal\")  # Set aspect ratio to 1\n    ax3.grid(True)  # Add gridlines\n\n    # Calculate coordinate separation in 2D\n    separation_2d = AhA[:, 1:3] - AhB[:, 1:3]\n    # Plot coordinate separation in 2D\n    ax4 = fig.add_subplot(2, 2, 4)\n    ax4.plot(*separation_2d.T, color=\"black\")\n    ax4.scatter(*separation_2d[-1], color=\"black\")\n    ax4.set_xlabel(\"x1 - x2\")\n    ax4.set_ylabel(\"y1 - y2\")\n    ax4.set_title(\"Coordinate separation (2D)\")\n    ax4.set_aspect(\"equal\")  # Set aspect ratio to 1\n    ax4.grid(True)  # Add gridlines\n\n    plt.subplots_adjust(wspace=0.3, hspace=0.3)\n    return fig\n\n\n@click.command(name=\"trajectories\")\n@click.argument(\n    \"h5_files\",\n    nargs=-1,\n    required=True,\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.option(\n    \"--subfile-name-aha\",\n    \"-A\",\n    default=\"ApparentHorizons/ControlSystemAhA_Centers.dat\",\n    help=(\n        \"Name of subfile containing the apparent horizon centers for object A.\"\n    ),\n)\n@click.option(\n    \"--subfile-name-ahb\",\n    \"-B\",\n    default=\"ApparentHorizons/ControlSystemAhB_Centers.dat\",\n    help=(\n        \"Name of subfile containing the apparent horizon centers for object B.\"\n    ),\n)\n@click.option(\n    \"--sample-rate\",\n    type=int,\n    default=1,\n    show_default=True,\n    help=(\n        \"Downsample data to speed up plots. E.g. a value of 10 means every 10th\"\n        \" point is plotted.\"\n    ),\n)\n@click.option(\n    \"--figsize\",\n    nargs=2,\n    type=float,\n    default=(10.0, 10.0),\n    show_default=True,\n    help=\"Figure size as width and height in inches\",\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\ndef plot_trajectories_command(\n    h5_files, subfile_name_aha, subfile_name_ahb, sample_rate, figsize\n):\n    \"\"\"Plot trajectories in inspiral simulation\n\n    Concatenates partial trajectories from each h5 reductions files and plots\n    full trajectories.\n    \"\"\"\n    AhA, AhB = import_A_and_B(h5_files, subfile_name_aha, subfile_name_ahb)\n    fig = plt.figure(figsize=figsize)\n    return plot_trajectory(AhA[::sample_rate], AhB[::sample_rate], fig)\n\n\nif __name__ == \"__main__\":\n    plot_trajectories_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/ReadInputFile.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\"\"\"Tools for parsing YAML input files\n\nTo simply read an input file as a dictionary, use a standard YAML parser like\nPyYAML or ruaml.yaml::\n\n    import yaml\n    with open(input_file_path) as open_input_file:\n        metadata, input_file = yaml.safe_load_all(open_input_file)\n\nIt's also possible to load just the metadata without the overhead of parsing the\nfull input file::\n\n    with open(input_file_path) as open_input_file:\n        metadata = next(yaml.safe_load_all(open_input_file))\n\nThe functions in this module provide additional functionality to work with input\nfiles.\n\"\"\"\n\n\ndef find_event(\n    event_name: str, events_and_triggers_name: str, input_file: dict\n) -> dict:\n    \"\"\"Find a particular event in the given list of events of the input file.\n\n    Arguments:\n      event_name: The name of an event like \"ObserveTimeSteps\".\n      events_and_triggers_name: The name of a list of events.\n      input_file: The input file read in as a dictionary.\n\n    Returns: The event as a dictionary, or None if the event wasn't found.\n    \"\"\"\n    for trigger_and_events in input_file[events_and_triggers_name]:\n        try:\n            for event in trigger_and_events[\"Events\"]:\n                if isinstance(event, str):\n                    if event == event_name:\n                        return {}\n                elif event_name in event:\n                    return event[event_name]\n        except TypeError:\n            # Backwards compatibility for input files without metadata (can be\n            # removed once people have rebased)\n            for event in trigger_and_events[1]:\n                if event_name in event:\n                    return event[event_name]\n    return None\n\n\ndef find_phase_change(phase_change_name: str, input_file: dict) -> dict:\n    \"\"\"Find a particular phase change in the \"PhaseChangeAndTriggers\"\n\n    Arguments:\n      phase_change_name: The name of a phase change like\n        \"CheckpointAndExitAfterWallclock\".\n      input_file: The input file read in as a dictionary.\n\n    Returns: The phase change as a dictionary, or None if the phase change\n      wasn't found.\n    \"\"\"\n    if (\n        \"PhaseChangeAndTriggers\" not in input_file\n        or not input_file[\"PhaseChangeAndTriggers\"]\n    ):\n        return None\n    for trigger_and_phase_changes in input_file[\"PhaseChangeAndTriggers\"]:\n        for phase_change in trigger_and_phase_changes[\"PhaseChanges\"]:\n            if isinstance(phase_change, str):\n                if phase_change == phase_change_name:\n                    return {}\n            elif phase_change_name in phase_change:\n                return phase_change[phase_change_name]\n    return None\n"
  },
  {
    "path": "src/Visualization/Python/Render1D.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nfrom itertools import cycle\nfrom typing import Dict, Optional\n\nimport click\nimport matplotlib.animation\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom scipy.interpolate import lagrange\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Frame, tnsr\nfrom spectre.Domain import (\n    FunctionOfTime,\n    deserialize_domain,\n    deserialize_functions_of_time,\n)\nfrom spectre.IO.H5.IterElements import Element, iter_elements\nfrom spectre.Spectral import Basis\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.support.Logging import configure_logging\nfrom spectre.Visualization.Plot import (\n    apply_stylesheet_command,\n    show_or_save_plot_command,\n)\nfrom spectre.Visualization.PlotDatFile import parse_functions\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_bounds(volfiles, obs_ids, vars):\n    \"\"\"Get the bounds in both x and y over all observations, vars, and files\"\"\"\n    x_bounds = [np.inf, -np.inf]\n    y_bounds = [np.inf, -np.inf]\n    for obs_id in obs_ids:\n        for element, vars_data in iter_elements(volfiles, obs_id, vars):\n            x_bounds[0] = min(x_bounds[0], np.min(element.inertial_coordinates))\n            x_bounds[1] = max(x_bounds[1], np.max(element.inertial_coordinates))\n            y_bounds[0] = min(y_bounds[0], np.nanmin(vars_data))\n            y_bounds[1] = max(y_bounds[1], np.nanmax(vars_data))\n    return x_bounds, y_bounds\n\n\ndef plot_element(\n    element: Element,\n    vars_data: np.ndarray,\n    vars: Dict[str, str],\n    var_props: dict,\n    show_collocation_points: bool,\n    show_element_boundaries: bool,\n    show_basis_polynomials: bool,\n    time: Optional[float] = None,\n    functions_of_time: Optional[Dict[str, FunctionOfTime]] = None,\n    logical_space: np.ndarray = np.linspace(-1, 1, 50),\n    handles: Optional[dict] = None,\n):\n    \"\"\"Plot a 1D element, or update the plot in an animation\"\"\"\n\n    # We store plots in these dicts so we can update them later in an animation\n    # by calling `plot_element` again\n    if handles is not None:\n        element_handles = handles.setdefault(element.id, dict())\n        point_handles = element_handles.setdefault(\"point\", dict())\n        boundary_handles = element_handles.setdefault(\"boundary\", dict())\n        var_handles = element_handles.setdefault(\"var\", dict())\n        interpolation_handles = element_handles.setdefault(\"interp\", dict())\n        basis_handles = element_handles.setdefault(\"basis\", dict())\n\n    # We collect legend items and return them from this function\n    legend_items = dict()\n\n    inertial_coords = np.array(element.inertial_coordinates)[0]\n\n    # Plot collocation points\n    if show_collocation_points:\n        for i, coord in enumerate(inertial_coords):\n            if handles and i in point_handles:\n                point_handles[i].set_xdata(coord)\n            else:\n                point_handles[i] = plt.axvline(\n                    coord, color=\"black\", ls=\"dotted\", alpha=0.2\n                )\n        legend_items[\"Collocation points\"] = point_handles[0]\n        # Clean up leftover handles\n        if handles:\n            all_point_handles = sorted(list(point_handles.keys()))\n            for i in all_point_handles[all_point_handles.index(i) + 1 :]:\n                point_handles.pop(i).remove()\n\n    # Plot element boundaries\n    if show_element_boundaries:\n        logical_boundaries = tnsr.I[DataVector, 1, Frame.ElementLogical](\n            np.array([[-1.0, 1.0]])\n        )\n        element_boundaries = np.asarray(\n            element.map(\n                logical_boundaries,\n                time=time,\n                functions_of_time=functions_of_time,\n            )\n        )[0]\n        for i, coord in enumerate(element_boundaries):\n            if handles and i in boundary_handles:\n                boundary_handles[i].set_xdata(coord)\n            else:\n                boundary_handles[i] = plt.axvline(coord, color=\"black\")\n        legend_items[\"Element boundaries\"] = boundary_handles[0]\n\n    # Prepare Lagrange interpolation\n    # Only show Lagrange interpolation for spectral elements. Finite-difference\n    # elements just show the data points.\n    show_lagrange_interpolation = all(\n        [\n            basis == Basis.Legendre or basis == Basis.Chebyshev\n            for basis in element.mesh.basis()\n        ]\n    )\n    if show_lagrange_interpolation:\n        # These are the control points for the Lagrange interpolation\n        logical_coords = np.array(element.logical_coordinates)[0]\n        # These are the points where we plot the interpolation\n        logical_space_tensor = tnsr.I[DataVector, 1, Frame.ElementLogical](\n            np.expand_dims(logical_space, axis=0)\n        )\n        inertial_space = np.asarray(\n            element.map(\n                logical_space_tensor,\n                time=time,\n                functions_of_time=functions_of_time,\n            )\n        )[0]\n\n    # Plot selected variables\n    for (var, label), var_data in zip(vars.items(), vars_data):\n        # Plot data points\n        if handles and var in var_handles:\n            var_handles[var].set_data(inertial_coords, var_data)\n        else:\n            var_handles[var] = plt.plot(\n                inertial_coords,\n                var_data,\n                marker=\".\",\n                ls=\"none\",\n                **var_props[var],\n            )[0]\n        legend_items[label] = var_handles[var]\n        # Plot Lagrange interpolation\n        if show_lagrange_interpolation:\n            interpolant = lagrange(logical_coords, var_data)\n            if handles and var in interpolation_handles:\n                interpolation_handles[var].set_data(\n                    inertial_space, interpolant(logical_space)\n                )\n            else:\n                interpolation_handles[var] = plt.plot(\n                    inertial_space, interpolant(logical_space), **var_props[var]\n                )[0]\n            # Plot polynomial basis\n            if show_basis_polynomials:\n                unit_weights = np.eye(len(logical_coords))\n                for xi_i in range(len(logical_coords)):\n                    basis_id = (var, xi_i)\n                    basis_polynomial = lagrange(\n                        logical_coords, var_data[xi_i] * unit_weights[xi_i]\n                    )\n                    if handles and basis_id in basis_handles:\n                        basis_handles[basis_id].set_data(\n                            inertial_space, basis_polynomial(logical_space)\n                        )\n                    else:\n                        basis_handles[basis_id] = plt.plot(\n                            inertial_space,\n                            basis_polynomial(logical_space),\n                            color=\"black\",\n                            alpha=0.2,\n                        )[0]\n        elif handles:\n            # Clean up leftover handles\n            if var in interpolation_handles:\n                interpolation_handles.pop(var).remove()\n            for basis_id in list(basis_handles.keys()):\n                basis_handles.pop(basis_id).remove()\n    if show_lagrange_interpolation and show_basis_polynomials:\n        legend_items[\"Lagrange basis\"] = basis_handles[basis_id]\n\n    return legend_items\n\n\n@click.command(name=\"render-1d\")\n@click.argument(\n    \"h5_files\",\n    nargs=-1,\n    required=True,\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.option(\n    \"--subfile-name\",\n    \"-d\",\n    help=(\n        \"Name of subfile within h5 file containing \"\n        \"1D volume data to be rendered.\"\n    ),\n)\n@click.option(\n    \"--var\",\n    \"-y\",\n    \"vars\",\n    multiple=True,\n    callback=parse_functions,\n    help=(\n        \"Name of variable to plot, e.g. 'Psi' or 'Error(Psi)'. \"\n        \"Can be specified multiple times. \"\n        \"If unspecified, plot all available variables. \"\n        \"Labels for variables can be specified as key-value pairs such as \"\n        \"'Error(Psi)=$L_2(\\\\psi)$'. Remember to wrap the key-value pair in \"\n        \"quotes on the command line to avoid issues with special characters \"\n        \"or spaces.\"\n    ),\n)\n@click.option(\n    \"--list-vars\",\n    \"-l\",\n    is_flag=True,\n    help=\"Print available variables and exit.\",\n)\n@click.option(\n    \"--step\",\n    type=int,\n    help=(\n        \"If specified, renders the integer observation step \"\n        \"instead of an animation. Set to '-1' for the last step.\"\n    ),\n)\n@click.option(\n    \"--interval\",\n    default=100,\n    type=float,\n    help=\"Delay between frames in milliseconds\",\n)\n# Plotting options\n@click.option(\n    \"--x-label\",\n    help=\"The label on the x-axis.\",\n    show_default=\"name of the x-axis column\",\n)\n@click.option(\n    \"--y-label\",\n    required=False,\n    help=\"The label on the y-axis.\",\n    show_default=\"no label\",\n)\n@click.option(\"--x-logscale\", is_flag=True, help=\"Set the x-axis to log scale.\")\n@click.option(\"--y-logscale\", is_flag=True, help=\"Set the y-axis to log scale.\")\n@click.option(\n    \"--x-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the x-axis.\",\n)\n@click.option(\n    \"--y-bounds\",\n    type=float,\n    nargs=2,\n    help=\"The lower and upper bounds of the y-axis.\",\n)\n@click.option(\n    \"--title\", \"-t\", help=\"Title of the graph.\", show_default=\"subfile name\"\n)\n@apply_stylesheet_command()\n@show_or_save_plot_command()\n@click.option(\"--show-collocation-points\", is_flag=True)\n@click.option(\"--show-element-boundaries\", is_flag=True)\n@click.option(\"--show-basis-polynomials\", is_flag=True)\ndef render_1d_command(\n    h5_files,\n    subfile_name,\n    list_vars,\n    vars,\n    x_label,\n    y_label,\n    x_logscale,\n    y_logscale,\n    x_bounds,\n    y_bounds,\n    title,\n    step,\n    interval,\n    **plot_element_kwargs,\n):\n    \"\"\"Render 1D data\"\"\"\n    open_h5_files = [spectre_h5.H5File(filename, \"r\") for filename in h5_files]\n\n    # Print available subfile names and exit\n    if not subfile_name:\n        raise RequiredChoiceError(\n            (\n                \"Specify '--subfile-name' / '-d' to select a\"\n                \" subfile containing 1D volume data.\"\n            ),\n            choices=open_h5_files[0].all_vol_files(),\n        )\n\n    volfiles = [h5file.get_vol(subfile_name) for h5file in open_h5_files]\n    dim = volfiles[0].get_dimension()\n    assert (\n        dim == 1\n    ), f\"The selected subfile contains {dim}D volume data, not 1D.\"\n    obs_ids = volfiles[0].list_observation_ids()  # Already sorted by obs value\n    all_vars = volfiles[0].list_tensor_components(obs_ids[0])\n    if \"InertialCoordinates_x\" in all_vars:\n        all_vars.remove(\"InertialCoordinates_x\")\n\n    # Print available variables and exit\n    if list_vars:\n        import rich.columns\n\n        rich.print(rich.columns.Columns(all_vars))\n        return\n    for var in vars:\n        if var not in all_vars:\n            raise RequiredChoiceError(\n                f\"Unknown variable '{var}'.\", choices=all_vars\n            )\n    if not vars:\n        vars = {var: var for var in all_vars}\n    plot_element_kwargs[\"vars\"] = vars\n\n    # Evaluate property cycles for each variable (by default this is just\n    # 'color'). We do multiple plotting commands per variable (at least one per\n    # element), so we don't want matplotlib to cycle through the properties at\n    # every plotting command.\n    prop_cycle = {\n        key: cycle(values)\n        for key, values in plt.rcParams[\"axes.prop_cycle\"].by_key().items()\n    }\n    var_props = {\n        var: {key: next(values) for key, values in prop_cycle.items()}\n        for var in vars\n    }\n    plot_element_kwargs[\"var_props\"] = var_props\n\n    # Animate or single frame?\n    if len(obs_ids) == 1:\n        animate = False\n        obs_id = obs_ids[0]\n    elif step is None:\n        animate = True\n        obs_id = obs_ids[0]\n    else:\n        animate = False\n        obs_id = obs_ids[step]\n    obs_value = volfiles[0].get_observation_value(obs_id)\n\n    # For Lagrange interpolation\n    domain = deserialize_domain[1](volfiles[0].get_domain())\n    if domain.is_time_dependent():\n        functions_of_time = deserialize_functions_of_time(\n            volfiles[0].get_functions_of_time(obs_id)\n        )\n        plot_element_kwargs[\"functions_of_time\"] = functions_of_time\n        plot_element_kwargs[\"time\"] = obs_value\n\n    # We store plots here so we can update them later\n    plot_element_kwargs[\"handles\"] = dict()\n\n    # Plot first frame\n    fig = plt.figure()\n    for element, vars_data in iter_elements(volfiles, obs_id, vars):\n        legend_items = plot_element(element, vars_data, **plot_element_kwargs)\n\n    # Configure the axes\n    if x_logscale:\n        plt.xscale(\"log\")\n    if y_logscale:\n        plt.yscale(\"log\")\n    plt.xlabel(x_label if x_label else \"x\")\n    plt.ylabel(y_label)\n    plt.legend(legend_items.values(), legend_items.keys())\n    title_handle = plt.title(title if title else f\"t = {obs_value:g}\")\n    if animate and not (x_bounds and y_bounds):\n        data_bounds = get_bounds(volfiles, obs_ids, vars)\n        if not x_bounds:\n            x_bounds = data_bounds[0]\n        if not y_bounds:\n            y_bounds = data_bounds[1]\n            if not y_logscale:\n                margin = (y_bounds[1] - y_bounds[0]) * 0.05\n                y_bounds[0] -= margin\n                y_bounds[1] += margin\n    if x_bounds:\n        plt.xlim(*x_bounds)\n    if y_bounds:\n        plt.ylim(*y_bounds)\n\n    # Animate the plot or return early\n    if not animate:\n        return fig\n\n    # Close current volume files\n    for h5_file in open_h5_files:\n        h5_file.close_current_object()\n\n    # The volume files need to be local within the function, not captured from\n    # the global scope\n    def update_plot(frame):\n        local_volfiles = [\n            h5file.get_vol(subfile_name) for h5file in open_h5_files\n        ]\n        obs_id = obs_ids[frame]\n        obs_value = local_volfiles[0].get_observation_value(obs_id)\n        if domain.is_time_dependent():\n            functions_of_time = deserialize_functions_of_time(\n                local_volfiles[0].get_functions_of_time(obs_id)\n            )\n            plot_element_kwargs[\"functions_of_time\"] = functions_of_time\n            plot_element_kwargs[\"time\"] = obs_value\n        title_handle.set_text(title if title else f\"t = {obs_value:g}\")\n        for element, vars_data in iter_elements(local_volfiles, obs_id, vars):\n            plot_element(element, vars_data, **plot_element_kwargs)\n\n        for h5_file in open_h5_files:\n            h5_file.close_current_object()\n\n    return matplotlib.animation.FuncAnimation(\n        fig=fig,\n        func=update_plot,\n        frames=len(obs_ids),\n        interval=interval,\n        blit=False,\n    )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.INFO)\n    render_1d_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/Render3D/Bbh.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\n\nimport click\nimport rich.columns\n\nlogger = logging.getLogger(__name__)\n\n\ndef _parse_step(ctx, param, value):\n    if value is None:\n        return None\n    if value.lower() == \"first\":\n        return 0\n    if value.lower() == \"last\":\n        return -1\n    return int(value)\n\n\ndef ah_vis(ah_xmf: str, render_view: str):\n    \"\"\"Helper function for visualizing the apparent horizons of the objects.\n\n    Arguments:\n    ah_xmf: Path to the xmf file of the object.\n    render_view: The current view in paraview to add the horizon to.\"\"\"\n    import paraview.simple as pv\n\n    Ah_xmf = pv.XDMFReader(registrationName=ah_xmf, FileNames=[ah_xmf])\n    transform_1 = pv.Transform(registrationName=\"Transform1\", Input=Ah_xmf)\n    transform_1.Transform = \"Transform\"\n    transform_1.Transform.Translate = [0.0, 0.0, 2.0]\n    transform_1_display = pv.Show(\n        transform_1, render_view, \"UnstructuredGridRepresentation\"\n    )\n    transform_1_display.SetScalarBarVisibility(render_view, False)\n    # Sets apparent horizon color to black\n    transform_1_display.AmbientColor = [0.0, 0.0, 0.0]\n    transform_1_display.DiffuseColor = [0.0, 0.0, 0.0]\n\n    render_view.Update()\n    pv.ColorBy(transform_1_display, None)\n\n\ndef render_bbh(\n    volume_xmf: str,\n    output: str,\n    aha_xmf: str,\n    ahb_xmf: str,\n    time_step: int = 0,\n    animate: bool = False,\n    camera_angle: str = \"Side\",\n    zoom_factor: float = 1.0,\n    color_map: str = \"Rainbow Uniform\",\n    show_grid: bool = False,\n    show_time: bool = False,\n):\n    \"\"\"Generate Pictures from XMF files for BBH Visualizations\n\n    Generates pictures from BBH runs using the XMF files generated using\n    generate-xdmf. This script requires that the Lapse and SpatialRicciScalar\n    were output in the volume data.\n\n    Arguments:\n\n      volume_xmf: Path to the volume data xmf file.\n      output: Name of output file generated from paraview. Include extensions\n      such as '.png'\n      aha_xmf: Path to the apparent horizon xmf file for object A.\n      ahb_xmf: Path to the apparent horizon xmf file for object B.\n      camera_angle: Specified camera angle, defaults to Side if empty. Other\n      possible angles Top and Wide\n      color_map: Color map for the lapse, defaults to 'Rainbow Uniform'. Other\n      color maps include 'Inferno (matplotlib)', 'Viridis (matplotlib)', etc.\n      show_grid: Shows the grid lines of the domain.\n      show_time: Shows the simulation time.\n\n    To splice all the pictures into a video, try using FFmpeg\"\"\"\n    import paraview.simple as pv\n\n    version = pv.GetParaViewVersion()\n    if version < (5, 11) or version > (5, 11):\n        logger.warning(\n            \"WARNING: Your Paraview version is not 5.11, \"\n            \"the script may not work correctly.\"\n        )\n\n    # Volume Data Visualization\n    volume_files_xmf = pv.XDMFReader(\n        registrationName=volume_xmf, FileNames=[volume_xmf]\n    )\n\n    # Check for Lapse and SpatialRicciScalar\n    variables = list(volume_files_xmf.PointData.keys())\n    assert (\n        \"Lapse\" in variables\n    ), \"Lapse not found in volume data, the script will not work correctly.\"\n    assert \"SpatialRicciScalar\" in variables, (\n        \"SpatialRicciScalar not found in volume data, the script will not work\"\n        \" correctly.\"\n    )\n\n    render_view = pv.GetActiveViewOrCreate(\"RenderView\")\n\n    # Color the grid\n    color_transfer_function = pv.GetColorTransferFunction(\"Lapse\")\n    color_transfer_function.Discretize = 0\n    color_transfer_function.ApplyPreset(color_map, True)\n    color_transfer_function.InvertTransferFunction()\n\n    # Slice volume data\n    slice = pv.Slice(registrationName=\"slice\", Input=volume_files_xmf)\n    slice.SliceType = \"Plane\"\n    slice.HyperTreeGridSlicer = \"Plane\"\n    slice.SliceOffsetValues = [0.0]\n    slice.SliceType.Normal = [0.0, 0.0, 1.0]\n    slice.Triangulatetheslice = 0\n\n    # Warp grid by spatial ricci scalar\n    warp_by_scalar = pv.WarpByScalar(\n        registrationName=\"WarpByScalar\", Input=slice\n    )\n    warp_by_scalar.Scalars = [\"POINTS\", \"SpatialRicciScalar\"]\n    warp_by_scalar.ScaleFactor = 2.5\n    warp_by_scalar.Normal = [0.0, 0.0, -1.0]\n    warp_by_scalar_display = pv.Show(\n        warp_by_scalar, render_view, \"GeometryRepresentation\"\n    )\n    warp_by_scalar_display.SetScalarBarVisibility(render_view, False)\n\n    # Apparent Horizon Visualization\n    if aha_xmf:\n        ah_vis(aha_xmf, render_view)\n    if ahb_xmf:\n        ah_vis(ahb_xmf, render_view)\n\n    if show_grid:\n        warp_by_scalar_display.Representation = \"Surface With Edges\"\n\n    pv.LoadPalette(paletteName=\"GradientBackground\")\n    render_view.OrientationAxesVisibility = 0\n    pv.SetActiveSource(warp_by_scalar)\n    warp_by_scalar_display.Opacity = 0.8\n    pv.ColorBy(warp_by_scalar_display, (\"POINTS\", \"Lapse\"))\n    layout = pv.GetLayout()\n    layout.SetSize(1920, 1080)\n\n    # Camera placements\n    # Top down view\n    if camera_angle == \"Top\":\n        render_view.CameraPosition = [0.0, 0.0, 36.90869716569761]\n        render_view.CameraFocalPoint = [0.0, 0.0, 0.6894899550131899]\n        render_view.CameraViewUp = [0, 1, 0]\n        render_view.CameraParallelScale = 424.27024700303446\n    # Wide/Inbetween View\n    elif camera_angle == \"Wide\":\n        render_view.CameraPosition = [\n            -89.0,\n            -17.0,\n            25.0,\n        ]\n        render_view.CameraFocalPoint = [\n            -0.3921962951264054,\n            1.6346750682876983,\n            -0.34522248814953405,\n        ]\n        render_view.CameraViewUp = [\n            0.0,\n            0.0,\n            1.0,\n        ]\n    # Side View\n    else:\n        render_view.CameraPosition = [\n            -29.944619336722987,\n            -3.666072157343372,\n            2.895224044348878,\n        ]\n        render_view.CameraFocalPoint = [\n            -0.13267040638072278,\n            0.6356115665206243,\n            -0.37352608789235847,\n        ]\n        render_view.CameraViewUp = [0.0, 0.0, 1.0]\n        render_view.CameraParallelScale = 519.6152422706632\n    camera = pv.GetActiveCamera()\n    pv.ResetCamera()\n    camera.Zoom(zoom_factor)\n\n    # Simulation time\n    if show_time:\n        time_filter = pv.AnnotateTimeFilter(\n            registrationName=\"annotate_time_filter\", Input=slice\n        )\n        time_filter.Format = \"Time: {time:0.2f}M\"\n        annotate_time_filter_display = pv.Show(\n            time_filter, render_view, \"TextSourceRepresentation\"\n        )\n        annotate_time_filter_display.FontSize = 45\n\n    # Capture all frames\n    animation_scene = pv.GetAnimationScene()\n    animation_scene.PlayMode = \"Snap To TimeSteps\"\n\n    # Save animation/screenshot\n    if animate:\n        pv.SaveAnimation(output, render_view)\n    else:\n        render_view.ViewTime = volume_files_xmf.TimestepValues[time_step]\n        pv.Render()\n        pv.SaveScreenshot(output, render_view)\n\n\n@click.command(name=\"bbh\", help=render_bbh.__doc__)\n@click.argument(\n    \"volume_xmf\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.option(\n    \"--output\",\n    \"-o\",\n    type=click.Path(\n        exists=False, file_okay=True, dir_okay=False, writable=True\n    ),\n    required=True,\n    help=\"Output file. Include extension such as '.png'.\",\n)\n@click.option(\n    \"--aha-xmf\",\n    \"-a\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    help=\"Optional xmf file for AhA visualization\",\n)\n@click.option(\n    \"--ahb-xmf\",\n    \"-b\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    help=\"Optional xmf file for AhB visualization\",\n)\n@click.option(\n    \"--time-step\",\n    \"-t\",\n    callback=_parse_step,\n    default=\"first\",\n    show_default=True,\n    help=(\n        \"Select a time step. Specify '-1' or 'last' to select the last time\"\n        \" step.\"\n    ),\n)\n@click.option(\n    \"--animate\", is_flag=True, help=\"Produce an animation of all time steps.\"\n)\n@click.option(\n    \"--camera-angle\",\n    \"-c\",\n    default=\"Side\",\n    type=click.Choice([\"Side\", \"Top\", \"Wide\"]),\n    help=(\n        \"Determines which camera angle to use: Default is the Side view.Top\"\n        \" view is right above the excisions at t = 0. Wide is further out and\"\n        \" inbetween Side and Top view\"\n    ),\n)\n@click.option(\"zoom_factor\", \"--zoom\", help=\"Zoom factor.\", default=1.0)\n@click.option(\n    \"--color-map\",\n    \"-m\",\n    default=\"Rainbow Uniform\",\n    help=(\n        'Determines how to color the domain, common color maps are \"Inferno'\n        ' (matplotlib)\", \"Viridis (matplotlib). Defaults to Rainbow Uniform.\"'\n    ),\n)\n@click.option(\n    \"--show-grid\",\n    is_flag=True,\n    help=\"Show grid lines\",\n)\n@click.option(\n    \"--show-time\",\n    is_flag=True,\n    help=\"Show simulation time\",\n)\ndef render_bbh_command(**kwargs):\n    _rich_traceback_guard = True\n    render_bbh(**kwargs)\n\n\nif __name__ == \"__main__\":\n    logging.basicConfig(level=logging.INFO)\n    render_bbh_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/Render3D/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  Render3D\n  MODULE_PATH \"Visualization\"\n  PYTHON_FILES\n  __init__.py\n  Clip.py\n  Domain.py\n  Bbh.py\n)\n"
  },
  {
    "path": "src/Visualization/Python/Render3D/Clip.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport click\nimport rich\n\n\ndef _parse_step(ctx, param, value):\n    if value is None:\n        return None\n    if value.lower() == \"first\":\n        return 0\n    if value.lower() == \"last\":\n        return -1\n    return int(value)\n\n\n@click.command(name=\"clip\")\n@click.argument(\n    \"xmf_file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.option(\n    \"--output\",\n    \"-o\",\n    type=click.Path(writable=True),\n    required=True,\n    help=\"Output file. Include extension such as '.png'.\",\n)\n@click.option(\n    \"--variable\",\n    \"-y\",\n    help=\"Variable to plot. Lists available variables when not specified.\",\n)\n@click.option(\n    \"--time-step\",\n    \"-t\",\n    callback=_parse_step,\n    default=\"first\",\n    show_default=True,\n    help=(\n        \"Select a time step. Specify '-1' or 'last' to select the last time\"\n        \" step.\"\n    ),\n)\n@click.option(\n    \"--animate\", is_flag=True, help=\"Produce an animation of all time steps.\"\n)\n@click.option(\n    \"log_scale\",\n    \"--log\",\n    is_flag=True,\n    help=\"Plot variable in log scale.\",\n)\n@click.option(\n    \"--show-grid\",\n    is_flag=True,\n    help=\"Show grid lines\",\n)\n@click.option(\"zoom_factor\", \"--zoom\", help=\"Zoom factor.\", default=1.0)\n@click.option(\n    \"--clip-origin\",\n    nargs=3,\n    type=float,\n    default=(0.0, 0.0, 0.0),\n    help=\"Origin of the clipping plane\",\n    show_default=True,\n)\n@click.option(\n    \"--clip-normal\",\n    nargs=3,\n    type=float,\n    default=(0.0, 0.0, 1.0),\n    help=\"Normal of the clipping plane\",\n    show_default=True,\n)\ndef render_clip_command(\n    xmf_file,\n    output,\n    variable,\n    time_step,\n    animate,\n    log_scale,\n    show_grid,\n    zoom_factor,\n    clip_origin,\n    clip_normal,\n):\n    \"\"\"Renders a clip normal to the z-direction.\n\n    XMF_FILE is the path to the XMF file that references the simulation data.\n    It is typically generated by the 'generate-xdmf' command.\n\n    This is a quick way to get some insight into the simulation data. For more\n    advanced renderings, open the XMF file in an interactive ParaView session,\n    or implement rendering commands specialized for your use case.\n    \"\"\"\n    import paraview.simple as pv\n\n    # Load data\n    volume_data = pv.XDMFReader(\n        registrationName=\"VolumeData\", FileNames=[xmf_file]\n    )\n\n    # Select variable\n    if not variable:\n        import rich.columns\n\n        all_variables = volume_data.PointData.keys()\n        rich.print(rich.columns.Columns(all_variables))\n        return\n\n    render_view = pv.GetActiveViewOrCreate(\"RenderView\")\n    render_view.AxesGrid.Visibility = 1\n\n    # Configure colors\n    color_transfer_function = pv.GetColorTransferFunction(variable)\n    if log_scale:\n        color_transfer_function.UseLogScale = 1\n\n    # Clip\n    clip = pv.Clip(registrationName=\"Clip\", Input=volume_data)\n    clip.ClipType = \"Plane\"\n    clip.ClipType.Origin = clip_origin\n    clip.ClipType.Normal = clip_normal\n\n    # Show clip\n    clip_display = pv.Show(clip, render_view)\n    if show_grid:\n        clip_display.Representation = \"Surface With Edges\"\n    else:\n        clip_display.Representation = \"Surface\"\n    pv.ColorBy(clip_display, (\"POINTS\", variable))\n    clip_display.SetScalarBarVisibility(render_view, True)\n\n    # Show time annotation\n    annotate_time = pv.AnnotateTimeFilter(\n        registrationName=\"AnnotateTimeFilter\", Input=volume_data\n    )\n    pv.Show(annotate_time, render_view)\n\n    # Set resolution\n    layout = pv.GetLayout()\n    layout.SetSize(1920, 1080)\n\n    # Configure camera\n    render_view.ResetCamera(True)\n    render_view.CameraViewAngle /= zoom_factor\n\n    # Capture all frames\n    animation_scene = pv.GetAnimationScene()\n    animation_scene.PlayMode = \"Snap To TimeSteps\"\n\n    if animate:\n        pv.SaveAnimation(output, render_view)\n    else:\n        render_view.ViewTime = volume_data.TimestepValues[time_step]\n        pv.SaveScreenshot(output, render_view)\n\n\nif __name__ == \"__main__\":\n    render_clip_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/Render3D/Domain.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom typing import Optional\n\nimport click\nimport numpy as np\nimport rich\n\n\ndef _parse_step(ctx, param, value):\n    if value is None:\n        return None\n    if value.lower() == \"first\":\n        return 0\n    if value.lower() == \"last\":\n        return -1\n    return int(value)\n\n\ndef render_domain(\n    xmf_file: str,\n    output: str,\n    hi_res_xmf_file: Optional[str] = None,\n    time_step: int = 0,\n    show_time_annotation: bool = False,\n    animate: bool = False,\n    zoom_factor: float = 1.0,\n    camera_theta: float = 0.0,\n    camera_phi: float = 0.0,\n    clip_origin: tuple[float, float, float] = (0.0, 0.0, 0.0),\n    clip_normal: tuple[float, float, float] = (0.0, 0.0, 1.0),\n    slice: bool = False,\n    background_color: tuple[float, float, float] = (1.0, 1.0, 1.0),\n):\n    \"\"\"Renders a 3D domain with elements and grid lines\n\n    This rendering is a starting point for visualizations of the domain\n    geometry, e.g. for publications.\n\n    XMF_FILE is the path to the XMF file that references the simulation data.\n    It is typically generated by the 'generate-xdmf' command.\n    You can also provide a second XMF file with higher resolution data, which\n    is used to render the outlines of elements to make them smoother.\n    \"\"\"\n    import paraview.simple as pv\n\n    # Load data\n    volume_data = pv.XDMFReader(\n        registrationName=\"VolumeData\", FileNames=[xmf_file]\n    )\n    if hi_res_xmf_file:\n        hi_res_volume_data = pv.XDMFReader(\n            registrationName=\"HiResVolumeData\", FileNames=[hi_res_xmf_file]\n        )\n\n    render_view = pv.GetActiveViewOrCreate(\"RenderView\")\n    render_view.UseLight = 0\n    render_view.UseColorPaletteForBackground = 0\n    render_view.Background = background_color\n    render_view.OrientationAxesVisibility = 0\n    if not animate:\n        render_view.ViewTime = volume_data.TimestepValues[time_step]\n        pv.UpdatePipeline(time=render_view.ViewTime, proxy=volume_data)\n\n    def slice_or_clip(triangulate, **kwargs):\n        if slice:\n            result = pv.Slice(\n                **kwargs,\n                SliceType=\"Plane\",\n                Triangulatetheslice=triangulate,\n            )\n            result.SliceType.Origin = clip_origin\n            result.SliceType.Normal = clip_normal\n        else:\n            result = pv.Clip(**kwargs, ClipType=\"Plane\")\n            result.ClipType.Origin = clip_origin\n            result.ClipType.Normal = clip_normal\n        return result\n\n    # Show grid\n    grid = slice_or_clip(\n        registrationName=\"Grid\", Input=volume_data, triangulate=False\n    )\n    grid_display = pv.Show(grid, render_view)\n    grid_display.Representation = \"Surface With Edges\"\n    grid_display.LineWidth = 1.0\n    grid_display.EdgeColor = 3 * [0.6]\n    # The following line works around a failure in `pv.ColorBy(..., None)`\n    grid_display.ColorArrayName = (\"POINTS\", None)\n    pv.ColorBy(grid_display, None)\n\n    # Show outline\n    outline_data = hi_res_volume_data if hi_res_xmf_file else volume_data\n    outline = slice_or_clip(\n        registrationName=\"Outline\", Input=outline_data, triangulate=True\n    )\n    outline_display = pv.Show(outline, render_view)\n    outline_display.Representation = \"Feature Edges\"\n    outline_display.LineWidth = 3.0\n    outline_display.DiffuseColor = 3 * [0.0]\n    outline_display.AmbientColor = 3 * [0.0]\n    # The following line works around a failure in `pv.ColorBy(..., None)`\n    outline_display.ColorArrayName = (\"POINTS\", None)\n    pv.ColorBy(outline_display, None)\n\n    # Show time annotation\n    if show_time_annotation:\n        annotate_time = pv.AnnotateTimeFilter(\n            registrationName=\"AnnotateTimeFilter\", Input=volume_data\n        )\n        annotate_time_display = pv.Show(annotate_time, render_view)\n        annotate_time_display.Color = 3 * [0.0]\n        annotate_time_display.FontSize = 24\n\n    # Set resolution\n    layout = pv.GetLayout()\n    layout.SetSize(1200, 1200)\n\n    # Configure camera\n    camera = pv.GetActiveCamera()\n    camera.SetFocalPoint(*clip_origin)\n    camera.SetPosition(*(np.array(clip_origin) + np.array(clip_normal) * 10.0))\n    camera.Roll(camera_phi)\n    camera.Elevation(-camera_theta)\n    pv.ResetCamera()\n    camera.Zoom(zoom_factor)\n\n    # Capture all frames\n    animation_scene = pv.GetAnimationScene()\n    animation_scene.PlayMode = \"Snap To TimeSteps\"\n\n    if animate:\n        pv.SaveAnimation(output, render_view)\n    else:\n        pv.Render()\n        pv.SaveScreenshot(output, render_view)\n\n\n@click.command(name=\"domain\", help=render_domain.__doc__)\n@click.argument(\n    \"xmf_file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.argument(\n    \"hi_res_xmf_file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    required=False,\n)\n@click.option(\n    \"--output\",\n    \"-o\",\n    type=click.Path(writable=True),\n    required=True,\n    help=\"Output file. Include extension such as '.png'.\",\n)\n@click.option(\n    \"--time-step\",\n    \"-t\",\n    callback=_parse_step,\n    default=\"first\",\n    show_default=True,\n    help=(\n        \"Select a time step. Specify '-1' or 'last' to select the last time\"\n        \" step.\"\n    ),\n)\n@click.option(\n    \"--show-time-annotation\", is_flag=True, help=\"Show time annotation.\"\n)\n@click.option(\n    \"--animate\", is_flag=True, help=\"Produce an animation of all time steps.\"\n)\n@click.option(\"zoom_factor\", \"--zoom\", help=\"Zoom factor.\", default=1.0)\n@click.option(\n    \"--camera-theta\",\n    type=float,\n    default=0.0,\n    help=\"Viewing angle from the z-axis in degrees.\",\n    show_default=True,\n)\n@click.option(\n    \"--camera-phi\",\n    type=float,\n    default=0.0,\n    help=\"Viewing angle around the z-axis in degrees.\",\n    show_default=True,\n)\n@click.option(\n    \"--clip-origin\",\n    \"--slice-origin\",\n    nargs=3,\n    type=float,\n    default=(0.0, 0.0, 0.0),\n    help=\"Origin of the clipping plane\",\n    show_default=True,\n)\n@click.option(\n    \"--clip-normal\",\n    \"--slice-normal\",\n    nargs=3,\n    type=float,\n    default=(0.0, 0.0, 1.0),\n    help=\"Normal of the clipping plane\",\n    show_default=True,\n)\n@click.option(\n    \"--slice/--clip\",\n    \"slice\",\n    default=False,\n    help=\"Use a slice instead of a clip.\",\n    show_default=True,\n)\n@click.option(\n    \"--background-color\",\n    nargs=3,\n    type=float,\n    default=(1.0, 1.0, 1.0),\n    help=\"Background color in RGB fractions (white is 1 1 1, black is 0 0 0).\",\n    show_default=True,\n)\ndef render_domain_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    render_domain(**kwargs)\n\n\nif __name__ == \"__main__\":\n    render_domain_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/Render3D/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport click\n\nfrom spectre.support.CliExceptions import RequiredChoiceError\n\n\nclass Render3DCommands(click.Group):\n    def list_commands(self, ctx):\n        return [\n            \"bbh\",\n            \"clip\",\n            \"domain\",\n        ]\n\n    def get_command(self, ctx, name):\n        if name == \"clip\":\n            from spectre.Visualization.Render3D.Clip import render_clip_command\n\n            return render_clip_command\n        elif name == \"domain\":\n            from spectre.Visualization.Render3D.Domain import (\n                render_domain_command,\n            )\n\n            return render_domain_command\n        elif name == \"bbh\":\n            from spectre.Visualization.Render3D.Bbh import render_bbh_command\n\n            return render_bbh_command\n        raise RequiredChoiceError(\n            f\"The command '{name}' is not implemented.\",\n            choices=self.list_commands(ctx),\n        )\n\n\n@click.group(cls=Render3DCommands)\ndef render_3d_command():\n    \"\"\"Renders a 3D visualization of simulation data.\n\n    See subcommands for possible renderings.\n    \"\"\"\n\n\nif __name__ == \"__main__\":\n    render_3d_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "src/Visualization/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport click\n\nfrom spectre.support.CliExceptions import RequiredChoiceError\n\n\nclass PlotCommands(click.Group):\n    def list_commands(self, ctx):\n        return [\n            \"along-line\",\n            \"cce\",\n            \"control-system\",\n            \"dat\",\n            \"eccentricity-control\",\n            \"elliptic-convergence\",\n            \"memory-monitors\",\n            \"power-monitors\",\n            \"size-control\",\n            \"slice\",\n            \"trajectories\",\n        ]\n\n    def get_command(self, ctx, name):\n        if name == \"along-line\":\n            from spectre.Visualization.PlotAlongLine import (\n                plot_along_line_command,\n            )\n\n            return plot_along_line_command\n        if name == \"cce\":\n            from spectre.Visualization.PlotCce import plot_cce_command\n\n            return plot_cce_command\n        elif name == \"control-system\":\n            from spectre.Visualization.PlotControlSystem import (\n                plot_control_system_command,\n            )\n\n            return plot_control_system_command\n        elif name == \"dat\":\n            from spectre.Visualization.PlotDatFile import plot_dat_command\n\n            return plot_dat_command\n        elif name in [\"eccentricity-control\", \"ecc-control\"]:\n            from spectre.Visualization.PlotEccentricityControl import (\n                plot_eccentricity_control_command,\n            )\n\n            return plot_eccentricity_control_command\n        elif name == \"elliptic-convergence\":\n            from spectre.Visualization.PlotEllipticConvergence import (\n                plot_elliptic_convergence_command,\n            )\n\n            return plot_elliptic_convergence_command\n        elif name == \"memory-monitors\":\n            from spectre.Visualization.PlotMemoryMonitors import (\n                plot_memory_monitors_command,\n            )\n\n            return plot_memory_monitors_command\n        elif name == \"power-monitors\":\n            from spectre.Visualization.PlotPowerMonitors import (\n                plot_power_monitors_command,\n            )\n\n            return plot_power_monitors_command\n        elif name in [\"size\", \"size-control\"]:\n            from spectre.Visualization.PlotSizeControl import (\n                plot_size_control_command,\n            )\n\n            return plot_size_control_command\n        elif name == \"slice\":\n            from spectre.Visualization.PlotSlice import plot_slice_command\n\n            return plot_slice_command\n        elif name == \"trajectories\":\n            from spectre.Visualization.PlotTrajectories import (\n                plot_trajectories_command,\n            )\n\n            return plot_trajectories_command\n        raise RequiredChoiceError(\n            f\"The command '{name}' is not implemented.\",\n            choices=self.list_commands(ctx),\n        )\n\n\n@click.group(cls=PlotCommands)\ndef plot_command():\n    \"\"\"Plot data from simulations\n\n    See subcommands for available plots.\n    \"\"\"\n    pass\n"
  },
  {
    "path": "src/Visualization/Python/plots.mplstyle",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Use a lighter gray than the default for axis grids\ngrid.color: lightgray\n\n# This is the 'deep' color palette from the 'seaborn' package. It's a variation\n# of matplotlib's default, but slightly more muted and more colorblind friendly.\n# To increase colorblind friendliness further we skip the orange color.\naxes.prop_cycle: cycler('color', ['4c72b0', '55a868', 'c44e52', '8172b3', '937860', 'da8bc3', '8c8c8c', 'ccb974', '64b5cd'])\n\nsavefig.bbox: tight\n"
  },
  {
    "path": "support/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Pipelines)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "support/Charm/v6.7.patch",
    "content": "diff --git a/src/scripts/charmc b/src/scripts/charmc\nindex 27720c2a3..00174b02e 100755\n--- a/src/scripts/charmc\n+++ b/src/scripts/charmc\n@@ -1816,7 +1816,7 @@ if test -n \"$BALANCER\"\n then\n     echo \"extern void LBDefaultCreate(const char *);\" >> $modInitSrc\n fi\n-echo \"void _registerExternalModules(char **argv) {\" >> $modInitSrc\n+echo \"void _registerExternalModules(char **argv) {static_cast<void>(argv);\" >> $modInitSrc\n for module in $MODULES; do\n     [ -n \"$VERBOSE\" ] && echo \"Adding registration for module $module\"\n     echo \"  _register$module();\" >> $modInitSrc\n@@ -1858,7 +1858,7 @@ fi\n echo \"}\" >> $modInitSrc\n \n # creating projections\n-echo \"void _createTraces(char **argv) {\" >> $modInitSrc\n+echo \"void _createTraces(char **argv) {static_cast<void>(argv);\" >> $modInitSrc\n for trace in $TRACEMODE; do\n     [ -n \"$VERBOSE\" ] && echo \"Adding registration for trace $trace\"\n     echo \"extern void _createTrace$trace(char **argv);\" >> $modInitSrc\ndiff --git a/src/util/pup.h b/src/util/pup.h\nindex a660ccc75..beebfddf0 100644\n--- a/src/util/pup.h\n+++ b/src/util/pup.h\n@@ -55,6 +55,7 @@ class bar {\n #define __CK_PUP_H\n \n #include <stdio.h> /*<- for \"FILE *\" */\n+#include <type_traits>\n \n #ifndef __cplusplus\n #error \"Use pup_c.h for C programs-- pup.h is for C++ programs\"\n@@ -696,6 +697,11 @@ public:\n     PUPable_decl_inside_template(SINGLE_ARG(className))   \\\n     PUPable_operator_inside(SINGLE_ARG(className))\n \n+#define PUPable_decl_base_template(baseClassName, className)   \\\n+  PUPable_decl_inside_base_template(SINGLE_ARG(baseClassName), \\\n+                                    SINGLE_ARG(className))     \\\n+      PUPable_operator_inside(SINGLE_ARG(className))\n+\n //PUPable_decl for classes inside a namespace: inside body\n #define PUPable_decl_inside(className) \\\n private: \\\n@@ -716,6 +722,22 @@ public: \\\n     static void register_PUP_ID(const char* name) { \\\n         my_PUP_ID=register_constructor(name,call_PUP_constructor);}\n \n+#define PUPable_decl_inside_base_template(baseClassName, className)      \\\n+ private:                                                                \\\n+  static PUP::able *call_PUP_constructor(void) {                         \\\n+    return new className((CkMigrateMessage *)0);                         \\\n+  }                                                                      \\\n+  static PUP::able::PUP_ID my_PUP_ID;                                    \\\n+                                                                         \\\n+ public:                                                                 \\\n+  virtual const PUP::able::PUP_ID &get_PUP_ID(void) const {              \\\n+    return my_PUP_ID;                                                    \\\n+  }                                                                      \\\n+  static void register_PUP_ID(const char *name) {                        \\\n+    my_PUP_ID =                                                          \\\n+        baseClassName::register_constructor(name, call_PUP_constructor); \\\n+  }\n+\n //PUPable_decl for classes inside a namespace: in header at file scope\n #define PUPable_decl_outside(className) \\\n      PUPable_operator_outside(className)\n@@ -751,6 +773,20 @@ public:\\\n \t\t{my_PUP_ID=register_constructor(name,\\\n \t\t              className::call_PUP_constructor);}\n \n+#define PUPable_def_specialized_template(className) \\\n+template <>\\\n+\tPUP::able *className::call_PUP_constructor(void) \\\n+\t\t{ return new className((CkMigrateMessage *)0);}\\\n+template <>\\\n+\tconst PUP::able::PUP_ID &className::get_PUP_ID(void) const\\\n+\t\t{ return className::my_PUP_ID; }\\\n+template <>\\\n+\tPUP::able::PUP_ID className::my_PUP_ID;\\\n+template <>\\\n+\tvoid className::register_PUP_ID(const char* name)\\\n+\t\t{my_PUP_ID=register_constructor(name,\\\n+\t\t              className::call_PUP_constructor);}\n+\n //Definitions to include exactly once at file scope\n #define PUPable_def_template(className) \\\n \ttemplate<> PUP::able::PUP_ID className::my_PUP_ID = 0;\n@@ -900,11 +936,29 @@ template<class T> inline void PUParray(PUP::er &p,T *t,int n) { \\\n /**\n   Default operator|: call pup routine.\n */\n-template<class T>\n-inline void operator|(PUP::er &p,T &t) { \n-\tp.syncComment(PUP::sync_begin_object);\n-\tt.pup(p);\n-\tp.syncComment(PUP::sync_end_object); \n+namespace charm_pup_details {\n+\n+template <typename... Ts>\n+struct make_void {\n+  using type = void;\n+};\n+template <typename... Ts>\n+using void_t = typename make_void<Ts...>::type;\n+\n+template <typename T, typename U = void>\n+struct is_pupable : std::false_type{};\n+\n+template <typename T>\n+struct is_pupable<\n+    T, void_t<decltype(std::declval<T>().pup(std::declval<PUP::er &>()))>>\n+    : std::true_type {};\n+\n+}  // namespace charm_pup_details\n+template <class T, typename std::enable_if<charm_pup_details::is_pupable<T>::value, int>::type = 0>\n+inline void operator|(PUP::er &p, T &t) {\n+  p.syncComment(PUP::sync_begin_object);\n+  t.pup(p);\n+  p.syncComment(PUP::sync_end_object);\n }\n \n /**\ndiff --git a/src/util/pup_stl.h b/src/util/pup_stl.h\nindex ef122b7bc..79c0dbd82 100644\n--- a/src/util/pup_stl.h\n+++ b/src/util/pup_stl.h\n@@ -48,8 +48,8 @@ namespace PUP {\n   inline void PUP_stl_container(er &p,container &c);\n   template <class container,class dtype>\n   inline void PUP_stl_map(er &p,container &c);\n-  template <class T>\n-  inline void operator|(er &p,typename std::vector<T> &v);\n+  template <class T, class A>\n+  inline void operator|(er &p,typename std::vector<T, A> &v);\n   template <class T>\n   inline void operator|(er &p,typename std::list<T> &v);\n   template <class V,class T,class Cmp>\n@@ -143,17 +143,10 @@ namespace PUP {\n   inline void PUP_stl_container(er &p,container &c) {\n     p.syncComment(sync_begin_array);\n     int nElem=PUP_stl_container_size(p,c);\n-    if (p.isUnpacking()) \n-      { //Unpacking: Extract each element and push_back:\n-\tc.resize(0);\n-\tfor (int i=0;i<nElem;i++) {\n-\t  p.syncComment(sync_item);\n-\t  dtype n;\n-\t  p|n;\n-\t  c.push_back(n);\n-\t} \n-      }\n-    else PUP_stl_container_items<container, dtype>(p,c);\n+    if (p.isUnpacking()) {  // Unpacking: Extract each element and push_back:\n+      c.resize(nElem);\n+    }\n+    PUP_stl_container_items<container, dtype>(p, c);\n     p.syncComment(sync_end_array);\n   }\n   //Map objects don't have a \"push_back\", while vector and list\n@@ -174,9 +167,21 @@ namespace PUP {\n     p.syncComment(sync_end_list);\n   }\n \n-  template <class T> \n-  inline void operator|(er &p,typename std::vector<T> &v)\n-  { PUP_stl_container<std::vector<T>,T>(p,v); }\n+  template <class T, class A>\n+  inline void operator|(er &p, typename std::vector<T, A> &v) {\n+    if (std::is_arithmetic<T>::value) {\n+      int number_of_elements = PUP_stl_container_size(p, v);\n+      if (p.isUnpacking()) {\n+        if (v.size() != number_of_elements) {\n+          v.resize(static_cast<size_t>(number_of_elements));\n+          v.shrink_to_fit();\n+        }\n+      }\n+      PUParray(p, v.data(), number_of_elements);\n+    } else {\n+      PUP_stl_container<std::vector<T, A>, T>(p, v);\n+    }\n+  }\n   template <class T> \n   inline void operator|(er &p,typename std::list<T> &v)\n   { PUP_stl_container<std::list<T>,T>(p,v); }\n"
  },
  {
    "path": "support/Charm/v6.8.patch",
    "content": "diff --git a/src/util/pup.h b/src/util/pup.h\nindex 0cf0043..61fecbe 100644\n--- a/src/util/pup.h\n+++ b/src/util/pup.h\n@@ -55,6 +55,7 @@ class bar {\n #define __CK_PUP_H\n \n #include <stdio.h> /*<- for \"FILE *\" */\n+#include <type_traits>\n \n #ifndef __cplusplus\n #error \"Use pup_c.h for C programs-- pup.h is for C++ programs\"\n@@ -703,6 +704,11 @@ public:\n     PUPable_decl_inside_template(SINGLE_ARG(className))   \\\n     PUPable_operator_inside(SINGLE_ARG(className))\n \n+#define PUPable_decl_base_template(baseClassName, className)                   \\\n+  PUPable_decl_inside_base_template(SINGLE_ARG(baseClassName),                 \\\n+                                    SINGLE_ARG(className))                     \\\n+      PUPable_operator_inside(SINGLE_ARG(className))\n+\n //PUPable_decl for classes inside a namespace: inside body\n #define PUPable_decl_inside(className) \\\n private: \\\n@@ -723,6 +729,22 @@ public: \\\n     static void register_PUP_ID(const char* name) { \\\n         my_PUP_ID=register_constructor(name,call_PUP_constructor);}\n \n+#define PUPable_decl_inside_base_template(baseClassName, className)            \\\n+private:                                                                       \\\n+  static PUP::able *call_PUP_constructor(void) {                               \\\n+    return new className((CkMigrateMessage *)0);                               \\\n+  }                                                                            \\\n+  static PUP::able::PUP_ID my_PUP_ID;                                          \\\n+                                                                               \\\n+public:                                                                        \\\n+  virtual const PUP::able::PUP_ID &get_PUP_ID(void) const {                    \\\n+    return my_PUP_ID;                                                          \\\n+  }                                                                            \\\n+  static void register_PUP_ID(const char *name) {                              \\\n+    my_PUP_ID =                                                                \\\n+        baseClassName::register_constructor(name, call_PUP_constructor);       \\\n+  }\n+\n //PUPable_decl for classes inside a namespace: in header at file scope\n #define PUPable_decl_outside(className) \\\n      PUPable_operator_outside(className)\n@@ -907,11 +929,29 @@ template<class T> inline void PUParray(PUP::er &p,T *t,size_t n) { \\\n /**\n   Default operator|: call pup routine.\n */\n-template<class T>\n-inline void operator|(PUP::er &p,T &t) { \n-\tp.syncComment(PUP::sync_begin_object);\n-\tt.pup(p);\n-\tp.syncComment(PUP::sync_end_object); \n+namespace charm_pup_details {\n+\n+template <typename... Ts>\n+struct make_void {\n+  using type = void;\n+};\n+template <typename... Ts> using void_t = typename make_void<Ts...>::type;\n+\n+template <typename T, typename U = void> struct is_pupable : std::false_type {};\n+\n+template <typename T>\n+struct is_pupable<\n+    T, void_t<decltype(std::declval<T>().pup(std::declval<PUP::er &>()))>>\n+    : std::true_type {};\n+\n+}  // namespace charm_pup_details\n+\n+template <class T, typename std::enable_if<\n+                       charm_pup_details::is_pupable<T>::value, int>::type = 0>\n+inline void operator|(PUP::er &p, T &t) {\n+  p.syncComment(PUP::sync_begin_object);\n+  t.pup(p);\n+  p.syncComment(PUP::sync_end_object);\n }\n \n /**\ndiff --git a/src/util/pup_stl.h b/src/util/pup_stl.h\nindex 1f1d9e5..6fc96e4 100644\n--- a/src/util/pup_stl.h\n+++ b/src/util/pup_stl.h\n@@ -46,10 +46,6 @@ namespace PUP {\n   inline void operator|(er &p,typename std::pair<const A,B> &v);\n   template <class T>\n   inline void operator|(er &p,std::complex<T> &v);\n-#if !CMK_USING_XLC\n-  template <class T>\n-  inline void operator|(er &p, std::unique_ptr<T, std::default_delete<T>> &ptr);\n-#endif\n   template <class charType>\n   inline void operator|(er &p,typename std::basic_string<charType> &v);\n   inline void operator|(er &p,std::string &v);\n@@ -109,21 +105,6 @@ namespace PUP {\n     p|re; p|im;\n     v=std::complex<T>(re,im);\n   }\n-#if !CMK_USING_XLC\n-  template <class T>\n-  inline void operator|(er &p, std::unique_ptr<T, std::default_delete<T>> &ptr)\n-  {\n-    bool nonNull = static_cast<bool>(ptr);\n-    p|nonNull;\n-\n-    if (nonNull) {\n-      if (p.isUnpacking())\n-        ptr.reset(new T);\n-\n-      p|(*ptr);\n-    }\n-  }\n-#endif\n   template <class charType> \n   inline void operator|(er &p,typename std::basic_string<charType> &v)\n   {\n@@ -254,17 +235,6 @@ namespace PUP {\n   inline void operator|(er &p,typename std::map<V,T,Cmp> &m)\n   //{ PUP_stl_map<std::map<V,T,Cmp>,std::pair<const V,T> >(p,m); }    // 'const' confuses old version of a SUN CC compiler\n   { PUP_stl_map<std::map<V,T,Cmp>,std::pair<V,T> >(p,m); }\n-#if !CMK_USING_XLC\n-  template <class V,class T,class Cmp>\n-  inline void operator|(er &p,typename std::unordered_map<V,T,Cmp> &m)\n-  //{ PUP_stl_map<std::unordered_map<V,T,Cmp>,std::pair<const V,T> >(p,m); }    // 'const' confuses old version of a SUN CC compiler\n-  { PUP_stl_map<std::unordered_map<V,T,Cmp>,std::pair<V,T> >(p,m); }\n-#else\n-  template <class V,class T,class Cmp>\n-  inline void operator|(er &p,typename std::tr1::unordered_map<V,T,Cmp> &m)\n-  //{ PUP_stl_map<std::unordered_map<V,T,Cmp>,std::pair<const V,T> >(p,m); }    // 'const' confuses old version of a SUN CC compiler\n-  { PUP_stl_map<std::tr1::unordered_map<V,T,Cmp>,std::pair<V,T> >(p,m); }\n-#endif\n   template <class V,class T,class Cmp> \n   inline void operator|(er &p,typename std::multimap<V,T,Cmp> &m)\n   { PUP_stl_map<std::multimap<V,T,Cmp>,std::pair<const V,T> >(p,m); }\n"
  },
  {
    "path": "support/Charm/v7.0.0.patch",
    "content": "diff --git a/cmake/detect-features-cxx.cmake b/cmake/detect-features-cxx.cmake\nindex d3aa6ab94..830da9cf1 100644\n--- a/cmake/detect-features-cxx.cmake\n+++ b/cmake/detect-features-cxx.cmake\n@@ -38,12 +38,12 @@ endif()\n \n # Needed so that tlsglobals works correctly with --build-shared\n # See https://github.com/UIUC-PPL/charm/issues/3168 for details.\n-check_cxx_compiler_flag(\"-ftls-model=initial-exec\" CMK_COMPILER_KNOWS_FTLS_MODEL)\n-if(CMK_COMPILER_KNOWS_FTLS_MODEL)\n-  set(OPTS_CC \"${OPTS_CC} -ftls-model=initial-exec\")\n-  set(OPTS_CXX \"${OPTS_CXX} -ftls-model=initial-exec\")\n-  set(OPTS_LD \"${OPTS_LD} -ftls-model=initial-exec\")\n-endif()\n+# check_cxx_compiler_flag(\"-ftls-model=initial-exec\" CMK_COMPILER_KNOWS_FTLS_MODEL)\n+# if(CMK_COMPILER_KNOWS_FTLS_MODEL)\n+#   set(OPTS_CC \"${OPTS_CC} -ftls-model=initial-exec\")\n+#   set(OPTS_CXX \"${OPTS_CXX} -ftls-model=initial-exec\")\n+#   set(OPTS_LD \"${OPTS_LD} -ftls-model=initial-exec\")\n+# endif()\n \n # Allow seeing own symbols dynamically, needed for programmatic backtraces\n check_cxx_compiler_flag(\"-rdynamic\" CMK_COMPILER_KNOWS_RDYNAMIC)\ndiff --git a/src/scripts/configure.ac b/src/scripts/configure.ac\nindex f6c0f311b..41a7c9f46 100644\n--- a/src/scripts/configure.ac\n+++ b/src/scripts/configure.ac\n@@ -820,15 +820,15 @@ then\n fi\n \n # Needed so that tlsglobals works correctly with --build-shared\n-# See https://github.com/UIUC-PPL/charm/issues/3168 for details.\n-test_cxx \"whether C++ compiler accepts -ftls-model=initial-exec\" \"yes\" \"no\" \"-ftls-model=initial-exec\"\n-if test $strictpass -eq 1\n-then\n-    add_flag 'CMK_COMPILER_KNOWS_FTLS_MODEL=\"1\"' \"tlsglobals\"\n-    OPTS_CC=\"$OPTS_CC -ftls-model=initial-exec\"\n-    OPTS_CXX=\"$OPTS_CXX -ftls-model=initial-exec\"\n-    OPTS_LD=\"$OPTS_LD -ftls-model=initial-exec\"\n-fi\n+# # See https://github.com/UIUC-PPL/charm/issues/3168 for details.\n+# test_cxx \"whether C++ compiler accepts -ftls-model=initial-exec\" \"yes\" \"no\" \"-ftls-model=initial-exec\"\n+# if test $strictpass -eq 1\n+# then\n+#     add_flag 'CMK_COMPILER_KNOWS_FTLS_MODEL=\"1\"' \"tlsglobals\"\n+#     OPTS_CC=\"$OPTS_CC -ftls-model=initial-exec\"\n+#     OPTS_CXX=\"$OPTS_CXX -ftls-model=initial-exec\"\n+#     OPTS_LD=\"$OPTS_LD -ftls-model=initial-exec\"\n+# fi\n \n # Test for a flag important for shared linking\n test_cxx \"whether C++ compiler accepts -fvisibility=hidden\" \"yes\" \"no\" \"-fvisibility=hidden\"\ndiff --git a/src/util/ckhashtable.h b/src/util/ckhashtable.h\nindex 3f2c895aa..6b19c4641 100644\n--- a/src/util/ckhashtable.h\n+++ b/src/util/ckhashtable.h\n@@ -437,9 +437,9 @@ as a fast key like this:\n template <class T> class CkHashtableAdaptorT {\n \tT val;\n public:\n-\tCkHashtableAdaptorT<T>(const T &v):val(v) {}\n+\tCkHashtableAdaptorT(const T &v):val(v) {}\n \t/**added to allow pup to do Key k while unPacking*/\n-\tCkHashtableAdaptorT<T>(){}\n+\tCkHashtableAdaptorT(){}\n \toperator T & () {return val;}\n \toperator const T & () const {return val;}\n \tinline CkHashCode hash(void) const \ndiff --git a/src/util/pup_util.C b/src/util/pup_util.C\nindex 82206c118..22ad68fd9 100644\n--- a/src/util/pup_util.C\n+++ b/src/util/pup_util.C\n@@ -580,12 +580,16 @@ static PUP_registry *PUP_getRegistry(void) {\n \treturn reg;\n }\n \n-const PUP_regEntry *PUP_getRegEntry(const PUP::able::PUP_ID &id)\n+const PUP_regEntry *PUP_getRegEntry(const PUP::able::PUP_ID &id,\n+                                    const char *const name_hint = NULL)\n {\n \tconst PUP_regEntry *cur=(const PUP_regEntry *)(\n \t\tPUP_getRegistry()->CkHashtable::get((const void *)&id) );\n-\tif (cur==NULL)\n-\t\tCmiAbort(\"Unrecognized PUP::able::PUP_ID. is there an unregistered module?\");\n+\tif (cur==NULL){\n+          if (name_hint != NULL)\n+            CmiAbort(\"Unrecognized PUP::able::PUP_ID for %s\", name_hint);\n+          CmiAbort(\"Unrecognized PUP::able::PUP_ID. is there an unregistered module?\");\n+        }\n \treturn cur;\n }\n \n@@ -623,7 +627,7 @@ void PUP::er::object(able** a)\n \t\t} else {\n \t\t\tconst PUP::able::PUP_ID &id=(*a)->get_PUP_ID();\n \t\t\tid.pup(*this);\n-\t\t\tr=PUP_getRegEntry(id);\n+\t\t\tr=PUP_getRegEntry(id, typeid(**a).name());\n \t\t}\n \t}\n \tsyncComment(PUP::sync_begin_object,r->name);\n"
  },
  {
    "path": "support/Charm/v7.0.1.patch",
    "content": "diff --git a/cmake/detect-features-cxx.cmake b/cmake/detect-features-cxx.cmake\nindex d3aa6ab94..830da9cf1 100644\n--- a/cmake/detect-features-cxx.cmake\n+++ b/cmake/detect-features-cxx.cmake\n@@ -38,12 +38,12 @@ endif()\n \n # Needed so that tlsglobals works correctly with --build-shared\n # See https://github.com/UIUC-PPL/charm/issues/3168 for details.\n-check_cxx_compiler_flag(\"-ftls-model=initial-exec\" CMK_COMPILER_KNOWS_FTLS_MODEL)\n-if(CMK_COMPILER_KNOWS_FTLS_MODEL)\n-  set(OPTS_CC \"${OPTS_CC} -ftls-model=initial-exec\")\n-  set(OPTS_CXX \"${OPTS_CXX} -ftls-model=initial-exec\")\n-  set(OPTS_LD \"${OPTS_LD} -ftls-model=initial-exec\")\n-endif()\n+# check_cxx_compiler_flag(\"-ftls-model=initial-exec\" CMK_COMPILER_KNOWS_FTLS_MODEL)\n+# if(CMK_COMPILER_KNOWS_FTLS_MODEL)\n+#   set(OPTS_CC \"${OPTS_CC} -ftls-model=initial-exec\")\n+#   set(OPTS_CXX \"${OPTS_CXX} -ftls-model=initial-exec\")\n+#   set(OPTS_LD \"${OPTS_LD} -ftls-model=initial-exec\")\n+# endif()\n \n # Allow seeing own symbols dynamically, needed for programmatic backtraces\n check_cxx_compiler_flag(\"-rdynamic\" CMK_COMPILER_KNOWS_RDYNAMIC)\ndiff --git a/src/scripts/configure.ac b/src/scripts/configure.ac\nindex f6c0f311b..41a7c9f46 100644\n--- a/src/scripts/configure.ac\n+++ b/src/scripts/configure.ac\n@@ -820,15 +820,15 @@ then\n fi\n \n # Needed so that tlsglobals works correctly with --build-shared\n-# See https://github.com/UIUC-PPL/charm/issues/3168 for details.\n-test_cxx \"whether C++ compiler accepts -ftls-model=initial-exec\" \"yes\" \"no\" \"-ftls-model=initial-exec\"\n-if test $strictpass -eq 1\n-then\n-    add_flag 'CMK_COMPILER_KNOWS_FTLS_MODEL=\"1\"' \"tlsglobals\"\n-    OPTS_CC=\"$OPTS_CC -ftls-model=initial-exec\"\n-    OPTS_CXX=\"$OPTS_CXX -ftls-model=initial-exec\"\n-    OPTS_LD=\"$OPTS_LD -ftls-model=initial-exec\"\n-fi\n+# # See https://github.com/UIUC-PPL/charm/issues/3168 for details.\n+# test_cxx \"whether C++ compiler accepts -ftls-model=initial-exec\" \"yes\" \"no\" \"-ftls-model=initial-exec\"\n+# if test $strictpass -eq 1\n+# then\n+#     add_flag 'CMK_COMPILER_KNOWS_FTLS_MODEL=\"1\"' \"tlsglobals\"\n+#     OPTS_CC=\"$OPTS_CC -ftls-model=initial-exec\"\n+#     OPTS_CXX=\"$OPTS_CXX -ftls-model=initial-exec\"\n+#     OPTS_LD=\"$OPTS_LD -ftls-model=initial-exec\"\n+# fi\n \n # Test for a flag important for shared linking\n test_cxx \"whether C++ compiler accepts -fvisibility=hidden\" \"yes\" \"no\" \"-fvisibility=hidden\"\ndiff --git a/src/util/ckhashtable.h b/src/util/ckhashtable.h\nindex 3f2c895aa..6b19c4641 100644\n--- a/src/util/ckhashtable.h\n+++ b/src/util/ckhashtable.h\n@@ -437,9 +437,9 @@ as a fast key like this:\n template <class T> class CkHashtableAdaptorT {\n \tT val;\n public:\n-\tCkHashtableAdaptorT<T>(const T &v):val(v) {}\n+\tCkHashtableAdaptorT(const T &v):val(v) {}\n \t/**added to allow pup to do Key k while unPacking*/\n-\tCkHashtableAdaptorT<T>(){}\n+\tCkHashtableAdaptorT(){}\n \toperator T & () {return val;}\n \toperator const T & () const {return val;}\n \tinline CkHashCode hash(void) const \ndiff --git a/src/util/pup_util.C b/src/util/pup_util.C\nindex 82206c118..22ad68fd9 100644\n--- a/src/util/pup_util.C\n+++ b/src/util/pup_util.C\n@@ -580,12 +580,16 @@ static PUP_registry *PUP_getRegistry(void) {\n \treturn reg;\n }\n \n-const PUP_regEntry *PUP_getRegEntry(const PUP::able::PUP_ID &id)\n+const PUP_regEntry *PUP_getRegEntry(const PUP::able::PUP_ID &id,\n+                                    const char *const name_hint = NULL)\n {\n \tconst PUP_regEntry *cur=(const PUP_regEntry *)(\n \t\tPUP_getRegistry()->CkHashtable::get((const void *)&id) );\n-\tif (cur==NULL)\n-\t\tCmiAbort(\"Unrecognized PUP::able::PUP_ID. is there an unregistered module?\");\n+\tif (cur==NULL){\n+          if (name_hint != NULL)\n+            CmiAbort(\"Unrecognized PUP::able::PUP_ID for %s\", name_hint);\n+          CmiAbort(\"Unrecognized PUP::able::PUP_ID. is there an unregistered module?\");\n+        }\n \treturn cur;\n }\n \n@@ -623,7 +627,7 @@ void PUP::er::object(able** a)\n \t\t} else {\n \t\t\tconst PUP::able::PUP_ID &id=(*a)->get_PUP_ID();\n \t\t\tid.pup(*this);\n-\t\t\tr=PUP_getRegEntry(id);\n+\t\t\tr=PUP_getRegEntry(id, typeid(**a).name());\n \t\t}\n \t}\n \tsyncComment(PUP::sync_begin_object,r->name);\n"
  },
  {
    "path": "support/DevEnvironments/spack.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# SpECTRE development environment that can be installed with the Spack package\n# manager, both on clusters and personal machines.\n#\n# To install this environment, first clone\n# [Spack](https://github.com/spack/spack) and refer to the\n# [docs](https://spack.readthedocs.io/) for an introduction to Spack. Then:\n#\n#   $ spack env create YOUR_ENV_NAME support/DevEnvironments/spack.yaml\n#   $ spack env activate YOUR_ENV_NAME -p\n#\n# Now you can adjust the environment for your system. You may want to `spack\n# remove` and `spack add` some packages, e.g., to customize the Charm++\n# installation or to omit packages provided by your system or installed via\n# another package manager. To generate the list of packages that will be\n# installed, run:\n#\n#   $ spack concretize -f [--reuse]\n#\n# You may want to run `spack external find` and concretize with `--reuse` to\n# avoid reinstalling a bunch of system-provided packages. When you are happy\n# with the concretized environment, run:\n#\n#   $ spack install\n#\n# All dependencies will be installed in the Spack build tree and linked into the\n# environment. Now you can run CMake, build SpECTRE, etc. To pass options like\n# `CHARM_ROOT` to CMake, if necessary, you can find the location of installed\n# packages with `spack location --install-dir`.\n#\n# Since the `spack` command is quite slow, you can also generate a module file\n# that is much faster to source:\n#\n#   $ spack env loads -r\n#\n# Now you can activate the environment by sourcing the generated module file.\n#\n# See the [Spack docs on environments](https://spack.readthedocs.io/en/latest/environments.html)\n# for more information.\n\nspack:\n  specs:\n  - 'blaze@3.8:3.8'\n  - 'boost@1.60:+math+program_options'\n  - 'catch2@3.4.0:3'\n  # Charm++:\n  # - The 'multicore' backend runs with shared memory on a single node. On\n  #   clusters you should choose one of the multi-node backends instead.\n  - 'charmpp@7.0.0: backend=multicore'\n  - 'cmake@3.18:'\n  - doxygen\n  - gsl\n  - hdf5 -mpi\n  - jemalloc\n  - 'libxsmm@1.16.1:'\n  - openblas\n  - 'python@3.8:'\n  - 'yaml-cpp@0.6.3:'\n  concretizer:\n    unify: true\n  view: true\n"
  },
  {
    "path": "support/Environments/anvil_gcc.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Load system modules\nspectre_load_sys_modules() {\n    # Make sure the core default modules are loaded.\n    # Without these you can get weird compilation errors\n    # that make no sense.\n    module load modtree/cpu libfabric zlib numactl\n\n    module load gcc/11.2.0\n    module load boost/1.74.0\n    module load cmake/3.20.0\n    module load gsl/2.4\n    module load hdf5/1.10.7\n    module load openblas/0.3.17\n    module load python/3.9.5\n    module unload openmpi\n}\n\n# Unload system modules\nspectre_unload_sys_modules() {\n    module unload boost/1.74.0\n    module unload cmake/3.20.0\n    module unload gsl/2.4\n    module unload hdf5/1.10.7\n    module unload openblas/0.3.17\n    module unload python/3.9.5\n    module unload gcc/11.2.0\n}\n\n\nspectre_setup_modules() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n\n    \"${SPECTRE_HOME}/support/Environments/setup/anvil_gcc.sh\" \"$@\"\n    local ret=$?\n    if [ \"${ret}\" -ne 0 ] ; then\n        echo >&2\n        echo \"Module setup failed!\" >&2\n    fi\n    return \"${ret}\"\n}\n\nspectre_unload_modules() {\n    module unload spectre_python\n    module unload charm\n    module unload yaml-cpp\n    module unload libxsmm\n    module unload libsharp\n    module unload catch\n    module unload brigand\n    module unload blaze\n    module unload impi\n\n    spectre_unload_sys_modules\n}\n\nspectre_load_modules() {\n    spectre_load_sys_modules\n\n    module load impi\n    module load blaze\n    module load brigand\n    module load catch\n    module load libsharp\n    module load libxsmm\n    module load yaml-cpp\n    module load charm\n    module load spectre_python\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n    # -D USE_LD=ld - ld.gold seems to hang linking the main executables\n    cmake -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D MEMORY_ALLOCATOR=SYSTEM \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D Python_EXECUTABLE=`which python3` \\\n          -D USE_LD=ld \\\n          -D SPECTRE_TEST_RUNNER=\"$(pwd)/bin/charmrun\" \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/caltech_hpc_gcc.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_setup_modules() {\n    module use /central/groups/sxs/modules/\n    echo \"Place the following line in your '~/.bashrc' so you don't have to \"\n    echo \"run 'spectre_setup_modules' every time you log in:\"\n    echo \"\"\n    echo \"module use /central/groups/sxs/modules/\"\n}\n\nspectre_load_modules() {\n    module use /central/groups/sxs/modules/\n    module use /resnick/groups/sxs/modules/\n    module load spectre-deps/2025-09\n}\n\nspectre_unload_modules() {\n    module use /central/groups/sxs/modules/\n    module use /resnick/groups/sxs/modules/\n    module unload spectre-deps/2025-09\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n\n    # Notes:\n    # - Choosing the memory allocator to be JEMALLOC is important. When using\n    #   the SYSTEM allocator, during BBH runs dumping volume data too often\n    #   would result in memory corruption errors. Switching to JEMALLOC fixed\n    #   these issues. It is still unclear *why* the SYSTEM allocator caused\n    #   these issues. --Kyle Nelli\n    # - We turn of docs because we aren't loading a Doxygen module. Could be\n    #   added though.\n    # - We override the architecture to skylake because that's the oldest type\n    #   of nodes on CaltechHPC. All dependencies were also compiled for skylake.\n    #   This means we should be able to run on all types of nodes available on\n    #   the cluster.\n    cmake -D CMAKE_C_COMPILER=gcc \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D BLA_VENDOR=OpenBLAS \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D BUILD_DOCS=OFF \\\n          -D DEBUG_SYMBOLS=OFF \\\n          -D MEMORY_ALLOCATOR=JEMALLOC \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D MACHINE=CaltechHpc \\\n          -D OVERRIDE_ARCH=skylake \\\n          -D ENABLE_PARAVIEW=ON \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/expanse_gcc.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Load system modules\nspectre_load_sys_modules() {\n    # Make sure the core default modules are loaded.\n    # Without these you can get weird compilation errors\n    # that make no sense.\n    module load shared cpu sdsc DefaultModules slurm/expanse numactl\n\n    module load gcc/10.2.0\n    module load openblas/0.3.10-openmp\n    module load gsl/2.5\n    module load boost/1.74.0\n    module load cmake/3.18.2\n    module load hwloc\n    module load libunwind\n    module load intel-mpi/2019.8.254\n    module load hdf5/1.10.7\n    module load python/3.8.5\n    module load doxygen/1.8.17\n}\n\n# Unload system modules\nspectre_unload_sys_modules() {\n    module unload doxygen/1.8.17\n    module unload python/3.8.5\n    module unload hdf5/1.10.7\n    module unload intel-mpi/2019.8.254\n    module unload libunwind\n    module unload hwloc\n    module unload cmake/3.18.2\n    module unload boost/1.74.0\n    module unload gsl/2.5\n    module unload openblas/0.3.10-openmp\n    module unload gcc/10.2.0\n}\n\n\nspectre_setup_modules() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n\n    \"${SPECTRE_HOME}/support/Environments/setup/expanse_gcc.sh\" \"$@\"\n    local ret=$?\n    if [ \"${ret}\" -ne 0 ] ; then\n        echo >&2\n        echo \"Module setup failed!\" >&2\n    fi\n    return \"${ret}\"\n}\n\nspectre_unload_modules() {\n    module unload spectre_zlib\n    module unload spectre_python\n    module unload charm\n    module unload scotch\n    module unload yaml-cpp\n    module unload jemalloc\n    module unload libxsmm\n    module unload libsharp\n    module unload catch\n    module unload brigand\n    module unload blaze\n\n    spectre_unload_sys_modules\n}\n\nspectre_load_modules() {\n    spectre_load_sys_modules\n\n    module load spectre_zlib\n    module load spectre_python\n    module load blaze\n    module load brigand\n    module load catch\n    module load libsharp\n    module load libxsmm\n    module load jemalloc\n    module load yaml-cpp\n    module load scotch\n    module load charm\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n    # note that without the below gcc LFS flags, the PCH becomes\n    # inconsistent with the source flags, resulting in an invalid PCH\n    cmake -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D MEMORY_ALLOCATOR=JEMALLOC \\\n          -D DEBUG_SYMBOLS=off \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D CMAKE_CXX_FLAGS=\"-D_FILE_OFFSET_BITS=64 \\\n-D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE\" \\\n          -D USE_SCOTCH_LB=ON \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/frontera_gcc.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Load system modules\nspectre_load_sys_modules() {\n    # impi is loaded, which it should be by default\n    # but explicitly load it in just in case\n    module load gcc/13.2.0\n    module load impi\n    module load mkl\n    module load gsl\n    module load hdf5\n    module load boost\n    module load cmake/3.24.2\n}\n\n# Unload system modules\nspectre_unload_sys_modules() {\n    module unload cmake\n    module unload boost\n    module unload hdf5\n    module unload gsl\n    module unload mkl\n    module unload gcc/13.2.0\n    # Don't unload impi as this is one of the default system modules\n}\n\n\nspectre_setup_modules() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n\n    \"${SPECTRE_HOME}/support/Environments/setup/frontera_gcc.sh\" \"$@\"\n    local ret=$?\n    if [ \"${ret}\" -ne 0 ] ; then\n        echo >&2\n        echo \"Module setup failed!\" >&2\n    fi\n    return \"${ret}\"\n}\n\nspectre_unload_modules() {\n    module unload spectre_python\n    module unload charm_mpi\n    module unload yaml-cpp\n    module unload spectre_boost\n    module unload libxsmm\n    module unload libsharp\n    module unload brigand\n    module unload blaze\n\n    spectre_unload_sys_modules\n}\n\nspectre_load_modules() {\n    spectre_load_sys_modules\n\n    module load blaze\n    module load brigand\n    module load libsharp\n    module load libxsmm\n    module load spectre_boost\n    module load yaml-cpp\n    module load charm_mpi\n    module load spectre_python\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n    # -D USE_LD=ld - ld.gold seems to hang linking the main executables\n    # no functioning Python 3.8 with newer gcc version\n    cmake -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D MEMORY_ALLOCATOR=SYSTEM \\\n          -D ENABLE_PYTHON=OFF \\\n          -D BUILD_TESTING=OFF \\\n          -D BUILD_PYTHON_BINDINGS=OFF \\\n          -D BUILD_DOCS=OFF \\\n          -D USE_LD=ld \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/mbot.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_setup_modules() {\n    echo \"All modules on Mbot are provided by the system\"\n}\n\nspectre_load_modules() {\n    # The order here is important\n    module load gcc/11.4.0\n    module load spectre-deps > /dev/null 2>&1\n}\n\nspectre_unload_modules() {\n    # The order here is important\n    module unload spectre-deps > /dev/null 2>&1\n    module unload gcc/11.4.0\n}\n\nspectre_run_cmake_gcc() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules > /dev/null 2>&1\n    cmake -D CMAKE_C_COMPILER=gcc \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D MEMORY_ALLOCATOR=JEMALLOC \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D ENABLE_PARAVIEW=ON \\\n          -D MACHINE=Mbot \\\n          -D USE_XSIMD=yes \\\n          -D DEBUG_SYMBOLS=OFF \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n\nspectre_run_cmake_clang() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules > /dev/null 2>&1\n    cmake -D CMAKE_C_COMPILER=clang \\\n          -D CMAKE_CXX_COMPILER=clang++ \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D MEMORY_ALLOCATOR=JEMALLOC \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D ENABLE_PARAVIEW=ON \\\n          -D MACHINE=Mbot \\\n          -D USE_XSIMD=yes \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/ocean2.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_setup_modules() {\n    echo \"All modules on Ocean2 are provided by the system\"\n}\n\nspectre_unload_modules() {\n    module unload gnu12/12.3.0\n    module unload intel/mpi/2021.16\n    module unload cmake/3.24.2\n    module unload openblas/0.3.27\n    module unload blaze/3.8\n    module unload boost/1.85.0\n    module unload catch2/3.5.4\n    module unload gsl/2.8\n    module unload hdf5/1.14.6\n    module unload jemalloc/5.3.0\n    module unload libxsmm/1.17\n    module unload yaml-cpp/0.7.0\n    module unload libffi/3.4.5\n    module unload python/3.12.4\n    module unload python/spectre-python-2025.08.19\n    module unload llvm/18.1.8\n    module unload libbacktrace/2024.07.09\n    module unload yasm/1.3.0\n    module unload ffmpeg/7.0.1\n    module unload fftw/3.3.10\n    module unload petsc/3.21.3\n    module unload charm/8.0.0\n    module unload libbacktrace/2024.07.09\n    module unload xsimd/13.2.0\n}\n\nspectre_load_modules() {\n    module load gnu12/12.3.0\n    module load intel/mpi/2021.16\n    module load cmake/3.24.2\n    module load openblas/0.3.27\n    module load blaze/3.8\n    module load boost/1.85.0\n    module load catch2/3.5.4\n    module load gsl/2.8\n    module load hdf5/1.14.6\n    module load jemalloc/5.3.0\n    module load libxsmm/1.17\n    module load yaml-cpp/0.7.0\n    module load libffi/3.4.5\n    module load python/3.12.4\n    module load python/spectre-python-2025.08.19\n    module load llvm/18.1.8\n    module load libbacktrace/2024.07.09\n    module load yasm/1.3.0\n    module load ffmpeg/7.0.1\n    module load fftw/3.3.10\n    module load petsc/3.21.3\n    module load charm/8.0.0\n    module load libbacktrace/2024.07.09\n    module load xsimd/13.2.0\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n    cmake -D CMAKE_C_COMPILER=gcc \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D BLA_VENDOR=OpenBLAS \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D BUILD_DOCS=OFF \\\n          -D DEBUG_SYMBOLS=OFF \\\n          -D MEMORY_ALLOCATOR=JEMALLOC \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D BUILD_SHARED_LIBS=ON \\\n          -D MACHINE=Ocean2 \\\n          -D ENABLE_PARAVIEW=OFF \\\n          -D BOOTSTRAP_PY_DEPS=ON \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/ocean2_orca1.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_setup_modules() {\n    echo \"All modules on Ocean2 are provided by the system\"\n}\n\nspectre_unload_modules() {\n    module unload gnu12/12.3.0\n    module unload intel/mpi/2021.16\n    module unload git/2.43.0\n    module unload cmake/3.24.2\n    module unload python/3.12.4\n    module unload orca1/openblas/0.3.27\n    module unload orca1/boost/1.85.0\n    module unload orca1/gsl/2.8\n    module unload orca1/hdf5/1.12.3\n    module unload orca1/charm/8.0.0\n    module unload orca1/libxsmm/1.16.1\n    module unload orca1/catch2/3.5.4\n    module unload orca1/yaml-cpp/0.7.0\n    module unload blaze/3.8\n    module unload xsimd/13.2.0\n}\n\nspectre_load_modules() {\n    module load gnu12/12.3.0\n    module load intel/mpi/2021.16\n    module load git/2.43.0\n    module load cmake/3.24.2\n    module load python/3.12.4\n    module load orca1/openblas/0.3.27\n    module load orca1/boost/1.85.0\n    module load orca1/gsl/2.8\n    module load orca1/hdf5/1.12.3\n    module load orca1/charm/8.0.0\n    module load orca1/libxsmm/1.16.1\n    module load orca1/catch2/3.5.4\n    module load orca1/yaml-cpp/0.7.0\n    module load blaze/3.8\n    module load xsimd/13.2.0\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n    cmake -D CMAKE_C_COMPILER=gcc \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D BLA_VENDOR=OpenBLAS \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D BUILD_DOCS=OFF \\\n          -D DEBUG_SYMBOLS=OFF \\\n          -D MEMORY_ALLOCATOR=SYSTEM \\\n          -D SPECTRE_FETCH_MISSING_DEPS=OFF \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D BUILD_SHARED_LIBS=ON \\\n          -D MACHINE=Ocean2_orca1 \\\n          -D ENABLE_PARAVIEW=OFF \\\n          -D BOOTSTRAP_PY_DEPS=ON \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/ocean_gcc.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Environment for the ocean cluster, located at Cal State Fullerton.\n#\n# Access ocean via `ssh ocean.fullerton.edu`\n#\n# For questions regarding ocean or to request access, please contact\n# Geoffrey Lovelace by email (glovelace at fullerton dot edu).\n# Access is normally restricted to members of Cal State Fullerton's\n# Nicholas and Lee Begovich Center for Gravitational-Wave Physics and Astronomy\n# and their collaborators.\n\nspectre_setup_modules() {\n    echo \"All modules on ocean are provided by the system\"\n}\n\nspectre_unload_modules() {\n    module unload prun/1.2\n    module unload gmp/4.3.2\n    module unload mpc/1.0.3\n    module unload mpf4/3.1.6\n    module unload gnu11/11.3.0\n    module unload openmpi/4.1.4\n    module unload llvm/13.0.1\n    module unload cmake/3.24.1\n    module unload python/3.12.9\n    module unload openblas-0.3.20-gcc-11.3.0-tc4qxfv\n    module unload zlib-1.2.12-gcc-11.3.0-ge3ye5j\n    module unload blaze-3.8-gcc-11.3.0-y7sgzzc\n    module unload brigand-master-gcc-11.3.0-nsmmsxm\n    module unload libsharp-1.0.0-gcc-11.3.0-w7e7n5z\n    module unload catch2/3.4.0\n    module unload gsl-2.7.1-gcc-11.3.0-sey3z3o\n    module unload jemalloc-5.2.1-gcc-11.3.0-r63hemp\n    module unload yaml-cpp-0.7.0-gcc-11.3.0-a4rumor\n    module unload boost-1.79.0-gcc-11.3.0-ck2cccn\n    module unload hdf5-1.12.2-gcc-11.3.0-dly2yyu\n    module unload binutils-2.38-gcc-11.3.0-fmchbp7\n    module unload libxsmm/1.16.1\n    module unload charm-7.0.0-gnu11-022324\n    module unload git/2.19.6\n    module unload doxygen/1.9.5\n    module unload fftw-3.3.10-gcc-11.3.0-g2odatg\n}\n\nspectre_load_modules() {\n    module purge\n    module load prun/1.2\n    module load gmp/4.3.2\n    module load mpc/1.0.3\n    module load mpfr/3.1.6\n    module load gnu11/11.3.0\n    module load openmpi/4.1.4\n    module load llvm/13.0.1\n    module load cmake/3.24.1\n    module load python/3.12.9\n    export MODULEPATH=$MODULEPATH:/opt/ohpc/pub/apps\\\n/spack2022/share/spack/modules/linux-centos7-broadwell/\n    module load openblas-0.3.20-gcc-11.3.0-tc4qxfv\n    module load zlib-1.2.12-gcc-11.3.0-ge3ye5j\n    module load blaze-3.8-gcc-11.3.0-y7sgzzc\n    module load brigand-master-gcc-11.3.0-nsmmsxm\n    module load libsharp-1.0.0-gcc-11.3.0-w7e7n5z\n    module load catch2/3.4.0\n    module load gsl-2.7.1-gcc-11.3.0-sey3z3o\n    module load jemalloc-5.2.1-gcc-11.3.0-r63hemp\n    module load yaml-cpp-0.7.0-gcc-11.3.0-a4rumor\n    module load boost-1.79.0-gcc-11.3.0-ck2cccn\n    module load hdf5-1.12.2-gcc-11.3.0-dly2yyu\n    module load binutils-2.38-gcc-11.3.0-fmchbp7\n    module load libxsmm/1.16.1\n    module load charm-7.0.0-gnu11-022324\n    module load git/2.19.6\n    module load doxygen/1.9.5\n    module load fftw-3.3.10-gcc-11.3.0-g2odatg\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n    export GCC_HOME=/opt/ohpc/pub/compiler/gcc/11.3.0/bin\n    cmake -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D CMAKE_C_COMPILER=gcc \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_Fortran_COMPILER=${GCC_HOME}/gfortran \\\n          -D USE_PCH=ON \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D MACHINE=Ocean \\\n          -D BOOTSTRAP_PY_DEPS=ON \\\n          -D DEBUG_SYMBOLS=OFF \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/oscar.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_setup_modules() {\n    module use /oscar/data/icerm/knelli/modules\n    echo \"Place the following line in your '~/.bashrc' so you don't have to \"\n    echo \"run 'spectre_setup_modules' every time you log in:\"\n    echo \"\"\n    echo \"module use /oscar/data/icerm/knelli/modules\"\n}\n\nspectre_load_modules() {\n    module purge\n    module use /oscar/data/icerm/knelli/modules\n    module use /users/nvu8/modules\n    # Load ParaView before other Python packages, so that our Python packages\n    # are higher in priority than ParaView's bundled Python packages\n    module load paraview/5.11.2-osmesa\n    module load spectre-deps/oscar-2024-07\n}\n\nspectre_unload_modules() {\n    module use /oscar/data/icerm/knelli/modules\n    module use /users/nvu8/modules\n    module unload spectre-deps/oscar-2024-07\n    module unload paraview/5.11.2-osmesa\n    module purge\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n\n    cmake -D CMAKE_C_COMPILER=clang \\\n          -D CMAKE_CXX_COMPILER=clang++ \\\n          -D CMAKE_Fortran_COMPILER=/oscar/rt/9.2/software/0.20-generic/\\\n0.20.1/opt/spack/linux-rhel9-x86_64_v3/gcc-11.3.1/\\\ngcc-13.1.0-nvrtbp3ngdnok3fg22pzxxczitvtu7ge/bin/gfortran \\\n          -D CCACHE_LAUNCHER_EXTRA_ENV_VARS=\\\n\"CCACHE_CONFIGPATH=/oscar/data/icerm/knelli/spectre_cache_read.conf;\"\\\n\"CCACHE_BASEDIR=${SPECTRE_HOME}\" \\\n          -D USE_PCH=OFF \\\n          -D USE_LD=gold \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D BLA_VENDOR=OpenBLAS \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D BUILD_DOCS=OFF \\\n          -D MEMORY_ALLOCATOR=JEMALLOC \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D MACHINE=Oscar \\\n          -D OVERRIDE_ARCH=cascadelake \\\n          -D ENABLE_PARAVIEW=ON \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/perlmutter.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_load_modules() {\n    module use /global/homes/n/nilsvu/modules\n    module load cray-pmi\n    module load cray-hdf5/1.12.2.9\n    module load spectre-2024-04\n}\n\nspectre_unload_modules() {\n    module unload cray-pmi\n    module unload cray-hdf5/1.12.2.9\n    module unload spectre-2024-04\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n    cmake -D CMAKE_C_COMPILER=cc \\\n          -D CMAKE_CXX_COMPILER=CC \\\n          -D CMAKE_Fortran_COMPILER=ftn \\\n          -D MEMORY_ALLOCATOR=SYSTEM \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D DEBUG_SYMBOLS=OFF \\\n          -D MACHINE=Perlmutter \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/setup/anvil_gcc.sh",
    "content": "#!/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset -e\n\nif [ $# != 1 ]; then\n    echo \"You must pass one argument to spectre_setup_modules, which\"\n    echo \"is the directory where you want the dependencies to be built.\"\n    exit 1\nfi\n\n# If we wanted to use AOCC (the AMD Optimizing Compiler Collection)\n# we can hopefully still build the 3rd party libs with GCC. AOCC uses\n# clang as its frontend and so ABI compatibility with GCC should be\n# fine.\n. \"${SPECTRE_HOME}/support/Environments/anvil_gcc.sh\"\n\ndep_dir=`realpath $1`\nmkdir -p $dep_dir\ncd $dep_dir\n\n# log all output from this script\nexec > >(tee \"log.$(date +%F-%T)\") 2>&1\n\nmkdir -p $dep_dir/modules\n\nspectre_load_sys_modules\n\n# Setup Intel MPI module\nintel_dir=/apps/anvil/external/apps/intel/cluster.2019.5\nimpi_dir=${intel_dir}/compilers_and_libraries_2019.5.281/linux/mpi/intel64/\ncat >$dep_dir/modules/impi <<EOF\n#%Module1.0\nconflict        openmpi mvapich2 impi\n\nsetenv IMPI_VERSION \"2019.5\"\nsetenv IMPI_HOME \"$impi_dir\"\n\nprepend-path    PATH            $impi_dir/bin\nprepend-path    CPATH           $impi_dir/include\nprepend-path    LIBRARY_PATH    $impi_dir/lib\nprepend-path    LD_RUN_PATH     $impi_dir/lib\nprepend-path    LD_LIBRARY_PATH $impi_dir/lib\nprepend-path    MANPATH         $impi_dir/../man\nEOF\n\nmodule use $dep_dir/modules\nmodule load impi\n\nif [ -f catch/include/catch.hpp ]; then\n    echo \"Catch is already installed\"\nelse\n    echo \"Installing catch...\"\n    mkdir -p $dep_dir/catch/include\n    cd $dep_dir/catch/include\n    wget \\\n        https://github.com/catchorg/Catch2/releases/download/v2.13.0/catch.hpp \\\n        -O catch.hpp\n    echo \"Installed Catch into $dep_dir/catch\"\ncat >$dep_dir/modules/catch <<EOF\n#%Module1.0\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/catch/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/catch/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f blaze/include/blaze/Blaze.h ]; then\n    echo \"Blaze is already installed\"\nelse\n    echo \"Installing Blaze...\"\n    mkdir -p $dep_dir/blaze/\n    cd $dep_dir/blaze/\n    wget https://bitbucket.org/blaze-lib/blaze/downloads/blaze-3.8.tar.gz \\\n         -O blaze.tar.gz\n    tar -xzf blaze.tar.gz\n    cd blaze-3.8\n    mkdir build\n    cd build\n    cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=$dep_dir/blaze/ ..\n    make install\n    echo \"Installed Blaze into $dep_dir/blaze\"\n    cat >$dep_dir/modules/blaze <<EOF\n#%Module1.0\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/blaze/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/blaze/\"\nEOF\nfi\ncd $dep_dir\n\n\nif [ -f brigand/include/brigand/brigand.hpp ]; then\n    echo \"Brigand is already installed\"\nelse\n    echo \"Installing Brigand...\"\n    rm -rf $dep_dir/brigand\n    git clone https://github.com/edouarda/brigand.git\n    echo \"Installed Brigand into $dep_dir/brigand\"\n    cat >$dep_dir/modules/brigand <<EOF\n#%Module1.0\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/brigand/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/brigand/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f libxsmm/lib/libxsmm.a ]; then\n    echo \"LIBXSMM is already installed\"\nelse\n    echo \"Installing LIBXSMM...\"\n    rm -rf $dep_dir/libxsmm\n    wget https://github.com/libxsmm/libxsmm/archive/1.16.1.tar.gz -O libxsmm.tar.gz\n    tar -xzf libxsmm.tar.gz\n    mv libxsmm-* libxsmm\n    cd libxsmm\n    make CXX=g++ CC=gcc FC=gfortran AVX=2 MIC=0 INTRINSICS=1 -j4\n    spectre_load_sys_modules\n    cd $dep_dir\n    rm libxsmm.tar.gz\n    echo \"Installed LIBXSMM into $dep_dir/libxsmm\"\n    cat >$dep_dir/modules/libxsmm <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/libxsmm/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/libxsmm/lib\"\nprepend-path C_INCLUDE_PATH \"$dep_dir/libxsmm/include\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/libxsmm/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/libxsmm/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/yaml-cpp/lib/libyaml-cpp.a ]; then\n    echo \"yaml-cpp is already installed\"\nelse\n    echo \"Installing yaml-cpp...\"\n    wget https://github.com/jbeder/yaml-cpp/archive/yaml-cpp-0.6.2.tar.gz \\\n         -O yaml-cpp.tar.gz\n    tar -xzf yaml-cpp.tar.gz\n    mv yaml-cpp-* yaml-cpp-build\n    cd $dep_dir/yaml-cpp-build\n    mkdir build\n    cd build\n    cmake -D CMAKE_BUILD_TYPE=Release -D YAML_CPP_BUILD_TESTS=OFF \\\n          -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ \\\n          -D YAML_CPP_BUILD_CONTRIB=OFF \\\n          -D YAML_CPP_BUILD_TOOLS=ON \\\n          -D CMAKE_INSTALL_PREFIX=$dep_dir/yaml-cpp ..\n    make -j4\n    make install\n    cd $dep_dir\n    rm -r yaml-cpp-build\n    rm -r yaml-cpp.tar.gz\n    echo \"Installed yaml-cpp into $dep_dir/yaml-cpp\"\n    cat >$dep_dir/modules/yaml-cpp <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/yaml-cpp/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/yaml-cpp/lib\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/yaml-cpp/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/yaml-cpp/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/libsharp/lib/libsharp.a ]; then\n    echo \"libsharp is already installed\"\nelse\n    echo \"Installing libsharp...\"\n    wget https://github.com/Libsharp/libsharp/archive/v1.0.0.tar.gz \\\n         -O libsharp.tar.gz\n    tar -xzf libsharp.tar.gz\n    mv libsharp-* libsharp_build\n    cd $dep_dir/libsharp_build\n    autoconf\n    ./configure --prefix=$dep_dir/libsharp --disable-openmp\n    make -j4\n    mv ./auto $dep_dir/libsharp\n    cd $dep_dir\n    rm -r libsharp_build\n    rm libsharp.tar.gz\n    echo \"Installed libsharp into $dep_dir/libsharp\"\n    cat >$dep_dir/modules/libsharp <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/libsharp/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/libsharp/lib\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/libsharp/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/libsharp/\"\nEOF\nfi\ncd $dep_dir\n\n# Set up Charm++ because that can be difficult\ncharm_version=7.0.0\ncharm_config=mpi-linux-x86_64-smp\nif [ -f $dep_dir/charm/${charm_config}/lib/libck.a ]; then\n    echo \"Charm++ is already installed\"\nelse\n    echo \"Installing Charm++...\"\n    wget https://github.com/UIUC-PPL/charm/archive/v${charm_version}.tar.gz\n    tar xzf v${charm_version}.tar.gz\n    mv charm-${charm_version} charm\n    cd $dep_dir/charm\n    ./build LIBS ${charm_config} --with-production -j6\n    cd $dep_dir\n    rm v${charm_version}.tar.gz\n    echo \"Installed Charm++ into $dep_dir/charm\"\n    cat >$dep_dir/modules/charm <<EOF\n#%Module1.0\nprepend-path PATH \"$dep_dir/charm/${charm_config}/bin\"\nprepend-path LIBRARY_PATH \"$dep_dir/charm/${charm_config}/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/charm/${charm_config}/lib\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/charm/${charm_config}/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/charm/${charm_config}/\"\nsetenv CHARM_VERSION ${charm_version}\nsetenv CHARM_HOME $dep_dir/charm/${charm_config}\nsetenv CHARM_ROOT $dep_dir/charm/${charm_config}\nEOF\nfi\ncd $dep_dir\n\npython3 -m venv --system-site-packages $dep_dir/py_env\nexport VIRTUAL_ENV=$dep_dir/py_env\nexport PATH=${VIRTUAL_ENV}/bin:${PATH}\ncat >$dep_dir/modules/spectre_python <<EOF\n#%Module1.0\nsetenv VIRTUAL_ENV $dep_dir/py_env\nprepend-path PATH ${VIRTUAL_ENV}/bin\nEOF\nHDF5_DIR=$HDF5_HOME pip install --no-binary=h5py \\\n  -r $SPECTRE_HOME/support/Python/requirements.txt\n\nmodule unload impi\nmodule unuse $dep_dir/modules\n\nprintf \"\\n\\nIMPORTANT!!!\\nIn order to be able to use these modules you\\n\"\necho \"must run:\"\necho \"  module use $dep_dir/modules\"\necho \"You will need to do this every time you compile SpECTRE, so you may\"\necho \"want to add it to your ~/.bashrc.\"\n"
  },
  {
    "path": "support/Environments/setup/expanse_gcc.sh",
    "content": "#!/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset -e\n\nif [ $# != 1 ]; then\n    echo \"You must pass one argument to spectre_setup_modules, which\"\n    echo \"is the directory where you want the dependencies to be built.\"\n    exit 1\nfi\n\n# If we wanted to use AOCC (the AMD Optimizing Compiler Collection)\n# we can hopefully still build the 3rd party libs with GCC. AOCC uses\n# clang as its frontend and so ABI compatibility with GCC should be\n# fine.\n. \"${SPECTRE_HOME}/support/Environments/expanse_gcc.sh\"\n\nstart_dir=`pwd`\ndep_dir=`realpath $1`\nif [ $# != 1 ]; then\n    echo \"You must pass one argument to spectre_setup_modules, which\"\n    echo \"is the directory where you want the dependencies to be built.\"\n    return 1\nfi\nmkdir -p $dep_dir\ncd $dep_dir\n\n# log all output from this script\nexec > >(tee \"log.$(date +%F-%T)\") 2>&1\n\nmkdir -p $dep_dir/modules\n\nspectre_load_sys_modules\n\nif [ -f catch/include/catch.hpp ]; then\n    echo \"Catch is already installed\"\nelse\n    echo \"Installing catch...\"\n    mkdir -p $dep_dir/catch/include\n    cd $dep_dir/catch/include\n    wget \\\n        https://github.com/catchorg/Catch2/releases/download/v2.13.0/catch.hpp \\\n        -O catch.hpp\n    echo \"Installed Catch into $dep_dir/catch\"\n    cat >$dep_dir/modules/catch <<EOF\n#%Module1.0\nprepend-path CPATH \"$dep_dir/catch/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/catch/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f blaze/include/blaze/Blaze.h ]; then\n    echo \"Blaze is already installed\"\nelse\n    echo \"Installing Blaze...\"\n    mkdir -p $dep_dir/blaze/\n    cd $dep_dir/blaze/\n    wget https://bitbucket.org/blaze-lib/blaze/downloads/blaze-3.8.tar.gz \\\n         -O blaze.tar.gz\n    echo \"Unpacking Blaze. This can take a few minutes...\"\n    tar -xzf blaze.tar.gz\n    cd blaze-3.8\n    mkdir build\n    cd build\n    cmake -D CMAKE_BUILD_TYPE=Release \\\n          -D BLAZE_SHARED_MEMORY_PARALLELIZATION=NO \\\n          -D CMAKE_INSTALL_PREFIX=$dep_dir/blaze/ ..\n    make install\n    echo \"Installed Blaze into $dep_dir/blaze\"\n    cat >$dep_dir/modules/blaze <<EOF\n#%Module1.0\nprepend-path CPATH \"$dep_dir/blaze/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/blaze/\"\nEOF\nfi\ncd $dep_dir\n\n\nif [ -f brigand/include/brigand/brigand.hpp ]; then\n    echo \"Brigand is already installed\"\nelse\n    echo \"Installing Brigand...\"\n    rm -rf $dep_dir/brigand\n    git clone https://github.com/edouarda/brigand.git\n    echo \"Installed Brigand into $dep_dir/brigand\"\n    cat >$dep_dir/modules/brigand <<EOF\n#%Module1.0\nprepend-path CPATH \"$dep_dir/brigand/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/brigand/\"\nEOF\nfi\ncd $dep_dir\n\nmodule use $dep_dir/modules\nif [ -f libxsmm/lib/libxsmm.a ]; then\n    echo \"LIBXSMM is already installed\"\nelse\n    echo \"Installing LIBXSMM...\"\n    rm -rf $dep_dir/libxsmm\n    wget https://github.com/hfp/libxsmm/archive/1.16.1.tar.gz \\\n         -O libxsmm.tar.gz\n    tar -xzf libxsmm.tar.gz\n    mv libxsmm-* libxsmm\n    cd libxsmm\n    # Attempted an updated binutils, but does not improve matters\n    # Manual settings based on a rough understanding of the available\n    # vector intrinsics on the EPYC 7002 processors on Expanse\n    make CXX=g++ CC=gcc FC=gfortran INTRINSICS=1 AVX=2 -j4\n    cd $dep_dir\n    rm libxsmm.tar.gz\n    echo \"Installed LIBXSMM into $dep_dir/libxsmm\"\n    cat >$dep_dir/modules/libxsmm <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/libxsmm/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/libxsmm/lib\"\nprepend-path CPATH \"$dep_dir/libxsmm/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/libxsmm/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/yaml-cpp/lib/libyaml-cpp.a ]; then\n    echo \"yaml-cpp is already installed\"\nelse\n    echo \"Installing yaml-cpp...\"\n    wget https://github.com/jbeder/yaml-cpp/archive/yaml-cpp-0.6.2.tar.gz \\\n         -O yaml-cpp.tar.gz\n    tar -xzf yaml-cpp.tar.gz\n    mv yaml-cpp-* yaml-cpp-build\n    cd $dep_dir/yaml-cpp-build\n    mkdir build\n    cd build\n    cmake -D CMAKE_BUILD_TYPE=Release -D YAML_CPP_BUILD_TESTS=OFF \\\n          -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ \\\n          -D YAML_CPP_BUILD_CONTRIB=OFF \\\n          -D YAML_CPP_BUILD_TOOLS=ON \\\n          -D CMAKE_INSTALL_PREFIX=$dep_dir/yaml-cpp ..\n    make -j4\n    make install\n    cd $dep_dir\n    rm -r yaml-cpp-build\n    rm -r yaml-cpp.tar.gz\n    echo \"Installed yaml-cpp into $dep_dir/yaml-cpp\"\n    cat >$dep_dir/modules/yaml-cpp <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/yaml-cpp/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/yaml-cpp/lib\"\nprepend-path CPATH \"$dep_dir/yaml-cpp/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/yaml-cpp/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/libsharp/lib/libsharp.a ]; then\n    echo \"libsharp is already installed\"\nelse\n    echo \"Installing libsharp...\"\n    wget https://github.com/Libsharp/libsharp/archive/v1.0.0.tar.gz \\\n         -O libsharp.tar.gz\n    tar -xzf libsharp.tar.gz\n    mv libsharp-* libsharp_build\n    cd $dep_dir/libsharp_build\n    autoconf\n    ./configure --prefix=$dep_dir/libsharp --disable-openmp\n    make -j4\n    mv ./auto $dep_dir/libsharp\n    cd $dep_dir\n    rm -r libsharp_build\n    rm libsharp.tar.gz\n    echo \"Installed libsharp into $dep_dir/libsharp\"\n    cat >$dep_dir/modules/libsharp <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/libsharp/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/libsharp/lib\"\nprepend-path CPATH \"$dep_dir/libsharp/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/libsharp/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/scotch/lib/libscotch.a ]; then\n    echo \"Scotch is already installed\"\nelse\n    echo \"Installing Scotch...\"\n    wget https://gitlab.inria.fr/scotch/scotch/-/archive/v6.1.0/scotch-v6.1.0.tar.bz2\n    tar xjf scotch-v6.1.0.tar.bz2\n    mv scotch-v6.1.0 scotch\n    cd $dep_dir/scotch\n    cp src/Make.inc/Makefile.inc.x86-64_pc_linux2 src/Makefile.inc\n    cd src\n    make -j4\n    cd $dep_dir\n    rm scotch-v6.1.0.tar.bz2\n    echo \"Installed Scotch into $dep_dir/scotch\"\n    cat >$dep_dir/modules/scotch <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/scotch/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/scotch/lib\"\nprepend-path CPATH \"$dep_dir/scotch/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/scotch/\"\nEOF\nfi\n\nmodule use $dep_dir/modules\nmodule load scotch\ncharm_version=7.0.0\ncharm_config=mpi-linux-x86_64-smp\n# Set up Charm++ because that can be difficult\nif [ -f $dep_dir/charm/$charm_config/lib/libck.a ]; then\n    echo \"Charm++ is already installed\"\nelse\n    echo \"Installing Charm++...\"\n    wget https://github.com/UIUC-PPL/charm/archive/v$charm_version.tar.gz\n    echo \"Unpacking Charm++, this can take a few minutes\"\n    tar xzf v$charm_version.tar.gz\n    mv charm-$charm_version charm\n    cd $dep_dir/charm\n    ./build LIBS $charm_config --with-production -j4\n    cd $charm_config\n    make ScotchLB\n    cd $dep_dir\n    rm v$charm_version.tar.gz\n    echo \"Installed Charm++ into $dep_dir/charm\"\n    cat >$dep_dir/modules/charm <<EOF\n#%Module1.0\nprepend-path PATH \"$dep_dir/charm/$charm_config/bin\"\nprepend-path LIBRARY_PATH \"$dep_dir/charm/$charm_config/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/charm/$charm_config/lib\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/charm/$charm_config/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/charm/$charm_config/\"\nsetenv CHARM_VERSION $charm_version\nsetenv CHARM_HOME $dep_dir/charm/$charm_config\nsetenv CHARM_ROOT $dep_dir/charm/$charm_config\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/jemalloc/lib/libjemalloc.a ]; then\n    echo \"jemalloc is already set up\"\nelse\n    echo \"Installing jemalloc...\"\n    wget https://github.com/jemalloc/jemalloc/releases/download/5.2.1/jemalloc-5.2.1.tar.bz2\n    tar -xjf jemalloc-5.2.1.tar.bz2\n    mv jemalloc-5.2.1 jemalloc\n    cd $dep_dir/jemalloc\n    ./autogen.sh\n    make\n    cd $dep_dir\n    rm jemalloc-5.2.1.tar.bz2\n    echo \"Installed jemalloc into $dep_dir/jemalloc\"\n    cat >$dep_dir/modules/jemalloc <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/jemalloc/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/jemalloc/lib\"\nprepend-path CPATH \"$dep_dir/jemalloc/include\"\nprepend-path PATH \"$dep_dir/jemalloc/bin\"\nprepend-path LD_RUN_PATH \"$dep_dir/jemalloc/lib\"\nsetenv JEMALLOC_HOME $dep_dir/jemalloc/\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/jemalloc/\"\nEOF\nfi\n\npython3 -m venv --system-site-packages $dep_dir/py_env\nexport VIRTUAL_ENV=$dep_dir/py_env\nexport PATH=${VIRTUAL_ENV}/bin:${PATH}\ncat >$dep_dir/modules/spectre_python <<EOF\n#%Module1.0\nsetenv VIRTUAL_ENV $dep_dir/py_env\nprepend-path PATH ${VIRTUAL_ENV}/bin\nEOF\nHDF5_DIR=$HDF5HOME pip install --no-binary=h5py \\\n  -r $SPECTRE_HOME/support/Python/requirements.txt\n\ncd $dep_dir\n\nif [ -f $dep_dir/modules/spectre_zlib ]; then\n    echo \"zlib is already installed\"\nelse\n    echo \"Installing zlib module...\"\n    # The zlib configuration is a bit questionable. Basically, there are\n    # several different copies installed, and by default the incorrect one seems\n    # to be found.\n    spack_base=/cm/shared/apps/spack\n    spack_root_path=$spack_base/cpu/opt/spack/linux-centos8-zen2/gcc-10.2.0/\n    zlib_path=$spack_root_path/zlib-1.2.11-rchx6la4w4coybgwftagexqeqwmsqlgo/\n\n    cat >$dep_dir/modules/spectre_zlib <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$zlib_path/lib\"\nprepend-path LD_LIBRARY_PATH \"$zlib_path/lib\"\nprepend-path CPATH \"$zlib_path/include\"\nprepend-path LD_RUN_PATH \"$zlib_path/lib\"\nsetenv ZLIB_ROOT $zlib_path/\nprepend-path CMAKE_PREFIX_PATH \"$zlib_path/\"\nEOF\nfi\n\ncd $start_dir\n\nspectre_unload_sys_modules\nmodule unload scotch\n\nprintf \"\\n\\nIMPORTANT!!!\\nIn order to be able to use these modules you\\n\"\necho \"must run:\"\necho \"  module use $dep_dir/modules\"\necho \"You will need to do this every time you compile SpECTRE, so if you\"\necho \"want this SpECTRE environment to be your primary environment, you may\"\necho \"want to add it to your ~/.bashrc.\"\n"
  },
  {
    "path": "support/Environments/setup/frontera_gcc.sh",
    "content": "#!/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset -e\n\n. \"${SPECTRE_HOME}/support/Environments/frontera_gcc.sh\"\n\nif [ $# != 1 ]; then\n    echo \"You must pass one argument to spectre_setup_modules, which\"\n    echo \"is the directory where you want the dependencies to be built.\"\n    exit 1\nfi\n\ndep_dir=`realpath $1`\nmkdir -p $dep_dir\ncd $dep_dir\n\n# log all output from this script\nexec > >(tee \"log.$(date +%F-%T)\") 2>&1\n\nmkdir -p $dep_dir/modules\n\nspectre_load_sys_modules\n\nif [ -f catch/include/catch.hpp ]; then\n    echo \"Catch is already installed\"\nelse\n    echo \"Installing catch...\"\n    mkdir -p $dep_dir/catch/include\n    cd $dep_dir/catch/include\n    wget https://github.com/catchorg/Catch2/releases/download/v2.13.0/catch.hpp -O catch.hpp\n    echo \"Installed Catch into $dep_dir/catch\"\ncat >$dep_dir/modules/catch <<EOF\n#%Module1.0\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/catch/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/catch/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f blaze/include/blaze/Blaze.h ]; then\n    echo \"Blaze is already installed\"\nelse\n    echo \"Installing Blaze...\"\n    mkdir -p $dep_dir/blaze/\n    cd $dep_dir/blaze/\n    wget https://bitbucket.org/blaze-lib/blaze/downloads/blaze-3.8.tar.gz -O blaze.tar.gz\n    tar -xzf blaze.tar.gz\n    mv blaze-* include\n    echo \"Installed Blaze into $dep_dir/blaze\"\n    cat >$dep_dir/modules/blaze <<EOF\n#%Module1.0\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/blaze/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/blaze/\"\nEOF\nfi\ncd $dep_dir\n\n\nif [ -f brigand/include/brigand/brigand.hpp ]; then\n    echo \"Brigand is already installed\"\nelse\n    echo \"Installing Brigand...\"\n    rm -rf $dep_dir/brigand\n    git clone https://github.com/edouarda/brigand.git\n    echo \"Installed Brigand into $dep_dir/brigand\"\n    cat >$dep_dir/modules/brigand <<EOF\n#%Module1.0\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/brigand/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/brigand/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f libxsmm/lib/libxsmm.a ]; then\n    echo \"LIBXSMM is already installed\"\nelse\n    echo \"Installing LIBXSMM...\"\n    rm -rf $dep_dir/libxsmm\n    wget https://github.com/libxsmm/libxsmm/archive/1.16.1.tar.gz -O libxsmm.tar.gz\n    tar -xzf libxsmm.tar.gz\n    mv libxsmm-* libxsmm\n    cd libxsmm\n    make CXX=g++ CC=gcc FC=gfortran -j4\n    cd $dep_dir\n    rm libxsmm.tar.gz\n    echo \"Installed LIBXSMM into $dep_dir/libxsmm\"\n    cat >$dep_dir/modules/libxsmm <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/libxsmm/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/libxsmm/lib\"\nprepend-path C_INCLUDE_PATH \"$dep_dir/libxsmm/include\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/libxsmm/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/libxsmm/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/yaml-cpp/lib/libyaml-cpp.a ]; then\n    echo \"yaml-cpp is already installed\"\nelse\n    echo \"Installing yaml-cpp...\"\n    wget https://github.com/jbeder/yaml-cpp/archive/yaml-cpp-0.6.2.tar.gz -O yaml-cpp.tar.gz\n    tar -xzf yaml-cpp.tar.gz\n    mv yaml-cpp-* yaml-cpp-build\n    cd $dep_dir/yaml-cpp-build\n    mkdir build\n    cd build\n    cmake -D CMAKE_BUILD_TYPE=Release -D YAML_CPP_BUILD_TESTS=OFF \\\n          -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ \\\n          -D YAML_CPP_BUILD_CONTRIB=OFF \\\n          -D YAML_CPP_BUILD_TOOLS=ON \\\n          -D CMAKE_INSTALL_PREFIX=$dep_dir/yaml-cpp ..\n    make -j4\n    make install\n    cd $dep_dir\n    rm -r yaml-cpp-build\n    rm -r yaml-cpp.tar.gz\n    echo \"Installed yaml-cpp into $dep_dir/yaml-cpp\"\n    cat >$dep_dir/modules/yaml-cpp <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/yaml-cpp/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/yaml-cpp/lib\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/yaml-cpp/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/yaml-cpp/\"\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/libsharp/lib/libsharp.a ]; then\n    echo \"libsharp is already installed\"\nelse\n    echo \"Installing libsharp...\"\n    wget https://github.com/Libsharp/libsharp/archive/v1.0.0.tar.gz -O libsharp.tar.gz\n    tar -xzf libsharp.tar.gz\n    mv libsharp-* libsharp_build\n    cd $dep_dir/libsharp_build\n    autoconf\n    ./configure --prefix=$dep_dir/libsharp --disable-openmp\n    make -j4\n    mv ./auto $dep_dir/libsharp\n    cd $dep_dir\n    rm -r libsharp_build\n    rm libsharp.tar.gz\n    echo \"Installed libsharp into $dep_dir/libsharp\"\n    cat >$dep_dir/modules/libsharp <<EOF\n#%Module1.0\nprepend-path LIBRARY_PATH \"$dep_dir/libsharp/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/libsharp/lib\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/libsharp/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/libsharp/\"\nEOF\nfi\ncd $dep_dir\n\n# Set up Charm++ because that can be difficult\ncharm_version=7.0.0\nbackend=mpi\ncharm_config=${backend}-linux-x86_64-smp\nif [ -f $dep_dir/charm/${charm_config}/lib/libck.a ]; then\n    echo \"Charm++ is already installed\"\nelse\n    echo \"Installing Charm++...\"\n    wget https://github.com/UIUC-PPL/charm/archive/v${charm_version}.tar.gz\n    tar xzf v${charm_version}.tar.gz\n    mv charm-${charm_version} charm\n    cd $dep_dir/charm\n    if [ -f ${SPECTRE_HOME}/support/Charm/v${charm_version}.patch ]; then\n        git apply ${SPECTRE_HOME}/support/Charm/v${charm_version}.patch\n    fi\n    ./build LIBS ${charm_config} --with-production -j6\n    cd $dep_dir\n    rm v${charm_version}.tar.gz\n    echo \"Installed Charm++ into $dep_dir/charm\"\n    cat >$dep_dir/modules/charm_${backend} <<EOF\n#%Module1.0\nprepend-path PATH \"$dep_dir/charm/${charm_config}/bin\"\nprepend-path LIBRARY_PATH \"$dep_dir/charm/${charm_config}/lib\"\nprepend-path LD_LIBRARY_PATH \"$dep_dir/charm/${charm_config}/lib\"\nprepend-path CPLUS_INCLUDE_PATH \"$dep_dir/charm/${charm_config}/include\"\nprepend-path CMAKE_PREFIX_PATH \"$dep_dir/charm/${charm_config}/\"\nsetenv CHARM_VERSION ${charm_version}\nsetenv CHARM_HOME $dep_dir/charm/${charm_config}\nsetenv CHARM_ROOT $dep_dir/charm/${charm_config}\nEOF\nfi\ncd $dep_dir\n\nif [ -f $dep_dir/modules/spectre_boost ]; then\n    echo \"Boost is already set up\"\nelse\n    cat >$dep_dir/modules/spectre_boost <<EOF\n#%Module1.0\nprepend-path CPLUS_INCLUDE_PATH \"$BOOST_ROOT/include\"\nEOF\nfi\ncd $dep_dir\n\npython3 -m venv --system-site-packages $dep_dir/py_env\nexport VIRTUAL_ENV=$dep_dir/py_env\nexport PATH=${VIRTUAL_ENV}/bin:${PATH}\ncat >$dep_dir/modules/spectre_python <<EOF\n#%Module1.0\nsetenv VIRTUAL_ENV $dep_dir/py_env\nprepend-path PATH ${VIRTUAL_ENV}/bin\nEOF\nHDF5_DIR=$TACC_HDF5_DIR pip install --no-binary=h5py \\\n  -r $SPECTRE_HOME/support/Python/requirements.txt\n\nprintf \"\\n\\nIMPORTANT!!!\\nIn order to be able to use these modules you\\n\"\necho \"must run:\"\necho \"  module use $dep_dir/modules\"\necho \"You will need to do this every time you compile SpECTRE, so you may\"\necho \"want to add it to your ~/.bashrc.\"\n"
  },
  {
    "path": "support/Environments/setup/mbot_install.sh",
    "content": "#!/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset -e\n\nif [ $# != 1 ]; then\n    echo \"You must pass one argument to mbot_install, which\"\n    echo \"is the directory where you want the dependencies to be built.\"\n    exit 1\nfi\n\nPARALLEL_MAKE_ARG=64\nINSTALL_DEV_TOOLS=NO # If YES, installs Emacs, tmux\nUSE_LUA=YES # Some module systems do not support lua modules.\ndep_dir=`realpath $1`\nmkdir -p $dep_dir\ncd $dep_dir\n\n# log all output from this script\nexec > >(tee \"log.$(date +%F-%T)\") 2>&1\n\nmkdir -p $dep_dir/modules\n\nif [ \"${SPECTRE_HOME}\" = \"\" ]; then\n    echo \"You must set SPECTRE_HOME to the SpECTRE source directory\"\n    exit 1\nfi\n\n################################################################\n# Autotools Begin\n################################################################\n# We install autotools globally independent of compiler versions.\n# This should be fine since we don't link against autotools, we\n# just use it to configure and build 3rd party libs.\ncd $dep_dir\n_AUTOTOOLS_VERSION=2.72\n_AUTOMAKE_VERSION=1.16\n_LIBTOOL_VERSION=2.4\nLOCATION=$dep_dir/autotools/${_AUTOTOOLS_VERSION}\n# We need to set up the module file first since each part of autotools\n# needs to be able to use the previous parts.\nMODULE_FILE=$dep_dir/modules/global/autotools/${_AUTOTOOLS_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## We use the following versions of autotools:\n##  autoconf: ${_AUTOTOOLS_VERSION}\n##  automake: ${_AUTOMAKE_VERSION}\n##  libtool: ${_LIBTOOL_VERSION}\n##\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/share/man to MANPATH\"\n        puts stderr \"\\tenvironment variables.\"\n        puts stderr \"\"\n        puts stderr \"\\tThis allows you to use the autotools\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment to use the autotools\"\n\n# for Tcl script use only\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        autotools\n\nprepend-path    PATH            \\$apps_path/bin\nprepend-path    MANPATH         \\$apps_path/share/man\nprepend-path  CMAKE_PREFIX_PATH  \\$apps_path\nEOF\nfi\n\nmodule use $dep_dir/modules/global\nmodule load autotools/2.72\nif [ -d ${LOCATION} ]; then\n    echo \"autotools version ${_AUTOTOOLS_VERSION} already installed\"\nelse\n    # We don't install m4 since the system one is reasonably new.\n    wget https://ftp.gnu.org/gnu/autoconf/autoconf-${_AUTOTOOLS_VERSION}.tar.gz\n    tar xf autoconf-${_AUTOTOOLS_VERSION}.tar.gz\n    cd ./autoconf-${_AUTOTOOLS_VERSION}\n    ./configure --prefix=${LOCATION}\n    make all install\n    cd ../\n    rm -rf ./autoconf-${_AUTOTOOLS_VERSION} \\\n       ./autoconf-${_AUTOTOOLS_VERSION}.tar.gz\n\n    wget https://ftp.gnu.org/gnu/automake/automake-${_AUTOMAKE_VERSION}.tar.gz\n    tar xf automake-${_AUTOMAKE_VERSION}.tar.gz\n    cd ./automake-${_AUTOMAKE_VERSION}\n    ./configure --prefix=${LOCATION}\n    make all install\n    cd ../\n    rm -rf ./automake-${_AUTOMAKE_VERSION} \\\n       ./automake-${_AUTOMAKE_VERSION}.tar.gz\n\n    wget https://ftp.gnu.org/gnu/libtool/libtool-${_LIBTOOL_VERSION}.tar.gz\n    tar xf libtool-${_LIBTOOL_VERSION}.tar.gz\n    cd ./libtool-${_LIBTOOL_VERSION}\n    ./configure --prefix=${LOCATION}\n    make all install\n    cd ../\n    rm -rf ./libtool-${_LIBTOOL_VERSION} ./libtool-${_LIBTOOL_VERSION}.tar.gz\n    chmod -R 555 ${LOCATION}\nfi\n################################################################\n# Autotools End\n################################################################\n\nif [ \"${INSTALL_DEV_TOOLS}\" = \"YES\" ]; then\n    ################################################################\n    # pinentry begin\n    ################################################################\n    _PINENTRY_VERSION=1.2.1\n    LOCATION=$dep_dir/pinentry/${_PINENTRY_VERSION}\n    if [ -d ${LOCATION} ]; then\n        echo \"pinentry version ${_PINENTRY_VERSION} already installed\"\n    else\n        wget https://www.gnupg.org/ftp/gcrypt/libgpg-error/libgpg-error-1.47.tar.bz2\n        tar xf libgpg-error-1.47.tar.bz2\n        cd ./libgpg-error-1.47\n        ./configure --prefix=${LOCATION} \\\n                    --enable-static --disable-shared\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./libgpg-error-1.47.tar.bz2 ./libgpg-error-1.47\n\n        wget https://www.gnupg.org/ftp/gcrypt/libassuan/libassuan-2.5.6.tar.bz2\n        tar xf libassuan-2.5.6.tar.bz2\n        cd libassuan-2.5.6\n        ./configure --prefix=${LOCATION} --with-libgpg-error-prefix=${LOCATION} \\\n                    --enable-static --disable-shared\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./libassuan-2.5.6.tar.bz2 ./libassuan-2.5.6\n\n        wget https://www.gnupg.org/ftp/gcrypt/pinentry/pinentry-${_PINENTRY_VERSION}.tar.bz2\n        tar xf pinentry-${_PINENTRY_VERSION}.tar.bz2\n        cd pinentry-${_PINENTRY_VERSION}\n        ./configure --prefix=${LOCATION} \\\n                    --enable-pinentry-tty \\\n                    --enable-inside-emacs=yes \\\n                    --with-libgpg-error-prefix=${LOCATION} \\\n                    --with-libassuan-prefix=${LOCATION} \\\n                    --disable-pinentry-curses \\\n                    --disable-fallback-curses \\\n                    --enable-static --disable-shared\n        make -j${PARALLEL_MAKE_ARG}\n        rm ${LOCATION}/bin/*\n        make install\n        cd ../\n        rm -rf ./pinentry-${_PINENTRY_VERSION}.tar.bz2 \\\n           ./pinentry-${_PINENTRY_VERSION}\n        chmod -R 555 ${LOCATION}\n    fi\n\n    MODULE_FILE=$dep_dir/modules/global/pinentry/${_PINENTRY_VERSION}\n    mkdir -p `dirname ${MODULE_FILE}`\n    if [ -f ${MODULE_FILE} ]; then\n        echo \"Module file ${MODULE_FILE} already exists\"\n    else\n        cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"Sets up pinentry\"\n}\nmodule-whatis \"Sets up your environment so you can use Pinentry (v${_PINENTRY_VERSION})\"\n\n# for Tcl script use only\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict pinentry\n\nprepend-path PATH \"\\$apps_path/bin\"\nEOF\n        chmod -R 555 ${MODULE_FILE}\n    fi\n    ################################################################\n    # pinentry end\n    ################################################################\n\n    ################################################################\n    # tmux begin\n    ################################################################\n    _TMUX_VERSION=3.3a\n    LOCATION=$dep_dir/tmux/${_TMUX_VERSION}\n    if [ -d ${LOCATION} ]; then\n        echo \"tmux version ${_TMUX_VERSION} already installed\"\n    else\n        wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz\n        tar xf libevent-2.1.12-stable.tar.gz\n        cd ./libevent-2.1.12-stable/\n        ./configure --prefix=${LOCATION} --enable-static --disable-shared --with-pic\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./libevent-2.1.12-stable/ \\\n           ./libevent-2.1.12-stable.tar.gz\n\n        wget https://invisible-island.net/datafiles/release/ncurses.tar.gz\n        tar xf ncurses.tar.gz\n        cd ./ncurses-*\n        ./configure --with-termlib --without-shared --with-pic \\\n                    --enable-pc-files \\\n                    --with-pkg-config-libdir=${LOCATION}/lib/pkgconfig \\\n                    --prefix=${LOCATION}\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./ncurses*\n\n        wget https://github.com/tmux/tmux/archive/refs/tags/${_TMUX_VERSION}.tar.gz\n        tar xf ${_TMUX_VERSION}.tar.gz\n        cd ./tmux-${_TMUX_VERSION}\n        module unload autotools/${_AUTOTOOLS_VERSION}\n        ./autogen.sh\n        PKG_CONFIG_PATH=${PKG_CONFIG_PATH}:${LOCATION}/lib/pkgconfig ./configure \\\n                       --prefix=${LOCATION}\n        make -j${PARALLEL_MAKE_ARG}\n        # Remove any binaries from libevent or ncurses since we don't need them.\n        # This avoids polluting the PATH.\n        rm -f ${LOCATION}/bin/*\n        make install\n        cd ../\n        rm -rf ./${_TMUX_VERSION}.tar.gz ./tmux-${_TMUX_VERSION}\n        module load autotools/${_AUTOTOOLS_VERSION}\n\n        chmod -R 555 ${LOCATION}\n    fi\n\n    MODULE_FILE=$dep_dir/modules/global/tmux/${_TMUX_VERSION}\n    mkdir -p `dirname ${MODULE_FILE}`\n    if [ -f ${MODULE_FILE} ]; then\n        echo \"Module file ${MODULE_FILE} already exists\"\n    else\n        cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"Sets up tmux\"\n}\nmodule-whatis \"Sets up your environment so you can use Tmux (v${_TMUX_VERSION})\"\n\n# for Tcl script use only\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict tmux\n\nprepend-path PATH \"\\$apps_path/bin\"\nEOF\n        chmod -R 555 ${MODULE_FILE}\n    fi\n    ################################################################\n    # tmux end\n    ################################################################\n\n    ################################################################\n    # Emacs begin\n    ################################################################\n    _EMACS_VERSION=29.3\n    LOCATION=$dep_dir/emacs/${_EMACS_VERSION}\n    if [ -d ${LOCATION} ]; then\n        echo \"Emacs version ${_EMACS_VERSION} already installed\"\n    else\n        _BEFORE_CPATH=$CPATH\n        _BEFORE_LIBRARY_PATH=$LIBRARY_PATH\n        _BEFORE_PKG_CONFIG_PATH=$PKG_CONFIG_PATH\n        export CPATH=${LOCATION}/include\n        export LIBRARY_PATH=${LOCATION}/lib/:${LOCATION}/lib64\n        export PKG_CONFIG_PATH=${LOCATION}/lib/pkgconfig:${LOCATION}/lib64/pkgconfig\n\n        wget https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz\n        tar xf gmp-6.3.0.tar.xz\n        cd ./gmp-6.3.0\n        ./configure --disable-shared --enable-static --with-pic --prefix=${LOCATION}\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./gmp-6.3.0.tar.xz ./gmp-6.3.0\n\n        wget https://github.com/akheron/jansson/releases/download/v2.14/jansson-2.14.tar.gz\n        tar xf jansson-2.14.tar.gz\n        cd ./jansson-2.14\n        ./configure --disable-shared --enable-static --with-pic --prefix=${LOCATION}\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./jansson-2.14.tar.gz ./jansson-2.14\n\n        wget https://sqlite.org/2024/sqlite-autoconf-3450100.tar.gz\n        tar xf sqlite-autoconf-3450100.tar.gz\n        cd ./sqlite-autoconf-3450100\n        # Build sqlite shared, but have Emacs link against the system install.\n        # The system install doesn't have the headers, just the library.\n        # ./configure --disable-shared --enable-static --with-pic --prefix=${LOCATION}\n        ./configure --with-pic --prefix=${LOCATION}\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./sqlite-autoconf*\n\n        wget https://invisible-island.net/datafiles/release/ncurses.tar.gz\n        tar xf ncurses.tar.gz\n        cd ./ncurses-*\n        mkdir -p ${LOCATION}/lib/pkgconfig\n        ./configure --with-termlib --without-shared --with-pic --prefix=${LOCATION} \\\n                    --enable-pc-files --with-pkg-config-libdir=${LOCATION}/lib/pkgconfig\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./ncurses*\n\n        # The below are needed (and not fully working) if GNUTLS is desired.\n        wget https://ftp.gnu.org/gnu/nettle/nettle-3.9.1.tar.gz\n        tar xf nettle-3.9.1.tar.gz\n        cd ./nettle-3.9.1\n        ./configure --disable-shared --enable-static --with-pic --prefix=${LOCATION}\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./nettle*\n\n        wget https://github.com/libexpat/libexpat/releases/download/R_2_5_0/expat-2.5.0.tar.bz2\n        tar xf expat-2.5.0.tar.bz2\n        cd ./expat-2.5.0\n        ./configure --disable-shared --enable-static --with-pic --prefix=${LOCATION}\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./expat*\n\n        wget https://www.nlnetlabs.nl/downloads/unbound/unbound-1.19.0.tar.gz\n        tar xf unbound-1.19.0.tar.gz\n        cd ./unbound-1.19.0\n        ./configure --disable-shared --enable-static --with-pic \\\n                        --with-libexpat=${LOCATION} --prefix=${LOCATION}\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf ./unbound*\n\n        wget https://www.gnupg.org/ftp/gcrypt/gnutls/v3.7/gnutls-3.7.10.tar.xz\n        tar xf gnutls-3.7.10.tar.xz\n        cd gnutls-3.7.10\n        ./configure --with-pic --prefix=${LOCATION} \\\n                    --enable-pc-files \\\n                    --with-pkg-config-libdir=${LOCATION}/lib/pkgconfig \\\n                    --without-p11-kit --with-included-unistring \\\n                    --with-included-libtasn1\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../\n        rm -rf gnutls-*\n\n        # Install Emacs!\n        wget https://us.mirrors.cicku.me/gnu/emacs/emacs-${_EMACS_VERSION}.tar.xz\n        tar xf emacs-${_EMACS_VERSION}.tar.xz\n        cd ./emacs-${_EMACS_VERSION}\n        ./configure --without-xpm --without-jpeg --without-tiff --without-gif \\\n                    --without-png --without-rsvg --without-webp \\\n                    --without-cairo --without-imagemagick \\\n                    --without-native-image-api --without-selinux --without-gpm \\\n                    --with-json --with-sqlite3=yes \\\n                    --prefix=${LOCATION}\n        # Maybe someday we can build with native compilation and tree-sitter.\n        #\n        # Native needs libgccjit\n        #\n        # Tree-sitter needs: Rust, Node.js, and gcc\n        #  --with-native-compilation --with-tree-sitter\n        make -j${PARALLEL_MAKE_ARG}\n        # clear bin directory so we only add emacs to the PATH.\n        rm ${LOCATION}/bin/*\n        make install\n        cd ../\n        rm -rf ./emacs-${_EMACS_VERSION}*\n\n        chmod -R 555 ${LOCATION}\n\n        export CPATH=$_BEFORE_CPATH\n        export LIBRARY_PATH=$_BEFORE_LIBRARY_PATH\n        export PKG_CONFIG_PATH=$_BEFORE_PKG_CONFIG_PATH\n    fi\n\n    MODULE_FILE=$dep_dir/modules/global/emacs/${_EMACS_VERSION}\n    mkdir -p `dirname ${MODULE_FILE}`\n    if [ -f ${MODULE_FILE} ]; then\n        echo \"Module file ${MODULE_FILE} already exists\"\n    else\n        cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"Sets up Emacs\"\n}\nmodule-whatis \"Sets up your environment so you can use Emacs (v${_EMACS_VERSION})\"\n\n# for Tcl script use only\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict emacs\n\nprepend-path PATH \"\\$apps_path/bin\"\nEOF\n        chmod -R 555 ${MODULE_FILE}\n    fi\n    ################################################################\n    # Emacs end\n    ################################################################\nfi\n\n################################################################\n# GCC Begin\n################################################################\ncd $dep_dir\n_GCC_VERSION=11.4.0\nLOCATION=$dep_dir/gcc/${_GCC_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"GCC version ${_GCC_VERSION} already installed\"\nelse\n    wget http://mirror.rit.edu/gnu/gcc/gcc-${_GCC_VERSION}/gcc-${_GCC_VERSION}.tar.gz\n    tar xf gcc-${_GCC_VERSION}.tar.gz\n    cd gcc-${_GCC_VERSION}\n    # download_prerequisites gets things like mpc, mpfr, and gmp\n    ./contrib/download_prerequisites\n    ./configure --disable-multilib --enable-languages=c,c++,fortran \\\n                --enable-lto \\\n                --enable-quad \\\n                --with-system-zlib \\\n                --enable-host-shared \\\n                --prefix=${LOCATION}\n    make -j${PARALLEL_MAKE_ARG}\n    make -j${PARALLEL_MAKE_ARG} install\n    cd $dep_dir\n    rm -rf gcc-${_GCC_VERSION} gcc-${_GCC_VERSION}.tar.gz\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=$dep_dir/modules/global/gcc/${_GCC_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\n# We install module files into their own sub-directory so that all packages\n# built with the same GCC version are in the same sub-directory.\nMODULE_GCC_LOCATION=$dep_dir/modules/gcc-${_GCC_VERSION}\nmkdir -p ${MODULE_GCC_LOCATION}\n\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tSets GCC_HOME to be \\$apps_path\"\n        puts stderr \"\\tDefines CC, CXX, F77 and F90 to be GNU compilers\"\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib64 to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib64 to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib64 to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/share/man to MANPATH\"\n        puts stderr \"\\tenvironment variables.\"\n        puts stderr \"\"\n        puts stderr \"\\tThis allows you to use the GNU Compilers (v${_GCC_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment to use the GNU compilers (v${_GCC_VERSION})\"\n\n# for Tcl script use only\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        gcc intel pgi open64 gnu gnu12\n\npushenv  GCC_VERSION     $_GCC_VERSION\npushenv  GCC_HOME        \\$apps_path\npushenv  CC              gcc\npushenv  CXX             g++\npushenv  F77             gfortran\npushenv  F90             gfortran\n\nprepend-path    PATH            \\$apps_path/bin\nprepend-path    CPATH           \\$apps_path/include\nprepend-path    C_INCLUDE_PATH           \\$apps_path/include\nprepend-path    INCLUDE           \\$apps_path/include\nprepend-path    LIBRARY_PATH    \\$apps_path/lib64\nprepend-path    LD_RUN_PATH     \\$apps_path/lib64\nprepend-path    LD_LIBRARY_PATH \\$apps_path/lib64\nprepend-path    MANPATH         \\$apps_path/share/man\nprepend-path  CMAKE_PREFIX_PATH  \\$apps_path\nprepend-path MODULEPATH ${MODULE_GCC_LOCATION}/libraries\nprepend-path MODULEPATH ${MODULE_GCC_LOCATION}/tools\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load gcc/${_GCC_VERSION}\nINSTALL_GCC_LOCATION=$dep_dir/gcc-${_GCC_VERSION}\nmkdir -p ${INSTALL_GCC_LOCATION}\n################################################################\n# GCC End\n################################################################\n\n################################################################\n# hdf5 Begin\n################################################################\ncd $dep_dir\n_HDF5_VERSION=1.12.3\nLOCATION=${INSTALL_GCC_LOCATION}/hdf5/${_HDF5_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"HDF5 version ${_HDF5_VERSION} already installed\"\nelse\n    wget https://hdf-wordpress-1.s3.amazonaws.com/wp-content/uploads/manual/HDF5/HDF5_${_HDF5_VERSION//\\./_}/src/hdf5-${_HDF5_VERSION}.tar.gz\n    tar -xzf hdf5-${_HDF5_VERSION}.tar.gz && cd hdf5-${_HDF5_VERSION}\n    mkdir -p ${LOCATION}\n    ./configure CC=gcc --enable-build-mode=production \\\n                --enable-file-locking=yes --with-pic \\\n                --prefix=${LOCATION}\n    make -j${PARALLEL_MAKE_ARG} && make install PREFIX=${LOCATION}\n    cd ..\n    rm -rf hdf5-${_HDF5_VERSION} hdf5-${_HDF5_VERSION}.tar.gz\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/hdf5/${_HDF5_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## HDF5 built without file locking\n##\nproc ModulesHelp { } {\n  global dotversion\n  global apps_path\n\n  puts stderr \"\\Sets HDF5_HOME to be \\$apps_path\"\n  puts stderr \"\\Sets HDF5_ROOT to be \\$apps_path\"\n  puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n  puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n  puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n  puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n  puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n  puts stderr \"\\tenvironment variables.\"\n  puts stderr \"\"\n  puts stderr \"\\tThis allows you to use HDF5 tools and libraries (v${_HDF5_VERSION}).\"\n  puts stderr \"\"\n}\n\nmodule-whatis \"Sets up your environment to use HDF5 (v${_HDF5_VERSION})\"\n\n# for Tcl script use only\nset dotversion  3.2.6\n\nset apps_path ${LOCATION}\n\nconflict  HDF4\nconflict  HDF5\n\nsetenv  HDF5_VERSION  ${_HDF5_VERSION}\nsetenv  HDF5_HOME \\$apps_path\nsetenv  HDF5_ROOT \\$apps_path\n\nprepend-path  CMAKE_PREFIX_PATH \\$apps_path/\nprepend-path  PATH              \\$apps_path/bin\nprepend-path  CPATH             \\$apps_path/include\nprepend-path  LIBRARY_PATH      \\$apps_path/lib\nprepend-path  LD_RUN_PATH       \\$apps_path/lib\nprepend-path  LD_LIBRARY_PATH   \\$apps_path/lib\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load hdf5/${_HDF5_VERSION}\n################################################################\n# hdf5 End\n################################################################\n\n################################################################\n# Python Begin\n################################################################\ncd $dep_dir\n_PYTHON_VERSION=3.10.3\nPYTHON_INCLUDE_DIR=python3.10\nLOCATION=${INSTALL_GCC_LOCATION}/python/${_PYTHON_VERSION}\n_PYTHON_LOCATION=${INSTALL_GCC_LOCATION}/python/${_PYTHON_VERSION}\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/python/${_PYTHON_VERSION}.lua\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    # We set this up as a Lua module because that way we can use the depends_on\n    # function to automatically load and unload HDF5. The TCL-based module files\n    # seems to be the \"old\" way of doing them.\n    cat >${MODULE_FILE} <<EOF\nhelp([[\n  Sets PYTHON_HOME to be ${LOCATION}\n  Prepends ${LOCATION}/bin to PATH\n  Prepends ${LOCATION}/include to CPATH\n  Prepends ${LOCATION}/include/${PYTHON_INCLUDE_DIR} to CPATH\n  Prepends ${LOCATION}/lib to LIBRARY_PATH\n  Prepends ${LOCATION}/lib to LD_RUN_PATH\n  Prepends ${LOCATION}/lib to LD_LIBRARY_PATH\n  Prepends ${LOCATION}/share/man to MANPATH\n  environment variables.\n\n  This allows you to use the Python (v${_PYTHON_VERSION})\n]])\n\nwhatis(\"Sets up your environment to use the Python (v${_PYTHON_VERSION})\")\n\nconflict(\"python\")\n\ndepends_on(\"hdf5/${_HDF5_VERSION}\")\n\npushenv(\"PYTHON_VERSION\", \"${_PYTHON_VERSION}\")\npushenv(\"PYTHON_HOME\", \"${LOCATION}\")\n\nlocal apps_path = \"${LOCATION}\"\n\nprepend_path(\"PATH\", pathJoin(apps_path, \"/bin\"))\nprepend_path(\"CPATH\", pathJoin(apps_path, \"/include\"))\nprepend_path(\"CPATH\", pathJoin(apps_path, \"/include/${PYTHON_INCLUDE_DIR}\"))\nprepend_path(\"C_INCLUDE_PATH\", pathJoin(apps_path, \"/include\"))\nprepend_path(\"C_INCLUDE_PATH\", pathJoin(apps_path, \"/include/${PYTHON_INCLUDE_DIR}\"))\nprepend_path(\"PKG_CONFIG_PATH\", pathJoin(apps_path, \"/lib/pkgconfig\"))\nprepend_path(\"LIBRARY_PATH\", pathJoin(apps_path, \"/lib\"))\nprepend_path(\"LD_RUN_PATH\", pathJoin(apps_path, \"/lib\"))\nprepend_path(\"LD_LIBRARY_PATH\", pathJoin(apps_path, \"/lib\"))\nprepend_path(\"MANPATH\", pathJoin(apps_path, \"/share/man\"))\nprepend_path(\"CMAKE_PREFIX_PATH\", apps_path)\nprepend_path(\"PYTHONPATH\", pathJoin(apps_path, \"/lib/${PYTHON_INCLUDE_DIR}/site-packages\"))\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load python/${_PYTHON_VERSION}\n\ncd $dep_dir\nif [ -d ${LOCATION} ]; then\n    echo \"Python version ${_PYTHON_VERSION} already installed\"\nelse\n    wget https://github.com/libffi/libffi/archive/refs/tags/v3.4.4.tar.gz\n    tar xf v3.4.4.tar.gz\n    cd libffi-3.4.4\n    ./autogen.sh\n    ./configure --prefix=${LOCATION} --disable-docs\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd $dep_dir\n    rm -rf libffi-3.4.4 v3.4.4.tar.gz\n    # Some versions of python, like 3.10.3, don't search lib64, even if it's in\n    # the paths, so symlink libffi to the lib directory. We don't add lib64\n    # to the PATHs so other projects don't find them multiple times.\n    mkdir -p ${LOCATION}/lib/\n    cd ${LOCATION}/lib/\n    ln -s ../lib64/* ./\n    cd $dep_dir\n\n    # Build ncurses.\n    #\n    # We need to include termlib and termcap, and also to symlink a lot of\n    # headers so that python picks them up.\n    wget https://invisible-island.net/datafiles/release/ncurses.tar.gz\n    tar xf ncurses.tar.gz\n    cd ./ncurses-6.3\n    ./configure --with-libtool --with-termlib --with-shared \\\n                --with-gpm --enable-termcap --prefix=${LOCATION}\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    ln -s ${LOCATION}/include/ncurses/ncurses.h ${LOCATION}/include/ncurses.h\n    ln -s ${LOCATION}/include/ncurses/ncurses_dll.h \\\n       ${LOCATION}/include/ncurses_dll.h\n    ln -s ${LOCATION}/include/ncurses/term.h ${LOCATION}/include/term.h\n    ln -s ${LOCATION}/include/ncurses/curses.h ${LOCATION}/include/curses.h\n    ln -s ${LOCATION}/include/ncurses/panel.h ${LOCATION}/include/panel.h\n    cd ../\n    rm -rf ncurses-6.3 ncurses.tar.gz\n\n    # Build readline.\n    #\n    # To avoid undefined symbol UP, we need to build against termcap.\n    wget https://git.savannah.gnu.org/cgit/readline.git/snapshot/readline-8.2.tar.gz\n    tar xf readline-8.2.tar.gz\n    cd readline-8.2\n    ./configure --disable-install-examples --with-shared --enable-shared \\\n                --with-shared-termcap-library --prefix=${LOCATION}\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd ../\n    rm -rf readline-8.2 readline-8.2.tar.gz\n\n    # Add gdmb3 just in case somebody needs it.\n    wget https://ftp.gnu.org/gnu/gdbm/gdbm-1.23.tar.gz\n    tar xf gdbm-1.23.tar.gz\n    cd gdbm-1.23\n    ./configure --prefix=${LOCATION}\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd ../\n    rm -rf gdbm-1.23 gdbm-1.23.tar.gz\n\n    # Build sqlite which is needed by Jupyter.\n    wget https://sqlite.org/2024/sqlite-autoconf-3450100.tar.gz\n    tar xf sqlite-autoconf-3450100.tar.gz\n    cd ./sqlite-autoconf-3450100\n    # Build sqlite shared, but have Emacs link against the system install.\n    # The system install doesn't have the headers, just the library.\n    # ./configure --disable-shared --enable-static --with-pic --prefix=${LOCATION}\n    ./configure --with-pic --prefix=${LOCATION}\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd ../\n    rm -rf ./sqlite-autoconf*\n\n    # Build cpython (python itself).\n    #\n    # Other dependencies like gdbm, dbm, uuid, and tcl+tk (for tkinter) could be\n    # compiled before python.\n    wget https://github.com/python/cpython/archive/refs/tags/v$_PYTHON_VERSION.tar.gz\n    tar xf v${_PYTHON_VERSION}.tar.gz\n    cd cpython-${_PYTHON_VERSION}\n    ./configure --enable-optimizations --with-lto --enable-shared \\\n                --with-curses --prefix=${LOCATION}\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    # Create links for python and pip\n    if [ ! -e ${LOCATION}/bin/python ]; then\n        ln -s ${LOCATION}/bin/python3 ${LOCATION}/bin/python\n    fi\n    if [ ! -e ${LOCATION}/bin/pip ]; then\n        ln -s ${LOCATION}/bin/pip3 ${LOCATION}/bin/pip\n    fi\n    cd ../\n    rm -rf ./cpython-${_PYTHON_VERSION} v${_PYTHON_VERSION}.tar.gz\n\n    # Update pip to latest version\n    pip install -U pip\n    # Install meson, mako, numpy, and h5py to use our compiled HDF5.\n    python -m pip install meson mako numpy cmake-language-server\n    HDF5_DIR=${LOCATION} python -m pip install \\\n            --no-binary=h5py --no-cache-dir h5py\n    python -m pip install jupyter-server ipykernel notebook jupyterlab \\\n           matplotlib\n\n    chmod -R 555 ${LOCATION}\nfi\n################################################################\n# Python End\n################################################################\n\n################################################################\n# CMake Begin\n################################################################\ncd $dep_dir\n_CMAKE_VERSION=3.28.1\nLOCATION=${INSTALL_GCC_LOCATION}/cmake/${_CMAKE_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"CMake version ${_CMAKE_VERSION} already installed\"\nelse\n    wget -O cmake-install.sh \\\n         \"https://github.com/Kitware/CMake/releases/download/v${_CMAKE_VERSION}/cmake-${_CMAKE_VERSION}-linux-x86_64.sh\"\n    mkdir -p ${LOCATION}\n    sh cmake-install.sh --prefix=${LOCATION} --skip-license\n    rm cmake-install.sh\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/cmake/${_CMAKE_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tSets CMAKE_HOME to be \\$apps_path\"\n        puts stderr \"\\tPrepends $apps_path/bin to PATH\"\n        puts stderr \"\\tenvironment variable.\"\n        puts stderr \"\"\n        puts stderr \"\\tThis allows you to run cmake (${_CMAKE_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Allows users to run cmake (${_CMAKE_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        cmake\n\npushenv CMAKE_VERSION ${_CMAKE_VERSION}\npushenv CMAKE_HOME \\$apps_path\n\nprepend-path    PATH            \\$apps_path/bin\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load cmake/${_CMAKE_VERSION}\n################################################################\n# CMake End\n################################################################\n\n################################################################\n# Ninja Begin\n################################################################\ncd $dep_dir\n_NINJA_VERSION=1.10.1\nLOCATION=${INSTALL_GCC_LOCATION}/ninja/${_NINJA_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Ninja version ${_NINJA_VERSION} already installed\"\nelse\n    wget https://github.com/ninja-build/ninja/archive/refs/tags/v${_NINJA_VERSION}.tar.gz \\\n         -O ninja.tar.gz\n    tar -xzf ninja.tar.gz && cd ninja-${_NINJA_VERSION}\n    mkdir -p ${LOCATION}\n    mkdir build\n    cd build\n    cmake -D CMAKE_INSTALL_PREFIX=${LOCATION} \\\n          -D CMAKE_BUILD_TYPE=Release ..\n    make -j${PARALLEL_MAKE_ARG} install\n    cd $dep_dir\n    rm -rf ninja.tar.gz ninja-${_NINJA_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/ninja/${_NINJA_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tSets NINJA_HOME to be \\$apps_path\"\n        puts stderr \"\\tPrepends $apps_path/bin to PATH\"\n        puts stderr \"\\tenvironment variable.\"\n        puts stderr \"\"\n        puts stderr \"\\tThis allows you to run ninja (${_NINJA_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Allows users to run ninja (${_NINJA_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        ninja\n\nsetenv NINJA_VERSION ${_NINJA_VERSION}\nsetenv NINJA_HOME \\$apps_path\n\nprepend-path    PATH            \\$apps_path/bin\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load ninja/${_NINJA_VERSION}\n################################################################\n# Ninja End\n################################################################\n\n################################################################\n# hwloc Begin\n################################################################\ncd $dep_dir\n_HWLOC_VERSION=2.10.0\nLOCATION=${INSTALL_GCC_LOCATION}/hwloc/${_HWLOC_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Hwloc ${_HWLOC_VERSION} is already installed\"\nelse\n    wget https://download.open-mpi.org/release/hwloc/v2.10/hwloc-${_HWLOC_VERSION}.tar.gz \\\n         -O hwloc.tar.gz\n    tar -xzf hwloc.tar.gz\n    cd hwloc-${_HWLOC_VERSION}\n    ./configure --disable-cuda --with-pick \\\n                --prefix=${LOCATION}\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd ..\n    rm -r hwloc.tar.gz hwloc-${_HWLOC_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/hwloc/${_HWLOC_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tSets HWLOC_HOME to be \\$apps_path\"\n        puts stderr \"\\tPrepends $apps_path/bin to PATH\"\n        puts stderr \"\\tenvironment variable.\"\n        puts stderr \"\"\n        puts stderr \"\\tThis allows you to run hwloc (${_HWLOC_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Allows users to run hwloc (${_HWLOC_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        hwloc\n\nsetenv HWLOC_VERSION ${_HWLOC_VERSION}\nsetenv HWLOC_HOME \\$apps_path\n\nprepend-path    PATH            \\$apps_path/bin\nprepend-path    LIBRARY_PATH    \\$apps_path/lib\nprepend-path    LD_RUN_PATH     \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH \\$apps_path/lib\nprepend-path    MANPATH         \\$apps_path/share/man\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load hwloc/${_HWLOC_VERSION}\n################################################################\n# hwloc end\n################################################################\n\n################################################################\n# OpenBLAS Begin\n################################################################\ncd $dep_dir\n_OPENBLAS_VERSION=0.3.25\nLOCATION=${INSTALL_GCC_LOCATION}/openblas/${_OPENBLAS_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"OpenBLAS ${_OPENBLAS_VERSION} is already installed\"\nelse\n    wget https://github.com/OpenMathLib/OpenBLAS/releases/download/v${_OPENBLAS_VERSION}/OpenBLAS-${_OPENBLAS_VERSION}.tar.gz\n    tar -xzf OpenBLAS-${_OPENBLAS_VERSION}.tar.gz\n    cd OpenBLAS-${_OPENBLAS_VERSION}\n    make FC=gfortran USE_THREAD=0 USE_LOCKING=1 NO_AVX512=1 -j${PARALLEL_MAKE_ARG}\n    mkdir -p ${LOCATION}\n    make PREFIX=${LOCATION} install\n    cd ..\n    rm -r OpenBLAS-${_OPENBLAS_VERSION}.tar.gz OpenBLAS-${_OPENBLAS_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/openblas/${_OPENBLAS_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## OpenBLAS v${_OPENBLAS_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use OpenBLAS (v${_OPENBLAS_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use the OpenBLAS (v${_OPENBLAS_VERSION}) libraries\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        openblas\n\nsetenv  BLAS_VERSION    0.3.25\nsetenv  BLAS_HOME       \\$apps_path\n\nprepend-path    LIBRARY_PATH         \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH    \\$apps_path/lib\nprepend-path    CPATH                      \\$apps_path/include\nprepend-path  CMAKE_PREFIX_PATH  \\$apps_path\nprepend-path  PATH               \\$apps_path/bin\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load openblas/${_OPENBLAS_VERSION}\n################################################################\n# OpenBLAS End\n################################################################\n\n################################################################\n# libxsmm Begin\n################################################################\ncd $dep_dir\n_LIBXSMM_VERSION=1.16.1\nLOCATION=${INSTALL_GCC_LOCATION}/libxsmm/${_LIBXSMM_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"LIBXSMM ${_LIBXSMM_VERSION} already installed.\"\nelse\n    wget https://github.com/libxsmm/libxsmm/archive/${_LIBXSMM_VERSION}.tar.gz -O libxsmm.tar.gz\n    tar -xzf libxsmm.tar.gz && rm libxsmm.tar.gz && cd libxsmm-*\n    make AVX=2 PREFIX=${LOCATION} install -j${PARALLEL_MAKE_ARG}\n    cd ../\n    rm -r libxsmm-*\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/libxsmm/${_LIBXSMM_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## LIBXSMM for small matrix multiplications ${_LIBXSMM_VERSION}\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use libxsmm (${_LIBXSMM_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use libxsmm (${_LIBXSMM_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        libxsmm\n\nprepend-path CPATH              \\$apps_path/include\nprepend-path LIBRARY_PATH       \\$apps_path/lib\nprepend-path LD_LIBRARY_PATH    \\$apps_path/lib\nprepend-path LD_RUN_PATH        \\$apps_path/lib\nprepend-path CMAKE_PREFIX_PATH  \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load libxsmm/${_LIBXSMM_VERSION}\n################################################################\n# libxsmm End\n################################################################\n\n################################################################\n# gsl Begin\n################################################################\ncd $dep_dir\n_GSL_VERSION=2.7\nLOCATION=${INSTALL_GCC_LOCATION}/gsl/${_GSL_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"GSL ${_GSL_VERSION} already installed\"\nelse\n    wget https://mirror.ibcp.fr/pub/gnu/gsl/gsl-${_GSL_VERSION}.tar.gz\n    tar -xzf gsl-${_GSL_VERSION}.tar.gz && cd gsl-${_GSL_VERSION}\n    ./configure --prefix=${LOCATION} --with-pic\n    make -j${PARALLEL_MAKE_ARG} && make install\n    cd ..\n    rm -rf ./gsl-${_GSL_VERSION}.tar.gz ./gsl-${_GSL_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/gsl/${_GSL_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## GNU GSL ${_GSL_VERSION}\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n  puts stderr \"\\tSets env variable _GSL_VERSIONN to ${_GSL_VERSION}\"\n  puts stderr \"\\tSets env variable GSL_HOME to \\$apps_path\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/share/man to MANPATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use GSL (v${_GSL_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use GSL (v${_GSL_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        gsl\n\nsetenv GSL_VERSION \"${_GSL_VERSION}\"\nsetenv GSL_HOME \\$apps_path\n\nprepend-path CPATH              \\$apps_path/include\nprepend-path LIBRARY_PATH       \\$apps_path/lib\nprepend-path LD_LIBRARY_PATH    \\$apps_path/lib\nprepend-path LD_RUN_PATH        \\$apps_path/lib\nprepend-path CMAKE_PREFIX_PATH  \\$apps_path\nprepend-path PATH               \\$apps_path/bin\nprepend-path MANPATH            \\$apps_path/share/man\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\nmodule load gsl/${_GSL_VERSION}\n################################################################\n# gsl End\n################################################################\n\n################################################################\n# Blaze Begin\n################################################################\ncd $dep_dir\n_BLAZE_VERSION=3.8\nLOCATION=${INSTALL_GCC_LOCATION}/blaze/${_BLAZE_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Blaze version ${_BLAZE_VERSION} already installed\"\nelse\n    wget https://bitbucket.org/blaze-lib/blaze/downloads/blaze-${_BLAZE_VERSION}.tar.gz -O blaze.tar.gz\n    tar -xzf blaze.tar.gz && cd blaze-${_BLAZE_VERSION}\n    cmake -D CMAKE_INSTALL_PREFIX=${LOCATION} \\\n          -D CMAKE_BUILD_TYPE=Release .\n    make install\n    cd ../\n    rm -rf ./blaze.tar.gz ./blaze-${_BLAZE_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/blaze/${_BLAZE_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## Blaze v${_BLAZE_VERSION}\n##\n## Install using Install.sh\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use header-only library Blaze (v3.8)\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use the header-only library Blaze (v3.8)\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        blaze\n\nprepend-path    LIBRARY_PATH        \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH   \\$apps_path/lib\nprepend-path    CPATH                     \\$apps_path/include\nprepend-path  CMAKE_PREFIX_PATH \\$apps_path/\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# Blaze End\n################################################################\n\n################################################################\n# Boost Begin\n################################################################\ncd $dep_dir\n_BOOST_VERSION=1.82.0\nLOCATION=${INSTALL_GCC_LOCATION}/boost/${_BOOST_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Boost version ${_BOOST_VERSION} already installed\"\nelse\n    wget https://boostorg.jfrog.io/artifactory/main/release/${_BOOST_VERSION}/source/boost_${_BOOST_VERSION//\\./_}.tar.gz -O boost.tar.gz\n    tar -xzf boost.tar.gz && cd boost_${_BOOST_VERSION//\\./_}\n    mkdir -p ${LOCATION}\n    ./bootstrap.sh --prefix=${LOCATION}\n    ./b2 --without-python --without-mpi install\n    cd ..\n    rm -rf boost.tar.gz boost_${_BOOST_VERSION//\\./_}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/boost/${_BOOST_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## Boost ${_BOOST_VERSION}\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n  puts stderr \"\\tSets env variable _BOOST_VERSIONN to ${_BOOST_VERSION}\"\n  puts stderr \"\\tSets env variable BOOST_HOME to \\$apps_path\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use Boost (v${_BOOST_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use Boost (v${_BOOST_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        boost\n\nsetenv BOOST_VERSION \"${_BOOST_VERSION}\"\nsetenv BOOST_HOME \\$apps_path\n\nprepend-path CPATH              \\$apps_path/include\nprepend-path LIBRARY_PATH       \\$apps_path/lib\nprepend-path LD_LIBRARY_PATH    \\$apps_path/lib\nprepend-path LD_RUN_PATH        \\$apps_path/lib\nprepend-path CMAKE_PREFIX_PATH  \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# Boost End\n################################################################\n\n################################################################\n# Brigand Begin\n################################################################\n#\n# Brigand is now bundled with spectre so no need to install.\n#\n# cd $dep_dir\n# _BRIGAND_VERSION=`date +%Y.%m.%d`\n# LOCATION=${INSTALL_GCC_LOCATION}/brigand/${_BRIGAND_VERSION}\n# if [ -d ${LOCATION} ]; then\n#     echo \"Brigand version ${_BRIGAND_VERSION} already installed\"\n# else\n#     git clone https://github.com/edouarda/brigand.git brigand_${_BRIGAND_VERSION}\n#     cd brigand_${_BRIGAND_VERSION}\n#     mkdir -p ${LOCATION}\n#     cp -r include/ ${LOCATION}\n#     cd ..\n#     rm -rf brigand_${_BRIGAND_VERSION}\n#     chmod -R 555 ${LOCATION}\n# fi\n\n# MODULE_FILE=${MODULE_GCC_LOCATION}/libraries/brigand/${_BRIGAND_VERSION}\n# mkdir -p `dirname ${MODULE_FILE}`\n# if [ -f ${MODULE_FILE} ]; then\n#     echo \"Module file ${MODULE_FILE} already exists\"\n# else\n#     cat >${MODULE_FILE} <<EOF\n# #%Module1.0#####################################################################\n# ##\n# ## dot modulefile\n# ## brigand/master\n# proc ModulesHelp { } {\n#         global dotversion\n#         global apps_path\n\n#         puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n#         puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n#         puts stderr \"\"\n#         puts stderr \"\\tSets up your environment to use header-only template metaprogramming library Brigand\"\n#         puts stderr \"\"\n# }\n\n# module-whatis   \"Sets up your environment so you can use the header-only template metaprogramming library Brigand at it's master branch of ${_BRIGAND_VERSION}\"\n\n# set     dotversion      3.2.6\n\n# set     apps_path       ${LOCATION}\n\n# conflict        brigand\n\n# prepend-path CPATH              \\$apps_path/include\n# prepend-path CMAKE_PREFIX_PATH  \\$apps_path\n# EOF\n#     chmod -R 555 ${MODULE_FILE}\n# fi\n################################################################\n# Brigand End\n################################################################\n\n################################################################\n# jemalloc Begin\n################################################################\ncd $dep_dir\n_JEMALLOC_VERSION=5.3.0\nLOCATION=${INSTALL_GCC_LOCATION}/jemalloc/${_JEMALLOC_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Jemalloc version ${_JEMALLOC_VERSION} already installed\"\nelse\n    wget https://github.com/jemalloc/jemalloc/archive/refs/tags/${_JEMALLOC_VERSION}.tar.gz\n    tar -xzf ${_JEMALLOC_VERSION}.tar.gz\n    cd jemalloc-${_JEMALLOC_VERSION}\n    mkdir -p ${LOCATION}\n    ./autogen.sh --prefix=${LOCATION}\n    make CFLAGS=-fPIC -j${PARALLEL_MAKE_ARG}\n    make install\n    cd ..\n    rm -rf ${_JEMALLOC_VERSION}.tar.gz jemalloc-${_JEMALLOC_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/jemalloc/${_JEMALLOC_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## jemalloc ${_JEMALLOC_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/share/man to MANPATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use jemalloc (v${_JEMALLOC_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use jemalloc (v${_JEMALLOC_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        jemalloc\n\nprepend-path CPATH              \\$apps_path/include\nprepend-path LIBRARY_PATH       \\$apps_path/lib\nprepend-path LD_LIBRARY_PATH    \\$apps_path/lib\nprepend-path LD_RUN_PATH        \\$apps_path/lib\nprepend-path CMAKE_PREFIX_PATH  \\$apps_path\nprepend-path PATH               \\$apps_path/bin\nprepend-path MANPATH            \\$apps_path/share/man\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# jemalloc End\n################################################################\n\n################################################################\n# catch Begin\n################################################################\ncd $dep_dir\n_CATCH_VERSION=3.5.1\nLOCATION=${INSTALL_GCC_LOCATION}/catch/${_CATCH_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Catch version ${_CATCH_VERSION} already installed\"\nelse\n    wget https://github.com/catchorg/Catch2/archive/refs/tags/v${_CATCH_VERSION}.tar.gz -O catch.tar.gz\n    tar -xzf catch.tar.gz && cd Catch2-${_CATCH_VERSION}\n    mkdir -p ${LOCATION}\n    cmake  -B build -D CMAKE_BUILD_TYPE=Release -D BUILD_TESTING=OFF \\\n           -DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=ON \\\n           -D CMAKE_POSITION_INDEPENDENT_CODE=YES \\\n           -D CMAKE_INSTALL_PREFIX=${LOCATION}\n    cmake --build build/ --target install\n    cd $dep_dir\n    rm -rf catch.tar.gz Catch2-${_CATCH_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/catch/${_CATCH_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## Testing library Catch2 v${_CATCH_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use the testing library Catch2 (v${_CATCH_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use the testing library Catch2 (v${_CATCH_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        catch catch2 Catch Catch2\n\nprepend-path CPATH              \\$apps_path/include\nprepend-path LIBRARY_PATH       \\$apps_path/lib\nprepend-path LD_LIBRARY_PATH    \\$apps_path/lib\nprepend-path LD_RUN_PATH        \\$apps_path/lib\nprepend-path CMAKE_PREFIX_PATH  \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# catch End\n################################################################\n\n################################################################\n# libsharp Begin\n################################################################\n#\n# libsharp is now bundled with spectre so no need to install\n#\n# cd $dep_dir\n# _LIBSHARP_VERSION=1.0.0\n# LOCATION=${INSTALL_GCC_LOCATION}/libsharp/${_LIBSHARP_VERSION}\n# if [ -d ${LOCATION} ]; then\n#     echo \"Libsharp version ${_LIBSHARP_VERSION} already installed\"\n# else\n#     wget https://github.com/Libsharp/libsharp/archive/v${_LIBSHARP_VERSION}.tar.gz -O libsharp.tar.gz\n#     tar -xzf libsharp.tar.gz && cd libsharp-*\n#     mkdir -p ${LOCATION}\n#     autoconf\n#     ./configure --prefix=${LOCATION} --disable-openmp --enable-pic\n#     make PREFIX=${LOCATION}\n#     cd $dep_dir\n#     rm -rf libsharp.tar.gz libsharp-*\n#     chmod -R 555 ${LOCATION}\n# fi\n\n# MODULE_FILE=${MODULE_GCC_LOCATION}/libraries/libsharp/${_LIBSHARP_VERSION}\n# mkdir -p `dirname ${MODULE_FILE}`\n# if [ -f ${MODULE_FILE} ]; then\n#     echo \"Module file ${MODULE_FILE} already exists\"\n# else\n#     cat >${MODULE_FILE} <<EOF\n# #%Module1.0#####################################################################\n# ##\n# ## dot modulefile\n# ##\n# ## Spherical harmonic library libsharp v${_LIBSHARP_VERSION}\n# ##\n# proc ModulesHelp { } {\n#         global dotversion\n#         global apps_path\n\n#         puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n#         puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n#         puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n#         puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n#         puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n#         puts stderr \"\"\n#         puts stderr \"\\tSets up your environment to use libsharp (v${_LIBSHARP_VERSION})\"\n#         puts stderr \"\"\n# }\n\n# module-whatis   \"Sets up your environment to use libsharp (v${_LIBSHARP_VERSION})\"\n\n# set     dotversion      3.2.6\n\n# set     apps_path       ${LOCATION}\n\n# conflict        libsharp\n\n# prepend-path CPATH              \\$apps_path/include\n# prepend-path LIBRARY_PATH       \\$apps_path/lib\n# prepend-path LD_LIBRARY_PATH    \\$apps_path/lib\n# prepend-path LD_RUN_PATH        \\$apps_path/lib\n# prepend-path CMAKE_PREFIX_PATH  \\$apps_path\n# EOF\n#     chmod -R 555 ${MODULE_FILE}\n# fi\n################################################################\n# libsharp End\n################################################################\n\n################################################################\n# yaml-cpp Begin\n################################################################\ncd $dep_dir\n_YAMLCPP_VERSION=0.8.0\nLOCATION=${INSTALL_GCC_LOCATION}/yaml-cpp/${_YAMLCPP_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Yaml-Cpp version ${_YAMLCPP_VERSION} already installed\"\nelse\n    wget https://github.com/jbeder/yaml-cpp/archive/refs/tags/${_YAMLCPP_VERSION}.tar.gz -O yaml-cpp.tar.gz\n    tar -xzf yaml-cpp.tar.gz && cd yaml-cpp-${_YAMLCPP_VERSION}\n    mkdir build && cd build\n    cmake -D CMAKE_BUILD_TYPE=Release \\\n          -D YAML_CPP_BUILD_TESTS=OFF \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_POSITION_INDEPENDENT_CODE=ON \\\n          -D YAML_CPP_BUILD_CONTRIB=OFF \\\n          -D YAML_CPP_BUILD_TOOLS=ON \\\n          -D CMAKE_INSTALL_PREFIX=${LOCATION} ../\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd $dep_dir\n    rm -rf yaml-cpp.tar.gz yaml-cpp-*\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/yaml-cpp/${_YAMLCPP_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## Spherical harmonic library yaml-cpp v${_YAMLCPP_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use yaml-cpp (v${_YAMLCPP_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment to use yaml-cpp (v${_YAMLCPP_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        yaml-cpp\n\nprepend-path CPATH              \\$apps_path/include\nprepend-path LIBRARY_PATH       \\$apps_path/lib\nprepend-path LD_LIBRARY_PATH    \\$apps_path/lib\nprepend-path LD_RUN_PATH        \\$apps_path/lib\nprepend-path CMAKE_PREFIX_PATH  \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# yaml-cpp End\n################################################################\n\n################################################################\n# Google Benchmark Begin\n################################################################\ncd $dep_dir\n_GOOGLE_BENCHMARK_VERSION=1.8.3\nLOCATION=${INSTALL_GCC_LOCATION}/google_benchmark/${_GOOGLE_BENCHMARK_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Google Benchmark version ${_GOOGLE_BENCHMARK_VERSION} already installed\"\nelse\n    wget https://github.com/google/benchmark/archive/refs/tags/v${_GOOGLE_BENCHMARK_VERSION}.tar.gz -O google_benchmark.tar.gz\n    tar -xzf google_benchmark.tar.gz && cd benchmark-${_GOOGLE_BENCHMARK_VERSION}\n    mkdir build && cd build\n    cmake -D CMAKE_BUILD_TYPE=Release \\\n          -D CMAKE_POSITION_INDEPENDENT_CODE=ON \\\n          -D BENCHMARK_DOWNLOAD_DEPENDENCIES=OFF \\\n          -D BENCHMARK_ENABLE_TESTING=OFF \\\n          -D CMAKE_INSTALL_PREFIX=${LOCATION} ../\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd $dep_dir\n    rm -rf google_benchmark.tar.gz benchmark-*\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/google_benchmark/${_GOOGLE_BENCHMARK_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## Spherical harmonic library google_benchmark v${_GOOGLE_BENCHMARK_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use google_benchmark (v${_GOOGLE_BENCHMARK_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment to use google_benchmark (v${_GOOGLE_BENCHMARK_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        google_benchmark\n\nprepend-path CPATH              \\$apps_path/include\nprepend-path LIBRARY_PATH       \\$apps_path/lib\nprepend-path LD_LIBRARY_PATH    \\$apps_path/lib\nprepend-path LD_RUN_PATH        \\$apps_path/lib\nprepend-path CMAKE_PREFIX_PATH  \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# Google Benchmark End\n################################################################\n\n################################################################\n# LLVM Begin\n################################################################\ncd $dep_dir\n_LLVM_VERSION=17.0.6\nLOCATION=${INSTALL_GCC_LOCATION}/llvm/${_LLVM_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"LLVM version ${_LLVM_VERSION} already installed\"\nelse\n    git clone --depth=1 --branch llvmorg-${_LLVM_VERSION} \\\n        https://github.com/llvm/llvm-project.git\n    mkdir -p llvm-project/build\n    cd llvm-project/build\n\n    GCC_DIR=$(dirname $(dirname `which gcc`))\n    cmake -S ../llvm -B ./ \\\n          -D LLVM_ENABLE_PROJECTS=\"clang;clang-tools-extra;compiler-rt;lldb\" \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D CMAKE_INSTALL_PREFIX=${LOCATION} \\\n          -D LLVM_BUILD_LLVM_DYLIB=ON \\\n          -D ENABLE_LLVM_SHARED=ON \\\n          -D CMAKE_CXX_LINK_FLAGS=\"-Wl,-rpath,${GCC_DIR}/lib64 -L${GCC_DIR}/lib64\" \\\n          ..\n    make -j${PARALLEL_MAKE_ARG}\n    make -j${PARALLEL_MAKE_ARG} install\n\n    LLVM_DIR=${LOCATION}\n    cd $LLVM_DIR/bin && ln -s $GCC_DIR/bin/* .\n    cd $LLVM_DIR/include && ln -s $GCC_DIR/include/* .\n    cd $LLVM_DIR/lib && ln -s $GCC_DIR/lib/* .\n    if [ -d $LLVM_DIR/lib64 ]; then\n        cd $LLVM_DIR/lib64 && ln -s $GCC_DIR/lib64/* .\n    fi\n    cd $LLVM_DIR/libexec && ln -s $GCC_DIR/libexec/* .\n    cd $dep_dir\n    rm -rf llvm-project\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/llvm/${_LLVM_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## LLVM v${_LLVM_VERSION}\n##\n## To check that the right GCC version is used, run clang++ -v. You will get\n## something like:\n##\n## Found candidate GCC installation:\n##    /opt/sxs/test/LLVM/17.0.6/bin/../lib/gcc/x86_64-pc-linux-gnu/12.2.0\n## Selected GCC installation:\n##    /opt/sxs/test/LLVM/17.0.6/bin/../lib/gcc/x86_64-pc-linux-gnu/12.2.0\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tThe LLVM project, including extras like clang-tidy,\"\n        puts stderr \"\\tclang-format, libc++, and even templight.\"\n        puts stderr \"\\tSets LLVM_HOME to be \\$apps_path\"\n        puts stderr \"\\tDoes NOT define CC, and CXX to be LLVM clang compiler\"\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/share/man to MANPATH\"\n        puts stderr \"\\tenvironment variables.\"\n        puts stderr \"\"\n        puts stderr \"\\tAllows you to use the Clang Compiler (v${_LLVM_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment to use LLVM (v${_LLVM_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        llvm intel pgi open64\n\nsetenv  LLVM_VERSION    ${_LLVM_VERSION}\nsetenv  LLVM_HOME       \\$apps_path\n# Don't set the compilers so we can easily use clang tools like clang-format\n# with GCC. Really, whenever someone compiles they should explicitly choose\n# the compiler to use.\n# setenv  CC              clang\n# setenv  CXX             clang++\n\nprepend-path    PATH            \\$apps_path/bin\nprepend-path    CPATH           \\$apps_path/include\nprepend-path    C_INCLUDE_PATH  \\$apps_path/include\nprepend-path    LIBRARY_PATH    \\$apps_path/lib\nprepend-path    LD_RUN_PATH     \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH \\$apps_path/lib\nprepend-path    MANPATH         \\$apps_path/share/man\nprepend-path    CMAKE_PREFIX_PATH               \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# LLVM End\n################################################################\n\n################################################################\n# xsimd Begin\n################################################################\ncd $dep_dir\n_XSIMD_VERSION=12.1.1\nLOCATION=${INSTALL_GCC_LOCATION}/xsimd/${_XSIMD_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"xsimd version ${_XSIMD_VERSION} already installed\"\nelse\n    wget https://github.com/xtensor-stack/xsimd/archive/refs/tags/${_XSIMD_VERSION}.tar.gz \\\n         -O xsimd.tar.gz\n    tar -xzf xsimd.tar.gz && cd xsimd-${_XSIMD_VERSION}\n    cmake -D CMAKE_INSTALL_PREFIX=${LOCATION} \\\n          -D CMAKE_BUILD_TYPE=Release .\n    make install\n    cd ../\n    rm -rf ./xsimd.tar.gz ./xsimd-${_XSIMD_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/xsimd/${_XSIMD_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## xsimd v${_XSIMD_VERSION}\n##\n## Install using Install.sh\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use header-only library xsimd (v${_XSIMD_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use the header-only library xsimd (v${_XSIMD_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        xsimd\n\nprepend-path    LIBRARY_PATH        \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH   \\$apps_path/lib\nprepend-path    CPATH                     \\$apps_path/include\nprepend-path  CMAKE_PREFIX_PATH \\$apps_path/\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# xsimd End\n################################################################\n\n################################################################\n# ccache Begin\n################################################################\ncd $dep_dir\n_CCACHE_VERSION=4.9\nLOCATION=${INSTALL_GCC_LOCATION}/ccache/${_CCACHE_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"ccache version ${_CCACHE_VERSION} already installed\"\nelse\n    wget https://github.com/ccache/ccache/releases/download/v${_CCACHE_VERSION}/ccache-${_CCACHE_VERSION}-linux-x86_64.tar.xz \\\n         -O ccache.tar.xz\n    tar xf ccache.tar.xz\n    mkdir -p ${LOCATION}/bin\n    cp ./ccache-${_CCACHE_VERSION}-linux-x86_64/ccache ${LOCATION}/bin\n    rm -r ./ccache.tar.xz \\\n       ./ccache-${_CCACHE_VERSION}-linux-x86_64\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/ccache/${_CCACHE_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## ccache v${_CCACHE_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use ccache (v${_CCACHE_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use ccache (v${_CCACHE_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        ccache\n\nprepend-path    PATH        \\$apps_path/bin\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# ccache End\n################################################################\n\n################################################################\n# Intel MPI begin\n################################################################\ncd $dep_dir\n_IMPI_VERSION=2021.11\nLOCATION=${INSTALL_GCC_LOCATION}/intel/mpi/${_IMPI_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"Intel MPI version ${_IMPI_VERSION} already installed\"\nelse\n    mkdir ./intel_build\n    cd ./intel_build\n    # Note: likely the install and the URL need to be updated together.\n    IMPI_INSTALL=l_mpi_oneapi_p_2021.11.0.49513_offline.sh\n    wget https://registrationcenter-download.intel.com/akdlm/IRC_NAS/2c45ede0-623c-4c8e-9e09-bed27d70fa33/${IMPI_INSTALL}\n    chmod +x ./${IMPI_INSTALL}\n    ./${IMPI_INSTALL} -a --silent --install-dir ${LOCATION} --eula accept\n    cd ../\n    rm -rf ./intel_build\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/impi/${_IMPI_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -e ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    ln -s ${LOCATION}/mpi/${_IMPI_VERSION}/etc/modulefiles/mpi/${_IMPI_VERSION} \\\n       ${MODULE_FILE}\nfi\n################################################################\n# Intel MPI end\n################################################################\n\n################################################################\n# FFTW begin\n################################################################\ncd $dep_dir\nFFTW_VERSION=3.3.10\nLOCATION=${INSTALL_GCC_LOCATION}/fftw/${FFTW_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"FFTW version ${FFTW_VERSION} already installed\"\nelse\n    wget https://fftw.org/fftw-${FFTW_VERSION}.tar.gz\n    tar xzf fftw-${FFTW_VERSION}.tar.gz\n    cd fftw-${FFTW_VERSION}\n    ./configure --prefix=${LOCATION} \\\n                --enable-avx2 \\\n                --enable-fma \\\n                --disable-mpi \\\n                --disable-threads \\\n                --disable-openmp \\\n                --with-pic\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd ../\n    rm -rf fftw-${FFTW_VERSION} fftw-${FFTW_VERSION}.tar.gz\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/fftw/${FFTW_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## FFTW v${FFTW_VERSION}\n##\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tFFTW ${FFTW_VERSION}.\"\n        puts stderr \"\\tSets FFTW_HOME to be \\$apps_path\"\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/share/man to MANPATH\"\n        puts stderr \"\\tenvironment variables.\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment to use FFTW (v${FFTW_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        fftw\n\npushenv  FFTW_VERSION    ${FFTW_VERSION}\npushenv  FFTW_HOME       \\$apps_path\n\nprepend-path    PATH            \\$apps_path/bin\nprepend-path    CPATH           \\$apps_path/include\nprepend-path    LIBRARY_PATH    \\$apps_path/lib\nprepend-path    LD_RUN_PATH     \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH \\$apps_path/lib\nprepend-path    MANPATH         \\$apps_path/share/man\nprepend-path    CMAKE_PREFIX_PATH               \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# FFTW end\n################################################################\n\n################################################################\n# libbacktrace begin\n################################################################\ncd $dep_dir\nLIBBACKTRACE_VERSION=1.0\nLOCATION=${INSTALL_GCC_LOCATION}/libbacktrace/${LIBBACKTRACE_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"libbacktrace version ${LIBBACKTRACE_VERSION} already installed\"\nelse\n    git clone --depth=1 https://github.com/ianlancetaylor/libbacktrace.git \\\n        libbacktrace_clone\n    cd libbacktrace_clone\n    ./configure --prefix=${LOCATION} --with-pic\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd ../\n    rm -rf ./libbacktrace_clone\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/libbacktrace/${LIBBACKTRACE_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## libbacktrace ${LIBBACKTRACE_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tSets BACKTRACE_HOME to be \\$apps_path\"\n        puts stderr \"\\tPrepends \\$apps_path/include to CPATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use libbacktrace (${LIBBACKTRACE_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment to use libbacktrace (${LIBBACKTRACE_VERSION})\"\n\n# for Tcl script use only\nset     dotversion      3.1.6\n\nset     apps_path       ${LOCATION}\n\npushenv  BACKTRACE_HOME  \\$apps_path\n\nprepend-path    CPATH           \\$apps_path/include\nprepend-path    C_INCLUDE_PATH  \\$apps_path/include\nprepend-path    LIBRARY_PATH    \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH \\$apps_path/lib\nprepend-path    LD_RUN_PATH     \\$apps_path/lib\nprepend-path CMAKE_PREFIX_PATH \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# libbacktrace end\n################################################################\n\n################################################################\n# doxygen begin\n################################################################\ncd $dep_dir\nDOXYGEN_VERSION=1.10.0\nLOCATION=${INSTALL_GCC_LOCATION}/doxygen/${DOXYGEN_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"doxygen version ${DOXYGEN_VERSION} already installed\"\nelse\n    module load llvm/${_LLVM_VERSION}\n    wget https://github.com/doxygen/doxygen/archive/refs/tags/Release_${DOXYGEN_VERSION//\\./_}.tar.gz \\\n         -O doxygen.tar.gz\n    tar xf doxygen.tar.gz\n    cd doxygen-Release_${DOXYGEN_VERSION//\\./_}\n    mkdir -p ./build\n    cd build\n    # We get runtime errors if building against libclang, unfortunately.\n    cmake -D CMAKE_INSTALL_PREFIX=${LOCATION} \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D use_libclang=OFF \\\n          ..\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd $dep_dir\n    rm -rf doxygen-Release_${DOXYGEN_VERSION//\\./_}\n    rm ./doxygen.tar.gz\n    module unload llvm/${_LLVM_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/doxygen/${DOXYGEN_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## doxygen v${DOXYGEN_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use doxygen (v${DOXYGEN_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use doxygen (v${DOXYGEN_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        doxygen\n\nprepend-path    PATH        \\$apps_path/bin\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# doxygen end\n################################################################\n\n################################################################\n# lcov begin\n################################################################\ncd $dep_dir\nLCOV_VERSION=2.0\nLOCATION=${INSTALL_GCC_LOCATION}/lcov/${LCOV_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"lcov version ${LCOV_VERSION} already installed\"\nelse\n    wget https://github.com/linux-test-project/lcov/archive/refs/tags/v${LCOV_VERSION}.tar.gz \\\n         -O lcov.tar.gz\n    tar xf lcov.tar.gz\n    cd ./lcov-${LCOV_VERSION}\n    make PREFIX=${LOCATION} install\n    cd $dep_dir\n    rm -rf lcov.tar.gz ./lcov-${LCOV_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/lcov/${LCOV_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## lcov v${LCOV_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/share/man to MAN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use lcov (v${LCOV_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use lcov (v${LCOV_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        lcov\n\nprepend-path    PATH        \\$apps_path/bin\nprepend-path    LIBRARY_PATH    \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH \\$apps_path/lib\nprepend-path    LD_RUN_PATH     \\$apps_path/lib\nprepend-path    MANPATH         \\$apps_path/share/man\nprepend-path CMAKE_PREFIX_PATH \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# lcov end\n################################################################\n\n################################################################\n# petsc begin\n################################################################\ncd $dep_dir\nPETSC_VERSION=3.13.6\nLOCATION=${INSTALL_GCC_LOCATION}/petsc/${PETSC_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"petsc version ${PETSC_VERSION} already installed\"\nelse\n    module load \\\n           openblas/${_OPENBLAS_VERSION} \\\n           impi/${_IMPI_VERSION}\n    git clone -b v${PETSC_VERSION} --depth=1 \\\n        https://gitlab.com/petsc/petsc.git petsc-${PETSC_VERSION}\n    cd petsc-${PETSC_VERSION}\n    ./configure \\\n        --prefix=${LOCATION} \\\n        --enable-debug=0 \\\n        --COPTFLAGS=\"-O3\" --CXXOPTFLAGS=\"-O3\" --FOPTFLAGS=\"-O3\" \\\n        --with-blas-lib=$BLAS_HOME/lib/libopenblas.so \\\n        --with-lapack-lib=$BLAS_HOME/lib/libopenblas.so\n    make -j${PARALLEL_MAKE_ARG}\n    make install\n    cd ../\n    rm -rf ./petsc-${PETSC_VERSION}\n    module unload \\\n           openblas/${_OPENBLAS_VERSION} \\\n           impi/${_IMPI_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/libraries/petsc/${PETSC_VERSION}\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\n#%Module1.0#####################################################################\n##\n## dot modulefile\n##\n## petsc v${PETSC_VERSION}\n##\nproc ModulesHelp { } {\n        global dotversion\n        global apps_path\n\n        puts stderr \"\\tPrepends \\$apps_path/bin to PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_LIBRARY_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/lib to LD_RUN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/share/man to MAN_PATH\"\n        puts stderr \"\\tPrepends \\$apps_path/ to CMAKE_PREFIX_PATH\"\n        puts stderr \"\"\n        puts stderr \"\\tSets up your environment to use petsc (v${PETSC_VERSION})\"\n        puts stderr \"\"\n}\n\nmodule-whatis   \"Sets up your environment so you can use petsc (v${PETSC_VERSION})\"\n\nset     dotversion      3.2.6\n\nset     apps_path       ${LOCATION}\n\nconflict        petsc\n\nsetenv PETSC_HOME \\$apps_path\n\nprepend-path    PATH        \\$apps_path/bin\nprepend-path    LIBRARY_PATH    \\$apps_path/lib\nprepend-path    LD_LIBRARY_PATH \\$apps_path/lib\nprepend-path    LD_RUN_PATH     \\$apps_path/lib\nprepend-path    MANPATH         \\$apps_path/share/man\nprepend-path CMAKE_PREFIX_PATH \\$apps_path\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# petsc end\n################################################################\n\n################################################################\n# Charm++ begin\n################################################################\ncd $dep_dir\n_CHARM_VERSION=7.0.0\nLOCATION=${INSTALL_GCC_LOCATION}/charm/${_CHARM_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"charm version ${_CHARM_VERSION} already installed\"\nelse\n    module purge\n    module load gcc/${_GCC_VERSION} \\\n           cmake/${_CMAKE_VERSION} \\\n           hwloc/${_HWLOC_VERSION} \\\n           impi/${_IMPI_VERSION}\n    git clone https://github.com/UIUC-PPL/charm.git ${LOCATION}\n    cd ${LOCATION}/\n    git checkout v${_CHARM_VERSION}\n    git apply $SPECTRE_HOME/support/Charm/v${_CHARM_VERSION}.patch\n    # git add -u\n    # git commit -m \"Apply SpECTRE patch\"\n    ./build LIBS mpi-linux-x86_64 smp -g -fPIC --with-production \\\n            --destination=mpi-linux-x86_64-smp-gcc -j32\n    cd $dep_dir\n    chmod -R 555 ${LOCATION}\n\n    git clone https://github.com/UIUC-PPL/charm.git ${LOCATION}-tracing\n    cd ${LOCATION}-tracing/\n    git checkout v${_CHARM_VERSION}\n    git apply $SPECTRE_HOME/support/Charm/v${_CHARM_VERSION}.patch\n    ./build LIBS mpi-linux-x86_64 smp -g -fPIC --with-production \\\n            --destination=mpi-linux-x86_64-smp-gcc-tracing --enable-tracing \\\n            -j32\n    cd $dep_dir\n    chmod -R 555 ${LOCATION}-tracing/\n\n    module unload hwloc/${_HWLOC_VERSION} \\\n           impi/${_IMPI_VERSION}\n\nfi\n\nMODULE_FILE_BASE=${MODULE_GCC_LOCATION}/libraries/charm/${_CHARM_VERSION}\n_write_charm_module() {\n    MODULE_FILE=${MODULE_FILE_BASE}${2}.lua\n    mkdir -p `dirname ${MODULE_FILE}`\n    if [ -f ${MODULE_FILE} ]; then\n        echo \"Module file ${MODULE_FILE} already exists\"\n    else\n        # We set this up as a Lua module because that way we can use the\n        # depends_on function to automatically load and unload HDF5. The\n        # TCL-based module files seems to be the \"old\" way of doing them.\n        cat >${MODULE_FILE} <<EOF\nhelp([[\n  Sets CHARM_HOME to ${1}\n  Sets CHARM_ROOT to ${1}\n  Sets CHARM_VERSION to ${_CHARM_VERSION}\n  Prepends ${1}/bin to PATH\n  Prepends ${1}/include to CPATH\n  Prepends ${1}/include to C_INCLUDE_PATH\n  Prepends ${1}/lib to LIBRARY_PATH\n  Prepends ${1}/lib to LD_RUN_PATH\n  Prepends ${1}/lib to LD_LIBRARY_PATH\n  Prepends ${1} to CMAKE_PREFIX_PATH\n  environment variables.\n\n  This allows you to use the Charm (v${_CHARM_VERSION})\n]])\n\nwhatis(\"Sets up your environment to use the Charm (v${_CHARM_VERSION})\")\n\nconflict(\"charm\")\n\nlocal apps_path = \"${1}\"\n\ndepends_on(\"hwloc/${_HWLOC_VERSION}\")\ndepends_on(\"impi/${_IMPI_VERSION}\")\n\npushenv(\"CHARM_VERSION\", \"${_CHARM_VERSION}\")\npushenv(\"CHARM_HOME\", apps_path)\npushenv(\"CHARM_ROOT\", apps_path)\n\nprepend_path(\"PATH\", pathJoin(apps_path, \"/bin\"))\nprepend_path(\"CPATH\", pathJoin(apps_path, \"/include\"))\nprepend_path(\"C_INCLUDE_PATH\", pathJoin(apps_path, \"/include\"))\nprepend_path(\"LIBRARY_PATH\", pathJoin(apps_path, \"/lib\"))\nprepend_path(\"LD_RUN_PATH\", pathJoin(apps_path, \"/lib\"))\nprepend_path(\"LD_LIBRARY_PATH\", pathJoin(apps_path, \"/lib\"))\nprepend_path(\"CMAKE_PREFIX_PATH\", apps_path)\nEOF\n        chmod -R 555 ${MODULE_FILE}\n    fi\n}\n_write_charm_module \"${LOCATION}/mpi-linux-x86_64-smp-gcc\" \"\"\n_write_charm_module \"${LOCATION}-tracing/mpi-linux-x86_64-smp-gcc-tracing\" \"-tracing\"\n################################################################\n# Charm++ end\n################################################################\n\n################################################################\n# spectre-python begin\n################################################################\ncd $dep_dir\nLOCATION=${INSTALL_GCC_LOCATION}/spectre-python/${_PYTHON_VERSION}\nif [ -d ${LOCATION} ]; then\n    echo \"spectre-python version ${_PYTHON_VERSION} already installed\"\nelse\n    module load python/${_PYTHON_VERSION}\n    python -m venv ${LOCATION}\n    source ${LOCATION}/bin/activate\n    python -m pip install -U pip\n    python -m pip install --no-cache-dir \\\n        -r $SPECTRE_HOME/support/Python/requirements.txt \\\n        -r $SPECTRE_HOME/support/Python/dev_requirements.txt\n    python -m pip install \"pybind11[global]\"\n    # For paraview, etc.\n    python -m pip install meson mako cmake-language-server\n    deactivate\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/spectre-python/${_PYTHON_VERSION}.lua\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    # We set this up as a Lua module because that way we can use the depends_on\n    # function to automatically load and unload HDF5. The TCL-based module files\n    # seems to be the \"old\" way of doing them.\n    cat >${MODULE_FILE} <<EOF\nhelp([[\n  This allows you to use the SpECTRE Python (v${_PYTHON_VERSION})\n]])\n\nwhatis(\"Sets up your environment to use the SpECTRE Python (v${_PYTHON_VERSION})\")\n\nlocal apps_path = \"${LOCATION}\"\n\ndepends_on(\"hdf5/${_HDF5_VERSION}\")\ndepends_on(\"python/${_PYTHON_VERSION}\")\n\npushenv(\"VIRTUAL_ENV\", \"${LOCATION}\")\n\nprepend_path(\"PATH\", pathJoin(apps_path, \"/bin\"))\nprepend_path(\"PYTHONPATH\", pathJoin(apps_path, \"/lib/${PYTHON_INCLUDE_DIR}/site-packages\"))\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# spectre-python end\n################################################################\n\n################################################################\n# spectre-deps begin\n################################################################\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/spectre-deps.lua\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    # We set this up as a Lua module because that way we can use the depends_on\n    # function to automatically load and unload HDF5. The TCL-based module files\n    # seems to be the \"old\" way of doing them.\n    cat >${MODULE_FILE} <<EOF\nhelp([[\n  Load SpECTRE dependencies.\n]])\n\nwhatis(\"Loads SpECTRE dependencies\")\n\nconflict(\"spectre-deps\")\n\ndepends_on(\"blaze/${_BLAZE_VERSION}\")\ndepends_on(\"boost/${_BOOST_VERSION}\")\ndepends_on(\"brigand/${_BRIGAND_VERSION}\")\ndepends_on(\"catch/${_CATCH_VERSION}\")\ndepends_on(\"charm/${_CHARM_VERSION}\")\ndepends_on(\"fftw/${FFTW_VERSION}\")\ndepends_on(\"google_benchmark/${_GOOGLE_BENCHMARK_VERSION}\")\ndepends_on(\"gsl/${_GSL_VERSION}\")\ndepends_on(\"hdf5/${_HDF5_VERSION}\")\ndepends_on(\"impi/${_IMPI_VERSION}\")\ndepends_on(\"jemalloc/${_JEMALLOC_VERSION}\")\ndepends_on(\"libbacktrace/${LIBBACKTRACE_VERSION}\")\ndepends_on(\"libsharp/${_LIBSHARP_VERSION}\")\ndepends_on(\"libxsmm/${_LIBXSMM_VERSION}\")\ndepends_on(\"openblas/${_OPENBLAS_VERSION}\")\ndepends_on(\"petsc/${PETSC_VERSION}\")\ndepends_on(\"xsimd/${_XSIMD_VERSION}\")\ndepends_on(\"yaml-cpp/${_YAMLCPP_VERSION}\")\n\ndepends_on(\"ccache/${_CCACHE_VERSION}\")\ndepends_on(\"cmake/${_CMAKE_VERSION}\")\ndepends_on(\"doxygen/${DOXYGEN_VERSION}\")\ndepends_on(\"hwloc/${_HWLOC_VERSION}\")\ndepends_on(\"lcov/${LCOV_VERSION}\")\ndepends_on(\"llvm/${_LLVM_VERSION}\")\ndepends_on(\"ninja/${_NINJA_VERSION}\")\ndepends_on(\"spectre-python/${_PYTHON_VERSION}\")\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n################################################################\n# spectre-deps end\n################################################################\n\n################################################################\n# paraview begin\n################################################################\ncd $dep_dir\nMESA_VERSION=23.3.4\nCOMMON_LOCATION=${INSTALL_GCC_LOCATION}/paraview/common\nif [ -d ${COMMON_LOCATION} ]; then\n    echo \"paraview commons version already installed\"\nelse\n    module load python/${_PYTHON_VERSION} \\\n           llvm/${_LLVM_VERSION} \\\n           impi/${_IMPI_VERSION} \\\n           hdf5/${_HDF5_VERSION} \\\n           libbacktrace/${LIBBACKTRACE_VERSION} \\\n           cmake/${_CMAKE_VERSION} \\\n           ninja/${_NINJA_VERSION} \\\n           llvm/${_LLVM_VERSION}\n    # Set up mesa dependency libdrm\n    wget https://gitlab.freedesktop.org/mesa/drm/-/archive/libdrm-2.4.120/drm-libdrm-2.4.120.tar.gz\n    tar xf drm-libdrm-2.4.120.tar.gz\n    cd drm-libdrm-2.4.120\n    meson setup build -Dprefix=${COMMON_LOCATION}\n    cd build\n    ninja\n    ninja install\n    cd ../../\n    rm -rf drm-libdrm-2.4.120.tar.gz drm-libdrm-2.4.120\n\n    wget https://archive.mesa3d.org/mesa-${MESA_VERSION}.tar.xz\n    tar xf mesa-${MESA_VERSION}.tar.xz\n    cd mesa-${MESA_VERSION}\n    PKG_CONFIG_PATH=$PKG_CONFIG_PATH:${COMMON_LOCATION}/lib64/pkgconfig\n    meson setup build/ -Dosmesa=true -Dgallium-drivers=swrast \\\n          -Dvulkan-drivers=swrast -Degl=disabled -Dglx=disabled \\\n          -Dgbm=disabled -Dopengl=true -Dgles1=disabled -Dgles2=disabled \\\n          -Dxmlconfig=disabled -Dexpat=disabled -Dcpp_rtti=false \\\n          -Dplatforms= -Dprefix=${COMMON_LOCATION}\n    cd ./build\n    ninja\n    ninja install\n    cd ../../\n    rm -rf mesa-${MESA_VERSION} mesa-${MESA_VERSION}.tar.xz\n    module unload python/${_PYTHON_VERSION} \\\n           llvm/${_LLVM_VERSION} \\\n           impi/${_IMPI_VERSION} \\\n           hdf5/${_HDF5_VERSION} \\\n           libbacktrace/${LIBBACKTRACE_VERSION} \\\n           cmake/${_CMAKE_VERSION} \\\n           ninja/${_NINJA_VERSION}\n    chmod -R 555 ${LOCATION}\nfi\n\nMODULE_FILE=${MODULE_GCC_LOCATION}/tools/paraview/common.lua\nmkdir -p `dirname ${MODULE_FILE}`\nif [ -f ${MODULE_FILE} ]; then\n    echo \"Module file ${MODULE_FILE} already exists\"\nelse\n    cat >${MODULE_FILE} <<EOF\nhelp([[\nparaview dependencies\n]])\nwhatis(\"Sets up ParaView dependencies\")\n\nlocal apps_path = \"${COMMON_LOCATION}\"\n\ndepends_on(\"boost/${_BOOST_VERSION}\")\ndepends_on(\"llvm/${_LLVM_VERSION}\")\ndepends_on(\"impi/${_IMPI_VERSION}\")\ndepends_on(\"hdf5/${_HDF5_VERSION}\")\ndepends_on(\"libbacktrace/${LIBBACKTRACE_VERSION}\")\ndepends_on(\"cmake/${_CMAKE_VERSION}\")\ndepends_on(\"ninja/${_NINJA_VERSION}\")\n\nprepend_path(\"CPATH\", pathJoin(apps_path, \"/include\"))\nprepend_path(\"C_INCLUDE_PATH\", pathJoin(apps_path, \"/include\"))\nprepend_path(\"LIBRARY_PATH\", pathJoin(apps_path, \"/lib64\"))\nprepend_path(\"LD_RUN_PATH\", pathJoin(apps_path, \"/lib64\"))\nprepend_path(\"LD_LIBRARY_PATH\", pathJoin(apps_path, \"/lib64\"))\nprepend_path(\"CMAKE_PREFIX_PATH\", apps_path)\nEOF\n    chmod -R 555 ${MODULE_FILE}\nfi\n\n# Set up a few different versions of paraview so people can connect more\n# easily\n_build_paraview() {\n    PARAVIEW_VERSION=$1\n    LOCATION=${INSTALL_GCC_LOCATION}/paraview/${PARAVIEW_VERSION}\n    if [ -d ${LOCATION} ]; then\n        echo \"paraview version ${PARAVIEW_VERSION} already installed\"\n    else\n        module load python/${_PYTHON_VERSION} \\\n           paraview/common\n        wget https://www.paraview.org/paraview-downloads/download.php\\?submit\\=Download\\&version\\=v${PARAVIEW_VERSION%.*}\\&type\\=source\\&os\\=Sources\\&downloadFile\\=ParaView-v${PARAVIEW_VERSION}.tar.xz \\\n             -O ParaView-v${PARAVIEW_VERSION}.tar.xz\n        tar xf ParaView-v${PARAVIEW_VERSION}.tar.xz\n        cd ParaView-v${PARAVIEW_VERSION}\n        mkdir -p ./build\n        cd ./build\n        cmake -D CMAKE_INSTALL_PREFIX=${LOCATION} \\\n              -D CMAKE_BUILD_TYPE=Release \\\n              -D CMAKE_C_COMPILER=gcc -D CMAKE_CXX_COMPILER=g++ \\\n              -D PARAVIEW_USE_QT=NO -D VTK_USE_X=OFF \\\n              -D OPENGL_INCLUDE_DIR=IGNORE -D OPENGL_gl_LIBRARY=IGNORE \\\n              -D VTK_OPENGL_HAS_OSMESA=ON \\\n              -D VTK_DEFAULT_RENDER_WINDOW_OFFSCREEN=ON \\\n              -D PARAVIEW_BUILD_SHARED_LIBS=ON -D BUILD_TESTING=OFF \\\n              -D VTK_SMP_IMPLEMENTATION_TYPE=OpenMP \\\n              -D PARAVIEW_USE_MPI=ON \\\n              -D PARAVIEW_ENABLE_XDMF2=ON -D PARAVIEW_ENABLE_XDMF3=ON \\\n              -D OSMESA_INCLUDE_DIR=${COMMON_LOCATION}/include \\\n              -D OSMESA_LIBRARY=${COMMON_LOCATION}/lib64/libOSMesa.so \\\n              -D CMAKE_INSTALL_RPATH=${COMMON_LOCATION}/lib64 \\\n              -D PARAVIEW_USE_PYTHON=YES \\\n              -Wno-dev \\\n              ..\n        make -j${PARALLEL_MAKE_ARG}\n        make install\n        cd ../../\n        rm -rf ./ParaView-v${PARAVIEW_VERSION} \\\n           ParaView-v${PARAVIEW_VERSION}.tar.xz\n        module unload \\\n               python/${_PYTHON_VERSION} \\\n               paraview/common\n        chmod -R 555 ${LOCATION}\n    fi\n\n    MODULE_FILE=${MODULE_GCC_LOCATION}/tools/paraview/${PARAVIEW_VERSION}.lua\n    mkdir -p `dirname ${MODULE_FILE}`\n    if [ -f ${MODULE_FILE} ]; then\n        echo \"Module file ${MODULE_FILE} already exists\"\n    else\n        cat >${MODULE_FILE} <<EOF\nhelp([[\n  ParaView v${PARAVIEW_VERSION}\n]])\nwhatis(\"Sets up your environment so you can use ParaView (v${PARAVIEW_VERSION})\")\n\nlocal apps_path = \"${LOCATION}\"\n\nconflict(\"paraview\")\n\ndepends_on(\"boost/${_BOOST_VERSION}\")\ndepends_on(\"llvm/${_LLVM_VERSION}\")\ndepends_on(\"impi/${_IMPI_VERSION}\")\ndepends_on(\"hdf5/${_HDF5_VERSION}\")\ndepends_on(\"python/${_PYTHON_VERSION}\")\n\nprepend_path(\"PATH\", pathJoin(apps_path, \"/bin\"))\nprepend_path(\"CPATH\", pathJoin(apps_path, \"/include\"))\nprepend_path(\"C_INCLUDE_PATH\", pathJoin(apps_path, \"/include\"))\nprepend_path(\"LIBRARY_PATH\", pathJoin(apps_path, \"/lib64\"))\nprepend_path(\"LD_RUN_PATH\", pathJoin(apps_path, \"/lib64\"))\nprepend_path(\"LD_LIBRARY_PATH\", pathJoin(apps_path, \"/lib64\"))\nprepend_path(\"CMAKE_PREFIX_PATH\", apps_path)\nEOF\n        chmod -R 555 ${MODULE_FILE}\n    fi\n}\n_build_paraview 5.10.1\n_build_paraview 5.11.1\n################################################################\n# paraview end\n################################################################\n"
  },
  {
    "path": "support/Environments/sonic.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_setup_modules() {\n    echo \"All modules on Sonic are provided by the system\"\n}\n\nspectre_load_modules() {\n    # The order here is important\n    module load sxs\n    module load spectre-env > /dev/null 2>&1\n}\n\nspectre_unload_modules() {\n    # The order here is important\n    module unload spectre-env > /dev/null 2>&1\n    module unload sxs\n}\n\nspectre_run_cmake_clang() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n\n    spectre_load_modules > /dev/null 2>&1\n    cmake -D CMAKE_C_COMPILER=clang \\\n          -D CMAKE_CXX_COMPILER=clang++ \\\n          -D CMAKE_Fortran_COMPILER=flang \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D MEMORY_ALLOCATOR=SYSTEM \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D ENABLE_PARAVIEW=ON \\\n          -D MACHINE=Sonic \\\n          -D USE_XSIMD=yes \\\n          -D DEBUG_SYMBOLS=OFF \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n\nspectre_run_cmake_gcc() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n\n    spectre_load_modules > /dev/null 2>&1\n    cmake -D CMAKE_C_COMPILER=gcc \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D MEMORY_ALLOCATOR=SYSTEM \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D ENABLE_PARAVIEW=ON \\\n          -D MACHINE=Sonic \\\n          -D USE_XSIMD=yes \\\n          -D DEBUG_SYMBOLS=OFF \\\n          \"$@\" \\\n          $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/urania.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_load_modules() {\n    module load gcc/11\n    module load impi/2021.7\n    module load boost/1.79\n    module load gsl/1.16\n    module load cmake/3.26\n    module load hdf5-serial/1.12.2\n    module load anaconda/3/2021.11\n    module load paraview/5.10\n    # Load Spack environment\n    source /u/guilara/repos/spack/share/spack/setup-env.sh\n    spack env activate env3_spectre_impi\n    # Load python environment\n    source /u/guilara/envs/spectre_env/bin/activate\n    # Define Charm paths\n    export CHARM_ROOT=/u/guilara/charm_impi_3/mpi-linux-x86_64-smp\n    export PATH=$PATH:/u/guilara/charm_impi_3/mpi-linux-x86_64-smp/bin\n}\n\nspectre_unload_modules() {\n    module unload gcc/11\n    module unload impi/2021.7\n    module unload boost/1.79\n    module unload gsl/1.16\n    module unload cmake/3.26\n    module unload hdf5-serial/1.12.2\n    module unload anaconda/3/2021.11\n    module unload paraview/5.10\n    # Unload Spack environment\n    spack env deactivate\n    # Unload python environment\n    deactivate\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n\n    cmake -D CMAKE_C_COMPILER=gcc \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D DEBUG_SYMBOLS=OFF \\\n          -D BUILD_SHARED_LIBS=ON \\\n          -D MEMORY_ALLOCATOR=JEMALLOC \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D MACHINE=Urania \\\n          -D SPEC_ROOT=/u/guilara/repos/spec \\\n          -D SPECTRE_FETCH_MISSING_DEPS=ON \\\n          \"$@\" $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Environments/viper.sh",
    "content": "#!/bin/env sh\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_load_modules() {\n    module load gcc/13\n    module load impi/2021.11\n    module load boost/1.83\n    module load gsl/2.7\n    module load cmake/3.30\n    module load hdf5-serial/1.14.1\n    module load python-waterboa/2024.06\n    module load doxygen/1.10.0\n    module load paraview/5.11\n    # Load Spack environment\n    source /u/guilara/repos/spack/share/spack/setup-env.sh\n    spack env activate env3_spectre_impi\n    # Load python environment\n    source /u/guilara/envs/spectre_env/bin/activate\n    # Define Charm paths\n    export CHARM_ROOT=/u/guilara/charm_impi/mpi-linux-x86_64-smp\n    export PATH=$PATH:/u/guilara/charm_impi/mpi-linux-x86_64-smp/bin\n}\n\nspectre_unload_modules() {\n    module load gcc/13\n    module load impi/2021.11\n    module load boost/1.83\n    module load gsl/2.7\n    module load cmake/3.30\n    module load hdf5-serial/1.14.1\n    module load python-waterboa/2024.06\n    module load doxygen/1.10.0\n    module load paraview/5.11\n    # Unload Spack environment\n    spack env deactivate\n    # Unload python environment\n    deactivate\n}\n\nspectre_run_cmake() {\n    if [ -z ${SPECTRE_HOME} ]; then\n        echo \"You must set SPECTRE_HOME to the cloned SpECTRE directory\"\n        return 1\n    fi\n    spectre_load_modules\n\n    cmake -D CMAKE_C_COMPILER=gcc \\\n          -D CMAKE_CXX_COMPILER=g++ \\\n          -D CMAKE_Fortran_COMPILER=gfortran \\\n          -D CHARM_ROOT=$CHARM_ROOT \\\n          -D CMAKE_BUILD_TYPE=Release \\\n          -D DEBUG_SYMBOLS=OFF \\\n          -D BUILD_SHARED_LIBS=ON \\\n          -D MEMORY_ALLOCATOR=JEMALLOC \\\n          -D BUILD_PYTHON_BINDINGS=ON \\\n          -D MACHINE=Viper \\\n          -D SPEC_ROOT=/u/guilara/repos/spec \\\n          -D Catch2_DIR=/u/guilara/repos/Catch2/install_dir/lib64/cmake/Catch2 \\\n          \"$@\" $SPECTRE_HOME\n}\n"
  },
  {
    "path": "support/Machines/CaltechHpc.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: CaltechHpc\n  Description: |\n    Supercomputer at Caltech.\n    More information:\n    https://www.hpc.caltech.edu/documentation\n  # Split one physical 56 core node into two charm nodes\n  DefaultTasksPerNode: 2\n  DefaultProcsPerTask: 28\n  DefaultQueue: \"expansion\"\n  DefaultTimeLimit: \"1-00:00:00\"\n  LaunchCommandSingleNode: [\"mpirun\", \"-n\", \"1\"]\n  LaunchCommandLoginNode: []\n"
  },
  {
    "path": "support/Machines/Mbot.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: Mbot\n  Description: |\n    Supercomputer at Cornell hosted by Nils Deppe.\n    More information:\n    https://github.com/sxs-collaboration/WelcomeToSXS/wiki/Mbot\n  DefaultTasksPerNode: 6\n  DefaultProcsPerTask: 32\n  DefaultQueue: \"normal\"\n  DefaultTimeLimit: \"1-00:00:00\"\n  LaunchCommandSingleNode: [\"mpirun\", \"-n\", \"1\"]\n  LaunchCommandLoginNode: [\"mpirun\", \"-n\", \"1\"]\n"
  },
  {
    "path": "support/Machines/Ocean2.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: Ocean2\n  Description: |\n    Supercomputer at Cal State Fullerton hosted by Geoffrey Lovelace\n    More information:\n    Contact Geoffrey Lovelace for more information.\n  DefaultTasksPerNode: 6\n  DefaultProcsPerTask: 32\n  DefaultQueue: \"normal\"\n  DefaultTimeLimit: \"01-00:00:00\"\n  LaunchCommandSingleNode: []\n  LaunchCommandLoginNode: []\n"
  },
  {
    "path": "support/Machines/Ocean2_orca1.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: Ocean2_orca1\n  Description: |\n    Orca-1 Partition on the Ocean2 Supercomputer at Cal State Fullerton hosted\n    by Geoffrey Lovelace\n    More information:\n    Contact Geoffrey Lovelace for more information.\n  DefaultTasksPerNode: 1\n  DefaultProcsPerTask: 20\n  DefaultQueue: \"orca-1\"\n  DefaultTimeLimit: \"01-00:00:00\"\n  LaunchCommandSingleNode: []\n  LaunchCommandLoginNode: []\n"
  },
  {
    "path": "support/Machines/Oscar.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: Oscar\n  Description: |\n    Supercomputer at Brown.\n    More information:\n    https://docs.ccv.brown.edu/oscar\n  # Split one physical 32 core node into two charm nodes\n  DefaultTasksPerNode: 2\n  DefaultProcsPerTask: 16\n  DefaultQueue: \"batch\"\n  DefaultTimeLimit: \"1-00:00:00\"\n  LaunchCommandSingleNode: [\"mpirun\", \"-n\", \"1\"]\n  LaunchCommandLoginNode: []\n"
  },
  {
    "path": "support/Machines/Perlmutter.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: Perlmutter\n  Description: |\n    DOE supercomputer at Lawrence Berkely National Laboratory\n    More information:\n    https://docs.nersc.gov/systems/perlmutter/\n  DefaultTasksPerNode: 8\n  DefaultProcsPerTask: 32\n  DefaultQueue: \"regular\"\n  DefaultTimeLimit: \"1-00:00:00\"\n  LaunchCommandSingleNode: []\n  LaunchCommandLoginNode: []\n"
  },
  {
    "path": "support/Machines/Sonic.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: Sonic\n  Description: |\n    HPC at ICTS-TIFR hosted by AstroRel.\n    More information:\n    https://it.icts.res.in/docs/sonic-cluster/\n  DefaultTasksPerNode: 3\n  DefaultProcsPerTask: 32\n  DefaultQueue: \"long\"\n  DefaultTimeLimit: \"1-00:00:00\"\n  LaunchCommandSingleNode: [\"mpirun\", \"-n\", \"1\"]\n  LaunchCommandLoginNode: []\n"
  },
  {
    "path": "support/Machines/Urania.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: Urania\n  Description: |\n    Supercomputer at the Max Planck Computing Data Facilty.\n  DefaultProcsPerNode: 72\n  DefaultQueue: \"p.urania\"\n  DefaultTimeLimit: \"1-00:00:00\"\n  LaunchCommandSingleNode: [\"srun\", \"-n\", \"1\"]\n  LaunchCommandLoginNode: []\n"
  },
  {
    "path": "support/Machines/Viper.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nMachine:\n  Name: Viper\n  Description: |\n    Supercomputer at the Max Planck Computing Data Facilty.\n  DefaultProcsPerNode: 128\n  DefaultQueue: \"p.general\"\n  DefaultTimeLimit: \"1-00:00:00\"\n  LaunchCommandSingleNode: [\"srun\", \"-n\", \"1\"]\n  LaunchCommandLoginNode: []\n"
  },
  {
    "path": "support/Pipelines/Bbh/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  Bbh\n  MODULE_PATH Pipelines\n  PYTHON_FILES\n  __init__.py\n  ControlId.py\n  EccentricityControl.py\n  FindHorizon.py\n  InitialData.py\n  InitialData.yaml\n  Inspiral.py\n  Inspiral.yaml\n  PostprocessId.py\n  Ringdown.py\n  Ringdown.yaml\n)\n\n# Create a target to compile all executables for this pipeline\nset(PIPELINE_TARGET bbh)\nadd_custom_target(${PIPELINE_TARGET})\nadd_dependencies(\n  ${PIPELINE_TARGET}\n  EvolveGhBinaryBlackHole\n  EvolveGhSingleBlackHole\n  SolveXcts\n  )\n"
  },
  {
    "path": "support/Pipelines/Bbh/ControlId.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\nfrom typing import Dict, List, Literal, Optional, Sequence, Union\n\nimport h5py\nimport numpy as np\nimport yaml\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5 import to_dataframe\nfrom spectre.Pipelines.Bbh.InitialData import TargetParams, generate_id\n\nlogger = logging.getLogger(__name__)\n\n# The default tolerance is just below the effect of junk radiation on the\n# controlled paramaters, which  is about 1.0e-3.\nDEFAULT_RESIDUAL_TOLERANCE = 1.0e-4\nDEFAULT_MAX_ITERATIONS = 30\nDEFAULT_CONTROL_DELAY = 2\n\n# Free data choices associated with each physical parameter\n# Note 1: the values below need to match the argument names of `generate_id`.\n# Note 2: conformal_mass_a/b and conformal_spin_a/b refer to the Kerr masses and\n#         spins used in the background.\nFreeDataFromParams: Dict[TargetParams, str] = {\n    \"MassA\": \"conformal_mass_a\",\n    \"MassB\": \"conformal_mass_b\",\n    \"DimensionlessSpinA\": \"horizon_rotation_a\",\n    \"DimensionlessSpinB\": \"horizon_rotation_b\",\n    \"CenterOfMass\": \"center_of_mass_offset\",\n    \"AdmLinearMomentum\": \"linear_velocity\",\n    \"AdmMass\": \"radial_expansion_velocity\",\n    \"AdmAngularMomentumZ\": \"orbital_angular_velocity\",\n}\n\n# Quantites (free data or parameters) that are scalars\n# Note: this is useful for switching between dictionaries and arrays below.\nScalarQuantities = [\n    \"MassA\",\n    \"MassB\",\n    \"conformal_mass_a\",\n    \"conformal_mass_b\",\n    \"AdmMass\",\n    \"AdmAngularMomentumZ\",\n    \"radial_expansion_velocity\",\n    \"orbital_angular_velocity\",\n]\n\n\ndef control_id(\n    id_input_file_path: Union[str, Path],\n    control_params: List[TargetParams],\n    id_run_dir: Optional[Union[str, Path]] = None,\n    residual_tolerance: float = DEFAULT_RESIDUAL_TOLERANCE,\n    max_iterations: int = DEFAULT_MAX_ITERATIONS,\n    control_delay: int = DEFAULT_CONTROL_DELAY,\n    refinement_level: int = 1,\n    polynomial_order: int = 6,\n    negative_expansion_bc: bool = True,\n):\n    \"\"\"Control BBH physical parameters.\n\n    This function is called after initial data has been generated and horizons\n    have been found in 'PostprocessId.py'. It uses an iterative scheme to drive\n    the black hole physical parameters (e.g., masses and spins) closer to the\n    desired values.\n\n    For each iteration, this function does the following:\n\n    - Determine new guesses for ID input parameters.\n\n    - Generate initial data using these guesses and post-process it.\n\n    - Compute the difference between the measured physical parameters and their\n      desired values.\n\n    Supported control parameters:\n      MassA: Mass of the larger black hole.\n      MassB: Mass of the smaller black hole.\n      DimensionlessSpinA: Dimensionless spin of the larger black hole.\n      DimensionlessSpinB: Dimensionless spin of the smaller black hole.\n      CenterOfMass: Center of mass integral in general relativity.\n      AdmLinearMomentum: ADM linear momentum.\n      AdmMass: ADM mass / energy (useful for hyperbolic encounters).\n      AdmAngularMomentumZ: ADM angular momentum along the z-axis (useful for\n        hyperbolic encounters).\n\n    A subset of these parameters can be chosen as the 'control_params'. The\n    input file metadata must contain a 'TargetParams' dictionary with the\n    corresponding target values.\n    Example of control_params for an equal-mass non-spinning run with minimal\n    drift of the center of mass:\n    ```yaml\n    TargetParams:\n        MassA: 0.5\n        MassB: 0.5\n        DimensionlessSpinA: [0., 0., 0.]\n        DimensionlessSpinB: [0., 0., 0.]\n        CenterOfMass: [0., 0., 0.]\n        AdmLinearMomentum: [0., 0., 0.]\n    ```\n\n    Arguments:\n      control_params: List of parameters to control.\n      id_input_file_path: Path to the input file of the first initial data run.\n      id_run_dir: Directory of the first initial data run. If not provided, the\n        directory of the input file is used.\n      residual_tolerance: Residual tolerance used for termination condition.\n        (Default: 1.e-4)\n      max_iterations: Maximum of iterations allowed. Note: each iteration is\n        very expensive as it needs to solve an entire initial data problem.\n        (Default: 30)\n      control_delay: Number of iterations before control of delayed parameters\n        starts. We have found that delaying the control of asymptotic quantities\n        (ADM mass, ADM momenta, and center of mass) helps convergence.\n        (Default: 2)\n      refinement_level: h-refinement used in control loop.\n      polynomial_order: p-refinement used in control loop.\n      negative_expansion_bc: Place the excisions inside of apparent horizons.\n    \"\"\"\n\n    assert (\n        len(control_params) > 0\n    ), \"At least one control parameter must be specified.\"\n\n    # Read input file\n    if id_run_dir is None:\n        id_run_dir = Path(id_input_file_path).resolve().parent\n    with open(id_input_file_path, \"r\") as open_input_file:\n        id_metadata, id_input_file = yaml.safe_load_all(open_input_file)\n    target_params = id_metadata[\"TargetParams\"]\n    binary_data = id_input_file[\"Background\"][\"Binary\"]\n    domain_data = id_input_file[\"DomainCreator\"][\"BinaryCompactObject\"]\n\n    # Get initial xyz offset\n    # Note: CenterOfMassOffset contains only the yz offsets, so we need to get\n    # the x offset from XCoords\n    x_B, x_A = binary_data[\"XCoords\"]\n    separation = x_A - x_B\n    Newtonian_x_A = (\n        target_params[\"MassB\"]\n        / (target_params[\"MassA\"] + target_params[\"MassB\"])\n        * separation\n    )\n    x_offset = x_A - Newtonian_x_A\n    y_offset, z_offset = binary_data[\"CenterOfMassOffset\"]\n\n    # Get initial horizon rotations\n    orbital_angular_velocity = binary_data[\"AngularVelocity\"]\n    horizon_rotation_a = domain_data[\"ObjectA\"][\"Interior\"][\n        \"ExciseWithBoundaryCondition\"\n    ][\"ApparentHorizon\"][\"Rotation\"]\n    horizon_rotation_a[2] -= orbital_angular_velocity\n    horizon_rotation_b = domain_data[\"ObjectB\"][\"Interior\"][\n        \"ExciseWithBoundaryCondition\"\n    ][\"ApparentHorizon\"][\"Rotation\"]\n    horizon_rotation_b[2] -= orbital_angular_velocity\n\n    # Combine initial choices of free data in a dictionary\n    initial_free_data = dict(\n        conformal_mass_a=binary_data[\"ObjectRight\"][\"KerrSchild\"][\"Mass\"],\n        conformal_mass_b=binary_data[\"ObjectLeft\"][\"KerrSchild\"][\"Mass\"],\n        horizon_rotation_a=horizon_rotation_a,\n        horizon_rotation_b=horizon_rotation_b,\n        center_of_mass_offset=[x_offset, y_offset, z_offset],\n        linear_velocity=binary_data[\"LinearVelocity\"],\n        radial_expansion_velocity=binary_data[\"Expansion\"],\n        orbital_angular_velocity=orbital_angular_velocity,\n    )\n\n    # Prepare file and legends to output diagnostic data\n    output_filename = f\"{id_run_dir}/../ControlParams.h5\"\n    residual_legend = []\n    jacobian_legend = []\n    for param in control_params:\n        if param in ScalarQuantities:\n            residual_legend.append(param)\n        else:\n            residual_legend.extend([f\"{param}_{xyz}\" for xyz in \"xyz\"])\n    for residual in residual_legend:\n        for free_data in [\n            FreeDataFromParams[param] for param in control_params\n        ]:\n            if free_data in ScalarQuantities:\n                jacobian_legend.append(f\"d{residual} / d{free_data}\")\n            else:\n                jacobian_legend.extend(\n                    [f\"d{residual} / d{free_data}_{xyz}\" for xyz in \"xyz\"]\n                )\n\n    iteration = 0\n    control_run_dir = id_run_dir\n\n    # Function to be minimized\n    def Residual(u):\n        nonlocal iteration\n        nonlocal control_run_dir\n\n        if iteration > 0:\n            logger.info(\n                \"\\n\"\n                \"==========================================\"\n                f\" Control of BBH Parameters ({iteration}) \"\n                \"==========================================\"\n            )\n            control_run_dir = f\"{id_run_dir}/../ControlParams_{iteration:03}\"\n\n            # Start with initial free data choices and update the ones being\n            # controlled in `control_params` with the numeric value from `u`\n            free_data = initial_free_data.copy()\n            u_iterator = iter(u)\n            for key in [FreeDataFromParams[param] for param in control_params]:\n                if key in ScalarQuantities:\n                    free_data[key] = next(u_iterator)\n                else:\n                    free_data[key] = [next(u_iterator) for _ in range(3)]\n\n            # Run ID and find horizons\n            generate_id(\n                target_params,\n                **free_data,\n                separation=separation,\n                run_dir=control_run_dir,\n                control=False,\n                evolve=False,\n                scheduler=None,\n                refinement_level=refinement_level,\n                polynomial_order=polynomial_order,\n                negative_expansion_bc=negative_expansion_bc,\n            )\n\n        # Initialize dictionary to hold the measured physical parameters\n        measured_params: Dict[TargetParams, Union[float, Sequence[float]]] = {}\n\n        # Get black hole physical parameters\n        with spectre_h5.H5File(\n            f\"{control_run_dir}/Horizons.h5\", \"r\"\n        ) as horizons_file:\n            AhA_quantities = to_dataframe(\n                horizons_file.get_dat(\"AhA.dat\")\n            ).iloc[-1]\n\n            if \"MassA\" in control_params:\n                measured_params[\"MassA\"] = AhA_quantities[\"ChristodoulouMass\"]\n            if \"DimensionlessSpinA\" in control_params:\n                measured_params[\"DimensionlessSpinA\"] = np.array(\n                    [\n                        AhA_quantities[\"DimensionlessSpinVector_x\"],\n                        AhA_quantities[\"DimensionlessSpinVector_y\"],\n                        AhA_quantities[\"DimensionlessSpinVector_z\"],\n                    ]\n                )\n\n            horizons_file.close_current_object()\n            AhB_quantities = to_dataframe(\n                horizons_file.get_dat(\"AhB.dat\")\n            ).iloc[-1]\n\n            if \"MassB\" in control_params:\n                measured_params[\"MassB\"] = AhB_quantities[\"ChristodoulouMass\"]\n            if \"DimensionlessSpinB\" in control_params:\n                measured_params[\"DimensionlessSpinB\"] = np.array(\n                    [\n                        AhB_quantities[\"DimensionlessSpinVector_x\"],\n                        AhB_quantities[\"DimensionlessSpinVector_y\"],\n                        AhB_quantities[\"DimensionlessSpinVector_z\"],\n                    ]\n                )\n\n        # Get ADM integrals\n        with spectre_h5.H5File(\n            f\"{control_run_dir}/BbhReductions.h5\", \"r\"\n        ) as reductions_file:\n            adm_integrals = to_dataframe(\n                reductions_file.get_dat(\"AdmIntegrals.dat\")\n            ).iloc[-1]\n\n            if \"CenterOfMass\" in control_params:\n                measured_params[\"CenterOfMass\"] = np.array(\n                    [\n                        adm_integrals[\"CenterOfMass_x\"],\n                        adm_integrals[\"CenterOfMass_y\"],\n                        adm_integrals[\"CenterOfMass_z\"],\n                    ]\n                )\n            if \"AdmLinearMomentum\" in control_params:\n                measured_params[\"AdmLinearMomentum\"] = np.array(\n                    [\n                        adm_integrals[\"AdmLinearMomentum_x\"],\n                        adm_integrals[\"AdmLinearMomentum_y\"],\n                        adm_integrals[\"AdmLinearMomentum_z\"],\n                    ]\n                )\n            if \"AdmMass\" in control_params:\n                measured_params[\"AdmMass\"] = adm_integrals[\"AdmMass\"]\n            if \"AdmAngularMomentumZ\" in control_params:\n                measured_params[\"AdmAngularMomentumZ\"] = adm_integrals[\n                    \"AdmAngularMomentum_z\"\n                ]\n\n        # Compute residual of physical parameters\n        residual = np.array([])\n        for key in control_params:\n            target = target_params[key]\n            assert target is not None, (\n                f\"Attempting to control parameter '{key}' but no target value\"\n                \" is provided.\"\n            )\n            if key in ScalarQuantities:\n                residual = np.append(residual, [measured_params[key] - target])\n            else:\n                residual = np.append(residual, measured_params[key] - target)\n        logger.info(f\"Control Residual = {np.max(np.abs(residual)):e}\")\n        with spectre_h5.H5File(output_filename, \"a\") as output_file:\n            dat_file = output_file.try_insert_dat(\n                \"Residuals\", residual_legend, 0\n            )\n            dat_file.append(residual)\n\n        return residual\n\n    # Initial guess for free data\n    u = np.array([])\n    for key in [FreeDataFromParams[param] for param in control_params]:\n        if key in ScalarQuantities:\n            u = np.append(u, [initial_free_data[key]])\n        else:\n            u = np.append(u, initial_free_data[key])\n\n    # Initial residual\n    F = Residual(u)\n\n    # Initialize Jacobian as an identity matrix\n    J = np.identity(len(u))\n\n    # Prepare map between parameters and their indices so that we can specify\n    # Jacobian terms below\n    param_index_map = dict()\n    param_index = 0\n    for param in control_params:\n        param_index_map[param] = param_index\n        param_index_map[FreeDataFromParams[param]] = param_index\n        param_index += 1 if param in ScalarQuantities else 3\n\n    # Adjust non-unity components of the Jacobian\n    #\n    # The expressions below come from differentiating the Kerr expressions\n    # chi = - 2 r Omega and r = M (1 + sqrt(1 - chi^2)), where chi is the\n    # dimensionless spin, r is the horizon radius, Omega is the horizon\n    # rotation, and M is the mass.\n    for spin_key, mass_key, horizon_rotation in zip(\n        [\"DimensionlessSpinA\", \"DimensionlessSpinB\"],\n        [\"conformal_mass_a\", \"conformal_mass_b\"],\n        [horizon_rotation_a, horizon_rotation_b],\n    ):\n        conformal_mass = u[param_index_map[mass_key]]\n        conformal_spin = target_params[spin_key]\n        spin_term = 1.0 + np.sqrt(1 - np.dot(conformal_spin, conformal_spin))\n        for i in range(3):\n            J[\n                param_index_map[spin_key] + i,\n                param_index_map[mass_key],\n            ] = (\n                -2.0 * horizon_rotation[i] * spin_term\n            )\n            J[\n                param_index_map[spin_key] + i,\n                param_index_map[spin_key] + i,\n            ] = (\n                -2.0 * conformal_mass * spin_term\n            )\n    # The expression below is the reduced mass of the system, which shows up in\n    # the Newtonian expressions further below.\n    q = target_params[\"MassRatio\"]\n    eta = q / (q + 1) ** 2\n    # The expressions below come from differentiating the Newtonian\n    # approximation E_ADM ~ M + 1/2 eta adot0^2 D0^2, where adot0 is the\n    # initial radial expansion velocity and D0 is the initial\n    # separation. They are evaluated under the conditions M=1 and q=MA/MB, which\n    # holds (at least approximately) for the initial guess of the free data.\n    if \"AdmMass\" in control_params:\n        adot0 = initial_free_data[\"radial_expansion_velocity\"]\n        J[\n            param_index_map[\"AdmMass\"],\n            param_index_map[\"radial_expansion_velocity\"],\n        ] = (\n            eta * adot0 * separation**2\n        )\n        if \"MassA\" in control_params:\n            J[\n                param_index_map[\"AdmMass\"], param_index_map[\"conformal_mass_a\"]\n            ] = (1.0 + 0.5 * eta / q * adot0**2 * separation**2)\n        if \"MassB\" in control_params:\n            J[\n                param_index_map[\"AdmMass\"], param_index_map[\"conformal_mass_b\"]\n            ] = (1.0 + 0.5 * q * eta * adot0**2 * separation**2)\n    # The expressions below come from differentiating the Newtonian\n    # approximation J_ADM ~ eta D0^2 Omega0, where D0 is the initial\n    # separation and Omega0 is the initial angular orbital velocity.\n    if \"AdmAngularMomentumZ\" in control_params:\n        OmegaZ0 = initial_free_data[\"orbital_angular_velocity\"]\n        J[\n            param_index_map[\"AdmAngularMomentumZ\"],\n            param_index_map[\"orbital_angular_velocity\"],\n        ] = (\n            eta * separation**2\n        )\n        if \"MassA\" in control_params:\n            J[\n                param_index_map[\"AdmAngularMomentumZ\"],\n                param_index_map[\"conformal_mass_a\"],\n            ] = (\n                eta / q * separation**2 * OmegaZ0\n            )\n        if \"MassB\" in control_params:\n            J[\n                param_index_map[\"AdmAngularMomentumZ\"],\n                param_index_map[\"conformal_mass_b\"],\n            ] = (\n                q * eta * separation**2 * OmegaZ0\n            )\n    with spectre_h5.H5File(output_filename, \"a\") as output_file:\n        dat_file = output_file.try_insert_dat(\"Jacobian\", jacobian_legend, 0)\n        dat_file.append(J.flatten())\n\n    # Indices of parameters for which the control is delayed in the first\n    # iterations to avoid going off-bounds\n    #\n    # Note: We have experimented with other modifications to Broyden's\n    # method, including damping the initial updates of the free data / Jacobian\n    # and enforcing a diagonal Jacobian. None of them converged as fast as the\n    # delay approach used here. When doing a more complete study in parameter\n    # space, we should try to find a more robust approach that works for\n    # multiple configurations.\n    delayed_indices = np.array([], dtype=bool)\n    delayed_params = [\n        \"CenterOfMass\",\n        \"AdmLinearMomentum\",\n        \"AdmMass\",\n        \"AdmAngularMomentumZ\",\n    ]\n    for key in control_params:\n        if key in ScalarQuantities:\n            delayed_indices = np.append(\n                delayed_indices, [key in delayed_params]\n            )\n        else:\n            delayed_indices = np.append(\n                delayed_indices, [key in delayed_params] * 3\n            )\n\n    while iteration < max_iterations:\n        iteration += 1\n\n        # Update the free parameters using a quasi-Newton-Raphson method\n        Delta_u = -np.dot(np.linalg.inv(J), F)\n        if iteration < control_delay:\n            Delta_u[delayed_indices] = 0.0\n\n        u += Delta_u\n\n        # Compute residual and check stopping condition\n        F = Residual(u)\n        if np.max(np.abs(F)) < residual_tolerance:\n            break\n        if iteration < control_delay:\n            F[delayed_indices] = 0.0\n\n        # Update the Jacobian using Broyden's method\n        J += np.outer(F, Delta_u) / np.dot(Delta_u, Delta_u)\n        with spectre_h5.H5File(output_filename, \"a\") as output_file:\n            dat_file = output_file.try_insert_dat(\n                \"Jacobian\", jacobian_legend, 0\n            )\n            dat_file.append(J.flatten())\n\n    return control_run_dir\n"
  },
  {
    "path": "support/Pipelines/Bbh/EccentricityControl.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\nfrom typing import Optional, Sequence, Union\n\nimport click\nimport pandas as pd\nimport yaml\n\nfrom spectre.Pipelines.Bbh.InitialData import generate_id\nfrom spectre.Pipelines.Bbh.Inspiral import (\n    INSPIRAL_INPUT_FILE_TEMPLATE,\n    start_inspiral,\n)\nfrom spectre.Pipelines.EccentricityControl.EccentricityControlParams import (\n    eccentricity_control_params,\n    eccentricity_control_params_options,\n)\nfrom spectre.support.DirectoryStructure import PipelineStep, list_pipeline_steps\nfrom spectre.support.Schedule import scheduler_options\n\nlogger = logging.getLogger(__name__)\n\n\ndef eccentricity_control(\n    h5_files: Union[Union[str, Path], Sequence[Union[str, Path]]],\n    id_input_file_path: Union[str, Path],\n    pipeline_dir: Union[str, Path],\n    # Eccentricity control parameters\n    tmin: Optional[float] = None,\n    tmax: Optional[float] = None,\n    plot_output_dir: Optional[Union[str, Path]] = None,\n    ecc_params_output_file: Optional[Union[str, Path]] = None,\n    # Options for continuing the evolution\n    evolve: bool = True,\n    branch_levs_when_complete: Optional[Sequence[int]] = None,\n    inspiral_input_file_path: Optional[Union[str, Path]] = None,\n    inspiral_run_dir: Optional[Union[str, Path]] = None,\n    inspiral_input_file_template: Union[\n        str, Path\n    ] = INSPIRAL_INPUT_FILE_TEMPLATE,\n    # Scheduler options\n    **scheduler_kwargs,\n):\n    \"\"\"Adjust orbital parameters for eccentricity control.\n\n    This function can be called after the inspiral has run (see the 'Next'\n    section of the Inspiral.yaml file).\n\n    This function does the following:\n\n    - Reads orbital parameters from the 'id_input_file_path'.\n\n    - Get the new orbital parameters by calling the function\n      'eccentricity_control_params' in\n      'spectre.Pipelines.EccentricityControl.EccentricityControl'.\n      See this function for default values and more details on the arguments.\n\n    - If the eccentricity is below an absolute tolerance, continue the\n      evolution.\n\n    - Generates new initial data based on updated orbital parameters using the\n      'generate_id' function.\n\n    Arguments:\n      h5_files: files that contain the trajectory data\n      id_input_file_path: path to the input file of the initial data run\n      pipeline_dir: directory where the pipeline outputs are stored.\n      evolve: Evolve the initial data after generation to continue eccentricity\n        control. You can disable this to generate only the new initial data if\n        you want to manually start the next inspiral.\n      branch_levs_when_complete: Optional list of levs to start when\n        eccentricity control is complete. Each lev will run in a separate\n        subdirectory. If no levs are specified, the simulation will just stop\n        after eccentricity control. See `Inspiral.INSPIRAL_LEVS` for the\n        definition of the levs.\n      inspiral_input_file_path: Path to the input file for the inspiral run.\n        Required only if `branch_levs_when_complete` is specified, as the\n        simulation will continue from this data.\n      inspiral_run_dir: Directory where the inspiral run was executed.\n        Defaults to the directory of the `inspiral_input_file_path`.\n      inspiral_input_file_template: Input file template to start the\n        `branch_levs_when_complete`. Defaults to the\n        `INSPIRAL_INPUT_FILE_TEMPLATE`.\n\n    See the 'eccentricity_control_params' function for details on the other\n    arguments, as well as the 'schedule' function for the scheduling options.\n    \"\"\"\n    # Read and process the initial data input file\n    with open(id_input_file_path, \"r\") as open_input_file:\n        id_metadata, id_input_file = yaml.safe_load_all(open_input_file)\n    target_params = id_metadata[\"TargetParams\"]\n    assert (\n        target_params[\"Eccentricity\"] is not None\n        and target_params[\"EccentricityAbsoluteTolerance\"] is not None\n    ), (\n        \"For eccentricity control the target eccentricity and its tolerance\"\n        \" must be set.\"\n    )\n\n    # Find the current eccentricity and determine new parameters to put into\n    # generate-id\n    ecc_params = eccentricity_control_params(\n        h5_files,\n        id_input_file_path,\n        tmin=tmin,\n        tmax=tmax,\n        plot_output_dir=plot_output_dir,\n        ecc_params_output_file=ecc_params_output_file,\n    )\n\n    # Continue the evolution if eccentricity is below threshold\n    if (\n        abs(ecc_params[\"Eccentricity\"] - target_params[\"Eccentricity\"])\n        <= target_params[\"EccentricityAbsoluteTolerance\"]\n    ):\n        logger.info(\"Eccentricity control complete.\")\n        if branch_levs_when_complete:\n            # Continue inspiral in a subdirectory for each lev\n            for lev in branch_levs_when_complete:\n                lev_label = f\"Lev{lev}\"\n                pipeline_steps = list_pipeline_steps(pipeline_dir)\n                lev_dir = (\n                    pipeline_steps[-1].next(label=lev_label)\n                    if pipeline_steps\n                    else PipelineStep.first(pipeline_dir, label=lev_label)\n                )\n                start_inspiral(\n                    # Start from inspiral data\n                    id_input_file_path=inspiral_input_file_path,\n                    id_run_dir=inspiral_run_dir,\n                    id_subfile_name=\"PostJunkVolumeData\",\n                    lev=lev,\n                    inspiral_input_file_template=inspiral_input_file_template,\n                    continue_with_ringdown=True,\n                    pipeline_dir=lev_dir.path,\n                    **scheduler_kwargs,\n                )\n        return\n\n    # Generate new initial data based on updated orbital parameters\n    id_params = id_metadata[\"Next\"][\"With\"]\n    binary_data = id_input_file[\"Background\"][\"Binary\"]\n    x_B, x_A = binary_data[\"XCoords\"]\n    separation = x_A - x_B\n    x_offset = x_A - target_params[\"MassB\"] * separation\n    y_offset, z_offset = binary_data[\"CenterOfMassOffset\"]\n    binary_domain = id_input_file[\"DomainCreator\"][\"BinaryCompactObject\"]\n    generate_id(\n        target_params,\n        # New orbital parameters\n        separation=separation,\n        orbital_angular_velocity=ecc_params[\"NewOmega0\"],\n        radial_expansion_velocity=ecc_params[\"NewAdot0\"],\n        # Initial guesses for ID control\n        conformal_mass_a=binary_data[\"ObjectRight\"][\"KerrSchild\"][\"Mass\"],\n        conformal_mass_b=binary_data[\"ObjectLeft\"][\"KerrSchild\"][\"Mass\"],\n        horizon_rotation_a=binary_domain[\"ObjectA\"][\"Interior\"][\n            \"ExciseWithBoundaryCondition\"\n        ][\"ApparentHorizon\"][\"Rotation\"],\n        horizon_rotation_b=binary_domain[\"ObjectB\"][\"Interior\"][\n            \"ExciseWithBoundaryCondition\"\n        ][\"ApparentHorizon\"][\"Rotation\"],\n        center_of_mass_offset=[x_offset, y_offset, z_offset],\n        linear_velocity=binary_data[\"LinearVelocity\"],\n        # Scheduling options\n        refinement_level=id_params[\"control_refinement_level\"],\n        polynomial_order=id_params[\"control_polynomial_order\"],\n        control=True,\n        evolve=evolve,\n        eccentricity_control=True,\n        pipeline_dir=pipeline_dir,\n        **scheduler_kwargs,\n    )\n\n\n@click.command(name=\"eccentricity-control\", help=eccentricity_control.__doc__)\n@eccentricity_control_params_options\n@click.option(\n    \"--pipeline-dir\",\n    \"-d\",\n    type=click.Path(\n        writable=True,\n        path_type=Path,\n    ),\n    help=\"Directory where steps in the pipeline are created.\",\n)\n@click.option(\n    \"--evolve/--no-evolve\",\n    default=True,\n    show_default=True,\n    help=(\n        \"Evolve the initial data after generation to continue eccentricity \"\n        \"control. You can disable this to generate only the new initial data \"\n        \"if you want to manually start the next inspiral.\"\n    ),\n)\n@scheduler_options\ndef eccentricity_control_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    eccentricity_control(**kwargs)\n"
  },
  {
    "path": "support/Pipelines/Bbh/FindHorizon.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport glob\nimport logging\nfrom pathlib import Path\nfrom typing import Optional, Sequence, Type, Union\n\nimport click\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.ApparentHorizonFinder import FastFlow, FlowType, Status\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import tnsr\nfrom spectre.Domain import (\n    deserialize_domain,\n    deserialize_functions_of_time,\n    strahlkorper_in_inertial_frame_aligned,\n)\nfrom spectre.IO.Exporter import ObservationId, interpolate_tensors_to_points\nfrom spectre.IO.H5 import (\n    list_observations,\n    open_volfiles,\n    open_volfiles_command,\n)\nfrom spectre.PointwiseFunctions.GeneralRelativity.Surfaces import (\n    horizon_quantities,\n)\nfrom spectre.Spectral import Basis, Quadrature\nfrom spectre.SphericalHarmonics import (\n    Frame,\n    Strahlkorper,\n    cartesian_coords,\n    ylm_legend_and_data,\n)\n\nlogger = logging.getLogger(__name__)\n\n\ndef _strahlkorper_vol_data(strahlkorper):\n    \"\"\"Volume data representation of a Strahlkorper, can be written to H5.\"\"\"\n    coords = cartesian_coords(strahlkorper)\n    return [\n        spectre_h5.ElementVolumeData(\n            element_name=\"Horizon\",\n            components=[\n                spectre_h5.TensorComponent(\n                    \"InertialCoordinates_\" + \"xyz\"[d], coords[d]\n                )\n                for d in range(3)\n            ],\n            extents=strahlkorper.physical_extents,\n            basis=2 * [Basis.SphericalHarmonic],\n            quadrature=[\n                Quadrature.Gauss,\n                Quadrature.Equiangular,\n            ],\n        )\n    ]\n\n\ndef _horizon_reduction_data(quantities):\n    \"\"\"Row of horizon quantities, can be written to an H5 dat file.\"\"\"\n    # Flatten vector quantities into a single row of values. So the spin is\n    # returned as 3 columns with the x, y, and z components.\n    legend = []\n    reduction_data = []\n    for key, value in quantities.items():\n        if isinstance(value, float):\n            legend.append(key)\n            reduction_data.append(value)\n        elif len(value) == 3:\n            legend.extend([f\"{key}_{xyz}\" for xyz in \"xyz\"])\n            reduction_data.extend(value)\n        else:\n            raise ValueError(\n                f\"Unsupported shape of horizon quantity {key}: {value}\"\n            )\n    return legend, reduction_data\n\n\ndef vec_to_string(vec):\n    \"\"\"Convert a vector to a (readable) string.\"\"\"\n    return f\"[{', '.join(f'{v:g}' for v in vec)}]\"\n\n\ndef find_horizon(\n    h5_files: Union[str, Sequence[str]],\n    subfile_name: str,\n    obs_id: int,\n    obs_time: float,\n    initial_guess: Strahlkorper[Frame.Inertial],\n    fast_flow: Optional[FastFlow] = None,\n    output_surfaces_file: Optional[Union[str, Path]] = None,\n    output_coeffs_subfile: Optional[str] = None,\n    output_coords_subfile: Optional[str] = None,\n    output_reductions_file: Optional[Union[str, Path]] = None,\n    output_quantities_subfile: Optional[str] = None,\n    output_l_max: Optional[int] = None,\n    tensor_names: Optional[Sequence[str]] = None,\n):\n    \"\"\"Find an apparent horizon in volume data.\n\n    The volume data must contain the spatial metric, inverse spatial metric,\n    extrinsic curvature, spatial Christoffel symbols, and spatial Ricci tensor.\n    The data is assumed to be in the \"inertial\" frame.\n\n    Arguments:\n      h5_files: List of H5 files containing volume data or glob pattern.\n      subfile_name: Name of the volume data subfile in the 'h5_files'.\n      obs_id: Observation ID in the volume data.\n      obs_time: Time of the observation.\n      initial_guess: Initial guess for the horizon. Specify a\n        'spectre.SphericalHarmonics.Strahlkorper[Frame.Inertial]'.\n      fast_flow: Optional. FastFlow object that controls the horizon finder.\n        If not specified, a FastFlow object with default parameters is used.\n      output_surfaces_file: Optional. H5 output file where the horizon Ylm\n        coefficients will be written. Can be a new or existing file. Requires\n        either 'output_coeffs_subfile' or 'output_coords_subfile' is also\n        specified, or both.\n      output_coeffs_subfile: Optional. Name of the subfile in the\n        'output_surfaces_file' where the horizon Ylm coefficients will be\n        written. These can be used to reconstruct the horizon, e.g. to\n        initialize excisions in domains.\n      output_coords_subfile: Optional. Name of the subfile in the\n        'output_surfaces_file' where the horizon coordinates will be written.\n        These can be used for visualization.\n      output_reductions_file: Optional. H5 output file where the reduction\n        quantities on the horizon will be written, e.g. masses and spins.\n        Can be a new or existing file. Requires 'output_quantities_subfile'\n        is also specified.\n      output_quantities_subfile: Optional. Name of the subfile in the\n        'output_reductions_file' where the horizon quantities will be written,\n        e.g. masses and spins.\n      output_l_max: Optional. Maximum l-mode for the horizon Ylm coefficients\n        written to the output file. Defaults to the l_max of the initial guess.\n        Only used if 'output_coeffs_subfile' is specified.\n      tensor_names: Optional. List of tensor names in the volume data that\n        represent the spatial metric, inverse spatial metric, extrinsic\n        curvature, spatial Christoffel symbols, and spatial Ricci tensor, in\n        this order. Defaults to [\"SpatialMetric\", \"InverseSpatialMetric\",\n        \"ExtrinsicCurvature\", \"SpatialChristoffelSecondKind\", \"SpatialRicci\"].\n\n    Returns: The Strahlkorper representing the horizon, and a dictionary of\n      horizon quantities (e.g. area, mass, spin, etc.).\n    \"\"\"\n    # Validate input arguments\n    if output_surfaces_file:\n        assert output_coeffs_subfile or output_coords_subfile, (\n            \"Specify either 'output_coeffs_subfile' or 'output_coords_subfile'\"\n            \" or both.\"\n        )\n    if not tensor_names:\n        tensor_names = [\n            \"SpatialMetric\",\n            \"InverseSpatialMetric\",\n            \"ExtrinsicCurvature\",\n            \"SpatialChristoffelSecondKind\",\n            \"SpatialRicci\",\n        ]\n    if fast_flow is None:\n        fast_flow = FastFlow(\n            FlowType.Fast,\n            alpha=1.0,\n            beta=0.5,\n            abs_tol=1e-12,\n            truncation_tol=0.01,\n            divergence_tol=1.2,\n            divergence_iter=5,\n            max_its=300,\n        )\n    strahlkorper = initial_guess\n    while True:\n        l_mesh = fast_flow.current_l_mesh(strahlkorper)\n        prolonged_strahlkorper = Strahlkorper[Frame.Inertial](\n            l_mesh, l_mesh, strahlkorper\n        )\n        (\n            inv_spatial_metric,\n            extrinsic_curvature,\n            spatial_christoffel_second_kind,\n        ) = interpolate_tensors_to_points(\n            h5_files,\n            subfile_name,\n            observation=ObservationId(obs_id),\n            target_points=cartesian_coords(prolonged_strahlkorper),\n            tensor_names=tensor_names[1:4],\n            tensor_types=[\n                tnsr.II[DataVector, 3],\n                tnsr.ii[DataVector, 3],\n                tnsr.Ijj[DataVector, 3],\n            ],\n        )\n        status, iter_info = fast_flow.iterate_horizon_finder(\n            strahlkorper,\n            upper_spatial_metric=inv_spatial_metric,\n            extrinsic_curvature=extrinsic_curvature,\n            christoffel_2nd_kind=spatial_christoffel_second_kind,\n        )\n        logger.debug(\n            f\"Horizon finder iteration {iter_info.iteration}: {status}.\"\n            f\" {iter_info.r_min:g} <= r <= {iter_info.r_max:g},\"\n            f\" {iter_info.min_residual:.2e} <= residual <=\"\n            f\" {iter_info.max_residual:.2e}\"\n        )\n        if status == Status.SuccessfulIteration:\n            continue\n        elif int(status) > 0:\n            logger.info(\n                \"Found horizon around\"\n                f\" {vec_to_string(strahlkorper.expansion_center)} with\"\n                f\" {iter_info.r_min:g} <= r <= {iter_info.r_max:g}.\"\n            )\n            break\n        else:\n            raise RuntimeError(f\"Horizon finder failed with status {status}.\")\n    # Compute horizon quantities\n    # This is independent of the horizon find and could move into a separate\n    # function, or disabled on request, if we ever need to find a horizon\n    # without computing these quantities.\n    (\n        spatial_metric,\n        inv_spatial_metric,\n        extrinsic_curvature,\n        spatial_christoffel_second_kind,\n        spatial_ricci,\n    ) = interpolate_tensors_to_points(\n        h5_files,\n        subfile_name,\n        observation=ObservationId(obs_id),\n        target_points=cartesian_coords(strahlkorper),\n        tensor_names=tensor_names,\n        tensor_types=[\n            tnsr.ii[DataVector, 3],\n            tnsr.II[DataVector, 3],\n            tnsr.ii[DataVector, 3],\n            tnsr.Ijj[DataVector, 3],\n            tnsr.ii[DataVector, 3],\n        ],\n    )\n    quantities = horizon_quantities(\n        strahlkorper,\n        spatial_metric=spatial_metric,\n        inv_spatial_metric=inv_spatial_metric,\n        extrinsic_curvature=extrinsic_curvature,\n        spatial_christoffel_second_kind=spatial_christoffel_second_kind,\n        spatial_ricci=spatial_ricci,\n    )\n    # Write the horizon to a file and return it\n    if output_surfaces_file:\n        if Path(output_surfaces_file).suffix not in [\".h5\", \".hdf5\"]:\n            output_surfaces_file += \".h5\"\n        if output_coeffs_subfile:\n            legend, ylm_data = ylm_legend_and_data(\n                strahlkorper, obs_time, output_l_max or strahlkorper.l_max\n            )\n            with spectre_h5.H5File(\n                str(output_surfaces_file), \"a\"\n            ) as output_file:\n                datfile = output_file.try_insert_dat(\n                    output_coeffs_subfile, legend, 0\n                )\n                datfile.append(ylm_data)\n        if output_coords_subfile:\n            vol_data = _strahlkorper_vol_data(strahlkorper)\n            with spectre_h5.H5File(\n                str(output_surfaces_file), \"a\"\n            ) as output_file:\n                volfile = output_file.try_insert_vol(output_coords_subfile, 0)\n                volfile.write_volume_data(obs_id, obs_time, vol_data)\n    if output_reductions_file:\n        assert output_quantities_subfile, (\n            \"Specify 'output_quantities_subfile' if 'output_reductions_file'\"\n            \" is specified.\"\n        )\n        if Path(output_reductions_file).suffix not in [\".h5\", \".hdf5\"]:\n            output_reductions_file += \".h5\"\n        legend, reduction_data = _horizon_reduction_data(quantities)\n        with spectre_h5.H5File(str(output_reductions_file), \"a\") as output_file:\n            datfile = output_file.try_insert_dat(\n                output_quantities_subfile, legend, 0\n            )\n            datfile.append(reduction_data)\n    return strahlkorper, quantities\n\n\ndef use_excision_as_horizon(\n    h5_files: Union[str, Sequence[str]],\n    subfile_name: str,\n    obs_id: int,\n    obs_time: float,\n    l_max: int,\n    radius: float,\n    center: Sequence[float],\n    output_reductions_file: Optional[Union[str, Path]] = None,\n    output_quantities_subfile: Optional[str] = None,\n    tensor_names: Optional[Sequence[str]] = None,\n):\n    \"\"\"Use excision to compute horizon quantities.\n\n    The volume data must contain the spatial metric, inverse spatial metric,\n    extrinsic curvature, spatial Christoffel symbols, and spatial Ricci tensor.\n\n    Arguments:\n      h5_files: List of H5 files containing volume data or glob pattern.\n      subfile_name: Name of the volume data subfile in the 'h5_files'.\n      obs_id: Observation ID in the volume data.\n      obs_time: Time of the observation.\n      l_max: Maximum l-mode for the horizon Ylm representation.\n      radius: Radius of the excision.\n      center: Center of the excision.\n      output_reductions_file: Optional. H5 output file where the reduction\n        quantities on the horizon will be written, e.g. masses and spins.\n        Can be a new or existing file. Requires 'output_quantities_subfile'\n        is also specified.\n      output_quantities_subfile: Optional. Name of the subfile in the\n        'output_reductions_file' where the horizon quantities will be written,\n        e.g. masses and spins.\n      tensor_names: Optional. List of tensor names in the volume data that\n        represent the spatial metric, inverse spatial metric, extrinsic\n        curvature, spatial Christoffel symbols, and spatial Ricci tensor, in\n        this order. Defaults to [\"SpatialMetric\", \"InverseSpatialMetric\",\n        \"ExtrinsicCurvature\", \"SpatialChristoffelSecondKind\", \"SpatialRicci\"].\n\n    Returns: The Strahlkorper representing the horizon, and a dictionary of\n      horizon quantities (e.g. area, mass, spin, etc.).\n    \"\"\"\n    if not tensor_names:\n        tensor_names = [\n            \"SpatialMetric\",\n            \"InverseSpatialMetric\",\n            \"ExtrinsicCurvature\",\n            \"SpatialChristoffelSecondKind\",\n            \"SpatialRicci\",\n        ]\n\n    # Define excision in the grid frame\n    excision_grid = Strahlkorper[Frame.Grid](l_max, radius, center)\n\n    # Interpolate the tensors to the excision points\n    (\n        spatial_metric,\n        inv_spatial_metric,\n        extrinsic_curvature,\n        spatial_christoffel_second_kind,\n        spatial_ricci,\n    ) = interpolate_tensors_to_points(\n        h5_files,\n        subfile_name,\n        observation=ObservationId(obs_id),\n        target_points=cartesian_coords(excision_grid),\n        tensor_names=tensor_names,\n        tensor_types=[\n            tnsr.ii[DataVector, 3],\n            tnsr.II[DataVector, 3],\n            tnsr.ii[DataVector, 3],\n            tnsr.Ijj[DataVector, 3],\n            tnsr.ii[DataVector, 3],\n        ],\n    )\n\n    # Deserialize domain and get the excision in the inertial frame\n    for volfile in open_volfiles(h5_files, subfile_name, obs_id):\n        dim = volfile.get_dimension()\n        domain = deserialize_domain[dim](volfile.get_domain())\n        if domain.is_time_dependent():\n            functions_of_time = deserialize_functions_of_time(\n                volfile.get_functions_of_time(obs_id)\n            )\n            excision_inertial = strahlkorper_in_inertial_frame_aligned(\n                excision_grid, domain, functions_of_time, obs_time\n            )\n        else:\n            # When the domain is time-independent, the excision is the same in\n            # the grid and inertial frames.\n            excision_inertial = Strahlkorper[Frame.Inertial](\n                l_max, radius, center\n            )\n        break\n\n    # Compute horizon quantities\n    quantities = horizon_quantities(\n        excision_inertial,\n        spatial_metric=spatial_metric,\n        inv_spatial_metric=inv_spatial_metric,\n        extrinsic_curvature=extrinsic_curvature,\n        spatial_christoffel_second_kind=spatial_christoffel_second_kind,\n        spatial_ricci=spatial_ricci,\n    )\n\n    if output_reductions_file:\n        assert output_quantities_subfile, (\n            \"Specify 'output_quantities_subfile' if 'output_reductions_file'\"\n            \" is specified.\"\n        )\n        if Path(output_reductions_file).suffix not in [\".h5\", \".hdf5\"]:\n            output_reductions_file += \".h5\"\n        legend, reduction_data = _horizon_reduction_data(quantities)\n        with spectre_h5.H5File(str(output_reductions_file), \"a\") as output_file:\n            datfile = output_file.try_insert_dat(\n                output_quantities_subfile, legend, 0\n            )\n            datfile.append(reduction_data)\n\n    return excision_inertial, quantities\n\n\n@click.command(name=\"find-horizon\")\n@open_volfiles_command(\n    obs_id_required=True, multiple_vars=True, vars_required=False\n)\n@click.option(\n    \"--l-max\",\n    \"-l\",\n    type=int,\n    required=True,\n    help=\"Max l-mode for the horizon search.\",\n)\n@click.option(\n    \"--initial-radius\",\n    \"-r\",\n    type=float,\n    required=True,\n    help=\"Initial coordinate radius of the horizon.\",\n)\n@click.option(\n    \"--center\",\n    \"-C\",\n    nargs=3,\n    type=float,\n    required=True,\n    help=\"Coordinate center of the horizon.\",\n)\n@click.option(\n    \"--output-surfaces-file\",\n    type=click.Path(writable=True),\n    help=(\n        \"H5 output file where the horizon Ylm coefficients will be written. Can\"\n        \" be a new or existing file.\"\n    ),\n)\n@click.option(\n    \"--output-coeffs-subfile\",\n    help=(\n        \"Name of the subfile in the 'output_surfaces_file' where the horizon\"\n        \" Ylm coefficients will be written. These can be used to reconstruct\"\n        \" the horizon, e.g. to initialize excisions in domains.\"\n    ),\n)\n@click.option(\n    \"--output-coords-subfile\",\n    help=(\n        \"Name of the subfile in the 'output_surfaces_file' where the horizon\"\n        \" coordinates will be written. These can be used for visualization.\"\n    ),\n)\n@click.option(\n    \"--output-reductions-file\",\n    type=click.Path(writable=True),\n    help=(\n        \"H5 output file where the reduction quantities on the horizon will be\"\n        \" written, e.g. masses and spins. Can be a new or existing file.\"\n    ),\n)\n@click.option(\n    \"--output-quantities-subfile\",\n    help=(\n        \"Name of the subfile in the 'output_reductions_file' where the horizon\"\n        \" quantities will be written, e.g. masses and spins.\"\n    ),\n)\ndef find_horizon_command(l_max, initial_radius, center, vars, **kwargs):\n    \"\"\"Find an apparent horizon in volume data.\"\"\"\n    initial_guess = Strahlkorper[Frame.Inertial](\n        l_max=l_max, radius=initial_radius, center=center\n    )\n    horizon, quantities = find_horizon(\n        initial_guess=initial_guess,\n        tensor_names=vars,\n        **kwargs,\n    )\n\n    # Output horizon quantities\n    import rich.table\n\n    table = rich.table.Table(show_header=False, box=None)\n    for name, value in quantities.items():\n        table.add_row(\n            name,\n            (\n                f\"{value:g}\"\n                if isinstance(value, float)\n                else vec_to_string(value)\n            ),\n        )\n    rich.print(table)\n\n\nif __name__ == \"__main__\":\n    find_horizon_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Pipelines/Bbh/InitialData.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\nfrom typing import Dict, Literal, Optional, Sequence, Union\n\nimport click\nimport numpy as np\nimport yaml\nfrom rich.pretty import pretty_repr\n\nfrom spectre.Pipelines.EccentricityControl.InitialOrbitalParameters import (\n    initial_orbital_parameters,\n)\nfrom spectre.support.DirectoryStructure import PipelineStep, list_pipeline_steps\nfrom spectre.support.Schedule import schedule, scheduler_options\n\nlogger = logging.getLogger(__name__)\n\nID_INPUT_FILE_TEMPLATE = Path(__file__).parent / \"InitialData.yaml\"\n\nTargetParams = Literal[\n    \"MassRatio\",\n    \"MassA\",\n    \"MassB\",\n    \"DimensionlessSpinA\",\n    \"DimensionlessSpinB\",\n    \"CenterOfMass\",\n    \"AdmLinearMomentum\",\n    \"AdmMass\",\n    \"AdmAngularMomentumZ\",\n    \"Eccentricity\",\n    \"EccentricityAbsoluteTolerance\",\n    \"MeanAnomalyFraction\",\n    \"NumOrbits\",\n    \"TimeToMerger\",\n    \"EvolutionLev\",\n]\n\nDEFAULT_TARGET_PARAMS: Dict[TargetParams, float] = {\n    \"EccentricityAbsoluteTolerance\": 1e-3,\n}\n\n\ndef L1_distance(m1, m2, separation):\n    \"\"\"Distance of the L1 Lagrangian point from m1, in Newtonian gravity\"\"\"\n    return separation * (0.5 - 0.227 * np.log10(m2 / m1))\n\n\ndef id_parameters(\n    conformal_mass_a: float,\n    conformal_mass_b: float,\n    horizon_rotation_a: Sequence[float],\n    horizon_rotation_b: Sequence[float],\n    center_of_mass_offset: Sequence[float],\n    linear_velocity: Sequence[float],\n    separation: float,\n    orbital_angular_velocity: float,\n    radial_expansion_velocity: float,\n    refinement_level: int,\n    polynomial_order: int,\n    negative_expansion_bc: bool,\n    target_params: Dict[TargetParams, Union[float, Sequence[float]]],\n):\n    \"\"\"Determine initial data parameters from options.\n\n    These parameters fill the 'ID_INPUT_FILE_TEMPLATE'.\n\n    Arguments:\n      conformal_mass_a: Mass parameter of the larger black hole.\n      conformal_mass_b: Mass parameter of the smaller black hole.\n      horizon_rotation_a: Rotation parameter of the larger black hole, Omega_A.\n      horizon_rotation_b: Rotation parameter of the smaller black hole, Omega_B.\n      center_of_mass_offset: Offset from the Newtonian center of mass.\n      linear_velocity: Velocity added to the shift boundary condition.\n      separation: Coordinate separation D of the black holes.\n      orbital_angular_velocity: Omega_0.\n      radial_expansion_velocity: adot_0.\n      refinement_level: h-refinement level.\n      polynomial_order: p-refinement level.\n      negative_expansion_bc: Place the excision boundaries inside of the\n        apparent horizons.\n      target_params: Target parameters for the initial data control loop.\n    \"\"\"\n    for required_target in [\n        \"MassA\",\n        \"MassB\",\n        \"DimensionlessSpinA\",\n        \"DimensionlessSpinB\",\n    ]:\n        assert (\n            required_target in target_params\n        ), f\"{required_target} must be specified in 'target_params'.\"\n\n    x_A = (\n        target_params[\"MassB\"]\n        / (target_params[\"MassA\"] + target_params[\"MassB\"])\n        * separation\n        + center_of_mass_offset[0]\n    )\n    x_B = x_A - separation\n\n    # Spins\n    chi_A = np.asarray(target_params[\"DimensionlessSpinA\"])\n    r_plus_A = conformal_mass_a * (1.0 + np.sqrt(1 - np.dot(chi_A, chi_A)))\n    Omega_A = horizon_rotation_a\n    Omega_A[2] += orbital_angular_velocity\n    chi_B = np.asarray(target_params[\"DimensionlessSpinB\"])\n    r_plus_B = conformal_mass_b * (1.0 + np.sqrt(1 - np.dot(chi_B, chi_B)))\n    Omega_B = horizon_rotation_b\n    Omega_B[2] += orbital_angular_velocity\n    if negative_expansion_bc:\n        # For high spins, we need to place the excisions closer to the outer\n        # horizon in order to avoid the inner horizon.\n        excision_factor = (\n            0.97\n            if max(np.linalg.norm(chi_A), np.linalg.norm(chi_B)) > 0.9\n            else 0.93\n        )\n    else:\n        excision_factor = 1.0\n    # Falloff widths of superposition\n    L1_dist_A = L1_distance(conformal_mass_a, conformal_mass_b, separation)\n    L1_dist_B = separation - L1_dist_A\n    falloff_width_A = 3.0 / 5.0 * L1_dist_A\n    falloff_width_B = 3.0 / 5.0 * L1_dist_B\n    # This extra refinement was found through trial and error and allowed mass\n    # ratio 6 to evolve through inspiral stably. This extra refinement doesn't\n    # seem to scale linearly with mass ratio. The current hard-coded limits (3\n    # and 5) were enough to find initial data for mass ratio 50 (no evolution\n    # attempted).\n    q = target_params[\"MassA\"] / target_params[\"MassB\"]\n    extra_radial_refinement_l = min(round(q / 3.0) - 1 if (q > 3.0) else 0, 3)\n    extra_radial_refinement_p = min(round(q / 5.0) if (q > 5.0) else 0, 5)\n    horizon_l_max = (\n        40 if max(np.linalg.norm(chi_A), np.linalg.norm(chi_B)) > 0.9 else 20\n    )\n    return {\n        \"ConformalMassRight\": conformal_mass_a,\n        \"ConformalMassLeft\": conformal_mass_b,\n        \"XRight\": x_A,\n        \"XLeft\": x_B,\n        \"CenterOfMassOffset_y\": center_of_mass_offset[1],\n        \"CenterOfMassOffset_z\": center_of_mass_offset[2],\n        \"LinearVelocity_x\": linear_velocity[0],\n        \"LinearVelocity_y\": linear_velocity[1],\n        \"LinearVelocity_z\": linear_velocity[2],\n        \"ExcisionRadiusRight\": excision_factor * r_plus_A,\n        \"ExcisionRadiusLeft\": excision_factor * r_plus_B,\n        \"ObjectOuterRadius\": separation / 3.75,\n        \"OrbitalAngularVelocity\": orbital_angular_velocity,\n        \"RadialExpansionVelocity\": radial_expansion_velocity,\n        \"ConformalSpinRight_x\": chi_A[0],\n        \"ConformalSpinRight_y\": chi_A[1],\n        \"ConformalSpinRight_z\": chi_A[2],\n        \"ConformalSpinLeft_x\": chi_B[0],\n        \"ConformalSpinLeft_y\": chi_B[1],\n        \"ConformalSpinLeft_z\": chi_B[2],\n        \"HorizonRotationRight_x\": Omega_A[0],\n        \"HorizonRotationRight_y\": Omega_A[1],\n        \"HorizonRotationRight_z\": Omega_A[2],\n        \"HorizonRotationLeft_x\": Omega_B[0],\n        \"HorizonRotationLeft_y\": Omega_B[1],\n        \"HorizonRotationLeft_z\": Omega_B[2],\n        \"FalloffWidthRight\": falloff_width_A,\n        \"FalloffWidthLeft\": falloff_width_B,\n        \"EnvelopeRadius\": 4.0 * separation,\n        # Resolution\n        \"L\": refinement_level,\n        \"P\": polynomial_order,\n        \"ExtraRadRef\": extra_radial_refinement_l,\n        \"ExtraRadPoints\": extra_radial_refinement_p,\n        \"HorizonLMax\": horizon_l_max,\n    }\n\n\ndef generate_id(\n    target_params: Dict[TargetParams, Union[float, Sequence[float]]],\n    # Orbital parameters\n    separation: Optional[float] = None,\n    orbital_angular_velocity: Optional[float] = None,\n    radial_expansion_velocity: Optional[float] = None,\n    # Control parameters\n    conformal_mass_a: Optional[float] = None,\n    conformal_mass_b: Optional[float] = None,\n    horizon_rotation_a: Optional[Sequence[float]] = None,\n    horizon_rotation_b: Optional[Sequence[float]] = None,\n    center_of_mass_offset: Sequence[float] = [0.0, 0.0, 0.0],\n    linear_velocity: Sequence[float] = [0.0, 0.0, 0.0],\n    # Resolution\n    refinement_level: int = 1,\n    polynomial_order: int = 9,\n    # Scheduling options\n    id_input_file_template: Union[str, Path] = ID_INPUT_FILE_TEMPLATE,\n    control: bool = False,\n    evolve: bool = False,\n    eccentricity_control: bool = False,\n    negative_expansion_bc: bool = True,\n    pipeline_dir: Optional[Union[str, Path]] = None,\n    run_dir: Optional[Union[str, Path]] = None,\n    segments_dir: Optional[Union[str, Path]] = None,\n    out_file_name: str = \"spectre.out\",\n    **scheduler_kwargs,\n):\n    \"\"\"Generate initial data for a BBH simulation.\n\n    Parameters for the initial data will be inserted into the\n    'id_input_file_template'. The remaining options are forwarded to the\n    'schedule' command. See 'schedule' docs for details.\n\n    The 'target_params' are the parameters that the simulation should converge\n    to. These values will never change during the simulation. Two control loops\n    will try to drive the simulation towards these values, i.e., ID control\n    (enable with 'control=True') and eccentricity control (enable with\n    'eccentricity_control=True').\n\n    ## ID control\n\n    The ID control loop adjusts the conformal masses and spins to drive the\n    horizon masses and spins to the specified values in a series of consecutive\n    initial data solves. See 'support.Pipelines.Bbh.ControlId' for details. If\n    unspecified, initial guesses for the conformal masses and spins default to\n    the target masses and spins.\n\n    ## Eccentricity control\n\n    The eccentricity control loop adjusts the orbital parameters (initial\n    coordinate separation D_0, orbital angular velocity Omega_0, and radial\n    expansion velocity adot_0) to drive the eccentricity to the specified value\n    in a series of short evolutions. See\n    'support.Pipelines.Bbh.EccentricityControl' for details. If unspecified,\n    initial guesses for the orbital parameters are obtained with the function\n    'initial_orbital_parameters' in\n    'support.Pipelines.EccentricityControl.InitialOrbitalParameters'.\n\n    Scheduling options:\n      id_input_file_template: Input file template where parameters are inserted.\n      control: If set to True, a postprocessing control loop will adjust the\n        input parameters to drive the horizon masses and spins to the specified\n        values. If set to False, the horizon masses and spins in the generated\n        data will differ from the input parameters. (default: False)\n      evolve: Set to True to evolve the initial data after generation.\n      eccentricity_control: If set to True, an eccentricity reduction script is\n        run on the initial data to correct the initial orbital parameters.\n      negative_expansion_bc: If set to True, the excision boundaries are set to\n        be inside of the apparent horizons. This helps to find horizons and\n        start an evolution from the initial data without extrapolation.\n        (default: True)\n      pipeline_dir: Directory where steps in the pipeline are created. Required\n        when 'evolve' is set to True. The initial data will be created in a\n        subdirectory '001_InitialData'.\n      run_dir: Directory where the initial data is generated. Mutually exclusive\n        with 'pipeline_dir'.\n      segments_dir: Directory where the evolution data is generated. Mutually\n        exclusive with 'pipeline_dir' and 'run_dir'.\n      out_file_name: Optional. Name of the log file. (Default: \"spectre.out\")\n    \"\"\"\n    logger.warning(\n        \"The BBH pipeline is still experimental. Please review the\"\n        \" generated input files.\"\n    )\n\n    # Resolve directories\n    if pipeline_dir:\n        pipeline_dir = Path(pipeline_dir).resolve()\n    assert segments_dir is None, (\n        \"Initial data generation doesn't use segments at the moment. Specify\"\n        \" '--run-dir' / '-o' or '--pipeline-dir' / '-d' instead.\"\n    )\n    if evolve:\n        assert pipeline_dir is not None, (\n            \"Specify a '--pipeline-dir' / '-d' to evolve the initial data.\"\n            \" Don't specify a '--run-dir' / '-o' because it will be created in\"\n            \" the 'pipeline_dir' automatically.\"\n        )\n        assert run_dir is None, (\n            \"Specify the '--pipeline-dir' / '-d' rather than '--run-dir' / '-o'\"\n            \" when evolving the initial data. Directories for the initial data,\"\n            \" evolution, etc will be created in the 'pipeline_dir'\"\n            \" automatically.\"\n        )\n    # If there is a pipeline directory, set run directory as well\n    if pipeline_dir and not run_dir:\n        pipeline_steps = list_pipeline_steps(pipeline_dir)\n        if pipeline_steps:  # Check if the list is not empty\n            run_dir = pipeline_steps[-1].next(label=\"InitialData\").path\n        else:\n            run_dir = PipelineStep.first(\n                directory=pipeline_dir, label=\"InitialData\"\n            ).path\n    # If we run a control loop, then run initial data in a subdirectory\n    if control:\n        run_dir = f\"{run_dir}/ControlParams_000\"\n        out_file_name = f\"../{out_file_name}\"\n\n    # Determine orbital parameters\n    if (\n        separation is None\n        or orbital_angular_velocity is None\n        or radial_expansion_velocity is None\n    ):\n        separation, orbital_angular_velocity, radial_expansion_velocity = (\n            initial_orbital_parameters(\n                target_params,\n                separation=separation,\n                orbital_angular_velocity=orbital_angular_velocity,\n                radial_expansion_velocity=radial_expansion_velocity,\n            )\n        )\n    if eccentricity_control:\n        assert (\n            target_params[\"Eccentricity\"] is not None\n        ), \"For eccentricity control the target eccentricity must be set.\"\n\n    # This is an empirical factor based on an equal-mass non-spinning case, in\n    # which ~0.41 conformal masses result in ~0.5 horizon masses.\n    # mass_initial_guess_factor = 1.0\n    mass_initial_guess_factor = 0.82\n    if conformal_mass_a is None:\n        conformal_mass_a = mass_initial_guess_factor * target_params[\"MassA\"]\n    if conformal_mass_b is None:\n        conformal_mass_b = mass_initial_guess_factor * target_params[\"MassB\"]\n\n    # The 0.9 is an empirical factor that avoids an ill-posed elliptic problem\n    # for very high spins.\n    if horizon_rotation_a is None:\n        chi_a = np.asarray(target_params[\"DimensionlessSpinA\"])\n        rotation_initial_guess_factor = (\n            0.9 if np.linalg.norm(chi_a) > 0.99 else 1.0\n        )\n        horizon_rotation_a = (\n            -rotation_initial_guess_factor\n            * 0.5\n            * chi_a\n            / (conformal_mass_a * (1.0 + np.sqrt(1 - np.dot(chi_a, chi_a))))\n        )\n    if horizon_rotation_b is None:\n        chi_b = np.asarray(target_params[\"DimensionlessSpinB\"])\n        rotation_initial_guess_factor = (\n            0.9 if np.linalg.norm(chi_b) > 0.99 else 1.0\n        )\n        horizon_rotation_b = (\n            -rotation_initial_guess_factor\n            * 0.5\n            * chi_b\n            / (conformal_mass_b * (1.0 + np.sqrt(1 - np.dot(chi_b, chi_b))))\n        )\n\n    # Determine initial data parameters from options\n    id_params = id_parameters(\n        conformal_mass_a=conformal_mass_a,\n        conformal_mass_b=conformal_mass_b,\n        horizon_rotation_a=horizon_rotation_a,\n        horizon_rotation_b=horizon_rotation_b,\n        separation=separation,\n        orbital_angular_velocity=orbital_angular_velocity,\n        radial_expansion_velocity=radial_expansion_velocity,\n        center_of_mass_offset=center_of_mass_offset,\n        linear_velocity=linear_velocity,\n        refinement_level=refinement_level,\n        polynomial_order=polynomial_order,\n        negative_expansion_bc=negative_expansion_bc,\n        target_params=target_params,\n    )\n    logger.debug(f\"Initial data parameters: {pretty_repr(id_params)}\")\n\n    # Store target parameters in the input file\n    id_params[\"TargetParams\"] = yaml.safe_dump(\n        {\"TargetParams\": target_params}\n    ).strip()\n\n    # Schedule!\n    return schedule(\n        id_input_file_template,\n        **id_params,\n        **scheduler_kwargs,\n        control=control,\n        evolve=evolve,\n        eccentricity_control=eccentricity_control,\n        negative_expansion_bc=negative_expansion_bc,\n        pipeline_dir=pipeline_dir,\n        run_dir=run_dir,\n        segments_dir=segments_dir,\n        out_file_name=out_file_name,\n    )\n\n\n@click.command(name=\"generate-id\", help=generate_id.__doc__)\n@click.option(\n    \"--mass-ratio\",\n    \"-q\",\n    type=click.FloatRange(1.0, None),\n    help=\"Mass ratio of the binary, defined as q = M_A / M_B >= 1.\",\n    required=True,\n)\n@click.option(\n    \"--dimensionless-spin-A\",\n    \"--chi-A\",\n    type=click.FloatRange(-1.0, 1.0),\n    nargs=3,\n    help=\"Dimensionless spin of the larger black hole, chi_A.\",\n    required=True,\n)\n@click.option(\n    \"--dimensionless-spin-B\",\n    \"--chi-B\",\n    type=click.FloatRange(-1.0, 1.0),\n    nargs=3,\n    help=\"Dimensionless spin of the smaller black hole, chi_B.\",\n    required=True,\n)\n# Orbital parameters\n@click.option(\n    \"--separation\",\n    \"-D\",\n    type=click.FloatRange(0.0, None, min_open=True),\n    help=\"Coordinate separation D of the black holes.\",\n)\n@click.option(\n    \"--orbital-angular-velocity\",\n    \"-w\",\n    type=float,\n    help=\"Orbital angular velocity Omega_0.\",\n)\n@click.option(\n    \"--radial-expansion-velocity\",\n    \"-a\",\n    type=float,\n    help=(\n        \"Radial expansion velocity adot0 which is radial velocity over radius.\"\n    ),\n)\n@click.option(\n    \"--eccentricity\",\n    \"-e\",\n    type=click.FloatRange(0.0, 1.0),\n    help=(\n        \"Eccentricity of the orbit. Specify together with _one_ of the other\"\n        \" orbital parameters. Currently only an eccentricity of 0 is supported\"\n        \" (circular orbit).\"\n    ),\n)\n@click.option(\n    \"--eccentricity-abs-tol\",\n    type=click.FloatRange(0.0, 1.0),\n    default=DEFAULT_TARGET_PARAMS[\"EccentricityAbsoluteTolerance\"],\n    show_default=True,\n    help=\"Absolute tolerance for eccentricity control.\",\n)\n@click.option(\n    \"--mean-anomaly-fraction\",\n    \"-l\",\n    type=click.FloatRange(0.0, 1.0, max_open=True),\n    help=(\n        \"Mean anomaly of the orbit divided by 2 pi, so it is a number between 0\"\n        \" and 1. The value 0 corresponds to the pericenter of the orbit\"\n        \" (closest approach), and the value 0.5 corresponds to the apocenter of\"\n        \" the orbit (farthest distance).\"\n    ),\n)\n@click.option(\n    \"--num-orbits\",\n    type=click.FloatRange(0.0, None, min_open=True),\n    help=(\n        \"Number of orbits until merger. Specify together with a zero\"\n        \" eccentricity to compute initial orbital parameters for a circular\"\n        \" orbit.\"\n    ),\n)\n@click.option(\n    \"--time-to-merger\",\n    type=click.FloatRange(0.0, None, min_open=True),\n    help=(\n        \"Time to merger. Specify together with a zero eccentricity to compute\"\n        \" initial orbital parameters for a circular orbit.\"\n    ),\n)\n# Resolution\n@click.option(\n    \"--evolution-lev\",\n    \"--lev\",\n    type=int,\n    help=(\n        \"Resolution level for the evolution. See 'start-inspiral' for details.\"\n    ),\n    default=1,\n    show_default=True,\n)\n@click.option(\n    \"--refinement-level\",\n    \"-L\",\n    type=click.IntRange(0, None),\n    help=\"h-refinement level for the initial data.\",\n    default=1,\n    show_default=True,\n)\n@click.option(\n    \"--polynomial-order\",\n    \"-P\",\n    type=click.IntRange(1, None),\n    help=\"p-refinement level for the initial data.\",\n    default=9,\n    show_default=True,\n)\n# Scheduling options\n@click.option(\n    \"--id-input-file-template\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n    default=ID_INPUT_FILE_TEMPLATE,\n    help=\"Input file template for the initial data.\",\n    show_default=True,\n)\n@click.option(\n    \"--control/--no-control\",\n    default=True,\n    show_default=True,\n    help=\"Control BBH physical parameters.\",\n)\n@click.option(\n    \"--evolve\",\n    is_flag=True,\n    help=(\n        \"Evolve the initial data after generation. When this flag\"\n        \"is specified, you must also specify a pipeline directory (-d),\"\n        \"instead of a run directory (-o).\"\n    ),\n)\n@click.option(\n    \"--eccentricity-control\",\n    is_flag=True,\n    help=(\n        \"Perform eccentricity reduction script that finds current eccentricity\"\n        \"and better guesses for the input orbital parameters.\"\n    ),\n)\n@click.option(\n    \"--negative-expansion-bc/--no-negative-expansion-bc\",\n    default=True,\n    show_default=True,\n    help=(\n        \"Use negative expansion boundary condition so that the excision\"\n        \"boundaries are inside of the apparent horizons. This helps to find\"\n        \"horizons and start an evolution from the initial data without\"\n        \"extrapolation.\"\n    ),\n)\n@click.option(\n    \"--pipeline-dir\",\n    \"-d\",\n    type=click.Path(\n        writable=True,\n        path_type=Path,\n    ),\n    help=\"Directory where steps in the pipeline are created.\",\n)\n@scheduler_options\ndef generate_id_command(\n    mass_ratio,\n    dimensionless_spin_a,\n    dimensionless_spin_b,\n    separation,\n    orbital_angular_velocity,\n    radial_expansion_velocity,\n    eccentricity,\n    eccentricity_abs_tol,\n    mean_anomaly_fraction,\n    num_orbits,\n    time_to_merger,\n    evolution_lev,\n    **kwargs,\n):\n    _rich_traceback_guard = True  # Hide traceback until here\n    target_params = {\n        \"MassRatio\": mass_ratio,\n        \"MassA\": mass_ratio / (1.0 + mass_ratio),\n        \"MassB\": 1.0 / (1.0 + mass_ratio),\n        \"DimensionlessSpinA\": dimensionless_spin_a,\n        \"DimensionlessSpinB\": dimensionless_spin_b,\n        \"Eccentricity\": eccentricity,\n        \"EccentricityAbsoluteTolerance\": eccentricity_abs_tol,\n        \"MeanAnomalyFraction\": mean_anomaly_fraction,\n        \"NumOrbits\": num_orbits,\n        \"TimeToMerger\": time_to_merger,\n        \"CenterOfMass\": [0.0, 0.0, 0.0],\n        \"AdmLinearMomentum\": [0.0, 0.0, 0.0],\n        \"EvolutionLev\": evolution_lev,\n    }\n    if kwargs[\"eccentricity_control\"]:\n        # Only circular orbits are currently supported for eccentricity control,\n        # so set target eccentricity to zero\n        target_params[\"Eccentricity\"] = 0.0\n\n    generate_id(\n        target_params,\n        separation=separation,\n        orbital_angular_velocity=orbital_angular_velocity,\n        radial_expansion_velocity=radial_expansion_velocity,\n        **kwargs,\n    )\n\n\nif __name__ == \"__main__\":\n    generate_id_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Pipelines/Bbh/InitialData.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveXcts\n{{ TargetParams }}\nExpectedOutput:\n  - BbhReductions.h5\n  - BbhVolume*.h5\nNext:\n  Run: spectre.Pipelines.Bbh.PostprocessId:postprocess_id\n  With:\n    id_input_file_path: __file__\n    id_run_dir: ./\n    pipeline_dir: {{ pipeline_dir | default(\"None\") }}\n    horizon_l_max: {{ HorizonLMax }}\n    control: {{ control }}\n    control_refinement_level: {{ L }}\n    control_polynomial_order: {{ P }}\n    control_params:\n      - MassA\n      - MassB\n      - DimensionlessSpinA\n      - DimensionlessSpinB\n      - CenterOfMass\n      - AdmLinearMomentum\n    evolve: {{ evolve }}\n    eccentricity_control: {{ eccentricity_control }}\n    negative_expansion_bc: {{ negative_expansion_bc }}\n    scheduler: {{ scheduler | default(\"None\") }}\n    copy_executable: {{ copy_executable | default(\"None\") }}\n    submit_script_template: {{ submit_script_template | default(\"None\") }}\n    submit: True\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nBackground: &background\n  Binary:\n    XCoords: [&x_left {{ XLeft }}, &x_right {{ XRight }}]\n    CenterOfMassOffset:\n      - &y_offset {{ CenterOfMassOffset_y }}\n      - &z_offset {{ CenterOfMassOffset_z }}\n    ObjectLeft: &kerr_left\n      KerrSchild:\n        Mass: &mass_left {{ ConformalMassLeft }}\n        Spin: &spin_left\n          - {{ ConformalSpinLeft_x }}\n          - {{ ConformalSpinLeft_y }}\n          - {{ ConformalSpinLeft_z }}\n        Center: [0., 0., 0.]\n        Velocity: [0., 0., 0.]\n    ObjectRight: &kerr_right\n      KerrSchild:\n        Mass: &mass_right {{ ConformalMassRight }}\n        Spin: &spin_right\n          - {{ ConformalSpinRight_x }}\n          - {{ ConformalSpinRight_y }}\n          - {{ ConformalSpinRight_z }}\n        Center: [0., 0., 0.]\n        Velocity: [0., 0., 0.]\n    AngularVelocity: {{ OrbitalAngularVelocity }}\n    Expansion: {{ RadialExpansionVelocity }}\n    LinearVelocity:\n      - {{ LinearVelocity_x }}\n      - {{ LinearVelocity_y }}\n      - {{ LinearVelocity_z }}\n    FalloffWidths:\n      - {{ FalloffWidthLeft }}\n      - {{ FalloffWidthRight }}\n\nInitialGuess: *background\n\nDomainCreator:\n  BinaryCompactObject:\n    ObjectA:\n      InnerRadius: {{ ExcisionRadiusRight }}\n      OuterRadius: {{ ObjectOuterRadius }}\n      XCoord: *x_right\n      Interior:\n        ExciseWithBoundaryCondition:\n          ApparentHorizon:\n            Center: [*x_right, *y_offset, *z_offset]\n            Rotation:\n              - {{ HorizonRotationRight_x }}\n              - {{ HorizonRotationRight_y }}\n              - {{ HorizonRotationRight_z }}\n            Lapse: *kerr_right\n            {% if negative_expansion_bc %}\n            NegativeExpansion: *kerr_right\n            {% else %}\n            NegativeExpansion: None\n            {% endif %}\n      UseLogarithmicMap: True\n    ObjectB:\n      InnerRadius: {{ ExcisionRadiusLeft }}\n      OuterRadius: {{ ObjectOuterRadius }}\n      XCoord: *x_left\n      Interior:\n        ExciseWithBoundaryCondition:\n          ApparentHorizon:\n            Center: [*x_left, *y_offset, *z_offset]\n            Rotation:\n              - {{ HorizonRotationLeft_x }}\n              - {{ HorizonRotationLeft_y }}\n              - {{ HorizonRotationLeft_z }}\n            Lapse: *kerr_left\n            {% if negative_expansion_bc %}\n            NegativeExpansion: *kerr_left\n            {% else %}\n            NegativeExpansion: None\n            {% endif %}\n      UseLogarithmicMap: True\n    CenterOfMassOffset: [*y_offset, *z_offset]\n    Envelope:\n      Radius: &outer_shell_inner_radius {{ EnvelopeRadius }}\n      RadialDistribution: Projective\n    OuterShell:\n      Radius: &outer_radius 1e5\n      RadialPartitioning: []\n      RadialDistribution: &outer_shell_distribution Inverse\n      OpeningAngle: 120.0\n      BoundaryCondition: Robin\n    UseEquiangularMap: True\n    CubeScale: 1.0\n    InitialRefinement:\n      ObjectAShell:     [{{ L }}, {{ L }}, {{ L }}]\n      ObjectBShell:     [{{ L }}, {{ L }}, {{ L + ExtraRadRef }}]\n      ObjectACube:      [{{ L }}, {{ L }}, {{ L }}]\n      ObjectBCube:      [{{ L }}, {{ L }}, {{ L }}]\n      Envelope:         [{{ L }}, {{ L }}, {{ L + 1 }}]\n      OuterShell0:       [{{ L + 1 }}, {{ L + 1 }}, {{ L }}]\n    # This p-refinement represents a crude manual optimization of the domain. We\n    # will need AMR to optimize the domain further.\n    InitialGridPoints:\n      ObjectAShell:   [{{ P + 1}}, {{ P + 1}}, {{ P + 5}}]\n      ObjectBShell:   [{{ P + 1}}, {{ P + 1}}, {{ P + 5 + ExtraRadPoints}}]\n      ObjectACube:    [{{ P + 1}}, {{ P + 1}}, {{ P + 2}}]\n      ObjectBCube:    [{{ P + 1}}, {{ P + 1}}, {{ P + 2}}]\n      Envelope:       [{{ P + 1}}, {{ P + 1}}, {{ P + 1}}]\n      OuterShell0:     [{{ P + 1}}, {{ P + 1}}, {{ P + 1}}]\n    TimeDependentMaps:\n      InitialTime: 0.\n      ExpansionMap: None\n      RotationMap: None\n      TranslationMap: None\n      SkewMap: None\n      ShapeMapA:\n        LMax: {{ HorizonLMax }}\n        CoefficientTruncationLimit: 0.\n        InitialValues:\n          Mass: *mass_right\n          Spin: *spin_right\n        SizeInitialValues: Auto\n        TransitionEndsAtCube: False\n      ShapeMapB:\n        CoefficientTruncationLimit: 0.\n        LMax: {{ HorizonLMax }}\n        InitialValues:\n          Mass: *mass_left\n          Spin: *spin_left\n        SizeInitialValues: Auto\n        TransitionEndsAtCube: False\n      GridCenters: None\n\nAmr:\n  Verbosity: Verbose\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.5\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: WeakInertial\n\nObservers:\n  VolumeFileName: \"BbhVolume\"\n  ReductionFileName: \"BbhReductions\"\n\nNonlinearSolver:\n  NewtonRaphson:\n    ConvergenceCriteria:\n      MaxIterations: 20\n      RelativeResidual: 0.\n      AbsoluteResidual: 1.e-8\n    SufficientDecrease: 1.e-4\n    MaxGlobalizationSteps: 10\n    DampingFactor: 1.\n    Verbosity: Verbose\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 100\n      RelativeResidual: 1.e-3\n      AbsoluteResidual: 1.e-9\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: True\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    MaxOverlap: 2\n    Iterations: 3\n    Verbosity: Silent\n    SubdomainSolver:\n      Gmres:\n        ConvergenceCriteria:\n          MaxIterations: 3\n          RelativeResidual: 1.e-4\n          AbsoluteResidual: 1.e-10\n        Verbosity: Silent\n        Restart: None\n        Preconditioner:\n          MinusLaplacian:\n            Solver:\n              ExplicitInverse:\n                WriteMatrixToFile: None\n            BoundaryConditions: Auto\n    SkipResets: True\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates:\n  InnerRadius: *outer_shell_inner_radius\n  OuterRadius: *outer_radius\n  Compression: *outer_shell_distribution\n\nEventsAndTriggersAtIterations:\n  - Trigger: HasConverged\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - ConformalFactor\n            - Lapse\n            - Shift\n            - ShiftExcess\n            - SpatialMetric\n            - InverseSpatialMetric\n            - SpatialChristoffelSecondKind\n            - ExtrinsicCurvature\n            - SpatialRicci\n            - RadiallyCompressedCoordinates\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n      - ObserveAdmIntegrals\n\nRandomizeInitialGuess: None\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n  SkipResets: True\n"
  },
  {
    "path": "support/Pipelines/Bbh/Inspiral.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport glob\nimport logging\nfrom pathlib import Path\nfrom typing import Optional, Union\n\nimport click\nimport numpy as np\nimport yaml\nfrom rich.pretty import pretty_repr\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5 import available_subfiles, open_volfiles, select_observation\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.support.DirectoryStructure import PipelineStep, list_pipeline_steps\nfrom spectre.support.Schedule import schedule, scheduler_options\n\nlogger = logging.getLogger(__name__)\n\nINSPIRAL_INPUT_FILE_TEMPLATE = Path(__file__).parent / \"Inspiral.yaml\"\n\n# Resolution levels defined in terms of p-refinement\n# To be replaced once AMR is used.\nINSPIRAL_LEVS = {\n    lev_number: {\n        \"label\": f\"Lev{lev_number}\",\n        \"refinement_level\": 1,\n        \"polynomial_order\": 7 + lev_number,\n    }\n    for lev_number in range(-2, 11)\n}\n\n\n# These parameters come from empirically tested values in SpEC and SpECTRE\ndef _control_system_params(\n    mass_left: float,\n    mass_right: float,\n    spin_magnitude_left: float,\n    spin_magnitude_right: float,\n) -> dict:\n    total_mass = mass_left + mass_right\n    mass_ratio = max(mass_left, mass_right) / min(mass_left, mass_right)\n    if spin_magnitude_left > 0.9 or spin_magnitude_right > 0.9:\n        damping_time_base = 0.1\n        decrease_threshold_base = 2e-4\n        max_damping_timescale = 10.0\n    else:\n        damping_time_base = 0.2\n        decrease_threshold_base = 2e-3\n        max_damping_timescale = 20.0\n\n    kinematic_timescale = damping_time_base * total_mass\n    decrease_threshold = (\n        0.1 * decrease_threshold_base / (mass_ratio + 1.0 / mass_ratio)\n    )\n    increase_threshold_fraction = 0.25\n    min_kinematic_timescale = 1e-2\n\n    size_b_timescale = damping_time_base * 0.2 * mass_left\n    # Found that changing the size_a timescale to be the same timescale as\n    # size_b fixed early incoming char speeds for higher mass ratio runs.\n    size_a_timescale = (\n        size_b_timescale\n        if (mass_ratio > 2.0)\n        else damping_time_base * 0.2 * mass_right\n    )\n\n    return {\n        \"MinKinematicTimescale\": min_kinematic_timescale,\n        \"MaxDampingTimescale\": max_damping_timescale,\n        \"KinematicTimescale\": kinematic_timescale,\n        \"MinSkewTimescale\": 5.0 * min_kinematic_timescale,\n        \"SkewTimescale\": 0.5 * (\n            5.0 * min_kinematic_timescale + max_damping_timescale\n        ),\n        \"SizeATimescale\": size_a_timescale,\n        \"SizeBTimescale\": size_b_timescale,\n        \"ShapeATimescale\": 5.0 * kinematic_timescale,\n        \"ShapeBTimescale\": 5.0 * kinematic_timescale,\n        \"SizeIncreaseThreshold\": 1e-3,\n        \"DecreaseThreshold\": decrease_threshold,\n        \"IncreaseThreshold\": increase_threshold_fraction * decrease_threshold,\n        \"SizeBMaxTimescale\": 10 if spin_magnitude_left > 0.9 else 20,\n        \"SizeAMaxTimescale\": 10 if spin_magnitude_right > 0.9 else 20,\n    }\n\n\ndef _constraint_damping_params(\n    mass_left: float,\n    mass_right: float,\n    initial_separation: float,\n) -> dict:\n    total_mass = mass_left + mass_right\n    return {\n        # SpEC chooses a Gamma0Constant of 0.001, but we found 0.01 produces\n        # smaller constraints violations in the envelope/outer shell region.\n        \"Gamma0Constant\": 0.01 / total_mass,\n        \"Gamma0LeftAmplitude\": 4.0 / mass_left,\n        \"Gamma0LeftWidth\": 7.0 * mass_left,\n        \"Gamma0RightAmplitude\": 4.0 / mass_right,\n        \"Gamma0RightWidth\": 7.0 * mass_right,\n        # SpEC chooses a Gamm0OriginAmplitude of 0.075, but we found that 0.75\n        # produces a smaller burst of constraints from junk radiation.\n        \"Gamma0OriginAmplitude\": 0.75 / total_mass,\n        \"Gamma0OriginWidth\": 2.5 * initial_separation,\n        \"Gamma1Width\": 10.0 * initial_separation,\n    }\n\n\ndef inspiral_parameters(\n    id_input_file: dict,\n    id_metadata: dict,\n    id_run_dir: Union[str, Path],\n    id_subfile_name: Optional[str],\n    id_horizons_path: Optional[Union[str, Path]],\n) -> dict:\n    \"\"\"Determine inspiral parameters from SpECTRE initial data.\n\n    These parameters fill the 'INSPIRAL_INPUT_FILE_TEMPLATE'.\n\n    Arguments:\n      id_input_file: Initial data input file as a dictionary.\n      id_metadata: Metadata of the initial data input file as a dictionary.\n      id_run_dir: Directory of the initial data run. Paths in the input file\n        are relative to this directory.\n      id_subfile_name: Subfile name inside the H5 volume files where the\n        initial data is stored. If 'None' and only one subfile exists, that\n        subfile is used. Otherwise, the subfile must be specified.\n      id_horizons_path: Path to H5 file containing information about the\n        horizons in the ID (e.g. mass, spin, spherical harmonic coefficients).\n        If this is 'None', the default is the 'Horizons.h5' file inside\n        'id_run_dir'.\n    \"\"\"\n    # For constraints and control system params we just use the target masses\n    # and spins, not the values measured on the horizons, because these numbers\n    # don't have to be exact and the horizon quantities will change a bit during\n    # the evolution anyway.\n    if \"TargetParams\" in id_metadata:\n        target_params = id_metadata[\"TargetParams\"]\n    else:\n        # Fall back to retrieving the target parameters from the input file\n        # for older initial data that didn't store them in the metadata.\n        # These are the conformal (background) Kerr-Schild parameters, but they\n        # are close enough to the target parameters for our purposes here.\n        id_binary = id_input_file[\"Background\"][\"Binary\"]\n        object_left = id_binary[\"ObjectLeft\"][\"KerrSchild\"]\n        object_right = id_binary[\"ObjectRight\"][\"KerrSchild\"]\n        mass_ratio = object_right[\"Mass\"] / object_left[\"Mass\"]\n        target_params = {\n            \"MassRatio\": mass_ratio,\n            \"MassA\": object_right[\"Mass\"],\n            \"MassB\": object_left[\"Mass\"],\n            \"DimensionlessSpinA\": object_right[\"Spin\"],\n            \"DimensionlessSpinB\": object_left[\"Spin\"],\n        }\n\n    mass_ratio = target_params[\"MassRatio\"]\n    mass_a = mass_ratio / (1.0 + mass_ratio)\n    mass_b = 1.0 / (1.0 + mass_ratio)\n    spin_magnitude_a = np.linalg.norm(target_params[\"DimensionlessSpinA\"])\n    spin_magnitude_b = np.linalg.norm(target_params[\"DimensionlessSpinB\"])\n    # Initial data can be either from an ID solve or from a previous evolution\n    id_from_evolution = \"Evolution\" in id_input_file\n    id_domain_creator = id_input_file[\"DomainCreator\"][\"BinaryCompactObject\"]\n    # This factor is to account for the ID excision not being the same shape as\n    # the final horizon found after the last iteration. Found through trial and\n    # error that increasing the excision size by this factor allowed the runs to\n    # evolve without early incoming char speeds.\n    # For unequal masses, this was found through trial and error that\n    # decreasing the excision size of object b allowed the runs to evolve\n    # without early incoming char speeds.\n    excision_radius_factor_a = 1.0 if id_from_evolution else 1.0385\n    excision_radius_factor_b = (\n        1.0 if (id_from_evolution or mass_ratio > 2.0) else 1.0385\n    )\n    initial_separation = (\n        id_domain_creator[\"ObjectA\"][\"XCoord\"]\n        - id_domain_creator[\"ObjectB\"][\"XCoord\"]\n    )\n\n    # Resolve subfile name in the H5 files\n    id_file_glob = str(\n        Path(id_run_dir).resolve()\n        / (id_input_file[\"Observers\"][\"VolumeFileName\"] + \"*.h5\")\n    )\n    if not id_subfile_name:\n        id_subfiles = available_subfiles(\n            glob.glob(id_file_glob),\n            extension=\".vol\",\n        )\n        if len(id_subfiles) == 1:\n            id_subfile_name = id_subfiles[0]\n            logger.info(\n                f\"Selected subfile {id_subfile_name} (the only available one).\"\n            )\n        else:\n            raise RequiredChoiceError(\n                (\n                    \"Specify '--id-subfile-name' to select a subfile containing\"\n                    \" volume data.\"\n                ),\n                choices=id_subfiles,\n            )\n\n    params = {\n        # Initial data files\n        \"IdFileGlob\": id_file_glob,\n        \"IdSubfile\": id_subfile_name,\n        \"IdFromEvolution\": id_from_evolution,\n        # Domain geometry\n        \"ExcisionRadiusA\": (\n            id_domain_creator[\"ObjectA\"][\"InnerRadius\"]\n            * excision_radius_factor_a\n        ),\n        \"ExcisionRadiusB\": (\n            id_domain_creator[\"ObjectB\"][\"InnerRadius\"]\n            * excision_radius_factor_b\n        ),\n        \"ObjectOuterRadius\": initial_separation / 2.5,\n        \"XCoordA\": id_domain_creator[\"ObjectA\"][\"XCoord\"],\n        \"XCoordB\": id_domain_creator[\"ObjectB\"][\"XCoord\"],\n        \"CenterOfMassOffset_y\": id_domain_creator[\"CenterOfMassOffset\"][0],\n        \"CenterOfMassOffset_z\": id_domain_creator[\"CenterOfMassOffset\"][1],\n        \"EnvelopeRadius\": 100.0 / 15.0 * initial_separation,\n        # SpEC chooses the outer radius based on a Newtonian estimate of the\n        # wave zone (See function AutoRmax in SpEC/Support/Perl/SpEC.pm). This\n        # may need to be ported over eventually. The CCE extraction radii may\n        # also need to be adjusted to account for different outer shell radii.\n        \"OuterShellRadius\": 600.0 / 15.0 * initial_separation,\n        # Extra resolution for unequal masses (to be replaced with AMR)\n        # This extra refinement was found through trial and error and allowed\n        # mass ratio 6 to evolve through inspiral stably.\n        \"ExtraRadRef\": round(mass_ratio / 2.0) - 1 if (mass_ratio > 2.0) else 0,\n        \"ExtraRadPoints\": round(mass_ratio / 5.0) if (mass_ratio > 5.0) else 0,\n    }\n\n    # Initial functions of time (set from ID or load from evolution data)\n    if id_from_evolution:\n        first_volfile = id_file_glob.replace(\"*\", \"0\")\n        _, obs_time = select_observation(\n            open_volfiles(first_volfile, id_subfile_name), step=-1\n        )\n        params.update(\n            {\n                \"InitialTime\": obs_time,\n                \"FotFilename\": first_volfile,\n            }\n        )\n    else:\n        id_shape_A = id_domain_creator[\"TimeDependentMaps\"][\"ShapeMapA\"]\n        id_shape_B = id_domain_creator[\"TimeDependentMaps\"][\"ShapeMapB\"]\n        id_binary = id_input_file[\"Background\"][\"Binary\"]\n        horizons_filename = (\n            Path(id_horizons_path)\n            if id_horizons_path is not None\n            else Path(id_run_dir) / \"Horizons.h5\"\n        )\n        # Initial functions of time\n        params.update(\n            {\n                \"InitialAngularVelocity\": id_binary[\"AngularVelocity\"],\n                \"RadialExpansionVelocity\": float(id_binary[\"Expansion\"]),\n                \"HorizonsFile\": str(horizons_filename.resolve()),\n                \"AhASubfileName\": \"AhA/Coefficients\",\n                \"AhBSubfileName\": \"AhB/Coefficients\",\n                \"ExcisionAShapeMass\": id_shape_A[\"InitialValues\"][\"Mass\"],\n                \"ExcisionAShapeSpin_x\": id_shape_A[\"InitialValues\"][\"Spin\"][0],\n                \"ExcisionAShapeSpin_y\": id_shape_A[\"InitialValues\"][\"Spin\"][1],\n                \"ExcisionAShapeSpin_z\": id_shape_A[\"InitialValues\"][\"Spin\"][2],\n                \"ExcisionBShapeMass\": id_shape_B[\"InitialValues\"][\"Mass\"],\n                \"ExcisionBShapeSpin_x\": id_shape_B[\"InitialValues\"][\"Spin\"][0],\n                \"ExcisionBShapeSpin_y\": id_shape_B[\"InitialValues\"][\"Spin\"][1],\n                \"ExcisionBShapeSpin_z\": id_shape_B[\"InitialValues\"][\"Spin\"][2],\n            }\n        )\n\n    # Constraint damping parameters\n    params.update(\n        _constraint_damping_params(\n            mass_left=mass_b,\n            mass_right=mass_a,\n            initial_separation=initial_separation,\n        )\n    )\n\n    # Control system\n    params.update(\n        _control_system_params(\n            mass_left=mass_b,\n            mass_right=mass_a,\n            spin_magnitude_left=spin_magnitude_b,\n            spin_magnitude_right=spin_magnitude_a,\n        )\n    )\n\n    # Store target parameters in the input file\n    params[\"TargetParams\"] = yaml.safe_dump(\n        {\"TargetParams\": target_params}\n    ).strip()\n\n    return params\n\n\ndef _load_spec_id_params(id_params_file: Path) -> dict:\n    \"\"\"Load SpEC initial data parameters from 'ID_Params.perl'.\"\"\"\n    # Here we have to deal with SpEC storing the initial data parameters in a\n    # perl script (yay!). We convert the perl syntax to YAML and load that into\n    # a Python dictionary.\n    id_params_yaml = (\n        # Drop last three lines that contain the 'ID_Origin' variable\n        \"\\n\".join(id_params_file.read_text().split(\"\\n\")[:-3])\n        # Convert perl syntax to YAML\n        .replace(\"$\", \"\")\n        .replace(\"@\", \"\")\n        .replace(\";\", \"\")\n        .replace(\" = \", \": \")\n        .replace(\"=\", \": \")\n        .replace(\"(\", \"[\")\n        .replace(\")\", \"]\")\n    )\n    logger.debug(f\"ID_Params.perl converted to YAML:\\n{id_params_yaml}\")\n    return yaml.safe_load(id_params_yaml)\n\n\ndef inspiral_parameters_spec(\n    id_params: dict,\n    id_run_dir: Union[str, Path],\n) -> dict:\n    \"\"\"Determine inspiral parameters from SpEC initial data.\n\n    These parameters fill the 'INSPIRAL_INPUT_FILE_TEMPLATE'.\n\n    Arguments:\n      id_params: Initial data parameters loaded from 'ID_Params.perl'.\n      id_run_dir: Directory of the initial data, which contains\n        'ID_Params.perl' and 'GrDomain.input'.\n    \"\"\"\n\n    mass_left = id_params[\"ID_MB\"]\n    mass_right = id_params[\"ID_MA\"]\n    mass_ratio = mass_right / mass_left\n    spin_magnitude_left = id_params[\"ID_chiBMagnitude\"]\n    spin_magnitude_right = id_params[\"ID_chiAMagnitude\"]\n    initial_separation = id_params[\"ID_d\"]\n\n    params = {\n        # Initial data files\n        \"SpecDataDirectory\": str(Path(id_run_dir).resolve()),\n        # Domain geometry\n        # SpEC excision in ID_Params.perl is 0.89 * horizon radius, but\n        # usually you want to excise less than the maximum. Here use 6% larger,\n        # or about 0.9434 * horizon radius.\n        \"ExcisionRadiusA\": id_params[\"ID_rExcA\"] * 1.06,\n        \"ExcisionRadiusB\": id_params[\"ID_rExcB\"] * 1.06,\n        \"ObjectOuterRadius\": initial_separation / 2.5,\n        \"XCoordA\": id_params[\"ID_cA\"][0],\n        \"XCoordB\": id_params[\"ID_cB\"][0],\n        # COM offset in y and z is the same for both objects\n        \"CenterOfMassOffset_y\": id_params[\"ID_cA\"][1],\n        \"CenterOfMassOffset_z\": id_params[\"ID_cA\"][2],\n        # Initial functions of time\n        \"InitialAngularVelocity\": id_params[\"ID_Omega0\"],\n        \"RadialExpansionVelocity\": id_params[\"ID_adot0\"],\n        \"EnvelopeRadius\": 100.0 / 15.0 * initial_separation,\n        # SpEC chooses the outer radius based on a Newtonian estimate of the\n        # wave zone (See function AutoRmax in SpEC/Support/Perl/SpEC.pm). This\n        # may need to be ported over eventually. The CCE extraction radii may\n        # also need to be adjusted to account for different outer shell radii.\n        \"OuterShellRadius\": 600.0 / 15.0 * initial_separation,\n        # Extra resolution for unequal masses (to be replaced with AMR)\n        # This extra refinement was found through trial and error and allowed\n        # mass ratio 6 to evolve through inspiral stably.\n        \"ExtraRadRef\": round(mass_ratio / 2.0) - 1 if (mass_ratio > 2.0) else 0,\n        \"ExtraRadPoints\": round(mass_ratio / 5.0) if (mass_ratio > 5.0) else 0,\n    }\n\n    # Constraint damping parameters\n    params.update(\n        _constraint_damping_params(\n            mass_left=mass_left,\n            mass_right=mass_right,\n            initial_separation=initial_separation,\n        )\n    )\n\n    # Control system\n    params.update(\n        _control_system_params(\n            mass_left=mass_left,\n            mass_right=mass_right,\n            spin_magnitude_left=spin_magnitude_left,\n            spin_magnitude_right=spin_magnitude_right,\n        )\n    )\n\n    return params\n\n\ndef start_inspiral(\n    id_input_file_path: Union[str, Path],\n    lev: Optional[int] = None,\n    refinement_level: Optional[int] = None,\n    polynomial_order: Optional[int] = None,\n    id_run_dir: Optional[Union[str, Path]] = None,\n    id_subfile_name: Optional[str] = None,\n    inspiral_input_file_template: Union[\n        str, Path\n    ] = INSPIRAL_INPUT_FILE_TEMPLATE,\n    id_horizons_path: Optional[Union[str, Path]] = None,\n    continue_with_ringdown: bool = False,\n    eccentricity_control: bool = False,\n    pipeline_dir: Optional[Union[str, Path]] = None,\n    run_dir: Optional[Union[str, Path]] = None,\n    segments_dir: Optional[Union[str, Path]] = None,\n    **scheduler_kwargs,\n):\n    \"\"\"Schedule an inspiral simulation from initial data.\n\n    Point the ID_INPUT_FILE_PATH to the input file of your initial data run,\n    or to an 'ID_Params.perl' file from SpEC.\n    Also specify 'id_run_dir' if the initial data was run in a different\n    directory than where the input file is. Parameters for the inspiral will be\n    determined from the initial data and inserted into the\n    'inspiral_input_file_template'. The remaining options are forwarded to the\n    'schedule' command. See 'schedule' docs for details.\n\n    ## Resource allocation\n\n    Runs on 4 nodes by default when scheduled on a cluster. Set 'num_nodes' to\n    adjust.\n    \"\"\"\n    logger.warning(\n        \"The BBH pipeline is still experimental. Please review the\"\n        \" generated input files.\"\n    )\n    assert not (continue_with_ringdown and eccentricity_control), (\n        \"Cannot enable both 'continue_with_ringdown' and\"\n        \" 'eccentricity_control'. Choose which of the two to perform next.\"\n    )\n\n    # Determine inspiral parameters from initial data\n    if id_run_dir is None:\n        id_run_dir = Path(id_input_file_path).resolve().parent\n    if Path(id_input_file_path).name == \"ID_Params.perl\":\n        # Load SpEC initial data (ID_Params.perl)\n        inspiral_params = inspiral_parameters_spec(\n            _load_spec_id_params(Path(id_input_file_path)),\n            id_run_dir,\n        )\n    else:\n        # Load SpECTRE initial data\n        with open(id_input_file_path, \"r\") as open_input_file:\n            id_metadata, id_input_file = yaml.safe_load_all(open_input_file)\n        inspiral_params = inspiral_parameters(\n            id_input_file,\n            id_metadata,\n            id_run_dir,\n            id_subfile_name=id_subfile_name,\n            id_horizons_path=id_horizons_path,\n        )\n\n    # Determine resolution\n    if lev is not None:\n        assert (refinement_level is None) and (polynomial_order is None), (\n            \"The option 'lev' is mutually exclusive with 'refinement_level' and\"\n            \" 'polynomial_order'.\"\n        )\n        selected_lev = INSPIRAL_LEVS[lev]\n        refinement_level = selected_lev[\"refinement_level\"]\n        polynomial_order = selected_lev[\"polynomial_order\"]\n    else:\n        assert (refinement_level is not None) and (\n            polynomial_order is not None\n        ), (\n            \"Resolution not specified. Provide either 'lev' or both\"\n            \" 'refinement_level' and 'polynomial_order'.\"\n        )\n    inspiral_params.update(\n        {\n            \"Lev\": lev,\n            \"L\": refinement_level,\n            \"P\": polynomial_order,\n        }\n    )\n\n    # Set final time for eccentricity control to 2-3 orbits. This can be set\n    # more dynamically in the future.\n    if eccentricity_control:\n        inspiral_params[\"FinalTime\"] = (\n            500 + 5 * np.pi / inspiral_params[\"InitialAngularVelocity\"]\n        )\n\n    logger.debug(f\"Inspiral parameters: {pretty_repr(inspiral_params)}\")\n\n    # Resolve directories\n    if pipeline_dir:\n        pipeline_dir = Path(pipeline_dir).resolve()\n    if continue_with_ringdown:\n        assert pipeline_dir is not None, (\n            \"Specify a '--pipeline-dir' / '-d' to continue with the ringdown\"\n            \" simulation automatically. Don't specify a '--run-dir' / '-o' or\"\n            \" '--segments-dir' / '-O' because it will be created in the\"\n            \" 'pipeline_dir' automatically.\"\n        )\n        assert run_dir is None and segments_dir is None, (\n            \"Specify the '--pipeline-dir' / '-d' rather than '--run-dir' / '-o'\"\n            \" or '--segments-dir' / '-O' when continuing with the ringdown\"\n            \" simulation. Directories for the evolution will be created in the\"\n            \" 'pipeline_dir' automatically.\"\n        )\n    if pipeline_dir and not run_dir and not segments_dir:\n        pipeline_steps = list_pipeline_steps(pipeline_dir)\n        if pipeline_steps:  # Check if the list is not empty\n            segments_dir = pipeline_steps[-1].next(label=\"Inspiral\").path\n        else:\n            segments_dir = PipelineStep.first(\n                directory=pipeline_dir, label=\"Inspiral\"\n            ).path\n\n    # Determine resource allocation\n    if (\n        scheduler_kwargs.get(\"scheduler\") is not None\n        and scheduler_kwargs.get(\"num_procs\") is None\n        and scheduler_kwargs.get(\"num_nodes\") is None\n    ):\n        # By default just run on a fixed number of cores for now. We can\n        # make this smarter later (e.g. scale with the number of elements).\n        scheduler_kwargs[\"num_procs\"] = 180\n\n    # Schedule!\n    return schedule(\n        inspiral_input_file_template,\n        **inspiral_params,\n        **scheduler_kwargs,\n        continue_with_ringdown=continue_with_ringdown,\n        eccentricity_control=eccentricity_control,\n        id_input_file_path=Path(id_input_file_path).resolve(),\n        pipeline_dir=pipeline_dir,\n        run_dir=run_dir,\n        segments_dir=segments_dir,\n    )\n\n\n@click.command(name=\"start-inspiral\", help=start_inspiral.__doc__)\n@click.argument(\n    \"id_input_file_path\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n)\n@click.option(\n    \"-i\",\n    \"--id-run-dir\",\n    type=click.Path(\n        exists=True,\n        file_okay=False,\n        dir_okay=True,\n        readable=True,\n        path_type=Path,\n    ),\n    help=(\n        \"Directory of the initial data run. Paths in the input file are\"\n        \" relative to this directory.\"\n    ),\n    show_default=\"directory of the ID_INPUT_FILE_PATH\",\n)\n@click.option(\n    \"--id-subfile-name\",\n    help=(\n        \"Name of the subfile within the initial data H5 files containing the \"\n        \"volume data to read in. \"\n        \"Optional if the H5 files have only one subfile.\"\n    ),\n)\n@click.option(\n    \"--inspiral-input-file-template\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n    default=INSPIRAL_INPUT_FILE_TEMPLATE,\n    help=\"Input file template for the inspiral.\",\n    show_default=True,\n)\n@click.option(\n    \"--id-horizons-path\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n    default=None,\n    show_default=\"Horizons.h5 inside 'id-run-dir'\",\n    help=(\n        \"H5 file that holds information of the horizons of the ID solve. If\"\n        \" this file does not exist in your ID directory, run 'spectre bbh\"\n        \" postprocess-id' in the ID directory to generate it. Note that this is\"\n        \" not needed if you are starting from a SpEC ID_Params.perl file.\"\n    ),\n)\n@click.option(\n    \"--lev\",\n    type=int,\n    help=(\n        \"Resolution levels defined in terms of h and p refinement. For integer\"\n        \" N, LevN corresponds to refinement level L = 1 and polynomial order P\"\n        \" = 7 + N. Mutually exclusive with options '-L' and '-P'\"\n    ),\n)\n@click.option(\n    \"--refinement-level\",\n    \"-L\",\n    type=int,\n    help=\"h-refinement level.\",\n)\n@click.option(\n    \"--polynomial-order\",\n    \"-P\",\n    type=int,\n    help=\"p-refinement level.\",\n)\n@click.option(\n    \"--continue-with-ringdown\",\n    is_flag=True,\n    help=(\n        \"Continue with the ringdown simulation once a common horizon has\"\n        \" formed.\"\n    ),\n)\n@click.option(\n    \"--eccentricity-control\",\n    is_flag=True,\n    help=(\n        \"Perform eccentricity reduction script that finds current eccentricity\"\n        \"and better guesses for the input orbital parameters.\"\n    ),\n)\n@click.option(\n    \"--pipeline-dir\",\n    \"-d\",\n    type=click.Path(\n        writable=True,\n        path_type=Path,\n    ),\n    help=\"Directory where steps in the pipeline are created.\",\n)\n@scheduler_options\ndef start_inspiral_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    start_inspiral(**kwargs)\n\n\nif __name__ == \"__main__\":\n    start_inspiral_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Pipelines/Bbh/Inspiral.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhBinaryBlackHole\n{{ TargetParams }}\nLev: {{ Lev | default(\"None\") }}\n{% if continue_with_ringdown %}\nNext:\n  Run: spectre.Pipelines.Bbh.Ringdown:start_ringdown\n  With:\n    inspiral_input_file_path: __file__\n    inspiral_run_dir: ./\n    lev: {{ Lev }}\n    pipeline_dir: {{ pipeline_dir }}\n    scheduler: {{ scheduler | default(\"None\") }}\n    copy_executable: {{ copy_executable | default(\"None\") }}\n    submit_script_template: {{ submit_script_template | default(\"None\") }}\n    submit: True\n{% elif eccentricity_control %}\nNext:\n  Run: spectre.Pipelines.Bbh.EccentricityControl:eccentricity_control\n  With:\n    h5_files: \"../Segment_*/BbhReductions.h5\"\n    id_input_file_path: {{ id_input_file_path }}\n    plot_output_dir: ./\n    ecc_params_output_file: \"../EccentricityParams.yaml\"\n    pipeline_dir: {{ pipeline_dir }}\n    # Options for continuing the evolution\n    branch_levs_when_complete: [ {{ Lev }} ]\n    inspiral_input_file_path: __file__\n    inspiral_run_dir: ./\n    inspiral_input_file_template: {{ input_file_template | default(\"None\") }}\n    # Scheduler options\n    scheduler: {{ scheduler | default(\"None\") }}\n    copy_executable: {{ copy_executable | default(\"None\") }}\n    submit_script_template: {{ submit_script_template | default(\"None\") }}\n    submit: True\n{% endif %}\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPointsAndGridSpacing\n\nInitialData:\n{% if SpecDataDirectory is defined %}\n  SpecInitialData:\n    DataDirectory: \"{{ SpecDataDirectory }}\"\n{% else %}\n  NumericInitialData:\n    FileGlob: \"{{ IdFileGlob }}\"\n    Subgroup: \"{{ IdSubfile }}\"\n  {% if IdFromEvolution %}\n    ObservationValue: {{ InitialTime }}\n    ObservationValueEpsilon: Auto\n    ElementsAreIdentical: True\n    Variables:\n      SpacetimeMetric: SpacetimeMetric\n      Pi: Pi\n      Phi: Phi\n  {% else %}\n    ObservationValue: Last\n    ObservationValueEpsilon: Auto\n    ElementsAreIdentical: False\n    Variables:\n      Lapse: Lapse\n      # Load a shift that is not corotating. See `docs/Examples/BbhInitialData`\n      # for details.\n      Shift: ShiftExcess\n      SpatialMetric: SpatialMetric\n      ExtrinsicCurvature: ExtrinsicCurvature\n  {% endif %}\n{% endif %}\n\nDomainCreator:\n  BinaryCompactObject:\n    ObjectA:\n      InnerRadius: &ExcisionRadiusA {{ ExcisionRadiusA }}\n      # Found that the max allowable value for the OuterRadius is 6.0. Higher\n      # values cause the constraints to blow up the cubes around each object.\n      OuterRadius: {{ ObjectOuterRadius }}\n      XCoord: &XCoordA {{ XCoordA }}\n      Interior:\n        ExciseWithBoundaryCondition:\n          DemandOutgoingCharSpeeds:\n      UseLogarithmicMap: true\n    ObjectB:\n      InnerRadius: &ExcisionRadiusB {{ ExcisionRadiusB }}\n      # Found that the max allowable value for the OuterRadius is 6.0. Higher\n      # values cause the constraints to blow up the cubes around each object.\n      OuterRadius: {{ ObjectOuterRadius }}\n      XCoord: &XCoordB {{ XCoordB }}\n      Interior:\n        ExciseWithBoundaryCondition:\n          DemandOutgoingCharSpeeds:\n      UseLogarithmicMap: true\n    CenterOfMassOffset:\n      - &YOffset {{ CenterOfMassOffset_y }}\n      - &ZOffset {{ CenterOfMassOffset_z }}\n    Envelope:\n      Radius: {{ EnvelopeRadius }}\n      # Radial distribution is logarithmic to transition from grid point\n      # spacing around the excisions to the grid point spacing in the\n      # outer shell\n      RadialDistribution: Logarithmic\n    OuterShell:\n      Radius: {{ OuterShellRadius }}\n      # Radial distribution is linear to resolve gravitational waves\n      RadialPartitioning: []\n      RadialDistribution: Linear\n      OpeningAngle: 120.0\n      BoundaryCondition:\n        ConstraintPreservingBjorhus:\n          Type: ConstraintPreservingPhysical\n          IncomingWaveProfile: None\n    UseEquiangularMap: True\n    # Found that a CubeScale of 1.2 worked well for equal mass non-spinning\n    # inspirals and allowed for more common horizon finds.\n    CubeScale: 1.2\n    # These values have been tested to be stable for full equal mass mergers\n    # with constraints ~10^-7 for L=1, P=10\n    InitialRefinement:\n      ObjectAShell:   [{{ L + 1 }}, {{ L + 1 }}, {{ L + 1 }}]\n      ObjectACube:    [{{ L + 2 }}, {{ L + 2 }}, {{ L + 1 }}]\n      ObjectBShell:   [{{ L + 1 }}, {{ L + 1 }}, {{ L + 1 + ExtraRadRef }}]\n      ObjectBCube:    [{{ L + 2 }}, {{ L + 2 }}, {{ L + 1 }}]\n      Envelope:       [{{ L + 1 }}, {{ L + 1 }}, {{ L + 1 }}]\n      OuterShell0:    [{{ L     }}, {{ L     }}, {{ L + 2 }}]\n    InitialGridPoints:\n      ObjectAShell:   [{{ P + 3 }}, {{ P + 3 }}, {{ P + 1 }}]\n      ObjectACube:    [{{ P     }}, {{ P     }}, {{ P - 1 }}]\n      ObjectBShell:   [{{ P + 3 }}, {{ P + 3 }}, {{ P + 1 + ExtraRadPoints }}]\n      ObjectBCube:    [{{ P     }}, {{ P     }}, {{ P - 1 }}]\n      Envelope:       [{{ P + 3 }}, {{ P + 3 }}, {{ P + 5 }}]\n      OuterShell0:    [{{ P + 3 }}, {{ P + 3 }}, {{ P + 4 }}]\n{% if IdFromEvolution %}\n    TimeDependentMaps:\n      InitialTime: &InitialTime {{ InitialTime }}\n      ExpansionMap:\n        H5Filename: \"{{ FotFilename }}\"\n        SubfileName: \"{{ IdSubfile }}\"\n      RotationMap:\n        H5Filename: \"{{ FotFilename }}\"\n        SubfileName: \"{{ IdSubfile }}\"\n      TranslationMap:\n        H5Filename: \"{{ FotFilename }}\"\n        SubfileName: \"{{ IdSubfile }}\"\n      SkewMap:\n        H5Filename: \"{{ FotFilename }}\"\n        SubfileName: \"{{ IdSubfile }}\"\n      ShapeMapA:\n        CoefficientTruncationLimit: 1e-16\n        H5Filename: \"{{ FotFilename }}\"\n        SubfileName: \"{{ IdSubfile }}\"\n        LMax: Auto\n        TransitionEndsAtCube: True\n      ShapeMapB:\n        CoefficientTruncationLimit: 1e-16\n        H5Filename: \"{{ FotFilename }}\"\n        SubfileName: \"{{ IdSubfile }}\"\n        LMax: Auto\n        TransitionEndsAtCube: True\n      GridCenters: None\n{% else %}\n    TimeDependentMaps:\n      InitialTime: &InitialTime 0.0\n      ExpansionMap:\n        InitialValues: [1.0, {{ RadialExpansionVelocity }}, 0.0]\n        AsymptoticVelocityOuterBoundary: -1.0e-6\n        DecayTimescaleOuterBoundary: 50.0\n      RotationMap:\n        InitialAngularVelocity: [0.0, 0.0, {{ InitialAngularVelocity }}]\n      TranslationMap:\n        InitialValues: [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]\n      SkewMap:\n        InitialValuesY: [0.0, 0.0, 0.0]\n        InitialValuesZ: [0.0, 0.0, 0.0]\n      ShapeMapA:\n        LMax: &ShapeLMax 10\n        CoefficientTruncationLimit: 1e-16\n  {% if SpecDataDirectory is defined %}\n        InitialValues: Spherical\n  {% else %}\n        InitialValues:\n          Mass: {{ ExcisionAShapeMass }}\n          Spin:\n            - {{ ExcisionAShapeSpin_x }}\n            - {{ ExcisionAShapeSpin_y }}\n            - {{ ExcisionAShapeSpin_z }}\n  {% endif %}\n        SizeInitialValues: [0.0, 0.0, 0.0]\n        TransitionEndsAtCube: true\n      ShapeMapB:\n        LMax: *ShapeLMax\n        CoefficientTruncationLimit: 1e-16\n  {% if SpecDataDirectory is defined %}\n        InitialValues: Spherical\n  {% else %}\n        InitialValues:\n          Mass: {{ ExcisionBShapeMass }}\n          Spin:\n            - {{ ExcisionBShapeSpin_x }}\n            - {{ ExcisionBShapeSpin_y }}\n            - {{ ExcisionBShapeSpin_z }}\n  {% endif %}\n        SizeInitialValues: [0.0, 0.0, 0.0]\n        TransitionEndsAtCube: true\n      GridCenters: None\n{% endif %}\n\nEvolution:\n  InitialTime: *InitialTime\n  InitialTimeStep: 0.0002\n  MinimumTimeStep: 1e-7\n  # This is the smallest interval we'd need to observe time step/constraints. If\n  # you need it smaller you can edit it, but make sure to change the slab\n  # intervals in the EventsAndTriggersAtSlabs\n  InitialSlabSize: 0.1\n  VariableOrderAlgorithm:\n    GoalOrder: 4\n  LtsStepChoosers:\n    - LimitIncrease:\n        Factor: 2\n    - PreventRapidIncrease\n    - ErrorControl:\n        # Found that 100x smaller timestep tolerances reduced the noise in the\n        # constraints significantly\n        AbsoluteTolerance: 1e-10\n        RelativeTolerance: 1e-8\n        MaxFactor: 2\n        MinFactor: 0.25\n        SafetyFactor: 0.95\n  # Found that order 4 offers a significant speedup compared to order 5\n  TimeStepper:\n    AdamsMoultonPcMonotonic:\n      Order: 4\n\n# Set gauge and constraint damping parameters.\n# The values here are chosen empirically based on values that proved\n# sucessful in SpEC evolutions of binary black holes.\n# Note: Gaussian width = W / sqrt(34.54), so exp(-W^2/w^2) = 1e-15 at x=W,\n# is used in the damped-harmonic gauge parameters.\n# In SpEC, GaugeItems.input set what spectre calls W and spec calls\n# SecondaryWeightRmax. See\n# EvolutionSystems/GeneralizedHarmonic/DampedHarmonicGaugeItems.cpp\n# line 463 in https://github.com/sxs-collaboration/spec for where the Gaussian\n# is actually computed in SpEC.\nEvolutionSystem:\n  GeneralizedHarmonic:\n    GaugeCondition:\n      DampedHarmonic:\n        SpatialDecayWidth: 17.0152695482514 # From SpEC run: 100.0/sqrt(34.54)\n        Amplitudes: [1.0, 0.0, 1.0]         # From SpEC run: damped harmonic\n        Exponents: [2, 2, 2]                # From SpEC run\n    DampingFunctionGamma0: &ConstraintDampingTripleGaussian\n      TimeDependentTripleGaussian:\n        Constant: {{ Gamma0Constant }}\n        Gaussian1:\n          Amplitude: {{ Gamma0RightAmplitude }}\n          Width: {{ Gamma0RightWidth }}\n          Center: [*XCoordA, *YOffset, *ZOffset]\n        Gaussian2:\n          Amplitude: {{ Gamma0LeftAmplitude }}\n          Width:  {{ Gamma0LeftWidth }}\n          Center: [*XCoordB, *YOffset, *ZOffset]\n        Gaussian3:\n          Amplitude: {{ Gamma0OriginAmplitude }}\n          Width: {{ Gamma0OriginWidth }}\n          Center: [0.0, 0.0, 0.0]\n        MovementMethod: ExpansionFactor\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -0.999\n        Amplitude: 0.999\n        Width: {{ Gamma1Width }}\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2: *ConstraintDampingTripleGaussian\n\n# Half power was determined to filter only the last term up to N = 19\n# since we support up to 20 gridpoints.\nFiltering:\n  ExpFilter0:\n    Alpha: 36.0\n    HalfPower: 420\n    Enable: true\n    BlocksToFilter: All\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    AveragedUpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"BbhVolume\"\n  ReductionFileName: \"BbhReductions\"\n  SurfaceFileName: \"BbhSurfaces\"\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      SeparationLessThan:\n        Value: &CommonHorizonActivationSeparation 2.38\n    PhaseChanges:\n      # Terminal completion path: Evolve -> WriteCheckpoint -> Exit.\n      - BbhCheckpointAndExitIfComplete\n{% if segments_dir is defined %}\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          # Trigger checkpoint often enough so it triggers within the last 30\n          # minutes of the run\n          Interval: 10\n          Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: 23.5\n{% endif %}\n\nEventsAndTriggersAtCheckpoints:\n  - Trigger: Always\n    Events:\n      - ObserveFields:\n          SubfileName: Checkpoints\n          VariablesToObserve:\n            - SpacetimeMetric\n            - Pi\n            - Phi\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nEventsAndTriggersAtSlabs:\n  # Observe time step and cheap constraints every slab\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - ObserveTimeStep:\n          SubfileName: TimeSteps\n          PrintTimeToTerminal: True\n          ObservePerCore: False\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n          - Name: Lapse\n            NormType: L2Norm\n            Components: Individual\n          - Name: PointwiseL2Norm(GaugeConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(ThreeIndexConstraint)\n            NormType: L2Norm\n            Components: Sum\n  # Observe constraint energy more sparsely because it is expensive to compute.\n  # Also use it for ErrorIfDataTooBig so we only compute it once.\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 100\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: ConstraintEnergy\n          TensorsToObserve:\n          - Name: ConstraintEnergy\n            NormType: L2Norm\n            Components: Sum\n      - ErrorIfDataTooBig:\n          Threshold: 10\n          VariablesToCheck: [ConstraintEnergy]\n      - ErrorIfDataTooBig:\n          Threshold: 100\n          VariablesToCheck: [SpacetimeMetric]\n  # Observe apparent horizons often enough so they find the next horizon based\n  # on the previous initial guess. This is an issue with the AH finders not\n  # sharing initial guesses and needs to be fixed.\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 0\n    Events:\n      - ObservationAhA\n      - ObservationAhB\n      - ObservationExcisionBoundaryA\n      - ObservationExcisionBoundaryB\n  # Observe volume data for visualization and debugging\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2000\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            # For diagnostics:\n            - ConstraintEnergy\n            - PointwiseL2Norm(GaugeConstraint)\n            - PointwiseL2Norm(ThreeIndexConstraint)\n            # For visualization:\n            - Lapse\n            - SpatialRicciScalar\n            - Psi4Real\n          InterpolateToMesh: None\n          # Save disk space by saving single precision data. This is enough\n          # for visualization.\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Float\n          FloatingPointTypes: [Float]\n          BlocksToObserve: All\n# Trigger to find common horizon and gather volume data needed for ringdown.\n  - Trigger:\n      SeparationLessThan:\n        Value: *CommonHorizonActivationSeparation\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 0\n          StepChoosers:\n            - Constant: 0.01\n      - BbhCheckConstraintThresholds\n      - ObservationAhC:\n          SubfileName: ForContinuation\n          VariablesToObserve:\n            - SpacetimeMetric\n            - Pi\n            - Phi\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          # This volume data is for ringdown, so double precision is needed.\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n{% if eccentricity_control %}\n  # If running eccentricity control, only run short inspiral\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: {{ FinalTime }}\n    Events:\n      # Save volume data for continuation\n      - ObserveFields:\n          SubfileName: PostJunkVolumeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            - Pi\n            - Phi\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n      - Completion\n{% elif FinalTime is defined %}\n  # Support running only to a specified final time\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: {{ FinalTime }}\n    Events:\n      - Completion\n{% endif %}\n  # Discard old data to reduce memory use\n  - Trigger: Always\n    Events:\n      - CleanFunctionsOfTime\n\nEventsAndTriggersAtSteps:\n\nEventsAndDenseTriggers:\n  # BondiSachs output needs to be often enough for CCE to run properly. An\n  # interval of 0.1 was found to work well in SpEC.\n  - Trigger:\n      Times:\n        EvenlySpaced:\n          Interval: 0.1\n          Offset: 0.0\n    Events:\n      - BondiSachsInterpolation\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n    - ObserveTimeStepVolume:\n        SubfileName: FailureTimeStep\n        FloatingPointType: Double\n        CoordinatesFloatingPointType: Float\n\nInterpolator:\n  Verbosity: Silent\n\nBbhCompletionCriteria:\n  MinCommonHorizonSuccessesBeforeChecks: 8\n  MaxCommonHorizonSuccesses: 100\n  GaugeConstraintLinfThreshold: 1.0\n  ThreeIndexConstraintLinfThreshold: 1.0\n  CommonHorizonLMaxThreshold: 8\n  ConstraintCheckVerbose: false\n\nApparentHorizons:\n  LMax: 33\n  ObservationAhA: &AhA\n    Criteria:\n    InitialGuess:\n      LMax: &ObsLMax 10\n      Radius: {{ ExcisionRadiusA * 1.5 }}\n      Center: [*XCoordA, *YOffset, *ZOffset]\n    FastFlow: &DefaultFastFlow\n      Flow: Fast\n      Alpha: 1.0\n      Beta: 0.5\n      AbsTol: 1e-12\n      TruncationTol: 1e-2\n      DivergenceTol: 1.2\n      DivergenceIter: 5\n      MaxIts: 100\n    Verbosity: Quiet\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: [\"ObjectAShell\", \"ObjectACube\"]\n  ObservationAhB: &AhB\n    Criteria:\n    InitialGuess:\n      LMax: *ObsLMax\n      Radius: {{ ExcisionRadiusB * 1.5 }}\n      Center: [*XCoordB, *YOffset, *ZOffset]\n    FastFlow: *DefaultFastFlow\n    Verbosity: Quiet\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: [\"ObjectBShell\", \"ObjectBCube\"]\n  ObservationAhC:\n    Criteria:\n    InitialGuess:\n      LMax: 33\n      Radius: 10.0\n      Center: [0.0, 0.0, 0.0]\n    FastFlow: *DefaultFastFlow\n    Verbosity: Quiet\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind:\n      - \"ObjectAShell\"\n      - \"ObjectACube\"\n      - \"ObjectBShell\"\n      - \"ObjectBCube\"\n      - \"Envelope\"\n  ControlSystemAhA: *AhA\n  ControlSystemAhB: *AhB\n  ControlSystemCharSpeedAhA: *AhA\n  ControlSystemCharSpeedAhB: *AhB\n\nInterpolationTargets:\n  BondiSachsInterpolation:\n    LMax: 16\n    Radius: [100, 150, 200]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n  ObservationExcisionBoundaryA: &ExBdryA\n    LMax: *ObsLMax\n    Center: [*XCoordA, *YOffset, *ZOffset]\n    Radius: *ExcisionRadiusA\n    AngularOrdering: \"Strahlkorper\"\n  ObservationExcisionBoundaryB: &ExBdryB\n    LMax: *ObsLMax\n    Center: [*XCoordB, *YOffset, *ZOffset]\n    Radius: *ExcisionRadiusB\n    AngularOrdering: \"Strahlkorper\"\n  ControlSystemCharSpeedExcisionA: *ExBdryA\n  ControlSystemCharSpeedExcisionB: *ExBdryB\n\nControlSystems:\n  WriteDataToDisk: true\n  MeasurementsPerUpdate: 4\n  DelayUpdate: true\n  Verbosity: Silent\n  Expansion: &KinematicControlSystem\n    Averager: &KinematicAverager\n      AverageTimescaleFraction: &AvgTimescaleFrac 0.25\n      Average0thDeriv: false\n    Controller: &KinematicController\n      # Changed UpdateFraction from 0.03 to 0.3 to increase run speed\n      UpdateFraction: 0.3\n    TimescaleTuner: &KinematicTuner\n      InitialTimescales: {{ KinematicTimescale }}\n      MinTimescale: &MinTimescale {{ MinKinematicTimescale }}\n      MaxTimescale: &MaxTimescale {{ MaxDampingTimescale }}\n      IncreaseThreshold: &IncreaseThreshold {{ IncreaseThreshold }}\n      DecreaseThreshold: &DecreaseThreshold {{ DecreaseThreshold }}\n      IncreaseFactor: &IncreaseFactor 1.01\n      DecreaseFactor: &DecreaseFactor 0.98\n    ControlError:\n  Rotation: *KinematicControlSystem\n  Translation: *KinematicControlSystem\n  Skew:\n    Averager: *KinematicAverager\n    Controller: *KinematicController\n    TimescaleTuner:\n      InitialTimescales: {{ SkewTimescale }}\n      MinTimescale: {{ MinSkewTimescale }}\n      MaxTimescale: *MaxTimescale\n      IncreaseThreshold: *IncreaseThreshold\n      DecreaseThreshold: *DecreaseThreshold\n      IncreaseFactor: *IncreaseFactor\n      DecreaseFactor: *DecreaseFactor\n    ControlError:\n  ShapeA:\n    Averager: &SizeShapeAverager\n      AverageTimescaleFraction: *AvgTimescaleFrac\n      Average0thDeriv: true\n    Controller: *KinematicController\n    TimescaleTuner:\n      InitialTimescales: {{ ShapeATimescale }}\n      MinTimescale: *MinTimescale\n      MaxTimescale: *MaxTimescale\n      IncreaseThreshold: *IncreaseThreshold\n      DecreaseThreshold: *DecreaseThreshold\n      IncreaseFactor: *IncreaseFactor\n      DecreaseFactor: *DecreaseFactor\n    ControlError:\n  ShapeB:\n    Averager: *SizeShapeAverager\n    Controller: *KinematicController\n    TimescaleTuner:\n      InitialTimescales: {{ ShapeBTimescale }}\n      MinTimescale: *MinTimescale\n      MaxTimescale: *MaxTimescale\n      IncreaseThreshold: *IncreaseThreshold\n      DecreaseThreshold: *DecreaseThreshold\n      IncreaseFactor: *IncreaseFactor\n      DecreaseFactor: *DecreaseFactor\n    ControlError:\n  SizeA:\n    Averager: *SizeShapeAverager\n    Controller: &SizeController\n      UpdateFraction: 0.2\n    TimescaleTuner:\n      InitialTimescales: &SizeATimescale {{ SizeATimescale }}\n      MinTimescale: &SizeMinTimescale 1.0e-4\n      MaxTimescale: {{ SizeAMaxTimescale }}\n      IncreaseThreshold: &SizeIncreaseThreshold {{ SizeIncreaseThreshold }}\n      IncreaseFactor: *IncreaseFactor\n    ControlError:\n      MaxNumTimesForZeroCrossingPredictor: 4\n      SmoothAvgTimescaleFraction: 0.25\n      DeltaRDriftOutwardOptions: None\n      DeltaRDriftInwardOptions: None\n      InitialState: Initial\n      SmootherTuner:\n        InitialTimescales: *SizeATimescale\n        MinTimescale: *MinTimescale\n        MaxTimescale: *MaxTimescale\n        IncreaseThreshold: *IncreaseThreshold\n        DecreaseThreshold: *DecreaseThreshold\n        IncreaseFactor: *IncreaseFactor\n        DecreaseFactor: *DecreaseFactor\n  SizeB:\n    Averager: *SizeShapeAverager\n    Controller: *SizeController\n    TimescaleTuner:\n      InitialTimescales: &SizeBTimescale {{ SizeBTimescale }}\n      MinTimescale: *SizeMinTimescale\n      MaxTimescale: {{ SizeBMaxTimescale }}\n      IncreaseThreshold: *SizeIncreaseThreshold\n      IncreaseFactor: *IncreaseFactor\n    ControlError:\n      MaxNumTimesForZeroCrossingPredictor: 4\n      SmoothAvgTimescaleFraction: 0.25\n      DeltaRDriftOutwardOptions: None\n      DeltaRDriftInwardOptions: None\n      InitialState: Initial\n      SmootherTuner:\n        InitialTimescales: *SizeBTimescale\n        MinTimescale: *MinTimescale\n        MaxTimescale: *MaxTimescale\n        IncreaseThreshold: *IncreaseThreshold\n        DecreaseThreshold: *DecreaseThreshold\n        IncreaseFactor: *IncreaseFactor\n        DecreaseFactor: *DecreaseFactor\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "support/Pipelines/Bbh/PostprocessId.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport glob\nimport logging\nfrom pathlib import Path\nfrom typing import List, Optional, Union\n\nimport click\nimport yaml\n\nfrom spectre.IO.H5 import open_volfiles, select_observation\nfrom spectre.Pipelines.Bbh.ControlId import (\n    DEFAULT_CONTROL_DELAY,\n    DEFAULT_MAX_ITERATIONS,\n    DEFAULT_RESIDUAL_TOLERANCE,\n    TargetParams,\n    control_id,\n)\nfrom spectre.Pipelines.Bbh.FindHorizon import (\n    find_horizon,\n    use_excision_as_horizon,\n    vec_to_string,\n)\nfrom spectre.SphericalHarmonics import Frame, Strahlkorper\nfrom spectre.support.Schedule import schedule, scheduler_options\nfrom spectre.Visualization.ReadInputFile import find_event\n\nlogger = logging.getLogger(__name__)\n\n\ndef postprocess_id(\n    id_input_file_path: Union[str, Path],\n    id_run_dir: Optional[Union[str, Path]] = None,\n    horizon_l_max: int = 16,\n    horizons_file: Optional[Union[str, Path]] = None,\n    control: bool = True,\n    control_residual_tolerance: float = DEFAULT_RESIDUAL_TOLERANCE,\n    control_max_iterations: int = DEFAULT_MAX_ITERATIONS,\n    control_refinement_level: int = 1,\n    control_polynomial_order: int = 6,\n    control_delay: int = DEFAULT_CONTROL_DELAY,\n    control_params: List[TargetParams] = [],\n    evolve: bool = False,\n    eccentricity_control: bool = False,\n    negative_expansion_bc: bool = True,\n    pipeline_dir: Optional[Union[str, Path]] = None,\n    **scheduler_kwargs,\n):\n    \"\"\"Postprocess initial data after generation.\n\n    This function is called automatically after the initial data has been\n    generated (see the 'Next' section in the 'InitialData.yaml' input file), or\n    manually by pointing the ID_INPUT_FILE_PATH to the input file of the initial\n    data run. Also specify 'id_run_dir' if the initial data was run in a\n    different directory than where the input file is.\n\n    This function does the following:\n\n    - Find apparent horizons in the data to determine quantities like the masses\n      and spins of the black holes. These quantities are stored in the given\n      'horizons_file' in subfiles 'Ah{A,B}.dat'. In addition, the horizon\n      surface coordinates and coefficients are written to the 'horizons_file' in\n      subfiles 'Ah{A,B}/Coordinates' and 'Ah{A,B}/Coefficients'.\n\n    - If 'control' is set to True, run a control loop such that masses and\n      spins of the horizons match the input parameters. See ControlId.py for\n      details.\n\n    - Start the inspiral if 'evolve' is set to True.\n\n    Arguments:\n      id_input_file_path: Path to the input file of the initial data run.\n      id_run_dir: Directory of the initial data run. Paths in the input file are\n        relative to this directory. If not provided, the directory of the input\n        file is used.\n      horizon_l_max: Maximum l-mode for the horizon search.\n      horizons_file: Path to the file where the horizon data is written to.\n        Default is 'Horizons.h5' in the 'id_run_dir'.\n      control: Control BBH physical parameters (default: True).\n      control_residual_tolerance: Residual tolerance used for control.\n      control_max_iterations: Maximum of iterations allowed for control.\n      control_refinement_level: h-refinement used for control.\n      control_polynomial_order: p-refinement used for control.\n      control_delay: Numer of iterations before control of delayed parameters\n        starts. See ControlId.py for details.\n      control_params: List of parameters to control. See ControlId.py\n        for details.\n      evolve: Evolve the initial data after postprocessing (default: False).\n      negative_expansion_bc: Place the excisions inside of apparent horizons.\n      pipeline_dir: Directory where steps in the pipeline are created.\n        Required if 'evolve' is set to True.\n    \"\"\"\n    # Read input file\n    with open(id_input_file_path, \"r\") as open_input_file:\n        id_metadata, id_input_file = yaml.safe_load_all(open_input_file)\n    target_params = id_metadata[\"TargetParams\"]\n    x_B, x_A = id_input_file[\"Background\"][\"Binary\"][\"XCoords\"]\n    y_offset, z_offset = id_input_file[\"Background\"][\"Binary\"][\n        \"CenterOfMassOffset\"\n    ]\n    id_domain = id_input_file[\"DomainCreator\"][\"BinaryCompactObject\"]\n    excision_radius_A = id_domain[f\"ObjectA\"][\"InnerRadius\"]\n    excision_radius_B = id_domain[f\"ObjectB\"][\"InnerRadius\"]\n    volfile_name = id_input_file[\"Observers\"][\"VolumeFileName\"]\n    id_subfile_name = find_event(\n        \"ObserveFields\", \"EventsAndTriggersAtIterations\", id_input_file\n    )[\"SubfileName\"]\n\n    # Find latest observation in output data\n    if id_run_dir is None:\n        id_run_dir = Path(id_input_file_path).resolve().parent\n    id_volfiles = glob.glob(str(Path(id_run_dir) / (volfile_name + \"*.h5\")))\n    obs_id, _ = select_observation(\n        open_volfiles(id_volfiles, id_subfile_name), step=-1\n    )\n\n    # Find horizons and write to the output file\n    if not horizons_file:\n        horizons_file = Path(id_run_dir) / \"Horizons.h5\"\n    for object_label, xcoord, excision_radius in zip(\n        [\"AhA\", \"AhB\"], [x_A, x_B], [excision_radius_A, excision_radius_B]\n    ):\n        if negative_expansion_bc:\n            _, horizon_quantities = find_horizon(\n                id_volfiles,\n                subfile_name=id_subfile_name,\n                obs_id=obs_id,\n                obs_time=0.0,\n                initial_guess=Strahlkorper[Frame.Inertial](\n                    l_max=horizon_l_max,\n                    radius=excision_radius * 1.5,\n                    center=[xcoord, y_offset, z_offset],\n                ),\n                output_surfaces_file=horizons_file,\n                output_coeffs_subfile=f\"{object_label}/Coefficients\",\n                output_coords_subfile=f\"{object_label}/Coordinates\",\n                output_reductions_file=horizons_file,\n                output_quantities_subfile=object_label,\n            )\n        else:\n            _, horizon_quantities = use_excision_as_horizon(\n                id_volfiles,\n                subfile_name=id_subfile_name,\n                obs_id=obs_id,\n                obs_time=0.0,\n                l_max=horizon_l_max,\n                radius=excision_radius,\n                center=[xcoord, y_offset, z_offset],\n                output_reductions_file=horizons_file,\n                output_quantities_subfile=object_label,\n            )\n        logger.info(\n            f\"{object_label} has mass\"\n            f\" {horizon_quantities['ChristodoulouMass']:g} and spin\"\n            f\" {vec_to_string(horizon_quantities['DimensionlessSpinVector'])}.\"\n        )\n    logger.info(f\"Horizons found and written to {horizons_file}.\")\n\n    if control:\n        last_control_run_dir = control_id(\n            id_input_file_path=id_input_file_path,\n            control_params=control_params,\n            id_run_dir=id_run_dir,\n            residual_tolerance=control_residual_tolerance,\n            max_iterations=control_max_iterations,\n            control_delay=control_delay,\n            refinement_level=control_refinement_level,\n            polynomial_order=control_polynomial_order,\n            negative_expansion_bc=negative_expansion_bc,\n        )\n        id_run_dir = last_control_run_dir\n        id_input_file_path = f\"{last_control_run_dir}/InitialData.yaml\"\n\n    # Start the inspiral from the ID if requested\n    if evolve:\n        from spectre.Pipelines.Bbh.Inspiral import start_inspiral\n\n        start_inspiral(\n            id_input_file_path,\n            id_run_dir=id_run_dir,\n            id_subfile_name=\"VolumeData\",\n            continue_with_ringdown=not eccentricity_control,\n            eccentricity_control=eccentricity_control,\n            pipeline_dir=pipeline_dir,\n            lev=target_params[\"EvolutionLev\"],\n            **scheduler_kwargs,\n        )\n\n\n@click.command(name=\"postprocess-id\", help=postprocess_id.__doc__)\n@click.argument(\n    \"id_input_file_path\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n)\n@click.option(\n    \"-i\",\n    \"--id-run-dir\",\n    type=click.Path(\n        exists=True,\n        file_okay=False,\n        dir_okay=True,\n        readable=True,\n        path_type=Path,\n    ),\n    help=(\n        \"Directory of the initial data run. Paths in the input file are\"\n        \" relative to this directory.\"\n    ),\n    show_default=\"directory of the ID_INPUT_FILE_PATH\",\n)\n@click.option(\n    \"--control\",\n    default=True,\n    show_default=True,\n    help=\"Control BBH physical parameters during postprocessing.\",\n)\n@click.option(\n    \"--evolve\",\n    is_flag=True,\n    help=\"Evolve the initial data after postprocessing.\",\n)\n@click.option(\n    \"--pipeline-dir\",\n    \"-d\",\n    type=click.Path(writable=True, path_type=Path),\n    help=\"Directory where steps in the pipeline are created.\",\n)\n@click.option(\n    \"--horizon-l-max\",\n    type=click.IntRange(0, None),\n    help=\"Maximum l-mode for the horizon search.\",\n    default=16,\n    show_default=True,\n)\n@click.option(\n    \"--horizons-file\",\n    type=click.Path(writable=True, path_type=Path),\n    help=\"Path to the file where the horizon data is written to.\",\n    show_default=\"Horizons.h5 in the ID_RUN_DIR\",\n)\n@scheduler_options\ndef postprocess_id_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    postprocess_id(**kwargs)\n\n\nif __name__ == \"__main__\":\n    postprocess_id_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Pipelines/Bbh/Ringdown.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\nfrom typing import Optional, Union\n\nimport click\nimport numpy as np\nimport yaml\nfrom rich.pretty import pretty_repr\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Evolution.Ringdown.ComputeRingdownShapeAndTranslationFoT import (\n    compute_ringdown_shape_and_translation_fot,\n)\n\n# next import out of order to avoid Unrecognized PUP::able::PUP_ID error\nfrom spectre.IO.H5.FunctionsOfTimeFromVolume import (\n    functions_of_time_from_volume,\n)\nfrom spectre.support.DirectoryStructure import PipelineStep, list_pipeline_steps\nfrom spectre.support.Schedule import schedule, scheduler_options\n\nlogger = logging.getLogger(__name__)\n\nRINGDOWN_INPUT_FILE_TEMPLATE = Path(__file__).parent / \"Ringdown.yaml\"\n\n# Resolution levels defined in terms of p-refinement\n# To be replaced once AMR is used.\nRINGDOWN_LEVS = {\n    lev_number: {\n        \"label\": f\"Lev{lev_number}\",\n        \"refinement_level\": 2,\n        \"polynomial_order\": 10 + lev_number,\n    }\n    for lev_number in range(-2, 11)\n}\n\n\ndef ringdown_parameters(\n    inspiral_input_file: dict,\n    inspiral_metadata: dict,\n    inspiral_run_dir: Union[str, Path],\n    fot_vol_subfile: str,\n) -> dict:\n    \"\"\"Determine ringdown parameters from the inspiral.\n\n    These parameters fill the 'RINGDOWN_INPUT_FILE_TEMPLATE'.\n\n    Arguments:\n      inspiral_input_file: Inspiral input file as a dictionary.\n      id_run_dir: Directory of the inspiral run. Paths in the input file\n        are relative to this directory.\n      fot_vol_subfile: Subfile in volume data used for Functions of Time.\n      refinement_level: h-refinement level.\n      polynomial_order: p-refinement level.\n    \"\"\"\n    return {\n        # Initial data files\n        \"IdFileGlob\": str(\n            Path(inspiral_run_dir).resolve()\n            / (inspiral_input_file[\"Observers\"][\"VolumeFileName\"] + \"*.h5\")\n        ),\n        \"IdFileGlobSubgroup\": fot_vol_subfile,\n        # Store target parameters in the input file\n        \"TargetParams\": yaml.safe_dump(\n            {\"TargetParams\": inspiral_metadata[\"TargetParams\"]}\n        ).strip(),\n    }\n\n\ndef start_ringdown(\n    inspiral_run_dir: Union[str, Path],\n    number_of_ahc_finds_for_fit: int,\n    match_time: float,\n    settling_timescale: float,\n    zero_coefs_eps: float,\n    lev: Optional[int],\n    refinement_level: Optional[int],\n    polynomial_order: Optional[int],\n    inspiral_input_file: Optional[Union[str, Path]] = None,\n    ahc_reductions_path: Optional[Union[str, Path]] = None,\n    ahc_subfile: str = \"ObservationAhC_Ylm\",\n    fot_vol_h5_path: Optional[Union[str, Path]] = None,\n    fot_vol_subfile: str = \"ForContinuation\",\n    path_to_output_h5: Optional[Union[str, Path]] = None,\n    output_subfile_prefix: str = \"Distorted\",\n    ringdown_input_file_template: Union[\n        str, Path\n    ] = RINGDOWN_INPUT_FILE_TEMPLATE,\n    pipeline_dir: Optional[Union[str, Path]] = None,\n    run_dir: Optional[Union[str, Path]] = None,\n    segments_dir: Optional[Union[str, Path]] = None,\n    **scheduler_kwargs,\n):\n    \"\"\"Schedule a ringdown simulation from the inspiral.\n\n    Point the inspiral_run_dir to the last inspiral segment. Also specify\n    'inspiral_input_file' if the simulation was run in a different directory\n    than where the input file is. Parameters for the ringdown will be determined\n    from the inspiral and inserted into the\n    'ringdown_input_file_template'. The remaining options are forwarded to the\n    'schedule' command. See 'schedule' docs for details.\n\n    Here 'parameters for the ringdown' includes the information needed to\n    initialize the time-dependent maps, including the shape map. Common horizon\n    shape coefficients in the ringdown distorted frame will be written to\n    disk that the ringdown input file will point to.\n\n    Arguments:\n        inspiral_run_dir: Path to the last segment in the inspiral run\n        directory.\n        number_of_ahc_finds_for_fit: The number of ahc finds that will be used\n        in the fit.\n        match_time: The time to match the time dependent maps at.\n        settling_timescale: The settling timescale for the rotation and\n        expansion maps.\n        zero_coefs_eps: \"If the sum of a given coefficient over all\n        'number_of_ahc_finds_for_fit' is less than 'zero_coefs_eps', set that\n        coefficient to 0.0 exactly.\"\n        refinement_level: The initial h refinement level for ringdown.\n        polynomial_order: The initial p refinement level for ringdown.\n        inspiral_input_file: The input file used for during the Inspiral,\n        defaults to the Inspiral.yaml inside the inspiral_run_dir.\n        ahc_reductions_path: The full path to the BbhReductions file that\n        contains AhC data, defaults to BbhReductions.h5 in the inspiral_run_dir.\n        ahc_subfile: Subfile containing reduction data at times of AhC finds,\n        defaults to 'ObservationAhC_Ylm'.\n        fot_vol_h5_path: The full path to any volume data containing the\n        functions of time at the time of AhC finds, defaults to BbhVolume0.h5 in\n        the inspiral_run_dir.\n        fot_vol_subfile: Subfile containing volume data at times of AhC finds,\n        defaults to 'ForContinuation'.\n        path_to_output_h5: H5 file to output horizon coefficients needed for\n        Ringdown.\n        output_subfile_prefix: Subfile prefix for output data, defaults to\n        'Distorted'.\n        ringdown_input_file_template: Yaml to insert ringdown coefficients into.\n    \"\"\"\n    logger.warning(\n        \"The BBH pipeline is still experimental. Please review the\"\n        \" generated input files. In particular, the ringdown BBH pipline has\"\n        \" been tested for q=1, q=2, spin=0 inspirals but does not\"\n        \" yet support choosing an excision radius automatically.\"\n    )\n    # Determine ringdown parameters from inspiral\n    # Resolve and set correct files/paths.\n    if inspiral_input_file is None:\n        inspiral_input_file = inspiral_run_dir / \"Inspiral.yaml\"\n\n    if ahc_reductions_path is None:\n        ahc_reductions_path = inspiral_run_dir / \"BbhReductions.h5\"\n\n    if fot_vol_h5_path is None:\n        fot_vol_h5_path = inspiral_run_dir / \"BbhVolume0.h5\"\n\n    with open(inspiral_input_file, \"r\") as open_input_file:\n        inspiral_metadata, inspiral_input_file = yaml.safe_load_all(\n            open_input_file\n        )\n\n    # Resolve directories\n    if pipeline_dir:\n        pipeline_dir = Path(pipeline_dir).resolve()\n    if pipeline_dir and not segments_dir and not run_dir:\n        pipeline_steps = list_pipeline_steps(pipeline_dir)\n        if pipeline_steps:\n            segments_dir = pipeline_steps[-1].next(label=\"Ringdown\").path\n        else:\n            segments_dir = PipelineStep.first(\n                directory=pipeline_dir, label=\"Ringdown\"\n            ).path\n\n    if path_to_output_h5 == None:\n        ringdown_dir = Path(segments_dir or run_dir)\n        ringdown_dir.mkdir(parents=True, exist_ok=True)\n        path_to_output_h5 = (\n            Path(ringdown_dir).resolve() / \"RingdownShapeCoefs.h5\"\n        )\n    else:\n        path_to_output_h5 = Path(path_to_output_h5).resolve()\n\n    ringdown_params = ringdown_parameters(\n        inspiral_input_file,\n        inspiral_metadata,\n        inspiral_run_dir,\n        fot_vol_subfile=fot_vol_subfile,\n    )\n\n    # Determine resolution\n    if lev is not None:\n        assert (refinement_level is None) and (polynomial_order is None), (\n            \"The option 'lev' is mutually exclusive with 'refinement_level' and\"\n            \" 'polynomial_order'.\"\n        )\n        selected_lev = RINGDOWN_LEVS[lev]\n        refinement_level = selected_lev[\"refinement_level\"]\n        polynomial_order = selected_lev[\"polynomial_order\"]\n    else:\n        assert (refinement_level is not None) and (\n            polynomial_order is not None\n        ), (\n            \"Resolution not specified. Provide either 'lev' or both\"\n            \" 'refinement_level' and 'polynomial_order'.\"\n        )\n    ringdown_params.update(\n        {\n            \"Lev\": lev,\n            \"L\": refinement_level,\n            \"P\": polynomial_order,\n        }\n    )\n\n    evaluated_fot_dict = functions_of_time_from_volume(\n        fot_vol_h5_path=str(fot_vol_h5_path),\n        fot_vol_subfile=fot_vol_subfile,\n        match_time=match_time,\n    )\n    match_time = evaluated_fot_dict[\"MatchTime\"]\n\n    # This section checks for functions of time in the dictionary. It also\n    # alters some values in the evaluated functions of time dictionary. The\n    # ringdown only requires the outer boundary expansion map so here we set the\n    # inner expansion map to the identity (this should be changed to a settle to\n    # const at a later time). We also set the translation map to 0 since\n    # translation is not supported in the transition to ringdown yet.\n    if \"ExpansionOuterBoundary\" not in evaluated_fot_dict:\n        raise ValueError(\n            f\"The transition to ringdown script requires the \"\n            f\" ExpansionOuterBoundary functions of time to be valid at the\"\n            f\" match time. \"\n        )\n    if \"Rotation\" not in evaluated_fot_dict:\n        evaluated_fot_dict[\"Rotation\"] = [\n            [1.0, 0.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0, 0.0],\n        ]\n    evaluated_fot_dict[\"Expansion\"] = [1.0, 0.0, 0.0]\n    # This was added so we don't try to read in translation function of time\n    # history if there was no translation map in the inspiral.\n    if \"Translation\" not in evaluated_fot_dict:\n        evaluated_fot_dict[\"Translation\"] = None\n\n    (\n        ringdown_ylm_coefs,\n        ringdown_ylm_legend,\n        ahc_translation_fot,\n    ) = compute_ringdown_shape_and_translation_fot(\n        path_to_volume_data=fot_vol_h5_path,\n        volume_subfile_name=fot_vol_subfile,\n        ahc_reductions_path=str(ahc_reductions_path),\n        ahc_subfile=ahc_subfile,\n        evaluated_fot_dict=evaluated_fot_dict,\n        number_of_ahc_finds_for_fit=number_of_ahc_finds_for_fit,\n        match_time=match_time,\n        settling_timescale=settling_timescale,\n        zero_coefs_eps=zero_coefs_eps,\n    )\n\n    # Setting up and writing the distorted coefficients output file.\n    output_subfile_ahc = output_subfile_prefix + \"AhC_Ylm\"\n    output_subfile_dt_ahc = output_subfile_prefix + \"dtAhC_Ylm\"\n    output_subfile_dt2_ahc = output_subfile_prefix + \"dt2AhC_Ylm\"\n\n    with spectre_h5.H5File(file_name=path_to_output_h5, mode=\"a\") as h5file:\n        ahc_datfile = h5file.insert_dat(\n            path=\"/\" + output_subfile_ahc,\n            legend=ringdown_ylm_legend[0],\n            version=0,\n        )\n        ahc_datfile.append(ringdown_ylm_coefs[0])\n        h5file.close_current_object()\n        ahc_dt_datfile = h5file.insert_dat(\n            path=\"/\" + output_subfile_dt_ahc,\n            legend=ringdown_ylm_legend[1],\n            version=0,\n        )\n        ahc_dt_datfile.append(ringdown_ylm_coefs[1])\n        h5file.close_current_object()\n        ahc_dt2_datfile = h5file.insert_dat(\n            path=\"/\" + output_subfile_dt2_ahc,\n            legend=ringdown_ylm_legend[2],\n            version=0,\n        )\n        ahc_dt2_datfile.append(ringdown_ylm_coefs[2])\n    logger.debug(\"Obtained ringdown coefs\")\n    # Print out coefficients for insertion into BBH domain\n    logger.debug(\"Expansion: \" + str(evaluated_fot_dict[\"Expansion\"]))\n    logger.debug(\n        \"ExpansionOutrBdry: \"\n        + str(evaluated_fot_dict[\"ExpansionOuterBoundary\"])\n    )\n    logger.debug(\"Rotation: \" + str(evaluated_fot_dict[\"Rotation\"]))\n    logger.debug(\"Match time: \" + str(match_time))\n    logger.debug(\"Settling timescale: \" + str(settling_timescale))\n    logger.debug(\"Lmax: \" + str(int(ringdown_ylm_coefs[0][4])))\n\n    ringdown_params[\"MatchTime\"] = match_time\n    ringdown_params[\"ShapeMapLMax\"] = int(ringdown_ylm_coefs[0][4])\n    ringdown_params[\"PathToAhCCoefsH5File\"] = path_to_output_h5\n    ringdown_params[\"AhCCoefsSubfilePrefix\"] = output_subfile_prefix\n    ringdown_params[\"Rotation\"] = yaml.safe_dump(\n        evaluated_fot_dict[\"Rotation\"],\n        default_flow_style=True,\n        width=float(\"inf\"),\n    ).strip()\n    ringdown_params[\"ExpansionOuterBdry\"] = yaml.safe_dump(\n        evaluated_fot_dict[\"ExpansionOuterBoundary\"],\n        default_flow_style=True,\n        width=float(\"inf\"),\n    ).strip()\n    ringdown_params[\"Translation\"] = yaml.safe_dump(\n        ahc_translation_fot,\n        default_flow_style=True,\n        width=float(\"inf\"),\n    ).strip()\n\n    ringdown_params[\"OuterBdryRadius\"] = inspiral_input_file[\"DomainCreator\"][\n        \"BinaryCompactObject\"\n    ][\"OuterShell\"][\"Radius\"]\n    # Give the black hole 200M to relax, and then the light travel time\n    # to the outer boundary for the gravitational waves to leave the domain\n    ringdown_params[\"FinalTime\"] = (\n        match_time + ringdown_params[\"OuterBdryRadius\"] + 200.0\n    )\n    logger.debug(f\"Ringdown parameters: {pretty_repr(ringdown_params)}\")\n\n    # Determine resource allocation\n    if (\n        scheduler_kwargs.get(\"scheduler\") is not None\n        and scheduler_kwargs.get(\"num_procs\") is None\n        and scheduler_kwargs.get(\"num_nodes\") is None\n    ):\n        # By default just run on a fixed number of cores for now. We can\n        # make this smarter later (e.g. scale with the number of elements).\n        scheduler_kwargs[\"num_procs\"] = 180\n\n    # Schedule!\n    return schedule(\n        ringdown_input_file_template,\n        **ringdown_params,\n        **scheduler_kwargs,\n        pipeline_dir=pipeline_dir,\n        run_dir=run_dir,\n        segments_dir=segments_dir,\n    )\n\n\n@click.command(name=\"start-ringdown\", help=start_ringdown.__doc__)\n@click.argument(\n    \"inspiral_run_dir\",\n    type=click.Path(\n        exists=True,\n        file_okay=False,\n        dir_okay=True,\n        readable=True,\n        path_type=Path,\n    ),\n)\n@click.option(\n    \"-i\",\n    \"--inspiral-input-file\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n    default=None,\n    help=\"Path to Inspiral yaml, defaults to Inspiral.yaml in directory given.\",\n)\n@click.option(\n    \"--ahc-reductions-path\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n    default=None,\n    help=(\n        \"Path to reduction file containing AhC coefs, defaults to\"\n        \" 'BbhReductions.h5' in directory given.\"\n    ),\n)\n@click.option(\n    \"--ahc-subfile\",\n    type=str,\n    default=\"ObservationAhC_Ylm\",\n    help=(\n        \"Subfile path name in reduction data containing AhC coefs, defaults to\"\n        \" 'ObservationAhC_Ylm'\"\n    ),\n)\n@click.option(\n    \"--fot-vol-h5-path\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n    default=None,\n    help=(\n        \"Path to volume data file containing functions of time, defaults to\"\n        \" 'BbhVolume0.h5' in directory given.\"\n    ),\n)\n@click.option(\n    \"--fot-vol-subfile\",\n    type=str,\n    default=\"ForContinuation\",\n    help=(\n        \"Subfile in volume data with functions of time at different times,\"\n        \" defaults to 'ForContinuation'.\"\n    ),\n)\n@click.option(\n    \"--path-to-output-h5\",\n    type=click.Path(\n        exists=False,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n    default=None,\n    help=(\n        \"Output h5 file for shape coefs, defaults to directory where command\"\n        \" was run.\"\n    ),\n)\n@click.option(\n    \"--output-subfile-prefix\",\n    type=str,\n    default=\"Distorted\",\n    help=\"Output subfile prefix for AhC coefs, defaults to 'Distorted'\",\n)\n@click.option(\n    \"--number-of-ahc-finds-for-fit\",\n    type=int,\n    default=10,\n    help=\"Number of AhC finds that will be used for the fit.\",\n)\n@click.option(\n    \"--match-time\",\n    type=float,\n    default=None,\n    help=\"Desired match time (volume data must contain data at this time)\",\n)\n@click.option(\n    \"--settling-timescale\",\n    type=float,\n    default=10.0,\n    help=\"Damping timescale for settle to const functions of time\",\n)\n@click.option(\n    \"--zero-coefs-eps\",\n    type=float,\n    default=None,\n    help=(\n        \"Sets shape coefficients to exactly 0.0 if the sum over all\"\n        \" '--number-of-ahc-finds-for-fit' are within zero-coefs-eps close\"\n        \" to 0.0\"\n    ),\n)\n@click.option(\n    \"--lev\",\n    type=int,\n    help=(\n        \"Resolution levels defined in terms of h and p refinement. For integer\"\n        \" N, LevN corresponds to refinement level L = 2 and polynomial order P\"\n        \" = 10 + N. Mutually exclusive with options '-L' and '-P'\"\n    ),\n)\n@click.option(\n    \"--refinement-level\",\n    \"-L\",\n    type=int,\n    help=\"h-refinement level.\",\n    show_default=True,\n)\n@click.option(\n    \"--polynomial-order\",\n    \"-P\",\n    type=int,\n    help=\"p-refinement level.\",\n    show_default=True,\n)\n@click.option(\n    \"--ringdown-input-file-template\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n    default=RINGDOWN_INPUT_FILE_TEMPLATE,\n    help=\"Input file template for the ringdown.\",\n    show_default=True,\n)\n@click.option(\n    \"--pipeline-dir\",\n    \"-d\",\n    type=click.Path(\n        writable=True,\n        path_type=Path,\n    ),\n    help=\"Directory where steps in the pipeline are created.\",\n)\n@scheduler_options\ndef start_ringdown_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    start_ringdown(**kwargs)\n\n\nif __name__ == \"__main__\":\n    start_ringdown_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Pipelines/Bbh/Ringdown.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhSingleBlackHole\n{{ TargetParams }}\nLev: {{ Lev | default(\"None\") }}\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPointsAndGridSpacing\n\n# Note: most of the parameters in this file are just made up. They should be\n# replaced with values that make sense once we have a better idea of the\n# transition to ringdown.\n\nInitialData:\n  NumericInitialData:\n    FileGlob: \"{{ IdFileGlob }}\"\n    Subgroup: \"{{ IdFileGlobSubgroup }}\"\n    ObservationValue: &InitialTime {{ MatchTime }}\n    ObservationValueEpsilon: Auto\n    ElementsAreIdentical: False\n    Variables:\n      SpacetimeMetric: SpacetimeMetric\n      Pi: Pi\n      Phi: Phi\n\nDomainCreator:\n  Sphere:\n    # InnerRadius is not yet set automatically. The value of 1.45 works\n    # for equal-mass, zero spin, quasicircular.\n    InnerRadius: &InnerRadius 1.45\n    OuterRadius: \"{{ OuterBdryRadius }}\"\n    Interior:\n      ExciseWithBoundaryCondition:\n        DemandOutgoingCharSpeeds:\n    InitialRefinement:\n      Shell0: [{{ L }}, {{ L }}, {{ L + 2 }}]\n      Shell1: [{{ L }}, {{ L }}, {{ L + 4 }}]\n    InitialGridPoints: {{ P + 1 }}\n    UseEquiangularMap: True\n    EquatorialCompression: None\n    # The radial partitioning split has been tested for\n    # equal-mass, zero spin, quasi-circular, but not\n    # yet for other cases.\n    RadialPartitioning: [50.0]\n    RadialDistribution: [Logarithmic, Linear]\n    WhichWedges: All\n    TimeDependentMaps:\n      InitialTime: *InitialTime\n      ShapeMap:\n        LMax: &MaximumL {{ ShapeMapLMax }}\n        CoefficientTruncationLimit: 1e-12\n        InitialValues:\n          H5Filename: \"{{ PathToAhCCoefsH5File }}\"\n          SubfileNames: [\"{{ AhCCoefsSubfilePrefix }}AhC_Ylm\",\n                         \"{{ AhCCoefsSubfilePrefix }}dtAhC_Ylm\",\n                         \"{{ AhCCoefsSubfilePrefix }}dt2AhC_Ylm\"]\n          MatchTime: *InitialTime\n          MatchTimeEpsilon: 1.e-10\n          SetL1CoefsToZero: True\n          CheckFrame: True\n        # The excision surface initially has some outward velocity to avoid\n        # incoming char speeds. The size control system should catch the\n        # excision surface before it collides with the apparent horizon.\n        # This value works for equal-mass, nonspinning, quasicircular,\n        # and has not been tested for other configurations.\n        SizeInitialValues: [0.0, -1.0, 0.0]\n      RotationMap:\n        InitialQuaternions: {{ Rotation }}\n        DecayTimescale: 1.0\n      ExpansionMap:\n        # During the ringdown, only the outer boundary's expansion map is\n        # applied.\n        InitialValues: [1.0, 0.0, 0.0]\n        InitialValuesOuterBoundary: {{ ExpansionOuterBdry }}\n        DecayTimescale: 1.0\n        DecayTimescaleOuterBoundary: 1.0\n      TranslationMap:\n        InitialValues: {{ Translation }}\n      TransitionRotScaleTrans: True\n    OuterBoundaryCondition:\n      ConstraintPreservingBjorhus:\n        Type: ConstraintPreservingPhysical\n        IncomingWaveProfile: None\n\nEvolution:\n  InitialTime: *InitialTime\n  InitialTimeStep: 0.0001\n  MinimumTimeStep: 1e-7\n  # This is the smallest interval we'd need to observe time step/constraints. If\n  # you would like more frequent output, consider using dense output.\n  InitialSlabSize: 0.1\n  VariableOrderAlgorithm:\n    GoalOrder: 4\n  LtsStepChoosers:\n    - LimitIncrease:\n        Factor: 2\n    - ErrorControl:\n        AbsoluteTolerance: 1e-10\n        RelativeTolerance: 1e-8\n        MaxFactor: 2\n        MinFactor: 0.25\n        SafetyFactor: 0.95\n  TimeStepper:\n    AdamsMoultonPcMonotonic:\n      Order: 4\n\nEvolutionSystem:\n  GeneralizedHarmonic:\n    GaugeCondition:\n      DampedHarmonic:\n        SpatialDecayWidth: 17.0152695482514\n        Amplitudes: [1.0, 0.0, 1.0]\n        Exponents: [2, 2, 2]\n    # The parameter choices here come from our experience with the Spectral\n    # Einstein Code (SpEC). They should be suitable for evolutions of a\n    # perturbation of a Kerr-Schild black hole.\n    DampingFunctionGamma0:\n      # SpEC uses a sum of 2 gaussians. Here, the expansion func of time\n      # is just the identity, so TimeDependentTripleGaussian ought to work here\n      # as a time-independent triple Gaussian..\n      # The Gaussian amplitudes, widths, and centers are set according to what\n      # SpEC does ringdown for an equal-mass, zero-spin, circular inspiral.\n      TimeDependentTripleGaussian:\n        Constant: 0.01\n        Gaussian1:\n          Amplitude: 7.0\n          Width: 2.5\n          Center: [0.0, 0.0, 0.0]\n        Gaussian2:\n          Amplitude: 1.0\n          Width: 100.\n          Center: [0.0, 0.0, 0.0]\n        Gaussian3:\n          Amplitude: 0.0\n          Width: 100.0\n          Center: [0.0, 0.0, 0.0]\n        MovementMethod: ExpansionFactor\n    DampingFunctionGamma1:\n      Constant:\n        Value: -1.0\n    DampingFunctionGamma2:\n      TimeDependentTripleGaussian:\n        Constant: 0.001\n        Gaussian1:\n          Amplitude: 7.0\n          Width: 2.5\n          Center: [0.0, 0.0, 0.0]\n        Gaussian2:\n          Amplitude: 0.1\n          Width: 100.0\n          Center: [0.0, 0.0, 0.0]\n        Gaussian3:\n          Amplitude: 0.0\n          Width: 100.0\n          Center: [0.0, 0.0, 0.0]\n        MovementMethod: ExpansionFactor\n\nFiltering:\n  ExpFilter0:\n    Alpha: 64.0\n    HalfPower: 210\n    Enable: true\n    BlocksToFilter: All\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    AveragedUpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"BbhVolume\"\n  ReductionFileName: \"BbhReductions\"\n  SurfaceFileName: \"BbhSurfaces\"\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n       EvenlySpaced:\n         # Current implementation checks wallclock at these global syncs\n         Interval: 10\n         Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: 23.5\n\nEventsAndTriggersAtCheckpoints:\n  - Trigger: Always\n    Events:\n      - ObserveFields:\n          SubfileName: Checkpoints\n          VariablesToObserve:\n            - SpacetimeMetric\n            - Pi\n            - Phi\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - ObserveTimeStep:\n          SubfileName: TimeSteps\n          PrintTimeToTerminal: True\n          ObservePerCore: False\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n          - Name: Lapse\n            NormType: L2Norm\n            Components: Individual\n          - Name: PointwiseL2Norm(GaugeConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(ThreeIndexConstraint)\n            NormType: L2Norm\n            Components: Sum\n  # Observe constraint energy more sparsely because it is expensive to compute.\n  # Also use it for ErrorIfDataTooBig so we only compute it once.\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 100\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: ConstraintEnergy\n          TensorsToObserve:\n          - Name: ConstraintEnergy\n            NormType: L2Norm\n            Components: Sum\n      - ErrorIfDataTooBig:\n          Threshold: 10\n          VariablesToCheck: [ConstraintEnergy]\n      - ErrorIfDataTooBig:\n          Threshold: 100\n          VariablesToCheck: [SpacetimeMetric]\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - ApparentHorizon\n      - ExcisionBoundary\n  # Observe volume data for visualization and debugging\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1000\n          Offset: 0\n    Events:\n      # SpatialRicciScalar and Psi4Real output for visualizations scripts.\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            # For diagnostics:\n            - ConstraintEnergy\n            - PointwiseL2Norm(GaugeConstraint)\n            - PointwiseL2Norm(ThreeIndexConstraint)\n            # For visualization:\n            - Lapse\n            - SpatialRicciScalar\n            - Psi4Real\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Float\n          FloatingPointTypes: [Float]\n          BlocksToObserve: All\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: {{ FinalTime }}\n    Events:\n      - Completion\n  # Discard old data to reduce memory use\n  - Trigger: Always\n    Events:\n      - CleanFunctionsOfTime\n\nEventsAndTriggersAtSteps:\n\nEventsAndDenseTriggers:\n  # BondiSachs output needs to be often enough for CCE to run properly. An\n  # interval of 0.1 was found to work well in SpEC.\n  - Trigger:\n      Times:\n        EvenlySpaced:\n          Interval: 0.1\n          Offset: 0.0\n    Events:\n      - BondiSachsInterpolation\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n    - ObserveTimeStepVolume:\n        SubfileName: FailureTimeStep\n        FloatingPointType: Double\n        CoordinatesFloatingPointType: Float\n\nInterpolator:\n  Verbosity: Silent\n\nApparentHorizons:\n  LMax: *MaximumL\n  ApparentHorizon: &Ah\n    Criteria:\n    InitialGuess:\n      LMax: *MaximumL\n      Radius: 2.2\n      Center: [0., 0., 0.]\n    FastFlow:\n      Flow: Fast\n      Alpha: 1.0\n      Beta: 0.5\n      AbsTol: 1e-12\n      TruncationTol: 1e-5\n      DivergenceTol: 5\n      DivergenceIter: 5\n      MaxIts: 100\n    Verbosity: Quiet\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: [\"Shell0\"]\n  ControlSystemSingleAh: *Ah\n  ControlSystemCharSpeedAh: *Ah\n\nInterpolationTargets:\n  BondiSachsInterpolation:\n    LMax: 16\n    Radius: [100, 150, 200]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n  ExcisionBoundary: &ExBdry\n    LMax: *MaximumL\n    Center: [0., 0., 0.]\n    Radius: *InnerRadius\n    AngularOrdering: \"Strahlkorper\"\n  ControlSystemCharSpeedExcision: *ExBdry\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nControlSystems:\n  WriteDataToDisk: false\n  MeasurementsPerUpdate: 4\n  DelayUpdate: true\n  Verbosity: Silent\n  Translation:\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: false\n    Controller:\n      UpdateFraction: 0.3\n    TimescaleTuner:\n      InitialTimescales: 0.2\n      MinTimescale: 1.0e-2\n      MaxTimescale: 10.0\n      IncreaseThreshold: 2.5e-4\n      DecreaseThreshold: 1.0e-3\n      IncreaseFactor: 1.01\n      DecreaseFactor: 0.98\n    ControlError:\n  Shape:\n    Averager:\n      AverageTimescaleFraction: 0.25\n      # This is different from inspiral but same as SpEC.\n      # See ComputePostMergerDataFromAhC.pl:2278\n      Average0thDeriv: true\n    Controller:\n      UpdateFraction: 0.3\n    TimescaleTuner:\n      InitialTimescales: 0.04\n      MinTimescale: 1.0e-3\n      MaxTimescale: 10.0\n      IncreaseThreshold: 2.5e-4\n      DecreaseThreshold: 1.0e-3\n      IncreaseFactor: 1.01\n      DecreaseFactor: 0.98\n    ControlError:\n  Size:\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: true\n    Controller:\n      UpdateFraction: 0.1\n    TimescaleTuner:\n      InitialTimescales: 0.008\n      MinTimescale: 1.0e-5\n      MaxTimescale: 20.0\n      IncreaseThreshold: 2.5e-4\n      IncreaseFactor: 1.01\n    ControlError:\n      MaxNumTimesForZeroCrossingPredictor: 4\n      SmoothAvgTimescaleFraction: 0.25\n      DeltaRDriftOutwardOptions: None\n      DeltaRDriftInwardOptions: None\n      InitialState: DeltaR\n      SmootherTuner:\n        InitialTimescales: [0.02]\n        MinTimescale: 1.0e-4\n        MaxTimescale: 20.0\n        IncreaseThreshold: 2.5e-4\n        DecreaseThreshold: 1.0e-3\n        IncreaseFactor: 1.01\n        DecreaseFactor: 0.9\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "support/Pipelines/Bbh/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport click\n\nfrom spectre.support.CliExceptions import RequiredChoiceError\n\n\n# Load subcommands lazily, i.e., only import the module when the subcommand is\n# invoked. This is important so the CLI responds quickly.\nclass Bbh(click.Group):\n    def list_commands(self, ctx):\n        return [\n            \"eccentricity-control\",\n            \"find-horizon\",\n            \"generate-id\",\n            \"postprocess-id\",\n            \"start-inspiral\",\n            \"start-ringdown\",\n        ]\n\n    def get_command(self, ctx, name):\n        if name in [\"eccentricity-control\", \"ecc-control\"]:\n            from .EccentricityControl import eccentricity_control_command\n\n            return eccentricity_control_command\n        elif name == \"find-horizon\":\n            from .FindHorizon import find_horizon_command\n\n            return find_horizon_command\n        elif name == \"generate-id\":\n            from .InitialData import generate_id_command\n\n            return generate_id_command\n        elif name == \"postprocess-id\":\n            from .PostprocessId import postprocess_id_command\n\n            return postprocess_id_command\n        elif name == \"start-inspiral\":\n            from .Inspiral import start_inspiral_command\n\n            return start_inspiral_command\n        elif name == \"start-ringdown\":\n            from .Ringdown import start_ringdown_command\n\n            return start_ringdown_command\n        raise RequiredChoiceError(\n            f\"The command '{name}' is not implemented.\",\n            choices=self.list_commands(ctx),\n        )\n\n\n@click.group(name=\"bbh\", cls=Bbh)\ndef bbh_pipeline():\n    \"\"\"Pipeline for binary black hole simulations.\"\"\"\n    pass\n\n\nif __name__ == \"__main__\":\n    bbh_pipeline(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Pipelines/Bns/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  Bns\n  MODULE_PATH Pipelines\n  PYTHON_FILES\n  __init__.py\n  ComputeTrajectories.py\n)\n\n# Create a target to compile all executables for this pipeline\nset(PIPELINE_TARGET bns)\nadd_custom_target(${PIPELINE_TARGET})\n"
  },
  {
    "path": "support/Pipelines/Bns/ComputeTrajectories.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport click\nimport h5py\nimport numpy as np\n\n\n@click.command(name=\"compute-trajectories\")\n@click.argument(\n    \"h5file_name\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n    nargs=1,\n    required=True,\n)\ndef compute_trajectories_command(h5file_name: str):\n    \"\"\"Extracts the neutron star trajectories and computes the separation.\n\n    This uses the '/ControlSystems/BnsInertialCenters.dat' data from the control\n    system for the neutron star centers in code units (solar masses). It then\n    computes the separation both in code units and kilometers assuming code\n    units are $M_{\\mathrm{sun}}=1$.\n\n    The output is printed to screen and should be redirected into a CSV/dat\n    file.\n    \"\"\"\n\n    control_system_subfile = \"/ControlSystems/BnsInertialCenters.dat\"\n\n    with h5py.File(h5file_name, \"r\") as h5file:\n        dat_file = h5file.get(control_system_subfile)\n        if dat_file is None:\n            subfiles = available_subfiles(h5_filename, extension=\".dat\")\n            raise ValueError(\n                f\"Unable to find subfile {control_system_subfile}. Known \"\n                f\"files are:\\n{subfiles}\"\n            )\n        legend = list(dat_file.attrs[\"Legend\"])\n        control_system_data = np.asarray(dat_file)\n\n    time = control_system_data[:, legend.index(\"Time\")]\n    x_a = control_system_data[\n        :,\n        legend.index(\"Center_A_x\"),\n    ]\n    y_a = control_system_data[\n        :,\n        legend.index(\"Center_A_y\"),\n    ]\n    z_a = control_system_data[\n        :,\n        legend.index(\"Center_A_z\"),\n    ]\n    x_b = control_system_data[\n        :,\n        legend.index(\"Center_B_x\"),\n    ]\n    y_b = control_system_data[\n        :,\n        legend.index(\"Center_B_y\"),\n    ]\n    z_b = control_system_data[\n        :,\n        legend.index(\"Center_B_z\"),\n    ]\n    coord_separation = control_system_data[:, 4:7] - control_system_data[:, 1:4]\n    separation = np.sqrt(\n        coord_separation[:, 0] ** 2\n        + coord_separation[:, 1] ** 2\n        + coord_separation[:, 2] ** 2\n    )\n\n    print(\"\"\"\n# 0: Time\n# 1: x_A\n# 2: y_A\n# 3: z_A\n# 4: x_B\n# 5: y_B\n# 6: z_B\n# 7: Separation [M_sun]\n# 8: Separation [km]\"\"\")\n    m_sun_to_km = 1.47651\n    for i in range(0, len(time)):\n        print(\n            time[i],\n            x_a[i],\n            y_a[i],\n            z_a[i],\n            x_b[i],\n            y_b[i],\n            z_b[i],\n            separation[i],\n            separation[i] * m_sun_to_km,\n        )\n\n\nif __name__ == \"__main__\":\n    compute_trajectories_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Pipelines/Bns/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport click\n\nfrom spectre.support.CliExceptions import RequiredChoiceError\n\n\n# Load subcommands lazily, i.e., only import the module when the subcommand is\n# invoked. This is important so the CLI responds quickly.\nclass Bns(click.Group):\n    def list_commands(self, ctx):\n        return [\n            \"compute-trajectories\",\n        ]\n\n    def get_command(self, ctx, name):\n        if name == \"compute-trajectories\":\n            from .ComputeTrajectories import compute_trajectories_command\n\n            return compute_trajectories_command\n\n\n@click.group(name=\"bns\", cls=Bns)\ndef bns_pipeline():\n    \"\"\"Pipeline for binary neutron star simulations.\"\"\"\n    pass\n\n\nif __name__ == \"__main__\":\n    bns_pipeline(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Pipelines/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Bbh)\nadd_subdirectory(Bns)\nadd_subdirectory(EccentricityControl)\n"
  },
  {
    "path": "support/Pipelines/EccentricityControl/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  EccentricityControl\n  MODULE_PATH Pipelines\n  PYTHON_FILES\n  __init__.py\n  EccentricityControlParams.py\n  InitialOrbitalParameters.py\n)\n"
  },
  {
    "path": "support/Pipelines/EccentricityControl/EccentricityControlParams.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport functools\nimport glob\nimport logging\nfrom pathlib import Path\nfrom typing import Dict, Literal, Optional, Sequence, Tuple, Union\n\nimport click\nimport h5py\nimport numpy as np\nimport pandas as pd\nimport yaml\n\nfrom spectre.IO.H5 import to_dataframe\nfrom spectre.support.CheckSpecImport import check_spec_import\nfrom spectre.support.Yaml import SafeDumper\nfrom spectre.Visualization.PlotTrajectories import import_A_and_B\n\nlogger = logging.getLogger(__name__)\n\n# Keys of the dictionary returned by eccentricity_control_params\nEccentricityParams = Literal[\n    \"Eccentricity\",\n    \"EccentricityError\",\n    \"Omega0\",\n    \"Adot0\",\n    \"D0\",\n    \"DeltaOmega0\",\n    \"DeltaAdot0\",\n    \"DeltaD0\",\n    \"NewOmega0\",\n    \"NewAdot0\",\n    \"NewD0\",\n    \"Tmin\",\n    \"Tmax\",\n]\n\nDEFAULT_AHA_TRAJECTORIES = \"ApparentHorizons/ControlSystemAhA_Centers.dat\"\nDEFAULT_AHB_TRAJECTORIES = \"ApparentHorizons/ControlSystemAhB_Centers.dat\"\n\n\ndef eccentricity_control_params(\n    h5_files: Union[Union[str, Path], Sequence[Union[str, Path]]],\n    id_input_file_path: Union[str, Path],\n    subfile_name_aha_trajectories: str = DEFAULT_AHA_TRAJECTORIES,\n    subfile_name_ahb_trajectories: str = DEFAULT_AHB_TRAJECTORIES,\n    subfile_name_aha_quantities: str = \"ObservationAhA.dat\",\n    subfile_name_ahb_quantities: str = \"ObservationAhB.dat\",\n    tmin: Optional[float] = None,\n    tmax: Optional[float] = None,\n    plot_output_dir: Optional[Union[str, Path]] = None,\n    ecc_params_output_file: Optional[Union[str, Path]] = None,\n) -> Dict[EccentricityParams, float]:\n    \"\"\"Get new orbital parameters for a binary system to control eccentricity.\n\n    The eccentricity is estimated from the trajectories of the binary objects\n    and updates to the orbital parameters are suggested to drive the orbit to\n    the target eccentricity, using SpEC's OmegaDotEccRemoval.py. Currently\n    supports only circular target orbits (target eccentricity = 0).\n\n    Arguments:\n      h5_files: Paths to the H5 files containing the trajectory data (e.g.\n        BbhReductions.h5). Can also be a glob pattern.\n      id_input_file_path: Path to the initial data input file from which the\n        evolution started. This file contains the initial data parameters that\n        are being controlled.\n      subfile_name_aha_trajectories: (Optional) Name of the subfile containing\n        the apparent horizon centers for object A.\n      subfile_name_ahb_trajectories: (Optional) Name of the subfile containing\n        the apparent horizon centers for object B.\n      subfile_name_aha_quantities: (Optional) Name of the subfile containing the\n        quantities measured on apparent horizon A (masses and spins).\n      subfile_name_ahb_quantities: (Optional) Name of the subfile containing the\n        quantities measured on apparent horizon B (masses and spins).\n      tmin: (Optional) The lower time bound for the eccentricity estimate.\n        Used to remove initial junk and transients in the data. If unspecified,\n        uses SpEC's 'OmegaDotEccRemoval.FindTmin' to estimate it.\n      tmax: (Optional) The upper time bound for the eccentricity estimate.\n        A reasonable value would include 2-3 orbits.\n        Default is '500 + 5 * pi / Omega0'.\n      plot_output_dir: (Optional) Output directory for plots.\n      ecc_params_output_file: (Optional) Output file for the results.\n\n    Returns:\n        Dictionary with the keys listed in 'EccentricityParams'.\n    \"\"\"\n    # Import functions from SpEC until we have ported them over\n    check_spec_import(\n        contains_commit=\"ecfabf1ce78daeacbdd026625a02215c8e84af0e\",\n    )\n    from OmegaDotEccRemoval import (\n        ComputeOmegaAndDerivsFromFile,\n        FindTmin,\n        performAllFits,\n    )\n\n    # Make sure h5_files is a sequence\n    if isinstance(h5_files, str):\n        h5_files = glob.glob(h5_files)\n        h5_files.sort()\n    if isinstance(h5_files, Path):\n        h5_files = [h5_files]\n\n    # Read initial data parameters from input file\n    with open(id_input_file_path, \"r\") as open_input_file:\n        id_metadata, id_input_file = yaml.safe_load_all(open_input_file)\n    target_params = id_metadata[\"TargetParams\"]\n    target_eccentricity = target_params[\"Eccentricity\"]\n    assert (\n        target_eccentricity == 0.0\n    ), \"Only circular orbits are currently supported for eccentricity control.\"\n    id_binary = id_input_file[\"Background\"][\"Binary\"]\n    Omega0 = id_binary[\"AngularVelocity\"]\n    adot0 = id_binary[\"Expansion\"]\n    D0 = id_binary[\"XCoords\"][1] - id_binary[\"XCoords\"][0]\n\n    # Load trajectory data\n    traj_A, traj_B = import_A_and_B(\n        h5_files, subfile_name_aha_trajectories, subfile_name_ahb_trajectories\n    )\n    t, Omega, dOmegadt, OmegaVec = ComputeOmegaAndDerivsFromFile(traj_A, traj_B)\n\n    # Set time bounds if not provided\n    if tmin is None:\n        tmin = max(FindTmin(t, dOmegadt, 500), t[0])\n    if tmax is None:\n        tmax = min(500 + 5 * np.pi / Omega0, t[-1])\n    logger.info(\n        \"Estimating eccentricity from trajectory data in time range\"\n        f\" {tmin:.3f} to {tmax:.3f}.\"\n    )\n\n    # Load horizon parameters from evolution data at reference time (tmin)\n    def get_horizons_data(reductions_file):\n        with h5py.File(reductions_file, \"r\") as open_h5file:\n            horizons_data = []\n            for ab, subfile_name in zip(\n                \"AB\", [subfile_name_aha_quantities, subfile_name_ahb_quantities]\n            ):\n                ah_subfile = open_h5file.get(subfile_name)\n                if ah_subfile is not None:\n                    horizons_data.append(\n                        to_dataframe(ah_subfile)\n                        .set_index(\"Time\")\n                        .add_prefix(f\"Ah{ab} \")\n                    )\n            if not horizons_data:\n                return pd.DataFrame()\n            return pd.concat(horizons_data, axis=1)\n\n    horizon_params = pd.concat(map(get_horizons_data, h5_files))\n    if horizon_params.empty:\n        logger.warning(\n            \"No horizon data found. \"\n            \"Using initial data masses and ignoring spins.\"\n        )\n        mA = target_params[\"MassA\"]\n        mB = target_params[\"MassB\"]\n        sA = sB = None\n    else:\n        mA = horizon_params[\"AhA ChristodoulouMass\"].iloc[0]\n        mB = horizon_params[\"AhB ChristodoulouMass\"].iloc[0]\n        if \"AhA DimensionfulSpinVector_x\" in horizon_params.columns:\n            sA = np.array(\n                [horizon_params.index]\n                + [\n                    horizon_params[f\"AhA DimensionfulSpinVector_{xyz}\"]\n                    for xyz in \"xyz\"\n                ]\n            ).T\n            sB = np.array(\n                [horizon_params.index]\n                + [\n                    horizon_params[f\"AhB DimensionfulSpinVector_{xyz}\"]\n                    for xyz in \"xyz\"\n                ]\n            ).T\n        else:\n            logger.warning(\"No horizon spins found in data, ignoring spins.\")\n            sA = sB = None\n\n    # Call into SpEC's OmegaDotEccRemoval.py\n    # Despite the name, this SpEC function takes arrays of data\n    eccentricity, delta_Omega0, delta_adot0, delta_D0, ecc_std_dev, _ = (\n        performAllFits(\n            XA=traj_A,\n            XB=traj_B,\n            t=t,\n            Omega=Omega,\n            dOmegadt=dOmegadt,\n            OmegaVec=OmegaVec,\n            mA=mA,\n            mB=mB,\n            sA=sA,\n            sB=sB,\n            IDparam_omega0=Omega0,\n            IDparam_adot0=adot0,\n            IDparam_D0=D0,\n            tmin=tmin,\n            tmax=tmax,\n            tref=tmin,\n            opt_freq_filter=True,\n            opt_varpro=True,\n            opt_type=\"bbh\",\n            opt_tmin=tmin,\n            opt_improved_Omega0_update=True,\n            check_periastron_advance=True,\n            plot_output_dir=plot_output_dir,\n            Source=\"\",\n        )\n    )\n    logger.info(\n        f\"Eccentricity estimate is {eccentricity:g} +/- {ecc_std_dev:e}.\"\n        \" Update orbital parameters as follows\"\n        f\" for target eccentricity {target_eccentricity:g} (choose two):\\n\"\n        f\"Omega0 += {delta_Omega0:e} -> {Omega0 + delta_Omega0:.8g}\\n\"\n        f\"adot0 += {delta_adot0:e} -> {adot0 + delta_adot0:e}\\n\"\n        f\"D0 += {delta_D0:e} -> {D0 + delta_D0:.8g}\"\n    )\n    # These keys must correspond to 'EccentricityParams'\n    ecc_params = {\n        \"Eccentricity\": eccentricity,\n        \"EccentricityError\": ecc_std_dev,\n        \"Omega0\": Omega0,\n        \"Adot0\": adot0,\n        \"D0\": D0,\n        \"DeltaOmega0\": delta_Omega0,\n        \"DeltaAdot0\": delta_adot0,\n        \"DeltaD0\": delta_D0,\n        \"NewOmega0\": Omega0 + delta_Omega0,\n        \"NewAdot0\": adot0 + delta_adot0,\n        \"NewD0\": D0 + delta_D0,\n        \"Tmin\": tmin,\n        \"Tmax\": tmax,\n    }\n    if ecc_params_output_file:\n        with open(ecc_params_output_file, \"w\") as open_file:\n            yaml.dump(ecc_params, open_file, Dumper=SafeDumper)\n    return ecc_params\n\n\ndef eccentricity_control_params_options(f):\n    \"\"\"CLI options for the 'eccentricity_control_params' function.\n\n    These options can be used by CLI commands that call the\n    'eccentricity_control_params' function.\n    \"\"\"\n\n    @click.argument(\n        \"h5_files\",\n        nargs=-1,\n        type=click.Path(\n            exists=True, file_okay=True, dir_okay=False, readable=True\n        ),\n    )\n    @click.option(\n        \"--subfile-name-aha-trajectories\",\n        default=DEFAULT_AHA_TRAJECTORIES,\n        show_default=True,\n        help=(\n            \"Name of subfile containing the apparent horizon centers for\"\n            \" object A.\"\n        ),\n    )\n    @click.option(\n        \"--subfile-name-ahb-trajectories\",\n        default=DEFAULT_AHB_TRAJECTORIES,\n        show_default=True,\n        help=(\n            \"Name of subfile containing the apparent horizon centers for\"\n            \" object B.\"\n        ),\n    )\n    @click.option(\n        \"--subfile-name-aha-quantities\",\n        default=\"ObservationAhA.dat\",\n        show_default=True,\n        help=(\n            \"Name of subfile containing the quantities measured on apparent\"\n            \" horizon A (masses and spins).\"\n        ),\n    )\n    @click.option(\n        \"--subfile-name-ahb-quantities\",\n        default=\"ObservationAhB.dat\",\n        show_default=True,\n        help=(\n            \"Name of subfile containing the quantities measured on apparent\"\n            \" horizon A (masses and spins).\"\n        ),\n    )\n    @click.option(\n        \"--id-input-file\",\n        \"-i\",\n        \"id_input_file_path\",\n        required=True,\n        help=\"Input file with initial data parameters.\",\n    )\n    @click.option(\n        \"--tmin\",\n        type=float,\n        help=(\n            \"The lower time bound for the eccentricity estimate. Used to remove\"\n            \" initial junk and transients in the data.\"\n        ),\n    )\n    @click.option(\n        \"--tmax\",\n        type=float,\n        help=(\n            \"The upper time bound for the eccentricity estimate. A reasonable\"\n            \" value would include 2-3 orbits.\"\n        ),\n    )\n    @click.option(\n        \"--plot-output-dir\",\n        type=click.Path(writable=True),\n        help=\"Output directory for plots.\",\n    )\n    @click.option(\n        \"--ecc-params-output-file\",\n        type=click.Path(writable=True),\n        help=\"Output file for the new orbital parameters.\",\n    )\n    @functools.wraps(f)\n    def wrapper(*args, **kwargs):\n        return f(*args, **kwargs)\n\n    return wrapper\n\n\n@click.command(\n    name=\"eccentricity-control-params\", help=eccentricity_control_params.__doc__\n)\n@eccentricity_control_params_options\ndef eccentricity_control_params_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    eccentricity_control_params(**kwargs)\n"
  },
  {
    "path": "support/Pipelines/EccentricityControl/InitialOrbitalParameters.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom typing import Optional, Sequence, Tuple\n\nimport numpy as np\nfrom scipy.optimize import minimize\n\nfrom spectre.support.CheckSpecImport import check_spec_import\n\nlogger = logging.getLogger(__name__)\n\n\ndef initial_orbital_parameters(\n    target_params: dict,\n    separation: Optional[float] = None,\n    orbital_angular_velocity: Optional[float] = None,\n    radial_expansion_velocity: Optional[float] = None,\n) -> Tuple[float, float, float]:\n    \"\"\"Estimate initial orbital parameters from a Post-Newtonian approximation.\n\n    Given the target eccentricity and one other orbital parameter, this function\n    estimates the initial separation 'D', orbital angular velocity 'Omega_0',\n    and radial expansion velocity 'adot_0' of a binary system in general\n    relativity from a Post-Newtonian (PN) approximation.\n    The result can be used to start an eccentricity control procedure to tune\n    the initial orbital parameters further.\n\n    Currently only zero eccentricity (circular orbits) are supported, and the\n    implementation uses functions from SpEC's ZeroEccParamsFromPN.py. This\n    should be generalized.\n\n    Arguments:\n      mass_ratio: Defined as q = M_A / M_B >= 1.\n      dimensionless_spin_a: Dimensionless spin of the larger black hole, chi_A.\n      dimensionless_spin_b: Dimensionless spin of the smaller black hole, chi_B.\n      eccentricity: Eccentricity of the orbit. Specify together with _one_ of\n        the other orbital parameters. Currently only an eccentricity of 0 is\n        supported (circular orbit).\n      mean_anomaly_fraction: Mean anomaly of the orbit divided by 2 pi, so it is\n        a number between 0 and 1. The value 0 corresponds to the pericenter of\n        the orbit (closest approach), the value 0.5 corresponds to the apocenter\n        of the orbit (farthest distance), and the value 1 corresponds to the\n        pericenter again. Currently this is unused because only an eccentricity\n        of 0 is supported.\n      separation: Coordinate separation D of the black holes.\n      orbital_angular_velocity: Omega_0.\n      radial_expansion_velocity: adot_0.\n      num_orbits: Number of orbits until merger.\n      time_to_merger: Time to merger.\n\n    Returns: Tuple of the initial separation 'D_0', orbital angular velocity\n      'Omega_0', and radial expansion velocity 'adot_0'.\n    \"\"\"\n    # If all orbital parameters are already specified, return early\n    if (\n        separation is not None\n        and orbital_angular_velocity is not None\n        and radial_expansion_velocity is not None\n    ):\n        return separation, orbital_angular_velocity, radial_expansion_velocity\n\n    mass_ratio = target_params[\"MassRatio\"]\n    dimensionless_spin_a = np.asarray(target_params[\"DimensionlessSpinA\"])\n    dimensionless_spin_b = np.asarray(target_params[\"DimensionlessSpinB\"])\n    eccentricity = target_params[\"Eccentricity\"]\n    mean_anomaly_fraction = target_params.get(\"MeanAnomalyFraction\")\n    num_orbits = target_params.get(\"NumOrbits\")\n    time_to_merger = target_params.get(\"TimeToMerger\")\n\n    # Check input parameters for consistency\n    assert eccentricity is not None, (\n        \"Specify all orbital parameters 'separation',\"\n        \" 'orbital_angular_velocity', and 'radial_expansion_velocity', or\"\n        \" specify an 'eccentricity' plus one orbital parameter.\"\n    )\n    if eccentricity != 0.0:\n        assert mean_anomaly_fraction is not None, (\n            \"If you specify a nonzero 'eccentricity' you must also specify a\"\n            \" 'mean_anomaly_fraction'.\"\n        )\n    # The functions from SpEC currently work only for zero eccentricity. We will\n    # need to generalize this for eccentric orbits.\n    assert eccentricity == 0.0, (\n        \"Initial orbital parameters can currently only be computed for zero\"\n        \" eccentricity.\"\n    )\n    assert radial_expansion_velocity is None, (\n        \"Can't use the 'radial_expansion_velocity' to compute orbital\"\n        \" parameters. Remove it and choose another orbital parameter.\"\n    )\n    assert (\n        (separation is not None)\n        ^ (orbital_angular_velocity is not None)\n        ^ (num_orbits is not None)\n        ^ (time_to_merger is not None)\n    ), (\n        \"Specify an 'eccentricity' plus _one_ of the following orbital\"\n        \" parameters: 'separation', 'orbital_angular_velocity', 'num_orbits',\"\n        \" 'time_to_merger'.\"\n    )\n\n    # Import functions from SpEC until we have ported them over. These functions\n    # call old Fortran code (LSODA) through scipy.integrate.odeint, which leads\n    # to lots of noise in stdout. When porting these functions, we should\n    # modernize them to use scipy.integrate.solve_ivp.\n    check_spec_import()\n    from ZeroEccParamsFromPN import nOrbitsAndTotalTime, omegaAndAdot\n\n    # Find an omega0 that gives the right number of orbits or time to merger\n    if num_orbits is not None or time_to_merger is not None:\n        opt_result = minimize(\n            lambda x: (\n                abs(\n                    nOrbitsAndTotalTime(\n                        q=mass_ratio,\n                        chiA0=dimensionless_spin_a,\n                        chiB0=dimensionless_spin_b,\n                        omega0=x[0],\n                    )[0 if num_orbits is not None else 1]\n                    - (num_orbits if num_orbits is not None else time_to_merger)\n                )\n            ),\n            x0=[0.01],\n            method=\"Nelder-Mead\",\n        )\n        if not opt_result.success:\n            raise ValueError(\n                \"Failed to find an orbital angular velocity that gives the\"\n                \" desired number of orbits or time to merger. Error:\"\n                f\" {opt_result.message}\"\n            )\n        orbital_angular_velocity = opt_result.x[0]\n        logger.debug(\n            f\"Found orbital angular velocity: {orbital_angular_velocity}\"\n        )\n\n    # Find the separation that gives the desired orbital angular velocity\n    if orbital_angular_velocity is not None:\n        opt_result = minimize(\n            lambda x: abs(\n                omegaAndAdot(\n                    r=x[0],\n                    q=mass_ratio,\n                    chiA=dimensionless_spin_a,\n                    chiB=dimensionless_spin_b,\n                    rPrime0=1.0,  # Choice also made in SpEC\n                )[0]\n                - orbital_angular_velocity\n            ),\n            x0=[10.0],\n            method=\"Nelder-Mead\",\n        )\n        if not opt_result.success:\n            raise ValueError(\n                \"Failed to find a separation that gives the desired orbital\"\n                f\" angular velocity. Error: {opt_result.message}\"\n            )\n        separation = opt_result.x[0]\n        logger.debug(f\"Found initial separation: {separation}\")\n\n    # Find the radial expansion velocity\n    new_orbital_angular_velocity, radial_expansion_velocity = omegaAndAdot(\n        r=separation,\n        q=mass_ratio,\n        chiA=dimensionless_spin_a,\n        chiB=dimensionless_spin_b,\n        rPrime0=1.0,  # Choice also made in SpEC\n    )\n    if orbital_angular_velocity is None:\n        orbital_angular_velocity = new_orbital_angular_velocity\n    else:\n        assert np.isclose(\n            new_orbital_angular_velocity, orbital_angular_velocity, rtol=1e-4\n        ), (\n            \"Orbital angular velocity is inconsistent with separation.\"\n            \" Maybe the rootfind failed to reach sufficient accuracy.\"\n        )\n\n    # Estimate number of orbits and time to merger\n    num_orbits, time_to_merger = nOrbitsAndTotalTime(\n        q=mass_ratio,\n        chiA0=dimensionless_spin_a,\n        chiB0=dimensionless_spin_b,\n        omega0=orbital_angular_velocity,\n    )\n    logger.info(\n        \"Selected approximately circular orbit. Number of orbits:\"\n        f\" {num_orbits:g}. Time to merger: {time_to_merger:g} M.\"\n    )\n    return separation, orbital_angular_velocity, radial_expansion_velocity\n"
  },
  {
    "path": "support/Pipelines/EccentricityControl/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "support/Profiling/ScoreP/spectre.flt",
    "content": "SCOREP_FILE_NAMES_BEGIN\n  EXCLUDE\n        *parse_as*\n        *Option*\n        *CoordinateMaps*\n        *reset_compute_items_after_mutate*\nSCOREP_FILE_NAMES_END\n"
  },
  {
    "path": "support/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (NOT ENABLE_PYTHON)\n  return()\nendif()\n\noption(MACHINE \"Select a machine that we know how to run on, such as a \\\nparticular supercomputer\" OFF)\nspectre_python_add_module(\n  support\n  PYTHON_FILES\n  CheckSpecImport.py\n  CliExceptions.py\n  DirectoryStructure.py\n  Logging.py\n  Machines.py\n  Resubmit.py\n  RunNext.py\n  Schedule.py\n  Yaml.py\n)\n\nif(MACHINE)\n  message(STATUS \"Selected machine: ${MACHINE}\")\n  configure_file(\n    ${CMAKE_SOURCE_DIR}/support/Machines/${MACHINE}.yaml\n    ${SPECTRE_PYTHON_PREFIX}/support/Machine.yaml\n    )\n  configure_file(\n    ${CMAKE_SOURCE_DIR}/support/SubmitScripts/SubmitTemplateBase.sh\n    ${SPECTRE_PYTHON_PREFIX}/support/SubmitTemplateBase.sh\n    @ONLY\n    )\n  configure_file(\n    ${CMAKE_SOURCE_DIR}/support/SubmitScripts/${MACHINE}.sh\n    ${SPECTRE_PYTHON_PREFIX}/support/SubmitTemplate.sh\n    @ONLY\n    )\nendif()\n\n# Generate shell completion scripts. These don't usually change, so they are\n# committed to the source code and configured to the build dir. See the\n# [click docs](https://click.palletsprojects.com/shell-completion/)\n# for instructions how to regenerate the shell completion scripts.\nconfigure_file(\n  shell-completion.bash\n  ${SPECTRE_PYTHON_PREFIX_PARENT}/shell-completion.bash @ONLY)\nconfigure_file(\n  shell-completion.fish\n  ${SPECTRE_PYTHON_PREFIX_PARENT}/shell-completion.fish @ONLY)\nconfigure_file(\n  shell-completion.zsh\n  ${SPECTRE_PYTHON_PREFIX_PARENT}/shell-completion.zsh @ONLY)\n"
  },
  {
    "path": "support/Python/CheckSpecImport.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom pathlib import Path\nfrom typing import Optional\n\n\ndef check_spec_import(contains_commit: Optional[str] = None):\n    \"\"\"Check that the SpEC Python modules can be imported.\n\n    Arguments:\n      contains_commit: If specified, check that the SpEC repository contains\n        the specified commit. This can be used as a version check to ensure\n        that the installed SpEC version is compatible with the code that is\n        importing it.\n    \"\"\"\n    try:\n        import Utils as spec_utils\n    except ImportError:\n        raise ImportError(\n            \"Importing from SpEC failed. Make sure you have pointed \"\n            \"'-D SPEC_ROOT' to a SpEC installation when configuring the build \"\n            \"with CMake.\"\n        )\n    if contains_commit:\n        import subprocess\n\n        spec_repo_dir = Path(spec_utils.__file__).parent.parent.parent\n        result = subprocess.run(\n            [\"git\", \"merge-base\", \"--is-ancestor\", contains_commit, \"HEAD\"],\n            cwd=spec_repo_dir,\n            capture_output=True,\n            text=True,\n        )\n        if result.returncode != 0:\n            raise ImportError(\n                \"SpEC version mismatch: requested revision\"\n                f\" {contains_commit} is not in the SpEC repository at\"\n                f\" {spec_repo_dir}.\"\n            )\n"
  },
  {
    "path": "support/Python/CliExceptions.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport click\nimport rich\n\n\nclass RequiredChoiceError(click.UsageError):\n    \"\"\"Error raised in the CLI when a required choice is not specified.\n\n    Shows the list of available choices alongside the 'click.UsageError'.\n    \"\"\"\n\n    def __init__(self, message, choices):\n        super().__init__(message)\n        self.choices = choices\n\n    def format_message(self):\n        import rich.columns\n        import rich.console\n\n        # Format available choices as rows for better readability\n        console = rich.console.Console()\n        with console.capture() as capture:\n            table = rich.table.Table(box=None, show_header=False)\n            for choice in self.choices:\n                table.add_row(choice)\n            console.print(table)\n        choices_cols = capture.get().strip(\"\\n\")\n\n        return f\"{self.message} Available choices:\\n\\n{choices_cols}\"\n"
  },
  {
    "path": "support/Python/DirectoryStructure.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport re\nfrom dataclasses import dataclass\nfrom pathlib import Path\nfrom typing import List, Optional, Union\n\nlogger = logging.getLogger(__name__)\n\n\n@dataclass(frozen=True, order=True)\nclass Checkpoint:\n    \"\"\"State of a simulation saved to disk\n\n    An executable can write multiple checkpoints during its execution, and can\n    be restarted from those checkpoints. The 'id' enumerates the checkpoints.\n\n    We currently write checkpoints in a directory structure like this:\n\n    ```\n    RUN_DIR/\n        Checkpoints/\n            Checkpoint_0000/\n            Checkpoint_0001/\n            ...\n    ```\n\n    WARNING: Don't assume checkpoints always exist in the above directory\n    structure. You don't want your code to break when checkpoints are copied,\n    moved around, or renamed.\n    \"\"\"\n\n    path: Path\n    id: int\n\n    NAME_PATTERN = re.compile(r\"Checkpoint_(\\d+)\")\n    NUM_DIGITS = 4\n\n    @classmethod\n    def match(cls, path: Union[str, Path]) -> Optional[\"Checkpoint\"]:\n        \"\"\"Checks if the 'path' is a checkpoint\"\"\"\n        path = Path(path)\n        match = re.match(cls.NAME_PATTERN, path.resolve().name)\n        if not match:\n            return None\n        return cls(path=path, id=int(match.group(1)))\n\n\ndef list_checkpoints(checkpoints_dir: Union[str, Path]) -> List[Checkpoint]:\n    \"\"\"All checkpoints in the 'checkpoints_dir'\"\"\"\n    checkpoints_dir = Path(checkpoints_dir)\n    if not checkpoints_dir.exists():\n        return []\n    matches = map(Checkpoint.match, checkpoints_dir.iterdir())\n    return sorted(match for match in matches if match)\n\n\n@dataclass(frozen=True, order=True)\nclass Segment:\n    \"\"\"Part of a simulation that ran as one executable invocation\n\n    We have to split simulations into segments because supercomputers don't\n    typically support unlimited run times. Therefore, we terminate the job,\n    write the simulation state to disk as a checkpoint, and submit a new job\n    that restarts from the last checkpoint.\n\n    We currently write segments in a directory structure like this:\n\n    ```\n    SEGMENTS_DIR/\n        Segment_0000/\n            InputFile.yaml\n            Submit.sh\n            Output.h5\n            Checkpoints/\n        Segment_0001/...\n    ```\n\n    WARNING: Don't assume that simulations always have the above directory\n    structure. You don't want your code to break when files are copied, moved\n    around, or renamed. Instead of relying on some directory structure, have\n    your code take the files it needs as input. This is quite easy using globs.\n    \"\"\"\n\n    path: Path\n    id: int\n\n    NAME_PATTERN = re.compile(r\"Segment_(\\d+)\")\n    NUM_DIGITS = 4\n\n    @classmethod\n    def first(cls, directory: Union[str, Path]) -> \"Segment\":\n        name = \"Segment_\" + \"0\" * cls.NUM_DIGITS\n        return Segment(path=Path(directory) / name, id=0)\n\n    @property\n    def next(self) -> \"Segment\":\n        next_id = self.id + 1\n        next_name = \"Segment_\" + str(next_id).zfill(self.NUM_DIGITS)\n        return Segment(path=self.path.resolve().parent / next_name, id=next_id)\n\n    @classmethod\n    def match(cls, path: Union[str, Path]) -> Optional[\"Segment\"]:\n        \"\"\"Checks if the 'path' is a segment\"\"\"\n        path = Path(path)\n        match = re.match(cls.NAME_PATTERN, path.resolve().name)\n        if not match:\n            return None\n        return cls(path=path, id=int(match.group(1)))\n\n    @property\n    def checkpoints_dir(self) -> Path:\n        return self.path / \"Checkpoints\"\n\n    @property\n    def checkpoints(self) -> List[Checkpoint]:\n        return list_checkpoints(self.checkpoints_dir)\n\n\ndef list_segments(segments_dir: Union[str, Path]) -> List[Segment]:\n    \"\"\"All segments in the 'segments_dir'\"\"\"\n    segments_dir = Path(segments_dir)\n    if not segments_dir.exists():\n        return []\n    matches = map(Segment.match, segments_dir.iterdir())\n    return sorted(match for match in matches if match)\n\n\n@dataclass(frozen=True, order=True)\nclass PipelineStep:\n    \"\"\"A step in a pipeline\n\n    Each pipeline step is a numbered directory with a label, e.g.\n    \"000_InitialData\".\n    It can contain a simulation or a pre- or post-processing step\n    such as archiving.\n    Here is an example for the directory structure:\n\n    ```\n    BASE_DIR/\n        000_InitialData/\n            InputFile.yaml\n            Submit.sh\n            ...\n        001_Inspiral/\n            Segment_0000/\n                InputFile.yaml\n                Submit.sh\n                ...\n            Segment_0001/\n                ...\n        002_InitialData/\n            ...\n        ...\n    ```\n\n    Note: \"InitialData\" and \"Inspiral\" are examples, any name can be used.\n    \"\"\"\n\n    path: Path\n    id: int\n    label: str\n\n    NAME_PATTERN = re.compile(r\"(\\d+)_(.+)\")\n    NUM_DIGITS = 3\n\n    @classmethod\n    def first(cls, directory: Union[str, Path], label: str) -> \"PipelineStep\":\n        \"\"\"Create the first directory in a sequence\"\"\"\n        name = \"0\" * cls.NUM_DIGITS + \"_\" + label\n        return PipelineStep(path=Path(directory) / name, id=0, label=label)\n\n    def next(self, label: str) -> \"PipelineStep\":\n        \"\"\"Get the next directory in the sequence\"\"\"\n        next_id = self.id + 1\n        next_name = f\"{str(next_id).zfill(self.NUM_DIGITS)}_{label}\"\n        return PipelineStep(\n            path=self.path.resolve().parent / next_name,\n            id=next_id,\n            label=label,\n        )\n\n    @classmethod\n    def match(cls, path: Union[str, Path]) -> Optional[\"PipelineStep\"]:\n        \"\"\"Checks if the 'path' is a step in the pipeline\"\"\"\n        path = Path(path)\n        match = re.match(cls.NAME_PATTERN, path.resolve().name)\n        if not match:\n            return None\n        return cls(path=path, id=int(match.group(1)), label=match.group(2))\n\n\ndef list_pipeline_steps(base_dir: Union[str, Path]) -> List[PipelineStep]:\n    \"\"\"List all subdirectories in the base directory\"\"\"\n    base_dir = Path(base_dir)\n    if not base_dir.exists():\n        return []\n    matches = map(PipelineStep.match, base_dir.iterdir())\n    return sorted(match for match in matches if match)\n"
  },
  {
    "path": "support/Python/Logging.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\n\nimport rich.logging\n\n\ndef configure_logging(log_level: int):\n    \"\"\"\n    Configure logging for our python scripts using the 'logging' module\n\n    This is factored out into a free function so that any time we need to add\n    module-specific logging configuration, we only have to add it to one place.\n\n    Logging verbosity of the spectre module is set to the 'log_level'.\n    For other modules we set the log level to WARNING or above, so we don't get\n    debug output from all the modules we import.\n    \"\"\"\n    logging.basicConfig(\n        level=max(log_level, logging.WARNING),\n        format=\"%(message)s\",\n        datefmt=\"[%X]\",\n        handlers=[rich.logging.RichHandler()],\n    )\n    logging.getLogger(\"spectre\").setLevel(log_level)\n"
  },
  {
    "path": "support/Python/Machines.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\"\"\"Support for host machines, such as supercomputers.\n\nMachines are defined as YAML files in 'support/Machines/'. To add support for a\nnew machine, add a YAML file that defines a `Machine:` key with the attributes\nlisted in the `Machine` class below. Also add a submit script with the same\nname to 'support/SubmitScripts/'.\n\nTo select a machine, specify the `MACHINE` option when configuring the CMake\nbuild.\n\"\"\"\n\nimport os\nfrom dataclasses import dataclass\nfrom typing import List\n\n# functools.cache was added in Py 3.9. Fall back to 'lru_cache' in earlier\n# versions, which is pretty much the same but slightly slower.\ntry:\n    from functools import cache\nexcept ImportError:\n    from functools import lru_cache\n\n    cache = lru_cache(maxsize=None)\n\nimport yaml\n\n\n@dataclass(frozen=True)\nclass Machine(yaml.YAMLObject):\n    \"\"\"A machine we know how to run on, such as a particular supercomputer.\n\n    Many configuration options for job submission are set in the submit script\n    for the machine (in 'support/SubmitScripts/'). Here we provide additional\n    metadata about the machine.\n\n    Attributes:\n      Name: A short name for the machine. Must match the YAML file name.\n      Description: A description of the machine. Give some basic context and\n        any information that may help people get started using the machine.\n        Provide links to wiki pages, signup pages, etc., for additional\n        information.\n      DefaultTasksPerNode: Default number of tasks per node (MPI ranks).\n        Often chosen to be the number of sockets on a node.\n      DefaultProcsPerTask: Default number of worker threads spawned per task.\n        It is often advised to leave one core per node or socket free for\n        communication, so this might be the number of cores or hyperthreads\n        per node or socket minus one.\n      DefaultQueue: Default queue that jobs are submitted to. On Slurm systems\n        you can see the available queues with `sinfo`.\n      DefaultTimeLimit: Default wall time limit for submitted jobs. For\n        acceptable formats, see: https://slurm.schedmd.com/sbatch.html#OPT_time\n      LaunchCommandSingleNode: Command to launch an executable on a single\n        compute node, e.g. [\"mpirun\", \"-n\", \"1\"]. This is used to run\n        executables on interactive compute nodes. This is _not_ the full command\n        to launch an executable in scheduled jobs, which can be found in the\n        submit script instead. This is also not used on non-compute (login)\n        nodes.\n      LaunchCommandLoginNode: Command to launch an executable on a the\n        login node. This is the counterpart to `LaunchCommandSingleNode` which\n        is to be used as a prefix for commands run on non-compute (login) nodes.\n    \"\"\"\n\n    yaml_tag = \"!Machine\"\n    yaml_loader = yaml.SafeLoader\n    # The YAML machine files can have these attributes:\n    Name: str\n    Description: str\n    DefaultTasksPerNode: int\n    DefaultProcsPerTask: int\n    DefaultQueue: str\n    DefaultTimeLimit: str\n    LaunchCommandSingleNode: List[str]\n    LaunchCommandLoginNode: List[str]\n\n    def on_compute_node(self) -> bool:\n        \"\"\"Determines whether or not we are running on a compute node.\"\"\"\n        return os.environ.get(\"SLURM_JOB_ID\") is not None\n\n    @property\n    def launch_command(self) -> List[str]:\n        \"\"\"The command to launch an executable on the machine.\n\n        Prepend this list to the command you want to run.\n        \"\"\"\n        if self.on_compute_node():\n            return self.LaunchCommandSingleNode\n        else:\n            return self.LaunchCommandLoginNode\n\n\n# Parse YAML machine files as Machine objects\nyaml.SafeLoader.add_path_resolver(\"!Machine\", [\"Machine\"], dict)\n\n\nclass UnknownMachineError(Exception):\n    \"\"\"Indicates we were unsuccessful in identifying the current machine\"\"\"\n\n    pass\n\n\n@cache\ndef this_machine(\n    machinefile_path=os.path.join(os.path.dirname(__file__), \"Machine.yaml\"),\n    raise_exception=True,\n) -> Machine:\n    \"\"\"Determine the machine we are running on.\n\n    Specify the 'MACHINE' option in the CMake build configuration to select a\n    machine, or pass the 'machinefile_path' argument to this function.\n\n    Arguments:\n      machinefile_path: Path to a YAML file that describes the current machine.\n        Defaults to the machine selected in the CMake build configuration.\n      raise_exception: If 'True' (default), raises an 'UnknownMachineError' if\n        no machine was selected. Otherwise, returns 'None' if no machine was\n        selected.\n\n    Returns: A 'Machine' object that describes the current machine, or 'None' if\n      no machine was selected and 'raise_exception' is 'False'.\n    \"\"\"\n    if not os.path.exists(machinefile_path):\n        if not raise_exception:\n            return None\n        raise UnknownMachineError(\n            \"No machine was selected. Specify the 'MACHINE' option when \"\n            \"configuring the build with CMake. If you are running on a new \"\n            \"machine, please add it to 'support/Machines/'. The machine file \"\n            f\"was expected at the following path:\\n  {machinefile_path}\"\n        )\n    with open(machinefile_path, \"r\") as open_machinefile:\n        return yaml.safe_load(open_machinefile)[\"Machine\"]\n"
  },
  {
    "path": "support/Python/Resubmit.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport subprocess\nfrom pathlib import Path\nfrom typing import Optional\n\nimport click\nimport yaml\n\nfrom spectre.support.DirectoryStructure import Segment, list_segments\nfrom spectre.support.Schedule import schedule\n\n\ndef resubmit(\n    segments_dir: Path,\n    context_file_name: str = \"SchedulerContext.yaml\",\n    **kwargs,\n) -> Optional[subprocess.CompletedProcess]:\n    \"\"\"Create the next segment in the SEGMENTS_DIR and schedule it\n\n    \\f\n    Arguments:\n      segments_dir: Path to the segments directory, or path to the last segment\n        in the segments directory. The next segment will be created here.\n      context_file_name: Optional. Name of the file that stores the context\n        for resubmissions in the 'run_dir'. This file gets created by\n        'spectre.support.schedule'. (Default: \"SchedulerContext.yaml\")\n\n    Returns: The 'subprocess.CompletedProcess' representing the process\n      that scheduled the run. Returns 'None' if no run was scheduled.\n    \"\"\"\n    # Resolve segments dir (if invoked from within a segment)\n    segment = Segment.match(segments_dir)\n    if segment:\n        segments_dir = segment.path.resolve().parent\n    # Resolve last segment\n    all_segments = list_segments(segments_dir)\n    assert all_segments, (\n        f\"Directory '{segments_dir}' contains no segments \"\n        f\"that match the pattern '{Segment.NAME_PATTERN.pattern}'.\"\n    )\n    last_segment = all_segments[-1]\n    if segment:\n        assert segment.path.resolve() == last_segment.path.resolve(), (\n            f\"The specified segment ({segment.path}) is not the last in the\"\n            f\" directory ({last_segment.path}). If you want to resubmit from\"\n            \" the specified segment, remove the later segments and try again.\"\n            \" Otherwise, resubmit from the last segment or the parent\"\n            \" (segments) directory.\"\n        )\n    context_file = last_segment.path / context_file_name\n    # Load context\n    with open(context_file, \"r\") as open_context_file:\n        context = yaml.safe_load(open_context_file)\n    # Continue from the last checkpoint\n    last_segment_checkpoints = last_segment.checkpoints\n    assert last_segment_checkpoints, (\n        f\"The last segment '{last_segment.path}' contains no checkpoints. It\"\n        \" may be incomplete. Did you forget to remove it?\"\n    )\n    context[\"from_checkpoint\"] = last_segment_checkpoints[-1]\n    # We resubmit the rendered input file from the last segment instead of\n    # trying to render the template again (the input file template wasn't copied\n    # to the segments directory, so there's no guarantee it still exists)\n    context[\"input_file_template\"] = last_segment.path / context[\"input_file\"]\n    # Remove the old `run_dir`. It will be set to the new segment.\n    del context[\"run_dir\"]\n    # Override the `segments_dir` just to be safe\n    context[\"segments_dir\"] = segments_dir\n    # Override with CLI options\n    context.update(kwargs)\n    schedule(**context)\n\n\n@click.command(name=\"resubmit\", help=resubmit.__doc__)\n@click.argument(\n    \"segments_dirs\",\n    type=click.Path(\n        exists=True,\n        file_okay=False,\n        dir_okay=True,\n        readable=True,\n        path_type=Path,\n    ),\n    nargs=-1,\n    required=True,\n)\n@click.option(\n    \"--submit/--no-submit\",\n    default=None,\n    help=(\n        \"Submit jobs automatically. If neither option is \"\n        \"specified, a prompt will ask for confirmation before \"\n        \"a job is submitted.\"\n    ),\n)\n@click.option(\n    \"--context-file-name\",\n    default=\"SchedulerContext.yaml\",\n    show_default=True,\n    help=\"Name of the context file that supports resubmissions.\",\n)\ndef resubmit_command(segments_dirs, **kwargs):\n    _rich_traceback_guard = True  # Hide tracebacks until here\n    for segment_dir in segments_dirs:\n        resubmit(segment_dir, **kwargs)\n\n\nif __name__ == \"__main__\":\n    resubmit_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Python/RunNext.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport contextlib\nimport importlib\nimport logging\nimport os\nfrom pathlib import Path\n\nimport click\nimport yaml\n\nlogger = logging.getLogger(__name__)\n\n\n@contextlib.contextmanager\ndef working_directory(cwd: Path):\n    \"\"\"Temporarily change the working directory to 'cwd'.\"\"\"\n    prev_cwd = Path.cwd()\n    os.chdir(cwd)\n    try:\n        yield\n    finally:\n        os.chdir(prev_cwd)\n\n\ndef run_next(next_entrypoint: dict, input_file_path: Path, cwd: Path):\n    \"\"\"Run the next entrypoint specified in the input file metadata\n\n    Invokes the Python function specified in the 'Next' section of the input\n    file metadata. It can be specified like this:\n\n    \\b\n    ```yaml\n    # Input file metadata\n    Next:\n      Run: spectre.Pipelines.Bbh.Inspiral:start_inspiral\n      With:\n        # Arguments to pass to the function\n        submit: True\n    ---\n    # Rest of the input file\n    ```\n\n    The function will be invoked in the `cwd` directory ('--input-run-dir' /\n    '-i'), which defaults to the directory of the input file. The following\n    special values can be used for the arguments:\n\n    - '__file__': The (absolute) path of the input file.\n    - 'None': The Python value None.\n\n    \\f\n    Arguments:\n      next_entrypoint: The Python function to run. Must be a dictionary with\n        the following keys:\n        - \"Run\": The Python module and function to run, separated by a colon.\n          For example, \"spectre.Pipelines.Bbh.Ringdown:start_ringdown\".\n        - \"With\": A dictionary of arguments to pass to the function.\n      input_file_path: Path to the input file that specified the entrypoint.\n        Used to resolve '__file__' in the entrypoint arguments.\n      cwd: The working directory in which to run the entrypoint. Used to\n        resolve relative paths in the entrypoint arguments.\n    \"\"\"\n    next_function = next_entrypoint[\"Run\"].split(\":\")\n    next_function = getattr(\n        importlib.import_module(next_function[0]), next_function[1]\n    )\n    next_args = next_entrypoint[\"With\"]\n    substitutions = {\n        \"__file__\": input_file_path.resolve(),\n        \"None\": None,\n    }\n    next_args = {\n        key: (\n            substitutions.get(value, value) if isinstance(value, str) else value\n        )\n        for key, value in next_args.items()\n    }\n    with working_directory(cwd):\n        return next_function(**next_args)\n\n\n@click.command(name=\"run-next\", help=run_next.__doc__)\n@click.argument(\n    \"input_file_path\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n)\n@click.option(\n    \"-i\",\n    \"--input-run-dir\",\n    type=click.Path(\n        exists=True,\n        file_okay=False,\n        dir_okay=True,\n        readable=True,\n        path_type=Path,\n    ),\n    help=(\n        \"Directory where the input file ran. Paths in the input file are\"\n        \" relative to this directory.\"\n    ),\n    show_default=\"directory of the INPUT_FILE_PATH\",\n)\ndef run_next_command(input_file_path, input_run_dir):\n    if input_run_dir is None:\n        input_run_dir = Path(input_file_path).resolve().parent\n    with open(input_file_path) as open_input_file:\n        metadata = next(yaml.safe_load_all(open_input_file))\n    if not metadata or \"Next\" not in metadata:\n        logger.info(\n            \"The input file metadata lists no 'Next' entrypoint. Nothing to do.\"\n        )\n        return\n    run_next(\n        metadata[\"Next\"], input_file_path=input_file_path, cwd=input_run_dir\n    )\n\n\nif __name__ == \"__main__\":\n    run_next_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Python/Schedule.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport functools\nimport logging\nimport os\nimport re\nimport shutil\nimport subprocess\nfrom pathlib import Path\nfrom typing import Optional, Sequence, Union\n\nimport click\nimport jinja2\nimport jinja2.meta\nimport numpy as np\nimport yaml\nfrom rich.pretty import pretty_repr\n\nfrom spectre.support.DirectoryStructure import (\n    Checkpoint,\n    Segment,\n    list_checkpoints,\n    list_segments,\n)\nfrom spectre.support.Machines import this_machine\nfrom spectre.support.RunNext import run_next\nfrom spectre.support.Yaml import SafeDumper\nfrom spectre.tools.ValidateInputFile import validate_input_file\nfrom spectre.Visualization.ReadInputFile import find_phase_change\n\nlogger = logging.getLogger(__name__)\n\n# CMake configures the submit script templates for the current machine to this\n# path\ndefault_submit_script_template = Path(__file__).parent / \"SubmitTemplate.sh\"\n\n\ndef _resolve_executable(executable: Union[str, Path]) -> Path:\n    \"\"\"Look up the 'executable' in the PATH\n\n    Raises 'ValueError' if the executable is not found.\n    \"\"\"\n    logger.debug(f\"Resolving executable: {executable}\")\n    # This default bin dir is already added in spectre.__main__.py, but only\n    # when running the CLI. It is the bin dir of the build directory that\n    # contains this script. When running Python code outside the CLI this should\n    # also be the default bin dir.\n    default_bin_dir = Path(__file__).parent.parent.parent.parent.resolve()\n    path = os.environ[\"PATH\"] + \":\" + str(default_bin_dir)\n    which_exec = shutil.which(executable, path=path)\n    if not which_exec:\n        raise ValueError(\n            f\"Executable not found: {executable}. Make sure it is compiled. To\"\n            \" look for executables in a specific build directory make sure it\"\n            \" is in the 'PATH' or use the 'spectre --build-dir / -b' option.\"\n        )\n    return Path(which_exec).resolve()\n\n\ndef _write_or_overwrite(\n    text: str, path: Path, error_hint: Optional[str] = None, force: bool = False\n):\n    \"\"\"Write the 'text' to a file at 'path'\n\n    Raise an 'OSError' if the file already exists, unless called with 'force'\n    or if the existing file content is identical to the 'text'.\n    The 'hint' is appended to the error message.\n    \"\"\"\n    if path.exists():\n        if path.read_text() == text:\n            return\n        if not force:\n            raise OSError(\n                f\"File already exists at '{path}'. Retry with \"\n                \"'force' ('--force' / '-f') to overwrite.\"\n                + ((\"\\n\" + error_hint) if error_hint else \"\")\n            )\n    logger.debug(f\"Write file: {path}\")\n    path.write_text(text)\n\n\ndef _copy_to_dir(src_file: Path, dest_dir: Path, force: bool = False) -> Path:\n    \"\"\"Copy the 'src_file' to the 'dest_dir', keeping the file name the same\n\n    Returns the path to the new file.\n    \"\"\"\n    assert src_file.is_file()\n    assert dest_dir.is_dir()\n    if src_file.resolve().parent == dest_dir.resolve():\n        return src_file\n    dest = (dest_dir / src_file.name).resolve()\n    if dest.exists() and not force:\n        raise OSError(\n            f\"File already exists at '{dest}'. Retry with \"\n            \"'force' ('--force' / '-f') to overwrite.\"\n        )\n    logging.debug(f\"Copy file: {src_file} -> {dest}\")\n    shutil.copy(src_file, dest)\n    return dest\n\n\ndef _copy_submit_script_template(\n    submit_script_template: Path,\n    dest_dir: Path,\n    template_env: jinja2.Environment,\n    force: bool = False,\n) -> Path:\n    dest = _copy_to_dir(submit_script_template, dest_dir, force=force)\n    # Also copy all referenced templates (the \"SubmitBase.sh\" parent template)\n    syntax_tree = template_env.parse(dest.read_text())\n    for referenced_template in jinja2.meta.find_referenced_templates(\n        syntax_tree\n    ):\n        referenced_template_src = (\n            submit_script_template.resolve().parent / referenced_template\n        )\n        _copy_to_dir(referenced_template_src, dest_dir, force=force)\n    return dest\n\n\ndef schedule(\n    input_file_template: Union[str, Path],\n    scheduler: Optional[Union[str, Sequence]],\n    no_schedule: Optional[bool] = None,\n    executable: Optional[Union[str, Path]] = None,\n    run_dir: Optional[Union[str, Path]] = None,\n    segments_dir: Optional[Union[str, Path]] = None,\n    copy_executable: Optional[bool] = None,\n    job_name: Optional[str] = None,\n    submit_script_template: Optional[Union[str, Path]] = None,\n    from_checkpoint: Optional[Union[str, Path, Checkpoint]] = None,\n    input_file_name: Optional[str] = None,\n    submit_script_name: str = \"Submit.sh\",\n    out_file_name: str = \"spectre.out\",\n    context_file_name: str = \"SchedulerContext.yaml\",\n    submit: Optional[bool] = None,\n    clean_output: bool = False,\n    force: bool = False,\n    validate: Optional[bool] = True,\n    profile_with: Optional[str] = None,\n    extra_params: dict = {},\n    **kwargs,\n) -> Optional[subprocess.CompletedProcess]:\n    \"\"\"Schedule executable runs with an input file\n\n    Configures the input file, submit script, etc. to the 'run_dir', and then\n    invokes the 'scheduler' to submit the run (typically \"sbatch\"). You can also\n    bypass the scheduler and run the executable directly by setting the\n    'scheduler' to 'None'.\n\n    # Selecting the executable\n\n    Specify either a path to the executable, or just its name if it's in the\n    'PATH'. If unspecified, the 'Executable' listed in the input file metadata\n    is used.\n\n    By default, the executable and submit scripts will be copied to the segments\n    directory to support resubmissions (see below). See the 'copy_executable'\n    argument docs for details on controlling this behavior.\n\n    # Segments and run directories\n\n    You can set either the 'run_dir' or the 'segments_dir' to specify where the\n    executable will run (but not both). If you specify a 'run_dir', the\n    executable will run in it directly. If you specify a 'segments_dir', a new\n    segment will be created and used as the 'run_dir'. Segments are named with\n    incrementing integers and continue the run from the previous segment. For\n    example, the following is a typical 'segments_dir':\n\n    \\b\n    ```sh\n    # Copy of the executable\n    MyExecutable\n    # Copy of the submit script template (base and machine-specific)\n    SubmitTemplateBase.sh\n    SubmitTemplate.sh\n    # One segment per day\n    Segment_0000/\n        InputFile.yaml\n        Submit.sh\n        Output.h5\n        # Occasional checkpoints, and a checkpoint before termination\n        Checkpoints/\n            Checkpoint_0000/\n            Checkpoint_0001/...\n    # Next segment continues from last checkpoint of previous segment\n    Segment_0001/...\n    ```\n\n    You can omit the 'run_dir' if the current working directory already contains\n    the input file.\n\n    # Placeholders\n\n    The input file, submit script, 'run_dir', 'segments_dir', and 'job_name' can\n    have placeholders like '{{ num_nodes }}'. They must conform to the\n    [Jinja template format](https://jinja.palletsprojects.com/en/3.0.x/templates/).\n    The placeholders are resolved in the following stages.\n    The following parameters are available as placeholders:\n\n    1. 'job_name' (if specified):\n\n    \\b\n        - All arguments to this function, including all additional '**kwargs'.\n          For example, the additional '**kwargs' can include parameters\n          controlling resolution in the input file.\n        - 'executable_name': Just the name of the executable (basename of the\n          'executable' path).\n\n    2. 'run_dir' and 'segments_dir':\n\n    \\b\n        - All parameters from the previous stage.\n        - 'job_name': Either the resolved 'job_name' from the previous stage, or\n          the 'executable_name' if unspecified.\n\n    3. Input file & submit script:\n\n    \\b\n        - All parameters from the previous stages.\n        - 'run_dir': Absolute path to the 'run_dir'.\n        - 'segments_dir': Absolute path to the 'segments_dir', or 'None' if no\n          segments directory is available.\n        - 'input_file': Relative path to the configured input file (in the\n          'run_dir').\n        - 'out_file': Absolute path to the log file (in the 'run_dir').\n        - 'spectre_cli': Absolute path to the SpECTRE CLI.\n        - Typical additional parameters used in submit scripts are 'queue' and\n          'time_limit'.\n\n    The parameters used to render the submit script are stored in a context file\n    (named 'context_file_name') in the 'run_dir' to support resubmissions. The\n    context file is used by 'spectre.support.resubmit' to schedule the next\n    segment using the same parameters.\n\n    # Scheduling multiple runs\n\n    You can pass ranges of parameters to the '**kwargs' of this function to\n    schedule multiple runs using the same input file template. For example, you\n    can do an h-convergence test by using a placeholder for the refinement level\n    in your input file:\n\n    \\b\n    ```yaml\n    # In the domain creator:\n    InitialRefinement: {{ lev }}\n    ```\n\n    When a parameter in '**kwargs' is an iterable, the 'schedule' function will\n    recurse for every element in the iterable. For example, you can schedule\n    multiple runs for a convergence test like this:\n\n    \\b\n    ```py\n    schedule(\n        run_dir=\"Lev{{ lev }}\",\n        # ...\n        lev=range(1, 3))\n    ```\n\n    \\f\n    Arguments:\n      input_file_template: Path to an input file. It will be copied to the\n        'run_dir'. It can be a Jinja template (see above).\n      scheduler: 'None' to run the executable directly, or a scheduler such as\n        \"sbatch\" to submit the run to a queue.\n      no_schedule: Optional. If 'True', override the 'scheduler' to 'None'.\n        Useful to specify on the command line where the 'scheduler' defaults to\n        \"sbatch\" on clusters.\n      executable: Path or name of the executable to run. If unspecified, use the\n        'Executable' set in the input file metadata.\n      run_dir: The directory to which input file, submit script, etc. are\n        copied, and relative to which the executable will run.\n        Can be a Jinja template (see above).\n      segments_dir: The directory in which a new segment is created as the\n        'run_dir'. Mutually exclusive with 'run_dir'.\n        Can be a Jinja template (see above).\n      copy_executable: Copy the executable to the run or segments directory.\n        By default (when set to 'None'):\n          - If '--run-dir' / '-o' is set, don't copy.\n          - If '--segments-dir' / '-O' is set, copy to segments directory to\n            support resubmission.\n        When set to 'True':\n          - If '--run-dir' / '-o' is set, copy to the run directory.\n          - If '--segments-dir' / '-O' is set, copy to segments directory to\n            support resubmission. Still don't copy to individual segments.\n        When set to 'False': Never copy.\n      job_name: Optional. A string describing the job.\n        Can be a Jinja template (see above). (Default: executable name)\n      submit_script_template: Optional. Path to a submit script. It will be\n        copied to the 'run_dir' if a 'scheduler' is set. Can be a Jinja template\n        (see above). (Default: value of 'default_submit_script_template')\n      from_checkpoint: Optional. Path to a checkpoint directory.\n      input_file_name: Optional. Filename of the input file in the 'run_dir'.\n        (Default: basename of the 'input_file_template')\n      submit_script_name: Optional. Filename of the submit script. (Default:\n        \"Submit.sh\")\n      out_file_name: Optional. Name of the log file. (Default:\n        \"spectre.out\")\n      context_file_name: Optional. Name of the file that stores the context\n        for resubmissions in the `run_dir`. Used by `spectre.support.resubmit`.\n        (Default: \"SchedulerContext.yaml\")\n      submit: Optional. If 'True', automatically submit jobs using the\n        'scheduler'. If 'False', skip the job submission. If 'None', prompt for\n        confirmation before submitting.\n      clean_output: Optional. When 'True', use\n        'spectre.tools.CleanOutput.clean_output' to clean up existing output\n        files in the 'run_dir' before scheduling the run. (Default: 'False')\n      force: Optional. When 'True', overwrite input file and submit script\n        in the 'run_dir' instead of raising an error when they already exist.\n      validate: Optional. When 'True', validate that the input file is parsed\n        correctly. When 'False' skip this step.\n      profile_with: Optional. When set to \"hpctoolkit\", enable profiling with\n        HPCToolkit. No other profilers are currently supported. The executable\n        must be compiled so that it's compatible with HPCToolkit (see\n        https://spectre-code.org/profiling.html). This will modify the submit\n        script to run the executable with 'hpcrun' and postprocess the profiling\n        data with 'hpcstruct' and 'hpcprof'. (Default: False).\n      extra_params: Optional. Dictionary of extra parameters passed to input\n        file and submit script templates. Parameters can also be passed as\n        keyword arguments to this function instead.\n\n    Returns: The 'subprocess.CompletedProcess' representing either the process\n      that scheduled the run, or the process that ran the executable if\n      'scheduler' is 'None'. Returns 'None' if no or multiple runs were\n      scheduled.\n    \"\"\"\n    # Defaults\n    input_file_template = Path(input_file_template)\n    if not input_file_name:\n        input_file_name = input_file_template.resolve().name\n    if no_schedule:\n        scheduler = None\n    if scheduler and not submit_script_template:\n        submit_script_template = default_submit_script_template\n    if isinstance(from_checkpoint, Checkpoint):\n        from_checkpoint = from_checkpoint.path\n    if from_checkpoint:\n        from_checkpoint = Path(from_checkpoint).resolve()\n\n    # Snapshot function arguments for template substitutions\n    kwargs.update(extra_params)\n    del extra_params\n    all_args = locals().copy()\n    del all_args[\"kwargs\"]\n\n    # Recursively schedule ranges of runs\n    for key, value in kwargs.items():\n        # Check if the parameter is an iterable\n        if isinstance(value, str):\n            # Strings are iterable, but we don't want to treat them as such\n            continue\n        try:\n            iter(value)\n        except TypeError:\n            continue\n        # Recurse for each value of the iterable\n        for value_i in value:\n            logger.info(f\"Recurse for {key}={value_i}\")\n            kwargs_i = kwargs.copy()\n            kwargs_i.update({key: value_i})\n            try:\n                schedule(**all_args, **kwargs_i)\n            except:\n                logger.exception(f\"Recursion for {key}={value_i} failed.\")\n        return\n\n    # Resolve number of cores, nodes, etc.\n    machine = this_machine(raise_exception=False)\n    num_procs = kwargs.get(\"num_procs\")\n    num_nodes = kwargs.get(\"num_nodes\")\n    num_slurm_tasks = None\n    if num_procs:\n        assert (\n            num_nodes is None or num_nodes == 1\n        ), \"Specify either 'num_procs' or 'num_nodes', not both.\"\n        if machine:\n            # Approximately round up to nearest number of full nodes\n            cores_per_node = (\n                machine.DefaultTasksPerNode * machine.DefaultProcsPerTask\n            )\n            if num_procs <= cores_per_node:\n                num_nodes = 1\n                num_slurm_tasks = int(\n                    np.ceil(num_procs / machine.DefaultProcsPerTask)\n                )\n            else:\n                num_nodes = int(np.ceil(num_procs / cores_per_node))\n                logger.info(f\"Rounded up to run on {num_nodes} full node(s).\")\n                num_procs = None\n        else:\n            num_nodes = 1\n    # Update kwargs with resolved num_procs and num_nodes (used to build\n    # `context` below)\n    kwargs.update(\n        num_procs=num_procs,\n        num_nodes=num_nodes,\n        num_slurm_tasks=num_slurm_tasks,\n    )\n\n    # Set up template environment with basic configuration\n    template_env = jinja2.Environment(\n        undefined=jinja2.StrictUndefined,\n        trim_blocks=True,\n        lstrip_blocks=True,\n        keep_trailing_newline=True,\n    )\n\n    # Start collecting parameters for template substitutions. We filter 'None'\n    # values so they properly trigger undefined-variable errors when templates\n    # need them.\n    context = {\n        key: value\n        for key, value in dict(**all_args, **kwargs).items()\n        if value is not None\n    }\n\n    # Read input file template\n    input_file_contents = input_file_template.read_text()\n\n    # Resolve executable\n    if not executable:\n        # Can't parse the full input file yet because we haven't collected all\n        # parameters yet. Instead, just parse the metadata. We use the YAML\n        # document start indicator '---' to drop the rest of the input file.\n        # Note that the document start indicator is optional for the first\n        # document in the file and there may be comments or a version directive\n        # before it, so we drop the last document in the file rather than split\n        # on the first '---'.\n        metadata_template = input_file_contents.rpartition(\"---\")[0]\n        metadata_yaml = template_env.from_string(metadata_template).render(\n            context\n        )\n        metadata = yaml.safe_load(metadata_yaml)\n        try:\n            executable = metadata[\"Executable\"]\n        except (KeyError, TypeError) as err:\n            raise ValueError(\n                \"Specify an 'executable' ('--executable' / '-E') \"\n                \"or list one in the input file metadata \"\n                \"as 'Executable:'.\"\n            ) from err\n    executable = _resolve_executable(executable)\n    logger.info(f\"Running with executable: {executable}\")\n    # Only set executable name because the path may change if we copy it later\n    context.update(executable_name=executable.name)\n\n    # Resolve job_name\n    if job_name:\n        job_name = template_env.from_string(job_name).render(context).strip()\n    else:\n        job_name = executable.name\n    context.update(job_name=job_name)\n\n    # Resolve run_dir and segments_dir\n    if run_dir and segments_dir:\n        raise ValueError(\n            \"Specify either 'run_dir' ('--run-dir' / '-o') \"\n            \"or 'segments_dir' ('--segments-dir' / '-O'), not both.\"\n        )\n    elif not run_dir and not segments_dir:\n        # Neither run_dir nor segments_dir were specified. Set run_dir to the\n        # current working directory.\n        if input_file_template.resolve().parent == Path.cwd():\n            run_dir = Path.cwd()\n        else:\n            raise ValueError(\n                \"Specify a 'run_dir' ('--run-dir' / '-o') \"\n                \"or a 'segments_dir' ('--segments-dir' / '-O'), \"\n                \"or place the input file into the current directory.\"\n            )\n    # At this point either run_dir or segments_dir are set, but not both.\n    if run_dir:\n        # Run directly in the run_dir. If the run_dir looks like a segment, set\n        # the segments_dir so resubmitting works.\n        run_dir = Path(\n            template_env.from_string(str(run_dir)).render(context).strip()\n        )\n        if Segment.match(run_dir):\n            segments_dir = run_dir.resolve().parent\n            all_segments = list_segments(segments_dir)\n    else:\n        # Run in next segment in the segments_dir\n        segments_dir = Path(\n            template_env.from_string(str(segments_dir)).render(context).strip()\n        )\n        all_segments = list_segments(segments_dir)\n        if all_segments:\n            run_dir = all_segments[-1].next.path\n        else:\n            run_dir = Segment.first(segments_dir).path\n    if segments_dir and all_segments:\n        # Make sure we're continuing the last checkpoint of the last segment.\n        # This requirement can be relaxed in the future if needed.\n        assert from_checkpoint, (\n            f\"Found existing segments in directory '{segments_dir}'. Use\"\n            \" '--from-last-checkpoint' to continue from the last\"\n            \" checkpoint in this directory.\"\n        )\n        last_segment = all_segments[-1]\n        assert (\n            from_checkpoint.parent == last_segment.checkpoints_dir.resolve()\n        ), (\n            \"You're not continuing from the last segment\"\n            f\" ({last_segment.path}). It is technically possible to continue\"\n            \" from a different checkpoint, but probably wrong. This assert\"\n            \" safeguards against inconsistent usage. If you want to continue\"\n            f\" from the checkpoint you specified ({from_checkpoint}), choose a\"\n            \" different directory to run in. Otherwise, use\"\n            \" '--from-last-checkpoint SEGMENTS_DIR' to continue from the\"\n            \" latest segment.\"\n        )\n        last_segment_checkpoints = last_segment.checkpoints\n        assert last_segment_checkpoints, (\n            f\"The last segment '{last_segment.path}' has no checkpoints to\"\n            \" continue from. It is technically possible to continue from a\"\n            \" different checkpoint, but probably wrong. This assert safeguards\"\n            \" against inconsistent usage. If you want to continue from the\"\n            f\" checkpoint you specified ({from_checkpoint}), choose a different\"\n            \" directory to run in. Otherwise, remove the incomplete segment\"\n            \" and use '--from-last-checkpoint SEGMENTS_DIR' to continue from\"\n            \" the latest segment.\"\n        )\n        last_checkpoint = last_segment_checkpoints[-1]\n        assert from_checkpoint == last_checkpoint.path.resolve(), (\n            \"You're not continuing from the previous segment's last checkpoint\"\n            f\" ({last_checkpoint.path}). This is technically possible, but\"\n            \" probably wrong. This assert safeguards against inconsistent\"\n            \" usage. If you want to continue from the checkpoint you specified\"\n            f\" ({from_checkpoint}), choose a different directory to run in.\"\n            \" Otherwise, use '--from-last-checkpoint LAST_SEGMENT' to continue\"\n            \" from the last checkpoint.\"\n        )\n    context.update(run_dir=run_dir.resolve())\n    if segments_dir:\n        context.update(segments_dir=segments_dir.resolve())\n\n    # Resolve outfile\n    out_file = run_dir / out_file_name\n    context.update(out_file=out_file.resolve())\n\n    # Create the run directory\n    logger.info(f\"Configure run directory '{run_dir}'\")\n    run_dir.mkdir(parents=True, exist_ok=True)\n\n    # Configure input file\n    input_file_path = run_dir / input_file_name\n    context.update(input_file=input_file_name)\n    logger.debug(\n        f\"Configure input file template '{input_file_template}' with these\"\n        f\" parameters: {pretty_repr(context)}\"\n    )\n    rendered_input_file = template_env.from_string(input_file_contents).render(\n        context\n    )\n    _write_or_overwrite(\n        rendered_input_file,\n        input_file_path,\n        error_hint=(\n            \"If you're scheduling multiple runs, use \"\n            \"placeholders in the directory name such as:\\n\"\n            \"  -p lev=1...3 --run-dir 'Lev{{ lev }}'\"\n        ),\n        force=force,\n    )\n\n    # Validate input file\n    if validate:\n        validate_input_file(\n            input_file_path.resolve(), executable=executable, work_dir=run_dir\n        )\n\n    # - If the input file may request resubmissions, make sure we have a\n    #   segments directory\n    metadata, input_file = yaml.safe_load_all(rendered_input_file)\n    wallclock_exit_phase_change = find_phase_change(\n        \"CheckpointAndExitAfterWallclock\", input_file\n    )\n    if wallclock_exit_phase_change is not None and not segments_dir:\n        raise ValueError(\n            \"Found 'CheckpointAndExitAfterWallclock' in the input file but \"\n            \"no 'segments_dir' ('--segments-dir' / '-O') is set. \"\n            \"Specify a segments directory to enable resubmissions, or \"\n            \"remove 'CheckpointAndExitAfterWallclock' from the input file.\"\n        )\n\n    # Clean output\n    if clean_output:\n        from spectre.tools.CleanOutput import clean_output\n\n        clean_output(input_file=input_file_path, output_dir=run_dir, force=True)\n\n    # Copy executable to run directory if enabled\n    if copy_executable and not segments_dir:\n        executable = _copy_to_dir(executable, run_dir, force=force)\n\n    # If requested, run executable directly and return early\n    if not scheduler:\n        assert num_nodes is None or num_nodes == 1, (\n            \"Running executables directly is only supported on a single node. \"\n            \"Set the 'scheduler' ('--scheduler') to submit a multi-node job \"\n            \"to the queue.\"\n        )\n        auto_provision = num_procs is None\n        provision_info = (\n            \"all available cores\"\n            if auto_provision\n            else f\"{num_procs} core{'s'[:num_procs!=1]}\"\n        )\n        logger.info(\n            f\"Run '{executable.name}' in '{run_dir}' on {provision_info}.\"\n        )\n        machine = this_machine(raise_exception=False)\n        if profile_with is None:\n            profiling_command = []\n        elif profile_with == \"hpctoolkit\":\n            profiling_command = [\n                \"hpcrun\",\n                \"-t\",\n                \"-o\",\n                \"hpctoolkit-measurements\",\n            ]\n        else:\n            raise ValueError(\n                f\"Unsupported profiler: {profile_with}. Currently, only\"\n                \" 'hpctoolkit' is supported.\"\n            )\n        run_command = (\n            (machine.launch_command if machine else [])\n            + profiling_command\n            + [\n                str(executable),\n                \"--input-file\",\n                str(input_file_path.resolve()),\n            ]\n        )\n        if auto_provision:\n            run_command += [\"+auto-provision\"]\n        else:\n            run_command += [\"+p\", str(num_procs)]\n        if from_checkpoint:\n            run_command += [\"+restart\", str(from_checkpoint)]\n        logger.debug(f\"Run command: {run_command}\")\n        if submit is False:\n            return\n        env = os.environ.copy()\n        # Disable multithreading so our executables have control over the\n        # parallelization\n        env[\"OMP_NUM_THREADS\"] = \"1\"\n        env[\"OPENBLAS_NUM_THREADS\"] = \"1\"\n        env[\"MKL_NUM_THREADS\"] = \"1\"\n        process = subprocess.Popen(run_command, cwd=run_dir, env=env)\n        # Realtime streaming of _captured_ stdout and stderr to the console\n        # doesn't seem to work reliably, so we just let the process stream\n        # directly to the console and wait for it to complete.\n        process.wait()\n        # Raise errors on non-zero exit codes\n        if process.returncode != 0:\n            raise subprocess.CalledProcessError(\n                returncode=process.returncode, cmd=run_command\n            )\n        if profile_with == \"hpctoolkit\":\n            subprocess.run(\n                [\"hpcstruct\", \"hpctoolkit-measurements\"],\n                cwd=run_dir,\n                check=True,\n            )\n            subprocess.run(\n                [\n                    \"hpcprof\",\n                    \"-o\",\n                    \"hpctoolkit-database\",\n                    \"hpctoolkit-measurements\",\n                ],\n                cwd=run_dir,\n                check=True,\n            )\n        # Run the 'Next' entrypoint listed in the input file metadata\n        if metadata and \"Next\" in metadata:\n            run_next(\n                metadata[\"Next\"], input_file_path=input_file_path, cwd=run_dir\n            )\n        return process\n\n    # Copy executable to segments directory\n    if (copy_executable or copy_executable is None) and segments_dir:\n        executable = _copy_to_dir(executable, segments_dir, force=force)\n    context.update(executable=executable, copy_executable=copy_executable)\n\n    # Resolve CLI for resubmissions\n    # This is the path of the `spectre` CLI where this script is installed.\n    # We may have to make this more robust for resubmissions if we run into\n    # problems or unexpected behavior.\n    spectre_cli = Path(__file__).parent.parent.parent.parent / \"spectre\"\n    if spectre_cli:\n        context.update(spectre_cli=spectre_cli)\n\n    # Configure submit script\n    submit_script_template = Path(submit_script_template).resolve()\n    if segments_dir:\n        submit_script_template = _copy_submit_script_template(\n            submit_script_template,\n            segments_dir,\n            template_env=template_env,\n            force=force,\n        )\n    context.update(submit_script_template=submit_script_template)\n    logger.debug(\n        f\"Configure submit script template '{submit_script_template}' with\"\n        f\" these parameters: {pretty_repr(context)}\"\n    )\n    # Use a FileSystemLoader to support template inheritance\n    submit_script_template_env = template_env.overlay(\n        loader=jinja2.FileSystemLoader(submit_script_template.parent)\n    )\n    rendered_submit_script = submit_script_template_env.get_template(\n        submit_script_template.name\n    ).render(context)\n    submit_script_path = run_dir / submit_script_name\n    _write_or_overwrite(rendered_submit_script, submit_script_path, force=force)\n\n    # Write context to file to support resubmissions\n    if segments_dir:\n        with open(run_dir / context_file_name, \"w\") as open_context_file:\n            yaml.dump(context, open_context_file, Dumper=SafeDumper)\n\n    # Submit\n    if submit or (\n        submit is None\n        and click.confirm(f\"Submit '{submit_script_path}'?\", default=True)\n    ):\n        if isinstance(scheduler, str):\n            scheduler = [scheduler]\n        submit_process = subprocess.run(\n            list(scheduler) + [submit_script_name],\n            cwd=run_dir,\n            capture_output=True,\n            text=True,\n        )\n        try:\n            submit_process.check_returncode()\n        except subprocess.CalledProcessError as err:\n            raise RuntimeError(\n                f\"Failed submitting job '{job_name}':\\n\"\n                f\"{submit_process.stderr.strip()}\"\n            ) from err\n        # Write Job ID to a file\n        matched_submit_msg = re.match(\n            r\"Submitted batch job (\\d+)\", submit_process.stdout\n        )\n        if matched_submit_msg:\n            jobid = matched_submit_msg.group(1)\n            (run_dir / \"jobid.txt\").write_text(jobid)\n        else:\n            logger.warning(\n                f\"Unable to parse job ID from output: \" + submit_process.stdout\n            )\n            jobid = None\n        logger.info(\n            f\"Submitted job '{job_name}' ({jobid}). \"\n            f\"Output will be written to: {out_file}\"\n        )\n        return submit_process\n\n\ndef _parse_param(value):\n    \"\"\"Parse an additional command-line parameter for template substitutions\n\n    The following values are supported:\n\n    - Integers or floats\n    - List of values: \"1,2,3\"\n    - Exclusive range: \"0..3\" or \"0..<3\" (the latter is clearer, but \"<\" is a\n      special character in the shell)\n    - Inclusive range: \"0...3\"\n    - Exponentiated values: Single numbers like \"2**3\" or \"10**4\", or ranges\n      like \"10**4...6\"\n\n    Note: The syntax for ranges is borrowed from the Swift language:\n    https://docs.swift.org/swift-book/documentation/the-swift-programming-language/basicoperators/#Range-Operators\n    \"\"\"\n    if not isinstance(value, str):\n        return value\n    value = value.strip()\n    # Exponent prefix: 2**x or 10**x, where x is parsed recursively\n    match = re.match(r\"(\\d+)[*]{2}(.+)$\", value)\n    if match:\n        logger.debug(f\"'{value}' is exponentiated\")\n        base = int(match.group(1))\n        exponent = _parse_param(match.group(2))\n        try:\n            # Single exponentiated number\n            return base**exponent\n        except TypeError:\n            # Exponent is a range\n            return [base**exponent_i for exponent_i in exponent]\n    # List\n    value_list = value.strip(\",[]\").split(\",\")\n    if len(value_list) > 1:\n        logger.debug(f\"'{value}' is a list\")\n        return [_parse_param(element.strip()) for element in value_list]\n    # Exclusive range: 0..3 or 0..<3 (the latter is clearer, but '<' is a\n    # special character in the shell)\n    match = re.match(r\"(-?\\d+)[.]{2}[<]?(-?\\d+)$\", value)\n    if match:\n        logger.debug(f\"'{value}' is an exclusive range\")\n        return range(int(match.group(1)), int(match.group(2)))\n    # Inclusive range: 0...3\n    match = re.match(r\"(-?\\d+)[.]{3}(-?\\d+)$\", value)\n    if match:\n        logger.debug(f\"'{value}' is an inclusive range\")\n        return range(int(match.group(1)), int(match.group(2)) + 1)\n    # Integers\n    match = re.match(r\"(-?\\d+)$\", value)\n    if match:\n        logger.debug(f\"'{value}' is an int\")\n        return int(match.group(1))\n    # Floats\n    match = re.match(r\"(-?\\d+[.]\\d*)$\", value)\n    if match:\n        logger.debug(f\"'{value}' is a float\")\n        return float(match.group(1))\n    return value\n\n\ndef _parse_params(ctx, param, all_values):\n    if all_values is None:\n        return {}\n    params = {}\n    for value in all_values:\n        key_and_value = value.split(\"=\")\n        if len(key_and_value) != 2:\n            raise click.BadParameter(\n                f\"The value of '{value}' could not be parsed as a key-value \"\n                \"pair. It should have a single '=' or none.\"\n            )\n        params[key_and_value[0]] = _parse_param(key_and_value[1])\n    return params\n\n\ndef scheduler_options(f):\n    \"\"\"CLI options for the 'schedule' function.\n\n    These options can be reused by other CLI commands that call the 'schedule'\n    function.\n    \"\"\"\n\n    @click.option(\n        \"--executable\",\n        \"-E\",\n        show_default=\"executable listed in input file\",\n        help=(\n            \"The executable to run. Can be a path, or just the name of the\"\n            \" executable if it's in the 'PATH'. If unspecified, the\"\n            \" 'Executable' listed in the input file metadata is used.\"\n        ),\n    )\n    @click.option(\n        \"--run-dir\",\n        \"-o\",\n        # No `type=click.Path` because this can be a Jinja template\n        help=(\n            \"The directory to which input file, submit script, etc. are \"\n            \"copied, relative to which the executable will run, and to \"\n            \"which output files are written. \"\n            \"Defaults to the current working directory if the input file is \"\n            \"already there. \"\n            \"Mutually exclusive with '--segments-dir' / '-O'.\"\n        ),\n    )\n    @click.option(\n        \"--segments-dir\",\n        \"-O\",\n        # No `type=click.Path` because this can be a Jinja template\n        help=(\n            \"The directory in which to create the next segment. \"\n            \"Requires '--from-checkpoint' or '--from-last-checkpoint' \"\n            \"unless starting the first segment.\"\n        ),\n    )\n    @click.option(\n        \"--copy-executable/--no-copy-executable\",\n        default=None,\n        help=(\n            \"Copy the executable to the run or segments directory. \"\n            \"(1) When no flag is specified: \"\n            \"If '--run-dir' / '-o' is set, don't copy. \"\n            \"If '--segments-dir' / '-O' is set, copy to segments \"\n            \"directory to support resubmission. \"\n            \"(2) When '--copy-executable' is specified: \"\n            \"If '--run-dir' / '-o' is set, copy to the run \"\n            \"directory. \"\n            \"If '--segments-dir' / '-O' is set, copy to segments \"\n            \"directory to support resubmission. Still don't copy to \"\n            \"individual segments. \"\n            \"(3) When '--no-copy-executable' is specified: \"\n            \"Never copy.\"\n        ),\n    )\n    @click.option(\n        \"--clean-output\",\n        \"-C\",\n        is_flag=True,\n        help=(\n            \"Clean up existing output files in the run directory \"\n            \"before running the executable. \"\n            \"See the 'spectre clean-output' command for details.\"\n        ),\n    )\n    @click.option(\n        \"--force\",\n        \"-f\",\n        is_flag=True,\n        help=(\n            \"Overwrite existing files in the '--run-dir' / '-o'. \"\n            \"You may also want to use '--clean-output'.\"\n        ),\n    )\n    @click.option(\n        \"--validate/--no-validate\",\n        default=True,\n        help=\"Validate or skip the validation of the input file.\",\n    )\n    @click.option(\n        \"--profile-with\",\n        type=click.Choice([\"hpctoolkit\"], case_sensitive=True),\n        default=None,\n        help=(\n            \"Set to 'hpctoolkit' to enable profiling with HPCToolkit. No other\"\n            \" profilers are currently supported. The executable must be\"\n            \" compiled so that it's compatible with HPCToolkit (see\"\n            \" https://spectre-code.org/profiling.html). This will modify the\"\n            \" submit script to run the executable with 'hpcrun' and postprocess\"\n            \" the profiling data with 'hpcstruct' and 'hpcprof'.\"\n        ),\n    )\n    # Scheduling options\n    @click.option(\n        \"--scheduler\",\n        default=(\"sbatch\" if default_submit_script_template.exists() else None),\n        show_default=(\n            True if default_submit_script_template.exists() else \"none\"\n        ),\n        help=\"The scheduler invoked to queue jobs on the machine.\",\n    )\n    @click.option(\n        \"--no-schedule\",\n        is_flag=True,\n        help=\"Run the executable directly, without scheduling it.\",\n    )\n    @click.option(\n        \"--submit-script-template\",\n        default=None,\n        show_default=str(default_submit_script_template),\n        # No `type=click.Path` because this can be a Jinja template\n        help=(\n            \"Path to a submit script. \"\n            \"It will be copied to the 'run_dir'. It can be a [Jinja template](\"\n            \"https://jinja.palletsprojects.com/en/3.0.x/templates/) \"\n            \"(see main help text for possible placeholders).\"\n        ),\n    )\n    @click.option(\n        \"--job-name\",\n        \"-J\",\n        show_default=\"executable name\",\n        help=(\n            \"A short name for the job \"\n            \"(see main help text for possible placeholders).\"\n        ),\n    )\n    @click.option(\n        \"--num-procs\",\n        \"-j\",\n        \"-c\",\n        type=_parse_param,\n        help=(\n            \"Number of worker threads. Less than a full node will only set as \"\n            \"many Slurm ntasks-per-node as required by machine configuration. \"\n            \"Mutually exclusive with '--num-nodes' / '-N'.\"\n        ),\n    )\n    @click.option(\n        \"--num-nodes\", \"-N\", type=_parse_param, help=\"Number of nodes\"\n    )\n    @click.option(\"--queue\", help=\"Name of the queue.\")\n    @click.option(\n        \"--time-limit\",\n        \"-t\",\n        help=\"Wall time limit. Must be compatible with the chosen queue.\",\n    )\n    @click.option(\n        \"--param\",\n        \"-p\",\n        \"extra_params\",\n        multiple=True,\n        callback=_parse_params,\n        help=(\n            \"Forward an additional parameter to the input file \"\n            \"and submit script templates. \"\n            \"Can be specified multiple times. \"\n            \"Each entry must be a 'key=value' pair, where the key is \"\n            \"the parameter name. The value can be an int, float, \"\n            \"string, a comma-separated list, an inclusive range \"\n            \"like '0...3', an exclusive range like '0..3' or '0..<3', \"\n            \"or an exponentiated value or range like \"\n            \"'2**3' or '10**4...6'. \"\n            \"If a parameter is a list or range, multiple runs are \"\n            \"scheduled recursively. \"\n            \"You can also use the parameter in the 'job_name' and \"\n            \"in the 'run_dir' or 'segment_dir', and when scheduling \"\n            \"ranges of runs you probably should.\"\n        ),\n    )\n    @click.option(\n        \"--submit/--no-submit\",\n        default=None,\n        help=(\n            \"Submit jobs automatically. If neither option is \"\n            \"specified, a prompt will ask for confirmation before \"\n            \"a job is submitted.\"\n        ),\n    )\n    @click.option(\n        \"--context-file-name\",\n        default=\"SchedulerContext.yaml\",\n        show_default=True,\n        help=\"Name of the context file that supports resubmissions.\",\n    )\n    @functools.wraps(f)\n    def wrapper(*args, **kwargs):\n        return f(*args, **kwargs)\n\n    return wrapper\n\n\n@click.command(\n    name=\"schedule\", help=schedule.__doc__.replace(\"**kwargs\", \"--params\")\n)\n@click.argument(\n    \"input_file_template\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n)\n@scheduler_options\n@click.option(\n    \"--from-checkpoint\",\n    type=click.Path(\n        exists=True,\n        file_okay=False,\n        dir_okay=True,\n        readable=True,\n        path_type=Path,\n    ),\n    help=\"Restart from this checkpoint.\",\n)\n@click.option(\n    \"--from-last-checkpoint\",\n    type=click.Path(\n        exists=True,\n        file_okay=False,\n        dir_okay=True,\n        readable=True,\n        path_type=Path,\n    ),\n    help=\"Restart from the last checkpoint in this directory.\",\n)\ndef schedule_command(\n    from_checkpoint,\n    from_last_checkpoint,\n    **kwargs,\n):\n    _rich_traceback_guard = True  # Hide traceback until here\n    if from_checkpoint and from_last_checkpoint:\n        raise click.UsageError(\n            \"Specify either '--from-checkpoint' or '--from-last-checkpoint', \"\n            \"not both.\"\n        )\n    if from_last_checkpoint:\n        segments = list_segments(from_last_checkpoint)\n        if segments:\n            segment = segments[-1]\n        else:\n            segment = Segment.match(from_last_checkpoint)\n        if segment:\n            all_checkpoints = segment.checkpoints\n            assert all_checkpoints, (\n                f\"The segment '{segment}' contains no checkpoints. It may\"\n                \" be incomplete. Did you forget to remove it?\"\n            )\n        else:\n            all_checkpoints = list_checkpoints(from_last_checkpoint)\n            assert all_checkpoints, (\n                f\"Directory '{from_last_checkpoint}' contains no checkpoints \"\n                f\"that match the pattern '{Checkpoint.NAME_PATTERN.pattern}'.\"\n            )\n        from_checkpoint = all_checkpoints[-1]\n    schedule(from_checkpoint=from_checkpoint, **kwargs)\n\n\nif __name__ == \"__main__\":\n    schedule_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "support/Python/Yaml.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom pathlib import Path\n\nimport numpy as np\nimport yaml\n\n\n# Write `pathlib.Path` objects to YAML as plain strings\ndef _path_representer(dumper: yaml.Dumper, path: Path) -> yaml.nodes.ScalarNode:\n    return dumper.represent_scalar(\"tag:yaml.org,2002:str\", str(path))\n\n\n# Write `numpy.float64` as regular floats\ndef _numpy_representer(\n    dumper: yaml.Dumper, value: np.float64\n) -> yaml.nodes.ScalarNode:\n    return dumper.represent_scalar(\"tag:yaml.org,2002:float\", str(value))\n\n\nSafeDumper = yaml.SafeDumper\nSafeDumper.add_multi_representer(Path, _path_representer)\nSafeDumper.add_multi_representer(np.float64, _numpy_representer)\n"
  },
  {
    "path": "support/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n__version__ = \"@SPECTRE_VERSION@\"\n"
  },
  {
    "path": "support/Python/__main__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\n\nimport click\nimport rich.traceback\nimport yaml\n\nimport spectre\nfrom spectre.support.CliExceptions import RequiredChoiceError\nfrom spectre.support.Logging import configure_logging\nfrom spectre.support.Machines import UnknownMachineError, this_machine\n\nlogger = logging.getLogger(__name__)\n\n\n# Load subcommands lazily, i.e., only import the module when the subcommand is\n# invoked. This is important so the CLI responds quickly.\nclass Cli(click.Group):\n    def list_commands(self, ctx):\n        return [\n            \"bbh\",\n            \"clean-output\",\n            \"combine-h5\",\n            \"element-id-to-paraview\",\n            \"delete-subfiles\",\n            \"eccentricity-control-params\",\n            \"extend-connectivity\",\n            \"extract-dat\",\n            \"extract-input\",\n            \"find-radial-surface\",\n            \"generate-tetrahedral-connectivity\",\n            \"generate-xdmf\",\n            \"interpolate-to-mesh\",\n            \"interpolate-to-points\",\n            \"plot\",\n            \"render-1d\",\n            \"render-3d\",\n            \"resubmit\",\n            \"run-next\",\n            \"schedule\",\n            \"simplify-traces\",\n            \"status\",\n            \"transform-volume-data\",\n            \"validate\",\n        ]\n\n    def get_command(self, ctx, name):\n        if name == \"bbh\":\n            from spectre.Pipelines.Bbh import bbh_pipeline\n\n            return bbh_pipeline\n        if name == \"bns\":\n            from spectre.Pipelines.Bns import bns_pipeline\n\n            return bns_pipeline\n        if name == \"clean-output\":\n            from spectre.tools.CleanOutput import clean_output_command\n\n            return clean_output_command\n        elif name == \"combine-h5\":\n            from spectre.IO.H5.CombineH5 import combine_h5_command\n\n            return combine_h5_command\n        elif name == \"element-id-to-paraview\":\n            from spectre.Visualization.ElementIdToParaview import (\n                element_id_to_paraview_command,\n            )\n\n            return element_id_to_paraview_command\n        elif name == \"delete-subfiles\":\n            from spectre.IO.H5.DeleteSubfiles import delete_subfiles_command\n\n            return delete_subfiles_command\n        elif name in [\"eccentricity-control-params\", \"ecc-control-params\"]:\n            from spectre.Pipelines.EccentricityControl.EccentricityControlParams import (\n                eccentricity_control_params_command,\n            )\n\n            return eccentricity_control_params_command\n        elif name == \"extend-connectivity\":\n            from spectre.IO.H5.ExtendConnectivityData import (\n                extend_connectivity_data_command,\n            )\n\n            return extend_connectivity_data_command\n        elif name == \"extract-dat\":\n            from spectre.IO.H5.ExtractDatFromH5 import extract_dat_command\n\n            return extract_dat_command\n        elif name == \"extract-input\":\n            from spectre.IO.H5.ExtractInputSourceYamlFromH5 import (\n                extract_input_source_from_h5_command,\n            )\n\n            return extract_input_source_from_h5_command\n        elif name == \"find-radial-surface\":\n            from spectre.SurfaceFinder.FindRadialSurface import (\n                find_radial_surface_command,\n            )\n\n            return find_radial_surface_command\n        elif name == \"generate-tetrahedral-connectivity\":\n            from spectre.Visualization.GenerateTetrahedralConnectivity import (\n                generate_tetrahedral_connectivity_command,\n            )\n\n            return generate_tetrahedral_connectivity_command\n        elif name == \"generate-xdmf\":\n            from spectre.Visualization.GenerateXdmf import generate_xdmf_command\n\n            return generate_xdmf_command\n        elif name in [\"interpolate-to-points\", \"interpolate-to-coords\"]:\n            from spectre.IO.Exporter.InterpolateToPoints import (\n                interpolate_to_points_command,\n            )\n\n            return interpolate_to_points_command\n        elif name == \"interpolate-to-mesh\":\n            from spectre.IO.H5.InterpolateToMesh import (\n                interpolate_to_mesh_command,\n            )\n\n            return interpolate_to_mesh_command\n        elif name == \"plot\":\n            from spectre.Visualization import plot_command\n\n            return plot_command\n        elif name == \"render-1d\":\n            from spectre.Visualization.Render1D import render_1d_command\n\n            return render_1d_command\n        elif name == \"render-3d\":\n            from spectre.Visualization.Render3D import render_3d_command\n\n            return render_3d_command\n        elif name == \"resubmit\":\n            from spectre.support.Resubmit import resubmit_command\n\n            return resubmit_command\n        elif name == \"run-next\":\n            from spectre.support.RunNext import run_next_command\n\n            return run_next_command\n        elif name in [\"schedule\", \"run\"]:\n            from spectre.support.Schedule import schedule_command\n\n            return schedule_command\n        elif name == \"simplify-traces\":\n            from spectre.tools.CharmSimplifyTraces import (\n                simplify_traces_command,\n            )\n\n            return simplify_traces_command\n        elif name == \"status\":\n            from spectre.tools.Status import status_command\n\n            return status_command\n        elif name in [\"transform-volume-data\", \"transform-vol\"]:\n            from spectre.IO.H5.TransformVolumeData import (\n                transform_volume_data_command,\n            )\n\n            return transform_volume_data_command\n        elif name == \"validate\":\n            from spectre.tools.ValidateInputFile import (\n                validate_input_file_command,\n            )\n\n            return validate_input_file_command\n        raise RequiredChoiceError(\n            f\"The command '{name}' is not implemented.\",\n            choices=self.list_commands(ctx),\n        )\n\n\ndef print_machine(ctx, param, value):\n    if not value or ctx.resilient_parsing:\n        return\n    try:\n        machine = this_machine()\n        click.echo(machine.Name)\n        ctx.exit(1)\n    except UnknownMachineError as exc:\n        click.echo(exc)\n        ctx.exit()\n\n\ndef read_config_file(ctx, param, config_file):\n    if not config_file:\n        return\n    config_file = os.path.expanduser(config_file)\n    if not os.path.exists(config_file):\n        return\n    with open(config_file, \"r\") as open_config_file:\n        config = yaml.safe_load(open_config_file)\n    ctx.default_map = config\n\n\n# Set up CLI entry point\n@click.group(\n    context_settings=dict(help_option_names=[\"-h\", \"--help\"]),\n    help=f\"SpECTRE version: {spectre.__version__}\",\n    cls=Cli,\n)\n@click.version_option(version=spectre.__version__, message=\"%(version)s\")\n@click.option(\n    \"--machine\",\n    is_flag=True,\n    expose_value=False,\n    is_eager=True,\n    callback=print_machine,\n    help=\"Show the machine we're running on and exit.\",\n)\n@click.option(\n    \"--debug\",\n    \"log_level\",\n    flag_value=logging.DEBUG,\n    help=\"Enable debug logging.\",\n)\n@click.option(\n    \"--silent\",\n    \"log_level\",\n    flag_value=logging.CRITICAL,\n    help=\"Disable all logging.\",\n)\n@click.option(\n    \"--build-dir\",\n    \"-b\",\n    type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True),\n    help=(\n        \"Prepend a build directory to the PATH \"\n        \"so subprocesses can find executables in it. \"\n        \"Without this option, executables are found in the \"\n        \"current PATH, and fall back to the build directory \"\n        \"in which this Python script is installed.\"\n    ),\n)\n@click.option(\n    \"--profile\",\n    is_flag=True,\n    help=(\n        \"Enable profiling. \"\n        \"Expect slower execution due to profiling overhead. \"\n        \"A summary of the results is printed to the terminal. \"\n        \"Use the '--output-profile' option to write the results \"\n        \"to a file.\"\n    ),\n)\n@click.option(\n    \"--output-profile\",\n    type=click.Path(writable=True),\n    help=(\n        \"Write profiling results to a file. \"\n        \"The file can be opened by profiling visualization tools \"\n        \"such as 'pstats' or 'gprof2dot'. \"\n        \"See the Python 'cProfile' docs for details.\"\n    ),\n)\n@click.option(\n    \"-c\",\n    \"--config-file\",\n    type=click.Path(file_okay=True, dir_okay=False),\n    callback=read_config_file,\n    default=\"~/.config/spectre.yaml\",\n    show_default=True,\n    is_eager=True,\n    expose_value=False,\n    envvar=\"SPECTRE_CONFIG_FILE\",\n    help=(\n        \"Configuration file in YAML format. Can provide defaults \"\n        \"for command-line options and additional configuration. \"\n        \"To specify options for subcommands, list them in a \"\n        \"section with the same name as the subcommand. \"\n        \"All options that are listed in the help string for a \"\n        \"subcommand are supported. Unless otherwise specified in \"\n        \"the help string, use the name of the option with dashes \"\n        \"replaced by underscores. Example:\\n\\n\"\n        \"\\b\\n\"\n        \"status:\\n\"\n        \"  starttime: now-2days\\n\"\n        \"  state_styles:\\n\"\n        \"    RUNNING: blink\\n\"\n        \"plot:\\n\"\n        \"  dat:\\n\"\n        \"    stylesheet: path/to/stylesheet.mplstyle\\n\\n\"\n        \"The path of the config file can also be specified by \"\n        \"setting the 'SPECTRE_CONFIG_FILE' environment variable.\"\n    ),\n)\ndef cli(log_level, build_dir, profile, output_profile):\n    configure_logging(log_level=log_level or logging.INFO)\n    # Format tracebacks with rich\n    # - Suppress traceback entries from modules that we don't care about\n    rich.traceback.install(\n        show_locals=log_level is None or log_level <= logging.DEBUG,\n        extra_lines=(\n            3 if log_level is None or log_level <= logging.DEBUG else 0\n        ),\n        suppress=[click],\n    )\n    # Add the build directory to the PATH so subprocesses can find executables.\n    # - We respect the user's PATH and only add a build directory if it was\n    #   explicitly specified on the command line.\n    if build_dir:\n        os.environ[\"PATH\"] = (\n            os.path.join(build_dir, \"bin\") + \":\" + os.environ[\"PATH\"]\n        )\n    # - We fall back to executables in this file's build directory as a last\n    #   resort.\n    default_bin_dir = os.path.dirname(\n        os.path.dirname(os.path.dirname(__file__))\n    )\n    os.environ[\"PATH\"] = os.environ[\"PATH\"] + \":\" + default_bin_dir\n\n    # Configure profiling\n    if profile:\n        import atexit\n        import cProfile\n        import pstats\n        from io import StringIO\n\n        logger.info(\n            \"Profiling is enabled. Expect slower execution due to \"\n            \"profiling overhead.\"\n        )\n        profiler = cProfile.Profile()\n        profiler.enable()\n\n        # Output profiling result at exit\n        def complete_profiling():\n            profiler.disable()\n            if output_profile:\n                # Write to file\n                profiler.dump_stats(output_profile)\n            else:\n                # Print to terminal\n                import rich.console\n\n                console = rich.console.Console()\n                console.rule(\n                    \"[bold]Profiling Result\", style=\"black\", align=\"center\"\n                )\n                profile_report = StringIO()\n                stats = pstats.Stats(\n                    profiler, stream=profile_report\n                ).sort_stats(pstats.SortKey.TIME)\n                stats.print_stats(20)\n                console.print(profile_report.getvalue())\n\n        atexit.register(complete_profiling)\n\n\nif __name__ == \"__main__\":\n    cli(prog_name=\"spectre\")\n"
  },
  {
    "path": "support/Python/dev_requirements.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Optional Python packages used for development, but not for running the code.\n#\n# Make sure that you are working in a Python venv. You can set one up like this\n# in a directory of your choice:\n#\n#   $ python3 -m venv path/to/env\n#   $ . path/to/env/bin/activate\n#\n# Then, install the Python dependencies into the environment like this:\n#\n#   $ pip3 install -r support/Python/dev_requirements.txt\n\n# Building and postprocessing documentation\nbeautifulsoup4\n# coverxygen 1.8.2 has an enum-handling regression (upstream issue #37).\n# Remove this pin once a fix is released.\ncoverxygen != 1.8.2\nnbconvert\npybtex\nsphinx\nsphinx-click\nfuro\nmyst-parser\n# Code formatting. The required version is locked in the pyproject.toml config.\nblack == 23.3.0\nisort ~= 5.12\n"
  },
  {
    "path": "support/Python/requirements.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Python packages needed to run the Python bindings.\n#\n# Make sure that you are working in a Python venv. You can set one up like this\n# in a directory of your choice:\n#\n#   $ python3 -m venv path/to/env\n#   $ . path/to/env/bin/activate\n#\n# Then, install the Python dependencies into the environment like this:\n#\n#   $ pip3 install -r support/Python/requirements.txt\n\n# Click: to compose our Python CLI, load subcommands lazily, and for\n# autocompletion\nclick\nh5py >= 3.5.0\n# Humanize: For human-readable output like \"2 hours ago\"\nhumanize\n# Jinja2: To work with placeholders in input files and submit scripts\nJinja2\nnumpy\nmatplotlib\n# Pandas: To work with columns of data. Need a sufficiently recent version\n# to do some fancy indexing in Status CLI\npandas >= 1.5\npybind11 >= 2.7.0\npyyaml\n# Rich: to format CLI output and tracebacks\nrich >= 12.0.0\nscipy\n"
  },
  {
    "path": "support/Python/shell-completion.bash",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Generated by click:\n# https://click.palletsprojects.com/shell-completion/\n\n_spectre_completion() {\n    local IFS=$'\\n'\n    local response\n\n    response=$(env COMP_WORDS=\"${COMP_WORDS[*]}\" COMP_CWORD=$COMP_CWORD \\\n_SPECTRE_COMPLETE=bash_complete $1)\n\n    for completion in $response; do\n        IFS=',' read type value <<< \"$completion\"\n\n        if [[ $type == 'dir' ]]; then\n            COMPREPLY=()\n            compopt -o dirnames\n        elif [[ $type == 'file' ]]; then\n            COMPREPLY=()\n            compopt -o default\n        elif [[ $type == 'plain' ]]; then\n            COMPREPLY+=($value)\n        fi\n    done\n\n    return 0\n}\n\n_spectre_completion_setup() {\n    complete -o nosort -F _spectre_completion spectre\n}\n\n_spectre_completion_setup;\n\n"
  },
  {
    "path": "support/Python/shell-completion.fish",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Generated by click:\n# https://click.palletsprojects.com/shell-completion/\n\nfunction _spectre_completion;\n    set -l response;\n\n    for value in (env _SPECTRE_COMPLETE=fish_complete \\\nCOMP_WORDS=(commandline -cp) COMP_CWORD=(commandline -t) spectre);\n        set response $response $value;\n    end;\n\n    for completion in $response;\n        set -l metadata (string split \",\" $completion);\n\n        if test $metadata[1] = \"dir\";\n            __fish_complete_directories $metadata[2];\n        else if test $metadata[1] = \"file\";\n            __fish_complete_path $metadata[2];\n        else if test $metadata[1] = \"plain\";\n            echo $metadata[2];\n        end;\n    end;\nend;\n\ncomplete --no-files --command spectre --arguments \"(_spectre_completion)\";\n\n"
  },
  {
    "path": "support/Python/shell-completion.zsh",
    "content": "#compdef spectre\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Generated by click:\n# https://click.palletsprojects.com/shell-completion/\n\n_spectre_completion() {\n    local -a completions\n    local -a completions_with_descriptions\n    local -a response\n    (( ! $+commands[spectre] )) && return 1\n\n    response=(\"${(@f)$(env COMP_WORDS=\"${words[*]}\" \\\nCOMP_CWORD=$((CURRENT-1)) _SPECTRE_COMPLETE=zsh_complete spectre)}\")\n\n    for type key descr in ${response}; do\n        if [[ \"$type\" == \"plain\" ]]; then\n            if [[ \"$descr\" == \"_\" ]]; then\n                completions+=(\"$key\")\n            else\n                completions_with_descriptions+=(\"$key\":\"$descr\")\n            fi\n        elif [[ \"$type\" == \"dir\" ]]; then\n            _path_files -/\n        elif [[ \"$type\" == \"file\" ]]; then\n            _path_files -f\n        fi\n    done\n\n    if [ -n \"$completions_with_descriptions\" ]; then\n        _describe -V unsorted completions_with_descriptions -U\n    fi\n\n    if [ -n \"$completions\" ]; then\n        compadd -U -V unsorted -a completions\n    fi\n}\n\ncompdef _spectre_completion spectre;\n\n"
  },
  {
    "path": "support/SubmitScripts/Anvil.sh",
    "content": "#!/bin/bash -\n#SBATCH -o spectre.out\n#SBATCH -e spectre.out\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(1) }}\n#SBATCH --cpus-per-task=128\n#SBATCH --no-requeue\n#SBATCH -J SpectreJob\n#SBATCH --nodes 2\n#SBATCH -t 12:00:00\n#SBATCH -p standard\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# To run a job on Anvil:\n# - Set the -J, --nodes, and -t options above, which correspond to job name,\n#   number of nodes, and wall time limit in HH:MM:SS, respectively.\n# - Set the build directory, run directory, executable name,\n#   and input file below. The input file path is relative to ${RUN_DIR}.\n#\n# NOTE: The executable will not be copied from the build directory, so if you\n#       update your build directory this file will use the updated executable.\n#\n# Optionally, if you need more control over how SpECTRE is launched on\n# Anvil you can edit the launch command at the end of this file directly.\n#\n# To submit the script to the queue run:\n#   sbatch Anvil.sh\n\nexport SPECTRE_HOME=$PROJECT/$USER/spectre/spectre\nexport SPECTRE_BUILD_DIR=$SPECTRE_HOME/build\nexport SPECTRE_DEPS=$PROJECT/$USER/spectre/deps\nexport RUN_DIR=$PWD\nexport SPECTRE_EXECUTABLE=$RUN_DIR/EvolveGhBinaryBlackHole\nexport SPECTRE_INPUT_FILE=$RUN_DIR/BinaryBlackHole.yaml\n\nmodule use $SPECTRE_DEPS/modules\nsource $SPECTRE_HOME/support/Environments/anvil_gcc.sh\nspectre_load_modules\n\n############################################################################\n# Set desired permissions for files created with this script\numask 0022\n\nexport PATH=${SPECTRE_BUILD_DIR}/bin:$PATH\ncd ${RUN_DIR}\n\nCHARM_PPN=$(expr ${SLURM_CPUS_PER_TASK} - 1)\necho \"Slurm tasks: ${SLURM_NTASKS}\"\necho \"Slurm cpus per task: ${SLURM_CPUS_PER_TASK}\"\necho \"Charm ppn: ${CHARM_PPN}\"\n\nmodule list\n\nmpirun -n ${SLURM_NTASKS} \\\n  ${SPECTRE_EXECUTABLE} ++ppn ${CHARM_PPN} \\\n  +setcpuaffinity --input-file ${SPECTRE_INPUT_FILE} \\\n  >> ${RUN_DIR}/spectre.out 2>&1\n"
  },
  {
    "path": "support/SubmitScripts/CaltechHpc.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Supercomputer at Caltech.\n# More information:\n# https://www.hpc.caltech.edu/documentation\n\n# This submit script currently requests cascadelake nodes (with 56 cores per\n# node). We can adjust it to request other types of nodes later.\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(2) }}\n#SBATCH --cpus-per-task 28\n#SBATCH --constraint=cascadelake\n#SBATCH -p {{ queue | default(\"expansion\") }}\n#SBATCH -t {{ time_limit | default(\"1-00:00:00\") }}\n#SBATCH --exclusive\n{% if reservation is defined %}\n#SBATCH --reservation={{ reservation }}\n{% endif %}\n{% endblock %}\n\n{% block run_command %}\nmpirun -n ${SLURM_NTASKS} \\\n  ${SPECTRE_PROFILING_PREFIX} \\\n  ${SPECTRE_EXECUTABLE} --input-file ${SPECTRE_INPUT_FILE} \\\n  ++ppn ${CHARM_PPN} +setcpuaffinity +no_isomalloc_sync \\\n  ${SPECTRE_CHECKPOINT:+ +restart \"${SPECTRE_CHECKPOINT}\"}\n{% endblock %}\n"
  },
  {
    "path": "support/SubmitScripts/Expanse.sh",
    "content": "#!/bin/bash -\n#SBATCH -o spectre.stdout\n#SBATCH -e spectre.stderr\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(128) }}\n#SBATCH --mem=248G\n#SBATCH -J KerrSchild\n#SBATCH --nodes 2\n#SBATCH -A csd275\n#SBATCH -p compute\n#SBATCH -t 0:03:00\n#SBATCH -D .\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# To run a job on Expanse:\n# - Set the -J, --nodes, and -t options above, which correspond to job name,\n#   number of nodes, and wall time limit in HH:MM:SS, respectively.\n# - Set an `#SBATCH -A` argument for the computing account id.\n# - Set the build directory, run directory, executable name,\n#   and input file below.\n# - For large jobs (multiple nodes), use -p compute and --ntasks-per-node 128\n# - For small jobs (fewer than 128 procs), use -p shared set the number of\n#   processors as --ntasks-per-node\n#\n# NOTE: The executable will not be copied from the build directory, so if you\n#       update your build directory this file will use the updated executable.\n#\n# Optionally, if you need more control over how SpECTRE is launched on\n# Expanse you can edit the launch command at the end of this file directly.\n#\n# To submit the script to the queue run:\n#   sbatch Expanse.sh\n\n# Replace these paths with the path to your build directory, to the source root\n# directory, the spectre dependencies module directory, and to the directory\n# where you want the output to appear, i.e. the run directory.\n# E.g., if you cloned spectre in your home directory, set\n# SPECTRE_BUILD_DIR to ${HOME}/spectre/build. If you want to run in a\n# directory called \"Run\" in the current directory, set\n# SPECTRE_RUN_DIR to ${PWD}/Run\nexport SPECTRE_BUILD_DIR=${HOME}/spectre-build/\nexport SPECTRE_HOME=${HOME}/spectre/\nexport SPECTRE_MODULE_DIR=${HOME}/Codes/spectre_deps/modules/\nexport SPECTRE_RUN_DIR=${PWD}/Run\n\n# Choose the executable and input file to run\n# To use an input file in the current directory, set\n# SPECTRE_INPUT_FILE to ${PWD}/InputFileName.yaml\nexport SPECTRE_EXECUTABLE=${SPECTRE_BUILD_DIR}/bin/EvolveGhKerrSchild\nexport SPECTRE_INPUT_FILE=${PWD}/KerrSchild.yaml\n\n# These commands load the relevant modules and cd into the run directory,\n# creating it if it doesn't exist\nsource ${SPECTRE_HOME}/support/Environments/expanse_gcc.sh\nmodule use ${SPECTRE_MODULE_DIR}\nspectre_load_modules\nmodule list\n\nmkdir -p ${SPECTRE_RUN_DIR}\ncd ${SPECTRE_RUN_DIR}\n\n# Copy the input file into the run directory, to preserve it\ncp ${SPECTRE_INPUT_FILE} ${SPECTRE_RUN_DIR}/\n\n# Set desired permissions for files created with this script\numask 0022\n\n# Set the path to include the build directory's bin directory\nexport PATH=${SPECTRE_BUILD_DIR}/bin:$PATH\n\n# Generate the nodefile\necho \"Running on the following nodes:\"\necho ${SLURM_NODELIST}\ntouch nodelist.$SLURM_JOBID\nfor node in $(echo $SLURM_NODELIST | scontrol show hostnames); do\n  echo \"host ${node}\" >> nodelist.$SLURM_JOBID\ndone\n\n# tuned for 4 communication threads -- initial tests indicated\n# that was most efficient for a basic KerrSchild executable with\n# around 2k elements on 2 nodes.\n# to adjust the number of comm threads:\n#  - set the denominator in WORKER_THREADS_PER_NODE to number of comm threads\n#  - set the multiplier of SLURM_NNODES in WORKER_THREADS to number of\n#    comm threads\n#  - adjust the pemap to be a number of ranges equal to the number of comm\n#    threads, and appropriate intervals of 128/number_of_comm_threads - 1\n#  - adjust the commap to be a number of values equal to the number of comm\n#    threads, likely just n * 128/number_of_comm_theads, for\n#    n=0..number_of_comm_threads\nWORKER_THREADS_PER_NODE=$((SLURM_NTASKS_PER_NODE / 4 - 1))\nWORKER_THREADS=$((SLURM_NPROCS - 4 * SLURM_NNODES))\nSPECTRE_COMMAND=\"${SPECTRE_EXECUTABLE} \\\n++p ${WORKER_THREADS} ++ppn ${WORKER_THREADS_PER_NODE} \\\n+pemap 1-31,33-63,65-95,97-127 +commap 0,32,64,96 \\\n++nodelist nodelist.${SLURM_JOBID} +balancer RecBipartLB\"\n\n# When invoking through `charmrun`, charm will initiate remote sessions which\n# will wipe out environment settings unless it is forced to re-initialize the\n# spectre environment between the start of the remote session and starting the\n# spectre executable\necho \"#!/bin/sh\nsource ${SPECTRE_HOME}/support/Environments/expanse_gcc.sh\nmodule use ${SPECTRE_MODULE_DIR}\nspectre_load_modules\n\\$@\n\" > runscript\n\n\nchmod u+x ./runscript\n\ncharmrun ++runscript ./runscript ${SPECTRE_COMMAND} \\\n         --input-file ${SPECTRE_INPUT_FILE}\n"
  },
  {
    "path": "support/SubmitScripts/Frontera.sh",
    "content": "#!/bin/bash\n#SBATCH -J MyJobName       # Job name\n#SBATCH -o MyJobName.o%j   # Name of stdout output file\n#SBATCH -e MyJobName.e%j   # Name of stderr error file\n#SBATCH -p small           # Queue (partition) name - for 3+ nodes use 'normal'\n#SBATCH -N 2               # Total # of nodes\n#SBATCH -n 2               # Total # of tasks, must be number of nodes\n#SBATCH -t 00:30:00        # Run time (hh:mm:ss)\n# #SBATCH --mail-type=all  # Send email at begin and end of job\n# #SBATCH --mail-user=username@tacc.utexas.edu\n# #SBATCH -A account       # Project/Allocation name (req'd if you have more than 1)\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# To run a job on Frontera:\n# - Set the -J, -N, -n, and -t options above, which correspond to job name,\n#   number of nodes, number of tasks, and wall time limit in HH:MM:SS.\n# - Set the build directory, run directory, executable name,\n#   and input file below. The input file path is relative to ${RUN_DIR}.\n#\n# NOTE: The executable will not be copied from the build directory, so if you\n#       update your build directory this file will use the updated executable.\n#\n# Optionally, if you need more control over how SpECTRE is launched on\n# Frontera you can edit the launch command at the end of this file directly.\n#\n# To submit the script to the queue run:\n#   sbatch Frontera.sh\n\n# UPDATE THESE!!!\nexport SPECTRE_EXECUTABLE=./bin/ParallelInfo\nexport SPECTRE_INPUT_FILE=./Test.yaml\n\n# Print out diagnostic info\nmodule list 2>&1\npwd\ndate\n\necho \"\"\necho \"\"\necho \"\"\n\nexport IBRUN_TASKS_PER_NODE=1\n\nif [ -f ${SPECTRE_EXECUTABLE} ]; then\n    if [ -f ${SPECTRE_INPUT_FILE} ]; then\n        ibrun -n ${SLURM_JOB_NUM_NODES} \\\n            ${SPECTRE_EXECUTABLE} ++ppn 55 \\\n            --input-file ${SPECTRE_INPUT_FILE} 2>&1\n    else\n        echo \"Could not find input file ${SPECTRE_INPUT_FILE}\"\n        exit 1\n    fi\nelse\n    echo \"Could not find executable ${SPECTRE_EXECUTABLE}\"\n    exit 1\nfi\n"
  },
  {
    "path": "support/SubmitScripts/Mbot.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Mbot is a supercomputer at Cornell hosted by Nils Deppe.\n# More information:\n# https://github.com/sxs-collaboration/WelcomeToSXS/wiki/Mbot\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(6) }}\n#SBATCH --cpus-per-task 32\n#SBATCH -p {{ queue | default(\"nd357_0001\") }}\n#SBATCH -t {{ time_limit | default(\"1-00:00:00\") }}\n{% endblock %}\n"
  },
  {
    "path": "support/SubmitScripts/Ocean2.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Ocean2 is a supercomputer at Cal State Fullerton hosted by Geoffrey Lovelace.\n# More information:\n# Ask Geoffrey Lovelace for more information on Ocean2.\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(6) }}\n#SBATCH --cpus-per-task 32\n#SBATCH -p {{ queue | default(\"normal\") }}\n#SBATCH -t {{ time_limit | default(\"01-00:00:00\") }}\n{% endblock %}\n"
  },
  {
    "path": "support/SubmitScripts/Ocean2_orca1.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Orca-1 Partition on the Ocean2 Supercomputer at Cal State Fullerton hosted by\n# Geoffrey Lovelace\n# More information:\n# Ask Geoffrey Lovelace for more information on Ocean2.\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(1) }}\n#SBATCH --cpus-per-task 20\n#SBATCH -p {{ queue | default(\"orca-1\") }}\n#SBATCH -t {{ time_limit | default(\"01-00:00:00\") }}\n{% endblock %}\n"
  },
  {
    "path": "support/SubmitScripts/Oscar.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Supercomputer at Brown.\n# More information:\n# https://docs.ccv.brown.edu/oscar\n\n# This submit script currently requests cascadelake nodes (with 32 cores per\n# node). We can adjust it to request other types of nodes later.\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(2) }}\n#SBATCH --cpus-per-task 16\n#SBATCH -p {{ queue | default(\"batch\") }}\n#SBATCH -t {{ time_limit | default(\"1-00:00:00\") }}\n#SBATCH --constraint=cascade\n{% endblock %}\n"
  },
  {
    "path": "support/SubmitScripts/Perlmutter.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# DOE supercomputer at Lawrence Berkely National Laboratory\n# More information:\n# https://docs.nersc.gov/systems/perlmutter/\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --constraint cpu\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(8) }}\n#SBATCH --cpus-per-task 32\n#SBATCH -q {{ queue | default(\"regular\") }}\n#SBATCH -t {{ time_limit | default(\"1-00:00:00\") }}\n{% endblock %}\n\n{% block run_command %}\nsrun -n ${SLURM_NTASKS} \\\n  ${SPECTRE_PROFILING_PREFIX} \\\n  ${SPECTRE_EXECUTABLE} --input-file ${SPECTRE_INPUT_FILE} \\\n  ++ppn ${CHARM_PPN} +setcpuaffinity \\\n  ${SPECTRE_CHECKPOINT:+ +restart \"${SPECTRE_CHECKPOINT}\"}\n{% endblock %}\n"
  },
  {
    "path": "support/SubmitScripts/Sonic.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Sonic is an HPC at ICTS-TIFR hosted by its AstroRel group.\n# More information:\n# https://it.icts.res.in/docs/sonic-cluster/\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(3) }}\n#SBATCH --cpus-per-task 32\n#SBATCH -p {{ queue | default(\"long\") }}\n#SBATCH -t {{ time_limit | default(\"1-00:00:00\") }}\n{% endblock %}\n"
  },
  {
    "path": "support/SubmitScripts/SubmitTemplateBase.sh",
    "content": "#!/bin/bash -\n{% block head %}\n#SBATCH -o {{ out_file }}\n#SBATCH -e {{ out_file }}\n#SBATCH -J {{ job_name }}\n#SBATCH --no-requeue\n#SBATCH --comment \"SPECTRE_INPUT_FILE={{ input_file }}\"\n{% endblock %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This script is a template for submitting jobs to a cluster with Slurm.\n# See `support/Python/Schedule.py` for how this template is used and how\n# placeholders are resolved.\n\nexport RUN_DIR={{ run_dir }}\nexport SPECTRE_INPUT_FILE={{ input_file }}\nexport SPECTRE_EXECUTABLE={{ executable }}\nexport SPECTRE_CHECKPOINT={{ from_checkpoint | default(\"\") }}\nexport SPECTRE_CLI={{ spectre_cli }}\n\n{% block charm_ppn %}\nCHARM_PPN=$(expr ${SLURM_CPUS_PER_TASK} - 1)\n{% endblock %}\n\necho \"###################################\"\necho \"######       JOB INFO        ######\"\necho \"###################################\"\necho\necho \"Job ID: ${SLURM_JOB_ID}\"\necho \"Run Directory: ${RUN_DIR}\"\necho \"Submit Directory: ${SLURM_SUBMIT_DIR}\"\necho \"Queue: ${SLURM_JOB_PARTITION}\"\necho \"Nodelist: ${SLURM_JOB_NODELIST}\"\necho \"Tasks: ${SLURM_NTASKS}\"\necho \"CPUs per Task: ${SLURM_CPUS_PER_TASK}\"\necho \"Charm ppn: ${CHARM_PPN}\"\necho \"PATH: ${PATH}\"\necho \"Executable: ${SPECTRE_EXECUTABLE}\"\necho \"CLI: ${SPECTRE_CLI}\"\necho\n\n{% block list_modules %}\nmodule list\n{% endblock %}\n\n# Disable multithreading so our executables have control over the\n# parallelization\nexport OMP_NUM_THREADS=1\nexport OPENBLAS_NUM_THREADS=1\nexport MKL_NUM_THREADS=1\n\n############################################################################\n# Set desired permissions for files created with this script\numask 0022\n\necho\necho \"###################################\"\necho \"######   Executable Output   ######\"\necho \"###################################\"\necho\n\ncd ${RUN_DIR}\n\n{% if profile_with is defined and profile_with == \"hpctoolkit\" %}\n# Enable profiling with HPCToolkit\n# - Enable time tracing with '-t'\nexport SPECTRE_PROFILING_PREFIX=\"hpcrun -t -o hpctoolkit-measurements\"\n{% endif %}\n\n{% block run_command %}\nmpirun -n ${SLURM_NTASKS} \\\n  ${SPECTRE_PROFILING_PREFIX} \\\n  ${SPECTRE_EXECUTABLE} --input-file ${SPECTRE_INPUT_FILE} \\\n  ++ppn ${CHARM_PPN} +setcpuaffinity \\\n  ${SPECTRE_CHECKPOINT:+ +restart \"${SPECTRE_CHECKPOINT}\"}\n{% endblock %}\n\n{% if profile_with is defined and profile_with == \"hpctoolkit\" %}\n# Postprocess profiling data\nhpcstruct hpctoolkit-measurements\nhpcprof -o hpctoolkit-database hpctoolkit-measurements\n{% endif %}\n\nexit_code=$?\n\n{% if segments_dir is defined %}\n# Resubmit\nif [ $exit_code -eq 2 ]; then\n  sleep 10s\n  ${SPECTRE_CLI} resubmit . \\\n    --context-file-name {{ context_file_name }} \\\n    --submit\n  exit $?\nfi\n{% endif %}\n\n# Run next entrypoint listed in input file\nif [ $exit_code -eq 0 ]; then\n  sleep 10s\n  ${SPECTRE_CLI} run-next ${SPECTRE_INPUT_FILE} -i .\n  exit $?\nfi\n\nexit $exit_code\n"
  },
  {
    "path": "support/SubmitScripts/Urania.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Urania -- HPC cluster of ACR division of MPI for Grav Physics, housed at the\n# Max Planck Computing & Data Facility.\n# https://docs.mpcdf.mpg.de/doc/computing/clusters/systems/Gravitational_Physics_ACR.html\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(1) }}\n#SBATCH --ntasks-per-core=1\n#SBATCH --cpus-per-task=72\n#SBATCH -t {{ time_limit | default(\"1-00:00:00\") }}\n#SBATCH -p {{ queue | default(\"p.urania\") }}\n{% endblock %}\n\n{% block charm_ppn %}\n# Two thread for communication\nCHARM_PPN=$(expr ${SLURM_CPUS_PER_TASK} - 2)\n{% endblock %}\n\n{% block list_modules %}\n# Load compiler and MPI modules with explicit version specifications,\n# consistently with the versions used to build the executable.\nsource ${SPECTRE_HOME}/support/Environments/urania.sh\nspectre_load_modules\n{% endblock %}\n\n{% block run_command %}\nsrun -n ${SLURM_NTASKS} \\\n    ${SPECTRE_PROFILING_PREFIX} \\\n    ${SPECTRE_EXECUTABLE} --input-file ${SPECTRE_INPUT_FILE} \\\n    ++ppn ${CHARM_PPN} +pemap 0-34,36-70 +commap 35,71 \\\n    ${SPECTRE_CHECKPOINT:+ +restart \"${SPECTRE_CHECKPOINT}\"}\n{% endblock %}\n"
  },
  {
    "path": "support/SubmitScripts/Viper.sh",
    "content": "{% extends \"SubmitTemplateBase.sh\" %}\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Supercomputer at the Max Planck Computing Data Facility.\n# More information:\n# https://docs.mpcdf.mpg.de/doc/computing/viper-user-guide.html\n\n{% block head %}\n{{ super() -}}\n#SBATCH --nodes {{ num_nodes | default(1) }}\n#SBATCH --ntasks-per-node {{ num_slurm_tasks | default(1) }}\n#SBATCH --ntasks-per-core=1\n#SBATCH --cpus-per-task=128\n#SBATCH -t {{ time_limit | default(\"1-00:00:00\") }}\n#SBATCH -p {{ queue | default(\"p.general\") }}\n{% endblock %}\n\n{% block charm_ppn %}\n# Two thread for communication\nCHARM_PPN=$(expr ${SLURM_CPUS_PER_TASK} - 2)\n{% endblock %}\n\n{% block list_modules %}\nsource ${SPECTRE_HOME}/support/Environments/viper.sh\nspectre_load_modules\n\n{% endblock %}\n\n{% block run_command %}\nsrun -n ${SLURM_NTASKS} \\\n    ${SPECTRE_PROFILING_PREFIX} \\\n    ${SPECTRE_EXECUTABLE} --input-file ${SPECTRE_INPUT_FILE} \\\n    ++ppn ${CHARM_PPN} +pemap 0-62,64-126 +commap 63,127 \\\n    ${SPECTRE_CHECKPOINT:+ +restart \"${SPECTRE_CHECKPOINT}\"}\n{% endblock %}\n"
  },
  {
    "path": "support/TeXLive/texlive.profile",
    "content": "selected_scheme scheme-infraonly\n\nTEXDIR ./\n\nTEXMFCONFIG $TEXMFSYSCONFIG\nTEXMFHOME $TEXMFLOCAL\nTEXMFLOCAL ./texmf-local\nTEXMFSYSCONFIG ./texmf-config\nTEXMFSYSVAR ./texmf-var\nTEXMFVAR $TEXMFSYSVAR\n\ntlpdbopt_install_docfiles 0\ntlpdbopt_install_srcfiles 0\ntlpdbopt_autobackup 0\n"
  },
  {
    "path": "tests/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(support)\nadd_subdirectory(tools)\nadd_subdirectory(Unit)\n"
  },
  {
    "path": "tests/InputFiles/Burgers/Step.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveBurgers\nTesting:\n  Timeout: 5\n  Check: parse;execute\n  Priority: High\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nInitialData: &InitialData\n  Step:\n    LeftValue: 2.\n    RightValue: 1.\n    InitialPosition: -0.5\n\nEvolution:\n  InitialTime: 0.0\n  InitialSlabSize: 0.01\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n  VariableOrderAlgorithm:\n    GoalOrder: 3\n  LtsStepChoosers:\n    - Cfl:\n        SafetyFactor: 0.5\n    - LimitIncrease:\n        Factor: 2.0\n\nPhaseChangeAndTriggers:\n\nDomainCreator:\n  AlignedLattice:\n    # These bounds are chosen so the large DG-only element on the left\n    # takes bigger steps than the subcell-allowed elements in the\n    # middle, and the small DG-only element on the right takes smaller\n    # steps.  (The speeds are larger on the left, so the element size\n    # differences have to be exagerated more than expected.)\n    BlockBounds: [[-1.0, 0.3, 0.5, 0.7, 0.9, 1.0]]\n    InitialLevels: [0]\n    InitialGridPoints: [7]\n    RefinedLevels:\n    RefinedGridPoints:\n    BlocksToExclude:\n    BoundaryConditions:\n      - DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Hll:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: [Block(0), Block(4)]\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n      LtsStepsPerSlab: 4\n  SubcellSolver:\n    Reconstructor: MonotonisedCentral\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger: Always\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 0\n          StepChoosers:\n            - FixedLtsRatio:\n                StepChoosers:\n                  - Cfl:\n                      SafetyFactor: 0.5\n            - LimitIncrease:\n                Factor: 2.0\n\nEventsAndTriggersAtSteps:\n\nEventsAndDenseTriggers:\n  - Trigger:\n      Times:\n        Specified:\n          Values: [0.123456]\n    Events:\n      - Completion\n\nObservers:\n  VolumeFileName: \"BurgersStepVolume\"\n  ReductionFileName: \"BurgersStepReductions\"\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestBouncingBlackHole.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: AnalyticTestCharacteristicExtract\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 20\n  Priority: High\nExpectedOutput:\n  - CharacteristicExtractReduction.h5\nOutputFileChecks:\n  - Label: \"check_news\"\n    Subfile: \"/SpectreR0030.cce/News\"\n    FileGlob: \"CharacteristicExtractReduction.h5\"\n    ExpectedDataSubfile: \"/Cce/News_expected.dat\"\n    AbsoluteTolerance: 5e-5\n\n---\n\nEvolution:\n  InitialTimeStep: 0.01\n  InitialSlabSize: 0.8\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    CharacteristicEvolution:\n      Proc: Auto\n      Exclusive: false\n    AnalyticWorldtubeBoundary:\n      Proc: Auto\n      Exclusive: false\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractUnusedVolume\"\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: false\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.1\n      - LimitIncrease:\n          Factor: 2\n\n  LMax: 8\n  NumberOfRadialPoints: 8\n  ObservationLMax: 8\n\n  StartTime: 0.0\n  EndTime: 0.8\n  ExtractionRadius: 30.0\n\n  AnalyticSolution:\n    BouncingBlackHole:\n      Period: 40.0\n      ExtractionRadius: 30.0\n      Mass: 1.0\n      Amplitude: 2.0\n\n  Filtering:\n    RadialFilterHalfPower: 24\n    RadialFilterAlpha: 35.0\n    FilterLMax: 6\n\n  ScriInterpOrder: 3\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestGaugeWave.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: AnalyticTestCharacteristicExtract\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 20\n  Priority: High\nExpectedOutput:\n  - CharacteristicExtractReduction.h5\nOutputFileChecks:\n  - Label: \"check_news\"\n    Subfile: \"/SpectreR0040.cce/News\"\n    FileGlob: \"CharacteristicExtractReduction.h5\"\n    ExpectedDataSubfile: \"/Cce/News_expected.dat\"\n    AbsoluteTolerance: 5e-10\n\n---\n\nEvolution:\n  InitialTimeStep: 0.1\n  InitialSlabSize: 0.8\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    CharacteristicEvolution:\n      Proc: Auto\n      Exclusive: false\n    AnalyticWorldtubeBoundary:\n      Proc: Auto\n      Exclusive: false\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractUnusedVolume\"\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: false\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.4\n      - LimitIncrease:\n          Factor: 2\n\n  LMax: 8\n  NumberOfRadialPoints: 8\n  ObservationLMax: 8\n\n  StartTime: 0.0\n  EndTime: 4.0\n  ExtractionRadius: 40.0\n\n  AnalyticSolution:\n    GaugeWave:\n      ExtractionRadius: 40.0\n      Mass: 1.0\n      Frequency: 0.5\n      Amplitude: 0.1\n      PeakTime: 50.0\n      Duration: 10.0\n\n  Filtering:\n    RadialFilterHalfPower: 24\n    RadialFilterAlpha: 35.0\n    FilterLMax: 6\n\n  ScriInterpOrder: 3\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestLinearizedBondiSachs.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: AnalyticTestCharacteristicExtract\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 20\n  Priority: High\nExpectedOutput:\n  - CharacteristicExtractReduction.h5\nOutputFileChecks:\n  - Label: \"check_news\"\n    Subfile: \"/SpectreR0040.cce/News\"\n    FileGlob: \"CharacteristicExtractReduction.h5\"\n    ExpectedDataSubfile: \"/Cce/News_expected.dat\"\n    AbsoluteTolerance: 1e-2\n\n---\n\nEvolution:\n  InitialTimeStep: 0.1\n  InitialSlabSize: 0.8\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    CharacteristicEvolution:\n      Proc: Auto\n      Exclusive: false\n    AnalyticWorldtubeBoundary:\n      Proc: Auto\n      Exclusive: false\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractUnusedVolume\"\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: false\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.4\n      - LimitIncrease:\n          Factor: 2\n\n  LMax: 8\n  NumberOfRadialPoints: 8\n  ObservationLMax: 8\n\n  StartTime: 0.0\n  EndTime: 5.0\n  ExtractionRadius: 40.0\n\n  AnalyticSolution:\n    LinearizedBondiSachs:\n      ExtractionRadius: 40.0\n      InitialModes: [[0.20, 0.10], [0.08, 0.04]]\n      Frequency: 0.2\n\n  Filtering:\n    RadialFilterHalfPower: 24\n    RadialFilterAlpha: 35.0\n    FilterLMax: 6\n\n  ScriInterpOrder: 3\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestRobinsonTrautman.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: AnalyticTestCharacteristicExtract\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 20\n  Priority: High\nExpectedOutput:\n  - CharacteristicExtractReduction.h5\nOutputFileChecks:\n  - Label: \"check_news\"\n    Subfile: \"/Cce/News_Noninertial.dat\"\n    FileGlob: \"CharacteristicExtractReduction.h5\"\n    ExpectedDataSubfile: \"/Cce/News_Noninertial_expected.dat\"\n    AbsoluteTolerance: 5e-11\n\n---\n\nEvolution:\n  InitialTimeStep: 0.1\n  InitialSlabSize: 0.6\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    CharacteristicEvolution:\n      Proc: Auto\n      Exclusive: false\n    AnalyticWorldtubeBoundary:\n      Proc: Auto\n      Exclusive: false\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractUnusedVolume\"\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: false\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.1\n      - LimitIncrease:\n          Factor: 2\n\n  LMax: 10\n  NumberOfRadialPoints: 8\n  ObservationLMax: 8\n\n  StartTime: 0.0\n  EndTime: 0.5\n  ExtractionRadius: 40.0\n\n  AnalyticSolution:\n    RobinsonTrautman:\n      InitialModes:\n        # l = 0\n        - [0.0, 0.0]\n        # l = 1\n        - [0.0, 0.0]\n        - [0.0, 0.0]\n        - [0.0, 0.0]\n        # l = 2\n        - [0.01, 0.005]\n      ExtractionRadius: 40.0\n      LMax: 10\n      Tolerance: 1e-10\n      StartTime: 0.0\n\n  Filtering:\n    RadialFilterHalfPower: 24\n    RadialFilterAlpha: 35.0\n    FilterLMax: 8\n\n  ScriInterpOrder: 4\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestRotatingSchwarzschild.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: AnalyticTestCharacteristicExtract\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 20\n  Priority: High\nExpectedOutput:\n  - CharacteristicExtractReduction.h5\nOutputFileChecks:\n  - Label: \"check_news\"\n    Subfile: \"/SpectreR0020.cce/News\"\n    FileGlob: \"CharacteristicExtractReduction.h5\"\n    ExpectedDataSubfile: \"/Cce/News_expected.dat\"\n    AbsoluteTolerance: 1e-11\n\n---\n\nEvolution:\n  InitialTimeStep: 0.1\n  InitialSlabSize: 0.8\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    CharacteristicEvolution:\n      Proc: Auto\n      Exclusive: false\n    AnalyticWorldtubeBoundary:\n      Proc: Auto\n      Exclusive: false\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractUnusedVolume\"\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: false\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.2\n      - LimitIncrease:\n          Factor: 2\n\n  LMax: 8\n  NumberOfRadialPoints: 8\n  ObservationLMax: 8\n\n  StartTime: 0.0\n  EndTime: 2.4\n  ExtractionRadius: 20.0\n\n  AnalyticSolution:\n    RotatingSchwarzschild:\n      ExtractionRadius: 20.0\n      Mass: 1.0\n      Frequency: 0.0\n\n  Filtering:\n    RadialFilterHalfPower: 24\n    RadialFilterAlpha: 35.0\n    FilterLMax: 6\n\n  ScriInterpOrder: 3\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestTeukolskyWave.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: AnalyticTestCharacteristicExtract\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 20\n  Priority: High\nExpectedOutput:\n  - CharacteristicExtractReduction.h5\nOutputFileChecks:\n  - Label: \"check_news\"\n    Subfile: \"/SpectreR0040.cce/News\"\n    FileGlob: \"CharacteristicExtractReduction.h5\"\n    ExpectedDataSubfile: \"/Cce/News_expected.dat\"\n    AbsoluteTolerance: 5e-10\n\n---\n\nEvolution:\n  InitialTimeStep: 0.1\n  InitialSlabSize: 1.0\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    CharacteristicEvolution:\n      Proc: Auto\n      Exclusive: false\n    AnalyticWorldtubeBoundary:\n      Proc: Auto\n      Exclusive: false\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractUnusedVolume\"\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: false\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.5\n      - LimitIncrease:\n          Factor: 2\n\n  LMax: 8\n  NumberOfRadialPoints: 8\n  ObservationLMax: 8\n\n  StartTime: -6.0\n  EndTime: -1.0\n  ExtractionRadius: 40.0\n\n  AnalyticSolution:\n    TeukolskyWave:\n      ExtractionRadius: 40.0\n      Amplitude: 0.1\n      Duration: 2.0\n\n  Filtering:\n    RadialFilterHalfPower: 24\n    RadialFilterAlpha: 35.0\n    FilterLMax: 6\n\n  ScriInterpOrder: 3\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestVolumeNewmanPenroseSpinCoefficients.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: AnalyticTestCharacteristicExtract\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 20\n  Priority: High\nExpectedOutput:\n  - CharacteristicExtractVolume.h5\nOutputFileChecks:\n  - Label: \"check_alpha\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/Obs.*/NewmanPenroseAlpha\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\n  - Label: \"check_beta\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/Obs.*/NewmanPenroseBeta\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\n  # NewmanPenroseGamma is non-vanishing and is checked below by the script\n  # AnalyticTestVolumeNewmanPenroseSpinCoefficientsRhoGammaMu.py\n  - Label: \"check_epsilon\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/Obs.*/NewmanPenroseEpsilon\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\n  - Label: \"check_tau\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/Obs.*/NewmanPenroseTau\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\n  - Label: \"check_sigma\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/Obs.*/NewmanPenroseSigma\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\n  # NewmanPenroseRho is non-vanishing and is checked below by the script\n  # AnalyticTestVolumeNewmanPenroseSpinCoefficientsRhoGammaMu.py\n  - Label: \"check_pi\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/Obs.*/NewmanPenrosePi\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\n  - Label: \"check_nu\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/Obs.*/NewmanPenroseNu\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\n  # NewmanPenroseMu is non-vanishing and is checked below by the script\n  # AnalyticTestVolumeNewmanPenroseSpinCoefficientsRhoGammaMu.py\n  - Label: \"check_lambda\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/Obs.*/NewmanPenroseLambda\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\nExtraFileCheckExecs:\n  - Label: \"check_rho_gamma_and_mu\"\n    Executable: \"AnalyticTestVolumeNewmanPenroseSpinCoefficientsRhoGammaMu.py\"\n    Arguments: [\"--atol\", \"1.e-10\", \"--rtol\", \"1.e-10\"]\n\n---\n\nEvolution:\n  InitialTimeStep: 0.01\n  InitialSlabSize: 0.8\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    CharacteristicEvolution:\n      Proc: Auto\n      Exclusive: false\n    AnalyticWorldtubeBoundary:\n      Proc: Auto\n      Exclusive: false\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractVolume\"\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: false\n      - ObserveFields:\n          SubgroupName: \"CceVolumeData\"\n          VariablesToObserve:\n            - OneMinusY\n            - InertialRetardedTime\n            - NewmanPenroseAlpha\n            - NewmanPenroseBeta\n            - NewmanPenroseGamma\n            - NewmanPenroseEpsilon\n            # - NewmanPenroseKappa # In our choice of tetrad, $\\kappa=0$\n            - NewmanPenroseTau\n            - NewmanPenroseSigma\n            - NewmanPenroseRho\n            - NewmanPenrosePi\n            - NewmanPenroseNu\n            - NewmanPenroseMu\n            - NewmanPenroseLambda\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.1\n      - LimitIncrease:\n          Factor: 2\n\n  LMax: 8\n  NumberOfRadialPoints: 8\n  ObservationLMax: 8\n\n  StartTime: 0.0\n  EndTime: 0.8\n  ExtractionRadius: 30.0\n\n  AnalyticSolution:\n    BouncingBlackHole:\n      Period: 40.0\n      ExtractionRadius: 30.0\n      Mass: 1.0\n      Amplitude: 0.0\n\n  Filtering:\n    RadialFilterHalfPower: 24\n    RadialFilterAlpha: 35.0\n    FilterLMax: 6\n\n  ScriInterpOrder: 3\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestVolumeNewmanPenroseSpinCoefficientsRhoGammaMu.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport argparse\nimport logging\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom AnalyticTestVolumeUtils import CheckCceVolumeBase, load_cce_volume_field\n\n\nclass CheckRhoGammaMuTestCase(CheckCceVolumeBase):\n    \"\"\"Unit tests for the analytically expected values of the Newman-Penrose\n    spin coefficients rho, gamma, and mu, in the Schwarzschild background.\"\"\"\n\n    def setUp(self):\n        super().setUp()\n        # We can do extra setup here... but don't need to\n\n    def test_rho(self):\n        rho = load_cce_volume_field(\n            self.vol_f, self.vol_obsids[0], \"NewmanPenroseRho\"\n        )\n\n        one_over_r_vol = np.zeros_like(rho)\n        one_over_r_vol[:, 0] = np.sqrt(4.0 * np.pi) * self.one_over_r\n\n        try:\n            npt.assert_allclose(\n                rho,\n                -one_over_r_vol / np.sqrt(2.0),\n                atol=self.atol,\n                rtol=self.rtol,\n            )\n        except AssertionError as e:\n            np.set_printoptions(precision=16)\n            print(f\"DESIRED: { -one_over_r_vol / np.sqrt(2.0) }\")\n            print(f\"ACTUAL: {rho}\")\n            raise AssertionError(\n                \"Test data is not equal to the expected data\"\n            ) from e\n\n    def test_gamma(self):\n        gamma = load_cce_volume_field(\n            self.vol_f, self.vol_obsids[0], \"NewmanPenroseGamma\"\n        )\n\n        one_over_r_sq_vol = np.zeros_like(gamma)\n        one_over_r_sq_vol[:, 0] = np.sqrt(4.0 * np.pi) * self.one_over_r**2\n\n        try:\n            npt.assert_allclose(\n                gamma,\n                one_over_r_sq_vol / np.sqrt(2.0),\n                atol=self.atol,\n                rtol=self.rtol,\n            )\n        except AssertionError as e:\n            np.set_printoptions(precision=16)\n            print(f\"DESIRED: { one_over_r_sq_vol / np.sqrt(2.0) }\")\n            print(f\"ACTUAL: {gamma}\")\n            raise AssertionError(\n                \"Test data is not equal to the expected data\"\n            ) from e\n\n    def test_mu(self):\n        mu = load_cce_volume_field(\n            self.vol_f, self.vol_obsids[0], \"NewmanPenroseMu\"\n        )\n\n        one_over_r_vol = np.zeros_like(mu)\n        one_over_r_vol[:, 0] = np.sqrt(4.0 * np.pi) * self.one_over_r\n        one_over_r_sq_vol = np.zeros_like(mu)\n        one_over_r_sq_vol[:, 0] = np.sqrt(4.0 * np.pi) * self.one_over_r**2\n\n        try:\n            npt.assert_allclose(\n                mu,\n                (2.0 * one_over_r_sq_vol - one_over_r_vol) / np.sqrt(2.0),\n                atol=self.atol,\n                rtol=self.rtol,\n            )\n        except AssertionError as e:\n            np.set_printoptions(precision=16)\n            print(\n                \"DESIRED:\"\n                f\" {(2.0 * one_over_r_sq_vol - one_over_r_vol) / np.sqrt(2.0)}\"\n            )\n            print(f\"ACTUAL: {mu}\")\n            raise AssertionError(\n                \"Test data is not equal to the expected data\"\n            ) from e\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--input-filename\")\n    parser.add_argument(\"--run-directory\")\n    parser.add_argument(\"--cmake-source-directory\")\n    parser.add_argument(\"--cmake-bin-directory\")\n    parser.add_argument(\"--atol\", type=float, default=1.0e-8)\n    parser.add_argument(\"--rtol\", type=float, default=1.0e-5)\n    logging.basicConfig(level=logging.INFO)\n    duplicate_test_case, remaining_args = parser.parse_known_args(\n        namespace=CheckRhoGammaMuTestCase\n    )\n    del duplicate_test_case\n    # Use of full command-line arguments breaks the unit-test framework\n    # (which needs to take its own command-line arguments), so we only pass\n    # on the remaining args after we've retrieved used ones\n    unittest.main(argv=[parser.prog] + remaining_args, verbosity=2)\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestVolumeUtils.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\"\"\"Utilities shared between volume CCE tests.\n\"\"\"\n\nimport os\nimport re\nimport unittest\n\nimport h5py\nimport numpy as np\nimport yaml\n\n\ndef load_cce_volume_field(vol_f, obsid, name):\n    \"\"\"Load a field from a CCE volume file.\n\n    Parameters\n    ----------\n    vol_f : h5py.File\n        An open CCE volume file\n    obsid : str | int\n        The observation id from which to load the field.\n    name : str\n        The name of the field to load (e.g. \"J\", \"NewmanPenroseGamma\", \"Psi2\"\n\n    Returns\n    -------\n    field : np.ndarray\n        An array with dtype=np.complex128, and of shape\n        (int(number_of_radial_shells), int((l_max + 1) ** 2)). Mode indexing\n        always starts at (ℓ=0,m=0), with m indexing fastest, i.e. in the order\n        (0,0), (1,-1), (1,0), (1,1), (2,-2), ...\n    \"\"\"\n\n    obsid_path = f\"/CceVolumeData/VolumeData.vol/ObservationId{obsid}\"\n    (number_of_radial_shells, l_max, _) = vol_f[f\"{obsid_path}/total_extents\"][\n        ()\n    ]\n    field = vol_f[f\"{obsid_path}/{name}\"][()]\n    field = field.view(np.complex128).reshape(\n        [int(number_of_radial_shells), int((l_max + 1) ** 2)]\n    )\n    return field\n\n\nclass CheckCceVolumeBase(unittest.TestCase):\n    \"\"\"A base class for CCE volume unit tests in the Schwarzschild background.\n\n    This is used in both\n    AnalyticTestVolumeNewmanPenroseSpinCoefficientsRhoGammaMu.py and\n    AnalyticTestVolumeWeylPsi2.py. The common code is collected here to avoid\n    repeating ourselves.\n\n    Attributes\n    ----------\n    input_filename : str\n        The path of the yaml file for the test\n    vol_f_path : str\n        The path to the h5 volume file containing the data to be tested\n    vol_f : h5py.File\n        The opened volume file object\n    one_over_r : h5py.Dataset\n        An array (like an ndarray) containing the radial collocation points in\n        terms of 1/r\n    vol_obsids : list[str]\n        A list of the observation id strings found in the volume file\n    setUp_completed : bool\n        A sentinel tracking if the setUp() function completed successfully. This\n        attribute may not be present, so its presence should be detected via\n        hasattr()\n    \"\"\"\n\n    def setUp(self):\n        if hasattr(self, \"setUp_completed\") and self.setUp_completed:\n            return\n\n        # Parse the yaml file to know what we're looking for\n        with open(self.input_filename, \"r\") as open_input_file:\n            parsed_yaml = list(yaml.safe_load_all(open_input_file))\n\n        # parsed_yaml[0] is the testing part of the stream;\n        # parsed_yaml[1] is the input file for the Cce executable\n\n        extraction_R = parsed_yaml[1][\"Cce\"][\"ExtractionRadius\"]\n        self.vol_f_path = os.path.join(\n            self.run_directory,\n            parsed_yaml[1][\"Observers\"][\"VolumeFileName\"] + \".h5\",\n        )\n        self.vol_f = h5py.File(self.vol_f_path, \"r\")\n\n        obsid_pat = re.compile(r\"ObservationId(?P<obsid>.+)\")\n        one_minus_y_obsids = [\n            obsid_pat.match(k).group(\"obsid\")\n            for k in self.vol_f[\"/CceVolumeData/OneMinusY.vol\"].keys()\n        ]\n\n        one_minus_y = self.vol_f[\n            \"/CceVolumeData/OneMinusY.vol/ObservationId\"\n            + one_minus_y_obsids[0]\n            + \"/OneMinusY\"\n        ][()]\n        R_over_r = 0.5 * one_minus_y\n        self.one_over_r = R_over_r / extraction_R\n\n        self.vol_obsids = [\n            obsid_pat.match(k).group(\"obsid\")\n            for k in self.vol_f[\"/CceVolumeData/VolumeData.vol\"].keys()\n        ]\n\n        self.setUp_completed = True\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestVolumeWeylPsi2.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport argparse\nimport logging\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom AnalyticTestVolumeUtils import CheckCceVolumeBase, load_cce_volume_field\n\n\nclass CheckPsi2Case(CheckCceVolumeBase):\n    \"\"\"Unit tests for the analytically expected value of the Newman-Penrose\n    Weyl scalar Psi_2 in the Schwarzschild background.\"\"\"\n\n    def setUp(self):\n        super().setUp()\n        # We can do extra setup here... but don't need to\n\n    def test_psi2(self):\n        psi2 = load_cce_volume_field(self.vol_f, self.vol_obsids[0], \"Psi2\")\n\n        one_over_r_cubed_vol = np.zeros_like(psi2)\n        one_over_r_cubed_vol[:, 0] = np.sqrt(4.0 * np.pi) * self.one_over_r**3\n\n        try:\n            npt.assert_allclose(\n                psi2,\n                -one_over_r_cubed_vol,\n                atol=self.atol,\n                rtol=self.rtol,\n            )\n        except AssertionError as e:\n            np.set_printoptions(precision=16)\n            print(f\"DESIRED: { -one_over_r_cubed_vol }\")\n            print(f\"ACTUAL: {psi2}\")\n            raise AssertionError(\n                \"Test data is not equal to the expected data\"\n            ) from e\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--input-filename\")\n    parser.add_argument(\"--run-directory\")\n    parser.add_argument(\"--cmake-source-directory\")\n    parser.add_argument(\"--cmake-bin-directory\")\n    parser.add_argument(\"--atol\", type=float, default=1.0e-8)\n    parser.add_argument(\"--rtol\", type=float, default=1.0e-5)\n    logging.basicConfig(level=logging.INFO)\n    duplicate_test_case, remaining_args = parser.parse_known_args(\n        namespace=CheckPsi2Case\n    )\n    del duplicate_test_case\n    # Use of full command-line arguments breaks the unit-test framework\n    # (which needs to take its own command-line arguments), so we only pass\n    # on the remaining args after we've retrieved used ones\n    unittest.main(argv=[parser.prog] + remaining_args, verbosity=2)\n"
  },
  {
    "path": "tests/InputFiles/Cce/AnalyticTestVolumeWeylScalars.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: AnalyticTestCharacteristicExtract\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 20\n  Priority: High\nExpectedOutput:\n  - CharacteristicExtractVolume.h5\nOutputFileChecks:\n  - Label: \"check_psi0\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/ObservationId.*/Psi0\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\n  - Label: \"check_psi1\"\n    Subfile: \"/CceVolumeData/VolumeData.vol/ObservationId.*/Psi1\"\n    FileGlob: \"CharacteristicExtractVolume.h5\"\n    # By not specifying an expected value, we test against 0\n    AbsoluteTolerance: 1e-8\nExtraFileCheckExecs:\n  - Label: \"check_psi2\"\n    Executable: \"AnalyticTestVolumeWeylPsi2.py\"\n    Arguments: [\"--atol\", \"1.e-10\", \"--rtol\", \"1.e-10\"]\n\n---\n\nEvolution:\n  InitialTimeStep: 0.01\n  InitialSlabSize: 0.8\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    CharacteristicEvolution:\n      Proc: Auto\n      Exclusive: false\n    AnalyticWorldtubeBoundary:\n      Proc: Auto\n      Exclusive: false\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractVolume\"\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: false\n      - ObserveFields:\n          SubgroupName: \"CceVolumeData\"\n          VariablesToObserve:\n            - OneMinusY\n            - InertialRetardedTime\n            - Psi0\n            - Psi1\n            - Psi2\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.1\n      - LimitIncrease:\n          Factor: 2\n\n  LMax: 8\n  NumberOfRadialPoints: 8\n  ObservationLMax: 8\n\n  StartTime: 0.0\n  EndTime: 0.8\n  ExtractionRadius: 30.0\n\n  AnalyticSolution:\n    BouncingBlackHole:\n      Period: 40.0\n      ExtractionRadius: 30.0\n      Mass: 1.0\n      Amplitude: 0.0\n\n  Filtering:\n    RadialFilterHalfPower: 24\n    RadialFilterAlpha: 35.0\n    FilterLMax: 6\n\n  ScriInterpOrder: 3\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/CharacteristicExtract.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This block is used by testing and the SpECTRE command line interface.\nExecutable: CharacteristicExtract\nTesting:\n  Check: parse\n  Priority: High\n\n---\n# Start of the input file that controls the CCE evolution.\n\nEvolution:\n  # The initial step sizes isn't super important because we use error-based\n  # adaptive time stepping to adjust the step size.\n  InitialTimeStep: 0.25\n  # The initial Slab size controls how often the EventsAndTriggers are run. They\n  # are run once per Slab.\n  InitialSlabSize: 10.0\n\nResourceInfo:\n  # Can ignore this section since CCE performs best on a single core.\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nObservers:\n  # The reduction file is where to write the CCE output of quantities at future\n  # null infinity.\n  # Specifically, it will be in a `/SpectreRXXXX.cce` where the number is the\n  # ExtractionRadius specified below.\n  ReductionFileName: \"CharacteristicExtractReduction\"\n  # The volume file is where to write the quantities from the bulk of the\n  # spacetime. The fields to extract can be specified in the section\n  # EventsAndTriggersAtSlabs.Events.ObserveFields below.\n  VolumeFileName: \"CharacteristicExtractVolume\"\n\nEventsAndTriggersAtSlabs:\n  # Write the CCE time step every Slab. A Slab is a fixed length of simulation\n  # time and is not influenced by the dynamically adjusted step size.\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          # The output is written into the \"ReductionFileName\" HDF5 file under\n          # \"/SubfileName.dat\"\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: true\n      # # If you want to dump volume data into VolumeFileName (specified above),\n      # # you can uncomment the ObserveFields option here:\n      # - ObserveFields:\n      #     # Data will be written in the group named by SubgroupName inside the\n      #     # h5 file specified by VolumeFileName. If dumping any volume data,\n      #     # it is recommended to also output OneMinusY and\n      #     # InertialRetardedTime so as to conveniently provide the\n      #     # coordinates. See the documentation of\n      #     # src/Evolution/Systems/Cce/Events/ObserveFields.hpp for details of\n      #     # the Cce volume format.\n      #     SubgroupName: \"CceVolumeData\"\n      #     VariablesToObserve:\n      #       - OneMinusY\n      #       - InertialRetardedTime\n      #       - J\n      #       - Psi0\n      #       - Psi1\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3 # Going to higher order doesn't seem necessary for CCE\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.1 # Don't take steps bigger than 0.1M\n      - LimitIncrease:\n          Factor: 2\n      - ErrorControl(SwshVars):\n          AbsoluteTolerance: 1e-9\n          RelativeTolerance: 1e-7\n          # These factors control how much the time step is changed at once.\n          MaxFactor: 2\n          MinFactor: 0.25\n          # How close to the \"perfect\" time step we take. Since the \"perfect\"\n          # value assumes a linear system, we need some safety factor since our\n          # system is nonlinear, and also so that we reduce how often we retake\n          # time steps.\n          SafetyFactor: 0.9\n      - ErrorControl(CoordVars):\n          AbsoluteTolerance: 1e-9\n          RelativeTolerance: 1e-8\n          # These factors control how much the time step is changed at once.\n          MaxFactor: 2\n          MinFactor: 0.25\n          # How close to the \"perfect\" time step we take. Since the \"perfect\"\n          # value assumes a linear system, we need some safety factor since our\n          # system is nonlinear, and also so that we reduce how often we retake\n          # time steps.\n          SafetyFactor: 0.9\n\n  # The number of angular modes used by the CCE evolution. This must be larger\n  # than ObservationLMax. We always use all of the m modes for the LMax since\n  # using fewer m modes causes aliasing-driven instabilities.\n  LMax: 20\n  # Probably don't need more than 15 radial grid points, but could increase\n  # up to ~20\n  NumberOfRadialPoints: 15\n  # The maximum ell we use for writing waveform output. While CCE can dump\n  # more, you should be cautious with higher modes since mode mixing, truncation\n  # error, and systematic numerical effects can have significant contamination\n  # in these modes.\n  ObservationLMax: 8\n\n  InitializeJ:\n    # To see what other J-initialization procedures are available, comment\n    # out this group of options and do, e.g. \"Blah:\" The code will print\n    # an error message with the available options and a help string.\n    # More details can be found at spectre-code.org.\n    ConformalFactor:\n      AngularCoordTolerance: 1e-13\n      MaxIterations: 1000 # Do extra iterations in case we improve.\n      RequireConvergence: False # Often don't converge to 1e-13, but that's fine\n      OptimizeL0Mode: True\n      UseBetaIntegralEstimate: False\n      ConformalFactorIterationHeuristic: SpinWeight1CoordPerturbation\n      UseInputModes: False\n      InputModes: []\n\n  StartTime: Auto # Start at the first time in file\n  EndTime: Auto   # End at the last time in file\n  # If the CCE file name is of the form NameOfFileRXXXX.h5 then\n  # ExtractionRadius can be Auto. If the filename does not contain the radius,\n  # then you can specify it explicitly. For example:\n  #   BoundaryDataFilename: CceInputData.h5\n  #   ExtractionRadius: 257\n  BoundaryDataFilename: CceR0257.h5\n  ExtractionRadius: Auto\n  # How we interpolate the worldtube data in time for CCE.\n  H5Interpolator:\n    BarycentricRationalSpanInterpolator:\n      MinOrder: 10\n      MaxOrder: 10\n\n  # Loads this many time steps in from the HDF5 files at once. Fewer file system\n  # accesses improve performance, but requires more RAM.\n  H5LookaheadTimes: 10000\n\n  Filtering:\n    # Using half-power 64 means we effectively have a Heavidside filter, zeroing\n    # out the highest mode only.\n    RadialFilterHalfPower: 64\n    RadialFilterAlpha: 35.0\n    # The number of angular modes not touched by the angular filter. This should\n    # be about 2 smaller than LMax used for the CCE evolution.\n    FilterLMax: 18\n\n  ScriInterpOrder: 5\n  # How often per CCE time step to output. Given the tight default tolerances of\n  # 1e-6 with an Adams-Bashforth stepper, once per time step is fine for most\n  # systems.\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/Cce/KleinGordonCharacteristicExtract.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This block is used by testing and the SpECTRE command line interface.\nExecutable: KleinGordonCharacteristicExtract\nTesting:\n  Check: parse\n  Priority: High\n\n---\n# Start of the input file that controls the CCE evolution.\n\nEvolution:\n  # The initial step sizes isn't super important because we use error-based\n  # adaptive time stepping to adjust the step size.\n  InitialTimeStep: 0.25\n  # The initial Slab size controls how often the EventsAndTriggers are run. They\n  # are run once per Slab.\n  InitialSlabSize: 10.0\n\nResourceInfo:\n  # Can ignore this section since CCE performs best on a single core.\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nObservers:\n  VolumeFileName: \"CharacteristicExtractUnusedVolume\"\n  # The reduction file is where the CCE output will be written.\n  # Specifically, it will be in a `/SpectreRXXXX.cce` where the number is the\n  # ExtractionRadius specified below.\n  ReductionFileName: \"CharacteristicExtractReduction\"\n\nEventsAndTriggersAtSlabs:\n  # Write the CCE time step every Slab. A Slab is a fixed length of simulation\n  # time and is not influenced by the dynamically adjusted step size.\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Offset: 0\n          Interval: 1\n    Events:\n      - ObserveTimeStep:\n          # The output is written into the \"ReductionFileName\" HDF5 file under\n          # \"/SubfileName.dat\"\n          SubfileName: CceTimeStep\n          PrintTimeToTerminal: true\n\nEventsAndTriggersAtSteps:\n\nCce:\n  Evolution:\n    TimeStepper:\n      AdamsBashforth:\n        Order: 3 # Going to higher order doesn't seem necessary for CCE\n    VariableOrderAlgorithm:\n      GoalOrder: 3\n    MinimumTimeStep: 1e-7\n    LtsStepChoosers:\n      - Constant: 0.1 # Don't take steps bigger than 0.1M\n      - LimitIncrease:\n          Factor: 2\n      - ErrorControl(SwshVars):\n          AbsoluteTolerance: 1e-9\n          RelativeTolerance: 1e-7\n          # These factors control how much the time step is changed at once.\n          MaxFactor: 2\n          MinFactor: 0.25\n          # How close to the \"perfect\" time step we take. Since the \"perfect\"\n          # value assumes a linear system, we need some safety factor since our\n          # system is nonlinear, and also so that we reduce how often we retake\n          # time steps.\n          SafetyFactor: 0.9\n      - ErrorControl(CoordVars):\n          AbsoluteTolerance: 1e-9\n          RelativeTolerance: 1e-8\n          # These factors control how much the time step is changed at once.\n          MaxFactor: 2\n          MinFactor: 0.25\n          # How close to the \"perfect\" time step we take. Since the \"perfect\"\n          # value assumes a linear system, we need some safety factor since our\n          # system is nonlinear, and also so that we reduce how often we retake\n          # time steps.\n          SafetyFactor: 0.9\n\n  # The number of angular modes used by the CCE evolution. This must be larger\n  # than ObservationLMax. We always use all of the m modes for the LMax since\n  # using fewer m modes causes aliasing-driven instabilities.\n  LMax: 20\n  # Probably don't need more than 15 radial grid points, but could increase\n  # up to ~20\n  NumberOfRadialPoints: 15\n  # The maximum ell we use for writing waveform output. While CCE can dump\n  # more, you should be cautious with higher modes since mode mixing, truncation\n  # error, and systematic numerical effects can have significant contamination\n  # in these modes.\n  ObservationLMax: 8\n\n  InitializeJ:\n    # To see what other J-initialization procedures are available, comment\n    # out this group of options and do, e.g. \"Blah:\" The code will print\n    # an error message with the available options and a help string.\n    # More details can be found at spectre-code.org.\n    ConformalFactor:\n      AngularCoordTolerance: 1e-13\n      MaxIterations: 1000 # Do extra iterations in case we improve.\n      RequireConvergence: False # Often don't converge to 1e-13, but that's fine\n      OptimizeL0Mode: True\n      UseBetaIntegralEstimate: False\n      ConformalFactorIterationHeuristic: SpinWeight1CoordPerturbation\n      UseInputModes: False\n      InputModes: []\n\n  StartTime: Auto # Start at the first time in file\n  EndTime: Auto   # End at the last time in file\n  # If the CCE file name is of the form NameOfFileRXXXX.h5 then\n  # ExtractionRadius can be Auto. If the filename does not contain the radius,\n  # then you can specify it explicitly. For example:\n  #   BoundaryDataFilename: CceInputData.h5\n  #   ExtractionRadius: 257\n  BoundaryDataFilename: CceR0257.h5\n  KleinGordonBoundaryDataFilename: CceR0257.h5\n  ExtractionRadius: Auto\n  # How we interpolate the worldtube data in time for CCE.\n  H5Interpolator:\n    BarycentricRationalSpanInterpolator:\n      MinOrder: 10\n      MaxOrder: 10\n\n  # Loads this many time steps in from the HDF5 files at once. Fewer file system\n  # accesses improve performance, but requires more RAM.\n  H5LookaheadTimes: 10000\n\n  Filtering:\n    # Using half-power 64 means we effectively have a Heavidside filter, zeroing\n    # out the highest mode only.\n    RadialFilterHalfPower: 64\n    RadialFilterAlpha: 35.0\n    # The number of angular modes not touched by the angular filter. This should\n    # be about 2 smaller than LMax used for the CCE evolution.\n    FilterLMax: 18\n\n  ScriInterpOrder: 5\n  # How often per CCE time step to output. Given the tight default tolerances of\n  # 1e-6 with an Adams-Bashforth stepper, once per time step is fine for most\n  # systems.\n  ScriOutputDensity: 1\n"
  },
  {
    "path": "tests/InputFiles/CurvedScalarWave/PlaneWaveMinkowski1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveCurvedScalarWaveMinkowski1D\nTesting:\n  Check: parse;execute\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nInitialData:\n  PlaneWave:\n    WaveVector: [1.0]\n    Center: [0.0]\n    Profile:\n      Sinusoid:\n        Amplitude: 1.0\n        Wavenumber: 1.0\n        Phase: 0.0\n\nBackgroundSpacetime:\n  Minkowski:\n\nPhaseChangeAndTriggers:\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  InitialSlabSize: 0.01\n  VariableOrderAlgorithm:\n    GoalOrder: 3\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n  LtsStepChoosers:\n    - LimitIncrease:\n        Factor: 2\n    - PreventRapidIncrease\n    - Cfl:\n        SafetyFactor: 0.2\n\nDomainCreator:\n  Interval:\n    LowerBound: [0.0]\n    UpperBound: [6.283185307179586]\n    Distribution: [Linear]\n    InitialRefinement: [1]\n    InitialGridPoints: [5]\n    TimeDependence: None\n    BoundaryConditions: [Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nFiltering:\n  ExpFilter0:\n    Alpha: 12\n    HalfPower: 32\n    Enable: false\n    BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [100]\n    Events:\n      - Completion\n\nEventsAndTriggersAtSteps:\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"PlaneWaveMinkowski3DVolume\"\n  ReductionFileName: \"PlaneWaveMinkowski3DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/CurvedScalarWave/PlaneWaveMinkowski2D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveCurvedScalarWaveMinkowski2D\nTesting:\n  Check: parse;execute\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nInitialData:\n  PlaneWave:\n    WaveVector: [1.0, 1.0]\n    Center: [0.0, 0.0]\n    Profile:\n      Sinusoid:\n        Amplitude: 1.0\n        Wavenumber: 1.0\n        Phase: 0.0\n\nBackgroundSpacetime:\n  Minkowski:\n\nPhaseChangeAndTriggers:\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  InitialSlabSize: 0.01\n  VariableOrderAlgorithm:\n    GoalOrder: 3\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n  LtsStepChoosers:\n    - LimitIncrease:\n        Factor: 2\n    - PreventRapidIncrease\n    - Cfl:\n        SafetyFactor: 0.2\n\nDomainCreator:\n  Rectangle:\n    LowerBound: [0.0, 0.0]\n    UpperBound: [6.283185307179586, 6.283185307179586]\n    Distribution: [Linear, Linear]\n    InitialRefinement: [1, 1]\n    InitialGridPoints: [5, 5]\n    TimeDependence: None\n    BoundaryConditions: [Periodic, Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nFiltering:\n  ExpFilter0:\n    Alpha: 12\n    HalfPower: 32\n    Enable: false\n    BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [100]\n    Events:\n      - Completion\n\nEventsAndTriggersAtSteps:\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"PlaneWaveMinkowski2DVolume\"\n  ReductionFileName: \"PlaneWaveMinkowski2DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/CurvedScalarWave/PlaneWaveMinkowski3D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveCurvedScalarWaveMinkowski3D\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 15\n  Priority: High\nExpectedOutput:\n  - PlaneWaveMinkowski3DVolume0.h5\n  - PlaneWaveMinkowski3DReductions.h5\nOutputFileChecks:\n  - Label: constraint violations\n    Subfile: /Norms.dat\n    FileGlob: PlaneWaveMinkowski3DReductions.h5\n    SkipColumns: [0, 1, 2]\n    AbsoluteTolerance: 0.02\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    SphericalSurface:\n      Proc: Auto\n      Exclusive: false\n\nInitialData:\n  PlaneWave:\n    WaveVector: [1.0, 1.0, 1.0]\n    Center: [0.0, 0.0, 0.0]\n    Profile:\n      Sinusoid:\n        Amplitude: 1.0\n        Wavenumber: 1.0\n        Phase: 0.0\n\nBackgroundSpacetime:\n  Minkowski:\n\nPhaseChangeAndTriggers:\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  InitialSlabSize: 0.01\n  VariableOrderAlgorithm:\n    GoalOrder: 3\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n  LtsStepChoosers:\n    - LimitIncrease:\n        Factor: 2\n    - PreventRapidIncrease\n    - Cfl:\n        SafetyFactor: 0.2\n\nDomainCreator:\n  Brick:\n    LowerBound: [0.0, 0.0, 0.0]\n    UpperBound: [6.283185307179586, 6.283185307179586, 6.283185307179586]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [1, 1, 1]\n    InitialGridPoints: [6, 6, 6]\n    TimeDependence: None\n    BoundaryConditions: [Periodic, Periodic, Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nFiltering:\n  ExpFilter0:\n    Alpha: 12\n    HalfPower: 32\n    Enable: false\n    BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [30]\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [0, 15]\n    Events:\n      - ObserveFields:\n          SubfileName: VolumePsi0And25\n          VariablesToObserve: [\"Psi\"]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeVarsConstraintsEvery10Slabs\n          VariablesToObserve:\n            - Psi\n            - Pi\n            - Phi\n            - PointwiseL2Norm(OneIndexConstraint)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n            - Name: PointwiseL2Norm(OneIndexConstraint)\n              NormType: L2Norm\n              Components: Individual\n            - Name: PointwiseL2Norm(TwoIndexConstraint)\n              NormType: L2Norm\n              Components: Individual\n\nEventsAndTriggersAtSteps:\n\nEventsAndDenseTriggers:\n\nInterpolator:\n  Verbosity: Silent\n\nInterpolationTargets:\n  SphericalSurface:\n    LMax: 10\n    Center: [0., 0., 0.]\n    Radius: 1.\n    AngularOrdering: Strahlkorper\n\nObservers:\n  VolumeFileName: \"PlaneWaveMinkowski3DVolume\"\n  ReductionFileName: \"PlaneWaveMinkowski3DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/CurvedScalarWave/WorldtubeKerrSchild.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveWorldtubeCurvedScalarWaveKerrSchild3D\nTesting:\n  Check: parse\n  Priority: High\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nInitialData:\n  ZerothOrderPuncture:\n    ParticlePosition: [&XCoordWorldtube 10., 0., 0.]\n    ParticleVelocity: [0., 0.01, 0.]\n    ParticleCharge: &Charge 0.5\n\nBackgroundSpacetime:\n  KerrSchild:\n    Mass: 1.\n    Center: [0., 0., 0.]\n    Spin: [0., 0., 0.]\n    Velocity: [0., 0., 0.]\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nPhaseChangeAndTriggers:\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0001\n  MinimumTimeStep: 1e-7\n  TimeStepper: Rk5Tsitouras\n\nDomainCreator:\n  BinaryCompactObject:\n    InitialRefinement: 0\n    InitialGridPoints: 12\n    UseEquiangularMap: true\n    ObjectB:\n      InnerRadius: 1.9\n      OuterRadius: 2.2\n      XCoord: -1e-64\n      Interior:\n        ExciseWithBoundaryCondition: DemandOutgoingCharSpeeds\n      UseLogarithmicMap: false\n    ObjectA:\n      InnerRadius: 1.6\n      OuterRadius: 2.\n      XCoord: *XCoordWorldtube\n      Interior:\n        ExciseWithBoundaryCondition: Worldtube\n      UseLogarithmicMap: false\n    CenterOfMassOffset: [0., 0.]\n    CubeScale: 1.0\n    Envelope:\n      RadialDistribution: Linear\n      Radius: 50.\n    OuterShell:\n      Radius: 400.\n      RadialPartitioning: []\n      RadialDistribution: Linear\n      OpeningAngle: 90.0\n      BoundaryCondition: ConstraintPreservingSphericalRadiation\n    TimeDependentMaps:\n      InitialTime: 0.\n      ExpansionMap:\n        InitialValues: [1., 0., 0.]\n        AsymptoticVelocityOuterBoundary: 0.\n        DecayTimescaleOuterBoundary: 1.\n      RotationMap:\n        InitialAngularVelocity: [0., 0., 0.001]\n      TranslationMap:\n        InitialValues: [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]\n      SkewMap: None\n      ShapeMapA:\n        LMax: 8\n        CoefficientTruncationLimit: 0.\n        InitialValues: Spherical\n        SizeInitialValues: [0., 0., 0.]\n        TransitionEndsAtCube: false\n      ShapeMapB:\n        LMax: 8\n        CoefficientTruncationLimit: 0.\n        InitialValues: Spherical\n        SizeInitialValues: [0., 0., 0.]\n        TransitionEndsAtCube: false\n      GridCenters: None\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5000\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: Volume\n          VariablesToObserve:\n            - Psi\n            - OneIndexConstraint\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1000\n          Offset: 0\n    Events:\n      - Spheres\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThan\n        Value: 2000.\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [0]\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 0\n          StepChoosers:\n            - Cfl:\n                SafetyFactor: 0.35\n\nWorldtube:\n  ExcisionSphere: ExcisionSphereA\n  ExpansionOrder: 1\n  Charge: *Charge\n  SelfForceOptions: None\n  WorldtubeRadiusOptions:\n    Amplitude: 2.\n    TransitionRadius: 10.\n    TransitionWidth: 0.05\n    Exponent: 1.5\n  BlackHoleRadiusOptions:\n    Amplitude: 1.9\n    TransitionRadius: 10.\n    TransitionWidth: 0.05\n    Exponent: 1.\n  ObserveCoefficientsTrigger:\n    Slabs:\n      EvenlySpaced:\n        Interval: 200\n        Offset: 0\n  Verbosity: Quiet\n\nInterpolator:\n  Verbosity: Silent\n\nInterpolationTargets:\n  Spheres:\n    LMax: 10\n    Center: [0.,0.,0.]\n    Radius: [400.]\n    AngularOrdering: Strahlkorper\n\nFiltering:\n  ExpFilter0:\n    Alpha: 12\n    HalfPower: 32\n    Enable: true\n    BlocksToFilter: All\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"Volume\"\n  ReductionFileName: \"Reductions\"\n  SurfaceFileName: \"Surfaces\"\n"
  },
  {
    "path": "tests/InputFiles/Elasticity/BentBeam.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveElasticity2D\nTesting:\n  Timeout: 5\n  Check: parse;execute_check_output\nExpectedOutput:\n  - ElasticBentBeam2DReductions.h5\n  - ElasticBentBeam2DVolume0.h5\nOutputFileChecks:\n  - Label: Discretization error\n    Subfile: /ErrorNorms.dat\n    FileGlob: ElasticBentBeam2DReductions.h5\n    SkipColumns: [0, 1, 2]\n    AbsoluteTolerance: 1.e-8\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &solution\n  BentBeam:\n    Length: 2.\n    Height: 1.\n    BendingMoment: 1.\n    Material: &iron\n      BulkModulus: 79.36507936507935\n      ShearModulus: 38.75968992248062\n\nMaterial:\n  IsotropicHomogeneous: *iron\n\nInitialGuess: Zero\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Rectangle:\n    LowerBound: [-1., -0.5]\n    UpperBound: [1., 0.5]\n    Distribution: [Linear, Linear]\n    InitialRefinement: [1, 0]\n    InitialGridPoints: [3, 3]\n    TimeDependence: None\n    BoundaryConditions:\n      - AnalyticSolution:\n          Solution: *solution\n          Displacement: Dirichlet\n      - AnalyticSolution:\n          Solution: *solution\n          Displacement: Dirichlet\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: StrongInertial\n\nObservers:\n  VolumeFileName: \"ElasticBentBeam2DVolume\"\n  ReductionFileName: \"ElasticBentBeam2DReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 1\n      RelativeResidual: 1.e-8\n      AbsoluteResidual: 1.e-14\n    Verbosity: Verbose\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: False\n    Verbosity: Quiet\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Quiet\n    SubdomainSolver:\n      ExplicitInverse:\n        WriteMatrixToFile: None\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nEventsAndTriggersAtIterations:\n  - Trigger: HasConverged\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(Displacement)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - Displacement\n            - PotentialEnergyDensity\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nAmr:\n  Verbosity: Quiet\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n"
  },
  {
    "path": "tests/InputFiles/Elasticity/HalfSpaceMirror.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveElasticity3D\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 45\n  Priority: High\nExpectedOutput:\n  - ElasticHalfSpaceMirrorReductions.h5\n  - ElasticHalfSpaceMirrorVolume0.h5\nOutputFileChecks:\n  - Label: Discretization error\n    Subfile: ErrorNorms.dat\n    FileGlob: ElasticHalfSpaceMirrorReductions.h5\n    ExpectedData:\n      - [1, 171, 3.394818e-01, 5.9458e-04]\n    AbsoluteTolerance: 1e-6\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &solution\n  HalfSpaceMirror:\n    BeamWidth: 0.177\n    Material: &fused_silica\n      BulkModulus: 36.36363636363637\n      ShearModulus: 30.76923076923077\n    IntegrationIntervals: 350\n    AbsoluteTolerance: 1e-12\n    RelativeTolerance: 1e-10\n\nMaterial:\n  IsotropicHomogeneous: *fused_silica\n\nInitialGuess: Zero\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Cylinder:\n    InnerRadius: 0.08\n    OuterRadius: 0.6\n    LowerZBound: 0\n    UpperZBound: 0.3\n    InitialRefinement: [1, 0, 0]\n    InitialGridPoints: [2, 3, 3]\n    UseEquiangularMap: True\n    RadialPartitioning: []\n    PartitioningInZ: []\n    RadialDistribution: [Linear]\n    DistributionInZ: [Linear]\n    BoundaryConditions:\n      LowerZ:\n        AnalyticSolution:\n          Solution: *solution\n          Displacement: Dirichlet\n      UpperZ:\n        AnalyticSolution:\n          Solution: *solution\n          Displacement: Dirichlet\n      Mantle:\n        AnalyticSolution:\n          Solution: *solution\n          Displacement: Dirichlet\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: StrongInertial\n\nObservers:\n  VolumeFileName: \"ElasticHalfSpaceMirrorVolume\"\n  ReductionFileName: \"ElasticHalfSpaceMirrorReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 100\n      RelativeResidual: 0.\n      AbsoluteResidual: 1.e-6\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: True\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Silent\n    SubdomainSolver:\n      Gmres:\n        ConvergenceCriteria:\n          MaxIterations: 10\n          RelativeResidual: 1.e-3\n          AbsoluteResidual: 1.e-12\n        Verbosity: Silent\n        Restart: None\n        Preconditioner:\n          MinusLaplacian:\n            Solver:\n              ExplicitInverse:\n                WriteMatrixToFile: None\n            BoundaryConditions: Auto\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nEventsAndTriggersAtIterations:\n  - Trigger: HasConverged\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(Displacement)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveNorms:\n          SubfileName: VolumeIntegrals\n          TensorsToObserve:\n            - Name: PotentialEnergyDensity\n              NormType: VolumeIntegral\n              Components: Individual\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - Displacement\n            - Strain\n            - Stress\n            - PotentialEnergyDensity\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nAmr:\n  Verbosity: Quiet\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n"
  },
  {
    "path": "tests/InputFiles/Elasticity/SingleCoatingMirror.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveElasticity3D\nTesting:\n  Check: parse\nExpectedOutput:\n  - MirrorReductions.h5\n  - MirrorVolume0.h5\nOutputFileChecks:\n  - Label: Volume integrals\n    Subfile: VolumeIntegrals.dat\n    FileGlob: MirrorReductions.h5\n    SkipColumns: [0, 1, 2]\n    ExpectedData: [[1.47912989657707e-02]]\n    AbsoluteTolerance: 1.e-4\n  - Label: Coating integrals\n    Subfile: VolumeIntegralsLayer0.dat\n    FileGlob: MirrorReductions.h5\n    SkipColumns: [0, 1, 2]\n    ExpectedData: [[3.12266515383387e-04]]\n    AbsoluteTolerance: 1.e-4\n  - Label: Substrate integrals\n    Subfile: VolumeIntegralsLayer1.dat\n    FileGlob: MirrorReductions.h5\n    SkipColumns: [0, 1, 2]\n    ExpectedData: [[1.44790324503873e-02]]\n    AbsoluteTolerance: 1.e-4\n\n---\n\nBackground:\n  # Only used to compute errors, which quantify how far away the solution is\n  # from the half-space mirror solution. Has no effect on the numerical\n  # simulation.\n  &half_space_mirror\n  HalfSpaceMirror:\n    BeamWidth: &beam_width 0.17677669534\n    Material: &fused_silica\n      BulkModulus: 36.36363636363637\n      ShearModulus: 30.76923076923077\n    IntegrationIntervals: 350\n    AbsoluteTolerance: 1e-12\n    RelativeTolerance: 1e-10\n\nInitialGuess: *half_space_mirror  # Or: Zero\n\nRandomizeInitialGuess: None\n\nMaterial:\n  Layer0: &algaas\n    CubicCrystal:\n      C_11: 119.94\n      C_12: 55.38\n      C_44: 59.15\n    # Or: effective-isotropic approximation\n    # IsotropicHomogeneous:\n    #   BulkModulus: 92.59263333\n    #   ShearModulus: 37.8788\n  Layer1:\n    IsotropicHomogeneous: *fused_silica\n\nDomainCreator:\n  Cylinder:\n    InnerRadius: 0.2\n    OuterRadius: 12.5\n    LowerZBound: 0.\n    UpperZBound: 12.50683\n    # Order: [r, phi, z]\n    InitialRefinement:\n      Layer0: [0, 0, 0]\n      Layer1: [0, 0, 2]\n    InitialGridPoints:\n      Layer0: [5, 5, 3]\n      Layer1: [5, 5, 5]\n    UseEquiangularMap: True\n    RadialPartitioning: [0.7]\n    PartitioningInZ: [&coating_thickness 0.00683]\n    RadialDistribution: [Linear, Logarithmic]\n    DistributionInZ: [Linear, Logarithmic]\n    BoundaryConditions:\n      LowerZ:\n        LaserBeam:\n          BeamWidth: *beam_width\n      UpperZ: Fixed\n      Mantle: Free\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: StrongInertial\n\nObservers:\n  VolumeFileName: \"MirrorVolume\"\n  ReductionFileName: \"MirrorReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 1000\n      RelativeResidual: 0.\n      AbsoluteResidual: 1.e-4\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: True\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Silent\n    SubdomainSolver:\n      Gmres:\n        ConvergenceCriteria:\n          MaxIterations: 3\n          RelativeResidual: 1.e-4\n          AbsoluteResidual: 1.e-12\n        Verbosity: Silent\n        Restart: None\n        Preconditioner:\n          MinusLaplacian:\n            Solver:\n              ExplicitInverse:\n                WriteMatrixToFile: None\n            BoundaryConditions: Auto\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nEventsAndTriggersAtIterations:\n  - Trigger: HasConverged\n    Events:\n      - ObserveNorms: &error_norms\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(Displacement)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveNorms: &volume_integrals\n          SubfileName: VolumeIntegrals\n          TensorsToObserve:\n            - Name: PotentialEnergyDensity\n              NormType: VolumeIntegral\n              Components: Individual\n      - ObserveNormsPerLayer: *error_norms\n      - ObserveNormsPerLayer: *volume_integrals\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - Displacement\n            - Strain\n            - Stress\n            - PotentialEnergyDensity\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Float\n          FloatingPointTypes: [Float]\n          BlocksToObserve: All\n\nAmr:\n  Verbosity: Quiet\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n"
  },
  {
    "path": "tests/InputFiles/ExampleExecutables/HelloWorldNoCharm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: HelloWorldNoCharm\nTesting:\n  Check: parse;execute\n  Priority: High\n\n---\n"
  },
  {
    "path": "tests/InputFiles/ExampleExecutables/PiMonteCarlo.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Metadata for testing an exectuable\nExecutable: PiMonteCarlo\nTesting:\n  Check: parse;execute\n---\n# Options to control the executable's behavior\nDartsPerIteration: 10000   # How many darts each processor throws each iteration\nAccuracyGoal: 1.e-2        # Fractional accuracy goal for pi estimate\n\n# Options that control how elements are distributed to processors when\n# the executable is run\nResourceInfo:\n  # Avoid placing array elements or singleton components on\n  # global processor 0?\n  AvoidGlobalProc0: false\n  # Options to control how singletons are allocated; Auto means default\n  # behavior. This option could e.g. assign a singleton to a specific processor\n  Singletons: Auto\n"
  },
  {
    "path": "tests/InputFiles/ExampleExecutables/RandomAmr1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: RandomAmr1D\nTesting:\n  Check: parse;execute\n  Timeout: 10\n  ExpectedExitCode: 2\n  Priority: High\nExpectedOutput:\n  - Checkpoints/Checkpoint_0000\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nAmr:\n  Criteria:\n    - RandomH:\n        ProbabilityWeights:\n          Split: 2\n          Join: 1\n          DoNothing: 1\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: [0, 8]\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n\nDomainCreator:\n  Interval:\n    LowerBound: [-0.5]\n    UpperBound: [6.5]\n    Distribution: [Linear]\n    InitialRefinement: [2]\n    InitialGridPoints: [2]\n    TimeDependence: None\n    IsPeriodicIn: [False]\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Always\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(CheckDomain)\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: 0.001\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nSpatialDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n"
  },
  {
    "path": "tests/InputFiles/ExampleExecutables/RandomAmrKeepCoarseGrids1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: RandomAmrKeepCoarseGrids1D\nTesting:\n  Check: parse;execute\n  Timeout: 10\n  ExpectedExitCode: 2\n  Priority: High\nExpectedOutput:\n  - Checkpoints/Checkpoint_0000\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nAmr:\n  Criteria:\n    - RandomH:\n        ProbabilityWeights:\n          Split: 2\n          Join: 1\n          DoNothing: 1\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: [0, 8]\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: False\n  MaxCoarseLevels: Auto\n  Verbosity: Verbose\n\nDomainCreator:\n  Interval:\n    LowerBound: [-0.5]\n    UpperBound: [6.5]\n    Distribution: [Linear]\n    InitialRefinement: [2]\n    InitialGridPoints: [2]\n    TimeDependence: None\n    IsPeriodicIn: [False]\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Always\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(UpdateSections)\n      - VisitAndReturn(CheckDomain)\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: 0.0\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nSpatialDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n"
  },
  {
    "path": "tests/InputFiles/ExampleExecutables/SingletonHelloWorld.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SingletonHelloWorld\nTesting:\n  Check: parse;execute\n  Priority: High\n\n---\n\nName: Albert Einstein\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/InputFiles/ExportCoordinates/Input1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: ExportCoordinates1D\nTesting:\n  Check: parse;execute\n  Priority: High\nExpectedOutput:\n  - ExportCoordinates1DVolume0.h5\n  - ExportCoordinates1DReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nAmr:\n  Criteria:\n    - RandomH:\n        ProbabilityWeights:\n          Split: 2\n          Join: 1\n          DoNothing: 1\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: [0, 4]\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [1]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence:\n      UniformTranslation:\n        InitialTime: 0.0\n        Velocity: [0.5]\n\nSpatialDiscretization:\n  ActiveGrid: Dg\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: 1.0\n    Events:\n      - Completion\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.1\n  TimeStepper:\n    AdamsBashforth:\n      Order: 1\n\nObservers:\n  VolumeFileName: \"ExportCoordinates1DVolume\"\n  ReductionFileName: \"ExportCoordinates1DReductions\"\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Always\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(CheckDomain)\n"
  },
  {
    "path": "tests/InputFiles/ExportCoordinates/Input2D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: ExportCoordinates2D\nTesting:\n  Check: parse;execute\n  Priority: High\nExpectedOutput:\n  - ExportCoordinates2DVolume0.h5\n  - ExportCoordinates2DReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Rectangle:\n    LowerBound: [-0.5, -0.5]\n    UpperBound: [0.5, 0.5]\n    Distribution: [Linear, Linear]\n    IsPeriodicIn: [false, false]\n    InitialRefinement: [2, 2]\n    InitialGridPoints: [5, 5]\n    TimeDependence:\n      UniformTranslation:\n        InitialTime: 0.0\n        Velocity: [0.5, 0.0]\n\nSpatialDiscretization:\n  ActiveGrid: Dg\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: 1.0\n    Events:\n      - Completion\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.1\n  TimeStepper:\n    AdamsBashforth:\n      Order: 1\n\nObservers:\n  VolumeFileName: \"ExportCoordinates2DVolume\"\n  ReductionFileName: \"ExportCoordinates2DReductions\"\n\nPhaseChangeAndTriggers:\n"
  },
  {
    "path": "tests/InputFiles/ExportCoordinates/Input3D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: ExportCoordinates3D\nTesting:\n  Check: parse;execute\n  Priority: High\nExpectedOutput:\n  - ExportCoordinates3DVolume0.h5\n  - ExportCoordinates3DReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Brick:\n    LowerBound: [-0.5, -0.5, -0.5]\n    UpperBound: [0.5, 0.5, 0.5]\n    Distribution: [Linear, Linear, Linear]\n    IsPeriodicIn: [false, false, false]\n    InitialRefinement: [0, 0, 0]\n    InitialGridPoints: [3, 3, 3]\n    TimeDependence:\n      UniformTranslation:\n        InitialTime: 0.0\n        Velocity: [0.5, 0.0, 0.0]\n\nSpatialDiscretization:\n  ActiveGrid: Dg\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.1\n  TimeStepper:\n    AdamsBashforth:\n      Order: 1\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: 1.0\n    Events:\n      - Completion\n\nObservers:\n  VolumeFileName: \"ExportCoordinates3DVolume\"\n  ReductionFileName: \"ExportCoordinates3DReductions\"\n\nPhaseChangeAndTriggers:\n"
  },
  {
    "path": "tests/InputFiles/ExportCoordinates/InputTimeDependent3D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: ExportCoordinates3D\nTesting:\n  Check: parse;execute\n  Timeout: 10\n  Priority: High\nExpectedOutput:\n  - ExportCoordinates3DVolume0.h5\n  - ExportCoordinates3DReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  # Parameters are chosen for an equal-mass, non-spinning binary black hole\n  # using superposed-Kerr-Schild initial data created with the\n  # Spectral Einstein Code (SpEC). The time-dependent maps are given\n  # arbitrary time-dependence.\n  BinaryCompactObject:\n    ObjectA:\n      InnerRadius: 0.45825\n      OuterRadius: 6.0\n      XCoord: 7.683\n      ExciseInterior: true\n      UseLogarithmicMap: true\n    ObjectB:\n      InnerRadius: 0.45825\n      OuterRadius: 6.0\n      XCoord: -7.683\n      ExciseInterior: true\n      UseLogarithmicMap: true\n    CenterOfMassOffset: [0., 0.]\n    Envelope:\n      Radius: 100.0\n      RadialDistribution: Linear\n    OuterShell:\n      Radius: 300.0\n      RadialPartitioning: []\n      RadialDistribution: Linear\n      OpeningAngle: 90.0\n    UseEquiangularMap: True\n    CubeScale: 1.0\n    InitialRefinement:\n      ObjectAShell:   [1, 1, 1]\n      ObjectBShell:   [1, 1, 1]\n      ObjectACube:    [1, 1, 0]\n      ObjectBCube:    [1, 1, 0]\n      Envelope: [1, 1, 1]\n      OuterShell0:     [1, 1, 1]\n    InitialGridPoints: 3\n    TimeDependentMaps:\n      InitialTime: 0.0\n      ExpansionMap:\n        InitialValues: [1.0, -4.6148457646200002e-05, 0.0]\n        AsymptoticVelocityOuterBoundary: -1.0e-6\n        DecayTimescaleOuterBoundary: 50.0\n      RotationMap:\n        InitialAngularVelocity: [0.0, 0.0, 1.5264577062000000e-02]\n      TranslationMap:\n        InitialValues: [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]\n      SkewMap:\n        InitialValuesY: [-0.4, 0.0, 0.0]\n        InitialValuesZ: [-0.4, 0.0, 0.0]\n      ShapeMapA:\n        CoefficientTruncationLimit: 0.\n        LMax: 8\n        InitialValues: Spherical\n        SizeInitialValues: [0.0, 0.0, 0.0]\n        TransitionEndsAtCube: true\n      ShapeMapB:\n        CoefficientTruncationLimit: 0.\n        LMax: 8\n        InitialValues: Spherical\n        SizeInitialValues: [0.0, 0.0, 0.0]\n        TransitionEndsAtCube: true\n      GridCenters: None\n\nSpatialDiscretization:\n  ActiveGrid: Dg\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.5\n  TimeStepper:\n    AdamsBashforth:\n      Order: 1\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: 0.5\n    Events:\n      - Completion\n  - Trigger: Always\n    Events:\n      - MonitorMemory:\n          ComponentsToMonitor: All\n\nObservers:\n  VolumeFileName: \"ExportCoordinates3DVolume\"\n  ReductionFileName: \"ExportCoordinates3DReductions\"\n\n# Intentionally after the completion time to avoid writing checkpoints on CI\nPhaseChangeAndTriggers:\n  - Trigger:\n      SlabCompares:\n        Comparison: EqualTo\n        Value: 2\n    PhaseChanges:\n      - VisitAndReturn(WriteCheckpoint)\n"
  },
  {
    "path": "tests/InputFiles/ExportEquationOfStateForRotNS/ExportEosForRotNS.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: ExportEquationOfStateForRotNS\nTesting:\n  Check: parse;execute\n  Priority: High\nExpectedOutput:\n  - EOS.GN\n\n---\n\nEquationOfState:\n  # This equation of state is what is used in https://arxiv.org/abs/2301.13818\n  # for the quark-hadron hybrid star. It is a fit to the DBHF_2507 equation of\n  # state. It combines 3 enthalpy parametrized parts and stitches to a spectral\n  # parametrization at low densities.\n  Enthalpy(Enthalpy(Enthalpy(Spectral))):\n    ReferenceDensity: 0.001822038675716006\n    MinimumDensity: 0.001822038675716006\n    MaximumDensity: 0.0034\n    TrigScaling: 0.0\n    PolynomialCoefficients:\n      [1.2730598378768818, 1.2730598378768818, 0.6365299189384409,\n      0.21217663964614697, 0.05304415991153674, 0.01060883198230735,\n      0.0017681386637178915, 0.0002525912376739845, 3.157390470924806e-05,\n      3.5082116343608955e-06]\n    SinCoefficients: [0.0]\n    CosCoefficients: [0.0]\n    TransitionDeltaEpsilon: 0.0\n    Enthalpy:\n      ReferenceDensity: 0.0011334511674839399\n      MinimumDensity: 0.0011334511674839399\n      MaximumDensity: 0.001822038675716006\n      TrigScaling: 0.0\n      PolynomialCoefficients: [1.272585148960061, 0.001]\n      SinCoefficients: [0.0]\n      CosCoefficients: [0.0]\n      TransitionDeltaEpsilon: 0.0\n      Enthalpy:\n        ReferenceDensity: 0.00022669023349678794\n        MinimumDensity: 0.0004533804669935759\n        MaximumDensity: 0.0011334511674839399\n        TrigScaling: 1.26426988871305\n        PolynomialCoefficients:\n          [1.0,0.08063293075870805,4.406887319408924e-26,\n          8.177895241388924e-22,0.013558242085066733,0.004117320982626606,\n          9.757362504479485e-26,1.5646573325075753e-30,0.00016253964205058317]\n        SinCoefficients:\n          [0.0003763514388305583,0.017968749910748837,0.008140052979970034,\n          -0.003067418379116628,-0.0008236601907322793]\n        CosCoefficients:\n          [-0.01080996024705052,-0.003421193490191067,0.012325774692378716,\n          0.004367136076912163,-0.00020374276952538073]\n        TransitionDeltaEpsilon: 0.0\n        Spectral:\n          ReferenceDensity: 4.533804669935759e-05\n          ReferencePressure: 9.970647727158039e-08\n          Coefficients: [1.2, 0.0, 1.34440187653529, -0.46098357752567365]\n          UpperDensity: 0.0004533804669935759\nOutputFileName: ./EOS.GN\nNumberOfPoints: 40000\nLowerBoundRestMassDensityCgs: 1.0e3\nUpperBoundRestMassDensityCgs: 1.0e16\n"
  },
  {
    "path": "tests/InputFiles/ForceFree/FastWave.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveForceFree\nTesting:\n  Check: parse;execute\n  Priority: High\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nInitialData: &InitialData\n  FastWave:\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.005\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    ClassicalRungeKutta4\n\nEvolutionSystem:\n  ForceFree:\n    ConstraintDamping:\n      KappaPsi: 1.0\n      KappaPhi: 1.0\n    ForceFreeCurrent:\n      ParallelConductivity: 0.0\n\nDomainCreator:\n  Brick:\n    LowerBound: [-1.0, -1.0, -1.0]\n    UpperBound: [1.0, 1.0, 1.0]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [2, 0, 0]\n    InitialGridPoints: [6, 6, 6]\n    TimeDependence: None\n    BoundaryConditions:\n      - DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n      - Periodic\n      - Periodic\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Rusanov:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nFiltering:\n  ExpFilter0:\n    Alpha: 36\n    HalfPower: 50\n    Enable: false\n    BlocksToFilter: All\n\nObservers:\n  VolumeFileName: \"ForceFreeFastWaveVolume\"\n  ReductionFileName: \"ForceFreeFastWaveReductions\"\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [2]\n    Events:\n      - Completion\n\nEventsAndDenseTriggers:\n\nPhaseChangeAndTriggers:\n"
  },
  {
    "path": "tests/InputFiles/GeneralizedHarmonic/CylindricalBinaryBlackHole.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhBinaryBlackHole\nTesting:\n  Check: parse\n  Priority: High\n  Timeout: 10\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPointsAndGridSpacing\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0002\n  MinimumTimeStep: 1e-7\n  InitialSlabSize: 0.25\n  VariableOrderAlgorithm:\n    GoalOrder: 3\n  LtsStepChoosers:\n    - LimitIncrease:\n        Factor: 2\n    - PreventRapidIncrease\n    - ElementSizeCfl:\n        SafetyFactor: 0.5\n    - ErrorControl:\n        AbsoluteTolerance: 1e-8\n        RelativeTolerance: 1e-6\n        MaxFactor: 2\n        MinFactor: 0.25\n        SafetyFactor: 0.95\n  TimeStepper:\n    AdamsBashforth:\n      Order: 5\n\nDomainCreator:\n  CylindricalBinaryCompactObject:\n    CenterA: &CenterA [&XCoordA 7.683, 0.0, 0.0]\n    CenterB: &CenterB [&XCoordB -7.683, 0.0, 0.0]\n    RadiusA: &RadiusA 0.45825\n    RadiusB: &RadiusB 0.45825\n    IncludeInnerSphereA: true\n    IncludeInnerSphereB: true\n    IncludeOuterSphere: true\n    UseEquiangularMap: true\n    OuterRadius: 300.0\n    InitialRefinement: 2\n    InitialGridPoints: 7\n    BoundaryConditions:\n      InnerBoundary:\n        DemandOutgoingCharSpeeds:\n      OuterBoundary:\n        ConstraintPreservingBjorhus:\n          Type: ConstraintPreservingPhysical\n          IncomingWaveProfile: None\n    TimeDependentMaps:\n      InitialTime: 0.0\n      ExpansionMap:\n        InitialValues: [1.0, -4.6148457646200002e-05, 0.0]\n        AsymptoticVelocityOuterBoundary: -1.0e-6\n        DecayTimescaleOuterBoundary: 50.0\n      RotationMap:\n        InitialAngularVelocity: [0.0, 0.0, 1.5264577062000000e-02]\n      TranslationMap:\n        InitialValues: [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]\n      SkewMap: None\n      ShapeMapA:\n        CoefficientTruncationLimit: 0.\n        LMax: &LMax 10\n        InitialValues: Spherical\n        SizeInitialValues: [0.0, 0.0, 0.0]\n      ShapeMapB:\n        CoefficientTruncationLimit: 0.\n        LMax: *LMax\n        InitialValues: Spherical\n        SizeInitialValues: [0.0, 0.0, 0.0]\n      GridCenters: None\n\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n    - ObserveTimeStepVolume:\n        SubfileName: FailureTimeStep\n        FloatingPointType: Double\n        CoordinatesFloatingPointType: Float\n\n# Set gauge and constraint damping parameters.\n# The values here are chosen empirically based on values that proved\n# successful in SpEC evolutions of binary black holes.\n# Note: Gaussian width = W / sqrt(34.54), so exp(-W^2/w^2) = 1e-15 at x=W,\n# is used in the damped-harmonic gauge parameters.\n# In SpEC, GaugeItems.input set what spectre calls W and spec calls\n# SecondaryWeightRmax. See\n# EvolutionSystems/GeneralizedHarmonic/DampedHarmonicGaugeItems.cpp\n# line 463 in https://github.com/sxs-collaboration/spec for where the Gaussian\n# is actually computed in SpEC.\nEvolutionSystem:\n  GeneralizedHarmonic:\n    GaugeCondition:\n      DampedHarmonic:\n        SpatialDecayWidth: 17.0152695482514 # From SpEC run: 100.0 / sqrt(34.54)\n        Amplitudes: [1.0, 1.0, 1.0]         # From SpEC run: damped harmonic\n        Exponents: [2, 2, 2]                # From SpEC run\n    DampingFunctionGamma0:\n      TimeDependentTripleGaussian:\n        Constant: 0.001             # 0.001 / (m_A + m_B)\n        Gaussian1:\n          Amplitude: 8.0             # 4.0 / m_A\n          Width: 3.5                 # 7.0 * m_A\n          Center: [*XCoordA, 0.0, 0.0] # [x_A, 0, 0]\n        Gaussian2:\n          Amplitude: 8.0             # 4.0 / m_B\n          Width: 3.5                 # 7.0 * m_B\n          Center: [*XCoordB, 0.0, 0.0]  # [x_B, 0, 0]\n        Gaussian3:\n          Amplitude: 0.075            # 0.075 / (m_A + m_B)\n          Width: 38.415              # 2.5 * (x_B - x_A)\n          Center: [0.0, 0.0, 0.0]\n        MovementMethod: ExpansionFactor\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -0.999\n        Amplitude: 0.999\n        Width: 153.66                # 10.0 * (x_B - x_A)\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      TimeDependentTripleGaussian:\n        Constant: 0.001              # 0.001 / (m_A + m_B)\n        Gaussian1:\n          Amplitude: 8.0             # 4.0 / m_A\n          Width: 3.5                 # 7.0 * m_A\n          Center: [*XCoordA, 0.0, 0.0] # [x_A, 0, 0]\n        Gaussian2:\n          Amplitude: 8.0             # 4.0 / m_B\n          Width: 3.5                 # 7.0 * m_B\n          Center: [*XCoordB, 0.0, 0.0]  # [x_B, 0, 0]\n        Gaussian3:\n          Amplitude: 0.075            # 0.075 / (m_A + m_B)\n          Width: 38.415              # 2.5 * (x_B - x_A)\n          Center: [0.0, 0.0, 0.0]\n        MovementMethod: ExpansionFactor\n\nAmr:\n  Criteria:\n    - TruncationError:\n        VariablesToMonitor: [SpacetimeMetric]\n        AbsoluteTarget: 1.e-6\n        RelativeTarget: 1.e-6\n    - Persson:\n        VariablesToMonitor: [SpacetimeMetric]\n        NumHighestModes: 2\n        Exponent: 4\n        AbsoluteTolerance: 0\n        CoarseningFactor: 0.1\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          # Current implementation checks wallclock at these global syncs\n          Interval: 10\n          Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: 23.5\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 200\n          Offset: 40\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(CheckDomain)\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    AveragedUpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nFiltering:\n  ExpFilter0:\n    Alpha: 36.0\n    HalfPower: 24\n    Enable: False\n    BlocksToFilter: All\n\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n          - Name: Lapse\n            NormType: L2Norm\n            Components: Individual\n          - Name: PointwiseL2Norm(GaugeConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(ThreeIndexConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(FourIndexConstraint)\n            NormType: L2Norm\n            Components: Sum\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 20\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - Lapse\n            - Shift\n            - PointwiseL2Norm(GaugeConstraint)\n            - PointwiseL2Norm(ThreeIndexConstraint)\n            - PointwiseL2Norm(FourIndexConstraint)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - ObservationAhA\n      - ObservationAhB\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 100\n          Offset: 0\n    Events:\n      - MonitorMemory:\n          ComponentsToMonitor: All\n  - Trigger:\n      SeparationLessThan:\n        Value: 2.0\n    Events:\n      - ObservationAhC:\n          SubfileName: ForContinuation\n          VariablesToObserve:\n            - SpacetimeMetric\n            - Pi\n            - Phi\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          # This volume data is for ringdown, so double precision is needed.\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThan\n        Value: 0.02\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 100\n          Offset: 30\n    Events:\n      - RefineMesh\n\nEventsAndTriggersAtSteps:\n\nBbhCompletionCriteria:\n  MinCommonHorizonSuccessesBeforeChecks: 8\n  MaxCommonHorizonSuccesses: 100\n  GaugeConstraintLinfThreshold: 1.0\n  ThreeIndexConstraintLinfThreshold: 1.0\n  CommonHorizonLMaxThreshold: 8\n  ConstraintCheckVerbose: false\n\nObservers:\n  VolumeFileName: \"GhBinaryBlackHoleVolumeData\"\n  ReductionFileName: \"GhBinaryBlackHoleReductionData\"\n  SurfaceFileName: \"GhBinaryBlackHoleSurfacesData\"\n\nInterpolator:\n  Verbosity: Silent\n\nApparentHorizons:\n  LMax: 20\n  ObservationAhA: &AhA\n    Criteria:\n    InitialGuess:\n      LMax: *LMax\n      Radius: 2.2\n      Center: [*XCoordA, 0.0, 0.0]\n    FastFlow: &DefaultFastFlow\n      Flow: Fast\n      Alpha: 1.0\n      Beta: 0.5\n      AbsTol: 1e-12\n      TruncationTol: 1e-2\n      DivergenceTol: 1.2\n      DivergenceIter: 5\n      MaxIts: 100\n    Verbosity: Verbose\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: All\n  ObservationAhB: &AhB\n    Criteria:\n    InitialGuess:\n      LMax: *LMax\n      Radius: 2.2\n      Center: [*XCoordB, 0.0, 0.0]\n    FastFlow: *DefaultFastFlow\n    Verbosity: Verbose\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: All\n  ObservationAhC:\n    Criteria:\n    InitialGuess:\n      LMax: 20\n      Radius: 10.0\n      Center: [0.0, 0.0, 0.0]\n    FastFlow: *DefaultFastFlow\n    Verbosity: Verbose\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: All\n  ControlSystemAhA: *AhA\n  ControlSystemAhB: *AhB\n  ControlSystemCharSpeedAhA: *AhA\n  ControlSystemCharSpeedAhB: *AhB\n\nInterpolationTargets:\n  BondiSachsInterpolation:\n    LMax: 16\n    Radius: [100, 150, 200]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n  ObservationExcisionBoundaryA: &ExBdryA\n    LMax: *LMax\n    Radius: *RadiusA\n    Center: *CenterA\n    AngularOrdering: Strahlkorper\n  ObservationExcisionBoundaryB: &ExBdryB\n    LMax: *LMax\n    Radius: *RadiusB\n    Center: *CenterB\n    AngularOrdering: Strahlkorper\n  ControlSystemCharSpeedExcisionA: *ExBdryA\n  ControlSystemCharSpeedExcisionB: *ExBdryB\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nControlSystems:\n  WriteDataToDisk: true\n  MeasurementsPerUpdate: 4\n  DelayUpdate: true\n  Verbosity: Silent\n  Expansion:\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: false\n    Controller:\n      UpdateFraction: 0.03\n    TimescaleTuner:\n      InitialTimescales: [0.2]\n      MinTimescale: 1.0e-2\n      MaxTimescale: 10.0\n      IncreaseThreshold: 2.5e-4\n      DecreaseThreshold: 1.0e-3\n      IncreaseFactor: 1.01\n      DecreaseFactor: 0.98\n    ControlError:\n  Rotation:\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: false\n    Controller:\n      UpdateFraction: 0.03\n    TimescaleTuner:\n      InitialTimescales: [0.2, 0.2, 0.2]\n      MinTimescale: 1.0e-2\n      MaxTimescale: 10.0\n      IncreaseThreshold: 2.5e-4\n      DecreaseThreshold: 1.0e-3\n      IncreaseFactor: 1.01\n      DecreaseFactor: 0.98\n    ControlError:\n  Translation: None\n  Skew: None\n  ShapeA: &ShapeControl\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: false\n    Controller:\n      UpdateFraction: 0.03\n    TimescaleTuner:\n      InitialTimescales: 2.0\n      MinTimescale: 1.0e-2\n      MaxTimescale: 10.0\n      IncreaseThreshold: 2.5e-4\n      DecreaseThreshold: 1.0e-3\n      IncreaseFactor: 1.01\n      DecreaseFactor: 0.98\n    ControlError:\n  ShapeB: *ShapeControl\n  SizeA: &SizeControl\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: true\n    Controller:\n      UpdateFraction: 0.06\n    TimescaleTuner:\n      InitialTimescales: 0.2\n      MinTimescale: 1.0e-4\n      MaxTimescale: 20.0\n      IncreaseThreshold: 2.5e-4\n      IncreaseFactor: 1.01\n    ControlError:\n      MaxNumTimesForZeroCrossingPredictor: 4\n      SmoothAvgTimescaleFraction: 0.25\n      DeltaRDriftOutwardOptions: None\n      DeltaRDriftInwardOptions: None\n      InitialState: Initial\n      SmootherTuner:\n        InitialTimescales: [0.2]\n        MinTimescale: 1.0e-4\n        MaxTimescale: 20.0\n        IncreaseThreshold: 2.5e-4\n        DecreaseThreshold: 1.0e-3\n        IncreaseFactor: 1.01\n        DecreaseFactor: 0.9\n  SizeB: *SizeControl\n\n# initial_data.h5 should contain numerical initial data on a grid that covers\n# the domain given above\n# - One way to produce initial data is with the `SolveXcts` executable. See the\n#   example in `docs/Examples/BbhInitialData`.\n# - You can also produce initial data with SpEC, interpolate it (using SpEC) to\n#   the domain given above (using the `ExportCoordinates` executable to get the\n#   coordinates of all grid points in the domain), and then load it here by\n#   selecting either the GH variables (`SpacetimeMetric`, `Pi`, `Phi`) or the\n#   ADM variables (`Lapse`, `Shift`, `SpatialMetric`, `ExtrinsicCurvature`).\n#   In this case, set `ElementsAreIdentical: True` because the data is already given\n#   on this exact grid.\nInitialData:\n  NumericInitialData:\n    FileGlob: \"/path/to/initial_data.h5\"\n    Subgroup: \"VolumeData\"\n    ObservationValue: Last\n    ObservationValueEpsilon: Auto\n    ElementsAreIdentical: False\n    Variables:\n      Lapse: Lapse\n      # Load a shift that is not corotating. See `docs/Examples/BbhInitialData`\n      # for details.\n      Shift: ShiftExcess\n      SpatialMetric: SpatialMetric\n      ExtrinsicCurvature: ExtrinsicCurvature\n"
  },
  {
    "path": "tests/InputFiles/GeneralizedHarmonic/GaugeWave1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhNoBlackHole1D\nTesting:\n  Check: parse;execute\n  Timeout: 8\nExpectedOutput:\n  - GhGaugeWave1DVolume0.h5\n  - GhGaugeWave1DReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.002\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n       EvenlySpaced:\n         # Current implementation checks wallclock at these global syncs\n         Interval: 100\n         Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: None\n\nInitialData: &InitialData\n  GeneralizedHarmonic(GaugeWave):\n    Amplitude: 0.5\n    Wavelength: 6.283185307179586\n\nDomainCreator:\n  RotatedIntervals:\n    LowerBound: [0.0]\n    Midpoint: [3.0]\n    UpperBound: [6.283185307179586]\n    InitialRefinement: [1]\n    InitialGridPoints: [[7,7]]\n    TimeDependence:\n      UniformTranslation:\n         InitialTime: 0.0\n         Velocity: [0.5]\n    BoundaryConditions:\n      LowerBoundary:\n        DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n      UpperBoundary:\n        DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n\nEvolutionSystem:\n  GeneralizedHarmonic:\n    GaugeCondition:\n      AnalyticChristoffel:\n        AnalyticPrescription: *InitialData\n    DampingFunctionGamma0:\n      GaussianPlusConstant:\n        Constant: 1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0]\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0]\n    DampingFunctionGamma2:\n      GaussianPlusConstant:\n        Constant: 1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0]\n\nFiltering:\n  ExpFilter0:\n    Alpha: 36.0\n    HalfPower: 64\n    Enable: false\n    BlocksToFilter: All\n\nSpatialDiscretization:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: Gauss\n  BoundaryCorrection:\n    AveragedUpwindPenalty:\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(SpacetimeMetric)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            - Pi\n            - Phi\n            - PointwiseL2Norm(GaugeConstraint)\n            - PointwiseL2Norm(ThreeIndexConstraint)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [5]\n    Events:\n      - Completion\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"GhGaugeWave1DVolume\"\n  ReductionFileName: \"GhGaugeWave1DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/GeneralizedHarmonic/GaugeWave3D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhNoBlackHole3D\nTesting:\n  Check: parse;execute\n  Timeout: 8\n  Priority: High\nExpectedOutput:\n  - GhGaugeWave3DVolume0.h5\n  - GhGaugeWave3DReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0002\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n       EvenlySpaced:\n         # Current implementation checks wallclock at these global syncs\n         Interval: 100\n         Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: None\n\nInitialData: &InitialData\n  GeneralizedHarmonic(GaugeWave):\n    Amplitude: 0.1\n    Wavelength: 1.0\n\nDomainCreator:\n  Brick:\n    LowerBound: [0.0, 0.0, 0.0]\n    UpperBound: [1.0, 1.0, 1.0]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [1, 1, 1]\n    InitialGridPoints: [5, 5, 5]\n    TimeDependence: None\n    BoundaryConditions: [Periodic, Periodic, Periodic]\n\nEvolutionSystem:\n  GeneralizedHarmonic:\n    GaugeCondition:\n      AnalyticChristoffel:\n        AnalyticPrescription: *InitialData\n    DampingFunctionGamma0:\n      GaussianPlusConstant:\n        Constant: 1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      GaussianPlusConstant:\n        Constant: 1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0, 0.0, 0.0]\n\nFiltering:\n  ExpFilter0:\n    Alpha: 36.0\n    HalfPower: 64\n    Enable: false\n    BlocksToFilter: All\n\nSpatialDiscretization:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n  BoundaryCorrection:\n    AveragedUpwindPenalty:\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(SpacetimeMetric)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            - Pi\n            - Phi\n            - PointwiseL2Norm(GaugeConstraint)\n            - PointwiseL2Norm(ThreeIndexConstraint)\n            - PointwiseL2Norm(FourIndexConstraint)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [2]\n    Events:\n      - Completion\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"GhGaugeWave3DVolume\"\n  ReductionFileName: \"GhGaugeWave3DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/GeneralizedHarmonic/KerrSchild.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhSingleBlackHole\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 30\n  Priority: High\nExpectedOutput:\n  - GhKerrSchildVolume0.h5\n  - GhKerrSchildReductions.h5\n  - GhKerrSchildSurfaces.h5\nOutputFileChecks:\n  - Label: \"check_horizon_find\"\n    Subfile: \"/ApparentHorizon.dat\"\n    FileGlob: \"GhKerrSchildReductions.h5\"\n    AbsoluteTolerance: 1e2\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0002\n  MinimumTimeStep: 1e-7\n  # *Important* this slab size was chosen for testing purposes, a more realistic\n  # slab size for evolutions would be 0.1 to observe constraints. If you would\n  # like more frequent output, consider using dense output.\n  InitialSlabSize: 0.001\n  VariableOrderAlgorithm:\n    GoalOrder: 4\n  LtsStepChoosers:\n    - LimitIncrease:\n        Factor: 2\n    - ElementSizeCfl:\n        SafetyFactor: 0.5\n    - ErrorControl:\n        AbsoluteTolerance: 1e-10\n        RelativeTolerance: 1e-8\n        MaxFactor: 2\n        MinFactor: 0.25\n        SafetyFactor: 0.95\n  # Found that order 4 offers a significant speedup compared to order 5\n  TimeStepper:\n    AdamsBashforth:\n      Order: 4\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Quiet\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n       EvenlySpaced:\n         # Current implementation checks wallclock at these global syncs\n         Interval: 100\n         Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: None\n\nInitialData: &InitialData\n  GeneralizedHarmonic(KerrSchild):\n    Mass: 1.0\n    Spin: [0.0, 0.0, 0.0]\n    Center: &Center [0.0, 0.0, 0.0]\n    Velocity: [0.0, 0.0, 0.0]\n\nDomainCreator:\n  Sphere:\n    InnerRadius: &InnerRadius 1.9\n    OuterRadius: 2.3\n    Interior:\n      ExciseWithBoundaryCondition:\n        DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n    InitialRefinement: 0\n    InitialGridPoints: 5\n    UseEquiangularMap: true\n    EquatorialCompression: None\n    RadialPartitioning: []\n    RadialDistribution: [Logarithmic]\n    WhichWedges: All\n    TimeDependentMaps: None\n    OuterBoundaryCondition:\n      DirichletAnalytic:\n        AnalyticPrescription: *InitialData\n\nEvolutionSystem:\n  GeneralizedHarmonic:\n    GaugeCondition:\n      AnalyticChristoffel:\n        AnalyticPrescription: *InitialData\n    # The parameter choices here come from our experience with the Spectral\n    # Einstein Code (SpEC). They should be suitable for evolutions of a\n    # perturbation of a Kerr-Schild black hole.\n    DampingFunctionGamma0:\n      GaussianPlusConstant:\n        Constant: 0.001\n        Amplitude: 3.0\n        Width: 11.313708499\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -1.0\n        Amplitude: 0.0\n        Width: 11.313708499\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      GaussianPlusConstant:\n        Constant: 0.001\n        Amplitude: 1.0\n        Width: 11.313708499\n        Center: [0.0, 0.0, 0.0]\n\nFiltering:\n  ExpFilter0:\n    Alpha: 36.0\n    HalfPower: 64\n    Enable: true\n    BlocksToFilter: All\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    AveragedUpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    Events:\n      - ObserveTimeStep:\n          SubfileName: TimeSteps\n          PrintTimeToTerminal: True\n          ObservePerCore: False\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(SpacetimeMetric)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n          - Name: Lapse\n            NormType: L2Norm\n            Components: Individual\n          - Name: PointwiseL2Norm(GaugeConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(ThreeIndexConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(FourIndexConstraint)\n            NormType: L2Norm\n            Components: Sum\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            - Pi\n            - Phi\n            - GaugeH\n            - PointwiseL2Norm(GaugeConstraint)\n            - PointwiseL2Norm(ThreeIndexConstraint)\n            - PointwiseL2Norm(FourIndexConstraint)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    Events:\n      - ApparentHorizon\n      - ExcisionBoundary\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [5]\n    Events:\n      - Completion\n\nEventsAndTriggersAtSteps:\n\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n    - ObserveTimeStepVolume:\n        SubfileName: FailureTimeStep\n        FloatingPointType: Double\n        CoordinatesFloatingPointType: Float\n\nObservers:\n  VolumeFileName: \"GhKerrSchildVolume\"\n  ReductionFileName: \"GhKerrSchildReductions\"\n  SurfaceFileName: \"GhKerrSchildSurfaces\"\n\nInterpolator:\n  Verbosity: Silent\n\nApparentHorizons:\n  LMax: 10\n  ApparentHorizon: &Ah\n    Criteria:\n      - Residual:\n          MinResidual: 1.e-2\n          MaxResidual: 1.e-1\n          MinResolutionL: 4\n    InitialGuess:\n      LMax: &LMax 8\n      Radius: 2.2\n      Center: *Center\n    FastFlow:\n      Flow: Fast\n      Alpha: 1.0\n      Beta: 0.5\n      AbsTol: 1e-12\n      TruncationTol: 1e-2\n      DivergenceTol: 1.2\n      DivergenceIter: 5\n      MaxIts: 100\n    Verbosity: Verbose\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: All\n  ControlSystemSingleAh: *Ah\n  ControlSystemCharSpeedAh: *Ah\n\nInterpolationTargets:\n  BondiSachsInterpolation:\n    LMax: 16\n    Radius: [100, 150, 200]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n  ExcisionBoundary: &ExBdry\n    LMax: *LMax\n    Center: *Center\n    Radius: *InnerRadius\n    AngularOrdering: \"Strahlkorper\"\n  ControlSystemCharSpeedExcision: *ExBdry\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nControlSystems:\n  WriteDataToDisk: true\n  MeasurementsPerUpdate: 4\n  DelayUpdate: true\n  Verbosity: Silent\n  Translation: None\n  Shape: None\n  Size: None\n"
  },
  {
    "path": "tests/InputFiles/GrMhd/GhValenciaDivClean/BinaryNeutronStar.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhValenciaDivCleanBns\nTesting:\n  Check: parse\n  Timeout: 8\n  Priority: High\n  CopyFiles: EvolutionParameters.perl\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nEvolution:\n  InitialTime: 0.0\n  # The initial time step gets overridden by `ChangeSlabSize` below\n  InitialTimeStep: 0.075\n  MinimumTimeStep: 1e-7\n  # Control systems only work with AdamsBashforth\n  TimeStepper:\n    AdamsMoultonPcMonotonic:\n      Order: 3\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          # Trigger checkpoint often enough so it triggers within\n          # the last 30 minutes of the run\n          Interval: 20\n          Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: 119.0\n  - Trigger:\n      SeparationLessThan:\n        Value: &RotationOffSeparation 5\n    PhaseChanges:\n      - VisitAndReturn(DisableRotationControl):\n\n# Values taken from the Xcts/HeadOnBns.yaml input file\nEquationOfState: &eos\n  Equilibrium3D(HybridEos(PolytropicFluid)):\n    HybridEos:\n      PolytropicFluid:\n        PolytropicConstant: 123.6\n        PolytropicExponent: 2.\n      ThermalAdiabaticIndex: 2.0\n\nDomainCreator:\n  BinaryCompactObject:\n    ObjectA:\n      CartesianCubeAtXCoord: &XCoordA 14\n    ObjectB:\n      CartesianCubeAtXCoord: &XCoordB -14\n    CenterOfMassOffset: [0., 0.]\n    Envelope:\n      Radius: 50.0\n      # Radial distribution is logarithmic to transition from grid point\n      # spacing around the excisions to the grid point spacing in the\n      # outer shell\n      RadialDistribution: Logarithmic\n    OuterShell:\n      Radius: 605.0\n      # Radial distribution is linear to resolve gravitational waves\n      RadialDistribution: [Linear, Linear, Linear]\n      RadialPartitioning: [80.0, 125.0]\n      OpeningAngle: 120.0\n      BoundaryCondition:\n        ConstraintPreservingFreeOutflow:\n          Type: ConstraintPreservingPhysical\n          IncomingWaveProfile: None\n    UseEquiangularMap: True\n    CubeScale: 1.0\n    InitialRefinement:\n      ObjectA: [4, 4, 4]\n      ObjectB: [4, 4, 4]\n      OuterShell0: [2, 2, 0]\n      OuterShell1: [1, 1, 0]\n      OuterShell2: [0, 0, 3]\n      EnvelopeUpperZLeft:  [3, 3, 1]\n      EnvelopeUpperZRight: [3, 3, 1]\n      EnvelopeLowerZLeft:  [3, 3, 1]\n      EnvelopeLowerZRight: [3, 3, 1]\n      EnvelopeUpperYLeft:  [3, 3, 1]\n      EnvelopeUpperYRight: [3, 3, 1]\n      EnvelopeLowerYLeft:  [3, 3, 1]\n      EnvelopeLowerYRight: [3, 3, 1]\n      EnvelopeUpperX:      [3, 3, 0]\n      EnvelopeLowerX:      [3, 3, 0]\n\n    InitialGridPoints:\n      ObjectA: [5, 5, 5]\n      ObjectB: [5, 5, 5]\n      Envelope: [8, 8, 10]\n      OuterShell0: [9, 9, 12]\n      OuterShell1: [11, 11, 14]\n      OuterShell2UpperZLeft:  [12, 12, 14]\n      OuterShell2UpperZRight: [12, 12, 14]\n      OuterShell2LowerZLeft:  [12, 12, 14]\n      OuterShell2LowerZRight: [12, 12, 14]\n      OuterShell2UpperYLeft:  [12, 12, 14]\n      OuterShell2UpperYRight: [12, 12, 14]\n      OuterShell2LowerYLeft:  [12, 12, 14]\n      OuterShell2LowerYRight: [12, 12, 14]\n      OuterShell2UpperX: [13, 13, 14]\n      OuterShell2LowerX: [13, 13, 14]\n\n    TimeDependentMaps:\n      InitialTime: 0.0\n      SkewMap: None\n      ExpansionMap:\n        InitialValues: [1.0, 0.0, 0.0]\n        AsymptoticVelocityOuterBoundary: -1.0e-6\n        DecayTimescaleOuterBoundary: 50.0\n      RotationMap:\n        InitialAngularVelocity: [0.0, 0.0, 0.00801722]\n      TranslationMap:\n        InitialValues: [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]\n      ShapeMapA: None\n      ShapeMapB: None\n      GridCenters:\n        ScaleInspiralRateBy: 0.8\n        SpecEvolutionParametersPerlFile: ./EvolutionParameters.perl\n\nVariableFixing:\n  FixConservatives:\n    Enable: false # Only needed when not using Kastaun for recovery\n    CutoffD: &CutoffD 1.1e-15\n    MinimumValueOfD: &MinimumD 1.0e-15\n    CutoffYe: 0.0\n    MinimumValueOfYe: 0.0\n    SafetyFactorForB: &BSafetyFactor 1.0e-12\n    SafetyFactorForS: 1.0e-4\n    SafetyFactorForSCutoffD: 1.0e-8\n    SafetyFactorForSSlope: 0.0001\n    MagneticField: AssumeZero\n  FixToAtmosphere:\n    DensityOfAtmosphere: 1.0e-15\n    DensityCutoff: &AtmosphereDensityCutoff 1.1e-15\n    VelocityLimiting:\n      AtmosphereMaxVelocity: 0\n      NearAtmosphereMaxVelocity: 1.0e-4\n      AtmosphereDensityCutoff: 3.0e-15\n      TransitionDensityBound: 3.0e-14\n    KappaLimiting:\n      DensityLowerBound: 2.0e-14\n      EplisonKappaMinus: 1.0e-3\n      DensityUpperBound: 2.0e-13\n      EpsilonKappaMax: 0.01\n      LimitAboveDensityUpperBound: False\n      MinTemperature: FromEos\n  LimitLorentzFactor:\n    Enable: false # Only needed when not using Kastaun for recovery\n    MaxDensityCutoff: 1.0e-08\n    LorentzFactorCap: 10.0\n\nPrimitiveFromConservative:\n  CutoffDForInversion: *CutoffD\n  DensityWhenSkippingInversion: *MinimumD\n  KastaunMaxLorentzFactor: 10.0\n\nEvolutionSystem:\n  ValenciaDivClean:\n    DampingParameter: 1.0\n  GeneralizedHarmonic:\n    # These gauge parameters will need some tuning for the binary\n    GaugeCondition: Harmonic\n    DampingFunctionGamma0:\n      # Parameters from SpEC run of equal mass non-spinning,\n      # polytrope K=100 Gamma=2\n      TimeDependentTripleGaussian:\n        MovementMethod: ObjectCenters\n        Constant: 0.01\n        Gaussian1:\n          Amplitude: 0.06277857994\n          Width: 7.884855\n          Center: Auto\n        Gaussian2:\n          Amplitude: 0.06277857994\n          Width: 7.884855\n          Center: Auto\n        Gaussian3:\n          Amplitude: 0.06277857994\n          Width: 51.60996\n          Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -0.999\n        Amplitude: 0.999\n        Width: 318.534\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      # Parameters from SpEC run of equal mass non-spinning,\n      # polytrope K=100 Gamma=2\n      TimeDependentTripleGaussian:\n        MovementMethod: ObjectCenters\n        Constant: 0.01\n        Gaussian1:\n          Amplitude: 0.94167869922\n          Width: 7.884855\n          Center: Auto\n        Gaussian2:\n          Amplitude: 0.94167869922\n          Width: 7.884855\n          Center: Auto\n        Gaussian3:\n          Amplitude: 0.19182343873\n          Width: 51.60996\n          Center: [0.0, 0.0, 0.0]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    ProductAveragedUpwindPenaltyAndHll:\n      AveragedUpwindPenalty:\n      Hll:\n        MagneticFieldMagnitudeForHydro: 1.0e-16\n        LightSpeedDensityCutoff: 1.0e-8\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 20\n          MinTciCallsAfterRollback: 10\n          MinimumClearTcis: 2\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: [Envelope, OuterShell0, OuterShell1, OuterShell2]\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n    TciOptions:\n      MinimumValueOfD: 1.0e-20\n      MinimumValueOfYe: 1.0e-20\n      MinimumValueOfTildeTau: 1.0e-50\n      AtmosphereDensity: *AtmosphereDensityCutoff\n      SafetyFactorForB: *BSafetyFactor\n      MagneticFieldCutoff: DoNotCheckMagneticField\n  SubcellSolver:\n    Reconstructor:\n      PositivityPreservingAdaptiveOrderPrim:\n        Alpha5: 3.8\n        Alpha7: None\n        Alpha9: None\n        LowOrderReconstructor: MonotonisedCentral\n        AtmosphereTreatment: Never\n        ReconstructRhoTimesTemperature: true\n    FilterOptions:\n      SpacetimeDissipation: 0.3\n\nFiltering:\n  ExpFilter0:\n    Alpha: 64\n    # Half power was determined to filter only the last term up to N = 19\n    # since we support up to 20 gridpoints.\n    HalfPower: 420\n    Enable: true\n    BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  # Set time step at t=0\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [0]\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 0\n          StepChoosers:\n            - Cfl:\n                SafetyFactor: &CflSafetyFactor 0.4\n  # Update time step size regularly.\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 100\n          Offset: 100\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 20\n          StepChoosers:\n            - Cfl:\n                SafetyFactor: *CflSafetyFactor\n\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 120\n          Offset: 0\n    Events:\n      - BondiSachsInterpolation\n\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    Events:\n      - ObserveTimeStep:\n            SubfileName: TimeSteps\n            PrintTimeToTerminal: True\n            ObservePerCore: False\n\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2000\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: MaxDensity\n          TensorsToObserve:\n            - Name: RestMassDensity\n              NormType: Max\n              Components: Individual\n      - ObserveNorms:\n          SubfileName: Constraints\n          TensorsToObserve:\n          - Name: PointwiseL2Norm(GaugeConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(ThreeIndexConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(ConstraintEnergy)\n            NormType: L2Norm\n            Components: Sum\n\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2000\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: MetricDiagnostics\n          TensorsToObserve:\n          - Name: DetSpatialMetric\n            NormType: Max\n            Components: Individual\n          - Name: DetSpatialMetric\n            NormType: Min\n            Components: Individual\n          - Name: Lapse\n            NormType: Max\n            Components: Individual\n          - Name: Lapse\n            NormType: Min\n            Components: Individual\n      - ObserveNorms:\n          SubfileName: FluidQuantities\n          TensorsToObserve:\n          - Name: MassWeightedInternalEnergy\n            NormType: VolumeIntegral\n            Components: Sum\n          - Name: MassWeightedKineticEnergy\n            NormType: VolumeIntegral\n            Components: Sum\n          - Name: TildeDUnboundUtCriterion\n            NormType: VolumeIntegral\n            Components: Individual\n\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2000\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeSpacetimeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            - DetSpatialMetric\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Float\n          FloatingPointTypes: [Float]\n          BlocksToObserve: All\n      - ObserveFields:\n          SubfileName: VolumeHydroData\n          VariablesToObserve:\n            - RestMassDensity\n            - Temperature\n            - ElectronFraction\n            - SpatialVelocity\n            - MagneticField\n            - DivergenceCleaningField\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Float\n          FloatingPointTypes: [Float]\n          BlocksToObserve: [ObjectA, ObjectB]\n\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 100\n          Offset: 0\n    Events:\n      - ObserveTimeStepVolume:\n          SubfileName: TimeStepSizeInVolume\n          CoordinatesFloatingPointType: Float\n          FloatingPointType: Float\n      - ObserveFields:\n          SubfileName: VolumeDiagnosticData\n          VariablesToObserve:\n            - PointwiseL2Norm(GaugeConstraint)\n            - PointwiseL2Norm(ThreeIndexConstraint)\n            - TciStatus\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Float\n          FloatingPointTypes: [Float]\n          BlocksToObserve: All\n\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThan\n        Value: 6000.0 # About 9 orbits plus 1200M post-merger\n    Events:\n      - Completion\n\nInterpolationTargets:\n  BondiSachsInterpolation:\n    LMax: 16\n    # So far the R=200M_sun seems to be best. Could be that we need a bigger\n    # outer boundary or that BBH and BNS just are sufficiently different that\n    # even a closer worldtube may do better.\n    Radius: [100, 150, 200, 250, 300, 400,  500]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nObservers:\n  VolumeFileName: \"GhMhdVolume\"\n  ReductionFileName: \"GhMhdReductions\"\n\nInterpolator:\n  Verbosity: Silent\n\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n    - ObserveFields:\n        SubfileName: VolumeData\n        VariablesToObserve:\n          - SpacetimeMetric\n          - Lapse\n          - Shift\n          - DetSpatialMetric\n          - RestMassDensity\n          - Pressure\n          - SpatialVelocity\n          - LorentzFactor\n          - SpecificInternalEnergy\n          - Temperature\n          - MagneticField\n          - ElectronFraction\n          - DivergenceCleaningField\n          - PointwiseL2Norm(GaugeConstraint)\n          - PointwiseL2Norm(ThreeIndexConstraint)\n          - TciStatus\n        InterpolateToMesh: None\n        ProjectToMesh: None\n        CoordinatesFloatingPointType: Double\n        FloatingPointTypes: [Double]\n        BlocksToObserve: All\n\n# Control systems are disabled by default\nControlSystems:\n  DisableRotationWhen:\n    DisableAtSeparation: *RotationOffSeparation\n    RotationDecayTimescale: 60.0\n  WriteDataToDisk: true\n  MeasurementsPerUpdate: 4\n  DelayUpdate: true\n  Verbosity: Silent\n  Expansion: None\n  Rotation:\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: false\n    Controller:\n      UpdateFraction: 0.03\n    TimescaleTuner:\n      InitialTimescales: [10.0, 10.0, 10.0]\n      MinTimescale: 1.0e-2\n      MaxTimescale: 10.0\n      IncreaseThreshold: 2.5e-4\n      DecreaseThreshold: 1.0e-3\n      IncreaseFactor: 1.01\n      DecreaseFactor: 0.98\n    ControlError:\n  Translation:\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: false\n    Controller:\n      UpdateFraction: 0.25\n    TimescaleTuner:\n      InitialTimescales: [10.0, 10.0, 10.0]\n      MinTimescale: 1.0e-2\n      MaxTimescale: 20.0\n      IncreaseThreshold: 2.5e-4\n      DecreaseThreshold: 1.0e-3\n      IncreaseFactor: 1.01\n      DecreaseFactor: 0.98\n    ControlError:\n  GridCenters:\n    Averager:\n      AverageTimescaleFraction: 0.25\n      Average0thDeriv: false\n    Controller:\n      UpdateFraction: 0.25\n    TimescaleTuner:\n      InitialTimescales: 10.0\n      MinTimescale: 1.0e-2\n      MaxTimescale: 20.0\n      IncreaseThreshold: 2.5e-3\n      DecreaseThreshold: 2.5e-2\n      IncreaseFactor: 1.01\n      DecreaseFactor: 0.98\n    ControlError:\n\nInitialData:\n  # SpecInitialData3dEos:\n  #   DataDirectory: \"/path/to/EvID\"\n  #   EquationOfState: *eos\n  #   DensityCutoff: *AtmosphereDensityCutoff\n  #   AtmosphereDensity: *AtmosphereDensity\n  #   ElectronFraction: 0.15\n  NumericInitialData:\n    FileGlob: BnsVolume*.h5\n    Subgroup: VolumeData\n    ObservationValue: Last\n    ObservationValueEpsilon: Auto\n    ElementsAreIdentical: False\n    GhVariables:\n      Lapse: Lapse\n      Shift: ShiftExcess\n      SpatialMetric: SpatialMetric\n      ExtrinsicCurvature: ExtrinsicCurvature\n    HydroVariables:\n      RestMassDensity: RestMassDensity\n      LowerSpatialFourVelocity: LowerSpatialFourVelocity\n      ElectronFraction: 0.15\n      MagneticField: 0.\n    DensityCutoff: *AtmosphereDensityCutoff\n"
  },
  {
    "path": "tests/InputFiles/GrMhd/GhValenciaDivClean/CoreCollapseSupernova.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\nExecutable: EvolveGhValenciaDivCleanCoreCollapseSupernova\nTesting:\n  Check: parse\n  Timeout: 8\n  Priority: High\n  CopyFiles: ../../../../tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapseID.dat; ../../../../tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\nExpectedOutput:\n  - GhMhdCCSNVolume0.h5\n  - GhMhdCCSNReductions.h5\n\n---\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 1.0e-5\n  MinimumTimeStep: 1.0e-7\n  TimeStepper:\n    Rk3HesthavenSsp:\n\nPhaseChangeAndTriggers:\n\nInitialData: &InitialData\n  GeneralizedHarmonic(CcsnCollapse):\n    ProgenitorFilename: \"./CcsnCollapseID.dat\"\n    TableFilename: \"./dd2_unit_test.h5\"\n    TableSubFilename: \"/dd2\"\n    CentralAngularVelocity: 0.0 #2.4635e-5 #5 rad/s\n    DifferentialRotationParameter: 338.53\n    MaxDensityRatioForLinearInterpolation: 100\n\nEquationOfState:\n  FromInitialData\n\n# Note: this domain is chosen so that we only need to simulate the interior\n# of the star\nDomainCreator:\n  AlignedLattice:\n    BlockBounds: [[0.0, 33.853, 13541.2],\n                  [-1.0, 1.0],\n                  [-1.0, 1.0]]\n    InitialGridPoints: [1, 1, 1]\n    InitialLevels: [1, 0, 0]\n    RefinedLevels:\n      # inner block\n      - LowerCornerIndex: [0, 0, 0]\n        UpperCornerIndex: [1, 1, 1]\n        Refinement: [1, 0, 0]\n      #outer block\n      - LowerCornerIndex: [1, 0, 0]\n        UpperCornerIndex: [2, 1, 1]\n        Refinement: [1, 0, 0]\n    RefinedGridPoints: []\n    BlocksToExclude: []\n    BoundaryConditions:\n        - DirichletAnalytic:\n            AnalyticPrescription: *InitialData\n        - DirichletAnalytic:\n            AnalyticPrescription: *InitialData\n        - DirichletAnalytic:\n            AnalyticPrescription: *InitialData\n\nVariableFixing:\n  FixConservatives:\n    Enable: false # Only needed when not using Kastaun for recovery\n    CutoffD: &CutoffD 1.0e-16\n    MinimumValueOfD: &MinimumD 1.0e-16\n    CutoffYe: 0.0\n    MinimumValueOfYe: 0.0\n    SafetyFactorForB: 1.0e-12\n    SafetyFactorForS: 1.0e-12\n    SafetyFactorForSCutoffD: 1.0e-12\n    SafetyFactorForSSlope: 0.0\n    MagneticField: AssumeZero\n  FixToAtmosphere:\n    DensityOfAtmosphere: 1.0e-15\n    DensityCutoff: &AtmosphereDensityCutoff 1.1e-15\n    VelocityLimiting:\n      AtmosphereMaxVelocity: 0\n      NearAtmosphereMaxVelocity: 1.0e-4\n      AtmosphereDensityCutoff: 3.0e-15\n      TransitionDensityBound: 3.0e-14\n    KappaLimiting:\n      DensityLowerBound: 2.0e-14\n      EplisonKappaMinus: 1.0e-3\n      DensityUpperBound: 2.0e-13\n      EpsilonKappaMax: 0.01\n      LimitAboveDensityUpperBound: False\n      MinTemperature: FromEos\n  LimitLorentzFactor:\n    Enable: false # Only needed when not using Kastaun for recovery\n    MaxDensityCutoff: 1.0e-12\n    LorentzFactorCap: 1.0\n  ParameterizedDeleptonization:\n    Enable: true\n    HighDensityScale: 3.24e-5\n    LowDensityScale: 3.24e-11\n    ElectronFractionAtHighDensity: 0.285\n    ElectronFractionAtLowDensity: 0.5\n    ElectronFractionCorrectionScale: 0.035\n\nPrimitiveFromConservative:\n  CutoffDForInversion: *CutoffD\n  DensityWhenSkippingInversion: *MinimumD\n  KastaunMaxLorentzFactor: 10.0\n\n\nEvolutionSystem:\n  ValenciaDivClean:\n    DampingParameter: 0.0\n  GeneralizedHarmonic:\n    GaugeCondition:\n      Harmonic:\n    DampingFunctionGamma0:\n      GaussianPlusConstant:\n        Constant: 0\n        Amplitude: 0\n        Width: 90\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: 0\n        Amplitude: 0\n        Width: 210\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      GaussianPlusConstant:\n        Constant: 0.01\n        Amplitude: 4\n        Width: 90\n        Center: [0.0, 0.0, 0.0]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    ProductAveragedUpwindPenaltyAndHll:\n      AveragedUpwindPenalty:\n      Hll:\n        MagneticFieldMagnitudeForHydro: 1.0e-16\n        LightSpeedDensityCutoff: 1.0e-8\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: Gauss\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: true\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 6\n      FdInterpolationOrder: 1\n    TciOptions:\n      MinimumValueOfD: 1.0e-15\n      MinimumValueOfYe: 1.0e-20\n      MinimumValueOfTildeTau: 1.0e-40\n      AtmosphereDensity: 1.0e-15\n      SafetyFactorForB: 1.0e-12\n      MagneticFieldCutoff: DoNotCheckMagneticField\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentralPrim:\n        AtmosphereTreatment: Never\n        ReconstructRhoTimesTemperature: true\n    FilterOptions:\n      SpacetimeDissipation: 0.9\n\nFiltering:\n  ExpFilter0:\n    Alpha: 36\n    HalfPower: 64\n    Enable: true\n    BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 1\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 5\n          StepChoosers:\n            - ErrorControl:\n                AbsoluteTolerance: 1.0e-6\n                RelativeTolerance: 1.0e-4\n                MaxFactor: 2\n                MinFactor: 0.25\n                SafetyFactor: 0.95\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 40\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: max_density\n          TensorsToObserve:\n            - Name: RestMassDensity\n              NormType: Max\n              Components: Individual\n      - ObserveNorms:\n          SubfileName: min_data_lapse\n          TensorsToObserve:\n            - Name: Lapse\n              NormType: Min\n              Components: Individual\n      - ObserveNorms:\n          SubfileName: max_three_index_constraint\n          TensorsToObserve:\n            - Name: PointwiseL2Norm(ThreeIndexConstraint)\n              NormType: Max\n              Components: Individual\n\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 100\n          Offset: 0\n    Events:\n      - ObserveTimeStep:\n            SubfileName: TimeSteps\n            PrintTimeToTerminal: True\n            ObservePerCore: False\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1000\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          BlocksToObserve: All\n          VariablesToObserve:\n            - SpacetimeMetric\n            - RestMassDensity\n            - Pressure\n            - ElectronFraction\n            - MagneticField\n            - Temperature\n            - SpatialVelocity\n            - SpecificInternalEnergy\n            - PointwiseL2Norm(GaugeConstraint)\n            - TildeD\n            - TildeS\n            - TildeTau\n            - Phi\n            - Pi\n            - Lapse\n            - PointwiseL2Norm(ThreeIndexConstraint)\n            - ThreeIndexConstraint\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: 40592\n    Events:\n      - Completion\n\nInterpolationTargets:\n  BondiSachsInterpolation:\n    LMax: 16\n    Radius: [1]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nObservers:\n  VolumeFileName: \"GhMhdCCSNVolume\"\n  ReductionFileName: \"GhMhdCCSNReductions\"\n\nInterpolator:\n  Verbosity: Silent\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n"
  },
  {
    "path": "tests/InputFiles/GrMhd/GhValenciaDivClean/EvolutionParameters.perl",
    "content": "# -*- perl -*-\n#This file should be read by your evolution's DoMultipleRuns.input\n#It defines the properties of the initial data that are relevant to do\n#an evolution\n\n#Compiled at mbot:/home/fs01/spec1168/spec\n#Error checking disabled (to activate add -DDEBUG to CPPFLAGS)\n#Code Revision ddabc19dbd98539df19d1c1b3a52ac43e1087325\n#Linked on 2024-08-30T11:19:47-04:00\n#Executed BnsIdSolver\n#Centers of the neutron stars\n@CenterNS1 = (-16.2,0,0);\n@CenterNS2 = (16.1996,3.59764e-05,0);\n\n$ID_d = 32.3996;\n\n#Baryon masses of the neutron stars\n$MassNS1 = 1.4958;\n$MassNS2 = 1.4958;\n$ADMmassNS1 = 1.35;\n$ADMmassNS2 = 1.35;\n#Radii of the stars\n$r1 = 6.06997;\n$r2 = 6.06997;\n\n#Central baryon density of neutron stars\n$CentralDensity1 = 0.00137887;\n$CentralDensity2 = 0.00137887;\n#Orbital parameters of the binary\n$InitialOmega = 0.00801722; # deprecated\n\n$ID_Omega0 = 0.00801722;\n\n#Inital rate of shrinking\n$ID_adot0 = -0.00008095;\n#Equation of state\n$EOS = 'SpectralGamma(GammaCoefs=2.000000,0.00000, 0.4029, -0.1008; \\\nGammaThermal=1.75; rho0=1.0118e-4; P0=3.3625e-7; RhoMax=0.005;)';\n#Initial Adm Energy\n$InitialAdmEnergy = 2.67601\n"
  },
  {
    "path": "tests/InputFiles/GrMhd/GhValenciaDivClean/GhMhdBondiMichel.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhValenciaDivCleanWithHorizon\nTesting:\n  Check: parse;execute\n  Timeout: 8\n  Priority: High\nExpectedOutput:\n  - GhMhdBondiMichelVolume0.h5\n  - GhMhdBondiMichelReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1000\n          Offset: 5\n    PhaseChanges:\n      - VisitAndReturn(LoadBalancing)\n\nInitialData: &InitialData\n  GeneralizedHarmonic(BondiMichel):\n    Mass: 1.0\n    SonicRadius: 6.0\n    SonicDensity: 1.0e-5\n    PolytropicExponent: 1.4\n    MagFieldStrength: 1.0e-2\n\nEquationOfState:\n  FromInitialData\n\nDomainCreator:\n  Sphere:\n    InnerRadius: 1.9\n    OuterRadius: 20.0\n    Interior:\n      ExciseWithBoundaryCondition:\n        DirichletAnalytic:\n            AnalyticPrescription: *InitialData\n    InitialRefinement: 1\n    InitialGridPoints: 2\n    UseEquiangularMap: true\n    EquatorialCompression: None\n    WhichWedges: All\n    RadialPartitioning: []\n    RadialDistribution: [Logarithmic]\n    TimeDependentMaps: None\n    OuterBoundaryCondition:\n      DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n\nVariableFixing:\n  FixConservatives:\n    Enable: false # Only needed when not using Kastaun for recovery\n    CutoffD: &CutoffD 1.0e-12\n    MinimumValueOfD: &MinimumD 1.0e-12\n    CutoffYe: 0.0\n    MinimumValueOfYe: 0.0\n    SafetyFactorForB: 1.0e-12\n    SafetyFactorForS: 1.0e-12\n    SafetyFactorForSCutoffD: 1.0e-12\n    SafetyFactorForSSlope: 0.0\n    MagneticField: AssumeNonZero\n  FixToAtmosphere:\n    DensityOfAtmosphere: 1.0e-12\n    DensityCutoff: 1.0e-12\n    VelocityLimiting:\n      AtmosphereMaxVelocity: 0\n      NearAtmosphereMaxVelocity: 1.0e-4\n      AtmosphereDensityCutoff: 3.0e-12\n      TransitionDensityBound: 1.0e-11\n    KappaLimiting:\n      DensityLowerBound: 2.0e-11\n      EplisonKappaMinus: 1.0e-3\n      DensityUpperBound: 2.0e-10\n      EpsilonKappaMax: 0.01\n      LimitAboveDensityUpperBound: False\n      MinTemperature: FromEos\n  LimitLorentzFactor:\n    Enable: false # Only needed when not using Kastaun for recovery\n    MaxDensityCutoff: 1.0e-12\n    LorentzFactorCap: 1.0\n\nPrimitiveFromConservative:\n  CutoffDForInversion: *CutoffD\n  DensityWhenSkippingInversion: *MinimumD\n  KastaunMaxLorentzFactor: 10.0\n\nEvolutionSystem:\n  ValenciaDivClean:\n    DampingParameter: 0.0\n  GeneralizedHarmonic:\n    GaugeCondition:\n      AnalyticChristoffel:\n        AnalyticPrescription: *InitialData\n    DampingFunctionGamma0:\n      GaussianPlusConstant:\n        Constant: 1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      GaussianPlusConstant:\n        Constant: 1.0\n        Amplitude: 0.0\n        Width: 1.0\n        Center: [0.0, 0.0, 0.0]\n\n\nSpatialDiscretization:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n  BoundaryCorrection:\n    ProductAveragedUpwindPenaltyAndRusanov:\n      AveragedUpwindPenalty:\n      Rusanov:\n\nFiltering:\n  ExpFilter0:\n    Enable: false\n    Alpha: 64\n    HalfPower: 420\n    BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 0\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 5\n          StepChoosers:\n            - Cfl:\n                SafetyFactor: 0.6\n            - LimitIncrease:\n                Factor: 1.5\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(SpacetimeMetric)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(RestMassDensity)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(SpecificInternalEnergy)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(SpatialVelocity)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(MagneticField)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(DivergenceCleaningField)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(LorentzFactor)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pressure)\n              NormType: L2Norm\n              Components: Sum\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            - RestMassDensity\n            - Pressure\n            - MagneticField\n            - PointwiseL2Norm(GaugeConstraint)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double, Double, Double, Double, Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 3\n    Events:\n      - AhA\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [2]\n    Events:\n      - Completion\n\nApparentHorizons:\n  LMax: 4\n  AhA:\n    Criteria:\n    InitialGuess:\n      LMax: 4\n      Radius: 2.2\n      Center: [0.0, 0.0, 0.0]\n    FastFlow:\n      Flow: Fast\n      Alpha: 1.0\n      Beta: 0.5\n      AbsTol: 1e-12\n      TruncationTol: 1e-2\n      DivergenceTol: 1.2\n      DivergenceIter: 5\n      MaxIts: 100\n    Verbosity: Verbose\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: All\n\nInterpolationTargets:\n  BondiSachsInterpolation:\n    LMax: 16\n    Radius: [10]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nObservers:\n  VolumeFileName: \"GhMhdBondiMichelVolume\"\n  ReductionFileName: \"GhMhdBondiMichelReductions\"\n\nInterpolator:\n  Verbosity: Silent\n\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n"
  },
  {
    "path": "tests/InputFiles/GrMhd/GhValenciaDivClean/GhMhdTovStar.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveGhValenciaDivClean\nTesting:\n  Check: parse;execute\n  Timeout: 30\n  Priority: High\nExpectedOutput:\n  - GhMhdTovStarVolume0.h5\n  - GhMhdTovStarReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    # Both RK3 and AM-PC 3 should work. These are the \"standard\" time steppers\n    # for spectre for hydro right now.\n    Rk3HesthavenSsp:\n    # AdamsMoultonPc:\n    #   Order: 3\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1000\n          Offset: 5\n    PhaseChanges:\n      - VisitAndReturn(LoadBalancing)\n\nInitialData: &InitialData\n  GeneralizedHarmonic(TovStar):\n    CentralDensity: 1.28e-3\n    EquationOfState:\n        PolytropicFluid:\n            PolytropicConstant: 100.0\n            PolytropicExponent: 2.0\n    Coordinates: Isotropic\n\nEquationOfState:\n  FromInitialData\n# Note: this domain is chosen so that we only need to simulate the interior\n# of the star\nDomainCreator:\n  Brick:\n    LowerBound: [-5.0, -5.0, -5.0]\n    UpperBound: [5.0, 5.0, 5.0]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [1, 1, 1]\n    InitialGridPoints: [5, 5, 5]\n    TimeDependence: None\n    BoundaryConditions:\n      - DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n      - DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n      - DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n\nVariableFixing:\n  FixConservatives:\n    Enable: false # Only needed when not using Kastaun for recovery\n    CutoffD: &CutoffD 1.1e-15\n    MinimumValueOfD: &MinimumD 1.0e-15\n    CutoffYe: 0.0\n    MinimumValueOfYe: 0.0\n    SafetyFactorForB: &BSafetyFactor 1.0e-12\n    SafetyFactorForS: 1.0e-12\n    SafetyFactorForSCutoffD: 1.0e-8\n    SafetyFactorForSSlope: 0.0001\n    MagneticField: AssumeZero\n  FixToAtmosphere:\n    DensityOfAtmosphere: 1.0e-15\n    DensityCutoff: &AtmosphereDensityCutoff 1.1e-15\n    VelocityLimiting:\n      AtmosphereMaxVelocity: 0\n      NearAtmosphereMaxVelocity: 1.0e-4\n      AtmosphereDensityCutoff: 3.0e-15\n      TransitionDensityBound: 3.0e-14\n    KappaLimiting:\n      DensityLowerBound: 2.0e-14\n      EplisonKappaMinus: 1.0e-3\n      DensityUpperBound: 2.0e-13\n      EpsilonKappaMax: 0.01\n      LimitAboveDensityUpperBound: False\n      MinTemperature: FromEos\n  LimitLorentzFactor:\n    Enable: false # Only needed when not using Kastaun for recovery\n    MaxDensityCutoff: 1.0e-12\n    LorentzFactorCap: 1.0\n\nPrimitiveFromConservative:\n  CutoffDForInversion: *CutoffD\n  DensityWhenSkippingInversion: *MinimumD\n  KastaunMaxLorentzFactor: 10.0\n\nEvolutionSystem:\n  ValenciaDivClean:\n    DampingParameter: 0.0\n  GeneralizedHarmonic:\n    GaugeCondition:\n      AnalyticChristoffel:\n        AnalyticPrescription: *InitialData\n    DampingFunctionGamma0:\n      GaussianPlusConstant:\n        Constant: 0.01\n        Amplitude: 0.12\n        # Width comes from the radius of the star in code units.\n        Width: 7.884855\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -0.999\n        Amplitude: 0.999\n        # Width is chosen so that the outer boundary is a few sigma out,\n        # but large enough that the bulk dynamics have gamma_2=0\n        Width: 30.0\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      GaussianPlusConstant:\n        Constant: 0.01\n        Amplitude: 1.2\n        # Width comes from the radius of the star in code units.\n        Width: 7.884855\n        Center: [0.0, 0.0, 0.0]\n\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    ProductAveragedUpwindPenaltyAndRusanov:\n      AveragedUpwindPenalty:\n      Rusanov:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n    TciOptions:\n      MinimumValueOfD: 1.0e-20\n      MinimumValueOfYe: 1.0e-20\n      MinimumValueOfTildeTau: 1.0e-50\n      AtmosphereDensity: *AtmosphereDensityCutoff\n      SafetyFactorForB: *BSafetyFactor\n      MagneticFieldCutoff: DoNotCheckMagneticField\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentralPrim:\n        AtmosphereTreatment: Never\n        ReconstructRhoTimesTemperature: true\n    FilterOptions:\n      SpacetimeDissipation: 0.1\n\nFiltering:\n  ExpFilter0:\n    Alpha: 36\n    HalfPower: 64\n    Enable: true\n    BlocksToFilter: All\n\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 0\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 5\n          StepChoosers:\n            - Cfl:\n                SafetyFactor: 0.6\n            - LimitIncrease:\n                Factor: 1.5\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(SpacetimeMetric)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(RestMassDensity)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(SpecificInternalEnergy)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(SpatialVelocity)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(MagneticField)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(DivergenceCleaningField)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(LorentzFactor)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pressure)\n              NormType: L2Norm\n              Components: Sum\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - SpacetimeMetric\n            - RestMassDensity\n            - Pressure\n            - MagneticField\n            - PointwiseL2Norm(GaugeConstraint)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double, Double, Double, Double, Double]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [3]\n    Events:\n      - Completion\n\nInterpolationTargets:\n  BondiSachsInterpolation:\n    LMax: 16\n    Radius: [1]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nObservers:\n  VolumeFileName: \"GhMhdTovStarVolume\"\n  ReductionFileName: \"GhMhdTovStarReductions\"\n\nInterpolator:\n  Verbosity: Silent\n\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n"
  },
  {
    "path": "tests/InputFiles/GrMhd/ValenciaDivClean/BlastWave.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveValenciaDivClean\nTesting:\n  Check: parse;execute\n  Timeout: 8\n  Priority: High\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.01\n  MinimumTimeStep: 1e-7\n  TimeStepper: Rk3HesthavenSsp\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n       EvenlySpaced:\n         # Current implementation checks wallclock at these global syncs\n         Interval: 100\n         Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: None\n\nDomainCreator:\n  Brick:\n    LowerBound: [-6.0, -6.0, -6.0]\n    UpperBound: [6.0, 6.0, 6.0]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [2, 2, 0]\n    InitialGridPoints: [3, 3, 3]\n    TimeDependence: None\n    BoundaryConditions: [Periodic, Periodic, Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Rusanov:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n    TciOptions:\n      MinimumValueOfD: 1.0e-20\n      MinimumValueOfYe: 1.0e-20\n      MinimumValueOfTildeTau: 1.0e-50\n      MagneticFieldCutoff: DoNotCheckMagneticField\n      AtmosphereDensity: 1.01e-15\n      SafetyFactorForB: 1.0e-12\n  SubcellSolver:\n    Reconstructor:\n      Wcns5zPrim:\n        NonlinearWeightExponent: 2\n        Epsilon: 1.0e-15\n        FallbackReconstructor: MonotonisedCentral\n        MaxNumberOfExtrema: 0\n        ReconstructRhoTimesTemperature: true\n\nInitialData:\n  BlastWave:\n    InnerRadius: 0.8\n    OuterRadius: 1.0\n    InnerDensity: 1.0e-2\n    OuterDensity: 1.0e-4\n    InnerPressure: 1.0\n    OuterPressure: 5.0e-4\n    MagneticField: [0.1, 0.0, 0.0]\n    AdiabaticIndex: 1.3333333333333333333\n    Geometry: Cylindrical\n\nEquationOfState:\n  FromInitialData\n\nEvolutionSystem:\n  ValenciaDivClean:\n    DampingParameter: 1.0\n\nVariableFixing:\n  FixConservatives:\n    Enable: false # Only needed when not using Kastaun for recovery\n    CutoffD: &CutoffD 1.0e-12\n    MinimumValueOfD: &MinimumD 1.0e-12\n    CutoffYe: 0.0\n    MinimumValueOfYe: 0.0\n    SafetyFactorForB: 1.0e-12\n    SafetyFactorForS: 1.0e-12\n    SafetyFactorForSCutoffD: 1.0e-12\n    SafetyFactorForSSlope: 0.0\n    MagneticField: AssumeNonZero\n  FixToAtmosphere:\n    DensityOfAtmosphere: 1.0e-12\n    DensityCutoff: 1.0e-12\n    VelocityLimiting:\n      AtmosphereMaxVelocity: 0\n      NearAtmosphereMaxVelocity: 1.0e-4\n      AtmosphereDensityCutoff: 3.0e-12\n      TransitionDensityBound: 1.0e-11\n    KappaLimiting:\n      DensityLowerBound: 2.0e-11\n      EplisonKappaMinus: 1.0e-3\n      DensityUpperBound: 2.0e-10\n      EpsilonKappaMax: 0.01\n      LimitAboveDensityUpperBound: False\n      MinTemperature: FromEos\n\nPrimitiveFromConservative:\n  CutoffDForInversion: *CutoffD\n  DensityWhenSkippingInversion: *MinimumD\n  KastaunMaxLorentzFactor: 10.0\n\nObservers:\n  VolumeFileName: \"ValenciaDivCleanBlastWaveVolume\"\n  ReductionFileName: \"ValenciaDivCleanBlastWaveReductions\"\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [2]\n    Events:\n      - Completion\n\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n"
  },
  {
    "path": "tests/InputFiles/GrMhd/ValenciaDivClean/FishboneMoncriefDisk.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveValenciaDivCleanWithHorizon\nTesting:\n  Check: parse;execute\n  Timeout: 20\n  Priority: High\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons:\n    KerrHorizon:\n      Proc: Auto\n      Exclusive: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.01\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n  # StepChoosers are needed only for local time stepping\n  # LtsStepChoosers:\n  #   - LimitIncrease:\n  #       Factor: 2\n  #   - PreventRapidIncrease\n  #   - Cfl:\n  #       SafetyFactor: 0.2\n  # InitialSlabSize is only needed for local time stepping\n  # InitialSlabSize: 0.01\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n       EvenlySpaced:\n         # Current implementation checks wallclock at these global syncs\n         Interval: 100\n         Offset: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: None\n\nInitialData: &initial_data\n  FishboneMoncriefDisk:\n    BhMass: &BhMass 1.0\n    BhDimlessSpin: &BhDimlessSpin 0.9375\n    InnerEdgeRadius: 6.0\n    MaxPressureRadius: 12.0\n    PolytropicConstant: 0.001\n    PolytropicExponent: 1.3333333333333333333333\n    Noise: 0.0\n\nEquationOfState:\n  FromInitialData\n\nDomainCreator:\n  Brick:\n    LowerBound: [10.5, 0.0, 0.0]\n    UpperBound: [11.5, 1.0, 1.0]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [0, 0, 0]\n    InitialGridPoints: [5, 5, 5]\n    TimeDependence: None\n    BoundaryConditions:\n      - DirichletAnalytic:\n          AnalyticPrescription: *initial_data\n      - DirichletAnalytic:\n          AnalyticPrescription: *initial_data\n      - DirichletAnalytic:\n          AnalyticPrescription: *initial_data\n\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Rusanov:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n    TciOptions:\n      MinimumValueOfD: 1.0e-20\n      MinimumValueOfYe: 1.0e-20\n      MinimumValueOfTildeTau: 1.0e-50\n      MagneticFieldCutoff: DoNotCheckMagneticField\n      AtmosphereDensity: 1.01e-15\n      SafetyFactorForB: 1.0e-12\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentralPrim:\n        ReconstructRhoTimesTemperature: true\n\n\nEvolutionSystem:\n  ValenciaDivClean:\n    DampingParameter: 1.0\n\nVariableFixing:\n  FixConservatives:\n    Enable: false # Only needed when not using Kastaun for recovery\n    CutoffD: &CutoffD 1.0e-12\n    MinimumValueOfD: &MinimumD 1.0e-12\n    CutoffYe: 0.0\n    MinimumValueOfYe: 0.0\n    SafetyFactorForB: 1.0e-12\n    SafetyFactorForS: 1.0e-12\n    SafetyFactorForSCutoffD: 1.0e-12\n    SafetyFactorForSSlope: 0.0\n    MagneticField: AssumeNonZero\n  FixToAtmosphere:\n    DensityOfAtmosphere: 1.0e-12\n    DensityCutoff: 1.0e-12\n    VelocityLimiting:\n      AtmosphereMaxVelocity: 0\n      NearAtmosphereMaxVelocity: 1.0e-4\n      AtmosphereDensityCutoff: 3.0e-12\n      TransitionDensityBound: 1.0e-11\n    KappaLimiting:\n      DensityLowerBound: 2.0e-11\n      EplisonKappaMinus: 1.0e-3\n      DensityUpperBound: 2.0e-10\n      EpsilonKappaMax: 0.01\n      LimitAboveDensityUpperBound: False\n      MinTemperature: FromEos\n\nPrimitiveFromConservative:\n  CutoffDForInversion: *CutoffD\n  DensityWhenSkippingInversion: *MinimumD\n  KastaunMaxLorentzFactor: 10.0\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [10]\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - ChangeSlabSize:\n          # DelayChange: 0 forces a synchronization after every slab.\n          # It is more efficient to use a higher value.  This is for\n          # testing.\n          DelayChange: 0\n          StepChoosers:\n            - Maximum: 0.05\n            - LimitIncrease:\n                Factor: 2\n            - Cfl:\n                SafetyFactor: 0.2\n            - PreventRapidIncrease\n\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n\nObservers:\n  VolumeFileName: \"ValenciaDivCleanFishboneMoncriefDiskVolume\"\n  ReductionFileName: \"ValenciaDivCleanFishboneMoncriefDiskReductions\"\n\nInterpolator:\n  Verbosity: Silent\n\nInterpolationTargets:\n  KerrHorizon:\n    LMax: 10\n    Center: [0.0, 0.0, 0.0]\n    Mass: *BhMass\n    DimensionlessSpin: [0.0, 0.0, *BhDimlessSpin]\n    AngularOrdering: Strahlkorper\n"
  },
  {
    "path": "tests/InputFiles/NewtonianEuler/RiemannProblem1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveNewtonianEuler1D\nTesting:\n  Check: parse;execute\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    Rk3HesthavenSsp\n\nPhaseChangeAndTriggers:\n\nEquationOfState:\n  IdealFluid:\n    AdiabaticIndex: 1.4\n    MinTemperature: 0.0\n\nInitialData: &InitialData\n  RiemannProblem:\n    AdiabaticIndex: 1.4\n    InitialPosition: 0.25\n    LeftMassDensity: 1.0\n    LeftVelocity: [0.0]\n    LeftPressure: 1.0\n    RightMassDensity: 0.125\n    RightVelocity: [0.0]\n    RightPressure: 0.1\n    PressureStarTol: 1e-9\n\nDomainCreator:\n  Interval:\n    LowerBound: [-0.25]\n    UpperBound: [0.75]\n    Distribution: [Linear]\n    InitialRefinement: [2]\n    InitialGridPoints: [6]\n    TimeDependence: None\n    BoundaryConditions:\n      - DirichletAnalytic:\n          AnalyticPrescription:\n            *InitialData\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Hll:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentralPrim:\n\nEvolutionSystem:\n  SourceTerm:\n    NoSource:\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: 0.2\n    Events:\n      - Completion\n  - Trigger:\n      Always\n    Events:\n      - ChangeSlabSize:\n          # Smaller values for DelayChange make the error control more\n          # responsive, while larger values reduce the global coupling\n          # between all the elements.  Experimentally, varying this\n          # number has little qualitative effect on chosen step size,\n          # as long as the initial step size is not too large for the\n          # requested accuracy.\n          DelayChange: 10\n          StepChoosers:\n            - ErrorControl:\n                AbsoluteTolerance: 1.0e-3\n                RelativeTolerance: 1.0e-3\n                MaxFactor: 10000.0\n                MinFactor: 0.0\n                SafetyFactor: 0.9\n      - ChangeSlabSize:\n          # These step choosers require no communication, so can be\n          # applied with a delay of 0 with no downside.\n          DelayChange: 0\n          StepChoosers:\n            - LimitIncrease:\n                Factor: 2.0\n            - PreventRapidIncrease\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"NewtonianEulerRiemannProblem1DVolume\"\n  ReductionFileName: \"NewtonianEulerRiemannProblem1DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/NewtonianEuler/RiemannProblem2D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveNewtonianEuler2D\nTesting:\n  Check: parse;execute\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    Rk3HesthavenSsp\n\nPhaseChangeAndTriggers:\n\nEquationOfState:\n  IdealFluid:\n    AdiabaticIndex: 1.4\n    MinTemperature: 0.0\n\nInitialData: &InitialData\n  RiemannProblem:\n    AdiabaticIndex: 1.4\n    InitialPosition: 0.25\n    LeftMassDensity: 1.0\n    LeftVelocity: [0.0, 0.5]\n    LeftPressure: 1.0\n    RightMassDensity: 0.125\n    RightVelocity: [0.0, 0.2]\n    RightPressure: 0.1\n    PressureStarTol: 1e-9\n\nDomainCreator:\n  Rectangle:\n    LowerBound: [-0.25, 0.0]\n    UpperBound: [0.75, 0.1]\n    Distribution: [Linear, Linear]\n    InitialRefinement: [4, 0]\n    InitialGridPoints: [2, 2]\n    TimeDependence: None\n    BoundaryConditions:\n      - DirichletAnalytic:\n          AnalyticPrescription:\n            *InitialData\n      - DirichletAnalytic:\n          AnalyticPrescription:\n            *InitialData\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Hll:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentralPrim:\n\nEvolutionSystem:\n  SourceTerm:\n    NoSource:\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [10]\n    Events:\n      - Completion\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"NewtonianEulerRiemannProblem2DVolume\"\n  ReductionFileName: \"NewtonianEulerRiemannProblem2DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/NewtonianEuler/RiemannProblem3D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveNewtonianEuler3D\nTesting:\n  Check: parse;execute\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.0001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    Rk3HesthavenSsp\n\nPhaseChangeAndTriggers:\n\nEquationOfState:\n  IdealFluid:\n    AdiabaticIndex: 1.4\n    MinTemperature: 0.0\n\nInitialData: &InitialData\n  RiemannProblem:\n    AdiabaticIndex: 1.4\n    InitialPosition: 0.25\n    LeftMassDensity: 1.0\n    LeftVelocity: [0.0, 0.5, -0.3]\n    LeftPressure: 1.0\n    RightMassDensity: 0.125\n    RightVelocity: [0.0, 0.2, 0.1]\n    RightPressure: 0.1\n    PressureStarTol: 1e-9\n\nDomainCreator:\n  Brick:\n    LowerBound: [-0.25, 0.0, 0.0]\n    UpperBound: [0.75, 0.1, 0.1]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [4, 0, 0]\n    InitialGridPoints: [2, 2, 2]\n    TimeDependence: None\n    BoundaryConditions:\n      - DirichletAnalytic:\n          AnalyticPrescription:\n            *InitialData\n      - DirichletAnalytic:\n          AnalyticPrescription:\n            *InitialData\n      - DirichletAnalytic:\n          AnalyticPrescription:\n            *InitialData\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Hll:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentralPrim:\n\nEvolutionSystem:\n  SourceTerm:\n    NoSource:\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [10]\n    Events:\n      - Completion\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"NewtonianEulerRiemannProblem3DVolume\"\n  ReductionFileName: \"NewtonianEulerRiemannProblem3DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/Poisson/Lorentzian.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolvePoisson3D\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 60\n  Priority: High\nExpectedOutput:\n  - LorentzianReductions.h5\n  - LorentzianVolume0.h5\nOutputFileChecks:\n  - Label: Discretization error\n    Subfile: ErrorNorms.dat\n    FileGlob: LorentzianReductions.h5\n    ExpectedData:\n      - [0, 1625, 3.14162705992265e+34, 1.28010014008500e-01]\n      - [1, 1625, 3.14162705992265e+34, 7.44115187323337e-02]\n      - [2, 2808, 2.09439617691359e+34, 1.40173310841862e-02]\n    AbsoluteTolerance: 0.\n    RelativeTolerance: 1e-4\n\n---\n\nBackground: &solution\n  Lorentzian:\n    PlusConstant: 0.\n\nInitialGuess: Zero\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Sphere:\n    InnerRadius: 5.\n    OuterRadius: &outer_radius 1e9\n    Interior:\n      FillWithSphericity: 0.\n    InitialRefinement: 0\n    InitialGridPoints: 5\n    UseEquiangularMap: True\n    EquatorialCompression: None\n    RadialPartitioning: [&outer_shell_inner_radius 20.]\n    RadialDistribution: [Linear, &outer_shell_distribution Inverse]\n    WhichWedges: All\n    TimeDependentMaps: None\n    OuterBoundaryCondition:\n      AnalyticSolution:\n        Solution: *solution\n        Field: Dirichlet\n\nAmr:\n  Verbosity: Verbose\n  Criteria:\n    - IncreaseResolution\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 2\n\nPhaseChangeAndTriggers:\n  # Run AMR in every iteration, but not on the initial guess\n  - Trigger:\n      EveryNIterations:\n        N: 1\n        Offset: 1\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(UpdateSections)\n      - VisitAndReturn(CheckDomain)\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: WeakInertial\n\nObservers:\n  VolumeFileName: \"LorentzianVolume\"\n  ReductionFileName: \"LorentzianReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 100\n      RelativeResidual: 0.\n      AbsoluteResidual: 1.e-5\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: False\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Silent\n    SubdomainSolver:\n      ExplicitInverse:\n        WriteMatrixToFile: None\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates:\n  InnerRadius: *outer_shell_inner_radius\n  OuterRadius: *outer_radius\n  Compression: *outer_shell_distribution\n\nEventsAndTriggersAtIterations:\n  - Trigger: Always\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(Field)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - Field\n            - Error(Field)\n            - RadiallyCompressedCoordinates\n            - FixedSource(Field)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/InputFiles/Poisson/ProductOfSinusoids1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolvePoisson1D\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 10\nExpectedOutput:\n  - PoissonProductOfSinusoids1DReductions.h5\n  - PoissonProductOfSinusoids1DVolume0.h5\nOutputFileChecks:\n  - Label: Discretization error\n    Subfile: ErrorNorms.dat\n    FileGlob: PoissonProductOfSinusoids1DReductions.h5\n    ExpectedData:\n      - [0, 8, 4.71238898038469e+00, 7.07106781186548e-01]\n      - [1, 8, 4.71238898038469e+00, 4.14037391064113e-03]\n      - [2, 16, 4.71238898038469e+00, 1.96421037243931e-04]\n      - [3, 20, 4.71238898038469e+00, 1.47125554015345e-05]\n    AbsoluteTolerance: 1e-6\n  - Label: Linear solver convergence\n    Subfile: GmresResiduals.dat\n    FileGlob: PoissonProductOfSinusoids1DReductions.h5\n    SkipColumns: [1] # Skip walltime\n    ExpectedData:\n      # Each AMR iteration is solved directly by the multigrid bottom solver\n      # AMR iteration 0\n      - [0, 1.218121e+01]\n      - [1, 0.0]\n      # AMR iteration 1\n      - [0, 2.094477e-01]\n      - [1, 0.0]\n      # AMR iteration 2\n      - [0, 3.158830e-02]\n      - [1, 0.0]\n    AbsoluteTolerance: 1e-6\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &solution\n  ProductOfSinusoids:\n    WaveNumbers: [1]\n\nInitialGuess:\n  Zero:\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Interval:\n    LowerBound: [-1.570796326794896]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    InitialRefinement: [1]\n    InitialGridPoints: [4]\n    TimeDependence: None\n    BoundaryConditions:\n      - Lower:\n          AnalyticSolution:\n            Solution: *solution\n            Field: Dirichlet\n        Upper:\n          AnalyticSolution:\n            Solution: *solution\n            Field: Neumann\n\nAmr:\n  Verbosity: Debug\n  Criteria:\n    - DriveToTargetRefinementLevels:\n        # First AMR iteration will split the 2 elements in 4\n        Target: [2]\n        OscillationAtTarget: [DoNothing]\n    - DriveToTargetNumberOfGridPoints:\n        # Second AMR iteration will increase num points from 4 to 5\n        Target: [5]\n        OscillationAtTarget: [DoNothing]\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: 0\n  Iterations: 3\n\nPhaseChangeAndTriggers:\n  # Run AMR in every iteration, but not on the initial guess\n  - Trigger:\n      EveryNIterations:\n        N: 1\n        Offset: 1\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(UpdateSections)\n      - VisitAndReturn(CheckDomain)\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: StrongInertial\n\nObservers:\n  VolumeFileName: \"PoissonProductOfSinusoids1DVolume\"\n  ReductionFileName: \"PoissonProductOfSinusoids1DReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 10\n      RelativeResidual: 1.e-10\n      AbsoluteResidual: 1.e-6\n    Verbosity: Verbose\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: 0\n    PreSmoothing: True\n    UseBottomSolver: True\n    PostSmoothingAtBottom: False\n    Verbosity: Silent\n    OutputVolumeData: True\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Silent\n    SubdomainSolver:\n      ExplicitInverse:\n        WriteMatrixToFile: None\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates: None\n\nEventsAndTriggersAtIterations:\n  - Trigger: Always\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(Field)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve: [Field]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n"
  },
  {
    "path": "tests/InputFiles/Poisson/ProductOfSinusoids2D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolvePoisson2D\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 10\nExpectedOutput:\n  - PoissonProductOfSinusoids2DReductions.h5\n  - PoissonProductOfSinusoids2DVolume0.h5\n  - SubdomainMatrix*.txt\nOutputFileChecks:\n  - Label: Discretization error\n    Subfile: ErrorNorms.dat\n    FileGlob: PoissonProductOfSinusoids2DReductions.h5\n    ExpectedData:\n      - [1, 24, 7.40220330081702e+00, 3.70906457380056e-03]\n    AbsoluteTolerance: 1e-12\n  - Label: Linear solver convergence\n    Subfile: GmresResiduals.dat\n    FileGlob: PoissonProductOfSinusoids2DReductions.h5\n    SkipColumns: [1] # Skip walltime\n    ExpectedData:\n      - [0, 2.30405926122538e+01]\n      - [1, 0.]\n    AbsoluteTolerance: 1e-12\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &solution\n  ProductOfSinusoids:\n    WaveNumbers: [1, 1]\n\nInitialGuess:\n  Zero:\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Rectangle:\n    LowerBound: [-1.570796326794896, 0.]\n    UpperBound: [3.141592653589793, 1.570796326794896]\n    Distribution: [Linear, Linear]\n    InitialRefinement: [1, 0]\n    InitialGridPoints: [4, 3]\n    TimeDependence: None\n    BoundaryConditions:\n      - AnalyticSolution:\n          Solution: *solution\n          Field: Dirichlet\n      - AnalyticSolution:\n          Solution: *solution\n          Field: Dirichlet\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: Gauss\n    Formulation: StrongInertial\n\nObservers:\n  VolumeFileName: \"PoissonProductOfSinusoids2DVolume\"\n  ReductionFileName: \"PoissonProductOfSinusoids2DReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 1\n      RelativeResidual: 1.e-10\n      AbsoluteResidual: 1.e-10\n    Verbosity: Verbose\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: False\n    Verbosity: Verbose\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Quiet\n    SubdomainSolver:\n      ExplicitInverse:\n        WriteMatrixToFile: \"SubdomainMatrix\"\n    ObservePerCoreReductions: False\n    OutputVolumeData: True\n\nRadiallyCompressedCoordinates: None\n\nEventsAndTriggersAtIterations:\n  - Trigger: HasConverged\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(Field)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve: [Field]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nAmr:\n  Verbosity: Quiet\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nBuildMatrix:\n  MatrixSubfileName: \"FullMatrix\"\n  Verbosity: Verbose\n  EnableDirectSolve: True\n"
  },
  {
    "path": "tests/InputFiles/Poisson/ProductOfSinusoids3D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolvePoisson3D\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 10\n  Priority: High\nExpectedOutput:\n  - PoissonProductOfSinusoids3DReductions.h5\n  - PoissonProductOfSinusoids3DVolume0.h5\nOutputFileChecks:\n  - Label: Discretization error\n    Subfile: ErrorNorms.dat\n    FileGlob: PoissonProductOfSinusoids3DReductions.h5\n    ExpectedData:\n      - [1, 120, 2.32547075102249e+01, 4.28138911064402e-03]\n    AbsoluteTolerance: 1e-12\n  - Label: Linear solver convergence\n    Subfile: GmresResiduals.dat\n    FileGlob: PoissonProductOfSinusoids3DReductions.h5\n    SkipColumns: [1] # Skip walltime\n    ExpectedData:\n      - [0, 1.56746105639827e+02]\n      - [1, 1.12740301193524e-11]\n    AbsoluteTolerance: 1e-12\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &solution\n  ProductOfSinusoids:\n    WaveNumbers: [1, 1, 1]\n\nInitialGuess:\n  Zero:\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Brick:\n    LowerBound: [-1.570796326794896, 0., 0.]\n    UpperBound: [3.141592653589793, 1.570796326794896, 3.141592653589793]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [1, 0, 0]\n    InitialGridPoints: [4, 3, 5]\n    TimeDependence: None\n    BoundaryConditions:\n      - AnalyticSolution:\n          Solution: *solution\n          Field: Dirichlet\n      - AnalyticSolution:\n          Solution: *solution\n          Field: Dirichlet\n      - AnalyticSolution:\n          Solution: *solution\n          Field: Dirichlet\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: False\n    Quadrature: GaussLobatto\n    Formulation: WeakInertial\n\nObservers:\n  VolumeFileName: \"PoissonProductOfSinusoids3DVolume\"\n  ReductionFileName: \"PoissonProductOfSinusoids3DReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 1\n      RelativeResidual: 1.e-6\n      AbsoluteResidual: 1.e-6\n    Verbosity: Verbose\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: True\n    PostSmoothingAtBottom: False\n    Verbosity: Verbose\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Quiet\n    SubdomainSolver:\n      ExplicitInverse:\n        WriteMatrixToFile: None\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates: None\n\nEventsAndTriggersAtIterations:\n  - Trigger: HasConverged\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(Field)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve: [Field]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nAmr:\n  Verbosity: Quiet\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n"
  },
  {
    "path": "tests/InputFiles/PreprocessCceWorldtube/AdmFirstOrderDriverPreprocessCceWorldtube.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nInputH5File: InputFilename.h5\nOutputH5File: ReducedWorldtubeR0292.h5\n# [first_order_input_data_format_example]\nInputDataFormat:\n  AdmMetricNodal:\n    Lapse:\n      Advective: True\n    Shift:\n      Advective: True\n      FirstOrderDriverFactor: 0.75 # Eq. 4.89 B&S\n# [first_order_input_data_format_example]\nExtractionRadius: 292\nFixSpecNormalization: False\nDescendingM: False\nBufferDepth: Auto\nLMaxFactor: 3\n"
  },
  {
    "path": "tests/InputFiles/PreprocessCceWorldtube/AdmSecondOrderDriverPreprocessCceWorldtube.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nInputH5File: InputFilename.h5\nOutputH5File: ReducedWorldtubeR0292.h5\n# [second_order_input_data_format_example]\nInputDataFormat:\n  AdmMetricNodal:\n    Lapse:\n      Advective: True\n    Shift:\n      Advective: True\n      ConformalChristoffelFactor: 1.0 # Eq. 12 Hilditch 2013\n      SecondOrderDriverEta: 2.0 # Eq. 12 Hilditch 2013\n# [second_order_input_data_format_example]\nExtractionRadius: 292\nFixSpecNormalization: False\nDescendingM: False\nBufferDepth: Auto\nLMaxFactor: 3\n"
  },
  {
    "path": "tests/InputFiles/PreprocessCceWorldtube/PreprocessCceWorldtube.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# [preprocess_cce_worldtube_yaml_doxygen_example]\nInputH5File: InputFilename.h5\nOutputH5File: ReducedWorldtubeR0292.h5\nInputDataFormat: MetricModal\nExtractionRadius: 292\nFixSpecNormalization: False\nDescendingM: False\nBufferDepth: Auto\nLMaxFactor: 3\n# [preprocess_cce_worldtube_yaml_doxygen_example]\n"
  },
  {
    "path": "tests/InputFiles/Punctures/MultiplePunctures.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolvePunctures\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 60\n  Priority: High\nExpectedOutput:\n  - PuncturesReductions.h5\n  - PuncturesVolume0.h5\nOutputFileChecks:\n  - Label: Volume integrals\n    Subfile: VolumeIntegrals.dat\n    FileGlob: PuncturesReductions.h5\n    ExpectedData:\n      - [0, 448, 4.18881285829836e+03, 4.55724627651079e-02]\n      - [1, 448, 4.18881285829836e+03, 4.19621052692138e-02]\n      - [2, 1262, 4.18883545625731e+03, 5.07147168589194e-02]\n    AbsoluteTolerance: 1e-8\n  - Label: Nonlinear solver convergence\n    Subfile: NewtonRaphsonResiduals.dat\n    FileGlob: PuncturesReductions.h5\n    SkipColumns: [1] # Skip walltime\n    ExpectedData:\n      # AMR iteration 0\n      - [0, 0, 7.32123992463425e-02, 1.]\n      - [1, 0, 3.51364804783180e-04, 1.]\n      - [2, 0, 6.28169780068392e-09, 1.]\n      # AMR iteration 1\n      - [0, 0, 1.10970183155322e-01, 1.]\n      - [1, 0, 3.36432810360753e-05, 1.]\n      - [2, 0, 1.05116741194963e-09, 1.]\n    AbsoluteTolerance: 1e-6\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground:\n  MultiplePunctures:\n    Punctures:\n      # Offset from center so the puncture doesn't coincide with a grid point\n      - Position: [0.1, 0.1, 0.1]\n        Mass: 1.\n        Momentum: [0., 0., 0.]\n        Spin: [0.1, 0.2, 0.3]\n\nInitialGuess: Flatness\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Sphere:\n    InnerRadius: 2.\n    OuterRadius: 10.\n    Interior:\n      FillWithSphericity: 0.\n    InitialRefinement: 0\n    InitialGridPoints: 4\n    RadialPartitioning: []\n    RadialDistribution: [Linear]\n    UseEquiangularMap: True\n    EquatorialCompression: None\n    WhichWedges: All\n    TimeDependentMaps: None\n    OuterBoundaryCondition: Flatness\n\nAmr:\n  Verbosity: Verbose\n  Criteria:\n    # h-refine (split) elements at the punctures, and p-refine everywhere else\n    - RefineAtPunctures\n    - IncreaseResolution\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: 0\n  Iterations: 2\n\nPhaseChangeAndTriggers:\n  # Run AMR in every iteration, but not on the initial guess\n  - Trigger:\n      EveryNIterations:\n        N: 1\n        Offset: 1\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(UpdateSections)\n      - VisitAndReturn(CheckDomain)\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: StrongInertial\n\nObservers:\n  VolumeFileName: \"PuncturesVolume\"\n  ReductionFileName: \"PuncturesReductions\"\n\nNonlinearSolver:\n  NewtonRaphson:\n    ConvergenceCriteria:\n      MaxIterations: 20\n      RelativeResidual: 0.\n      AbsoluteResidual: 1.e-8\n    SufficientDecrease: 1.e-4\n    MaxGlobalizationSteps: 40\n    DampingFactor: 1.\n    Verbosity: Quiet\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 30\n      RelativeResidual: 1.e-4\n      AbsoluteResidual: 1.e-12\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: 0\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: False\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Silent\n    SubdomainSolver:\n      Gmres:\n        ConvergenceCriteria:\n          MaxIterations: 3\n          RelativeResidual: 1.e-4\n          AbsoluteResidual: 1.e-12\n        Verbosity: Silent\n        Restart: None\n        Preconditioner:\n          MinusLaplacian:\n            Solver:\n              ExplicitInverse:\n                WriteMatrixToFile: None\n            BoundaryConditions: Auto\n    SkipResets: True\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates: None\n\nEventsAndTriggersAtIterations:\n  - Trigger: Always\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - Field\n            - Alpha\n            - Beta\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n      - ObserveNorms:\n          SubfileName: VolumeIntegrals\n          TensorsToObserve:\n            - Name: AdmMassIntegrand\n              NormType: VolumeIntegral\n              Components: Individual\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n  SkipResets: True\n"
  },
  {
    "path": "tests/InputFiles/RadiationTransport/M1Grey/ConstantM1.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveM1Grey\nTesting:\n  Timeout: 5\n  Check: parse;execute\n  Priority: High\nExpectedOutput:\n  - M1GreyReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.01\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    Rk3Kennedy:\n  Imex:\n    Mode: SemiImplicit\n    SolveTolerance: 1.0e-5\n\nPhaseChangeAndTriggers:\n\nInitialData: &InitialData\n  ConstantM1:\n    MeanVelocity: [0.1, 0.2, 0.15]\n    ComovingEnergyDensity: 1.0\n\nDomainCreator:\n  Brick:\n    LowerBound: [10.5, 0.0, 0.0]\n    UpperBound: [11.5, 1.0, 1.0]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [0, 0, 0]\n    InitialGridPoints: [5, 5, 5]\n    TimeDependence: None\n    BoundaryConditions:\n      - DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n      - DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n      - DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Rusanov:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 3\n          Offset: 5\n    Events:\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(TildeE_ElectronNeutrinos1)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(TildeS_ElectronNeutrinos1)\n              NormType: L2Norm\n              Components: Sum\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [10]\n    Events:\n      - Completion\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"M1GreyVolume\"\n  ReductionFileName: \"M1GreyReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarAdvection/Krivodonova1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarAdvection1D\nTesting:\n  Check: parse;execute\nExpectedOutput:\n  - ScalarAdvectionKrivodonova1DVolume0.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper: Rk3HesthavenSsp\n\nPhaseChangeAndTriggers:\n\nDomainCreator:\n  Interval:\n    LowerBound: [-1.0]\n    UpperBound: [1.0]\n    Distribution: [Linear]\n    InitialRefinement: [2]\n    InitialGridPoints: [5]\n    TimeDependence: None\n    BoundaryConditions: [Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Rusanov:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n    TciOptions:\n      UCutoff: 1.0e-10\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentral\n\nInitialData:\n  Krivodonova:\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [10]\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve: [U, TciStatus]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Float, Float]\n          BlocksToObserve: All\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"ScalarAdvectionKrivodonova1DVolume\"\n  ReductionFileName: \"ScalarAdvectionKrivodonova1DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarAdvection/Kuzmin2D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarAdvection2D\nTesting:\n  Check: parse;execute\n  Priority: High\nExpectedOutput:\n  - ScalarAdvectionKuzmin2DVolume0.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper: Rk3HesthavenSsp\n\nPhaseChangeAndTriggers:\n\nDomainCreator:\n  Rectangle:\n    LowerBound: [0.0, 0.0]\n    UpperBound: [1.0, 1.0]\n    Distribution: [Linear, Linear]\n    InitialRefinement: [2, 2]\n    InitialGridPoints: [5, 5]\n    TimeDependence: None\n    BoundaryConditions: [Periodic, Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Rusanov:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n    TciOptions:\n      UCutoff: 1.0e-10\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentral\n\nInitialData:\n  Kuzmin:\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [10]\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve: [U, TciStatus]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Float, Float]\n          BlocksToObserve: All\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"ScalarAdvectionKuzmin2DVolume\"\n  ReductionFileName: \"ScalarAdvectionKuzmin2DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarAdvection/Sinusoid1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarAdvection1D\nTesting:\n  Check: parse;execute\nExpectedOutput:\n  - ScalarAdvectionSinusoid1DVolume0.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper: Rk3HesthavenSsp\n\nPhaseChangeAndTriggers:\n\nDomainCreator:\n  Interval:\n    LowerBound: [-1.0]\n    UpperBound: [1.0]\n    Distribution: [Linear]\n    InitialRefinement: [2]\n    InitialGridPoints: [5]\n    TimeDependence: None\n    BoundaryConditions: [Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    Rusanov:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n    Subcell:\n      TroubledCellIndicator:\n        PerssonTci:\n          Exponent: 4.0\n          NumHighestModes: 1\n        RdmpTci:\n          Delta0: 1.0e-7\n          Epsilon: 1.0e-3\n        FdToDgTci:\n          NumberOfStepsBetweenTciCalls: 1\n          MinTciCallsAfterRollback: 1\n          MinimumClearTcis: 1\n        AlwaysUseSubcells: false\n        EnableExtensionDirections: false\n        UseHalo: false\n        OnlyDgBlocksAndGroups: None\n      SubcellToDgReconstructionMethod: DimByDim\n      FiniteDifferenceDerivativeOrder: 2\n      FdInterpolationOrder: 1\n    TciOptions:\n      UCutoff: 1.0e-10\n  SubcellSolver:\n    Reconstructor:\n      MonotonisedCentral\n\nInitialData:\n  Sinusoid:\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [10]\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve: [U, TciStatus]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Float, Float]\n          BlocksToObserve: All\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"ScalarAdvectionSinusoid1DVolume\"\n  ReductionFileName: \"ScalarAdvectionSinusoid1DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarTensor/KerrSchildSphericalHarmonic.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarTensorSingleBlackHole\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 40\nExpectedOutput:\n  - KerrSchildSphericalHarmonicVolume0.h5\n  - KerrSchildSphericalHarmonicReductions.h5\n  - KerrSchildSphericalHarmonicSurfaces.h5\nOutputFileChecks:\n  - Label: \"check_horizon_find\"\n    Subfile: \"/ApparentHorizon.dat\"\n    FileGlob: \"KerrSchildSphericalHarmonicReductions.h5\"\n    AbsoluteTolerance: 1e2\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n  InitialSlabSize: 0.001\n  VariableOrderAlgorithm:\n    GoalOrder: 3\n  LtsStepChoosers:\n    - LimitIncrease:\n        Factor: 2\n    - PreventRapidIncrease\n    - Cfl:\n        SafetyFactor: 0.2\n\nPhaseChangeAndTriggers:\n\nInitialData: &InitialData\n  GeneralizedHarmonic(KerrSphericalHarmonic):\n    Mass: 1.0\n    Spin: [0.0, 0.0, 0.0]\n    Amplitude: 0.001\n    Radius: 8.0\n    Width: 2.0\n    Mode: [0, 0]\n\nDomainCreator:\n  Sphere:\n    InnerRadius: &InnerRadius 1.9\n    OuterRadius: 25.0\n    Interior:\n      ExciseWithBoundaryCondition:\n        DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n          Amplitude: 0.0\n    InitialRefinement: [0, 0, 1]\n    InitialGridPoints: [6, 6, 6]\n    UseEquiangularMap: true\n    EquatorialCompression: None\n    RadialPartitioning: []\n    RadialDistribution: [Logarithmic]\n    WhichWedges: All\n    TimeDependentMaps: None\n    OuterBoundaryCondition:\n      DirichletAnalytic:\n        AnalyticPrescription: *InitialData\n        Amplitude: 0.0\n\nEvolutionSystem:\n  GeneralizedHarmonic:\n    GaugeCondition:\n      AnalyticChristoffel:\n        AnalyticPrescription: *InitialData\n    # The parameter choices here come from our experience with the Spectral\n    # Einstein Code (SpEC). They should be suitable for evolutions of a\n    # perturbation of a Kerr-Schild black hole.\n    DampingFunctionGamma0:\n      GaussianPlusConstant:\n        Constant: 0.001\n        Amplitude: 3.0\n        Width: 11.313708499\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: -1.0\n        Amplitude: 0.0\n        Width: 11.313708499\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      GaussianPlusConstant:\n        Constant: 0.001\n        Amplitude: 1.0\n        Width: 11.313708499\n        Center: [0.0, 0.0, 0.0]\n  ScalarTensor:\n    ScalarMass: 0.0\n    # Ramp up effectively disabled\n    RampUpStart: -2.0\n    RampUpDuration: 1.0\n    CouplingParameters:\n      Linear: 0.0\n      Quadratic: 0.0\n      Quartic: 0.0\n    DampingFunctionGamma1:\n      GaussianPlusConstant:\n        Constant: 0.0\n        Amplitude: 0.0\n        Width: 11.313708499\n        Center: [0.0, 0.0, 0.0]\n    DampingFunctionGamma2:\n      GaussianPlusConstant:\n        Constant: 0.001\n        Amplitude: 1.0\n        Width: 11.313708499\n        Center: [0.0, 0.0, 0.0]\n\nFiltering:\n  ExpFilter0:\n    Alpha: 36.0\n    HalfPower: 64\n    Enable: true\n    BlocksToFilter: All\n  ExpFilter1:\n    Alpha: 36.0\n    HalfPower: 64\n    Enable: true\n    BlocksToFilter: All\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    ProductAveragedUpwindPenaltyGHAndUpwindPenaltyScalar:\n      AveragedUpwindPenaltyGH:\n      UpwindPenaltyScalar:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      TimeCompares:\n        Comparison: GreaterThan\n        Value: 0.1\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [2]\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    Events:\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n          - Name: Lapse\n            NormType: L2Norm\n            Components: Individual\n          - Name: PointwiseL2Norm(GaugeConstraint)\n            NormType: L2Norm\n            Components: Sum\n          - Name: PointwiseL2Norm(ThreeIndexConstraint)\n            NormType: L2Norm\n            Components: Sum\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 5\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - Csw(Pi)\n            - Lapse\n            - PointwiseL2Norm(GaugeConstraint)\n            - PointwiseL2Norm(TwoIndexConstraint)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Float\n          FloatingPointTypes: [Float]\n          BlocksToObserve: All\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 2\n    Events:\n      - ApparentHorizon\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 4\n          Offset: 0\n    Events:\n      - ExcisionBoundaryA\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    Events:\n      - SphericalSurface\n\nEventsAndTriggersAtSteps:\n\nEventsAndDenseTriggers:\n\nEventsRunAtCleanup:\n  ObservationValue: -1000.0\n  Events:\n    - ObserveTimeStepVolume:\n        SubfileName: FailureTimeStep\n        FloatingPointType: Double\n        CoordinatesFloatingPointType: Float\n\nObservers:\n  VolumeFileName: \"KerrSchildSphericalHarmonicVolume\"\n  ReductionFileName: \"KerrSchildSphericalHarmonicReductions\"\n  SurfaceFileName: \"KerrSchildSphericalHarmonicSurfaces\"\n\nInterpolator:\n  Verbosity: Silent\n\nApparentHorizons:\n  LMax: &LMax 4\n  ApparentHorizon: &Ah\n    Criteria:\n    InitialGuess:\n      LMax: *LMax\n      Radius: 2.2\n      Center: [0., 0., 0.]\n    FastFlow:\n      Flow: Fast\n      Alpha: 1.0\n      Beta: 0.5\n      AbsTol: 1e-12\n      TruncationTol: 1e-5\n      DivergenceTol: 5\n      DivergenceIter: 5\n      MaxIts: 100\n    Verbosity: Quiet\n    MaxComputeCoordsRetries: 3\n    BlocksForHorizonFind: [\"Shell0\"]\n  ControlSystemSingleAh: *Ah\n  ControlSystemCharSpeedAh: *Ah\n\nInterpolationTargets:\n  ExcisionBoundaryA: &ExBdry\n    LMax: *LMax\n    Center: [0.0, 0.0, 0.0]\n    Radius: *InnerRadius\n    AngularOrdering: \"Strahlkorper\"\n  BondiSachsInterpolation:\n    LMax: 16\n    Radius: [100, 150, 200]\n    Center: [0, 0, 0]\n    AngularOrdering: Cce\n  SphericalSurface:\n    LMax: 10\n    Center: [0., 0., 0.]\n    Radius: 12.0\n    AngularOrdering: Strahlkorper\n  ControlSystemCharSpeedExcision: *ExBdry\n\nCce:\n  BondiSachsOutputFilePrefix: \"BondiSachs\"\n\nControlSystems:\n  WriteDataToDisk: false\n  MeasurementsPerUpdate: 4\n  DelayUpdate: true\n  Verbosity: Silent\n  Shape: None\n  Translation: None\n  Size: None\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/InputFiles/ScalarWave/PlaneWave1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarWave1D\nTesting:\n  Check: parse;execute\n  Priority: High\nExpectedOutput:\n  - ScalarWavePlaneWave1DReductions.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nInitialData: &InitialData\n  PlaneWave:\n    WaveVector: [1.0]\n    Center: [0.0]\n    Profile:\n      Sinusoid:\n        Amplitude: 1.0\n        Wavenumber: 1.0\n        Phase: 0.0\n\nAmr:\n  Criteria:\n    - DriveToTargetRefinementLevels:\n        Target: [4]\n        OscillationAtTarget: [Split]\n    - TruncationError:\n        VariablesToMonitor: [Psi]\n        AbsoluteTarget: 1.e-6\n        RelativeTarget: 1.e-6\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 2\n          Offset: 0\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(CheckDomain)\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 3\n          Offset: 0\n    PhaseChanges:\n      - VisitAndReturn(WriteCheckpoint)\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\nDomainCreator:\n  RotatedIntervals:\n    LowerBound: [0.0]\n    Midpoint: [3.14159]\n    UpperBound: [6.283185307179586]\n    InitialRefinement: [1]\n    InitialGridPoints: [[7, 3]]\n    TimeDependence: None\n    BoundaryConditions:\n      LowerBoundary:\n        DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n      UpperBoundary:\n        DirichletAnalytic:\n          AnalyticPrescription: *InitialData\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\n# If filtering is enabled in the executable the filter can be controlled using:\n# Filtering:\n#   ExpFilter0:\n#     Alpha: 36\n#     HalfPower: 128\n#     Enable: false\n#     BlocksToFilter: All\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - RefineMesh\n      - ObserveAmrStats:\n          PrintToTerminal: True\n          ObservePerCore: False\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [5]\n    Events:\n      - Completion\n\nEventsAndTriggersAtCheckpoints:\n  - Trigger: Always\n    Events:\n      - ObserveFields:\n          SubfileName: CheckpointVolumeData\n          VariablesToObserve: [\"Psi\", \"Pi\", \"Phi\"]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"ScalarWavePlaneWave1DVolume\"\n  ReductionFileName: \"ScalarWavePlaneWave1DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarWave/PlaneWave1DEventsAndTriggersExample.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarWave1D\nTesting:\n  Check: parse\n  Priority: High\nExpectedOutput:\n  - ScalarWavePlaneWave1DEventsAndTriggersExampleVolume.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nInitialData:\n  PlaneWave:\n    WaveVector: [1.0]\n    Center: [0.0]\n    Profile:\n      Sinusoid:\n        Amplitude: 1.0\n        Wavenumber: 1.0\n        Phase: 0.0\n\nAmr:\n  Criteria:\n    - DriveToTargetRefinementLevels:\n        Target: [2]\n        OscillationAtTarget: [Split]\n    - TruncationError:\n        VariablesToMonitor: [Psi]\n        AbsoluteTarget: 1.e-6\n        RelativeTarget: 1.e-6\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(CheckDomain)\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\nDomainCreator:\n  Interval:\n    LowerBound: [0.0]\n    UpperBound: [6.283185307179586]\n    Distribution: [Linear]\n    InitialRefinement: [0]\n    InitialGridPoints: [2]\n    TimeDependence: None\n    BoundaryConditions: [Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\nEventsAndDenseTriggers:\n\nEventsAndTriggersAtCheckpoints:\n\n# [multiple_events]\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: Fields\n          VariablesToObserve: [Psi]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(Psi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n  - Trigger: Always\n    Events:\n      - Completion\n# [multiple_events]\n\nObservers:\n  VolumeFileName: \"ScalarWavePlaneWave1DEventsAndTriggersExampleVolume\"\n  ReductionFileName: \"ScalarWavePlaneWave1DEventsAndTriggersExampleReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarWave/PlaneWave1DObserveExample.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarWave1D\nTesting:\n  Check: parse\n  Priority: High\nExpectedOutput:\n  - ScalarWavePlaneWave1DObserveExampleVolume.h5\n  - ScalarWavePlaneWave1DObserveExampleReductions.h5\n  - ScalarWavePlaneWave1DObserveExampleReductions0.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nInitialData:\n  PlaneWave:\n    WaveVector: [1.0]\n    Center: [0.0]\n    Profile:\n      Sinusoid:\n        Amplitude: 1.0\n        Wavenumber: 1.0\n        Phase: 0.0\n\nAmr:\n  Criteria:\n    - DriveToTargetRefinementLevels:\n        Target: [2]\n        OscillationAtTarget: [Split]\n    - TruncationError:\n        VariablesToMonitor: [Psi]\n        AbsoluteTarget: 1.e-6\n        RelativeTarget: 1.e-6\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 10\n          Offset: 0\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(CheckDomain)\n\nEvolution:\n  InitialTime: &InitialTime\n    0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\nDomainCreator:\n  Interval:\n    LowerBound: [0.0]\n    UpperBound: [6.283185307179586]\n    Distribution: [Linear]\n    InitialRefinement: [2]\n    InitialGridPoints: [7]\n    TimeDependence:\n      UniformTranslation:\n        InitialTime: *InitialTime\n        Velocity: [0.5]\n    BoundaryConditions: [Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\n# If filtering is enabled in the executable the filter can be controlled using:\n# Filtering:\n#   ExpFilter0:\n#     Alpha: 36\n#     HalfPower: 128\n#     Enable: false\n#     BlocksToFilter: All\n\n# [observe_event_trigger]\nEventsAndDenseTriggers:\n  - Trigger:\n      Times:\n        Specified:\n          Values: [0.0, 1.0]\n    Events:\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(Psi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 1\n          Offset: 0\n    Events:\n      - RefineMesh\n      - ObserveAmrStats:\n          PrintToTerminal: True\n          ObservePerCore: True\n  - Trigger:\n      Slabs:\n        Specified:\n          Values: [100]\n    Events:\n      - Completion\n  - Trigger:\n      Slabs:\n        EvenlySpaced:\n          Interval: 50\n          Offset: 0\n    Events:\n      - ObserveFields:\n          SubfileName: VolumePsiPiPhiEvery50Slabs\n          VariablesToObserve: [\"Psi\", \"Pi\", \"Phi\"]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double, Float, Float]\n          BlocksToObserve: All\n# [observe_event_trigger]\n\nObservers:\n  VolumeFileName: \"ScalarWavePlaneWave1DObserveExampleVolume\"\n  ReductionFileName: \"ScalarWavePlaneWave1DObserveExampleReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarWave/PlaneWave2D.overlay_0000.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n\n# CheckpointAndExitAfterWallclock must still be in\n# PhaseChangeAndTriggers so that the restart logic will run.\nPhaseChangeAndTriggers:\n  - Trigger:\n      SlabCompares:\n        Comparison: GreaterThan\n        Value: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: None\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      SlabCompares:\n        Comparison: GreaterThanOrEqualTo\n        Value: 3\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorsFinal\n          TensorsToObserve:\n            - Name: Error(Psi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveFields:\n          SubfileName: FieldsFinal\n          VariablesToObserve: [Psi]\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n      - Completion\n"
  },
  {
    "path": "tests/InputFiles/ScalarWave/PlaneWave2D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarWave2D\nTesting:\n  Check: parse;execute\n  Timeout: 20\nExpectedExitCode: 2 0\nExpectedOutput:\n  - Checkpoints/Checkpoint_0000\n  - ScalarWavePlaneWave2DReductions.h5\n  - ScalarWavePlaneWave2DVolume0.h5\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nInitialData:\n  PlaneWave:\n    WaveVector: [1.0, 1.0]\n    Center: [0.0, 0.0]\n    Profile:\n      Sinusoid:\n        Amplitude: 1.0\n        Wavenumber: 1.0\n        Phase: 0.0\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n\nPhaseChangeAndTriggers:\n  - Trigger:\n      SlabCompares:\n        Comparison: GreaterThan\n        Value: 0\n    PhaseChanges:\n      - CheckpointAndExitAfterWallclock:\n          WallclockHours: 0.0\n\nEvolution:\n  InitialTime: &initial_time 0.0\n  # Test backwards evolution in an integration test\n  InitialTimeStep: -0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\n\nDomainCreator:\n  Rectangle:\n    LowerBound: [0.0, 0.0]\n    UpperBound: [6.283185307179586, 6.283185307179586]\n    Distribution: [Linear, Linear]\n    InitialRefinement: [2, 2]\n    InitialGridPoints: [4, 4]\n    TimeDependence: None\n    BoundaryConditions: [Periodic, Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\n# Filtering is being tested by the 2D executable (see EvolveScalarWave.hpp)\nFiltering:\n  ExpFilter0:\n    Alpha: 36\n    HalfPower: 128\n    Enable: true\n    BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  # Added on restart by option overlay\n  # - Trigger:\n  #     SlabCompares:\n  #       Comparison: GreaterThanOrEqualTo\n  #       Value: 3\n  #   Events:\n  #     - ObserveNorms:\n  #         SubfileName: ErrorsFinal\n  #         TensorsToObserve:\n  #           - Name: Error(Psi)\n  #             NormType: L2Norm\n  #             Components: Sum\n  #           - Name: Error(Phi)\n  #             NormType: L2Norm\n  #             Components: Sum\n  #           - Name: Error(Pi)\n  #             NormType: L2Norm\n  #             Components: Sum\n  #     - ObserveFields:\n  #         SubfileName: FieldsFinal\n  #         VariablesToObserve: [Psi]\n  #         InterpolateToMesh: None\n  #         CoordinatesFloatingPointType: Double\n  #         FloatingPointTypes: [Double]\n  #         BlocksToObserve: All\n  #     - Completion\n\nEventsAndDenseTriggers:\n  # Test LTS dense output at the initial time\n  - Trigger:\n      Times:\n        Specified:\n          Values: [*initial_time]\n    Events:\n      - ObserveNorms:\n          SubfileName: Errors\n          TensorsToObserve:\n            - Name: Error(Psi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Phi)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(Pi)\n              NormType: L2Norm\n              Components: Sum\n\nObservers:\n  VolumeFileName: \"ScalarWavePlaneWave2DVolume\"\n  ReductionFileName: \"ScalarWavePlaneWave2DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarWave/PlaneWave3D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarWave3D\nTesting:\n  Check: parse;execute\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nInitialData:\n  PlaneWave:\n    WaveVector: [1.0, 1.0, 1.0]\n    Center: [0.0, 0.0, 0.0]\n    Profile:\n      Sinusoid:\n        Amplitude: 1.0\n        Wavenumber: 1.0\n        Phase: 0.0\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n\nPhaseChangeAndTriggers:\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\nDomainCreator:\n  Brick:\n    LowerBound: [0.0, 0.0, 0.0]\n    UpperBound: [6.283185307179586, 6.283185307179586, 6.283185307179586]\n    Distribution: [Linear, Linear, Linear]\n    InitialRefinement: [1, 1, 1]\n    InitialGridPoints: [5, 5, 5]\n    TimeDependence: None\n    BoundaryConditions: [Periodic, Periodic, Periodic]\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\n# If filtering is enabled in the executable the filter can be controlled using:\n# Filtering:\n#   ExpFilter0:\n#     Alpha: 36\n#     HalfPower: 128\n#     Enable: false\n#     BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Times:\n        Specified:\n          Values: [&final_time 0.05]\n    Events:\n      - Completion\n  - Trigger: Always\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 0\n          StepChoosers:\n            - StepToTimes:\n                Times:\n                  Specified:\n                    Values: [*final_time]\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"ScalarWavePlaneWave3DVolume\"\n  ReductionFileName: \"ScalarWavePlaneWave3DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/ScalarWave/PlaneWave3DCartoonSphere.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: EvolveScalarWave3D\nTesting:\n  Check: parse;execute\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nInitialData: &InitialData\n  RegularSphericalWave:\n    Profile:\n      Gaussian:\n        Amplitude: 2.0\n        Width: 1.0\n        Center: 0.0\n\nAmr:\n  Criteria:\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      RefinementLevel: Auto\n      NumGridPoints: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  Verbosity: Verbose\n\nPhaseChangeAndTriggers:\n\nEvolution:\n  InitialTime: 0.0\n  InitialTimeStep: 0.001\n  MinimumTimeStep: 1e-7\n  TimeStepper:\n    AdamsBashforth:\n      Order: 3\n\nDomainCreator:\n  CartoonSphere1D:\n    InnerRadius: 0.0\n    OuterRadius: 20.0\n    InitialRadialRefinement: [3, 2]\n    InitialNumberOfRadialGridPoints: 5\n    RadialPartitioning: [10.0]\n    RadialDistributions: [Linear, Logarithmic]\n    TimeDependence: None\n    InnerBoundaryCondition:\n      DirichletAnalytic:\n        AnalyticPrescription: *InitialData\n    OuterBoundaryCondition:\n      DirichletAnalytic:\n        AnalyticPrescription: *InitialData\n\nSpatialDiscretization:\n  BoundaryCorrection:\n    UpwindPenalty:\n  DiscontinuousGalerkin:\n    Formulation: StrongInertial\n    Quadrature: GaussLobatto\n\n# If filtering is enabled in the executable the filter can be controlled using:\n# Filtering:\n#   ExpFilter0:\n#     Alpha: 36\n#     HalfPower: 128\n#     Enable: false\n#     BlocksToFilter: All\n\nEventsAndTriggersAtCheckpoints:\n\nEventsAndTriggersAtSlabs:\n  - Trigger:\n      Times:\n        Specified:\n          Values: [&final_time 0.05]\n    Events:\n      - Completion\n  - Trigger: Always\n    Events:\n      - ChangeSlabSize:\n          DelayChange: 0\n          StepChoosers:\n            - StepToTimes:\n                Times:\n                  Specified:\n                    Values: [*final_time]\n\nEventsAndDenseTriggers:\n\nObservers:\n  VolumeFileName: \"CartoonSphereGaussianWave3DVolume\"\n  ReductionFileName: \"CartoonSphereGaussianWave3DReductions\"\n"
  },
  {
    "path": "tests/InputFiles/SelfForce/GrCircularOrbit.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveGrSelfForce\nTesting:\n  Check: parse\nExpectedOutput:\n  - GrSelfForceReductions.h5\n  - GrSelfForceVolume0.h5\n\n---\n\nBackground: &background\n  CircularOrbit:\n    BlackHoleMass: &bh_mass 1.\n    BlackHoleSpin: &bh_spin 0.5\n    OrbitalRadius: &orbital_radius 10.\n    MModeNumber: &m_mode_number 1\n\nInitialGuess: *background\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  AlignedLattice:\n    # Order of dimensions is (r_*, theta)\n    BlockBounds:\n      # Puncture position is at r_* = 12.775940471833115\n      - [-50., 0., 25.55188094366623, 350.] # r_*\n      # theta\n      - [0., 1., 2.1415926536, 3.1415926536]\n    InitialGridPoints: [4, 4]\n    InitialLevels: [0, 0]\n    RefinedGridPoints: []\n    BlocksToExclude: []\n    RefinedLevels:\n      # bottom row\n      - LowerCornerIndex: [0, 0] # bottom left\n        UpperCornerIndex: [1, 1]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [1, 0] # bottom middle\n        UpperCornerIndex: [2, 1]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [2, 0] # bottom right\n        UpperCornerIndex: [3, 1]\n        Refinement: [1, 0]\n      # middle row\n      - LowerCornerIndex: [0, 1] # middle left\n        UpperCornerIndex: [1, 2]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [1, 1] # source region\n        UpperCornerIndex: [2, 2]\n        Refinement: [1, 1]\n      - LowerCornerIndex: [2, 1] # middle right\n        UpperCornerIndex: [3, 2]\n        Refinement: [1, 0]\n      # top row\n      - LowerCornerIndex: [0, 2] # top left\n        UpperCornerIndex: [1, 3]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [1, 2] # top middle\n        UpperCornerIndex: [2, 3]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [2, 2] # top right\n        UpperCornerIndex: [3, 3]\n        Refinement: [1, 0]\n    BoundaryConditions:\n      # Radial boundary conditions\n      - Sommerfeld:\n          BlackHoleMass: *bh_mass\n          BlackHoleSpin: *bh_spin\n          OrbitalRadius: *orbital_radius\n          MModeNumber: *m_mode_number\n      # Angular boundary conditions\n      - Angular:\n          MModeNumber: *m_mode_number\n\nAmr:\n  Verbosity: Debug\n  Criteria:\n    # - RefineAtPuncture\n    - IncreaseResolution\n  EnableInBlocks: All\n  Policies:\n    Isotropy: Anisotropic\n    EnforceTwoToOneBalanceInNormalDirection: True\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: True\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 2\n\nPhaseChangeAndTriggers:\n  # Run AMR in every iteration, but not on the initial guess\n  - Trigger:\n      EveryNIterations:\n        N: 1\n        Offset: 1\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(UpdateSections)\n      - VisitAndReturn(CheckDomain)\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.5\n    Massive: True\n    Quadrature: Gauss\n    Formulation: StrongInertial\n\nObservers:\n  VolumeFileName: \"GrSelfForceVolume\"\n  ReductionFileName: \"GrSelfForceReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 100\n      RelativeResidual: 0\n      AbsoluteResidual: 1.e-12\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: 0\n    PreSmoothing: True\n    UseBottomSolver: True\n    PostSmoothingAtBottom: False\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 2\n    MaxOverlap: Auto\n    Verbosity: Silent\n    SubdomainSolver:\n      ExplicitInverse:\n        WriteMatrixToFile: None\n        ComplexShift: 0.0\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nEventsAndTriggersAtIterations:\n  - Trigger: Always\n    Events:\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - MMode\n            - BoyerLindquistRadius\n          BlocksToObserve: All\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Silent\n  EnableDirectSolve: True\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: False\n  Singletons: Auto\n"
  },
  {
    "path": "tests/InputFiles/SelfForce/ScalarCircularOrbit.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveScalarSelfForce\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 10\nExpectedOutput:\n  - ScalarSelfForceReductions.h5\n  - ScalarSelfForceVolume0.h5\nOutputFileChecks:\n  - Label: SelfForce\n    Subfile: SelfForce.dat\n    FileGlob: ScalarSelfForceReductions.h5\n    ExpectedData:\n      # Iteration | NumPoints | NumContributingElements\n      # | Re(SelfForce_r) | Im(SelfForce_r)\n      # | Re(SelfForce_theta) | Im(SelfForce_theta)\n      - [0, 240, 4, 0., 0., 0., 0.]\n      - [1, 240, 4, -1.365e-4, 9.138e-5, 0., 0.]\n      - [2, 663, 4, -1.365e-4, 9.138e-5, 0., 0.]\n    AbsoluteTolerance: 1.5e-4\n    RelativeTolerance: 0.\n\n---\n\nBackground: &background\n  CircularOrbit:\n    BlackHoleMass: &bh_mass 1.\n    BlackHoleSpin: &bh_spin 0.5\n    OrbitalRadius: &orbital_radius 10.\n    MModeNumber: &m_mode_number 1\n    HyperboloidalSlicingTransitions: [-25., 0., 25.55188094366623, 50.]\n    ImposeEquatorialSymmetry: False\n\nInitialGuess: *background\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  AlignedLattice:\n    # Order of dimensions is (r_*, cos(theta))\n    BlockBounds:\n      # Puncture position is at r_* = 12.775940471833115\n      - [-50., 0., 25.55188094366623, 350.] # r_*\n      - [-1., -0.5, 0.5, 1.]  # cos(theta)\n    InitialGridPoints: [4, 4]\n    InitialLevels: [0, 0]\n    RefinedGridPoints: []\n    BlocksToExclude: []\n    RefinedLevels:\n      # bottom row\n      - LowerCornerIndex: [0, 0] # bottom left\n        UpperCornerIndex: [1, 1]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [1, 0] # bottom middle\n        UpperCornerIndex: [2, 1]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [2, 0] # bottom right\n        UpperCornerIndex: [3, 1]\n        Refinement: [1, 0]\n      # middle row\n      - LowerCornerIndex: [0, 1] # middle left\n        UpperCornerIndex: [1, 2]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [1, 1] # source region\n        UpperCornerIndex: [2, 2]\n        Refinement: [1, 1]\n      - LowerCornerIndex: [2, 1] # middle right\n        UpperCornerIndex: [3, 2]\n        Refinement: [1, 0]\n      # top row\n      - LowerCornerIndex: [0, 2] # top left\n        UpperCornerIndex: [1, 3]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [1, 2] # top middle\n        UpperCornerIndex: [2, 3]\n        Refinement: [0, 0]\n      - LowerCornerIndex: [2, 2] # top right\n        UpperCornerIndex: [3, 3]\n        Refinement: [1, 0]\n    BoundaryConditions:\n      # Radial boundary conditions\n      - Lower:\n          Sommerfeld:\n            BlackHoleMass: *bh_mass\n            BlackHoleSpin: *bh_spin\n            OrbitalRadius: *orbital_radius\n            MModeNumber: *m_mode_number\n            HyperboloidalSlicing: True\n            Order: 1\n        Upper:\n          Sommerfeld:\n            BlackHoleMass: *bh_mass\n            BlackHoleSpin: *bh_spin\n            OrbitalRadius: *orbital_radius\n            MModeNumber: *m_mode_number\n            HyperboloidalSlicing: True\n            Order: 2\n      # Angular boundary conditions\n      - None\n\nAmr:\n  Verbosity: Quiet\n  Criteria:\n    - RefineAtPuncture\n    - IncreaseResolution\n  EnableInBlocks: All\n  Policies:\n    Isotropy: Anisotropic\n    EnforceTwoToOneBalanceInNormalDirection: True\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: True\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 2\n\nPhaseChangeAndTriggers:\n  # Run AMR in every iteration, but not on the initial guess\n  - Trigger:\n      EveryNIterations:\n        N: 1\n        Offset: 1\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(UpdateSections)\n      - VisitAndReturn(CheckDomain)\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.5\n    Massive: True\n    Quadrature: Gauss\n    Formulation: StrongInertial\n\nObservers:\n  VolumeFileName: \"ScalarSelfForceVolume\"\n  ReductionFileName: \"ScalarSelfForceReductions\"\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 100\n      RelativeResidual: 0\n      AbsoluteResidual: 1.e-12\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: 0\n    PreSmoothing: True\n    UseBottomSolver: True\n    PostSmoothingAtBottom: False\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 2\n    MaxOverlap: Auto\n    Verbosity: Silent\n    SubdomainSolver:\n      ExplicitInverse:\n        WriteMatrixToFile: None\n        ComplexShift: 0.0\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nEventsAndTriggersAtIterations:\n  - Trigger: Always\n    Events:\n      - ObserveSelfForce\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - MMode\n            - BoyerLindquistRadius\n          BlocksToObserve: All\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Silent\n  EnableDirectSolve: True\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: False\n  Singletons: Auto\n"
  },
  {
    "path": "tests/InputFiles/Xcts/BinaryBlackHole.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveXcts\nTesting:\n  Check: parse\n  Timeout: 30\n  Priority: High\nExpectedOutput:\n  - BbhReductions.h5\n  - BbhVolume0.h5\nOutputFileChecks:\n  - Label: Norms\n    Subfile: Norms.dat\n    FileGlob: BbhReductions.h5\n    ExpectedData:\n      - [\n          5,\n          20304,\n          1.74532762048460e+33,\n          1.10996259399346e+00,\n          8.68051629831159e-01,\n          6.28127905852040e-01,\n          4.94959532218436e-03,\n          1.25936870550045e-03,\n          1.29835755854743e-03,\n          1.26478484974052e-03,\n        ]\n      - [\n          5,\n          20304,\n          1.74532762048460e+33,\n          1.10996259399346e+00,\n          8.68051629831159e-01,\n          6.28127905852040e-01,\n          4.94959532218436e-03,\n          1.25936870550045e-03,\n          1.29835755854743e-03,\n          1.26478484974052e-03,\n        ]\n      - [\n          5,\n          20304,\n          1.74532762048460e+33,\n          1.10996259399346e+00,\n          8.68051629831159e-01,\n          6.28127905852040e-01,\n          4.94959532218436e-03,\n          1.25936870550045e-03,\n          1.29835755854743e-03,\n          1.26478484974052e-03,\n        ]\n    AbsoluteTolerance: 0.\n    RelativeTolerance: 1e-9\n  - Label: Nonlinear solver convergence\n    Subfile: NewtonRaphsonResiduals.dat\n    FileGlob: BbhReductions.h5\n    SkipColumns: [1] # Skip walltime\n    ExpectedData:\n      # AMR iteration 0\n      - [0, 0, 9.58836468792872e+00, 1.]\n      - [1, 0, 2.56744819082553e+00, 1.]\n      - [2, 0, 6.92792186565806e-03, 1.]\n      - [3, 0, 1.21895158596328e-03, 1.]\n      - [4, 0, 7.97507362468631e-07, 1.]\n      - [5, 0, 7.12541536160998e-10, 1.]\n      # AMR iteration 1\n\n    AbsoluteTolerance: 1e-6\n\n---\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &background\n  Binary:\n    XCoords: [&x_left -8., &x_right 8.]\n    CenterOfMassOffset: [&y_offset 0., &z_offset 0.]\n    ObjectLeft: &kerr_left\n      KerrSchild:\n        Mass: 0.4229\n        Spin: [0., 0., 0.]\n        Center: [0., 0., 0.]\n        Velocity: [0., 0., 0.]\n    ObjectRight: &kerr_right\n      KerrSchild:\n        Mass: 0.4229\n        Spin: [0., 0., 0.]\n        Center: [0., 0., 0.]\n        Velocity: [0., 0., 0.]\n    AngularVelocity: 0.0144\n    Expansion: 0.\n    LinearVelocity: [0., 0., 0.]\n    FalloffWidths: [4.8, 4.8]\n\nInitialGuess: *background\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  BinaryCompactObject:\n    ObjectA:\n      InnerRadius: 0.8\n      OuterRadius: 4.\n      XCoord: *x_right\n      Interior:\n        ExciseWithBoundaryCondition:\n          ApparentHorizon:\n            Center: [*x_right, *y_offset, *z_offset]\n            Rotation: [0., 0., 0.]\n            Lapse: *kerr_right\n            NegativeExpansion: *kerr_right\n      UseLogarithmicMap: True\n    ObjectB:\n      InnerRadius: 0.8\n      OuterRadius: 4.\n      XCoord: *x_left\n      Interior:\n        ExciseWithBoundaryCondition:\n          ApparentHorizon:\n            Center: [*x_left, *y_offset, *z_offset]\n            Rotation: [0., 0., 0.]\n            Lapse: *kerr_left\n            NegativeExpansion: *kerr_left\n      UseLogarithmicMap: True\n    CenterOfMassOffset: [*y_offset, *z_offset]\n    Envelope:\n      Radius: &outer_shell_inner_radius 60.\n      RadialDistribution: Projective\n    OuterShell:\n      Radius: &outer_radius 1.e9\n      RadialPartitioning: []\n      RadialDistribution: &outer_shell_distribution Inverse\n      OpeningAngle: 120.0\n      BoundaryCondition: Flatness\n    UseEquiangularMap: True\n    CubeScale: 1.0\n    InitialRefinement:\n      ObjectAShell:     [0, 0, 0]\n      ObjectBShell:     [0, 0, 0]\n      ObjectACube:      [0, 0, 0]\n      ObjectBCube:      [0, 0, 0]\n      Envelope:         [0, 0, 1]\n      OuterShell0:       [0, 0, 2]\n    # This p-refinement represents a crude manual optimization of the domain. We\n    # will need AMR to optimize the domain further.\n    InitialGridPoints:\n      ObjectAShell:   [6, 6, 10]\n      ObjectBShell:   [6, 6, 10]\n      ObjectACube:    [6, 6, 7]\n      ObjectBCube:    [6, 6, 7]\n      Envelope:       [6, 6, 6]\n      OuterShell0:     [6, 6, 6]\n    TimeDependentMaps: None\n\nAmr:\n  Verbosity: Verbose\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: WeakInertial\n\nObservers:\n  VolumeFileName: \"BbhVolume\"\n  ReductionFileName: \"BbhReductions\"\n\nNonlinearSolver:\n  NewtonRaphson:\n    ConvergenceCriteria:\n      MaxIterations: 20\n      RelativeResidual: 1.e-10\n      AbsoluteResidual: 1.e-11\n    SufficientDecrease: 1.e-4\n    MaxGlobalizationSteps: 40\n    DampingFactor: 1.\n    Verbosity: Verbose\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 100\n      RelativeResidual: 1.e-3\n      AbsoluteResidual: 1.e-10\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: True\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    MaxOverlap: 2\n    Iterations: 3\n    Verbosity: Silent\n    SubdomainSolver:\n      Gmres:\n        ConvergenceCriteria:\n          MaxIterations: 3\n          RelativeResidual: 1.e-4\n          AbsoluteResidual: 1.e-10\n        Verbosity: Silent\n        Restart: None\n        Preconditioner:\n          MinusLaplacian:\n            Solver:\n              ExplicitInverse:\n                WriteMatrixToFile: None\n            BoundaryConditions: Auto\n    SkipResets: True\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates:\n  InnerRadius: *outer_shell_inner_radius\n  OuterRadius: *outer_radius\n  Compression: *outer_shell_distribution\n\nEventsAndTriggersAtIterations:\n  - Trigger: Always\n    Events:\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n            - Name: ConformalFactor\n              NormType: Max\n              Components: Individual\n            - Name: Lapse\n              NormType: Min\n              Components: Individual\n            - Name: Magnitude(ShiftExcess)\n              NormType: Max\n              Components: Individual\n            - Name: HamiltonianConstraint\n              NormType: L2Norm\n              Components: Individual\n            - Name: MomentumConstraint\n              NormType: L2Norm\n              Components: Individual\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - ConformalFactor\n            - Lapse\n            - Shift\n            - ShiftExcess\n            - SpatialMetric\n            - ExtrinsicCurvature\n            - HamiltonianConstraint\n            - MomentumConstraint\n            - RadiallyCompressedCoordinates\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n  SkipResets: True\n"
  },
  {
    "path": "tests/InputFiles/Xcts/HeadOnBns.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveXcts\nTesting:\n  Check: parse\n  Timeout: 30\n  Priority: High\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &background\n  Binary:\n    XCoords: [&x_left -20., &x_right 20.]\n    CenterOfMassOffset: [&y_offset 0., &z_offset 0.]\n    ObjectLeft: &star_left\n      TovStar:\n        # Values taken from Sec. V.C and Fig. 8 in\n        # https://arxiv.org/abs/1708.07358.\n        # This central density corresponds to h = 1.2, or about 5e14 g/cm^3.\n        CentralDensity: 8.087415253997405e-4\n        EquationOfState:\n          PolytropicFluid:\n            PolytropicConstant: 123.6489\n            PolytropicExponent: 2.\n        Coordinates: Isotropic\n    ObjectRight: *star_left\n    AngularVelocity: 0.\n    Expansion: 0.\n    LinearVelocity: [0., 0., 0.]\n    FalloffWidths: None\n\nInitialGuess: *background\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  BinaryCompactObject:\n    ObjectA:\n      InnerRadius: 4.\n      OuterRadius: &star_radius 9.709353324763269\n      XCoord: *x_right\n      Interior: Auto\n      UseLogarithmicMap: False\n    ObjectB:\n      InnerRadius: 4.\n      OuterRadius: *star_radius\n      XCoord: *x_left\n      Interior: Auto\n      UseLogarithmicMap: False\n    CenterOfMassOffset: [*y_offset, *z_offset]\n    Envelope:\n      Radius: &outer_shell_inner_radius 120.\n      RadialDistribution: Projective\n    OuterShell:\n      Radius: &outer_radius 600.\n      RadialPartitioning: []\n      RadialDistribution: &outer_shell_distribution Inverse\n      OpeningAngle: 90.0\n      BoundaryCondition: Flatness\n    UseEquiangularMap: True\n    CubeScale: 1.0\n    # This h-refinement is set up so spherical wedges have equal angular size.\n    # Once the domain supports equatorial compression (or similar) this\n    # h-refinement will simplify considerably.\n    InitialRefinement:\n      ObjectAInterior:            [1, 1, 1]\n      ObjectBInterior:            [1, 1, 1]\n      ObjectAShell:               [1, 1, 1]\n      ObjectBShell:               [1, 1, 1]\n      ObjectACube:                [1, 1, 1]\n      ObjectBCube:                [1, 1, 1]\n      EnvelopeUpperZLeft:         [1, 1, 0]\n      EnvelopeLowerZLeft:         [1, 1, 0]\n      EnvelopeUpperYLeft:         [1, 1, 0]\n      EnvelopeLowerYLeft:         [1, 1, 0]\n      EnvelopeLowerX:             [1, 1, 0]\n      EnvelopeUpperZRight:        [1, 1, 0]\n      EnvelopeLowerZRight:        [1, 1, 0]\n      EnvelopeUpperYRight:        [1, 1, 0]\n      EnvelopeLowerYRight:        [1, 1, 0]\n      EnvelopeUpperX:             [1, 1, 0]\n      OuterShell0UpperZLeft:       [0, 1, 0]\n      OuterShell0LowerZLeft:       [0, 1, 0]\n      OuterShell0UpperYLeft:       [0, 1, 0]\n      OuterShell0LowerYLeft:       [0, 1, 0]\n      OuterShell0LowerX:           [1, 1, 0]\n      OuterShell0UpperZRight:      [0, 1, 0]\n      OuterShell0LowerZRight:      [0, 1, 0]\n      OuterShell0UpperYRight:      [0, 1, 0]\n      OuterShell0LowerYRight:      [0, 1, 0]\n      OuterShell0UpperX:           [1, 1, 0]\n    # This p-refinement represents a crude manual optimization of the domain. We\n    # will need AMR to optimize the domain further.\n    InitialGridPoints:\n      ObjectAInterior: [4, 4, 4]\n      ObjectBInterior: [4, 4, 4]\n      ObjectAShell:    [4, 4, 5]\n      ObjectBShell:    [4, 4, 5]\n      ObjectACube:     [4, 4, 5]\n      ObjectBCube:     [4, 4, 5]\n      Envelope:        [4, 4, 4]\n      OuterShell0:      [4, 4, 3]\n    TimeDependentMaps: None\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: WeakInertial\n\nObservers:\n  VolumeFileName: \"BnsVolume\"\n  ReductionFileName: \"BnsReductions\"\n\nNonlinearSolver:\n  NewtonRaphson:\n    ConvergenceCriteria:\n      MaxIterations: 20\n      RelativeResidual: 1.e-10\n      AbsoluteResidual: 1.e-11\n    SufficientDecrease: 1.e-4\n    MaxGlobalizationSteps: 40\n    DampingFactor: 1.\n    Verbosity: Verbose\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 100\n      RelativeResidual: 1.e-3\n      AbsoluteResidual: 1.e-10\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: True\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    MaxOverlap: 2\n    Iterations: 3\n    Verbosity: Silent\n    SubdomainSolver:\n      Gmres:\n        ConvergenceCriteria:\n          MaxIterations: 3\n          RelativeResidual: 1.e-4\n          AbsoluteResidual: 1.e-10\n        Verbosity: Silent\n        Restart: None\n        Preconditioner:\n          MinusLaplacian:\n            Solver:\n              ExplicitInverse:\n                WriteMatrixToFile: None\n            BoundaryConditions: Auto\n    SkipResets: True\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates:\n  InnerRadius: *outer_shell_inner_radius\n  OuterRadius: *outer_radius\n  Compression: *outer_shell_distribution\n\nEventsAndTriggersAtIterations:\n  - Trigger: HasConverged\n    Events:\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n            - Name: ConformalFactor\n              NormType: Max\n              Components: Individual\n            - Name: Lapse\n              NormType: Min\n              Components: Individual\n            - Name: Magnitude(ShiftExcess)\n              NormType: Max\n              Components: Individual\n            - Name: Conformal(EnergyDensity)\n              NormType: Max\n              Components: Individual\n            - Name: Conformal(StressTrace)\n              NormType: Max\n              Components: Individual\n            - Name: HamiltonianConstraint\n              NormType: L2Norm\n              Components: Individual\n            - Name: MomentumConstraint\n              NormType: L2Norm\n              Components: Individual\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - ConformalFactor\n            - Lapse\n            - Shift\n            - ShiftExcess\n            - SpatialMetric\n            - ExtrinsicCurvature\n            - HamiltonianConstraint\n            - MomentumConstraint\n            - Conformal(EnergyDensity)\n            - Conformal(StressTrace)\n            - RestMassDensity\n            - SpatialVelocity\n            - LorentzFactor\n            - LowerSpatialFourVelocity\n            - MagneticField\n            - RadiallyCompressedCoordinates\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nAmr:\n  Verbosity: Quiet\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n  SkipResets: True\n"
  },
  {
    "path": "tests/InputFiles/Xcts/KerrSchild.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveXcts\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 120\n  Priority: High\nExpectedOutput:\n  - KerrSchildReductions.h5\n  - KerrSchildVolume0.h5\nOutputFileChecks:\n  - Label: Discretization error\n    Subfile: ErrorNorms.dat\n    FileGlob: KerrSchildReductions.h5\n    ExpectedData:\n      - [\n          0,\n          384,\n          4.20277018789526e+03,\n          0.00000000000000e+00,\n          2.16998022741518e-01,\n          3.75478040264121e-01,\n        ]\n      - [\n          1,\n          384,\n          4.20277018789526e+03,\n          1.09488566071970e-03,\n          5.31467285484924e-03,\n          1.17765764810308e-02,\n        ]\n      - [\n          2,\n          750,\n          4.17021935018685e+03,\n          1.00730328853184e-04,\n          4.72781860739432e-04,\n          1.17830554422638e-03,\n        ]\n    AbsoluteTolerance: 1e-10\n  - Label: Nonlinear solver convergence\n    Subfile: NewtonRaphsonResiduals.dat\n    FileGlob: KerrSchildReductions.h5\n    SkipColumns: [1] # Skip walltime\n    ExpectedData:\n      # AMR iteration 0\n      - [0, 0, 6.86317921700308e+01, 1.]\n      - [1, 0, 2.54081356038966e+00, 1.]\n      - [2, 0, 1.06821671508785e-01, 1.]\n      - [3, 0, 5.29758557594090e-04, 1.]\n      - [4, 0, 1.23004570709784e-08, 1.]\n      # AMR iteration 1\n      - [0, 0, 3.22212957410157e+00, 1.]\n      - [1, 0, 2.70099316920737e-02, 1.]\n      - [2, 0, 2.09933729188050e-06, 1.]\n      - [3, 0, 3.13426600315318e-11, 1.]\n    AbsoluteTolerance: 1e-4\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &solution\n  KerrSchild:\n    Mass: &KerrMass 1.\n    Spin: &KerrSpin [0., 0., 0.6]\n    Center: &KerrCenter [0., 0., 0.]\n    Velocity: [0., 0., 0.]\n\nInitialGuess: Flatness\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Sphere:\n    # Boyer-Lindquist radius a bit inside the horizon:\n    # 0.89 * (M + sqrt(M^2 - a^2))\n    InnerRadius: &InnerRadius 1.602\n    OuterRadius: &OuterRadius 10.\n    Interior:\n      ExciseWithBoundaryCondition:\n        AnalyticSolution:\n          Solution: *solution\n          ConformalFactorMinusOne: Dirichlet\n          LapseTimesConformalFactorMinusOne: Dirichlet\n          ShiftExcess: Dirichlet\n    InitialRefinement: 0\n    InitialGridPoints: 4\n    UseEquiangularMap: True\n    EquatorialCompression: None\n    WhichWedges: All\n    RadialPartitioning: []\n    RadialDistribution: [Logarithmic]\n    TimeDependentMaps:\n      Shape:\n        InitialTime: 0.\n        LMax: 18\n        CoefficientTruncationLimit: 0.\n        Mass: *KerrMass\n        Spin: *KerrSpin\n        Center: *KerrCenter\n        InnerRadius: *InnerRadius\n        OuterRadius: *OuterRadius\n    OuterBoundaryCondition:\n      AnalyticSolution:\n        Solution: *solution\n        ConformalFactorMinusOne: Dirichlet\n        LapseTimesConformalFactorMinusOne: Dirichlet\n        ShiftExcess: Dirichlet\n\nAmr:\n  Verbosity: Verbose\n  Criteria:\n    - IncreaseResolution\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 2\n\nPhaseChangeAndTriggers:\n  # Run AMR in every iteration, but not on the initial guess\n  - Trigger:\n      EveryNIterations:\n        N: 1\n        Offset: 1\n    PhaseChanges:\n      - VisitAndReturn(EvaluateAmrCriteria)\n      - VisitAndReturn(AdjustDomain)\n      - VisitAndReturn(UpdateSections)\n      - VisitAndReturn(CheckDomain)\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: WeakInertial\n\nObservers:\n  VolumeFileName: \"KerrSchildVolume\"\n  ReductionFileName: \"KerrSchildReductions\"\n\nNonlinearSolver:\n  NewtonRaphson:\n    ConvergenceCriteria:\n      MaxIterations: 20\n      RelativeResidual: 0.\n      AbsoluteResidual: 1.e-6\n    SufficientDecrease: 1.e-4\n    MaxGlobalizationSteps: 40\n    DampingFactor: 1.\n    Verbosity: Quiet\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 30\n      RelativeResidual: 1.e-4\n      AbsoluteResidual: 1.e-12\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: False\n    Verbosity: Silent\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Silent\n    SubdomainSolver:\n      Gmres:\n        ConvergenceCriteria:\n          MaxIterations: 3\n          RelativeResidual: 1.e-4\n          AbsoluteResidual: 1.e-12\n        Verbosity: Silent\n        Restart: None\n        Preconditioner:\n          MinusLaplacian:\n            Solver:\n              ExplicitInverse:\n                WriteMatrixToFile: None\n            BoundaryConditions: Auto\n    SkipResets: True\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates: None\n\nEventsAndTriggersAtIterations:\n  - Trigger: Always\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(ConformalFactorMinusOne)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(LapseTimesConformalFactorMinusOne)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(ShiftExcess)\n              NormType: L2Norm\n              Components: Sum\n  - Trigger: HasConverged\n    Events:\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n            - Name: HamiltonianConstraint\n              NormType: L2Norm\n              Components: Individual\n            - Name: MomentumConstraint\n              NormType: L2Norm\n              Components: Individual\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - ConformalFactor\n            - Lapse\n            - Shift\n            - SpatialMetric\n            - ExtrinsicCurvature\n            - HamiltonianConstraint\n            - MomentumConstraint\n            - Error(ConformalFactorMinusOne)\n            - Error(LapseTimesConformalFactorMinusOne)\n            - Error(ShiftExcess)\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Double\n          FloatingPointTypes: [Double]\n          BlocksToObserve: All\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n  # Can't skip resets if bottom solver is enabled because we start at flatness\n  SkipResets: False\n"
  },
  {
    "path": "tests/InputFiles/Xcts/TovStar.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nExecutable: SolveXcts\nTesting:\n  Check: parse;execute_check_output\n  Timeout: 60\n  Priority: High\nExpectedOutput:\n  - TovStarReductions.h5\n  - TovStarVolume0.h5\nOutputFileChecks:\n  - Label: Discretization error\n    Subfile: /ErrorNorms.dat\n    FileGlob: TovStarReductions.h5\n    SkipColumns: [0, 1, 2]\n    AbsoluteTolerance: 2.e-6\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nBackground: &solution\n  TovStar:\n    CentralDensity: 1.e-3\n    EquationOfState:\n      PolytropicFluid:\n        PolytropicConstant: 1.\n        PolytropicExponent: 2\n    Coordinates: Isotropic\n\nInitialGuess: Flatness\n\nRandomizeInitialGuess: None\n\nDomainCreator:\n  Sphere:\n    InnerRadius: 0.2\n    OuterRadius: 1.24984447898\n    Interior:\n      FillWithSphericity: 0.\n    InitialRefinement: 0\n    InitialGridPoints: [5, 5, 5]\n    RadialPartitioning: []\n    RadialDistribution: [Linear]\n    UseEquiangularMap: True\n    EquatorialCompression: None\n    WhichWedges: All\n    TimeDependentMaps: None\n    OuterBoundaryCondition:\n      AnalyticSolution:\n        Solution: *solution\n        ConformalFactorMinusOne: Dirichlet\n        LapseTimesConformalFactorMinusOne: Dirichlet\n        ShiftExcess: Dirichlet\n\nDiscretization:\n  DiscontinuousGalerkin:\n    PenaltyParameter: 1.\n    Massive: True\n    Quadrature: GaussLobatto\n    Formulation: WeakInertial\n\nObservers:\n  VolumeFileName: \"TovStarVolume\"\n  ReductionFileName: \"TovStarReductions\"\n\nNonlinearSolver:\n  NewtonRaphson:\n    ConvergenceCriteria:\n      MaxIterations: 10\n      RelativeResidual: 1.e-8\n      AbsoluteResidual: 1.e-10\n    SufficientDecrease: 1.e-4\n    MaxGlobalizationSteps: 40\n    DampingFactor: 1.\n    Verbosity: Quiet\n\nLinearSolver:\n  Gmres:\n    ConvergenceCriteria:\n      MaxIterations: 30\n      RelativeResidual: 1.e-4\n      AbsoluteResidual: 1.e-12\n    Verbosity: Quiet\n\n  Multigrid:\n    Iterations: 1\n    InitialCoarseLevels: Auto\n    PreSmoothing: True\n    UseBottomSolver: False\n    PostSmoothingAtBottom: False\n    Verbosity: Verbose\n    OutputVolumeData: False\n\n  SchwarzSmoother:\n    Iterations: 3\n    MaxOverlap: 2\n    Verbosity: Verbose\n    SubdomainSolver:\n      Gmres:\n        ConvergenceCriteria:\n          MaxIterations: 3\n          RelativeResidual: 1.e-4\n          AbsoluteResidual: 1.e-12\n        Verbosity: Silent\n        Restart: None\n        Preconditioner:\n          MinusLaplacian:\n            Solver:\n              ExplicitInverse:\n                WriteMatrixToFile: None\n            BoundaryConditions: Auto\n    SkipResets: True\n    ObservePerCoreReductions: False\n    OutputVolumeData: False\n\nRadiallyCompressedCoordinates: None\n\nEventsAndTriggersAtIterations:\n  - Trigger: HasConverged\n    Events:\n      - ObserveNorms:\n          SubfileName: ErrorNorms\n          TensorsToObserve:\n            - Name: Error(ConformalFactorMinusOne)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(LapseTimesConformalFactorMinusOne)\n              NormType: L2Norm\n              Components: Sum\n            - Name: Error(ShiftExcess)\n              NormType: L2Norm\n              Components: Sum\n      - ObserveNorms:\n          SubfileName: Norms\n          TensorsToObserve:\n            - Name: HamiltonianConstraint\n              NormType: L2Norm\n              Components: Individual\n            - Name: MomentumConstraint\n              NormType: L2Norm\n              Components: Individual\n      - ObserveFields:\n          SubfileName: VolumeData\n          VariablesToObserve:\n            - ConformalFactor\n            - Lapse\n            - Error(ConformalFactorMinusOne)\n            - Error(LapseTimesConformalFactorMinusOne)\n            - Conformal(EnergyDensity)\n            - Conformal(StressTrace)\n            - HamiltonianConstraint\n          InterpolateToMesh: None\n          ProjectToMesh: None\n          CoordinatesFloatingPointType: Float\n          FloatingPointTypes: [Float]\n          BlocksToObserve: All\n\nAmr:\n  Verbosity: Quiet\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: True\n  MaxCoarseLevels: Auto\n  Iterations: 1\n\nPhaseChangeAndTriggers: []\n\nBuildMatrix:\n  MatrixSubfileName: None\n  Verbosity: Verbose\n  EnableDirectSolve: True\n  SkipResets: True\n"
  },
  {
    "path": "tests/Unit/ArchitectureVectorization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This executable builds only core components for testing Blaze vectorization\n# on different architectures with different compilers.\n#\n# It can be used to test that vectorization with Blaze correctly works on all\n# of our target architectures without needing access to the CPUs. This is\n# made possible by the Intel Software Development Emulator.\n#\n# The relevant architectures as of 2020 and their Intel SDE flags are:\n# SDE Flag  march Flag                                Architecture\n# -nhm      -nehalem                                  Nehalem CPU\n# -wsm      -westmere                                 Westmere CPU\n# -snb      -sandybridge                              Sandy Bridge CPU\n# -ivb      -ivybridge                                Ivy Bridge CPU\n# -hsw      -haswell                                  Haswell CPU\n# -bdw      -broadwell                                Broadwell CPU\n# -skl      -skylake                                  Skylake CPU\n# -clx      -cascadelake                              Cascade Lake CPU\n# -cpx      -cooperlake                               Cooper Lake CPU\n# -icx      -icelake-server                           Ice Lake server CPU\n# -tgl      -tigerlake                                Tiger Lake CPU\n# -adl      -alderlake                                Alder Lake CPU\n# -spr      -sapphirerapids                           Sapphire Rapids CPU\n#\n# Note that KNL is not listed. KNL-type chips have been discontinued by Intel\n# due to poor performance.\nset(EXECUTABLE \"TestArchitectureVectorization\")\n\nset(SOURCES\n  ../DataStructures/Test_BlazeInteroperability.cpp\n  ../DataStructures/Test_DataVector.cpp\n  ../DataStructures/Test_DataVectorBinaryOperations.cpp\n  ../DataStructures/Test_ComplexDataVector.cpp\n  ../DataStructures/Test_ComplexDataVectorBinaryOperations.cpp\n  )\n\nadd_test_library(${EXECUTABLE} \"${SOURCES}\")\n\ntarget_link_libraries(\n  ${EXECUTABLE}\n  PRIVATE\n  DataStructures\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Unit test executables can use this module if they want to run in a Charm++\n# environment. They just have to add `WITH_CHARM` when calling\n# `add_test_library`.\nadd_charm_module(TestMainCharm)\n\n# Set include directory for all targets in this directory\ninclude_directories(${CMAKE_SOURCE_DIR}/tests/Unit)\n# Charm++ generated headers are created in the build directory\n# (by `add_charm_module`)\ninclude_directories(SYSTEM ${CMAKE_BINARY_DIR}/tests/Unit)\n\nadd_subdirectory(ControlSystem)\nadd_subdirectory(DataStructures)\nadd_subdirectory(Domain)\nadd_subdirectory(Elliptic)\nadd_subdirectory(Executables)\nadd_subdirectory(Evolution)\nadd_subdirectory(Framework)\nadd_subdirectory(Helpers)\nadd_subdirectory(IO)\nadd_subdirectory(Informer)\nadd_subdirectory(NumericalAlgorithms)\nadd_subdirectory(Options)\nadd_subdirectory(Parallel)\nadd_subdirectory(ParallelAlgorithms)\nadd_subdirectory(PointwiseFunctions)\nadd_subdirectory(ArchitectureVectorization)\nadd_subdirectory(Time)\nadd_subdirectory(Utilities)\nadd_subdirectory(Visualization)\n\n# Setup code coverage for unit tests\nif(COVERAGE)\n  # Setup unit test coverage target.\n  setup_target_for_coverage(\n      Unit\n      ${CMAKE_BINARY_DIR}/docs/html\n      unit-test-coverage\n      ${CMAKE_CTEST_COMMAND}\n      DEPENDS unit-tests\n      TESTRUNNER_ARGS\n      -j2 -L unit --output-on-failure --repeat after-timeout:3\n      IGNORE_COV\n      '${CMAKE_BINARY_DIR}/Informer/InfoAtLink.cpp'\n      '${CMAKE_SOURCE_DIR}/tests/*'\n      # The functions in CharmCompatibility.cpp are not intended to be called\n      '${CMAKE_SOURCE_DIR}/src/PythonBindings/CharmCompatibility.cpp'\n      # Ignore generated source files in build directory in coverage reporting\n      '${CMAKE_BINARY_DIR}/*'\n  )\nendif()\n\noption(\n  UNIT_TESTS_IN_TEST_EXECUTABLES\n  \"Build unit-tests as part of test-executables\"\n  ON\n  )\n\nif (UNIT_TESTS_IN_TEST_EXECUTABLES)\n  add_dependencies(test-executables unit-tests)\nendif (UNIT_TESTS_IN_TEST_EXECUTABLES)\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Actions/Test_GridCenters.cpp\n  Actions/Test_Initialization.cpp\n  Actions/Test_InitializeMeasurements.cpp\n  Actions/Test_LimitTimeStep.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Actions/Test_GridCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"ControlSystem/Actions/GridCenters.hpp\"\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTime,\n                 control_system::Tags::IsActiveMap>;\n  using simple_tags = db::AddSimpleTags<::domain::Tags::Element<3>,\n                                        Tags::TimeStepId, ::Tags::Time>;\n  using compute_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<control_system::Actions::SwitchGridRotationToSettle>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nstruct UpdateFot {\n  static void apply(gsl::not_null<domain::FunctionsOfTimeMap*> current_fots,\n                    const domain::FunctionsOfTimeMap& new_fots) {\n    *current_fots = clone_unique_ptrs(new_fots);\n  }\n};\n\nvoid test_action() {\n  register_classes_with_charm<\n      domain::FunctionsOfTime::PiecewisePolynomial<3>,\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<3>,\n      domain::FunctionsOfTime::SettleToConstantQuaternion>();\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\n  using component = Component<Metavariables>;\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"GridCenters\"] =\n      std::make_unique<::domain::FunctionsOfTime::PiecewisePolynomial<3>>(\n          0.0,\n          std::array<DataVector, 4>{{{16.0, 0.0, 0.0, -16.0, 0.0, 0.0},\n                                     {-0.001, 0.0, 0.0, 0.002, 0.0, 0.0},\n                                     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0},\n                                     {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}}},\n          std::numeric_limits<double>::max());\n  const double initial_omega_z = 0.01;\n  auto init_func_rotation = make_array<4, DataVector>(DataVector{3, 0.0});\n  init_func_rotation[1][2] = initial_omega_z;\n  auto init_quaternion = make_array<1, DataVector>(DataVector{4, 0.0});\n  init_quaternion[0][0] = 1.0;\n  functions_of_time[\"Rotation\"] =\n      std::make_unique<domain::FunctionsOfTime::QuaternionFunctionOfTime<3>>(\n          0.0, init_quaternion, init_func_rotation, 1.0e5);\n  std::unordered_map<std::string, bool> is_active_map{{\"GridCenters\", true},\n                                                      {\"Rotation\", true}};\n\n  MockRuntimeSystem runner{\n      {control_system::DisableRotationWhen{6.0, 60.0}},\n      {clone_unique_ptrs(functions_of_time), is_active_map}};\n\n  const ElementId<3> element_id_zero{0, {}};\n  const ElementId<3> element_id_nonzero{1, {}};\n  const std::vector element_ids{element_id_zero, element_id_nonzero};\n  TimeStepId time_step_id{true, 1, Time{Slab{0.0, 1.0e3}, Rational{2, 256}}};\n  const double time = time_step_id.step_time().value();\n\n  ActionTesting::emplace_array_component_and_initialize<component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, element_id_zero,\n      {::Element<3>{element_id_zero, {}}, time_step_id, time});\n  ActionTesting::emplace_array_component_and_initialize<component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, element_id_nonzero,\n      {::Element<3>{element_id_nonzero, {}}, time_step_id, time});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  CHECK_FALSE(ActionTesting::next_action_if_ready<component>(\n      make_not_null(&runner), element_id_zero,\n      Catch::Matchers::ContainsSubstring(\n          \"Expected to be at a Slab boundary when changing the \"\n          \"Rotation function of time to a SettleToConstant\")));\n\n  // No error if not on element zero\n  CHECK(ActionTesting::next_action_if_ready<component>(make_not_null(&runner),\n                                                       element_id_nonzero));\n\n  time_step_id = TimeStepId{true, 1, Time{Slab{0.0, 1.0e3}, Rational{0, 256}}};\n\n  for (const auto& id : element_ids) {\n    db::mutate<::Tags::TimeStepId, ::Tags::Time>(\n        [&time_step_id](const gsl::not_null<TimeStepId*> box_time_step_id,\n                        const gsl::not_null<double*> box_time) {\n          *box_time_step_id = time_step_id;\n          *box_time = box_time_step_id->step_time().value();\n        },\n        make_not_null(&ActionTesting::get_databox<component>(\n            make_not_null(&runner), id)));\n  }\n\n  CHECK_FALSE(ActionTesting::next_action_if_ready<component>(\n      make_not_null(&runner), element_id_zero,\n      Catch::Matchers::ContainsSubstring(\n          \"Disabling the rotation control system should happen when the \"\n          \"separation is less than or equal to \")));\n\n  // No error if not on element zero\n  CHECK(ActionTesting::next_action_if_ready<component>(make_not_null(&runner),\n                                                       element_id_nonzero));\n\n  time_step_id =\n      TimeStepId{true, 2, Time{Slab{12.0e3, 17.0e3}, Rational{0, 256}}};\n\n  for (const auto& id : element_ids) {\n    db::mutate<::Tags::TimeStepId, ::Tags::Time>(\n        [&time_step_id](const gsl::not_null<TimeStepId*> box_time_step_id,\n                        const gsl::not_null<double*> box_time) {\n          *box_time_step_id = time_step_id;\n          *box_time = box_time_step_id->step_time().value();\n        },\n        make_not_null(&ActionTesting::get_databox<component>(\n            make_not_null(&runner), id)));\n  }\n\n  // Test that a non-zero Element doesn't change the function of time.\n  CHECK(ActionTesting::next_action_if_ready<component>(make_not_null(&runner),\n                                                       element_id_nonzero));\n  for (const auto& id : element_ids) {\n    CHECK(get<control_system::Tags::IsActiveMap>(\n              ActionTesting::cache<component>(runner, id))\n              .at(\"Rotation\"));\n    REQUIRE(get<domain::Tags::FunctionsOfTime>(\n                ActionTesting::cache<component>(runner, id))\n                .at(\"Rotation\") != nullptr);\n    CHECK(dynamic_cast<\n              const domain::FunctionsOfTime::SettleToConstantQuaternion* const>(\n              get<domain::Tags::FunctionsOfTime>(\n                  ActionTesting::cache<component>(runner, id))\n                  .at(\"Rotation\")\n                  .get()) == nullptr);\n  }\n\n  // Remove things we need from FunctionsOfTime map to test ERRORs\n  auto& cache = ActionTesting::cache<component>(runner, element_id_zero);\n  Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFot>(\n      cache, [&functions_of_time]() {\n        auto local_fots = clone_unique_ptrs(functions_of_time);\n        local_fots.erase(\"GridCenters\");\n        return local_fots;\n      }());\n  CHECK_FALSE(ActionTesting::next_action_if_ready<component>(\n      make_not_null(&runner), element_id_zero,\n      Catch::Matchers::ContainsSubstring(\"There is no function of time named \"\n                                         \"'GridCenters', which is required \")));\n  Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFot>(\n      cache, [&functions_of_time]() {\n        auto local_fots = clone_unique_ptrs(functions_of_time);\n        local_fots.erase(\"Rotation\");\n        return local_fots;\n      }());\n  CHECK_FALSE(ActionTesting::next_action_if_ready<component>(\n      make_not_null(&runner), element_id_zero,\n      Catch::Matchers::ContainsSubstring(\"There is no function of time named \"\n                                         \"'Rotation', which means that \")));\n  Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFot>(\n      cache, [&functions_of_time]() {\n        auto local_fots = clone_unique_ptrs(functions_of_time);\n        return local_fots;\n      }());\n\n  // Check that the zero Element changes the function of time.\n  REQUIRE(ActionTesting::next_action_if_ready<component>(make_not_null(&runner),\n                                                         element_id_zero));\n  for (const auto& id : element_ids) {\n    CHECK_FALSE(get<control_system::Tags::IsActiveMap>(\n                    ActionTesting::cache<component>(runner, id))\n                    .at(\"Rotation\"));\n    REQUIRE(get<domain::Tags::FunctionsOfTime>(\n                ActionTesting::cache<component>(runner, id))\n                .at(\"Rotation\") != nullptr);\n    CHECK(dynamic_cast<\n              const domain::FunctionsOfTime::SettleToConstantQuaternion* const>(\n              get<domain::Tags::FunctionsOfTime>(\n                  ActionTesting::cache<component>(runner, id))\n                  .at(\"Rotation\")\n                  .get()) != nullptr);\n  }\n}\n\nvoid test_tags() {\n  TestHelpers::db::test_simple_tag<control_system::Tags::DisableRotationWhen>(\n      \"DisableRotationWhen\");\n\n  const control_system::DisableRotationWhen disable_opts =\n      serialize_and_deserialize(\n          control_system::Tags::DisableRotationWhen::create_from_options(\n              TestHelpers::test_option_tag<\n                  control_system::OptionTags::DisableRotationWhen>(\n                  \"DisableAtSeparation: 6\\n\"\n                  \"RotationDecayTimescale: 40\\n\")));\n  // REQUIRE because the action tests will fail if this doesn't work.\n  REQUIRE(disable_opts.disable_at_separation == 6.0);\n  REQUIRE(disable_opts.rotation_decay_timescale == 40.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Actions.GridCenters\",\n                  \"[Unit][ControlSystem]\") {\n  test_tags();\n  test_action();\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Actions/Test_Initialization.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"ControlSystem/Actions/Initialization.hpp\"\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct LabelA {};\nstruct LabelB {};\nstruct LabelC {};\n\nconstexpr size_t order = 2;\n\ntemplate <typename Label, typename Measurement>\nstruct MockControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return pretty_type::short_name<Label>(); }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return get_output(i);\n  }\n  using measurement = Measurement;\n  using control_error = control_system::TestHelpers::ControlError;\n  static constexpr size_t deriv_order = order;\n  using simple_tags = tmpl::list<>;\n};\n\nusing mock_control_sys_1 =\n    MockControlSystem<LabelA, control_system::TestHelpers::Measurement<LabelA>>;\nusing mock_control_sys_2 =\n    MockControlSystem<LabelB, control_system::TestHelpers::Measurement<LabelA>>;\nusing mock_control_sys_3 =\n    MockControlSystem<LabelC, control_system::TestHelpers::Measurement<LabelC>>;\n\ntemplate <typename Metavariables, typename ControlSystem>\nstruct MockControlComponent {\n  using array_index = int;\n  using component_being_mocked = ControlComponent<Metavariables, ControlSystem>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n\n  using system = ControlSystem;\n\n  using simple_tags = tmpl::list<>;\n\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\nstruct MockMetavars {\n  using mutable_global_cache_tags =\n      tmpl::list<control_system::Tags::MeasurementTimescales>;\n  using const_global_cache_tags =\n      tmpl::list<control_system::Tags::SystemToCombinedNames,\n                 control_system::Tags::IsActiveMap>;\n  using component_list = tmpl::transform<\n      tmpl::list<mock_control_sys_1, mock_control_sys_2, mock_control_sys_3>,\n      tmpl::bind<MockControlComponent, tmpl::pin<MockMetavars>, tmpl::_1>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Initialization\",\n                  \"[Unit][ControlSystem]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  Averager<order - 1> averager{0.5, true};\n  Averager<order - 1> expected_averager = averager;\n  int current_measurement{};\n\n  const double damping_time = 1.0;\n  TimescaleTuner<true> tuner{\n      std::vector<double>{damping_time}, 10.0, 0.1, 0.1, 1.01, 2.0, 0.99};\n  Controller<order> controller{0.3};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      measurement_timescales{};\n\n  const double initial_time = 0.5;\n  const double expr_time = 1.0;\n\n  const DataVector timescale =\n      control_system::calculate_measurement_timescales(controller, tuner, 4);\n\n  expected_averager.assign_time_between_measurements(min(timescale));\n\n  measurement_timescales[\"LabelALabelB\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          initial_time, std::array<DataVector, 1>{{timescale}}, expr_time);\n  measurement_timescales[\"LabelC\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          initial_time, std::array<DataVector, 1>{{timescale}}, expr_time);\n\n  std::unordered_map<std::string, std::string> system_to_combined_names{};\n  system_to_combined_names[\"LabelA\"] = \"LabelALabelB\";\n  system_to_combined_names[\"LabelB\"] = \"LabelALabelB\";\n  system_to_combined_names[\"LabelC\"] = \"LabelC\";\n  const std::unordered_map<std::string, bool> is_active_map{\n      {\"LabelA\", true}, {\"LabelB\", false}, {\"LabelC\", true}};\n\n  std::unordered_map<std::string, control_system::UpdateAggregator>\n      aggregators{};\n\n  Parallel::GlobalCache<MockMetavars> cache{\n      {std::move(system_to_combined_names), is_active_map},\n      {std::move(measurement_timescales)}};\n\n  const Parallel::GlobalCache<MockMetavars>& cache_reference = cache;\n\n  control_system::Actions::Initialize<MockMetavars, mock_control_sys_1>::apply(\n      make_not_null(&averager), make_not_null(&current_measurement),\n      make_not_null(&aggregators), &cache_reference);\n\n  CHECK(expected_averager == averager);\n  CHECK(current_measurement == 0);\n\n  CHECK(aggregators.count(\"LabelALabelB\") == 1);\n  CHECK(aggregators.count(\"LabelC\") == 1);\n\n  CHECK(aggregators.at(\"LabelALabelB\").combined_name() == \"LabelALabelB\");\n  CHECK(aggregators.at(\"LabelC\").combined_name() == \"LabelC\");\n\n  CHECK_FALSE(aggregators.at(\"LabelALabelB\").is_ready(is_active_map));\n  CHECK_FALSE(aggregators.at(\"LabelC\").is_ready(is_active_map));\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Actions/Test_InitializeMeasurements.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"ControlSystem/Actions/InitializeMeasurements.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"ControlSystem/Tags/FutureMeasurements.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/Trigger.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockDistributedObject.hpp\"\n#include \"Framework/MockRuntimeSystem.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeSteppers/AdamsMoultonPc.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <typename Id>\nstruct LinkedMessageId;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace {\ntemplate <typename ExpectedSystems>\nstruct Submeasurement\n    : tt::ConformsTo<control_system::protocols::Submeasurement> {\n  template <typename ControlSystems>\n  using interpolation_target_tag = void;\n  template <typename ControlSystems>\n  using horizon_metavars = void;\n\n  template <typename ControlSystems>\n  using event = control_system::TestHelpers::TestEvent<ExpectedSystems,\n                                                       ExpectedSystems, false>;\n};\n\ntemplate <typename ExpectedSystems>\nstruct Measurement : tt::ConformsTo<control_system::protocols::Measurement> {\n  using submeasurements = tmpl::list<Submeasurement<ExpectedSystems>>;\n};\n\nstruct SystemB;\n\nstruct SystemA : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return \"A\"; }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return get_output(i);\n  }\n  using simple_tags = tmpl::list<>;\n  using measurement = Measurement<tmpl::list<SystemA, SystemB>>;\n  using control_error = control_system::TestHelpers::ControlError;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstruct SystemB : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return \"B\"; }\n  static std::optional<std::string> component_name(\n      const size_t /*i*/, const size_t /*num_components*/) {\n    return std::nullopt;\n  }\n  using simple_tags = tmpl::list<>;\n  using measurement = Measurement<tmpl::list<SystemA, SystemB>>;\n  using control_error = control_system::TestHelpers::ControlError;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstruct SystemC : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return \"C\"; }\n  static std::optional<std::string> component_name(\n      const size_t /*i*/, const size_t /*num_components*/) {\n    return std::nullopt;\n  }\n  using simple_tags = tmpl::list<>;\n  using measurement = Measurement<tmpl::list<SystemC>>;\n  using control_error = control_system::TestHelpers::ControlError;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstatic_assert(tt::assert_conforms_to_v<\n              SystemA, control_system::protocols::ControlSystem>);\nstatic_assert(tt::assert_conforms_to_v<\n              SystemB, control_system::protocols::ControlSystem>);\nstatic_assert(tt::assert_conforms_to_v<\n              SystemC, control_system::protocols::ControlSystem>);\n\nusing control_systems = tmpl::list<SystemA, SystemB, SystemC>;\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n\n  using simple_tags_from_options = tmpl::list<::Tags::EventsAndDenseTriggers>;\n\n  using simple_tags = tmpl::list<Tags::TimeStepId, Tags::Time, Tags::TimeStep>;\n  using compute_tags = tmpl::push_back<\n      time_stepper_ref_tags<TimeStepper>,\n      Parallel::Tags::FromGlobalCache<::domain::Tags::FunctionsOfTimeInitialize,\n                                      Metavariables>>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::ConcreteTimeStepper<TimeStepper>,\n                 control_system::Tags::Verbosity>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<\n          ActionTesting::InitializeDataBox<simple_tags, compute_tags>,\n          evolution::Actions::InitializeRunEventsAndDenseTriggers,\n          control_system::Actions::InitializeMeasurements<control_systems>>>>;\n};\n\ntemplate <bool LocalTimeStepping>\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n  static constexpr bool local_time_stepping = LocalTimeStepping;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Event, control_system::metafunctions::control_system_events<\n                              control_systems>>,\n        tmpl::pair<DenseTrigger,\n                   control_system::control_system_triggers<control_systems>>>;\n  };\n};\n\ntemplate <bool LocalTimeStepping, bool MonotonicTimeStepper>\nvoid test_initialize_measurements(const bool ab_active, const bool c_active,\n                                  const bool delay_update) {\n  CAPTURE(LocalTimeStepping);\n  CAPTURE(MonotonicTimeStepper);\n  CAPTURE(ab_active);\n  CAPTURE(c_active);\n  CAPTURE(delay_update);\n\n  using TimeStepperType = TimeSteppers::AdamsMoultonPc<MonotonicTimeStepper>;\n\n  register_factory_classes_with_charm<Metavariables<LocalTimeStepping>>();\n  register_classes_with_charm<domain::FunctionsOfTime::PiecewisePolynomial<0>,\n                              TimeStepperType>();\n\n  using MockRuntimeSystem =\n      ActionTesting::MockRuntimeSystem<Metavariables<LocalTimeStepping>>;\n  using component = Component<Metavariables<LocalTimeStepping>>;\n  const component* const component_p = nullptr;\n\n  const ElementId<3> element_id{0};\n\n  // Details shouldn't matter\n  const double initial_time = 2.0;\n  const size_t measurements_per_update = 6;\n  const double initial_timescale = 1.5;\n  const double fot_expiration = 4.0;\n\n  const double step_to_expiration_ratio = c_active ? 3.0 : 0.2;\n\n  auto time_stepper = std::make_unique<TimeStepperType>(4);\n\n  const auto initial_slab = Slab::with_duration_from_start(\n      initial_time, step_to_expiration_ratio * (fot_expiration - initial_time));\n  const auto initial_time_step = initial_slab.duration();\n\n  const auto timescale =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          0.0, std::array{DataVector{initial_timescale}}, 2.0);\n  const auto inactive_timescale =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          0.0, std::array{DataVector{std::numeric_limits<double>::infinity()}},\n          std::numeric_limits<double>::infinity());\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      timescales;\n  timescales.emplace(\"AB\",\n                     (ab_active ? timescale : inactive_timescale)->get_clone());\n  timescales.emplace(\"C\",\n                     (c_active ? timescale : inactive_timescale)->get_clone());\n\n  const auto fot =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          0.0, std::array{DataVector{1.0}}, fot_expiration);\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions;\n  functions.emplace(\"A\", fot->get_clone());\n  functions.emplace(\"B\", fot->get_clone());\n  functions.emplace(\"C\", fot->get_clone());\n\n  MockRuntimeSystem runner{{std::move(time_stepper), ::Verbosity::Silent,\n                            measurements_per_update, delay_update},\n                           {std::move(functions), std::move(timescales)}};\n  ActionTesting::emplace_array_component_and_initialize<component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, element_id,\n      {Tags::TimeStepId::type{true, 0, {}}, initial_time, initial_time_step},\n      ::Tags::EventsAndDenseTriggers::type{});\n\n  // InitializeRunEventsAndDenseTriggers\n  ActionTesting::next_action<component>(make_not_null(&runner), element_id);\n  if (not delay_update and not MonotonicTimeStepper) {\n    CHECK_THROWS_WITH(ActionTesting::next_action<component>(\n                          make_not_null(&runner), element_id),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Chosen TimeStepper requires DelayUpdate: true\"));\n    return;\n  }\n  // InitializeMeasurements\n  ActionTesting::next_action<component>(make_not_null(&runner), element_id);\n\n  auto& cache = ActionTesting::cache<component>(runner, element_id);\n  auto& box =\n      ActionTesting::get_databox<component>(make_not_null(&runner), element_id);\n\n  CHECK(db::get<control_system::Tags::FutureMeasurements<\n            tmpl::list<SystemA, SystemB>>>(box)\n            .next_measurement() ==\n        std::optional(ab_active ? initial_time\n                                : std::numeric_limits<double>::infinity()));\n  CHECK(db::get<control_system::Tags::FutureMeasurements<tmpl::list<SystemC>>>(\n            box)\n            .next_measurement() ==\n        std::optional(c_active ? initial_time\n                               : std::numeric_limits<double>::infinity()));\n\n  CHECK(db::get<control_system::Tags::FutureMeasurements<\n            tmpl::list<SystemA, SystemB>>>(box)\n            .next_update() ==\n        (ab_active ? std::nullopt\n                   : std::optional(std::numeric_limits<double>::infinity())));\n  CHECK(db::get<control_system::Tags::FutureMeasurements<tmpl::list<SystemC>>>(\n            box)\n            .next_update() ==\n        (c_active ? std::nullopt\n                  : std::optional(std::numeric_limits<double>::infinity())));\n\n  if (c_active) {\n    // 4 is because of the 3.0 in the step_to_expiration_ratio above.\n    const auto reduced_time_step =\n        LocalTimeStepping ? initial_time_step / 4\n                          : Slab(initial_time, fot_expiration).duration();\n    CHECK(db::get<::Tags::TimeStep>(box) == reduced_time_step);\n  } else {\n    CHECK(db::get<::Tags::TimeStep>(box) == initial_time_step);\n  }\n\n  auto& events_and_dense_triggers =\n      db::get_mutable_reference<::Tags::EventsAndDenseTriggers>(\n          make_not_null(&box));\n  // This call initializes events_and_dense_triggers internals\n  events_and_dense_triggers.next_trigger(box);\n  CAPTURE(db::get<Tags::Time>(box));\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           element_id, component_p) ==\n        (ab_active or c_active\n             ? EventsAndDenseTriggers::TriggeringState::NeedsEvolvedVariables\n             : EventsAndDenseTriggers::TriggeringState::Ready));\n\n  using ListAB = tmpl::list<SystemA, SystemB>;\n  using ListC = tmpl::list<SystemC>;\n  Submeasurement<ListAB>::event<ListAB>::call_count = 0;\n  Submeasurement<ListC>::event<ListC>::call_count = 0;\n  events_and_dense_triggers.run_events(box, cache, 0, component_p);\n\n  CHECK(Submeasurement<ListAB>::event<ListAB>::call_count ==\n        (ab_active ? 1 : 0));\n  CHECK(Submeasurement<ListC>::event<ListC>::call_count == (c_active ? 1 : 0));\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.InitializeMeasurements\",\n                  \"[ControlSystem][Unit]\") {\n  for (const auto& [ab_active, c_active, delay_update] :\n       cartesian_product(std::array{true, false}, std::array{true, false},\n                         std::array{true, false})) {\n    test_initialize_measurements<false, true>(ab_active, c_active,\n                                              delay_update);\n    test_initialize_measurements<false, false>(ab_active, c_active,\n                                               delay_update);\n    test_initialize_measurements<true, true>(ab_active, c_active, delay_update);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Actions/Test_LimitTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"ControlSystem/Actions/LimitTimeStep.hpp\"\n#include \"ControlSystem/FutureMeasurements.hpp\"\n#include \"ControlSystem/Tags/FutureMeasurements.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/AdamsMoultonPc.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct LabelA {};\nstruct LabelB {};\nstruct LabelC {};\n\nusing MeasurementA = control_system::TestHelpers::Measurement<LabelA>;\nusing MeasurementB = control_system::TestHelpers::Measurement<LabelB>;\n\nusing systemsA =\n    tmpl::list<control_system::TestHelpers::System<0, LabelA, MeasurementA>>;\nusing systemsB =\n    tmpl::list<control_system::TestHelpers::System<0, LabelB, MeasurementB>,\n               control_system::TestHelpers::System<0, LabelC, MeasurementB>>;\n\nusing control_systems = tmpl::append<systemsA, systemsB>;\n\nstruct Var : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using mutable_global_cache_tags =\n      tmpl::list<control_system::Tags::MeasurementTimescales,\n                 domain::Tags::FunctionsOfTime>;\n  using simple_tags =\n      db::AddSimpleTags<control_system::Tags::FutureMeasurements<systemsA>,\n                        control_system::Tags::FutureMeasurements<systemsB>,\n                        Tags::ConcreteTimeStepper<TimeStepper>,\n                        Tags::TimeStepId, Tags::Next<Tags::TimeStepId>,\n                        Tags::TimeStep, Tags::AdaptiveSteppingDiagnostics,\n                        Tags::HistoryEvolvedVariables<Var>>;\n  using compute_tags = time_stepper_ref_tags<TimeStepper>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<control_system::Actions::LimitTimeStep<control_systems>>>>;\n};\n\nstruct Metavariables {\n  static constexpr bool local_time_stepping = false;\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nvoid test(const std::string& test_label, const double initial_time,\n          const double initial_step_end,\n          const std::optional<double>& expected_step_end,\n          const std::vector<std::pair<double, double>>& measurement_updatesA,\n          const std::vector<std::pair<double, double>>& fot_updatesA,\n          const std::vector<std::pair<double, double>>& measurement_updatesBC,\n          const std::vector<std::pair<double, double>>& fot_updatesB,\n          const std::vector<std::pair<double, double>>& fot_updatesC,\n          std::unique_ptr<TimeStepper> stepper =\n              std::make_unique<TimeSteppers::Rk3HesthavenSsp>(),\n          TimeSteppers::History<Var::type> history = {}) {\n  INFO(test_label);\n  ASSERT(measurement_updatesA.size() > 1, \"Bad argument\");\n  ASSERT(measurement_updatesBC.size() > 1, \"Bad argument\");\n  ASSERT(fot_updatesA.size() > 1, \"Bad argument\");\n  ASSERT(fot_updatesB.size() > 1, \"Bad argument\");\n  ASSERT(fot_updatesC.size() > 1, \"Bad argument\");\n\n  const ElementId<3> element_id{0};\n\n  const Slab initial_slab(initial_time, initial_step_end);\n  const TimeStepId initial_id(true, 0, initial_slab.start());\n\n  const size_t measurements_per_update = 3;\n\n  const auto setup_fot =\n      [](const std::vector<std::pair<double, double>>& updates) {\n        auto fot =\n            std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n                updates.front().first,\n                std::array{DataVector{updates.front().second}},\n                updates[1].first);\n        for (size_t i = 1; i < updates.size() - 1; ++i) {\n          fot->update(updates[i].first, DataVector{updates[i].second},\n                      updates[i + 1].first);\n        }\n        return fot;\n      };\n\n  control_system::Tags::MeasurementTimescales::type timescales{};\n  timescales[\"LabelA\"] = setup_fot(measurement_updatesA);\n  timescales[\"LabelBLabelC\"] = setup_fot(measurement_updatesBC);\n  domain::Tags::FunctionsOfTime::type functions_of_time{};\n  functions_of_time[\"LabelA\"] = setup_fot(fot_updatesA);\n  functions_of_time[\"LabelB\"] = setup_fot(fot_updatesB);\n  functions_of_time[\"LabelC\"] = setup_fot(fot_updatesC);\n\n  const auto setup_measurements =\n      [&initial_time](\n          const domain::FunctionsOfTime::FunctionOfTime& timescale) {\n        if (timescale.func(0.0)[0][0] ==\n            std::numeric_limits<double>::infinity()) {\n          return control_system::FutureMeasurements(\n              1, std::numeric_limits<double>::infinity());\n        }\n\n        control_system::FutureMeasurements measurements(measurements_per_update,\n                                                        0.0);\n        measurements.update(timescale);\n        while (measurements.next_measurement().value_or(\n                   std::numeric_limits<double>::infinity()) <= initial_time) {\n          measurements.pop_front();\n        }\n        return measurements;\n      };\n\n  auto measurementsA = setup_measurements(*timescales[\"LabelA\"]);\n  auto measurementsBC = setup_measurements(*timescales[\"LabelBLabelC\"]);\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\n  using component = Component<Metavariables>;\n  MockRuntimeSystem runner{\n      {1e-8}, {std::move(timescales), std::move(functions_of_time)}};\n  ActionTesting::emplace_array_component_and_initialize<component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, element_id,\n      {std::move(measurementsA), std::move(measurementsBC), std::move(stepper),\n       initial_id, TimeStepId{}, initial_slab.duration(),\n       AdaptiveSteppingDiagnostics{}, std::move(history)});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  const bool ready = ActionTesting::next_action_if_ready<component>(\n      make_not_null(&runner), element_id);\n  if (ready and expected_step_end.has_value()) {\n    const Slab expected_slab(initial_time, *expected_step_end);\n    CHECK(ActionTesting::get_databox_tag<component, Tags::TimeStep>(\n              runner, element_id) == expected_slab.duration());\n  } else {\n    CHECK(not ready);\n    CHECK(not expected_step_end.has_value());\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Actions.LimitTimeStep\",\n                  \"[Unit][ControlSystem]\") {\n  register_classes_with_charm<\n      TimeSteppers::AdamsBashforth, TimeSteppers::AdamsMoultonPc<false>,\n      TimeSteppers::Rk3HesthavenSsp,\n      domain::FunctionsOfTime::PiecewisePolynomial<0>>();\n  const double infinity = std::numeric_limits<double>::infinity();\n  const double nan = std::numeric_limits<double>::signaling_NaN();\n  const double arbitrary = -1234.0;\n\n  // For each active control system below, the interval from the\n  // measurement triggering the update to the FoT expiration time is\n  // indicated as the goal interval.  A step must occur in this\n  // interval to avoid a deadlock.\n\n  // clang-format off\n  test(\"No control systems\", 1.0, 5.0, 5.0,\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Step much shorter than limit\", 1.0, 5.0, 5.0,\n       {{0.0, 10.0}, {50.0, nan}},\n       {{0.0, arbitrary}, {50.0, nan}},  // goal range [20, 50]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Limited by expiration\", 1.0, 5.0, 4.0,\n       {{0.0, 1.0}, {5.0, nan}},\n       {{0.0, arbitrary}, {4.0, nan}},  // goal range [2, 4]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Adjusted to keep steps even\", 1.0, 8.0, 6.0,\n       {{0.0, 5.0}, {20.0, nan}},\n       {{0.0, arbitrary}, {11.0, nan}},  // goal range [10, 11]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Past update but not to expiration\", 1.0, 8.0, 8.0,\n       {{0.0, 3.0}, {20.0, nan}},\n       {{0.0, arbitrary}, {11.0, nan}},  // goal range [6, 11]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Limited by expiration, 2 systems\", 1.0, 7.0, 5.0,\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, 1.0}, {5.0, nan}},\n       {{0.0, arbitrary}, {5.0, nan}},  // goal range [2, 5]\n       {{0.0, arbitrary}, {6.0, nan}});  // goal range [2, 6]\n  test(\"Limited by expiration, 2 measurements\", 1.0, 6.0, 5.0,\n       {{0.0, 2.0}, {10.0, nan}},\n       {{0.0, arbitrary}, {6.0, nan}},  // goal range [4, 6]\n       {{0.0, 1.0}, {5.0, nan}},\n       {{0.0, arbitrary}, {5.0, nan}},  // goal range [2, 5]\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Adjusted to keep steps even, 2 systems\", 1.0, 11.0, 7.0,\n       {{0.0, 6.0}, {20.0, nan}},\n       {{0.0, arbitrary}, {13.0, nan}},  // goal range [12, 13]\n       {{0.0, 2.0}, {20.0, nan}},\n       {{0.0, arbitrary}, {15.0, nan}},  // goal range [4, 15]\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Adjusted to keep steps even, limited by update\", 1.0, 11.0, 8.0,\n       {{0.0, 6.0}, {20.0, nan}},\n       {{0.0, arbitrary}, {13.0, nan}},  // goal range [12, 13]\n       {{0.0, 4.0}, {20.0, nan}},\n       {{0.0, arbitrary}, {15.0, nan}},  // goal range [8, 15]\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Would be limited by expiration as of now\", 1.0, 5.0, 5.0,\n       {{0.0, 2.0}, {5.0, nan}},\n       {{0.0, arbitrary}, {2.0, arbitrary}, {20.0, nan}},  // goal range [4, 20]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Insufficient timescale data\", 1.0, 5.0, std::nullopt,\n       {{0.0, 10.0}, {5.0, nan}},\n       {{0.0, arbitrary}, {50.0, nan}},  // goal range [?, ?]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Barely sufficient timescale data\", 1.0, 5.0, 5.0,\n       {{0.0, 10.0}, {10.0, nan}},\n       {{0.0, arbitrary}, {50.0, nan}},  // goal range [20, 50]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Insufficient FoT data\", 1.0, 5.0, std::nullopt,\n       {{0.0, 10.0}, {50.0, nan}},\n       {{0.0, arbitrary}, {15.0, nan}},  // goal range [20, ?]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n  test(\"Barely insufficient FoT data\", 1.0, 5.0, std::nullopt,\n       {{0.0, 10.0}, {50.0, nan}},\n       {{0.0, arbitrary}, {20.0, nan}},  // goal range [20, ?]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}});\n\n  test(\"Does nothing with Adams-Bashforth\", 1.0, 5.0, 5.0,\n       {{0.0, 1.0}, {5.0, nan}},\n       {{0.0, arbitrary}, {3.0, nan}},  // goal range [2, 3]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       std::make_unique<TimeSteppers::AdamsBashforth>(4));\n  test(\"Doesn't need data with Adams-Bashforth\", 1.0, 5.0, 5.0,\n       {{0.0, 10.0}, {5.0, nan}},\n       {{0.0, arbitrary}, {50.0, nan}},  // goal range [?, ?]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       std::make_unique<TimeSteppers::AdamsBashforth>(4));\n\n  TimeSteppers::History<Var::type> history{};\n  history.insert(TimeStepId(true, -1, Time(Slab(1.0, 5.0), {1, 2})), {}, {});\n  history.insert(TimeStepId(true, 0, Slab(1.0, 5.0).start()), {}, {});\n  test(\"Step can't change but is OK\", 1.0, 5.0, 5.0,\n       {{0.0, 10.0}, {50.0, nan}},\n       {{0.0, arbitrary}, {50.0, nan}},  // goal range [20, 50]\n       {{0.0, infinity}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       {{0.0, arbitrary}, {infinity, nan}},\n       std::make_unique<TimeSteppers::AdamsMoultonPc<false>>(4), history);\n  CHECK_THROWS_WITH(\n      test(\"Step can't change but is not OK\", 1.0, 5.0, 0.0,\n           {{0.0, 1.0}, {5.0, nan}},\n           {{0.0, arbitrary}, {4.0, nan}},  // goal range [2, 4]\n           {{0.0, infinity}, {infinity, nan}},\n           {{0.0, arbitrary}, {infinity, nan}},\n           {{0.0, arbitrary}, {infinity, nan}},\n           std::make_unique<TimeSteppers::AdamsMoultonPc<false>>(4), history),\n      Catch::Matchers::ContainsSubstring(\n          \"Step must be decreased to avoid control-system deadlock, but \"\n          \"time-stepper requires a fixed step size.\"));\n  // clang-format on\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ControlSystem\")\n\nset(LIBRARY_SOURCES\n  Test_Averager.cpp\n  Test_CleanFunctionsOfTime.cpp\n  Test_CombinedName.cpp\n  Test_Controller.cpp\n  Test_EventTriggerMetafunctions.cpp\n  Test_ExpirationTimes.cpp\n  Test_FutureMeasurements.cpp\n  Test_IsSize.cpp\n  Test_Measurements.cpp\n  Test_Metafunctions.cpp\n  Test_RunCallbacks.cpp\n  Test_Tags.cpp\n  Test_TimescaleTuner.cpp\n  Test_Trigger.cpp\n  Test_UpdateFunctionOfTime.cpp\n  Test_WriteData.cpp\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(ControlErrors)\nadd_subdirectory(Protocols)\nadd_subdirectory(Systems)\nadd_subdirectory(Tags)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ApparentHorizonFinder\n  ControlSystem\n  ControlSystemMeasurements\n  EventsAndDenseTriggers\n  FunctionsOfTime\n  H5\n  Observer\n  ObserverHelpers\n  Parallel\n  PostNewtonianHelpers\n  SphericalHarmonics\n  Time\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  ControlErrors/Test_ComovingCharSpeedDerivative.cpp\n  ControlErrors/Test_Expansion.cpp\n  ControlErrors/Test_GridCenters.cpp\n  ControlErrors/Test_Rotation.cpp\n  ControlErrors/Test_Shape.cpp\n  ControlErrors/Test_SizeControlStates.cpp\n  ControlErrors/Test_SizeError.cpp\n  ControlErrors/Test_Skew.cpp\n  ControlErrors/Test_StateHistory.cpp\n  ControlErrors/Test_Translation.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/ComovingCharSpeedDerivative.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef deriv_normalized_normal(\n    grid_frame_excision_sphere_radius,\n    excision_rhat,\n    normalized_normal_one_form,\n    one_over_excision_normal_one_form_norm,\n    inverse_spatial_metric_on_excision_boundary,\n    spatial_christoffel_second_kind,\n    inverse_jacobian_grid_to_distorted,\n):\n    Y00 = 0.5 / np.sqrt(np.pi)\n\n    return Y00 * (\n        np.einsum(\n            \"j,ji->i\",\n            normalized_normal_one_form,\n            inverse_jacobian_grid_to_distorted,\n        )\n        / grid_frame_excision_sphere_radius\n        - np.einsum(\n            \"j,jk,kl,l,i->i\",\n            normalized_normal_one_form,\n            inverse_jacobian_grid_to_distorted,\n            inverse_spatial_metric_on_excision_boundary,\n            normalized_normal_one_form,\n            normalized_normal_one_form,\n        )\n        / grid_frame_excision_sphere_radius\n        - np.einsum(\n            \"i,p,j,pk,m,jkm->i\",\n            normalized_normal_one_form,\n            normalized_normal_one_form,\n            normalized_normal_one_form,\n            inverse_spatial_metric_on_excision_boundary,\n            excision_rhat,\n            spatial_christoffel_second_kind,\n        )\n    )\n\n\ndef comoving_char_speed_derivative(\n    lambda_00,\n    dt_lambda_00,\n    horizon_00,\n    dt_horizon_00,\n    grid_frame_excision_sphere_radius,\n    excision_rhat,\n    excision_normal_one_form,\n    one_over_excision_normal_one_form_norm,\n    distorted_components_of_grid_shift,\n    inverse_spatial_metric_on_excision_boundary,\n    spatial_christoffel_second_kind,\n    deriv_lapse,\n    deriv_of_distorted_shift,\n    inverse_jacobian_grid_to_distorted,\n):\n    Y00 = 0.5 / np.sqrt(np.pi)\n\n    normalized_normal_one_form = (\n        excision_normal_one_form * one_over_excision_normal_one_form_norm\n    )\n\n    temp = (\n        excision_rhat\n        * Y00\n        * (\n            dt_lambda_00\n            - dt_horizon_00\n            * (lambda_00 - grid_frame_excision_sphere_radius / Y00)\n            / horizon_00\n        )\n        + distorted_components_of_grid_shift\n    )\n\n    deriv_normal = deriv_normalized_normal(\n        grid_frame_excision_sphere_radius,\n        excision_rhat,\n        normalized_normal_one_form,\n        one_over_excision_normal_one_form_norm,\n        inverse_spatial_metric_on_excision_boundary,\n        spatial_christoffel_second_kind,\n        inverse_jacobian_grid_to_distorted,\n    )\n\n    return np.einsum(\"i,i\", deriv_normal, temp) - Y00 * (\n        np.einsum(\n            \"i,j,ji\",\n            normalized_normal_one_form,\n            excision_rhat,\n            deriv_of_distorted_shift,\n        )\n        - np.einsum(\"i,i\", excision_rhat, deriv_lapse)\n        + (dt_horizon_00 / horizon_00)\n        * np.einsum(\"i,i\", excision_rhat, normalized_normal_one_form)\n    )\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_ComovingCharSpeedDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"ControlSystem/ControlErrors/Size/ComovingCharSpeedDerivative.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nnamespace {\n\ntemplate <typename DataType>\nvoid test_comoving_char_speed_derivative(const DataType& used_for_size) {\n  pypp::check_with_random_values<14>(\n      &control_system::size::comoving_char_speed_derivative,\n      \"ComovingCharSpeedDerivative\", {{\"comoving_char_speed_derivative\"}},\n      {{\n          {-0.5, 0.},\n          {-1., 1.},\n          {2.2, 2.5},\n          {-1., 1.},\n          {1.9, 2.1},\n          {-0.8, 0.8},\n          {-1., 1.},\n          {0.5, 1.},\n          {-1., 1.},\n          {1., 2.},\n          {-1., 1.},\n          {-1., 1.},\n          {-1., 1.},\n          {-1., 1.},\n      }},\n      used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.ControlSystem.ControlErrors.ComovingCharSpeedDerivative\",\n    \"[Domain][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"ControlSystem/ControlErrors/\");\n  DataVector used_for_size(3);\n  test_comoving_char_speed_derivative(used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_Expansion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system {\nnamespace {\nvoid test_expansion_control_error() {\n  constexpr size_t deriv_order = 2;\n  using metavars =\n      TestHelpers::control_system::MockMetavars<0, 0, deriv_order, 0>;\n  using element_component = typename metavars::element_component;\n  using expansion_system = typename metavars::expansion_system;\n\n  // Global things\n  domain::FunctionsOfTime::register_derived_with_charm();\n  const double initial_time = 0.0;\n  const double initial_separation = 15.0;\n\n  // Set up the system helper.\n  TestHelpers::control_system::SystemHelper<metavars> system_helper{};\n\n  const std::string input_options =\n      \"Evolution:\\n\"\n      \"  InitialTime: 0.0\\n\"\n      \"DomainCreator:\\n\"\n      \"  FakeCreator:\\n\"\n      \"    NumberOfComponents:\\n\"\n      \"      Expansion: 1\\n\"\n      \"ControlSystems:\\n\"\n      \"  WriteDataToDisk: false\\n\"\n      \"  MeasurementsPerUpdate: 4\\n\"\n      \"  DelayUpdate: true\\n\"\n      \"  Expansion:\\n\"\n      \"    Averager:\\n\"\n      \"      AverageTimescaleFraction: 0.25\\n\"\n      \"      Average0thDeriv: true\\n\"\n      \"    Controller:\\n\"\n      \"      UpdateFraction: 0.3\\n\"\n      \"    TimescaleTuner:\\n\"\n      \"      InitialTimescales: [0.5]\\n\"\n      \"      MinTimescale: 0.1\\n\"\n      \"      MaxTimescale: 10.\\n\"\n      \"      DecreaseThreshold: 2.0\\n\"\n      \"      IncreaseThreshold: 0.1\\n\"\n      \"      IncreaseFactor: 1.01\\n\"\n      \"      DecreaseFactor: 0.99\\n\"\n      \"    ControlError:\\n\";\n\n  // Initialize everything within the system helper\n  system_helper.setup_control_system_test(\n      initial_time, initial_separation, input_options,\n      TestHelpers::control_system::initialize_expansion_functions_of_time<\n          expansion_system>);\n\n  // Get references to everything that was set up inside the system helper. The\n  // domain and two functions of time are not const references because they need\n  // to be moved into the runner\n  auto& domain = system_helper.domain();\n  auto& is_active_map = system_helper.is_active_map();\n  auto& initial_functions_of_time = system_helper.initial_functions_of_time();\n  auto& initial_measurement_timescales =\n      system_helper.initial_measurement_timescales();\n  auto system_to_combined_names = system_helper.system_to_combined_names();\n  const std::string expansion_name =\n      system_helper.template name<expansion_system>();\n\n  auto grid_center_A = domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  auto grid_center_B = domain.excision_spheres().at(\"ExcisionSphereB\").center();\n\n  // Setup runner and element component because it's the easiest way to get the\n  // global cache\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {\"DummyFileName\", std::move(domain), 4, true, false, ::Verbosity::Silent,\n       std::move(is_active_map), std::move(grid_center_A),\n       std::move(grid_center_B), std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n  const auto& cache = ActionTesting::cache<element_component>(runner, 0);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n\n  using QueueTuple = tuples::TaggedTuple<\n      control_system::QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n      control_system::QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>>;\n\n  // Create fake measurements. For expansion we only care about the x component\n  // because that's all that is used. A is on the positive x-axis, B is on the\n  // negative x-axis\n  const double pos_A_x = 10.0;\n  const double pos_B_x = -5.0;\n  QueueTuple fake_measurement_tuple{DataVector{pos_A_x, 0.0, 0.0},\n                                    DataVector{pos_B_x, 0.0, 0.0}};\n\n  using ControlError = expansion_system::control_error;\n\n  // This is before the first expiration time\n  const double check_time = 0.1;\n  const DataVector control_error =\n      ControlError{}(::TimescaleTuner<true>{}, cache, check_time,\n                     expansion_name, fake_measurement_tuple);\n\n  const auto& expansion_f_of_t =\n      dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>&>(\n          *functions_of_time.at(expansion_name));\n  // Since we haven't updated, the expansion factor should just be 1.0\n  const double exp_factor = expansion_f_of_t.func(check_time)[0][0];\n  const double pos_diff = pos_A_x - pos_B_x;\n  const double grid_diff = initial_separation;\n\n  const DataVector expected_control_error{exp_factor *\n                                          (pos_diff / grid_diff - 1.0)};\n\n  CHECK_ITERABLE_APPROX(control_error, expected_control_error);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.ControlErrors.Expansion\",\n                  \"[ControlSystem][Unit]\") {\n  test_expansion_control_error();\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_GridCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n\n#include \"ControlSystem/ControlErrors/GridCenters.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"ControlSystem/UpdateTimescaleTuner.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Norms.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavars {\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 domain::Tags::ObjectCenter<domain::ObjectLabel::A>,\n                 domain::Tags::ObjectCenter<domain::ObjectLabel::B>>;\n  using observed_reduction_data_tags = tmpl::list<>;\n  using component_list = tmpl::list<observers::ObserverWriter<Metavars>>;\n  void pup(PUP::er& /*p*/) {}\n};\n\nconstexpr double grid_x_coord = 6.0;\n\nvoid test_grid_centers(\n    const double time, const double measured_grid_x_coord,\n    const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>&\n        function_of_time,\n    const DataVector& expected_error) {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"GridCenters\"] = function_of_time->get_clone();\n  const Parallel::GlobalCache<Metavars> cache{\n      {std::move(functions_of_time),\n       tnsr::I<double, 3, Frame::Grid>{{grid_x_coord, 0.0, 0.0}},\n       tnsr::I<double, 3, Frame::Grid>{{-grid_x_coord, 0.0, 0.0}}, \"\", \"\",\n       std::vector<std::string>{}}};\n  using grid_center_A =\n      control_system::QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>;\n  using grid_center_B =\n      control_system::QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>;\n  const tuples::TaggedTuple<grid_center_A, grid_center_B> measurements{\n      std::array{measured_grid_x_coord, 0.0, 0.0},\n      std::array{-measured_grid_x_coord, 0.0, 0.0}};\n\n  using GridCentersError = control_system::ControlErrors::GridCenters;\n\n  const auto error_class_creation =\n      TestHelpers::test_creation<GridCentersError, Metavars>(\"\");\n  GridCentersError error_class =\n      serialize_and_deserialize(error_class_creation);\n\n  CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n  error_class.reset();\n  CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n\n  TimescaleTuner<true> tuner{std::vector<double>{20.0, 20.0}, 20.0, 0.01,\n                             1.0e-4, 1.01};\n\n  CHECK_ITERABLE_APPROX(\n      error_class(tuner, cache, time, \"GridCenters\", measurements),\n      expected_error);\n\n  REQUIRE(not error_class.get_suggested_timescale().has_value());\n\n  const DataVector old_timescale = tuner.current_timescale();\n\n  control_system::update_timescale_tuner(\n      make_not_null(&tuner), make_not_null(&error_class), ::Verbosity::Silent,\n      time, \"GridCenters\"s);\n\n  CHECK(old_timescale == tuner.current_timescale());\n  CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.ControlErrors.GridCenters\",\n                  \"[ControlSystem][Unit]\") {\n  const double initial_time = 0.0;\n  const double expiration_time = 5.0;\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>\n      function_of_time =\n          std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n              initial_time,\n              std::array{DataVector{6.0, 0.0, 0.0, -6.0, 0.0, 0.0},\n                         DataVector{6, 0.0}, DataVector{6, 0.0}},\n              expiration_time);\n\n  test_grid_centers(initial_time, grid_x_coord, function_of_time,\n                    DataVector{6, 0.0});\n  test_grid_centers(initial_time, 0.5 * grid_x_coord, function_of_time,\n                    {-3.0, 0.0, 0.0, 3.0, 0.0, 0.0});\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_Rotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system {\nnamespace {\nvoid test_rotation_control_error() {\n  // Since we are only doing rotation, turn off the\n  // other control systems by passing 0 for their deriv orders\n  constexpr size_t deriv_order = 2;\n  using metavars =\n      TestHelpers::control_system::MockMetavars<0, deriv_order, 0, 0>;\n  using element_component = typename metavars::element_component;\n  using rotation_system = typename metavars::rotation_system;\n  MAKE_GENERATOR(gen);\n\n  // Global things\n  domain::FunctionsOfTime::register_derived_with_charm();\n  const double initial_time = 0.0;\n  const double initial_separation = 15.0;\n\n  // Set up the system helper.\n  TestHelpers::control_system::SystemHelper<metavars> system_helper{};\n\n  const std::string input_options =\n      \"Evolution:\\n\"\n      \"  InitialTime: 0.0\\n\"\n      \"DomainCreator:\\n\"\n      \"  FakeCreator:\\n\"\n      \"    NumberOfComponents:\\n\"\n      \"      Rotation: 3\\n\"\n      \"ControlSystems:\\n\"\n      \"  WriteDataToDisk: false\\n\"\n      \"  MeasurementsPerUpdate: 4\\n\"\n      \"  DelayUpdate: true\\n\"\n      \"  Rotation:\\n\"\n      \"    Averager:\\n\"\n      \"      AverageTimescaleFraction: 0.25\\n\"\n      \"      Average0thDeriv: true\\n\"\n      \"    Controller:\\n\"\n      \"      UpdateFraction: 0.3\\n\"\n      \"    TimescaleTuner:\\n\"\n      \"      InitialTimescales: 0.5\\n\"\n      \"      MinTimescale: 0.1\\n\"\n      \"      MaxTimescale: 10.\\n\"\n      \"      DecreaseThreshold: 2.0\\n\"\n      \"      IncreaseThreshold: 0.1\\n\"\n      \"      IncreaseFactor: 1.01\\n\"\n      \"      DecreaseFactor: 0.99\\n\"\n      \"    ControlError:\\n\";\n\n  // Initialize everything within the system helper\n  system_helper.setup_control_system_test(\n      initial_time, initial_separation, input_options,\n      TestHelpers::control_system::initialize_rotation_functions_of_time<\n          rotation_system>);\n\n  // Get references to everything that was set up inside the system helper. The\n  // domain and two functions of time are not const references because they need\n  // to be moved into the runner\n  auto& domain = system_helper.domain();\n  auto& is_active_map = system_helper.is_active_map();\n  auto& initial_functions_of_time = system_helper.initial_functions_of_time();\n  auto& initial_measurement_timescales =\n      system_helper.initial_measurement_timescales();\n  auto system_to_combined_names = system_helper.system_to_combined_names();\n  const std::string rotation_name =\n      system_helper.template name<rotation_system>();\n\n  auto grid_center_A = domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  auto grid_center_B = domain.excision_spheres().at(\"ExcisionSphereB\").center();\n\n  // Setup runner and element component because it's the easiest way to get the\n  // global cache\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {\"DummyFileName\", std::move(domain), 4, true, false, ::Verbosity::Silent,\n       std::move(is_active_map), std::move(grid_center_A),\n       std::move(grid_center_B), std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n  const auto& cache = ActionTesting::cache<element_component>(runner, 0);\n\n  using QueueTuple = tuples::TaggedTuple<\n      control_system::QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n      control_system::QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>>;\n\n  // Create fake measurements.\n  const DataVector pos_A{{2.0, 3.0, 6.0}};\n  const DataVector pos_B{{-3.0, -4.0, 5.0}};\n  QueueTuple fake_measurement_tuple{pos_A, pos_B};\n\n  using ControlError = rotation_system::control_error;\n\n  // This is before the first expiration time\n  const double check_time = 0.1;\n  const DataVector control_error =\n      ControlError{}(::TimescaleTuner<true>{}, cache, check_time, rotation_name,\n                     fake_measurement_tuple);\n\n  // Calculated error = (grid_diff cross pos_diff) / (grid_diff dot pos_diff) by\n  // hand\n  const DataVector expected_control_error =\n      DataVector{{0.0, -15.0, 105.0}} / 75.0;\n\n  CHECK_ITERABLE_APPROX(control_error, expected_control_error);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.ControlErrors.Rotation\",\n                  \"[ControlSystem][Unit]\") {\n  test_rotation_control_error();\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_Shape.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Shape.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/Systems/Shape.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Distorted;\n}  // namespace Frame\n\nnamespace control_system {\nnamespace {\nusing FoTMap = std::unordered_map<\n    std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;\nusing Strahlkorper = ylm::Strahlkorper<Frame::Distorted>;\n\nconstexpr size_t deriv_order = 2;\n\nvoid check_shape_control_error(const size_t ah_l_max, const size_t lambda_l_max,\n                               std::mt19937& generator) {\n  using metavars =\n      TestHelpers::control_system::MockMetavars<0, 0, 0, deriv_order>;\n  using system = typename metavars::shape_system;\n  using ControlError = system::control_error;\n  using element_component = typename metavars::element_component;\n\n  const tnsr::I<double, 3, Frame::Grid> origin{0.0};\n  const double ah_radius = 1.5;\n  const double initial_time = 0.0;\n  Strahlkorper fake_ah{ah_l_max, ah_l_max, ah_radius,\n                       make_array<double, 3>(origin)};\n  auto& fake_ah_coefs = fake_ah.coefficients();\n\n  const ylm::Spherepack lambda_spherepack{lambda_l_max, lambda_l_max};\n\n  // Setup initial shape map coefficients. In the map the coefficients are\n  // stored as the negative of the actual spherical harmonic coefficients\n  // because that's just how the map is defined. But since these are random\n  // numbers it doesn't matter for initial data\n  auto initial_shape_func = make_array<deriv_order + 1, DataVector>(\n      DataVector{lambda_spherepack.spectral_size(), 0.0});\n  ylm::SpherepackIterator iter{lambda_l_max, lambda_l_max};\n  std::uniform_real_distribution<double> coef_dist{-1.0, 1.0};\n  for (size_t i = 0; i < initial_shape_func.size(); i++) {\n    for (iter.reset(); iter; ++iter) {\n      // Enforce l=0,l=1 components to be 0 always\n      if (iter.l() == 0 or iter.l() == 1) {\n        continue;\n      }\n      gsl::at(initial_shape_func, i)[iter()] = make_with_random_values<double>(\n          make_not_null(&generator), coef_dist, 1);\n    }\n  }\n\n  // Setup initial size map parameters\n  auto initial_size_func =\n      make_array<deriv_order + 1, DataVector>(DataVector{1, 0.0});\n  // Excision sphere radius needs to be inside the AH\n  const double excision_radius = 0.75 * ah_radius;\n  // We only test for a constant size function of time. A test with a changing\n  // size can be added in later\n  initial_size_func[0][0] = ah_radius;\n\n  // Setup control system stuff\n  const std::string shape_name = system::name();\n  const std::string size_name =\n      ControlErrors::detail::size_name<::domain::ObjectLabel::A>();\n  const std::string excision_sphere_A_name =\n      ControlErrors::detail::excision_sphere_name<::domain::ObjectLabel::A>();\n  const std::string excision_sphere_B_name =\n      ControlErrors::detail::excision_sphere_name<::domain::ObjectLabel::B>();\n\n  // Since the map for A/B are independent of each other, we only need to test\n  // one of them\n  FoTMap initial_functions_of_time{};\n  FoTMap initial_measurement_timescales{};\n  initial_functions_of_time[shape_name] = std::make_unique<\n      domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>>(\n      initial_time, initial_shape_func, 0.5);\n  initial_functions_of_time[size_name] = std::make_unique<\n      domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>>(\n      initial_time, initial_size_func, std::numeric_limits<double>::infinity());\n\n  // Fake domain\n  Domain<3> fake_domain{\n      std::vector<Block<3>>{},\n      {{excision_sphere_A_name,\n        ExcisionSphere<3>{excision_radius,\n                          origin,\n                          {{0, Direction<3>::lower_zeta()},\n                           {1, Direction<3>::lower_zeta()},\n                           {2, Direction<3>::lower_zeta()},\n                           {3, Direction<3>::lower_zeta()},\n                           {4, Direction<3>::lower_zeta()},\n                           {5, Direction<3>::lower_zeta()}}}},\n       {excision_sphere_B_name,\n        ExcisionSphere<3>{excision_radius,\n                          origin,\n                          {{0, Direction<3>::lower_zeta()},\n                           {1, Direction<3>::lower_zeta()},\n                           {2, Direction<3>::lower_zeta()},\n                           {3, Direction<3>::lower_zeta()},\n                           {4, Direction<3>::lower_zeta()},\n                           {5, Direction<3>::lower_zeta()}}}}}};\n\n  auto grid_center_A =\n      fake_domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  auto grid_center_B =\n      fake_domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  // Not used, but still needed for the runner\n  std::unordered_map<std::string, std::string> system_to_combined_names{};\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  // Excision centers aren't used so their values can be anything\n  MockRuntimeSystem runner{\n      {\"DummyFilename\", std::move(fake_domain), 4, true, false,\n       ::Verbosity::Silent, std::unordered_map<std::string, bool>{},\n       std::move(grid_center_A), std::move(grid_center_B),\n       std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n\n  auto& cache = ActionTesting::cache<element_component>(runner, 0);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n\n  const double check_time = 0.1;\n\n  const auto measurement_coefs = make_with_random_values<DataVector>(\n      make_not_null(&generator), coef_dist, fake_ah_coefs);\n  fake_ah_coefs = measurement_coefs;\n\n  using QueueTuple = tuples::TaggedTuple<control_system::QueueTags::Horizon<\n      Frame::Distorted, ::domain::ObjectLabel::A>>;\n  QueueTuple fake_measurement_tuple{fake_ah};\n\n  const DataVector control_error =\n      ControlError{}(::TimescaleTuner<true>{}, cache, check_time, shape_name,\n                     fake_measurement_tuple);\n\n  const auto lambda_00_coef =\n      functions_of_time.at(size_name)->func(check_time)[0][0];\n  const double Y00 = sqrt(0.25 / M_PI);\n  const auto lambda_lm_coefs =\n      functions_of_time.at(shape_name)->func(check_time)[0];\n\n  const DataVector ah_coefs_for_expected =\n      lambda_lm_coefs.size() == measurement_coefs.size()\n          ? measurement_coefs\n          : fake_ah.ylm_spherepack().prolong_or_restrict(measurement_coefs,\n                                                         lambda_spherepack);\n\n  DataVector expected_control_error =\n      -(excision_radius / Y00 - lambda_00_coef) /\n          (sqrt(0.5 * M_PI) * ah_coefs_for_expected[iter.set(0, 0)()]) *\n          ah_coefs_for_expected -\n      lambda_lm_coefs;\n  // We don't control l=0 or l=1 modes\n  for (iter.reset(); iter; ++iter) {\n    if (iter.l() == 0 or iter.l() == 1) {\n      expected_control_error[iter()] = 0.0;\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(control_error, expected_control_error);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.ControlErrors.Shape\",\n                  \"[ControlSystem][Unit]\") {\n  MAKE_GENERATOR(generator);\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  // ah_l_max < lambda_l_max (prolong), equal, and ah_l_max > lambda_l_max\n  check_shape_control_error(8, 10, generator);\n  check_shape_control_error(10, 10, generator);\n  CHECK_THROWS_WITH(\n      check_shape_control_error(12, 10, generator),\n      Catch::Matchers::ContainsSubstring(\n          \"Horizon A has l_max = 12 but the shape map has l_max = 10. The \"\n          \"horizon l_max must be less than or equal to the shape map l_max.\"));\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_SizeControlStates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <optional>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRNoDrift.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Factory.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Initial.hpp\"\n#include \"ControlSystem/ControlErrors/Size/RegisterDerivedWithCharm.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\n// Params passed into each test.\nstruct TestParams {\n  // These are reasonable values for quantities that won't change in\n  // the various logic tests.\n  const double original_target_char_speed{0.011};\n  const std::optional<double> average_radial_distance{\n      0.01};  // This is what SpEC calls DeltaR.\n  // The following means that the excision boundary radius in the grid frame\n  // is 2.01.\n  // Recall that horizon_00 is a Spherepack coefficient and not a raw\n  // spherical harmonic coefficient.\n  double horizon_00{4.02 * sqrt(2.0)};\n  double avg_distorted_normal_dot_unit_coord_vector{1.0};\n  // Defaults are values for quantities that we will vary so that the\n  // logic makes different decisions.\n  double damping_time{0.1};\n  double min_char_speed{0.01};\n  double min_comoving_char_speed{-0.02};\n  double control_err_delta_r{0.03};\n  std::optional<double> max_allowed_radial_distance{1.e100};\n  // By default here we turn off state DeltaRDriftInward\n  // by setting the following two options to nullopt.\n  std::optional<double> min_allowed_radial_distance{std::nullopt};\n  std::optional<double> min_allowed_char_speed{std::nullopt};\n  std::optional<double> inward_drift_velocity{0.005};\n  bool comoving_char_speed_increasing_inward{false};\n  control_system::size::CrossingTimeInfo crossing_time_info{\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt};\n};\n\ntemplate <typename InitialState, typename FinalState>\nvoid do_test(const TestParams& test_params,\n             const bool expected_discontinuous_change_has_occurred,\n             const std::optional<double> expected_suggested_time_scale,\n             const double expected_target_char_speed) {\n  const std::string initial_state = InitialState{}.name();\n  const std::string final_state = FinalState{}.name();\n  CAPTURE(initial_state);\n  CAPTURE(final_state);\n  CAPTURE(expected_discontinuous_change_has_occurred);\n  CAPTURE(expected_suggested_time_scale);\n  CAPTURE(expected_target_char_speed);\n  CAPTURE(test_params.original_target_char_speed);\n  CAPTURE(test_params.damping_time);\n  CAPTURE(test_params.min_char_speed);\n  CAPTURE(test_params.min_comoving_char_speed);\n  CAPTURE(test_params.control_err_delta_r);\n  CAPTURE(test_params.max_allowed_radial_distance);\n  CAPTURE(test_params.min_allowed_radial_distance);\n  CAPTURE(test_params.min_allowed_char_speed);\n  CAPTURE(test_params.inward_drift_velocity);\n  CAPTURE(test_params.crossing_time_info.char_speed_will_hit_zero_first);\n  CAPTURE(\n      test_params.crossing_time_info.horizon_will_hit_excision_boundary_first);\n  CAPTURE(test_params.crossing_time_info.t_char_speed);\n  CAPTURE(test_params.crossing_time_info.t_comoving_char_speed);\n  CAPTURE(test_params.crossing_time_info.t_delta_radius);\n  CAPTURE(test_params.crossing_time_info.t_drift_limit_delta_radius);\n  CAPTURE(test_params.crossing_time_info.t_drift_limit);\n  CAPTURE(test_params.comoving_char_speed_increasing_inward);\n  // Set reasonable values for quantities that won't change in the various\n  // logic tests.\n  const double target_drift_velocity = 0.001;\n  const std::optional<double> original_suggested_time_scale = std::nullopt;\n  const bool original_discontinuous_change_has_occurred = false;\n\n  const control_system::size::StateUpdateArgs update_args{\n      test_params.min_char_speed,\n      test_params.min_comoving_char_speed,\n      test_params.horizon_00,\n      test_params.control_err_delta_r,\n      test_params.average_radial_distance,\n      test_params.max_allowed_radial_distance,\n      test_params.avg_distorted_normal_dot_unit_coord_vector,\n      test_params.inward_drift_velocity,\n      test_params.min_allowed_radial_distance,\n      test_params.min_allowed_char_speed,\n      test_params.comoving_char_speed_increasing_inward};\n  control_system::size::Info info{\n      TestHelpers::test_factory_creation<control_system::size::State,\n                                         InitialState>(initial_state),\n      test_params.damping_time,\n      test_params.original_target_char_speed,\n      target_drift_velocity,\n      original_suggested_time_scale,\n      original_discontinuous_change_has_occurred};\n\n  // Check serialization of info\n  const auto info_copy = serialize_and_deserialize(info);\n  CHECK_FALSE(info.state == nullptr);\n  const auto info_copy2 = info_copy;\n  CHECK_FALSE(info_copy2.state == nullptr);\n  // Note that there is no equality operator for info.state, because the\n  // state contains no data; so here we check that the state can be cast to\n  // the type it should be.\n  CHECK(dynamic_cast<InitialState*>(info_copy.state.get()) != nullptr);\n  CHECK(info_copy.damping_time == info.damping_time);\n  CHECK(info_copy.target_char_speed == info.target_char_speed);\n  CHECK(info_copy.target_drift_velocity == info.target_drift_velocity);\n  CHECK(info_copy.suggested_time_scale == info.suggested_time_scale);\n  CHECK(info_copy.discontinuous_change_has_occurred ==\n        info.discontinuous_change_has_occurred);\n\n  auto state = info.state->get_clone();\n  const std::string update_message = state->update(\n      make_not_null(&info), update_args, test_params.crossing_time_info);\n  CAPTURE(update_message);\n\n  // These messages are hardcoded in the states\n  CHECK(update_message.find(\"Current state \" + initial_state) !=\n        std::string::npos);\n  CHECK(update_message.find_last_of(final_state) != std::string::npos);\n\n  CHECK(info.state.get()->number() == FinalState{}.number());\n  CHECK(info.damping_time == test_params.damping_time);\n  CHECK(info.target_char_speed == expected_target_char_speed);\n  CHECK(info.target_drift_velocity == target_drift_velocity);\n  CHECK(info.suggested_time_scale == expected_suggested_time_scale);\n  CHECK(info.discontinuous_change_has_occurred ==\n        expected_discontinuous_change_has_occurred);\n\n  info.reset();\n  CHECK(info.damping_time == test_params.damping_time);\n  CHECK(info.target_char_speed == expected_target_char_speed);\n  CHECK(info.target_drift_velocity == target_drift_velocity);\n  CHECK_FALSE(info.suggested_time_scale.has_value());\n  CHECK_FALSE(info.discontinuous_change_has_occurred);\n}\n\n// For states X=Initial and X=AhSpeed, the logic for the transition\n// from state X to state DeltaR is almost the same as the logic to\n// transition from state X to state\n// DeltaRDriftInward. test_transition_to_delta_r_inward encodes the\n// differences between ending up in state DeltaR and ending up in\n// state DeltaRDriftInward.  Every test below that ends up in state\n// DeltaR from state Initial or from state AhSpeed should call\n// test_transition_to_delta_r_inward afterwards.\ntemplate <typename InitialState>\nvoid test_transition_to_delta_r_inward(\n    TestParams test_params, const std::optional<double> suggested_time_scale,\n    const double target_char_speed) {\n  // Should_activate_inward_drift is true iff all of the following are true:\n  //  1. inward_drift_velocity has a value.\n  //  2. min_char_speed <= 0.9*min_allowed_char_speed or\n  //     min_allowed_char_speed has no value\n  //  3. avg_radial_distance <= 0.9*min_allowed_radial_distance or\n  //     min_allowed_radial_distance has no value\n  //  4. comoving_char_speed_increasing_inward is true\n  //  5. min_allowed_char_speed has a value or\n  //     min_allowed_radial_distance has a value\n  //\n  // should_transition_from_state_delta_r_to_inward_drift is true iff\n  // all of the following are true:\n  // A. should_activate_inward_drift is true\n  // B. t_drift_limit >= damping time or t_drift_limit has no value\n  //\n  // should_transition_from_state_inward_drift_to_delta_r_no_drift is\n  // true iff should_transition_from_state_delta_r_to_inward_drift is false.\n\n  // On entry to this function, 1, 2, and 3 above are true, but 4 and 5\n  // above are false.\n  // Also, on entry to this function, t_drift_limit has no value so B.\n  // is satisfied.\n\n  // Here we make 4 true, but 5 is still false.\n  // So 1,2,3,4 are true and 5 is false so we stay in state DeltaR.\n  test_params.comoving_char_speed_increasing_inward = true;\n  do_test<InitialState, control_system::size::States::DeltaR>(\n      test_params, true, suggested_time_scale, target_char_speed);\n\n  // Here we make 4 and 5 true, but 2 is now false (because limit is 0.9).\n  // So 1,3,4,5 are true and 2 is false so we stay in state DeltaR.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.91;\n  do_test<InitialState, control_system::size::States::DeltaR>(\n      test_params, true, suggested_time_scale, target_char_speed);\n\n  // Here we make 4 and 5 true, but 2 is now false (because limit is 0.9)\n  // and 3 is now false.\n  // So 1,4,5 are true and 2,3 false so we stay in state DeltaR.\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.91;\n  do_test<InitialState, control_system::size::States::DeltaR>(\n      test_params, true, suggested_time_scale, target_char_speed);\n\n  // Here 1,2,4,5 are true and 3 false so we stay in state DeltaR.\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.89;\n  do_test<InitialState, control_system::size::States::DeltaR>(\n      test_params, true, suggested_time_scale, target_char_speed);\n\n  // Now all 1,2,3,4,5 are true.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.89;\n  do_test<InitialState, control_system::size::States::DeltaRDriftInward>(\n      test_params, true, suggested_time_scale,\n      std::min(test_params.inward_drift_velocity.value(),\n               0.5 * test_params.min_char_speed /\n                   test_params.avg_distorted_normal_dot_unit_coord_vector));\n\n  // Now 4 above is false, even though 1,2,3,5 are true. So stay in State\n  // DeltaR.\n  test_params.comoving_char_speed_increasing_inward = false;\n  do_test<InitialState, control_system::size::States::DeltaR>(\n      test_params, true, suggested_time_scale, target_char_speed);\n  test_params.comoving_char_speed_increasing_inward = true;\n\n  // Now 1 above is false, even though 2,3,4,5 are true. So stay in State\n  // DeltaR.\n  test_params.inward_drift_velocity = std::nullopt;\n  do_test<InitialState, control_system::size::States::DeltaR>(\n      test_params, true, suggested_time_scale, target_char_speed);\n\n  // 1,2,3,4,5 are true, so go to state DeltaRDriftInward.  This is\n  // the same as the test above, but now the std::min in the last\n  // argument takes a different value.\n  test_params.inward_drift_velocity = 0.1;\n  do_test<InitialState, control_system::size::States::DeltaRDriftInward>(\n      test_params, true, suggested_time_scale,\n      std::min(test_params.inward_drift_velocity.value(),\n               0.5 * test_params.min_char_speed /\n                   test_params.avg_distorted_normal_dot_unit_coord_vector));\n}\n\nvoid test_size_control_update() {\n  TestParams test_params;  // With reasonable default values.\n\n  // The parameters of the tests below are chosen by hand so that the\n  // union of all the tests hit all of the 'if' statements in all of\n  // the control_system::size::State::update functions.\n  //\n  // Each of the tests below is also done in SpEC (with the same input\n  // parameters and the same expected results), to ensure that SpEC\n  // and SpECTRE have the same size control logic.\n\n  // First we do tests of state control_system::size::Label::Initial.\n\n  // should do nothing\n  do_test<control_system::size::States::Initial,\n          control_system::size::States::Initial>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // should go into DeltaR state\n  test_params.min_comoving_char_speed = 0.02;\n  do_test<control_system::size::States::Initial,\n          control_system::size::States::DeltaR>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n  test_transition_to_delta_r_inward<control_system::size::States::Initial>(\n      test_params, std::nullopt, test_params.original_target_char_speed);\n\n  // Make deltar cross zero after damping time.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 1.1 * test_params.damping_time, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::Initial,\n          control_system::size::States::DeltaR>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n  test_transition_to_delta_r_inward<control_system::size::States::Initial>(\n      test_params, std::nullopt, test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 0.9 * test_params.damping_time, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::Initial,\n          control_system::size::States::DeltaR>(\n      test_params, true, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n  test_transition_to_delta_r_inward<control_system::size::States::Initial>(\n      test_params, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time, faster than char speed.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.91 * test_params.damping_time, std::nullopt,\n      0.899 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::Initial,\n          control_system::size::States::DeltaR>(\n      test_params, true, 0.899 * test_params.damping_time,\n      test_params.original_target_char_speed);\n  test_transition_to_delta_r_inward<control_system::size::States::Initial>(\n      test_params, 0.899 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time, same as char speed.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.9 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::Initial,\n          control_system::size::States::DeltaR>(\n      test_params, true, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n  test_transition_to_delta_r_inward<control_system::size::States::Initial>(\n      test_params, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time, slower than char speed.\n  // Now it goes to state AhSpeed.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.89 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::Initial,\n          control_system::size::States::AhSpeed>(\n      test_params, true, 0.89 * test_params.damping_time,\n      test_params.min_char_speed * 1.01);\n\n  // Trigger DeltaRDriftOutward by changing max_allowed_radial_distance\n  test_params.max_allowed_radial_distance = 0.001;\n  // Make sure nothing is in danger.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n  // Comoving speed should be <0 or else we get state DeltaR and not\n  // DeltaRDriftOutward.\n  test_params.min_comoving_char_speed = -0.02;\n  do_test<control_system::size::States::Initial,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n  // Turn off DeltaRDriftOutward again.\n  test_params.max_allowed_radial_distance = std::nullopt;\n\n  // Now do DeltaR tests\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n\n  // Should stay in DeltaR.\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Should stay in DeltaR but change suggested timescale\n  // because inward_drift_limit_in_danger.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n      0.95 * test_params.damping_time);\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.98;\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.98;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, 0.95 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Should stay in DeltaR because\n  // t_drift_limit is less than damping time.\n  test_params.comoving_char_speed_increasing_inward = true;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, 0.95 * test_params.damping_time,\n      std::nullopt);\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.89;\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.89;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Exactly the same but now all the crossing times are null,\n  // so it should go to state DeltaRDriftInward with no change in timescale,\n  // but a change in target_char_speed.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaRDriftInward>(\n      test_params, true, std::nullopt,\n      std::min(test_params.inward_drift_velocity.value(),\n               0.5 * test_params.min_char_speed /\n                   test_params.avg_distorted_normal_dot_unit_coord_vector));\n\n  // Should stay in state DeltaR if either CharSpeed or DeltaR\n  // are above the min limits.\n  // So test both cases and then put back the previous values of the limits.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.91;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.89;\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.91;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.89;\n\n  // Should stay in DeltaR if comoving_char_speed_increasing_inward is false.\n  test_params.comoving_char_speed_increasing_inward = false;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Turn off DeltaRDriftInward again.\n  test_params.min_allowed_char_speed = std::nullopt;\n  test_params.min_allowed_radial_distance = std::nullopt;\n\n  // Should change suggested time scale\n  test_params.min_comoving_char_speed = 0.02;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, 0.99 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Should do nothing\n  test_params.control_err_delta_r = 1.e-4;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Make deltar cross zero *slightly* before damping time; should do\n  // nothing (depends on tolerance in control_system::size::StateDeltaR).\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 0.999 * test_params.damping_time,\n      std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 0.9 * test_params.damping_time, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time, faster than char speed.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.91 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time, same as char speed.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.9 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time, slower than char speed.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.89 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaR>(\n      test_params, false, 0.89 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Same crossing_time_info, but comoving_char_speed is negative.\n  // Should have different result as previous test.\n  test_params.min_comoving_char_speed = -0.02;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::AhSpeed>(\n      test_params, true, 0.89 * test_params.damping_time,\n      test_params.min_char_speed * 1.01);\n\n  // Same as 2 tests ago, but comoving_char_speed will cross zero far\n  // in the future.  Should be same result as previous test.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.89 * test_params.damping_time, 1.e12, 0.9 * test_params.damping_time,\n      std::nullopt, std::nullopt);\n  test_params.min_comoving_char_speed = 0.02;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::AhSpeed>(\n      test_params, true, 0.89 * test_params.damping_time,\n      test_params.min_char_speed * 1.01);\n\n  // Trigger DeltaRDriftOutward by changing max_allowed_radial_distance\n  test_params.max_allowed_radial_distance = 0.001;\n  // Make sure nothing is in danger.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n  // Comoving speed should be <0 or else we get state DeltaR and not\n  // DeltaRDriftOutward.\n  test_params.min_comoving_char_speed = -0.02;\n  do_test<control_system::size::States::DeltaR,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n\n  // Turn off DeltaRDriftOutward\n  test_params.max_allowed_radial_distance = std::nullopt;\n\n  // Now do AhSpeed tests\n  test_params.min_comoving_char_speed = -0.02;\n  test_params.control_err_delta_r = 0.03;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n\n  // Should do nothing.\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Should change to DeltaR state.\n  test_params.min_comoving_char_speed = 0.02;\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::DeltaR>(test_params, true, std::nullopt,\n                                                0.0);\n  test_transition_to_delta_r_inward<control_system::size::States::AhSpeed>(\n      test_params, std::nullopt, 0.0);\n\n  // Should do nothing because min_comoving_char_speed is smaller than\n  // min_char_speed.\n  test_params.min_comoving_char_speed = 0.99 * test_params.min_char_speed;\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Now should change to DeltaR state if min_char_speed is larger than\n  // target_char_speed\n  test_params.min_char_speed = 0.012;\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::DeltaR>(test_params, true, std::nullopt,\n                                                0.0);\n  test_transition_to_delta_r_inward<control_system::size::States::AhSpeed>(\n      test_params, std::nullopt, 0.0);\n\n  // Now it should do nothing because comoving crossing time is very small.\n  test_params.min_char_speed = 0.01;\n  test_params.min_comoving_char_speed = 0.02;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, 1.e-10, std::nullopt, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Now it should go to DeltaR because comoving crossing time is large.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, 100.0, std::nullopt, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::DeltaR>(test_params, true, std::nullopt,\n                                                0.0);\n  test_transition_to_delta_r_inward<control_system::size::States::AhSpeed>(\n      test_params, std::nullopt, 0.0);\n\n  // Now it should do nothing because comoving is decreasing faster than\n  // charspeeds.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      1000.0, 100.0, std::nullopt, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Now it should think delta_r is in danger,\n  // and it should go to DeltaR state. And it should change the damping time.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 19.0 * test_params.damping_time, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::DeltaR>(\n      test_params, true, 19.0 * test_params.damping_time, 0.0);\n  test_transition_to_delta_r_inward<control_system::size::States::AhSpeed>(\n      test_params, 19.0 * test_params.damping_time, 0.0);\n\n  // But now with comoving_char_speed negative it should stay in AhSpeed\n  // state, but with a change in target speed.\n  test_params.min_comoving_char_speed = -0.02;\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, true, test_params.damping_time,\n      0.125 * test_params.min_char_speed);\n\n  // With min_comoving_char_speed positive, it should still stay in\n  // AhSpeed state if char_speed has a positive crossing time.\n  test_params.min_comoving_char_speed = 0.02;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      1.e10, std::nullopt, 19.0 * test_params.damping_time, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, true, test_params.damping_time,\n      0.125 * test_params.min_char_speed);\n\n  // .. but not if the delta_r crossing time is small enough.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      1.e10, std::nullopt, 4.99 * test_params.damping_time, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::DeltaR>(\n      test_params, true, 4.99 * test_params.damping_time, 0.0);\n  test_transition_to_delta_r_inward<control_system::size::States::AhSpeed>(\n      test_params, 4.99 * test_params.damping_time, 0.0);\n\n  // If it thinks char speed is in danger, and the target char speed is\n  // greater than the char speed, it changes the timescale and\n  // nothing else.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.89 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, false, 0.89 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // ...but in the same situation, if char speed is greater than the\n  // target speed, it resets the target speed too.\n  test_params.min_char_speed = test_params.original_target_char_speed * 1.0001;\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, false, 0.89 * test_params.damping_time,\n      test_params.min_char_speed * 1.01);\n\n  // Same situation as previous, but char speed is *barely* in danger.\n  test_params.min_char_speed = test_params.original_target_char_speed * 1.09999;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.98999 * test_params.damping_time, std::nullopt,\n      0.99 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, false, 0.98999 * test_params.damping_time,\n      test_params.min_char_speed * 1.01);\n\n  // Same situation as previous, but char speed is *barely not* in danger,\n  // and DeltaR is also not in danger.  Should go to DeltaR state.\n  test_params.min_char_speed = test_params.original_target_char_speed * 1.10001;\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::DeltaR>(test_params, true, std::nullopt,\n                                                0.0);\n  test_transition_to_delta_r_inward<control_system::size::States::AhSpeed>(\n      test_params, std::nullopt, 0.0);\n\n  // Again char speed is *barely not* in danger, but for a different reason.\n  test_params.min_char_speed = test_params.original_target_char_speed * 1.09999;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.99001 * test_params.damping_time, std::nullopt,\n      0.992 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::DeltaR>(test_params, true, std::nullopt,\n                                                0.0);\n  test_transition_to_delta_r_inward<control_system::size::States::AhSpeed>(\n      test_params, std::nullopt, 0.0);\n\n  // Should go into state DeltaRDriftOutward.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n  test_params.min_comoving_char_speed = -0.02;\n  test_params.min_char_speed = -0.01;\n  test_params.max_allowed_radial_distance = 0.00069;\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n\n  // Should not go into state DeltaRDriftOutward because\n  // CharSpeed is not in danger and its crossing time is valid.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      100.0, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::AhSpeed,\n          control_system::size::States::AhSpeed>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Now do DeltaRDriftOutward tests\n  test_params.max_allowed_radial_distance = 0.001;\n  test_params.min_char_speed = 0.01;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n\n  // Should do nothing.\n  do_test<control_system::size::States::DeltaRDriftOutward,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Make deltar cross zero *slightly* before damping time; it should still do\n  // nothing (depends on tolerance in control_system::size::DeltaRDriftOutward).\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 0.999 * test_params.damping_time,\n      std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaRDriftOutward,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Make charspeed cross zero slightly after damping time; it should\n  // still do nothing.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      1.001 * test_params.damping_time, std::nullopt, std::nullopt,\n      std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaRDriftOutward,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Make deltar cross zero before damping time. Now it should suggest\n  // a new damping time.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 0.9 * test_params.damping_time, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::DeltaRDriftOutward,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, false, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Make deltar and charspeed cross zero before damping time, deltar\n  // faster than char speed.  Should suggest new damping time.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.91 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaRDriftOutward,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, false, 0.9 * test_params.damping_time,\n      test_params.original_target_char_speed);\n\n  // Make deltar and charspeed cross zero before damping time, deltar\n  // slower than char speed.  Should go to AhSpeed.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.89 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaRDriftOutward,\n          control_system::size::States::AhSpeed>(\n      test_params, true, 0.89 * test_params.damping_time,\n      1.01 * test_params.min_char_speed);\n\n  // Should go to state DeltaR because distance < max_allowed_radial_distance.\n  test_params.max_allowed_radial_distance = 1.e100;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n  do_test<control_system::size::States::DeltaRDriftOutward,\n          control_system::size::States::DeltaR>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n\n  // Tests for state DeltaRDriftInward.\n  // If CharSpeed not in danger and DeltaR not in danger, then we\n  // look at a few things.\n  // First we check if we should transition to state DeltaRNoDrift.  To\n  // transition to State DeltaRNoDrift, EITHER all of the following are true:\n  //  1. t_drift_limit < tdamp\n  //  2. t_drift_limit is valid\n  //  3. inward_drift_velocity is nonzero\n  // OR at least one of the following are true:\n  //  4. inward_drift_velocity is nullopt\n  //  5. min_char_speed > 0.9*min_allowed_char_speed and\n  //     min_allowed_char_speed is valid\n  //  6. avg_radial_distance > 0.9*min_allowed_radial_distance and\n  //     min_allowed_radial_distance is valid\n  //  7. comoving_char_speed_increasing_inward is false\n  //  8. min_allowed_char_speed is invalid and\n  //     min_allowed_radial_distance is invalid\n\n  // Here t_drift_limit is invalid, so 1+2+3 is false.\n  // But 8. above is true, so change to DeltaRNoDrift.\n  test_params.comoving_char_speed_increasing_inward = true;\n  test_params.min_allowed_radial_distance = std::nullopt;\n  test_params.min_allowed_char_speed = std::nullopt;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n\n  // No transition because 8 above is no longer true.\n  // (also 5 above is not true)\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.89;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRDriftInward>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // No transition because 8 above is no longer true.\n  // (also 6 above is not true)\n  test_params.min_allowed_char_speed = std::nullopt;\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.89;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRDriftInward>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Change to state DeltaRNoDrift with a timescale for t_drift_limit,\n  // because t_drift_limit is now less than damping time.\n  // Happens because 1,2,3 above are all true.\n  // Note that 8 and 6 above are still false, i.e. all of 4 thru 8 are false.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n      0.95 * test_params.damping_time);\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, true, test_params.crossing_time_info.t_drift_limit,\n      test_params.original_target_char_speed);\n\n  // Still change to State DeltaRNoDrift if CharSpeed is above the limit.\n  // Note that 1. above is false.  Now 5 is true.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n      1.2 * test_params.damping_time);\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.91;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, true, test_params.crossing_time_info.t_drift_limit,\n      test_params.original_target_char_speed);\n\n  // Still change to State DeltaRNoDrift if DeltaR is above the limit.\n  // Note that 1. above is false.  Now 5 and 6 are true.\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.91;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, true, test_params.crossing_time_info.t_drift_limit,\n      test_params.original_target_char_speed);\n\n  // Now put DeltaR below the limit. Still goes to state DeltaRNoDrift.\n  // Note that 1. above is false.  Now 6 is true, but the rest of 4-8 are false.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.89;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, true, test_params.crossing_time_info.t_drift_limit,\n      test_params.original_target_char_speed);\n\n  // Now put DeltaR below the limit.\n  // Now it doesn't transition because all of 4-8 are false,\n  // and 1 is still false.\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.89;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRDriftInward>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Goes to DeltaRNoDrift because 4 above is true.\n  test_params.inward_drift_velocity = std::nullopt;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, true, test_params.crossing_time_info.t_drift_limit,\n      test_params.original_target_char_speed);\n\n  // Goes to DeltaRNoDrift because 7 above is true.\n  test_params.inward_drift_velocity = 0.005;\n  test_params.comoving_char_speed_increasing_inward = false;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, true, test_params.crossing_time_info.t_drift_limit,\n      test_params.original_target_char_speed);\n  test_params.comoving_char_speed_increasing_inward = true;\n\n  // Now put DeltaR below the DeltaRDriftOutward limit.\n  // Should go to DeltaRDriftOutward.\n  test_params.max_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 3.5;\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n  test_params.max_allowed_radial_distance = std::nullopt;\n\n  // Now both CharSpeed and DeltaR are below the limit, but\n  // damping_time is large and t_drift_limit is active.  Now it should\n  // stay in State DeltaRDriftInward but change the timescale.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.89;\n  test_params.damping_time = 5.0;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 20.0, std::nullopt,\n      1.1 * test_params.damping_time);\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRDriftInward>(\n      test_params, false, 0.99 * test_params.damping_time,\n      std::min(test_params.inward_drift_velocity.value(),\n               0.5 * test_params.min_char_speed /\n                   test_params.avg_distorted_normal_dot_unit_coord_vector));\n  test_params.damping_time = 0.1;\n\n  // Now do DeltaRInDanger. Should stay in State DeltaRDriftInward but with\n  // different timescale.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 0.9 * test_params.damping_time, std::nullopt,\n      1.1 * test_params.damping_time);\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::DeltaRDriftInward>(\n      test_params, false, test_params.crossing_time_info.t_delta_radius,\n      test_params.original_target_char_speed);\n\n  // Now do CharSpeedInDanger. Should go to State AhSpeed with new\n  // damping time.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.89 * test_params.damping_time, std::nullopt,\n      0.9 * test_params.damping_time, std::nullopt,\n      1.1 * test_params.damping_time);\n  do_test<control_system::size::States::DeltaRDriftInward,\n          control_system::size::States::AhSpeed>(\n      test_params, true, test_params.crossing_time_info.t_char_speed,\n      test_params.min_char_speed * 1.01);\n\n  // The following tests start in state DeltaRNoDrift\n\n  // CharSpeed is in danger, but ComovingCharSpeed is positive, so\n  // stays in State DeltaRNoDrift with new timescale.\n  test_params.min_comoving_char_speed = 0.02;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.89 * test_params.damping_time, std::nullopt, std::nullopt, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, false, test_params.crossing_time_info.t_char_speed,\n      test_params.original_target_char_speed);\n\n  // CharSpeed is in danger, but ComovingCharSpeed is negative, so\n  // goes to State AhSpeed.\n  test_params.min_comoving_char_speed = -0.02;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::AhSpeed>(\n      test_params, true, test_params.crossing_time_info.t_char_speed,\n      test_params.min_char_speed * 1.01);\n\n  // CharSpeed is in danger, ComovingCharSpeed is positive,\n  // but ComovingCharSpeed is decreasing (i.e.\n  // its crossing time is positive), so\n  // goes to State AhSpeed.\n  test_params.min_comoving_char_speed = 0.02;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      0.89 * test_params.damping_time, 0.02, std::nullopt, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::AhSpeed>(\n      test_params, true, test_params.crossing_time_info.t_char_speed,\n      test_params.min_char_speed * 1.01);\n\n  // CharSpeed is in danger, ComovingCharSpeed negative,\n  // ComovingCharSpeed is decreasing (i.e.\n  // its crossing time is positive), so\n  // goes to State AhSpeed.\n  test_params.min_comoving_char_speed = -0.02;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::AhSpeed>(\n      test_params, true, test_params.crossing_time_info.t_char_speed,\n      test_params.min_char_speed * 1.01);\n\n  // DeltaRex is in danger, so stays in State DeltaRNoDrift with different\n  // timescale.\n  test_params.min_comoving_char_speed = 0.02;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, 0.7 * test_params.damping_time, std::nullopt,\n      std::nullopt);\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, false, test_params.crossing_time_info.t_delta_radius,\n      test_params.original_target_char_speed);\n\n  // To transition from DeltaRNoDrift to DeltaR, we require\n  // at least one of the following to be true:\n  // A. t_drift_limit = std::nullopt\n  // B. delta_r > 0.99 min_allowed_radial_distance\n  // C. char_speed > 0.99 min_allowed_char_speed\n  //\n  // If none of the above are true, then we stay in DeltaRNoDrift with\n  // a different timescale if both of the following are true:\n  // D. t_drift_limit < damping_time\n  // E. Either min_allowed_radial_distance or min_allowed_char_speed\n  //    has a value.\n  //\n  // If either D. or E. is false, then we stay in DeltaRNoDrift but\n  // keep the same timescale.\n\n  // A, B, and C above are false, and D. and E. are true.\n  // We stay in State DeltaRNoDrift with a different timescale.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, 0.3 * test_params.damping_time,\n      std::nullopt);\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.98;\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.98;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, false, test_params.crossing_time_info.t_drift_limit,\n      test_params.original_target_char_speed);\n\n  // A, B are false, C. is true. (D. and E. are true but do not matter here).\n  // We exit DeltaRNoDrift.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.991;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // A, C are false, B. is true. (D. and E. are true but do not matter here).\n  // We exit DeltaRNoDrift.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.98;\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.991;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // B, C are false, A. is true. (D. and E. are true but do not matter here).\n  // We exit DeltaRNoDrift.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt);\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.98;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // A, B are false, C true. (D. false and E. true).\n  // We exit DeltaRNoDrift.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.991;\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, 1.1 * test_params.damping_time,\n      std::nullopt);\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // A, C are false, B true. (D. false and E. true).\n  // We exit DeltaRNoDrift.\n  test_params.min_allowed_char_speed = test_params.min_char_speed / 0.98;\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.991;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaR>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // A, B, C are false. (D. false and E. true).\n  // We stay in State DeltaRNoDrift but with the same timescale.\n  test_params.min_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 0.98;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // A, B, C are false. (D. true and E. false).\n  // We stay in State DeltaRNoDrift but with the same timescale.\n  test_params.crossing_time_info = control_system::size::CrossingTimeInfo(\n      std::nullopt, std::nullopt, std::nullopt, 0.77 * test_params.damping_time,\n      std::nullopt);\n  test_params.min_allowed_char_speed = std::nullopt;\n  test_params.min_allowed_radial_distance = std::nullopt;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaRNoDrift>(\n      test_params, false, std::nullopt, test_params.original_target_char_speed);\n\n  // Here we want to enter state DeltaRDriftOutward.\n  test_params.max_allowed_radial_distance =\n      test_params.average_radial_distance.value() / 1.2;\n  do_test<control_system::size::States::DeltaRNoDrift,\n          control_system::size::States::DeltaRDriftOutward>(\n      test_params, true, std::nullopt, test_params.original_target_char_speed);\n}\n\nvoid test_size_control_error() {\n  // This is a very rudimentary test.  It just computes\n  // the same thing as the thing it is testing, but coded differently.\n  const control_system::size::ControlErrorArgs args{0.01, 0.03, 0.04, 1.2,\n                                                    0.33};\n  const control_system::size::Info info{\n      std::make_unique<control_system::size::States::Initial>(),\n      1.1,\n      0.011,\n      1.e-3,\n      2.e-3,\n      false};\n  CHECK(control_system::size::States::Initial{}.control_error(info, args) ==\n        -0.329);\n  CHECK(control_system::size::States::AhSpeed{}.control_error(info, args) ==\n        approx(0.001 * sqrt(4.0 * M_PI) / 1.2));\n  CHECK(control_system::size::States::DeltaR{}.control_error(info, args) ==\n        0.03);\n  CHECK(control_system::size::States::DeltaRDriftOutward{}.control_error(\n            info, args) == 0.04);\n}\n\ntemplate <typename State>\nvoid test_clone_and_serialization() {\n  std::unique_ptr<control_system::size::State> state =\n      std::make_unique<State>();\n\n  // Note that we don't check equality here.  None of the derived\n  // classes of control_system::size::State actually have data.\n  // We just check that the types are correct.\n  CHECK(dynamic_cast<State*>(serialize_and_deserialize(state).get()) !=\n        nullptr);\n\n  // Note that we don't check equality here.  None of the derived\n  // classes of control_system::size::State actually have data.\n  // We just check that the types are correct.\n  CHECK(dynamic_cast<State*>(state->get_clone().get()) != nullptr);\n}\n\nvoid test_name_and_number() {\n  const control_system::size::States::Initial initial{};\n  const control_system::size::States::AhSpeed ah_speed{};\n  const control_system::size::States::DeltaR delta_r{};\n  const control_system::size::States::DeltaRDriftInward delta_r_drift_inward{};\n  const control_system::size::States::DeltaRNoDrift delta_r_no_drift{};\n  const control_system::size::States::DeltaRDriftOutward\n      delta_r_drift_outward{};\n\n  CHECK(initial.name() == \"Initial\"s);\n  CHECK(initial.number() == 0_st);\n  CHECK(ah_speed.name() == \"AhSpeed\"s);\n  CHECK(ah_speed.number() == 1_st);\n  CHECK(delta_r.name() == \"DeltaR\"s);\n  CHECK(delta_r.number() == 2_st);\n  CHECK(delta_r_drift_inward.name() == \"DeltaRDriftInward\"s);\n  CHECK(delta_r_drift_inward.number() == 3_st);\n  CHECK(delta_r_no_drift.name() == \"DeltaRNoDrift\"s);\n  CHECK(delta_r_no_drift.number() == 4_st);\n  CHECK(delta_r_drift_outward.name() == \"DeltaRDriftOutward\"s);\n  CHECK(delta_r_drift_outward.number() == 5_st);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.SizeControlStates\", \"[Domain][Unit]\") {\n  control_system::size::register_derived_with_charm();\n  test_size_control_update();\n  test_size_control_error();\n  test_clone_and_serialization<control_system::size::States::Initial>();\n  test_clone_and_serialization<control_system::size::States::AhSpeed>();\n  test_clone_and_serialization<control_system::size::States::DeltaR>();\n  test_clone_and_serialization<\n      control_system::size::States::DeltaRDriftInward>();\n  test_clone_and_serialization<control_system::size::States::DeltaRNoDrift>();\n  test_clone_and_serialization<\n      control_system::size::States::DeltaRDriftOutward>();\n  test_name_and_number();\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_SizeError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <vector>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/ControlErrors/Size.hpp\"\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftInward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Error.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Factory.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Initial.hpp\"\n#include \"ControlSystem/ControlErrors/Size/RegisterDerivedWithCharm.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Update.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"ControlSystem/UpdateTimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/Interpolation/ZeroCrossingPredictor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Solutions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Distorted;\n}  // namespace Frame\n\nnamespace {\nconstexpr double Y00 = 0.25 * M_2_SQRTPI;\n\nstruct Metavars {\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 domain::Tags::Domain<3>, control_system::Tags::WriteDataToDisk,\n                 control_system::Tags::Verbosity>;\n  using observed_reduction_data_tags = tmpl::list<>;\n  using component_list = tmpl::list<observers::ObserverWriter<Metavars>>;\n  void pup(PUP::er& /*p*/) {}\n\n  struct factory_creation\n      : public tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<control_system::size::State,\n                   control_system::size::States::factory_creatable_states>>;\n  };\n};\n\nvoid test_control_error_delta_r() {\n  const double horizon_00 = 2.0;\n  const double dt_horizon_00 = 1.0;\n  const double lambda_00 = 3.0;\n  const double dt_lambda_00 = 4.0;\n  // This is 0 so we avoid the term with Y00 so we can get an (easy) exact\n  // calculation\n  const double grid_frame_excision_radius = 0.0;\n\n  const double control_error_delta_r =\n      control_system::size::control_error_delta_r(horizon_00, dt_horizon_00,\n                                                  lambda_00, dt_lambda_00,\n                                                  grid_frame_excision_radius);\n\n  CHECK(control_error_delta_r == approx(-2.5));\n}\n\nvoid test_size_error_horizon_higher_res_than_excision() {\n  control_system::size::Info info{\n      std::make_unique<control_system::size::States::Initial>(),\n      0.1,\n      0.0,\n      0.0,\n      std::nullopt,\n      false};\n\n  intrp::ZeroCrossingPredictor predictor_char_speed;\n  intrp::ZeroCrossingPredictor predictor_comoving_char_speed;\n  intrp::ZeroCrossingPredictor predictor_delta_radius;\n  intrp::ZeroCrossingPredictor predictor_drift_limit_char_speed;\n  intrp::ZeroCrossingPredictor predictor_drift_limit_delta_radius;\n\n  const size_t horizon_l_max = 3;\n  const size_t excision_l_max = 2;\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  const ylm::Strahlkorper<Frame::Distorted> horizon(horizon_l_max, 1.0, center);\n  const ylm::Strahlkorper<Frame::Distorted> excision_boundary(excision_l_max,\n                                                              0.9, center);\n\n  const size_t excision_size =\n      excision_boundary.ylm_spherepack().physical_size();\n\n  const Scalar<DataVector> lapse{DataVector(excision_size, 1.0)};\n  const tnsr::I<DataVector, 3, Frame::Distorted> frame_components_of_grid_shift{\n      excision_size, 0.0};\n  tnsr::ii<DataVector, 3, Frame::Distorted> spatial_metric{excision_size, 0.0};\n  tnsr::II<DataVector, 3, Frame::Distorted> inverse_spatial_metric{\n      excision_size, 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = DataVector(excision_size, 1.0);\n    inverse_spatial_metric.get(i, i) = DataVector(excision_size, 1.0);\n  }\n  const Scalar<DataVector> deriv_comoving_char_speed{\n      DataVector(excision_size, 0.0)};\n\n  CHECK_THROWS_WITH(\n      control_system::size::control_error(\n          make_not_null(&info), make_not_null(&predictor_char_speed),\n          make_not_null(&predictor_comoving_char_speed),\n          make_not_null(&predictor_delta_radius),\n          make_not_null(&predictor_drift_limit_char_speed),\n          make_not_null(&predictor_drift_limit_delta_radius), 0.0, 0.0,\n          std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n          horizon.coefficients()[0], 0.0, horizon, excision_boundary, lapse,\n          frame_components_of_grid_shift, spatial_metric,\n          inverse_spatial_metric, deriv_comoving_char_speed),\n      Catch::Matchers::ContainsSubstring(\n          \"excision boundary resolution is at least as high\"));\n}\n\ntemplate <typename InitialState, typename FinalState>\nvoid test_size_error_one_step(\n    const gsl::not_null<intrp::ZeroCrossingPredictor*> predictor_char_speed,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_comoving_char_speed,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*> predictor_delta_radius,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_drift_limit_char_speed,\n    const gsl::not_null<intrp::ZeroCrossingPredictor*>\n        predictor_drift_limit_delta_radius,\n    const double time, const double grid_excision_boundary_radius,\n    const double distorted_excision_boundary_radius_initial,\n    const double distorted_excision_boundary_velocity,\n    const double distorted_horizon_radius,\n    const double distorted_horizon_velocity, const double target_char_speed,\n    const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>&\n        function_of_time,\n    const double expected_error) {\n  const std::string initial_state = pretty_type::name<InitialState>();\n  const std::string final_state = pretty_type::name<FinalState>();\n  CAPTURE(initial_state);\n  CAPTURE(final_state);\n  const double initial_damping_time = 0.1;\n  const double initial_target_drift_velocity = 0.0;\n  const double initial_suggested_time_scale = 0.0;\n  // Set max_allowed_radial_distance so that State::DeltaRDriftOutward\n  // does not transition to DeltaR with the chosen radial_distance of 0.5\n  // below. This is fine-tuned.\n  const std::optional<double> max_allowed_radial_distance{0.049};\n  // The NOLINTs for these following 3 vars are because clang-tidy\n  // wants these to be const, but their values are changed (sometimes)\n  // by the if constexpr below.\n  std::optional<double> inward_drift_velocity{}; // NOLINT\n  std::optional<double> min_allowed_radial_distance{}; // NOLINT\n  std::optional<double> min_allowed_char_speed{}; // NOLINT\n  if constexpr (std::is_same_v<\n                    FinalState,\n                    control_system::size::States::DeltaRDriftInward>) {\n    // Set constants so that State::DeltaRDriftInward does not transition\n    // to DeltaRNoDrift for the chosen parameters in the test.\n    // This is fine-tuned.\n    min_allowed_radial_distance = 0.06;\n    inward_drift_velocity = 0.001;\n    min_allowed_char_speed = 0.068;\n  }\n  control_system::size::Info info{std::make_unique<InitialState>(),\n                                  initial_damping_time,\n                                  target_char_speed,\n                                  initial_target_drift_velocity,\n                                  initial_suggested_time_scale,\n                                  false};\n\n  const size_t l_max = 8;\n\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  ylm::Strahlkorper<Frame::Distorted> horizon(l_max, distorted_horizon_radius,\n                                              center);\n  ylm::Strahlkorper<Frame::Distorted> excision_boundary(\n      l_max, distorted_excision_boundary_radius_initial, center);\n  ylm::Strahlkorper<Frame::Distorted> time_deriv_horizon(\n      l_max, distorted_horizon_velocity, center);\n\n  // Get Cartesian coordinates on excision boundary\n  // (and other temp variables that are necessary to compute it).\n  Variables<\n      tmpl::list<::Tags::Tempi<0, 2, ::Frame::Spherical<Frame::Distorted>>,\n                 ::Tags::Tempi<1, 3, Frame::Distorted>,\n                 ::Tags::TempI<2, 3, Frame::Distorted>,\n                 ::Tags::TempI<3, 3, Frame::Distorted>, ::Tags::TempScalar<4>,\n                 Tags::TempScalar<5>>>\n      temp_buffer(excision_boundary.ylm_spherepack().physical_size());\n  auto& theta_phi =\n      get<::Tags::Tempi<0, 2, ::Frame::Spherical<Frame::Distorted>>>(\n          temp_buffer);\n  auto& r_hat = get<::Tags::Tempi<1, 3, Frame::Distorted>>(temp_buffer);\n  auto& cartesian_coords =\n      get<Tags::TempI<2, 3, Frame::Distorted>>(temp_buffer);\n  auto& shifty_quantity = get<Tags::TempI<3, 3, Frame::Distorted>>(temp_buffer);\n  auto& radius = get<::Tags::TempScalar<4>>(temp_buffer);\n  auto& deriv_comoving_char_speed = get<::Tags::TempScalar<5>>(temp_buffer);\n  ylm::Tags::ThetaPhiCompute<Frame::Distorted>::function(\n      make_not_null(&theta_phi), excision_boundary);\n  ylm::Tags::RhatCompute<Frame::Distorted>::function(make_not_null(&r_hat),\n                                                     theta_phi);\n  ylm::Tags::RadiusCompute<Frame::Distorted>::function(make_not_null(&radius),\n                                                       excision_boundary);\n  ylm::Tags::CartesianCoordsCompute<Frame::Distorted>::function(\n      make_not_null(&cartesian_coords), excision_boundary, radius, r_hat);\n\n  // Get analytic Schwarzschild solution\n  gr::Solutions::KerrSchild solution(\n      1.0, std::array<double, 3>{{0.0, 0.0, 0.0}}, center);\n  const auto vars = solution.variables(\n      cartesian_coords, time,\n      typename gr::Solutions::KerrSchild::tags<DataVector, Frame::Distorted>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, 3, Frame::Distorted>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, Frame::Distorted>>(vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Distorted>>(\n          vars);\n  const auto& spatial_christoffel = get<\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, Frame::Distorted>>(\n      vars);\n  const auto& deriv_lapse =\n      get<gr::AnalyticSolution<3>::DerivLapse<DataVector, Frame::Distorted>>(\n          vars);\n  const auto& deriv_shift =\n      get<gr::AnalyticSolution<3>::DerivShift<DataVector, Frame::Distorted>>(\n          vars);\n  // Just make it identity for this test\n  InverseJacobian<DataVector, 3, Frame::Grid, Frame::Distorted>\n      invjac_grid_to_distorted{get(lapse)};\n  get<0, 0>(invjac_grid_to_distorted) = 1.0;\n  get<1, 1>(invjac_grid_to_distorted) = 1.0;\n  get<2, 2>(invjac_grid_to_distorted) = 1.0;\n\n  // Now compute shifty quantity, which is distorted shift plus\n  // grid-to-distorted frame-velocity.\n  for (size_t i = 0; i < 3; ++i) {\n    shifty_quantity.get(i) =\n        shift.get(i) + distorted_excision_boundary_velocity *\n                           cartesian_coords.get(i) /\n                           distorted_excision_boundary_radius_initial;\n  }\n\n  // Here set deriv_comoving_char_speed to 1.0 artificially.\n  // deriv_comoving_char_speed is not used in the control error,\n  // except in determining which state to change to, in which case\n  // only the sign of deriv_comoving_char_speed matters.\n  get(deriv_comoving_char_speed) = 1.0;\n\n  const auto lambda_dt_lambda = function_of_time->func_and_deriv(time);\n  const double control_error_delta_r =\n      control_system::size::control_error_delta_r(\n          horizon.coefficients()[0], time_deriv_horizon.coefficients()[0],\n          lambda_dt_lambda[0][0], lambda_dt_lambda[1][0],\n          grid_excision_boundary_radius);\n\n  std::optional<double> control_error_delta_r_outward{};\n\n  if constexpr (std::is_same_v<\n                    FinalState,\n                    control_system::size::States::DeltaRDriftOutward>) {\n    // The 0.001 should match the value below\n    control_error_delta_r_outward =\n        control_error_delta_r + distorted_horizon_radius * 0.001 / Y00;\n  }\n\n  auto error = control_system::size::control_error(\n      make_not_null(&info), predictor_char_speed, predictor_comoving_char_speed,\n      predictor_delta_radius, predictor_drift_limit_char_speed,\n      predictor_drift_limit_delta_radius, time, control_error_delta_r,\n      control_error_delta_r_outward, max_allowed_radial_distance,\n      inward_drift_velocity, min_allowed_radial_distance,\n      min_allowed_char_speed, horizon.coefficients()[0],\n      time_deriv_horizon.coefficients()[0], horizon, excision_boundary, lapse,\n      shifty_quantity, spatial_metric, inverse_spatial_metric,\n      deriv_comoving_char_speed);\n\n  // Check error and parts of info.\n  //\n  // Note that Test_SizeControlStates does extensive tests\n  // of control_system::size::State::update() and\n  // control_system::size::State::control_error(), which are the\n  // main thing that happens inside of control_error.\n  // Here we merely check that control_error does the correct\n  // thing for a few cases.\n  CAPTURE(error.update_message);\n  CHECK(info.state.get()->number() == FinalState{}.number());\n  CHECK(error.control_error == approx(expected_error));\n\n  // Now check the control error class, but only if the initial state is Initial\n  // because the control error class is hard coded to start in the Initial state\n  if constexpr (std::is_same_v<InitialState,\n                               control_system::size::States::Initial>) {\n    using size_error =\n        control_system::ControlErrors::Size<2, domain::ObjectLabel::A>;\n    static_assert(\n        tt::assert_conforms_to_v<size_error,\n                                 control_system::protocols::ControlError>);\n\n    auto error_class = TestHelpers::test_creation<size_error, Metavars>(\n        \"MaxNumTimesForZeroCrossingPredictor: 4\\n\"\n        \"SmoothAvgTimescaleFraction: 0.25\\n\"\n        \"DeltaRDriftOutwardOptions: None\\n\"\n        \"DeltaRDriftInwardOptions: None\\n\"\n        \"InitialState: Initial\\n\"\n        \"SmootherTuner:\\n\"\n        \"  InitialTimescales: 0.2\\n\"\n        \"  MinTimescale: 1.0e-4\\n\"\n        \"  MaxTimescale: 20.0\\n\"\n        \"  IncreaseThreshold: 2.5e-4\\n\"\n        \"  DecreaseThreshold: 1.0e-3\\n\"\n        \"  IncreaseFactor: 1.01\\n\"\n        \"  DecreaseFactor: 0.98\\n\");\n\n    CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n    CHECK_FALSE(error_class.discontinuous_change_has_occurred());\n\n    TimescaleTuner<false> tuner{std::vector<double>{0.1}, 1.0, 0.01, 1.0e-4,\n                                1.01};\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time{};\n    functions_of_time[\"Size\"] = function_of_time->get_clone();\n    Domain<3> domain{\n        std::vector<Block<3>>{},\n        {{\"ExcisionSphereA\", ExcisionSphere<3>{grid_excision_boundary_radius,\n                                               tnsr::I<double, 3, Frame::Grid>{\n                                                   std::array{0.0, 0.0, 0.0}},\n                                               {}}}}};\n    Parallel::GlobalCache<Metavars> cache{\n        {std::move(functions_of_time), std::move(domain), false,\n         ::Verbosity::Silent, \"\", \"\", std::vector<std::string>{}}};\n    using ExcisionQuantities =\n        control_system::QueueTags::SizeExcisionQuantities<Frame::Distorted>;\n    using HorizonQuantities =\n        control_system::QueueTags::SizeHorizonQuantities<Frame::Distorted>;\n    tuples::TaggedTuple<ExcisionQuantities, HorizonQuantities> measurements{\n        ExcisionQuantities::type{excision_boundary, lapse, shifty_quantity,\n                                 spatial_metric, inverse_spatial_metric,\n                                 spatial_christoffel, deriv_lapse, deriv_shift,\n                                 invjac_grid_to_distorted},\n        HorizonQuantities::type{horizon, time_deriv_horizon}};\n\n    const double control_error_from_class =\n        error_class(tuner, cache, time, \"Size\"s, measurements)[0];\n    const auto control_error_history = error_class.control_error_history();\n\n    // These should be identical because the control error class calls the\n    // control_error function. The horizon smoothing averager would normally\n    // make this slightly different, but it needs a few times before it can do\n    // anything. Therefore, the smoothing aspect of the control error from class\n    // is not tested yet. So in the meantime, the initial values for the horizon\n    // coef and its derivatives are the ones specified above\n    CHECK(control_error_from_class == error.control_error);\n\n    CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n    CHECK(error_class.discontinuous_change_has_occurred() !=\n          std::is_same_v<InitialState, FinalState>);\n    // The current time is popped back so we only get times in the past\n    CHECK(control_error_history.empty());\n\n    size_error error_class_copied = error_class;\n\n    // We test the update_averager and update_timescale_tuner functions here\n    // because we already have the nice infrastructure of a cache and the\n    // control error class.\n    Averager<1> averager{0.25, true};\n    const DataVector timescale{1, 0.1};\n    // Populate the averager so it will have data\n    averager.update(time - 1.0, DataVector{1, 0.0}, timescale);\n    averager.update(time, DataVector{1, 0.0}, timescale);\n    CHECK(averager(0.0).has_value());\n\n    const bool expected_discontinuous_change =\n        error_class.discontinuous_change_has_occurred();\n    // If a discontinuous change has occurred, this call will clear the control\n    // error and the averager, then repopulate the averager with the existing\n    // control error history. However, since there is no history (checked\n    // above), the operator() of the averager will return a nullopt. If there\n    // wasn't a discontinuous change, then nothing happens\n    control_system::size::update_averager(make_not_null(&averager),\n                                          make_not_null(&error_class), cache,\n                                          time, timescale, \"Size\"s, 2);\n\n    CHECK(averager(0.0).has_value() != expected_discontinuous_change);\n\n    error_class = error_class_copied;\n\n    const DataVector old_timescale = tuner.current_timescale();\n\n    control_system::update_timescale_tuner(make_not_null(&tuner),\n                                           make_not_null(&error_class),\n                                           ::Verbosity::Silent, time, \"Size\"s);\n\n    // Since there is no suggested timescale, the tuner keeps its old timescale.\n    // However the control error is always reset.\n    CHECK(old_timescale == tuner.current_timescale());\n\n    CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n    CHECK_FALSE(error_class.discontinuous_change_has_occurred());\n    CHECK(control_error_history.empty());\n  }\n}\n\ntemplate <typename InitialState, typename FinalState>\nvoid test_size_error(const double grid_excision_boundary_radius,\n                     const double distorted_excision_boundary_radius_initial,\n                     const double distorted_excision_boundary_velocity,\n                     const double distorted_horizon_radius,\n                     const double distorted_horizon_velocity,\n                     const double target_char_speed,\n                     const double expected_error) {\n  const double initial_time = 0.0;\n  std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> function_of_time(\n      new domain::FunctionsOfTime::PiecewisePolynomial<3>(\n          initial_time,\n          std::array<DataVector, 4>{\n              {{(grid_excision_boundary_radius -\n                 distorted_excision_boundary_radius_initial) /\n                Y00},\n               {-distorted_excision_boundary_velocity / Y00},\n               {0.0},\n               {0.0}}},\n          std::numeric_limits<double>::infinity()));\n\n  intrp::ZeroCrossingPredictor predictor_char_speed;\n  intrp::ZeroCrossingPredictor predictor_comoving_char_speed;\n  intrp::ZeroCrossingPredictor predictor_delta_radius;\n  intrp::ZeroCrossingPredictor predictor_drift_limit_char_speed;\n  intrp::ZeroCrossingPredictor predictor_drift_limit_delta_radius;\n\n  test_size_error_one_step<InitialState, FinalState>(\n      make_not_null(&predictor_char_speed),\n      make_not_null(&predictor_comoving_char_speed),\n      make_not_null(&predictor_delta_radius),\n      make_not_null(&predictor_drift_limit_char_speed),\n      make_not_null(&predictor_drift_limit_delta_radius), initial_time,\n      grid_excision_boundary_radius, distorted_excision_boundary_radius_initial,\n      distorted_excision_boundary_velocity, distorted_horizon_radius,\n      distorted_horizon_velocity, target_char_speed, function_of_time,\n      expected_error);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.SizeError\", \"[Domain][Unit]\") {\n  control_system::size::register_derived_with_charm();\n  test_control_error_delta_r();\n  test_size_error_horizon_higher_res_than_excision();\n  // Should go to DeltaR state with error of zero, since ComovingMinCharSpeed\n  // will be positive.\n  test_size_error<control_system::size::States::Initial,\n                  control_system::size::States::DeltaR>(1.98, 1.98, 0.0, 2.0,\n                                                        0.0, 0.0, 0.0);\n  // Should remain in Initial state, since ComovingMinCharSpeed will\n  // be negative.  Note that the way we make ComovingMinCharSpeed negative\n  // is we put the excision boundary outside (!) the horizon, which normally\n  // should never happen but here it serves the purpose of this test.\n  test_size_error<control_system::size::States::Initial,\n                  control_system::size::States::Initial>(2.01, 2.01, 0.0, 2.0,\n                                                         0.0, 0.0, 0.0);\n  const double horizon_velocity = 0.01;\n  const double excision_velocity = 0.03;\n  const double excision_grid = 1.95;\n  const double target_char_speed = 0.05;\n  {\n    // The following is computed by hand from arxiv:1211.6079 eq. 96.\n    const double horizon_distorted = 2.0;\n    const double excision_distorted = 1.98;\n    const double expected_control_error =\n        (-horizon_velocity * 0.5 * excision_distorted + excision_velocity) /\n        Y00;\n    // Should stay in state DeltaR.\n    test_size_error<control_system::size::States::DeltaR,\n                    control_system::size::States::DeltaR>(\n        excision_grid, excision_distorted, excision_velocity, horizon_distorted,\n        horizon_velocity, target_char_speed, expected_control_error);\n  }\n  {\n    // The following is computed by hand from arxiv:1211.6079 eq. 96.\n    const double horizon_distorted = 2.0;\n    const double excision_distorted = 1.98;\n    const double expected_control_error =\n        (-horizon_velocity * 0.5 * excision_distorted + excision_velocity) /\n        Y00;\n    // Should go to state DeltaR.\n    test_size_error<control_system::size::States::DeltaRNoDrift,\n                    control_system::size::States::DeltaR>(\n        excision_grid, excision_distorted, excision_velocity, horizon_distorted,\n        horizon_velocity, target_char_speed, expected_control_error);\n  }\n\n  {\n    // The following is computed by hand from arxiv:1211.6079 eq. 97.\n    const double horizon_distorted = 2.0;\n    const double excision_distorted = 1.95;\n    const double drift_velocity = 0.001;\n    const double expected_control_error =\n        (-horizon_velocity * 0.5 * excision_distorted + excision_velocity +\n         horizon_distorted * drift_velocity) /\n        Y00;\n    // Should stay in state DeltaRDriftOutward.\n    test_size_error<control_system::size::States::DeltaRDriftOutward,\n                    control_system::size::States::DeltaRDriftOutward>(\n        excision_grid, excision_distorted, excision_velocity, horizon_distorted,\n        horizon_velocity, target_char_speed, expected_control_error);\n  }\n\n  {\n    // The following is computed by hand from arxiv:1211.6079 eq. 96 plus\n    // the target char speed.\n    const double horizon_distorted = 2.0;\n    const double excision_distorted = 1.95;\n    const double expected_control_error =\n        (-horizon_velocity * 0.5 * excision_distorted + excision_velocity) /\n            Y00 +\n        target_char_speed;\n    // Should stay in state DeltaRDriftInward.\n    test_size_error<control_system::size::States::DeltaRDriftInward,\n                    control_system::size::States::DeltaRDriftInward>(\n        excision_grid, excision_distorted, excision_velocity, horizon_distorted,\n        horizon_velocity, target_char_speed, expected_control_error);\n  }\n\n  {\n    // The following is computed by hand from arxiv:1211.6079 eq. 92\n    // and the Schwarzshild solution in Kerr-schild coords.\n    const double horizon_distorted = 2.0;\n    const double excision_distorted = 2.0;\n    const double mass = 1.0;  // hardcoded in test.\n    const double normal_radial_one_form_mag =\n        sqrt(1.0 + 2.0 * mass / excision_distorted);\n    const double lapse = 1.0 / sqrt(1.0 + 2.0 * mass / excision_distorted);\n    const double radial_shift_mag = 2.0 * mass / excision_distorted /\n                                    (1.0 + 2.0 * mass / excision_distorted);\n    const double shifty_quantity_mag = radial_shift_mag + excision_velocity;\n    const double char_speed =\n        -lapse + normal_radial_one_form_mag * shifty_quantity_mag;\n    const double expected_control_error =\n        (char_speed - target_char_speed) / (Y00 * normal_radial_one_form_mag);\n    // Should stay in state AhSpeed.\n    test_size_error<control_system::size::States::AhSpeed,\n                    control_system::size::States::AhSpeed>(\n        excision_grid, excision_distorted, excision_velocity, horizon_distorted,\n        horizon_velocity, target_char_speed, expected_control_error);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_Skew.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n\n#include \"ControlSystem/ControlErrors/Skew.hpp\"\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"ControlSystem/UpdateTimescaleTuner.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Norms.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavars {\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 domain::Tags::ObjectCenter<domain::ObjectLabel::A>,\n                 domain::Tags::ObjectCenter<domain::ObjectLabel::B>>;\n  using observed_reduction_data_tags = tmpl::list<>;\n  using component_list = tmpl::list<observers::ObserverWriter<Metavars>>;\n  void pup(PUP::er& /*p*/) {}\n};\n\nconstexpr double grid_x_coord = 6.0;\nconstexpr size_t l_max = 8;\nconstexpr double radius = 0.5;\n\nvoid test_skew(const double time,\n               const ylm::Strahlkorper<Frame::Distorted>& horizon_a,\n               const ylm::Strahlkorper<Frame::Distorted>& horizon_b,\n               const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>&\n                   function_of_time,\n               const DataVector& expected_error,\n               const std::optional<double>& expected_timescale,\n               const std::optional<Approx>& custom_approx = std::nullopt) {\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"Skew\"] = function_of_time->get_clone();\n  const std::array<double, 3> grid_center_a{{grid_x_coord, 0.0, 0.0}};\n  const std::array<double, 3> grid_center_b{{-grid_x_coord, 0.0, 0.0}};\n  const Parallel::GlobalCache<Metavars> cache{\n      {std::move(functions_of_time),\n       tnsr::I<double, 3, Frame::Grid>{grid_center_a},\n       tnsr::I<double, 3, Frame::Grid>{grid_center_b}, \"\", \"\",\n       std::vector<std::string>{}}};\n  using HorizonA = control_system::QueueTags::Horizon<Frame::Distorted,\n                                                      ::domain::ObjectLabel::A>;\n  using HorizonB = control_system::QueueTags::Horizon<Frame::Distorted,\n                                                      ::domain::ObjectLabel::B>;\n  const tuples::TaggedTuple<HorizonA, HorizonB> measurements{horizon_a,\n                                                             horizon_b};\n\n  using SkewError = control_system::ControlErrors::Skew;\n\n  const auto error_class_creation =\n      TestHelpers::test_creation<SkewError, Metavars>(\"\");\n  SkewError error_class = serialize_and_deserialize(error_class_creation);\n\n  CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n  error_class.reset();\n  CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n\n  TimescaleTuner<true> tuner{std::vector<double>{20.0, 20.0}, 20.0, 0.01,\n                             1.0e-4, 1.01};\n\n  auto approx_to_use = custom_approx.value_or(approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      error_class(tuner, cache, time, \"Skew\", measurements), expected_error,\n      approx_to_use);\n\n  REQUIRE(error_class.get_suggested_timescale().has_value() ==\n          expected_timescale.has_value());\n  if (expected_timescale.has_value()) {\n    CHECK(error_class.get_suggested_timescale().value() ==\n          approx(expected_timescale.value()));\n  }\n\n  const DataVector old_timescale = tuner.current_timescale();\n\n  control_system::update_timescale_tuner(make_not_null(&tuner),\n                                         make_not_null(&error_class),\n                                         ::Verbosity::Silent, time, \"Skew\"s);\n\n  if (expected_timescale.has_value()) {\n    CHECK(old_timescale != tuner.current_timescale());\n  } else {\n    CHECK(old_timescale == tuner.current_timescale());\n  }\n  error_class.reset();\n  CHECK_FALSE(error_class.get_suggested_timescale().has_value());\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.ControlErrors.Skew\",\n                  \"[ControlSystem][Unit]\") {\n  const double initial_time = 0.0;\n  const double expiration_time = 5.0;\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>\n      function_of_time =\n          std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n              initial_time, make_array<3>(DataVector{2, 0.0}), expiration_time);\n\n  const std::array<double, 3> horizon_center_a{grid_x_coord, 0.0, 0.0};\n  const std::array<double, 3> horizon_center_b{-grid_x_coord, 0.0, 0.0};\n  {\n    ylm::Strahlkorper<Frame::Distorted> horizon_a(l_max, radius,\n                                                  horizon_center_a);\n    ylm::Strahlkorper<Frame::Distorted> horizon_b(l_max, radius,\n                                                  horizon_center_b);\n\n    // Not activated\n    test_skew(initial_time, horizon_a, horizon_b, function_of_time,\n              DataVector{2, 0.0}, std::nullopt);\n\n    horizon_a = ylm::Strahlkorper<Frame::Distorted>(l_max, 5.0 * radius,\n                                                    horizon_center_a);\n    horizon_b = ylm::Strahlkorper<Frame::Distorted>(l_max, 5.0 * radius,\n                                                    horizon_center_b);\n\n    // Activated, but error is still zero because the horizon isn't distorted.\n    // However, we do have a suggested timescale now\n    test_skew(initial_time, horizon_a, horizon_b, function_of_time,\n              DataVector{2, 0.0}, {6.0});\n  }\n\n  // Create surface that is just a sphere that's shifted by a constant offset,\n  // aka \\vec{r}' = \\vec{r} + \\vec{c} where c is the center shift away from the\n  // original center\n  {\n    // Need high resolution because of the wonky shape of the surface we are\n    // creating\n    const size_t l_max_high_res = 6 * l_max;\n    ylm::Strahlkorper<Frame::Distorted> horizon_a{\n        l_max_high_res, 5.0 * radius, {0.0, 0.0, 0.0}};\n    auto coords = ylm::cartesian_coords(horizon_a);\n    const std::array<double, 3> center_offset{0.0, 0.0,\n                                              5.0 * radius / sqrt(2.0)};\n    // Recenter the coords\n    for (size_t i = 0; i < 3; i++) {\n      coords.get(i) -= gsl::at(center_offset, i);\n    }\n    const auto radii = pointwise_l2_norm(coords);\n\n    // Redistribute theta so that theta=pi/2 is along the x-axis again\n    const auto& ylm = horizon_a.ylm_spherepack();\n    const auto original_theta_phis = ylm.theta_phi_points();\n    const std::array<DataVector, 2> new_theta_phis{\n        square(original_theta_phis[0]) / M_PI, original_theta_phis[1]};\n\n    // Interpolate shifted coordinates to these new theta/phi points\n    const auto interpolation_info =\n        ylm.set_up_interpolation_info(new_theta_phis);\n    std::array<DataVector, 3> new_coords{get<0>(coords), get<1>(coords),\n                                         get<2>(coords)};\n    for (size_t i = 0; i < 3; i++) {\n      ylm.interpolate(make_not_null(&gsl::at(new_coords, i)),\n                      make_not_null(coords.get(i).data()), interpolation_info);\n    }\n    const auto new_radii = magnitude(new_coords);\n\n    horizon_a = ylm::Strahlkorper<Frame::Distorted>(\n        l_max_high_res, l_max_high_res, new_radii, horizon_center_a);\n\n    const ylm::Strahlkorper<Frame::Distorted> horizon_b{\n        l_max_high_res, 5.0 * radius, horizon_center_b};\n\n    // These numbers are computed by hand give the center offset above\n    const double weight_a = exp(5.0 / (12.0 * sqrt(2.0)) - 1.0);\n    const DataVector expected_control_error{\n        0.0, -0.5 * (1.0 - tanh((19.0 - 5.0 / sqrt(2.0)) / 2.4 - 5.0)) *\n                 weight_a * M_PI_4 / (weight_a + exp(-7.0 / 12.0))};\n\n    // Because of all the interpolation, our result isn't as accurate, even with\n    // the high LMax resolution\n    const Approx custom_approx = Approx::custom().epsilon(1.0e-6).scale(1.0);\n    test_skew(initial_time, horizon_a, horizon_b, function_of_time,\n              expected_control_error, {6.0}, {custom_approx});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_StateHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <deque>\n#include <numeric>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"ControlSystem/ControlErrors/Size/AhSpeed.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaR.hpp\"\n#include \"ControlSystem/ControlErrors/Size/DeltaRDriftOutward.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Info.hpp\"\n#include \"ControlSystem/ControlErrors/Size/Initial.hpp\"\n#include \"ControlSystem/ControlErrors/Size/RegisterDerivedWithCharm.hpp\"\n#include \"ControlSystem/ControlErrors/Size/StateHistory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace control_system::size {\nnamespace {\nvoid test_state_history(const size_t num_times_to_store) {\n  CAPTURE(num_times_to_store);\n  Info info{\n      std::make_unique<States::Initial>(), 1.0, 1.0, 1.0, std::nullopt, false};\n  ControlErrorArgs control_error_args{1.0, 1.0, 1.0, 1.0, 1.0};\n\n  StateHistory state_history{num_times_to_store};\n  const std::vector<size_t> states{0, 1, 2, 3, 4, 5};\n\n  // Test that as we fill up the history, we have the expected number of stored\n  // entries and that they are the correct values, for each state\n  for (size_t i = 0; i < num_times_to_store; i++) {\n    const double time = static_cast<double>(i);\n    state_history.store(time, info, control_error_args);\n\n    for (const size_t state : states) {\n      CAPTURE(state);\n      const auto history = state_history.state_history(state);\n      CHECK(history.size() == i + 1);\n      for (size_t j = 0; j < history.size(); j++) {\n        double stored_time, control_error;\n        std::tie(stored_time, control_error) = history[j];\n        CHECK(static_cast<double>(j) == stored_time);\n        // These are hand calculated from the above parameters Info and\n        // ControlErrorArgs.\n        switch (state) {\n          case 0:\n            CHECK(control_error == 0.0);\n            break;\n          case 1:\n            CHECK(control_error == 0.0);\n            break;\n          case 2:\n            CHECK(control_error == 1.0);\n            break;\n          case 3:\n            CHECK(control_error == 2.0);\n            break;\n          case 4:\n            CHECK(control_error == 1.0);\n            break;\n          case 5:\n            CHECK(control_error == 1.0);\n            break;\n          default:\n            ERROR(\"Unknown state: \" << state);\n        }\n      }\n    }\n  }\n\n  // Test that trying to store one more value causes us to pop off the initial\n  // (first) value and add this new one to the end (last), while keeping the\n  // total number of entries at num_times_to_store\n  state_history.store(static_cast<double>(num_times_to_store), info,\n                      control_error_args);\n  for (const size_t state : states) {\n    const auto history = state_history.state_history(state);\n    CHECK(history.size() == num_times_to_store);\n    CHECK(history.front().first == 1.0);\n    CHECK(history.back().first == static_cast<double>(num_times_to_store));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.ControlErrors.StateHistory\",\n                  \"[Domain][Unit]\") {\n  control_system::size::register_derived_with_charm();\n  for (size_t num_times = 1; num_times < 5; num_times++) {\n    test_state_history(num_times);\n  }\n}\n}  // namespace control_system::size\n"
  },
  {
    "path": "tests/Unit/ControlSystem/ControlErrors/Test_Translation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"ControlSystem/ControlErrors/Expansion.hpp\"\n#include \"ControlSystem/ControlErrors/Rotation.hpp\"\n#include \"ControlSystem/ControlErrors/Translation.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system {\nnamespace {\nvoid test_translation_control_error() {\n  // Since we are only doing translation, turn off the\n  // other control systems by passing 0 for their deriv orders\n  constexpr size_t deriv_order = 2;\n  using metavars =\n      TestHelpers::control_system::MockMetavars<deriv_order, 0, 0, 0>;\n  using element_component = typename metavars::element_component;\n  using translation_system = typename metavars::translation_system;\n\n  // Global things\n  domain::FunctionsOfTime::register_derived_with_charm();\n  const double initial_time = 0.0;\n  const double initial_separation = 15.0;\n\n  // Set up the system helper.\n  TestHelpers::control_system::SystemHelper<metavars> system_helper{};\n\n  const std::string input_options =\n      \"Evolution:\\n\"\n      \"  InitialTime: 0.0\\n\"\n      \"DomainCreator:\\n\"\n      \"  FakeCreator:\\n\"\n      \"    NumberOfComponents:\\n\"\n      \"      Translation: 3\\n\"\n      \"ControlSystems:\\n\"\n      \"  WriteDataToDisk: false\\n\"\n      \"  MeasurementsPerUpdate: 4\\n\"\n      \"  DelayUpdate: true\\n\"\n      \"  Translation:\\n\"\n      \"    Averager:\\n\"\n      \"      AverageTimescaleFraction: 0.25\\n\"\n      \"      Average0thDeriv: true\\n\"\n      \"    Controller:\\n\"\n      \"      UpdateFraction: 0.3\\n\"\n      \"    TimescaleTuner:\\n\"\n      \"      InitialTimescales: [0.5, 0.5, 0.5]\\n\"\n      \"      MinTimescale: 0.1\\n\"\n      \"      MaxTimescale: 10.\\n\"\n      \"      DecreaseThreshold: 2.0\\n\"\n      \"      IncreaseThreshold: 0.1\\n\"\n      \"      IncreaseFactor: 1.01\\n\"\n      \"      DecreaseFactor: 0.99\\n\"\n      \"    ControlError:\\n\";\n\n  // Initialize everything within the system helper\n  system_helper.setup_control_system_test(\n      initial_time, initial_separation, input_options,\n      TestHelpers::control_system::initialize_translation_functions_of_time<\n          translation_system>);\n\n  // Get references to everything that was set up inside the system helper. The\n  // domain and two functions of time are not const references because they need\n  // to be moved into the runner\n  auto& domain = system_helper.domain();\n  auto& is_active_map = system_helper.is_active_map();\n  auto& initial_functions_of_time = system_helper.initial_functions_of_time();\n  auto& initial_measurement_timescales =\n      system_helper.initial_measurement_timescales();\n  auto system_to_combined_names = system_helper.system_to_combined_names();\n  const std::string translation_name =\n      system_helper.template name<translation_system>();\n\n  auto grid_center_A = domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  auto grid_center_B = domain.excision_spheres().at(\"ExcisionSphereB\").center();\n\n  // Setup runner and element component because it's the easiest way to get the\n  // global cache\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {\"DummyFileName\", std::move(domain), 4, true, false, ::Verbosity::Silent,\n       std::move(is_active_map), std::move(grid_center_A),\n       std::move(grid_center_B), std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n  const auto& cache = ActionTesting::cache<element_component>(runner, 0);\n\n  using QueueTuple = tuples::TaggedTuple<\n      control_system::QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n      control_system::QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>>;\n\n  // Create fake measurements.\n  const DataVector pos_A{{2.0, 3.0, 6.0}};\n  const DataVector pos_B{{-3.0, -4.0, 5.0}};\n  const DataVector grid_A{{initial_separation / 2.0, 0.0, 0.0}};\n  const DataVector grid_B{{-initial_separation / 2.0, 0.0, 0.0}};\n  QueueTuple fake_measurement_tuple{pos_A, pos_B};\n\n  const DataVector grid_position_average = 0.5 * (grid_A + grid_B);\n  const DataVector current_position_average = 0.5 * (pos_A + pos_B);\n\n  const DataVector grid_separation = grid_A - grid_B;\n  const DataVector current_separation = pos_A - pos_B;\n\n  double current_separation_dot_grid_separation = 0.0;\n  double current_separation_dot_grid_average = 0.0;\n  double grid_separation_dot_grid_average = 0.0;\n  double grid_separation_dot_grid_separation = 0.0;\n  for (size_t i = 0; i < 3; i++) {\n    current_separation_dot_grid_separation +=\n        current_separation[i] * grid_separation[i];\n    current_separation_dot_grid_average +=\n        current_separation[i] * grid_position_average[i];\n    grid_separation_dot_grid_average +=\n        grid_separation[i] * grid_position_average[i];\n    grid_separation_dot_grid_separation +=\n        grid_separation[i] * grid_separation[i];\n  }\n\n  using ControlError = translation_system::control_error;\n\n  // This is before the first expiration time\n  const double check_time = 0.1;\n  const DataVector control_error =\n      ControlError{}(::TimescaleTuner<true>{}, cache, check_time,\n                     translation_name, fake_measurement_tuple);\n\n  // The quaternion should be the unit quaternion (1,0,0,0) which means the\n  // quaternion multiplication in the translation control error is the identity\n  // so we avoid actually doing quaternion multiplication. Also the expansion\n  // factor should be 1.0 so we don't have to multiply/divide by that where we\n  // normally would have\n  const DataVector expected_control_error =\n      (grid_separation_dot_grid_separation * current_position_average -\n       current_separation_dot_grid_separation * grid_position_average -\n       grid_separation_dot_grid_average * current_separation +\n       current_separation_dot_grid_average * grid_separation) /\n      grid_separation_dot_grid_separation;\n  ;\n\n  Approx custom_approx = Approx::custom().epsilon(1.0e-14).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(control_error, expected_control_error,\n                               custom_approx);\n}\n\nstruct SingleMetavars {\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n  using component_list = tmpl::list<>;\n};\n\nvoid test_single_object_translation_control_error() {\n  using ControlError = ControlErrors::Translation<1>;\n  using QueueTag =\n      control_system::QueueTags::Center<::domain::ObjectLabel::None,\n                                        Frame::Grid>;\n  using QueueTuple = tuples::TaggedTuple<QueueTag>;\n  using CacheType = Parallel::GlobalCache<SingleMetavars>;\n\n  const TimescaleTuner<true> unused_tuner{};\n  // Along x-axis makes things easier to test\n  const QueueTuple fake_measurement_tuple{DataVector{-0.2, 0.0, 0.0}};\n\n  const double check_time = 0.1;\n  const std::string function_of_time_name{\"Translation\"};\n\n  {\n    const CacheType cache{{domain::FunctionsOfTimeMap{}}};\n\n    const DataVector control_error =\n        ControlError{}(unused_tuner, cache, check_time, function_of_time_name,\n                       fake_measurement_tuple);\n    const DataVector& expected_control_error =\n        tuples::get<QueueTag>(fake_measurement_tuple);\n    CHECK(control_error == expected_control_error);\n  }\n\n  {\n    domain::FunctionsOfTimeMap functions_of_time{};\n    functions_of_time[\"Expansion\"] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n            0.0, std::array{DataVector{0.5}}, check_time * 10.0);\n    const CacheType cache{{std::move(functions_of_time)}};\n\n    const DataVector control_error =\n        ControlError{}(unused_tuner, cache, check_time, function_of_time_name,\n                       fake_measurement_tuple);\n    const DataVector expected_control_error =\n        0.5 * tuples::get<QueueTag>(fake_measurement_tuple);\n    CHECK(control_error == expected_control_error);\n  }\n\n  {\n    domain::FunctionsOfTimeMap functions_of_time{};\n    functions_of_time[\"Expansion\"] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n            0.0, std::array{DataVector{0.5}}, check_time * 10.0);\n    functions_of_time[\"Rotation\"] =\n        std::make_unique<domain::FunctionsOfTime::QuaternionFunctionOfTime<2>>(\n            0.0, std::array{DataVector{1.0, 0.0, 0.0, 0.0}},\n            std::array{DataVector{3, 0.0}, DataVector{0.0, 0.0, 1.5},\n                       DataVector{3, 0.0}},\n            check_time * 10.0);\n    const CacheType cache{{std::move(functions_of_time)}};\n\n    const DataVector control_error =\n        ControlError{}(unused_tuner, cache, check_time, function_of_time_name,\n                       fake_measurement_tuple);\n    // Rotation and expand\n    const DataVector expected_control_error =\n        0.5 * -0.2 * DataVector{cos(0.1 * 1.5), sin(0.1 * 1.5), 0.0};\n    CHECK_ITERABLE_APPROX(control_error, expected_control_error);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.ControlErrors.Translation\",\n                  \"[ControlSystem][Unit]\") {\n  test_translation_control_error();\n  test_single_object_translation_control_error();\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Protocols/Test_Protocols.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Protocols/Test_Protocols.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"Helpers/ControlSystem/Examples.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::TestHelpers::ExampleSubmeasurement,\n                             control_system::protocols::Submeasurement>);\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::TestHelpers::ExampleMeasurement,\n                             control_system::protocols::Measurement>);\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::TestHelpers::ExampleControlSystem,\n                             control_system::protocols::ControlSystem>);\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::TestHelpers::ExampleControlError,\n                             control_system::protocols::ControlError>);\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Systems/Test_Expansion.cpp\n  Systems/Test_GridCenters.cpp\n  Systems/Test_Shape.cpp\n  Systems/Test_Size.cpp\n  Systems/Test_Skew.cpp\n  Systems/Test_Rotation.cpp\n  Systems/Test_RotScaleTrans.cpp\n  Systems/Test_Translation.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/Test_Expansion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"ControlSystem/Systems/Expansion.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/PostNewtonian/BinaryTrajectories.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace control_system {\nnamespace {\nusing ExpansionMap = domain::CoordinateMaps::TimeDependent::CubicScale<3>;\n\nusing CoordMap =\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, ExpansionMap>;\n\ntemplate <size_t DerivOrder>\nvoid test_expansion_control_system() {\n  using metavars =\n      TestHelpers::control_system::MockMetavars<0, 0, DerivOrder, 0>;\n  using expansion_component = typename metavars::expansion_component;\n  using element_component = typename metavars::element_component;\n  using observer_component = typename metavars::observer_component;\n  using expansion_system = typename metavars::expansion_system;\n  MAKE_GENERATOR(gen);\n\n  // Global things\n  domain::FunctionsOfTime::register_derived_with_charm();\n  const double initial_time = 0.0;\n  const double initial_separation = 15.0;\n  // This final time is chosen so that the damping timescales have adequate time\n  // to reach the maximum damping timescale\n  const double final_time = 500.0;\n\n  // Set up the system helper.\n  TestHelpers::control_system::SystemHelper<metavars> system_helper{};\n\n  const std::string input_options =\n      \"Evolution:\\n\"\n      \"  InitialTime: 0.0\\n\"\n      \"DomainCreator:\\n\"\n      \"  FakeCreator:\\n\"\n      \"    NumberOfComponents:\\n\"\n      \"      Expansion: 1\\n\"\n      \"ControlSystems:\\n\"\n      \"  WriteDataToDisk: false\\n\"\n      \"  MeasurementsPerUpdate: 4\\n\"\n      \"  DelayUpdate: true\\n\"\n      \"  Expansion:\\n\"\n      \"    Averager:\\n\"\n      \"      AverageTimescaleFraction: 0.25\\n\"\n      \"      Average0thDeriv: true\\n\"\n      \"    Controller:\\n\"\n      \"      UpdateFraction: 0.3\\n\"\n      \"    TimescaleTuner:\\n\"\n      \"      InitialTimescales: [0.5]\\n\"\n      \"      MinTimescale: 0.1\\n\"\n      \"      MaxTimescale: 10.\\n\"\n      \"      DecreaseThreshold: 2.0\\n\"\n      \"      IncreaseThreshold: 0.1\\n\"\n      \"      IncreaseFactor: 1.01\\n\"\n      \"      DecreaseFactor: 0.99\\n\"\n      \"    ControlError:\\n\";\n\n  // Initialize everything within the system helper\n  system_helper.setup_control_system_test(\n      initial_time, initial_separation, input_options,\n      TestHelpers::control_system::initialize_expansion_functions_of_time<\n          expansion_system>);\n\n  // Get references to everything that was set up inside the system helper. The\n  // domain and two functions of time are not const references because they need\n  // to be moved into the runner\n  auto& domain = system_helper.domain();\n  auto& is_active_map = system_helper.is_active_map();\n  auto& initial_functions_of_time = system_helper.initial_functions_of_time();\n  auto& initial_measurement_timescales =\n      system_helper.initial_measurement_timescales();\n  const auto& init_exp_tuple =\n      system_helper.template init_tuple<expansion_system>();\n  auto system_to_combined_names = system_helper.system_to_combined_names();\n\n  auto grid_center_A = domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  auto grid_center_B = domain.excision_spheres().at(\"ExcisionSphereB\").center();\n\n  // Setup runner and all components\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {\"DummyFileName\", std::move(domain), 4, true, false, ::Verbosity::Silent,\n       std::move(is_active_map), std::move(grid_center_A),\n       std::move(grid_center_B), std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_singleton_component_and_initialize<\n      expansion_component>(make_not_null(&runner), ActionTesting::NodeId{0},\n                           ActionTesting::LocalCoreId{0}, init_exp_tuple);\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n  ActionTesting::emplace_nodegroup_component<observer_component>(\n      make_not_null(&runner));\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const BinaryTrajectories binary_trajectories{initial_separation};\n\n  const std::string expansion_name =\n      system_helper.template name<expansion_system>();\n\n  // Create coordinate maps for mapping the PN expansion to the \"grid\" frame\n  // where the control system does its calculations\n  // The outer boundary is at 1000.0 so that we don't have to worry about it.\n  ExpansionMap expansion_map{1000.0, expansion_name,\n                             expansion_name + \"OuterBoundary\"s};\n\n  CoordMap coord_map{expansion_map};\n\n  // Get the functions of time from the cache to use in the maps\n  const auto& cache = ActionTesting::cache<element_component>(runner, 0);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n\n  const auto position_function = [&binary_trajectories](const double time) {\n    const double separation = binary_trajectories.separation(time);\n    std::array<tnsr::I<double, 3>, 2> result{};\n    get<0>(result[0]) = 0.5 * separation;\n    get<1>(result[0]) = 0.0;\n    get<2>(result[0]) = 0.0;\n    get<0>(result[1]) = -0.5 * separation;\n    get<1>(result[1]) = 0.0;\n    get<2>(result[1]) = 0.0;\n    return result;\n  };\n\n  const auto horizon_function = [&position_function, &runner,\n                                 &coord_map](const double time) {\n    return TestHelpers::control_system::\n        build_horizons_for_basic_control_systems<element_component>(\n            time, runner, position_function, coord_map);\n  };\n\n  // Run the actual control system test.\n  system_helper.run_control_system_test(runner, final_time, make_not_null(&gen),\n                                        horizon_function);\n\n  // Grab results\n  const auto grid_positions = TestHelpers::control_system::\n      grid_frame_horizon_centers_for_basic_control_systems<element_component>(\n          final_time, runner, position_function, coord_map);\n\n  // Our expected positions are just the initial positions\n  const tnsr::I<double, 3, Frame::Grid> expected_grid_position_of_a{\n      {0.5 * initial_separation, 0.0, 0.0}};\n  const tnsr::I<double, 3, Frame::Grid> expected_grid_position_of_b{\n      {-0.5 * initial_separation, 0.0, 0.0}};\n\n  const auto& expansion_f_of_t =\n      dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>&>(\n          *functions_of_time.at(expansion_name));\n\n  const double exp_factor = expansion_f_of_t.func(final_time)[0][0];\n\n  // The control system gets more accurate the longer you run for. However, this\n  // is the floor of our accuracy for a changing function of time.\n  Approx custom_approx = Approx::custom().epsilon(5.0e-6).scale(1.0);\n  const double expected_exp_factor =\n      binary_trajectories.separation(final_time) / initial_separation;\n  CHECK(custom_approx(expected_exp_factor) == exp_factor);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_grid_position_of_a, grid_positions[0],\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_grid_position_of_b, grid_positions[1],\n                               custom_approx);\n}\n\nvoid test_names() {\n  using expansion = control_system::Systems::Expansion<\n      2, control_system::measurements::BothHorizons>;\n\n  CHECK(pretty_type::name<expansion>() == \"Expansion\");\n  CHECK(*expansion::component_name(0, 1) == \"Expansion\");\n  CHECK(*expansion::component_name(1, 1) == \"Expansion\");\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::string component_name = *expansion::component_name(1, 2);\n        (void)component_name;\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Expansion control expects 1 component but there are 2 instead.\"));\n#endif  // SPECTRE_DEBUG\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Systems.Expansion\",\n                  \"[ControlSystem][Unit]\") {\n  test_names();\n  test_expansion_control_system<2>();\n  test_expansion_control_system<3>();\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/Test_GridCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/GridCenters.hpp\"\n#include \"ControlSystem/Measurements/BNSCenterOfMass.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Systems/GridCenters.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace control_system {\nnamespace {\nusing both_centers = control_system::measurements::BothNSCenters;\nusing GridCenters = control_system::Systems::GridCenters<2, both_centers>;\nstatic_assert(tt::assert_conforms_to_v<\n              GridCenters, control_system::protocols::ControlSystem>);\nusing measurement_queue = GridCenters::MeasurementQueue;\n\nusing all_tags = measurement_queue::type::queue_tags_list;\n\nsize_t message_queue_call_count = 0;  // NOLINT\n\ntemplate <typename LinkedMessageQueueTag, typename Processor,\n          typename... QueueTags>\nstruct MockUpdateMessageQueue {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTags>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const LinkedMessageId<typename LinkedMessageQueueTag::type::IdType>&\n      /*id_and_previous*/,\n      typename QueueTags::type... /*message*/) {\n    ++message_queue_call_count;\n  }\n};\n\n// The Nvidia compiler crashes if we define these lists inside the MockComponent\n// struct.\nusing replace_these_simple_actions_mock_component =\n    tmpl::list<::Actions::UpdateMessageQueue<\n        measurement_queue, control_system::UpdateControlSystem<GridCenters>,\n        QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n        QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>,\n        QueueTags::Center<::domain::ObjectLabel::A, Frame::Inertial>,\n        QueueTags::Center<::domain::ObjectLabel::B, Frame::Inertial>>>;\nusing with_these_simple_actions_mock_component =\n    tmpl::list<MockUpdateMessageQueue<\n        measurement_queue, control_system::UpdateControlSystem<GridCenters>,\n        QueueTags::Center<::domain::ObjectLabel::A, Frame::Grid>,\n        QueueTags::Center<::domain::ObjectLabel::B, Frame::Grid>,\n        QueueTags::Center<::domain::ObjectLabel::A, Frame::Inertial>,\n        QueueTags::Center<::domain::ObjectLabel::B, Frame::Inertial>>>;\n\ntemplate <typename Metavariables>\nstruct MockComponent\n    : public TestHelpers::control_system::MockControlComponent<Metavariables,\n                                                               GridCenters> {\n  using replace_these_simple_actions =\n      replace_these_simple_actions_mock_component;\n  using with_these_simple_actions = with_these_simple_actions_mock_component;\n};\n\nstruct Metavars {\n  using observed_reduction_data_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<3>, control_system::Tags::Verbosity>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 control_system::Tags::MeasurementTimescales>;\n  using component_list =\n      tmpl::list<MockComponent<Metavars>,\n                 ::TestHelpers::observers::MockObserverWriter<Metavars>>;\n};\n\nvoid test_grid_centers_process_measurement() {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::creators::register_derived_with_charm();\n  const domain::creators::Brick creator{\n      std::array{-10.0, -10.0, -10.0},\n      std::array{10.0, 10.0, 10.0},\n      std::array{0_st, 0_st, 0_st},\n      std::array{8_st, 8_st, 8_st},\n      std::array{false, false, false},\n      {},\n      std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          0.0, std::array{0.0, 0.0, 0.0}, std::array{0.0, 0.0, 0.0})};\n\n  using component = MockComponent<Metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavars>;\n  MockRuntimeSystem runner{\n      {creator.create_domain(), ::Verbosity::Silent, 4, true, false,\n       std::unordered_map<std::string, bool>{},\n       tnsr::I<double, 3, Frame::Grid>{std::array{-16.0, 0.0, 0.0}},\n       tnsr::I<double, 3, Frame::Grid>{std::array{16.0, 0.0, 0.0}},\n       std::unordered_map<std::string, std::string>{}},\n      {creator.functions_of_time(),\n       control_system::Tags::MeasurementTimescales::type{}}};\n\n  ActionTesting::emplace_singleton_component<component>(make_not_null(&runner),\n                                                        {0}, {0});\n\n  auto& cache = ActionTesting::cache<component>(runner, 0);\n\n  const LinkedMessageId<double> id{0.0, std::nullopt};\n  const std::array grid_centers_a{-16.0, 0.0, 0.0};\n  const std::array grid_centers_b{16.0, 0.0, 0.0};\n  const std::array inertial_centers_a{-14.0, 2.0, 0.0};\n  const std::array inertial_centers_b{14.0, 2.0, 0.0};\n\n  GridCenters::process_measurement::apply(\n      both_centers::FindTwoCenters{}, grid_centers_a, grid_centers_b,\n      inertial_centers_a, inertial_centers_b, cache, id);\n\n  CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner, 0) ==\n        1);\n  CHECK(message_queue_call_count == 0);\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n\n  CHECK(ActionTesting::is_simple_action_queue_empty<component>(runner, 0));\n  CHECK(message_queue_call_count == 1);\n}\n\nvoid test_names() {\n  CHECK(pretty_type::name<GridCenters>() == \"GridCenters\");\n  CHECK(*GridCenters::component_name(0, 6) == \"A_x\");\n  CHECK(*GridCenters::component_name(1, 6) == \"A_y\");\n  CHECK(*GridCenters::component_name(2, 6) == \"A_z\");\n  CHECK(*GridCenters::component_name(3, 6) == \"B_x\");\n  CHECK(*GridCenters::component_name(4, 6) == \"B_y\");\n  CHECK(*GridCenters::component_name(5, 6) == \"B_z\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Systems.GridCenters\",\n                  \"[ControlSystem][Unit]\") {\n  test_names();\n  test_grid_centers_process_measurement();\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/Test_RotScaleTrans.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/PostNewtonian/BinaryTrajectories.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace control_system {\nnamespace {\nusing TranslationMap = domain::CoordinateMaps::TimeDependent::Translation<3>;\nusing RotationMap = domain::CoordinateMaps::TimeDependent::Rotation<3>;\nusing ExpansionMap = domain::CoordinateMaps::TimeDependent::CubicScale<3>;\n\nusing CoordMap =\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, ExpansionMap,\n                          RotationMap, TranslationMap>;\n\nstd::string create_input_string(const std::string& name) {\n  const std::string name_str = \"  \"s + name + \":\\n\"s;\n  const std::string base_string1{\n      \"    Averager:\\n\"\n      \"      AverageTimescaleFraction: 0.25\\n\"\n      \"      Average0thDeriv: true\\n\"\n      \"    Controller:\\n\"\n      \"      UpdateFraction: 0.3\\n\"\n      \"    TimescaleTuner:\\n\"\n      \"      InitialTimescales: 0.5\\n\"\n      \"      MinTimescale: 0.1\\n\"\n      \"      MaxTimescale: 10.\\n\"\n      \"      DecreaseThreshold: 2.0\\n\"\n      \"      IncreaseThreshold: 0.1\\n\"\n      \"      IncreaseFactor: 1.01\\n\"\n      \"      DecreaseFactor: 0.99\\n\"\n      \"    ControlError:\\n\"};\n\n  return name_str + base_string1;\n}\n\ntemplate <size_t TranslationDerivOrder, size_t RotationDerivOrder,\n          size_t ExpansionDerivOrder>\nvoid test_rotscaletrans_control_system(const double rotation_eps = 5.0e-5) {\n  INFO(\"Translation: \"s + get_output(TranslationDerivOrder) + \", Rotation: \"s +\n       get_output(RotationDerivOrder) + \", Expansion: \"s +\n       get_output(ExpansionDerivOrder));\n  using metavars = TestHelpers::control_system::MockMetavars<\n      TranslationDerivOrder, RotationDerivOrder, ExpansionDerivOrder, 0>;\n  using element_component = typename metavars::element_component;\n  using translation_component = typename metavars::translation_component;\n  using rotation_component = typename metavars::rotation_component;\n  using expansion_component = typename metavars::expansion_component;\n  using translation_system = typename metavars::translation_system;\n  using rotation_system = typename metavars::rotation_system;\n  using expansion_system = typename metavars::expansion_system;\n  MAKE_GENERATOR(gen);\n\n  // Global things\n  domain::FunctionsOfTime::register_derived_with_charm();\n  const double initial_time = 0.0;\n  const double initial_separation = 15.0;\n  // This final time is chosen so that the damping timescales have adequate time\n  // to reach the maximum damping timescale\n  const double final_time = 500.0;\n\n  // Set up the system helper\n  TestHelpers::control_system::SystemHelper<metavars> system_helper{};\n\n  const std::string translation_name =\n      system_helper.template name<translation_system>();\n  const std::string rotation_name =\n      system_helper.template name<rotation_system>();\n  const std::string expansion_name =\n      system_helper.template name<expansion_system>();\n\n  std::string input_options =\n      \"Evolution:\\n\"\n      \"  InitialTime: 0.0\\n\"\n      \"DomainCreator:\\n\"\n      \"  FakeCreator:\\n\"\n      \"    NumberOfComponents:\\n\"\n      \"      Translation: 3\\n\"\n      \"      Rotation: 3\\n\"\n      \"      Expansion: 1\\n\"\n      \"ControlSystems:\\n\"\n      \"  WriteDataToDisk: false\\n\"\n      \"  MeasurementsPerUpdate: 4\\n\"\n      \"  DelayUpdate: true\\n\";\n  input_options += create_input_string(translation_name);\n  input_options += create_input_string(rotation_name);\n  input_options += create_input_string(expansion_name);\n\n  const auto initialize_functions_of_time =\n      [](const gsl::not_null<std::unordered_map<\n             std::string,\n             std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n             functions_of_time,\n         const double local_initial_time,\n         const std::unordered_map<std::string, double>&\n             initial_expiration_times) {\n        TestHelpers::control_system::initialize_expansion_functions_of_time<\n            expansion_system>(functions_of_time, local_initial_time,\n                              initial_expiration_times);\n        TestHelpers::control_system::initialize_rotation_functions_of_time<\n            rotation_system>(functions_of_time, local_initial_time,\n                             initial_expiration_times);\n        return TestHelpers::control_system::\n            initialize_translation_functions_of_time<translation_system>(\n                functions_of_time, local_initial_time,\n                initial_expiration_times);\n      };\n\n  // Initialize everything within the system helper\n  system_helper.setup_control_system_test(initial_time, initial_separation,\n                                          input_options,\n                                          initialize_functions_of_time);\n\n  // Get references to everything that was set up inside the system helper. The\n  // domain and two functions of time are not const references because they need\n  // to be moved into the runner\n  auto& domain = system_helper.domain();\n  auto& is_active_map = system_helper.is_active_map();\n  auto& initial_functions_of_time = system_helper.initial_functions_of_time();\n  auto& initial_measurement_timescales =\n      system_helper.initial_measurement_timescales();\n  const auto& init_trans_tuple =\n      system_helper.template init_tuple<translation_system>();\n  const auto& init_rot_tuple =\n      system_helper.template init_tuple<rotation_system>();\n  const auto& init_exp_tuple =\n      system_helper.template init_tuple<expansion_system>();\n  auto system_to_combined_names = system_helper.system_to_combined_names();\n\n  // Setup runner and all components\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {\"DummyFileName\", std::move(domain), 4, true, false, ::Verbosity::Silent,\n       std::move(is_active_map),\n       tnsr::I<double, 3, Frame::Grid>{{0.5 * initial_separation, 0.0, 0.0}},\n       tnsr::I<double, 3, Frame::Grid>{{-0.5 * initial_separation, 0.0, 0.0}},\n       std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_singleton_component_and_initialize<\n      translation_component>(make_not_null(&runner), ActionTesting::NodeId{0},\n                             ActionTesting::LocalCoreId{0}, init_trans_tuple);\n  ActionTesting::emplace_singleton_component_and_initialize<rotation_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, init_rot_tuple);\n  ActionTesting::emplace_singleton_component_and_initialize<\n      expansion_component>(make_not_null(&runner), ActionTesting::NodeId{0},\n                           ActionTesting::LocalCoreId{0}, init_exp_tuple);\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // PN orbits\n  const std::array<double, 3> initial_velocity{0.1, -0.2, 0.3};\n  const BinaryTrajectories binary_trajectories{initial_separation,\n                                               initial_velocity};\n\n  // Create coordinate maps for mapping the PN trajectories to the \"grid\" frame\n  // where the control system does its calculations\n  TranslationMap translation_map{translation_name};\n  RotationMap rotation_map{rotation_name};\n  // The outer boundary is at 1000.0 so that we don't have to worry about it.\n  ExpansionMap expansion_map{1000.0, expansion_name,\n                             expansion_name + \"OuterBoundary\"s};\n\n  CoordMap coord_map{expansion_map, rotation_map, translation_map};\n\n  // Get the functions of time from the cache to use in the maps\n  const auto& cache = ActionTesting::cache<element_component>(runner, 0);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n\n  const auto position_function = [&binary_trajectories](const double time) {\n    return binary_trajectories.positions(time);\n  };\n\n  const auto horizon_function = [&position_function, &runner,\n                                 &coord_map](const double time) {\n    return TestHelpers::control_system::\n        build_horizons_for_basic_control_systems<element_component>(\n            time, runner, position_function, coord_map);\n  };\n\n  // Run the actual control system test.\n  system_helper.run_control_system_test(runner, final_time, make_not_null(&gen),\n                                        horizon_function);\n\n  // Grab results\n  const auto grid_positions = TestHelpers::control_system::\n      grid_frame_horizon_centers_for_basic_control_systems<element_component>(\n          final_time, runner, position_function, coord_map);\n\n  // Our expected positions are just the initial positions\n  const tnsr::I<double, 3, Frame::Grid> expected_grid_position_of_a{\n      {0.5 * initial_separation, 0.0, 0.0}};\n  const tnsr::I<double, 3, Frame::Grid> expected_grid_position_of_b{\n      {-0.5 * initial_separation, 0.0, 0.0}};\n\n  const auto& rotation_f_of_t = dynamic_cast<\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<RotationDerivOrder>&>(\n      *functions_of_time.at(rotation_name));\n  const auto& translation_f_of_t = dynamic_cast<\n      domain::FunctionsOfTime::PiecewisePolynomial<TranslationDerivOrder>&>(\n      *functions_of_time.at(translation_name));\n  const auto& expansion_f_of_t = dynamic_cast<\n      domain::FunctionsOfTime::PiecewisePolynomial<ExpansionDerivOrder>&>(\n      *functions_of_time.at(expansion_name));\n\n  const auto omega = rotation_f_of_t.angle_func_and_deriv(final_time)[1];\n  const auto trans_and_2_derivs =\n      translation_f_of_t.func_and_2_derivs(final_time);\n  const auto expansion_factor = expansion_f_of_t.func(final_time)[0][0];\n\n  // The control system gets more accurate the longer you run for. This is\n  // the accuracy we can achieve in this amount of time.\n  Approx custom_approx1 = Approx::custom().epsilon(5.0e-5).scale(1.0);\n  Approx custom_approx2 = Approx::custom().epsilon(rotation_eps).scale(1.0);\n\n  const DataVector expected_omega{\n      {0.0, 0.0, binary_trajectories.angular_velocity(final_time)}};\n  const DataVector expected_translation_2nd_deriv{3, 0.0};\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_omega, omega, custom_approx1);\n  CHECK_ITERABLE_CUSTOM_APPROX(trans_and_2_derivs[2],\n                               expected_translation_2nd_deriv, custom_approx1);\n  CHECK_ITERABLE_CUSTOM_APPROX(trans_and_2_derivs[1],\n                               DataVector(initial_velocity), custom_approx1);\n  CHECK_ITERABLE_CUSTOM_APPROX(trans_and_2_derivs[0],\n                               DataVector(initial_velocity * final_time),\n                               custom_approx1);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      binary_trajectories.separation(final_time) / initial_separation,\n      expansion_factor, custom_approx1);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_grid_position_of_a, grid_positions[0],\n                               custom_approx2);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_grid_position_of_b, grid_positions[1],\n                               custom_approx2);\n}\n}  // namespace\n\n// Currently the test takes a long time because the logic for the control system\n// isn't the most optimal. This should be fixed in the near future.\n// [[TimeOut, 25]]\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Systems.RotScaleTrans\",\n                  \"[ControlSystem][Unit]\") {\n  // For rotation deriv order 2, we need a different epsilon because controlling\n  // the first derivative of omega (second derivative of the angle) doesn't give\n  // as accurate results for the final positions of the objects in the grid\n  // frame. This is also done is Systems/Test_Rotation.\n  test_rotscaletrans_control_system<2, 2, 2>(5.0e-3);\n  test_rotscaletrans_control_system<3, 2, 2>(5.0e-3);\n  test_rotscaletrans_control_system<2, 3, 2>();\n  test_rotscaletrans_control_system<2, 2, 3>(5.0e-3);\n  test_rotscaletrans_control_system<3, 3, 2>();\n  test_rotscaletrans_control_system<2, 3, 3>();\n  test_rotscaletrans_control_system<3, 3, 3>();\n}\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/Test_Rotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"ControlSystem/Systems/Rotation.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/PostNewtonian/BinaryTrajectories.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace control_system::measurements {\nstruct BothHorizons;\n}  // namespace control_system::measurements\n\nnamespace control_system {\nnamespace {\nusing RotationMap = domain::CoordinateMaps::TimeDependent::Rotation<3>;\n\nusing CoordMap =\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, RotationMap>;\n\ntemplate <size_t DerivOrder>\nvoid test_rotation_control_system(const bool newtonian) {\n  // Since we are only doing rotation, turn off the\n  // other control systems by passing 0 for their deriv orders\n  using metavars =\n      TestHelpers::control_system::MockMetavars<0, DerivOrder, 0, 0>;\n  using rotation_component = typename metavars::rotation_component;\n  using element_component = typename metavars::element_component;\n  using rotation_system = typename metavars::rotation_system;\n  MAKE_GENERATOR(gen);\n\n  // Global things\n  domain::FunctionsOfTime::register_derived_with_charm();\n  const double initial_time = 0.0;\n  const double initial_separation = 15.0;\n  // This final time is chosen so that the damping timescales have adequate time\n  // to reach the maximum damping timescale\n  const double final_time = 300.0;\n\n  // Set up the system helper.\n  TestHelpers::control_system::SystemHelper<metavars> system_helper{};\n\n  const std::string input_options =\n      \"Evolution:\\n\"\n      \"  InitialTime: 0.0\\n\"\n      \"DomainCreator:\\n\"\n      \"  FakeCreator:\\n\"\n      \"    NumberOfComponents:\\n\"\n      \"      Rotation: 3\\n\"\n      \"ControlSystems:\\n\"\n      \"  WriteDataToDisk: false\\n\"\n      \"  MeasurementsPerUpdate: 4\\n\"\n      \"  DelayUpdate: true\\n\"\n      \"  Rotation:\\n\"\n      \"    Averager:\\n\"\n      \"      AverageTimescaleFraction: 0.25\\n\"\n      \"      Average0thDeriv: true\\n\"\n      \"    Controller:\\n\"\n      \"      UpdateFraction: 0.3\\n\"\n      \"    TimescaleTuner:\\n\"\n      \"      InitialTimescales: 0.5\\n\"\n      \"      MinTimescale: 0.1\\n\"\n      \"      MaxTimescale: 10.\\n\"\n      \"      DecreaseThreshold: 2.0\\n\"\n      \"      IncreaseThreshold: 0.1\\n\"\n      \"      IncreaseFactor: 1.01\\n\"\n      \"      DecreaseFactor: 0.99\\n\"\n      \"    ControlError:\\n\";\n\n  // Initialize everything within the system helper\n  system_helper.setup_control_system_test(\n      initial_time, initial_separation, input_options,\n      TestHelpers::control_system::initialize_rotation_functions_of_time<\n          rotation_system>);\n\n  // Get references to everything that was set up inside the system helper. The\n  // domain and two functions of time are not const references because they need\n  // to be moved into the runner\n  auto& domain = system_helper.domain();\n  auto& is_active_map = system_helper.is_active_map();\n  auto& initial_functions_of_time = system_helper.initial_functions_of_time();\n  auto& initial_measurement_timescales =\n      system_helper.initial_measurement_timescales();\n  const auto& init_rot_tuple =\n      system_helper.template init_tuple<rotation_system>();\n  auto system_to_combined_names = system_helper.system_to_combined_names();\n\n  auto grid_center_A = domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  auto grid_center_B = domain.excision_spheres().at(\"ExcisionSphereB\").center();\n\n  // Setup runner and all components\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {\"DummyFileName\", std::move(domain), 4, true, false, ::Verbosity::Silent,\n       std::move(is_active_map), std::move(grid_center_A),\n       std::move(grid_center_B), std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_singleton_component_and_initialize<rotation_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, init_rot_tuple);\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const BinaryTrajectories binary_trajectories{\n      initial_separation, {0.0, 0.0, 0.0}, newtonian};\n\n  const std::string rotation_name =\n      system_helper.template name<rotation_system>();\n\n  // Create coordinate map for mapping the PN rotation to the \"grid\" frame\n  // where the control system does its calculations\n  RotationMap rotation_map{rotation_name};\n\n  CoordMap coord_map{rotation_map};\n\n  // Get the functions of time from the cache to use in the maps\n  const auto& cache = ActionTesting::cache<element_component>(runner, 0);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n\n  const auto position_function = [&binary_trajectories](const double time) {\n    return binary_trajectories.positions_no_expansion(time);\n  };\n\n  const auto horizon_function = [&position_function, &runner,\n                                 &coord_map](const double time) {\n    return TestHelpers::control_system::\n        build_horizons_for_basic_control_systems<element_component>(\n            time, runner, position_function, coord_map);\n  };\n\n  // Run the actual control system test.\n  system_helper.run_control_system_test(runner, final_time, make_not_null(&gen),\n                                        horizon_function);\n\n  // Grab results\n  const auto grid_positions = TestHelpers::control_system::\n      grid_frame_horizon_centers_for_basic_control_systems<element_component>(\n          final_time, runner, position_function, coord_map);\n\n  // Our expected positions are just the initial positions\n  const tnsr::I<double, 3, Frame::Grid> expected_grid_position_of_a{\n      {0.5 * initial_separation, 0.0, 0.0}};\n  const tnsr::I<double, 3, Frame::Grid> expected_grid_position_of_b{\n      {-0.5 * initial_separation, 0.0, 0.0}};\n\n  const auto& rotation_f_of_t = dynamic_cast<\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<DerivOrder>&>(\n      *functions_of_time.at(rotation_name));\n\n  const auto omega = rotation_f_of_t.angle_func_and_deriv(final_time)[1];\n\n  // The control system gets more accurate the longer you run for. This is\n  // the accuracy we can achieve in this amount of time. For PN deriv order 2,\n  // we need a different epsilon because controlling the first derivative of\n  // omega (second derivative of the angle) doesn't give as accurate results for\n  // the final positions of the objects in the grid frame.\n  double eps = 5.0e-5;\n  if (DerivOrder == 2 and not newtonian) {\n    eps = 5.0e-3;\n  }\n  Approx custom_approx1 = Approx::custom().epsilon(5.0e-5).scale(1.0);\n  Approx custom_approx2 = Approx::custom().epsilon(eps).scale(1.0);\n  const DataVector expected_omega{\n      {0.0, 0.0, binary_trajectories.angular_velocity(final_time)}};\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_omega, omega, custom_approx1);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_grid_position_of_a, grid_positions[0],\n                               custom_approx2);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_grid_position_of_b, grid_positions[1],\n                               custom_approx2);\n}\n\nvoid test_names() {\n  using rotation = control_system::Systems::Rotation<\n      2, control_system::measurements::BothHorizons>;\n\n  CHECK(pretty_type::name<rotation>() == \"Rotation\");\n  CHECK(*rotation::component_name(0, 3) == \"x\");\n  CHECK(*rotation::component_name(1, 3) == \"y\");\n  CHECK(*rotation::component_name(2, 3) == \"z\");\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::string component_name = *rotation::component_name(1, 4);\n        (void)component_name;\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Rotation control expects 3 components but there are 4 instead.\"));\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Systems.Rotation\",\n                  \"[ControlSystem][Unit]\") {\n  test_names();\n  test_rotation_control_system<2>(true);\n  test_rotation_control_system<2>(false);\n  test_rotation_control_system<3>(true);\n  test_rotation_control_system<3>(false);\n}\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/Test_Shape.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Shape.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/Systems/Shape.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Distorted;\n}  // namespace Frame\n\nnamespace control_system {\nnamespace {\nusing FoTMap = std::unordered_map<\n    std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;\nusing Strahlkorper = ylm::Strahlkorper<Frame::Distorted>;\ntemplate <typename Metavars>\nusing SystemHelper = TestHelpers::control_system::SystemHelper<Metavars>;\n\ntemplate <typename Generator, typename Metavars, size_t DerivOrder>\nvoid test_shape_control(\n    const gsl::not_null<Generator*> generator,\n    const gsl::not_null<SystemHelper<Metavars>*> system_helper,\n    const double initial_time, const double final_time, const size_t l_max,\n    const domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>&\n        ah_coefs_function_of_time,\n    const double func_eps, const double deriv_eps) {\n  using system = typename Metavars::shape_system;\n  using shape_component = typename Metavars::shape_component;\n  using element_component = typename Metavars::element_component;\n\n  auto& domain = system_helper->domain();\n  auto& is_active_map = system_helper->is_active_map();\n  auto& initial_functions_of_time = system_helper->initial_functions_of_time();\n  auto& initial_measurement_timescales =\n      system_helper->initial_measurement_timescales();\n\n  auto grid_center_A = domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  auto grid_center_B = domain.excision_spheres().at(\"ExcisionSphereB\").center();\n\n  const auto& init_shape_tuple = system_helper->template init_tuple<system>();\n  auto system_to_combined_names = system_helper->system_to_combined_names();\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavars>;\n  // Excision centers aren't used so their values can be anything\n  MockRuntimeSystem runner{\n      {\"DummyFileName\", std::move(domain), 4, true, false, ::Verbosity::Silent,\n       std::move(is_active_map), std::move(grid_center_A),\n       std::move(grid_center_B), std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_singleton_component_and_initialize<shape_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, init_shape_tuple);\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n\n  runner.set_phase(Parallel::Phase::Testing);\n\n  const auto& cache = ActionTesting::cache<element_component>(runner, 0);\n  const auto& cache_domain = get<::domain::Tags::Domain<3>>(cache);\n  const auto& excision_sphere = cache_domain.excision_spheres().at(\n      control_system::ControlErrors::detail::excision_sphere_name<\n          ::domain::ObjectLabel::A>());\n\n  ylm::SpherepackIterator iter{l_max, l_max};\n  const double ah_radius =\n      ah_coefs_function_of_time.func(initial_time)[0][iter.set(0, 0)()];\n  const tnsr::I<double, 3, Frame::Grid>& grid_center = excision_sphere.center();\n  Strahlkorper horizon_a{l_max, l_max, ah_radius,\n                         make_array<double, 3>(grid_center)};\n  // B just needs to exist. Doesn't have to be valid\n  const Strahlkorper horizon_b{};\n\n  const auto horizon_measurement =\n      [&horizon_a, &horizon_b, &ah_coefs_function_of_time](const double time) {\n        auto& mutable_horizon_coefficients = horizon_a.coefficients();\n        mutable_horizon_coefficients =\n            ah_coefs_function_of_time.func_and_2_derivs(time)[0];\n\n        return std::pair<Strahlkorper, Strahlkorper>{horizon_a, horizon_b};\n      };\n\n  // Run the test, specifying that we are only using 1 horizon\n  system_helper->run_control_system_test(runner, final_time, generator,\n                                         horizon_measurement);\n\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n  const double excision_radius = excision_sphere.radius();\n  const std::string size_name =\n      control_system::ControlErrors::detail::size_name<\n          ::domain::ObjectLabel::A>();\n  const std::string shape_name = system_helper->template name<system>();\n\n  const auto lambda_00_coef =\n      functions_of_time.at(size_name)->func(final_time)[0][0];\n  const double Y00 = sqrt(0.25 / M_PI);\n\n  auto ah_coefs_and_derivs =\n      ah_coefs_function_of_time.func_and_2_derivs(final_time);\n\n  // Our expected coefs are just the (minus) coefs of the AH (except for l=0,l=1\n  // which should be zero) scaled by the relative size factor defined in the\n  // control error\n  auto expected_shape_coefs =\n      -1.0 * (excision_radius / Y00 - lambda_00_coef) /\n      (sqrt(0.5 * M_PI) * ah_coefs_and_derivs[0][iter.set(0, 0)()]) *\n      ah_coefs_and_derivs;\n  // Manually set 0,0 component to 0\n  expected_shape_coefs[0][iter.set(0, 0)()] = 0.0;\n\n  const auto& shape_func =\n      dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>&>(\n          *functions_of_time.at(shape_name));\n\n  const auto shape_coefs = shape_func.func_and_2_derivs(final_time);\n\n  Approx custom_approx = Approx::custom().epsilon(1.0).scale(1.0);\n  for (size_t i = 0; i < shape_coefs.size(); i++) {\n    if (i == 0) {\n      custom_approx = Approx::custom().epsilon(func_eps).scale(1.0);\n    } else {\n      custom_approx = Approx::custom().epsilon(deriv_eps).scale(1.0);\n    }\n    INFO(\"i = \" + get_output(i));\n    CHECK_ITERABLE_CUSTOM_APPROX(gsl::at(shape_coefs, i),\n                                 gsl::at(expected_shape_coefs, i),\n                                 custom_approx);\n  }\n}\n\ntemplate <size_t DerivOrder, typename Generator>\nvoid test_suite(const gsl::not_null<Generator*> generator, const size_t l_max,\n                const double looser_eps, const double stricter_eps) {\n  // First 3 zeros are for translation, rotation, and expansion\n  using metavars =\n      TestHelpers::control_system::MockMetavars<0, 0, 0, DerivOrder>;\n  using system = typename metavars::shape_system;\n\n  // Responsible for running all control system checks\n  SystemHelper<metavars> system_helper{};\n  ylm::SpherepackIterator iter{l_max, l_max};\n  const size_t num_ah_coeffs = iter.spherepack_array_size();\n  std::uniform_real_distribution<double> coef_dist{-0.1, 0.1};\n  // Hard code the radius to something other than 1 because shape doesn't depend\n  // on the size of the strahlkorper\n  const double ah_radius = 1.63;\n  const double initial_time = 0.0;\n  const double final_time = 100.0;\n\n  // Setup initial shape map coefficients. In the map the coefficients are\n  // stored as the negative of the actual spherical harmonic coefficients\n  // because that's just how the map is defined. But since these are random\n  // numbers it doesn't matter for initial data. Also set initial size map\n  // coefficient. Here we only test non-changing size. A test with a changing\n  // size parameter can be added in later if needed.\n  const auto initialize_shape_functions_of_time =\n      [&iter, &generator, &coef_dist, &ah_radius](\n          const gsl::not_null<std::unordered_map<\n              std::string,\n              std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n              functions_of_time,\n          const double local_initial_time,\n          const std::unordered_map<std::string, double>&\n              initial_expiration_times) {\n        auto initial_shape_func = make_array<DerivOrder + 1, DataVector>(\n            DataVector{iter.spherepack_array_size(), 0.0});\n\n        for (size_t i = 0; i < initial_shape_func.size(); i++) {\n          for (iter.reset(); iter; ++iter) {\n            // Enforce l=0,l=1 components to be 0 always\n            if (iter.l() == 0 or iter.l() == 1) {\n              continue;\n            }\n            gsl::at(initial_shape_func, i)[iter()] =\n                make_with_random_values<double>(generator, coef_dist, 1.0);\n          }\n        }\n\n        const std::string shape_name = system::name();\n        (*functions_of_time)[shape_name] = std::make_unique<\n            domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>>(\n            local_initial_time, initial_shape_func,\n            initial_expiration_times.at(shape_name));\n\n        auto initial_size_func =\n            make_array<DerivOrder + 1, DataVector>(DataVector{1, 0.0});\n        initial_size_func[0][0] = ah_radius;\n        const std::string size_name =\n            ControlErrors::detail::size_name<::domain::ObjectLabel::A>();\n        (*functions_of_time)[size_name] = std::make_unique<\n            domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>>(\n            local_initial_time, initial_size_func,\n            std::numeric_limits<double>::infinity());\n\n        // Excision sphere radius just needs to be inside the horizon. In an\n        // actual run we'd want the excision surface closer to the horizon\n        return 0.75 * ah_radius;\n      };\n\n  const std::string num_ah_coeffs_str = MakeString{} << num_ah_coeffs;\n\n  const std::string input_options =\n      \"Evolution:\\n\"\n      \"  InitialTime: 0.0\\n\"\n      \"DomainCreator:\\n\"\n      \"  FakeCreator:\\n\"\n      \"    NumberOfComponents:\\n\"\n      \"      ShapeA: \" +\n      num_ah_coeffs_str +\n      \"\\n\"\n      \"ControlSystems:\\n\"\n      \"  WriteDataToDisk: false\\n\"\n      \"  MeasurementsPerUpdate: 4\\n\"\n      \"  DelayUpdate: true\\n\"\n      \"  ShapeA:\\n\"\n      \"    Averager:\\n\"\n      \"      AverageTimescaleFraction: 0.25\\n\"\n      \"      Average0thDeriv: true\\n\"\n      \"    Controller:\\n\"\n      \"      UpdateFraction: 0.3\\n\"\n      \"    TimescaleTuner:\\n\"\n      \"      InitialTimescales: 0.3\\n\"\n      \"      MinTimescale: 0.1\\n\"\n      \"      MaxTimescale: 10.\\n\"\n      \"      DecreaseThreshold: 4e-3\\n\"\n      \"      IncreaseThreshold: 1e-3\\n\"\n      \"      IncreaseFactor: 1.01\\n\"\n      \"      DecreaseFactor: 0.98\\n\"\n      \"    ControlError:\\n\";\n\n  // Separation doesn't matter because we are only using AhA\n  system_helper.setup_control_system_test(initial_time, 15.0, input_options,\n                                          initialize_shape_functions_of_time);\n\n  const DataVector zero_dv{num_ah_coeffs, 0.0};\n  const auto zero_array = make_array<DerivOrder + 1, DataVector>(zero_dv);\n\n  // Allocate now and reuse as we go\n  auto initial_ah_coefs = zero_array;\n  domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder> expected_ah_coefs{};\n\n  const std::string deriv_order_string =\n      \"DerivOrder=\" + get_output(DerivOrder) + \": \";\n  {\n    INFO(deriv_order_string + \"Stationary spherical AH\");\n    // Set all coefs back to zero first\n    initial_ah_coefs = zero_array;\n    initial_ah_coefs[0][iter.set(0, 0)()] = ah_radius;\n    expected_ah_coefs = {initial_time, initial_ah_coefs,\n                         std::numeric_limits<double>::infinity()};\n    test_shape_control(generator, make_not_null(&system_helper), initial_time,\n                       final_time, l_max, expected_ah_coefs, stricter_eps,\n                       stricter_eps);\n  }\n  system_helper.reset();\n  {\n    INFO(deriv_order_string + \"Stationary non-spherical AH\");\n    // Set all coefs back to zero first\n    initial_ah_coefs = zero_array;\n    for (iter.reset(); iter; ++iter) {\n      // Enforce l=0,l=1 components to be 0 always\n      if (iter.l() == 0 or iter.l() == 1) {\n        continue;\n      }\n      initial_ah_coefs[0][iter()] =\n          make_with_random_values<double>(generator, coef_dist, 1);\n    }\n    // Ensure that radius of AH is positive and constant\n    initial_ah_coefs[0][iter.set(0, 0)()] = ah_radius;\n    initial_ah_coefs[1] = zero_dv;\n    initial_ah_coefs[2] = zero_dv;\n    expected_ah_coefs = {initial_time, initial_ah_coefs,\n                         std::numeric_limits<double>::infinity()};\n    test_shape_control(generator, make_not_null(&system_helper), initial_time,\n                       final_time, l_max, expected_ah_coefs, stricter_eps,\n                       stricter_eps);\n  }\n  system_helper.reset();\n  {\n    INFO(deriv_order_string + \"AH coefficients linearly increasing/decreasing\");\n    // Set all coefs back to zero first\n    initial_ah_coefs = zero_array;\n    for (iter.reset(); iter; ++iter) {\n      // Enforce l=0,l=1 components to be 0 always\n      if (iter.l() == 0 or iter.l() == 1) {\n        continue;\n      }\n      initial_ah_coefs[0][iter()] =\n          make_with_random_values<double>(generator, coef_dist, 1);\n      initial_ah_coefs[1][iter()] =\n          make_with_random_values<double>(generator, coef_dist, 1);\n    }\n    // Ensure that radius of AH is positive and constant\n    initial_ah_coefs[0][iter.set(0, 0)()] = ah_radius;\n    initial_ah_coefs[1][iter.set(0, 0)()] = 0.0;\n    initial_ah_coefs[2] = zero_dv;\n    expected_ah_coefs = {initial_time, initial_ah_coefs,\n                         std::numeric_limits<double>::infinity()};\n    test_shape_control(generator, make_not_null(&system_helper), initial_time,\n                       final_time, l_max, expected_ah_coefs, stricter_eps,\n                       stricter_eps);\n  }\n  system_helper.reset();\n  {\n    INFO(deriv_order_string +\n         \"AH coefficients quadratically increasing/decreasing\");\n    // Set all coefs back to zero first\n    initial_ah_coefs = zero_array;\n    for (iter.reset(); iter; ++iter) {\n      // Enforce l=0,l=1 components to be 0 always\n      if (iter.l() == 0 or iter.l() == 1) {\n        continue;\n      }\n      for (size_t i = 0; i < 3; i++) {\n        gsl::at(initial_ah_coefs, i)[iter()] =\n            make_with_random_values<double>(generator, coef_dist, 1);\n      }\n    }\n    // Ensure that radius of AH is positive and constant\n    initial_ah_coefs[0][iter.set(0, 0)()] = ah_radius;\n    initial_ah_coefs[1][iter.set(0, 0)()] = 0.0;\n    initial_ah_coefs[2][iter.set(0, 0)()] = 0.0;\n    expected_ah_coefs = {initial_time, initial_ah_coefs,\n                         std::numeric_limits<double>::infinity()};\n    test_shape_control(generator, make_not_null(&system_helper), initial_time,\n                       final_time, l_max, expected_ah_coefs, looser_eps,\n                       looser_eps);\n  }\n  system_helper.reset();\n  {\n    INFO(deriv_order_string + \"AH coefficients oscillating sinusoidally\");\n    // Set all coefs back to zero first\n    initial_ah_coefs = zero_array;\n    // All coefficients (and derivatives) are of the form\n    // coef = sin(freq * time + offset)\n    // dtcoef = freq * cos(freq * time + offset)\n    // d2tcoef = -freq^2 * sin(freq * time + offset)\n    // d3tcoef = -freq^3 * cos(freq * time + offset)\n\n    // Only allow at most one full oscillation by the time we reach the end.\n    // That way the coefficients aren't oscillating too fast\n    std::uniform_real_distribution<double> freq_dist{0.0,\n                                                     2.0 * M_PI / final_time};\n    const double amplitude = 0.1;\n    DataVector freqs{zero_dv};\n    DataVector offsets{zero_dv};\n    for (iter.reset(); iter; ++iter) {\n      // Enforce l=0,l=1 components to be 0 always\n      if (iter.l() == 0 or iter.l() == 1) {\n        continue;\n      }\n      const auto offset =\n          make_with_random_values<double>(generator, coef_dist, 1);\n      const auto freq =\n          make_with_random_values<double>(generator, freq_dist, 1);\n      freqs[iter()] = freq;\n      offsets[iter()] = offset;\n      initial_ah_coefs[0][iter()] = sin(freq * initial_time + offset);\n      initial_ah_coefs[1][iter()] = freq * cos(freq * initial_time + offset);\n      initial_ah_coefs[2][iter()] =\n          -square(freq) * sin(freq * initial_time + offset);\n      if constexpr (DerivOrder > 2) {\n        initial_ah_coefs[3][iter()] =\n            -cube(freq) * cos(freq * initial_time + offset);\n      }\n    }\n    // Ensure that radius of AH is positive and constant\n    initial_ah_coefs = amplitude * initial_ah_coefs;\n    initial_ah_coefs[0][iter.set(0, 0)()] = ah_radius;\n    initial_ah_coefs[1][iter.set(0, 0)()] = 0.0;\n    initial_ah_coefs[2][iter.set(0, 0)()] = 0.0;\n    if constexpr (DerivOrder > 2) {\n      initial_ah_coefs[3][iter.set(0, 0)()] = 0.0;\n    }\n\n    // Initialize the expected function of time\n    const double dt = 0.1;\n    double time = initial_time + dt;\n    expected_ah_coefs = {initial_time, initial_ah_coefs, time};\n\n    // Update it's derivative often so the function is smooth\n    DataVector updated_deriv = zero_dv;\n\n    const auto update_deriv = [&updated_deriv, &iter, &time, &amplitude, &freqs,\n                               &offsets]() {\n      for (iter.reset(); iter; ++iter) {\n        if (iter.l() == 0 or iter.l() == 1) {\n          continue;\n        }\n        const double freq = freqs[iter()];\n        const double offset = offsets[iter()];\n        if (DerivOrder == 2) {\n          updated_deriv[iter()] =\n              -amplitude * square(freq) * sin(freq * time + offset);\n        } else {\n          updated_deriv[iter()] =\n              -amplitude * cube(freq) * cos(freq * time + offset);\n        }\n      }\n    };\n\n    while (time < final_time) {\n      update_deriv();\n      expected_ah_coefs.update(time, updated_deriv, time + dt);\n      time += dt;\n    }\n\n    // Use looser_eps for both because the function is not perfectly represented\n    // by a polynomial\n    test_shape_control(generator, make_not_null(&system_helper), initial_time,\n                       final_time, l_max, expected_ah_coefs, looser_eps,\n                       looser_eps);\n  }\n}\n\nvoid test_names() {\n  using shape = control_system::Systems::Shape<::domain::ObjectLabel::A, 2,\n                                               measurements::BothHorizons>;\n\n  CHECK(pretty_type::name<shape>() == \"ShapeA\");\n\n  const size_t l_max = 3;\n  ylm::SpherepackIterator iter(l_max, l_max);\n  const size_t size = iter.spherepack_array_size();\n\n  // Check known valid indices\n  for (iter.reset(); iter; ++iter) {\n    const auto component_name = shape::component_name(iter(), size);\n    CHECK(component_name.has_value());\n    const int m = iter() < size / 2 ? static_cast<int>(iter.m())\n                                    : -static_cast<int>(iter.m());\n    const std::string check_name =\n        \"l\"s + get_output(iter.l()) + \"m\"s + get_output(m);\n    CHECK(*component_name == check_name);\n  }\n\n  // We hard code the names for this specific ell so we know they are right. The\n  // (--) are just place holders for spherepack components that don't correspond\n  // to an l,m. They won't be checked (if everything is working properly)\n  const std::vector<std::string> expected_names{\n      \"l0m0\", \"(--)\",  \"(--)\",  \"(--)\", \"l1m0\", \"l1m1\",  \"(--)\",  \"(--)\",\n      \"l2m0\", \"l2m1\",  \"l2m2\",  \"(--)\", \"l3m0\", \"l3m1\",  \"l3m2\",  \"l3m3\",\n      \"(--)\", \"(--)\",  \"(--)\",  \"(--)\", \"(--)\", \"l1m-1\", \"(--)\",  \"(--)\",\n      \"(--)\", \"l2m-1\", \"l2m-2\", \"(--)\", \"(--)\", \"l3m-1\", \"l3m-2\", \"l3m-3\"};\n\n  CHECK(size == expected_names.size());\n\n  // Check all indices\n  iter.reset();\n  for (size_t i = 0; i < size; i++) {\n    const auto compact_index = iter.compact_index(i);\n    const auto component_name = shape::component_name(i, size);\n    CHECK(compact_index.has_value() == component_name.has_value());\n    if (component_name.has_value()) {\n      CHECK(*compact_index == iter.current_compact_index());\n      const std::string& check_name = expected_names[i];\n      CHECK(*component_name == check_name);\n      ++iter;\n    }\n  }\n}\n\n// [[TimeOut, 30]]\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Systems.Shape\", \"[ControlSystem][Unit]\") {\n  MAKE_GENERATOR(generator);\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  test_names();\n\n  // For some of the AHs (quadratic and sinusoid), the control system isn't able\n  // to perfectly match the expected map parameters, but it is able to get the\n  // control error to be a small constant offset (rather than 0). This larger\n  // epsilon accounts for the difference in the map parameters based off the\n  // constant offset in the control error.\n  const double custom_approx_looser_eps = 5.0e-3;\n  // For everything else, we can use a stricter epsilon\n  const double custom_approx_stricter_eps = 5.0e-7;\n\n  std::vector<size_t> ells_to_test{2, 5};\n  for (const auto& ell : ells_to_test) {\n    test_suite<2>(make_not_null(&generator), ell, custom_approx_looser_eps,\n                  custom_approx_stricter_eps);\n    test_suite<3>(make_not_null(&generator), ell, custom_approx_looser_eps,\n                  custom_approx_stricter_eps);\n  }\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/Test_Size.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Size/State.hpp\"\n#include \"ControlSystem/Measurements/CharSpeed.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Systems/Size.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace control_system {\nnamespace {\nusing size = control_system::Systems::Size<domain::ObjectLabel::None, 2>;\nstatic_assert(\n    tt::assert_conforms_to_v<size, control_system::protocols::ControlSystem>);\nusing measurement_queue = size::MeasurementQueue;\nusing char_speed =\n    control_system::measurements::CharSpeed<domain::ObjectLabel::None>;\n\nusing all_tags = measurement_queue::type::queue_tags_list;\n\nsize_t message_queue_call_count = 0;\n\ntemplate <typename LinkedMessageQueueTag, typename Processor,\n          typename... QueueTags>\nstruct MockUpdateMessageQueue {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTags>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const LinkedMessageId<typename LinkedMessageQueueTag::type::IdType>&\n      /*id_and_previous*/,\n      typename QueueTags::type... /*message*/) {\n    ++message_queue_call_count;\n  }\n};\n\n// The Nvidia compiler crashes if we define these lists inside the MockComponent\n// struct.\nusing replace_these_simple_actions_mock_component = tmpl::transform<\n    all_tags,\n    tmpl::bind<::Actions::UpdateMessageQueue, tmpl::pin<measurement_queue>,\n               tmpl::pin<control_system::UpdateControlSystem<size>>, tmpl::_1>>;\nusing with_these_simple_actions_mock_component = tmpl::transform<\n    all_tags,\n    tmpl::bind<MockUpdateMessageQueue, tmpl::pin<measurement_queue>,\n               tmpl::pin<control_system::UpdateControlSystem<size>>, tmpl::_1>>;\n\ntemplate <typename Metavariables>\nstruct MockComponent\n    : public TestHelpers::control_system::MockControlComponent<Metavariables,\n                                                               size> {\n  using replace_these_simple_actions =\n      replace_these_simple_actions_mock_component;\n  using with_these_simple_actions = with_these_simple_actions_mock_component;\n};\n\nstruct Metavars {\n  using observed_reduction_data_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<3>, control_system::Tags::Verbosity>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 control_system::Tags::MeasurementTimescales>;\n  using component_list =\n      tmpl::list<MockComponent<Metavars>,\n                 ::TestHelpers::observers::MockObserverWriter<Metavars>>;\n};\n\nvoid test_size_process_measurement() {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::creators::register_derived_with_charm();\n  const domain::creators::Brick creator{\n      std::array{-10.0, -10.0, -10.0},\n      std::array{10.0, 10.0, 10.0},\n      std::array{0_st, 0_st, 0_st},\n      std::array{8_st, 8_st, 8_st},\n      std::array{false, false, false},\n      {},\n      std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          0.0, std::array{0.0, 0.0, 0.0}, std::array{0.0, 0.0, 0.0})};\n\n  using component = MockComponent<Metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavars>;\n  MockRuntimeSystem runner{\n      {creator.create_domain(), ::Verbosity::Silent, 4, true, false,\n       std::unordered_map<std::string, bool>{},\n       tnsr::I<double, 3, Frame::Grid>{std::array{0.0, 0.0, 0.0}},\n       tnsr::I<double, 3, Frame::Grid>{std::array{0.0, 0.0, 0.0}},\n       std::unordered_map<std::string, std::string>{}},\n      {creator.functions_of_time(),\n       control_system::Tags::MeasurementTimescales::type{}}};\n\n  ActionTesting::emplace_singleton_component<component>(make_not_null(&runner),\n                                                        {0}, {0});\n\n  auto& cache = ActionTesting::cache<component>(runner, 0);\n\n  // The data here doesn't matter except for the excision surface because it's\n  // the only one that is used in any calculations. And even then, the only\n  // restriction is that is must be contained within our domain above.\n  const LinkedMessageId<double> id{0.0, std::nullopt};\n  const Scalar<DataVector> zero_scalar{};\n  const tnsr::I<DataVector, 3, Frame::Distorted> zero_tnsr_I{};\n  const tnsr::ii<DataVector, 3, Frame::Distorted> zero_tnsr_ii{};\n  const tnsr::II<DataVector, 3, Frame::Distorted> zero_tnsr_II{};\n  const tnsr::Ijj<DataVector, 3, Frame::Distorted>& spatial_christoffel{};\n  const tnsr::i<DataVector, 3, Frame::Distorted>& deriv_lapse{};\n  const tnsr::iJ<DataVector, 3, Frame::Distorted>& deriv_shift{};\n  const ::InverseJacobian<DataVector, 3, Frame::Grid, Frame::Distorted>&\n      inv_jac_grid_to_distorted{};\n  const ylm::Strahlkorper<Frame::Distorted> horizon{};\n  const ylm::Strahlkorper<Frame::Grid> excision{2, 2.0,\n                                                std::array{0.0, 0.0, 0.0}};\n\n  size::process_measurement::apply<Metavars>(\n      char_speed::Excision{}, excision, zero_scalar, zero_tnsr_I, zero_tnsr_ii,\n      zero_tnsr_II, spatial_christoffel, deriv_lapse, deriv_shift,\n      inv_jac_grid_to_distorted, cache, id);\n\n  // We only check that the proper number of actions have been called.\n  CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner, 0) ==\n        1);\n  CHECK(message_queue_call_count == 0);\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n\n  size::process_measurement::apply<Metavars>(char_speed::Horizon{}, horizon,\n                                             horizon, cache, id);\n\n  CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner, 0) ==\n        1);\n  CHECK(message_queue_call_count == 1);\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n\n  CHECK(ActionTesting::is_simple_action_queue_empty<component>(runner, 0));\n  CHECK(message_queue_call_count == 2);\n}\n\nvoid test_names() {\n  CHECK(pretty_type::name<size>() == \"Size\");\n  CHECK(*size::component_name(0, 1) == \"Size\");\n  CHECK(*size::component_name(1, 1) == \"Size\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Systems.Size\", \"[ControlSystem][Unit]\") {\n  test_names();\n  test_size_process_measurement();\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/Test_Skew.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Skew.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Systems/Skew.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace control_system {\nnamespace {\nusing both_horizons = control_system::measurements::BothHorizons;\nusing skew = control_system::Systems::Skew<2, both_horizons>;\nstatic_assert(\n    tt::assert_conforms_to_v<skew, control_system::protocols::ControlSystem>);\nusing measurement_queue = skew::MeasurementQueue;\n\nusing all_tags = measurement_queue::type::queue_tags_list;\n\nsize_t message_queue_call_count = 0;  // NOLINT\n\ntemplate <typename LinkedMessageQueueTag, typename Processor,\n          typename... QueueTags>\nstruct MockUpdateMessageQueue {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTags>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const LinkedMessageId<typename LinkedMessageQueueTag::type::IdType>&\n      /*id_and_previous*/,\n      typename QueueTags::type... /*message*/) {\n    ++message_queue_call_count;\n  }\n};\n\n// The Nvidia compiler crashes if we define these lists inside the MockComponent\n// struct.\nusing replace_these_simple_actions_mock_component = tmpl::transform<\n    all_tags,\n    tmpl::bind<::Actions::UpdateMessageQueue, tmpl::pin<measurement_queue>,\n               tmpl::pin<control_system::UpdateControlSystem<skew>>, tmpl::_1>>;\nusing with_these_simple_actions_mock_component = tmpl::transform<\n    all_tags,\n    tmpl::bind<MockUpdateMessageQueue, tmpl::pin<measurement_queue>,\n               tmpl::pin<control_system::UpdateControlSystem<skew>>, tmpl::_1>>;\n\ntemplate <typename Metavariables>\nstruct MockComponent\n    : public TestHelpers::control_system::MockControlComponent<Metavariables,\n                                                               skew> {\n  using replace_these_simple_actions =\n      replace_these_simple_actions_mock_component;\n  using with_these_simple_actions = with_these_simple_actions_mock_component;\n};\n\nstruct Metavars {\n  using observed_reduction_data_tags = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<3>, control_system::Tags::Verbosity>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 control_system::Tags::MeasurementTimescales>;\n  using component_list =\n      tmpl::list<MockComponent<Metavars>,\n                 ::TestHelpers::observers::MockObserverWriter<Metavars>>;\n};\n\nvoid test_skew_process_measurement() {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::creators::register_derived_with_charm();\n  const domain::creators::Brick creator{\n      std::array{-10.0, -10.0, -10.0},\n      std::array{10.0, 10.0, 10.0},\n      std::array{0_st, 0_st, 0_st},\n      std::array{8_st, 8_st, 8_st},\n      std::array{false, false, false},\n      {},\n      std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          0.0, std::array{0.0, 0.0, 0.0}, std::array{0.0, 0.0, 0.0})};\n\n  using component = MockComponent<Metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavars>;\n  MockRuntimeSystem runner{\n      {creator.create_domain(), ::Verbosity::Silent, 4, true, false,\n       std::unordered_map<std::string, bool>{},\n       tnsr::I<double, 3, Frame::Grid>{std::array{0.0, 0.0, 0.0}},\n       tnsr::I<double, 3, Frame::Grid>{std::array{0.0, 0.0, 0.0}},\n       std::unordered_map<std::string, std::string>{}},\n      {creator.functions_of_time(),\n       control_system::Tags::MeasurementTimescales::type{}}};\n\n  ActionTesting::emplace_singleton_component<component>(make_not_null(&runner),\n                                                        {0}, {0});\n\n  auto& cache = ActionTesting::cache<component>(runner, 0);\n\n  // The data here doesn't matter except for the excision surface because it's\n  // the only one that is used in any calculations. And even then, the only\n  // restriction is that is must be contained within our domain above.\n  const LinkedMessageId<double> id{0.0, std::nullopt};\n  const ylm::Strahlkorper<Frame::Distorted> horizon_a{\n      4_st, 1.0, std::array{0.0, 0.0, 0.0}};\n  const ylm::Strahlkorper<Frame::Distorted> horizon_b{\n      4_st, 2.0, std::array{0.0, 0.0, 0.0}};\n\n  skew::process_measurement::apply(\n      both_horizons::FindHorizon<domain::ObjectLabel::A>{}, horizon_a, cache,\n      id);\n\n  // We only check that the proper number of actions have been called.\n  CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner, 0) ==\n        1);\n  CHECK(message_queue_call_count == 0);\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n\n  skew::process_measurement::apply(\n      both_horizons::FindHorizon<domain::ObjectLabel::B>{}, horizon_b, cache,\n      id);\n\n  CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner, 0) ==\n        1);\n  CHECK(message_queue_call_count == 1);\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n\n  CHECK(ActionTesting::is_simple_action_queue_empty<component>(runner, 0));\n  CHECK(message_queue_call_count == 2);\n}\n\nvoid test_names() {\n  CHECK(pretty_type::name<skew>() == \"Skew\");\n  CHECK(*skew::component_name(0, 2) == \"Y\");\n  CHECK(*skew::component_name(1, 2) == \"Z\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Systems.Skew\", \"[ControlSystem][Unit]\") {\n  test_names();\n  test_skew_process_measurement();\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Systems/Test_Translation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"ControlSystem/Systems/Translation.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace control_system {\nnamespace {\nusing TranslationMap = domain::CoordinateMaps::TimeDependent::Translation<3>;\n\nusing CoordMap =\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap>;\n\ntemplate <size_t DerivOrder>\nvoid test_translation_control_system() {\n  // Since we are only doing translation, turn off the\n  // other control systems by passing 0 for their deriv orders\n  using metavars =\n      TestHelpers::control_system::MockMetavars<DerivOrder, 0, 0, 0>;\n  using translation_component = typename metavars::translation_component;\n  using element_component = typename metavars::element_component;\n  using translation_system = typename metavars::translation_system;\n  MAKE_GENERATOR(gen);\n\n  // Global things\n  domain::FunctionsOfTime::register_derived_with_charm();\n  const double initial_time = 0.0;\n  const double initial_separation = 15.0;\n  // This final time is chosen so that the damping timescales have adequate time\n  // to reach the maximum damping timescale\n  const double final_time = 600.0;\n\n  // Set up the system helper.\n  TestHelpers::control_system::SystemHelper<metavars> system_helper{};\n\n  const std::string input_options =\n      \"Evolution:\\n\"\n      \"  InitialTime: 0.0\\n\"\n      \"DomainCreator:\\n\"\n      \"  FakeCreator:\\n\"\n      \"    NumberOfComponents:\\n\"\n      \"      Translation: 3\\n\"\n      \"ControlSystems:\\n\"\n      \"  WriteDataToDisk: false\\n\"\n      \"  MeasurementsPerUpdate: 4\\n\"\n      \"  DelayUpdate: true\\n\"\n      \"  Translation:\\n\"\n      \"    Averager:\\n\"\n      \"      AverageTimescaleFraction: 0.25\\n\"\n      \"      Average0thDeriv: true\\n\"\n      \"    Controller:\\n\"\n      \"      UpdateFraction: 0.3\\n\"\n      \"    TimescaleTuner:\\n\"\n      \"      InitialTimescales: 0.3\\n\"\n      \"      MinTimescale: 0.1\\n\"\n      \"      MaxTimescale: 10.\\n\"\n      \"      DecreaseThreshold: 2.0\\n\"\n      \"      IncreaseThreshold: 0.1\\n\"\n      \"      IncreaseFactor: 1.01\\n\"\n      \"      DecreaseFactor: 0.99\\n\"\n      \"    ControlError:\\n\";\n\n  // Initialize everything within the system helper\n  system_helper.setup_control_system_test(\n      initial_time, initial_separation, input_options,\n      TestHelpers::control_system::initialize_translation_functions_of_time<\n          translation_system>);\n\n  // Get references to everything that was set up inside the system helper. The\n  // domain and two functions of time are not const references because they need\n  // to be moved into the runner\n  auto& domain = system_helper.domain();\n  auto& is_active_map = system_helper.is_active_map();\n  auto& initial_functions_of_time = system_helper.initial_functions_of_time();\n  auto& initial_measurement_timescales =\n      system_helper.initial_measurement_timescales();\n  const auto& init_trans_tuple =\n      system_helper.template init_tuple<translation_system>();\n  auto system_to_combined_names = system_helper.system_to_combined_names();\n  const std::string translation_name =\n      system_helper.template name<translation_system>();\n\n  auto grid_center_A = domain.excision_spheres().at(\"ExcisionSphereA\").center();\n  auto grid_center_B = domain.excision_spheres().at(\"ExcisionSphereB\").center();\n\n  // Setup runner and all components\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {\"DummyFileName\", std::move(domain), 4, true, false, ::Verbosity::Silent,\n       std::move(is_active_map), std::move(grid_center_A),\n       std::move(grid_center_B), std::move(system_to_combined_names)},\n      {std::move(initial_functions_of_time),\n       std::move(initial_measurement_timescales)}};\n  ActionTesting::emplace_singleton_component_and_initialize<\n      translation_component>(make_not_null(&runner), ActionTesting::NodeId{0},\n                             ActionTesting::LocalCoreId{0}, init_trans_tuple);\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Create coordinate map for mapping the translation to the \"grid\" frame\n  // where the control system does its calculations\n  TranslationMap translation_map{translation_name};\n\n  CoordMap coord_map{translation_map};\n\n  // Get the functions of time from the cache to use in the maps\n  const auto& cache = ActionTesting::cache<element_component>(runner, 0);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n\n  const std::array<double, 3> velocity{{0.1, -0.2, 0.3}};\n\n  const auto position_function = [&initial_separation,\n                                  &velocity](const double time) {\n    std::array<tnsr::I<double, 3>, 2> result{};\n    get<0>(result[0]) = 0.5 * initial_separation + velocity[0] * time;\n    get<1>(result[0]) = velocity[1] * time;\n    get<2>(result[0]) = velocity[2] * time;\n    get<0>(result[1]) = -0.5 * initial_separation + velocity[0] * time;\n    get<1>(result[1]) = velocity[1] * time;\n    get<2>(result[1]) = velocity[2] * time;\n    return result;\n  };\n\n  const auto horizon_function = [&position_function, &runner,\n                                 &coord_map](const double time) {\n    return TestHelpers::control_system::\n        build_horizons_for_basic_control_systems<element_component>(\n            time, runner, position_function, coord_map);\n  };\n\n  // Run the actual control system test.\n  system_helper.run_control_system_test(runner, final_time, make_not_null(&gen),\n                                        horizon_function);\n\n  // Grab results\n  const auto grid_positions = TestHelpers::control_system::\n      grid_frame_horizon_centers_for_basic_control_systems<element_component>(\n          final_time, runner, position_function, coord_map);\n\n  // Our expected positions are just the initial positions\n  const tnsr::I<double, 3, Frame::Grid> expected_grid_position_of_a{\n      {0.5 * initial_separation, 0.0, 0.0}};\n  const tnsr::I<double, 3, Frame::Grid> expected_grid_position_of_b{\n      {-0.5 * initial_separation, 0.0, 0.0}};\n\n  const auto& translation_f_of_t =\n      dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>&>(\n          *functions_of_time.at(translation_name));\n\n  const auto trans_and_2_derivs =\n      translation_f_of_t.func_and_2_derivs(final_time);\n\n  // The control system gets more accurate the longer you run for. This is\n  // the accuracy we can achieve in this amount of time.\n  Approx custom_approx = Approx::custom().epsilon(1.0e-8).scale(1.0);\n  const DataVector expected_translation_2nd_deriv{3, 0.0};\n  CHECK_ITERABLE_CUSTOM_APPROX(trans_and_2_derivs[2],\n                               expected_translation_2nd_deriv, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(trans_and_2_derivs[1], DataVector(velocity),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      trans_and_2_derivs[0], DataVector(velocity * final_time), custom_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_grid_position_of_a, grid_positions[0],\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_grid_position_of_b, grid_positions[1],\n                               custom_approx);\n}\n\nvoid test_names() {\n  using translation = control_system::Systems::Translation<\n      2, control_system::measurements::BothHorizons, 2>;\n\n  CHECK(pretty_type::name<translation>() == \"Translation\");\n  CHECK(*translation::component_name(0, 3) == \"x\");\n  CHECK(*translation::component_name(1, 3) == \"y\");\n  CHECK(*translation::component_name(2, 3) == \"z\");\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::string component_name = *translation::component_name(1, 4);\n        (void)component_name;\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Translation control expects 3 components but there are 4 instead.\"));\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Systems.Translation\",\n                  \"[ControlSystem][Unit]\") {\n  test_names();\n  test_translation_control_system<2>();\n  test_translation_control_system<3>();\n}\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Tags/Test_FunctionsOfTimeInitialize.cpp\n  Tags/Test_FutureMeasurements.cpp\n  Tags/Test_MeasurementTimescales.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Tags/Test_FunctionsOfTimeInitialize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Tags/FunctionsOfTimeInitialize.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace OptionTags {\nstruct InitialTime;\n}  // namespace OptionTags\n\nnamespace {\nconst double initial_time = 2.0;\n\ntemplate <size_t Index>\nstruct FakeControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static constexpr size_t deriv_order = 2;\n  static std::string name() { return \"Controlled\"s + get_output(Index); }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return get_output(i);\n  }\n  using measurement = control_system::TestHelpers::Measurement<\n      control_system::TestHelpers::TestStructs_detail::LabelA>;\n  using simple_tags = tmpl::list<>;\n  using control_error = control_system::TestHelpers::ControlError;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n\n  using control_systems = tmpl::list<FakeControlSystem<1>, FakeControlSystem<2>,\n                                     FakeControlSystem<3>>;\n  using component_list =\n      control_system::control_components<Metavariables, control_systems>;\n};\n\nstruct MetavariablesNoControlSystems {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<>;\n};\n\nclass TestCreator : public DomainCreator<1> {\n public:\n  explicit TestCreator(const bool add_controlled)\n      : add_controlled_(add_controlled) {}\n  Domain<1> create_domain() const override { ERROR(\"\"); }\n  std::vector<DirectionMap<\n      1, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override {\n    ERROR(\"\");\n  }\n  std::vector<std::array<size_t, 1>> initial_extents() const override {\n    ERROR(\"\");\n  }\n  std::vector<std::array<size_t, 1>> initial_refinement_levels()\n      const override {\n    ERROR(\"\");\n  }\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override {\n    const std::array<DataVector, 3> initial_values{{{-1.0}, {-2.0}, {-3.0}}};\n    const std::array<DataVector, 3> initial_values3{\n        {{-1.0, 1.0}, {-2.0, 2.0}, {-3.0, 3.0}}};\n\n    std::unordered_map<std::string, double> expiration_times{\n        {\"Uncontrolled\", std::numeric_limits<double>::infinity()}};\n    if (add_controlled_) {\n      expiration_times[\"Controlled1\"] = std::numeric_limits<double>::infinity();\n      expiration_times[\"Controlled2\"] = std::numeric_limits<double>::infinity();\n      expiration_times[\"Controlled3\"] = std::numeric_limits<double>::infinity();\n      for (auto& [name, expr_time] : initial_expiration_times) {\n        if (expiration_times.count(name) == 1) {\n          expiration_times[name] = expr_time;\n        }\n      }\n    }\n\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        result{};\n\n    if (add_controlled_) {\n      result.insert(\n          {\"Controlled1\",\n           std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n               initial_time, initial_values,\n               expiration_times.at(\"Controlled1\"))});\n      result.insert(\n          {\"Controlled2\",\n           std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n               initial_time, initial_values,\n               expiration_times.at(\"Controlled2\"))});\n      result.insert(\n          {\"Controlled3\",\n           std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n               initial_time, initial_values3,\n               expiration_times.at(\"Controlled3\"))});\n    }\n    result.insert(\n        {\"Uncontrolled\",\n         std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n             initial_time, initial_values,\n             expiration_times.at(\"Uncontrolled\"))});\n    return result;\n  }\n\n private:\n  bool add_controlled_{};\n};\n\nclass BadCreator : public DomainCreator<1> {\n public:\n  Domain<1> create_domain() const override { ERROR(\"\"); }\n  std::vector<DirectionMap<\n      1, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override {\n    ERROR(\"\");\n  }\n  std::vector<std::array<size_t, 1>> initial_extents() const override {\n    ERROR(\"\");\n  }\n  std::vector<std::array<size_t, 1>> initial_refinement_levels()\n      const override {\n    ERROR(\"\");\n  }\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                             initial_expiration_times = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override {\n    TestCreator good_creator{true};\n    auto functions_of_time =\n        good_creator.functions_of_time(initial_expiration_times);\n\n    // Mimick a domain creator that has improperly set the expiration time for\n    // one of the functions of time\n    const auto& function_to_replace = functions_of_time.begin()->second;\n    if (not initial_expiration_times.empty()) {\n      functions_of_time[initial_expiration_times.begin()->first] =\n          std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n              initial_time,\n              function_to_replace->func_and_2_derivs(initial_time),\n              std::numeric_limits<double>::infinity());\n    }\n\n    return functions_of_time;\n  }\n};\n\ntemplate <size_t Index>\nusing OptionHolder = control_system::OptionHolder<FakeControlSystem<Index>>;\n\ntemplate <typename ControlSys>\nusing ControlSysInputs =\n    control_system::OptionTags::ControlSystemInputs<ControlSys>;\n\nconstexpr int measurements_per_update = 4;\n\nvoid test_functions_of_time_tag(const bool delay_update) {\n  INFO(\"Test FunctionsOfTimeInitialize tag\");\n  CAPTURE(delay_update);\n  using fot_tag = control_system::Tags::FunctionsOfTimeInitialize;\n  using Creator = std::unique_ptr<::DomainCreator<1>>;\n\n  const Creator creator = std::make_unique<TestCreator>(true);\n\n  // Initial expiration times are set to be update_fraction *\n  // min(current_timescale) where update_fraction is the argument to the\n  // Controller. This value for the timescale was chosen to give an expiration\n  // time between the two expiration times used above in the TestCreator\n  const double timescale = 27.0;\n  const double timescale2 = 0.1;\n  const TimescaleTuner<true> tuner1(std::vector<double>{timescale}, 10.0,\n                                    1.0e-3, 1.0e-4, 1.01, 1.0e-2, 0.99);\n  const TimescaleTuner<true> tuner2(timescale2, 10.0, 1.0e-3, 1.0e-4, 1.01,\n                                    1.0e-2, 0.99);\n  const Averager<1> averager(0.25, true);\n  const double update_fraction = 0.3;\n  const Controller<2> controller(update_fraction);\n  const control_system::TestHelpers::ControlError control_error{};\n\n  const std::optional<OptionHolder<1>> option_holder1 = std::nullopt;\n  const std::optional<OptionHolder<2>> option_holder2 =\n      OptionHolder<2>{averager, controller, tuner1, control_error};\n  const std::optional<OptionHolder<3>> option_holder3 =\n      OptionHolder<3>{averager, controller, tuner2, control_error};\n\n  // First test construction with only control systems\n  fot_tag::type functions_of_time = fot_tag::create_from_options<Metavariables>(\n      creator, measurements_per_update, delay_update, initial_time,\n      option_holder1, option_holder2, option_holder3);\n\n  const double min_measurement_timescale = update_fraction *\n                                           std::min(timescale, timescale2) /\n                                           measurements_per_update;\n  double min_expiration_time = initial_time;\n  // First measurement is initial_time, so start counter at 1\n  for (int measure = 1; measure < measurements_per_update; ++measure) {\n    min_expiration_time += min_measurement_timescale;\n  }\n  if (delay_update) {\n    min_expiration_time += min_measurement_timescale;\n  }\n\n  // 1 isn't active\n  CHECK(functions_of_time.at(\"Controlled1\")->time_bounds()[1] ==\n        std::numeric_limits<double>::infinity());\n  CHECK(functions_of_time.at(\"Controlled2\")->time_bounds()[1] ==\n        min_expiration_time);\n  CHECK(functions_of_time.at(\"Controlled3\")->time_bounds()[1] ==\n        min_expiration_time);\n  CHECK(functions_of_time.at(\"Uncontrolled\")->time_bounds()[1] ==\n        std::numeric_limits<double>::infinity());\n\n  static_assert(\n      std::is_same_v<\n          fot_tag::option_tags<Metavariables>,\n          tmpl::list<\n              domain::OptionTags::DomainCreator<Metavariables::volume_dim>,\n              control_system::OptionTags::MeasurementsPerUpdate,\n              control_system::OptionTags::DelayUpdate,\n              ::OptionTags::InitialTime, ControlSysInputs<FakeControlSystem<1>>,\n              ControlSysInputs<FakeControlSystem<2>>,\n              ControlSysInputs<FakeControlSystem<3>>>>);\n\n  {\n    // Next test construction without control systems\n    static_assert(\n        std::is_same_v<fot_tag::option_tags<MetavariablesNoControlSystems>,\n                       tmpl::list<domain::OptionTags::DomainCreator<\n                           MetavariablesNoControlSystems::volume_dim>>>);\n    const Creator not_controlled_creator = std::make_unique<TestCreator>(false);\n    auto no_control_sys_fot =\n        fot_tag::create_from_options<MetavariablesNoControlSystems>(\n            not_controlled_creator);\n    CHECK(no_control_sys_fot.size() == 1);\n    CHECK(no_control_sys_fot.contains(\"Uncontrolled\"));\n    // -3.0 comes from the TestCreator above\n    CHECK(no_control_sys_fot.at(\"Uncontrolled\")\n              ->func_and_2_derivs(initial_time)[2][0] == -3.0);\n    CHECK_FALSE(no_control_sys_fot.contains(\"Controlled2\"));\n  }\n}\n\nusing fot_tag = control_system::Tags::FunctionsOfTimeInitialize;\nusing Creator = std::unique_ptr<::DomainCreator<1>>;\n\nvoid not_controlling(const bool is_active) {\n  const Creator creator = std::make_unique<TestCreator>(true);\n\n  const TimescaleTuner<true> tuner(std::vector<double>{1.0}, 10.0, 1.0e-3,\n                                   1.0e-4, 1.01, 1.0e-2, 0.99);\n  const Averager<1> averager(0.25, true);\n  const double update_fraction = 0.3;\n  const Controller<2> controller(update_fraction);\n  const control_system::TestHelpers::ControlError control_error{};\n  constexpr bool delay_update = true;\n\n  const std::optional<OptionHolder<1>> option_holder1 =\n      is_active ? std::optional{OptionHolder<1>{averager, controller, tuner,\n                                                control_error}}\n                : std::nullopt;\n  const std::optional<OptionHolder<2>> option_holder2 =\n      is_active ? std::optional{OptionHolder<2>{averager, controller, tuner,\n                                                control_error}}\n                : std::nullopt;\n  const std::optional<OptionHolder<3>> option_holder3 =\n      is_active ? std::optional{OptionHolder<3>{averager, controller, tuner,\n                                                control_error}}\n                : std::nullopt;\n  const std::optional<OptionHolder<4>> option_holder4 =\n      is_active ? std::optional{OptionHolder<4>{averager, controller, tuner,\n                                                control_error}}\n                : std::nullopt;\n\n  [[maybe_unused]] fot_tag::type functions_of_time =\n      fot_tag::create_from_options<Metavariables>(\n          creator, measurements_per_update, delay_update, initial_time,\n          option_holder1, option_holder2, option_holder3, option_holder4);\n}\n\nvoid incompatible(const bool is_active) {\n  const Creator creator = std::make_unique<BadCreator>();\n\n  const TimescaleTuner<true> tuner(std::vector<double>{1.0}, 10.0, 1.0e-3,\n                                   1.0e-4, 1.01, 1.0e-2, 0.99);\n  const Averager<1> averager(0.25, true);\n  const double update_fraction = 0.3;\n  const Controller<2> controller(update_fraction);\n  const control_system::TestHelpers::ControlError control_error{};\n  constexpr bool delay_update = true;\n\n  const std::optional<OptionHolder<1>> option_holder1 =\n      is_active ? std::optional{OptionHolder<1>{averager, controller, tuner,\n                                                control_error}}\n                : std::nullopt;\n  const std::optional<OptionHolder<2>> option_holder2 =\n      is_active ? std::optional{OptionHolder<2>{averager, controller, tuner,\n                                                control_error}}\n                : std::nullopt;\n  const std::optional<OptionHolder<3>> option_holder3 =\n      is_active ? std::optional{OptionHolder<3>{averager, controller, tuner,\n                                                control_error}}\n                : std::nullopt;\n\n  [[maybe_unused]] fot_tag::type functions_of_time =\n      fot_tag::create_from_options<Metavariables>(\n          creator, measurements_per_update, delay_update, initial_time,\n          option_holder1, option_holder2, option_holder3);\n}\n\nvoid test_errors(const bool is_active) {\n  if (is_active) {\n    CHECK_THROWS_WITH(\n        not_controlling(is_active),\n        Catch::Matchers::ContainsSubstring(\n            \"is not controlling a function of time. Check that the \"\n            \"DomainCreator you have chosen uses all of the control systems in \"\n            \"the executable. The existing functions of time are\"));\n    CHECK_THROWS_WITH(incompatible(is_active),\n                      Catch::Matchers::ContainsSubstring(\n                          \"It is possible that the DomainCreator you are using \"\n                          \"isn't compatible with the control systems\"));\n  } else {\n    CHECK_NOTHROW(not_controlling(is_active));\n    CHECK_NOTHROW(incompatible(is_active));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Tags.FunctionsOfTimeInitialize\",\n                  \"[ControlSystem][Unit]\") {\n  test_functions_of_time_tag(true);\n  test_functions_of_time_tag(false);\n  test_errors(true);\n  test_errors(false);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Tags/Test_FutureMeasurements.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"ControlSystem/Tags/FutureMeasurements.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct ControlSystem;\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Tags.FutureMeasurements\",\n                  \"[Unit][ControlSystem]\") {\n  TestHelpers::db::test_simple_tag<\n      control_system::Tags::FutureMeasurements<tmpl::list<ControlSystem>>>(\n      \"FutureMeasurements\");\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Tags/Test_MeasurementTimescales.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/CalculateMeasurementTimescales.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace OptionTags {\nstruct InitialTime;\nstruct InitialTimeStep;\n}  // namespace OptionTags\n\nnamespace {\ntemplate <size_t Index>\nstruct Label {};\nconst double initial_time = 0.9;\n\ntemplate <size_t Index>\nstruct FakeControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static constexpr size_t deriv_order = 2;\n  static std::string name() { return \"Controlled\"s + get_output(Index); }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return get_output(i);\n  }\n  using measurement =\n      control_system::TestHelpers::Measurement<Label<Index % 3>>;\n  using simple_tags = tmpl::list<>;\n  using control_error = control_system::TestHelpers::ControlError;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  // This will create 5 control systems and 3 measurements. Two measurements\n  // have two control systems and one measurement has only one control system.\n  // The indices are chosen because of the % 3 above\n  using control_systems = tmpl::list<FakeControlSystem<1>, FakeControlSystem<2>,\n                                     FakeControlSystem<4>, FakeControlSystem<5>,\n                                     FakeControlSystem<6>>;\n  using component_list =\n      control_system::control_components<Metavariables, control_systems>;\n};\n\ntemplate <size_t DerivOrder>\nvoid test_calculate_measurement_timescales() {\n  INFO(\"Test calculate measurement timescales\");\n  const double timescale = 20.0;\n  const TimescaleTuner<true> tuner(std::vector<double>{timescale}, 10.0, 1.0e-3,\n                                   1.0e-4, 1.01, 1.0e-2, 0.99);\n  const double update_fraction = 0.25;\n  const Controller<DerivOrder> controller(update_fraction);\n\n  const int measurements_per_update = 4;\n  const auto measurement_timescales =\n      control_system::calculate_measurement_timescales(controller, tuner,\n                                                       measurements_per_update);\n\n  const DataVector expected_measurement_timescales =\n      DataVector{{timescale}} * update_fraction /\n      double(measurements_per_update);\n\n  CHECK(expected_measurement_timescales == measurement_timescales);\n}\n\ntemplate <size_t Index>\nusing OptionHolder = control_system::OptionHolder<FakeControlSystem<Index>>;\n\nvoid test_measurement_tag() {\n  INFO(\"Test measurement tag\");\n  using measurement_tag = control_system::Tags::MeasurementTimescales;\n  static_assert(\n      tmpl::size<measurement_tag::option_tags<Metavariables>>::value == 10);\n\n  using FakeCreator = TestHelpers::control_system::FakeCreator;\n\n  const double time_step = 0.2;\n  {\n    const double averaging_fraction = 0.25;\n    const Averager<1> averager(averaging_fraction, true);\n    const double update_fraction = 0.3;\n    const Controller<2> controller(update_fraction);\n    const control_system::TestHelpers::ControlError control_error{};\n\n    const double timescale_long = 27.0;\n    const TimescaleTuner<true> tuner1(\n        std::vector<double>{timescale_long, timescale_long * 2.0}, 10.0, 1.0e-3,\n        1.0e-4, 1.01, 1.0e-2, 0.99);\n    const double timescale_short = 0.5;\n    TimescaleTuner<true> tuner2(timescale_short, 10.0, 1.0e-3, 1.0e-4, 1.01,\n                                1.0e-2, 0.99);\n    tuner2.resize_timescales(2);\n    const TimescaleTuner<true> tuner4 = tuner2;\n    const TimescaleTuner<true>& tuner5 = tuner1;\n\n    const std::optional<OptionHolder<1>> option_holder1 =\n        OptionHolder<1>{averager, controller, tuner1, control_error};\n    // Control system 2 is not active so the measurement timescale and\n    // expiration time should both be infinity\n    const std::optional<OptionHolder<2>> option_holder2 = std::nullopt;\n    const std::optional<OptionHolder<4>> option_holder4 =\n        OptionHolder<4>{averager, controller, tuner4, control_error};\n    const std::optional<OptionHolder<5>> option_holder5 =\n        OptionHolder<5>{averager, controller, tuner5, control_error};\n    const std::optional<OptionHolder<6>> option_holder6 = std::nullopt;\n\n    const std::unique_ptr<DomainCreator<3>> creator =\n        std::make_unique<FakeCreator>(std::unordered_map<std::string, size_t>{\n            {FakeControlSystem<1>::name(), 2},\n            {FakeControlSystem<2>::name(), 2},\n            {FakeControlSystem<4>::name(), 2},\n            {FakeControlSystem<5>::name(), 2},\n            {FakeControlSystem<6>::name(), 2}});\n\n    static_assert(\n        std::is_same_v<\n            measurement_tag::option_tags<Metavariables>,\n            tmpl::list<control_system::OptionTags::MeasurementsPerUpdate,\n                       control_system::OptionTags::DelayUpdate,\n                       domain::OptionTags::DomainCreator<3>,\n                       ::OptionTags::InitialTime, ::OptionTags::InitialTimeStep,\n                       control_system::OptionTags::ControlSystemInputs<\n                           FakeControlSystem<1>>,\n                       control_system::OptionTags::ControlSystemInputs<\n                           FakeControlSystem<2>>,\n                       control_system::OptionTags::ControlSystemInputs<\n                           FakeControlSystem<4>>,\n                       control_system::OptionTags::ControlSystemInputs<\n                           FakeControlSystem<5>>,\n                       control_system::OptionTags::ControlSystemInputs<\n                           FakeControlSystem<6>>>>);\n    const int measurements_per_update = 4;\n    const bool delay_update = true;\n    const measurement_tag::type timescales =\n        measurement_tag::create_from_options<Metavariables>(\n            measurements_per_update, delay_update, creator, initial_time,\n            time_step, option_holder1, option_holder2, option_holder4,\n            option_holder5, option_holder6);\n    CHECK(timescales.size() == 3);\n    CHECK(timescales.count(\"Controlled1Controlled4\") == 1);\n    CHECK(timescales.count(\"Controlled2Controlled5\") == 1);\n    CHECK(timescales.count(\"Controlled6\") == 1);\n\n    const double measure_time14 =\n        std::min(min(control_system::calculate_measurement_timescales(\n                     controller, tuner1, measurements_per_update)),\n                 min(control_system::calculate_measurement_timescales(\n                     controller, tuner4, measurements_per_update)));\n    // Control system 2 is turned off\n    const double measure_time25 =\n        min(control_system::calculate_measurement_timescales(\n            controller, tuner5, measurements_per_update));\n    const double measure_time6 = std::numeric_limits<double>::infinity();\n    const double expr_time14 =\n        initial_time + update_fraction * timescale_short - 0.5 * measure_time14;\n    const double expr_time25 =\n        initial_time + update_fraction * timescale_long - 0.5 * measure_time25;\n    const double expr_time6 = std::numeric_limits<double>::infinity();\n\n    CHECK(timescales.at(\"Controlled1Controlled4\")->time_bounds() ==\n          std::array{initial_time, expr_time14});\n    CHECK(timescales.at(\"Controlled1Controlled4\")->func(1.0)[0] ==\n          DataVector{measure_time14});\n\n    CHECK(timescales.at(\"Controlled2Controlled5\")->time_bounds() ==\n          std::array{initial_time, expr_time25});\n    CHECK(timescales.at(\"Controlled2Controlled5\")->func(2.0)[0] ==\n          DataVector{measure_time25});\n\n    CHECK(timescales.at(\"Controlled6\")->time_bounds() ==\n          std::array{initial_time, expr_time6});\n    CHECK(timescales.at(\"Controlled6\")->func(2.0)[0] ==\n          DataVector{measure_time6});\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const TimescaleTuner<true> tuner1(std::vector<double>{27.0}, 10.0,\n                                          1.0e-3, 1.0e-4, 1.01, 1.0e-2, 0.99);\n        const TimescaleTuner<true> tuner2(std::vector<double>{0.1}, 10.0,\n                                          1.0e-3, 1.0e-4, 1.01, 1.0e-2, 0.99);\n        const Averager<1> averager(0.25, true);\n        const Controller<2> controller(0.3);\n        const control_system::TestHelpers::ControlError control_error{};\n\n        std::optional<OptionHolder<1>> option_holder1 =\n            OptionHolder<1>{averager, controller, tuner1, control_error};\n        std::optional<OptionHolder<2>> option_holder2 =\n            OptionHolder<2>{averager, controller, tuner1, control_error};\n        std::optional<OptionHolder<3>> option_holder3 =\n            OptionHolder<3>{averager, controller, tuner2, control_error};\n\n        const std::unique_ptr<DomainCreator<3>> creator =\n            std::make_unique<FakeCreator>(\n                std::unordered_map<std::string, size_t>{\n                    {FakeControlSystem<1>::name(), 1},\n                    {FakeControlSystem<2>::name(), 1},\n                    {FakeControlSystem<3>::name(), 1}});\n\n        measurement_tag::create_from_options<Metavariables>(\n            4, true, creator, initial_time, -1.0, option_holder1,\n            option_holder2, option_holder3);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Control systems can only be used in forward-in-time evolutions.\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Tags.MeasurementTimescales\",\n                  \"[ControlSystem][Unit]\") {\n  test_calculate_measurement_timescales<2>();\n  test_calculate_measurement_timescales<3>();\n  test_measurement_tag();\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_Averager.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\ntemplate <size_t DerivOrder>\nvoid test_linear() {\n  INFO(\"Test linear.\");\n  double t = 0.0;\n  const double dt = 0.1;\n  const double final_time = 5.0;\n\n  // test true and false `using_average_0th_deriv_of_q`\n  Averager<DerivOrder> averager_t(0.5, true);\n  Averager<DerivOrder> averager_f(0.5, false);\n\n  CHECK(averager_t.using_average_0th_deriv_of_q());\n  CHECK_FALSE(averager_f.using_average_0th_deriv_of_q());\n\n  // define custom approx for higher derivative checks\n  Approx custom_approx = Approx::custom().epsilon(1.0e-11).scale(1.0);\n\n  while (t < final_time) {\n    // test using an analytic function f(t) = t\n    DataVector analytic_func{DerivOrder + 1, 0.0};\n    analytic_func[0] = t;\n    analytic_func[1] = 1.0;\n\n    // update exponential averager\n    averager_t.update(t, {analytic_func[0]}, {0.1});\n    averager_f.update(t, {analytic_func[0]}, {0.1});\n    // compare values once averager has sufficient data\n    if (averager_t(t)) {\n      const auto result_t = averager_t(t).value();\n      // check function value, which should agree with the effective time\n      CHECK(approx(result_t[0][0]) == averager_t.average_time(t));\n      // check derivatives\n      // The exponential averager uses finite differencing to approximate the\n      // derivatives, which is why we enforce a less stringent check here.\n      for (size_t i = 1; i < DerivOrder + 1; i++) {\n        CHECK(custom_approx(gsl::at(result_t, i)[0]) == analytic_func[i]);\n      }\n    }\n    if (averager_f(t)) {\n      const auto result_f = averager_f(t).value();\n      // check function value, which should agree with the true time `t`\n      CHECK(approx(result_f[0][0]) == t);\n      // check derivatives\n      // The exponential averager uses finite differencing to approximate the\n      // derivatives, which is why we enforce a less stringent check here.\n      for (size_t i = 1; i < DerivOrder + 1; i++) {\n        CHECK(custom_approx(gsl::at(result_f, i)[0]) == analytic_func[i]);\n      }\n    }\n\n    t += dt;\n  }\n}\n\ntemplate <size_t DerivOrder>\nvoid test_semianalytic() {\n  INFO(\"Test semianalytic.\");\n  double t = 0.0;\n  const double final_time = 5.0;\n\n  // The equations suggests that our exponential averager is equivalent\n  // to the simple exponential averaging method of\n  // F_avg(t) = \\alpha*F(t) + (1 - \\alpha)*F_avg(t_{-1}}\n  // where \\alpha = \\tau_m / (W(t)*D) and D = 1.0 + \\tau_m/tau_avg\n  // However, the exponential averager that we are testing has time varying\n  // weight, so we allow \\alpha to be a function of time and update at\n  // each timestep.\n\n  // some vars for a simple exponential average to compare against\n  std::array<DataVector, DerivOrder + 1> avg_values =\n      make_array<DerivOrder + 1, DataVector>(DataVector{1, 0.0});\n  double avg_time = 0.0;\n\n  // the measurement timescale (tau_m)\n  const double tau_m = 0.1;\n  const double avg_tscale_fac = 1.0;\n  const double damping_time = 0.1;\n  const double denom = 1.0 + tau_m / (avg_tscale_fac * damping_time);\n  // the average weight, represents W(t) in the above comment\n  double avg_weight = 0.0;\n\n  Averager<DerivOrder> averager(avg_tscale_fac, true);\n\n  // define custom approx for second derivative checks\n  Approx custom_approx = Approx::custom().epsilon(1.0e-11).scale(1.0);\n\n  while (t < final_time) {\n    // test using an analytic function f(t) = t**DerivOrder\n    DataVector analytic_func{DerivOrder + 1, 0.0};\n    if constexpr (DerivOrder == 1) {\n      analytic_func[0] = t;\n      analytic_func[1] = 1.0;\n    } else if constexpr (DerivOrder == 2) {\n      analytic_func[0] = square(t);\n      analytic_func[1] = 2.0 * t;\n      analytic_func[2] = 2.0;\n    } else if constexpr (DerivOrder == 3) {\n      analytic_func[0] = cube(t);\n      analytic_func[1] = 3.0 * square(t);\n      analytic_func[2] = 6.0 * t;\n      analytic_func[3] = 6.0;\n    }\n\n    // update exponential averager\n    averager.update(t, {analytic_func[0]}, {damping_time});\n    // compare values once averager has sufficient data\n    if (averager(t)) {\n      // update the weight and \\alpha\n      avg_weight = (tau_m + avg_weight) / denom;\n      const double alpha = tau_m / avg_weight / denom;\n\n      // do simple average of time, analytic func and its analytic derivatives\n      avg_time = alpha * t + (1.0 - alpha) * avg_time;\n      for (size_t i = 0; i < DerivOrder + 1; i++) {\n        gsl::at(avg_values, i) =\n            alpha * analytic_func[i] + (1.0 - alpha) * gsl::at(avg_values, i);\n      }\n      auto result = averager(t).value();\n\n      // check that the effective times agree with the averaged time\n      CHECK(approx(averager.average_time(t)) == avg_time);\n      // check function value with stricter approx\n      CHECK(approx(result[0][0]) == avg_values[0][0]);\n      // check derivatives\n      // Again, this check is slightly looser than the others due to\n      // numerical differentiation (see comment in Averager.Linear test)\n      CHECK_ITERABLE_CUSTOM_APPROX(result, avg_values, custom_approx);\n    } else {\n      avg_time = t;\n      for (size_t i = 0; i < DerivOrder + 1; i++) {\n        gsl::at(avg_values, i) = analytic_func[i];\n      }\n    }\n\n    t += tau_m;\n  }\n}\n\ntemplate <size_t DerivOrder>\nvoid test_functionality() {\n  INFO(\"Test functionality.\");\n  double t = 0.0;\n  const double dt = 0.1;\n\n  Averager<DerivOrder> averager(0.5, false);\n\n  // test the validity of data functionality\n  // data not valid yet\n  CHECK_FALSE(static_cast<bool>(averager(t)));\n  // first update\n  averager.update(t, {t}, {0.1});\n  // data not valid yet\n  CHECK_FALSE(static_cast<bool>(averager(t)));\n  t += dt;\n  // second update\n  averager.update(t, {t}, {0.1});\n  // different deriv orders need different number of updates for data to become\n  // valid\n  if constexpr (DerivOrder > 1) {\n    // data not valid yet\n    CHECK_FALSE(static_cast<bool>(averager(t)));\n    t += dt;\n    // third update\n    averager.update(t, {t}, {0.1});\n  }\n  if constexpr (DerivOrder > 2) {\n    // data not valid yet\n    CHECK_FALSE(static_cast<bool>(averager(t)));\n    t += dt;\n    // fourth update\n    averager.update(t, {t}, {0.1});\n  }\n  // data should be valid now\n  CHECK(static_cast<bool>(averager(t)));\n  CHECK(averager(t).value()[0][0] == t);\n  t += dt;\n  // data should currently be invalid since there was no update at this new `t`\n  CHECK_FALSE(static_cast<bool>(averager(t)));\n  // last updated time should then be the time before this step\n  CHECK(approx(averager.last_time_updated()) == (t - dt));\n\n  // test the clear() function:\n  // update averager again, so that data is valid\n  averager.update(t, {t}, {0.1});\n  CHECK(static_cast<bool>(averager(t)));\n  // clear the averager, which should make the data no longer valid\n  averager.clear();\n  CHECK_FALSE(static_cast<bool>(averager(t)));\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      []() {\n        Averager<2> averager(0.5, false);\n\n        averager.update(0.5, {0.0}, {0.1});\n        averager.update(0.5, {0.0}, {0.1});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"at or before the last time\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Averager<2> averager(0.5, false);\n\n        averager.update(0.5, {0.0}, {0.1});\n        averager.update(0.3, {0.0}, {0.1});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"at or before the last time\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        double t = 0.0;\n        constexpr size_t deriv_order = 2;\n\n        Averager<deriv_order> averager(1.0, false);\n        averager.update(t, {{0.2, 0.3}}, {0.1});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The number of supplied timescales (1) does not match\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        double t = 0.0;\n        constexpr size_t deriv_order = 2;\n\n        Averager<deriv_order> averager(1.0, false);\n\n        averager.update(t, {0.1}, {0.1});\n        averager.update(t, {{0.2, 0.3}}, {0.1});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The number of components in the raw_q provided (2) does\"));\n\n  CHECK_THROWS_WITH(Averager<2>(0.0, true),\n                    Catch::Matchers::ContainsSubstring(\"must be positive\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Averager<2> averager(1.0, true);\n        averager.last_time_updated();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The time history has not been updated yet\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Averager<2> averager(1.0, true);\n        averager.average_time(0.0);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot return averaged values because the averager does not\"));\n}\n\ntemplate <size_t DerivOrder>\nvoid test_equality_and_serialization() {\n  INFO(\"Test equality.\");\n  Averager<DerivOrder> averager1(1.0, true);\n  Averager<DerivOrder> averager2(1.0, true);\n  Averager<DerivOrder> averager3(0.5, false);\n\n  CHECK(averager1 == averager2);\n  CHECK(averager1 != averager3);\n  CHECK_FALSE(averager1 == averager3);\n\n  CHECK(averager1.avg_timescale_frac() == 1.0);\n  CHECK(averager3.avg_timescale_frac() == 0.5);\n\n  averager2.update(0.1, {0.2}, {0.3});\n  CHECK_FALSE(averager1 == averager2);\n\n  CHECK(serialize_and_deserialize(averager1) == averager1);\n}\n\ntemplate <size_t DerivOrder>\nvoid test_create_from_options() {\n  INFO(\"Test options.\");\n  Averager<DerivOrder> averager(0.5, true);\n  auto averager2 = TestHelpers::test_creation<Averager<DerivOrder>>(\n      \"AverageTimescaleFraction: 0.5\\n\"\n      \"Average0thDeriv: True\");\n\n  CHECK(averager == averager2);\n}\n\ntemplate <size_t DerivOrder>\nvoid test_move() {\n  INFO(\"Test move.\");\n  Averager<DerivOrder> averager(0.25, false);\n  static_assert(std::is_move_constructible<Averager<DerivOrder>>::value,\n                \"Averager is not nothrow move constructible\");\n  static_assert(std::is_move_assignable<Averager<DerivOrder>>::value,\n                \"Averager is not nothrow move assignable\");\n  // update with junk data\n  CHECK(averager.is_ready(0.3));\n  averager.update(0.3, {0.1}, {0.1});\n  averager.assign_time_between_measurements(0.1);\n  CHECK_FALSE(averager.is_ready(0.35));\n  CHECK(averager.is_ready(0.5));\n  averager.assign_time_between_measurements(0.3);\n  averager.update(0.5, {0.2}, {0.1});\n  averager.update(0.7, {0.4}, {0.1});\n  averager.update(0.9, {0.6}, {0.1});\n  // get correct values for comparison\n  auto last_time = averager.last_time_updated();\n  auto avg_time = averager.average_time(0.9);\n  auto avg_q = averager.using_average_0th_deriv_of_q();\n  auto avg_values = averager(0.9).value();\n  // test move constructor\n  auto new_averager(std::move(averager));\n  // check moved values against stored values\n  CHECK(last_time == new_averager.last_time_updated());\n  CHECK(avg_time == new_averager.average_time(0.9));\n  CHECK(avg_q == new_averager.using_average_0th_deriv_of_q());\n  CHECK(avg_values[0][0] == new_averager(0.9).value()[0][0]);\n  // test move assignment\n  Averager<DerivOrder> new_averager2(0.1, true);\n  new_averager2 = std::move(new_averager);\n  // check moved values against stored values\n  CHECK(last_time == new_averager2.last_time_updated());\n  CHECK(avg_time == new_averager2.average_time(0.9));\n  CHECK(avg_q == new_averager2.using_average_0th_deriv_of_q());\n  CHECK(avg_values[0][0] == new_averager2(0.9).value()[0][0]);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Averager\", \"[ControlSystem][Unit]\") {\n  test_errors();\n  {\n    INFO(\"Deriv Order 1\");\n    test_linear<1>();\n    test_semianalytic<1>();\n    test_functionality<1>();\n    test_equality_and_serialization<1>();\n    test_create_from_options<1>();\n    test_move<1>();\n  }\n\n  {\n    INFO(\"Deriv Order 2\");\n    test_linear<2>();\n    test_semianalytic<2>();\n    test_functionality<2>();\n    test_equality_and_serialization<2>();\n    test_create_from_options<2>();\n    test_move<2>();\n  }\n\n  {\n    INFO(\"Deriv Order 3\");\n    test_linear<3>();\n    test_semianalytic<3>();\n    test_functionality<3>();\n    test_equality_and_serialization<3>();\n    test_create_from_options<3>();\n    test_move<3>();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_CleanFunctionsOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <initializer_list>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"ControlSystem/CleanFunctionsOfTime.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct Component {\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using metavariables = Metavariables;\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTime,\n                 control_system::Tags::MeasurementTimescales>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct Metavars {\n  using component_list = tmpl::list<Component<Metavars>>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.CleanFunctionsOfTime\",\n                  \"[Unit][ControlSystem]\") {\n  // ActionTesting doesn't support reductions, so this just tests the\n  // reduction action as a simple action.\n\n  domain::FunctionsOfTime::register_derived_with_charm();\n  using component = Component<Metavars>;\n\n  const std::initializer_list<std::string> names{\"A\", \"B\", \"C\"};\n\n  domain::FunctionsOfTime::PiecewisePolynomial<0> fot_template(\n      0.0, std::array{DataVector{1.0}}, 1.7);\n  fot_template.update(1.7, DataVector{1.0}, 2.9);\n  fot_template.update(2.9, DataVector{1.0}, 5.8);\n  fot_template.update(5.8, DataVector{1.0}, 7.2);\n  fot_template.update(7.2, DataVector{1.0}, 10.1);\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      init_functions_of_time{};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      init_measurement_timescales{};\n\n  for (const auto& name : names) {\n    init_functions_of_time[name] = fot_template.get_clone();\n    init_measurement_timescales[name] = fot_template.get_clone();\n  }\n\n  ActionTesting::MockRuntimeSystem<Metavars> runner{\n      {},\n      {std::move(init_functions_of_time),\n       std::move(init_measurement_timescales)}};\n  ActionTesting::emplace_nodegroup_component<component>(make_not_null(&runner));\n\n  ActionTesting::simple_action<component,\n                               control_system::CleanFunctionsOfTimeAction>(\n      make_not_null(&runner), 0_st, 5.0);\n\n  const auto& cache = ActionTesting::cache<component>(runner, 0_st);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n  const auto& measurement_timescales =\n      Parallel::get<control_system::Tags::MeasurementTimescales>(cache);\n\n  for (const auto& name : names) {\n    CHECK(functions_of_time.at(name)->time_bounds()[0] > 1.0);\n    CHECK(functions_of_time.at(name)->time_bounds()[0] <= 5.0);\n    CHECK(measurement_timescales.at(name)->time_bounds()[0] > 1.0);\n    CHECK(measurement_timescales.at(name)->time_bounds()[0] <= 5.0);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_CombinedName.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"ControlSystem/CombinedName.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Index>\nstruct Name {\n  static std::string name() { return \"Name\" + get_output(Index); }\n};\n\ntemplate <typename Label>\nusing measurement = control_system::TestHelpers::Measurement<Label>;\n\ntemplate <typename ControlSystemLabel, typename MeasurementLabel>\nusing system =\n    control_system::TestHelpers::System<2, ControlSystemLabel,\n                                        measurement<MeasurementLabel>>;\n\nstruct LabelA {};\nstruct LabelB {};\nstruct LabelC {};\nstruct LabelD {};\n\nvoid test_system_to_combined_names() {\n  using control_systems =\n      tmpl::list<system<LabelA, LabelA>, system<LabelB, LabelA>,\n                 system<LabelC, LabelA>, system<LabelD, LabelD>>;\n\n  const std::unordered_map<std::string, std::string> system_to_combined_names =\n      control_system::system_to_combined_names<control_systems>();\n  std::unordered_map<std::string, std::string>\n      expected_system_to_combined_names{};\n  const std::string long_combined_name{\"LabelALabelBLabelC\"};\n  expected_system_to_combined_names[\"LabelA\"] = long_combined_name;\n  expected_system_to_combined_names[\"LabelB\"] = long_combined_name;\n  expected_system_to_combined_names[\"LabelC\"] = long_combined_name;\n  expected_system_to_combined_names[\"LabelD\"] = \"LabelD\";\n\n  CHECK(expected_system_to_combined_names == system_to_combined_names);\n}\n\nvoid test_combined_name() {\n  using list_of_names = tmpl::list<Name<3>, Name<0>, Name<2>, Name<1>>;\n\n  const std::string& combined = control_system::combined_name<list_of_names>();\n  CHECK(combined == \"Name0Name1Name2Name3\");\n\n  const std::string& empty = control_system::combined_name<tmpl::list<>>();\n  CHECK(empty.empty());\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.CombinedName\", \"[Unit][ControlSystem]\") {\n  test_system_to_combined_names();\n  test_combined_name();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_Controller.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\ntemplate <size_t DerivOrder>\nvoid test_controller() {\n  INFO(\"Test controller\");\n  const double decrease_timescale_threshold = 1.0e-2;\n  const double increase_timescale_threshold = 1.0e-4;\n  const double increase_factor = 1.01;\n  const double decrease_factor = 0.99;\n  const double max_timescale = 10.0;\n  const double min_timescale = 1.0e-3;\n  const double initial_timescale = 1.0e-2;\n\n  TimescaleTuner<true> tst(std::vector<double>{initial_timescale},\n                           max_timescale, min_timescale,\n                           increase_timescale_threshold, increase_factor,\n                           decrease_timescale_threshold, decrease_factor);\n\n  // test following a sinusoidal target function\n  double t = 0.1;\n  const double dt = 1.0e-3;\n  const double final_time = 5.0;\n  const double freq = 3.0;\n\n  auto init_func = make_array<DerivOrder + 1, DataVector>(DataVector{1, 0.0});\n  init_func[0] = {std::sin(freq * t)};\n  init_func[1] = {freq * std::cos(freq * t)};\n  init_func[2] = {-square(freq) * std::sin(freq * t)};\n  if constexpr (DerivOrder > 2) {\n    init_func[3] = {-cube(freq) * std::cos(freq * t)};\n  }\n\n  // properly initialize the function of time to match our target function\n  std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t =\n      std::make_unique<\n          domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>>(\n          t, init_func, t + dt);\n\n  Controller<DerivOrder> control_signal{0.5};\n  const double t_offset = 0.0;\n\n  while (t < final_time) {\n    auto target_func = make_array<DerivOrder, DataVector>(DataVector{1, 0.0});\n    target_func[0] = {std::sin(freq * t)};\n    target_func[1] = {freq * std::cos(freq * t)};\n    auto lambda = target_func;\n    if constexpr (DerivOrder == 2) {\n      lambda = f_of_t->func_and_deriv(t);\n    } else {\n      target_func[2] = {-square(freq) * std::sin(freq * t)};\n      lambda = f_of_t->func_and_2_derivs(t);\n    }\n    // check that the error is within the specified tolerance, which is\n    // maintained by the TimescaleTuner adjusting the damping time\n    CHECK(fabs(target_func[0][0] - lambda[0][0]) <\n          decrease_timescale_threshold);\n\n    // compute q = target - lambda\n    // explicitly computing the derivatives of q here for testing purposes,\n    // whereas, in practice, these will be computed numerically\n    const auto q_and_derivs = target_func - lambda;\n\n    // get the control signal for updating the FunctionOfTime\n    const DataVector U = control_signal(t, tst.current_timescale(),\n                                        q_and_derivs, t_offset, t_offset);\n\n    t += dt;\n    f_of_t->update(t, {U}, t + dt);\n\n    // update the timescale\n    tst.update_timescale({{q_and_derivs[0], q_and_derivs[1]}});\n  }\n}\n\ntemplate <size_t DerivOrder>\nvoid test_timeoffsets() {\n  INFO(\"Test time offsets\");\n  const double decrease_timescale_threshold = 1.0e-2;\n  const double increase_timescale_threshold = 1.0e-4;\n  const double increase_factor = 1.01;\n  const double decrease_factor = 0.99;\n  const double max_timescale = 10.0;\n  const double min_timescale = 1.0e-3;\n  const double initial_timescale = 1.0e-2;\n\n  TimescaleTuner<true> tst(std::vector<double>{initial_timescale},\n                           max_timescale, min_timescale,\n                           increase_timescale_threshold, increase_factor,\n                           decrease_timescale_threshold, decrease_factor);\n\n  // test following a sinusoidal target function\n  double t = 0.1;\n  const double dt = 1.0e-3;\n  const double final_time = 5.0;\n  const double freq = 3.0;\n\n  // some vars for a rough averaging procedure\n  const double alpha = 0.1;\n  auto avg_qs = make_array<DerivOrder, DataVector>(DataVector{1, 0.0});\n  double avg_time = 0.0;\n\n  auto init_func = make_array<DerivOrder + 1, DataVector>(DataVector{1, 0.0});\n  init_func[0] = {std::sin(freq * t)};\n  init_func[1] = {freq * std::cos(freq * t)};\n  init_func[2] = {-square(freq) * std::sin(freq * t)};\n  if constexpr (DerivOrder > 2) {\n    init_func[3] = {-cube(freq) * std::cos(freq * t)};\n  }\n\n  // properly initialize the function of time to match our target function\n  std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t =\n      std::make_unique<\n          domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>>(\n          t, init_func, t + dt);\n\n  Controller<DerivOrder> control_signal{0.5};\n\n  while (t < final_time) {\n    auto target_func = make_array<DerivOrder, DataVector>(DataVector{1, 0.0});\n    target_func[0] = {std::sin(freq * t)};\n    target_func[1] = {freq * std::cos(freq * t)};\n    auto lambda = target_func;\n    if constexpr (DerivOrder == 2) {\n      lambda = f_of_t->func_and_deriv(t);\n    } else {\n      target_func[2] = {-square(freq) * std::sin(freq * t)};\n      lambda = f_of_t->func_and_2_derivs(t);\n    }\n    // check that the error is within the specified tolerance, which is\n    // maintained by the TimescaleTuner adjusting the damping time\n    CHECK(fabs(target_func[0][0] - lambda[0][0]) <\n          decrease_timescale_threshold);\n\n    // compute q = target - lambda\n    // explicitly computing the derivatives of q here for testing purposes,\n    // whereas, in practice, these will be computed numerically\n    const auto q_and_derivs = target_func - lambda;\n\n    // average q and its derivatives\n    if (t == 0.1) {\n      avg_time = t;\n      avg_qs = q_and_derivs;\n    } else {\n      avg_time = alpha * t + (1.0 - alpha) * avg_time;\n      avg_qs[0] = alpha * q_and_derivs[0] + (1.0 - alpha) * avg_qs[0];\n      avg_qs[1] = alpha * q_and_derivs[1] + (1.0 - alpha) * avg_qs[1];\n      if constexpr (DerivOrder > 2) {\n        avg_qs[2] = alpha * q_and_derivs[2] + (1.0 - alpha) * avg_qs[2];\n      }\n    }\n\n    // get the time offset due to averaging\n    const double t_offset = t - avg_time;\n\n    // get the control signal for updating the FunctionOfTime\n    const DataVector U =\n        control_signal(t, tst.current_timescale(), avg_qs, t_offset, t_offset);\n\n    t += dt;\n    f_of_t->update(t, {U}, t + dt);\n\n    // update the timescale\n    tst.update_timescale({{avg_qs[0], avg_qs[1]}});\n  }\n}\n\ntemplate <size_t DerivOrder>\nvoid test_timeoffsets_noaverageq() {\n  INFO(\"Test time offsets not averaging Q\");\n  const double decrease_timescale_threshold = 1.0e-2;\n  const double increase_timescale_threshold = 1.0e-4;\n  const double increase_factor = 1.01;\n  const double decrease_factor = 0.99;\n  const double max_timescale = 10.0;\n  const double min_timescale = 1.0e-3;\n  const double initial_timescale = 1.0e-2;\n\n  TimescaleTuner<true> tst(std::vector<double>{initial_timescale},\n                           max_timescale, min_timescale,\n                           increase_timescale_threshold, increase_factor,\n                           decrease_timescale_threshold, decrease_factor);\n\n  // test following a sinusoidal target function\n  double t = 0.1;\n  const double dt = 1.0e-3;\n  const double final_time = 5.0;\n  const double freq = 3.0;\n\n  // some vars for a rough averaging procedure\n  const double alpha = 0.1;\n  auto avg_qs = make_array<DerivOrder, DataVector>(DataVector{1, 0.0});\n  double avg_time = 0.0;\n\n  auto init_func = make_array<DerivOrder + 1, DataVector>(DataVector{1, 0.0});\n  init_func[0] = {std::sin(freq * t)};\n  init_func[1] = {freq * std::cos(freq * t)};\n  init_func[2] = {-square(freq) * std::sin(freq * t)};\n  if constexpr (DerivOrder > 2) {\n    init_func[3] = {-cube(freq) * std::cos(freq * t)};\n  }\n\n  // properly initialize the function of time to match our target function\n  std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t =\n      std::make_unique<\n          domain::FunctionsOfTime::PiecewisePolynomial<DerivOrder>>(\n          t, init_func, t + dt);\n\n  Controller<DerivOrder> control_signal{0.5};\n\n  while (t < final_time) {\n    auto target_func = make_array<DerivOrder, DataVector>(DataVector{1, 0.0});\n    target_func[0] = {std::sin(freq * t)};\n    target_func[1] = {freq * std::cos(freq * t)};\n    auto lambda = target_func;\n    if constexpr (DerivOrder == 2) {\n      lambda = f_of_t->func_and_deriv(t);\n    } else {\n      target_func[2] = {-square(freq) * std::sin(freq * t)};\n      lambda = f_of_t->func_and_2_derivs(t);\n    }\n    // check that the error is within the specified tolerance, which is\n    // maintained by the TimescaleTuner adjusting the damping time\n    CHECK(fabs(target_func[0][0] - lambda[0][0]) <\n          decrease_timescale_threshold);\n\n    // compute q = target - lambda\n    // explicitly computing the derivatives of q here for testing purposes,\n    // whereas, in practice, these will be computed numerically\n    const auto q_and_derivs = target_func - lambda;\n\n    // average the derivatives of q (do not average q)\n    if (t == 0.1) {\n      avg_time = t;\n      avg_qs = q_and_derivs;\n    } else {\n      avg_time = alpha * t + (1.0 - alpha) * avg_time;\n      avg_qs[0] = q_and_derivs[0];\n      avg_qs[1] = alpha * q_and_derivs[1] + (1.0 - alpha) * avg_qs[1];\n      if constexpr (DerivOrder > 2) {\n        avg_qs[2] = alpha * q_and_derivs[2] + (1.0 - alpha) * avg_qs[2];\n      }\n    }\n\n    // since q is not averaged, there is no time offset\n    const double q_t_offset = 0.0;\n    // get the derivative time offset due to averaging\n    const double t_offset = t - avg_time;\n\n    // get the control signal for updating the FunctionOfTime\n    const DataVector U = control_signal(t, tst.current_timescale(), avg_qs,\n                                        q_t_offset, t_offset);\n\n    t += dt;\n    f_of_t->update(t, {U}, t + dt);\n\n    // update the timescale\n    tst.update_timescale({{avg_qs[0], avg_qs[1]}});\n  }\n}\n\ntemplate <size_t DerivOrder>\nvoid test_equality_and_serialization() {\n  INFO(\"Test equality and serialization\");\n  Controller<DerivOrder> controller1{0.5};\n  Controller<DerivOrder> controller2{1.0};\n  Controller<DerivOrder> controller3{0.5};\n  controller3.assign_time_between_updates(2.0);\n\n  CHECK(controller1 != controller2);\n  CHECK_FALSE(controller1 == controller2);\n  CHECK(controller1 != controller3);\n\n  controller1.assign_time_between_updates(2.0);\n\n  CHECK(controller1 == controller3);\n  CHECK(controller1.get_update_fraction() == 0.5);\n\n  Controller<DerivOrder> controller1_serialized =\n      serialize_and_deserialize(controller1);\n\n  CHECK(controller1 == controller1_serialized);\n}\n\ntemplate <size_t DerivOrder>\nvoid test_is_ready() {\n  INFO(\"Test is ready\");\n  double time = 0.1;\n  double min_timescale = 0.2;\n  const double curr_expr_time = 0.2;\n  auto avg_qs = make_array<DerivOrder, DataVector>(DataVector{1, 0.0});\n\n  Controller<DerivOrder> controller{0.5};\n  controller.set_initial_update_time(0.1);\n  controller.assign_time_between_updates(min_timescale);\n\n  CHECK_FALSE(controller.is_ready(time));\n  time = curr_expr_time;\n  CHECK(controller.is_ready(time));\n\n  const double new_expr_time = controller.next_expiration_time(curr_expr_time);\n\n  // Don't care what the control signal is, just that last_updated_time_ is\n  // updated;\n  const DataVector control_signal =\n      controller(time, {min_timescale}, avg_qs, 0.0, 0.0);\n\n  CHECK(controller.is_ready(new_expr_time));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Controller\", \"[ControlSystem][Unit]\") {\n  {\n    INFO(\"DerivOrder 2\");\n    test_controller<2>();\n    test_timeoffsets<2>();\n    test_timeoffsets_noaverageq<2>();\n    test_equality_and_serialization<2>();\n    test_is_ready<2>();\n  }\n  {\n    INFO(\"DerivOrder 3\");\n    test_controller<3>();\n    test_timeoffsets<3>();\n    test_timeoffsets_noaverageq<3>();\n    test_equality_and_serialization<3>();\n    test_is_ready<3>();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_EventTriggerMetafunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"ControlSystem/Trigger.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct LabelA;\nstruct LabelB;\nstruct LabelC;\n\nusing SystemA0 = control_system::TestHelpers::System<\n    2, LabelA, control_system::TestHelpers::Measurement<LabelA>>;\nusing SystemA1 = control_system::TestHelpers::System<\n    2, LabelB, control_system::TestHelpers::Measurement<LabelA>>;\nusing SystemB0 = control_system::TestHelpers::System<\n    2, LabelC, control_system::TestHelpers::Measurement<LabelB>>;\n\ntemplate <template <typename> typename Metafunction,\n          template <typename> typename ResultObject>\nconstexpr bool check() {\n  using result = Metafunction<tmpl::list<SystemA0, SystemB0, SystemA1>>;\n\n  using expectedA_option0 = ResultObject<tmpl::list<SystemA0, SystemA1>>;\n  using expectedA_option1 = ResultObject<tmpl::list<SystemA1, SystemA0>>;\n  using expectedB = ResultObject<tmpl::list<SystemB0>>;\n\n  // The orders of the lists is unspecified.\n  static_assert(tmpl::size<result>::value == 2);\n  static_assert(tmpl::list_contains_v<result, expectedB>);\n  static_assert(tmpl::list_contains_v<result, expectedA_option0> or\n                tmpl::list_contains_v<result, expectedA_option1>);\n  return true;\n}\n\nstatic_assert(\n    check<control_system::control_system_triggers, control_system::Trigger>());\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_ExpirationTimes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"ControlSystem/CalculateMeasurementTimescales.hpp\"\n#include \"ControlSystem/ExpirationTimes.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Index>\nstruct Label {};\n\ntemplate <size_t Index>\nstruct FakeControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static constexpr size_t deriv_order = 2;\n  static std::string name() { return \"Controlled\"s + get_output(Index); }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return get_output(i);\n  }\n  using measurement =\n      control_system::TestHelpers::Measurement<Label<(Index < 3 ? 0 : 1)>>;\n  using simple_tags = tmpl::list<>;\n  using control_error = control_system::TestHelpers::ControlError;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\ntemplate <size_t Index>\nusing OptionHolder = control_system::OptionHolder<FakeControlSystem<Index>>;\n\nvoid check_expiration_times(\n    const std::unordered_map<std::string, double>& initial_expiration_times,\n    const std::unordered_map<std::string, double>&\n        expected_initial_expiration_times) {\n  CHECK(expected_initial_expiration_times.size() ==\n        initial_expiration_times.size());\n  for (auto& [expected_name, expected_expiration_time] :\n       expected_initial_expiration_times) {\n    CHECK(initial_expiration_times.count(expected_name) == 1);\n    CHECK(initial_expiration_times.at(expected_name) ==\n          expected_expiration_time);\n  }\n}\n\nvoid test_expiration_time_construction() {\n  const double initial_time = 0.9;\n  constexpr int measurements_per_update = 4;\n\n  const double timescale = 2.0;\n  const TimescaleTuner<true> tuner1(std::vector<double>{timescale}, 10.0,\n                                    1.0e-3, 1.0e-4, 1.01, 1.0e-2, 0.99);\n  TimescaleTuner<true> tuner2(0.1, 10.0, 1.0e-3, 1.0e-4, 1.01, 1.0e-2, 0.99);\n  tuner2.resize_timescales(2);\n  const Averager<1> averager(0.25, true);\n  const double update_fraction = 0.3;\n  const Controller<2> controller(update_fraction);\n  const control_system::TestHelpers::ControlError control_error{};\n\n  const std::optional<OptionHolder<1>> option_holder1 =\n      OptionHolder<1>{averager, controller, tuner1, control_error};\n  const std::optional<OptionHolder<2>> option_holder2 =\n      OptionHolder<2>{averager, controller, tuner2, control_error};\n  const std::optional<OptionHolder<3>> option_holder3 = std::nullopt;\n\n  using FakeCreator = TestHelpers::control_system::FakeCreator;\n\n  const std::unique_ptr<DomainCreator<3>> creator1 =\n      std::make_unique<FakeCreator>(std::unordered_map<std::string, size_t>{});\n  const std::unique_ptr<DomainCreator<3>> creator2 =\n      std::make_unique<FakeCreator>(std::unordered_map<std::string, size_t>{\n          {FakeControlSystem<1>::name(), 1}});\n  const std::unique_ptr<DomainCreator<3>> creator3 =\n      std::make_unique<FakeCreator>(std::unordered_map<std::string, size_t>{\n          {FakeControlSystem<1>::name(), 1},\n          {FakeControlSystem<2>::name(), 2},\n          {FakeControlSystem<3>::name(), 1}});\n\n  for (const bool delay_update : {true, false}) {\n    CAPTURE(delay_update);\n    {\n      INFO(\"No control systems\");\n      const auto initial_expiration_times =\n          control_system::initial_expiration_times(\n              initial_time, measurements_per_update, delay_update, creator1);\n\n      const std::unordered_map<std::string, double>\n          expected_initial_expiration_times{};\n\n      check_expiration_times(initial_expiration_times,\n                             expected_initial_expiration_times);\n    }\n    {\n      INFO(\"One control system\");\n      const auto initial_expiration_times =\n          control_system::initial_expiration_times(\n              initial_time, measurements_per_update, delay_update, creator2,\n              option_holder1);\n\n      const std::unordered_map<std::string, double>\n          expected_initial_expiration_times{\n              {FakeControlSystem<1>::name(),\n               // This is ok to use here because we test it below\n               control_system::function_of_time_initial_expiration_time(\n                   initial_time,\n                   control_system::calculate_measurement_timescales(\n                       controller, tuner1, measurements_per_update),\n                   measurements_per_update, delay_update)}};\n\n      check_expiration_times(initial_expiration_times,\n                             expected_initial_expiration_times);\n    }\n    {\n      INFO(\"Three control system\");\n      const auto initial_expiration_times =\n          control_system::initial_expiration_times(\n              initial_time, measurements_per_update, delay_update, creator3,\n              option_holder1, option_holder2, option_holder3);\n\n      const double min_measurement_timescale =\n          std::min(min(control_system::calculate_measurement_timescales(\n                       controller, tuner1, measurements_per_update)),\n                   min(control_system::calculate_measurement_timescales(\n                       controller, tuner2, measurements_per_update)));\n      const double min_expiration_time =\n          control_system::function_of_time_initial_expiration_time(\n              initial_time, DataVector{min_measurement_timescale},\n              measurements_per_update, delay_update);\n\n      const std::unordered_map<std::string, double>\n          expected_initial_expiration_times{\n              {FakeControlSystem<1>::name(), min_expiration_time},\n              {FakeControlSystem<2>::name(), min_expiration_time},\n              {FakeControlSystem<3>::name(),\n               std::numeric_limits<double>::infinity()}};\n\n      check_expiration_times(initial_expiration_times,\n                             expected_initial_expiration_times);\n    }\n  }\n}\n\nvoid test_fot_measurement_expr_time() {\n  const DataVector old_measurement_timescales{2.2, 3.3, 1.1};\n  const DataVector new_measurement_timescales{1.2, 2.3, 3.4};\n\n  const double time = 0.6;\n  const int measurements_per_update = 3;\n\n  const double fot_expr_time_nondelayed =\n      control_system::function_of_time_expiration_time(\n          time, old_measurement_timescales, new_measurement_timescales,\n          measurements_per_update, false);\n  const double fot_expr_time_delayed =\n      control_system::function_of_time_expiration_time(\n          time, old_measurement_timescales, new_measurement_timescales,\n          measurements_per_update, true);\n  CHECK(fot_expr_time_delayed ==\n        fot_expr_time_nondelayed + min(old_measurement_timescales));\n  const double expected_fot_expr_time = time + min(new_measurement_timescales) +\n                                        min(new_measurement_timescales) +\n                                        min(new_measurement_timescales);\n\n  CHECK(fot_expr_time_nondelayed == expected_fot_expr_time);\n\n  const double fot_initial_expr_time_nondelayed =\n      control_system::function_of_time_initial_expiration_time(\n          time, new_measurement_timescales, measurements_per_update, false);\n  const double fot_initial_expr_time_delayed =\n      control_system::function_of_time_initial_expiration_time(\n          time, new_measurement_timescales, measurements_per_update, true);\n  CHECK(fot_initial_expr_time_delayed ==\n        fot_initial_expr_time_nondelayed + min(new_measurement_timescales));\n  CHECK(fot_initial_expr_time_delayed == expected_fot_expr_time);\n\n  const double measurement_expr_time_delayed =\n      control_system::measurement_expiration_time(\n          time, old_measurement_timescales, new_measurement_timescales,\n          measurements_per_update, true);\n  const double measurement_expr_time_nondelayed =\n      control_system::measurement_expiration_time(\n          time, old_measurement_timescales, new_measurement_timescales,\n          measurements_per_update, false);\n  CHECK(measurement_expr_time_delayed ==\n        approx(fot_expr_time_delayed - 0.5 * min(new_measurement_timescales)));\n  CHECK(\n      measurement_expr_time_nondelayed ==\n      approx(fot_expr_time_nondelayed - 0.5 * min(new_measurement_timescales)));\n\n  const double measurement_initial_expr_time_delayed =\n      control_system::measurement_initial_expiration_time(\n          time, new_measurement_timescales, measurements_per_update, true);\n  const double measurement_initial_expr_time_nondelayed =\n      control_system::measurement_initial_expiration_time(\n          time, new_measurement_timescales, measurements_per_update, false);\n  CHECK(measurement_initial_expr_time_delayed ==\n        approx(fot_initial_expr_time_delayed -\n               0.5 * min(new_measurement_timescales)));\n  CHECK(measurement_initial_expr_time_nondelayed ==\n        approx(fot_initial_expr_time_nondelayed -\n               0.5 * min(new_measurement_timescales)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.ExpirationTimes\",\n                  \"[ControlSystem][Unit]\") {\n  test_expiration_time_construction();\n  test_fot_measurement_expr_time();\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_FutureMeasurements.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <optional>\n\n#include \"ControlSystem/FutureMeasurements.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.FutureMeasurements\",\n                  \"[Unit][ControlSystem]\") {\n  control_system::FutureMeasurements measurements(5, 1.0);\n  REQUIRE(measurements.next_measurement() == std::optional(1.0));\n  REQUIRE(not measurements.next_update().has_value());\n\n  domain::FunctionsOfTime::PiecewisePolynomial<0> measurement_timescales(\n      1.0, {{{0.5}}}, 1.75);\n  measurements.update(measurement_timescales);\n  REQUIRE(measurements.next_measurement() == std::optional(1.0));\n  REQUIRE(not measurements.next_update().has_value());\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(1.5));\n  REQUIRE(not measurements.next_update().has_value());\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(2.0));\n  REQUIRE(not measurements.next_update().has_value());\n\n  measurements.pop_front();\n  REQUIRE(not measurements.next_measurement().has_value());\n  REQUIRE(not measurements.next_update().has_value());\n\n  measurement_timescales.update(1.75, {1.0}, 5.0);\n  measurements.update(measurement_timescales);\n  REQUIRE(measurements.next_measurement() == std::optional(3.0));\n  REQUIRE(measurements.next_update() == std::optional(4.0));\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(4.0));\n  REQUIRE(measurements.next_update() == std::optional(4.0));\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(5.0));\n  REQUIRE(not measurements.next_update().has_value());\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(6.0));\n  REQUIRE(not measurements.next_update().has_value());\n\n  measurement_timescales.update(5.0, {0.5}, 10.0);\n  measurements.update(measurement_timescales);\n  REQUIRE(measurements.next_measurement() == std::optional(6.0));\n  REQUIRE(measurements.next_update() == std::optional(7.5));\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(6.5));\n  REQUIRE(measurements.next_update() == std::optional(7.5));\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(7.0));\n  REQUIRE(measurements.next_update() == std::optional(7.5));\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(7.5));\n  REQUIRE(measurements.next_update() == std::optional(7.5));\n\n  measurements.pop_front();\n  REQUIRE(measurements.next_measurement() == std::optional(8.0));\n  REQUIRE(measurements.next_update() == std::optional(10.0));\n\n  const auto measurements_copy = serialize_and_deserialize(measurements);\n  REQUIRE(measurements_copy.next_measurement() == std::optional(8.0));\n  REQUIRE(measurements_copy.next_update() == std::optional(10.0));\n  REQUIRE(measurements == measurements_copy);\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_IsSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"ControlSystem/IsSize.hpp\"\n#include \"ControlSystem/Measurements/BNSCenterOfMass.hpp\"\n#include \"ControlSystem/Systems/Expansion.hpp\"\n#include \"ControlSystem/Systems/Size.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n\nnamespace control_system {\nnamespace {\nusing yes_size = control_system::Systems::Size<domain::ObjectLabel::A, 3>;\nusing both_horizons = control_system::measurements::BothHorizons;\nusing no_size = control_system::Systems::Expansion<2, both_horizons>;\n\nstatic_assert(size::is_size_v<yes_size>);\nstatic_assert(not size::is_size_v<no_size>);\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_Measurements.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <typeinfo>\n\n#include \"ControlSystem/Measurements/BNSCenterOfMass.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Measurements/CharSpeed.hpp\"\n#include \"ControlSystem/Measurements/SingleHorizon.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/Examples.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nusing example_list =\n    tmpl::list<control_system::TestHelpers::ExampleControlSystem>;\n\n// BothHorizons\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::measurements::BothHorizons::\n                                 FindHorizon<::domain::ObjectLabel::A>,\n                             control_system::protocols::Submeasurement>);\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::measurements::BothHorizons,\n                             control_system::protocols::Measurement>);\nstatic_assert(tt::assert_conforms_to_v<\n              control_system::measurements::BothHorizons::FindHorizon<\n                  ::domain::ObjectLabel::A>::horizon_metavars<example_list>,\n              ah::protocols::HorizonMetavars>);\nstatic_assert(\n    not control_system::measurements::BothHorizons::FindHorizon<\n        ::domain::ObjectLabel::A>::event<example_list>::factory_creatable);\n\n// SingleHorizon\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::measurements::SingleHorizon<\n                                 ::domain::ObjectLabel::B>::Submeasurement,\n                             control_system::protocols::Submeasurement>);\nstatic_assert(\n    tt::assert_conforms_to_v<\n        control_system::measurements::SingleHorizon<::domain::ObjectLabel::B>,\n        control_system::protocols::Measurement>);\nstatic_assert(\n    tt::assert_conforms_to_v<\n        control_system::measurements::SingleHorizon<::domain::ObjectLabel::B>::\n            Submeasurement::horizon_metavars<example_list>,\n        ah::protocols::HorizonMetavars>);\nstatic_assert(\n    not control_system::measurements::SingleHorizon<::domain::ObjectLabel::B>::\n        Submeasurement::event<example_list>::factory_creatable);\n\n// BothNSCenters\nstatic_assert(tt::assert_conforms_to_v<\n              control_system::measurements::BothNSCenters::FindTwoCenters,\n              control_system::protocols::Submeasurement>);\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::measurements::BothNSCenters,\n                             control_system::protocols::Measurement>);\nstatic_assert(\n    not control_system::measurements::BothNSCenters::FindTwoCenters::event<\n        example_list>::factory_creatable);\n\n// CharSpeed\nstatic_assert(tt::assert_conforms_to_v<\n              control_system::measurements::CharSpeed<domain::ObjectLabel::A>,\n              control_system::protocols::Measurement>);\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::measurements::CharSpeed<\n                                 domain::ObjectLabel::A>::Excision,\n                             control_system::protocols::Submeasurement>);\nstatic_assert(\n    tt::assert_conforms_to_v<control_system::measurements::CharSpeed<\n                                 domain::ObjectLabel::A>::Horizon,\n                             control_system::protocols::Submeasurement>);\nstatic_assert(tt::assert_conforms_to_v<\n              control_system::measurements::CharSpeed<domain::ObjectLabel::A>::\n                  Horizon::horizon_metavars<example_list>,\n              ah::protocols::HorizonMetavars>);\nstatic_assert(\n    not control_system::measurements::CharSpeed<domain::ObjectLabel::A>::\n        Excision::event<example_list>::factory_creatable);\nstatic_assert(\n    not control_system::measurements::CharSpeed<domain::ObjectLabel::A>::\n        Horizon::event<example_list>::factory_creatable);\n\nnamespace {\nvoid test_serialization() {\n  using EventPtr = std::unique_ptr<::Event>;\n  using non_factory_creatable_event =\n      control_system::measurements::BothHorizons::FindHorizon<\n          ::domain::ObjectLabel::A>::event<example_list>;\n  using factory_creatable_event =\n      typename non_factory_creatable_event::factory_creatable_class;\n\n  register_classes_with_charm<non_factory_creatable_event,\n                              factory_creatable_event>();\n\n  const EventPtr non_factory_event =\n      std::make_unique<non_factory_creatable_event>();\n  const EventPtr factory_event = std::make_unique<factory_creatable_event>();\n\n  const EventPtr serialized_non_factory_event =\n      serialize_and_deserialize(non_factory_event);\n  const EventPtr serialized_factory_event =\n      serialize_and_deserialize(factory_event);\n\n#ifdef __clang__\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wpotentially-evaluated-expression\"\n#endif  // __clang__\n  CHECK(typeid(*serialized_non_factory_event.get()) ==\n        typeid(*non_factory_event.get()));\n  CHECK(typeid(*serialized_factory_event.get()) ==\n        typeid(*factory_event.get()));\n#ifdef __clang__\n#pragma clang diagnostic pop\n#endif  // __clang__\n}\n\n// Check that the center of mass is at the expected location for this test.\nvoid check_centers(const std::array<double, 3>& center_a,\n                   const std::array<double, 3>& center_b,\n                   const bool grid_centers = true) {\n  const std::array<double, 3> expected_grid_center_a{1.0, 4.5 / 8.0, 3.0 / 8.0};\n  const std::array<double, 3> expected_grid_center_b{-1.0, 1.0, 0.0};\n\n  if (grid_centers) {\n    CHECK(center_a == expected_grid_center_a);\n    CHECK(center_b == expected_grid_center_b);\n  } else {\n    const std::array<double, 3> expected_inertial_center_a{\n        cos(0.1) * expected_grid_center_a[0] +\n            -sin(0.1) * expected_grid_center_a[1],\n        sin(0.1) * expected_grid_center_a[0] +\n            cos(0.1) * expected_grid_center_a[1],\n        expected_grid_center_a[2]};\n    const std::array<double, 3> expected_inertial_center_b{\n        cos(0.1) * expected_grid_center_b[0] +\n            -sin(0.1) * expected_grid_center_b[1],\n        sin(0.1) * expected_grid_center_b[0] +\n            cos(0.1) * expected_grid_center_b[1],\n        expected_grid_center_b[2]};\n\n    CHECK_ITERABLE_APPROX(center_a, expected_inertial_center_a);\n    CHECK_ITERABLE_APPROX(center_b, expected_inertial_center_b);\n  }\n}\n\nstruct MockControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return \"MockControlSystem\"; }\n  static std::optional<std::string> component_name(\n      const size_t /*i*/, const size_t num_components) {\n    ASSERT(num_components == 1,\n           \"This control system expected 1 component but there are \"\n               << num_components << \" instead.\");\n    return \"Phi\";\n  }\n  using measurement = control_system::measurements::BothNSCenters;\n  using simple_tags = tmpl::list<\n      control_system::TestHelpers::ExampleControlSystem::MeasurementQueue>;\n  static constexpr size_t deriv_order = 2;\n  using control_error = control_system::TestHelpers::ExampleControlError;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags =\n        tmpl::list<control_system::measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::A, Frame::Grid>,\n                   control_system::measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::B, Frame::Grid>,\n                   control_system::measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::A, Frame::Inertial>,\n                   control_system::measurements::Tags::NeutronStarCenter<\n                       ::domain::ObjectLabel::B, Frame::Inertial>>;\n    using submeasurement =\n        control_system::measurements::BothNSCenters::FindTwoCenters;\n\n    template <typename Metavariables>\n    static void apply(submeasurement /*meta*/,\n                      const std::array<double, 3> grid_center_a,\n                      const std::array<double, 3> grid_center_b,\n                      const std::array<double, 3> inertial_center_a,\n                      const std::array<double, 3> inertial_center_b,\n                      Parallel::GlobalCache<Metavariables>& /*cache*/,\n                      const LinkedMessageId<double>& /*measurement_id*/) {\n      check_centers(grid_center_a, grid_center_b, true);\n      check_centers(inertial_center_a, inertial_center_b, false);\n      // Avoid unused variable warning for deriv_order, which is required\n      // as part of the control_system protocol.\n      CHECK(2 == deriv_order);\n    }\n  };\n};\n\ntemplate <typename Metavariables>\nstruct MockControlSystemComponent {\n  using component_being_mocked =\n      ControlComponent<Metavariables, MockControlSystem>;\n  using const_global_cache_tags =\n      tmpl::list<control_system::Tags::Verbosity, domain::Tags::Domain<3>>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using simple_tags_from_options = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\nstruct MockWriteReductionDataRow {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& file_legend,\n                    const std::tuple<std::vector<double>>& data_row) {\n    CHECK((subfile_name == \"/ControlSystems/BnsGridCenters\" or\n           subfile_name == \"/ControlSystems/BnsInertialCenters\"));\n    CHECK(file_legend ==\n          std::vector<std::string>{\"Time\", \"Center_A_x\", \"Center_A_y\",\n                                   \"Center_A_z\", \"Center_B_x\", \"Center_B_y\",\n                                   \"Center_B_z\", \"Center_System_x\",\n                                   \"Center_System_y\", \"Center_System_z\"});\n\n    const std::vector<double>& data = get<0>(data_row);\n\n    CHECK(data[0] == 1.0);\n    CHECK(data.size() == file_legend.size());\n    check_centers({data[1], data[2], data[3]}, {data[4], data[5], data[6]},\n                  subfile_name == \"/ControlSystems/BnsGridCenters\");\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using const_global_cache_tags =\n      tmpl::list<control_system::Tags::WriteDataToDisk>;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<>>>>>;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n\n  using replace_these_threaded_actions =\n      tmpl::list<observers::ThreadedActions::WriteReductionDataRow>;\n  using with_these_threaded_actions = tmpl::list<MockWriteReductionDataRow>;\n};\n\nstruct Metavariables {\n  using observed_reduction_data_tags = tmpl::list<>;\n  void pup(PUP::er& /*p*/) {}\n  using component_list = tmpl::list<MockObserverWriter<Metavariables>,\n                                    MockControlSystemComponent<Metavariables>>;\n};\n\n}  // namespace\n\n// This test tests the calculation of the center of mass of a star\n// in the FindTwoCenters submeasurement\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.FindTwoCenters\",\n                  \"[ControlSystem][Unit]\") {\n  test_serialization();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  domain::creators::register_derived_with_charm();\n\n  // Part 1 of the test: calculation of the relevant integrals\n  // within a (mock) element.\n  const Mesh<3> mesh(2, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered);\n  const Scalar<DataVector> tilde_d{\n      DataVector{0.0, 0.5, 1.0, 1.0, 0.5, 0.0, 1.0, 1.0}};\n  const Scalar<DataVector> inv_det_jacobian{\n      DataVector{0.2, 0.2, 0.4, 0.4, 0.5, 0.5, 1.0, 1.0}};\n  const DataVector x_coord{-1.0, 1.0, -1.0, 1.0, 1.0, 2.0, 0.0, 2.0};\n  const DataVector y_coord{0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0};\n  const DataVector z_coord{0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0};\n  const tnsr::I<DataVector, 3, Frame::Grid> x_grid{{x_coord, y_coord, z_coord}};\n\n  // mass_a and mass_b: Integral of tilde_d/inv_det_jacobian for x>=0 and x<0\n  // first_moment_a, first_moment_b : Same as masses, but multiplying the\n  // integrand by x_grid\n  double mass_a = 0.;\n  double mass_b = 0.;\n  std::array<double, 3> first_moment_a = {0., 0., 0.};\n  std::array<double, 3> first_moment_b = {0., 0., 0.};\n  control_system::measurements::center_of_mass_integral_on_element(\n      &mass_a, &mass_b, &first_moment_a, &first_moment_b, mesh,\n      inv_det_jacobian, tilde_d, x_grid);\n  // Comparison with expected answer\n  CHECK(mass_a == 8.0);\n  CHECK(mass_b == 2.5);\n  CHECK(first_moment_a == std::array<double, 3>{8.0, 4.5, 3.0});\n  CHECK(first_moment_b == std::array<double, 3>{-2.5, 2.5, 0.0});\n\n  // Use BNS domain with rotation for realistic block structure, but simple time\n  // dependent maps for testing\n  const domain::creators::bco::TimeDependentMapOptions<false> time_dep_opts{\n      0.0,\n      std::nullopt,\n      domain::creators::time_dependent_options::RotationMapOptions<false>{\n          std::array{0.0, 0.0, 0.1}},\n      std::nullopt,\n      std::nullopt,\n      std::nullopt,\n      std::nullopt,\n      std::nullopt};\n  const domain::creators::BinaryCompactObject<false> binary_compact_object{\n      domain::creators::BinaryCompactObject<false>::CartesianCubeAtXCoord{20.0},\n      domain::creators::BinaryCompactObject<false>::CartesianCubeAtXCoord{\n          -20.0},\n      {0.0, 0.0},\n      100.0,\n      500.0,\n      1.0,\n      {0_st},\n      {4_st},\n      true,\n      domain::CoordinateMaps::Distribution::Projective,\n      std::vector<double>{},\n      domain::CoordinateMaps::Distribution::Linear,\n      90.0,\n      time_dep_opts};\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\n  using control_system_component = MockControlSystemComponent<Metavariables>;\n  using obs_writer = MockObserverWriter<Metavariables>;\n\n  MockRuntimeSystem runner{\n      {true, ::Verbosity::Silent, binary_compact_object.create_domain()},\n      {binary_compact_object.functions_of_time()}};\n  ActionTesting::emplace_singleton_component<control_system_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0});\n  ActionTesting::emplace_nodegroup_component_and_initialize<obs_writer>(\n      make_not_null(&runner), {});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  auto& cache = ActionTesting::cache<control_system_component>(runner, 0);\n\n  LinkedMessageId<double> measurement_id{1.0, 0.0};\n  const ElementId<3> unused_element_id{};\n\n  using ControlSystems = tmpl::list<MockControlSystem>;\n  auto box = db::create<db::AddSimpleTags<Tags::Time>>(1.0);\n\n  // Test post-reduction action\n  control_system::measurements::PostReductionSendBNSStarCentersToControlSystem<\n      ControlSystems>::\n      template apply<control_system_component>(box, cache, unused_element_id,\n                                               measurement_id, mass_a, mass_b,\n                                               first_moment_a, first_moment_b);\n\n  // One for grid centers, one for inertial centers\n  CHECK(ActionTesting::number_of_queued_threaded_actions<obs_writer>(runner,\n                                                                     0) == 2);\n  for (size_t i = 0; i < 2; i++) {\n    ActionTesting::invoke_queued_threaded_action<obs_writer>(\n        make_not_null(&runner), 0);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_Metafunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <type_traits>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"Helpers/ControlSystem/Examples.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::metafunctions {\nnamespace {\nstruct LabelA;\nstruct LabelB;\nstruct LabelC;\nstruct Metavariables;\n\ntemplate <typename Label, typename ControlSystems>\nstruct SomeEvent {};\n\nnamespace Measurements {\nusing MeasurementA = control_system::TestHelpers::Measurement<LabelA>;\nusing MeasurementB = control_system::TestHelpers::Measurement<LabelB>;\nusing SystemA0 = control_system::TestHelpers::System<2, LabelA, MeasurementA>;\nusing SystemA1 = control_system::TestHelpers::System<2, LabelB, MeasurementA>;\nusing SystemB0 = control_system::TestHelpers::System<2, LabelC, MeasurementB>;\nusing ComponentA = ControlComponent<Metavariables, SystemA0>;\nusing ComponentB = ControlComponent<Metavariables, SystemB0>;\n\nusing test_systems = tmpl::list<SystemA0, SystemB0, SystemA1>;\nusing fewer_test_systems = tmpl::list<SystemA0, SystemB0>;\n\nstatic_assert(std::is_same_v<measurement<SystemA0>::type, MeasurementA>);\n\nstatic_assert(std::is_same_v<measurements_t<test_systems>,\n                             measurements<test_systems>::type>);\n\n// Order is unspecified\nstatic_assert(std::is_same_v<measurements_t<test_systems>,\n                             tmpl::list<MeasurementA, MeasurementB>> or\n              std::is_same_v<measurements_t<test_systems>,\n                             tmpl::list<MeasurementB, MeasurementA>>);\n\nstatic_assert(\n    std::is_same_v<\n        control_systems_with_measurement_t<test_systems, MeasurementA>,\n        control_systems_with_measurement<test_systems, MeasurementA>::type>);\nstatic_assert(\n    std::is_same_v<\n        control_systems_with_measurement_t<test_systems, MeasurementB>,\n        control_systems_with_measurement<test_systems, MeasurementB>::type>);\n\n// Order is unspecified\nstatic_assert(\n    std::is_same_v<\n        control_systems_with_measurement_t<test_systems, MeasurementA>,\n        tmpl::list<SystemA0, SystemA1>> or\n    std::is_same_v<\n        control_systems_with_measurement_t<test_systems, MeasurementA>,\n        tmpl::list<SystemA1, SystemA0>>);\nstatic_assert(std::is_same_v<\n              control_systems_with_measurement_t<test_systems, MeasurementB>,\n              tmpl::list<SystemB0>>);\n\nstatic_assert(\n    std::is_same_v<\n        control_system::control_components<Metavariables, fewer_test_systems>,\n        tmpl::list<ControlComponent<Metavariables, SystemA0>,\n                   ControlComponent<Metavariables, SystemB0>>> or\n    std::is_same_v<\n        control_system::control_components<Metavariables, fewer_test_systems>,\n        tmpl::list<ControlComponent<Metavariables, SystemB0>,\n                   ControlComponent<Metavariables, SystemA0>>>);\n}  // namespace Measurements\n\nnamespace Components {\ntemplate <typename Metavars, typename ControlSystem>\nstruct MockControlComponent {\n  using component_being_mocked = ControlComponent<Metavars, ControlSystem>;\n};\n\ntemplate <typename Metavars>\nusing ComponentA0 = ControlComponent<Metavars, Measurements::SystemA0>;\ntemplate <typename Metavars>\nusing ComponentB0 = ControlComponent<Metavars, Measurements::SystemB0>;\ntemplate <typename Metavars>\nusing ComponentA1 = ControlComponent<Metavars, Measurements::SystemA1>;\ntemplate <typename Metavars>\nusing MockComponentA1 = MockControlComponent<Metavars, Measurements::SystemA1>;\n\nstruct Metavars {\n  using component_list =\n      tmpl::list<ComponentA0<Metavars>, ComponentB0<Metavars>,\n                 MockComponentA1<Metavars>>;\n};\n\nusing expected_all_control_components =\n    tmpl::list<ComponentA0<Metavars>, ComponentB0<Metavars>,\n               ComponentA1<Metavars>>;\n\nusing all_control_components = metafunctions::all_control_components<Metavars>;\nstatic_assert(\n    tmpl::size<tmpl::list_difference<all_control_components,\n                                     expected_all_control_components>>::value ==\n    0);\nstatic_assert(\n    tmpl::size<tmpl::list_difference<expected_all_control_components,\n                                     all_control_components>>::value == 0);\n}  // namespace Components\n\nnamespace InterpolationTargetTags {\ntemplate <typename Systems>\nstruct Target;\ntemplate <typename Systems>\nstruct HorizonMetavars;\n\nstruct SubmeasurementNoVoid\n    : tt::ConformsTo<control_system::protocols::Submeasurement> {\n  template <typename ControlSystems>\n  using interpolation_target_tag = Target<ControlSystems>;\n  template <typename ControlSystems>\n  using horizon_metavars = HorizonMetavars<ControlSystems>;\n\n  template <typename ControlSystems>\n  using event = SomeEvent<LabelA, ControlSystems>;\n};\n\nstruct SubmeasurementVoid\n    : tt::ConformsTo<control_system::protocols::Submeasurement> {\n  template <typename ControlSystems>\n  using interpolation_target_tag = void;\n  template <typename ControlSystems>\n  using horizon_metavars = void;\n\n  template <typename ControlSystems>\n  using event = SomeEvent<LabelA, ControlSystems>;\n};\n\nstruct Measurement : tt::ConformsTo<control_system::protocols::Measurement> {\n  using submeasurements = tmpl::list<SubmeasurementNoVoid, SubmeasurementVoid>;\n};\n\nstruct MeasurementEmpty\n    : tt::ConformsTo<control_system::protocols::Measurement> {\n  using submeasurements = tmpl::list<>;\n};\n\ntemplate <typename Label>\nstruct ControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return \"ControlSystem\"; }\n  using measurement = Measurement;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstruct ControlSystemEmpty\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return \"Empty\"; }\n  using measurement = MeasurementEmpty;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstatic_assert(std::is_same_v<submeasurements<Measurement>::type,\n                             Measurement::submeasurements>);\nstatic_assert(std::is_same_v<submeasurements_t<Measurement>,\n                             submeasurements<Measurement>::type>);\n\nstatic_assert(\n    std::is_same_v<\n        interpolation_target_tags<tmpl::list<\n            ControlSystem<LabelA>, ControlSystem<LabelB>, ControlSystemEmpty>>,\n        tmpl::list<\n            Target<tmpl::list<ControlSystem<LabelA>, ControlSystem<LabelB>>>>>);\nstatic_assert(\n    std::is_same_v<\n        horizon_metavars<tmpl::list<ControlSystem<LabelA>,\n                                    ControlSystem<LabelB>, ControlSystemEmpty>>,\n        tmpl::list<HorizonMetavars<\n            tmpl::list<ControlSystem<LabelA>, ControlSystem<LabelB>>>>>);\n}  // namespace InterpolationTargetTags\n\nnamespace Events {\ntemplate <typename Label>\nstruct Submeasurement\n    : tt::ConformsTo<control_system::protocols::Submeasurement> {\n  template <typename ControlSystems>\n  using interpolation_target_tag = void;\n  template <typename ControlSystems>\n  using horizon_metavars = void;\n  template <typename ControlSystems>\n  using event = SomeEvent<Label, ControlSystems>;\n};\ntemplate <typename... Labels>\nstruct Measurement : tt::ConformsTo<control_system::protocols::Measurement> {\n  using submeasurements = tmpl::list<Submeasurement<Labels>...>;\n};\n\nusing MeasurementA = Measurement<LabelA>;\nusing MeasurementBC = Measurement<LabelB, LabelC>;\nusing SystemA = control_system::TestHelpers::System<2, LabelA, MeasurementA>;\nusing SystemBC = control_system::TestHelpers::System<2, LabelB, MeasurementBC>;\n\nusing expected_events = tmpl::list<SomeEvent<LabelA, tmpl::list<SystemA>>,\n                                   SomeEvent<LabelB, tmpl::list<SystemBC>>,\n                                   SomeEvent<LabelC, tmpl::list<SystemBC>>>;\n\nusing events = control_system_events<tmpl::list<SystemA, SystemBC>>;\n\nstatic_assert(\n    tmpl::size<tmpl::list_difference<expected_events, events>>::value == 0);\nstatic_assert(\n    tmpl::size<tmpl::list_difference<events, expected_events>>::value == 0);\n}  // namespace Events\n}  // namespace\n}  // namespace control_system::metafunctions\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_RunCallbacks.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <type_traits>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Metafunctions.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ControlSystem/Examples.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct LabelA {};\nusing Measurement = control_system::TestHelpers::Measurement<LabelA, true>;\nstruct System : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return \"A\"; }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return get_output(i);\n  }\n  using measurement = Measurement;\n  using simple_tags = tmpl::list<>;\n  using control_error = control_system::TestHelpers::ControlError;\n  static constexpr size_t deriv_order = 2;\n\n  struct SubmeasurementQueueTag {\n    using type = double;\n  };\n\n  struct MeasurementQueue : db::SimpleTag {\n    using type = LinkedMessageQueue<double, tmpl::list<SubmeasurementQueueTag>>;\n  };\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags =\n        tmpl::list<control_system::TestHelpers::MeasurementResultTag>;\n\n    template <typename Submeasurement, typename Metavariables>\n    static void apply(Submeasurement /*meta*/, const double measurement_result,\n                      Parallel::GlobalCache<Metavariables>& cache,\n                      const LinkedMessageId<double>& measurement_id) {\n      // Process the submeasurement results and send whatever is\n      // necessary to the control system component.  Usually calls\n      // some simple action.\n      auto& control_system_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, System>>(cache);\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue,\n          control_system::TestHelpers::SomeControlSystemUpdater,\n          SubmeasurementQueueTag>>(control_system_proxy, measurement_id,\n                                   measurement_result);\n    }\n  };\n};\nstatic_assert(\n    tt::assert_conforms_to_v<System, control_system::protocols::ControlSystem>);\n// avoid unused variable warning\nstatic_assert(System::deriv_order == 2);\n\nusing MeasureEvent = tmpl::front<\n    typename Measurement::submeasurements>::template event<tmpl::list<System>>;\n\ntemplate <typename Metavariables>\nstruct ElementComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockControlSystemComponent {\n  using component_being_mocked = ControlComponent<Metavariables, System>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using const_global_cache_tags = tmpl::list<control_system::Tags::Verbosity>;\n  using simple_tags_from_options =\n      tmpl::list<System::MeasurementQueue,\n                 control_system::TestHelpers::MeasurementResultTime,\n                 control_system::TestHelpers::MeasurementResultTag>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<ElementComponent<Metavariables>,\n                                    MockControlSystemComponent<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Event, tmpl::list<MeasureEvent>>>;\n  };\n};\n}  // namespace\n\n// This tests control_system::RunCallbacks and also does additional testing of\n// the protocol examples.\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.RunCallbacks\", \"[ControlSystem][Unit]\") {\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\n  using element_component = ElementComponent<Metavariables>;\n  using control_system_component = MockControlSystemComponent<Metavariables>;\n  const element_component* const element_component_p = nullptr;\n\n  MockRuntimeSystem runner{{::Verbosity::Silent}};\n  ActionTesting::emplace_array_component<element_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n  ActionTesting::emplace_singleton_component<control_system_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, System::MeasurementQueue::type{}, 0.0,\n      0.0);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& cache = ActionTesting::cache<element_component>(runner, 0);\n  // Making our own DataBox is easier than using the one in the mock\n  // element.\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::Time, ::Tags::PreviousTriggerTime,\n                        control_system::TestHelpers::SomeTagOnElement>>(\n      Metavariables{}, 1.234, std::optional<double>{}, 5.678);\n\n  const MeasureEvent event{};\n  auto obs_box = make_observation_box<db::AddComputeTags<\n      control_system::TestHelpers::SomeOtherTagOnElementCompute>>(\n      make_not_null(&box));\n  event.run(make_not_null(&obs_box), cache, 0, element_component_p, {});\n\n  CHECK(\n      ActionTesting::number_of_queued_simple_actions<control_system_component>(\n          runner, 0) == 2);\n  for (size_t i = 0; i < 2; i++) {\n    ActionTesting::invoke_queued_simple_action<control_system_component>(\n        make_not_null(&runner), 0);\n  }\n  CHECK(ActionTesting::is_simple_action_queue_empty<control_system_component>(\n      runner, 0));\n\n  CHECK(ActionTesting::get_databox_tag<\n            control_system_component,\n            control_system::TestHelpers::MeasurementResultTime>(runner, 0) ==\n        1.234);\n  CHECK(ActionTesting::get_databox_tag<\n            control_system_component,\n            control_system::TestHelpers::MeasurementResultTag>(runner, 0) ==\n        5.678);\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <vector>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/Tags/FunctionsOfTimeInitialize.hpp\"\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/OptionTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/ControlSystem/SystemHelpers.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct LabelA {};\nstruct LabelB {};\nstruct Rotation {};\nusing system = control_system::TestHelpers::System<\n    2, LabelA, control_system::TestHelpers::Measurement<LabelA>>;\nusing system2 = control_system::TestHelpers::System<\n    2, LabelB, control_system::TestHelpers::Measurement<LabelA>>;\nusing quat_system = control_system::TestHelpers::System<\n    2, Rotation, control_system::TestHelpers::Measurement<Rotation>>;\n\nstruct MetavarsEmpty {\n  static constexpr size_t volume_dim = 3;\n};\n\nusing FakeCreator = TestHelpers::control_system::FakeCreator;\n\nvoid test_all_tags() {\n  INFO(\"Test all tags\");\n  using write_tag = control_system::Tags::WriteDataToDisk;\n  TestHelpers::db::test_simple_tag<write_tag>(\"WriteDataToDisk\");\n  using observe_tag = control_system::Tags::ObserveCenters;\n  TestHelpers::db::test_simple_tag<observe_tag>(\"ObserveCenters\");\n  using averager_tag = control_system::Tags::Averager<system>;\n  TestHelpers::db::test_simple_tag<averager_tag>(\"Averager\");\n  using timescaletuner_tag = control_system::Tags::TimescaleTuner<system>;\n  TestHelpers::db::test_simple_tag<timescaletuner_tag>(\"TimescaleTuner\");\n  using controller_tag = control_system::Tags::Controller<system>;\n  TestHelpers::db::test_simple_tag<controller_tag>(\"Controller\");\n  using fot_tag = control_system::Tags::FunctionsOfTimeInitialize;\n  TestHelpers::db::test_simple_tag<fot_tag>(\"FunctionsOfTime\");\n  using verbosity_tag = control_system::Tags::Verbosity;\n  TestHelpers::db::test_simple_tag<verbosity_tag>(\"Verbosity\");\n\n  using control_error_tag = control_system::Tags::ControlError<system>;\n  TestHelpers::db::test_simple_tag<control_error_tag>(\"ControlError\");\n\n  using active_tag = control_system::Tags::IsActiveMap;\n  TestHelpers::db::test_simple_tag<active_tag>(\"IsActiveMap\");\n\n  using measurement_tag = control_system::Tags::MeasurementTimescales;\n  TestHelpers::db::test_simple_tag<measurement_tag>(\"MeasurementTimescales\");\n\n  using measurements_per_update_tag =\n      control_system::Tags::MeasurementsPerUpdate;\n  TestHelpers::db::test_simple_tag<measurements_per_update_tag>(\n      \"MeasurementsPerUpdate\");\n  TestHelpers::db::test_simple_tag<control_system::Tags::DelayUpdate>(\n      \"DelayUpdate\");\n  using current_measurement_tag =\n      control_system::Tags::CurrentNumberOfMeasurements;\n  TestHelpers::db::test_simple_tag<current_measurement_tag>(\n      \"CurrentNumberOfMeasurements\");\n  using system_to_combined_tag = control_system::Tags::SystemToCombinedNames;\n  TestHelpers::db::test_simple_tag<system_to_combined_tag>(\n      \"SystemToCombinedNames\");\n  using aggregators_tag = control_system::Tags::UpdateAggregators;\n  TestHelpers::db::test_simple_tag<aggregators_tag>(\"UpdateAggregators\");\n}\n\nvoid test_control_sys_inputs() {\n  INFO(\"Test control system inputs\");\n  const double decrease_timescale_threshold = 1.0e-2;\n  const double increase_timescale_threshold = 1.0e-4;\n  const double increase_factor = 1.01;\n  const double decrease_factor = 0.99;\n  const double max_timescale = 10.0;\n  const double min_timescale = 1.0e-3;\n  const TimescaleTuner<true> expected_tuner(\n      std::vector<double>{1.}, max_timescale, min_timescale,\n      increase_timescale_threshold, increase_factor,\n      decrease_timescale_threshold, decrease_factor);\n  const Averager<1> expected_averager(0.25, true);\n  const Controller<2> expected_controller(0.3);\n  const std::string expected_name{\"LabelA\"};\n\n  const auto input_holder = TestHelpers::test_option_tag<\n      control_system::OptionTags::ControlSystemInputs<system>>(\n      \"Averager:\\n\"\n      \"  AverageTimescaleFraction: 0.25\\n\"\n      \"  Average0thDeriv: true\\n\"\n      \"Controller:\\n\"\n      \"  UpdateFraction: 0.3\\n\"\n      \"TimescaleTuner:\\n\"\n      \"  InitialTimescales: [1.]\\n\"\n      \"  MinTimescale: 1e-3\\n\"\n      \"  MaxTimescale: 10.\\n\"\n      \"  DecreaseThreshold: 1e-2\\n\"\n      \"  IncreaseThreshold: 1e-4\\n\"\n      \"  IncreaseFactor: 1.01\\n\"\n      \"  DecreaseFactor: 0.99\\n\"\n      \"ControlError:\\n\");\n  REQUIRE(input_holder.has_value());\n  CHECK(expected_averager == input_holder->averager);\n  CHECK(expected_controller == input_holder->controller);\n  CHECK(expected_tuner == input_holder->tuner);\n  CHECK(\n      expected_name ==\n      std::decay_t<decltype(input_holder)>::value_type::control_system::name());\n\n  const auto write_data =\n      TestHelpers::test_option_tag<control_system::OptionTags::WriteDataToDisk>(\n          \"true\");\n  CHECK(write_data);\n  // We don't check the control error because the example one is empty and\n  // doesn't have a comparison operator. Once a control error is added that\n  // contains member data (and thus, options), then it can be tested\n}\n\nvoid test_individual_tags() {\n  const auto create_expected_tuner = [](const size_t num_components) {\n    const double decrease_timescale_threshold = 1.0e-2;\n    const double increase_timescale_threshold = 1.0e-4;\n    const double increase_factor = 1.01;\n    const double decrease_factor = 0.99;\n    const double max_timescale = 10.0;\n    const double min_timescale = 1.0e-3;\n    return TimescaleTuner<true>{std::vector<double>(num_components, 1.0),\n                                max_timescale,\n                                min_timescale,\n                                increase_timescale_threshold,\n                                increase_factor,\n                                decrease_timescale_threshold,\n                                decrease_factor};\n  };\n\n  const auto tuner_str = [](const bool is_active) -> std::string {\n    return is_active ? \"Averager:\\n\"\n                       \"  AverageTimescaleFraction: 0.25\\n\"\n                       \"  Average0thDeriv: true\\n\"\n                       \"Controller:\\n\"\n                       \"  UpdateFraction: 0.3\\n\"\n                       \"TimescaleTuner:\\n\"\n                       \"  InitialTimescales: 1.\\n\"\n                       \"  MinTimescale: 1e-3\\n\"\n                       \"  MaxTimescale: 10.\\n\"\n                       \"  DecreaseThreshold: 1e-2\\n\"\n                       \"  IncreaseThreshold: 1e-4\\n\"\n                       \"  IncreaseFactor: 1.01\\n\"\n                       \"  DecreaseFactor: 0.99\\n\"\n                       \"ControlError:\\n\"\n                     : \"None\";\n  };\n\n  using tuner_tag = control_system::Tags::TimescaleTuner<system>;\n  using quat_tuner_tag = control_system::Tags::TimescaleTuner<quat_system>;\n\n  const auto holder = TestHelpers::test_option_tag<\n      control_system::OptionTags::ControlSystemInputs<system>>(tuner_str(true));\n  const auto holder2 = TestHelpers::test_option_tag<\n      control_system::OptionTags::ControlSystemInputs<system2>>(\n      tuner_str(true));\n  const auto quat_holder = TestHelpers::test_option_tag<\n      control_system::OptionTags::ControlSystemInputs<quat_system>>(\n      tuner_str(true));\n  const auto inactive_holder = TestHelpers::test_option_tag<\n      control_system::OptionTags::ControlSystemInputs<system>>(\n      tuner_str(false));\n\n  const std::unique_ptr<DomainCreator<3>> creator =\n      std::make_unique<FakeCreator>(std::unordered_map<std::string, size_t>{\n          {system::name(), 2}, {quat_system::name(), 3}});\n  const std::unique_ptr<DomainCreator<3>> creator_empty =\n      std::make_unique<FakeCreator>(std::unordered_map<std::string, size_t>{});\n\n  const TimescaleTuner<true> created_tuner =\n      tuner_tag::create_from_options<MetavarsEmpty>(holder, creator, 0.0);\n  const TimescaleTuner<true> quat_created_tuner =\n      quat_tuner_tag::create_from_options<MetavarsEmpty>(quat_holder, creator,\n                                                         0.0);\n  const TimescaleTuner<true> inactive_created_tuner =\n      tuner_tag::create_from_options<MetavarsEmpty>(inactive_holder,\n                                                    creator_empty, 0.0);\n\n  CHECK(created_tuner == create_expected_tuner(2));\n  CHECK(quat_created_tuner == create_expected_tuner(3));\n  CHECK(inactive_created_tuner == TimescaleTuner<true>{});\n\n  using control_error_tag = control_system::Tags::ControlError<system>;\n  using control_error_tag2 = control_system::Tags::ControlError<system2>;\n\n  // We're only checking for errors here, so the fact that it doesn't error is\n  // enough of a check. We test errors below.\n  [[maybe_unused]] const control_error_tag::type created_control_error =\n      control_error_tag::create_from_options(holder);\n  [[maybe_unused]] const control_error_tag2::type created_control_error2 =\n      control_error_tag2::create_from_options(holder2);\n\n  using controller_tag = control_system::Tags::Controller<system>;\n\n  Controller<2> expected_controller(0.3);\n  expected_controller.set_initial_update_time(0.0);\n  expected_controller.assign_time_between_updates(\n      min(created_tuner.current_timescale()));\n\n  const Controller<2> created_controller =\n      controller_tag::create_from_options<MetavarsEmpty>(holder, creator, 0.0);\n\n  CHECK(created_controller == expected_controller);\n\n  using active_map_tag = control_system::Tags::IsActiveMap;\n\n  const auto active_map = active_map_tag::create_from_options<MetavarsEmpty>(\n      quat_holder, holder2, inactive_holder);\n  CHECK(active_map == std::unordered_map<std::string, bool>{{\"Rotation\", true},\n                                                            {\"LabelB\", true},\n                                                            {\"LabelA\", false}});\n}\n\nstruct NamesMetavars {\n  using component_list =\n      tmpl::list<ControlComponent<NamesMetavars, system>,\n                 ControlComponent<NamesMetavars, system2>,\n                 ControlComponent<NamesMetavars, quat_system>>;\n};\n\nvoid test_system_to_combined_names_tag() {\n  using system_to_combined_tag = control_system::Tags::SystemToCombinedNames;\n\n  const std::unordered_map<std::string, std::string> system_to_combined_names =\n      system_to_combined_tag::create_from_options<NamesMetavars>();\n\n  CHECK(system_to_combined_names.count(\"LabelA\") == 1);\n  CHECK(system_to_combined_names.count(\"LabelB\") == 1);\n  CHECK(system_to_combined_names.count(\"Rotation\") == 1);\n\n  CHECK(system_to_combined_names.at(\"LabelA\") == \"LabelALabelB\");\n  CHECK(system_to_combined_names.at(\"LabelB\") == \"LabelALabelB\");\n  CHECK(system_to_combined_names.at(\"Rotation\") == \"Rotation\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Tags\", \"[ControlSystem][Unit]\") {\n  test_all_tags();\n  test_control_sys_inputs();\n  test_individual_tags();\n  test_system_to_combined_names_tag();\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_TimescaleTuner.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <vector>\n\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <bool AllowDecrease>\nvoid test_increase_or_decrease() {\n  // If we don't allow decrease, then just set the threshold to infinity. We\n  // can't set it to max otherwise we'll get an FPE.\n  const double decrease_timescale_threshold =\n      AllowDecrease ? 1.0e-2 : std::numeric_limits<double>::infinity();\n  const double increase_timescale_threshold = 1.0e-4;\n  const double increase_factor = 1.01;\n  const double decrease_factor = AllowDecrease ? 0.99 : 1.0;\n  const double max_timescale = 10.0;\n  const double min_timescale = 1.0e-3;\n\n  TimescaleTuner<AllowDecrease> tst(\n      1.0, max_timescale, min_timescale, increase_timescale_threshold,\n      increase_factor, decrease_timescale_threshold, decrease_factor);\n\n  const DataVector timescale{1.0};\n\n  CHECK_FALSE(tst.timescales_have_been_set());\n  tst.resize_timescales(timescale.size(), timescale[0]);\n  CHECK(tst.timescales_have_been_set());\n  CHECK(tst.current_timescale() == timescale);\n\n  // Check the suggested timescale function\n  // (1) timescale < min_timescale\n  double suggested_tscale = min_timescale * 0.5;\n  tst.set_timescale_if_in_allowable_range(suggested_tscale);\n  CHECK(tst.current_timescale() ==\n        make_with_value<DataVector>(timescale, min_timescale));\n  // (2) min_timescale < timescale < max_timescale\n  suggested_tscale = 0.5 * (min_timescale + max_timescale);\n  tst.set_timescale_if_in_allowable_range(suggested_tscale);\n  CHECK(tst.current_timescale() ==\n        make_with_value<DataVector>(timescale, suggested_tscale));\n  // (3) max_timescale < timescale\n  suggested_tscale = max_timescale * 2.0;\n  tst.set_timescale_if_in_allowable_range(suggested_tscale);\n  CHECK(tst.current_timescale() ==\n        make_with_value<DataVector>(timescale, max_timescale));\n\n  // set timescale for remaining tests\n  double tscale = 10.0;\n  tst.set_timescale_if_in_allowable_range(tscale);\n  CHECK(tst.current_timescale() ==\n        make_with_value<DataVector>(timescale, tscale));\n\n  // helper vars for greater than and less than one, used to choose\n  // the correct Q for each CHECK\n  const double greater_than_one = 1.1;\n  const double less_than_one = 0.9;\n\n  auto run_tests = [&](double sign_of_q) {\n    // Here, situations that trigger the outer conditional related to a decrease\n    // in timescale are numbered, while the nested conditional choices for each\n    // associated outer case are suffixed with letters\n\n    DataVector q{1, 0.0};\n    DataVector qdot{1, 0.0};\n\n    if constexpr (AllowDecrease) {\n      // (1) |Q| > decrease_timescale_threshold\n      //     |\\dot{Q}| <= decrease_timescale_threshold/timescale\n      q = sign_of_q * greater_than_one * decrease_timescale_threshold;\n      qdot = sign_of_q * less_than_one * decrease_timescale_threshold / tscale;\n\n      // (1a) Q\\dot{Q} > 0\n      //      |\\dot{Q}| >= 0.5*|Q|/timescale\n      // the error is large and growing: decrease timescale\n      tscale *= decrease_factor;\n      tst.update_timescale({{q, qdot}});\n      CHECK(tst.current_timescale() ==\n            make_with_value<DataVector>(timescale, tscale));\n\n      // (1b) Q\\dot{Q} <= 0\n      //      |\\dot{Q}| < 0.5*|Q|/timescale\n      // the error is not decreasing quickly enough: decrease timescale\n      qdot = -less_than_one * 0.5 * q / tscale;\n      tscale *= decrease_factor;\n      tst.update_timescale({{q, qdot}});\n      CHECK(tst.current_timescale() ==\n            make_with_value<DataVector>(timescale, tscale));\n\n      // (1c) Q\\dot{Q} > 0\n      //      |\\dot{Q}| < 0.5*|Q|/timescale\n      // the error is large and growing quickly: decrease timescale\n      qdot *= -1.0;\n      tscale *= decrease_factor;\n      tst.update_timescale({{q, qdot}});\n      CHECK(tst.current_timescale() ==\n            make_with_value<DataVector>(timescale, tscale));\n\n      // (2) |Q| <= decrease_timescale_threshold\n      //     |\\dot{Q}| > decrease_timescale_threshold/timescale\n      q = sign_of_q * less_than_one * decrease_timescale_threshold;\n      qdot =\n          sign_of_q * greater_than_one * decrease_timescale_threshold / tscale;\n\n      // (2a) Q\\dot{Q} > 0\n      //      |\\dot{Q}| >= 0.5*|Q|/timescale\n      // the error is growing quickly: decrease timescale\n      tscale *= decrease_factor;\n      tst.update_timescale({{q, qdot}});\n      CHECK(tst.current_timescale() ==\n            make_with_value<DataVector>(timescale, tscale));\n\n      // (2b) Q\\dot{Q} <= 0\n      //      |\\dot{Q}| < 0.5*|Q|/timescale\n      // NOTE: the second piece is unachievable, given the conditions of (2).\n      // This check is included in the do nothing test.\n\n      // (2c) Q\\dot{Q} > 0\n      //      |\\dot{Q}| < 0.5*|Q|/timescale\n      // NOTE: the second piece is unachievable, given the conditions of (2),\n      // however, Q\\dot{Q} > 0 is sufficient to trigger a decrease.\n      // the error is growing quickly: decrease timescale\n      tscale *= decrease_factor;\n      tst.update_timescale({{q, qdot}});\n      CHECK(tst.current_timescale() ==\n            make_with_value<DataVector>(timescale, tscale));\n\n      // (3) |Q| > decrease_timescale_threshold\n      //     |\\dot{Q}| > decrease_timescale_threshold/timescale\n      q = sign_of_q * greater_than_one * decrease_timescale_threshold;\n      qdot =\n          sign_of_q * greater_than_one * decrease_timescale_threshold / tscale;\n\n      // (3a) Q\\dot{Q} > 0\n      //      |\\dot{Q}| >= 0.5*|Q|/timescale\n      // the error is large and growing quickly: decrease timescale\n      tscale *= decrease_factor;\n      tst.update_timescale({{q, qdot}});\n      CHECK(tst.current_timescale() ==\n            make_with_value<DataVector>(timescale, tscale));\n\n      // (3b) Q\\dot{Q} <= 0\n      //      |\\dot{Q}| < 0.5*|Q|/timescale\n      // the error is large and not decreasing quickly enough: decrease\n      // timescale\n      qdot *= -1.0;\n      q = sign_of_q * greater_than_one * 2.0 * greater_than_one *\n          decrease_timescale_threshold;\n      tscale *= decrease_factor;\n      tst.update_timescale({{q, qdot}});\n      CHECK(tst.current_timescale() ==\n            make_with_value<DataVector>(timescale, tscale));\n\n      // (3c) Q\\dot{Q} > 0\n      //      |\\dot{Q}| < 0.5*|Q|/timescale\n      // the error is large and growing quickly: decrease timescale\n      qdot *= -1.0;\n      tscale *= decrease_factor;\n      tst.update_timescale({{q, qdot}});\n      CHECK(tst.current_timescale() ==\n            make_with_value<DataVector>(timescale, tscale));\n    }\n\n    // There is only one case which triggers an increase in the timescale\n    // (4) |Q| < increase_timescale_threshold\n    //     |\\dot{Q}| < increase_timescale_threshold/timescale\n    // the error and time derivative are sufficiently small: increase timescale\n    tscale *= 0.5;\n    tst.set_timescale_if_in_allowable_range(tscale);\n    q = sign_of_q * less_than_one * increase_timescale_threshold;\n    qdot = sign_of_q * less_than_one * increase_timescale_threshold / tscale;\n    tscale *= increase_factor;\n    tst.update_timescale({{q, qdot}});\n    CHECK(tst.current_timescale() ==\n          make_with_value<DataVector>(timescale, tscale));\n  };\n  // testing for Q>0 and Q<0\n  run_tests(1.0);\n  run_tests(-1.0);\n}\n\ntemplate <bool AllowDecrease>\nvoid test_no_change_to_timescale() {\n  // If we don't allow decrease, then just set the threshold to infinity. We\n  // can't set it to max otherwise we'll get an FPE.\n  const double decrease_timescale_threshold =\n      AllowDecrease ? 1.0e-2 : std::numeric_limits<double>::infinity();\n  const double increase_timescale_threshold = 1.0e-4;\n  const double increase_factor = 1.01;\n  const double decrease_factor = AllowDecrease ? 0.99 : 1.0;\n  const double max_timescale = 10.0;\n  const double min_timescale = 1.0e-3;\n\n  TimescaleTuner<AllowDecrease> tst(\n      std::vector<double>{1.0}, max_timescale, min_timescale,\n      increase_timescale_threshold, increase_factor,\n      decrease_timescale_threshold, decrease_factor);\n\n  const DataVector timescale{1.0};\n  CHECK(tst.current_timescale() == timescale);\n\n  double tscale = 10.0;\n  tst.set_timescale_if_in_allowable_range(tscale);\n  CHECK(tst.current_timescale() ==\n        make_with_value<DataVector>(timescale, tscale));\n\n  // helper vars for greater than and less than one, used to choose\n  // the correct Q for each CHECK\n  const double greater_than_one = 1.1;\n  const double less_than_one = 0.9;\n\n  auto run_tests = [&](double sign_of_q) {\n    // (1) |Q| > decrease_timescale_threshold\n    //     |\\dot{Q}| <= decrease_timescale_threshold/timescale\n    DataVector q{sign_of_q * greater_than_one * decrease_timescale_threshold};\n    DataVector qdot{-1.0 * sign_of_q * less_than_one *\n                    decrease_timescale_threshold / tscale};\n\n    // (1d) Q\\dot{Q} <= 0\n    //      |\\dot{Q}| >= 0.5*|Q|/timescale\n    // the error is large, but decreasing quickly enough: do nothing\n    tst.update_timescale({{q, qdot}});\n    CHECK(tst.current_timescale() ==\n          make_with_value<DataVector>(timescale, tscale));\n\n    // (2) |Q| <= decrease_timescale_threshold\n    //     |\\dot{Q}| > decrease_timescale_threshold/timescale\n    q = sign_of_q * less_than_one * decrease_timescale_threshold;\n    qdot = -1.0 * sign_of_q * greater_than_one * decrease_timescale_threshold /\n           tscale;\n\n    // (2b) Q\\dot{Q} <= 0\n    //      |\\dot{Q}| < 0.5*|Q|/timescale\n    // NOTE: the second piece is unachievable, given the conditions of (2).\n    // the error is small and decreasing: do nothing\n    tst.update_timescale({{q, qdot}});\n    CHECK(tst.current_timescale() ==\n          make_with_value<DataVector>(timescale, tscale));\n\n    // (2d) Q\\dot{Q} <= 0\n    //      |\\dot{Q}| >= 0.5*|Q|/timescale\n    // the error is small and decreasing quickly: do nothing\n    q = less_than_one * 2.0 * qdot * tscale;\n    qdot *= -1.0;\n    tst.update_timescale({{q, qdot}});\n    CHECK(tst.current_timescale() ==\n          make_with_value<DataVector>(timescale, tscale));\n\n    // (3) |Q| > decrease_timescale_threshold\n    //     |\\dot{Q}| > decrease_timescale_threshold/timescale\n    q = sign_of_q * greater_than_one * decrease_timescale_threshold;\n    qdot = -1.0 * sign_of_q * greater_than_one * decrease_timescale_threshold /\n           tscale;\n\n    // (3d) Q\\dot{Q} <= 0\n    //      |\\dot{Q}| >= 0.5*|Q|/timescale\n    // the error is large, but decreasing quickly: do nothing\n    tst.update_timescale({{q, qdot}});\n    CHECK(tst.current_timescale() ==\n          make_with_value<DataVector>(timescale, tscale));\n\n    // (4) |Q| < increase_timescale_threshold\n    //     |\\dot{Q}| >= (increase_timescale_threshold-|Q|)/timescale\n    // the error and time derivative are sufficiently small: increase timescale\n    q = sign_of_q * less_than_one * increase_timescale_threshold;\n    qdot = sign_of_q * greater_than_one *\n           (increase_timescale_threshold - fabs(q)) / tscale;\n    tst.update_timescale({{q, qdot}});\n    CHECK(tst.current_timescale() ==\n          make_with_value<DataVector>(timescale, tscale));\n\n    // (5) |Q| >= increase_timescale_threshold\n    //     |\\dot{Q}| < (increase_timescale_threshold-|Q|)/timescale\n    // the error and time derivative are sufficiently small: increase timescale\n    q = sign_of_q * greater_than_one * increase_timescale_threshold;\n    qdot = sign_of_q * less_than_one *\n           (increase_timescale_threshold - fabs(q)) / tscale;\n    tst.update_timescale({{q, qdot}});\n    CHECK(tst.current_timescale() ==\n          make_with_value<DataVector>(timescale, tscale));\n\n    // (6) |Q| >= increase_timescale_threshold\n    //     |\\dot{Q}| >= (increase_timescale_threshold-|Q|)/timescale\n    // the error and time derivative are sufficiently small: increase timescale\n    q = sign_of_q * greater_than_one * increase_timescale_threshold;\n    qdot = sign_of_q * greater_than_one *\n           (increase_timescale_threshold - fabs(q)) / tscale;\n    tst.update_timescale({{q, qdot}});\n    CHECK(tst.current_timescale() ==\n          make_with_value<DataVector>(timescale, tscale));\n  };\n  run_tests(1.0);   // test positive Q\n  run_tests(-1.0);  // test negative Q\n}\n\ntemplate <bool AllowDecrease>\nvoid test_create_from_options() {\n  const double decrease_timescale_threshold =\n      AllowDecrease ? 1.0e-2 : std::numeric_limits<double>::max();\n  const double increase_timescale_threshold = 1.0e-4;\n  const double increase_factor = 1.01;\n  const double decrease_factor = AllowDecrease ? 0.99 : 1.0;\n  const double max_timescale = 10.0;\n  const double min_timescale = 1.0e-3;\n\n  const TimescaleTuner<AllowDecrease> expected{\n      std::vector<double>{1.}, max_timescale,\n      min_timescale,           increase_timescale_threshold,\n      increase_factor,         decrease_timescale_threshold,\n      decrease_factor};\n\n  const auto tst = TestHelpers::test_creation<TimescaleTuner<AllowDecrease>>(\n      \"InitialTimescales: [1.]\\n\"\n      \"MinTimescale: 1e-3\\n\"\n      \"MaxTimescale: 10.\\n\"\n      \"IncreaseThreshold: 1e-4\\n\"\n      \"IncreaseFactor: 1.01\\n\" +\n      (AllowDecrease ? \"DecreaseThreshold: 1e-2\\n\"\n                       \"DecreaseFactor: 0.99\\n\"s\n                     : \"\"s));\n  CHECK(tst == expected);\n\n  auto tst2 = TestHelpers::test_creation<TimescaleTuner<AllowDecrease>>(\n      \"InitialTimescales: 1.\\n\"\n      \"MinTimescale: 1e-3\\n\"\n      \"MaxTimescale: 10.\\n\"\n      \"IncreaseThreshold: 1e-4\\n\"\n      \"IncreaseFactor: 1.01\\n\" +\n      (AllowDecrease ? \"DecreaseThreshold: 1e-2\\n\"\n                       \"DecreaseFactor: 0.99\\n\"s\n                     : \"\"s));\n\n  CHECK_FALSE(tst2 == expected);\n  tst2.resize_timescales(1);\n  CHECK(tst2 == expected);\n}\n\ntemplate <bool AllowDecrease>\nvoid test_equality_and_serialization() {\n  const double decrease_timescale_threshold = 1.0e-2;\n  const double increase_timescale_threshold = 1.0e-4;\n  const double increase_factor = 1.01;\n  const double decrease_factor = AllowDecrease ? 0.99 : 1.0;\n  const double max_timescale = 10.0;\n  const double min_timescale = 1.0e-3;\n\n  TimescaleTuner<AllowDecrease> tst1(\n      std::vector<double>{0.3}, max_timescale, min_timescale,\n      increase_timescale_threshold, increase_factor,\n      decrease_timescale_threshold, decrease_factor);\n\n  TimescaleTuner<AllowDecrease> tst2(\n      std::vector<double>{0.4}, max_timescale, min_timescale,\n      increase_timescale_threshold, increase_factor,\n      decrease_timescale_threshold, decrease_factor);\n\n  TimescaleTuner<AllowDecrease> tst3{};\n\n  CHECK(tst1 == tst1);\n  CHECK(tst1 != tst2);\n  CHECK(serialize_and_deserialize(tst1) == tst1);\n  CHECK(tst1 != tst3);\n  CHECK(serialize_and_deserialize(tst3) == tst3);\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double decrease_timescale_threshold = 1.0e-2;\n        const double increase_timescale_threshold = 1.0e-4;\n        const double increase_factor = 1.01;\n        const double decrease_factor = 0.99;\n        const double max_timescale = 10.0;\n        const double min_timescale = 1.0e-3;\n\n        const std::vector<double> init_timescale{0.0};\n        TimescaleTuner<true> tst(init_timescale, max_timescale, min_timescale,\n                                 increase_timescale_threshold, increase_factor,\n                                 decrease_timescale_threshold, decrease_factor);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Initial timescale must be > 0\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double decrease_timescale_threshold = 1.0e-2;\n        const double increase_timescale_threshold = 1.0e-4;\n        const double increase_factor = 1.01;\n        const double decrease_factor = 0.99;\n        const double max_timescale = 10.0;\n        const double min_timescale = 1.0e-3;\n\n        TimescaleTuner<false> tst(\n            {0.1}, max_timescale, min_timescale, increase_timescale_threshold,\n            increase_factor, decrease_timescale_threshold, decrease_factor);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"If 'AllowDecrease' is false, then \"\n                                         \"the specified decrease_factor must \"\n                                         \"be 1.0\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      const double decrease_timescale_threshold = 1.0e-2;\n                      const double increase_timescale_threshold = 1.0e-4;\n                      const double max_timescale = 10.0;\n                      const double min_timescale = 1.0e-3;\n\n                      const double increase_factor = 1.1;\n                      const double decrease_factor = -0.99;\n\n                      TimescaleTuner<true> tst(\n                          {1.0}, max_timescale, min_timescale,\n                          increase_timescale_threshold, increase_factor,\n                          decrease_timescale_threshold, decrease_factor);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"must satisfy 0 < decrease_factor <= 1\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      const double decrease_timescale_threshold = 1.0e-2;\n                      const double increase_timescale_threshold = 1.0e-4;\n                      const double max_timescale = 10.0;\n                      const double min_timescale = 1.0e-3;\n\n                      const double increase_factor = 1.1;\n                      const double decrease_factor = 1.01;\n\n                      TimescaleTuner<true> tst(\n                          {1.0}, max_timescale, min_timescale,\n                          increase_timescale_threshold, increase_factor,\n                          decrease_timescale_threshold, decrease_factor);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"must satisfy 0 < decrease_factor <= 1\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      const double decrease_timescale_threshold = 1.0e-2;\n                      const double increase_timescale_threshold = 1.0e-4;\n                      const double max_timescale = 10.0;\n                      const double min_timescale = 1.0e-3;\n\n                      const double increase_factor = 0.99;\n                      const double decrease_factor = 0.8;\n\n                      TimescaleTuner<true> tst(\n                          {1.0}, max_timescale, min_timescale,\n                          increase_timescale_threshold, increase_factor,\n                          decrease_timescale_threshold, decrease_factor);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\"must be >= 1\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      const double decrease_timescale_threshold = 1.0e-2;\n                      const double increase_timescale_threshold = 1.0e-4;\n                      const double increase_factor = 1.01;\n                      const double decrease_factor = 0.99;\n\n                      const double max_timescale = 10.0;\n                      const double min_timescale = 0.0;\n\n                      TimescaleTuner<true> tst(\n                          {1.0}, max_timescale, min_timescale,\n                          increase_timescale_threshold, increase_factor,\n                          decrease_timescale_threshold, decrease_factor);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\"must be > 0\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      const double decrease_timescale_threshold = 1.0e-2;\n                      const double increase_timescale_threshold = 1.0e-4;\n                      const double increase_factor = 1.01;\n                      const double decrease_factor = 0.99;\n\n                      const double max_timescale = 1.0e-4;\n                      const double min_timescale = 1.0e-3;\n\n                      TimescaleTuner<true> tst(\n                          {1.0}, max_timescale, min_timescale,\n                          increase_timescale_threshold, increase_factor,\n                          decrease_timescale_threshold, decrease_factor);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"must be > than the specified minimum timescale\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      const double increase_factor = 1.01;\n                      const double decrease_factor = 0.99;\n                      const double max_timescale = 10.0;\n                      const double min_timescale = 1.0e-3;\n\n                      const double decrease_timescale_threshold = 1.0e-2;\n                      const double increase_timescale_threshold = 0.0;\n\n                      TimescaleTuner<true> tst(\n                          {1.0}, max_timescale, min_timescale,\n                          increase_timescale_threshold, increase_factor,\n                          decrease_timescale_threshold, decrease_factor);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The specified increase-timescale threshold\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double increase_factor = 1.01;\n        const double decrease_factor = 0.99;\n        const double max_timescale = 10.0;\n        const double min_timescale = 1.0e-3;\n\n        const double decrease_timescale_threshold = 1.0e-4;\n        const double increase_timescale_threshold = 1.0e-3;\n\n        TimescaleTuner<true> tst({1.0}, max_timescale, min_timescale,\n                                 increase_timescale_threshold, increase_factor,\n                                 decrease_timescale_threshold, decrease_factor);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"must be > than the specified increase-timescale threshold\"));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double decrease_timescale_threshold = 1.0e-2;\n        const double increase_timescale_threshold = 1.0e-4;\n        const double increase_factor = 1.01;\n        const double decrease_factor = 0.99;\n        const double max_timescale = 10.0;\n        const double min_timescale = 1.0e-3;\n\n        const std::vector<double> init_timescale{{1.0, 2.0}};\n        TimescaleTuner<true> tst(init_timescale, max_timescale, min_timescale,\n                                 increase_timescale_threshold, increase_factor,\n                                 decrease_timescale_threshold, decrease_factor);\n\n        const std::array<DataVector, 2> qs{{{2.0}, {3.0}}};\n        tst.update_timescale(qs);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"One or both of the number of components in q_and_dtq\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double decrease_timescale_threshold = 1.0e-2;\n        const double increase_timescale_threshold = 1.0e-4;\n        const double increase_factor = 1.01;\n        const double decrease_factor = 0.99;\n        const double max_timescale = 10.0;\n        const double min_timescale = 1.0e-3;\n\n        TimescaleTuner<true> tst(1.0, max_timescale, min_timescale,\n                                 increase_timescale_threshold, increase_factor,\n                                 decrease_timescale_threshold, decrease_factor);\n\n        const std::array<DataVector, 2> qs{{{2.0}}};\n        tst.update_timescale(qs);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Damping timescales in the TimescaleTuner have not been set yet.\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double decrease_timescale_threshold = 1.0e-2;\n        const double increase_timescale_threshold = 1.0e-4;\n        const double increase_factor = 1.01;\n        const double decrease_factor = 0.99;\n        const double max_timescale = 10.0;\n        const double min_timescale = 1.0e-3;\n\n        TimescaleTuner<true> tst(1.0, max_timescale, min_timescale,\n                                 increase_timescale_threshold, increase_factor,\n                                 decrease_timescale_threshold, decrease_factor);\n\n        tst.current_timescale();\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Damping timescales in the TimescaleTuner have not been set yet.\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double decrease_timescale_threshold = 1.0e-2;\n        const double increase_timescale_threshold = 1.0e-4;\n        const double increase_factor = 1.01;\n        const double decrease_factor = 0.99;\n        const double max_timescale = 10.0;\n        const double min_timescale = 1.0e-3;\n\n        const std::vector<double> init_timescale{{1.0, 2.0}};\n        TimescaleTuner<true> tst(init_timescale, max_timescale, min_timescale,\n                                 increase_timescale_threshold, increase_factor,\n                                 decrease_timescale_threshold, decrease_factor);\n\n        tst.resize_timescales(0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Damping timescales must have a non-zero number of components.\"));\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.TimescaleTuner\",\n                  \"[ControlSystem][Unit]\") {\n  tmpl::for_each<tmpl::list<std::true_type, std::false_type>>(\n      [](const auto bool_v) {\n        constexpr bool allow_decrease =\n            tmpl::type_from<decltype(bool_v)>::value;\n        CAPTURE(allow_decrease);\n        test_increase_or_decrease<allow_decrease>();\n        test_no_change_to_timescale<allow_decrease>();\n        test_create_from_options<allow_decrease>();\n        test_equality_and_serialization<allow_decrease>();\n      });\n  test_errors();\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_Trigger.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <unordered_map>\n#include <utility>\n\n#include \"ControlSystem/FutureMeasurements.hpp\"\n#include \"ControlSystem/Tags/FutureMeasurements.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/Trigger.hpp\"\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockDistributedObject.hpp\"\n#include \"Framework/MockRuntimeSystem.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct LabelA {};\nstruct LabelB {};\nstruct LabelC {};\n\nusing measurement = control_system::TestHelpers::Measurement<LabelA>;\nusing SystemA = control_system::TestHelpers::System<2, LabelA, measurement>;\nusing SystemB = control_system::TestHelpers::System<2, LabelB, measurement>;\nusing SystemC = control_system::TestHelpers::System<2, LabelC, measurement>;\nusing control_systems = tmpl::list<SystemA, SystemB, SystemC>;\n\nusing MeasureTrigger = control_system::Trigger<control_systems>;\n\nusing MeasurementFoT = domain::FunctionsOfTime::PiecewisePolynomial<0>;\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n\n  using simple_tags =\n      tmpl::list<Tags::Time,\n                 control_system::Tags::FutureMeasurements<control_systems>>;\n  using compute_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<control_system::Tags::Verbosity>;\n  using mutable_global_cache_tags =\n      tmpl::list<control_system::Tags::MeasurementTimescales>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags, compute_tags>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DenseTrigger, tmpl::list<MeasureTrigger>>>;\n  };\n};\n\nusing MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\nusing component = Component<Metavariables>;\n\nvoid test_trigger_no_replace() {\n  register_classes_with_charm<MeasurementFoT>();\n  const component* const component_p = nullptr;\n\n  control_system::Tags::MeasurementTimescales::type measurement_timescales{};\n  measurement_timescales[\"LabelALabelBLabelC\"] =\n      std::make_unique<MeasurementFoT>(0.0, std::array{DataVector{0.5}}, 0.5);\n  measurement_timescales[\"DifferentMeasurement\"] =\n      std::make_unique<MeasurementFoT>(0.0, std::array{DataVector{1.0}}, 0.1);\n\n  control_system::FutureMeasurements future_measurements(6, 0.0);\n\n  const ElementId<3> element_id{0};\n\n  MockRuntimeSystem runner{{::Verbosity::Silent},\n                           {std::move(measurement_timescales)}};\n  ActionTesting::emplace_array_component_and_initialize<component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, element_id,\n      {0.0, std::move(future_measurements)});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& box =\n      ActionTesting::get_databox<component>(make_not_null(&runner), element_id);\n  auto& cache = ActionTesting::cache<component>(runner, element_id);\n\n  const auto set_time = [&box](const double time) {\n    db::mutate<Tags::Time>(\n        [&time](const gsl::not_null<double*> box_time) { *box_time = time; },\n        make_not_null(&box));\n  };\n\n  MeasureTrigger typed_trigger = serialize_and_deserialize(MeasureTrigger{});\n  DenseTrigger& trigger = typed_trigger;\n\n  // At the initial time, the trigger should be triggered and we should know\n  // the next check time\n  REQUIRE(trigger.is_triggered(make_not_null(&box), cache, element_id,\n                               component_p) == std::optional{true});\n  REQUIRE(trigger.next_check_time(make_not_null(&box), cache, element_id,\n                                  component_p) == std::optional{0.5});\n\n  set_time(0.25);\n\n  // Set the time to sometime before the next check time. Shouldn't be\n  // triggered, but should still have the same check time as before\n  REQUIRE(trigger.is_triggered(make_not_null(&box), cache, element_id,\n                               component_p) == std::optional{false});\n  REQUIRE(trigger.next_check_time(make_not_null(&box), cache, element_id,\n                                  component_p) == std::optional{0.5});\n\n  set_time(0.5);\n\n  // Now at the previous next check time, we should trigger. We also know the\n  // new check time\n  REQUIRE(trigger.is_triggered(make_not_null(&box), cache, element_id,\n                               component_p) == std::optional{true});\n  REQUIRE(trigger.next_check_time(make_not_null(&box), cache, element_id,\n                                  component_p) == std::optional{1.0});\n\n  set_time(0.75);\n\n  // Another intermediate time where we shouldn't trigger.\n  REQUIRE(trigger.is_triggered(make_not_null(&box), cache, element_id,\n                               component_p) == std::optional{false});\n  REQUIRE(trigger.next_check_time(make_not_null(&box), cache, element_id,\n                                  component_p) == std::optional{1.0});\n\n  // Update the measurement timescales\n  Parallel::mutate<control_system::Tags::MeasurementTimescales,\n                   control_system::UpdateSingleFunctionOfTime>(\n      cache, \"LabelALabelBLabelC\"s, 0.5, DataVector{1.0}, 4.0);\n\n  // Now we should be able to calculate the next check time once again and it\n  // should be the same as it was before, since the current time hasn't changed.\n  REQUIRE(trigger.next_check_time(make_not_null(&box), cache, element_id,\n                                  component_p) == std::optional{1.0});\n\n  set_time(1.0);\n\n  // Now the time is at the next trigger time and all measurement timescales\n  // are valid so we should be able to determine the next check time\n  REQUIRE(trigger.is_triggered(make_not_null(&box), cache, element_id,\n                               component_p) == std::optional{true});\n  REQUIRE(trigger.next_check_time(make_not_null(&box), cache, element_id,\n                                  component_p) == std::optional{2.0});\n\n  set_time(2.0);\n\n  // At the next trigger time and timescales are valid so we can calculate\n  // the next check time.\n  REQUIRE(trigger.is_triggered(make_not_null(&box), cache, element_id,\n                               component_p) == std::optional{true});\n  REQUIRE(trigger.next_check_time(make_not_null(&box), cache, element_id,\n                                  component_p) == std::optional{3.0});\n}\n\nvoid test_trigger_with_replace() {\n  register_classes_with_charm<MeasurementFoT>();\n  const component* const component_p = nullptr;\n\n  const ElementId<3> element_id{0};\n\n  control_system::Tags::MeasurementTimescales::type measurement_timescales{};\n  measurement_timescales[\"LabelALabelBLabelC\"] =\n      std::make_unique<MeasurementFoT>(\n          0.0, std::array{DataVector{std::numeric_limits<double>::infinity()}},\n          std::numeric_limits<double>::infinity());\n\n  control_system::FutureMeasurements future_measurements(\n      6, std::numeric_limits<double>::infinity());\n\n  MockRuntimeSystem runner{{}, {std::move(measurement_timescales)}};\n  ActionTesting::emplace_array_component_and_initialize<component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, element_id,\n      {0.0, std::move(future_measurements)});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& box =\n      ActionTesting::get_databox<component>(make_not_null(&runner), element_id);\n  auto& cache = ActionTesting::cache<component>(runner, element_id);\n\n  MeasureTrigger typed_trigger = serialize_and_deserialize(MeasureTrigger{});\n  DenseTrigger& trigger = typed_trigger;\n\n  const auto is_triggered =\n      trigger.is_triggered(make_not_null(&box), cache, element_id, component_p);\n  CHECK(is_triggered == std::optional{false});\n  const auto next_check = trigger.next_check_time(make_not_null(&box), cache,\n                                                  element_id, component_p);\n  CHECK(next_check == std::optional{std::numeric_limits<double>::infinity()});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.Trigger\", \"[Domain][Unit]\") {\n  test_trigger_no_replace();\n  test_trigger_with_replace();\n}\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_UpdateFunctionOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstant.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct System1 {\n  static std::string name() { return \"FoT1\"; }\n};\nstruct System2 {\n  static std::string name() { return \"FoT2\"; }\n};\nstruct System3 {\n  static std::string name() { return \"FoT3\"; }\n};\n\ntemplate <typename Metavariables, size_t Index = 1>\nstruct TestSingleton {\n  static std::string name() { return \"FoT\" + get_output(Index); }\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = size_t;\n  using metavariables = Metavariables;\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 control_system::Tags::MeasurementTimescales,\n                 // Usually this is constant, but we have it mutable for this\n                 // test because we need to change its value to test some\n                 // ASSERTs\n                 control_system::Tags::SystemToCombinedNames,\n                 control_system::Tags::IsActiveMap>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<\n          tmpl::list<control_system::Tags::UpdateAggregators>>>>>;\n};\n\nstruct TestingMetavariables {\n  using component_list = tmpl::list<TestSingleton<TestingMetavariables>>;\n};\n\nvoid test_mutates() {\n  constexpr size_t deriv_order = 2;\n  const double t0 = 0.0;\n  const double expr_time = 1.5;\n  double update_time = expr_time;\n  double expiration_time = 2.0;\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  // Construct unordered map\n  const std::string pp_name{\"TestPiecewisePolynomial\"};\n  const std::string quatfot_name{\"TestQuatFunctionOfTime\"};\n  std::array<DataVector, deriv_order + 1> init_pp_func{\n      {DataVector{3, 0.0}, DataVector{3, 0.0}, DataVector{3, 0.0}}};\n  std::array<DataVector, 1> init_quat_func{DataVector{4, 1.0}};\n\n  domain::FunctionsOfTime::PiecewisePolynomial<deriv_order> expected_pp{\n      t0, init_pp_func, expr_time};\n  domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>\n      expected_quatfot{t0, init_quat_func, init_pp_func, expr_time};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      measurement_timescales{};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      f_of_t_map{};\n  f_of_t_map[pp_name] = std::make_unique<\n      domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>>(\n      t0, init_pp_func, expr_time);\n  f_of_t_map[quatfot_name] = std::make_unique<\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>>(\n      t0, init_quat_func, init_pp_func, expr_time);\n\n  const DataVector updated_deriv{3, 1.0};\n\n  // Construct mock system and set to Testing phase\n  ActionTesting::MockRuntimeSystem<TestingMetavariables> runsys{\n      {},\n      {std::move(f_of_t_map), std::move(measurement_timescales),\n       std::unordered_map<std::string, std::string>{},\n       std::unordered_map<std::string, bool>{}}};\n  ActionTesting::emplace_singleton_component_and_initialize<\n      TestSingleton<TestingMetavariables>>(make_not_null(&runsys),\n                                           ActionTesting::NodeId{0},\n                                           ActionTesting::LocalCoreId{0}, {});\n  ActionTesting::set_phase(make_not_null(&runsys), Parallel::Phase::Testing);\n\n  // Update functions of time in global cache with new deriv\n  auto& cache =\n      ActionTesting::cache<TestSingleton<TestingMetavariables>>(runsys, 0_st);\n\n  for (auto& name : {pp_name, quatfot_name}) {\n    Parallel::mutate<domain::Tags::FunctionsOfTime,\n                     control_system::UpdateSingleFunctionOfTime>(\n        cache, name, update_time, updated_deriv, expiration_time);\n  }\n\n  // Update expected function of time\n  expected_pp.update(update_time, updated_deriv, expiration_time);\n  expected_quatfot.update(update_time, updated_deriv, expiration_time);\n\n  // Check that the FunctionsOfTime are what we expected\n  const auto& cache_f_of_t_map = get<domain::Tags::FunctionsOfTime>(cache);\n  {\n    const auto& cache_pp = dynamic_cast<\n        const domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>&>(\n        *(cache_f_of_t_map.at(pp_name)));\n    const auto& cache_quatfot = dynamic_cast<\n        const domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>&>(\n        *(cache_f_of_t_map.at(quatfot_name)));\n\n    CHECK(cache_pp == expected_pp);\n    CHECK(cache_quatfot == expected_quatfot);\n  }\n\n  // Check updating both at the same time\n  update_time = expiration_time;\n  expiration_time += 1.0;\n  std::unordered_map<std::string, std::pair<DataVector, double>> update_args{};\n  for (const auto& name : {pp_name, quatfot_name}) {\n    update_args[name] = std::make_pair(updated_deriv, expiration_time);\n  }\n\n  Parallel::mutate<domain::Tags::FunctionsOfTime,\n                   control_system::UpdateMultipleFunctionsOfTime>(\n      cache, update_time, std::move(update_args));\n\n  expected_pp.update(update_time, updated_deriv, expiration_time);\n  expected_quatfot.update(update_time, updated_deriv, expiration_time);\n\n  {\n    const auto& cache_pp = dynamic_cast<\n        const domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>&>(\n        *(cache_f_of_t_map.at(pp_name)));\n    const auto& cache_quatfot = dynamic_cast<\n        const domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>&>(\n        *(cache_f_of_t_map.at(quatfot_name)));\n\n    CHECK(cache_pp == expected_pp);\n    CHECK(cache_quatfot == expected_quatfot);\n  }\n}\n\nvoid test_update_aggregator() {\n  // Notice the combined name has an extra state that isn't active\n  const std::string combined_name{\n      \"AlaskaCaliforniaFloridaIllinoisTexasWisconsin\"};\n  std::unordered_set<std::string> names{\"Illinois\", \"California\", \"Wisconsin\",\n                                        \"Florida\", \"Texas\"};\n  std::unordered_map<std::string, DataVector> signals{};\n  signals[\"Wisconsin\"] = DataVector{5.0};\n  signals[\"Texas\"] = DataVector{4.0};\n  signals[\"Illinois\"] = DataVector{3.0};\n  signals[\"Florida\"] = DataVector{2.0};\n  signals[\"California\"] = DataVector{1.0};\n  std::unordered_map<std::string, bool> active_map{\n      {\"Alaska\", false},  {\"California\", true}, {\"Florida\", true},\n      {\"Illinois\", true}, {\"Texas\", true},      {\"Wisconsin\", true}};\n\n  control_system::UpdateAggregator unserialized_aggregator{combined_name,\n                                                           names};\n  control_system::UpdateAggregator aggregator =\n      serialize_and_deserialize(unserialized_aggregator);\n\n  CHECK(aggregator.combined_name() == combined_name);\n\n  aggregator.insert(\"Wisconsin\", DataVector{1.0}, 10.0, signals.at(\"Wisconsin\"),\n                    5.0);\n  CHECK_FALSE(aggregator.is_ready(active_map));\n  aggregator.insert(\"Texas\", DataVector{2.0}, 9.0, signals.at(\"Texas\"), 4.0);\n  CHECK_FALSE(aggregator.is_ready(active_map));\n  aggregator.insert(\"Illinois\", DataVector{3.0}, 8.0, signals.at(\"Illinois\"),\n                    3.0);\n  CHECK_FALSE(aggregator.is_ready(active_map));\n  aggregator.insert(\"Florida\", DataVector{4.0}, 7.0, signals.at(\"Florida\"),\n                    2.0);\n  CHECK_FALSE(aggregator.is_ready(active_map));\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      aggregator.insert(\"Florida\", DataVector{}, 1.0, DataVector{}, 1.0),\n      Catch::Matchers::ContainsSubstring(\"Already received expiration time \"\n                                         \"data for control system 'Florida'\"));\n  CHECK_THROWS_WITH(\n      aggregator.insert(\"Alaska\", DataVector{}, 1.0, DataVector{}, 1.0),\n      Catch::Matchers::ContainsSubstring(\"Received expiration time data for a \"\n                                         \"non-active control system 'Alaska'\"));\n#endif\n  active_map.at(\"California\") = false;\n  CHECK(aggregator.is_ready(active_map));\n  active_map.at(\"California\") = true;\n  CHECK_FALSE(aggregator.is_ready(active_map));\n  aggregator.insert(\"California\", DataVector{5.0}, 6.0,\n                    signals.at(\"California\"), 1.0);\n  CHECK(aggregator.is_ready(active_map));\n\n  const std::unordered_map<std::string, std::pair<DataVector, double>>\n      combined_fot = aggregator.combined_fot_expiration_times(active_map);\n  CHECK(aggregator.is_ready(active_map));\n  const std::pair<double, double> combined_measurements =\n      aggregator.combined_measurement_expiration_time(active_map);\n  CHECK_FALSE(aggregator.is_ready(active_map));\n\n  CHECK(combined_fot.size() == names.size());\n  for (const auto& name : names) {\n    CAPTURE(name);\n    CHECK(combined_fot.count(name) == 1);\n    CHECK(combined_fot.at(name) == std::make_pair(signals[name], 1.0));\n  }\n\n  CHECK(combined_measurements == std::make_pair(1.0, 6.0));\n}\n\nstruct AggregatorMetavariables {\n  using metavariables = AggregatorMetavariables;\n  using component_list = tmpl::list<TestSingleton<metavariables, 1>,\n                                    TestSingleton<metavariables, 2>,\n                                    TestSingleton<metavariables, 3>>;\n};\n\nstruct SetSystemToCombinedNames {\n  static void apply(\n      const gsl::not_null<std::unordered_map<std::string, std::string>*>\n          cache_system_to_combined_names,\n      const std::unordered_map<std::string, std::string>&\n          new_system_to_combined_names) {\n    *cache_system_to_combined_names = new_system_to_combined_names;\n  }\n};\n\nvoid test_aggregate_update_action() {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  using metavars = AggregatorMetavariables;\n  using component1 = TestSingleton<metavars, 1>;\n  using component2 = TestSingleton<metavars, 2>;\n  using component3 = TestSingleton<metavars, 3>;\n  const double t0 = 0.0;\n  const double old_fot_expiration13 = 4.0;\n  const double old_fot_expiration2 = 2.0;\n  const double old_measurement_expiration13 = old_fot_expiration13 - 0.5;\n  const double old_measurement_expiration2 = old_fot_expiration2 - 0.5;\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      f_of_t_map{};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      measurement_map{};\n\n  // We must set up the functions of time and measurement timescales in a\n  // consistent state. Here FoT1 and FoT3 use the same measurement timescale,\n  // and this have the same expiration time. FoT2 uses a different measurement\n  // timescale so it has it's own expiration\n  f_of_t_map[\"FoT1\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          t0, std::array{DataVector{1.0}}, old_fot_expiration13);\n  f_of_t_map[\"FoT2\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          t0, std::array{DataVector{1.0}}, old_fot_expiration2);\n  f_of_t_map[\"FoT3\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          t0, std::array{DataVector{1.0}}, old_fot_expiration13);\n\n  measurement_map[\"FoT1FoT3\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          t0, std::array{DataVector{4.0}}, old_measurement_expiration13);\n  measurement_map[\"FoT2\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          t0, std::array{DataVector{2.0}}, old_measurement_expiration2);\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      expected_f_of_t_map = clone_unique_ptrs(f_of_t_map);\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      expected_measurement_map = clone_unique_ptrs(measurement_map);\n\n  control_system::UpdateAggregator aggregator13{\"FoT1FoT3\", {\"FoT1\", \"FoT3\"}};\n  control_system::UpdateAggregator aggregator2{\"FoT2\", {\"FoT2\"}};\n  std::unordered_map<std::string, control_system::UpdateAggregator>\n      aggregators{};\n  aggregators[\"FoT1FoT3\"] = aggregator13;\n  aggregators[\"FoT2\"] = aggregator2;\n  auto aggregators_copy = aggregators;\n  const std::unordered_map<std::string, bool> active_map{\n      {\"FoT1\", true}, {\"FoT3\", true}, {\"FoT2\", true}};\n\n  std::unordered_map<std::string, std::string> system_to_combined_names{};\n  system_to_combined_names[\"FoT1\"] = \"FoT1FoT3\";\n  system_to_combined_names[\"FoT2\"] = \"FoT2\";\n  system_to_combined_names[\"FoT3\"] = \"FoT1FoT3\";\n  auto system_to_combined_names_copy = system_to_combined_names;\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {},\n      {std::move(f_of_t_map), std::move(measurement_map),\n       std::move(system_to_combined_names_copy), active_map}};\n  ActionTesting::emplace_singleton_component_and_initialize<component1>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, {std::move(aggregators_copy)});\n  ActionTesting::emplace_singleton_component_and_initialize<component2>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, {});\n  ActionTesting::emplace_singleton_component_and_initialize<component3>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, {});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& cache = ActionTesting::cache<component1>(runner, 0_st);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n  const auto& measurement_timescales =\n      Parallel::get<control_system::Tags::MeasurementTimescales>(cache);\n\n  using tag = control_system::Tags::UpdateAggregators;\n  const std::unordered_map<std::string, control_system::UpdateAggregator>&\n      box_aggregators_component1 =\n          ActionTesting::get_databox_tag<component1, tag>(runner, 0);\n  const std::unordered_map<std::string, control_system::UpdateAggregator>&\n      box_aggregators_component2 =\n          ActionTesting::get_databox_tag<component2, tag>(runner, 0);\n  const std::unordered_map<std::string, control_system::UpdateAggregator>&\n      box_aggregators_component3 =\n          ActionTesting::get_databox_tag<component3, tag>(runner, 0);\n\n  CHECK_FALSE(box_aggregators_component1.empty());\n  CHECK(box_aggregators_component2.empty());\n  CHECK(box_aggregators_component3.empty());\n\n  const DataVector new_measurement_timescale1{2.0};\n  const DataVector new_measurement_timescale2{3.0};\n  const DataVector new_measurement_timescale3{4.0};\n  const DataVector new_measurement_timescale13{\n      std::min(new_measurement_timescale1[0], new_measurement_timescale3[0])};\n  const double new_fot_expiration1 = old_fot_expiration13 + 2.0;\n  const double new_fot_expiration2 = old_fot_expiration2 + 1.0;\n  const double new_fot_expiration3 = old_fot_expiration13 + 1.5;\n  const double new_fot_expiration13 =\n      std::min(new_fot_expiration1, new_fot_expiration3);\n  const DataVector control_signal{1.0};\n  const double new_measurement_expiration1 = new_fot_expiration1 - 0.5;\n  const double new_measurement_expiration2 = new_fot_expiration2 - 0.5;\n  const double new_measurement_expiration3 = new_fot_expiration3 - 0.5;\n  const double new_measurement_expiration13 =\n      std::min(new_measurement_expiration1, new_measurement_expiration3);\n\n#ifdef SPECTRE_DEBUG\n  {\n    auto& box =\n        ActionTesting::get_databox<component1>(make_not_null(&runner), 0);\n\n    // Set these to empty so we can trigger an ASSERT\n    Parallel::mutate<control_system::Tags::SystemToCombinedNames,\n                     SetSystemToCombinedNames>(\n        cache, std::unordered_map<std::string, std::string>{});\n\n    CHECK_THROWS_WITH(\n        (ActionTesting::simple_action<component1,\n                                      control_system::AggregateUpdate<System1>>(\n            make_not_null(&runner), 0_st, new_measurement_timescale1,\n            old_measurement_expiration13, new_measurement_expiration1,\n            control_signal, old_fot_expiration13, new_fot_expiration1)),\n        Catch::Matchers::ContainsSubstring(\n            \"Expected name 'FoT1' to be in map of system-to-combined names, \"\n            \"but it wasn't.\"));\n\n    // Set these back\n    Parallel::mutate<control_system::Tags::SystemToCombinedNames,\n                     SetSystemToCombinedNames>(cache, system_to_combined_names);\n\n    // Now set the update aggregators to be empty to hit a different ASSERT\n    db::mutate<tag>(\n        [](const auto local_aggregators) { local_aggregators->clear(); },\n        make_not_null(&box));\n\n    CHECK_THROWS_WITH(\n        (ActionTesting::simple_action<component1,\n                                      control_system::AggregateUpdate<System1>>(\n            make_not_null(&runner), 0_st, new_measurement_timescale1,\n            old_measurement_expiration13, new_measurement_expiration1,\n            control_signal, old_fot_expiration13, new_fot_expiration1)),\n        Catch::Matchers::ContainsSubstring(\n            \"Expected combined name 'FoT1FoT3' to be in map of aggregators, \"\n            \"but it wasn't.\"));\n\n    // Set them back so we can do the rest of the test\n    db::mutate<tag>(\n        [&aggregators](const auto local_aggregators) {\n          *local_aggregators = aggregators;\n        },\n        make_not_null(&box));\n  }\n#endif\n\n  // Note that we have to get these references *after* we do the clear() and\n  // re-assignment above in the debug build checks otherwise we get a dangling\n  // reference.\n  const control_system::UpdateAggregator& box_aggregator13 =\n      box_aggregators_component1.at(\"FoT1FoT3\");\n  const control_system::UpdateAggregator& box_aggregator2 =\n      box_aggregators_component1.at(\"FoT2\");\n\n  // The aggregators will never \"be ready\" to use because when they actually are\n  // ready, they are reset immediately during the simple action\n  CHECK_FALSE(box_aggregator13.is_ready(active_map));\n  CHECK_FALSE(box_aggregator2.is_ready(active_map));\n  CHECK(box_aggregators_component2.empty());\n  CHECK(box_aggregators_component3.empty());\n\n  // Update one of the two functions of time for the 13 measurement\n  ActionTesting::simple_action<component1,\n                               control_system::AggregateUpdate<System1>>(\n      make_not_null(&runner), 0_st, new_measurement_timescale1,\n      old_measurement_expiration13, new_measurement_expiration1, control_signal,\n      old_fot_expiration13, new_fot_expiration1);\n\n  CHECK_FALSE(box_aggregator13.is_ready(active_map));\n  CHECK_FALSE(box_aggregator2.is_ready(active_map));\n  CHECK(box_aggregators_component2.empty());\n  CHECK(box_aggregators_component3.empty());\n\n  // Update the 2 measurement and check that the function of time and\n  // measurement timescale have been updated appropriately\n  ActionTesting::simple_action<component1,\n                               control_system::AggregateUpdate<System2>>(\n      make_not_null(&runner), 0_st, new_measurement_timescale2,\n      old_measurement_expiration2, new_measurement_expiration2, control_signal,\n      old_fot_expiration2, new_fot_expiration2);\n\n  CHECK_FALSE(box_aggregator13.is_ready(active_map));\n  CHECK_FALSE(box_aggregator2.is_ready(active_map));\n  CHECK(box_aggregators_component2.empty());\n  CHECK(box_aggregators_component3.empty());\n\n  expected_f_of_t_map[\"FoT2\"]->update(old_fot_expiration2, control_signal,\n                                      new_fot_expiration2);\n  expected_measurement_map[\"FoT2\"]->update(old_measurement_expiration2,\n                                           new_measurement_timescale2,\n                                           new_measurement_expiration2);\n\n  const auto check_equal =\n      [](const std::string& name,\n         const std::unordered_map<\n             std::string,\n             std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>& test,\n         const std::unordered_map<\n             std::string,\n             std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n             expected) {\n        CAPTURE(name);\n        CHECK(dynamic_cast<\n                  const domain::FunctionsOfTime::PiecewisePolynomial<0>&>(\n                  *test.at(name)) ==\n              dynamic_cast<\n                  const domain::FunctionsOfTime::PiecewisePolynomial<0>&>(\n                  *expected.at(name)));\n      };\n\n  check_equal(\"FoT2\", functions_of_time, expected_f_of_t_map);\n  check_equal(\"FoT2\", measurement_timescales, expected_measurement_map);\n\n  // Update the second of the 13 measurement which should update both functions\n  // of time and just the one measurement timescale\n  ActionTesting::simple_action<component1,\n                               control_system::AggregateUpdate<System3>>(\n      make_not_null(&runner), 0_st, new_measurement_timescale3,\n      old_measurement_expiration13, new_measurement_expiration3, control_signal,\n      old_fot_expiration13, new_fot_expiration3);\n\n  CHECK_FALSE(box_aggregator13.is_ready(active_map));\n  CHECK_FALSE(box_aggregator2.is_ready(active_map));\n  CHECK(box_aggregators_component2.empty());\n  CHECK(box_aggregators_component3.empty());\n\n  expected_f_of_t_map[\"FoT1\"]->update(old_fot_expiration13, control_signal,\n                                      new_fot_expiration13);\n  expected_f_of_t_map[\"FoT3\"]->update(old_fot_expiration13, control_signal,\n                                      new_fot_expiration13);\n  expected_measurement_map[\"FoT1FoT3\"]->update(old_measurement_expiration13,\n                                               new_measurement_timescale13,\n                                               new_measurement_expiration13);\n\n  check_equal(\"FoT1\", functions_of_time, expected_f_of_t_map);\n  check_equal(\"FoT3\", functions_of_time, expected_f_of_t_map);\n  check_equal(\"FoT1FoT3\", measurement_timescales, expected_measurement_map);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.UpdateFunctionOfTime\",\n                  \"[Unit][ControlSystem]\") {\n  test_mutates();\n  test_update_aggregator();\n  test_aggregate_update_action();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ControlSystem/Test_WriteData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <random>\n#include <string>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/WriteData.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/ControlSystem/TestStructs.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system {\nnamespace {\nstruct LabelA {};\n\nconstexpr size_t total_components = 3;\n\nstruct FakeControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static constexpr size_t deriv_order = 2;\n  static std::string name() {\n    return pretty_type::short_name<FakeControlSystem>();\n  }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return i == 0 ? std::optional<std::string>{\"Foo\"}\n                  : (i == 1 ? std::optional<std::string>{\"Bar\"} : std::nullopt);\n  }\n  using measurement = control_system::TestHelpers::Measurement<LabelA>;\n  using simple_tags = tmpl::list<>;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstruct FakeQuatControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static constexpr size_t deriv_order = 3;\n  static std::string name() {\n    return pretty_type::short_name<FakeQuatControlSystem>();\n  }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return get_output(i);\n  }\n  using measurement = control_system::TestHelpers::Measurement<LabelA>;\n  using simple_tags = tmpl::list<>;\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\ntemplate <typename Metavariables, typename ControlSystem>\nstruct MockControlComponent {\n  using component_being_mocked = ControlComponent<Metavariables, ControlSystem>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using array_index = int;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct TestMetavars {\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  using component_list =\n      tmpl::list<::TestHelpers::observers::MockObserverWriter<TestMetavars>,\n                 MockControlComponent<TestMetavars, FakeControlSystem>,\n                 MockControlComponent<TestMetavars, FakeQuatControlSystem>>;\n};\n\nusing FoTPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n\ntemplate <typename ControlSystem, typename Metavariables>\nvoid check_written_data(\n    const ActionTesting::MockRuntimeSystem<Metavariables>& runner,\n    const std::vector<double>& times, FoTPtr& fot,\n    const std::vector<std::array<DataVector, 2>>& q_and_derivs,\n    const std::vector<DataVector>& timescales) {\n  std::array<DataVector, 3> func_and_2_derivs{};\n  // This has to be the same as in control_system::write_components_to_disk\n  const std::vector<std::string> compare_legend{\n      \"Time\",         \"FunctionOfTime\", \"dtFunctionOfTime\", \"d2tFunctionOfTime\",\n      \"ControlError\", \"dtControlError\", \"DampingTimescale\"};\n\n  auto& read_file = ActionTesting::get_databox_tag<\n      ::TestHelpers::observers::MockObserverWriter<Metavariables>,\n      ::TestHelpers::observers::MockReductionFileTag>(runner, 0);\n  for (size_t component_num = 0; component_num < total_components;\n       component_num++) {\n    // per file checks\n    const auto component_name_opt =\n        ControlSystem::component_name(component_num, total_components);\n    if constexpr (std::is_same_v<ControlSystem, FakeControlSystem>) {\n      if (component_num == 2) {\n        CHECK_FALSE(component_name_opt);\n        continue;\n      }\n    }\n    const auto& dataset = read_file.get_dat(\n        \"/ControlSystems/\" + ControlSystem::name() + \"/\" + *component_name_opt);\n    const Matrix& data = dataset.get_data();\n    const std::vector<std::string>& legend = dataset.get_legend();\n    // Check legend is correct\n    for (size_t i = 0; i < legend.size(); i++) {\n      CHECK(legend[i] == compare_legend[i]);\n    }\n    CHECK(data.rows() == times.size());\n\n    // per time per file checks\n    for (size_t time_num = 0; time_num < times.size(); time_num++) {\n      const double time = times[time_num];\n      const auto* const quat_func_of_time =\n          dynamic_cast<const domain::FunctionsOfTime::QuaternionFunctionOfTime<\n              ControlSystem::deriv_order>*>(fot.get());\n      if (quat_func_of_time == nullptr) {\n        func_and_2_derivs = fot->func_and_2_derivs(time);\n      } else {\n        func_and_2_derivs = quat_func_of_time->angle_func_and_2_derivs(time);\n      }\n\n      // check time is correct\n      size_t offset = 0;\n      CHECK(data(time_num, offset) == time);\n      ++offset;\n\n      // check values for lambda are correct\n      for (size_t deriv_num = 0; deriv_num < 3; deriv_num++) {\n        CHECK(data(time_num, offset) ==\n              func_and_2_derivs[deriv_num][component_num]);\n        ++offset;\n      }\n\n      // check control error and derivs are correct\n      for (size_t deriv_num = 0; deriv_num < 2; deriv_num++) {\n        CHECK(data(time_num, offset) ==\n              q_and_derivs[time_num][deriv_num][component_num]);\n        ++offset;\n      }\n\n      // check timescales are correct\n      CHECK(data(time_num, offset) == timescales[time_num][component_num]);\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ControlSystem.WriteData\", \"[Unit][ControlSystem]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  constexpr size_t deriv_order = FakeControlSystem::deriv_order;\n  constexpr size_t quat_deriv_order = FakeQuatControlSystem::deriv_order;\n  using observer = ::TestHelpers::observers::MockObserverWriter<TestMetavars>;\n  using control_comp = MockControlComponent<TestMetavars, FakeControlSystem>;\n  using quat_control_comp =\n      MockControlComponent<TestMetavars, FakeQuatControlSystem>;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist{-1.0, 1.0};\n\n  // set up runner and stuff\n  ActionTesting::MockRuntimeSystem<TestMetavars> runner{{}};\n  runner.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_nodegroup_component_and_initialize<observer>(\n      make_not_null(&runner), {});\n  ActionTesting::emplace_singleton_component<control_comp>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0});\n  ActionTesting::emplace_singleton_component<quat_control_comp>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0});\n  auto& cache = ActionTesting::cache<observer>(runner, 0);\n\n  runner.set_phase(Parallel::Phase::Execute);\n\n  // set up data to write\n  FoTPtr normal_fot = std::make_unique<\n      domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>>(\n      0.0,\n      std::array<DataVector, deriv_order + 1>{\n          {make_with_random_values<DataVector>(\n               make_not_null(&gen), dist, DataVector{total_components, 0.0}),\n           make_with_random_values<DataVector>(\n               make_not_null(&gen), dist, DataVector{total_components, 0.0}),\n           make_with_random_values<DataVector>(\n               make_not_null(&gen), dist, DataVector{total_components, 0.0})}},\n      5.0);\n\n  FoTPtr quat_fot = std::make_unique<\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<quat_deriv_order>>(\n      0.0, std::array<DataVector, 1>{{{1.0, 0.0, 0.0, 0.0}}},\n      std::array<DataVector, quat_deriv_order + 1>{\n          {make_with_random_values<DataVector>(\n               make_not_null(&gen), dist, DataVector{total_components, 0.0}),\n           make_with_random_values<DataVector>(\n               make_not_null(&gen), dist, DataVector{total_components, 0.0}),\n           make_with_random_values<DataVector>(\n               make_not_null(&gen), dist, DataVector{total_components, 0.0}),\n           make_with_random_values<DataVector>(\n               make_not_null(&gen), dist, DataVector{total_components, 0.0})}},\n      5.0);\n\n  const std::vector<double> times{0.0, 0.1, 0.2, 0.3, 0.4, 0.5};\n  std::vector<std::array<DataVector, 2>> normal_q_and_derivs{times.size()};\n  std::vector<std::array<DataVector, 2>> quat_q_and_derivs{times.size()};\n  std::vector<DataVector> normal_timescales{times.size(),\n                                            DataVector{total_components}};\n  std::vector<DataVector> quat_timescales{times.size(),\n                                          DataVector{total_components}};\n\n  // write some data\n  for (size_t i = 0; i < times.size(); i++) {\n    const double time = times[i];\n    // Doesn't really matter if the timescales are negative. This is just\n    // testing printing\n    fill_with_random_values(make_not_null(&normal_timescales[i]),\n                            make_not_null(&gen), make_not_null(&dist));\n    fill_with_random_values(make_not_null(&quat_timescales[i]),\n                            make_not_null(&gen), make_not_null(&dist));\n    for (size_t j = 0; j < 2; j++) {\n      gsl::at(normal_q_and_derivs[i], j) = make_with_random_values<DataVector>(\n          make_not_null(&gen), dist, DataVector{total_components, 0.0});\n      gsl::at(quat_q_and_derivs[i], j) = make_with_random_values<DataVector>(\n          make_not_null(&gen), dist, DataVector{total_components, 0.0});\n    }\n\n    write_components_to_disk<FakeControlSystem>(\n        time, cache, normal_fot, normal_q_and_derivs[i], normal_timescales[i]);\n    write_components_to_disk<FakeQuatControlSystem>(\n        time, cache, quat_fot, quat_q_and_derivs[i], quat_timescales[i]);\n\n    // 3 for one control system, 2 for the other (because of the nullopt)\n    size_t num_threaded_actions =\n        ActionTesting::number_of_queued_threaded_actions<observer>(runner, 0);\n    const size_t expected_num_threaded_actions = 2 * total_components - 1;\n    CHECK(num_threaded_actions == expected_num_threaded_actions);\n    for (size_t j = 0; j < expected_num_threaded_actions; j++) {\n      ActionTesting::invoke_queued_threaded_action<observer>(\n          make_not_null(&runner), 0);\n    }\n\n    num_threaded_actions =\n        ActionTesting::number_of_queued_threaded_actions<observer>(runner, 0);\n    CHECK(not num_threaded_actions);\n  }\n\n  check_written_data<FakeControlSystem>(runner, times, normal_fot,\n                                        normal_q_and_derivs, normal_timescales);\n  check_written_data<FakeQuatControlSystem>(runner, times, quat_fot,\n                                            quat_q_and_derivs, quat_timescales);\n}\n}  // namespace\n}  // namespace control_system\n"
  },
  {
    "path": "tests/Unit/DataStructures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DataStructures\")\n\nset(LIBRARY_SOURCES\n  Test_ApplyMatrices.cpp\n  Test_BlazeInteroperability.cpp\n  Test_CachedTempBuffer.cpp\n  Test_ComplexDataVector.cpp\n  Test_ComplexDataVectorBinaryOperations.cpp\n  Test_ComplexDiagonalModalOperator.cpp\n  Test_ComplexModalVector.cpp\n  Test_ComplexModalVectorInhomogeneousOperations.cpp\n  Test_DataVector.cpp\n  Test_DataVectorBinaryOperations.cpp\n  Test_DiagonalModalOperator.cpp\n  Test_DynamicBuffer.cpp\n  Test_ExtractPoint.cpp\n  Test_FixedHashMap.cpp\n  Test_FloatingPointType.cpp\n  Test_IdPair.cpp\n  Test_Index.cpp\n  Test_IndexIterator.cpp\n  Test_LeviCivitaIterator.cpp\n  Test_LinkedMessageId.cpp\n  Test_LinkedMessageQueue.cpp\n  Test_MathWrapper.cpp\n  Test_ModalVector.cpp\n  Test_ModalVectorInhomogeneousOperations.cpp\n  Test_MoreComplexDiagonalModalOperatorMath.cpp\n  Test_MoreDiagonalModalOperatorMath.cpp\n  Test_NonZeroStaticSizeVector.cpp\n  Test_SimpleSparseMatrix.cpp\n  Test_SliceIterator.cpp\n  Test_SliceTensorToVariables.cpp\n  Test_SliceVariables.cpp\n  Test_SparseMatrixFiller.cpp\n  Test_SpinWeighted.cpp\n  Test_StaticDeque.cpp\n  Test_StripeIterator.cpp\n  Test_TaggedContainers.cpp\n  Test_TaggedTuple.cpp\n  Test_Tags.cpp\n  Test_TempBuffer.cpp\n  Test_Transpose.cpp\n  Test_Variables.cpp\n  Test_VectorImpl.cpp\n  Test_VectorImplTestHelper.cpp\n  )\n\n# The following test is disabled for nvcc because nvcc can't handle it\n# (it fails to compile templates, can probably be fixed).\nif(NOT ${KOKKOS_CXX_COMPILER_ID} STREQUAL \"NVIDIA\")\n  list(APPEND LIBRARY_SOURCES Test_TaggedVariant.cpp)\nendif()\n\nadd_subdirectory(DataBox)\nadd_subdirectory(Tensor)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  Domain\n  Options\n  Utilities\n  )\n\n# [example_add_pybindings_test]\nspectre_add_python_bindings_test(\n  \"Unit.DataStructures.Python.DataVector\"\n  Test_DataVector.py\n  \"Unit;DataStructures;Python\"\n  PyDataStructures)\n# [example_add_pybindings_test]\nspectre_add_python_bindings_test(\n  \"Unit.DataStructures.Python.Matrix\"\n  Test_Matrix.py\n  \"Unit;DataStructures;Python\"\n  PyDataStructures)\nspectre_add_python_bindings_test(\n  \"Unit.DataStructures.Python.ModalVector\"\n  Test_ModalVector.py\n  \"Unit;DataStructures;Python\"\n  PyDataStructures)\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DataBox\")\n\nset(LIBRARY_SOURCES\n  Test_DataBox.cpp\n  Test_DataBoxDocumentation.cpp\n  Test_DataBoxPrefixes.cpp\n  Test_MetavariablesTag.cpp\n  Test_ObservationBox.cpp\n  Test_PrefixHelpers.cpp\n  Test_Protocols.cpp\n  Test_TagName.cpp\n  Test_TagTraits.cpp\n  Test_TestHelpers.cpp\n  Test_ValidateSelection.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ErrorHandling\n  Options\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_DataBox.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cctype>\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <pup.h>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/DataOnSlice.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/SubitemTag.hpp\"\n#include \"DataStructures/DataBox/Subitems.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nstruct NoSuchType;\ntemplate <typename TagsList>\nclass Variables;\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n\nnamespace {\nvoid multiply_by_two(const gsl::not_null<double*> result, const double value) {\n  *result = 2.0 * value;\n}\n\nvoid append_word(const gsl::not_null<std::string*> result,\n                 const std::string& text, const double value) {\n  std::stringstream ss;\n  ss << value;\n  *result = text + ss.str();\n}\n\nnamespace test_databox_tags {\n// [databox_tag_example]\nstruct Tag0 : db::SimpleTag {\n  using type = double;\n};\n// [databox_tag_example]\nstruct Tag0FromOption : Tag0 {\n  using base = Tag0;\n};\nstruct Tag1 : db::SimpleTag {\n  using type = std::vector<double>;\n};\nstruct Tag2 : db::SimpleTag {\n  using type = std::string;\n};\nstruct Tag3 : db::SimpleTag {\n  using type = std::string;\n};\n\nstruct Tag4 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Tag4Compute : Tag4, db::ComputeTag {\n  using base = Tag4;\n  using return_type = double;\n  static constexpr auto function = multiply_by_two;\n  using argument_tags = tmpl::list<Tag0>;\n};\n\nstruct Tag5 : db::SimpleTag {\n  using type = std::string;\n};\n\nstruct Tag5Compute : Tag5, db::ComputeTag {\n  using base = Tag5;\n  using return_type = std::string;\n  static constexpr auto function = append_word;\n  using argument_tags = tmpl::list<Tag2, Tag4>;\n};\n\nstruct Tag6 : db::SimpleTag {\n  using type = std::string;\n};\n\nstruct Tag6Compute : Tag6, db::ComputeTag {\n  using base = Tag6;\n  using return_type = std::string;\n  static void function(gsl::not_null<std::string*> result,\n                       const std::string& s) {\n    *result = s;\n  }\n  using argument_tags = tmpl::list<Tag2>;\n};\n\n// [compute_item_tag_function]\nstruct Lambda0 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Lambda0Compute : Lambda0, db::ComputeTag {\n  using base = Lambda0;\n  using return_type = double;\n  static constexpr void function(const gsl::not_null<double*> result,\n                                 const double a) {\n    *result = 3.0 * a;\n  }\n  using argument_tags = tmpl::list<Tag0>;\n};\n// [compute_item_tag_function]\n\nstruct Lambda1 : db::SimpleTag {\n  using type = double;\n};\n\n// [compute_item_tag_no_tags]\nstruct Lambda1Compute : Lambda1, db::ComputeTag {\n  using base = Lambda1;\n  using return_type = double;\n  static constexpr void function(const gsl::not_null<double*> result) {\n    *result = 7.0;\n  }\n  using argument_tags = tmpl::list<>;\n};\n// [compute_item_tag_no_tags]\n\n// [databox_prefix_tag_example]\ntemplate <typename Tag>\nstruct TagPrefix : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n  static std::string name() { return \"TagPrefix(\" + db::tag_name<Tag>() + \")\"; }\n};\n// [databox_prefix_tag_example]\n\nstruct Pointer : db::SimpleTag {\n  using type = std::unique_ptr<int>;\n};\n\nstruct PointerToCounter : db::SimpleTag {\n  using type = std::unique_ptr<int>;\n};\n\nstruct PointerToCounterCompute : PointerToCounter, db::ComputeTag {\n  using base = PointerToCounter;\n  using return_type = std::unique_ptr<int>;\n  static void function(const gsl::not_null<return_type*> result, const int& p) {\n    *result = std::make_unique<int>(p + 1);\n  }\n  using argument_tags = tmpl::list<Pointer>;\n};\n\nstruct PointerToSum : db::SimpleTag {\n  using type = std::unique_ptr<int>;\n};\n\nstruct PointerToSumCompute : PointerToSum, db::ComputeTag {\n  using base = PointerToSum;\n  using return_type = std::unique_ptr<int>;\n  static void function(const gsl::not_null<return_type*> ret, const int& arg,\n                       const int& same_arg) {\n    *ret = std::make_unique<int>(arg + same_arg);\n  }\n  using argument_tags = tmpl::list<PointerToCounter, PointerToCounter>;\n};\n\nstruct Tag0Ref : db::SimpleTag {\n  using type = double;\n};\n\nstruct Tag0Reference : Tag0Ref, db::ReferenceTag {\n  using base = Tag0Ref;\n  using argument_tags = tmpl::list<Tag0>;\n  static const double& get(const double& tag0_value) { return tag0_value; }\n};\n}  // namespace test_databox_tags\n\nvoid test_databox() {\n  INFO(\"test databox\");\n  const auto create_original_box = []() {\n    // [create_databox]\n    auto original_box = db::create<\n        db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                          test_databox_tags::Tag1, test_databox_tags::Tag2>,\n        db::AddComputeTags<test_databox_tags::Tag4Compute,\n                           test_databox_tags::Tag5Compute,\n                           test_databox_tags::Lambda0Compute,\n                           test_databox_tags::Lambda1Compute>>(\n        3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s);\n    // [create_databox]\n    return original_box;\n  };\n  {\n    INFO(\"Default-construction\");\n    auto box = db::create<\n        db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                          test_databox_tags::Tag1, test_databox_tags::Tag2,\n                          test_databox_tags::Tag3>,\n        db::AddComputeTags<test_databox_tags::Tag4Compute>>();\n    CHECK(db::get<test_databox_tags::Tag0>(box) == 0.);\n    CHECK(db::get<test_databox_tags::Tag1>(box).empty());\n    CHECK(db::get<test_databox_tags::Tag2>(box).empty());\n    CHECK(db::get<test_databox_tags::Tag4>(db::as_access(box)) == 0.);\n    // Mutate box via db::Access\n    db::mutate<test_databox_tags::Tag0, test_databox_tags::Tag2>(\n        [](const gsl::not_null<double*> val,\n           const gsl::not_null<std::string*> str) {\n          *val = 1.5;\n          *str = \"My Sample String\";\n        },\n        make_not_null(&db::as_access(box)));\n    CHECK(db::get<test_databox_tags::Tag3>(db::as_access(box)).empty());\n    CHECK(db::get<test_databox_tags::Tag0>(db::as_access(box)) == 1.5);\n    CHECK(db::get<test_databox_tags::Tag2>(db::as_access(box)) ==\n          \"My Sample String\"s);\n    CHECK(db::get<test_databox_tags::Tag4>(db::as_access(box)) == 3.);\n\n    // Mutate box directly\n    db::mutate<test_databox_tags::Tag0, test_databox_tags::Tag2>(\n        [](const gsl::not_null<double*> val,\n           const gsl::not_null<std::string*> str) {\n          *val = 2.5;\n          *str = \"My Sample String 2\";\n        },\n        make_not_null(&box));\n    CHECK(db::get<test_databox_tags::Tag3>(box).empty());\n    CHECK(db::get<test_databox_tags::Tag0>(box) == 2.5);\n    CHECK(db::get<test_databox_tags::Tag2>(box) == \"My Sample String 2\"s);\n    CHECK(db::get<test_databox_tags::Tag4>(box) == 5.);\n  }\n  {\n    const auto original_box = create_original_box();\n    static_assert(\n        std::is_same<\n            decltype(original_box),\n            const db::DataBox<db::detail::expand_subitems<tmpl::append<\n                tmpl::list<test_databox_tags::Tag0FromOption,\n                           test_databox_tags::Tag1, test_databox_tags::Tag2>,\n                tmpl::list<test_databox_tags::Tag4Compute,\n                           test_databox_tags::Tag5Compute,\n                           test_databox_tags::Lambda0Compute,\n                           test_databox_tags::Lambda1Compute>>>>>::value,\n        \"Failed to create original_box\");\n\n    // [using_db_get]\n    const auto& tag0 = db::get<test_databox_tags::Tag0>(original_box);\n    // [using_db_get]\n    CHECK(tag0 == 3.14);\n    // Check retrieving chained compute item result\n    CHECK(db::get<test_databox_tags::Tag5>(original_box) ==\n          \"My Sample String6.28\"s);\n    CHECK(db::get<test_databox_tags::Lambda0>(original_box) == 3.0 * 3.14);\n    CHECK(db::get<test_databox_tags::Lambda1>(original_box) == 7.0);\n  }\n  {\n    const auto box = db::create<\n        db::AddSimpleTags<test_databox_tags::Pointer>,\n        db::AddComputeTags<test_databox_tags::PointerToCounterCompute,\n                           test_databox_tags::PointerToSumCompute>>(\n        std::make_unique<int>(3));\n    using DbTags = decltype(box)::tags_list;\n    static_assert(\n        std::is_same_v<db::const_item_type<test_databox_tags::Pointer, DbTags>,\n                       const int&>,\n        \"Wrong type for const_item_type on unique_ptr simple item\");\n    static_assert(\n        std::is_same_v<decltype(db::get<test_databox_tags::Pointer>(box)),\n                       db::const_item_type<test_databox_tags::Pointer, DbTags>>,\n        \"Wrong type for get on unique_ptr simple item\");\n    CHECK(db::get<test_databox_tags::Pointer>(box) == 3);\n    CHECK(db::get<test_databox_tags::Pointer>(db::as_access(box)) == 3);\n\n    static_assert(\n        std::is_same_v<db::const_item_type<test_databox_tags::Pointer, DbTags>,\n                       const int&>,\n        \"Wrong type for const_item_type on unique_ptr simple item by base\");\n    static_assert(\n        std::is_same_v<decltype(db::get<test_databox_tags::Pointer>(box)),\n                       db::const_item_type<test_databox_tags::Pointer, DbTags>>,\n        \"Wrong type for get on unique_ptr simple item by base\");\n    CHECK(db::get<test_databox_tags::Pointer>(box) == 3);\n\n    static_assert(\n        std::is_same_v<\n            db::const_item_type<test_databox_tags::PointerToCounter, DbTags>,\n            const int&>,\n        \"Wrong type for const_item_type on unique_ptr compute item\");\n    static_assert(\n        std::is_same_v<\n            decltype(db::get<test_databox_tags::PointerToCounter>(box)),\n            db::const_item_type<test_databox_tags::PointerToCounter, DbTags>>,\n        \"Wrong type for get on unique_ptr compute item\");\n    CHECK(db::get<test_databox_tags::PointerToCounter>(box) == 4);\n    CHECK(db::get<test_databox_tags::PointerToCounter>(db::as_access(box)) ==\n          4);\n\n    static_assert(\n        std::is_same_v<\n            db::const_item_type<test_databox_tags::PointerToCounter, DbTags>,\n            const int&>,\n        \"Wrong type for const_item_type on unique_ptr compute item by base\");\n    static_assert(\n        std::is_same_v<\n            decltype(db::get<test_databox_tags::PointerToCounter>(box)),\n            db::const_item_type<test_databox_tags::PointerToCounter, DbTags>>,\n        \"Wrong type for get on unique_ptr compute item by base\");\n    CHECK(db::get<test_databox_tags::PointerToCounter>(box) == 4);\n\n    static_assert(\n        std::is_same_v<\n            db::const_item_type<test_databox_tags::PointerToSum, DbTags>,\n            const int&>,\n        \"Wrong type for const_item_type on unique_ptr\");\n    static_assert(\n        std::is_same_v<\n            decltype(db::get<test_databox_tags::PointerToSum>(box)),\n            db::const_item_type<test_databox_tags::PointerToSum, DbTags>>,\n        \"Wrong type for get on unique_ptr\");\n    CHECK(db::get<test_databox_tags::PointerToSum>(box) == 8);\n    CHECK(db::get<test_databox_tags::PointerToSum>(db::as_access(box)) == 8);\n  }\n}\n\nnamespace ArgumentTypeTags {\nstruct NonCopyable : db::SimpleTag {\n  using type = ::NonCopyable;\n};\ntemplate <size_t N>\nstruct String : db::SimpleTag {\n  using type = std::string;\n};\n}  // namespace ArgumentTypeTags\n\nvoid test_create_argument_types() {\n  INFO(\"test create argument types\");\n  std::string mutable_string = \"mutable\";\n  const std::string const_string = \"const\";\n  std::string move_string = \"move\";\n  const std::string const_move_string = \"const move\";\n  // clang-tidy: std::move of a const variable\n  auto box = db::create<db::AddSimpleTags<\n      ArgumentTypeTags::NonCopyable, ArgumentTypeTags::String<0>,\n      ArgumentTypeTags::String<1>, ArgumentTypeTags::String<2>,\n      ArgumentTypeTags::String<3>>>(NonCopyable{}, mutable_string, const_string,\n                                    std::move(move_string),\n                                    std::move(const_move_string));  // NOLINT\n  CHECK(mutable_string == \"mutable\");\n  CHECK(const_string == \"const\");\n  CHECK(db::get<ArgumentTypeTags::String<0>>(box) == \"mutable\");\n  CHECK(db::get<ArgumentTypeTags::String<1>>(box) == \"const\");\n  CHECK(db::get<ArgumentTypeTags::String<2>>(box) == \"move\");\n  CHECK(db::get<ArgumentTypeTags::String<3>>(box) == \"const move\");\n\n  CHECK(db::get<ArgumentTypeTags::String<0>>(db::as_access(box)) == \"mutable\");\n  CHECK(db::get<ArgumentTypeTags::String<1>>(db::as_access(box)) == \"const\");\n  CHECK(db::get<ArgumentTypeTags::String<2>>(db::as_access(box)) == \"move\");\n  CHECK(db::get<ArgumentTypeTags::String<3>>(db::as_access(box)) ==\n        \"const move\");\n}\n\nvoid test_get_databox() {\n  INFO(\"test get databox\");\n  auto original_box = db::create<\n      db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                        test_databox_tags::Tag1, test_databox_tags::Tag2>,\n      db::AddComputeTags<test_databox_tags::Tag4Compute,\n                         test_databox_tags::Tag5Compute>>(\n      3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s);\n  CHECK(std::addressof(original_box) ==\n        std::addressof(db::get<Tags::DataBox>(original_box)));\n  // [databox_self_tag_example]\n  auto check_result_no_args = [](const decltype(original_box)& box) {\n    CHECK(db::get<test_databox_tags::Tag2>(box) == \"My Sample String\"s);\n    CHECK(db::get<test_databox_tags::Tag5>(box) == \"My Sample String6.28\"s);\n  };\n  db::apply<tmpl::list<Tags::DataBox>>(check_result_no_args, original_box);\n  // [databox_self_tag_example]\n  struct CheckResultNoArgsApply {\n    static void apply(const decltype(original_box)& box) {\n      CHECK(db::get<test_databox_tags::Tag2>(box) == \"My Sample String\"s);\n      CHECK(db::get<test_databox_tags::Tag5>(box) == \"My Sample String6.28\"s);\n    }\n  };\n  db::apply<tmpl::list<Tags::DataBox>>(CheckResultNoArgsApply{}, original_box);\n}\n\nvoid trigger_get_databox_error() {\n  auto original_box = db::create<\n      db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                        test_databox_tags::Tag1, test_databox_tags::Tag2>,\n      db::AddComputeTags<test_databox_tags::Tag4Compute,\n                         test_databox_tags::Tag5Compute>>(\n      3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s);\n  CHECK(std::addressof(original_box) ==\n        std::addressof(db::get<Tags::DataBox>(original_box)));\n  db::mutate<test_databox_tags::Tag0>(\n      [&original_box](const gsl::not_null<double*> /*tag0*/) {\n        (void)db::get<Tags::DataBox>(original_box);\n      },\n      make_not_null(&original_box));\n}\n\nvoid test_get_databox_error() {\n  CHECK_THROWS_WITH(\n      trigger_get_databox_error(),\n      Catch::Matchers::ContainsSubstring(\n          \"Unable to retrieve a (compute) item 'DataBox' from the DataBox from \"\n          \"within a call to mutate. You must pass these either through the \"\n          \"capture list of the lambda or the constructor of a class, this \"\n          \"restriction exists to avoid complexity.\"));\n}\n\ntemplate <typename T>\nvoid test_mutate(T& original_box) {\n  CHECK(approx(db::get<test_databox_tags::Tag4>(original_box)) == 3.14 * 2.0);\n  // [databox_mutate_example]\n  db::mutate<test_databox_tags::Tag0, test_databox_tags::Tag1>(\n      [](const gsl::not_null<double*> tag0,\n         const gsl::not_null<std::vector<double>*> tag1,\n         const double compute_tag0) {\n        CHECK(6.28 == compute_tag0);\n        *tag0 = 10.32;\n        (*tag1)[0] = 837.2;\n      },\n      make_not_null(&original_box),\n      db::get<test_databox_tags::Tag4>(original_box));\n  CHECK(10.32 == db::get<test_databox_tags::Tag0>(original_box));\n  CHECK(837.2 == db::get<test_databox_tags::Tag1>(original_box)[0]);\n  // [databox_mutate_example]\n  CHECK(approx(db::get<test_databox_tags::Tag4>(original_box)) == 10.32 * 2.0);\n\n  auto result = db::mutate<test_databox_tags::Tag0>(\n      [](const gsl::not_null<double*> tag0, const double compute_tag0) {\n        return *tag0 * compute_tag0;\n      },\n      make_not_null(&original_box),\n      db::get<test_databox_tags::Tag4>(original_box));\n  CHECK(result == square(10.32) * 2.0);\n  auto pointer_to_result = db::mutate<test_databox_tags::Tag0>(\n      [&result](const gsl::not_null<double*> tag0, const double compute_tag0) {\n        *tag0 /= compute_tag0;\n        return &result;\n      },\n      make_not_null(&original_box),\n      db::get<test_databox_tags::Tag4>(original_box));\n  CHECK(db::get<test_databox_tags::Tag0>(original_box) == 0.5);\n  CHECK(pointer_to_result == &result);\n\n  db::mutate<test_databox_tags::Pointer>(\n      [](auto p) {\n        static_assert(std::is_same_v<typename test_databox_tags::Pointer::type,\n                                     std::unique_ptr<int>>,\n                      \"Wrong type for item_type on unique_ptr\");\n        static_assert(\n            std::is_same_v<\n                decltype(p),\n                gsl::not_null<typename test_databox_tags::Pointer::type*>>,\n            \"Wrong type for mutate on unique_ptr\");\n        CHECK(**p == 3);\n        *p = std::make_unique<int>(5);\n      },\n      make_not_null(&original_box));\n  CHECK(db::get<test_databox_tags::Pointer>(original_box) == 5);\n}\n\nvoid test_mutate() {\n  INFO(\"test mutate\");\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                          test_databox_tags::Tag1, test_databox_tags::Tag2,\n                          test_databox_tags::Pointer>,\n        db::AddComputeTags<test_databox_tags::Tag4Compute,\n                           test_databox_tags::Tag5Compute>>(\n        3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s,\n        std::make_unique<int>(3));\n  };\n  {\n    INFO(\"Using DataBox\");\n    auto box = create_box();\n    test_mutate(box);\n  }\n  {\n    INFO(\"Using db::Access\");\n    auto box = create_box();\n    test_mutate(db::as_access(box));\n  }\n}\n\nvoid trigger_mutate_locked_get() {\n  auto original_box = db::create<\n      db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                        test_databox_tags::Tag1, test_databox_tags::Tag2>,\n      db::AddComputeTags<test_databox_tags::Tag4Compute,\n                         test_databox_tags::Tag5Compute>>(\n      3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s);\n  db::mutate<test_databox_tags::Tag0, test_databox_tags::Tag1>(\n      [&original_box](const gsl::not_null<double*> tag0,\n                      const gsl::not_null<std::vector<double>*> tag1) {\n        db::get<test_databox_tags::Tag4>(original_box);\n        *tag0 = 10.32;\n        (*tag1)[0] = 837.2;\n      },\n      make_not_null(&original_box));\n}\n\nvoid trigger_mutate_locked_mutate() {\n  auto original_box = db::create<\n      db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                        test_databox_tags::Tag1, test_databox_tags::Tag2>,\n      db::AddComputeTags<test_databox_tags::Tag4Compute,\n                         test_databox_tags::Tag5Compute>>(\n      3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s);\n  db::mutate<test_databox_tags::Tag0>(\n      [&original_box](const gsl::not_null<double*> /*unused*/) {\n        db::mutate<test_databox_tags::Tag1>(\n            [](const gsl::not_null<std::vector<double>*> tag1) {\n              (*tag1)[0] = 10.0;\n            },\n            make_not_null(&original_box));\n      }, make_not_null(&original_box));\n}\n\nvoid test_mutate_locked() {\n  CHECK_THROWS_WITH(\n      trigger_mutate_locked_get(),\n      Catch::Matchers::ContainsSubstring(\n          \"Unable to retrieve a (compute) item 'Tag4' from the DataBox from \"\n          \"within a call to mutate. You must pass these either through the \"\n          \"capture list of the lambda or the constructor of a class, this \"\n          \"restriction exists to avoid complexity\"));\n  CHECK_THROWS_WITH(\n      trigger_mutate_locked_mutate(),\n      Catch::Matchers::ContainsSubstring(\n          \"Unable to mutate a DataBox that is already being \"\n          \"mutated. This error occurs when mutating a DataBox from \"\n          \"inside the invokable passed to the mutate function\"));\n}\n\nvoid test_mutate_box() {\n  INFO(\"test mutate entire box\");\n  auto box =\n      db::create<db::AddSimpleTags<test_databox_tags::Tag0FromOption>>(3.14);\n  db::mutate<Tags::DataBox>(\n      [&box](const gsl::not_null<decltype(box)*> mutate_box) {\n        CHECK(&box == &*mutate_box);\n        // Verify that the box is not locked\n        db::mutate<test_databox_tags::Tag0>(\n            [](const gsl::not_null<double*> x) { CHECK(*x == 3.14); },\n            mutate_box);\n      },\n      make_not_null(&box));\n}\n\nstruct NonCopyableFunctor {\n  NonCopyableFunctor() = default;\n  NonCopyableFunctor(const NonCopyableFunctor&) = delete;\n  NonCopyableFunctor(NonCopyableFunctor&&) = delete;\n  NonCopyableFunctor& operator=(const NonCopyableFunctor&) = delete;\n  NonCopyableFunctor& operator=(NonCopyableFunctor&&) = delete;\n  ~NonCopyableFunctor() = default;\n\n  // The && before the function body requires the object to be an\n  // rvalue for the method to be called.  This checks that the apply\n  // functions correctly preserve the value category of the functor.\n  template <typename... Args>\n  void operator()(Args&&... /*unused*/) && {}\n};\n\nvoid test_apply() {\n  INFO(\"test apply\");\n  auto original_box =\n      db::create<db::AddSimpleTags<\n                     test_databox_tags::Tag0FromOption, test_databox_tags::Tag1,\n                     test_databox_tags::Tag2, test_databox_tags::Pointer>,\n                 db::AddComputeTags<test_databox_tags::Tag4Compute,\n                                    test_databox_tags::Tag5Compute,\n                                    test_databox_tags::PointerToCounterCompute,\n                                    test_databox_tags::PointerToSumCompute>>(\n          3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s,\n          std::make_unique<int>(3));\n  auto check_result_no_args = [](const std::string& sample_string,\n                                 const auto& computed_string) {\n    CHECK(sample_string == \"My Sample String\"s);\n    CHECK(computed_string == \"My Sample String6.28\"s);\n  };\n  db::apply<tmpl::list<test_databox_tags::Tag2, test_databox_tags::Tag5>>(\n      check_result_no_args, original_box);\n  db::apply<tmpl::list<test_databox_tags::Tag2, test_databox_tags::Tag5>>(\n      check_result_no_args, original_box);\n\n  // [apply_example]\n  auto check_result_args = [](const std::string& sample_string,\n                              const auto& computed_string, const auto& vector) {\n    CHECK(sample_string == \"My Sample String\"s);\n    CHECK(computed_string == \"My Sample String6.28\"s);\n    CHECK(vector == (std::vector<double>{8.7, 93.2, 84.7}));\n  };\n  db::apply<tmpl::list<test_databox_tags::Tag2, test_databox_tags::Tag5>>(\n      check_result_args, original_box,\n      db::get<test_databox_tags::Tag1>(original_box));\n  // [apply_example]\n\n  db::apply<tmpl::list<>>(NonCopyableFunctor{}, original_box);\n\n  // [apply_struct_example]\n  struct ApplyCallable {\n    static void apply(const std::string& sample_string,\n                      const std::string& computed_string,\n                      const std::vector<double>& vector) {\n      CHECK(sample_string == \"My Sample String\"s);\n      CHECK(computed_string == \"My Sample String6.28\"s);\n      CHECK(vector == (std::vector<double>{8.7, 93.2, 84.7}));\n    }\n  };\n  db::apply<tmpl::list<test_databox_tags::Tag2, test_databox_tags::Tag5>>(\n      ApplyCallable{}, original_box,\n      db::get<test_databox_tags::Tag1>(original_box));\n  // [apply_struct_example]\n  db::apply<tmpl::list<test_databox_tags::Tag2, test_databox_tags::Tag5>>(\n      ApplyCallable{}, original_box,\n      db::get<test_databox_tags::Tag1>(original_box));\n  // [apply_stateless_struct_example]\n  struct StatelessApplyCallable {\n    using argument_tags =\n        tmpl::list<test_databox_tags::Tag2, test_databox_tags::Tag5>;\n    static void apply(const std::string& sample_string,\n                      const std::string& computed_string,\n                      const std::vector<double>& vector) {\n      CHECK(sample_string == \"My Sample String\"s);\n      CHECK(computed_string == \"My Sample String6.28\"s);\n      CHECK(vector == (std::vector<double>{8.7, 93.2, 84.7}));\n    }\n  };\n  db::apply<StatelessApplyCallable>(\n      original_box, db::get<test_databox_tags::Tag1>(original_box));\n  // [apply_stateless_struct_example]\n  db::apply(StatelessApplyCallable{}, original_box,\n            db::get<test_databox_tags::Tag1>(original_box));\n\n  db::apply<tmpl::list<test_databox_tags::Pointer,\n                       test_databox_tags::PointerToCounter,\n                       test_databox_tags::PointerToSum>>(\n      [](const int& simple, const int& compute, const int& compute_mutating) {\n        CHECK(simple == 3);\n        CHECK(compute == 4);\n        CHECK(compute_mutating == 8);\n      },\n      original_box);\n\n  struct PointerApplyCallable {\n    using argument_tags = tmpl::list<test_databox_tags::Pointer,\n                                     test_databox_tags::PointerToCounter,\n                                     test_databox_tags::PointerToSum>;\n    static void apply(const int& simple, const int& compute,\n                      const int& compute_mutating) {\n      CHECK(simple == 3);\n      CHECK(compute == 4);\n      CHECK(compute_mutating == 8);\n    }\n  };\n  db::apply<PointerApplyCallable>(original_box);\n\n  {\n    INFO(\"Test apply with optional reference argument\");\n    db::apply<tmpl::list<test_databox_tags::Tag1, test_databox_tags::Tag1>>(\n        [](const std::optional<\n               std::reference_wrapper<const std::vector<double>>>\n               optional_ref,\n           const auto& const_ref) {\n          REQUIRE(optional_ref.has_value());\n          CHECK(optional_ref->get() == const_ref);\n        },\n        original_box);\n  }\n}\n\nstruct Var1 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Grid>;\n};\n\nstruct Var1Compute : Var1, db::ComputeTag {\n  using base = Var1;\n  using return_type = tnsr::I<DataVector, 3, Frame::Grid>;\n  static void function(const gsl::not_null<return_type*> result) {\n    *result = tnsr::I<DataVector, 3, Frame::Grid>(5_st, 2.0);\n  }\n  using argument_tags = tmpl::list<>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <class Tag, class VolumeDim, class Frame>\nstruct PrefixTag0 : db::PrefixTag, db::SimpleTag {\n  using type = TensorMetafunctions::prepend_spatial_index<\n      typename Tag::type, VolumeDim::value, UpLo::Lo, Frame>;\n  using tag = Tag;\n};\n\nusing two_vars = tmpl::list<Var1, Var2>;\nusing vector_only = tmpl::list<Var1>;\nusing scalar_only = tmpl::list<Var2>;\n\nstatic_assert(\n    std::is_same_v<\n        tmpl::back<db::wrap_tags_in<PrefixTag0, scalar_only, tmpl::size_t<2>,\n                                    Frame::Grid>>::type,\n        tnsr::i<DataVector, 2, Frame::Grid>>,\n    \"Failed db::wrap_tags_in scalar_only\");\n\nstatic_assert(\n    std::is_same_v<\n        tmpl::back<db::wrap_tags_in<PrefixTag0, vector_only, tmpl::size_t<3>,\n                                    Frame::Grid>>::type,\n        tnsr::iJ<DataVector, 3, Frame::Grid>>,\n    \"Failed db::wrap_tags_in vector_only\");\n\nstatic_assert(\n    std::is_same_v<\n        tmpl::back<db::wrap_tags_in<PrefixTag0, two_vars, tmpl::size_t<2>,\n                                    Frame::Grid>>::type,\n        tnsr::i<DataVector, 2, Frame::Grid>>,\n    \"Failed db::wrap_tags_in two_vars scalar\");\n\nstatic_assert(\n    std::is_same_v<\n        tmpl::front<db::wrap_tags_in<PrefixTag0, two_vars, tmpl::size_t<3>,\n                                     Frame::Grid>>::type,\n        tnsr::iJ<DataVector, 3, Frame::Grid>>,\n    \"Failed db::wrap_tags_in two_vars vector\");\n\nnamespace test_databox_tags {\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\nstruct ScalarTag2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct VectorTag2 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\nstruct ScalarTag3 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct VectorTag3 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\nstruct ScalarTag4 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct VectorTag4 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3>;\n};\nstruct IncompleteType; // Forward declare, do not define on purpose.\nstruct TagForIncompleteType : db::SimpleTag {\n  using type = IncompleteType;\n};\n}  // namespace test_databox_tags\n\nnamespace {\nvoid multiply_scalar_by_two(\n    const gsl::not_null<Variables<tmpl::list<test_databox_tags::ScalarTag2,\n                                             test_databox_tags::VectorTag2>>*>\n        result,\n    const Scalar<DataVector>& scalar) {\n  *result = Variables<\n      tmpl::list<test_databox_tags::ScalarTag2, test_databox_tags::VectorTag2>>{\n      scalar.begin()->size(), 2.0};\n  get<test_databox_tags::ScalarTag2>(*result).get() = scalar.get() * 2.0;\n}\n\nvoid multiply_scalar_by_four(const gsl::not_null<Scalar<DataVector>*> result,\n                             const Scalar<DataVector>& scalar) {\n  *result = Scalar<DataVector>(scalar.get() * 4.0);\n}\n\nvoid multiply_scalar_by_three(const gsl::not_null<Scalar<DataVector>*> result,\n                              const Scalar<DataVector>& scalar) {\n  *result = Scalar<DataVector>(scalar.get() * 3.0);\n}\n\nvoid divide_scalar_by_three(const gsl::not_null<Scalar<DataVector>*> result,\n                            const Scalar<DataVector>& scalar) {\n  *result = Scalar<DataVector>(scalar.get() / 3.0);\n}\n\nvoid divide_scalar_by_two(\n    const gsl::not_null<Variables<tmpl::list<test_databox_tags::VectorTag3,\n                                             test_databox_tags::ScalarTag3>>*>\n        result,\n    const Scalar<DataVector>& scalar) {\n  *result = Variables<\n      tmpl::list<test_databox_tags::VectorTag3, test_databox_tags::ScalarTag3>>{\n      scalar.begin()->size(), 10.0};\n  get<test_databox_tags::ScalarTag3>(*result).get() = scalar.get() / 2.0;\n}\n\nvoid multiply_variables_by_two(\n    const gsl::not_null<Variables<tmpl::list<test_databox_tags::ScalarTag4,\n                                             test_databox_tags::VectorTag4>>*>\n        result,\n    const Variables<tmpl::list<test_databox_tags::ScalarTag,\n                               test_databox_tags::VectorTag>>& vars) {\n  *result = Variables<\n      tmpl::list<test_databox_tags::ScalarTag4, test_databox_tags::VectorTag4>>(\n      vars.number_of_grid_points(), 2.0);\n  get<test_databox_tags::ScalarTag4>(*result).get() *=\n      get<test_databox_tags::ScalarTag>(vars).get();\n  get<0>(get<test_databox_tags::VectorTag4>(*result)) *=\n      get<0>(get<test_databox_tags::VectorTag>(vars));\n  get<1>(get<test_databox_tags::VectorTag4>(*result)) *=\n      get<1>(get<test_databox_tags::VectorTag>(vars));\n  get<2>(get<test_databox_tags::VectorTag4>(*result)) *=\n      get<2>(get<test_databox_tags::VectorTag>(vars));\n}\n}  // namespace\n\nnamespace test_databox_tags {\nusing MultiplyScalarByTwo = ::Tags::Variables<\n    tmpl::list<test_databox_tags::ScalarTag2, test_databox_tags::VectorTag2>>;\n\nstruct MultiplyScalarByTwoCompute : MultiplyScalarByTwo, db::ComputeTag {\n  using base = MultiplyScalarByTwo;\n  using variables_tags =\n      tmpl::list<test_databox_tags::ScalarTag2, test_databox_tags::VectorTag2>;\n  using return_type = Variables<variables_tags>;\n  static constexpr auto function = multiply_scalar_by_two;\n  using argument_tags = tmpl::list<test_databox_tags::ScalarTag>;\n};\n\nstruct MultiplyScalarByFour : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct MultiplyScalarByFourCompute : MultiplyScalarByFour, db::ComputeTag {\n  using base = MultiplyScalarByFour;\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function = multiply_scalar_by_four;\n  using argument_tags = tmpl::list<test_databox_tags::ScalarTag2>;\n};\n\nstruct MultiplyScalarByThree : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct MultiplyScalarByThreeCompute : MultiplyScalarByThree, db::ComputeTag {\n  using base = MultiplyScalarByThree;\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function = multiply_scalar_by_three;\n  using argument_tags = tmpl::list<test_databox_tags::MultiplyScalarByFour>;\n};\n\nstruct DivideScalarByThree : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct DivideScalarByThreeCompute : DivideScalarByThree, db::ComputeTag {\n  using base = DivideScalarByThree;\n  using return_type = Scalar<DataVector>;\n  static constexpr auto function = divide_scalar_by_three;\n  using argument_tags = tmpl::list<test_databox_tags::MultiplyScalarByThree>;\n};\n\nusing DivideScalarByTwo = ::Tags::Variables<\n    tmpl::list<test_databox_tags::VectorTag3, test_databox_tags::ScalarTag3>>;\n\nstruct DivideScalarByTwoCompute : DivideScalarByTwo, db::ComputeTag {\n  using base = DivideScalarByTwo;\n  using return_type = Variables<\n      tmpl::list<test_databox_tags::VectorTag3, test_databox_tags::ScalarTag3>>;\n  static constexpr auto function = divide_scalar_by_two;\n  using argument_tags = tmpl::list<test_databox_tags::DivideScalarByThree>;\n};\n\nusing MultiplyVariablesByTwo = ::Tags::Variables<\n    tmpl::list<test_databox_tags::ScalarTag4, test_databox_tags::VectorTag4>>;\n\nstruct MultiplyVariablesByTwoCompute : MultiplyVariablesByTwo, db::ComputeTag {\n  using base = MultiplyVariablesByTwo;\n  using return_type = Variables<\n      tmpl::list<test_databox_tags::ScalarTag4, test_databox_tags::VectorTag4>>;\n  static constexpr auto function = multiply_variables_by_two;\n  using argument_tags = tmpl::list<Tags::Variables<\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>>;\n};\n}  // namespace test_databox_tags\n\ntemplate <typename T>\nvoid test_variables(T& box) {\n  using vars_tag = Tags::Variables<\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>;\n  const auto check_references_match = [&box]() {\n    const auto& vars_original = db::get<vars_tag>(box);\n    CHECK(get(db::get<test_databox_tags::ScalarTag>(box)).data() ==\n          get(get<test_databox_tags::ScalarTag>(vars_original)).data());\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(db::get<test_databox_tags::VectorTag>(box).get(i).data() ==\n            get<test_databox_tags::VectorTag>(vars_original).get(i).data());\n    }\n\n    const auto& vars_mul_scalar =\n        db::get<test_databox_tags::MultiplyScalarByTwo>(box);\n    CHECK(get(db::get<test_databox_tags::ScalarTag2>(box)).data() ==\n          get(get<test_databox_tags::ScalarTag2>(vars_mul_scalar)).data());\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(db::get<test_databox_tags::VectorTag2>(box).get(i).data() ==\n            get<test_databox_tags::VectorTag2>(vars_mul_scalar).get(i).data());\n    }\n\n    const auto& vars_div_scalar =\n        db::get<test_databox_tags::DivideScalarByTwo>(box);\n    CHECK(get(db::get<test_databox_tags::ScalarTag3>(box)).data() ==\n          get(get<test_databox_tags::ScalarTag3>(vars_div_scalar)).data());\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(db::get<test_databox_tags::VectorTag3>(box).get(i).data() ==\n            get<test_databox_tags::VectorTag3>(vars_div_scalar).get(i).data());\n    }\n\n    const auto& vars_mul_two =\n        db::get<test_databox_tags::MultiplyVariablesByTwo>(box);\n    CHECK(get(db::get<test_databox_tags::ScalarTag4>(box)).data() ==\n          get(get<test_databox_tags::ScalarTag4>(vars_mul_two)).data());\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(db::get<test_databox_tags::VectorTag4>(box).get(i).data() ==\n            get<test_databox_tags::VectorTag4>(vars_mul_two).get(i).data());\n    }\n  };\n\n  CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n        Scalar<DataVector>(DataVector(2, 3.)));\n  CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 3.))));\n  CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n        Scalar<DataVector>(DataVector(2, 6.)));\n  CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n  CHECK(db::get<test_databox_tags::MultiplyScalarByFour>(box) ==\n        Scalar<DataVector>(DataVector(2, 24.)));\n  CHECK(db::get<test_databox_tags::MultiplyScalarByThree>(box) ==\n        Scalar<DataVector>(DataVector(2, 72.)));\n  CHECK(db::get<test_databox_tags::DivideScalarByThree>(box) ==\n        Scalar<DataVector>(DataVector(2, 24.)));\n  CHECK(db::get<test_databox_tags::ScalarTag3>(box) ==\n        Scalar<DataVector>(DataVector(2, 12.)));\n  CHECK(db::get<test_databox_tags::VectorTag3>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 10.))));\n  CHECK(db::get<test_databox_tags::ScalarTag4>(box) ==\n        Scalar<DataVector>(DataVector(2, 6.)));\n  CHECK(db::get<test_databox_tags::VectorTag4>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  {\n    const auto& vars = db::get<test_databox_tags::MultiplyVariablesByTwo>(box);\n    CHECK(get<test_databox_tags::ScalarTag4>(vars) ==\n          Scalar<DataVector>(DataVector(2, 6.)));\n    CHECK(get<test_databox_tags::VectorTag4>(vars) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  }\n  check_references_match();\n\n  db::mutate<test_databox_tags::ScalarTag>(\n      [](const gsl::not_null<Scalar<DataVector>*> scalar) {\n        scalar->get() = 4.0;\n      },\n      make_not_null(&box));\n\n  CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n        Scalar<DataVector>(DataVector(2, 4.)));\n  CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 3.))));\n  CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n        Scalar<DataVector>(DataVector(2, 8.)));\n  CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n  CHECK(db::get<test_databox_tags::MultiplyScalarByFour>(box) ==\n        Scalar<DataVector>(DataVector(2, 32.)));\n  CHECK(db::get<test_databox_tags::MultiplyScalarByThree>(box) ==\n        Scalar<DataVector>(DataVector(2, 96.)));\n  CHECK(db::get<test_databox_tags::DivideScalarByThree>(box) ==\n        Scalar<DataVector>(DataVector(2, 32.)));\n  CHECK(db::get<test_databox_tags::ScalarTag3>(box) ==\n        Scalar<DataVector>(DataVector(2, 16.)));\n  CHECK(db::get<test_databox_tags::VectorTag3>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 10.))));\n  CHECK(db::get<test_databox_tags::ScalarTag4>(box) ==\n        Scalar<DataVector>(DataVector(2, 8.)));\n  CHECK(db::get<test_databox_tags::VectorTag4>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  {\n    const auto& vars = db::get<test_databox_tags::MultiplyVariablesByTwo>(box);\n    CHECK(get<test_databox_tags::ScalarTag4>(vars) ==\n          Scalar<DataVector>(DataVector(2, 8.)));\n    CHECK(get<test_databox_tags::VectorTag4>(vars) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  }\n  check_references_match();\n\n  db::mutate<Tags::Variables<\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>>(\n      [](const auto vars) {\n        get<test_databox_tags::ScalarTag>(*vars).get() = 6.0;\n      },\n      make_not_null(&box));\n\n  CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n        Scalar<DataVector>(DataVector(2, 6.)));\n  CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 3.))));\n  CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n        Scalar<DataVector>(DataVector(2, 12.)));\n  CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n  CHECK(db::get<test_databox_tags::MultiplyScalarByFour>(box) ==\n        Scalar<DataVector>(DataVector(2, 48.)));\n  CHECK(db::get<test_databox_tags::MultiplyScalarByThree>(box) ==\n        Scalar<DataVector>(DataVector(2, 144.)));\n  CHECK(db::get<test_databox_tags::DivideScalarByThree>(box) ==\n        Scalar<DataVector>(DataVector(2, 48.)));\n  CHECK(db::get<test_databox_tags::ScalarTag3>(box) ==\n        Scalar<DataVector>(DataVector(2, 24.)));\n  CHECK(db::get<test_databox_tags::VectorTag3>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 10.))));\n  CHECK(db::get<test_databox_tags::ScalarTag4>(box) ==\n        Scalar<DataVector>(DataVector(2, 12.)));\n  CHECK(db::get<test_databox_tags::VectorTag4>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  {\n    const auto& vars = db::get<test_databox_tags::MultiplyVariablesByTwo>(box);\n    CHECK(get<test_databox_tags::ScalarTag4>(vars) ==\n          Scalar<DataVector>(DataVector(2, 12.)));\n    CHECK(get<test_databox_tags::VectorTag4>(vars) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  }\n  check_references_match();\n\n  db::mutate<Tags::Variables<\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>>(\n      [](const auto vars) {\n        get<test_databox_tags::ScalarTag>(*vars).get() = 4.0;\n        get<test_databox_tags::VectorTag>(*vars) =\n            tnsr::I<DataVector, 3>(DataVector(2, 6.));\n      },\n      make_not_null(&box));\n\n  CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n        Scalar<DataVector>(DataVector(2, 4.)));\n  CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n        Scalar<DataVector>(DataVector(2, 8.)));\n  CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n  CHECK(db::get<test_databox_tags::MultiplyScalarByFour>(box) ==\n        Scalar<DataVector>(DataVector(2, 32.)));\n  CHECK(db::get<test_databox_tags::MultiplyScalarByThree>(box) ==\n        Scalar<DataVector>(DataVector(2, 96.)));\n  CHECK(db::get<test_databox_tags::DivideScalarByThree>(box) ==\n        Scalar<DataVector>(DataVector(2, 32.)));\n  CHECK(db::get<test_databox_tags::ScalarTag3>(box) ==\n        Scalar<DataVector>(DataVector(2, 16.)));\n  CHECK(db::get<test_databox_tags::VectorTag3>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 10.))));\n  CHECK(db::get<test_databox_tags::ScalarTag4>(box) ==\n        Scalar<DataVector>(DataVector(2, 8.)));\n  CHECK(db::get<test_databox_tags::VectorTag4>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 12.))));\n  {\n    const auto& vars = db::get<test_databox_tags::MultiplyVariablesByTwo>(box);\n    CHECK(get<test_databox_tags::ScalarTag4>(vars) ==\n          Scalar<DataVector>(DataVector(2, 8.)));\n    CHECK(get<test_databox_tags::VectorTag4>(vars) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 12.))));\n  }\n  check_references_match();\n}\n\nvoid test_variables() {\n  INFO(\"test variables\");\n  using vars_tag = Tags::Variables<\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>;\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<vars_tag>,\n        db::AddComputeTags<test_databox_tags::MultiplyScalarByTwoCompute,\n                           test_databox_tags::MultiplyScalarByFourCompute,\n                           test_databox_tags::MultiplyScalarByThreeCompute,\n                           test_databox_tags::DivideScalarByThreeCompute,\n                           test_databox_tags::DivideScalarByTwoCompute,\n                           test_databox_tags::MultiplyVariablesByTwoCompute>>(\n        Variables<tmpl::list<test_databox_tags::ScalarTag,\n                             test_databox_tags::VectorTag>>(2, 3.));\n  };\n  {\n    INFO(\"Using DataBox\");\n    auto box = create_box();\n    test_variables(box);\n  }\n  {\n    INFO(\"Using db::Access\");\n    auto box = create_box();\n    test_variables(db::as_access(box));\n  }\n}\n\nstruct Tag1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct Tag2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <typename T>\nvoid test_variables2(T& box) {\n  db::mutate<Tags::Variables<tmpl::list<Tag1, Tag2>>>(\n      [](const auto vars) { *vars = Variables<tmpl::list<Tag1, Tag2>>(1, 2.); },\n      make_not_null(&box));\n  CHECK(db::get<Tag1>(box) == Scalar<DataVector>(DataVector(1, 2.)));\n}\n\nvoid test_variables2() {\n  INFO(\"test variables2\");\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<Tags::Variables<tmpl::list<Tag1, Tag2>>>>(\n        Variables<tmpl::list<Tag1, Tag2>>(1, 1.));\n  };\n  {\n    INFO(\"Using DataBox\");\n    auto box = create_box();\n    test_variables2(box);\n  }\n  {\n    INFO(\"Using db::Access\");\n    auto box = create_box();\n    test_variables2(db::as_access(box));\n  }\n}\n\ntemplate <typename T>\nvoid test_reset_compute_items(T& box) {\n  CHECK(approx(db::get<test_databox_tags::Tag4>(box)) == 3.14 * 2.0);\n  CHECK(db::get<test_databox_tags::Tag5>(box) == \"My Sample String6.28\");\n  CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n        Scalar<DataVector>(DataVector(2, 3.)));\n  CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 3.))));\n  CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n        Scalar<DataVector>(DataVector(2, 6.)));\n  CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n  CHECK(db::get<test_databox_tags::ScalarTag4>(box) ==\n        Scalar<DataVector>(DataVector(2, 6.)));\n  CHECK(db::get<test_databox_tags::VectorTag4>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  {\n    const auto& vars = db::get<test_databox_tags::MultiplyVariablesByTwo>(box);\n    CHECK(get<test_databox_tags::ScalarTag4>(vars) ==\n          Scalar<DataVector>(DataVector(2, 6.)));\n    CHECK(get<test_databox_tags::VectorTag4>(vars) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 6.))));\n  }\n  // The compute item overwrites the variables, so we can detect a\n  // reset by seeing if the address changes\n  const auto* vars_address =\n      db::get<test_databox_tags::MultiplyVariablesByTwo>(box).data();\n\n  db::mutate<test_databox_tags::Tag2>(\n      [](const gsl::not_null<std::string*> tag2) { *tag2 = \"My New String\"; },\n      make_not_null(&box));\n  CHECK(db::get<test_databox_tags::Tag5>(box) == \"My New String6.28\");\n  CHECK(vars_address ==\n        db::get<test_databox_tags::MultiplyVariablesByTwo>(box).data());\n\n  db::mutate<test_databox_tags::Tag0, test_databox_tags::ScalarTag>(\n      [](const gsl::not_null<double*> tag0,\n         const gsl::not_null<Scalar<DataVector>*> scalar) {\n        *tag0 = 2.718;\n        get(*scalar) = 5.;\n      },\n      make_not_null(&box));\n  CHECK(db::get<test_databox_tags::Tag5>(box) == \"My New String5.436\");\n  CHECK(get<test_databox_tags::ScalarTag2>(box) ==\n        Scalar<DataVector>(DataVector(2, 10.)));\n  CHECK(get<test_databox_tags::ScalarTag4>(box) ==\n        Scalar<DataVector>(DataVector(2, 10.)));\n  CHECK(vars_address !=\n        db::get<test_databox_tags::MultiplyVariablesByTwo>(box).data());\n\n  db::mutate<Tags::Variables<\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>>(\n      [](const gsl::not_null<Variables<tmpl::list<\n             test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>*>\n             vars) { get(get<test_databox_tags::ScalarTag>(*vars)) = 1.; },\n      make_not_null(&box));\n  CHECK(get<test_databox_tags::ScalarTag2>(box) ==\n        Scalar<DataVector>(DataVector(2, 2.)));\n  CHECK(get<test_databox_tags::ScalarTag4>(box) ==\n        Scalar<DataVector>(DataVector(2, 2.)));\n\n  // Make sure this compiles.\n  db::mutate<>([]() {}, make_not_null(&box));\n}\n\nvoid test_reset_compute_items() {\n  INFO(\"test reset compute items\");\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<\n            test_databox_tags::Tag0FromOption, test_databox_tags::Tag1,\n            test_databox_tags::Tag2,\n            Tags::Variables<tmpl::list<test_databox_tags::ScalarTag,\n                                       test_databox_tags::VectorTag>>>,\n        db::AddComputeTags<test_databox_tags::Tag4Compute,\n                           test_databox_tags::Tag5Compute,\n                           test_databox_tags::MultiplyScalarByTwoCompute,\n                           test_databox_tags::MultiplyVariablesByTwoCompute>>(\n        3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s,\n        Variables<tmpl::list<test_databox_tags::ScalarTag,\n                             test_databox_tags::VectorTag>>(2, 3.));\n  };\n  {\n    INFO(\"DataBox\");\n    auto box = create_box();\n    test_reset_compute_items(box);\n  }\n  {\n    INFO(\"Access\");\n    auto box = create_box();\n    test_reset_compute_items(db::as_access(box));\n  }\n}\n\nnamespace ExtraResetTags {\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct Int : db::SimpleTag {\n  using type = int;\n};\nstruct CheckReset : db::SimpleTag {\n  using type = int;\n};\nstruct CheckResetCompute : CheckReset, db::ComputeTag {\n  using base = CheckReset;\n  using return_type = int;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool first_call;\n  static auto function(const gsl::not_null<int*> result,\n                       const ::Variables<tmpl::list<Var>>& /*unused*/) {\n    CHECK(first_call);\n    first_call = false;\n    *result = 0;\n  }\n  using argument_tags = tmpl::list<Tags::Variables<tmpl::list<Var>>>;\n};\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool CheckResetCompute::first_call = true;\n}  // namespace ExtraResetTags\n\ntemplate <typename T>\nvoid test_variables_extra_reset(T& box) {\n  CHECK(db::get<ExtraResetTags::CheckReset>(box) == 0);\n  db::mutate<ExtraResetTags::Int>([](const gsl::not_null<int*> /*unused*/) {},\n                                  make_not_null(&box));\n  CHECK(db::get<ExtraResetTags::CheckReset>(box) == 0);\n}\n\nvoid test_variables_extra_reset() {\n  INFO(\"test variables extra reset\");\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<ExtraResetTags::Int,\n                          Tags::Variables<tmpl::list<ExtraResetTags::Var>>>,\n        db::AddComputeTags<ExtraResetTags::CheckResetCompute>>(\n        1, Variables<tmpl::list<ExtraResetTags::Var>>(2, 3.));\n  };\n  {\n    ExtraResetTags::CheckResetCompute::first_call = true;\n    INFO(\"Using DataBox\");\n    auto box = create_box();\n    test_variables_extra_reset(box);\n  }\n  {\n    ExtraResetTags::CheckResetCompute::first_call = true;\n    INFO(\"Using db::Access\");\n    auto box = create_box();\n    test_variables_extra_reset(db::as_access(box));\n  }\n}\n\n// [mutate_apply_struct_definition_example]\nstruct TestDataboxMutateApply {\n  // delete copy semantics just to make sure it works. Not necessary in general.\n  TestDataboxMutateApply() = default;\n  TestDataboxMutateApply(const TestDataboxMutateApply&) = delete;\n  TestDataboxMutateApply& operator=(const TestDataboxMutateApply&) = delete;\n  TestDataboxMutateApply(TestDataboxMutateApply&&) = default;\n  TestDataboxMutateApply& operator=(TestDataboxMutateApply&&) = default;\n  ~TestDataboxMutateApply() = default;\n\n  // These typelists are used by the `db::mutate_apply` overload that does not\n  // require these lists as template arguments\n  using return_tags =\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>;\n  using argument_tags = tmpl::list<test_databox_tags::Tag2>;\n\n  static void apply(const gsl::not_null<Scalar<DataVector>*> scalar,\n                    const gsl::not_null<tnsr::I<DataVector, 3>*> vector,\n                    const std::string& tag2) {\n    scalar->get() *= 2.0;\n    get<0>(*vector) *= 3.0;\n    get<1>(*vector) *= 4.0;\n    get<2>(*vector) *= 5.0;\n    CHECK(tag2 == \"My Sample String\"s);\n  }\n};\n// [mutate_apply_struct_definition_example]\n\nstruct TestDataboxMutateApply2 {\n  using return_tags = tmpl::list<test_databox_tags::ScalarTag>;\n  using argument_tags = tmpl::list<test_databox_tags::Tag2>;\n\n  static void apply(const gsl::not_null<Scalar<DataVector>*> scalar,\n                    const std::string& tag2) {\n    CHECK(*scalar == Scalar<DataVector>(DataVector(2, 6.)));\n    CHECK(tag2 == \"My Sample String\"s);\n  }\n};\n\nstruct PointerMutateApply {\n  using return_tags = tmpl::list<test_databox_tags::Pointer>;\n  using argument_tags = tmpl::list<test_databox_tags::PointerToCounter,\n                                   test_databox_tags::PointerToSum>;\n  static void apply(const gsl::not_null<std::unique_ptr<int>*> ret,\n                    const int& compute, const int& compute_mutating) {\n    **ret = 7;\n    CHECK(compute == 7);\n    CHECK(compute_mutating == 14);\n  }\n};\n\nstruct PointerMutateApply2 {\n  using return_tags = tmpl::list<test_databox_tags::Pointer>;\n  using argument_tags = tmpl::list<test_databox_tags::PointerToCounter>;\n  static void apply(const gsl::not_null<std::unique_ptr<int>*> ret,\n                    const int& compute) {\n    **ret = 8;\n    CHECK(compute == 8);\n  }\n};\n\ntemplate <typename T>\nvoid test_mutate_apply(T& box) {\n  constexpr bool using_db_access = std::is_same_v<std::decay_t<T>, db::Access>;\n  CHECK(approx(db::get<test_databox_tags::Tag4>(box)) == 3.14 * 2.0);\n  CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n        Scalar<DataVector>(DataVector(2, 3.)));\n  CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 3.))));\n  CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n        Scalar<DataVector>(DataVector(2, 6.)));\n  CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n        (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n\n  {\n    INFO(\"Apply function or lambda\");\n    // [mutate_apply_struct_example_stateful]\n    db::mutate_apply(TestDataboxMutateApply{}, make_not_null(&box));\n    // [mutate_apply_struct_example_stateful]\n    db::mutate_apply(TestDataboxMutateApply2{}, make_not_null(&box));\n\n    CHECK(approx(db::get<test_databox_tags::Tag4>(box)) == 3.14 * 2.0);\n    CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n          Scalar<DataVector>(DataVector(2, 6.)));\n    CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n          (tnsr::I<DataVector, 3>{\n              {{DataVector(2, 9.), DataVector(2, 12.), DataVector(2, 15.)}}}));\n    CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n          Scalar<DataVector>(DataVector(2, 12.)));\n    CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n    // [mutate_apply_lambda_example]\n    db::mutate_apply<\n        tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>,\n        tmpl::list<test_databox_tags::Tag2>>(\n        [](const gsl::not_null<Scalar<DataVector>*> scalar,\n           const gsl::not_null<tnsr::I<DataVector, 3>*> vector,\n           const std::string& tag2) {\n          scalar->get() *= 2.0;\n          get<0>(*vector) *= 3.0;\n          get<1>(*vector) *= 4.0;\n          get<2>(*vector) *= 5.0;\n          CHECK(tag2 == \"My Sample String\"s);\n        },\n        make_not_null(&box));\n    // [mutate_apply_lambda_example]\n    if constexpr (not using_db_access) {\n      db::mutate_apply<tmpl::list<test_databox_tags::ScalarTag>,\n                       tmpl::list<test_databox_tags::Tag2>>(\n          [](const gsl::not_null<Scalar<DataVector>*> scalar,\n             const std::string& tag2) {\n            CHECK(*scalar == Scalar<DataVector>(DataVector(2, 12.)));\n            CHECK(tag2 == \"My Sample String\"s);\n          },\n          make_not_null(&box));\n    }\n    CHECK(approx(db::get<test_databox_tags::Tag4>(box)) == 3.14 * 2.0);\n    CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n          Scalar<DataVector>(DataVector(2, 12.)));\n    CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n          (tnsr::I<DataVector, 3>{\n              {{DataVector(2, 27.), DataVector(2, 48.), DataVector(2, 75.)}}}));\n    CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n          Scalar<DataVector>(DataVector(2, 24.)));\n    CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n    // check with a forwarded return value\n    if constexpr (not using_db_access) {\n      size_t size_of_internal_string =\n          db::mutate_apply<tmpl::list<test_databox_tags::ScalarTag>,\n                           tmpl::list<test_databox_tags::Tag2>>(\n              [](const gsl::not_null<Scalar<DataVector>*> scalar,\n                 const std::string& tag2) {\n                CHECK(*scalar == Scalar<DataVector>(DataVector(2, 12.)));\n                CHECK(tag2 == \"My Sample String\"s);\n                return tag2.size();\n              },\n              make_not_null(&box));\n      CHECK(size_of_internal_string == 16_st);\n    } else {\n      size_t size_of_internal_string =\n          db::mutate_apply<tmpl::list<test_databox_tags::ScalarTag>,\n                           tmpl::list<test_databox_tags::Tag2>>(\n              [](const gsl::not_null<Scalar<DataVector>*> scalar,\n                 const std::string& tag2) {\n                CHECK(*scalar == Scalar<DataVector>(DataVector(2, 12.)));\n                CHECK(tag2 == \"My Sample String\"s);\n                return tag2.size();\n              },\n              make_not_null(&box));\n      CHECK(size_of_internal_string == 16_st);\n    }\n\n    db::mutate_apply<\n        tmpl::list<Tags::Variables<tmpl::list<test_databox_tags::ScalarTag,\n                                              test_databox_tags::VectorTag>>>,\n        tmpl::list<test_databox_tags::Tag2>>(\n        [](const gsl::not_null<Variables<tmpl::list<\n               test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>*>\n               vars,\n           const std::string& tag2) {\n          get<test_databox_tags::ScalarTag>(*vars).get() *= 2.0;\n          get<0>(get<test_databox_tags::VectorTag>(*vars)) *= 3.0;\n          get<1>(get<test_databox_tags::VectorTag>(*vars)) *= 4.0;\n          get<2>(get<test_databox_tags::VectorTag>(*vars)) *= 5.0;\n          CHECK(tag2 == \"My Sample String\"s);\n        },\n        make_not_null(&box));\n\n    CHECK(approx(db::get<test_databox_tags::Tag4>(box)) == 3.14 * 2.0);\n    CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n          Scalar<DataVector>(DataVector(2, 24.)));\n    CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n          (tnsr::I<DataVector, 3>{{{DataVector(2, 81.), DataVector(2, 192.),\n                                    DataVector(2, 375.)}}}));\n    CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n          Scalar<DataVector>(DataVector(2, 48.)));\n    CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n  }\n\n  {\n    INFO(\"Stateless struct with tags lists\");\n    // [mutate_apply_struct_example_stateless]\n    db::mutate_apply<TestDataboxMutateApply>(make_not_null(&box));\n    // [mutate_apply_struct_example_stateless]\n    CHECK(approx(db::get<test_databox_tags::Tag4>(box)) == 3.14 * 2.0);\n    CHECK(db::get<test_databox_tags::ScalarTag>(box) ==\n          Scalar<DataVector>(DataVector(2, 48.)));\n    CHECK(db::get<test_databox_tags::VectorTag>(box) ==\n          (tnsr::I<DataVector, 3>{{{DataVector(2, 243.), DataVector(2, 768.),\n                                    DataVector(2, 1875.)}}}));\n    CHECK(db::get<test_databox_tags::ScalarTag2>(box) ==\n          Scalar<DataVector>(DataVector(2, 96.)));\n    CHECK(db::get<test_databox_tags::VectorTag2>(box) ==\n          (tnsr::I<DataVector, 3>(DataVector(2, 2.))));\n  }\n\n  {\n    INFO(\"unique_ptr\");\n    db::mutate_apply<tmpl::list<test_databox_tags::Pointer>, tmpl::list<>>(\n        [](const gsl::not_null<std::unique_ptr<int>*> p) { **p = 5; },\n        make_not_null(&box));\n    db::mutate_apply<tmpl::list<>,\n                     tmpl::list<test_databox_tags::Pointer,\n                                test_databox_tags::PointerToCounter,\n                                test_databox_tags::PointerToSum>>(\n        [](const int& simple, const int& compute, const int& compute_mutating) {\n          CHECK(simple == 5);\n          CHECK(compute == 6);\n          CHECK(compute_mutating == 12);\n        },\n        make_not_null(&box));\n    db::mutate_apply<tmpl::list<test_databox_tags::Pointer>, tmpl::list<>>(\n        [](const gsl::not_null<std::unique_ptr<int>*> p) { **p = 6; },\n        make_not_null(&box));\n    if constexpr (not using_db_access) {\n      db::mutate_apply<tmpl::list<>,\n                       tmpl::list<test_databox_tags::Pointer,\n                                  test_databox_tags::PointerToCounter>>(\n          [](const int& simple, const int& compute) {\n            CHECK(simple == 6);\n            CHECK(compute == 7);\n          },\n          make_not_null(&box));\n    }\n\n    db::mutate_apply<PointerMutateApply>(make_not_null(&box));\n    CHECK(db::get<test_databox_tags::Pointer>(box) == 7);\n\n    db::mutate_apply<PointerMutateApply2>(make_not_null(&box));\n    CHECK(db::get<test_databox_tags::Pointer>(box) == 8);\n  }\n\n  if constexpr (not using_db_access) {\n    INFO(\"Tags::DataBox\");\n    db::mutate_apply<tmpl::list<>,\n                     tmpl::list<::Tags::DataBox, test_databox_tags::Tag0>>(\n        [](const T& /*box*/, const double& /*tag0*/) {}, make_not_null(&box));\n    db::mutate_apply<tmpl::list<::Tags::DataBox>,\n                     tmpl::list<test_databox_tags::Tag0>>(\n        [](const gsl::not_null<T*> /*box*/, const double& /*tag0*/) {},\n        make_not_null(&box));\n\n    struct ReadApply {\n      using return_tags [[maybe_unused]] = tmpl::list<>;\n      using argument_tags [[maybe_unused]] = tmpl::list<::Tags::DataBox>;\n      static void apply(const T& /*box*/) {}\n    };\n    struct ReadCall {\n      using return_tags [[maybe_unused]] = tmpl::list<>;\n      using argument_tags [[maybe_unused]] = tmpl::list<::Tags::DataBox>;\n      void operator()(const T& /*box*/) const {}\n    };\n    struct WriteApply {\n      using return_tags [[maybe_unused]] = tmpl::list<::Tags::DataBox>;\n      using argument_tags [[maybe_unused]] = tmpl::list<>;\n      static void apply(const gsl::not_null<T*> /*box*/) {}\n    };\n    struct WriteCall {\n      using return_tags [[maybe_unused]] = tmpl::list<::Tags::DataBox>;\n      using argument_tags [[maybe_unused]] = tmpl::list<>;\n      void operator()(const gsl::not_null<T*> /*box*/) const {}\n    };\n\n    db::mutate_apply<ReadApply>(make_not_null(&box));\n    db::mutate_apply<ReadCall>(make_not_null(&box));\n    db::mutate_apply<WriteApply>(make_not_null(&box));\n    db::mutate_apply<WriteCall>(make_not_null(&box));\n  }\n}\n\nvoid test_mutate_apply() {\n  INFO(\"test mutate apply\");\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<\n            test_databox_tags::Tag0FromOption, test_databox_tags::Tag1,\n            test_databox_tags::Tag2,\n            Tags::Variables<tmpl::list<test_databox_tags::ScalarTag,\n                                       test_databox_tags::VectorTag>>,\n            test_databox_tags::Pointer>,\n        db::AddComputeTags<test_databox_tags::Tag4Compute,\n                           test_databox_tags::Tag5Compute,\n                           test_databox_tags::MultiplyScalarByTwoCompute,\n                           test_databox_tags::PointerToCounterCompute,\n                           test_databox_tags::PointerToSumCompute>>(\n        3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s,\n        Variables<tmpl::list<test_databox_tags::ScalarTag,\n                             test_databox_tags::VectorTag>>(2, 3.),\n        std::make_unique<int>(3));\n  };\n  {\n    INFO(\"DataBox\");\n    auto box = create_box();\n    test_mutate_apply(box);\n  }\n  {\n    INFO(\"Access\");\n    auto box = create_box();\n    test_mutate_apply(db::as_access(box));\n  }\n}\n\nstatic_assert(\n    std::is_same_v<\n        db::compute_databox_type<tmpl::list<\n            test_databox_tags::Tag0, test_databox_tags::Tag1,\n            Tags::Variables<tmpl::list<test_databox_tags::ScalarTag,\n                                       test_databox_tags::VectorTag>>,\n            test_databox_tags::Tag4Compute,\n            test_databox_tags::MultiplyScalarByTwoCompute>>,\n        db::DataBox<tmpl::list<\n            test_databox_tags::Tag0, test_databox_tags::Tag1,\n            Tags::Variables<brigand::list<test_databox_tags::ScalarTag,\n                                          test_databox_tags::VectorTag>>,\n            test_databox_tags::ScalarTag, test_databox_tags::VectorTag,\n            test_databox_tags::Tag4Compute,\n            test_databox_tags::MultiplyScalarByTwoCompute,\n            ::Tags::Subitem<test_databox_tags::ScalarTag2,\n                            test_databox_tags::MultiplyScalarByTwoCompute>,\n            ::Tags::Subitem<test_databox_tags::VectorTag2,\n                            test_databox_tags::MultiplyScalarByTwoCompute>>>>,\n    \"Failed testing db::compute_databox_type\");\n\nstatic_assert(\n    std::is_same_v<\n        db::compute_databox_type<\n            tmpl::list<test_databox_tags::TagForIncompleteType>>,\n        db::DataBox<tmpl::list<test_databox_tags::TagForIncompleteType>>>,\n    \"Failed testing db::compute_databox_type for incomplete type\");\n\nvoid multiply_by_two_mutate(const gsl::not_null<std::vector<double>*> t,\n                            const double value) {\n  if (t->empty()) {\n    t->resize(10);\n  }\n  for (auto& p : *t) {\n    p = 2.0 * value;\n  }\n}\n\n// [databox_mutating_compute_item_function]\nvoid mutate_variables(\n    const gsl::not_null<Variables<tmpl::list<test_databox_tags::ScalarTag,\n                                             test_databox_tags::VectorTag>>*>\n        t,\n    const double value) {\n  if (t->number_of_grid_points() != 10) {\n    *t = Variables<\n        tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>(\n        10, 0.0);\n  }\n  for (auto& p : get<test_databox_tags::ScalarTag>(*t)) {\n    p = 2.0 * value;\n  }\n  for (auto& p : get<test_databox_tags::VectorTag>(*t)) {\n    p = 3.0 * value;\n  }\n}\n// [databox_mutating_compute_item_function]\n\nnamespace test_databox_tags {\nstruct MutateTag0 : db::SimpleTag {\n  using type = std::vector<double>;\n};\n\nstruct MutateTag0Compute : MutateTag0, db::ComputeTag {\n  using return_type = std::vector<double>;\n  using base = MutateTag0;\n  static constexpr auto function = multiply_by_two_mutate;\n  using argument_tags = tmpl::list<Tag0>;\n};\n\nstruct MutateVariables : db::SimpleTag {\n  using type = Variables<\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>;\n};\n\n// [databox_mutating_compute_item_tag]\nstruct MutateVariablesCompute : MutateVariables, db::ComputeTag {\n  using base = MutateVariables;\n  static constexpr auto function = mutate_variables;\n  using return_type = Variables<\n      tmpl::list<test_databox_tags::ScalarTag, test_databox_tags::VectorTag>>;\n  using argument_tags = tmpl::list<Tag0>;\n};\n// [databox_mutating_compute_item_tag]\n}  // namespace test_databox_tags\n\ntemplate <typename T>\nvoid test_mutating_compute_item(T& original_box) {\n  const double* const initial_data_location_mutating =\n      db::get<test_databox_tags::MutateTag0>(original_box).data();\n  const std::array<const double* const, 4>\n      initial_variables_data_location_mutate{\n          {get<test_databox_tags::ScalarTag>(\n               db::get<test_databox_tags::MutateVariables>(original_box))\n               .get()\n               .data(),\n           get<0>(\n               get<test_databox_tags::VectorTag>(\n                   db::get<test_databox_tags::MutateVariables>(original_box)))\n               .data(),\n           get<1>(\n               get<test_databox_tags::VectorTag>(\n                   db::get<test_databox_tags::MutateVariables>(original_box)))\n               .data(),\n           get<2>(\n               get<test_databox_tags::VectorTag>(\n                   db::get<test_databox_tags::MutateVariables>(original_box)))\n               .data()}};\n\n  CHECK(approx(db::get<test_databox_tags::Tag4>(original_box)) == 3.14 * 2.0);\n  CHECK_ITERABLE_APPROX(db::get<test_databox_tags::MutateTag0>(original_box),\n                        std::vector<double>(10, 2.0 * 3.14));\n  CHECK_ITERABLE_APPROX(\n      get<test_databox_tags::ScalarTag>(\n          db::get<test_databox_tags::MutateVariables>(original_box)),\n      Scalar<DataVector>(DataVector(10, 2.0 * 3.14)));\n  CHECK_ITERABLE_APPROX(\n      get<test_databox_tags::VectorTag>(\n          db::get<test_databox_tags::MutateVariables>(original_box)),\n      typename test_databox_tags::VectorTag::type(DataVector(10, 3.0 * 3.14)));\n\n  db::mutate<test_databox_tags::Tag0, test_databox_tags::Tag1>(\n\n      [](const gsl::not_null<double*> tag0,\n         const gsl::not_null<std::vector<double>*> tag1,\n         const double compute_tag0) {\n        CHECK(6.28 == compute_tag0);\n        *tag0 = 10.32;\n        (*tag1)[0] = 837.2;\n      },\n      make_not_null(&original_box),\n      db::get<test_databox_tags::Tag4>(original_box));\n\n  CHECK(10.32 == db::get<test_databox_tags::Tag0>(original_box));\n  CHECK(837.2 == db::get<test_databox_tags::Tag1>(original_box)[0]);\n  CHECK(approx(db::get<test_databox_tags::Tag4>(original_box)) == 10.32 * 2.0);\n  CHECK_ITERABLE_APPROX(db::get<test_databox_tags::MutateTag0>(original_box),\n                        std::vector<double>(10, 2.0 * 10.32));\n  CHECK(initial_data_location_mutating ==\n        db::get<test_databox_tags::MutateTag0>(original_box).data());\n  CHECK_ITERABLE_APPROX(\n      get<test_databox_tags::ScalarTag>(\n          db::get<test_databox_tags::MutateVariables>(original_box)),\n      Scalar<DataVector>(DataVector(10, 2.0 * 10.32)));\n  CHECK_ITERABLE_APPROX(\n      get<test_databox_tags::VectorTag>(\n          db::get<test_databox_tags::MutateVariables>(original_box)),\n      typename test_databox_tags::VectorTag::type(DataVector(10, 3.0 * 10.32)));\n\n  // Check that the memory allocated by std::vector has not changed, which is\n  // the key feature of mutating compute items.\n  CHECK(initial_variables_data_location_mutate ==\n        (std::array<const double* const, 4>{\n            {get<test_databox_tags::ScalarTag>(\n                 db::get<test_databox_tags::MutateVariables>(original_box))\n                 .get()\n                 .data(),\n             get<0>(\n                 get<test_databox_tags::VectorTag>(\n                     db::get<test_databox_tags::MutateVariables>(original_box)))\n                 .data(),\n             get<1>(\n                 get<test_databox_tags::VectorTag>(\n                     db::get<test_databox_tags::MutateVariables>(original_box)))\n                 .data(),\n             get<2>(\n                 get<test_databox_tags::VectorTag>(\n                     db::get<test_databox_tags::MutateVariables>(original_box)))\n                 .data()}}));\n}\n\nvoid test_mutating_compute_item() {\n  INFO(\"test mutating compute item\");\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                          test_databox_tags::Tag1, test_databox_tags::Tag2>,\n        db::AddComputeTags<test_databox_tags::MutateTag0Compute,\n                           test_databox_tags::MutateVariablesCompute,\n                           test_databox_tags::Tag4Compute,\n                           test_databox_tags::Tag5Compute>>(\n        3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s);\n  };\n  {\n    INFO(\"DataBox\");\n    auto box = create_box();\n    test_mutating_compute_item(box);\n  }\n  {\n    INFO(\"Access\");\n    auto box = create_box();\n    test_mutating_compute_item(db::as_access(box));\n  }\n}\n\nnamespace DataBoxTest_detail {\nstruct vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Grid>;\n};\n\nstruct scalar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct vector2 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Grid>;\n};\n}  // namespace DataBoxTest_detail\n\nvoid test_data_on_slice_single() {\n  INFO(\"test data on slice single\");\n  const size_t x_extents = 2;\n  const size_t y_extents = 3;\n  const size_t z_extents = 4;\n  const size_t vec_size = DataBoxTest_detail::vector::type::size();\n  Index<3> extents(x_extents, y_extents, z_extents);\n  auto box = db::create<db::AddSimpleTags<DataBoxTest_detail::vector>>([]() {\n    Variables<tmpl::list<DataBoxTest_detail::vector>> vars(24, 0.);\n    for (size_t s = 0; s < vars.size(); ++s) {\n      // clang-tidy: do not use pointer arithmetic\n      vars.data()[s] = s;  // NOLINT\n    }\n    return get<DataBoxTest_detail::vector>(vars);\n  }());\n\n  Variables<tmpl::list<DataBoxTest_detail::vector>> expected_vars_sliced_in_x(\n      y_extents * z_extents, 0.);\n  Variables<tmpl::list<DataBoxTest_detail::vector>> expected_vars_sliced_in_y(\n      x_extents * z_extents, 0.);\n  Variables<tmpl::list<DataBoxTest_detail::vector>> expected_vars_sliced_in_z(\n      x_extents * y_extents, 0.);\n  const size_t x_offset = 1;\n  const size_t y_offset = 2;\n  const size_t z_offset = 1;\n\n  for (size_t s = 0; s < expected_vars_sliced_in_x.size(); ++s) {\n    // clang-tidy: do not use pointer arithmetic\n    expected_vars_sliced_in_x.data()[s] = x_offset + s * x_extents;  // NOLINT\n  }\n  for (size_t i = 0; i < vec_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t z = 0; z < z_extents; ++z) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_y\n            .data()[x + x_extents * (z + z_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y_offset + z * y_extents);\n      }\n    }\n  }\n  for (size_t i = 0; i < vec_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t y = 0; y < y_extents; ++y) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_z\n            .data()[x + x_extents * (y + y_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y + y_extents * z_offset);\n      }\n    }\n  }\n  CHECK(\n      // [data_on_slice]\n      db::data_on_slice(box, extents, 0, x_offset,\n                        tmpl::list<DataBoxTest_detail::vector>{})\n      // [data_on_slice]\n      == expected_vars_sliced_in_x);\n  CHECK(db::data_on_slice(box, extents, 1, y_offset,\n                          tmpl::list<DataBoxTest_detail::vector>{}) ==\n        expected_vars_sliced_in_y);\n  CHECK(db::data_on_slice(box, extents, 2, z_offset,\n                          tmpl::list<DataBoxTest_detail::vector>{}) ==\n        expected_vars_sliced_in_z);\n}\n\nvoid test_data_on_slice() {\n  INFO(\"test data on slice\");\n  const size_t x_extents = 2;\n  const size_t y_extents = 3;\n  const size_t z_extents = 4;\n  const size_t vec_size = DataBoxTest_detail::vector::type::size();\n  Index<3> extents(x_extents, y_extents, z_extents);\n  auto box = db::create<\n      db::AddSimpleTags<DataBoxTest_detail::vector, DataBoxTest_detail::scalar,\n                        DataBoxTest_detail::vector2>>(\n      []() {\n        Variables<tmpl::list<DataBoxTest_detail::vector>> vars(24, 0.);\n        for (size_t s = 0; s < vars.size(); ++s) {\n          // clang-tidy: do not use pointer arithmetic\n          vars.data()[s] = s;  // NOLINT\n        }\n        return get<DataBoxTest_detail::vector>(vars);\n      }(),\n      Scalar<DataVector>(DataVector{8.9, 0.7, 6.7}),\n      []() {\n        Variables<tmpl::list<DataBoxTest_detail::vector>> vars(24, 0.);\n        for (size_t s = 0; s < vars.size(); ++s) {\n          // clang-tidy: do not use pointer arithmetic\n          vars.data()[s] = s * 10.0;  // NOLINT\n        }\n        return get<DataBoxTest_detail::vector>(vars);\n      }());\n\n  Variables<tmpl::list<DataBoxTest_detail::vector>> expected_vars_sliced_in_x(\n      y_extents * z_extents, 0.);\n  Variables<tmpl::list<DataBoxTest_detail::vector>> expected_vars_sliced_in_y(\n      x_extents * z_extents, 0.);\n  Variables<tmpl::list<DataBoxTest_detail::vector>> expected_vars_sliced_in_z(\n      x_extents * y_extents, 0.);\n  const size_t x_offset = 1;\n  const size_t y_offset = 2;\n  const size_t z_offset = 1;\n\n  for (size_t s = 0; s < expected_vars_sliced_in_x.size(); ++s) {\n    // clang-tidy: do not use pointer arithmetic\n    expected_vars_sliced_in_x.data()[s] = x_offset + s * x_extents;  // NOLINT\n  }\n  for (size_t i = 0; i < vec_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t z = 0; z < z_extents; ++z) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_y\n            .data()[x + x_extents * (z + z_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y_offset + z * y_extents);\n      }\n    }\n  }\n  for (size_t i = 0; i < vec_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t y = 0; y < y_extents; ++y) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_z\n            .data()[x + x_extents * (y + y_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y + y_extents * z_offset);\n      }\n    }\n  }\n  // x slice\n  {\n    const auto sliced0 = data_on_slice(\n        box, extents, 0, x_offset,\n        tmpl::list<DataBoxTest_detail::vector, DataBoxTest_detail::vector2>{});\n    CHECK(get<DataBoxTest_detail::vector>(sliced0) ==\n          get<DataBoxTest_detail::vector>(expected_vars_sliced_in_x));\n    CHECK(get<DataBoxTest_detail::vector2>(sliced0) ==\n          get<DataBoxTest_detail::vector>(\n              Variables<tmpl::list<DataBoxTest_detail::vector>>(\n                  expected_vars_sliced_in_x * 10.0)));\n    const auto sliced1 = data_on_slice(\n        box, extents, 0, x_offset, tmpl::list<DataBoxTest_detail::vector2>{});\n    CHECK(get<DataBoxTest_detail::vector2>(sliced1) ==\n          get<DataBoxTest_detail::vector>(\n              Variables<tmpl::list<DataBoxTest_detail::vector>>(\n                  expected_vars_sliced_in_x * 10.0)));\n  }\n  // y slice\n  {\n    const auto sliced0 = data_on_slice(\n        box, extents, 1, y_offset,\n        tmpl::list<DataBoxTest_detail::vector, DataBoxTest_detail::vector2>{});\n    CHECK(get<DataBoxTest_detail::vector>(sliced0) ==\n          get<DataBoxTest_detail::vector>(expected_vars_sliced_in_y));\n    CHECK(get<DataBoxTest_detail::vector2>(sliced0) ==\n          get<DataBoxTest_detail::vector>(\n              Variables<tmpl::list<DataBoxTest_detail::vector>>(\n                  expected_vars_sliced_in_y * 10.0)));\n    const auto sliced1 = data_on_slice(\n        box, extents, 1, y_offset, tmpl::list<DataBoxTest_detail::vector2>{});\n    CHECK(get<DataBoxTest_detail::vector2>(sliced1) ==\n          get<DataBoxTest_detail::vector>(\n              Variables<tmpl::list<DataBoxTest_detail::vector>>(\n                  expected_vars_sliced_in_y * 10.0)));\n  }\n  // z slice\n  {\n    const auto sliced0 = data_on_slice(\n        box, extents, 2, z_offset,\n        tmpl::list<DataBoxTest_detail::vector, DataBoxTest_detail::vector2>{});\n    CHECK(get<DataBoxTest_detail::vector>(sliced0) ==\n          get<DataBoxTest_detail::vector>(expected_vars_sliced_in_z));\n    CHECK(get<DataBoxTest_detail::vector2>(sliced0) ==\n          get<DataBoxTest_detail::vector>(\n              Variables<tmpl::list<DataBoxTest_detail::vector>>(\n                  expected_vars_sliced_in_z * 10.0)));\n    const auto sliced1 = data_on_slice(\n        box, extents, 2, z_offset, tmpl::list<DataBoxTest_detail::vector2>{});\n    CHECK(get<DataBoxTest_detail::vector2>(sliced1) ==\n          get<DataBoxTest_detail::vector>(\n              Variables<tmpl::list<DataBoxTest_detail::vector>>(\n                  expected_vars_sliced_in_z * 10.0)));\n  }\n}\n}  // namespace\n\nnamespace {\n// For tests with subitems we create a `Parent` tag here. It's just a pair of\n// two items (think a `Variables` with tensor tags). The two items are made\n// available as `First` and `Second` tags in the databox.\n\n// We can't use raw fundamental types as subitems because subitems\n// need to have a reference-like nature.\ntemplate <typename T>\nclass Boxed {\n public:\n  explicit Boxed(T data) : data_(std::move(data)), ptr_(&data_) {}\n  Boxed() : data_(), ptr_(&data_) {}\n  Boxed(const Boxed&) = delete;\n  Boxed(Boxed&& other) : data_(std::move(other.data_)), ptr_(&data_) {}\n  Boxed& operator=(const Boxed&) = delete;\n  Boxed& operator=(Boxed&& other) {\n    data_ = std::move(other.data_);\n    return *this;\n  }\n  ~Boxed() = default;\n\n  T& operator*() { return data_; }\n  const T& operator*() const { return data_; }\n  T* const& ptr() const { return ptr_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | data_; }\n\n private:\n  T data_;\n  T* ptr_;\n};\n\ntemplate <size_t N>\nstruct Parent : db::SimpleTag {\n  using type = std::pair<Boxed<int>, Boxed<double>>;\n};\ntemplate <size_t N>\nstruct ParentCompute : Parent<N>, db::ComputeTag {\n  using base = Parent<N>;\n  using return_type = std::pair<Boxed<int>, Boxed<double>>;\n  static void function(const gsl::not_null<return_type*> result,\n                       const std::pair<Boxed<int>, Boxed<double>>& arg) {\n    count++;\n    *result = std::make_pair(Boxed<int>(*arg.first + 1),\n                             Boxed<double>(*arg.second * 2.));\n  }\n  using argument_tags = tmpl::list<Parent<N - 1>>;\n  static int count;\n};\n\ntemplate <size_t N>\nint ParentCompute<N>::count = 0;\n\ntemplate <size_t N>\nstruct First : db::SimpleTag {\n  using type = int*;\n\n  static constexpr size_t index = 0;\n};\ntemplate <size_t N>\nstruct Second : db::SimpleTag {\n  using type = double*;\n\n  static constexpr size_t index = 1;\n};\n}  // namespace\n\nnamespace db {\ntemplate <size_t N>\nstruct Subitems<Parent<N>> {\n  using type = tmpl::list<First<N>, Second<N>>;\n  using tag = Parent<N>;\n\n  template <typename Subtag>\n  static void create_item(\n      const gsl::not_null<typename tag::type*> parent_value,\n      const gsl::not_null<typename Subtag::type*> sub_value) {\n    *sub_value = std::get<Subtag::index>(*parent_value).ptr();\n  }\n};\n\ntemplate <size_t N>\nstruct Subitems<ParentCompute<N>> {\n  using type = tmpl::list<First<N>, Second<N>>;\n  using tag = ParentCompute<N>;\n\n  template <typename Subtag>\n  static const typename Subtag::type& create_compute_item(\n      const typename tag::type& parent_value) {\n    return std::get<Subtag::index>(parent_value).ptr();\n  }\n};\n}  // namespace db\n\nnamespace {\n// Now we go one step further with subitems: we store two `Parent` tags in this\n// `LeftAndRight` tag (think a Variables in a DirectionMap). Then we make the\n// subitems of the `Parent` available as `LeftAndRight` subitems as well. So we\n// have this structure:\n//\n// - LeftAndRight<Parent> is in the DataBox (Parent contains First and Second)\n// - LeftAndRight<First> and LeftAndRight<Second> are accessible as subitems\ntemplate <typename Tag>\nstruct LeftAndRight : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = std::pair<typename tag::type, typename tag::type>;\n};\n\nstruct ArgTag : db::SimpleTag {\n  using type = size_t;\n};\n\nstruct LeftAndRightParentCompute : LeftAndRight<Parent<0>>, db::ComputeTag {\n  using base = LeftAndRight<Parent<0>>;\n  using argument_tags = tmpl::list<ArgTag>;\n  using return_type = typename LeftAndRight<Parent<0>>::type;\n  static void function(const gsl::not_null<return_type*> result,\n                       const size_t arg) {\n    *result = std::make_pair(\n        // Left\n        std::make_pair(\n            // First\n            Boxed<int>(static_cast<int>(arg) + 1),\n            // Second\n            Boxed<double>(static_cast<double>(arg) * 2.)),\n        // Right\n        std::make_pair(\n            // First\n            Boxed<int>(static_cast<int>(arg) + 2),\n            // Second\n            Boxed<double>(static_cast<double>(arg) * 3.)));\n  }\n};\n\n}  // namespace\n\nnamespace db {\ntemplate <>\nstruct Subitems<LeftAndRightParentCompute> {\n  using tag = LeftAndRightParentCompute;\n  using type = tmpl::list<LeftAndRight<First<0>>, LeftAndRight<Second<0>>>;\n\n  template <typename Subtag>\n  static void create_item(\n      const gsl::not_null<typename tag::type*> parent_value,\n      const gsl::not_null<typename Subtag::type*> sub_value) {\n    const auto& [left_parent, right_parent] = *parent_value;\n    auto& [left_sub, right_sub] = *sub_value;\n    left_sub = std::get<Subtag::tag::index>(left_parent).ptr();\n    right_sub = std::get<Subtag::tag::index>(right_parent).ptr();\n  }\n\n  template <typename Subtag>\n  static void create_compute_item(\n      const gsl::not_null<typename Subtag::type*> sub_value,\n      const typename tag::type& parent_value) {\n    const auto& [left_parent, right_parent] = parent_value;\n    auto& [left_sub, right_sub] = *sub_value;\n    left_sub = std::get<Subtag::tag::index>(left_parent).ptr();\n    right_sub = std::get<Subtag::tag::index>(right_parent).ptr();\n  }\n};\n}  // namespace db\n\nnamespace Tags {\n// These subitems are implemented as compute tags, because they can't just\n// const-reference the parent value. They have to create a new LeftAndRight pair\n// (like Variables in a DirectionMap).\ntemplate <typename FirstOrSecond>\nstruct Subitem<LeftAndRight<FirstOrSecond>, LeftAndRightParentCompute,\n               Requires<std::is_same_v<FirstOrSecond, First<0>> or\n                        std::is_same_v<FirstOrSecond, Second<0>>>>\n    : db::ComputeTag, LeftAndRight<FirstOrSecond> {\n  using base = LeftAndRight<FirstOrSecond>;\n  using return_type = typename base::type;\n  using parent_tag = LeftAndRightParentCompute;\n  using argument_tags = tmpl::list<parent_tag::base>;\n  static void function(const gsl::not_null<return_type*> subitems,\n                       const typename parent_tag::type& parent_value) {\n    ::db::Subitems<parent_tag>::template create_compute_item<base>(\n        subitems, parent_value);\n  }\n};\n}  // namespace Tags\n\nnamespace {\n\nvoid test_subitems() {\n  INFO(\"test subitems\");\n  {\n    auto box = db::create<db::AddSimpleTags<Parent<0>>,\n                          db::AddComputeTags<ParentCompute<1>>>(\n        std::make_pair(Boxed<int>(5), Boxed<double>(3.5)));\n\n    static_assert(std::is_same_v<\n                  decltype(box)::tags_list,\n                  tmpl::list<Parent<0>, First<0>, Second<0>, ParentCompute<1>,\n                             Tags::Subitem<First<1>, ParentCompute<1>>,\n                             Tags::Subitem<Second<1>, ParentCompute<1>>>>);\n\n    static_assert(\n        std::is_same_v<decltype(box)::immutable_item_tags,\n                       tmpl::list<ParentCompute<1>,\n                                  Tags::Subitem<First<1>, ParentCompute<1>>,\n                                  Tags::Subitem<Second<1>, ParentCompute<1>>>>);\n\n    static_assert(std::is_same_v<decltype(box)::immutable_item_creation_tags,\n                                 tmpl::list<ParentCompute<1>>>);\n\n    static_assert(std::is_same_v<decltype(box)::mutable_item_tags,\n                                 tmpl::list<Parent<0>, First<0>, Second<0>>>);\n\n    static_assert(std::is_same_v<decltype(box)::mutable_subitem_tags,\n                                 tmpl::list<First<0>, Second<0>>>);\n\n    static_assert(std::is_same_v<decltype(box)::compute_item_tags,\n                                 tmpl::list<ParentCompute<1>>>);\n\n    using immutable_subitem_tags =\n        tmpl::filter<decltype(box)::immutable_item_tags,\n                     tt::is_a<::Tags::Subitem, tmpl::_1>>;\n    static_assert(\n        std::is_same_v<immutable_subitem_tags,\n                       tmpl::list<Tags::Subitem<First<1>, ParentCompute<1>>,\n                                  Tags::Subitem<Second<1>, ParentCompute<1>>>>);\n\n    TestHelpers::db::test_reference_tag<\n        ::Tags::Subitem<First<1>, ParentCompute<1>>>(\"First\");\n\n    CHECK(*db::get<First<0>>(box) == 5);\n    CHECK(*db::get<First<1>>(box) == 6);\n    CHECK(*db::get<Second<0>>(box) == 3.5);\n    CHECK(*db::get<Second<1>>(box) == 7);\n\n    db::mutate<Second<0>>([](const gsl::not_null<double**> x) { **x = 12.; },\n                          make_not_null(&box));\n\n    CHECK(*db::get<First<0>>(box) == 5);\n    CHECK(*db::get<First<1>>(box) == 6);\n    CHECK(*db::get<Second<0>>(box) == 12.);\n    CHECK(*db::get<Second<1>>(box) == 24.);\n\n    {\n      const auto copy_box = serialize_and_deserialize(box);\n      CHECK(*db::get<First<0>>(copy_box) == 5);\n      CHECK(*db::get<First<1>>(copy_box) == 6);\n      CHECK(*db::get<Second<0>>(copy_box) == 12.);\n      CHECK(*db::get<Second<1>>(copy_box) == 24.);\n    }\n\n    {\n      auto move_box = std::move(box);\n      db::mutate<First<0>>([](const gsl::not_null<int**> val) { **val = 10; },\n                           make_not_null(&move_box));\n      CHECK(*db::get<First<0>>(move_box) == 10);\n      CHECK(*db::get<First<1>>(move_box) == 11);\n      CHECK(*db::get<Second<0>>(move_box) == 12.);\n      CHECK(*db::get<Second<1>>(move_box) == 24.);\n    }\n\n  }\n  {\n    INFO(\"Default-construction with subitems\");\n    auto box = db::create<db::AddSimpleTags<Parent<0>>,\n                          db::AddComputeTags<ParentCompute<1>>>();\n    // Check the default-constructed DataBox links subitems correctly\n    CHECK(db::get<Parent<0>>(box).first.ptr() == db::get<First<0>>(box));\n    CHECK(db::get<Parent<0>>(box).second.ptr() == db::get<Second<0>>(box));\n    db::mutate<Parent<0>>(\n        [](const gsl::not_null<std::pair<Boxed<int>, Boxed<double>>*> val) {\n          *val = std::make_pair(Boxed<int>(5), Boxed<double>(3.5));\n        },\n        make_not_null(&box));\n    CHECK(*db::get<First<0>>(box) == 5);\n    CHECK(*db::get<Second<0>>(box) == 3.5);\n    CHECK(*db::get<First<1>>(box) == 6);\n    CHECK(*db::get<Second<1>>(box) == 7.);\n    db::mutate<First<0>, Second<0>>(\n        [](const gsl::not_null<int**> a, const gsl::not_null<double**> b) {\n          **a = 2;\n          **b = 2.5;\n        },\n        make_not_null(&box));\n    CHECK(*db::get<First<0>>(box) == 2);\n    CHECK(*db::get<Second<0>>(box) == 2.5);\n    CHECK(*db::get<First<1>>(box) == 3);\n    CHECK(*db::get<Second<1>>(box) == 5.);\n  }\n  {\n    INFO(\"Mutation with subitems\");\n    auto box = db::create<db::AddSimpleTags<Parent<0>>,\n                          db::AddComputeTags<ParentCompute<1>>>(\n        std::make_pair(Boxed<int>(2), Boxed<double>(3.5)));\n    CHECK(*db::get<First<1>>(box) == 3);\n    db::mutate<Parent<0>>(\n        [](const gsl::not_null<std::pair<Boxed<int>, Boxed<double>>*> val) {\n          *val = std::make_pair(Boxed<int>(5), Boxed<double>(2.5));\n        },\n        make_not_null(&box));\n    CHECK(*db::get<First<1>>(box) == 6);\n  }\n  {\n    INFO(\"Mutation with compute-subitems\");\n    auto box = db::create<db::AddSimpleTags<ArgTag>,\n                          db::AddComputeTags<LeftAndRightParentCompute>>(2_st);\n    CHECK(*db::get<LeftAndRight<First<0>>>(box).first == 3);\n    db::mutate<ArgTag>([](const gsl::not_null<size_t*> arg) { *arg = 3; },\n                       make_not_null(&box));\n    CHECK(*db::get<LeftAndRight<First<0>>>(box).first == 4);\n  }\n}\n\nnamespace test_databox_tags {\nstruct Tag0Int : db::SimpleTag {\n  using type = int;\n};\n\ntemplate <typename ArgumentTag>\nstruct OverloadType : db::SimpleTag {\n  using type = double;\n};\n\n// [overload_compute_tag_type]\ntemplate <typename ArgumentTag>\nstruct OverloadTypeCompute : OverloadType<ArgumentTag>, db::ComputeTag {\n  using base = OverloadType<ArgumentTag>;\n  using return_type = double;\n  static constexpr void function(const gsl::not_null<double*> result,\n                                 const int& a) {\n    *result = 5 * a;\n  }\n\n  static constexpr void function(const gsl::not_null<double*> result,\n                                 const double a) {\n    *result = 3.2 * a;\n  }\n  using argument_tags = tmpl::list<ArgumentTag>;\n};\n// [overload_compute_tag_type]\n\ntemplate <typename ArgumentTag0, typename ArgumentTag1 = void>\nstruct OverloadNumberOfArgs : db::SimpleTag {\n  using type = double;\n};\n\n// [overload_compute_tag_number_of_args]\ntemplate <typename ArgumentTag0, typename ArgumentTag1 = void>\nstruct OverloadNumberOfArgsCompute\n    : OverloadNumberOfArgs<ArgumentTag0, ArgumentTag1>,\n      db::ComputeTag {\n  using base = OverloadNumberOfArgs<ArgumentTag0, ArgumentTag1>;\n  using return_type = double;\n\n  static constexpr void function(const gsl::not_null<double*> result,\n                                 const double a) {\n    *result = 3.2 * a;\n  }\n\n  static constexpr void function(const gsl::not_null<double*> result,\n                                 const double a, const double b) {\n    *result = a * b;\n  }\n\n  using argument_tags =\n      tmpl::conditional_t<std::is_same_v<void, ArgumentTag1>,\n                          tmpl::list<ArgumentTag0>,\n                          tmpl::list<ArgumentTag0, ArgumentTag1>>;\n};\n// [overload_compute_tag_number_of_args]\n\ntemplate <typename ArgumentTag>\nstruct Template : db::SimpleTag {\n  using type = typename ArgumentTag::type;\n};\n\n// [overload_compute_tag_template]\ntemplate <typename ArgumentTag>\nstruct TemplateCompute : Template<ArgumentTag>, db::ComputeTag {\n  using base = Template<ArgumentTag>;\n  using return_type = typename ArgumentTag::type;\n\n  template <typename T>\n  static constexpr void function(const gsl::not_null<T*> result, const T& a) {\n    *result = 5 * a;\n  }\n\n  using argument_tags = tmpl::list<ArgumentTag>;\n};\n// [overload_compute_tag_template]\n}  // namespace test_databox_tags\n\ntemplate <typename T>\nvoid test_overload_compute_tags(T& box) {\n  CHECK(db::get<test_databox_tags::OverloadType<test_databox_tags::Tag0>>(\n            box) == 8.4 * 3.2);\n  CHECK(db::get<test_databox_tags::OverloadType<test_databox_tags::Tag0Int>>(\n            box) == -3 * 5);\n  CHECK(\n      db::get<test_databox_tags::OverloadNumberOfArgs<test_databox_tags::Tag0>>(\n          box) == 3.2 * 8.4);\n  CHECK(db::get<test_databox_tags::OverloadNumberOfArgs<\n            test_databox_tags::Tag0,\n            test_databox_tags::OverloadType<test_databox_tags::Tag0>>>(box) ==\n        8.4 * 3.2 * 8.4);\n  CHECK(db::get<test_databox_tags::Template<test_databox_tags::Tag0>>(box) ==\n        8.4 * 5.0);\n  CHECK(db::get<test_databox_tags::Template<test_databox_tags::Tag0Int>>(box) ==\n        -3 * 5);\n}\n\nvoid test_overload_compute_tags() {\n  INFO(\"testing overload compute tags.\");\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<test_databox_tags::Tag0, test_databox_tags::Tag0Int>,\n        db::AddComputeTags<\n            test_databox_tags::OverloadTypeCompute<test_databox_tags::Tag0>,\n            test_databox_tags::OverloadTypeCompute<test_databox_tags::Tag0Int>,\n            test_databox_tags::OverloadNumberOfArgsCompute<\n                test_databox_tags::Tag0>,\n            test_databox_tags::OverloadNumberOfArgsCompute<\n                test_databox_tags::Tag0,\n                test_databox_tags::OverloadType<test_databox_tags::Tag0>>,\n            test_databox_tags::TemplateCompute<test_databox_tags::Tag0>,\n            test_databox_tags::TemplateCompute<test_databox_tags::Tag0Int>>>(\n        8.4, -3);\n  };\n  {\n    INFO(\"Using DataBox\");\n    auto box = create_box();\n    test_overload_compute_tags(box);\n  }\n  {\n    INFO(\"Using db::Access\");\n    auto box = create_box();\n    test_overload_compute_tags(db::as_access(box));\n  }\n}\n\nnamespace TestTags {\nnamespace {\nstruct MyTag0 {\n  using type = int;\n};\n\nstruct MyTag1 {\n  using type = double;\n};\n\nstruct TupleTag : db::SimpleTag {\n  using type = tuples::TaggedTuple<MyTag0, MyTag1>;\n};\n}  // namespace\n}  // namespace TestTags\n\nvoid test_with_tagged_tuple() {\n  // Test that having a TaggedTuple inside a DataBox works properly\n  auto box = db::create<db::AddSimpleTags<TestTags::TupleTag>>(\n      tuples::TaggedTuple<TestTags::MyTag0, TestTags::MyTag1>{123, 2.3});\n  auto box2 = std::move(box);\n  CHECK(tuples::get<TestTags::MyTag0>(db::get<TestTags::TupleTag>(box2)) ==\n        123);\n  CHECK(tuples::get<TestTags::MyTag1>(db::get<TestTags::TupleTag>(box2)) ==\n        2.3);\n}\n\nvoid serialization_non_subitem_simple_items() {\n  INFO(\"serialization of a DataBox with non-Subitem simple items only\");\n  auto serialization_test_box = db::create<\n      db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                        test_databox_tags::Tag1, test_databox_tags::Tag2>>(\n      3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s);\n  const double* before_0 =\n      &db::get<test_databox_tags::Tag0>(serialization_test_box);\n  const std::vector<double>* before_1 =\n      &db::get<test_databox_tags::Tag1>(serialization_test_box);\n  const std::string* before_2 =\n      &db::get<test_databox_tags::Tag2>(serialization_test_box);\n\n  auto deserialized_serialization_test_box =\n      serialize_and_deserialize(serialization_test_box);\n  CHECK(db::get<test_databox_tags::Tag0>(serialization_test_box) == 3.14);\n  CHECK(db::get<test_databox_tags::Tag0>(deserialized_serialization_test_box) ==\n        3.14);\n  CHECK(before_0 == &db::get<test_databox_tags::Tag0>(serialization_test_box));\n  CHECK(before_0 !=\n        &db::get<test_databox_tags::Tag0>(deserialized_serialization_test_box));\n  CHECK(db::get<test_databox_tags::Tag1>(serialization_test_box) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(db::get<test_databox_tags::Tag1>(deserialized_serialization_test_box) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(before_1 == &db::get<test_databox_tags::Tag1>(serialization_test_box));\n  CHECK(before_1 !=\n        &db::get<test_databox_tags::Tag1>(deserialized_serialization_test_box));\n  CHECK(db::get<test_databox_tags::Tag2>(serialization_test_box) ==\n        \"My Sample String\"s);\n  CHECK(db::get<test_databox_tags::Tag2>(deserialized_serialization_test_box) ==\n        \"My Sample String\"s);\n  CHECK(before_2 == &db::get<test_databox_tags::Tag2>(serialization_test_box));\n  CHECK(before_2 !=\n        &db::get<test_databox_tags::Tag2>(deserialized_serialization_test_box));\n\n  auto copied_items = db::copy_items<\n      tmpl::list<test_databox_tags::Tag1, test_databox_tags::Tag2>>(\n      serialization_test_box);\n  CHECK(get<test_databox_tags::Tag1>(copied_items) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(get<test_databox_tags::Tag2>(copied_items) == \"My Sample String\"s);\n}\n\nvoid serialization_subitems_simple_items() {\n  INFO(\"serialization of a DataBox with Subitem and non-Subitem simple items\");\n  auto serialization_test_box =\n      db::create<db::AddSimpleTags<test_databox_tags::Tag0FromOption, Parent<0>,\n                                   test_databox_tags::Tag1,\n                                   test_databox_tags::Tag2, Parent<1>>>(\n          3.14, std::make_pair(Boxed<int>(5), Boxed<double>(3.5)),\n          std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s,\n          std::make_pair(Boxed<int>(9), Boxed<double>(-4.5)));\n  const double* before_0 =\n      &db::get<test_databox_tags::Tag0>(serialization_test_box);\n  const std::vector<double>* before_1 =\n      &db::get<test_databox_tags::Tag1>(serialization_test_box);\n  const std::string* before_2 =\n      &db::get<test_databox_tags::Tag2>(serialization_test_box);\n  const std::pair<Boxed<int>, Boxed<double>>* before_parent0 =\n      &db::get<Parent<0>>(serialization_test_box);\n  int* const* before_parent0f = &db::get<First<0>>(serialization_test_box);\n  double* const* before_parent0s = &db::get<Second<0>>(serialization_test_box);\n  const std::pair<Boxed<int>, Boxed<double>>* before_parent1 =\n      &db::get<Parent<1>>(serialization_test_box);\n  int* const* before_parent1f = &db::get<First<1>>(serialization_test_box);\n  double* const* before_parent1s = &db::get<Second<1>>(serialization_test_box);\n\n  auto deserialized_serialization_test_box =\n      serialize_and_deserialize(serialization_test_box);\n  CHECK(db::get<test_databox_tags::Tag0>(serialization_test_box) == 3.14);\n  CHECK(db::get<test_databox_tags::Tag0>(deserialized_serialization_test_box) ==\n        3.14);\n  CHECK(before_0 == &db::get<test_databox_tags::Tag0>(serialization_test_box));\n  CHECK(before_0 !=\n        &db::get<test_databox_tags::Tag0>(deserialized_serialization_test_box));\n  CHECK(*db::get<First<0>>(serialization_test_box) == 5);\n  CHECK(*db::get<Second<0>>(serialization_test_box) == 3.5);\n  CHECK(*db::get<First<0>>(deserialized_serialization_test_box) == 5);\n  CHECK(*db::get<Second<0>>(deserialized_serialization_test_box) == 3.5);\n  CHECK(before_parent0 == &db::get<Parent<0>>(serialization_test_box));\n  CHECK(before_parent0 !=\n        &db::get<Parent<0>>(deserialized_serialization_test_box));\n  CHECK(before_parent0f == &db::get<First<0>>(serialization_test_box));\n  CHECK(before_parent0f !=\n        &db::get<First<0>>(deserialized_serialization_test_box));\n  CHECK(before_parent0s == &db::get<Second<0>>(serialization_test_box));\n  CHECK(before_parent0s !=\n        &db::get<Second<0>>(deserialized_serialization_test_box));\n  CHECK(db::get<test_databox_tags::Tag1>(serialization_test_box) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(db::get<test_databox_tags::Tag1>(deserialized_serialization_test_box) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(before_1 == &db::get<test_databox_tags::Tag1>(serialization_test_box));\n  CHECK(before_1 !=\n        &db::get<test_databox_tags::Tag1>(deserialized_serialization_test_box));\n  CHECK(db::get<test_databox_tags::Tag2>(serialization_test_box) ==\n        \"My Sample String\"s);\n  CHECK(db::get<test_databox_tags::Tag2>(deserialized_serialization_test_box) ==\n        \"My Sample String\"s);\n  CHECK(before_2 == &db::get<test_databox_tags::Tag2>(serialization_test_box));\n  CHECK(before_2 !=\n        &db::get<test_databox_tags::Tag2>(deserialized_serialization_test_box));\n  CHECK(*db::get<First<1>>(serialization_test_box) == 9);\n  CHECK(*db::get<Second<1>>(serialization_test_box) == -4.5);\n  CHECK(*db::get<First<1>>(deserialized_serialization_test_box) == 9);\n  CHECK(*db::get<Second<1>>(deserialized_serialization_test_box) == -4.5);\n  CHECK(before_parent1 == &db::get<Parent<1>>(serialization_test_box));\n  CHECK(before_parent1 !=\n        &db::get<Parent<1>>(deserialized_serialization_test_box));\n  CHECK(before_parent1f == &db::get<First<1>>(serialization_test_box));\n  CHECK(before_parent1f !=\n        &db::get<First<1>>(deserialized_serialization_test_box));\n  CHECK(before_parent1s == &db::get<Second<1>>(serialization_test_box));\n  CHECK(before_parent1s !=\n        &db::get<Second<1>>(deserialized_serialization_test_box));\n}\n\ntemplate <int Id>\nstruct CountingFunc {\n  static void apply(const gsl::not_null<double*> result) {\n    count++;\n    *result = 8.2;\n  }\n  static int count;\n};\n\ntemplate <int Id>\nint CountingFunc<Id>::count = 0;\n\ntemplate <int Id>\nstruct CountingTag : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <int Id>\nstruct CountingTagCompute : CountingTag<Id>, db::ComputeTag {\n  using base = CountingTag<Id>;\n  using return_type = double;\n  static constexpr auto function = CountingFunc<Id>::apply;\n  using argument_tags = tmpl::list<>;\n};\n\ntemplate <size_t SecondId>\nstruct CountingTagDouble : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <size_t SecondId>\nstruct CountingTagDoubleCompute : CountingTagDouble<SecondId>, db::ComputeTag {\n  using base = CountingTagDouble<SecondId>;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> result, double* const& t) {\n    count++;\n    *result = *t * 6.0;\n  }\n  using argument_tags = tmpl::list<Second<SecondId>>;\n  static int count;\n};\n\ntemplate <size_t SecondId>\nint CountingTagDoubleCompute<SecondId>::count = 0;\n\n// clang-tidy: this function is too long. Yes, well we need to check lots\nvoid serialization_subitem_compute_items() {  // NOLINT\n  INFO(\"serialization of a DataBox with Subitem compute items\");\n  auto serialization_test_box =\n      db::create<db::AddSimpleTags<test_databox_tags::Tag0FromOption, Parent<0>,\n                                   test_databox_tags::Tag1,\n                                   test_databox_tags::Tag2, Parent<1>>,\n                 db::AddComputeTags<\n                     CountingTagCompute<1>, test_databox_tags::Tag4Compute,\n                     ParentCompute<2>, test_databox_tags::Tag5Compute,\n                     ParentCompute<3>, CountingTagCompute<0>,\n                     CountingTagDoubleCompute<2>, CountingTagDoubleCompute<3>>>(\n          3.14, std::make_pair(Boxed<int>(5), Boxed<double>(3.5)),\n          std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s,\n          std::make_pair(Boxed<int>(9), Boxed<double>(-4.5)));\n  const double* before_0 =\n      &db::get<test_databox_tags::Tag0>(serialization_test_box);\n  const std::vector<double>* before_1 =\n      &db::get<test_databox_tags::Tag1>(serialization_test_box);\n  const std::string* before_2 =\n      &db::get<test_databox_tags::Tag2>(serialization_test_box);\n  const std::pair<Boxed<int>, Boxed<double>>* before_parent0 =\n      &db::get<Parent<0>>(serialization_test_box);\n  int* const* before_parent0f = &db::get<First<0>>(serialization_test_box);\n  double* const* before_parent0s = &db::get<Second<0>>(serialization_test_box);\n  const std::pair<Boxed<int>, Boxed<double>>* before_parent1 =\n      &db::get<Parent<1>>(serialization_test_box);\n  int* const* before_parent1f = &db::get<First<1>>(serialization_test_box);\n  double* const* before_parent1s = &db::get<Second<1>>(serialization_test_box);\n  CHECK(db::get<test_databox_tags::Tag4>(serialization_test_box) == 6.28);\n  const double* before_compute_tag0 =\n      &db::get<test_databox_tags::Tag4>(serialization_test_box);\n  CHECK(CountingFunc<0>::count == 0);\n  CHECK(CountingFunc<1>::count == 0);\n  CHECK(db::get<CountingTag<0>>(serialization_test_box) == 8.2);\n  const double* before_counting_tag0 =\n      &db::get<CountingTag<0>>(serialization_test_box);\n  CHECK(CountingFunc<0>::count == 1);\n  CHECK(CountingFunc<1>::count == 0);\n\n  CHECK(ParentCompute<2>::count == 0);\n  CHECK(ParentCompute<3>::count == 0);\n  const std::pair<Boxed<int>, Boxed<double>>* before_parent2 =\n      &db::get<Parent<2>>(serialization_test_box);\n  CHECK(ParentCompute<2>::count == 1);\n  CHECK(ParentCompute<3>::count == 0);\n  int* const* before_parent2_first = &db::get<First<2>>(serialization_test_box);\n  double* const* before_parent2_second =\n      &db::get<Second<2>>(serialization_test_box);\n\n  // Check we are correctly pointing into parent\n  CHECK(&*(db::get<Parent<2>>(serialization_test_box).first) ==\n        &*db::get<First<2>>(serialization_test_box));\n  CHECK(&*(db::get<Parent<2>>(serialization_test_box).second) ==\n        &*db::get<Second<2>>(serialization_test_box));\n\n  CHECK(*(db::get<Parent<2>>(serialization_test_box).first) == 10);\n  CHECK(*(db::get<Parent<2>>(serialization_test_box).second) == -9.0);\n\n  CHECK(*db::get<First<2>>(serialization_test_box) == 10);\n  CHECK(*db::get<Second<2>>(serialization_test_box) == -9.0);\n  CHECK(ParentCompute<2>::count == 1);\n  CHECK(ParentCompute<3>::count == 0);\n\n  // Check compute items that take subitems\n  CHECK(CountingTagDoubleCompute<2>::count == 0);\n  CHECK(db::get<CountingTagDouble<2>>(serialization_test_box) == -9.0 * 6.0);\n  CHECK(CountingTagDoubleCompute<2>::count == 1);\n  const double* const before_compute_tag2 =\n      &db::get<CountingTagDouble<2>>(serialization_test_box);\n\n  auto deserialized_serialization_test_box =\n      serialize_and_deserialize(serialization_test_box);\n  CHECK(CountingFunc<0>::count == 1);\n  CHECK(CountingFunc<1>::count == 0);\n  CHECK(db::get<test_databox_tags::Tag0>(serialization_test_box) == 3.14);\n  CHECK(db::get<test_databox_tags::Tag0>(deserialized_serialization_test_box) ==\n        3.14);\n  CHECK(before_0 == &db::get<test_databox_tags::Tag0>(serialization_test_box));\n  CHECK(before_0 !=\n        &db::get<test_databox_tags::Tag0>(deserialized_serialization_test_box));\n  CHECK(*db::get<First<0>>(serialization_test_box) == 5);\n  CHECK(*db::get<Second<0>>(serialization_test_box) == 3.5);\n  CHECK(*db::get<First<0>>(deserialized_serialization_test_box) == 5);\n  CHECK(*db::get<Second<0>>(deserialized_serialization_test_box) == 3.5);\n  CHECK(before_parent0 == &db::get<Parent<0>>(serialization_test_box));\n  CHECK(before_parent0 !=\n        &db::get<Parent<0>>(deserialized_serialization_test_box));\n  CHECK(before_parent0f == &db::get<First<0>>(serialization_test_box));\n  CHECK(before_parent0f !=\n        &db::get<First<0>>(deserialized_serialization_test_box));\n  CHECK(before_parent0s == &db::get<Second<0>>(serialization_test_box));\n  CHECK(before_parent0s !=\n        &db::get<Second<0>>(deserialized_serialization_test_box));\n  CHECK(db::get<test_databox_tags::Tag1>(serialization_test_box) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(db::get<test_databox_tags::Tag1>(deserialized_serialization_test_box) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(before_1 == &db::get<test_databox_tags::Tag1>(serialization_test_box));\n  CHECK(before_1 !=\n        &db::get<test_databox_tags::Tag1>(deserialized_serialization_test_box));\n  CHECK(db::get<test_databox_tags::Tag2>(serialization_test_box) ==\n        \"My Sample String\"s);\n  CHECK(db::get<test_databox_tags::Tag2>(deserialized_serialization_test_box) ==\n        \"My Sample String\"s);\n  CHECK(before_2 == &db::get<test_databox_tags::Tag2>(serialization_test_box));\n  CHECK(before_2 !=\n        &db::get<test_databox_tags::Tag2>(deserialized_serialization_test_box));\n  CHECK(*db::get<First<1>>(serialization_test_box) == 9);\n  CHECK(*db::get<Second<1>>(serialization_test_box) == -4.5);\n  CHECK(*db::get<First<1>>(deserialized_serialization_test_box) == 9);\n  CHECK(*db::get<Second<1>>(deserialized_serialization_test_box) == -4.5);\n  CHECK(before_parent1 == &db::get<Parent<1>>(serialization_test_box));\n  CHECK(before_parent1 !=\n        &db::get<Parent<1>>(deserialized_serialization_test_box));\n  CHECK(before_parent1f == &db::get<First<1>>(serialization_test_box));\n  CHECK(before_parent1f !=\n        &db::get<First<1>>(deserialized_serialization_test_box));\n  CHECK(before_parent1s == &db::get<Second<1>>(serialization_test_box));\n  CHECK(before_parent1s !=\n        &db::get<Second<1>>(deserialized_serialization_test_box));\n  // Check compute items\n  CHECK(db::get<test_databox_tags::Tag4>(deserialized_serialization_test_box) ==\n        6.28);\n  CHECK(&db::get<test_databox_tags::Tag4>(\n            deserialized_serialization_test_box) != before_compute_tag0);\n  CHECK(db::get<test_databox_tags::Tag5>(deserialized_serialization_test_box) ==\n        \"My Sample String6.28\"s);\n\n  CHECK(CountingFunc<0>::count == 1);\n  CHECK(CountingFunc<1>::count == 0);\n  CHECK(db::get<CountingTag<0>>(serialization_test_box) == 8.2);\n  CHECK(CountingFunc<0>::count == 1);\n  CHECK(CountingFunc<1>::count == 0);\n  CHECK(&db::get<CountingTag<0>>(serialization_test_box) ==\n        before_counting_tag0);\n\n  CHECK(db::get<CountingTag<0>>(deserialized_serialization_test_box) == 8.2);\n  CHECK(CountingFunc<0>::count == 2);\n  CHECK(CountingFunc<1>::count == 0);\n  CHECK(&db::get<CountingTag<0>>(deserialized_serialization_test_box) !=\n        before_counting_tag0);\n  CHECK(CountingFunc<0>::count == 2);\n  CHECK(CountingFunc<1>::count == 0);\n  CHECK(db::get<CountingTag<1>>(deserialized_serialization_test_box) == 8.2);\n  CHECK(CountingFunc<0>::count == 2);\n  CHECK(CountingFunc<1>::count == 1);\n  CHECK(db::get<CountingTag<1>>(serialization_test_box) == 8.2);\n  CHECK(CountingFunc<0>::count == 2);\n  CHECK(CountingFunc<1>::count == 2);\n  CHECK(&db::get<CountingTag<1>>(serialization_test_box) !=\n        &db::get<CountingTag<1>>(deserialized_serialization_test_box));\n\n  CHECK(ParentCompute<2>::count == 1);\n  CHECK(ParentCompute<3>::count == 0);\n  CHECK(&db::get<Parent<2>>(serialization_test_box) == before_parent2);\n  // Check we are correctly pointing into parent\n  CHECK(&*(db::get<Parent<2>>(serialization_test_box).first) ==\n        &*db::get<First<2>>(serialization_test_box));\n  CHECK(&*(db::get<Parent<2>>(serialization_test_box).second) ==\n        &*db::get<Second<2>>(serialization_test_box));\n  // Check that we did not reset the subitems items in the initial DataBox\n  CHECK(&db::get<First<2>>(serialization_test_box) == before_parent2_first);\n  CHECK(&db::get<Second<2>>(serialization_test_box) == before_parent2_second);\n  CHECK(*(db::get<Parent<2>>(serialization_test_box).first) == 10);\n  CHECK(*(db::get<Parent<2>>(serialization_test_box).second) == -9.0);\n  CHECK(*(db::get<Parent<2>>(deserialized_serialization_test_box).first) == 10);\n  CHECK(&db::get<Parent<2>>(deserialized_serialization_test_box) !=\n        before_parent2);\n  CHECK(*(db::get<Parent<2>>(deserialized_serialization_test_box).second) ==\n        -9.0);\n  CHECK(*db::get<First<2>>(deserialized_serialization_test_box) == 10);\n  CHECK(*db::get<Second<2>>(deserialized_serialization_test_box) == -9.0);\n  CHECK(ParentCompute<2>::count == 2);\n  CHECK(ParentCompute<3>::count == 0);\n  CHECK(&db::get<Parent<2>>(deserialized_serialization_test_box) !=\n        before_parent2);\n  // Check pointers in deserialized box\n  CHECK(&db::get<First<2>>(deserialized_serialization_test_box) !=\n        before_parent2_first);\n  CHECK(&db::get<Second<2>>(deserialized_serialization_test_box) !=\n        before_parent2_second);\n  // Check we are correctly pointing into new parent and not old\n  CHECK(&*(db::get<Parent<2>>(deserialized_serialization_test_box).first) ==\n        &*db::get<First<2>>(deserialized_serialization_test_box));\n  CHECK(&*(db::get<Parent<2>>(deserialized_serialization_test_box).second) ==\n        &*db::get<Second<2>>(deserialized_serialization_test_box));\n  CHECK(&*(db::get<Parent<2>>(deserialized_serialization_test_box).first) !=\n        &*db::get<First<2>>(serialization_test_box));\n  CHECK(&*(db::get<Parent<2>>(deserialized_serialization_test_box).second) !=\n        &*db::get<Second<2>>(serialization_test_box));\n\n  CHECK(*(db::get<Parent<3>>(serialization_test_box).first) == 11);\n  CHECK(ParentCompute<2>::count == 2);\n  CHECK(ParentCompute<3>::count == 1);\n  CHECK(*(db::get<Parent<3>>(serialization_test_box).second) == -18.0);\n  CHECK(ParentCompute<2>::count == 2);\n  CHECK(ParentCompute<3>::count == 1);\n  CHECK(*db::get<First<3>>(serialization_test_box) == 11);\n  CHECK(*db::get<Second<3>>(serialization_test_box) == -18.0);\n  CHECK(ParentCompute<2>::count == 2);\n  CHECK(ParentCompute<3>::count == 1);\n  CHECK(*(db::get<Parent<3>>(deserialized_serialization_test_box).first) == 11);\n  CHECK(ParentCompute<2>::count == 2);\n  CHECK(ParentCompute<3>::count == 2);\n  CHECK(*(db::get<Parent<3>>(deserialized_serialization_test_box).second) ==\n        -18.0);\n  CHECK(*db::get<First<3>>(deserialized_serialization_test_box) == 11);\n  CHECK(*db::get<Second<3>>(deserialized_serialization_test_box) == -18.0);\n  CHECK(ParentCompute<2>::count == 2);\n  CHECK(ParentCompute<3>::count == 2);\n\n  // Check that all the Parent<3> related objects point to the right place\n  CHECK(&*(db::get<Parent<3>>(deserialized_serialization_test_box).first) ==\n        &*db::get<First<3>>(deserialized_serialization_test_box));\n  CHECK(&*(db::get<Parent<3>>(deserialized_serialization_test_box).second) ==\n        &*db::get<Second<3>>(deserialized_serialization_test_box));\n  CHECK(&*(db::get<Parent<3>>(serialization_test_box).first) ==\n        &*db::get<First<3>>(serialization_test_box));\n  CHECK(&*(db::get<Parent<3>>(serialization_test_box).second) ==\n        &*db::get<Second<3>>(serialization_test_box));\n  CHECK(&*db::get<First<3>>(deserialized_serialization_test_box) !=\n        &*db::get<First<3>>(serialization_test_box));\n  CHECK(&*db::get<Second<3>>(deserialized_serialization_test_box) !=\n        &*db::get<Second<3>>(serialization_test_box));\n\n  // Check compute items that depend on the subitems\n  CHECK(CountingTagDoubleCompute<2>::count == 1);\n  CHECK(db::get<CountingTagDouble<2>>(serialization_test_box) == -9.0 * 6.0);\n  CHECK(before_compute_tag2 ==\n        &db::get<CountingTagDouble<2>>(serialization_test_box));\n  CHECK(db::get<CountingTagDouble<2>>(deserialized_serialization_test_box) ==\n        -9.0 * 6.0);\n  CHECK(before_compute_tag2 !=\n        &db::get<CountingTagDouble<2>>(deserialized_serialization_test_box));\n  CHECK(CountingTagDoubleCompute<2>::count == 2);\n\n  CHECK(CountingTagDoubleCompute<3>::count == 0);\n  CHECK(db::get<CountingTagDouble<3>>(serialization_test_box) == -18.0 * 6.0);\n  CHECK(db::get<CountingTagDouble<3>>(deserialized_serialization_test_box) ==\n        -18.0 * 6.0);\n  CHECK(&db::get<CountingTagDouble<3>>(serialization_test_box) !=\n        &db::get<CountingTagDouble<3>>(deserialized_serialization_test_box));\n  CHECK(CountingTagDoubleCompute<3>::count == 2);\n\n  // Mutate subitems 1 in deserialized to see that changes propagate correctly\n  db::mutate<Second<1>>([](const gsl::not_null<double**> x) { **x = 12.; },\n                        make_not_null(&serialization_test_box));\n  CHECK(ParentCompute<2>::count == 2);\n  CHECK(CountingTagDoubleCompute<2>::count == 2);\n  CHECK(db::get<CountingTagDouble<2>>(serialization_test_box) == 24.0 * 6.0);\n  CHECK(ParentCompute<2>::count == 3);\n  CHECK(CountingTagDoubleCompute<2>::count == 3);\n  CHECK(CountingTagDoubleCompute<3>::count == 2);\n  CHECK(db::get<CountingTagDouble<3>>(serialization_test_box) == 48.0 * 6.0);\n  CHECK(CountingTagDoubleCompute<3>::count == 3);\n\n  db::mutate<Second<1>>([](const gsl::not_null<double**> x) { **x = -7.; },\n                        make_not_null(&deserialized_serialization_test_box));\n  CHECK(ParentCompute<2>::count == 3);\n  CHECK(CountingTagDoubleCompute<2>::count == 3);\n  CHECK(db::get<CountingTagDouble<2>>(deserialized_serialization_test_box) ==\n        -14.0 * 6.0);\n  CHECK(ParentCompute<2>::count == 4);\n  CHECK(CountingTagDoubleCompute<2>::count == 4);\n  CHECK(CountingTagDoubleCompute<3>::count == 3);\n  CHECK(db::get<CountingTagDouble<3>>(deserialized_serialization_test_box) ==\n        -28.0 * 6.0);\n  CHECK(CountingTagDoubleCompute<3>::count == 4);\n\n  // Check things didn't get modified in the original DataBox\n  CHECK(ParentCompute<2>::count == 4);\n  CHECK(CountingTagDoubleCompute<2>::count == 4);\n  CHECK(db::get<CountingTagDouble<2>>(serialization_test_box) == 24.0 * 6.0);\n  CHECK(ParentCompute<2>::count == 4);\n  CHECK(CountingTagDoubleCompute<2>::count == 4);\n  CHECK(CountingTagDoubleCompute<3>::count == 4);\n  CHECK(db::get<CountingTagDouble<3>>(serialization_test_box) == 48.0 * 6.0);\n  CHECK(CountingTagDoubleCompute<3>::count == 4);\n\n  CountingFunc<0>::count = 0;\n  CountingFunc<1>::count = 0;\n  CountingTagDoubleCompute<2>::count = 0;\n  CountingTagDoubleCompute<3>::count = 0;\n  ParentCompute<2>::count = 0;\n  ParentCompute<3>::count = 0;\n}\n\nvoid serialization_compute_items_of_base_tags() {\n  INFO(\"serialization of a DataBox with compute items depending on base tags\");\n  auto original_box =\n      db::create<db::AddSimpleTags<test_databox_tags::Tag2>,\n                 db::AddComputeTags<test_databox_tags::Tag6Compute>>(\n          \"My Sample String\"s);\n  CHECK(db::get<test_databox_tags::Tag2>(original_box) == \"My Sample String\");\n  CHECK(db::get<test_databox_tags::Tag6>(original_box) == \"My Sample String\");\n  auto copied_box = serialize_and_deserialize(original_box);\n  CHECK(db::get<test_databox_tags::Tag2>(copied_box) == \"My Sample String\");\n  CHECK(db::get<test_databox_tags::Tag6>(copied_box) == \"My Sample String\");\n\n  auto copied_items =\n      db::copy_items<tmpl::list<test_databox_tags::Tag2>>(original_box);\n  CHECK(get<test_databox_tags::Tag2>(copied_items) == \"My Sample String\");\n}\n\nvoid serialization_of_pointers() {\n  INFO(\"Serialization of pointers\");\n  const auto box =\n      db::create<db::AddSimpleTags<test_databox_tags::Pointer>,\n                 db::AddComputeTags<test_databox_tags::PointerToCounterCompute,\n                                    test_databox_tags::PointerToSumCompute>>(\n          std::make_unique<int>(3));\n  const auto check = [](const decltype(box)& check_box) {\n    CHECK(db::get<test_databox_tags::Pointer>(check_box) == 3);\n    CHECK(db::get<test_databox_tags::PointerToCounter>(check_box) == 4);\n    CHECK(db::get<test_databox_tags::PointerToSum>(check_box) == 8);\n  };\n  check(serialize_and_deserialize(box));  // before compute items evaluated\n  check(box);\n  check(serialize_and_deserialize(box));  // after compute items evaluated\n\n  auto copied_items =\n      db::copy_items<tmpl::list<test_databox_tags::Pointer>>(box);\n  check(box);\n  CHECK(*get<test_databox_tags::Pointer>(copied_items) == 3);\n  CHECK(get<test_databox_tags::Pointer>(copied_items).get() !=\n        &db::get<test_databox_tags::Pointer>(box));\n}\n\nvoid test_serialization_and_copy_items() {\n  serialization_non_subitem_simple_items();\n  serialization_subitems_simple_items();\n  serialization_subitem_compute_items();\n  serialization_compute_items_of_base_tags();\n  serialization_of_pointers();\n}\n\nnamespace test_databox_tags {\n// [databox_reference_tag_example]\ntemplate <typename... Tags>\nstruct TaggedTuple : db::SimpleTag {\n  using type = tuples::TaggedTuple<Tags...>;\n};\n\ntemplate <typename Tag, typename ParentTag>\nstruct FromTaggedTuple : Tag, db::ReferenceTag {\n  using base = Tag;\n  using argument_tags = tmpl::list<ParentTag>;\n\n  static const auto& get(const typename ParentTag::type& tagged_tuple) {\n    return tuples::get<Tag>(tagged_tuple);\n  }\n};\n// [databox_reference_tag_example]\n\n// The following is an overcomplicated tag setup to test the dependency\n// resolution of compute tags and reference tags. We shouldn't do this sort of\n// \"tag programming\" in source code. The test setup is this:\nstruct SwitchTag : db::SimpleTag {\n  using type = bool;\n};\n\nstruct Var0 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Tag0OrVar0 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Tag0OrVar0Reference : Tag0OrVar0, db::ReferenceTag {\n  using base = Tag0OrVar0;\n  using argument_tags = tmpl::list<SwitchTag, Tag0, Var0>;\n\n  static const double& get(const bool& switch_value, const double& tag0_value,\n                           const Scalar<DataVector>& var0_value) {\n    if (switch_value) {\n      return tag0_value;\n    } else {\n      return ::get(var0_value)[0];\n    }\n  }\n};\n\nstruct Tag0OrVar0TimesTwo : db::SimpleTag  {\n  using type = double;\n};\n\nstruct Tag0OrVar0TimesTwoCompute : Tag0OrVar0TimesTwo,\n                                db::ComputeTag {\n  using base = Tag0OrVar0TimesTwo;\n  using return_type = double;\n  using argument_tags = tmpl::list<Tag0OrVar0>;\n  static constexpr auto function = multiply_by_two;\n};\n\n}  // namespace test_databox_tags\n\ntemplate <typename T>\nvoid test_reference_item(T& box) {\n  using tuple_tag = test_databox_tags::TaggedTuple<\n      test_databox_tags::Tag0, test_databox_tags::Tag1, test_databox_tags::Tag2,\n      test_databox_tags::Tag4>;\n  const auto& tagged_tuple = get<tuple_tag>(box);\n  CHECK(get<test_databox_tags::Tag0>(tagged_tuple) == 3.14);\n  CHECK(get<test_databox_tags::Tag1>(tagged_tuple) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(get<test_databox_tags::Tag2>(tagged_tuple) == \"My Sample String\"s);\n  CHECK(get<test_databox_tags::Tag0>(box) == 3.14);\n  CHECK(get<test_databox_tags::Tag1>(box) ==\n        std::vector<double>{8.7, 93.2, 84.7});\n  CHECK(get<test_databox_tags::Tag2>(box) == \"My Sample String\"s);\n  // Checking dependency resolution of reference tags and compute tags\n  CHECK(get<test_databox_tags::SwitchTag>(box));\n  CHECK(get<test_databox_tags::Tag0OrVar0>(box) == 3.14);\n  CHECK(get<test_databox_tags::Tag0OrVar0TimesTwo>(box) == 2. * 3.14);\n  db::mutate<tuple_tag>(\n      [](const auto tuple) { get<test_databox_tags::Tag0>(*tuple) = 4.5; },\n      make_not_null(&box));\n  CHECK(get<test_databox_tags::Tag0OrVar0TimesTwo>(box) == 9.);\n  db::mutate<test_databox_tags::SwitchTag>(\n      [](const gsl::not_null<bool*> switch_value) { *switch_value = false; },\n      make_not_null(&box));\n  CHECK(get<test_databox_tags::Tag0OrVar0>(box) == 1.);\n  CHECK(get<test_databox_tags::Tag0OrVar0TimesTwo>(box) == 2.);\n  db::mutate<test_databox_tags::Var0>(\n      [](const gsl::not_null<Scalar<DataVector>*> var0_value) {\n        get(*var0_value) = 0.5;\n      },\n      make_not_null(&box));\n  CHECK(get<test_databox_tags::Tag0OrVar0TimesTwo>(box) == 1.);\n}\n\nvoid test_reference_item() {\n  INFO(\"test reference item\");\n  using tuple_tag = test_databox_tags::TaggedTuple<\n      test_databox_tags::Tag0, test_databox_tags::Tag1, test_databox_tags::Tag2,\n      test_databox_tags::Tag4>;\n  using vars_tag = Tags::Variables<tmpl::list<test_databox_tags::Var0>>;\n  const auto create_box = []() {\n    return db::create<\n        db::AddSimpleTags<tuple_tag, vars_tag, test_databox_tags::SwitchTag>,\n        db::AddComputeTags<test_databox_tags::FromTaggedTuple<\n                               test_databox_tags::Tag0, tuple_tag>,\n                           test_databox_tags::FromTaggedTuple<\n                               test_databox_tags::Tag1, tuple_tag>,\n                           test_databox_tags::FromTaggedTuple<\n                               test_databox_tags::Tag2, tuple_tag>,\n                           test_databox_tags::FromTaggedTuple<\n                               test_databox_tags::Tag4, tuple_tag>,\n                           test_databox_tags::Tag0OrVar0Reference,\n                           test_databox_tags::Tag0OrVar0TimesTwoCompute>>(\n        tuples::TaggedTuple<test_databox_tags::Tag0, test_databox_tags::Tag1,\n                            test_databox_tags::Tag2, test_databox_tags::Tag4>{\n            3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s,\n            1.},\n        Variables<tmpl::list<test_databox_tags::Var0>>{1, 1.}, true);\n  };\n  {\n    INFO(\"Using DataBox\");\n    auto box = create_box();\n    test_reference_item(box);\n  }\n  {\n    INFO(\"Using db::Access\");\n    auto box = create_box();\n    test_reference_item(db::as_access(box));\n  }\n}\n\nvoid test_get_mutable_reference() {\n  INFO(\"test get_mutable_reference\");\n  // Make sure the presence of tags that could not be extracted\n  // doesn't prevent the function from working on other tags.\n  auto box = db::create<db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                                          test_databox_tags::Tag2, Parent<0>>,\n                        db::AddComputeTags<test_databox_tags::Tag4Compute>>(\n      3.14, \"Original string\"s,\n      std::make_pair(Boxed<int>(5), Boxed<double>(3.5)));\n\n  decltype(auto) ref =\n      db::get_mutable_reference<test_databox_tags::Tag2>(make_not_null(&box));\n  static_assert(std::is_same_v<decltype(ref), std::string&>);\n  decltype(auto) base_ref =\n      db::get_mutable_reference<test_databox_tags::Tag2>(make_not_null(&box));\n  static_assert(std::is_same_v<decltype(base_ref), std::string&>);\n  CHECK(&ref == &base_ref);\n  ref = \"New string\";\n  CHECK(db::get<test_databox_tags::Tag2>(box) == \"New string\");\n\n  // These should all fail to compile:\n  // db::get_mutable_reference<test_databox_tags::Tag0>(make_not_null(&box));\n  // db::get_mutable_reference<test_databox_tags::Tag4>(make_not_null(&box));\n  // db::get_mutable_reference<test_databox_tags::Tag4Compute>(\n  //     make_not_null(&box));\n  // db::get_mutable_reference<Parent<0>>(make_not_null(&box));\n  // db::get_mutable_reference<First<0>>(make_not_null(&box));\n}\n\nvoid test_output() {\n  INFO(\"test output\");\n  auto box = db::create<\n      db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                        test_databox_tags::Tag1, test_databox_tags::Tag2>,\n      db::AddComputeTags<test_databox_tags::Tag4Compute,\n                         test_databox_tags::Tag5Compute,\n                         test_databox_tags::Tag0Reference>>(\n      3.14, std::vector<double>{8.7, 93.2, 84.7}, \"My Sample String\"s);\n  std::string output_types = db::as_access(box).print_types();\n  std::string expected_types =\n      \"      DataBox type aliases:\"\n      \"      using tags_list = [(anonymous \"\n      \"namespace)::test_databox_tags::Tag0FromOption,\"\n      \"(anonymous namespace)::test_databox_tags::Tag1,\"\n      \"(anonymous namespace)::test_databox_tags::Tag2,\"\n      \"(anonymous namespace)::test_databox_tags::Tag4Compute,\"\n      \"(anonymous namespace)::test_databox_tags::Tag5Compute,\"\n      \"(anonymous namespace)::test_databox_tags::Tag0Reference,\"\n      \"];\"\n      \"      using immutable_item_tags = [\"\n      \"(anonymous namespace)::test_databox_tags::Tag4Compute,\"\n      \"(anonymous namespace)::test_databox_tags::Tag5Compute,\"\n      \"(anonymous namespace)::test_databox_tags::Tag0Reference,\"\n      \"];\"\n      \"      using immutable_item_creation_tags = [\"\n      \"(anonymous namespace)::test_databox_tags::Tag4Compute,\"\n      \"(anonymous namespace)::test_databox_tags::Tag5Compute,\"\n      \"(anonymous namespace)::test_databox_tags::Tag0Reference,\"\n      \"];\"\n      \"      using mutable_item_tags = [\"\n      \"(anonymous namespace)::test_databox_tags::Tag0FromOption,\"\n      \"(anonymous namespace)::test_databox_tags::Tag1,\"\n      \"(anonymous namespace)::test_databox_tags::Tag2,\"\n      \"];\"\n      \"      using mutable_subitem_tags = [];\"\n      \"      using compute_item_tags = [\"\n      \"(anonymous namespace)::test_databox_tags::Tag4Compute,\"\n      \"(anonymous namespace)::test_databox_tags::Tag5Compute,\"\n      \"];\"\n      \"      using reference_item_tags = [\"\n      \"(anonymous namespace)::test_databox_tags::Tag0Reference,\"\n      \"];\"\n      \"      using edge_list = brigand::list<brigand::edge<(anonymous \"\n      \"namespace)::test_databox_tags::Tag0FromOption, (anonymous \"\n      \"namespace)::test_databox_tags::Tag4Compute, \"\n      \"brigand::integral_constant<int, 1> >, brigand::edge<(anonymous \"\n      \"namespace)::test_databox_tags::Tag2, (anonymous \"\n      \"namespace)::test_databox_tags::Tag5Compute, \"\n      \"brigand::integral_constant<int, 1> >, brigand::edge<(anonymous \"\n      \"namespace)::test_databox_tags::Tag4Compute, (anonymous \"\n      \"namespace)::test_databox_tags::Tag5Compute, \"\n      \"brigand::integral_constant<int, 1> >, brigand::edge<(anonymous \"\n      \"namespace)::test_databox_tags::Tag0FromOption, (anonymous \"\n      \"namespace)::test_databox_tags::Tag0Reference, \"\n      \"brigand::integral_constant<int, 1> > >;\";\n  // Remove whitespace since it may vary between compilers\n  auto remove_whitespace = [](std::string& str) {\n    str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end());\n  };\n  remove_whitespace(output_types);\n  remove_whitespace(expected_types);\n  CHECK(output_types == expected_types);\n\n  std::string output_tags = db::as_access(box).print_tags();\n  std::string expected_tags =\n      \"Simpletags(3)=[\"\n      \"(anonymousnamespace)::test_databox_tags::Tag0FromOption,\"\n      \"(anonymousnamespace)::test_databox_tags::Tag1,\"\n      \"(anonymousnamespace)::test_databox_tags::Tag2,\"\n      \"];\"\n      \"Simpletagssubitems(0)=[];\"\n      \"Computetags(2)=[\"\n      \"(anonymousnamespace)::test_databox_tags::Tag4Compute,\"\n      \"(anonymousnamespace)::test_databox_tags::Tag5Compute,\"\n      \"];Referencetags(1)=[\"\n      \"(anonymousnamespace)::test_databox_tags::Tag0Reference,\"\n      \"];\";\n  // Remove whitespace since it may vary between compilers\n  remove_whitespace(output_tags);\n  remove_whitespace(expected_tags);\n  CHECK(output_tags == expected_tags);\n\n  std::string output_mutable_items = box.print_items<false>();\n  std::string expected_mutable_items =\n      \"Items:\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag0FromOption\\n\"\n      \"Type:  double\\n\"\n      \"Value: 3.14\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag1\\n\"\n      \"Type:  std::vector<double>\\n\"\n      \"Value: (8.7,93.2,84.7)\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag2\\n\"\n      \"Type:  std::string\\n\"\n      \"Value: My Sample String\\n\";\n  CHECK(output_mutable_items == expected_mutable_items);\n  std::string output_items = box.print_items<true>();\n  std::string expected_items =\n      \"Items:\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag0FromOption\\n\"\n      \"Type:  double\\n\"\n      \"Value: 3.14\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag1\\n\"\n      \"Type:  std::vector<double>\\n\"\n      \"Value: (8.7,93.2,84.7)\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag2\\n\"\n      \"Type:  std::string\\n\"\n      \"Value: My Sample String\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag4Compute\\n\"\n      \"Type:  double\\n\"\n      \"Value: 6.28\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag5Compute\\n\"\n      \"Type:  std::string\\n\"\n      \"Value: My Sample String6.28\\n\"\n      \"----------\\n\"\n      \"Name:  (anonymous namespace)::test_databox_tags::Tag0Reference\\n\"\n      \"Type:  double\\n\"\n      \"Value: 3.14\\n\";\n  CHECK(output_items == expected_items);\n  std::ostringstream os;\n  os << box;\n  std::string output_stream = os.str();\n  std::string expected_stream = expected_types + \"\\n\" + expected_items+ \"\\n\";\n  remove_whitespace(output_stream);\n  remove_whitespace(expected_stream);\n  CHECK(output_stream == expected_stream);\n  const auto item_size = box.size_of_items();\n  CHECK(item_size.size() == 5);\n  CHECK(item_size.at(\n            \"(anonymous namespace)::test_databox_tags::Tag0FromOption\") == 8);\n  CHECK(item_size.at(\"(anonymous namespace)::test_databox_tags::Tag1\") == 32);\n  CHECK(item_size.at(\"(anonymous namespace)::test_databox_tags::Tag2\") == 24);\n  CHECK(item_size.at(\"(anonymous namespace)::test_databox_tags::Tag4Compute\") ==\n        8);\n  CHECK(item_size.at(\"(anonymous namespace)::test_databox_tags::Tag5Compute\") ==\n        28);\n\n  const auto box_with_ptrs =\n      db::create<db::AddSimpleTags<test_databox_tags::Pointer>,\n                 db::AddComputeTags<test_databox_tags::PointerToCounterCompute,\n                                    test_databox_tags::PointerToSumCompute>>(\n          std::make_unique<int>(3));\n  const auto item_size_ptrs = box_with_ptrs.size_of_items();\n  CHECK(item_size_ptrs.size() == 3);\n  CHECK(item_size_ptrs.at(\n            \"(anonymous namespace)::test_databox_tags::Pointer\") == 4);\n  CHECK(item_size_ptrs.at(\n            \"(anonymous \"\n            \"namespace)::test_databox_tags::PointerToCounterCompute\") == 0);\n  CHECK(item_size_ptrs.at(\n            \"(anonymous namespace)::test_databox_tags::PointerToSumCompute\") ==\n        0);\n  db::get<test_databox_tags::PointerToSum>(box_with_ptrs);\n  const auto item_size_ptrs_after = box_with_ptrs.size_of_items();\n  CHECK(item_size_ptrs_after.size() == 3);\n  CHECK(item_size_ptrs_after.at(\n            \"(anonymous namespace)::test_databox_tags::Pointer\") == 4);\n  CHECK(item_size_ptrs_after.at(\n            \"(anonymous \"\n            \"namespace)::test_databox_tags::PointerToCounterCompute\") == 4);\n  CHECK(item_size_ptrs_after.at(\n            \"(anonymous namespace)::test_databox_tags::PointerToSumCompute\") ==\n        4);\n}\n\nvoid test_exception_safety() {\n  struct FakeError {};\n\n  using test_databox_tags::ScalarTag;\n  using vars1_tag = Tags::Variables<tmpl::list<ScalarTag>>;\n  const auto make_vars1 = [](const size_t num_points, const double value) {\n    vars1_tag::type vars(num_points);\n    get(get<ScalarTag>(vars)) = value;\n    return vars;\n  };\n  using test_databox_tags::ScalarTag2;\n  using vars2_tag = Tags::Variables<tmpl::list<ScalarTag2>>;\n  const auto make_vars2 = [](const size_t num_points, const double value) {\n    vars2_tag::type vars(num_points);\n    get(get<ScalarTag2>(vars)) = value;\n    return vars;\n  };\n\n  auto box = db::create<db::AddSimpleTags<test_databox_tags::Tag0FromOption,\n                                          vars1_tag, vars2_tag>,\n                        db::AddComputeTags<test_databox_tags::Tag4Compute>>(\n      1.0, make_vars1(1, 1.0), make_vars2(1, 1.0));\n  // Make sure everything is evaluated\n  CHECK(db::get<test_databox_tags::Tag4>(box) == 2.0);\n  CHECK(db::get<ScalarTag>(box) == Scalar<DataVector>(DataVector(1, 1.0)));\n  CHECK(db::get<ScalarTag2>(box) == Scalar<DataVector>(DataVector(1, 1.0)));\n  try {\n    db::mutate<test_databox_tags::Tag0, vars1_tag, ScalarTag2>(\n        [&make_vars1](const gsl::not_null<double*> tag0,\n                      const gsl::not_null<vars1_tag::type*> vars,\n                      const gsl::not_null<Scalar<DataVector>*> scalar) {\n          *tag0 = 2.0;\n          *vars = make_vars1(2, 2.0);\n          get(*scalar) = 2.0;\n          throw FakeError{};\n        },\n        make_not_null(&box));\n  } catch (FakeError) {\n  }\n  CHECK(db::get<test_databox_tags::Tag4>(box) == 4.0);\n  CHECK(db::get<vars1_tag>(box).data() == get(db::get<ScalarTag>(box)).data());\n  CHECK(db::get<ScalarTag>(box) == Scalar<DataVector>(DataVector(2, 2.0)));\n  CHECK(db::get<vars2_tag>(box).data() == get(db::get<ScalarTag2>(box)).data());\n  CHECK(db::get<ScalarTag2>(box) == Scalar<DataVector>(DataVector(1, 2.0)));\n  try {\n    db::mutate_apply<tmpl::list<test_databox_tags::Tag0, vars1_tag, ScalarTag2>,\n                     tmpl::list<>>(\n        [&make_vars1](const gsl::not_null<double*> tag0,\n                      const gsl::not_null<vars1_tag::type*> vars,\n                      const gsl::not_null<Scalar<DataVector>*> scalar) {\n          *tag0 = 3.0;\n          *vars = make_vars1(3, 3.0);\n          get(*scalar) = 3.0;\n          throw FakeError{};\n        },\n        make_not_null(&box));\n  } catch (FakeError) {\n  }\n  CHECK(db::get<test_databox_tags::Tag4>(box) == 6.0);\n  CHECK(db::get<vars1_tag>(box).data() == get(db::get<ScalarTag>(box)).data());\n  CHECK(db::get<ScalarTag>(box) == Scalar<DataVector>(DataVector(3, 3.0)));\n  CHECK(db::get<vars2_tag>(box).data() == get(db::get<ScalarTag2>(box)).data());\n  CHECK(db::get<ScalarTag2>(box) == Scalar<DataVector>(DataVector(1, 3.0)));\n  // Make sure the box is still usable\n  db::mutate<test_databox_tags::Tag0, vars1_tag, ScalarTag2>(\n      [&make_vars1](const gsl::not_null<double*> tag0,\n                    const gsl::not_null<vars1_tag::type*> vars,\n                    const gsl::not_null<Scalar<DataVector>*> scalar) {\n        *tag0 = 4.0;\n        *vars = make_vars1(4, 4.0);\n        get(*scalar) = 4.0;\n      },\n      make_not_null(&box));\n  CHECK(db::get<test_databox_tags::Tag4>(box) == 8.0);\n  CHECK(db::get<vars1_tag>(box).data() == get(db::get<ScalarTag>(box)).data());\n  CHECK(db::get<ScalarTag>(box) == Scalar<DataVector>(DataVector(4, 4.0)));\n  CHECK(db::get<vars2_tag>(box).data() == get(db::get<ScalarTag2>(box)).data());\n  CHECK(db::get<ScalarTag2>(box) == Scalar<DataVector>(DataVector(1, 4.0)));\n}\n\nvoid test_access_errors() {\n  auto box = db::create<\n      db::AddSimpleTags<test_databox_tags::Tag0, test_databox_tags::Tag1>,\n      db::AddComputeTags<test_databox_tags::Tag4Compute>>();\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      db::get<test_databox_tags::Tag2>(db::as_access(box)),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Tag2 is not found in the DataBox. \"\n                               \"Known tags are:\\\\n.*Tag1.*Tag4(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      db::mutate<test_databox_tags::Tag2>([](const auto& /*unused*/) {},\n                                          &db::as_access(box)),\n      Catch::Matchers::Matches(\n          \"(.|\\\\n)*Cannot mutate tag.*Tag2.*Known tags are:\\\\n.*Tag1(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      db::mutate<test_databox_tags::Tag4>([](const auto& /*unused*/) {},\n                                          &db::as_access(box)),\n      Catch::Matchers::Matches(\n          \"(.|\\\\n)*Cannot mutate tag.*Tag4.*Known tags are:\\\\n.*Tag1(.|\\\\n)*\"));\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataBox\", \"[Unit][DataStructures]\") {\n  test_databox();\n  test_create_argument_types();\n  test_get_databox();\n  test_get_databox_error();\n  test_mutate();\n  test_mutate_locked();\n  test_mutate_box();\n  test_apply();\n  test_variables();\n  test_variables2();\n  test_reset_compute_items();\n  test_variables_extra_reset();\n  test_mutate_apply();\n  test_mutating_compute_item();\n  test_data_on_slice_single();\n  test_data_on_slice();\n  test_subitems();\n  test_overload_compute_tags();\n  test_with_tagged_tuple();\n  test_serialization_and_copy_items();\n  test_reference_item();\n  test_get_mutable_reference();\n  test_output();\n  test_exception_safety();\n  test_access_errors();\n}\n\n// Test`tag_is_retrievable_v`\nnamespace {\nnamespace tags_types {\nstruct SimpleTag : db::SimpleTag {\n  using type = double;\n};\n\nstruct DummyTag : db::SimpleTag {\n  using type = int;\n};\n}  // namespace tags_types\n\nstatic_assert(\n    db::tag_is_retrievable_v<tags_types::SimpleTag,\n                             db::DataBox<tmpl::list<tags_types::SimpleTag>>>,\n    \"Failed testing tag_is_retrievable_v\");\nstatic_assert(\n    not db::tag_is_retrievable_v<\n        tags_types::DummyTag, db::DataBox<tmpl::list<tags_types::SimpleTag>>>,\n    \"Failed testing tag_is_retrievable_v\");\nstatic_assert(\n    db::tag_is_retrievable_v<::Tags::DataBox,\n                             db::DataBox<tmpl::list<tags_types::SimpleTag>>>,\n    \"Failed testing tag_is_retrievable_v\");\n\nnamespace test_creation_tag {\nstruct Simple : db::SimpleTag {\n  using type = double;\n};\n\nstruct Compute : Simple, db::ComputeTag {\n  using base = Simple;\n  using argument_tags = tmpl::list<>;\n  static void function(gsl::not_null<double*>);\n};\n\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nusing VariablesTag = ::Tags::Variables<tmpl::list<ScalarTag>>;\n\nstruct ComputeVariables : VariablesTag, db::ComputeTag {\n  using base = VariablesTag;\n  using argument_tags = tmpl::list<>;\n  static void function(gsl::not_null<type*>);\n};\n\nusing SimpleBox = db::compute_databox_type<tmpl::list<Simple, ScalarTag>>;\nstatic_assert(std::is_same_v<db::creation_tag<Simple, SimpleBox>, Simple>);\nstatic_assert(\n    std::is_same_v<db::creation_tag<ScalarTag, SimpleBox>, ScalarTag>);\n\nusing ComplexBox = db::compute_databox_type<tmpl::list<Compute, VariablesTag>>;\nstatic_assert(std::is_same_v<db::creation_tag<Compute, ComplexBox>, Compute>);\nstatic_assert(std::is_same_v<db::creation_tag<Simple, ComplexBox>, Compute>);\nstatic_assert(\n    std::is_same_v<db::creation_tag<VariablesTag, ComplexBox>, VariablesTag>);\nstatic_assert(\n    std::is_same_v<db::creation_tag<ScalarTag, ComplexBox>, VariablesTag>);\n\nusing ComputeVarsBox = db::compute_databox_type<tmpl::list<ComputeVariables>>;\nstatic_assert(std::is_same_v<db::creation_tag<ComputeVariables, ComputeVarsBox>,\n                             ComputeVariables>);\nstatic_assert(std::is_same_v<db::creation_tag<VariablesTag, ComputeVarsBox>,\n                             ComputeVariables>);\nstatic_assert(std::is_same_v<db::creation_tag<ScalarTag, ComputeVarsBox>,\n                             ComputeVariables>);\n}  // namespace test_creation_tag\n\nnamespace test_tag_depends_on {\nstruct Simple : db::SimpleTag {\n  using type = int;\n};\nstruct Simple2 : db::SimpleTag {\n  using type = int;\n};\nstruct Provider : db::SimpleTag {\n  using type = int;\n};\nstruct Consumer : db::SimpleTag {\n  using type = int;\n};\nstruct ConsumerCompute : Consumer, db::ComputeTag {\n  using base = Consumer;\n  using argument_tags = tmpl::list<Provider>;\n  static void function(gsl::not_null<int*> /*result*/, int /*base_consumer*/) {}\n};\nstruct ChainedConsumer : db::SimpleTag {\n  using type = int;\n};\nstruct ChainedConsumerCompute : ChainedConsumer, db::ComputeTag {\n  using base = ChainedConsumer;\n  using argument_tags = tmpl::list<Consumer>;\n  static void function(gsl::not_null<int*> /*result*/, int /*base_consumer*/) {}\n};\n\nusing SimpleBox = db::compute_databox_type<\n    tmpl::list<Simple, Simple2, Provider, ConsumerCompute>>;\nstatic_assert(db::tag_depends_on_v<Simple, Simple, SimpleBox>);\nstatic_assert(not db::tag_depends_on_v<Simple2, Simple, SimpleBox>);\nstatic_assert(not db::tag_depends_on_v<Provider, Simple, SimpleBox>);\nstatic_assert(not db::tag_depends_on_v<ConsumerCompute, Simple, SimpleBox>);\nstatic_assert(not db::tag_depends_on_v<Consumer, Simple, SimpleBox>);\nstatic_assert(db::tag_depends_on_v<ConsumerCompute, Provider, SimpleBox>);\nstatic_assert(db::tag_depends_on_v<Consumer, Provider, SimpleBox>);\nstatic_assert(db::tag_depends_on_v<ConsumerCompute, Provider, SimpleBox>);\nstatic_assert(db::tag_depends_on_v<Consumer, Provider, SimpleBox>);\nstatic_assert(not db::tag_depends_on_v<Provider, Consumer, SimpleBox>);\nstatic_assert(db::tag_depends_on_v<Provider, Provider, SimpleBox>);\n\nusing SimpleBox2 =\n    db::compute_databox_type<tmpl::list<Provider, ConsumerCompute>>;\nstatic_assert(db::tag_depends_on_v<ConsumerCompute, Provider, SimpleBox2>);\nstatic_assert(db::tag_depends_on_v<Consumer, Provider, SimpleBox2>);\nstatic_assert(not db::tag_depends_on_v<Provider, Consumer, SimpleBox2>);\n\nusing ChainBox = db::compute_databox_type<\n    tmpl::list<Provider, ConsumerCompute, ChainedConsumerCompute>>;\nstatic_assert(db::tag_depends_on_v<ChainedConsumerCompute, Provider, ChainBox>);\nstatic_assert(db::tag_depends_on_v<ChainedConsumer, Provider, ChainBox>);\nstatic_assert(\n    db::tag_depends_on_v<ChainedConsumerCompute, ConsumerCompute, ChainBox>);\nstatic_assert(db::tag_depends_on_v<ChainedConsumer, ConsumerCompute, ChainBox>);\nstatic_assert(db::tag_depends_on_v<ChainedConsumerCompute, Consumer, ChainBox>);\nstatic_assert(db::tag_depends_on_v<ChainedConsumer, Consumer, ChainBox>);\n\nstruct TensorProvider : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct TensorConsumer : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nusing VariablesProvider = ::Tags::Variables<tmpl::list<TensorProvider>>;\nusing VariablesConsumer = ::Tags::Variables<tmpl::list<TensorConsumer>>;\nstruct VariablesConsumerCompute : VariablesConsumer, db::ComputeTag {\n  using base = VariablesConsumer;\n  using argument_tags = tmpl::list<VariablesProvider>;\n  static void function(gsl::not_null<type*>, const VariablesProvider::type&);\n};\nstruct ConsumerOfTensor : db::SimpleTag {\n  using type = int;\n};\nstruct ConsumerOfTensorCompute : ConsumerOfTensor, db::ComputeTag {\n  using base = ConsumerOfTensor;\n  using argument_tags = tmpl::list<TensorConsumer>;\n  static void function(gsl::not_null<type*>, const TensorProvider::type&);\n};\n\nusing VariablesBox = db::compute_databox_type<tmpl::list<\n    VariablesProvider, VariablesConsumerCompute, ConsumerOfTensorCompute>>;\nstatic_assert(\n    db::tag_depends_on_v<VariablesProvider, VariablesProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<TensorProvider, VariablesProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<VariablesProvider, TensorProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<TensorProvider, TensorProvider, VariablesBox>);\n\nstatic_assert(\n    db::tag_depends_on_v<VariablesConsumer, VariablesConsumer, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<TensorConsumer, VariablesConsumer, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<VariablesConsumer, TensorConsumer, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<TensorConsumer, TensorConsumer, VariablesBox>);\n\nstatic_assert(\n    db::tag_depends_on_v<VariablesConsumer, VariablesProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<TensorConsumer, VariablesProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<VariablesConsumer, TensorProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<TensorConsumer, TensorProvider, VariablesBox>);\n\nstatic_assert(not db::tag_depends_on_v<VariablesProvider, VariablesConsumer,\n                                       VariablesBox>);\nstatic_assert(\n    not db::tag_depends_on_v<TensorProvider, VariablesConsumer, VariablesBox>);\nstatic_assert(\n    not db::tag_depends_on_v<VariablesProvider, TensorConsumer, VariablesBox>);\nstatic_assert(\n    not db::tag_depends_on_v<TensorProvider, TensorConsumer, VariablesBox>);\n\nstatic_assert(\n    db::tag_depends_on_v<ConsumerOfTensor, VariablesProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<ConsumerOfTensor, VariablesProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<ConsumerOfTensor, TensorProvider, VariablesBox>);\nstatic_assert(\n    db::tag_depends_on_v<ConsumerOfTensor, TensorProvider, VariablesBox>);\n\n// Having a compute tag depending on a subtag (ConsumerOfTensorCompute\n// above) changes the internal representation of the dependency graph,\n// so also test without it.\nusing VariablesBox2 = db::compute_databox_type<\n    tmpl::list<VariablesProvider, VariablesConsumerCompute>>;\nstatic_assert(\n    db::tag_depends_on_v<VariablesProvider, VariablesProvider, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<TensorProvider, VariablesProvider, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<VariablesProvider, TensorProvider, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<TensorProvider, TensorProvider, VariablesBox2>);\n\nstatic_assert(\n    db::tag_depends_on_v<VariablesConsumer, VariablesConsumer, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<TensorConsumer, VariablesConsumer, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<VariablesConsumer, TensorConsumer, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<TensorConsumer, TensorConsumer, VariablesBox2>);\n\nstatic_assert(\n    db::tag_depends_on_v<VariablesConsumer, VariablesProvider, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<TensorConsumer, VariablesProvider, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<VariablesConsumer, TensorProvider, VariablesBox2>);\nstatic_assert(\n    db::tag_depends_on_v<TensorConsumer, TensorProvider, VariablesBox2>);\n\nstatic_assert(not db::tag_depends_on_v<VariablesProvider, VariablesConsumer,\n                                       VariablesBox2>);\nstatic_assert(\n    not db::tag_depends_on_v<TensorProvider, VariablesConsumer, VariablesBox2>);\nstatic_assert(\n    not db::tag_depends_on_v<VariablesProvider, TensorConsumer, VariablesBox2>);\nstatic_assert(\n    not db::tag_depends_on_v<TensorProvider, TensorConsumer, VariablesBox2>);\n}  // namespace test_tag_depends_on\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_DataBoxDocumentation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <map>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n\n// We want the code to be nicely formatted for the documentation, not here.\n// clang-format off\nnamespace {\ndouble working_without_databoxes_1() {\n// [working_without_databoxes_small_program_1]\n// Set up variables:\nconst double velocity = 4.0;\nconst double radius = 2.0;\nconst double density = 0.5;\nconst double volume = 10.0;\n\n// Use variables:\nconst double mass = density * volume;\nconst double acceleration = velocity * velocity / radius;\nreturn mass * acceleration;\n// [working_without_databoxes_small_program_1]\n}\n\n// [working_without_databoxes_mass_compute]\ndouble mass_compute(const double density, const double volume) {\n  return density * volume;\n}\n// [working_without_databoxes_mass_compute]\n\n// [working_without_databoxes_accel_compute]\ndouble acceleration_compute(\n    const double velocity, const double radius) {\n  return velocity * velocity / radius;\n}\n// [working_without_databoxes_accel_compute]\n\ndouble working_without_databoxes_2() {\n// [working_without_databoxes_small_program_2]\n// Set up variables:\nconst double velocity = 4.0;\nconst double radius = 2.0;\nconst double density = 0.5;\nconst double volume = 10.0;\n\n// Use variables:\nconst double mass = mass_compute(density, volume);\nconst double acceleration = acceleration_compute(velocity, radius);\nreturn mass * acceleration;\n// [working_without_databoxes_small_program_2]\n}\n\n// [working_without_databoxes_force_compute]\ndouble force_compute(const double velocity, const double radius,\n                     const double density, const double volume) {\n  const double mass = mass_compute(density, volume);\n  const double acceleration = acceleration_compute(velocity, radius);\n  return mass *  acceleration;\n}\n// [working_without_databoxes_force_compute]\n\nvoid failed_acceleration() {\n// [working_without_databoxes_failed_accel]\nconst double velocity = 4.0;\nconst double radius = 2.0;\nconst double acceleration = acceleration_compute(velocity, radius);\nconst double failed_acceleration = acceleration_compute(radius, velocity);\n// [working_without_databoxes_failed_accel]\n  CHECK(not(acceleration == failed_acceleration));\n}\n\ndouble std_map_databox_1() {\n// [std_map_databox_small_program_1]\n// Set up variables:\nconst double velocity = 4.0;\nconst double radius = 2.0;\nconst double density = 0.5;\nconst double volume = 10.0;\n\n// Set up databox:\nstd::map<std::string, double> naive_databox;\nnaive_databox[\"Velocity\"] = velocity;\nnaive_databox[\"Radius\"] = radius;\nnaive_databox[\"Density\"] = density;\nnaive_databox[\"Volume\"] = volume;\n// [std_map_databox_small_program_1]\n  return naive_databox[\"Density\"] * naive_databox[\"Volume\"] *\n         naive_databox[\"Velocity\"] * naive_databox[\"Velocity\"] /\n         naive_databox[\"Radius\"];\n}\n\n// [std_map_databox_mass_compute]\ndouble mass_compute(const std::map<std::string, double>& box) {\n  return box.at(\"Density\") * box.at(\"Volume\");\n}\n// [std_map_databox_mass_compute]\n\n// [std_map_databox_accel_compute]\ndouble acceleration_compute(const std::map<std::string, double>& box) {\n  return box.at(\"Velocity\") * box.at(\"Velocity\") / box.at(\"Radius\");\n}\n// [std_map_databox_accel_compute]\n\n// [std_map_databox_force_compute]\ndouble force_compute(const std::map<std::string, double>& box) {\n  const double mass = mass_compute(box);\n  const double acceleration = acceleration_compute(box);\n  return mass * acceleration;\n}\n// [std_map_databox_force_compute]\n\ndouble std_map_databox_2() {\n// [std_map_databox_small_program_2]\n// Set up variables:\nconst double velocity = 4.0;\nconst double radius = 2.0;\nconst double density = 0.5;\nconst double volume = 10.0;\n\n// Set up databox:\nstd::map<std::string, double> naive_databox;\nnaive_databox[\"Velocity\"] = velocity;\nnaive_databox[\"Radius\"] = radius;\nnaive_databox[\"Density\"] = density;\nnaive_databox[\"Volume\"] = volume;\n\n// Use variables:\nreturn force_compute(naive_databox);\n// [std_map_databox_small_program_2]\n}\n\nbool std_tuple_databox_example() {\n// [std_tuple_databox_1]\nstd::tuple<double, size_t, bool> sophomore_databox =\n  std::make_tuple(1.2, 8, true);\n// [std_tuple_databox_1]\n\n// [std_tuple_databox_2]\nconst bool bool_quantity = std::get<bool>(sophomore_databox);\n// value obtained is `true`\n// [std_tuple_databox_2]\nreturn bool_quantity;\n}\n\nnamespace sophomore {\n// [std_tuple_tags]\nstruct Velocity{};\nstruct Radius{};\nstruct Density{};\nstruct Volume{};\n// [std_tuple_tags]\n\ndouble std_tuple_databox_1() {\n// [std_tuple_small_program_1]\nstd::tuple<std::pair<Velocity,double>,\n           std::pair<Radius, double>, std::pair<Density, double>,\n           std::pair<Volume, double>> sophomore_databox =\n  std::make_tuple(std::make_pair(Velocity{}, 4.0),\n                  std::make_pair(Radius{}, 2.0), std::make_pair(Density{}, 0.5),\n                  std::make_pair(Volume{}, 10.0));\n// [std_tuple_small_program_1]\n  return std::get<std::pair<Density, double>>(sophomore_databox).second *\n         std::get<std::pair<Volume, double>>(sophomore_databox).second *\n         std::get<std::pair<Velocity, double>>(sophomore_databox).second *\n         std::get<std::pair<Velocity, double>>(sophomore_databox).second /\n         std::get<std::pair<Radius, double>>(sophomore_databox).second;\n}\n\n// [std_tuple_mass_compute]\ntemplate<typename... Pairs>\ndouble mass_compute(const std::tuple<Pairs...>& box) {\n  return std::get<std::pair<Density, double>>(box).second *\n         std::get<std::pair<Volume, double>>(box).second;\n}\n// [std_tuple_mass_compute]\n\n// [std_tuple_acceleration_compute]\ntemplate<typename... Pairs>\ndouble acceleration_compute(const std::tuple<Pairs...>& box) {\n  return std::get<std::pair<Velocity, double>>(box).second *\n         std::get<std::pair<Velocity, double>>(box).second /\n         std::get<std::pair<Radius, double>>(box).second;\n}\n// [std_tuple_acceleration_compute]\n\n// [std_tuple_force_compute]\ntemplate<typename... Pairs>\ndouble force_compute(const std::tuple<Pairs...>& box) {\n  const double mass = mass_compute(box);\n  const double acceleration = acceleration_compute(box);\n  return mass * acceleration;\n}\n// [std_tuple_force_compute]\n\ndouble std_tuple_databox_2() {\n// [std_tuple_small_program_2]\nstd::tuple<std::pair<Velocity,double>,\n           std::pair<Radius, double>, std::pair<Density, double>,\n           std::pair<Volume, double>> sophomore_databox =\n  std::make_tuple(std::make_pair(Velocity{}, 4.0),\n                  std::make_pair(Radius{}, 2.0), std::make_pair(Density{}, 0.5),\n                  std::make_pair(Volume{}, 10.0));\n\n// [std_tuple_small_program_2]\n  return force_compute(sophomore_databox);\n}\n} // namespace sophomore\n\nnamespace junior {\n// [tagged_tuple_tags]\nstruct Velocity {\n  using type = double;\n};\nstruct Radius {\n  using type = double;\n};\nstruct Density {\n  using type = double;\n};\nstruct Volume {\n  using type = double;\n};\n// [tagged_tuple_tags]\n\ndouble tagged_tuple_databox_1() {\n// [tagged_tuple_databox_1]\ntuples::TaggedTuple<Velocity, Radius, Density, Volume> junior_databox{\n  4.0, 2.0, 0.5, 10.0};\n// [tagged_tuple_databox_1]\n  return tuples::get<Density>(junior_databox) *\n         tuples::get<Volume>(junior_databox) *\n         tuples::get<Velocity>(junior_databox) *\n         tuples::get<Velocity>(junior_databox) /\n         tuples::get<Radius>(junior_databox);\n}\n\n// [tagged_tuple_mass_compute]\ntemplate<typename... Tags>\ndouble mass_compute(const tuples::TaggedTuple<Tags...>& box) {\n  return tuples::get<Density>(box) * tuples::get<Volume>(box);\n}\n// [tagged_tuple_mass_compute]\n\n// [tagged_tuple_acceleration_compute]\ntemplate<typename... Tags>\ndouble acceleration_compute(const tuples::TaggedTuple<Tags...>& box) {\n  return tuples::get<Velocity>(box) * tuples::get<Velocity>(box) /\n         tuples::get<Radius>(box);\n}\n// [tagged_tuple_acceleration_compute]\n\n// [tagged_tuple_force_compute]\ntemplate<typename... Tags>\ndouble force_compute(const tuples::TaggedTuple<Tags...>& box) {\n  const double mass = mass_compute(box);\n  const double acceleration = acceleration_compute(box);\n  return mass * acceleration;\n}\n// [tagged_tuple_force_compute]\n} // namespace junior\n\nnamespace proper{\n// [proper_databox_tags]\nstruct Velocity : db::SimpleTag {\n  using type = double;\n};\nstruct Radius : db::SimpleTag {\n  using type = double;\n};\nstruct Density : db::SimpleTag {\n  using type = double;\n};\nstruct Volume : db::SimpleTag {\n  using type = double;\n};\nstruct Mass : db::SimpleTag {\n  using type = double;\n};\n// [proper_databox_tags]\n\ndouble refined_databox_1() {\n// [refined_databox]\nconst auto refined_databox = db::create<\n    db::AddSimpleTags<\n      Velocity, Radius, Density, Volume>>(4.0, 2.0, 0.5, 10.0);\n// [refined_databox]\n// [refined_databox_get]\nconst double velocity = db::get<Velocity>(refined_databox);\n// [refined_databox_get]\nconst double radius = db::get<Radius>(refined_databox);\nconst double density = db::get<Density>(refined_databox);\nconst double volume = db::get<Volume>(refined_databox);\nreturn density * volume *  velocity * velocity / radius;\n}\n\nvoid mass_from_density_and_volume(const gsl::not_null<double*> result,\n  const double density, const double volume) {\n  *result = density * volume;\n}\n// [compute_tags]\nstruct MassCompute : db::ComputeTag, Mass {\n  using base = Mass;\n  using return_type = double;\n  static constexpr auto function = &mass_from_density_and_volume;\n  using argument_tags = tmpl::list<Density, Volume>;\n};\n// [compute_tags]\n\nvoid acceleration_from_velocity_and_radius(const gsl::not_null<double*> result,\n  const double velocity, const double radius) {\n  *result = velocity * velocity / radius;\n}\n\nstruct Acceleration : db::SimpleTag {\n  using type = double;\n};\n\nstruct AccelerationCompute : db::ComputeTag, Acceleration {\n  using base = Acceleration;\n  using return_type = double;\n  static constexpr auto function = &acceleration_from_velocity_and_radius;\n  using argument_tags = tmpl::list<Velocity, Radius>;\n};\n\n// [compute_tags_force_compute]\nstruct Force : db::SimpleTag {\n  using type = double;\n};\n\nstruct ForceCompute : db::ComputeTag, Force {\n  using base = Force;\n  using return_type = double;\n  static constexpr void function(const gsl::not_null<double*> result,\n    const double mass, const double acceleration) {\n    *result = mass * acceleration; }\n  using argument_tags = tmpl::list<Mass, Acceleration>;\n};\n// [compute_tags_force_compute]\n} // namespace proper\n\n// [mutate_tags]\nstruct Time : db::SimpleTag {\n  using type = double;\n};\n\nstruct TimeStep : db::SimpleTag {\n  using type = double;\n};\n\nstruct EarthGravity : db::SimpleTag {\n  using type = double;\n};\n\nstruct FallingSpeed : db::SimpleTag {\n  using type = double;\n};\n// [mutate_tags]\n\n// [intended_mutation]\nstruct IntendedMutation {\n  static void apply(const gsl::not_null<double*> time,\n     const gsl::not_null<double*> falling_speed,\n     const double time_step,\n     const double earth_gravity) {\n    *time += time_step;\n    *falling_speed += time_step * earth_gravity;\n  }\n};\n// [intended_mutation]\n\n// [intended_mutation2]\nstruct IntendedMutation2 {\n  using return_tags = tmpl::list<Time, FallingSpeed>;\n  using argument_tags = tmpl::list<TimeStep, EarthGravity>;\n\n  static void apply(const gsl::not_null<double*> time,\n     const gsl::not_null<double*> falling_speed,\n     const double time_step,\n     const double earth_gravity) {\n    *time += time_step;\n    *falling_speed += time_step * earth_gravity;\n  }\n};\n// [intended_mutation2]\n\n// [my_first_action]\ntemplate <typename Mutator>\nstruct MyFirstAction{\n  template<typename DbTagsList>\n  static void apply(\n    const gsl::not_null<db::DataBox<DbTagsList>*> time_dependent_databox)\n      {\n    db::mutate_apply<Mutator>(time_dependent_databox);\n  }\n};\n// [my_first_action]\n\n} // namespace\n// clang-format on\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataBox.Documentation\",\n                  \"[Unit][DataStructures]\") {\nconst double velocity = 4.0;\nconst double radius = 2.0;\nconst double density = 0.5;\nconst double volume = 10.0;\nconst double mass = mass_compute(density, volume);\nconst double acceleration = acceleration_compute(velocity, radius);\nconst double force = force_compute(velocity, radius, density, volume);\nCHECK(mass == density*volume);\nCHECK(acceleration == velocity * velocity / radius);\nCHECK(force == mass * acceleration);\nfailed_acceleration();\nCHECK(force == working_without_databoxes_1());\nCHECK(force == working_without_databoxes_2());\nCHECK(force == std_map_databox_1());\nCHECK(force == std_map_databox_2());\nstd::map<std::string, double> naive_databox;\nnaive_databox[\"Velocity\"] = velocity;\nnaive_databox[\"Radius\"] = radius;\nnaive_databox[\"Density\"] = density;\nnaive_databox[\"Volume\"] = volume;\nCHECK(mass == mass_compute(naive_databox));\nCHECK(acceleration == acceleration_compute(naive_databox));\nCHECK(force == force_compute(naive_databox));\nCHECK(std_tuple_databox_example());\nstd::tuple<std::pair<sophomore::Velocity, double>,\n           std::pair<sophomore::Radius, double>,\n           std::pair<sophomore::Density, double>,\n           std::pair<sophomore::Volume, double>>\n    sophomore_databox =\n        std::make_tuple(std::make_pair(sophomore::Velocity{}, 4.0),\n                        std::make_pair(sophomore::Radius{}, 2.0),\n                        std::make_pair(sophomore::Density{}, 0.5),\n                        std::make_pair(sophomore::Volume{}, 10.0));\nCHECK(mass == sophomore::mass_compute(sophomore_databox));\nCHECK(acceleration == sophomore::acceleration_compute(sophomore_databox));\nCHECK(force == sophomore::force_compute(sophomore_databox));\nCHECK(force == sophomore::std_tuple_databox_1());\nCHECK(force == sophomore::std_tuple_databox_2());\ntuples::TaggedTuple<junior::Velocity, junior::Radius, junior::Density,\n                    junior::Volume>\n    junior_databox{velocity, radius, density, volume};\nCHECK(mass == junior::mass_compute(junior_databox));\nCHECK(acceleration == junior::acceleration_compute(junior_databox));\nCHECK(force == junior::force_compute(junior_databox));\nCHECK(force == junior::tagged_tuple_databox_1());\nCHECK(force == proper::refined_databox_1());\nconst auto refined_databox = db::create<\n    db::AddSimpleTags<proper::Velocity, proper::Radius, proper::Density,\n                      proper::Volume>,\n    db::AddComputeTags<proper::MassCompute, proper::AccelerationCompute,\n                       proper::ForceCompute>>(velocity, radius, density,\n                                              volume);\nCHECK(force == db::get<proper::Force>(refined_databox));\n\n// [time_dep_databox]\nauto time_dependent_databox = db::create<\n    db::AddSimpleTags<\n      Time, TimeStep, EarthGravity, FallingSpeed>>(0.0, 0.1, -9.8, -10.0);\ndb::mutate_apply<\n  //MutateTags\n  tmpl::list<Time, FallingSpeed>,\n  //ArgumentTags\n  tmpl::list<TimeStep, EarthGravity>>(\n  [](const gsl::not_null<double*> time,\n     const gsl::not_null<double*> falling_speed,\n     const double time_step,\n     const double earth_gravity) {\n    *time += time_step;\n    *falling_speed += time_step * earth_gravity;\n  },\n  make_not_null(&time_dependent_databox));\n// [time_dep_databox]\nCHECK(0.0 + 0.1 == approx(db::get<Time>(time_dependent_databox)));\nCHECK(-10.0 + 0.1 * -9.8 ==\n  approx(db::get<FallingSpeed>(time_dependent_databox)));\n\n// [time_dep_databox2]\ndb::mutate_apply<\n  tmpl::list<Time, FallingSpeed>,\n  tmpl::list<TimeStep, EarthGravity>>(\n    IntendedMutation{}, make_not_null(&time_dependent_databox));\n// [time_dep_databox2]\nCHECK(0.0 + 0.2 == approx(db::get<Time>(time_dependent_databox)));\nCHECK(-10.0 + 0.2 * -9.8 ==\n  approx(db::get<FallingSpeed>(time_dependent_databox)));\n\n// [time_dep_databox3]\ndb::mutate_apply<IntendedMutation2>(make_not_null(&time_dependent_databox));\n// [time_dep_databox3]\nCHECK(0.0 + 0.3 == approx(db::get<Time>(time_dependent_databox)));\nCHECK(-10.0 + 0.3 * -9.8 ==\n  approx(db::get<FallingSpeed>(time_dependent_databox)));\n\n// [time_dep_databox4]\nMyFirstAction<IntendedMutation2>::apply(make_not_null(&time_dependent_databox));\n// [time_dep_databox4]\nCHECK(0.0 + 0.4 == approx(db::get<Time>(time_dependent_databox)));\nCHECK(-10.0 + 0.4 * -9.8 ==\n  approx(db::get<FallingSpeed>(time_dependent_databox)));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_DataBoxPrefixes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass DataVector;\n\nnamespace {\nstruct Tag : db::SimpleTag {\n  using type = double;\n};\n\nstruct TensorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, 2>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataBox.Prefixes\",\n                  \"[Unit][DataStructures]\") {\n  // [dt_name]\n  TestHelpers::db::test_prefix_tag<Tags::dt<Tag>>(\"dt(Tag)\");\n  // [dt_name]\n  using Dim = tmpl::size_t<2>;\n  using Frame = Frame::Inertial;\n  using VariablesTag = Tags::Variables<tmpl::list<TensorTag>>;\n  // [flux_name]\n  TestHelpers::db::test_prefix_tag<Tags::Flux<TensorTag, Dim, Frame>>(\n      \"Flux(TensorTag)\");\n  TestHelpers::db::test_prefix_tag<Tags::Flux<VariablesTag, Dim, Frame>>(\n      \"Flux(Variables(TensorTag))\");\n  // [flux_name]\n  // [source_name]\n  TestHelpers::db::test_prefix_tag<Tags::Source<Tag>>(\"Source(Tag)\");\n  // [source_name]\n  TestHelpers::db::test_prefix_tag<Tags::FixedSource<Tag>>(\"FixedSource(Tag)\");\n  // [initial_name]\n  TestHelpers::db::test_prefix_tag<Tags::Initial<Tag>>(\"Initial(Tag)\");\n  // [initial_name]\n  // [normal_dot_flux_name]\n  TestHelpers::db::test_prefix_tag<Tags::NormalDotFlux<Tag>>(\n      \"NormalDotFlux(Tag)\");\n  // [normal_dot_flux_name]\n  // [normal_dot_numerical_flux_name]\n  TestHelpers::db::test_prefix_tag<Tags::NormalDotNumericalFlux<Tag>>(\n      \"NormalDotNumericalFlux(Tag)\");\n  // [normal_dot_numerical_flux_name]\n  TestHelpers::db::test_prefix_tag<Tags::Previous<Tag>>(\"Previous(Tag)\");\n  // [next_name]\n  TestHelpers::db::test_prefix_tag<Tags::Next<Tag>>(\"Next(Tag)\");\n  // [next_name]\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_MetavariablesTag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename T>\nstruct TestMetavariables {\n  static constexpr size_t dim = Dim;\n  using stored_type = T;\n};\n\nnamespace Tags {\nstruct Dim : db::SimpleTag {\n  using type = size_t;\n};\n\nstruct DimCompute : Dim, db::ComputeTag {\n  using base = Dim;\n  template <typename Metavars>\n  static void function(gsl::not_null<size_t*> result,\n                       const Metavars& /*metavars*/) {\n    *result = Metavars::dim;\n  }\n  using argument_tags = tmpl::list<Parallel::Tags::Metavariables>;\n};\n\nstruct IsString : db::SimpleTag {\n  using type = bool;\n};\n}  // namespace Tags\n\nstruct Mutator : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<Tags::IsString>;\n  using argument_tags = tmpl::list<Parallel::Tags::Metavariables>;\n\n  template <typename Metavars>\n  static void apply(const gsl::not_null<bool*> is_string,\n                    const Metavars& /*meta*/) {\n    *is_string = std::is_same_v<typename Metavars::stored_type, std::string>;\n  }\n};\n\nstatic_assert(not db::is_compute_tag_v<Parallel::Tags::Metavariables>);\nstatic_assert(not db::is_reference_tag_v<Parallel::Tags::Metavariables>);\nstatic_assert(not db::is_immutable_item_tag_v<Parallel::Tags::Metavariables>);\nstatic_assert(not db::is_mutable_item_tag_v<Parallel::Tags::Metavariables>);\nstatic_assert(not db::is_simple_tag_v<Parallel::Tags::Metavariables>);\nstatic_assert(not db::is_creation_tag_v<Parallel::Tags::Metavariables>);\nstatic_assert(db::is_tag_v<Parallel::Tags::Metavariables>);\n\nstatic_assert(not db::is_compute_tag_v<\n              Parallel::Tags::MetavariablesImpl<TestMetavariables<11, int>>>);\nstatic_assert(not db::is_reference_tag_v<\n              Parallel::Tags::MetavariablesImpl<TestMetavariables<11, int>>>);\nstatic_assert(not db::is_immutable_item_tag_v<\n              Parallel::Tags::MetavariablesImpl<TestMetavariables<11, int>>>);\nstatic_assert(db::is_mutable_item_tag_v<\n              Parallel::Tags::MetavariablesImpl<TestMetavariables<11, int>>>);\nstatic_assert(db::is_simple_tag_v<\n              Parallel::Tags::MetavariablesImpl<TestMetavariables<11, int>>>);\nstatic_assert(db::is_creation_tag_v<\n              Parallel::Tags::MetavariablesImpl<TestMetavariables<11, int>>>);\nstatic_assert(db::is_tag_v<\n              Parallel::Tags::MetavariablesImpl<TestMetavariables<11, int>>>);\n\nstatic_assert(db::tag_is_retrievable_v<\n              Parallel::Tags::Metavariables,\n              db::DataBox<tmpl::list<Parallel::Tags::MetavariablesImpl<\n                  TestMetavariables<3, std::string>>>>>);\n\nstatic_assert(not db::tag_is_retrievable_v<Parallel::Tags::Metavariables,\n                                           db::DataBox<tmpl::list<Tags::Dim>>>);\n\nstatic_assert(std::is_same_v<\n              const TestMetavariables<9, char>&,\n              db::const_item_type<Parallel::Tags::Metavariables,\n                                  tmpl::list<Parallel::Tags::MetavariablesImpl<\n                                      TestMetavariables<9, char>>>>>);\n\nvoid test() {\n  auto box = db::create<db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<\n                                              TestMetavariables<2, double>>,\n                                          Tags::IsString>,\n                        db::AddComputeTags<Tags::DimCompute>>();\n  // Test get\n  static_assert(std::is_same_v<\n                TestMetavariables<2, double>,\n                typename std::decay_t<\n                    decltype(db::get<Parallel::Tags::Metavariables>(box))>>);\n\n  // Test compute item\n  CHECK(db::get<Tags::Dim>(box) == 2);\n\n  db::mutate<Tags::IsString>(\n      [](const gsl::not_null<bool*> is_string) { *is_string = true; },\n      make_not_null(&box));\n  CHECK(db::get<Tags::IsString>(box));\n\n  // Test mutator\n  db::mutate_apply(Mutator{}, make_not_null(&box));\n  CHECK_FALSE(db::get<Tags::IsString>(box));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataBox.MetavarialbesTag\",\n                  \"[Unit][DataStructures]\") {\n  TestHelpers::db::test_simple_tag<\n      Parallel::Tags::MetavariablesImpl<TestMetavariables<7, int>>>(\n      \"Metavariables\");\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_ObservationBox.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid multiply_by_two(const gsl::not_null<double*> result, const double value) {\n  *result = 2.0 * value;\n}\n\nstruct Tag0 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Tag1 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Tag1Compute : Tag1, db::ComputeTag {\n  using base = Tag1;\n  using return_type = double;\n  static constexpr auto function = multiply_by_two;\n  using argument_tags = tmpl::list<Tag0>;\n};\n\nstruct Tag2 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Tag2Compute : Tag2, db::ComputeTag {\n  using base = Tag2;\n  using return_type = double;\n  static constexpr auto function = multiply_by_two;\n  using argument_tags = tmpl::list<Tag1>;\n};\n\nstruct Tag3 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Tag3Compute : Tag3, db::ComputeTag {\n  using base = Tag3;\n  using return_type = double;\n  static auto function(const gsl::not_null<double*> result, const double value0,\n                       const double value1, const double value2) {\n    ++times_called;\n    *result = value0 + value1 + value2;\n  }\n  using argument_tags = tmpl::list<Tag0, Tag1, Tag2>;\n  static size_t times_called;\n};\n\nsize_t Tag3Compute::times_called = 0;\n\nstruct ScalarVar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct VectorVar : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n};\n\nstruct VarsCompute : db::ComputeTag,\n                     ::Tags::Variables<tmpl::list<ScalarVar, VectorVar>> {\n  using base = ::Tags::Variables<tmpl::list<ScalarVar, VectorVar>>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<>;\n  static void function(\n      const gsl::not_null<::Variables<tmpl::list<ScalarVar, VectorVar>>*>\n          result) {\n    result->initialize(5, 1.0);\n    for (size_t i = 0; i < 3; ++i) {\n      get<VectorVar>(*result).get(i) += static_cast<double>(i) + 1.0;\n    }\n  }\n};\n\nstruct Pointer : db::SimpleTag {\n  using type = std::unique_ptr<double>;\n};\n\n// Test that we can return values from the apply functions and that we can\n// retrieve both the nested DataBox and the ObservationBox via the argument\n// tags.\nstruct SubtractNumberAndReturn {\n  using argument_tags =\n      tmpl::list<::Tags::ObservationBox, ::Tags::DataBox, Tag3, Pointer>;\n\n  template <typename DbTagsList, typename ComputeTagsList>\n  double operator()(\n      const ObservationBox<ComputeTagsList, db::DataBox<DbTagsList>>& obs_box,\n      const db::DataBox<DbTagsList>& box, const double tag3,\n      const double pointed, const double number) const {\n    CHECK(get<Tag0>(obs_box) == 2.0);\n    CHECK(get<Tag1>(obs_box) == 4.0);\n    CHECK(get<Tag2>(obs_box) == (2.0 * 2.0 * 2.0));\n    CHECK(get<Tag3>(obs_box) == (2.0 + 2.0 * 2.0 + 2.0 * 2.0 * 2.0));\n    CHECK(get<Tag0>(box) == 2.0);\n    CHECK(get<Tag1>(box) == 4.0);\n    CHECK(pointed == 2.0);\n    return tag3 - number;\n  }\n};\n\nstruct SubtractNumberAndReturnApply {\n  using argument_tags =\n      tmpl::list<::Tags::ObservationBox, ::Tags::DataBox, Tag3, Pointer>;\n\n  template <typename DbTagsList, typename ComputeTagsList>\n  static double apply(\n      const ObservationBox<ComputeTagsList, db::DataBox<DbTagsList>>& obs_box,\n      const db::DataBox<DbTagsList>& box, const double tag3,\n      const double pointed, const double number) {\n    CHECK(get<Tag0>(obs_box) == 2.0);\n    CHECK(get<Tag1>(obs_box) == 4.0);\n    CHECK(get<Tag2>(obs_box) == (2.0 * 2.0 * 2.0));\n    CHECK(get<Tag3>(obs_box) == (2.0 + 2.0 * 2.0 + 2.0 * 2.0 * 2.0));\n    CHECK(get<Tag0>(box) == 2.0);\n    CHECK(get<Tag1>(box) == 4.0);\n    CHECK(pointed == 2.0);\n    return tag3 - number;\n  }\n};\n\nstruct MutateAndReturn {\n  using return_tags = tmpl::list<Tag0, Pointer>;\n  using argument_tags = tmpl::list<Tag3, Pointer>;\n\n  double operator()(\n      const gsl::not_null<double*> tag0,\n      const gsl::not_null<std::unique_ptr<double>*> mutable_pointed,\n      const double tag3, const double& const_pointed, const double expected0,\n      const double new0) const {\n    CHECK(*tag0 == expected0);\n    CHECK(tag3 == (expected0 + 2.0 * expected0 + 2.0 * 2.0 * expected0));\n    *tag0 = new0;\n    CHECK(const_pointed == expected0);\n    **mutable_pointed = new0;\n    CHECK(const_pointed == new0);\n    return expected0;\n  }\n};\n\nstruct MutateAndReturnApply {\n  using return_tags = tmpl::list<Tag0, Pointer>;\n  using argument_tags = tmpl::list<Tag3, Pointer>;\n\n  static double apply(\n      const gsl::not_null<double*> tag0,\n      const gsl::not_null<std::unique_ptr<double>*> mutable_pointed,\n      const double tag3, const double& const_pointed, const double expected0,\n      const double new0) {\n    CHECK(*tag0 == expected0);\n    CHECK(tag3 == (expected0 + 2.0 * expected0 + 2.0 * 2.0 * expected0));\n    *tag0 = new0;\n    CHECK(const_pointed == expected0);\n    **mutable_pointed = new0;\n    CHECK(const_pointed == new0);\n    return expected0;\n  }\n};\n\nstruct MutateAndReturnWithoutLists {\n  double operator()(\n      const gsl::not_null<double*> tag0,\n      const gsl::not_null<std::unique_ptr<double>*> mutable_pointed,\n      const double tag3, const double& const_pointed, const double expected0,\n      const double new0) const {\n    CHECK(*tag0 == expected0);\n    CHECK(tag3 == (expected0 + 2.0 * expected0 + 2.0 * 2.0 * expected0));\n    *tag0 = new0;\n    CHECK(const_pointed == expected0);\n    **mutable_pointed = new0;\n    CHECK(const_pointed == new0);\n    return expected0;\n  }\n};\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataBox.ObservationBox\",\n                  \"[Unit][DataStructures]\") {\n  auto db_box = db::create<db::AddSimpleTags<Tag0, Pointer>,\n                           db::AddComputeTags<Tag1Compute>>(\n      2.0, std::make_unique<double>(2.0));\n  auto obs_box = make_observation_box<\n      db::AddComputeTags<Tag2Compute, Tag3Compute, VarsCompute>>(\n      make_not_null(&db_box));\n  CHECK(Tag3Compute::times_called == 0);\n  CHECK(get<Tag0>(obs_box) == 2.0);\n  CHECK(get<Tag1>(obs_box) == 4.0);\n  CHECK(get<Tag2>(obs_box) == (2.0 * 2.0 * 2.0));\n  CHECK(get<Tag3>(obs_box) == (2.0 + 2.0 * 2.0 + 2.0 * 2.0 * 2.0));\n  CHECK(Tag3Compute::times_called == 1);\n  // Call a second time to make sure the compute tag didn't get evaluated again.\n  CHECK(get<Tag3>(obs_box) == (2.0 + 2.0 * 2.0 + 2.0 * 2.0 * 2.0));\n  CHECK(Tag3Compute::times_called == 1);\n  CHECK(&db_box == &get<::Tags::DataBox>(obs_box));\n  CHECK(&obs_box == &get<::Tags::ObservationBox>(obs_box));\n\n  CHECK(apply(SubtractNumberAndReturn{}, obs_box, 5.0) ==\n        (get<Tag3>(obs_box) - 5.0));\n  CHECK(get(get<ScalarVar>(obs_box)) == DataVector(5, 1.0));\n  CHECK(get<0>(get<VectorVar>(obs_box)) == DataVector(5, 2.0));\n  CHECK(get<1>(get<VectorVar>(obs_box)) == DataVector(5, 3.0));\n  CHECK(get<2>(get<VectorVar>(obs_box)) == DataVector(5, 4.0));\n\n  CHECK(apply(SubtractNumberAndReturnApply{}, obs_box, 2.0) ==\n        (get<Tag3>(obs_box) - 2.0));\n\n  CHECK(Tag3Compute::times_called == 1);\n  CHECK(get<Pointer>(obs_box) == 2.0);\n  CHECK(mutate_apply(MutateAndReturn{}, make_not_null(&obs_box), 2.0, 3.0) ==\n        2.0);\n  CHECK(Tag3Compute::times_called == 1);\n  CHECK(get<Pointer>(obs_box) == 3.0);\n  CHECK(mutate_apply(MutateAndReturnApply{}, make_not_null(&obs_box), 3.0,\n                     4.0) == 3.0);\n  CHECK(Tag3Compute::times_called == 2);\n  CHECK(get<Pointer>(obs_box) == 4.0);\n  CHECK(mutate_apply<MutateAndReturn::return_tags,\n                     MutateAndReturn::argument_tags>(\n            MutateAndReturnWithoutLists{}, make_not_null(&obs_box), 4.0, 5.0) ==\n        4.0);\n  CHECK(Tag3Compute::times_called == 3);\n  CHECK(get<Tag3>(obs_box) == (5.0 + 2.0 * 5.0 + 2.0 * 2.0 * 5.0));\n  CHECK(Tag3Compute::times_called == 4);\n}\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_PrefixHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nclass DataVector;\ntemplate <typename TagsList>\nclass Variables;\n\nnamespace {\nusing Var = TestHelpers::Tags::Scalar<>;\n\nusing Var2 = TestHelpers::Tags::Scalar2<>;\n\ntemplate <typename Tag>\nusing Prefix = TestHelpers::Tags::Prefix1<Tag>;\n\ntemplate <typename Tag>\nusing Prefix2 = TestHelpers::Tags::Prefix2<Tag>;\n\ntemplate <typename Arg0, typename Arg1, typename Arg2>\nstruct SomeType;\n\ntemplate <typename Tag, typename Arg1, typename Arg2>\nstruct PrefixWithArgs : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = SomeType<typename Tag::type, Arg1, Arg2>;\n};\n\nusing vars_list = tmpl::list<Var, Var2>;\nusing prefix_vars_list = tmpl::list<Prefix<Var>, Prefix<Var2>>;\nusing double_prefix_vars_list =\n    tmpl::list<Prefix<Prefix<Var>>, Prefix<Prefix<Var2>>>;\nusing both_prefix_vars_list =\n    tmpl::list<Prefix2<Prefix<Var>>, Prefix2<Prefix<Var2>>>;\n\ntemplate <typename Arg1, typename Arg2>\nusing args_vars_list = tmpl::list<PrefixWithArgs<Var, Arg1, Arg2>,\n                                  PrefixWithArgs<Var2, Arg1, Arg2>>;\n\ntemplate <typename Arg1, typename Arg2>\nusing args_prefix_vars_list =\n    tmpl::list<PrefixWithArgs<Prefix<Var>, Arg1, Arg2>,\n               PrefixWithArgs<Prefix<Var2>, Arg1, Arg2>>;\n\ntemplate <typename Arg1, typename Arg2>\nusing prefix_args_vars_list =\n    tmpl::list<Prefix<PrefixWithArgs<Var, Arg1, Arg2>>,\n               Prefix<PrefixWithArgs<Var2, Arg1, Arg2>>>;\n}  // namespace\n\n// Test db::wrap_tags_in\nstatic_assert(\n    std::is_same_v<db::wrap_tags_in<Prefix, vars_list>, prefix_vars_list>);\nstatic_assert(\n    std::is_same_v<db::wrap_tags_in<PrefixWithArgs, vars_list, int, double>,\n                   args_vars_list<int, double>>);\nstatic_assert(std::is_same_v<db::wrap_tags_in<Prefix, prefix_vars_list>,\n                             double_prefix_vars_list>);\nstatic_assert(std::is_same_v<db::wrap_tags_in<Prefix2, prefix_vars_list>,\n                             both_prefix_vars_list>);\nstatic_assert(std::is_same_v<\n              db::wrap_tags_in<PrefixWithArgs, prefix_vars_list, int, double>,\n              args_prefix_vars_list<int, double>>);\nstatic_assert(\n    std::is_same_v<db::wrap_tags_in<Prefix, args_vars_list<int, double>>,\n                   prefix_args_vars_list<int, double>>);\n\n// Test db::add_tag_prefix on tag\nstatic_assert(std::is_same_v<db::add_tag_prefix<Prefix, Var>, Prefix<Var>>);\nstatic_assert(\n    std::is_same_v<db::add_tag_prefix<PrefixWithArgs, Var, int, double>,\n                   PrefixWithArgs<Var, int, double>>);\nstatic_assert(std::is_same_v<db::add_tag_prefix<Prefix, Prefix<Var>>,\n                             Prefix<Prefix<Var>>>);\nstatic_assert(std::is_same_v<db::add_tag_prefix<Prefix2, Prefix<Var>>,\n                             Prefix2<Prefix<Var>>>);\nstatic_assert(\n    std::is_same_v<db::add_tag_prefix<Prefix, PrefixWithArgs<Var, int, double>>,\n                   Prefix<PrefixWithArgs<Var, int, double>>>);\nstatic_assert(\n    std::is_same_v<db::add_tag_prefix<PrefixWithArgs, Prefix<Var>, int, double>,\n                   PrefixWithArgs<Prefix<Var>, int, double>>);\n\nnamespace {\nusing vars_tag = Tags::Variables<vars_list>;\nusing prefix_vars_tag = Tags::Variables<prefix_vars_list>;\nusing double_prefix_vars_tag = Tags::Variables<double_prefix_vars_list>;\nusing both_prefix_vars_tag = Tags::Variables<both_prefix_vars_list>;\ntemplate <typename Arg1, typename Arg2>\nusing args_vars_tag = Tags::Variables<args_vars_list<Arg1, Arg2>>;\ntemplate <typename Arg1, typename Arg2>\nusing args_prefix_vars_tag = Tags::Variables<args_prefix_vars_list<Arg1, Arg2>>;\ntemplate <typename Arg1, typename Arg2>\nusing prefix_args_vars_tag = Tags::Variables<prefix_args_vars_list<Arg1, Arg2>>;\n}  // namespace\n\n// Test db::add_tag_prefix on Variables tag\nstatic_assert(\n    std::is_same_v<db::add_tag_prefix<Prefix, vars_tag>, prefix_vars_tag>);\nstatic_assert(\n    std::is_same_v<db::add_tag_prefix<PrefixWithArgs, vars_tag, int, double>,\n                   args_vars_tag<int, double>>);\nstatic_assert(std::is_same_v<db::add_tag_prefix<Prefix, prefix_vars_tag>,\n                             double_prefix_vars_tag>);\nstatic_assert(std::is_same_v<db::add_tag_prefix<Prefix2, prefix_vars_tag>,\n                             both_prefix_vars_tag>);\nstatic_assert(\n    std::is_same_v<db::add_tag_prefix<Prefix, args_vars_tag<int, double>>,\n                   prefix_args_vars_tag<int, double>>);\nstatic_assert(std::is_same_v<\n              db::add_tag_prefix<PrefixWithArgs, prefix_vars_tag, int, double>,\n              args_prefix_vars_tag<int, double>>);\n\n// Test db::remove_tag_prefix on tag\nstatic_assert(std::is_same_v<db::remove_tag_prefix<Prefix<Var>>, Var>);\nstatic_assert(std::is_same_v<\n              db::remove_tag_prefix<PrefixWithArgs<Var, int, double>>, Var>);\nstatic_assert(\n    std::is_same_v<db::remove_tag_prefix<Prefix<Prefix<Var>>>, Prefix<Var>>);\nstatic_assert(\n    std::is_same_v<db::remove_tag_prefix<Prefix2<Prefix<Var>>>, Prefix<Var>>);\nstatic_assert(std::is_same_v<\n              db::remove_tag_prefix<Prefix<PrefixWithArgs<Var, int, double>>>,\n              PrefixWithArgs<Var, int, double>>);\nstatic_assert(std::is_same_v<\n              db::remove_tag_prefix<PrefixWithArgs<Prefix<Var>, int, double>>,\n              Prefix<Var>>);\n\n// Test db::remove_tag_prefix on Variables tag\nstatic_assert(std::is_same_v<db::remove_tag_prefix<prefix_vars_tag>, vars_tag>);\nstatic_assert(std::is_same_v<db::remove_tag_prefix<args_vars_tag<int, double>>,\n                             vars_tag>);\nstatic_assert(std::is_same_v<db::remove_tag_prefix<double_prefix_vars_tag>,\n                             prefix_vars_tag>);\nstatic_assert(std::is_same_v<db::remove_tag_prefix<both_prefix_vars_tag>,\n                             prefix_vars_tag>);\nstatic_assert(\n    std::is_same_v<db::remove_tag_prefix<prefix_args_vars_tag<int, double>>,\n                   args_vars_tag<int, double>>);\nstatic_assert(\n    std::is_same_v<db::remove_tag_prefix<args_prefix_vars_tag<int, double>>,\n                   prefix_vars_tag>);\n\n// Test db::remove_all_prefixes on tag\nstatic_assert(std::is_same_v<db::remove_all_prefixes<Prefix<Var>>, Var>);\nstatic_assert(std::is_same_v<\n              db::remove_all_prefixes<PrefixWithArgs<Var, int, double>>, Var>);\nstatic_assert(\n    std::is_same_v<db::remove_all_prefixes<Prefix<Prefix<Var>>>, Var>);\nstatic_assert(\n    std::is_same_v<db::remove_all_prefixes<Prefix2<Prefix<Var>>>, Var>);\nstatic_assert(std::is_same_v<\n              db::remove_all_prefixes<Prefix<PrefixWithArgs<Var, int, double>>>,\n              Var>);\nstatic_assert(std::is_same_v<\n              db::remove_all_prefixes<PrefixWithArgs<Prefix<Var>, int, double>>,\n              Var>);\n\n// Test db::remove_all_prefixes on Variables tag\nstatic_assert(\n    std::is_same_v<db::remove_all_prefixes<prefix_vars_tag>, vars_tag>);\nstatic_assert(std::is_same_v<\n              db::remove_all_prefixes<args_vars_tag<int, double>>, vars_tag>);\nstatic_assert(\n    std::is_same_v<db::remove_all_prefixes<double_prefix_vars_tag>, vars_tag>);\nstatic_assert(\n    std::is_same_v<db::remove_all_prefixes<both_prefix_vars_tag>, vars_tag>);\nstatic_assert(\n    std::is_same_v<db::remove_all_prefixes<prefix_args_vars_tag<int, double>>,\n                   vars_tag>);\nstatic_assert(\n    std::is_same_v<db::remove_all_prefixes<args_prefix_vars_tag<int, double>>,\n                   vars_tag>);\n\nstatic_assert(std::is_same_v<db::prefix_variables<Prefix, double>, double>);\nstatic_assert(\n    std::is_same_v<db::prefix_variables<Prefix, SomeType<int, int, int>>,\n                   SomeType<int, int, int>>);\nstatic_assert(\n    std::is_same_v<db::prefix_variables<PrefixWithArgs, SomeType<int, int, int>,\n                                        float, char>,\n                   SomeType<int, int, int>>);\nstatic_assert(std::is_same_v<db::prefix_variables<Prefix, Variables<vars_list>>,\n                             Variables<prefix_vars_list>>);\nstatic_assert(\n    std::is_same_v<db::prefix_variables<Prefix, Variables<prefix_vars_list>>,\n                   Variables<double_prefix_vars_list>>);\nstatic_assert(\n    std::is_same_v<\n        db::prefix_variables<PrefixWithArgs, Variables<vars_list>, float, char>,\n        Variables<args_vars_list<float, char>>>);\n\nstatic_assert(std::is_same_v<db::unprefix_variables<double>, double>);\nstatic_assert(std::is_same_v<db::unprefix_variables<SomeType<int, int, int>>,\n                             SomeType<int, int, int>>);\nstatic_assert(std::is_same_v<db::unprefix_variables<SomeType<int, int, int>>,\n                             SomeType<int, int, int>>);\nstatic_assert(\n    std::is_same_v<db::unprefix_variables<Variables<prefix_vars_list>>,\n                   Variables<vars_list>>);\nstatic_assert(\n    std::is_same_v<db::unprefix_variables<Variables<double_prefix_vars_list>>,\n                   Variables<prefix_vars_list>>);\nstatic_assert(std::is_same_v<\n              db::unprefix_variables<Variables<args_vars_list<float, char>>>,\n              Variables<vars_list>>);\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_Protocols.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"Helpers/DataStructures/DataBox/Examples.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nstatic_assert(tt::assert_conforms_to_v<db::TestHelpers::ExampleMutator,\n                                       db::protocols::Mutator>);\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_TagName.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestTags.hpp\"\n\nnamespace {\nstruct NamedSimple : db::SimpleTag {\n  static std::string name() { return \"NameOfSimple\"; }\n};\n\nstruct SimpleNamedCompute : TestHelpers::db::Tags::Simple, db::ComputeTag {\n  static std::string name() { return \"NameOfSimpleCompute\"; }\n};\n\nstruct NamedSimpleNamedCompute : NamedSimple, db::ComputeTag {\n  static std::string name() { return \"NameOfNamedSimpleCompute\"; }\n};\n\nstruct NamedSimpleCompute : NamedSimple, db::ComputeTag {\n  using base = NamedSimple;\n};\n\nstruct SimpleNamedReference : TestHelpers::db::Tags::Simple, db::ReferenceTag {\n  static std::string name() { return \"NameOfSimpleReference\"; }\n};\n\nstruct NamedSimpleNamedReference : NamedSimple, db::ReferenceTag {\n  static std::string name() { return \"NameOfNamedSimpleReference\"; }\n};\n\nstruct NamedSimpleReference : NamedSimple, db::ReferenceTag {\n  using base = NamedSimple;\n};\n\ntemplate <typename Tag>\nstruct NamedLabel : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  static std::string name() {\n    return \"NameOfLabel(\" + db::tag_name<Tag>() + \")\";\n  }\n};\n\ntemplate <typename Tag>\nstruct LabelNamedCompute : TestHelpers::db::Tags::Label<Tag>, db::ComputeTag {\n  using tag = Tag;\n  static std::string name() {\n    return \"NameOfLabelCompute(\" + db::tag_name<Tag>() + \")\";\n  }\n};\n\ntemplate <typename Tag>\nstruct NamedLabelCompute : NamedLabel<Tag>, db::ComputeTag {\n  using base = NamedLabel<Tag>;\n  using tag = Tag;\n};\n\ntemplate <typename Tag>\nstruct NamedLabelNamedCompute : NamedLabel<Tag>, db::ComputeTag {\n  using tag = Tag;\n  static std::string name() {\n    return \"NameOfNamedLabelCompute(\" + db::tag_name<Tag>() + \")\";\n  }\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataBox.TagName\",\n                  \"[Unit][DataStructures]\") {\n  CHECK(db::tag_name<TestHelpers::db::Tags::Simple>() == \"Simple\");\n  CHECK(db::tag_name<TestHelpers::db::Tags::SimpleCompute>() == \"Simple\");\n  CHECK(db::tag_name<TestHelpers::db::Tags::SimpleReference>() == \"Simple\");\n  CHECK(db::tag_name<NamedSimple>() == \"NameOfSimple\");\n  CHECK(db::tag_name<SimpleNamedCompute>() == \"NameOfSimpleCompute\");\n  CHECK(db::tag_name<NamedSimpleNamedCompute>() == \"NameOfNamedSimpleCompute\");\n  CHECK(db::tag_name<NamedSimpleCompute>() == \"NameOfSimple\");\n  CHECK(db::tag_name<SimpleNamedReference>() == \"NameOfSimpleReference\");\n  CHECK(db::tag_name<NamedSimpleNamedReference>() ==\n        \"NameOfNamedSimpleReference\");\n  CHECK(db::tag_name<NamedSimpleReference>() == \"NameOfSimple\");\n\n  CHECK(db::tag_name<\n            TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>() ==\n        \"Label(Simple)\");\n  CHECK(db::tag_name<TestHelpers::db::Tags::Label<\n            TestHelpers::db::Tags::SimpleCompute>>() == \"Label(Simple)\");\n  CHECK(db::tag_name<TestHelpers::db::Tags::Label<NamedSimple>>() ==\n        \"Label(NameOfSimple)\");\n  CHECK(db::tag_name<TestHelpers::db::Tags::Label<SimpleNamedCompute>>() ==\n        \"Label(NameOfSimpleCompute)\");\n  CHECK(db::tag_name<TestHelpers::db::Tags::Label<NamedSimpleNamedCompute>>() ==\n        \"Label(NameOfNamedSimpleCompute)\");\n  CHECK(db::tag_name<TestHelpers::db::Tags::Label<NamedSimpleCompute>>() ==\n        \"Label(NameOfSimple)\");\n\n  CHECK(db::tag_name<NamedLabel<TestHelpers::db::Tags::Simple>>() ==\n        \"NameOfLabel(Simple)\");\n  CHECK(db::tag_name<LabelNamedCompute<TestHelpers::db::Tags::Simple>>() ==\n        \"NameOfLabelCompute(Simple)\");\n  CHECK(db::tag_name<NamedLabelCompute<TestHelpers::db::Tags::Simple>>() ==\n        \"NameOfLabel(Simple)\");\n  CHECK(db::tag_name<NamedLabelNamedCompute<TestHelpers::db::Tags::Simple>>() ==\n        \"NameOfNamedLabelCompute(Simple)\");\n\n  CHECK(db::tag_name<NamedLabel<NamedSimple>>() == \"NameOfLabel(NameOfSimple)\");\n  CHECK(db::tag_name<LabelNamedCompute<NamedSimple>>() ==\n        \"NameOfLabelCompute(NameOfSimple)\");\n  CHECK(db::tag_name<NamedLabelCompute<NamedSimple>>() ==\n        \"NameOfLabel(NameOfSimple)\");\n  CHECK(db::tag_name<NamedLabelNamedCompute<NamedSimple>>() ==\n        \"NameOfNamedLabelCompute(NameOfSimple)\");\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_TagTraits.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestTags.hpp\"\n\nstatic_assert(not db::is_compute_tag_v<TestHelpers::db::Tags::Bad>);\nstatic_assert(not db::is_compute_tag_v<TestHelpers::db::Tags::Simple>);\nstatic_assert(db::is_compute_tag_v<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(not db::is_compute_tag_v<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(not db::is_compute_tag_v<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::compute_tag<TestHelpers::db::Tags::Bad>);\nstatic_assert(not db::compute_tag<TestHelpers::db::Tags::Simple>);\nstatic_assert(db::compute_tag<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(not db::compute_tag<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(not db::compute_tag<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::is_reference_tag_v<TestHelpers::db::Tags::Bad>);\nstatic_assert(not db::is_reference_tag_v<TestHelpers::db::Tags::Simple>);\nstatic_assert(not db::is_reference_tag_v<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(db::is_reference_tag_v<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(not db::is_reference_tag_v<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::reference_tag<TestHelpers::db::Tags::Bad>);\nstatic_assert(not db::reference_tag<TestHelpers::db::Tags::Simple>);\nstatic_assert(not db::reference_tag<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(db::reference_tag<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(not db::reference_tag<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::is_immutable_item_tag_v<TestHelpers::db::Tags::Bad>);\nstatic_assert(not db::is_immutable_item_tag_v<TestHelpers::db::Tags::Simple>);\nstatic_assert(\n    db::is_immutable_item_tag_v<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(\n    db::is_immutable_item_tag_v<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(not db::is_immutable_item_tag_v<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::immutable_item_tag<TestHelpers::db::Tags::Bad>);\nstatic_assert(not db::immutable_item_tag<TestHelpers::db::Tags::Simple>);\nstatic_assert(db::immutable_item_tag<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(db::immutable_item_tag<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(not db::immutable_item_tag<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::is_mutable_item_tag_v<TestHelpers::db::Tags::Bad>);\nstatic_assert(db::is_mutable_item_tag_v<TestHelpers::db::Tags::Simple>);\nstatic_assert(\n    not db::is_mutable_item_tag_v<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(\n    not db::is_mutable_item_tag_v<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(db::is_mutable_item_tag_v<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::mutable_item_tag<TestHelpers::db::Tags::Bad>);\nstatic_assert(db::mutable_item_tag<TestHelpers::db::Tags::Simple>);\nstatic_assert(not db::mutable_item_tag<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(not db::mutable_item_tag<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(db::mutable_item_tag<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::is_simple_tag_v<TestHelpers::db::Tags::Bad>);\nstatic_assert(db::is_simple_tag_v<TestHelpers::db::Tags::Simple>);\nstatic_assert(not db::is_simple_tag_v<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(not db::is_simple_tag_v<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(db::is_simple_tag_v<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::simple_tag<TestHelpers::db::Tags::Bad>);\nstatic_assert(db::simple_tag<TestHelpers::db::Tags::Simple>);\nstatic_assert(not db::simple_tag<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(not db::simple_tag<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(db::simple_tag<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::is_creation_tag_v<TestHelpers::db::Tags::Bad>);\nstatic_assert(db::is_creation_tag_v<TestHelpers::db::Tags::Simple>);\nstatic_assert(db::is_creation_tag_v<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(db::is_creation_tag_v<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(db::is_creation_tag_v<\n              TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::is_tag_v<TestHelpers::db::Tags::Bad>);\nstatic_assert(db::is_tag_v<TestHelpers::db::Tags::Simple>);\nstatic_assert(db::is_tag_v<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(db::is_tag_v<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(\n    db::is_tag_v<TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nstatic_assert(not db::tag<TestHelpers::db::Tags::Bad>);\nstatic_assert(db::tag<TestHelpers::db::Tags::Simple>);\nstatic_assert(db::tag<TestHelpers::db::Tags::SimpleCompute>);\nstatic_assert(db::tag<TestHelpers::db::Tags::SimpleReference>);\nstatic_assert(\n    db::tag<TestHelpers::db::Tags::Label<TestHelpers::db::Tags::Simple>>);\n\nnamespace {\nenum class TagType1 { Simple, Compute, Reference, Other };\n\ntemplate <db::tag Tag>\nstruct ConceptTest1 {\n  static constexpr TagType1 value = TagType1::Other;\n};\n\ntemplate <db::simple_tag Tag>\nstruct ConceptTest1<Tag> {\n  static constexpr TagType1 value = TagType1::Simple;\n};\n\ntemplate <db::compute_tag Tag>\nstruct ConceptTest1<Tag> {\n  static constexpr TagType1 value = TagType1::Compute;\n};\n\ntemplate <db::reference_tag Tag>\nstruct ConceptTest1<Tag> {\n  static constexpr TagType1 value = TagType1::Reference;\n};\n\nstatic_assert(ConceptTest1<TestHelpers::db::Tags::Simple>::value ==\n              TagType1::Simple);\nstatic_assert(ConceptTest1<TestHelpers::db::Tags::SimpleCompute>::value ==\n              TagType1::Compute);\nstatic_assert(ConceptTest1<TestHelpers::db::Tags::SimpleReference>::value ==\n              TagType1::Reference);\nstatic_assert(ConceptTest1<Parallel::Tags::Metavariables>::value ==\n              TagType1::Other);\n\nenum class TagType2 { Compute, Immutable, Tag };\n\ntemplate <db::tag Tag>\nstruct ConceptTest2 {\n  static constexpr TagType2 value = TagType2::Tag;\n};\n\ntemplate <db::immutable_item_tag Tag>\nstruct ConceptTest2<Tag> {\n  static constexpr TagType2 value = TagType2::Immutable;\n};\n\ntemplate <db::compute_tag Tag>\nstruct ConceptTest2<Tag> {\n  static constexpr TagType2 value = TagType2::Compute;\n};\n\nstatic_assert(ConceptTest2<TestHelpers::db::Tags::Simple>::value ==\n              TagType2::Tag);\nstatic_assert(ConceptTest2<TestHelpers::db::Tags::SimpleCompute>::value ==\n              TagType2::Compute);\nstatic_assert(ConceptTest2<TestHelpers::db::Tags::SimpleReference>::value ==\n              TagType2::Immutable);\nstatic_assert(ConceptTest2<Parallel::Tags::Metavariables>::value ==\n              TagType2::Tag);\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestTags.hpp\"\n\nnamespace {\nstruct NamedSimple : db::SimpleTag {\n  static std::string name() { return \"NamedSimpleName\"; }\n  using type = int;\n};\n\nstruct SimpleFromOption : TestHelpers::db::Tags::Simple {\n  using base = TestHelpers::db::Tags::Simple;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataBox.TestHelpers\",\n                  \"[Unit][DataStructures]\") {\n  // We can't check most failure cases, so we just check that some\n  // valid cases are accepted.\n  TestHelpers::db::test_simple_tag<TestHelpers::db::Tags::Simple>(\"Simple\");\n  TestHelpers::db::test_simple_tag<NamedSimple>(\"NamedSimpleName\");\n  TestHelpers::db::test_simple_tag<SimpleFromOption>(\"Simple\");\n  TestHelpers::db::test_compute_tag<TestHelpers::db::Tags::SimpleCompute>(\n      \"Simple\");\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/DataBox/Test_ValidateSelection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/ValidateSelection.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct Tag1 : db::SimpleTag {\n  using type = int;\n};\n\nstruct Tag2 : db::SimpleTag {\n  using type = int;\n  static std::string name() { return \"CustomTag2\"; }\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Options.ValidateSelection\", \"[Unit][Options]\") {\n  db::validate_selection<tmpl::list<Tag1, Tag2>>({\"Tag1\"}, Options::Context{});\n  CHECK_THROWS_WITH((db::validate_selection<tmpl::list<Tag1, Tag2>>(\n                        {\"Tag2\"}, Options::Context{})),\n                    Catch::Matchers::ContainsSubstring(\"Invalid selection\"));\n  db::validate_selection<tmpl::list<Tag1, Tag2>>({\"CustomTag2\", \"Tag1\"},\n                                                 Options::Context{});\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Tensor\")\n\nset(LIBRARY_SOURCES\n  Test_CombineSpacetimeView.cpp\n  Test_ContractFirstNIndices.cpp\n  Test_Identity.cpp\n  Test_Metafunctions.cpp\n  Test_Slice.cpp\n  Test_Tensor.cpp\n  )\n\nif(SPECTRE_KOKKOS)\n  list(APPEND LIBRARY_SOURCES Test_TensorKokkos.cpp)\nendif()\n\nadd_subdirectory(EagerMath)\nadd_subdirectory(Expressions)\nadd_subdirectory(Python)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  DataStructuresHelpers\n  DomainStructure\n  ErrorHandling\n  Utilities\n  Spectral\n  SpectreKokkos\n  )\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EagerMath\")\n\nset(LIBRARY_SOURCES\n  Test_CartesianToSpherical.cpp\n  Test_CrossProduct.cpp\n  Test_Determinant.cpp\n  Test_DeterminantAndInverse.cpp\n  Test_DotProduct.cpp\n  Test_FrameTransform.cpp\n  Test_GramSchmidtOrthonormalize.cpp\n  Test_Magnitude.cpp\n  Test_Norms.cpp\n  Test_OrthonormalOneform.cpp\n  Test_OuterProduct.cpp\n  Test_RaiseOrLowerIndex.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  DomainHelpers\n  ErrorHandling\n  GeneralRelativity\n  GeneralRelativityHelpers\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/CrossProduct.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\n# Curved-space cross product returning a vector, given vectors a, b\ndef cross_product_up(a, b, inverse_metric, det_metric):\n    return np.einsum(\"ij,j\", inverse_metric, np.cross(a, b)) * np.sqrt(\n        det_metric\n    )\n\n\n# Curved-space cross product returning a covector, given vector a, covector b\ndef cross_product_lo(a, b, inverse_metric, det_metric):\n    return np.cross(a, np.einsum(\"ij,j\", inverse_metric, b)) * np.sqrt(\n        det_metric\n    )\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/FrameTransform.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef to_different_frame(src, jacobian):\n    # Jacobian here is d^x_src/d^x_dest\n    return np.einsum(\"ab,ac,bd\", src, jacobian, jacobian)\n\n\ndef to_different_frame_Scalar(src, jacobian):\n    # Jacobian here is d^x_src/d^x_dest\n    return src\n\n\ndef to_different_frame_I(src, jacobian):\n    # Jacobian here is d^x_src/d^x_dest\n    inv_jacobian = np.linalg.inv(jacobian)\n    return np.einsum(\"a,ca\", src, inv_jacobian)\n\n\ndef to_different_frame_i(src, jacobian):\n    # Jacobian here is d^x_src/d^x_dest\n    return np.einsum(\"a,ac\", src, jacobian)\n\n\ndef to_different_frame_iJ(src, jacobian):\n    # Jacobian here is d^x_src/d^x_dest\n    inv_jacobian = np.linalg.inv(jacobian)\n    return np.einsum(\"ab,ac,db\", src, jacobian, inv_jacobian)\n\n\ndef to_different_frame_ii(src, jacobian):\n    # Jacobian here is d^x_src/d^x_dest\n    return np.einsum(\"ab,ac,bd\", src, jacobian, jacobian)\n\n\ndef to_different_frame_II(src, jacobian):\n    # Jacobian here is d^x_src/d^x_dest\n    inv_jacobian = np.linalg.inv(jacobian)\n    return np.einsum(\"ab,ca,db\", src, inv_jacobian, inv_jacobian)\n\n\ndef to_different_frame_ijj(src, jacobian):\n    # Jacobian here is d^x_src/d^x_dest\n    return np.einsum(\"abe,ac,bd,ef\", src, jacobian, jacobian, jacobian)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.DataStructures.Tensor.Python.EagerMath\"\n  Test_EagerMath.py\n  \"Unit;DataStructures;Python\"\n  PyTensorEagerMath)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Python/Test_EagerMath.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport itertools\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Jacobian, Scalar, tnsr\nfrom spectre.DataStructures.Tensor.EagerMath import determinant, magnitude\n\n\ndef to_numpy(tensor):\n    # Construct a Numpy array where the first 'rank' dimensions are the indices\n    # and the last dimension is the number of grid points. This is an\n    # inefficient way to store the data because it ignores symmetries, which is\n    # why it currently isn't a public function.\n    num_points = len(tensor[0])\n    result = np.zeros(tensor.rank * (tensor.dim,) + (num_points,))\n    for indices in itertools.product(*(tensor.rank * (range(tensor.dim),))):\n        result[indices] = tensor.get(*indices)\n    return result\n\n\nclass TestEagerMath(unittest.TestCase):\n    def test_determinant(self):\n        data = np.random.rand(9, 4)\n        jacobian = Jacobian[DataVector, 3](data)\n        det = determinant(jacobian)\n        det_numpy = np.linalg.det(np.moveaxis(to_numpy(jacobian), -1, 0))\n        npt.assert_allclose(np.array(det)[0], det_numpy)\n\n    def test_magnitude(self):\n        # Euclidean\n        data = np.random.rand(3, 4)\n        vector = tnsr.I[DataVector, 3](data)\n        mag = magnitude(vector)\n        mag_numpy = np.linalg.norm(data, axis=0)\n        npt.assert_allclose(np.array(mag)[0], mag_numpy)\n        # Non-Euclidean\n        data_metric = np.random.rand(6, 4)\n        metric = tnsr.ii[DataVector, 3](data_metric)\n        mag = magnitude(vector, metric)\n        mag_numpy = np.sqrt(\n            np.einsum(\"i...,j...,ij...\", data, data, to_numpy(metric))\n        )\n        npt.assert_allclose(np.array(mag)[0], mag_numpy)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef raise_or_lower_first_index(tensor, metric):\n    return np.einsum(\"ij,ikl\", metric, tensor)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_CartesianToSpherical.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/CartesianToSpherical.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.CartesianToSpherical\",\n                  \"[DataStructures][Unit]\") {\n  {\n    tnsr::I<double, 2, Frame::Grid> x{{{1., 1.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(sqrt(2.0)));\n    CHECK(get<1>(x_spherical) == approx(M_PI_4));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(1.));\n    CHECK(get<1>(x_back) == approx(1.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, 1.}, DataVector{5, 0.}, DataVector{5, 0.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(1.));\n    CHECK(get<1>(x_spherical) == approx(M_PI_2));\n    CHECK(get<2>(x_spherical) == approx(0.));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(1.));\n    CHECK(get<1>(x_back) == approx(0.));\n    CHECK(get<2>(x_back) == approx(0.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, 0.}, DataVector{5, 1.}, DataVector{5, 0.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(1.));\n    CHECK(get<1>(x_spherical) == approx(M_PI_2));\n    CHECK(get<2>(x_spherical) == approx(M_PI_2));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(0.));\n    CHECK(get<1>(x_back) == approx(1.));\n    CHECK(get<2>(x_back) == approx(0.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, -1.}, DataVector{5, 0.}, DataVector{5, 0.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(1.));\n    CHECK(get<1>(x_spherical) == approx(M_PI_2));\n    CHECK(get<2>(x_spherical) == approx(M_PI));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(-1.));\n    CHECK(get<1>(x_back) == approx(0.));\n    CHECK(get<2>(x_back) == approx(0.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, 1.}, DataVector{5, 0.}, DataVector{5, 1.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(sqrt(2.)));\n    CHECK(get<1>(x_spherical) == approx(M_PI_4));\n    CHECK(get<2>(x_spherical) == approx(0.));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(1.));\n    CHECK(get<1>(x_back) == approx(0.));\n    CHECK(get<2>(x_back) == approx(1.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, 1.}, DataVector{5, 0.}, DataVector{5, -1.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(sqrt(2.)));\n    CHECK(get<1>(x_spherical) == approx(3. * M_PI_4));\n    CHECK(get<2>(x_spherical) == approx(0.));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(1.));\n    CHECK(get<1>(x_back) == approx(0.));\n    CHECK(get<2>(x_back) == approx(-1.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, 0.}, DataVector{5, 0.}, DataVector{5, 1.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(1.));\n    CHECK(get<1>(x_spherical) == approx(0.));\n    CHECK(get<2>(x_spherical) == approx(0.));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(0.));\n    CHECK(get<1>(x_back) == approx(0.));\n    CHECK(get<2>(x_back) == approx(1.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, 0.}, DataVector{5, 0.}, DataVector{5, -1.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(1.));\n    CHECK(get<1>(x_spherical) == approx(M_PI));\n    CHECK(get<2>(x_spherical) == approx(0.));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(0.));\n    CHECK(get<1>(x_back) == approx(0.));\n    CHECK(get<2>(x_back) == approx(-1.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, 1.}, DataVector{5, 1.}, DataVector{5, 1.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(sqrt(3.)));\n    CHECK(get<1>(x_spherical) == approx(acos(1. / sqrt(3.))));\n    CHECK(get<2>(x_spherical) == approx(M_PI_4));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(1.));\n    CHECK(get<1>(x_back) == approx(1.));\n    CHECK(get<2>(x_back) == approx(1.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, -1.}, DataVector{5, 1.}, DataVector{5, 1.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(sqrt(3.)));\n    CHECK(get<1>(x_spherical) == approx(acos(1. / sqrt(3.))));\n    CHECK(get<2>(x_spherical) == approx(3. * M_PI_4));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(-1.));\n    CHECK(get<1>(x_back) == approx(1.));\n    CHECK(get<2>(x_back) == approx(1.));\n  }\n  {\n    tnsr::I<DataVector, 3, Frame::Inertial> x{\n        {DataVector{5, -1.}, DataVector{5, -1.}, DataVector{5, 1.}}};\n    const auto x_spherical = cartesian_to_spherical(x);\n    CHECK(get<0>(x_spherical) == approx(sqrt(3.)));\n    CHECK(get<1>(x_spherical) == approx(acos(1. / sqrt(3.))));\n    CHECK(get<2>(x_spherical) == approx(-3. * M_PI_4));\n    const auto x_back = spherical_to_cartesian(x_spherical);\n    CHECK(get<0>(x_back) == approx(-1.));\n    CHECK(get<1>(x_back) == approx(-1.));\n    CHECK(get<2>(x_back) == approx(1.));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_CrossProduct.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/CrossProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nvoid check_cross_product(const DataType& used_for_size) {\n  // Make constants used to create vectors and one_forms\n  const auto zero = make_with_value<DataType>(used_for_size, 0.0);\n  const auto one_over_three_hundred_twenty_seven =\n      make_with_value<DataType>(used_for_size, 1.0 / 327.0);\n  const auto one_half = make_with_value<DataType>(used_for_size, 0.5);\n  const auto minus_one_half = make_with_value<DataType>(used_for_size, -0.5);\n  const auto one = make_with_value<DataType>(used_for_size, 1.0);\n  const auto minus_one = make_with_value<DataType>(used_for_size, -1.0);\n  const auto two = make_with_value<DataType>(used_for_size, 2.0);\n  const auto minus_two = make_with_value<DataType>(used_for_size, -2.0);\n  const auto minus_three = make_with_value<DataType>(used_for_size, -3.0);\n  const auto four = make_with_value<DataType>(used_for_size, 4.0);\n  const auto minus_five = make_with_value<DataType>(used_for_size, -5.0);\n  const auto twelve = make_with_value<DataType>(used_for_size, 12.0);\n  const auto twenty_two = make_with_value<DataType>(used_for_size, 22.0);\n  const auto minus_thirty_three =\n      make_with_value<DataType>(used_for_size, -33.0);\n  const auto forty_four = make_with_value<DataType>(used_for_size, 44.0);\n  const auto sixty_four = make_with_value<DataType>(used_for_size, 64.0);\n\n  // Test Euclidean cross product for a known generic case\n  const tnsr::I<DataType, 3, Frame::Grid> vector_a{\n      {{minus_three, twelve, four}}};\n  const tnsr::I<DataType, 3, Frame::Grid> vector_b{{{four, minus_five, two}}};\n  const tnsr::i<DataType, 3, Frame::Grid> covector_b{{{four, minus_five, two}}};\n  const tnsr::I<DataType, 3, Frame::Grid> vector_expected{\n      {{forty_four, twenty_two, minus_thirty_three}}};\n  const tnsr::i<DataType, 3, Frame::Grid> covector_expected{\n      {{forty_four, twenty_two, minus_thirty_three}}};\n  CHECK_ITERABLE_APPROX(cross_product(vector_a, vector_b), vector_expected);\n  CHECK_ITERABLE_APPROX(cross_product(vector_a, covector_b), covector_expected);\n\n  // Test curved-space cross product for a known generic case\n  const tnsr::II<DataType, 3, Frame::Grid> inverse_metric = [&used_for_size]() {\n    auto tensor =\n        make_with_value<tnsr::II<DataType, 3, Frame::Grid>>(used_for_size, 0.0);\n    get<0, 0>(tensor) = 2.0;\n    get<0, 1>(tensor) = -3.0;\n    get<0, 2>(tensor) = 4.0;\n    get<1, 1>(tensor) = -5.0;\n    get<1, 2>(tensor) = -12.0;\n    get<2, 2>(tensor) = -13.0;\n    return tensor;\n  }();\n\n  const auto det_metric = Scalar<DataType>{one_over_three_hundred_twenty_seven};\n\n  // The known case for curved-space happens to involve the same vector_a\n  // but different vector_b as for the Euclidean cross product test\n  const tnsr::I<DataType, 3, Frame::Grid> curved_vector_b{\n      {{minus_five, four, two}}};\n  const tnsr::i<DataType, 3, Frame::Grid> curved_covector_b =\n      [&used_for_size]() {\n        auto tensor = make_with_value<tnsr::i<DataType, 3, Frame::Grid>>(\n            used_for_size, 0.0);\n        get<0>(tensor) = 53.0 / 109.0;\n        get<1>(tensor) = 97.0 / 109.0;\n        get<2>(tensor) = -90.0 / 109.0;\n        return tensor;\n      }();\n\n  const tnsr::I<DataType, 3, Frame::Grid> curved_vector_expected =\n      [&det_metric]() {\n        auto tensor = make_with_value<tnsr::I<DataType, 3, Frame::Grid>>(\n            get(det_metric), 0.0);\n        get<0>(tensor) = 250.0 * sqrt(get(det_metric));\n        get<1>(tensor) = -530.0 * sqrt(get(det_metric));\n        get<2>(tensor) = -424.0 * sqrt(get(det_metric));\n        return tensor;\n      }();\n  const tnsr::i<DataType, 3, Frame::Grid> curved_covector_expected =\n      [&det_metric]() {\n        auto tensor = make_with_value<tnsr::i<DataType, 3, Frame::Grid>>(\n            get(det_metric), 0.0);\n        get<0>(tensor) = 8.0 * sqrt(get(det_metric));\n        get<1>(tensor) = -14.0 * sqrt(get(det_metric));\n        get<2>(tensor) = 48.0 * sqrt(get(det_metric));\n        return tensor;\n      }();\n\n  CHECK_ITERABLE_APPROX(\n      cross_product(vector_a, curved_vector_b, inverse_metric, det_metric),\n      curved_vector_expected);\n  CHECK_ITERABLE_APPROX(\n      cross_product(vector_a, curved_covector_b, inverse_metric, det_metric),\n      curved_covector_expected);\n\n  // Test Euclidean cross product for orthogonal unit vectors\n  const tnsr::I<DataType, 3, Frame::Grid> vector_x_hat{{{one, zero, zero}}};\n  const tnsr::I<DataType, 3, Frame::Grid> covector_x_hat{{{one, zero, zero}}};\n  const tnsr::I<DataType, 3, Frame::Grid> vector_y_hat{{{zero, one, zero}}};\n  const tnsr::I<DataType, 3, Frame::Grid> covector_y_hat{{{zero, one, zero}}};\n  const tnsr::I<DataType, 3, Frame::Grid> vector_z_hat{{{zero, zero, one}}};\n  const tnsr::I<DataType, 3, Frame::Grid> covector_z_hat{{{zero, zero, one}}};\n  const tnsr::I<DataType, 3, Frame::Grid> vector_minus_z_hat{\n      {{zero, zero, minus_one}}};\n  const tnsr::I<DataType, 3, Frame::Grid> covector_minus_z_hat{\n      {{zero, zero, minus_one}}};\n  CHECK_ITERABLE_APPROX(cross_product(vector_x_hat, vector_y_hat),\n                        vector_z_hat);\n  CHECK_ITERABLE_APPROX(cross_product(vector_x_hat, covector_y_hat),\n                        covector_z_hat);\n  CHECK_ITERABLE_APPROX(cross_product(vector_y_hat, vector_x_hat),\n                        vector_minus_z_hat);\n  CHECK_ITERABLE_APPROX(cross_product(vector_y_hat, covector_x_hat),\n                        covector_minus_z_hat);\n\n  // Test curved-space cross product for orthogonal unit vectors\n  const tnsr::I<DataType, 3, Frame::Grid> curved_vector_x_hat{\n      {{one_half, zero, zero}}};\n  const tnsr::i<DataType, 3, Frame::Grid> curved_covector_x_hat{\n      {{two, zero, zero}}};\n  const tnsr::I<DataType, 3, Frame::Grid> curved_vector_y_hat{\n      {{zero, one_half, zero}}};\n  const tnsr::i<DataType, 3, Frame::Grid> curved_covector_y_hat{\n      {{zero, two, zero}}};\n  const tnsr::I<DataType, 3, Frame::Grid> curved_vector_z_hat{\n      {{zero, zero, one_half}}};\n  const tnsr::i<DataType, 3, Frame::Grid> curved_covector_z_hat{\n      {{zero, zero, two}}};\n  const tnsr::I<DataType, 3, Frame::Grid> curved_vector_minus_z_hat{\n      {{zero, zero, minus_one_half}}};\n  const tnsr::i<DataType, 3, Frame::Grid> curved_covector_minus_z_hat{\n      {{zero, zero, minus_two}}};\n\n  const tnsr::II<DataType, 3, Frame::Grid> inverse_metric_simple =\n      [&used_for_size]() {\n        auto tensor = make_with_value<tnsr::II<DataType, 3, Frame::Grid>>(\n            used_for_size, 0.0);\n        get<0, 0>(tensor) = 0.25;\n        get<1, 1>(tensor) = 0.25;\n        get<2, 2>(tensor) = 0.25;\n        return tensor;\n      }();\n  const auto det_metric_simple = Scalar<DataType>{sixty_four};\n\n  CHECK_ITERABLE_APPROX(cross_product(curved_vector_x_hat, curved_vector_y_hat,\n                                      inverse_metric_simple, det_metric_simple),\n                        curved_vector_z_hat);\n  CHECK_ITERABLE_APPROX(\n      cross_product(curved_vector_x_hat, curved_covector_y_hat,\n                    inverse_metric_simple, det_metric_simple),\n      curved_covector_z_hat);\n  CHECK_ITERABLE_APPROX(cross_product(curved_vector_y_hat, curved_vector_x_hat,\n                                      inverse_metric_simple, det_metric_simple),\n                        curved_vector_minus_z_hat);\n  CHECK_ITERABLE_APPROX(\n      cross_product(curved_vector_y_hat, curved_covector_x_hat,\n                    inverse_metric_simple, det_metric_simple),\n      curved_covector_minus_z_hat);\n\n  // Test spacetime cross product (4D)\n  const tnsr::A<DataType, 3, Frame::Grid> spacetime_vector_t{\n      {{one, zero, zero, zero}}};\n  const tnsr::A<DataType, 3, Frame::Grid> spacetime_vector_x{\n      {{zero, one, zero, zero}}};\n  const tnsr::A<DataType, 3, Frame::Grid> spacetime_vector_y{\n      {{zero, zero, one, zero}}};\n  const tnsr::A<DataType, 3, Frame::Grid> spacetime_vector_z{\n      {{zero, zero, zero, one}}};\n\n  auto inverse_spacetime_metric =\n      make_with_value<tnsr::AA<DataType, 3, Frame::Grid>>(used_for_size, 0.0);\n  get<0, 0>(inverse_spacetime_metric) = -1.0;\n  get<1, 1>(inverse_spacetime_metric) = 1.0;\n  get<2, 2>(inverse_spacetime_metric) = 1.0;\n  get<3, 3>(inverse_spacetime_metric) = 1.0;\n  auto spacetime_metric =\n      make_with_value<tnsr::aa<DataType, 3, Frame::Grid>>(used_for_size, 0.0);\n  get<0, 0>(spacetime_metric) = -1.0;\n  get<1, 1>(spacetime_metric) = 1.0;\n  get<2, 2>(spacetime_metric) = 1.0;\n  get<3, 3>(spacetime_metric) = 1.0;\n  const auto spacetime_metric_determinant =\n      make_with_value<Scalar<DataType>>(used_for_size, -1.0);\n\n  auto result_up =\n      make_with_value<tnsr::A<DataType, 3, Frame::Grid>>(used_for_size, 0.0);\n  cross_product(make_not_null(&result_up), spacetime_vector_t,\n                spacetime_vector_x, spacetime_vector_y,\n                inverse_spacetime_metric, spacetime_metric_determinant);\n  CHECK_ITERABLE_APPROX(result_up, spacetime_vector_z);\n  auto result_lo =\n      make_with_value<tnsr::a<DataType, 3, Frame::Grid>>(used_for_size, 0.0);\n  cross_product(make_not_null(&result_lo),\n                raise_or_lower_index(spacetime_vector_t, spacetime_metric),\n                raise_or_lower_index(spacetime_vector_x, spacetime_metric),\n                raise_or_lower_index(spacetime_vector_y, spacetime_metric),\n                spacetime_metric, spacetime_metric_determinant);\n  CHECK_ITERABLE_APPROX(\n      result_lo, raise_or_lower_index(spacetime_vector_z, spacetime_metric));\n\n  // Test c++ vs. python using random values\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataType, 3, Frame::Grid> (*)(\n          const tnsr::I<DataType, 3, Frame::Grid>&,\n          const tnsr::I<DataType, 3, Frame::Grid>&)>(\n          &cross_product<DataType, SpatialIndex<3, UpLo::Up, Frame::Grid>>),\n      \"numpy\", \"cross\", {{{-10.0, 10.0}}}, vector_x_hat);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::i<DataType, 3, Frame::Grid> (*)(\n          const tnsr::I<DataType, 3, Frame::Grid>&,\n          const tnsr::i<DataType, 3, Frame::Grid>&)>(\n          &cross_product<DataType, SpatialIndex<3, UpLo::Up, Frame::Grid>>),\n      \"numpy\", \"cross\", {{{-10.0, 10.0}}}, vector_x_hat);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataType, 3, Frame::Grid> (*)(\n          const tnsr::I<DataType, 3, Frame::Grid>&,\n          const tnsr::I<DataType, 3, Frame::Grid>&,\n          const tnsr::II<DataType, 3, Frame::Grid>&, const Scalar<DataType>&)>(\n          &cross_product<DataType, SpatialIndex<3, UpLo::Up, Frame::Grid>>),\n      \"CrossProduct\", \"cross_product_up\", {{{1.0, 10.0}}}, curved_vector_x_hat);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::i<DataType, 3, Frame::Grid> (*)(\n          const tnsr::I<DataType, 3, Frame::Grid>&,\n          const tnsr::i<DataType, 3, Frame::Grid>&,\n          const tnsr::II<DataType, 3, Frame::Grid>&, const Scalar<DataType>&)>(\n          &cross_product<DataType, SpatialIndex<3, UpLo::Up, Frame::Grid>>),\n      \"CrossProduct\", \"cross_product_lo\", {{{1.0, 10.0}}}, curved_vector_x_hat);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.CrossProduct\",\n                  \"[DataStructures][Unit]\") {\n  // Set up a python environment for check_with_random_values\n  const pypp::SetupLocalPythonEnvironment local_python_env(\n      \"DataStructures/Tensor/EagerMath/\");\n  check_cross_product(std::numeric_limits<double>::signaling_NaN());\n  check_cross_product(\n      DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_Determinant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.Determinant\",\n                  \"[DataStructures][Unit]\") {\n  // Test determinant function on general (no symmetry) matrices:\n  // * use rank-2 Tensor in 1-4 dimensions, i.e. 1x1, 2x2, 3x3, 4x4 matrices.\n  // * use Tensor<double, ...>, i.e. data at single spatial point.\n  SECTION(\"Test general Tensor<double,...> matrices\") {\n    {\n      const tnsr::ij<double, 1, Frame::Grid> matrix(1.3);\n      const auto det = determinant(matrix);\n      CHECK(1.3 == approx(det.get()));\n    }\n\n    {\n      tnsr::ij<double, 2, Frame::Grid> matrix{};\n      get<0, 0>(matrix) = 2.1;\n      get<0, 1>(matrix) = 4.5;\n      get<1, 0>(matrix) = -18.3;\n      get<1, 1>(matrix) = -10.9;\n      const auto det = determinant(matrix);\n      CHECK(59.46 == approx(det.get()));\n    }\n\n    {\n      tnsr::ij<double, 3, Frame::Grid> matrix{};\n      get<0, 0>(matrix) = 1.1;\n      get<0, 1>(matrix) = -10.4;\n      get<0, 2>(matrix) = -4.5;\n      get<1, 0>(matrix) = 3.2;\n      get<1, 1>(matrix) = 15.4;\n      get<1, 2>(matrix) = -19.2;\n      get<2, 0>(matrix) = 4.3;\n      get<2, 1>(matrix) = 16.8;\n      get<2, 2>(matrix) = 2.0;\n      const auto det = determinant(matrix);\n      CHECK(1369.95 == approx(det.get()));\n    }\n\n    {\n      tnsr::ij<double, 4, Frame::Grid> matrix{};\n      get<0, 0>(matrix) = 1.1;\n      get<0, 1>(matrix) = -10.4;\n      get<0, 2>(matrix) = -4.5;\n      get<0, 3>(matrix) = 3.2;\n      get<1, 0>(matrix) = 15.4;\n      get<1, 1>(matrix) = -19.2;\n      get<1, 2>(matrix) = 4.3;\n      get<1, 3>(matrix) = 16.8;\n      get<2, 0>(matrix) = 2.0;\n      get<2, 1>(matrix) = 3.1;\n      get<2, 2>(matrix) = 4.2;\n      get<2, 3>(matrix) = -0.2;\n      get<3, 0>(matrix) = -3.2;\n      get<3, 1>(matrix) = 2.6;\n      get<3, 2>(matrix) = 1.5;\n      get<3, 3>(matrix) = 2.8;\n      const auto det = determinant(matrix);\n      CHECK(1049.7072 == approx(det.get()));\n    }\n  }\n\n  // Test determinant function on symmetric matrices:\n  // * use rank-2 Tensor in 2-4 dimensions (symmetry meaningless for dim==1).\n  // * use Tensor<double, ...>, i.e. data at single spatial point.\n  SECTION(\"Test symmetric Tensor<double,...> matrices\") {\n    {\n      tnsr::ii<double, 2, Frame::Grid> matrix{};\n      get<0, 0>(matrix) = 2.1;\n      get<0, 1>(matrix) = 4.5;\n      get<1, 1>(matrix) = -10.9;\n      const auto det = determinant(matrix);\n      CHECK(-43.14 == approx(det.get()));\n    }\n\n    {\n      tnsr::ii<double, 3, Frame::Grid> matrix{};\n      get<0, 0>(matrix) = 1.1;\n      get<0, 1>(matrix) = -10.4;\n      get<0, 2>(matrix) = -4.5;\n      get<1, 1>(matrix) = 3.2;\n      get<1, 2>(matrix) = 15.4;\n      get<2, 2>(matrix) = -19.2;\n      const auto det = determinant(matrix);\n      CHECK(3124.852 == approx(det.get()));\n    }\n\n    {\n      tnsr::ii<double, 4, Frame::Grid> matrix{};\n      get<0, 0>(matrix) = 1.1;\n      get<0, 1>(matrix) = -10.4;\n      get<0, 2>(matrix) = -4.5;\n      get<0, 3>(matrix) = 3.2;\n      get<1, 1>(matrix) = 15.4;\n      get<1, 2>(matrix) = -19.2;\n      get<1, 3>(matrix) = 4.3;\n      get<2, 2>(matrix) = 16.8;\n      get<2, 3>(matrix) = 2.0;\n      get<3, 3>(matrix) = 3.1;\n      const auto det = determinant(matrix);\n      CHECK(-22819.6093 == approx(det.get()));\n    }\n  }\n\n  SECTION(\"Test Tensors of DataVector\") {\n    tnsr::ij<DataVector, 2, Frame::Grid> matrix{};\n    get<0, 0>(matrix) = DataVector({6.0, 5.9, 9.8, 6.4});\n    get<0, 1>(matrix) = DataVector({6.1, 0.3, 2.4, 5.7});\n    get<1, 0>(matrix) = DataVector({4.2, 7.1, 1.1, 6.5});\n    get<1, 1>(matrix) = DataVector({7.2, 8.4, 6.1, 3.7});\n    const auto det = determinant(matrix);\n    CHECK(17.58 == approx(det.get()[0]));\n    CHECK(47.43 == approx(det.get()[1]));\n    CHECK(57.14 == approx(det.get()[2]));\n    CHECK(-13.37 == approx(det.get()[3]));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_DeterminantAndInverse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\n// In the spirit of the Tensor type aliases, but for a rank-2 Tensor with each\n// index in a different frame. If Fr1 == Fr2, then this reduces to tnsr::iJ.\ntemplate <typename DataType, size_t Dim, typename Fr1, typename Fr2>\nusing tnsr_iJ = Tensor<DataType, tmpl::integral_list<int32_t, 2, 1>,\n                       index_list<SpatialIndex<Dim, UpLo::Lo, Fr1>,\n                                  SpatialIndex<Dim, UpLo::Up, Fr2>>>;\n\ntemplate <typename TensorType>\nvoid verify_det_and_inv_1d() {\n  TensorType t{};\n  get<0, 0>(t) = 2.0;\n  const auto det_inv = determinant_and_inverse(t);\n  CHECK(det_inv.first.get() == 2.0);\n  CHECK((get<0, 0>(det_inv.second)) == 0.5);\n}\n\ntemplate <typename TensorType>\nvoid verify_det_and_inv_generic_2d() {\n  TensorType t{};\n  get<0, 0>(t) = 2.0;\n  get<0, 1>(t) = 3.0;\n  get<1, 0>(t) = 4.0;\n  get<1, 1>(t) = 5.0;\n  const auto det_inv = determinant_and_inverse(t);\n  CHECK(det_inv.first.get() == -2.0);\n  CHECK((get<0, 0>(det_inv.second)) == -2.5);\n  CHECK((get<0, 1>(det_inv.second)) == 1.5);\n  CHECK((get<1, 0>(det_inv.second)) == 2.0);\n  CHECK((get<1, 1>(det_inv.second)) == -1.0);\n}\n\ntemplate <typename TensorType>\nvoid verify_det_and_inv_generic_3d() {\n  TensorType t{};\n  get<0, 0>(t) = 2.0;\n  get<0, 1>(t) = 3.0;\n  get<0, 2>(t) = 6.0;\n  get<1, 0>(t) = 4.0;\n  get<1, 1>(t) = 5.0;\n  get<1, 2>(t) = 7.0;\n  get<2, 0>(t) = 8.0;\n  get<2, 1>(t) = 9.0;\n  get<2, 2>(t) = 10.0;\n  const auto det_inv = determinant_and_inverse(t);\n  CHECK(det_inv.first.get() == -2.0);\n  CHECK((get<0, 0>(det_inv.second)) == 6.5);\n  CHECK((get<0, 1>(det_inv.second)) == -12.0);\n  CHECK((get<0, 2>(det_inv.second)) == 4.5);\n  CHECK((get<1, 0>(det_inv.second)) == -8.0);\n  CHECK((get<1, 1>(det_inv.second)) == 14.0);\n  CHECK((get<1, 2>(det_inv.second)) == -5.0);\n  CHECK((get<2, 0>(det_inv.second)) == 2.0);\n  CHECK((get<2, 1>(det_inv.second)) == -3.0);\n  CHECK((get<2, 2>(det_inv.second)) == 1.0);\n}\n\ntemplate <typename TensorType>\nvoid verify_det_and_inv_generic_4d() {\n  TensorType t{};\n  get<0, 0>(t) = 2.0;\n  get<0, 1>(t) = 3.0;\n  get<0, 2>(t) = 6.0;\n  get<0, 3>(t) = 11.0;\n  get<1, 0>(t) = 4.0;\n  get<1, 1>(t) = 5.0;\n  get<1, 2>(t) = 7.0;\n  get<1, 3>(t) = 12.0;\n  get<2, 0>(t) = 8.0;\n  get<2, 1>(t) = 9.0;\n  get<2, 2>(t) = 10.0;\n  get<2, 3>(t) = 13.0;\n  get<3, 0>(t) = 14.0;\n  get<3, 1>(t) = 15.0;\n  get<3, 2>(t) = 16.0;\n  get<3, 3>(t) = 17.0;\n  const auto det_inv = determinant_and_inverse(t);\n  CHECK(det_inv.first.get() == -8.0);\n  CHECK((get<0, 0>(det_inv.second)) == -4.0);\n  CHECK((get<0, 1>(det_inv.second)) == 9.0);\n  CHECK((get<0, 2>(det_inv.second)) == -9.5);\n  CHECK((get<0, 3>(det_inv.second)) == 3.5);\n  CHECK((get<1, 0>(det_inv.second)) == 3.25);\n  CHECK((get<1, 1>(det_inv.second)) == -8.5);\n  CHECK((get<1, 2>(det_inv.second)) == 10.0);\n  CHECK((get<1, 3>(det_inv.second)) == -3.75);\n  CHECK((get<2, 0>(det_inv.second)) == 1.25);\n  CHECK((get<2, 1>(det_inv.second)) == -1.5);\n  CHECK((get<2, 2>(det_inv.second)) == 0.0);\n  CHECK((get<2, 3>(det_inv.second)) == 0.25);\n  CHECK((get<3, 0>(det_inv.second)) == -0.75);\n  CHECK((get<3, 1>(det_inv.second)) == 1.5);\n  CHECK((get<3, 2>(det_inv.second)) == -1.0);\n  CHECK((get<3, 3>(det_inv.second)) == 0.25);\n}\n\ntemplate <typename TensorType>\nvoid verify_det_and_inv_symmetric_2d() {\n  TensorType t{};\n  get<0, 0>(t) = 2.0;\n  get<0, 1>(t) = 3.0;\n  get<1, 1>(t) = 5.0;\n  const auto det_inv = determinant_and_inverse(t);\n  CHECK(det_inv.first.get() == 1.0);\n  CHECK((get<0, 0>(det_inv.second)) == 5.0);\n  CHECK((get<0, 1>(det_inv.second)) == -3.0);\n  CHECK((get<1, 0>(det_inv.second)) == -3.0);\n  CHECK((get<1, 1>(det_inv.second)) == 2.0);\n}\n\ntemplate <typename TensorType>\nvoid verify_det_and_inv_symmetric_3d() {\n  TensorType t{};\n  get<0, 0>(t) = 2.0;\n  get<0, 1>(t) = 3.0;\n  get<0, 2>(t) = 6.0;\n  get<1, 1>(t) = 5.0;\n  get<1, 2>(t) = 7.0;\n  get<2, 2>(t) = 10.0;\n  const auto det_inv = determinant_and_inverse(t);\n  CHECK(det_inv.first.get() == -16.0);\n  CHECK((get<0, 0>(det_inv.second)) == -0.0625);\n  CHECK((get<0, 1>(det_inv.second)) == -0.75);\n  CHECK((get<0, 2>(det_inv.second)) == 0.5625);\n  CHECK((get<1, 0>(det_inv.second)) == -0.75);\n  CHECK((get<1, 1>(det_inv.second)) == 1.0);\n  CHECK((get<1, 2>(det_inv.second)) == -0.25);\n  CHECK((get<2, 0>(det_inv.second)) == 0.5625);\n  CHECK((get<2, 1>(det_inv.second)) == -0.25);\n  CHECK((get<2, 2>(det_inv.second)) == -0.0625);\n}\n\ntemplate <typename TensorType>\nvoid verify_det_and_inv_symmetric_4d() {\n  TensorType t{};\n  get<0, 0>(t) = 2.0;\n  get<0, 1>(t) = 3.0;\n  get<0, 2>(t) = 6.0;\n  get<0, 3>(t) = 11.0;\n  get<1, 1>(t) = 5.0;\n  get<1, 2>(t) = 7.0;\n  get<1, 3>(t) = 12.0;\n  get<2, 2>(t) = 10.0;\n  get<2, 3>(t) = 13.0;\n  get<3, 3>(t) = 17.0;\n  const auto det_inv = determinant_and_inverse(t);\n  CHECK(det_inv.first.get() == -100.0);\n  CHECK((get<0, 0>(det_inv.second)) == approx(0.84));\n  CHECK((get<0, 1>(det_inv.second)) == approx(-0.94));\n  CHECK((get<0, 2>(det_inv.second)) == approx(-0.34));\n  CHECK((get<0, 3>(det_inv.second)) == approx(0.38));\n  CHECK((get<1, 0>(det_inv.second)) == approx(-0.94));\n  CHECK((get<1, 1>(det_inv.second)) == approx(1.04));\n  CHECK((get<1, 2>(det_inv.second)) == approx(-0.06));\n  CHECK((get<1, 3>(det_inv.second)) == approx(-0.08));\n  CHECK((get<2, 0>(det_inv.second)) == approx(-0.34));\n  CHECK((get<2, 1>(det_inv.second)) == approx(-0.06));\n  CHECK((get<2, 2>(det_inv.second)) == approx(0.84));\n  CHECK((get<2, 3>(det_inv.second)) == approx(-0.38));\n  CHECK((get<3, 0>(det_inv.second)) == approx(0.38));\n  CHECK((get<3, 1>(det_inv.second)) == approx(-0.08));\n  CHECK((get<3, 2>(det_inv.second)) == approx(-0.38));\n  CHECK((get<3, 3>(det_inv.second)) == approx(0.16));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.DeterminantAndInverse\",\n                  \"[DataStructures][Unit]\") {\n  // Check that the inverse tensor has the expected index structure -- for an\n  // input Tensor of type T^a_b, the inverse should have type T^b_a.\n  {\n    static_assert(\n        std::is_same_v<\n            Tensor<double, tmpl::integral_list<int32_t, 2, 1>,\n                   index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                              SpatialIndex<2, UpLo::Lo, Frame::Grid>>>,\n            decltype(determinant_and_inverse(\n                std::declval<\n                    Tensor<double, tmpl::integral_list<int32_t, 2, 1>,\n                           index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                                      SpatialIndex<2, UpLo::Lo,\n                                                   Frame::Inertial>>>>()))::\n                second_type>,\n        \"Inverse tensor has incorrect index structure.\");\n  }\n\n  // Check paired determinant and inverse for 1x1 through 4x4 tensors, both\n  // generic and symmetric, with both spatial and spacetime indices.\n  {\n    verify_det_and_inv_1d<tnsr::ii<double, 1, Frame::Grid>>();\n    verify_det_and_inv_1d<tnsr::ij<double, 1, Frame::Grid>>();\n    verify_det_and_inv_1d<tnsr_iJ<double, 1, Frame::Grid, Frame::Inertial>>();\n\n    verify_det_and_inv_symmetric_2d<tnsr::ii<double, 2, Frame::Grid>>();\n    verify_det_and_inv_generic_2d<tnsr::ij<double, 2, Frame::Grid>>();\n    verify_det_and_inv_generic_2d<\n        tnsr_iJ<double, 2, Frame::Grid, Frame::Inertial>>();\n\n    verify_det_and_inv_symmetric_3d<tnsr::ii<double, 3, Frame::Grid>>();\n    verify_det_and_inv_generic_3d<tnsr::ij<double, 3, Frame::Grid>>();\n    verify_det_and_inv_generic_3d<\n        tnsr_iJ<double, 3, Frame::Grid, Frame::Inertial>>();\n\n    verify_det_and_inv_symmetric_4d<tnsr::ii<double, 4, Frame::Grid>>();\n    verify_det_and_inv_generic_4d<tnsr::ij<double, 4, Frame::Grid>>();\n    verify_det_and_inv_generic_4d<\n        tnsr_iJ<double, 4, Frame::Grid, Frame::Inertial>>();\n\n    verify_det_and_inv_symmetric_2d<tnsr::aa<double, 1, Frame::Grid>>();\n    verify_det_and_inv_generic_2d<tnsr::ab<double, 1, Frame::Grid>>();\n\n    verify_det_and_inv_symmetric_3d<tnsr::aa<double, 2, Frame::Grid>>();\n    verify_det_and_inv_generic_3d<tnsr::ab<double, 2, Frame::Grid>>();\n\n    verify_det_and_inv_symmetric_4d<tnsr::aa<double, 3, Frame::Grid>>();\n    verify_det_and_inv_generic_4d<tnsr::ab<double, 3, Frame::Grid>>();\n  }\n\n  // Check paired determinant and inverse for a Tensor<DataVector>.\n  {\n    tnsr::ij<DataVector, 2, Frame::Grid> t{};\n    get<0, 0>(t) = DataVector({2.0, 3.0, 5.0, 1.0});\n    get<0, 1>(t) = DataVector({3.0, 5.0, 4.0, -1.0});\n    get<1, 0>(t) = DataVector({4.0, 2.0, 2.0, 1.0});\n    get<1, 1>(t) = DataVector({5.0, 3.0, 2.0, 1.0});\n    const auto det_inv = determinant_and_inverse(t);\n    CHECK(det_inv.first.get() == DataVector({-2.0, -1.0, 2.0, 2.0}));\n    CHECK((get<0, 0>(det_inv.second)) == DataVector({-2.5, -3.0, 1.0, 0.5}));\n    CHECK((get<0, 1>(det_inv.second)) == DataVector({1.5, 5.0, -2.0, 0.5}));\n    CHECK((get<1, 0>(det_inv.second)) == DataVector({2.0, 2.0, -1.0, -0.5}));\n    CHECK((get<1, 1>(det_inv.second)) == DataVector({-1.0, -3.0, 2.5, 0.5}));\n  }\n\n  // Check Variables<determinant, inverse> for a Tensor<DataVector>\n  {\n    {\n      // 2D\n      using my_tnsr_type = tnsr::ij<DataVector, 2, Frame::Grid>;\n      using my_tnsr_inv_type = tnsr::IJ<DataVector, 2, Frame::Grid>;\n      using my_tnsr_det_type = Scalar<DataVector>;\n\n      struct MyDetTag : db::SimpleTag {\n        static std::string name() { return \"DummyDetTag\"; }\n        using type = my_tnsr_det_type;\n      };\n      struct MyInvTag : db::SimpleTag {\n        static std::string name() { return \"DummyInvTag\"; }\n        using type = my_tnsr_inv_type;\n      };\n\n      my_tnsr_type t{};\n      get<0, 0>(t) = DataVector({2.0, 3.0, 5.0, 1.0});\n      get<0, 1>(t) = DataVector({3.0, 5.0, 4.0, -1.0});\n      get<1, 0>(t) = DataVector({4.0, 2.0, 2.0, 1.0});\n      get<1, 1>(t) = DataVector({5.0, 3.0, 2.0, 1.0});\n      const auto det_inv = determinant_and_inverse<MyDetTag, MyInvTag>(t);\n      CHECK(get(get<MyDetTag>(det_inv)) == DataVector({-2.0, -1.0, 2.0, 2.0}));\n      CHECK((get<0, 0>(get<MyInvTag>(det_inv))) ==\n            DataVector({-2.5, -3.0, 1.0, 0.5}));\n      CHECK((get<0, 1>(get<MyInvTag>(det_inv))) ==\n            DataVector({1.5, 5.0, -2.0, 0.5}));\n      CHECK((get<1, 0>(get<MyInvTag>(det_inv))) ==\n            DataVector({2.0, 2.0, -1.0, -0.5}));\n      CHECK((get<1, 1>(get<MyInvTag>(det_inv))) ==\n            DataVector({-1.0, -3.0, 2.5, 0.5}));\n    }\n    {\n      // 4D\n      using my_tnsr_type = tnsr::ij<DataVector, 4, Frame::Grid>;\n      using my_tnsr_inv_type = tnsr::IJ<DataVector, 4, Frame::Grid>;\n      using my_tnsr_det_type = Scalar<DataVector>;\n\n      struct MyDetTag : db::SimpleTag {\n        static std::string name() { return \"DummyDetTag\"; }\n        using type = my_tnsr_det_type;\n      };\n      struct MyInvTag : db::SimpleTag {\n        static std::string name() { return \"DummyInvTag\"; }\n        using type = my_tnsr_inv_type;\n      };\n\n      my_tnsr_type t{};\n      get<0, 0>(t) = DataVector({2.0, 3.0});\n      get<0, 1>(t) = DataVector({3.0, 5.0});\n      get<0, 2>(t) = DataVector({4.0, 2.0});\n      get<0, 3>(t) = DataVector({5.0, 3.0});\n      get<1, 0>(t) = DataVector({2.0, -3.0});\n      get<1, 1>(t) = DataVector({-3.0, 5.0});\n      get<1, 2>(t) = DataVector({4.0, 2.0});\n      get<1, 3>(t) = DataVector({-5.0, 3.0});\n      get<2, 0>(t) = DataVector({2.0, 3.0});\n      get<2, 1>(t) = DataVector({-3.0, -5.0});\n      get<2, 2>(t) = DataVector({4.0, 2.0});\n      get<2, 3>(t) = DataVector({5.0, 3.0});\n      get<3, 0>(t) = DataVector({2.0, 3.0});\n      get<3, 1>(t) = DataVector({3.0, 5.0});\n      get<3, 2>(t) = DataVector({-4.0, -2.0});\n      get<3, 3>(t) = DataVector({5.0, 3.0});\n      const auto det_inv = determinant_and_inverse<MyDetTag, MyInvTag>(t);\n      CHECK_ITERABLE_APPROX(get(get<MyDetTag>(det_inv)),\n                            DataVector({-960.0, 720.0}));\n      CHECK_ITERABLE_APPROX((get<0, 0>(get<MyInvTag>(det_inv))),\n                            DataVector({0.0, 1.0 / 6.0}));\n      CHECK_ITERABLE_APPROX((get<0, 1>(get<MyInvTag>(det_inv))),\n                            DataVector({0.25, -1.0 / 6.0}));\n      CHECK_ITERABLE_APPROX((get<0, 2>(get<MyInvTag>(det_inv))),\n                            DataVector({0.0, 0.0}));\n      CHECK_ITERABLE_APPROX((get<0, 3>(get<MyInvTag>(det_inv))),\n                            DataVector({0.25, 0.0}));\n      CHECK_ITERABLE_APPROX((get<1, 0>(get<MyInvTag>(det_inv))),\n                            DataVector({1.0 / 6.0, 0.1}));\n      CHECK_ITERABLE_APPROX((get<1, 1>(get<MyInvTag>(det_inv))),\n                            DataVector({0.0, 0.0}));\n      CHECK_ITERABLE_APPROX((get<1, 2>(get<MyInvTag>(det_inv))),\n                            DataVector({-1.0 / 6.0, -0.1}));\n      CHECK_ITERABLE_APPROX((get<1, 3>(get<MyInvTag>(det_inv))),\n                            DataVector({0.0, 0.0}));\n      CHECK_ITERABLE_APPROX((get<2, 0>(get<MyInvTag>(det_inv))),\n                            DataVector({0.125, 0.25}));\n      CHECK_ITERABLE_APPROX((get<2, 1>(get<MyInvTag>(det_inv))),\n                            DataVector({0.0, 0.0}));\n      CHECK_ITERABLE_APPROX((get<2, 2>(get<MyInvTag>(det_inv))),\n                            DataVector({0.0, 0.0}));\n      CHECK_ITERABLE_APPROX((get<2, 3>(get<MyInvTag>(det_inv))),\n                            DataVector({-0.125, -0.25}));\n      CHECK_ITERABLE_APPROX((get<3, 0>(get<MyInvTag>(det_inv))),\n                            DataVector({0.0, -1.0 / 6.0}));\n      CHECK_ITERABLE_APPROX((get<3, 1>(get<MyInvTag>(det_inv))),\n                            DataVector({-0.1, 1.0 / 6.0}));\n      CHECK_ITERABLE_APPROX((get<3, 2>(get<MyInvTag>(det_inv))),\n                            DataVector({0.1, 1.0 / 6.0}));\n      CHECK_ITERABLE_APPROX((get<3, 3>(get<MyInvTag>(det_inv))),\n                            DataVector({0.0, 1.0 / 6.0}));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_DotProduct.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.EuclideanDotProduct\",\n                  \"[DataStructures][Unit]\") {\n  // Check for DataVectors\n  {\n    const size_t npts = 5;\n    const DataVector one(npts, 1.0);\n    const DataVector two(npts, 2.0);\n    const DataVector minus_three(npts, -3.0);\n    const DataVector four(npts, 4.0);\n    const DataVector minus_five(npts, -5.0);\n    const DataVector twelve(npts, 12.0);\n\n    const tnsr::i<DataVector, 1, Frame::Grid> one_d_covector_a{{{two}}};\n    const tnsr::i<DataVector, 1, Frame::Grid> one_d_covector_b{{{four}}};\n    const tnsr::I<DataVector, 1, Frame::Grid> one_d_vector_c{{{four}}};\n    CHECK_ITERABLE_APPROX(get(dot_product(one_d_covector_a, one_d_covector_b)),\n                          DataVector(npts, 8.0));\n    CHECK_ITERABLE_APPROX(get(dot_product(one_d_covector_a, one_d_vector_c)),\n                          DataVector(npts, 8.0));\n\n    const tnsr::i<DataVector, 1, Frame::Grid> negative_one_d_covector{\n        {{minus_three}}};\n    const tnsr::I<DataVector, 1, Frame::Grid> negative_one_d_vector{\n        {{minus_three}}};\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(negative_one_d_covector, one_d_covector_b)),\n        DataVector(npts, -12.0));\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(negative_one_d_covector, one_d_vector_c)),\n        DataVector(npts, -12.0));\n\n    const tnsr::A<DataVector, 1, Frame::Grid> one_d_vector_a{\n        {{minus_three, four}}};\n    const tnsr::A<DataVector, 1, Frame::Grid> one_d_vector_b{{{two, twelve}}};\n    const tnsr::a<DataVector, 1, Frame::Grid> one_d_covector_c{{{two, twelve}}};\n    CHECK_ITERABLE_APPROX(get(dot_product(one_d_vector_a, one_d_vector_b)),\n                          DataVector(npts, 42.0));\n    CHECK_ITERABLE_APPROX(get(dot_product(one_d_vector_a, one_d_covector_c)),\n                          DataVector(npts, 42.0));\n\n    const tnsr::I<DataVector, 2, Frame::Grid> two_d_vector_a{\n        {{minus_five, twelve}}};\n    const tnsr::I<DataVector, 2, Frame::Grid> two_d_vector_b{{{two, four}}};\n    const tnsr::i<DataVector, 2, Frame::Grid> two_d_covector_b{{{two, four}}};\n    CHECK_ITERABLE_APPROX(get(dot_product(two_d_vector_a, two_d_vector_b)),\n                          DataVector(npts, 38.0));\n    CHECK_ITERABLE_APPROX(get(dot_product(two_d_vector_a, two_d_covector_b)),\n                          DataVector(npts, 38.0));\n\n    const tnsr::i<DataVector, 3, Frame::Grid> three_d_covector_a{\n        {{minus_three, twelve, four}}};\n    const tnsr::i<DataVector, 3, Frame::Grid> three_d_covector_b{\n        {{four, minus_five, two}}};\n    const tnsr::I<DataVector, 3, Frame::Grid> three_d_vector_b{\n        {{four, minus_five, two}}};\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(three_d_covector_a, three_d_covector_b)),\n        DataVector(npts, -64.0));\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(three_d_covector_a, three_d_vector_b)),\n        DataVector(npts, -64.0));\n\n    // 5D example\n    const tnsr::a<DataVector, 4, Frame::Grid> five_d_covector_a{\n        {{two, twelve, four, one, two}}};\n    const tnsr::a<DataVector, 4, Frame::Grid> five_d_covector_b{\n        {{minus_five, minus_three, two, four, one}}};\n    const tnsr::A<DataVector, 4, Frame::Grid> five_d_vector_b{\n        {{minus_five, minus_three, two, four, one}}};\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(five_d_covector_a, five_d_covector_b)),\n        DataVector(npts, -32.0));\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(five_d_covector_a, five_d_vector_b)),\n        DataVector(npts, -32.0));\n  }\n  // Check case for doubles\n  {\n    const tnsr::i<double, 1, Frame::Grid> one_d_covector_double_a{{{2.}}};\n    const tnsr::i<double, 1, Frame::Grid> one_d_covector_double_b{{{4.}}};\n    const tnsr::I<double, 1, Frame::Grid> one_d_vector_double_b{{{4.}}};\n    CHECK(get(dot_product(one_d_covector_double_a, one_d_covector_double_b)) ==\n          8.0);\n    CHECK(get(dot_product(one_d_covector_double_a, one_d_vector_double_b)) ==\n          8.0);\n\n    const tnsr::a<double, 4, Frame::Grid> five_d_covector_double_a{\n        {{2.0, 12.0, 4.0, 1.0, 2.0}}};\n    const tnsr::a<double, 4, Frame::Grid> five_d_covector_double_b{\n        {{4.0, 2.0, -4.0, 3.0, 5.0}}};\n    const tnsr::A<double, 4, Frame::Grid> five_d_vector_double_b{\n        {{4.0, 2.0, -4.0, 3.0, 5.0}}};\n    CHECK(get(dot_product(five_d_covector_double_a,\n                          five_d_covector_double_b)) == 29.0);\n    CHECK(get(dot_product(five_d_covector_double_a,\n                          five_d_vector_double_b)) == 29.0);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.DotProduct\",\n                  \"[DataStructures][Unit]\") {\n  // Check for DataVectors\n  {\n    const size_t npts = 5;\n    const DataVector one(npts, 1.0);\n    const DataVector two(npts, 2.0);\n    const DataVector minus_three(npts, -3.0);\n    const DataVector four(npts, 4.0);\n    const DataVector minus_five(npts, -5.0);\n    const DataVector twelve(npts, 12.0);\n    const DataVector thirteen(npts, 13.0);\n\n    const tnsr::i<DataVector, 1, Frame::Grid> one_d_covector_a{{{two}}};\n    const tnsr::i<DataVector, 1, Frame::Grid> one_d_covector_b{{{four}}};\n    const tnsr::II<DataVector, 1, Frame::Grid> inv_h = [&four]() {\n      tnsr::II<DataVector, 1, Frame::Grid> tensor;\n      get<0, 0>(tensor) = four;\n      return tensor;\n    }();\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(one_d_covector_a, one_d_covector_b, inv_h)),\n        DataVector(npts, 32.0));\n\n    const tnsr::i<DataVector, 3, Frame::Grid> three_d_covector_a{\n        {{minus_three, twelve, four}}};\n    const tnsr::i<DataVector, 3, Frame::Grid> three_d_covector_b{\n        {{minus_five, four, two}}};\n    const tnsr::II<DataVector, 3, Frame::Grid> inv_g =\n        [&two, &minus_three, &four, &minus_five, &twelve, &thirteen]() {\n          tnsr::II<DataVector, 3, Frame::Grid> tensor;\n          get<0, 0>(tensor) = two;\n          get<0, 1>(tensor) = minus_three;\n          get<0, 2>(tensor) = four;\n          get<1, 1>(tensor) = minus_five;\n          get<1, 2>(tensor) = twelve;\n          get<2, 2>(tensor) = thirteen;\n          return tensor;\n        }();\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(three_d_covector_a, three_d_covector_b, inv_g)),\n        DataVector(npts, 486.0));\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(three_d_covector_a, three_d_covector_a, inv_g)),\n        DataVector(npts, 778.0));\n  }\n\n  {\n    // Check for doubles\n    const tnsr::i<double, 1, Frame::Grid> one_d_covector_a{2.0};\n    const tnsr::i<double, 1, Frame::Grid> one_d_covector_b{4.0};\n    const tnsr::II<double, 1, Frame::Grid> inv_h = []() {\n      tnsr::II<double, 1, Frame::Grid> tensor{};\n      get<0, 0>(tensor) = 4.0;\n      return tensor;\n    }();\n\n    CHECK(get(dot_product(one_d_covector_a, one_d_covector_b, inv_h)) == 32.0);\n\n    const tnsr::i<double, 3, Frame::Grid> three_d_covector_a{\n        {{-3.0, 12.0, 4.0}}};\n    const tnsr::i<double, 3, Frame::Grid> three_d_covector_b{\n        {{-5.0, 4.0, 2.0}}};\n    const tnsr::II<double, 3, Frame::Grid> inv_g = []() {\n      tnsr::II<double, 3, Frame::Grid> tensor{};\n      get<0, 0>(tensor) = 2.0;\n      get<0, 1>(tensor) = -3.0;\n      get<0, 2>(tensor) = 4.0;\n      get<1, 1>(tensor) = -5.0;\n      get<1, 2>(tensor) = 12.0;\n      get<2, 2>(tensor) = 13.0;\n      return tensor;\n    }();\n    CHECK(get(dot_product(three_d_covector_a, three_d_covector_b, inv_g)) ==\n          486.0);\n    CHECK(get(dot_product(three_d_covector_a, three_d_covector_a, inv_g)) ==\n          778.0);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_FrameTransform.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/FrameTransform.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables/FrameTransform.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n\nnamespace transform {\nnamespace {\n\nstruct Var1 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 2, Frame::Inertial>;\n};\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::Ij<DataVector, 2, Frame::Inertial>;\n};\n\ntemplate <typename SrcTensorType, typename DestTensorType, typename DataType,\n          size_t Dim, typename DestFrame, typename SrcFrame>\nDestTensorType tensor_transformed_by_python(\n    const SrcTensorType& src_tensor, const DestTensorType& /*dest_tensor*/,\n    const ::Jacobian<DataType, Dim, DestFrame, SrcFrame>& jacobian,\n    const std::string& suffix) {\n  return pypp::call<DestTensorType>(\n      \"FrameTransform\", \"to_different_frame\" + suffix, src_tensor, jacobian);\n}\n\ntemplate <size_t Dim, typename SrcFrame, typename DestFrame, typename DataType>\nvoid test_transform_to_different_frame(const DataType& used_for_size) {\n  // Transform src->dest and then dest->src and ensure we recover\n  // what we started with.\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> interval_dis_small(-0.1, 0.1);\n  const auto nn_generator = make_not_null(&gen);\n  const auto nn_interval_dis = make_not_null(&interval_dis);\n  const auto nn_interval_dis_small = make_not_null(&interval_dis_small);\n\n  const auto src_tensor =\n      make_with_random_values<tnsr::ii<DataType, Dim, SrcFrame>>(\n          nn_generator, nn_interval_dis, used_for_size);\n  const auto jacobian = [&]() {\n    auto jacobian_l =\n        make_with_random_values<::Jacobian<DataType, Dim, DestFrame, SrcFrame>>(\n            nn_generator, nn_interval_dis_small, used_for_size);\n    // Ensure an invertible Jacobian by making the diagonal\n    // elements 1+small, and all other elements small.\n    for (size_t i = 0; i < Dim; ++i) {\n      jacobian_l.get(i, i) += 1.0;\n    }\n    return jacobian_l;\n  }();\n\n  const auto dest_tensor = transform::to_different_frame(src_tensor, jacobian);\n  const auto expected_src_tensor = transform::to_different_frame(\n      dest_tensor, determinant_and_inverse(jacobian).second);\n  CHECK_ITERABLE_APPROX(expected_src_tensor, src_tensor);\n\n  // check transformation of tensor with python test\n  CHECK_ITERABLE_APPROX(\n      dest_tensor,\n      tensor_transformed_by_python(src_tensor, dest_tensor, jacobian, \"\"));\n\n  // check with different overloads\n  const auto test_transform = [&jacobian, &nn_generator, &nn_interval_dis,\n                               &used_for_size](auto transform_type,\n                                               const std::string& suffix) {\n    const auto src_tnsr = make_with_random_values<decltype(transform_type)>(\n        nn_generator, nn_interval_dis, used_for_size);\n    const auto dest_tnsr = transform::to_different_frame(\n        src_tnsr, jacobian, determinant_and_inverse(jacobian).second);\n    const auto dest_tnsr_py = tensor_transformed_by_python(\n        src_tnsr, dest_tnsr, jacobian, \"_\" + suffix);\n    CHECK_ITERABLE_APPROX(dest_tnsr, dest_tnsr_py);\n  };\n  test_transform(Scalar<DataType>{}, \"Scalar\");\n  test_transform(tnsr::I<DataType, Dim, SrcFrame>{}, \"I\");\n  test_transform(tnsr::i<DataType, Dim, SrcFrame>{}, \"i\");\n  test_transform(tnsr::iJ<DataType, Dim, SrcFrame>{}, \"iJ\");\n  test_transform(tnsr::ii<DataType, Dim, SrcFrame>{}, \"ii\");\n  test_transform(tnsr::II<DataType, Dim, SrcFrame>{}, \"II\");\n  test_transform(tnsr::ijj<DataType, Dim, SrcFrame>{}, \"ijj\");\n\n  // special test case for the Scalar using not_null.\n  const auto src_scalar = make_with_random_values<Scalar<DataType>>(\n      nn_generator, nn_interval_dis, used_for_size);\n  auto dest_scalar = make_with_value<Scalar<DataType>>(\n      src_scalar, std::numeric_limits<double>::signaling_NaN());\n  transform::to_different_frame(make_not_null(&dest_scalar), src_scalar,\n                                jacobian,\n                                determinant_and_inverse(jacobian).second);\n  CHECK_ITERABLE_APPROX(dest_scalar, src_scalar);\n}\n\nvoid test_transform_first_index_to_different_frame() {\n  INFO(\"Transform first index\");\n  {\n    INFO(\"1D\");\n    InverseJacobian<double, 1, Frame::ElementLogical, Frame::Inertial>\n        inv_jacobian{};\n    get<0, 0>(inv_jacobian) = 2.0;\n    {\n      INFO(\"Vector\");\n      const tnsr::I<double, 1, Frame::Inertial> input{1.0};\n      const auto result = first_index_to_different_frame(input, inv_jacobian);\n      static_assert(std::is_same_v<std::decay_t<decltype(result)>,\n                                   tnsr::I<double, 1, Frame::ElementLogical>>);\n      CHECK(get<0>(result) == 2.0);\n    }\n    {\n      INFO(\"Rank 2 tensor\");\n      tnsr::Ij<double, 1, Frame::Inertial> input{};\n      get<0, 0>(input) = 1.0;\n      const auto result = first_index_to_different_frame(input, inv_jacobian);\n      static_assert(\n          std::is_same_v<\n              std::decay_t<decltype(result)>,\n              Tensor<\n                  double, Symmetry<1, 2>,\n                  index_list<SpatialIndex<1, UpLo::Up, Frame::ElementLogical>,\n                             SpatialIndex<1, UpLo::Lo, Frame::Inertial>>>>);\n      CHECK(get<0, 0>(result) == 2.0);\n    }\n  }\n  {\n    INFO(\"2D\");\n    InverseJacobian<double, 2, Frame::ElementLogical, Frame::Inertial>\n        inv_jacobian{};\n    get<0, 0>(inv_jacobian) = 2.0;\n    get<1, 1>(inv_jacobian) = 3.0;\n    get<0, 1>(inv_jacobian) = 0.5;\n    get<1, 0>(inv_jacobian) = 1.5;\n    {\n      INFO(\"Vector\");\n      const tnsr::I<double, 2, Frame::Inertial> input{{1.0, 2.0}};\n      const auto result = first_index_to_different_frame(input, inv_jacobian);\n      static_assert(std::is_same_v<std::decay_t<decltype(result)>,\n                                   tnsr::I<double, 2, Frame::ElementLogical>>);\n      CHECK(get<0>(result) == 3.);\n      CHECK(get<1>(result) == 7.5);\n    }\n    {\n      INFO(\"Rank 2 tensor\");\n      tnsr::Ij<double, 2, Frame::Inertial> input{};\n      get<0, 0>(input) = 1.0;\n      get<1, 0>(input) = 2.0;\n      get<0, 1>(input) = 3.0;\n      get<1, 1>(input) = 4.0;\n      const auto result = first_index_to_different_frame(input, inv_jacobian);\n      static_assert(\n          std::is_same_v<\n              std::decay_t<decltype(result)>,\n              Tensor<\n                  double, Symmetry<1, 2>,\n                  index_list<SpatialIndex<2, UpLo::Up, Frame::ElementLogical>,\n                             SpatialIndex<2, UpLo::Lo, Frame::Inertial>>>>);\n      CHECK(get<0, 0>(result) == 3.);\n      CHECK(get<1, 0>(result) == 7.5);\n      CHECK(get<0, 1>(result) == 8.);\n      CHECK(get<1, 1>(result) == 16.5);\n    }\n  }\n  {\n    INFO(\"Variables\");\n    const size_t num_points = 3;\n    InverseJacobian<DataVector, 2, Frame::ElementLogical, Frame::Inertial>\n        inv_jacobian{num_points};\n    get<0, 0>(inv_jacobian) = 2.0;\n    get<1, 1>(inv_jacobian) = 3.0;\n    get<0, 1>(inv_jacobian) = 0.5;\n    get<1, 0>(inv_jacobian) = 1.5;\n    Variables<tmpl::list<Var1, Var2>> input{num_points};\n    std::iota(get<Var1>(input).begin(), get<Var1>(input).end(), 1.0);\n    std::iota(get<Var2>(input).begin(), get<Var2>(input).end(), 1.0);\n    CAPTURE(input);\n    const auto result = first_index_to_different_frame(input, inv_jacobian);\n    const auto& var1 =\n        get<Tags::TransformedFirstIndex<Var1, Frame::ElementLogical>>(result);\n    const auto& var2 =\n        get<Tags::TransformedFirstIndex<Var2, Frame::ElementLogical>>(result);\n    CHECK(get<0>(var1) == 3.);\n    CHECK(get<1>(var1) == 7.5);\n    CHECK(get<0, 0>(var2) == 3.);\n    CHECK(get<1, 0>(var2) == 7.5);\n    CHECK(get<0, 1>(var2) == 8.);\n    CHECK(get<1, 1>(var2) == 16.5);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Tensor.EagerMath.FrameTransform\",\n                  \"[DataStructures][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"DataStructures/Tensor/EagerMath/\");\n  const DataVector dv(5);\n  test_transform_to_different_frame<1, Frame::Grid, Frame::Inertial>(double{});\n  test_transform_to_different_frame<2, Frame::Grid, Frame::Inertial>(double{});\n  test_transform_to_different_frame<3, Frame::Grid, Frame::Inertial>(double{});\n  test_transform_to_different_frame<1, Frame::Grid, Frame::Inertial>(dv);\n  test_transform_to_different_frame<2, Frame::Grid, Frame::Inertial>(dv);\n  test_transform_to_different_frame<3, Frame::Grid, Frame::Inertial>(dv);\n  test_transform_to_different_frame<1, Frame::Inertial, Frame::Distorted>(\n      double{});\n  test_transform_to_different_frame<2, Frame::Inertial, Frame::Distorted>(\n      double{});\n  test_transform_to_different_frame<3, Frame::Inertial, Frame::Distorted>(\n      double{});\n  test_transform_to_different_frame<1, Frame::Inertial, Frame::Distorted>(dv);\n  test_transform_to_different_frame<2, Frame::Inertial, Frame::Distorted>(dv);\n  test_transform_to_different_frame<3, Frame::Inertial, Frame::Distorted>(dv);\n  test_transform_to_different_frame<1, Frame::NoFrame, Frame::Inertial>(\n      double{});\n  test_transform_to_different_frame<2, Frame::NoFrame, Frame::Inertial>(\n      double{});\n  test_transform_to_different_frame<3, Frame::NoFrame, Frame::Inertial>(\n      double{});\n  test_transform_to_different_frame<1, Frame::NoFrame, Frame::Inertial>(dv);\n  test_transform_to_different_frame<2, Frame::NoFrame, Frame::Inertial>(dv);\n  test_transform_to_different_frame<3, Frame::NoFrame, Frame::Inertial>(dv);\n  test_transform_first_index_to_different_frame();\n}\n\n}  // namespace transform\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_GramSchmidtOrthonormalize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/GramSchmidtOrthonormalize.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Tensor.EagerMath.GramSchmidtOrthonormalize\",\n                  \"[DataStructures][Unit]\") {\n  const tnsr::aa<double, 3> minkowski_metric = []() {\n    tnsr::aa<double, 3> metric{};\n    get<0, 0>(metric) = -1.0;\n    get<1, 1>(metric) = 1.0;\n    get<2, 2>(metric) = 1.0;\n    get<3, 3>(metric) = 1.0;\n    return metric;\n  }();\n  const tnsr::ii<double, 3> flat_spatial = []() {\n    tnsr::ii<double, 3> metric{};\n    get<0, 0>(metric) = 1.0;\n    get<1, 1>(metric) = 1.0;\n    get<2, 2>(metric) = 1.0;\n    return metric;\n  }();\n\n  {\n    INFO(\"Already orthonormal basis\");\n    tnsr::A<double, 3> vec1{{{1.0, 0.0, 0.0, 0.0}}};\n    tnsr::A<double, 3> vec2{{{0.0, 1.0, 0.0, 0.0}}};\n    tnsr::A<double, 3> vec3{{{0.0, 0.0, 1.0, 0.0}}};\n\n    gram_schmidt_orthonormalize(\n        std::array{make_not_null(&vec1), make_not_null(&vec2),\n                   make_not_null(&vec3)},\n        minkowski_metric);\n\n    CHECK(get(dot_product(vec1, vec1, minkowski_metric)) == approx(-1.0));\n    CHECK(get(dot_product(vec2, vec2, minkowski_metric)) == approx(1.0));\n    CHECK(get(dot_product(vec3, vec3, minkowski_metric)) == approx(1.0));\n    CHECK(get(dot_product(vec1, vec2, minkowski_metric)) == approx(0.0));\n    CHECK(get(dot_product(vec1, vec3, minkowski_metric)) == approx(0.0));\n    CHECK(get(dot_product(vec2, vec3, minkowski_metric)) == approx(0.0));\n  }\n\n  {\n    INFO(\"Spacetime vectors\");\n    tnsr::A<double, 3> vec1{{{2.0, 1.0, 0.0, 0.0}}};\n    tnsr::A<double, 3> vec2{{{1.0, 1.0, 0.0, 0.0}}};  // <- null vector!\n    tnsr::A<double, 3> vec3{{{1.0, 2.0, -1.0, 1.0}}};\n\n    gram_schmidt_orthonormalize(\n        std::array{make_not_null(&vec1), make_not_null(&vec2),\n                   make_not_null(&vec3)},\n        minkowski_metric);\n\n    CHECK(get(dot_product(vec1, vec1, minkowski_metric)) == approx(-1.0));\n    // Null vector is now spatial\n    CHECK(get(dot_product(vec2, vec2, minkowski_metric)) == approx(1.0));\n    CHECK(get(dot_product(vec3, vec3, minkowski_metric)) == approx(1.0));\n    CHECK(get(dot_product(vec1, vec2, minkowski_metric)) == approx(0.0));\n    CHECK(get(dot_product(vec1, vec3, minkowski_metric)) == approx(0.0));\n    CHECK(get(dot_product(vec2, vec3, minkowski_metric)) == approx(0.0));\n  }\n\n  {\n    INFO(\"Spatial vectors\");\n    tnsr::I<double, 3> vec1{{{1.0, 0.0, 0.0}}};\n    tnsr::I<double, 3> vec2{{{1.0, 1.0, 0.0}}};\n    tnsr::I<double, 3> vec3{{{1.0, 1.0, 1.0}}};\n\n    gram_schmidt_orthonormalize(\n        std::array{make_not_null(&vec1), make_not_null(&vec2),\n                   make_not_null(&vec3)},\n        flat_spatial);\n\n    CHECK(get(dot_product(vec1, vec1, flat_spatial)) == approx(1.0));\n    CHECK(get(dot_product(vec2, vec2, flat_spatial)) == approx(1.0));\n    CHECK(get(dot_product(vec3, vec3, flat_spatial)) == approx(1.0));\n    CHECK(get(dot_product(vec1, vec2, flat_spatial)) == approx(0.0));\n    CHECK(get(dot_product(vec1, vec3, flat_spatial)) == approx(0.0));\n    CHECK(get(dot_product(vec2, vec3, flat_spatial)) == approx(0.0));\n  }\n\n  {\n    INFO(\"Nontrivial metric\");\n    const tnsr::aa<double, 3> tilted_metric = []() {\n      tnsr::aa<double, 3> metric{};\n      get<0, 0>(metric) = -2.0;\n      get<0, 1>(metric) = -0.5;\n      get<0, 2>(metric) = 0.3;\n      get<0, 3>(metric) = -0.1;\n      get<1, 1>(metric) = 3.0;\n      get<1, 2>(metric) = 0.4;\n      get<1, 3>(metric) = 0.2;\n      get<2, 2>(metric) = 1.5;\n      get<2, 3>(metric) = -0.3;\n      get<3, 3>(metric) = 2.5;\n      return metric;\n    }();\n\n    tnsr::A<double, 3> vec1{{{2.0, 1.0, 0.0, 0.0}}};\n    tnsr::A<double, 3> vec2{{{1.0, 2.0, 1.0, 0.0}}};\n    tnsr::A<double, 3> vec3{{{0.5, 0.0, 1.0, -1.0}}};\n\n    gram_schmidt_orthonormalize(\n        std::array{make_not_null(&vec1), make_not_null(&vec2),\n                   make_not_null(&vec3)},\n        tilted_metric);\n\n    CHECK(get(dot_product(vec1, vec1, tilted_metric)) == approx(-1.0));\n    CHECK(get(dot_product(vec2, vec2, tilted_metric)) == approx(1.0));\n    CHECK(get(dot_product(vec3, vec3, tilted_metric)) == approx(1.0));\n    CHECK(get(dot_product(vec1, vec2, tilted_metric)) == approx(0.0));\n    CHECK(get(dot_product(vec1, vec3, tilted_metric)) == approx(0.0));\n    CHECK(get(dot_product(vec2, vec3, tilted_metric)) == approx(0.0));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_Magnitude.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test_euclidean_magnitude() {\n  // Check for DataVectors\n  {\n    const size_t npts = 5;\n    const DataVector one(npts, 1.0);\n    const DataVector two(npts, 2.0);\n    const DataVector minus_three(npts, -3.0);\n    const DataVector four(npts, 4.0);\n    const DataVector minus_five(npts, -5.0);\n    const DataVector twelve(npts, 12.0);\n\n    const tnsr::i<DataVector, 1, Frame::Grid> one_d_covector{{{two}}};\n    CHECK_ITERABLE_APPROX(get(magnitude(one_d_covector)), two);\n\n    const tnsr::i<DataVector, 1, Frame::Grid> negative_one_d_covector{\n        {{minus_three}}};\n    CHECK_ITERABLE_APPROX(get(magnitude(negative_one_d_covector)),\n                          (DataVector{npts, 3.0}));\n\n    const tnsr::A<DataVector, 1, Frame::Grid> one_d_vector{\n        {{minus_three, four}}};\n    CHECK_ITERABLE_APPROX(get(magnitude(one_d_vector)),\n                          (DataVector{npts, 5.0}));\n\n    const tnsr::I<DataVector, 2, Frame::Grid> two_d_vector{\n        {{minus_five, twelve}}};\n    CHECK_ITERABLE_APPROX(get(magnitude(two_d_vector)),\n                          (DataVector{npts, 13.0}));\n\n    const tnsr::i<DataVector, 3, Frame::Grid> three_d_covector{\n        {{minus_three, twelve, four}}};\n    CHECK_ITERABLE_APPROX(get(magnitude(three_d_covector)),\n                          (DataVector{npts, 13.0}));\n\n    const tnsr::a<DataVector, 4, Frame::Grid> five_d_covector{\n        {{two, twelve, four, one, two}}};\n    CHECK_ITERABLE_APPROX(get(magnitude(five_d_covector)),\n                          (DataVector{npts, 13.0}));\n  }\n  // Check case for doubles\n  {\n    const tnsr::i<double, 1, Frame::Grid> one_d_covector_double{{{2.}}};\n    CHECK(get(magnitude(one_d_covector_double)) == 2.);\n\n    const tnsr::a<double, 4, Frame::Grid> five_d_covector_double{\n        {{2, 12, 4, 1, 2}}};\n    CHECK(get(magnitude(five_d_covector_double)) == 13.);\n  }\n}\n\nvoid test_magnitude() {\n  // Check for DataVectors\n  {\n    const size_t npts = 5;\n    const DataVector one(npts, 1.0);\n    const DataVector two(npts, 2.0);\n    const DataVector minus_three(npts, -3.0);\n    const DataVector four(npts, 4.0);\n    const DataVector minus_five(npts, -5.0);\n    const DataVector twelve(npts, 12.0);\n    const DataVector thirteen(npts, 13.0);\n\n    const tnsr::i<DataVector, 1, Frame::Grid> one_d_covector{{{two}}};\n    const tnsr::II<DataVector, 1, Frame::Grid> inv_h = [&four]() {\n      tnsr::II<DataVector, 1, Frame::Grid> tensor;\n      get<0, 0>(tensor) = four;\n      return tensor;\n    }();\n\n    CHECK_ITERABLE_APPROX(get(magnitude(one_d_covector, inv_h)),\n                          (DataVector{npts, 4.0}));\n    const tnsr::i<DataVector, 3, Frame::Grid> three_d_covector{\n        {{minus_three, twelve, four}}};\n    const tnsr::II<DataVector, 3, Frame::Grid> inv_g =\n        [&two, &minus_three, &four, &minus_five, &twelve, &thirteen]() {\n          tnsr::II<DataVector, 3, Frame::Grid> tensor;\n          get<0, 0>(tensor) = two;\n          get<0, 1>(tensor) = minus_three;\n          get<0, 2>(tensor) = four;\n          get<1, 1>(tensor) = minus_five;\n          get<1, 2>(tensor) = twelve;\n          get<2, 2>(tensor) = thirteen;\n          return tensor;\n        }();\n    CHECK_ITERABLE_APPROX(get(magnitude(three_d_covector, inv_g)),\n                          (DataVector{npts, sqrt(778.0)}));\n  }\n\n  {\n    // Check for doubles\n    const tnsr::i<double, 1, Frame::Grid> one_d_covector{2.0};\n    const tnsr::II<double, 1, Frame::Grid> inv_h = []() {\n      tnsr::II<double, 1, Frame::Grid> tensor{};\n      get<0, 0>(tensor) = 4.0;\n      return tensor;\n    }();\n\n    CHECK(get(magnitude(one_d_covector, inv_h)) == 4.0);\n\n    const tnsr::i<double, 3, Frame::Grid> three_d_covector{{{-3.0, 12.0, 4.0}}};\n    const tnsr::II<double, 3, Frame::Grid> inv_g = []() {\n      tnsr::II<double, 3, Frame::Grid> tensor{};\n      get<0, 0>(tensor) = 2.0;\n      get<0, 1>(tensor) = -3.0;\n      get<0, 2>(tensor) = 4.0;\n      get<1, 1>(tensor) = -5.0;\n      get<1, 2>(tensor) = 12.0;\n      get<2, 2>(tensor) = 13.0;\n      return tensor;\n    }();\n    CHECK(get(magnitude(three_d_covector, inv_g)) == sqrt(778.0));\n  }\n\n  {\n    INFO(\"Spacetime vector magnitude\");\n    const tnsr::A<double, 3> timelike_vector{{{1., 0., 0., 0.}}};\n    const tnsr::aa<double, 3> metric = []() {\n      tnsr::aa<double, 3> result{};\n      get<0, 0>(result) = -1.0;\n      get<1, 1>(result) = 1.0;\n      get<2, 2>(result) = 1.0;\n      get<3, 3>(result) = 1.0;\n      return result;\n    }();\n\n    CHECK(get(magnitude(timelike_vector, metric)) == 1.0);\n  }\n}\n\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Grid>;\n};\ntemplate <size_t Dim>\nstruct Covector : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Grid>;\n};\nstruct Metric : db::SimpleTag {\n  using type = tnsr::ii<DataVector, 3, Frame::Grid>;\n};\nstruct InverseMetric : db::SimpleTag {\n  using type = tnsr::II<DataVector, 3, Frame::Grid>;\n};\nvoid test_magnitude_tags() {\n  const auto box =\n      db::create<db::AddSimpleTags<Vector, Covector<2>>,\n                 db::AddComputeTags<Tags::EuclideanMagnitude<Vector>,\n                                    Tags::EuclideanMagnitude<Covector<2>>,\n                                    Tags::NormalizedCompute<Vector>,\n                                    Tags::NormalizedCompute<Covector<2>>>>(\n          tnsr::I<DataVector, 3, Frame::Grid>({{{1., 2.}, {2., 3.}, {2., 6.}}}),\n          tnsr::i<DataVector, 2, Frame::Grid>({{{3., 5.}, {4., 12.}}}));\n\n  CHECK(db::get<Tags::Magnitude<Vector>>(box) ==\n        Scalar<DataVector>({{{3., 7.}}}));\n  CHECK(db::get<Tags::Magnitude<Covector<2>>>(box) ==\n        Scalar<DataVector>({{{5., 13.}}}));\n  CHECK(db::get<Tags::Normalized<Vector>>(box) ==\n        tnsr::I<DataVector, 3, Frame::Grid>(\n            {{{1. / 3., 2. / 7.}, {2. / 3., 3. / 7.}, {2. / 3., 6. / 7.}}}));\n  CHECK(db::get<Tags::Normalized<Covector<2>>>(box) ==\n        tnsr::i<DataVector, 2, Frame::Grid>(\n            {{{3. / 5., 5. / 13.}, {4. / 5., 12. / 13.}}}));\n\n  using Tag = Vector;\n  TestHelpers::db::test_prefix_tag<Tags::Magnitude<Tag>>(\"Magnitude(Vector)\");\n  TestHelpers::db::test_prefix_tag<Tags::Normalized<Tag>>(\"Normalized(Vector)\");\n  TestHelpers::db::test_compute_tag<Tags::EuclideanMagnitude<Tag>>(\n      \"Magnitude(Vector)\");\n  TestHelpers::db::test_compute_tag<Tags::NormalizedCompute<Tag>>(\n      \"Normalized(Vector)\");\n}\n\nvoid test_general_magnitude_tags() {\n  constexpr size_t npts = 5;\n  const tnsr::i<DataVector, 3, Frame::Grid> covector{\n      {{DataVector{npts, -3.0}, DataVector{npts, 12.0},\n        DataVector{npts, 4.0}}}};\n  const tnsr::II<DataVector, 3, Frame::Grid> inv_metric = []() {\n    auto tensor = make_with_value<tnsr::II<DataVector, 3, Frame::Grid>>(\n        DataVector{npts}, 0.0);\n    get<0, 0>(tensor) = 2.0;\n    get<0, 1>(tensor) = -3.0;\n    get<0, 2>(tensor) = 4.0;\n    get<1, 1>(tensor) = -5.0;\n    get<1, 2>(tensor) = 12.0;\n    get<2, 2>(tensor) = 13.0;\n    return tensor;\n  }();\n\n  const tnsr::I<DataVector, 3, Frame::Grid> vector{\n      {{DataVector{npts, -3.0}, DataVector{npts, 12.0},\n        DataVector{npts, 4.0}}}};\n  const tnsr::ii<DataVector, 3, Frame::Grid> metric = []() {\n    auto tensor = make_with_value<tnsr::ii<DataVector, 3, Frame::Grid>>(\n        DataVector{npts}, 0.0);\n    get<0, 0>(tensor) = 2.0;\n    get<0, 1>(tensor) = -3.0;\n    get<0, 2>(tensor) = 4.0;\n    get<1, 1>(tensor) = -5.0;\n    get<1, 2>(tensor) = 12.0;\n    get<2, 2>(tensor) = 13.0;\n    return tensor;\n  }();\n\n  const auto box =\n      db::create<db::AddSimpleTags<Vector, Covector<3>, Metric, InverseMetric>,\n                 db::AddComputeTags<\n                     Tags::NonEuclideanMagnitude<Vector, Metric>,\n                     Tags::NonEuclideanMagnitude<Covector<3>, InverseMetric>,\n                     Tags::NormalizedCompute<Vector>,\n                     Tags::NormalizedCompute<Covector<3>>>>(vector, covector,\n                                                            metric, inv_metric);\n  TestHelpers::db::test_compute_tag<\n      Tags::NonEuclideanMagnitude<Vector, Metric>>(\"Magnitude(Vector)\");\n\n  CHECK_ITERABLE_APPROX(get(db::get<Tags::Magnitude<Vector>>(box)),\n                        (DataVector{npts, sqrt(778.0)}));\n  CHECK_ITERABLE_APPROX(get<0>(db::get<Tags::Normalized<Vector>>(box)),\n                        get<0>(vector) / sqrt(778.0));\n  CHECK_ITERABLE_APPROX(get<1>(db::get<Tags::Normalized<Vector>>(box)),\n                        get<1>(vector) / sqrt(778.0));\n  CHECK_ITERABLE_APPROX(get<2>(db::get<Tags::Normalized<Vector>>(box)),\n                        get<2>(vector) / sqrt(778.0));\n\n  CHECK_ITERABLE_APPROX(get(db::get<Tags::Magnitude<Covector<3>>>(box)),\n                        (DataVector{npts, sqrt(778.0)}));\n  CHECK_ITERABLE_APPROX(get<0>(db::get<Tags::Normalized<Covector<3>>>(box)),\n                        get<0>(covector) / sqrt(778.0));\n  CHECK_ITERABLE_APPROX(get<1>(db::get<Tags::Normalized<Covector<3>>>(box)),\n                        get<1>(covector) / sqrt(778.0));\n  CHECK_ITERABLE_APPROX(get<2>(db::get<Tags::Normalized<Covector<3>>>(box)),\n                        get<2>(covector) / sqrt(778.0));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.Magnitude\",\n                  \"[DataStructures][Unit]\") {\n  test_euclidean_magnitude();\n  test_magnitude();\n  test_magnitude_tags();\n  test_general_magnitude_tags();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_Norms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Norms.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct MyScalar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\ntemplate <size_t Dim, typename Frame>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame>;\n};\ntemplate <size_t Dim, typename Frame>\nstruct Covector : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame>;\n};\ntemplate <size_t Dim, typename Frame>\nstruct Metric : db::SimpleTag {\n  using type = tnsr::ii<DataVector, Dim, Frame>;\n};\ntemplate <size_t Dim, typename Frame>\nstruct InverseMetric : db::SimpleTag {\n  using type = tnsr::II<DataVector, Dim, Frame>;\n};\n\ntemplate <typename Frame>\nvoid test_l2_norm_tag() {\n  constexpr size_t npts = 5;\n\n  const DataVector one(npts, 1.);\n  const DataVector two(npts, 2.);\n  const DataVector minus_three(npts, -3.);\n  const DataVector four(npts, 4.);\n  const DataVector minus_five(npts, -5.);\n  const DataVector twelve(npts, 12.);\n  const auto mixed = []() {\n    DataVector tmp(npts, 0.);\n    for (size_t i = 0; i < npts; ++i) {\n      if (i % 2) {\n        tmp[i] = 3.5;\n      } else {\n        tmp[i] = 4.5;\n      }\n    }\n    return tmp;\n  }();\n\n  // create test tensors\n  const auto psi_1d = [&npts, &minus_five]() {\n    tnsr::ii<DataVector, 1, Frame> tmp{npts};\n    get<0, 0>(tmp) = minus_five;\n    return tmp;\n  }();\n  const auto psi_2d = [&npts, &one, &two]() {\n    tnsr::ii<DataVector, 2, Frame> tmp{npts};\n    get<0, 0>(tmp) = two;\n    get<0, 1>(tmp) = two;\n    get<1, 0>(tmp) = two;\n    get<1, 1>(tmp) = one;\n    return tmp;\n  }();\n  const auto psi_3d = [&npts, &one, &two, &minus_three]() {\n    tnsr::ii<DataVector, 3, Frame> tmp{npts};\n    get<0, 0>(tmp) = one;\n    get<0, 1>(tmp) = two;\n    get<0, 2>(tmp) = minus_three;\n    get<1, 1>(tmp) = two;\n    get<1, 2>(tmp) = two;\n    get<2, 2>(tmp) = one;\n    return tmp;\n  }();\n\n  const auto box = db::create<\n      db::AddSimpleTags<MyScalar, Vector<1, Frame>, Vector<2, Frame>,\n                        Vector<3, Frame>, Covector<1, Frame>,\n                        Covector<2, Frame>, Covector<3, Frame>,\n                        Metric<1, Frame>, Metric<2, Frame>, Metric<3, Frame>,\n                        InverseMetric<1, Frame>, InverseMetric<2, Frame>,\n                        InverseMetric<3, Frame>>,\n      db::AddComputeTags<Tags::PointwiseL2NormCompute<MyScalar>,\n                         Tags::PointwiseL2NormCompute<Vector<1, Frame>>,\n                         Tags::PointwiseL2NormCompute<Vector<2, Frame>>,\n                         Tags::PointwiseL2NormCompute<Vector<3, Frame>>,\n                         Tags::PointwiseL2NormCompute<Covector<1, Frame>>,\n                         Tags::PointwiseL2NormCompute<Covector<2, Frame>>,\n                         Tags::PointwiseL2NormCompute<Covector<3, Frame>>,\n                         Tags::PointwiseL2NormCompute<Metric<1, Frame>>,\n                         Tags::PointwiseL2NormCompute<Metric<2, Frame>>,\n                         Tags::PointwiseL2NormCompute<Metric<3, Frame>>,\n                         Tags::PointwiseL2NormCompute<InverseMetric<1, Frame>>,\n                         Tags::PointwiseL2NormCompute<InverseMetric<2, Frame>>,\n                         Tags::PointwiseL2NormCompute<InverseMetric<3, Frame>>,\n                         Tags::L2NormCompute<MyScalar>,\n                         Tags::L2NormCompute<Vector<1, Frame>>,\n                         Tags::L2NormCompute<Vector<2, Frame>>,\n                         Tags::L2NormCompute<Vector<3, Frame>>,\n                         Tags::L2NormCompute<Covector<1, Frame>>,\n                         Tags::L2NormCompute<Covector<2, Frame>>,\n                         Tags::L2NormCompute<Covector<3, Frame>>,\n                         Tags::L2NormCompute<Metric<1, Frame>>,\n                         Tags::L2NormCompute<Metric<2, Frame>>,\n                         Tags::L2NormCompute<Metric<3, Frame>>,\n                         Tags::L2NormCompute<InverseMetric<1, Frame>>,\n                         Tags::L2NormCompute<InverseMetric<2, Frame>>,\n                         Tags::L2NormCompute<InverseMetric<3, Frame>>>>(\n      Scalar<DataVector>{{{minus_three}}},\n      tnsr::I<DataVector, 1, Frame>{{{mixed}}},\n      tnsr::I<DataVector, 2, Frame>{{{minus_three, mixed}}},\n      tnsr::I<DataVector, 3, Frame>{{{minus_three, mixed, four}}},\n      tnsr::i<DataVector, 1, Frame>{{{four}}},\n      tnsr::i<DataVector, 2, Frame>{{{four, two}}},\n      tnsr::i<DataVector, 3, Frame>{{{four, two, twelve}}}, psi_1d, psi_2d,\n      psi_3d, determinant_and_inverse(psi_1d).second,\n      determinant_and_inverse(psi_2d).second,\n      determinant_and_inverse(psi_3d).second);\n\n  // Test point-wise L2-norm against precomputed values\n  // rank 0\n  CHECK_ITERABLE_APPROX(get(db::get<Tags::PointwiseL2Norm<MyScalar>>(box)),\n                        DataVector(npts, 3.));\n  // rank (1, 0)\n  const auto verification_vec_1d = []() {\n    DataVector tmp(npts, 0.);\n    for (size_t i = 0; i < npts; ++i) {\n      if (i % 2) {\n        tmp[i] = 3.5;\n      } else {\n        tmp[i] = 4.5;\n      }\n    }\n    return tmp;\n  }();\n  const auto verification_vec_2d = []() {\n    DataVector tmp(npts, 0.);\n    for (size_t i = 0; i < npts; ++i) {\n      if (i % 2) {\n        tmp[i] = 4.6097722286464435;\n      } else {\n        tmp[i] = 5.408326913195984;\n      }\n    }\n    return tmp;\n  }();\n  const auto verification_vec_3d = []() {\n    DataVector tmp(npts, 0.);\n    for (size_t i = 0; i < npts; ++i) {\n      if (i % 2) {\n        tmp[i] = 6.103277807866851;\n      } else {\n        tmp[i] = 6.726812023536855;\n      }\n    }\n    return tmp;\n  }();\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Vector<1, Frame>>>(box)),\n      verification_vec_1d);\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Vector<2, Frame>>>(box)),\n      verification_vec_2d);\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Vector<3, Frame>>>(box)),\n      verification_vec_3d);\n  // rank (0, 1)\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Covector<1, Frame>>>(box)),\n      DataVector(npts, 4.));\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Covector<2, Frame>>>(box)),\n      DataVector(npts, 4.47213595499958));\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Covector<3, Frame>>>(box)),\n      DataVector(npts, 12.806248474865697));\n  // rank (0, 2)\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Metric<1, Frame>>>(box)),\n      DataVector(npts, 5.));\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Metric<2, Frame>>>(box)),\n      DataVector(npts, 3.605551275463989));\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<Metric<3, Frame>>>(box)),\n      DataVector(npts, 6.324555320336759));\n  // rank (2, 0)\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<InverseMetric<1, Frame>>>(box)),\n      DataVector(npts, 0.2));\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<InverseMetric<2, Frame>>>(box)),\n      DataVector(npts, 1.8027756377319946));\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::PointwiseL2Norm<InverseMetric<3, Frame>>>(box)),\n      DataVector(npts, 0.4787135538781691));\n\n  // Test L2-norm reduced over domain, against precomputed values\n  // rank 0\n  CHECK(db::get<Tags::L2Norm<MyScalar>>(box) == approx(3.));\n  // rank (1, 0)\n  CHECK(db::get<Tags::L2Norm<Vector<1, Frame>>>(box) ==\n        approx(4.129164564412516));\n  CHECK(db::get<Tags::L2Norm<Vector<2, Frame>>>(box) ==\n        approx(5.103920062069938));\n  CHECK(db::get<Tags::L2Norm<Vector<3, Frame>>>(box) ==\n        approx(6.48459713474939));\n  // rank (0, 1)\n  CHECK(db::get<Tags::L2Norm<Covector<1, Frame>>>(box) == approx(4.));\n  CHECK(db::get<Tags::L2Norm<Covector<2, Frame>>>(box) ==\n        approx(4.47213595499958));\n  CHECK(db::get<Tags::L2Norm<Covector<3, Frame>>>(box) ==\n        approx(12.806248474865697));\n  // rank (0, 2)\n  CHECK(db::get<Tags::L2Norm<Metric<1, Frame>>>(box) == approx(5.));\n  CHECK(db::get<Tags::L2Norm<Metric<2, Frame>>>(box) ==\n        approx(3.605551275463989));\n  CHECK(db::get<Tags::L2Norm<Metric<3, Frame>>>(box) ==\n        approx(6.324555320336759));\n  // rank (2, 0)\n  CHECK(db::get<Tags::L2Norm<InverseMetric<1, Frame>>>(box) == approx(0.2));\n  CHECK(db::get<Tags::L2Norm<InverseMetric<2, Frame>>>(box) ==\n        approx(1.8027756377319946));\n  CHECK(db::get<Tags::L2Norm<InverseMetric<3, Frame>>>(box) ==\n        approx(0.4787135538781691));\n\n  // Check tag names\n  using Tag = MyScalar;\n  TestHelpers::db::test_simple_tag<Tags::PointwiseL2Norm<Tag>>(\n      \"PointwiseL2Norm(MyScalar)\");\n  TestHelpers::db::test_compute_tag<Tags::PointwiseL2NormCompute<Tag>>(\n      \"PointwiseL2Norm(MyScalar)\");\n  TestHelpers::db::test_simple_tag<Tags::L2Norm<Tag>>(\"L2Norm(MyScalar)\");\n  TestHelpers::db::test_compute_tag<Tags::L2NormCompute<Tag>>(\n      \"L2Norm(MyScalar)\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.Norms\",\n                  \"[DataStructures][Unit]\") {\n  test_l2_norm_tag<Frame::Grid>();\n  test_l2_norm_tag<Frame::Inertial>();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_OrthonormalOneform.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/OrthonormalOneform.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct TestOrthonormalForms {\n  template <typename DataType, typename Frame>\n  void operator()(\n      const tnsr::i<DataType, Dim, Frame>& unit_form,\n      const tnsr::ii<DataType, Dim, Frame>& spatial_metric,\n      const tnsr::II<DataType, Dim, Frame>& inv_spatial_metric) const;\n};\n\ntemplate <>\nstruct TestOrthonormalForms<2> {\n  template <typename DataType, typename Frame>\n  void operator()(\n      const tnsr::i<DataType, 2, Frame>& unit_form,\n      const tnsr::ii<DataType, 2, Frame>& /*spatial_metric*/,\n      const tnsr::II<DataType, 2, Frame>& inv_spatial_metric) const {\n    const auto orthonormal_form =\n        orthonormal_oneform(unit_form, inv_spatial_metric);\n\n    const auto zero = make_with_value<Scalar<DataType>>(unit_form, 0.0);\n    const auto one = make_with_value<Scalar<DataType>>(unit_form, 1.0);\n    CHECK_ITERABLE_APPROX(dot_product(unit_form, unit_form, inv_spatial_metric),\n                          one);\n    CHECK_ITERABLE_APPROX(\n        dot_product(orthonormal_form, orthonormal_form, inv_spatial_metric),\n        one);\n    CHECK_ITERABLE_APPROX(\n        dot_product(unit_form, orthonormal_form, inv_spatial_metric), zero);\n  }\n};\n\ntemplate <>\nstruct TestOrthonormalForms<3> {\n  template <typename DataType, typename Frame>\n  void operator()(\n      const tnsr::i<DataType, 3, Frame>& unit_form,\n      const tnsr::ii<DataType, 3, Frame>& spatial_metric,\n      const tnsr::II<DataType, 3, Frame>& inv_spatial_metric) const {\n    const auto first_orthonormal_form =\n        orthonormal_oneform(unit_form, inv_spatial_metric);\n    const auto second_orthonormal_form =\n        orthonormal_oneform(unit_form, first_orthonormal_form, spatial_metric,\n                            determinant(spatial_metric));\n\n    const auto zero = make_with_value<Scalar<DataType>>(unit_form, 0.0);\n    const auto one = make_with_value<Scalar<DataType>>(unit_form, 1.0);\n    CHECK_ITERABLE_APPROX(dot_product(unit_form, unit_form, inv_spatial_metric),\n                          one);\n    CHECK_ITERABLE_APPROX(\n        dot_product(first_orthonormal_form, first_orthonormal_form,\n                    inv_spatial_metric),\n        one);\n    CHECK_ITERABLE_APPROX(\n        dot_product(second_orthonormal_form, second_orthonormal_form,\n                    inv_spatial_metric),\n        one);\n    CHECK_ITERABLE_APPROX(\n        dot_product(unit_form, first_orthonormal_form, inv_spatial_metric),\n        zero);\n    CHECK_ITERABLE_APPROX(\n        dot_product(unit_form, second_orthonormal_form, inv_spatial_metric),\n        zero);\n    CHECK_ITERABLE_APPROX(\n        dot_product(first_orthonormal_form, second_orthonormal_form,\n                    inv_spatial_metric),\n        zero);\n  }\n};\n\ntemplate <size_t Dim, typename Frame, typename DataType>\nvoid check_orthonormal_forms(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  const TestOrthonormalForms<Dim> test;\n\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<Dim, DataType, Frame>(\n          &generator, used_for_size);\n  const auto inv_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto unit_vector = random_unit_normal(&generator, spatial_metric);\n  const auto unit_form = raise_or_lower_index(unit_vector, spatial_metric);\n\n  // test for random unit form\n  test(unit_form, spatial_metric, inv_spatial_metric);\n\n  // test for unit form along coordinate axes\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    auto basis_form = euclidean_basis_vector(direction, used_for_size);\n    const DataType inv_norm =\n        1.0 / get(magnitude(basis_form, inv_spatial_metric));\n    for (size_t i = 0; i < Dim; ++i) {\n      basis_form.get(i) *= inv_norm;\n    }\n    test(basis_form, spatial_metric, inv_spatial_metric);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.OrthonormalOneform\",\n                  \"[Unit][DataStructures]\") {\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(check_orthonormal_forms, (2, 3),\n                                    (Frame::Inertial))\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_OuterProduct.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/OuterProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\ntemplate <typename DataType>\nvoid test(const DataType& used_for_size) {\n  // Explicit types in return values to check return types (symmetry etc)\n  {\n    const tnsr::iJ<DataType, 1> result =\n        outer_product(make_with_value<tnsr::i<DataType, 1>>(used_for_size, 2.),\n                      make_with_value<tnsr::I<DataType, 1>>(used_for_size, 4.));\n    CHECK_ITERABLE_APPROX((get<0, 0>(result)),\n                          make_with_value<DataType>(used_for_size, 8.));\n  }\n  {\n    const tnsr::ijj<DataType, 1> result = outer_product(\n        make_with_value<tnsr::i<DataType, 1>>(used_for_size, 2.),\n        make_with_value<tnsr::ii<DataType, 1>>(used_for_size, 4.));\n    CHECK_ITERABLE_APPROX((get<0, 0, 0>(result)),\n                          make_with_value<DataType>(used_for_size, 8.));\n  }\n  {\n    const tnsr::Ijk<DataType, 1> result = outer_product(\n        make_with_value<tnsr::I<DataType, 1>>(used_for_size, 2.),\n        make_with_value<tnsr::ij<DataType, 1>>(used_for_size, 4.));\n    CHECK_ITERABLE_APPROX((get<0, 0, 0>(result)),\n                          make_with_value<DataType>(used_for_size, 8.));\n  }\n  {\n    tnsr::i<DataType, 2> lhs{};\n    get<0>(lhs) = make_with_value<DataType>(used_for_size, 1.);\n    get<1>(lhs) = make_with_value<DataType>(used_for_size, 2.);\n    tnsr::Ij<DataType, 2> rhs{};\n    get<0, 0>(rhs) = make_with_value<DataType>(used_for_size, 1.);\n    get<0, 1>(rhs) = make_with_value<DataType>(used_for_size, 2.);\n    get<1, 0>(rhs) = make_with_value<DataType>(used_for_size, 3.);\n    get<1, 1>(rhs) = make_with_value<DataType>(used_for_size, 4.);\n    const tnsr::iJk<DataType, 2> result = outer_product(lhs, rhs);\n    CHECK_ITERABLE_APPROX((get<0, 0, 0>(result)),\n                          make_with_value<DataType>(used_for_size, 1.));\n    CHECK_ITERABLE_APPROX((get<0, 0, 1>(result)),\n                          make_with_value<DataType>(used_for_size, 2.));\n    CHECK_ITERABLE_APPROX((get<0, 1, 0>(result)),\n                          make_with_value<DataType>(used_for_size, 3.));\n    CHECK_ITERABLE_APPROX((get<0, 1, 1>(result)),\n                          make_with_value<DataType>(used_for_size, 4.));\n    CHECK_ITERABLE_APPROX((get<1, 0, 0>(result)),\n                          make_with_value<DataType>(used_for_size, 2.));\n    CHECK_ITERABLE_APPROX((get<1, 0, 1>(result)),\n                          make_with_value<DataType>(used_for_size, 4.));\n    CHECK_ITERABLE_APPROX((get<1, 1, 0>(result)),\n                          make_with_value<DataType>(used_for_size, 6.));\n    CHECK_ITERABLE_APPROX((get<1, 1, 1>(result)),\n                          make_with_value<DataType>(used_for_size, 8.));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.EagerMath.OuterProduct\",\n                  \"[DataStructures][Unit]\") {\n  test(DataVector(3));\n  test(3.);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_RaiseOrLowerIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim, UpLo UpOrLo, IndexType Index, typename DataType>\nvoid test_raise_or_lower_first_index(const DataType& used_for_size) {\n  using Index0 =\n      Tensor_detail::TensorIndexType<Dim, UpOrLo, Frame::Inertial, Index>;\n  using Index1 =\n      Tensor_detail::TensorIndexType<Dim, UpLo::Lo, Frame::Inertial, Index>;\n  Tensor<DataType, Symmetry<2, 1, 1>,\n         index_list<change_index_up_lo<Index0>, Index1, Index1>> (*f)(\n      const Tensor<DataType, Symmetry<2, 1, 1>,\n                   index_list<Index0, Index1, Index1>>&,\n      const Tensor<DataType, Symmetry<1, 1>,\n                   index_list<change_index_up_lo<Index0>,\n                              change_index_up_lo<Index0>>>&) =\n      &raise_or_lower_first_index<DataType, Index0, Index1>;\n  pypp::check_with_random_values<1>(f, \"RaiseOrLowerIndex\",\n                                    \"raise_or_lower_first_index\",\n                                    {{{-10., 10.}}}, used_for_size);\n}\n\ntemplate <size_t Dim, UpLo UpOrLo, IndexType Index, typename DataType>\nvoid test_raise_or_lower(const DataType& used_for_size) {\n  using Index0 =\n      Tensor_detail::TensorIndexType<Dim, UpOrLo, Frame::Inertial, Index>;\n  Tensor<DataType, Symmetry<1>, index_list<change_index_up_lo<Index0>>> (*f)(\n      const Tensor<DataType, Symmetry<1>, index_list<Index0>>&,\n      const Tensor<DataType, Symmetry<1, 1>,\n                   index_list<change_index_up_lo<Index0>,\n                              change_index_up_lo<Index0>>>&) =\n      &raise_or_lower_index<DataType, DataType, Index0>;\n  pypp::check_with_random_values<1>(f, \"numpy\", \"matmul\", {{{-10., 10.}}},\n                                    used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Tensor.EagerMath.RaiseOrLowerIndex\",\n                  \"[DataStructures][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"DataStructures/Tensor/EagerMath/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_raise_or_lower_first_index, (1, 2, 3),\n                                    (UpLo::Lo, UpLo::Up),\n                                    (IndexType::Spatial, IndexType::Spacetime));\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_raise_or_lower, (1, 2, 3),\n                                    (UpLo::Lo, UpLo::Up),\n                                    (IndexType::Spatial, IndexType::Spacetime));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Test_Trace.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim, UpLo UpLo0, UpLo UpLo1, typename Fr,\n          IndexType TypeOfIndex, typename DataType>\nvoid test_trace_last_indices(const DataType& used_for_size) {\n  using Index0 = Tensor_detail::TensorIndexType<Dim, UpLo0, Fr, TypeOfIndex>;\n  using Index1 = Tensor_detail::TensorIndexType<Dim, UpLo1, Fr, TypeOfIndex>;\n  Tensor<DataType, Symmetry<1>, index_list<Index0>> (*f)(\n      const Tensor<DataType, Symmetry<2, 1, 1>,\n                   index_list<Index0, Index1, Index1>>&,\n      const Tensor<DataType, Symmetry<1, 1>,\n                   index_list<change_index_up_lo<Index1>,\n                              change_index_up_lo<Index1>>>&) =\n      &trace_last_indices<DataType, Index0, Index1>;\n  pypp::check_with_random_values<1>(f, \"Trace\", \"trace_last_indices\",\n                                    {{{-10., 10.}}}, used_for_size);\n}\ntemplate <size_t Dim, UpLo UpOrLo, typename Fr, IndexType TypeOfIndex,\n          typename DataType>\nvoid test_trace(const DataType& used_for_size) {\n  using Index0 = Tensor_detail::TensorIndexType<Dim, UpOrLo, Fr, TypeOfIndex>;\n  Scalar<DataType> (*f)(\n      const Tensor<DataType, Symmetry<1, 1>, index_list<Index0, Index0>>&,\n      const Tensor<DataType, Symmetry<1, 1>,\n                   index_list<change_index_up_lo<Index0>,\n                              change_index_up_lo<Index0>>>&) =\n      &trace<DataType, Index0>;\n  pypp::check_with_random_values<1>(f, \"numpy\", \"tensordot\", {{{-10., 10.}}},\n                                    used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Tensor.EagerMath.Trace\", \"[DataStructures][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"DataStructures/Tensor/EagerMath/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_trace_last_indices, (1, 2, 3),\n                                    (UpLo::Lo, UpLo::Up), (UpLo::Lo, UpLo::Up),\n                                    (Frame::Grid, Frame::Inertial),\n                                    (IndexType::Spatial, IndexType::Spacetime));\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_trace, (1, 2, 3), (UpLo::Lo, UpLo::Up),\n                                    (Frame::Grid, Frame::Inertial),\n                                    (IndexType::Spatial, IndexType::Spacetime));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/EagerMath/Trace.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef trace_last_indices(tensor, metric):\n    return np.einsum(\"ij,kij\", metric, tensor)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Expressions\")\n\nset(LIBRARY_SOURCES\n  Test_AddSubSymmetry.cpp\n  Test_AddSubtract.cpp\n  Test_Contract.cpp\n  Test_DataTypeSupport.cpp\n  Test_Divide.cpp\n  Test_Evaluate.cpp\n  Test_EvaluateComplex.cpp\n  Test_EvaluateRank3NonSymmetric.cpp\n  Test_EvaluateRank3Symmetric.cpp\n  Test_EvaluateRank4.cpp\n  Test_EvaluateSpatialAndTimeSpacetimeIndex.cpp\n  Test_EvaluateSpatialSpacetimeIndex.cpp\n  Test_EvaluateTimeIndex.cpp\n  Test_Examples.cpp\n  Test_MixedOperations.cpp\n  Test_Negate.cpp\n  Test_Product.cpp\n  Test_ProductHighRankIntermediate.cpp\n  Test_SpatialSpacetimeIndex.cpp\n  Test_SquareRoot.cpp\n  Test_TensorIndex.cpp\n  Test_TensorIndexTransformation.cpp\n  Test_TimeIndex.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  GeneralRelativity\n  GeneralRelativityHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_AddSubSymmetry.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test_impl_consistency() {\n  const std::string error_msg =\n      \"Symmetry and AddSubSymmetry are no longer using the same canonical \"\n      \"form. You must update the implementation for \"\n      \"tenex::detail::get_addsub_symm to use the same canonical form as \"\n      \"Symmetry and update the expected result symmetries for the unit test \"\n      \"cases in this file.\";\n\n  if (not std::is_same_v<Symmetry<>, tmpl::integral_list<std::int32_t>>) {\n    ERROR(error_msg);\n  }\n  if (not std::is_same_v<Symmetry<4>, tmpl::integral_list<std::int32_t, 1>>) {\n    ERROR(error_msg);\n  }\n  if (not std::is_same_v<Symmetry<1, 2>,\n                         tmpl::integral_list<std::int32_t, 2, 1>>) {\n    ERROR(error_msg);\n  }\n  if (not std::is_same_v<Symmetry<3, 5>,\n                         tmpl::integral_list<std::int32_t, 2, 1>>) {\n    ERROR(error_msg);\n  }\n  if (not std::is_same_v<Symmetry<2, 2, 2>,\n                         tmpl::integral_list<std::int32_t, 1, 1, 1>>) {\n    ERROR(error_msg);\n  }\n  if (not std::is_same_v<Symmetry<8, 4, 5, 5, 8>,\n                         tmpl::integral_list<std::int32_t, 1, 3, 2, 2, 1>>) {\n    ERROR(error_msg);\n  }\n}\n\nvoid test_rank0() {\n  using symm = Symmetry<>;\n  using tensorindex_list = make_tensorindex_list<>;\n\n  CHECK(\n      std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                         symm, symm, tensorindex_list, tensorindex_list>::type,\n                     tmpl::integral_list<std::int32_t>>);\n}\n\nvoid test_rank1() {\n  using symm = Symmetry<1>;\n  using tensorindex_list = make_tensorindex_list<ti::a>;\n\n  CHECK(\n      std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                         symm, symm, tensorindex_list, tensorindex_list>::type,\n                     tmpl::integral_list<std::int32_t, 1>>);\n}\n\nvoid test_rank2() {\n  using symmetric_symm = Symmetry<1, 1>;\n  using asymmetric_symm = Symmetry<2, 1>;\n  using tensorindex_list_ij = make_tensorindex_list<ti::i, ti::j>;\n  using tensorindex_list_ji = make_tensorindex_list<ti::j, ti::i>;\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symmetric_symm, symmetric_symm, tensorindex_list_ij,\n                           tensorindex_list_ij>::type,\n                       tmpl::integral_list<std::int32_t, 1, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symmetric_symm, symmetric_symm, tensorindex_list_ij,\n                           tensorindex_list_ji>::type,\n                       tmpl::integral_list<std::int32_t, 1, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           asymmetric_symm, symmetric_symm, tensorindex_list_ij,\n                           tensorindex_list_ij>::type,\n                       tmpl::integral_list<std::int32_t, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           asymmetric_symm, symmetric_symm, tensorindex_list_ij,\n                           tensorindex_list_ji>::type,\n                       tmpl::integral_list<std::int32_t, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symmetric_symm, asymmetric_symm, tensorindex_list_ij,\n                           tensorindex_list_ij>::type,\n                       tmpl::integral_list<std::int32_t, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symmetric_symm, asymmetric_symm, tensorindex_list_ij,\n                           tensorindex_list_ji>::type,\n                       tmpl::integral_list<std::int32_t, 2, 1>>);\n}\n\nvoid test_rank3() {\n  using symm_111 = Symmetry<1, 1, 1>;\n  using symm_121 = Symmetry<1, 2, 1>;\n  using symm_211 = Symmetry<2, 1, 1>;\n  using symm_221 = Symmetry<2, 2, 1>;\n  using symm_321 = Symmetry<3, 2, 1>;\n\n  using tensorindex_list_abc = make_tensorindex_list<ti::a, ti::b, ti::c>;\n  using tensorindex_list_acb = make_tensorindex_list<ti::a, ti::c, ti::b>;\n  using tensorindex_list_bac = make_tensorindex_list<ti::b, ti::a, ti::c>;\n  using tensorindex_list_bca = make_tensorindex_list<ti::b, ti::c, ti::a>;\n  using tensorindex_list_cab = make_tensorindex_list<ti::c, ti::a, ti::b>;\n  using tensorindex_list_cba = make_tensorindex_list<ti::c, ti::b, ti::a>;\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_111, symm_121, tensorindex_list_abc,\n                           tensorindex_list_bca>::type,\n                       tmpl::integral_list<std::int32_t, 2, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_121, symm_111, tensorindex_list_abc,\n                           tensorindex_list_bca>::type,\n                       tmpl::integral_list<std::int32_t, 1, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_111, symm_221, tensorindex_list_abc,\n                           tensorindex_list_acb>::type,\n                       tmpl::integral_list<std::int32_t, 1, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_221, symm_111, tensorindex_list_abc,\n                           tensorindex_list_acb>::type,\n                       tmpl::integral_list<std::int32_t, 2, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_121, symm_221, tensorindex_list_abc,\n                           tensorindex_list_cab>::type,\n                       tmpl::integral_list<std::int32_t, 1, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_221, symm_121, tensorindex_list_abc,\n                           tensorindex_list_cab>::type,\n                       tmpl::integral_list<std::int32_t, 3, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_221, symm_121, tensorindex_list_cab,\n                           tensorindex_list_abc>::type,\n                       tmpl::integral_list<std::int32_t, 2, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_121, symm_221, tensorindex_list_cab,\n                           tensorindex_list_abc>::type,\n                       tmpl::integral_list<std::int32_t, 3, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_121, symm_221, tensorindex_list_abc,\n                           tensorindex_list_acb>::type,\n                       tmpl::integral_list<std::int32_t, 1, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_221, symm_121, tensorindex_list_abc,\n                           tensorindex_list_acb>::type,\n                       tmpl::integral_list<std::int32_t, 2, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_111, symm_321, tensorindex_list_abc,\n                           tensorindex_list_bac>::type,\n                       tmpl::integral_list<std::int32_t, 3, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_321, symm_111, tensorindex_list_abc,\n                           tensorindex_list_bac>::type,\n                       tmpl::integral_list<std::int32_t, 3, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_211, symm_321, tensorindex_list_abc,\n                           tensorindex_list_cba>::type,\n                       tmpl::integral_list<std::int32_t, 3, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           symm_321, symm_211, tensorindex_list_abc,\n                           tensorindex_list_cba>::type,\n                       tmpl::integral_list<std::int32_t, 3, 2, 1>>);\n}\n\nvoid test_high_rank() {\n  using tensorindex_list =\n      make_tensorindex_list<ti::a, ti::b, ti::c, ti::d, ti::e>;\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           Symmetry<2, 1, 1, 1, 1>, Symmetry<3, 2, 2, 1, 1>,\n                           tensorindex_list, tensorindex_list>::type,\n                       tmpl::integral_list<std::int32_t, 3, 2, 2, 1, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           Symmetry<3, 2, 2, 1, 1>, Symmetry<2, 1, 1, 1, 1>,\n                           tensorindex_list, tensorindex_list>::type,\n                       tmpl::integral_list<std::int32_t, 3, 2, 2, 1, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           Symmetry<1, 1, 2, 1, 1>, Symmetry<4, 3, 1, 2, 1>,\n                           tensorindex_list, tensorindex_list>::type,\n                       tmpl::integral_list<std::int32_t, 5, 4, 3, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           Symmetry<4, 3, 1, 2, 1>, Symmetry<1, 1, 2, 1, 1>,\n                           tensorindex_list, tensorindex_list>::type,\n                       tmpl::integral_list<std::int32_t, 5, 4, 3, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           Symmetry<1, 2, 2, 2, 1>, Symmetry<1, 2, 1, 1, 1>,\n                           tensorindex_list, tensorindex_list>::type,\n                       tmpl::integral_list<std::int32_t, 1, 3, 2, 2, 1>>);\n\n  CHECK(std::is_same_v<typename tenex::detail::AddSubSymmetry<\n                           Symmetry<1, 2, 1, 1, 1>, Symmetry<1, 2, 2, 2, 1>,\n                           tensorindex_list, tensorindex_list>::type,\n                       tmpl::integral_list<std::int32_t, 1, 3, 2, 2, 1>>);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.AddSubSymmetry\",\n                  \"[DataStructures][Unit]\") {\n  test_impl_consistency();\n  test_rank0();\n  test_rank1();\n  test_rank2();\n  test_rank3();\n  test_high_rank();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_AddSubtract.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <complex>\n#include <cstddef>\n#include <random>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n// Checks that the number of ops in the expressions match what is expected\nvoid test_tensor_ops_properties() {\n  const Scalar<double> G{};\n  const double H = 0.0;\n  const tnsr::ii<double, 3> R{};\n  const tnsr::ij<double, 3> S{};\n  const tnsr::aa<double, 3> T{};\n\n  const auto scalar_expression = H - G() - H;\n  const auto R_plus_S = R(ti::i, ti::j) + S(ti::i, ti::j);\n  const auto R_minus_T = R(ti::i, ti::j) - T(ti::j, ti::i);\n  const auto large_expression = R(ti::i, ti::j) + S(ti::i, ti::j) -\n                                T(ti::j, ti::i) + S(ti::j, ti::i) +\n                                S(ti::i, ti::j) - R(ti::j, ti::i);\n\n  CHECK(scalar_expression.num_ops_subtree == 2);\n  CHECK(R_plus_S.num_ops_subtree == 1);\n  CHECK(R_minus_T.num_ops_subtree == 1);\n  CHECK(large_expression.num_ops_subtree == 5);\n}\n\n// \\brief Test the sum and difference of a `double` and tensor expression is\n// correctly evaluated\n//\n// \\details\n// The cases tested are:\n// - \\f$L = R + S\\f$\n// - \\f$L = R - S\\f$\n// - \\f$L = G^{i}{}_{i} + R\\f$\n// - \\f$L = G^{i}{}_{i} - R\\f$\n// - \\f$L = R + S + T\\f$\n// - \\f$L = R - G^{i}{}_{i} + T\\f$\n//\n// where \\f$R\\f$ and \\f$T\\f$ are `double`s and \\f$S\\f$, \\f$G\\f$, and \\f$L\\f$\n// are Tensors with data type `double` or DataVector.\n//\n// \\tparam DataType the type of data being stored in the tensor expression\n// operand of the sums and differences\ntemplate <typename Generator, typename DataType>\nvoid test_addsub_double(const gsl::not_null<Generator*> generator,\n                        const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto S = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n  const auto G = make_with_random_values<tnsr::Ij<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  DataType G_trace = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    G_trace += G.get(i, i);\n  }\n\n  // \\f$L = R + S\\f$\n  const Tensor<DataType> R_plus_S = tenex::evaluate(5.6 + S());\n  // \\f$L = R - S\\f$\n  const Tensor<DataType> R_minus_S = tenex::evaluate(1.1 - S());\n  // \\f$L = G^{i}{}_{i} + R\\f$\n  const Tensor<DataType> G_plus_R = tenex::evaluate(G(ti::I, ti::i) + 8.2);\n  // \\f$L = G^{i}{}_{i} - R\\f$\n  const Tensor<DataType> G_minus_R = tenex::evaluate(G(ti::I, ti::i) - 3.5);\n  // \\f$L = R + S + T\\f$\n  const Tensor<DataType> R_plus_S_plus_T = tenex::evaluate(0.7 + S() + 9.8);\n  // \\f$L = R - G^{i}{}_{i} + T\\f$\n  const Tensor<DataType> R_minus_G_plus_T =\n      tenex::evaluate(5.9 - G(ti::I, ti::i) + 4.7);\n\n  CHECK(R_plus_S.get() == 5.6 + S.get());\n  CHECK(R_minus_S.get() == 1.1 - S.get());\n  CHECK_ITERABLE_APPROX(G_plus_R.get(), G_trace + 8.2);\n  CHECK_ITERABLE_APPROX(G_minus_R.get(), G_trace - 3.5);\n  CHECK(R_plus_S_plus_T.get() == 0.7 + S.get() + 9.8);\n  CHECK_ITERABLE_APPROX(R_minus_G_plus_T.get(), 5.9 - G_trace + 4.7);\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_addsub_tensor(const gsl::not_null<Generator*> generator,\n                        const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  // Test scalars\n  const auto scalar_1 = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n  const auto scalar_2 = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n\n  Tensor<DataType> lhs_scalar = tenex::evaluate(scalar_1() + scalar_2());\n  CHECK(lhs_scalar.get() == get(scalar_1) + get(scalar_2));\n\n  // Test rank 2\n  const auto All = make_with_random_values<tnsr::aa<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const auto Hll = make_with_random_values<tnsr::ab<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  // [use_tensor_index]\n  const tnsr::ab<DataType, 3, Frame::Grid> Gll =\n      tenex::evaluate<ti::a, ti::b>(All(ti::a, ti::b) + Hll(ti::a, ti::b));\n  const tnsr::ab<DataType, 3, Frame::Grid> Gll2 =\n      tenex::evaluate<ti::a, ti::b>(All(ti::a, ti::b) + Hll(ti::b, ti::a));\n  const tnsr::ab<DataType, 3, Frame::Grid> Gll3 =\n      tenex::evaluate<ti::a, ti::b>(All(ti::a, ti::b) + Hll(ti::b, ti::a) +\n                                    All(ti::b, ti::a) - Hll(ti::b, ti::a));\n\n  // [use_tensor_index]\n  for (int i = 0; i < 4; ++i) {\n    for (int j = 0; j < 4; ++j) {\n      CHECK(Gll.get(i, j) == All.get(i, j) + Hll.get(i, j));\n      CHECK(Gll2.get(i, j) == All.get(i, j) + Hll.get(j, i));\n      CHECK_ITERABLE_APPROX(Gll3.get(i, j), 2.0 * All.get(i, j));\n    }\n  }\n\n  // Test rank 3\n  const auto Alll = make_with_random_values<\n      Tensor<DataType, Symmetry<1, 1, 2>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n  const auto Hlll =\n      make_with_random_values<tnsr::abc<DataType, 3, Frame::Grid>>(\n          generator, distribution, used_for_size);\n  const auto Rlll =\n      make_with_random_values<tnsr::abb<DataType, 3, Frame::Grid>>(\n          generator, distribution, used_for_size);\n  const auto Slll = make_with_random_values<\n      Tensor<DataType, Symmetry<1, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const tnsr::abc<DataType, 3, Frame::Grid> Glll =\n      tenex::evaluate<ti::a, ti::b, ti::c>(Alll(ti::a, ti::b, ti::c) +\n                                           Hlll(ti::a, ti::b, ti::c));\n  const tnsr::abc<DataType, 3, Frame::Grid> Glll2 =\n      tenex::evaluate<ti::a, ti::b, ti::c>(Alll(ti::a, ti::b, ti::c) +\n                                           Hlll(ti::b, ti::a, ti::c));\n  const tnsr::abc<DataType, 3, Frame::Grid> Glll3 =\n      tenex::evaluate<ti::a, ti::b, ti::c>(\n          Alll(ti::a, ti::b, ti::c) + Hlll(ti::b, ti::a, ti::c) +\n          Alll(ti::b, ti::a, ti::c) - Hlll(ti::b, ti::a, ti::c));\n  // testing LHS symmetry is nonsymmetric when RHS operands do not have\n  // symmetries in common\n  const tnsr::abc<DataType, 3, Frame::Grid> Glll4 =\n      tenex::evaluate<ti::a, ti::b, ti::c>(Alll(ti::b, ti::c, ti::a) +\n                                           Rlll(ti::c, ti::a, ti::b));\n  // testing LHS symmetry preserves shared RHS symmetry when RHS operands have\n  // symmetries in common\n  const tnsr::abb<DataType, 3, Frame::Grid> Glll5 =\n      tenex::evaluate<ti::a, ti::b, ti::c>(Alll(ti::b, ti::c, ti::a) -\n                                           Rlll(ti::a, ti::c, ti::b));\n\n  for (int i = 0; i < 4; ++i) {\n    for (int j = 0; j < 4; ++j) {\n      for (int k = 0; k < 4; ++k) {\n        CHECK(Glll.get(i, j, k) == Alll.get(i, j, k) + Hlll.get(i, j, k));\n        CHECK(Glll2.get(i, j, k) == Alll.get(i, j, k) + Hlll.get(j, i, k));\n        CHECK_ITERABLE_APPROX(Glll3.get(i, j, k), 2.0 * Alll.get(i, j, k));\n        CHECK(Glll4.get(i, j, k) == Alll.get(j, k, i) + Rlll.get(k, i, j));\n        CHECK(Glll5.get(i, j, k) == Alll.get(j, k, i) - Rlll.get(i, k, j));\n      }\n    }\n  }\n\n  // testing with expressions having spatial indices for spacetime indices\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      Glll6 = tenex::evaluate<ti::a, ti::j, ti::k>(Rlll(ti::a, ti::j, ti::k) +\n                                                   Slll(ti::a, ti::j, ti::k));\n  tnsr::iab<DataType, 3, Frame::Grid> Glll7{};\n  tenex::evaluate<ti::j, ti::a, ti::k>(\n      make_not_null(&Glll7),\n      Slll(ti::j, ti::k, ti::a) - Rlll(ti::k, ti::a, ti::j));\n\n  for (int a = 0; a < 4; ++a) {\n    for (int j = 0; j < 3; ++j) {\n      for (int k = 0; k < 3; ++k) {\n        CHECK(Glll6.get(a, j, k) ==\n              Rlll.get(a, j + 1, k + 1) + Slll.get(a, j, k + 1));\n        CHECK(Glll7.get(j, a, k + 1) ==\n              Slll.get(j + 1, k, a) - Rlll.get(k + 1, a, j + 1));\n      }\n    }\n  }\n\n  // testing with operands having time indices for spacetime indices\n  const tnsr::ab<DataType, 3, Frame::Grid> Gll7 = tenex::evaluate<ti::c, ti::b>(\n      Alll(ti::c, ti::t, ti::b) + Hlll(ti::b, ti::c, ti::t));\n\n  for (int c = 0; c < 4; ++c) {\n    for (int b = 0; b < 4; ++b) {\n      CHECK(Gll7.get(c, b) == Alll.get(c, 0, b) + Hlll.get(b, c, 0));\n    }\n  }\n\n  const tnsr::aa<DataType, 3, Frame::Grid> Gll8 = tenex::evaluate<ti::d, ti::c>(\n      Alll(ti::c, ti::d, ti::t) - All(ti::d, ti::c));\n\n  for (int d = 0; d < 4; ++d) {\n    for (int c = 0; c < 4; ++c) {\n      CHECK(Gll8.get(d, c) == Alll.get(c, d, 0) - All.get(d, c));\n    }\n  }\n\n  const tnsr::aa<DataType, 3, Frame::Grid> Gll9 = tenex::evaluate<ti::a, ti::b>(\n      All(ti::b, ti::a) + Alll(ti::b, ti::a, ti::t));\n\n  for (int a = 0; a < 4; ++a) {\n    for (int b = 0; b < 4; ++b) {\n      CHECK(Gll9.get(a, b) == All.get(b, a) + Alll.get(b, a, 0));\n    }\n  }\n\n  // Assign a placeholder to the LHS tensor's components before it is computed\n  // so that when test expressions below only compute time components, we can\n  // check that LHS spatial components haven't changed\n  const auto spatial_component_placeholder_value =\n      TestHelpers::tenex::component_placeholder_value<DataType>::value;\n\n  auto Gll10 = make_with_value<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>>(\n      used_for_size, spatial_component_placeholder_value);\n  tenex::evaluate<ti::t, ti::d, ti::b, ti::T>(\n      make_not_null(&Gll10), All(ti::b, ti::d) - Hll(ti::b, ti::d));\n\n  for (int d = 0; d < 4; ++d) {\n    for (int b = 0; b < 4; ++b) {\n      CHECK(Gll10.get(0, d, b, 0) == All.get(b, d) - Hll.get(b, d));\n      for (size_t i = 0; i < 3; ++i) {\n        for (size_t j = 0; j < 3; ++j) {\n          CHECK(Gll10.get(i + 1, d, b, j + 1) ==\n                spatial_component_placeholder_value);\n        }\n      }\n    }\n  }\n\n  auto Gll11 = make_with_value<\n      Tensor<DataType, Symmetry<2, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      used_for_size, spatial_component_placeholder_value);\n  tenex::evaluate<ti::a, ti::c, ti::t>(\n      make_not_null(&Gll11), Rlll(ti::t, ti::c, ti::a) + All(ti::a, ti::c));\n\n  for (int a = 0; a < 4; ++a) {\n    for (int c = 0; c < 4; ++c) {\n      CHECK(Gll11.get(a, c, 0) == Rlll.get(0, c, a) + All.get(a, c));\n      for (size_t i = 0; i < 3; ++i) {\n        CHECK(Gll11.get(a, c, i + 1) == spatial_component_placeholder_value);\n      }\n    }\n  }\n\n  auto Gll12 = make_with_value<tnsr::abc<DataType, 3, Frame::Grid>>(\n      used_for_size, spatial_component_placeholder_value);\n  tenex::evaluate<ti::g, ti::t, ti::h>(\n      make_not_null(&Gll12), Hll(ti::h, ti::g) - Alll(ti::g, ti::t, ti::h));\n\n  for (int g = 0; g < 4; ++g) {\n    for (int h = 0; h < 4; ++h) {\n      CHECK(Gll12.get(g, 0, h) == Hll.get(h, g) - Alll.get(g, 0, h));\n      for (size_t i = 0; i < 3; ++i) {\n        CHECK(Gll12.get(g, i + 1, h) == spatial_component_placeholder_value);\n      }\n    }\n  }\n\n  auto Gll13 = make_with_value<tnsr::ab<DataType, 3, Frame::Grid>>(\n      used_for_size, spatial_component_placeholder_value);\n  tenex::evaluate<ti::t, ti::f>(make_not_null(&Gll13),\n                                Rlll(ti::t, ti::t, ti::f) - Hll(ti::f, ti::t));\n\n  for (int f = 0; f < 4; ++f) {\n    CHECK(Gll13.get(0, f) == Rlll.get(0, 0, f) - Hll.get(f, 0));\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(Gll13.get(i + 1, f) == spatial_component_placeholder_value);\n    }\n  }\n\n  const auto T = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n\n  auto Gll14 = make_with_value<tnsr::Ab<DataType, 3, Frame::Grid>>(\n      used_for_size, spatial_component_placeholder_value);\n  tenex::evaluate<ti::T, ti::t>(\n      make_not_null(&Gll14),\n      Hll(ti::t, ti::t) - T() - Rlll(ti::t, ti::t, ti::t) + 9.1);\n\n  CHECK(Gll14.get(0, 0) == Hll.get(0, 0) - get(T) - Rlll.get(0, 0, 0) + 9.1);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      CHECK(Gll14.get(i + 1, j + 1) == spatial_component_placeholder_value);\n    }\n  }\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_addsub(const gsl::not_null<Generator*> generator,\n                 const DataType& used_for_size) {\n  test_addsub_double(generator, used_for_size);\n  test_addsub_tensor(generator, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.AddSubtract\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  test_tensor_ops_properties();\n  test_addsub(make_not_null(&generator),\n              std::numeric_limits<double>::signaling_NaN());\n  test_addsub(\n      make_not_null(&generator),\n      std::complex<double>(std::numeric_limits<double>::signaling_NaN(),\n                           std::numeric_limits<double>::signaling_NaN()));\n  test_addsub(make_not_null(&generator),\n              DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n  test_addsub(\n      make_not_null(&generator),\n      ComplexDataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_Contract.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <climits>\n#include <complex>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\n// Checks that the number of ops in the expressions match what is expected\nvoid test_tensor_ops_properties() {\n  const tnsr::Ij<double, 3> R{};\n  const Tensor<double, Symmetry<4, 3, 2, 1>,\n               index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                          SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                          SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                          SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>\n      S{};\n\n  // Expected: (TotalDim - 1) adds (3 - 1) adds = 2 total ops\n  const auto R_contracted = R(ti::I, ti::i);\n  // Expected: (TotalDim - 1) adds = (4 - 1) adds = 3 total ops\n  const auto S_contract_one_pair = S(ti::A, ti::B, ti::c, ti::a);\n  // Expected:\n  //   (SpatialDim * TotalDim - 1) adds = (3 * 4 - 1) adds = 11 total ops\n  const auto S_contract_both_pairs = S(ti::K, ti::A, ti::a, ti::k);\n\n  CHECK(R_contracted.num_ops_subtree == 2);\n  CHECK(S_contract_one_pair.num_ops_subtree == 3);\n  CHECK(S_contract_both_pairs.num_ops_subtree == 11);\n}\n\n// Contractions are performed by summing over multi-indices in an order that is\n// implementation defined. What is considered the \"next lowest\" and\n// \"next highest\" multi-indices should be opposites of each other. This test\n// checks this, as well as checking that the \"lowest\" and \"highest\"\n// multi-indices being summed are correctly determined.\nvoid test_contraction_summation_consistency() {\n  const tnsr::II<double, 3, Frame::Inertial> R{};\n  const tnsr::iab<double, 3, Frame::Inertial> S{};\n\n  // L is a `TensorContract`, not a `Tensor`\n  const auto L = R(ti::J, ti::I) * S(ti::i, ti::a, ti::j);\n  // multi-index for L_2\n  const std::array<size_t, 1> L_multi_index = {2};\n\n  const std::array<size_t, 5> lowest_multi_index =\n      L.get_lowest_multi_index_to_sum(L_multi_index);\n  const std::array<size_t, 5> expected_lowest_multi_index = {0, 0, 0, 2, 1};\n  CHECK(lowest_multi_index == expected_lowest_multi_index);\n\n  const std::array<size_t, 5> highest_multi_index =\n      L.get_highest_multi_index_to_sum(L_multi_index);\n  const std::array<size_t, 5> expected_highest_multi_index = {2, 2, 2, 2, 3};\n  CHECK(highest_multi_index == expected_highest_multi_index);\n\n  std::array<size_t, 5> current_multi_index = expected_lowest_multi_index;\n  while (current_multi_index != expected_highest_multi_index) {\n    const auto next_lowest_multi_index =\n        L.get_next_lowest_multi_index_to_sum(current_multi_index);\n    CHECK(L.get_next_highest_multi_index_to_sum(next_lowest_multi_index) ==\n          current_multi_index);\n    current_multi_index = next_lowest_multi_index;\n  }\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_contractions_rank2(const gsl::not_null<Generator*> generator,\n                             const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  // Contract (upper, lower) tensor\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const auto Rul =\n      make_with_random_values<tnsr::Ij<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n\n  const Tensor<DataType> RIi_contracted = tenex::evaluate(Rul(ti::I, ti::i));\n\n  DataType expected_RIi_sum = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    expected_RIi_sum += Rul.get(i, i);\n  }\n  CHECK_ITERABLE_APPROX(RIi_contracted.get(), expected_RIi_sum);\n\n  // Contract (lower, upper) tensor\n  const auto Rlu = make_with_random_values<tnsr::aB<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType> RgG_contracted = tenex::evaluate(Rlu(ti::g, ti::G));\n\n  DataType expected_RgG_sum = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t g = 0; g < 4; g++) {\n    expected_RgG_sum += Rlu.get(g, g);\n  }\n  CHECK_ITERABLE_APPROX(RgG_contracted.get(), expected_RgG_sum);\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_contractions_rank3(const gsl::not_null<Generator*> generator,\n                             const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  // Contract first and second indices of (lower, upper, lower) tensor\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const auto Rlul = make_with_random_values<\n      Tensor<DataType, Symmetry<3, 2, 1>,\n             index_list<SpatialIndex<2, UpLo::Lo, Frame::Grid>,\n                        SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      RiIj_contracted = tenex::evaluate<ti::j>(Rlul(ti::i, ti::I, ti::j));\n\n  for (size_t j = 0; j < 3; j++) {\n    DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t i = 0; i < 2; i++) {\n      expected_sum += Rlul.get(i, i, j);\n    }\n    CHECK_ITERABLE_APPROX(RiIj_contracted.get(j), expected_sum);\n  }\n\n  // Contract first and third indices of (upper, upper, lower) tensor\n  const auto Ruul =\n      make_with_random_values<tnsr::IJk<DataType, 3, Frame::Grid>>(\n          generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<3, UpLo::Up, Frame::Grid>>>\n      RJLj_contracted = tenex::evaluate<ti::L>(Ruul(ti::J, ti::L, ti::j));\n\n  for (size_t l = 0; l < 3; l++) {\n    DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t j = 0; j < 3; j++) {\n      expected_sum += Ruul.get(j, l, j);\n    }\n    CHECK_ITERABLE_APPROX(RJLj_contracted.get(l), expected_sum);\n  }\n\n  // Contract second and third indices of (upper, lower, upper) tensor\n  const auto Rulu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1, 2>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>\n      RBfF_contracted = tenex::evaluate<ti::B>(Rulu(ti::B, ti::f, ti::F));\n\n  for (size_t b = 0; b < 4; b++) {\n    DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t f = 0; f < 4; f++) {\n      expected_sum += Rulu.get(b, f, f);\n    }\n    CHECK_ITERABLE_APPROX(RBfF_contracted.get(b), expected_sum);\n  }\n\n  // Contract first and third indices of (lower, lower, upper) tensor with mixed\n  // TensorIndexTypes\n  const auto Rllu = make_with_random_values<\n      Tensor<DataType, Symmetry<3, 2, 1>,\n             index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Up, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      RiaI_contracted = tenex::evaluate<ti::a>(Rllu(ti::i, ti::a, ti::I));\n\n  for (size_t a = 0; a < 4; a++) {\n    DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t i = 0; i < 3; i++) {\n      expected_sum += Rllu.get(i, a, i);\n    }\n    CHECK_ITERABLE_APPROX(RiaI_contracted.get(a), expected_sum);\n  }\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_contractions_rank4(const gsl::not_null<Generator*> generator,\n                             const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  // Contract first and second indices of (lower, upper, upper, lower) tensor to\n  // rank 2 tensor\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const auto Rluul = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n                        SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpatialIndex<2, UpLo::Lo, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                          SpatialIndex<2, UpLo::Lo, Frame::Inertial>>>\n      RiIKj_contracted =\n          tenex::evaluate<ti::K, ti::j>(Rluul(ti::i, ti::I, ti::K, ti::j));\n\n  for (size_t k = 0; k < 3; k++) {\n    for (size_t j = 0; j < 2; j++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t i = 0; i < 2; i++) {\n        expected_sum += Rluul.get(i, i, k, j);\n      }\n      CHECK_ITERABLE_APPROX(RiIKj_contracted.get(k, j), expected_sum);\n    }\n  }\n\n  // Contract first and third indices of (upper, upper, lower, lower) tensor to\n  // rank 2 tensor\n  const auto Ruull = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                          SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      RABac_contracted =\n          tenex::evaluate<ti::B, ti::c>(Ruull(ti::A, ti::B, ti::a, ti::c));\n\n  for (size_t b = 0; b < 3; b++) {\n    for (size_t c = 0; c < 4; c++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t a = 0; a < 4; a++) {\n        expected_sum += Ruull.get(a, b, a, c);\n      }\n      CHECK_ITERABLE_APPROX(RABac_contracted.get(b, c), expected_sum);\n    }\n  }\n\n  // Contract first and fourth indices of (upper, upper, upper, lower) tensor to\n  // rank 2 tensor\n  const auto Ruuul = make_with_random_values<\n      Tensor<DataType, Symmetry<3, 2, 3, 1>,\n             index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<2, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                          SpatialIndex<2, UpLo::Up, Frame::Grid>>>\n      RLJIl_contracted =\n          tenex::evaluate<ti::J, ti::I>(Ruuul(ti::L, ti::J, ti::I, ti::l));\n\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t i = 0; i < 2; i++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t l = 0; l < 2; l++) {\n        expected_sum += Ruuul.get(l, j, i, l);\n      }\n      CHECK_ITERABLE_APPROX(RLJIl_contracted.get(j, i), expected_sum);\n    }\n  }\n\n  // Contract second and third indices of (upper, upper, lower, upper) tensor to\n  // rank 2 tensor\n  const auto Ruulu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 2, 1, 2>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<1, 1>,\n               index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                          SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>\n      REDdA_contracted =\n          tenex::evaluate<ti::E, ti::A>(Ruulu(ti::E, ti::D, ti::d, ti::A));\n\n  for (size_t e = 0; e < 4; e++) {\n    for (size_t a = 0; a < 4; a++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t d = 0; d < 4; d++) {\n        expected_sum += Ruulu.get(e, d, d, a);\n      }\n      CHECK_ITERABLE_APPROX(REDdA_contracted.get(e, a), expected_sum);\n    }\n  }\n\n  // Contract second and fourth indices of (lower, upper, lower, lower) tensor\n  // to rank 2 tensor\n  const auto Rlull = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n                        SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpatialIndex<2, UpLo::Lo, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>\n      RkJij_contracted =\n          tenex::evaluate<ti::k, ti::i>(Rlull(ti::k, ti::J, ti::i, ti::j));\n\n  for (size_t k = 0; k < 2; k++) {\n    for (size_t i = 0; i < 3; i++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t j = 0; j < 2; j++) {\n        expected_sum += Rlull.get(k, j, i, j);\n      }\n      CHECK_ITERABLE_APPROX(RkJij_contracted.get(k, i), expected_sum);\n    }\n  }\n\n  // Contract third and fourth indices of (upper, lower, lower, upper) tensor to\n  // rank 2 tensor\n  const auto Rullu = make_with_random_values<\n      Tensor<DataType, Symmetry<3, 2, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                          SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>\n      RFcgG_contracted =\n          tenex::evaluate<ti::F, ti::c>(Rullu(ti::F, ti::c, ti::g, ti::G));\n\n  for (size_t f = 0; f < 4; f++) {\n    for (size_t c = 0; c < 3; c++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t g = 0; g < 3; g++) {\n        expected_sum += Rullu.get(f, c, g, g);\n      }\n      CHECK_ITERABLE_APPROX(RFcgG_contracted.get(f, c), expected_sum);\n    }\n  }\n\n  // Contract first and second indices of (upper, lower, upper, upper) tensor to\n  // rank 2 tensor and reorder indices\n  const auto Ruluu = make_with_random_values<\n      Tensor<DataType, Symmetry<3, 2, 3, 1>,\n             index_list<SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<2, UpLo::Up, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                          SpatialIndex<3, UpLo::Up, Frame::Grid>>>\n      RKkIJ_contracted_to_JI =\n          tenex::evaluate<ti::J, ti::I>(Ruluu(ti::K, ti::k, ti::I, ti::J));\n\n  for (size_t j = 0; j < 2; j++) {\n    for (size_t i = 0; i < 3; i++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t k = 0; k < 3; k++) {\n        expected_sum += Ruluu.get(k, k, i, j);\n      }\n      CHECK_ITERABLE_APPROX(RKkIJ_contracted_to_JI.get(j, i), expected_sum);\n    }\n  }\n\n  // Contract first and third indices of (lower, upper, upper, upper) tensor to\n  // rank 2 tensor and reorder indices\n  const auto Rluuu = make_with_random_values<\n      Tensor<DataType, Symmetry<3, 2, 1, 2>,\n             index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<2, UpLo::Up, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<1, 1>,\n               index_list<SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                          SpacetimeIndex<2, UpLo::Up, Frame::Grid>>>\n      RbCBE_contracted_to_EC =\n          tenex::evaluate<ti::E, ti::C>(Rluuu(ti::b, ti::C, ti::B, ti::E));\n\n  for (size_t e = 0; e < 3; e++) {\n    for (size_t c = 0; c < 3; c++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t b = 0; b < 3; b++) {\n        expected_sum += Rluuu.get(b, c, b, e);\n      }\n      CHECK_ITERABLE_APPROX(RbCBE_contracted_to_EC.get(e, c), expected_sum);\n    }\n  }\n\n  // Contract first and fourth indices of (upper, lower, lower, lower) tensor to\n  // rank 2 tensor and reorder indices\n  const auto Rulll = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1, 1, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<1, 1>,\n               index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                          SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      RAdba_contracted_to_bd =\n          tenex::evaluate<ti::b, ti::d>(Rulll(ti::A, ti::d, ti::b, ti::a));\n\n  for (size_t b = 0; b < 4; b++) {\n    for (size_t d = 0; d < 4; d++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t a = 0; a < 4; a++) {\n        expected_sum += Rulll.get(a, d, b, a);\n      }\n      CHECK_ITERABLE_APPROX(RAdba_contracted_to_bd.get(b, d), expected_sum);\n    }\n  }\n\n  // Contract second and third indices of (lower, lower, upper, lower) tensor to\n  // rank 2 tensor and reorder indices\n  const auto Rllul = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpatialIndex<2, UpLo::Lo, Frame::Grid>,\n                        SpatialIndex<2, UpLo::Lo, Frame::Grid>,\n                        SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                          SpatialIndex<2, UpLo::Lo, Frame::Grid>>>\n      RljJi_contracted_to_il =\n          tenex::evaluate<ti::i, ti::l>(Rllul(ti::l, ti::j, ti::J, ti::i));\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t l = 0; l < 2; l++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t j = 0; j < 2; j++) {\n        expected_sum += Rllul.get(l, j, j, i);\n      }\n      CHECK_ITERABLE_APPROX(RljJi_contracted_to_il.get(i, l), expected_sum);\n    }\n  }\n\n  // Contract second and fourth indices of (lower, lower, upper, upper) tensor\n  // to rank 2 tensor and reorder indices\n  const auto Rlluu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 2, 1, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                          SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>\n      RagDG_contracted_to_Da =\n          tenex::evaluate<ti::D, ti::a>(Rlluu(ti::a, ti::g, ti::D, ti::G));\n\n  for (size_t d = 0; d < 4; d++) {\n    for (size_t a = 0; a < 4; a++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t g = 0; g < 4; g++) {\n        expected_sum += Rlluu.get(a, g, d, g);\n      }\n      CHECK_ITERABLE_APPROX(RagDG_contracted_to_Da.get(d, a), expected_sum);\n    }\n  }\n\n  // Contract third and fourth indices of (lower, upper, lower, upper) tensor to\n  // rank 2 tensor and reorder indices\n  const auto Rlulu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1, 2, 1>,\n             index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpatialIndex<3, UpLo::Up, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>\n      RlJiI_contracted_to_Jl =\n          tenex::evaluate<ti::J, ti::l>(Rlulu(ti::l, ti::J, ti::i, ti::I));\n\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t l = 0; l < 3; l++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t i = 0; i < 3; i++) {\n        expected_sum += Rlulu.get(l, j, i, i);\n      }\n      CHECK_ITERABLE_APPROX(RlJiI_contracted_to_Jl.get(j, l), expected_sum);\n    }\n  }\n\n  // Contract first and second indices AND third and fourth indices to rank 0\n  // tensor\n  const auto Rulul = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<2, UpLo::Lo, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  const Tensor<DataType> RKkLl_contracted =\n      tenex::evaluate(Rulul(ti::K, ti::k, ti::L, ti::l));\n\n  DataType expected_RKkLl_sum = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t k = 0; k < 2; k++) {\n    for (size_t l = 0; l < 3; l++) {\n      expected_RKkLl_sum += Rulul.get(k, k, l, l);\n    }\n  }\n  CHECK_ITERABLE_APPROX(RKkLl_contracted.get(), expected_RKkLl_sum);\n\n  // Contract first and third indices and second and fourth indices to rank 0\n  // tensor\n  const Tensor<DataType> RcaCA_contracted =\n      tenex::evaluate(Rlluu(ti::c, ti::a, ti::C, ti::A));\n\n  DataType expected_RcaCA_sum = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t c = 0; c < 4; c++) {\n    for (size_t a = 0; a < 4; a++) {\n      expected_RcaCA_sum += Rlluu.get(c, a, c, a);\n    }\n  }\n  CHECK_ITERABLE_APPROX(RcaCA_contracted.get(), expected_RcaCA_sum);\n\n  // Contract first and fourth indices and second and third indices to rank 0\n  // tensor\n  const Tensor<DataType> RjIiJ_contracted =\n      tenex::evaluate(Rlulu(ti::j, ti::I, ti::i, ti::J));\n\n  DataType expected_RjIiJ_sum = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t i = 0; i < 3; i++) {\n      expected_RjIiJ_sum += Rlulu.get(j, i, i, j);\n    }\n  }\n  CHECK_ITERABLE_APPROX(RjIiJ_contracted.get(), expected_RjIiJ_sum);\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_spatial_spacetime_index(const gsl::not_null<Generator*> generator,\n                                  const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  // Contract (spatial, spacetime) tensor\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const auto R = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n  const Tensor<DataType> R_contracted = tenex::evaluate(R(ti::I, ti::i));\n\n  // Contract (spacetime, spatial) tensor\n  const auto S = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n  const Tensor<DataType> S_contracted = tenex::evaluate(S(ti::K, ti::k));\n\n  // Contract (spacetime, spacetime) tensor using generic spatial indices\n  const auto T = make_with_random_values<tnsr::aB<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const Tensor<DataType> T_contracted = tenex::evaluate(T(ti::j, ti::J));\n\n  DataType expected_R_sum = make_with_value<DataType>(used_for_size, 0.0);\n  DataType expected_S_sum = make_with_value<DataType>(used_for_size, 0.0);\n  DataType expected_T_sum = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    expected_R_sum += R.get(i, i + 1);\n    expected_S_sum += S.get(i + 1, i);\n    expected_T_sum += T.get(i + 1, i + 1);\n  }\n  CHECK_ITERABLE_APPROX(R_contracted.get(), expected_R_sum);\n  CHECK_ITERABLE_APPROX(S_contracted.get(), expected_S_sum);\n  CHECK_ITERABLE_APPROX(T_contracted.get(), expected_T_sum);\n\n  const auto G = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  // Contract one (spatial, spacetime) pair of indices of a tensor that also\n  // takes a generic spatial index for a single non-contracted spacetime index\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                          SpatialIndex<3, UpLo::Up, Frame::Grid>>>\n      G_contracted_1 =\n          tenex::evaluate<ti::i, ti::K>(G(ti::K, ti::j, ti::i, ti::J));\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t k = 0; k < 3; k++) {\n      DataType expected_G_sum_1 = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t j = 0; j < 3; j++) {\n        expected_G_sum_1 += G.get(k, j, i + 1, j + 1);\n      }\n      CHECK_ITERABLE_APPROX(G_contracted_1.get(i, k), expected_G_sum_1);\n    }\n  }\n\n  // Contract one (spacetime, spacetime) pair of indices using generic spatial\n  // indices and then one (spatial, spatial) pair of indices\n  const Tensor<DataType> G_contracted_2 =\n      tenex::evaluate(G(ti::I, ti::i, ti::j, ti::J));\n\n  DataType expected_G_sum_2 = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      expected_G_sum_2 += G.get(i, i, j + 1, j + 1);\n    }\n  }\n  CHECK_ITERABLE_APPROX(G_contracted_2.get(), expected_G_sum_2);\n\n  // Contract two (spatial, spacetime) pairs of indices\n  const Tensor<DataType> G_contracted_3 =\n      tenex::evaluate(G(ti::I, ti::j, ti::i, ti::J));\n\n  DataType expected_G_sum_3 = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      expected_G_sum_3 += G.get(i, j, i + 1, j + 1);\n    }\n  }\n  CHECK_ITERABLE_APPROX(G_contracted_3.get(), expected_G_sum_3);\n\n  const auto H = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  // Contract one (spacetime, spacetime) pair of indices using generic spacetime\n  // indices and then one (spacetime, spacetime) pair of indices using generic\n  // spatial indices\n  const Tensor<DataType> H_contracted_1 =\n      tenex::evaluate(H(ti::i, ti::I, ti::a, ti::A));\n\n  DataType expected_H_sum_1 = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t a = 0; a < 4; a++) {\n      expected_H_sum_1 += H.get(i + 1, i + 1, a, a);\n    }\n  }\n  CHECK_ITERABLE_APPROX(H_contracted_1.get(), expected_H_sum_1);\n\n  // Contract two (spacetime, spacetime) pair of indices using generic spatial\n  // indices\n  const Tensor<DataType> H_contracted_2 =\n      tenex::evaluate(H(ti::j, ti::I, ti::i, ti::J));\n\n  DataType expected_H_sum_2 = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t i = 0; i < 3; i++) {\n      expected_H_sum_2 += H.get(j + 1, i + 1, i + 1, j + 1);\n    }\n  }\n  CHECK_ITERABLE_APPROX(H_contracted_2.get(), expected_H_sum_2);\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_time_index(const gsl::not_null<Generator*> generator,\n                     const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto R = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n  // Contract RHS tensor with time index to a LHS tensor without a time index\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  // \\f$L_{b} = R^{at}{}_{ab}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>\n      R_contracted_1 = tenex::evaluate<ti::b>(R(ti::A, ti::T, ti::a, ti::b));\n\n  for (size_t b = 0; b < 4; b++) {\n    DataType expected_R_sum_1 = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t a = 0; a < 4; a++) {\n      expected_R_sum_1 += R.get(a, 0, a, b);\n    }\n    CHECK_ITERABLE_APPROX(R_contracted_1.get(b), expected_R_sum_1);\n  }\n\n  // Contract RHS tensor with upper and lower time index to a LHS tensor without\n  // time indices\n  // \\f$L = R^{at}{}_{at}\\f$\n  //\n  // Makes sure that `TensorContract` does not get confused by the presence of\n  // an upper and lower time index in the RHS tensor, which is different than\n  // the presence of an upper and lower generic index\n  const Tensor<DataType> R_contracted_2 =\n      tenex::evaluate(R(ti::A, ti::T, ti::a, ti::t));\n  DataType expected_R_sum_2 = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t a = 0; a < 4; a++) {\n    expected_R_sum_2 += R.get(a, 0, a, 0);\n  }\n  CHECK_ITERABLE_APPROX(R_contracted_2.get(), expected_R_sum_2);\n\n  const auto S = make_with_random_values<\n      Tensor<DataType, Symmetry<3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n  // Assign a placeholder to the LHS tensor's components before it is computed\n  // so that when test expressions below only compute time components, we can\n  // check that LHS spatial components haven't changed\n  const auto spatial_component_placeholder_value =\n      TestHelpers::tenex::component_placeholder_value<DataType>::value;\n  auto S_contracted = make_with_value<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>>(\n      used_for_size, spatial_component_placeholder_value);\n\n  // Contract a RHS tensor without time indices to a LHS tensor with time\n  // indices\n  // \\f$L_{tt}{}^{bt} = R^{ba}{}_{a}\\f$\n  //\n  // Makes sure that `TensorContract` does not get confused by the presence of\n  // an upper and lower time index in the LHS tensor, which is different than\n  // the presence of an upper and lower generic index. Also makes sure that\n  // a contraction can be evaluated to a LHS tensor of higher rank than the\n  // rank that results from contracting the RHS\n  ::tenex::evaluate<ti::t, ti::t, ti::B, ti::T>(make_not_null(&S_contracted),\n                                                S(ti::B, ti::A, ti::a));\n\n  for (size_t b = 0; b < 4; b++) {\n    DataType expected_S_sum = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t a = 0; a < 4; a++) {\n      expected_S_sum += S.get(b, a, a);\n    }\n    CHECK_ITERABLE_APPROX(S_contracted.get(0, 0, b, 0), expected_S_sum);\n\n    for (size_t i = 0; i < 3; i++) {\n      for (size_t j = 0; j < 3; j++) {\n        for (size_t k = 0; k < 3; k++) {\n          CHECK(S_contracted.get(i + 1, j + 1, b, k + 1) ==\n                spatial_component_placeholder_value);\n        }\n      }\n    }\n  }\n\n  const auto T = make_with_random_values<\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n  auto T_contracted = make_with_value<\n      Tensor<DataType, Symmetry<3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      used_for_size, spatial_component_placeholder_value);\n\n  // Contract a RHS tensor with time indices to a LHS tensor with time indices\n  // \\f$L^{tb}{}_{t} = R_{at}{}^{ab}\\f$\n  ::tenex::evaluate<ti::T, ti::B, ti::t>(make_not_null(&T_contracted),\n                                         T(ti::a, ti::t, ti::A, ti::B));\n\n  for (size_t b = 0; b < 4; b++) {\n    DataType expected_T_sum = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t a = 0; a < 4; a++) {\n      expected_T_sum += T.get(a, 0, a, b);\n    }\n    CHECK_ITERABLE_APPROX(T_contracted.get(0, b, 0), expected_T_sum);\n\n    for (size_t i = 0; i < 3; i++) {\n      for (size_t j = 0; j < 3; j++) {\n        CHECK(T_contracted.get(i + 1, b, j + 1) ==\n              spatial_component_placeholder_value);\n      }\n    }\n  }\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_contractions(const gsl::not_null<Generator*> generator,\n                       const DataType& used_for_size) {\n  test_contractions_rank2(generator, used_for_size);\n  test_contractions_rank3(generator, used_for_size);\n  test_contractions_rank4(generator, used_for_size);\n  test_spatial_spacetime_index(generator, used_for_size);\n  test_time_index(generator, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.Contract\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  test_tensor_ops_properties();\n  test_contraction_summation_consistency();\n  test_contractions(make_not_null(&generator),\n                    std::numeric_limits<double>::signaling_NaN());\n  test_contractions(\n      make_not_null(&generator),\n      std::complex<double>(std::numeric_limits<double>::signaling_NaN(),\n                           std::numeric_limits<double>::signaling_NaN()));\n  test_contractions(\n      make_not_null(&generator),\n      DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n  test_contractions(\n      make_not_null(&generator),\n      ComplexDataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_DataTypeSupport.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// \\file\n// Tests data-type specific properties and configuration within\n// `TensorExpression`s\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Helpers/DataStructures/CustomStaticSizeVector.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct ArbitraryType {};\n\ntemplate <typename ValueType>\nusing number_expression = tenex::NumberAsExpression<ValueType>;\ntemplate <typename ValueType>\nusing tensor_expression =\n    tenex::TensorAsExpression<Scalar<ValueType>, tmpl::list<>>;\n\nconstexpr size_t custom_vector_static_size =\n    CustomComplexStaticSizeVector::static_size;\n\ntemplate <bool Expected, typename DataTypes>\nvoid test_is_supported_number_datatype() {\n  tmpl::for_each<DataTypes>([](auto datatype_value) {\n    using datatype = tmpl::type_from<decltype(datatype_value)>;\n    // Tested at compile time so other tests can use this\n    static_assert(\n        tenex::detail::is_supported_number_datatype_v<datatype> == Expected,\n        \"Test for tenex::detail::test_is_supported_number_datatype failed.\");\n  });\n}\n\ntemplate <bool Expected, typename DataTypes>\nvoid test_is_supported_tensor_datatype() {\n  tmpl::for_each<DataTypes>([](auto datatype_value) {\n    using datatype = tmpl::type_from<decltype(datatype_value)>;\n    // Tested at compile time so other tests can use this\n    static_assert(\n        tenex::detail::is_supported_tensor_datatype_v<datatype> == Expected,\n        \"Test for tenex::detail::test_is_supported_tensor_datatype failed.\");\n  });\n}\n\ntemplate <typename T, typename Expected>\nvoid test_upcast_if_derived_vector_type() {\n  // Tested at compile time so other tests can use this\n  static_assert(\n      std::is_same_v<\n          typename tenex::detail::upcast_if_derived_vector_type<T>::type,\n          Expected>,\n      \"Test for tenex::detail::upcast_if_derived_vector_type failed.\");\n}\n\ntemplate <typename DataType, typename ExpectedComplexDataType>\nvoid test_get_complex_datatype() {\n  CHECK(std::is_same_v<\n        typename tenex::detail::get_complex_datatype<DataType>::type,\n        ExpectedComplexDataType>);\n}\n\ntemplate <typename MaybeComplexDataType, typename OtherDataType>\nvoid test_is_complex_datatype_of(const bool expected) {\n  CHECK(tenex::detail::is_complex_datatype_of_v<MaybeComplexDataType,\n                                                OtherDataType> == expected);\n}\n\ntemplate <typename LhsDataType, typename RhsDataType>\nvoid test_is_assignable(const bool support_expected) {\n  CHECK(tenex::detail::is_assignable_v<LhsDataType, RhsDataType> ==\n        support_expected);\n}\n\ntemplate <typename X1, typename X2, typename ExpectedBinOpDataType>\nvoid test_binop_datatype_support() {\n  if constexpr (not std::is_same_v<ExpectedBinOpDataType, NoSuchType>) {\n    CHECK(tenex::detail::binop_datatypes_are_supported_v<X1, X2> == true);\n  } else {\n    CHECK(tenex::detail::binop_datatypes_are_supported_v<X1, X2> == false);\n  }\n\n  CHECK(std::is_same_v<\n        typename tenex::detail::get_binop_datatype_impl<\n            typename tenex::detail::upcast_if_derived_vector_type<X1>::type,\n            typename tenex::detail::upcast_if_derived_vector_type<X2>::type>::\n            type,\n        ExpectedBinOpDataType>);\n}\n\ntemplate <typename X1, typename X2>\nvoid test_tensor_binop_datatypes_are_supported(const bool support_expected) {\n  CHECK(\n      tenex::detail::tensor_binop_datatypes_are_supported_impl<X1, X2>::value ==\n      support_expected);\n}\n\ntemplate <typename T1, typename T2>\nvoid test_tensorexpression_binop_datatypes_are_supported(\n    const bool support_expected) {\n  CHECK(tenex::detail::tensorexpression_binop_datatypes_are_supported_impl<\n            T1, T2>::value == support_expected);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.DataTypeSupport\",\n                  \"[DataStructures][Unit]\") {\n  // Test which numeric types can and can't appear as terms in\n  // `TensorExpression`s\n\n  test_is_supported_number_datatype<true,\n                                    tmpl::list<double, std::complex<double>>>();\n  test_is_supported_number_datatype<\n      false, tmpl::list<int, float, std::complex<int>, std::complex<float>,\n                        DataVector, ComplexDataVector, ModalVector,\n                        ComplexModalVector, ArbitraryType>>();\n\n  // Test which types can and can't appear as a `Tensor`s data type in a\n  // `TensorExpression`\n\n  test_is_supported_tensor_datatype<\n      true, tmpl::list<double, std::complex<double>, DataVector,\n                       ComplexDataVector>>();\n  test_is_supported_tensor_datatype<\n      false, tmpl::list<int, float, std::complex<int>, std::complex<float>,\n                        ModalVector, ComplexModalVector, ArbitraryType>>();\n\n  // Test helper function that upcasts derived `VectorImpl` types to their\n  // base `VectorImpl` types\n\n  test_upcast_if_derived_vector_type<double, double>();\n  test_upcast_if_derived_vector_type<int, int>();\n  test_upcast_if_derived_vector_type<float, float>();\n  test_upcast_if_derived_vector_type<std::complex<double>,\n                                     std::complex<double>>();\n  test_upcast_if_derived_vector_type<std::complex<int>, std::complex<int>>();\n  test_upcast_if_derived_vector_type<std::complex<float>,\n                                     std::complex<float>>();\n  test_upcast_if_derived_vector_type<DataVector,\n                                     VectorImpl<double, DataVector>>();\n  test_upcast_if_derived_vector_type<VectorImpl<double, DataVector>,\n                                     VectorImpl<double, DataVector>>();\n  test_upcast_if_derived_vector_type<\n      ComplexDataVector, VectorImpl<std::complex<double>, ComplexDataVector>>();\n  test_upcast_if_derived_vector_type<\n      VectorImpl<std::complex<double>, ComplexDataVector>,\n      VectorImpl<std::complex<double>, ComplexDataVector>>();\n  test_upcast_if_derived_vector_type<ModalVector,\n                                     VectorImpl<double, ModalVector>>();\n  test_upcast_if_derived_vector_type<VectorImpl<double, ModalVector>,\n                                     VectorImpl<double, ModalVector>>();\n  test_upcast_if_derived_vector_type<\n      ComplexModalVector,\n      VectorImpl<std::complex<double>, ComplexModalVector>>();\n  test_upcast_if_derived_vector_type<\n      VectorImpl<std::complex<double>, ComplexModalVector>,\n      VectorImpl<std::complex<double>, ComplexModalVector>>();\n  test_upcast_if_derived_vector_type<\n      CustomComplexStaticSizeVector,\n      VectorImpl<std::complex<double>, CustomComplexStaticSizeVector,\n                 custom_vector_static_size>>();\n  test_upcast_if_derived_vector_type<\n      VectorImpl<std::complex<double>, CustomComplexStaticSizeVector,\n                 custom_vector_static_size>,\n      VectorImpl<std::complex<double>, CustomComplexStaticSizeVector,\n                 custom_vector_static_size>>();\n  test_upcast_if_derived_vector_type<ArbitraryType, ArbitraryType>();\n\n  // Test that we correctly get the complex-valued partner type to another type\n\n  test_get_complex_datatype<double, std::complex<double>>();\n  test_get_complex_datatype<int, std::complex<int>>();\n  test_get_complex_datatype<float, std::complex<float>>();\n  test_get_complex_datatype<std::complex<double>, NoSuchType>();\n  test_get_complex_datatype<std::complex<int>, NoSuchType>();\n  test_get_complex_datatype<std::complex<float>, NoSuchType>();\n  test_get_complex_datatype<DataVector, ComplexDataVector>();\n  test_get_complex_datatype<ComplexDataVector, NoSuchType>();\n  test_get_complex_datatype<CustomComplexStaticSizeVector, NoSuchType>();\n  test_get_complex_datatype<ArbitraryType, NoSuchType>();\n  test_get_complex_datatype<NoSuchType, NoSuchType>();\n\n  // Test whether the first data type is known to be the complex partner to\n  // the second data type\n\n  test_is_complex_datatype_of<double, double>(false);\n  test_is_complex_datatype_of<double, float>(false);\n  test_is_complex_datatype_of<double, std::complex<double>>(false);\n  test_is_complex_datatype_of<double, std::complex<float>>(false);\n  test_is_complex_datatype_of<double, DataVector>(false);\n  test_is_complex_datatype_of<double, ComplexDataVector>(false);\n  test_is_complex_datatype_of<double, ArbitraryType>(false);\n  test_is_complex_datatype_of<double, NoSuchType>(false);\n\n  test_is_complex_datatype_of<std::complex<double>, double>(true);\n  test_is_complex_datatype_of<std::complex<double>, float>(false);\n  test_is_complex_datatype_of<std::complex<double>, std::complex<double>>(\n      false);\n  test_is_complex_datatype_of<std::complex<double>, std::complex<float>>(false);\n  test_is_complex_datatype_of<std::complex<double>, DataVector>(false);\n  test_is_complex_datatype_of<std::complex<double>, ComplexDataVector>(false);\n  test_is_complex_datatype_of<std::complex<double>, ArbitraryType>(false);\n  test_is_complex_datatype_of<std::complex<double>, NoSuchType>(false);\n\n  test_is_complex_datatype_of<DataVector, double>(false);\n  test_is_complex_datatype_of<DataVector, float>(false);\n  test_is_complex_datatype_of<DataVector, std::complex<double>>(false);\n  test_is_complex_datatype_of<DataVector, std::complex<float>>(false);\n  test_is_complex_datatype_of<DataVector, DataVector>(false);\n  test_is_complex_datatype_of<DataVector, ComplexDataVector>(false);\n  test_is_complex_datatype_of<DataVector, ArbitraryType>(false);\n  test_is_complex_datatype_of<DataVector, NoSuchType>(false);\n\n  test_is_complex_datatype_of<ComplexDataVector, double>(false);\n  test_is_complex_datatype_of<ComplexDataVector, float>(false);\n  test_is_complex_datatype_of<ComplexDataVector, std::complex<double>>(false);\n  test_is_complex_datatype_of<ComplexDataVector, std::complex<float>>(false);\n  test_is_complex_datatype_of<ComplexDataVector, DataVector>(true);\n  test_is_complex_datatype_of<ComplexDataVector, ComplexDataVector>(false);\n  test_is_complex_datatype_of<ComplexDataVector, ArbitraryType>(false);\n  test_is_complex_datatype_of<ComplexDataVector, NoSuchType>(false);\n\n  test_is_complex_datatype_of<NoSuchType, double>(false);\n  test_is_complex_datatype_of<NoSuchType, float>(false);\n  test_is_complex_datatype_of<NoSuchType, std::complex<double>>(false);\n  test_is_complex_datatype_of<NoSuchType, std::complex<float>>(false);\n  test_is_complex_datatype_of<NoSuchType, DataVector>(false);\n  test_is_complex_datatype_of<NoSuchType, ComplexDataVector>(false);\n  test_is_complex_datatype_of<NoSuchType, ArbitraryType>(false);\n  test_is_complex_datatype_of<NoSuchType, NoSuchType>(false);\n\n  // Test whether the first data type is assignable to the second data type\n\n  test_is_assignable<double, double>(true);\n  test_is_assignable<double, std::complex<double>>(false);\n  test_is_assignable<double, DataVector>(false);\n  test_is_assignable<double, ComplexDataVector>(false);\n  test_is_assignable<double, CustomComplexStaticSizeVector>(false);\n  test_is_assignable<double, ArbitraryType>(false);\n\n  test_is_assignable<std::complex<double>, double>(true);\n  test_is_assignable<std::complex<double>, std::complex<double>>(true);\n  test_is_assignable<std::complex<double>, DataVector>(false);\n  test_is_assignable<std::complex<double>, ComplexDataVector>(false);\n  test_is_assignable<std::complex<double>, CustomComplexStaticSizeVector>(\n      false);\n  test_is_assignable<std::complex<double>, ArbitraryType>(false);\n\n  test_is_assignable<DataVector, double>(true);\n  test_is_assignable<DataVector, std::complex<double>>(false);\n  test_is_assignable<DataVector, DataVector>(true);\n  test_is_assignable<DataVector, ComplexDataVector>(false);\n  test_is_assignable<DataVector, CustomComplexStaticSizeVector>(false);\n  test_is_assignable<DataVector, ArbitraryType>(false);\n\n  test_is_assignable<ComplexDataVector, double>(true);\n  test_is_assignable<ComplexDataVector, std::complex<double>>(true);\n  test_is_assignable<ComplexDataVector, DataVector>(true);\n  test_is_assignable<ComplexDataVector, ComplexDataVector>(true);\n  test_is_assignable<ComplexDataVector, CustomComplexStaticSizeVector>(false);\n  test_is_assignable<ComplexDataVector, ArbitraryType>(false);\n\n  test_is_assignable<CustomComplexStaticSizeVector, double>(true);\n  test_is_assignable<CustomComplexStaticSizeVector, std::complex<double>>(true);\n  test_is_assignable<CustomComplexStaticSizeVector, DataVector>(false);\n  test_is_assignable<CustomComplexStaticSizeVector, ComplexDataVector>(false);\n  test_is_assignable<CustomComplexStaticSizeVector,\n                     CustomComplexStaticSizeVector>(true);\n  test_is_assignable<CustomComplexStaticSizeVector, ArbitraryType>(false);\n\n  test_is_assignable<ArbitraryType, double>(false);\n  test_is_assignable<ArbitraryType, std::complex<double>>(false);\n  test_is_assignable<ArbitraryType, DataVector>(false);\n  test_is_assignable<ArbitraryType, ComplexDataVector>(false);\n  test_is_assignable<ArbitraryType, CustomComplexStaticSizeVector>(false);\n  // true because is_assignable does not check if the types are supported types\n  test_is_assignable<ArbitraryType, ArbitraryType>(true);\n\n  // Test the type resulting from performing a binary arithmetic operation\n  // between two types\n\n  test_binop_datatype_support<double, double, double>();\n  test_binop_datatype_support<double, std::complex<double>,\n                              std::complex<double>>();\n  test_binop_datatype_support<double, DataVector, DataVector>();\n  test_binop_datatype_support<double, ComplexDataVector, ComplexDataVector>();\n  test_binop_datatype_support<double, CustomComplexStaticSizeVector,\n                              CustomComplexStaticSizeVector>();\n  test_binop_datatype_support<double, ArbitraryType, NoSuchType>();\n  test_binop_datatype_support<double, NoSuchType, NoSuchType>();\n\n  test_binop_datatype_support<std::complex<double>, double,\n                              std::complex<double>>();\n  test_binop_datatype_support<std::complex<double>, std::complex<double>,\n                              std::complex<double>>();\n  test_binop_datatype_support<std::complex<double>, DataVector,\n                              ComplexDataVector>();\n  test_binop_datatype_support<std::complex<double>, ComplexDataVector,\n                              ComplexDataVector>();\n  test_binop_datatype_support<std::complex<double>,\n                              CustomComplexStaticSizeVector,\n                              CustomComplexStaticSizeVector>();\n  test_binop_datatype_support<std::complex<double>, ArbitraryType,\n                              NoSuchType>();\n  test_binop_datatype_support<std::complex<double>, NoSuchType, NoSuchType>();\n\n  test_binop_datatype_support<DataVector, double, DataVector>();\n  test_binop_datatype_support<DataVector, std::complex<double>,\n                              ComplexDataVector>();\n  test_binop_datatype_support<DataVector, DataVector, DataVector>();\n  test_binop_datatype_support<DataVector, ComplexDataVector,\n                              ComplexDataVector>();\n  test_binop_datatype_support<DataVector, CustomComplexStaticSizeVector,\n                              NoSuchType>();\n  test_binop_datatype_support<DataVector, ArbitraryType, NoSuchType>();\n  test_binop_datatype_support<DataVector, NoSuchType, NoSuchType>();\n\n  test_binop_datatype_support<ComplexDataVector, double, ComplexDataVector>();\n  test_binop_datatype_support<ComplexDataVector, std::complex<double>,\n                              ComplexDataVector>();\n  test_binop_datatype_support<ComplexDataVector, DataVector,\n                              ComplexDataVector>();\n  test_binop_datatype_support<ComplexDataVector, ComplexDataVector,\n                              ComplexDataVector>();\n  test_binop_datatype_support<ComplexDataVector, CustomComplexStaticSizeVector,\n                              NoSuchType>();\n  test_binop_datatype_support<ComplexDataVector, ArbitraryType, NoSuchType>();\n  test_binop_datatype_support<ComplexDataVector, NoSuchType, NoSuchType>();\n\n  test_binop_datatype_support<CustomComplexStaticSizeVector, double,\n                              CustomComplexStaticSizeVector>();\n  test_binop_datatype_support<CustomComplexStaticSizeVector,\n                              std::complex<double>,\n                              CustomComplexStaticSizeVector>();\n  test_binop_datatype_support<CustomComplexStaticSizeVector, DataVector,\n                              NoSuchType>();\n  test_binop_datatype_support<CustomComplexStaticSizeVector, ComplexDataVector,\n                              NoSuchType>();\n  test_binop_datatype_support<CustomComplexStaticSizeVector, ArbitraryType,\n                              NoSuchType>();\n  test_binop_datatype_support<CustomComplexStaticSizeVector,\n                              CustomComplexStaticSizeVector,\n                              CustomComplexStaticSizeVector>();\n  test_binop_datatype_support<CustomComplexStaticSizeVector, NoSuchType,\n                              NoSuchType>();\n\n  test_binop_datatype_support<ArbitraryType, double, NoSuchType>();\n  test_binop_datatype_support<ArbitraryType, std::complex<double>,\n                              NoSuchType>();\n  test_binop_datatype_support<ArbitraryType, DataVector, NoSuchType>();\n  test_binop_datatype_support<ArbitraryType, ComplexDataVector, NoSuchType>();\n  test_binop_datatype_support<ArbitraryType, CustomComplexStaticSizeVector,\n                              NoSuchType>();\n  // ArbitraryType is result because get_binop_datatype_impl does not check if\n  // the types are supported types\n  test_binop_datatype_support<ArbitraryType, ArbitraryType, ArbitraryType>();\n  test_binop_datatype_support<ArbitraryType, NoSuchType, NoSuchType>();\n\n  test_binop_datatype_support<NoSuchType, double, NoSuchType>();\n  test_binop_datatype_support<NoSuchType, std::complex<double>, NoSuchType>();\n  test_binop_datatype_support<NoSuchType, DataVector, NoSuchType>();\n  test_binop_datatype_support<NoSuchType, ComplexDataVector, NoSuchType>();\n  test_binop_datatype_support<NoSuchType, ArbitraryType, NoSuchType>();\n  test_binop_datatype_support<NoSuchType, CustomComplexStaticSizeVector,\n                              NoSuchType>();\n  test_binop_datatype_support<NoSuchType, NoSuchType, NoSuchType>();\n\n  // Test whether binary operations can be performed between two `Tensor`s with\n  // the given data types\n\n  test_tensor_binop_datatypes_are_supported<double, double>(true);\n  test_tensor_binop_datatypes_are_supported<double, std::complex<double>>(true);\n  test_tensor_binop_datatypes_are_supported<double, DataVector>(false);\n  test_tensor_binop_datatypes_are_supported<double, ComplexDataVector>(false);\n  test_tensor_binop_datatypes_are_supported<double, ArbitraryType>(false);\n\n  test_tensor_binop_datatypes_are_supported<std::complex<double>, double>(true);\n  test_tensor_binop_datatypes_are_supported<std::complex<double>,\n                                            std::complex<double>>(true);\n  test_tensor_binop_datatypes_are_supported<std::complex<double>, DataVector>(\n      false);\n  test_tensor_binop_datatypes_are_supported<std::complex<double>,\n                                            ComplexDataVector>(false);\n  test_tensor_binop_datatypes_are_supported<std::complex<double>,\n                                            ArbitraryType>(false);\n\n  test_tensor_binop_datatypes_are_supported<DataVector, double>(false);\n  test_tensor_binop_datatypes_are_supported<DataVector, std::complex<double>>(\n      false);\n  test_tensor_binop_datatypes_are_supported<DataVector, DataVector>(true);\n  test_tensor_binop_datatypes_are_supported<DataVector, ComplexDataVector>(\n      true);\n  test_tensor_binop_datatypes_are_supported<DataVector, ArbitraryType>(false);\n\n  test_tensor_binop_datatypes_are_supported<ComplexDataVector, double>(false);\n  test_tensor_binop_datatypes_are_supported<ComplexDataVector,\n                                            std::complex<double>>(false);\n  test_tensor_binop_datatypes_are_supported<ComplexDataVector, DataVector>(\n      true);\n  test_tensor_binop_datatypes_are_supported<ComplexDataVector,\n                                            ComplexDataVector>(true);\n  test_tensor_binop_datatypes_are_supported<ComplexDataVector, ArbitraryType>(\n      false);\n\n  test_tensor_binop_datatypes_are_supported<ArbitraryType, double>(false);\n  test_tensor_binop_datatypes_are_supported<ArbitraryType,\n                                            std::complex<double>>(false);\n  test_tensor_binop_datatypes_are_supported<ArbitraryType, DataVector>(false);\n  test_tensor_binop_datatypes_are_supported<ArbitraryType, ComplexDataVector>(\n      false);\n  // true because tensor_binop_datatypes_are_supported_impl does not check if\n  // the types are supported types\n  test_tensor_binop_datatypes_are_supported<ArbitraryType, ArbitraryType>(true);\n\n  // Test whether binary operations can be performed between two\n  // `TensorExpression`s with the given data types\n\n  test_tensorexpression_binop_datatypes_are_supported<\n      number_expression<double>, tensor_expression<double>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      number_expression<double>, tensor_expression<std::complex<double>>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      number_expression<double>, tensor_expression<DataVector>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      number_expression<double>, tensor_expression<ComplexDataVector>>(true);\n\n  test_tensorexpression_binop_datatypes_are_supported<\n      number_expression<std::complex<double>>, tensor_expression<double>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      number_expression<std::complex<double>>,\n      tensor_expression<std::complex<double>>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      number_expression<std::complex<double>>, tensor_expression<DataVector>>(\n      true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      number_expression<std::complex<double>>,\n      tensor_expression<ComplexDataVector>>(true);\n\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<double>, number_expression<double>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<double>, number_expression<std::complex<double>>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<double>, tensor_expression<double>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<double>, tensor_expression<std::complex<double>>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<double>, tensor_expression<DataVector>>(false);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<double>, tensor_expression<ComplexDataVector>>(false);\n\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<std::complex<double>>, number_expression<double>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<std::complex<double>>,\n      number_expression<std::complex<double>>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<std::complex<double>>, tensor_expression<double>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<std::complex<double>>,\n      tensor_expression<std::complex<double>>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<std::complex<double>>, tensor_expression<DataVector>>(\n      false);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<std::complex<double>>,\n      tensor_expression<ComplexDataVector>>(false);\n\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<DataVector>, number_expression<double>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<DataVector>, number_expression<std::complex<double>>>(\n      true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<DataVector>, tensor_expression<double>>(false);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<DataVector>, tensor_expression<std::complex<double>>>(\n      false);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<DataVector>, tensor_expression<DataVector>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<DataVector>, tensor_expression<ComplexDataVector>>(\n      true);\n\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<ComplexDataVector>, number_expression<double>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<ComplexDataVector>,\n      number_expression<std::complex<double>>>(true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<ComplexDataVector>, tensor_expression<double>>(false);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<ComplexDataVector>,\n      tensor_expression<std::complex<double>>>(false);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<ComplexDataVector>, tensor_expression<DataVector>>(\n      true);\n  test_tensorexpression_binop_datatypes_are_supported<\n      tensor_expression<ComplexDataVector>,\n      tensor_expression<ComplexDataVector>>(true);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_Divide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n// Checks that the number of ops in the expressions match what is expected\nvoid test_tensor_ops_properties() {\n  const Scalar<double> G{5.0};\n  const double H = 5.0;\n  const tnsr::ii<double, 3> R{};\n  const tnsr::ij<double, 3> S{};\n\n  const auto H_over_G = H / G();\n  const auto H_over_G_over_H = H / G() / H;\n  const auto R_over_G = R(ti::i, ti::j) / G();\n  const auto S_over_H = S(ti::i, ti::j) / H;\n  const auto R_plus_S_over_G_times_H =\n      (R(ti::i, ti::j) + S(ti::i, ti::j)) / (G() * H);\n\n  CHECK(H_over_G.num_ops_subtree == 1);\n  CHECK(H_over_G_over_H.num_ops_subtree == 2);\n  CHECK(R_over_G.num_ops_subtree == 1);\n  CHECK(S_over_H.num_ops_subtree == 1);\n  CHECK(R_plus_S_over_G_times_H.num_ops_subtree == 3);\n}\n\n// \\brief Test the division of a tensor expression over a `double` is correctly\n// evaluated\n//\n// \\details\n// The cases tested are:\n// - \\f$L_{ij} = S_{ij} / R\\f$\n// - \\f$L_{ij} = (R * S_{ij} / T) / G\\f$\n//\n// where \\f$R\\f$, \\f$T\\f$, and \\f$G\\f$ are `double`s and \\f$S\\f$ and \\f$L\\f$ are\n// Tensors with data type `double` or DataVector.\n//\n// \\tparam DataType the type of data being stored in the numerator\ntemplate <typename Generator, typename DataType>\nvoid test_divide_double_denominator(const gsl::not_null<Generator*> generator,\n                                    const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-2.0, 2.0);\n  constexpr size_t dim = 3;\n\n  const auto S = make_with_random_values<tnsr::ii<DataType, dim>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{ij} = S_{ij} / R\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const tnsr::ii<DataType, dim> Lij_from_Sij_over_R =\n      tenex::evaluate<ti::i, ti::j>(S(ti::i, ti::j) / 4.3);\n  // \\f$L_{ij} = (R * S_{ij} / T / G)\\f$\n  const tnsr::ii<DataType, dim> Lij_from_R_Sij_over_T =\n      tenex::evaluate<ti::i, ti::j>((-5.2 * S(ti::i, ti::j) / 1.6) / 2.1);\n\n  for (size_t i = 0; i < dim; i++) {\n    for (size_t j = 0; j < dim; j++) {\n      CHECK_ITERABLE_APPROX(Lij_from_Sij_over_R.get(i, j), S.get(i, j) / 4.3);\n      CHECK_ITERABLE_APPROX(Lij_from_R_Sij_over_T.get(i, j),\n                            -5.2 * S.get(i, j) / 1.6 / 2.1);\n    }\n  }\n}\n\n// \\brief Test the division of a `double` over a tensor expression is correctly\n// evaluated\n//\n// \\details\n// The cases tested are:\n// - \\f$L = R / S\\f$\n// - \\f$L = R / \\sqrt{T^j{}_j}\\f$\n// - \\f$L = 1.0 / (R + R + R + R + R + R + R + R + R)\\f$\n//\n// where \\f$R\\f$ is a `double` and \\f$S\\f$, \\f$T\\f$, and \\f$L\\f$ are tensors\n// with data type `double` or DataVector.\n//\n// \\tparam DataType the type of data being stored in the numerator\ntemplate <typename Generator, typename DataType>\nvoid test_divide_double_numerator(const gsl::not_null<Generator*> generator,\n                                  const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 2.0);\n  constexpr size_t dim = 3;\n\n  const auto S = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n  const auto T = make_with_random_values<tnsr::Ij<DataType, dim>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L = R / S\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const Scalar<DataType> result1 = tenex::evaluate(2.1 / S());\n  CHECK_ITERABLE_APPROX(get(result1), 2.1 / get(S));\n\n  // \\f$L = R / \\sqrt{T^j{}_j}\\f$\n  const Scalar<DataType> result2 =\n      tenex::evaluate(-5.7 / sqrt(T(ti::J, ti::j)));\n\n  DataType trace_T = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t j = 0; j < dim; j++) {\n    trace_T += T.get(j, j);\n  }\n  CHECK_ITERABLE_APPROX(get(result2), -5.7 / sqrt(trace_T));\n\n  const Scalar<DataVector> R{{{{1.0}}}};\n  Scalar<DataVector> result3{};\n\n  // \\f$L = 1.0 / (R + R + R + R + R + R + R + R + R)\\f$\n  tenex::evaluate(make_not_null(&result3),\n                  1.0 / (R() + R() + R() + R() + R() + R() + R() + R() + R()));\n  CHECK_ITERABLE_APPROX(get(result3), 1.0 / (9.0 * get(R)));\n}\n\n// \\brief Test the division of a tensor expression over a rank 0 tensor\n// expression is correctly evaluated\n//\n// \\details\n// The cases tested are:\n// - \\f$L_{i}{}^{j} = S^{i}{}_{j} / R\\f$\n// - \\f$L^{k}{}_{i} = (R T_{i}{}^{k}) / (T_{j}{}^{l} S^{j}{}_{l})\\f$\n// - \\f$L_{i}{}^{k} = T_{i}{}^{k} / R^2 / R\\f$\n//\n// where \\f$R\\f$, \\f$S\\f$, \\f$T\\f$, and \\f$L\\f$ are Tensors with data type\n// `double` or DataVector.\n//\n// \\tparam DataType the type of data being stored in the operands and result\ntemplate <typename Generator, typename DataType>\nvoid test_divide_rank0_denominator(const gsl::not_null<Generator*> generator,\n                                   const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 2.0);\n  constexpr size_t dim = 3;\n\n  const auto R = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n  const auto S = make_with_random_values<tnsr::Ij<DataType, dim>>(\n      generator, distribution, used_for_size);\n  const auto T = make_with_random_values<tnsr::iJ<DataType, dim>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{i}{}^{j} = S^{i}{}_{j} / R\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const tnsr::iJ<DataType, dim> result1 =\n      tenex::evaluate<ti::j, ti::I>(S(ti::I, ti::j) / R());\n\n  for (size_t i = 0; i < dim; i++) {\n    for (size_t j = 0; j < dim; j++) {\n      CHECK_ITERABLE_APPROX(result1.get(j, i), S.get(i, j) / get(R));\n    }\n  }\n\n  // \\f$L^{k}{}_{i} = (R T_{i}{}^{k}) / (T_{j}{}^{l} S^{j}{}_{l})\\f$\n  const tnsr::Ij<DataType, dim> result2 = tenex::evaluate<ti::K, ti::i>(\n      (R() * T(ti::i, ti::K)) / ((T(ti::j, ti::L) * S(ti::J, ti::l))));\n\n  DataType result2_expected_denominator =\n      make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t j = 0; j < dim; j++) {\n    for (size_t l = 0; l < dim; l++) {\n      result2_expected_denominator += T.get(j, l) * S.get(j, l);\n    }\n  }\n\n  for (size_t i = 0; i < dim; i++) {\n    for (size_t k = 0; k < dim; k++) {\n      CHECK_ITERABLE_APPROX(\n          result2.get(k, i),\n          get(R) * T.get(i, k) / result2_expected_denominator);\n    }\n  }\n\n  // \\f$L_{i}{}^{k} = T_{i}{}^{k} / R^2 / R\\f$\n  const tnsr::iJ<DataType, dim> result3 =\n      tenex::evaluate<ti::i, ti::K>(T(ti::i, ti::K) / square(R()) / R());\n\n  DataType result3_expected_denominator = get(R) * get(R) * get(R);\n  for (size_t i = 0; i < dim; i++) {\n    for (size_t k = 0; k < dim; k++) {\n      CHECK_ITERABLE_APPROX(result3.get(i, k),\n                            T.get(i, k) / result3_expected_denominator);\n    }\n  }\n}\n\n// \\brief Test the division of a tensor expression over a rank 0 tensor\n// expression is correctly evaluated when generic indices are used for some\n// spacetime indices\n//\n// \\details\n// The cases tested are:\n// - \\f$L_{ai} = (T_{a}{}^{i} - S^{i}{}_{a}) / R\\f$\n// - \\f$L = (R / (T_{j}{}^{l} S^{j}{}_{l}) / 2\\f$\n//\n// where \\f$R\\f$, \\f$S\\f$, \\f$T\\f$, and \\f$L\\f$ are Tensors with data type\n// `double` or DataVector.\n//\n// \\tparam DataType the type of data being stored in the operands and result\ntemplate <typename Generator, typename DataType>\nvoid test_divide_spatial_spacetime_index(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 2.0);\n  constexpr size_t dim = 3;\n  using aI = Tensor<DataType, Symmetry<2, 1>,\n                    index_list<SpacetimeIndex<dim, UpLo::Lo, Frame::Inertial>,\n                               SpatialIndex<dim, UpLo::Up, Frame::Inertial>>>;\n\n  const auto R = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n  const auto S = make_with_random_values<tnsr::Ab<DataType, dim>>(\n      generator, distribution, used_for_size);\n  const auto T =\n      make_with_random_values<aI>(generator, distribution, used_for_size);\n\n  // \\f$L_{ai} = (T_{a}{}^{i} - S^{i}{}_{a}) / R\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const aI result1 =\n      tenex::evaluate<ti::a, ti::I>((T(ti::a, ti::I) - S(ti::I, ti::a)) / R());\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    for (size_t i = 0; i < dim; i++) {\n      CHECK_ITERABLE_APPROX(result1.get(a, i),\n                            (T.get(a, i) - S.get(i + 1, a)) / get(R));\n    }\n  }\n\n  // \\f$L = (R / (T_{j}{}^{l} S^{j}{}_{l}) / 2\\f$\n  const Scalar<DataType> result2 =\n      tenex::evaluate(R() / (T(ti::j, ti::L) * S(ti::J, ti::l)) / 2.0);\n\n  DataType result2_expected_denominator =\n      make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t j = 0; j < dim; j++) {\n    for (size_t l = 0; l < dim; l++) {\n      result2_expected_denominator += T.get(j + 1, l) * S.get(j + 1, l + 1);\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(get(result2),\n                        0.5 * get(R) / result2_expected_denominator);\n}\n\n// \\brief Test the division of a tensor expression over a rank 0 tensor\n// expression is correctly evaluated when time indices are used for some\n// spacetime indices\n//\n// \\details\n// The cases tested are:\n// - \\f$L^{a} = S^{a}{}_{t} / \\sqrt{R}\\f$\n// - \\f$L = R / (T_{tt} / S^{t}{}_{t} \\f$\n//\n// where \\f$R\\f$, \\f$S\\f$, \\f$T\\f$, and \\f$L\\f$ are Tensors with data type\n// `double` or DataVector.\n//\n// \\tparam DataType the type of data being stored in the operands and result\ntemplate <typename Generator, typename DataType>\nvoid test_divide_time_index(const gsl::not_null<Generator*> generator,\n                            const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 2.0);\n  constexpr size_t dim = 3;\n\n  const auto R = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n  const auto S = make_with_random_values<tnsr::Ab<DataType, dim>>(\n      generator, distribution, used_for_size);\n  const auto T = make_with_random_values<tnsr::ab<DataType, dim>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L^{a} = S^{a}{}_{t} / \\sqrt{R}\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const tnsr::A<DataType, dim> result1 =\n      tenex::evaluate<ti::A>((S(ti::A, ti::t)) / sqrt(R()));\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    CHECK_ITERABLE_APPROX(result1.get(a), (S.get(a, 0)) / sqrt(get(R)));\n  }\n\n  // \\f$L = R / (T_{tt} / S^{t}{}_{t} \\f$\n  const Scalar<DataType> result2 =\n      tenex::evaluate(R() / T(ti::t, ti::t) / S(ti::T, ti::t));\n\n  CHECK_ITERABLE_APPROX(get(result2), get(R) / T.get(0, 0) / S.get(0, 0));\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_divide(const gsl::not_null<Generator*> generator,\n                 const DataType& used_for_size) {\n  test_divide_double_denominator(generator, used_for_size);\n  test_divide_double_numerator(generator, used_for_size);\n  test_divide_rank0_denominator(generator, used_for_size);\n  test_divide_spatial_spacetime_index(generator, used_for_size);\n  test_divide_time_index(generator, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.Divide\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  test_tensor_ops_properties();\n  test_divide(make_not_null(&generator),\n              std::numeric_limits<double>::signaling_NaN());\n  test_divide(\n      make_not_null(&generator),\n      std::complex<double>(std::numeric_limits<double>::signaling_NaN(),\n                           std::numeric_limits<double>::signaling_NaN()));\n  test_divide(make_not_null(&generator),\n              DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n  test_divide(\n      make_not_null(&generator),\n      ComplexDataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_Evaluate.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <cstdint>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/Evaluate.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank0.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank1.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank2.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <std::int32_t... Is>\nusing expected_symm = tmpl::integral_list<std::int32_t, Is...>;\n\ntemplate <IndexType... Is>\nusing indextype_list = tmpl::integral_list<IndexType, Is...>;\n\nconst IndexType spatial_index = IndexType::Spatial;\nconst IndexType spacetime_index = IndexType::Spacetime;\n\ntemplate <auto&... TensorIndices>\nvoid test_contains_indices_to_contract_impl(const bool expected) {\n  CHECK(tenex::detail::contains_indices_to_contract<sizeof...(TensorIndices)>(\n            {{std::decay_t<decltype(TensorIndices)>::value...}}) == expected);\n}\n\n// Tests the helper function `tenex::detail::contains_indices_to_contract`\n// correctly determines whether or not a list of tensor indices contains at\n// least one index pair to contract\nvoid test_contains_indices_to_contract() {\n  test_contains_indices_to_contract_impl<ti::a, ti::b, ti::c>(false);\n  test_contains_indices_to_contract_impl<ti::I, ti::j>(false);\n  test_contains_indices_to_contract_impl<ti::j>(false);\n  test_contains_indices_to_contract_impl(false);\n\n  test_contains_indices_to_contract_impl<ti::d, ti::D>(true);\n  test_contains_indices_to_contract_impl<ti::I, ti::i>(true);\n  test_contains_indices_to_contract_impl<ti::a, ti::K, ti::B, ti::b>(true);\n  test_contains_indices_to_contract_impl<ti::j, ti::c, ti::J, ti::A, ti::a>(\n      true);\n}\n\n// Tests that the canonical ordering of symmetry values by `Symmetry` is\n// consistent with what `tenex::detail::get_reordered_tensorindex_values`\n// expects, which is that the symmetry values assigned to indepdenent indices\n// is ascending from the rightmost position moving leftward with the rightmost\n// symmetry value starting at 1\nvoid test_lhs_tensorindex_reorder_symm_consistency() {\n  const std::string error_msg =\n      \"tenex::detail::get_reordered_tensorindex_values() assumes a canonical \"\n      \"form for Symmetry that is no longer the actual canonical form of \"\n      \"Symmetry. The logic of this unit test and \"\n      \"tenex::detail::get_reordered_tensorindex_values() must be updated to \"\n      \"agree with the current canonical form for Symmetry\";\n  INFO(error_msg);\n\n  CHECK(std::is_same_v<Symmetry<>, expected_symm<>>);\n  CHECK(std::is_same_v<Symmetry<4>, expected_symm<1>>);\n  CHECK(std::is_same_v<Symmetry<1, 2>, expected_symm<2, 1>>);\n  CHECK(std::is_same_v<Symmetry<3, 5>, expected_symm<2, 1>>);\n  CHECK(std::is_same_v<Symmetry<2, 2, 2>, expected_symm<1, 1, 1>>);\n  CHECK(std::is_same_v<Symmetry<8, 4, 5, 5, 8>, expected_symm<1, 3, 2, 2, 1>>);\n}\n\n// Tests that the canonical ordering of multi-indices by\n// `Tensor_detail::Structure` is consistent with what\n// `tenex::detail::evaluate_impl` expects. Specifically, it checks that the\n// canonical multi-indices of independent tensor components are ordered such\n// that within each subset of symmetric indices, the index values are\n// ascending from the rightmost index to the left, which is what `evaluate_impl`\n// assumes.\nvoid test_evaluate_and_canon_multi_index_consistency() {\n  const std::string error_msg =\n      \"tenex::evaluate() assumes a canonical form for multi-indices that is no \"\n      \"longer consistent with the canonical form defined by \"\n      \"Tensor_detail::Structure. The logic of this unit test and \"\n      \"tenex::detail::evaluate_impl must be updated to agree with the current \"\n      \"canonical form for multi-indices.\";\n  INFO(error_msg);\n\n  using datatype = double;\n  using frame = Frame::Inertial;\n\n  using iii = tnsr::iii<datatype, 3>::structure;\n  using aaa = Tensor<datatype, Symmetry<1, 1, 1>,\n                     index_list<SpacetimeIndex<3, UpLo::Lo, frame>,\n                                SpacetimeIndex<3, UpLo::Lo, frame>,\n                                SpacetimeIndex<3, UpLo::Lo, frame>>>::structure;\n  using iaai = Tensor<datatype, Symmetry<1, 2, 2, 1>,\n                      index_list<SpatialIndex<3, UpLo::Lo, frame>,\n                                 SpacetimeIndex<3, UpLo::Lo, frame>,\n                                 SpacetimeIndex<3, UpLo::Lo, frame>,\n                                 SpatialIndex<3, UpLo::Lo, frame>>>::structure;\n  using iiaa =\n      Tensor<datatype, Symmetry<2, 2, 1, 1>,\n             index_list<SpatialIndex<2, UpLo::Lo, frame>,\n                        SpatialIndex<2, UpLo::Lo, frame>,\n                        SpacetimeIndex<2, UpLo::Lo, frame>,\n                        SpacetimeIndex<2, UpLo::Lo, frame>>>::structure;\n  using aiai = Tensor<datatype, Symmetry<2, 1, 2, 1>,\n                      index_list<SpacetimeIndex<3, UpLo::Lo, frame>,\n                                 SpatialIndex<3, UpLo::Lo, frame>,\n                                 SpacetimeIndex<3, UpLo::Lo, frame>,\n                                 SpatialIndex<3, UpLo::Lo, frame>>>::structure;\n  using iiii = Tensor<datatype, Symmetry<1, 1, 1, 1>,\n                      index_list<SpatialIndex<3, UpLo::Lo, frame>,\n                                 SpatialIndex<3, UpLo::Lo, frame>,\n                                 SpatialIndex<3, UpLo::Lo, frame>,\n                                 SpatialIndex<3, UpLo::Lo, frame>>>::structure;\n\n  for (size_t i = 0; i < iii::size(); i++) {\n    const auto canon_multi_index = iii::get_canonical_tensor_index(i);\n\n    CHECK(canon_multi_index[0] >= canon_multi_index[1]);\n    CHECK(canon_multi_index[1] >= canon_multi_index[2]);\n  }\n\n  for (size_t i = 0; i < aaa::size(); i++) {\n    const auto canon_multi_index = aaa::get_canonical_tensor_index(i);\n\n    CHECK(canon_multi_index[0] >= canon_multi_index[1]);\n    CHECK(canon_multi_index[1] >= canon_multi_index[2]);\n  }\n\n  for (size_t i = 0; i < iaai::size(); i++) {\n    const auto canon_multi_index = iaai::get_canonical_tensor_index(i);\n\n    CHECK(canon_multi_index[0] >= canon_multi_index[3]);\n    CHECK(canon_multi_index[1] >= canon_multi_index[2]);\n  }\n\n  for (size_t i = 0; i < iiaa::size(); i++) {\n    const auto canon_multi_index = iiaa::get_canonical_tensor_index(i);\n\n    CHECK(canon_multi_index[0] >= canon_multi_index[1]);\n    CHECK(canon_multi_index[2] >= canon_multi_index[3]);\n  }\n\n  for (size_t i = 0; i < aiai::size(); i++) {\n    const auto canon_multi_index = aiai::get_canonical_tensor_index(i);\n\n    CHECK(canon_multi_index[0] >= canon_multi_index[2]);\n    CHECK(canon_multi_index[1] >= canon_multi_index[3]);\n  }\n\n  for (size_t i = 0; i < iiii::size(); i++) {\n    const auto canon_multi_index = iiii::get_canonical_tensor_index(i);\n\n    CHECK(canon_multi_index[0] >= canon_multi_index[1]);\n    CHECK(canon_multi_index[1] >= canon_multi_index[2]);\n    CHECK(canon_multi_index[2] >= canon_multi_index[3]);\n  }\n}\n\n// \\brief Test evaluation of rank 0, rank 1, and rank 2 tensors\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_evaluate_rank_012() {\n  // Rank 0\n  TestHelpers::tenex::test_evaluate_rank_0<DataType>();\n\n  // Rank 1: spacetime\n  TestHelpers::tenex::test_evaluate_rank_1<true, DataType,\n                                           indextype_list<spacetime_index>,\n                                           ti::a, Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_1<\n      true, DataType, indextype_list<spacetime_index>, ti::b, Frame::Grid>();\n  TestHelpers::tenex::test_evaluate_rank_1<true, DataType,\n                                           indextype_list<spacetime_index>,\n                                           ti::A, Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_1<\n      true, DataType, indextype_list<spacetime_index>, ti::B, Frame::Grid>();\n\n  // Rank 1: spatial\n  TestHelpers::tenex::test_evaluate_rank_1<\n      true, DataType, indextype_list<spatial_index>, ti::i, Frame::Grid>();\n  TestHelpers::tenex::test_evaluate_rank_1<\n      true, DataType, indextype_list<spatial_index>, ti::j, Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_1<\n      true, DataType, indextype_list<spatial_index>, ti::I, Frame::Grid>();\n  TestHelpers::tenex::test_evaluate_rank_1<\n      true, DataType, indextype_list<spatial_index>, ti::J, Frame::Inertial>();\n\n  // Rank 2: nonsymmetric, spacetime only\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spacetime_index, spacetime_index>, ti::a, ti::b,\n      Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spacetime_index, spacetime_index>, ti::D, ti::C,\n      Frame::Distorted>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spacetime_index, spacetime_index>, ti::e, ti::F,\n      Frame::Grid>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spacetime_index, spacetime_index>, ti::G, ti::b,\n      Frame::ElementLogical>();\n\n  // Rank 2: nonsymmetric, spatial only\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spatial_index, spatial_index>, ti::j, ti::i,\n      Frame::ElementLogical>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spatial_index, spatial_index>, ti::I, ti::J,\n      Frame::Grid>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spatial_index, spatial_index>, ti::k, ti::M,\n      Frame::Distorted>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spatial_index, spatial_index>, ti::M, ti::k,\n      Frame::Inertial>();\n\n  // Rank 2: nonsymmetric, spacetime and spatial mixed\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spacetime_index, spatial_index>, ti::c, ti::I,\n      Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spacetime_index, spatial_index>, ti::A, ti::i,\n      Frame::Grid>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spatial_index, spacetime_index>, ti::J, ti::C,\n      Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<2, 1>,\n      indextype_list<spatial_index, spacetime_index>, ti::m, ti::e,\n      Frame::Grid>();\n\n  // Rank 2: symmetric, spacetime\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<1, 1>,\n      indextype_list<spacetime_index, spacetime_index>, ti::a, ti::d,\n      Frame::Grid>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      false, DataType, Symmetry<1, 1>,\n      indextype_list<spacetime_index, spacetime_index>, ti::a, ti::d,\n      Frame::Inertial, Symmetry<2, 1>>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<1, 1>,\n      indextype_list<spacetime_index, spacetime_index>, ti::G, ti::B,\n      Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      false, DataType, Symmetry<1, 1>,\n      indextype_list<spacetime_index, spacetime_index>, ti::G, ti::B,\n      Frame::Grid, Symmetry<2, 1>>();\n\n  // Rank 2: symmetric, spatial\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<1, 1>,\n      indextype_list<spatial_index, spatial_index>, ti::j, ti::i,\n      Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      false, DataType, Symmetry<1, 1>,\n      indextype_list<spatial_index, spatial_index>, ti::j, ti::i, Frame::Grid,\n      Symmetry<2, 1>>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      true, DataType, Symmetry<1, 1>,\n      indextype_list<spatial_index, spatial_index>, ti::I, ti::J,\n      Frame::Grid>();\n  TestHelpers::tenex::test_evaluate_rank_2<\n      false, DataType, Symmetry<1, 1>,\n      indextype_list<spatial_index, spatial_index>, ti::I, ti::J,\n      Frame::Inertial, Symmetry<2, 1>>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.Evaluate\",\n                  \"[DataStructures][Unit]\") {\n  // Test tenex::evaluate implementation details\n  test_contains_indices_to_contract();\n  test_lhs_tensorindex_reorder_symm_consistency();\n  test_evaluate_and_canon_multi_index_consistency();\n\n  // Test tenex::evaluate for ranks 0, 1, and 2 tensors\n  test_evaluate_rank_012<double>();\n  test_evaluate_rank_012<DataVector>();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_EvaluateComplex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// \\file\n// Tests evaluation of `TensorExpression`s with complex-valued LHS `Tensor`s\n// and RHS expressions that may contain real-valued terms, complex-valued terms,\n// or both\n//\n// \\details\n// The tests in this file are designed to test that these fundamentally work:\n// - Using `evaluate` with complex types on the RHS and/or LHS\n// - Using `TensorExpression` mathematical operations with complex types\n// - Evaluating expressions with both real-valued and complex-valued terms\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <complex>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <typename T1, typename T2>\nvoid check_values_equal(const T1& lhs_value, const T2& rhs_value) {\n  CHECK_ITERABLE_APPROX(lhs_value, rhs_value);\n}\n\ntemplate <>\nvoid check_values_equal<std::complex<double>, double>(\n    const std::complex<double>& lhs_value, const double& rhs_value) {\n  CHECK(std::imag(lhs_value) == 0.0);\n  CHECK_ITERABLE_APPROX(std::real(lhs_value), rhs_value);\n}\n\ntemplate <>\nvoid check_values_equal<ComplexDataVector, double>(\n    const ComplexDataVector& lhs_value, const double& rhs_value) {\n  for (size_t i = 0; i < lhs_value.size(); i++) {\n    CHECK(std::imag(lhs_value[i]) == 0.0);\n    CHECK_ITERABLE_APPROX(std::real(lhs_value[i]), rhs_value);\n  }\n}\n\ntemplate <>\nvoid check_values_equal<ComplexDataVector, DataVector>(\n    const ComplexDataVector& lhs_value, const DataVector& rhs_value) {\n  for (size_t i = 0; i < lhs_value.size(); i++) {\n    CHECK(std::imag(lhs_value[i]) == 0.0);\n    CHECK_ITERABLE_APPROX(std::real(lhs_value[i]), rhs_value[i]);\n  }\n}\n\n// \\brief Test assignment of LHS `Tensor` to single RHS term\n//\n// \\tparam LhsDataType the data type of LHS `Tensor`\n// \\tparam RhsDataType the data type of the RHS term\ntemplate <typename Generator, typename LhsDataType, typename RhsDataType>\nvoid test_assignment_to_single_term(const gsl::not_null<Generator*> generator,\n                                    const LhsDataType& used_for_size_lhs,\n                                    const RhsDataType& used_for_size_rhs) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  // if the RHS is a number, also test the assignment of LHS to the number\n  if constexpr (tenex::detail::is_supported_number_datatype_v<RhsDataType>) {\n    const auto R1 = make_with_random_values<RhsDataType>(\n        generator, distribution, used_for_size_rhs);\n    Scalar<LhsDataType> L1{used_for_size_lhs};\n    tenex::evaluate(make_not_null(&L1), R1);\n    check_values_equal(get(L1), R1);\n  }\n\n  // assign Scalar<LhsDataType> to Scalar<RhsDataType>\n  const auto R2 = make_with_random_values<Scalar<RhsDataType>>(\n      generator, distribution, used_for_size_rhs);\n  Scalar<LhsDataType> L2{used_for_size_lhs};\n  tenex::evaluate(make_not_null(&L2), R2());\n  check_values_equal(get(L2), get(R2));\n\n  // assign Tensor<LhsDataType, ...> to Tensor<RhsDataType, ...>\n  const auto R3 = make_with_random_values<tnsr::ii<RhsDataType, 3>>(\n      generator, distribution, used_for_size_rhs);\n  tnsr::ii<LhsDataType, 3> L3{used_for_size_lhs};\n  tenex::evaluate<ti::j, ti::i>(make_not_null(&L3), R3(ti::i, ti::j));\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = i; j < 3; j++) {\n      check_values_equal(L3.get(j, i), R3.get(i, j));\n    }\n  }\n}\n\n// \\brief Test assignment of LHS `Tensor` to a RHS expression containing\n// mathematical operations\n//\n// \\tparam LhsDataType the data type of LHS `Tensor`\n// \\tparam RhsDataType the data type of the RHS term\ntemplate <typename Generator, typename LhsDataType, typename RhsDataType>\nvoid test_evaluate_ops(const gsl::not_null<Generator*> generator,\n                       const LhsDataType& used_for_size_lhs,\n                       const RhsDataType& used_for_size_rhs) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  const auto R =\n      make_with_random_values<tnsr::Ab<RhsDataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size_rhs);\n  const auto S =\n      make_with_random_values<tnsr::aB<RhsDataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size_rhs);\n  const auto T = make_with_random_values<Scalar<RhsDataType>>(\n      generator, distribution, used_for_size_rhs);\n\n  // test evaluation of unary ops\n  Scalar<LhsDataType> L_contraction{used_for_size_lhs};\n  tenex::evaluate(make_not_null(&L_contraction), R(ti::A, ti::a));\n  RhsDataType expected_L_contraction =\n      make_with_value<RhsDataType>(used_for_size_rhs, 0.0);\n  for (size_t a = 0; a < 4; a++) {\n    expected_L_contraction += R.get(a, a);\n  }\n  check_values_equal(get(L_contraction), expected_L_contraction);\n\n  tnsr::Ab<LhsDataType, 3, Frame::Inertial> L_negation{used_for_size_lhs};\n  tenex::evaluate<ti::B, ti::a>(make_not_null(&L_negation), -S(ti::a, ti::B));\n  for (size_t a = 0; a < 4; a++) {\n    for (size_t b = 0; b < 4; b++) {\n      check_values_equal(L_negation.get(b, a), -S.get(a, b));\n    }\n  }\n\n  Scalar<LhsDataType> L_square_root{used_for_size_lhs};\n  tenex::evaluate(make_not_null(&L_square_root), sqrt(T()));\n  check_values_equal(get(L_square_root), sqrt(get(T)));\n\n  // test evaluation of binary ops\n  tnsr::Ab<LhsDataType, 3, Frame::Inertial> L_addition{used_for_size_lhs};\n  tnsr::Ab<LhsDataType, 3, Frame::Inertial> L_subtraction{used_for_size_lhs};\n  tenex::evaluate<ti::A, ti::b>(make_not_null(&L_addition),\n                                R(ti::A, ti::b) + S(ti::b, ti::A));\n  tenex::evaluate<ti::B, ti::a>(make_not_null(&L_subtraction),\n                                S(ti::a, ti::B) - R(ti::B, ti::a));\n  for (size_t a = 0; a < 4; a++) {\n    for (size_t b = 0; b < 4; b++) {\n      check_values_equal(L_addition.get(a, b), R.get(a, b) + S.get(b, a));\n      check_values_equal(L_subtraction.get(b, a), S.get(a, b) - R.get(b, a));\n    }\n  }\n\n  tnsr::aB<LhsDataType, 3, Frame::Inertial> L_product{used_for_size_lhs};\n  tenex::evaluate<ti::a, ti::B>(make_not_null(&L_product),\n                                R(ti::C, ti::a) * S(ti::c, ti::B));\n  for (size_t a = 0; a < 4; a++) {\n    for (size_t b = 0; b < 4; b++) {\n      RhsDataType expected_sum =\n          make_with_value<RhsDataType>(used_for_size_rhs, 0.0);\n      for (size_t c = 0; c < 4; c++) {\n        expected_sum += R.get(c, a) * S.get(c, b);\n      }\n      check_values_equal(L_product.get(a, b), expected_sum);\n    }\n  }\n\n  tnsr::aB<LhsDataType, 3, Frame::Inertial> L_division{used_for_size_lhs};\n  tenex::evaluate<ti::a, ti::B>(make_not_null(&L_division),\n                                S(ti::a, ti::B) / T());\n  for (size_t a = 0; a < 4; a++) {\n    for (size_t b = 0; b < 4; b++) {\n      check_values_equal(L_division.get(a, b), S.get(a, b) / get(T));\n    }\n  }\n}\n\n// \\brief Test evaluation of RHS binary operations between real-valued and\n// complex-valued terms\n//\n// \\details\n// Tests when (1) the terms are both `Tensor`s and (2) when one term is a\n// `Tensor` and the other is a number\n//\n// Note: Binary operations between a complex number and a\n// `Tensor<DataVector, ...>` are only tested for multiplication. This is because\n// for `std::complex<double> OP DataVector`, Blaze currently only supports\n// multiplication.\n//\n// \\tparam ComplexDataType the data type of the complex-valued operand\n// \\tparam RhsDataType the data type of the real-valued operand\ntemplate <typename Generator, typename ComplexDataType, typename RealDataType>\nvoid test_bin_ops_with_real_and_complex(\n    const gsl::not_null<Generator*> generator,\n    const ComplexDataType& used_for_size_complex,\n    const RealDataType& used_for_size_real,\n    const double used_for_random_real_number,\n    const std::complex<double> used_for_random_complex_number) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n  constexpr size_t Dim = 2;\n\n  // Operands for test expressions\n\n  const auto real_Ij =\n      make_with_random_values<tnsr::Ij<RealDataType, Dim, Frame::Grid>>(\n          generator, distribution, used_for_size_real);\n  const auto complex_Ij =\n      make_with_random_values<tnsr::Ij<ComplexDataType, Dim, Frame::Grid>>(\n          generator, distribution, used_for_size_complex);\n  const auto real_iJ =\n      make_with_random_values<tnsr::iJ<RealDataType, Dim, Frame::Grid>>(\n          generator, distribution, used_for_size_real);\n  const auto complex_iJ =\n      make_with_random_values<tnsr::iJ<ComplexDataType, Dim, Frame::Grid>>(\n          generator, distribution, used_for_size_complex);\n  const auto real_scalar = make_with_random_values<Scalar<RealDataType>>(\n      generator, distribution, used_for_size_real);\n  const auto complex_scalar = make_with_random_values<Scalar<ComplexDataType>>(\n      generator, distribution, used_for_size_complex);\n  const auto real_number = make_with_random_values<double>(\n      generator, distribution, used_for_random_real_number);\n  const auto complex_number = make_with_random_values<std::complex<double>>(\n      generator, distribution, used_for_random_complex_number);\n\n  // Tested expressions\n\n  // addition\n  const Scalar<ComplexDataType> complex_tensor_plus_real_number =\n      tenex::evaluate(complex_scalar() + real_number);\n  const Scalar<ComplexDataType> real_number_plus_complex_tensor =\n      tenex::evaluate(real_number + complex_scalar());\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      complex_tensor_plus_real_tensor = tenex::evaluate<ti::i, ti::J>(\n          complex_iJ(ti::i, ti::J) + real_iJ(ti::i, ti::J));\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      real_tensor_plus_complex_tensor = tenex::evaluate<ti::i, ti::J>(\n          real_Ij(ti::J, ti::i) + complex_Ij(ti::J, ti::i));\n\n  // subtraction\n  const Scalar<ComplexDataType> complex_tensor_minus_real_number =\n      tenex::evaluate(complex_scalar() - real_number);\n  const Scalar<ComplexDataType> real_number_minus_complex_tensor =\n      tenex::evaluate(real_number - complex_scalar());\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      complex_tensor_minus_real_tensor = tenex::evaluate<ti::i, ti::J>(\n          complex_iJ(ti::i, ti::J) - real_iJ(ti::i, ti::J));\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      real_tensor_minus_complex_tensor = tenex::evaluate<ti::i, ti::J>(\n          real_Ij(ti::J, ti::i) - complex_Ij(ti::J, ti::i));\n\n  // multiplication\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      complex_tensor_times_real_number =\n          tenex::evaluate<ti::i, ti::J>(complex_iJ(ti::i, ti::J) * real_number);\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      real_number_times_complex_tensor =\n          tenex::evaluate<ti::i, ti::J>(real_number * complex_Ij(ti::J, ti::i));\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      real_tensor_times_complex_number =\n          tenex::evaluate<ti::i, ti::J>(real_iJ(ti::i, ti::J) * complex_number);\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      complex_number_times_real_tensor =\n          tenex::evaluate<ti::i, ti::J>(complex_number * real_iJ(ti::i, ti::J));\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      complex_tensor_times_real_tensor = tenex::evaluate<ti::i, ti::J>(\n          complex_iJ(ti::i, ti::K) * real_iJ(ti::k, ti::J));\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      real_tensor_times_complex_tensor = tenex::evaluate<ti::i, ti::J>(\n          real_Ij(ti::K, ti::i) * complex_Ij(ti::J, ti::k));\n\n  // division\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      complex_tensor_over_real_number =\n          tenex::evaluate<ti::i, ti::J>(complex_iJ(ti::i, ti::J) / real_number);\n  const Scalar<ComplexDataType> real_number_over_complex_tensor =\n      tenex::evaluate(real_number / complex_scalar());\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      complex_tensor_over_real_tensor = tenex::evaluate<ti::i, ti::J>(\n          complex_iJ(ti::i, ti::J) / real_scalar());\n  const tnsr::iJ<ComplexDataType, Dim, Frame::Grid>\n      real_tensor_over_complex_tensor = tenex::evaluate<ti::i, ti::J>(\n          real_Ij(ti::J, ti::i) / complex_scalar());\n\n  // Check rank == 0 results\n\n  // addition\n  CHECK(get(complex_tensor_plus_real_number) ==\n        get(complex_scalar) + real_number);\n  CHECK(get(real_number_plus_complex_tensor) ==\n        real_number + get(complex_scalar));\n\n  // subtraction\n  CHECK(get(complex_tensor_minus_real_number) ==\n        get(complex_scalar) - real_number);\n  CHECK(get(real_number_minus_complex_tensor) ==\n        real_number - get(complex_scalar));\n\n  // division\n  CHECK(get(real_number_over_complex_tensor) ==\n        real_number / get(complex_scalar));\n\n  // Check rank > 0 results\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = 0; j < Dim; j++) {\n      ComplexDataType expected_sum_complex_tensor_times_real_tensor =\n          complex_iJ.get(i, 0) * real_iJ.get(0, j);\n      ComplexDataType expected_sum_real_tensor_times_complex_tensor =\n          real_Ij.get(0, i) * complex_Ij.get(j, 0);\n      for (size_t k = 1; k < Dim; k++) {\n        expected_sum_complex_tensor_times_real_tensor +=\n            complex_iJ.get(i, k) * real_iJ.get(k, j);\n        expected_sum_real_tensor_times_complex_tensor +=\n            real_Ij.get(k, i) * complex_Ij.get(j, k);\n      }\n\n      // addition\n      CHECK(complex_tensor_plus_real_tensor.get(i, j) ==\n            complex_iJ.get(i, j) + real_iJ.get(i, j));\n      CHECK(real_tensor_plus_complex_tensor.get(i, j) ==\n            real_Ij.get(j, i) + complex_Ij.get(j, i));\n\n      // subtraction\n      CHECK(complex_tensor_minus_real_tensor.get(i, j) ==\n            complex_iJ.get(i, j) - real_iJ.get(i, j));\n      CHECK(real_tensor_minus_complex_tensor.get(i, j) ==\n            real_Ij.get(j, i) - complex_Ij.get(j, i));\n\n      // multiplication\n      CHECK(complex_tensor_times_real_number.get(i, j) ==\n            complex_iJ.get(i, j) * real_number);\n      CHECK(real_number_times_complex_tensor.get(i, j) ==\n            real_number * complex_Ij.get(j, i));\n      CHECK(real_tensor_times_complex_number.get(i, j) ==\n            real_iJ.get(i, j) * complex_number);\n      CHECK(complex_number_times_real_tensor.get(i, j) ==\n            complex_number * real_iJ.get(i, j));\n      CHECK_ITERABLE_APPROX(complex_tensor_times_real_tensor.get(i, j),\n                            expected_sum_complex_tensor_times_real_tensor);\n      CHECK_ITERABLE_APPROX(real_tensor_times_complex_tensor.get(i, j),\n                            expected_sum_real_tensor_times_complex_tensor);\n\n      // division\n      CHECK_ITERABLE_APPROX(complex_tensor_over_real_number.get(i, j),\n                            complex_iJ.get(i, j) / real_number);\n      CHECK(complex_tensor_over_real_tensor.get(i, j) ==\n            complex_iJ.get(i, j) / get(real_scalar));\n      CHECK(real_tensor_over_complex_tensor.get(i, j) ==\n            real_Ij.get(j, i) / get(complex_scalar));\n    }\n  }\n}\n\n// \\brief Test evaluation of large RHS `TensorExpression`s\n//\n// \\details\n// Test cases include large expressions with:\n// - only real-valued `Tensor`s\n// - only complex-valued `Tensor`s\n// - real-valued and complex-valued `Tensor`s\n// - real-valued `Tensor`s and a real-valued number\n// - complex-valued `Tensor`s and a complex-valued number (see note below)\n// - real-valued `Tensor`s and a complex-valued number\n// - complex-valued `Tensor`s and a real-valued number\n//\n// Note: Binary operations between a complex number and a\n// `Tensor<DataVector, ...>` are only tested for multiplication. This is because\n// for `std::complex<double> OP DataVector`, Blaze currently only supports\n// multiplication.\n//\n// \\tparam ComplexDataType the data type of the complex-valued operand\n// \\tparam RhsDataType the data type of the real-valued operand\ntemplate <typename Generator, typename ComplexDataType, typename RealDataType>\nvoid test_evaluate_large_expressions(\n    const gsl::not_null<Generator*> generator,\n    const ComplexDataType& used_for_size_complex,\n    const RealDataType& used_for_size_real,\n    const double used_for_random_real_number,\n    const std::complex<double> used_for_random_complex_number) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  // Operands for test expressions\n\n  const auto real_scalar = make_with_random_values<Scalar<RealDataType>>(\n      generator, distribution, used_for_size_real);\n  const auto complex_scalar = make_with_random_values<Scalar<ComplexDataType>>(\n      generator, distribution, used_for_size_complex);\n  const auto real_number = make_with_random_values<double>(\n      generator, distribution, used_for_random_real_number);\n  const auto complex_number = make_with_random_values<std::complex<double>>(\n      generator, distribution, used_for_random_complex_number);\n\n  // Tested expressions\n\n  const auto real_scalar_times_8 =\n      real_scalar() + real_scalar() + real_scalar() + real_scalar() +\n      real_scalar() + real_scalar() + real_scalar() + real_scalar();\n  const auto real_scalar_times_64 = real_scalar_times_8 + real_scalar_times_8 +\n                                    real_scalar_times_8 + real_scalar_times_8 +\n                                    real_scalar_times_8 + real_scalar_times_8 +\n                                    real_scalar_times_8 + real_scalar_times_8;\n\n  const auto complex_scalar_times_8 = complex_scalar() + complex_scalar() +\n                                      complex_scalar() + complex_scalar() +\n                                      complex_scalar() + complex_scalar() +\n                                      complex_scalar() + complex_scalar();\n  const auto complex_scalar_times_64 =\n      complex_scalar_times_8 + complex_scalar_times_8 + complex_scalar_times_8 +\n      complex_scalar_times_8 + complex_scalar_times_8 + complex_scalar_times_8 +\n      complex_scalar_times_8 + complex_scalar_times_8;\n\n  // large expressions of `Tensor`s\n  const Scalar<RealDataType> real_tensor_times_real_tensor_result =\n      tenex::evaluate(real_scalar_times_64);\n  const Scalar<ComplexDataType> complex_tensor_times_complex_tensor_result =\n      tenex::evaluate(complex_scalar_times_64);\n  const Scalar<ComplexDataType> complex_tensor_times_real_tensor_result =\n      tenex::evaluate(complex_scalar_times_64 * real_scalar_times_64);\n  const Scalar<ComplexDataType> real_tensor_times_complex_tensor_result =\n      tenex::evaluate(real_scalar_times_64 * complex_scalar_times_64);\n\n  // large expressions of `Tensor`s and a number\n  const Scalar<RealDataType> real_tensor_times_real_number_result =\n      tenex::evaluate(real_scalar_times_64 * real_number);\n  const Scalar<RealDataType> real_number_times_real_tensor_result =\n      tenex::evaluate(real_number * real_scalar_times_64);\n  const Scalar<ComplexDataType> complex_tensor_times_real_number_result =\n      tenex::evaluate(complex_scalar_times_64 * real_number);\n  const Scalar<ComplexDataType> real_number_times_complex_tensor_result =\n      tenex::evaluate(real_number * complex_scalar_times_64);\n  const Scalar<ComplexDataType> real_tensor_times_complex_number_result =\n      tenex::evaluate(real_scalar_times_64 * complex_number);\n  const Scalar<ComplexDataType> complex_number_times_real_tensor_result =\n      tenex::evaluate(complex_number * real_scalar_times_64);\n  const Scalar<ComplexDataType> complex_tensor_times_complex_number_result =\n      tenex::evaluate(complex_scalar_times_64 * complex_number);\n  const Scalar<ComplexDataType> complex_number_times_complex_tensor_result =\n      tenex::evaluate(complex_number * complex_scalar_times_64);\n\n  // check expressions with only `Tensor`s\n  CHECK_ITERABLE_APPROX(get(real_tensor_times_real_tensor_result),\n                        64.0 * get(real_scalar));\n  CHECK_ITERABLE_APPROX(get(complex_tensor_times_complex_tensor_result),\n                        64.0 * get(complex_scalar));\n  CHECK_ITERABLE_APPROX(get(complex_tensor_times_real_tensor_result),\n                        64.0 * 64.0 * (get(complex_scalar) * get(real_scalar)));\n  CHECK_ITERABLE_APPROX(get(real_tensor_times_complex_tensor_result),\n                        64.0 * 64.0 * (get(complex_scalar) * get(real_scalar)));\n\n  // check expressions with `Tensor`s and numbers\n  CHECK_ITERABLE_APPROX(get(real_tensor_times_real_number_result),\n                        64.0 * get(real_scalar) * real_number);\n  CHECK_ITERABLE_APPROX(get(real_number_times_real_tensor_result),\n                        64.0 * get(real_scalar) * real_number);\n  CHECK_ITERABLE_APPROX(get(complex_tensor_times_real_number_result),\n                        64.0 * get(complex_scalar) * real_number);\n  CHECK_ITERABLE_APPROX(get(real_number_times_complex_tensor_result),\n                        64.0 * get(complex_scalar) * real_number);\n  CHECK_ITERABLE_APPROX(get(real_tensor_times_complex_number_result),\n                        64.0 * get(real_scalar) * complex_number);\n  CHECK_ITERABLE_APPROX(get(complex_number_times_real_tensor_result),\n                        64.0 * get(real_scalar) * complex_number);\n  CHECK_ITERABLE_APPROX(get(complex_tensor_times_complex_number_result),\n                        64.0 * get(complex_scalar) * complex_number);\n  CHECK_ITERABLE_APPROX(get(complex_number_times_complex_tensor_result),\n                        64.0 * get(complex_scalar) * complex_number);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.EvaluateComplex\",\n                  \"[Unit][DataStructures]\") {\n  MAKE_GENERATOR(generator);\n\n  const size_t vector_size = 2;\n\n  const double used_for_size_real_double =\n      std::numeric_limits<double>::signaling_NaN();\n  const std::complex<double> used_for_size_complex_double =\n      std::complex<double>(std::numeric_limits<double>::signaling_NaN(),\n                           std::numeric_limits<double>::signaling_NaN());\n  const DataVector used_for_size_real_datavector =\n      DataVector(vector_size, std::numeric_limits<double>::signaling_NaN());\n  const ComplexDataVector used_for_size_complex_datavector = ComplexDataVector(\n      vector_size, std::numeric_limits<double>::signaling_NaN());\n\n  // Test assignment of complex-valued LHS `Tensor` to single RHS term\n  test_assignment_to_single_term(make_not_null(&generator),\n                                 used_for_size_complex_double,\n                                 used_for_size_real_double);\n  test_assignment_to_single_term(make_not_null(&generator),\n                                 used_for_size_complex_double,\n                                 used_for_size_complex_double);\n  test_assignment_to_single_term(make_not_null(&generator),\n                                 used_for_size_complex_datavector,\n                                 used_for_size_real_double);\n  test_assignment_to_single_term(make_not_null(&generator),\n                                 used_for_size_complex_datavector,\n                                 used_for_size_real_datavector);\n  test_assignment_to_single_term(make_not_null(&generator),\n                                 used_for_size_complex_datavector,\n                                 used_for_size_complex_datavector);\n\n  // Test assignment of a complex-valued LHS `Tensor` to a RHS expression\n  // containing mathematical operations\n  test_evaluate_ops(make_not_null(&generator), used_for_size_complex_double,\n                    used_for_size_real_double);\n  test_evaluate_ops(make_not_null(&generator), used_for_size_complex_double,\n                    used_for_size_complex_double);\n  test_evaluate_ops(make_not_null(&generator), used_for_size_complex_datavector,\n                    used_for_size_real_double);\n  test_evaluate_ops(make_not_null(&generator), used_for_size_complex_datavector,\n                    used_for_size_real_datavector);\n  test_evaluate_ops(make_not_null(&generator), used_for_size_complex_datavector,\n                    used_for_size_complex_datavector);\n\n  // Test evaluation of RHS binary operations between real-valued and\n  // complex-valued terms\n  test_bin_ops_with_real_and_complex(\n      make_not_null(&generator), used_for_size_complex_double,\n      used_for_size_real_double, used_for_size_real_double,\n      used_for_size_complex_double);\n  test_bin_ops_with_real_and_complex(\n      make_not_null(&generator), used_for_size_complex_datavector,\n      used_for_size_real_datavector, used_for_size_real_double,\n      used_for_size_complex_double);\n\n  // Test evaluation of large RHS `TensorExpression`s\n  test_evaluate_large_expressions(\n      make_not_null(&generator), used_for_size_complex_double,\n      used_for_size_real_double, used_for_size_real_double,\n      used_for_size_complex_double);\n  test_evaluate_large_expressions(\n      make_not_null(&generator), used_for_size_complex_datavector,\n      used_for_size_real_datavector, used_for_size_real_double,\n      used_for_size_complex_double);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_EvaluateRank3NonSymmetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Rank 3 test cases for tenex::evaluate are split into this file and\n// Test_EvaluateRank3Symmetric.cpp in order to reduce compile time memory usage\n// per cpp file.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank3.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <IndexType... Is>\nusing indextype_list = tmpl::integral_list<IndexType, Is...>;\n\nconst IndexType spatial_index = IndexType::Spatial;\nconst IndexType spacetime_index = IndexType::Spacetime;\n\n// \\brief Test evaluation of rank 3 tensors with no symmetry\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_evaluate_rank_3() {\n  // spacetime, spacetime, spatial\n  TestHelpers::tenex::test_evaluate_rank_3<\n      true, DataType, Symmetry<3, 2, 1>,\n      indextype_list<spacetime_index, spacetime_index, spatial_index>, ti::d,\n      ti::A, ti::i, Frame::Inertial>();\n\n  // spatial, spacetime, spatial\n  TestHelpers::tenex::test_evaluate_rank_3<\n      true, DataType, Symmetry<3, 2, 1>,\n      indextype_list<spatial_index, spacetime_index, spatial_index>, ti::K,\n      ti::f, ti::m, Frame::Grid>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.DataStructures.Tensor.Expression.EvaluateRank3NonSymmetric\",\n    \"[DataStructures][Unit]\") {\n  test_evaluate_rank_3<double>();\n  test_evaluate_rank_3<DataVector>();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_EvaluateRank3Symmetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Rank 3 test cases for tenex::evaluate are split into this file and\n// Test_EvaluateRank3NonSymmetric.cpp in order to reduce compile time memory\n// usage per cpp file.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank3.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <IndexType... Is>\nusing indextype_list = tmpl::integral_list<IndexType, Is...>;\n\nconst IndexType spatial_index = IndexType::Spatial;\nconst IndexType spacetime_index = IndexType::Spacetime;\n\n// \\brief Test evaluation of rank 3 tensors with symmetry\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_evaluate_rank_3() {\n  // first and second indices symmetric\n  TestHelpers::tenex::test_evaluate_rank_3<\n      true, DataType, Symmetry<2, 2, 1>,\n      indextype_list<spacetime_index, spacetime_index, spacetime_index>, ti::b,\n      ti::a, ti::C, Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_3<\n      false, DataType, Symmetry<2, 2, 1>,\n      indextype_list<spatial_index, spatial_index, spacetime_index>, ti::L,\n      ti::J, ti::I, Frame::Grid, Symmetry<3, 2, 1>>();\n\n  // first and third indices symmetric\n  TestHelpers::tenex::test_evaluate_rank_3<\n      true, DataType, Symmetry<1, 2, 1>,\n      indextype_list<spatial_index, spatial_index, spatial_index>, ti::i, ti::k,\n      ti::j, Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_3<\n      false, DataType, Symmetry<1, 2, 1>,\n      indextype_list<spacetime_index, spatial_index, spacetime_index>, ti::f,\n      ti::M, ti::c, Frame::Grid, Symmetry<3, 2, 1>>();\n\n  // second and third indices symmetric\n  TestHelpers::tenex::test_evaluate_rank_3<\n      true, DataType, Symmetry<2, 1, 1>,\n      indextype_list<spacetime_index, spatial_index, spatial_index>, ti::c,\n      ti::I, ti::K, Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_3<\n      false, DataType, Symmetry<2, 1, 1>,\n      indextype_list<spatial_index, spacetime_index, spacetime_index>, ti::J,\n      ti::b, ti::c, Frame::Grid, Symmetry<3, 2, 1>>();\n\n  // fully symmetric\n  TestHelpers::tenex::test_evaluate_rank_3<\n      true, DataType, Symmetry<1, 1, 1>,\n      indextype_list<spacetime_index, spacetime_index, spacetime_index>, ti::f,\n      ti::d, ti::a, Frame::Inertial>();\n  TestHelpers::tenex::test_evaluate_rank_3<\n      false, DataType, Symmetry<1, 1, 1>,\n      indextype_list<spatial_index, spatial_index, spatial_index>, ti::k, ti::l,\n      ti::i, Frame::Grid, Symmetry<2, 2, 1>>();\n  TestHelpers::tenex::test_evaluate_rank_3<\n      false, DataType, Symmetry<1, 1, 1>,\n      indextype_list<spacetime_index, spacetime_index, spacetime_index>, ti::g,\n      ti::b, ti::c, Frame::Inertial, Symmetry<1, 2, 1>>();\n  TestHelpers::tenex::test_evaluate_rank_3<\n      false, DataType, Symmetry<1, 1, 1>,\n      indextype_list<spatial_index, spatial_index, spatial_index>, ti::M, ti::N,\n      ti::J, Frame::Grid, Symmetry<2, 1, 1>>();\n  TestHelpers::tenex::test_evaluate_rank_3<\n      false, DataType, Symmetry<1, 1, 1>,\n      indextype_list<spacetime_index, spacetime_index, spacetime_index>, ti::E,\n      ti::A, ti::B, Frame::Distorted, Symmetry<3, 2, 1>>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.DataStructures.Tensor.Expression.EvaluateRank3Symmetric\",\n    \"[DataStructures][Unit]\") {\n  test_evaluate_rank_3<double>();\n  test_evaluate_rank_3<DataVector>();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_EvaluateRank4.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank4.hpp\"\n\nnamespace {\n// \\brief Test evaluation of rank 4 tensors\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_evaluate_rank_4() {\n  // nonsymmetric\n  TestHelpers::tenex::test_evaluate_rank_4<\n      true, DataType, Symmetry<4, 3, 2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<1, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::b, ti::A, ti::k, ti::l>();\n\n  // second and third indices symmetric\n  TestHelpers::tenex::test_evaluate_rank_4<\n      true, DataType, Symmetry<3, 2, 2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<1, UpLo::Lo, Frame::Grid>>,\n      ti::G, ti::d, ti::a, ti::j>();\n\n  // first, second, and fourth indices symmetric\n  TestHelpers::tenex::test_evaluate_rank_4<\n      true, DataType, Symmetry<2, 2, 1, 2>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::j, ti::i, ti::k, ti::l>();\n\n  // fully symmetric\n  TestHelpers::tenex::test_evaluate_rank_4<\n      true, DataType, Symmetry<1, 1, 1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::F, ti::A, ti::C, ti::D>();\n\n  // different LHS symmetry\n  TestHelpers::tenex::test_evaluate_rank_4<\n      false, DataType, Symmetry<2, 1, 2, 2>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::j, ti::i, ti::k, ti::l, Symmetry<3, 2, 1, 1>>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.EvaluateRank4\",\n                  \"[DataStructures][Unit]\") {\n  test_evaluate_rank_4<double>();\n  test_evaluate_rank_4<DataVector>();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_EvaluateSpatialAndTimeSpacetimeIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank2.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank4.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n// \\brief Test evaluation of tensors where generic spatial indices and/or\n// concrete time indices are used for RHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename Generator, typename DataType>\nvoid test_rhs(const gsl::not_null<Generator*> generator,\n              const DataType& used_for_size) {\n  // Note: this function doesn't utilize test helper functions like\n  // test_evaluate() because they aren't generic enough to handle\n  // test cases where the number of indices on the RHS and LHS are not equal.\n  // Instead, we have to manually check each test case of interest.\n\n  const std::uniform_real_distribution<> distribution(0.1, 1.0);\n  constexpr size_t dim = 3;\n  using frame = Frame::Inertial;\n\n  // RHS tensors with non-symmetric spacetime indices\n  const auto R_ab = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_AB = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_Ab = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_aB = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n\n  // RHS tensors with symmetric spacetime indices\n  const auto S_ab = make_with_random_values<\n      Tensor<DataType, Symmetry<1, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto S_AB = make_with_random_values<\n      Tensor<DataType, Symmetry<1, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n\n  // Evaluations of non-symmetric RHS tensors\n\n  // \\f$L_{i} = R_{it}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_R_it = tenex::evaluate<ti::i>(R_ab(ti::i, ti::t));\n  // \\f$L_{i} = R_{ti}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_R_ti = tenex::evaluate<ti::i>(R_ab(ti::t, ti::i));\n  // \\f$L^{i} = R^{it}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_R_IT = tenex::evaluate<ti::I>(R_AB(ti::I, ti::T));\n  // \\f$L^{i} = R^{ti}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_R_TI = tenex::evaluate<ti::I>(R_AB(ti::T, ti::I));\n  // \\f$L^{i} = R^{i}{}_{t}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_R_It = tenex::evaluate<ti::I>(R_Ab(ti::I, ti::t));\n  // \\f$L_{i} = R^{t}{}_{i}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_R_Ti = tenex::evaluate<ti::i>(R_Ab(ti::T, ti::i));\n  // \\f$L_{i} = R_{i}{}^{t}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_R_iT = tenex::evaluate<ti::i>(R_aB(ti::i, ti::T));\n  // \\f$L^{i} = R_{t}{}^{i}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_R_tI = tenex::evaluate<ti::I>(R_aB(ti::t, ti::I));\n\n  // Evaluations of symmetric RHS tensors\n\n  // \\f$L_{i} = S_{it}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_S_it = tenex::evaluate<ti::i>(S_ab(ti::i, ti::t));\n  // \\f$L_{i} = S_{ti}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_S_ti = tenex::evaluate<ti::i>(S_ab(ti::t, ti::i));\n  // \\f$L^{i} = S^{it}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_S_IT = tenex::evaluate<ti::I>(S_AB(ti::I, ti::T));\n  // \\f$L^{i} = S^{ti}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_S_TI = tenex::evaluate<ti::I>(S_AB(ti::T, ti::I));\n\n  for (size_t i = 0; i < dim; i++) {\n    CHECK(L_i_from_R_it.get(i) == R_ab.get(i + 1, 0));\n    CHECK(L_i_from_R_ti.get(i) == R_ab.get(0, i + 1));\n    CHECK(L_I_from_R_IT.get(i) == R_AB.get(i + 1, 0));\n    CHECK(L_I_from_R_TI.get(i) == R_AB.get(0, i + 1));\n    CHECK(L_I_from_R_It.get(i) == R_Ab.get(i + 1, 0));\n    CHECK(L_i_from_R_Ti.get(i) == R_Ab.get(0, i + 1));\n    CHECK(L_i_from_R_iT.get(i) == R_aB.get(i + 1, 0));\n    CHECK(L_I_from_R_tI.get(i) == R_aB.get(0, i + 1));\n\n    CHECK(L_i_from_S_it.get(i) == S_ab.get(i + 1, 0));\n    CHECK(L_i_from_S_ti.get(i) == S_ab.get(0, i + 1));\n    CHECK(L_I_from_S_IT.get(i) == S_AB.get(i + 1, 0));\n    CHECK(L_I_from_S_TI.get(i) == S_AB.get(0, i + 1));\n  }\n}\n\n// \\brief Test evaluation of tensors where generic spatial indices and/or\n// concrete time indices are used for LHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename Generator, typename DataType>\nvoid test_lhs(const gsl::not_null<Generator*> generator,\n              const DataType& used_for_size) {\n  // Note: this function doesn't utilize test helper functions like\n  // test_evaluate() because they aren't generic enough to handle\n  // test cases where the number of indices on the RHS and LHS are not equal.\n  // Instead, we have to manually check each test case of interest.\n\n  const std::uniform_real_distribution<> distribution(0.1, 1.0);\n  constexpr size_t dim = 3;\n  using frame = Frame::Inertial;\n\n  const auto R =\n      make_with_random_values<DataType>(generator, distribution, used_for_size);\n\n  if constexpr (not is_derived_of_vector_impl_v<DataType>) {\n    // Test evaluation of RHS scalar to non-symmetric LHS rank 2\n\n    // \\f$L_{it} = R\\f$\n    auto L_it_from_R = make_with_value<\n        Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                          SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::i, ti::t>(make_not_null(&L_it_from_R), R);\n    // \\f$L_{ti} = R\\f$\n    auto L_ti_from_R = make_with_value<\n        Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                          SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::t, ti::i>(make_not_null(&L_ti_from_R), R);\n    // \\f$L^{it} = R\\f$\n    auto L_IT_from_R = make_with_value<\n        Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                          SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::I, ti::T>(make_not_null(&L_IT_from_R), R);\n    // \\f$L^{ti} = R\\f$\n    auto L_TI_from_R = make_with_value<\n        Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                          SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::T, ti::I>(make_not_null(&L_TI_from_R), R);\n    // \\f$L^{i}{}_{t} = R\\f$\n    auto L_It_from_R = make_with_value<\n        Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                          SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::I, ti::t>(make_not_null(&L_It_from_R), R);\n    // \\f$L^{t}{}_{i} = R\\f$\n    auto L_Ti_from_R = make_with_value<\n        Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                          SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::T, ti::i>(make_not_null(&L_Ti_from_R), R);\n    // \\f$L_{i}{}^{t} = R\\f$\n    auto L_iT_from_R = make_with_value<\n        Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                          SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::i, ti::T>(make_not_null(&L_iT_from_R), R);\n    // \\f$L_{t}{}^{i} = R\\f$\n    auto L_tI_from_R = make_with_value<\n        Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                          SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::t, ti::I>(make_not_null(&L_tI_from_R), R);\n\n    for (size_t a = 0; a < dim + 1; a++) {\n      for (size_t b = 0; b < dim + 1; b++) {\n        const auto expected_value =\n            (a > 0 and b == 0)\n                ? R\n                : TestHelpers::tenex::component_placeholder_value<\n                      DataType>::value;\n        CHECK(L_it_from_R.get(a, b) == expected_value);\n        CHECK(L_ti_from_R.get(b, a) == expected_value);\n        CHECK(L_IT_from_R.get(a, b) == expected_value);\n        CHECK(L_TI_from_R.get(b, a) == expected_value);\n        CHECK(L_It_from_R.get(a, b) == expected_value);\n        CHECK(L_Ti_from_R.get(b, a) == expected_value);\n        CHECK(L_iT_from_R.get(a, b) == expected_value);\n        CHECK(L_tI_from_R.get(b, a) == expected_value);\n      }\n    }\n\n    // Test evaluation of RHS scalar to symmetric LHS rank 2\n\n    // \\f$M_{it} = R\\f$\n    auto M_it_from_R = make_with_value<\n        Tensor<DataType, Symmetry<1, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                          SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::i, ti::t>(make_not_null(&M_it_from_R), R);\n    // \\f$M_{ti} = R\\f$\n    auto M_ti_from_R = make_with_value<\n        Tensor<DataType, Symmetry<1, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                          SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::t, ti::i>(make_not_null(&M_ti_from_R), R);\n    // \\f$M^{it} = R\\f$\n    auto M_IT_from_R = make_with_value<\n        Tensor<DataType, Symmetry<1, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                          SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::I, ti::T>(make_not_null(&M_IT_from_R), R);\n    // \\f$M^{ti} = R\\f$\n    auto M_TI_from_R = make_with_value<\n        Tensor<DataType, Symmetry<1, 1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                          SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n        used_for_size,\n        TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    tenex::evaluate<ti::T, ti::I>(make_not_null(&M_TI_from_R), R);\n\n    for (size_t a = 0; a < dim + 1; a++) {\n      for (size_t b = a; b < dim + 1; b++) {\n        const auto expected_value =\n            (a == 0 and b > 0)\n                ? R\n                : TestHelpers::tenex::component_placeholder_value<\n                      DataType>::value;\n        CHECK(M_it_from_R.get(a, b) == expected_value);\n        CHECK(M_ti_from_R.get(b, a) == expected_value);\n        CHECK(M_IT_from_R.get(a, b) == expected_value);\n        CHECK(M_TI_from_R.get(b, a) == expected_value);\n      }\n    }\n  }\n\n  // RHS Rank 1\n\n  const auto R_i = make_with_random_values<tnsr::i<DataType, dim, frame>>(\n      generator, distribution, used_for_size);\n  const auto R_I = make_with_random_values<tnsr::I<DataType, dim, frame>>(\n      generator, distribution, used_for_size);\n\n  // Test evaluation of RHS rank 1 to non-symmetric LHS rank 2\n\n  // \\f$L_{it} = R_i\\f$\n  auto L_it_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::i, ti::t>(make_not_null(&L_it_from_R_i), R_i(ti::i));\n  // \\f$L_{ti} = R_i\\f$\n  auto L_ti_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::i>(make_not_null(&L_ti_from_R_i), R_i(ti::i));\n  // \\f$L^{it} = R^i\\f$\n  auto L_IT_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::I, ti::T>(make_not_null(&L_IT_from_R_I), R_I(ti::I));\n  // \\f$L^{ti} = R^i\\f$\n  auto L_TI_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::I>(make_not_null(&L_TI_from_R_I), R_I(ti::I));\n  // \\f$L^{i}{}_{t} = R^i\\f$\n  auto L_It_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::I, ti::t>(make_not_null(&L_It_from_R_I), R_I(ti::I));\n  // \\f$L^{t}{}_{i} = R_i\\f$\n  auto L_Ti_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::i>(make_not_null(&L_Ti_from_R_i), R_i(ti::i));\n  // \\f$L_{i}{}^{t} = R_i\\f$\n  auto L_iT_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::i, ti::T>(make_not_null(&L_iT_from_R_i), R_i(ti::i));\n  // \\f$L_{t}{}^{i} = R^i\\f$\n  auto L_tI_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::I>(make_not_null(&L_tI_from_R_I), R_I(ti::I));\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    for (size_t b = 0; b < dim + 1; b++) {\n      if (a > 0 and b == 0) {\n        CHECK(L_it_from_R_i.get(a, b) == R_i.get(a - 1));\n        CHECK(L_ti_from_R_i.get(b, a) == R_i.get(a - 1));\n        CHECK(L_IT_from_R_I.get(a, b) == R_I.get(a - 1));\n        CHECK(L_TI_from_R_I.get(b, a) == R_I.get(a - 1));\n        CHECK(L_It_from_R_I.get(a, b) == R_I.get(a - 1));\n        CHECK(L_Ti_from_R_i.get(b, a) == R_i.get(a - 1));\n        CHECK(L_iT_from_R_i.get(a, b) == R_i.get(a - 1));\n        CHECK(L_tI_from_R_I.get(b, a) == R_I.get(a - 1));\n      } else {\n        CHECK(L_it_from_R_i.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_ti_from_R_i.get(b, a) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_IT_from_R_I.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_TI_from_R_I.get(b, a) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_It_from_R_I.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_Ti_from_R_i.get(b, a) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_iT_from_R_i.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_tI_from_R_I.get(b, a) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      }\n    }\n  }\n\n  // Test evaluation of RHS rank 1 to symmetric LHS rank 2\n\n  // \\f$M_{it} = R_i\\f$\n  auto M_it_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::i, ti::t>(make_not_null(&M_it_from_R_i), R_i(ti::i));\n  // \\f$M_{ti} = R_i\\f$\n  auto M_ti_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::i>(make_not_null(&M_ti_from_R_i), R_i(ti::i));\n  // \\f$M^{it} = R^i\\f$\n  auto M_IT_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::I, ti::T>(make_not_null(&M_IT_from_R_I), R_I(ti::I));\n  // \\f$M^{ti} = R^i\\f$\n  auto M_TI_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::I>(make_not_null(&M_TI_from_R_I), R_I(ti::I));\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    for (size_t b = a; b < dim + 1; b++) {\n      if (a == 0 and b > 0) {\n        CHECK(M_it_from_R_i.get(a, b) == R_i.get(b - 1));\n        CHECK(M_ti_from_R_i.get(b, a) == R_i.get(b - 1));\n        CHECK(M_IT_from_R_I.get(a, b) == R_I.get(b - 1));\n        CHECK(M_TI_from_R_I.get(b, a) == R_I.get(b - 1));\n      } else {\n        CHECK(M_it_from_R_i.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(M_ti_from_R_i.get(b, a) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(M_IT_from_R_I.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(M_TI_from_R_I.get(b, a) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      }\n    }\n  }\n}\n\n// \\brief Test evaluation of rank 2 tensors where generic spatial indices and/or\n// concrete time indices are used for RHS and LHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_rhs_and_lhs_rank_2() {\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::t>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::t, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::t, ti::i>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::t, ti::i, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::I, ti::T>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::I, ti::T, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::T, ti::I>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::T, ti::I, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::I, ti::t>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::T, ti::i>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::i, ti::T>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::t, ti::I>();\n}\n\n// \\brief Test evaluation of rank 4 tensors where generic spatial indices and/or\n// concrete time indices are used for RHS and LHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_rhs_and_lhs_rank_4() {\n  using frame = Frame::Inertial;\n  using index_list_abcd = index_list<\n      SpacetimeIndex<3, UpLo::Lo, frame>, SpacetimeIndex<3, UpLo::Lo, frame>,\n      SpacetimeIndex<3, UpLo::Lo, frame>, SpacetimeIndex<3, UpLo::Lo, frame>>;\n\n  TestHelpers::tenex::test_evaluate_rank_4<\n      false, DataType, Symmetry<1, 2, 1, 1>, index_list_abcd, ti::t, ti::a,\n      ti::j, ti::i, Symmetry<1, 3, 2, 1>>();\n}\n\n// \\brief Test evaluation of tensors where concrete time indices and spatial\n// indices are used for spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_evaluate_spatial_and_time_spacetime_index(\n    const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n\n  test_rhs(make_not_null(&generator), used_for_size);\n  test_lhs(make_not_null(&generator), used_for_size);\n  test_rhs_and_lhs_rank_2<DataType>();\n  test_rhs_and_lhs_rank_4<DataType>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.DataStructures.Tensor.Expression.\"\n    \"EvaluateSpatialAndTimeSpacetimeIndex\",\n    \"[DataStructures][Unit]\") {\n  test_evaluate_spatial_and_time_spacetime_index(\n      std::numeric_limits<double>::signaling_NaN());\n  test_evaluate_spatial_and_time_spacetime_index(\n      DataVector(3, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_EvaluateSpatialSpacetimeIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank2.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank4.hpp\"\n\nnamespace {\n// \\brief Test evaluation of tensors where generic spatial indices are used for\n// RHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_rhs() {\n  // test RHS Symmetry<2, 1>\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::a, ti::i, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::i, ti::a, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::A, ti::i, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::a, ti::I, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Up, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::I, ti::a, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::i, ti::A, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::A, ti::I, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Up, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::I, ti::A, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::j, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::j, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::j, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Grid>>,\n      ti::J, ti::k, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Grid>>,\n      ti::J, ti::k, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Grid>>,\n      ti::J, ti::k, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::l, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::l, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::l, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::K, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Up, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::K, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Up, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::K, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Up, Frame::Grid>>>();\n\n  // test RHS Symmetry<1, 1> to LHS Symmetry<1, 1> and <2, 1>\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::a, ti::i, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::a, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::A, ti::I, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Up, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::I, ti::A, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Distorted>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Distorted>>,\n      ti::J, ti::I, Symmetry<1, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Distorted>,\n                 SpatialIndex<3, UpLo::Up, Frame::Distorted>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Distorted>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Distorted>>,\n      ti::J, ti::I, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Distorted>,\n                 SpatialIndex<3, UpLo::Up, Frame::Distorted>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      true, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::j, ti::k, Symmetry<1, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::j, ti::k, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>>();\n}\n\n// \\brief Test evaluation of tensors where generic spatial indices are used for\n// LHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_lhs() {\n  // test RHS Symmetry<2, 1>\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::a, ti::i, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::A, ti::i, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::a, ti::I, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::A, ti::I, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::a, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::I, ti::a, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::i, ti::A, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>,\n      ti::I, ti::A, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::j, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::i, ti::j, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::i, ti::j, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::J, ti::k, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::J, ti::k, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::J, ti::k, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::l, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::K, ti::I, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::K, ti::I, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Up, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::K, ti::I, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>();\n\n  // test RHS Symmetry<1, 1> to LHS Symmetry<1, 1> and <2, 1>\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::K, ti::J, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::K, ti::J, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::K, ti::J, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::K, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::l, ti::k, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::l, ti::k, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::l, ti::k, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::l, ti::k, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>();\n}\n\n// \\brief Test evaluation of rank 2 tensors where generic spatial indices are\n// used for RHS and LHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_rhs_and_lhs_rank2() {\n  // test RHS Symmetry<2, 1>\n\n  // - two lower spatial tensor indices\n  // - RHS one or two spacetime indices\n  // - LHS two spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::j, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::n, ti::m, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::m, ti::i, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  // - upper spatial tensor index, lower spatial tensor index\n  // - RHS two spacetime indices\n  // - LHS one or two spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::I, ti::j, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::K, ti::i, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::N, ti::k, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>();\n\n  // - lower spatial tensor indices\n  // - RHS spatial index, spacetime index\n  // - LHS spatial index, spacetime index\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::j, ti::k>();\n\n  // - upper spatial tensor indices\n  // - RHS spacetime index, spatial index\n  // - LHS spatial index, spacetime index\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::L, ti::J, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>>();\n\n  // - lower spatial tensor index, upper spatial tensor index\n  // - RHS spatial index, spacetime index\n  // - LHS spacetime index, spatial index\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::i, ti::M, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>>();\n\n  // - lower spatial tensor indices\n  // - RHS spacetime index, spatial index\n  // - LHS spatial index, spacetime index\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::k, ti::j>();\n\n  // - upper spatial tensor indices\n  // - RHS spatial index, spacetime index\n  // - LHS spacetime index, spatial index\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::L, ti::J, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Up, Frame::Inertial>>>();\n\n  // - lower spacetime tensor index, lower spatial tensor index\n  // - RHS spacetime indices\n  // - LHS spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::a, ti::i>();\n\n  // - lower spatial tensor index, lower spacetime tensor index\n  // - RHS spacetime indices\n  // - LHS spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::a>();\n\n  // - upper spacetime tensor index, upper spatial tensor index\n  // - RHS spacetime indices\n  // - LHS spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::A, ti::I>();\n\n  // - upper spatial tensor index, upper spacetime tensor index\n  // - RHS spacetime indices\n  // - LHS spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::I, ti::A>();\n\n  // - lower spatial tensor index, upper spacetime tensor index\n  // - RHS spacetime indices\n  // - LHS spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::i, ti::A>();\n\n  // - lower spacetime tensor index, upper spatial tensor index\n  // - RHS spacetime indices\n  // - LHS spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::a, ti::I>();\n\n  // - upper spatial tensor index, lower spacetime tensor index\n  // - RHS spacetime indices\n  // - LHS spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::I, ti::a>();\n\n  // - upper spacetime tensor index, lower spatial tensor index\n  // - RHS spacetime indices\n  // - LHS spacetime indices\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::A, ti::i>();\n\n  // test RHS Symmetry<1, 1> to LHS Symmetry<1, 1> and <2, 1>\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::a, ti::i>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::a, ti::i, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::A, ti::I>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::A, ti::I, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::a>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::a, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::I, ti::A>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::I, ti::A, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::j>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::j, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::J, ti::I>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::J, ti::I, Symmetry<2, 1>>();\n}\n\n// \\brief Test evaluation of rank 4 tensors where generic spatial indices are\n// used for RHS and LHS spacetime indices\ntemplate <typename DataType>\nvoid test_rhs_and_lhs_rank4() {\n  // tests that return type is what is expected\n  TestHelpers::tenex::test_evaluate_rank_4<\n      true, DataType, Symmetry<1, 2, 2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>,\n      ti::i, ti::k, ti::a, ti::j, Symmetry<1, 3, 2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Grid>>>();\n\n  // tests that only spatial components are filled for LHS tensor arg\n  TestHelpers::tenex::test_evaluate_rank_4<\n      false, DataType, Symmetry<1, 1, 2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::m, ti::c, ti::i, ti::j, Symmetry<1, 1, 2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>();\n}\n\n// \\brief Test evaluation of tensors where generic spatial indices are used for\n// spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_evaluate_spatial_spacetime_index() {\n  test_rhs<DataType>();\n  test_lhs<DataType>();\n  test_rhs_and_lhs_rank2<DataType>();\n  test_rhs_and_lhs_rank4<DataType>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.DataStructures.Tensor.Expression.EvaluateSpatialSpacetimeIndex\",\n    \"[DataStructures][Unit]\") {\n  test_evaluate_spatial_spacetime_index<double>();\n  test_evaluate_spatial_spacetime_index<DataVector>();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_EvaluateTimeIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank2.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/EvaluateRank4.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n// \\brief Test evaluation of tensors where concrete time indices are used for\n// RHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename Generator, typename DataType>\nvoid test_rhs(const gsl::not_null<Generator*> generator,\n              const DataType& used_for_size) {\n  // Note: this function doesn't utilize test helper functions like\n  // test_evaluate() because they aren't generic enough to handle\n  // test cases where the number of indices on the RHS and LHS are not equal.\n  // Instead, we have to manually check each test case of interest.\n\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n  constexpr size_t dim = 3;\n  using frame = Frame::Inertial;\n\n  // Rank 1 testing\n\n  const auto R_a = make_with_random_values<Tensor<\n      DataType, Symmetry<1>, index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_A = make_with_random_values<Tensor<\n      DataType, Symmetry<1>, index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L = R_{t}\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const Scalar<DataType> L_from_R_t = tenex::evaluate(R_a(ti::t));\n  // \\f$L = R_{T}\\f$\n  const Scalar<DataType> L_from_R_T = tenex::evaluate(R_A(ti::T));\n\n  CHECK(get(L_from_R_t) == R_a.get(0));\n  CHECK(get(L_from_R_T) == R_A.get(0));\n\n  // Rank 2 testing\n\n  // RHS tensors with non-symmetric spacetime indices\n  const auto R_ab = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_AB = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_Ab = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_aB = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n  // RHS tensors with one spacetime and one spatial index\n  const auto R_ai = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                        SpatialIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_ia = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpatialIndex<dim, UpLo::Lo, frame>,\n                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_IA = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpatialIndex<dim, UpLo::Up, frame>,\n                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_AI = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                        SpatialIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_Ai = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                        SpatialIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_Ia = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpatialIndex<dim, UpLo::Up, frame>,\n                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_aI = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                        SpatialIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_iA = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpatialIndex<dim, UpLo::Lo, frame>,\n                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n  // RHS tensors with symmetric spacetime indices\n  const auto S_ab = make_with_random_values<\n      Tensor<DataType, Symmetry<1, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto S_AB = make_with_random_values<\n      Tensor<DataType, Symmetry<1, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n\n  // Evaluations of non-symmetric RHS tensors\n\n  // \\f$L_{a} = R_{at}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>\n      L_a_from_R_at = tenex::evaluate<ti::a>(R_ab(ti::a, ti::t));\n  // \\f$L_{a} = R_{ta}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>\n      L_a_from_R_ta = tenex::evaluate<ti::a>(R_ab(ti::t, ti::a));\n  // \\f$L = R_{tt}\\f$\n  const Scalar<DataType> L_from_R_tt = tenex::evaluate(R_ab(ti::t, ti::t));\n  // \\f$L^{a} = R^{at}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>\n      L_A_from_R_AT = tenex::evaluate<ti::A>(R_AB(ti::A, ti::T));\n  // \\f$L^{a} = R^{ta}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>\n      L_A_from_R_TA = tenex::evaluate<ti::A>(R_AB(ti::T, ti::A));\n  // \\f$L = R^{tt}\\f$\n  const Scalar<DataType> L_from_R_TT = tenex::evaluate(R_AB(ti::T, ti::T));\n  // \\f$L^{a} = R^{a}{}_{t}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>\n      L_A_from_R_At = tenex::evaluate<ti::A>(R_Ab(ti::A, ti::t));\n  // \\f$L_{a} = R^{t}{}_{a}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>\n      L_a_from_R_Ta = tenex::evaluate<ti::a>(R_Ab(ti::T, ti::a));\n  // \\f$L = R^{t}{}_{t}\\f$\n  const Scalar<DataType> L_from_R_Tt = tenex::evaluate(R_Ab(ti::T, ti::t));\n  // \\f$L_{a} = R_{a}{}^{t}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>\n      L_a_from_R_aT = tenex::evaluate<ti::a>(R_aB(ti::a, ti::T));\n  // \\f$L^{a} = R_{t}{}^{a}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>\n      L_A_from_R_tA = tenex::evaluate<ti::A>(R_aB(ti::t, ti::A));\n  // \\f$L = R_{t}{}^{t}\\f$\n  const Scalar<DataType> L_from_R_tT = tenex::evaluate(R_aB(ti::t, ti::T));\n\n  // Evaluations of symmetric RHS tensors\n\n  // \\f$L_{a} = S_{at}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>\n      L_a_from_S_at = tenex::evaluate<ti::a>(S_ab(ti::a, ti::t));\n  // \\f$L_{a} = S_{ta}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>\n      L_a_from_S_ta = tenex::evaluate<ti::a>(S_ab(ti::t, ti::a));\n  // \\f$L = S_{tt}\\f$\n  const Scalar<DataType> L_from_S_tt = tenex::evaluate(S_ab(ti::t, ti::t));\n  // \\f$L^{a} = S^{at}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>\n      L_A_from_S_AT = tenex::evaluate<ti::A>(S_AB(ti::A, ti::T));\n  // \\f$L^{a} = S^{ta}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>\n      L_A_from_S_TA = tenex::evaluate<ti::A>(S_AB(ti::T, ti::A));\n  // \\f$L = S^{tt}\\f$\n  const Scalar<DataType> L_from_S_TT = tenex::evaluate(S_AB(ti::T, ti::T));\n\n  // Evaluations of RHS tensors with one spatial and one spacetime index\n\n  // \\f$L_{i} = R_{it}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_R_it = tenex::evaluate<ti::i>(R_ia(ti::i, ti::t));\n  // \\f$L_{i} = R_{ti}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_R_ti = tenex::evaluate<ti::i>(R_ai(ti::t, ti::i));\n  // \\f$L^{i} = R^{it}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_R_IT = tenex::evaluate<ti::I>(R_IA(ti::I, ti::T));\n  // \\f$L^{i} = R^{ti}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_R_TI = tenex::evaluate<ti::I>(R_AI(ti::T, ti::I));\n  // \\f$L^{i} = R^{i}{}_{t}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_R_It = tenex::evaluate<ti::I>(R_Ia(ti::I, ti::t));\n  // \\f$L_{i} = R^{t}{}_{i}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_R_Ti = tenex::evaluate<ti::i>(R_Ai(ti::T, ti::i));\n  // \\f$L_{i} = R_{i}{}^{t}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, frame>>>\n      L_i_from_R_iT = tenex::evaluate<ti::i>(R_iA(ti::i, ti::T));\n  // \\f$L^{i} = R_{t}{}^{i}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpatialIndex<dim, UpLo::Up, frame>>>\n      L_I_from_R_tI = tenex::evaluate<ti::I>(R_aI(ti::t, ti::I));\n\n  CHECK(get(L_from_R_tt) == R_ab.get(0, 0));\n  CHECK(get(L_from_R_TT) == R_AB.get(0, 0));\n  CHECK(get(L_from_R_Tt) == R_Ab.get(0, 0));\n  CHECK(get(L_from_R_tT) == R_aB.get(0, 0));\n\n  CHECK(get(L_from_S_tt) == S_ab.get(0, 0));\n  CHECK(get(L_from_S_TT) == S_AB.get(0, 0));\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    CHECK(L_a_from_R_at.get(a) == R_ab.get(a, 0));\n    CHECK(L_a_from_R_ta.get(a) == R_ab.get(0, a));\n    CHECK(L_A_from_R_AT.get(a) == R_AB.get(a, 0));\n    CHECK(L_A_from_R_TA.get(a) == R_AB.get(0, a));\n    CHECK(L_A_from_R_At.get(a) == R_Ab.get(a, 0));\n    CHECK(L_a_from_R_Ta.get(a) == R_Ab.get(0, a));\n    CHECK(L_a_from_R_aT.get(a) == R_aB.get(a, 0));\n    CHECK(L_A_from_R_tA.get(a) == R_aB.get(0, a));\n\n    CHECK(L_a_from_S_at.get(a) == S_ab.get(a, 0));\n    CHECK(L_a_from_S_ta.get(a) == S_ab.get(0, a));\n    CHECK(L_A_from_S_AT.get(a) == S_AB.get(a, 0));\n    CHECK(L_A_from_S_TA.get(a) == S_AB.get(0, a));\n  }\n\n  for (size_t i = 0; i < dim; i++) {\n    CHECK(L_i_from_R_it.get(i) == R_ia.get(i, 0));\n    CHECK(L_i_from_R_ti.get(i) == R_ai.get(0, i));\n    CHECK(L_I_from_R_IT.get(i) == R_IA.get(i, 0));\n    CHECK(L_I_from_R_TI.get(i) == R_AI.get(0, i));\n    CHECK(L_I_from_R_It.get(i) == R_Ia.get(i, 0));\n    CHECK(L_i_from_R_Ti.get(i) == R_Ai.get(0, i));\n    CHECK(L_i_from_R_iT.get(i) == R_iA.get(i, 0));\n    CHECK(L_I_from_R_tI.get(i) == R_aI.get(0, i));\n  }\n}\n\n// \\brief Test evaluation of tensors where concrete time indices are used for\n// LHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename Generator, typename DataType>\nvoid test_lhs(const gsl::not_null<Generator*> generator,\n              const DataType& used_for_size) {\n  // Note: this function doesn't utilize test helper functions like\n  // test_evaluate() because they aren't generic enough to handle\n  // test cases where the number of indices on the RHS and LHS are not equal.\n  // Instead, we have to manually check each test case of interest.\n\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n  constexpr size_t dim = 3;\n  using frame = Frame::Inertial;\n\n  const auto R = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n\n  // Test evaluation of RHS scalar to LHS rank 1\n\n  // \\f$L_{t} = R\\f$\n  //\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  //\n  // Assign a placeholder to the LHS tensor's components before it is computed\n  // so that when test expressions below only compute time components, we can\n  // check that LHS spatial components haven't changed\n  auto L_t_from_R =\n      make_with_value<Tensor<DataType, Symmetry<1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t>(make_not_null(&L_t_from_R), R());\n  // \\f$L_{T} = R\\f$\n  auto L_T_from_R =\n      make_with_value<Tensor<DataType, Symmetry<1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T>(make_not_null(&L_T_from_R), R());\n\n  CHECK(L_t_from_R.get(0) == get(R));\n  CHECK(L_T_from_R.get(0) == get(R));\n  for (size_t i = 0; i < dim; i++) {\n    CHECK(L_t_from_R.get(i + 1) ==\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    CHECK(L_T_from_R.get(i + 1) ==\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  }\n\n  // Test evaluation of RHS scalar to non-symmetric LHS rank 2\n\n  // \\f$L_{tt} = R\\f$\n  auto L_tt_from_R =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::t>(make_not_null(&L_tt_from_R), R());\n  // \\f$L^{tt} = R\\f$\n  auto L_TT_from_R =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::T>(make_not_null(&L_TT_from_R), R());\n  // \\f$L^{t}{}_{t} = R\\f$\n  auto L_Tt_from_R =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::t>(make_not_null(&L_Tt_from_R), R());\n  // \\f$L_{t}{}^{t} = R\\f$\n  auto L_tT_from_R =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::T>(make_not_null(&L_tT_from_R), R());\n\n  // Test evaluation of RHS scalar to symmetric LHS rank 2\n\n  // \\f$M_{tt} = R\\f$\n  auto M_tt_from_R =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::t>(make_not_null(&M_tt_from_R), R());\n  // \\f$M^{tt} = R\\f$\n  auto M_TT_from_R =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::T>(make_not_null(&M_TT_from_R), R());\n  // \\f$M^{t}{}_{t} = R\\f$\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    for (size_t b = 0; b < dim + 1; b++) {\n      if (a == 0 and b == 0) {\n        CHECK(L_tt_from_R.get(0, 0) == get(R));\n        CHECK(L_TT_from_R.get(0, 0) == get(R));\n        CHECK(L_Tt_from_R.get(0, 0) == get(R));\n        CHECK(L_tT_from_R.get(0, 0) == get(R));\n\n        CHECK(M_tt_from_R.get(0, 0) == get(R));\n        CHECK(M_TT_from_R.get(0, 0) == get(R));\n      } else {\n        CHECK(L_tt_from_R.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_TT_from_R.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_Tt_from_R.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(L_tT_from_R.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n\n        CHECK(M_tt_from_R.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(M_TT_from_R.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      }\n    }\n  }\n\n  // Evaluations of non-symmetric LHS tensors\n\n  const auto R_a = make_with_random_values<Tensor<\n      DataType, Symmetry<1>, index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_A = make_with_random_values<Tensor<\n      DataType, Symmetry<1>, index_list<SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{at} = R_{a}\\f$\n  auto L_at_from_R_a =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::a, ti::t>(make_not_null(&L_at_from_R_a), R_a(ti::a));\n  // \\f$L_{ta} = R_{a}\\f$\n  auto L_ta_from_R_a =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::a>(make_not_null(&L_ta_from_R_a), R_a(ti::a));\n  // \\f$L^{at} = R^{a}\\f$\n  auto L_AT_from_R_A =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::A, ti::T>(make_not_null(&L_AT_from_R_A), R_A(ti::A));\n  // \\f$L^{ta} = R^{a}\\f$\n  auto L_TA_from_R_A =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::A>(make_not_null(&L_TA_from_R_A), R_A(ti::A));\n  // \\f$L^{a}{}_{t} = R^{a}\\f$\n  auto L_At_from_R_A =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::A, ti::t>(make_not_null(&L_At_from_R_A), R_A(ti::A));\n  // \\f$L^{t}{}_{a} = R_{a}\\f$\n  auto L_Ta_from_R_a =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::a>(make_not_null(&L_Ta_from_R_a), R_a(ti::a));\n  // \\f$L_{a}{}^{t} = R_{a}\\f$\n  auto L_aT_from_R_a =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::a, ti::T>(make_not_null(&L_aT_from_R_a), R_a(ti::a));\n  // \\f$L_{t}{}^{a} = R^{a}\\f$\n  auto L_tA_from_R_A =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::A>(make_not_null(&L_tA_from_R_A), R_A(ti::A));\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    CHECK(L_at_from_R_a.get(a, 0) == R_a.get(a));\n    CHECK(L_ta_from_R_a.get(0, a) == R_a.get(a));\n    CHECK(L_AT_from_R_A.get(a, 0) == R_A.get(a));\n    CHECK(L_TA_from_R_A.get(0, a) == R_A.get(a));\n    CHECK(L_At_from_R_A.get(a, 0) == R_A.get(a));\n    CHECK(L_Ta_from_R_a.get(0, a) == R_a.get(a));\n    CHECK(L_aT_from_R_a.get(a, 0) == R_a.get(a));\n    CHECK(L_tA_from_R_A.get(0, a) == R_A.get(a));\n\n    for (size_t i = 0; i < dim; i++) {\n      CHECK(L_at_from_R_a.get(a, i + 1) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_ta_from_R_a.get(i + 1, a) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_AT_from_R_A.get(a, i + 1) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_TA_from_R_A.get(i + 1, a) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_At_from_R_A.get(a, i + 1) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_Ta_from_R_a.get(i + 1, a) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_aT_from_R_a.get(a, i + 1) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_tA_from_R_A.get(i + 1, a) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    }\n  }\n\n  // Evaluations of symmetric LHS tensors\n\n  // \\f$M_{at} = R_{a}\\f$\n  auto M_at_from_R_a =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::a, ti::t>(make_not_null(&M_at_from_R_a), R_a(ti::a));\n  // \\f$M_{ta} = R_{a}\\f$\n  auto M_ta_from_R_a =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::a>(make_not_null(&M_ta_from_R_a), R_a(ti::a));\n  // \\f$M^{at} = R^{a}\\f$\n  auto M_AT_from_R_A =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::A, ti::T>(make_not_null(&M_AT_from_R_A), R_A(ti::A));\n  // \\f$M^{ta} = R^{a}\\f$\n  auto M_TA_from_R_A =\n      make_with_value<Tensor<DataType, Symmetry<1, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::A>(make_not_null(&M_TA_from_R_A), R_A(ti::A));\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    for (size_t b = a; b < dim + 1; b++) {\n      if (a == 0) {\n        CHECK(M_at_from_R_a.get(a, b) == R_a.get(b));\n        CHECK(M_ta_from_R_a.get(a, b) == R_a.get(b));\n        CHECK(M_AT_from_R_A.get(a, b) == R_A.get(b));\n        CHECK(M_TA_from_R_A.get(a, b) == R_A.get(b));\n      } else {\n        CHECK(M_at_from_R_a.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(M_ta_from_R_a.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(M_AT_from_R_A.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        CHECK(M_TA_from_R_A.get(a, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      }\n    }\n  }\n\n  // Evaluations of RHS tensors with one spatial and one spacetime index\n\n  const auto R_i = make_with_random_values<Tensor<\n      DataType, Symmetry<1>, index_list<SpatialIndex<dim, UpLo::Lo, frame>>>>(\n      generator, distribution, used_for_size);\n  const auto R_I = make_with_random_values<Tensor<\n      DataType, Symmetry<1>, index_list<SpatialIndex<dim, UpLo::Up, frame>>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{it} = R_{i}\\f$\n  auto L_it_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpatialIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::i, ti::t>(make_not_null(&L_it_from_R_i), R_i(ti::i));\n  // \\f$L_{ti} = R_{i}\\f$\n  auto L_ti_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpatialIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::i>(make_not_null(&L_ti_from_R_i), R_i(ti::i));\n  // \\f$L^{it} = R^{i}\\f$\n  auto L_IT_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpatialIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::I, ti::T>(make_not_null(&L_IT_from_R_I), R_I(ti::I));\n  // \\f$L^{ti} = R^{i}\\f$\n  auto L_TI_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpatialIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::I>(make_not_null(&L_TI_from_R_I), R_I(ti::I));\n  // \\f$L^{i}{}_{t} = R^{i}\\f$\n  auto L_It_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpatialIndex<dim, UpLo::Up, frame>,\n                                        SpacetimeIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::I, ti::t>(make_not_null(&L_It_from_R_I), R_I(ti::I));\n  // \\f$L^{t}{}_{i} = R_{i}\\f$\n  auto L_Ti_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Up, frame>,\n                                        SpatialIndex<dim, UpLo::Lo, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::T, ti::i>(make_not_null(&L_Ti_from_R_i), R_i(ti::i));\n  // \\f$L_{i}{}^{t} = R_{i}\\f$\n  auto L_iT_from_R_i =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpatialIndex<dim, UpLo::Lo, frame>,\n                                        SpacetimeIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::i, ti::T>(make_not_null(&L_iT_from_R_i), R_i(ti::i));\n  // \\f$L_{t}{}^{i} = R^{i}\\f$\n  auto L_tI_from_R_I =\n      make_with_value<Tensor<DataType, Symmetry<2, 1>,\n                             index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                        SpatialIndex<dim, UpLo::Up, frame>>>>(\n          used_for_size,\n          TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::t, ti::I>(make_not_null(&L_tI_from_R_I), R_I(ti::I));\n\n  for (size_t i = 0; i < dim; i++) {\n    CHECK(L_it_from_R_i.get(i, 0) == R_i.get(i));\n    CHECK(L_ti_from_R_i.get(0, i) == R_i.get(i));\n    CHECK(L_IT_from_R_I.get(i, 0) == R_I.get(i));\n    CHECK(L_TI_from_R_I.get(0, i) == R_I.get(i));\n    CHECK(L_It_from_R_I.get(i, 0) == R_I.get(i));\n    CHECK(L_Ti_from_R_i.get(0, i) == R_i.get(i));\n    CHECK(L_iT_from_R_i.get(i, 0) == R_i.get(i));\n    CHECK(L_tI_from_R_I.get(0, i) == R_I.get(i));\n    for (size_t j = 0; j < dim; j++) {\n      CHECK(L_it_from_R_i.get(i, j + 1) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_ti_from_R_i.get(j + 1, i) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_IT_from_R_I.get(i, j + 1) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_TI_from_R_I.get(j + 1, i) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_It_from_R_I.get(i, j + 1) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_Ti_from_R_i.get(j + 1, i) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_iT_from_R_i.get(i, j + 1) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n      CHECK(L_tI_from_R_I.get(j + 1, i) ==\n            TestHelpers::tenex::component_placeholder_value<DataType>::value);\n    }\n  }\n}\n\n// \\brief Test evaluation of rank 2 tensors where concrete time indices are used\n// for RHS and LHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_rhs_and_lhs_rank_2() {\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::a, ti::t>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::a, ti::t, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::t, ti::a>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::t, ti::a, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::t, ti::t>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::t, ti::t, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::T, ti::T>();\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<1, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::T, ti::T, Symmetry<2, 1>>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::A, ti::t>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::T, ti::a>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::a, ti::T>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::t, ti::A>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::t, ti::i>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n      ti::i, ti::t>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::I, ti::t>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                 SpatialIndex<2, UpLo::Lo, Frame::Inertial>>,\n      ti::T, ti::i>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n                 SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>,\n      ti::i, ti::T>();\n\n  TestHelpers::tenex::test_evaluate_rank_2_impl<\n      false, DataType, Symmetry<2, 1>,\n      index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                 SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n      ti::t, ti::I>();\n}\n\n// \\brief Test evaluation of rank 4 tensors where concrete time indices are used\n// for RHS and LHS spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename Generator, typename DataType>\nvoid test_rhs_and_lhs_rank_4(const gsl::not_null<Generator*> generator,\n                             const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n  constexpr size_t dim = 3;\n\n  const auto R = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<dim, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<dim, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{a}{}^{t}{}_{tb} = R_{tba}\\f$\n  //\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  //\n  // Assign a placeholder to the LHS tensor's components before it is computed\n  // so that when test expressions below only compute time components, we can\n  // check that LHS spatial components haven't changed\n  auto LaTtb_from_R_tba = make_with_value<\n      Tensor<DataType, Symmetry<2, 3, 2, 1>,\n             index_list<SpacetimeIndex<dim, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<dim, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<dim, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<dim, UpLo::Lo, Frame::Grid>>>>(\n      used_for_size,\n      TestHelpers::tenex::component_placeholder_value<DataType>::value);\n  tenex::evaluate<ti::a, ti::T, ti::t, ti::b>(make_not_null(&LaTtb_from_R_tba),\n                                              R(ti::t, ti::b, ti::a));\n\n  for (size_t a = 0; a < dim + 1; a++) {\n    for (size_t b = 0; b < dim + 1; b++) {\n      CHECK(LaTtb_from_R_tba.get(a, 0, 0, b) == R.get(0, b, a));\n\n      for (size_t i = 0; i < 3; i++) {\n        for (size_t j = 0; j < 3; j++) {\n          CHECK(\n              LaTtb_from_R_tba.get(a, i + 1, j + 1, b) ==\n              TestHelpers::tenex::component_placeholder_value<DataType>::value);\n        }\n      }\n    }\n  }\n}\n\n// \\brief Test evaluation of tensors where concrete time indices are used for\n// spacetime indices\n//\n// \\tparam DataType the type of data being stored in the expression operands\ntemplate <typename DataType>\nvoid test_evaluate_time_index(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n\n  test_rhs(make_not_null(&generator), used_for_size);\n  test_lhs(make_not_null(&generator), used_for_size);\n  test_rhs_and_lhs_rank_2<DataType>();\n  test_rhs_and_lhs_rank_4(make_not_null(&generator), used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.EvaluateTimeIndex\",\n                  \"[DataStructures][Unit]\") {\n  test_evaluate_time_index(std::numeric_limits<double>::signaling_NaN());\n  test_evaluate_time_index(\n      DataVector(3, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_Examples.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// \\file\n// Defines and tests examples for how to use `TensorExpression`s\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n// test `tenex::evaluate()`\ntemplate <typename Generator, typename DataType>\nvoid test_evaluate(const gsl::not_null<Generator*> generator,\n                   const std::uniform_real_distribution<>& distribution,\n                   const DataType& used_for_size) {\n  constexpr size_t Dim = 3;\n\n  const auto R = make_with_random_values<tnsr::ab<DataType, Dim>>(\n      generator, distribution, used_for_size);\n  const auto g = make_with_random_values<tnsr::AA<DataType, Dim>>(\n      generator, distribution, used_for_size);\n\n  tnsr::Ab<DataType, Dim> expected_result{};\n  for (size_t c = 0; c < Dim + 1; c++) {\n    for (size_t b = 0; b < Dim + 1; b++) {\n      expected_result.get(c, b) = R.get(0, b) * g.get(0, c);\n      for (size_t a = 1; a < Dim + 1; a++) {\n        expected_result.get(c, b) += R.get(a, b) * g.get(a, c);\n      }\n    }\n  }\n\n  {\n    // [te_example_evaluate_lhs_return]\n    auto R_up =\n        tenex::evaluate<ti::C, ti::b>(R(ti::a, ti::b) * g(ti::A, ti::C));\n    // [te_example_evaluate_lhs_return]\n    CHECK_ITERABLE_APPROX(R_up, expected_result);\n  }\n  {\n    // [te_example_evaluate_lhs_arg]\n    tnsr::Ab<DataType, Dim> R_up{};\n    tenex::evaluate<ti::C, ti::b>(make_not_null(&R_up),\n                                  R(ti::a, ti::b) * g(ti::A, ti::C));\n    // [te_example_evaluate_lhs_arg]\n    CHECK_ITERABLE_APPROX(R_up, expected_result);\n  }\n}\n\n// tests arithmetric operations\ntemplate <typename Generator, typename DataType>\nvoid test_basic_operations(const gsl::not_null<Generator*> generator,\n                           const std::uniform_real_distribution<>& distribution,\n                           const DataType& used_for_size) {\n  constexpr size_t Dim = 3;\n\n  const auto R = make_with_random_values<tnsr::ab<DataType, Dim>>(\n      generator, distribution, used_for_size);\n  const auto S = make_with_random_values<tnsr::ab<DataType, Dim>>(\n      generator, distribution, used_for_size);\n  const auto T = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n  const auto U = make_with_random_values<tnsr::Ab<DataType, Dim>>(\n      generator, distribution, used_for_size);\n  const auto V = make_with_random_values<tnsr::aBC<DataType, Dim>>(\n      generator, distribution, used_for_size);\n  const auto G = make_with_random_values<tnsr::a<DataType, Dim>>(\n      generator, distribution, used_for_size);\n  const auto H = make_with_random_values<tnsr::A<DataType, Dim>>(\n      generator, distribution, used_for_size);\n\n  // some examples below are forced to be more than one line against the wishes\n  // of clang-tidy because the doxygen \\snippet function can't render\n  // single-lined code snippets\n\n  // addition\n  {\n    // [te_example_addition]\n    auto L =\n        tenex::evaluate<ti::a, ti::b>(R(ti::a, ti::b) + S(ti::b, ti::a));\n    // [te_example_addition]\n\n    tnsr::ab<DataType, Dim> expected_result{};\n    for (size_t a = 0; a < Dim + 1; a++) {\n      for (size_t b = 0; b < Dim + 1; b++) {\n        expected_result.get(a, b) = R.get(a, b) + S.get(b, a);\n      }\n    }\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  // subtraction\n  {\n    // [te_example_subtraction]\n    auto L =\n        tenex::evaluate(1.0 - T());\n    // [te_example_subtraction]\n\n    const Scalar<DataType> expected_result{1.0 - get(T)};\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  {\n    // [te_example_contraction_to_scalar]\n    auto L =\n        tenex::evaluate(U(ti::A, ti::a));\n    // [te_example_contraction_to_scalar]\n\n    Scalar<DataType> expected_result{get<0, 0>(U)};\n    for (size_t a = 1; a < Dim + 1; a++) {\n      get(expected_result) += U.get(a, a);\n    }\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  {\n    // [te_example_contraction_to_tensor]\n    auto L =\n        tenex::evaluate<ti::B>(V(ti::a, ti::B, ti::A));\n    // [te_example_contraction_to_tensor]\n\n    tnsr::A<DataType, Dim> expected_result{};\n    for (size_t b = 0; b < Dim + 1; b++) {\n      expected_result.get(b) = V.get(0, b, 0);\n      for (size_t a = 1; a < Dim + 1; a++) {\n        expected_result.get(b) += V.get(a, b, a);\n      }\n    }\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  {\n    // [te_example_inner_product]\n    auto L =\n        tenex::evaluate(G(ti::a) * H(ti::A));\n    // [te_example_inner_product]\n\n    Scalar<DataType> expected_result{get<0>(G) * get<0>(H)};\n    for (size_t a = 1; a < Dim + 1; a++) {\n      get(expected_result) += G.get(a) * H.get(a);\n    }\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  {\n    // [te_example_inner_and_outer_product]\n    auto L = tenex::evaluate<ti::c, ti::b>(T() * G(ti::a) * G(ti::c) *\n                                           U(ti::A, ti::b));\n    // [te_example_inner_and_outer_product]\n\n    tnsr::ab<DataType, Dim> expected_result{};\n    for (size_t c = 0; c < Dim + 1; c++) {\n      for (size_t b = 0; b < Dim + 1; b++) {\n        expected_result.get(c, b) = get<0>(G) * G.get(c) * U.get(0, b);\n        for (size_t a = 1; a < Dim + 1; a++) {\n          expected_result.get(c, b) += G.get(a) * G.get(c) * U.get(a, b);\n        }\n        expected_result.get(c, b) *= get(T);\n      }\n    }\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  {\n    // [te_example_division_by_number]\n    auto L =\n        tenex::evaluate<ti::a>(G(ti::a) / 2.0);\n    // [te_example_division_by_number]\n\n    tnsr::a<DataType, Dim> expected_result{};\n    for (size_t a = 0; a < Dim + 1; a++) {\n      expected_result.get(a) = G.get(a) / 2.0;\n    }\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  {\n    // [te_example_division_by_tensor]\n    auto L =\n        tenex::evaluate<ti::b, ti::a>(R(ti::a, ti::b) / T());\n    // [te_example_division_by_tensor]\n\n    tnsr::ab<DataType, Dim> expected_result{};\n    for (size_t b = 0; b < Dim + 1; b++) {\n      for (size_t a = 0; a < Dim + 1; a++) {\n        expected_result.get(b, a) = R.get(a, b) / get(T);\n      }\n    }\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  {\n    // [te_example_division_by_tensor_expression]\n    auto L =\n        tenex::evaluate(5.0 / (U(ti::A, ti::a) + 1.0));\n    // [te_example_division_by_tensor_expression]\n\n    auto expected_result =\n        make_with_value<Scalar<DataType>>(used_for_size, 1.0);\n    for (size_t a = 0; a < Dim + 1; a++) {\n      get(expected_result) += U.get(a, a);\n    }\n    get(expected_result) = 5.0 / get(expected_result);\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  // square root\n  {\n    // [te_example_square_root_tensor]\n    auto L =\n        tenex::evaluate(sqrt(T()));\n    // [te_example_square_root_tensor]\n\n    const Scalar<DataType> expected_result{sqrt(get(T))};\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n  {\n    // [te_example_square_root_inner_product]\n    auto L =\n        tenex::evaluate(sqrt(G(ti::a) * H(ti::A)));\n    // [te_example_square_root_inner_product]\n\n    Scalar<DataType> expected_result{get<0>(G) * get<0>(H)};\n    for (size_t a = 1; a < Dim + 1; a++) {\n      get(expected_result) += G.get(a) * H.get(a);\n    }\n    get(expected_result) = sqrt(get(expected_result));\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n}\n\n// tests LHS symmetry deduction and override by user\ntemplate <typename Generator, typename DataType>\nvoid test_specify_lhs_symmetry(\n    const gsl::not_null<Generator*> generator,\n    const std::uniform_real_distribution<>& distribution,\n    const DataType& used_for_size) {\n  constexpr size_t Dim = 3;\n\n  const auto R = make_with_random_values<tnsr::a<DataType, Dim>>(\n      generator, distribution, used_for_size);\n\n  tnsr::aa<DataType, Dim> expected_result{};\n  for (size_t a = 0; a < Dim + 1; a++) {\n    for (size_t b = a; b < Dim + 1; b++) {\n      expected_result.get(a, b) = R.get(a) * R.get(b);\n    }\n  }\n\n  {\n    // [te_example_deduced_lhs_symmetry_fail]\n    auto L = tenex::evaluate<ti::a, ti::b>(R(ti::a) * R(ti::b));\n    static_assert(std::is_same_v<decltype(L), tnsr::ab<DataType, Dim>>);\n    // [te_example_deduced_lhs_symmetry_fail]\n\n    for (size_t a = 0; a < 4; a++) {\n      for (size_t b = 0; b < 4; b++) {\n        CHECK(L.get(a, b) == expected_result.get(a, b));\n        CHECK(L.get(b, a) == expected_result.get(a, b));\n      }\n    }\n  }\n  {\n    // [te_example_deduced_lhs_symmetry_force]\n    tnsr::aa<DataType, 3> L{};\n    tenex::evaluate<ti::a, ti::b>(make_not_null(&L), R(ti::a) * R(ti::b));\n    // [te_example_deduced_lhs_symmetry_force]\n\n    CHECK_ITERABLE_APPROX(L, expected_result);\n  }\n}\n\n// tests assignment of a RHS number to a LHS tensor\ntemplate <typename DataType>\nvoid test_assign_number() {\n  static_assert(std::is_same_v<DataType, double> or\n                std::is_same_v<DataType, DataVector>);\n\n  constexpr size_t Dim = 3;\n\n  if constexpr (std::is_same_v<DataType, double>) {\n    // [te_example_assign_number_to_tensor_of_numbers]\n    tnsr::ab<double, Dim> L{};\n    tenex::evaluate<ti::a, ti::b>(make_not_null(&L), -1.0);\n    // [te_example_assign_number_to_tensor_of_numbers]\n\n    const auto expected_result = make_with_value<tnsr::ab<double, Dim>>(\n        std::numeric_limits<double>::signaling_NaN(), -1.0);\n    CHECK(L == expected_result);\n  } else if constexpr (std::is_same_v<DataType, DataVector>) {\n    // [te_example_assign_number_to_tensor_of_vectors]\n    // construct LHS tensor with size 5 DataVector\n    tnsr::ab<DataVector, Dim> L{DataVector(5, 0.0)};\n    tenex::evaluate<ti::a, ti::b>(make_not_null(&L), -1.0);\n    // [te_example_assign_number_to_tensor_of_vectors]\n\n    const size_t num_points = L[0].size();\n    const auto expected_result = make_with_value<tnsr::ab<DataVector, Dim>>(\n        DataVector(num_points, std::numeric_limits<double>::signaling_NaN()),\n        -1.0);\n    CHECK(L == expected_result);\n  }\n}\n\n// tests usage of spatial and time indices for spacetime indices on the RHS\ntemplate <typename Generator, typename DataType>\nvoid test_rhs_spatial_and_time_indices(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  constexpr size_t Dim = 3;\n\n  // use gr random helper functions and calculate spacetime metric explicitly to\n  // avoid taking square root of a negative in the tested expression\n  const Scalar<DataType> random_lapse =\n      TestHelpers::gr::random_lapse(generator, used_for_size);\n  const tnsr::I<DataType, Dim> random_shift =\n      TestHelpers::gr::random_shift<Dim>(generator, used_for_size);\n  const tnsr::ii<DataType, Dim> random_spatial_metric =\n      TestHelpers::gr::random_spatial_metric<Dim>(generator, used_for_size);\n\n  auto spacetime_metric = make_with_value<tnsr::aa<DataType, Dim>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n  gr::spacetime_metric(make_not_null(&spacetime_metric), random_lapse,\n                       random_shift, random_spatial_metric);\n  const auto& shift = random_shift;\n  const auto& expected_result = random_lapse;\n\n  // [te_example_rhs_spatial_and_time_indices]\n  auto lapse =\n      tenex::evaluate(sqrt(shift(ti::I) * spacetime_metric(ti::i, ti::t) -\n                           spacetime_metric(ti::t, ti::t)));\n  // [te_example_rhs_spatial_and_time_indices]\n\n  CHECK_ITERABLE_APPROX(lapse, expected_result);\n}\n\n// tests usage of spatial and time indices for spacetime indices on the LHS\ntemplate <typename Generator, typename DataType>\nvoid test_lhs_spatial_and_time_indices(\n    const gsl::not_null<Generator*> generator,\n    const std::uniform_real_distribution<>& distribution,\n    const DataType& used_for_size) {\n  constexpr size_t Dim = 3;\n\n  const auto spatial_metric = make_with_random_values<tnsr::ii<DataType, Dim>>(\n      generator, distribution, used_for_size);\n  const auto shift = make_with_random_values<tnsr::I<DataType, Dim>>(\n      generator, distribution, used_for_size);\n  const auto lapse = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n\n  // [te_example_lhs_spatial_and_time_indices]\n  tnsr::aa<DataType, Dim> spacetime_metric{};\n  tenex::evaluate<ti::t, ti::t>(\n      make_not_null(&spacetime_metric),\n      -square(lapse()) +\n          shift(ti::M) * shift(ti::N) * spatial_metric(ti::m, ti::n));\n  tenex::evaluate<ti::t, ti::i>(make_not_null(&spacetime_metric),\n                                spatial_metric(ti::m, ti::i) * shift(ti::M));\n  tenex::evaluate<ti::i, ti::j>(make_not_null(&spacetime_metric),\n                                spatial_metric(ti::i, ti::j));\n  // [te_example_lhs_spatial_and_time_indices]\n\n  // note: there are more efficient ways to implement this equation, but\n  // choosing the simplest to read and write\n  tnsr::aa<DataType, 3> expected_result{};\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = i; j < Dim; j++) {\n      expected_result.get(i + 1, j + 1) = spatial_metric.get(i, j);\n    }\n  }\n\n  for (size_t i = 0; i < Dim; i++) {\n    expected_result.get(0, i + 1) = spatial_metric.get(0, i) * shift.get(0);\n    for (size_t m = 1; m < Dim; m++) {\n      expected_result.get(0, i + 1) += spatial_metric.get(m, i) * shift.get(m);\n    }\n  }\n\n  get<0, 0>(expected_result) = -square(get(lapse));\n  for (size_t m = 0; m < Dim; m++) {\n    for (size_t n = 0; n < Dim; n++) {\n      get<0, 0>(expected_result) +=\n          spatial_metric.get(m, n) * shift.get(m) * shift.get(n);\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(spacetime_metric, expected_result);\n}\n\n// tests using real-valued and complex-valued terms in the RHS expression\ntemplate <typename Generator, typename RealDataType>\nvoid test_complex(const gsl::not_null<Generator*> generator,\n                  const std::uniform_real_distribution<>& distribution,\n                  const RealDataType& used_for_size) {\n  static_assert(std::is_same_v<RealDataType, double> or\n                std::is_same_v<RealDataType, DataVector>);\n\n  constexpr size_t Dim = 3;\n\n  if constexpr (std::is_same_v<RealDataType, double>) {\n    // [te_example_complex_double]\n    const auto x = make_with_random_values<tnsr::I<double, Dim>>(\n        generator, distribution, used_for_size);\n    const auto y = make_with_random_values<tnsr::I<double, Dim>>(\n        generator, distribution, used_for_size);\n    const std::complex<double> i{0.0, 1.0};\n\n    const tnsr::I<std::complex<double>, Dim> z =\n        tenex::evaluate<ti::I>(x(ti::I) + i * y(ti::I));\n    // [te_example_complex_double]\n\n    tnsr::I<std::complex<double>, Dim> expected_result{};\n\n    get<0>(expected_result) = get<0>(x) + i * get<0>(y);\n    for (size_t j = 1; j < Dim; j++) {\n      expected_result.get(j) = x.get(j) + i * y.get(j);\n    }\n\n    CHECK_ITERABLE_APPROX(z, expected_result);\n  } else if constexpr (std::is_same_v<RealDataType, DataVector>) {\n    // [te_example_complex_vector]\n    const auto x = make_with_random_values<tnsr::I<DataVector, Dim>>(\n        generator, distribution, used_for_size);\n    const auto y = make_with_random_values<tnsr::I<DataVector, Dim>>(\n        generator, distribution, used_for_size);\n    const std::complex<double> i{0.0, 1.0};\n\n    const tnsr::I<ComplexDataVector, Dim> z =\n        tenex::evaluate<ti::I>(x(ti::I) + i * y(ti::I));\n    // [te_example_complex_vector]\n\n    tnsr::I<ComplexDataVector, Dim> expected_result{};\n\n    get<0>(expected_result) = get<0>(x) + i * get<0>(y);\n    for (size_t j = 1; j < Dim; j++) {\n      expected_result.get(j) = x.get(j) + i * y.get(j);\n    }\n\n    CHECK_ITERABLE_APPROX(z, expected_result);\n  }\n}\n\n// runs all example tests (see individual functions)\ntemplate <typename Generator, typename DataType>\nvoid test_examples(const gsl::not_null<Generator*> generator,\n                   const std::uniform_real_distribution<>& distribution,\n                   const DataType& used_for_size) {\n  test_evaluate(generator, distribution, used_for_size);\n  test_basic_operations(generator, distribution, used_for_size);\n  test_specify_lhs_symmetry(generator, distribution, used_for_size);\n  test_assign_number<DataType>();\n  test_rhs_spatial_and_time_indices(generator, used_for_size);\n  test_lhs_spatial_and_time_indices(generator, distribution, used_for_size);\n  test_complex(generator, distribution, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.Examples\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n  const std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  test_examples(make_not_null(&generator), distribution,\n                std::numeric_limits<double>::signaling_NaN());\n  test_examples(make_not_null(&generator), distribution,\n                DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_MixedOperations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <complex>\n#include <cstddef>\n#include <random>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Computes \\f$L_{a} = R_{ab} S^{b} + G_{a} - H_{ba}{}^{b} T\\f$\ntemplate <typename R_t, typename S_t, typename G_t, typename H_t, typename T_t,\n          typename DataType>\nG_t compute_expected_mixed_arithmetic_ops(const R_t& R, const S_t& S,\n                                          const G_t& G, const H_t& H,\n                                          const T_t& T,\n                                          const DataType& used_for_size) {\n  using result_tensor_type = G_t;\n  result_tensor_type expected_result{};\n  const size_t dim = tmpl::front<typename R_t::index_list>::dim;\n  for (size_t a = 0; a < dim; a++) {\n    DataType expected_Rab_SB_product =\n        make_with_value<DataType>(used_for_size, 0.0);\n    DataType expected_HbaB_contracted_value =\n        make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t b = 0; b < dim; b++) {\n      expected_Rab_SB_product += R.get(a, b) * S.get(b);\n      expected_HbaB_contracted_value += H.get(b, a, b);\n    }\n    expected_result.get(a) = expected_Rab_SB_product + G.get(a) -\n                             (expected_HbaB_contracted_value * T.get());\n  }\n\n  return expected_result;\n}\n\n// Computes the lapse from the inverse spatial metric and spacetime metric\n//\n// The lapse is calulated using the following equation:\n// \\f$\\alpha = \\sqrt{\\gamma^{ij} g_{jt} g_{it} - g_{tt}}\\f$\ntemplate <typename DataType>\nScalar<DataType> compute_expected_rhs_spacetime_index_subsets(\n    const tnsr::II<DataType, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::aa<DataType, 3, Frame::Inertial>& spacetime_metric,\n    const DataType& used_for_size) {\n  DataType expected_gamma_g_g_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      expected_gamma_g_g_product += spatial_metric.get(i, j) *\n                                    spacetime_metric.get(j + 1, 0) *\n                                    spacetime_metric.get(i + 1, 0);\n    }\n  }\n\n  Scalar<DataType> expected_result{\n      sqrt(expected_gamma_g_g_product - spacetime_metric.get(0, 0))};\n\n  return expected_result;\n}\n\n// Computes the spacetime derivative of the spacetime metric\n//\n// \\f$\\partial_c g_{ab}\\f$ is computed using the following two equations:\n// (1) \\f$\\partial_t g_{ab} = -\\alpha \\Pi_{ab} + \\beta^i \\Phi_{iab}\\f$\n// (2) \\f$\\partial_i g_{ab} = \\Phi_{iab}\\f$\ntemplate <typename DataType>\ntnsr::abb<DataType, 3, Frame::Inertial>\ncompute_expected_lhs_spacetime_index_subsets(\n    const Scalar<DataType>& alpha,\n    const tnsr::I<DataType, 3, Frame::Inertial>& beta,\n    const tnsr::aa<DataType, 3, Frame::Inertial>& pi,\n    const tnsr::iaa<DataType, 3, Frame::Inertial>& phi,\n    const DataType& used_for_size) {\n  tnsr::abb<DataType, 3, Frame::Inertial> expected_result{};\n\n  for (size_t a = 0; a < 4; a++) {\n    for (size_t b = 0; b < 4; b++) {\n      DataType expected_beta_phi_product =\n          make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t i = 0; i < 3; i++) {\n        expected_beta_phi_product += beta.get(i) * phi.get(i, a, b);\n        expected_result.get(i + 1, a, b) = phi.get(i, a, b);\n      }\n      expected_result.get(0, a, b) =\n          -alpha.get() * pi.get(a, b) + expected_beta_phi_product;\n    }\n  }\n\n  return expected_result;\n}\n\n// Aliases used for test_large_equation()\ntemplate <typename DataType, size_t Dim>\nusing result_tensor_type = tnsr::aa<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing spacetime_deriv_gauge_function_type = tnsr::ab<DataType, Dim>;\ntemplate <typename DataType>\nusing pi_two_normals_type = Scalar<DataType>;\ntemplate <typename DataType, size_t Dim>\nusing pi_type = tnsr::aa<DataType, Dim>;\ntemplate <typename DataType>\nusing gamma0_type = Scalar<DataType>;\ntemplate <typename DataType, size_t Dim>\nusing normal_spacetime_one_form_type = tnsr::a<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing gauge_constraint_type = tnsr::a<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing spacetime_metric_type = tnsr::aa<DataType, Dim>;\ntemplate <typename DataType>\nusing normal_dot_gauge_constraint_type = Scalar<DataType>;\ntemplate <typename DataType, size_t Dim>\nusing christoffel_second_kind_type = tnsr::Abb<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing gauge_function_type = tnsr::a<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing pi_2_up_type = tnsr::aB<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing phi_1_up_type = tnsr::Iaa<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing phi_3_up_type = tnsr::iaB<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing christoffel_first_kind_3_up_type = tnsr::abC<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing pi_one_normal_type = tnsr::a<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing inverse_spatial_metric_type = tnsr::II<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing d_phi_type = tnsr::ijaa<DataType, Dim>;\ntemplate <typename DataType>\nusing lapse_type = Scalar<DataType>;\ntemplate <typename DataType>\nusing gamma1gamma2_type = Scalar<DataType>;\ntemplate <typename DataType, size_t Dim>\nusing shift_dot_three_index_constraint_type = tnsr::aa<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing shift_type = tnsr::I<DataType, Dim>;\ntemplate <typename DataType, size_t Dim>\nusing d_pi_type = tnsr::iaa<DataType, Dim>;\n\n// Computes the Generalized Harmonic equation for the time derivative,\n// \\f$\\partial_t \\Pi_{ab} \\f$ (eq 36 of \\cite Lindblom2005qh). This is taken\n// from the implementation of `gh::TimeDerivative`.\ntemplate <typename DataType, size_t Dim>\nresult_tensor_type<DataType, Dim> compute_expected_large_equation(\n    const spacetime_deriv_gauge_function_type<DataType, Dim>&\n        spacetime_deriv_gauge_function,\n    const pi_two_normals_type<DataType>& pi_two_normals,\n    const pi_type<DataType, Dim>& pi, const gamma0_type<DataType>& gamma0,\n    const normal_spacetime_one_form_type<DataType, Dim>&\n        normal_spacetime_one_form,\n    const gauge_constraint_type<DataType, Dim>& gauge_constraint,\n    const spacetime_metric_type<DataType, Dim>& spacetime_metric,\n    const normal_dot_gauge_constraint_type<DataType>&\n        normal_dot_gauge_constraint,\n    const christoffel_second_kind_type<DataType, Dim>& christoffel_second_kind,\n    const gauge_function_type<DataType, Dim>& gauge_function,\n    const pi_2_up_type<DataType, Dim>& pi_2_up,\n    const phi_1_up_type<DataType, Dim>& phi_1_up,\n    const phi_3_up_type<DataType, Dim>& phi_3_up,\n    const christoffel_first_kind_3_up_type<DataType, Dim>&\n        christoffel_first_kind_3_up,\n    const pi_one_normal_type<DataType, Dim>& pi_one_normal,\n    const inverse_spatial_metric_type<DataType, Dim>& inverse_spatial_metric,\n    const d_phi_type<DataType, Dim>& d_phi, const lapse_type<DataType>& lapse,\n    const gamma1gamma2_type<DataType>& gamma1gamma2,\n    const shift_dot_three_index_constraint_type<DataType, Dim>&\n        shift_dot_three_index_constraint,\n    const shift_type<DataType, Dim>& shift,\n    const d_pi_type<DataType, Dim>& d_pi) {\n  result_tensor_type<DataType, Dim> expected_result{};\n\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    for (size_t nu = mu; nu < Dim + 1; ++nu) {\n      expected_result.get(mu, nu) =\n          -spacetime_deriv_gauge_function.get(mu, nu) -\n          spacetime_deriv_gauge_function.get(nu, mu) -\n          0.5 * get(pi_two_normals) * pi.get(mu, nu) +\n          get(gamma0) *\n              (normal_spacetime_one_form.get(mu) * gauge_constraint.get(nu) +\n               normal_spacetime_one_form.get(nu) * gauge_constraint.get(mu)) -\n          get(gamma0) * spacetime_metric.get(mu, nu) *\n              get(normal_dot_gauge_constraint);\n\n      for (size_t delta = 0; delta < Dim + 1; ++delta) {\n        expected_result.get(mu, nu) +=\n            2.0 * christoffel_second_kind.get(delta, mu, nu) *\n                gauge_function.get(delta) -\n            2.0 * pi.get(mu, delta) * pi_2_up.get(nu, delta);\n\n        for (size_t n = 0; n < Dim; ++n) {\n          expected_result.get(mu, nu) +=\n              2.0 * phi_1_up.get(n, mu, delta) * phi_3_up.get(n, nu, delta);\n        }\n\n        for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n          expected_result.get(mu, nu) -=\n              2.0 * christoffel_first_kind_3_up.get(mu, alpha, delta) *\n              christoffel_first_kind_3_up.get(nu, delta, alpha);\n        }\n      }\n\n      for (size_t m = 0; m < Dim; ++m) {\n        expected_result.get(mu, nu) -=\n            pi_one_normal.get(m + 1) * phi_1_up.get(m, mu, nu);\n\n        for (size_t n = 0; n < Dim; ++n) {\n          expected_result.get(mu, nu) -=\n              inverse_spatial_metric.get(m, n) * d_phi.get(m, n, mu, nu);\n        }\n      }\n\n      expected_result.get(mu, nu) *= get(lapse);\n\n      expected_result.get(mu, nu) +=\n          get(gamma1gamma2) * shift_dot_three_index_constraint.get(mu, nu);\n\n      for (size_t m = 0; m < Dim; ++m) {\n        // DualFrame term\n        expected_result.get(mu, nu) += shift.get(m) * d_pi.get(m, mu, nu);\n      }\n    }\n  }\n\n  return expected_result;\n}\n\n// Includes an expression with addition, subtraction, an inner product, an outer\n// product, a contraction, and a scalar\ntemplate <typename Generator, typename DataType>\nvoid test_mixed_arithmetic_ops(const gsl::not_null<Generator*> generator,\n                               const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  const auto R = make_with_random_values<tnsr::ab<DataType, 3, Frame::Grid>>(\n      generator, make_not_null(&distribution), used_for_size);\n\n  const auto S = make_with_random_values<tnsr::A<DataType, 3, Frame::Grid>>(\n      generator, make_not_null(&distribution), used_for_size);\n\n  const auto G = make_with_random_values<tnsr::a<DataType, 3, Frame::Grid>>(\n      generator, make_not_null(&distribution), used_for_size);\n\n  const auto H = make_with_random_values<tnsr::abC<DataType, 3, Frame::Grid>>(\n      generator, make_not_null(&distribution), used_for_size);\n\n  const auto T = make_with_random_values<Scalar<DataType>>(\n      generator, make_not_null(&distribution), used_for_size);\n\n  using result_tensor_type = tnsr::a<DataType, 3, Frame::Grid>;\n  result_tensor_type expected_result_tensor =\n      compute_expected_mixed_arithmetic_ops(R, S, G, H, T, used_for_size);\n  // \\f$L_{a} = R_{ab} S^{b} + G_{a} - H_{ba}{}^{b} T\\f$\n  // [use_evaluate_to_return_result]\n  result_tensor_type actual_result_tensor_returned = tenex::evaluate<ti::a>(\n      R(ti::a, ti::b) * S(ti::B) + G(ti::a) - H(ti::b, ti::a, ti::B) * T());\n  // [use_evaluate_to_return_result]\n\n  // [use_evaluate_with_result_as_arg]\n  result_tensor_type actual_result_tensor_filled{};\n  tenex::evaluate<ti::a>(\n      make_not_null(&actual_result_tensor_filled),\n      R(ti::a, ti::b) * S(ti::B) + G(ti::a) - H(ti::b, ti::a, ti::B) * T());\n  // [use_evaluate_with_result_as_arg]\n\n  for (size_t a = 0; a < 4; a++) {\n    CHECK_ITERABLE_APPROX(actual_result_tensor_returned.get(a),\n                          expected_result_tensor.get(a));\n    CHECK_ITERABLE_APPROX(actual_result_tensor_filled.get(a),\n                          expected_result_tensor.get(a));\n  }\n\n  // Test with TempTensor for LHS tensor\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<::Tags::TempTensor<1, result_tensor_type>>>\n        actual_result_tensor_temp_var{used_for_size.size()};\n    result_tensor_type& actual_result_tensor_temp =\n        get<::Tags::TempTensor<1, result_tensor_type>>(\n            actual_result_tensor_temp_var);\n    ::tenex::evaluate<ti::a>(\n        make_not_null(&actual_result_tensor_temp),\n        R(ti::a, ti::b) * S(ti::B) + G(ti::a) - H(ti::b, ti::a, ti::B) * T());\n\n    for (size_t a = 0; a < 4; a++) {\n      CHECK_ITERABLE_APPROX(actual_result_tensor_temp.get(a),\n                            expected_result_tensor.get(a));\n    }\n  }\n}\n\n// Includes an expression with subtraction, inner products, an outer product,\n// a square root, a scalar, generic spatial indices used for spacetime indices,\n// and concrete time indices used for spacetime indices\n//\n// Note: This is an expanded calculation of the lapse from the shift and\n// spacetime metric, where the shift is calculated from the inverse spatial\n// metric and spacetime metric.\ntemplate <typename Generator, typename DataType>\nvoid test_rhs_spacetime_index_subsets(const gsl::not_null<Generator*> generator,\n                                      const DataType& used_for_size) {\n  // Use a higher distribution for sptial metric than spacetime metric to ensure\n  // we do not take the square root of a negative number\n  std::uniform_real_distribution<> spatial_metric_distribution(3.0, 4.0);\n  std::uniform_real_distribution<> spacetime_metric_distribution(1.0, 2.0);\n\n  const auto spatial_metric =\n      make_with_random_values<tnsr::II<DataType, 3, Frame::Inertial>>(\n          generator, make_not_null(&spatial_metric_distribution),\n          used_for_size);\n\n  const auto spacetime_metric =\n      make_with_random_values<tnsr::aa<DataType, 3, Frame::Inertial>>(\n          generator, make_not_null(&spacetime_metric_distribution),\n          used_for_size);\n\n  const Scalar<DataType> expected_result_tensor =\n      compute_expected_rhs_spacetime_index_subsets(\n          spatial_metric, spacetime_metric, used_for_size);\n  // \\f$\\alpha = \\sqrt{\\gamma^{ij} g_{jt} g_{it} - g_{tt}}\\f$\n  const Scalar<DataType> actual_result_tensor_returned = tenex::evaluate(\n      sqrt(spatial_metric(ti::I, ti::J) * spacetime_metric(ti::j, ti::t) *\n               spacetime_metric(ti::i, ti::t) -\n           spacetime_metric(ti::t, ti::t)));\n  Scalar<DataType> actual_result_tensor_filled{};\n  tenex::evaluate(\n      make_not_null(&actual_result_tensor_filled),\n      sqrt(spatial_metric(ti::I, ti::J) * spacetime_metric(ti::j, ti::t) *\n               spacetime_metric(ti::i, ti::t) -\n           spacetime_metric(ti::t, ti::t)));\n\n  CHECK_ITERABLE_APPROX(actual_result_tensor_returned.get(),\n                        expected_result_tensor.get());\n  CHECK_ITERABLE_APPROX(actual_result_tensor_filled.get(),\n                        expected_result_tensor.get());\n\n  // Test with TempTensor for LHS tensor\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<::Tags::TempTensor<1, Tensor<DataType>>>>\n        actual_result_tensor_temp_var{used_for_size.size()};\n    Scalar<DataType>& actual_result_tensor_temp =\n        get<::Tags::TempTensor<1, Tensor<DataType>>>(\n            actual_result_tensor_temp_var);\n    ::tenex::evaluate(\n        make_not_null(&actual_result_tensor_temp),\n        sqrt(spatial_metric(ti::I, ti::J) * spacetime_metric(ti::j, ti::t) *\n                 spacetime_metric(ti::i, ti::t) -\n             spacetime_metric(ti::t, ti::t)));\n\n    CHECK_ITERABLE_APPROX(actual_result_tensor_temp.get(),\n                          expected_result_tensor.get());\n  }\n}\n\n// Includes an expression with addition, subtraction, an inner product, outer\n// products, a scalar, and two calls to `evaluate` that fill the time and\n// spatial components of the result tensor, respectively\n//\n// Note: This is the calculation of the spacetime derivative of the spacetime\n// metric, \\f$\\partial_c g_{ab}\\f$\ntemplate <typename Generator, typename DataType>\nvoid test_lhs_spacetime_index_subsets(const gsl::not_null<Generator*> generator,\n                                      const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  const auto alpha = make_with_random_values<Scalar<DataType>>(\n      generator, make_not_null(&distribution), used_for_size);\n\n  const auto beta =\n      make_with_random_values<tnsr::I<DataType, 3, Frame::Inertial>>(\n          generator, make_not_null(&distribution), used_for_size);\n\n  const auto pi =\n      make_with_random_values<tnsr::aa<DataType, 3, Frame::Inertial>>(\n          generator, make_not_null(&distribution), used_for_size);\n\n  const auto phi =\n      make_with_random_values<tnsr::iaa<DataType, 3, Frame::Inertial>>(\n          generator, make_not_null(&distribution), used_for_size);\n\n  using result_tensor_type = tnsr::abb<DataType, 3, Frame::Inertial>;\n  result_tensor_type expected_result_tensor =\n      compute_expected_lhs_spacetime_index_subsets(alpha, beta, pi, phi,\n                                                   used_for_size);\n  result_tensor_type actual_result_tensor_filled{};\n  // \\f$\\partial_t g_{ab} = -\\alpha \\Pi_{ab} + \\beta^i \\Phi_{iab}\\f$\n  tenex::evaluate<ti::t, ti::a, ti::b>(\n      make_not_null(&actual_result_tensor_filled),\n      -1.0 * alpha() * pi(ti::a, ti::b) +\n          beta(ti::I) * phi(ti::i, ti::a, ti::b));\n  // \\f$\\partial_i g_{ab} = \\Phi_{iab}\\f$\n  tenex::evaluate<ti::i, ti::a, ti::b>(\n      make_not_null(&actual_result_tensor_filled), phi(ti::i, ti::a, ti::b));\n\n  for (size_t c = 0; c < 4; c++) {\n    for (size_t a = 0; a < 4; a++) {\n      for (size_t b = 0; b < 4; b++) {\n        CHECK_ITERABLE_APPROX(actual_result_tensor_filled.get(c, a, b),\n                              expected_result_tensor.get(c, a, b));\n      }\n    }\n  }\n\n  // Test with TempTensor for LHS tensor\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<::Tags::TempTensor<1, result_tensor_type>>>\n        actual_result_tensor_temp_var{used_for_size.size()};\n    result_tensor_type& actual_result_tensor_temp =\n        get<::Tags::TempTensor<1, result_tensor_type>>(\n            actual_result_tensor_temp_var);\n    tenex::evaluate<ti::t, ti::a, ti::b>(\n        make_not_null(&actual_result_tensor_temp),\n        -1.0 * alpha() * pi(ti::a, ti::b) +\n            beta(ti::I) * phi(ti::i, ti::a, ti::b));\n    tenex::evaluate<ti::i, ti::a, ti::b>(\n        make_not_null(&actual_result_tensor_temp), phi(ti::i, ti::a, ti::b));\n\n    for (size_t c = 0; c < 4; c++) {\n      for (size_t a = 0; a < 4; a++) {\n        for (size_t b = 0; b < 4; b++) {\n          CHECK_ITERABLE_APPROX(actual_result_tensor_temp.get(c, a, b),\n                                expected_result_tensor.get(c, a, b));\n        }\n      }\n    }\n  }\n}\n\n// This test case is the Generalized Harmonic equation for the time derivative,\n// \\f$\\partial_t \\Pi_{ab} \\f$ (eq 36 of \\cite Lindblom2005qh).\n//\n// This test case is distinct in that it is a large equation and tests calls to\n// `tenex::update`.\ntemplate <typename Generator, typename DataType>\nvoid test_large_equation(const gsl::not_null<Generator*> generator,\n                         const DataType& used_for_size) {\n  constexpr size_t Dim = 3;\n\n  using result_tensor_type = result_tensor_type<DataType, Dim>;\n  using spacetime_deriv_gauge_function_type =\n      spacetime_deriv_gauge_function_type<DataType, Dim>;\n  using pi_two_normals_type = pi_two_normals_type<DataType>;\n  using pi_type = pi_type<DataType, Dim>;\n  using gamma0_type = gamma0_type<DataType>;\n  using normal_spacetime_one_form_type =\n      normal_spacetime_one_form_type<DataType, Dim>;\n  using gauge_constraint_type = gauge_constraint_type<DataType, Dim>;\n  using spacetime_metric_type = spacetime_metric_type<DataType, Dim>;\n  using normal_dot_gauge_constraint_type =\n      normal_dot_gauge_constraint_type<DataType>;\n  using christoffel_second_kind_type =\n      christoffel_second_kind_type<DataType, Dim>;\n  using gauge_function_type = gauge_function_type<DataType, Dim>;\n  using pi_2_up_type = pi_2_up_type<DataType, Dim>;\n  using phi_1_up_type = phi_1_up_type<DataType, Dim>;\n  using phi_3_up_type = phi_3_up_type<DataType, Dim>;\n  using christoffel_first_kind_3_up_type =\n      christoffel_first_kind_3_up_type<DataType, Dim>;\n  using pi_one_normal_type = pi_one_normal_type<DataType, Dim>;\n  using inverse_spatial_metric_type =\n      inverse_spatial_metric_type<DataType, Dim>;\n  using d_phi_type = d_phi_type<DataType, Dim>;\n  using lapse_type = lapse_type<DataType>;\n  using gamma1gamma2_type = gamma1gamma2_type<DataType>;\n  using shift_dot_three_index_constraint_type =\n      shift_dot_three_index_constraint_type<DataType, Dim>;\n  using shift_type = shift_type<DataType, Dim>;\n  using d_pi_type = d_pi_type<DataType, Dim>;\n\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  // Not inputs to the eq, but needed to make some variables\n  // that are computed by raising something in order for symmetric\n  // assumptions to hold\n  const auto inverse_spacetime_metric =\n      make_with_random_values<tnsr::AA<DataType, Dim>>(generator, distribution,\n                                                       used_for_size);\n  const auto phi = make_with_random_values<tnsr::iaa<DataType, Dim>>(\n      generator, distribution, used_for_size);\n\n  // RHS: spacetime_deriv_gauge_function\n  const spacetime_deriv_gauge_function_type spacetime_deriv_gauge_function =\n      make_with_random_values<spacetime_deriv_gauge_function_type>(\n          generator, distribution, used_for_size);\n\n  // RHS: pi_two_normals\n  const pi_two_normals_type pi_two_normals =\n      make_with_random_values<pi_two_normals_type>(generator, distribution,\n                                                   used_for_size);\n\n  // RHS: pi\n  const pi_type pi =\n      make_with_random_values<pi_type>(generator, distribution, used_for_size);\n\n  // RHS: gamma0\n  const gamma0_type gamma0 = make_with_random_values<gamma0_type>(\n      generator, distribution, used_for_size);\n\n  // RHS: normal_spacetime_one_form\n  const normal_spacetime_one_form_type normal_spacetime_one_form =\n      make_with_random_values<normal_spacetime_one_form_type>(\n          generator, distribution, used_for_size);\n\n  // RHS: gauge_constraint\n  const gauge_constraint_type gauge_constraint =\n      make_with_random_values<gauge_constraint_type>(generator, distribution,\n                                                     used_for_size);\n\n  // RHS: spacetime_metric\n  const spacetime_metric_type spacetime_metric =\n      make_with_random_values<spacetime_metric_type>(generator, distribution,\n                                                     used_for_size);\n\n  // RHS: normal_dot_gauge_constraint\n  const normal_dot_gauge_constraint_type normal_dot_gauge_constraint =\n      make_with_random_values<normal_dot_gauge_constraint_type>(\n          generator, distribution, used_for_size);\n\n  // RHS: christoffel_second_kind\n  const christoffel_second_kind_type christoffel_second_kind =\n      make_with_random_values<christoffel_second_kind_type>(\n          generator, distribution, used_for_size);\n\n  // RHS: gauge_function\n  const gauge_function_type gauge_function =\n      make_with_random_values<gauge_function_type>(generator, distribution,\n                                                   used_for_size);\n\n  // RHS: pi_2_up\n  pi_2_up_type pi_2_up(used_for_size);\n  for (size_t a = 0; a < Dim + 1; a++) {\n    for (size_t b = 0; b < Dim + 1; b++) {\n      pi_2_up.get(a, b) = 0.0;\n      for (size_t c = 0; c < Dim + 1; c++) {\n        pi_2_up.get(a, b) += inverse_spacetime_metric.get(c, b) * pi.get(a, c);\n      }\n    }\n  }\n\n  // RHS: phi_3_up\n  phi_3_up_type phi_3_up(used_for_size);\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t a = 0; a < Dim + 1; a++) {\n      for (size_t b = 0; b < Dim + 1; b++) {\n        phi_3_up.get(i, a, b) = 0.0;\n        for (size_t c = 0; c < Dim + 1; c++) {\n          phi_3_up.get(i, a, b) +=\n              inverse_spacetime_metric.get(c, b) * phi.get(i, a, c);\n        }\n      }\n    }\n  }\n\n  // RHS: christoffel_first_kind_3_up\n  const christoffel_first_kind_3_up_type christoffel_first_kind_3_up =\n      make_with_random_values<christoffel_first_kind_3_up_type>(\n          generator, distribution, used_for_size);\n\n  // RHS: pi_one_normal\n  const pi_one_normal_type pi_one_normal =\n      make_with_random_values<pi_one_normal_type>(generator, distribution,\n                                                  used_for_size);\n\n  // RHS: inverse_spatial_metric\n  inverse_spatial_metric_type inverse_spatial_metric(used_for_size);\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = 0; j < Dim; j++) {\n      inverse_spatial_metric.get(i, j) =\n          inverse_spacetime_metric.get(i + 1, j + 1);\n    }\n  }\n\n  // RHS: phi_1_up\n  // Note: arg out of order down here bc needs inverse_spatial_metric\n  phi_1_up_type phi_1_up(used_for_size);\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t a = 0; a < Dim + 1; a++) {\n      for (size_t b = a; b < Dim + 1; b++) {\n        phi_1_up.get(i, a, b) = 0.0;\n        for (size_t j = 0; j < Dim; j++) {\n          phi_1_up.get(i, a, b) +=\n              inverse_spatial_metric.get(i, j) * phi.get(j, a, b);\n        }\n      }\n    }\n  }\n\n  // RHS: d_phi\n  const d_phi_type d_phi = make_with_random_values<d_phi_type>(\n      generator, distribution, used_for_size);\n\n  // RHS: lapse\n  const lapse_type lapse = make_with_random_values<lapse_type>(\n      generator, distribution, used_for_size);\n\n  // RHS: gamma1gamma2\n  const gamma1gamma2_type gamma1gamma2 =\n      make_with_random_values<gamma1gamma2_type>(generator, distribution,\n                                                 used_for_size);\n\n  // RHS: shift_dot_three_index_constraint\n  const shift_dot_three_index_constraint_type shift_dot_three_index_constraint =\n      make_with_random_values<shift_dot_three_index_constraint_type>(\n          generator, distribution, used_for_size);\n\n  // RHS: shift\n  const shift_type shift = make_with_random_values<shift_type>(\n      generator, distribution, used_for_size);\n\n  // RHS: d_pi\n  const d_pi_type d_pi = make_with_random_values<d_pi_type>(\n      generator, distribution, used_for_size);\n\n  // LHS: dt_pi\n  const result_tensor_type expected_result_tensor =\n      compute_expected_large_equation(\n          spacetime_deriv_gauge_function, pi_two_normals, pi, gamma0,\n          normal_spacetime_one_form, gauge_constraint, spacetime_metric,\n          normal_dot_gauge_constraint, christoffel_second_kind, gauge_function,\n          pi_2_up, phi_1_up, phi_3_up, christoffel_first_kind_3_up,\n          pi_one_normal, inverse_spatial_metric, d_phi, lapse, gamma1gamma2,\n          shift_dot_three_index_constraint, shift, d_pi);\n\n  // LHS: dt_pi\n  // [use_update]\n  result_tensor_type actual_result_tensor_filled{};\n  tenex::evaluate<ti::a, ti::b>(\n      make_not_null(&actual_result_tensor_filled),\n      -spacetime_deriv_gauge_function(ti::a, ti::b) -\n          spacetime_deriv_gauge_function(ti::b, ti::a) -\n          0.5 * pi_two_normals() * pi(ti::a, ti::b) +\n          gamma0() *\n              (normal_spacetime_one_form(ti::a) * gauge_constraint(ti::b) +\n               normal_spacetime_one_form(ti::b) * gauge_constraint(ti::a)) -\n          gamma0() * spacetime_metric(ti::a, ti::b) *\n              normal_dot_gauge_constraint() +\n          2.0 * christoffel_second_kind(ti::C, ti::a, ti::b) *\n              gauge_function(ti::c) -\n          2.0 * pi(ti::a, ti::c) * pi_2_up(ti::b, ti::C));\n\n  tenex::update<ti::a, ti::b>(\n      make_not_null(&actual_result_tensor_filled),\n      actual_result_tensor_filled(ti::a, ti::b) +\n          2.0 * phi_1_up(ti::I, ti::a, ti::c) * phi_3_up(ti::i, ti::b, ti::C));\n\n  tenex::update<ti::a, ti::b>(\n      make_not_null(&actual_result_tensor_filled),\n      actual_result_tensor_filled(ti::a, ti::b) -\n          2.0 * christoffel_first_kind_3_up(ti::a, ti::d, ti::C) *\n              christoffel_first_kind_3_up(ti::b, ti::c, ti::D));\n\n  tenex::update<ti::a, ti::b>(\n      make_not_null(&actual_result_tensor_filled),\n      actual_result_tensor_filled(ti::a, ti::b) -\n          pi_one_normal(ti::j) * phi_1_up(ti::J, ti::a, ti::b));\n\n  tenex::update<ti::a, ti::b>(make_not_null(&actual_result_tensor_filled),\n                              actual_result_tensor_filled(ti::a, ti::b) -\n                                  inverse_spatial_metric(ti::J, ti::K) *\n                                      d_phi(ti::j, ti::k, ti::a, ti::b));\n\n  tenex::update<ti::a, ti::b>(\n      make_not_null(&actual_result_tensor_filled),\n      actual_result_tensor_filled(ti::a, ti::b) * lapse() +\n          gamma1gamma2() * shift_dot_three_index_constraint(ti::a, ti::b) +\n          shift(ti::J) * d_pi(ti::j, ti::a, ti::b));\n  // [use_update]\n\n  Approx approx = Approx::custom().epsilon(1e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(actual_result_tensor_filled,\n                               expected_result_tensor, approx);\n\n  // Test with TempTensor for LHS tensor\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<\n        ::Tags::TempTensor<0, result_tensor_type>,\n        ::Tags::TempTensor<1, spacetime_deriv_gauge_function_type>,\n        ::Tags::TempTensor<2, pi_two_normals_type>,\n        ::Tags::TempTensor<3, pi_type>, ::Tags::TempTensor<4, gamma0_type>,\n        ::Tags::TempTensor<5, normal_spacetime_one_form_type>,\n        ::Tags::TempTensor<6, gauge_constraint_type>,\n        ::Tags::TempTensor<7, spacetime_metric_type>,\n        ::Tags::TempTensor<8, normal_dot_gauge_constraint_type>,\n        ::Tags::TempTensor<9, christoffel_second_kind_type>,\n        ::Tags::TempTensor<10, gauge_function_type>,\n        ::Tags::TempTensor<11, pi_2_up_type>,\n        ::Tags::TempTensor<12, phi_1_up_type>,\n        ::Tags::TempTensor<13, phi_3_up_type>,\n        ::Tags::TempTensor<14, christoffel_first_kind_3_up_type>,\n        ::Tags::TempTensor<15, pi_one_normal_type>,\n        ::Tags::TempTensor<16, inverse_spatial_metric_type>,\n        ::Tags::TempTensor<17, d_phi_type>, ::Tags::TempTensor<18, lapse_type>,\n        ::Tags::TempTensor<19, gamma1gamma2_type>,\n        ::Tags::TempTensor<20, shift_dot_three_index_constraint_type>,\n        ::Tags::TempTensor<21, shift_type>, ::Tags::TempTensor<22, d_pi_type>>>\n        vars{used_for_size.size()};\n\n    // RHS: spacetime_deriv_gauge_function\n    spacetime_deriv_gauge_function_type& spacetime_deriv_gauge_function_temp =\n        get<::Tags::TempTensor<1, spacetime_deriv_gauge_function_type>>(vars);\n    spacetime_deriv_gauge_function_temp = spacetime_deriv_gauge_function;\n\n    // RHS: pi_two_normals\n    pi_two_normals_type& pi_two_normals_temp =\n        get<::Tags::TempTensor<2, pi_two_normals_type>>(vars);\n    pi_two_normals_temp = pi_two_normals;\n\n    // RHS: pi\n    pi_type& pi_temp = get<::Tags::TempTensor<3, pi_type>>(vars);\n    pi_temp = pi;\n\n    // RHS: gamma0\n    gamma0_type& gamma0_temp = get<::Tags::TempTensor<4, gamma0_type>>(vars);\n    gamma0_temp = gamma0;\n\n    // RHS: normal_spacetime_one_form\n    normal_spacetime_one_form_type& normal_spacetime_one_form_temp =\n        get<::Tags::TempTensor<5, normal_spacetime_one_form_type>>(vars);\n    normal_spacetime_one_form_temp = normal_spacetime_one_form;\n\n    // RHS: gauge_constraint\n    gauge_constraint_type& gauge_constraint_temp =\n        get<::Tags::TempTensor<6, gauge_constraint_type>>(vars);\n    gauge_constraint_temp = gauge_constraint;\n\n    // RHS: spacetime_metric\n    spacetime_metric_type& spacetime_metric_temp =\n        get<::Tags::TempTensor<7, spacetime_metric_type>>(vars);\n    spacetime_metric_temp = spacetime_metric;\n\n    // RHS: normal_dot_gauge_constraint\n    normal_dot_gauge_constraint_type& normal_dot_gauge_constraint_temp =\n        get<::Tags::TempTensor<8, normal_dot_gauge_constraint_type>>(vars);\n    normal_dot_gauge_constraint_temp = normal_dot_gauge_constraint;\n\n    // RHS: christoffel_second_kind\n    christoffel_second_kind_type& christoffel_second_kind_temp =\n        get<::Tags::TempTensor<9, christoffel_second_kind_type>>(vars);\n    christoffel_second_kind_temp = christoffel_second_kind;\n\n    // RHS: gauge_function\n    gauge_function_type& gauge_function_temp =\n        get<::Tags::TempTensor<10, gauge_function_type>>(vars);\n    gauge_function_temp = gauge_function;\n\n    // RHS: pi_2_up\n    pi_2_up_type& pi_2_up_temp =\n        get<::Tags::TempTensor<11, pi_2_up_type>>(vars);\n    pi_2_up_temp = pi_2_up;\n\n    // RHS: phi_1_up\n    phi_1_up_type& phi_1_up_temp =\n        get<::Tags::TempTensor<12, phi_1_up_type>>(vars);\n    phi_1_up_temp = phi_1_up;\n\n    // RHS: phi_3_up\n    phi_3_up_type& phi_3_up_temp =\n        get<::Tags::TempTensor<13, phi_3_up_type>>(vars);\n    phi_3_up_temp = phi_3_up;\n\n    // RHS: christoffel_first_kind_3_up\n    christoffel_first_kind_3_up_type& christoffel_first_kind_3_up_temp =\n        get<::Tags::TempTensor<14, christoffel_first_kind_3_up_type>>(vars);\n    christoffel_first_kind_3_up_temp = christoffel_first_kind_3_up;\n\n    // RHS: pi_one_normal\n    pi_one_normal_type& pi_one_normal_temp =\n        get<::Tags::TempTensor<15, pi_one_normal_type>>(vars);\n    pi_one_normal_temp = pi_one_normal;\n\n    // RHS: inverse_spatial_metric\n    inverse_spatial_metric_type& inverse_spatial_metric_temp =\n        get<::Tags::TempTensor<16, inverse_spatial_metric_type>>(vars);\n    inverse_spatial_metric_temp = inverse_spatial_metric;\n\n    // RHS: d_phi\n    d_phi_type& d_phi_temp = get<::Tags::TempTensor<17, d_phi_type>>(vars);\n    d_phi_temp = d_phi;\n\n    // RHS: lapse\n    lapse_type& lapse_temp = get<::Tags::TempTensor<18, lapse_type>>(vars);\n    lapse_temp = lapse;\n\n    // RHS: gamma1gamma2\n    gamma1gamma2_type& gamma1gamma2_temp =\n        get<::Tags::TempTensor<19, gamma1gamma2_type>>(vars);\n    gamma1gamma2_temp = gamma1gamma2;\n\n    // RHS: shift_dot_three_index_constraint\n    shift_dot_three_index_constraint_type&\n        shift_dot_three_index_constraint_temp =\n            get<::Tags::TempTensor<20, shift_dot_three_index_constraint_type>>(\n                vars);\n    shift_dot_three_index_constraint_temp = shift_dot_three_index_constraint;\n\n    // RHS: shift\n    shift_type& shift_temp = get<::Tags::TempTensor<21, shift_type>>(vars);\n    shift_temp = shift;\n\n    // RHS: d_pi\n    d_pi_type& d_pi_temp = get<::Tags::TempTensor<22, d_pi_type>>(vars);\n    d_pi_temp = d_pi;\n\n    // LHS: dt_pi\n    result_tensor_type& actual_result_tensor_temp =\n        get<::Tags::TempTensor<0, result_tensor_type>>(vars);\n\n    tenex::evaluate<ti::a, ti::b>(\n        make_not_null(&actual_result_tensor_temp),\n        -spacetime_deriv_gauge_function_temp(ti::a, ti::b) -\n            spacetime_deriv_gauge_function_temp(ti::b, ti::a) -\n            0.5 * pi_two_normals_temp() * pi_temp(ti::a, ti::b) +\n            gamma0_temp() * (normal_spacetime_one_form_temp(ti::a) *\n                                 gauge_constraint_temp(ti::b) +\n                             normal_spacetime_one_form_temp(ti::b) *\n                                 gauge_constraint_temp(ti::a)) -\n            gamma0_temp() * spacetime_metric_temp(ti::a, ti::b) *\n                normal_dot_gauge_constraint_temp() +\n            2.0 * christoffel_second_kind_temp(ti::C, ti::a, ti::b) *\n                gauge_function_temp(ti::c) -\n            2.0 * pi_temp(ti::a, ti::c) * pi_2_up_temp(ti::b, ti::C) +\n            2.0 * phi_3_up_temp(ti::i, ti::a, ti::C) *\n                phi_1_up_temp(ti::I, ti::b, ti::c) -\n            2.0 * christoffel_first_kind_3_up_temp(ti::a, ti::d, ti::C) *\n                christoffel_first_kind_3_up_temp(ti::b, ti::c, ti::D));\n\n    tenex::update<ti::a, ti::b>(\n        make_not_null(&actual_result_tensor_temp),\n        actual_result_tensor_temp(ti::a, ti::b) -\n            pi_one_normal_temp(ti::j) * phi_1_up_temp(ti::J, ti::a, ti::b));\n\n    tenex::update<ti::a, ti::b>(make_not_null(&actual_result_tensor_temp),\n                                actual_result_tensor_temp(ti::a, ti::b) -\n                                    inverse_spatial_metric_temp(ti::J, ti::K) *\n                                        d_phi_temp(ti::j, ti::k, ti::a, ti::b));\n\n    tenex::update<ti::a, ti::b>(\n        make_not_null(&actual_result_tensor_temp),\n        actual_result_tensor_temp(ti::a, ti::b) * lapse_temp() +\n            gamma1gamma2_temp() *\n                shift_dot_three_index_constraint_temp(ti::a, ti::b) +\n            shift_temp(ti::J) * d_pi_temp(ti::j, ti::a, ti::b));\n\n    CHECK_ITERABLE_CUSTOM_APPROX(actual_result_tensor_temp,\n                                 expected_result_tensor, approx);\n  }\n}\n\n// Tests the assignment of a RHS `double` to a LHS tensor. Includes testing\n// multiple calls to `evaluate` that fill the time and spatial components of the\n// result tensor, respectively.\ntemplate <typename DataType>\nvoid test_assign_double(const DataType& used_for_size) {\n  // [assign_double_to_index_subsets]\n  tnsr::iab<DataType, 3, Frame::Inertial> L_tensor(used_for_size);\n  const gsl::not_null<tnsr::iab<DataType, 3, Frame::Inertial>*> L =\n      make_not_null(&L_tensor);\n\n  // \\f$L_{itt} = 8.2\\f$\n  tenex::evaluate<ti::i, ti::t, ti::t>(L, 8.2);\n  // \\f$L_{itj} = 2.2\\f$\n  tenex::evaluate<ti::i, ti::t, ti::j>(L, 2.2);\n  // \\f$L_{ijt} = -1.9\\f$\n  tenex::evaluate<ti::i, ti::j, ti::t>(L, -1.9);\n  // \\f$L_{ijk} = -0.5\\f$\n  tenex::evaluate<ti::i, ti::j, ti::k>(L, -0.5);\n  // [assign_double_to_index_subsets]\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t a = 0; a < 4; a++) {\n      for (size_t b = 0; b < 4; b++) {\n        DataType expected_result =\n            make_with_value<DataType>(used_for_size, 0.0);\n\n        if (a == 0 and b == 0) {\n          expected_result = 8.2;\n        } else if (a == 0) {\n          expected_result = 2.2;\n        } else if (b == 0) {\n          expected_result = -1.9;\n        } else {\n          expected_result = -0.5;\n        }\n\n        CHECK(L->get(i, a, b) == expected_result);\n      }\n    }\n  }\n\n  // Test with TempTensor for LHS tensor\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<\n        ::Tags::TempTensor<1, tnsr::iab<DataType, 3, Frame::Inertial>>>>\n        L_temp_var{used_for_size.size()};\n    tnsr::iab<DataType, 3, Frame::Inertial>& L_temp_tensor =\n        get<::Tags::TempTensor<1, tnsr::iab<DataType, 3, Frame::Inertial>>>(\n            L_temp_var);\n    const gsl::not_null<tnsr::iab<DataType, 3, Frame::Inertial>*> L_temp =\n        make_not_null(&L_temp_tensor);\n\n    // \\f$L_{itt} = 8.2\\f$\n    tenex::evaluate<ti::i, ti::t, ti::t>(L_temp, 8.2);\n    // \\f$L_{itj} = 2.2\\f$\n    tenex::evaluate<ti::i, ti::t, ti::j>(L_temp, 2.2);\n    // \\f$L_{ijt} = -1.9\\f$\n    tenex::evaluate<ti::i, ti::j, ti::t>(L_temp, -1.9);\n    // \\f$L_{ijk} = -0.5\\f$\n    tenex::evaluate<ti::i, ti::j, ti::k>(L_temp, -0.5);\n\n    for (size_t i = 0; i < 3; i++) {\n      for (size_t a = 0; a < 4; a++) {\n        for (size_t b = 0; b < 4; b++) {\n          DataType expected_result =\n              make_with_value<DataType>(used_for_size, 0.0);\n\n          if (a == 0 and b == 0) {\n            expected_result = 8.2;\n          } else if (a == 0) {\n            expected_result = 2.2;\n          } else if (b == 0) {\n            expected_result = -1.9;\n          } else {\n            expected_result = -0.5;\n          }\n\n          CHECK(L_temp->get(i, a, b) == expected_result);\n        }\n      }\n    }\n  }\n}\n\n// Test cases include equations with a mixture of arithmetic operations or more\n// that one assignment of the LHS tensor (more than one call to\n// `tenex::evaluate` or `tenex::update`)\ntemplate <typename Generator, typename DataType>\nvoid test_mixed_operations(const gsl::not_null<Generator*> generator,\n                           const DataType& used_for_size) {\n  test_mixed_arithmetic_ops(generator, used_for_size);\n  test_rhs_spacetime_index_subsets(generator, used_for_size);\n  test_lhs_spacetime_index_subsets(generator, used_for_size);\n  test_large_equation(generator, used_for_size);\n  test_assign_double(used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.MixedOperations\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  test_mixed_operations(make_not_null(&generator),\n                        std::numeric_limits<double>::signaling_NaN());\n  test_mixed_operations(\n      make_not_null(&generator),\n      std::complex<double>(std::numeric_limits<double>::signaling_NaN(),\n                           std::numeric_limits<double>::signaling_NaN()));\n  test_mixed_operations(\n      make_not_null(&generator),\n      DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n  test_mixed_operations(\n      make_not_null(&generator),\n      ComplexDataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_Negate.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <complex>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n// Checks that the number of ops in the expressions match what is expected\nvoid test_tensor_ops_properties() {\n  const Scalar<double> G{5.0};\n  const double H = 5.0;\n  const tnsr::ii<double, 3> R{};\n  const tnsr::ij<double, 3> S{};\n\n  const auto neg_G = -G();\n  const auto neg_R = -R(ti::i, ti::j);\n  // Below, -H should be `NumberAsExpression(-H)`, so the * should be the only\n  // op for this expression\n  const auto neg_H_times_G = -H * G();\n  const auto neg_S_minus_R_times_G = -(S(ti::j, ti::i) - R(ti::i, ti::j) * G());\n\n  CHECK(neg_G.num_ops_subtree == 1);\n  CHECK(neg_R.num_ops_subtree == 1);\n  CHECK(neg_H_times_G.num_ops_subtree == 1);\n  CHECK(neg_S_minus_R_times_G.num_ops_subtree == 3);\n}\n\n// \\brief Test the unary `-` correctly negates a tensor expression\n//\n// \\details\n// The cases tested are:\n// - \\f$L_{ji}{}^{k} = -R^{k}_{ji}\\f$\n// - \\f$L^{i}{}_{kj} = -(R^{i}_{kj} + S^{i}_{kj})\\f$\n//\n// \\tparam DataType the type of data being stored in the tensors\ntemplate <typename Generator, typename DataType>\nvoid test_negate(const gsl::not_null<Generator*> generator,\n                 const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-2.0, 2.0);\n  constexpr size_t dim = 3;\n\n  const auto R =\n      make_with_random_values<tnsr::Ijj<DataType, dim, Frame::Inertial>>(\n          generator, make_not_null(&distribution), used_for_size);\n  const auto S =\n      make_with_random_values<tnsr::Ijj<DataType, dim, Frame::Inertial>>(\n          generator, make_not_null(&distribution), used_for_size);\n\n  // \\f$L_{ji}{}^{k} = -R^{k}_{ji}\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const Tensor<DataType, Symmetry<2, 2, 1>,\n               index_list<SpatialIndex<dim, UpLo::Lo, Frame::Inertial>,\n                          SpatialIndex<dim, UpLo::Lo, Frame::Inertial>,\n                          SpatialIndex<dim, UpLo::Up, Frame::Inertial>>>\n      result1 = tenex::evaluate<ti::j, ti::i, ti::K>(-R(ti::K, ti::j, ti::i));\n  // \\f$L^{i}{}_{kj} = -(R^{i}_{kj} + S^{i}_{kj})\\f$\n  const tnsr::Ijj<DataType, dim> result2 = tenex::evaluate<ti::I, ti::k, ti::j>(\n      -(R(ti::I, ti::k, ti::j) + S(ti::I, ti::k, ti::j)));\n\n  for (size_t i = 0; i < dim; i++) {\n    for (size_t j = 0; j < dim; j++) {\n      for (size_t k = 0; k < dim; k++) {\n        CHECK(result1.get(j, i, k) == -R.get(k, j, i));\n        CHECK(result2.get(i, k, j) == -R.get(i, k, j) - S.get(i, k, j));\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.Negate\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  test_tensor_ops_properties();\n  test_negate(make_not_null(&generator),\n              std::numeric_limits<double>::signaling_NaN());\n  test_negate(\n      make_not_null(&generator),\n      std::complex<double>(std::numeric_limits<double>::signaling_NaN(),\n                           std::numeric_limits<double>::signaling_NaN()));\n  test_negate(make_not_null(&generator),\n              DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n  test_negate(\n      make_not_null(&generator),\n      ComplexDataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_Product.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <complex>\n#include <cstddef>\n#include <random>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename... Ts>\n// Checks that the number of ops in the expressions match what is expected\nvoid test_tensor_ops_properties() {\n  const Scalar<double> G{5.0};\n  const double H = 5.0;\n  const tnsr::II<double, 3> R{};\n  const tnsr::ia<double, 3> S{};\n\n  const auto HG_outer_product = H * G();\n  const auto HGG_outer_product = H * G() * G();\n  const auto RG_outer_product = R(ti::I, ti::J) * G();\n  const auto HSGR_outer_product = H * S(ti::i, ti::j) * G() * R(ti::K, ti::L);\n  // Expected: 3 multiplies + 2 adds = 5 total ops\n  const auto RS_inner_product = R(ti::I, ti::J) * S(ti::j, ti::k);\n  // Expected: 9 multiplies + 8 adds = 17 total ops\n  const auto RS_fully_contract = R(ti::I, ti::J) * S(ti::j, ti::i);\n  // Expected:\n  // If tempIk = R(ti::I, ti::J) * S(ti::j, ti::k) is 5 ops (see above), then\n  // (tempIk) * R(ti::K, ti::L) should be:\n  //      (3 multiplies * (tmpIk ops)) + 3 multiplies + 2 adds = 20 total ops\n  const auto RSR_inner_product =\n      R(ti::I, ti::J) * S(ti::j, ti::k) * R(ti::K, ti::L);\n  // Expected:\n  // If tempij = ((G() * H) * (S(ti::i, ti::j) - S(ti::j, ti::i))), then tempij\n  // should be 2 multiplies + 1 subtract = 3 total ops. Then,\n  // (tempij) * R(ti::I, ti::K) should be:\n  //    (3 multiplies * (tempij ops)) + 3 multiplies + 2 adds = 14 total ops\n  const auto mixed_op_expression =\n      ((G() * H) * (S(ti::i, ti::j) - S(ti::j, ti::i))) * R(ti::I, ti::K);\n\n  CHECK(HG_outer_product.num_ops_subtree == 1);\n  CHECK(HGG_outer_product.num_ops_subtree == 2);\n  CHECK(RG_outer_product.num_ops_subtree == 1);\n  CHECK(HSGR_outer_product.num_ops_subtree == 3);\n  CHECK(RS_inner_product.num_ops_subtree == 5);\n  CHECK(RS_fully_contract.num_ops_subtree == 17);\n  CHECK(RSR_inner_product.num_ops_subtree == 20);\n  CHECK(mixed_op_expression.num_ops_subtree == 14);\n}\n\n// \\brief Test the outer product and of a tensor expression and `double` is\n// correctly evaluated\n//\n// \\details\n// The cases tested are:\n// - \\f$L_{ij} = R * S_{ij}\\f$\n// - \\f$L_{ij} = S_{ij} * R\\f$\n// - \\f$L_{ij} = R * S_{ij} * T\\f$\n//\n// where \\f$R\\f$ and \\f$T\\f$ are `double`s and \\f$S\\f$ and \\f$L\\f$ are Tensors\n// with data type `double` or DataVector.\n//\n// \\tparam DataType the type of data being stored in the tensor operand of the\n// products\ntemplate <typename Generator, typename DataType>\nvoid test_outer_product_double(const gsl::not_null<Generator*> generator,\n                               const DataType& used_for_size) {\n  constexpr size_t dim = 3;\n  using tensor_type =\n      Tensor<DataType, Symmetry<1, 1>,\n             index_list<SpatialIndex<dim, UpLo::Lo, Frame::Inertial>,\n                        SpatialIndex<dim, UpLo::Lo, Frame::Inertial>>>;\n\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto S = make_with_random_values<tensor_type>(generator, distribution,\n                                                      used_for_size);\n\n  // \\f$L_{ij} = R * S_{ij}\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const tensor_type Lij_from_R_Sij =\n      tenex::evaluate<ti::i, ti::j>(5.6 * S(ti::i, ti::j));\n  // \\f$L_{ij} = S_{ij} * R\\f$\n  const tensor_type Lij_from_Sij_R =\n      tenex::evaluate<ti::i, ti::j>(S(ti::i, ti::j) * -8.1);\n  // \\f$L_{ij} = R * S_{ij} * T\\f$\n  const tensor_type Lij_from_R_Sij_T =\n      tenex::evaluate<ti::i, ti::j>(-1.7 * S(ti::i, ti::j) * 0.6);\n\n  for (size_t i = 0; i < dim; i++) {\n    for (size_t j = 0; j < dim; j++) {\n      CHECK_ITERABLE_APPROX(Lij_from_R_Sij.get(i, j), 5.6 * S.get(i, j));\n      CHECK_ITERABLE_APPROX(Lij_from_Sij_R.get(i, j), S.get(i, j) * -8.1);\n      CHECK_ITERABLE_APPROX(Lij_from_R_Sij_T.get(i, j),\n                            -1.7 * S.get(i, j) * 0.6);\n    }\n  }\n}\n\n// \\brief Test the outer product of a rank 0 tensor with another tensor is\n// correctly evaluated\n//\n// \\details\n// The outer product cases tested are:\n// - \\f$L = R * R\\f$\n// - \\f$L = R * R * R\\f$\n// - \\f$L^{a} = R * S^{a}\\f$\n// - \\f$L_{ai} = R * T_{ai}\\f$\n//\n// For the last two cases, both operand orderings are tested. For the last case,\n// both LHS index orderings are tested.\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename Generator, typename DataType>\nvoid test_outer_product_rank_0_operand(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto R = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L = R * R\\f$\n  CHECK_ITERABLE_APPROX(tenex::evaluate(R() * R()).get(), R.get() * R.get());\n  // \\f$L = R * R * R\\f$\n  CHECK_ITERABLE_APPROX(tenex::evaluate(R() * R() * R()).get(),\n                        R.get() * R.get() * R.get());\n\n  const auto Su =\n      make_with_random_values<tnsr::A<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n\n  // \\f$L^{a} = R * S^{a}\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const decltype(Su) LA_from_R_SA = tenex::evaluate<ti::A>(R() * Su(ti::A));\n  // \\f$L^{a} = S^{a} * R\\f$\n  const decltype(Su) LA_from_SA_R = tenex::evaluate<ti::A>(Su(ti::A) * R());\n\n  for (size_t a = 0; a < 4; a++) {\n    CHECK_ITERABLE_APPROX(LA_from_R_SA.get(a), R.get() * Su.get(a));\n    CHECK_ITERABLE_APPROX(LA_from_SA_R.get(a), Su.get(a) * R.get());\n  }\n\n  const auto Tll = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{ai} = R * T_{ai}\\f$\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>\n      Lai_from_R_Tai = tenex::evaluate<ti::a, ti::i>(R() * Tll(ti::a, ti::i));\n  // \\f$L_{ia} = R * T_{ai}\\f$\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                          SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>\n      Lia_from_R_Tai = tenex::evaluate<ti::i, ti::a>(R() * Tll(ti::a, ti::i));\n  // \\f$L_{ai} = T_{ai} * R\\f$\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>\n      Lai_from_Tai_R = tenex::evaluate<ti::a, ti::i>(Tll(ti::a, ti::i) * R());\n  // \\f$L_{ia} = T_{ai} * R\\f$\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                          SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>>>\n      Lia_from_Tai_R = tenex::evaluate<ti::i, ti::a>(Tll(ti::a, ti::i) * R());\n\n  for (size_t a = 0; a < 3; a++) {\n    for (size_t i = 0; i < 3; i++) {\n      const DataType expected_R_Tai_product = R.get() * Tll.get(a, i);\n      CHECK_ITERABLE_APPROX(Lai_from_R_Tai.get(a, i), expected_R_Tai_product);\n      CHECK_ITERABLE_APPROX(Lia_from_R_Tai.get(i, a), expected_R_Tai_product);\n\n      const DataType expected_Tai_R_product = Tll.get(a, i) * R.get();\n      CHECK_ITERABLE_APPROX(Lai_from_Tai_R.get(a, i), expected_Tai_R_product);\n      CHECK_ITERABLE_APPROX(Lia_from_Tai_R.get(i, a), expected_Tai_R_product);\n    }\n  }\n}\n\n// \\brief Test the outer product of rank 1 tensors with another tensor is\n// correctly evaluated\n//\n// \\details\n// The outer product cases tested are:\n// - \\f$L^{a}{}_{i} = R_{i} * S^{a}\\f$\n// - \\f$L^{ja}{}_{i} = R_{i} * S^{a} * T^{j}\\f$\n// - \\f$L_{k}{}^{c}{}_{d} = S^{c} * G_{dk}\\f$\n// - \\f$L^{c}{}_{dk} = G_{dk} * S^{c}\\f$\n//\n// For each case, the LHS index order is different from the order in the\n// operands.\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename Generator, typename DataType>\nvoid test_outer_product_rank_1_operand(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto Rl = make_with_random_values<tnsr::i<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  const auto Su = make_with_random_values<tnsr::A<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L^{a}{}_{i} = R_{i} * S^{a}\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      LAi_from_Ri_SA = tenex::evaluate<ti::A, ti::i>(Rl(ti::i) * Su(ti::A));\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t a = 0; a < 4; a++) {\n      CHECK_ITERABLE_APPROX(LAi_from_Ri_SA.get(a, i), Rl.get(i) * Su.get(a));\n    }\n  }\n\n  const auto Tu = make_with_random_values<tnsr::I<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L^{ja}{}_{i} = R_{i} * S^{a} * T^{j}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                          SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      LJAi_from_Ri_SA_TJ = tenex::evaluate<ti::J, ti::A, ti::i>(\n          Rl(ti::i) * Su(ti::A) * Tu(ti::J));\n\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t a = 0; a < 4; a++) {\n      for (size_t i = 0; i < 3; i++) {\n        CHECK_ITERABLE_APPROX(LJAi_from_Ri_SA_TJ.get(j, a, i),\n                              Rl.get(i) * Su.get(a) * Tu.get(j));\n      }\n    }\n  }\n\n  const auto Gll = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Grid>,\n                        SpatialIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{k}{}^{c}{}_{d} = S^{c} * G_{dk}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                          SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                          SpacetimeIndex<2, UpLo::Lo, Frame::Grid>>>\n      LkCd_from_SC_Gdk =\n          tenex::evaluate<ti::k, ti::C, ti::d>(Su(ti::C) * Gll(ti::d, ti::k));\n  // \\f$L^{c}{}_{dk} = G_{dk} * S^{c}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                          SpacetimeIndex<2, UpLo::Lo, Frame::Grid>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      LCdk_from_Gdk_SC =\n          tenex::evaluate<ti::C, ti::d, ti::k>(Gll(ti::d, ti::k) * Su(ti::C));\n\n  for (size_t k = 0; k < 3; k++) {\n    for (size_t c = 0; c < 4; c++) {\n      for (size_t d = 0; d < 3; d++) {\n        CHECK_ITERABLE_APPROX(LkCd_from_SC_Gdk.get(k, c, d),\n                              Su.get(c) * Gll.get(d, k));\n        CHECK_ITERABLE_APPROX(LCdk_from_Gdk_SC.get(c, d, k),\n                              Gll.get(d, k) * Su.get(c));\n      }\n    }\n  }\n}\n\n// \\brief Test the outer product of two rank 2 tensors is correctly evaluated\n//\n// \\details\n// All LHS index orders are tested. One such example case:\n// \\f$L_{abc}{}^{i} = R_{ab} * S^{i}{}_{c}\\f$\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename Generator, typename DataType>\nvoid test_outer_product_rank_2x2_operands(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  using R_index = SpacetimeIndex<3, UpLo::Lo, Frame::Grid>;\n  using S_first_index = SpatialIndex<4, UpLo::Up, Frame::Grid>;\n  using S_second_index = SpacetimeIndex<2, UpLo::Lo, Frame::Grid>;\n\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto Rll = make_with_random_values<\n      Tensor<DataType, Symmetry<1, 1>, index_list<R_index, R_index>>>(\n      generator, distribution, used_for_size);\n  const auto Sul = make_with_random_values<Tensor<\n      DataType, Symmetry<2, 1>, index_list<S_first_index, S_second_index>>>(\n      generator, distribution, used_for_size);\n\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const Tensor<DataType, Symmetry<3, 3, 2, 1>,\n               index_list<R_index, R_index, S_first_index, S_second_index>>\n      L_abIc = tenex::evaluate<ti::a, ti::b, ti::I, ti::c>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 3, 2, 1>,\n               index_list<R_index, R_index, S_second_index, S_first_index>>\n      L_abcI = tenex::evaluate<ti::a, ti::b, ti::c, ti::I>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 3, 1>,\n               index_list<R_index, S_first_index, R_index, S_second_index>>\n      L_aIbc = tenex::evaluate<ti::a, ti::I, ti::b, ti::c>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 3>,\n               index_list<R_index, S_first_index, S_second_index, R_index>>\n      L_aIcb = tenex::evaluate<ti::a, ti::I, ti::c, ti::b>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 3, 1>,\n               index_list<R_index, S_second_index, R_index, S_first_index>>\n      L_acbI = tenex::evaluate<ti::a, ti::c, ti::b, ti::I>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 3>,\n               index_list<R_index, S_second_index, S_first_index, R_index>>\n      L_acIb = tenex::evaluate<ti::a, ti::c, ti::I, ti::b>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n\n  const Tensor<DataType, Symmetry<3, 3, 2, 1>,\n               index_list<R_index, R_index, S_first_index, S_second_index>>\n      L_baIc = tenex::evaluate<ti::b, ti::a, ti::I, ti::c>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 3, 2, 1>,\n               index_list<R_index, R_index, S_second_index, S_first_index>>\n      L_bacI = tenex::evaluate<ti::b, ti::a, ti::c, ti::I>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 3, 1>,\n               index_list<R_index, S_first_index, R_index, S_second_index>>\n      L_bIac = tenex::evaluate<ti::b, ti::I, ti::a, ti::c>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 3>,\n               index_list<R_index, S_first_index, S_second_index, R_index>>\n      L_bIca = tenex::evaluate<ti::b, ti::I, ti::c, ti::a>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 3, 1>,\n               index_list<R_index, S_second_index, R_index, S_first_index>>\n      L_bcaI = tenex::evaluate<ti::b, ti::c, ti::a, ti::I>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 3>,\n               index_list<R_index, S_second_index, S_first_index, R_index>>\n      L_bcIa = tenex::evaluate<ti::b, ti::c, ti::I, ti::a>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n\n  const Tensor<DataType, Symmetry<3, 2, 2, 1>,\n               index_list<S_first_index, R_index, R_index, S_second_index>>\n      L_Iabc = tenex::evaluate<ti::I, ti::a, ti::b, ti::c>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 2>,\n               index_list<S_first_index, R_index, S_second_index, R_index>>\n      L_Iacb = tenex::evaluate<ti::I, ti::a, ti::c, ti::b>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 2, 1>,\n               index_list<S_first_index, R_index, R_index, S_second_index>>\n      L_Ibac = tenex::evaluate<ti::I, ti::b, ti::a, ti::c>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 2>,\n               index_list<S_first_index, R_index, S_second_index, R_index>>\n      L_Ibca = tenex::evaluate<ti::I, ti::b, ti::c, ti::a>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 1>,\n               index_list<S_first_index, S_second_index, R_index, R_index>>\n      L_Icab = tenex::evaluate<ti::I, ti::c, ti::a, ti::b>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 1>,\n               index_list<S_first_index, S_second_index, R_index, R_index>>\n      L_Icba = tenex::evaluate<ti::I, ti::c, ti::b, ti::a>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n\n  const Tensor<DataType, Symmetry<3, 2, 2, 1>,\n               index_list<S_second_index, R_index, R_index, S_first_index>>\n      L_cabI = tenex::evaluate<ti::c, ti::a, ti::b, ti::I>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 2>,\n               index_list<S_second_index, R_index, S_first_index, R_index>>\n      L_caIb = tenex::evaluate<ti::c, ti::a, ti::I, ti::b>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 2, 1>,\n               index_list<S_second_index, R_index, R_index, S_first_index>>\n      L_cbaI = tenex::evaluate<ti::c, ti::b, ti::a, ti::I>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 2>,\n               index_list<S_second_index, R_index, S_first_index, R_index>>\n      L_cbIa = tenex::evaluate<ti::c, ti::b, ti::I, ti::a>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 1>,\n               index_list<S_second_index, S_first_index, R_index, R_index>>\n      L_cIab = tenex::evaluate<ti::c, ti::I, ti::a, ti::b>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n  const Tensor<DataType, Symmetry<3, 2, 1, 1>,\n               index_list<S_second_index, S_first_index, R_index, R_index>>\n      L_cIba = tenex::evaluate<ti::c, ti::I, ti::b, ti::a>(Rll(ti::a, ti::b) *\n                                                           Sul(ti::I, ti::c));\n\n  for (size_t a = 0; a < R_index::dim; a++) {\n    for (size_t b = 0; b < R_index::dim; b++) {\n      for (size_t i = 0; i < S_first_index::dim; i++) {\n        for (size_t c = 0; c < S_second_index::dim; c++) {\n          CHECK_ITERABLE_APPROX(L_abIc.get(a, b, i, c),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_abcI.get(a, b, c, i),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_aIbc.get(a, i, b, c),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_aIcb.get(a, i, c, b),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_acbI.get(a, c, b, i),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_acIb.get(a, c, i, b),\n                                Rll.get(a, b) * Sul.get(i, c));\n\n          CHECK_ITERABLE_APPROX(L_baIc.get(b, a, i, c),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_bacI.get(b, a, c, i),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_bIac.get(b, i, a, c),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_bIca.get(b, i, c, a),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_bcaI.get(b, c, a, i),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_bcIa.get(b, c, i, a),\n                                Rll.get(a, b) * Sul.get(i, c));\n\n          CHECK_ITERABLE_APPROX(L_Iabc.get(i, a, b, c),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_Iacb.get(i, a, c, b),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_Ibac.get(i, b, a, c),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_Ibca.get(i, b, c, a),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_Icab.get(i, c, a, b),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_Icba.get(i, c, b, a),\n                                Rll.get(a, b) * Sul.get(i, c));\n\n          CHECK_ITERABLE_APPROX(L_cabI.get(c, a, b, i),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_caIb.get(c, a, i, b),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_cbaI.get(c, b, a, i),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_cbIa.get(c, b, i, a),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_cIab.get(c, i, a, b),\n                                Rll.get(a, b) * Sul.get(i, c));\n          CHECK_ITERABLE_APPROX(L_cIba.get(c, i, b, a),\n                                Rll.get(a, b) * Sul.get(i, c));\n        }\n      }\n    }\n  }\n}\n\n// \\brief Test the outer product of a rank 0, rank 1, and rank 2 tensor is\n// correctly evaluated\n//\n// \\details\n// The outer product cases tested are permutations of the form:\n// - \\f$L^{a}{}_{ib} = R * S^{a} * T_{bi}\\f$\n//\n// Each case represents an ordering for the operands and the LHS indices.\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename Generator, typename DataType>\nvoid test_outer_product_rank_0x1x2_operands(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto R = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n\n  using S_index = SpacetimeIndex<3, UpLo::Up, Frame::Inertial>;\n  using T_first_index = SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>;\n  using T_second_index = SpatialIndex<4, UpLo::Lo, Frame::Inertial>;\n\n  const auto Su = make_with_random_values<\n      Tensor<DataType, Symmetry<1>, index_list<S_index>>>(\n      generator, distribution, used_for_size);\n  const auto Tll = make_with_random_values<Tensor<\n      DataType, Symmetry<2, 1>, index_list<T_first_index, T_second_index>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$R * S^{a} * T_{bi}\\f$\n  const auto R_SA_Tbi_expr = R() * Su(ti::A) * Tll(ti::b, ti::i);\n  // \\f$L^{a}{}_{bi} = R * S^{a} * T_{bi}\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_first_index, T_second_index>>\n      LAbi_from_R_SA_Tbi = tenex::evaluate<ti::A, ti::b, ti::i>(R_SA_Tbi_expr);\n  // \\f$L^{a}{}_{ib} = R * S^{a} * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_second_index, T_first_index>>\n      LAib_from_R_SA_Tbi = tenex::evaluate<ti::A, ti::i, ti::b>(R_SA_Tbi_expr);\n  // \\f$L_{b}{}^{a}{}_{i} = R * S^{a} * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, S_index, T_second_index>>\n      LbAi_from_R_SA_Tbi = tenex::evaluate<ti::b, ti::A, ti::i>(R_SA_Tbi_expr);\n  // \\f$L_{bi}{}^{a} = R * S^{a} * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, T_second_index, S_index>>\n      LbiA_from_R_SA_Tbi = tenex::evaluate<ti::b, ti::i, ti::A>(R_SA_Tbi_expr);\n  // \\f$L_{i}{}^{a}{}_{b} = R * S^{a} * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, S_index, T_first_index>>\n      LiAb_from_R_SA_Tbi = tenex::evaluate<ti::i, ti::A, ti::b>(R_SA_Tbi_expr);\n  // \\f$L_{ib}{}^{a} = R * S^{a} * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, T_first_index, S_index>>\n      LibA_from_R_SA_Tbi = tenex::evaluate<ti::i, ti::b, ti::A>(R_SA_Tbi_expr);\n\n  // \\f$R * T_{bi} * S^{a}\\f$\n  const auto R_Tbi_SA_expr = R() * Tll(ti::b, ti::i) * Su(ti::A);\n  // \\f$L^{a}{}_{bi} = R * T_{bi} * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_first_index, T_second_index>>\n      LAbi_from_R_Tbi_SA = tenex::evaluate<ti::A, ti::b, ti::i>(R_Tbi_SA_expr);\n  // \\f$L^{a}{}_{ib} = R * T_{bi} * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_second_index, T_first_index>>\n      LAib_from_R_Tbi_SA = tenex::evaluate<ti::A, ti::i, ti::b>(R_Tbi_SA_expr);\n  // \\f$L_{b}{}^{a}{}_{i} = R * T_{bi} * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, S_index, T_second_index>>\n      LbAi_from_R_Tbi_SA = tenex::evaluate<ti::b, ti::A, ti::i>(R_Tbi_SA_expr);\n  // \\f$L_{bi}{}^{a} = R * R * T_{bi} * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, T_second_index, S_index>>\n      LbiA_from_R_Tbi_SA = tenex::evaluate<ti::b, ti::i, ti::A>(R_Tbi_SA_expr);\n  // \\f$L_{i}{}^{a}{}_{b} = R * T_{bi} * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, S_index, T_first_index>>\n      LiAb_from_R_Tbi_SA = tenex::evaluate<ti::i, ti::A, ti::b>(R_Tbi_SA_expr);\n  // \\f$L_{ib}{}^{a} = R * T_{bi} * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, T_first_index, S_index>>\n      LibA_from_R_Tbi_SA = tenex::evaluate<ti::i, ti::b, ti::A>(R_Tbi_SA_expr);\n\n  // \\f$S^{a} * R * T_{bi}\\f$\n  const auto SA_R_Tbi_expr = Su(ti::A) * R() * Tll(ti::b, ti::i);\n  // \\f$L^{a}{}_{bi} = S^{a} * R * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_first_index, T_second_index>>\n      LAbi_from_SA_R_Tbi = tenex::evaluate<ti::A, ti::b, ti::i>(SA_R_Tbi_expr);\n  // \\f$L^{a}{}_{ib} = S^{a} * R * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_second_index, T_first_index>>\n      LAib_from_SA_R_Tbi = tenex::evaluate<ti::A, ti::i, ti::b>(SA_R_Tbi_expr);\n  // \\f$L_{b}{}^{a}{}_{i} = S^{a} * R * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, S_index, T_second_index>>\n      LbAi_from_SA_R_Tbi = tenex::evaluate<ti::b, ti::A, ti::i>(SA_R_Tbi_expr);\n  // \\f$L_{bi}{}^{a} = S^{a} * R * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, T_second_index, S_index>>\n      LbiA_from_SA_R_Tbi = tenex::evaluate<ti::b, ti::i, ti::A>(SA_R_Tbi_expr);\n  // \\f$L_{i}{}^{a}{}_{b} = S^{a} * R * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, S_index, T_first_index>>\n      LiAb_from_SA_R_Tbi = tenex::evaluate<ti::i, ti::A, ti::b>(SA_R_Tbi_expr);\n  // \\f$L_{ib}{}^{a} = S^{a} * R * T_{bi}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, T_first_index, S_index>>\n      LibA_from_SA_R_Tbi = tenex::evaluate<ti::i, ti::b, ti::A>(SA_R_Tbi_expr);\n\n  // \\f$S^{a} * T_{bi} * R\\f$\n  const auto SA_Tbi_R_expr = Su(ti::A) * Tll(ti::b, ti::i) * R();\n  // \\f$L^{a}{}_{bi} = S^{a} * T_{bi} * R\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_first_index, T_second_index>>\n      LAbi_from_SA_Tbi_R = tenex::evaluate<ti::A, ti::b, ti::i>(SA_Tbi_R_expr);\n  // \\f$L^{a}{}_{ib} = S^{a} * T_{bi} * R\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_second_index, T_first_index>>\n      LAib_from_SA_Tbi_R = tenex::evaluate<ti::A, ti::i, ti::b>(SA_Tbi_R_expr);\n  // \\f$L_{b}{}^{a}{}_{i} = S^{a} * T_{bi} * R\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, S_index, T_second_index>>\n      LbAi_from_SA_Tbi_R = tenex::evaluate<ti::b, ti::A, ti::i>(SA_Tbi_R_expr);\n  // \\f$L_{bi}{}^{a} = S^{a} * T_{bi} * R\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, T_second_index, S_index>>\n      LbiA_from_SA_Tbi_R = tenex::evaluate<ti::b, ti::i, ti::A>(SA_Tbi_R_expr);\n  // \\f$L_{i}{}^{a}{}_{b} = S^{a} * T_{bi} * R\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, S_index, T_first_index>>\n      LiAb_from_SA_Tbi_R = tenex::evaluate<ti::i, ti::A, ti::b>(SA_Tbi_R_expr);\n  // \\f$L_{ib}{}^{a} = S^{a} * T_{bi} * R\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, T_first_index, S_index>>\n      LibA_from_SA_Tbi_R = tenex::evaluate<ti::i, ti::b, ti::A>(SA_Tbi_R_expr);\n\n  // \\f$T_{bi} * R * S^{a}\\f$\n  const auto Tbi_R_SA_expr = Tll(ti::b, ti::i) * R() * Su(ti::A);\n  // \\f$L^{a}{}_{bi} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_first_index, T_second_index>>\n      LAbi_from_Tbi_R_SA = tenex::evaluate<ti::A, ti::b, ti::i>(Tbi_R_SA_expr);\n  // \\f$L^{a}{}_{ib} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_second_index, T_first_index>>\n      LAib_from_Tbi_R_SA = tenex::evaluate<ti::A, ti::i, ti::b>(Tbi_R_SA_expr);\n  // \\f$L_{b}{}^{a}{}_{i} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, S_index, T_second_index>>\n      LbAi_from_Tbi_R_SA = tenex::evaluate<ti::b, ti::A, ti::i>(Tbi_R_SA_expr);\n  // \\f$L_{bi}{}^{a} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, T_second_index, S_index>>\n      LbiA_from_Tbi_R_SA = tenex::evaluate<ti::b, ti::i, ti::A>(Tbi_R_SA_expr);\n  // \\f$L_{i}{}^{a}{}_{b} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, S_index, T_first_index>>\n      LiAb_from_Tbi_R_SA = tenex::evaluate<ti::i, ti::A, ti::b>(Tbi_R_SA_expr);\n  // \\f$L_{ib}{}^{a} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, T_first_index, S_index>>\n      LibA_from_Tbi_R_SA = tenex::evaluate<ti::i, ti::b, ti::A>(Tbi_R_SA_expr);\n\n  // \\f$T_{bi} * S^{a} * R\\f$\n  const auto Tbi_SA_R_expr = Tll(ti::b, ti::i) * Su(ti::A) * R();\n  // \\f$L^{a}{}_{bi} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_first_index, T_second_index>>\n      LAbi_from_Tbi_SA_R = tenex::evaluate<ti::A, ti::b, ti::i>(Tbi_SA_R_expr);\n  // \\f$L^{a}{}_{ib} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<S_index, T_second_index, T_first_index>>\n      LAib_from_Tbi_SA_R = tenex::evaluate<ti::A, ti::i, ti::b>(Tbi_SA_R_expr);\n  // \\f$L_{b}{}^{a}{}_{i} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, S_index, T_second_index>>\n      LbAi_from_Tbi_SA_R = tenex::evaluate<ti::b, ti::A, ti::i>(Tbi_SA_R_expr);\n  // \\f$L_{bi}{}^{a} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_first_index, T_second_index, S_index>>\n      LbiA_from_Tbi_SA_R = tenex::evaluate<ti::b, ti::i, ti::A>(Tbi_SA_R_expr);\n  // \\f$L_{i}{}^{a}{}_{b} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, S_index, T_first_index>>\n      LiAb_from_Tbi_SA_R = tenex::evaluate<ti::i, ti::A, ti::b>(Tbi_SA_R_expr);\n  // \\f$L_{ib}{}^{a} = T_{bi} * R * S^{a}\\f$\n  const Tensor<DataType, Symmetry<3, 2, 1>,\n               index_list<T_second_index, T_first_index, S_index>>\n      LibA_from_Tbi_SA_R = tenex::evaluate<ti::i, ti::b, ti::A>(Tbi_SA_R_expr);\n\n  for (size_t a = 0; a < S_index::dim; a++) {\n    for (size_t b = 0; b < T_first_index::dim; b++) {\n      for (size_t i = 0; i < T_second_index::dim; i++) {\n        const DataType expected_product = R.get() * Su.get(a) * Tll.get(b, i);\n\n        CHECK_ITERABLE_APPROX(LAbi_from_R_SA_Tbi.get(a, b, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LAib_from_R_SA_Tbi.get(a, i, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbAi_from_R_SA_Tbi.get(b, a, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbiA_from_R_SA_Tbi.get(b, i, a),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LiAb_from_R_SA_Tbi.get(i, a, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LibA_from_R_SA_Tbi.get(i, b, a),\n                              expected_product);\n\n        CHECK_ITERABLE_APPROX(LAbi_from_R_Tbi_SA.get(a, b, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LAib_from_R_Tbi_SA.get(a, i, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbAi_from_R_Tbi_SA.get(b, a, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbiA_from_R_Tbi_SA.get(b, i, a),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LiAb_from_R_Tbi_SA.get(i, a, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LibA_from_R_Tbi_SA.get(i, b, a),\n                              expected_product);\n\n        CHECK_ITERABLE_APPROX(LAbi_from_SA_R_Tbi.get(a, b, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LAib_from_SA_R_Tbi.get(a, i, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbAi_from_SA_R_Tbi.get(b, a, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbiA_from_SA_R_Tbi.get(b, i, a),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LiAb_from_SA_R_Tbi.get(i, a, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LibA_from_SA_R_Tbi.get(i, b, a),\n                              expected_product);\n\n        CHECK_ITERABLE_APPROX(LAbi_from_SA_Tbi_R.get(a, b, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LAib_from_SA_Tbi_R.get(a, i, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbAi_from_SA_Tbi_R.get(b, a, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbiA_from_SA_Tbi_R.get(b, i, a),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LiAb_from_SA_Tbi_R.get(i, a, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LibA_from_SA_Tbi_R.get(i, b, a),\n                              expected_product);\n\n        CHECK_ITERABLE_APPROX(LAbi_from_Tbi_R_SA.get(a, b, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LAib_from_Tbi_R_SA.get(a, i, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbAi_from_Tbi_R_SA.get(b, a, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbiA_from_Tbi_R_SA.get(b, i, a),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LiAb_from_Tbi_R_SA.get(i, a, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LibA_from_Tbi_R_SA.get(i, b, a),\n                              expected_product);\n\n        CHECK_ITERABLE_APPROX(LAbi_from_Tbi_SA_R.get(a, b, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LAib_from_Tbi_SA_R.get(a, i, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbAi_from_Tbi_SA_R.get(b, a, i),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LbiA_from_Tbi_SA_R.get(b, i, a),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LiAb_from_Tbi_SA_R.get(i, a, b),\n                              expected_product);\n        CHECK_ITERABLE_APPROX(LibA_from_Tbi_SA_R.get(i, b, a),\n                              expected_product);\n      }\n    }\n  }\n}\n\n// \\brief Test the inner product of two rank 1 tensors is correctly evaluated\n//\n// \\details\n// The inner product cases tested are:\n// - \\f$L = R^{a} * S_{a}\\f$\n// - \\f$L = S_{a} * R^{a}\\f$\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename Generator, typename DataType>\nvoid test_inner_product_rank_1x1_operands(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto Ru = make_with_random_values<tnsr::A<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const auto Sl = make_with_random_values<tnsr::a<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L = R^{a} * S_{a}\\f$\n  const Tensor<DataType> L_from_RA_Sa = tenex::evaluate(Ru(ti::A) * Sl(ti::a));\n  // \\f$L = S_{a} * R^{a}\\f$\n  const Tensor<DataType> L_from_Sa_RA = tenex::evaluate(Sl(ti::a) * Ru(ti::A));\n\n  DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t a = 0; a < 4; a++) {\n    expected_sum += (Ru.get(a) * Sl.get(a));\n  }\n  CHECK_ITERABLE_APPROX(L_from_RA_Sa.get(), expected_sum);\n  CHECK_ITERABLE_APPROX(L_from_Sa_RA.get(), expected_sum);\n}\n\n// \\brief Test the inner product of two rank 2 tensors is correctly evaluated\n//\n// \\details\n// All cases in this test contract both pairs of indices of the two rank 2\n// tensor operands to a resulting rank 0 tensor. For each case, the two tensor\n// operands have one spacetime and one spatial index. Each case is a permutation\n// of the positions of contracted pairs and their valences. One such example\n// case: \\f$L = R_{ai} * S^{ai}\\f$\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename Generator, typename DataType>\nvoid test_inner_product_rank_2x2_operands(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  using lower_spacetime_index = SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>;\n  using upper_spacetime_index = SpacetimeIndex<3, UpLo::Up, Frame::Inertial>;\n  using lower_spatial_index = SpatialIndex<2, UpLo::Lo, Frame::Inertial>;\n  using upper_spatial_index = SpatialIndex<2, UpLo::Up, Frame::Inertial>;\n\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  // All tensor variables starting with 'R' refer to tensors whose first index\n  // is a spacetime index and whose second index is a spatial index. Conversely,\n  // all tensor variables starting with 'S' refer to tensors whose first index\n  // is a spatial index and whose second index is a spacetime index.\n  const auto Rll = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<lower_spacetime_index, lower_spatial_index>>>(\n      generator, distribution, used_for_size);\n  const auto Sll = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<lower_spatial_index, lower_spacetime_index>>>(\n      generator, distribution, used_for_size);\n  const auto Ruu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<upper_spacetime_index, upper_spatial_index>>>(\n      generator, distribution, used_for_size);\n  const auto Suu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<upper_spatial_index, upper_spacetime_index>>>(\n      generator, distribution, used_for_size);\n  const auto Rlu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<lower_spacetime_index, upper_spatial_index>>>(\n      generator, distribution, used_for_size);\n  const auto Slu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<lower_spatial_index, upper_spacetime_index>>>(\n      generator, distribution, used_for_size);\n  const auto Rul = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<upper_spacetime_index, lower_spatial_index>>>(\n      generator, distribution, used_for_size);\n  const auto Sul = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<upper_spatial_index, lower_spacetime_index>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L = Rll_{ai} * Ruu^{ai}\\f$\n  const Tensor<DataType> L_aiAI_product =\n      tenex::evaluate(Rll(ti::a, ti::i) * Ruu(ti::A, ti::I));\n  // \\f$L = Rll_{ai} * Suu^{ia}\\f$\n  const Tensor<DataType> L_aiIA_product =\n      tenex::evaluate(Rll(ti::a, ti::i) * Suu(ti::I, ti::A));\n  // \\f$L = Ruu^{ai} * Rll_{ai}\\f$\n  const Tensor<DataType> L_AIai_product =\n      tenex::evaluate(Ruu(ti::A, ti::I) * Rll(ti::a, ti::i));\n  // \\f$L = Ruu^{ai} * Sll_{ia}\\f$\n  const Tensor<DataType> L_AIia_product =\n      tenex::evaluate(Ruu(ti::A, ti::I) * Sll(ti::i, ti::a));\n  // \\f$L = Rlu_{a}{}^{i} * Rul^{a}{}_{i}\\f$\n  const Tensor<DataType> L_aIAi_product =\n      tenex::evaluate(Rlu(ti::a, ti::I) * Rul(ti::A, ti::i));\n  // \\f$L = Rlu_{a}{}^{i} * Slu_{i}{}^{a}\\f$\n  const Tensor<DataType> L_aIiA_product =\n      tenex::evaluate(Rlu(ti::a, ti::I) * Slu(ti::i, ti::A));\n  // \\f$L = Rul^{a}{}_{i} * Rlu_{a}{}^{i}\\f$\n  const Tensor<DataType> L_AiaI_product =\n      tenex::evaluate(Rul(ti::A, ti::i) * Rlu(ti::a, ti::I));\n  // \\f$L = Rul^{a}{}_{i} * Sul^{i}{}_{a}\\f$\n  const Tensor<DataType> L_AiIa_product =\n      tenex::evaluate(Rul(ti::A, ti::i) * Sul(ti::I, ti::a));\n\n  DataType L_aiAI_expected_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n  DataType L_aiIA_expected_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n  DataType L_AIai_expected_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n  DataType L_AIia_expected_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n  DataType L_aIAi_expected_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n  DataType L_aIiA_expected_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n  DataType L_AiaI_expected_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n  DataType L_AiIa_expected_product =\n      make_with_value<DataType>(used_for_size, 0.0);\n\n  for (size_t a = 0; a < lower_spacetime_index::dim; a++) {\n    for (size_t i = 0; i < lower_spatial_index::dim; i++) {\n      L_aiAI_expected_product += (Rll.get(a, i) * Ruu.get(a, i));\n      L_aiIA_expected_product += (Rll.get(a, i) * Suu.get(i, a));\n      L_AIai_expected_product += (Ruu.get(a, i) * Rll.get(a, i));\n      L_AIia_expected_product += (Ruu.get(a, i) * Sll.get(i, a));\n      L_aIAi_expected_product += (Rlu.get(a, i) * Rul.get(a, i));\n      L_aIiA_expected_product += (Rlu.get(a, i) * Slu.get(i, a));\n      L_AiaI_expected_product += (Rul.get(a, i) * Rlu.get(a, i));\n      L_AiIa_expected_product += (Rul.get(a, i) * Sul.get(i, a));\n    }\n  }\n  CHECK_ITERABLE_APPROX(L_aiAI_product.get(), L_aiAI_expected_product);\n  CHECK_ITERABLE_APPROX(L_aiIA_product.get(), L_aiIA_expected_product);\n  CHECK_ITERABLE_APPROX(L_AIai_product.get(), L_AIai_expected_product);\n  CHECK_ITERABLE_APPROX(L_AIia_product.get(), L_AIia_expected_product);\n  CHECK_ITERABLE_APPROX(L_aIAi_product.get(), L_aIAi_expected_product);\n  CHECK_ITERABLE_APPROX(L_aIiA_product.get(), L_aIiA_expected_product);\n  CHECK_ITERABLE_APPROX(L_AiaI_product.get(), L_AiaI_expected_product);\n  CHECK_ITERABLE_APPROX(L_AiIa_product.get(), L_AiIa_expected_product);\n}\n\n// \\brief Test the product of two tensors with one pair of indices to contract\n// is correctly evaluated\n//\n// \\details\n// The product cases tested are:\n// - \\f$L_{b} = R_{ab} * T^{a}\\f$\n// - \\f$L_{ac} = R_{ab} * S^{b}_{c}\\f$\n//\n// All cases in this test contract one pair of indices of the two tensor\n// operands. Each case is a permutation of the position of the contracted pair\n// and the ordering of the LHS indices.\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename Generator, typename DataType>\nvoid test_two_term_inner_outer_product(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  using R_index = SpacetimeIndex<3, UpLo::Lo, Frame::Grid>;\n  using T_index = SpacetimeIndex<3, UpLo::Up, Frame::Grid>;\n\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto Rll = make_with_random_values<\n      Tensor<DataType, Symmetry<1, 1>, index_list<R_index, R_index>>>(\n      generator, distribution, used_for_size);\n  const auto Tu = make_with_random_values<\n      Tensor<DataType, Symmetry<1>, index_list<T_index>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{b} = R_{ab} * T^{a}\\f$\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  using Lb = Tensor<DataType, Symmetry<1>, index_list<R_index>>;\n  const Lb Lb_from_Rab_TA =\n      tenex::evaluate<ti::b>(Rll(ti::a, ti::b) * Tu(ti::A));\n  // \\f$L_{b} = R_{ba} * T^{a}\\f$\n  const Lb Lb_from_Rba_TA =\n      tenex::evaluate<ti::b>(Rll(ti::b, ti::a) * Tu(ti::A));\n  // \\f$L_{b} = T^{a} * R_{ab}\\f$\n  const Lb Lb_from_TA_Rab =\n      tenex::evaluate<ti::b>(Tu(ti::A) * Rll(ti::a, ti::b));\n  // \\f$L_{b} = T^{a} * R_{ba}\\f$\n  const Lb Lb_from_TA_Rba =\n      tenex::evaluate<ti::b>(Tu(ti::A) * Rll(ti::b, ti::a));\n\n  for (size_t b = 0; b < R_index::dim; b++) {\n    DataType expected_product = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t a = 0; a < T_index::dim; a++) {\n      expected_product += (Rll.get(a, b) * Tu.get(a));\n    }\n    CHECK_ITERABLE_APPROX(Lb_from_Rab_TA.get(b), expected_product);\n    CHECK_ITERABLE_APPROX(Lb_from_Rba_TA.get(b), expected_product);\n    CHECK_ITERABLE_APPROX(Lb_from_TA_Rab.get(b), expected_product);\n    CHECK_ITERABLE_APPROX(Lb_from_TA_Rba.get(b), expected_product);\n  }\n\n  using S_lower_index = SpacetimeIndex<2, UpLo::Lo, Frame::Grid>;\n  using S_upper_index = SpacetimeIndex<3, UpLo::Up, Frame::Grid>;\n\n  const auto Sul =\n      make_with_random_values<Tensor<DataType, Symmetry<2, 1>,\n                                     index_list<S_upper_index, S_lower_index>>>(\n          generator, distribution, used_for_size);\n  const auto Slu =\n      make_with_random_values<Tensor<DataType, Symmetry<2, 1>,\n                                     index_list<S_lower_index, S_upper_index>>>(\n          generator, distribution, used_for_size);\n\n  // \\f$L_{ac} = R_{ab} * S^{b}_{c}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<R_index, S_lower_index>>\n      L_abBc_to_ac =\n          tenex::evaluate<ti::a, ti::c>(Rll(ti::a, ti::b) * Sul(ti::B, ti::c));\n  // \\f$L_{ca} = R_{ab} * S^{b}_{c}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<S_lower_index, R_index>>\n      L_abBc_to_ca =\n          tenex::evaluate<ti::c, ti::a>(Rll(ti::a, ti::b) * Sul(ti::B, ti::c));\n  // \\f$L_{ac} = R_{ab} * S_{c}^{b}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<R_index, S_lower_index>>\n      L_abcB_to_ac =\n          tenex::evaluate<ti::a, ti::c>(Rll(ti::a, ti::b) * Slu(ti::c, ti::B));\n  // \\f$L_{ca} = R_{ab} * S_{c}^{b}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<S_lower_index, R_index>>\n      L_abcB_to_ca =\n          tenex::evaluate<ti::c, ti::a>(Rll(ti::a, ti::b) * Slu(ti::c, ti::B));\n  // \\f$L_{ac} = R_{ba} * S^{b}_{c}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<R_index, S_lower_index>>\n      L_baBc_to_ac =\n          tenex::evaluate<ti::a, ti::c>(Rll(ti::b, ti::a) * Sul(ti::B, ti::c));\n  // \\f$L_{ca} = R_{ba} * S^{b}_{c}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<S_lower_index, R_index>>\n      L_baBc_to_ca =\n          tenex::evaluate<ti::c, ti::a>(Rll(ti::b, ti::a) * Sul(ti::B, ti::c));\n  // \\f$L_{ac} = R_{ba} * S_{c}^{b}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<R_index, S_lower_index>>\n      L_bacB_to_ac =\n          tenex::evaluate<ti::a, ti::c>(Rll(ti::b, ti::a) * Slu(ti::c, ti::B));\n  // \\f$L_{ca} = R_{ba} * S_{c}^{b}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<S_lower_index, R_index>>\n      L_bacB_to_ca =\n          tenex::evaluate<ti::c, ti::a>(Rll(ti::b, ti::a) * Slu(ti::c, ti::B));\n\n  for (size_t a = 0; a < R_index::dim; a++) {\n    for (size_t c = 0; c < S_lower_index::dim; c++) {\n      DataType L_abBc_expected_product =\n          make_with_value<DataType>(used_for_size, 0.0);\n      DataType L_abcB_expected_product =\n          make_with_value<DataType>(used_for_size, 0.0);\n      DataType L_baBc_expected_product =\n          make_with_value<DataType>(used_for_size, 0.0);\n      DataType L_bacB_expected_product =\n          make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t b = 0; b < 4; b++) {\n        L_abBc_expected_product += (Rll.get(a, b) * Sul.get(b, c));\n        L_abcB_expected_product += (Rll.get(a, b) * Slu.get(c, b));\n        L_baBc_expected_product += (Rll.get(b, a) * Sul.get(b, c));\n        L_bacB_expected_product += (Rll.get(b, a) * Slu.get(c, b));\n      }\n      CHECK_ITERABLE_APPROX(L_abBc_to_ac.get(a, c), L_abBc_expected_product);\n      CHECK_ITERABLE_APPROX(L_abBc_to_ca.get(c, a), L_abBc_expected_product);\n      CHECK_ITERABLE_APPROX(L_abcB_to_ac.get(a, c), L_abcB_expected_product);\n      CHECK_ITERABLE_APPROX(L_abcB_to_ca.get(c, a), L_abcB_expected_product);\n      CHECK_ITERABLE_APPROX(L_baBc_to_ac.get(a, c), L_baBc_expected_product);\n      CHECK_ITERABLE_APPROX(L_baBc_to_ca.get(c, a), L_baBc_expected_product);\n      CHECK_ITERABLE_APPROX(L_bacB_to_ac.get(a, c), L_bacB_expected_product);\n      CHECK_ITERABLE_APPROX(L_bacB_to_ca.get(c, a), L_bacB_expected_product);\n    }\n  }\n}\n\n// \\brief Test the product of three tensors involving both inner and outer\n// products of indices is correctly evaluated\n//\n// \\details\n// The product cases tested are:\n// - \\f$L_{i} = R^{j} * S_{j} * T_{i}\\f$\n// - \\f$L_{i}{}^{k} = S_{j} * T_{i} * G^{jk}\\f$\n//\n// For each case, multiple operand orderings are tested. For the second case,\n// both LHS index orderings are also tested.\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename Generator, typename DataType>\nvoid test_three_term_inner_outer_product(\n    const gsl::not_null<Generator*> generator, const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto Ru =\n      make_with_random_values<tnsr::I<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n  const auto Sl =\n      make_with_random_values<tnsr::i<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n  const auto Tl =\n      make_with_random_values<tnsr::i<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n\n  // \\f$L_{i} = R^{j} * S_{j} * T_{i}\\f$\n  const decltype(Tl) Li_from_Jji =\n      tenex::evaluate<ti::i>(Ru(ti::J) * Sl(ti::j) * Tl(ti::i));\n  // \\f$L_{i} = R^{j} * T_{i} * S_{j}\\f$\n  const decltype(Tl) Li_from_Jij =\n      tenex::evaluate<ti::i>(Ru(ti::J) * Tl(ti::i) * Sl(ti::j));\n  // \\f$L_{i} = T_{i} * S_{j} * R^{j}\\f$\n  const decltype(Tl) Li_from_ijJ =\n      tenex::evaluate<ti::i>(Tl(ti::i) * Sl(ti::j) * Ru(ti::J));\n\n  for (size_t i = 0; i < 3; i++) {\n    DataType expected_product = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t j = 0; j < 3; j++) {\n      expected_product += (Ru.get(j) * Sl.get(j) * Tl.get(i));\n    }\n    CHECK_ITERABLE_APPROX(Li_from_Jji.get(i), expected_product);\n    CHECK_ITERABLE_APPROX(Li_from_Jij.get(i), expected_product);\n    CHECK_ITERABLE_APPROX(Li_from_ijJ.get(i), expected_product);\n  }\n\n  using T_index = tmpl::front<typename decltype(Tl)::index_list>;\n  using G_index = SpatialIndex<3, UpLo::Up, Frame::Inertial>;\n\n  const auto Guu = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>, index_list<G_index, G_index>>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L_{i}{}^{k} = S_{j} * T_{i} * G^{jk}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<T_index, G_index>>\n      LiK_from_Sj_Ti_GJK = tenex::evaluate<ti::i, ti::K>(Sl(ti::j) * Tl(ti::i) *\n                                                         Guu(ti::J, ti::K));\n  // \\f$L^{k}{}_{i} = S_{j} * T_{i} * G^{jk}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<G_index, T_index>>\n      LKi_from_Sj_Ti_GJK = tenex::evaluate<ti::K, ti::i>(Sl(ti::j) * Tl(ti::i) *\n                                                         Guu(ti::J, ti::K));\n  // \\f$L_{i}{}^{k} = S_{j} *  G^{jk} * T_{i}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<T_index, G_index>>\n      LiK_from_Sj_GJK_Ti = tenex::evaluate<ti::i, ti::K>(\n          Sl(ti::j) * Guu(ti::J, ti::K) * Tl(ti::i));\n  // \\f$L^{k}{}_{i} = S_{j} *  G^{jk} * T_{i}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<G_index, T_index>>\n      LKi_from_Sj_GJK_Ti = tenex::evaluate<ti::K, ti::i>(\n          Sl(ti::j) * Guu(ti::J, ti::K) * Tl(ti::i));\n  // \\f$L_{i}{}^{k} = T_{i} * S_{j} * G^{jk}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<T_index, G_index>>\n      LiK_from_Ti_Sj_GJK = tenex::evaluate<ti::i, ti::K>(Tl(ti::i) * Sl(ti::j) *\n                                                         Guu(ti::J, ti::K));\n  // \\f$L^{k}{}_{i} = T_{i} * S_{j} * G^{jk}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<G_index, T_index>>\n      LKi_from_Ti_Sj_GJK = tenex::evaluate<ti::K, ti::i>(Tl(ti::i) * Sl(ti::j) *\n                                                         Guu(ti::J, ti::K));\n  // \\f$L_{i}{}^{k} = T_{i} * G^{jk} * S_{j}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<T_index, G_index>>\n      LiK_from_Ti_GJK_Sj = tenex::evaluate<ti::i, ti::K>(\n          Tl(ti::i) * Guu(ti::J, ti::K) * Sl(ti::j));\n  // \\f$L^{k}{}_{i} = T_{i} * G^{jk} * S_{j}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<G_index, T_index>>\n      LKi_from_Ti_GJK_Sj = tenex::evaluate<ti::K, ti::i>(\n          Tl(ti::i) * Guu(ti::J, ti::K) * Sl(ti::j));\n  // \\f$L_{i}{}^{k} = G^{jk} * S_{j} * T_{i}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<T_index, G_index>>\n      LiK_from_GJK_Sj_Ti = tenex::evaluate<ti::i, ti::K>(Guu(ti::J, ti::K) *\n                                                         Sl(ti::j) * Tl(ti::i));\n  // \\f$L^{k}{}_{i} = G^{jk} * S_{j} * T_{i}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<G_index, T_index>>\n      LKi_from_GJK_Sj_Ti = tenex::evaluate<ti::K, ti::i>(Guu(ti::J, ti::K) *\n                                                         Sl(ti::j) * Tl(ti::i));\n  // \\f$L_{i}{}^{k} = G^{jk} * T_{i} * S_{j}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<T_index, G_index>>\n      LiK_from_GJK_Ti_Sj = tenex::evaluate<ti::i, ti::K>(Guu(ti::J, ti::K) *\n                                                         Tl(ti::i) * Sl(ti::j));\n  // \\f$L^{k}{}_{i} = G^{jk} * T_{i} * S_{j}\\f$\n  const Tensor<DataType, Symmetry<2, 1>, index_list<G_index, T_index>>\n      LKi_from_GJK_Ti_Sj = tenex::evaluate<ti::K, ti::i>(Guu(ti::J, ti::K) *\n                                                         Tl(ti::i) * Sl(ti::j));\n\n  for (size_t k = 0; k < G_index::dim; k++) {\n    for (size_t i = 0; i < T_index::dim; i++) {\n      DataType expected_product =\n          make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t j = 0; j < G_index::dim; j++) {\n        expected_product += (Sl.get(j) * Tl.get(i) * Guu.get(j, k));\n      }\n      CHECK_ITERABLE_APPROX(LiK_from_Sj_Ti_GJK.get(i, k), expected_product);\n      CHECK_ITERABLE_APPROX(LKi_from_Sj_Ti_GJK.get(k, i), expected_product);\n      CHECK_ITERABLE_APPROX(LiK_from_Sj_GJK_Ti.get(i, k), expected_product);\n      CHECK_ITERABLE_APPROX(LKi_from_Sj_GJK_Ti.get(k, i), expected_product);\n      CHECK_ITERABLE_APPROX(LiK_from_Ti_Sj_GJK.get(i, k), expected_product);\n      CHECK_ITERABLE_APPROX(LKi_from_Ti_Sj_GJK.get(k, i), expected_product);\n      CHECK_ITERABLE_APPROX(LiK_from_Ti_GJK_Sj.get(i, k), expected_product);\n      CHECK_ITERABLE_APPROX(LKi_from_Ti_GJK_Sj.get(k, i), expected_product);\n      CHECK_ITERABLE_APPROX(LiK_from_GJK_Sj_Ti.get(i, k), expected_product);\n      CHECK_ITERABLE_APPROX(LKi_from_GJK_Sj_Ti.get(k, i), expected_product);\n      CHECK_ITERABLE_APPROX(LiK_from_GJK_Ti_Sj.get(i, k), expected_product);\n      CHECK_ITERABLE_APPROX(LKi_from_GJK_Ti_Sj.get(k, i), expected_product);\n    }\n  }\n}\n\n// \\brief Test the products of tensors where generic spatial indices are used\n// for spacetime indices\n//\n// \\details\n// The product cases tested are:\n// - \\f$L = R^{i} * S_{i}\\f$\n// - \\f$L = R^{k} * T_{k}\\f$\n// - \\f$L_{j, a, i}{}^{b} = G_{i, a} * H_{j}{}^{b}\\f$\n// - \\f$L_{k, i} = H_{i}{}^{j} * G_{k, j}\\f$\n//\n// where \\f$R\\f$'s index is spacetime, \\f$S\\f$' index is spatial, \\f$T\\f$'s\n// index is spacetime, \\f$G\\f$'s indices are spacetime, and \\f$H\\f$'s first\n// index is spatial and second is spacetime.\n//\n// \\tparam DataType the type of data being stored in the tensor operand of the\n// products\ntemplate <typename Generator, typename DataType>\nvoid test_spatial_spacetime_index(const gsl::not_null<Generator*> generator,\n                                  const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto R = make_with_random_values<tnsr::A<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const auto S = make_with_random_values<tnsr::i<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const auto T = make_with_random_values<tnsr::a<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L = R^{i} * S_{i}\\f$\n  // (spatial) * (spacetime) inner product with generic spatial indices\n  // Use explicit type (vs auto) for LHS Tensor so the compiler checks the\n  // return type of `evaluate`\n  const Tensor<DataType> RS = tenex::evaluate(R(ti::I) * S(ti::i));\n  // \\f$L = R^{k} * T_{k}\\f$\n  // (spacetime) * (spacetime) inner product with generic spatial indices\n  const Tensor<DataType> RT = tenex::evaluate(R(ti::K) * T(ti::k));\n\n  DataType expected_RS_product = make_with_value<DataType>(used_for_size, 0.0);\n  DataType expected_RT_product = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    expected_RS_product += R.get(i + 1) * S.get(i);\n    expected_RT_product += R.get(i + 1) * T.get(i + 1);\n  }\n  CHECK_ITERABLE_APPROX(RS.get(), expected_RS_product);\n  CHECK_ITERABLE_APPROX(RT.get(), expected_RT_product);\n\n  const auto G =\n      make_with_random_values<tnsr::aa<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n  const auto H =\n      make_with_random_values<tnsr::iA<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n\n  // \\f$L_{j, a, i}{}^{b} = G_{i, a} * H_{j}{}^{b}\\f$\n  // rank 2 x rank 2 outer product containing a spacetime index with a generic\n  // spatial index\n  const Tensor<DataType, Symmetry<4, 3, 2, 1>,\n               index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                          SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                          SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>\n      GH = tenex::evaluate<ti::j, ti::a, ti::i, ti::B>(G(ti::i, ti::a) *\n                                                       H(ti::j, ti::B));\n  for (size_t j = 0; j < 3; j++) {\n    for (size_t a = 0; a < 4; a++) {\n      for (size_t i = 0; i < 3; i++) {\n        for (size_t b = 0; b < 4; b++) {\n          CHECK_ITERABLE_APPROX(GH.get(j, a, i, b),\n                                G.get(i + 1, a) * H.get(j, b));\n        }\n      }\n    }\n  }\n\n  // \\f$L_{k, i} = H_{i}{}^{j} * G_{k, j}\\f$\n  // inner and outer product in one expression using generic spatial indices\n  // for spacetime indices\n  const Tensor<DataType, Symmetry<2, 1>,\n               index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                          SpatialIndex<3, UpLo::Lo, Frame::Inertial>>>\n      HG = tenex::evaluate<ti::k, ti::i>(H(ti::i, ti::J) * G(ti::k, ti::j));\n  for (size_t k = 0; k < 3; k++) {\n    for (size_t i = 0; i < 3; i++) {\n      DataType expected_product = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t j = 0; j < 3; j++) {\n        expected_product += H.get(i, j + 1) * G.get(k + 1, j + 1);\n      }\n      CHECK_ITERABLE_APPROX(HG.get(k, i), expected_product);\n    }\n  }\n}\n\n// \\brief Test the products of tensors where concrete time indices are used for\n// spacetime indices\n//\n// \\details\n// The product cases tested are:\n// - \\f$L = R_{t} * S * R_{t}\\f$\n// - \\f$L^{b} = G_{t}{}^{a} * H_{a}{}^{tb}\\f$\n// - \\f$L^{T}{}_{ba} = R_{a} * R_{b}\\f$\n// - \\f$L_^{ct} = G_{t}{}^{b} * R_{b} * H_{t}^{ta}\\f$\n//\n// \\tparam DataType the type of data being stored in the tensor operand of the\n// products\ntemplate <typename Generator, typename DataType>\nvoid test_time_index(const gsl::not_null<Generator*> generator,\n                     const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto R = make_with_random_values<tnsr::a<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const auto S = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L = R_{t} * S * R_{t}\\f$\n  const Tensor<DataType> L = tenex::evaluate(R(ti::t) * S() * R(ti::t));\n  CHECK_ITERABLE_APPROX(L.get(), R.get(0) * S.get() * R.get(0));\n\n  const auto G = make_with_random_values<tnsr::aB<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const auto H = make_with_random_values<tnsr::aBC<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L^{b} = G_{t}{}^{a} * H_{a}{}^{tb}\\f$\n  const Tensor<DataType, Symmetry<1>,\n               index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>\n      L_B = tenex::evaluate<ti::B>(G(ti::t, ti::A) * H(ti::a, ti::T, ti::B));\n\n  for (size_t b = 0; b < 4; b++) {\n    DataType expected_product = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t a = 0; a < 4; a++) {\n      expected_product += G.get(0, a) * H.get(a, 0, b);\n    }\n    CHECK_ITERABLE_APPROX(L_B.get(b), expected_product);\n  }\n\n  // Assign a placeholder to the LHS tensor's components before it is computed\n  // so that when test expressions below only compute time components, we can\n  // check that LHS spatial components haven't changed\n  const auto spatial_component_placeholder_value =\n      TestHelpers::tenex::component_placeholder_value<DataType>::value;\n  auto L_Tba = make_with_value<\n      Tensor<DataType, Symmetry<2, 1, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>>(\n      used_for_size, spatial_component_placeholder_value);\n  // \\f$L^{T}{}_{ba} = R_{a} * R_{b}\\f$\n  tenex::evaluate<ti::T, ti::b, ti::a>(make_not_null(&L_Tba),\n                                       R(ti::a) * R(ti::b));\n\n  for (size_t b = 0; b < 4; b++) {\n    for (size_t a = 0; a < 4; a++) {\n      CHECK_ITERABLE_APPROX(L_Tba.get(0, b, a), R.get(a) * R.get(b));\n      for (size_t i = 0; i < 3; i++) {\n        CHECK(L_Tba.get(i + 1, b, a) == spatial_component_placeholder_value);\n      }\n    }\n  }\n\n  auto L_CT = make_with_value<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Up, Frame::Grid>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Grid>>>>(\n      used_for_size, spatial_component_placeholder_value);\n  // \\f$L_^{ct} = G_{t}{}^{b} * R_{b} * H_{t}^{ta}\\f$\n  tenex::evaluate<ti::C, ti::T>(\n      make_not_null(&L_CT),\n      G(ti::t, ti::B) * H(ti::b, ti::A, ti::C) * G(ti::a, ti::T));\n\n  for (size_t c = 0; c < 4; c++) {\n    DataType expected_product = make_with_value<DataType>(used_for_size, 0.0);\n    for (size_t b = 0; b < 4; b++) {\n      for (size_t a = 0; a < 4; a++) {\n        expected_product += G.get(0, b) * H.get(b, a, c) * G.get(a, 0);\n      }\n    }\n    CHECK_ITERABLE_APPROX(L_CT.get(c, 0), expected_product);\n\n    for (size_t i = 0; i < 3; i++) {\n      CHECK(L_CT.get(c, i + 1) == spatial_component_placeholder_value);\n    }\n  }\n}\n\ntemplate <typename Generator, typename DataType>\nvoid test_products(const gsl::not_null<Generator*> generator,\n                   const DataType& used_for_size) {\n  // Test evaluation of outer products\n  test_outer_product_double(generator, used_for_size);\n  test_outer_product_rank_0_operand(generator, used_for_size);\n  test_outer_product_rank_1_operand(generator, used_for_size);\n  test_outer_product_rank_2x2_operands(generator, used_for_size);\n  test_outer_product_rank_0x1x2_operands(generator, used_for_size);\n\n  // Test evaluation of inner products\n  test_inner_product_rank_1x1_operands(generator, used_for_size);\n  test_inner_product_rank_2x2_operands(generator, used_for_size);\n\n  // Test evaluation of expressions involving both inner and outer products\n  test_two_term_inner_outer_product(generator, used_for_size);\n  test_three_term_inner_outer_product(generator, used_for_size);\n\n  // Test product expressions where generic spatial indices are used for\n  // spacetime indices\n  test_spatial_spacetime_index(generator, used_for_size);\n\n  // Test product expressions where time indices are used for spacetime indices\n  test_time_index(generator, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.Product\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  test_tensor_ops_properties();\n  test_products(make_not_null(&generator),\n                std::numeric_limits<double>::signaling_NaN());\n  test_products(\n      make_not_null(&generator),\n      std::complex<double>(std::numeric_limits<double>::signaling_NaN(),\n                           std::numeric_limits<double>::signaling_NaN()));\n  test_products(make_not_null(&generator),\n                DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n  test_products(\n      make_not_null(&generator),\n      ComplexDataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_ProductHighRankIntermediate.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <cstddef>\n#include <iterator>\n#include <numeric>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename... Ts>\nvoid assign_unique_values_to_tensor(\n    gsl::not_null<Tensor<double, Ts...>*> tensor) {\n  std::iota(tensor->begin(), tensor->end(), 0.0);\n}\n\ntemplate <typename... Ts>\nvoid assign_unique_values_to_tensor(\n    gsl::not_null<Tensor<DataVector, Ts...>*> tensor) {\n  double value = 0.0;\n  for (auto index_it = tensor->begin(); index_it != tensor->end(); index_it++) {\n    for (auto vector_it = index_it->begin(); vector_it != index_it->end();\n         vector_it++) {\n      *vector_it = value;\n      value += 1.0;\n    }\n  }\n}\n\n// \\brief Test the evaluation of the product of three tensors involving inner\n// products, outer products, and an intermediate tensor expression that\n// represents a high rank tensor\n//\n// \\details\n// The product case tested is:\n// - \\f$L^{c}{}_{dkl} = R_{jb}{}^{a} * (S_{da}{}^{BC} * T^{j}{}_{kl})\\f$\n//\n// This is intended as a stress test for TensorContract and OuterProduct,\n// as the product of the 2nd and 3rd operands represents a rank 7 tensor\n// and 3 of its indices are contracted in the inner product with the first\n// operand.\n//\n// Note: When the following expression is tested instead, a segfault occurs\n// for gcc-9 Release mode:\n// - \\f$L^{c}{}_{dkl} = R_{ijb}{}^{a} * (S_{da}{}^{BC} * T^{j}{}_{kl}{}^{i})\\f$\n// For unknown reasons, the internal vectors of a blaze::DVecDVecMultExpr\n// (lhs and rhs) live at 0x0, and iterating with begin() causes a segfault.\n// The most likely cause is thought to be a bug either in the optimizer or\n// blaze.\n//\n// \\tparam DataType the type of data being stored in the product operands\ntemplate <typename DataType>\nvoid test_high_rank_intermediate(const DataType& used_for_size) {\n  using frame = Frame::Inertial;\n  using A_index = SpacetimeIndex<3, UpLo::Up, frame>;\n  using a_index = SpacetimeIndex<3, UpLo::Lo, frame>;\n  using B_index = SpacetimeIndex<3, UpLo::Up, frame>;\n  using b_index = SpacetimeIndex<3, UpLo::Lo, frame>;\n  using C_index = B_index;\n  using d_index = SpacetimeIndex<3, UpLo::Lo, frame>;\n  using J_index = SpatialIndex<3, UpLo::Up, frame>;\n  using j_index = SpatialIndex<3, UpLo::Lo, frame>;\n  using k_index = SpatialIndex<3, UpLo::Lo, frame>;\n  using l_index = SpatialIndex<3, UpLo::Lo, frame>;\n\n  Tensor<DataType, Symmetry<3, 2, 1>, index_list<j_index, b_index, A_index>> R(\n      used_for_size);\n  assign_unique_values_to_tensor(make_not_null(&R));\n  Tensor<DataType, Symmetry<3, 2, 1, 1>,\n         index_list<d_index, a_index, B_index, C_index>>\n      S(used_for_size);\n  assign_unique_values_to_tensor(make_not_null(&S));\n  Tensor<DataType, Symmetry<3, 2, 1>, index_list<J_index, k_index, l_index>> T(\n      used_for_size);\n  assign_unique_values_to_tensor(make_not_null(&T));\n\n  // \\f$L^{c}{}_{dkl} = R_{jb}{}^{a} * (S_{da}{}^{BC} * T^{j}{}_{kl})\\f$\n  const Tensor<DataType, Symmetry<4, 3, 2, 1>,\n               index_list<C_index, d_index, k_index, l_index>>\n      actual_result = tenex::evaluate<ti::C, ti::d, ti::k, ti::l>(\n          R(ti::j, ti::b, ti::A) *\n          (S(ti::d, ti::a, ti::B, ti::C) * T(ti::J, ti::k, ti::l)));\n\n  for (size_t c = 0; c < C_index::dim; c++) {\n    for (size_t d = 0; d < d_index::dim; d++) {\n      for (size_t k = 0; k < k_index::dim; k++) {\n        for (size_t l = 0; l < l_index::dim; l++) {\n          DataType expected_product_component =\n              make_with_value<DataType>(used_for_size, 0.0);\n            for (size_t j = 0; j < j_index::dim; j++) {\n              for (size_t b = 0; b < b_index::dim; b++) {\n                for (size_t a = 0; a < a_index::dim; a++) {\n                  expected_product_component +=\n                      R.get(j, b, a) * S.get(d, a, b, c) * T.get(j, k, l);\n                }\n              }\n            }\n          CHECK_ITERABLE_APPROX(actual_result.get(c, d, k, l),\n                                expected_product_component);\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.DataStructures.Tensor.Expression.ProductHighRankIntermediate\",\n    \"[DataStructures][Unit]\") {\n  test_high_rank_intermediate(std::numeric_limits<double>::signaling_NaN());\n  test_high_rank_intermediate(\n      DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_SpatialSpacetimeIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/Expressions/SpatialSpacetimeIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n\nnamespace {\nconstexpr size_t dim = 3;\nusing frame = Frame::Grid;\n\nusing index_list_empty = index_list<>;\nusing index_list_a = index_list<SpacetimeIndex<dim, UpLo::Lo, frame>>;\nusing index_list_i = index_list<SpatialIndex<dim, UpLo::Lo, frame>>;\nusing index_list_abc = index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                  SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                  SpacetimeIndex<dim, UpLo::Lo, frame>>;\nusing index_list_aBC = index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                  SpacetimeIndex<dim, UpLo::Up, frame>,\n                                  SpacetimeIndex<dim, UpLo::Up, frame>>;\nusing index_list_aBI = index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                  SpacetimeIndex<dim, UpLo::Up, frame>,\n                                  SpatialIndex<dim, UpLo::Up, frame>>;\nusing index_list_aIB = index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                  SpatialIndex<dim, UpLo::Up, frame>,\n                                  SpacetimeIndex<dim, UpLo::Up, frame>>;\nusing index_list_aIJ = index_list<SpacetimeIndex<dim, UpLo::Lo, frame>,\n                                  SpatialIndex<dim, UpLo::Up, frame>,\n                                  SpatialIndex<dim, UpLo::Up, frame>>;\nusing index_list_iAB = index_list<SpatialIndex<dim, UpLo::Lo, frame>,\n                                  SpacetimeIndex<dim, UpLo::Up, frame>,\n                                  SpacetimeIndex<dim, UpLo::Up, frame>>;\nusing index_list_iAJ = index_list<SpatialIndex<dim, UpLo::Lo, frame>,\n                                  SpacetimeIndex<dim, UpLo::Up, frame>,\n                                  SpatialIndex<dim, UpLo::Up, frame>>;\nusing index_list_iJA = index_list<SpatialIndex<dim, UpLo::Lo, frame>,\n                                  SpatialIndex<dim, UpLo::Up, frame>,\n                                  SpacetimeIndex<dim, UpLo::Up, frame>>;\nusing index_list_iJK = index_list<SpatialIndex<dim, UpLo::Lo, frame>,\n                                  SpatialIndex<dim, UpLo::Up, frame>,\n                                  SpatialIndex<dim, UpLo::Up, frame>>;\n\nusing ti_list_empty = tmpl::list<>;\nusing ti_list_a = make_tensorindex_list<ti::a>;\nusing ti_list_i = make_tensorindex_list<ti::i>;\nusing ti_list_abc = make_tensorindex_list<ti::a, ti::b, ti::c>;\nusing ti_list_ijk = make_tensorindex_list<ti::i, ti::j, ti::k>;\nusing ti_list_abi = make_tensorindex_list<ti::a, ti::b, ti::i>;\nusing ti_list_aij = make_tensorindex_list<ti::a, ti::i, ti::j>;\nusing ti_list_iaj = make_tensorindex_list<ti::i, ti::a, ti::j>;\nusing ti_list_ija = make_tensorindex_list<ti::i, ti::j, ti::a>;\n\nusing positions_list_empty = tmpl::integral_list<size_t>;\nusing positions_list_0 = tmpl::integral_list<size_t, 0>;\nusing positions_list_1 = tmpl::integral_list<size_t, 1>;\nusing positions_list_2 = tmpl::integral_list<size_t, 2>;\nusing positions_list_01 = tmpl::integral_list<size_t, 0, 1>;\nusing positions_list_12 = tmpl::integral_list<size_t, 1, 2>;\nusing positions_list_02 = tmpl::integral_list<size_t, 0, 2>;\nusing positions_list_012 = tmpl::integral_list<size_t, 0, 1, 2>;\n\nvoid test_spatial_spacetime_index_positions() {\n  CHECK(std::is_same_v<tenex::detail::spatial_spacetime_index_positions<\n                           index_list_empty, ti_list_empty>,\n                       positions_list_empty>);\n  CHECK(std::is_same_v<tenex::detail::spatial_spacetime_index_positions<\n                           index_list_a, ti_list_a>,\n                       positions_list_empty>);\n  CHECK(std::is_same_v<tenex::detail::spatial_spacetime_index_positions<\n                           index_list_a, ti_list_i>,\n                       positions_list_0>);\n  CHECK(std::is_same_v<tenex::detail::spatial_spacetime_index_positions<\n                           index_list_aBC, ti_list_abc>,\n                       positions_list_empty>);\n  CHECK(std::is_same_v<tenex::detail::spatial_spacetime_index_positions<\n                           index_list_aBC, ti_list_iaj>,\n                       positions_list_02>);\n  CHECK(std::is_same_v<tenex::detail::spatial_spacetime_index_positions<\n                           index_list_aBC, ti_list_abc>,\n                       positions_list_empty>);\n  CHECK(std::is_same_v<tenex::detail::spatial_spacetime_index_positions<\n                           index_list_aBC, ti_list_abi>,\n                       positions_list_2>);\n}\n\nvoid test_get_spatial_spacetime_index_symmetry() {\n  constexpr std::array<std::int32_t, 0> symm_empty = {{}};\n  constexpr std::array<std::int32_t, 1> symm_1 = {{1}};\n  constexpr std::array<std::int32_t, 3> symm_111 = {{1, 1, 1}};\n  constexpr std::array<std::int32_t, 3> symm_211 = {{2, 1, 1}};\n  constexpr std::array<std::int32_t, 3> symm_121 = {{1, 2, 1}};\n  constexpr std::array<std::int32_t, 3> symm_221 = {{2, 2, 1}};\n  constexpr std::array<std::int32_t, 3> symm_321 = {{3, 2, 1}};\n\n  constexpr std::array<size_t, 0> positions_empty = {{}};\n  constexpr std::array<size_t, 1> positions_0 = {{0}};\n  constexpr std::array<size_t, 1> positions_1 = {{1}};\n  constexpr std::array<size_t, 1> positions_2 = {{2}};\n  constexpr std::array<size_t, 2> positions_01 = {{0, 1}};\n  constexpr std::array<size_t, 2> positions_02 = {{0, 2}};\n  constexpr std::array<size_t, 2> positions_12 = {{1, 2}};\n  constexpr std::array<size_t, 3> positions_012 = {{0, 1, 2}};\n\n  // Rank 0\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_empty, positions_empty)) == detail::symmetry(symm_empty));\n\n  // Rank 1\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_1, positions_empty)) == detail::symmetry(symm_1));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_1, positions_0)) == detail::symmetry(symm_1));\n\n  // Rank 3, input symmetry with three symmetric indices\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_111, positions_0)) == detail::symmetry(symm_211));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_111, positions_1)) == detail::symmetry(symm_121));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_111, positions_2)) == detail::symmetry(symm_221));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_111, positions_01)) == detail::symmetry(symm_221));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_111, positions_02)) == detail::symmetry(symm_121));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_111, positions_12)) == detail::symmetry(symm_211));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_111, positions_012)) == detail::symmetry(symm_111));\n\n  // Rank 3, input symmetry with two symmetric indices\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_121, positions_empty)) == detail::symmetry(symm_121));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_121, positions_0)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_121, positions_1)) == detail::symmetry(symm_121));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_121, positions_2)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_121, positions_01)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_121, positions_02)) == detail::symmetry(symm_121));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_121, positions_12)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_121, positions_012)) == detail::symmetry(symm_121));\n\n  // Rank 3, input symmetry with no symmetric indices\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_321, positions_empty)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_321, positions_0)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_321, positions_1)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_321, positions_2)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_321, positions_01)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_321, positions_02)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_321, positions_12)) == detail::symmetry(symm_321));\n  CHECK(detail::symmetry(tenex::detail::get_spatial_spacetime_index_symmetry(\n            symm_321, positions_012)) == detail::symmetry(symm_321));\n}\n\nvoid test_replace_spatial_spacetime_indices() {\n  // Rank 0\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_empty, positions_list_empty>,\n                       index_list_empty>);\n\n  // Rank 1\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_a, positions_list_empty>,\n                       index_list_a>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_a, positions_list_0>,\n                       index_list_i>);\n\n  // Rank 3, one spacetime index\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_iJA, positions_list_empty>,\n                       index_list_iJA>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_iJA, positions_list_2>,\n                       index_list_iJK>);\n\n  // Rank 3, two spacetime indices\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_iAB, positions_list_empty>,\n                       index_list_iAB>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_iAB, positions_list_1>,\n                       index_list_iJA>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_iAB, positions_list_2>,\n                       index_list_iAJ>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_iAB, positions_list_12>,\n                       index_list_iJK>);\n\n  // Rank 3, three spacetime indices\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_aBC, positions_list_empty>,\n                       index_list_aBC>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_aBC, positions_list_0>,\n                       index_list_iAB>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_aBC, positions_list_1>,\n                       index_list_aIB>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_aBC, positions_list_2>,\n                       index_list_aBI>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_aBC, positions_list_01>,\n                       index_list_iJA>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_aBC, positions_list_02>,\n                       index_list_iAJ>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_aBC, positions_list_12>,\n                       index_list_aIJ>);\n  CHECK(std::is_same_v<tenex::detail::replace_spatial_spacetime_indices<\n                           index_list_aBC, positions_list_012>,\n                       index_list_iJK>);\n}\n\nvoid test_spatial_spacetime_index_transformation_from_positions() {\n  constexpr std::array<size_t, 0> positions_empty = {{}};\n  constexpr std::array<size_t, 1> positions_0 = {{0}};\n  constexpr std::array<size_t, 1> positions_1 = {{1}};\n  constexpr std::array<size_t, 1> positions_2 = {{2}};\n  constexpr std::array<size_t, 2> positions_12 = {{1, 2}};\n\n  constexpr std::array<std::int32_t, 0> transformation_empty = {{}};\n  constexpr std::array<std::int32_t, 1> transformation_0 = {{0}};\n  constexpr std::array<std::int32_t, 1> transformation_m1 = {{-1}};\n  constexpr std::array<std::int32_t, 1> transformation_p1 = {{1}};\n  constexpr std::array<std::int32_t, 3> transformation_000 = {{0, 0, 0}};\n  constexpr std::array<std::int32_t, 3> transformation_m10p10 = {{-1, 0, 1}};\n  constexpr std::array<std::int32_t, 3> transformation_00m1 = {{0, 0, -1}};\n\n  // Rank 0\n  CHECK(tenex::detail::spatial_spacetime_index_transformation_from_positions<0>(\n            positions_empty, positions_empty) == transformation_empty);\n\n  // Rank 1\n  CHECK(tenex::detail::spatial_spacetime_index_transformation_from_positions<1>(\n            positions_empty, positions_empty) == transformation_0);\n  CHECK(tenex::detail::spatial_spacetime_index_transformation_from_positions<1>(\n            positions_0, positions_0) == transformation_0);\n  CHECK(tenex::detail::spatial_spacetime_index_transformation_from_positions<1>(\n            positions_0, positions_empty) == transformation_m1);\n  CHECK(tenex::detail::spatial_spacetime_index_transformation_from_positions<1>(\n            positions_empty, positions_0) == transformation_p1);\n\n  // Rank 3\n  CHECK(tenex::detail::spatial_spacetime_index_transformation_from_positions<3>(\n            positions_empty, positions_empty) == transformation_000);\n  CHECK(tenex::detail::spatial_spacetime_index_transformation_from_positions<3>(\n            positions_0, positions_2) == transformation_m10p10);\n  CHECK(tenex::detail::spatial_spacetime_index_transformation_from_positions<3>(\n            positions_12, positions_1) == transformation_00m1);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.SpatialSpacetimeIndex\",\n                  \"[DataStructures][Unit]\") {\n  test_spatial_spacetime_index_positions();\n  test_get_spatial_spacetime_index_symmetry();\n  test_replace_spatial_spacetime_indices();\n  test_spatial_spacetime_index_transformation_from_positions();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_SquareRoot.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <complex>\n#include <cstddef>\n#include <random>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Symmetry.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n// Checks that the number of ops in the expressions match what is expected\nvoid test_tensor_ops_properties() {\n  const Scalar<double> G{5.0};\n  const double H = 5.0;\n\n  const auto sqrt_G = sqrt(G());\n  const auto sqrt_HGG = sqrt(H * G() * G());\n  // Expected: 2 sqrt + 2 negations + 2 multiplies = 6 total ops\n  const auto sqrt_sqrt_GHG = sqrt(sqrt(-G() * H * -G()));\n\n  CHECK(sqrt_G.num_ops_subtree == 1);\n  CHECK(sqrt_HGG.num_ops_subtree == 3);\n  CHECK(sqrt_sqrt_GHG.num_ops_subtree == 6);\n}\n\n// \\brief Test the square root of a rank 0 tensor expression is correctly\n// evaluated\n//\n// \\details\n// The cases tested are:\n// - \\f$L = \\sqrt{R}\\f$\n// - \\f$L = \\sqrt{S_{k}{}^{k}}\\f$\n// - \\f$L = \\sqrt{S_{k}{}^{k} * T}\\f$\n//\n// where \\f$R\\f$ and \\f$L\\f$ are rank 0 Tensors, \\f$S\\f$ is a rank 2 Tensor,\n// and \\f$T\\f$ is a `double`.\n//\n// \\tparam DataType the type of data being stored in the tensor operands of the\n// expressions tested\ntemplate <typename Generator, typename DataType>\nvoid test_sqrt(const gsl::not_null<Generator*> generator,\n               const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  const auto R = make_with_random_values<Scalar<DataType>>(\n      generator, distribution, used_for_size);\n\n  // \\f$L = \\sqrt{R}\\f$\n  Tensor<DataType> sqrt_R = tenex::evaluate(sqrt(R()));\n  CHECK(sqrt_R.get() == sqrt(R.get()));\n\n  const auto S =\n      make_with_random_values<tnsr::iJ<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n\n  DataType S_trace = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    S_trace += S.get(i, i);\n  }\n\n  // \\f$L = \\sqrt{S_{k}{}^{k}}\\f$\n  const Tensor<DataType> sqrt_S = tenex::evaluate(sqrt(S(ti::k, ti::K)));\n  // \\f$L = \\sqrt{S_{k}{}^{k} * T}\\f$\n  const Tensor<DataType> sqrt_S_T =\n      tenex::evaluate(sqrt(S(ti::k, ti::K) * 3.6));\n  CHECK_ITERABLE_APPROX(sqrt_S.get(), sqrt(S_trace));\n  CHECK_ITERABLE_APPROX(sqrt_S_T.get(), sqrt(S_trace * 3.6));\n\n  const auto G = make_with_random_values<tnsr::I<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const auto H = make_with_random_values<tnsr::a<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  DataType GH_product = make_with_value<DataType>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    GH_product += G.get(i) * H.get(i + 1);\n  }\n\n  // Test expression that uses generic spatial index for a spacetime index\n  // \\f$L = \\sqrt{G^{j} H_{j}\\f$\n  const Tensor<DataType> sqrt_GH_product =\n      tenex::evaluate(sqrt(G(ti::J) * H(ti::j)));\n  CHECK_ITERABLE_APPROX(sqrt_GH_product.get(), sqrt(GH_product));\n\n  const auto T = make_with_random_values<\n      Tensor<DataType, Symmetry<2, 1>,\n             index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  // Test expression that uses concrete time index for a spacetime index\n  // \\f$L = \\sqrt{T_{t, t}\\f$\n  const Scalar<DataType> sqrt_T_time = tenex::evaluate(sqrt(T(ti::t, ti::t)));\n  CHECK(sqrt_T_time.get() == sqrt(T.get(0, 0)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.SquareRoot\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  test_tensor_ops_properties();\n  test_sqrt(make_not_null(&generator),\n            std::numeric_limits<double>::signaling_NaN());\n  test_sqrt(make_not_null(&generator),\n            std::complex<double>(std::numeric_limits<double>::signaling_NaN(),\n                                 std::numeric_limits<double>::signaling_NaN()));\n  test_sqrt(make_not_null(&generator),\n            DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n  test_sqrt(make_not_null(&generator),\n            ComplexDataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_TensorIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.TensorIndex\",\n                  \"[DataStructures][Unit]\") {\n  // Test `make_tensorindex_list`\n  // Check at compile time since some other tests use this metafunction\n  static_assert(std::is_same_v<make_tensorindex_list<ti::j, ti::A, ti::b>,\n                               tmpl::list<std::decay_t<decltype(ti::j)>,\n                                          std::decay_t<decltype(ti::A)>,\n                                          std::decay_t<decltype(ti::b)>>>,\n                \"make_tensorindex_list failed for non-empty list\");\n  static_assert(std::is_same_v<make_tensorindex_list<>, tmpl::list<>>,\n                \"make_tensorindex_list failed for empty list\");\n\n  // Test `get_tensorindex_value_with_opposite_valence`\n  //\n  // For (1) lower spacetime indices, (2) upper spacetime indices, (3) lower\n  // spatial indices, and (4) upper spatial indices, the encoding of the index\n  // with the same index type but opposite valence is checked for the following\n  // cases: (i) the smallest encoding, (ii) the largest encoding, and (iii) a\n  // value in between.\n\n  // Lower spacetime\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(0) ==\n        tenex::TensorIndex_detail::upper_sentinel);\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::upper_sentinel - 1) ==\n        tenex::TensorIndex_detail::spatial_sentinel - 1);\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(115) ==\n        tenex::TensorIndex_detail::upper_sentinel + 115);\n\n  // Upper spacetime\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::upper_sentinel) == 0);\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::spatial_sentinel - 1) ==\n        tenex::TensorIndex_detail::upper_sentinel - 1);\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::upper_sentinel + 88) == 88);\n\n  // Lower spatial\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::spatial_sentinel) ==\n        tenex::TensorIndex_detail::upper_spatial_sentinel);\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::upper_spatial_sentinel - 1) ==\n        tenex::TensorIndex_detail::max_sentinel - 1);\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::spatial_sentinel + 232) ==\n        tenex::TensorIndex_detail::upper_spatial_sentinel + 232);\n\n  // Upper spatial\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::upper_spatial_sentinel) ==\n        tenex::TensorIndex_detail::spatial_sentinel);\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::max_sentinel - 1) ==\n        tenex::TensorIndex_detail::upper_spatial_sentinel - 1);\n  CHECK(tenex::get_tensorindex_value_with_opposite_valence(\n            tenex::TensorIndex_detail::upper_spatial_sentinel + 3) ==\n        tenex::TensorIndex_detail::spatial_sentinel + 3);\n\n  // Test tensorindex_list_is_valid\n  CHECK(tenex::tensorindex_list_is_valid<make_tensorindex_list<>>::value);\n  CHECK(tenex::tensorindex_list_is_valid<make_tensorindex_list<ti::J>>::value);\n  CHECK(tenex::tensorindex_list_is_valid<\n        make_tensorindex_list<ti::a, ti::c, ti::I, ti::B>>::value);\n  CHECK(tenex::tensorindex_list_is_valid<\n        make_tensorindex_list<ti::t, ti::T, ti::T, ti::T, ti::t>>::value);\n  CHECK(tenex::tensorindex_list_is_valid<\n        make_tensorindex_list<ti::d, ti::T, ti::D>>::value);\n  CHECK(not tenex::tensorindex_list_is_valid<\n        make_tensorindex_list<ti::I, ti::a, ti::I>>::value);\n\n  // Test tensorindex_list_is_valid\n  CHECK(tenex::tensorindex_list_is_valid<make_tensorindex_list<>>::value);\n  CHECK(tenex::tensorindex_list_is_valid<make_tensorindex_list<ti::J>>::value);\n  CHECK(tenex::tensorindex_list_is_valid<\n        make_tensorindex_list<ti::a, ti::c, ti::I, ti::B>>::value);\n  CHECK(tenex::tensorindex_list_is_valid<\n        make_tensorindex_list<ti::t, ti::T, ti::T, ti::T, ti::t>>::value);\n  CHECK(tenex::tensorindex_list_is_valid<\n        make_tensorindex_list<ti::d, ti::T, ti::D>>::value);\n  CHECK(not tenex::tensorindex_list_is_valid<\n        make_tensorindex_list<ti::I, ti::a, ti::I>>::value);\n\n  // Test generic_indices_at_same_positions\n  CHECK(\n      tenex::generic_indices_at_same_positions<make_tensorindex_list<>,\n                                               make_tensorindex_list<>>::value);\n  CHECK(tenex::generic_indices_at_same_positions<\n        make_tensorindex_list<ti::a, ti::c, ti::I, ti::B>,\n        make_tensorindex_list<ti::a, ti::c, ti::I, ti::B>>::value);\n  CHECK(not tenex::generic_indices_at_same_positions<\n        make_tensorindex_list<ti::a, ti::c, ti::I, ti::B>,\n        make_tensorindex_list<ti::a, ti::c, ti::i, ti::B>>::value);\n  CHECK(not tenex::generic_indices_at_same_positions<\n        make_tensorindex_list<ti::a, ti::c, ti::I, ti::B>,\n        make_tensorindex_list<ti::a, ti::c, ti::I>>::value);\n  CHECK(not tenex::generic_indices_at_same_positions<\n        make_tensorindex_list<ti::a, ti::c, ti::I>,\n        make_tensorindex_list<ti::a, ti::c, ti::I, ti::B>>::value);\n  CHECK(not tenex::generic_indices_at_same_positions<\n        make_tensorindex_list<ti::j, ti::B>,\n        make_tensorindex_list<ti::B, ti::j>>::value);\n  CHECK(tenex::generic_indices_at_same_positions<\n        make_tensorindex_list<ti::T>, make_tensorindex_list<ti::t>>::value);\n  CHECK(tenex::generic_indices_at_same_positions<\n        make_tensorindex_list<ti::t, ti::T, ti::T>,\n        make_tensorindex_list<ti::T, ti::t, ti::T>>::value);\n  CHECK(tenex::generic_indices_at_same_positions<\n        make_tensorindex_list<ti::i, ti::t, ti::C>,\n        make_tensorindex_list<ti::i, ti::T, ti::C>>::value);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_TensorIndexTransformation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank0.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank1.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank2.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank3.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank4.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationTimeIndex.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.DataStructures.Tensor.Expression.TensorIndexTransformation\",\n    \"[DataStructures][Unit]\") {\n  // Rank 0\n  TestHelpers::tenex::test_tensor_index_transformation_rank_0();\n\n  // Rank 1\n  TestHelpers::tenex::test_tensor_index_transformation_rank_1(ti::k);\n\n  // Rank 2\n  TestHelpers::tenex::test_tensor_index_transformation_rank_2(ti::i, ti::j);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_2(ti::j, ti::i);\n\n  // Rank 3\n  TestHelpers::tenex::test_tensor_index_transformation_rank_3(ti::a, ti::b,\n                                                              ti::c);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_3(ti::a, ti::c,\n                                                              ti::b);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_3(ti::b, ti::a,\n                                                              ti::c);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_3(ti::b, ti::c,\n                                                              ti::a);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_3(ti::c, ti::a,\n                                                              ti::b);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_3(ti::c, ti::b,\n                                                              ti::a);\n\n  // Rank 4\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::a, ti::b,\n                                                              ti::c, ti::d);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::a, ti::b,\n                                                              ti::d, ti::c);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::a, ti::c,\n                                                              ti::b, ti::d);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::a, ti::c,\n                                                              ti::d, ti::b);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::a, ti::d,\n                                                              ti::b, ti::c);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::a, ti::d,\n                                                              ti::c, ti::b);\n\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::b, ti::a,\n                                                              ti::c, ti::d);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::b, ti::a,\n                                                              ti::c, ti::d);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::b, ti::c,\n                                                              ti::a, ti::d);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::b, ti::c,\n                                                              ti::d, ti::a);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::b, ti::d,\n                                                              ti::a, ti::c);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::b, ti::d,\n                                                              ti::c, ti::a);\n\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::c, ti::a,\n                                                              ti::b, ti::d);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::c, ti::a,\n                                                              ti::d, ti::b);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::c, ti::b,\n                                                              ti::a, ti::d);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::c, ti::b,\n                                                              ti::d, ti::a);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::c, ti::d,\n                                                              ti::a, ti::b);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::c, ti::d,\n                                                              ti::b, ti::a);\n\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::d, ti::a,\n                                                              ti::b, ti::c);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::d, ti::a,\n                                                              ti::c, ti::b);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::d, ti::b,\n                                                              ti::a, ti::c);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::d, ti::b,\n                                                              ti::c, ti::a);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::d, ti::c,\n                                                              ti::a, ti::b);\n  TestHelpers::tenex::test_tensor_index_transformation_rank_4(ti::d, ti::c,\n                                                              ti::b, ti::a);\n\n  // Test transformations involving time indices\n  TestHelpers::tenex::test_tensor_index_transformation_with_time_indices();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Expressions/Test_TimeIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TimeIndex.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Expression.TimeIndex\",\n                  \"[DataStructures][Unit]\") {\n  // Test tt::is_time_index\n  CHECK(tt::is_time_index<std::decay_t<decltype(ti::t)>>::value);\n  CHECK(tt::is_time_index<std::decay_t<decltype(ti::T)>>::value);\n  CHECK(not tt::is_time_index<std::decay_t<decltype(ti::b)>>::value);\n  CHECK(not tt::is_time_index<std::decay_t<decltype(ti::I)>>::value);\n\n  // Test is_time_index_value\n  CHECK(tenex::detail::is_time_index_value(ti::t.value));\n  CHECK(tenex::detail::is_time_index_value(ti::T.value));\n  CHECK(not tenex::detail::is_time_index_value(ti::C.value));\n  CHECK(not tenex::detail::is_time_index_value(ti::j.value));\n\n  // Lists of TensorIndexs used for testing below\n  using empty_index_list = make_tensorindex_list<>;\n  using a = make_tensorindex_list<ti::a>;\n  using aiC = make_tensorindex_list<ti::a, ti::i, ti::C>;\n  using t = make_tensorindex_list<ti::t>;\n  using TT = make_tensorindex_list<ti::T, ti::T>;\n  using tc = make_tensorindex_list<ti::t, ti::c>;\n  using c = make_tensorindex_list<ti::c>;\n  using atTBjT =\n      make_tensorindex_list<ti::a, ti::t, ti::T, ti::B, ti::j, ti::T>;\n  using aBj = make_tensorindex_list<ti::a, ti::B, ti::j>;\n\n  // Test remove_time_indices\n  CHECK(\n      std::is_same_v<tenex::detail::remove_time_indices<empty_index_list>::type,\n                     empty_index_list>);\n  CHECK(std::is_same_v<tenex::detail::remove_time_indices<a>::type, a>);\n  CHECK(std::is_same_v<tenex::detail::remove_time_indices<aiC>::type, aiC>);\n  CHECK(std::is_same_v<tenex::detail::remove_time_indices<t>::type,\n                       empty_index_list>);\n  CHECK(std::is_same_v<tenex::detail::remove_time_indices<TT>::type,\n                       empty_index_list>);\n  CHECK(std::is_same_v<tenex::detail::remove_time_indices<tc>::type, c>);\n  CHECK(std::is_same_v<tenex::detail::remove_time_indices<atTBjT>::type, aBj>);\n\n  // Test get_time_index_positions\n  const std::array<size_t, 0> expected_empty_positions{};\n  CHECK(tenex::detail::get_time_index_positions<empty_index_list>() ==\n        expected_empty_positions);\n  CHECK(tenex::detail::get_time_index_positions<a>() ==\n        expected_empty_positions);\n  CHECK(tenex::detail::get_time_index_positions<aiC>() ==\n        expected_empty_positions);\n  const std::array<size_t, 1> expected_t_positions{0};\n  CHECK(tenex::detail::get_time_index_positions<t>() == expected_t_positions);\n  const std::array<size_t, 2> expected_TT_positions{0, 1};\n  CHECK(tenex::detail::get_time_index_positions<TT>() == expected_TT_positions);\n  const std::array<size_t, 1> expected_tc_positions{0};\n  CHECK(tenex::detail::get_time_index_positions<tc>() == expected_tc_positions);\n  const std::array<size_t, 3> expected_atTBjT_positions{1, 2, 5};\n  CHECK(tenex::detail::get_time_index_positions<atTBjT>() ==\n        expected_atTBjT_positions);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.DataStructures.Tensor.Python.Tensor\"\n  Test_Tensor.py\n  \"Unit;DataStructures;Python\"\n  PyTensor)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Python/Test_Tensor.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import (\n    Frame,\n    InverseJacobian,\n    Jacobian,\n    Scalar,\n    tnsr,\n)\nfrom spectre.Domain import jacobian_diagnostic\nfrom spectre.PointwiseFunctions.Punctures import adm_mass_integrand\nfrom spectre.Spectral import Basis, Mesh, Quadrature\n\n\nclass TestTensor(unittest.TestCase):\n    def test_tensor(self):\n        coords = tnsr.I[DataVector, 3, Frame.Inertial](num_points=4, fill=0.0)\n        spacetime_coords = tnsr.A[DataVector, 3, Frame.Inertial](\n            num_points=1, fill=0.0\n        )\n        self.assertEqual(coords.rank, 1)\n        self.assertEqual(coords.size, 3)\n        self.assertEqual(coords.dim, 3)\n        self.assertEqual(len(coords), 3)\n        self.assertEqual(spacetime_coords.rank, coords.rank)\n        self.assertEqual(spacetime_coords.size, coords.size + 1)\n        self.assertEqual(spacetime_coords.dim, coords.dim + 1)\n        self.assertEqual(len(spacetime_coords), len(coords) + 1)\n        npt.assert_equal(coords[0], np.zeros(4))\n        npt.assert_equal(coords[1], np.zeros(4))\n        npt.assert_equal(coords[2], np.zeros(4))\n        coords[0] = DataVector(4, 1.0)\n        coords[1] = DataVector(4, 2.0)\n        coords[2] = DataVector(4, 3.0)\n        self.assertEqual(coords.get_storage_index(0), 0)\n        self.assertEqual(coords.get_storage_index(1), 1)\n        self.assertEqual(coords.get_storage_index(2), 2)\n        for d, xyz in enumerate(coords):\n            npt.assert_equal(xyz, np.ones(4) * (d + 1))\n            npt.assert_equal(xyz, coords.get(d))\n            self.assertEqual(coords.multiplicity(d), 1)\n            self.assertEqual(coords.component_suffix(d), [\"_x\", \"_y\", \"_z\"][d])\n\n    def test_construct_from_list(self):\n        data = [DataVector(xyz) for xyz in np.random.rand(3, 4)]\n        coords = tnsr.I[DataVector, 3, Frame.Inertial](data)\n        npt.assert_equal(np.array(coords), data)\n\n    def test_numpy_interoperability(self):\n        data = np.random.rand(3, 4)\n        data_scalar = np.random.rand(4)\n        data_symm_tensor = np.random.rand(6, 4)\n        for copy in [True, False]:\n            coords = tnsr.I[DataVector, 3, Frame.Inertial](data, copy=copy)\n            for i, (a, b) in enumerate(zip(coords, data)):\n                npt.assert_equal(a, b, f\"Mismatch at index {i}\")\n            npt.assert_equal(np.array(coords), data)\n            symm_tensor = tnsr.ii[DataVector, 3](data_symm_tensor, copy=copy)\n            for i, (a, b) in enumerate(zip(symm_tensor, data_symm_tensor)):\n                npt.assert_equal(a, b, f\"Mismatch at index {i}\")\n            npt.assert_equal(np.array(symm_tensor), data_symm_tensor)\n            # Construction of Scalar from 1D array\n            scalar = Scalar[DataVector](data_scalar, copy=copy)\n            npt.assert_equal(np.array(scalar), [data_scalar])\n        with self.assertRaisesRegex(RuntimeError, \"expected to be 2D\"):\n            tnsr.ii[DataVector, 3](np.random.rand(3, 3, 4))\n        with self.assertRaisesRegex(RuntimeError, \"3 independent components\"):\n            tnsr.I[DataVector, 3](np.random.rand(2, 4))\n        # Implicit conversion from Numpy array to scalar\n        adm_mass_integrand(\n            field=np.random.rand(4),\n            alpha=np.random.rand(4),\n            beta=np.random.rand(4),\n        )\n        # Implicit conversion from Numpy array to vector\n        mesh = Mesh[3](3, Basis.Legendre, Quadrature.GaussLobatto)\n        jac = Jacobian[DataVector, 3](np.random.rand(9, 27))\n        jacobian_diagnostic(\n            jacobian=jac, inertial_coords=np.random.rand(3, 27), mesh=mesh\n        )\n        # Higher-rank tensor don't convert implicitly\n        with self.assertRaisesRegex(\n            TypeError, \"incompatible function arguments\"\n        ):\n            jacobian_diagnostic(\n                jacobian=np.random.rand(9, 27),\n                inertial_coords=np.random.rand(3, 27),\n                mesh=Mesh[3](3, Basis.Legendre, Quadrature.GaussLobatto),\n            )\n\n    def test_buffer_strides(self):\n        # The transpose should set up data with non-unit strides\n        original_data = np.random.rand(4, 3)\n        data = original_data.T\n        coords = tnsr.I[DataVector, 3, Frame.Inertial](data)\n        for i, (a, b) in enumerate(zip(coords, data)):\n            npt.assert_equal(a, b, f\"Mismatch at index {i}\")\n        npt.assert_equal(np.array(coords), data)\n        # Non-owning DataVectors don't work with strides != 1\n        with self.assertRaisesRegex(RuntimeError, \"Non-owning\"):\n            coords = tnsr.I[DataVector, 3, Frame.Inertial](data, copy=False)\n\n    def test_tensor_double(self):\n        coords = tnsr.I[float, 3, Frame.Inertial](fill=0.0)\n        coords[0] = 1.0\n        coords[1] = 2.0\n        coords[2] = 3.0\n        npt.assert_equal(np.array(coords), [1.0, 2.0, 3.0])\n\n    def test_scalar(self):\n        scalar = Scalar[DataVector](num_points=4, fill=1.0)\n        self.assertEqual(scalar.size, 1)\n        self.assertEqual(scalar.rank, 0)\n        self.assertEqual(scalar.dim, None)\n        npt.assert_equal(np.array(scalar), np.ones((1, 4)))\n        npt.assert_equal(scalar[0], scalar.get())\n\n    def test_jacobian(self):\n        jac = Jacobian[DataVector, 3](num_points=4, fill=1.0)\n        inv_jac = InverseJacobian[DataVector, 3](num_points=4, fill=1.0)\n        npt.assert_equal(np.array(jac), np.ones((9, 4)))\n        npt.assert_equal(np.array(inv_jac), np.ones((9, 4)))\n        self.assertEqual(jac.rank, 2)\n        self.assertEqual(inv_jac.rank, 2)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Test_CombineSpacetimeView.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/CombineSpacetimeView.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n\nnamespace {\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.CombineSpacetimeView\",\n                  \"[DataStructures][Unit]\") {\n  const size_t SpatialDim = 3;\n  const DataVector used_for_size(5);\n  MAKE_GENERATOR(generator);\n  const auto nn_gen = make_not_null(&generator);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  const auto nn_dist = make_not_null(&distribution);\n\n  tnsr::a<double, SpatialDim, Frame::Inertial> test_spacetime_vector;\n  const auto scalar_time_component =\n      make_with_random_values<Scalar<double>>(nn_gen, nn_dist, used_for_size);\n  const auto vector_spatial_components =\n      make_with_random_values<tnsr::i<double, SpatialDim, Frame::Inertial>>(\n          nn_gen, nn_dist, used_for_size);\n\n  combine_spacetime_view<SpatialDim, UpLo::Lo, Frame::Inertial>(\n      make_not_null(&test_spacetime_vector), scalar_time_component,\n      vector_spatial_components);\n\n  CHECK(test_spacetime_vector.get(0) == get(scalar_time_component));\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    CHECK(test_spacetime_vector.get(i + 1) == vector_spatial_components.get(i));\n  }\n\n  tnsr::Abb<DataVector, SpatialDim, Frame::Inertial> test_spacetime_tensor;\n  const auto tensor_time_component = make_with_random_values<\n      tnsr::aa<DataVector, SpatialDim, Frame::Inertial>>(nn_gen, nn_dist,\n                                                         used_for_size);\n  const auto tensor_spatial_components = make_with_random_values<\n      tnsr::Iaa<DataVector, SpatialDim, Frame::Inertial>>(nn_gen, nn_dist,\n                                                          used_for_size);\n\n  combine_spacetime_view<SpatialDim, UpLo::Up, Frame::Inertial>(\n      make_not_null(&test_spacetime_tensor), tensor_time_component,\n      tensor_spatial_components);\n\n  for (size_t i = 0; i <= SpatialDim; ++i) {\n    for (size_t j = 0; j <= SpatialDim; ++j) {\n      CHECK(test_spacetime_tensor.get(0, i, j) ==\n            tensor_time_component.get(i, j));\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        CHECK(test_spacetime_tensor.get(k + 1, i, j) ==\n              tensor_spatial_components.get(k, i, j));\n      }\n    }\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Test_ContractFirstNIndices.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/ContractFirstNIndices.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\ntemplate <typename Generator, typename DataType>\nvoid test(const gsl::not_null<Generator*> generator,\n          const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto U = make_with_random_values<tnsr::i<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n  const auto V = make_with_random_values<tnsr::ii<DataType, 3, Frame::Grid>>(\n      generator, distribution, used_for_size);\n\n  using UV_type = tnsr::ijj<DataType, 3, Frame::Grid>;\n  // contract 0 indices\n  const UV_type UV_returned = contract_first_n_indices<0>(U, V);\n  UV_type UV_filled{};\n  contract_first_n_indices<0>(make_not_null(&UV_filled), U, V);\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      for (size_t k = j; k < 3; k++) {\n        const auto expected_result = U.get(i) * V.get(j, k);\n\n        CHECK_ITERABLE_APPROX(UV_returned.get(i, j, k), expected_result);\n        CHECK_ITERABLE_APPROX(UV_filled.get(i, j, k), expected_result);\n      }\n    }\n  }\n\n  const auto R =\n      make_with_random_values<tnsr::abc<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n  const auto S =\n      make_with_random_values<tnsr::ABc<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n\n  // tnsr::abCd\n  using RS1_type =\n      Tensor<DataType, Symmetry<4, 3, 2, 1>,\n             index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>>;\n  // contract first index (spacetime)\n  const RS1_type RS1_returned = contract_first_n_indices<1>(R, S);\n  RS1_type RS1_filled{};\n  contract_first_n_indices<1>(make_not_null(&RS1_filled), R, S);\n\n  for (size_t b = 0; b < 4; b++) {\n    for (size_t c = 0; c < 4; c++) {\n      for (size_t d = 0; d < 4; d++) {\n        for (size_t e = 0; e < 4; e++) {\n          DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n          for (size_t a = 0; a < 4; a++) {\n            expected_sum += R.get(a, b, c) * S.get(a, d, e);\n          }\n\n          CHECK_ITERABLE_APPROX(RS1_returned.get(b, c, d, e), expected_sum);\n          CHECK_ITERABLE_APPROX(RS1_filled.get(b, c, d, e), expected_sum);\n        }\n      }\n    }\n  }\n\n  using RS2_type = tnsr::ab<DataType, 3, Frame::Inertial>;\n  // contract first two indices (both spacetime)\n  const RS2_type RS2_returned = contract_first_n_indices<2>(R, S);\n  RS2_type RS2_filled{};\n  contract_first_n_indices<2>(make_not_null(&RS2_filled), R, S);\n\n  for (size_t c = 0; c < 4; c++) {\n    for (size_t d = 0; d < 4; d++) {\n      DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n      for (size_t a = 0; a < 4; a++) {\n        for (size_t b = 0; b < 4; b++) {\n          expected_sum += R.get(a, b, c) * S.get(a, b, d);\n        }\n      }\n\n      CHECK_ITERABLE_APPROX(RS2_returned.get(c, d), expected_sum);\n      CHECK_ITERABLE_APPROX(RS2_filled.get(c, d), expected_sum);\n    }\n  }\n\n  const auto G =\n      make_with_random_values<tnsr::Ijaa<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n  // tnsr::iJA\n  const auto H = make_with_random_values<\n      Tensor<DataType, Symmetry<3, 2, 1>,\n             index_list<SpatialIndex<3, UpLo::Lo, Frame::Inertial>,\n                        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>>>(\n      generator, distribution, used_for_size);\n\n  using GH_type = tnsr::aaB<DataType, 3, Frame::Inertial>;\n  // contract first two indices (both spatial) of two tensors of different rank\n  const GH_type GH_returned = contract_first_n_indices<2>(G, H);\n  GH_type GH_filled{};\n  contract_first_n_indices<2>(make_not_null(&GH_filled), G, H);\n\n  using HG_type = tnsr::Abb<DataType, 3, Frame::Inertial>;\n  // for checking that opposite order of operands gives us the \"same\" result\n  // mathematically (though the LHS index order will be different)\n  const HG_type HG_returned = contract_first_n_indices<2>(H, G);\n  HG_type HG_filled{};\n  contract_first_n_indices<2>(make_not_null(&HG_filled), H, G);\n\n  for (size_t a = 0; a < 4; a++) {\n    for (size_t b = 0; b < 4; b++) {\n      for (size_t c = 0; c < 4; c++) {\n        DataType expected_sum = make_with_value<DataType>(used_for_size, 0.0);\n        for (size_t i = 0; i < 3; i++) {\n          for (size_t j = 0; j < 3; j++) {\n            expected_sum += G.get(i, j, a, b) * H.get(i, j, c);\n          }\n        }\n\n        CHECK_ITERABLE_APPROX(GH_returned.get(a, b, c), expected_sum);\n        CHECK_ITERABLE_APPROX(GH_filled.get(a, b, c), expected_sum);\n        CHECK_ITERABLE_APPROX(HG_returned.get(c, a, b), expected_sum);\n        CHECK_ITERABLE_APPROX(HG_filled.get(c, a, b), expected_sum);\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.ContractFirstNIndices\",\n                  \"[DataStructures][Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  test(make_not_null(&generator), std::numeric_limits<double>::signaling_NaN());\n  test(make_not_null(&generator),\n       DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Test_Identity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_identity(const DataType& used_for_size) {\n  const auto identity_matrix{identity<Dim>(used_for_size)};\n\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = 0; j < Dim; j++) {\n      if (i == j) {\n        CHECK_ITERABLE_APPROX(identity_matrix.get(i, j),\n                              make_with_value<DataType>(used_for_size, 1.0));\n      } else {\n        CHECK_ITERABLE_APPROX(identity_matrix.get(i, j),\n                              make_with_value<DataType>(used_for_size, 0.0));\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Identity\",\n                  \"[DataStructures][Unit]\") {\n  const double d(std::numeric_limits<double>::signaling_NaN());\n  test_identity<1>(d);\n  test_identity<2>(d);\n  test_identity<3>(d);\n\n  const DataVector dv(5);\n  test_identity<1>(dv);\n  test_identity<2>(dv);\n  test_identity<3>(dv);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Test_Metafunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdint>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass DataVector;\nnamespace Frame {\nstruct BlockLogical;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\n// Test check_index_symmetry\nstatic_assert(TensorMetafunctions::check_index_symmetry_v<tmpl::list<>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(TensorMetafunctions::check_index_symmetry_v<\n                  Symmetry<1>, SpatialIndex<3, UpLo::Lo, Frame::Grid>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(TensorMetafunctions::check_index_symmetry_v<\n                  Symmetry<1>, SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(TensorMetafunctions::check_index_symmetry_v<\n                  Symmetry<1>, SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(TensorMetafunctions::check_index_symmetry_v<\n                  Symmetry<1, 1>, SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                  SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(not TensorMetafunctions::check_index_symmetry_v<\n                  Symmetry<1, 1>, SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n                  SpatialIndex<3, UpLo::Lo, Frame::Inertial>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(not TensorMetafunctions::check_index_symmetry_v<\n                  Symmetry<1, 1>, SpatialIndex<2, UpLo::Up, Frame::Inertial>,\n                  SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(not TensorMetafunctions::check_index_symmetry_v<\n                  Symmetry<1, 1>, SpatialIndex<3, UpLo::Up, Frame::Grid>,\n                  SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(not TensorMetafunctions::check_index_symmetry_v<\n                  Symmetry<1, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n                  SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n              \"Failed testing check_index_symmetry\");\nstatic_assert(\n    TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<2, 1, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    not TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<2, 1, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<2, UpLo::Up, Frame::Inertial>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    not TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<2, 1, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Lo, Frame::Inertial>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    not TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<2, 1, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Distorted>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    not TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<2, 1, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<1, 2, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpacetimeIndex<3, UpLo::Up, Frame::Inertial>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    not TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<1, 2, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpacetimeIndex<2, UpLo::Up, Frame::Inertial>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    not TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<1, 2, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpacetimeIndex<3, UpLo::Lo, Frame::Inertial>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    not TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<1, 2, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpacetimeIndex<3, UpLo::Up, Frame::BlockLogical>>,\n    \"Failed testing check_index_symmetry\");\nstatic_assert(\n    not TensorMetafunctions::check_index_symmetry_v<\n        Symmetry<1, 2, 1>, SpacetimeIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>,\n        SpatialIndex<3, UpLo::Up, Frame::Inertial>>,\n    \"Failed testing check_index_symmetry\");\n\n// Test prepend_spacetime_index and prepend_spatial_index\nstatic_assert(std::is_same_v<tnsr::aB<double, 3, Frame::Grid>,\n                             TensorMetafunctions::prepend_spacetime_index<\n                                 tnsr::A<double, 3, Frame::Grid>, 3, UpLo::Lo,\n                                 Frame::Grid>>,\n              \"Failed testing prepend_spacetime_index\");\nstatic_assert(std::is_same_v<tnsr::iJ<double, 3, Frame::Grid>,\n                             TensorMetafunctions::prepend_spatial_index<\n                                 tnsr::I<double, 3, Frame::Grid>, 3, UpLo::Lo,\n                                 Frame::Grid>>,\n              \"Failed testing prepend_spatial_index\");\nstatic_assert(\n    std::is_same_v<\n        tnsr::iiJJ<double, 3, Frame::Grid>,\n        TensorMetafunctions::prepend_two_symmetric_spatial_indices<\n            tnsr::II<double, 3, Frame::Grid>, 3, UpLo::Lo, Frame::Grid>>,\n    \"Failed testing prepend_two_symmetric_spatial_indices\");\n\n// Test remove_first_index\nstatic_assert(\n    std::is_same_v<Scalar<double>, TensorMetafunctions::remove_first_index<\n                                       tnsr::a<double, 3, Frame::Grid>>>,\n    \"Failed testing remove_first_index\");\nstatic_assert(std::is_same_v<tnsr::A<double, 3, Frame::Grid>,\n                             TensorMetafunctions::remove_first_index<\n                                 tnsr::aB<double, 3, Frame::Grid>>>,\n              \"Failed testing remove_first_index\");\nstatic_assert(std::is_same_v<tnsr::ab<double, 3, Frame::Grid>,\n                             TensorMetafunctions::remove_first_index<\n                                 tnsr::abc<double, 3, Frame::Grid>>>,\n              \"Failed testing remove_first_index\");\nstatic_assert(\n    std::is_same_v<\n        tnsr::ab<double, 3, Frame::Grid>,\n        TensorMetafunctions::remove_first_index<Tensor<\n            double, tmpl::integral_list<std::int32_t, 2, 2, 1>,\n            index_list<Tensor_detail::TensorIndexType<3, UpLo::Lo, Frame::Grid,\n                                                      IndexType::Spacetime>,\n                       Tensor_detail::TensorIndexType<3, UpLo::Lo, Frame::Grid,\n                                                      IndexType::Spacetime>,\n                       Tensor_detail::TensorIndexType<3, UpLo::Lo, Frame::Grid,\n                                                      IndexType::Spacetime>>>>>,\n    \"Failed testing remove_first_index\");\nstatic_assert(std::is_same_v<tnsr::aa<double, 3, Frame::Grid>,\n                             TensorMetafunctions::remove_first_index<\n                                 tnsr::abb<double, 3, Frame::Grid>>>,\n              \"Failed testing remove_first_index\");\n\n// Test change_all_valences\nstatic_assert(\n    std::is_same_v<Scalar<double>,\n                   TensorMetafunctions::change_all_valences<Scalar<double>>>);\nstatic_assert(std::is_same_v<tnsr::iAA<DataVector, 3, Frame::Grid>,\n                             TensorMetafunctions::change_all_valences<\n                                 tnsr::Iaa<DataVector, 3, Frame::Grid>>>);\n\n// Test swap_type\nstatic_assert(\n    std::is_same_v<tnsr::ij<double, 3>, TensorMetafunctions::swap_type<\n                                            double, tnsr::ij<DataVector, 3>>>,\n    \"Failed testing swap_type\");\n\n// Test any_index_in_frame_v\nstatic_assert(TensorMetafunctions::any_index_in_frame_v<\n                  tnsr::iJ<double, 3, Frame::Grid>, Frame::Grid>,\n              \"Failed testing any_index_in_frame_v where it should match\");\nstatic_assert(not TensorMetafunctions::any_index_in_frame_v<\n                  tnsr::iJ<double, 3, Frame::Grid>, Frame::Inertial>,\n              \"Failed testing any_index_in_frame_v where it should not match\");\nstatic_assert(\n    TensorMetafunctions::any_index_in_frame_v<\n        Tensor<\n            double, tmpl::integral_list<std::int32_t, 2, 1>,\n            index_list<Tensor_detail::TensorIndexType<\n                           3, UpLo::Lo, Frame::Inertial, IndexType::Spacetime>,\n                       Tensor_detail::TensorIndexType<3, UpLo::Lo, Frame::Grid,\n                                                      IndexType::Spacetime>>>,\n        Frame::Inertial>,\n    \"Failed testing any_index_in_frame_v for a tensor with different frames \"\n    \"for different indices\");\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Test_Slice.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\ntemplate <typename VectorType>\nvoid test_variables_slice() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{5, 10};\n\n  const size_t x_extents = sdist(gen);\n  const size_t y_extents = sdist(gen);\n  const size_t z_extents = sdist(gen);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>> vars{\n      x_extents * y_extents * z_extents};\n  const size_t tensor_size =\n      TestHelpers::Tags::Vector<VectorType>::type::size();\n  Index<3> extents(x_extents, y_extents, z_extents);\n\n  // Test data_on_slice function by using a predictable data set where each\n  // entry is assigned a value equal to its index\n  for (size_t s = 0; s < vars.size(); ++s) {\n    // clang-tidy: do not use pointer arithmetic\n    vars.data()[s] = s;  // NOLINT\n  }\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_x(y_extents * z_extents, 0.);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_y(x_extents * z_extents, 0.);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_z(x_extents * y_extents, 0.);\n  const size_t x_offset = sdist(gen) % x_extents;\n  const size_t y_offset = sdist(gen) % y_extents;\n  const size_t z_offset = sdist(gen) % z_extents;\n\n  for (size_t s = 0; s < expected_vars_sliced_in_x.size(); ++s) {\n    // clang-tidy: do not use pointer arithmetic\n    expected_vars_sliced_in_x.data()[s] = x_offset + s * x_extents;  // NOLINT\n  }\n  for (size_t i = 0; i < tensor_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t z = 0; z < z_extents; ++z) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_y\n            .data()[x + x_extents * (z + z_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y_offset + z * y_extents);\n      }\n    }\n  }\n  for (size_t i = 0; i < tensor_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t y = 0; y < y_extents; ++y) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_z\n            .data()[x + x_extents * (y + y_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y + y_extents * z_offset);\n      }\n    }\n  }\n\n  INFO(\"Test simple slice\");\n  CHECK(data_on_slice(get<TestHelpers::Tags::Vector<VectorType>>(vars), extents,\n                      0, x_offset) ==\n        get<TestHelpers::Tags::Vector<VectorType>>(expected_vars_sliced_in_x));\n  CHECK(data_on_slice(get<TestHelpers::Tags::Vector<VectorType>>(vars), extents,\n                      1, y_offset) ==\n        get<TestHelpers::Tags::Vector<VectorType>>(expected_vars_sliced_in_y));\n  CHECK(data_on_slice(get<TestHelpers::Tags::Vector<VectorType>>(vars), extents,\n                      2, z_offset) ==\n        get<TestHelpers::Tags::Vector<VectorType>>(expected_vars_sliced_in_z));\n\n  INFO(\"Test slice of a std::optional<Tensor>\");\n  REQUIRE(data_on_slice(std::make_optional(\n                            get<TestHelpers::Tags::Vector<VectorType>>(vars)),\n                        extents, 0, x_offset)\n              .has_value());\n  CHECK(data_on_slice(std::make_optional(\n                          get<TestHelpers::Tags::Vector<VectorType>>(vars)),\n                      extents, 0, x_offset)\n            .value() ==\n        get<TestHelpers::Tags::Vector<VectorType>>(expected_vars_sliced_in_x));\n  REQUIRE(data_on_slice(std::make_optional(\n                            get<TestHelpers::Tags::Vector<VectorType>>(vars)),\n                        extents, 1, y_offset)\n              .has_value());\n  CHECK(data_on_slice(std::make_optional(\n                          get<TestHelpers::Tags::Vector<VectorType>>(vars)),\n                      extents, 1, y_offset)\n            .value() ==\n        get<TestHelpers::Tags::Vector<VectorType>>(expected_vars_sliced_in_y));\n  REQUIRE(data_on_slice(std::make_optional(\n                            get<TestHelpers::Tags::Vector<VectorType>>(vars)),\n                        extents, 2, z_offset)\n              .has_value());\n  CHECK(data_on_slice(std::make_optional(\n                          get<TestHelpers::Tags::Vector<VectorType>>(vars)),\n                      extents, 2, z_offset)\n            .value() ==\n        get<TestHelpers::Tags::Vector<VectorType>>(expected_vars_sliced_in_z));\n\n  CHECK_FALSE(\n      data_on_slice(std::optional<tnsr::I<VectorType, 3, Frame::Inertial>>{},\n                    extents, 0, x_offset)\n          .has_value());\n\n  // Test not_null<std::optional<Tensor>> interface\n  using TensorType = tnsr::I<VectorType, 3>;\n  auto optional_tensor = std::make_optional(TensorType{});\n  CHECK(optional_tensor.has_value());\n  data_on_slice(make_not_null(&optional_tensor), std::optional<TensorType>{},\n                extents, 0, x_offset);\n  CHECK_FALSE(optional_tensor.has_value());\n\n  data_on_slice(\n      make_not_null(&optional_tensor),\n      std::make_optional(get<TestHelpers::Tags::Vector<VectorType>>(vars)),\n      extents, 0, x_offset);\n  REQUIRE(optional_tensor.has_value());\n  CHECK(optional_tensor.value() ==\n        get<TestHelpers::Tags::Vector<VectorType>>(expected_vars_sliced_in_x));\n\n  optional_tensor = std::nullopt;\n  CHECK_FALSE(optional_tensor.has_value());\n  data_on_slice(make_not_null(&optional_tensor), std::optional<TensorType>{},\n                extents, 0, x_offset);\n  CHECK_FALSE(optional_tensor.has_value());\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Slice\",\n                  \"[DataStructures][Unit]\") {\n  test_variables_slice<ComplexDataVector>();\n  test_variables_slice<ComplexModalVector>();\n  test_variables_slice<DataVector>();\n  test_variables_slice<ModalVector>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Test_Tensor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <cstdint>\n#include <iterator>\n#include <numeric>\n#include <ostream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n// [change_up_lo]\nusing Index = SpatialIndex<3, UpLo::Lo, Frame::Grid>;\nusing UpIndex = change_index_up_lo<Index>;\nstatic_assert(std::is_same_v<UpIndex, SpatialIndex<3, UpLo::Up, Frame::Grid>>,\n              \"Failed testing change_index_up_lo\");\n// [change_up_lo]\n\n// [is_frame_physical]\nstatic_assert(not Frame::is_frame_physical_v<Frame::BlockLogical>,\n              \"Failed testing Frame::is_frame_physical\");\nstatic_assert(not Frame::is_frame_physical_v<Frame::ElementLogical>,\n              \"Failed testing Frame::is_frame_physical\");\nstatic_assert(not Frame::is_frame_physical_v<Frame::Distorted>,\n              \"Failed testing Frame::is_frame_physical\");\nstatic_assert(not Frame::is_frame_physical_v<Frame::Grid>,\n              \"Failed testing Frame::is_frame_physical\");\nstatic_assert(Frame::is_frame_physical_v<Frame::Inertial>,\n              \"Failed testing Frame::is_frame_physical\");\n// [is_frame_physical]\n\n// Test Symmetry metafunction\nstatic_assert(\n    std::is_same_v<Symmetry<4, 1>, tmpl::integral_list<std::int32_t, 2, 1>>,\n    \"Failed testing Symmetry\");\nstatic_assert(\n    std::is_same_v<Symmetry<1, 4>, tmpl::integral_list<std::int32_t, 2, 1>>,\n    \"Failed testing Symmetry\");\nstatic_assert(std::is_same_v<Symmetry<4>, tmpl::integral_list<std::int32_t, 1>>,\n              \"Failed testing Symmetry\");\nstatic_assert(std::is_same_v<Symmetry<1, 2, 1, 3>,\n                             tmpl::integral_list<std::int32_t, 2, 3, 2, 1>>,\n              \"Failed testing Symmetry\");\nstatic_assert(std::is_same_v<Symmetry<5, 3, 1, 1, 5>,\n                             tmpl::integral_list<std::int32_t, 1, 3, 2, 2, 1>>,\n              \"Failed testing Symmetry\");\n\nstatic_assert(not std::is_constructible_v<\n                  Tensor<double, Symmetry<>, tmpl::list<>>, tmpl::list<>>,\n              \"Tensor construction failed to be SFINAE friendly\");\nstatic_assert(\n    std::is_constructible_v<Tensor<double, Symmetry<>, tmpl::list<>>, double>,\n    \"Tensor construction failed to be SFINAE friendly\");\n\nnamespace {\ntemplate <typename DataType, size_t SpatialDim, typename Fr = Frame::Inertial,\n          IndexType Index = IndexType::Spacetime>\nusing abcd = Tensor<\n    DataType, tmpl::integral_list<std::int32_t, 4, 3, 2, 1>,\n    index_list<\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>,\n        Tensor_detail::TensorIndexType<SpatialDim, UpLo::Lo, Fr, Index>>>;\n\n// Test construction of high-rank, high-dim tensors\nconstexpr abcd<double, 7> check_construction{};\n// Silence unused variable warning\nstatic_assert(check_construction.rank() == 4, \"Wrong structure\");\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.ComponentNames\",\n                  \"[DataStructures][Unit]\") {\n  // [spatial_vector]\n  tnsr::I<double, 3, Frame::Grid> spatial_vector3{};\n  // [spatial_vector]\n  CHECK(spatial_vector3.component_name(std::array<size_t, 1>{{0}}) == \"x\");\n  CHECK(spatial_vector3.component_name(std::array<size_t, 1>{{1}}) == \"y\");\n  CHECK(spatial_vector3.component_name(std::array<size_t, 1>{{2}}) == \"z\");\n\n  // [spacetime_vector]\n  tnsr::A<double, 3, Frame::Grid> spacetime_vector3{};\n  // [spacetime_vector]\n  CHECK(spacetime_vector3.component_name(std::array<size_t, 1>{{0}}) == \"t\");\n  CHECK(spacetime_vector3.component_name(std::array<size_t, 1>{{1}}) == \"x\");\n  CHECK(spacetime_vector3.component_name(std::array<size_t, 1>{{2}}) == \"y\");\n  CHECK(spacetime_vector3.component_name(std::array<size_t, 1>{{3}}) == \"z\");\n\n  // [scalar]\n  Tensor<double> scalar{};\n  // [scalar]\n  CHECK(scalar.component_name() == \"Scalar\");\n\n  // [rank_3_122]\n  Tensor<double, Symmetry<1, 2, 2>,\n         index_list<SpacetimeIndex<1, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<1, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<1, UpLo::Lo, Frame::Grid>>>\n      tensor_1{};\n  // [rank_3_122]\n  CHECK(tensor_1.component_name(std::array<size_t, 3>{{0, 0, 0}}) == \"txx\");\n  CHECK(tensor_1.component_name(std::array<size_t, 3>{{1, 0, 0}}) == \"xxx\");\n\n  Tensor<double, Symmetry<1, 2, 2>,\n         index_list<SpacetimeIndex<2, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<2, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<2, UpLo::Lo, Frame::Grid>>>\n      tensor_2{};\n\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{0, 0, 0}}) == \"txx\");\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{1, 0, 0}}) == \"xxx\");\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{2, 0, 0}}) == \"yxx\");\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{0, 1, 0}}) == \"tyx\");\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{1, 1, 0}}) == \"xyx\");\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{2, 1, 0}}) == \"yyx\");\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{0, 1, 1}}) == \"tyy\");\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{1, 1, 1}}) == \"xyy\");\n  CHECK(tensor_2.component_name(std::array<size_t, 3>{{2, 1, 1}}) == \"yyy\");\n\n  Tensor<double, Symmetry<1, 2, 2>,\n         index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor_3{};\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{0, 0, 0}}) == \"xxx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{1, 0, 0}}) == \"yxx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{2, 0, 0}}) == \"zxx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{0, 1, 0}}) == \"xyx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{1, 1, 0}}) == \"yyx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{2, 1, 0}}) == \"zyx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{0, 2, 0}}) == \"xzx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{1, 2, 0}}) == \"yzx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{2, 2, 0}}) == \"zzx\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{0, 1, 1}}) == \"xyy\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{1, 1, 1}}) == \"yyy\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{2, 1, 1}}) == \"zyy\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{0, 2, 1}}) == \"xzy\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{1, 2, 1}}) == \"yzy\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{2, 2, 1}}) == \"zzy\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{0, 2, 2}}) == \"xzz\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{1, 2, 2}}) == \"yzz\");\n  CHECK(tensor_3.component_name(std::array<size_t, 3>{{2, 2, 2}}) == \"zzz\");\n\n  Tensor<double, Symmetry<1, 2, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor_4{};\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 0, 0}}) == \"ttt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 0, 0}}) == \"xtt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 0, 0}}) == \"ytt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 0, 0}}) == \"ztt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 1, 0}}) == \"txt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 1, 0}}) == \"xxt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 1, 0}}) == \"yxt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 1, 0}}) == \"zxt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 2, 0}}) == \"tyt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 2, 0}}) == \"xyt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 2, 0}}) == \"yyt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 2, 0}}) == \"zyt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 3, 0}}) == \"tzt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 3, 0}}) == \"xzt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 3, 0}}) == \"yzt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 3, 0}}) == \"zzt\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 1, 1}}) == \"txx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 1, 1}}) == \"xxx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 1, 1}}) == \"yxx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 1, 1}}) == \"zxx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 2, 1}}) == \"tyx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 2, 1}}) == \"xyx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 2, 1}}) == \"yyx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 2, 1}}) == \"zyx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 3, 1}}) == \"tzx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 3, 1}}) == \"xzx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 3, 1}}) == \"yzx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 3, 1}}) == \"zzx\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 2, 2}}) == \"tyy\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 2, 2}}) == \"xyy\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 2, 2}}) == \"yyy\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 2, 2}}) == \"zyy\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 3, 2}}) == \"tzy\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 3, 2}}) == \"xzy\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 3, 2}}) == \"yzy\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 3, 2}}) == \"zzy\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{0, 3, 3}}) == \"tzz\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{1, 3, 3}}) == \"xzz\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{2, 3, 3}}) == \"yzz\");\n  CHECK(tensor_4.component_name(std::array<size_t, 3>{{3, 3, 3}}) == \"zzz\");\n\n  Tensor<double, Symmetry<1, 2, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor_5{};\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 0, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"aaa\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 0, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"baa\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 0, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"caa\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 0, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"daa\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 1, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"aba\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 1, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bba\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 1, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"cba\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 1, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"dba\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 2, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"aca\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 2, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bca\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 2, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"cca\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 2, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"dca\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 3, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"ada\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 3, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bda\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 3, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"cda\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 3, 0}},\n                                make_array<3>(std::string(\"abcd\"))) == \"dda\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 1, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"abb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 1, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bbb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 1, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"cbb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 1, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"dbb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 2, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"acb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 2, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bcb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 2, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"ccb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 2, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"dcb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 3, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"adb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 3, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bdb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 3, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"cdb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 3, 1}},\n                                make_array<3>(std::string(\"abcd\"))) == \"ddb\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 2, 2}},\n                                make_array<3>(std::string(\"abcd\"))) == \"acc\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 2, 2}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bcc\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 2, 2}},\n                                make_array<3>(std::string(\"abcd\"))) == \"ccc\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 2, 2}},\n                                make_array<3>(std::string(\"abcd\"))) == \"dcc\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 3, 2}},\n                                make_array<3>(std::string(\"abcd\"))) == \"adc\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 3, 2}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bdc\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 3, 2}},\n                                make_array<3>(std::string(\"abcd\"))) == \"cdc\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 3, 2}},\n                                make_array<3>(std::string(\"abcd\"))) == \"ddc\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{0, 3, 3}},\n                                make_array<3>(std::string(\"abcd\"))) == \"add\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{1, 3, 3}},\n                                make_array<3>(std::string(\"abcd\"))) == \"bdd\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{2, 3, 3}},\n                                make_array<3>(std::string(\"abcd\"))) == \"cdd\");\n  CHECK(tensor_5.component_name(std::array<size_t, 3>{{3, 3, 3}},\n                                make_array<3>(std::string(\"abcd\"))) == \"ddd\");\n\n  // The component_name is tested extensively above, so not checking that again\n  // for component_suffix\n  CHECK(scalar.component_suffix().empty());\n  CHECK(tensor_1.component_suffix(std::array<size_t, 3>{{0, 0, 0}}) == \"_txx\");\n  CHECK(tensor_5.component_suffix(std::array<size_t, 3>{{0, 2, 1}},\n                                  make_array<3>(std::string(\"abcd\"))) ==\n        \"_acb\");\n  CHECK(scalar.component_suffix(0).empty());\n  CHECK(tensor_2.component_suffix(3) == \"_tyx\");\n  CHECK(tensor_3.component_suffix(12) == \"_xzy\");\n\n  CHECK_THROWS_WITH(\n      (Tensor<double, Symmetry<1>,\n              index_list<SpacetimeIndex<5, UpLo::Lo, Frame::Grid>>>{3.0}\n           .component_name(std::array<size_t, 1>{{4}})),\n      Catch::Matchers::ContainsSubstring(\n          \"Tensor dim[0] must be 1,2,3, or 4 for default axis_labels\"));\n  CHECK_THROWS_WITH(\n      (Tensor<double, Symmetry<1>,\n              index_list<SpatialIndex<6, UpLo::Lo, Frame::Grid>>>{3.0}\n           .component_name(std::array<size_t, 1>{{4}})),\n      Catch::Matchers::ContainsSubstring(\n          \"Tensor dim[0] must be 1,2, or 3 for default axis_labels\"));\n  CHECK_THROWS_WITH(\n      (Tensor<double, Symmetry<1, 2, 2>,\n              index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                         SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                         SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>{}\n           .component_name(\n               tensor_5.get_tensor_index(size_t{0}),  // 0 can be a pointer\n               make_array<3>(std::string(\"abcdefgh\")))),\n      Catch::Matchers::ContainsSubstring(\n          \"Dimension mismatch: Tensor has dim = 4, but you \"\n          \"specified 8 different labels in abcdefgh\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.RankAndSize\",\n                  \"[DataStructures][Unit]\") {\n  {\n    Scalar<double> scalar{2.8};\n    CHECK(scalar.multiplicity(0_st) == 1);  // 0 can be a pointer\n    CHECK(scalar.symmetries() == (std::array<int, 0>{}));\n    CHECK(scalar.get() == 2.8);\n  }\n\n  {\n    Tensor<double, Symmetry<3>,\n           index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n        spatial_vector3{std::array<double, 3>{{1, 8, 3}}};\n    CHECK(index_dim<0>(spatial_vector3) == 3);\n    CHECK(1 == spatial_vector3.rank());\n    CHECK(3 == spatial_vector3.size());\n    CHECK(spatial_vector3.get(0) == 1);\n    CHECK(spatial_vector3.get(1) == 8);\n    CHECK(spatial_vector3.get(2) == 3);\n    CHECK(get<0>(spatial_vector3) == 1);\n    CHECK(get<1>(spatial_vector3) == 8);\n    CHECK(get<2>(spatial_vector3) == 3);\n  }\n\n  {\n    // test const functions\n    const Tensor<double, Symmetry<3>,\n                 index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n        spatial_vector3{std::array<double, 3>{{1, 8, 3}}};\n    CHECK(Scalar<double>{}.multiplicity(0_st) == 1);  // 0 can be a pointer\n    CHECK(index_dim<0>(spatial_vector3) == 3);\n    CHECK(1 == spatial_vector3.rank());\n    CHECK(3 == spatial_vector3.size());\n    CHECK(spatial_vector3.get(0) == 1);\n    CHECK(spatial_vector3.get(1) == 8);\n    CHECK(spatial_vector3.get(2) == 3);\n    CHECK(get<0>(spatial_vector3) == 1);\n    CHECK(get<1>(spatial_vector3) == 8);\n    CHECK(get<2>(spatial_vector3) == 3);\n  }\n\n  {\n    // [index_dim]\n    using T = Tensor<double, Symmetry<1, 2, 3>,\n                     index_list<SpacetimeIndex<2, UpLo::Up, Frame::Inertial>,\n                                SpatialIndex<1, UpLo::Up, Frame::Inertial>,\n                                SpatialIndex<2, UpLo::Up, Frame::Inertial>>>;\n    const T t{};\n    CHECK(index_dim<0>(t) == 3);\n    CHECK(index_dim<1>(t) == 1);\n    CHECK(index_dim<2>(t) == 2);\n    CHECK(T::index_dim(0) == 3);\n    CHECK(T::index_dim(1) == 1);\n    CHECK(T::index_dim(2) == 2);\n    CHECK(T::index_dims() == std::array<size_t, 3>{{3, 1, 2}});\n    // [index_dim]\n  }\n\n  Tensor<double, Symmetry<3>,\n         index_list<SpatialIndex<4, UpLo::Lo, Frame::Grid>>>\n      spatial_vector4{};\n  CHECK(1 == spatial_vector4.rank());\n  CHECK(4 == spatial_vector4.size());\n  Tensor<double, Symmetry<1, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      symmetric_rank2_dim4{};\n  CHECK(2 == symmetric_rank2_dim4.rank());\n  CHECK(10 == symmetric_rank2_dim4.size());\n  double i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : symmetric_rank2_dim4) {\n    p = i;\n    ++i;\n  }\n  for (size_t j = 0; j < 3; ++j) {\n    for (size_t k = 0; k < 3; ++k) {\n      CHECK(symmetric_rank2_dim4.get(j, k) == symmetric_rank2_dim4.get(k, j));\n    }\n  }\n  Tensor<double, Symmetry<1, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      rank2_4{};\n  CHECK(2 == rank2_4.rank());\n  CHECK(16 == rank2_4.size());\n  Tensor<double, Symmetry<1, 2, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      symmetric_rank3_dim4{};\n  CHECK(3 == symmetric_rank3_dim4.rank());\n  CHECK(40 == symmetric_rank3_dim4.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : symmetric_rank3_dim4) {\n    p = i;\n    ++i;\n  }\n  for (size_t j = 0; j < 3; ++j) {\n    for (size_t k = 0; k < 3; ++k) {\n      for (size_t l = 0; l < 3; ++l) {\n        CHECK(symmetric_rank3_dim4.get(j, k, l) ==\n              symmetric_rank3_dim4.get(j, l, k));\n        if (l != j) {\n          CHECK(symmetric_rank3_dim4.get(j, k, l) !=\n                symmetric_rank3_dim4.get(l, k, j));\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<1, 1, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      symmetric_all_rank3_dim4{};\n  CHECK(3 == symmetric_all_rank3_dim4.rank());\n  CHECK(20 == symmetric_all_rank3_dim4.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : symmetric_all_rank3_dim4) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        CHECK(symmetric_all_rank3_dim4.get(l, j, k) ==\n              symmetric_all_rank3_dim4.get(l, k, j));\n        CHECK(symmetric_all_rank3_dim4.get(l, j, k) ==\n              symmetric_all_rank3_dim4.get(j, k, l));\n        CHECK(symmetric_all_rank3_dim4.get(l, j, k) ==\n              symmetric_all_rank3_dim4.get(j, l, k));\n        CHECK(symmetric_all_rank3_dim4.get(l, j, k) ==\n              symmetric_all_rank3_dim4.get(k, l, j));\n        CHECK(symmetric_all_rank3_dim4.get(l, j, k) ==\n              symmetric_all_rank3_dim4.get(k, j, l));\n      }\n    }\n  }\n  Tensor<double, Symmetry<1, 1, 1>,\n         index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      iii{};\n  CHECK(3 == iii.rank());\n  CHECK(10 == iii.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : iii) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 2; ++l) {\n    for (size_t j = 0; j < 2; ++j) {\n      for (size_t k = 0; k < 2; ++k) {\n        CHECK(iii.get(l, j, k) == iii.get(l, k, j));\n        CHECK(iii.get(l, j, k) == iii.get(j, k, l));\n        CHECK(iii.get(l, j, k) == iii.get(j, l, k));\n        CHECK(iii.get(l, j, k) == iii.get(k, l, j));\n        CHECK(iii.get(l, j, k) == iii.get(k, j, l));\n      }\n    }\n  }\n  Tensor<double, Symmetry<3, 2, 1, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      abcc{};\n  CHECK(4 == abcc.rank());\n  CHECK(160 == abcc.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : abcc) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(abcc.get(l, j, k, m) == abcc.get(l, j, m, k));\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<2, 3, 1, 3>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      abcb{};\n  CHECK(4 == abcb.rank());\n  CHECK(160 == abcb.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : abcb) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(abcb.get(l, j, k, m) == abcb.get(l, m, k, j));\n          if (j != l) {\n            CHECK(abcb.get(l, j, k, m) != abcb.get(j, l, k, m));\n          }\n          if (k != l) {\n            CHECK(abcb.get(l, j, k, m) != abcb.get(k, j, l, m));\n          }\n          if (j != k) {\n            CHECK(abcb.get(l, j, k, m) != abcb.get(l, k, j, m));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<3, 2, 1, 3>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      abca{};\n  CHECK(4 == abca.rank());\n  CHECK(160 == abca.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : abca) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(abca.get(l, j, k, m) == abca.get(m, j, k, l));\n          if (j != l) {\n            CHECK(abca.get(l, j, k, m) != abca.get(j, l, k, m));\n          }\n          if (k != l) {\n            CHECK(abca.get(l, j, k, m) != abca.get(k, j, l, m));\n          }\n          if (k != j) {\n            CHECK(abca.get(l, j, k, m) != abca.get(l, k, j, m));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<3, 2, 3, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      abac{};\n  CHECK(4 == abac.rank());\n  CHECK(160 == abac.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : abac) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(abac.get(l, j, k, m) == abac.get(k, j, l, m));\n          if (l != j) {\n            CHECK(abac.get(l, j, k, m) != abac.get(j, l, k, m));\n          }\n          if (l != m) {\n            CHECK(abac.get(l, j, k, m) != abac.get(m, j, k, l));\n          }\n          if (j != m) {\n            CHECK(abac.get(l, j, k, m) != abac.get(l, m, k, j));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<3, 3, 2, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      aabc{};\n  CHECK(4 == aabc.rank());\n  CHECK(160 == aabc.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : aabc) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(aabc.get(l, j, k, m) == aabc.get(j, l, k, m));\n          if (l != k) {\n            CHECK(aabc.get(l, j, k, m) != aabc.get(k, j, l, m));\n          }\n          if (l != m) {\n            CHECK(aabc.get(l, j, k, m) != aabc.get(m, j, k, l));\n          }\n          if (m != k) {\n            CHECK(aabc.get(l, j, k, m) != aabc.get(l, j, m, k));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<2, 2, 1, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      aabb{};\n  CHECK(4 == aabb.rank());\n  CHECK(100 == aabb.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : aabb) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(aabb.get(l, j, k, m) == aabb.get(j, l, k, m));\n          CHECK(aabb.get(l, j, k, m) == aabb.get(j, l, m, k));\n          CHECK(aabb.get(l, j, k, m) == aabb.get(l, j, m, k));\n          if (j != k) {\n            CHECK(aabb.get(l, j, k, m) != aabb.get(l, k, j, m));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<2, 1, 2, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      abab{};\n  CHECK(4 == abab.rank());\n  CHECK(100 == abab.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : abab) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(abab.get(l, j, k, m) == abab.get(k, j, l, m));\n          CHECK(abab.get(l, j, k, m) == abab.get(k, m, l, j));\n          CHECK(abab.get(l, j, k, m) == abab.get(l, m, k, j));\n          if (l != j) {\n            CHECK(abab.get(l, j, k, m) != abab.get(j, l, k, m));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<2, 1, 1, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      abba{};\n  CHECK(4 == abba.rank());\n  CHECK(100 == abba.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : abba) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(abba.get(l, j, k, m) == abba.get(l, k, j, m));\n          CHECK(abba.get(l, j, k, m) == abba.get(m, k, j, l));\n          CHECK(abba.get(l, j, k, m) == abba.get(m, j, k, l));\n          if (l != j) {\n            CHECK(abba.get(l, j, k, m) != abba.get(j, l, k, m));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<2, 2, 2, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      aaab{};\n  CHECK(4 == aaab.rank());\n  CHECK(80 == aaab.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : aaab) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(aaab.get(l, j, k, m) == aaab.get(l, k, j, m));\n          CHECK(aaab.get(l, j, k, m) == aaab.get(j, l, k, m));\n          CHECK(aaab.get(l, j, k, m) == aaab.get(j, k, l, m));\n          CHECK(aaab.get(l, j, k, m) == aaab.get(k, l, j, m));\n          CHECK(aaab.get(l, j, k, m) == aaab.get(k, j, l, m));\n          if (l != m) {\n            CHECK(aaab.get(l, j, k, m) != aaab.get(m, j, k, l));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<2, 2, 1, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      aaba{};\n  CHECK(4 == aaba.rank());\n  CHECK(80 == aaba.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : aaba) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(aaba.get(l, j, k, m) == aaba.get(l, m, k, j));\n          CHECK(aaba.get(l, j, k, m) == aaba.get(j, l, k, m));\n          CHECK(aaba.get(l, j, k, m) == aaba.get(j, m, k, l));\n          CHECK(aaba.get(l, j, k, m) == aaba.get(m, l, k, j));\n          CHECK(aaba.get(l, j, k, m) == aaba.get(m, j, k, l));\n          if(l != k) {\n            CHECK(aaba.get(l, j, k, m) != aaba.get(k, j, l, m));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<2, 1, 2, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      abaa{};\n  CHECK(4 == abaa.rank());\n  CHECK(80 == abaa.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : abaa) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(abaa.get(l, j, k, m) == abaa.get(l, j, m, k));\n          CHECK(abaa.get(l, j, k, m) == abaa.get(k, j, m, l));\n          CHECK(abaa.get(l, j, k, m) == abaa.get(k, j, l, m));\n          CHECK(abaa.get(l, j, k, m) == abaa.get(m, j, l, k));\n          CHECK(abaa.get(l, j, k, m) == abaa.get(m, j, k, l));\n          if (l != j) {\n            CHECK(abaa.get(l, j, k, m) != abaa.get(j, l, k, m));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<2, 1, 1, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      abbb{};\n  CHECK(4 == abbb.rank());\n  CHECK(80 == abbb.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : abbb) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(abbb.get(l, j, k, m) == abbb.get(l, j, m, k));\n          CHECK(abbb.get(l, j, k, m) == abbb.get(l, k, m, j));\n          CHECK(abbb.get(l, j, k, m) == abbb.get(l, k, j, m));\n          CHECK(abbb.get(l, j, k, m) == abbb.get(l, m, k, j));\n          CHECK(abbb.get(l, j, k, m) == abbb.get(l, m, j, k));\n          if (l != j) {\n            CHECK(abbb.get(l, j, k, m) != abbb.get(j, l, k, m));\n          }\n        }\n      }\n    }\n  }\n  Tensor<double, Symmetry<1, 1, 1, 1>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      aaaa{};\n  CHECK(4 == aaaa.rank());\n  CHECK(35 == aaaa.size());\n  i = 1.948;  // not a value likely to be default-constructed to\n  for (auto& p : aaaa) {\n    p = i;\n    ++i;\n  }\n  for (size_t l = 0; l < 3; ++l) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t m = 0; m < 3; ++m) {\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(l, j, m, k));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(l, k, m, j));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(l, k, j, m));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(l, m, k, j));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(l, m, j, k));\n\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(j, k, l, m));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(j, k, m, l));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(j, m, k, l));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(j, m, l, k));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(j, l, m, k));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(j, l, k, m));\n\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(k, m, j, l));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(k, m, l, j));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(k, j, m, l));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(k, j, l, m));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(k, l, j, m));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(k, l, m, j));\n\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(m, l, j, k));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(m, l, k, j));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(m, j, l, k));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(m, j, k, l));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(m, k, l, j));\n          CHECK(aaaa.get(l, j, k, m) == aaaa.get(m, k, j, l));\n        }\n      }\n    }\n  }\n}\n\nnamespace {\nconstexpr size_t test_constexpr_tensor() {\n  const tnsr::aa<int, 3> tensor{1};\n  static_assert(tensor.size() == 10);\n  size_t num_components = 0;\n  for (const auto& component : tensor) {\n    (void)component;\n    num_components++;\n  }\n  return num_components;\n}\n\nstatic_assert(test_constexpr_tensor() == 10,\n              \"Tensor iterators should work in constexpr context\");\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Indices\",\n                  \"[DataStructures][Unit]\") {\n  // Tests that iterators correctly handle the symmetries. However, as a result\n  // the test is implementation defined\n  Tensor<double, Symmetry<1, 2, 2>,\n         index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor{};\n  auto it = tensor.begin();\n  for (size_t j = 0; j < 3; ++j) {\n    for (size_t i = j; i < 3; ++i) {\n      for (size_t k = 0; k < 3; ++k) {\n        CHECK(make_array(k, i, j) == tensor.get_tensor_index(it));\n        ++it;\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Iterating\",\n                  \"[DataStructures][Unit]\") {\n  // Fills two tensors with values such that A+B =1 and then checks that this is\n  // the case doing a for (int...) loop over the Tensor's underlying storage\n  // vector, which is iterating over all independent components.\n  const int dim = 4;\n  Tensor<DataVector, Symmetry<1, 2, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor_a(1_st);\n  Tensor<DataVector, Symmetry<1, 2, 2>,\n         index_list<SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpacetimeIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor_b(1_st);\n\n  CHECK(dim * dim * (dim + 1) / 2 == tensor_a.size());\n  CHECK(tensor_a.size() == tensor_b.size());\n\n  // Fill the tensors\n  for (size_t i = 0; i < dim; i++) {\n    for (size_t j = 0; j < dim; j++) {\n      for (size_t k = j; k < dim; k++) {\n        tensor_a.get(i, j, k)[0] = (i + 1.0) * (j + 2.0) * k;\n        tensor_b.get(i, j, k)[0] = 1.0 - (i + 1.0) * (j + 2.0) * k;\n      }\n    }\n  }\n\n  // Test simple iteration with an integer\n  size_t count = 0;\n  for (size_t i = 0; i < tensor_a.size(); ++i) {\n    CHECK(tensor_a[i][0] + tensor_b[i][0] == 1);\n    count++;\n  }\n  CHECK(count == dim * dim * (dim + 1) / 2);\n\n  // Check iteration with an integer and forward iterator\n  count = 0;\n  for (auto p = tensor_a.begin(); p != tensor_a.end(); ++p) {\n    CHECK(tensor_a[count][0] == (*p)[0]);\n    CHECK(&tensor_a[count] == &(*p));\n    count++;\n  }\n  CHECK(count == tensor_a.size());\n  // Check const version\n  count = 0;\n  for (const auto& p : tensor_a) {\n    CHECK(tensor_a[count][0] == p[0]);\n    CHECK(&tensor_a[count] == &p);\n    count++;\n  }\n  CHECK(count == tensor_a.size());\n  // Check iteration with an integer and reverse iterator\n  count = tensor_a.size();\n  for (auto p = tensor_a.rbegin(); p != tensor_a.rend(); ++p) {\n    CHECK(tensor_a[count - 1][0] == (*p)[0]);\n    CHECK(&tensor_a[count - 1] == &(*p));\n    count--;\n  }\n  CHECK(count == 0);\n\n  // Check sum of components\n  const int dim2 = 3;\n  Tensor<DataVector, Symmetry<1, 2, 2>,\n         index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor_c(1_st);\n  for (size_t k = 0; k < dim2; k++) {\n    for (size_t i = 0; i < dim2; i++) {\n      for (size_t j = i; j < dim2; j++) {\n        tensor_c.get(k, i, j)[0] = 100. * (k + 1.) + 10 * (i + 1.) + j + 1.;\n      }\n    }\n  }\n\n  count = 0;\n  double sum = 0.;\n  for (const auto& elem : tensor_c) {\n    ++count;\n    sum += elem[0];\n  }\n  CHECK(count == 18);\n  CHECK(sum == 3942.);\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.IndexByVector\",\n                  \"[DataStructures][Unit]\") {\n  Tensor<DataVector, Symmetry<1, 2, 2>,\n         index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor(1_st);\n  // These numbers are deliberately chosen to be obscure for debugging\n  // purposes.\n  double inc = 137.4;\n  for (auto it = tensor.begin(); it != tensor.end(); ++it) {\n    (*it)[0] = inc;\n    // A.get_tensor_index(it) returns a vector<int> which is used to test\n    // A(vector<int>)\n    CHECK(tensor.get(tensor.get_tensor_index(it))[0] == inc);\n    inc += 23.1;\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Multiplicity\",\n                  \"[DataStructures][Unit]\") {\n  Tensor<DataVector, Symmetry<1, 2, 2>,\n         index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>,\n                    SpatialIndex<3, UpLo::Lo, Frame::Grid>>>\n      tensor;\n\n  // Test multiplicity by iterator\n  for (auto it = tensor.cbegin(); it != tensor.cend(); ++it) {\n    const auto& indices = tensor.get_tensor_index(it);\n    const size_t multiplicity = (indices[1] == indices[2] ? 1 : 2);\n    CHECK(multiplicity == tensor.multiplicity(it));\n  }\n\n  // Test multiplicity by integer\n  for (size_t i = 0; i < tensor.size(); ++i) {\n    const auto& indices = tensor.get_tensor_index(i);\n    const size_t multiplicity = (indices[1] == indices[2] ? 1 : 2);\n    CHECK(multiplicity == tensor.multiplicity(i));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.StreamData\",\n                  \"[DataStructures][Unit]\") {\n  std::string compare_out =\n      \"T(0)=(2,2,2,2,2,2,2,2,2,2)\\n\"\n      \"T(1)=(2,2,2,2,2,2,2,2,2,2)\\n\"\n      \"T(2)=(2,2,2,2,2,2,2,2,2,2)\";\n\n  CHECK(get_output(Tensor<DataVector, Symmetry<1>,\n                          index_list<SpatialIndex<3, UpLo::Lo, Frame::Grid>>>(\n            10_st, 2.0)) == compare_out);\n\n  compare_out =\n      \"T()=(2,2,2,2,2,2,2,2,2,2)\";\n  CHECK(get_output(Scalar<DataVector>(10_st, 2.0)) == compare_out);\n\n  compare_out =\n      \"T(0,0,0)=0\\n\"\n      \"T(1,0,0)=1\\n\"\n      \"T(2,0,0)=2\\n\"\n      \"T(0,1,0)=3\\n\"\n      \"T(1,1,0)=4\\n\"\n      \"T(2,1,0)=5\\n\"\n      \"T(0,2,0)=6\\n\"\n      \"T(1,2,0)=7\\n\"\n      \"T(2,2,0)=8\\n\"\n      \"T(0,1,1)=9\\n\"\n      \"T(1,1,1)=10\\n\"\n      \"T(2,1,1)=11\\n\"\n      \"T(0,2,1)=12\\n\"\n      \"T(1,2,1)=13\\n\"\n      \"T(2,2,1)=14\\n\"\n      \"T(0,2,2)=15\\n\"\n      \"T(1,2,2)=16\\n\"\n      \"T(2,2,2)=17\";\n\n  CHECK(get_output([]() {\n          tnsr::abb<double, 2, Frame::Inertial> tensor{};\n          std::iota(tensor.begin(), tensor.end(), 0);\n          return tensor;\n        }()) == compare_out);\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.StreamStructure\",\n                  \"[DataStructures][Unit]\") {\n  {\n    const Scalar<double> tensor{};\n    CHECK(get_output(tensor.symmetries()) == \"()\");\n    CHECK(get_output(tensor.index_types()) == \"()\");\n    CHECK(get_output(tensor.index_dims()) == \"()\");\n    CHECK(get_output(tensor.index_valences()) == \"()\");\n    CHECK(get_output(tensor.index_frames()) == \"()\");\n  }\n  {\n    using structure = Tensor_detail::Structure<\n        Symmetry<1, 1, 3, 2>, SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n        SpatialIndex<2, UpLo::Lo, Frame::Inertial>,\n        SpacetimeIndex<3, UpLo::Lo, Frame::BlockLogical>,\n        SpacetimeIndex<2, UpLo::Up, Frame::Distorted>>;\n\n    CHECK(get_output(structure::symmetries()) == \"(3,3,2,1)\");\n    CHECK(get_output(structure::index_types()) ==\n          \"(Spatial,Spatial,Spacetime,Spacetime)\");\n    CHECK(get_output(structure::dims()) == \"(2,2,4,3)\");\n    CHECK(get_output(structure::index_valences()) == \"(Lo,Lo,Lo,Up)\");\n    CHECK(get_output(structure::index_frames()) ==\n          \"(Inertial,Inertial,BlockLogical,Distorted)\");\n  }\n}\n\nnamespace {\n// Helper for calculating the flattened 1D index of a multi-dimensional index\ntemplate <size_t Rank>\nsize_t get_flattened_index(const cpp20::array<size_t, Rank>& tensor_index,\n                           const std::array<size_t, Rank>& index_dims) {\n  if constexpr (Rank == 0) {\n    return 0;\n  } else if constexpr (Rank == 1) {\n    return tensor_index[0];\n  } else {\n    size_t flattened_index = gsl::at(tensor_index, Rank - 1);\n    for (size_t i = Rank - 2; i < Rank; i--) {\n      flattened_index =\n          gsl::at(tensor_index, i) + gsl::at(index_dims, i) * flattened_index;\n    }\n    return flattened_index;\n  }\n}\n\n// Checks that:\n// (1) `get_storage_index` and `get_canonical_tensor_index` are inverses\n// (2) `canonicalize_tensor_index` yields the same canonical multi-index for\n//     components with symmetric indices\n//\n// (1) tests `Structure`'s interface while (2) tests its internal logic\ntemplate <typename S>\nvoid check_tensor_index_canonicalization(const S& structure) {\n  constexpr size_t rank = S::rank();\n  static_assert(\n      rank > 0 and rank <= 4,\n      \"check_tensor_index_canonicalization only implemented for ranks 1 - 4\");\n\n  using symmetry = typename S::symmetry;\n  constexpr auto index_dims = S::dims();\n  constexpr size_t num_ind_components = S::size();\n  constexpr size_t num_total_components =\n      Tensor_detail::number_of_components(index_dims);\n  std::array<bool, num_total_components> index_hit{};\n  std::fill(index_hit.begin(), index_hit.end(), false);\n\n  size_t num_unique_canon_indices = 0;\n  if constexpr (rank == 1) {\n    for (size_t i = 0; i < index_dims[0]; i++) {\n      const auto canon_multi_index =\n          Tensor_detail::canonicalize_tensor_index<symmetry>(\n              cpp20::array<size_t, rank>{{i}});\n      const size_t flattened_index =\n          get_flattened_index(canon_multi_index, index_dims);\n      if (not gsl::at(index_hit, flattened_index)) {\n        num_unique_canon_indices++;\n      }\n      gsl::at(index_hit, flattened_index) = true;\n\n      CHECK(structure.get_storage_index(std::array<size_t, rank>{{i}}) ==\n            structure.get_storage_index(structure.get_canonical_tensor_index(\n                structure.get_storage_index(std::array<size_t, rank>{{i}}))));\n    }\n  } else if constexpr (rank == 2) {\n    for (size_t i = 0; i < index_dims[0]; i++) {\n      for (size_t j = 0; j < index_dims[1]; j++) {\n        const auto canon_multi_index =\n            Tensor_detail::canonicalize_tensor_index<symmetry>(\n                cpp20::array<size_t, rank>{{i, j}});\n        const size_t flattened_index =\n            get_flattened_index(canon_multi_index, index_dims);\n        if (not gsl::at(index_hit, flattened_index)) {\n          num_unique_canon_indices++;\n        }\n        gsl::at(index_hit, flattened_index) = true;\n\n        CHECK(structure.get_storage_index(std::array<size_t, rank>{{i, j}}) ==\n              structure.get_storage_index(structure.get_canonical_tensor_index(\n                  structure.get_storage_index(\n                      std::array<size_t, rank>{{i, j}}))));\n      }\n    }\n  } else if constexpr (rank == 3) {\n    for (size_t i = 0; i < index_dims[0]; i++) {\n      for (size_t j = 0; j < index_dims[1]; j++) {\n        for (size_t k = 0; k < index_dims[2]; k++) {\n          const auto canon_multi_index =\n              Tensor_detail::canonicalize_tensor_index<symmetry>(\n                  cpp20::array<size_t, rank>{{i, j, k}});\n          const size_t flattened_index =\n              get_flattened_index(canon_multi_index, index_dims);\n          if (not gsl::at(index_hit, flattened_index)) {\n            num_unique_canon_indices++;\n          }\n          gsl::at(index_hit, flattened_index) = true;\n\n          CHECK(\n              structure.get_storage_index(\n                  std::array<size_t, rank>{{i, j, k}}) ==\n              structure.get_storage_index(structure.get_canonical_tensor_index(\n                  structure.get_storage_index(\n                      std::array<size_t, rank>{{i, j, k}}))));\n        }\n      }\n    }\n  } else {\n    for (size_t i = 0; i < index_dims[0]; i++) {\n      for (size_t j = 0; j < index_dims[1]; j++) {\n        for (size_t k = 0; k < index_dims[2]; k++) {\n          for (size_t l = 0; l < index_dims[3]; l++) {\n            const auto canon_multi_index =\n                Tensor_detail::canonicalize_tensor_index<symmetry>(\n                    cpp20::array<size_t, rank>{{i, j, k, l}});\n            const size_t flattened_index =\n                get_flattened_index(canon_multi_index, index_dims);\n            if (not gsl::at(index_hit, flattened_index)) {\n              num_unique_canon_indices++;\n            }\n            gsl::at(index_hit, flattened_index) = true;\n\n            CHECK(structure.get_storage_index(\n                      std::array<size_t, rank>{{i, j, k, l}}) ==\n                  structure.get_storage_index(\n                      structure.get_canonical_tensor_index(\n                          structure.get_storage_index(\n                              std::array<size_t, rank>{{i, j, k, l}}))));\n          }\n        }\n      }\n    }\n  }\n\n  CHECK(num_unique_canon_indices == num_ind_components);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Structure.Indices\",\n                  \"[DataStructures][Unit]\") {\n  const int spatial_dim1 = 3;\n  const Tensor_detail::Structure<\n      Symmetry<2, 1, 1>, SpatialIndex<spatial_dim1, UpLo::Lo, Frame::Grid>,\n      SpatialIndex<spatial_dim1, UpLo::Lo, Frame::Grid>,\n      SpatialIndex<spatial_dim1, UpLo::Lo, Frame::Grid>>\n      structure1;\n\n  constexpr size_t spatial_dim2 = 1;\n  const Tensor_detail::Structure<\n      Symmetry<1, 2, 1>, SpacetimeIndex<spatial_dim2, UpLo::Lo, Frame::Grid>,\n      SpatialIndex<spatial_dim2, UpLo::Lo, Frame::Grid>,\n      SpacetimeIndex<spatial_dim2, UpLo::Lo, Frame::Grid>>\n      structure2;\n\n  constexpr size_t spatial_dim3 = 3;\n  const Tensor_detail::Structure<\n      Symmetry<1, 1, 1>, SpatialIndex<spatial_dim3, UpLo::Up, Frame::Inertial>,\n      SpatialIndex<spatial_dim3, UpLo::Up, Frame::Inertial>,\n      SpatialIndex<spatial_dim3, UpLo::Up, Frame::Inertial>>\n      structure3;\n\n  constexpr size_t spatial_dim4 = 3;\n  const Tensor_detail::Structure<\n      Symmetry<1, 2, 2, 1>,\n      SpatialIndex<spatial_dim4, UpLo::Lo, Frame::Inertial>,\n      SpacetimeIndex<spatial_dim4, UpLo::Up, Frame::Inertial>,\n      SpacetimeIndex<spatial_dim4, UpLo::Up, Frame::Inertial>,\n      SpatialIndex<spatial_dim4, UpLo::Lo, Frame::Inertial>>\n      structure4;\n\n  check_tensor_index_canonicalization(structure1);\n  check_tensor_index_canonicalization(structure2);\n  check_tensor_index_canonicalization(structure3);\n  check_tensor_index_canonicalization(structure4);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Serialization.Tensor\",\n                  \"[DataStructures][Unit][Serialization]\") {\n  constexpr size_t dim = 4;\n  tnsr::Abb<DataVector, dim - 1, Frame::Grid> tensor(1_st);\n  // Fill the tensors\n  for (size_t i = 0; i < dim; i++) {\n    for (size_t j = 0; j < dim; j++) {\n      for (size_t k = j; k < dim; k++) {\n        tensor.get(i, j, k)[0] = (i + 1.0) * (j + 2.0) * k;\n      }\n    }\n  }\n  test_serialization(tensor);\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.out_of_bounds\",\n                  \"[DataStructures][Unit]\") {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(([]() {\n                      tnsr::Abb<double, 3, Frame::Grid> tensor(1_st);\n                      tensor[1000];\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"index >= 0 and index < narrow_cast<Size>\"));\n  CHECK_THROWS_WITH(([]() {\n                      const tnsr::Abb<double, 3, Frame::Grid> tensor(1_st);\n                      tensor[1000];\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"index >= 0 and index < narrow_cast<Size>\"));\n  CHECK_THROWS_WITH(([]() {\n                      Scalar<double> tensor(1_st);\n                      tensor.multiplicity(1000);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"index >= 0 and index < narrow_cast<Size>\"));\n  CHECK_THROWS_WITH(([]() {\n                      tnsr::I<double, 3, Frame::Grid> tensor(1_st);\n                      tensor.get_tensor_index(1000);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"index >= 0 and index < narrow_cast<Size>\"));\n#else\n  CHECK(true);\n#endif\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.GetVectorOfData\",\n                  \"[Unit][DataStructures]\") {\n  // NOTE: This test depends on the implementation of serialize and Tensor,\n  // but that is inevitable without making the test more complicated.\n  // [init_vector]\n  tnsr::I<DataVector, 3, Frame::Grid> tensor_data_vector{\n      {{{1., 2., 3.}, {4., 5., 6.}, {7., 8., 9.}}}};\n  Scalar<DataVector> scalar_data_vector{{{{1., 2., 3.}}}};\n  // [init_vector]\n  CHECK(std::make_pair(std::vector<std::string>{\"x\", \"y\", \"z\"},\n                       std::vector<DataVector>{\n                           {1., 2., 3.}, {4., 5., 6.}, {7., 8., 9.}}) ==\n        tensor_data_vector.get_vector_of_data());\n  CHECK(std::make_pair(std::vector<std::string>{\"Scalar\"},\n                       std::vector<DataVector>{{1., 2., 3.}}) ==\n        scalar_data_vector.get_vector_of_data());\n\n  tnsr::I<double, 3, Frame::Grid> tensor_double{{{1.0, 2.0, 3.0}}};\n  CHECK(std::make_pair(std::vector<std::string>{\"x\", \"y\", \"z\"},\n                       std::vector<double>{1.0, 2.0, 3.0}) ==\n        tensor_double.get_vector_of_data());\n\n  const Scalar<double> scalar{0.8};\n  CHECK(std::make_pair(std::vector<std::string>{\"Scalar\"},\n                       std::vector<double>{0.8}) ==\n        scalar.get_vector_of_data());\n}\n\n// [example_spectre_test_case]\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Frames\",\n                  \"[Unit][DataStructures]\") {\n  CHECK(\"BlockLogical\" == get_output(Frame::BlockLogical{}));\n  CHECK(\"ElementLogical\" == get_output(Frame::ElementLogical{}));\n  CHECK(\"Grid\" == get_output(Frame::Grid{}));\n  CHECK(\"Inertial\" == get_output(Frame::Inertial{}));\n  CHECK(\"Distorted\" == get_output(Frame::Distorted{}));\n  CHECK(\"NoFrame\" == get_output(Frame::NoFrame{}));\n}\n// [example_spectre_test_case]\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.MakeWithValue\",\n                  \"[Unit][DataStructures]\") {\n  const auto check_double = [](const auto& used_for_size) {\n    const auto tensor_double =\n        make_with_value<tnsr::I<double, 2>>(used_for_size, 7.0);\n    const double expected_value = 7.0;\n    CHECK(get<0>(tensor_double) == expected_value);\n    CHECK(get<1>(tensor_double) == expected_value);\n    tnsr::I<double, 2> resized{};\n    set_number_of_grid_points(make_not_null(&resized), used_for_size);\n    // Nothing to check.  Just verify compilation.\n  };\n  check_double(1.2);\n  check_double(std::complex<double>{1.2, 1.3});\n  check_double(DataVector{5});\n  check_double(ComplexDataVector{5});\n  check_double(ModalVector{5});\n  check_double(ComplexModalVector{5});\n  check_double(Tensor<double>{});\n  check_double(Tensor<std::complex<double>>{});\n  check_double(Tensor<DataVector>{5_st});\n  check_double(Tensor<ComplexDataVector>{5_st});\n  check_double(Tensor<ModalVector>{5_st});\n  check_double(Tensor<ComplexModalVector>{5_st});\n\n  const auto check_real_complex_double = [](const auto& used_for_size) {\n    const auto complex_tensor_made_with_complex_values =\n        make_with_value<tnsr::i<std::complex<double>, 3>>(used_for_size, 1.2);\n    const std::complex<double> expected_value{1.2, 0.0};\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(complex_tensor_made_with_complex_values.get(i) == expected_value);\n    }\n    tnsr::i<std::complex<double>, 3> resized{};\n    set_number_of_grid_points(make_not_null(&resized), used_for_size);\n    // Nothing to check.  Just verify compilation.\n  };\n  check_real_complex_double(1.2);\n  check_real_complex_double(std::complex<double>{1.2, 1.3});\n  check_real_complex_double(DataVector{5});\n  check_real_complex_double(ComplexDataVector{5});\n  check_real_complex_double(ModalVector{5});\n  check_real_complex_double(ComplexModalVector{5});\n  check_real_complex_double(Tensor<double>{});\n  check_real_complex_double(Tensor<std::complex<double>>{});\n  check_real_complex_double(Tensor<DataVector>{5_st});\n  check_real_complex_double(Tensor<ComplexDataVector>{5_st});\n  check_real_complex_double(Tensor<ModalVector>{5_st});\n  check_real_complex_double(Tensor<ComplexModalVector>{5_st});\n\n  const auto check_complex_complex_double =\n      [](const auto& used_for_size) {\n        const auto complex_tensor_made_with_complex_values =\n            make_with_value<tnsr::i<std::complex<double>, 3>>(\n                used_for_size, std::complex<double>{3.0, 1.0});\n        const std::complex<double> expected_value{3.0, 1.0};\n        for (size_t i = 0; i < 3; ++i) {\n          CHECK(complex_tensor_made_with_complex_values.get(i) ==\n                expected_value);\n        }\n      };\n  check_complex_complex_double(1.2);\n  check_complex_complex_double(std::complex<double>{1.2, 1.3});\n  check_complex_complex_double(DataVector{5});\n  check_complex_complex_double(ComplexDataVector{5});\n  check_complex_complex_double(ModalVector{5});\n  check_complex_complex_double(ComplexModalVector{5});\n  check_complex_complex_double(Tensor<double>{});\n  check_complex_complex_double(Tensor<std::complex<double>>{});\n  check_complex_complex_double(Tensor<DataVector>{5_st});\n  check_complex_complex_double(Tensor<ComplexDataVector>{5_st});\n  check_complex_complex_double(Tensor<ModalVector>{5_st});\n  check_complex_complex_double(Tensor<ComplexModalVector>{5_st});\n\n  const auto check_data_vector = [](const auto& used_for_size) {\n    const auto tensor =\n        make_with_value<tnsr::i<DataVector, 2>>(used_for_size, 6.0);\n    const double expected_value = 6.0;\n    for (size_t i = 0; i < 2; ++i) {\n      REQUIRE(tensor.get(i).size() == 5);\n      for (size_t j = 0; j < 5; ++j) {\n        CHECK(tensor.get(i)[j] == expected_value);\n      }\n    }\n    tnsr::i<DataVector, 2> resized{};\n    set_number_of_grid_points(make_not_null(&resized), used_for_size);\n    for (size_t i = 0; i < 2; ++i) {\n      CHECK(resized.get(i).size() == 5);\n    }\n  };\n  check_data_vector(5_st);\n  check_data_vector(DataVector{5});\n  check_data_vector(ComplexDataVector{5});\n  check_data_vector(ModalVector{5});\n  check_data_vector(ComplexModalVector{5});\n  check_data_vector(Tensor<DataVector>{5_st});\n  check_data_vector(Tensor<ComplexDataVector>{5_st});\n  check_data_vector(Tensor<ModalVector>{5_st});\n  check_data_vector(Tensor<ComplexModalVector>{5_st});\n\n  const auto check_complex_data_vector = [](const auto& used_for_size) {\n    const auto complex_tensor =\n        make_with_value<tnsr::i<ComplexDataVector, 3>>(used_for_size, 2.0);\n    const std::complex<double> expected_value{2.0, 0.0};\n    for (size_t i = 0; i < 3; ++i) {\n      REQUIRE(complex_tensor.get(i).size() == 5);\n      for (size_t j = 0; j < 5; ++j) {\n        CHECK(complex_tensor.get(i)[j] == expected_value);\n      }\n    }\n    tnsr::i<ComplexDataVector, 3> resized{};\n    set_number_of_grid_points(make_not_null(&resized), used_for_size);\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(resized.get(i).size() == 5);\n    }\n  };\n  check_complex_data_vector(5_st);\n  check_complex_data_vector(DataVector{5});\n  check_complex_data_vector(ComplexDataVector{5});\n  check_complex_data_vector(ModalVector{5});\n  check_complex_data_vector(ComplexModalVector{5});\n  check_complex_data_vector(Tensor<DataVector>{5_st});\n  check_complex_data_vector(Tensor<ComplexDataVector>{5_st});\n  check_complex_data_vector(Tensor<ModalVector>{5_st});\n  check_complex_data_vector(Tensor<ComplexModalVector>{5_st});\n\n  const auto check_modal_vector = [](const auto& structure) {\n    const auto tensor =\n        make_with_value<tnsr::i<ModalVector, 2>>(structure, 9.0);\n    const double expected_value = 9.0;\n    for (size_t i = 0; i < 2; ++i) {\n      REQUIRE(tensor.get(i).size() == 5);\n      for (size_t j = 0; j < 5; ++j) {\n        CHECK(tensor.get(i)[j] == expected_value);\n      }\n    }\n    tnsr::i<ModalVector, 2> resized{};\n    set_number_of_grid_points(make_not_null(&resized), structure);\n    for (size_t i = 0; i < 2; ++i) {\n      CHECK(resized.get(i).size() == 5);\n    }\n  };\n  check_modal_vector(5_st);\n  check_modal_vector(DataVector{5});\n  check_modal_vector(ComplexDataVector{5});\n  check_modal_vector(ModalVector{5});\n  check_modal_vector(ComplexModalVector{5});\n  check_modal_vector(Tensor<DataVector>{5_st});\n  check_modal_vector(Tensor<ComplexDataVector>{5_st});\n  check_modal_vector(Tensor<ModalVector>{5_st});\n  check_modal_vector(Tensor<ComplexModalVector>{5_st});\n\n  const auto check_complex_modal_vector = [](const auto& structure) {\n    const auto complex_tensor =\n        make_with_value<tnsr::i<ComplexModalVector, 3>>(structure, -2.0);\n    const std::complex<double> expected_value{-2.0, 0.0};\n    for (size_t i = 0; i < 3; ++i) {\n      REQUIRE(complex_tensor.get(i).size() == 5);\n      for (size_t j = 0; j < 5; ++j) {\n        CHECK(complex_tensor.get(i)[j] == expected_value);\n      }\n    }\n    tnsr::i<ComplexModalVector, 3> resized{};\n    set_number_of_grid_points(make_not_null(&resized), structure);\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(resized.get(i).size() == 5);\n    }\n  };\n  check_complex_modal_vector(5_st);\n  check_complex_modal_vector(DataVector{5});\n  check_complex_modal_vector(ComplexDataVector{5});\n  check_complex_modal_vector(ModalVector{5});\n  check_complex_modal_vector(ComplexModalVector{5});\n  check_complex_modal_vector(Tensor<DataVector>{5_st});\n  check_complex_modal_vector(Tensor<ComplexDataVector>{5_st});\n  check_complex_modal_vector(Tensor<ModalVector>{5_st});\n  check_complex_modal_vector(Tensor<ComplexModalVector>{5_st});\n\n  const auto check_spin_weighted_complex_data_vector =\n      [](const auto& used_for_size) {\n        const auto spin_weighted_complex =\n            make_with_value<Scalar<SpinWeighted<ComplexDataVector, 2>>>(\n                used_for_size, 3.0);\n        const std::complex<double> expected_value{3.0, 0.0};\n        REQUIRE(get(spin_weighted_complex).data().size() == 5);\n        for (size_t j = 0; j < 5; ++j) {\n          CHECK(get(spin_weighted_complex).data()[j] == expected_value);\n        }\n        Scalar<SpinWeighted<ComplexDataVector, 2>> resized{};\n        set_number_of_grid_points(make_not_null(&resized), used_for_size);\n        CHECK(get(resized).data().size() == 5);\n      };\n  check_spin_weighted_complex_data_vector(5_st);\n  check_spin_weighted_complex_data_vector(DataVector{5});\n  check_spin_weighted_complex_data_vector(ComplexDataVector{5});\n  check_spin_weighted_complex_data_vector(ModalVector{5});\n  check_spin_weighted_complex_data_vector(ComplexModalVector{5});\n  check_spin_weighted_complex_data_vector(Tensor<DataVector>{5_st});\n  check_spin_weighted_complex_data_vector(Tensor<ComplexDataVector>{5_st});\n  check_spin_weighted_complex_data_vector(Tensor<ModalVector>{5_st});\n  check_spin_weighted_complex_data_vector(Tensor<ComplexModalVector>{5_st});\n  check_spin_weighted_complex_data_vector(\n      Tensor<SpinWeighted<ComplexDataVector, -1>>{5_st});\n  check_spin_weighted_complex_data_vector(\n      Tensor<SpinWeighted<ComplexModalVector, 1>>{5_st});\n\n  const auto check_spin_weighted_complex_modal_vector =\n      [](const auto& structure) {\n        const auto spin_weighted_complex =\n            make_with_value<Scalar<SpinWeighted<ComplexModalVector, 2>>>(\n                structure, -4.0);\n        const std::complex<double> expected_value{-4.0, 0.0};\n        REQUIRE(get(spin_weighted_complex).data().size() == 5);\n        for (size_t j = 0; j < 5; ++j) {\n          CHECK(get(spin_weighted_complex).data()[j] == expected_value);\n        }\n        Scalar<SpinWeighted<ComplexModalVector, 2>> resized{};\n        set_number_of_grid_points(make_not_null(&resized), structure);\n        CHECK(get(resized).data().size() == 5);\n      };\n  check_spin_weighted_complex_modal_vector(5_st);\n  check_spin_weighted_complex_modal_vector(DataVector{5});\n  check_spin_weighted_complex_modal_vector(ComplexDataVector{5});\n  check_spin_weighted_complex_modal_vector(ModalVector{5});\n  check_spin_weighted_complex_modal_vector(ComplexModalVector{5});\n  check_spin_weighted_complex_modal_vector(Tensor<DataVector>{5_st});\n  check_spin_weighted_complex_modal_vector(Tensor<ComplexDataVector>{5_st});\n  check_spin_weighted_complex_modal_vector(Tensor<ModalVector>{5_st});\n  check_spin_weighted_complex_modal_vector(Tensor<ComplexModalVector>{5_st});\n  check_spin_weighted_complex_modal_vector(\n      Tensor<SpinWeighted<ComplexDataVector, -1>>{5_st});\n  check_spin_weighted_complex_modal_vector(\n      Tensor<SpinWeighted<ComplexModalVector, 1>>{5_st});\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Tensor/Test_TensorKokkos.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <Kokkos_Core_fwd.hpp>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/AtIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Kokkos/KokkosCore.hpp\"\n\nnamespace {\n\nKOKKOS_FUNCTION Scalar<double> tensor_func(const Scalar<double>& input) {\n  return Scalar<double>(2.0 * get(input));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tensor.Kokkos\",\n                  \"[Unit][DataStructures]\") {\n  {\n    INFO(\"Testing Kokkos pointwise tensor operation\");\n    const size_t num_points = 3;\n    Scalar<Kokkos::View<double*>> scalar{\"scalar\", num_points};\n    Kokkos::parallel_for(\n        \"fill\", num_points,\n        KOKKOS_LAMBDA(const int i) { get(scalar)(i) = 2.0 * i; });\n\n    // Invoke pointwise tensor operations\n    Scalar<Kokkos::View<double*>> result{\"result\", num_points};\n    Kokkos::parallel_for(\n        \"compute\", num_points, KOKKOS_LAMBDA(const int i) {\n          const Scalar<double> scalar_i = make_at_index(scalar, i);\n          const Scalar<double> result_i = tensor_func(scalar_i);\n          set_at_index(make_not_null(&result), result_i, i);\n        });\n\n    // Check result\n    Kokkos::fence();\n    const auto result_host = Kokkos::create_mirror_view(get(result));\n    Kokkos::deep_copy(result_host, get(result));\n    for (size_t i = 0; i < num_points; ++i) {\n      CHECK(result_host(i) == 4.0 * i);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ApplyMatrices.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <functional>\n#include <random>\n#include <type_traits>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\nnamespace {\nconstexpr Spectral::Basis basis = Spectral::Basis::Legendre;\nconstexpr Spectral::Quadrature quadrature = Spectral::Quadrature::Gauss;\ntemplate <size_t Dim>\nconstexpr size_t max_points = 4;\ntemplate <>\nconstexpr size_t max_points<3> = 3;\n\ntemplate <typename T>\nusing ScalarType = Scalar<T>;\n\ntemplate <typename T>\nusing VectorType = tnsr::I<T, 2>;\n\nstruct ScalarTag : db::SimpleTag {\n  using type = ScalarType<DataVector>;\n};\n\nstruct TensorTag : db::SimpleTag {\n  using type = VectorType<DataVector>;\n};\n\nstruct ComplexScalarTag : db::SimpleTag {\n  using type = ScalarType<ComplexDataVector>;\n};\n\nstruct ComplexTensorTag : db::SimpleTag {\n  using type = VectorType<ComplexDataVector>;\n};\n\ntemplate <typename LocalScalarTag, typename LocalTensorTag, size_t Dim>\nVariables<tmpl::list<LocalScalarTag, LocalTensorTag>> polynomial_data(\n    const Mesh<Dim>& mesh, const Index<Dim>& powers,\n    const typename LocalScalarTag::type::type::ElementType fill_value) {\n  const auto coords = logical_coordinates(mesh);\n  Variables<tmpl::list<LocalScalarTag, LocalTensorTag>> result(\n      mesh.number_of_grid_points(), fill_value);\n  for (size_t i = 0; i < Dim; ++i) {\n    get(get<LocalScalarTag>(result)) *= pow(coords.get(i), powers[i]);\n    get<0>(get<LocalTensorTag>(result)) *= 2.0 * pow(coords.get(i), powers[i]);\n    get<1>(get<LocalTensorTag>(result)) *= 3.0 * pow(coords.get(i), powers[i]);\n  }\n  return result;\n}\n\ntemplate <typename LocalScalarTag, typename LocalTensorTag, size_t Dim,\n          size_t FilledDim = 0>\nstruct CheckApply {\n  static void apply(\n      const Mesh<Dim>& source_mesh, const Mesh<Dim>& dest_mesh,\n      const Index<Dim>& powers,\n      std::array<Matrix, Dim> matrices = std::array<Matrix, Dim>{}) {\n    if (source_mesh.extents(FilledDim) == dest_mesh.extents(FilledDim)) {\n      // Check implicit identity\n      CheckApply<LocalScalarTag, LocalTensorTag, Dim, FilledDim + 1>::apply(\n          source_mesh, dest_mesh, powers, matrices);\n    }\n    matrices[FilledDim] = Spectral::interpolation_matrix(\n        source_mesh.slice_through(FilledDim),\n        Spectral::collocation_points(dest_mesh.slice_through(FilledDim)));\n    CheckApply<LocalScalarTag, LocalTensorTag, Dim, FilledDim + 1>::apply(\n        source_mesh, dest_mesh, powers, matrices);\n  }\n};\n\ntemplate <typename LocalScalarTag, typename LocalTensorTag, size_t Dim>\nstruct CheckApply<LocalScalarTag, LocalTensorTag, Dim, Dim> {\n  static void apply(const Mesh<Dim>& source_mesh, const Mesh<Dim>& dest_mesh,\n                    const Index<Dim>& powers,\n                    const std::array<Matrix, Dim>& matrices = {}) {\n    MAKE_GENERATOR(gen);\n    UniformCustomDistribution<\n        tt::get_fundamental_type_t<typename LocalScalarTag::type::type>>\n        dist{0.1, 5.0};\n    const auto fill_value = make_with_random_values<\n        typename LocalScalarTag::type::type::ElementType>(make_not_null(&gen),\n                                                          make_not_null(&dist));\n    const auto source_data = polynomial_data<LocalScalarTag, LocalTensorTag>(\n        source_mesh, powers, fill_value);\n    const auto result =\n        apply_matrices(matrices, source_data, source_mesh.extents());\n    const auto expected = polynomial_data<LocalScalarTag, LocalTensorTag>(\n        dest_mesh, powers, fill_value);\n    // Using this over CHECK_ITERABLE_APPROX speeds the test up by a\n    // factor of 6 or so.\n    Approx custom_approx = Approx::custom().epsilon(1.e-13).scale(1.0);\n    for (const auto p : result - expected) {\n      CHECK_ITERABLE_CUSTOM_APPROX(p, 0.0, custom_approx);\n    }\n    const auto ref_matrices =\n        make_array<std::reference_wrapper<const Matrix>, Dim>(matrices);\n    CHECK(apply_matrices(ref_matrices, source_data, source_mesh.extents()) ==\n          result);\n    const auto vector_result = apply_matrices(\n        matrices, get(get<LocalScalarTag>(source_data)), source_mesh.extents());\n    for (const auto p : vector_result - get(get<LocalScalarTag>(expected))) {\n      CHECK_ITERABLE_APPROX(p, 0.0);\n    }\n    CHECK(apply_matrices(ref_matrices, get(get<LocalScalarTag>(source_data)),\n                         source_mesh.extents()) == vector_result);\n\n    // Test multiple independent components in a \"vector\"\n    using DataType =\n        std::decay_t<decltype(get(get<LocalScalarTag>(source_data)))>;\n    const size_t num_pts = get(get<LocalScalarTag>(source_data)).size();\n    DataType t0{2 * num_pts};\n    for (size_t i = 0; i < num_pts; ++i) {\n      t0[i] = get(get<LocalScalarTag>(source_data))[i];\n      t0[i + num_pts] = 2.0 * t0[i];\n    }\n    DataType expected_t0{2 * get(get<LocalScalarTag>(expected)).size()};\n    for (size_t i = 0; i < get(get<LocalScalarTag>(expected)).size(); ++i) {\n      expected_t0[i] = get(get<LocalScalarTag>(expected))[i];\n      expected_t0[i + get(get<LocalScalarTag>(expected)).size()] =\n          2.0 * expected_t0[i];\n    }\n    CHECK_ITERABLE_APPROX(\n        apply_matrices(ref_matrices, t0, source_mesh.extents()), expected_t0);\n    CHECK_ITERABLE_APPROX(\n        apply_matrices<DataType>(ref_matrices, t0, source_mesh.extents()),\n        expected_t0);\n  }\n};\n\ntemplate <typename LocalScalarTag, typename LocalTensorTag, size_t Dim>\nvoid test_interpolation() {\n  const auto too_few_points = [](const size_t extent) {\n    return extent < Spectral::minimum_number_of_points<basis, quadrature>;\n  };\n\n  for (IndexIterator<Dim> source_extents(Index<Dim>(max_points<Dim> + 1));\n       source_extents; ++source_extents) {\n    if (std::any_of(source_extents->begin(), source_extents->end(),\n                    too_few_points)) {\n      continue;\n    }\n    if (Dim == 3 and\n        std::count(source_extents->begin(), source_extents->end(), 3) > 2) {\n      // Skip some 3-extent combinations to reduce test runtime\n      continue;\n    }\n    CAPTURE(*source_extents);\n    for (IndexIterator<Dim> dest_extents(Index<Dim>(max_points<Dim> + 1));\n         dest_extents; ++dest_extents) {\n      if (std::any_of(dest_extents->begin(), dest_extents->end(),\n                      too_few_points)) {\n        continue;\n      }\n      if (Dim == 3 and\n          std::count(dest_extents->begin(), dest_extents->end(), 3) > 1) {\n        // Skip some 3-extent combinations to reduce test runtime\n        continue;\n      }\n      CAPTURE(*dest_extents);\n      Index<Dim> max_powers;\n      for (size_t i = 0; i < Dim; ++i) {\n        max_powers[i] = std::min((*source_extents)[i], (*dest_extents)[i]);\n      }\n      for (IndexIterator<Dim> powers(max_powers); powers; ++powers) {\n        CAPTURE(*powers);\n        Mesh<Dim> source_mesh{(*source_extents).indices(), basis, quadrature};\n        Mesh<Dim> dest_mesh{(*dest_extents).indices(), basis, quadrature};\n        CheckApply<LocalScalarTag, LocalTensorTag, Dim>::apply(\n            std::move(source_mesh), std::move(dest_mesh), *powers);\n      }\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 8]]\nSPECTRE_TEST_CASE(\"Unit.DataStructures.ApplyMatrices\",\n                  \"[DataStructures][Unit]\") {\n  {\n    INFO(\"DataVector test\");\n    test_interpolation<ScalarTag, TensorTag, 1>();\n    test_interpolation<ScalarTag, TensorTag, 2>();\n    test_interpolation<ScalarTag, TensorTag, 3>();\n  }\n  {\n    INFO(\"ComplexDataVector test\");\n    test_interpolation<ComplexScalarTag, ComplexTensorTag, 1>();\n    test_interpolation<ComplexScalarTag, ComplexTensorTag, 2>();\n    test_interpolation<ComplexScalarTag, ComplexTensorTag, 3>();\n  }\n  // Can't use test_interpolation for 0 because Tensor errors on\n  // Dim=0.\n  const Index<0> extents{};\n  Variables<tmpl::list<ScalarTag, TensorTag>> data(extents.product());\n  get(get<ScalarTag>(data)) = DataVector{2.0};\n  get<0>(get<TensorTag>(data)) = DataVector{3.0};\n  get<1>(get<TensorTag>(data)) = DataVector{4.0};\n\n  Variables<tmpl::list<ComplexScalarTag, ComplexTensorTag>> complex_data(\n      extents.product());\n  get(get<ComplexScalarTag>(complex_data)) =\n      ComplexDataVector{std::complex<double>{2.0, 3.0}};\n  get<0>(get<ComplexTensorTag>(complex_data)) =\n      ComplexDataVector{std::complex<double>{3.0, 4.0}};\n  get<1>(get<ComplexTensorTag>(complex_data)) =\n      ComplexDataVector{std::complex<double>{4.0, 5.0}};\n\n  const std::array<Matrix, 0> matrices{};\n  // Can't construct the array directly because of\n  // https://bugs.llvm.org/show_bug.cgi?id=35491 .\n  // make_array contains a workaround.\n  const std::array<std::reference_wrapper<const Matrix>, 0> ref_matrices =\n      make_array<0, std::reference_wrapper<const Matrix>>(\n          std::add_const_t<Matrix>{});\n\n  CHECK(apply_matrices(matrices, data, extents) == data);\n  CHECK(apply_matrices(ref_matrices, data, extents) == data);\n\n  CHECK(apply_matrices(matrices, complex_data, extents) == complex_data);\n  CHECK(apply_matrices(ref_matrices, complex_data, extents) == complex_data);\n\n  const DataVector& data_vector = get(get<ScalarTag>(data));\n  CHECK(apply_matrices(matrices, data_vector, extents) == data_vector);\n  CHECK(apply_matrices(ref_matrices, data_vector, extents) == data_vector);\n\n  const ComplexDataVector& complex_data_vector =\n      get(get<ComplexScalarTag>(complex_data));\n  CHECK(apply_matrices(matrices, complex_data_vector, extents) ==\n        complex_data_vector);\n  CHECK(apply_matrices(ref_matrices, complex_data_vector, extents) ==\n        complex_data_vector);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_BlazeInteroperability.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/CompressedMatrix.hpp\"\n#include \"DataStructures/CompressedVector.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"DataStructures/StaticMatrix.hpp\"\n#include \"DataStructures/StaticVector.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.BlazeInteroperability\",\n                  \"[DataStructures][Unit]\") {\n  {\n    INFO(\"StaticVector\");\n    test_serialization(blaze::StaticVector<double, 4>{0., 1., 0., 2.});\n    CHECK(make_with_value<blaze::StaticVector<double, 3>>(\n              blaze::DynamicVector<double>(3), 1.) ==\n          blaze::StaticVector<double, 3>(1.));\n    CHECK(TestHelpers::test_creation<blaze::StaticVector<double, 4>>(\n              \"[0., 1., 0., 2.]\") ==\n          blaze::StaticVector<double, 4>{0., 1., 0., 2.});\n\n    blaze::StaticVector<double, 4> v{0., 1., 0., 2.};\n    set_number_of_grid_points(make_not_null(&v), 4_st);\n    CHECK(v == blaze::StaticVector<double, 4>{0., 1., 0., 2.});\n#ifdef SPECTRE_DEBUG\n    CHECK_THROWS_WITH(set_number_of_grid_points(make_not_null(&v), 3_st),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Tried to resize a StaticVector to 3\"));\n#endif  // SPECTRE_DEBUG\n  }\n  {\n    INFO(\"DynamicVector\");\n    test_serialization(blaze::DynamicVector<double>{0., 1., 0., 2.});\n    CHECK(make_with_value<blaze::DynamicVector<double>>(\n              blaze::DynamicVector<double>(3), 1.) ==\n          blaze::DynamicVector<double>(3, 1.));\n    CHECK(TestHelpers::test_creation<blaze::DynamicVector<double>>(\n              \"[0., 1., 0., 2.]\") ==\n          blaze::DynamicVector<double>{0., 1., 0., 2.});\n\n    blaze::DynamicVector<double> v{0., 1., 0., 2.};\n    set_number_of_grid_points(make_not_null(&v), 4_st);\n    CHECK(v == blaze::DynamicVector<double>{0., 1., 0., 2.});\n    set_number_of_grid_points(make_not_null(&v), 3_st);\n    CHECK(v.size() == 3);\n  }\n  {\n    INFO(\"Complex DynamicVector\");\n    using namespace std::complex_literals;\n    test_serialization(blaze::DynamicVector<std::complex<double>>{\n        0. + 1.i, 1. + 2.i, 0. - 1.i, 2. + 3.i});\n    CHECK(make_with_value<blaze::DynamicVector<std::complex<double>>>(\n              blaze::DynamicVector<std::complex<double>>(3), 1.) ==\n          blaze::DynamicVector<std::complex<double>>(3, 1.));\n    CHECK(\n        TestHelpers::test_creation<blaze::DynamicVector<std::complex<double>>>(\n            \"[[0., 1.], [1., 2.], [0., -1.], [2., 3.]]\") ==\n        blaze::DynamicVector<std::complex<double>>{0. + 1.i, 1. + 2.i, 0. - 1.i,\n                                                   2. + 3.i});\n\n    blaze::DynamicVector<std::complex<double>> v{0. + 1.i, 1. + 2.i, 0. - 1.i,\n                                                 2. + 3.i};\n    set_number_of_grid_points(make_not_null(&v), 4_st);\n    CHECK(v == blaze::DynamicVector<std::complex<double>>{0. + 1.i, 1. + 2.i,\n                                                          0. - 1.i, 2. + 3.i});\n    set_number_of_grid_points(make_not_null(&v), 3_st);\n    CHECK(v.size() == 3);\n  }\n  {\n    INFO(\"CompressedVector\");\n    test_serialization(blaze::CompressedVector<double>{0., 1., 0., 2.});\n    CHECK(serialize_and_deserialize(\n              blaze::CompressedVector<double>{0., 1., 0., 2.})\n              .nonZeros() == 2);\n    CHECK(TestHelpers::test_creation<blaze::CompressedVector<double>>(\n              \"[0., 1., 0., 2.]\") ==\n          blaze::CompressedVector<double>{0., 1., 0., 2.});\n    CHECK(TestHelpers::test_creation<blaze::CompressedVector<double>>(\n              \"[0., 1., 0., 2.]\")\n              .nonZeros() == 2);\n  }\n  {\n    INFO(\"StaticMatrix\");\n    test_serialization(blaze::StaticMatrix<double, 2, 3, blaze::columnMajor>{\n        {0., 1., 2.}, {3., 0., 4.}});\n    test_serialization(blaze::StaticMatrix<double, 2, 3, blaze::rowMajor>{\n        {0., 1., 2.}, {3., 0., 4.}});\n    CHECK(TestHelpers::test_creation<\n              blaze::StaticMatrix<double, 2, 3, blaze::columnMajor>>(\n              \"[[0., 1., 2.], [3., 0., 4.]]\") ==\n          blaze::StaticMatrix<double, 2, 3, blaze::columnMajor>{{0., 1., 2.},\n                                                                {3., 0., 4.}});\n    CHECK(TestHelpers::test_creation<\n              blaze::StaticMatrix<double, 2, 3, blaze::rowMajor>>(\n              \"[[0., 1., 2.], [3., 0., 4.]]\") ==\n          blaze::StaticMatrix<double, 2, 3, blaze::rowMajor>{{0., 1., 2.},\n                                                             {3., 0., 4.}});\n  }\n  {\n    INFO(\"DynamicMatrix\");\n    test_serialization(blaze::DynamicMatrix<double, blaze::columnMajor>{\n        {0., 1., 2.}, {3., 0., 4.}});\n    test_serialization(blaze::DynamicMatrix<double, blaze::rowMajor>{\n        {0., 1., 2.}, {3., 0., 4.}});\n    CHECK(TestHelpers::test_creation<\n              blaze::DynamicMatrix<double, blaze::columnMajor>>(\n              \"[[0., 1., 2.], [3., 0., 4.]]\") ==\n          blaze::DynamicMatrix<double, blaze::columnMajor>{{0., 1., 2.},\n                                                           {3., 0., 4.}});\n    CHECK(TestHelpers::test_creation<\n              blaze::DynamicMatrix<double, blaze::rowMajor>>(\n              \"[[0., 1., 2.], [3., 0., 4.]]\") ==\n          blaze::DynamicMatrix<double, blaze::rowMajor>{{0., 1., 2.},\n                                                        {3., 0., 4.}});\n    // Test `write_csv`\n    std::stringstream ss{};\n    blaze::DynamicMatrix<double, blaze::columnMajor> matrix{{0., 1., 2.},\n                                                            {3., 0., 4.}};\n    write_csv(ss, matrix);\n    CHECK(ss.str() == \"0,1,2\\n3,0,4\\n\");\n  }\n  {\n    INFO(\"Complex DynamicMatrix\");\n    using namespace std::complex_literals;\n    test_serialization(\n        blaze::DynamicMatrix<std::complex<double>, blaze::columnMajor>{\n            {0. + 1.i, 1. + 3.i, 2. + 2.i}, {3. + 4.i, 0. - 1.i, 4. + 5.i}});\n    test_serialization(\n        blaze::DynamicMatrix<std::complex<double>, blaze::rowMajor>{\n            {0. + 1.i, 1. + 3.i, 2. + 2.i}, {3. + 4.i, 0. - 1.i, 4. + 5.i}});\n    CHECK(TestHelpers::test_creation<\n              blaze::DynamicMatrix<std::complex<double>, blaze::columnMajor>>(\n              \"[[[0., 1.], [1., 3.], [2., 2.]], [[3., 4.], [0., -1.], [4., \"\n              \"5.]]]\") ==\n          blaze::DynamicMatrix<std::complex<double>, blaze::columnMajor>{\n              {0. + 1.i, 1. + 3.i, 2. + 2.i}, {3. + 4.i, 0. - 1.i, 4. + 5.i}});\n    CHECK(TestHelpers::test_creation<\n              blaze::DynamicMatrix<std::complex<double>, blaze::rowMajor>>(\n              \"[[[0., 1.], [1., 3.], [2., 2]], [[3., 4.], [0., -1.], [4., \"\n              \"5.]]]\") ==\n          blaze::DynamicMatrix<std::complex<double>, blaze::rowMajor>{\n              {0. + 1.i, 1. + 3.i, 2. + 2.i}, {3. + 4.i, 0. - 1.i, 4. + 5.i}});\n    // Test `write_csv`\n    std::stringstream ss{};\n    blaze::DynamicMatrix<std::complex<double>, blaze::columnMajor> matrix{\n        {{0. + 1.i, 1. + 3.i, 2. + 2.i}, {3. + 4.i, 0. - 1.i, 4. + 5.i}}};\n    write_csv(ss, matrix);\n    CHECK(ss.str() == \"(0,1),(1,3),(2,2)\\n(3,4),(0,-1),(4,5)\\n\");\n  }\n  {\n    INFO(\"CompressedMatrix\");\n    test_serialization(blaze::CompressedMatrix<double, blaze::columnMajor>{\n        {0., 1., 2.}, {3., 0., 4.}});\n    CHECK(serialize_and_deserialize(\n              blaze::CompressedMatrix<double, blaze::columnMajor>{{0., 1., 2.},\n                                                                  {3., 0., 4.}})\n              .nonZeros() == 4);\n    test_serialization(blaze::CompressedMatrix<double, blaze::rowMajor>{\n        {0., 1., 2.}, {3., 0., 4.}});\n    CHECK(serialize_and_deserialize(\n              blaze::CompressedMatrix<double, blaze::rowMajor>{{0., 1., 2.},\n                                                               {3., 0., 4.}})\n              .nonZeros() == 4);\n    CHECK(TestHelpers::test_creation<\n              blaze::CompressedMatrix<double, blaze::columnMajor>>(\n              \"[[0., 1., 2.], [3., 0., 4.]]\") ==\n          blaze::CompressedMatrix<double, blaze::columnMajor>{{0., 1., 2.},\n                                                              {3., 0., 4.}});\n    CHECK(TestHelpers::test_creation<\n              blaze::CompressedMatrix<double, blaze::columnMajor>>(\n              \"[[0., 1., 2.], [3., 0., 4.]]\")\n              .nonZeros() == 4);\n    CHECK(TestHelpers::test_creation<\n              blaze::CompressedMatrix<double, blaze::rowMajor>>(\n              \"[[0., 1., 2.], [3., 0., 4.]]\") ==\n          blaze::CompressedMatrix<double, blaze::rowMajor>{{0., 1., 2.},\n                                                           {3., 0., 4.}});\n    CHECK(TestHelpers::test_creation<\n              blaze::CompressedMatrix<double, blaze::rowMajor>>(\n              \"[[0., 1., 2.], [3., 0., 4.]]\")\n              .nonZeros() == 4);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_CachedTempBuffer.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/CachedTempBuffer.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nnamespace Tags {\ntemplate <typename DataType>\nstruct Scalar1 : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType>\nstruct Scalar2 : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType>\nstruct Vector1 : db::SimpleTag {\n  using type = tnsr::I<DataType, 2>;\n};\n\ntemplate <typename DataType>\nstruct Vector2 : db::SimpleTag {\n  using type = tnsr::I<DataType, 2>;\n};\n\ntemplate <typename Tag>\nstruct Count {\n  using type = size_t;\n};\n}  // namespace Tags\n\n// [alias]\ntemplate <typename DataType>\nusing Cache =\n    CachedTempBuffer<Tags::Scalar1<DataType>, Tags::Scalar2<DataType>,\n                     Tags::Vector1<DataType>, Tags::Vector2<DataType>>;\n// [alias]\n\ntemplate <typename DataType>\nusing Counter = tuples::TaggedTuple<\n    Tags::Count<Tags::Scalar1<DataType>>, Tags::Count<Tags::Scalar2<DataType>>,\n    Tags::Count<Tags::Vector1<DataType>>, Tags::Count<Tags::Vector2<DataType>>>;\n\ntemplate <typename DataType>\nclass Computer {\n public:\n  explicit Computer(const gsl::not_null<Counter<DataType>*> counter)\n      : counter_(counter) {}\n\n  void operator()(const gsl::not_null<Scalar<DataType>*> scalar1,\n                  const gsl::not_null<Cache<DataType>*> /*cache*/,\n                  Tags::Scalar1<DataType> /*meta*/) const {\n    ++get<Tags::Count<Tags::Scalar1<DataType>>>(*counter_);\n    get(*scalar1) = 7.0;\n  }\n\n  // [compute_func]\n  void operator()(const gsl::not_null<Scalar<DataType>*> scalar2,\n                  const gsl::not_null<Cache<DataType>*> cache,\n                  Tags::Scalar2<DataType> /*meta*/) const {\n    // [compute_func]\n    ++get<Tags::Count<Tags::Scalar2<DataType>>>(*counter_);\n    const auto& vector1 = cache->get_var(*this, Tags::Vector1<DataType>{});\n\n    get(*scalar2) = get<1>(vector1);\n  }\n\n  void operator()(const gsl::not_null<tnsr::I<DataType, 2>*> vector1,\n                  const gsl::not_null<Cache<DataType>*> /*cache*/,\n                  Tags::Vector1<DataType> /*meta*/) const {\n    ++get<Tags::Count<Tags::Vector1<DataType>>>(*counter_);\n    get<0>(*vector1) = 10.0;\n    get<1>(*vector1) = 11.0;\n  }\n\n  void operator()(const gsl::not_null<tnsr::I<DataType, 2>*> vector2,\n                  const gsl::not_null<Cache<DataType>*> cache,\n                  Tags::Vector2<DataType> /*meta*/) const {\n    ++get<Tags::Count<Tags::Vector2<DataType>>>(*counter_);\n    const auto& scalar2 = cache->get_var(*this, Tags::Scalar2<DataType>{});\n    const auto& vector1 = cache->get_var(*this, Tags::Vector1<DataType>{});\n\n    get<0>(*vector2) = get<0>(vector1) * get(scalar2);\n    get<1>(*vector2) = get<1>(vector1) * get(scalar2);\n  }\n\n private:\n  gsl::not_null<Counter<DataType>*> counter_;\n};\n\ntemplate <typename DataType>\nvoid test_cached_temp_buffer(const DataType& used_for_size) {\n  Counter<DataType> counter{};\n  get<Tags::Count<Tags::Scalar1<DataType>>>(counter) = 0;\n  get<Tags::Count<Tags::Scalar2<DataType>>>(counter) = 0;\n  get<Tags::Count<Tags::Vector1<DataType>>>(counter) = 0;\n  get<Tags::Count<Tags::Vector2<DataType>>>(counter) = 0;\n\n  const auto check_counts = [&counter](\n                                const size_t scalar1, const size_t scalar2,\n                                const size_t vector1, const size_t vector2) {\n    CHECK(get<Tags::Count<Tags::Scalar1<DataType>>>(counter) == scalar1);\n    CHECK(get<Tags::Count<Tags::Scalar2<DataType>>>(counter) == scalar2);\n    CHECK(get<Tags::Count<Tags::Vector1<DataType>>>(counter) == vector1);\n    CHECK(get<Tags::Count<Tags::Vector2<DataType>>>(counter) == vector2);\n  };\n\n  Cache<DataType> cache(get_size(used_for_size));\n  Computer<DataType> computer(&counter);\n  check_counts(0, 0, 0, 0);\n  CHECK(get(cache.get_var(computer, Tags::Scalar1<DataType>{})) == 7.0);\n  check_counts(1, 0, 0, 0);\n  CHECK(get<0>(cache.get_var(computer, Tags::Vector2<DataType>{})) == 110.0);\n  check_counts(1, 1, 1, 1);\n  CHECK(get<1>(cache.get_var(computer, Tags::Vector2<DataType>{})) == 121.0);\n  check_counts(1, 1, 1, 1);\n  CHECK(get<0>(cache.get_var(computer, Tags::Vector1<DataType>{})) == 10.0);\n  check_counts(1, 1, 1, 1);\n\n  CHECK(get_size(get(cache.get_var(computer, Tags::Scalar1<DataType>{}))) ==\n        get_size(used_for_size));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.CachedTempBuffer\",\n                  \"[DataStructures][Unit]\") {\n  test_cached_temp_buffer(std::numeric_limits<double>::signaling_NaN());\n  test_cached_temp_buffer(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ComplexDataVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <tuple>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_complex_data_vector_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n  const TestHelpers::VectorImpl::Bound mone_one{{-1.0, 1.0}};\n  const TestHelpers::VectorImpl::Bound gt_one{{1.0, 100.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.01, 100.0}};\n  const auto unary_ops = std::make_tuple(\n      std::make_tuple(funcl::Abs<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Acos<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Acosh<>{}, std::make_tuple(gt_one)),\n      std::make_tuple(funcl::Asin<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Asinh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Atan<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Atanh<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Conj<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Cos<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Cosh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Exp<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Imag<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::InvSqrt<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Log<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Real<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Sin<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Sinh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Square<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Sqrt<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Tan<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Tanh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<1>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<-2>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<3>{}, std::make_tuple(generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ComplexDataVector>(unary_ops);\n\n  // [test_vector_math_usage]\n  const auto real_unary_ops = std::make_tuple(\n      std::make_tuple(funcl::Real<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Imag<>{}, std::make_tuple(generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, DataVector>(real_unary_ops);\n  // [test_vector_math_usage]\n\n  // Note that the binary operations have been moved to\n  // `Test_ComplexDataVectorBinaryOperations.cpp` in an effort to better\n  // parallelize the build.\n}\n\nvoid test_norms() {\n  INFO(\"Test norms\");\n  // Test l1Norm and l2Norm:\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> dist{-5, 10};\n  ComplexDataVector vector(30);\n  fill_with_random_values(make_not_null(&vector), make_not_null(&gen),\n                          make_not_null(&dist));\n  double l1norm = 0.0;\n  double l2norm = 0.0;\n  for (const std::complex<double> value : vector) {\n    l1norm += std::abs(value);\n    l2norm += square(std::abs(value));\n  }\n  l2norm = std::sqrt(l2norm);\n  CHECK(blaze::real(l1Norm(vector)) == approx(l1norm));\n  CHECK(blaze::real(l2Norm(vector)) == approx(l2norm));\n  CHECK(blaze::imag(l1Norm(vector)) == approx(0.0));\n  CHECK(blaze::imag(l2Norm(vector)) == approx(0.0));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.ComplexDataVector\",\n                  \"[DataStructures][Unit]\") {\n  {\n    INFO(\"test construct and assign\");\n    TestHelpers::VectorImpl::vector_test_construct_and_assign<\n        ComplexDataVector, std::complex<double>>();\n  }\n  {\n    INFO(\"test construct and assign from doubles\");\n    TestHelpers::VectorImpl::vector_test_construct_and_assign<ComplexDataVector,\n                                                              double>();\n  }\n  {\n    INFO(\"test serialize and deserialize\");\n    TestHelpers::VectorImpl::vector_test_serialize<ComplexDataVector,\n                                                   std::complex<double>>();\n  }\n  {\n    INFO(\"test serialize and deserialize from doubles\");\n    TestHelpers::VectorImpl::vector_test_serialize<ComplexDataVector, double>();\n  }\n  {\n    INFO(\"test set_data_ref functionality\");\n    TestHelpers::VectorImpl::vector_test_ref<ComplexDataVector,\n                                             std::complex<double>>();\n  }\n  {\n    INFO(\"test math after move\");\n    TestHelpers::VectorImpl::vector_test_math_after_move<\n        ComplexDataVector, std::complex<double>>();\n  }\n  {\n    INFO(\"test math after move using doubles to initialize\");\n    TestHelpers::VectorImpl::vector_test_math_after_move<ComplexDataVector,\n                                                         double>();\n  }\n  {\n    INFO(\"test ComplexDataVector math operations\");\n    test_complex_data_vector_math();\n  }\n  test_norms();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ComplexDataVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::ExpressionAssign),\n      Catch::Matchers::ContainsSubstring(\"Must assign into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ComplexDataVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Copy),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ComplexDataVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Move),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ComplexDataVectorBinaryOperations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is separated from `Test_ComplexDataVector.cpp` in an effort to\n// parallelize the test builds.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <tuple>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_complex_data_vector_multiple_operand_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-10.0, 10.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.1, 10.0}};\n\n  const auto binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Divides<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Multiplies<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ComplexDataVector,\n      ComplexDataVector>(binary_ops);\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, DataVector, ComplexDataVector>(\n      binary_ops);\n\n  const auto inplace_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::DivAssign<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::MinusAssign<>{},\n                      std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::MultAssign<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::PlusAssign<>{},\n                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Inplace, ComplexDataVector,\n      ComplexDataVector>(inplace_binary_ops);\n\n  const auto cascaded_ops = std::make_tuple(\n      std::make_tuple(funcl::Multiplies<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)),\n      std::make_tuple(funcl::Multiplies<funcl::Exp<>, funcl::Sin<>>{},\n                      std::make_tuple(generic, generic, generic)),\n      std::make_tuple(\n          funcl::Plus<funcl::Atan<funcl::Tan<>>, funcl::Divides<>>{},\n          std::make_tuple(generic, generic, positive)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, ComplexDataVector,\n      ComplexDataVector, ComplexDataVector>(cascaded_ops);\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, ComplexDataVector, DataVector,\n      ComplexDataVector>(cascaded_ops);\n\n  const auto array_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict,\n      std::array<ComplexDataVector, 2>, std::array<ComplexDataVector, 2>>(\n      array_binary_ops);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.ComplexDataVector.MultipleOperands\",\n                  \"[DataStructures][Unit]\") {\n    test_complex_data_vector_multiple_operand_math();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ComplexDiagonalModalOperator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <tuple>\n\n#include \"DataStructures/ComplexDiagonalModalOperator.hpp\"\n#include \"DataStructures/DiagonalModalOperator.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_complex_diagonal_modal_operator_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.01, 100.0}};\n\n  const auto unary_ops = std::make_tuple(\n      std::make_tuple(funcl::Conj<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Imag<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Real<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Sqrt<>{}, std::make_tuple(generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ComplexDiagonalModalOperator>(\n      unary_ops);\n\n  const auto binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Divides<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Multiplies<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ComplexDiagonalModalOperator>(\n      binary_ops);\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ComplexDiagonalModalOperator,\n      DiagonalModalOperator>(binary_ops);\n\n  const auto inplace_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::DivAssign<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::MinusAssign<>{},\n                      std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::MultAssign<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::PlusAssign<>{},\n                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Inplace, ComplexDiagonalModalOperator,\n      DiagonalModalOperator>(inplace_binary_ops);\n\n  // Note that a collection of additional operations that involve acting on\n  // complex modal vectors with complex diagonal modal operators have been moved\n  // to `Test_MoreComplexDiagonalModalOperatorMath.cpp` in an effort to better\n  // parallelize the build.\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.ComplexDiagonalModalOperator\",\n                  \"[DataStructures][Unit]\") {\n  {\n    INFO(\"test construct and assign\");\n    TestHelpers::VectorImpl::vector_test_construct_and_assign<\n        ComplexDiagonalModalOperator, std::complex<double>>();\n  }\n  {\n    INFO(\"test serialize and deserialize\");\n    TestHelpers::VectorImpl::vector_test_serialize<ComplexDiagonalModalOperator,\n                                                   std::complex<double>>();\n  }\n  {\n    INFO(\"test set_data_ref functionality\");\n    TestHelpers::VectorImpl::vector_test_ref<ComplexDiagonalModalOperator,\n                                             std::complex<double>>();\n  }\n  {\n    INFO(\"test math after move\");\n    TestHelpers::VectorImpl::vector_test_math_after_move<\n        ComplexDiagonalModalOperator, std::complex<double>>();\n  }\n  {\n    INFO(\"test ComplexDiagonalModalOperator math operations\");\n    test_complex_diagonal_modal_operator_math();\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<\n          ComplexDiagonalModalOperator>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::ExpressionAssign),\n      Catch::Matchers::ContainsSubstring(\"Must assign into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<\n          ComplexDiagonalModalOperator>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Copy),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<\n          ComplexDiagonalModalOperator>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Move),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ComplexModalVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <tuple>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_complex_modal_vector_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n\n  const auto unary_ops = std::make_tuple(\n      std::make_tuple(funcl::Conj<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Imag<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Real<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Abs<>{}, std::make_tuple(generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ComplexModalVector>(unary_ops);\n\n  const auto real_unary_ops = std::make_tuple(\n      std::make_tuple(funcl::Imag<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Real<>{}, std::make_tuple(generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ModalVector>(real_unary_ops);\n\n  const auto binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ComplexModalVector,\n      ModalVector>(binary_ops);\n\n  const auto cascaded_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, ComplexModalVector,\n      ModalVector>(cascaded_ops);\n\n  const auto array_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict,\n      std::array<ComplexModalVector, 2>>(array_binary_ops);\n\n  // Note that the binary operations that involve a complex modal vector and\n  // various scalar types have been moved to\n  // `Test_ComplexModalVectorInhomogeneousOperations.cpp` in an effort to better\n  // parallelize the build.\n}\n\nvoid test_norms() {\n  INFO(\"Test norms\");\n  // Test l1Norm and l2Norm:\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> dist{-5, 10};\n  ComplexModalVector vector(30);\n  fill_with_random_values(make_not_null(&vector), make_not_null(&gen),\n                          make_not_null(&dist));\n  double l1norm = 0.0;\n  double l2norm = 0.0;\n  for (const std::complex<double> value : vector) {\n    l1norm += std::abs(value);\n    l2norm += square(std::abs(value));\n  }\n  l2norm = std::sqrt(l2norm);\n  CHECK(blaze::real(l1Norm(vector)) == approx(l1norm));\n  CHECK(blaze::real(l2Norm(vector)) == approx(l2norm));\n  CHECK(blaze::imag(l1Norm(vector)) == approx(0.0));\n  CHECK(blaze::imag(l2Norm(vector)) == approx(0.0));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.ComplexModalVector\",\n                  \"[DataStructures][Unit]\") {\n  {\n    INFO(\"test construct and assign\");\n    TestHelpers::VectorImpl::vector_test_construct_and_assign<\n        ComplexModalVector, std::complex<double>>();\n  }\n  {\n    INFO(\"test serialize and deserialize\");\n    TestHelpers::VectorImpl::vector_test_serialize<ComplexModalVector,\n                                                   std::complex<double>>();\n  }\n  {\n    INFO(\"test set_data_ref functionality\");\n    TestHelpers::VectorImpl::vector_test_ref<ComplexModalVector,\n                                             std::complex<double>>();\n  }\n  {\n    INFO(\"test math after move\");\n    TestHelpers::VectorImpl::vector_test_math_after_move<\n        ComplexModalVector, std::complex<double>>();\n  }\n  {\n    INFO(\"test ComplexModalVector math operations\");\n    test_complex_modal_vector_math();\n  }\n  test_norms();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ComplexModalVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::ExpressionAssign),\n      Catch::Matchers::ContainsSubstring(\"Must assign into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ComplexModalVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Copy),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ComplexModalVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Move),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ComplexModalVectorInhomogeneousOperations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file has been separated from `Test_ComplexModalVector.cpp` in an effort\n// to parallelize the test builds.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <tuple>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_complex_modal_vector_inhomogeneous_binary_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-10.0, 10.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.1, 10.0}};\n\n  const auto just_element_with_modal_vector_ops =\n      std::make_tuple(std::make_tuple(funcl::Multiplies<>{},\n                                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly, double,\n      ComplexModalVector>(just_element_with_modal_vector_ops);\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      std::complex<double>, ComplexModalVector>(\n      just_element_with_modal_vector_ops);\n\n  const auto just_modal_vector_with_element_ops = std::make_tuple(\n      std::make_tuple(funcl::Multiplies<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Divides<>{}, std::make_tuple(generic, positive)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      ComplexModalVector, double>(just_modal_vector_with_element_ops);\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      ComplexModalVector, std::complex<double>>(\n      just_modal_vector_with_element_ops);\n\n  const auto just_modal_vector_with_modal_vector_ops =\n      std::make_tuple(std::make_tuple(funcl::MinusAssign<>{},\n                                      std::make_tuple(generic, generic)),\n                      std::make_tuple(funcl::PlusAssign<>{},\n                                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      ComplexModalVector, ComplexModalVector>(\n      just_modal_vector_with_modal_vector_ops);\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      ComplexModalVector, ModalVector>(just_modal_vector_with_modal_vector_ops);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.DataStructures.ComplexModalVector.InhomogeneousOperations\",\n    \"[DataStructures][Unit]\") {\n  test_complex_modal_vector_inhomogeneous_binary_math();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_DataVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <tuple>\n\n#include \"DataStructures/Blaze/IntegerPow.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_data_vector_unary_math() {\n  // [test_functions_with_vector_arguments_example]\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n  const TestHelpers::VectorImpl::Bound mone_one{{-1.0, 1.0}};\n  const TestHelpers::VectorImpl::Bound gt_one{{1.0, 100.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.01, 100.0}};\n  const auto unary_ops = std::make_tuple(\n      std::make_tuple(funcl::Abs<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Acos<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Acosh<>{}, std::make_tuple(gt_one)),\n      std::make_tuple(funcl::Asin<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Asinh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Atan<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Atanh<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Cbrt<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Cos<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Cosh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Erf<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Exp<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Exp2<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Fabs<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::InvCbrt<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::InvSqrt<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Log<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Log10<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Log2<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Sin<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Sinh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::StepFunction<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Square<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Sqrt<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Tan<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Tanh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<1>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<-2>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<3>{}, std::make_tuple(generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, DataVector>(unary_ops);\n  // [test_functions_with_vector_arguments_example]\n\n  // Note that the binary operations have been moved to\n  // `Test_DataVectorBinaryOperations.cpp` in an effort to better parallelize\n  // the build.\n}\n\nvoid test_norms() {\n  // Test l1Norm and l2Norm:\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> dist{-5, 10};\n  DataVector vector(30);\n  fill_with_random_values(make_not_null(&vector), make_not_null(&gen),\n                          make_not_null(&dist));\n  double l1norm = 0.0;\n  double l2norm = 0.0;\n  for (const double value : vector) {\n    l1norm += std::abs(value);\n    l2norm += square(value);\n  }\n  l2norm = std::sqrt(l2norm);\n  // Since l1Norm(vector) and l2Norm(vector) use SIMD we shouldn't expect the\n  // results to be bitwise identical.\n  CHECK(l1norm == approx(l1Norm(vector)));\n  CHECK(l2norm == approx(l2Norm(vector)));\n}\n\nvoid test_integer_pow() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> dist{-5, 10};\n  DataVector vector(30);\n  fill_with_random_values(make_not_null(&vector), make_not_null(&gen),\n                          make_not_null(&dist));\n  for (size_t e = 0; e < 15; ++e) {\n    const DataVector int_pow = integer_pow(vector, e);\n    const DataVector double_pow = pow(vector, static_cast<double>(e));\n    CHECK_ITERABLE_APPROX(int_pow, double_pow);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataVector\", \"[DataStructures][Unit]\") {\n  {\n    INFO(\"test construct and assign\");\n    TestHelpers::VectorImpl::vector_test_construct_and_assign<DataVector,\n                                                              double>();\n  }\n  {\n    INFO(\"test serialize and deserialize\");\n    TestHelpers::VectorImpl::vector_test_serialize<DataVector, double>();\n  }\n  {\n    INFO(\"test set_data_ref functionality\");\n    TestHelpers::VectorImpl::vector_test_ref<DataVector, double>();\n  }\n  {\n    INFO(\"test math after move\");\n    TestHelpers::VectorImpl::vector_test_math_after_move<DataVector, double>();\n  }\n  {\n    INFO(\"test DataVector math operations\");\n    test_data_vector_unary_math();\n  }\n  {\n    INFO(\"test norms of DataVectors\");\n    test_norms();\n  }\n  {\n    INFO(\"test integer power of DataVectors\");\n    test_integer_pow();\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<DataVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::ExpressionAssign),\n      Catch::Matchers::ContainsSubstring(\"Must assign into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<DataVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Copy),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<DataVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Move),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_DataVector.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport math\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\n\n\nclass TestDataVector(unittest.TestCase):\n    def test_len(self):\n        a = DataVector(5, 6.7)\n        self.assertEqual(len(a), 5)\n\n    def test_math_double(self):\n        a = DataVector(5, 6.7)\n        self.assertEqual(a + 2.3, DataVector(5, 9.0))\n        self.assertEqual(2.3 + a, DataVector(5, 9.0))\n        self.assertEqual(a - 2.3, DataVector(5, 4.4))\n        self.assertEqual(2.3 - a, DataVector(5, -4.4))\n        self.assertEqual(2.0 * a, DataVector(5, 2.0 * 6.7))\n        self.assertEqual(a * 2.0, DataVector(5, 2.0 * 6.7))\n        self.assertEqual(a / 2.0, DataVector(5, 6.7 / 2.0))\n        self.assertEqual(2.0 / a, DataVector(5, 2.0 / 6.7))\n        self.assertEqual(a**2.0, DataVector(5, 6.7**2.0))\n\n        b = DataVector([7.2, -3.9])\n        self.assertEqual((b + 2.3)[0], 9.5)\n        self.assertEqual((b + 2.3)[1], -1.6)\n        self.assertEqual((2.3 + b)[0], 9.5)\n        self.assertEqual((2.3 + b)[1], -1.6)\n\n        self.assertEqual((b - 2.3)[0], 4.9)\n        self.assertAlmostEqual((b - 2.3)[1], -6.2)\n        self.assertEqual((2.3 - b)[0], -4.9)\n        self.assertAlmostEqual((2.3 - b)[1], 6.2)\n\n        self.assertEqual((2.0 * b)[0], 14.4)\n        self.assertEqual((2.0 * b)[1], -7.8)\n        self.assertEqual((b * 2.0)[0], 14.4)\n        self.assertEqual((b * 2.0)[1], -7.8)\n\n        self.assertEqual((2.0 / b)[0], 0.2777777777777778)\n        self.assertEqual((2.0 / b)[1], -0.5128205128205129)\n        self.assertEqual((b / 2.0)[0], 3.6)\n        self.assertEqual((b / 2.0)[1], -1.95)\n\n    def test_math_datavector(self):\n        a = DataVector(5, 6.7)\n        b = DataVector(5, 8.7)\n        self.assertEqual(a + b, DataVector(5, 6.7 + 8.7))\n        self.assertEqual(a - b, DataVector(5, 6.7 - 8.7))\n        self.assertEqual(a * b, DataVector(5, 6.7 * 8.7))\n        self.assertEqual(a / b, DataVector(5, 6.7 / 8.7))\n\n        c = DataVector([1.5, 2.5])\n        d = DataVector([3.0, 7.5])\n        self.assertEqual((c + d)[0], 4.5)\n        self.assertEqual((c + d)[1], 10.0)\n        self.assertEqual((c - d)[0], -1.5)\n        self.assertEqual((c - d)[1], -5.0)\n        self.assertEqual((c * d)[0], 4.5)\n        self.assertEqual((c * d)[1], 18.75)\n        self.assertEqual((c / d)[0], 0.5)\n        self.assertEqual((c / d)[1], 1.0 / 3.0)\n\n    def test_negate(self):\n        a = DataVector(5, 6.7)\n        self.assertEqual(-a, DataVector(5, -6.7))\n        b = DataVector([1.5, 3.7])\n        self.assertEqual((-b)[0], -1.5)\n        self.assertEqual((-b)[1], -3.7)\n\n    def test_bounds_check(self):\n        a = DataVector(5, 6.7)\n        self.assertRaises(RuntimeError, lambda: a[5])\n\n        def assignment_test():\n            a[5] = 8\n\n        self.assertRaises(RuntimeError, assignment_test)\n\n    def test_output(self):\n        a = DataVector(5, 6.7)\n        self.assertEqual(str(a), \"(6.7,6.7,6.7,6.7,6.7)\")\n        b = DataVector([3.8, 7.2])\n        self.assertEqual(str(b), \"(3.8,7.2)\")\n\n    def test_abs(self):\n        a = DataVector(5, -6.0)\n        self.assertEqual(a.abs(), DataVector(5, 6.0))\n        b = DataVector([1.0, -2.0])\n        self.assertEqual(b.abs()[0], 1.0)\n        self.assertEqual(b.abs()[1], 2.0)\n\n    def test_acos(self):\n        a = DataVector(5, 0.67)\n        self.assertEqual(a.acos()[0], math.acos(0.67))\n        b = DataVector([1.0, -0.5])\n        self.assertEqual(b.acos()[0], 0)\n        self.assertEqual(b.acos()[1], math.acos(-0.5))\n\n    def test_acosh(self):\n        a = DataVector(5, 1.0)\n        self.assertEqual(a.acosh(), DataVector(5, math.acosh(1.0)))\n        b = DataVector([1.0, 2.0])\n        self.assertEqual(b.acosh()[0], 0.0)\n        self.assertEqual(b.acosh()[1], math.acosh(2.0))\n\n    def test_asin(self):\n        a = DataVector(5, 0.67)\n        self.assertEqual(a.asin(), DataVector(5, math.asin(0.67)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.asin()[0], 0.0)\n        self.assertEqual(b.asin()[1], math.asin(-0.5))\n\n    def test_asinh(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.sinh(), DataVector(5, math.sinh(4.0)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.asinh()[0], 0.0)\n        self.assertEqual(b.asinh()[1], math.asinh(-0.5))\n\n    def test_atan(self):\n        a = DataVector(5, 0.67)\n        self.assertEqual(a.atan(), DataVector(5, math.atan(0.67)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.atan()[0], 0.0)\n        self.assertEqual(b.atan()[1], math.atan(-0.5))\n\n    def test_atan2(self):\n        a = DataVector(5, 0.67)\n        b = DataVector(5, -4.0)\n        self.assertEqual(a.atan2(b), DataVector(5, math.atan2(0.67, -4.0)))\n        x = DataVector([3, 100])\n        y = DataVector([1, -3])\n        z = DataVector([math.atan2(3, 1), math.atan2(100, -3)])\n        self.assertEqual(x.atan2(y), z)\n\n    def test_atanh(self):\n        a = DataVector(5, 0.67)\n        self.assertEqual(a.atanh(), DataVector(5, math.atanh(0.67)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.atanh()[0], 0.0)\n        self.assertEqual(b.atanh()[1], math.atanh(-0.5))\n\n    def test_cbrt(self):\n        a = DataVector(5, 8.0)\n        self.assertEqual(a.cbrt(), DataVector(5, 2.0))\n        b = DataVector([1.0, 64.0])\n        self.assertEqual(b.cbrt()[0], 1.0)\n        self.assertEqual(b.cbrt()[1], 4.0)\n\n    def test_cos(self):\n        a = DataVector(5, 0.67)\n        self.assertEqual(a.cos(), DataVector(5, math.cos(0.67)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.cos()[0], 1.0)\n        self.assertEqual(b.cos()[1], math.cos(-0.5))\n\n    def test_cosh(self):\n        a = DataVector(5, 1.0)\n        self.assertEqual(a.cosh(), DataVector(5, math.cosh(1.0)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.cosh()[0], 1.0)\n        self.assertEqual(b.cosh()[1], math.cosh(-0.5))\n\n    def test_erf(self):\n        a = DataVector(5, 1.0)\n        self.assertAlmostEqual(a.erf()[0], math.erf(1.0))\n        b = DataVector([2.0, 3.0])\n        self.assertAlmostEqual(b.erf()[0], math.erf(2.0))\n        self.assertAlmostEqual(b.erf()[1], math.erf(3.0))\n\n    def test_erfc(self):\n        a = DataVector(5, 1)\n        self.assertAlmostEqual(a.erfc()[0], math.erfc(1.0))\n        b = DataVector([2.0, 3.0])\n        self.assertAlmostEqual(b.erfc()[0], math.erfc(2.0))\n        self.assertAlmostEqual(b.erfc()[1], math.erfc(3.0))\n\n    def test_exp(self):\n        a = DataVector(5, 1.0)\n        self.assertEqual(a.exp(), DataVector(5, math.exp(1.0)))\n        b = DataVector([2.0, -0.5])\n        self.assertEqual(b.exp()[0], math.exp(2.0))\n        self.assertEqual(b.exp()[1], math.exp(-0.5))\n\n    def test_exp2(self):\n        a = DataVector(5, 1.0)\n        self.assertEqual(a.exp2(), DataVector(5, 2.0))\n        b = DataVector([2.0, -0.5])\n        self.assertAlmostEqual(b.exp2()[0], 4.0)\n        self.assertAlmostEqual(b.exp2()[1], 1.0 / math.sqrt(2.0))\n\n    def test_exp10(self):\n        a = DataVector(5, 1.0)\n        self.assertEqual(a.exp10(), DataVector(5, 10.0))\n        b = DataVector([2.0, -0.5])\n        self.assertEqual(b.exp10()[0], 100.0)\n        self.assertEqual(b.exp10()[1], 1.0 / math.sqrt(10.0))\n\n    def test_fabs(self):\n        a = DataVector(5, -0.67)\n        self.assertEqual(a.fabs(), DataVector(5, 0.67))\n        b = DataVector([2.0, -0.5])\n        self.assertEqual(b.fabs()[0], 2.0)\n        self.assertEqual(b.fabs()[1], 0.5)\n\n    def test_hypot(self):\n        a = DataVector(5, 3.0)\n        b = DataVector(5, 4.0)\n        self.assertEqual(a.hypot(b)[0], 5.0)\n        x = DataVector([0.0, 3.0])\n        y = DataVector([5.0, 4.0])\n        self.assertEqual(x.hypot(y), DataVector(2, 5.0))\n\n    def test_invcbrt(self):\n        a = DataVector(5, 8.0)\n        self.assertEqual(a.invcbrt(), DataVector(5, 0.5))\n        b = DataVector([1.0, -64.0])\n        self.assertEqual(b.invcbrt()[0], 1.0)\n        self.assertEqual(b.invcbrt()[1], -0.25)\n\n    def test_invsqrt(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.invsqrt(), DataVector(5, 0.5))\n        b = DataVector([1.0, 16.0])\n        self.assertEqual(b.invsqrt()[0], 1.0)\n        self.assertEqual(b.invsqrt()[1], 0.25)\n\n    def test_log(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.log(), DataVector(5, math.log(4.0)))\n        b = DataVector([1.0, 0.5])\n        self.assertEqual(b.log()[0], 0.0)\n        self.assertEqual(b.log()[1], math.log(0.5))\n\n    def test_log2(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.log2(), DataVector(5, 2.0))\n        b = DataVector([1.0, 0.5])\n        self.assertEqual(b.log2()[0], 0.0)\n        self.assertEqual(b.log2()[1], -1.0)\n\n    def test_log10(self):\n        a = DataVector(5, 100.0)\n        self.assertEqual(a.log10(), DataVector(5, 2.0))\n        b = DataVector([1.0, 0.1])\n        self.assertEqual(b.log10()[0], 0.0)\n        self.assertEqual(b.log10()[1], -1.0)\n\n    def test_max(self):\n        a = DataVector([4.0, 5.0, 4.0, 4.0, 4.0])\n        self.assertEqual(a.max(), 5.0)\n        b = DataVector([1.0, 0.5])\n        self.assertEqual(b.max(), 1.0)\n\n    def test_min(self):\n        a = DataVector([4.0, 2.0, 4.0, 4.0, 4.0])\n        self.assertEqual(a.min(), 2.0)\n        b = DataVector([1.0, -0.5])\n        self.assertEqual(b.min(), -0.5)\n\n    def test_pow(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.pow(2), DataVector(5, 16.0))\n        b = DataVector([1.0, -0.5])\n        self.assertEqual(b.pow(2)[0], 1.0)\n        self.assertEqual(b.pow(2)[1], 0.25)\n\n    def test_sin(self):\n        a = DataVector(5, 6.7)\n        self.assertEqual(a.sin(), DataVector(5, math.sin(6.7)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.sin()[0], 0.0)\n        self.assertEqual(b.sin()[1], math.sin(-0.5))\n\n    def test_sinh(self):\n        a = DataVector(5, 3.0)\n        self.assertEqual(a.sinh(), DataVector(5, math.sinh(3.0)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.sinh()[0], 0.0)\n        self.assertEqual(b.sinh()[1], math.sinh(-0.5))\n\n    def test_sqrt(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.sqrt(), DataVector(5, 2.0))\n        b = DataVector([1.0, 0.25])\n        self.assertEqual(b.sqrt()[0], 1.0)\n        self.assertEqual(b.sqrt()[1], 0.5)\n\n    def test_step_function(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.step_function(), DataVector(5, 1.0))\n        b = DataVector([1.0, -0.5])\n        self.assertEqual(b.step_function()[0], 1.0)\n        self.assertEqual(b.step_function()[1], 0.0)\n\n    def test_tan(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.tan(), DataVector(5, math.tan(4.0)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.tan()[0], 0.0)\n        self.assertEqual(b.tan()[1], math.tan(-0.5))\n\n    def test_tanh(self):\n        a = DataVector(5, 4.0)\n        self.assertEqual(a.tanh(), DataVector(5, math.tanh(4.0)))\n        b = DataVector([0.0, -0.5])\n        self.assertEqual(b.tanh()[0], 0.0)\n        self.assertEqual(b.tanh()[1], math.tanh(-0.5))\n\n    def test_numpy_compatibility(self):\n        b = np.array([1.0, 2.0, 3.0])\n        c = DataVector([1.0, 2.0, 3.0])\n        self.assertTrue(((b + c) == np.array([2.0, 4.0, 6.0])).all())\n        x = np.linspace(0, 2 * np.pi, 10)\n        npt.assert_allclose(DataVector(x).sin(), np.sin(x))\n        # Convert a DataVector to a Numpy array\n        c_array_copy = np.array(c)\n        npt.assert_equal(c_array_copy, b)\n        # Changing the copy shouldn't change the DataVector\n        c_array_copy[2] = 4.0\n        npt.assert_equal(c, b)\n        c_array_reference = np.array(c, copy=False)\n        npt.assert_equal(c_array_reference, b)\n        # Changing the reference should change the DataVector as well\n        c_array_reference[2] = 4.0\n        self.assertEqual(c[2], 4.0)\n        # Convert a Numpy array to a DataVector\n        b_dv_copy = DataVector(b)\n        self.assertEqual(b_dv_copy, DataVector([1.0, 2.0, 3.0]))\n        b_dv_copy[2] = 4.0\n        self.assertEqual(b[2], 3.0)\n        b_dv_reference = DataVector(b, copy=False)\n        b_dv_reference[2] = 4.0\n        self.assertEqual(b[2], 4.0)\n\n    def test_iterator(self):\n        a = DataVector([1.0, 2.0, 3.0, 4.0, 5.0])\n        for i, val in enumerate(a):\n            self.assertEqual(val, a[i])\n\n    def test_size_constructor(self):\n        a = DataVector(3)\n        self.assertEqual(len(a), 3)\n\n    def test_list_constructor(self):\n        a = DataVector([1.0, 2.0, 3.0, 4.0, 5.0])\n        self.assertEqual(len(a), 5)\n        self.assertEqual(a[3], 4.0)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_DataVectorBinaryOperations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is separated from `Test_DataVector.cpp` in an effort to parallelize\n// the test builds.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_data_vector_multiple_operand_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-10.0, 10.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.1, 10.0}};\n\n  const auto binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Divides<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Multiplies<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, DataVector>(binary_ops);\n\n  const auto inplace_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::DivAssign<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::MinusAssign<>{},\n                      std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::MultAssign<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::PlusAssign<>{},\n                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Inplace, DataVector>(\n      inplace_binary_ops);\n\n  const auto strict_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Atan2<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::Hypot<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, DataVector>(strict_binary_ops);\n\n  const auto cascaded_ops = std::make_tuple(\n      std::make_tuple(funcl::Multiplies<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)),\n      std::make_tuple(funcl::Multiplies<funcl::Exp<>, funcl::Sin<>>{},\n                      std::make_tuple(generic, generic)),\n      std::make_tuple(\n          funcl::Plus<funcl::Atan<funcl::Tan<>>, funcl::Divides<>>{},\n          std::make_tuple(generic, generic, positive)),\n      // step function with multiplication should be tested due to resolved\n      // [issue 1122](https://github.com/sxs-collaboration/spectre/issues/1122)\n      std::make_tuple(funcl::StepFunction<\n                          funcl::Plus<funcl::Multiplies<>, funcl::Identity>>{},\n                      std::make_tuple(generic, generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, DataVector>(cascaded_ops);\n\n  const auto array_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, std::array<DataVector, 2>>(\n      array_binary_ops);\n\n  const auto test_cross = [](const DataVector& dv1, const DataVector& dv2) {\n    return DataVector{{dv1[1] * dv2[2] - dv1[2] * dv2[1],\n                       dv1[2] * dv2[0] - dv1[0] * dv2[2],\n                       dv1[0] * dv2[1] - dv1[1] * dv2[0]}};\n  };\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> dist{generic[0], generic[1]};\n  const auto dv1 = make_with_random_values<DataVector>(\n      make_not_null(&generator), dist, DataVector{3, 0.0});\n  const auto dv2 = make_with_random_values<DataVector>(\n      make_not_null(&generator), dist, DataVector{3, 0.0});\n\n  // Check Blaze implementation of cross product\n  CHECK(cross(dv1, dv2) == test_cross(dv1, dv2));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataVector.MultipleOperands\",\n                  \"[DataStructures][Unit]\") {\n  test_data_vector_multiple_operand_math();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_DiagonalModalOperator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <tuple>\n\n#include \"DataStructures/DiagonalModalOperator.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_diagonal_modal_operator_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.01, 100.0}};\n\n  const auto binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Divides<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Multiplies<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, DiagonalModalOperator>(\n      binary_ops);\n\n  const auto inplace_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::DivAssign<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::MinusAssign<>{},\n                      std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::MultAssign<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::PlusAssign<>{},\n                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Inplace, DiagonalModalOperator>(\n      inplace_binary_ops);\n\n  // Note that a collection of additional operations that involve acting on\n  // modal vectors with diagonal modal operators have been moved to\n  // `Test_MoreDiagonalModalOperatorMath.cpp` in an effort to better\n  // parallelize the build.\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DiagonalModalOperator\",\n                  \"[DataStructures][Unit]\") {\n  {\n    INFO(\"test construct and assign\");\n    TestHelpers::VectorImpl::vector_test_construct_and_assign<\n        DiagonalModalOperator, double>();\n  }\n  {\n    INFO(\"test serialize and deserialize\");\n    TestHelpers::VectorImpl::vector_test_serialize<DiagonalModalOperator,\n                                                   double>();\n  }\n  {\n    INFO(\"test set_data_ref functionality\");\n    TestHelpers::VectorImpl::vector_test_ref<DiagonalModalOperator, double>();\n  }\n  {\n    INFO(\"test math after move\");\n    TestHelpers::VectorImpl::vector_test_math_after_move<DiagonalModalOperator,\n                                                         double>();\n  }\n  {\n    INFO(\"test DiagonalModalOperator math operations\");\n    test_diagonal_modal_operator_math();\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<\n          DiagonalModalOperator>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::ExpressionAssign),\n      Catch::Matchers::ContainsSubstring(\"Must assign into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<\n          DiagonalModalOperator>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Copy),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<\n          DiagonalModalOperator>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Move),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_DynamicBuffer.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicBuffer.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TypeTraits/IsIterable.hpp\"\n\nnamespace {\n\ntemplate <typename T>\nvoid check_results(const DynamicBuffer<T>& dynamic_buffer,\n                   const std::vector<T>& expected) {\n  CHECK(dynamic_buffer.size() == expected.size());\n\n  for (size_t i = 0; i < dynamic_buffer.size(); ++i) {\n    CHECK(dynamic_buffer[i] == expected[i]);\n    CHECK(dynamic_buffer.at(i) == expected[i]);\n  }\n\n  size_t i = 0;\n  for (auto it = dynamic_buffer.begin(); it != dynamic_buffer.end();\n       ++it, ++i) {\n    CHECK(*it == expected[i]);\n  }\n\n  i = 0;\n  for (const auto& val : dynamic_buffer) {\n    CHECK(val == expected[i]);\n    ++i;\n  }\n}\n\ntemplate <typename T>\nstd::vector<T> create_random_data(const gsl::not_null<std::mt19937*> gen,\n                                  const size_t number_of_vectors,\n                                  const size_t number_of_grid_points) {\n  auto value_distribution = std::uniform_real_distribution(\n      std::numeric_limits<double>::min(), std::numeric_limits<double>::max());\n\n  std::vector<T> res(number_of_vectors);\n  for (size_t i = 0; i < number_of_vectors; ++i) {\n    res[i] = make_with_random_values<T>(gen, value_distribution,\n                                        number_of_grid_points);\n  }\n  return res;\n}\n\ntemplate <typename T>\nvoid test_dynamic_buffer() {\n  MAKE_GENERATOR(gen);\n  auto size_distribution = std::uniform_int_distribution(1, 10);\n  auto number_of_grid_points = static_cast<size_t>(size_distribution(gen));\n  const auto number_of_vectors = static_cast<size_t>(size_distribution(gen));\n  if constexpr (std::is_same_v<double, T>) {\n    number_of_grid_points = 1;\n  }\n\n  CAPTURE(number_of_grid_points);\n  CAPTURE(number_of_vectors);\n\n  const auto expected = create_random_data<T>(\n      make_not_null(&gen), number_of_vectors, number_of_grid_points);\n\n  DynamicBuffer<T> dynamic_buffer(number_of_vectors, number_of_grid_points);\n\n  for (size_t i = 0; i < number_of_vectors; ++i) {\n    dynamic_buffer.at(i) = expected.at(i);\n  }\n\n  dynamic_buffer = serialize_and_deserialize(dynamic_buffer);\n  check_results(dynamic_buffer, expected);\n\n  test_copy_semantics(dynamic_buffer);\n  auto dynamic_buffer_for_move = dynamic_buffer;\n  test_move_semantics(std::move(dynamic_buffer_for_move), dynamic_buffer);\n\n  DynamicBuffer<T> dynamic_buffer_copied(dynamic_buffer);\n  DynamicBuffer<T> dynamic_buffer_assigned;\n  dynamic_buffer_assigned = dynamic_buffer;\n\n  if constexpr (tt::is_iterable_v<T>) {\n    auto copied_it = dynamic_buffer_copied.begin();\n    auto assigned_it = dynamic_buffer_assigned.begin();\n    // check that the new buffers don't point to the same data storage location\n    for (auto it = dynamic_buffer.begin(); it != dynamic_buffer.end();\n         ++it, ++copied_it, ++assigned_it) {\n      // first `DataVector` of dynamic_buffer\n      const auto first_data_vector = *it;\n\n      // underlying C array\n      const auto array = first_data_vector.data();\n\n      // address of first element\n      const auto address = &*array;\n\n      // analogously for copied DynamicBuffers\n      CHECK(&*((*copied_it).data()) != address);\n      CHECK(&*((*assigned_it).data()) != address);\n    }\n    CHECK(copied_it == dynamic_buffer_copied.end());\n    CHECK(assigned_it == dynamic_buffer_assigned.end());\n  }\n\n  DynamicBuffer<T> dynamic_buffer_moved = std::move(dynamic_buffer);\n\n  dynamic_buffer_copied = serialize_and_deserialize(dynamic_buffer_copied);\n  dynamic_buffer_assigned = serialize_and_deserialize(dynamic_buffer_assigned);\n  dynamic_buffer_moved = serialize_and_deserialize(dynamic_buffer_moved);\n\n  check_results(dynamic_buffer_copied, expected);\n  check_results(dynamic_buffer_assigned, expected);\n  check_results(dynamic_buffer_moved, expected);\n}\n\n}  // namespace\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DynamicBuffer\",\n                  \"[DataStructures][Unit]\") {\n  for (size_t i = 0; i < 20; ++i) {\n    test_dynamic_buffer<DataVector>();\n    test_dynamic_buffer<double>();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ExtractPoint.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ExtractPoint.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing namespace std::complex_literals;\n\ntemplate <typename DataType>\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType>\nstruct TensorTag : db::SimpleTag {\n  using type = tnsr::ii<DataType, 2>;\n};\n\ntemplate <typename ValueType, typename... Structure,\n          typename VectorType = std::conditional_t<\n              std::is_same_v<ValueType, std::complex<double>>,\n              ComplexDataVector, DataVector>>\nTensor<VectorType, Structure...> promote_to_data_vectors(\n    const Tensor<ValueType, Structure...>& tensor) {\n  Tensor<VectorType, Structure...> result(1_st);\n  for (size_t i = 0; i < result.size(); ++i) {\n    result[i] = tensor[i];\n  }\n  return result;\n}\n\ntemplate <typename VectorType,\n          typename ValueType = typename VectorType::value_type>\nvoid test_extract_point() {\n  // Test with owning Tensors as well as Variables to make sure there\n  // are no assumptions about memory layout in the tensor versions.\n  Scalar<VectorType> scalar(2_st);\n  tnsr::ii<VectorType, 2> tensor(2_st);\n  if constexpr (std::is_same_v<VectorType, ComplexDataVector>) {\n    get(scalar) = ComplexDataVector{3.0 + 5.0i, 4.0 + 6.0i};\n    get<0, 0>(tensor) = ComplexDataVector{1.0 + 3.0i, 2.0 + 4.0i};\n    get<0, 1>(tensor) = ComplexDataVector{3.0 + 5.0i, 4.0 + 6.0i};\n    get<1, 1>(tensor) = ComplexDataVector{5.0 + 7.0i, 6.0 + 8.0i};\n  } else {\n    get(scalar) = DataVector{3.0, 4.0};\n    get<0, 0>(tensor) = DataVector{1.0, 2.0};\n    get<0, 1>(tensor) = DataVector{3.0, 4.0};\n    get<1, 1>(tensor) = DataVector{5.0, 6.0};\n  }\n  Variables<tmpl::list<ScalarTag<VectorType>, TensorTag<VectorType>>> variables(\n      2);\n  get<ScalarTag<VectorType>>(variables) = scalar;\n  get<TensorTag<VectorType>>(variables) = tensor;\n\n  Scalar<VectorType> reconstructed_scalar{VectorType(2)};\n  tnsr::ii<VectorType, 2> reconstructed_tensor(VectorType(2));\n  Scalar<VectorType> reconstructed_scalar_from_dv{VectorType(2)};\n  tnsr::ii<VectorType, 2> reconstructed_tensor_from_dv(VectorType(2));\n  Variables<tmpl::list<ScalarTag<VectorType>, TensorTag<VectorType>>>\n      reconstructed_variables(2);\n\n  const auto check_point = [&](const size_t index,\n                               const ValueType scalar_component,\n                               const std::array<ValueType, 3>&\n                                   tensor_components) {\n    const Scalar<ValueType> expected_scalar{scalar_component};\n    tnsr::ii<ValueType, 2> expected_tensor;\n    get<0, 0>(expected_tensor) = tensor_components[0];\n    get<0, 1>(expected_tensor) = tensor_components[1];\n    get<1, 1>(expected_tensor) = tensor_components[2];\n    const Scalar<VectorType> expected_scalar_dv =\n        promote_to_data_vectors(expected_scalar);\n    const tnsr::ii<VectorType, 2> expected_tensor_dv =\n        promote_to_data_vectors(expected_tensor);\n    Variables<tmpl::list<ScalarTag<VectorType>, TensorTag<VectorType>>>\n        expected_variables(1);\n    get<ScalarTag<VectorType>>(expected_variables) = expected_scalar_dv;\n    get<TensorTag<VectorType>>(expected_variables) = expected_tensor_dv;\n\n    {\n      Scalar<ValueType> result{};\n      extract_point(make_not_null(&result), scalar, index);\n      CHECK(result == expected_scalar);\n    }\n    {\n      tnsr::ii<ValueType, 2> result{};\n      extract_point(make_not_null(&result), tensor, index);\n      CHECK(result == expected_tensor);\n    }\n    CHECK(extract_point(scalar, index) == expected_scalar);\n    CHECK(extract_point(tensor, index) == expected_tensor);\n    {\n      Scalar<VectorType> result(1_st);\n      extract_point(make_not_null(&result), scalar, index);\n      CHECK(result == expected_scalar_dv);\n    }\n    {\n      tnsr::ii<VectorType, 2> result(1_st);\n      extract_point(make_not_null(&result), tensor, index);\n      CHECK(result == expected_tensor_dv);\n    }\n    {\n      Variables<tmpl::list<ScalarTag<VectorType>, TensorTag<VectorType>>>\n          result(1);\n      extract_point(make_not_null(&result), variables, index);\n      CHECK(result == expected_variables);\n    }\n    CHECK(extract_point(variables, index) == expected_variables);\n\n    overwrite_point(make_not_null(&reconstructed_scalar), expected_scalar,\n                    index);\n    overwrite_point(make_not_null(&reconstructed_tensor), expected_tensor,\n                    index);\n    overwrite_point(make_not_null(&reconstructed_scalar_from_dv),\n                    expected_scalar_dv, index);\n    overwrite_point(make_not_null(&reconstructed_tensor_from_dv),\n                    expected_tensor_dv, index);\n    overwrite_point(make_not_null(&reconstructed_variables), expected_variables,\n                    index);\n  };\n  if constexpr (std::is_same_v<VectorType, ComplexDataVector>) {\n    check_point(0, 3.0 + 5.0i, {{1.0 + 3.0i, 3.0 + 5.0i, 5.0 + 7.0i}});\n    check_point(1, 4.0 + 6.0i, {{2.0 + 4.0i, 4.0 + 6.0i, 6.0 + 8.0i}});\n  } else {\n    check_point(0, 3.0, {{1.0, 3.0, 5.0}});\n    check_point(1, 4.0, {{2.0, 4.0, 6.0}});\n  }\n\n  CHECK(reconstructed_scalar == scalar);\n  CHECK(reconstructed_tensor == tensor);\n  CHECK(reconstructed_scalar_from_dv == scalar);\n  CHECK(reconstructed_tensor_from_dv == tensor);\n  CHECK(reconstructed_variables == variables);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.ExtractPoint\",\n                  \"[DataStructures][Unit]\") {\n  test_extract_point<DataVector>();\n  test_extract_point<ComplexDataVector>();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_FixedHashMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <initializer_list>\n#include <iterator>\n#include <ostream>\n#include <pup.h>\n#include <stdexcept>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/FixedHashMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid check_single_element_map_with_direction(const Direction<Dim>& direction) {\n  FixedHashMap<2 * Dim, Direction<Dim>, std::pair<double, NonCopyable>,\n               DirectionHash<Dim>>\n      map;\n\n  map.emplace(direction, std::make_pair(1.5, NonCopyable{}));\n  const typename decltype(map)::value_type entry(\n      std::as_const(direction), std::make_pair(1.5, NonCopyable{}));\n\n  const auto& const_map = map;\n\n  test_iterators(map);\n  check_cmp(const_map.begin(), const_map.end());\n  check_cmp(map.begin(), map.end());\n  check_cmp(map.cbegin(), map.end());\n  check_cmp(map.begin(), map.cend());\n\n  CHECK(not const_map.empty());\n  CHECK(const_map.size() == 1);\n\n  CHECK(const_map.count(direction) == 1);\n  CHECK(const_map.count(direction.opposite()) == 0);\n\n  CHECK(const_map.find(direction) == const_map.begin());\n  CHECK(const_map.find(direction.opposite()) == const_map.end());\n\n  CHECK(*const_map.begin() == entry);\n  CHECK(&const_map.at(direction) == &const_map.begin()->second);\n\n  auto it = map.begin();\n  CHECK(*it == entry);\n  CHECK(it.operator->() == &*it);\n  auto it2 = it;\n  CHECK(*it++ == entry);\n  CHECK(it != it2);\n  CHECK(*it2 == entry);\n  CHECK(it == map.end());\n  CHECK(++it2 == map.end());\n  CHECK(it2 == map.end());\n}\n\ntemplate <size_t MaxSize>\nvoid test_direction_key() {\n  for (const auto& direction : Direction<2>::all_directions()) {\n    check_single_element_map_with_direction(direction);\n  }\n\n  FixedHashMap<MaxSize, Direction<2>, double, DirectionHash<2>> map;\n\n  CHECK(map.empty());\n  CHECK(map.size() == 0);  // NOLINT(readability-container-size-empty)\n\n  CHECK(map.begin() == std::as_const(map).begin());\n  CHECK(map.begin() == std::as_const(map).cbegin());\n  CHECK(map.end() == std::as_const(map).end());\n  CHECK(map.end() == std::as_const(map).cend());\n  CHECK(map.begin() == map.end());\n\n  const auto dir1 = Direction<2>::lower_eta();\n  const auto dir2 = Direction<2>::upper_xi();\n  const auto dir3 = Direction<2>::lower_xi();\n  {\n    CHECK(map.count(dir1) == 0);\n    const auto result = map.emplace(dir1, 1.);\n    CHECK(result.second);\n    CHECK(result.first == map.find(dir1));\n    CHECK(map.count(dir1) == 1);\n  }\n  CHECK(not map.empty());\n  CHECK(map.size() == 1);\n  {\n    CHECK(map.count(dir2) == 0);\n    const auto result = map.insert(std::make_pair(dir2, 2.));\n    CHECK(result.second);\n    CHECK(result.first == map.find(dir2));\n    CHECK(map.count(dir2) == 1);\n  }\n  CHECK(not map.empty());\n  CHECK(map.size() == 2);\n  {\n    const auto result = map.insert(std::make_pair(dir2, 3.));\n    CHECK(not result.second);\n    CHECK(result.first == map.find(dir2));\n    CHECK(map.count(dir2) == 1);\n  }\n  CHECK(map.size() == 2);\n  {\n    const auto result = map.emplace(dir1, 4.);\n    CHECK(not result.second);\n    CHECK(result.first == map.find(dir1));\n    CHECK(map.count(dir1) == 1);\n  }\n  CHECK(map.size() == 2);\n  CHECK(std::as_const(map).at(dir1) == 1.);\n  CHECK(std::as_const(map).at(dir2) == 2.);\n  map.at(dir2) = 5.;\n  CHECK(std::as_const(map).at(dir2) == 5.);\n  CHECK(map.size() == 2);\n  CHECK(&std::as_const(map).find(dir1)->second == &std::as_const(map).at(dir1));\n  CHECK(&std::as_const(map).find(dir2)->second == &std::as_const(map).at(dir2));\n  CHECK(std::as_const(map).find(dir3) == map.end());\n\n  const auto check_exception = [&dir3](auto& passed_map) {\n    CHECK_THROWS_MATCHES(\n        passed_map.at(dir3), std::out_of_range,\n        Catch::Matchers::MessageMatches(Catch::Matchers::ContainsSubstring(\n            get_output(dir3) + \" not in FixedHashMap\")));\n  };\n  check_exception(map);\n  check_exception(std::as_const(map));\n\n  map[dir3] = 6.;\n  CHECK(map.size() == 3);\n  CHECK(std::as_const(map).at(dir3) == 6.);\n\n  test_iterators(map);\n\n  {\n    // The map doesn't guarantee an iteration order, so this block\n    // would put the map into a difficult-to-describe state.  We use a\n    // copy to not have to deal with that.\n    auto map2 = map;\n    const auto second = std::next(map2.begin());\n    const auto third = std::next(map2.begin(), 2);\n    // Checks iterator mutability correctness for erase\n    const auto next = map2.erase(std::as_const(map2).find(second->first));\n    CHECK(map2.size() == 2);\n    CHECK(next == third);\n    next->second = -1.;\n    CHECK(std::next(map2.begin())->second == -1.);\n  }\n\n  CHECK(map == map);\n  CHECK_FALSE(map != map);\n  {\n    // Test copy and move\n    const auto check_equal = [](const auto& a, const auto& b) {\n      CHECK(a == b);\n      CHECK(b == a);\n      CHECK_FALSE(a != b);\n      CHECK_FALSE(b != a);\n    };\n    auto map2 = map;\n    check_equal(map, map2);\n    auto map3 = std::move(map2);\n    check_equal(map, map3);\n    decltype(map) map4;\n    map4 = std::move(map3);\n    check_equal(map, map4);\n    decltype(map) map5;\n    map5 = map4;\n    check_equal(map, map5);\n\n    // Test moving non-copyable entries\n    FixedHashMap<MaxSize, Direction<2>, NonCopyable, DirectionHash<2>> nc_map;\n    nc_map.emplace(Direction<2>::upper_xi(), NonCopyable{});\n    FixedHashMap<MaxSize, Direction<2>, NonCopyable, DirectionHash<2>> nc_map2;\n    nc_map2.emplace(Direction<2>::upper_xi(), NonCopyable{});\n\n    check_equal(nc_map, nc_map2);\n    auto nc_map3 = std::move(nc_map2);\n    check_equal(nc_map, nc_map3);\n    FixedHashMap<MaxSize, Direction<2>, NonCopyable, DirectionHash<2>> nc_map4;\n    nc_map4 = std::move(nc_map3);\n    check_equal(nc_map, nc_map4);\n  }\n  {\n    auto map2 = map;\n    map2.erase(dir1);\n    CHECK_FALSE(map == map2);\n    CHECK_FALSE(map2 == map);\n    CHECK(map != map2);\n    CHECK(map2 != map);\n  }\n  {\n    auto map2 = map;\n    map2.at(dir1) = -1.;\n    CHECK_FALSE(map == map2);\n    CHECK_FALSE(map2 == map);\n    CHECK(map != map2);\n    CHECK(map2 != map);\n  }\n  {\n    // Check initializer list constructor\n    CHECK(decltype(map)(\n              std::initializer_list<typename decltype(map)::value_type>{})\n              .empty());\n    CHECK(map == decltype(map){{dir1, map.at(dir1)},\n                               {dir2, map.at(dir2)},\n                               {dir3, map.at(dir3)}});\n    CHECK(map == decltype(map){{dir2, map.at(dir2)},\n                               {dir3, map.at(dir3)},\n                               {dir1, map.at(dir1)}});\n  }\n\n  test_serialization(map);\n\n  CHECK(get_output(map) == get_output(std::unordered_map<Direction<2>, double>(\n                               map.begin(), map.end())));\n\n  CHECK(map.contains(dir1));\n  CHECK(map.erase(dir1) == 1);\n  CHECK_FALSE(map.contains(dir1));\n  CHECK(map.size() == 2);\n  CHECK(map.find(dir1) == map.end());\n  CHECK(map.erase(dir1) == 0);\n  CHECK(map.size() == 2);\n  CHECK(map.find(dir1) == map.end());\n\n  map.clear();\n  CHECK(map.empty());\n  CHECK(map.size() == 0);  // NOLINT(readability-container-size-empty)\n  test_iterators(map);\n\n  test_copy_semantics(map);\n  const auto map2 = map;\n  test_move_semantics(std::move(map), map2);\n}\n\n// A non-copyable size_t wrapper for testing non-copyable keys.\nstruct NonCopyableSizeT {\n  constexpr NonCopyableSizeT() = default;\n  constexpr NonCopyableSizeT(const NonCopyableSizeT&) = delete;\n  constexpr NonCopyableSizeT& operator=(const NonCopyableSizeT&) = delete;\n  constexpr NonCopyableSizeT(NonCopyableSizeT&&) = default;\n  NonCopyableSizeT& operator=(NonCopyableSizeT&&) = default;\n  ~NonCopyableSizeT() = default;\n\n  // Intentional implicit conversion\n  // NOLINTNEXTLINE(google-explicit-constructor)\n  NonCopyableSizeT(size_t t) : value(t) {}\n\n  void pup(PUP::er& p) { p | value; }  // NOLINT\n\n  size_t value = 0;\n};\ninline bool operator==(const NonCopyableSizeT& a, const NonCopyableSizeT& b) {\n  return a.value == b.value;\n}\ninline bool operator!=(const NonCopyableSizeT& a, const NonCopyableSizeT& b) {\n  return not(a == b);\n}\ninline std::ostream& operator<<(std::ostream& os, const NonCopyableSizeT& v) {\n  return os << v.value;\n}\n\n// A hash that hashes two inputs to the same value\nstruct RepeatedHash {\n  size_t operator()(const size_t t) const {\n    if (t == 3) {\n      return 2;\n    }\n    if (t == 4) {\n      return 5;\n    }\n    return t;\n  }\n\n  size_t operator()(const NonCopyableSizeT& t) const {\n    if (t.value == 3) {\n      return 2;\n    }\n    if (t.value == 4) {\n      return 5;\n    }\n    return t.value;\n  }\n};\n\ntemplate <typename KeyType>\n// NOLINTNEXTLINE(google-readability-function-size, readability-function-size)\nvoid test_repeated_key() {\n  using CopyableKeyMapType = FixedHashMap<6, size_t, size_t, RepeatedHash>;\n  using NonCopyableKeyMapType =\n      FixedHashMap<6, NonCopyableSizeT, size_t, RepeatedHash>;\n  FixedHashMap<6, KeyType, size_t, RepeatedHash> map;\n  CHECK(map.empty());\n  // NOLINTNEXTLINE(readability-container-size-empty)\n  CHECK(map.size() == 0);\n\n  map.emplace(1, 11);\n  CHECK_FALSE(map.empty());\n  CHECK(map.size() == 1);\n  CHECK(map.at(1) == 11);\n  CHECK(map.count(1) == 1);\n  CHECK(map.count(2) == 0);\n  CHECK(map.contains(1));\n  CHECK_FALSE(map.contains(2));\n  CHECK_FALSE(map.contains(3));\n  CHECK(map.find(1) != map.end());\n  CHECK(map.find(2) == map.end());\n  CHECK(std::as_const(map).find(1) != map.end());\n  CHECK(std::as_const(map).find(2) == map.end());\n\n  Overloader{[](const gsl::not_null<CopyableKeyMapType*> local_map) {\n               (*local_map)[2] = 12;\n             },\n             [](const gsl::not_null<NonCopyableKeyMapType*> local_map) {\n               local_map->insert({2, 12});\n             }}(make_not_null(&map));\n  CHECK(map.size() == 2);\n  CHECK_FALSE(map.empty());\n  CHECK(map.at(2) == 12);\n  CHECK(map.count(1) == 1);\n  CHECK(map.count(2) == 1);\n  CHECK(map.count(3) == 0);\n  CHECK(map.contains(1));\n  CHECK(map.contains(2));\n  CHECK_FALSE(map.contains(3));\n  CHECK(map.find(1) != map.end());\n  CHECK(map.find(2) != map.end());\n  CHECK(map.find(3) == map.end());\n  CHECK(std::as_const(map).find(1) != map.end());\n  CHECK(std::as_const(map).find(2) != map.end());\n  CHECK(std::as_const(map).find(3) == map.end());\n\n  map.insert({3, 13});\n  CHECK(map.size() == 3);\n  CHECK_FALSE(map.empty());\n  CHECK(map.at(3) == 13);\n  CHECK(std::as_const(map).at(3) == 13);\n  Overloader{[](const gsl::not_null<CopyableKeyMapType*> local_map) {\n               CHECK((*local_map)[3] == 13);\n             },\n             [](const gsl::not_null<NonCopyableKeyMapType*> /*local_map*/) {}}(\n      make_not_null(&map));\n  CHECK(map.count(1) == 1);\n  CHECK(map.count(2) == 1);\n  CHECK(map.count(3) == 1);\n  CHECK(map.contains(1));\n  CHECK(map.contains(2));\n  CHECK(map.contains(3));\n  CHECK(map.find(1) != map.end());\n  CHECK(map.find(2) != map.end());\n  CHECK(map.find(3) != map.end());\n  CHECK(map.find(2) != map.find(3));\n  CHECK(std::as_const(map).find(1) != map.end());\n  CHECK(std::as_const(map).find(2) != map.end());\n  CHECK(std::as_const(map).find(3) != map.end());\n  CHECK(std::as_const(map).find(2) != map.find(3));\n\n  map.erase(2);\n  CHECK(map.size() == 2);\n  CHECK_FALSE(map.empty());\n  CHECK(map.count(1) == 1);\n  CHECK(map.count(2) == 0);\n  CHECK(map.count(3) == 1);\n  CHECK(map.contains(1));\n  CHECK_FALSE(map.contains(2));\n  CHECK(map.contains(3));\n  CHECK(map.find(1) != map.end());\n  CHECK(map.find(2) == map.end());\n  CHECK(map.find(3) != map.end());\n  CHECK(map.find(2) != map.find(3));\n\n  // Now check key that results in wrapping past end of storage_type\n  map.insert({4, 14});\n  CHECK(map.size() == 3);\n  CHECK_FALSE(map.empty());\n  CHECK(map.count(1) == 1);\n  CHECK(map.count(2) == 0);\n  CHECK(map.count(3) == 1);\n  CHECK(map.count(4) == 1);\n  CHECK(map.contains(4));\n  CHECK_FALSE(map.contains(2));\n  CHECK(map.find(1) != map.end());\n  CHECK(map.find(2) == map.end());\n  CHECK(map.find(3) != map.end());\n  CHECK(map.find(4) != map.end());\n  CHECK(map.at(4) == 14);\n  CHECK(std::as_const(map).at(4) == 14);\n  Overloader{[](const gsl::not_null<CopyableKeyMapType*> local_map) {\n               CHECK((*local_map)[4] == 14);\n             },\n             [](const gsl::not_null<NonCopyableKeyMapType*> /*local_map*/) {}}(\n      make_not_null(&map));\n\n  Overloader{[](const gsl::not_null<CopyableKeyMapType*> local_map) {\n               (*local_map)[5] = 15;\n             },\n             [](const gsl::not_null<NonCopyableKeyMapType*> local_map) {\n               local_map->insert_or_assign(5, 15);\n             }}(make_not_null(&map));\n  CHECK(map.size() == 4);\n  CHECK_FALSE(map.empty());\n  CHECK(map.count(1) == 1);\n  CHECK(map.count(2) == 0);\n  CHECK(map.count(3) == 1);\n  CHECK(map.count(4) == 1);\n  CHECK(map.count(5) == 1);\n  CHECK(map.contains(5));\n  CHECK(map.find(1) != map.end());\n  CHECK(map.find(2) == map.end());\n  CHECK(map.find(3) != map.end());\n  CHECK(map.find(4) != map.end());\n  CHECK(map.find(5) != map.end());\n  CHECK(map.find(4) != map.find(5));\n  CHECK(map.at(5) == 15);\n  CHECK(std::as_const(map).at(5) == 15);\n  Overloader{[](const gsl::not_null<CopyableKeyMapType*> local_map) {\n               CHECK((*local_map)[5] == 15);\n             },\n             [](const gsl::not_null<NonCopyableKeyMapType*> /*local_map*/) {}}(\n      make_not_null(&map));\n\n  map.erase(4);\n  CHECK(map.size() == 3);\n  CHECK_FALSE(map.empty());\n  CHECK(map.count(1) == 1);\n  CHECK(map.count(2) == 0);\n  CHECK(map.count(3) == 1);\n  CHECK(map.count(5) == 1);\n  CHECK(map.contains(5));\n  CHECK(map.find(1) != map.end());\n  CHECK(map.find(2) == map.end());\n  CHECK(map.find(3) != map.end());\n  CHECK(map.find(4) == map.end());\n  CHECK(map.find(5) != map.end());\n  CHECK(map.find(4) != map.find(5));\n  CHECK(map.at(5) == 15);\n  CHECK(std::as_const(map).at(5) == 15);\n  Overloader{[](const gsl::not_null<CopyableKeyMapType*> local_map) {\n               CHECK((*local_map)[5] == 15);\n             },\n             [](const gsl::not_null<NonCopyableKeyMapType*> /*local_map*/) {}}(\n      make_not_null(&map));\n\n  const auto check_4 = [](const auto& l_it_bool_to_4, const size_t expected,\n                          const bool inserted,\n                          // GCC warns about shadowing so we make a very\n                          // specific name for the map\n                          auto check_4_local_map) {\n    CHECK(l_it_bool_to_4.second == inserted);\n    CHECK(check_4_local_map->size() == 4);\n    CHECK_FALSE(check_4_local_map->empty());\n    CHECK(check_4_local_map->count(1) == 1);\n    CHECK(check_4_local_map->count(2) == 0);\n    CHECK(check_4_local_map->count(3) == 1);\n    CHECK(check_4_local_map->count(4) == 1);\n    CHECK(check_4_local_map->count(5) == 1);\n    CHECK(check_4_local_map->contains(4));\n    CHECK(check_4_local_map->find(1) != check_4_local_map->end());\n    CHECK(check_4_local_map->find(2) == check_4_local_map->end());\n    CHECK(check_4_local_map->find(3) != check_4_local_map->end());\n    CHECK(check_4_local_map->find(4) != check_4_local_map->end());\n    CHECK(check_4_local_map->find(5) != check_4_local_map->end());\n    CHECK(check_4_local_map->find(4) != check_4_local_map->find(5));\n    CHECK(check_4_local_map->find(4) == l_it_bool_to_4.first);\n    CHECK(check_4_local_map->at(4) == expected);\n    CHECK(std::as_const(*check_4_local_map).at(4) == expected);\n    Overloader{\n        [expected](const gsl::not_null<CopyableKeyMapType*> more_local_map) {\n          CHECK((*more_local_map)[4] == expected);\n        },\n        [](const gsl::not_null<NonCopyableKeyMapType*> /*local_map*/) {}}(\n        check_4_local_map);\n  };\n\n  Overloader{\n      [&check_4](const gsl::not_null<CopyableKeyMapType*> local_map) {\n        // Test serialization and that FixedHashMap works correctly after.\n        test_serialization(*local_map);\n\n        auto after_map = serialize_and_deserialize(*local_map);\n        CHECK(after_map.size() == 3);\n        CHECK_FALSE(after_map.empty());\n        CHECK(after_map.count(1) == 1);\n        CHECK(after_map.count(2) == 0);\n        CHECK(after_map.count(3) == 1);\n        CHECK(after_map.count(5) == 1);\n        CHECK(after_map.contains(5));\n        CHECK(after_map.find(1) != after_map.end());\n        CHECK(after_map.find(2) == after_map.end());\n        CHECK(after_map.find(3) != after_map.end());\n        CHECK(after_map.find(4) == after_map.end());\n        CHECK(after_map.find(5) != after_map.end());\n        CHECK(after_map.find(4) != after_map.find(5));\n        CHECK(after_map.at(5) == 15);\n        CHECK(std::as_const(after_map).at(5) == 15);\n        CHECK(after_map[5] == 15);\n\n        check_4(after_map.insert_or_assign(4, 14), 14, true, &after_map);\n        check_4(after_map.insert_or_assign(4, 24), 24, false, &after_map);\n\n        after_map.erase(4);\n        const size_t key_4 = 4;\n        check_4(after_map.insert_or_assign(key_4, 34), 34, true, &after_map);\n        check_4(after_map.insert_or_assign(key_4, 44), 44, false, &after_map);\n      },\n      [&check_4](const gsl::not_null<NonCopyableKeyMapType*> local_map) {\n        check_4(local_map->insert_or_assign(4, 14), 14, true, local_map);\n        check_4(local_map->insert_or_assign(4, 24), 24, false, local_map);\n\n        local_map->erase(4);\n        const size_t key_4 = 4;\n        check_4(local_map->insert_or_assign(key_4, 34), 34, true, local_map);\n        check_4(local_map->insert_or_assign(key_4, 44), 44, false, local_map);\n      }}(make_not_null(&map));\n\n  test_iterators(map);\n  Overloader{[](const gsl::not_null<CopyableKeyMapType*> local_map) {\n               test_copy_semantics(*local_map);\n               const auto map2 = *local_map;\n               test_move_semantics(std::move(*local_map), map2);\n             },\n             [](const gsl::not_null<NonCopyableKeyMapType*> /*local_map*/) {}}(\n      make_not_null(&map));\n}\n\nvoid test_equality() {\n  {\n    INFO(\"Equality should not depend on insertion order\");\n    // Using a hash that is not ordered\n    using Map = FixedHashMap<2, Direction<1>, double, std::hash<Direction<1>>>;\n    const auto dir1 = Direction<1>::lower_xi();\n    const auto dir2 = Direction<1>::upper_xi();\n    Map map{};\n    map.emplace(dir1, 1.);\n    map.emplace(dir2, 2.);\n    Map same_map{};\n    same_map.emplace(dir2, 2.);\n    same_map.emplace(dir1, 1.);\n    CHECK(map == same_map);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.FixedHashMap\",\n                  \"[DataStructures][Unit]\") {\n  test_direction_key<4>();\n  test_repeated_key<size_t>();\n  test_repeated_key<NonCopyableSizeT>();\n  test_equality();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_FloatingPointType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <FloatingPointType FloatType>\nvoid test_construct_from_options() {\n  const auto created =\n      TestHelpers::test_creation<FloatingPointType>(get_output(FloatType));\n  CHECK(created == FloatType);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.FloatingPointType\",\n                  \"[Unit][DataStructures]\") {\n  CHECK(get_output(FloatingPointType::Float) == \"Float\");\n  CHECK(get_output(FloatingPointType::Double) == \"Double\");\n\n  test_construct_from_options<FloatingPointType::Float>();\n  test_construct_from_options<FloatingPointType::Double>();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_IdPair.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/IdPair.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.IdPair\", \"[Unit][DataStructures]\") {\n  auto id_pair = make_id_pair(5, std::vector<double>{1.2, 7.8});\n  static_assert(std::is_same_v<decltype(id_pair.id), int>,\n                \"Failed checking type decay in make_id_pair\");\n  static_assert(std::is_same_v<decltype(id_pair.data), std::vector<double>>,\n                \"Failed checking type decay in make_id_pair\");\n  CHECK(id_pair.id == 5);\n  CHECK(id_pair.data == std::vector<double>{1.2, 7.8});\n  CHECK(id_pair == make_id_pair(5, std::vector<double>{1.2, 7.8}));\n  CHECK(id_pair != make_id_pair(6, std::vector<double>{1.2, 7.8}));\n  CHECK(id_pair != make_id_pair(5, std::vector<double>{1.3, 7.8}));\n  CHECK(id_pair != make_id_pair(7, std::vector<double>{1.3, 7.8}));\n\n  // Test stream operator\n  CHECK(get_output(id_pair) == \"(5,(1.2,7.8))\");\n\n  // Test PUP\n  test_serialization(id_pair);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_Index.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <numeric>\n#include <string>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_collapsed_index(const Index<Dim> extents) {\n  for (IndexIterator<Dim> index_it(extents); index_it; ++index_it) {\n    CAPTURE(*index_it);\n    Index<Dim> index;\n    for (size_t d = 0; d < Dim; ++d) {\n      index[d] = (*index_it)[d];\n    }\n    // Compare the collapsed index of the IndexIterator to the collapsed index\n    // computed by the free function to make sure the two implementations are\n    // consistent.\n    CHECK(index_it.collapsed_index() == collapsed_index(index, extents));\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_expanded_index(const Index<Dim>& extents) {\n  // Check that we recover same index when we expand and then collapse.\n  const size_t total_points = extents.product();\n  for (size_t i = 0; i < total_points; ++i) {\n    const Index<Dim> expanded = expanded_index(i, extents);\n    const size_t collapsed = collapsed_index(expanded, extents);\n    CHECK(collapsed == i);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Index\", \"[DataStructures][Unit]\") {\n  Index<0> index_0d;\n  CHECK(index_0d.product() == 1);\n  CHECK(index_0d.size() == 0);\n  CHECK(index_0d.indices() == (std::array<size_t, 0>{}));\n  Index<1> index_1d(3);\n  CHECK(index_1d.product() == 3);\n  CHECK(index_1d.indices() == (std::array<size_t, 1>{{3}}));\n  CHECK(index_1d[0] == 3);\n  CHECK(index_1d.size() == 1);\n  CHECK(get_output(index_1d) == \"(3)\");\n  const Index<2> index_2d(4);\n  CHECK(index_2d.product() == 16);\n  CHECK(index_2d.indices() == (std::array<size_t, 2>{{4,4}}));\n  CHECK(index_2d[0] == 4);\n  CHECK(index_2d[1] == 4);\n  CHECK(index_2d.slice_away(0)[0] == 4);\n  CHECK(index_2d.slice_away(1)[0] == 4);\n  CHECK(index_2d.size() == 2);\n  // clang-tidy: do not use pointer arithmetic\n  CHECK(index_2d.data()[0] == 4);  // NOLINT\n  CHECK(index_2d.data()[1] == 4);  // NOLINT\n  CHECK(get_output(index_2d) == \"(4,4)\");\n  Index<3> index_3d(1, 2, 3);\n  CHECK(index_3d.size() == 3);\n  CHECK(index_3d.product() == 6);\n  CHECK(index_3d.indices() == (std::array<size_t, 3>{{1, 2, 3}}));\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(index_3d[i] == i + 1);\n    // clang-tidy: do not use pointer arithmetic\n    CHECK(index_3d.data()[i] == i + 1);  // NOLINT\n  }\n  CHECK(index_3d.slice_away(0)[0] == 2);\n  CHECK(index_3d.slice_away(0)[1] == 3);\n  CHECK(index_3d.slice_away(0).product() == 6);\n  CHECK(index_3d.slice_away(1)[0] == 1);\n  CHECK(index_3d.slice_away(1)[1] == 3);\n  CHECK(index_3d.slice_away(1).product() == 3);\n  CHECK(index_3d.slice_away(2)[0] == 1);\n  CHECK(index_3d.slice_away(2)[1] == 2);\n  CHECK(index_3d.slice_away(2).product() == 2);\n\n  // Check iterator\n  CHECK(6 == std::accumulate(index_3d.begin(), index_3d.end(), 0_st));\n\n  // Check serialization\n  test_serialization(index_0d);\n  test_serialization(index_1d);\n  test_serialization(index_2d);\n  test_serialization(index_3d);\n\n  // Test inequivalence operator\n  CHECK(index_3d != Index<3>(2, 4, 9));\n\n  CHECK(get_output(index_3d) == \"(1,2,3)\");\n\n  test_copy_semantics(index_3d);\n  auto index_3d_copy = index_3d;\n  // clang-tidy: std::move of index_3d (trivial) does nothing\n  test_move_semantics(std::move(index_3d), index_3d_copy);  // NOLINT\n\n  // Test indexing into an Index\n  const Index<1> one_d_index{3};\n  const Index<1> one_d_index_with{2};\n  CHECK(collapsed_index(one_d_index_with, one_d_index) == 2);\n  const Index<2> two_d_index{3, 4};\n  const Index<2> two_d_index_with{1, 2};\n  CHECK(collapsed_index(two_d_index_with, two_d_index) == 7);\n  const Index<3> three_d_index{3, 4, 2};\n  const Index<3> three_d_index_with{2, 3, 1};\n  CHECK(collapsed_index(three_d_index_with, three_d_index) == 23);\n  const Index<3> extents_for_iterator{5, 6, 2};\n  for (IndexIterator<3> ii{extents_for_iterator}; ii; ++ii) {\n    CHECK(collapsed_index(*ii, extents_for_iterator) == ii.collapsed_index());\n  }\n  test_collapsed_index(extents_for_iterator);\n\n  // Test expanded_index\n  CHECK(expanded_index(0, Index<1>{3}) == Index<1>{0});\n  CHECK(expanded_index(2, Index<1>{3}) == Index<1>{2});\n  CHECK(expanded_index(7, Index<2>{3, 4}) == Index<2>{1, 2});\n  CHECK(expanded_index(23, Index<3>{3, 4, 2}) == Index<3>{2, 3, 1});\n  test_expanded_index(extents_for_iterator);\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH((collapsed_index(Index<3>{4, 4, 4}, Index<3>{2, 3, 4})),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The requested index in the dimension\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_IndexIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.IndexIterator\",\n                  \"[DataStructures][Unit]\") {\n  // [index_iterator_example]\n  Index<3> elements(1, 2, 3);\n  for (IndexIterator<3> index_it(elements); index_it; ++index_it) {\n    // Use the index iterator to do something super awesome\n    CHECK(Index<3>(-1, -1, -1) != *index_it);\n  }\n  // [index_iterator_example]\n\n  IndexIterator<3> index_iterator(elements);\n  auto check_next =\n      [&index_iterator,\n       call_num =\n           0](const Index<3>& expected) mutable {  // NOLINT(spectre-mutable)\n        CHECK(index_iterator);\n        CHECK(index_iterator() == expected);\n        CHECK(*index_iterator == expected);\n        CHECK(index_iterator->indices() == expected.indices());\n        CHECK(index_iterator.collapsed_index() ==\n              static_cast<size_t>(call_num));\n        ++index_iterator;\n        ++call_num;\n      };\n  check_next(Index<3>(0, 0, 0));\n  check_next(Index<3>(0, 1, 0));\n  check_next(Index<3>(0, 0, 1));\n  check_next(Index<3>(0, 1, 1));\n  check_next(Index<3>(0, 0, 2));\n  check_next(Index<3>(0, 1, 2));\n  CHECK(not index_iterator);\n\n  // Test 0D IndexIterator\n  IndexIterator<0> index_iterator_0d(Index<0>{});\n  CHECK(index_iterator_0d);\n  CHECK_FALSE(++index_iterator_0d);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_LeviCivitaIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.LeviCivitaIterator\",\n                  \"[DataStructures][Unit]\") {\n  // Test 1D\n  std::array<int, 1> signs_1d = {{1}};\n  std::array<std::array<size_t, 1>, 1> indexes_1d = {\n      {std::array<size_t, 1>{{0}}}};\n\n  size_t i = 0;\n  for (LeviCivitaIterator<1> it; it; ++it) {\n    CHECK(it() == gsl::at(indexes_1d, i));\n    CHECK(it.sign() == gsl::at(signs_1d, i));\n    ++i;\n  }\n  CHECK(i == 1);\n\n  // Test 2D\n  std::array<int, 2> signs_2d = {{1, -1}};\n  std::array<std::array<size_t, 2>, 2> indexes_2d = {\n      {std::array<size_t, 2>{{0, 1}}, std::array<size_t, 2>{{1, 0}}}};\n\n  i = 0;\n  for (LeviCivitaIterator<2> it; it; ++it) {\n    CHECK(it() == gsl::at(indexes_2d, i));\n    CHECK(it.sign() == gsl::at(signs_2d, i));\n    ++i;\n  }\n  CHECK(i == 2);\n\n  // Test 3D\n  std::array<int, 6> signs_3d = {{1, -1, -1, 1, 1, -1}};\n  std::array<std::array<size_t, 3>, 6> indexes_3d = {\n      {std::array<size_t, 3>{{0, 1, 2}}, std::array<size_t, 3>{{0, 2, 1}},\n       std::array<size_t, 3>{{1, 0, 2}}, std::array<size_t, 3>{{1, 2, 0}},\n       std::array<size_t, 3>{{2, 0, 1}}, std::array<size_t, 3>{{2, 1, 0}}}};\n\n  i = 0;\n  for (LeviCivitaIterator<3> it; it; ++it) {\n    CHECK(it() == gsl::at(indexes_3d, i));\n    CHECK(it.sign() == gsl::at(signs_3d, i));\n    ++i;\n  }\n  CHECK(i == 6);\n\n  // Test 4D\n  std::array<int, 24> signs_4d = {{1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1,\n                                   1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1}};\n  std::array<std::array<size_t, 4>, 24> indexes_4d = {\n      {std::array<size_t, 4>{{0, 1, 2, 3}},\n       std::array<size_t, 4>{{0, 1, 3, 2}},\n       std::array<size_t, 4>{{0, 2, 1, 3}},\n       std::array<size_t, 4>{{0, 2, 3, 1}},\n       std::array<size_t, 4>{{0, 3, 1, 2}},\n       std::array<size_t, 4>{{0, 3, 2, 1}},\n       std::array<size_t, 4>{{1, 0, 2, 3}},\n       std::array<size_t, 4>{{1, 0, 3, 2}},\n       std::array<size_t, 4>{{1, 2, 0, 3}},\n       std::array<size_t, 4>{{1, 2, 3, 0}},\n       std::array<size_t, 4>{{1, 3, 0, 2}},\n       std::array<size_t, 4>{{1, 3, 2, 0}},\n       std::array<size_t, 4>{{2, 0, 1, 3}},\n       std::array<size_t, 4>{{2, 0, 3, 1}},\n       std::array<size_t, 4>{{2, 1, 0, 3}},\n       std::array<size_t, 4>{{2, 1, 3, 0}},\n       std::array<size_t, 4>{{2, 3, 0, 1}},\n       std::array<size_t, 4>{{2, 3, 1, 0}},\n       std::array<size_t, 4>{{3, 0, 1, 2}},\n       std::array<size_t, 4>{{3, 0, 2, 1}},\n       std::array<size_t, 4>{{3, 1, 0, 2}},\n       std::array<size_t, 4>{{3, 1, 2, 0}},\n       std::array<size_t, 4>{{3, 2, 0, 1}},\n       std::array<size_t, 4>{{3, 2, 1, 0}}}};\n\n  i = 0;\n  for (LeviCivitaIterator<4> it; it; ++it) {\n    CHECK(it() == gsl::at(indexes_4d, i));\n    CHECK(it.sign() == gsl::at(signs_4d, i));\n    ++i;\n  }\n  CHECK(i == 24);\n\n  // Demonstrate using the iterator to compute a cross product\n  // [levi_civita_iterator_example]\n  const std::array<double, 3> vector_a = {{2.0, 3.0, 4.0}};\n  const std::array<double, 3> vector_b = {{7.0, 6.0, 5.0}};\n  const std::array<double, 3> a_cross_b_expected = {{-9.0, 18.0, -9.0}};\n\n  std::array<double, 3> a_cross_b = {{0.0, 0.0, 0.0}};\n  for (LeviCivitaIterator<3> it; it; ++it) {\n    gsl::at(a_cross_b, it[0]) +=\n        it.sign() * gsl::at(vector_a, it[1]) * gsl::at(vector_b, it[2]);\n  }\n  CHECK_ITERABLE_APPROX(a_cross_b, a_cross_b_expected);\n  // [levi_civita_iterator_example]\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_LinkedMessageId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <map>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace {\nvoid test_id() {\n  const LinkedMessageId<int> id_one_nothing{1, {}};\n  const LinkedMessageId<int> id_one_two{1, 2};\n  const LinkedMessageId<int> id_two_nothing{2, {}};\n  CHECK(id_one_nothing == id_one_nothing);\n  CHECK_FALSE(id_one_nothing != id_one_nothing);\n  CHECK_FALSE(id_one_nothing == id_one_two);\n  CHECK(id_one_nothing != id_one_two);\n  CHECK_FALSE(id_one_nothing == id_two_nothing);\n  CHECK(id_one_nothing != id_two_nothing);\n  CHECK(get_output(id_one_two) == \"1 (2)\");\n\n  const LinkedMessageId<double> time_and_previous{0.123, 0.1012};\n  test_serialization(time_and_previous);\n  const LinkedMessageId<double> same{0.123, 0.1012};\n\n  CHECK(time_and_previous == same);\n  CHECK_FALSE(time_and_previous != same);\n  const LinkedMessageId<double> not_same{0.124, 0.101};\n  CHECK(time_and_previous != not_same);\n  CHECK_FALSE(time_and_previous == not_same);\n\n  std::string string_of_object = MakeString{} << time_and_previous;\n  std::string expected_string_of_object = MakeString{} << 0.123 << \" (\"\n                                                       << 0.1012 << \")\";\n  CHECK(string_of_object == expected_string_of_object);\n\n  LinkedMessageIdLessComparator<double> comparator{};\n  CHECK(comparator(time_and_previous, not_same));\n  CHECK_FALSE(comparator(not_same, time_and_previous));\n  CHECK_FALSE(comparator(time_and_previous, same));\n  CHECK_FALSE(comparator(same, time_and_previous));\n  CHECK(comparator(time_and_previous, 0.2));\n  CHECK_FALSE(comparator(0.2, time_and_previous));\n  CHECK(comparator(0.1, time_and_previous));\n  CHECK_FALSE(comparator(time_and_previous, 0.1));\n\n  // check that we can use LinkedMessageId<double> as a map key\n  std::map<LinkedMessageId<double>, double,\n           LinkedMessageIdLessComparator<double>>\n      test_map{};\n  test_map.insert({LinkedMessageId<double>{0.123, 0.120}, 0.5});\n  test_map.insert({LinkedMessageId<double>{0.120, 0.115}, 0.4});\n  CHECK(test_map.begin()->first == LinkedMessageId<double>{0.120, 0.115});\n  CHECK(test_map[LinkedMessageId<double>{0.123, 0.120}] == 0.5);\n\n  // check that we can use LinkedMessageId<double> as an unordered_map key\n  std::unordered_map<LinkedMessageId<double>, double> test_unordered_map{};\n  test_unordered_map.insert({LinkedMessageId<double>{0.123, 0.120}, 0.5});\n  test_unordered_map.insert({LinkedMessageId<double>{0.120, 0.115}, 0.4});\n  CHECK(test_unordered_map[LinkedMessageId<double>{0.120, 0.115}] == 0.4);\n  CHECK(test_map[LinkedMessageId<double>{0.123, 0.120}] == 0.5);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.LinkedMessageId\",\n                  \"[Unit][DataStructures]\") {\n  test_id();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_LinkedMessageQueue.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Label1;\nstruct Label2;\n\n// Can't use \"Queue\" because Charm defines a type with that name.\ntemplate <typename Label>\nstruct MyQueue {\n  // Non-copyable\n  using type = std::unique_ptr<double>;\n};\n\nvoid test_queue() {\n  LinkedMessageQueue<int, tmpl::list<MyQueue<Label1>, MyQueue<Label2>>> queue{};\n  CHECK(not queue.next_ready_id().has_value());\n\n  queue.insert<MyQueue<Label1>, MyQueue<Label2>>(\n      {1, {}}, std::make_unique<double>(1.1), std::make_unique<double>(-1.1));\n\n  CHECK(queue.next_ready_id() == std::optional{1});\n  {\n    const auto out = queue.extract();\n    CHECK(*tuples::get<MyQueue<Label1>>(out) == 1.1);\n    CHECK(*tuples::get<MyQueue<Label2>>(out) == -1.1);\n  }\n\n  queue.insert<MyQueue<Label2>>({3, {1}}, std::make_unique<double>(-3.3));\n  CHECK(not queue.next_ready_id().has_value());\n  queue.insert<MyQueue<Label2>>({2, {3}}, std::make_unique<double>(-2.2));\n  CHECK(not queue.next_ready_id().has_value());\n  queue.insert<MyQueue<Label1>>({2, {3}}, std::make_unique<double>(2.2));\n\n  const auto finish_checks = [](decltype(queue) test_queue) {\n    CHECK(not test_queue.next_ready_id().has_value());\n    test_queue.insert<MyQueue<Label1>>({3, {1}}, std::make_unique<double>(3.3));\n\n    CHECK(test_queue.next_ready_id() == std::optional{3});\n    {\n      const auto out = test_queue.extract();\n      CHECK(*tuples::get<MyQueue<Label1>>(out) == 3.3);\n      CHECK(*tuples::get<MyQueue<Label2>>(out) == -3.3);\n    }\n\n    CHECK(test_queue.next_ready_id() == std::optional{2});\n    {\n      const auto out = test_queue.extract();\n      CHECK(*tuples::get<MyQueue<Label1>>(out) == 2.2);\n      CHECK(*tuples::get<MyQueue<Label2>>(out) == -2.2);\n    }\n  };\n  finish_checks(serialize_and_deserialize(queue));\n  finish_checks(std::move(queue));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.LinkedMessageQueue\",\n                  \"[Unit][DataStructures]\") {\n  test_queue();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        LinkedMessageQueue<int, tmpl::list<MyQueue<Label1>, MyQueue<Label2>>>\n            queue{};\n        queue.insert<MyQueue<Label1>>({1, {}}, std::make_unique<double>(1.1));\n        queue.insert<MyQueue<Label1>>({1, {}}, std::make_unique<double>(1.1));\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Received duplicate messages at id 1 and \"\n          \"previous id --.\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        LinkedMessageQueue<int, tmpl::list<MyQueue<Label1>, MyQueue<Label2>>>\n            queue{};\n        queue.insert<MyQueue<Label1>>({1, {}}, std::make_unique<double>(1.1));\n        queue.insert<MyQueue<Label2>>({2, {}}, std::make_unique<double>(1.1));\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Received messages with different ids (1 and \"\n          \"2) but the same previous id (--).\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        LinkedMessageQueue<int, tmpl::list<MyQueue<Label1>, MyQueue<Label2>>>\n            queue{};\n        queue.insert<MyQueue<Label1>>({1, {}}, std::make_unique<double>(1.1));\n        queue.extract();\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot extract before all messages have been received.\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_MathWrapper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"Helpers/DataStructures/MathWrapper.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename T>\nvoid test_into_math_wrapper_type_scalar(T value) {\n  const auto copy = value;\n  auto type_erased = into_math_wrapper_type(std::move(value));\n  static_assert(std::is_same_v<decltype(type_erased), math_wrapper_type<T>>);\n  CHECK(type_erased == copy);\n}\n\ntemplate <typename T>\nvoid test_into_math_wrapper_type_vector(T value) {\n  const auto* const data = value.data();\n  auto type_erased = into_math_wrapper_type(std::move(value));\n  static_assert(std::is_same_v<decltype(type_erased), math_wrapper_type<T>>);\n  CHECK(type_erased.data() == data);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.MathWrapper\", \"[Unit][Utilities]\") {\n  TestHelpers::MathWrapper::test_type<double>(1.0, 2.0, 3.0);\n  TestHelpers::MathWrapper::test_type<std::complex<double>>(\n      {1.0, 2.0}, {3.0, 4.0}, std::complex<double>{5.0, 6.0});\n  TestHelpers::MathWrapper::test_type<DataVector>({1.0, 2.0}, {3.0, 4.0}, 5.0);\n  TestHelpers::MathWrapper::test_type<ComplexDataVector>(\n      {std::complex<double>{1.0, 2.0}, std::complex<double>{3.0, 4.0}},\n      {std::complex<double>{5.0, 6.0}, std::complex<double>{7.0, 8.0}},\n      std::complex<double>{9.0, 10.0});\n  TestHelpers::MathWrapper::test_type<std::array<double, 2>>({1.0, 2.0},\n                                                             {3.0, 4.0}, 5.0);\n\n  // [MathWrapper]\n  double mutable_double = 1.0;\n  const double const_double = 2.0;\n  const auto mutable_wrapper = make_math_wrapper(&mutable_double);\n  const auto const_wrapper = make_math_wrapper(const_double);\n  *mutable_wrapper += *const_wrapper;\n  CHECK(mutable_double == 3.0);\n  // [MathWrapper]\n\n  test_into_math_wrapper_type_scalar(3.4);\n  test_into_math_wrapper_type_scalar(std::complex{3.4, 5.6});\n  test_into_math_wrapper_type_vector(DataVector{1.2, 3.4, 5.6});\n  test_into_math_wrapper_type_vector(\n      ComplexDataVector{std::complex{1.2, 3.4}, std::complex{5.6, 7.8}});\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_Matrix.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport math\nimport unittest\n\nimport numpy as np\n\nfrom spectre.DataStructures import Matrix\n\n\nclass TestMatrix(unittest.TestCase):\n    def test_shape(self):\n        M = Matrix(3, 5)\n        self.assertEqual(M.shape, (3, 5))\n\n    def test_getitem(self):\n        M = Matrix(3, 4)\n        M[1, 2] = 3\n        M[0, 0] = 0\n        M[2, 1] = 1\n        self.assertEqual(M[1, 2], 3)\n        self.assertEqual(M[0, 0], 0)\n        self.assertEqual(M[2, 1], 1)\n\n    def test_string(self):\n        M = Matrix(2, 2)\n        M[0, 0] = 1\n        M[0, 1] = 2\n        M[1, 0] = 3\n        M[1, 1] = 4\n        self.assertEqual(\n            str(M),\n            \"(            1            2 )\\n(            3            4 )\\n\",\n        )\n\n    def test_bounds_check(self):\n        a = Matrix(2, 2)\n        self.assertRaises(RuntimeError, lambda: a[5, 2])\n\n        def assignment_test():\n            a[5, 2] = 8\n\n        self.assertRaises(RuntimeError, assignment_test)\n\n    def test_numpy_interoperability(self):\n        M = Matrix(2, 2)\n        M[0, 0] = 1.0\n        M[1, 0] = 2.7\n        M[0, 1] = 5.42\n        M[1, 1] = -2.3\n        # Convert Matrix to Numpy array\n        A = np.array(M)\n        self.assertTrue(isinstance(A, np.ndarray))\n        self.assertEqual(A[0, 0], 1.0)\n        self.assertEqual(A[1, 0], 2.7)\n        self.assertEqual(A[0, 1], 5.42)\n        self.assertEqual(A[1, 1], -2.3)\n        # Convert Numpy array to Matrix\n        M_from_array = Matrix(A)\n        self.assertEqual(M_from_array[0, 0], 1.0)\n        self.assertEqual(M_from_array[1, 0], 2.7)\n        self.assertEqual(M_from_array[0, 1], 5.42)\n        self.assertEqual(M_from_array[1, 1], -2.3)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ModalVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <tuple>\n\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_modal_vector_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n\n  const auto unary_ops = std::make_tuple(\n      std::make_tuple(funcl::Abs<>{}, std::make_tuple(generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, ModalVector>(unary_ops);\n\n  const auto binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, ModalVector>(binary_ops);\n\n  const auto cascaded_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, ModalVector>(cascaded_ops);\n\n  const auto array_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, std::array<ModalVector, 2>>(\n      array_binary_ops);\n\n  // Note that the binary operations that involve a modal vector and a double\n  // have been moved to `Test_ModalVectorInhomogeneousOperations.cpp` in an\n  // effort to better parallelize the build.\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.ModalVector\", \"[DataStructures][Unit]\") {\n  {\n    INFO(\"test construct and assign\");\n    TestHelpers::VectorImpl::vector_test_construct_and_assign<ModalVector,\n                                                              double>();\n  }\n  {\n    INFO(\"test serialize and deserialize\");\n    TestHelpers::VectorImpl::vector_test_serialize<ModalVector, double>();\n  }\n  {\n    INFO(\"test set_data_ref functionality\");\n    TestHelpers::VectorImpl::vector_test_ref<ModalVector, double>();\n  }\n  {\n    INFO(\"test math after move\");\n    TestHelpers::VectorImpl::vector_test_math_after_move<ModalVector, double>();\n  }\n  {\n    INFO(\"test ModalVector math operations\");\n    test_modal_vector_math();\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ModalVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::ExpressionAssign),\n      Catch::Matchers::ContainsSubstring(\"Must assign into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ModalVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Copy),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::VectorImpl::vector_ref_test_size_error<ModalVector>(\n          TestHelpers::VectorImpl::RefSizeErrorTestKind::Move),\n      Catch::Matchers::ContainsSubstring(\"Must copy into same size\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ModalVector.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport math\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import ModalVector\n\n\nclass TestModalVector(unittest.TestCase):\n    def test_len(self):\n        a = ModalVector(5, 6.7)\n        self.assertEqual(len(a), 5)\n\n    def test_math_double(self):\n        b = ModalVector([7.2, -3.9])\n\n        self.assertEqual((2.0 * b)[0], 14.4)\n        self.assertEqual((2.0 * b)[1], -7.8)\n        self.assertEqual((b * 2.0)[0], 14.4)\n        self.assertEqual((b * 2.0)[1], -7.8)\n\n        self.assertEqual((b / 2.0)[0], 3.6)\n        self.assertEqual((b / 2.0)[1], -1.95)\n\n    def test_math_modalvector(self):\n        a = ModalVector(5, 6.7)\n        b = ModalVector(5, 8.7)\n        self.assertEqual(a + b, ModalVector(5, 6.7 + 8.7))\n        self.assertEqual(a - b, ModalVector(5, 6.7 - 8.7))\n\n        c = ModalVector([1.5, 2.5])\n        d = ModalVector([3.0, 7.5])\n        self.assertEqual((c + d)[0], 4.5)\n        self.assertEqual((c + d)[1], 10.0)\n        self.assertEqual((c - d)[0], -1.5)\n        self.assertEqual((c - d)[1], -5.0)\n\n    def test_negate(self):\n        a = ModalVector(5, 6.7)\n        self.assertEqual(-a, ModalVector(5, -6.7))\n        b = ModalVector([1.5, 3.7])\n        self.assertEqual((-b)[0], -1.5)\n        self.assertEqual((-b)[1], -3.7)\n\n    def test_bounds_check(self):\n        a = ModalVector(5, 6.7)\n        self.assertRaises(RuntimeError, lambda: a[5])\n\n        def assignment_test():\n            a[5] = 8\n\n        self.assertRaises(RuntimeError, assignment_test)\n\n    def test_output(self):\n        a = ModalVector(5, 6.7)\n        self.assertEqual(str(a), \"(6.7,6.7,6.7,6.7,6.7)\")\n        b = ModalVector([3.8, 7.2])\n        self.assertEqual(str(b), \"(3.8,7.2)\")\n\n    def test_abs(self):\n        a = ModalVector(5, -6.0)\n        self.assertEqual(a.abs(), ModalVector(5, 6.0))\n        b = ModalVector([1.0, -2.0])\n        self.assertEqual(b.abs()[0], 1.0)\n        self.assertEqual(b.abs()[1], 2.0)\n\n    def test_numpy_compatibility(self):\n        b = np.array([1.0, 2.0, 3.0])\n        c = ModalVector([1.0, 2.0, 3.0])\n        self.assertTrue(((b + c) == np.array([2.0, 4.0, 6.0])).all())\n        x = np.linspace(0, 2 * np.pi, 10)\n        npt.assert_allclose(ModalVector(x).abs(), np.abs(x))\n        # Convert a ModalVector to a Numpy array\n        c_array_copy = np.array(c)\n        npt.assert_equal(c_array_copy, b)\n        # Changing the copy shouldn't change the ModalVector\n        c_array_copy[2] = 4.0\n        npt.assert_equal(c, b)\n        c_array_reference = np.array(c, copy=False)\n        npt.assert_equal(c_array_reference, b)\n        # Changing the reference should change the ModalVector as well\n        c_array_reference[2] = 4.0\n        self.assertEqual(c[2], 4.0)\n        # Convert a Numpy array to a ModalVector\n        b_dv_copy = ModalVector(b)\n        self.assertEqual(b_dv_copy, ModalVector([1.0, 2.0, 3.0]))\n        b_dv_copy[2] = 4.0\n        self.assertEqual(b[2], 3.0)\n        b_dv_reference = ModalVector(b, copy=False)\n        b_dv_reference[2] = 4.0\n        self.assertEqual(b[2], 4.0)\n\n    def test_iterator(self):\n        a = ModalVector([1.0, 2.0, 3.0, 4.0, 5.0])\n        for i, val in enumerate(a):\n            self.assertEqual(val, a[i])\n\n    def test_size_constructor(self):\n        a = ModalVector(3)\n        self.assertEqual(len(a), 3)\n\n    def test_list_constructor(self):\n        a = ModalVector([1.0, 2.0, 3.0, 4.0, 5.0])\n        self.assertEqual(len(a), 5)\n        self.assertEqual(a[3], 4.0)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_ModalVectorInhomogeneousOperations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is separated from `Test_ModalVector.cpp` in an effort to\n// parallelize the test builds.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <tuple>\n\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_modal_vector_inhomogeneous_binary_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-10.0, 10.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.1, 10.0}};\n\n  const auto just_double_with_modal_vector_ops =\n      std::make_tuple(std::make_tuple(funcl::Multiplies<>{},\n                                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly, double,\n      ModalVector>(just_double_with_modal_vector_ops);\n\n  const auto just_modal_vector_with_double_ops = std::make_tuple(\n      std::make_tuple(funcl::Multiplies<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Divides<>{}, std::make_tuple(generic, positive)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly, ModalVector,\n      double>(just_modal_vector_with_double_ops);\n\n  const auto just_modal_vector_with_modal_vector_ops =\n      std::make_tuple(std::make_tuple(funcl::MinusAssign<>{},\n                                      std::make_tuple(generic, generic)),\n                      std::make_tuple(funcl::PlusAssign<>{},\n                                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly, ModalVector,\n      ModalVector>(just_modal_vector_with_modal_vector_ops);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.ModalVector.InhomogeneousOperations\",\n                  \"[DataStructures][Unit]\") {\n  test_modal_vector_inhomogeneous_binary_math();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_MoreComplexDiagonalModalOperatorMath.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is separated from `Test_ComplexDiagonalModalOperator.cpp` in an\n// effort to parallelize the test builds.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <tuple>\n\n#include \"DataStructures/ComplexDiagonalModalOperator.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DiagonalModalOperator.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_additional_complex_diagonal_modal_operator_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n\n  const auto acting_on_modal_vector = std::make_tuple(std::make_tuple(\n      funcl::Multiplies<>{}, std::make_tuple(generic, generic)));\n\n  // the operation isn't really \"inplace\", but we carefully forbid the operation\n  // between two ModalVectors, which will be avoided in the inplace test case,\n  // which checks only combinations with the ComplexDiagonalModalOperator as the\n  // first argument.\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Inplace, ComplexDiagonalModalOperator,\n      ModalVector, ComplexModalVector>(acting_on_modal_vector);\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      DiagonalModalOperator, ComplexModalVector>(acting_on_modal_vector);\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      ComplexModalVector, DiagonalModalOperator>(acting_on_modal_vector);\n  // testing the other ordering\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly, ModalVector,\n      ComplexDiagonalModalOperator>(acting_on_modal_vector);\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      ComplexModalVector, ComplexDiagonalModalOperator>(acting_on_modal_vector);\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly,\n      ComplexModalVector, DiagonalModalOperator>(acting_on_modal_vector);\n\n  const auto cascaded_ops = std::make_tuple(\n      std::make_tuple(funcl::Multiplies<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)),\n      std::make_tuple(funcl::Minus<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, ComplexDiagonalModalOperator,\n      DiagonalModalOperator>(cascaded_ops);\n\n  const auto array_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict,\n      std::array<ComplexDiagonalModalOperator, 2>>(array_binary_ops);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.DataStructures.ComplexDiagonalModalOperator.AdditionalMath\",\n    \"[DataStructures][Unit]\") {\n  test_additional_complex_diagonal_modal_operator_math();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_MoreDiagonalModalOperatorMath.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file is separated from `Test_DiagonalModalOperator.cpp` in an effort to\n// parallelize the test builds.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <tuple>\n\n#include \"DataStructures/DiagonalModalOperator.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_additional_diagonal_modal_operator_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n\n  const auto acting_on_modal_vector = std::make_tuple(std::make_tuple(\n      funcl::Multiplies<>{}, std::make_tuple(generic, generic)));\n\n  // the operation isn't really \"inplace\", but we carefully forbid the operation\n  // between two ModalVectors, which will be avoided in the inplace test case,\n  // which checks only combinations with the DiagonalModalOperator as the first\n  // argument.\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Inplace, DiagonalModalOperator,\n      ModalVector>(acting_on_modal_vector);\n  // testing the other ordering\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::GivenOrderOfArgumentsOnly, ModalVector,\n      DiagonalModalOperator>(acting_on_modal_vector);\n\n  const auto cascaded_ops = std::make_tuple(\n      std::make_tuple(funcl::Multiplies<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)),\n      std::make_tuple(funcl::Minus<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, DiagonalModalOperator>(\n      cascaded_ops);\n\n  const auto array_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict,\n      std::array<DiagonalModalOperator, 2>>(array_binary_ops);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DiagonalModalOperator.AdditionalMath\",\n                  \"[DataStructures][Unit]\") {\n  test_additional_diagonal_modal_operator_math();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_NonZeroStaticSizeVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <complex>\n#include <tuple>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/CustomStaticSizeVector.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nvoid test_vector_unary_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-100.0, 100.0}};\n  const TestHelpers::VectorImpl::Bound mone_one{{-1.0, 1.0}};\n  const TestHelpers::VectorImpl::Bound gt_one{{1.0, 100.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.01, 100.0}};\n  const auto unary_ops = std::make_tuple(\n      std::make_tuple(funcl::Abs<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Acos<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Acosh<>{}, std::make_tuple(gt_one)),\n      std::make_tuple(funcl::Asin<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Asinh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Atan<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Atanh<>{}, std::make_tuple(mone_one)),\n      std::make_tuple(funcl::Cbrt<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Cos<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Cosh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Erf<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Exp<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Exp2<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Fabs<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::InvCbrt<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::InvSqrt<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Log<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Log10<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Log2<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Sin<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Sinh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::StepFunction<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Square<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Sqrt<>{}, std::make_tuple(positive)),\n      std::make_tuple(funcl::Tan<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::Tanh<>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<1>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<-2>{}, std::make_tuple(generic)),\n      std::make_tuple(funcl::UnaryPow<3>{}, std::make_tuple(generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, CustomStaticSizeVector>(\n      unary_ops);\n}\n\nvoid test_asserts() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        TestHelpers::VectorImpl::vector_ref_test_size_error<\n            CustomStaticSizeVector>(\n            TestHelpers::VectorImpl::RefSizeErrorTestKind::ExpressionAssign);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Must assign into same size\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      TestHelpers::VectorImpl::vector_ref_test_size_error<\n                          CustomStaticSizeVector>(\n                          TestHelpers::VectorImpl::RefSizeErrorTestKind::Copy);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Must copy into same size\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      TestHelpers::VectorImpl::vector_ref_test_size_error<\n                          CustomStaticSizeVector>(\n                          TestHelpers::VectorImpl::RefSizeErrorTestKind::Move);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Must copy into same size\"));\n#endif\n}\n\nvoid test_vector_multiple_operand_math() {\n  const TestHelpers::VectorImpl::Bound generic{{-10.0, 10.0}};\n  const TestHelpers::VectorImpl::Bound positive{{0.1, 10.0}};\n\n  const auto binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Divides<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Multiplies<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Normal, CustomStaticSizeVector>(\n      binary_ops);\n\n  const auto inplace_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::DivAssign<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::MinusAssign<>{},\n                      std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::MultAssign<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::PlusAssign<>{},\n                      std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Inplace, CustomStaticSizeVector>(\n      inplace_binary_ops);\n\n  const auto strict_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Atan2<>{}, std::make_tuple(generic, positive)),\n      std::make_tuple(funcl::Hypot<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, CustomStaticSizeVector>(\n      strict_binary_ops);\n\n  const auto cascaded_ops = std::make_tuple(\n      std::make_tuple(funcl::Multiplies<funcl::Plus<>, funcl::Identity>{},\n                      std::make_tuple(generic, generic, generic)),\n      std::make_tuple(funcl::Multiplies<funcl::Exp<>, funcl::Sin<>>{},\n                      std::make_tuple(generic, generic)),\n      std::make_tuple(\n          funcl::Plus<funcl::Atan<funcl::Tan<>>, funcl::Divides<>>{},\n          std::make_tuple(generic, generic, positive)),\n      // step function with multiplication should be tested due to resolved\n      // [issue 1122](https://github.com/sxs-collaboration/spectre/issues/1122)\n      std::make_tuple(funcl::StepFunction<\n                          funcl::Plus<funcl::Multiplies<>, funcl::Identity>>{},\n                      std::make_tuple(generic, generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict, CustomStaticSizeVector>(\n      cascaded_ops);\n\n  const auto array_binary_ops = std::make_tuple(\n      std::make_tuple(funcl::Minus<>{}, std::make_tuple(generic, generic)),\n      std::make_tuple(funcl::Plus<>{}, std::make_tuple(generic, generic)));\n\n  TestHelpers::VectorImpl::test_functions_with_vector_arguments<\n      TestHelpers::VectorImpl::TestKind::Strict,\n      std::array<CustomStaticSizeVector, 2>>(array_binary_ops);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.NonZeroStaticSizeVector\",\n                  \"[DataStructures][Unit]\") {\n  {\n    INFO(\"test construct and assign\");\n    TestHelpers::VectorImpl::vector_test_construct_and_assign<\n        CustomStaticSizeVector, double>();\n  }\n  {\n    INFO(\"test serialize and deserialize\");\n    TestHelpers::VectorImpl::vector_test_serialize<CustomStaticSizeVector,\n                                                   double>();\n  }\n  {\n    INFO(\"test set_data_ref functionality\");\n    TestHelpers::VectorImpl::vector_test_ref<CustomStaticSizeVector, double>();\n  }\n  {\n    INFO(\"test math after move\");\n    TestHelpers::VectorImpl::vector_test_math_after_move<CustomStaticSizeVector,\n                                                         double>();\n  }\n  {\n    INFO(\"test CustomStaticSizeVector math operations\");\n    test_vector_unary_math();\n  }\n  {\n    INFO(\"test asserts of CustomStaticSizeVectors\");\n    test_asserts();\n  }\n  {\n    INFO(\"test CustomStaticSizeVectors multiple operand math\");\n    test_vector_multiple_operand_math();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_SimpleSparseMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid test_simple_sparse_matrix() {\n  SimpleSparseMatrix A;\n\n  // Important that these are emplaced_back sorted by row.\n  // Otherwise we should use SparseMatrixFiller to fill the matrices.\n  std::vector<SparseMatrixElement> elements{\n      {0, 0, 4.0}, {0, 3, 3.0}, {1, 3, 6.0}, {1, 5, 1.0},\n      {2, 2, 7.0}, {4, 3, 3.0}, {4, 5, 5.0}};\n\n  A.fill(elements);\n\n  // Check that elements are filled correctly and that\n  // SimpleSparseMatrix indexing is working.\n  for (size_t row = 0; row < 5; ++row) {\n    for (size_t column = 0; column < 5; ++column) {\n      const auto it =\n          alg::find_if(elements, [row, column](const SparseMatrixElement& t) {\n            return t.row_dest == row and t.column_src == column;\n          });\n      if (it != elements.end()) {\n        CHECK(A(row, column) == it->value);\n      } else {\n        CHECK(A(row, column) == 0.0);\n      }\n    }\n  }\n\n  // Create vector x\n  const std::vector<double> x{2.0, 3.0, 4.0, 5.0, 6.0, 7.0};\n\n  // Create vector y = Ax\n  const std::vector<double> y{23.0, 37.0, 28.0, 0.0, 50.0};\n\n  std::vector<double> ytest(5, 0);\n  A.increment_multiply_on_right(make_not_null(&ytest), 0, 1, x, 0, 1);\n\n  for (size_t i = 0; i < ytest.size(); ++i) {\n    CHECK(y[i] == approx(ytest[i]));\n  }\n\n  // Now test offsets and strides\n  // xx is the same as x, except offset is 1 and stride is 2.\n  const std::vector<double> xx{1.0,  2.0, 4.0,  3.0, 9.0,  4.0,\n                               25.0, 5.0, 49.0, 6.0, 64.0, 7.0};\n  // yytest will be the same as ytest, except offset is 2 and stride is 3.\n  std::vector<double> yytest(17, 0);\n  A.increment_multiply_on_right(make_not_null(&yytest), 2, 3, xx, 1, 2);\n\n  for (size_t i = 0; i < y.size(); ++i) {\n    CHECK(y[i] == approx(yytest[2 + 3 * i]));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.SimpleSparseMatrix\",\n                  \"[DataStructures][Unit]\") {\n  test_simple_sparse_matrix();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_SliceIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid check_slice_iterator_helper(SliceIterator si) {\n  CHECK((si.slice_offset() == 0 and si.volume_offset() == 0 and si and ++si));\n  CHECK((si.slice_offset() == 1 and si.volume_offset() == 1 and si and ++si));\n  CHECK((si.slice_offset() == 2 and si.volume_offset() == 2 and si and ++si));\n  CHECK((si.slice_offset() == 3 and si.volume_offset() == 12 and si and ++si));\n  CHECK((si.slice_offset() == 4 and si.volume_offset() == 13 and si and ++si));\n  CHECK((si.slice_offset() == 5 and si.volume_offset() == 14 and si and ++si));\n  CHECK((si.slice_offset() == 6 and si.volume_offset() == 24 and si and ++si));\n  CHECK((si.slice_offset() == 7 and si.volume_offset() == 25 and si and ++si));\n  CHECK((si.slice_offset() == 8 and si.volume_offset() == 26 and si and ++si));\n  CHECK((si.slice_offset() == 9 and si.volume_offset() == 36 and si and ++si));\n  CHECK((si.slice_offset() == 10 and si.volume_offset() == 37 and si and ++si));\n  CHECK((si.slice_offset() == 11 and si.volume_offset() == 38 and si and ++si));\n  CHECK((si.slice_offset() == 12 and si.volume_offset() == 48 and si and ++si));\n  CHECK((si.slice_offset() == 13 and si.volume_offset() == 49 and si and ++si));\n  CHECK((si.slice_offset() == 14 and si.volume_offset() == 50 and si));\n  CHECK((not++si and not si));\n}\n\ntemplate <size_t Dim>\nvoid check_slice_and_volume_indices(const Index<Dim>& extents) {\n  const auto t = volume_and_slice_indices(extents);\n  const auto& indices = t.second;\n  for (size_t d = 0; d < Dim; ++d) {\n    size_t index = 0;\n    for (SliceIterator sli_lower(extents, d, 0),\n         sli_upper(extents, d, extents[d] - 1);\n         sli_lower and sli_upper;\n         (void)++sli_lower, (void)++sli_upper, (void)++index) {\n      CHECK(gsl::at(gsl::at(indices, d).first, index).first ==\n            sli_lower.volume_offset());\n      CHECK(gsl::at(gsl::at(indices, d).first, index).second ==\n            sli_lower.slice_offset());\n\n      CHECK(gsl::at(gsl::at(indices, d).second, index).first ==\n            sli_upper.volume_offset());\n      CHECK(gsl::at(gsl::at(indices, d).second, index).second ==\n            sli_upper.slice_offset());\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.SliceIterator\",\n                  \"[DataStructures][Unit]\") {\n  SECTION(\"SliceIterator class\") {\n    size_t i = 0;\n    for (SliceIterator si(Index<3>(3, 4, 5), 2, 0); si; ++si) {\n      CHECK(i == si.slice_offset());\n      CHECK(i == si.volume_offset());\n      i++;\n    }\n    SliceIterator slice_iter(Index<3>(3, 4, 5), 1, 0);\n    check_slice_iterator_helper(slice_iter);\n    slice_iter.reset();\n    check_slice_iterator_helper(slice_iter);\n    // Run the iterator only partially, then reset\n    ++slice_iter;\n    slice_iter.reset();\n    check_slice_iterator_helper(slice_iter);\n  }\n  SECTION(\"volume_and_slice_indices function\") {\n    check_slice_and_volume_indices(Index<1>{2});\n    check_slice_and_volume_indices(Index<2>{2, 2});\n    check_slice_and_volume_indices(Index<2>{5, 4});\n    check_slice_and_volume_indices(Index<2>{3, 4});\n    check_slice_and_volume_indices(Index<3>{2, 2, 2});\n    check_slice_and_volume_indices(Index<3>{6, 4, 3});\n    check_slice_and_volume_indices(Index<3>{6, 4, 5});\n    check_slice_and_volume_indices(Index<3>{3, 4, 5});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_SliceTensorToVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/SliceTensorToVariables.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\ntemplate <typename VectorType>\nvoid test_variables_slice() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{5, 10};\n\n  const size_t x_extents = sdist(gen);\n  const size_t y_extents = sdist(gen);\n  const size_t z_extents = sdist(gen);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>> vars{\n      x_extents * y_extents * z_extents};\n  const size_t tensor_size =\n      TestHelpers::Tags::Vector<VectorType>::type::size();\n  Index<3> extents(x_extents, y_extents, z_extents);\n\n  // Test data_on_slice function by using a predictable data set where each\n  // entry is assigned a value equal to its index\n  for (size_t s = 0; s < vars.size(); ++s) {\n    // clang-tidy: do not use pointer arithmetic\n    vars.data()[s] = s;  // NOLINT\n  }\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_x(y_extents * z_extents, 0.);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_y(x_extents * z_extents, 0.);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_z(x_extents * y_extents, 0.);\n  const size_t x_offset = sdist(gen) % x_extents;\n  const size_t y_offset = sdist(gen) % y_extents;\n  const size_t z_offset = sdist(gen) % z_extents;\n\n  for (size_t s = 0; s < expected_vars_sliced_in_x.size(); ++s) {\n    // clang-tidy: do not use pointer arithmetic\n    expected_vars_sliced_in_x.data()[s] = x_offset + s * x_extents;  // NOLINT\n  }\n  for (size_t i = 0; i < tensor_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t z = 0; z < z_extents; ++z) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_y\n            .data()[x + x_extents * (z + z_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y_offset + z * y_extents);\n      }\n    }\n  }\n  for (size_t i = 0; i < tensor_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t y = 0; y < y_extents; ++y) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_z\n            .data()[x + x_extents * (y + y_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y + y_extents * z_offset);\n      }\n    }\n  }\n\n  CHECK(data_on_slice<TestHelpers::Tags::Vector<VectorType>>(\n            extents, 0, x_offset,\n            get<TestHelpers::Tags::Vector<VectorType>>(vars)) ==\n        expected_vars_sliced_in_x);\n  CHECK(data_on_slice<TestHelpers::Tags::Vector<VectorType>>(\n            extents, 1, y_offset,\n            get<TestHelpers::Tags::Vector<VectorType>>(vars)) ==\n        expected_vars_sliced_in_y);\n  CHECK(data_on_slice<TestHelpers::Tags::Vector<VectorType>>(\n            extents, 2, z_offset,\n            get<TestHelpers::Tags::Vector<VectorType>>(vars)) ==\n        expected_vars_sliced_in_z);\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.SliceTensorToVariables\",\n                  \"[DataStructures][Unit]\") {\n  test_variables_slice<ComplexDataVector>();\n  test_variables_slice<ComplexModalVector>();\n  test_variables_slice<DataVector>();\n  test_variables_slice<ModalVector>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_SliceVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\nnamespace {\ntemplate <typename VectorType>\nvoid test_variables_slice() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{5, 10};\n\n  const size_t x_extents = sdist(gen);\n  const size_t y_extents = sdist(gen);\n  const size_t z_extents = sdist(gen);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>> vars{\n      x_extents * y_extents * z_extents};\n  const size_t tensor_size =\n      TestHelpers::Tags::Vector<VectorType>::type::size();\n  Index<3> extents(x_extents, y_extents, z_extents);\n\n  // Test data_on_slice function by using a predictable data set where each\n  // entry is assigned a value equal to its index\n  for (size_t s = 0; s < vars.size(); ++s) {\n    // clang-tidy: do not use pointer arithmetic\n    vars.data()[s] = s;  // NOLINT\n  }\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_x(y_extents * z_extents, 0.);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_y(x_extents * z_extents, 0.);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>\n      expected_vars_sliced_in_z(x_extents * y_extents, 0.);\n  const size_t x_offset = sdist(gen) % x_extents;\n  const size_t y_offset = sdist(gen) % y_extents;\n  const size_t z_offset = sdist(gen) % z_extents;\n\n  for (size_t s = 0; s < expected_vars_sliced_in_x.size(); ++s) {\n    // clang-tidy: do not use pointer arithmetic\n    expected_vars_sliced_in_x.data()[s] = x_offset + s * x_extents;  // NOLINT\n  }\n  for (size_t i = 0; i < tensor_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t z = 0; z < z_extents; ++z) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_y\n            .data()[x + x_extents * (z + z_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y_offset + z * y_extents);\n      }\n    }\n  }\n  for (size_t i = 0; i < tensor_size; ++i) {\n    for (size_t x = 0; x < x_extents; ++x) {\n      for (size_t y = 0; y < y_extents; ++y) {\n        // clang-tidy: do not use pointer arithmetic\n        expected_vars_sliced_in_z\n            .data()[x + x_extents * (y + y_extents * i)] =  // NOLINT\n            i * extents.product() + x + x_extents * (y + y_extents * z_offset);\n      }\n    }\n  }\n\n  CHECK(data_on_slice(vars, extents, 0, x_offset) == expected_vars_sliced_in_x);\n  CHECK(data_on_slice(vars, extents, 1, y_offset) == expected_vars_sliced_in_y);\n  CHECK(data_on_slice(vars, extents, 2, z_offset) == expected_vars_sliced_in_z);\n}\n\ntemplate <typename VectorType>\nvoid test_variables_add_slice_to_data() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<\n      tt::get_fundamental_type_t<typename VectorType::value_type>>\n      dist{-100.0, 100.0};\n\n  // Test adding two slices on different 'axes' to a Variables\n  std::array<VectorType, 3> orig_vals;\n  std::fill(orig_vals.begin(), orig_vals.end(), VectorType{8});\n  fill_with_random_values(make_not_null(&orig_vals), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  std::array<VectorType, 3> slice0_vals;\n  std::fill(slice0_vals.begin(), slice0_vals.end(), VectorType{4});\n  fill_with_random_values(make_not_null(&slice0_vals), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  std::array<VectorType, 3> slice1_vals;\n  std::fill(slice1_vals.begin(), slice1_vals.end(), VectorType{2});\n  fill_with_random_values(make_not_null(&slice1_vals), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  using Tensor = typename TestHelpers::Tags::Vector<VectorType>::type;\n  const Index<2> extents{{{4, 2}}};\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>> vars(\n      extents.product());\n  get<TestHelpers::Tags::Vector<VectorType>>(vars) =\n      Tensor{{{orig_vals[0], orig_vals[1], orig_vals[2]}}};\n  {\n    const auto slice_extents = extents.slice_away(0);\n    Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>> slice(\n        slice_extents.product(), 0.);\n    get<TestHelpers::Tags::Vector<VectorType>>(slice) =\n        Tensor{{{slice1_vals[0], slice1_vals[1], slice1_vals[2]}}};\n    add_slice_to_data(make_not_null(&vars), slice, extents, 0, 2);\n  }\n\n  {\n    const auto slice_extents = extents.slice_away(1);\n    Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>> slice(\n        slice_extents.product(), 0.);\n    get<TestHelpers::Tags::Vector<VectorType>>(slice) =\n        Tensor{{{slice0_vals[0], slice0_vals[1], slice0_vals[2]}}};\n    add_slice_to_data(make_not_null(&vars), slice, extents, 1, 1);\n  }\n\n  {\n    // Test using add_slice_to_data with prefixed Variables\n    const auto slice_extents = extents.slice_away(0);\n    Variables<tmpl::list<\n        TestHelpers::Tags::Prefix0<TestHelpers::Tags::Vector<VectorType>>>>\n        slice(slice_extents.product(), 0.);\n    get<TestHelpers::Tags::Prefix0<TestHelpers::Tags::Vector<VectorType>>>(\n        slice) = Tensor{{{slice1_vals[0], slice1_vals[1], slice1_vals[2]}}};\n    add_slice_to_data(make_not_null(&vars), slice, extents, 0, 2);\n  }\n\n  // The slice0_vals should have been added twice to the second half of each of\n  // the three vectors in the tensor. The slice1_vals should have been added to\n  // entries 2 and 6 in each vector.\n  // clang-format off\n  const Tensor expected{\n      {{{orig_vals[0].at(0),\n         orig_vals[0].at(1),\n         orig_vals[0].at(2) + 2. * slice1_vals[0].at(0),\n         orig_vals[0].at(3),\n         orig_vals[0].at(4) + slice0_vals[0].at(0),\n         orig_vals[0].at(5) + slice0_vals[0].at(1),\n         orig_vals[0].at(6) + slice0_vals[0].at(2) + 2. * slice1_vals[0].at(1),\n         orig_vals[0].at(7) + slice0_vals[0].at(3)},\n        {orig_vals[1].at(0),\n         orig_vals[1].at(1),\n         orig_vals[1].at(2) + 2. * slice1_vals[1].at(0),\n         orig_vals[1].at(3),\n         orig_vals[1].at(4) + slice0_vals[1].at(0),\n         orig_vals[1].at(5) + slice0_vals[1].at(1),\n         orig_vals[1].at(6) + slice0_vals[1].at(2) + 2. * slice1_vals[1].at(1),\n         orig_vals[1].at(7) + slice0_vals[1].at(3)},\n        {orig_vals[2].at(0),\n         orig_vals[2].at(1),\n         orig_vals[2].at(2) + 2. * slice1_vals[2].at(0),\n         orig_vals[2].at(3),\n         orig_vals[2].at(4) + slice0_vals[2].at(0),\n         orig_vals[2].at(5) + slice0_vals[2].at(1),\n         orig_vals[2].at(6) + slice0_vals[2].at(2) + 2. * slice1_vals[2].at(1),\n         orig_vals[2].at(7) + slice0_vals[2].at(3)}}}};\n  // clang-format on\n\n  CHECK_ITERABLE_APPROX(expected,\n                        get<TestHelpers::Tags::Vector<VectorType>>(vars));\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.SliceVariables\",\n                  \"[DataStructures][Unit]\") {\n  {\n    INFO(\"Test Variables slice utilities\");\n    test_variables_slice<ComplexDataVector>();\n    test_variables_slice<ComplexModalVector>();\n    test_variables_slice<DataVector>();\n    test_variables_slice<ModalVector>();\n  }\n  {\n    INFO(\"Test adding slice values to Variables\");\n    test_variables_add_slice_to_data<ComplexDataVector>();\n    test_variables_add_slice_to_data<ComplexModalVector>();\n    test_variables_add_slice_to_data<DataVector>();\n    test_variables_add_slice_to_data<ModalVector>();\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        Variables<tmpl::list<TestHelpers::Tags::Vector<DataVector>>> vars(10,\n                                                                          0.);\n        const Variables<tmpl::list<TestHelpers::Tags::Vector<DataVector>>>\n            slice(2, 0.);\n        add_slice_to_data(make_not_null(&vars), slice, Index<2>{{{4, 2}}}, 0,\n                          0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"volume_vars has wrong number of grid points.  Expected 8, got 10\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        Variables<tmpl::list<TestHelpers::Tags::Vector<DataVector>>> vars(8,\n                                                                          0.);\n        const Variables<tmpl::list<TestHelpers::Tags::Vector<DataVector>>>\n            slice(5, 0.);\n        add_slice_to_data(make_not_null(&vars), slice, Index<2>{{{4, 2}}}, 0,\n                          0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"vars_on_slice has wrong number of grid points.  Expected 2, got 5\"));\n#endif\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_SparseMatrixFiller.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <blaze/math/CompressedMatrix.h>\n#include <cstddef>\n\n#include \"DataStructures/SparseMatrixFiller.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <typename SparseMatrixType>\nvoid test_sparse_matrix_filler(const bool use_map_method) {\n  const size_t num_cols = 6;\n\n  SparseMatrixFiller filler(num_cols, use_map_method, 1.0);\n  // Add in random order.\n  // Note that empty rows must be explicitly added in the blaze\n  // routines, so here we have rows with zero elements, rows with 1\n  // element, and rows with >1 element to test all the edge cases.\n  // Also note that the last row is empty, which tests another edge case.\n  filler.add(4.0, 0, 0);\n  filler.add(6.0, 1, 3);\n  filler.add(2.0, 1, 3);  // This element is repeated! Important for test.\n  filler.add(3.0, 0, 3);\n  filler.add(7.0, 2, 2);\n  filler.add(5.0, 4, 5);\n  filler.add(3.0, 4, 3);\n  filler.add(7.0, 0, 3);  // This element is repeated! Important for test.\n\n  SparseMatrixType matrix;\n  filler.fill(make_not_null(&matrix));\n\n  if constexpr (not std::is_same_v<SparseMatrixType, SimpleSparseMatrix>) {\n    // Matrix is square so number of rows and columns is the same.\n    // SimpleSparseMatrix doesn't have rows, columns, or size member fns.\n    CHECK(matrix.rows() == num_cols);\n    CHECK(matrix.columns() == num_cols);\n    CHECK(size(matrix) == num_cols * num_cols);\n  }\n\n  CHECK(matrix(0, 0) == 4.0);\n  CHECK(matrix(1, 3) == 8.0);\n  CHECK(matrix(0, 3) == 10.0);\n  CHECK(matrix(2, 2) == 7.0);\n  CHECK(matrix(4, 5) == 5.0);\n  CHECK(matrix(4, 3) == 3.0);\n\n  // Values that we did not fill\n  CHECK(matrix(1, 0) == 0.0);\n  CHECK(matrix(2, 0) == 0.0);\n  CHECK(matrix(3, 0) == 0.0);\n  CHECK(matrix(4, 0) == 0.0);\n  CHECK(matrix(5, 0) == 0.0);\n  CHECK(matrix(0, 1) == 0.0);\n  CHECK(matrix(1, 1) == 0.0);\n  CHECK(matrix(2, 1) == 0.0);\n  CHECK(matrix(3, 1) == 0.0);\n  CHECK(matrix(4, 1) == 0.0);\n  CHECK(matrix(5, 1) == 0.0);\n  CHECK(matrix(0, 2) == 0.0);\n  CHECK(matrix(1, 2) == 0.0);\n  CHECK(matrix(3, 2) == 0.0);\n  CHECK(matrix(4, 2) == 0.0);\n  CHECK(matrix(5, 2) == 0.0);\n  CHECK(matrix(2, 3) == 0.0);\n  CHECK(matrix(3, 3) == 0.0);\n  CHECK(matrix(5, 3) == 0.0);\n  CHECK(matrix(0, 4) == 0.0);\n  CHECK(matrix(1, 4) == 0.0);\n  CHECK(matrix(2, 4) == 0.0);\n  CHECK(matrix(3, 4) == 0.0);\n  CHECK(matrix(4, 4) == 0.0);\n  CHECK(matrix(5, 4) == 0.0);\n  CHECK(matrix(0, 5) == 0.0);\n  CHECK(matrix(1, 5) == 0.0);\n  CHECK(matrix(2, 5) == 0.0);\n  CHECK(matrix(3, 5) == 0.0);\n  CHECK(matrix(5, 5) == 0.0);\n}\n\n// This test touches some edge cases for Blaze matrices\n// that the other tests do not.\nvoid test_sparse_matrix_filler_last_row_filled(const bool use_map_method) {\n  const size_t num_cols = 3;\n\n  SparseMatrixFiller filler(num_cols, use_map_method, 1.0);\n  // Add in random order.\n  // First row has only one element (unlike the other tests)\n  // Last row is nonempty with one nonzero column (unlike the other tests)\n  filler.add(6.0, 1, 2);\n  filler.add(7.0, 2, 2);\n  filler.add(4.0, 0, 0);\n  filler.add(2.0, 1, 2);  // This element is repeated! Important for test.\n\n  blaze::CompressedMatrix<double, blaze::rowMajor> matrix;\n  filler.fill(make_not_null(&matrix));\n\n  // Matrix is square so number of rows and columns is the same.\n  CHECK(matrix.rows() == num_cols);\n  CHECK(matrix.columns() == num_cols);\n  CHECK(size(matrix) == num_cols * num_cols);\n\n  CHECK(matrix(0, 0) == 4.0);\n  CHECK(matrix(1, 2) == 8.0);\n  CHECK(matrix(2, 2) == 7.0);\n\n  // Values that we did not fill\n  CHECK(matrix(1, 0) == 0.0);\n  CHECK(matrix(2, 0) == 0.0);\n  CHECK(matrix(0, 1) == 0.0);\n  CHECK(matrix(1, 1) == 0.0);\n  CHECK(matrix(2, 1) == 0.0);\n  CHECK(matrix(0, 2) == 0.0);\n}\n\n// This test touches some edge cases for Blaze matrices\n// that the other tests do not.\nvoid test_sparse_matrix_filler_first_row_empty(const bool use_map_method) {\n  const size_t num_cols = 3;\n\n  SparseMatrixFiller filler(num_cols, use_map_method, 1.0);\n  // Add in random order.\n  // First row is empty (unlike the other tests)\n  // Last row is nonempty with multiple nonzero columns (unlike the other tests)\n  filler.add(7.0, 2, 2);\n  filler.add(6.0, 1, 2);\n  filler.add(9.0, 2, 0);\n  filler.add(2.0, 1, 2);  // This element is repeated! Important for test.\n\n  blaze::CompressedMatrix<double, blaze::rowMajor> matrix;\n  filler.fill(make_not_null(&matrix));\n\n  // Matrix is square so number of rows and columns is the same.\n  CHECK(matrix.rows() == num_cols);\n  CHECK(matrix.columns() == num_cols);\n  CHECK(size(matrix) == num_cols * num_cols);\n\n  CHECK(matrix(2, 0) == 9.0);\n  CHECK(matrix(1, 2) == 8.0);\n  CHECK(matrix(2, 2) == 7.0);\n\n  // Values that we did not fill\n  CHECK(matrix(0, 0) == 0.0);\n  CHECK(matrix(1, 0) == 0.0);\n  CHECK(matrix(0, 1) == 0.0);\n  CHECK(matrix(1, 1) == 0.0);\n  CHECK(matrix(2, 1) == 0.0);\n  CHECK(matrix(0, 2) == 0.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.SparseMatrixFiller\",\n                  \"[DataStructures][Unit]\") {\n  test_sparse_matrix_filler<SimpleSparseMatrix>(true);\n  test_sparse_matrix_filler<SimpleSparseMatrix>(false);\n  test_sparse_matrix_filler<blaze::CompressedMatrix<double, blaze::rowMajor>>(\n      true);\n  test_sparse_matrix_filler<blaze::CompressedMatrix<double, blaze::rowMajor>>(\n      false);\n  test_sparse_matrix_filler_last_row_filled(true);\n  test_sparse_matrix_filler_last_row_filled(false);\n  test_sparse_matrix_filler_first_row_empty(true);\n  test_sparse_matrix_filler_first_row_empty(false);\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_SpinWeighted.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\n// tests for is_any_spin_weighted\nstatic_assert(is_any_spin_weighted_v<SpinWeighted<int, 3>>,\n              \"failed testing is_any_spin_weighted\");\nstatic_assert(is_any_spin_weighted_v<SpinWeighted<DataVector, 0>>,\n              \"failed testing is_any_spin_weighted\");\nstatic_assert(not is_any_spin_weighted_v<ComplexDataVector>,\n              \"failed testing is_any_spin_weighted\");\n\n// tests for is_spin_weighted_of\nstatic_assert(is_spin_weighted_of_v<DataVector, SpinWeighted<DataVector, 1>>,\n              \"failed testing is_spin_weighted_of\");\nstatic_assert(is_spin_weighted_of_v<ComplexDataVector,\n                                    SpinWeighted<ComplexDataVector, -1>>,\n              \"failed testing is_spin_weighted_of\");\nstatic_assert(\n    not is_spin_weighted_of_v<ComplexDataVector, SpinWeighted<DataVector, -2>>,\n    \"failed testing is_spin_weighted_of\");\nstatic_assert(not is_spin_weighted_of_v<ComplexDataVector, ComplexDataVector>,\n              \"failed testing is_spin_weighted_of\");\n\n// tests for is_spin_weighted_of_same_type\nstatic_assert(is_spin_weighted_of_same_type_v<SpinWeighted<DataVector, -2>,\n                                              SpinWeighted<DataVector, 1>>,\n              \"failed testing is_spin_weighted_of_same_type\");\nstatic_assert(\n    is_spin_weighted_of_same_type_v<SpinWeighted<ComplexDataVector, 0>,\n                                    SpinWeighted<ComplexDataVector, -1>>,\n    \"failed testing is_spin_weighted_of_same_type\");\nstatic_assert(not is_spin_weighted_of_same_type_v<ComplexDataVector,\n                                                  SpinWeighted<DataVector, -2>>,\n              \"failed testing is_spin_weighted_of_same_type\");\nstatic_assert(\n    not is_spin_weighted_of_same_type_v<SpinWeighted<ComplexDataVector, 1>,\n                                        SpinWeighted<DataVector, 1>>,\n    \"failed testing is_spin_weighted_of_same_type\");\n\nnamespace {\ntemplate <typename SpinWeightedType, typename CompatibleType>\nvoid test_spinweights() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<SpinWeightedType>>\n      spin_weighted_dist{\n          static_cast<tt::get_fundamental_type_t<SpinWeightedType>>(\n              1.0),  // avoid divide by 0\n          static_cast<tt::get_fundamental_type_t<SpinWeightedType>>(100.0)};\n\n  UniformCustomDistribution<tt::get_fundamental_type_t<CompatibleType>>\n      compatible_dist{\n          static_cast<tt::get_fundamental_type_t<CompatibleType>>(\n              1.0),  // avoid divide by 0\n          static_cast<tt::get_fundamental_type_t<CompatibleType>>(100.0)};\n\n  UniformCustomDistribution<size_t> size_dist{5, 10};\n  const size_t size = size_dist(gen);\n\n  auto spin_weight_0 =\n      make_with_random_values<SpinWeighted<SpinWeightedType, 0>>(\n          make_not_null(&gen), make_not_null(&spin_weighted_dist), size);\n  const auto spin_weight_1 =\n      make_with_random_values<SpinWeighted<SpinWeightedType, 1>>(\n          make_not_null(&gen), make_not_null(&spin_weighted_dist), size);\n  const auto spin_weight_m2 =\n      make_with_random_values<SpinWeighted<SpinWeightedType, -2>>(\n          make_not_null(&gen), make_not_null(&spin_weighted_dist), size);\n  const auto no_spin_weight = make_with_random_values<SpinWeightedType>(\n      make_not_null(&gen), make_not_null(&spin_weighted_dist), size);\n\n  const auto exp_spin_weight_0 = exp(spin_weight_0);\n  const auto sqrt_spin_weight_0 = sqrt(spin_weight_0);\n  CHECK_ITERABLE_APPROX(exp_spin_weight_0.data(), exp(spin_weight_0.data()));\n  CHECK_ITERABLE_APPROX(sqrt_spin_weight_0.data(), sqrt(spin_weight_0.data()));\n\n  const auto serialized_and_deserialized_copy =\n      serialize_and_deserialize(spin_weight_0);\n  CHECK(spin_weight_0 == serialized_and_deserialized_copy);\n\n  const auto compatible_spin_weight_0 =\n      make_with_random_values<SpinWeighted<CompatibleType, 0>>(\n          make_not_null(&gen), make_not_null(&compatible_dist), size);\n  const auto compatible_spin_weight_1 =\n      make_with_random_values<SpinWeighted<CompatibleType, 1>>(\n          make_not_null(&gen), make_not_null(&compatible_dist), size);\n  const auto compatible_spin_weight_m2 =\n      make_with_random_values<SpinWeighted<CompatibleType, -2>>(\n          make_not_null(&gen), make_not_null(&compatible_dist), size);\n  const auto compatible_no_spin_weight =\n      make_with_random_values<CompatibleType>(\n          make_not_null(&gen), make_not_null(&compatible_dist), size);\n\n  SpinWeighted<SpinWeightedType, 1> rvalue_assigned_spin_weight_1{\n      spin_weight_1 + compatible_spin_weight_1};\n  CHECK(rvalue_assigned_spin_weight_1.data() ==\n        spin_weight_1.data() + compatible_spin_weight_1.data());\n  rvalue_assigned_spin_weight_1 = spin_weight_1 - compatible_spin_weight_1;\n  CHECK(rvalue_assigned_spin_weight_1.data() ==\n        spin_weight_1.data() - compatible_spin_weight_1.data());\n\n  SpinWeighted<SpinWeightedType, -2> lvalue_assigned_spin_weight_m2{\n      spin_weight_m2};\n  CHECK(lvalue_assigned_spin_weight_m2.data() == spin_weight_m2.data());\n  lvalue_assigned_spin_weight_m2 = compatible_spin_weight_m2;\n  CHECK(lvalue_assigned_spin_weight_m2.data() ==\n        compatible_spin_weight_m2.data());\n\n  // check compile-time spin values\n  static_assert(decltype(spin_weight_0)::spin == 0,\n                \"assert failed for the spin of a spin-weight 0\");\n  static_assert(decltype(spin_weight_1)::spin == 1,\n                \"assert failed for the spin of a spin-weight 1\");\n  static_assert(decltype(compatible_spin_weight_0 / spin_weight_m2)::spin == 2,\n                \"assert failed for the spin of a spin-weight ratio.\");\n  static_assert(decltype(compatible_spin_weight_1 * spin_weight_1)::spin == 2,\n                \"assert failed for the spin of a spin-weight product.\");\n\n  // check that valid spin combinations work\n  CHECK(spin_weight_0 + spin_weight_0 ==\n        SpinWeighted<SpinWeightedType, 0>{spin_weight_0.data() +\n                                          spin_weight_0.data()});\n  CHECK(spin_weight_0 - no_spin_weight ==\n        SpinWeighted<decltype(std::declval<SpinWeightedType>() -\n                              std::declval<SpinWeightedType>()),\n                     0>{spin_weight_0.data() - no_spin_weight});\n  CHECK(spin_weight_1 * spin_weight_m2 ==\n        SpinWeighted<SpinWeightedType, -1>{spin_weight_1.data() *\n                                           spin_weight_m2.data()});\n  CHECK(\n      compatible_spin_weight_1 / spin_weight_m2 ==\n      SpinWeighted<decltype(std::declval<CompatibleType>() /\n                            std::declval<SpinWeightedType>()),\n                   3>{compatible_spin_weight_1.data() / spin_weight_m2.data()});\n\n  // check that plain data types act as spin 0\n  CHECK(\n      spin_weight_0 + no_spin_weight ==\n      SpinWeighted<SpinWeightedType, 0>{spin_weight_0.data() + no_spin_weight});\n  CHECK(compatible_no_spin_weight - spin_weight_0 ==\n        SpinWeighted<decltype(std::declval<CompatibleType>() -\n                              std::declval<SpinWeightedType>()),\n                     0>{compatible_no_spin_weight - spin_weight_0.data()});\n  CHECK(\n      spin_weight_1 * no_spin_weight ==\n      SpinWeighted<SpinWeightedType, 1>{spin_weight_1.data() * no_spin_weight});\n  CHECK(no_spin_weight / spin_weight_m2 ==\n        SpinWeighted<decltype(std::declval<SpinWeightedType>() /\n                              std::declval<SpinWeightedType>()),\n                     2>{no_spin_weight / spin_weight_m2.data()});\n  CHECK(-spin_weight_1 ==\n        SpinWeighted<SpinWeightedType, 1>(-spin_weight_1.data()));\n  CHECK(spin_weight_m2 ==\n        SpinWeighted<SpinWeightedType, -2>(spin_weight_m2.data()));\n\n  SpinWeighted<SpinWeightedType, 0> sum = spin_weight_0 + spin_weight_0;\n  spin_weight_0 += spin_weight_0;\n  CHECK(spin_weight_0 == sum);\n\n  SpinWeighted<SpinWeightedType, 0> difference = spin_weight_0 - no_spin_weight;\n  spin_weight_0 -= no_spin_weight;\n  CHECK(spin_weight_0 == difference);\n}\n\nusing SpinWeightedTypePairs =\n    tmpl::list<tmpl::list<std::complex<double>, double>,\n               tmpl::list<ComplexDataVector, std::complex<double>>>;\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.SpinWeighted\",\n                  \"[DataStructures][Unit]\") {\n  tmpl::for_each<SpinWeightedTypePairs>([](auto x) {\n    using type_pair = typename decltype(x)::type;\n    test_spinweights<tmpl::front<type_pair>, tmpl::back<type_pair>>();\n  });\n\n  const SpinWeighted<ComplexDataVector, 1> size_created_spin_weight_1{5};\n  CHECK(size_created_spin_weight_1.data().size() == 5);\n  CHECK(size_created_spin_weight_1.size() == 5);\n\n  const SpinWeighted<ComplexDataVector, 1> const_view;\n  make_const_view(make_not_null(&const_view), size_created_spin_weight_1, 2, 2);\n  CHECK(const_view.size() == 2);\n  CHECK(const_view.data().data() ==\n        size_created_spin_weight_1.data().data() + 2);\n\n  SpinWeighted<ComplexDataVector, -2> size_and_value_created_spin_weight_m2{\n      5, 4.0};\n  CHECK(size_and_value_created_spin_weight_m2.data() ==\n        ComplexDataVector{5, 4.0});\n  CHECK(size_and_value_created_spin_weight_m2.size() == 5);\n\n  // test destructive resize for vector type\n  SpinWeighted<ComplexDataVector, 2> destructive_resize_check{5, 4.0};\n  const SpinWeighted<ComplexDataVector, 2> destructive_resize_copy =\n      destructive_resize_check;\n  // check unchanged if no resize\n  destructive_resize_check.destructive_resize(5);\n  CHECK(destructive_resize_check == destructive_resize_copy);\n  // check resize occurs if appropriate\n  destructive_resize_check.destructive_resize(6);\n  CHECK(destructive_resize_check != destructive_resize_copy);\n  CHECK(destructive_resize_check.size() == destructive_resize_copy.size() + 1);\n\n  CHECK(make_with_value<SpinWeighted<double, 2>>(2_st, 1.1) ==\n        SpinWeighted<double, 2>(1.1));\n  CHECK(make_with_value<SpinWeighted<DataVector, 2>>(2_st, 1.1) ==\n        SpinWeighted<DataVector, 2>(DataVector{1.1, 1.1}));\n  CHECK(make_with_value<SpinWeighted<DataVector, 2>>(\n            SpinWeighted<DataVector, 2>(DataVector{1.2, 2.1}), 1.1) ==\n        SpinWeighted<DataVector, 2>(DataVector{1.1, 1.1}));\n\n  {\n    SpinWeighted<double, 2> spin_double(1.1);\n    set_number_of_grid_points(make_not_null(&spin_double), 2_st);\n    CHECK(spin_double == SpinWeighted<double, 2>(1.1));\n    set_number_of_grid_points(make_not_null(&spin_double), 1.2);\n    CHECK(spin_double == SpinWeighted<double, 2>(1.1));\n  }\n  {\n    SpinWeighted<DataVector, 2> spin_vector(DataVector{1.1, 1.2});\n    set_number_of_grid_points(make_not_null(&spin_vector), 2_st);\n    CHECK(spin_vector == SpinWeighted<DataVector, 2>(DataVector{1.1, 1.2}));\n    set_number_of_grid_points(make_not_null(&spin_vector), 3_st);\n    CHECK(spin_vector.size() == 3);\n  }\n}\n\n// A macro which will static_assert fail when LHSTYPE OP RHSTYPE succeeds during\n// SFINAE. Used to make sure we can't violate spin addition rules.\n// clang-tidy: wants parens around macro argument, but that breaks macro\n#define CHECK_TYPE_OPERATION_FAIL(TAG, OP, LHSTYPE, RHSTYPE)                  \\\n  template <typename T1, typename T2, typename = std::void_t<>>               \\\n  struct TAG : std::true_type {};                                             \\\n  template <typename T1, typename T2>                                         \\\n  struct TAG<T1, T2,                                                          \\\n             std::void_t<decltype(std::declval<T1>() OP std::declval<T2>())>> \\\n      : std::false_type {};                                                   \\\n  static_assert(TAG<LHSTYPE, RHSTYPE>::value, /*NOLINT*/                      \\\n                \"static_assert failed, \" #LHSTYPE #OP #RHSTYPE \" had a type\")\n\nusing SpinZero = SpinWeighted<double, 0>;\nusing SpinOne = SpinWeighted<double, 1>;\nusing SpinTwo = SpinWeighted<double, 2>;\n\nCHECK_TYPE_OPERATION_FAIL(spin_check_1, +, SpinZero, SpinOne);\nCHECK_TYPE_OPERATION_FAIL(spin_check_2, +, SpinOne, SpinTwo);\nCHECK_TYPE_OPERATION_FAIL(spin_check_3, +,\n                          decltype(std::declval<SpinZero>() *\n                                   std::declval<SpinTwo>()),\n                          SpinOne);\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_StaticDeque.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <deque>\n#include <initializer_list>\n#include <limits>\n#include <ostream>\n#include <pup.h>\n#include <stdexcept>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/CircularDeque.hpp\"\n#include \"DataStructures/StaticDeque.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n// This gives a uniform interface with CircularDeque to make sharing\n// test code easier.\ntemplate <size_t Size>\nstruct curry_static_deque {\n  template <typename T>\n  using f = StaticDeque<T, Size>;\n};\n\nstruct AllocationChecker {\n  int value{12345};\n\n  AllocationChecker() { register_self(); }\n  AllocationChecker(const AllocationChecker& other) : value(other.value) {\n    register_self();\n  }\n  AllocationChecker& operator=(const AllocationChecker&) = default;\n\n  ~AllocationChecker() { unregister_self(); }\n\n  AllocationChecker(const int v) : value(v) { register_self(); }\n\n  static bool was_clean() {\n    const bool clean = live_objects_.empty();\n    live_objects_.clear();\n    return clean;\n  }\n\n  void pup(PUP::er& p) { p | value; }\n\n private:\n  void register_self() const {\n    const bool inserted = live_objects_.insert(this).second;\n    REQUIRE(inserted);\n  }\n\n  void unregister_self() const {\n    const int removed = live_objects_.erase(this);\n    REQUIRE(removed == 1);\n  }\n\n  static std::unordered_set<const AllocationChecker*> live_objects_;\n};\n\nstd::unordered_set<const AllocationChecker*> AllocationChecker::live_objects_{};\n\nbool operator==(const AllocationChecker& a, const AllocationChecker& b) {\n  return a.value == b.value;\n}\n[[maybe_unused]] bool operator!=(const AllocationChecker& a,\n                                 const AllocationChecker& b) {\n  return not(a == b);\n}\n\nstd::ostream& operator<<(std::ostream& s, const AllocationChecker& a) {\n  return s << a.value;\n}\n\ntemplate <int>\nstruct ForwardingTesterArg {\n  ForwardingTesterArg() = delete;\n  ForwardingTesterArg(const ForwardingTesterArg&) = delete;\n  ForwardingTesterArg(ForwardingTesterArg&&) { CHECK(false); }\n  ForwardingTesterArg& operator=(const ForwardingTesterArg&) = delete;\n  ForwardingTesterArg& operator=(ForwardingTesterArg&&) {\n    CHECK(false);\n    return *this;\n  }\n\n  explicit ForwardingTesterArg(int) {}\n};\n\nstruct ForwardingTester {\n  ForwardingTester() = delete;\n  ForwardingTester(const ForwardingTester&) = delete;\n  ForwardingTester(ForwardingTester&&) = default;\n  ForwardingTester& operator=(const ForwardingTester&) = delete;\n  ForwardingTester& operator=(ForwardingTester&&) {\n    CHECK(false);\n    return *this;\n  }\n\n  ForwardingTester(ForwardingTesterArg<0>&&, ForwardingTesterArg<1>&&) {}\n};\n\ntemplate <template <typename> typename Deque>\nvoid test_construction_and_assignment() {\n  { CHECK(Deque<int>{}.size() == 0); }\n  {\n    const Deque<int> deque(3, 6);\n    CHECK(deque.size() == 3);\n    CHECK(deque[0] == 6);\n    CHECK(deque[1] == 6);\n    CHECK(deque[2] == 6);\n  }\n  {\n    const Deque<int> deque(3);\n    CHECK(deque.size() == 3);\n    CHECK(deque[0] == 0);\n    CHECK(deque[1] == 0);\n    CHECK(deque[2] == 0);\n  }\n  {\n    const Deque<int> deque{3, 4, 5};\n    CHECK(deque.size() == 3);\n    CHECK(deque[0] == 3);\n    CHECK(deque[1] == 4);\n    CHECK(deque[2] == 5);\n  }\n  {\n    std::initializer_list<int> init{3, 4, 5};\n    const Deque<int> deque(init.begin(), init.end());\n    CHECK(deque.size() == 3);\n    CHECK(deque[0] == 3);\n    CHECK(deque[1] == 4);\n    CHECK(deque[2] == 5);\n  }\n  {\n    const Deque<int> deque1{3, 4, 5};\n    const Deque<int> deque2 = deque1;\n    CHECK(deque1.size() == 3);\n    CHECK(deque1[0] == 3);\n    CHECK(deque1[1] == 4);\n    CHECK(deque1[2] == 5);\n    CHECK(deque2.size() == 3);\n    CHECK(deque2[0] == 3);\n    CHECK(deque2[1] == 4);\n    CHECK(deque2[2] == 5);\n  }\n  {\n    Deque<int> deque1{3, 4, 5};\n    const Deque<int> deque2 = std::move(deque1);\n    CHECK(deque2.size() == 3);\n    CHECK(deque2[0] == 3);\n    CHECK(deque2[1] == 4);\n    CHECK(deque2[2] == 5);\n  }\n  {\n    Deque<NonCopyable> deque1{};\n    const Deque<NonCopyable> deque2 = std::move(deque1);\n  }\n\n  {\n    const Deque<int> deque1{3, 4, 5};\n    Deque<int> deque2{6, 7, 8, 9};\n    deque2 = deque1;\n    CHECK(deque1.size() == 3);\n    CHECK(deque1[0] == 3);\n    CHECK(deque1[1] == 4);\n    CHECK(deque1[2] == 5);\n    CHECK(deque2.size() == 3);\n    CHECK(deque2[0] == 3);\n    CHECK(deque2[1] == 4);\n    CHECK(deque2[2] == 5);\n  }\n  {\n    Deque<int> deque1{3, 4, 5};\n    Deque<int> deque2{6, 7, 8, 9};\n    deque2 = std::move(deque1);\n    CHECK(deque2.size() == 3);\n    CHECK(deque2[0] == 3);\n    CHECK(deque2[1] == 4);\n    CHECK(deque2[2] == 5);\n  }\n  {\n    Deque<NonCopyable> deque1{};\n    Deque<NonCopyable> deque2{};\n    deque2 = std::move(deque1);\n  }\n  {\n    Deque<int> deque{6, 7, 8, 9};\n    deque = {3, 4, 5};\n    CHECK(deque.size() == 3);\n    CHECK(deque[0] == 3);\n    CHECK(deque[1] == 4);\n    CHECK(deque[2] == 5);\n  }\n}\n\ntemplate <typename Deque, typename F>\nvoid compare_with_stl_impl(const F operation) {\n  {\n    std::deque<AllocationChecker> stl_deque{1, 2, 3, 4, 5};\n    Deque static_deque{1, 2, 3, 4, 5};\n    // Get the internals into a more interesting state.\n    stl_deque.push_front(0);\n    static_deque.push_front(0);\n    if constexpr (std::is_same_v<decltype(operation(make_not_null(&stl_deque))),\n                                 void>) {\n      operation(make_not_null(&stl_deque));\n      operation(make_not_null(&static_deque));\n    } else if constexpr (std::is_same_v<decltype(operation(\n                                            make_not_null(&stl_deque))),\n                                        decltype(operation(\n                                            make_not_null(&static_deque)))>) {\n      CHECK(operation(make_not_null(&stl_deque)) ==\n            operation(make_not_null(&static_deque)));\n    } else {\n      // Probably returning iterators\n\n      // These evaluations cannot be inlined because the value of\n      // .begin() could depend on evaluation order.\n      const auto stl_result = operation(make_not_null(&stl_deque));\n      const auto static_result = operation(make_not_null(&static_deque));\n      CHECK((stl_result - stl_deque.begin()) ==\n            (static_result - static_deque.begin()));\n    }\n    CAPTURE(stl_deque);\n    CAPTURE(static_deque);\n    CHECK(stl_deque.size() == static_deque.size());\n    CHECK(std::equal(stl_deque.begin(), stl_deque.end(), static_deque.begin(),\n                     static_deque.end()));\n  }\n  CHECK(AllocationChecker::was_clean());\n}\n\n#define STRINGIFY_LINE(line) STRINGIFY_LINE2(line)\n#define STRINGIFY_LINE2(line) #line\n\n#define COMPARE_WITH_STL(operation)            \\\n  do {                                         \\\n    INFO(\"Line \" STRINGIFY_LINE(__LINE__));    \\\n    INFO(#operation);                          \\\n    /* `Deque` is from the calling function */ \\\n    compare_with_stl_impl<Deque>(operation);   \\\n  } while (false)\n\ntemplate <template <typename> typename DequeTemplate>\nvoid test_against_stl() {\n  using Deque = DequeTemplate<AllocationChecker>;\n  COMPARE_WITH_STL(([](const auto deque) {\n    // Some of the checks below pick numbers above and below this.\n    REQUIRE(deque->size() == 6);\n  }));\n\n  COMPARE_WITH_STL(([](const auto deque) { return deque->assign(4, 9); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->assign(8, 9); }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->assign(init.begin(), init.end());\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->assign(init);\n  }));\n\n  COMPARE_WITH_STL(([](const auto deque) { return deque->at(0); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->at(5); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return std::as_const(*deque).at(0); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return std::as_const(*deque).at(5); }));\n  COMPARE_WITH_STL(([](const auto deque) { return (*deque)[0]; }));\n  COMPARE_WITH_STL(([](const auto deque) { return (*deque)[5]; }));\n  COMPARE_WITH_STL(([](const auto deque) { return std::as_const(*deque)[0]; }));\n  COMPARE_WITH_STL(([](const auto deque) { return std::as_const(*deque)[5]; }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->front(); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return std::as_const(*deque).front(); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->back(); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return std::as_const(*deque).back(); }));\n\n  COMPARE_WITH_STL(([](const auto deque) { return deque->begin(); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return std::as_const(*deque).begin(); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return std::as_const(*deque).cbegin(); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->end(); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return std::as_const(*deque).end(); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return std::as_const(*deque).cend(); }));\n  // Reverse iterators won't work with the macro.  This is all\n  // inherited code tested elsewhere, anyway.\n\n  COMPARE_WITH_STL(([](const auto deque) { return deque->size(); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->shrink_to_fit(); }));\n\n  COMPARE_WITH_STL(([](const auto deque) { return deque->clear(); }));\n\n  // Test at each end and in each half.\n  COMPARE_WITH_STL(([](const auto deque) {\n    const AllocationChecker value = 9;\n    return deque->insert(deque->begin(), value);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    const AllocationChecker value = 9;\n    return deque->insert(deque->end(), value);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    const AllocationChecker value = 9;\n    return deque->insert(deque->begin() + 2, value);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    const AllocationChecker value = 9;\n    return deque->insert(deque->end() - 2, value);\n  }));\n\n  COMPARE_WITH_STL(([](const auto deque) {\n    AllocationChecker value = 9;\n    return deque->insert(deque->begin(), std::move(value));\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    AllocationChecker value = 9;\n    return deque->insert(deque->end(), std::move(value));\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    AllocationChecker value = 9;\n    return deque->insert(deque->begin() + 2, std::move(value));\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    AllocationChecker value = 9;\n    return deque->insert(deque->end() - 2, std::move(value));\n  }));\n\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->insert(deque->begin(), 2, 9); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->insert(deque->end(), 2, 9); }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->insert(deque->begin() + 2, 2, 9);\n  }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->insert(deque->end() - 2, 2, 9); }));\n\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->insert(deque->begin(), init.begin(), init.end());\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->insert(deque->end(), init.begin(), init.end());\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->insert(deque->begin() + 2, init.begin(), init.end());\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->insert(deque->end() - 2, init.begin(), init.end());\n  }));\n\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->insert(deque->begin(), init);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->insert(deque->end(), init);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->insert(deque->begin() + 2, init);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    std::initializer_list<AllocationChecker> init{3, 4, 5};\n    return deque->insert(deque->end() - 2, init);\n  }));\n\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->emplace(deque->begin(), 9); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->emplace(deque->end(), 9); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->emplace(deque->begin() + 2, 9); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->emplace(deque->end() - 2, 9); }));\n\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->erase(deque->begin()); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->erase(deque->end() - 1); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->erase(deque->begin() + 2); }));\n  COMPARE_WITH_STL(\n      ([](const auto deque) { return deque->erase(deque->end() - 3); }));\n\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->erase(deque->begin(), deque->begin() + 2);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->erase(deque->end() - 3, deque->end() - 1);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->erase(deque->begin() + 2, deque->begin() + 4);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->erase(deque->end() - 5, deque->end() - 3);\n  }));\n\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->erase(deque->begin(), deque->begin());\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->erase(deque->end() - 1, deque->end() - 1);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->erase(deque->begin() + 2, deque->begin() + 2);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    return deque->erase(deque->end() - 3, deque->end() - 3);\n  }));\n\n  COMPARE_WITH_STL(([](const auto deque) {\n    const AllocationChecker value = 9;\n    return deque->push_back(value);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    AllocationChecker value = 9;\n    return deque->push_back(std::move(value));\n  }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->emplace_back(9); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->pop_back(); }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    const AllocationChecker value = 9;\n    return deque->push_front(value);\n  }));\n  COMPARE_WITH_STL(([](const auto deque) {\n    AllocationChecker value = 9;\n    return deque->push_front(std::move(value));\n  }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->emplace_front(9); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->pop_front(); }));\n\n  COMPARE_WITH_STL(([](const auto deque) { return deque->resize(4); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->resize(6); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->resize(4, 9); }));\n  COMPARE_WITH_STL(([](const auto deque) { return deque->resize(6, 9); }));\n}\n\ntemplate <template <typename> typename Deque>\nvoid test_misc_operations() {\n  {\n    const Deque<int> deque{1};\n    CHECK(not deque.empty());\n  }\n  {\n    const Deque<int> deque{};\n    CHECK(deque.empty());\n  }\n\n  CHECK_THROWS_AS(([]() {\n                    Deque<int> deque{1, 2};\n                    deque.at(2);\n                  })(),\n                  std::out_of_range);\n\n  {\n    Deque<int> deque1{1, 2};\n    Deque<int> deque2{2};\n    // Different internal state\n    deque2.push_front(1);\n    CHECK(deque1 == deque2);\n    CHECK_FALSE(deque1 != deque2);\n    CHECK_FALSE(deque1 < deque2);\n    CHECK_FALSE(deque1 > deque2);\n    CHECK(deque1 <= deque2);\n    CHECK(deque1 >= deque2);\n  }\n  {\n    Deque<int> deque1{1, 2, 3};\n    Deque<int> deque2{3};\n    // Different internal state\n    deque2.push_front(1);\n    CHECK_FALSE(deque1 == deque2);\n    CHECK(deque1 != deque2);\n    CHECK(deque1 < deque2);\n    CHECK_FALSE(deque1 > deque2);\n    CHECK(deque1 <= deque2);\n    CHECK_FALSE(deque1 >= deque2);\n  }\n  {\n    Deque<int> deque1{1, 2, 3};\n    Deque<int> deque2{2};\n    // Different internal state\n    deque2.push_front(1);\n    CHECK_FALSE(deque1 == deque2);\n    CHECK(deque1 != deque2);\n    CHECK_FALSE(deque1 < deque2);\n    CHECK(deque1 > deque2);\n    CHECK_FALSE(deque1 <= deque2);\n    CHECK(deque1 >= deque2);\n  }\n\n  {\n    Deque<AllocationChecker> deque1{1, 2, 3, 4, 5};\n    Deque<AllocationChecker> deque2{7, 8};\n    deque1.swap(deque2);\n    CHECK(deque1 == Deque<AllocationChecker>{7, 8});\n    CHECK(deque2 == Deque<AllocationChecker>{1, 2, 3, 4, 5});\n  }\n  CHECK(AllocationChecker::was_clean());\n\n  {\n    Deque<AllocationChecker> deque1{1, 2, 3, 4, 5};\n    Deque<AllocationChecker> deque2{7, 8};\n    using std::swap;\n    swap(deque1, deque2);\n    CHECK(deque1 == Deque<AllocationChecker>{7, 8});\n    CHECK(deque2 == Deque<AllocationChecker>{1, 2, 3, 4, 5});\n  }\n  CHECK(AllocationChecker::was_clean());\n\n  // Check argument forwarding\n  {\n    Deque<ForwardingTester> deque{};\n    deque.insert(deque.begin(), ForwardingTester(ForwardingTesterArg<0>(1),\n                                                 ForwardingTesterArg<1>(1)));\n    deque.emplace(deque.begin(), ForwardingTesterArg<0>(1),\n                  ForwardingTesterArg<1>(1));\n    deque.emplace(deque.end(), ForwardingTesterArg<0>(1),\n                  ForwardingTesterArg<1>(1));\n    deque.push_back(\n        ForwardingTester(ForwardingTesterArg<0>(1), ForwardingTesterArg<1>(1)));\n    deque.emplace_back(ForwardingTesterArg<0>(1), ForwardingTesterArg<1>(1));\n    deque.push_front(\n        ForwardingTester(ForwardingTesterArg<0>(1), ForwardingTesterArg<1>(1)));\n    deque.emplace_front(ForwardingTesterArg<0>(1), ForwardingTesterArg<1>(1));\n  }\n\n  {\n    Deque<AllocationChecker> deque{3, 4, 5};\n    test_serialization(deque);\n  }\n  CHECK(AllocationChecker::was_clean());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.StaticDeque\", \"[Unit][DataStructures]\") {\n  test_construction_and_assignment<curry_static_deque<4>::f>();\n  test_construction_and_assignment<CircularDeque>();\n\n  // Some tests require a maximum size of at least 9.\n  test_against_stl<curry_static_deque<9>::f>();\n  test_against_stl<CircularDeque>();\n\n  test_misc_operations<curry_static_deque<9>::f>();\n  test_misc_operations<CircularDeque>();\n\n  {\n    const StaticDeque<int, 3> deque{};\n    CHECK(deque.max_size() == 3);\n    CHECK(deque.capacity() == 3);\n  }\n  {\n    CircularDeque<int> deque{};\n    CHECK(deque.max_size() == std::numeric_limits<size_t>::max());\n    CHECK(deque.capacity() == 0);\n    deque.push_back(1);\n    CHECK(deque.capacity() == 1);\n    deque.push_front(1);\n    CHECK(deque.capacity() == 2);\n    deque.pop_back();\n    CHECK(deque.capacity() == 2);\n    deque.push_back(1);\n    CHECK(deque.capacity() == 2);\n    deque.pop_back();\n    deque.shrink_to_fit();\n    CHECK(deque.capacity() == 1);\n  }\n  {\n    CircularDeque<int> deque1{1, 2};\n    CircularDeque<int> deque2{3, 4, 5};\n    const int* const data1 = &deque1.front();\n    const int* const data2 = &deque2.front();\n    using std::swap;\n    swap(deque1, deque2);\n    CHECK(deque1.capacity() == 3);\n    CHECK(deque2.capacity() == 2);\n    CHECK(&deque1.front() == data2);\n    CHECK(&deque2.front() == data1);\n    deque1.swap(deque2);\n    CHECK(deque1.capacity() == 2);\n    CHECK(deque2.capacity() == 3);\n    CHECK(&deque1.front() == data1);\n    CHECK(&deque2.front() == data2);\n  }\n\n  {\n    StaticDeque<int, 9> deque{1, 2};\n    CHECK(deque.capacity() == 9);\n    deque.reserve(8);\n    CHECK(deque.capacity() == 9);\n#ifdef SPECTRE_DEBUG\n    CHECK_THROWS_WITH(deque.reserve(10), Catch::Matchers::ContainsSubstring(\n                                             \"Cannot enlarge a StaticDeque\"));\n#endif\n  }\n  {\n    CircularDeque<int> deque{1, 2};\n    CHECK(deque.capacity() == 2);\n    deque.reserve(9);\n    CHECK(deque.capacity() == 9);\n    deque.reserve(8);\n    CHECK(deque.capacity() == 9);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_StripeIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/StripeIterator.hpp\"\n\nnamespace {\nvoid check_stripe_iterator_helper(StripeIterator s) {\n  CHECK((s.stride() == 3 and s.offset() == 0 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 1 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 2 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 12 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 13 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 14 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 24 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 25 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 26 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 36 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 37 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 38 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 48 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 49 and s and ++s));\n  CHECK((s.stride() == 3 and s.offset() == 50 and s));\n  CHECK((not++s and not s));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.StripeIterator\",\n                  \"[DataStructures][Unit]\") {\n  size_t i = 0;\n  for (StripeIterator s(Index<3>(3, 4, 5), 0); s; ++s) {\n    CHECK(s.offset() == i);\n    CHECK(s.stride() == 1);\n    i += 3;\n  }\n  i = 0;\n  for (StripeIterator s(Index<3>(3, 4, 5), 2); s; ++s) {\n    CHECK(s.offset() == i);\n    CHECK(s.stride() == 12);\n    i++;\n  }\n  check_stripe_iterator_helper(StripeIterator(Index<3>(3, 4, 5), 1));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_TaggedContainers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct MyDouble : db::SimpleTag {\n  using type = double;\n};\nstruct MyScalar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct MyDouble2 : db::SimpleTag {\n  using type = double;\n};\n}  // namespace Tags\n\ntemplate <size_t NumberOfArgs, size_t NumberOfReturnTags>\nstruct Function {\n  using return_tags = tmpl::conditional_t<\n      NumberOfReturnTags == 0, tmpl::list<>,\n      tmpl::conditional_t<NumberOfReturnTags == 1, tmpl::list<Tags::MyDouble2>,\n                          tmpl::list<Tags::MyDouble2, Tags::MyDouble>>>;\n  using argument_tags = tmpl::conditional_t<\n      NumberOfArgs == 0, tmpl::list<>,\n      tmpl::conditional_t<NumberOfArgs == 1, tmpl::list<Tags::MyDouble>,\n                          tmpl::list<Tags::MyDouble, Tags::MyScalar>>>;\n  static_assert(NumberOfArgs < 3);\n  static_assert(NumberOfReturnTags < 3);\n\n  static double apply() { return 3.1; }\n\n  double operator()() { return 3.3; }\n\n  static double apply(const gsl::not_null<double*> result) {\n    *result = 4.1;\n    return 5.1;\n  }\n\n  double operator()(const gsl::not_null<double*> result) {\n    *result = 4.3;\n    return 5.3;\n  }\n\n  static double apply(const gsl::not_null<double*> result,\n                      const gsl::not_null<double*> result2) {\n    *result = 4.2;\n    *result2 = 2 * *result;\n    return 5.2;\n  }\n\n  double operator()(const gsl::not_null<double*> result,\n                    const gsl::not_null<double*> result2) {\n    *result = 4.4;\n    *result2 = 2 * *result;\n    return 5.4;\n  }\n\n  static double apply(const gsl::not_null<double*> result, const double input) {\n    *result = input;\n    return 2.0 * input;\n  }\n\n  double operator()(const gsl::not_null<double*> result, const double input) {\n    *result = 3.0 * input;\n    return input;\n  }\n\n  static double apply(const double input) { return 2.0 * input; }\n\n  double operator()(const double input) { return input; }\n\n  static double apply(const gsl::not_null<double*> result,\n                      const gsl::not_null<double*> result2,\n                      const double input) {\n    *result = input;\n    *result2 = 2 * *result;\n    return 2.0 * input;\n  }\n\n  double operator()(const gsl::not_null<double*> result,\n                    const gsl::not_null<double*> result2, const double input) {\n    *result = 3.0 * input;\n    *result2 = 2 * *result;\n    return input;\n  }\n\n  static double apply(const double input, const Scalar<DataVector>& my_scalar) {\n    CHECK(get(my_scalar) == 3.2);\n    return 2.0 * input;\n  }\n\n  double operator()(const double input, const Scalar<DataVector>& my_scalar) {\n    CHECK(get(my_scalar) == 3.2);\n    return input;\n  }\n\n  static double apply(const gsl::not_null<double*> result, const double input,\n                      const Scalar<DataVector>& my_scalar) {\n    CHECK(get(my_scalar) == 3.2);\n    *result = input;\n    return 2.0 * input;\n  }\n\n  double operator()(const gsl::not_null<double*> result, const double input,\n                    const Scalar<DataVector>& my_scalar) {\n    CHECK(get(my_scalar) == 3.2);\n    *result = 3.0 * input;\n    return input;\n  }\n\n  static double apply(const gsl::not_null<double*> result,\n                      const gsl::not_null<double*> result2, const double input,\n                      const Scalar<DataVector>& my_scalar) {\n    CHECK(get(my_scalar) == 3.2);\n    *result = input;\n    *result2 = 2 * *result;\n    return 2.0 * input;\n  }\n\n  double operator()(const gsl::not_null<double*> result,\n                    const gsl::not_null<double*> result2, const double input,\n                    const Scalar<DataVector>& my_scalar) {\n    CHECK(get(my_scalar) == 3.2);\n    *result = 3.0 * input;\n    *result2 = 2 * *result;\n    return input;\n  }\n};\n\n// Test that desired tags are retrieved.\nvoid test_tagged_containers() {\n  const auto box = db::create<db::AddSimpleTags<Tags::MyDouble>>(1.2);\n  tuples::TaggedTuple<Tags::MyDouble2> tuple(4.4);\n  Variables<tmpl::list<Tags::MyScalar>> vars(10ul, 3.2);\n\n  CHECK(get<Tags::MyDouble>(box) == 1.2);\n  CHECK(get<Tags::MyDouble2>(tuple) == 4.4);\n  CHECK(get<Tags::MyScalar>(vars).get() == 3.2);\n\n  // Note: tests do not pass if DataVector only has a single component!\n  const auto box2 = db::create<db::AddSimpleTags<Tags::MyScalar>>(\n      Scalar<DataVector>{10ul, 1.2});\n\n  CHECK(get<Tags::MyScalar>(vars, box2).get() == 3.2);\n  CHECK(get<Tags::MyScalar>(box2, vars).get() == 1.2);\n\n  CHECK(get<Tags::MyScalar>(tuple, box2, vars).get() == 1.2);\n  CHECK(get<Tags::MyScalar>(box2, tuple, vars).get() == 1.2);\n  CHECK(get<Tags::MyScalar>(box2, vars, tuple).get() == 1.2);\n\n  CHECK(get<Tags::MyDouble>(box, tuple) == 1.2);\n  CHECK(get<Tags::MyDouble2>(tuple, vars) == 4.4);\n  CHECK(get<Tags::MyScalar>(vars, box).get() == 3.2);\n\n  CHECK(get<Tags::MyDouble>(tuple, box) == 1.2);\n  CHECK(get<Tags::MyDouble2>(vars, tuple) == 4.4);\n  CHECK(get<Tags::MyScalar>(box, vars).get() == 3.2);\n\n  CHECK(get<Tags::MyDouble>(box, tuple, vars) == 1.2);\n  CHECK(get<Tags::MyDouble2>(tuple, vars, box) == 4.4);\n  CHECK(get<Tags::MyScalar>(vars, box, tuple).get() == 3.2);\n\n  CHECK(get<Tags::MyDouble>(box, vars, tuple) == 1.2);\n  CHECK(get<Tags::MyDouble2>(tuple, box, vars) == 4.4);\n  CHECK(get<Tags::MyScalar>(vars, tuple, box).get() == 3.2);\n\n  INFO(\"Test invoke and apply\");\n  tuples::TaggedTuple<Tags::MyDouble, Tags::MyDouble2> tuple2(0.0, 0.0);\n\n  get<Tags::MyDouble2>(tuple) = 1.0;\n  CHECK(apply(make_not_null(&tuple), Function<0, 0>{}, box, vars) == 3.1);\n  CHECK(get<Tags::MyDouble2>(tuple) == 1.0);\n  get<Tags::MyDouble2>(tuple) = 0.0;\n  CHECK(invoke(make_not_null(&tuple), Function<0, 0>{}, box, vars) == 3.3);\n  CHECK(get<Tags::MyDouble2>(tuple) == 0.0);\n\n  CHECK(apply(make_not_null(&tuple), Function<0, 1>{}, box, vars) == 5.1);\n  CHECK(get<Tags::MyDouble2>(tuple) == 4.1);\n  get<Tags::MyDouble2>(tuple) = 0.0;\n  CHECK(invoke(make_not_null(&tuple), Function<0, 1>{}, box, vars) == 5.3);\n  CHECK(get<Tags::MyDouble2>(tuple) == 4.3);\n  get<Tags::MyDouble2>(tuple) = 1.0;\n\n  CHECK(apply(make_not_null(&tuple2), Function<0, 2>{}, box, vars) == 5.2);\n  CHECK(get<Tags::MyDouble2>(tuple2) == 4.2);\n  CHECK(get<Tags::MyDouble>(tuple2) == 8.4);\n  get<Tags::MyDouble>(tuple2) = get<Tags::MyDouble2>(tuple2) = 0.0;\n  CHECK(invoke(make_not_null(&tuple2), Function<0, 2>{}, box, vars) == 5.4);\n  CHECK(get<Tags::MyDouble2>(tuple2) == 4.4);\n  CHECK(get<Tags::MyDouble>(tuple2) == 8.8);\n  get<Tags::MyDouble>(tuple2) = get<Tags::MyDouble2>(tuple2) = 0.0;\n\n  get<Tags::MyDouble2>(tuple) = 1.0;\n  CHECK(apply(make_not_null(&tuple), Function<1, 0>{}, box, vars) ==\n        2.0 * get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple) == 1.0);\n  get<Tags::MyDouble2>(tuple) = 2.0;\n  CHECK(invoke(make_not_null(&tuple), Function<1, 0>{}, box, vars) ==\n        get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple) == 2.0);\n  get<Tags::MyDouble2>(tuple) = 0.0;\n\n  CHECK(apply(make_not_null(&tuple), Function<1, 1>{}, box, vars) ==\n        2.0 * get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple) == get<Tags::MyDouble>(box));\n  get<Tags::MyDouble2>(tuple) = 0.0;\n  CHECK(invoke(make_not_null(&tuple), Function<1, 1>{}, box, vars) ==\n        get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple) == 3.0 * get<Tags::MyDouble>(box));\n  get<Tags::MyDouble2>(tuple) = 0.0;\n\n  CHECK(apply(make_not_null(&tuple2), Function<1, 2>{}, box, vars) ==\n        2.0 * get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple2) == get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble>(tuple2) == 2.0 * get<Tags::MyDouble2>(tuple2));\n  get<Tags::MyDouble>(tuple2) = get<Tags::MyDouble2>(tuple2) = 0.0;\n  CHECK(invoke(make_not_null(&tuple2), Function<1, 2>{}, box, vars) ==\n        get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple2) == 3.0 * get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble>(tuple2) == 2.0 * get<Tags::MyDouble2>(tuple2));\n  get<Tags::MyDouble>(tuple2) = get<Tags::MyDouble2>(tuple2) = 0.0;\n\n  CHECK(apply(make_not_null(&tuple), Function<2, 0>{}, box, vars) ==\n        2.0 * get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple) == 0.0);\n  get<Tags::MyDouble2>(tuple) = 0.0;\n  CHECK(invoke(make_not_null(&tuple), Function<2, 0>{}, box, vars) ==\n        get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple) == 0.0);\n  get<Tags::MyDouble2>(tuple) = 0.0;\n\n  CHECK(apply(make_not_null(&tuple), Function<2, 1>{}, box, vars) ==\n        2.0 * get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple) == get<Tags::MyDouble>(box));\n  get<Tags::MyDouble2>(tuple) = 0.0;\n  CHECK(invoke(make_not_null(&tuple), Function<2, 1>{}, box, vars) ==\n        get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple) == 3.0 * get<Tags::MyDouble>(box));\n  get<Tags::MyDouble2>(tuple) = 0.0;\n\n  CHECK(apply(make_not_null(&tuple2), Function<2, 2>{}, box, vars) ==\n        2.0 * get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple2) == get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble>(tuple2) == 2.0 * get<Tags::MyDouble2>(tuple2));\n  get<Tags::MyDouble>(tuple2) = get<Tags::MyDouble2>(tuple2) = 0.0;\n  CHECK(invoke(make_not_null(&tuple2), Function<2, 2>{}, box, vars) ==\n        get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble2>(tuple2) == 3.0 * get<Tags::MyDouble>(box));\n  CHECK(get<Tags::MyDouble>(tuple2) == 2.0 * get<Tags::MyDouble2>(tuple2));\n  get<Tags::MyDouble>(tuple2) = get<Tags::MyDouble2>(tuple2) = 0.0;\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.TaggedContainers\",\n                  \"[Unit][DataStructures]\") {\n  test_tagged_containers();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_TaggedTuple.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace {\nstruct name {\n  using type = std::string;\n};\n\nstruct age {\n  using type = int;\n};\nstruct email {\n  using type = std::string;\n};\nstruct parents {\n  using type = std::vector<std::string>;\n};\n\nstruct not_streamable {\n  explicit not_streamable(int /*unused*/) {}\n  not_streamable() = default;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nconstexpr bool operator==(const not_streamable& /*unused*/,\n                          const not_streamable& /*unused*/) {\n  return true;\n}\n\nstruct non_copyable_grades {\n  using type = std::vector<std::unique_ptr<std::string>>;\n};\n\nstruct not_streamable_tag {\n  using type = not_streamable;\n};\n\n// [expand_tuple_example_function]\nstruct FirstArg {\n  using type = int;\n};\nstruct SecondArg {\n  using type = double;\n};\ndouble test_function(const int first_arg, const double second_arg) {\n  return static_cast<double>(first_arg) + second_arg;\n}\n// [expand_tuple_example_function]\n\nvoid test_general() {\n  // [construction_example]\n  tuples::TaggedTuple<name, age, email, parents, not_streamable_tag> test(\n      \"bla\", 17, \"bla@bla.bla\", std::vector<std::string>{\"Mom\", \"Dad\"}, 0);\n  // [construction_example]\n  {\n    // Test structured bindings\n    const auto& [name_v, age_v, email_v, parents_v, not_streamable_tag_v] =\n        test;\n    CHECK(name_v == get<name>(test));\n    CHECK(age_v == get<age>(test));\n    CHECK(email_v == get<email>(test));\n    CHECK(parents_v == get<parents>(test));\n    CHECK(not_streamable_tag_v == get<not_streamable_tag>(test));\n  }\n  {\n    // Test structured bindings\n    auto& [name_v, age_v, email_v, parents_v, not_streamable_tag_v] = test;\n    CHECK(name_v == get<name>(test));\n    CHECK(age_v == get<age>(test));\n    CHECK(email_v == get<email>(test));\n    CHECK(parents_v == get<parents>(test));\n    CHECK(not_streamable_tag_v == get<not_streamable_tag>(test));\n  }\n  static_assert(tuples::TaggedTuple<name, age, email>::size() == 3,\n                \"Failed to test size of TaggedTuple\");\n  {\n    const std::string expected_output =\n        \"TaggedTuple:\\n\"\n        \"----------\\n\"\n        \"Name:  (anonymous namespace)::name\\n\"\n        \"Type:  std::string\\n\"\n        \"Value: bla\\n\"\n        \"----------\\n\"\n        \"Name:  (anonymous namespace)::age\\n\"\n        \"Type:  int\\n\"\n        \"Value: 17\\n\"\n        \"----------\\n\"\n        \"Name:  (anonymous namespace)::email\\n\"\n        \"Type:  std::string\\n\"\n        \"Value: bla@bla.bla\\n\"\n        \"----------\\n\"\n        \"Name:  (anonymous namespace)::parents\\n\"\n        \"Type:  std::vector<std::string>\\n\"\n        \"Value: (Mom,Dad)\\n\"\n        \"----------\\n\"\n        \"Name:  (anonymous namespace)::not_streamable_tag\\n\"\n        \"Type:  (anonymous namespace)::not_streamable\\n\"\n        \"Value: UNSTREAMABLE\\n\";\n    std::stringstream ss;\n    ss << test;\n    CHECK(ss.str() == expected_output);\n  }\n  CHECK(test.size() == 5);\n  // [get_example]\n  CHECK(\"bla\" == tuples::get<name>(test));\n  CHECK(17 == tuples::get<age>(test));\n  CHECK(\"bla@bla.bla\" == tuples::get<email>(test));\n  // [get_example]\n  auto& name_temp = tuples::get<name>(test);\n  name_temp = \"Dennis\";\n  CHECK(tuples::get<name>(test) == \"Dennis\");\n  const auto& name_temp2 = tuples::get<name>(test);\n  CHECK(name_temp2 == \"Dennis\");\n  auto name_temp3 = std::move(tuples::get<name>(test));\n  CHECK(name_temp3 == \"Dennis\");\n\n  tuples::get<name>(test) = \"Eamonn\";\n  const auto test2 = test;\n  CHECK(17 == tuples::get<age>(test2));\n\n  {\n    const std::string expected_output = \"TaggedTuple:\\n\";\n    std::stringstream ss;\n    ss << tuples::TaggedTuple<>{};\n    CHECK(ss.str() == expected_output);\n  }\n\n  test_serialization(test2);\n\n  // Test reorder with non-const lvalue, and make sure\n  // that the non-const lvalue doesn't change.\n  const auto test3 = tuples::reorder<\n      tuples::TaggedTuple<email, not_streamable_tag, parents, age, name>>(test);\n  CHECK(test3 ==\n        tuples::TaggedTuple<email, not_streamable_tag, parents, age, name>{\n            \"bla@bla.bla\", 0, std::vector<std::string>{\"Mom\", \"Dad\"}, 17,\n            \"Eamonn\"});\n  CHECK(test ==\n        tuples::TaggedTuple<name, age, email, parents, not_streamable_tag>{\n            \"Eamonn\", 17, \"bla@bla.bla\", std::vector<std::string>{\"Mom\", \"Dad\"},\n            0});\n\n  // [reorder_example]\n  const auto test4 = tuples::reorder<\n      tuples::TaggedTuple<email, not_streamable_tag, parents, age, name>>(\n      std::move(test));\n  // [reorder_example]\n  CHECK(test4 ==\n        tuples::TaggedTuple<email, not_streamable_tag, parents, age, name>{\n            \"bla@bla.bla\", 0, std::vector<std::string>{\"Mom\", \"Dad\"}, 17,\n            \"Eamonn\"});\n\n  // [tagged_tuple_cat_example]\n  const tuples::TaggedTuple<name, age> test5(\"Eamonn\", 17);\n  const tuples::TaggedTuple<email, parents, not_streamable_tag> test6(\n      \"bla@bla.bla\", std::vector<std::string>{\"Mom\", \"Dad\"}, 0);\n  const auto test7 = tuples::tagged_tuple_cat(test5, test6);\n  // [tagged_tuple_cat_example]\n  CHECK(test7 ==\n        tuples::TaggedTuple<name, age, email, parents, not_streamable_tag>{\n            \"Eamonn\", 17, \"bla@bla.bla\", std::vector<std::string>{\"Mom\", \"Dad\"},\n            0});\n\n  // Test tagged_tuple_cat with a non-copyable type to ensure proper\n  // functionality of rvalue version.\n  std::vector<std::unique_ptr<std::string>> grades{};\n  grades.reserve(2);\n  grades.emplace_back(std::make_unique<std::string>(\"Aplus\"));\n  grades.emplace_back(std::make_unique<std::string>(\"F\"));\n  tuples::TaggedTuple<name, age> test8(\"Eamonn\", 17);\n  tuples::TaggedTuple<non_copyable_grades, parents, not_streamable_tag> test9(\n      std::move(grades), std::vector<std::string>{\"Mom\", \"Dad\"}, 0);\n  const auto test10 =\n      tuples::tagged_tuple_cat(std::move(test8), std::move(test9));\n  CHECK(*tuples::get<non_copyable_grades>(test10)[0] == \"Aplus\");\n\n  {\n    INFO(\"Expand tuple\");\n    // [expand_tuple_example]\n    const double extra_factor = 3.;\n    const double result = tuples::apply(\n        [&extra_factor](const auto&... expanded_args) {\n          return extra_factor * test_function(expanded_args...);\n        },\n        tuples::TaggedTuple<FirstArg, SecondArg>{1, 2.});\n    // [expand_tuple_example]\n    CHECK(result == 9.);\n    CHECK(1 == tuples::apply<tmpl::list<FirstArg>>(\n                   [](const int arg) { return arg; },\n                   tuples::TaggedTuple<FirstArg, SecondArg>{1, 2.}));\n    CHECK(0 == tuples::apply<tmpl::list<>>(\n                   []() { return 0; },\n                   tuples::TaggedTuple<FirstArg, SecondArg>{1, 2.}));\n  }\n}\n\nint global_of_no_default = 0;\nint global_time_mock = 0;\n\nstruct empty_base {};\nbool operator==(const empty_base& /*unused*/, const empty_base& /*unused*/) {\n  return true;\n}\nstruct no_default {\n  no_default() = delete;\n  explicit no_default(int i) { global_of_no_default = i; }\n};\nbool operator==(const no_default& /*unused*/, const no_default& /*unused*/) {\n  return true;\n}\n\nnamespace tags {\nstruct empty_base {\n  using type = ::empty_base;\n};\nstruct no_default {\n  using type = ::no_default;\n};\nstruct no_default2 {\n  using type = ::no_default;\n};\n}  // namespace tags\n\nnamespace tags {\n\nstruct Int {\n  using type = int;\n};\nstruct Int1 {\n  using type = int;\n};\nstruct Int2 {\n  using type = int;\n};\n\nstruct Short0 {\n  using type = short;\n};\n}  // namespace tags\n\ntemplate <class Tuple, class... Value>\nTuple test_impl(Value&&... value) {\n  Tuple tuple(std::forward<Value>(value)...);\n  return tuple;\n}\n\ntemplate <class Tag, class Tuple, class... Values>\nvoid check_get(Tuple& t, Values&&... values) {\n  CHECK((tuples::get<Tag>(static_cast<Tuple&>(t)) ==\n         typename Tag::type{values...}));\n  CHECK(tuples::get<Tag>(static_cast<const Tuple&>(t)) ==\n        typename Tag::type{values...});\n  CHECK(tuples::get<Tag>(static_cast<Tuple&&>(t)) ==\n        typename Tag::type{values...});\n  CHECK(tuples::get<Tag>(static_cast<const Tuple&&>(t)) ==\n        typename Tag::type{values...});\n}\n\nvoid test_no_tag() {\n  tuples::TaggedTuple<> dummy;\n  tuples::TaggedTuple<> dummy1;\n  dummy.swap(dummy1);\n  // Catch requires us to have at least one CHECK in each test\n  // The Unit.Utilities.TaggedTuple.NoTag does not need to check anything\n  CHECK(true);\n}\n\nvoid test_single_tag() {\n  const int i = 0;\n  {\n    auto t = test_impl<tuples::TaggedTuple<tags::Int>>(1);\n    CHECK(tuples::get<tags::Int>(t) == 1);\n  }\n  {\n    const auto t = test_impl<tuples::TaggedTuple<tags::Int>>(1);\n    CHECK(tuples::get<tags::Int>(t) == 1);\n  }\n\n  {\n    auto t = test_impl<tuples::TaggedTuple<tags::Int>>(1);\n    auto t2 = tuples::get<tags::Int>(std::move(t));\n    CHECK(t2 == 1);\n  }\n  {\n    const auto t = test_impl<tuples::TaggedTuple<tags::Int>>(1);\n    CHECK(tuples::get<tags::Int>(\n              static_cast<const tuples::TaggedTuple<tags::Int>&&>(t)) == 1);\n  }\n\n  {\n    auto t = test_impl<tuples::TaggedTuple<tags::Int>>(i);\n    CHECK(tuples::get<tags::Int>(t) == i);\n  }\n  {\n    const auto t = test_impl<tuples::TaggedTuple<tags::Int>>(i);\n    CHECK(tuples::get<tags::Int>(t) == i);\n  }\n\n  {\n    auto t = test_impl<tuples::TaggedTuple<tags::Int>>(i);\n    auto t2 = tuples::get<tags::Int>(std::move(t));\n    CHECK(t2 == i);\n  }\n  {\n    const auto t = test_impl<tuples::TaggedTuple<tags::Int>>(i);\n    CHECK(tuples::get<tags::Int>(\n              static_cast<const tuples::TaggedTuple<tags::Int>&&>(t)) == i);\n  }\n}\n\nvoid test_ebo() {\n  const int i = 1;\n  test_impl<tuples::TaggedTuple<tags::no_default>>(i);\n  CHECK(global_of_no_default == 1);\n\n  auto t =\n      test_impl<tuples::TaggedTuple<tags::no_default, tags::no_default2,\n                                    tags::empty_base>>(10, 100, empty_base{});\n  CHECK((global_of_no_default == 10 or global_of_no_default == 100));\n  check_get<tags::no_default>(t, 0);\n  CHECK(global_of_no_default == 0);\n  check_get<tags::no_default2>(t, 0);\n  CHECK(global_of_no_default == 0);\n\n  auto t2 = test_impl<tuples::TaggedTuple<tags::empty_base>>();\n  check_get<tags::empty_base>(t2);\n}\n\n// C++17 Draft 23.5.3.1 Construction\nstruct non_copyable {\n  explicit non_copyable() = default;\n  // clang-tidy: copy constructor should not be explicit\n  explicit non_copyable(non_copyable const&) = default;  // NOLINT\n  non_copyable& operator=(non_copyable const&) = delete;\n  // clang-tidy: move constructor should not be explicit\n  explicit non_copyable(non_copyable&&) = default;  // NOLINT\n  non_copyable& operator=(non_copyable&&) = default;\n  ~non_copyable() = default;\n};\n\nnamespace tags {\nstruct non_copyable {\n  using type = ::non_copyable;\n};\n}  // namespace tags\n\nvoid test_construction() {\n  {\n    // Test copy and move constructors\n    tuples::TaggedTuple<tags::Int, tags::Short0> t0{2, 9};\n    CHECK(tuples::get<tags::Int>(t0) == 2);\n    CHECK(tuples::get<tags::Short0>(t0) == 9);\n    tuples::TaggedTuple<tags::Int, tags::Short0> t1(t0);\n    CHECK(tuples::get<tags::Int>(t1) == 2);\n    CHECK(tuples::get<tags::Short0>(t1) == 9);\n    CHECK(t1 == t0);\n    tuples::TaggedTuple<tags::Int, tags::Short0> t2(std::move(t0));\n    CHECK(tuples::get<tags::Int>(t2) == 2);\n    CHECK(tuples::get<tags::Short0>(t2) == 9);\n    CHECK(t2 == t1);\n  }\n  {\n    // Construct one tuple from another where the types are convertible\n    // implicit conversion constructors\n    tuples::TaggedTuple<tags::Int, tags::Short0> t0{2, 9};\n    CHECK(tuples::get<tags::Int>(t0) == 2);\n    CHECK(tuples::get<tags::Short0>(t0) == 9);\n    tuples::TaggedTuple<tags::Int2, tags::Int1> t1(t0);\n    CHECK(tuples::get<tags::Int2>(t1) == 2);\n    CHECK(tuples::get<tags::Int1>(t1) == 9);\n    CHECK(tuples::get<tags::Int>(t0) == 2);\n    CHECK(tuples::get<tags::Short0>(t0) == 9);\n    tuples::TaggedTuple<tags::Int2, tags::Int1> t2(std::move(t0));\n    CHECK(tuples::get<tags::Int2>(t2) == 2);\n    CHECK(tuples::get<tags::Int1>(t2) == 9);\n    // explicit constructors\n    tuples::TaggedTuple<tags::non_copyable, tags::Int, tags::Short0> t3{\n        non_copyable{}, 2, 9};\n    CHECK(tuples::get<tags::Int>(t3) == 2);\n    CHECK(tuples::get<tags::Short0>(t3) == 9);\n    tuples::TaggedTuple<tags::non_copyable, tags::Int, tags::Int1> t4{t3};\n    CHECK(tuples::get<tags::Int>(t4) == 2);\n    CHECK(tuples::get<tags::Int1>(t4) == 9);\n    tuples::TaggedTuple<tags::non_copyable, tags::Int, tags::Int1> t5{\n        std::move(t3)};\n    CHECK(tuples::get<tags::Int>(t5) == 2);\n    CHECK(tuples::get<tags::Int1>(t5) == 9);\n  }\n}\n\n// C++17 Draft 23.5.3.2 Assignment\nvoid test_assignment() {\n  {\n    // Assignment from same type of TaggedTuple\n    tuples::TaggedTuple<tags::Int, tags::Short0> t0{2, 9};\n    CHECK(tuples::get<tags::Int>(t0) == 2);\n    CHECK(tuples::get<tags::Short0>(t0) == 9);\n    tuples::TaggedTuple<tags::Int, tags::Short0> t1{3, 4};\n    CHECK(tuples::get<tags::Int>(t1) == 3);\n    CHECK(tuples::get<tags::Short0>(t1) == 4);\n    t1 = t0;\n    CHECK(tuples::get<tags::Int>(t1) == 2);\n    CHECK(tuples::get<tags::Short0>(t1) == 9);\n    tuples::TaggedTuple<tags::Int, tags::Short0> t2{3, 4};\n    CHECK(tuples::get<tags::Int>(t2) == 3);\n    CHECK(tuples::get<tags::Short0>(t2) == 4);\n    t2 = std::move(t0);\n    CHECK(tuples::get<tags::Int>(t2) == 2);\n    CHECK(tuples::get<tags::Short0>(t2) == 9);\n    tuples::TaggedTuple<> t3{};\n    tuples::TaggedTuple<> t4{};\n    t3 = t4;\n  }\n  {\n    // Assignment from different type of TaggedTuple\n    tuples::TaggedTuple<tags::Int, tags::Short0> t0{2, 9};\n    CHECK(tuples::get<tags::Int>(t0) == 2);\n    CHECK(tuples::get<tags::Short0>(t0) == 9);\n    tuples::TaggedTuple<tags::Int2, tags::Int1> t1(3, 4);\n    CHECK(tuples::get<tags::Int2>(t1) == 3);\n    CHECK(tuples::get<tags::Int1>(t1) == 4);\n    t1 = t0;\n    CHECK(tuples::get<tags::Int2>(t1) == 2);\n    CHECK(tuples::get<tags::Int1>(t1) == 9);\n    tuples::TaggedTuple<tags::Int2, tags::Int1> t2(3, 4);\n    CHECK(tuples::get<tags::Int2>(t2) == 3);\n    CHECK(tuples::get<tags::Int1>(t2) == 4);\n    t2 = std::move(t0);\n    CHECK(tuples::get<tags::Int2>(t2) == 2);\n    CHECK(tuples::get<tags::Int1>(t2) == 9);\n  }\n}\n\n// C++17 Draft 23.5.3.8 Relational operators\nstruct NotNoExceptCompare {\n  explicit NotNoExceptCompare(int v) : v_(v) {}\n\n  int v_{0};\n};\nbool operator==(NotNoExceptCompare const& lhs, NotNoExceptCompare const& rhs) {\n  return lhs.v_ == rhs.v_;\n}\nbool operator!=(NotNoExceptCompare const& lhs, NotNoExceptCompare const& rhs) {\n  return not(lhs == rhs);\n}\n\nstruct timed_compare {\n  explicit timed_compare(int v) : v_(v) {}\n  int v_{0};\n};\nbool operator==(timed_compare const& lhs, timed_compare const& rhs) {\n  global_time_mock++;\n  return lhs.v_ == rhs.v_;\n}\n\nnamespace relational_tags {\nstruct Int0 {\n  using type = int;\n};\nstruct Int1 {\n  using type = int;\n};\nstruct Int2 {\n  using type = int;\n};\n\nstruct NotNoExceptCompare {\n  using type = ::NotNoExceptCompare;\n};\n\nstruct TimedCompare0 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare1 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare2 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare3 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare4 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare5 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare6 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare7 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare8 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare9 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare10 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare11 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare12 {\n  using type = ::timed_compare;\n};\nstruct TimedCompare13 {\n  using type = ::timed_compare;\n};\n}  // namespace relational_tags\n\nvoid test_equivalence() {\n  {\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t0{1, 4, 5};\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t1{1, 4, 5};\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t2{1, 3, 5};\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t3{2, 4, 5};\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t4{1, 4, 6};\n    CHECK(t0 == t1);\n    CHECK(t0 == t0);\n    CHECK_FALSE(t0 != t1);\n    CHECK_FALSE(t0 == t2);\n    CHECK(t0 != t2);\n    CHECK_FALSE(t0 == t3);\n    CHECK(t0 != t3);\n    CHECK_FALSE(t0 == t4);\n    CHECK(t0 != t4);\n    // Check that short circuiting in comparisons works correctly\n    tuples::TaggedTuple<\n        relational_tags::TimedCompare0, relational_tags::TimedCompare1,\n        relational_tags::TimedCompare2, relational_tags::TimedCompare3,\n        relational_tags::TimedCompare4, relational_tags::TimedCompare5,\n        relational_tags::TimedCompare6, relational_tags::TimedCompare7,\n        relational_tags::TimedCompare8, relational_tags::TimedCompare9,\n        relational_tags::TimedCompare10, relational_tags::TimedCompare11,\n        relational_tags::TimedCompare12, relational_tags::TimedCompare13>\n        t5{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};\n    tuples::TaggedTuple<\n        relational_tags::TimedCompare0, relational_tags::TimedCompare1,\n        relational_tags::TimedCompare2, relational_tags::TimedCompare3,\n        relational_tags::TimedCompare4, relational_tags::TimedCompare5,\n        relational_tags::TimedCompare6, relational_tags::TimedCompare7,\n        relational_tags::TimedCompare8, relational_tags::TimedCompare9,\n        relational_tags::TimedCompare10, relational_tags::TimedCompare11,\n        relational_tags::TimedCompare12, relational_tags::TimedCompare13>\n        t6{1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};\n    global_time_mock = 0;\n    CHECK_FALSE(t5 == t6);\n    CHECK(global_time_mock == 1);\n\n    global_time_mock = 0;\n    CHECK(t5 != t6);\n    CHECK(global_time_mock == 1);\n\n    tuples::TaggedTuple<\n        relational_tags::TimedCompare0, relational_tags::TimedCompare1,\n        relational_tags::TimedCompare2, relational_tags::TimedCompare3,\n        relational_tags::TimedCompare4, relational_tags::TimedCompare5,\n        relational_tags::TimedCompare6, relational_tags::TimedCompare7,\n        relational_tags::TimedCompare8, relational_tags::TimedCompare9,\n        relational_tags::TimedCompare10, relational_tags::TimedCompare11,\n        relational_tags::TimedCompare12, relational_tags::TimedCompare13>\n        t7{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14};\n    global_time_mock = 0;\n    CHECK_FALSE(t5 == t7);\n    CHECK(global_time_mock == 14);\n\n    global_time_mock = 0;\n    CHECK(t5 != t7);\n    CHECK(global_time_mock == 14);\n  }\n  CHECK(NotNoExceptCompare{1} == NotNoExceptCompare{1});\n  CHECK_FALSE(NotNoExceptCompare{1} == NotNoExceptCompare{0});\n  CHECK(NotNoExceptCompare{1} != NotNoExceptCompare{0});\n  CHECK_FALSE(NotNoExceptCompare{1} != NotNoExceptCompare{1});\n\n#if __cplusplus >= 201402L\n  {\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t0{1, 4, 5};\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t1{1, 4, 5};\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t2{1, 3, 5};\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t3{2, 4, 5};\n    constexpr tuples::TaggedTuple<relational_tags::Int0, relational_tags::Int1,\n                                  relational_tags::Int2>\n        t4{1, 4, 6};\n    static_assert(t0 == t1,\n                  \"Failed testing Unit.Utilities.TaggedTuple.relational\");\n    static_assert(not(t0 == t2),\n                  \"Failed testing Unit.Utilities.TaggedTuple.relational\");\n    static_assert(not(t0 == t3),\n                  \"Failed testing Unit.Utilities.TaggedTuple.relational\");\n    static_assert(not(t0 == t4),\n                  \"Failed testing Unit.Utilities.TaggedTuple.relational\");\n    static_assert(t0 != t2,\n                  \"Failed testing Unit.Utilities.TaggedTuple.relational\");\n    static_assert(t0 != t3,\n                  \"Failed testing Unit.Utilities.TaggedTuple.relational\");\n    static_assert(t0 != t4,\n                  \"Failed testing Unit.Utilities.TaggedTuple.relational\");\n  }\n#endif\n}\n\nstruct lex_time_compared {\n  explicit lex_time_compared(char c) : c_(c) {}\n  char c_;\n};\n\nbool operator<(lex_time_compared const& lhs, lex_time_compared const& rhs) {\n  global_time_mock++;\n  return lhs.c_ < rhs.c_;\n}\n\nnamespace relational_tags {\nstruct Char0 {\n  using type = char;\n};\nstruct Char1 {\n  using type = char;\n};\nstruct Char2 {\n  using type = char;\n};\n\nstruct LexTimeComp0 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp1 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp2 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp3 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp4 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp5 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp6 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp7 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp8 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp9 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp10 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp11 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp12 {\n  using type = lex_time_compared;\n};\nstruct LexTimeComp13 {\n  using type = lex_time_compared;\n};\n}  // namespace relational_tags\n\nvoid test_relational() {\n  {\n    // Check lexicographical comparison\n    tuples::TaggedTuple<relational_tags::Char0, relational_tags::Char1,\n                        relational_tags::Char2>\n        t0{'a', 'a', 'c'};\n    tuples::TaggedTuple<relational_tags::Char0, relational_tags::Char1,\n                        relational_tags::Char2>\n        t1{'a', 'a', 'd'};\n    tuples::TaggedTuple<relational_tags::Char0, relational_tags::Char1,\n                        relational_tags::Char2>\n        t2{'a', 'a', 'c'};\n    tuples::TaggedTuple<relational_tags::Char0, relational_tags::Char1,\n                        relational_tags::Char2>\n        t3{'a', 'a', 'b'};\n    CHECK(t0 < t1);\n    CHECK(t0 <= t1);\n    CHECK(t0 <= t2);\n\n    CHECK(t0 > t3);\n    CHECK(t0 >= t2);\n    CHECK(t0 >= t3);\n  }\n  {\n    // Check short circuiting works correctly\n    tuples::TaggedTuple<\n        relational_tags::LexTimeComp0, relational_tags::LexTimeComp1,\n        relational_tags::LexTimeComp2, relational_tags::LexTimeComp3,\n        relational_tags::LexTimeComp4, relational_tags::LexTimeComp5,\n        relational_tags::LexTimeComp6, relational_tags::LexTimeComp7,\n        relational_tags::LexTimeComp8, relational_tags::LexTimeComp9,\n        relational_tags::LexTimeComp10, relational_tags::LexTimeComp11,\n        relational_tags::LexTimeComp12, relational_tags::LexTimeComp13>\n        t0{'a', 'a', 'a', 'a', 'a', 'a', 'a',\n           'a', 'a', 'a', 'a', 'a', 'a', 'a'};\n    tuples::TaggedTuple<\n        relational_tags::LexTimeComp0, relational_tags::LexTimeComp1,\n        relational_tags::LexTimeComp2, relational_tags::LexTimeComp3,\n        relational_tags::LexTimeComp4, relational_tags::LexTimeComp5,\n        relational_tags::LexTimeComp6, relational_tags::LexTimeComp7,\n        relational_tags::LexTimeComp8, relational_tags::LexTimeComp9,\n        relational_tags::LexTimeComp10, relational_tags::LexTimeComp11,\n        relational_tags::LexTimeComp12, relational_tags::LexTimeComp13>\n        t1{'b', 'a', 'a', 'a', 'a', 'a', 'a',\n           'a', 'a', 'a', 'a', 'a', 'a', 'a'};\n    global_time_mock = 0;\n    CHECK(t0 < t1);\n    CHECK(global_time_mock == 1);\n    global_time_mock = 0;\n    CHECK_FALSE(t0 > t1);\n    CHECK(global_time_mock == 2);\n\n    tuples::TaggedTuple<\n        relational_tags::LexTimeComp0, relational_tags::LexTimeComp1,\n        relational_tags::LexTimeComp2, relational_tags::LexTimeComp3,\n        relational_tags::LexTimeComp4, relational_tags::LexTimeComp5,\n        relational_tags::LexTimeComp6, relational_tags::LexTimeComp7,\n        relational_tags::LexTimeComp8, relational_tags::LexTimeComp9,\n        relational_tags::LexTimeComp10, relational_tags::LexTimeComp11,\n        relational_tags::LexTimeComp12, relational_tags::LexTimeComp13>\n        t2{'a', 'a', 'a', 'a', 'a', 'a', 'a',\n           'a', 'a', 'a', 'a', 'a', 'a', 'a'};\n    global_time_mock = 0;\n    CHECK(t0 <= t2);\n    CHECK(global_time_mock == 28);\n  }\n#if __cplusplus >= 201402L\n  {\n    // Check constexpr lexicographical comparison\n    constexpr tuples::TaggedTuple<\n        relational_tags::Char0, relational_tags::Char1, relational_tags::Char2>\n        t0{'a', 'a', 'c'};\n    constexpr tuples::TaggedTuple<\n        relational_tags::Char0, relational_tags::Char1, relational_tags::Char2>\n        t1{'a', 'a', 'd'};\n    constexpr tuples::TaggedTuple<\n        relational_tags::Char0, relational_tags::Char1, relational_tags::Char2>\n        t2{'a', 'a', 'c'};\n    constexpr tuples::TaggedTuple<\n        relational_tags::Char0, relational_tags::Char1, relational_tags::Char2>\n        t3{'a', 'a', 'b'};\n    static_assert(t0 < t1, \"Failed testing relational operators\");\n    static_assert(t0 <= t1, \"Failed testing relational operators\");\n    static_assert(t0 <= t2, \"Failed testing relational operators\");\n\n    static_assert(t0 > t3, \"Failed testing relational operators\");\n    static_assert(t0 >= t2, \"Failed testing relational operators\");\n    static_assert(t0 >= t3, \"Failed testing relational operators\");\n  }\n#endif\n}\n\nstatic_assert(\n    tuples::tuple_size<\n        tuples::TaggedTuple<tags::no_default, tags::empty_base>>::value == 2,\n    \"Failed check tuple_size\");\nstatic_assert(\n    tuples::tuple_size<\n        const tuples::TaggedTuple<tags::no_default, tags::empty_base>>::value ==\n        2,\n    \"Failed check tuple_size\");\nstatic_assert(tuples::tuple_size<volatile tuples::TaggedTuple<\n                      tags::no_default, tags::empty_base>>::value == 2,\n              \"Failed check tuple_size\");\nstatic_assert(tuples::tuple_size<const volatile tuples::TaggedTuple<\n                      tags::no_default, tags::empty_base>>::value == 2,\n              \"Failed check tuple_size\");\n\nstatic_assert(tuples::tuple_size<tuples::TaggedTuple<>>::value == 0,\n              \"Failed check tuple_size\");\nstatic_assert(tuples::tuple_size<const tuples::TaggedTuple<>>::value == 0,\n              \"Failed check tuple_size\");\nstatic_assert(tuples::tuple_size<volatile tuples::TaggedTuple<>>::value == 0,\n              \"Failed check tuple_size\");\nstatic_assert(tuples::tuple_size<const volatile tuples::TaggedTuple<>>::value ==\n                  0,\n              \"Failed check tuple_size\");\n\n// C++17 Draft 23.5.3.3 swap\nstruct not_swappable {\n  explicit not_swappable(int v) : v_(v) {}\n  int v_;\n};\n\nstruct throws_swap {\n  throws_swap() = default;\n  explicit throws_swap(int v) : v_(v) {}\n\n  int v_{0};\n};\n\nvoid swap(throws_swap& lhs, throws_swap& rhs) {\n  using std::swap;\n  using tuples::swap;\n  swap(lhs.v_, rhs.v_);\n}\n\nint global_swappable_value = 0;\n\nstruct empty_base_swappable {};\n\nvoid swap(empty_base_swappable& /*lhs*/, empty_base_swappable& /*rhs*/) {\n  global_swappable_value++;\n}\n\nstruct empty_base_throws_swappable {};\n\nvoid swap(empty_base_throws_swappable& /*lhs*/,\n          empty_base_throws_swappable& /*rhs*/) {\n  global_swappable_value++;\n}\n\nstatic_assert(tuples::tuples_detail::is_swappable_with<double, double>::value,\n              \"Failed testing tuples::tuples_detail::is_swappable_with\");\nstatic_assert(tuples::tuples_detail::is_swappable_with<double&, double>::value,\n              \"Failed testing tuples::tuples_detail::is_swappable_with\");\nstatic_assert(tuples::tuples_detail::is_swappable_with<double&, double&>::value,\n              \"Failed testing tuples::tuples_detail::is_swappable_with\");\nstatic_assert(\n    not tuples::tuples_detail::is_swappable_with<double const&, double>::value,\n    \"Failed testing tuples::tuples_detail::is_swappable_with\");\n\nstatic_assert(\n    not tuples::tuples_detail::is_swappable_with<double, not_swappable>::value,\n    \"Failed testing tuples::tuples_detail::is_swappable_with\");\nstatic_assert(\n    not tuples::tuples_detail::is_swappable_with<double&, not_swappable>::value,\n    \"Failed testing tuples::tuples_detail::is_swappable_with\");\nstatic_assert(\n    not tuples::tuples_detail::is_swappable_with<double, not_swappable&>::value,\n    \"Failed testing tuples::tuples_detail::is_swappable_with\");\nstatic_assert(not tuples::tuples_detail::is_swappable_with<\n                  double&, not_swappable&>::value,\n              \"Failed testing tuples::tuples_detail::is_swappable_with\");\nstatic_assert(not tuples::tuples_detail::is_swappable_with<\n                  double const&, not_swappable>::value,\n              \"Failed testing tuples::tuples_detail::is_swappable_with\");\n\nstatic_assert(\n    tuples::tuples_detail::is_swappable_with<throws_swap, throws_swap>::value,\n    \"Failed testing tuples::tuples_detail::is_swappable_with\");\n\nnamespace swap_tags {\nstruct Int0 {\n  using type = int;\n};\nstruct Int1 {\n  using type = int;\n};\nstruct NotSwappable {\n  using type = not_swappable;\n};\n\nstruct ThrowsSwap {\n  using type = throws_swap;\n};\n\nstruct EmptyBaseSwap0 {\n  using type = empty_base_swappable;\n};\nstruct EmptyBaseSwap1 {\n  using type = empty_base_swappable;\n};\n\nstruct EmptyBaseThrowsSwap0 {\n  using type = empty_base_throws_swappable;\n};\nstruct EmptyBaseThrowsSwap1 {\n  using type = empty_base_throws_swappable;\n};\n}  // namespace swap_tags\n\nvoid test_swap() {\n  {\n    tuples::TaggedTuple<> t0{};\n    tuples::TaggedTuple<> t1{};\n    tuples::swap(t0, t1);\n  }\n  {\n    tuples::TaggedTuple<swap_tags::Int0, swap_tags::Int1> t0{1, 3};\n    tuples::TaggedTuple<swap_tags::Int0, swap_tags::Int1> t1{4, 5};\n    CHECK(tuples::get<swap_tags::Int0>(t0) == 1);\n    CHECK(tuples::get<swap_tags::Int1>(t0) == 3);\n    CHECK(tuples::get<swap_tags::Int0>(t1) == 4);\n    CHECK(tuples::get<swap_tags::Int1>(t1) == 5);\n    tuples::swap(t0, t1);\n    CHECK(tuples::get<swap_tags::Int0>(t1) == 1);\n    CHECK(tuples::get<swap_tags::Int1>(t1) == 3);\n    CHECK(tuples::get<swap_tags::Int0>(t0) == 4);\n    CHECK(tuples::get<swap_tags::Int1>(t0) == 5);\n  }\n  {\n    tuples::TaggedTuple<swap_tags::ThrowsSwap> t0{1};\n    tuples::TaggedTuple<swap_tags::ThrowsSwap> t1{2};\n    CHECK(tuples::get<swap_tags::ThrowsSwap>(t0).v_ == 1);\n    CHECK(tuples::get<swap_tags::ThrowsSwap>(t1).v_ == 2);\n    tuples::swap(t0, t1);\n    CHECK(tuples::get<swap_tags::ThrowsSwap>(t0).v_ == 2);\n    CHECK(tuples::get<swap_tags::ThrowsSwap>(t1).v_ == 1);\n  }\n  {\n    tuples::TaggedTuple<swap_tags::EmptyBaseSwap0, swap_tags::EmptyBaseSwap1>\n        t0;\n    tuples::TaggedTuple<swap_tags::EmptyBaseSwap0, swap_tags::EmptyBaseSwap1>\n        t1;\n    global_swappable_value = 0;\n    tuples::swap(t0, t1);\n    CHECK(global_swappable_value == 2);\n  }\n  {\n    tuples::TaggedTuple<swap_tags::EmptyBaseThrowsSwap0,\n                        swap_tags::EmptyBaseThrowsSwap1>\n        t0;\n    tuples::TaggedTuple<swap_tags::EmptyBaseThrowsSwap0,\n                        swap_tags::EmptyBaseThrowsSwap1>\n        t1;\n    global_swappable_value = 0;\n    tuples::swap(t0, t1);\n    CHECK(global_swappable_value == 2);\n  }\n}\n\nnamespace tags {\nstruct NonCopyable0 {\n  using type = std::vector<std::unique_ptr<int>>;\n};\nstruct NonCopyable1 {\n  using type = std::vector<std::unique_ptr<int>>;\n};\n}  // namespace tags\n\nvoid test_noncopyable() {\n  std::vector<std::unique_ptr<int>> a{};\n  a.reserve(2);\n  a.emplace_back(std::make_unique<int>(1));\n  a.emplace_back(std::make_unique<int>(3));\n  tuples::TaggedTuple<tags::NonCopyable0, tags::NonCopyable1> t(\n      std::move(a), tags::NonCopyable1::type{});\n  CHECK(*tuples::get<tags::NonCopyable0>(t)[0] == 1);\n  CHECK(*tuples::get<tags::NonCopyable0>(t)[1] == 3);\n}\n\nnamespace tags {\nstruct Copyable0 {\n  using type = std::vector<double>;\n};\nstruct Copyable1 {\n  using type = std::vector<double>;\n};\n}  // namespace tags\n\nvoid test_single_tag_constructor() {\n  {\n    std::vector<std::unique_ptr<int>> a{};\n    a.reserve(2);\n    a.emplace_back(std::make_unique<int>(1));\n    a.emplace_back(std::make_unique<int>(3));\n    tuples::TaggedTuple<tags::NonCopyable0> t0(std::move(a));\n    tuples::TaggedTuple<tags::NonCopyable1> t1(std::move(t0));\n    CHECK(*tuples::get<tags::NonCopyable1>(t1)[0] == 1);\n    CHECK(*tuples::get<tags::NonCopyable1>(t1)[1] == 3);\n  }\n  {\n    std::vector<double> a{1.0, 3.0};\n    tuples::TaggedTuple<tags::Copyable0> t0(a);\n    tuples::TaggedTuple<tags::Copyable1> t1(t0);\n    CHECK(tuples::get<tags::Copyable1>(t1) == a);\n  }\n  {\n    std::vector<double> a{1.0, 3.0};\n    tuples::TaggedTuple<tags::Copyable0> t0 = a;\n    tuples::TaggedTuple<tags::Copyable1> t1(t0);\n    CHECK(tuples::get<tags::Copyable1>(t1) == a);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.TaggedTuple\", \"[Utilities][Unit]\") {\n  test_general();\n  test_no_tag();\n  test_single_tag();\n  test_ebo();\n  test_construction();\n  test_assignment();\n  test_equivalence();\n  test_relational();\n  test_swap();\n  test_noncopyable();\n  test_single_tag_constructor();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_TaggedVariant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <functional>\n#include <memory>\n#include <type_traits>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n#if (not defined(__clang__) and __GNUC__ < 12) or \\\n    (defined(__clang__) and __clang__ < 15)\n#define LIMITED_CONSTEXPR\n#endif\n\nnamespace {\nnamespace Tags {\nstruct Int {\n  using type = int;\n};\n\ntemplate <typename T>\nstruct Templated {\n  using type = T;\n};\n\nstruct MoveOnly {\n  using type = std::unique_ptr<int>;\n};\n}  // namespace Tags\n\nstruct CreatedStruct {\n  struct Opt1 {\n    static constexpr Options::String help = \"First option\";\n    using type = int;\n  };\n  struct Opt2 {\n    static constexpr Options::String help = \"Second option\";\n    using type = int;\n  };\n  static constexpr Options::String help = \"The struct\";\n  using options = tmpl::list<Opt1, Opt2>;\n  CreatedStruct() = default;\n  CreatedStruct(const int opt1_in, const int opt2_in)\n      : opt1(opt1_in), opt2(opt2_in) {}\n  int opt1{};\n  int opt2{};\n};\n\nbool operator==(const CreatedStruct& a, const CreatedStruct& b) {\n  return a.opt1 == b.opt1 and a.opt2 == b.opt2;\n}\n\nnamespace OptionTags {\nstruct Integer {\n  static constexpr Options::String help = \"An integer\";\n  using type = int;\n};\n\nstruct Struct {\n  static constexpr Options::String help = \"A struct\";\n  using type = CreatedStruct;\n};\n}  // namespace OptionTags\n\nconstexpr variants::TaggedVariant<Tags::Int> simple_variant(\n    std::in_place_type<Tags::Int>, 4);\nstatic_assert(simple_variant.index() == 0);\nstatic_assert(not simple_variant.valueless_by_exception());\nstatic_assert(holds_alternative<Tags::Int>(simple_variant));\nstatic_assert(get<Tags::Int>(simple_variant) == 4);\n\n// [construct single]\nconstexpr variants::TaggedVariant<Tags::Int> simple_variant2(4);\n// [construct single]\nstatic_assert(simple_variant2.index() == 0);\nstatic_assert(not simple_variant2.valueless_by_exception());\nstatic_assert(holds_alternative<Tags::Int>(simple_variant2));\nstatic_assert(get<Tags::Int>(simple_variant2) == 4);\nstatic_assert(simple_variant == simple_variant2);\n\nstatic_assert(visit([](const std::pair<tmpl::type_<Tags::Int>, const int&>&\n                           entry) { return entry.second; },\n                    simple_variant) == 4);\n\n// [construct in_place_type]\nconstexpr variants::TaggedVariant<Tags::Int, Tags::Templated<double>>\n    int_variant(std::in_place_type<Tags::Int>, 1);\n// [construct in_place_type]\nstatic_assert(int_variant.index() == 0);\nstatic_assert(not int_variant.valueless_by_exception());\nstatic_assert(holds_alternative<Tags::Int>(int_variant));\nstatic_assert(not holds_alternative<Tags::Templated<double>>(int_variant));\nstatic_assert(get<Tags::Int>(int_variant) == 1);\nstatic_assert(visit(\n    [](const auto& entry) {\n      return std::is_same_v<typename std::decay_t<decltype(entry)>::first_type,\n                            tmpl::type_<Tags::Int>> and\n             entry.second == 1;\n    },\n    int_variant));\n\nconstexpr variants::TaggedVariant<Tags::Int, Tags::Templated<double>>\n    double_variant(std::in_place_type<Tags::Templated<double>>, 4.56);\nstatic_assert(double_variant.index() == 1);\nstatic_assert(not double_variant.valueless_by_exception());\nstatic_assert(holds_alternative<Tags::Templated<double>>(double_variant));\nstatic_assert(not holds_alternative<Tags::Int>(double_variant));\nstatic_assert(get<Tags::Templated<double>>(double_variant) == 4.56);\nstatic_assert(visit(\n    [](const auto& entry) {\n      return std::is_same_v<typename std::decay_t<decltype(entry)>::first_type,\n                            tmpl::type_<Tags::Templated<double>>> and\n             entry.second == 4.56;\n    },\n    double_variant));\n\nstatic_assert(visit(\n    [](const auto& entry1, const auto& entry2) {\n      return std::is_same_v<typename std::decay_t<decltype(entry1)>::first_type,\n                            tmpl::type_<Tags::Int>> and\n             std::is_same_v<typename std::decay_t<decltype(entry2)>::first_type,\n                            tmpl::type_<Tags::Templated<double>>> and\n             entry1.second == 1 and entry2.second == 4.56;\n    },\n    int_variant, double_variant));\n\nconstexpr variants::TaggedVariant<Tags::Int, Tags::Templated<double>>\n    double_variant2(std::in_place_type<Tags::Templated<double>>, -1.23);\n\n// NOLINTBEGIN(misc-redundant-expression)\nstatic_assert(int_variant == int_variant);\nstatic_assert(not(int_variant != int_variant));\nstatic_assert(not(int_variant < int_variant));\nstatic_assert(not(int_variant > int_variant));\nstatic_assert(int_variant <= int_variant);\nstatic_assert(int_variant >= int_variant);\n// NOLINTEND(misc-redundant-expression)\n\nstatic_assert(not(int_variant == double_variant));\nstatic_assert(int_variant != double_variant);\nstatic_assert(int_variant < double_variant);\nstatic_assert(not(int_variant > double_variant));\nstatic_assert(int_variant <= double_variant);\nstatic_assert(not(int_variant >= double_variant));\n\nstatic_assert(not(int_variant == double_variant2));\nstatic_assert(int_variant != double_variant2);\nstatic_assert(int_variant < double_variant2);\nstatic_assert(not(int_variant > double_variant2));\nstatic_assert(int_variant <= double_variant2);\nstatic_assert(not(int_variant >= double_variant2));\n\nstatic_assert(not(double_variant == double_variant2));\nstatic_assert(double_variant != double_variant2);\nstatic_assert(not(double_variant < double_variant2));\nstatic_assert(double_variant > double_variant2);\nstatic_assert(not(double_variant <= double_variant2));\nstatic_assert(double_variant >= double_variant2);\n\nstatic_assert(int_variant == variants::TaggedVariant<Tags::Int>(1));\nstatic_assert(not(int_variant != variants::TaggedVariant<Tags::Int>(1)));\nstatic_assert(int_variant != variants::TaggedVariant<Tags::Int>(2));\nstatic_assert(not(int_variant == variants::TaggedVariant<Tags::Int>(2)));\nstatic_assert(int_variant !=\n              variants::TaggedVariant<Tags::Templated<double>>(1.0));\nstatic_assert(not(int_variant ==\n                  variants::TaggedVariant<Tags::Templated<double>>(1.0)));\n\nstatic_assert(variants::TaggedVariant<Tags::Int>(1) == int_variant);\nstatic_assert(not(variants::TaggedVariant<Tags::Int>(1) != int_variant));\nstatic_assert(variants::TaggedVariant<Tags::Int>(2) != int_variant);\nstatic_assert(not(variants::TaggedVariant<Tags::Int>(2) == int_variant));\nstatic_assert(variants::TaggedVariant<Tags::Templated<double>>(1.0) !=\n              int_variant);\nstatic_assert(not(variants::TaggedVariant<Tags::Templated<double>>(1.0) ==\n                  int_variant));\n\nconstexpr variants::TaggedVariant<Tags::Int, Tags::Templated<double>>\n    default_variant{};\nstatic_assert(holds_alternative<Tags::Int>(default_variant));\n\nconstexpr variants::TaggedVariant<Tags::Int, Tags::Templated<double>>\n    converted_variant = variants::TaggedVariant<Tags::Templated<double>>(4.56);\nstatic_assert(converted_variant == double_variant);\nstatic_assert([]() {\n  variants::TaggedVariant<Tags::Int, Tags::Templated<double>> variant(\n      std::in_place_type<Tags::Templated<double>>, 4.56);\n  const variants::TaggedVariant<Tags::Int, Tags::Templated<double>>\n      assigned_variant = std::move(variant);\n  return converted_variant == assigned_variant;\n}());\nstatic_assert([]() {\n  variants::TaggedVariant<Tags::Templated<double>> variant(4.56);\n  const variants::TaggedVariant<Tags::Int, Tags::Templated<double>>\n      converted_variant2 = std::move(variant);\n  return converted_variant == converted_variant2;\n}());\n\nstatic_assert([]() {\n  // [convert]\n  variants::TaggedVariant<Tags::Int, Tags::Templated<double>> input(\n      std::in_place_type<Tags::Int>, 4);\n  variants::TaggedVariant<Tags::Templated<double>, Tags::Templated<int>,\n                          Tags::Int>\n      complex_converted(std::move(input));\n  return get<Tags::Int>(complex_converted) == 4;\n  // [convert]\n}());\n\nstatic_assert([]() {\n  variants::TaggedVariant<Tags::Int, Tags::Templated<double>> variant(\n      std::in_place_type<Tags::Templated<double>>, 1.23);\n  decltype(auto) lvalue_mut = get<Tags::Templated<double>>(variant);\n  decltype(auto) lvalue_const =\n      get<Tags::Templated<double>>(std::as_const(variant));\n  decltype(auto) rvalue_mut = get<Tags::Templated<double>>(std::move(variant));\n  decltype(auto) rvalue_const =\n  // NOLINTNEXTLINE(performance-move-const-arg)\n      get<Tags::Templated<double>>(std::move(std::as_const(variant)));\n  decltype(auto) if_mut = get_if<Tags::Templated<double>>(&variant);\n  decltype(auto) if_const =\n      get_if<Tags::Templated<double>>(&std::as_const(variant));\n\n  static_assert(std::is_same_v<decltype(lvalue_mut), double&>);\n  static_assert(std::is_same_v<decltype(lvalue_const), const double&>);\n  static_assert(std::is_same_v<decltype(rvalue_mut), double&&>);\n  static_assert(std::is_same_v<decltype(rvalue_const), const double&&>);\n  static_assert(std::is_same_v<decltype(if_mut), double*>);\n  static_assert(std::is_same_v<decltype(if_const), const double*>);\n\n  variants::TaggedVariant<Tags::Int, Tags::Templated<double>>* const null_mut =\n      nullptr;\n  const variants::TaggedVariant<Tags::Int, Tags::Templated<double>>* const\n      null_const = nullptr;\n\n  return lvalue_mut == 1.23 and &lvalue_mut == &lvalue_const and\n         &lvalue_mut == &rvalue_mut and &lvalue_mut == &rvalue_const and\n         &lvalue_mut == if_mut and &lvalue_mut == if_const and\n         get_if<Tags::Int>(&variant) == nullptr and\n         get_if<Tags::Int>(&std::as_const(variant)) == nullptr and\n         get_if<Tags::Int>(null_mut) == nullptr and\n         get_if<Tags::Int>(null_const) == nullptr;\n}());\n\nconstexpr bool test_emplace() {\n  // [emplace]\n  variants::TaggedVariant<Tags::Int, Tags::Templated<double>> variant{};\n  decltype(auto) emplace_result =\n      variant.emplace<Tags::Templated<double>>(1.23);\n  // [emplace]\n  static_assert(std::is_same_v<decltype(emplace_result), double&>);\n  return variant.index() == 1 and emplace_result == 1.23 and\n         &emplace_result == &get<Tags::Templated<double>>(variant);\n}\n#ifndef LIMITED_CONSTEXPR\n// Tested at runtime below\nstatic_assert(test_emplace());\n#endif\n\nstatic_assert([]() {\n  variants::TaggedVariant<Tags::Int> v{};\n  visit(\n      []<typename Entry>(const Entry& /*entry*/) {\n        static_assert(\n            std::is_same_v<Entry, std::pair<tmpl::type_<Tags::Int>, int&>>);\n      },\n      v);\n  return true;\n}());\nstatic_assert([]() {\n  const variants::TaggedVariant<Tags::Int> v{};\n  visit(\n      []<typename Entry>(const Entry& /*entry*/) {\n        static_assert(\n            std::is_same_v<Entry,\n                           std::pair<tmpl::type_<Tags::Int>, const int&>>);\n      },\n      v);\n  return true;\n}());\nstatic_assert([]() {\n  variants::TaggedVariant<Tags::Int> v{};\n  visit(\n      []<typename Entry>(const Entry& /*entry*/) {\n        static_assert(\n            std::is_same_v<Entry, std::pair<tmpl::type_<Tags::Int>, int&&>>);\n      },\n      std::move(v));\n  return true;\n}());\nstatic_assert([]() {\n  const variants::TaggedVariant<Tags::Int> v{};\n  visit(\n      []<typename Entry>(const Entry& /*entry*/) {\n        static_assert(\n            std::is_same_v<Entry,\n                           std::pair<tmpl::type_<Tags::Int>, const int&&>>);\n      },\n      std::move(v));  // NOLINT(performance-move-const-arg)\n  return true;\n}());\n\nstatic_assert([]() {\n  variants::TaggedVariant<Tags::Int> v1{};\n  variants::TaggedVariant<Tags::Templated<double>> v2{};\n  visit(\n      []<typename Entry1, typename Entry2>(const Entry1& /*entry1*/,\n                                           const Entry2& /*entry2*/) {\n        static_assert(\n            std::is_same_v<Entry1, std::pair<tmpl::type_<Tags::Int>, int&>>);\n        static_assert(\n            std::is_same_v<\n                Entry2,\n                std::pair<tmpl::type_<Tags::Templated<double>>, double&&>>);\n      },\n      v1, std::move(v2));\n  return true;\n}());\n\nstatic_assert(\n    visit<double>([](const auto& entry) { return entry.second; },\n                  variants::TaggedVariant<Tags::Int, Tags::Templated<double>>(\n                      std::in_place_type<Tags::Templated<double>>, 1.23)) ==\n    1.23);\n// Check this compiles\nstatic_assert(\n    (visit<void>([](const auto& entry) { return entry.second; },\n                 variants::TaggedVariant<Tags::Int, Tags::Templated<double>>(\n                     std::in_place_type<Tags::Templated<double>>, 1.23)),\n     true));\n\nstatic_assert(variants::visit([]() { return true; }));\nstatic_assert(variants::visit<bool>([]() { return true; }));\n\nstruct DerivedFromVariant : variants::TaggedVariant<Tags::Int> {};\nstatic_assert(variants::visit([](const auto& /*unused*/) { return true; },\n                              DerivedFromVariant{}));\n\nconstexpr bool test_swap() {\n  variants::TaggedVariant<Tags::Int, Tags::Templated<double>> v1(\n      std::in_place_type<Tags::Int>, 5);\n  variants::TaggedVariant<Tags::Int, Tags::Templated<double>> v2(\n      std::in_place_type<Tags::Templated<double>>, 1.23);\n  v1.swap(v2);\n  if (get<Tags::Templated<double>>(v1) != 1.23 or get<Tags::Int>(v2) != 5) {\n    return false;\n  }\n  using std::swap;\n  swap(v1, v2);\n  return get<Tags::Int>(v1) == 5 and get<Tags::Templated<double>>(v2) == 1.23;\n}\n#ifndef LIMITED_CONSTEXPR\n// Tested at runtime below\nstatic_assert(test_swap());\n#endif\n\nstruct ValuelessCausingType {\n  ValuelessCausingType() = default;\n  [[noreturn]] ValuelessCausingType(const ValuelessCausingType& /*other*/) {\n    throw 0;\n  }\n  ValuelessCausingType& operator=(const ValuelessCausingType&) = delete;\n  ~ValuelessCausingType() = default;\n};\n\nbool operator==(const ValuelessCausingType& /*a*/,\n                const ValuelessCausingType& /*b*/) {\n  return true;\n}\nbool operator<(const ValuelessCausingType& /*a*/,\n               const ValuelessCausingType& /*b*/) {\n  return false;\n}\n\nstruct ValuelessCausingTag {\n  using type = ValuelessCausingType;\n};\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.TaggedVariant\",\n                  \"[DataStructures][Unit]\") {\n  // Test stuff that can't be done in constexpr\n\n  // errors\n  {\n    const variants::TaggedVariant<Tags::Int, Tags::Templated<double>> variant(\n        std::in_place_type<Tags::Int>, 3);\n    CHECK_THROWS_AS(get<Tags::Templated<double>>(variant),\n                    std::bad_variant_access);\n  }\n\n  // Repeated type\n  {\n    const variants::TaggedVariant<Tags::Int, Tags::Templated<int>>\n        repeated_int_variant(std::in_place_type<Tags::Templated<int>>, 4);\n    CHECK(repeated_int_variant.index() == 1);\n    CHECK(not repeated_int_variant.valueless_by_exception());\n    CHECK(holds_alternative<Tags::Templated<int>>(repeated_int_variant));\n    CHECK(not holds_alternative<Tags::Int>(repeated_int_variant));\n    CHECK(get<Tags::Templated<int>>(repeated_int_variant) == 4);\n    CHECK_THROWS_AS(get<Tags::Int>(repeated_int_variant),\n                    std::bad_variant_access);\n  }\n\n  // move-only types (easier at runtime)\n  {\n    using Variant = variants::TaggedVariant<Tags::Int, Tags::MoveOnly>;\n    const Variant v1(std::in_place_type<Tags::MoveOnly>,\n                     std::make_unique<int>(1));\n    CHECK(*get<Tags::MoveOnly>(v1) == 1);\n    Variant v2(\n        variants::TaggedVariant<Tags::MoveOnly>(std::make_unique<int>(2)));\n    CHECK(*get<Tags::MoveOnly>(v2) == 2);\n    Variant v3{};\n    v3 = variants::TaggedVariant<Tags::MoveOnly>(std::make_unique<int>(3));\n    CHECK(*get<Tags::MoveOnly>(v3) == 3);\n    Variant v4(std::move(v3));\n    CHECK(*get<Tags::MoveOnly>(v4) == 3);\n    Variant v5{};\n    v5 = std::move(v4);\n    CHECK(*get<Tags::MoveOnly>(v5) == 3);\n    Variant v6{};\n    v6.emplace<Tags::MoveOnly>(std::make_unique<int>(6));\n    CHECK(*get<Tags::MoveOnly>(v6) == 6);\n\n    visit(\n        []<typename Tag>(const std::pair<tmpl::type_<Tag>,\n                                         const typename Tag::type&>& entry) {\n          CHECK(std::is_same_v<Tag, Tags::MoveOnly>);\n          if constexpr (std::is_same_v<Tag, Tags::MoveOnly>) {\n            CHECK(*entry.second == 1);\n          }\n        },\n        v1);\n    visit(\n        []<typename Tag>(\n            const std::pair<tmpl::type_<Tag>, typename Tag::type&&>& entry) {\n          CHECK(std::is_same_v<Tag, Tags::MoveOnly>);\n          if constexpr (std::is_same_v<Tag, Tags::MoveOnly>) {\n            CHECK(*entry.second == 6);\n          }\n        },\n        std::move(v6));\n  }\n\n  // valueless_by_exception\n  {\n    variants::TaggedVariant<Tags::Int, ValuelessCausingTag> valueless_variant{};\n    CHECK_THROWS_AS(\n        (valueless_variant =\n             variants::TaggedVariant<Tags::Int, ValuelessCausingTag>(\n                 std::in_place_type<ValuelessCausingTag>)),\n        int);\n    CHECK(valueless_variant.valueless_by_exception());\n    CHECK(valueless_variant.index() == std::variant_npos);\n    CHECK(not holds_alternative<Tags::Int>(valueless_variant));\n    CHECK(not holds_alternative<ValuelessCausingTag>(valueless_variant));\n    CHECK_THROWS_AS(get<Tags::Int>(valueless_variant), std::bad_variant_access);\n    CHECK_THROWS_AS(visit([](const auto& /*unused*/) {}, valueless_variant),\n                    std::bad_variant_access);\n\n    variants::TaggedVariant<Tags::Int, ValuelessCausingTag> good_variant(\n        std::in_place_type<Tags::Int>, 5);\n\n    CHECK(valueless_variant == valueless_variant);\n    CHECK_FALSE(valueless_variant != valueless_variant);\n    CHECK_FALSE(valueless_variant < valueless_variant);\n    CHECK_FALSE(valueless_variant > valueless_variant);\n    CHECK(valueless_variant <= valueless_variant);\n    CHECK(valueless_variant >= valueless_variant);\n\n    CHECK_FALSE(good_variant == valueless_variant);\n    CHECK(good_variant != valueless_variant);\n    CHECK_FALSE(good_variant < valueless_variant);\n    CHECK(good_variant > valueless_variant);\n    CHECK_FALSE(good_variant <= valueless_variant);\n    CHECK(good_variant >= valueless_variant);\n\n    CHECK_FALSE(valueless_variant == good_variant);\n    CHECK(valueless_variant != good_variant);\n    CHECK(valueless_variant < good_variant);\n    CHECK_FALSE(valueless_variant > good_variant);\n    CHECK(valueless_variant <= good_variant);\n    CHECK_FALSE(valueless_variant >= good_variant);\n  }\n\n  // hashing\n  {\n    using Variant = variants::TaggedVariant<Tags::Int, Tags::Templated<double>>;\n    const Variant v1(std::in_place_type<Tags::Int>, 1);\n    const Variant v2(std::in_place_type<Tags::Templated<double>>, 2.0);\n    const std::hash<Variant> h{};\n    CHECK(h(v1) != h(v2));\n  }\n\n  // visit example\n  {\n    // clang-tidy is complaining about a move in a loop.  Presumably\n    // the \"do while (false)\" in the CHECK macro.\n    // NOLINTBEGIN(bugprone-use-after-move)\n    // [visit]\n    const variants::TaggedVariant<Tags::Int, Tags::Templated<double>>\n        visit_example(std::in_place_type<Tags::Int>, 5);\n    CHECK(visit(\n              []<typename Tag>(\n                  const std::pair<tmpl::type_<Tag>, const typename Tag::type&>&\n                      entry) {\n                if constexpr (std::is_same_v<Tag, Tags::Int>) {\n                  // Deduced return types must match for all tags if\n                  // no template argument is given to visit.\n                  return static_cast<double>(entry.second + 1);\n                } else {\n                  static_assert(std::is_same_v<Tag, Tags::Templated<double>>);\n                  return entry.second;\n                }\n              },\n              visit_example) == 6.0);\n    variants::TaggedVariant<Tags::Int, Tags::Templated<double>> visit_example2(\n        std::in_place_type<Tags::Templated<double>>, 1.23);\n    CHECK(visit<double>(\n              []<typename Tag>(const std::pair<tmpl::type_<Tag>,\n                                               typename Tag::type&&>& entry) {\n                if constexpr (std::is_same_v<Tag, Tags::Int>) {\n                  // Implicitly converted to double because of\n                  // template argument of visit.\n                  return entry.second + 1;\n                } else {\n                  static_assert(std::is_same_v<Tag, Tags::Templated<double>>);\n                  return entry.second;\n                }\n              },\n              std::move(visit_example2)) == 1.23);\n    // [visit]\n    // NOLINTEND(bugprone-use-after-move)\n  }\n\n  test_serialization(\n      variants::TaggedVariant<Tags::Int, Tags::Templated<double>>(\n          std::in_place_type<Tags::Templated<double>>, 1.23));\n\n  // parsing\n  {\n    using Variant =\n        variants::TaggedVariant<OptionTags::Integer, OptionTags::Struct>;\n    CHECK(TestHelpers::test_creation<Variant>(\"Integer: 5\") ==\n          Variant(std::in_place_type<OptionTags::Integer>, 5));\n    CHECK(TestHelpers::test_creation<Variant>(\"Struct:\\n\"\n                                              \"  Opt1: 1\\n\"\n                                              \"  Opt2: 3\\n\") ==\n          Variant(std::in_place_type<OptionTags::Struct>, 1, 3));\n  }\n\n#ifdef LIMITED_CONSTEXPR\n  // Tested in static_asserts for other compilers.\n  CHECK(test_emplace());\n  CHECK(test_swap());\n#endif\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nclass ComplexDataVector;\nclass DataVector;\nclass ModalVector;\n\nnamespace {\nstruct ComplexScalarTag : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n  static std::string name() { return \"ComplexScalar\"; }\n};\n\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"Scalar\"; }\n};\n\ntemplate <size_t Dim>\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n  static std::string name() { return \"I<\" + std::to_string(Dim) + \">\"; }\n};\n\nvoid test_mean_tag() {\n  static_assert(\n      std::is_same_v<typename Tags::Mean<ScalarTag>::type, Scalar<double>>,\n      \"Failed testing Tags::Mean<ScalarTag>\");\n  static_assert(std::is_same_v<typename Tags::Mean<VectorTag<1>>::type,\n                               tnsr::I<double, 1>>,\n                \"Failed testing Tags::Mean<ScalarTag>\");\n  static_assert(std::is_same_v<typename Tags::Mean<VectorTag<3>>::type,\n                               tnsr::I<double, 3>>,\n                \"Failed testing Tags::Mean<ScalarTag>\");\n  TestHelpers::db::test_prefix_tag<Tags::Mean<ScalarTag>>(\"Mean(Scalar)\");\n  TestHelpers::db::test_prefix_tag<Tags::Mean<VectorTag<1>>>(\"Mean(I<1>)\");\n  TestHelpers::db::test_prefix_tag<Tags::Mean<VectorTag<3>>>(\"Mean(I<3>)\");\n}\n\nvoid test_modal_tag() {\n  static_assert(std::is_same_v<typename Tags::Modal<ScalarTag>::type,\n                               Scalar<ModalVector>>,\n                \"Failed testing Tags::Modal<ScalarTag>\");\n  static_assert(std::is_same_v<typename Tags::Modal<VectorTag<1>>::type,\n                               tnsr::I<ModalVector, 1>>,\n                \"Failed testing Tags::Modal<VectorTag<1>>\");\n  static_assert(std::is_same_v<typename Tags::Modal<VectorTag<3>>::type,\n                               tnsr::I<ModalVector, 3>>,\n                \"Failed testing Tags::Modal<VectorTag<3>>\");\n  TestHelpers::db::test_prefix_tag<Tags::Modal<ScalarTag>>(\"Modal(Scalar)\");\n  TestHelpers::db::test_prefix_tag<Tags::Modal<VectorTag<1>>>(\"Modal(I<1>)\");\n  TestHelpers::db::test_prefix_tag<Tags::Modal<VectorTag<3>>>(\"Modal(I<3>)\");\n}\n\nvoid test_spin_weighted_tag() {\n  static_assert(\n      std::is_same_v<\n          typename Tags::SpinWeighted<ComplexScalarTag,\n                                      std::integral_constant<int, 1>>::type,\n          Scalar<SpinWeighted<ComplexDataVector, 1>>>,\n      \"Failed testing Tags::SpinWeighted<ScalarTag>\");\n  TestHelpers::db::test_prefix_tag<Tags::SpinWeighted<ComplexScalarTag,\n                           std::integral_constant<int, -2>>>(\n        \"SpinWeighted(ComplexScalar, -2)\");\n}\n\nvoid test_multiplies_tag() {\n  using test_multiplies_tag = Tags::Multiplies<\n      Tags::SpinWeighted<ComplexScalarTag, std::integral_constant<int, 1>>,\n      Tags::SpinWeighted<ComplexScalarTag, std::integral_constant<int, -2>>>;\n  static_assert(\n      std::is_same_v<typename test_multiplies_tag::type,\n                     Scalar<SpinWeighted<ComplexDataVector, -1>>>,\n      \"Failed testing Tags::Multiplies for Tags::SpinWeighted operands\");\n  TestHelpers::db::test_prefix_tag<test_multiplies_tag>(\n        \"Multiplies(SpinWeighted(ComplexScalar, 1), \"\n        \"SpinWeighted(ComplexScalar, -2))\");\n}\n}  // namespace\n\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Tags\", \"[Unit][DataStructures]\") {\n  test_mean_tag();\n  test_modal_tag();\n  test_spin_weighted_tag();\n  test_multiplies_tag();\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_TempBuffer.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nvoid test_temp_buffer(const DataType& x) {\n  TempBuffer<tmpl::list<::Tags::TempI<0, 3, Frame::Inertial, DataType>,\n                        ::Tags::TempScalar<1, DataType>>>\n      buffer(get_size(x));\n\n  auto& vec = get<::Tags::TempI<0, 3, Frame::Inertial, DataType>>(buffer);\n  auto& scalar = get<::Tags::TempScalar<1, DataType>>(buffer);\n\n  // Do a few operations on the vector and scalar.\n  get<0>(vec) = x + 1.0;\n  get<1>(vec) = square(x);\n  get<2>(vec) = 6.0 * x;\n\n  scalar = magnitude(vec);\n\n  const auto expected_scalar = make_with_value<Scalar<DataType>>(x, 13.0);\n\n  // Get a separate handle `scalar2` to make sure that the value in\n  // the buffer has changed.\n  auto& scalar2 = get<::Tags::TempScalar<1, DataType>>(buffer);\n  CHECK_ITERABLE_APPROX(scalar2, expected_scalar);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.TempBuffer\", \"[DataStructures][Unit]\") {\n  test_temp_buffer(2.0);\n  test_temp_buffer(DataVector(5, 2.0));\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_Transpose.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <typename TagsList>\nclass Variables;\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct Var1 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Grid>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nusing two_vars = tmpl::list<Var1<Dim>, Var2>;\n\ntemplate <size_t Dim>\nusing one_var = tmpl::list<Var1<Dim>>;\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Transpose\", \"[DataStructures][Unit]\") {\n  // clang-format off\n  // [transpose_matrix]\n  const DataVector matrix{ 1.,  2.,  3.,\n                           4.,  5.,  6.,\n                           7.,  8.,  9.,\n                          10., 11., 12.};\n  CHECK(transpose(matrix, 3, 4) == DataVector{1.,  4.,  7., 10.,\n                                              2.,  5.,  8., 11.,\n                                              3.,  6.,  9., 12.});\n  // [transpose_matrix]\n  // clang-format on\n\n  DataVector transposed_matrix(12);\n  raw_transpose(make_not_null(transposed_matrix.data()), matrix.data(), 3, 4);\n  CHECK(transposed_matrix ==\n        DataVector{1., 4., 7., 10., 2., 5., 8., 11., 3., 6., 9., 12.});\n\n  // [return_transpose_example]\n  const size_t chunk_size = 8;\n  const size_t number_of_chunks = 2;\n  const size_t n_pts = chunk_size * number_of_chunks;\n  DataVector data(n_pts);\n  for (size_t i = 0; i < data.size(); ++i) {\n    data[i] = static_cast<double>(i * i);\n  }\n  DataVector transposed_data(n_pts, 0.);\n  transposed_data = transpose(data, chunk_size, number_of_chunks);\n  for (size_t i = 0; i < chunk_size; ++i) {\n    for (size_t j = 0; j < number_of_chunks; ++j) {\n      CHECK(data[i + chunk_size * j] ==\n            transposed_data[j + number_of_chunks * i]);\n    }\n  }\n  // [return_transpose_example]\n  std::fill(transposed_data.begin(), transposed_data.end(), 0.0);\n  DataVector ref_to_data;\n  ref_to_data.set_data_ref(&data);\n  DataVector ref_to_transposed_data;\n  ref_to_transposed_data.set_data_ref(&transposed_data);\n  CHECK(not ref_to_data.is_owning());\n  CHECK(not ref_to_transposed_data.is_owning());\n  ref_to_transposed_data = transpose(ref_to_data, chunk_size, number_of_chunks);\n  for (size_t i = 0; i < chunk_size; ++i) {\n    for (size_t j = 0; j < number_of_chunks; ++j) {\n      // clang-tidy: pointer arithmetic\n      CHECK(ref_to_data[i + chunk_size * j] ==                  // NOLINT\n            ref_to_transposed_data[j + number_of_chunks * i]);  // NOLINT\n      CHECK(data[i + chunk_size * j] ==                         // NOLINT\n            ref_to_transposed_data[j + number_of_chunks * i]);  // NOLINT\n    }\n  }\n\n  // [transpose_by_not_null_example]\n  const size_t chunk_size_vars = 8;\n  const size_t n_grid_pts = 2 * chunk_size_vars;\n  Variables<two_vars<2>> variables(n_grid_pts, 0.);\n  for (size_t i = 0; i < variables.size(); ++i) {\n    // clang-tidy: pointer arithmetic\n    variables.data()[i] = i * i;  // NOLINT\n  }\n  const size_t number_of_chunks_vars = variables.size() / chunk_size_vars;\n  auto transposed_vars = variables;\n  transpose(make_not_null(&transposed_vars), variables, chunk_size_vars,\n            number_of_chunks_vars);\n  for (size_t i = 0; i < chunk_size_vars; ++i) {\n    for (size_t j = 0; j < number_of_chunks_vars; ++j) {\n      // clang-tidy: pointer arithmetic\n      CHECK(variables.data()[i + chunk_size_vars * j] ==             // NOLINT\n            transposed_vars.data()[j + number_of_chunks_vars * i]);  // NOLINT\n    }\n  }\n  // [transpose_by_not_null_example]\n\n  // [partial_transpose_example]\n  Variables<one_var<2>> partial_vars(n_grid_pts, 0.);\n  get<Var1<2>>(partial_vars) = get<Var1<2>>(variables);\n  Variables<one_var<2>> partial_transpose(n_grid_pts, 0.);\n  const size_t partial_number_of_chunks = 2 * number_of_chunks_vars / 3;\n  transpose(make_not_null(&partial_transpose), variables, chunk_size_vars,\n            partial_number_of_chunks);\n  for (size_t i = 0; i < chunk_size_vars; ++i) {\n    for (size_t j = 0; j < partial_number_of_chunks; ++j) {\n      // clang-tidy: pointer arithmetic\n      CHECK(partial_transpose\n                .data()[j + partial_number_of_chunks * i] ==  // NOLINT\n            variables.data()[i + chunk_size_vars * j]);       // NOLINT\n      CHECK(partial_transpose\n                .data()[j + partial_number_of_chunks * i] ==  // NOLINT\n            partial_vars.data()[i + chunk_size_vars * j]);    // NOLINT\n    }\n  }\n  // [partial_transpose_example]\n\n  const auto another_partial_transpose =\n      transpose<Variables<two_vars<2>>, Variables<one_var<2>>>(\n          variables, chunk_size_vars, partial_number_of_chunks);\n  for (size_t i = 0; i < chunk_size_vars; ++i) {\n    for (size_t j = 0; j < partial_number_of_chunks; ++j) {\n      // clang-tidy: pointer arithmetic\n      CHECK(another_partial_transpose\n                .data()[j + partial_number_of_chunks * i] ==  // NOLINT\n            variables.data()[i + chunk_size_vars * j]);       // NOLINT\n      CHECK(another_partial_transpose\n                .data()[j + partial_number_of_chunks * i] ==  // NOLINT\n            partial_vars.data()[i + chunk_size_vars * j]);    // NOLINT\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_Variables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <random>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/MathWrapper.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\nstatic_assert(\n    std::is_move_constructible<\n        Variables<tmpl::list<TestHelpers::Tags::Scalar<DataVector>,\n                             TestHelpers::Tags::Vector<DataVector>>>>::value);\n\nstatic_assert(not std::is_convertible_v<\n              DataVector,\n              Variables<tmpl::list<TestHelpers::Tags::Scalar<DataVector>>>>);\n\nnamespace {\nstd::string repeat_string_with_commas(const std::string& str,\n                                      const size_t repeats) {\n  MakeString t;\n  for (size_t i = 0; i < repeats - 1; ++i) {\n    t << str << \",\";\n  }\n  t << str;\n  return t;\n}\n\n// An empty Variables is a separate implementation, so we put tests\n// for it in a separate function here.\nvoid test_empty_variables() {\n  Variables<tmpl::list<>> empty_vars;\n  auto serialized_empty_vars = serialize_and_deserialize(empty_vars);\n  CHECK(serialized_empty_vars == empty_vars);\n\n  // The following test with a std::tuple<Variables<tmpl::list<>>>\n  // revealed a gcc8 bug that passed the above test (the one that\n  // serializes/deserializes a Variables<tmpl::list<>>).\n  std::tuple<int, std::string, Variables<tmpl::list<>>> tuple_with_empty_vars{\n      3, \"hello\", {}};\n  auto serialized_tuple_with_empty_vars =\n      serialize_and_deserialize(tuple_with_empty_vars);\n  CHECK(serialized_tuple_with_empty_vars == tuple_with_empty_vars);\n\n  CHECK(get_output(tuple_with_empty_vars) == \"(3,hello,{})\");\n\n  CHECK(not contains_allocations(empty_vars));\n  // Functions do nothing, but need to compile\n  empty_vars.initialize(0);\n  const Variables<tmpl::list<>> input_vars;\n  empty_vars.assign_subset(input_vars);\n  CHECK(empty_vars.number_of_grid_points() == 0);\n  CHECK(empty_vars.size() == 0);\n}\n\ntemplate <typename VectorType>\nvoid test_variables_construction_and_access() {\n  using value_type = typename VectorType::value_type;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points = sdist(gen);\n  // Test constructed and member function initialized Variables are identical\n  const auto initial_fill_value = make_with_random_values<value_type>(\n      make_not_null(&gen), make_not_null(&dist));\n\n  using VariablesType =\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                           TestHelpers::Tags::Scalar<VectorType>,\n                           TestHelpers::Tags::Scalar2<VectorType>>>;\n\n  VariablesType filled_variables{number_of_grid_points, initial_fill_value};\n\n  VariablesType initialized_variables;\n  initialized_variables.initialize(number_of_grid_points, initial_fill_value);\n  CHECK(filled_variables == initialized_variables);\n\n  CHECK(filled_variables.size() ==\n        filled_variables.number_of_grid_points() *\n            filled_variables.number_of_independent_components);\n  CHECK(number_of_grid_points == filled_variables.number_of_grid_points());\n  CHECK(number_of_grid_points * 5 == filled_variables.size());\n  for (size_t i = 0; i < filled_variables.size(); ++i) {\n    // clang-tidy: do not use pointer arithmetic\n    CHECK(initial_fill_value == filled_variables.data()[i]);  // NOLINT\n  }\n  // Test that assigning to a tensor pointing into a Variables correctly sets\n  // the Variables\n  auto& tensor_in_filled_variables =\n      get<TestHelpers::Tags::Vector<VectorType>>(filled_variables);\n  CHECK(VectorType{number_of_grid_points, initial_fill_value} ==\n        get<0>(tensor_in_filled_variables));\n  CHECK(VectorType{number_of_grid_points, initial_fill_value} ==\n        get<1>(tensor_in_filled_variables));\n  CHECK(VectorType{number_of_grid_points, initial_fill_value} ==\n        get<2>(tensor_in_filled_variables));\n\n  {\n    // Test structured bindings\n    const auto& [vector, scalar1, scalar2] = filled_variables;\n    CHECK(get(scalar1).data() ==\n          get(get<TestHelpers::Tags::Scalar<VectorType>>(filled_variables))\n              .data());\n    CHECK(get(scalar2).data() ==\n          get(get<TestHelpers::Tags::Scalar2<VectorType>>(filled_variables))\n              .data());\n    for (size_t i = 0; i < 3; ++i) {\n      CAPTURE(i);\n      CHECK(vector.get(i).data() ==\n            get<TestHelpers::Tags::Vector<VectorType>>(filled_variables)\n                .get(i)\n                .data());\n    }\n  }\n\n  const auto value_for_reference_assignment =\n      make_with_random_values<value_type>(make_not_null(&gen),\n                                          make_not_null(&dist));\n\n  tnsr::I<VectorType, 3> tensor_for_reference_assignment{\n      number_of_grid_points, value_for_reference_assignment};\n  tensor_in_filled_variables = tensor_for_reference_assignment;\n\n  // clang-tidy: do not use pointer arithmetic\n  for (size_t i = 0; i < number_of_grid_points * 3; ++i) {\n    CHECK(value_for_reference_assignment ==\n          filled_variables.data()[i]);  // NOLINT\n  }\n  CHECK(VectorType{number_of_grid_points, value_for_reference_assignment} ==\n        get<0>(tensor_in_filled_variables));\n  CHECK(VectorType{number_of_grid_points, value_for_reference_assignment} ==\n        get<1>(tensor_in_filled_variables));\n  CHECK(VectorType{number_of_grid_points, value_for_reference_assignment} ==\n        get<2>(tensor_in_filled_variables));\n\n  const auto value_for_tensor_constructor = make_with_random_values<value_type>(\n      make_not_null(&gen), make_not_null(&dist));\n\n  tensor_in_filled_variables = tnsr::I<VectorType, 3>{\n      number_of_grid_points, value_for_tensor_constructor};\n\n  // clang-tidy: do not use pointer arithmetic\n  for (size_t i = 0; i < number_of_grid_points * 3; ++i) {\n    CHECK(value_for_tensor_constructor ==\n          filled_variables.data()[i]);  // NOLINT\n  }\n  CHECK(VectorType{number_of_grid_points, value_for_tensor_constructor} ==\n        get<0>(tensor_in_filled_variables));\n  CHECK(VectorType{number_of_grid_points, value_for_tensor_constructor} ==\n        get<1>(tensor_in_filled_variables));\n  CHECK(VectorType{number_of_grid_points, value_for_tensor_constructor} ==\n        get<2>(tensor_in_filled_variables));\n\n  // Test const vector in variables points to right values\n  const auto& const_tensor_in_filled_variables =\n      get<TestHelpers::Tags::Vector<VectorType>>(filled_variables);\n  CHECK(get<0>(const_tensor_in_filled_variables) ==\n        VectorType{number_of_grid_points, value_for_tensor_constructor});\n  CHECK(get<1>(const_tensor_in_filled_variables) ==\n        VectorType{number_of_grid_points, value_for_tensor_constructor});\n  CHECK(get<2>(const_tensor_in_filled_variables) ==\n        VectorType{number_of_grid_points, value_for_tensor_constructor});\n\n  // Test equivalence and inequivalence operators\n  VariablesType another_filled_variables{number_of_grid_points,\n                                         initial_fill_value};\n  // handle case of same value rolled twice on the random number generators\n  CHECK((filled_variables == another_filled_variables) ==\n        (initial_fill_value == value_for_tensor_constructor));\n  another_filled_variables = filled_variables;\n  CHECK(another_filled_variables == filled_variables);\n\n  // Test default constructed variables is allowed\n  VariablesType default_variables;\n\n  CHECK(default_variables.size() == 0);\n  CHECK(default_variables.number_of_grid_points() == 0);\n  default_variables = another_filled_variables;\n  CHECK(another_filled_variables == default_variables);\n\n  std::string expected_output_initial = repeat_string_with_commas(\n      get_output(initial_fill_value), number_of_grid_points);\n\n  std::string expected_output_tensor = repeat_string_with_commas(\n      get_output(value_for_tensor_constructor), number_of_grid_points);\n\n  // Test stream operator\n  const std::string expected_output =\n      \"Vector:\\n\"\n      \"T(0)=(\" +\n      expected_output_tensor +\n      \")\\n\"\n      \"T(1)=(\" +\n      expected_output_tensor +\n      \")\\n\"\n      \"T(2)=(\" +\n      expected_output_tensor +\n      \")\\n\\n\"\n      \"Scalar:\\n\"\n      \"T()=(\" +\n      expected_output_initial +\n      \")\\n\\n\"\n      \"Scalar2:\\n\"\n      \"T()=(\" +\n      expected_output_initial + \")\";\n  CHECK(get_output(filled_variables) == expected_output);\n\n  // Check self-assignment\n#if defined(__clang__) && __clang_major__ > 6\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wself-assign-overloaded\"\n#endif  // defined(__clang__) && __clang_major__ > 6\n  filled_variables = filled_variables;  // NOLINT\n#if defined(__clang__) && __clang_major__ > 6\n#pragma GCC diagnostic pop\n#endif  // defined(__clang__) && __clang_major__ > 6\n  CHECK(filled_variables == another_filled_variables);\n\n  TestHelpers::db::test_simple_tag<\n      Tags::Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                 TestHelpers::Tags::Scalar<VectorType>,\n                                 TestHelpers::Tags::Scalar2<VectorType>>>>(\n      \"Variables(Vector,Scalar,Scalar2)\");\n}\n\ntemplate <typename VectorType>\nvoid test_variables_move() {\n  using value_type = typename VectorType::value_type;\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points1 = sdist(gen);\n  const size_t number_of_grid_points2 = sdist(gen);\n  const auto initial_fill_values =\n      make_with_random_values<std::array<value_type, 2>>(make_not_null(&gen),\n                                                         make_not_null(&dist));\n\n  // Test moving with assignment operator\n  {\n    // non-owning semantics\n    std::vector<value_type> non_owning_data_to(\n        number_of_grid_points1 * Vars::number_of_independent_components,\n        initial_fill_values[0]);\n    Vars non_owning_move_to(non_owning_data_to.data(),\n                            non_owning_data_to.size());\n    std::vector<value_type> non_owning_data_from(\n        number_of_grid_points2 * Vars::number_of_independent_components,\n        initial_fill_values[1]);\n    Vars non_owning_move_from(non_owning_data_from.data(),\n                              non_owning_data_from.size());\n\n    // Test that copying a non-owning provides an owning\n    Vars copied_non_owning{non_owning_move_to};\n    CHECK(copied_non_owning.is_owning());\n    CHECK(not non_owning_move_to.is_owning());\n    CHECK(copied_non_owning.size() == non_owning_move_to.size());\n    CHECK(copied_non_owning.number_of_grid_points() ==\n          non_owning_move_to.number_of_grid_points());\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              copied_non_owning)[0][0] == copied_non_owning.data());\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              copied_non_owning)[0][0] != non_owning_move_from.data());\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              copied_non_owning)[0][0] != non_owning_move_to.data());\n\n    copied_non_owning = non_owning_move_from;\n    CHECK(copied_non_owning.is_owning());\n    CHECK(not non_owning_move_from.is_owning());\n    CHECK(copied_non_owning.size() == non_owning_move_from.size());\n    CHECK(copied_non_owning.number_of_grid_points() ==\n          non_owning_move_from.number_of_grid_points());\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              copied_non_owning)[0][0] == copied_non_owning.data());\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              copied_non_owning)[0][0] != non_owning_move_from.data());\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              copied_non_owning)[0][0] != non_owning_move_to.data());\n\n    Vars initializer_move_to = std::move(non_owning_move_to);\n    CHECK(not initializer_move_to.is_owning());\n    CHECK(initializer_move_to ==\n          Vars{number_of_grid_points1, initial_fill_values[0]});\n    CHECK(initializer_move_to.data() == non_owning_data_to.data());\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              initializer_move_to)[0][0] == non_owning_data_to.data());\n    CHECK(non_owning_move_to.is_owning());\n    CHECK(non_owning_move_to.size() == 0);\n\n    non_owning_move_to = std::move(non_owning_move_from);\n    CHECK(non_owning_move_from.is_owning());\n    CHECK(non_owning_move_from.size() == 0);\n    CHECK(not non_owning_move_to.is_owning());\n    CHECK(non_owning_move_to ==\n          Vars{number_of_grid_points2, initial_fill_values[1]});\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              non_owning_move_to)[0][0] == non_owning_data_from.data());\n\n    // Intentionally testing self-move\n#if defined(__clang__) or __GNUC__ >= 13\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wself-move\"\n#endif\n    non_owning_move_to = std::move(non_owning_move_to);\n#if defined(__clang__) or __GNUC__ >= 13\n#pragma GCC diagnostic pop\n#endif\n    // clang-tidy: false positive 'move_to' used after it was moved\n    CHECK(non_owning_move_to ==  // NOLINT\n          Vars{number_of_grid_points2, initial_fill_values[1]});\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              non_owning_move_to)[0][0] == non_owning_data_from.data());\n    CHECK(not non_owning_move_to.is_owning());\n\n    // Test set_data_ref case that wasn't implicitly tested above.\n    Vars non_owning_set_data{};\n    non_owning_set_data.set_data_ref(make_not_null(&non_owning_move_to));\n    CHECK(non_owning_set_data.data() == non_owning_data_from.data());\n    CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n              non_owning_set_data)[0][0] == non_owning_data_from.data());\n    CHECK(not non_owning_set_data.is_owning());\n  }\n\n  Vars move_to{number_of_grid_points1, initial_fill_values[0]};\n  Vars move_from{number_of_grid_points2, initial_fill_values[1]};\n\n  Vars initializer_move_to = std::move(move_to);\n  CHECK(initializer_move_to ==\n        Vars{number_of_grid_points1, initial_fill_values[0]});\n  CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(\n            initializer_move_to)[0][0] == initializer_move_to.data());\n\n  move_to = std::move(move_from);\n  CHECK(move_to == Vars{number_of_grid_points2, initial_fill_values[1]});\n  CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(move_to)[0][0] ==\n        move_to.data());\n\n  // Intentionally testing self-move\n#if defined(__clang__) or __GNUC__ >= 13\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wself-move\"\n#endif\n  move_to = std::move(move_to);\n#if defined(__clang__) or __GNUC__ >= 13\n#pragma GCC diagnostic pop\n#endif\n  // clang-tidy: false positive 'move_to' used after it was moved\n  CHECK(move_to ==  // NOLINT\n        Vars{number_of_grid_points2, initial_fill_values[1]});\n  CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(move_to)[0][0] ==\n        move_to.data());\n\n  move_to = Vars{number_of_grid_points2, initial_fill_values[0]};\n  CHECK(move_to == Vars{number_of_grid_points2, initial_fill_values[0]});\n  CHECK(&get<TestHelpers::Tags::Vector<VectorType>>(move_to)[0][0] ==\n        move_to.data());\n\n  move_to = Vars{};\n  CHECK(move_to.size() == 0);\n  Vars default_for_construction{};\n  const auto constructed_from_default = std::move(default_for_construction);\n  CHECK(constructed_from_default.size() == 0);\n\n  {\n    Vars reuse_after_move{number_of_grid_points1, initial_fill_values[0]};\n    { Vars constructed{std::move(reuse_after_move)}; }\n    // NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move)\n    reuse_after_move.initialize(number_of_grid_points1, initial_fill_values[1]);\n    CHECK(reuse_after_move ==\n          Vars{number_of_grid_points1, initial_fill_values[1]});\n    CHECK(get<0>(get<TestHelpers::Tags::Vector<VectorType>>(\n              reuse_after_move))[0] == initial_fill_values[1]);\n\n    {\n      Vars assigned{};\n      assigned = std::move(reuse_after_move);\n    }\n    // NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move)\n    reuse_after_move.initialize(number_of_grid_points1, initial_fill_values[0]);\n    CHECK(reuse_after_move ==\n          Vars{number_of_grid_points1, initial_fill_values[0]});\n    CHECK(get<0>(get<TestHelpers::Tags::Vector<VectorType>>(\n              reuse_after_move))[0] == initial_fill_values[0]);\n  }\n\n  {\n    Vars size_one{1, initial_fill_values[0]};\n    Vars size_two{2, initial_fill_values[0]};\n    const value_type* size_one_data = size_one.data();\n    const value_type* size_two_data = size_two.data();\n    Vars size_one_moved(std::move(size_one));\n    Vars size_two_moved(std::move(size_two));\n    CHECK(size_one_moved.data() != size_one_data);\n    CHECK(size_two_moved.data() == size_two_data);\n  }\n}\n\ntemplate <typename VectorType>\nvoid test_variables_math() {\n  using value_type = typename VectorType::value_type;\n  using TestVariablesType =\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                           TestHelpers::Tags::Scalar<VectorType>,\n                           TestHelpers::Tags::Scalar2<VectorType>>>;\n\n  MAKE_GENERATOR(gen);\n  // keep distribution somewhat near 1.0 to avoid accumulation of errors in\n  // cascaded math\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{1.0,\n                                                                         10.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t num_points = sdist(gen);  // number of grid points\n  const auto value_in_variables = make_with_random_values<value_type>(\n      make_not_null(&gen), make_not_null(&dist));\n  const auto rand_vals = make_with_random_values<std::array<value_type, 3>>(\n      make_not_null(&gen), make_not_null(&dist));\n\n  // Test math +, -, *, /\n  const auto test_close = [&num_points, &value_in_variables](\n                              const TestVariablesType& vars,\n                              const TestVariablesType& close) {\n    CHECK(close != vars);\n    CHECK_FALSE(close == vars);\n    CHECK_VARIABLES_APPROX(close, vars);\n    const TestVariablesType equivalent{num_points, value_in_variables};\n    CHECK(equivalent == vars);\n    CHECK_FALSE(equivalent != vars);\n  };\n\n  std::vector<value_type> owning_data(\n      num_points * TestVariablesType::number_of_independent_components,\n      value_in_variables);\n  const TestVariablesType owning_vars{num_points, value_in_variables};\n  const TestVariablesType non_owning_vars{owning_data.data(),\n                                          owning_data.size()};\n\n  {\n    const TestVariablesType close{num_points,\n                                  value_in_variables * (1.0 + 1.0e-14)};\n    const TestVariablesType non_owning_close{\n        const_cast<value_type*>(close.data()), close.size()};\n    test_close(owning_vars, close);\n    test_close(non_owning_vars, close);\n    test_close(owning_vars, non_owning_close);\n    test_close(non_owning_vars, non_owning_close);\n  }\n\n  TestVariablesType expected{num_points, rand_vals.at(0) * value_in_variables};\n\n  const auto test_math = [&expected, &num_points, &rand_vals,\n                          &value_in_variables](const TestVariablesType& vars) {\n    expected =\n        TestVariablesType{num_points, rand_vals.at(0) * value_in_variables};\n    CHECK_VARIABLES_APPROX(expected, rand_vals[0] * vars);\n    expected = TestVariablesType{num_points, value_in_variables * rand_vals[0]};\n    CHECK_VARIABLES_APPROX(expected, vars * rand_vals[0]);\n    expected = TestVariablesType{num_points, value_in_variables / rand_vals[0]};\n    CHECK_VARIABLES_APPROX(expected, vars / rand_vals[0]);\n\n    expected = TestVariablesType{num_points, 4. * value_in_variables};\n    CHECK_VARIABLES_APPROX(expected, vars + vars + vars + vars);\n    expected = TestVariablesType{num_points, 3. * value_in_variables};\n    CHECK_VARIABLES_APPROX(expected, vars + (vars + vars));\n    // clang-tidy: both sides of overloaded operator are equivalent\n    expected = TestVariablesType{num_points, -2. * value_in_variables};\n    CHECK_VARIABLES_APPROX(expected, vars - vars - vars - vars);  // NOLINT\n    expected = TestVariablesType{num_points, value_in_variables};\n    CHECK_VARIABLES_APPROX(expected,\n                           vars - (vars - vars));  // NOLINT\n    expected = TestVariablesType{num_points, -value_in_variables};\n    CHECK_VARIABLES_APPROX(expected, vars - (vars + vars));\n    expected = TestVariablesType{num_points, value_in_variables};\n    CHECK_VARIABLES_APPROX(expected,\n                           vars - vars + vars);  // NOLINT\n    expected = TestVariablesType{num_points, value_in_variables};\n    CHECK_VARIABLES_APPROX(expected, vars + vars - vars);\n    expected = TestVariablesType{num_points, -value_in_variables};\n    CHECK_VARIABLES_APPROX(expected, -vars);\n    expected = TestVariablesType{num_points, value_in_variables};\n    CHECK_VARIABLES_APPROX(expected, +vars);\n  };\n\n  test_math(owning_vars);\n  test_math(non_owning_vars);\n\n  // Test math_assignment operators +=, -=, *=, /= with values\n  const auto assignment_test =\n      [&expected, &num_points, &rand_vals,\n       &value_in_variables](TestVariablesType& test_assignment) {\n        auto expected_val = value_in_variables * rand_vals[0];\n        test_assignment += TestVariablesType{num_points, value_in_variables};\n        expected_val += value_in_variables;\n        expected = TestVariablesType{num_points, expected_val};\n        CHECK_VARIABLES_APPROX(expected, test_assignment);\n        test_assignment -= TestVariablesType{num_points, rand_vals[1]};\n        expected_val -= rand_vals[1];\n        expected = TestVariablesType{num_points, expected_val};\n        CHECK_VARIABLES_APPROX(expected, test_assignment);\n        test_assignment *= rand_vals[2];\n        expected_val *= rand_vals[2];\n        expected = TestVariablesType{num_points, expected_val};\n        CHECK_VARIABLES_APPROX(expected, test_assignment);\n        test_assignment /= rand_vals[0];\n        expected_val /= rand_vals[0];\n        expected = TestVariablesType{num_points, expected_val};\n        CHECK_VARIABLES_APPROX(expected, test_assignment);\n\n        test_assignment +=\n            TestVariablesType{num_points, value_in_variables} * rand_vals[1];\n        expected_val += value_in_variables * rand_vals[1];\n        expected = TestVariablesType{num_points, expected_val};\n        CHECK_VARIABLES_APPROX(expected, test_assignment);\n        test_assignment -=\n            TestVariablesType{num_points, rand_vals[2]} * rand_vals[0];\n        expected_val -= rand_vals[2] * rand_vals[0];\n        expected = TestVariablesType{num_points, expected_val};\n        CHECK_VARIABLES_APPROX(expected, test_assignment);\n\n        TestVariablesType test_assignment2{num_points};\n        test_assignment2 = test_assignment * 1.0;\n        CHECK(test_assignment2 == test_assignment);\n        test_assignment2 = test_assignment2 * 1.0;\n        CHECK(test_assignment2 == test_assignment);\n      };\n\n  TestVariablesType owning_test_assignment(owning_vars * rand_vals[0]);\n  std::vector<value_type> owning_data_assignment(\n      num_points * TestVariablesType::number_of_independent_components,\n      value_in_variables * rand_vals[0]);\n  TestVariablesType non_owning_test_assignment(owning_data_assignment.data(),\n                                               owning_data_assignment.size());\n  assignment_test(owning_test_assignment);\n  assignment_test(non_owning_test_assignment);\n  CHECK(non_owning_test_assignment.data() == owning_data_assignment.data());\n\n  const auto check_components = [](const auto& variables,\n                                   const VectorType& tensor) {\n    tmpl::for_each<typename std::decay_t<decltype(variables)>::tags_list>(\n        [&variables, &tensor](auto tag) {\n          using Tag = tmpl::type_from<decltype(tag)>;\n          for (const auto& component : get<Tag>(variables)) {\n            CHECK_ITERABLE_APPROX(component, tensor);\n          }\n        });\n  };\n\n  const auto test_vector = make_with_random_values<VectorType>(\n      make_not_null(&gen), make_not_null(&dist), VectorType{4});\n\n  // Test math assignment operators +=, -=, *=, /= with vectors\n  const auto vector_math_test = [&check_components, &test_vector, &rand_vals](\n                                    TestVariablesType& test_vector_math) {\n    test_vector_math *= test_vector;\n    VectorType vec_expect = rand_vals[0] * test_vector;\n    check_components(test_vector_math, vec_expect);\n    vec_expect *= test_vector;\n    check_components(test_vector_math * test_vector, vec_expect);\n    check_components(test_vector * test_vector_math, vec_expect);\n    test_vector_math *= test_vector;\n    check_components(test_vector_math, vec_expect);\n    vec_expect /= test_vector;\n    check_components(test_vector_math / test_vector, vec_expect);\n    test_vector_math /= test_vector;\n    check_components(test_vector_math, vec_expect);\n  };\n\n  TestVariablesType test_vector_math(4, rand_vals[0]);\n  std::vector<value_type> data_test_vector_math(\n      4 * TestVariablesType::number_of_independent_components, rand_vals[0]);\n  TestVariablesType non_owning_test_vector_math(data_test_vector_math.data(),\n                                                data_test_vector_math.size());\n  vector_math_test(test_vector_math);\n  vector_math_test(non_owning_test_vector_math);\n}\n\ntemplate <typename VectorType>\nvoid test_variables_prefix_semantics() {\n  using TagList = tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                             TestHelpers::Tags::Scalar<VectorType>,\n                             TestHelpers::Tags::Scalar2<VectorType>>;\n  using VariablesType = Variables<TagList>;\n  using PrefixVariablesType =\n      Variables<db::wrap_tags_in<TestHelpers::Tags::Prefix0, TagList>>;\n  using value_type = typename VectorType::value_type;\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points = sdist(gen);\n  const auto variables_vals =\n      make_with_random_values<std::array<value_type, 4>>(make_not_null(&gen),\n                                                         make_not_null(&dist));\n\n  // Check move and copy from prefix variables to non-prefix variables via\n  // constructor\n  PrefixVariablesType prefix_vars_copy_construct_from{number_of_grid_points,\n                                                      variables_vals[0]};\n  PrefixVariablesType prefix_vars_move_construct_from{number_of_grid_points,\n                                                      variables_vals[1]};\n  VariablesType vars_to{prefix_vars_copy_construct_from};\n  VariablesType move_constructed_vars{\n      std::move(prefix_vars_move_construct_from)};\n\n  VariablesType expected{number_of_grid_points, variables_vals[0]};\n  CHECK_VARIABLES_APPROX(vars_to, expected);\n  expected = VariablesType(number_of_grid_points, variables_vals[1]);\n  CHECK_VARIABLES_APPROX(move_constructed_vars, expected);\n\n  VariablesType constructed_from_default{PrefixVariablesType{}};\n  CHECK(constructed_from_default.size() == 0);\n  PrefixVariablesType assigned_from_default{number_of_grid_points};\n  assigned_from_default = std::move(constructed_from_default);\n  CHECK(assigned_from_default.size() == 0);\n\n  {\n    VariablesType non_owning_vars_to{prefix_vars_copy_construct_from};\n    // Check move and copy from prefix variables to non-prefix variables via\n    // assignment operator\n    std::vector<value_type> non_owning_data_copy_from(\n        number_of_grid_points *\n            PrefixVariablesType::number_of_independent_components,\n        variables_vals[2]);\n    std::vector<value_type> non_owning_data_move_from(\n        number_of_grid_points *\n            PrefixVariablesType::number_of_independent_components,\n        variables_vals[3]);\n    PrefixVariablesType prefix_vars_copy_assign_from{\n        non_owning_data_copy_from.data(), non_owning_data_copy_from.size()};\n    PrefixVariablesType prefix_vars_move_assign_from{\n        non_owning_data_move_from.data(), non_owning_data_move_from.size()};\n    non_owning_vars_to = prefix_vars_copy_assign_from;\n    CHECK(not prefix_vars_copy_assign_from.is_owning());\n    CHECK(non_owning_vars_to.is_owning());\n    CHECK_VARIABLES_APPROX(\n        non_owning_vars_to,\n        VariablesType(number_of_grid_points, variables_vals[2]));\n    CHECK_VARIABLES_APPROX(\n        prefix_vars_copy_assign_from,\n        PrefixVariablesType(number_of_grid_points, variables_vals[2]));\n\n    non_owning_vars_to = std::move(prefix_vars_move_assign_from);\n    CHECK(prefix_vars_move_assign_from.is_owning());\n    CHECK(prefix_vars_move_assign_from.size() == 0);\n    CHECK(not non_owning_vars_to.is_owning());\n    CHECK_VARIABLES_APPROX(\n        non_owning_vars_to,\n        VariablesType(number_of_grid_points, variables_vals[3]));\n\n    // Test construction\n    PrefixVariablesType non_owning_prefix_vars_copy_construct_from{\n        non_owning_data_copy_from.data(), non_owning_data_copy_from.size()};\n    PrefixVariablesType non_owning_prefix_vars_move_construct_from{\n        non_owning_data_move_from.data(), non_owning_data_copy_from.size()};\n    const VariablesType copy_constructed{\n        non_owning_prefix_vars_copy_construct_from};\n    CHECK(not non_owning_prefix_vars_copy_construct_from.is_owning());\n    CHECK(copy_constructed.is_owning());\n    CHECK_VARIABLES_APPROX(\n        copy_constructed,\n        VariablesType(number_of_grid_points, variables_vals[2]));\n    CHECK_VARIABLES_APPROX(\n        non_owning_prefix_vars_copy_construct_from,\n        PrefixVariablesType(number_of_grid_points, variables_vals[2]));\n\n    const VariablesType move_constructed{\n        std::move(non_owning_prefix_vars_move_construct_from)};\n    CHECK(non_owning_prefix_vars_move_construct_from.is_owning());\n    CHECK(non_owning_prefix_vars_move_construct_from.size() == 0);\n    CHECK(non_owning_prefix_vars_move_construct_from.number_of_grid_points() ==\n          0);\n    CHECK(not move_constructed.is_owning());\n    CHECK_VARIABLES_APPROX(\n        move_constructed,\n        VariablesType(number_of_grid_points, variables_vals[3]));\n  }\n\n  // Check move and copy from prefix variables to non-prefix variables via\n  // assignment operator\n  PrefixVariablesType prefix_vars_copy_assign_from{number_of_grid_points,\n                                                   variables_vals[2]};\n  PrefixVariablesType prefix_vars_move_assign_from{number_of_grid_points,\n                                                   variables_vals[3]};\n  vars_to = prefix_vars_copy_assign_from;\n  CHECK_VARIABLES_APPROX(\n      vars_to, VariablesType(number_of_grid_points, variables_vals[2]));\n  vars_to = std::move(prefix_vars_move_assign_from);\n  CHECK_VARIABLES_APPROX(\n      vars_to, VariablesType(number_of_grid_points, variables_vals[3]));\n\n  // Test swap with prefix\n  PrefixVariablesType prefix_vars_swap{number_of_grid_points};\n  VariablesType vars_swap{2 * number_of_grid_points};\n  fill_with_random_values(make_not_null(&prefix_vars_swap), make_not_null(&gen),\n                          make_not_null(&dist));\n  fill_with_random_values(make_not_null(&vars_swap), make_not_null(&gen),\n                          make_not_null(&dist));\n  const PrefixVariablesType expected_vars_swap(prefix_vars_swap);\n  const VariablesType expected_prefix_vars_swap(vars_swap);\n  swap(vars_swap, prefix_vars_swap);\n  CHECK(\n      gsl::span<const value_type>(expected_vars_swap.data(),\n                                  2 * number_of_grid_points) ==\n      gsl::span<const value_type>(vars_swap.data(), 2 * number_of_grid_points));\n  CHECK(gsl::span<const value_type>(expected_prefix_vars_swap.data(),\n                                    number_of_grid_points) ==\n        gsl::span<const value_type>(prefix_vars_swap.data(),\n                                    number_of_grid_points));\n\n  {\n    VariablesType reuse_after_move{number_of_grid_points, 2.0};\n    { PrefixVariablesType constructed{std::move(reuse_after_move)}; }\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    reuse_after_move.initialize(number_of_grid_points, 3.0);\n    CHECK(reuse_after_move == VariablesType{number_of_grid_points, 3.0});\n    CHECK(get<0>(get<TestHelpers::Tags::Vector<VectorType>>(\n              reuse_after_move))[0] == 3.0);\n\n    {\n      PrefixVariablesType assigned{};\n      assigned = std::move(reuse_after_move);\n    }\n    // NOLINTNEXTLINE(bugprone-use-after-move)\n    reuse_after_move.initialize(number_of_grid_points, 4.0);\n    CHECK(reuse_after_move == VariablesType{number_of_grid_points, 4.0});\n    CHECK(get<0>(get<TestHelpers::Tags::Vector<VectorType>>(\n              reuse_after_move))[0] == 4.0);\n  }\n}\n\ntemplate <typename VectorType>\nvoid test_variables_prefix_math() {\n  using TagList = tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                             TestHelpers::Tags::Scalar<VectorType>,\n                             TestHelpers::Tags::Scalar2<VectorType>>;\n  using Prefix0VariablesType =\n      Variables<db::wrap_tags_in<TestHelpers::Tags::Prefix0, TagList>>;\n  using Prefix1VariablesType =\n      Variables<db::wrap_tags_in<TestHelpers::Tags::Prefix1, TagList>>;\n  using Prefix2VariablesType =\n      Variables<db::wrap_tags_in<TestHelpers::Tags::Prefix2, TagList>>;\n  using Prefix3VariablesType =\n      Variables<db::wrap_tags_in<TestHelpers::Tags::Prefix3, TagList>>;\n  using value_type = typename VectorType::value_type;\n\n  MAKE_GENERATOR(gen);\n  // keep distribution somewhat near 1.0 to avoid accumulation of errors in\n  // cascaded math\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{1.0,\n                                                                         10.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points = sdist(gen);\n  const auto value_in_variables = make_with_random_values<value_type>(\n      make_not_null(&gen), make_not_null(&dist));\n  const auto random_vals = make_with_random_values<std::array<value_type, 3>>(\n      make_not_null(&gen), make_not_null(&dist));\n\n  // Test arithmetic operators +, -, *, / for prefix Variables.\n\n  // We wish to verify that operations among multiple Variables with distinct\n  // prefixes give a Variables populated with the desired data. This is\n  // important for multiplying, for instance a variables populated with time\n  // derivatives with a variables populated with time intervals, which would\n  // have the same types, but have distinct prefixes.\n  Prefix0VariablesType prefix_vars0(number_of_grid_points, value_in_variables);\n  Prefix1VariablesType prefix_vars1(number_of_grid_points, value_in_variables);\n  Prefix2VariablesType prefix_vars2(number_of_grid_points, value_in_variables);\n  Prefix3VariablesType prefix_vars3(number_of_grid_points, value_in_variables);\n\n  Prefix3VariablesType expected{number_of_grid_points,\n                                value_in_variables * random_vals[0]};\n  CHECK_VARIABLES_APPROX(expected, random_vals[0] * prefix_vars0);\n  expected = Prefix3VariablesType{number_of_grid_points,\n                                  value_in_variables * random_vals[0]};\n  CHECK_VARIABLES_APPROX(expected, prefix_vars0 * random_vals[0]);\n  expected = Prefix3VariablesType{number_of_grid_points,\n                                  value_in_variables / random_vals[0]};\n  CHECK_VARIABLES_APPROX(expected, prefix_vars0 / random_vals[0]);\n  expected =\n      Prefix3VariablesType{number_of_grid_points, 4.0 * value_in_variables};\n  CHECK_VARIABLES_APPROX(\n      expected, prefix_vars0 + prefix_vars1 + prefix_vars2 + prefix_vars3);\n  expected =\n      Prefix3VariablesType{number_of_grid_points, 4.0 * value_in_variables};\n  CHECK_VARIABLES_APPROX(\n      expected, (prefix_vars0 + prefix_vars1) + (prefix_vars2 + prefix_vars3));\n  expected =\n      Prefix3VariablesType{number_of_grid_points, 3.0 * value_in_variables};\n\n  CHECK_VARIABLES_APPROX(expected,\n                         prefix_vars0 + (prefix_vars1 + prefix_vars2));\n\n  // clang-tidy: both sides of overloaded operator are equivalent\n  expected =\n      Prefix3VariablesType{number_of_grid_points, -2.0 * value_in_variables};\n  CHECK_VARIABLES_APPROX(\n      expected,\n      prefix_vars0 - prefix_vars1 - prefix_vars2 - prefix_vars3);  // NOLINT\n  expected =\n      Prefix3VariablesType{number_of_grid_points, 2.0 * value_in_variables};\n  CHECK_VARIABLES_APPROX(\n      expected,\n      prefix_vars0 - (prefix_vars1 - prefix_vars2 - prefix_vars3));  // NOLINT\n  expected =\n      Prefix3VariablesType{number_of_grid_points, 0.0 * value_in_variables};\n  CHECK_VARIABLES_APPROX(\n      expected,\n      (prefix_vars0 - prefix_vars1) - (prefix_vars2 - prefix_vars3));  // NOLINT\n  expected =\n      Prefix3VariablesType{number_of_grid_points, 1.0 * value_in_variables};\n  CHECK_VARIABLES_APPROX(\n      expected, prefix_vars0 - (prefix_vars1 - prefix_vars2));  // NOLINT\n  expected =\n      Prefix3VariablesType{number_of_grid_points, 1.0 * value_in_variables};\n  CHECK_VARIABLES_APPROX(expected,\n                         prefix_vars0 - prefix_vars1 + prefix_vars2);  // NOLINT\n  expected =\n      Prefix3VariablesType{number_of_grid_points, 1.0 * value_in_variables};\n  CHECK_VARIABLES_APPROX(expected, prefix_vars0 + prefix_vars1 - prefix_vars2);\n  expected = Prefix3VariablesType{number_of_grid_points, -value_in_variables};\n  CHECK_VARIABLES_APPROX(expected, -prefix_vars0);\n  expected = Prefix3VariablesType{number_of_grid_points, value_in_variables};\n  CHECK_VARIABLES_APPROX(expected, +prefix_vars0);\n\n  // Test assignment arithmetic operators +=, -=, *=, /= with values\n  Prefix0VariablesType test_assignment(prefix_vars0 * 1.0);\n  test_assignment +=\n      Prefix1VariablesType{number_of_grid_points, value_in_variables};\n  auto expected_val = 2.0 * value_in_variables;\n  Prefix0VariablesType expected_prefix0{number_of_grid_points, expected_val};\n  CHECK_VARIABLES_APPROX(expected_prefix0, test_assignment);\n  test_assignment -=\n      Prefix1VariablesType{number_of_grid_points, random_vals[0]};\n  expected_val -= random_vals[0];\n  expected_prefix0 = Prefix0VariablesType{number_of_grid_points, expected_val};\n  CHECK_VARIABLES_APPROX(expected_prefix0, test_assignment);\n  test_assignment *= random_vals[1];\n  expected_val *= random_vals[1];\n  expected_prefix0 = Prefix0VariablesType{number_of_grid_points, expected_val};\n  CHECK_VARIABLES_APPROX(expected_prefix0, test_assignment);\n  test_assignment /= random_vals[2];\n  expected_val /= random_vals[2];\n  expected_prefix0 = Prefix0VariablesType{number_of_grid_points, expected_val};\n  CHECK_VARIABLES_APPROX(expected_prefix0, test_assignment);\n\n  test_assignment +=\n      Prefix1VariablesType{number_of_grid_points, random_vals[0]} *\n      random_vals[1];\n  expected_val += random_vals[0] * random_vals[1];\n  expected_prefix0 = Prefix0VariablesType{number_of_grid_points, expected_val};\n  CHECK_VARIABLES_APPROX(expected_prefix0, test_assignment);\n  test_assignment -=\n      Prefix1VariablesType{number_of_grid_points, random_vals[0]} *\n      random_vals[2];\n  expected_val -= random_vals[0] * random_vals[2];\n  expected_prefix0 = Prefix0VariablesType{number_of_grid_points, expected_val};\n  CHECK_VARIABLES_APPROX(expected_prefix0, test_assignment);\n}\n\ntemplate <typename VectorType>\nvoid test_variables_serialization() {\n  using value_type = typename VectorType::value_type;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points = sdist(gen);\n  const auto values_in_variables =\n      make_with_random_values<std::array<value_type, 2>>(make_not_null(&gen),\n                                                         make_not_null(&dist));\n\n  // Test serialization of a Variables and a tuple of Variables\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>> test_variables(\n      number_of_grid_points, values_in_variables[0]);\n  test_serialization(test_variables);\n  auto tuple_of_test_variables = std::make_tuple(\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>{\n          number_of_grid_points, values_in_variables[1]});\n  test_serialization(tuple_of_test_variables);\n}\n\ntemplate <typename VectorType>\nvoid test_variables_assign_subset() {\n  using value_type = typename VectorType::value_type;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points = sdist(gen);\n  const auto values_in_variables =\n      make_with_random_values<std::array<value_type, 4>>(make_not_null(&gen),\n                                                         make_not_null(&dist));\n\n  // Test assigning a single tag to a Variables with two tags, either from a\n  // Variables or a TaggedTuple\n  const auto test_assign_to_vars_with_two_tags = [&number_of_grid_points,\n                                                  &values_in_variables](\n                                                     const auto& vars_subset0,\n                                                     const auto& vars_subset1,\n                                                     const value_type&\n                                                         vars_subset0_val,\n                                                     const value_type&\n                                                         vars_subset1_val) {\n    Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                         TestHelpers::Tags::Scalar<VectorType>>>\n        vars_set{number_of_grid_points, values_in_variables[0]};\n    CHECK(\n        get<TestHelpers::Tags::Vector<VectorType>>(vars_set) ==\n        tnsr::I<VectorType, 3>(number_of_grid_points, values_in_variables[0]));\n    CHECK(get<TestHelpers::Tags::Scalar<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, values_in_variables[0]));\n    vars_set.assign_subset(vars_subset0);\n    CHECK(get<TestHelpers::Tags::Vector<VectorType>>(vars_set) ==\n          tnsr::I<VectorType, 3>(number_of_grid_points, vars_subset0_val));\n    CHECK(get<TestHelpers::Tags::Scalar<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, values_in_variables[0]));\n    vars_set.assign_subset(vars_subset1);\n    CHECK(get<TestHelpers::Tags::Vector<VectorType>>(vars_set) ==\n          tnsr::I<VectorType, 3>(number_of_grid_points, vars_subset0_val));\n    CHECK(get<TestHelpers::Tags::Scalar<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, vars_subset1_val));\n  };\n\n  test_assign_to_vars_with_two_tags(\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>(\n          number_of_grid_points, values_in_variables[1]),\n      Variables<tmpl::list<TestHelpers::Tags::Scalar<VectorType>>>(\n          number_of_grid_points, values_in_variables[2]),\n      values_in_variables[1], values_in_variables[2]);\n  test_assign_to_vars_with_two_tags(\n      tuples::TaggedTuple<TestHelpers::Tags::Vector<VectorType>>(\n          typename TestHelpers::Tags::Vector<VectorType>::type{\n              number_of_grid_points, values_in_variables[1]}),\n      tuples::TaggedTuple<TestHelpers::Tags::Scalar<VectorType>>(\n          typename TestHelpers::Tags::Scalar<VectorType>::type{\n              number_of_grid_points, values_in_variables[2]}),\n      values_in_variables[1], values_in_variables[2]);\n\n  test_assign_to_vars_with_two_tags(\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                           TestHelpers::Tags::OneForm<VectorType>>>(\n          number_of_grid_points, values_in_variables[1]),\n      Variables<tmpl::list<TestHelpers::Tags::Scalar<VectorType>,\n                           TestHelpers::Tags::OneForm<VectorType>>>(\n          number_of_grid_points, values_in_variables[2]),\n      values_in_variables[1], values_in_variables[2]);\n  test_assign_to_vars_with_two_tags(\n      tuples::TaggedTuple<TestHelpers::Tags::Vector<VectorType>,\n                          TestHelpers::Tags::OneForm<VectorType>>(\n          typename TestHelpers::Tags::Vector<VectorType>::type{\n              number_of_grid_points, values_in_variables[1]},\n          typename TestHelpers::Tags::OneForm<VectorType>::type{\n              number_of_grid_points, values_in_variables[0]}),\n      tuples::TaggedTuple<TestHelpers::Tags::Scalar<VectorType>,\n                          TestHelpers::Tags::OneForm<VectorType>>(\n          typename TestHelpers::Tags::Scalar<VectorType>::type{\n              number_of_grid_points, values_in_variables[2]},\n          typename TestHelpers::Tags::OneForm<VectorType>::type{\n              number_of_grid_points, values_in_variables[0]}),\n      values_in_variables[1], values_in_variables[2]);\n\n  // Test assigning a single tag to a Variables with three tags, either from a\n  // Variables or a TaggedTuple\n  const auto test_assign_to_vars_with_three_tags = [&number_of_grid_points,\n                                                    &values_in_variables](\n                                                       const auto& vars_subset0,\n                                                       const auto& vars_subset1,\n                                                       const value_type&\n                                                           vars_subset0_val,\n                                                       const value_type&\n                                                           vars_subset1_val) {\n    Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                         TestHelpers::Tags::Scalar<VectorType>,\n                         TestHelpers::Tags::Scalar2<VectorType>>>\n        vars_set(number_of_grid_points, values_in_variables[0]);\n    CHECK(\n        get<TestHelpers::Tags::Vector<VectorType>>(vars_set) ==\n        tnsr::I<VectorType, 3>(number_of_grid_points, values_in_variables[0]));\n    CHECK(get<TestHelpers::Tags::Scalar<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, values_in_variables[0]));\n    CHECK(get<TestHelpers::Tags::Scalar2<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, values_in_variables[0]));\n    vars_set.assign_subset(vars_subset0);\n    CHECK(get<TestHelpers::Tags::Vector<VectorType>>(vars_set) ==\n          tnsr::I<VectorType, 3>(number_of_grid_points, vars_subset0_val));\n    CHECK(get<TestHelpers::Tags::Scalar<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, values_in_variables[0]));\n    CHECK(get<TestHelpers::Tags::Scalar2<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, values_in_variables[0]));\n    vars_set.assign_subset(vars_subset1);\n    CHECK(get<TestHelpers::Tags::Vector<VectorType>>(vars_set) ==\n          tnsr::I<VectorType, 3>(number_of_grid_points, vars_subset0_val));\n    CHECK(get<TestHelpers::Tags::Scalar<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, vars_subset1_val));\n    CHECK(get<TestHelpers::Tags::Scalar2<VectorType>>(vars_set) ==\n          ::Scalar<VectorType>(number_of_grid_points, values_in_variables[0]));\n  };\n\n  test_assign_to_vars_with_three_tags(\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>(\n          number_of_grid_points, values_in_variables[1]),\n      Variables<tmpl::list<TestHelpers::Tags::Scalar<VectorType>>>(\n          number_of_grid_points, values_in_variables[2]),\n      values_in_variables[1], values_in_variables[2]);\n  test_assign_to_vars_with_three_tags(\n      tuples::TaggedTuple<TestHelpers::Tags::Vector<VectorType>>(\n          typename TestHelpers::Tags::Vector<VectorType>::type{\n              number_of_grid_points, values_in_variables[1]}),\n      tuples::TaggedTuple<TestHelpers::Tags::Scalar<VectorType>>(\n          typename TestHelpers::Tags::Scalar<VectorType>::type{\n              number_of_grid_points, values_in_variables[2]}),\n      values_in_variables[1], values_in_variables[2]);\n\n  test_assign_to_vars_with_three_tags(\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                           TestHelpers::Tags::OneForm<VectorType>>>(\n          number_of_grid_points, values_in_variables[1]),\n      Variables<tmpl::list<TestHelpers::Tags::Scalar<VectorType>,\n                           TestHelpers::Tags::OneForm<VectorType>>>(\n          number_of_grid_points, values_in_variables[2]),\n      values_in_variables[1], values_in_variables[2]);\n  test_assign_to_vars_with_three_tags(\n      tuples::TaggedTuple<TestHelpers::Tags::Vector<VectorType>,\n                          TestHelpers::Tags::OneForm<VectorType>>(\n          typename TestHelpers::Tags::Vector<VectorType>::type{\n              number_of_grid_points, values_in_variables[1]},\n          typename TestHelpers::Tags::OneForm<VectorType>::type{\n              number_of_grid_points, values_in_variables[0]}),\n      tuples::TaggedTuple<TestHelpers::Tags::Scalar<VectorType>,\n                          TestHelpers::Tags::OneForm<VectorType>>(\n          typename TestHelpers::Tags::Scalar<VectorType>::type{\n              number_of_grid_points, values_in_variables[2]},\n          typename TestHelpers::Tags::OneForm<VectorType>::type{\n              number_of_grid_points, values_in_variables[0]}),\n      values_in_variables[1], values_in_variables[2]);\n\n  // Test assignment to a Variables with a single tag either from a Variables or\n  // a TaggedTuple\n  const auto test_assign_to_vars_with_one_tag =\n      [&number_of_grid_points, &values_in_variables](\n          const auto& vars_subset0, const value_type& vars_subset0_val) {\n        Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>> vars_set(\n            number_of_grid_points, values_in_variables[0]);\n        CHECK(get<TestHelpers::Tags::Vector<VectorType>>(vars_set) ==\n              tnsr::I<VectorType, 3>(number_of_grid_points,\n                                     values_in_variables[0]));\n        vars_set.assign_subset(vars_subset0);\n        CHECK(get<TestHelpers::Tags::Vector<VectorType>>(vars_set) ==\n              tnsr::I<VectorType, 3>(number_of_grid_points, vars_subset0_val));\n      };\n\n  test_assign_to_vars_with_one_tag(\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>(\n          number_of_grid_points, values_in_variables[1]),\n      values_in_variables[1]);\n  test_assign_to_vars_with_one_tag(\n      tuples::TaggedTuple<TestHelpers::Tags::Vector<VectorType>>(\n          typename TestHelpers::Tags::Vector<VectorType>::type{\n              number_of_grid_points, values_in_variables[1]}),\n      values_in_variables[1]);\n\n  test_assign_to_vars_with_one_tag(\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                           TestHelpers::Tags::OneForm<VectorType>>>(\n          number_of_grid_points, values_in_variables[1]),\n      values_in_variables[1]);\n  test_assign_to_vars_with_one_tag(\n      tuples::TaggedTuple<TestHelpers::Tags::Vector<VectorType>,\n                          TestHelpers::Tags::OneForm<VectorType>>(\n          typename TestHelpers::Tags::Vector<VectorType>::type{\n              number_of_grid_points, values_in_variables[1]},\n          typename TestHelpers::Tags::OneForm<VectorType>::type{\n              number_of_grid_points, values_in_variables[0]}),\n      values_in_variables[1]);\n}\n\ntemplate <typename VectorType>\nvoid test_variables_extract_subset() {\n  using value_type = typename VectorType::value_type;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points = sdist(gen);\n\n  const auto vars_subset0 = make_with_random_values<\n      Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>>>>(\n      make_not_null(&gen), make_not_null(&dist),\n      VectorType{number_of_grid_points});\n  const auto vars_subset1 = make_with_random_values<\n      Variables<tmpl::list<TestHelpers::Tags::Scalar<VectorType>>>>(\n      make_not_null(&gen), make_not_null(&dist),\n      VectorType{number_of_grid_points});\n\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                       TestHelpers::Tags::Scalar<VectorType>>>\n      vars(number_of_grid_points);\n  get<TestHelpers::Tags::Vector<VectorType>>(vars) =\n      get<TestHelpers::Tags::Vector<VectorType>>(vars_subset0);\n  get<TestHelpers::Tags::Scalar<VectorType>>(vars) =\n      get<TestHelpers::Tags::Scalar<VectorType>>(vars_subset1);\n  CHECK(vars.template extract_subset<\n            tmpl::list<TestHelpers::Tags::Vector<VectorType>>>() ==\n        vars_subset0);\n  CHECK(vars.template extract_subset<\n            tmpl::list<TestHelpers::Tags::Scalar<VectorType>>>() ==\n        vars_subset1);\n  CHECK(vars.template extract_subset<\n            tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                       TestHelpers::Tags::Scalar<VectorType>>>() == vars);\n}\n\ntemplate <typename VectorType>\nvoid test_variables_reference_subset() {\n  using value_type = typename VectorType::value_type;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  using FullVars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                        TestHelpers::Tags::Scalar<VectorType>>>;\n  const size_t number_of_grid_points = sdist(gen);\n\n  auto full_vars = make_with_random_values<FullVars>(\n      make_not_null(&gen), make_not_null(&dist), number_of_grid_points);\n  const auto check_reference = [&](auto tags) {\n    const auto reference_vars =\n        full_vars.template reference_subset<decltype(tags)>();\n    CHECK(not reference_vars.is_owning());\n    CHECK(reference_vars ==\n          full_vars.template extract_subset<decltype(tags)>());\n  };\n  check_reference(typename FullVars::tags_list{});\n  check_reference(tmpl::list<TestHelpers::Tags::Vector<VectorType>>{});\n  check_reference(tmpl::list<TestHelpers::Tags::Scalar<VectorType>>{});\n  {\n    auto empty_reference = full_vars.template reference_subset<tmpl::list<>>();\n    static_assert(\n        std::is_same_v<decltype(empty_reference), Variables<tmpl::list<>>>);\n  }\n}\n\ntemplate <typename VectorType>\nvoid test_variables_reference_with_different_prefixes() {\n  using value_type = typename VectorType::value_type;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points = sdist(gen);\n\n  using Variables1 = Variables<tmpl::list<\n      TestHelpers::Tags::Vector<VectorType>,\n      TestHelpers::Tags::Prefix0<TestHelpers::Tags::Scalar<VectorType>>>>;\n  using Variables2 = Variables<tmpl::list<\n      TestHelpers::Tags::Prefix0<TestHelpers::Tags::Vector<VectorType>>,\n      TestHelpers::Tags::Scalar<VectorType>>>;\n\n  auto owning = make_with_random_values<Variables1>(\n      make_not_null(&gen), make_not_null(&dist), number_of_grid_points);\n  auto non_owning =\n      owning.template reference_with_different_prefixes<Variables2>();\n  CHECK(owning.size() == non_owning.size());\n  CHECK(owning.data() == non_owning.data());\n}\n\ntemplate <typename VectorType>\nvoid test_variables_from_tagged_tuple() {\n  using value_type = typename VectorType::value_type;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n\n  const size_t number_of_grid_points = sdist(gen);\n  tuples::TaggedTuple<TestHelpers::Tags::Vector<VectorType>,\n                      TestHelpers::Tags::Scalar<VectorType>>\n      source;\n  get<TestHelpers::Tags::Vector<VectorType>>(source) = make_with_random_values<\n      typename TestHelpers::Tags::Vector<VectorType>::type>(\n      make_not_null(&gen), make_not_null(&dist),\n      VectorType{number_of_grid_points});\n  get<TestHelpers::Tags::Scalar<VectorType>>(source) = make_with_random_values<\n      typename TestHelpers::Tags::Scalar<VectorType>::type>(\n      make_not_null(&gen), make_not_null(&dist),\n      VectorType{number_of_grid_points});\n\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                       TestHelpers::Tags::Scalar<VectorType>>>\n      assigned(number_of_grid_points);\n  assigned.assign_subset(source);\n  const auto created = variables_from_tagged_tuple(source);\n  CHECK(assigned == created);\n\n  std::vector<value_type> owning_data(\n      number_of_grid_points * assigned.number_of_independent_components);\n  Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                       TestHelpers::Tags::Scalar<VectorType>>>\n      non_owning_assigned(owning_data.data(), owning_data.size());\n  non_owning_assigned.assign_subset(source);\n  CHECK(non_owning_assigned == created);\n  CHECK(non_owning_assigned.data() == owning_data.data());\n}\n\ntemplate <typename VectorType>\nvoid test_variables_equal_within_roundoff() {\n  INFO(pretty_type::name<VectorType>());\n  MAKE_GENERATOR(gen);\n  using value_type = typename VectorType::value_type;\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>>\n      roundoff_dist{-1.e-12, 1.e-12};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n  const size_t num_points = sdist(gen);\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                    TestHelpers::Tags::Scalar<VectorType>>>;\n\n  const auto do_checks = [&num_points, &gen, &roundoff_dist](Vars& vars) {\n    CHECK(equal_within_roundoff(vars, 0.));\n    CHECK(equal_within_roundoff(vars, 0., 0.));\n    CHECK(equal_within_roundoff(0., vars));\n    CHECK(equal_within_roundoff(vars, Vars{num_points, 0.}));\n    get(get<TestHelpers::Tags::Scalar<VectorType>>(vars)) += 1.;\n    CHECK_FALSE(equal_within_roundoff(vars, 0.));\n    CHECK_FALSE(equal_within_roundoff(vars, Vars{num_points, 0.}));\n    vars = make_with_random_values<Vars>(\n        make_not_null(&gen), make_not_null(&roundoff_dist), num_points);\n    CHECK_FALSE(equal_within_roundoff(vars, 0., 0.));\n    CHECK(equal_within_roundoff(vars, 0., 1.e-12));\n  };\n\n  Vars owning_vars{num_points, 0.};\n  do_checks(owning_vars);\n\n  std::vector<value_type> owning_data(\n      num_points * owning_vars.number_of_independent_components, 0.);\n  Vars non_owning_vars{owning_data.data(), owning_data.size()};\n  do_checks(non_owning_vars);\n}\n\ntemplate <typename VectorType>\nvoid test_math_wrapper() {\n  INFO(pretty_type::short_name<VectorType>());\n  MAKE_GENERATOR(gen);\n  using value_type = typename VectorType::value_type;\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> value_dist{\n      -10.0, 10.0};\n  UniformCustomDistribution<size_t> sdist{1, 5};\n  const size_t num_points = sdist(gen);\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                    TestHelpers::Tags::Scalar<VectorType>>>;\n  const auto vars1 = make_with_random_values<Vars>(\n      make_not_null(&gen), make_not_null(&value_dist), num_points);\n  const auto vars2 = make_with_random_values<Vars>(\n      make_not_null(&gen), make_not_null(&value_dist), num_points);\n  const auto scalar = make_with_random_values<typename Vars::value_type>(\n      make_not_null(&gen), make_not_null(&value_dist), num_points);\n  TestHelpers::MathWrapper::test_type(vars1, vars2, scalar);\n}\n\ntemplate <typename VectorType>\nvoid test_into_math_wrapper_type() {\n  INFO(pretty_type::short_name<VectorType>());\n  MAKE_GENERATOR(gen);\n  using value_type = typename VectorType::value_type;\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> value_dist{\n      -10.0, 10.0};\n  UniformCustomDistribution<size_t> sdist{2, 5};\n  const size_t num_points = sdist(gen);\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                    TestHelpers::Tags::Scalar<VectorType>>>;\n  const auto vars = make_with_random_values<Vars>(\n      make_not_null(&gen), make_not_null(&value_dist), num_points);\n  auto copy = vars;\n  const auto* const data = copy.data();\n  auto type_erased = into_math_wrapper_type(std::move(copy));\n  static_assert(std::is_same_v<decltype(type_erased), math_wrapper_type<Vars>>);\n  CHECK(type_erased.data() == data);\n  CHECK(std::equal(type_erased.begin(), type_erased.end(), vars.data(),\n                   vars.data() + vars.size()));\n}\n\ntemplate <typename VectorType>\nvoid test_contains_allocations() {\n  INFO(pretty_type::short_name<VectorType>());\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                    TestHelpers::Tags::Scalar<VectorType>>>;\n  CHECK(not contains_allocations(Vars{}));\n  CHECK(not contains_allocations(Vars{0}));\n  for (size_t size = 1; size < 5; ++size) {\n    CAPTURE(size);\n    Vars source{size};\n    const auto* const source_data = source.data();\n    const auto dest = std::move(source);\n    CHECK(contains_allocations(dest) == (source_data == dest.data()));\n  }\n}\n\ntemplate <typename VectorType>\nvoid test_make_with_value() {\n  INFO(pretty_type::short_name<VectorType>());\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                    TestHelpers::Tags::Scalar<VectorType>>>;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{1, 5};\n  const size_t num_points = sdist(gen);\n  CHECK(make_with_value<Vars>(DataVector(num_points, 4.5), -2.3) ==\n        Vars(num_points, -2.3));\n  CHECK(make_with_value<DataVector>(Vars(num_points, 4.5), -2.3) ==\n        DataVector(num_points, -2.3));\n}\n\ntemplate <typename VectorType>\nvoid test_set_number_of_grid_points() {\n  INFO(pretty_type::short_name<VectorType>());\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                    TestHelpers::Tags::Scalar<VectorType>>>;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{1, 5};\n  const size_t num_points = sdist(gen);\n\n  Vars resized{};\n  set_number_of_grid_points(make_not_null(&resized), num_points);\n  CHECK(resized.number_of_grid_points() == num_points);\n  DataVector resized_vector{};\n  set_number_of_grid_points(make_not_null(&resized_vector), Vars(num_points));\n  CHECK(resized_vector.size() == num_points);\n}\n\ntemplate <typename VectorType>\nvoid test_vector_ownership() {\n  INFO(pretty_type::short_name<VectorType>());\n  using value_type = typename VectorType::value_type;\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                    TestHelpers::Tags::Scalar<VectorType>>>;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  {\n    UniformCustomDistribution<size_t> sdist{2, 5};\n    const size_t num_points = sdist(gen);\n    auto vars = make_with_random_values<Vars>(make_not_null(&gen),\n                                              make_not_null(&dist), num_points);\n    const auto orig_scalar = get<TestHelpers::Tags::Scalar<VectorType>>(vars);\n    const auto* const data = vars.data();\n    VectorType transferred = std::move(vars).release();\n    CHECK(transferred.is_owning());\n    CHECK(transferred.data() == data);\n    CHECK(vars.size() == 0);  // NOLINT(bugprone-use-after-move)\n    const Vars transferred_back(std::move(transferred));\n    CHECK(transferred_back.is_owning());\n    CHECK(transferred_back.data() == data);\n    CHECK(transferred_back.number_of_grid_points() == num_points);\n    CHECK(transferred_back.size() ==\n          num_points * Vars::number_of_independent_components);\n    CHECK(get<TestHelpers::Tags::Scalar<VectorType>>(transferred_back) ==\n          orig_scalar);\n  }\n  {\n    INFO(\"One point\");\n    auto vars = make_with_random_values<Vars>(make_not_null(&gen),\n                                              make_not_null(&dist), 1_st);\n    const auto orig_scalar = get<TestHelpers::Tags::Scalar<VectorType>>(vars);\n    const auto* const data = vars.data();\n    std::array<value_type, Vars::number_of_independent_components> copy{};\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    std::copy(data, data + Vars::number_of_independent_components, copy.data());\n    VectorType transferred = std::move(vars).release();\n    CHECK(transferred.is_owning());\n    CHECK(transferred.size() == Vars::number_of_independent_components);\n    const auto* const transferred_data = transferred.data();\n    CHECK(transferred_data != data);\n    CHECK(std::equal(transferred.begin(), transferred.end(), copy.begin(),\n                     copy.end()));\n    CHECK(vars.size() == 0);  // NOLINT(bugprone-use-after-move)\n    const Vars transferred_back(std::move(transferred));\n    CHECK(transferred_back.is_owning());\n    CHECK(transferred_back.data() != transferred_data);\n    CHECK(transferred_back.number_of_grid_points() == 1);\n    CHECK(transferred_back.size() == Vars::number_of_independent_components);\n    CHECK(get<TestHelpers::Tags::Scalar<VectorType>>(transferred_back) ==\n          orig_scalar);\n  }\n  {\n    INFO(\"Zero points\");\n    Vars vars{};\n    VectorType transferred = std::move(vars).release();\n    CHECK(transferred.is_owning());\n    CHECK(transferred.size() == 0);\n    CHECK(vars.size() == 0);  // NOLINT(bugprone-use-after-move)\n    const Vars transferred_back(std::move(transferred));\n    CHECK(transferred_back.is_owning());\n    CHECK(transferred_back.number_of_grid_points() == 0);\n    CHECK(transferred_back.size() == 0);\n  }\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([] {\n        VectorType vector(Vars::number_of_independent_components);\n        VectorType non_owning_vector(vector.data(), vector.size());\n        Vars{std::move(non_owning_vector)};\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot use a non-owning vector as Variables storage.\"));\n  CHECK_THROWS_WITH(\n      ([] {\n        VectorType vector(Vars::number_of_independent_components + 1);\n        Vars{std::move(vector)};\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"must be a multiple of the number of independent components\"));\n  CHECK_THROWS_WITH(([] {\n                      VectorType vector(Vars::number_of_independent_components);\n                      Vars vars{vector.data(), vector.size()};\n                      std::move(vars).release();\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot release storage from a non-owning Variables.\"));\n#endif  // defined(SPECTRE_DEBUG)\n}\n\ntemplate <typename VectorType>\nvoid test_clear() {\n  INFO(pretty_type::short_name<VectorType>());\n  using value_type = typename VectorType::value_type;\n  using Vars = Variables<tmpl::list<TestHelpers::Tags::Vector<VectorType>,\n                                    TestHelpers::Tags::Scalar<VectorType>>>;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<value_type>> dist{-100.0,\n                                                                         100.0};\n  const auto test = [&dist, &gen](const size_t num_points) {\n    auto vars = make_with_random_values<Vars>(make_not_null(&gen),\n                                              make_not_null(&dist), num_points);\n    CHECK(vars.number_of_grid_points() == num_points);\n    CHECK(vars.size() == num_points * Vars::number_of_independent_components);\n    CHECK(get<1>(get<TestHelpers::Tags::Vector<VectorType>>(vars)).size() ==\n          num_points);\n    CHECK(get(get<TestHelpers::Tags::Scalar<VectorType>>(vars)).size() ==\n          num_points);\n    CHECK((vars * 1.0).size() == vars.size());\n    vars.initialize(0);\n    CHECK(vars.number_of_grid_points() == 0);\n    CHECK(vars.size() == 0);\n    CHECK(get<1>(get<TestHelpers::Tags::Vector<VectorType>>(vars)).size() == 0);\n    CHECK(get(get<TestHelpers::Tags::Scalar<VectorType>>(vars)).size() == 0);\n    CHECK((vars * 1.0).size() == vars.size());\n  };\n  UniformCustomDistribution<size_t> sdist{2, 5};\n  test(sdist(gen));\n  test(1);\n}\n\nvoid test_asserts() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        Variables<tmpl::list<TestHelpers::Tags::Vector<DataVector>,\n                             TestHelpers::Tags::Scalar<DataVector>,\n                             TestHelpers::Tags::Scalar2<DataVector>>>\n            vars(1, -3.0);\n        auto& tensor_in_vars = get<TestHelpers::Tags::Vector<DataVector>>(vars);\n        tensor_in_vars = tnsr::I<DataVector, 3>{10_st, -4.0};\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Must move into same size, not 10 into 1\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        Variables<tmpl::list<TestHelpers::Tags::Scalar<DataVector>>> vars;\n        get<TestHelpers::Tags::Scalar<DataVector>>(vars) =\n            Scalar<DataVector>{{{{0.}}}};\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Must move into same size, not 1 into 0\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        Variables<tmpl::list<TestHelpers::Tags::Scalar<DataVector>,\n                             TestHelpers::Tags::Scalar2<DataVector>>>\n            source(2, -3.0);\n        // Multiply by one to convert to an expression template.\n        (void)static_cast<\n            Variables<tmpl::list<TestHelpers::Tags::Vector<DataVector>>>>(\n            1.0 * source);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid size 4 for a Variables with 3 components.\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        Variables<tmpl::list<TestHelpers::Tags::Scalar<DataVector>,\n                             TestHelpers::Tags::Scalar2<DataVector>>>\n            source(2, -3.0);\n        Variables<tmpl::list<TestHelpers::Tags::Vector<DataVector>>>\n            destination;\n        // Multiply by one to convert to an expression template.\n        destination = 1.0 * source;\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid size 4 for a Variables with 3 components.\"));\n#endif // defined(SPECTRE_DEBUG)\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Variables\", \"[DataStructures][Unit]\") {\n  {\n    INFO(\"Test Variables construction, access, and assignment\");\n    test_variables_construction_and_access<ComplexDataVector>();\n    test_variables_construction_and_access<ComplexModalVector>();\n    test_variables_construction_and_access<DataVector>();\n    test_variables_construction_and_access<ModalVector>();\n  }\n\n  {\n    INFO(\"Test Variables move operations\");\n    test_variables_move<ComplexDataVector>();\n    test_variables_move<ComplexModalVector>();\n    test_variables_move<DataVector>();\n    test_variables_move<ModalVector>();\n  }\n\n  {\n    INFO(\"Test Variables arithmetic operations\");\n    test_variables_math<DataVector>();\n    test_variables_math<ComplexDataVector>();\n    // tests for ModalVector and ComplexModalVector omitted due to limited\n    // arithmetic operation support for ModalVectors\n  }\n\n  {\n    INFO(\"Test Prefix Variables move and copy semantics\");\n    test_variables_prefix_semantics<ComplexDataVector>();\n    test_variables_prefix_semantics<ComplexModalVector>();\n    test_variables_prefix_semantics<DataVector>();\n    test_variables_prefix_semantics<ModalVector>();\n  }\n\n  {\n    INFO(\"Test Prefix Variables arithmetic operations\");\n    test_variables_prefix_math<ComplexDataVector>();\n    test_variables_prefix_math<ComplexModalVector>();\n    test_variables_prefix_math<DataVector>();\n    test_variables_prefix_math<ModalVector>();\n  }\n\n  {\n    INFO(\"Test Variables serialization\");\n    test_variables_serialization<ComplexDataVector>();\n    test_variables_serialization<ComplexModalVector>();\n    test_variables_serialization<DataVector>();\n    test_variables_serialization<ModalVector>();\n  }\n\n  {\n    INFO(\"Test Variables assign subset\");\n    test_variables_assign_subset<ComplexDataVector>();\n    test_variables_assign_subset<ComplexModalVector>();\n    test_variables_assign_subset<DataVector>();\n    test_variables_assign_subset<ModalVector>();\n  }\n\n  {\n    INFO(\"Test Variables extract subset\");\n    test_variables_extract_subset<ComplexDataVector>();\n    test_variables_extract_subset<ComplexModalVector>();\n    test_variables_extract_subset<DataVector>();\n    test_variables_extract_subset<ModalVector>();\n  }\n\n  {\n    INFO(\"Test Variables reference subset\");\n    test_variables_reference_subset<ComplexDataVector>();\n    test_variables_reference_subset<ComplexModalVector>();\n    test_variables_reference_subset<DataVector>();\n    test_variables_reference_subset<ModalVector>();\n  }\n\n  {\n    INFO(\"Test Variables reference_with_different_prefixes\");\n    test_variables_reference_with_different_prefixes<ComplexDataVector>();\n    test_variables_reference_with_different_prefixes<ComplexModalVector>();\n    test_variables_reference_with_different_prefixes<DataVector>();\n    test_variables_reference_with_different_prefixes<ModalVector>();\n  }\n\n  {\n    INFO(\"Test variables_from_tagged_tuple\");\n    test_variables_from_tagged_tuple<ComplexDataVector>();\n    test_variables_from_tagged_tuple<ComplexModalVector>();\n    test_variables_from_tagged_tuple<DataVector>();\n    test_variables_from_tagged_tuple<ModalVector>();\n  }\n\n  {\n    INFO(\"Test equal_within_roundoff with Variables\");\n    test_variables_equal_within_roundoff<ComplexDataVector>();\n    test_variables_equal_within_roundoff<ComplexModalVector>();\n    test_variables_equal_within_roundoff<DataVector>();\n    test_variables_equal_within_roundoff<ModalVector>();\n  }\n\n  {\n    INFO(\"Test MathWrapper\");\n    test_math_wrapper<ComplexDataVector>();\n    test_math_wrapper<ComplexModalVector>();\n    test_math_wrapper<DataVector>();\n    test_math_wrapper<ModalVector>();\n  }\n\n  {\n    INFO(\"Test into_math_wrapper_type\");\n    // Only implemented for nodal data.\n    test_into_math_wrapper_type<ComplexDataVector>();\n    // test_math_wrapper<ComplexModalVector>();\n    test_into_math_wrapper_type<DataVector>();\n    // test_math_wrapper<ModalVector>();\n  }\n\n  {\n    INFO(\"Test contains_allocations\");\n    test_contains_allocations<ComplexDataVector>();\n    test_contains_allocations<ComplexModalVector>();\n    test_contains_allocations<DataVector>();\n    test_contains_allocations<ModalVector>();\n  }\n\n  {\n    INFO(\"Test make_with_value\");\n    test_make_with_value<ComplexDataVector>();\n    test_make_with_value<ComplexModalVector>();\n    test_make_with_value<DataVector>();\n    test_make_with_value<ModalVector>();\n  }\n\n  {\n    INFO(\"Test set_number_of_grid_points\");\n    test_set_number_of_grid_points<ComplexDataVector>();\n    test_set_number_of_grid_points<ComplexModalVector>();\n    test_set_number_of_grid_points<DataVector>();\n    test_set_number_of_grid_points<ModalVector>();\n  }\n\n  {\n    INFO(\"Test transferring ownership with vectors\");\n    test_vector_ownership<ComplexDataVector>();\n    test_vector_ownership<ComplexModalVector>();\n    test_vector_ownership<DataVector>();\n    test_vector_ownership<ModalVector>();\n  }\n\n  {\n    INFO(\"Test clearing\");\n    test_clear<ComplexDataVector>();\n    test_clear<ComplexModalVector>();\n    test_clear<DataVector>();\n    test_clear<ModalVector>();\n  }\n\n  TestHelpers::db::test_simple_tag<Tags::TempScalar<1>>(\"TempTensor1\");\n\n  SECTION(\"Test empty variables\") { test_empty_variables(); }\n\n  test_asserts();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_VectorImpl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n// [get_vector_element_type_example]\nstatic_assert(std::is_same_v<get_vector_element_type_t<DataVector>, double>,\n              \"Failed testing type trait get_vector_element_type\");\n\nstatic_assert(std::is_same_v<\n                  get_vector_element_type_t<std::array<DataVector, 2>>, double>,\n              \"Failed testing type trait get_vector_element_type\");\n\nstatic_assert(std::is_same_v<get_vector_element_type_t<std::complex<double>*>,\n                             std::complex<double>>,\n              \"Failed testing type trait get_vector_element_type\");\n\nstatic_assert(std::is_same_v<get_vector_element_type_t<DataVector&>, double>,\n              \"Failed testing type trait get_vector_element_type\");\n// [get_vector_element_type_example]\n\nstatic_assert(is_derived_of_vector_impl_v<DataVector>,\n              \"Failed testing type trait is_derived_of_vector_impl\");\nstatic_assert(is_derived_of_vector_impl_v<ComplexDataVector>,\n              \"Failed testing type trait is_derived_of_vector_impl\");\nstatic_assert(not is_derived_of_vector_impl_v<std::complex<double>>,\n              \"Failed testing type trait is_derived_of_vector_impl\");\nstatic_assert(not is_derived_of_vector_impl_v<std::vector<int>>,\n              \"Failed testing type trait is_derived_of_vector_impl\");\n\n"
  },
  {
    "path": "tests/Unit/DataStructures/Test_VectorImplTestHelper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Helpers/DataStructures/VectorImplTestHelper.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace TestHelpers {\nnamespace VectorImpl {\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.VectorImplTestHelper\",\n                  \"[DataStructures][Unit]\") {\n  // Test PUP size of owning and non-owning objects\n  DataVector owning{1.0, 4.0};\n  CHECK(size_of_object_in_bytes(owning) == 24);\n  DataVector non_owning{owning.data(), owning.size()};\n  REQUIRE(not non_owning.is_owning());\n  CHECK(size_of_object_in_bytes(non_owning) == 0);\n\n  // testing size utility\n  std::array<DataVector, 3> array_of_vectors = {\n      {{{1.0, 4.0}}, {{2.0, 5.0}}, {{3.0, 6.0}}}};\n  CHECK(get_size(array_of_vectors, detail::VectorOrArraySize{}) == 6);\n\n  std::array<ComplexDataVector, 3> array_of_complex_vectors = {\n      {{{std::complex<double>(1.0, 1.5), std::complex<double>(4.0, 4.5)}},\n       {{std::complex<double>(2.0, 2.5), std::complex<double>(5.0, 5.5)}},\n       {{std::complex<double>(3.0, 3.5), std::complex<double>(6.0, 6.5)}}}};\n  CHECK(get_size(array_of_complex_vectors, detail::VectorOrArraySize{}) == 6);\n\n  DataVector vector = {{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}};\n  CHECK(get_size(vector, detail::VectorOrArraySize{}) == 6);\n\n  ComplexDataVector complex_vector = {\n      {std::complex<double>(1.0, 1.5), std::complex<double>(2.0, 2.5),\n       std::complex<double>(3.0, 3.5), std::complex<double>(4.0, 4.5),\n       std::complex<double>(5.0, 5.5), std::complex<double>(6.0, 6.5)}};\n  CHECK(get_size(complex_vector, detail::VectorOrArraySize{}) == 6);\n\n  CHECK(get_size(1.0, detail::VectorOrArraySize{}) == 1);\n  CHECK(get_size(std::complex<double>(1.0, 1.5), detail::VectorOrArraySize{}) ==\n        1);\n\n  // testing indexing utility\n  const std::array<DataVector, 3> constant_array_of_vectors = {\n      {{{1.0, 4.0}}, {{2.0, 5.0}}, {{3.0, 6.0}}}};\n  const std::array<ComplexDataVector, 3> constant_array_of_complex_vectors = {\n      {{{std::complex<double>(1.0, 1.5), std::complex<double>(4.0, 4.5)}},\n       {{std::complex<double>(2.0, 2.5), std::complex<double>(5.0, 5.5)}},\n       {{std::complex<double>(3.0, 3.5), std::complex<double>(6.0, 6.5)}}}};\n\n  double fundamental_to_index = 9.0;\n  std::complex<double> complex_to_index = std::complex<double>(9.0, 9.5);\n\n  CHECK(get_element(constant_array_of_vectors, 1, detail::VectorOrArrayAt{}) ==\n        2.0);\n\n  CHECK(get_element(vector, 2, detail::VectorOrArrayAt{}) == 3.0);\n  get_element(vector, 2, detail::VectorOrArrayAt{}) = 10.0;\n  CHECK(get_element(vector, 2, detail::VectorOrArrayAt{}) == 10.0);\n\n  CHECK(get_element(array_of_vectors, 0, detail::VectorOrArrayAt{}) == 1.0);\n  get_element(array_of_vectors, 0, detail::VectorOrArrayAt{}) = 11.0;\n  CHECK(get_element(array_of_vectors, 0, detail::VectorOrArrayAt{}) == 11.0);\n\n  CHECK(get_element(fundamental_to_index, 4, detail::VectorOrArrayAt{}) == 9.0);\n  get_element(fundamental_to_index, 5, detail::VectorOrArrayAt{}) = 12.0;\n  CHECK(get_element(fundamental_to_index, 0, detail::VectorOrArrayAt{}) ==\n        12.0);\n\n  CHECK(get_element(constant_array_of_complex_vectors, 1,\n                    detail::VectorOrArrayAt{}) ==\n        std::complex<double>(2.0, 2.5));\n\n  CHECK(get_element(complex_vector, 2, detail::VectorOrArrayAt{}) ==\n        std::complex<double>(3.0, 3.5));\n  get_element(complex_vector, 2, detail::VectorOrArrayAt{}) =\n      std::complex<double>(10.0, 10.5);\n  CHECK(get_element(complex_vector, 2, detail::VectorOrArrayAt{}) ==\n        std::complex<double>(10.0, 10.5));\n\n  CHECK(get_element(array_of_complex_vectors, 0, detail::VectorOrArrayAt{}) ==\n        std::complex<double>(1.0, 1.5));\n  get_element(array_of_complex_vectors, 0, detail::VectorOrArrayAt{}) =\n      std::complex<double>(11.0, 11.5);\n  CHECK(get_element(array_of_complex_vectors, 0, detail::VectorOrArrayAt{}) ==\n        std::complex<double>(11.0, 11.5));\n\n  CHECK(get_element(complex_to_index, 4, detail::VectorOrArrayAt{}) ==\n        std::complex<double>(9.0, 9.5));\n  get_element(complex_to_index, 5, detail::VectorOrArrayAt{}) =\n      std::complex<double>(12.0, 12.5);\n  CHECK(get_element(complex_to_index, 0, detail::VectorOrArrayAt{}) ==\n        std::complex<double>(12.0, 12.5));\n}\n}  // namespace VectorImpl\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Amr\")\n\nset(LIBRARY_SOURCES\n  Test_Flag.cpp\n  Test_Helpers.cpp\n  Test_Info.cpp\n  Test_NeighborsOfChild.cpp\n  Test_NeighborsOfParent.cpp\n  Test_NewNeighborIds.cpp\n  Test_Tags.cpp\n  Test_UpdateAmrDecision.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Amr\n  Boost::boost\n  DataStructures\n  DataStructuresHelpers\n  DomainAmrHelpers\n  DomainHelpers\n  DomainStructure\n  DomainStructureHelpers\n  ErrorHandling\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/Test_Flag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Amr.Flag\", \"[Domain][Unit]\") {\n  CHECK(get_output(amr::Flag::Undefined) == \"Undefined\");\n  CHECK(get_output(amr::Flag::Join) == \"Join\");\n  CHECK(get_output(amr::Flag::DecreaseResolution) == \"DecreaseResolution\");\n  CHECK(get_output(amr::Flag::DoNothing) == \"DoNothing\");\n  CHECK(get_output(amr::Flag::IncreaseResolution) == \"IncreaseResolution\");\n  CHECK(get_output(amr::Flag::Split) == \"Split\");\n\n  const std::vector known_amr_flags{\n      amr::Flag::Undefined,          amr::Flag::Join,\n      amr::Flag::DecreaseResolution, amr::Flag::DoNothing,\n      amr::Flag::IncreaseResolution, amr::Flag::Split};\n\n  for (const auto flag : known_amr_flags) {\n    CHECK(flag == TestHelpers::test_creation<amr::Flag>(get_output(flag)));\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() { TestHelpers::test_creation<amr::Flag>(\"Bad flag name\"); }()),\n      Catch::Matchers::ContainsSubstring(\n          MakeString{} << \"Failed to convert \\\"Bad flag name\\\" to \"\n                          \"amr::Flag.\\nMust be one of \"\n                       << known_amr_flags << \".\"));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/Test_Helpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/rational.hpp>\n#include <cstddef>\n#include <deque>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid test_desired_refinement_levels() {\n  const ElementId<1> element_id_1d{0, {{SegmentId(2, 3)}}};\n  CHECK(amr::desired_refinement_levels(element_id_1d, {{amr::Flag::Split}}) ==\n        std::array<size_t, 1>{{3}});\n  CHECK(\n      amr::desired_refinement_levels(element_id_1d, {{amr::Flag::DoNothing}}) ==\n      std::array<size_t, 1>{{2}});\n  CHECK(amr::desired_refinement_levels(element_id_1d, {{amr::Flag::Join}}) ==\n        std::array<size_t, 1>{{1}});\n\n  const ElementId<2> element_id_2d{1, {{SegmentId(3, 5), SegmentId(1, 1)}}};\n  CHECK(amr::desired_refinement_levels(element_id_2d,\n                                       {{amr::Flag::Split, amr::Flag::Join}}) ==\n        std::array<size_t, 2>{{4, 0}});\n  CHECK(amr::desired_refinement_levels(\n            element_id_2d, {{amr::Flag::Join, amr::Flag::DoNothing}}) ==\n        std::array<size_t, 2>{{2, 1}});\n  CHECK(amr::desired_refinement_levels(element_id_2d,\n                                       {{amr::Flag::Join, amr::Flag::Join}}) ==\n        std::array<size_t, 2>{{2, 0}});\n  CHECK(amr::desired_refinement_levels(\n            element_id_2d, {{amr::Flag::DoNothing, amr::Flag::Split}}) ==\n        std::array<size_t, 2>{{3, 2}});\n  CHECK(amr::desired_refinement_levels(\n            element_id_2d, {{amr::Flag::DoNothing, amr::Flag::DoNothing}}) ==\n        std::array<size_t, 2>{{3, 1}});\n\n  const ElementId<3> element_id_3d{\n      7, {{SegmentId(5, 15), SegmentId(2, 0), SegmentId(4, 6)}}};\n  CHECK(amr::desired_refinement_levels(\n            element_id_3d,\n            {{amr::Flag::Split, amr::Flag::Join, amr::Flag::DoNothing}}) ==\n        std::array<size_t, 3>{{6, 1, 4}});\n}\n\ntemplate <size_t VolumeDim>\nvoid check_desired_refinement_levels_of_neighbor(\n    const ElementId<VolumeDim>& neighbor_id,\n    const std::array<amr::Flag, VolumeDim>& neighbor_flags) {\n  for (OrientationMapIterator<VolumeDim> orientation{}; orientation;\n       ++orientation) {\n    const auto desired_levels_my_frame = desired_refinement_levels_of_neighbor(\n        neighbor_id, neighbor_flags, *orientation);\n    const auto desired_levels_neighbor_frame =\n        desired_refinement_levels(neighbor_id, neighbor_flags);\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      CHECK(gsl::at(desired_levels_my_frame, d) ==\n            gsl::at(desired_levels_neighbor_frame, (*orientation)(d)));\n    }\n  }\n}\n\nvoid test_desired_refinement_levels_of_neighbor() {\n  const ElementId<1> neighbor_id_1d{0, {{SegmentId(2, 3)}}};\n  check_desired_refinement_levels_of_neighbor(neighbor_id_1d,\n                                              {{amr::Flag::Split}});\n  check_desired_refinement_levels_of_neighbor(neighbor_id_1d,\n                                              {{amr::Flag::DoNothing}});\n  check_desired_refinement_levels_of_neighbor(neighbor_id_1d,\n                                              {{amr::Flag::Join}});\n\n  const ElementId<2> neighbor_id_2d{1, {{SegmentId(3, 0), SegmentId(1, 1)}}};\n  check_desired_refinement_levels_of_neighbor(\n      neighbor_id_2d, {{amr::Flag::Split, amr::Flag::Join}});\n  check_desired_refinement_levels_of_neighbor(\n      neighbor_id_2d, {{amr::Flag::Join, amr::Flag::DoNothing}});\n  check_desired_refinement_levels_of_neighbor(\n      neighbor_id_2d, {{amr::Flag::Join, amr::Flag::Join}});\n  check_desired_refinement_levels_of_neighbor(\n      neighbor_id_2d, {{amr::Flag::DoNothing, amr::Flag::Split}});\n  check_desired_refinement_levels_of_neighbor(\n      neighbor_id_2d, {{amr::Flag::DoNothing, amr::Flag::DoNothing}});\n\n  const ElementId<3> neighbor_id_3d{\n      7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(4, 15)}}};\n  check_desired_refinement_levels_of_neighbor(\n      neighbor_id_3d,\n      {{amr::Flag::Split, amr::Flag::Join, amr::Flag::DoNothing}});\n  check_desired_refinement_levels_of_neighbor(\n      neighbor_id_3d,\n      {{amr::Flag::Join, amr::Flag::DoNothing, amr::Flag::Split}});\n}\n\nvoid test_fraction_of_block_volume() {\n  const ElementId<1> element_id_1d{0, {{SegmentId(2, 3)}}};\n  CHECK(fraction_of_block_volume(element_id_1d) ==\n        boost::rational<size_t>(1, 4));\n  const ElementId<2> element_id_2d{0, {{SegmentId(3, 0), SegmentId{1, 1}}}};\n  CHECK(fraction_of_block_volume(element_id_2d) ==\n        boost::rational<size_t>(1, 16));\n  const ElementId<3> element_id_3d{\n      7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(4, 15)}}};\n  CHECK(fraction_of_block_volume(element_id_3d) ==\n        boost::rational<size_t>(1, 2048));\n}\n\nvoid test_has_potential_sibling() {\n  const ElementId<1> element_id_root{0, {{SegmentId(0, 0)}}};\n  CHECK_FALSE(\n      amr::has_potential_sibling(element_id_root, Direction<1>::lower_xi()));\n  CHECK_FALSE(\n      amr::has_potential_sibling(element_id_root, Direction<1>::upper_xi()));\n  const ElementId<1> element_id_1d{0, {{SegmentId(2, 3)}}};\n  CHECK(amr::has_potential_sibling(element_id_1d, Direction<1>::lower_xi()));\n  CHECK_FALSE(\n      amr::has_potential_sibling(element_id_1d, Direction<1>::upper_xi()));\n\n  const ElementId<2> element_id_2d{0, {{SegmentId(3, 0), SegmentId{1, 1}}}};\n  CHECK(amr::has_potential_sibling(element_id_2d, Direction<2>::upper_xi()));\n  CHECK(amr::has_potential_sibling(element_id_2d, Direction<2>::lower_eta()));\n  CHECK_FALSE(\n      amr::has_potential_sibling(element_id_2d, Direction<2>::lower_xi()));\n  CHECK_FALSE(\n      amr::has_potential_sibling(element_id_2d, Direction<2>::upper_eta()));\n\n  const ElementId<3> element_id_3d{\n      7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(4, 15)}}};\n  CHECK(amr::has_potential_sibling(element_id_3d, Direction<3>::lower_xi()));\n  CHECK(amr::has_potential_sibling(element_id_3d, Direction<3>::upper_eta()));\n  CHECK(amr::has_potential_sibling(element_id_3d, Direction<3>::lower_zeta()));\n  CHECK_FALSE(\n      amr::has_potential_sibling(element_id_3d, Direction<3>::upper_xi()));\n  CHECK_FALSE(\n      amr::has_potential_sibling(element_id_3d, Direction<3>::lower_eta()));\n  CHECK_FALSE(\n      amr::has_potential_sibling(element_id_3d, Direction<3>::upper_zeta()));\n}\n\nvoid test_id_of_parent() {\n  const ElementId<1> element_id_1d{0, {{SegmentId(2, 3)}}};\n  CHECK(amr::id_of_parent(element_id_1d, std::array{amr::Flag::Join}) ==\n        ElementId<1>{0, {{SegmentId(1, 1)}}});\n  const ElementId<2> element_id_2d{0, {{SegmentId(3, 0), SegmentId{1, 1}}}};\n  CHECK(amr::id_of_parent(element_id_2d,\n                          std::array{amr::Flag::Join, amr::Flag::Join}) ==\n        ElementId<2>{0, {{SegmentId(2, 0), SegmentId(0, 0)}}});\n  CHECK(amr::id_of_parent(element_id_2d,\n                          std::array{amr::Flag::DoNothing, amr::Flag::Join}) ==\n        ElementId<2>{0, {{SegmentId(3, 0), SegmentId(0, 0)}}});\n  CHECK(amr::id_of_parent(element_id_2d,\n                          std::array{amr::Flag::Join, amr::Flag::DoNothing}) ==\n        ElementId<2>{0, {{SegmentId(2, 0), SegmentId(1, 1)}}});\n  const ElementId<3> element_id_3d{\n      7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(4, 15)}}};\n  CHECK(\n      amr::id_of_parent(\n          element_id_3d,\n          std::array{amr::Flag::Join, amr::Flag::Join, amr::Flag::Join}) ==\n      ElementId<3>{7, {{SegmentId(4, 15), SegmentId(1, 0), SegmentId(3, 7)}}});\n  CHECK(\n      amr::id_of_parent(\n          element_id_3d,\n          std::array{amr::Flag::Join, amr::Flag::Join, amr::Flag::DoNothing}) ==\n      ElementId<3>{7, {{SegmentId(4, 15), SegmentId(1, 0), SegmentId(4, 15)}}});\n  CHECK(\n      amr::id_of_parent(\n          element_id_3d,\n          std::array{amr::Flag::Join, amr::Flag::DoNothing, amr::Flag::Join}) ==\n      ElementId<3>{7, {{SegmentId(4, 15), SegmentId(2, 0), SegmentId(3, 7)}}});\n  CHECK(\n      amr::id_of_parent(\n          element_id_3d,\n          std::array{amr::Flag::DoNothing, amr::Flag::Join, amr::Flag::Join}) ==\n      ElementId<3>{7, {{SegmentId(5, 31), SegmentId(1, 0), SegmentId(3, 7)}}});\n  CHECK(\n      amr::id_of_parent(element_id_3d,\n                        std::array{amr::Flag::Join, amr::Flag::DoNothing,\n                                   amr::Flag::DoNothing}) ==\n      ElementId<3>{7, {{SegmentId(4, 15), SegmentId(2, 0), SegmentId(4, 15)}}});\n  CHECK(\n      amr::id_of_parent(element_id_3d,\n                        std::array{amr::Flag::DoNothing, amr::Flag::Join,\n                                   amr::Flag::DoNothing}) ==\n      ElementId<3>{7, {{SegmentId(5, 31), SegmentId(1, 0), SegmentId(4, 15)}}});\n  CHECK(\n      amr::id_of_parent(element_id_3d,\n                        std::array{amr::Flag::DoNothing, amr::Flag::DoNothing,\n                                   amr::Flag::Join}) ==\n      ElementId<3>{7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(3, 7)}}});\n}\n\nvoid test_ids_of_children() {\n  const ElementId<1> element_id_1d{0, {{SegmentId(2, 3)}}};\n  CHECK(amr::ids_of_children(element_id_1d, std::array{amr::Flag::Split}) ==\n        std::vector{ElementId<1>{0, {{SegmentId(3, 6)}}},\n                    ElementId<1>{0, {{SegmentId(3, 7)}}}});\n  const ElementId<2> element_id_2d{0, {{SegmentId(3, 0), SegmentId{1, 1}}}};\n  CHECK(amr::ids_of_children(element_id_2d,\n                             std::array{amr::Flag::Split, amr::Flag::Split}) ==\n        std::vector{ElementId<2>{0, {{SegmentId(4, 0), SegmentId(2, 2)}}},\n                    ElementId<2>{0, {{SegmentId(4, 0), SegmentId(2, 3)}}},\n                    ElementId<2>{0, {{SegmentId(4, 1), SegmentId(2, 2)}}},\n                    ElementId<2>{0, {{SegmentId(4, 1), SegmentId(2, 3)}}}});\n  CHECK(amr::ids_of_children(element_id_2d, std::array{amr::Flag::DoNothing,\n                                                       amr::Flag::Split}) ==\n        std::vector{ElementId<2>{0, {{SegmentId(3, 0), SegmentId(2, 2)}}},\n                    ElementId<2>{0, {{SegmentId(3, 0), SegmentId(2, 3)}}}});\n  CHECK(amr::ids_of_children(element_id_2d,\n                             std::array{amr::Flag::Split, amr::Flag::DoNothing},\n                             1) ==\n        std::vector{ElementId<2>{0, {{SegmentId(4, 0), SegmentId(1, 1)}}, 1},\n                    ElementId<2>{0, {{SegmentId(4, 1), SegmentId(1, 1)}}, 1}});\n  const ElementId<3> element_id_3d{\n      7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(4, 15)}}};\n  CHECK(amr::ids_of_children(\n            element_id_3d,\n            std::array{amr::Flag::Split, amr::Flag::Split, amr::Flag::Split}) ==\n        std::vector{\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(3, 0), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(3, 0), SegmentId(5, 31)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(3, 1), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(3, 1), SegmentId(5, 31)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(3, 0), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(3, 0), SegmentId(5, 31)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(3, 1), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(3, 1), SegmentId(5, 31)}}}});\n  CHECK(amr::ids_of_children(element_id_3d,\n                             std::array{amr::Flag::Split, amr::Flag::Split,\n                                        amr::Flag::DoNothing}) ==\n        std::vector{\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(3, 0), SegmentId(4, 15)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(3, 1), SegmentId(4, 15)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(3, 0), SegmentId(4, 15)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(3, 1), SegmentId(4, 15)}}}});\n  CHECK(amr::ids_of_children(element_id_3d,\n                             std::array{amr::Flag::Split, amr::Flag::DoNothing,\n                                        amr::Flag::Split}) ==\n        std::vector{\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(2, 0), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(2, 0), SegmentId(5, 31)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(2, 0), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(2, 0), SegmentId(5, 31)}}}});\n  CHECK(amr::ids_of_children(element_id_3d,\n                             std::array{amr::Flag::DoNothing, amr::Flag::Split,\n                                        amr::Flag::Split}) ==\n        std::vector{\n            ElementId<3>{\n                7, {{SegmentId(5, 31), SegmentId(3, 0), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(5, 31), SegmentId(3, 0), SegmentId(5, 31)}}},\n            ElementId<3>{\n                7, {{SegmentId(5, 31), SegmentId(3, 1), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(5, 31), SegmentId(3, 1), SegmentId(5, 31)}}}});\n  CHECK(amr::ids_of_children(element_id_3d,\n                             std::array{amr::Flag::Split, amr::Flag::DoNothing,\n                                        amr::Flag::DoNothing}) ==\n        std::vector{\n            ElementId<3>{\n                7, {{SegmentId(6, 62), SegmentId(2, 0), SegmentId(4, 15)}}},\n            ElementId<3>{\n                7, {{SegmentId(6, 63), SegmentId(2, 0), SegmentId(4, 15)}}}});\n  CHECK(amr::ids_of_children(element_id_3d,\n                             std::array{amr::Flag::DoNothing, amr::Flag::Split,\n                                        amr::Flag::DoNothing}) ==\n        std::vector{\n            ElementId<3>{\n                7, {{SegmentId(5, 31), SegmentId(3, 0), SegmentId(4, 15)}}},\n            ElementId<3>{\n                7, {{SegmentId(5, 31), SegmentId(3, 1), SegmentId(4, 15)}}}});\n  CHECK(amr::ids_of_children(element_id_3d, std::array{amr::Flag::DoNothing,\n                                                       amr::Flag::DoNothing,\n                                                       amr::Flag::Split}) ==\n        std::vector{\n            ElementId<3>{\n                7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(5, 30)}}},\n            ElementId<3>{\n                7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(5, 31)}}}});\n}\nElement<1> make_element(\n    const ElementId<1>& element_id,\n    const std::unordered_set<ElementId<1>>& lower_xi_neighbor_ids,\n    const std::unordered_set<ElementId<1>>& upper_xi_neighbor_ids) {\n  DirectionMap<1, Neighbors<1>> neighbors;\n  if (not lower_xi_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<1>::lower_xi(),\n                      Neighbors<1>{{lower_xi_neighbor_ids},\n                                   OrientationMap<1>::create_aligned()});\n  }\n  if (not upper_xi_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<1>::upper_xi(),\n                      Neighbors<1>{{upper_xi_neighbor_ids},\n                                   OrientationMap<1>::create_aligned()});\n  }\n  return Element<1>{element_id, std::move(neighbors)};\n}\n\nElement<2> make_element(\n    const ElementId<2>& element_id,\n    const std::unordered_set<ElementId<2>>& lower_xi_neighbor_ids,\n    const std::unordered_set<ElementId<2>>& upper_xi_neighbor_ids,\n    const std::unordered_set<ElementId<2>>& lower_eta_neighbor_ids,\n    const std::unordered_set<ElementId<2>>& upper_eta_neighbor_ids) {\n  DirectionMap<2, Neighbors<2>> neighbors;\n  if (not lower_xi_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<2>::lower_xi(),\n                      Neighbors<2>{{lower_xi_neighbor_ids},\n                                   OrientationMap<2>::create_aligned()});\n  }\n  if (not upper_xi_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<2>::upper_xi(),\n                      Neighbors<2>{{upper_xi_neighbor_ids},\n                                   OrientationMap<2>::create_aligned()});\n  }\n  if (not lower_eta_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<2>::lower_eta(),\n                      Neighbors<2>{{lower_eta_neighbor_ids},\n                                   OrientationMap<2>::create_aligned()});\n  }\n  if (not upper_eta_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<2>::upper_eta(),\n                      Neighbors<2>{{upper_eta_neighbor_ids},\n                                   OrientationMap<2>::create_aligned()});\n  }\n  return Element<2>{element_id, std::move(neighbors)};\n}\n\nvoid test_ids_of_joining_neighbors() {\n  const SegmentId xi_segment{3, 5};\n  const SegmentId xi_cousin{3, 6};\n  const SegmentId xi_sibling{3, 4};\n  const ElementId<1> element_id_1d{0, {{xi_segment}}};\n  const ElementId<1> sibling_id{0, {{xi_sibling}}};\n  const ElementId<1> id_uxi_c{0, {{xi_cousin}}};\n  const ElementId<1> id_uxi_cp{0, {{xi_cousin.id_of_parent()}}};\n  const ElementId<1> id_uxi_cc{0, {{xi_cousin.id_of_child(Side::Lower)}}};\n  const auto join = std::array{amr::Flag::Join};\n  for (const auto id_uxi : std::vector{id_uxi_c, id_uxi_cp, id_uxi_cc}) {\n    const auto element = make_element(element_id_1d, {sibling_id}, {id_uxi});\n    CHECK(amr::ids_of_joining_neighbors(element, join) ==\n          std::deque{sibling_id});\n  }\n\n  const SegmentId eta_segment{6, 15};\n  const SegmentId eta_cousin{6, 16};\n  const SegmentId eta_sibling{6, 14};\n  const ElementId<2> element_id_2d{0, {{xi_segment, eta_segment}}};\n  const ElementId<2> id_lxi_s_s{0, {{xi_sibling, eta_segment}}};\n  const ElementId<2> id_lxi_s_p{0, {{xi_sibling, eta_segment.id_of_parent()}}};\n  const ElementId<2> id_leta_s_s{0, {{xi_segment, eta_sibling}}};\n  const ElementId<2> id_leta_p_s{0, {{xi_segment.id_of_parent(), eta_sibling}}};\n  const ElementId<2> id_uxi{0, {{xi_cousin, eta_segment}}};\n  const ElementId<2> id_ueta{0, {{xi_segment, eta_cousin}}};\n  const std::array<amr::Flag, 2> join_join{{amr::Flag::Join, amr::Flag::Join}};\n  CHECK(amr::ids_of_joining_neighbors(\n            make_element(element_id_2d, {id_lxi_s_s}, {id_uxi}, {id_leta_s_s},\n                         {id_ueta}),\n            join_join) == std::deque{id_lxi_s_s, id_leta_s_s});\n  CHECK(amr::ids_of_joining_neighbors(\n            make_element(element_id_2d, {id_lxi_s_s}, {id_uxi}, {id_leta_p_s},\n                         {id_ueta}),\n            join_join) == std::deque{id_lxi_s_s, id_leta_p_s});\n  CHECK(amr::ids_of_joining_neighbors(\n            make_element(element_id_2d, {id_lxi_s_p}, {id_uxi}, {id_leta_s_s},\n                         {id_ueta}),\n            join_join) == std::deque{id_lxi_s_p, id_leta_s_s});\n  const std::array<amr::Flag, 2> join_stay{\n      {amr::Flag::Join, amr::Flag::DoNothing}};\n  CHECK(amr::ids_of_joining_neighbors(\n            make_element(element_id_2d, {id_lxi_s_s}, {id_uxi}, {id_leta_s_s},\n                         {id_ueta}),\n            join_stay) == std::deque{id_lxi_s_s});\n  CHECK(amr::ids_of_joining_neighbors(\n            make_element(element_id_2d, {id_lxi_s_s}, {id_uxi}, {id_leta_p_s},\n                         {id_ueta}),\n            join_stay) == std::deque{id_lxi_s_s});\n  const std::array<amr::Flag, 2> stay_join{\n      {amr::Flag::DoNothing, amr::Flag::Join}};\n  CHECK(amr::ids_of_joining_neighbors(\n            make_element(element_id_2d, {id_lxi_s_s}, {id_uxi}, {id_leta_s_s},\n                         {id_ueta}),\n            stay_join) == std::deque{id_leta_s_s});\n  CHECK(amr::ids_of_joining_neighbors(\n            make_element(element_id_2d, {id_lxi_s_p}, {id_uxi}, {id_leta_s_s},\n                         {id_ueta}),\n            stay_join) == std::deque{id_leta_s_s});\n}\n\nvoid test_is_child_that_creates_parent() {\n  const SegmentId xi_segment{3, 5};\n  const auto join = std::array{amr::Flag::Join};\n  CHECK_FALSE(\n      amr::is_child_that_creates_parent(ElementId<1>{0, {xi_segment}}, join));\n  CHECK(amr::is_child_that_creates_parent(\n      ElementId<1>{0, {xi_segment.id_of_sibling()}}, join));\n  const SegmentId eta_segment{6, 15};\n  const auto join_join = std::array{amr::Flag::Join, amr::Flag::Join};\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<2>{0, {xi_segment, eta_segment}}, join_join));\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<2>{0, {xi_segment, eta_segment.id_of_sibling()}}, join_join));\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<2>{0, {xi_segment.id_of_sibling(), eta_segment}}, join_join));\n  CHECK(amr::is_child_that_creates_parent(\n      ElementId<2>{0,\n                   {xi_segment.id_of_sibling(), eta_segment.id_of_sibling()}},\n      join_join));\n  const auto join_stay = std::array{amr::Flag::Join, amr::Flag::DoNothing};\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<2>{0, {xi_segment, eta_segment}}, join_stay));\n  CHECK(amr::is_child_that_creates_parent(\n      ElementId<2>{0, {xi_segment.id_of_sibling(), eta_segment}}, join_stay));\n  const SegmentId zeta_segment{4, 6};\n  const auto join_join_join =\n      std::array{amr::Flag::Join, amr::Flag::Join, amr::Flag::Join};\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<3>{0, {xi_segment, eta_segment, zeta_segment}},\n      join_join_join));\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<3>{0, {xi_segment, eta_segment.id_of_sibling(), zeta_segment}},\n      join_join_join));\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<3>{0, {xi_segment.id_of_sibling(), eta_segment, zeta_segment}},\n      join_join_join));\n  CHECK(amr::is_child_that_creates_parent(\n      ElementId<3>{0,\n                   {xi_segment.id_of_sibling(), eta_segment.id_of_sibling(),\n                    zeta_segment}},\n      join_join_join));\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<3>{0, {xi_segment, eta_segment, zeta_segment.id_of_sibling()}},\n      join_join_join));\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<3>{0,\n                   {xi_segment, eta_segment.id_of_sibling(),\n                    zeta_segment.id_of_sibling()}},\n      join_join_join));\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<3>{0,\n                   {xi_segment.id_of_sibling(), eta_segment,\n                    zeta_segment.id_of_sibling()}},\n      join_join_join));\n  CHECK_FALSE(amr::is_child_that_creates_parent(\n      ElementId<3>{0,\n                   {xi_segment.id_of_sibling(), eta_segment.id_of_sibling(),\n                    zeta_segment.id_of_sibling()}},\n      join_join_join));\n}\n\ntemplate <size_t Dim>\nvoid check(std::array<amr::Flag, Dim> flags,\n           const std::array<amr::Flag, Dim> expected_flags) {\n  const bool flags_should_change = (flags != expected_flags);\n  CHECK(amr::prevent_element_from_joining_while_splitting(\n            make_not_null(&flags)) == flags_should_change);\n  CHECK(flags == expected_flags);\n}\n\nvoid test_prevent_element_from_joining_while_splitting() {\n  const auto join = std::array{amr::Flag::Join};\n  const auto split = std::array{amr::Flag::Split};\n  const auto stay = std::array{amr::Flag::DoNothing};\n  check(join, join);\n  check(split, split);\n  check(stay, stay);\n\n  const auto join_join = std::array{amr::Flag::Join, amr::Flag::Join};\n  const auto join_split = std::array{amr::Flag::Join, amr::Flag::Split};\n  const auto join_stay = std::array{amr::Flag::Join, amr::Flag::DoNothing};\n  const auto split_join = std::array{amr::Flag::Split, amr::Flag::Join};\n  const auto split_split = std::array{amr::Flag::Split, amr::Flag::Split};\n  const auto split_stay = std::array{amr::Flag::Split, amr::Flag::DoNothing};\n  const auto stay_join = std::array{amr::Flag::DoNothing, amr::Flag::Join};\n  const auto stay_split = std::array{amr::Flag::DoNothing, amr::Flag::Split};\n  const auto stay_stay = std::array{amr::Flag::DoNothing, amr::Flag::DoNothing};\n  check(join_join, join_join);\n  check(join_split, stay_split);\n  check(join_stay, join_stay);\n  check(split_join, split_stay);\n  check(split_split, split_split);\n  check(split_stay, split_stay);\n  check(stay_join, stay_join);\n  check(stay_split, stay_split);\n  check(stay_stay, stay_stay);\n\n  const auto join_join_join =\n      std::array{amr::Flag::Join, amr::Flag::Join, amr::Flag::Join};\n  const auto join_join_split =\n      std::array{amr::Flag::Join, amr::Flag::Join, amr::Flag::Split};\n  const auto join_join_stay =\n      std::array{amr::Flag::Join, amr::Flag::Join, amr::Flag::DoNothing};\n  const auto join_split_join =\n      std::array{amr::Flag::Join, amr::Flag::Split, amr::Flag::Join};\n  const auto join_split_split =\n      std::array{amr::Flag::Join, amr::Flag::Split, amr::Flag::Split};\n  const auto join_split_stay =\n      std::array{amr::Flag::Join, amr::Flag::Split, amr::Flag::DoNothing};\n  const auto join_stay_join =\n      std::array{amr::Flag::Join, amr::Flag::DoNothing, amr::Flag::Join};\n  const auto join_stay_split =\n      std::array{amr::Flag::Join, amr::Flag::DoNothing, amr::Flag::Split};\n  const auto join_stay_stay =\n      std::array{amr::Flag::Join, amr::Flag::DoNothing, amr::Flag::DoNothing};\n  const auto split_join_join =\n      std::array{amr::Flag::Split, amr::Flag::Join, amr::Flag::Join};\n  const auto split_join_split =\n      std::array{amr::Flag::Split, amr::Flag::Join, amr::Flag::Split};\n  const auto split_join_stay =\n      std::array{amr::Flag::Split, amr::Flag::Join, amr::Flag::DoNothing};\n  const auto split_split_join =\n      std::array{amr::Flag::Split, amr::Flag::Split, amr::Flag::Join};\n  const auto split_split_split =\n      std::array{amr::Flag::Split, amr::Flag::Split, amr::Flag::Split};\n  const auto split_split_stay =\n      std::array{amr::Flag::Split, amr::Flag::Split, amr::Flag::DoNothing};\n  const auto split_stay_join =\n      std::array{amr::Flag::Split, amr::Flag::DoNothing, amr::Flag::Join};\n  const auto split_stay_split =\n      std::array{amr::Flag::Split, amr::Flag::DoNothing, amr::Flag::Split};\n  const auto split_stay_stay =\n      std::array{amr::Flag::Split, amr::Flag::DoNothing, amr::Flag::DoNothing};\n  const auto stay_join_join =\n      std::array{amr::Flag::Join, amr::Flag::Join, amr::Flag::Join};\n  const auto stay_join_split =\n      std::array{amr::Flag::DoNothing, amr::Flag::Join, amr::Flag::Split};\n  const auto stay_join_stay =\n      std::array{amr::Flag::DoNothing, amr::Flag::Join, amr::Flag::DoNothing};\n  const auto stay_split_join =\n      std::array{amr::Flag::DoNothing, amr::Flag::Split, amr::Flag::Join};\n  const auto stay_split_split =\n      std::array{amr::Flag::DoNothing, amr::Flag::Split, amr::Flag::Split};\n  const auto stay_split_stay =\n      std::array{amr::Flag::DoNothing, amr::Flag::Split, amr::Flag::DoNothing};\n  const auto stay_stay_join =\n      std::array{amr::Flag::DoNothing, amr::Flag::DoNothing, amr::Flag::Join};\n  const auto stay_stay_split =\n      std::array{amr::Flag::DoNothing, amr::Flag::DoNothing, amr::Flag::Split};\n  const auto stay_stay_stay = std::array{\n      amr::Flag::DoNothing, amr::Flag::DoNothing, amr::Flag::DoNothing};\n  check(join_join_join, join_join_join);\n  check(join_join_split, stay_stay_split);\n  check(join_join_stay, join_join_stay);\n  check(join_split_join, stay_split_stay);\n  check(join_split_split, stay_split_split);\n  check(join_split_stay, stay_split_stay);\n  check(join_stay_join, join_stay_join);\n  check(join_stay_split, stay_stay_split);\n  check(join_stay_stay, join_stay_stay);\n  check(split_join_join, split_stay_stay);\n  check(split_join_split, split_stay_split);\n  check(split_join_stay, split_stay_stay);\n  check(split_split_join, split_split_stay);\n  check(split_split_split, split_split_split);\n  check(split_split_stay, split_split_stay);\n  check(split_stay_join, split_stay_stay);\n  check(split_stay_split, split_stay_split);\n  check(split_stay_stay, split_stay_stay);\n  check(stay_join_join, stay_join_join);\n  check(stay_join_split, stay_stay_split);\n  check(stay_join_stay, stay_join_stay);\n  check(stay_split_join, stay_split_stay);\n  check(stay_split_split, stay_split_split);\n  check(stay_split_stay, stay_split_stay);\n  check(stay_stay_join, stay_stay_join);\n  check(stay_stay_split, stay_stay_split);\n  check(stay_stay_stay, stay_stay_stay);\n}\n\nvoid test_assertions() {\n#ifdef SPECTRE_DEBUG\n  const ElementId<1> element_id_1d{0, {{SegmentId(2, 3)}}};\n  const ElementId<2> element_id_2d{0, {{SegmentId(3, 0), SegmentId{1, 1}}}};\n  const ElementId<3> element_id_3d{\n      7, {{SegmentId(5, 31), SegmentId(2, 0), SegmentId(4, 15)}}};\n  const std::array flags_1d_undefined{amr::Flag::Undefined};\n  const std::array flags_2d_no_join{amr::Flag::DoNothing, amr::Flag::Split};\n  const std::array flags_2d_no_split{amr::Flag::DoNothing,\n                                     amr::Flag::DoNothing};\n  const std::array flags_3d_split_join{amr::Flag::DoNothing, amr::Flag::Split,\n                                       amr::Flag::Join};\n  CHECK_THROWS_WITH(\n      amr::desired_refinement_levels(element_id_1d, flags_1d_undefined),\n      Catch::Matchers::ContainsSubstring(\"Undefined Flag in dimension\"));\n  CHECK_THROWS_WITH(\n      amr::desired_refinement_levels_of_neighbor(\n          element_id_1d, flags_1d_undefined,\n          OrientationMap<1>{{{Direction<1>::lower_xi()}}}),\n      Catch::Matchers::ContainsSubstring(\"Undefined Flag in dimension\"));\n  CHECK_THROWS_WITH(\n      amr::id_of_parent(element_id_2d, flags_2d_no_join),\n      Catch::Matchers::ContainsSubstring(\"is not joining given flags\"));\n  CHECK_THROWS_WITH(amr::id_of_parent(element_id_3d, flags_3d_split_join),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Splitting and joining an Element is not supported\"));\n  CHECK_THROWS_WITH(\n      amr::ids_of_children(element_id_2d, flags_2d_no_split),\n      Catch::Matchers::ContainsSubstring(\"has no children given flags\"));\n  CHECK_THROWS_WITH(amr::ids_of_children(element_id_3d, flags_3d_split_join),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Splitting and joining an Element is not supported\"));\n  CHECK_THROWS_WITH(\n      amr::ids_of_joining_neighbors(Element<2>{element_id_2d, {}},\n                                    flags_2d_no_join),\n      Catch::Matchers::ContainsSubstring(\"is not joining given flags\"));\n  CHECK_THROWS_WITH(amr::ids_of_joining_neighbors(Element<3>{element_id_3d, {}},\n                                                  flags_3d_split_join),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Splitting and joining an Element is not supported\"));\n  CHECK_THROWS_WITH(\n      amr::is_child_that_creates_parent(element_id_2d, flags_2d_no_join),\n      Catch::Matchers::ContainsSubstring(\"is not joining given flags\"));\n  CHECK_THROWS_WITH(\n      amr::is_child_that_creates_parent(element_id_3d, flags_3d_split_join),\n      Catch::Matchers::ContainsSubstring(\n          \"Splitting and joining an Element is not supported\"));\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Amr.Helpers\", \"[Domain][Unit]\") {\n  test_desired_refinement_levels();\n  test_desired_refinement_levels_of_neighbor();\n  test_fraction_of_block_volume();\n  test_has_potential_sibling();\n  test_id_of_parent();\n  test_ids_of_children();\n  test_ids_of_joining_neighbors();\n  test_is_child_that_creates_parent();\n  test_prevent_element_from_joining_while_splitting();\n  test_assertions();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/Test_Info.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Amr.Info\", \"[Domain][Unit]\") {\n  const amr::Info<1> info_0{std::array{amr::Flag::Join},\n                            Mesh<1>{3_st, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto}};\n  const amr::Info<1> info_1{std::array{amr::Flag::Split},\n                            Mesh<1>{3_st, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto}};\n  const amr::Info<1> info_2{std::array{amr::Flag::Split},\n                            Mesh<1>{4_st, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto}};\n  test_serialization(info_0);\n  CHECK(info_0 != info_1);\n  CHECK(info_1 != info_2);\n  CHECK(info_0 != info_2);\n  const std::string expected_output = MakeString{}\n                                      << \"Flags: \" << info_0.flags\n                                      << \" New mesh: \" << info_0.new_mesh;\n  CHECK(get_output(info_0) == expected_output);\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/Test_NeighborsOfChild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/NeighborsOfChild.hpp\"\n#include \"Domain/Amr/NewNeighborIds.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/Amr/NeighborFlagHelpers.hpp\"\n#include \"Helpers/Domain/Structure/NeighborHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstd::vector<ElementId<Dim>> element_ids_to_test() {\n  static constexpr size_t max_level = 2;\n  std::vector<ElementId<Dim>> result{};\n  for (size_t lx = 0; lx <= max_level; ++lx) {\n    for (size_t ix = 0; ix < two_to_the(lx); ++ix) {\n      if constexpr (Dim == 1) {\n        result.emplace_back(0, std::array{SegmentId{lx, ix}});\n      } else {\n        for (size_t ly = 0; ly <= max_level; ++ly) {\n          for (size_t iy = 0; iy < two_to_the(ly); ++iy) {\n            if constexpr (Dim == 2) {\n              result.emplace_back(\n                  0, std::array{SegmentId{lx, ix}, SegmentId{ly, iy}});\n            } else {\n              for (size_t lz = 0; lz <= max_level; ++lz) {\n                for (size_t iz = 0; iz < two_to_the(lz); ++iz) {\n                  result.emplace_back(\n                      0, std::array{SegmentId{lx, ix}, SegmentId{ly, iy},\n                                    SegmentId{lz, iz}});\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::vector<Element<Dim>> valid_elements(\n    const gsl::not_null<std::mt19937*> generator,\n    const ElementId<Dim>& element_id) {\n  const SegmentId xi_segment = element_id.segment_id(0);\n  const double lower_xi_endpoint = xi_segment.endpoint(Side::Lower);\n  const auto valid_lower_xi_neighbors = TestHelpers::domain::valid_neighbors(\n      generator, element_id, Direction<Dim>::lower_xi(),\n      lower_xi_endpoint == -1.0 ? TestHelpers::domain::FaceType::Block\n                                : TestHelpers::domain::FaceType::Internal);\n  const double upper_xi_endpoint = xi_segment.endpoint(Side::Upper);\n  const auto valid_upper_xi_neighbors = TestHelpers::domain::valid_neighbors(\n      generator, element_id, Direction<Dim>::upper_xi(),\n      upper_xi_endpoint == 1.0 ? TestHelpers::domain::FaceType::Block\n                               : TestHelpers::domain::FaceType::Internal);\n  std::vector<Element<Dim>> result{};\n  for (const auto& [lower_xi_neighbors, upper_xi_neighbors] :\n       cartesian_product(valid_lower_xi_neighbors, valid_upper_xi_neighbors)) {\n    typename Element<Dim>::Neighbors_t neighbors{};\n    neighbors.emplace(Direction<Dim>::lower_xi(), lower_xi_neighbors);\n    neighbors.emplace(Direction<Dim>::upper_xi(), upper_xi_neighbors);\n    result.emplace_back(element_id, std::move(neighbors));\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nMesh<Dim> make_lgl_mesh(const std::array<size_t, Dim>& extents) {\n  return {extents, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto};\n}\n\ntemplate <size_t Dim>\nstd::vector<amr::Info<Dim>> valid_parent_info();\n\ntemplate <>\nstd::vector<amr::Info<1>> valid_parent_info<1>() {\n  return std::vector{\n      amr::Info<1>{std::array{amr::Flag::Split}, make_lgl_mesh<1>({{3}})}};\n}\n\ntemplate <>\nstd::vector<amr::Info<2>> valid_parent_info<2>() {\n  return std::vector{\n      amr::Info<2>{std::array{amr::Flag::Split, amr::Flag::Split},\n                   make_lgl_mesh<2>({{3, 3}})},\n      amr::Info<2>{std::array{amr::Flag::IncreaseResolution, amr::Flag::Split},\n                   make_lgl_mesh<2>({{4, 3}})},\n      amr::Info<2>{std::array{amr::Flag::Split, amr::Flag::IncreaseResolution},\n                   make_lgl_mesh<2>({{3, 4}})}};\n}\n\ntemplate <>\nstd::vector<amr::Info<3>> valid_parent_info<3>() {\n  return std::vector{\n      amr::Info<3>{\n          std::array{amr::Flag::Split, amr::Flag::Split, amr::Flag::Split},\n          make_lgl_mesh<3>({{3, 3, 3}})},\n      amr::Info<3>{std::array{amr::Flag::IncreaseResolution, amr::Flag::Split,\n                              amr::Flag::Split},\n                   make_lgl_mesh<3>({{4, 3, 3}})},\n      amr::Info<3>{std::array{amr::Flag::Split, amr::Flag::IncreaseResolution,\n                              amr::Flag::Split},\n                   make_lgl_mesh<3>({{3, 4, 3}})},\n      amr::Info<3>{std::array{amr::Flag::Split, amr::Flag::Split,\n                              amr::Flag::IncreaseResolution},\n                   make_lgl_mesh<3>({{3, 3, 4}})},\n      amr::Info<3>{std::array{amr::Flag::Split, amr::Flag::IncreaseResolution,\n                              amr::Flag::IncreaseResolution},\n                   make_lgl_mesh<3>({{3, 4, 4}})},\n      amr::Info<3>{std::array{amr::Flag::IncreaseResolution, amr::Flag::Split,\n                              amr::Flag::IncreaseResolution},\n                   make_lgl_mesh<3>({{4, 3, 4}})},\n      amr::Info<3>{std::array{amr::Flag::IncreaseResolution,\n                              amr::Flag::IncreaseResolution, amr::Flag::Split},\n                   make_lgl_mesh<3>({{4, 4, 3}})}};\n}\n\ntemplate <size_t Dim>\nTestHelpers::amr::valid_info_t<Dim> valid_parent_neighbor_info(\n    const Element<Dim>& element,\n    const std::array<::amr::Flag, Dim>& element_flags) {\n  TestHelpers::amr::valid_info_t<Dim> result{};\n  const auto valid_lower_xi_neighbor_info =\n      TestHelpers::amr::valid_neighbor_info(\n          element.id(), element_flags,\n          element.neighbors().at(Direction<Dim>::lower_xi()));\n  const auto valid_upper_xi_neighbor_info =\n      TestHelpers::amr::valid_neighbor_info(\n          element.id(), element_flags,\n          element.neighbors().at(Direction<Dim>::upper_xi()));\n  for (const auto& lower_xi_neighbor_info : valid_lower_xi_neighbor_info) {\n    for (const auto& upper_xi_neighbor_info : valid_upper_xi_neighbor_info) {\n      auto joined_flags = lower_xi_neighbor_info;\n      for (const auto& flags : upper_xi_neighbor_info) {\n        joined_flags.emplace(flags);\n      }\n      result.emplace_back(joined_flags);\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> generator) {\n  CAPTURE(Dim);\n  for (const auto& parent_id :\n       random_sample(3, element_ids_to_test<Dim>(), generator)) {\n    CAPTURE(parent_id);\n    for (const auto& parent :\n         random_sample(3, valid_elements(generator, parent_id), generator)) {\n      CAPTURE(parent);\n      for (const auto& parent_info : valid_parent_info<Dim>()) {\n        CAPTURE(parent_info);\n        for (const auto& parent_neighbor_info : random_sample(\n                 3, valid_parent_neighbor_info(parent, parent_info.flags),\n                 generator)) {\n          CAPTURE(parent_neighbor_info);\n          for (const auto& child_id :\n               amr::ids_of_children(parent_id, parent_info.flags)) {\n            CAPTURE(child_id);\n            const auto [new_neighbors, new_neighbor_meshes] =\n                amr::neighbors_of_child(parent, parent_info,\n                                        parent_neighbor_info, child_id);\n            for (const auto& direction : std::vector{\n                     Direction<Dim>::lower_xi(), Direction<Dim>::upper_xi()}) {\n              CAPTURE(direction);\n              TestHelpers::domain::check_neighbors(new_neighbors.at(direction),\n                                                   child_id, direction);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 30]]\nSPECTRE_TEST_CASE(\"Unit.Domain.Amr.NeighborsOfChild\", \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test<1>(make_not_null(&generator));\n  test<2>(make_not_null(&generator));\n  test<3>(make_not_null(&generator));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/Test_NeighborsOfParent.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/NeighborsOfParent.hpp\"\n#include \"Domain/Amr/NewNeighborIds.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\nconst SegmentId s_00{0, 0};\nconst SegmentId s_10{1, 0};\nconst SegmentId s_11{1, 1};\n\nvoid test_periodic_interval() {\n  const OrientationMap<1> aligned = OrientationMap<1>::create_aligned();\n  const ElementId<1> parent_id{0, std::array{s_00}};\n  const ElementId<1> child_1_id{0, std::array{s_10}};\n  const ElementId<1> child_2_id{0, std::array{s_11}};\n  const Mesh<1> parent_mesh{4, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> child_1_mesh{3, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> child_2_mesh{4, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto};\n\n  DirectionMap<1, Neighbors<1>> child_1_neighbors{};\n  child_1_neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors<1>{std::unordered_set{child_2_id}, aligned});\n  child_1_neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors<1>{std::unordered_set{child_2_id}, aligned});\n  const Element<1> child_1{child_1_id, std::move(child_1_neighbors)};\n  const std::unordered_map<ElementId<1>, amr::Info<1>> child_1_neighbor_info{\n      {child_2_id, {{{amr::Flag::Join}}, parent_mesh}}};\n\n  DirectionMap<1, Neighbors<1>> child_2_neighbors{};\n  child_2_neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors<1>{std::unordered_set{child_1_id}, aligned});\n  child_2_neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors<1>{std::unordered_set{child_1_id}, aligned});\n  const Element<1> child_2{child_2_id, std::move(child_2_neighbors)};\n  const std::unordered_map<ElementId<1>, amr::Info<1>> child_2_neighbor_info{\n      {child_1_id, {{{amr::Flag::Join}}, parent_mesh}}};\n\n  std::vector<std::tuple<const Element<1>&,\n                         const std::unordered_map<ElementId<1>, amr::Info<1>>&>>\n      children_elements_and_neighbor_info;\n  children_elements_and_neighbor_info.emplace_back(\n      std::forward_as_tuple(child_1, child_1_neighbor_info));\n  children_elements_and_neighbor_info.emplace_back(\n      std::forward_as_tuple(child_2, child_2_neighbor_info));\n\n  const auto [parent_neighbors, parent_neighbors_mesh] =\n      amr::neighbors_of_parent(parent_id, children_elements_and_neighbor_info);\n  DirectionMap<1, Neighbors<1>> expected_parent_neighbors{};\n  expected_parent_neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors<1>{std::unordered_set{parent_id}, aligned});\n  expected_parent_neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors<1>{std::unordered_set{parent_id}, aligned});\n  CHECK(parent_neighbors == expected_parent_neighbors);\n  DirectionalIdMap<1, Mesh<1>> expected_parent_neighbor_meshes{};\n  expected_parent_neighbor_meshes.insert(\n      {{Direction<1>::lower_xi(), parent_id}, parent_mesh});\n  expected_parent_neighbor_meshes.insert(\n      {{Direction<1>::upper_xi(), parent_id}, parent_mesh});\n}\n\nvoid test_interval() {\n  const OrientationMap<1> aligned = OrientationMap<1>::create_aligned();\n  const OrientationMap<1> flipped{std::array{Direction<1>::lower_xi()}};\n  const ElementId<1> parent_id{0, std::array{s_00}};\n  const ElementId<1> child_1_id{0, std::array{s_10}};\n  const ElementId<1> child_2_id{0, std::array{s_11}};\n  const ElementId<1> lower_neighbor_id{1, std::array{s_11}};\n  const ElementId<1> upper_neighbor_id{2, std::array{s_00}};\n  const Mesh<1> parent_mesh{4, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> child_1_mesh{3, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> child_2_mesh{4, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> lower_neighbor_mesh{5, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> upper_neighbor_mesh{6, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto};\n\n  DirectionMap<1, Neighbors<1>> child_1_neighbors{};\n  child_1_neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors<1>{std::unordered_set{lower_neighbor_id}, aligned});\n  child_1_neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors<1>{std::unordered_set{child_2_id}, aligned});\n  const Element<1> child_1{child_1_id, std::move(child_1_neighbors)};\n  const std::unordered_map<ElementId<1>, amr::Info<1>> child_1_neighbor_info{\n      {lower_neighbor_id, {{{amr::Flag::DoNothing}}, lower_neighbor_mesh}},\n      {child_2_id, {{{amr::Flag::Join}}, parent_mesh}}};\n\n  DirectionMap<1, Neighbors<1>> child_2_neighbors{};\n  child_2_neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors<1>{std::unordered_set{child_1_id}, aligned});\n  child_2_neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors<1>{std::unordered_set{upper_neighbor_id}, flipped});\n  const Element<1> child_2{child_2_id, std::move(child_2_neighbors)};\n  const std::unordered_map<ElementId<1>, amr::Info<1>> child_2_neighbor_info{\n      {child_1_id, {{{amr::Flag::Join}}, parent_mesh}},\n      {upper_neighbor_id, {{{amr::Flag::Split}}, upper_neighbor_mesh}}};\n\n  std::vector<std::tuple<const Element<1>&,\n                         const std::unordered_map<ElementId<1>, amr::Info<1>>&>>\n      children_elements_and_neighbor_info;\n  children_elements_and_neighbor_info.emplace_back(\n      std::forward_as_tuple(child_1, child_1_neighbor_info));\n  children_elements_and_neighbor_info.emplace_back(\n      std::forward_as_tuple(child_2, child_2_neighbor_info));\n\n  const auto [parent_neighbors, parent_neighbors_mesh] =\n      amr::neighbors_of_parent(parent_id, children_elements_and_neighbor_info);\n  DirectionMap<1, Neighbors<1>> expected_parent_neighbors{};\n  expected_parent_neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors<1>{std::unordered_set{lower_neighbor_id}, aligned});\n  const ElementId<1> split_upper_neighbor_id{2, std::array{s_11}};\n  expected_parent_neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors<1>{std::unordered_set{split_upper_neighbor_id}, flipped});\n  CHECK(parent_neighbors == expected_parent_neighbors);\n  DirectionalIdMap<1, Mesh<1>> expected_parent_neighbor_meshes{};\n  expected_parent_neighbor_meshes.insert(\n      {{Direction<1>::lower_xi(), parent_id}, lower_neighbor_mesh});\n  expected_parent_neighbor_meshes.insert(\n      {{Direction<1>::upper_xi(), parent_id}, upper_neighbor_mesh});\n}\n\nvoid test_rectangle() {\n  const OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n  const OrientationMap<2> rotated{\n      std::array{Direction<2>::lower_eta(), Direction<2>::upper_xi()}};\n  const Mesh<2> mesh;\n  const ElementId<2> parent_id{0, std::array{s_00, s_00}};\n  const ElementId<2> child_1_id{0, std::array{s_10, s_10}};\n  const ElementId<2> child_2_id{0, std::array{s_11, s_10}};\n  const ElementId<2> child_3_id{0, std::array{s_10, s_11}};\n  const ElementId<2> child_4_id{0, std::array{s_11, s_11}};\n  const ElementId<2> neighbor_1_id{1, std::array{s_11, s_00}};\n  const ElementId<2> neighbor_2_id{2, std::array{s_10, s_00}};\n  const ElementId<2> neighbor_3_id{2, std::array{s_11, s_11}};\n\n  const std::array join_join{amr::Flag::Join, amr::Flag::Join};\n  const std::array neighbor_1_flags{amr::Flag::Join, amr::Flag::DoNothing};\n  const std::array neighbor_2_flags{amr::Flag::DoNothing, amr::Flag::Split};\n  const std::array neighbor_3_flags{amr::Flag::DoNothing, amr::Flag::Join};\n\n  const ElementId<2> neighbor_4_id{1, std::array{s_00, s_00}};\n  const ElementId<2> neighbor_5_id{2, std::array{s_10, s_11}};\n  const ElementId<2> neighbor_6_id{2, std::array{s_11, s_00}};\n\n  DirectionMap<2, Neighbors<2>> child_1_neighbors{};\n  child_1_neighbors.emplace(\n      Direction<2>::lower_xi(),\n      Neighbors<2>{std::unordered_set{neighbor_1_id}, aligned});\n  child_1_neighbors.emplace(\n      Direction<2>::upper_xi(),\n      Neighbors<2>{std::unordered_set{child_2_id}, aligned});\n  child_1_neighbors.emplace(\n      Direction<2>::lower_eta(),\n      Neighbors<2>{std::unordered_set{child_3_id}, aligned});\n  child_1_neighbors.emplace(\n      Direction<2>::upper_eta(),\n      Neighbors<2>{std::unordered_set{child_3_id}, aligned});\n  const Element<2> child_1{child_1_id, std::move(child_1_neighbors)};\n  const std::unordered_map<ElementId<2>, amr::Info<2>> child_1_neighbor_info{\n      {neighbor_1_id, amr::Info<2>{neighbor_1_flags, mesh}},\n      {child_2_id, amr::Info<2>{join_join, mesh}},\n      {child_3_id, amr::Info<2>{join_join, mesh}}};\n\n  DirectionMap<2, Neighbors<2>> child_2_neighbors{};\n  child_2_neighbors.emplace(\n      Direction<2>::lower_xi(),\n      Neighbors<2>{std::unordered_set{child_1_id}, aligned});\n  child_2_neighbors.emplace(\n      Direction<2>::upper_xi(),\n      Neighbors<2>{std::unordered_set{neighbor_2_id}, rotated});\n  child_2_neighbors.emplace(\n      Direction<2>::lower_eta(),\n      Neighbors<2>{std::unordered_set{child_4_id}, aligned});\n  child_2_neighbors.emplace(\n      Direction<2>::upper_eta(),\n      Neighbors<2>{std::unordered_set{child_4_id}, aligned});\n  const Element<2> child_2{child_2_id, std::move(child_2_neighbors)};\n  const std::unordered_map<ElementId<2>, amr::Info<2>> child_2_neighbor_info{\n      {child_1_id, amr::Info<2>{join_join, mesh}},\n      {child_4_id, amr::Info<2>{join_join, mesh}},\n      {neighbor_2_id, amr::Info<2>{neighbor_2_flags, mesh}}};\n\n  DirectionMap<2, Neighbors<2>> child_3_neighbors{};\n  child_3_neighbors.emplace(\n      Direction<2>::lower_xi(),\n      Neighbors<2>{std::unordered_set{neighbor_1_id}, aligned});\n  child_3_neighbors.emplace(\n      Direction<2>::upper_xi(),\n      Neighbors<2>{std::unordered_set{child_4_id}, aligned});\n  child_3_neighbors.emplace(\n      Direction<2>::lower_eta(),\n      Neighbors<2>{std::unordered_set{child_1_id}, aligned});\n  child_3_neighbors.emplace(\n      Direction<2>::upper_eta(),\n      Neighbors<2>{std::unordered_set{child_1_id}, aligned});\n  const Element<2> child_3{child_3_id, std::move(child_3_neighbors)};\n  const std::unordered_map<ElementId<2>, amr::Info<2>> child_3_neighbor_info{\n      {neighbor_1_id, amr::Info<2>{neighbor_1_flags, mesh}},\n      {child_1_id, amr::Info<2>{join_join, mesh}},\n      {child_4_id, amr::Info<2>{join_join, mesh}}};\n\n  DirectionMap<2, Neighbors<2>> child_4_neighbors{};\n  child_4_neighbors.emplace(\n      Direction<2>::lower_xi(),\n      Neighbors<2>{std::unordered_set{child_3_id}, aligned});\n  child_4_neighbors.emplace(\n      Direction<2>::upper_xi(),\n      Neighbors<2>{std::unordered_set{neighbor_3_id}, rotated});\n  child_4_neighbors.emplace(\n      Direction<2>::lower_eta(),\n      Neighbors<2>{std::unordered_set{child_2_id}, aligned});\n  child_4_neighbors.emplace(\n      Direction<2>::upper_eta(),\n      Neighbors<2>{std::unordered_set{child_2_id}, aligned});\n  const Element<2> child_4{child_4_id, std::move(child_4_neighbors)};\n  const std::unordered_map<ElementId<2>, amr::Info<2>> child_4_neighbor_info{\n      {child_2_id, amr::Info<2>{join_join, mesh}},\n      {child_3_id, amr::Info<2>{join_join, mesh}},\n      {neighbor_3_id, amr::Info<2>{neighbor_3_flags, mesh}}};\n\n  std::vector<std::tuple<const Element<2>&,\n                         const std::unordered_map<ElementId<2>, amr::Info<2>>&>>\n      children_elements_and_neighbor_info;\n  children_elements_and_neighbor_info.emplace_back(\n      std::forward_as_tuple(child_1, child_1_neighbor_info));\n  children_elements_and_neighbor_info.emplace_back(\n      std::forward_as_tuple(child_2, child_2_neighbor_info));\n  children_elements_and_neighbor_info.emplace_back(\n      std::forward_as_tuple(child_3, child_3_neighbor_info));\n  children_elements_and_neighbor_info.emplace_back(\n      std::forward_as_tuple(child_4, child_4_neighbor_info));\n\n  const auto [parent_neighbors, parent_neighbors_mesh] =\n      amr::neighbors_of_parent(parent_id, children_elements_and_neighbor_info);\n  DirectionMap<2, Neighbors<2>> expected_parent_neighbors{};\n  expected_parent_neighbors.emplace(\n      Direction<2>::lower_xi(),\n      Neighbors<2>{std::unordered_set{neighbor_4_id}, aligned});\n  expected_parent_neighbors.emplace(\n      Direction<2>::upper_xi(),\n      Neighbors<2>{std::unordered_set{neighbor_5_id, neighbor_6_id}, rotated});\n  expected_parent_neighbors.emplace(\n      Direction<2>::lower_eta(),\n      Neighbors<2>{std::unordered_set{parent_id}, aligned});\n  expected_parent_neighbors.emplace(\n      Direction<2>::upper_eta(),\n      Neighbors<2>{std::unordered_set{parent_id}, aligned});\n  CHECK(parent_neighbors == expected_parent_neighbors);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Amr.NeighborsOfParent\", \"[Domain][Unit]\") {\n  test_periodic_interval();\n  test_interval();\n  test_rectangle();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/Test_NewNeighborIds.cpp",
    "content": "// // Distributed under the MIT License.\n// // See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/NewNeighborIds.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/Amr/NeighborFlagHelpers.hpp\"\n#include \"Helpers/Domain/Structure/NeighborHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstd::vector<ElementId<Dim>> element_ids_to_test() {\n  static constexpr size_t max_level = 5 - Dim;\n  std::vector<ElementId<Dim>> result{};\n  for (size_t lx = 0; lx <= max_level; ++lx) {\n    for (size_t ix = 0; ix < two_to_the(lx); ++ix) {\n      if constexpr (Dim == 1) {\n        result.emplace_back(0, std::array{SegmentId{lx, ix}});\n      } else {\n        for (size_t ly = 0; ly <= max_level; ++ly) {\n          for (size_t iy = 0; iy < two_to_the(ly); ++iy) {\n            if constexpr (Dim == 2) {\n              result.emplace_back(\n                  0, std::array{SegmentId{lx, ix}, SegmentId{ly, iy}});\n            } else {\n              for (size_t lz = 0; lz <= max_level; ++lz) {\n                for (size_t iz = 0; iz < two_to_the(lz); ++iz) {\n                  result.emplace_back(\n                      0, std::array{SegmentId{lx, ix}, SegmentId{ly, iy},\n                                    SegmentId{lz, iz}});\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> generator) {\n  std::array<size_t, Dim> extents{};\n  alg::iota(extents, 2_st);\n  const Mesh<Dim> expected_mesh{extents, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n\n  for (const auto& element_id : element_ids_to_test<Dim>()) {\n    CAPTURE(element_id);\n    const ElementId<Dim> new_element_id{element_id.block_id(),\n                                        element_id.segment_ids(),\n                                        element_id.grid_index() + 1};\n    for (const auto& direction :\n         random_sample(2, Direction<Dim>::all_directions(), generator)) {\n      CAPTURE(direction);\n      const size_t dim = direction.dimension();\n      const Side side = direction.side();\n      const SegmentId& normal_segment = element_id.segment_id(dim);\n      const double endpoint = normal_segment.endpoint(side);\n      if (endpoint == 1.0 or endpoint == -1.0) {\n        for (const auto face_type :\n             std::array{TestHelpers::domain::FaceType::Periodic,\n                        TestHelpers::domain::FaceType::Block}) {\n          for (const auto& neighbors :\n               random_sample(5,\n                             TestHelpers::domain::valid_neighbors(\n                                 generator, element_id, direction, face_type),\n                             generator)) {\n            CAPTURE(neighbors);\n            for (const auto& neighbor_info : random_sample(\n                     5,\n                     TestHelpers::amr::valid_neighbor_info(\n                         element_id,\n                         make_array<Dim>(::amr::Flag::IncreaseResolution),\n                         neighbors),\n                     generator)) {\n              CAPTURE(neighbor_info);\n              const auto neighbor_ids_and_meshes = new_neighbor_ids(\n                  new_element_id, direction, neighbors, neighbor_info);\n              std::unordered_set<ElementId<Dim>> new_neighbor_ids;\n              for (const auto& [id, mesh] : neighbor_ids_and_meshes) {\n                new_neighbor_ids.insert(id);\n                CHECK(neighbors.orientation(id)(mesh) == expected_mesh);\n              }\n              const auto new_neighbors = Neighbors<Dim>{\n                  std::move(new_neighbor_ids), neighbors.orientations(),\n                  neighbors.are_conforming()};\n              CAPTURE(new_neighbors);\n              TestHelpers::domain::check_neighbors(new_neighbors,\n                                                   new_element_id, direction);\n            }\n          }\n        }\n      } else {\n        for (const auto& neighbors :\n             random_sample(5,\n                           TestHelpers::domain::valid_neighbors(\n                               generator, element_id, direction),\n                           generator)) {\n          CAPTURE(neighbors);\n          for (const auto& neighbor_info : random_sample(\n                   5,\n                   TestHelpers::amr::valid_neighbor_info(\n                       element_id,\n                       make_array<Dim>(::amr::Flag::IncreaseResolution),\n                       neighbors),\n                   generator)) {\n            CAPTURE(neighbor_info);\n            const auto neighbor_ids_and_meshes = new_neighbor_ids(\n                new_element_id, direction, neighbors, neighbor_info);\n            std::unordered_set<ElementId<Dim>> new_neighbor_ids;\n            for (const auto& [id, mesh] : neighbor_ids_and_meshes) {\n              new_neighbor_ids.insert(id);\n              CHECK(neighbors.orientation(id)(mesh) == expected_mesh);\n            }\n            const auto new_neighbors =\n                Neighbors<Dim>{new_neighbor_ids, neighbors.orientations(),\n                               neighbors.are_conforming()};\n            CAPTURE(new_neighbors);\n            TestHelpers::domain::check_neighbors(new_neighbors, new_element_id,\n                                                 direction);\n          }\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Domain.Amr.NewNeighborIds\", \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test<1>(make_not_null(&generator));\n  test<2>(make_not_null(&generator));\n  test<3>(make_not_null(&generator));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  TestHelpers::db::test_simple_tag<amr::Tags::Info<Dim>>(\"Info\");\n  TestHelpers::db::test_simple_tag<amr::Tags::NeighborInfo<Dim>>(\n      \"NeighborInfo\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Amr.Tags\", \"[Domain][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Amr/Test_UpdateAmrDecision.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <map>\n#include <sstream>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/UpdateAmrDecision.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\n\ntemplate <size_t VolumeDim>\nvoid check_amr_decision_is_unchanged(\n    std::array<amr::Flag, VolumeDim> my_initial_amr_flags,\n    const Element<VolumeDim>& element, const ElementId<VolumeDim>& neighbor_id,\n    const std::array<amr::Flag, VolumeDim>& neighbor_amr_flags,\n    const bool enforce_two_to_one_balance_in_normal_direction) {\n  const auto expected_updated_flags = my_initial_amr_flags;\n  std::stringstream os;\n  os << neighbor_amr_flags;\n  INFO(os.str());\n  CHECK_FALSE(amr::update_amr_decision(\n      make_not_null(&my_initial_amr_flags), element, neighbor_id,\n      neighbor_amr_flags, enforce_two_to_one_balance_in_normal_direction));\n  CHECK(expected_updated_flags == my_initial_amr_flags);\n}\n\ntemplate <size_t VolumeDim>\nvoid check_amr_decision_is_changed(\n    std::array<amr::Flag, VolumeDim> my_initial_amr_flags,\n    const Element<VolumeDim>& element, const ElementId<VolumeDim>& neighbor_id,\n    const std::array<amr::Flag, VolumeDim>& neighbor_amr_flags,\n    const std::array<amr::Flag, VolumeDim>& expected_updated_flags,\n    const bool enforce_two_to_one_balance_in_normal_direction) {\n  std::stringstream os;\n  os << my_initial_amr_flags << \" \" << neighbor_amr_flags;\n  INFO(os.str());\n  CHECK(amr::update_amr_decision(\n      make_not_null(&my_initial_amr_flags), element, neighbor_id,\n      neighbor_amr_flags, enforce_two_to_one_balance_in_normal_direction));\n  CHECK(expected_updated_flags == my_initial_amr_flags);\n}\n\ntemplate <size_t VolumeDim>\nusing changed_flags_t = std::map<std::pair<std::array<amr::Flag, VolumeDim>,\n                                           std::array<amr::Flag, VolumeDim>>,\n                                 std::array<amr::Flag, VolumeDim>>;\ntemplate <size_t VolumeDim>\nvoid check_update_amr_decision(\n    const Element<VolumeDim>& element, const ElementId<VolumeDim>& neighbor_id,\n    const std::vector<std::array<amr::Flag, VolumeDim>>& all_flags,\n    const changed_flags_t<VolumeDim>& changed_flags,\n    const bool enforce_two_to_one_balance_in_normal_direction) {\n  for (const auto& my_flags : all_flags) {\n    for (const auto& neighbor_flags : all_flags) {\n      auto search =\n          changed_flags.find(std::make_pair(my_flags, neighbor_flags));\n      if (search == changed_flags.end()) {\n        check_amr_decision_is_unchanged(\n            my_flags, element, neighbor_id, neighbor_flags,\n            enforce_two_to_one_balance_in_normal_direction);\n      } else {\n        check_amr_decision_is_changed(\n            my_flags, element, neighbor_id, neighbor_flags, search->second,\n            enforce_two_to_one_balance_in_normal_direction);\n      }\n    }\n  }\n}\n\nElement<1> make_element(\n    const ElementId<1>& element_id,\n    const std::unordered_set<ElementId<1>>& lower_xi_neighbor_ids,\n    const std::unordered_set<ElementId<1>>& upper_xi_neighbor_ids) {\n  DirectionMap<1, Neighbors<1>> neighbors;\n  if (not lower_xi_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<1>::lower_xi(),\n                      Neighbors<1>{{lower_xi_neighbor_ids},\n                                   OrientationMap<1>::create_aligned()});\n  }\n  if (not upper_xi_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<1>::upper_xi(),\n                      Neighbors<1>{{upper_xi_neighbor_ids},\n                                   OrientationMap<1>::create_aligned()});\n  }\n  return Element<1>{element_id, std::move(neighbors)};\n}\n\nElement<2> make_element(\n    const ElementId<2>& element_id,\n    const std::unordered_set<ElementId<2>>& lower_xi_neighbor_ids,\n    const std::unordered_set<ElementId<2>>& upper_xi_neighbor_ids,\n    const std::unordered_set<ElementId<2>>& lower_eta_neighbor_ids,\n    const std::unordered_set<ElementId<2>>& upper_eta_neighbor_ids) {\n  DirectionMap<2, Neighbors<2>> neighbors;\n  if (not lower_xi_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<2>::lower_xi(),\n                      Neighbors<2>{{lower_xi_neighbor_ids},\n                                   OrientationMap<2>::create_aligned()});\n  }\n  if (not upper_xi_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<2>::upper_xi(),\n                      Neighbors<2>{{upper_xi_neighbor_ids},\n                                   OrientationMap<2>::create_aligned()});\n  }\n  if (not lower_eta_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<2>::lower_eta(),\n                      Neighbors<2>{{lower_eta_neighbor_ids},\n                                   OrientationMap<2>::create_aligned()});\n  }\n  if (not upper_eta_neighbor_ids.empty()) {\n    neighbors.emplace(Direction<2>::upper_eta(),\n                      Neighbors<2>{{upper_eta_neighbor_ids},\n                                   OrientationMap<2>::create_aligned()});\n  }\n  return Element<2>{element_id, std::move(neighbors)};\n}\n\ntemplate <bool EnforceTwoToOneInNormalDirection>\nvoid test_update_amr_decision_1d() {\n  INFO(EnforceTwoToOneInNormalDirection);\n  const std::array<amr::Flag, 1> split{{amr::Flag::Split}};\n  const std::array<amr::Flag, 1> join{{amr::Flag::Join}};\n  const std::array<amr::Flag, 1> stay{{amr::Flag::DoNothing}};\n  const std::vector<std::array<amr::Flag, 1>> all_flags{split, join, stay};\n\n  const SegmentId x_segment{3, 5};\n  const SegmentId x_cousin{3, 6};\n\n  const ElementId<1> element_id{0, {{x_segment}}};\n  const ElementId<1> id_lx_s{0, {{x_segment.id_of_sibling()}}};\n  const ElementId<1> id_ux_c{0, {{x_cousin}}};\n\n  auto element = make_element(element_id, {id_lx_s}, {id_ux_c});\n  // changed flags: first flags of pair is initial_amr_flags\n  //                second flags of pair is neighbor_amr_flags\n  //                last flags are the new amr_flags for the element\n  {\n    INFO(\"lower neighbor is sibling\");\n    check_update_amr_decision(\n        element, id_lx_s, all_flags,\n        changed_flags_t<1>{{{join, stay}, stay}, {{join, split}, stay}},\n        EnforceTwoToOneInNormalDirection);\n  }\n  {\n    INFO(\"upper neighbor is cousin\");\n    check_update_amr_decision(element, id_ux_c, all_flags,\n                              EnforceTwoToOneInNormalDirection\n                                  ? changed_flags_t<1>{{{join, split}, stay}}\n                                  : changed_flags_t<1>{},\n                              EnforceTwoToOneInNormalDirection);\n  }\n\n  const ElementId<1> id_lx_n{0, {{x_segment.id_of_abutting_nibling()}}};\n  const ElementId<1> id_ux_cp{0, {{x_cousin.id_of_parent()}}};\n  element = make_element(element_id, {id_lx_n}, {id_ux_cp});\n\n  {\n    INFO(\"lower neighbor is nibling\");\n    check_update_amr_decision(element, id_lx_n, all_flags,\n                              EnforceTwoToOneInNormalDirection\n                                  ? changed_flags_t<1>{{{join, join}, stay},\n                                                       {{join, stay}, stay},\n                                                       {{join, split}, split},\n                                                       {{stay, split}, split}}\n                                  : changed_flags_t<1>{{{join, join}, stay},\n                                                       {{join, stay}, stay},\n                                                       {{join, split}, stay}},\n                              EnforceTwoToOneInNormalDirection);\n  }\n  {\n    INFO(\"upper neighbor is parent of cousin\");\n    check_update_amr_decision(element, id_ux_cp, all_flags,\n                              changed_flags_t<1>{},\n                              EnforceTwoToOneInNormalDirection);\n  }\n  // note having no lower neighbor is okay for this test\n  const ElementId<1> id_ux_cc{0, {{x_cousin.id_of_child(Side::Lower)}}};\n  element = make_element(element_id, {}, {id_ux_cc});\n  {\n    INFO(\"upper neighbor is child of cousin\");\n    check_update_amr_decision(element, id_ux_cc, all_flags,\n                              EnforceTwoToOneInNormalDirection\n                                  ? changed_flags_t<1>{{{join, stay}, stay},\n                                                       {{join, split}, split},\n                                                       {{stay, split}, split}}\n                                  : changed_flags_t<1>{},\n                              EnforceTwoToOneInNormalDirection);\n  }\n}\n\ntemplate <bool EnforceTwoToOneInNormalDirection>\nvoid test_update_amr_decision_2d() {\n  const std::array<amr::Flag, 2> split_split{\n      {amr::Flag::Split, amr::Flag::Split}};\n  const std::array<amr::Flag, 2> stay_split{\n      {amr::Flag::DoNothing, amr::Flag::Split}};\n  const std::array<amr::Flag, 2> split_stay{\n      {amr::Flag::Split, amr::Flag::DoNothing}};\n  const std::array<amr::Flag, 2> join_stay{\n      {amr::Flag::Join, amr::Flag::DoNothing}};\n  const std::array<amr::Flag, 2> stay_stay{\n      {amr::Flag::DoNothing, amr::Flag::DoNothing}};\n  const std::array<amr::Flag, 2> join_join{{amr::Flag::Join, amr::Flag::Join}};\n  const std::array<amr::Flag, 2> stay_join{\n      {amr::Flag::DoNothing, amr::Flag::Join}};\n\n  const std::vector<std::array<amr::Flag, 2>> all_flags{\n      split_split, stay_split, split_stay, join_stay,\n      stay_stay,   join_join,  stay_join};\n\n  const SegmentId x_segment{3, 5};\n  const SegmentId x_cousin{3, 6};\n  const SegmentId y_segment{6, 15};\n  const SegmentId y_cousin{6, 16};\n\n  const ElementId<2> element_id{0, {{x_segment, y_segment}}};\n\n  const ElementId<2> id_lx_s_s{0, {{x_segment.id_of_sibling(), y_segment}}};\n  const ElementId<2> id_ux_c_s{0, {{x_cousin, y_segment}}};\n  const ElementId<2> id_ly_s_n{\n      0, {{x_segment, y_segment.id_of_abutting_nibling()}}};\n  const ElementId<2> id_uy_s_cp{0, {{x_segment, y_cousin.id_of_parent()}}};\n  auto element = make_element(element_id, {id_lx_s_s}, {id_ux_c_s}, {id_ly_s_n},\n                              {id_uy_s_cp});\n\n  // changed flags: first flags of pair are initial_amr_flags\n  //                second flags of pair are neighbor_amr_flags\n  //                last flags are the new amr_flags for the element\n\n  // neighbor same refinement in x and y, sibling side in x\n  check_update_amr_decision<2>(\n      element, id_lx_s_s, all_flags,\n      changed_flags_t<2>{{{join_stay, split_split}, stay_stay},\n                         {{join_stay, stay_split}, stay_stay},\n                         {{join_stay, split_stay}, stay_stay},\n                         {{join_stay, stay_stay}, stay_stay},\n                         {{join_stay, join_join}, stay_stay},\n                         {{join_stay, stay_join}, stay_stay},\n                         {{join_join, split_split}, stay_stay},\n                         {{join_join, stay_split}, stay_stay},\n                         {{join_join, split_stay}, stay_join},\n                         {{join_join, join_stay}, stay_join},\n                         {{join_join, stay_stay}, stay_join},\n                         {{join_join, stay_join}, stay_join},\n                         {{stay_join, split_split}, stay_stay},\n                         {{stay_join, stay_split}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor same refinement in x and y, non-sibling side in x\n  check_update_amr_decision<2>(\n      element, id_ux_c_s, all_flags,\n      changed_flags_t<2>{{{join_stay, split_split}, stay_stay},\n                         {{join_stay, split_stay}, stay_stay},\n                         {{join_join, split_split}, stay_stay},\n                         {{join_join, stay_split}, join_stay},\n                         {{join_join, split_stay}, stay_join},\n                         {{stay_join, split_split}, stay_stay},\n                         {{stay_join, stay_split}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor same in x, coarser in y, sibling side of y\n  check_update_amr_decision<2>(\n      element, id_ly_s_n, all_flags,\n      changed_flags_t<2>{{{split_stay, split_split}, split_split},\n                         {{split_stay, stay_split}, split_split},\n                         {{join_stay, split_split}, stay_split},\n                         {{join_stay, stay_split}, stay_split},\n                         {{join_stay, split_stay}, stay_stay},\n                         {{stay_stay, split_split}, stay_split},\n                         {{stay_stay, stay_split}, stay_split},\n                         {{join_join, split_split}, stay_split},\n                         {{join_join, stay_split}, stay_split},\n                         {{join_join, split_stay}, stay_stay},\n                         {{join_join, join_stay}, join_stay},\n                         {{join_join, stay_stay}, join_stay},\n                         {{join_join, join_join}, join_stay},\n                         {{join_join, stay_join}, join_stay},\n                         {{stay_join, split_split}, stay_split},\n                         {{stay_join, stay_split}, stay_split},\n                         {{stay_join, split_stay}, stay_stay},\n                         {{stay_join, join_stay}, stay_stay},\n                         {{stay_join, stay_stay}, stay_stay},\n                         {{stay_join, join_join}, stay_stay},\n                         {{stay_join, stay_join}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor same in x, coarser in y, non-sibling side of y\n  check_update_amr_decision<2>(\n      element, id_uy_s_cp, all_flags,\n      changed_flags_t<2>{{{join_stay, split_split}, stay_stay},\n                         {{join_stay, split_stay}, stay_stay},\n                         {{join_join, split_split}, stay_join},\n                         {{join_join, split_stay}, stay_join}},\n      EnforceTwoToOneInNormalDirection);\n\n  const ElementId<2> id_lx_s_p{\n      0, {{x_segment.id_of_sibling(), y_segment.id_of_parent()}}};\n  const ElementId<2> id_ly_p_n{\n      0, {{x_segment.id_of_parent(), y_segment.id_of_abutting_nibling()}}};\n  const ElementId<2> id_ux_c_p{0, {{x_cousin, y_segment.id_of_parent()}}};\n  const ElementId<2> id_uy_p_cp{\n      0, {{x_segment.id_of_parent(), y_cousin.id_of_parent()}}};\n  element = make_element(element_id, {id_lx_s_p}, {id_ux_c_p}, {id_ly_p_n},\n                         {id_uy_p_cp});\n  // neighbor same in x, coarser in y, sibling side of x\n  check_update_amr_decision<2>(\n      element, id_lx_s_p, all_flags,\n      changed_flags_t<2>{{{join_stay, split_split}, stay_stay},\n                         {{join_stay, stay_split}, stay_stay},\n                         {{join_stay, split_stay}, stay_stay},\n                         {{join_stay, stay_stay}, stay_stay},\n                         {{join_stay, join_stay}, stay_stay},\n                         {{join_stay, join_join}, stay_stay},\n                         {{join_stay, stay_join}, stay_stay},\n                         {{join_join, split_split}, stay_join},\n                         {{join_join, stay_split}, stay_join},\n                         {{join_join, split_stay}, stay_join},\n                         {{join_join, stay_stay}, stay_join},\n                         {{join_join, join_join}, stay_join},\n                         {{join_join, stay_join}, stay_join}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor same in x, coarser in y, non-sibling side of x\n  check_update_amr_decision<2>(\n      element, id_ux_c_p, all_flags,\n      changed_flags_t<2>{{{join_stay, split_split}, stay_stay},\n                         {{join_stay, split_stay}, stay_stay},\n                         {{join_join, split_split}, stay_join},\n                         {{join_join, split_stay}, stay_join}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor coarser in x, finer in y, sibling side of y\n  check_update_amr_decision<2>(\n      element, id_ly_p_n, all_flags,\n      changed_flags_t<2>{{{split_stay, split_split}, split_split},\n                         {{split_stay, stay_split}, split_split},\n                         {{join_stay, split_split}, stay_split},\n                         {{join_stay, stay_split}, stay_split},\n                         {{stay_stay, split_split}, stay_split},\n                         {{stay_stay, stay_split}, stay_split},\n                         {{join_join, split_split}, stay_split},\n                         {{join_join, stay_split}, stay_split},\n                         {{join_join, split_stay}, join_stay},\n                         {{join_join, join_stay}, join_stay},\n                         {{join_join, stay_stay}, join_stay},\n                         {{join_join, join_join}, join_stay},\n                         {{join_join, stay_join}, join_stay},\n                         {{stay_join, split_split}, stay_split},\n                         {{stay_join, stay_split}, stay_split},\n                         {{stay_join, split_stay}, stay_stay},\n                         {{stay_join, join_stay}, stay_stay},\n                         {{stay_join, stay_stay}, stay_stay},\n                         {{stay_join, join_join}, stay_stay},\n                         {{stay_join, stay_join}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor coarser in x and y, non-sibling side -f y\n  check_update_amr_decision<2>(element, id_uy_p_cp, all_flags,\n                               changed_flags_t<2>{},\n                               EnforceTwoToOneInNormalDirection);\n\n  const ElementId<2> id_lx_s_cl{\n      0, {{x_segment.id_of_sibling(), y_segment.id_of_child(Side::Lower)}}};\n  const ElementId<2> id_lx_s_cu{\n      0, {{x_segment.id_of_sibling(), y_segment.id_of_child(Side::Upper)}}};\n  const ElementId<2> id_ly_cl_n{0,\n                                {{x_segment.id_of_child(Side::Lower),\n                                  y_segment.id_of_abutting_nibling()}}};\n  const ElementId<2> id_ly_cu_n{0,\n                                {{x_segment.id_of_child(Side::Upper),\n                                  y_segment.id_of_abutting_nibling()}}};\n  const ElementId<2> id_ux_c_cl{\n      0, {{x_cousin, y_segment.id_of_child(Side::Lower)}}};\n  const ElementId<2> id_ux_c_cu{\n      0, {{x_cousin, y_segment.id_of_child(Side::Upper)}}};\n  const ElementId<2> id_uy_cl_cp{\n      0, {{x_segment.id_of_child(Side::Lower), y_cousin.id_of_parent()}}};\n  const ElementId<2> id_uy_cu_cp{\n      0, {{x_segment.id_of_child(Side::Upper), y_cousin.id_of_parent()}}};\n  element = make_element(element_id, {id_lx_s_cl, id_lx_s_cu},\n                         {id_ux_c_cl, id_ux_c_cu}, {id_ly_cl_n, id_ly_cu_n},\n                         {id_uy_cl_cp, id_uy_cu_cp});\n  // neighbor same in x, finer in y, sibling side of x\n  check_update_amr_decision<2>(\n      element, id_lx_s_cl, all_flags,\n      changed_flags_t<2>{{{split_stay, split_split}, split_split},\n                         {{split_stay, stay_split}, split_split},\n                         {{join_stay, split_split}, stay_split},\n                         {{join_stay, stay_split}, stay_split},\n                         {{join_stay, split_stay}, stay_stay},\n                         {{join_stay, join_stay}, stay_stay},\n                         {{join_stay, stay_stay}, stay_stay},\n                         {{join_stay, stay_join}, stay_stay},\n                         {{stay_stay, split_split}, stay_split},\n                         {{stay_stay, stay_split}, stay_split},\n                         {{join_join, split_split}, stay_split},\n                         {{join_join, stay_split}, stay_split},\n                         {{join_join, split_stay}, stay_stay},\n                         {{join_join, join_stay}, stay_stay},\n                         {{join_join, stay_stay}, stay_stay},\n                         {{join_join, join_join}, stay_join},\n                         {{join_join, stay_join}, stay_join},\n                         {{stay_join, split_split}, stay_split},\n                         {{stay_join, stay_split}, stay_split},\n                         {{stay_join, split_stay}, stay_stay},\n                         {{stay_join, join_stay}, stay_stay},\n                         {{stay_join, stay_stay}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor same in x, finer in y, non-sibling side of x\n  check_update_amr_decision<2>(\n      element, id_ux_c_cu, all_flags,\n      changed_flags_t<2>{{{split_stay, split_split}, split_split},\n                         {{split_stay, stay_split}, split_split},\n                         {{join_stay, split_split}, stay_split},\n                         {{join_stay, stay_split}, stay_split},\n                         {{join_stay, split_stay}, stay_stay},\n                         {{stay_stay, split_split}, stay_split},\n                         {{stay_stay, stay_split}, stay_split},\n                         {{join_join, split_split}, stay_split},\n                         {{join_join, stay_split}, stay_split},\n                         {{join_join, split_stay}, stay_stay},\n                         {{join_join, join_stay}, join_stay},\n                         {{join_join, stay_stay}, join_stay},\n                         {{stay_join, split_split}, stay_split},\n                         {{stay_join, stay_split}, stay_split},\n                         {{stay_join, split_stay}, stay_stay},\n                         {{stay_join, join_stay}, stay_stay},\n                         {{stay_join, stay_stay}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor finer in x and y, sibling side of y\n  check_update_amr_decision<2>(\n      element, id_ly_cl_n, all_flags,\n      changed_flags_t<2>{{{stay_split, split_split}, split_split},\n                         {{stay_split, split_stay}, split_split},\n                         {{split_stay, split_split}, split_split},\n                         {{split_stay, stay_split}, split_split},\n                         {{join_stay, split_split}, split_split},\n                         {{join_stay, stay_split}, stay_split},\n                         {{join_stay, split_stay}, split_stay},\n                         {{join_stay, stay_stay}, stay_stay},\n                         {{join_stay, stay_join}, stay_stay},\n                         {{stay_stay, split_split}, split_split},\n                         {{stay_stay, stay_split}, stay_split},\n                         {{stay_stay, split_stay}, split_stay},\n                         {{join_join, split_split}, split_split},\n                         {{join_join, stay_split}, stay_split},\n                         {{join_join, split_stay}, split_stay},\n                         {{join_join, join_stay}, join_stay},\n                         {{join_join, stay_stay}, stay_stay},\n                         {{join_join, join_join}, join_stay},\n                         {{join_join, stay_join}, stay_stay},\n                         {{stay_join, split_split}, split_split},\n                         {{stay_join, stay_split}, stay_split},\n                         {{stay_join, split_stay}, split_stay},\n                         {{stay_join, join_stay}, stay_stay},\n                         {{stay_join, stay_stay}, stay_stay},\n                         {{stay_join, join_join}, stay_stay},\n                         {{stay_join, stay_join}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor finer in x, coarser in y, non-sibling side of y\n  check_update_amr_decision<2>(\n      element, id_uy_cu_cp, all_flags,\n      changed_flags_t<2>{{{stay_split, split_split}, split_split},\n                         {{stay_split, split_stay}, split_split},\n                         {{join_stay, split_split}, split_stay},\n                         {{join_stay, stay_split}, stay_stay},\n                         {{join_stay, split_stay}, split_stay},\n                         {{join_stay, stay_stay}, stay_stay},\n                         {{join_stay, stay_join}, stay_stay},\n                         {{stay_stay, split_split}, split_stay},\n                         {{stay_stay, split_stay}, split_stay},\n                         {{join_join, split_split}, split_stay},\n                         {{join_join, stay_split}, stay_join},\n                         {{join_join, split_stay}, split_stay},\n                         {{join_join, stay_stay}, stay_join},\n                         {{join_join, stay_join}, stay_join},\n                         {{stay_join, split_split}, split_stay},\n                         {{stay_join, split_stay}, split_stay}},\n      EnforceTwoToOneInNormalDirection);\n\n  const ElementId<2> id_ux_cc_s{\n      0, {{x_cousin.id_of_child(Side::Lower), y_segment}}};\n  const ElementId<2> id_uy_cl_cc{0,\n                                 {{x_segment.id_of_child(Side::Lower),\n                                   y_cousin.id_of_child(Side::Lower)}}};\n  const ElementId<2> id_uy_cu_cc{0,\n                                 {{x_segment.id_of_child(Side::Upper),\n                                   y_cousin.id_of_child(Side::Lower)}}};\n\n  element = make_element(element_id, {}, {id_ux_cc_s}, {},\n                         {id_uy_cl_cc, id_uy_cu_cc});\n  // neighbor finer in x, same in y, non-sibling side of x\n  check_update_amr_decision<2>(\n      element, id_ux_cc_s, all_flags,\n      changed_flags_t<2>{{{stay_split, split_split}, split_split},\n                         {{stay_split, split_stay}, split_split},\n                         {{join_stay, split_split}, split_stay},\n                         {{join_stay, stay_split}, stay_stay},\n                         {{join_stay, split_stay}, split_stay},\n                         {{join_stay, stay_stay}, stay_stay},\n                         {{join_stay, stay_join}, stay_stay},\n                         {{stay_stay, split_split}, split_stay},\n                         {{stay_stay, split_stay}, split_stay},\n                         {{join_join, split_split}, split_stay},\n                         {{join_join, stay_split}, stay_stay},\n                         {{join_join, split_stay}, split_stay},\n                         {{join_join, stay_stay}, stay_join},\n                         {{join_join, stay_join}, stay_join},\n                         {{stay_join, split_split}, split_stay},\n                         {{stay_join, stay_split}, stay_stay},\n                         {{stay_join, split_stay}, split_stay}},\n      EnforceTwoToOneInNormalDirection);\n  // neighbor finer in x and y, non-sibling side of y\n  check_update_amr_decision<2>(\n      element, id_uy_cu_cc, all_flags,\n      changed_flags_t<2>{{{stay_split, split_split}, split_split},\n                         {{stay_split, split_stay}, split_split},\n                         {{split_stay, split_split}, split_split},\n                         {{split_stay, stay_split}, split_split},\n                         {{join_stay, split_split}, split_split},\n                         {{join_stay, stay_split}, stay_split},\n                         {{join_stay, split_stay}, split_stay},\n                         {{join_stay, stay_stay}, stay_stay},\n                         {{join_stay, stay_join}, stay_stay},\n                         {{stay_stay, split_split}, split_split},\n                         {{stay_stay, stay_split}, stay_split},\n                         {{stay_stay, split_stay}, split_stay},\n                         {{join_join, split_split}, split_split},\n                         {{join_join, stay_split}, stay_split},\n                         {{join_join, split_stay}, split_stay},\n                         {{join_join, join_stay}, join_stay},\n                         {{join_join, stay_stay}, stay_stay},\n                         {{join_join, stay_join}, stay_join},\n                         {{stay_join, split_split}, split_split},\n                         {{stay_join, stay_split}, stay_split},\n                         {{stay_join, split_stay}, split_stay},\n                         {{stay_join, join_stay}, stay_stay},\n                         {{stay_join, stay_stay}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n\n  const ElementId<2> id_uy_p_cc{\n      0, {{x_segment.id_of_parent(), y_cousin.id_of_child(Side::Lower)}}};\n  element = make_element(element_id, {}, {}, {}, {id_uy_p_cc});\n  // neighbor coarser in x, finer in y, non-sibling side of y\n  check_update_amr_decision<2>(\n      element, id_uy_p_cc, all_flags,\n      changed_flags_t<2>{{{split_stay, split_split}, split_split},\n                         {{split_stay, stay_split}, split_split},\n                         {{join_stay, split_split}, stay_split},\n                         {{join_stay, stay_split}, stay_split},\n                         {{stay_stay, split_split}, stay_split},\n                         {{stay_stay, stay_split}, stay_split},\n                         {{join_join, split_split}, stay_split},\n                         {{join_join, stay_split}, stay_split},\n                         {{join_join, split_stay}, join_stay},\n                         {{join_join, join_stay}, join_stay},\n                         {{join_join, stay_stay}, join_stay},\n                         {{stay_join, split_split}, stay_split},\n                         {{stay_join, stay_split}, stay_split},\n                         {{stay_join, split_stay}, stay_stay},\n                         {{stay_join, join_stay}, stay_stay},\n                         {{stay_join, stay_stay}, stay_stay}},\n      EnforceTwoToOneInNormalDirection);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Amr.UpdateAmrDecision\", \"[Domain][Unit]\") {\n  test_update_amr_decision_1d<true>();\n  test_update_amr_decision_1d<false>();\n  test_update_amr_decision_2d<true>();\n  // 3d is not tested for every case as the algorithm is independent of\n  // dimensions once there is a transverse direction for a neighbor and there\n  // are 729 AMR flag combinations for 45 different types of neighbors.\n}\n"
  },
  {
    "path": "tests/Unit/Domain/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DomainBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_BoundaryCondition.cpp\n  Test_GenericBcs.cpp\n  Test_GetBoundaryConditionsBase.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DomainBoundaryConditions\n  DomainBoundaryConditionsHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Domain/BoundaryConditions/Test_BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nnamespace helpers = TestHelpers::domain::BoundaryConditions;\n\ntemplate <size_t Dim>\nvoid test() {\n  const Direction<Dim> expected_direction = Direction<Dim>::upper_xi();\n  const std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      bc_default = std::make_unique<helpers::TestBoundaryCondition<Dim>>(\n          expected_direction);\n  const auto bc_default_deserialized = serialize_and_deserialize(bc_default);\n\n  const std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> bc =\n      std::make_unique<helpers::TestBoundaryCondition<Dim>>(expected_direction,\n                                                            10);\n  const auto bc_deserialized = serialize_and_deserialize(bc);\n\n  const auto perform_checks =\n      [&expected_direction](\n          const std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>&\n              boundary_condition,\n          const size_t expected_block_id) {\n        const auto& derived_boundary_condition =\n            dynamic_cast<const helpers::TestBoundaryCondition<Dim>&>(\n                *boundary_condition);\n        CHECK(derived_boundary_condition.direction() == expected_direction);\n        CHECK(derived_boundary_condition.block_id() == expected_block_id);\n        CHECK(derived_boundary_condition ==\n              helpers::TestBoundaryCondition<Dim>{expected_direction,\n                                                  expected_block_id});\n        CHECK(derived_boundary_condition !=\n              helpers::TestBoundaryCondition<Dim>{expected_direction.opposite(),\n                                                  expected_block_id});\n        CHECK(derived_boundary_condition !=\n              helpers::TestBoundaryCondition<Dim>{expected_direction,\n                                                  expected_block_id + 1});\n      };\n  perform_checks(bc_default, 0);\n  perform_checks(bc_default_deserialized, 0);\n  perform_checks(bc, 10);\n  perform_checks(bc_deserialized, 10);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.BoundaryConditions.BoundaryCondition\",\n                  \"[Unit][Domain]\") {\n  // This test tests both the base class and the test helpers together since\n  // they need to be compatible\n  helpers::register_derived_with_charm();\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/BoundaryConditions/Test_GenericBcs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nnamespace helpers = TestHelpers::domain::BoundaryConditions;\n\nSPECTRE_TEST_CASE(\"Unit.Domain.BoundaryConditions.GenericBcs\",\n                  \"[Unit][Domain]\") {\n  helpers::register_derived_with_charm();\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> periodic =\n      std::make_unique<helpers::TestPeriodicBoundaryCondition<1>>();\n  CHECK(is_periodic(periodic));\n  CHECK(is_periodic(serialize_and_deserialize(periodic)));\n  CHECK_FALSE(is_none(periodic));\n  CHECK_FALSE(is_none(serialize_and_deserialize(periodic)));\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> not_periodic =\n      std::make_unique<helpers::TestBoundaryCondition<1>>();\n  CHECK_FALSE(is_periodic(not_periodic));\n  CHECK_FALSE(is_periodic(serialize_and_deserialize(not_periodic)));\n  CHECK_FALSE(is_none(not_periodic));\n  CHECK_FALSE(is_none(serialize_and_deserialize(not_periodic)));\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> none =\n      std::make_unique<helpers::TestNoneBoundaryCondition<1>>();\n  CHECK(is_none(none));\n  CHECK(is_none(serialize_and_deserialize(none)));\n  CHECK_FALSE(is_periodic(none));\n  CHECK_FALSE(is_periodic(serialize_and_deserialize(none)));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/BoundaryConditions/Test_GetBoundaryConditionsBase.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <type_traits>\n\n#include \"Domain/BoundaryConditions/GetBoundaryConditionsBase.hpp\"\n\nnamespace domain::BoundaryConditions {\nnamespace {\nstruct SystemNoBoundaryConditionsBase {};\n\nstruct BoundaryConditionsBase {};\nstruct SystemWithBoundaryConditionsBase {\n  using boundary_conditions_base = BoundaryConditionsBase;\n};\n\nstatic_assert(\n    not has_boundary_conditions_base_v<SystemNoBoundaryConditionsBase>);\nstatic_assert(has_boundary_conditions_base_v<SystemWithBoundaryConditionsBase>);\n\nstatic_assert(std::is_same_v<\n              get_boundary_conditions_base<SystemWithBoundaryConditionsBase>,\n              BoundaryConditionsBase>);\nstatic_assert(\n    std::is_same_v<get_boundary_conditions_base<SystemNoBoundaryConditionsBase>,\n                   detail::TheSystemHasNoBoundaryConditionsBaseTypeAlias>);\n}  // namespace\n}  // namespace domain::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Domain/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(FunctionsOfTime)\nadd_subdirectory(Python)\nadd_subdirectory(Tags)\n\nset(LIBRARY \"Test_Domain\")\n\nset(LIBRARY_SOURCES\n  Test_AreaElement.cpp\n  Test_Block.cpp\n  Test_BlockAndElementLogicalCoordinates.cpp\n  Test_CoordinatesTag.cpp\n  Test_CoordsToDifferentFrame.cpp\n  Test_CreateInitialElement.cpp\n  Test_Domain.cpp\n  Test_DomainHelpers.cpp\n  Test_DomainTestHelpers.cpp\n  Test_ElementDistribution.cpp\n  Test_ElementMap.cpp\n  Test_ElementToBlockLogicalMap.cpp\n  Test_ElementSearchTree.cpp\n  Test_ExcisionSphere.cpp\n  Test_FaceNormal.cpp\n  Test_FlatLogicalMetric.cpp\n  Test_InterfaceHelpers.cpp\n  Test_InterfaceItems.cpp\n  Test_InterfaceLogicalCoordinates.cpp\n  Test_JacobianDiagnostic.cpp\n  Test_MinimumGridSpacing.cpp\n  Test_RadiallyCompressedCoordinates.cpp\n  Test_SizeOfElement.cpp\n  Test_StrahlkorperTransformations.cpp\n  Test_Tags.cpp\n  Test_TagsCharacteristicSpeeds.cpp\n  Test_TagsTimeDependent.cpp\n)\n\nadd_subdirectory(Amr)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(CoordinateMaps)\nadd_subdirectory(Creators)\nadd_subdirectory(Structure)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  Domain\n  DomainBoundaryConditionsHelpers\n  DomainCreators\n  DomainHelpers\n  GeneralRelativity\n  GeneralRelativitySolutions\n  H5\n  LinearOperators\n  SphericalHarmonics\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_CoordinateMaps\")\n\nset(LIBRARY_SOURCES\n  Test_Affine.cpp\n  Test_BulgedCube.cpp\n  Test_Composition.cpp\n  Test_CoordinateMap.cpp\n  Test_CylindricalEndcap.cpp\n  Test_CylindricalFlatEndcap.cpp\n  Test_CylindricalFlatSide.cpp\n  Test_CylindricalSide.cpp\n  Test_DiscreteRotation.cpp\n  Test_Distribution.cpp\n  Test_EquatorialCompression.cpp\n  Test_Equiangular.cpp\n  Test_FlatOffsetSphericalWedge.cpp\n  Test_FlatOffsetWedge.cpp\n  Test_Frustum.cpp\n  Test_Identity.cpp\n  Test_Interval.cpp\n  Test_KerrHorizonConforming.cpp\n  Test_PolarToCartesian.cpp\n  Test_ProductMaps.cpp\n  Test_Rotation.cpp\n  Test_SpecialMobius.cpp\n  Test_SphericalToCartesianPfaffian.cpp\n  Test_Tags.cpp\n  Test_TimeDependentHelpers.cpp\n  Test_UniformCylindricalEndcap.cpp\n  Test_UniformCylindricalFlatEndcap.cpp\n  Test_UniformCylindricalSide.cpp\n  Test_Wedge2D.cpp\n  Test_Wedge3D.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  DataStructuresHelpers\n  Domain\n  DomainStructure\n  ErrorHandling\n  FunctionsOfTime\n  GeneralRelativity\n  RootFinding\n  Spectral\n  Utilities\n)\n\nadd_subdirectory(Python)\nadd_subdirectory(TimeDependent)\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.CoordinateMaps.Python\"\n  Test_Composition.py\n  \"Unit;Domain\"\n  PyCoordinateMaps)\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Python/Test_Composition.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures.Tensor import DataVector, Frame, tnsr\nfrom spectre.Domain import ElementId, ElementMap\nfrom spectre.Domain.CoordinateMaps import (\n    CompositionMapElementLogicalBlockLogicalInertial3D,\n)\nfrom spectre.Domain.Creators import Sphere\n\n\nclass TestComposition(unittest.TestCase):\n    def test_composition(self):\n        domain = Sphere(\n            inner_radius=1.0,\n            outer_radius=3.0,\n            initial_refinement=0,\n            initial_number_of_grid_points=5,\n            use_equiangular_map=True,\n            excise=True,\n        ).create_domain()\n        element_id = ElementId[3](0)\n        element_map = ElementMap(element_id, domain)\n        self.assertIsInstance(\n            element_map, CompositionMapElementLogicalBlockLogicalInertial3D\n        )\n        # ElementLogical -> Inertial\n        npt.assert_almost_equal(\n            element_map(\n                tnsr.I[DataVector, 3, Frame.ElementLogical](\n                    np.array([[0.0, 0.0, -1.0]]).T\n                )\n            ),\n            np.array([[0.0, 0.0, 1.0]]).T,\n        )\n        # ElementLogical -> BlockLogical\n        npt.assert_almost_equal(\n            element_map.element_logical_to_block_logical(\n                tnsr.I[DataVector, 3, Frame.ElementLogical](\n                    np.array([[0.0, 0.0, -1.0]]).T\n                )\n            ),\n            np.array([[0.0, 0.0, -1.0]]).T,\n        )\n        # BlockLogical -> Inertial\n        npt.assert_almost_equal(\n            element_map.block_logical_to_inertial(\n                tnsr.I[DataVector, 3, Frame.BlockLogical](\n                    np.array([[0.0, 0.0, -1.0]]).T\n                )\n            ),\n            np.array([[0.0, 0.0, 1.0]]).T,\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Affine.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n\nnamespace domain {\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Affine\", \"[Domain][Unit]\") {\n  const double xA = -1.0;\n  const double xB = 1.0;\n  const double xa = -2.0;\n  const double xb = 2.0;\n\n  CoordinateMaps::Affine affine_map(xA, xB, xa, xb);\n\n  const double xi = 0.5 * (xA + xB);\n  const double x = xb * (xi - xA) / (xB - xA) + xa * (xB - xi) / (xB - xA);\n\n  const std::array<double, 1> point_A{{xA}};\n  const std::array<double, 1> point_B{{xB}};\n  const std::array<double, 1> point_a{{xa}};\n  const std::array<double, 1> point_b{{xb}};\n  const std::array<double, 1> point_xi{{xi}};\n  const std::array<double, 1> point_x{{x}};\n\n  CHECK(affine_map(point_A) == point_a);\n  CHECK(affine_map(point_B) == point_b);\n  CHECK(affine_map(point_xi) == point_x);\n\n  CHECK(affine_map.inverse(point_a).value() == point_A);\n  CHECK(affine_map.inverse(point_b).value() == point_B);\n  CHECK(affine_map.inverse(point_x).value() == point_xi);\n\n  const double inv_jacobian_00 = (xB - xA) / (xb - xa);\n\n  CHECK((get<0, 0>(affine_map.inv_jacobian(point_A))) == inv_jacobian_00);\n  CHECK((get<0, 0>(affine_map.inv_jacobian(point_B))) == inv_jacobian_00);\n  CHECK((get<0, 0>(affine_map.inv_jacobian(point_xi))) == inv_jacobian_00);\n\n  const double jacobian_00 = (xb - xa) / (xB - xA);\n  CHECK((get<0, 0>(affine_map.jacobian(point_A))) == jacobian_00);\n  CHECK((get<0, 0>(affine_map.jacobian(point_B))) == jacobian_00);\n  CHECK((get<0, 0>(affine_map.jacobian(point_xi))) == jacobian_00);\n\n  // Check inequivalence operator\n  CHECK_FALSE(affine_map != affine_map);\n  test_serialization(affine_map);\n\n  test_coordinate_map_argument_types(affine_map, point_xi);\n\n  check_if_map_is_identity(CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0});\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      (CoordinateMaps::Affine{-1.0, -1.0, -2.0, 2.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"The left and right boundaries for both source and target \"\n          \"interval must differ\"));\n  CHECK_THROWS_WITH(\n      (CoordinateMaps::Affine{-1.0, 1.0, 2.0, 2.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"The left and right boundaries for both source and target \"\n          \"interval must differ\"));\n#endif\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_BulgedCube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/BulgedCube.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_bulged_cube_fail() {\n  INFO(\"Bulged cube fail\");\n  const CoordinateMaps::BulgedCube map(2.0 * sqrt(3.0), 0.5, false);\n\n  // The corners of the mapped cube have coordinates +/- 2 in each\n  // dimension.  If we inverse-map points that are sufficiently\n  // outside of the mapped cube, then the inverse map will fail.\n  const std::array<double, 3> test_mapped_point1{{3.0, 3.0, 3.0}};\n  const std::array<double, 3> test_mapped_point2{{2.0, 2.0, 2.01}};\n  const std::array<double, 3> test_mapped_point3{{4.0, 0.0, 0.0}};\n\n  // These points are outside the mapped BulgedCube. So inverse should either\n  // return the correct inverse (which happens to be computable for\n  // these points) or it should return nullopt.\n  const std::array<double, 3> test_mapped_point4{{2.0, 2.0, 2.0}};\n  const std::array<double, 3> test_mapped_point5{{3.0, 0.0, 0.0}};\n\n  CHECK_FALSE(map.inverse(test_mapped_point1).has_value());\n  CHECK_FALSE(map.inverse(test_mapped_point2).has_value());\n  CHECK_FALSE(map.inverse(test_mapped_point3).has_value());\n  if (map.inverse(test_mapped_point4).has_value()) {\n    CHECK_ITERABLE_APPROX(map(map.inverse(test_mapped_point4).value()),\n                          test_mapped_point4);\n  }\n  if (map.inverse(test_mapped_point5).has_value()) {\n    CHECK_ITERABLE_APPROX(map(map.inverse(test_mapped_point5).value()),\n                          test_mapped_point5);\n  }\n}\n\nvoid test_bulged_cube(bool with_equiangular_map) {\n  INFO(\"Bulged cube\");\n  const std::array<double, 3> lower_corner{{-1.0, -1.0, -1.0}};\n  const std::array<double, 3> upper_corner{{1.0, 1.0, 1.0}};\n  const CoordinateMaps::BulgedCube map(2.0 * sqrt(3.0), 0.5,\n                                       with_equiangular_map);\n\n  CHECK(map(lower_corner)[0] == approx(-2.0));\n  CHECK(map(lower_corner)[1] == approx(-2.0));\n  CHECK(map(lower_corner)[2] == approx(-2.0));\n  CHECK(map(upper_corner)[0] == approx(2.0));\n  CHECK(map(upper_corner)[1] == approx(2.0));\n  CHECK(map(upper_corner)[2] == approx(2.0));\n\n  const std::array<double, 3> test_point1{{-1.0, 0.25, 0.0}};\n  const std::array<double, 3> test_point2{{1.0, 1.0, -0.5}};\n  const std::array<double, 3> test_point3{{0.7, -0.2, 0.4}};\n  const std::array<double, 3> test_point4{{0.0, 0.0, 0.0}};\n  const std::array<DataVector, 3> test_points{\n      {DataVector{-1.0, 1.0, 0.7, 0.0}, DataVector{0.25, 1.0, -0.2, 0.0},\n       DataVector{0.0, -0.5, 0.4, 0.0}}};\n  const DataVector& collocation_pts =\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(7);\n  const std::array<DataVector, 3> test_points2{\n      {collocation_pts, collocation_pts, collocation_pts}};\n\n  test_jacobian(map, test_point1);\n  test_jacobian(map, test_point2);\n  test_jacobian(map, test_point3);\n  test_jacobian(map, test_point4);\n\n  test_inv_jacobian(map, test_point1);\n  test_inv_jacobian(map, test_point2);\n  test_inv_jacobian(map, test_point3);\n  test_inv_jacobian(map, test_point4);\n\n  test_coordinate_map_implementation<CoordinateMaps::BulgedCube>(map);\n\n  CHECK(serialize_and_deserialize(map) == map);\n  CHECK_FALSE(serialize_and_deserialize(map) != map);\n\n  test_coordinate_map_argument_types(map, test_point1);\n\n  test_inverse_map(map, test_point1);\n  test_inverse_map(map, test_point2);\n  test_inverse_map(map, test_point3);\n  test_inverse_map(map, test_point4);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.BulgedCube\", \"[Domain][Unit]\") {\n  const CoordinateMaps::BulgedCube map(sqrt(3.0), 0, false);\n  const std::array<double, 3> lower_corner{{-1.0, -1.0, -1.0}};\n  const std::array<double, 3> upper_corner{{1.0, 1.0, 1.0}};\n  const std::array<double, 3> test_point1{{-1.0, 0.25, 0.0}};\n  const std::array<double, 3> test_point2{{1.0, 1.0, -0.5}};\n  const std::array<double, 3> test_point3{{0.7, -0.2, 0.4}};\n\n  CHECK_ITERABLE_APPROX(map(lower_corner), lower_corner);\n  CHECK_ITERABLE_APPROX(map(upper_corner), upper_corner);\n  CHECK_ITERABLE_APPROX(map(test_point1), test_point1);\n  CHECK_ITERABLE_APPROX(map(test_point2), test_point2);\n  CHECK_ITERABLE_APPROX(map(test_point3), test_point3);\n\n  test_jacobian(map, test_point1);\n  test_jacobian(map, test_point2);\n  test_jacobian(map, test_point3);\n\n  test_inv_jacobian(map, test_point1);\n  test_inv_jacobian(map, test_point2);\n  test_inv_jacobian(map, test_point3);\n\n  test_coordinate_map_implementation<CoordinateMaps::BulgedCube>(map);\n\n  CHECK(serialize_and_deserialize(map) == map);\n  CHECK_FALSE(serialize_and_deserialize(map) != map);\n\n  test_coordinate_map_argument_types(map, test_point1);\n  test_bulged_cube(true);   // Equiangular\n  test_bulged_cube(false);  // Equidistant\n  test_bulged_cube_fail();\n\n  check_if_map_is_identity(CoordinateMaps::BulgedCube{sqrt(3.0), 0, false});\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Composition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nnamespace {\n#ifdef SPECTRE_AUTODIFF\n// This test helper function computes the inverse Hessian by\n// auto-differentiating through the inv_jacobian function, which gives the\n// derivatives of inv_jacobian with respect to the source coordinates, i.e.\n// \\frac{\\partial}{\\partial\\xi^k}\\left(\\frac{\\partial\\xi^i}{\\partial\n// x^j}\\right). The inverse Hessian is then given by the chain rule\n// \\frac{\\partial^2\\xi^i}{\\partial x^j \\partial x^k} =\n// \\frac{\\partial\\xi^l}{\\partial x^k} *\n// \\frac{\\partial}{\\partial\\xi^l}\\left(\\frac{\\partial\\xi^i}{\\partial\n// x^j}\\right).\ntemplate <typename DataType, typename Map, size_t Dim>\nInverseHessian<DataType, Dim, Frame::ElementLogical, Frame::Inertial>\ninv_hessian_helper(\n    const Map& coordinate_map,\n    const tnsr::I<DataType, Dim, Frame::ElementLogical>& source_point) {\n  using SecondOrderDualNum = autodiff::HigherOrderDual<2, double>;\n\n  const size_t num_pts = get<0>(source_point).size();\n  ::InverseHessian<DataType, Dim, Frame::ElementLogical, Frame::Inertial>\n      inverse_hessian{num_pts};\n\n  for (size_t pts_index = 0; pts_index < num_pts; ++pts_index) {\n    tnsr::I<SecondOrderDualNum, Dim, Frame::ElementLogical> dual_source_coords;\n\n    if constexpr (std::is_same_v<DataType, double>) {\n      [&]<std::size_t... Ins>(std::index_sequence<Ins...>) {\n        ((get<Ins>(dual_source_coords) = get<Ins>(source_point)), ...);\n      }\n      (std::make_index_sequence<Dim>{});\n    } else {\n      [&]<std::size_t... Ins>(std::index_sequence<Ins...>) {\n        ((get<Ins>(dual_source_coords) =\n              gsl::at(get<Ins>(source_point), pts_index)),\n         ...);\n      }\n      (std::make_index_sequence<Dim>{});\n    }\n\n    for (size_t k = 0; k < Dim; ++k) {\n      for (size_t i = 0; i < Dim; ++i) {\n        for (size_t j = i; j < Dim; ++j) {\n          double inv_hessian_kij{0.0};\n\n          for (size_t l = 0; l < Dim; ++l) {\n            autodiff::seed<1>(dual_source_coords.get(l), 1.0);\n            for (size_t n = 1; n < Dim; ++n) {\n              autodiff::seed<1>(dual_source_coords.get((l + n) % Dim), 0.0);\n            }\n\n            const auto dual_inverse_jac =\n                coordinate_map.inv_jacobian(dual_source_coords);\n            const auto inv_jac_lj = autodiff::val(dual_inverse_jac.get(l, j));\n            const auto deriv_kli =\n                autodiff::derivative<1>(dual_inverse_jac.get(k, i));\n            inv_hessian_kij += inv_jac_lj * deriv_kli;\n          }\n\n          if constexpr (std::is_same_v<DataType, double>) {\n            inverse_hessian.get(k, i, j) = inv_hessian_kij;\n          } else {\n            inverse_hessian.get(k, i, j)[pts_index] = inv_hessian_kij;\n          }\n        }\n      }\n    }\n  }\n  return inverse_hessian;\n}\n#endif  // SPECTRE_AUTODIFF\n\nvoid test_composition() {\n  INFO(\"Composition\");\n\n  using Affine2D = ProductOf2Maps<Affine, Affine>;\n  register_classes_with_charm<\n      CoordinateMap<Frame::ElementLogical, Frame::BlockLogical, Affine2D>,\n      CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine2D>>();\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> logical_dist{-1., 1.};\n\n  // Domain is [0, 1] x [1, 3], and first dim is split in half, so element is\n  // [0, 0.5] x [1, 3]\n  const ElementId<2> element_id{0, {{{1, 0}, {0, 0}}}};\n  // Testing template deduction here\n  const Composition map{\n      element_to_block_logical_map(element_id),\n      std::make_unique<\n          CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine2D>>(\n          Affine2D{{-1., 1., 0., 1.}, {-1., 1., 1., 3.}})};\n\n  {\n    INFO(\"Semantics\");\n    test_serialization(map);\n    test_copy_semantics(map);\n    auto move_map = map;\n    test_move_semantics(std::move(move_map), map);\n  }\n\n  {\n    INFO(\"Properties\");\n    const auto& maps = map.maps();\n    CHECK(*get<0>(maps) == *element_to_block_logical_map(element_id));\n    CHECK((map.get_component<Frame::ElementLogical, Frame::BlockLogical>() ==\n           *get<0>(maps)));\n  }\n\n  CHECK_FALSE(map.is_identity());\n  CHECK_FALSE(map.inv_jacobian_is_time_dependent());\n  CHECK_FALSE(map.jacobian_is_time_dependent());\n  CHECK(map.function_of_time_names().empty());\n\n  const auto xi =\n      make_with_random_values<tnsr::I<DataVector, 2, Frame::ElementLogical>>(\n          make_not_null(&generator), make_not_null(&logical_dist),\n          DataVector(5));\n  const auto x = map(xi);\n  const auto jacobian = map.jacobian(xi);\n  const auto inv_jacobian = map.inv_jacobian(xi);\n#ifdef SPECTRE_AUTODIFF\n  const auto inv_hessian = map.inv_hessian(xi, inv_jacobian);\n#endif  // SPECTRE_AUTODIFF\n  CHECK_ITERABLE_APPROX(get<0>(x), (get<0>(xi) + 1.) * 0.25);\n  CHECK_ITERABLE_APPROX(get<1>(x), (get<1>(xi) + 2.));\n  CHECK_ITERABLE_APPROX((get<0, 0>(jacobian)), DataVector(5, 0.25));\n  CHECK_ITERABLE_APPROX((get<0, 0>(inv_jacobian)), DataVector(5, 4.));\n  CHECK_ITERABLE_APPROX((get<1, 1>(jacobian)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<1, 1>(inv_jacobian)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<1, 0>(jacobian)), DataVector(5, 0.));\n  CHECK_ITERABLE_APPROX((get<1, 0>(inv_jacobian)), DataVector(5, 0.));\n#ifdef SPECTRE_AUTODIFF\n  for (const auto& component : inv_hessian) {\n    CHECK_ITERABLE_APPROX(component, DataVector(5, 0.0));\n  }\n#endif  // SPECTRE_AUTODIFF\n  const auto x_target = tnsr::I<double, 2, Frame::Inertial>{{{0.5, 1.}}};\n  const auto inv = map.inverse(x_target);\n  REQUIRE(inv.has_value());\n  CHECK(get<0>(*inv) == approx(1.));\n  CHECK(get<1>(*inv) == approx(-1.));\n}\n\nvoid test_identity() {\n  INFO(\"Identity\");\n\n  using Affine3D = ProductOf3Maps<Affine, Affine, Affine>;\n  register_classes_with_charm<\n      CoordinateMap<Frame::ElementLogical, Frame::BlockLogical, Affine3D>,\n      CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine3D>>();\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> logical_dist{-1., 1.};\n\n  const ElementId<3> element_id{0, {{{0, 0}, {0, 0}, {0, 0}}}};\n  const Composition<\n      tmpl::list<Frame::ElementLogical, Frame::BlockLogical, Frame::Inertial>,\n      3>\n      map{element_to_block_logical_map(element_id),\n          std::make_unique<\n              CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine3D>>(\n              Affine3D{\n                  {-1., 1., -1., 1.}, {-1., 1., -1., 1.}, {-1., 1., -1., 1.}})};\n\n  CHECK(map.is_identity());\n  CHECK(map.function_of_time_names().empty());\n\n  const auto xi =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::ElementLogical>>(\n          make_not_null(&generator), make_not_null(&logical_dist),\n          DataVector(5));\n  const auto x = map(xi);\n  const auto jacobian = map.jacobian(xi);\n  const auto inv_jacobian = map.inv_jacobian(xi);\n#ifdef SPECTRE_AUTODIFF\n  const auto inv_hessian = map.inv_hessian(xi, inv_jacobian);\n#endif  // SPECTRE_AUTODIFF\n  CHECK_ITERABLE_APPROX(get<0>(x), get<0>(xi));\n  CHECK_ITERABLE_APPROX(get<1>(x), get<1>(xi));\n  CHECK_ITERABLE_APPROX(get<2>(x), get<2>(xi));\n  CHECK_ITERABLE_APPROX((get<0, 0>(jacobian)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<0, 0>(inv_jacobian)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<1, 1>(jacobian)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<1, 1>(inv_jacobian)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<2, 2>(jacobian)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<2, 2>(inv_jacobian)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<1, 0>(jacobian)), DataVector(5, 0.));\n  CHECK_ITERABLE_APPROX((get<1, 0>(inv_jacobian)), DataVector(5, 0.));\n  CHECK_ITERABLE_APPROX((get<2, 0>(jacobian)), DataVector(5, 0.));\n  CHECK_ITERABLE_APPROX((get<2, 0>(inv_jacobian)), DataVector(5, 0.));\n  CHECK_ITERABLE_APPROX((get<2, 1>(jacobian)), DataVector(5, 0.));\n  CHECK_ITERABLE_APPROX((get<2, 1>(inv_jacobian)), DataVector(5, 0.));\n#ifdef SPECTRE_AUTODIFF\n  for (const auto& component : inv_hessian) {\n    CHECK_ITERABLE_APPROX(component, DataVector(5, 0.0));\n  }\n#endif  // SPECTRE_AUTODIFF\n  const auto x_target = tnsr::I<double, 3, Frame::Inertial>{{{0.5, 1., 0.}}};\n  const auto inv = map.inverse(x_target);\n  REQUIRE(inv.has_value());\n  CHECK(get<0>(*inv) == approx(0.5));\n  CHECK(get<1>(*inv) == approx(1.));\n  CHECK(get<2>(*inv) == approx(0.));\n}\n\nvoid test_3d() {\n  INFO(\"3D\");\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> logical_dist{-1., 1.};\n\n  const ElementId<3> element_id{0, {{{1, 0}, {1, 0}, {2, 0}}}};\n  const Composition map{\n      element_to_block_logical_map(element_id),\n      std::make_unique<\n          CoordinateMap<Frame::BlockLogical, Frame::Inertial, Wedge<3>>>(\n          Wedge<3>{1., 3., 1., 1., OrientationMap<3>::create_aligned(), true})};\n  CHECK(map.function_of_time_names().empty());\n\n  // Check some points that are easy to calculate\n  // - On inner boundary\n  CHECK(get(magnitude(map(tnsr::I<double, 3, Frame::ElementLogical>{\n            {logical_dist(generator), logical_dist(generator), -1.}}))) ==\n        approx(1.));\n  // - On outer boundary of first radial element\n  CHECK(get(magnitude(map(tnsr::I<double, 3, Frame::ElementLogical>{\n            {logical_dist(generator), logical_dist(generator), 1.}}))) ==\n        approx(1.5));\n\n  // Check Jacobian is consistent with inverse\n  const auto xi =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::ElementLogical>>(\n          make_not_null(&generator), make_not_null(&logical_dist),\n          DataVector(5));\n  const auto jacobian = map.jacobian(xi);\n  const auto inv_jacobian = map.inv_jacobian(xi);\n  const auto identity = tenex::evaluate<ti::I, ti::j>(\n      jacobian(ti::I, ti::k) * inv_jacobian(ti::K, ti::j));\n  CHECK_ITERABLE_APPROX((get<0, 0>(identity)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<1, 1>(identity)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<2, 2>(identity)), DataVector(5, 1.));\n  CHECK_ITERABLE_APPROX((get<1, 0>(identity)), DataVector(5, 0.));\n  CHECK_ITERABLE_APPROX((get<2, 0>(identity)), DataVector(5, 0.));\n  CHECK_ITERABLE_APPROX((get<2, 1>(identity)), DataVector(5, 0.));\n\n#ifdef SPECTRE_AUTODIFF\n  const auto inv_hessian = map.inv_hessian(xi, inv_jacobian);\n  const auto alt_inv_hessian = inv_hessian_helper(map, xi);\n  CHECK_ITERABLE_APPROX(inv_hessian, alt_inv_hessian);\n#endif  // SPECTRE_AUTODIFF\n}\n\nvoid test_unsupported_autodiff() {\n  INFO(\"Unsupported autodiff\");\n\n#ifdef SPECTRE_AUTODIFF\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> logical_dist{-1., 1.};\n\n  const ElementId<3> element_id{0, {{{0, 0}, {0, 0}, {0, 0}}}};\n  // EquatorialCompression does not support autodiff\n  const Composition map{\n      element_to_block_logical_map(element_id),\n      std::make_unique<CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                     EquatorialCompression>>(\n          EquatorialCompression{1.5, 2})};\n\n  // Check that supports_hessian() correctly returns false\n  CHECK_FALSE(map.supports_hessian());\n\n  const auto xi =\n      make_with_random_values<tnsr::I<double, 3, Frame::ElementLogical>>(\n          make_not_null(&generator), make_not_null(&logical_dist),\n          0.0);\n\n  // Calling inv_hessian should trigger an error\n  const auto inv_jacobian = map.inv_jacobian(xi);\n  CHECK_THROWS_WITH(\n      map.inv_hessian(xi, inv_jacobian),\n      Catch::Matchers::ContainsSubstring(\n          \"At least one of the Maps does not support autodiff\") &&\n          Catch::Matchers::ContainsSubstring(\"EquatorialCompression\"));\n\n  // Also test with DataVector\n  const auto xi_dv =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::ElementLogical>>(\n          make_not_null(&generator), make_not_null(&logical_dist),\n          DataVector(5));\n\n  const auto inv_jacobian_dv = map.inv_jacobian(xi_dv);\n  CHECK_THROWS_WITH(\n      map.inv_hessian(xi_dv, inv_jacobian_dv),\n      Catch::Matchers::ContainsSubstring(\n          \"At least one of the Maps does not support autodiff\") &&\n          Catch::Matchers::ContainsSubstring(\"EquatorialCompression\"));\n#endif  // SPECTRE_AUTODIFF\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Composition\", \"[Domain][Unit]\") {\n  test_composition();\n  test_identity();\n  test_3d();\n  test_unsupported_autodiff();\n}\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_CoordinateMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/BulgedCube.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/SpecialMobius.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\n#ifdef SPECTRE_AUTODIFF\n// This test helper function computes the inverse Hessian by\n// auto-differentiating through the inv_jacobian function, which gives the\n// derivatives of inv_jacobian with respect to the source coordinates, i.e.\n// \\frac{\\partial}{\\partial\\xi^k}\\left(\\frac{\\partial\\xi^i}{\\partial\n// x^j}\\right). The inverse Hessian is then given by the chain rule\n// \\frac{\\partial^2\\xi^i}{\\partial x^j \\partial x^k} =\n// \\frac{\\partial\\xi^l}{\\partial x^k} *\n// \\frac{\\partial}{\\partial\\xi^l}\\left(\\frac{\\partial\\xi^i}{\\partial\n// x^j}\\right).\ntemplate <size_t Dim, typename SourceFrame, typename TargetFrame = Frame::Grid,\n          typename Map>\nauto inv_hessian_helper(const Map& coordinate_map,\n                        const tnsr::I<double, Dim, SourceFrame>& source_point)\n    -> InverseHessian<double, Dim, SourceFrame, TargetFrame> {\n  using SecondOrderDualNum = autodiff::HigherOrderDual<2, double>;\n\n  const size_t num_pts = 1;\n\n  InverseHessian<double, Dim, SourceFrame, TargetFrame> inverse_hessian{\n      num_pts};\n\n  for (size_t pts_index = 0; pts_index < num_pts; ++pts_index) {\n    tnsr::I<SecondOrderDualNum, Dim, SourceFrame> dual_source_coords;\n\n    [&]<std::size_t... Is>(std::index_sequence<Is...>) {\n      ((get<Is>(dual_source_coords) = get<Is>(source_point)), ...);\n    }\n    (std::make_index_sequence<Dim>{});\n\n    for (size_t k = 0; k < Dim; ++k) {\n      for (size_t i = 0; i < Dim; ++i) {\n        for (size_t j = i; j < Dim; ++j) {\n          double inv_hessian_kij{0.0};\n\n          for (size_t l = 0; l < Dim; ++l) {\n            autodiff::seed<1>(dual_source_coords.get(l), 1.0);\n            for (size_t n = 1; n < Dim; ++n) {\n              autodiff::seed<1>(dual_source_coords.get((l + n) % Dim), 0.0);\n            }\n\n            const auto dual_inverse_jac =\n                coordinate_map.inv_jacobian(dual_source_coords);\n            const auto inv_jac_lj = autodiff::val(dual_inverse_jac.get(l, j));\n            const auto deriv_kli =\n                autodiff::derivative<1>(dual_inverse_jac.get(k, i));\n            inv_hessian_kij += inv_jac_lj * deriv_kli;\n            inverse_hessian.get(k, i, j) = inv_hessian_kij;\n          }\n        }\n      }\n    }\n  }\n\n  return inverse_hessian;\n}\n#endif  // SPECTRE_AUTODIFF\n\ntemplate <typename Map1, typename Map2, typename DataType, size_t Dim>\nauto compose_jacobians(const Map1& map1, const Map2& map2,\n                       const std::array<DataType, Dim>& point) {\n  const auto jac1 = map1.jacobian(point);\n  const auto jac2 = map2.jacobian(map1(point));\n\n  auto result = make_with_value<\n      Jacobian<DataType, Dim, Frame::BlockLogical, Frame::Grid>>(point[0], 0.);\n  for (size_t target = 0; target < Dim; ++target) {\n    for (size_t source = 0; source < Dim; ++source) {\n      for (size_t dummy = 0; dummy < Dim; ++dummy) {\n        result.get(target, source) +=\n            jac2.get(target, dummy) * jac1.get(dummy, source);\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <typename Map1, typename Map2, typename DataType, size_t Dim>\nauto compose_inv_jacobians(const Map1& map1, const Map2& map2,\n                           const std::array<DataType, Dim>& point) {\n  const auto inv_jac1 = map1.inv_jacobian(point);\n  const auto inv_jac2 = map2.inv_jacobian(map1(point));\n\n  auto result = make_with_value<\n      InverseJacobian<DataType, Dim, Frame::BlockLogical, Frame::Grid>>(\n      point[0], 0.);\n  for (size_t target = 0; target < Dim; ++target) {\n    for (size_t source = 0; source < Dim; ++source) {\n      for (size_t dummy = 0; dummy < Dim; ++dummy) {\n        result.get(source, target) +=\n            inv_jac1.get(source, dummy) * inv_jac2.get(dummy, target);\n      }\n    }\n  }\n  return result;\n}\n\nvoid test_single_coordinate_map() {\n  INFO(\"Single coordinate map\");\n  using affine_map1d = CoordinateMaps::Affine;\n\n  const auto affine1d = make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n      affine_map1d{-1.0, 1.0, 2.0, 8.0});\n  const auto affine1d_base_inertial =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          affine_map1d{-1.0, 1.0, 2.0, 8.0});\n  const auto affine1d_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          affine_map1d{-1.0, 1.0, 2.0, 8.0});\n  const auto affine1d_base_from_inertial =\n      affine1d_base_inertial->get_to_grid_frame();\n  const auto first_affine1d = affine_map1d{-1.0, 1.0, 2.0, 8.0};\n\n  CHECK(affine1d == *affine1d_base);\n  CHECK(*affine1d_base == affine1d);\n\n  std::array<std::array<double, 1>, 4> coords1d{\n      {{{0.1}}, {{-8.2}}, {{5.7}}, {{2.9}}}};\n\n  const auto check_map_ptr = [](const auto& first_map, const auto& map,\n                                const auto& map_base, const auto& local_coord,\n                                const auto& local_source_points) {\n    // coord is a std::array, and tuple_size<array> gets the size\n    constexpr size_t dim =\n        std::tuple_size<std::decay_t<decltype(local_coord)>>::value;\n    CHECK((make_array<double, dim>((*map_base)(local_source_points))) ==\n          first_map(local_coord));\n    CHECK((make_array<double, dim>(\n              map_base->inverse(tnsr::I<double, dim, Frame::Grid>{local_coord})\n                  .value())) == first_map.inverse(local_coord).value());\n\n    const auto expected_jac_no_frame = first_map.jacobian(local_coord);\n    Jacobian<double, dim, Frame::BlockLogical, Frame::Grid>\n        local_expected_jac{};\n    REQUIRE(expected_jac_no_frame.size() == local_expected_jac.size());\n    for (size_t i = 0; i < local_expected_jac.size(); ++i) {\n      local_expected_jac[i] = expected_jac_no_frame[i];\n    }\n    CHECK_ITERABLE_APPROX(map_base->jacobian(local_source_points),\n                          local_expected_jac);\n\n    const auto expected_inv_jac_no_frame = first_map.inv_jacobian(local_coord);\n    InverseJacobian<double, dim, Frame::BlockLogical, Frame::Grid>\n        local_expected_inv_jac{};\n    REQUIRE(expected_inv_jac_no_frame.size() == local_expected_inv_jac.size());\n    for (size_t i = 0; i < local_expected_jac.size(); ++i) {\n      local_expected_inv_jac[i] = expected_inv_jac_no_frame[i];\n    }\n    CHECK_ITERABLE_APPROX(map_base->inv_jacobian(local_source_points),\n                          local_expected_inv_jac);\n\n#ifdef SPECTRE_AUTODIFF\n    const auto local_expected_inv_hessian = make_with_value<\n        InverseHessian<double, dim, Frame::BlockLogical, Frame::Grid>>(\n        get<0>(local_source_points), 0.0);\n    CHECK_ITERABLE_APPROX(\n        map_base->inv_hessian(local_source_points,\n                              map_base->inv_jacobian(local_source_points)),\n        local_expected_inv_hessian);\n#endif  // SPECTRE_AUTODIFF\n\n    const auto coords_jacs_velocity =\n        map_base->coords_frame_velocity_jacobians(local_source_points);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          map(local_source_points));\n    CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                          map.inv_jacobian(local_source_points));\n    CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                          map.jacobian(local_source_points));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<double, dim, Frame::Grid>{0.0}));\n  };\n\n  for (const auto& coord : coords1d) {\n    const tnsr::I<double, 1, Frame::BlockLogical> source_points{{{coord[0]}}};\n    CHECK((make_array<double, 1>(affine1d(\n              tnsr::I<double, 1, Frame::BlockLogical>{{{coord[0]}}}))) ==\n          first_affine1d(coord));\n    CHECK((make_array<double, 1>(\n              affine1d.inverse(tnsr::I<double, 1, Frame::Grid>{{{coord[0]}}})\n                  .value())) == first_affine1d.inverse(coord).value());\n\n    CHECK(affine1d.jacobian(source_points).get(0, 0) ==\n          first_affine1d.jacobian(coord).get(0, 0));\n\n    CHECK(affine1d.inv_jacobian(source_points).get(0, 0) ==\n          first_affine1d.inv_jacobian(coord).get(0, 0));\n\n    check_map_ptr(first_affine1d, affine1d, affine1d_base, coord,\n                  source_points);\n    check_map_ptr(first_affine1d, affine1d, affine1d_base_from_inertial, coord,\n                  source_points);\n  }\n\n  CHECK_FALSE(affine1d.is_identity());\n  CHECK_FALSE(affine1d_base->is_identity());\n  CHECK_FALSE(affine1d_base_from_inertial->is_identity());\n\n  CHECK_FALSE(affine1d.inv_jacobian_is_time_dependent());\n  CHECK_FALSE(affine1d.jacobian_is_time_dependent());\n  CHECK_FALSE(affine1d_base->jacobian_is_time_dependent());\n  CHECK_FALSE(affine1d_base->inv_jacobian_is_time_dependent());\n\n  CHECK(affine1d.function_of_time_names().empty());\n  CHECK(affine1d_base->function_of_time_names().empty());\n  CHECK(affine1d_base_from_inertial->function_of_time_names().empty());\n\n  using rotate2d = CoordinateMaps::Rotation<2>;\n\n  const auto first_rotated2d = rotate2d{M_PI_4};\n  const auto rotated2d =\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(first_rotated2d);\n  const auto rotated2d_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          first_rotated2d);\n  const auto rotated2d_base_inertial =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          first_rotated2d);\n  const auto rotated2d_base_from_inertial =\n      rotated2d_base_inertial->get_to_grid_frame();\n\n  CHECK(rotated2d == *rotated2d_base);\n  CHECK(*rotated2d_base == rotated2d);\n\n  std::array<std::array<double, 2>, 4> coords2d{\n      {{{0.1, 2.8}}, {{-8.2, 2.8}}, {{5.7, -4.9}}, {{2.9, 3.4}}}};\n\n  for (const auto& coord : coords2d) {\n    const tnsr::I<double, 2, Frame::BlockLogical> source_points{coord};\n\n    CHECK((make_array<double, 2>(rotated2d(source_points))) ==\n          first_rotated2d(coord));\n    CHECK((make_array<double, 2>(\n              rotated2d.inverse(tnsr::I<double, 2, Frame::Grid>{coord})\n                  .value())) == first_rotated2d.inverse(coord).value());\n\n    const auto expected_jac_inertial = first_rotated2d.jacobian(coord);\n    Jacobian<double, 2, Frame::BlockLogical, Frame::Grid> expected_jac_grid{};\n    REQUIRE(decltype(expected_jac_inertial)::size() ==\n            decltype(expected_jac_grid)::size());\n    for (size_t i = 0; i < decltype(expected_jac_inertial)::size(); ++i) {\n      expected_jac_grid[i] = expected_jac_inertial[i];\n    }\n    CHECK(rotated2d.jacobian(source_points) == expected_jac_grid);\n\n    const auto expected_inv_jac_inertial = first_rotated2d.inv_jacobian(coord);\n    InverseJacobian<double, 2, Frame::BlockLogical, Frame::Grid>\n        expected_inv_jac_grid{};\n    REQUIRE(decltype(expected_inv_jac_inertial)::size() ==\n            decltype(expected_inv_jac_grid)::size());\n    for (size_t i = 0; i < decltype(expected_inv_jac_inertial)::size(); ++i) {\n      expected_inv_jac_grid[i] = expected_inv_jac_inertial[i];\n    }\n    CHECK(rotated2d.inv_jacobian(source_points) == expected_inv_jac_grid);\n\n    check_map_ptr(first_rotated2d, rotated2d, rotated2d_base, coord,\n                  source_points);\n    check_map_ptr(first_rotated2d, rotated2d, rotated2d_base_from_inertial,\n                  coord, source_points);\n  }\n\n  CHECK_FALSE(rotated2d.is_identity());\n  CHECK_FALSE(rotated2d_base->is_identity());\n  CHECK_FALSE(rotated2d_base_from_inertial->is_identity());\n\n  CHECK_FALSE(rotated2d.inv_jacobian_is_time_dependent());\n  CHECK_FALSE(rotated2d.jacobian_is_time_dependent());\n  CHECK_FALSE(rotated2d_base->jacobian_is_time_dependent());\n  CHECK_FALSE(rotated2d_base->inv_jacobian_is_time_dependent());\n\n  CHECK(rotated2d.function_of_time_names().empty());\n  CHECK(rotated2d_base->function_of_time_names().empty());\n  CHECK(rotated2d_base_from_inertial->function_of_time_names().empty());\n\n  using rotate3d = CoordinateMaps::Rotation<3>;\n\n  const auto first_rotated3d = rotate3d{M_PI_4, M_PI_4, M_PI_2};\n  const auto rotated3d =\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(first_rotated3d);\n  const auto rotated3d_base_inertial =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          first_rotated3d);\n  const auto rotated3d_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          first_rotated3d);\n  const auto rotated3d_base_from_inertial =\n      rotated3d_base_inertial->get_to_grid_frame();\n\n  CHECK(rotated3d == *rotated3d_base);\n  CHECK(*rotated3d_base == rotated3d);\n\n  std::array<std::array<double, 3>, 4> coords3d{{{{0.1, 2.8, 9.3}},\n                                                 {{-8.2, 2.8, -9.7}},\n                                                 {{5.7, -4.9, 8.1}},\n                                                 {{2.9, 3.4, -7.8}}}};\n\n  for (const auto& coord : coords3d) {\n    const tnsr::I<double, 3, Frame::BlockLogical> source_points{\n        {{coord[0], coord[1], coord[2]}}};\n\n    CHECK((make_array<double, 3>(rotated3d(source_points))) ==\n          first_rotated3d(coord));\n    CHECK((make_array<double, 3>(rotated3d\n                                     .inverse(tnsr::I<double, 3, Frame::Grid>{\n                                         {{coord[0], coord[1], coord[2]}}})\n                                     .value())) ==\n          first_rotated3d.inverse(coord).value());\n\n    const auto expected_jac_inertial = first_rotated3d.jacobian(coord);\n    Jacobian<double, 3, Frame::BlockLogical, Frame::Grid> expected_jac_grid{};\n    REQUIRE(decltype(expected_jac_inertial)::size() ==\n            decltype(expected_jac_grid)::size());\n    for (size_t i = 0; i < decltype(expected_jac_inertial)::size(); ++i) {\n      expected_jac_grid[i] = expected_jac_inertial[i];\n    }\n    CHECK(rotated3d.jacobian(source_points) == expected_jac_grid);\n\n    const auto expected_inv_jac_inertial = first_rotated3d.inv_jacobian(coord);\n    InverseJacobian<double, 3, Frame::BlockLogical, Frame::Grid>\n        expected_inv_jac_grid{};\n    REQUIRE(decltype(expected_inv_jac_inertial)::size() ==\n            decltype(expected_inv_jac_grid)::size());\n    for (size_t i = 0; i < decltype(expected_inv_jac_inertial)::size(); ++i) {\n      expected_inv_jac_grid[i] = expected_inv_jac_inertial[i];\n    }\n    CHECK(rotated3d.inv_jacobian(source_points) == expected_inv_jac_grid);\n\n    check_map_ptr(first_rotated3d, rotated3d, rotated3d_base, coord,\n                  source_points);\n    check_map_ptr(first_rotated3d, rotated3d, rotated3d_base_from_inertial,\n                  coord, source_points);\n  }\n\n  CHECK_FALSE(rotated3d.is_identity());\n  CHECK_FALSE(rotated3d_base->is_identity());\n  CHECK_FALSE(rotated3d_base_from_inertial->is_identity());\n\n  CHECK_FALSE(rotated3d.inv_jacobian_is_time_dependent());\n  CHECK_FALSE(rotated3d.jacobian_is_time_dependent());\n  CHECK_FALSE(rotated3d_base->jacobian_is_time_dependent());\n  CHECK_FALSE(rotated3d_base->inv_jacobian_is_time_dependent());\n\n  CHECK(rotated3d.function_of_time_names().empty());\n  CHECK(rotated3d_base->function_of_time_names().empty());\n  CHECK(rotated3d_base_from_inertial->function_of_time_names().empty());\n}\n\nvoid test_coordinate_map_with_affine_map() {\n  INFO(\"Coordinate map with affine map\");\n  using affine_map = CoordinateMaps::Affine;\n  using affine_map_2d = CoordinateMaps::ProductOf2Maps<affine_map, affine_map>;\n  using affine_map_3d =\n      CoordinateMaps::ProductOf3Maps<affine_map, affine_map, affine_map>;\n\n  constexpr size_t number_of_points_checked = 10;\n\n  // Test 1D\n  const auto map = make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n      affine_map{-1.0, 1.0, 0.0, 2.3}, affine_map{0.0, 2.3, -0.5, 0.5});\n  for (size_t i = 1; i < number_of_points_checked + 1; ++i) {\n    const tnsr::I<double, 1, Frame::BlockLogical> source_points{2.0 / i + -1.0};\n    CHECK((tnsr::I<double, 1, Frame::Grid>(1.0 / i + -0.5))[0] ==\n          approx(map(source_points)[0]));\n    CHECK((tnsr::I<double, 1, Frame::BlockLogical>(2.0 / i + -1.0))[0] ==\n          approx(map.inverse(tnsr::I<double, 1, Frame::Grid>{1.0 / i + -0.5})\n                     .value()[0]));\n\n    const auto inv_jac = map.inv_jacobian(source_points);\n    CHECK(approx(inv_jac.get(0, 0)) == 2.0);\n    CHECK(approx(map.jacobian(source_points).get(0, 0)) == 0.5);\n#ifdef SPECTRE_AUTODIFF\n    CHECK(approx(map.inv_hessian(source_points, inv_jac).get(0, 0, 0)) == 0.0);\n#endif  // SPECTRE_AUTODIFF\n\n    const auto coords_jacs_velocity =\n        map.coords_frame_velocity_jacobians(source_points);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          map(source_points));\n    CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                          map.inv_jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                          map.jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<double, 1, Frame::Grid>{0.0}));\n  }\n\n  // Test 2D\n  const auto prod_map2d = make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n      affine_map_2d{affine_map{-1.0, 1.0, 0.0, 2.0},\n                    affine_map{0.0, 2.0, -0.5, 0.5}},\n      affine_map_2d{affine_map{0.0, 2.0, 2.0, 6.0},\n                    affine_map{-0.5, 0.5, 0.0, 8.0}});\n  for (size_t i = 1; i < number_of_points_checked + 1; ++i) {\n    const tnsr::I<double, 2, Frame::BlockLogical> source_points{\n        {{-1.0 + 2.0 / i, 0.0 + 2.0 / i}}};\n    const auto mapped_point = prod_map2d(source_points);\n    const auto expected_mapped_point =\n        tnsr::I<double, 2, Frame::Grid>{{{4.0 / i + 2.0, 8.0 / i + 0.0}}};\n    CHECK(get<0>(expected_mapped_point) == approx(get<0>(mapped_point)));\n    CHECK(get<1>(expected_mapped_point) == approx(get<1>(mapped_point)));\n\n    const auto inv_mapped_point = prod_map2d\n                                      .inverse(tnsr::I<double, 2, Frame::Grid>{\n                                          {{4.0 / i + 2.0, 8.0 / i + 0.0}}})\n                                      .value();\n    const auto expected_inv_mapped_point =\n        tnsr::I<double, 2, Frame::Grid>{{{-1.0 + 2.0 / i, 0.0 + 2.0 / i}}};\n    CHECK(get<0>(expected_inv_mapped_point) ==\n          approx(get<0>(inv_mapped_point)));\n    CHECK(get<1>(expected_inv_mapped_point) ==\n          approx(get<1>(inv_mapped_point)));\n\n    const auto inv_jac = prod_map2d.inv_jacobian(source_points);\n    CHECK(0.5 == approx(get<0, 0>(inv_jac)));\n    CHECK(0.0 == approx(get<1, 0>(inv_jac)));\n    CHECK(0.0 == approx(get<0, 1>(inv_jac)));\n    CHECK(0.25 == approx(get<1, 1>(inv_jac)));\n\n    const auto jac = prod_map2d.jacobian(source_points);\n    CHECK(2.0 == approx(get<0, 0>(jac)));\n    CHECK(0.0 == approx(get<1, 0>(jac)));\n    CHECK(0.0 == approx(get<0, 1>(jac)));\n    CHECK(4.0 == approx(get<1, 1>(jac)));\n\n#ifdef SPECTRE_AUTODIFF\n    auto inv_hessian = prod_map2d.inv_hessian(source_points, inv_jac);\n    for (auto& component : inv_hessian) {\n      CHECK(0.0 == approx(component));\n    }\n#endif  // SPECTRE_AUTODIFF\n\n    const auto coords_jacs_velocity =\n        prod_map2d.coords_frame_velocity_jacobians(source_points);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          prod_map2d(source_points));\n    CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                          prod_map2d.inv_jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                          prod_map2d.jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<double, 2, Frame::Grid>{0.0}));\n  }\n\n  // Test 3D\n  const auto prod_map3d = make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n      affine_map_3d{affine_map{-1.0, 1.0, 0.0, 2.0},\n                    affine_map{0.0, 2.0, -0.5, 0.5},\n                    affine_map{5.0, 7.0, -7.0, 7.0}},\n      affine_map_3d{affine_map{0.0, 2.0, 2.0, 6.0},\n                    affine_map{-0.5, 0.5, 0.0, 8.0},\n                    affine_map{-7.0, 7.0, 3.0, 23.0}});\n\n  for (size_t i = 1; i < number_of_points_checked + 1; ++i) {\n    const tnsr::I<double, 3, Frame::BlockLogical> source_points{\n        {{-1.0 + 2.0 / i, 0.0 + 2.0 / i, 5.0 + 2.0 / i}}};\n    const auto mapped_point = prod_map3d(source_points);\n    const auto expected_mapped_point = tnsr::I<double, 3, Frame::Grid>{\n        {{4.0 / i + 2.0, 8.0 / i + 0.0, 3.0 + 20.0 / i}}};\n    CHECK(get<0>(expected_mapped_point) == approx(get<0>(mapped_point)));\n    CHECK(get<1>(expected_mapped_point) == approx(get<1>(mapped_point)));\n    CHECK(get<2>(expected_mapped_point) == approx(get<2>(mapped_point)));\n\n    const auto inv_mapped_point =\n        prod_map3d\n            .inverse(tnsr::I<double, 3, Frame::Grid>{\n                {{4.0 / i + 2.0, 8.0 / i + 0.0, 3.0 + 20.0 / i}}})\n            .value();\n    const auto expected_inv_mapped_point = tnsr::I<double, 3, Frame::Grid>{\n        {{-1.0 + 2.0 / i, 0.0 + 2.0 / i, 5.0 + 2.0 / i}}};\n    CHECK(get<0>(expected_inv_mapped_point) ==\n          approx(get<0>(inv_mapped_point)));\n    CHECK(get<1>(expected_inv_mapped_point) ==\n          approx(get<1>(inv_mapped_point)));\n    CHECK(get<2>(expected_inv_mapped_point) ==\n          approx(get<2>(inv_mapped_point)));\n\n    const auto inv_jac = prod_map3d.inv_jacobian(source_points);\n    CHECK(0.5 == approx(get<0, 0>(inv_jac)));\n    CHECK(0.0 == approx(get<1, 0>(inv_jac)));\n    CHECK(0.0 == approx(get<0, 1>(inv_jac)));\n    CHECK(0.25 == approx(get<1, 1>(inv_jac)));\n    CHECK(0.0 == approx(get<0, 2>(inv_jac)));\n    CHECK(0.0 == approx(get<1, 2>(inv_jac)));\n    CHECK(0.0 == approx(get<2, 0>(inv_jac)));\n    CHECK(0.0 == approx(get<2, 1>(inv_jac)));\n    CHECK(0.1 == approx(get<2, 2>(inv_jac)));\n\n    const auto jac = prod_map3d.jacobian(source_points);\n    CHECK(2.0 == approx(get<0, 0>(jac)));\n    CHECK(0.0 == approx(get<1, 0>(jac)));\n    CHECK(0.0 == approx(get<0, 1>(jac)));\n    CHECK(4.0 == approx(get<1, 1>(jac)));\n    CHECK(0.0 == approx(get<0, 2>(jac)));\n    CHECK(0.0 == approx(get<1, 2>(jac)));\n    CHECK(0.0 == approx(get<2, 0>(jac)));\n    CHECK(0.0 == approx(get<2, 1>(jac)));\n    CHECK(10.0 == approx(get<2, 2>(jac)));\n\n#ifdef SPECTRE_AUTODIFF\n    auto inv_hessian = prod_map3d.inv_hessian(source_points, inv_jac);\n    for (auto& component : inv_hessian) {\n      CHECK(0.0 == approx(component));\n    }\n#endif  // SPECTRE_AUTODIFF\n\n    const auto coords_jacs_velocity =\n        prod_map3d.coords_frame_velocity_jacobians(source_points);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          prod_map3d(source_points));\n    CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                          prod_map3d.inv_jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                          prod_map3d.jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<double, 3, Frame::Grid>{0.0}));\n  }\n}\n\nvoid test_coordinate_map_with_rotation_map() {\n  INFO(\"Coordinate map with rotation map\");\n  using rotate2d = CoordinateMaps::Rotation<2>;\n  using rotate3d = CoordinateMaps::Rotation<3>;\n\n  // No 1D test because it would just the be affine map test\n\n  // Test 2D\n  const auto double_rotated2d =\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(rotate2d{M_PI_4},\n                                                            rotate2d{M_PI_2});\n  const auto first_rotated2d = rotate2d{M_PI_4};\n  const auto second_rotated2d = rotate2d{M_PI_2};\n\n  std::array<std::array<double, 2>, 4> coords2d{\n      {{{0.1, 2.8}}, {{-8.2, 2.8}}, {{5.7, -4.9}}, {{2.9, 3.4}}}};\n\n  for (size_t i = 0; i < coords2d.size(); ++i) {\n    INFO(i);\n    const auto coord = gsl::at(coords2d, i);\n    const tnsr::I<double, 2, Frame::BlockLogical> source_points{\n        {{coord[0], coord[1]}}};\n\n    CHECK((make_array<double, 2>(double_rotated2d(source_points))) ==\n          second_rotated2d(first_rotated2d(coord)));\n    CHECK((make_array<double, 2>(double_rotated2d\n                                     .inverse(tnsr::I<double, 2, Frame::Grid>{\n                                         {{coord[0], coord[1]}}})\n                                     .value())) ==\n          first_rotated2d.inverse(second_rotated2d.inverse(coord).value())\n              .value());\n\n    const auto jac = double_rotated2d.jacobian(source_points);\n    const auto expected_jac = compose_jacobians(\n        first_rotated2d, second_rotated2d, gsl::at(coords2d, i));\n    CHECK_ITERABLE_APPROX(jac, expected_jac);\n\n    const auto inv_jac = double_rotated2d.inv_jacobian(source_points);\n    const auto expected_inv_jac = compose_inv_jacobians(\n        first_rotated2d, second_rotated2d, gsl::at(coords2d, i));\n    CHECK_ITERABLE_APPROX(inv_jac, expected_inv_jac);\n\n#ifdef SPECTRE_AUTODIFF\n    auto inv_hessian = double_rotated2d.inv_hessian(source_points, inv_jac);\n    for (auto& component : inv_hessian) {\n      CHECK(0.0 == approx(component));\n    }\n#endif  // SPECTRE_AUTODIFF\n\n    const auto coords_jacs_velocity =\n        double_rotated2d.coords_frame_velocity_jacobians(source_points);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          double_rotated2d(source_points));\n    CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                          double_rotated2d.inv_jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                          double_rotated2d.jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<double, 2, Frame::Grid>{0.0}));\n  }\n\n  // Test 3D\n  const auto double_rotated3d =\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          rotate3d{M_PI_4, M_PI_4, M_PI_2}, rotate3d{M_PI_2, M_PI_4, M_PI_4});\n  const auto first_rotated3d = rotate3d{M_PI_4, M_PI_4, M_PI_2};\n  const auto second_rotated3d = rotate3d{M_PI_2, M_PI_4, M_PI_4};\n\n  std::array<std::array<double, 3>, 4> coords3d{{{{0.1, 2.8, 9.3}},\n                                                 {{-8.2, 2.8, -9.7}},\n                                                 {{5.7, -4.9, 8.1}},\n                                                 {{2.9, 3.4, -7.8}}}};\n\n  for (size_t i = 0; i < coords3d.size(); ++i) {\n    INFO(i);\n    const auto coord = gsl::at(coords3d, i);\n    const tnsr::I<double, 3, Frame::BlockLogical> source_points{\n        {{coord[0], coord[1], coord[2]}}};\n\n    CHECK((make_array<double, 3>(double_rotated3d(source_points))) ==\n          second_rotated3d(first_rotated3d(coord)));\n    CHECK((make_array<double, 3>(double_rotated3d\n                                     .inverse(tnsr::I<double, 3, Frame::Grid>{\n                                         {{coord[0], coord[1], coord[2]}}})\n                                     .value())) ==\n          first_rotated3d.inverse(second_rotated3d.inverse(coord).value())\n              .value());\n\n    const auto jac = double_rotated3d.jacobian(source_points);\n    const auto expected_jac = compose_jacobians(\n        first_rotated3d, second_rotated3d, gsl::at(coords3d, i));\n    CHECK_ITERABLE_APPROX(jac, expected_jac);\n\n    const auto inv_jac = double_rotated3d.inv_jacobian(source_points);\n    const auto expected_inv_jac = compose_inv_jacobians(\n        first_rotated3d, second_rotated3d, gsl::at(coords3d, i));\n    CHECK_ITERABLE_APPROX(inv_jac, expected_inv_jac);\n\n#ifdef SPECTRE_AUTODIFF\n    auto inv_hessian = double_rotated3d.inv_hessian(source_points, inv_jac);\n    for (auto& component : inv_hessian) {\n      CHECK(0.0 == approx(component));\n    }\n#endif  // SPECTRE_AUTODIFF\n\n    const auto coords_jacs_velocity =\n        double_rotated3d.coords_frame_velocity_jacobians(source_points);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          double_rotated3d(source_points));\n    CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                          double_rotated3d.inv_jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                          double_rotated3d.jacobian(source_points));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<double, 3, Frame::Grid>{0.0}));\n  }\n\n  // Check inequivalence operator\n  CHECK_FALSE(double_rotated3d != double_rotated3d);\n  test_serialization(double_rotated3d);\n}\n\nvoid test_coordinate_map_with_rotation_map_datavector() {\n  INFO(\"Coordinate map with rotation map datavector\");\n  using rotate2d = CoordinateMaps::Rotation<2>;\n  using rotate3d = CoordinateMaps::Rotation<3>;\n\n  // No 1D test because it would just the be affine map test\n\n  // Test 2D\n  {\n    const auto double_rotated2d =\n        make_coordinate_map<Frame::BlockLogical, Frame::Grid>(rotate2d{M_PI_4},\n                                                              rotate2d{M_PI_2});\n    const auto first_rotated2d = rotate2d{M_PI_4};\n    const auto second_rotated2d = rotate2d{M_PI_2};\n\n    const tnsr::I<DataVector, 2, Frame::BlockLogical> coords2d{\n        {{DataVector{0.1, -8.2, 5.7, 2.9}, DataVector{2.8, 2.8, -4.9, 3.4}}}};\n    const tnsr::I<DataVector, 2, Frame::Grid> coords2d_grid{\n        {{DataVector{0.1, -8.2, 5.7, 2.9}, DataVector{2.8, 2.8, -4.9, 3.4}}}};\n    const auto coords2d_array = make_array<DataVector, 2>(coords2d);\n\n    CHECK((make_array<DataVector, 2>(double_rotated2d(coords2d))) ==\n          second_rotated2d(first_rotated2d(coords2d_array)));\n\n    const auto jac = double_rotated2d.jacobian(coords2d);\n    const auto expected_jac =\n        compose_jacobians(first_rotated2d, second_rotated2d, coords2d_array);\n    CHECK_ITERABLE_APPROX(jac, expected_jac);\n\n    const auto inv_jac = double_rotated2d.inv_jacobian(coords2d);\n    const auto expected_inv_jac = compose_inv_jacobians(\n        first_rotated2d, second_rotated2d, coords2d_array);\n    CHECK_ITERABLE_APPROX(inv_jac, expected_inv_jac);\n\n#ifdef SPECTRE_AUTODIFF\n    const DataVector expected_zero{get<0>(coords2d).size(), 0.0};\n    auto inv_hessian = double_rotated2d.inv_hessian(coords2d, inv_jac);\n    for (auto& component : inv_hessian) {\n      CHECK_ITERABLE_APPROX(component, expected_zero);\n    }\n#endif  // SPECTRE_AUTODIFF\n\n    const auto coords_jacs_velocity =\n        double_rotated2d.coords_frame_velocity_jacobians(coords2d);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          double_rotated2d(coords2d));\n    CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                          double_rotated2d.inv_jacobian(coords2d));\n    CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                          double_rotated2d.jacobian(coords2d));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<DataVector, 2, Frame::Grid>{\n                              DataVector{coords2d.get(0).size(), 0.0}}));\n  }\n\n  // Test 3D\n  {\n    const auto first_rotated3d = rotate3d{M_PI_4, M_PI_4, M_PI_2};\n    const auto second_rotated3d = rotate3d{M_PI_2, M_PI_4, M_PI_4};\n    const auto double_rotated3d_full =\n        make_coordinate_map<Frame::BlockLogical, Frame::Grid>(first_rotated3d,\n                                                              second_rotated3d);\n    const auto double_rotated3d_base =\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n            first_rotated3d, second_rotated3d);\n    const auto& double_rotated3d = *double_rotated3d_base;\n\n    CHECK(double_rotated3d_full == double_rotated3d);\n\n    const auto different_rotated3d_base =\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n            second_rotated3d, first_rotated3d);\n    CHECK(*different_rotated3d_base == *different_rotated3d_base);\n    CHECK(*different_rotated3d_base != double_rotated3d);\n\n    const tnsr::I<DataVector, 3, Frame::BlockLogical> coords3d{\n        {{DataVector{0.1, -8.2, 5.7, 2.9}, DataVector{2.8, 2.8, -4.9, 3.4},\n          DataVector{9.3, -9.7, 8.1, -7.8}}}};\n    const tnsr::I<DataVector, 3, Frame::Grid> coords3d_grid{\n        {{DataVector{0.1, -8.2, 5.7, 2.9}, DataVector{2.8, 2.8, -4.9, 3.4},\n          DataVector{9.3, -9.7, 8.1, -7.8}}}};\n    const auto coords3d_array = make_array<DataVector, 3>(coords3d);\n\n    CHECK((make_array<DataVector, 3>(double_rotated3d(coords3d))) ==\n          second_rotated3d(first_rotated3d(coords3d_array)));\n\n    const auto jac = double_rotated3d.jacobian(coords3d);\n    const auto expected_jac =\n        compose_jacobians(first_rotated3d, second_rotated3d, coords3d_array);\n    CHECK_ITERABLE_APPROX(jac, expected_jac);\n\n    const auto inv_jac = double_rotated3d.inv_jacobian(coords3d);\n    const auto expected_inv_jac = compose_inv_jacobians(\n        first_rotated3d, second_rotated3d, coords3d_array);\n    CHECK_ITERABLE_APPROX(inv_jac, expected_inv_jac);\n\n#ifdef SPECTRE_AUTODIFF\n    const DataVector expected_zero{get<0>(coords3d).size(), 0.0};\n    auto inv_hessian = double_rotated3d.inv_hessian(coords3d, inv_jac);\n    for (auto& component : inv_hessian) {\n      CHECK_ITERABLE_APPROX(component, expected_zero);\n    }\n#endif  // SPECTRE_AUTODIFF\n\n    // Check inequivalence operator\n    CHECK_FALSE(double_rotated3d_full != double_rotated3d_full);\n    test_serialization(double_rotated3d_full);\n\n    const auto coords_jacs_velocity =\n        double_rotated3d.coords_frame_velocity_jacobians(coords3d);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          double_rotated3d(coords3d));\n    CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                          double_rotated3d.inv_jacobian(coords3d));\n    CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                          double_rotated3d.jacobian(coords3d));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<DataVector, 3, Frame::Grid>{\n                              DataVector{coords3d.get(0).size(), 0.0}}));\n  }\n}\n\nvoid test_coordinate_map_with_rotation_wedge() {\n  INFO(\"Coordinate map with rotation wedge\");\n  using Rotate = CoordinateMaps::Rotation<2>;\n  using Wedge2D = CoordinateMaps::Wedge<2>;\n\n  const auto first_map = Rotate(2.0);\n  const auto second_map =\n      Wedge2D(3.0, 7.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n              false);\n\n  const auto composed_map =\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(first_map,\n                                                            second_map);\n\n  const std::array<double, 2> test_point_array{{0.1, 0.8}};\n  const tnsr::I<double, 2, Frame::BlockLogical> test_point_vector(\n      test_point_array);\n\n  const auto mapped_point_array = second_map(first_map(test_point_array));\n  const auto mapped_point_vector = composed_map(test_point_vector);\n  CHECK((make_array<double, 2>(mapped_point_vector)) == mapped_point_array);\n\n  const auto jac = composed_map.jacobian(test_point_vector);\n  const auto expected_jac =\n      compose_jacobians(first_map, second_map, test_point_array);\n  CHECK_ITERABLE_APPROX(jac, expected_jac);\n\n  const auto inv_jac = composed_map.inv_jacobian(test_point_vector);\n  const auto expected_inv_jac =\n      compose_inv_jacobians(first_map, second_map, test_point_array);\n  CHECK_ITERABLE_APPROX(inv_jac, expected_inv_jac);\n\n#ifdef SPECTRE_AUTODIFF\n  const auto inv_hessian = composed_map.inv_hessian(test_point_vector, inv_jac);\n\n  const auto alt_inv_hessian =\n      inv_hessian_helper(composed_map, test_point_vector);\n\n  CHECK_ITERABLE_APPROX(inv_hessian, alt_inv_hessian);\n  for (auto& component : inv_hessian) {\n    CHECK_FALSE(0.0 == approx(component));\n  }\n#endif  // SPECTRE_AUTODIFF\n\n  const auto coords_jacs_velocity =\n      composed_map.coords_frame_velocity_jacobians(test_point_vector);\n  CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                        composed_map(test_point_vector));\n  CHECK_ITERABLE_APPROX(std::get<1>(coords_jacs_velocity),\n                        composed_map.inv_jacobian(test_point_vector));\n  CHECK_ITERABLE_APPROX(std::get<2>(coords_jacs_velocity),\n                        composed_map.jacobian(test_point_vector));\n  CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                        (tnsr::I<double, 2, Frame::Grid>{0.0}));\n}\n\nvoid test_make_vector_coordinate_map_base() {\n  INFO(\"Make vector coordinate map base\");\n  using Affine = CoordinateMaps::Affine;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n\n  const auto affine1d = make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n      Affine{-1.0, 1.0, 2.0, 8.0});\n  const auto affine1d_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          Affine{-1.0, 1.0, 2.0, 8.0});\n  const auto vector_of_affine1d =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          Affine{-1.0, 1.0, 2.0, 8.0});\n\n  CHECK(affine1d == *affine1d_base);\n  CHECK(*affine1d_base == affine1d);\n  CHECK(affine1d == *(vector_of_affine1d[0]));\n  CHECK(*(vector_of_affine1d[0]) == affine1d);\n\n  using Wedge2DMap = CoordinateMaps::Wedge<2>;\n  const auto upper_xi_wedge =\n      Wedge2DMap{1.0,\n                 2.0,\n                 0.0,\n                 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                 true};\n  const auto upper_eta_wedge =\n      Wedge2DMap{1.0,\n                 2.0,\n                 0.0,\n                 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                 true};\n  const auto lower_xi_wedge =\n      Wedge2DMap{1.0,\n                 2.0,\n                 0.0,\n                 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                 true};\n  const auto lower_eta_wedge =\n      Wedge2DMap{1.0,\n                 2.0,\n                 0.0,\n                 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                 true};\n  const auto vector_of_wedges =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n              true},\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n              true},\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n              true},\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n              true});\n\n  CHECK(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n            upper_xi_wedge) == *(vector_of_wedges[0]));\n  CHECK(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n            upper_eta_wedge) == *(vector_of_wedges[1]));\n  CHECK(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n            lower_xi_wedge) == *(vector_of_wedges[2]));\n  CHECK(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n            lower_eta_wedge) == *(vector_of_wedges[3]));\n  CHECK(*make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            upper_xi_wedge) == *(vector_of_wedges[0]));\n  CHECK(*make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            upper_eta_wedge) == *(vector_of_wedges[1]));\n  CHECK(*make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            lower_xi_wedge) == *(vector_of_wedges[2]));\n  CHECK(*make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            lower_eta_wedge) == *(vector_of_wedges[3]));\n\n  const auto wedges = std::vector<Wedge2DMap>{upper_xi_wedge, upper_eta_wedge,\n                                              lower_xi_wedge, lower_eta_wedge};\n  const auto vector_of_wedges2 =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 2>(\n          wedges);\n  CHECK(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n            upper_xi_wedge) == *(vector_of_wedges2[0]));\n  CHECK(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n            upper_eta_wedge) == *(vector_of_wedges2[1]));\n  CHECK(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n            lower_xi_wedge) == *(vector_of_wedges2[2]));\n  CHECK(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n            lower_eta_wedge) == *(vector_of_wedges2[3]));\n  CHECK(*make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            upper_xi_wedge) == *(vector_of_wedges2[0]));\n  CHECK(*make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            upper_eta_wedge) == *(vector_of_wedges2[1]));\n  CHECK(*make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            lower_xi_wedge) == *(vector_of_wedges2[2]));\n  CHECK(*make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            lower_eta_wedge) == *(vector_of_wedges2[3]));\n\n  const auto translation =\n      Affine2D{Affine{-1.0, 1.0, -1.0, 1.0}, Affine{-1.0, 1.0, 0.0, 2.0}};\n  const auto vector_of_translated_wedges =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 2>(\n          wedges, translation);\n\n  const auto translated_upper_xi_wedge =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n              true},\n          translation);\n  const auto translated_upper_eta_wedge =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n              true},\n          translation);\n  const auto translated_lower_xi_wedge =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n              true},\n          translation);\n  const auto translated_lower_eta_wedge =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n              true},\n          translation);\n  const auto translated_upper_xi_wedge_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n              true},\n          translation);\n  const auto translated_upper_eta_wedge_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n              true},\n          translation);\n  const auto translated_lower_xi_wedge_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n              true},\n          translation);\n  const auto translated_lower_eta_wedge_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Wedge2DMap{\n              1.0, 2.0, 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n              true},\n          translation);\n\n  CHECK(translated_upper_xi_wedge == *(vector_of_translated_wedges[0]));\n  CHECK(translated_upper_eta_wedge == *(vector_of_translated_wedges[1]));\n  CHECK(translated_lower_xi_wedge == *(vector_of_translated_wedges[2]));\n  CHECK(translated_lower_eta_wedge == *(vector_of_translated_wedges[3]));\n  CHECK(*translated_upper_xi_wedge_base == *(vector_of_translated_wedges[0]));\n  CHECK(*translated_upper_eta_wedge_base == *(vector_of_translated_wedges[1]));\n  CHECK(*translated_lower_xi_wedge_base == *(vector_of_translated_wedges[2]));\n  CHECK(*translated_lower_eta_wedge_base == *(vector_of_translated_wedges[3]));\n}\n\nvoid test_coordinate_maps_are_identity() {\n  INFO(\"Coordinate maps are identity\");\n  const auto giant_identity_map =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          CoordinateMaps::Identity<3>{},\n          CoordinateMaps::BulgedCube{sqrt(3.0), 0.0, false},\n          CoordinateMaps::DiscreteRotation<3>{\n              OrientationMap<3>::create_aligned()},\n          CoordinateMaps::EquatorialCompression{1.0},\n          CoordinateMaps::Frustum{\n              std::array<std::array<double, 2>, 4>{\n                  {{{-1.0, -1.0}}, {{1.0, 1.0}}, {{-1.0, -1.0}}, {{1.0, 1.0}}}},\n              -1.0, 1.0, OrientationMap<3>::create_aligned(), false},\n          CoordinateMaps::ProductOf3Maps<CoordinateMaps::Affine,\n                                         CoordinateMaps::Affine,\n                                         CoordinateMaps::Affine>{\n              CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0},\n              CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0},\n              CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0}},\n          CoordinateMaps::Rotation<3>{0.0, 0.0, 0.0},\n          CoordinateMaps::SpecialMobius{0.0});\n  const std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>\n      giant_identity_map_base =\n          std::make_unique<std::decay_t<decltype(giant_identity_map)>>(\n              giant_identity_map);\n  test_serialization(giant_identity_map);\n\n  CHECK(giant_identity_map.is_identity());\n  CHECK(giant_identity_map_base->is_identity());\n\n  CHECK_FALSE(giant_identity_map.inv_jacobian_is_time_dependent());\n  CHECK_FALSE(giant_identity_map.jacobian_is_time_dependent());\n  CHECK_FALSE(giant_identity_map_base->inv_jacobian_is_time_dependent());\n  CHECK_FALSE(giant_identity_map_base->jacobian_is_time_dependent());\n\n  CHECK(giant_identity_map.function_of_time_names().empty());\n  CHECK(giant_identity_map_base->function_of_time_names().empty());\n\n  const auto wedge = make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n      CoordinateMaps::Wedge<3>(0.2, 4.0, 0.0, 1.0,\n                               OrientationMap<3>::create_aligned(), true));\n  const auto wedge_composed_with_giant_identity =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          CoordinateMaps::Wedge<3>(0.2, 4.0, 0.0, 1.0,\n                                   OrientationMap<3>::create_aligned(), true),\n          CoordinateMaps::Identity<3>{},\n          CoordinateMaps::BulgedCube{sqrt(3.0), 0.0, false},\n          CoordinateMaps::DiscreteRotation<3>{\n              OrientationMap<3>::create_aligned()},\n          CoordinateMaps::EquatorialCompression{1.0},\n          CoordinateMaps::Frustum{\n              std::array<std::array<double, 2>, 4>{\n                  {{{-1.0, -1.0}}, {{1.0, 1.0}}, {{-1.0, -1.0}}, {{1.0, 1.0}}}},\n              -1.0, 1.0, OrientationMap<3>::create_aligned(), false},\n          CoordinateMaps::ProductOf3Maps<CoordinateMaps::Affine,\n                                         CoordinateMaps::Affine,\n                                         CoordinateMaps::Affine>{\n              CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0},\n              CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0},\n              CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0}},\n          CoordinateMaps::Rotation<3>{0.0, 0.0, 0.0},\n          CoordinateMaps::SpecialMobius{0.0});\n\n  CHECK_FALSE(wedge.is_identity());\n  CHECK_FALSE(wedge_composed_with_giant_identity.is_identity());\n\n  CHECK_FALSE(wedge.inv_jacobian_is_time_dependent());\n  CHECK_FALSE(wedge.jacobian_is_time_dependent());\n  CHECK_FALSE(\n      wedge_composed_with_giant_identity.inv_jacobian_is_time_dependent());\n  CHECK_FALSE(wedge_composed_with_giant_identity.jacobian_is_time_dependent());\n\n  for (size_t i = 1; i < 11; ++i) {\n    const auto source_point = tnsr::I<double, 3, Frame::BlockLogical>{\n        {{-1.0 + 2.0 / i, -1.0 + 2.0 / i, -1.0 + 2.0 / i}}};\n    const auto mapped_point = tnsr::I<double, 3, Frame::Inertial>{\n        {{-1.0 + 2.0 / i, -1.0 + 2.0 / i, -1.0 + 2.0 / i}}};\n    CHECK(get<0>(mapped_point) == get<0>(giant_identity_map(source_point)));\n    CHECK(get<1>(mapped_point) == get<1>(giant_identity_map(source_point)));\n    CHECK(get<2>(mapped_point) == get<2>(giant_identity_map(source_point)));\n    CHECK(get<0>(source_point) ==\n          get<0>(giant_identity_map.inverse(mapped_point).value()));\n    CHECK(get<1>(source_point) ==\n          get<1>(giant_identity_map.inverse(mapped_point).value()));\n    CHECK(get<2>(source_point) ==\n          get<2>(giant_identity_map.inverse(mapped_point).value()));\n    const auto wedge_mapped_point = wedge(source_point);\n    CHECK(get<0>(wedge_mapped_point) ==\n          get<0>(wedge_composed_with_giant_identity(source_point)));\n    CHECK(get<1>(wedge_mapped_point) ==\n          get<1>(wedge_composed_with_giant_identity(source_point)));\n    CHECK(get<2>(wedge_mapped_point) ==\n          get<2>(wedge_composed_with_giant_identity(source_point)));\n    CHECK(get<0>(wedge.inverse(wedge_mapped_point).value()) ==\n          get<0>(wedge_composed_with_giant_identity.inverse(wedge_mapped_point)\n                     .value()));\n    CHECK(get<1>(wedge.inverse(wedge_mapped_point).value()) ==\n          get<1>(wedge_composed_with_giant_identity.inverse(wedge_mapped_point)\n                     .value()));\n    CHECK(get<2>(wedge.inverse(wedge_mapped_point).value()) ==\n          get<2>(wedge_composed_with_giant_identity.inverse(wedge_mapped_point)\n                     .value()));\n\n    const auto inv_jac = giant_identity_map.inv_jacobian(source_point);\n    CHECK(1.0 == approx(get<0, 0>(inv_jac)));\n    CHECK(0.0 == approx(get<1, 0>(inv_jac)));\n    CHECK(0.0 == approx(get<2, 0>(inv_jac)));\n    CHECK(0.0 == approx(get<0, 1>(inv_jac)));\n    CHECK(1.0 == approx(get<1, 1>(inv_jac)));\n    CHECK(0.0 == approx(get<2, 1>(inv_jac)));\n    CHECK(0.0 == approx(get<0, 2>(inv_jac)));\n    CHECK(0.0 == approx(get<1, 2>(inv_jac)));\n    CHECK(1.0 == approx(get<2, 2>(inv_jac)));\n    const auto jac = giant_identity_map.jacobian(source_point);\n    CHECK(1.0 == approx(get<0, 0>(jac)));\n    CHECK(0.0 == approx(get<1, 0>(jac)));\n    CHECK(0.0 == approx(get<2, 0>(jac)));\n    CHECK(0.0 == approx(get<0, 1>(jac)));\n    CHECK(1.0 == approx(get<1, 1>(jac)));\n    CHECK(0.0 == approx(get<2, 1>(jac)));\n    CHECK(0.0 == approx(get<0, 2>(jac)));\n    CHECK(0.0 == approx(get<1, 2>(jac)));\n    CHECK(1.0 == approx(get<2, 2>(jac)));\n\n    const auto wedge_1_jac = wedge.jacobian(source_point);\n    const auto wedge_2_jac =\n        wedge_composed_with_giant_identity.jacobian(source_point);\n\n    CHECK(get<0, 0>(wedge_1_jac) == get<0, 0>(wedge_2_jac));\n    CHECK(get<1, 0>(wedge_1_jac) == get<1, 0>(wedge_2_jac));\n    CHECK(get<2, 0>(wedge_1_jac) == get<2, 0>(wedge_2_jac));\n    CHECK(get<0, 0>(wedge_1_jac) == get<0, 0>(wedge_2_jac));\n    CHECK(get<1, 0>(wedge_1_jac) == get<1, 0>(wedge_2_jac));\n    CHECK(get<2, 0>(wedge_1_jac) == get<2, 0>(wedge_2_jac));\n    CHECK(get<0, 0>(wedge_1_jac) == get<0, 0>(wedge_2_jac));\n    CHECK(get<1, 0>(wedge_1_jac) == get<1, 0>(wedge_2_jac));\n    CHECK(get<2, 0>(wedge_1_jac) == get<2, 0>(wedge_2_jac));\n\n    const auto wedge_1_jac_inv = wedge.inv_jacobian(source_point);\n    const auto wedge_2_jac_inv =\n        wedge_composed_with_giant_identity.inv_jacobian(source_point);\n    CHECK(get<0, 0>(wedge_1_jac_inv) == get<0, 0>(wedge_2_jac_inv));\n    CHECK(get<1, 0>(wedge_1_jac_inv) == get<1, 0>(wedge_2_jac_inv));\n    CHECK(get<2, 0>(wedge_1_jac_inv) == get<2, 0>(wedge_2_jac_inv));\n    CHECK(get<0, 0>(wedge_1_jac_inv) == get<0, 0>(wedge_2_jac_inv));\n    CHECK(get<1, 0>(wedge_1_jac_inv) == get<1, 0>(wedge_2_jac_inv));\n    CHECK(get<2, 0>(wedge_1_jac_inv) == get<2, 0>(wedge_2_jac_inv));\n    CHECK(get<0, 0>(wedge_1_jac_inv) == get<0, 0>(wedge_2_jac_inv));\n    CHECK(get<1, 0>(wedge_1_jac_inv) == get<1, 0>(wedge_2_jac_inv));\n    CHECK(get<2, 0>(wedge_1_jac_inv) == get<2, 0>(wedge_2_jac_inv));\n\n    const auto coords_jacs_velocity =\n        wedge_composed_with_giant_identity.coords_frame_velocity_jacobians(\n            source_point);\n    CHECK_ITERABLE_APPROX(std::get<0>(coords_jacs_velocity),\n                          wedge_composed_with_giant_identity(source_point));\n    CHECK_ITERABLE_APPROX(\n        std::get<1>(coords_jacs_velocity),\n        wedge_composed_with_giant_identity.inv_jacobian(source_point));\n    CHECK_ITERABLE_APPROX(\n        std::get<2>(coords_jacs_velocity),\n        wedge_composed_with_giant_identity.jacobian(source_point));\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<double, 3, Frame::Inertial>{0.0}));\n  }\n}\n\nvoid test_time_dependent_map() {\n  INFO(\"Time dependent CoordinateMap\");\n  // define vars for FunctionOfTime::PiecewisePolynomial f(t) = t**2.\n  const double initial_time = -1.;\n  const double final_time = 4.4;\n  constexpr size_t deriv_order = 3;\n\n  const std::array<DataVector, deriv_order + 1> init_func{\n      {{1.0}, {-2.0}, {2.0}, {0.0}}};\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"Translation\"] =\n      std::make_unique<Polynomial>(initial_time, init_func, final_time);\n\n  const CoordinateMaps::TimeDependent::Translation<1> trans_map{\"Translation\"};\n\n  // affine(x) = 1.5 * x + 5.5\n  domain::CoordinateMaps::Affine affine_map{-1., 1., 4., 7.};\n\n  const auto time_dependent_map_first =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(trans_map,\n                                                                affine_map);\n  const auto time_dependent_map_second =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(affine_map,\n                                                                trans_map);\n\n  CHECK(time_dependent_map_first.inv_jacobian_is_time_dependent());\n  CHECK(time_dependent_map_first.jacobian_is_time_dependent());\n  CHECK(time_dependent_map_second.inv_jacobian_is_time_dependent());\n  CHECK(time_dependent_map_second.jacobian_is_time_dependent());\n\n  CHECK(time_dependent_map_first.function_of_time_names().count(\n            \"Translation\") == 1);\n  CHECK(time_dependent_map_second.function_of_time_names().count(\n            \"Translation\") == 1);\n\n  const tnsr::I<double, 1, Frame::BlockLogical> tnsr_double_logical{{{3.2}}};\n  const tnsr::I<DataVector, 1, Frame::BlockLogical> tnsr_datavector_logical{\n      DataVector{{-4.3, 10.1, -3.5}}};\n\n  const tnsr::I<double, 1, Frame::Inertial> tnsr_double_inertial_1{{{39.34}}};\n  const tnsr::I<double, 1, Frame::Inertial> tnsr_double_inertial_2{{{29.66}}};\n\n  const tnsr::I<DataVector, 1, Frame::Inertial> tnsr_datavector_inertial_1{\n      DataVector{28.09, 49.69, 29.29}};\n  const tnsr::I<DataVector, 1, Frame::Inertial> tnsr_datavector_inertial_2{\n      DataVector{18.41, 40.01, 19.61}};\n\n  CHECK_ITERABLE_APPROX(time_dependent_map_first(tnsr_double_logical,\n                                                 final_time, functions_of_time),\n                        tnsr_double_inertial_1);\n\n  CHECK_ITERABLE_APPROX(time_dependent_map_second(\n                            tnsr_double_logical, final_time, functions_of_time),\n                        tnsr_double_inertial_2);\n\n  CHECK_ITERABLE_APPROX(time_dependent_map_first(tnsr_datavector_logical,\n                                                 final_time, functions_of_time),\n                        tnsr_datavector_inertial_1);\n  CHECK_ITERABLE_APPROX(\n      time_dependent_map_second(tnsr_datavector_logical, final_time,\n                                functions_of_time),\n      tnsr_datavector_inertial_2);\n\n  CHECK_ITERABLE_APPROX(\n      *(time_dependent_map_first.inverse(tnsr_double_inertial_1, final_time,\n                                         functions_of_time)),\n      tnsr_double_logical);\n  CHECK_ITERABLE_APPROX(\n      *(time_dependent_map_second.inverse(tnsr_double_inertial_2, final_time,\n                                          functions_of_time)),\n      tnsr_double_logical);\n\n  CHECK(time_dependent_map_first\n            .jacobian(tnsr_double_logical, final_time, functions_of_time)\n            .get(0, 0) == 1.5);\n  const auto inv_jac_double = time_dependent_map_first.inv_jacobian(\n      tnsr_double_logical, final_time, functions_of_time);\n  CHECK(inv_jac_double.get(0, 0) == 2. / 3.);\n  CHECK_ITERABLE_APPROX(\n      time_dependent_map_first\n          .jacobian(tnsr_datavector_logical, final_time, functions_of_time)\n          .get(0, 0),\n      (DataVector{1.5, 1.5, 1.5}));\n  const auto inv_jac_datavector = time_dependent_map_first.inv_jacobian(\n      tnsr_datavector_logical, final_time, functions_of_time);\n  CHECK_ITERABLE_APPROX(inv_jac_datavector.get(0, 0),\n                        (DataVector{2. / 3., 2. / 3., 2. / 3.}));\n\n  test_serialization(time_dependent_map_first);\n\n  const auto serialized_map =\n      serialize_and_deserialize(time_dependent_map_first);\n\n  CHECK_ITERABLE_APPROX(\n      serialized_map(tnsr_double_logical, final_time, functions_of_time),\n      tnsr_double_inertial_1);\n\n  CHECK_ITERABLE_APPROX(\n      serialized_map(tnsr_datavector_logical, final_time, functions_of_time),\n      tnsr_datavector_inertial_1);\n\n  CHECK_ITERABLE_APPROX(\n      *(serialized_map.inverse(tnsr_double_inertial_1, final_time,\n                               functions_of_time)),\n      tnsr_double_logical);\n\n  CHECK(serialized_map\n            .jacobian(tnsr_double_logical, final_time, functions_of_time)\n            .get(0, 0) == 1.5);\n  CHECK(serialized_map\n            .inv_jacobian(tnsr_double_logical, final_time, functions_of_time)\n            .get(0, 0) == 2. / 3.);\n  CHECK_ITERABLE_APPROX(\n      serialized_map\n          .jacobian(tnsr_datavector_logical, final_time, functions_of_time)\n          .get(0, 0),\n      (DataVector{1.5, 1.5, 1.5}));\n  CHECK_ITERABLE_APPROX(\n      serialized_map\n          .inv_jacobian(tnsr_datavector_logical, final_time, functions_of_time)\n          .get(0, 0),\n      (DataVector{2. / 3., 2. / 3., 2. / 3.}));\n\n  {\n    const auto coords_jacs_velocity =\n        serialized_map.coords_frame_velocity_jacobians(\n            tnsr_double_logical, final_time, functions_of_time);\n    CHECK(std::get<0>(coords_jacs_velocity) ==\n          serialized_map(tnsr_double_logical, final_time, functions_of_time));\n    CHECK(std::get<1>(coords_jacs_velocity) ==\n          serialized_map.inv_jacobian(tnsr_double_logical, final_time,\n                                      functions_of_time));\n    CHECK(std::get<2>(coords_jacs_velocity) ==\n          serialized_map.jacobian(tnsr_double_logical, final_time,\n                                  functions_of_time));\n    const auto velocity =\n        functions_of_time.at(\"Translation\")->func_and_deriv(final_time)[1];\n    // The 1.5 factor comes from the Jacobian\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<double, 1, Frame::Inertial>{\n                              1.5 * velocity[velocity.size() - 1]}));\n  }\n  {\n    const auto coords_jacs_velocity =\n        serialized_map.coords_frame_velocity_jacobians(\n            tnsr_datavector_logical, final_time, functions_of_time);\n    CHECK_ITERABLE_APPROX(\n        std::get<0>(coords_jacs_velocity),\n        serialized_map(tnsr_datavector_logical, final_time, functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        std::get<1>(coords_jacs_velocity),\n        serialized_map.inv_jacobian(tnsr_datavector_logical, final_time,\n                                    functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        std::get<2>(coords_jacs_velocity),\n        serialized_map.jacobian(tnsr_datavector_logical, final_time,\n                                functions_of_time));\n    const auto velocity =\n        functions_of_time.at(\"Translation\")->func_and_deriv(final_time)[1];\n    // The 1.5 factor comes from the Jacobian\n    CHECK_ITERABLE_APPROX(std::get<3>(coords_jacs_velocity),\n                          (tnsr::I<DataVector, 1, Frame::Inertial>{DataVector{\n                              tnsr_datavector_logical.get(0).size(),\n                              1.5 * velocity[velocity.size() - 1]}}));\n  }\n}\n\n#ifdef SPECTRE_AUTODIFF\nvoid test_time_dependent_map_for_hessian() {\n  INFO(\"Time dependent CoordinateMap for hessian\");\n  // define vars for FunctionOfTime::PiecewisePolynomial f(t) = t**2.\n  const double initial_time = -1.;\n  const double final_time = 4.4;\n  constexpr size_t deriv_order = 3;\n\n  const std::array<DataVector, deriv_order + 1> init_func{\n      {{1.0}, {-2.0}, {2.0}, {0.0}}};\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"Rotation\"] =\n      std::make_unique<Polynomial>(initial_time, init_func, final_time);\n\n  const CoordinateMaps::TimeDependent::Rotation<2> rot_map{\"Rotation\"};\n\n  // affine(x) = x, affine(y) = y + 1\n  using Affine = CoordinateMaps::Affine;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                                  CoordinateMaps::Affine>;\n  const auto affine_map =\n      Affine2D{Affine{-1.0, 1.0, -1.0, 1.0}, Affine{-1.0, 1.0, 0.0, 2.0}};\n\n  const auto time_dependent_map_first =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(rot_map,\n                                                                affine_map);\n\n  const tnsr::I<double, 2, Frame::BlockLogical> tnsr_double_logical{\n      {{3.2, -4.3}}};\n  const tnsr::I<DataVector, 2, Frame::BlockLogical> tnsr_datavector_logical{\n      {{{0.1, -4.3, 10.0}, {1.2, 10.1, -3.5}}}};\n\n  const auto inv_jac_double = time_dependent_map_first.inv_jacobian(\n      tnsr_double_logical, final_time, functions_of_time);\n  const auto inv_jac_datavector = time_dependent_map_first.inv_jacobian(\n      tnsr_datavector_logical, final_time, functions_of_time);\n\n  auto inv_hessian_double = time_dependent_map_first.inv_hessian(\n      tnsr_double_logical, inv_jac_double, final_time, functions_of_time);\n  for (const auto& component : inv_hessian_double) {\n    CHECK(component == 0.0);\n  }\n  auto inv_hessian_datavector = time_dependent_map_first.inv_hessian(\n      tnsr_datavector_logical, inv_jac_datavector, final_time,\n      functions_of_time);\n  for (const auto& component : inv_hessian_datavector) {\n    CHECK_ITERABLE_APPROX(component, (DataVector{0.0, 0.0, 0.0}));\n  }\n\n  test_serialization(time_dependent_map_first);\n\n  const auto serialized_map =\n      serialize_and_deserialize(time_dependent_map_first);\n\n  inv_hessian_double = serialized_map.inv_hessian(\n      tnsr_double_logical, inv_jac_double, final_time, functions_of_time);\n  for (const auto& component : inv_hessian_double) {\n    CHECK(component == 0.0);\n  }\n  inv_hessian_datavector =\n      serialized_map.inv_hessian(tnsr_datavector_logical, inv_jac_datavector,\n                                 final_time, functions_of_time);\n  for (const auto& component : inv_hessian_datavector) {\n    CHECK_ITERABLE_APPROX(component, (DataVector{0.0, 0.0, 0.0}));\n  }\n}\n#endif // SPECTRE_AUTODIFF\n\nvoid test_push_back() {\n  INFO(\"Coordinate map with affine map\");\n  using affine_map = CoordinateMaps::Affine;\n  using affine_map_2d = CoordinateMaps::ProductOf2Maps<affine_map, affine_map>;\n  using affine_map_3d =\n      CoordinateMaps::ProductOf3Maps<affine_map, affine_map, affine_map>;\n\n  const auto check_coord_map_push = [](const auto& first_map,\n                                       const auto& second_map) {\n    const auto first_coord_map =\n        make_coordinate_map<Frame::BlockLogical, Frame::Grid>(first_map);\n    const auto second_coord_map =\n        make_coordinate_map<Frame::BlockLogical, Frame::Grid>(second_map);\n    const auto composed_map =\n        make_coordinate_map<Frame::BlockLogical, Frame::Grid>(first_map,\n                                                              second_map);\n    const auto composed_push_back_map = push_back(first_coord_map, second_map);\n    const auto composed_push_front_map =\n        push_front(second_coord_map, first_map);\n    CHECK(composed_map == composed_push_back_map);\n    CHECK(composed_map == composed_push_front_map);\n\n    CHECK(composed_map == push_back(first_coord_map, second_coord_map));\n    CHECK(composed_map == push_front(second_coord_map, first_coord_map));\n  };\n\n  // Test 1d\n  check_coord_map_push(affine_map{-1.0, 1.0, 0.0, 2.3},\n                       affine_map{0.0, 2.3, -0.5, 0.5});\n  // Test 2d\n  check_coord_map_push(affine_map_2d{affine_map{-1.0, 1.0, 0.0, 2.0},\n                                     affine_map{0.0, 2.0, -0.5, 0.5}},\n                       affine_map_2d{affine_map{0.0, 2.0, 2.0, 6.0},\n                                     affine_map{-0.5, 0.5, 0.0, 8.0}});\n  // Test 3d\n  check_coord_map_push(affine_map_3d{affine_map{-1.0, 1.0, 0.0, 2.0},\n                                     affine_map{0.0, 2.0, -0.5, 0.5},\n                                     affine_map{5.0, 7.0, -7.0, 7.0}},\n                       affine_map_3d{affine_map{0.0, 2.0, 2.0, 6.0},\n                                     affine_map{-0.5, 0.5, 0.0, 8.0},\n                                     affine_map{-7.0, 7.0, 3.0, 23.0}});\n}\n\nvoid test_jacobian_is_time_dependent() {\n  using affine_map = CoordinateMaps::Affine;\n  using cubic_scale_map = CoordinateMaps::TimeDependent::CubicScale<1>;\n  using map_2d = CoordinateMaps::TimeDependent::ProductOf2Maps<affine_map,\n                                                               cubic_scale_map>;\n  using map_3d =\n      CoordinateMaps::TimeDependent::ProductOf3Maps<affine_map, affine_map,\n                                                    cubic_scale_map>;\n\n  const auto coord_map_1 =\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          cubic_scale_map(10.0, \"ExpansionA\", \"ExpansionB\"));\n  const auto coord_map_1_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          cubic_scale_map(10.0, \"ExpansionA\", \"ExpansionB\"));\n\n  const auto coord_map_2 =\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          map_2d(affine_map(-1.0, 1.0, 2.0, 3.0),\n                 cubic_scale_map(10.0, \"ExpansionA\", \"ExpansionB\")));\n  const auto coord_map_2_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          map_2d(affine_map(-1.0, 1.0, 2.0, 3.0),\n                 cubic_scale_map(10.0, \"ExpansionA\", \"ExpansionB\")));\n\n  const auto coord_map_3 =\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map_3d(\n          affine_map(-1.0, 1.0, 2.0, 3.0), affine_map(-1.0, 1.0, 2.0, 3.0),\n          cubic_scale_map(10.0, \"ExpansionA\", \"ExpansionB\")));\n  const auto coord_map_3_base =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(map_3d(\n          affine_map(-1.0, 1.0, 2.0, 3.0), affine_map(-1.0, 1.0, 2.0, 3.0),\n          cubic_scale_map(10.0, \"ExpansionA\", \"ExpansionB\")));\n\n  CHECK(coord_map_1.inv_jacobian_is_time_dependent());\n  CHECK(coord_map_1.jacobian_is_time_dependent());\n  CHECK(coord_map_1_base->inv_jacobian_is_time_dependent());\n  CHECK(coord_map_1_base->jacobian_is_time_dependent());\n\n  CHECK(coord_map_2.inv_jacobian_is_time_dependent());\n  CHECK(coord_map_2.jacobian_is_time_dependent());\n  CHECK(coord_map_2_base->inv_jacobian_is_time_dependent());\n  CHECK(coord_map_2_base->jacobian_is_time_dependent());\n\n  CHECK(coord_map_3.inv_jacobian_is_time_dependent());\n  CHECK(coord_map_3.jacobian_is_time_dependent());\n  CHECK(coord_map_3_base->inv_jacobian_is_time_dependent());\n  CHECK(coord_map_3_base->jacobian_is_time_dependent());\n}\n\nvoid test_coords_frame_velocity_jacobians() {\n  using affine_map = CoordinateMaps::Affine;\n  using trans_map = CoordinateMaps::TimeDependent::Translation<1>;\n  using affine_map_2d = CoordinateMaps::ProductOf2Maps<affine_map, affine_map>;\n  using trans_map_2d = CoordinateMaps::TimeDependent::Translation<2>;\n  using affine_map_3d =\n      CoordinateMaps::ProductOf3Maps<affine_map, affine_map, affine_map>;\n  using trans_map_3d = CoordinateMaps::TimeDependent::Translation<3>;\n\n  const double initial_time = 0.0;\n  const double final_time = 2.0;\n  const double time = 2.0;\n  constexpr size_t deriv_order = 3;\n\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"trans_1d\"] = std::make_unique<Polynomial>(\n      initial_time,\n      std::array<DataVector, deriv_order + 1>{{{1.0}, {-2.0}, {0.0}, {0.0}}},\n      final_time);\n  functions_of_time[\"trans_2d\"] = std::make_unique<Polynomial>(\n      initial_time,\n      std::array<DataVector, deriv_order + 1>{\n          {{2, 1.0}, {-2.0, 3.0}, {2, 0.0}, {2, 0.0}}},\n      final_time);\n  functions_of_time[\"trans_3d\"] = std::make_unique<Polynomial>(\n      initial_time,\n      std::array<DataVector, deriv_order + 1>{\n          {{3, 1.0}, {-2.0, 3.0, 4.5}, {3, 0.0}, {3, 0.0}}},\n      final_time);\n  functions_of_time[\"ExpansionA\"] = std::make_unique<Polynomial>(\n      initial_time,\n      std::array<DataVector, deriv_order + 1>{{{1.0}, {-0.01}, {0.0}, {0.0}}},\n      final_time);\n  functions_of_time[\"ExpansionB\"] = std::make_unique<Polynomial>(\n      initial_time,\n      std::array<DataVector, deriv_order + 1>{{{1.0}, {0.0}, {0.0}, {0.0}}},\n      final_time);\n\n  const auto composed_map_1d =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          trans_map{\"trans_1d\"}, affine_map{-1.0, 1.0, 0.0, 2.3},\n          trans_map{\"trans_1d\"});\n  CHECK(std::get<3>(composed_map_1d.coords_frame_velocity_jacobians(\n            tnsr::I<double, 1, Frame::BlockLogical>{0.5}, time,\n            functions_of_time)) ==\n        tnsr::I<double, 1, Frame::Inertial>{-2.0 + 1.15 * -2.0});\n  CHECK(std::get<3>(composed_map_1d.coords_frame_velocity_jacobians(\n            tnsr::I<DataVector, 1, Frame::BlockLogical>{\n                DataVector{-0.5, 0.0, 0.5}},\n            time, functions_of_time)) ==\n        tnsr::I<DataVector, 1, Frame::Inertial>{3_st, -2.0 + 1.15 * -2.0});\n\n  const auto composed_map_2d =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          trans_map_2d{\"trans_2d\"},\n          affine_map_2d{affine_map{-1.0, 1.0, 0.0, 2.3},\n                        affine_map{-1.0, 1.0, 1.0, 7.2}},\n          trans_map_2d{\"trans_2d\"});\n  CHECK(std::get<3>(composed_map_2d.coords_frame_velocity_jacobians(\n            tnsr::I<double, 2, Frame::BlockLogical>{0.5}, time,\n            functions_of_time)) ==\n        tnsr::I<double, 2, Frame::Inertial>{\n            {{-2.0 + 1.15 * -2.0, 3.0 + 3.1 * 3.0}}});\n  CHECK(std::get<3>(composed_map_2d.coords_frame_velocity_jacobians(\n            tnsr::I<DataVector, 2, Frame::BlockLogical>{\n                DataVector{-0.5, 0.0, 0.5}},\n            time, functions_of_time)) ==\n        tnsr::I<DataVector, 2, Frame::Inertial>{\n            {{DataVector{3_st, -2.0 + 1.15 * -2.0},\n              DataVector{3_st, 3.0 + 3.1 * 3.0}}}});\n\n  const trans_map_3d translation3d{\"trans_3d\"};\n  const affine_map_3d affine3d{affine_map{-1.0, 1.0, 0.0, 2.3},\n                               affine_map{-1.0, 1.0, 1.0, 7.2},\n                               affine_map{-1.0, 1.0, -10.0, 7.2}};\n  const CoordinateMaps::TimeDependent::CubicScale<3> cubic_scale{\n      20.0, \"ExpansionA\", \"ExpansionB\"};\n  const auto composed_map_3d =\n      make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          translation3d, affine3d, cubic_scale);\n  {\n    const std::array<double, 3> source_pt{{0.5, 0.25, -0.34}};\n    const std::array<double, 3> source_pt_cubic_scale =\n        affine3d(translation3d(source_pt, time, functions_of_time));\n    const std::array<double, 3> velocity_affine_map_frame{\n        {1.15 * -2.0, 3.1 * 3.0, 8.6 * 4.5}};\n    const auto cubic_scale_jac =\n        cubic_scale.jacobian(source_pt_cubic_scale, time, functions_of_time);\n    const auto cubic_scale_velocity = cubic_scale.frame_velocity(\n        source_pt_cubic_scale, time, functions_of_time);\n    const tnsr::I<double, 3, Frame::Inertial> expected_velocity{\n        {{cubic_scale_velocity[0] +\n              cubic_scale_jac.get(0, 0) * velocity_affine_map_frame[0] +\n              cubic_scale_jac.get(0, 1) * velocity_affine_map_frame[1] +\n              cubic_scale_jac.get(0, 2) * velocity_affine_map_frame[2],\n          cubic_scale_velocity[1] +\n              cubic_scale_jac.get(1, 0) * velocity_affine_map_frame[0] +\n              cubic_scale_jac.get(1, 1) * velocity_affine_map_frame[1] +\n              cubic_scale_jac.get(1, 2) * velocity_affine_map_frame[2],\n          cubic_scale_velocity[2] +\n              cubic_scale_jac.get(2, 0) * velocity_affine_map_frame[0] +\n              cubic_scale_jac.get(2, 1) * velocity_affine_map_frame[1] +\n              cubic_scale_jac.get(2, 2) * velocity_affine_map_frame[2]}}};\n\n    CHECK_ITERABLE_APPROX(\n        (std::get<3>(composed_map_3d.coords_frame_velocity_jacobians(\n            tnsr::I<double, 3, Frame::BlockLogical>{source_pt}, time,\n            functions_of_time))),\n        expected_velocity);\n  }\n  {\n    MAKE_GENERATOR(generator);\n    std::uniform_real_distribution<> dist(-1.0, 1.0);\n    const auto source_pt = make_with_random_values<std::array<DataVector, 3>>(\n        make_not_null(&generator), make_not_null(&dist), DataVector{5});\n    const std::array<DataVector, 3> source_pt_cubic_scale =\n        affine3d(translation3d(source_pt, time, functions_of_time));\n    const std::array<DataVector, 3> velocity_affine_map_frame{\n        {DataVector{5, 1.15} * -2.0, DataVector{5, 3.1} * 3.0,\n         DataVector{5, 8.6} * 4.5}};\n    const auto cubic_scale_jac =\n        cubic_scale.jacobian(source_pt_cubic_scale, time, functions_of_time);\n    const auto cubic_scale_velocity = cubic_scale.frame_velocity(\n        source_pt_cubic_scale, time, functions_of_time);\n    const tnsr::I<DataVector, 3, Frame::Inertial> expected_velocity{\n        {{cubic_scale_velocity[0] +\n              cubic_scale_jac.get(0, 0) * velocity_affine_map_frame[0] +\n              cubic_scale_jac.get(0, 1) * velocity_affine_map_frame[1] +\n              cubic_scale_jac.get(0, 2) * velocity_affine_map_frame[2],\n          cubic_scale_velocity[1] +\n              cubic_scale_jac.get(1, 0) * velocity_affine_map_frame[0] +\n              cubic_scale_jac.get(1, 1) * velocity_affine_map_frame[1] +\n              cubic_scale_jac.get(1, 2) * velocity_affine_map_frame[2],\n          cubic_scale_velocity[2] +\n              cubic_scale_jac.get(2, 0) * velocity_affine_map_frame[0] +\n              cubic_scale_jac.get(2, 1) * velocity_affine_map_frame[1] +\n              cubic_scale_jac.get(2, 2) * velocity_affine_map_frame[2]}}};\n    CHECK(std::get<3>(composed_map_3d.coords_frame_velocity_jacobians(\n              tnsr::I<DataVector, 3, Frame::BlockLogical>{source_pt}, time,\n              functions_of_time)) == expected_velocity);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMap\", \"[Domain][Unit]\") {\n  test_single_coordinate_map();\n  test_coordinate_map_with_affine_map();\n  test_coordinate_map_with_rotation_map();\n  test_coordinate_map_with_rotation_map_datavector();\n  test_coordinate_map_with_rotation_wedge();\n  test_make_vector_coordinate_map_base();\n  test_coordinate_maps_are_identity();\n  test_time_dependent_map();\n  #ifdef SPECTRE_AUTODIFF\n  test_time_dependent_map_for_hessian();\n  #endif\n  test_push_back();\n  test_jacobian_is_time_dependent();\n  test_coords_frame_velocity_jacobians();\n  static_assert(CoordinateMap_detail::has_combined_coords_frame_velocity_jacs_v<\n                3, domain::CoordinateMaps::TimeDependent::Shape>);\n  static_assert(\n      not CoordinateMap_detail::has_combined_coords_frame_velocity_jacs_v<\n          3, domain::CoordinateMaps::TimeDependent::Translation<3>>);\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_CylindricalEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <gsl/gsl_poly.h>\n#include <optional>\n#include <random>\n\n#include \"Domain/CoordinateMaps/CylindricalEndcap.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n\nnamespace domain {\nnamespace {\n\nvoid test_cylindrical_endcap_sphere_two_small() {\n  // Here sphere_two is contained in sphere_one.\n  INFO(\"CylindricalEndcapSphereTwoSmall\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose some random centers for sphere_one and sphere_two\n  const std::array<double, 3> center_one = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_one);\n  const std::array<double, 3> center_two = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_two);\n\n  const double dist_between_spheres =\n      sqrt(square(center_two[0] - center_one[0]) +\n           square(center_two[1] - center_one[1]) +\n           square(center_two[2] - center_one[2]));\n\n  // First pick the radius of the larger sphere to be large enough\n  // that the centers of both spheres are reasonably well inside.\n  // Also, if dist_between_spheres is really small, then make radius_one\n  // larger.\n  // Note that choosing 7.0 means that center_two[2]-center_one[2] is\n  // less than radius_one/7, so that center_two[2] will be less than\n  // z_plane below.\n  const double radius_one =\n      7.0 * (unit_dis(gen) + 1.0) * std::max(dist_between_spheres, 0.05);\n  CAPTURE(radius_one);\n\n  // Make sure z_plane intersects sphere_one on the +z side of the\n  // center. We don't allow the plane to be displaced by less than 15%\n  // or more than 95% of the radius.\n  const double z_plane =\n      center_one[2] + (0.15 + 0.8 * unit_dis(gen)) * radius_one;\n  CAPTURE(z_plane);\n\n  // Choose radius_two.\n  const double radius_two = [&radius_one, &dist_between_spheres, &unit_dis,\n                             &gen]() {\n    // Choose radius_two to be small enough that the space between the\n    // two spheres is not too cramped. If dist_between_spheres is too\n    // small, then limit it as we did when computing radius_one.\n    const double max_radius_two =\n        0.85 * (radius_one - std::max(dist_between_spheres, 0.05));\n\n    // Choose radius_two to be not too small compared to radius_one.\n    const double min_radius_two = 0.1 * radius_one;\n\n    return min_radius_two + unit_dis(gen) * (max_radius_two - min_radius_two);\n  }();\n  CAPTURE(radius_two);\n\n  // Now choose projection point to be very near the center of sphere_two.\n  // And make sure that projection point is less than z_plane.\n  const auto proj_center =\n      [&center_two, &radius_two, &z_plane, &angle_dis, &unit_dis, &gen]() {\n        const double radius =\n            std::min(0.1 * radius_two, 0.999 * (z_plane - center_two[2])) *\n            unit_dis(gen);\n        const double theta = 0.5 * angle_dis(gen);\n        const double phi = angle_dis(gen);\n        return std::array<double, 3>{\n            center_two[0] + radius * sin(theta) * cos(phi),\n            center_two[1] + radius * sin(theta) * sin(phi),\n            center_two[2] + radius * cos(theta)};\n      }();\n  CAPTURE(proj_center);\n\n  const CoordinateMaps::CylindricalEndcap map(\n      center_one, center_two, proj_center, radius_one, radius_two, z_plane);\n  test_suite_for_map_on_cylinder(map, 0.0, 1.0);\n}\n\nvoid test_cylindrical_endcap_sphere_two_large() {\n  // Here sphere_one is contained in sphere_two.\n  INFO(\"CylindricalEndcapSphereTwoLarge\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose some random centers for sphere_one and sphere_two\n  const std::array<double, 3> center_one = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_one);\n  const std::array<double, 3> center_two = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_two);\n\n  // Computes the maximum possible z-coordinate of the projection\n  // point, such that proj_center obeys the max_opening_angle\n  // restriction.  Consider a cone extending in the minus-z direction\n  // from the apex (center_one[0],center_one[1],max_proj_center_z),\n  // with an opening angle of max_opening_angle.  proj_center must lie\n  // inside that cone.\n  auto compute_max_proj_center_z =\n      [](double radius_one, double z_plane, double max_opening_angle,\n         const std::array<double, 3>& center_one_local) -> double {\n    const double cos_theta = (z_plane - center_one_local[2]) / radius_one;\n    return z_plane -\n           radius_one * sqrt(1.0 - square(cos_theta)) / tan(max_opening_angle);\n  };\n\n  const double dist_between_spheres =\n      sqrt(square(center_two[0] - center_one[0]) +\n           square(center_two[1] - center_one[1]) +\n           square(center_two[2] - center_one[2]));\n\n  // The choice of the maximum opening angle must agree with the\n  // value chosen in the sanity checks in CylindricalEndcap.cpp, or\n  // else the test will test configurations that do not correspond to\n  // those sanity checks.\n  // This opening angle is the maximum angle between the z-axis and\n  // the line segment that connects the projection point and any\n  // point on the circle where the z-plane intersects sphere_one.\n  const double max_opening_angle = M_PI / 3.0;\n\n  // Pick the radius of the smaller sphere to be not too small\n  // compared to the distance between the centers.  We choose\n  // 0.4*dist_between_spheres so that we don't have a microscopic\n  // sphere (without that term the radius could be arbitrarily small),\n  // but still allow the radius to be smaller than the distance\n  // between the centers.\n  const double radius_one = 0.4 * dist_between_spheres + unit_dis(gen);\n  CAPTURE(radius_one);\n\n  // Make sure z_plane intersects sphere_one on the +z side of the\n  // center. We don't allow the plane to be displaced by less than 15%\n  // or more than 95% of the radius.\n  const double z_plane =\n      center_one[2] + (0.15 + 0.8 * unit_dis(gen)) * radius_one;\n  CAPTURE(z_plane);\n\n  // Consider a cone formed by taking the intersection of z_plane and\n  // sphere_one (this is a circle called circle_one) and connecting it to\n  // center_one. Compute theta, where 2*theta is the opening angle of this\n  // cone. Call this cone 'cone_one'.\n  // cone_one is the cone centered at C_1 in the \"Allowed region for P\"\n  // figure in the doxygen documentation for ClyndricalEndcap.hpp.\n  const double cos_theta = (z_plane - center_one[2]) / radius_one;\n\n  // We will construct two new cones. The first we will call\n  // cone_regular. It is constructed so that cone_regular and\n  // cone_one intersect each other on circle_one at right angles.\n  // Cone_regular opens in the -z direction with opening angle\n  // 2*(pi/2-theta).\n  // cone_regular is the cone centered at S in the \"Allowed region for P\"\n  // figure in the doxygen documentation for ClyndricalEndcap.hpp.\n  // The apex of cone_regular is cone_regular_apex,\n  // defined as follows:\n  const std::array<double, 3> cone_regular_apex = {\n      center_one[0], center_one[1], center_one[2] + radius_one / cos_theta};\n  // A necessary condition for the map being\n  // invertible is that proj_center must lie either inside\n  // cone_regular (which opens in the -z direction and intersects\n  // cone_one), or proj_center must lie inside the reflection of\n  // cone_regular (which opens in the +z direction).  If proj_center\n  // is inside cone_regular (i.e. the one that opens in the -z\n  // direction), an additional condition for the map being\n  // invertible is that proj_center cannot lie between sphere_one\n  // and the center of cone_regular.\n\n  // The second cone, cone_opening, is the cone extending in the\n  // minus-z direction from the apex\n  // (center_one[0],center_one[1],max_proj_center_z).  proj_center\n  // must lie inside that cone.  Note that if max_opening_angle >\n  // pi/4 and if z_plane > center_one[2] then the apex of\n  // cone_opening is inside sphere_one.\n  const double max_proj_center_z = compute_max_proj_center_z(\n      radius_one, z_plane, max_opening_angle, center_one);\n\n  // Now that we have two cones, cone_regular and cone_opening.  We\n  // will choose proj_center so that it lies inside of both cones,\n  // and that it lies inside of sphere_two.\n  // [Note asin(cos_theta) = pi/2-theta]\n  const double cone_regular_opening_angle = asin(cos_theta);\n\n  // Because we have two cones, we do the random choice of proj_center\n  // and radius_two by rejection.  That is, we choose a proj_center\n  // inside cone_opening, and if this point is outside (or sufficiently near)\n  // cone_regular we reject proj_center and try again.\n  double radius_two = std::numeric_limits<double>::signaling_NaN();\n  std::array<double, 3> proj_center{z_plane, z_plane, z_plane};\n  bool found_proj_center = false;\n  while (not found_proj_center) {\n    // We typically expect success on the first iteration or the first\n    // few iterations if we get unlucky.  Let V_O be the volume of\n    // cone_opening that is contained inside sphere_two.  If\n    // cone_regular (modulo a small buffer angle, the 0.75 below)\n    // contains V_O, then success is certain the first\n    // time. Otherwise, the probability of success is roughly V_R/V_O,\n    // where V_R is the volume of the portion of cone_regular (modulo\n    // a small buffer angle, the 0.75 below) that is contained inside\n    // V_O.\n\n    // Pick radius of the larger sphere to enclose the smaller one\n    // (and not have too large a radius).\n    // The 1.01 is there so that the spheres do not almost touch, which\n    // causes problems with roundoff.\n    radius_two = (unit_dis(gen) + 1.01) * (radius_one + dist_between_spheres);\n\n    // Choose some smaller cone inside of cone_opening.\n    // We will place proj_center on this smaller cone.\n    const double beta = unit_dis(gen) * max_opening_angle;\n\n    // Choose an azimuthal coordinate for the point on the cone.\n    const double phi = angle_dis(gen);\n\n    // Choose the distance proj_dist between the apex of the cone and\n    // proj_center. proj_center must be inside sphere_two.  So\n    // determine the distance proj_dist_max at which the point intersects\n    // sphere_two. This radius is the solution of a quadratic equation\n    // a proj_dist_max^2 + b proj_dist_max + c = 0\n    const double a = 1.0;\n    const double b =\n        2.0 * (-(max_proj_center_z - center_two[2]) * cos(beta) +\n               (center_one[0] - center_two[0]) * sin(beta) * cos(phi) +\n               (center_one[1] - center_two[1]) * sin(beta) * sin(phi));\n    const double c = square(max_proj_center_z - center_two[2]) +\n                     square(center_one[0] - center_two[0]) +\n                     square(center_one[1] - center_two[1]) - square(radius_two);\n    ASSERT(c < 0.0,\n           \"max_proj_center_z is too negative. The apex \"\n           \"is not inside sphere_two\");\n\n    // Should be two real roots, one positive and one\n    // negative. Choose the positive one.\n    const double proj_dist_max = positive_root(a, b, c);\n\n    // Now get the distance along the cone\n    const double proj_dist = unit_dis(gen) * proj_dist_max;\n\n    // Now for the rejection.  We reject the point if it is outside\n    // cone_opening, and we also reject the point if it is too close\n    // to cone_opening (using a factor 0.75 that is the same as the\n    // factor in the ASSERTS in the CylindricalEndcap constructor).\n    const double alpha = 0.75 * cone_regular_opening_angle;\n\n    // If beta <= alpha, then there is no max distance, i.e.\n    // all distances are ok.\n    // If beta < alpha, then the max distance is given by\n    // (cone_regular_apex[2]-max_proj_center_z)*sin(alpha)/ sin(beta-alpha)\n    if (beta <= alpha or\n        proj_dist < (cone_regular_apex[2] - max_proj_center_z) * sin(alpha) /\n                        sin(beta - alpha)) {\n      // accept.\n      proj_center = std::array<double, 3>{\n          center_one[0] + proj_dist * sin(beta) * cos(phi),\n          center_one[1] + proj_dist * sin(beta) * sin(phi),\n          max_proj_center_z - proj_dist * cos(beta)};\n      found_proj_center = true;\n    }\n  }\n  CAPTURE(radius_two);\n  CAPTURE(proj_center);\n\n  const CoordinateMaps::CylindricalEndcap map(\n      center_one, center_two, proj_center, radius_one, radius_two, z_plane);\n  test_suite_for_map_on_cylinder(map, 0.0, 1.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.CylindricalEndcap\",\n                  \"[Domain][Unit]\") {\n  test_cylindrical_endcap_sphere_two_large();\n  test_cylindrical_endcap_sphere_two_small();\n  CHECK(not CoordinateMaps::CylindricalEndcap{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_CylindricalFlatEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <gsl/gsl_poly.h>\n#include <random>\n\n#include \"Domain/CoordinateMaps/CylindricalFlatEndcap.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_cylindrical_flat_endcap() {\n  INFO(\"CylindricalFlatEndcap\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose random radii for sphere_two and circle_one, and ensure\n  // that the two radii don't have a ratio of more than 10:1.\n  const double radius_two = 0.1 + 0.9 * unit_dis(gen);\n  CAPTURE(radius_two);\n  const double radius_one = 0.1 + 0.9 * unit_dis(gen);\n  CAPTURE(radius_one);\n\n  // Choose a random center for sphere_two.\n  const std::array<double, 3> center_two = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_two);\n\n  // Choose a random center for the circle, making sure the z-coordinate\n  // is outside sphere_two and below it by at least 5% of radius_two\n  // and at most 5 times radius_two.\n  // For the x and y coordinates, make sure that they are not displaced by\n  // more than radius_one + radius_two with respect to sphere_two.\n  const std::array<double, 3> center_one = {\n      center_two[0] + (radius_one + radius_two) * interval_dis(gen),\n      center_two[1] + (radius_one + radius_two) * interval_dis(gen),\n      center_two[2] - radius_two -\n          5.0 * radius_two * (0.01 + 0.99 * unit_dis(gen))};\n  CAPTURE(center_one);\n\n  // Pick proj_center inside sphere_two, but less than 95% from the\n  // surface.\n  const std::array<double, 3> proj_center = [&center_two, &radius_two, &gen,\n                                             &unit_dis, &interval_dis,\n                                             &angle_dis]() {\n    const double phi = angle_dis(gen);\n    const double cos_theta = interval_dis(gen);\n    const double sin_theta = sqrt(1.0 - square(cos_theta));\n    const double r = radius_two * 0.95 * unit_dis(gen);\n    return std::array<double, 3>{center_two[0] + r * sin_theta * cos(phi),\n                                 center_two[1] + r * sin_theta * sin(phi),\n                                 center_two[2] + r * cos_theta};\n  }();\n  CAPTURE(proj_center);\n\n  const CoordinateMaps::CylindricalFlatEndcap map(\n      center_one, center_two, proj_center, radius_one, radius_two);\n  test_suite_for_map_on_cylinder(map, 0.0, 1.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.CylindricalFlatEndcap\",\n                  \"[Domain][Unit]\") {\n  test_cylindrical_flat_endcap();\n  CHECK(not CoordinateMaps::CylindricalFlatEndcap{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_CylindricalFlatSide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <gsl/gsl_poly.h>\n#include <random>\n\n#include \"Domain/CoordinateMaps/CylindricalFlatSide.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_cylindrical_flat_side() {\n  INFO(\"CylindricalFlatSide\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose a random center and radius for sphere_two\n  const std::array<double, 3> center_two = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_two);\n  const double radius_two = 0.3 + unit_dis(gen);\n  CAPTURE(radius_two);\n\n  // Choose a random center for the annulus, making sure the\n  // z-coordinate is outside sphere_two and at least 5 percent below\n  // it.  Also make sure that the center of the annulus is not offset\n  // in x-y farther than 90% of the radius of sphere_two.\n  const std::array<double, 3> center_one = [&radius_two, &center_two, &unit_dis,\n                                            &angle_dis, &gen]() {\n    const double rho = unit_dis(gen) * 0.9 *  radius_two;\n    const double phi = angle_dis(gen);\n    const double x = center_two[0] + rho * cos(phi);\n    const double y = center_two[1] + rho * sin(phi);\n    const double z = center_two[2] - radius_two * (1.05 + unit_dis(gen));\n    return std::array<double, 3>{{x, y, z}};\n  }();\n  CAPTURE(center_one);\n\n  // Pick proj_center inside sphere_two.\n  // Don't let proj_center be too close to the edge of the sphere.\n  const std::array<double, 3> proj_center = [&center_two, &radius_two, &gen,\n                                             &unit_dis, &interval_dis,\n                                             &angle_dis]() {\n    const double phi = angle_dis(gen);\n    const double cos_theta = interval_dis(gen);\n    const double sin_theta = sqrt(1.0 - square(cos_theta));\n    const double r = radius_two * 0.85 * unit_dis(gen);\n    return std::array<double, 3>{center_two[0] + r * sin_theta * cos(phi),\n                                 center_two[1] + r * sin_theta * sin(phi),\n                                 center_two[2] + r * cos_theta};\n  }();\n  CAPTURE(proj_center);\n\n  const double dist_annulus_proj = sqrt(square(center_one[0] - proj_center[0]) +\n                                        square(center_one[1] - proj_center[1]) +\n                                        square(center_one[2] - proj_center[2]));\n\n  // Pick outer radius of annulus, but not too small.\n  // Smallness is decided by making sure angle subtended by annulus with\n  // respect to projection center is greater than 0.1 radians.\n  const double outer_radius = dist_annulus_proj * 0.1 + unit_dis(gen);\n  CAPTURE(outer_radius);\n\n  // Pick inner radius of annulus.\n  // Don't make the annulus too thin,\n  // and don't make the inner radius too small.\n  const double min_inner_radius_fac =\n      std::max(0.1, dist_annulus_proj * 0.03 / outer_radius);\n  const double max_inner_radius_fac = 0.9 - min_inner_radius_fac;\n  CHECK(max_inner_radius_fac>0.0);\n  const double inner_radius =\n      outer_radius *\n      (min_inner_radius_fac + max_inner_radius_fac * unit_dis(gen));\n  CAPTURE(inner_radius);\n\n  const CoordinateMaps::CylindricalFlatSide map(center_one, center_two,\n                                                proj_center, inner_radius,\n                                                outer_radius, radius_two);\n  test_suite_for_map_on_cylinder(map, 1.0, 2.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.CylindricalFlatSide\",\n                  \"[Domain][Unit]\") {\n  test_cylindrical_flat_side();\n  CHECK(not CoordinateMaps::CylindricalFlatSide{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_CylindricalSide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <gsl/gsl_poly.h>\n#include <random>\n\n#include \"Domain/CoordinateMaps/CylindricalSide.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n\nnamespace domain {\nnamespace {\n// If projection_point_near_z_plane is true, then the projection\n// point is placed near or on one of the z_planes, and near the center\n// of that plane.  Otherwise, the projection point is placed somewhere\n// away from the z_planes.\n//\n// Also, for projection_point_near_z_plane=true, the allowed value of\n// sphere_two is much larger than for projection_point_near_z_plane=false;\n// this reflects our usage of the maps.\nvoid test_cylindrical_side_sphere_two_encloses_sphere_one(\n    const bool projection_point_near_z_plane) {\n  INFO(\"CylindricalSideSphereTwoEnclosesSphereOne\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose some random centers for sphere_one and sphere_two\n  const std::array<double, 3> center_one = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_one);\n\n  const auto center_two = [&projection_point_near_z_plane, &center_one,\n                           &interval_dis, &gen]() -> std::array<double, 3> {\n    if (projection_point_near_z_plane) {\n      return {center_one[0], center_one[1], interval_dis(gen)};\n    } else {\n      return {interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n    }\n  }();\n  CAPTURE(center_two);\n  const double dist_between_spheres =\n      sqrt(square(center_two[0] - center_one[0]) +\n           square(center_two[1] - center_one[1]) +\n           square(center_two[2] - center_one[2]));\n\n  // Pick radius of sphere_one not too small compared to the distance\n  // between the centers.\n  const double radius_one = projection_point_near_z_plane\n                                ? dist_between_spheres + 0.1 + unit_dis(gen)\n                                : 0.3 * dist_between_spheres + unit_dis(gen);\n  CAPTURE(radius_one);\n\n  // Now construct sphere_two which we make sure encloses sphere_one,\n  // but doesn't have too large of a radius.\n  const double radius_two =\n      projection_point_near_z_plane\n          ? (3.0 * unit_dis(gen) + 1.01) * (radius_one + dist_between_spheres)\n          : (unit_dis(gen) + 1.01) * (radius_one + dist_between_spheres);\n  CAPTURE(radius_two);\n\n  const std::array<double, 2> z_planes =\n      [&gen, &unit_dis, &center_one, &radius_one,\n       &projection_point_near_z_plane]() {\n        if (projection_point_near_z_plane) {\n          // Make sure each z_plane intersects sphere_one in two locations;\n          // to do this, ensure that each plane is no closer than 25% of the\n          // radius to the min/max z-extents of sphere_one and no closer than\n          // 2% of the radius to the center.\n          const double z_plane_1 =\n              center_one[2] - (0.02 + 0.73 * unit_dis(gen)) * radius_one;\n          const double z_plane_2 =\n              center_one[2] + (0.02 + 0.73 * unit_dis(gen)) * radius_one;\n          return std::array<double, 2>{z_plane_1, z_plane_2};\n        } else {\n          // Make sure each z_plane intersects sphere_one in two locations;\n          // to do this, ensure that each plane is no closer than 10% of the\n          // radius to the min/max z-extents of sphere_one and no closer than\n          // 2% of the radius to the center.\n          const double z_plane_1 =\n              center_one[2] - (0.02 + 0.88 * unit_dis(gen)) * radius_one;\n          const double z_plane_2 =\n              center_one[2] + (0.02 + 0.88 * unit_dis(gen)) * radius_one;\n          return std::array<double, 2>{z_plane_1, z_plane_2};\n        }\n      }();\n  CAPTURE(z_planes);\n\n  // Keep proj_center inside sphere_1 and between (or on) the z_planes.\n  const std::array<double, 3> proj_center =\n      [&z_planes, &center_one, &radius_one, &gen, &unit_dis, &angle_dis,\n       &projection_point_near_z_plane]() {\n        // z is z coordinate of projection point.\n        // rho_max_factor is rho_max/rho_sphere_one (see below for\n        // definition of rho_sphere_one).\n        const auto& [z, rho_max_factor] =\n            [&z_planes, &projection_point_near_z_plane, &unit_dis,\n             &gen]() -> std::array<double, 2> {\n          if (projection_point_near_z_plane) {\n            // Place z exactly at one of the z-planes.\n            // Choose which plane randomly, with probability 1/2.\n            const double z_local =\n                unit_dis(gen) < 0.5 ? z_planes[0] : z_planes[1];\n            // Also, set rho to something very small.\n            return {z_local, 1.e-25};\n          } else {\n            // z is not near one of the z-planes.\n            // Note that z <= z_min but z < z_max.\n            const double z_min =\n                z_planes[0] + 0.1 * (z_planes[1] - z_planes[0]);\n            const double z_max =\n                z_planes[1] - 0.1 * (z_planes[1] - z_planes[0]);\n            const double z_local = z_min + (z_max - z_min) * unit_dis(gen);\n            return {z_local, 0.85};\n          }\n        }();\n        // rho_sphere_one is the radius of the circle formed by the\n        // intersection of sphere_one and a plane at coordinate z.\n        const double rho_sphere_one =\n            radius_one * sqrt(1.0 - square((z - center_one[2]) / radius_one));\n        const double rho_max = rho_sphere_one * rho_max_factor;\n        const double rho = unit_dis(gen) * rho_max;\n        const double phi = angle_dis(gen);\n        return std::array<double, 3>{center_one[0] + rho * cos(phi),\n                                     center_one[1] + rho * sin(phi), z};\n      }();\n  CAPTURE(proj_center);\n\n  const CoordinateMaps::CylindricalSide map(center_one, center_two, proj_center,\n                                            radius_one, radius_two, z_planes[0],\n                                            z_planes[1]);\n  test_suite_for_map_on_cylinder(map, 1.0, 2.0);\n}\n\nvoid test_cylindrical_side_sphere_one_encloses_sphere_two() {\n  INFO(\"CylindricalSideSphereOneEnclosesSphereTwo\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose some random center for sphere_one\n  const std::array<double, 3> center_one = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_one);\n\n  // Pick radius of sphere_one\n  const double radius_one = 1.5 * (1.0 + unit_dis(gen));\n  CAPTURE(radius_one);\n\n  // Make sure each z_plane intersects sphere_one in two locations;\n  // to do this, ensure that each plane is no closer than 8% of the\n  // radius to the min/max z-extents of sphere_one.\n  // Also make sure that each z_plane is not more than 20% away from\n  // the center of sphere_one (because we need to fit sphere_two between\n  // the planes).\n  const std::array<double, 2> z_planes = [&gen, &unit_dis, &center_one,\n                                          &radius_one]() {\n    const double z_plane_1 =\n        center_one[2] - (0.2 + 0.72 * unit_dis(gen)) * radius_one;\n    const double z_plane_2 =\n        center_one[2] + (0.2 + 0.72 * unit_dis(gen)) * radius_one;\n    return std::array<double, 2>{z_plane_1, z_plane_2};\n  }();\n  CAPTURE(z_planes);\n\n  // Choose sphere_two to be fully contained inside the z_planes,\n  // and not too small.\n  const double radius_two =\n      (z_planes[1] - z_planes[0]) * 0.25 * (1.0 + unit_dis(gen));\n  CAPTURE(radius_two);\n\n  // Choose center_two so that sphere_two is contained inside sphere_one\n  // and inside the z_planes.\n  const std::array<double, 3> center_two = [&z_planes, &radius_one, &radius_two,\n                                            &center_one, &angle_dis, &unit_dis,\n                                            &gen]() {\n    const double center_two_z_min = z_planes[0] + radius_two;\n    const double center_two_z_max = z_planes[1] - radius_two;\n    const double center_two_z =\n        center_two_z_min +\n        (center_two_z_max - center_two_z_min) * unit_dis(gen);\n    // Choose 0.85 so that there is some space between sphere_one and\n    // sphere_two.\n    const double rho = 0.85 * unit_dis(gen) *\n                       sqrt(square(radius_one - radius_two) -\n                            square(center_one[2] - center_two_z));\n    const double phi = angle_dis(gen);\n    return std::array<double, 3>{center_one[0] + rho * cos(phi),\n                                 center_one[1] + rho * sin(phi), center_two_z};\n  }();\n  CAPTURE(center_two);\n\n  // Keep proj_center inside sphere_two.\n  const std::array<double, 3> proj_center = [&center_two, &radius_two, &gen,\n                                             &unit_dis, &angle_dis]() {\n    // choose 0.9 so that proj_center is not on edge of sphere_two.\n    const double r = 0.9 * radius_two * unit_dis(gen);\n    const double theta = 2.0 * angle_dis(gen);\n    const double phi = angle_dis(gen);\n    return std::array<double, 3>{center_two[0] + r * sin(theta) * cos(phi),\n                                 center_two[1] + r * sin(theta) * sin(phi),\n                                 center_two[2] + r * cos(theta)};\n  }();\n  CAPTURE(proj_center);\n\n  const CoordinateMaps::CylindricalSide map(center_one, center_two, proj_center,\n                                            radius_one, radius_two, z_planes[0],\n                                            z_planes[1]);\n  test_suite_for_map_on_cylinder(map, 1.0, 2.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.CylindricalSide\",\n                  \"[Domain][Unit]\") {\n  test_cylindrical_side_sphere_two_encloses_sphere_one(true);\n  test_cylindrical_side_sphere_two_encloses_sphere_one(false);\n  test_cylindrical_side_sphere_one_encloses_sphere_two();\n  CHECK(not CoordinateMaps::CylindricalSide{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_DiscreteRotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_1d() {\n  INFO(\"1d\");\n  const CoordinateMaps::DiscreteRotation<1> identity_map1d{};\n  const CoordinateMaps::DiscreteRotation<1> rotation_nx{OrientationMap<1>{\n      std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}}}};\n\n  check_if_maps_are_equal(\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          CoordinateMaps::Identity<1>{}),\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(identity_map1d));\n  check_if_maps_are_equal(\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          CoordinateMaps::Affine{-1.0, 1.0, 1.0, -1.0}),\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(rotation_nx));\n\n  const std::array<double, 1> point_px{{0.5}};\n  const std::array<double, 1> point_nx{{-0.5}};\n  CHECK(rotation_nx(point_px) == point_nx);\n}\n\nvoid test_2d() {\n  INFO(\"2d\");\n  const CoordinateMaps::DiscreteRotation<2> identity_map2d{};\n  const CoordinateMaps::DiscreteRotation<2> rotation_ny_px{\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}}};\n\n  check_if_maps_are_equal(\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          CoordinateMaps::Identity<2>{}),\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(identity_map2d));\n  check_if_maps_are_equal(\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          CoordinateMaps::Rotation<2>{M_PI_2}),\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(rotation_ny_px));\n\n  const std::array<double, 2> point_px_py{{0.2, 0.5}};\n  const std::array<double, 2> point_ny_px{{-0.5, 0.2}};\n  CHECK(rotation_ny_px(point_px_py) == point_ny_px);\n}\n\nvoid test_3d() {\n  INFO(\"3d\");\n  const CoordinateMaps::DiscreteRotation<3> identity_map3d{};\n  const CoordinateMaps::DiscreteRotation<3> rotation_ny_nz_px{\n      OrientationMap<3>{std::array<Direction<3>, 3>{\n          {Direction<3>::lower_eta(), Direction<3>::lower_zeta(),\n           Direction<3>::upper_xi()}}}};\n\n  using Identity1D = CoordinateMaps::Identity<1>;\n  using Identity2D = CoordinateMaps::Identity<2>;\n  using Identity3D = CoordinateMaps::ProductOf2Maps<Identity1D, Identity2D>;\n  const Identity1D id1d{};\n  const Identity2D id2d{};\n  const Identity3D id3d{id1d, id2d};\n\n  check_if_maps_are_equal(\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(id3d),\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(identity_map3d));\n  check_if_maps_are_equal(\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          CoordinateMaps::Rotation<3>{M_PI_2, -M_PI_2, 0.0}),\n      make_coordinate_map<Frame::BlockLogical, Frame::Grid>(rotation_ny_nz_px));\n\n  const std::array<double, 3> point_px_py_pz{{0.2, 0.5, 0.3}};\n  const std::array<double, 3> point_ny_nz_px{{-0.5, -0.3, 0.2}};\n  CHECK(rotation_ny_nz_px(point_px_py_pz) == point_ny_nz_px);\n}\n\nvoid test_with_orientation() {\n  INFO(\"With orientation\");\n  for (OrientationMapIterator<2> map_i{}; map_i; ++map_i) {\n    if (get(determinant(discrete_rotation_jacobian(*map_i))) < 0.0) {\n      continue;\n    }\n    const CoordinateMaps::DiscreteRotation<2> coord_map{map_i()};\n    test_suite_for_map_on_unit_cube(coord_map);\n  }\n  for (OrientationMapIterator<3> map_i{}; map_i; ++map_i) {\n    if (get(determinant(discrete_rotation_jacobian(*map_i))) < 0.0) {\n      continue;\n    }\n    const CoordinateMaps::DiscreteRotation<3> coord_map{map_i()};\n    test_suite_for_map_on_unit_cube(coord_map);\n  }\n}\n\nvoid test_is_identity() {\n  INFO(\"Is identity\");\n  check_if_map_is_identity(\n      CoordinateMaps::DiscreteRotation<1>{OrientationMap<1>::create_aligned()});\n  CHECK(not CoordinateMaps::DiscreteRotation<1>{\n      OrientationMap<1>{\n          std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}}}}\n                .is_identity());\n\n  check_if_map_is_identity(\n      CoordinateMaps::DiscreteRotation<2>{OrientationMap<2>::create_aligned()});\n  CHECK(not CoordinateMaps::DiscreteRotation<2>{\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}}}\n                .is_identity());\n\n  check_if_map_is_identity(\n      CoordinateMaps::DiscreteRotation<3>{OrientationMap<3>::create_aligned()});\n  CHECK(not CoordinateMaps::DiscreteRotation<3>{\n      OrientationMap<3>{std::array<Direction<3>, 3>{\n          {Direction<3>::lower_eta(), Direction<3>::lower_zeta(),\n           Direction<3>::upper_xi()}}}}\n                .is_identity());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.DiscreteRotation\",\n                  \"[Domain][Unit]\") {\n  test_1d();\n  test_2d();\n  test_3d();\n  test_with_orientation();\n  test_is_identity();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Distribution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace domain::CoordinateMaps {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Distribution\", \"[Domain][Unit]\") {\n  CHECK(get_output(Distribution::Linear) == \"Linear\");\n  CHECK(get_output(Distribution::Equiangular) == \"Equiangular\");\n  CHECK(get_output(Distribution::Logarithmic) == \"Logarithmic\");\n  CHECK(get_output(Distribution::Inverse) == \"Inverse\");\n  CHECK(get_output(Distribution::Projective) == \"Projective\");\n  CHECK(TestHelpers::test_creation<Distribution>(\"Linear\") ==\n        Distribution::Linear);\n  CHECK(TestHelpers::test_creation<Distribution>(\"Equiangular\") ==\n        Distribution::Equiangular);\n  CHECK(TestHelpers::test_creation<Distribution>(\"Logarithmic\") ==\n        Distribution::Logarithmic);\n  CHECK(TestHelpers::test_creation<Distribution>(\"Inverse\") ==\n        Distribution::Inverse);\n  CHECK(TestHelpers::test_creation<Distribution>(\"Projective\") ==\n        Distribution::Projective);\n\n  CHECK(TestHelpers::test_creation<DistributionAndSingularityPosition>(\n            \"Linear\") == DistributionAndSingularityPosition{});\n  CHECK(TestHelpers::test_creation<DistributionAndSingularityPosition>(\n            \"Linear\") ==\n        DistributionAndSingularityPosition{Distribution::Linear, std::nullopt});\n  CHECK(TestHelpers::test_creation<DistributionAndSingularityPosition>(\n            \"Equiangular\") ==\n        DistributionAndSingularityPosition{Distribution::Equiangular});\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<DistributionAndSingularityPosition>(\n          \"Logarithmic\"),\n      Catch::Matchers::ContainsSubstring(\n          \"The distribution 'Logarithmic' requires a singularity position.\"));\n  CHECK(TestHelpers::test_creation<DistributionAndSingularityPosition>(\n            \"Logarithmic:\\n\"\n            \"  SingularityPosition: 1.5\") ==\n        DistributionAndSingularityPosition{Distribution::Logarithmic, 1.5});\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<DistributionAndSingularityPosition>(\"Inverse\"),\n      Catch::Matchers::ContainsSubstring(\n          \"The distribution 'Inverse' requires a singularity position.\"));\n  CHECK(TestHelpers::test_creation<DistributionAndSingularityPosition>(\n            \"Inverse:\\n\"\n            \"  SingularityPosition: 2.3\") ==\n        DistributionAndSingularityPosition{Distribution::Inverse, 2.3});\n  CHECK(TestHelpers::test_creation<DistributionAndSingularityPosition>(\n            \"Projective\") ==\n        DistributionAndSingularityPosition{Distribution::Projective});\n}\n\n}  // namespace domain::CoordinateMaps\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_EquatorialCompression.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <memory>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_suite() {  // Set up random number generator\n  INFO(\"Suite\");\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> aspect_ratio_dis(0.1, 10);\n\n  const double aspect_ratio = aspect_ratio_dis(gen);\n  CAPTURE(aspect_ratio);\n  const CoordinateMaps::EquatorialCompression angular_compression_map(\n      aspect_ratio);\n  test_suite_for_map_on_unit_cube(angular_compression_map);\n}\n\nvoid test_radius() {\n  INFO(\"Radius\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-10, 10);\n  std::uniform_real_distribution<> aspect_ratio_dis(0.1, 10);\n  const double x = real_dis(gen);\n  CAPTURE(x);\n  const double y = real_dis(gen);\n  CAPTURE(y);\n  const double z = real_dis(gen);\n  CAPTURE(z);\n  const double aspect_ratio = aspect_ratio_dis(gen);\n  CAPTURE(aspect_ratio);\n  const std::array<double, 3> input_point{{x, y, z}};\n\n  for (const auto& index_polar_axis : {0_st, 1_st, 2_st}) {\n    const CoordinateMaps::EquatorialCompression angular_compression_map(\n        aspect_ratio, index_polar_axis);\n    const auto result_point = angular_compression_map(input_point);\n\n    // The EquatorialCompression map should preserve radial lengths.\n    CHECK(magnitude(result_point) == approx(magnitude(input_point)));\n\n    // It is easy to mix up \\alpha with one over \\alpha,\n    // so this comment serves as a reminder to future readers\n    // as to which direction the \\alpha should go in the\n    // CHECK statement.\n    //\n    // The aspect ratio is the ratio of the distance perpendicular to\n    // the polar axis to the distance along the polar axis for a given point.\n    // The parameter name `aspect_ratio` is used because points with\n    // tan \\theta = 1, which have an aspect ratio of one, get mapped to points\n    // with tan \\theta = \\alpha, which have an aspect ratio of `aspect_ratio`.\n    //\n    // The angular compression map is:\n    //  tan \\theta' = \\alpha tan\\theta.\n    // Then, if the argument provided to the EquatorialCompression\n    // map is, for example, 4, points on the undistorted sphere\n    // that have an angle tan \\theta = sqrt(x^2 + y^2)/z are\n    // mapped to points that have an angle\n    //  tan \\theta' = 4*sqrt(x^2+y^2)/z\n    //              = sqrt(x'^2 + y'^2)/z'.\n\n    const auto& planar_distance =\n        sqrt(square(gsl::at(input_point, (index_polar_axis + 1) % 3)) +\n             square(gsl::at(input_point, (index_polar_axis + 2) % 3)));\n    const auto& result_planar_distance =\n        sqrt(square(gsl::at(result_point, (index_polar_axis + 1) % 3)) +\n             square(gsl::at(result_point, (index_polar_axis + 2) % 3)));\n\n    CHECK(aspect_ratio * planar_distance /\n              gsl::at(input_point, index_polar_axis) ==\n          approx(result_planar_distance /\n                 gsl::at(result_point, index_polar_axis)));\n  }\n}\n\nvoid test_is_identity() {\n  INFO(\"Is identity\");\n  check_if_map_is_identity(CoordinateMaps::EquatorialCompression{1.0});\n  CHECK(not CoordinateMaps::EquatorialCompression{0.9}.is_identity());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.EquatorialCompression\",\n                  \"[Domain][Unit]\") {\n  test_suite();\n  test_radius();\n  test_is_identity();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH((CoordinateMaps::EquatorialCompression(-0.2)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The aspect_ratio must be greater than zero.\"));\n#endif\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Equiangular.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Equiangular\", \"[Domain][Unit]\") {\n  const double xA = -1.0;\n  const double xB = 2.0;\n  const double xa = -3.0;\n  const double xb = 2.0;\n\n  CoordinateMaps::Equiangular equiangular_map(xA, xB, xa, xb);\n\n  const double xi = 0.5 * (xA + xB);\n  const double x = xb * (xi - xA) / (xB - xA) + xa * (xB - xi) / (xB - xA);\n  const double r1 = 0.172899453;\n  const double r2 = -0.234256219;\n\n  const std::array<double, 1> point_A{{xA}};\n  const std::array<double, 1> point_B{{xB}};\n  const std::array<double, 1> point_a{{xa}};\n  const std::array<double, 1> point_b{{xb}};\n  const std::array<double, 1> point_xi{{xi}};\n  const std::array<double, 1> point_x{{x}};\n  const std::array<double, 1> point_r1{{r1}};\n  const std::array<double, 1> point_r2{{r2}};\n\n  CHECK(equiangular_map(point_A)[0] == approx(point_a[0]));\n  CHECK(equiangular_map(point_B)[0] == approx(point_b[0]));\n  CHECK(equiangular_map(point_xi)[0] == approx(point_x[0]));\n  CHECK(equiangular_map(point_r1)[0] ==\n        approx(-0.5 + 2.5 * tan(M_PI_4 * (2.0 * r1 - 1.0) / 3.0)));\n  CHECK(equiangular_map(point_r2)[0] ==\n        approx(-0.5 + 2.5 * tan(M_PI_4 * (2.0 * r2 - 1.0) / 3.0)));\n\n  CHECK(equiangular_map.inverse(point_a).value()[0] == approx(point_A[0]));\n  CHECK(equiangular_map.inverse(point_b).value()[0] == approx(point_B[0]));\n  CHECK(equiangular_map.inverse(point_x).value()[0] == approx(point_xi[0]));\n  CHECK(equiangular_map.inverse(point_r1).value()[0] ==\n        approx(0.5 + 3.0 * atan(0.4 * r1 + 0.2) / M_PI_2));\n  CHECK(equiangular_map.inverse(point_r2).value()[0] ==\n        approx(0.5 + 3.0 * atan(0.4 * r2 + 0.2) / M_PI_2));\n\n  CHECK((get<0, 0>(equiangular_map.inv_jacobian(point_A))) *\n            (get<0, 0>(equiangular_map.jacobian(point_A))) ==\n        approx(1.0));\n  CHECK((get<0, 0>(equiangular_map.inv_jacobian(point_B))) *\n            (get<0, 0>(equiangular_map.jacobian(point_B))) ==\n        approx(1.0));\n  CHECK((get<0, 0>(equiangular_map.inv_jacobian(point_xi))) *\n            (get<0, 0>(equiangular_map.jacobian(point_xi))) ==\n        approx(1.0));\n\n  test_jacobian(equiangular_map, point_xi);\n  test_inv_jacobian(equiangular_map, point_xi);\n  test_inverse_map(equiangular_map, point_xi);\n\n  // Check inequivalence operator\n  CHECK_FALSE(equiangular_map != equiangular_map);\n  test_serialization(equiangular_map);\n\n  test_coordinate_map_argument_types(equiangular_map, point_xi);\n  test_coordinate_map_argument_types(equiangular_map, point_r1);\n  test_coordinate_map_argument_types(equiangular_map, point_r2);\n  CHECK(not CoordinateMaps::Equiangular{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_FlatOffsetSphericalWedge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <optional>\n#include <random>\n\n#include \"Domain/CoordinateMaps/FlatOffsetSphericalWedge.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n\nnamespace domain {\n\nnamespace {\nvoid test_flat_offset_wedge() {\n  INFO(\"FlatOffsetSphericalWedge\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n\n  // The value of outer_radius sets the scale.  Don't need to choose\n  // it to be random because we will scale all other variables in terms\n  // of outer_radius.\n  const double outer_radius = 10.0;\n  CAPTURE(outer_radius);\n\n  // The value of epsilon here must be the same as in the constructor\n  // of FlatOffsetSphericalWedge, so that the sanity checks in the constructor\n  // do not fail.  The sanity checks limit the values of the map\n  // parameters so that the map doesn't get too close to singular.\n  // With epsilon=0, the map might be singular for particularly\n  // unlucky choices of the random numbers; with epsilon>0 the map is\n  // always nonsingular (but might be close to singular depending on how\n  // small epsilon is and what random numbers are chosen).\n  const double epsilon = 0.1;\n\n  // To pass sanity checks in the constructor, we must have\n  // epsilon*outer_radius <= inner_radius <= (1-epsilon)*outer_radius\n  const double inner_radius =\n      outer_radius * (epsilon + (1.0 - 2.0 * epsilon) * unit_dis(gen));\n  CAPTURE(inner_radius);\n\n  // To pass sanity checks in the constructor, we must have\n  // D > epsilon*R1 and D < (1-epsilon)^2*(R2^2-R1^2)\n  // where D is lower_face_x_width, R1 is inner_radius, and R2 is outer_radius.\n  // Also, D < (1-epsilon)*R1.\n  // Note that for epsilon < 1, all these conditions are consistent.\n  const double lower_face_x_width_min = epsilon * inner_radius;\n  const double lower_face_x_width_max = std::min(\n      (1.0 - epsilon) * sqrt(square(outer_radius) - square(inner_radius)),\n      (1.0 - epsilon) * inner_radius);\n  const double lower_face_x_width =\n      lower_face_x_width_min +\n      unit_dis(gen) * (lower_face_x_width_max - lower_face_x_width_min);\n  CAPTURE(lower_face_x_width);\n\n  const CoordinateMaps::FlatOffsetSphericalWedge map(\n      lower_face_x_width, inner_radius, outer_radius);\n  test_suite_for_map_on_unit_cube(map);\n\n  // The following are tests that the inverse function correctly\n  // returns an invalid std::optional when called for a point that is\n  // outside the range of the map.\n\n  // point with x < 0\n  CHECK_FALSE(map.inverse({{-1.0, 0.0, 0.0}}));\n\n  // point with x > lower_face_x_width\n  CHECK_FALSE(map.inverse({{1.1 * lower_face_x_width, 0.0, 0.0}}));\n\n  // point with z==0 but x is in range\n  CHECK_FALSE(map.inverse({{0.5 * lower_face_x_width, 0.0, 0.0}}));\n\n  // point outside of opening angle in y\n  // At the x-midpoint of the Block,\n  // the inner radius is sqrt(R1^2-D^2/4), the outer radius is sqrt(R2^2-D^2/4).\n  // So here we place the point at 45+eps degrees midway between the inner\n  // and outer radius.\n  CHECK_FALSE(map.inverse(\n      {{0.5 * lower_face_x_width,\n        sqrt(2.1) * 0.5 *\n            (sqrt(square(outer_radius) - square(lower_face_x_width)) +\n             sqrt(square(inner_radius) - square(lower_face_x_width))),\n        sqrt(2.0) * 0.5 *\n            (sqrt(square(outer_radius) - square(lower_face_x_width)) +\n             sqrt(square(inner_radius) - square(lower_face_x_width)))}}));\n\n  // Too small z; last check in the inverse map.\n  CHECK_FALSE(\n      map.inverse({{0.5 * lower_face_x_width, 0.0, 0.5 * inner_radius}}));\n\n  // Now test the sanity checks in the constructor.\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{0.0, inner_radius,\n                                                  outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have zero lower_face_x_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{-lower_face_x_width,\n                                                  inner_radius, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have negative lower_face_x_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{lower_face_x_width, 0.0,\n                                                  outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Cannot have zero inner_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{lower_face_x_width,\n                                                  -inner_radius, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Cannot have negative inner_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{lower_face_x_width,\n                                                  inner_radius, 0.0});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Cannot have zero outer_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{lower_face_x_width,\n                                                  inner_radius, -outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Cannot have negative outer_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{1.1 * inner_radius,\n                                                  inner_radius, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Must have lower_face_x_width < inner_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{\n            lower_face_x_width, inner_radius,\n            0.99 * sqrt(square(inner_radius) + square(lower_face_x_width))});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Must have (outer_radius)^2 >\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{0.98 * epsilon * outer_radius,\n                                                  0.99 * epsilon * outer_radius,\n                                                  outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if inner_radius < epsilon*outer_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{\n            epsilon*outer_radius, (1.0 - 0.5 * epsilon) * outer_radius,\n            outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if inner_radius < epsilon*outer_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{0.5 * epsilon * inner_radius,\n                                                  inner_radius, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if lower_face_x_width < \"\n          \"epsilon*inner_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{\n            (1.0 - 0.5 * epsilon) * 0.5 * outer_radius, 0.5 * outer_radius,\n            outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if lower_face_x_width < \"\n          \"epsilon*inner_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetSphericalWedge{\n            (1.0 - 0.5 * epsilon) *\n                sqrt(square(outer_radius) - square(0.9 * outer_radius)),\n            0.9 * outer_radius, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if D^2 < (1-epsilon)^2\"));\n#endif\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.FlatOffsetSphericalWedge\",\n                  \"[Domain][Unit]\") {\n  test_flat_offset_wedge();\n  CHECK(not CoordinateMaps::FlatOffsetSphericalWedge{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_FlatOffsetWedge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n#include <random>\n\n#include \"Domain/CoordinateMaps/FlatOffsetWedge.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n\nnamespace domain {\n\nnamespace {\nvoid test_flat_offset_wedge() {\n  INFO(\"FlatOffsetWedge\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n\n  // The value of outer_radius sets the scale.  Don't need to choose\n  // it to be random because we will scale all other variables in terms\n  // of outer_radius.\n  const double outer_radius = 10.0;\n\n  // The value of epsilon here must be the same as in the constructor\n  // of FlatOffsetWedge, so that the sanity checks in the constructor\n  // do not faile.  The sanity checks limit the values of the map\n  // parameters so that the map doesn't get too close to singular.\n  // With epsilon=0, the map might be singular for particularly\n  // unlucky choices of the random numbers; with epsilon>0 the map is\n  // always nonsingular (but might be close to singular depending on how\n  // small epsilon is and what random numbers are chosen).\n  const double epsilon = 0.1;\n\n  // To pass sanity checks in the constructor, we must have\n  // epsilon*outer_radius <= lower_face_x_width <= (1-epsilon)*outer_radius\n  const double lower_face_x_width =\n      outer_radius * (epsilon + (1.0 - 2.0 * epsilon) * unit_dis(gen));\n  CAPTURE(lower_face_x_width);\n\n  // To pass sanity checks in the constructor, we must have\n  // L > epsilon*R and 2L^2 < (1-epsilon)^2*(R^2-D^2)\n  // where L is lower_face_y_half_width and D is lower_face_x_width\n  const double lower_face_y_half_width_min = epsilon * outer_radius;\n  const double lower_face_y_half_width_max =\n      (1.0 - epsilon) *\n      sqrt((square(outer_radius) - square(lower_face_x_width)) / 2.0);\n  const double lower_face_y_half_width =\n      lower_face_y_half_width_min +\n      unit_dis(gen) *\n          (lower_face_y_half_width_max - lower_face_y_half_width_min);\n  CAPTURE(lower_face_y_half_width);\n\n  const CoordinateMaps::FlatOffsetWedge map(lower_face_y_half_width,\n                                            lower_face_x_width, outer_radius);\n  test_suite_for_map_on_unit_cube(map);\n\n  // The following are tests that the inverse function correctly\n  // returns an invalid std::optional when called for a point that is\n  // outside the range of the map.\n\n  // point with x < 0\n  CHECK_FALSE(map.inverse({{-1.0, 0.0, 0.0}}));\n\n  // point with x > lower_face_x_width\n  CHECK_FALSE(map.inverse({{1.1 * lower_face_x_width, 0.0, 0.0}}));\n\n  // point with z==0 but x is ok\n  CHECK_FALSE(map.inverse({{0.5 * lower_face_x_width, 0.0, 0.0}}));\n\n  // point outside of opening angle in y\n  CHECK_FALSE(\n      map.inverse({{0.5 * lower_face_x_width, 1.2 * lower_face_y_half_width,\n                    1.02 * lower_face_y_half_width}}));\n\n  // point with z < lower_face_y_half_width\n  CHECK_FALSE(map.inverse(\n      {{0.5 * lower_face_x_width, 0.0, 0.5 * lower_face_y_half_width}}));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{0.0, lower_face_x_width,\n                                         outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have zero lower_face_y_half_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{-lower_face_y_half_width,\n                                         lower_face_x_width, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have negative lower_face_y_half_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{lower_face_y_half_width,\n                                         -lower_face_x_width, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have negative lower_face_x_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{lower_face_y_half_width, 0.0,\n                                         outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have zero lower_face_x_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{lower_face_y_half_width,\n                                         lower_face_x_width, -outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Cannot have negative outer_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{lower_face_y_half_width,\n                                         lower_face_x_width, 0.0});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Cannot have zero outer_radius\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{\n            sqrt(square(outer_radius) - square(lower_face_x_width)),\n            lower_face_x_width, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Must have R^2-D^2 > 2 L^2\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{lower_face_y_half_width,\n                                         0.99 * epsilon * outer_radius,\n                                         outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if lower_face_x_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{\n            outer_radius * sqrt(1.0 - square(1.001 * (1.0 - epsilon))) /\n                sqrt(2.01),\n            1.001 * (1 - epsilon) * outer_radius, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if lower_face_x_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{0.99 * epsilon * outer_radius,\n                                         lower_face_x_width, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if lower_face_y_half_width\"));\n  CHECK_THROWS_WITH(\n      [&]() {\n        (CoordinateMaps::FlatOffsetWedge{\n            sqrt(square(outer_radius) - square(lower_face_x_width)) *\n                (1 - epsilon) / sqrt(1.999),\n            lower_face_x_width, outer_radius});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is not tested if 2L^2 > (1-epsilon)\"));\n#endif\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.FlatOffsetWedge\",\n                  \"[Domain][Unit]\") {\n  test_flat_offset_wedge();\n  CHECK(not CoordinateMaps::FlatOffsetWedge{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Frustum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_suite_for_frustum(\n    const bool equiangular_map_at_outer,\n    const bool equiangular_map_at_inner,\n    const CoordinateMaps::Distribution zeta_distribution) {\n  INFO(\"Suite for frustum\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> lower_bound_lower_base_dis(-7, -3);\n  std::uniform_real_distribution<> upper_bound_lower_base_dis(3, 7);\n  std::uniform_real_distribution<> lower_bound_upper_base_dis(-13.5, -9);\n  std::uniform_real_distribution<> upper_bound_upper_base_dis(9, 13.5);\n  std::uniform_real_distribution<> angle_dis(55.0, 125.0);\n\n  const double lower_x_lower_base = lower_bound_lower_base_dis(gen);\n  CAPTURE(lower_x_lower_base);\n  const double lower_y_lower_base = lower_bound_lower_base_dis(gen);\n  CAPTURE(lower_y_lower_base);\n  const double upper_x_lower_base = upper_bound_lower_base_dis(gen);\n  CAPTURE(upper_x_lower_base);\n  const double upper_y_lower_base = upper_bound_lower_base_dis(gen);\n  CAPTURE(upper_y_lower_base);\n  const double lower_x_upper_base = lower_bound_upper_base_dis(gen);\n  CAPTURE(lower_x_upper_base);\n  const double lower_y_upper_base = lower_bound_upper_base_dis(gen);\n  CAPTURE(lower_y_upper_base);\n  const double upper_x_upper_base = upper_bound_upper_base_dis(gen);\n  CAPTURE(upper_x_upper_base);\n  const double upper_y_upper_base = upper_bound_upper_base_dis(gen);\n  CAPTURE(upper_y_upper_base);\n  const double upper_z = 3.0;\n  CAPTURE(upper_z);\n  const double lower_z = -1.0;\n  CAPTURE(lower_z);\n  const double opening_angle = angle_dis(gen) * M_PI / 180.0;\n  CAPTURE(opening_angle * 180.0 / M_PI);\n  CAPTURE(equiangular_map_at_outer);\n  CAPTURE(equiangular_map_at_inner);\n  CAPTURE(zeta_distribution);\n\n  // For diagnostic purposes, compute other Frustum quantities:\n  // Frustum cross factor is small when the diagonals across each of the bases\n  // are aligned, and large when there is an angle between them.\n  const double frustum_cross = (upper_x_lower_base - lower_x_lower_base) *\n                                   (upper_y_upper_base - lower_y_upper_base) -\n                               (upper_y_lower_base - lower_y_lower_base) *\n                                   (upper_x_upper_base - lower_x_upper_base);\n  CAPTURE(frustum_cross);\n\n  // Computes how vertical the walls are of the frustum. When these\n  // quantities are zero, the frustum walls are parallel to the z-axis. This\n  // is undesired because when bulging out the Frustum the coordinates are\n  // moved along radial lines emanating from the origin.\n  const double x_wall_sep_upper = upper_x_upper_base - upper_x_lower_base;\n  const double x_wall_sep_lower = lower_x_upper_base - lower_x_lower_base;\n  const double y_wall_sep_upper = upper_y_upper_base - upper_y_lower_base;\n  const double y_wall_sep_lower = lower_y_upper_base - lower_y_lower_base;\n  const double min_wall_sep = std::min(\n      {x_wall_sep_upper, x_wall_sep_lower, y_wall_sep_upper, y_wall_sep_lower});\n  CAPTURE(x_wall_sep_upper);\n  CAPTURE(x_wall_sep_lower);\n  CAPTURE(y_wall_sep_upper);\n  CAPTURE(y_wall_sep_lower);\n  CAPTURE(min_wall_sep);\n\n  // Computes how far away the base of the Frustum is from the origin. Since\n  // the bulged Frustum is constructed by moving coordinates along radial lines,\n  // having the base of the frustum be shifted away from the origin will lead to\n  // a distorted shape. If the Frustum test fails, check if the com_shift is\n  // above 1.8. A test failure might occur in 1/25,000 cases.\n  const double x_com_bottom = 0.5 * (lower_x_lower_base + upper_x_lower_base);\n  const double y_com_bottom = 0.5 * (lower_y_lower_base + upper_y_lower_base);\n  const double com_shift = sqrt(square(x_com_bottom) + square(y_com_bottom));\n  CAPTURE(com_shift);\n\n  for (OrientationMapIterator<3> map_i{}; map_i; ++map_i) {\n    if (get(determinant(discrete_rotation_jacobian(*map_i))) < 0.0) {\n      continue;\n    }\n    const std::array<std::array<double, 2>, 4> face_vertices{\n        {{{lower_x_lower_base, lower_y_lower_base}},\n         {{upper_x_lower_base, upper_y_lower_base}},\n         {{lower_x_upper_base, lower_y_upper_base}},\n         {{upper_x_upper_base, upper_y_upper_base}}}};\n\n    // The parameters of the Frustum are chosen so that the angles between\n    // the faces are not too obtuse or acute. The ratio of the length\n    // of the longer base to the height of the Frustum is at most 7:1. Frustums\n    // with larger ratios cannot be bulged out without the root find beginning\n    // to fail in extreme cases.\n    const CoordinateMaps::Frustum frustum_map(\n        face_vertices, lower_z, upper_z, map_i(), equiangular_map_at_outer,\n        equiangular_map_at_inner, zeta_distribution, std::nullopt, 1.0, 1.0,\n        opening_angle);\n    test_suite_for_map_on_unit_cube(frustum_map);\n  }\n}\n\nvoid test_frustum_fail() {\n  INFO(\"Frustum fail\");\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n  const CoordinateMaps::Frustum map(face_vertices, 2.0, 5.0,\n                                    OrientationMap<3>::create_aligned());\n\n  // For the choice of params above, any point with z<=-1 should fail.\n  const std::array<double, 3> test_mapped_point1{{3.0, 3.0, -1.0}};\n  const std::array<double, 3> test_mapped_point2{{6.0, -7.0, -1.0}};\n  const std::array<double, 3> test_mapped_point3{{6.0, -7.0, -3.0}};\n\n  // This is outside the mapped frustum, so inverse should either\n  // return the correct inverse (which happens to be computable for\n  // this point) or it should return nullopt.\n  const std::array<double, 3> test_mapped_point4{{0.0, 0.0, 9.0}};\n\n  CHECK_FALSE(map.inverse(test_mapped_point1).has_value());\n  CHECK_FALSE(map.inverse(test_mapped_point2).has_value());\n  CHECK_FALSE(map.inverse(test_mapped_point3).has_value());\n  if (map.inverse(test_mapped_point4).has_value()) {\n    CHECK_ITERABLE_APPROX(map(map.inverse(test_mapped_point4).value()),\n                          test_mapped_point4);\n  }\n}\n\nvoid test_alignment() {\n  INFO(\"Alignment\");\n  // This test tests that the logical axes point along the expected directions\n  // in physical space\n\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n  const double lower_bound = 2.0;\n  const double upper_bound = 5.0;\n\n  const auto wedge_directions = all_wedge_directions();\n  const CoordinateMaps::Frustum map_upper_zeta(\n      face_vertices, lower_bound, upper_bound,\n      wedge_directions[0]);  // Upper Z frustum\n  const CoordinateMaps::Frustum map_upper_eta(\n      face_vertices, lower_bound, upper_bound,\n      wedge_directions[2]);  // Upper Y frustum\n  const CoordinateMaps::Frustum map_upper_xi(\n      face_vertices, lower_bound, upper_bound,\n      wedge_directions[4]);  // Upper X Frustum\n  const CoordinateMaps::Frustum map_lower_zeta(\n      face_vertices, lower_bound, upper_bound,\n      wedge_directions[1]);  // Lower Z frustum\n  const CoordinateMaps::Frustum map_lower_eta(\n      face_vertices, lower_bound, upper_bound,\n      wedge_directions[3]);  // Lower Y frustum\n  const CoordinateMaps::Frustum map_lower_xi(\n      face_vertices, lower_bound, upper_bound,\n      wedge_directions[5]);  // Lower X frustum\n  const std::array<double, 3> lowest_corner{{-1.0, -1.0, -1.0}};\n  const std::array<double, 3> along_xi{{1.0, -1.0, -1.0}};\n  const std::array<double, 3> along_eta{{-1.0, 1.0, -1.0}};\n  const std::array<double, 3> along_zeta{{-1.0, -1.0, 1.0}};\n\n  const std::array<double, 3> lowest_physical_corner_in_map_upper_zeta{\n      {-2.0, -2.0, 2.0}};\n  const std::array<double, 3> lowest_physical_corner_in_map_upper_eta{\n      {-2.0, 2.0, -2.0}};\n  const std::array<double, 3> lowest_physical_corner_in_map_upper_xi{\n      {2.0, -2.0, -2.0}};\n  const std::array<double, 3> lowest_physical_corner_in_map_lower_zeta{\n      {-2.0, 2.0, -2.0}};\n  const std::array<double, 3> lowest_physical_corner_in_map_lower_eta{\n      {-2.0, -2.0, 2.0}};\n  const std::array<double, 3> lowest_physical_corner_in_map_lower_xi{\n      {-2.0, 2.0, -2.0}};\n\n  // Test that this map's logical axes point along +X, +Y, +Z:\n  CHECK(map_upper_zeta(along_xi)[0] == approx(2.0));\n  CHECK(map_upper_zeta(along_eta)[1] == approx(2.0));\n  CHECK(map_upper_zeta(along_zeta)[2] == approx(5.0));\n  CHECK_ITERABLE_APPROX(map_upper_zeta(lowest_corner),\n                        lowest_physical_corner_in_map_upper_zeta);\n\n  // Test that this map's logical axes point along +Z, +X, +Y:\n  CHECK(map_upper_eta(along_xi)[2] == approx(2.0));\n  CHECK(map_upper_eta(along_eta)[0] == approx(2.0));\n  CHECK(map_upper_eta(along_zeta)[1] == approx(5.0));\n  CHECK_ITERABLE_APPROX(map_upper_eta(lowest_corner),\n                        lowest_physical_corner_in_map_upper_eta);\n\n  // Test that this map's logical axes point along +Y, +Z, +X:\n  CHECK(map_upper_xi(along_xi)[1] == approx(2.0));\n  CHECK(map_upper_xi(along_eta)[2] == approx(2.0));\n  CHECK(map_upper_xi(along_zeta)[0] == approx(5.0));\n  CHECK_ITERABLE_APPROX(map_upper_xi(lowest_corner),\n                        lowest_physical_corner_in_map_upper_xi);\n\n  // Test that this map's logical axes point along +X, -Y, -Z:\n  CHECK(map_lower_zeta(along_xi)[0] == approx(2.0));\n  CHECK(map_lower_zeta(along_eta)[1] == approx(-2.0));\n  CHECK(map_lower_zeta(along_zeta)[2] == approx(-5.0));\n  CHECK_ITERABLE_APPROX(map_lower_zeta(lowest_corner),\n                        lowest_physical_corner_in_map_lower_zeta);\n\n  // Test that this map's logical axes point along -Z, +X, -Y:\n  CHECK(map_lower_eta(along_xi)[2] == approx(-2.0));\n  CHECK(map_lower_eta(along_eta)[0] == approx(2.0));\n  CHECK(map_lower_eta(along_zeta)[1] == approx(-5.0));\n  CHECK_ITERABLE_APPROX(map_lower_eta(lowest_corner),\n                        lowest_physical_corner_in_map_lower_eta);\n\n  // Test that this map's logical axes point along -Y, +Z, -X:\n  CHECK(map_lower_xi(along_xi)[1] == approx(-2.0));\n  CHECK(map_lower_xi(along_eta)[2] == approx(2.0));\n  CHECK(map_lower_xi(along_zeta)[0] == approx(-5.0));\n  CHECK_ITERABLE_APPROX(map_lower_xi(lowest_corner),\n                        lowest_physical_corner_in_map_lower_xi);\n}\n\nvoid test_auto_projective_scale_factor() {\n  INFO(\"Auto projective scale factor\");\n  // This test tests that the correct suggested projective scale factor\n  // is computed based on the side lengths of the frustum.\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> lower_bound_dis(-14, -2);\n  std::uniform_real_distribution<> upper_bound_dis(2, 14);\n  std::uniform_real_distribution<> logical_dis(-1, 1);\n\n  const double lower_x_lower_base = lower_bound_dis(gen);\n  CAPTURE(lower_x_lower_base);\n  const double lower_y_lower_base = lower_bound_dis(gen);\n  CAPTURE(lower_y_lower_base);\n  const double upper_x_lower_base = upper_bound_dis(gen);\n  CAPTURE(upper_x_lower_base);\n  const double upper_y_lower_base = upper_bound_dis(gen);\n  CAPTURE(upper_y_lower_base);\n  const double lower_x_upper_base = lower_bound_dis(gen);\n  CAPTURE(lower_x_upper_base);\n  const double lower_y_upper_base = lower_bound_dis(gen);\n  CAPTURE(lower_y_upper_base);\n  const double upper_x_upper_base = upper_bound_dis(gen);\n  CAPTURE(upper_x_upper_base);\n  const double upper_y_upper_base = upper_bound_dis(gen);\n  CAPTURE(upper_y_upper_base);\n  const double xi = logical_dis(gen);\n  CAPTURE(xi);\n  const double eta = logical_dis(gen);\n  CAPTURE(eta);\n  const double zeta = logical_dis(gen);\n  CAPTURE(zeta);\n\n  const std::array<double, 3> logical_coord{{0.0, 0.0, zeta}};\n  const double lower_bound = 2.0;\n  const double upper_bound = 5.0;\n  const double sigma_z = 0.5 * (upper_bound + lower_bound);\n  const double delta_z = 0.5 * (upper_bound - lower_bound);\n  const double w_delta = sqrt(((upper_x_lower_base - lower_x_lower_base) *\n                               (upper_y_lower_base - lower_y_lower_base)) /\n                              ((upper_x_upper_base - lower_x_upper_base) *\n                               (upper_y_upper_base - lower_y_upper_base)));\n  const double projective_zeta = (w_delta - 1.0 + zeta * (w_delta + 1.0)) /\n                                 (w_delta + 1.0 + zeta * (w_delta - 1.0));\n  const double expected_physical_z = sigma_z + delta_z * (projective_zeta);\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{lower_x_lower_base, lower_y_lower_base}},\n       {{upper_x_lower_base, upper_y_lower_base}},\n       {{lower_x_upper_base, lower_y_upper_base}},\n       {{upper_x_upper_base, upper_y_upper_base}}}};\n\n  const CoordinateMaps::Frustum map(\n      face_vertices, lower_bound, upper_bound,\n      OrientationMap<3>::create_aligned(), false, false,\n      CoordinateMaps::Distribution::Projective);  // Upper Z frustum\n  CHECK(map(logical_coord)[2] == approx(expected_physical_z));\n}\n\nvoid test_is_identity() {\n  INFO(\"Is identity\");\n  check_if_map_is_identity(CoordinateMaps::Frustum{\n      std::array<std::array<double, 2>, 4>{\n          {{{-1.0, -1.0}}, {{1.0, 1.0}}, {{-1.0, -1.0}}, {{1.0, 1.0}}}},\n      -1.0, 1.0, OrientationMap<3>::create_aligned(), false, false,\n      CoordinateMaps::Distribution::Linear});\n  CHECK(not CoordinateMaps::Frustum{\n      std::array<std::array<double, 2>, 4>{\n          {{{-1.0, -1.0}}, {{2.0, 1.0}}, {{-1.0, -3.0}}, {{1.0, 1.0}}}},\n      -1.0, 1.0, OrientationMap<3>::create_aligned(), false, false,\n      CoordinateMaps::Distribution::Linear}\n                .is_identity());\n}\n\nvoid test_bulged_frustum_jacobian() {\n  INFO(\"Bulged frustum jacobian\");\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n  const CoordinateMaps::Frustum map(\n      face_vertices, 2.0, 5.0, OrientationMap<3>::create_aligned(), false,\n      false, CoordinateMaps::Distribution::Projective, std::nullopt, 1.0);\n\n  const std::array<double, 3> test_point1{{-1.0, 0.25, 0.0}};\n  const std::array<double, 3> test_point2{{1.0, 1.0, -0.5}};\n  const std::array<double, 3> test_point3{{0.7, -0.2, 0.4}};\n  const std::array<double, 3> test_point4{{0.0, 0.0, 0.0}};\n\n  test_jacobian(map, test_point1);\n  test_jacobian(map, test_point2);\n  test_jacobian(map, test_point3);\n  test_jacobian(map, test_point4);\n}\n\nvoid test_bulged_frustum_inv_jacobian() {\n  INFO(\"Bulged frustum inverse jacobian\");\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n  const CoordinateMaps::Frustum map(\n      face_vertices, 2.0, 5.0, OrientationMap<3>::create_aligned(), false,\n      false, CoordinateMaps::Distribution::Projective, std::nullopt, 1.0);\n\n  const std::array<double, 3> test_point1{{-1.0, 0.25, 0.0}};\n  const std::array<double, 3> test_point2{{1.0, 1.0, -0.5}};\n  const std::array<double, 3> test_point3{{0.7, -0.2, 0.4}};\n  const std::array<double, 3> test_point4{{0.0, 0.0, 0.0}};\n\n  test_inv_jacobian(map, test_point1);\n  test_inv_jacobian(map, test_point2);\n  test_inv_jacobian(map, test_point3);\n  test_inv_jacobian(map, test_point4);\n}\n\nvoid test_bulged_frustum_inv_map() {\n  INFO(\"Bulged frustum inverse map\");\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n  const CoordinateMaps::Frustum map(\n      face_vertices, 2.0, 5.0, OrientationMap<3>::create_aligned(), false,\n      false, CoordinateMaps::Distribution::Projective, std::nullopt, 1.0);\n\n  const std::array<double, 3> test_point1{{-1.0, 0.25, 0.0}};\n  const std::array<double, 3> test_point2{{1.0, 1.0, -0.5}};\n  const std::array<double, 3> test_point3{{0.7, -0.2, 0.4}};\n  const std::array<double, 3> test_point4{{0.0, 0.0, 0.01}};\n\n  test_inverse_map(map, test_point1);\n  test_inverse_map(map, test_point2);\n  test_inverse_map(map, test_point3);\n  test_inverse_map(map, test_point4);\n}\n\nvoid test_bulged_frustum_equiangular_full() {\n  INFO(\"Bulged frustum jacobian\");\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n  const CoordinateMaps::Frustum map(\n      face_vertices, 2.0, 4.0, OrientationMap<3>::create_aligned(), true, true,\n      CoordinateMaps::Distribution::Projective, std::nullopt, 1.0, 0.0);\n\n  // Points on the upper +zeta face:\n  const std::array<std::array<double, 3>, 4> cornerpts{{{{-1.0, -1.0, 1.0}},\n                                                        {{-1.0, 1.0, 1.0}},\n                                                        {{1.0, -1.0, 1.0}},\n                                                        {{1.0, 1.0, 1.0}}}};\n\n  const std::array<std::array<double, 3>, 3> zeroxipts{\n      {{{0.0, -1.0, 1.0}}, {{0.0, 0.0, 1.0}}, {{0.0, 1.0, 1.0}}}};\n\n  const std::array<std::array<double, 3>, 3> zeroetapts{\n      {{{-1.0, 0.0, 1.0}}, {{0.0, 0.0, 1.0}}, {{1.0, 0.0, 1.0}}}};\n\n  // If Frustum is successfully bulged, points on upper +zeta face\n  // should have the same radius:\n  const double radius = 4.0 * sqrt(3.0);\n  for (size_t i = 0; i < 3; i++) {\n    CHECK(magnitude(map(gsl::at(cornerpts, i))) == approx(radius));\n    CHECK(magnitude(map(gsl::at(zeroxipts, i))) == approx(radius));\n    CHECK(magnitude(map(gsl::at(zeroetapts, i))) == approx(radius));\n  }\n  CHECK(magnitude(map(gsl::at(cornerpts, 3))) == approx(radius));\n\n  // If Frustum is successfully equiangular, distances equally spaced\n  // in logical space correspond to equal angular distances. For a map\n  // where a logical axis subtends an angle of theta, the jacobian\n  // is radius * theta / 2 at any point along this axis.\n  const double radius_times_theta_over_two = radius * M_PI_4;\n  for (size_t i = 0; i < 3; i++) {\n    const std::array<double, 3> zeroeta_dx_dxi{\n        {map.jacobian(gsl::at(zeroetapts, i)).get(0, 0),\n         map.jacobian(gsl::at(zeroetapts, i)).get(1, 0),\n         map.jacobian(gsl::at(zeroetapts, i)).get(2, 0)}};\n    CHECK(magnitude(zeroeta_dx_dxi) == approx(radius_times_theta_over_two));\n\n    const std::array<double, 3> zeroxi_dx_deta{\n        {map.jacobian(gsl::at(zeroxipts, i)).get(0, 1),\n         map.jacobian(gsl::at(zeroxipts, i)).get(1, 1),\n         map.jacobian(gsl::at(zeroxipts, i)).get(2, 1)}};\n    CHECK(magnitude(zeroxi_dx_deta) == approx(radius_times_theta_over_two));\n  }\n}\n\nvoid test_bulged_frustum_equiangular_upper() {\n  INFO(\"Bulged frustum jacobian\");\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{0.0, -2.0}}, {{2.0, 2.0}}, {{0.0, -4.0}}, {{4.0, 4.0}}}};\n  const CoordinateMaps::Frustum map(\n      face_vertices, 2.0, 4.0, OrientationMap<3>::create_aligned(), true, true,\n      CoordinateMaps::Distribution::Linear, std::nullopt, 1.0, 1.0);\n\n  const std::array<std::array<double, 3>, 4> cornerpts{{{{-1.0, -1.0, 1.0}},\n                                                        {{-1.0, 1.0, 1.0}},\n                                                        {{1.0, -1.0, 1.0}},\n                                                        {{1.0, 1.0, 1.0}}}};\n\n  const std::array<std::array<double, 3>, 3> lowerxipts{\n      {{{-1.0, -1.0, 1.0}}, {{-1.0, 0.0, 1.0}}, {{-1.0, 1.0, 1.0}}}};\n\n  const std::array<std::array<double, 3>, 3> zeroetapts{\n      {{{-1.0, 0.0, 1.0}}, {{0.0, 0.0, 1.0}}, {{1.0, 0.0, 1.0}}}};\n\n  // If Frustum is successfully bulged, points on upper +zeta face\n  // should have the same radius:\n  const double radius = 4.0 * sqrt(3.0);\n  for (size_t i = 0; i < 3; i++) {\n    CHECK(magnitude(map(gsl::at(cornerpts, i))) == approx(radius));\n    CHECK(magnitude(map(gsl::at(lowerxipts, i))) == approx(radius));\n    CHECK(magnitude(map(gsl::at(zeroetapts, i))) == approx(radius));\n  }\n  CHECK(magnitude(map(gsl::at(cornerpts, 3))) == approx(radius));\n\n  // If Frustum is successfully equiangular, distances equally spaced\n  // in logical space correspond to equal angular distances. For a map\n  // where a logical axis subtends an angle of theta, the jacobian\n  // is radius * theta / 2 at any point along this axis. For a full\n  // wedge map, which this frustum emulates, the angle theta is pi/2,\n  // as four congruent wedges will cover a great circle. The angle is\n  // divided by two to account for the fact that the logical cube has a\n  // length of two.\n  const double radius_times_theta_over_two = radius * M_PI_4;\n  for (size_t i = 0; i < 3; i++) {\n    const std::array<double, 3> zeroeta_dx_dxi{\n        {map.jacobian(gsl::at(zeroetapts, i)).get(0, 0),\n         map.jacobian(gsl::at(zeroetapts, i)).get(1, 0),\n         map.jacobian(gsl::at(zeroetapts, i)).get(2, 0)}};\n\n    // The map only subtends half the angle in the xi direction when the\n    // half equiangular map is used.\n    CHECK(magnitude(zeroeta_dx_dxi) ==\n          approx(0.5 * radius_times_theta_over_two));\n\n    const std::array<double, 3> lowerxi_dx_deta{\n        {map.jacobian(gsl::at(lowerxipts, i)).get(0, 1),\n         map.jacobian(gsl::at(lowerxipts, i)).get(1, 1),\n         map.jacobian(gsl::at(lowerxipts, i)).get(2, 1)}};\n\n    // The map must be checked at the xi=-1 points as the lower xi points\n    // on the upper half map correspond to the xi=0 points on the full half.\n    CHECK(magnitude(lowerxi_dx_deta) == approx(radius_times_theta_over_two));\n  }\n}\n\nvoid test_bulged_frustum_equiangular_lower() {\n  INFO(\"Bulged frustum jacobian\");\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{-2.0, -2.0}}, {{0.0, 2.0}}, {{-4.0, -4.0}}, {{0.0, 4.0}}}};\n  const CoordinateMaps::Frustum map(\n      face_vertices, 2.0, 4.0, OrientationMap<3>::create_aligned(), true, true,\n      CoordinateMaps::Distribution::Linear, std::nullopt, 1.0, -1.0);\n\n  const std::array<std::array<double, 3>, 4> cornerpts{{{{-1.0, -1.0, 1.0}},\n                                                        {{-1.0, 1.0, 1.0}},\n                                                        {{1.0, -1.0, 1.0}},\n                                                        {{1.0, 1.0, 1.0}}}};\n\n  const std::array<std::array<double, 3>, 3> upperxipts{\n      {{{1.0, -1.0, 1.0}}, {{1.0, 0.0, 1.0}}, {{1.0, 1.0, 1.0}}}};\n\n  const std::array<std::array<double, 3>, 3> zeroetapts{\n      {{{-1.0, 0.0, 1.0}}, {{0.0, 0.0, 1.0}}, {{1.0, 0.0, 1.0}}}};\n\n  // If Frustum is successfully bulged, points on upper +zeta face\n  // should have the same radius:\n  const double radius = 4.0 * sqrt(3.0);\n  for (size_t i = 0; i < 3; i++) {\n    CHECK(magnitude(map(gsl::at(cornerpts, i))) == approx(radius));\n    CHECK(magnitude(map(gsl::at(upperxipts, i))) == approx(radius));\n    CHECK(magnitude(map(gsl::at(zeroetapts, i))) == approx(radius));\n  }\n  CHECK(magnitude(map(gsl::at(cornerpts, 3))) == approx(radius));\n\n  // If Frustum is successfully equiangular, distances equally spaced\n  // in logical space correspond to equal angular distances. For a map\n  // where a logical axis subtends an angle of theta, the jacobian\n  // is radius * theta / 2 at any point along this axis. For a full\n  // wedge map, which this frustum emulates, the angle theta is pi/2,\n  // as four congruent wedges will cover a great circle. The angle is\n  // divided by two to account for the fact that the logical cube has a\n  // length of two.\n  const double radius_times_theta_over_two = radius * M_PI_4;\n  for (size_t i = 0; i < 3; i++) {\n    const std::array<double, 3> zeroeta_dx_dxi{\n        {map.jacobian(gsl::at(zeroetapts, i)).get(0, 0),\n         map.jacobian(gsl::at(zeroetapts, i)).get(1, 0),\n         map.jacobian(gsl::at(zeroetapts, i)).get(2, 0)}};\n\n    // The map only subtends half the angle in the xi direction when the\n    // half equiangular map is used.\n    CHECK(magnitude(zeroeta_dx_dxi) ==\n          approx(0.5 * radius_times_theta_over_two));\n\n    const std::array<double, 3> upperxi_dx_deta{\n        {map.jacobian(gsl::at(upperxipts, i)).get(0, 1),\n         map.jacobian(gsl::at(upperxipts, i)).get(1, 1),\n         map.jacobian(gsl::at(upperxipts, i)).get(2, 1)}};\n\n    // The map must be checked at the xi=+1 points as the upper xi points\n    // on the lower half map correspond to the xi=0 points on the full half.\n    CHECK(magnitude(upperxi_dx_deta) == approx(radius_times_theta_over_two));\n  }\n}\n\nvoid test_frustum_fail_equiangular() {\n  INFO(\"Frustum fail\");\n  const std::array<std::array<double, 2>, 4> face_vertices{\n      {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n  const std::array<double, 3> test_mapped_point1{{0.0, 50.0, 9.0}};\n  const std::array<double, 3> test_mapped_point2{{4.0, 4.0, 1.0}};\n  const std::array<double, 3> test_mapped_point3{{3.0, 3.0, 1.99}};\n  const CoordinateMaps::Frustum equiangular_map(\n      face_vertices, 2.0, 5.0, OrientationMap<3>::create_aligned(), true, true,\n      CoordinateMaps::Distribution::Linear, std::nullopt, 1.0, 1.0);\n  CHECK(not equiangular_map.inverse(test_mapped_point1).has_value());\n  CHECK(not equiangular_map.inverse(test_mapped_point2).has_value());\n  CHECK(not equiangular_map.inverse(test_mapped_point3).has_value());\n}\n\nvoid test_bulged_frustum_inverse() {\n  // Check that the bulged frustum inverse map works with input coordinates\n  // outside of the domain, and returns nullopt. The map involves a numerical\n  // rootfind, which has to terminate cleanly for this to work. These parameters\n  // are taken from a BBH domain where this case failed.\n  CoordinateMaps::Frustum frustum{\n      {{{{-40., -20.}},\n        {{0., 20.}},\n        {{-69.282032302755098385, -69.282032302755098385}},\n        {{0., 69.282032302755098385}}}},\n      20.,\n      69.282032302755098385,\n      OrientationMap<3>{{{Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                          Direction<3>::upper_eta()}}},\n      true,\n      true,\n      CoordinateMaps::Distribution::Projective,\n      std::nullopt,\n      1.,\n      -1.};\n  const auto inverse =\n      frustum.inverse({{-83.565015846289398382, -20.891253961572349596,\n                        -82.337943622612172589}});\n  CHECK_FALSE(inverse.has_value());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Frustum\", \"[Domain][Unit]\") {\n  test_frustum_fail();\n  test_suite_for_frustum(false, false,\n                         CoordinateMaps::Distribution::Linear);  // Equidistant\n  test_suite_for_frustum(true, false,\n                         CoordinateMaps::Distribution::Linear);  // Equidistant\n  test_suite_for_frustum(\n      true, true, CoordinateMaps::Distribution::Projective);  // Equiangular\n  test_suite_for_frustum(\n      true, true, CoordinateMaps::Distribution::Logarithmic);  // Equiangular\n  test_suite_for_frustum(\n      true, false, CoordinateMaps::Distribution::Logarithmic);  // Equiangular\n  test_alignment();\n  test_auto_projective_scale_factor();\n  test_is_identity();\n  test_bulged_frustum_jacobian();\n  test_bulged_frustum_inv_jacobian();\n  test_bulged_frustum_inv_map();\n  test_bulged_frustum_equiangular_full();\n  test_bulged_frustum_equiangular_upper();\n  test_bulged_frustum_equiangular_lower();\n  test_frustum_fail_equiangular();\n  test_bulged_frustum_inverse();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::array<std::array<double, 2>, 4> face_vertices{\n            {{{-2.0, -2.0}}, {{-3.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n        const double lower_bound = 2.0;\n        const double upper_bound = 5.0;\n\n        auto failed_frustum =\n            CoordinateMaps::Frustum(face_vertices, lower_bound, upper_bound,\n                                    OrientationMap<3>::create_aligned());\n        static_cast<void>(failed_frustum);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The lower bound for a coordinate must be numerically \"\n          \"less than the upper bound for that coordinate.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::array<std::array<double, 2>, 4> face_vertices{\n            {{{-2.0, -2.0}}, {{2.0, -3.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n        const double lower_bound = 2.0;\n        const double upper_bound = 5.0;\n\n        auto failed_frustum =\n            CoordinateMaps::Frustum(face_vertices, lower_bound, upper_bound,\n                                    OrientationMap<3>::create_aligned());\n        static_cast<void>(failed_frustum);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The lower bound for a coordinate must be numerically \"\n          \"less than the upper bound for that coordinate.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::array<std::array<double, 2>, 4> face_vertices{\n            {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{-5.0, 4.0}}}};\n        const double lower_bound = 2.0;\n        const double upper_bound = 5.0;\n\n        auto failed_frustum =\n            CoordinateMaps::Frustum(face_vertices, lower_bound, upper_bound,\n                                    OrientationMap<3>::create_aligned());\n        static_cast<void>(failed_frustum);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The lower bound for a coordinate must be numerically \"\n          \"less than the upper bound for that coordinate.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::array<std::array<double, 2>, 4> face_vertices{\n            {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, -5.0}}}};\n        const double lower_bound = 2.0;\n        const double upper_bound = 5.0;\n\n        auto failed_frustum =\n            CoordinateMaps::Frustum(face_vertices, lower_bound, upper_bound,\n                                    OrientationMap<3>::create_aligned());\n        static_cast<void>(failed_frustum);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The lower bound for a coordinate must be numerically \"\n          \"less than the upper bound for that coordinate.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::array<std::array<double, 2>, 4> face_vertices{\n            {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n        const double lower_bound = 2.0;\n        const double upper_bound = -2.0;\n\n        auto failed_frustum =\n            CoordinateMaps::Frustum(face_vertices, lower_bound, upper_bound,\n                                    OrientationMap<3>::create_aligned());\n        static_cast<void>(failed_frustum);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The lower bound for a coordinate must be numerically \"\n          \"less than the upper bound for that coordinate.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const std::array<std::array<double, 2>, 4> face_vertices{\n            {{{-2.0, -2.0}}, {{2.0, 2.0}}, {{-4.0, -4.0}}, {{4.0, 4.0}}}};\n        const double lower_bound = 2.0;\n        const double upper_bound = 5.0;\n        const CoordinateMaps::Distribution zeta_distribution =\n            CoordinateMaps::Distribution::Linear;\n        const bool with_equiangular_map = false;\n        const double sphericity = 1.3;\n\n        auto failed_frustum = CoordinateMaps::Frustum(\n            face_vertices, lower_bound, upper_bound,\n            OrientationMap<3>::create_aligned(), with_equiangular_map,\n            with_equiangular_map, zeta_distribution, std::nullopt, sphericity);\n        static_cast<void>(failed_frustum);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The sphericity must be set between 0.0, corresponding to a flat \"\n          \"surface, and 1.0, corresponding to a spherical surface, inclusive. \"\n          \"It is currently set to 1.3\"));\n#endif\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Identity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <size_t Dim>\nvoid test_identity() {\n  CoordinateMaps::Identity<Dim> identity_map;\n  const auto xi = make_array<Dim>(1.0);\n  const auto x = make_array<Dim>(1.0);\n  CHECK(identity_map(xi) == x);\n  CHECK(identity_map.inverse(x).value() == xi);\n  const auto inv_jac = identity_map.inv_jacobian(xi);\n  const auto jac = identity_map.jacobian(xi);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      CHECK(inv_jac.get(i, j) == (i == j ? 1.0 : 0.0));\n      CHECK(jac.get(i, j) == (i == j ? 1.0 : 0.0));\n    }\n  }\n  // Checks the inequivalence operator\n  CHECK_FALSE(identity_map != identity_map);\n  test_serialization(identity_map);\n\n  test_coordinate_map_argument_types(identity_map, xi);\n\n  check_if_map_is_identity(CoordinateMaps::Identity<Dim>{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Identity\", \"[Domain][Unit]\") {\n  test_identity<1>();\n  test_identity<2>();\n  test_identity<3>();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Interval.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_coordinate_map(\n    const double xA, const double xB, const double xa, const double xb,\n    const CoordinateMaps::Distribution distribution,\n    const std::optional<double> singularity_pos = std::nullopt) {\n  CoordinateMaps::Interval map(xA, xB, xa, xb, distribution, singularity_pos);\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(xA, xB);\n  const auto sample_points = make_with_random_values<std::array<double, 10>>(\n      make_not_null(&generator), make_not_null(&dist));\n  for (double scalar : sample_points) {\n    const std::array<double, 1> random_point{{scalar}};\n    test_coordinate_map_argument_types(map, random_point);\n    test_inverse_map(map, random_point);\n    test_jacobian(map, random_point);\n    test_inv_jacobian(map, random_point);\n  }\n  const std::array<double, 1> point_xA{{xA}};\n  const std::array<double, 1> point_xB{{xB}};\n  CHECK(get<0>(map(point_xA)) == approx(xa));\n  CHECK(get<0>(map(point_xB)) == approx(xb));\n\n  // Check inequivalence operator\n  CHECK_FALSE(map != map);\n  test_serialization(map);\n  CHECK(not map.is_identity());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Interval\", \"[Domain][Unit]\") {\n  const std::array<double, 1> point_1{{-0.5}};\n  const std::array<double, 1> point_2{{1 / 3.}};\n  const std::array<double, 1> point_3{{12 / 7.}};\n\n  test_coordinate_map(-1.0, 2.0, -3.1, 2.7,\n                      CoordinateMaps::Distribution::Linear);\n  CoordinateMaps::Interval linear_map(-1.0, 2.0, -3.1, 2.7,\n                                      CoordinateMaps::Distribution::Linear);\n  CHECK(get<0>(linear_map(point_1)) == approx(-6.4 / 3.));\n  CHECK(get<0>(linear_map(point_2)) == approx(-4.7 / 9.));\n  CHECK(get<0>(linear_map(point_3)) == approx(90.2 / 42.));\n\n  test_coordinate_map(-1.0, 2.0, -3.1, 2.7,\n                      CoordinateMaps::Distribution::Equiangular);\n  CoordinateMaps::Interval equiangular_map(\n      -1.0, 2.0, -3.1, 2.7, CoordinateMaps::Distribution::Equiangular);\n  CHECK(get<0>(equiangular_map(point_1)) == approx(-1.87431578064991));\n  CHECK(get<0>(equiangular_map(point_2)) == approx(-0.45371712422518));\n  CHECK(get<0>(equiangular_map(point_3)) == approx(1.9402974025886));\n\n  test_coordinate_map(-1.0, 2.0, -3.1, 2.7,\n                      CoordinateMaps::Distribution::Logarithmic, -3.2);\n  CoordinateMaps::Interval logarithmic_map_left(\n      -1.0, 2.0, -3.1, 2.7, CoordinateMaps::Distribution::Logarithmic, -3.2);\n  CHECK(get<0>(logarithmic_map_left(point_1)) == approx(-3.0026932232316066));\n  CHECK(get<0>(logarithmic_map_left(point_2)) == approx(-2.5875856781465209));\n  CHECK(get<0>(logarithmic_map_left(point_3)) == approx(0.80128456770888800));\n\n  test_coordinate_map(-1.0, 2.0, -3.1, 2.7,\n                      CoordinateMaps::Distribution::Logarithmic, 2.8);\n  CoordinateMaps::Interval logarithmic_map_right(\n      -1.0, 2.0, -3.1, 2.7, CoordinateMaps::Distribution::Logarithmic, 2.8);\n  CHECK(get<0>(logarithmic_map_right(point_1)) == approx(-0.190267286625263));\n  CHECK(get<0>(logarithmic_map_right(point_2)) == approx(1.836599930886074));\n  CHECK(get<0>(logarithmic_map_right(point_3)) == approx(2.652547353227159));\n\n  test_coordinate_map(-1.0, 2.0, -3.1, 2.7,\n                      CoordinateMaps::Distribution::Inverse, -3.2);\n  CoordinateMaps::Interval inverse_map_left(\n      -1.0, 2.0, -3.1, 2.7, CoordinateMaps::Distribution::Inverse, -3.2);\n  CHECK(get<0>(inverse_map_left(point_1)) == approx(-3.080405405405405));\n  CHECK(get<0>(inverse_map_left(point_2)) == approx(-3.022408026755852));\n  CHECK(get<0>(inverse_map_left(point_3)) == approx(-2.295620437956204));\n\n  test_coordinate_map(-1.0, 2.0, -3.1, 2.7,\n                      CoordinateMaps::Distribution::Inverse, 2.8);\n  CoordinateMaps::Interval inverse_map_right(\n      -1.0, 2.0, -3.1, 2.7, CoordinateMaps::Distribution::Inverse, 2.8);\n  CHECK(get<0>(inverse_map_right(point_1)) == approx(2.246875));\n  CHECK(get<0>(inverse_map_right(point_2)) == approx(2.579668049792531120));\n  CHECK(get<0>(inverse_map_right(point_3)) == approx(2.6896705253784505788));\n\n  check_if_map_is_identity(CoordinateMaps::Interval{\n      -1.5, 1.0, -1.5, 1.0, CoordinateMaps::Distribution::Linear});\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_KerrHorizonConforming.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/KerrHorizonConforming.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\n\nvoid test_map_helpers(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution mass_dist{0.5, 2.};\n  const double mass = mass_dist(*generator);\n  std::uniform_real_distribution spin_dist{-1. / sqrt(3), 1. / sqrt(3)};\n  const auto spin =\n      make_with_random_values<std::array<double, 3>>(generator, spin_dist, 3);\n  const domain::CoordinateMaps::KerrHorizonConforming map(mass, spin);\n  std::uniform_real_distribution point_dist{-10., 10.};\n  const auto random_point =\n      make_with_random_values<std::array<double, 3>>(generator, point_dist, 3);\n  test_serialization(map);\n  test_coordinate_map_argument_types(map, random_point);\n  test_inverse_map(map, random_point);\n  test_jacobian(map, random_point);\n  test_inv_jacobian(map, random_point);\n}\n\nvoid test_no_spin(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution mass_dist{0.5, 2.};\n  const double mass = mass_dist(*generator);\n  const std::array<double, 3> spin{0., 0., 0.};\n  std::uniform_real_distribution dist{-10., 10.};\n  const auto coords =\n      make_with_random_values<std::array<double, 3>>(generator, dist, 3);\n  const auto map = domain::CoordinateMaps::KerrHorizonConforming(mass, spin);\n  const auto res = map(coords);\n  CHECK_ITERABLE_APPROX(coords, res);\n}\n\nvoid test_random_spin(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution mass_dist{0.5, 2.};\n  const double mass = mass_dist(*generator);\n  std::uniform_real_distribution spin_dist{-1. / sqrt(3), 1. / sqrt(3)};\n  const auto spin =\n      make_with_random_values<std::array<double, 3>>(generator, spin_dist, 3);\n  // test coordinates on unit sphere\n  std::uniform_real_distribution point_dist{-10., 10.};\n  const size_t number_of_points = 1000;\n  auto coords = make_with_random_values<std::array<DataVector, 3>>(\n      generator, point_dist, number_of_points);\n  coords = coords / magnitude(coords);\n\n  const auto map = domain::CoordinateMaps::KerrHorizonConforming(mass, spin);\n  const auto mapped_coords = map(coords);\n\n  const DataVector coords_sq_min_spin_sq =\n      dot(mapped_coords, mapped_coords) - square(mass) * dot(spin, spin);\n\n  // explicit expression for Kerr-Schild radius r\n  DataVector r = coords_sq_min_spin_sq +\n                 sqrt(coords_sq_min_spin_sq * coords_sq_min_spin_sq +\n                      4 * square(mass * dot(mapped_coords, spin)));\n  r = sqrt(r / 2.);\n\n  for (const auto& val : r) {\n    CHECK(val == approx(1.));\n  }\n}\n\nvoid test_with_kerr_horizon(const double mass,\n                            const std::array<double, 3>& dimensionless_spin) {\n  CAPTURE(mass);\n  CAPTURE(dimensionless_spin);\n\n  // This is r_+\n  const double horizon_kerrschild_radius =\n      mass * (1. + sqrt(1. - dot(dimensionless_spin, dimensionless_spin)));\n  CAPTURE(horizon_kerrschild_radius);\n\n  // Set up a wedge with a Kerr-horizon-conforming inner surface.\n  // The inner radius of r_+ will be mapped to an ellipsoid with constant\n  // Boyer-Lindquist radius r_+.\n  const domain::CoordinateMaps::Wedge<3> wedge_map{\n      horizon_kerrschild_radius,\n      2. * horizon_kerrschild_radius,\n      1.,\n      1.,\n      OrientationMap<3>::create_aligned(),\n      false};\n  const domain::CoordinateMaps::KerrHorizonConforming horizon_map{\n      mass, dimensionless_spin};\n  const auto coord_map =\n      domain::make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n          wedge_map, horizon_map);\n\n  // Get some coordinates on the inner face\n  const Mesh<2> face_mesh{12, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords =\n      interface_logical_coordinates(face_mesh, Direction<3>::lower_zeta());\n  const tnsr::I<DataVector, 3> x = (*coord_map)(logical_coords);\n\n  // Check the coordinates on the inner face are on a Kerr horizon\n  const std::array<DataVector, 2> theta_phi{\n      {atan2(sqrt(square(get<0>(x)) + square(get<1>(x))), get<2>(x)),\n       atan2(get<1>(x), get<0>(x))}};\n  const DataVector expected_horizon_radii = get(\n      gr::Solutions::kerr_horizon_radius(theta_phi, mass, dimensionless_spin));\n  CHECK_ITERABLE_APPROX(get(magnitude(x)), expected_horizon_radii);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.KerrHorizonConforming\",\n                  \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test_map_helpers(make_not_null(&generator));\n  test_no_spin(make_not_null(&generator));\n  test_random_spin(make_not_null(&generator));\n  test_with_kerr_horizon(0.45, {{0., 0.2, 0.8}});\n}\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_PolarToCartesian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/PolarToCartesian.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_map_at_point(const CoordinateMaps::PolarToCartesian& map,\n                       const std::array<double, 2>& source_point,\n                       const std::array<double, 2>& target_point) {\n  test_inverse_map(map, source_point);\n  if (source_point != std::array{0.0, 0.0}) {  // inv jac singular at origin\n    test_coordinate_map_argument_types(map, source_point);\n    test_inv_jacobian(map, source_point);\n  }\n  CAPTURE(source_point);\n  CAPTURE(target_point);\n  CHECK_ITERABLE_APPROX(map(source_point), target_point);\n  CHECK_ITERABLE_APPROX(map.inverse(target_point).value(), source_point);\n}\n\nvoid test_map(const CoordinateMaps::PolarToCartesian& map) {\n  CHECK(not map.is_identity());\n  CHECK_FALSE(map != map);\n  test_serialization(map);\n  test_map_at_point(map, {{0.0, 0.0}}, {{0.0, 0.0}});\n  test_map_at_point(map, {{1.0, 0.0}}, {{1.0, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_4}}, {{M_SQRT1_2, M_SQRT1_2}});\n  test_map_at_point(map, {{1.0, M_PI_2}}, {{0.0, 1.0}});\n  test_map_at_point(map, {{1.0, 3.0 * M_PI_4}}, {{-M_SQRT1_2, M_SQRT1_2}});\n  test_map_at_point(map, {{1.0, M_PI}}, {{-1.0, 0.0}});\n  test_map_at_point(map, {{1.0, 5.0 * M_PI_4}}, {{-M_SQRT1_2, -M_SQRT1_2}});\n  test_map_at_point(map, {{1.0, 3.0 * M_PI_2}}, {{0.0, -1.0}});\n  test_map_at_point(map, {{1.0, 7.0 * M_PI_4}}, {{M_SQRT1_2, -M_SQRT1_2}});\n  test_map_at_point(map, {{1.0, M_PI}}, {{-1.0, std::copysign(0.0, -1.0)}});\n}\n\nvoid test() {\n  const CoordinateMaps::PolarToCartesian original_map{};\n  test_map(original_map);\n  const auto serialized_map = serialize_and_deserialize(original_map);\n  test_map(serialized_map);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.PolarToCartesian\",\n                  \"[Domain][Unit]\") {\n  test();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_ProductMaps.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_product_two_maps_fail() {\n  INFO(\"Product two maps fail\");\n  const CoordinateMaps::Affine affine(-1.0, 1.0, 3.0, 4.0);\n  const CoordinateMaps::Wedge<2> wedge(\n      0.2, 4.0, 0.0, 1.0, OrientationMap<2>::create_aligned(), true);\n  {\n    const CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                         CoordinateMaps::Wedge<2>>\n        map(affine, wedge);\n    // Should fail wedge map\n    const std::array<double, 3> mapped_point1{{3.5, -1.0, -1.0}};\n\n    // Should be ok\n    const std::array<double, 3> mapped_point2{{3.5, 3.0, -1.0}};\n\n    CHECK_FALSE(map.inverse(mapped_point1).has_value());\n    CHECK(map.inverse(mapped_point2).has_value());\n    CHECK_ITERABLE_APPROX(map(map.inverse(mapped_point2).value()),\n                          mapped_point2);\n  }\n\n  {\n    const CoordinateMaps::ProductOf2Maps<CoordinateMaps::Wedge<2>,\n                                         CoordinateMaps::Affine>\n        map(wedge, affine);\n    // Should fail wedge map\n    const std::array<double, 3> mapped_point1{{-1.0, -1.0, 3.5}};\n\n    // Should be ok\n    const std::array<double, 3> mapped_point2{{3.0, -1.0, 3.5}};\n\n    CHECK_FALSE(map.inverse(mapped_point1).has_value());\n    CHECK(map.inverse(mapped_point2).has_value());\n    CHECK_ITERABLE_APPROX(map(map.inverse(mapped_point2).value()),\n                          mapped_point2);\n  }\n}\nvoid test_product_of_2_maps() {\n  INFO(\"Product of two maps\");\n  using affine_map = CoordinateMaps::Affine;\n  using affine_map_2d = CoordinateMaps::ProductOf2Maps<affine_map, affine_map>;\n\n  const double xA = -1.0;\n  const double xB = 1.0;\n  const double xa = -2.0;\n  const double xb = 2.0;\n  affine_map affine_map_x(xA, xB, xa, xb);\n\n  const double yA = -2.0;\n  const double yB = 3.0;\n  const double ya = 5.0;\n  const double yb = -2.0;\n\n  affine_map affine_map_y(yA, yB, ya, yb);\n\n  affine_map_2d affine_map_xy(affine_map_x, affine_map_y);\n\n  const double xi = 0.5 * (xA + xB);\n  const double x = xb * (xi - xA) / (xB - xA) + xa * (xB - xi) / (xB - xA);\n  const double eta = 0.5 * (yA + yB);\n  const double y = yb * (eta - yA) / (yB - yA) + ya * (yB - eta) / (yB - yA);\n\n  const std::array<double, 2> point_A{{xA, yA}};\n  const std::array<double, 2> point_B{{xB, yB}};\n  const std::array<double, 2> point_xi{{xi, eta}};\n  const std::array<double, 2> point_a{{xa, ya}};\n  const std::array<double, 2> point_b{{xb, yb}};\n  const std::array<double, 2> point_x{{x, y}};\n\n  CHECK(affine_map_xy(point_A) == point_a);\n  CHECK(affine_map_xy(point_B) == point_b);\n  CHECK(affine_map_xy(point_xi) == point_x);\n\n  CHECK(affine_map_xy.inverse(point_a).value() == point_A);\n  CHECK(affine_map_xy.inverse(point_b).value() == point_B);\n  CHECK(affine_map_xy.inverse(point_x).value() == point_xi);\n\n  const double inv_jacobian_00 = (xB - xA) / (xb - xa);\n  const double inv_jacobian_11 = (yB - yA) / (yb - ya);\n  const auto inv_jac_A = affine_map_xy.inv_jacobian(point_A);\n  const auto inv_jac_B = affine_map_xy.inv_jacobian(point_B);\n  const auto inv_jac_xi = affine_map_xy.inv_jacobian(point_xi);\n\n  CHECK(inv_jac_A.get(0, 0) == inv_jacobian_00);\n  CHECK(inv_jac_B.get(0, 0) == inv_jacobian_00);\n  CHECK(inv_jac_xi.get(0, 0) == inv_jacobian_00);\n\n  CHECK(inv_jac_A.get(0, 1) == 0.0);\n  CHECK(inv_jac_B.get(0, 1) == 0.0);\n  CHECK(inv_jac_xi.get(0, 1) == 0.0);\n\n  CHECK(inv_jac_A.get(1, 0) == 0.0);\n  CHECK(inv_jac_B.get(1, 0) == 0.0);\n  CHECK(inv_jac_xi.get(1, 0) == 0.0);\n\n  CHECK(inv_jac_A.get(1, 1) == inv_jacobian_11);\n  CHECK(inv_jac_B.get(1, 1) == inv_jacobian_11);\n  CHECK(inv_jac_xi.get(1, 1) == inv_jacobian_11);\n\n  const double jacobian_00 = (xb - xa) / (xB - xA);\n  const double jacobian_11 = (yb - ya) / (yB - yA);\n  const auto jac_A = affine_map_xy.jacobian(point_A);\n  const auto jac_B = affine_map_xy.jacobian(point_B);\n  const auto jac_xi = affine_map_xy.jacobian(point_xi);\n\n  CHECK(jac_A.get(0, 0) == jacobian_00);\n  CHECK(jac_B.get(0, 0) == jacobian_00);\n  CHECK(jac_xi.get(0, 0) == jacobian_00);\n\n  CHECK(jac_A.get(0, 1) == 0.0);\n  CHECK(jac_B.get(0, 1) == 0.0);\n  CHECK(jac_xi.get(0, 1) == 0.0);\n\n  CHECK(jac_A.get(1, 0) == 0.0);\n  CHECK(jac_B.get(1, 0) == 0.0);\n  CHECK(jac_xi.get(1, 0) == 0.0);\n\n  CHECK(jac_A.get(1, 1) == jacobian_11);\n  CHECK(jac_B.get(1, 1) == jacobian_11);\n  CHECK(jac_xi.get(1, 1) == jacobian_11);\n\n  // Check Jacobians for DataVectors\n  const Mesh<2> mesh{8, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto tensor_logical_coords = logical_coordinates(mesh);\n  const std::array<DataVector, 2> logical_coords{\n      {tensor_logical_coords.get(0), tensor_logical_coords.get(1)}};\n  const auto volume_inv_jac = affine_map_xy.inv_jacobian(logical_coords);\n  const auto volume_jac = affine_map_xy.jacobian(logical_coords);\n  for (size_t i = 0; i < 2; ++i) {\n    for (size_t j = 0; j < 2; ++j) {\n      if (i == j) {\n        CHECK(volume_inv_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(),\n                         i == 0 ? inv_jacobian_00 : inv_jacobian_11));\n        CHECK(volume_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(),\n                         i == 0 ? jacobian_00 : jacobian_11));\n      } else {\n        CHECK(volume_inv_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(), 0.0));\n        CHECK(volume_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(), 0.0));\n      }\n    }\n  }\n\n  CHECK_FALSE(affine_map_xy != affine_map_xy);\n  test_serialization(affine_map_xy);\n\n  test_coordinate_map_argument_types(affine_map_xy, point_xi);\n  test_product_two_maps_fail();\n  check_if_map_is_identity(\n      CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                     CoordinateMaps::Affine>{\n          CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0},\n          CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0}});\n  CHECK(not CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                           CoordinateMaps::Affine>{\n      CoordinateMaps::Affine{-1.0, 2.0, -1.0, 1.0},\n      CoordinateMaps::Affine{-1.0, 1.0, -3.0, 1.0}}\n                .is_identity());\n}\n\nvoid test_product_of_3_maps() {\n  INFO(\"Product of 3 maps\");\n  using affine_map = CoordinateMaps::Affine;\n  using affine_map_3d =\n      CoordinateMaps::ProductOf3Maps<affine_map, affine_map, affine_map>;\n\n  const double xA = -1.0;\n  const double xB = 1.0;\n  const double xa = -2.0;\n  const double xb = 2.0;\n  affine_map affine_map_x(xA, xB, xa, xb);\n\n  const double yA = -2.0;\n  const double yB = 3.0;\n  const double ya = 5.0;\n  const double yb = -2.0;\n\n  affine_map affine_map_y(yA, yB, ya, yb);\n\n  const double zA = 4.0;\n  const double zB = 8.0;\n  const double za = -15.0;\n  const double zb = 12.0;\n\n  affine_map affine_map_z(zA, zB, za, zb);\n\n  affine_map_3d affine_map_xyz(affine_map_x, affine_map_y, affine_map_z);\n\n  const double xi = 0.5 * (xA + xB);\n  const double x = xb * (xi - xA) / (xB - xA) + xa * (xB - xi) / (xB - xA);\n  const double eta = 0.5 * (yA + yB);\n  const double y = yb * (eta - yA) / (yB - yA) + ya * (yB - eta) / (yB - yA);\n  const double zeta = 0.5 * (zA + zB);\n  const double z = zb * (zeta - zA) / (zB - zA) + za * (zB - zeta) / (zB - zA);\n\n  const std::array<double, 3> point_A{{xA, yA, zA}};\n  const std::array<double, 3> point_B{{xB, yB, zB}};\n  const std::array<double, 3> point_xi{{xi, eta, zeta}};\n  const std::array<double, 3> point_a{{xa, ya, za}};\n  const std::array<double, 3> point_b{{xb, yb, zb}};\n  const std::array<double, 3> point_x{{x, y, z}};\n\n  CHECK(affine_map_xyz(point_A) == point_a);\n  CHECK(affine_map_xyz(point_B) == point_b);\n  CHECK(affine_map_xyz(point_xi) == point_x);\n\n  CHECK(affine_map_xyz.inverse(point_a).value() == point_A);\n  CHECK(affine_map_xyz.inverse(point_b).value() == point_B);\n  CHECK(affine_map_xyz.inverse(point_x).value() == point_xi);\n\n  const double inv_jacobian_00 = (xB - xA) / (xb - xa);\n  const double inv_jacobian_11 = (yB - yA) / (yb - ya);\n  const double inv_jacobian_22 = (zB - zA) / (zb - za);\n  const auto inv_jac_A = affine_map_xyz.inv_jacobian(point_A);\n  const auto inv_jac_B = affine_map_xyz.inv_jacobian(point_B);\n  const auto inv_jac_xi = affine_map_xyz.inv_jacobian(point_xi);\n\n  CHECK(inv_jac_A.get(0, 0) == inv_jacobian_00);\n  CHECK(inv_jac_B.get(0, 0) == inv_jacobian_00);\n  CHECK(inv_jac_xi.get(0, 0) == inv_jacobian_00);\n\n  CHECK(inv_jac_A.get(0, 1) == 0.0);\n  CHECK(inv_jac_B.get(0, 1) == 0.0);\n  CHECK(inv_jac_xi.get(0, 1) == 0.0);\n\n  CHECK(inv_jac_A.get(0, 2) == 0.0);\n  CHECK(inv_jac_B.get(0, 2) == 0.0);\n  CHECK(inv_jac_xi.get(0, 2) == 0.0);\n\n  CHECK(inv_jac_A.get(1, 0) == 0.0);\n  CHECK(inv_jac_B.get(1, 0) == 0.0);\n  CHECK(inv_jac_xi.get(1, 0) == 0.0);\n\n  CHECK(inv_jac_A.get(1, 1) == inv_jacobian_11);\n  CHECK(inv_jac_B.get(1, 1) == inv_jacobian_11);\n  CHECK(inv_jac_xi.get(1, 1) == inv_jacobian_11);\n\n  CHECK(inv_jac_A.get(1, 2) == 0.0);\n  CHECK(inv_jac_B.get(1, 2) == 0.0);\n  CHECK(inv_jac_xi.get(1, 2) == 0.0);\n\n  CHECK(inv_jac_A.get(2, 0) == 0.0);\n  CHECK(inv_jac_B.get(2, 0) == 0.0);\n  CHECK(inv_jac_xi.get(2, 0) == 0.0);\n\n  CHECK(inv_jac_A.get(2, 1) == 0.0);\n  CHECK(inv_jac_B.get(2, 1) == 0.0);\n  CHECK(inv_jac_xi.get(2, 1) == 0.0);\n\n  CHECK(inv_jac_A.get(2, 2) == inv_jacobian_22);\n  CHECK(inv_jac_B.get(2, 2) == inv_jacobian_22);\n  CHECK(inv_jac_xi.get(2, 2) == inv_jacobian_22);\n\n  const double jacobian_00 = (xb - xa) / (xB - xA);\n  const double jacobian_11 = (yb - ya) / (yB - yA);\n  const double jacobian_22 = (zb - za) / (zB - zA);\n  const auto jac_A = affine_map_xyz.jacobian(point_A);\n  const auto jac_B = affine_map_xyz.jacobian(point_B);\n  const auto jac_xi = affine_map_xyz.jacobian(point_xi);\n\n  CHECK(jac_A.get(0, 0) == jacobian_00);\n  CHECK(jac_B.get(0, 0) == jacobian_00);\n  CHECK(jac_xi.get(0, 0) == jacobian_00);\n\n  CHECK(jac_A.get(0, 1) == 0.0);\n  CHECK(jac_B.get(0, 1) == 0.0);\n  CHECK(jac_xi.get(0, 1) == 0.0);\n\n  CHECK(jac_A.get(0, 2) == 0.0);\n  CHECK(jac_B.get(0, 2) == 0.0);\n  CHECK(jac_xi.get(0, 2) == 0.0);\n\n  CHECK(jac_A.get(1, 0) == 0.0);\n  CHECK(jac_B.get(1, 0) == 0.0);\n  CHECK(jac_xi.get(1, 0) == 0.0);\n\n  CHECK(jac_A.get(1, 1) == jacobian_11);\n  CHECK(jac_B.get(1, 1) == jacobian_11);\n  CHECK(jac_xi.get(1, 1) == jacobian_11);\n\n  CHECK(jac_A.get(1, 2) == 0.0);\n  CHECK(jac_B.get(1, 2) == 0.0);\n  CHECK(jac_xi.get(1, 2) == 0.0);\n\n  CHECK(jac_A.get(2, 0) == 0.0);\n  CHECK(jac_B.get(2, 0) == 0.0);\n  CHECK(jac_xi.get(2, 0) == 0.0);\n\n  CHECK(jac_A.get(2, 1) == 0.0);\n  CHECK(jac_B.get(2, 1) == 0.0);\n  CHECK(jac_xi.get(2, 1) == 0.0);\n\n  CHECK(jac_A.get(2, 2) == jacobian_22);\n  CHECK(jac_B.get(2, 2) == jacobian_22);\n  CHECK(jac_xi.get(2, 2) == jacobian_22);\n\n  // Check Jacobians for DataVectors\n  const Mesh<3> mesh{8, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto tensor_logical_coords = logical_coordinates(mesh);\n  const std::array<DataVector, 3> logical_coords{\n      {tensor_logical_coords.get(0), tensor_logical_coords.get(1),\n       tensor_logical_coords.get(2)}};\n  const auto volume_inv_jac = affine_map_xyz.inv_jacobian(logical_coords);\n  const auto volume_jac = affine_map_xyz.jacobian(logical_coords);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      if (i == j) {\n        CHECK(volume_inv_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(),\n                         i == 0 ? inv_jacobian_00\n                                : i == 1 ? inv_jacobian_11 : inv_jacobian_22));\n        CHECK(volume_jac.get(i, j) ==\n              DataVector(\n                  logical_coords[0].size(),\n                  i == 0 ? jacobian_00 : i == 1 ? jacobian_11 : jacobian_22));\n      } else {\n        CHECK(volume_inv_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(), 0.0));\n        CHECK(volume_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(), 0.0));\n      }\n    }\n  }\n\n  CHECK_FALSE(affine_map_xyz != affine_map_xyz);\n  test_serialization(affine_map_xyz);\n\n  test_coordinate_map_argument_types(affine_map_xyz, point_xi);\n  check_if_map_is_identity(\n      CoordinateMaps::ProductOf3Maps<CoordinateMaps::Affine,\n                                     CoordinateMaps::Affine,\n                                     CoordinateMaps::Affine>{\n          CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0},\n          CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0},\n          CoordinateMaps::Affine{-1.0, 1.0, -1.0, 1.0}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.ProductMaps\", \"[Domain][Unit]\") {\n  test_product_of_2_maps();\n  test_product_of_3_maps();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Rotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <typename T>\nvoid test_rotation_3(const CoordinateMaps::Rotation<3>& three_dim_rotation_map,\n                     const std::array<T, 3>& xi_hat,\n                     const std::array<T, 3>& eta_hat,\n                     const std::array<T, 3>& zeta_hat) {\n  const std::array<T, 3> zero_logical{{0.0, 0.0, 0.0}};\n  const std::array<T, 3> zero_grid{{0.0, 0.0, 0.0}};\n  const std::array<T, 3> xi{{1.0, 0.0, 0.0}};\n  const std::array<T, 3> eta{{0.0, 1.0, 0.0}};\n  const std::array<T, 3> zeta{{0.0, 0.0, 1.0}};\n\n  const auto inv_jac = three_dim_rotation_map.inv_jacobian(zero_logical);\n  const auto jac = three_dim_rotation_map.jacobian(zero_logical);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(gsl::at(three_dim_rotation_map(zero_logical), i) ==\n          approx(gsl::at(zero_grid, i)));\n    CHECK(gsl::at(three_dim_rotation_map(xi), i) == approx(gsl::at(xi_hat, i)));\n    CHECK(gsl::at(three_dim_rotation_map(eta), i) ==\n          approx(gsl::at(eta_hat, i)));\n    CHECK(gsl::at(three_dim_rotation_map(zeta), i) ==\n          approx(gsl::at(zeta_hat, i)));\n    CHECK(gsl::at(three_dim_rotation_map.inverse(zero_grid).value(), i) ==\n          approx(gsl::at(zero_logical, i)));\n    CHECK(gsl::at(three_dim_rotation_map.inverse(xi_hat).value(), i) ==\n          approx(gsl::at(xi, i)));\n    CHECK(gsl::at(three_dim_rotation_map.inverse(eta_hat).value(), i) ==\n          approx(gsl::at(eta, i)));\n    CHECK(gsl::at(three_dim_rotation_map.inverse(zeta_hat).value(), i) ==\n          approx(gsl::at(zeta, i)));\n    CHECK(inv_jac.get(0, i) == approx(gsl::at(xi_hat, i)));\n    CHECK(inv_jac.get(1, i) == approx(gsl::at(eta_hat, i)));\n    CHECK(inv_jac.get(2, i) == approx(gsl::at(zeta_hat, i)));\n    CHECK(jac.get(i, 0) == approx(gsl::at(xi_hat, i)));\n    CHECK(jac.get(i, 1) == approx(gsl::at(eta_hat, i)));\n    CHECK(jac.get(i, 2) == approx(gsl::at(zeta_hat, i)));\n  }\n  // Check inequivalence operator\n  CHECK_FALSE(three_dim_rotation_map != three_dim_rotation_map);\n  test_serialization(three_dim_rotation_map);\n\n  test_coordinate_map_argument_types(three_dim_rotation_map, xi);\n}\n\nvoid test_rotation_2() {\n  INFO(\"Rotation 2\");\n  CoordinateMaps::Rotation<2> half_pi_rotation_map(M_PI_2);\n\n  const auto xi0 = make_array<2>(0.0);\n  const auto x0 = make_array<2>(0.0);\n\n  CHECK(half_pi_rotation_map(xi0)[0] == approx(x0[0]));\n  CHECK(half_pi_rotation_map(xi0)[1] == approx(x0[1]));\n\n  CHECK(half_pi_rotation_map.inverse(x0).value()[0] == approx(xi0[0]));\n  CHECK(half_pi_rotation_map.inverse(x0).value()[1] == approx(xi0[1]));\n\n  const std::array<double, 2> xi1{{1.0, 0.0}};\n  const std::array<double, 2> x1{{0.0, 1.0}};\n\n  CHECK(half_pi_rotation_map(xi1)[0] == approx(x1[0]));\n  CHECK(half_pi_rotation_map(xi1)[1] == approx(x1[1]));\n\n  CHECK(half_pi_rotation_map.inverse(x1).value()[0] == approx(xi1[0]));\n  CHECK(half_pi_rotation_map.inverse(x1).value()[1] == approx(xi1[1]));\n\n  const std::array<double, 2> xi2{{0.0, 1.0}};\n  const std::array<double, 2> x2{{-1.0, 0.0}};\n\n  CHECK(half_pi_rotation_map(xi2)[0] == approx(x2[0]));\n  CHECK(half_pi_rotation_map(xi2)[1] == approx(x2[1]));\n\n  CHECK(half_pi_rotation_map.inverse(x2).value()[0] == approx(xi2[0]));\n  CHECK(half_pi_rotation_map.inverse(x2).value()[1] == approx(xi2[1]));\n\n  const auto inv_jac = half_pi_rotation_map.inv_jacobian(xi2);\n  CHECK((get<0, 0>(inv_jac)) == approx(0.0));\n  CHECK((get<0, 1>(inv_jac)) == approx(1.0));\n  CHECK((get<1, 0>(inv_jac)) == approx(-1.0));\n  CHECK((get<1, 1>(inv_jac)) == approx(0.0));\n\n  const auto jac = half_pi_rotation_map.jacobian(xi2);\n  CHECK((get<0, 0>(jac)) == approx(0.0));\n  CHECK((get<0, 1>(jac)) == approx(-1.0));\n  CHECK((get<1, 0>(jac)) == approx(1.0));\n  CHECK((get<1, 1>(jac)) == approx(0.0));\n\n  // Check inequivalence operator\n  CHECK_FALSE(half_pi_rotation_map != half_pi_rotation_map);\n  test_serialization(half_pi_rotation_map);\n\n  test_coordinate_map_argument_types(half_pi_rotation_map, xi1);\n  check_if_map_is_identity(CoordinateMaps::Rotation<2>{0.0});\n  CHECK(not CoordinateMaps::Rotation<2>(M_PI_2).is_identity());\n}\n\nvoid test_rotation_3() {\n  INFO(\"Rotation 3\");\n  test_rotation_3(CoordinateMaps::Rotation<3>(0.0, 0.0, 0.0),\n                  std::array<double, 3>{{1.0, 0.0, 0.0}},\n                  std::array<double, 3>{{0.0, 1.0, 0.0}},\n                  std::array<double, 3>{{0.0, 0.0, 1.0}});\n\n  test_rotation_3(CoordinateMaps::Rotation<3>(M_PI_2, 0.0, 0.0),\n                  std::array<double, 3>{{0.0, 1.0, 0.0}},\n                  std::array<double, 3>{{-1.0, 0.0, 0.0}},\n                  std::array<double, 3>{{0.0, 0.0, 1.0}});\n\n  test_rotation_3(CoordinateMaps::Rotation<3>(M_PI, 0.0, 0.0),\n                  std::array<double, 3>{{-1.0, 0.0, 0.0}},\n                  std::array<double, 3>{{0.0, -1.0, 0.0}},\n                  std::array<double, 3>{{0.0, 0.0, 1.0}});\n\n  test_rotation_3(CoordinateMaps::Rotation<3>(-M_PI_2, 0.0, 0.0),\n                  std::array<double, 3>{{0.0, -1.0, 0.0}},\n                  std::array<double, 3>{{1.0, 0.0, 0.0}},\n                  std::array<double, 3>{{0.0, 0.0, 1.0}});\n\n  test_rotation_3(CoordinateMaps::Rotation<3>(0.0, -M_PI_2, 0.0),\n                  std::array<double, 3>{{0.0, 0.0, 1.0}},\n                  std::array<double, 3>{{0.0, 1.0, 0.0}},\n                  std::array<double, 3>{{-1.0, 0.0, 0.0}});\n\n  test_rotation_3(CoordinateMaps::Rotation<3>(0.0, M_PI_2, 0.0),\n                  std::array<double, 3>{{0.0, 0.0, -1.0}},\n                  std::array<double, 3>{{0.0, 1.0, 0.0}},\n                  std::array<double, 3>{{1.0, 0.0, 0.0}});\n  check_if_map_is_identity(CoordinateMaps::Rotation<3>{0.0, 0.0, 0.0});\n  CHECK(not CoordinateMaps::Rotation<3>{0.0, 0.0, M_PI_2}.is_identity());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Rotation\", \"[Domain][Unit]\") {\n  test_rotation_2();\n  test_rotation_3();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_SpecialMobius.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <memory>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/SpecialMobius.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_suite() {  // Set up random number generator\n  INFO(\"Suite\");\n  MAKE_GENERATOR(gen);\n  // Note: Empirically we have found that the map is accurate\n  // to 12 decimal places for mu = 0.96.\n  // test_suite demands more accuracy than 12 decimal places\n  // so we narrow the range of mu correspondingly.\n  std::uniform_real_distribution<> mu_dis(-0.85, 0.85);\n\n  const double mu = mu_dis(gen);\n  CAPTURE(mu);\n  const CoordinateMaps::SpecialMobius special_mobius_map(mu);\n  test_suite_for_map_on_sphere(special_mobius_map);\n}\n\nvoid test_map() {\n  INFO(\"Map\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> radius_dis(0, 1);\n  std::uniform_real_distribution<> phi_dis(0, 2.0 * M_PI);\n  std::uniform_real_distribution<> theta_dis(0, M_PI);\n  // Note: Empirically we have found that the map is accurate\n  // to 12 decimal places for mu = 0.96.\n  std::uniform_real_distribution<> mu_dis(-0.90, 0.90);\n  const double theta = theta_dis(gen);\n  CAPTURE(theta);\n  const double phi = phi_dis(gen);\n  CAPTURE(phi);\n  const double radius = radius_dis(gen);\n  CAPTURE(radius);\n  const double x = radius * sin(theta) * cos(phi);\n  CAPTURE(x);\n  const double y = radius * sin(theta) * sin(phi);\n  CAPTURE(y);\n  const double z = radius * cos(theta);\n  CAPTURE(z);\n  const double mu = mu_dis(gen);\n  CAPTURE(mu);\n  const std::array<double, 3> input_point{{x, y, z}};\n  const CoordinateMaps::SpecialMobius special_mobius_map(mu);\n  const auto result_point = special_mobius_map(input_point);\n  const auto& result_y = result_point[1];\n  const auto& result_z = result_point[2];\n\n  // Points inside the unit ball should remain inside the unit ball\n  // under the SpecialMobius map:\n  CHECK(magnitude(result_point) < 1.0);\n  // As a map obtained by rotation about the x-axis, angles in\n  // the y-z plane must be preserved:\n  CHECK(atan2(y, z) == approx(atan2(result_y, result_z)));\n  // The value `mu` should correspond to the x-coordinate of\n  // the preimage of the origin under this map. That is, the\n  // point with the x-coordinate `mu` (and y=z=0) should be\n  // mapped to the origin:\n  CHECK_ITERABLE_APPROX(\n      (std::array<double, 3>{{0.0, 0.0, 0.0}}),\n      (special_mobius_map(std::array<double, 3>{{mu, 0.0, 0.0}})));\n  const std::array<double, 3> plus_one{{1.0, 0.0, 0.0}};\n  const std::array<double, 3> minus_one{{-1.0, 0.0, 0.0}};\n  // The points (1,0,0) and (-1,0,0) are fixed points:\n  CHECK_ITERABLE_APPROX((plus_one), (special_mobius_map(plus_one)));\n  CHECK_ITERABLE_APPROX((minus_one), (special_mobius_map(minus_one)));\n\n  // Point at which the map is singular.\n  // Since |mu|<1, this point also has x outside [-1,1].\n  const std::array<double, 3> bad_point{{-1.0 / mu, 0.0, 0.0}};\n  CHECK_FALSE(special_mobius_map.inverse(bad_point).has_value());\n}\n\nvoid test_large_mu() {\n  INFO(\"Large mu\");\n  const double mu = 0.95;\n  CAPTURE(mu);\n  // A point on the unit sphere with x=0, y!=z:\n  const std::array<double, 3> input_point{{0.0, 0.6, 0.8}};\n  const CoordinateMaps::SpecialMobius special_mobius_map(mu);\n  const auto result_point = special_mobius_map(input_point);\n  const auto expected_input_point =\n      special_mobius_map.inverse(result_point).value();\n  const auto& result_y = result_point[1];\n  const auto& result_z = result_point[2];\n  // The SpecialMobius map should map the unit sphere to itself:\n  CHECK(magnitude(result_point) == approx(1.0));\n  // As a map obtained by rotation about the x-axis, angles in\n  // the y-z plane must be preserved:\n  CHECK(atan2(0.6, 0.8) == approx(atan2(result_y, result_z)));\n  // The map is singular for mu = 1.0, but we can guarantee\n  // that the accuracy does not suffer even for values of mu\n  // that are close to 1.0:\n  CHECK(abs(expected_input_point[0] - 0.0) < 1.e-13);\n  CHECK(abs(expected_input_point[1] - 0.6) < 1.e-13);\n  CHECK(abs(expected_input_point[2] - 0.8) < 1.e-13);\n}\n\nvoid test_is_identity() {\n  INFO(\"Is identity\");\n  check_if_map_is_identity(CoordinateMaps::SpecialMobius{0.0});\n  CHECK(not CoordinateMaps::SpecialMobius{0.1}.is_identity());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.SpecialMobius\",\n                  \"[Domain][Unit]\") {\n  test_suite();\n  test_map();\n  test_large_mu();\n  test_is_identity();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(CoordinateMaps::SpecialMobius(-2.3),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The magnitude of mu must be less than 0.96.\"));\n  CHECK_THROWS_WITH(CoordinateMaps::SpecialMobius(0.98),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The magnitude of mu must be less than 0.96.\"));\n#endif\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_SphericalToCartesianPfaffian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_map_at_point(const CoordinateMaps::SphericalToCartesianPfaffian& map,\n                       const std::array<double, 3>& source_point,\n                       const std::array<double, 3>& target_point) {\n  test_inverse_map(map, source_point);\n  if (source_point !=\n      std::array{0.0, 0.5 * M_PI, 0.0}) {  // inv jac singular at origin\n    test_coordinate_map_argument_types(map, source_point);\n    test_inv_jacobian(map, source_point);\n  }\n  CHECK_ITERABLE_APPROX(map(source_point), target_point);\n  CHECK_ITERABLE_APPROX(map.inverse(target_point).value(), source_point);\n}\n\nvoid test_map(const CoordinateMaps::SphericalToCartesianPfaffian& map) {\n  CHECK(not map.is_identity());\n  CHECK_FALSE(map != map);\n  test_serialization(map);\n  const std::array<double, 3> source_origin{{0.0, 0.5 * M_PI, 0.0}};\n  const std::array<double, 3> target_origin{{0.0, 0.0, 0.0}};\n  test_map_at_point(map, source_origin, target_origin);\n  const std::array<double, 3> source_north_pole{{0.5, 0.0, 0.0}};\n  const std::array<double, 3> target_north_pole{{0.0, 0.0, 0.5}};\n  test_map_at_point(map, source_north_pole, target_north_pole);\n  const std::array<double, 3> source_south_pole{{0.25, M_PI, 0.0}};\n  const std::array<double, 3> target_south_pole{{0.0, 0.0, -0.25}};\n  test_map_at_point(map, source_south_pole, target_south_pole);\n  test_map_at_point(map, {{0.0, M_PI_2, 0.0}}, {{0.0, 0.0, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, 0.0}}, {{1.0, 0.0, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, M_PI_4}},\n                    {{M_SQRT1_2, M_SQRT1_2, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, M_PI_2}}, {{0.0, 1.0, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, 3.0 * M_PI_4}},\n                    {{-M_SQRT1_2, M_SQRT1_2, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, M_PI}}, {{-1.0, 0.0, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, 5.0 * M_PI_4}},\n                    {{-M_SQRT1_2, -M_SQRT1_2, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, 3.0 * M_PI_2}}, {{0.0, -1.0, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, 7.0 * M_PI_4}},\n                    {{M_SQRT1_2, -M_SQRT1_2, 0.0}});\n  test_map_at_point(map, {{1.0, M_PI_2, M_PI}},\n                    {{-1.0, std::copysign(0.0, -1.0), 0.0}});\n  const Mesh<3> mesh{\n      {5, 3, 5},\n      {Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n       Spectral::Basis::SphericalHarmonic},\n      {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n       Spectral::Quadrature::Equiangular}};\n  const auto xi = logical_coordinates(mesh);\n  const std::array<DataVector, 3> source_coords{xi[0] + 2.0, xi[1], xi[2]};\n  test_inv_jacobian(map, source_coords);\n}\n\nvoid test() {\n  const CoordinateMaps::SphericalToCartesianPfaffian original_map{};\n  test_map(original_map);\n  const auto serialized_map = serialize_and_deserialize(original_map);\n  test_map(serialized_map);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.SphericalToCartesianPfaffian\",\n                  \"[Domain][Unit]\") {\n  test();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  TestHelpers::db::test_simple_tag<CoordinateMaps::Tags::CoordinateMap<\n      Dim, Frame::BlockLogical, Frame::Grid>>(\n      \"CoordinateMap(BlockLogical,Grid)\");\n  TestHelpers::db::test_simple_tag<\n      CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid, Frame::Inertial>>(\n      \"CoordinateMap(Grid,Inertial)\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Tags\", \"[Unit][Domain]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_TimeDependentHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependentHelpers.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nclass DataVector;\n\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n\nnamespace {\ntemplate <size_t Dim>\nstruct TimeDepMap {\n  static constexpr size_t dim = Dim;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> operator()(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n};\ntemplate <size_t Dim>\nstruct TimeIndepMap {\n  static constexpr size_t dim = Dim;\n\n  template <typename T>\n  std::array<tt::remove_cvref_wrap_t<T>, Dim> operator()(\n      const std::array<T, Dim>& source_coords) const;\n};\n\ntemplate <size_t Dim>\nstruct TimeDepJac {\n  static constexpr size_t dim = Dim;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jacobian(\n      const std::array<T, Dim>& source_coords, double time,\n      const std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n          functions_of_time) const;\n};\ntemplate <size_t Dim>\nstruct TimeIndepJac {\n  static constexpr size_t dim = Dim;\n\n  template <typename T>\n  tnsr::Ij<tt::remove_cvref_wrap_t<T>, Dim, Frame::NoFrame> jacobian(\n      const std::array<T, Dim>& source_coords) const;\n};\n}  // namespace\n\nnamespace domain {\nstatic_assert(is_map_time_dependent_t<TimeDepMap<1>>::value,\n              \"Failed testing is_map_time_dependent_t\");\nstatic_assert(is_map_time_dependent_t<TimeDepMap<2>>::value,\n              \"Failed testing is_map_time_dependent_t\");\nstatic_assert(is_map_time_dependent_t<TimeDepMap<3>>::value,\n              \"Failed testing is_map_time_dependent_t\");\nstatic_assert(not is_map_time_dependent_t<TimeIndepMap<1>>::value,\n              \"Failed testing is_map_time_dependent_t\");\nstatic_assert(not is_map_time_dependent_t<TimeIndepMap<2>>::value,\n              \"Failed testing is_map_time_dependent_t\");\nstatic_assert(not is_map_time_dependent_t<TimeIndepMap<3>>::value,\n              \"Failed testing is_map_time_dependent_t\");\n\nstatic_assert(is_map_time_dependent_v<TimeDepMap<1>>,\n              \"Failed testing is_map_time_dependent_v\");\nstatic_assert(is_map_time_dependent_v<TimeDepMap<2>>,\n              \"Failed testing is_map_time_dependent_v\");\nstatic_assert(is_map_time_dependent_v<TimeDepMap<3>>,\n              \"Failed testing is_map_time_dependent_v\");\nstatic_assert(not is_map_time_dependent_v<TimeIndepMap<1>>,\n              \"Failed testing is_map_time_dependent_v\");\nstatic_assert(not is_map_time_dependent_v<TimeIndepMap<2>>,\n              \"Failed testing is_map_time_dependent_v\");\nstatic_assert(not is_map_time_dependent_v<TimeIndepMap<3>>,\n              \"Failed testing is_map_time_dependent_v\");\n\nstatic_assert(is_jacobian_time_dependent_t<TimeDepJac<1>, DataVector>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_t<TimeDepJac<1>, double>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_t<TimeDepJac<2>, DataVector>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_t<TimeDepJac<2>, double>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_t<TimeDepJac<3>, DataVector>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_t<TimeDepJac<3>, double>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(\n    not is_jacobian_time_dependent_t<TimeIndepJac<1>, DataVector>::value,\n    \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_t<TimeIndepJac<1>, double>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(\n    not is_jacobian_time_dependent_t<TimeIndepJac<2>, DataVector>::value,\n    \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_t<TimeIndepJac<2>, double>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(\n    not is_jacobian_time_dependent_t<TimeIndepJac<3>, DataVector>::value,\n    \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_t<TimeIndepJac<3>, double>::value,\n              \"Failed testing is_jacobian_time_dependent_t\");\n\nstatic_assert(is_jacobian_time_dependent_v<TimeDepJac<1>, DataVector>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_v<TimeDepJac<1>, double>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_v<TimeDepJac<2>, DataVector>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_v<TimeDepJac<2>, double>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_v<TimeDepJac<3>, DataVector>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(is_jacobian_time_dependent_v<TimeDepJac<3>, double>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_v<TimeIndepJac<1>, DataVector>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_v<TimeIndepJac<1>, double>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_v<TimeIndepJac<2>, DataVector>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_v<TimeIndepJac<2>, double>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_v<TimeIndepJac<3>, DataVector>,\n              \"Failed testing is_jacobian_time_dependent_t\");\nstatic_assert(not is_jacobian_time_dependent_v<TimeIndepJac<3>, double>,\n              \"Failed testing is_jacobian_time_dependent_t\");\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_UniformCylindricalEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n#include <random>\n\n#include \"Domain/CoordinateMaps/UniformCylindricalEndcap.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n\nnamespace domain {\n\nnamespace {\n\ndouble maximum_rho_for_invertibility(double rho_max_so_far, double center_one_z,\n                                     const std::array<double, 3>& center_two,\n                                     double radius_one, double radius_two,\n                                     double theta_max_one,\n                                     double theta_max_two) {\n  const auto is_invertible = [&](const double rho) {\n    // Direction shouldn't matter here, so just choose x-axis\n    return CoordinateMaps::\n                   is_uniform_cylindrical_endcap_invertible_on_sphere_one(\n                       {{center_two[0] + rho, center_two[1], center_one_z}},\n                       center_two, radius_one, radius_two, theta_max_one,\n                       theta_max_two)\n               ? 1.0\n               : -1.0;\n  };\n  const double invertible_at_zero = is_invertible(0.0);\n  const double invertible_at_max = is_invertible(rho_max_so_far);\n  ASSERT(invertible_at_zero == 1.0, \"Map should always be invertible at rho=0\");\n  if (invertible_at_zero * invertible_at_max > 0.0) {\n    // Root is not bracketed, so map is invertible everywhere.\n    return rho_max_so_far;\n  }\n\n  try {\n    rho_max_so_far =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(is_invertible, 0.0, rho_max_so_far,\n                            invertible_at_zero, invertible_at_max, 1.e-4,\n                            1.e-4);\n    rho_max_so_far = std::max(0.0, rho_max_so_far - 1.e-4);\n  } catch (std::exception&) {\n    CHECK(false);\n  }\n  return rho_max_so_far;\n}\n\nvoid test_uniform_cylindrical_endcap() {\n  // Here sphere_two is contained in sphere_one.\n  INFO(\"UniformCylindricalEndcap\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose some random center for sphere_two\n  const std::array<double, 3> center_two = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_two);\n\n  // Choose a random radius of sphere_two, reasonably large.\n  const double radius_two = 6.0 * (unit_dis(gen) + 1.0);\n  CAPTURE(radius_two);\n\n  // These angles describe how close the z-planes can be to the\n  // centers or edges of the spheres.\n  constexpr double min_angle = 0.075;\n  constexpr double max_angle_increment = 0.45 - min_angle;\n\n  // Make sure z_plane_two intersects sphere_two on the +z side of the\n  // center. We don't allow the plane to be too close to the center or\n  // too close to the edge.\n  const double z_plane_two =\n      center_two[2] +\n      cos((min_angle + max_angle_increment * unit_dis(gen)) * M_PI) *\n          radius_two;\n  CAPTURE(z_plane_two);\n\n  // Choose z_plane_frac_one = (z_plane_one - center_one[2])/radius_one\n  const double z_plane_frac_one =\n      cos((min_angle + max_angle_increment * unit_dis(gen)) * M_PI);\n\n  // Compute the minimum allowed value of the angle alpha.\n  const double theta_max_one = acos(z_plane_frac_one);\n  const double theta_max_two = acos((z_plane_two - center_two[2]) / radius_two);\n  const double min_alpha = 1.1 * std::max(theta_max_one, theta_max_two);\n\n  // Choose a random radius of sphere_one, not too small and not larger\n  // than sphere_two.\n  const double radius_one = [&center_two, &radius_two, &z_plane_frac_one,\n                             &z_plane_two, &min_alpha, &theta_max_one,\n                             &theta_max_two, &unit_dis, &gen]() {\n    // max_radius_one_to_fit_inside_sphere_two is the largest that\n    // radius_one can be and still satisfy both\n    // 0.98 radius_two >= radius_one + |C_1-C_2| and\n    // z_plane_two >= z_plane_one + 0.04*r_2.\n    // If radius_one takes on that value, then center_one-center_two\n    // must point in the minus-z direction, and only one value of\n    // center_one[2] is allowed.\n    const double max_radius_one_to_fit_inside_sphere_two =\n        (0.94 * radius_two + z_plane_two - center_two[2]) /\n        (1.0 + z_plane_frac_one);\n\n    // max_radius_one_for_alpha is the largest that radius_one can be\n    // and still satisfy alpha > min_alpha.  For tan(min_alpha) > 0,\n    // if max_radius_one_to_fit_inside_sphere_two is satisfied, then\n    // alpha > min_alpha imposes no additional restriction on radius.\n    const double max_radius_one_for_alpha =\n        min_alpha > 0.5 * M_PI\n            ? std::numeric_limits<double>::max()\n            : std::min(radius_two * sin(theta_max_two) / sin(theta_max_one),\n                       (0.98 * radius_two - z_plane_two + center_two[2] -\n                        radius_two * sin(theta_max_two) * tan(min_alpha)) /\n                           (1.0 - cos(theta_max_one) -\n                            sin(theta_max_one) * tan(min_alpha)));\n    CHECK(max_radius_one_for_alpha > 0.0);\n    // We add an additional safety factor of 0.99 to\n    // max_radius_one_to_fit_inside_sphere_two so that radius_one\n    // doesn't get quite that large.\n    const double max_radius_one = std::min(\n        {0.98 * radius_two, 0.99 * max_radius_one_to_fit_inside_sphere_two,\n         max_radius_one_for_alpha});\n    const double min_radius_one = 0.05 * radius_two;\n    CHECK(max_radius_one >= min_radius_one);\n    return min_radius_one + unit_dis(gen) * (max_radius_one - min_radius_one);\n  }();\n  CAPTURE(radius_one);\n\n  // Choose a random z-center of sphere_one.\n  const double center_one_z =\n      [&radius_two, &radius_one, &z_plane_frac_one, &center_two, &z_plane_two,\n       &min_alpha, &theta_max_one, &theta_max_two, &unit_dis, &gen]() {\n        const double max_center_one_z_from_alpha =\n            (tan(min_alpha) <= 0.0 or\n             radius_one * sin(theta_max_one) <= radius_two * sin(theta_max_two))\n                ? std::numeric_limits<double>::max()\n                : (radius_two * sin(theta_max_two) -\n                   radius_one * sin(theta_max_one)) *\n                      tan(min_alpha);\n        // max_center_one_z comes from the restriction\n        // z_plane_two >= z_plane_one + 0.04*radius_two, and the restriction\n        // 0.98 r_2 >= r_1 + | C_1 - C_2 |\n        const double max_center_one_z = std::min(\n            {max_center_one_z_from_alpha,\n             z_plane_two - z_plane_frac_one * radius_one - 0.04 * radius_two,\n             center_two[2] + 0.98 * radius_two - radius_one});\n        // min_center_one_z comes from the restriction 0.98 r_2 >= r_1 +\n        // |C_1-C_2|\n        const double min_center_one_z =\n            center_two[2] - 0.98 * radius_two + radius_one;\n        CHECK(min_center_one_z <= max_center_one_z);\n        return min_center_one_z +\n               unit_dis(gen) * (max_center_one_z - min_center_one_z);\n      }();\n\n  // Now we can compute z_plane_one\n  const double z_plane_one = center_one_z + radius_one * z_plane_frac_one;\n  CAPTURE(z_plane_one);\n\n  // Only thing remaining are the x and y centers of sphere_one.\n  const double horizontal_distance_spheres = [&z_plane_one, &z_plane_two,\n                                              &theta_max_one, &theta_max_two,\n                                              &min_alpha, &center_one_z,\n                                              &center_two, &radius_one,\n                                              &radius_two, &unit_dis, &gen]() {\n    // Let rho be the horizontal (x-y) distance between the centers of\n    // the spheres.\n\n    // maximum rho so that sphere2 is inside of sphere 1, with a\n    // safety factor of 0.98\n    const double max_rho_sphere = sqrt(square(0.98 * radius_two - radius_one) -\n                                       square(center_one_z - center_two[2]));\n\n    // Alpha always gets smaller when rho gets larger (for other\n    // quantities fixed). So if alpha < min_alpha even when rho=0, then\n    // there is no hope.  We always fail.\n    const double alpha_if_rho_is_zero =\n        atan2(z_plane_two - z_plane_one, radius_one * sin(theta_max_one) -\n                                             radius_two * sin(theta_max_two));\n    CHECK(alpha_if_rho_is_zero >= min_alpha);\n\n    const double max_rho_alpha_first_term =\n        abs(min_alpha - 0.5 * M_PI) < 1.e-4\n            ? 0.0\n            : (z_plane_two - z_plane_one) / tan(min_alpha);\n\n    // maximum rho so that the alpha condition is satisfied\n    const double max_rho_alpha = max_rho_alpha_first_term -\n                                 radius_one * sin(theta_max_one) +\n                                 radius_two * sin(theta_max_two);\n    const double max_rho = maximum_rho_for_invertibility(\n        std::min(max_rho_sphere, max_rho_alpha), center_one_z, center_two,\n        radius_one, radius_two, theta_max_one, theta_max_two);\n    return unit_dis(gen) * max_rho;\n  }();\n\n  const double phi = angle_dis(gen);\n  const std::array<double, 3> center_one = {\n      center_two[0] + horizontal_distance_spheres * cos(phi),\n      center_two[1] + horizontal_distance_spheres * sin(phi), center_one_z};\n  CAPTURE(center_one);\n\n  CHECK(CoordinateMaps::is_uniform_cylindrical_endcap_invertible_on_sphere_one(\n      center_one, center_two, radius_one, radius_two,\n      acos((z_plane_one - center_one[2]) / radius_one),\n      acos((z_plane_two - center_two[2]) / radius_two)));\n\n  const CoordinateMaps::UniformCylindricalEndcap map(\n      center_one, center_two, radius_one, radius_two, z_plane_one, z_plane_two);\n  test_suite_for_map_on_cylinder(map, 0.0, 1.0, true);\n\n  // The following are tests that the inverse function correctly\n  // returns an invalid std::optional when called for a point that is\n  // outside the range of the map.\n\n  // Point with z less than z_plane_one.\n  CHECK_FALSE(map.inverse({{0.0,0.0,z_plane_one-1.0}}));\n\n  // Point outside sphere_two\n  CHECK_FALSE(map.inverse(\n      {{center_two[0], center_two[1], center_two[2] + 1.2 * radius_two}}));\n\n  // Point inside sphere_one (but z>z_plane_one since z_plane_one\n  // intersects sphere_one)\n  CHECK_FALSE(map.inverse(\n      {{center_one[0], center_one[1], center_one[2] + 0.99 * radius_one}}));\n\n  // Point outside the cone\n  CHECK_FALSE(map.inverse(\n      {{center_one[0], center_one[1] + radius_one * sin(theta_max_one) * 1.01,\n        z_plane_one + (z_plane_one - center_one[2]) * 1.e-5}}));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.UniformCylindricalEndcap\",\n                  \"[Domain][Unit]\") {\n  test_uniform_cylindrical_endcap();\n  CHECK(not CoordinateMaps::UniformCylindricalEndcap{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_UniformCylindricalFlatEndcap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n#include <random>\n\n#include \"Domain/CoordinateMaps/UniformCylindricalFlatEndcap.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n\nnamespace domain {\n\nnamespace {\n\ndouble maximum_rho_for_invertibility(double rho_max_so_far, double z_center_two,\n                                     const std::array<double, 3>& center_one,\n                                     double radius_one, double radius_two,\n                                     double theta_max_one) {\n  // is_invertible returns a double and not a bool because it will be\n  // used in a root-finder below.\n  const auto is_invertible = [&](const double rho) {\n    // Direction shouldn't matter here, so just choose x-axis\n    return CoordinateMaps::\n                   is_uniform_cylindrical_flat_endcap_invertible_on_sphere_one(\n                       center_one,\n                       {{center_one[0] + rho, center_one[1], z_center_two}},\n                       radius_one, radius_two, theta_max_one)\n               ? 1.0\n               : -1.0;\n  };\n  const double invertible_at_zero = is_invertible(0.0);\n  const double invertible_at_max = is_invertible(rho_max_so_far);\n  // Sanity check here... Should always be invertible at zero.\n  CHECK(invertible_at_zero == 1.0);\n  if (invertible_at_zero * invertible_at_max > 0.0) {\n    // Root is not bracketed, so map is invertible everywhere.\n    return rho_max_so_far;\n  }\n\n  try {\n    rho_max_so_far =\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(is_invertible, 0.0, rho_max_so_far,\n                            invertible_at_zero, invertible_at_max, 1.e-4,\n                            1.e-4);\n    rho_max_so_far = std::max(0.0, rho_max_so_far - 1.e-4);\n  } catch (std::exception&) {\n    CHECK(false);\n  }\n  return rho_max_so_far;\n}\n\nvoid test_uniform_cylindrical_flat_endcap() {\n  // Here sphere_two is contained in sphere_one.\n  INFO(\"UniformCylindricalFlatEndcap\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose some random center for sphere_one\n  const std::array<double, 3> center_one = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_one);\n\n  // Choose a random radius of sphere_one.\n  const double radius_one = 2.0 * (unit_dis(gen) + 1.0);\n  CAPTURE(radius_one);\n\n  // These angles describe how close the z-planes can be to the\n  // centers or edges of the spheres.\n  constexpr double min_angle = 0.075;\n  constexpr double max_angle_increment = 0.35 - min_angle;\n\n  // Make sure z_plane_two intersects sphere_two on the +z side of the\n  // center. We don't allow the plane to be too close to the center or\n  // too close to the edge.\n  const double z_plane_one =\n      center_one[2] +\n      cos((min_angle + max_angle_increment * unit_dis(gen)) * M_PI) *\n          radius_one;\n  CAPTURE(z_plane_one);\n\n  // Compute z_center_two.\n  // z_center_two > C_1^z + 1.05*radius_one.\n  // z_center_two < C_1^z + 5*radius_one.\n  const double z_center_two =\n      center_one[2] + radius_one * (unit_dis(gen) * 3.95 + 1.05);\n\n  // Compute the minimum allowed value of the angle alpha.\n  const double theta_max_one = acos((z_plane_one - center_one[2]) / radius_one);\n  const double min_alpha = 1.1 * theta_max_one;\n\n  // Compute the radius of disk_two\n  const double radius_two = [&z_plane_one, &z_center_two, &radius_one,\n                             &min_alpha, &theta_max_one, &unit_dis, &gen]() {\n    const double max_radius_two = 2.0 * sin(theta_max_one) * radius_one;\n    const double min_radius_two_if_rho_is_zero =\n        min_alpha > 0.5 * M_PI\n            ? std::numeric_limits<double>::min()\n            : sin(theta_max_one) * radius_one -\n                  (z_center_two - z_plane_one) / tan(min_alpha);\n    const double min_radius_two = std::max(\n        min_radius_two_if_rho_is_zero, 0.1 * sin(theta_max_one) * radius_one);\n    CHECK(max_radius_two >= min_radius_two);\n    return min_radius_two + unit_dis(gen) * (max_radius_two - min_radius_two);\n  }();\n  CAPTURE(radius_two);\n\n  // Compute rho = horizontal distance between the centers.\n  const double horizontal_distance = [&radius_one, &radius_two, &z_plane_one,\n                                      &z_center_two, &center_one, &min_alpha,\n                                      &theta_max_one, &unit_dis, &gen]() {\n    const double max_rho_alpha =\n        min_alpha > 0.5 * M_PI\n            ? std::numeric_limits<double>::max()\n            : (z_center_two - z_plane_one) / tan(min_alpha) -\n                  sin(theta_max_one) * radius_one + radius_two;\n    const double max_rho_offset = sin(theta_max_one) * radius_one;\n    const double max_rho = maximum_rho_for_invertibility(\n        std::min(max_rho_offset, max_rho_alpha), z_center_two, center_one,\n        radius_one, radius_two, theta_max_one);\n    return unit_dis(gen) * max_rho;\n  }();\n\n  const double phi = angle_dis(gen);\n  const std::array<double, 3> center_two = {\n      center_one[0] + horizontal_distance * cos(phi),\n      center_one[1] + horizontal_distance * sin(phi), z_center_two};\n  CAPTURE(center_two);\n\n  const CoordinateMaps::UniformCylindricalFlatEndcap map(\n      center_one, center_two, radius_one, radius_two, z_plane_one);\n  test_suite_for_map_on_cylinder(map, 0.0, 1.0, true);\n\n  // The following are tests that the inverse function correctly\n  // returns an invalid std::optional when called for a point that is\n  // outside the range of the map.\n\n  // Point with z less than z_plane_one.\n  CHECK_FALSE(map.inverse({{0.0, 0.0, z_plane_one - 1.0}}).has_value());\n\n  // Point above disk.\n  CHECK_FALSE(map.inverse({{center_two[0], center_two[1],\n                            center_two[2] + 1.2 * radius_two}})\n                  .has_value());\n\n  // Point inside sphere_one (but z>z_plane_one since z_plane_one\n  // intersects sphere_one)\n  CHECK_FALSE(map.inverse({{center_one[0], center_one[1],\n                            center_one[2] + 0.99 * radius_one}})\n                  .has_value());\n\n  // Point outside the cone\n  CHECK_FALSE(\n      map.inverse({{center_one[0],\n                    center_one[1] + radius_one * sin(theta_max_one) * 1.01,\n                    z_plane_one + (z_plane_one - center_one[2]) * 1.e-5}})\n          .has_value());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.UniformCylindricalFlatEndcap\",\n                  \"[Domain][Unit]\") {\n  test_uniform_cylindrical_flat_endcap();\n  CHECK(not CoordinateMaps::UniformCylindricalFlatEndcap{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_UniformCylindricalSide.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n#include <random>\n\n#include \"Domain/CoordinateMaps/UniformCylindricalSide.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n\nnamespace domain {\n\nnamespace {\n\nvoid test_uniform_cylindrical_side_planes_equal(\n    const bool flip_z_axis = false) {\n  INFO(\"UniformCylindricalSidePlanesEqual\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose some random center for sphere_two\n  const std::array<double, 3> center_two = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_two);\n\n  // Choose a random radius of sphere_two, reasonably large.\n  const double radius_two = 6.0 * (unit_dis(gen) + 1.0);\n  CAPTURE(radius_two);\n\n  // Choose z_plane_frac_plus_one=(z_plane_plus_one-center_one[2])/radius_one\n  // Note here that max_angle_one_plus is > 0.5, so that\n  // z_plane_plus_one can be at a lower value of z than center_one[2],\n  // and thus z_plane_frac_plus_one may be positive or negative.\n  const double min_angle_one_plus = 0.15;\n  const double max_angle_one_plus = 0.59;\n  const double angle_one_plus =\n      min_angle_one_plus +\n      (max_angle_one_plus - min_angle_one_plus) * unit_dis(gen);\n  const double z_plane_frac_plus_one = cos(angle_one_plus * M_PI);\n\n  // Choose z_plane_frac_minus_one=(z_plane_minus_one-center_one[2])/radius_one\n  // (note that this quantity is < 0).\n  const double min_angle_one_minus = 0.15;\n  const double max_angle_one_minus = angle_one_plus > 0.4 ? 0.3 : 0.4;\n  // Note that we deliberately choose max_angle_one_plus +\n  // max_angle_one_minus < 1.  This ensures that z_plane_frac_plus_one\n  // > z_plane_frac_minus_one, which is important for\n  // max_radius_one_planes below.\n  const double z_plane_frac_minus_one =\n      -cos((min_angle_one_minus +\n            (max_angle_one_minus - min_angle_one_minus) * unit_dis(gen)) *\n           M_PI);\n\n  // Choose an angle for the positive z-plane\n  // Don't go too close to the edge if angle_one_plus is large.\n  const double min_angle_shared = angle_one_plus > 0.4 ? 0.25 : 0.15;\n  const double max_angle_shared = 0.75;\n  const double z_plane_plus_two =\n      center_two[2] +\n      cos((min_angle_shared +\n           (max_angle_shared - min_angle_shared) * unit_dis(gen)) *\n          M_PI) *\n          radius_two;\n  const double z_plane_plus_one = z_plane_plus_two;\n  CAPTURE(z_plane_plus_two);\n  CAPTURE(z_plane_plus_one);\n\n  // Choose an angle for the negative z-plane for sphere 2\n  // Note that min_angle_two must be < 1-max_angle_shared\n  // (otherwise we cannot fit a sphere_one)\n  // max_angle_two comes from the requirement that\n  // z_plane_minus_two < z_plane_plus_two - 0.18 radius_two.\n  const double min_angle_two = 0.15;\n  const double max_angle_two =\n      center_two[2] < z_plane_plus_two\n          ? 0.4\n          : std::min(0.4, acos((center_two[2] - z_plane_plus_two) / radius_two +\n                               0.18) /\n                              M_PI);\n  CHECK(min_angle_two < max_angle_two);\n\n  const double z_plane_minus_two =\n      center_two[2] -\n      cos((min_angle_two + (max_angle_two - min_angle_two) * unit_dis(gen)) *\n          M_PI) *\n          radius_two;\n  CAPTURE(z_plane_minus_two);\n\n  // Choose radius of sphere_one.\n  const double radius_one = [&z_plane_frac_plus_one, &z_plane_frac_minus_one,\n                             &radius_two, &center_two, &z_plane_plus_one,\n                             &z_plane_minus_two, &unit_dis, &gen]() {\n    // max_radius_one_planes is determined by the condition\n    // z_plane_minus_one >= z_plane_minus_two + 0.03 * radius_two\n    // The expression below is derived by\n    // evaluating z_plane_minus_one using the formula for\n    // z_plane_frac_minus_one, and eliminating center_one[2] using the formula\n    // for z_plane_frac_plus_one.\n    const double max_radius_one_planes =\n        (z_plane_plus_one - z_plane_minus_two - 0.03 * radius_two) /\n        (z_plane_frac_plus_one - z_plane_frac_minus_one);\n\n    // max_radius_one_fit is determined by the condition that\n    // 0.98 radius_two >= radius_one + |C_1-C_2|,\n    // eliminating center_one[2] using the formula\n    // for z_plane_frac_plus_one.\n    const double max_radius_one_fit =\n        std::min((0.98 * radius_two - center_two[2] + z_plane_plus_one) /\n                     (1.0 + z_plane_frac_plus_one),\n                 (0.98 * radius_two + center_two[2] - z_plane_plus_one) /\n                     (1.0 - z_plane_frac_plus_one));\n\n    // Compute the minimum allowed value of the angle alpha_minus.\n    // (these quantities are measured from zero; note the minus signs)\n    const double theta_max_minus_one = acos(-z_plane_frac_minus_one);\n    const double theta_max_minus_two =\n        acos(-(z_plane_minus_two - center_two[2]) / radius_two);\n    const double min_alpha_minus = 1.1 * theta_max_minus_one;\n    // max_radius_one_from_alpha comes from the restriction\n    // that alpha > min_alpha_minus\n    // and the expression for z_plane_frac_plus_one (used to eliminate\n    // center_one[2]) and the expression for z_plane_frac_minus_one.\n    const double max_radius_one_from_alpha =\n        (z_plane_plus_one - z_plane_minus_two +\n         radius_two * tan(min_alpha_minus) * sin(theta_max_minus_two)) /\n        (tan(min_alpha_minus) * sin(theta_max_minus_one) +\n         z_plane_frac_plus_one - z_plane_frac_minus_one);\n    const double max_radius_one = std::min(\n        {max_radius_one_planes, max_radius_one_fit, max_radius_one_from_alpha});\n    const double min_radius_one = 0.08 * radius_two;\n    CHECK(max_radius_one >= min_radius_one);\n    return min_radius_one + unit_dis(gen) * (max_radius_one - min_radius_one);\n  }();\n  CAPTURE(radius_one);\n\n  const std::array<double, 3> center_one = {\n      center_two[0], center_two[1],\n      z_plane_plus_two - z_plane_frac_plus_one * radius_one};\n  CAPTURE(center_one);\n\n  const double z_plane_minus_one =\n      center_one[2] + radius_one * z_plane_frac_minus_one;\n  CAPTURE(z_plane_minus_one);\n\n  if (flip_z_axis) {\n    // Here we test the map with z_plane_minus_one equal to\n    // z_plane_minus_two.  We do this by simply flipping the map\n    // parameters about the z axis (and exchanging parameters named\n    // plus and minus).  This way we don't need to rewrite the entire\n    // test for z_plane_minus_one==z_plane_minus_two.\n    const CoordinateMaps::UniformCylindricalSide map(\n        {{center_one[0], center_one[1], -center_one[2]}},\n        {{center_two[0], center_two[1], -center_two[2]}},\n        radius_one,\n        radius_two, -z_plane_minus_one, -z_plane_plus_one, -z_plane_minus_two,\n        -z_plane_plus_two);\n    test_suite_for_map_on_cylinder(map, 1.0, 2.0, true, true);\n  } else {\n    const CoordinateMaps::UniformCylindricalSide map(\n        center_one, center_two, radius_one, radius_two, z_plane_plus_one,\n        z_plane_minus_one, z_plane_plus_two, z_plane_minus_two);\n    test_suite_for_map_on_cylinder(map, 1.0, 2.0, true, true);\n  }\n}\n\nvoid test_uniform_cylindrical_side() {\n  INFO(\"UniformCylindricalSide\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n  std::uniform_real_distribution<> interval_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> angle_dis(0.0, 2.0 * M_PI);\n\n  // Choose some random center for sphere_two\n  const std::array<double, 3> center_two = {\n      interval_dis(gen), interval_dis(gen), interval_dis(gen)};\n  CAPTURE(center_two);\n\n  // Choose a random radius of sphere_two, reasonably large.\n  const double radius_two = 6.0 * (unit_dis(gen) + 1.0);\n  CAPTURE(radius_two);\n\n  // These angles describe how close the z-planes can be to the\n  // centers or edges of the spheres.\n  const double min_angle = 0.15;\n  const double max_angle = 0.4;\n\n  // Make sure z_plane_plus_two intersects sphere_two on the +z side of the\n  // center. We don't allow the plane to be too close to the center or\n  // too close to the edge.\n  const double z_plane_plus_two =\n      center_two[2] +\n      cos((min_angle + (max_angle - min_angle) * unit_dis(gen)) * M_PI) *\n          radius_two;\n  CAPTURE(z_plane_plus_two);\n\n  // Make sure z_plane_minus_two intersects sphere_two on the -z side of the\n  // center. We don't allow the plane to be too close to the center or\n  // too close to the edge.\n  const double z_plane_minus_two =\n      center_two[2] -\n      cos((min_angle + (max_angle - min_angle) * unit_dis(gen)) * M_PI) *\n          radius_two;\n  CAPTURE(z_plane_minus_two);\n\n  // Choose z_plane_frac_plus_one=(z_plane_plus_one-center_one[2])/radius_one\n  const double z_plane_frac_plus_one =\n      cos((min_angle + (max_angle - min_angle) * unit_dis(gen)) * M_PI);\n\n  // Choose\n  // z_plane_frac_minus_one=(z_plane_minus_one-center_one[2])/radius_one (note\n  // that this quantity is < 0).\n  const double z_plane_frac_minus_one =\n      -cos((min_angle + (max_angle - min_angle) * unit_dis(gen)) * M_PI);\n\n  // Compute the minimum allowed value of the angle alpha_plus.\n  const double theta_max_plus_one = acos(z_plane_frac_plus_one);\n  const double theta_max_plus_two =\n      acos((z_plane_plus_two - center_two[2]) / radius_two);\n  const double min_alpha_plus =\n      1.1 * std::max(theta_max_plus_one, theta_max_plus_two);\n\n  // Compute the minimum allowed value of the angle alpha_minus.\n  // (these quantities are measured from zero; note the minus signs)\n  const double theta_max_minus_one = acos(-z_plane_frac_minus_one);\n  const double theta_max_minus_two =\n      acos(-(z_plane_minus_two - center_two[2]) / radius_two);\n  const double min_alpha_minus =\n      1.1 * std::max(theta_max_minus_one, theta_max_minus_two);\n\n  // Choose a random radius of sphere_one, not too small and not larger\n  // than sphere_two.\n  const double radius_one = [&center_two, &radius_two, &z_plane_frac_plus_one,\n                             &z_plane_plus_two, &min_alpha_plus,\n                             &theta_max_plus_one, &theta_max_plus_two,\n                             &z_plane_frac_minus_one, &z_plane_minus_two,\n                             &min_alpha_minus, &theta_max_minus_one,\n                             &theta_max_minus_two, &unit_dis, &gen]() {\n    const double z_upper_separation = 0.03;\n    const double z_lower_separation = 0.03;\n    // max_radius_one_to_fit_inside_sphere_two_plus is the largest that\n    // radius_one can be and still satisfy both\n    // 0.98 radius_two >= radius_one + |C_1-C_2| and\n    // z_plane_plus_two >= z_plane_plus_one + z_upper_separation*radius_two\n    // when center_one_z is unknown and z_plane_plus_one is unknown\n    // (but the quantity z_plane_frac_plus_one is known).\n    // This value comes about when center_one and center_two have the\n    // same x and y components, and when center_one_z < center_two_z, and\n    // when center_one_z takes on its largest possible value consistent with\n    // 0.98 radius_two >= radius_one + |C_1-C_2|.\n    // The latter condition is C^z_1 >= radius_one + C^z_2 - 0.98 radius_two.\n    //\n    // Similarly, max_radius_one_to_fit_inside_sphere_two_minus\n    // is the largest that radius_one can be and still satisfy both\n    // 0.98 radius_two >= radius_one + |C_1-C_2| and\n    // z_plane_minus_two <= z_plane_minus_one - z_lower_separation*radius_two\n    // when center_one_z is unknown and z_plane_minus_one is unknown\n    // (but the quantity z_plane_frac_minus_one is known).\n    // This value comes about when center_one and center_two have the\n    // same x and y components, and when center_one_z > center_two_z, and\n    // when center_one_z takes on its smallest possible value consistent with\n    // 0.98 radius_two >= radius_one + |C_1-C_2|.\n    // The latter condition is C^z_1 <= -radius_one + C^z_2 + 0.98 radius_two.\n    //\n    // Here we take the min of both of the above quantities.\n    const double max_radius_one_to_fit_inside_sphere_two =\n        std::min((z_plane_plus_two - center_two[2] +\n                  (0.98 - z_upper_separation) * radius_two) /\n                     (z_plane_frac_plus_one + 1.0),\n                 (z_plane_minus_two - center_two[2] -\n                  (0.98 - z_lower_separation) * radius_two) /\n                     (z_plane_frac_minus_one - 1.0));\n    // max_radius_one_for_alpha_minus is the largest that radius_one can be\n    // and still satisfy alpha_minus > min_alpha_minus.  For\n    // tan(min_alpha_minus) > 0, if max_radius_one_to_fit_inside_sphere_two is\n    // satisfied, then alpha_minus > min_alpha_minus imposes no additional\n    // restriction on radius.\n    const double max_radius_one_for_alpha_plus =\n        min_alpha_plus > 0.5 * M_PI\n            ? std::numeric_limits<double>::max()\n            : std::min(\n                  radius_two * sin(theta_max_plus_two) /\n                      sin(theta_max_plus_one),\n                  (0.98 * radius_two - z_plane_plus_two + center_two[2] -\n                   radius_two * sin(theta_max_plus_two) * tan(min_alpha_plus)) /\n                      (1.0 - cos(theta_max_plus_one) -\n                       sin(theta_max_plus_one) * tan(min_alpha_plus)));\n    const double max_radius_one_for_alpha_minus =\n        min_alpha_minus > 0.5 * M_PI\n            ? std::numeric_limits<double>::max()\n            : std::min(radius_two * sin(theta_max_minus_two) /\n                           sin(theta_max_minus_one),\n                       (0.98 * radius_two + z_plane_minus_two - center_two[2] -\n                        radius_two * sin(theta_max_minus_two) *\n                            tan(min_alpha_minus)) /\n                           (1.0 - cos(theta_max_minus_one) -\n                            sin(theta_max_minus_one) * tan(min_alpha_minus)));\n    CHECK(max_radius_one_for_alpha_minus > 0.0);\n    CHECK(max_radius_one_for_alpha_plus > 0.0);\n    // max_radius_one_to_fit_between_plane_twos is the maximum radius_one\n    // that satisfies the two conditions\n    // z_plane_plus_two >= z_plane_plus_one + z_upper_separation*radius_two\n    // z_plane_minus_two <= z_plane_minus_one - z_lower_separation*radius_two\n    //\n    // This condition is derived from noting that\n    // z_plane_plus_one = center_one[2]+radius_one*z_plane_frac_plus_one\n    // and z_plane_minus_one = center_one[2]+radius_one*z_plane_frac_minus_one\n    // (recall z_plane_frac_minus_one is negative)\n    // and noting that the max value of center_one[2] is >= the min value\n    // of center_one[2].\n    const double max_radius_one_to_fit_between_plane_twos =\n        (z_plane_plus_two - z_plane_minus_two -\n         (z_upper_separation + z_lower_separation) * radius_two) /\n        (z_plane_frac_plus_one - z_plane_frac_minus_one);\n    CHECK(max_radius_one_to_fit_between_plane_twos > 0.0);\n\n    double max_radius_one = std::min(\n        {0.98 * radius_two, max_radius_one_to_fit_inside_sphere_two,\n         max_radius_one_to_fit_between_plane_twos,\n         max_radius_one_for_alpha_minus, max_radius_one_for_alpha_plus});\n    double min_radius_one = 0.08 * radius_two;\n\n    CHECK(max_radius_one >= min_radius_one);\n    return min_radius_one + unit_dis(gen) * (max_radius_one - min_radius_one);\n  }();\n  CAPTURE(radius_one);\n\n  // Choose a random z-center of sphere_one.\n  const double center_one_z = [&radius_two, &radius_one, &center_two,\n                               &z_plane_frac_plus_one, &z_plane_plus_two,\n                               &min_alpha_plus, &theta_max_plus_one,\n                               &theta_max_plus_two, &z_plane_frac_minus_one,\n                               &z_plane_minus_two, &min_alpha_minus,\n                               &theta_max_minus_one, &theta_max_minus_two,\n                               &unit_dis, &gen]() {\n    const double max_center_one_z_from_alpha_plus =\n        (tan(min_alpha_plus) <= 0.0 or radius_one * sin(theta_max_plus_one) <=\n                                           radius_two * sin(theta_max_plus_two))\n            ? std::numeric_limits<double>::max()\n            : (radius_two * sin(theta_max_plus_two) -\n               radius_one * sin(theta_max_plus_one)) *\n                  tan(min_alpha_plus);\n    // Note minus sign in min_center_one_z_from_alpha_minus\n    const double min_center_one_z_from_alpha_minus =\n        (tan(min_alpha_minus) <= 0.0 or\n         radius_one * sin(theta_max_minus_one) <=\n             radius_two * sin(theta_max_minus_two))\n            ? std::numeric_limits<double>::lowest()\n            : -(radius_two * sin(theta_max_minus_two) -\n                radius_one * sin(theta_max_minus_one)) *\n                  tan(min_alpha_minus);\n    CHECK(min_center_one_z_from_alpha_minus <=\n          max_center_one_z_from_alpha_plus);\n    // max_center_one_z comes from the restriction\n    // z_plane_plus_two >= z_plane_plus_one + 0.03*radius_two,\n    // and the restriction\n    // 0.98 r_2 >= r_1 + | C_1 - C_2 |\n    // and the restriction\n    // C^z_1 < C^z_2 + r_1 + r_2/5\n    // which is designed to not allow a tiny sphere 1 at the edge of\n    // a large sphere 2.\n    const double max_center_one_z =\n        std::min({max_center_one_z_from_alpha_plus,\n                  z_plane_plus_two - z_plane_frac_plus_one * radius_one -\n                      0.03 * radius_two,\n                  center_two[2] + radius_one + 0.2 * radius_two,\n                  center_two[2] + 0.98 * radius_two - radius_one});\n    // min_center_one_z comes from the restriction\n    // z_plane_minus_two <= z_plane_minus_one - 0.03*radius_two,\n    // and the restriction\n    // 0.98 r_2 >= r_1 + |C_1 - C_2 |\n    // and the restriction\n    // C^z_1 > C^z_2 - r_1 - r_2/5\n    // which is designed to not allow a tiny sphere 1 at the edge of\n    // a large sphere 2.\n    const double min_center_one_z =\n        std::max({min_center_one_z_from_alpha_minus,\n                  z_plane_minus_two - z_plane_frac_minus_one * radius_one +\n                      0.03 * radius_two,\n                  center_two[2] - radius_one - 0.2 * radius_two,\n                  center_two[2] - 0.98 * radius_two + radius_one});\n    CHECK(min_center_one_z <= max_center_one_z);\n    return min_center_one_z +\n           unit_dis(gen) * (max_center_one_z - min_center_one_z);\n  }();\n  CAPTURE(center_one_z);\n\n  // Now we can compute z_plane_plus_one and z_plane_minus_one\n  const double z_plane_plus_one =\n      center_one_z + radius_one * z_plane_frac_plus_one;\n  CAPTURE(z_plane_plus_one);\n  const double z_plane_minus_one =\n      center_one_z + radius_one * z_plane_frac_minus_one;\n  CAPTURE(z_plane_minus_one);\n\n  // Only thing remaining are the x and y centers of sphere_one.\n  const double horizontal_distance_spheres =\n      [&z_plane_plus_one, &z_plane_plus_two, &theta_max_plus_one,\n       &theta_max_plus_two, &min_alpha_plus, &z_plane_minus_one,\n       &z_plane_minus_two, &theta_max_minus_one, &theta_max_minus_two,\n       &min_alpha_minus, &center_one_z, &center_two, &radius_one, &radius_two,\n       &unit_dis, &gen]() {\n        // Let rho be the horizontal (x-y) distance between the centers of\n        // the spheres.\n\n        // maximum rho obeying the condition\n        // 0.98 R_2 <= R_1 + |C_1-C_2|\n        const double max_rho_sphere =\n            sqrt(square(0.98 * radius_two - radius_one) -\n                 square(center_one_z - center_two[2]));\n\n        // We don't want a tiny sphere 1 all the way on the edge of sphere 2.\n        // So demand that at least some of sphere_one lies along the polar\n        // axis of sphere_two.\n        const double max_rho_sphere2 = radius_one;\n\n        // We demand that the edge of sphere 1 is not too close to the\n        // edge of sphere 2.  But don't let max_rho_sphere3 be negative.\n        const double max_rho_sphere3 =\n            std::max(0.0, radius_two * 0.95 - radius_one);\n\n        // Alpha always gets smaller when rho gets larger (for other\n        // quantities fixed). So if alpha < min_alpha even when rho=0, then\n        // there is no hope.  We always fail.\n        const double alpha_plus_if_rho_is_zero =\n            atan2(z_plane_plus_two - z_plane_plus_one,\n                  radius_one * sin(theta_max_plus_one) -\n                      radius_two * sin(theta_max_plus_two));\n        CHECK(alpha_plus_if_rho_is_zero >= min_alpha_plus);\n        const double alpha_minus_if_rho_is_zero =\n            atan2(z_plane_minus_one - z_plane_minus_two,\n                  radius_one * sin(theta_max_minus_one) -\n                      radius_two * sin(theta_max_minus_two));\n        CHECK(alpha_minus_if_rho_is_zero >= min_alpha_minus);\n\n        const double max_rho_alpha_plus_first_term =\n            abs(min_alpha_plus - 0.5 * M_PI) < 1.e-4\n                ? 0.0\n                : (z_plane_plus_two - z_plane_plus_one) / tan(min_alpha_plus);\n\n        const double max_rho_alpha_minus_first_term =\n            abs(min_alpha_minus - 0.5 * M_PI) < 1.e-4\n                ? 0.0\n                : (z_plane_minus_one - z_plane_minus_two) /\n                      tan(min_alpha_minus);\n\n        // maximum rho so that the alpha condition is satisfied\n        const double max_rho_alpha_plus = max_rho_alpha_plus_first_term -\n                                          radius_one * sin(theta_max_plus_one) +\n                                          radius_two * sin(theta_max_plus_two);\n        const double max_rho_alpha_minus =\n            max_rho_alpha_minus_first_term -\n            radius_one * sin(theta_max_minus_one) +\n            radius_two * sin(theta_max_minus_two);\n        const double max_rho =\n            std::min({max_rho_sphere, max_rho_sphere2, max_rho_sphere3,\n                      max_rho_alpha_plus, max_rho_alpha_minus});\n        CHECK(max_rho >= 0.0);\n        return unit_dis(gen) * max_rho;\n      }();\n\n  const double phi = angle_dis(gen);\n  const std::array<double, 3> center_one = {\n      center_two[0] + horizontal_distance_spheres * cos(phi),\n      center_two[1] + horizontal_distance_spheres * sin(phi), center_one_z};\n  CAPTURE(center_one);\n\n  const CoordinateMaps::UniformCylindricalSide map(\n      center_one, center_two, radius_one, radius_two, z_plane_plus_one,\n      z_plane_minus_one, z_plane_plus_two, z_plane_minus_two);\n  test_suite_for_map_on_cylinder(map, 1.0, 2.0, true, true);\n\n  // The following are tests that the inverse function correctly\n  // returns an invalid std::optional when called for a point that is\n  // outside the range of the map.\n\n  // Point with z > z_plane_plus_two.\n  CHECK_FALSE(map.inverse({{0.0, 0.0, z_plane_plus_two + 1.0}}).has_value());\n\n  // Point with z < z_plane_minus_two.\n  CHECK_FALSE(map.inverse({{0.0, 0.0, z_plane_minus_two - 1.0}}).has_value());\n\n  // Point outside sphere_two\n  CHECK_FALSE(map.inverse({{center_two[0], center_two[1] + 1.01 * radius_two,\n                            center_two[2]}})\n                  .has_value());\n\n  // Point inside sphere_one (but z_plane_minus_one<z<z_plane_plus_one\n  // intersects sphere_one)\n  CHECK_FALSE(map.inverse({{center_one[0], center_one[1],\n                            0.5 * (z_plane_plus_one + z_plane_minus_one)}})\n                  .has_value());\n\n  // Point inside the northern cone\n  if (z_plane_plus_two != z_plane_plus_one) {\n    CHECK_FALSE(map.inverse({{center_two[0],\n                              center_two[1] +\n                                  radius_two * sin(theta_max_plus_two) * 0.98,\n                              z_plane_plus_two -\n                                  (z_plane_plus_two - center_two[2]) * 1.e-5}})\n                    .has_value());\n  }\n\n  // Point inside the southern cone\n  if (z_plane_minus_two != z_plane_minus_one) {\n    CHECK_FALSE(map.inverse({{center_two[0],\n                              center_two[1] +\n                                  radius_two * sin(theta_max_minus_two) * 0.98,\n                              z_plane_minus_two +\n                                  (center_two[2] - z_plane_minus_two) * 1.e-5}})\n                    .has_value());\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.UniformCylindricalSide\",\n                  \"[Domain][Unit]\") {\n  test_uniform_cylindrical_side();\n  test_uniform_cylindrical_side_planes_equal(false);\n  test_uniform_cylindrical_side_planes_equal(true);\n  CHECK(not CoordinateMaps::UniformCylindricalSide{}.is_identity());\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Wedge2D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\nnamespace {\nusing Wedge2D = CoordinateMaps::Wedge<2>;\n\nvoid test_wedge2d_all_orientations(const bool with_equiangular_map) {\n  INFO(\"Wedge2d all orientations\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1, 1);\n  std::uniform_real_distribution<> unit_dis(0, 1);\n  std::uniform_real_distribution<> inner_dis(1, 3);\n  std::uniform_real_distribution<> outer_dis(4, 7);\n  std::uniform_real_distribution<> cube_half_length_dist(8, 10);\n  std::uniform_real_distribution<> offset_coord_dist(-1, 1);\n\n  // Check that points on the corners of the reference square map to the correct\n  // corners of the wedge.\n  const std::array<double, 2> lower_right_corner{{1.0, -1.0}};\n  const std::array<double, 2> upper_right_corner{{1.0, 1.0}};\n  CAPTURE(gsl::at(lower_right_corner, 0));\n  CAPTURE(gsl::at(upper_right_corner, 0));\n  CAPTURE(gsl::at(lower_right_corner, 1));\n  CAPTURE(gsl::at(upper_right_corner, 1));\n\n  const double random_inner_radius_upper_xi = inner_dis(gen);\n  CAPTURE(random_inner_radius_upper_xi);\n  const double random_inner_radius_upper_eta = inner_dis(gen);\n  CAPTURE(random_inner_radius_upper_eta);\n  const double random_inner_radius_lower_xi = inner_dis(gen);\n  CAPTURE(random_inner_radius_lower_xi);\n  const double random_inner_radius_lower_eta = inner_dis(gen);\n  CAPTURE(random_inner_radius_lower_eta);\n  const double random_outer_radius_upper_xi = outer_dis(gen);\n  CAPTURE(random_outer_radius_upper_xi);\n  const double random_outer_radius_upper_eta = outer_dis(gen);\n  CAPTURE(random_outer_radius_upper_eta);\n  const double random_outer_radius_lower_xi = outer_dis(gen);\n  CAPTURE(random_outer_radius_lower_xi);\n  const double random_outer_radius_lower_eta = outer_dis(gen);\n  CAPTURE(random_outer_radius_lower_eta);\n\n  const Wedge2D map_upper_xi(\n      random_inner_radius_upper_xi, random_outer_radius_upper_xi, 0.0, 1.0,\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n      with_equiangular_map);\n  const Wedge2D map_upper_eta(\n      random_inner_radius_upper_eta, random_outer_radius_upper_eta, 0.0, 1.0,\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n      with_equiangular_map);\n  const Wedge2D map_lower_xi(\n      random_inner_radius_lower_xi, random_outer_radius_lower_xi, 0.0, 1.0,\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n      with_equiangular_map);\n  const Wedge2D map_lower_eta(\n      random_inner_radius_lower_eta, random_outer_radius_lower_eta, 0.0, 1.0,\n      OrientationMap<2>{std::array<Direction<2>, 2>{\n          {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n      with_equiangular_map);\n  CHECK(map_lower_eta != map_lower_xi);\n  CHECK(map_upper_eta != map_lower_eta);\n  CHECK(map_lower_eta != map_upper_xi);\n\n  CHECK(map_upper_xi(lower_right_corner)[0] ==\n        approx(random_outer_radius_upper_xi / sqrt(2.0)));\n  CHECK(map_upper_eta(lower_right_corner)[1] ==\n        approx(-random_outer_radius_upper_eta / sqrt(2.0)));\n  CHECK(map_lower_xi(upper_right_corner)[0] ==\n        approx(-random_outer_radius_lower_xi / sqrt(2.0)));\n  CHECK(map_lower_eta(upper_right_corner)[1] ==\n        approx(random_outer_radius_lower_eta / sqrt(2.0)));\n\n  // Check that random points on the edges of the reference square map to the\n  // correct edges of the wedge.\n  const std::array<double, 2> random_right_edge{{1.0, real_dis(gen)}};\n  const std::array<double, 2> random_left_edge{{-1.0, real_dis(gen)}};\n\n  CHECK(magnitude(map_upper_xi(random_right_edge)) ==\n        approx(random_outer_radius_upper_xi));\n  CHECK(map_upper_xi(random_left_edge)[0] ==\n        approx(random_inner_radius_upper_xi / sqrt(2.0)));\n  CHECK(magnitude(map_upper_eta(random_right_edge)) ==\n        approx(random_outer_radius_upper_eta));\n  CHECK(map_upper_eta(random_left_edge)[1] ==\n        approx(-random_inner_radius_upper_eta / sqrt(2.0)));\n  CHECK(magnitude(map_lower_xi(random_right_edge)) ==\n        approx(random_outer_radius_lower_xi));\n  CHECK(map_lower_xi(random_left_edge)[0] ==\n        approx(-random_inner_radius_lower_xi / sqrt(2.0)));\n  CHECK(magnitude(map_lower_eta(random_right_edge)) ==\n        approx(random_outer_radius_lower_eta));\n  CHECK(map_lower_eta(random_left_edge)[1] ==\n        approx(random_inner_radius_lower_eta / sqrt(2.0)));\n\n  const double inner_radius = inner_dis(gen);\n  CAPTURE(inner_radius);\n  const double cube_half_length = cube_half_length_dist(gen);\n  CAPTURE(cube_half_length);\n\n  using WedgeHalves = Wedge2D::WedgeHalves;\n  const std::array<WedgeHalves, 3> possible_halves = {\n      {WedgeHalves::UpperOnly, WedgeHalves::LowerOnly, WedgeHalves::Both}};\n\n  const std::array<double, 2> zero_offset{{0.0, 0.0}};\n  const std::array<std::array<double, 2>, 2> focal_offsets = {\n      {zero_offset, {{offset_coord_dist(gen), offset_coord_dist(gen)}}}};\n  for (OrientationMapIterator<2> map_i{}; map_i; ++map_i) {\n    if (get(determinant(discrete_rotation_jacobian(*map_i))) < 0.0) {\n      continue;\n    }\n    const auto& orientation = map_i();\n    CAPTURE(orientation);\n    for (const auto& halves : possible_halves) {\n      CAPTURE(halves);\n      for (const auto radial_distribution :\n           {CoordinateMaps::Distribution::Linear,\n            CoordinateMaps::Distribution::Logarithmic,\n            CoordinateMaps::Distribution::Inverse}) {\n        CAPTURE(radial_distribution);\n        for (const auto& focal_offset : focal_offsets) {\n          CAPTURE(focal_offset);\n          if (focal_offset == zero_offset) {\n            // test centered Wedge\n            {\n              const double outer_radius = outer_dis(gen);\n              CAPTURE(outer_radius);\n              // circularity != 1.0 is only supported for Wedges where the\n              // radial distribution is linear and there is no focal offset\n              const bool use_random_circularity =\n                  (radial_distribution == CoordinateMaps::Distribution::Linear);\n              const double inner_circularity =\n                  use_random_circularity ? unit_dis(gen) : 1.0;\n              CAPTURE(inner_circularity);\n              const double outer_circularity =\n                  use_random_circularity ? unit_dis(gen) : 1.0;\n              CAPTURE(outer_circularity);\n\n              test_suite_for_map_on_unit_cube(\n                  Wedge2D{inner_radius, outer_radius, inner_circularity,\n                          outer_circularity, orientation, with_equiangular_map,\n                          halves, radial_distribution});\n            }\n            {\n              // test spherical offset Wedge that is centered\n              const double outer_radius = outer_dis(gen);\n              CAPTURE(outer_radius);\n\n              const Wedge2D offset_wedge_with_no_offset(\n                  inner_radius, outer_radius, cube_half_length, focal_offset,\n                  orientation, with_equiangular_map, halves,\n                  radial_distribution);\n              test_suite_for_map_on_unit_cube(offset_wedge_with_no_offset);\n\n              // make sure offset wedge with no offset reduces to centered wedge\n              const Wedge2D expected_centered_wedge(\n                  inner_radius, outer_radius, 1.0, 1.0, orientation,\n                  with_equiangular_map, halves, radial_distribution);\n              check_if_maps_are_equal(\n                  domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(\n                      offset_wedge_with_no_offset),\n                  domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(\n                      expected_centered_wedge));\n            }\n            {\n              if (radial_distribution == CoordinateMaps::Distribution::Linear) {\n                // test cubical offset Wedge that is centered\n                const std::optional<double> outer_radius = std::nullopt;\n                CAPTURE(outer_radius);\n\n                const Wedge2D offset_wedge_with_no_offset(\n                    inner_radius, outer_radius, cube_half_length, focal_offset,\n                    orientation, with_equiangular_map, halves,\n                    radial_distribution);\n                test_suite_for_map_on_unit_cube(offset_wedge_with_no_offset);\n\n                // make sure offset wedge with no offset reduces to centered\n                // wedge\n                const Wedge2D expected_centered_wedge(\n                    inner_radius, cube_half_length * sqrt(2.0), 1.0, 0.0,\n                    orientation, with_equiangular_map, halves,\n                    radial_distribution);\n                check_if_maps_are_equal(\n                    domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(\n                        offset_wedge_with_no_offset),\n                    domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(\n                        expected_centered_wedge));\n              }\n            }\n          } else {\n            // test offset Wedge\n            if (radial_distribution == CoordinateMaps::Distribution::Linear) {\n              {\n                // test spherical offset Wedge with non-zero offset\n                const double outer_radius = outer_dis(gen);\n                CAPTURE(outer_radius);\n                test_suite_for_map_on_unit_cube(\n                    Wedge2D{inner_radius, outer_radius, cube_half_length,\n                            focal_offset, orientation, with_equiangular_map,\n                            halves, radial_distribution});\n              }\n              {\n                // test cubical offset Wedge with non-zero offset\n                const std::optional<double> outer_radius = std::nullopt;\n                CAPTURE(outer_radius);\n                test_suite_for_map_on_unit_cube(\n                    Wedge2D{inner_radius, outer_radius, cube_half_length,\n                            focal_offset, orientation, with_equiangular_map,\n                            halves, radial_distribution});\n              }\n            } else {\n              {\n                // test spherical offset Wedge with non-zero offset\n                const double outer_radius = outer_dis(gen);\n                CAPTURE(outer_radius);\n                test_suite_for_map_on_unit_cube(\n                    Wedge2D{inner_radius, outer_radius, cube_half_length,\n                            focal_offset, orientation, with_equiangular_map,\n                            halves, radial_distribution});\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_wedge2d_fail() {\n  INFO(\"Wedge2d fail\");\n  const auto centered_map =\n      Wedge2D(0.2, 4.0, 1.0, 1.0, OrientationMap<2>::create_aligned(), true);\n  const auto offset_map = Wedge2D(0.2, 2.0, 4.0, {{0.1, 0.}},\n                                  OrientationMap<2>::create_aligned(), true);\n\n  // Any point with x <= 0 should fail the inverse map with no focal offset\n  const std::array<double, 2> test_mapped_point1{{0.0, 3.0}};\n  const std::array<double, 2> test_mapped_point2{{0.0, -6.0}};\n  const std::array<double, 2> test_mapped_point3{{-1.0, 3.0}};\n\n  // Any point with x <= 0.1 should fail the inverse map with the focal offset\n  const std::array<double, 2> test_mapped_point4{{0.0, 3.0}};\n  const std::array<double, 2> test_mapped_point5{{0.0, -6.0}};\n  const std::array<double, 2> test_mapped_point6{{-1.0, 3.0}};\n\n  // This point is outside the mapped Wedges, so inverse should either return\n  // the correct inverse (which happens to be computable for this point for both\n  // Wedges) or it should return nullopt.\n  const std::array<double, 2> test_mapped_point7{{100.0, -6.0}};\n\n  // Check expected behavior for Wedge without offset\n  CHECK_FALSE(centered_map.inverse(test_mapped_point1).has_value());\n  CHECK_FALSE(centered_map.inverse(test_mapped_point2).has_value());\n  CHECK_FALSE(centered_map.inverse(test_mapped_point3).has_value());\n  if (centered_map.inverse(test_mapped_point7).has_value()) {\n    CHECK_ITERABLE_APPROX(\n        centered_map(centered_map.inverse(test_mapped_point7).value()),\n        test_mapped_point7);\n  }\n\n  // Check expected behavior for Wedge with offset\n  CHECK_FALSE(offset_map.inverse(test_mapped_point4).has_value());\n  CHECK_FALSE(offset_map.inverse(test_mapped_point5).has_value());\n  CHECK_FALSE(offset_map.inverse(test_mapped_point6).has_value());\n  if (offset_map.inverse(test_mapped_point7).has_value()) {\n    CHECK_ITERABLE_APPROX(\n        offset_map(offset_map.inverse(test_mapped_point7).value()),\n        test_mapped_point7);\n  }\n}\n\nvoid test_equality() {\n  INFO(\"Equality\");\n\n  const Wedge2D::WedgeHalves halves_to_use = Wedge2D::WedgeHalves::Both;\n  const Wedge2D::WedgeHalves changed_halves_to_use =\n      Wedge2D::WedgeHalves::UpperOnly;\n\n  const domain::CoordinateMaps::Distribution radial_distribution =\n      CoordinateMaps::Distribution::Linear;\n  const domain::CoordinateMaps::Distribution changed_radial_distribution =\n      CoordinateMaps::Distribution::Logarithmic;\n\n  const std::array<double, 1>& opening_angles{{M_PI_2}};\n  const std::array<double, 1>& changed_opening_angles{{M_PI_2 / 2.0}};\n\n  // centered wedges\n  const auto wedge2d =\n      Wedge2D(0.2, 4.0, 1.0, 1.0, OrientationMap<2>::create_aligned(), true,\n              halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_inner_radius_changed =\n      Wedge2D(0.3, 4.0, 1.0, 1.0, OrientationMap<2>::create_aligned(), true,\n              halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_outer_radius_changed =\n      Wedge2D(0.2, 4.2, 1.0, 1.0, OrientationMap<2>::create_aligned(), true,\n              halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_inner_circularity_changed =\n      Wedge2D(0.2, 4.0, 0.3, 1.0, OrientationMap<2>::create_aligned(), true,\n              halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_outer_circularity_changed =\n      Wedge2D(0.2, 4.0, 1.0, 0.9, OrientationMap<2>::create_aligned(), true,\n              halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_orientation_map_changed =\n      Wedge2D(0.2, 4.0, 1.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n              true, halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_use_equiangular_map_changed =\n      Wedge2D(0.2, 4.0, 1.0, 1.0, OrientationMap<2>::create_aligned(), false,\n              halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_halves_to_use_changed =\n      Wedge2D(0.2, 4.0, 1.0, 1.0, OrientationMap<2>::create_aligned(), true,\n              changed_halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_radial_distribution_changed =\n      Wedge2D(0.2, 4.0, 1.0, 1.0, OrientationMap<2>::create_aligned(), true,\n              halves_to_use, changed_radial_distribution, opening_angles);\n  const auto wedge2d_opening_angles_changed =\n      Wedge2D(0.2, 4.0, 1.0, 1.0, OrientationMap<2>::create_aligned(), true,\n              halves_to_use, radial_distribution, changed_opening_angles);\n\n  CHECK_FALSE(wedge2d == wedge2d_inner_radius_changed);\n  CHECK_FALSE(wedge2d == wedge2d_outer_radius_changed);\n  CHECK_FALSE(wedge2d == wedge2d_inner_circularity_changed);\n  CHECK_FALSE(wedge2d == wedge2d_outer_circularity_changed);\n  CHECK_FALSE(wedge2d == wedge2d_orientation_map_changed);\n  CHECK_FALSE(wedge2d == wedge2d_use_equiangular_map_changed);\n  CHECK_FALSE(wedge2d == wedge2d_halves_to_use_changed);\n  CHECK_FALSE(wedge2d == wedge2d_radial_distribution_changed);\n  CHECK_FALSE(wedge2d == wedge2d_opening_angles_changed);\n\n  // offset wedges\n  const auto wedge2d_offset =\n      Wedge2D(0.2, 4.0, 6.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n              true, halves_to_use, radial_distribution);\n  const auto wedge2d_offset_inner_radius_changed =\n      Wedge2D(0.1, 4.0, 6.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n              true, halves_to_use, radial_distribution);\n  const auto wedge2d_offset_outer_radius_changed =\n      Wedge2D(0.2, 3.0, 6.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n              true, halves_to_use, radial_distribution);\n  const auto wedge2d_offset_outer_circularity_changed = Wedge2D(\n      0.2, std::nullopt, 6.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n      true, halves_to_use, radial_distribution);\n  const auto wedge2d_offset_cube_half_length_changed =\n      Wedge2D(0.2, 4.0, 7.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n              true, halves_to_use, radial_distribution);\n  const auto wedge2d_offset_focal_offset_changed =\n      Wedge2D(0.2, 4.0, 6.0, {{0.2, 0.0}}, OrientationMap<2>::create_aligned(),\n              true, halves_to_use, radial_distribution);\n  const auto wedge2d_offset_orientation_map_changed =\n      Wedge2D(0.2, 4.0, 6.0, {{0.1, 0.0}},\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n              true, halves_to_use, radial_distribution);\n  const auto wedge2d_offset_use_equiangular_map_changed =\n      Wedge2D(0.2, 4.0, 6.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n              false, halves_to_use, radial_distribution);\n  const auto wedge2d_offset_halves_to_use_changed =\n      Wedge2D(0.2, 4.0, 6.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n              true, changed_halves_to_use, radial_distribution);\n  const auto wedge2d_offset_radial_distribution_changed =\n      Wedge2D(0.2, 4.0, 6.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n              true, halves_to_use, changed_radial_distribution);\n\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_inner_radius_changed);\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_outer_radius_changed);\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_outer_circularity_changed);\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_cube_half_length_changed);\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_focal_offset_changed);\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_orientation_map_changed);\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_use_equiangular_map_changed);\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_halves_to_use_changed);\n  CHECK_FALSE(wedge2d_offset == wedge2d_offset_radial_distribution_changed);\n\n  // make sure spherical offset wedge with zero offset reduces to centered wedge\n  const auto wedge2d_offset_centered_with_spherical_outer_circularity =\n      Wedge2D(0.2, 4.0, 6.0, {{0.0, 0.0}}, OrientationMap<2>::create_aligned(),\n              true, halves_to_use, radial_distribution);\n  CHECK(wedge2d == wedge2d_offset_centered_with_spherical_outer_circularity);\n\n  // make sure cubical offset wedge with zero offset reduces to centered wedge\n  const auto wedge2d_centered_with_flat_outer_circularity =\n      Wedge2D(0.2, sqrt(2.0), 1.0, 0.0, OrientationMap<2>::create_aligned(),\n              true, halves_to_use, radial_distribution, opening_angles);\n  const auto wedge2d_offset_centered_with_flat_outer_circularity = Wedge2D(\n      0.2, std::nullopt, 1.0, {{0.0, 0.0}}, OrientationMap<2>::create_aligned(),\n      true, halves_to_use, radial_distribution);\n  CHECK(wedge2d_centered_with_flat_outer_circularity ==\n        wedge2d_offset_centered_with_flat_outer_circularity);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Wedge2D.Map\", \"[Domain][Unit]\") {\n  test_wedge2d_fail();\n  test_wedge2d_all_orientations(false);  // Equidistant\n  test_wedge2d_all_orientations(true);   // Equiangular\n  test_equality();\n  CHECK(not Wedge2D{}.is_identity());\n\n#ifdef SPECTRE_DEBUG\n  // centered wedge checks\n  CHECK_THROWS_WITH(\n      Wedge2D(-0.2, 4.0, 0.0, 1.0, OrientationMap<2>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The radius of the inner surface must be greater than zero.\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(0.2, 4.0, -0.2, 1.0, OrientationMap<2>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"Sphericity of the inner surface must be between 0 and 1\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(0.2, 4.0, 0.0, -0.2, OrientationMap<2>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"Sphericity of the outer surface must be between 0 and 1\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(4.2, 4.0, 0.0, 1.0, OrientationMap<2>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The radius of the outer surface must be greater than \"\n          \"the radius of the inner surface.\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(3.0, 4.0, 1.0, 0.0, OrientationMap<2>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The arguments passed into the constructor for Wedge result in an \"\n          \"object where the outer surface is pierced by the inner surface.\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(0.2, 4.0, 0.0, 1.0, OrientationMap<2>::create_aligned(), true,\n              Wedge2D::WedgeHalves::Both,\n              domain::CoordinateMaps::Distribution::Logarithmic),\n      Catch::Matchers::ContainsSubstring(\n          \"Only the 'Linear' radial distribution is supported for \"\n          \"non-spherical wedges.\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(0.2, 4.0, 0.2, 1.0, OrientationMap<2>::create_aligned(), false,\n              Wedge2D::WedgeHalves::Both,\n              domain::CoordinateMaps::Distribution::Linear,\n              std::array<double, 1>{{M_PI_4}}),\n      Catch::Matchers::ContainsSubstring(\n          \"If using opening angles other than pi/2, then the \"\n          \"equiangular map option must be turned on.\"));\n\n  // offset wedge checks\n  CHECK_THROWS_WITH(\n      Wedge2D(-0.2, 4.0, 6.0, {{0.1, 0.0}}, OrientationMap<2>::create_aligned(),\n              true),\n      Catch::Matchers::ContainsSubstring(\n          \"The radius of the inner surface must be greater than zero.\"));\n  CHECK_THROWS_WITH(Wedge2D(4.2, 4.0, 6.0, {{0.1, 0.0}},\n                            OrientationMap<2>::create_aligned(), true),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The radius of the outer surface must be greater than \"\n                        \"the radius of the inner surface.\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(0.2, std::nullopt, 6.0, {{0.1, 0.0}},\n              OrientationMap<2>::create_aligned(), true,\n              Wedge2D::WedgeHalves::Both,\n              domain::CoordinateMaps::Distribution::Logarithmic),\n      Catch::Matchers::ContainsSubstring(\n          \"Only the 'Linear' radial distribution is supported for \"\n          \"non-spherical wedges.\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(3.0, std::nullopt, 3.0, {{0.0, 0.0}},\n              OrientationMap<2>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The arguments passed into the constructor for Wedge result in an \"\n          \"object where the outer surface is pierced by the inner surface.\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(0.2, 4.0, 1.0, {{4., 0.}}, OrientationMap<2>::create_aligned(),\n              true),\n      Catch::Matchers::ContainsSubstring(\n          \"For a spherical focally offset Wedge, the sum of the outer radius \"\n          \"and the coordinate of the focal offset with the largest magnitude \"\n          \"must be less than the cube half length. In other words, the \"\n          \"spherical surface at the given outer radius centered at the focal \"\n          \"offset must not pierce the cube of length 2 * cube_half_length_ \"\n          \"centered at the origin. See the Wedge class documentation for a \"\n          \"visual representation of this sphere and cube.\"));\n  CHECK_THROWS_WITH(\n      Wedge2D(0.2, std::nullopt, 1.0, {{4., 0.}},\n              OrientationMap<2>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"For a cubical focally offset Wedge, the sum of the inner radius \"\n          \"and the coordinate of the focal offset with the largest magnitude \"\n          \"must be less than the cube half length. In other words, the \"\n          \"spherical surface at the given inner radius centered at the focal \"\n          \"offset must not pierce the cube of length 2 * cube_half_length_ \"\n          \"centered at the origin. See the Wedge class documentation for a \"\n          \"visual representation of this sphere and cube.\"));\n#endif\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/Test_Wedge3D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\nnamespace {\nusing Wedge3D = CoordinateMaps::Wedge<3>;\n\nvoid test_wedge3d_all_directions() {\n  INFO(\"Wedge3d all directions\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0, 1);\n  std::uniform_real_distribution<> inner_dis(1, 3);\n  std::uniform_real_distribution<> outer_dis(5.2, 7);\n  std::uniform_real_distribution<> cube_half_length_dist(8, 10);\n  std::uniform_real_distribution<> offset_coord_dist(-1, 1);\n  std::uniform_real_distribution<> angle_dis(80.0, 90.0);\n\n  const double inner_radius = inner_dis(gen);\n  CAPTURE(inner_radius);\n  const double cube_half_length = cube_half_length_dist(gen);\n  CAPTURE(cube_half_length);\n\n  using WedgeHalves = Wedge3D::WedgeHalves;\n  const std::array<WedgeHalves, 3> halves_array = {\n      {WedgeHalves::UpperOnly, WedgeHalves::LowerOnly, WedgeHalves::Both}};\n  const std::array<double, 3> zero_offset{{0.0, 0.0, 0.0}};\n  const std::array<std::array<double, 3>, 2> focal_offsets = {\n      {zero_offset,\n       {{offset_coord_dist(gen), offset_coord_dist(gen),\n         offset_coord_dist(gen)}}}};\n  for (const auto& focal_offset : focal_offsets) {\n    CAPTURE(focal_offset);\n    // [cartesian_product_loop]\n    for (const auto& [halves, orientation, with_equiangular_map,\n                      with_adapted_equiangular_map, radial_distribution] :\n         random_sample<5>(\n             cartesian_product(\n                 halves_array, all_wedge_directions(), make_array(true, false),\n                 make_array(true, false),\n                 make_array(CoordinateMaps::Distribution::Linear,\n                            CoordinateMaps::Distribution::Linear,\n                            CoordinateMaps::Distribution::Linear,\n                            CoordinateMaps::Distribution::Logarithmic,\n                            CoordinateMaps::Distribution::Inverse)),\n             make_not_null(&gen))) {\n      // [cartesian_product_loop]\n      CAPTURE(halves);\n      CAPTURE(orientation);\n      CAPTURE(with_equiangular_map);\n      CAPTURE(radial_distribution);\n\n      if (focal_offset == zero_offset) {\n        // test centered Wedge\n        {\n          const double outer_radius = outer_dis(gen);\n          CAPTURE(outer_radius);\n          // sphericity != 1.0 is only supported for Wedges where the\n          // radial distribution is linear and there is no focal offset\n          const bool use_random_sphericity =\n              (radial_distribution == CoordinateMaps::Distribution::Linear);\n          const double inner_sphericity =\n              use_random_sphericity ? unit_dis(gen) : 1.0;\n          CAPTURE(inner_sphericity);\n          const double outer_sphericity =\n              use_random_sphericity ? unit_dis(gen) : 1.0;\n          CAPTURE(outer_sphericity);\n\n          test_suite_for_map_on_unit_cube(Wedge3D{\n              inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n              orientation, with_equiangular_map, halves, radial_distribution});\n        }\n        {\n          // test spherical offset Wedge that is centered\n          const double outer_radius = outer_dis(gen);\n          CAPTURE(outer_radius);\n\n          const Wedge3D offset_wedge_with_no_offset(\n              inner_radius, outer_radius, cube_half_length, focal_offset,\n              orientation, with_equiangular_map, halves, radial_distribution);\n          test_suite_for_map_on_unit_cube(offset_wedge_with_no_offset);\n\n          // make sure offset wedge with no offset reduces to centered wedge\n          const Wedge3D expected_centered_wedge(\n              inner_radius, outer_radius, 1.0, 1.0, orientation,\n              with_equiangular_map, halves, radial_distribution);\n          check_if_maps_are_equal(\n              domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(\n                  offset_wedge_with_no_offset),\n              domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(\n                  expected_centered_wedge));\n        }\n        {\n          if (radial_distribution == CoordinateMaps::Distribution::Linear) {\n            // test cubical offset Wedge that is centered\n            const std::optional<double> outer_radius = std::nullopt;\n            CAPTURE(outer_radius);\n\n            const Wedge3D offset_wedge_with_no_offset(\n                inner_radius, outer_radius, cube_half_length, focal_offset,\n                orientation, with_equiangular_map, halves, radial_distribution);\n            test_suite_for_map_on_unit_cube(offset_wedge_with_no_offset);\n\n            // make sure offset wedge with no offset reduces to centered\n            // wedge\n            const Wedge3D expected_centered_wedge(\n                inner_radius, cube_half_length * sqrt(3.0), 1.0, 0.0,\n                orientation, with_equiangular_map, halves, radial_distribution);\n            check_if_maps_are_equal(\n                domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(\n                    offset_wedge_with_no_offset),\n                domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(\n                    expected_centered_wedge));\n          }\n        }\n      } else {\n        // test offset Wedge\n        if (radial_distribution == CoordinateMaps::Distribution::Linear) {\n          {\n            // test spherical offset Wedge with non-zero offset\n            const double outer_radius = outer_dis(gen);\n            CAPTURE(outer_radius);\n            test_suite_for_map_on_unit_cube(\n                Wedge3D{inner_radius, outer_radius, cube_half_length,\n                        focal_offset, orientation, with_equiangular_map, halves,\n                        radial_distribution});\n          }\n          {\n            // test cubical offset Wedge with non-zero offset\n            const std::optional<double> outer_radius = std::nullopt;\n            CAPTURE(outer_radius);\n            test_suite_for_map_on_unit_cube(\n                Wedge3D{inner_radius, outer_radius, cube_half_length,\n                        focal_offset, orientation, with_equiangular_map, halves,\n                        radial_distribution});\n          }\n        } else {\n          {\n            // test spherical offset Wedge with non-zero offset\n            const double outer_radius = outer_dis(gen);\n            CAPTURE(outer_radius);\n            test_suite_for_map_on_unit_cube(\n                Wedge3D{inner_radius, outer_radius, cube_half_length,\n                        focal_offset, orientation, with_equiangular_map, halves,\n                        radial_distribution});\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_wedge3d_alignment() {\n  INFO(\"Wedge3d alignment\");\n  // This test tests that the logical axes point along the expected directions\n  // in physical space\n\n  const double inner_r = sqrt(3.0);\n  const double outer_r = 2.0 * sqrt(3.0);\n\n  using WedgeHalves = Wedge3D::WedgeHalves;\n  const auto wedge_directions = all_wedge_directions();\n\n  for (const auto& with_equiangular_map : {true, false}) {\n    CAPTURE(with_equiangular_map);\n    for (const auto radial_distribution :\n         {CoordinateMaps::Distribution::Linear,\n          CoordinateMaps::Distribution::Logarithmic,\n          CoordinateMaps::Distribution::Inverse}) {\n      CAPTURE(radial_distribution);\n      const double inner_sphericity =\n          radial_distribution == CoordinateMaps::Distribution::Linear ? 0.0\n                                                                      : 1.0;\n      const Wedge3D map_upper_zeta(inner_r, outer_r, inner_sphericity, 1.0,\n                                   wedge_directions[0], with_equiangular_map,\n                                   WedgeHalves::Both,\n                                   radial_distribution);  // Upper Z wedge\n      const Wedge3D map_upper_eta(inner_r, outer_r, inner_sphericity, 1.0,\n                                  wedge_directions[2], with_equiangular_map,\n                                  WedgeHalves::Both,\n                                  radial_distribution);  // Upper Y wedge\n      const Wedge3D map_upper_xi(inner_r, outer_r, inner_sphericity, 1.0,\n                                 wedge_directions[4], with_equiangular_map,\n                                 WedgeHalves::Both,\n                                 radial_distribution);  // Upper X Wedge\n      const Wedge3D map_lower_zeta(inner_r, outer_r, inner_sphericity, 1.0,\n                                   wedge_directions[1], with_equiangular_map,\n                                   WedgeHalves::Both,\n                                   radial_distribution);  // Lower Z wedge\n      const Wedge3D map_lower_eta(inner_r, outer_r, inner_sphericity, 1.0,\n                                  wedge_directions[3], with_equiangular_map,\n                                  WedgeHalves::Both,\n                                  radial_distribution);  // Lower Y wedge\n      const Wedge3D map_lower_xi(inner_r, outer_r, inner_sphericity, 1.0,\n                                 wedge_directions[5], with_equiangular_map,\n                                 WedgeHalves::Both,\n                                 radial_distribution);  // Lower X wedge\n      const std::array<double, 3> lowest_corner{{-1.0, -1.0, -1.0}};\n      const std::array<double, 3> along_xi{{1.0, -1.0, -1.0}};\n      const std::array<double, 3> along_eta{{-1.0, 1.0, -1.0}};\n      const std::array<double, 3> along_zeta{{-1.0, -1.0, 1.0}};\n\n      const std::array<double, 3> lowest_physical_corner_upper_zeta{\n          {-1.0, -1.0, 1.0}};\n      const std::array<double, 3> lowest_physical_corner_upper_eta{\n          {-1.0, 1.0, -1.0}};\n      const std::array<double, 3> lowest_physical_corner_upper_xi{\n          {1.0, -1.0, -1.0}};\n      const std::array<double, 3> lowest_physical_corner_lower_zeta{\n          {-1.0, 1.0, -1.0}};\n      const std::array<double, 3> lowest_physical_corner_lower_eta{\n          {-1.0, -1.0, 1.0}};\n      const std::array<double, 3> lowest_physical_corner_lower_xi{\n          {-1.0, 1.0, -1.0}};\n\n      // Test that this map's logical axes point along +X, +Y, +Z:\n      CHECK(map_upper_zeta(along_xi)[0] == approx(1.0));\n      CHECK(map_upper_zeta(along_eta)[1] == approx(1.0));\n      CHECK(map_upper_zeta(along_zeta)[2] == approx(2.0));\n      CHECK_ITERABLE_APPROX(map_upper_zeta(lowest_corner),\n                            lowest_physical_corner_upper_zeta);\n\n      // Test that this map's logical axes point along +Z, +X, +Y:\n      CHECK(map_upper_eta(along_xi)[2] == approx(1.0));\n      CHECK(map_upper_eta(along_eta)[0] == approx(1.0));\n      CHECK(map_upper_eta(along_zeta)[1] == approx(2.0));\n      CHECK_ITERABLE_APPROX(map_upper_eta(lowest_corner),\n                            lowest_physical_corner_upper_eta);\n\n      // Test that this map's logical axes point along +Y, +Z, +X:\n      CHECK(map_upper_xi(along_xi)[1] == approx(1.0));\n      CHECK(map_upper_xi(along_eta)[2] == approx(1.0));\n      CHECK(map_upper_xi(along_zeta)[0] == approx(2.0));\n      CHECK_ITERABLE_APPROX(map_upper_xi(lowest_corner),\n                            lowest_physical_corner_upper_xi);\n\n      // Test that this map's logical axes point along +X, -Y, -Z:\n      CHECK(map_lower_zeta(along_xi)[0] == approx(1.0));\n      CHECK(map_lower_zeta(along_eta)[1] == approx(-1.0));\n      CHECK(map_lower_zeta(along_zeta)[2] == approx(-2.0));\n      CHECK_ITERABLE_APPROX(map_lower_zeta(lowest_corner),\n                            lowest_physical_corner_lower_zeta);\n\n      // Test that this map's logical axes point along -Z, +X, -Y:\n      CHECK(map_lower_eta(along_xi)[2] == approx(-1.0));\n      CHECK(map_lower_eta(along_eta)[0] == approx(1.0));\n      CHECK(map_lower_eta(along_zeta)[1] == approx(-2.0));\n      CHECK_ITERABLE_APPROX(map_lower_eta(lowest_corner),\n                            lowest_physical_corner_lower_eta);\n\n      // Test that this map's logical axes point along -Y, +Z, -X:\n      CHECK(map_lower_xi(along_xi)[1] == approx(-1.0));\n      CHECK(map_lower_xi(along_eta)[2] == approx(1.0));\n      CHECK(map_lower_xi(along_zeta)[0] == approx(-2.0));\n      CHECK_ITERABLE_APPROX(map_lower_xi(lowest_corner),\n                            lowest_physical_corner_lower_xi);\n    }\n  }\n}\n\nvoid test_wedge3d_random_radii() {\n  INFO(\"Wedge3d random radii\");\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1, 1);\n  std::uniform_real_distribution<> inner_dis(1, 3);\n  std::uniform_real_distribution<> outer_dis(4, 7);\n  std::uniform_real_distribution<> cube_half_length_dist(8, 10);\n  std::uniform_real_distribution<> offset_coord_dist(-1, 1);\n  std::uniform_real_distribution<> angle_dis(55.0, 125.0);\n\n  // Check that points on the corners of the reference cube map to the correct\n  // corners of the wedge.\n  const std::array<double, 3> inner_corner{{-1.0, -1.0, -1.0}};\n  const std::array<double, 3> outer_corner{{1.0, 1.0, 1.0}};\n  const double random_inner_radius_lower_xi = inner_dis(gen);\n  CAPTURE(random_inner_radius_lower_xi);\n  const double random_inner_radius_lower_eta = inner_dis(gen);\n  CAPTURE(random_inner_radius_lower_eta);\n  const double random_inner_radius_lower_zeta = inner_dis(gen);\n  CAPTURE(random_inner_radius_lower_zeta);\n  const double random_inner_radius_upper_xi = inner_dis(gen);\n  CAPTURE(random_inner_radius_upper_xi);\n  const double random_inner_radius_upper_eta = inner_dis(gen);\n  CAPTURE(random_inner_radius_upper_eta);\n  const double random_inner_radius_upper_zeta = inner_dis(gen);\n  CAPTURE(random_inner_radius_upper_zeta);\n\n  const double random_outer_radius_lower_xi = outer_dis(gen);\n  CAPTURE(random_outer_radius_lower_xi);\n  const double random_outer_radius_lower_eta = outer_dis(gen);\n  CAPTURE(random_outer_radius_lower_eta);\n  const double random_outer_radius_lower_zeta = outer_dis(gen);\n  CAPTURE(random_outer_radius_lower_zeta);\n  const double random_outer_radius_upper_xi = outer_dis(gen);\n  CAPTURE(random_outer_radius_upper_xi);\n  const double random_outer_radius_upper_eta = outer_dis(gen);\n  CAPTURE(random_outer_radius_upper_eta);\n  const double random_outer_radius_upper_zeta = outer_dis(gen);\n  CAPTURE(random_outer_radius_upper_zeta);\n\n  const double random_opening_angle_xi = angle_dis(gen) * M_PI / 180.0;\n  CAPTURE(random_opening_angle_xi * 180.0 / M_PI);\n  const double random_opening_angle_eta = angle_dis(gen) * M_PI / 180.0;\n  CAPTURE(random_opening_angle_eta * 180.0 / M_PI);\n  const std::array<double, 2> random_opening_angles{\n      {random_opening_angle_xi, random_opening_angle_eta}};\n  const std::array<double, 2> default_angles{{M_PI_2, M_PI_2}};\n\n  const std::array<double, 3> zero_offset{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> random_focal_offset{\n      {offset_coord_dist(gen), offset_coord_dist(gen), offset_coord_dist(gen)}};\n  const std::array<std::array<double, 3>, 2> focal_offsets = {\n      {zero_offset, random_focal_offset}};\n  const double cube_half_length = cube_half_length_dist(gen);\n  CAPTURE(cube_half_length);\n\n  using WedgeHalves = Wedge3D::WedgeHalves;\n  const auto wedge_directions = all_wedge_directions();\n  for (const auto& focal_offset_upper_zeta : focal_offsets) {\n    CAPTURE(focal_offset_upper_zeta);\n    for (const auto& with_equiangular_map : {true, false}) {\n      CAPTURE(with_equiangular_map);\n      for (const auto radial_distribution :\n           {CoordinateMaps::Distribution::Linear,\n            CoordinateMaps::Distribution::Logarithmic,\n            CoordinateMaps::Distribution::Inverse}) {\n        CAPTURE(radial_distribution);\n        if (focal_offset_upper_zeta == zero_offset) {\n          const double inner_sphericity =\n              (radial_distribution == CoordinateMaps::Distribution::Linear)\n                  ? 0.0\n                  : 1.0;\n          CAPTURE(inner_sphericity);\n\n          const bool use_random_opening_angles = with_equiangular_map;\n          const std::array<double, 2> opening_angles =\n              use_random_opening_angles ? random_opening_angles\n                                        : default_angles;\n          CAPTURE(opening_angles);\n\n          const Wedge3D map_lower_xi(\n              random_inner_radius_lower_xi, random_outer_radius_lower_xi,\n              inner_sphericity, 1.0, wedge_directions[5], with_equiangular_map,\n              WedgeHalves::Both, radial_distribution, opening_angles);\n          const Wedge3D map_lower_eta(\n              random_inner_radius_lower_eta, random_outer_radius_lower_eta,\n              inner_sphericity, 1.0, wedge_directions[3], with_equiangular_map,\n              WedgeHalves::Both, radial_distribution, opening_angles);\n          const Wedge3D map_lower_zeta(\n              random_inner_radius_lower_zeta, random_outer_radius_lower_zeta,\n              inner_sphericity, 1.0, wedge_directions[1], with_equiangular_map,\n              WedgeHalves::Both, radial_distribution, opening_angles);\n          const Wedge3D map_upper_xi(\n              random_inner_radius_upper_xi, random_outer_radius_upper_xi,\n              inner_sphericity, 1.0, wedge_directions[4], with_equiangular_map,\n              WedgeHalves::Both, radial_distribution, opening_angles);\n          const Wedge3D map_upper_eta(\n              random_inner_radius_upper_eta, random_outer_radius_upper_eta,\n              inner_sphericity, 1.0, wedge_directions[2], with_equiangular_map,\n              WedgeHalves::Both, radial_distribution, opening_angles);\n          const Wedge3D map_upper_zeta(\n              random_inner_radius_upper_zeta, random_outer_radius_upper_zeta,\n              inner_sphericity, 1.0, wedge_directions[0], with_equiangular_map,\n              WedgeHalves::Both, radial_distribution, opening_angles);\n\n          const double cap_xi_one =\n              tan(with_equiangular_map ? 0.5 * opening_angles[0] : M_PI_4);\n          const double cap_eta_one =\n              tan(with_equiangular_map ? 0.5 * opening_angles[1] : M_PI_4);\n\n          const double one_over_rho =\n              1.0 / sqrt(1.0 + square(cap_xi_one) + square(cap_eta_one));\n\n          if (inner_sphericity == 1.0) {\n            CHECK(map_lower_xi(outer_corner)[0] ==\n                  approx(-random_outer_radius_lower_xi * one_over_rho));\n            CHECK(map_lower_eta(outer_corner)[1] ==\n                  approx(-random_outer_radius_lower_eta * one_over_rho));\n            CHECK(map_lower_zeta(outer_corner)[2] ==\n                  approx(-random_outer_radius_lower_zeta * one_over_rho));\n            CHECK(map_upper_xi(inner_corner)[0] ==\n                  approx(random_inner_radius_upper_xi * one_over_rho));\n            CHECK(map_upper_eta(inner_corner)[1] ==\n                  approx(random_inner_radius_upper_eta * one_over_rho));\n            CHECK(map_upper_zeta(inner_corner)[2] ==\n                  approx(random_inner_radius_upper_zeta * one_over_rho));\n          }\n\n          // Check that random points on the edges of the reference cube map to\n          // the correct edges of the wedge.\n          const std::array<double, 3> random_outer_face{\n              {real_dis(gen), real_dis(gen), 1.0}};\n          const std::array<double, 3> random_inner_face{\n              {real_dis(gen), real_dis(gen), -1.0}};\n          CAPTURE(random_outer_face);\n          CAPTURE(random_inner_face);\n\n          if (inner_sphericity == 0.0) {\n            CHECK(map_lower_xi(random_inner_face)[0] ==\n                  approx(-random_inner_radius_lower_xi / sqrt(3.0)));\n            CHECK(map_lower_eta(random_inner_face)[1] ==\n                  approx(-random_inner_radius_lower_eta / sqrt(3.0)));\n            CHECK(map_upper_xi(random_inner_face)[0] ==\n                  approx(random_inner_radius_upper_xi / sqrt(3.0)));\n            CHECK(map_upper_eta(random_inner_face)[1] ==\n                  approx(random_inner_radius_upper_eta / sqrt(3.0)));\n          }\n          CHECK(magnitude(map_lower_xi(random_outer_face)) ==\n                approx(random_outer_radius_lower_xi));\n          CHECK(magnitude(map_lower_eta(random_outer_face)) ==\n                approx(random_outer_radius_lower_eta));\n          CHECK(magnitude(map_upper_xi(random_outer_face)) ==\n                approx(random_outer_radius_upper_xi));\n          CHECK(magnitude(map_upper_eta(random_outer_face)) ==\n                approx(random_outer_radius_upper_eta));\n          CHECK(magnitude(map_lower_zeta(random_outer_face)) ==\n                approx(random_outer_radius_lower_zeta));\n          CHECK(magnitude(map_upper_zeta(random_outer_face)) ==\n                approx(random_outer_radius_upper_zeta));\n        } else {\n          // Generate the offsets for each Wedge such that when rotated to the\n          // upper zeta orientation, the offset coordinates are the same. These\n          // rotations of the coordinates are based on the orientations of the\n          // Wedges as defined by all_wedge_directions().\n          const std::array<double, 3> focal_offset_lower_xi{\n              {-focal_offset_upper_zeta[2], -focal_offset_upper_zeta[0],\n               focal_offset_upper_zeta[1]}};\n          const std::array<double, 3> focal_offset_lower_eta{\n              {focal_offset_upper_zeta[1], -focal_offset_upper_zeta[2],\n               -focal_offset_upper_zeta[0]}};\n          const std::array<double, 3> focal_offset_lower_zeta{\n              {focal_offset_upper_zeta[0], -focal_offset_upper_zeta[1],\n               -focal_offset_upper_zeta[2]}};\n          const std::array<double, 3> focal_offset_upper_xi{\n              {focal_offset_upper_zeta[2], focal_offset_upper_zeta[0],\n               focal_offset_upper_zeta[1]}};\n          const std::array<double, 3> focal_offset_upper_eta{\n              {focal_offset_upper_zeta[1], focal_offset_upper_zeta[2],\n               focal_offset_upper_zeta[0]}};\n\n          const Wedge3D map_lower_xi(\n              random_inner_radius_lower_xi, random_outer_radius_lower_xi,\n              cube_half_length, focal_offset_lower_xi, wedge_directions[5],\n              with_equiangular_map, WedgeHalves::Both, radial_distribution);\n          const Wedge3D map_lower_eta(\n              random_inner_radius_lower_eta, random_outer_radius_lower_eta,\n              cube_half_length, focal_offset_lower_eta, wedge_directions[3],\n              with_equiangular_map, WedgeHalves::Both, radial_distribution);\n          const Wedge3D map_lower_zeta(\n              random_inner_radius_lower_zeta, random_outer_radius_lower_zeta,\n              cube_half_length, focal_offset_lower_zeta, wedge_directions[1],\n              with_equiangular_map, WedgeHalves::Both, radial_distribution);\n          const Wedge3D map_upper_xi(\n              random_inner_radius_upper_xi, random_outer_radius_upper_xi,\n              cube_half_length, focal_offset_upper_xi, wedge_directions[4],\n              with_equiangular_map, WedgeHalves::Both, radial_distribution);\n          const Wedge3D map_upper_eta(\n              random_inner_radius_upper_eta, random_outer_radius_upper_eta,\n              cube_half_length, focal_offset_upper_eta, wedge_directions[2],\n              with_equiangular_map, WedgeHalves::Both, radial_distribution);\n          const Wedge3D map_upper_zeta(\n              random_inner_radius_upper_zeta, random_outer_radius_upper_zeta,\n              cube_half_length, focal_offset_upper_zeta, wedge_directions[0],\n              with_equiangular_map, WedgeHalves::Both, radial_distribution);\n\n          const double cap_xi_one = 1.0;\n          const double cap_eta_one = 1.0;\n\n          const double one_over_rho_inner_corner =\n              1.0 /\n              sqrt(square(1.0 - focal_offset_upper_zeta[2] / cube_half_length) +\n                   square(-cap_xi_one -\n                          focal_offset_upper_zeta[0] / cube_half_length) +\n                   square(-cap_eta_one -\n                          focal_offset_upper_zeta[1] / cube_half_length));\n          const double one_over_rho_outer_corner =\n              1.0 /\n              sqrt(square(1.0 - focal_offset_upper_zeta[2] / cube_half_length) +\n                   square(cap_xi_one -\n                          focal_offset_upper_zeta[0] / cube_half_length) +\n                   square(cap_eta_one -\n                          focal_offset_upper_zeta[1] / cube_half_length));\n\n          CHECK(map_lower_xi(outer_corner)[0] ==\n                approx(-(\n                    random_outer_radius_lower_xi * one_over_rho_outer_corner *\n                        (1.0 - focal_offset_upper_zeta[2] / cube_half_length) +\n                    focal_offset_upper_zeta[2])));\n          CHECK(map_lower_eta(outer_corner)[1] ==\n                approx(-(\n                    random_outer_radius_lower_eta * one_over_rho_outer_corner *\n                        (1.0 - focal_offset_upper_zeta[2] / cube_half_length) +\n                    focal_offset_upper_zeta[2])));\n          CHECK(map_lower_zeta(outer_corner)[2] ==\n                approx(-(\n                    random_outer_radius_lower_zeta * one_over_rho_outer_corner *\n                        (1.0 - focal_offset_upper_zeta[2] / cube_half_length) +\n                    focal_offset_upper_zeta[2])));\n          CHECK(\n              map_upper_xi(inner_corner)[0] ==\n              approx(random_inner_radius_upper_xi * one_over_rho_inner_corner *\n                         (1.0 - focal_offset_upper_zeta[2] / cube_half_length) +\n                     focal_offset_upper_zeta[2]));\n          CHECK(\n              map_upper_eta(inner_corner)[1] ==\n              approx(random_inner_radius_upper_eta * one_over_rho_inner_corner *\n                         (1.0 - focal_offset_upper_zeta[2] / cube_half_length) +\n                     focal_offset_upper_zeta[2]));\n          CHECK(map_upper_zeta(inner_corner)[2] ==\n                approx(\n                    random_inner_radius_upper_zeta * one_over_rho_inner_corner *\n                        (1.0 - focal_offset_upper_zeta[2] / cube_half_length) +\n                    focal_offset_upper_zeta[2]));\n\n          // Check that random points on the edges of the reference cube map to\n          // the correct edges of the wedge.\n          const std::array<double, 3> random_outer_face{\n              {real_dis(gen), real_dis(gen), 1.0}};\n          CAPTURE(random_outer_face);\n\n          CHECK(magnitude(map_lower_xi(random_outer_face) -\n                          focal_offset_lower_xi) ==\n                approx(random_outer_radius_lower_xi));\n          CHECK(magnitude(map_lower_eta(random_outer_face) -\n                          focal_offset_lower_eta) ==\n                approx(random_outer_radius_lower_eta));\n          CHECK(magnitude(map_upper_xi(random_outer_face) -\n                          focal_offset_upper_xi) ==\n                approx(random_outer_radius_upper_xi));\n          CHECK(magnitude(map_upper_eta(random_outer_face) -\n                          focal_offset_upper_eta) ==\n                approx(random_outer_radius_upper_eta));\n          CHECK(magnitude(map_lower_zeta(random_outer_face) -\n                          focal_offset_lower_zeta) ==\n                approx(random_outer_radius_lower_zeta));\n          CHECK(magnitude(map_upper_zeta(random_outer_face) -\n                          focal_offset_upper_zeta) ==\n                approx(random_outer_radius_upper_zeta));\n        }\n      }\n    }\n  }\n}\n\nvoid test_wedge3d_large_radius() {\n  INFO(\"Wedge3d large radius\");\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1, 1);\n  std::uniform_real_distribution<> angle_dis(55.0, 125.0);\n\n  const double inner_radius = 1.5;\n  const double outer_radius = 1.0e11;\n\n  const double opening_angle_xi = angle_dis(gen) * M_PI / 180.0;\n  CAPTURE(opening_angle_xi * 180.0 / M_PI);\n  const double opening_angle_eta = angle_dis(gen) * M_PI / 180.0;\n  CAPTURE(opening_angle_eta * 180.0 / M_PI);\n  const std::array<double, 2> opening_angles{\n      {opening_angle_xi, opening_angle_eta}};\n  const std::array<double, 2> default_angles{{M_PI_2, M_PI_2}};\n\n  // Check that random points on the edges of the reference cube map to the\n  // correct edges of the wedge.\n  const std::array<double, 3> random_inner_logical{\n      {real_dis(gen), real_dis(gen), -1.0}};\n  const std::array<double, 3> random_outer_logical{\n      {real_dis(gen), real_dis(gen), 1.0}};\n\n  using WedgeHalves = Wedge3D::WedgeHalves;\n  for (const auto& with_equiangular_map : {true, false}) {\n    CAPTURE(with_equiangular_map);\n    for (const auto& which_wedges :\n         {WedgeHalves::Both, WedgeHalves::UpperOnly, WedgeHalves::LowerOnly}) {\n      const Wedge3D map(inner_radius, outer_radius, 1.0, 1.0,\n                        OrientationMap<3>::create_aligned(),\n                        with_equiangular_map, which_wedges,\n                        CoordinateMaps::Distribution::Inverse,\n                        with_equiangular_map ? opening_angles : default_angles);\n      const double cap_xi_one =\n          tan(with_equiangular_map ? 0.5 * opening_angle_xi : M_PI_4);\n      const double cap_eta_one =\n          tan(with_equiangular_map ? 0.5 * opening_angle_eta : M_PI_4);\n      const double one_over_denominator =\n          1.0 / sqrt(1.0 + square(cap_xi_one) + square(cap_eta_one));\n\n      // 1/r^2 dr/dzeta\n      const double radius_scale_factor =\n          (outer_radius - inner_radius) / (2.0 * outer_radius * inner_radius);\n      const auto check_point = [&](const std::array<double, 3>& logical_point,\n                                   const double expected_radius) {\n        CAPTURE(logical_point);\n        CAPTURE(expected_radius);\n        const auto mapped_point = map(logical_point);\n        CAPTURE(mapped_point);\n\n        CHECK(magnitude(mapped_point) == approx(expected_radius));\n        CHECK_ITERABLE_APPROX(map.inverse(mapped_point).value(), logical_point);\n\n        const auto jacobian = map.jacobian(logical_point);\n        const auto inv_jacobian = map.inv_jacobian(logical_point);\n        CAPTURE(jacobian);\n        CAPTURE(inv_jacobian);\n        auto jacobian_approx = approx;\n        jacobian_approx.scale(square(expected_radius));\n\n        {\n          const std::array<double, 3> radial_vector =\n              mapped_point / magnitude(mapped_point);\n          std::array r_dot_jacobian{0.0, 0.0, 0.0};\n          for (size_t i = 0; i < 3; ++i) {\n            for (size_t j = 0; j < 3; ++j) {\n              gsl::at(r_dot_jacobian, i) +=\n                  gsl::at(radial_vector, j) * jacobian.get(j, i);\n            }\n          }\n\n          CAPTURE(r_dot_jacobian);\n          CHECK(gsl::at(r_dot_jacobian, 0) == jacobian_approx(0.0));\n          CHECK(gsl::at(r_dot_jacobian, 1) == jacobian_approx(0.0));\n          CHECK(gsl::at(r_dot_jacobian, 2) ==\n                jacobian_approx(radius_scale_factor * square(expected_radius)));\n        }\n\n        const auto jacobian_times_inverse = tenex::evaluate<ti::I, ti::j>(\n            jacobian(ti::I, ti::k) * inv_jacobian(ti::K, ti::j));\n        const auto inverse_times_jacobian = tenex::evaluate<ti::I, ti::j>(\n            inv_jacobian(ti::I, ti::k) * jacobian(ti::K, ti::j));\n        CAPTURE(jacobian_times_inverse);\n        CAPTURE(inverse_times_jacobian);\n        CHECK_ITERABLE_CUSTOM_APPROX(jacobian_times_inverse,\n                                     (identity<3, double>(0.0)),\n                                     jacobian_approx);\n        CHECK_ITERABLE_CUSTOM_APPROX(inverse_times_jacobian,\n                                     (identity<3, double>(0.0)),\n                                     jacobian_approx);\n      };\n\n      // Check that points on the corners of the reference cube map to\n      // the correct corners of the wedge.\n      for (const double x : {-1.0, 1.0}) {\n        for (const double y : {-1.0, 1.0}) {\n          const std::array inner_logical{x, y, -1.0};\n          const std::array outer_logical{x, y, 1.0};\n          check_point(inner_logical, inner_radius);\n          check_point(outer_logical, outer_radius);\n\n          if (not(which_wedges == WedgeHalves::LowerOnly and x == 1.0) and\n              not(which_wedges == WedgeHalves::UpperOnly and x == -1.0)) {\n            CHECK(map(inner_logical)[2] ==\n                  approx(inner_radius * one_over_denominator));\n            CHECK(map(outer_logical)[2] ==\n                  approx(outer_radius * one_over_denominator));\n          }\n        }\n      }\n\n      // Check at the center of the full wedge, as it is easy to\n      // calculate expected values there.\n      const auto check_center = [&](const double zeta,\n                                    const double expected_radius) {\n        const double xi_center = which_wedges == WedgeHalves::UpperOnly   ? -1.0\n                                 : which_wedges == WedgeHalves::LowerOnly ? 1.0\n                                                                          : 0.0;\n        const std::array logical_point{xi_center, 0.0, zeta};\n        check_point(logical_point, expected_radius);\n\n        CAPTURE(logical_point);\n\n        const auto mapped_point = map(logical_point);\n        CAPTURE(mapped_point);\n\n        CHECK(mapped_point[0] == approx(0.0));\n        CHECK(mapped_point[1] == approx(0.0));\n        CHECK(mapped_point[2] == approx(expected_radius));\n\n        const auto jacobian = map.jacobian(logical_point);\n        const auto inv_jacobian = map.inv_jacobian(logical_point);\n\n        std::array expected_jacobian_diagonal{\n            expected_radius *\n                (with_equiangular_map ? 0.5 * opening_angle_xi : 1.0),\n            expected_radius *\n                (with_equiangular_map ? 0.5 * opening_angle_eta : 1.0),\n            radius_scale_factor * square(expected_radius)};\n        if (which_wedges != WedgeHalves::Both) {\n          expected_jacobian_diagonal[0] *= 0.5;\n        }\n        for (size_t i = 0; i < 3; ++i) {\n          CAPTURE(i);\n          for (size_t j = 0; j < 3; ++j) {\n            CAPTURE(j);\n            if (i == j) {\n              CHECK(jacobian.get(i, i) ==\n                    approx(gsl::at(expected_jacobian_diagonal, i)));\n              CHECK(inv_jacobian.get(i, i) ==\n                    approx(1.0 / gsl::at(expected_jacobian_diagonal, i)));\n            } else {\n              CHECK(jacobian.get(i, j) == approx(0.0));\n              CHECK(inv_jacobian.get(i, j) == approx(0.0));\n            }\n          }\n        }\n      };\n      check_center(-1.0, inner_radius);\n      check_center(1.0, outer_radius);\n\n      check_point(random_inner_logical, inner_radius);\n      check_point(random_outer_logical, outer_radius);\n    }\n  }\n}\n\nvoid test_wedge3d_fail() {\n  INFO(\"Wedge3d fail\");\n\n  {\n    // Check expected behavior for Wedge without offset\n    const Wedge3D centered_map(0.2, 4.0, 0.0, 1.0,\n                               OrientationMap<3>::create_aligned(), true);\n\n    // Any point with z <= 0 should fail the inverse map with no focal offset\n    const std::array<double, 3> test_mapped_point1{{3.0, 3.0, 0.0}};\n    const std::array<double, 3> test_mapped_point2{{-3.0, 3.0, 0.0}};\n\n    // The above Wedge has a Linear radial distribution, so any point where\n    // rho^2 >= (-sphere_rate_/scaled_frustum_rate_)^2 = 1200 should fail for\n    // the inverse map, where rho = r (1 - z_0 / L) / (z - z_0), r is the\n    // distance from the focal_offset_ to the point being mapped, z is the\n    // z-component of the point being mapped, z_0 is the z-component of the\n    // focal_offset_, and L is the cube_half_length_ (see Wedge documentation\n    // for definitions of member variables).\n    const std::array<double, 3> test_mapped_point3{{sqrt(1198.0), 1.0, 1.0}};\n    const std::array<double, 3> test_mapped_point4{{30.0, sqrt(299.0), 1.0}};\n    const std::array<double, 3> test_mapped_point5{{30.0, sqrt(300.0), 1.0}};\n\n    // These points are outside the Wedge, so the inverse should either return\n    // the correct inverse (which happens to be computable for these points) or\n    // it should return nullopt.\n    const std::array<double, 3> test_mapped_point6{{30.0, sqrt(298.0), 1.0}};\n    const std::array<double, 3> test_mapped_point7{{2.0, 4.0, 6.0}};\n\n    CHECK_FALSE(centered_map.inverse(test_mapped_point1).has_value());\n    CHECK_FALSE(centered_map.inverse(test_mapped_point2).has_value());\n    CHECK_FALSE(centered_map.inverse(test_mapped_point3).has_value());\n    CHECK_FALSE(centered_map.inverse(test_mapped_point4).has_value());\n    CHECK_FALSE(centered_map.inverse(test_mapped_point5).has_value());\n    if (centered_map.inverse(test_mapped_point6).has_value()) {\n      Approx my_approx = Approx::custom().epsilon(1.e-10).scale(1.0);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          centered_map(centered_map.inverse(test_mapped_point6).value()),\n          test_mapped_point6, my_approx);\n    }\n    if (centered_map.inverse(test_mapped_point7).has_value()) {\n      CHECK_ITERABLE_APPROX(\n          centered_map(centered_map.inverse(test_mapped_point7).value()),\n          test_mapped_point7);\n    }\n  }\n\n  {\n    const Wedge3D offset_map(0.2, std::nullopt, 1.0, {{0., 0., 0.1}},\n                             OrientationMap<3>::create_aligned(), true);\n\n    // Any point with z <= 0.1 should fail the inverse map with the focal\n    // offset\n    const std::array<double, 3> test_mapped_point1{{0.3, 0.3, 0.1}};\n    const std::array<double, 3> test_mapped_point2{{-0.3, 0.3, 0.1}};\n\n    // These points are outside the Wedge, so the inverse should either return\n    // the correct inverse (which happens to be computable for these points) or\n    // it should return nullopt.\n    const std::array<double, 3> test_mapped_point3{{10.0, 12.0, 14.0}};\n    const std::array<double, 3> test_mapped_point4{{5.0, 5.0, 0.2}};\n\n    CHECK_FALSE(offset_map.inverse(test_mapped_point1).has_value());\n    CHECK_FALSE(offset_map.inverse(test_mapped_point2).has_value());\n    if (offset_map.inverse(test_mapped_point3).has_value()) {\n      Approx my_approx = Approx::custom().epsilon(1.e-10).scale(1.0);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          offset_map(offset_map.inverse(test_mapped_point3).value()),\n          test_mapped_point3, my_approx);\n    }\n    if (offset_map.inverse(test_mapped_point4).has_value()) {\n      CHECK_ITERABLE_APPROX(\n          offset_map(offset_map.inverse(test_mapped_point4).value()),\n          test_mapped_point4);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Wedge3D.Map\", \"[Domain][Unit]\") {\n  test_wedge3d_fail();\n  test_wedge3d_all_directions();\n  test_wedge3d_alignment();\n  test_wedge3d_random_radii();\n  test_wedge3d_large_radius();\n  CHECK(not Wedge3D{}.is_identity());\n\n#ifdef SPECTRE_DEBUG\n  // centered wedge checks\n  CHECK_THROWS_WITH(\n      Wedge3D(-0.2, 4.0, 0.0, 1.0, OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The radius of the inner surface must be greater than zero.\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(0.2, 4.0, -0.2, 1.0, OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"Sphericity of the inner surface must be between 0 and 1\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(0.2, 4.0, 0.0, -0.2, OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"Sphericity of the outer surface must be between 0 and 1\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(4.2, 4.0, 0.0, 1.0, OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The radius of the outer surface must be greater than \"\n          \"the radius of the inner surface.\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(3.0, 4.0, 1.0, 0.0, OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The arguments passed into the constructor for Wedge result in an \"\n          \"object where the outer surface is pierced by the inner surface.\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(0.2, 4.0, 0.8, 0.9, OrientationMap<3>::create_aligned(), true,\n              Wedge3D::WedgeHalves::Both,\n              domain::CoordinateMaps::Distribution::Logarithmic),\n      Catch::Matchers::ContainsSubstring(\n          \"Only the 'Linear' radial distribution is supported for \"\n          \"non-spherical wedges.\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(0.2, 4.0, 0.8, 0.9, OrientationMap<3>::create_aligned(), false,\n              Wedge3D::WedgeHalves::Both,\n              domain::CoordinateMaps::Distribution::Linear,\n              std::array<double, 2>{{M_PI_4 * 0.70, M_PI_4}}),\n      Catch::Matchers::ContainsSubstring(\n          \"If using opening angles other than pi/2, then the \"\n          \"equiangular map option must be turned on.\"));\n\n  // offset wedge checks\n  CHECK_THROWS_WITH(\n      Wedge3D(-0.2, 4.0, 6.0, {{0.1, 0.0, 0.0}},\n              OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The radius of the inner surface must be greater than zero.\"));\n  CHECK_THROWS_WITH(Wedge3D(4.2, 4.0, 6.0, {{0.1, 0.0, 0.0}},\n                            OrientationMap<3>::create_aligned(), true),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The radius of the outer surface must be greater than \"\n                        \"the radius of the inner surface.\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(0.2, std::nullopt, 6.0, {{0.1, 0.0, 0.0}},\n              OrientationMap<3>::create_aligned(), true,\n              Wedge3D::WedgeHalves::Both,\n              domain::CoordinateMaps::Distribution::Logarithmic),\n      Catch::Matchers::ContainsSubstring(\n          \"Only the 'Linear' radial distribution is supported for \"\n          \"non-spherical wedges.\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(3.0, std::nullopt, 2.0, {{0.0, 0.0, 0.0}},\n              OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"The arguments passed into the constructor for Wedge result in an \"\n          \"object where the outer surface is pierced by the inner surface.\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(0.2, 4.0, 1.0, {{5.0, 0.0, 0.0}},\n              OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"For a spherical focally offset Wedge, the sum of the outer radius \"\n          \"and the coordinate of the focal offset with the largest magnitude \"\n          \"must be less than the cube half length. In other words, the \"\n          \"spherical surface at the given outer radius centered at the focal \"\n          \"offset must not pierce the cube of length 2 * cube_half_length_ \"\n          \"centered at the origin. See the Wedge class documentation for a \"\n          \"visual representation of this sphere and cube.\"));\n  CHECK_THROWS_WITH(\n      Wedge3D(0.2, std::nullopt, 1.0, {{5.0, 0.0, 0.0}},\n              OrientationMap<3>::create_aligned(), true),\n      Catch::Matchers::ContainsSubstring(\n          \"For a cubical focally offset Wedge, the sum of the inner radius \"\n          \"and the coordinate of the focal offset with the largest magnitude \"\n          \"must be less than the cube half length. In other words, the \"\n          \"spherical surface at the given inner radius centered at the focal \"\n          \"offset must not pierce the cube of length 2 * cube_half_length_ \"\n          \"centered at the origin. See the Wedge class documentation for a \"\n          \"visual representation of this sphere and cube.\"));\n#endif\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_CoordinateMapsTimeDependent\")\n\nset(LIBRARY_SOURCES\n  Test_CubicScale.cpp\n  Test_ProductMaps.cpp\n  Test_Rotation.cpp\n  Test_RotationMatrixHelpers.cpp\n  Test_RotScaleTrans.cpp\n  Test_Shape.cpp\n  Test_Skew.cpp\n  Test_SphericalCompression.cpp\n  Test_Translation.cpp\n  )\n\nadd_subdirectory(ShapeMapTransitionFunctions)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DataStructuresHelpers\n  MathFunctions\n  FunctionsOfTime\n  SphericalHarmonics\n  Spectral\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  ShapeMapTransitionFunctions/Test_SphereTransition.cpp\n  ShapeMapTransitionFunctions/Test_Wedge.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Test_SphereTransition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Shape.SphereTransition\",\n                  \"[Domain][Unit]\") {\n  constexpr double eps = std::numeric_limits<double>::epsilon() * 100;\n  const size_t l_max = 4;\n  const size_t num_coefs = 2 * square(l_max + 1);\n  const double time = 1.3;\n\n  domain::FunctionsOfTimeMap functions_of_time{};\n  functions_of_time[\"Shape\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          0.0, std::array{DataVector{num_coefs, 1.e-3}}, 10.0);\n\n  const std::vector<std::array<double, 3>> test_points{\n      {2., 0., 0.},\n      {(1.0 - eps) * 2., 0., 0.},\n      {3., 0., 0.},\n      {4., 0., 0.},\n      {(1.0 + eps) * 4., 0., 0.}};\n\n  {\n    INFO(\"Sphere transition\");\n    SphereTransition sphere_transition{2., 4.};\n    sphere_transition = serialize_and_deserialize(sphere_transition);\n    SphereTransition sphere_transition_interior{2., 4., false, true};\n    sphere_transition_interior =\n        serialize_and_deserialize(sphere_transition_interior);\n    const domain::CoordinateMaps::TimeDependent::Shape shape_map{\n        std::array{0.0, 0.0, 0.0}, 0.0,\n        std::make_unique<SphereTransition>(sphere_transition), \"Shape\"};\n    const domain::CoordinateMaps::TimeDependent::Shape shape_map_interior{\n        std::array{0.0, 0.0, 0.0}, 0.0,\n        std::make_unique<SphereTransition>(sphere_transition_interior),\n        \"Shape\"};\n\n    const std::vector<double> function_values{0.5, 0.5, 0.5 / 3.0, 0.0, 0.0};\n\n    for (size_t i = 0; i < test_points.size(); i++) {\n      CAPTURE(test_points[i]);\n      CAPTURE(function_values[i]);\n      CHECK(sphere_transition(test_points[i], std::nullopt) ==\n            approx(function_values[i]));\n      const double radius = std::max(magnitude(test_points[i]), 2.0);\n      CHECK(sphere_transition(test_points[i], {1}) ==\n            approx(function_values[i] / radius));\n      test_inverse_map(shape_map, test_points[i], time, functions_of_time);\n    }\n\n    const std::vector<std::array<double, 3>> interior_points{{1.0, 0.0, 0.0},\n                                                             {0.0, 0.0, 0.0}};\n    const std::vector<double> interior_function_values{0.125, 0.0};\n    for (size_t i = 0; i < interior_points.size(); i++) {\n      CAPTURE(interior_points[i]);\n      CHECK(sphere_transition_interior(interior_points[i], std::nullopt) ==\n            interior_function_values[i]);\n      test_inverse_map(shape_map_interior, interior_points[i], time,\n                       functions_of_time);\n    }\n\n    // Check close, but not at, center and r_min\n    test_inverse_map(shape_map_interior, std::array{2.0 * eps, 0.0, 0.0}, time,\n                     functions_of_time);\n    test_inverse_map(shape_map_interior,\n                     std::array{(1.0 - eps) * 2.0, 0.0, 0.0}, time,\n                     functions_of_time);\n  }\n  {\n    INFO(\"Reverse sphere transition\");\n    SphereTransition sphere_transition{2., 4., true};\n    sphere_transition = serialize_and_deserialize(sphere_transition);\n\n    const domain::CoordinateMaps::TimeDependent::Shape shape_map{\n        std::array{0.0, 0.0, 0.0}, 0.0,\n        std::make_unique<SphereTransition>(sphere_transition), \"Shape\"};\n\n    const std::vector<double> function_values{0.0, 0.0, 0.5 / 3.0, 0.25, 0.25};\n\n    for (size_t i = 0; i < test_points.size(); i++) {\n      CAPTURE(test_points[i]);\n      CAPTURE(function_values[i]);\n      CHECK(sphere_transition(test_points[i], std::nullopt) ==\n            approx(function_values[i]));\n      const double radius = std::min(magnitude(test_points[i]), 4.0);\n      CHECK(sphere_transition(test_points[i], {1}) ==\n            approx(function_values[i] /\n                   (equal_within_roundoff(radius, 0.0) ? 1.0 : radius)));\n      test_inverse_map(shape_map, test_points[i], time, functions_of_time);\n    }\n  }\n}\n\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Test_Wedge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <random>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Wedge.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain::CoordinateMaps::ShapeMapTransitionFunctions {\nnamespace {\n// These don't really need to change so we make them global\nconstexpr double inner_radius = 0.5;\nconstexpr double outer_radius = 10.0;\n\nstd::array<double, 3> sph_to_cart(const double radius, const double theta,\n                                  const double phi) {\n  return std::array{radius * sin(theta) * cos(phi),\n                    radius * sin(theta) * sin(phi), radius * cos(theta)};\n}\n\nWedge make_wedge(\n    const double inner_sphericity, const double outer_sphericity,\n    const std::array<double, 3>& inner_center = std::array{0.0, 0.0, 0.0},\n    const std::array<double, 3>& outer_center = std::array{0.0, 0.0, 0.0},\n    const Wedge::Axis axis = Wedge::Axis::PlusZ, const bool reverse = false) {\n  const Wedge wedge{inner_center, inner_radius, inner_sphericity,\n                    outer_center, outer_radius, outer_sphericity,\n                    axis,         reverse};\n  return serialize_and_deserialize(wedge);\n}\n\n// Since this is hard to test for general points without just reimplementing the\n// function, we test for specific points where it's easier to calculate\nvoid test_gradient_no_offset() {\n  INFO(\"Test gradient no offset\");\n\n  const auto compute_gradient =\n      [&](const std::array<double, 3>& point, const double inner_distance,\n          const double outer_distance, const double inner_sphericity,\n          const double outer_sphericity) -> std::array<double, 3> {\n    const double radius = magnitude(point);\n    const double distance_difference = outer_distance - inner_distance;\n    const std::array<double, 3> grad_base =\n        1.0 / (sqrt(3.0) * radius) *\n        std::array{point[0], point[1],\n                   -(square(radius) - square(point[2])) / point[2]} /\n        point[2];\n    const std::array<double, 3> inner_grad =\n        (1.0 - inner_sphericity) * inner_radius * grad_base;\n    const std::array<double, 3> outer_grad =\n        (1.0 - outer_sphericity) * outer_radius * grad_base;\n    return ((outer_grad - point / radius) / distance_difference -\n            (outer_distance - radius) * (outer_grad - inner_grad) /\n                square(distance_difference)) /\n               radius -\n           point * (outer_distance - radius) / distance_difference /\n               cube(radius);\n  };\n\n  const auto test_z_axis = [&](const double inner_sphericity,\n                               const double outer_sphericity) {\n    INFO(\"Test z-axis\");\n    const Wedge wedge = make_wedge(inner_sphericity, outer_sphericity);\n    double inner_distance = inner_radius;\n    double outer_distance = outer_radius;\n    if (inner_sphericity == 0.0) {\n      inner_distance /= sqrt(3.0);\n    }\n    if (outer_sphericity == 0.0) {\n      outer_distance /= sqrt(3.0);\n    }\n\n    for (const double radius : {inner_distance, outer_distance,\n                                0.5 * (inner_distance + outer_distance)}) {\n      CAPTURE(radius);\n      const std::array point{0.0, 0.0, radius};\n      CAPTURE(point);\n\n      CHECK_ITERABLE_APPROX(\n          wedge.gradient(point),\n          (compute_gradient(point, inner_distance, outer_distance,\n                            inner_sphericity, outer_sphericity)));\n    }\n  };\n\n  const auto test_corners = [&](const double inner_sphericity,\n                                const double outer_sphericity) {\n    INFO(\"Test corners\");\n    const Wedge wedge = make_wedge(inner_sphericity, outer_sphericity);\n    // Corners will always be at sphere radius\n    for (const double radius :\n         {inner_radius, outer_radius, 0.5 * (inner_radius + outer_radius)}) {\n      for (const double phi :\n           {M_PI_4, 3.0 * M_PI_4, 5.0 * M_PI_4, 7.0 * M_PI_4}) {\n        CAPTURE(radius);\n        CAPTURE(phi * 180.0 / M_PI);\n        const std::array point =\n            sph_to_cart(radius, acos(1.0 / sqrt(3.0)), phi);\n        CAPTURE(point);\n\n        CHECK_ITERABLE_APPROX(\n            wedge.gradient(point),\n            (compute_gradient(point, inner_radius, outer_radius,\n                              inner_sphericity, outer_sphericity)));\n      }\n    }\n  };\n\n  {\n    INFO(\"Both boundaries spherical\");\n    test_z_axis(1.0, 1.0);\n    test_corners(1.0, 1.0);\n  }\n  {\n    INFO(\"Inner boundary spherical, outer boundary flat\");\n    test_z_axis(1.0, 0.0);\n    test_corners(1.0, 0.0);\n  }\n  {\n    INFO(\"Inner boundary flat, outer boundary spherical\");\n    test_z_axis(0.0, 1.0);\n    test_corners(0.0, 1.0);\n  }\n  {\n    INFO(\"Both boundaries flat\");\n    test_z_axis(0.0, 0.0);\n    test_corners(0.0, 0.0);\n  }\n  {\n    INFO(\"Interior region\");\n    const auto center_wedge =\n        make_wedge(1.0, 0.0, std::array{0.0, 0.0, 0.0},\n                   std::array{0.0, 0.0, 0.0}, Wedge::Axis::Interior);\n    CHECK(center_wedge.gradient(std::array{0.0, 0.0, 0.0}) ==\n          std::array{0.0, 0.0, 0.0});\n    CHECK(center_wedge.gradient(std::array{0.0, 0.0, inner_radius}) ==\n          std::array{0.0, 0.0, 8.0});\n    CHECK(center_wedge.gradient(std::array{0.0, 0.0, 0.5 * inner_radius}) ==\n          std::array{0.0, 0.0, 4.0});\n  }\n}\n\nvoid test_only_transition_no_offset() {\n  INFO(\"Test only transition no offset\");\n  const double inner_sphericity = 1.0;\n  const double outer_sphericity = 0.0;\n  const std::array<double, 3> inner_center{0.0, 0.0, 0.0};\n  const std::array<double, 3> outer_center{0.0, 0.0, 0.0};\n\n  const Wedge wedge =\n      make_wedge(inner_sphericity, outer_sphericity, inner_center, outer_center,\n                 Wedge::Axis::PlusX);\n  const Wedge wedge_reverse =\n      make_wedge(inner_sphericity, outer_sphericity, inner_center, outer_center,\n                 Wedge::Axis::PlusX, true);\n\n  const double inner_distance = 0.5;\n  const double outer_distance = outer_radius / sqrt(3.0);\n  const double distance_difference = outer_distance - inner_distance;\n\n  std::array<double, 3> point{4.0, 0.0, 0.0};\n  const double linear_function_value =\n      (outer_distance - 4.0) / distance_difference;\n\n  CHECK(wedge(point, std::nullopt) == approx(0.25 * linear_function_value));\n  CHECK(wedge(point, {1}) == approx(0.0625 * linear_function_value));\n  CHECK(wedge_reverse(point, std::nullopt) ==\n        approx(0.25 * (1.0 - linear_function_value)));\n  CHECK(wedge_reverse(point, {1}) ==\n        approx(0.0625 * (1.0 - linear_function_value)));\n  CHECK_ITERABLE_APPROX(\n      wedge.gradient(point),\n      (0.25 * std::array{-1.0 / distance_difference, 0.0, 0.0} -\n       cube(0.25) * linear_function_value * point));\n  CHECK_ITERABLE_APPROX(\n      wedge_reverse.gradient(point),\n      (0.25 * std::array{1.0 / distance_difference, 0.0, 0.0} -\n       cube(0.25) * (1.0 - linear_function_value) * point));\n\n  std::optional<double> orig_rad_over_rad{};\n  std::optional<double> orig_rad_over_rad_reverse{};\n  const auto set_orig_rad_over_rad =\n      [&](const std::array<double, 3>& mapped_point,\n          const double radial_distortion) {\n        orig_rad_over_rad =\n            wedge.original_radius_over_radius(mapped_point, radial_distortion);\n        orig_rad_over_rad_reverse = wedge_reverse.original_radius_over_radius(\n            mapped_point, radial_distortion);\n      };\n\n  // Test actual values\n  set_orig_rad_over_rad(point, 0.0);\n  CHECK(orig_rad_over_rad.has_value());\n  CHECK(orig_rad_over_rad.value() == approx(1.0));\n  CHECK(orig_rad_over_rad_reverse.has_value());\n  CHECK(orig_rad_over_rad_reverse.value() == approx(1.0));\n  set_orig_rad_over_rad(point * (1.0 - 0.25 * linear_function_value * 0.5),\n                        0.5);\n  CHECK(orig_rad_over_rad.has_value());\n  CHECK(orig_rad_over_rad.value() ==\n        approx(4.0 /\n               magnitude(point * (1.0 - 0.25 * linear_function_value * 0.5))));\n  set_orig_rad_over_rad(point * (1.0 + 0.25 * linear_function_value * 0.5),\n                        -0.5);\n  CHECK(orig_rad_over_rad.has_value());\n  CHECK(orig_rad_over_rad.value() ==\n        approx(4.0 /\n               magnitude(point * (1.0 + 0.25 * linear_function_value * 0.5))));\n  set_orig_rad_over_rad(point * 15.0, 0.0);\n  CHECK_FALSE(orig_rad_over_rad.has_value());\n  // Hit some internal checks\n  set_orig_rad_over_rad(point * 0.0, 0.0);\n  CHECK_FALSE(orig_rad_over_rad == std::optional{1.0});\n  CHECK_FALSE(orig_rad_over_rad_reverse == std::optional{1.0});\n  // Wedge is in x direction. Check other directions\n  const auto check_other_directions =\n      [&](const std::array<double, 3>& mapped_point) {\n        set_orig_rad_over_rad(mapped_point, 0.0);\n        CHECK(orig_rad_over_rad.has_value());\n        CHECK(orig_rad_over_rad.value() == 1.0);\n        CHECK(orig_rad_over_rad_reverse.has_value());\n        CHECK(orig_rad_over_rad_reverse.value() == 1.0);\n      };\n  check_other_directions(std::array{-4.0, 0.0, 0.0});\n  check_other_directions(std::array{0.0, 0.0, 4.0});\n  check_other_directions(std::array{4.0, 0.0, -4.0});\n  check_other_directions(std::array{0.0, 4.0, 0.0});\n  check_other_directions(std::array{0.0, -4.0, 0.0});\n  // At overall inner boundary.\n  set_orig_rad_over_rad(std::array{0.0, 0.0, 0.2 * inner_radius}, 0.4);\n  CHECK(orig_rad_over_rad.has_value());\n  CHECK(orig_rad_over_rad.value() == approx(5.0));\n  // Inside inner boundary\n  set_orig_rad_over_rad(std::array{0.0, 0.0, 0.1 * inner_radius}, 0.4);\n  CHECK_FALSE(orig_rad_over_rad.has_value());\n}\n\nvoid test_only_transition_offset() {\n  INFO(\"Test only transition offset\");\n  const double inner_sphericity = 1.0;\n  const double outer_sphericity = 0.0;\n  const std::array<double, 3> inner_center{-1.0, -1.0, 0.0};\n  const std::array<double, 3> outer_center{0.0, 0.0, 0.0};\n\n  const Wedge wedge =\n      make_wedge(inner_sphericity, outer_sphericity, inner_center, outer_center,\n                 Wedge::Axis::PlusX);\n  const Wedge wedge_reverse =\n      make_wedge(inner_sphericity, outer_sphericity, inner_center, outer_center,\n                 Wedge::Axis::PlusX, true);\n\n  // All these values were calculated by hand given the centers above and this\n  // specific point below\n  const double half_cube_length = outer_radius / sqrt(3.0);\n  const std::array<double, 3> point{4.0, 0.0, 0.0};\n  const std::array<double, 3> centered_point = point - inner_center;\n  const double radius_from_inner_center = magnitude(centered_point);\n\n  const double inner_distance = 0.5;\n  const double outer_distance = (half_cube_length + 1) * sqrt(26.0) / 5.0;\n  const double distance_difference = outer_distance - inner_distance;\n\n  const double linear_function_value =\n      (outer_distance - radius_from_inner_center) / distance_difference;\n\n  CHECK(wedge(centered_point, std::nullopt) ==\n        approx(linear_function_value / radius_from_inner_center));\n  CHECK(wedge(centered_point, {1}) ==\n        approx(linear_function_value / square(radius_from_inner_center)));\n  CHECK(wedge_reverse(centered_point, std::nullopt) ==\n        approx((1.0 - linear_function_value) / radius_from_inner_center));\n  CHECK(\n      wedge_reverse(centered_point, {1}) ==\n      approx((1.0 - linear_function_value) / square(radius_from_inner_center)));\n  const std::array<double, 3> projection_center = inner_center - outer_center;\n  // Because of PlusX\n  const std::array<double, 3> outer_gradient =\n      centered_point / radius_from_inner_center *\n          (half_cube_length - projection_center[0]) / centered_point[0] -\n      std::array{\n          (half_cube_length - projection_center[0]) / square(centered_point[0]),\n          0.0, 0.0} *\n          radius_from_inner_center;\n  const std::array<double, 3> expected_linear_gradient =\n      (outer_gradient - centered_point / radius_from_inner_center) /\n          distance_difference -\n      ((outer_distance - radius_from_inner_center) * outer_gradient /\n       square(distance_difference));\n  CHECK_ITERABLE_APPROX(wedge.gradient(centered_point),\n                        expected_linear_gradient / radius_from_inner_center -\n                            centered_point * linear_function_value /\n                                cube(radius_from_inner_center));\n  CHECK_ITERABLE_APPROX(\n      wedge_reverse.gradient(centered_point),\n      -1.0 * expected_linear_gradient / radius_from_inner_center -\n          centered_point * (1.0 - linear_function_value) /\n              cube(radius_from_inner_center));\n\n  std::optional<double> orig_rad_over_rad{};\n  std::optional<double> orig_rad_over_rad_reverse{};\n  const auto set_orig_rad_over_rad =\n      [&](const std::array<double, 3>& mapped_point,\n          const double radial_distortion) {\n        orig_rad_over_rad =\n            wedge.original_radius_over_radius(mapped_point, radial_distortion);\n        orig_rad_over_rad_reverse = wedge_reverse.original_radius_over_radius(\n            mapped_point, radial_distortion);\n      };\n\n  // Test actual values\n  set_orig_rad_over_rad(centered_point, 0.0);\n  CHECK(orig_rad_over_rad.has_value());\n  CHECK(orig_rad_over_rad.value() == approx(1.0));\n  CHECK(orig_rad_over_rad_reverse.has_value());\n  CHECK(orig_rad_over_rad_reverse.value() == approx(1.0));\n\n  std::array<double, 3> mapped_point =\n      centered_point *\n      (1.0 - linear_function_value / radius_from_inner_center * 0.5);\n  set_orig_rad_over_rad(mapped_point, 0.5);\n  CHECK(orig_rad_over_rad.has_value());\n  CHECK(orig_rad_over_rad.value() ==\n        approx(radius_from_inner_center / magnitude(mapped_point)));\n\n  mapped_point = centered_point * (1.0 - (1.0 - linear_function_value) /\n                                             radius_from_inner_center * 0.5);\n  set_orig_rad_over_rad(mapped_point, 0.5);\n  CHECK(orig_rad_over_rad_reverse.has_value());\n  CHECK(orig_rad_over_rad_reverse.value() ==\n        approx(radius_from_inner_center / magnitude(mapped_point)));\n\n  mapped_point = centered_point *\n                 (1.0 + linear_function_value / radius_from_inner_center * 0.5);\n  set_orig_rad_over_rad(mapped_point, -0.5);\n  CHECK(orig_rad_over_rad.has_value());\n  CHECK(orig_rad_over_rad.value() ==\n        approx(radius_from_inner_center / magnitude(mapped_point)));\n\n  mapped_point = centered_point * (1.0 + (1.0 - linear_function_value) /\n                                             radius_from_inner_center * 0.5);\n  set_orig_rad_over_rad(mapped_point, -0.5);\n  CHECK(orig_rad_over_rad_reverse.has_value());\n  CHECK(orig_rad_over_rad_reverse.value() ==\n        approx(radius_from_inner_center / magnitude(mapped_point)));\n\n  // Hit some internal checks\n  set_orig_rad_over_rad(centered_point * 0.0, 0.0);\n  CHECK_FALSE(orig_rad_over_rad.has_value());\n  CHECK_FALSE(orig_rad_over_rad_reverse.has_value());\n  set_orig_rad_over_rad(centered_point * 15.0, 0.0);\n  CHECK_FALSE(orig_rad_over_rad.has_value());\n\n  // Wedge is in x direction. Check other directions\n  const auto check_other_directions =\n      [&](const std::array<double, 3>& new_mapped_point) {\n        set_orig_rad_over_rad(new_mapped_point, 0.0);\n        CHECK(orig_rad_over_rad.has_value());\n        CHECK(orig_rad_over_rad.value() == 1.0);\n        CHECK(orig_rad_over_rad_reverse.has_value());\n        CHECK(orig_rad_over_rad_reverse.value() == 1.0);\n      };\n  check_other_directions(std::array{-4.0, 0.0, 0.0});\n  check_other_directions(std::array{0.0, 0.0, 4.0});\n  check_other_directions(std::array{4.0, 0.0, -4.0});\n  check_other_directions(std::array{0.0, 4.0, 0.0});\n  check_other_directions(std::array{0.0, -4.0, 0.0});\n\n  // At overall inner boundary.\n  set_orig_rad_over_rad(std::array{0.0, 0.0, 0.2 * inner_radius}, 0.4);\n  CHECK(orig_rad_over_rad.has_value());\n  CHECK(orig_rad_over_rad.value() == approx(5.0));\n}\n\n// This also doesn't need to change\nconstexpr size_t l_max = 4;\n\n// We add an option to check the jacobians because if a point is on a boundary,\n// then the numerical derivative will fail because the operator() isn't defined\n// outside the boundary\ntemplate <typename T>\nvoid test_points_shape_map(\n    const double inner_sphericity, const double outer_sphericity,\n    const std::array<double, 3>& inner_center,\n    const std::array<double, 3>& outer_center, const Wedge::Axis axis,\n    const double check_time, const std::string& fot_name,\n    const FunctionsOfTimeMap& functions_of_time, const std::array<T, 3>& points,\n    const bool reverse = false, const bool check_jacobians = true) {\n  std::unique_ptr<ShapeMapTransitionFunction> wedge = std::make_unique<Wedge>(\n      inner_center, inner_radius, inner_sphericity, outer_center, outer_radius,\n      outer_sphericity, static_cast<Wedge::Axis>(axis), reverse);\n\n  const TimeDependent::Shape shape{inner_center, 0.0, std::move(wedge),\n                                   fot_name};\n\n  // test_coordinate_map_argument_types creates its own DataVectors and we only\n  // allow the inverse to be called with doubles\n  if constexpr (std::is_same_v<T, double>) {\n    // If a point is beyond the inner or outer surfaces, then we can't call the\n    // Jacobian, and test_coordinate_map_argument_types() has a jacobian call in\n    // it.\n    if (check_jacobians) {\n      test_coordinate_map_argument_types(shape, points, check_time,\n                                         functions_of_time);\n    }\n    test_inverse_map(shape, points, check_time, functions_of_time);\n  }\n\n  test_frame_velocity(shape, points, check_time, functions_of_time);\n\n  if (check_jacobians) {\n    test_jacobian(shape, points, check_time, functions_of_time);\n    test_inv_jacobian(shape, points, check_time, functions_of_time);\n  }\n}\n\ntemplate <typename Generator>\nvoid test_in_shape_map_no_offset(const gsl::not_null<Generator*> generator,\n                                 const bool reverse) {\n  INFO(\"Test using shape map without offset\");\n  CAPTURE(reverse);\n  std::uniform_real_distribution<double> coef_dist{-0.01, 0.01};\n\n  const double initial_time = 1.0;\n  const double check_time = 2.0;\n  const size_t num_coefs = 2 * square(l_max + 1);\n  const std::array center{0.1, -0.2, 0.3};\n  const std::string fot_name{\"TheBean\"};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  auto coefs = make_with_random_values<DataVector>(\n      generator, make_not_null(&coef_dist), DataVector{num_coefs});\n  functions_of_time[fot_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          initial_time, std::array{std::move(coefs)},\n          std::numeric_limits<double>::infinity());\n\n  std::uniform_real_distribution<double> angle_dist{0.0, 2.0 * M_PI};\n  // This guarantees the radius of the point is within an acceptable distance of\n  // the inner/outer surface of the wedge even if we are reversed\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::uniform_real_distribution<double> radial_dist{\n      inner_radius + 1.e-3, outer_radius / sqrt(3.0) - 1.e-3};\n\n  for (const auto& [inner_sphericity, outer_sphericity] :\n       cartesian_product(std::array{1.0, 0.0}, std::array{1.0, 0.0})) {\n    CAPTURE(inner_sphericity);\n    CAPTURE(outer_sphericity);\n\n    // Test 10 points for each sphericity and orientation\n    for (size_t i = 0; i < 10; i++) {\n      const double radius = radial_dist(*generator);\n      const double theta = 0.5 * angle_dist(*generator);\n      const double phi = angle_dist(*generator);\n\n      CAPTURE(radius);\n      CAPTURE(theta);\n      CAPTURE(phi);\n\n      const std::array<double, 3> centered_point =\n          sph_to_cart(radius, theta, phi);\n\n      int axis = 0;\n      double max = abs(centered_point[0]);\n      for (int j = 1; j < 3; j++) {\n        if (const double maybe_max = abs(gsl::at(centered_point, j));\n            maybe_max > max) {\n          axis = j;\n          max = maybe_max;\n        }\n      }\n\n      // Because Wedge::Axis is defined slightly differently\n      const int axis_for_wedge =\n          (axis + 1) * (gsl::at(centered_point, axis) > 0.0 ? 1 : -1);\n\n      CAPTURE(centered_point);\n      CAPTURE(axis_for_wedge);\n\n      test_points_shape_map(inner_sphericity, outer_sphericity, center, center,\n                            static_cast<Wedge::Axis>(axis_for_wedge),\n                            check_time, fot_name, functions_of_time,\n                            centered_point + center, reverse, true);\n    }\n  }\n\n  const double eps = std::numeric_limits<double>::epsilon();\n  // Check for points within the interior\n  for (const auto& point :\n       {center, center + std::array{0.0, 2.0 * eps, 0.0},\n        center + std::array{0.0, 0.5 * inner_radius, 0.0},\n        center + std::array{0.0, (1.0 - 2.0 * eps) * inner_radius, 0.0},\n        center + std::array{0.0, inner_radius, 0.0}}) {\n    test_points_shape_map(1.0, 0.0, center, center, Wedge::Axis::Interior,\n                          check_time, fot_name, functions_of_time, point);\n  }\n}\n\ntemplate <typename Generator>\nvoid test_in_shape_map_offset(const gsl::not_null<Generator*> generator,\n                              const bool reverse) {\n  INFO(\"Test using shape map with offset\");\n  CAPTURE(reverse);\n  std::uniform_real_distribution<double> coef_dist{-0.01, 0.01};\n  // This guarantees the inner center will be inside the outer surface\n  std::uniform_real_distribution<double> center_dist{-2.0, 2.0};\n  const double half_cube_length = outer_radius / sqrt(3.0);\n\n  const double initial_time = 1.0;\n  const double check_time = 2.0;\n  const size_t num_coefs = 2 * square(l_max + 1);\n  const std::string fot_name{\"TheBean2.0\"};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  auto coefs = make_with_random_values<DataVector>(\n      generator, make_not_null(&coef_dist), DataVector{num_coefs});\n  functions_of_time[fot_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          initial_time, std::array{std::move(coefs)},\n          std::numeric_limits<double>::infinity());\n\n  // Worst case scenario is that inner and outer center are exactly opposite of\n  // each other, (2,2,2) and (-2,-2,-2) for example. Given that the half cube\n  // length L = 10/sqrt(3) = 5.77, we can take the smallest angle of any wedge\n  // and use that for all wedges to be safe.\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::uniform_real_distribution<double> theta_dist{\n      0.0, atan2(half_cube_length - 4.0, half_cube_length + 4.0)};\n  std::uniform_real_distribution<double> phi_dist{0.0, 2.0 * M_PI};  // NOLINT\n  std::uniform_int_distribution<int> direction_dist{0, 2};           // NOLINT\n  // This is a map between the integer representation of axes from the\n  // Wedge::Axis class to the index of the `orientation_maps` array\n  const std::unordered_map<int, int> axes{{3, 0},  {-3, 1}, {2, 2},\n                                          {-2, 3}, {1, 4},  {-1, 5}};\n  const auto orientation_maps = orientations_for_sphere_wrappings();\n\n  for (const double outer_sphericity :\n       std::array{1.0, 0.0, phi_dist(*generator) / (2.0 * M_PI)}) {\n    CAPTURE(outer_sphericity);\n\n    const auto inner_center = make_with_random_values<std::array<double, 3>>(\n        generator, make_not_null(&center_dist));\n    const auto outer_center = make_with_random_values<std::array<double, 3>>(\n        generator, make_not_null(&center_dist));\n\n    CAPTURE(inner_center);\n    CAPTURE(outer_center);\n\n    const auto make_point = [&](const std::optional<int>& axis_opt,\n                                const std::optional<int>& axis_sign_opt)\n        -> std::pair<int, std::array<double, 3>> {\n      const int axis = axis_opt.value_or(direction_dist(*generator));\n      const int axis_sign =\n          axis_sign_opt.value_or(coef_dist(*generator) > 0.0 ? 1 : -1);\n      const int axis_for_wedge = axis_sign * (axis + 1);\n      CAPTURE(axis_for_wedge);\n      // Compute the distance directly along the axis in the direction of this\n      // wedge to the cube surface. This is the smallest outer distance that\n      // will always work\n      const double minimal_outer_distance =\n          static_cast<double>(axis_sign) *\n              gsl::at(outer_center - inner_center, axis) +\n          half_cube_length;\n\n      // This guarantees the radius of the point is within the wedge and that\n      // numerical jacobians will succeed\n      // NOLINTNEXTLINE(misc-const-correctness)\n      std::uniform_real_distribution<double> radial_dist{\n          inner_radius + 1.e-3, minimal_outer_distance - 1.e-3};\n\n      // We construct points aligned in the +z direction with all the proper\n      // angles, and then rotate it to the correct wedge.\n      const double radius = radial_dist(*generator);\n      const double theta = theta_dist(*generator);\n      const double phi = phi_dist(*generator);\n      const std::array<double, 3> unrotated_point =\n          sph_to_cart(radius, theta, phi);\n      const DiscreteRotation<3> rotation{\n          gsl::at(orientation_maps, axes.at(axis_for_wedge))};\n\n      return {axis_for_wedge, rotation(unrotated_point) + inner_center};\n    };\n\n    // Test 10 random double points for each sphericity\n    for (size_t i = 0; i < 10; i++) {\n      INFO(\"Doubles\");\n      const auto [axis_for_wedge, point] =\n          make_point(std::nullopt, std::nullopt);\n\n      CAPTURE(axis_for_wedge);\n      CAPTURE(point);\n\n      test_points_shape_map(1.0, outer_sphericity, inner_center, outer_center,\n                            static_cast<Wedge::Axis>(axis_for_wedge),\n                            check_time, fot_name, functions_of_time, point,\n                            reverse);\n    }\n\n    // Now do a DataVector\n    {\n      INFO(\"DataVector\");\n      std::array<DataVector, 3> points = make_array<3>(DataVector{10_st});\n      const int axis = direction_dist(*generator);\n      const int axis_sign = coef_dist(*generator) > 0.0 ? 1 : -1;\n      const int axis_for_wedge = axis_sign * (axis + 1);\n      for (size_t i = 0; i < points[0].size(); i++) {\n        const auto axis_and_point = make_point(axis, axis_sign);\n        for (size_t j = 0; j < 3; j++) {\n          gsl::at(points, j)[i] = gsl::at(axis_and_point.second, j);\n        }\n      }\n\n      CAPTURE(axis_sign);\n      CAPTURE(points);\n\n      test_points_shape_map(1.0, outer_sphericity, inner_center, outer_center,\n                            static_cast<Wedge::Axis>(axis_for_wedge),\n                            check_time, fot_name, functions_of_time, points,\n                            reverse);\n    }\n\n    // Test all eight corners of the outer surface, but for each axis as well so\n    // we get all the wedges.\n    INFO(\"Testing corners with offset\");\n    for (const auto& [axis_for_wedge, unused] : axes) {\n      (void)unused;\n      const int axis = abs(axis_for_wedge) - 1;\n      const double axis_sign = axis_for_wedge > 0 ? 1.0 : -1.0;\n\n      CAPTURE(axis_for_wedge);\n\n      for (const double other_length_1 :\n           {-half_cube_length, half_cube_length}) {\n        for (const double other_length_2 :\n             {-half_cube_length, half_cube_length}) {\n          std::array<double, 3> point = outer_center;\n          gsl::at(point, axis) += axis_sign * half_cube_length;\n          gsl::at(point, (axis + 1) % 3) += other_length_1;\n          gsl::at(point, (axis + 2) % 3) += other_length_2;\n          CAPTURE(point);\n\n          // Don't test jacobians at corners because of numerical derivatives\n          test_points_shape_map(\n              1.0, outer_sphericity, inner_center, outer_center,\n              static_cast<Wedge::Axis>(axis_for_wedge), check_time, fot_name,\n              functions_of_time, point, reverse, false);\n        }  // for other_length_2\n      }    // for other_length_1\n    }      // for axis\n\n    const double eps = std::numeric_limits<double>::epsilon();\n    // Check for points within the interior\n    for (const auto& point :\n         {inner_center, inner_center + std::array{0.0, 2.0 * eps, 0.0},\n          inner_center + std::array{0.0, 0.5 * inner_radius, 0.0},\n          inner_center + std::array{0.0, (1.0 - 2.0 * eps) * inner_radius, 0.0},\n          inner_center + std::array{0.0, inner_radius, 0.0}}) {\n      test_points_shape_map(1.0, outer_sphericity, inner_center, outer_center,\n                            Wedge::Axis::Interior, check_time, fot_name,\n                            functions_of_time, point);\n    }\n  }  // for outer sphericity\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      (Wedge{std::array{0.0, 0.0, 0.0}, inner_radius, 0.5,\n             std::array{1.0, 1.0, 1.0}, outer_radius, 1.0, Wedge::Axis::PlusZ}),\n      Catch::Matchers::ContainsSubstring(\n          \"The sphericity of the inner surface must be exactly 1.0 if the \"\n          \"centers of the inner and outer object are different.\"));\n\n  CHECK_THROWS_WITH(\n      (Wedge{std::array{outer_radius * 2.0, 0.0, 0.0}, inner_radius, 1.0,\n             std::array{0.0, 0.0, 0.0}, outer_radius, 1.0, Wedge::Axis::PlusZ}),\n      Catch::Matchers::ContainsSubstring(\"The inner surface center\") and\n          Catch::Matchers::ContainsSubstring(\n              \"must be within the outer surface with center\"));\n\n  CHECK_THROWS_WITH(\n      (Wedge{std::array{0.0, 0.0, 0.0}, inner_radius, 0.5,\n             std::array{0.0, 0.0, 0.0}, outer_radius, 1.0, Wedge::Axis::None}),\n      Catch::Matchers::ContainsSubstring(\"Axis for Wedge shape map transition \"\n                                         \"function cannot be 'None'. Please \"\n                                         \"choose another.\"));\n\n  CHECK_THROWS_WITH(\n      (Wedge{std::array{0.0, 0.0, 0.0}, inner_radius, 1.0,\n             std::array{0.0, 0.0, 0.0}, outer_radius, 1.0,\n             Wedge::Axis::Interior, true}),\n      Catch::Matchers::ContainsSubstring(\n          \"When the axis is 'Interior', the inner surface must be a sphere \"\n          \"(sphericity 1.0) and reverse must be 'false'.\"));\n  CHECK_THROWS_WITH(\n      (Wedge{std::array{0.0, 0.0, 0.0}, inner_radius, 0.5,\n             std::array{0.0, 0.0, 0.0}, outer_radius, 1.0,\n             Wedge::Axis::Interior}),\n      Catch::Matchers::ContainsSubstring(\n          \"When the axis is 'Interior', the inner surface must be a sphere \"\n          \"(sphericity 1.0) and reverse must be 'false'.\"));\n}\n\ntemplate <typename Generator>\nvoid test_no_offset(const gsl::not_null<Generator*> generator) {\n  test_only_transition_no_offset();\n  test_gradient_no_offset();\n  test_in_shape_map_no_offset(generator, false);\n  test_in_shape_map_no_offset(generator, true);\n}\n\ntemplate <typename Generator>\nvoid test_offset(const gsl::not_null<Generator*> generator) {\n  test_only_transition_offset();\n  // We don't test the gradient explicitly here because the formulas would be\n  // too complicated, so we just let it be tested in the shape map test\n  test_in_shape_map_offset(generator, false);\n  test_in_shape_map_offset(generator, true);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.Shape.Wedge\", \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  domain::CoordinateMaps::ShapeMapTransitionFunctions::\n      register_derived_with_charm();\n  test_no_offset(make_not_null(&generator));\n  test_offset(make_not_null(&generator));\n  test_errors();\n}\n}  // namespace\n}  // namespace domain::CoordinateMaps::ShapeMapTransitionFunctions\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_CubicScale.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\nnamespace {\n// We will call with this function with arguments that are designed\n// to fail in a certain way.\nvoid cubic_scale_non_invertible(const double a0, const double b0,\n                                const double outer_boundary) {\n  double t = 4.0;\n  const double delta_t = 20.0;\n  constexpr size_t deriv_order = 2;\n\n  const std::array<DataVector, deriv_order + 1> init_func_a{\n      {{a0}, {0.0}, {0.0}}};\n  const std::array<DataVector, deriv_order + 1> init_func_b{\n      {{b0}, {0.0}, {0.0}}};\n\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n  std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n  f_of_t_list[\"expansion_a\"] =\n      std::make_unique<Polynomial>(t, init_func_a, t + delta_t);\n  f_of_t_list[\"expansion_b\"] =\n      std::make_unique<Polynomial>(t, init_func_b, t + delta_t);\n\n  const FoftPtr& expansion_a_base = f_of_t_list.at(\"expansion_a\");\n  const FoftPtr& expansion_b_base = f_of_t_list.at(\"expansion_b\");\n\n  const CoordinateMaps::TimeDependent::CubicScale<1> scale_map(\n      outer_boundary, \"expansion_a\", \"expansion_b\");\n  const std::array<double, 1> point_xi{{19.2}};\n\n  const std::array<double, 1> mapped_point{\n      {point_xi[0] *\n       (expansion_a_base->func(t)[0][0] +\n        (expansion_b_base->func(t)[0][0] - expansion_a_base->func(t)[0][0]) *\n            square(point_xi[0] / outer_boundary))}};\n\n  // this call should fail.\n  scale_map.inverse(mapped_point, t, f_of_t_list);\n}\n\nvoid test_boundaries() {\n  INFO(\"Boundaries\");\n  static constexpr size_t deriv_order = 2;\n\n  const auto run_tests = [](const double x0) {\n    double t = -0.5;\n    const double dt = 0.6;\n    const double final_time = 4.0;\n\n    const std::array<DataVector, deriv_order + 1> init_func_a{\n        {{1.0}, {0.0}, {0.0}}};\n    const std::array<DataVector, deriv_order + 1> init_func_b{\n        {{0.99}, {0.0}, {0.0}}};\n\n    using Polynomial =\n        domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n    using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n    std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n    f_of_t_list[\"expansion_a\"] =\n        std::make_unique<Polynomial>(t, init_func_a, final_time);\n    f_of_t_list[\"expansion_b\"] =\n        std::make_unique<Polynomial>(t, init_func_b, final_time);\n\n    const FoftPtr& expansion_a_base = f_of_t_list.at(\"expansion_a\");\n    const FoftPtr& expansion_b_base = f_of_t_list.at(\"expansion_b\");\n\n    const double outer_boundary = 20.0;\n    const CoordinateMaps::TimeDependent::CubicScale<1> scale_map(\n        outer_boundary, \"expansion_a\", \"expansion_b\");\n    const std::array<double, 1> point_xi{{x0}};\n\n    while (t < final_time) {\n      const double a = expansion_a_base->func_and_deriv(t)[0][0];\n      const double b = expansion_b_base->func_and_deriv(t)[0][0];\n\n      const std::array<double, 1> mapped_point{\n          {point_xi[0] * (a + (b - a) * square(point_xi[0] / outer_boundary))}};\n\n      CHECK_ITERABLE_APPROX(scale_map(point_xi, t, f_of_t_list), mapped_point);\n      REQUIRE(\n          scale_map.inverse(mapped_point, t, f_of_t_list).has_value());\n      CHECK_ITERABLE_APPROX(\n          scale_map.inverse(mapped_point, t, f_of_t_list).value(), point_xi);\n      t += dt;\n    }\n  };\n\n  // test inverse at inner and outer boundary values\n  run_tests(0.0);\n  run_tests(20.0);\n}\n\ntemplate <size_t Dim>\nstd::array<double, Dim> coords_single_root();\n\ntemplate <>\nstd::array<double, 1> coords_single_root() {\n  return {{19.2}};\n}\n\ntemplate <>\nstd::array<double, 2> coords_single_root() {\n  return {{19.2, 0.1}};\n}\n\ntemplate <>\nstd::array<double, 3> coords_single_root() {\n  return {{19.2, 0.1, -1.1}};\n}\n\ntemplate <size_t Dim>\nstd::array<double, Dim> coords_multi_root();\n\ntemplate <>\nstd::array<double, 1> coords_multi_root() {\n  return {{5.0}};\n}\n\ntemplate <>\nstd::array<double, 2> coords_multi_root() {\n  return {{5.0, 0.0}};\n}\n\ntemplate <>\nstd::array<double, 3> coords_multi_root() {\n  return {{5.0, 0.0, 0.0}};\n}\n\ntemplate <size_t Dim>\nvoid test(const bool linear_expansion) {\n  INFO(\"Map\");\n  CAPTURE(Dim);\n  CAPTURE(linear_expansion);\n  static constexpr size_t deriv_order = 2;\n\n  const auto run_tests = [linear_expansion](\n                             const std::array<DataVector, deriv_order + 1>&\n                                 init_func_a,\n                             const std::array<DataVector, deriv_order + 1>&\n                                 init_func_b,\n                             const double outer_boundary,\n                             const std::array<double, Dim> point_xi,\n                             const double final_time) {\n    const double initial_time = -0.5;\n    double t = -0.4;\n    const double dt = 0.6;\n\n    using Polynomial =\n        domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n    using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n    std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n    f_of_t_list[\"expansion_a\"] = std::make_unique<Polynomial>(\n        initial_time, init_func_a, final_time);\n    f_of_t_list[\"expansion_b\"] = std::make_unique<Polynomial>(\n        initial_time, init_func_b, final_time);\n\n    const FoftPtr& expansion_a_base = f_of_t_list.at(\"expansion_a\");\n    const FoftPtr& expansion_b_base = f_of_t_list.at(\"expansion_b\");\n\n    const CoordinateMaps::TimeDependent::CubicScale<Dim> scale_map(\n        outer_boundary, \"expansion_a\",\n        linear_expansion ? \"expansion_a\"s : \"expansion_b\"s);\n\n    const auto check_names = [&linear_expansion](const auto& names) {\n      CHECK(names.size() == (linear_expansion ? 1_st : 2_st));\n      CHECK(names.count(\"expansion_a\") == 1);\n      if (not linear_expansion) {\n        CHECK(names.count(\"expansion_b\") == 1);\n      }\n    };\n    check_names(scale_map.function_of_time_names());\n    // test serialized/deserialized map\n    const auto scale_map_deserialized = serialize_and_deserialize(scale_map);\n    check_names(scale_map_deserialized.function_of_time_names());\n\n    while (t < final_time) {\n      const double a = expansion_a_base->func_and_deriv(t)[0][0];\n      const double b =\n          linear_expansion ? a : expansion_b_base->func_and_deriv(t)[0][0];\n\n      const double radius_squared = square(magnitude(point_xi));\n      const std::array<double, Dim> mapped_point =\n          point_xi * (a + (b - a) * radius_squared / square(outer_boundary));\n\n      CHECK_ITERABLE_APPROX(scale_map(point_xi, t, f_of_t_list), mapped_point);\n      CHECK_ITERABLE_APPROX(\n          scale_map.inverse(mapped_point, t, f_of_t_list).value(), point_xi);\n      CHECK_ITERABLE_APPROX(scale_map_deserialized(point_xi, t, f_of_t_list),\n                            mapped_point);\n      CHECK_ITERABLE_APPROX(\n          scale_map_deserialized.inverse(mapped_point, t, f_of_t_list).value(),\n          point_xi);\n\n      if (not linear_expansion) {\n        // Check that inverse map returns invalid for mapped point outside\n        // the outer boundary and inside the inner boundary.\n        for (size_t i = 0; i < Dim; ++i) {\n          std::array<double, Dim> bad_mapped_point = make_array<Dim>(1.1);\n          gsl::at(bad_mapped_point, i) *= outer_boundary;\n          CHECK(not scale_map.inverse(bad_mapped_point, t, f_of_t_list));\n        }\n      }\n\n      test_jacobian(scale_map, point_xi, t, f_of_t_list);\n      test_inv_jacobian(scale_map, point_xi, t, f_of_t_list);\n      test_frame_velocity(scale_map, point_xi, t, f_of_t_list);\n\n      test_jacobian(scale_map_deserialized, point_xi, t, f_of_t_list);\n      test_inv_jacobian(scale_map_deserialized, point_xi, t, f_of_t_list);\n      test_frame_velocity(scale_map_deserialized, point_xi, t, f_of_t_list);\n\n      // Check inequivalence operator\n      CHECK_FALSE(scale_map != scale_map);\n      CHECK_FALSE(scale_map_deserialized != scale_map_deserialized);\n\n      // Check serialization\n      CHECK(scale_map == scale_map_deserialized);\n      CHECK_FALSE(scale_map != scale_map_deserialized);\n\n      test_coordinate_map_argument_types(scale_map, point_xi, t, f_of_t_list);\n\n      t += dt;\n    }\n  };\n\n  std::array<DataVector, deriv_order + 1> init_func_a1{\n      {{1.0}, {0.0007}, {-0.004}}};\n  std::array<DataVector, deriv_order + 1> init_func_b1{\n      {{1.0}, {-0.001}, {0.003}}};\n  run_tests(init_func_a1, init_func_b1, 20.0, coords_single_root<Dim>(), 4.0);\n\n  // test again with inputs that explicitly result in multiple roots\n  std::array<DataVector, deriv_order + 1> init_func_a2{{{1.0}, {0.0}, {0.0}}};\n  std::array<DataVector, deriv_order + 1> init_func_b2{{{0.99}, {0.0}, {0.0}}};\n  run_tests(init_func_a2, init_func_b2, 6.0, coords_multi_root<Dim>(), 0.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.TimeDependent.CubicScale\",\n                  \"[Domain][Unit]\") {\n  for (const auto linear_expansion : {false, true}) {\n    test<1>(linear_expansion);\n    test<2>(linear_expansion);\n    test<3>(linear_expansion);\n  }\n  test_boundaries();\n  CHECK(not CoordinateMaps::TimeDependent::CubicScale<1>{}.is_identity());\n\n  CHECK_THROWS_WITH(\n      cubic_scale_non_invertible(0.96, 0.58, 20.0),\n      Catch::Matchers::ContainsSubstring(\n          \"The map is invertible only if expansion_b >= expansion_a*2/3\"));\n  CHECK_THROWS_WITH(cubic_scale_non_invertible(-0.0001, 1.0, 20.0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"We require expansion_a > 0 for invertibility\"));\n  CHECK_THROWS_WITH(\n      cubic_scale_non_invertible(0.96, 1.0, 0.0),\n      Catch::Matchers::ContainsSubstring(\n          \"For invertability, we require outer_boundary to be positive\"));\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_ProductMaps.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <typename Map1, typename Map2>\nvoid test_product_of_2_maps_time_dep(\n    const CoordinateMaps::TimeDependent::ProductOf2Maps<Map1, Map2>& map2d,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double x_source_a, const double x_source_b, const double x_target_a,\n    const double x_target_b, const double xi, const double x, const double eta,\n    const double y, const double y_source_a, const double y_source_b,\n    const double y_target_a, const double y_target_b,\n    const std::array<double, 2>& expected_frame_velocity) {\n  using AffineMap = CoordinateMaps::Affine;\n  using TranslationMap = CoordinateMaps::TimeDependent::Translation<1>;\n  static_assert(\n      std::is_same_v<Map1, AffineMap> or std::is_same_v<Map1, TranslationMap>,\n      \"Map1 must be either an affine map or a translation map\");\n  static_assert(\n      std::is_same_v<Map2, AffineMap> or std::is_same_v<Map2, TranslationMap>,\n      \"Map2 must be either an affine map or a translation map\");\n\n  const auto& names = map2d.function_of_time_names();\n  if (std::is_same_v<Map1, TranslationMap> and\n      std::is_same_v<Map2, TranslationMap>) {\n    CHECK(names.size() == 2);\n    CHECK(names.count(\"translation_x\") == 1);\n    CHECK(names.count(\"translation_y\") == 1);\n  } else {\n    CHECK(names.size() == 1);\n    CHECK(names.count(\"translation\") == 1);\n  }\n\n  const std::array<double, 2> point_source_a{{x_source_a, y_source_a}};\n  const std::array<double, 2> point_source_b{{x_source_b, y_source_b}};\n  const std::array<double, 2> point_xi{{xi, eta}};\n  const std::array<double, 2> point_target_a{{x_target_a, y_target_a}};\n  const std::array<double, 2> point_target_b{{x_target_b, y_target_b}};\n  const std::array<double, 2> point_x{{x, y}};\n\n  CHECK(map2d(point_source_a, time, functions_of_time) == point_target_a);\n  CHECK(map2d(point_source_b, time, functions_of_time) == point_target_b);\n  CHECK(map2d(point_xi, time, functions_of_time) == point_x);\n\n  CHECK(map2d.inverse(point_target_a, time, functions_of_time).value() ==\n        point_source_a);\n  CHECK(map2d.inverse(point_target_b, time, functions_of_time).value() ==\n        point_source_b);\n  CHECK_ITERABLE_APPROX(map2d.inverse(point_x, time, functions_of_time).value(),\n                        point_xi);\n\n  const double inv_jacobian_00 =\n      (x_source_b - x_source_a) / (x_target_b - x_target_a);\n  const double inv_jacobian_11 =\n      (y_source_b - y_source_a) / (y_target_b - y_target_a);\n  const auto inv_jac_A =\n      map2d.inv_jacobian(point_source_a, time, functions_of_time);\n  const auto inv_jac_B =\n      map2d.inv_jacobian(point_source_b, time, functions_of_time);\n  const auto inv_jac_xi = map2d.inv_jacobian(point_xi, time, functions_of_time);\n\n  const auto check_jac = [](const auto& jac, const auto& expected_jac_00,\n                            const auto& expected_jac_11) {\n    CHECK(get<0, 0>(jac) == expected_jac_00);\n    CHECK(get<0, 1>(jac) == 0.0);\n    CHECK(get<1, 0>(jac) == 0.0);\n    CHECK(get<1, 1>(jac) == expected_jac_11);\n  };\n\n  check_jac(inv_jac_A, inv_jacobian_00, inv_jacobian_11);\n  check_jac(inv_jac_B, inv_jacobian_00, inv_jacobian_11);\n  check_jac(inv_jac_xi, inv_jacobian_00, inv_jacobian_11);\n\n  const double jacobian_00 =\n      (x_target_b - x_target_a) / (x_source_b - x_source_a);\n  const double jacobian_11 =\n      (y_target_b - y_target_a) / (y_source_b - y_source_a);\n  const auto jac_A = map2d.jacobian(point_source_a, time, functions_of_time);\n  const auto jac_B = map2d.jacobian(point_source_b, time, functions_of_time);\n  const auto jac_xi = map2d.jacobian(point_xi, time, functions_of_time);\n\n  check_jac(jac_A, jacobian_00, jacobian_11);\n  check_jac(jac_B, jacobian_00, jacobian_11);\n  check_jac(jac_xi, jacobian_00, jacobian_11);\n\n  CHECK(map2d.frame_velocity(point_source_a, time, functions_of_time) ==\n        expected_frame_velocity);\n  CHECK(map2d.frame_velocity(point_source_b, time, functions_of_time) ==\n        expected_frame_velocity);\n  CHECK(map2d.frame_velocity(point_xi, time, functions_of_time) ==\n        expected_frame_velocity);\n\n  // Check Jacobians for DataVectors\n  const Mesh<2> mesh{8, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto tensor_logical_coords = logical_coordinates(mesh);\n  const std::array<DataVector, 2> logical_coords{\n      {get<0>(tensor_logical_coords), get<1>(tensor_logical_coords)}};\n  const auto volume_inv_jac =\n      map2d.inv_jacobian(logical_coords, time, functions_of_time);\n  const auto volume_jac =\n      map2d.jacobian(logical_coords, time, functions_of_time);\n  for (size_t i = 0; i < 2; ++i) {\n    for (size_t j = 0; j < 2; ++j) {\n      if (i == j) {\n        CHECK(volume_inv_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(),\n                         i == 0 ? inv_jacobian_00 : inv_jacobian_11));\n        CHECK(volume_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(),\n                         i == 0 ? jacobian_00 : jacobian_11));\n      } else {\n        CHECK(volume_inv_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(), 0.0));\n        CHECK(volume_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(), 0.0));\n      }\n    }\n  }\n\n  // Check frame velocity with DataVectors\n  std::array<DataVector, 2> expected_frame_velocity_datavector{\n      {DataVector(logical_coords[0].size(), expected_frame_velocity[0]),\n       DataVector(logical_coords[1].size(), expected_frame_velocity[1])}};\n\n  CHECK(map2d.frame_velocity(logical_coords, time, functions_of_time) ==\n        expected_frame_velocity_datavector);\n\n  CHECK(map2d == map2d);\n  CHECK_FALSE(map2d != map2d);\n}\n\nvoid test_product_of_2_maps_time_dep() {\n  INFO(\"Product of two maps with time dependence\");\n  constexpr size_t deriv_order = 3;\n  using affine_map = CoordinateMaps::Affine;\n  using translation_map = CoordinateMaps::TimeDependent::Translation<1>;\n\n  const std::string f_of_t_name{\"translation\"};\n  const double time = 2.0;\n  const std::array<DataVector, deriv_order + 1> init_func{\n      {{1.0}, {-2.0}, {2.0}, {0.0}}};\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n  std::unordered_map<std::string, FoftPtr> functions_of_time{};\n  functions_of_time[f_of_t_name] =\n      std::make_unique<Polynomial>(0.0, init_func, 4.0);\n\n  {\n    // Test one time-dependent and one time-independent map case.\n    const double x_source_a = -1.0;\n    const double x_source_b = 1.0;\n    const double x_target_a = -2.0;\n    const double x_target_b = 2.0;\n\n    const double xi = 0.5 * (x_source_a + x_source_b);\n    const double x =\n        x_target_b * (xi - x_source_a) / (x_source_b - x_source_a) +\n        x_target_a * (x_source_b - xi) / (x_source_b - x_source_a);\n    const double eta = 0.7;\n    const double y = eta + functions_of_time.at(f_of_t_name)->func(time)[0][0];\n\n    const double y_source_a = -1.0;\n    const double y_source_b = 1.0;\n    const double y_target_a =\n        -1.0 + functions_of_time.at(f_of_t_name)->func(time)[0][0];\n    const double y_target_b =\n        1.0 + functions_of_time.at(f_of_t_name)->func(time)[0][0];\n\n    affine_map affine_map_x(x_source_a, x_source_b, x_target_a, x_target_b);\n    translation_map translation_map_y{f_of_t_name};\n\n    using Map2d =\n        CoordinateMaps::TimeDependent::ProductOf2Maps<affine_map,\n                                                      translation_map>;\n    Map2d map2d(affine_map_x, translation_map_y);\n\n    test_product_of_2_maps_time_dep(\n        map2d, time, functions_of_time, x_source_a, x_source_b, x_target_a,\n        x_target_b, xi, x, eta, y, y_source_a, y_source_b, y_target_a,\n        y_target_b,\n        {{0.0, functions_of_time.at(f_of_t_name)->func_and_deriv(time)[1][0]}});\n    test_product_of_2_maps_time_dep(\n        serialize_and_deserialize(map2d), time, functions_of_time, x_source_a,\n        x_source_b, x_target_a, x_target_b, xi, x, eta, y, y_source_a,\n        y_source_b, y_target_a, y_target_b,\n        {{0.0, functions_of_time.at(f_of_t_name)->func_and_deriv(time)[1][0]}});\n\n    using Map2d_b =\n        CoordinateMaps::TimeDependent::ProductOf2Maps<translation_map,\n                                                      affine_map>;\n    Map2d_b map2d_b(translation_map_y, affine_map_x);\n\n    test_product_of_2_maps_time_dep(\n        map2d_b, time, functions_of_time, y_source_a, y_source_b, y_target_a,\n        y_target_b, eta, y, xi, x, x_source_a, x_source_b, x_target_a,\n        x_target_b,\n        {{functions_of_time.at(f_of_t_name)->func_and_deriv(time)[1][0], 0.0}});\n    test_product_of_2_maps_time_dep(\n        serialize_and_deserialize(map2d_b), time, functions_of_time, y_source_a,\n        y_source_b, y_target_a, y_target_b, eta, y, xi, x, x_source_a,\n        x_source_b, x_target_a, x_target_b,\n        {{functions_of_time.at(f_of_t_name)->func_and_deriv(time)[1][0], 0.0}});\n  }\n\n  {\n    using Map2d =\n        CoordinateMaps::TimeDependent::ProductOf2Maps<translation_map,\n                                                      translation_map>;\n\n    const std::string f_of_t_name_x{\"translation_x\"};\n    const std::string f_of_t_name_y{\"translation_y\"};\n\n    translation_map translation_map_x{f_of_t_name_x};\n    translation_map translation_map_y{f_of_t_name_y};\n    Map2d map2d(translation_map_x, translation_map_y);\n\n    functions_of_time[f_of_t_name_x] = std::make_unique<Polynomial>(\n        0.0,\n        std::array<DataVector, deriv_order + 1>{{{1.0}, {-3.0}, {2.0}, {0.0}}},\n        4.0);\n    functions_of_time[f_of_t_name_y] = std::make_unique<Polynomial>(\n        0.0,\n        std::array<DataVector, deriv_order + 1>{{{1.0}, {2.4}, {2.0}, {0.0}}},\n        4.0);\n\n    const double x_source_a = -1.0;\n    const double x_source_b = 1.0;\n    const double x_target_a =\n        -1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n    const double x_target_b =\n        1.0 + +functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n\n    const double xi = 0.5;\n    const double x = xi + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n    const double eta = 0.7;\n    const double y =\n        eta + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n\n    const double y_source_a = -1.0;\n    const double y_source_b = 1.0;\n    const double y_target_a =\n        -1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n    const double y_target_b =\n        1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n\n    test_product_of_2_maps_time_dep(\n        map2d, time, functions_of_time, x_source_a, x_source_b, x_target_a,\n        x_target_b, xi, x, eta, y, y_source_a, y_source_b, y_target_a,\n        y_target_b,\n        {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n          functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0]}});\n    test_product_of_2_maps_time_dep(\n        serialize_and_deserialize(map2d), time, functions_of_time, x_source_a,\n        x_source_b, x_target_a, x_target_b, xi, x, eta, y, y_source_a,\n        y_source_b, y_target_a, y_target_b,\n        {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n          functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0]}});\n  }\n}\n\ntemplate <typename Map1, typename Map2, typename Map3>\nvoid test_product_of_3_maps_time_dep(\n    const CoordinateMaps::TimeDependent::ProductOf3Maps<Map1, Map2, Map3>&\n        map3d,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const double x_source_a, const double x_source_b, const double x_target_a,\n    const double x_target_b, const double xi, const double x, const double eta,\n    const double y, const double zeta, const double z, const double y_source_a,\n    const double y_source_b, const double y_target_a, const double y_target_b,\n    const double z_source_a, const double z_source_b, const double z_target_a,\n    const double z_target_b,\n    const std::array<double, 3>& expected_frame_velocity) {\n  using AffineMap = CoordinateMaps::Affine;\n  using TranslationMap = CoordinateMaps::TimeDependent::Translation<1>;\n  static_assert(\n      std::is_same_v<Map1, AffineMap> or std::is_same_v<Map1, TranslationMap>,\n      \"Map1 must be either an affine map or a translation map\");\n  static_assert(\n      std::is_same_v<Map2, AffineMap> or std::is_same_v<Map2, TranslationMap>,\n      \"Map2 must be either an affine map or a translation map\");\n  static_assert(\n      std::is_same_v<Map3, AffineMap> or std::is_same_v<Map3, TranslationMap>,\n      \"Map3 must be either an affine map or a translation map\");\n\n  const auto& names = map3d.function_of_time_names();\n  const size_t expected_size =\n      (std::is_same_v<Map1, TranslationMap> ? 1_st : 0_st) +\n      (std::is_same_v<Map2, TranslationMap> ? 1_st : 0_st) +\n      (std::is_same_v<Map3, TranslationMap> ? 1_st : 0_st);\n  CHECK(names.size() == expected_size);\n\n  if (std::is_same_v<Map1, TranslationMap>) {\n    CHECK(names.count(\"translation_x\") == 1);\n  }\n  if (std::is_same_v<Map2, TranslationMap>) {\n    CHECK(names.count(\"translation_y\") == 1);\n  }\n  if (std::is_same_v<Map3, TranslationMap>) {\n    CHECK(names.count(\"translation_z\") == 1);\n  }\n\n  const std::array<double, 3> point_source_a{\n      {x_source_a, y_source_a, z_source_a}};\n  const std::array<double, 3> point_source_b{\n      {x_source_b, y_source_b, z_source_b}};\n  const std::array<double, 3> point_xi{{xi, eta, zeta}};\n  const std::array<double, 3> point_target_a{\n      {x_target_a, y_target_a, z_target_a}};\n  const std::array<double, 3> point_target_b{\n      {x_target_b, y_target_b, z_target_b}};\n  const std::array<double, 3> point_x{{x, y, z}};\n\n  CHECK(map3d(point_source_a, time, functions_of_time) == point_target_a);\n  CHECK(map3d(point_source_b, time, functions_of_time) == point_target_b);\n  CHECK(map3d(point_xi, time, functions_of_time) == point_x);\n\n  CHECK(map3d.inverse(point_target_a, time, functions_of_time).value() ==\n        point_source_a);\n  CHECK(map3d.inverse(point_target_b, time, functions_of_time).value() ==\n        point_source_b);\n  CHECK_ITERABLE_APPROX(map3d.inverse(point_x, time, functions_of_time).value(),\n                        point_xi);\n\n  const double inv_jacobian_00 =\n      (x_source_b - x_source_a) / (x_target_b - x_target_a);\n  const double inv_jacobian_11 =\n      (y_source_b - y_source_a) / (y_target_b - y_target_a);\n  const double inv_jacobian_22 =\n      (z_source_b - z_source_a) / (z_target_b - z_target_a);\n  const auto inv_jac_A =\n      map3d.inv_jacobian(point_source_a, time, functions_of_time);\n  const auto inv_jac_B =\n      map3d.inv_jacobian(point_source_b, time, functions_of_time);\n  const auto inv_jac_xi = map3d.inv_jacobian(point_xi, time, functions_of_time);\n\n  const auto check_jac = [](const auto& jac, const auto& expected_jac_00,\n                            const auto& expected_jac_11,\n                            const auto& expected_jac_22) {\n    CHECK(get<0, 0>(jac) == expected_jac_00);\n    CHECK(get<1, 1>(jac) == expected_jac_11);\n    CHECK(get<2, 2>(jac) == expected_jac_22);\n    CHECK(get<0, 1>(jac) == 0.0);\n    CHECK(get<0, 2>(jac) == 0.0);\n    CHECK(get<1, 0>(jac) == 0.0);\n    CHECK(get<2, 0>(jac) == 0.0);\n    CHECK(get<1, 2>(jac) == 0.0);\n    CHECK(get<2, 1>(jac) == 0.0);\n  };\n\n  check_jac(inv_jac_A, inv_jacobian_00, inv_jacobian_11, inv_jacobian_22);\n  check_jac(inv_jac_B, inv_jacobian_00, inv_jacobian_11, inv_jacobian_22);\n  check_jac(inv_jac_xi, inv_jacobian_00, inv_jacobian_11, inv_jacobian_22);\n\n  const double jacobian_00 =\n      (x_target_b - x_target_a) / (x_source_b - x_source_a);\n  const double jacobian_11 =\n      (y_target_b - y_target_a) / (y_source_b - y_source_a);\n  const double jacobian_22 =\n      (z_target_b - z_target_a) / (z_source_b - z_source_a);\n  const auto jac_A = map3d.jacobian(point_source_a, time, functions_of_time);\n  const auto jac_B = map3d.jacobian(point_source_b, time, functions_of_time);\n  const auto jac_xi = map3d.jacobian(point_xi, time, functions_of_time);\n\n  check_jac(jac_A, jacobian_00, jacobian_11, jacobian_22);\n  check_jac(jac_B, jacobian_00, jacobian_11, jacobian_22);\n  check_jac(jac_xi, jacobian_00, jacobian_11, jacobian_22);\n\n  CHECK(map3d == map3d);\n  CHECK_FALSE(map3d != map3d);\n\n  CHECK(map3d.frame_velocity(point_source_a, time, functions_of_time) ==\n        expected_frame_velocity);\n  CHECK(map3d.frame_velocity(point_source_b, time, functions_of_time) ==\n        expected_frame_velocity);\n  CHECK(map3d.frame_velocity(point_xi, time, functions_of_time) ==\n        expected_frame_velocity);\n\n  // Check Jacobians for DataVectors\n  const Mesh<3> mesh{8, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto tensor_logical_coords = logical_coordinates(mesh);\n  const std::array<DataVector, 3> logical_coords{\n      {get<0>(tensor_logical_coords), get<1>(tensor_logical_coords),\n       get<2>(tensor_logical_coords)}};\n  const auto volume_inv_jac =\n      map3d.inv_jacobian(logical_coords, time, functions_of_time);\n  const auto volume_jac =\n      map3d.jacobian(logical_coords, time, functions_of_time);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      if (i == j) {\n        CHECK(volume_inv_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(),\n                         i == 0 ? inv_jacobian_00\n                                : i == 1 ? inv_jacobian_11 : inv_jacobian_22));\n        CHECK(volume_jac.get(i, j) ==\n              DataVector(\n                  logical_coords[0].size(),\n                  i == 0 ? jacobian_00 : i == 1 ? jacobian_11 : jacobian_22));\n      } else {\n        CHECK(volume_inv_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(), 0.0));\n        CHECK(volume_jac.get(i, j) ==\n              DataVector(logical_coords[0].size(), 0.0));\n      }\n    }\n  }\n\n  // Check frame velocity with DataVectors\n  std::array<DataVector, 3> expected_frame_velocity_datavector{\n      {DataVector(logical_coords[0].size(), expected_frame_velocity[0]),\n       DataVector(logical_coords[1].size(), expected_frame_velocity[1]),\n       DataVector(logical_coords[2].size(), expected_frame_velocity[2])}};\n\n  CHECK(map3d.frame_velocity(logical_coords, time, functions_of_time) ==\n        expected_frame_velocity_datavector);\n\n  CHECK(map3d == map3d);\n  CHECK_FALSE(map3d != map3d);\n}\n\nvoid test_product_of_3_maps() {\n  INFO(\"Product of 3 maps\");\n  constexpr size_t deriv_order = 3;\n  using affine_map = CoordinateMaps::Affine;\n  using translation_map = CoordinateMaps::TimeDependent::Translation<1>;\n\n  const std::string f_of_t_name_x{\"translation_x\"};\n  const std::string f_of_t_name_y{\"translation_y\"};\n  const std::string f_of_t_name_z{\"translation_z\"};\n\n  const double time = 2.0;\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n  std::unordered_map<std::string, FoftPtr> functions_of_time{};\n  functions_of_time[f_of_t_name_x] = std::make_unique<Polynomial>(\n      0.0,\n      std::array<DataVector, deriv_order + 1>{{{1.0}, {-3.0}, {1.3}, {0.0}}},\n      4.0);\n  functions_of_time[f_of_t_name_y] = std::make_unique<Polynomial>(\n      0.0,\n      std::array<DataVector, deriv_order + 1>{{{1.0}, {2.4}, {2.0}, {0.0}}},\n      4.0);\n  functions_of_time[f_of_t_name_z] = std::make_unique<Polynomial>(\n      0.0,\n      std::array<DataVector, deriv_order + 1>{{{1.0}, {0.4}, {4.0}, {0.0}}},\n      4.0);\n\n  {\n    // Test one time-dependent and two time-independent map case.\n    const double x_source_a = -1.0;\n    const double x_source_b = 1.0;\n    const double x_target_a = -2.0;\n    const double x_target_b = 2.0;\n    affine_map affine_map_x(x_source_a, x_source_b, x_target_a, x_target_b);\n\n    const double y_source_a = -2.0;\n    const double y_source_b = 3.0;\n    const double y_target_a = 5.0;\n    const double y_target_b = -2.0;\n    affine_map affine_map_y(y_source_a, y_source_b, y_target_a, y_target_b);\n\n    const double xi = 0.5 * (x_source_a + x_source_b);\n    const double x =\n        x_target_b * (xi - x_source_a) / (x_source_b - x_source_a) +\n        x_target_a * (x_source_b - xi) / (x_source_b - x_source_a);\n    const double eta = 0.5 * (y_source_a + y_source_b);\n    const double y =\n        y_target_b * (eta - y_source_a) / (y_source_b - y_source_a) +\n        y_target_a * (y_source_b - eta) / (y_source_b - y_source_a);\n\n    {\n      INFO(\"Check with z-direction map time-dependent\");\n      const double z_source_a = -1.0;\n      const double z_source_b = 1.0;\n      const double z_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n      const double z_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n\n      const double zeta = 0.7;\n      const double z =\n          zeta + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n\n      translation_map translation_map_z{f_of_t_name_z};\n      using Map3d =\n          CoordinateMaps::TimeDependent::ProductOf3Maps<affine_map, affine_map,\n                                                        translation_map>;\n      Map3d map3d{affine_map_x, affine_map_y, translation_map_z};\n\n      test_product_of_3_maps_time_dep(\n          map3d, time, functions_of_time, x_source_a, x_source_b, x_target_a,\n          x_target_b, xi, x, eta, y, zeta, z, y_source_a, y_source_b,\n          y_target_a, y_target_b, z_source_a, z_source_b, z_target_a,\n          z_target_b,\n          {{0.0, 0.0,\n            functions_of_time.at(f_of_t_name_z)->func_and_deriv(time)[1][0]}});\n      test_product_of_3_maps_time_dep(\n          serialize_and_deserialize(map3d), time, functions_of_time, x_source_a,\n          x_source_b, x_target_a, x_target_b, xi, x, eta, y, zeta, z,\n          y_source_a, y_source_b, y_target_a, y_target_b, z_source_a,\n          z_source_b, z_target_a, z_target_b,\n          {{0.0, 0.0,\n            functions_of_time.at(f_of_t_name_z)->func_and_deriv(time)[1][0]}});\n    }\n    {\n      INFO(\"Check with y-direction map time-dependent\");\n      const double z_source_a = -1.0;\n      const double z_source_b = 1.0;\n      const double z_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n      const double z_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n\n      const double zeta = 0.7;\n      const double z =\n          zeta + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n\n      translation_map translation_map_z{f_of_t_name_y};\n      using Map3d = CoordinateMaps::TimeDependent::ProductOf3Maps<\n          affine_map, translation_map, affine_map>;\n      Map3d map3d{affine_map_x, translation_map_z, affine_map_y};\n\n      test_product_of_3_maps_time_dep(\n          map3d, time, functions_of_time, x_source_a, x_source_b, x_target_a,\n          x_target_b, xi, x, zeta, z, eta, y, z_source_a, z_source_b,\n          z_target_a, z_target_b, y_source_a, y_source_b, y_target_a,\n          y_target_b,\n          {{0.0,\n            functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0],\n            0.0}});\n      test_product_of_3_maps_time_dep(\n          serialize_and_deserialize(map3d), time, functions_of_time, x_source_a,\n          x_source_b, x_target_a, x_target_b, xi, x, zeta, z, eta, y,\n          z_source_a, z_source_b, z_target_a, z_target_b, y_source_a,\n          y_source_b, y_target_a, y_target_b,\n          {{0.0,\n            functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0],\n            0.0}});\n    }\n    {\n      INFO(\"Check with x-direction map time-dependent\");\n      const double z_source_a = -1.0;\n      const double z_source_b = 1.0;\n      const double z_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n      const double z_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n\n      const double zeta = 0.7;\n      const double z =\n          zeta + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n\n      translation_map translation_map_z{f_of_t_name_x};\n      using Map3d =\n          CoordinateMaps::TimeDependent::ProductOf3Maps<translation_map,\n                                                        affine_map, affine_map>;\n      Map3d map3d{translation_map_z, affine_map_y, affine_map_x};\n\n      test_product_of_3_maps_time_dep(\n          map3d, time, functions_of_time, z_source_a, z_source_b, z_target_a,\n          z_target_b, zeta, z, eta, y, xi, x, y_source_a, y_source_b,\n          y_target_a, y_target_b, x_source_a, x_source_b, x_target_a,\n          x_target_b,\n          {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n            0.0, 0.0}});\n      test_product_of_3_maps_time_dep(\n          serialize_and_deserialize(map3d), time, functions_of_time, z_source_a,\n          z_source_b, z_target_a, z_target_b, zeta, z, eta, y, xi, x,\n          y_source_a, y_source_b, y_target_a, y_target_b, x_source_a,\n          x_source_b, x_target_a, x_target_b,\n          {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n            0.0, 0.0}});\n    }\n  }\n\n  {\n    // Test two time-dependent and one time-independent map case.\n    const double x_source_a = -1.0;\n    const double x_source_b = 1.0;\n    const double x_target_a = -2.0;\n    const double x_target_b = 2.0;\n    affine_map affine_map_x(x_source_a, x_source_b, x_target_a, x_target_b);\n\n    const double xi = 0.5 * (x_source_a + x_source_b);\n    const double x =\n        x_target_b * (xi - x_source_a) / (x_source_b - x_source_a) +\n        x_target_a * (x_source_b - xi) / (x_source_b - x_source_a);\n    {\n      INFO(\"Check with x-direction map time-independent\");\n      const double y_source_a = -1.0;\n      const double y_source_b = 1.0;\n      const double y_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n      const double y_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n      const double z_source_a = -1.0;\n      const double z_source_b = 1.0;\n      const double z_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n      const double z_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n\n      const double eta = 0.7;\n      const double y =\n          eta + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n      const double zeta = 0.7;\n      const double z =\n          zeta + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n\n      translation_map translation_map_y{f_of_t_name_y};\n      translation_map translation_map_z{f_of_t_name_z};\n      using Map3d = CoordinateMaps::TimeDependent::ProductOf3Maps<\n          affine_map, translation_map, translation_map>;\n      Map3d map3d{affine_map_x, translation_map_y, translation_map_z};\n\n      test_product_of_3_maps_time_dep(\n          map3d, time, functions_of_time, x_source_a, x_source_b, x_target_a,\n          x_target_b, xi, x, eta, y, zeta, z, y_source_a, y_source_b,\n          y_target_a, y_target_b, z_source_a, z_source_b, z_target_a,\n          z_target_b,\n          {{0.0,\n            functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0],\n            functions_of_time.at(f_of_t_name_z)->func_and_deriv(time)[1][0]}});\n      test_product_of_3_maps_time_dep(\n          serialize_and_deserialize(map3d), time, functions_of_time, x_source_a,\n          x_source_b, x_target_a, x_target_b, xi, x, eta, y, zeta, z,\n          y_source_a, y_source_b, y_target_a, y_target_b, z_source_a,\n          z_source_b, z_target_a, z_target_b,\n          {{0.0,\n            functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0],\n            functions_of_time.at(f_of_t_name_z)->func_and_deriv(time)[1][0]}});\n    }\n    {\n      INFO(\"Check with y-direction map time-independent\");\n      const double y_source_a = -1.0;\n      const double y_source_b = 1.0;\n      const double y_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n      const double y_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n      const double z_source_a = -1.0;\n      const double z_source_b = 1.0;\n      const double z_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n      const double z_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n\n      const double eta = 0.7;\n      const double y =\n          eta + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n      const double zeta = 0.7;\n      const double z =\n          zeta + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n\n      translation_map translation_map_y{f_of_t_name_x};\n      translation_map translation_map_z{f_of_t_name_z};\n      using Map3d = CoordinateMaps::TimeDependent::ProductOf3Maps<\n          translation_map, affine_map, translation_map>;\n      Map3d map3d{translation_map_y, affine_map_x, translation_map_z};\n\n      test_product_of_3_maps_time_dep(\n          map3d, time, functions_of_time, y_source_a, y_source_b, y_target_a,\n          y_target_b, eta, y, xi, x, zeta, z, x_source_a, x_source_b,\n          x_target_a, x_target_b, z_source_a, z_source_b, z_target_a,\n          z_target_b,\n          {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n            0.0,\n            functions_of_time.at(f_of_t_name_z)->func_and_deriv(time)[1][0]}});\n      test_product_of_3_maps_time_dep(\n          serialize_and_deserialize(map3d), time, functions_of_time, y_source_a,\n          y_source_b, y_target_a, y_target_b, eta, y, xi, x, zeta, z,\n          x_source_a, x_source_b, x_target_a, x_target_b, z_source_a,\n          z_source_b, z_target_a, z_target_b,\n          {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n            0.0,\n            functions_of_time.at(f_of_t_name_z)->func_and_deriv(time)[1][0]}});\n    }\n    {\n      INFO(\"Check with z-direction map time-independent\");\n      const double y_source_a = -1.0;\n      const double y_source_b = 1.0;\n      const double y_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n      const double y_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n      const double z_source_a = -1.0;\n      const double z_source_b = 1.0;\n      const double z_target_a =\n          -1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n      const double z_target_b =\n          1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n\n      const double eta = 0.7;\n      const double y =\n          eta + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n      const double zeta = 0.7;\n      const double z =\n          zeta + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n\n      translation_map translation_map_y{f_of_t_name_y};\n      translation_map translation_map_z{f_of_t_name_x};\n      using Map3d = CoordinateMaps::TimeDependent::ProductOf3Maps<\n          translation_map, translation_map, affine_map>;\n      Map3d map3d{translation_map_z, translation_map_y, affine_map_x};\n\n      test_product_of_3_maps_time_dep(\n          map3d, time, functions_of_time, z_source_a, z_source_b, z_target_a,\n          z_target_b, zeta, z, eta, y, xi, x, y_source_a, y_source_b,\n          y_target_a, y_target_b, x_source_a, x_source_b, x_target_a,\n          x_target_b,\n          {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n            functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0],\n            0.0}});\n      test_product_of_3_maps_time_dep(\n          serialize_and_deserialize(map3d), time, functions_of_time, z_source_a,\n          z_source_b, z_target_a, z_target_b, zeta, z, eta, y, xi, x,\n          y_source_a, y_source_b, y_target_a, y_target_b, x_source_a,\n          x_source_b, x_target_a, x_target_b,\n          {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n            functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0],\n            0.0}});\n    }\n  }\n  {\n    INFO(\"Check only time-dependent maps\");\n    const double x_source_a = -1.0;\n    const double x_source_b = 1.0;\n    const double x_target_a =\n        -1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n    const double x_target_b =\n        1.0 + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n    const double y_source_a = -1.0;\n    const double y_source_b = 1.0;\n    const double y_target_a =\n        -1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n    const double y_target_b =\n        1.0 + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n    const double z_source_a = -1.0;\n    const double z_source_b = 1.0;\n    const double z_target_a =\n        -1.0 + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n    const double z_target_b =\n        1.0 + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n\n    const double xi = 0.3;\n    const double x = xi + functions_of_time.at(f_of_t_name_x)->func(time)[0][0];\n    const double eta = 0.7;\n    const double y =\n        eta + functions_of_time.at(f_of_t_name_y)->func(time)[0][0];\n    const double zeta = 0.7;\n    const double z =\n        zeta + functions_of_time.at(f_of_t_name_z)->func(time)[0][0];\n\n    translation_map translation_map_x{f_of_t_name_x};\n    translation_map translation_map_y{f_of_t_name_y};\n    translation_map translation_map_z{f_of_t_name_z};\n    using Map3d = CoordinateMaps::TimeDependent::ProductOf3Maps<\n        translation_map, translation_map, translation_map>;\n    Map3d map3d{translation_map_x, translation_map_y, translation_map_z};\n\n    test_product_of_3_maps_time_dep(\n        map3d, time, functions_of_time, x_source_a, x_source_b, x_target_a,\n        x_target_b, xi, x, eta, y, zeta, z, y_source_a, y_source_b, y_target_a,\n        y_target_b, z_source_a, z_source_b, z_target_a, z_target_b,\n        {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n          functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0],\n          functions_of_time.at(f_of_t_name_z)->func_and_deriv(time)[1][0]}});\n    test_product_of_3_maps_time_dep(\n        serialize_and_deserialize(map3d), time, functions_of_time, x_source_a,\n        x_source_b, x_target_a, x_target_b, xi, x, eta, y, zeta, z, y_source_a,\n        y_source_b, y_target_a, y_target_b, z_source_a, z_source_b, z_target_a,\n        z_target_b,\n        {{functions_of_time.at(f_of_t_name_x)->func_and_deriv(time)[1][0],\n          functions_of_time.at(f_of_t_name_y)->func_and_deriv(time)[1][0],\n          functions_of_time.at(f_of_t_name_z)->func_and_deriv(time)[1][0]}});\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.TimeDependent.ProductMaps\",\n                  \"[Domain][Unit]\") {\n  test_product_of_2_maps_time_dep();\n  test_product_of_3_maps();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_RotScaleTrans.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/RotScaleTrans.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\ntemplate <size_t Dim>\nvoid test_RotScaleTrans() {\n  MAKE_GENERATOR(gen);\n  // define vars for FunctionOfTime::PiecewisePolynomial f(t) = t**2.\n  double initial_t = -1.0;\n  double t = -0.9;\n  const double dt = 0.3;\n  const double final_time = 2.0;\n  constexpr size_t deriv_order = 3;\n  const double inner_radius = 1.0;\n  const double outer_radius = 50.0;\n  const double angle = 1.0;\n  const double omega = -2.0;\n  const double dtomega = 2.0;\n  const double d2tomega = 0.0;\n\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n  using QuatFoT =\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>;\n  using BlockRegion =\n      typename domain::CoordinateMaps::TimeDependent::RotScaleTrans<\n          Dim>::BlockRegion;\n  std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n  std::array<DataVector, deriv_order + 1> init_func{};\n  const std::array<DataVector, deriv_order + 1> init_func_trans{\n      {{Dim, 1.0}, {Dim, -2.0}, {Dim, 2.0}, {Dim, 0.0}}};\n  const std::array<DataVector, deriv_order + 1> init_func_a{\n      {{0.58}, {0.0}, {0.0}, {0.0}}};\n  const std::array<DataVector, deriv_order + 1> init_func_b{\n      {{0.96}, {0.0}, {0.0}, {0.0}}};\n  const std::string rot_f_of_t_name{\"rotation_angle\"};\n  // custom_approx will be redifined multiple times below.\n  Approx custom_approx = Approx::custom().epsilon(1.0).scale(1.0);\n\n  if constexpr (Dim == 2) {\n    init_func = {{{angle}, {omega}, {dtomega}, {d2tomega}}};\n    f_of_t_list[rot_f_of_t_name] =\n        std::make_unique<Polynomial>(initial_t, init_func, final_time + dt);\n    custom_approx = Approx::custom().epsilon(5.0e-13).scale(1.0);\n  } else {\n    // Axis of rotation nhat = (1.0, -1.0, 1.0) / sqrt(3.0)\n    DataVector axis{{1.0, -1.0, 1.0}};\n    axis /= sqrt(3.0);\n    init_func = {axis * angle, axis * omega, axis * dtomega, axis * d2tomega};\n    // initial quaternion is (cos(angle/2), nhat*sin(angle/2))\n    const std::array<DataVector, 1> init_quat{\n        DataVector{{cos(angle / 2.0), axis[0] * sin(angle / 2.0),\n                    axis[1] * sin(angle / 2.0), axis[2] * sin(angle / 2.0)}}};\n    f_of_t_list[rot_f_of_t_name] = std::make_unique<QuatFoT>(\n        initial_t, init_quat, init_func, final_time + dt);\n    custom_approx = Approx::custom().epsilon(5.0e-11).scale(1.0);\n  }\n  f_of_t_list[\"expansion_a\"] =\n      std::make_unique<Polynomial>(initial_t, init_func_a, final_time);\n  f_of_t_list[\"expansion_b\"] =\n      std::make_unique<Polynomial>(initial_t, init_func_b, final_time);\n  f_of_t_list[\"translation\"] =\n      std::make_unique<Polynomial>(initial_t, init_func_trans, final_time + dt);\n\n  const FoftPtr& scale_a_of_t = f_of_t_list.at(\"expansion_a\");\n  const FoftPtr& scale_b_of_t = f_of_t_list.at(\"expansion_b\");\n  const std::pair<std::string, std::string> scale_pair{\"expansion_a\",\n                                                       \"expansion_b\"};\n\n  const auto map_serialize_and_deserialize =\n      [&](const auto scaling, const auto rotation, const auto translation,\n          const auto region) {\n        domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim> map{\n            scaling, rotation, translation, inner_radius, outer_radius, region};\n        return serialize_and_deserialize(map);\n      };\n  // Rotation, Scaling, Translation Inner\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_scale_trans_map_inner = map_serialize_and_deserialize(\n          scale_pair, \"rotation_angle\", \"translation\", BlockRegion::Inner);\n\n  // Rotation, Scaling, Translation Transition\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_scale_trans_map_transition = map_serialize_and_deserialize(\n          scale_pair, \"rotation_angle\", \"translation\", BlockRegion::Transition);\n\n  // Rotation, Scaling, Translation Outer\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_scale_trans_map_outer = map_serialize_and_deserialize(\n          scale_pair, \"rotation_angle\", \"translation\", BlockRegion::Outer);\n\n  // Rotation, Scaling Inner\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_scale_map_inner = map_serialize_and_deserialize(\n          scale_pair, \"rotation_angle\", std::nullopt, BlockRegion::Inner);\n\n  // Rotation, Scaling Transition\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_scale_map_transition = map_serialize_and_deserialize(\n          scale_pair, \"rotation_angle\", std::nullopt, BlockRegion::Transition);\n\n  // Rotation, Scaling Outer\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_scale_map_outer = map_serialize_and_deserialize(\n          scale_pair, \"rotation_angle\", std::nullopt, BlockRegion::Outer);\n\n  // Rotation, Translation Inner\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_trans_map_inner = map_serialize_and_deserialize(\n          std::nullopt, \"rotation_angle\", \"translation\", BlockRegion::Inner);\n\n  // Rotation, Translation Transition\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_trans_map_transition =\n          map_serialize_and_deserialize(std::nullopt, \"rotation_angle\",\n                                        \"translation\", BlockRegion::Transition);\n\n  // Rotation, Translation Outer\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      rot_trans_map_outer = map_serialize_and_deserialize(\n          std::nullopt, \"rotation_angle\", \"translation\", BlockRegion::Outer);\n\n  // Scaling, Translation Inner\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      scale_trans_map_inner = map_serialize_and_deserialize(\n          scale_pair, std::nullopt, \"translation\", BlockRegion::Inner);\n\n  // Scaling, Translation Transition\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      scale_trans_map_transition = map_serialize_and_deserialize(\n          scale_pair, std::nullopt, \"translation\", BlockRegion::Transition);\n\n  // Scaling, Translation Outer\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      scale_trans_map_outer = map_serialize_and_deserialize(\n          scale_pair, std::nullopt, \"translation\", BlockRegion::Outer);\n\n  // Rotation (doesn't matter which region you use)\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim> rot_map =\n      map_serialize_and_deserialize(std::nullopt, \"rotation_angle\",\n                                    std::nullopt, BlockRegion::Inner);\n\n  // Scaling Inner\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim> scale_map_inner =\n      map_serialize_and_deserialize(scale_pair, std::nullopt, std::nullopt,\n                                    BlockRegion::Inner);\n\n  // Scaling Transition\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      scale_map_transition = map_serialize_and_deserialize(\n          scale_pair, std::nullopt, std::nullopt, BlockRegion::Transition);\n\n  // Scaling Outer\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim> scale_map_outer =\n      map_serialize_and_deserialize(scale_pair, std::nullopt, std::nullopt,\n                                    BlockRegion::Outer);\n\n  // Translation Inner\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim> trans_map_inner =\n      map_serialize_and_deserialize(std::nullopt, std::nullopt, \"translation\",\n                                    BlockRegion::Inner);\n\n  // Translation Transition\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim>\n      trans_map_transition = map_serialize_and_deserialize(\n          std::nullopt, std::nullopt, \"translation\", BlockRegion::Transition);\n\n  // Translation Outer\n  domain::CoordinateMaps::TimeDependent::RotScaleTrans<Dim> trans_map_outer =\n      map_serialize_and_deserialize(std::nullopt, std::nullopt, \"translation\",\n                                    BlockRegion::Outer);\n\n  const double far_double_1 = Dim == 2 ? 30.0 : 25.0;\n  const double far_double_2 = Dim == 2 ? 35.36 : 28.87;\n  UniformCustomDistribution<double> dist_double{-10.0, 10.0};\n  UniformCustomDistribution<double> far_dist_double{far_double_1, far_double_2};\n  std::array<double, Dim> point_xi{};\n  std::array<DataVector, Dim> point_xi_dv{};\n  std::array<double, Dim> far_point_xi{};\n  fill_with_random_values(make_not_null(&point_xi), make_not_null(&gen),\n                          make_not_null(&dist_double));\n  fill_with_random_values(make_not_null(&far_point_xi), make_not_null(&gen),\n                          make_not_null(&far_dist_double));\n  for (size_t i = 0; i < Dim; i++) {\n    auto points = make_with_random_values<DataVector>(\n        make_not_null(&gen), make_not_null(&dist_double), DataVector(5));\n    gsl::at(point_xi_dv, i) = points;\n  }\n\n  while (t < final_time) {\n    std::array<double, Dim> translation{};\n    std::array<double, Dim> expected_rotation{};\n    std::array<DataVector, Dim> expected_rotation_dv{};\n    std::array<double, Dim> far_expected_rotation{};\n    const double radius = magnitude(point_xi);\n    const DataVector radius_dv = magnitude(point_xi_dv);\n    const double far_radius = magnitude(far_point_xi);\n    const Matrix rot_matrix =\n        rotation_matrix<Dim>(t, *(f_of_t_list[rot_f_of_t_name]));\n    const Matrix deriv_rot_matrix =\n        rotation_matrix_deriv<Dim>(t, *(f_of_t_list[rot_f_of_t_name]));\n    const double scale_a = scale_a_of_t->func_and_deriv(t)[0][0];\n    const double scale_b = scale_b_of_t->func_and_deriv(t)[0][0];\n    double radial_scaling_factor = 0.0;\n    double radial_translation_factor = 0.0;\n    custom_approx = Approx::custom().epsilon(1e-9).scale(1.0);\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(translation, i) = square(t);\n      for (size_t j = 0; j < Dim; j++) {\n        gsl::at(expected_rotation, i) +=\n            rot_matrix(i, j) * gsl::at(point_xi, j);\n        gsl::at(far_expected_rotation, i) +=\n            rot_matrix(i, j) * gsl::at(far_point_xi, j);\n      }\n    }\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(expected_rotation_dv, i) = rot_matrix(i, 0) * point_xi_dv[0];\n      for (size_t j = 1; j < Dim; j++) {\n        gsl::at(expected_rotation_dv, i) +=\n            rot_matrix(i, j) * gsl::at(point_xi_dv, j);\n      }\n    }\n    // Operator\n    CHECK_ITERABLE_APPROX(trans_map_inner(point_xi, t, f_of_t_list),\n                          point_xi + translation);\n    CHECK_ITERABLE_APPROX(scale_map_inner(point_xi, t, f_of_t_list),\n                          point_xi * scale_a);\n    CHECK_ITERABLE_APPROX(rot_map(point_xi, t, f_of_t_list), expected_rotation);\n    CHECK_ITERABLE_APPROX(rot_scale_map_inner(point_xi, t, f_of_t_list),\n                          expected_rotation * scale_a);\n    CHECK_ITERABLE_APPROX(rot_trans_map_inner(point_xi, t, f_of_t_list),\n                          expected_rotation + translation);\n    CHECK_ITERABLE_APPROX(scale_trans_map_inner(point_xi, t, f_of_t_list),\n                          point_xi * scale_a + translation);\n    CHECK_ITERABLE_APPROX(rot_scale_trans_map_inner(point_xi, t, f_of_t_list),\n                          expected_rotation * scale_a + translation);\n    // Operator DataVector\n    CHECK_ITERABLE_APPROX(\n        rot_scale_trans_map_inner(point_xi_dv, t, f_of_t_list),\n        expected_rotation_dv * scale_a + translation);\n\n    // Testing points close to inner radius\n    radial_scaling_factor =\n        ((inner_radius - radius) * (scale_a - scale_b) * outer_radius) /\n        ((outer_radius - inner_radius) * radius);\n    radial_translation_factor =\n        (inner_radius - radius) / (outer_radius - inner_radius);\n    CHECK_ITERABLE_APPROX(scale_map_transition(point_xi, t, f_of_t_list),\n                          point_xi * (radial_scaling_factor + scale_a));\n    CHECK_ITERABLE_APPROX(\n        trans_map_transition(point_xi, t, f_of_t_list),\n        point_xi + translation + translation * radial_translation_factor);\n    CHECK_ITERABLE_APPROX(\n        rot_scale_map_transition(point_xi, t, f_of_t_list),\n        expected_rotation * (radial_scaling_factor + scale_a));\n    CHECK_ITERABLE_APPROX(rot_trans_map_transition(point_xi, t, f_of_t_list),\n                          expected_rotation + translation +\n                              translation * radial_translation_factor);\n    CHECK_ITERABLE_APPROX(scale_trans_map_transition(point_xi, t, f_of_t_list),\n                          point_xi * (radial_scaling_factor + scale_a) +\n                              translation +\n                              translation * radial_translation_factor);\n    CHECK_ITERABLE_APPROX(\n        rot_scale_trans_map_transition(point_xi, t, f_of_t_list),\n        expected_rotation * (radial_scaling_factor + scale_a) + translation +\n            translation * radial_translation_factor);\n    // Testing far points close to outer radius.\n    if (far_radius < outer_radius) {\n      radial_scaling_factor =\n          ((outer_radius - far_radius) * (scale_a - scale_b) * inner_radius) /\n          ((outer_radius - inner_radius) * far_radius);\n      radial_translation_factor =\n          (outer_radius - far_radius) / (outer_radius - inner_radius);\n      CHECK_ITERABLE_APPROX(scale_map_transition(far_point_xi, t, f_of_t_list),\n                            far_point_xi * (radial_scaling_factor + scale_b));\n      CHECK_ITERABLE_APPROX(\n          trans_map_transition(far_point_xi, t, f_of_t_list),\n          far_point_xi + translation * radial_translation_factor);\n      CHECK_ITERABLE_APPROX(\n          rot_scale_map_transition(far_point_xi, t, f_of_t_list),\n          far_expected_rotation * (radial_scaling_factor + scale_b));\n      CHECK_ITERABLE_APPROX(\n          rot_trans_map_transition(far_point_xi, t, f_of_t_list),\n          far_expected_rotation + translation * radial_translation_factor);\n      CHECK_ITERABLE_APPROX(\n          scale_trans_map_transition(far_point_xi, t, f_of_t_list),\n          far_point_xi * (radial_scaling_factor + scale_b) +\n              translation * radial_translation_factor);\n      CHECK_ITERABLE_APPROX(\n          rot_scale_trans_map_transition(far_point_xi, t, f_of_t_list),\n          far_expected_rotation * (radial_scaling_factor + scale_b) +\n              translation * radial_translation_factor);\n    } else {\n      CHECK_ITERABLE_APPROX(scale_map_outer(far_point_xi, t, f_of_t_list),\n                            far_point_xi * scale_b);\n      CHECK_ITERABLE_APPROX(trans_map_outer(far_point_xi, t, f_of_t_list),\n                            far_point_xi);\n      CHECK_ITERABLE_APPROX(rot_scale_map_outer(far_point_xi, t, f_of_t_list),\n                            far_expected_rotation * scale_b);\n      CHECK_ITERABLE_APPROX(rot_trans_map_outer(far_point_xi, t, f_of_t_list),\n                            far_expected_rotation);\n      CHECK_ITERABLE_APPROX(scale_trans_map_outer(far_point_xi, t, f_of_t_list),\n                            far_point_xi * scale_b);\n      CHECK_ITERABLE_APPROX(\n          rot_scale_trans_map_outer(far_point_xi, t, f_of_t_list),\n          expected_rotation * scale_b);\n    }\n\n    const auto check_inner_maps_inverse = [&](const auto& point_to_check) {\n      test_inverse_map(rot_map, point_to_check, t, f_of_t_list);\n      test_inverse_map(scale_map_inner, point_to_check, t, f_of_t_list);\n      test_inverse_map(trans_map_inner, point_to_check, t, f_of_t_list);\n      test_inverse_map(rot_scale_map_inner, point_to_check, t, f_of_t_list);\n      test_inverse_map(rot_trans_map_inner, point_to_check, t, f_of_t_list);\n      test_inverse_map(scale_trans_map_inner, point_to_check, t, f_of_t_list);\n      test_inverse_map(rot_scale_trans_map_inner, point_to_check, t,\n                       f_of_t_list);\n    };\n    const auto check_transition_maps_inverse = [&](const auto& point_to_check) {\n      test_inverse_map(rot_map, point_to_check, t, f_of_t_list);\n      test_inverse_map(scale_map_transition, point_to_check, t, f_of_t_list);\n      test_inverse_map(trans_map_transition, point_to_check, t, f_of_t_list);\n      test_inverse_map(rot_scale_map_transition, point_to_check, t,\n                       f_of_t_list);\n      test_inverse_map(rot_trans_map_transition, point_to_check, t,\n                       f_of_t_list);\n      test_inverse_map(scale_trans_map_transition, point_to_check, t,\n                       f_of_t_list);\n      test_inverse_map(rot_scale_trans_map_transition, point_to_check, t,\n                       f_of_t_list);\n    };\n    const auto check_outer_maps_inverse = [&](const auto& point_to_check) {\n      test_inverse_map(rot_map, point_to_check, t, f_of_t_list);\n      test_inverse_map(scale_map_outer, point_to_check, t, f_of_t_list);\n      test_inverse_map(trans_map_outer, point_to_check, t, f_of_t_list);\n      test_inverse_map(rot_scale_map_outer, point_to_check, t, f_of_t_list);\n      test_inverse_map(rot_trans_map_outer, point_to_check, t, f_of_t_list);\n      test_inverse_map(scale_trans_map_outer, point_to_check, t, f_of_t_list);\n      test_inverse_map(rot_scale_trans_map_outer, point_to_check, t,\n                       f_of_t_list);\n    };\n    const auto check_all_maps_frame_velocity = [&](const auto& point_to_check) {\n      test_frame_velocity(rot_map, point_to_check, t, f_of_t_list,\n                          custom_approx);\n      test_frame_velocity(scale_map_inner, point_to_check, t, f_of_t_list);\n      test_frame_velocity(trans_map_inner, point_to_check, t, f_of_t_list);\n      test_frame_velocity(rot_scale_map_inner, point_to_check, t, f_of_t_list,\n                          custom_approx);\n      test_frame_velocity(rot_trans_map_inner, point_to_check, t, f_of_t_list,\n                          custom_approx);\n      test_frame_velocity(scale_trans_map_inner, point_to_check, t,\n                          f_of_t_list);\n      test_frame_velocity(rot_scale_trans_map_inner, point_to_check, t,\n                          f_of_t_list, custom_approx);\n      test_frame_velocity(scale_map_transition, point_to_check, t, f_of_t_list);\n      test_frame_velocity(trans_map_transition, point_to_check, t, f_of_t_list);\n      test_frame_velocity(rot_scale_map_transition, point_to_check, t,\n                          f_of_t_list, custom_approx);\n      test_frame_velocity(rot_trans_map_transition, point_to_check, t,\n                          f_of_t_list, custom_approx);\n      test_frame_velocity(scale_trans_map_transition, point_to_check, t,\n                          f_of_t_list);\n      test_frame_velocity(rot_scale_trans_map_transition, point_to_check, t,\n                          f_of_t_list, custom_approx);\n      test_frame_velocity(scale_map_outer, point_to_check, t, f_of_t_list);\n      test_frame_velocity(trans_map_outer, point_to_check, t, f_of_t_list);\n      test_frame_velocity(rot_scale_map_outer, point_to_check, t, f_of_t_list,\n                          custom_approx);\n      test_frame_velocity(rot_trans_map_outer, point_to_check, t, f_of_t_list,\n                          custom_approx);\n      test_frame_velocity(scale_trans_map_outer, point_to_check, t,\n                          f_of_t_list);\n      test_frame_velocity(rot_scale_trans_map_outer, point_to_check, t,\n                          f_of_t_list, custom_approx);\n    };\n    const auto check_all_maps_jacobian = [&](const auto& point_to_check) {\n      test_jacobian(rot_map, point_to_check, t, f_of_t_list, custom_approx);\n      test_inv_jacobian(rot_map, point_to_check, t, f_of_t_list);\n      test_jacobian(scale_map_inner, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(scale_map_inner, point_to_check, t, f_of_t_list);\n      test_jacobian(scale_map_transition, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(scale_map_transition, point_to_check, t, f_of_t_list);\n      test_jacobian(scale_map_outer, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(scale_map_outer, point_to_check, t, f_of_t_list);\n      test_jacobian(trans_map_inner, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(trans_map_inner, point_to_check, t, f_of_t_list);\n      test_jacobian(trans_map_transition, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(trans_map_transition, point_to_check, t, f_of_t_list);\n      test_jacobian(trans_map_outer, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(trans_map_outer, point_to_check, t, f_of_t_list);\n      test_jacobian(rot_scale_map_inner, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(rot_scale_map_inner, point_to_check, t, f_of_t_list);\n      test_jacobian(rot_scale_map_transition, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(rot_scale_map_transition, point_to_check, t,\n                        f_of_t_list);\n      test_jacobian(rot_scale_map_outer, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(rot_scale_map_outer, point_to_check, t, f_of_t_list);\n      test_jacobian(rot_trans_map_inner, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(rot_trans_map_inner, point_to_check, t, f_of_t_list);\n      test_jacobian(rot_trans_map_transition, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(rot_trans_map_transition, point_to_check, t,\n                        f_of_t_list);\n      test_jacobian(rot_trans_map_outer, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(rot_trans_map_outer, point_to_check, t, f_of_t_list);\n      test_jacobian(scale_trans_map_inner, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(scale_trans_map_inner, point_to_check, t, f_of_t_list);\n      test_jacobian(scale_trans_map_transition, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(scale_trans_map_transition, point_to_check, t,\n                        f_of_t_list);\n      test_jacobian(scale_trans_map_outer, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(scale_trans_map_outer, point_to_check, t, f_of_t_list);\n      test_jacobian(rot_scale_trans_map_inner, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(rot_scale_trans_map_inner, point_to_check, t,\n                        f_of_t_list);\n      test_jacobian(rot_scale_trans_map_transition, point_to_check, t,\n                    f_of_t_list, custom_approx);\n      test_inv_jacobian(rot_scale_trans_map_transition, point_to_check, t,\n                        f_of_t_list);\n      test_jacobian(rot_scale_trans_map_outer, point_to_check, t, f_of_t_list,\n                    custom_approx);\n      test_inv_jacobian(rot_scale_trans_map_outer, point_to_check, t,\n                        f_of_t_list);\n    };\n\n    if (radius <= inner_radius) {\n      check_inner_maps_inverse(point_xi);\n    } else {\n      check_transition_maps_inverse(point_xi);\n    }\n    check_all_maps_frame_velocity(point_xi);\n    check_all_maps_jacobian(point_xi);\n    check_all_maps_jacobian(point_xi_dv);\n\n    if (far_radius <= outer_radius) {\n      check_transition_maps_inverse(far_point_xi);\n    } else {\n      check_outer_maps_inverse(far_point_xi);\n    }\n    check_all_maps_frame_velocity(far_point_xi);\n    check_all_maps_jacobian(far_point_xi);\n\n    t += dt;\n  }\n\n  // test serialized/deserialized map and names\n  const auto rot_map_deserialized = serialize_and_deserialize(rot_map);\n  const auto scale_map_inner_deserialized =\n      serialize_and_deserialize(scale_map_inner);\n  const auto scale_map_transition_deserialized =\n      serialize_and_deserialize(scale_map_transition);\n  const auto scale_map_outer_deserialized =\n      serialize_and_deserialize(scale_map_outer);\n  const auto trans_map_inner_deserialized =\n      serialize_and_deserialize(trans_map_inner);\n  const auto trans_map_transition_deserialized =\n      serialize_and_deserialize(trans_map_transition);\n  const auto trans_map_outer_deserialized =\n      serialize_and_deserialize(trans_map_outer);\n  const auto rot_scale_map_inner_deserialized =\n      serialize_and_deserialize(rot_scale_map_inner);\n  const auto rot_scale_map_transition_deserialized =\n      serialize_and_deserialize(rot_scale_map_transition);\n  const auto rot_scale_map_outer_deserialized =\n      serialize_and_deserialize(rot_scale_map_outer);\n  const auto rot_trans_map_inner_deserialized =\n      serialize_and_deserialize(rot_trans_map_inner);\n  const auto rot_trans_map_transition_deserialized =\n      serialize_and_deserialize(rot_trans_map_transition);\n  const auto rot_trans_map_outer_deserialized =\n      serialize_and_deserialize(rot_trans_map_outer);\n  const auto scale_trans_map_inner_deserialized =\n      serialize_and_deserialize(scale_trans_map_inner);\n  const auto scale_trans_map_transition_deserialized =\n      serialize_and_deserialize(scale_trans_map_transition);\n  const auto scale_trans_map_outer_deserialized =\n      serialize_and_deserialize(scale_trans_map_outer);\n  const auto rot_scale_trans_map_inner_deserialized =\n      serialize_and_deserialize(rot_scale_trans_map_inner);\n  const auto rot_scale_trans_map_transition_deserialized =\n      serialize_and_deserialize(rot_scale_trans_map_transition);\n  const auto rot_scale_trans_map_outer_deserialized =\n      serialize_and_deserialize(rot_scale_trans_map_outer);\n\n  CHECK(rot_map == rot_map_deserialized);\n  CHECK(scale_map_inner == scale_map_inner_deserialized);\n  CHECK(scale_map_transition == scale_map_transition_deserialized);\n  CHECK(scale_map_outer == scale_map_outer_deserialized);\n  CHECK(trans_map_inner == trans_map_inner_deserialized);\n  CHECK(trans_map_transition == trans_map_transition_deserialized);\n  CHECK(trans_map_outer == trans_map_outer_deserialized);\n  CHECK(rot_scale_map_inner == rot_scale_map_inner_deserialized);\n  CHECK(rot_scale_map_transition == rot_scale_map_transition_deserialized);\n  CHECK(rot_scale_map_outer == rot_scale_map_outer_deserialized);\n  CHECK(rot_trans_map_inner == rot_trans_map_inner_deserialized);\n  CHECK(rot_trans_map_transition == rot_trans_map_transition_deserialized);\n  CHECK(rot_trans_map_outer == rot_trans_map_outer_deserialized);\n  CHECK(scale_trans_map_inner == scale_trans_map_inner_deserialized);\n  CHECK(scale_trans_map_transition == scale_trans_map_transition_deserialized);\n  CHECK(scale_trans_map_outer == scale_trans_map_outer_deserialized);\n  CHECK(rot_scale_trans_map_inner == rot_scale_trans_map_inner_deserialized);\n  CHECK(rot_scale_trans_map_transition ==\n        rot_scale_trans_map_transition_deserialized);\n  CHECK(rot_scale_trans_map_outer == rot_scale_trans_map_outer_deserialized);\n\n  const auto check_names1 = [](const auto& names) {\n    CHECK(names.size() == 1);\n    CHECK((names.count(\"rotation_angle\") == 1 or\n           names.count(\"translation\") == 1));\n  };\n  const auto check_names2 = [](const auto& names) {\n    CHECK(names.size() == 2);\n    CHECK((\n        (names.count(\"rotation_angle\") == 1 and\n         names.count(\"translation\") == 1) or\n        (names.count(\"expansion_a\") == 1 and names.count(\"expansion_b\") == 1)));\n  };\n  const auto check_names3 = [](const auto& names) {\n    CHECK(names.size() == 3);\n    CHECK((\n        (names.count(\"rotation_angle\") == 1 and\n         names.count(\"expansion_a\") == 1 and names.count(\"expansion_b\") == 1) or\n        (names.count(\"translation\") == 1 and names.count(\"expansion_a\") == 1 and\n         names.count(\"expansion_b\") == 1)));\n  };\n  const auto check_names4 = [](const auto& names) {\n    CHECK(names.size() == 4);\n    CHECK((names.count(\"rotation_angle\") == 1 and\n           names.count(\"expansion_a\") == 1 and\n           names.count(\"expansion_b\") == 1 and\n           names.count(\"translation\") == 1));\n  };\n  check_names1(rot_map.function_of_time_names());\n  check_names1(trans_map_inner.function_of_time_names());\n  check_names1(trans_map_transition.function_of_time_names());\n  check_names1(trans_map_outer.function_of_time_names());\n  check_names2(scale_map_inner.function_of_time_names());\n  check_names2(scale_map_transition.function_of_time_names());\n  check_names2(scale_map_outer.function_of_time_names());\n  check_names2(rot_trans_map_inner.function_of_time_names());\n  check_names2(rot_trans_map_transition.function_of_time_names());\n  check_names2(rot_trans_map_outer.function_of_time_names());\n  check_names3(rot_scale_map_inner.function_of_time_names());\n  check_names3(rot_scale_map_transition.function_of_time_names());\n  check_names3(rot_scale_map_outer.function_of_time_names());\n  check_names3(scale_trans_map_inner.function_of_time_names());\n  check_names3(scale_trans_map_transition.function_of_time_names());\n  check_names3(scale_trans_map_outer.function_of_time_names());\n  check_names4(rot_scale_trans_map_inner.function_of_time_names());\n  check_names4(rot_scale_trans_map_transition.function_of_time_names());\n  check_names4(rot_scale_trans_map_outer.function_of_time_names());\n}\nnamespace domain {\n// [[Timeout, 45]]\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.TimeDependent.RotScaleTrans\",\n                  \"[Domain][Unit]\") {\n  test_RotScaleTrans<2>();\n  test_RotScaleTrans<3>();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_Rotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nclass DataVector;\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n\nnamespace {\ntemplate <size_t Dim>\nMatrix analytic_rotation_matrix(double rotation_angle);\ntemplate <size_t Dim>\nMatrix analytic_rotation_matrix_deriv(double rotation_angle,\n                                      double angular_velocity);\n\ntemplate <>\nMatrix analytic_rotation_matrix<2>(const double rotation_angle) {\n  return Matrix{{cos(rotation_angle), -sin(rotation_angle)},\n                {sin(rotation_angle), cos(rotation_angle)}};\n}\n\ntemplate <>\nMatrix analytic_rotation_matrix_deriv<2>(const double rotation_angle,\n                                         const double angular_velocity) {\n  return Matrix{{-angular_velocity * sin(rotation_angle),\n                 -angular_velocity * cos(rotation_angle)},\n                {angular_velocity * cos(rotation_angle),\n                 -angular_velocity * sin(rotation_angle)}};\n}\n\ntemplate <>\nMatrix analytic_rotation_matrix<3>(const double rotation_angle) {\n  const double c = cos(rotation_angle);\n  const double s = sin(rotation_angle);\n  const double vx = 1.0 / sqrt(3.0);\n  const double vy = -vx;\n  const double vz = vx;\n  // rotate about (1, -1, 1) /root(3)\n  return Matrix{{c + vx * vx * (1.0 - c), vx * vy * (1.0 - c) - vz * s,\n                 vx * vz * (1.0 - c) + vy * s},\n                {vy * vx * (1.0 - c) + vz * s, c + vy * vy * (1.0 - c),\n                 vy * vz * (1.0 - c) - vx * s},\n                {vz * vx * (1.0 - c) - vy * s, vz * vy * (1.0 - c) + vx * s,\n                 c + vz * vz * (1.0 - c)}};\n}\n\ntemplate <>\nMatrix analytic_rotation_matrix_deriv<3>(const double rotation_angle,\n                                         const double angular_velocity) {\n  const double c = cos(rotation_angle);\n  const double s = sin(rotation_angle);\n  const double vx = 1.0 / sqrt(3.0);\n  const double vy = -vx;\n  const double vz = vx;\n  // rotate about (1.0, -1.0, 1.0) /root(3)\n  return Matrix{{angular_velocity * (vx * vx - 1.0) * s,\n                 angular_velocity * (vx * vy * s - vz * c),\n                 angular_velocity * (vx * vz * s + vy * c)},\n                {angular_velocity * (vx * vy * s + vz * c),\n                 angular_velocity * (vy * vy - 1.0) * s,\n                 angular_velocity * (vy * vz * s - vx * c)},\n                {angular_velocity * (vx * vz * s - vy * c),\n                 angular_velocity * (vy * vz * s + vx * c),\n                 angular_velocity * (vz * vz - 1.0) * s}};\n}\n\ntemplate <size_t Dim>\nstd::array<double, Dim> expected_mapped_point(\n    const std::array<double, Dim> initial_unmapped_point, const double time) {\n  const double rotation_angle = square(time);\n  const Matrix rotation_matrix = analytic_rotation_matrix<Dim>(rotation_angle);\n\n  std::array<double, Dim> result{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) = rotation_matrix(i, 0) * initial_unmapped_point[0];\n    for (size_t j = 1; j < Dim; j++) {\n      gsl::at(result, i) +=\n          rotation_matrix(i, j) * gsl::at(initial_unmapped_point, j);\n    }\n  }\n\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::array<double, Dim> expected_frame_velocity(\n    const std::array<double, Dim> initial_unmapped_point, const double time) {\n  const double rotation_angle = square(time);\n  const double angular_velocity = 2.0 * time;\n\n  const Matrix rotation_matrix_deriv =\n      analytic_rotation_matrix_deriv<Dim>(rotation_angle, angular_velocity);\n\n  std::array<double, Dim> result{};\n  for (size_t i = 0; i < Dim; i++) {\n    gsl::at(result, i) =\n        rotation_matrix_deriv(i, 0) * initial_unmapped_point[0];\n    for (size_t j = 1; j < Dim; j++) {\n      gsl::at(result, i) +=\n          rotation_matrix_deriv(i, j) * gsl::at(initial_unmapped_point, j);\n    }\n  }\n\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test_rotation_map() {\n  MAKE_GENERATOR(generator);\n  double t{-1.0};\n  const double dt{0.6};\n  const double final_time{4.0};\n  constexpr size_t deriv_order{3};\n\n  INFO(\"Spatial Dim = \" + get_output(Dim));\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  using QuatFoT =\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n\n  const std::string f_of_t_name{\"rotation_angle\"};\n  std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n  std::array<DataVector, deriv_order + 1> init_func{};\n  // Just need a placeholder for now. The correct epsilons will be set below\n  Approx custom_approx = Approx::custom().epsilon(1.0).scale(1.0);\n  // angle(t) = t^2\n  // omega(t) = 2 * t\n  // dtomega(t) = 2\n  const double angle = 1.0;\n  const double omega = -2.0;\n  const double dtomega = 2.0;\n  const double d2tomega = 0.0;\n  if constexpr (Dim == 2) {\n    init_func = {{{angle}, {omega}, {dtomega}, {d2tomega}}};\n    f_of_t_list[f_of_t_name] =\n        std::make_unique<Polynomial>(t, init_func, final_time + dt);\n    custom_approx = Approx::custom().epsilon(5.0e-13).scale(1.0);\n  } else {\n    // Axis of rotation nhat = (1.0, -1.0, 1.0) / sqrt(3.0)\n    DataVector axis{{1.0, -1.0, 1.0}};\n    axis /= sqrt(3.0);\n    init_func = {axis * angle, axis * omega, axis * dtomega, axis * d2tomega};\n    // initial quaternion is (cos(angle/2), nhat*sin(angle/2))\n    const std::array<DataVector, 1> init_quat{\n        DataVector{{cos(angle / 2.0), axis[0] * sin(angle / 2.0),\n                    axis[1] * sin(angle / 2.0), axis[2] * sin(angle / 2.0)}}};\n    f_of_t_list[f_of_t_name] =\n        std::make_unique<QuatFoT>(t, init_quat, init_func, final_time + dt);\n    custom_approx = Approx::custom().epsilon(5.0e-11).scale(1.0);\n  }\n\n  const domain::CoordinateMaps::TimeDependent::Rotation<Dim> rotation_map{\n      f_of_t_name};\n  const auto check_names = [&f_of_t_name](const auto& names) {\n    CHECK(names.size() == 1);\n    CHECK(names.count(f_of_t_name) == 1);\n  };\n  check_names(rotation_map.function_of_time_names());\n  const auto rotation_map_deserialized =\n      serialize_and_deserialize(rotation_map);\n  check_names(rotation_map_deserialized.function_of_time_names());\n\n  const std::uniform_real_distribution<double> dist{0.0, 5.0};\n  auto initial_unmapped_point =\n      make_with_random_values<std::array<double, Dim>>(\n          make_not_null(&generator), dist, std::array<double, Dim>{});\n\n  while (t < final_time) {\n    const std::array<double, Dim> expected{\n        expected_mapped_point(initial_unmapped_point, t)};\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        rotation_map(initial_unmapped_point, t, f_of_t_list), expected,\n        custom_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        rotation_map.inverse(expected, t, f_of_t_list).value(),\n        initial_unmapped_point, custom_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        rotation_map.frame_velocity(initial_unmapped_point, t, f_of_t_list),\n        expected_frame_velocity(initial_unmapped_point, t), custom_approx);\n\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        rotation_map_deserialized(initial_unmapped_point, t, f_of_t_list),\n        expected, custom_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        rotation_map_deserialized.inverse(expected, t, f_of_t_list).value(),\n        initial_unmapped_point, custom_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        rotation_map_deserialized.frame_velocity(initial_unmapped_point, t,\n                                                 f_of_t_list),\n        expected_frame_velocity(initial_unmapped_point, t), custom_approx);\n\n    const auto jac{\n        rotation_map.jacobian(initial_unmapped_point, t, f_of_t_list)};\n    const auto inv_jac{\n        rotation_map.inv_jacobian(initial_unmapped_point, t, f_of_t_list)};\n    const auto jac_deserialized{rotation_map_deserialized.jacobian(\n        initial_unmapped_point, t, f_of_t_list)};\n    const auto inv_jac_deserialized{rotation_map_deserialized.inv_jacobian(\n        initial_unmapped_point, t, f_of_t_list)};\n\n    const double rotation_angle = square(t);\n    const Matrix analytic_jac = analytic_rotation_matrix<Dim>(rotation_angle);\n\n    for (size_t i = 0; i < Dim; i++) {\n      for (size_t j = 0; j < Dim; j++) {\n        // Jacobian is same as rotation matrix\n        CHECK(jac.get(i, j) == custom_approx(analytic_jac(i, j)));\n        CHECK(jac_deserialized.get(i, j) == custom_approx(analytic_jac(i, j)));\n        // Inv Jacobian is same as inv of rotation matrix\n        CHECK(inv_jac.get(i, j) == custom_approx(analytic_jac(j, i)));\n        CHECK(inv_jac_deserialized.get(i, j) ==\n              custom_approx(analytic_jac(j, i)));\n      }\n    }\n    t += dt;\n  }\n\n  // Check inequivalence operator\n  CHECK_FALSE(rotation_map != rotation_map);\n  CHECK_FALSE(rotation_map_deserialized != rotation_map_deserialized);\n\n  // Check serialization\n  CHECK(rotation_map == rotation_map_deserialized);\n  CHECK_FALSE(rotation_map != rotation_map_deserialized);\n\n  test_coordinate_map_argument_types(rotation_map, initial_unmapped_point, t,\n                                     f_of_t_list);\n  CHECK(\n      not domain::CoordinateMaps::TimeDependent::Rotation<Dim>{}.is_identity());\n}\n\nvoid compare_rotation_maps() {\n  INFO(\"Compare rotation maps\");\n  double t{-1.0};\n  const double final_time{4.0};\n  constexpr size_t deriv_order{3};\n\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  using QuatFoT =\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n\n  const std::string quat_name{\"QuaternionRotation\"};\n  const std::string angle_name{\"RotationAngle\"};\n  std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n  std::array<DataVector, deriv_order + 1> init_func{};\n\n  // angle(t) = t^2\n  // omega(t) = 2 * t\n  // dtomega(t) = 2\n  const double angle = 1.0;\n  const double omega = -2.0;\n  const double dtomega = 2.0;\n  const double d2tomega = 0.0;\n  init_func = {{{angle}, {omega}, {dtomega}, {d2tomega}}};\n  f_of_t_list[angle_name] =\n      std::make_unique<Polynomial>(t, init_func, final_time);\n\n  // Axis of rotation nhat = (0.0, 0.0, 1.0)\n  DataVector axis{{0.0, 0.0, 1.0}};\n  init_func = {axis * angle, axis * omega, axis * dtomega, axis * d2tomega};\n  // initial quaternion is (cos(angle/2), nhat*sin(angle/2))\n  const std::array<DataVector, 1> init_quat{\n      DataVector{{cos(angle / 2.0), axis[0] * sin(angle / 2.0),\n                  axis[1] * sin(angle / 2.0), axis[2] * sin(angle / 2.0)}}};\n  f_of_t_list[quat_name] =\n      std::make_unique<QuatFoT>(t, init_quat, init_func, final_time);\n\n  using RotationMap2D = domain::CoordinateMaps::TimeDependent::Rotation<2>;\n  using IdentityMap = domain::CoordinateMaps::Identity<1>;\n  using RotationMap3D = domain::CoordinateMaps::TimeDependent::Rotation<3>;\n  using ProductRotationMap2D =\n      domain::CoordinateMaps::TimeDependent::ProductOf2Maps<RotationMap2D,\n                                                            IdentityMap>;\n\n  const RotationMap2D rotation_map_2d{angle_name};\n  const IdentityMap identity_map{};\n  const ProductRotationMap2D rotation_map_product{rotation_map_2d,\n                                                  identity_map};\n  const RotationMap3D rotation_map_3d{quat_name};\n\n  const domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                              ProductRotationMap2D>\n      rotation_map_from_2d{rotation_map_product};\n  const domain::CoordinateMap<Frame::Grid, Frame::Inertial, RotationMap3D>\n      rotation_map_from_3d{rotation_map_3d};\n\n  Approx custom_approx = Approx::custom().epsilon(5.0e-13).scale(1.0);\n  check_if_maps_are_equal(rotation_map_from_2d, rotation_map_from_3d,\n                          final_time, f_of_t_list, custom_approx);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.TimeDependent.Rotation\",\n                  \"[Domain][Unit]\") {\n  test_rotation_map<2>();\n  test_rotation_map<3>();\n\n  // The 3D map should be equal to the identity * 2D map if the rotation is\n  // about the z axis\n  compare_rotation_maps();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_RotationMatrixHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/RotationMatrixHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n\nnamespace {\nvoid check_rotation_matrix_helpers_3d(\n    const std::array<DataVector, 3>& init_angle,\n    const Matrix& expected_rot_matrix, const Matrix& expected_rot_matrix_deriv,\n    const double check_time) {\n  using QuatFoft = domain::FunctionsOfTime::QuaternionFunctionOfTime<2>;\n\n  const double t = 0.0;\n  const std::array<DataVector, 1> init_quat{DataVector{{1.0, 0.0, 0.0, 0.0}}};\n\n  QuatFoft quat_fot(t, init_quat, init_angle, t + 5.0);\n\n  const Matrix rot_matrix = rotation_matrix<3>(check_time, quat_fot);\n  const Matrix rot_matrix_deriv =\n      rotation_matrix_deriv<3>(check_time, quat_fot);\n\n  Approx custom_approx = Approx::custom().epsilon(5e-12).scale(1.0);\n  {\n    INFO(\"Check rotation matrix\");\n    CHECK_ITERABLE_CUSTOM_APPROX(rot_matrix, expected_rot_matrix,\n                                 custom_approx);\n  }\n  {\n    INFO(\"Check rotation matrix deriv\");\n    CHECK_ITERABLE_CUSTOM_APPROX(rot_matrix_deriv, expected_rot_matrix_deriv,\n                                 custom_approx);\n  }\n}\n\nvoid check_rotation_matrix_helpers_2d(const double init_omega,\n                                      const Matrix& expected_rot_matrix,\n                                      const Matrix& expected_rot_matrix_deriv,\n                                      const double check_time) {\n  using FoT = domain::FunctionsOfTime::PiecewisePolynomial<2>;\n\n  const double t = 0.0;\n  const std::array<DataVector, 3> init_angle_func{{{0.0}, {init_omega}, {0.0}}};\n\n  FoT function_of_time(t, init_angle_func, t + 5.0);\n\n  const Matrix rot_matrix = rotation_matrix<2>(check_time, function_of_time);\n  const Matrix rot_matrix_deriv =\n      rotation_matrix_deriv<2>(check_time, function_of_time);\n\n  {\n    INFO(\"Check rotation matrix\");\n    CHECK_ITERABLE_APPROX(rot_matrix, expected_rot_matrix);\n  }\n  {\n    INFO(\"Check rotation matrix deriv\");\n    CHECK_ITERABLE_APPROX(rot_matrix_deriv, expected_rot_matrix_deriv);\n  }\n}\n\nvoid test_rotation_matrix_helpers_2d() {\n  INFO(\"Dim = 2\");\n  const double omega = 2.0;\n  const double check_time = 1.0;\n\n  const std::array<DataVector, 3> init_angle{DataVector{0.0}, DataVector{omega},\n                                             DataVector{0.0}};\n\n  const double theta = omega * check_time;\n  const Matrix expected_rot_matrix_z{{cos(theta), -sin(theta)},\n                                     {sin(theta), cos(theta)}};\n  const Matrix expected_rot_matrix_deriv_z{\n      {-2.0 * sin(theta), -2.0 * cos(theta)},\n      {2.0 * cos(theta), -2.0 * sin(theta)}};\n\n  check_rotation_matrix_helpers_2d(omega, expected_rot_matrix_z,\n                                   expected_rot_matrix_deriv_z, check_time);\n}\n\nvoid test_rotation_matrix_helpers_3d() {\n  INFO(\"Dim = 3\");\n  {\n    INFO(\"Rotation about z\");\n    const double omega = 2.0;\n    const double check_time = 1.0;\n\n    const std::array<DataVector, 3> init_angle{\n        DataVector{3, 0.0}, DataVector{{0.0, 0.0, omega}}, DataVector{3, 0.0}};\n\n    const double theta = omega * check_time;\n    const Matrix expected_rot_matrix_z{{cos(theta), -sin(theta), 0.0},\n                                       {sin(theta), cos(theta), 0.0},\n                                       {0.0, 0.0, 1.0}};\n    const Matrix expected_rot_matrix_deriv_z{\n        {-2.0 * sin(theta), -2.0 * cos(theta), 0.0},\n        {2.0 * cos(theta), -2.0 * sin(theta), 0.0},\n        {0.0, 0.0, 0.0}};\n\n    check_rotation_matrix_helpers_3d(init_angle, expected_rot_matrix_z,\n                                     expected_rot_matrix_deriv_z, check_time);\n  }\n\n  {\n    INFO(\"Rotation about x\");\n    const double omega = 2.0;\n    const double check_time = 1.0;\n\n    const std::array<DataVector, 3> init_angle{\n        DataVector{3, 0.0}, DataVector{{omega, 0.0, 0.0}}, DataVector{3, 0.0}};\n\n    const double theta = omega * check_time;\n    const Matrix expected_rot_matrix_x{{1.0, 0.0, 0.0},\n                                       {0.0, cos(theta), -sin(theta)},\n                                       {0.0, sin(theta), cos(theta)}};\n    const Matrix expected_rot_matrix_deriv_x{\n        {0.0, 0.0, 0.0},\n        {0.0, -2.0 * sin(theta), -2.0 * cos(theta)},\n        {0.0, 2.0 * cos(theta), -2.0 * sin(theta)}};\n\n    check_rotation_matrix_helpers_3d(init_angle, expected_rot_matrix_x,\n                                     expected_rot_matrix_deriv_x, check_time);\n  }\n\n  {\n    INFO(\"Rotation about y\");\n    const double omega = 2.0;\n    const double check_time = 1.0;\n\n    const std::array<DataVector, 3> init_angle{\n        DataVector{3, 0.0}, DataVector{{0.0, omega, 0.0}}, DataVector{3, 0.0}};\n\n    const double theta = omega * check_time;\n    const Matrix expected_rot_matrix_y{{cos(theta), 0.0, sin(theta)},\n                                       {0.0, 1.0, 0.0},\n                                       {-sin(theta), 0.0, cos(theta)}};\n    const Matrix expected_rot_matrix_deriv_y{\n        {-2.0 * sin(theta), 0.0, 2.0 * cos(theta)},\n        {0.0, 0.0, 0.0},\n        {-2.0 * cos(theta), 0.0, -2.0 * sin(theta)}};\n\n    check_rotation_matrix_helpers_3d(init_angle, expected_rot_matrix_y,\n                                     expected_rot_matrix_deriv_y, check_time);\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Domain.CoordinateMaps.TimeDependent.RotationMatrixHelpers\",\n    \"[Unit][Domain]\") {\n  test_rotation_matrix_helpers_2d();\n  test_rotation_matrix_helpers_3d();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_Shape.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace domain {\nnamespace {\n\nconst std::complex<double> imag(0, 1);\nconstexpr double truncation_limit = 1e-14;\n\nstd::complex<double> Y00(double /*theta*/, double /*phi*/) {\n  return 0.5 * sqrt(1. / M_PI);\n}\n\nstd::complex<double> Y10(double theta, double /*phi*/) {\n  return 0.5 * sqrt(3. / M_PI) * cos(theta);\n}\n\nstd::complex<double> Y11(double theta, double phi) {\n  return -0.5 * sqrt(3. / (2. * M_PI)) * sin(theta) *\n         std::exp<double>(imag * phi);\n}\n\nstd::complex<double> Y20(double theta, double /*phi*/) {\n  return 0.25 * sqrt(5. / M_PI) * (3. * cos(theta) * cos(theta) - 1.);\n}\n\nstd::complex<double> Y21(double theta, double phi) {\n  return -0.5 * sqrt(15. / (2. * M_PI)) * sin(theta) * cos(theta) *\n         std::exp<double>(imag * phi);\n}\n\nstd::complex<double> Y22(double theta, double phi) {\n  return 0.25 * sqrt(15. / (2. * M_PI)) * sin(theta) * sin(theta) *\n         std::exp<double>(2. * imag * phi);\n}\n\nstd::complex<double> Y30(double theta, double /*phi*/) {\n  return 0.25 * sqrt(7. / M_PI) * (5 * pow(cos(theta), 3) - 3 * cos(theta));\n}\n\nstd::complex<double> Y31(double theta, double phi) {\n  return -0.125 * sqrt(21. / M_PI) * sin(theta) *\n         (5 * cos(theta) * cos(theta) - 1) * std::exp<double>(imag * phi);\n}\n\nstd::complex<double> Y32(double theta, double phi) {\n  return 0.25 * sqrt(105. / (2. * M_PI)) * sin(theta) * sin(theta) *\n         cos(theta) * std::exp<double>(2. * imag * phi);\n}\n\nstd::complex<double> Y33(double theta, double phi) {\n  return -0.125 * sqrt(35. / M_PI) * pow(sin(theta), 3) *\n         std::exp<double>(3. * imag * phi);\n}\n\n// vector of spherical harmonics, e.g. Y21 is harmonics[2][1]\nusing SphericalHarmonic = std::function<std::complex<double>(double, double)>;\nstd::vector<std::vector<SphericalHarmonic>> harmonics = {\n    {Y00}, {Y10, Y11}, {Y20, Y21, Y22}, {Y30, Y31, Y32, Y33}};\n\n// returns Ylm\nauto identity = [](size_t l, size_t m) {\n  return [l, m](double theta, double phi) {\n    return gsl::at(gsl::at(harmonics, l), m)(theta, phi);\n  };\n};\n\n// computes the coefficient C_{lm} = sqrt((l+m)(l-m)/(2l-1)(2l+1)) used by\n// `dtheta` below\ndouble c_coef(size_t l, size_t m) {\n  return sqrt(static_cast<double>((l + m) * (l - m)) /\n              ((2. * l - 1.) * (2. * l + 1.)));\n}\n\n// returns sin(\\theta) \\partial_\\theta Y_{l m} = l C_{l+1 m} Y_{l+1 m} -\n// (l+1) C_{l m} Y_{l m}\nauto dtheta = [](size_t l, size_t m) {\n  return [l, m](double theta, double phi) {\n    const auto term1 = l * c_coef(l + 1, m) *\n                       gsl::at(gsl::at(harmonics, l + 1), m)(theta, phi);\n    const auto term2 =\n        (l > 0 and m < l)\n            ? (l + 1.) * c_coef(l, m) *\n                  gsl::at(gsl::at(harmonics, l - 1), m)(theta, phi)\n            : std::complex(0.);\n    return term1 - term2;\n  };\n};\n\n// returns \\partial_\\phi Y_{l m} = i m Y_{l m}\nauto dphi = [](size_t l, size_t m) {\n  return [l, m](double theta, double phi) {\n    return std::complex(0., 1.) * static_cast<double>(m) *\n           gsl::at(gsl::at(harmonics, l), m)(theta, phi);\n  };\n};\n\n// Sums up spherical harmonics using the provided functions and coefficients\nauto evaluate_harmonic_expansion =\n    [](auto function, double theta, double phi,\n       const std::vector<std::vector<std::complex<double>>>& coefs,\n       const std::optional<double>& lambda_00_coef, size_t l_max) {\n      std::complex<double> res = 0.;\n      for (size_t l = 0; l <= l_max; ++l) {\n        if (l == 0 and lambda_00_coef.has_value()) {\n          CHECK(coefs[0][0] == 0.0);\n          res += lambda_00_coef.value() * function(l, 0)(theta, phi);\n        } else {\n          res += gsl::at(gsl::at(coefs, l), 0) * function(l, 0)(theta, phi);\n        }\n        for (size_t m = 1; m <= l; ++m) {\n          res += 2. * (std::real(gsl::at(gsl::at(coefs, l), m)) *\n                           std::real(function(l, m)(theta, phi)) -\n                       std::imag(gsl::at(gsl::at(coefs, l), m)) *\n                           std::imag(function(l, m)(theta, phi)));\n        }\n      }\n      ASSERT(std::imag(res) == approx(0.),\n             \"Expansion has non-zero imaginary component: \" << std::imag(res));\n      return std::real(res);\n    };\n\nusing FunctionsOfTimeMap =\n    std::unordered_map<std::string,\n                       std::unique_ptr<FunctionsOfTime::FunctionOfTime>>;\n\nstd::vector<std::vector<std::complex<double>>> generate_random_coefs(\n    size_t l_max, gsl::not_null<std::mt19937*> generator) {\n  std::vector<std::vector<std::complex<double>>> coefs{};\n  // if the coefficients are made too large, the map has a good chance of\n  // mapping through the center, causing the test to fail which is why we limit\n  // the coefficients to a magnitude of at most 0.01\n  std::uniform_real_distribution<double> coef_dist{0., 0.01};\n\n  for (size_t l = 0; l <= l_max; ++l) {\n    // m=0 is real\n    std::vector<std::complex<double>> tmp{\n        make_with_random_values<double>(generator, coef_dist, 1)};\n    for (size_t m = 1; m <= l; ++m) {\n      tmp.emplace_back(make_with_random_values<std::complex<double>>(\n          generator, coef_dist, 2));\n    }\n    coefs.emplace_back(tmp);\n  }\n\n  return coefs;\n}\n\ndouble generate_random_00_coef(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<double> coef_dist{0., 0.01};\n  return coef_dist(*generator);\n}\n\n// converts complex coefficients to format expected by spherepack\nDataVector convert_coefs_to_spherepack(\n    const std::vector<std::vector<std::complex<double>>>& coefs, size_t l_max) {\n  ylm::SpherepackIterator iter(l_max, l_max);\n  auto spherepack_coefs =\n      make_with_value<DataVector>(iter.spherepack_array_size(), 0.);\n\n  const double sqrt_2_by_pi = sqrt(2. / M_PI);\n  for (size_t l = 0; l <= l_max; ++l) {\n    iter.set(l, 0);\n    spherepack_coefs[iter()] =\n        sqrt_2_by_pi * std::real(gsl::at(gsl::at(coefs, l), 0));\n    for (size_t m = 1; m <= l; ++m) {\n      iter.set(l, m);\n      spherepack_coefs[iter()] =\n          pow(-1, m) * sqrt_2_by_pi * std::real(gsl::at(gsl::at(coefs, l), m));\n      iter.set(l, -m);\n      spherepack_coefs[iter()] =\n          pow(-1, m) * sqrt_2_by_pi * std::imag(gsl::at(gsl::at(coefs, l), m));\n    }\n  }\n  return spherepack_coefs;\n}\n\nDataVector pad_spherepack_coefs_with_truncation_values(\n    const DataVector& coefs, const size_t original_l_max,\n    const size_t target_l_max) {\n  CHECK(target_l_max >= original_l_max);\n\n  ylm::SpherepackIterator original_iter{original_l_max, original_l_max};\n  ylm::SpherepackIterator target_iter{target_l_max, target_l_max};\n  DataVector padded_coefs(target_iter.spherepack_array_size(), 0.0);\n\n  for (original_iter.reset(); original_iter; ++original_iter) {\n    target_iter.set(original_iter.l(), original_iter.m(),\n                    original_iter.coefficient_array());\n    padded_coefs[target_iter()] = coefs[original_iter()];\n  }\n\n  const double tiny_value = 0.5 * truncation_limit;\n  for (target_iter.reset(); target_iter; ++target_iter) {\n    if (target_iter.l() > original_l_max) {\n      padded_coefs[target_iter()] = tiny_value;\n    }\n  }\n  return padded_coefs;\n}\n\n// Generates the map, time, and a FunctionOfTime. `const_f_of_t` decides whether\n// the function of time will be constant which is needed for the analytical\n// comparisons.\ntemplate <typename TransitionFunction>\nvoid generate_random_map_time_and_f_of_time(\n    const gsl::not_null<CoordinateMaps::TimeDependent::Shape*> map,\n    const gsl::not_null<double*> time,\n    const gsl::not_null<FunctionsOfTimeMap*> functions_of_time,\n    const size_t l_max, const std::array<double, 3>& center,\n    const TransitionFunction& transition_func, const DataVector& ylm_coefs,\n    const std::optional<double>& lambda_00_coef, const bool const_f_of_t,\n    gsl::not_null<std::mt19937*> generator,\n    const std::optional<size_t> functions_of_time_l_max = std::nullopt) {\n  const std::string shape_f_of_t_name{\"Shape\"};\n  const std::optional<std::string> size_f_of_t_name{\"Size\"};\n\n  const size_t function_of_time_l_max = functions_of_time_l_max.value_or(l_max);\n  CHECK(function_of_time_l_max >= l_max);\n  const ylm::SpherepackIterator function_of_time_iterator{\n      function_of_time_l_max, function_of_time_l_max};\n  *map = CoordinateMaps::TimeDependent::Shape(\n      center, truncation_limit,\n      std::make_unique<TransitionFunction>(transition_func), shape_f_of_t_name,\n      lambda_00_coef.has_value() ? size_f_of_t_name : std::nullopt);\n  const auto& function_of_time_names = map->function_of_time_names();\n  const size_t expected_size = lambda_00_coef.has_value() ? 2_st : 1_st;\n  CHECK(function_of_time_names.size() == expected_size);\n  CHECK(function_of_time_names.count(\"Shape\") == 1);\n  if (lambda_00_coef.has_value()) {\n    CHECK(function_of_time_names.count(\"Size\") == 1);\n  }\n  // Choose a random time for evaluating the FunctionOfTime\n  std::uniform_real_distribution<double> time_dist{-1.0, 1.0};\n  *time = time_dist(*generator);\n\n  std::uniform_real_distribution<> dt_dis{0.1, 0.5};\n  const double initial_time{*time - dt_dis(*generator)};\n  const double expiration_time{*time + dt_dis(*generator)};\n\n  DataVector shape_dtcoefs{function_of_time_iterator.spherepack_array_size(),\n                           0.};\n  DataVector shape_ddtcoefs{function_of_time_iterator.spherepack_array_size(),\n                            0.};\n\n  if (not const_f_of_t) {\n    const auto dt_random_coefs = generate_random_coefs(l_max, generator);\n    shape_dtcoefs = pad_spherepack_coefs_with_truncation_values(\n        convert_coefs_to_spherepack(dt_random_coefs, l_max), l_max,\n        function_of_time_l_max);\n    const auto ddt_random_coefs = generate_random_coefs(l_max, generator);\n    shape_ddtcoefs = pad_spherepack_coefs_with_truncation_values(\n        convert_coefs_to_spherepack(ddt_random_coefs, l_max), l_max,\n        function_of_time_l_max);\n  }\n\n  if (lambda_00_coef.has_value()) {\n    DataVector size_dtcoefs{1, 0.};\n    DataVector size_ddtcoefs{1, 0.};\n    if (not const_f_of_t) {\n      size_dtcoefs[0] = generate_random_00_coef(generator);\n      size_ddtcoefs[0] = generate_random_00_coef(generator);\n    }\n    const std::array<DataVector, 3> initial_size_coefficients = {\n        DataVector{lambda_00_coef.value()}, size_dtcoefs, size_ddtcoefs};\n    (*functions_of_time)[size_f_of_t_name.value()] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n            initial_time, initial_size_coefficients, expiration_time);\n\n    shape_dtcoefs[0] = 0.0;\n    shape_ddtcoefs[0] = 0.0;\n  }\n\n  const DataVector shape_coefs = pad_spherepack_coefs_with_truncation_values(\n      ylm_coefs, l_max, function_of_time_l_max);\n  const std::array<DataVector, 3> initial_shape_coefficients = {\n      shape_coefs, shape_dtcoefs, shape_ddtcoefs};\n\n  (*functions_of_time)[shape_f_of_t_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time, initial_shape_coefficients, expiration_time);\n}\n\ntemplate <typename TransitionFunction>\nvoid test_map_helpers(const TransitionFunction& transition_func, size_t l_max,\n                      const bool include_size,\n                      gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution dist{-10., 10.};\n  const auto center =\n      make_with_random_values<std::array<double, 3>>(generator, dist, 3);\n\n  FunctionsOfTimeMap functions_of_time{};\n  double time{};\n  auto map = CoordinateMaps::TimeDependent::Shape{};\n  auto random_coefs = generate_random_coefs(l_max, generator);\n  DataVector spherepack_coefs =\n      convert_coefs_to_spherepack(random_coefs, l_max);\n  std::optional<double> random_00_coef{};\n  if (include_size) {\n    spherepack_coefs[0] = 0.0;\n    random_00_coef = generate_random_00_coef(generator);\n  }\n\n  generate_random_map_time_and_f_of_time(\n      make_not_null(&map), make_not_null(&time),\n      make_not_null(&functions_of_time), l_max, center, transition_func,\n      spherepack_coefs, random_00_coef, false, generator, l_max + 2);\n\n  const auto random_point =\n      make_with_random_values<std::array<double, 3>>(generator, dist, 3);\n\n  // Check map against a suite of functions in\n  // tests/Unit/Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\n  test_serialization(map);\n  test_copy_semantics(map);\n  CHECK_FALSE(map != map);\n  test_coordinate_map_argument_types(map, random_point, time,\n                                     functions_of_time);\n  test_inverse_map(map, random_point, time, functions_of_time);\n  test_frame_velocity(map, random_point, time, functions_of_time);\n  test_jacobian(map, random_point, time, functions_of_time);\n  test_inv_jacobian(map, random_point, time, functions_of_time);\n}\n\n// duplicates map but calculates spherical harmonics expansion directly.\nstd::array<DataVector, 3> calculate_analytical_map(\n    std::array<DataVector, 3> target_points, std::array<double, 3> center,\n    size_t l_max, const std::vector<std::vector<std::complex<double>>>& coefs,\n    const std::optional<double>& lambda_00_coef,\n    const CoordinateMaps::ShapeMapTransitionFunctions::\n        ShapeMapTransitionFunction& transition_func) {\n  const size_t num_points = target_points[0].size();\n\n  const auto centered_coords = target_points - center;\n\n  const DataVector target_thetas =\n      atan2(hypot(centered_coords[0], centered_coords[1]), centered_coords[2]);\n  const DataVector target_phis = atan2(centered_coords[1], centered_coords[0]);\n\n  DataVector angular_part(num_points);\n\n  for (size_t i = 0; i < num_points; ++i) {\n    angular_part[i] = evaluate_harmonic_expansion(\n        identity, gsl::at(target_thetas, i), gsl::at(target_phis, i), coefs,\n        lambda_00_coef, l_max);\n  }\n  const DataVector spatial_part =\n      transition_func(centered_coords, std::nullopt);\n  return target_points - centered_coords * angular_part * spatial_part;\n}\n\ntemplate <typename TransitionFunction>\nvoid test_analytical_solution(const TransitionFunction& transition_func,\n                              size_t l_max, const bool include_size,\n                              size_t num_points,\n                              gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution dist{-10., 10.};\n  const auto center =\n      make_with_random_values<std::array<double, 3>>(generator, dist, 3);\n  const auto target_data = make_with_random_values<std::array<DataVector, 3>>(\n      generator, dist, num_points);\n\n  auto random_coefs = generate_random_coefs(l_max, generator);\n\n  FunctionsOfTimeMap functions_of_time{};\n  double time{};\n  auto map = CoordinateMaps::TimeDependent::Shape{};\n\n  DataVector spherepack_coefs =\n      convert_coefs_to_spherepack(random_coefs, l_max);\n  std::optional<double> random_00_coef{};\n  if (include_size) {\n    random_coefs[0][0] = 0.0;\n    spherepack_coefs[0] = 0.0;\n    random_00_coef = generate_random_00_coef(generator);\n  }\n\n  generate_random_map_time_and_f_of_time(\n      make_not_null(&map), make_not_null(&time),\n      make_not_null(&functions_of_time), l_max, center, transition_func,\n      spherepack_coefs, random_00_coef, true, generator, l_max + 2);\n  const auto mapped_result = map(target_data, time, functions_of_time);\n  const auto analytical_result =\n      calculate_analytical_map(target_data, center, l_max, random_coefs,\n                               random_00_coef, transition_func);\n  CHECK_ITERABLE_APPROX(mapped_result, analytical_result);\n}\n\n// calculate the Jacobian using spherical harmonics directly\ntnsr::Ij<DataVector, 3, Frame::NoFrame> calculate_analytical_jacobian(\n    const std::array<DataVector, 3>& target_points,\n    const std::array<double, 3>& center, size_t l_max,\n    const std::vector<std::vector<std::complex<double>>>& coefs,\n    const std::optional<double>& lambda_00_coef,\n    const CoordinateMaps::ShapeMapTransitionFunctions::\n        ShapeMapTransitionFunction& transition_func) {\n  const size_t num_points = target_points[0].size();\n  const auto centered_coords = target_points - center;\n  const DataVector target_thetas =\n      atan2(hypot(centered_coords[0], centered_coords[1]), centered_coords[2]);\n  const DataVector target_phis = atan2(centered_coords[1], centered_coords[0]);\n  DataVector angular_part(num_points);\n  DataVector theta_gradient(num_points);\n  DataVector phi_gradient(num_points);\n  for (size_t i = 0; i < num_points; ++i) {\n    angular_part.at(i) = evaluate_harmonic_expansion(\n        identity, gsl::at(target_thetas, i), gsl::at(target_phis, i), coefs,\n        lambda_00_coef, l_max);\n    theta_gradient.at(i) = evaluate_harmonic_expansion(\n        dtheta, gsl::at(target_thetas, i), gsl::at(target_phis, i), coefs,\n        lambda_00_coef, l_max);\n    phi_gradient.at(i) = evaluate_harmonic_expansion(\n        dphi, gsl::at(target_thetas, i), gsl::at(target_phis, i), coefs,\n        lambda_00_coef, l_max);\n  }\n  // multiply angular gradient by inverse jacobian to get cartesian gradient\n  std::array<DataVector, 3> cartesian_gradient{};\n  cartesian_gradient[0] =\n      (cos(target_thetas) * cos(target_phis) * theta_gradient -\n       sin(target_phis) * phi_gradient) /\n      sin(target_thetas);\n  cartesian_gradient[1] =\n      (cos(target_thetas) * sin(target_phis) * theta_gradient +\n       cos(target_phis) * phi_gradient) /\n      sin(target_thetas);\n  cartesian_gradient[2] = -theta_gradient;\n\n  // this part essentially duplicates the code from the map\n  const DataVector radius = magnitude(centered_coords);\n  const DataVector spatial_part =\n      transition_func(centered_coords, std::nullopt);\n  const std::array<DataVector, 3> spatial_gradient =\n      transition_func.gradient(centered_coords);\n  tnsr::Ij<DataVector, 3, Frame::NoFrame> result(num_points);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      result.get(i, j) =\n          -gsl::at(centered_coords, i) *\n          (angular_part * gsl::at(spatial_gradient, j) +\n           gsl::at(cartesian_gradient, j) * spatial_part / radius);\n    }\n    result.get(i, i) += 1. - angular_part * spatial_part;\n  }\n  return result;\n}\n\ntemplate <typename TransitionFunction>\nvoid test_analytical_jacobian(const TransitionFunction& transition_func,\n                              size_t l_max, const bool include_size,\n                              size_t num_points,\n                              gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution dist{-10., 10.};\n  const auto center =\n      make_with_random_values<std::array<double, 3>>(generator, dist, 3);\n  auto target_data = make_with_random_values<std::array<DataVector, 3>>(\n      generator, dist, num_points);\n\n  // the analytical solution divides by sin(theta), so it might get into\n  // trouble at the poles, although it appeared fine without this safety\n  // measure in >10^8 tries. Here, the y-coordinate is increased if the point\n  // has theta close to zero.\n  const auto centered_coords = target_data - center;\n  const DataVector sin_thetas = sin(\n      atan2(hypot(centered_coords[0], centered_coords[1]), centered_coords[2]));\n  for (size_t i = 0; i < sin_thetas.size(); ++i) {\n    if (abs(gsl::at(sin_thetas, i)) < 1e-4) {\n      target_data[1][i] += 1.;\n    }\n  }\n\n  auto random_coefs = generate_random_coefs(l_max, generator);\n\n  FunctionsOfTimeMap functions_of_time{};\n  double time{};\n  auto map = CoordinateMaps::TimeDependent::Shape{};\n\n  DataVector spherepack_coefs =\n      convert_coefs_to_spherepack(random_coefs, l_max);\n  std::optional<double> random_00_coef{};\n  if (include_size) {\n    random_coefs[0][0] = 0.0;\n    spherepack_coefs[0] = 0.0;\n    random_00_coef = generate_random_00_coef(generator);\n  }\n\n  generate_random_map_time_and_f_of_time(\n      make_not_null(&map), make_not_null(&time),\n      make_not_null(&functions_of_time), l_max, center, transition_func,\n      spherepack_coefs, random_00_coef, true, generator, l_max + 2);\n  const auto mapped_jacobian =\n      map.jacobian(target_data, time, functions_of_time);\n  const auto analytical_jacobian =\n      calculate_analytical_jacobian(target_data, center, l_max, random_coefs,\n                                    random_00_coef, transition_func);\n  const Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(mapped_jacobian, analytical_jacobian,\n                               custom_approx);\n}\n\ntemplate <typename Generator>\nvoid test_inverse(const gsl::not_null<Generator*> generator) {\n  using TransitionFunc =\n      CoordinateMaps::ShapeMapTransitionFunctions::SphereTransition;\n  const double time = 1.0;\n  const TransitionFunc sphere_transition{1.0, 1.5};\n  CoordinateMaps::TimeDependent::Shape shape{\n      std::array{0.0, 0.0, 0.0}, 0.0,\n      std::make_unique<TransitionFunc>(sphere_transition), \"Shape\"};\n\n  DataVector coefs{ylm::Spherepack::spectral_size(10, 10), 0.0};\n  DataVector dt_coefs{ylm::Spherepack::spectral_size(10, 10), 0.0};\n  DataVector d2t_coefs{ylm::Spherepack::spectral_size(10, 10), 0.0};\n\n  const double factor = sqrt(2.0 / M_PI);\n\n  coefs[0] = 0.5 * factor;\n  dt_coefs[0] = -0.04 * factor;\n  d2t_coefs[0] = 0.003 * factor;\n\n  FunctionsOfTimeMap functions_of_time{};\n  functions_of_time[\"Shape\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          time, std::array{coefs, dt_coefs, d2t_coefs}, time + 3.0);\n\n  std::uniform_real_distribution<double> dist_phi{0.0, 2.0 * M_PI};\n  std::uniform_real_distribution<double> dist_theta{0.0, M_PI};\n\n  for (const double radius : std::array{1.0, 1.2, 1.5}) {\n    const double theta = dist_theta(*generator);\n    const double phi = dist_phi(*generator);\n\n    // Random point on a sphere of that radius\n    const double x = radius * sin(theta) * cos(phi);\n    const double y = radius * sin(theta) * sin(phi);\n    const double z = radius * cos(theta);\n    const std::array<double, 3> grid_coords{x, y, z};\n\n    const auto inertial_coords = shape(grid_coords, time, functions_of_time);\n\n    const auto mapped_coords =\n        shape.inverse(inertial_coords, time, functions_of_time);\n    CHECK(mapped_coords.has_value());\n    const double mapped_radius = magnitude(mapped_coords.value());\n\n    CHECK_ITERABLE_APPROX(grid_coords, mapped_coords.value());\n    CHECK(radius == approx(mapped_radius));\n  }\n}\n\ntemplate <typename TransitionFunction>\nvoid test_combined_call(const TransitionFunction& transition_func, size_t l_max,\n                        size_t num_points,\n                        gsl::not_null<std::mt19937*> generator) {\n  const std::uniform_real_distribution dist{-10., 10.};\n  const auto center =\n      make_with_random_values<std::array<double, 3>>(generator, dist, 3);\n  auto target_data = make_with_random_values<std::array<DataVector, 3>>(\n      generator, dist, num_points);\n\n  auto random_coefs = generate_random_coefs(l_max, generator);\n\n  FunctionsOfTimeMap functions_of_time{};\n  double time{};\n  auto map = CoordinateMaps::TimeDependent::Shape{};\n\n  DataVector spherepack_coefs =\n      convert_coefs_to_spherepack(random_coefs, l_max);\n  std::optional<double> random_00_coef{};\n  random_coefs[0][0] = 0.0;\n  spherepack_coefs[0] = 0.0;\n  random_00_coef = generate_random_00_coef(generator);\n  generate_random_map_time_and_f_of_time(\n      make_not_null(&map), make_not_null(&time),\n      make_not_null(&functions_of_time), l_max, center, transition_func,\n      spherepack_coefs, random_00_coef, true, generator, l_max + 2);\n  const auto single_coords = map(target_data, time, functions_of_time);\n  const auto single_frame_velocity =\n      map.frame_velocity(target_data, time, functions_of_time);\n  const auto single_jacobian =\n      map.jacobian(target_data, time, functions_of_time);\n\n  std::array<DataVector, 3> combined_frame_velocity{};\n  tnsr::Ij<DataVector, 3, Frame::NoFrame> combined_jacobian{};\n\n  map.coords_frame_velocity_jacobian(\n      make_not_null(&target_data), make_not_null(&combined_frame_velocity),\n      make_not_null(&combined_jacobian), time, functions_of_time);\n\n  CHECK_ITERABLE_APPROX(target_data, single_coords);\n  CHECK_ITERABLE_APPROX(combined_frame_velocity, single_frame_velocity);\n  CHECK_ITERABLE_APPROX(combined_jacobian, single_jacobian);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.TimeDependent.Shape\",\n                  \"[Domain][Unit]\") {\n  domain::CoordinateMaps::ShapeMapTransitionFunctions::\n      register_derived_with_charm();\n  const CoordinateMaps::ShapeMapTransitionFunctions::SphereTransition\n      sphere_transition{1e-7, 100.};\n\n  MAKE_GENERATOR(generator);\n\n  test_inverse(make_not_null(&generator));\n\n  const size_t num_points = 1000;\n\n  for (const auto include_size : make_array(false, true)) {\n    CAPTURE(include_size);\n    {\n      INFO(\"Testing MapHelpers\");\n      for (size_t l_max = 2; l_max < 12; ++l_max) {\n        CAPTURE(l_max);\n        test_map_helpers(sphere_transition, l_max, include_size,\n                         make_not_null(&generator));\n      }\n    }\n    {\n      INFO(\"Testing analytical solution\");\n      for (size_t l_max = 2; l_max <= 3; ++l_max) {\n        CAPTURE(l_max);\n        test_analytical_solution(sphere_transition, l_max, include_size,\n                                 num_points, make_not_null(&generator));\n      }\n    }\n    {\n      INFO(\"Testing analytical gradient\");\n      const size_t l_max = 2;\n      test_analytical_jacobian(sphere_transition, l_max, include_size, 1000,\n                               make_not_null(&generator));\n    }\n    {\n      INFO(\"Testing combined call\");\n      for (size_t l_max = 2; l_max <= 3; ++l_max) {\n        CAPTURE(l_max);\n        test_combined_call(sphere_transition, l_max, num_points,\n                           make_not_null(&generator));\n      }\n    }\n  }\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_Skew.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <random>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Skew.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\nnamespace {\nconstexpr size_t deriv_order = 2;\nusing Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\nusing FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n\ntemplate <typename T>\nstd::array<T, 3> sph_to_cart(const T& radius, const T& theta, const T& phi) {\n  return std::array<T, 3>{radius * sin(theta) * cos(phi),\n                          radius * sin(theta) * sin(phi), radius * cos(theta)};\n}\n\ntemplate <typename Generator>\nvoid test(const gsl::not_null<Generator*> generator) {\n  const double initial_time = 0.5;\n  double t = initial_time + 0.05;\n  const double dt = 0.6;\n  const double expiration_time = 20.0;\n\n  const std::string function_of_time_name{\"Skew\"};\n\n  // NOLINTBEGIN\n  std::uniform_real_distribution<double> fot_dist{-0.1, 0.1};\n  std::uniform_real_distribution<double> outer_radius_dist{50.0, 150.0};\n  std::uniform_real_distribution<double> angle_dist{0.0, 2.0 * M_PI};\n  // NOLINTEND\n\n  std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n  f_of_t_list[function_of_time_name] = std::make_unique<Polynomial>(\n      initial_time,\n      std::array<DataVector, 3>{\n          make_with_random_values<DataVector>(\n              generator, make_not_null(&fot_dist), DataVector{2, 0.0}),\n          0.1 * make_with_random_values<DataVector>(\n                    generator, make_not_null(&fot_dist), DataVector{2, 0.0}),\n          DataVector{2, 0.0}},\n      expiration_time);\n\n  const double outer_radius = outer_radius_dist(*generator);\n  // Subtracting 1e-3 from outer radius ensures that the jacobian test helper\n  // only evaluates the map within the outer radius\n  std::uniform_real_distribution<double> radius_dist{0.0, outer_radius - 1.e-3};\n  const std::array<double, 3> center =\n      50.0 * std::array{fot_dist(*generator), fot_dist(*generator),\n                        fot_dist(*generator)};\n  CAPTURE(outer_radius);\n  CAPTURE(center);\n\n  const CoordinateMaps::TimeDependent::Skew skew_map{function_of_time_name,\n                                                     center, outer_radius};\n\n  CHECK(skew_map.function_of_time_names().contains(function_of_time_name));\n\n  // test serialized/deserialized map\n  const auto skew_map_deserialized = serialize_and_deserialize(skew_map);\n  CHECK(skew_map_deserialized.function_of_time_names().contains(\n      function_of_time_name));\n\n  const Approx deriv_approx = Approx::custom().epsilon(1.e-9).scale(1.0);\n  const Approx inv_approx = Approx::custom().epsilon(5.e-13).scale(1.0);\n\n  while (t < expiration_time) {\n    CAPTURE(t);\n    const auto func_and_derivs =\n        f_of_t_list.at(function_of_time_name)->func_and_2_derivs(t);\n    CAPTURE(func_and_derivs);\n\n    const std::array<double, 3> point_xi = [&]() {\n      const double radius = radius_dist(*generator);\n      const double theta = 0.5 * angle_dist(*generator);\n      const double phi = angle_dist(*generator);\n      return sph_to_cart(radius, theta, phi);\n    }();\n\n    const std::array<DataVector, 3> dv_point_xi = [&]() {\n      const DataVector for_size{10, 0.0};\n      const auto radii = make_with_random_values<DataVector>(\n          generator, make_not_null(&radius_dist), for_size);\n      const DataVector thetas =\n          0.5 * make_with_random_values<DataVector>(\n                    generator, make_not_null(&angle_dist), for_size);\n      const auto phis = make_with_random_values<DataVector>(\n          generator, make_not_null(&angle_dist), for_size);\n\n      return sph_to_cart(radii, thetas, phis);\n    }();\n\n    CAPTURE(dv_point_xi);\n\n    const auto run_checks = [&](const auto& points) {\n      test_jacobian(skew_map, points, t, f_of_t_list, deriv_approx);\n      test_inv_jacobian(skew_map, points, t, f_of_t_list);\n      test_frame_velocity(skew_map, points, t, f_of_t_list, deriv_approx);\n\n      test_jacobian(skew_map_deserialized, points, t, f_of_t_list,\n                    deriv_approx);\n      test_inv_jacobian(skew_map_deserialized, points, t, f_of_t_list);\n      test_frame_velocity(skew_map_deserialized, points, t, f_of_t_list,\n                          deriv_approx);\n    };\n\n    run_checks(point_xi);\n    test_coordinate_map_argument_types(skew_map, point_xi, t, f_of_t_list);\n    test_coordinate_map_argument_types(skew_map_deserialized, point_xi, t,\n                                       f_of_t_list);\n    test_inverse_map(skew_map, point_xi, t, f_of_t_list, inv_approx);\n    test_inverse_map(skew_map_deserialized, point_xi, t, f_of_t_list,\n                     inv_approx);\n    run_checks(dv_point_xi);\n\n    t += dt;\n  }\n\n  // Check inequivalence operator\n  CHECK_FALSE(skew_map != skew_map);\n  CHECK_FALSE(skew_map_deserialized != skew_map_deserialized);\n\n  // Check serialization\n  CHECK(skew_map == skew_map_deserialized);\n  CHECK_FALSE(skew_map != skew_map_deserialized);\n}\n\nvoid test_specific_points() {\n  const std::string function_of_time_name{\"Skew\"};\n  const double time = 0.0;\n  std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n  // Use pi/4 so math is easy\n  f_of_t_list[function_of_time_name] = std::make_unique<Polynomial>(\n      time,\n      std::array{DataVector{2, M_PI_4}, DataVector{2, 0.0}, DataVector{2, 0.0}},\n      std::numeric_limits<double>::infinity());\n\n  // Make it unit so math is easy\n  const std::array<double, 3> center{0.0, -1.0 / sqrt(2.0), 1.0 / sqrt(2.0)};\n  const double outer_radius = 100.0;\n\n  const CoordinateMaps::TimeDependent::Skew skew_map{function_of_time_name,\n                                                     center, outer_radius};\n\n  std::array<double, 3> test_point{};\n  {\n    INFO(\"Center\");\n    test_point = center;\n    CAPTURE(test_point);\n\n    const auto mapped_point = skew_map(test_point, time, f_of_t_list);\n    // Should be exact\n    CHECK(mapped_point == test_point);\n    const auto inverse_point =\n        skew_map.inverse(mapped_point, time, f_of_t_list);\n    CHECK(inverse_point.has_value());\n    CHECK_ITERABLE_APPROX(inverse_point.value(), test_point);\n\n    const auto jacobian = skew_map.jacobian(test_point, time, f_of_t_list);\n    auto expected_jacobian = identity<3>(0.0);\n    // Because the angles are pi/4\n    get<0, 1>(expected_jacobian) =\n        -0.5 * (1.0 + cos(M_PI / square(outer_radius)));\n    get<0, 2>(expected_jacobian) = get<0, 1>(expected_jacobian);\n    CHECK_ITERABLE_APPROX(jacobian, expected_jacobian);\n  }\n  {\n    INFO(\"Half way along y-axis\");\n    test_point = std::array{0.0, 0.5 * outer_radius, 0.0};\n    CAPTURE(test_point);\n\n    const auto mapped_point = skew_map(test_point, time, f_of_t_list);\n    const double falloff = 0.25 * (2.0 + sqrt(2.0));\n    const double tan_sum = -test_point[1];\n    // Should be exact\n    CHECK_ITERABLE_APPROX(\n        mapped_point,\n        (std::array{falloff * tan_sum, test_point[1], test_point[2]}));\n    const auto inverse_point =\n        skew_map.inverse(mapped_point, time, f_of_t_list);\n    CHECK(inverse_point.has_value());\n    CHECK_ITERABLE_APPROX(inverse_point.value(), test_point);\n\n    const auto jacobian = skew_map.jacobian(test_point, time, f_of_t_list);\n    auto expected_jacobian = identity<3>(0.0);\n    get<0, 2>(expected_jacobian) = -falloff;\n    get<0, 1>(expected_jacobian) =\n        -0.5 * M_PI * tan_sum / (sqrt(2.0) * outer_radius) +\n        get<0, 2>(expected_jacobian);\n    CHECK_ITERABLE_APPROX(jacobian, expected_jacobian);\n  }\n  {\n    INFO(\"Outer radius\");\n    test_point = std::array{0.0, 0.0, outer_radius};\n    CAPTURE(test_point);\n\n    const auto mapped_point = skew_map(test_point, time, f_of_t_list);\n    CHECK_ITERABLE_APPROX(mapped_point, test_point);\n    const auto inverse_point =\n        skew_map.inverse(mapped_point, time, f_of_t_list);\n    CHECK(inverse_point.has_value());\n    CHECK_ITERABLE_APPROX(inverse_point.value(), test_point);\n    const auto jacobian = skew_map.jacobian(test_point, time, f_of_t_list);\n    CHECK_ITERABLE_APPROX(jacobian, identity<3>(0.0));\n  }\n  {\n    INFO(\"Outer radius plus eps\");\n    const double eps = std::numeric_limits<double>::epsilon();\n    test_point = std::array{0.0, outer_radius + eps, 0.0};\n    CAPTURE(test_point);\n\n    const auto mapped_point = skew_map(test_point, time, f_of_t_list);\n    CHECK_ITERABLE_APPROX(mapped_point, test_point);\n    const auto inverse_point =\n        skew_map.inverse(mapped_point, time, f_of_t_list);\n    CHECK(inverse_point.has_value());\n    CHECK_ITERABLE_APPROX(inverse_point.value(), test_point);\n    const auto jacobian = skew_map.jacobian(test_point, time, f_of_t_list);\n    CHECK_ITERABLE_APPROX(jacobian, identity<3>(0.0));\n  }\n}\n\nvoid test_errors() {\n#ifdef SPECTRE_DEBUG\n  const std::string function_of_time_name{\"Skew\"};\n  const double time = 0.0;\n  std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n  // Use values close to -PI/2 to trigger the error\n  f_of_t_list[function_of_time_name] = std::make_unique<Polynomial>(\n      time,\n      std::array{DataVector{2, -M_PI_2 + 1.e-3}, DataVector{2, 0.0},\n                 DataVector{2, 0.0}},\n      std::numeric_limits<double>::infinity());\n\n  const double outer_radius = 100.0;\n\n  const CoordinateMaps::TimeDependent::Skew skew_map{\n      function_of_time_name, std::array{0.0, 0.0, 0.0}, outer_radius};\n\n  const std::array<double, 3> point{50.0, 20.0, 30.0};\n\n  CHECK_THROWS_WITH((skew_map(point, time, f_of_t_list)),\n                    Catch::Matchers::ContainsSubstring(\"Skew map is singular\"));\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.TimeDependent.Skew\",\n                  \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test(make_not_null(&generator));\n  test_specific_points();\n  test_errors();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_SphericalCompression.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <sstream>\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/SphericalCompression.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\n// Generate a point in a random direction at a given radius\nauto points_in_random_direction_generator(\n    const gsl::not_null<std::mt19937*> generator,\n    const std::array<double, 3>& center) {\n  std::uniform_real_distribution<> phi_dis(0, 2.0 * M_PI);\n  std::uniform_real_distribution<> theta_dis(0, M_PI);\n  const double theta{theta_dis(*generator)};\n  const double phi{phi_dis(*generator)};\n\n  // Choose a point of unit radius with the randomly selected theta, phi.\n  // This test will rescale this point to place it in different regions.\n  const double rho_x0{sin(theta) * cos(phi)};\n  const double rho_y0{sin(theta) * sin(phi)};\n  const double rho_z0{cos(theta)};\n\n  // A helper that returns a point given a radius\n  auto point = [rho_x0, rho_y0, rho_z0, center](const double& radius) {\n    return std::array<double, 3>{{radius * rho_x0 + center[0],\n                                  radius * rho_y0 + center[1],\n                                  radius * rho_z0 + center[2]}};\n  };\n  return point;\n}\n\n// Generates the map, time, and a FunctionOfTime\ntemplate <bool InteriorMap>\nvoid generate_map_time_and_f_of_time(\n    const gsl::not_null<domain::CoordinateMaps::TimeDependent::\n                            SphericalCompression<InteriorMap>*>\n        map,\n    const gsl::not_null<double*> time,\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        functions_of_time,\n    const gsl::not_null<double*> min_radius,\n    const gsl::not_null<double*> max_radius,\n    const gsl::not_null<std::array<double, 3>*> center,\n    const gsl::not_null<std::mt19937*> generator,\n    const bool bad_max_radius = false, const bool missing_f_of_t = false) {\n  std::string f_of_t_name{\"ExpansionFactor\"};\n  // Set up the map\n  if (missing_f_of_t) {\n    f_of_t_name = \"WrongFunctionOfTimeName\"s;\n  }\n  std::uniform_real_distribution<> rad_dis{0.4, 0.7};\n  std::uniform_real_distribution<> drad_dis{0.1, 0.2};\n  const double rad{rad_dis(*generator)};\n  *min_radius = rad - drad_dis(*generator);\n  *max_radius = rad + drad_dis(*generator);\n  if (bad_max_radius) {\n    *max_radius = *min_radius;\n  }\n  std::uniform_real_distribution<> center_dis{-0.05, 0.05};\n  *center = std::array<double, 3>{\n      {center_dis(*generator), center_dis(*generator), center_dis(*generator)}};\n  domain::CoordinateMaps::TimeDependent::SphericalCompression<InteriorMap>\n      random_map{f_of_t_name, *min_radius, *max_radius, *center};\n  *map = random_map;\n\n  const auto& names = map->function_of_time_names();\n  CHECK(names.size() == 1);\n  CHECK(names.count(f_of_t_name) == 1);\n\n  // Choose a random time for evaluating the FunctionOfTime\n  std::uniform_real_distribution<> time_dis{-1.0, 1.0};\n  *time = time_dis(*generator);\n\n  // Create a FunctionsOfTime containing a FunctionOfTime that, when evaluated\n  // at time, is invertible. Recall that the map is invertible if\n  // min_radius - max_radius < lambda(t) / sqrt(4*pi) < min_radius. So\n  // lambda(t) = (min_radius - eps * max_radius) * sqrt(4*pi) is guaranteed\n  // invertible for 0 < eps < 1. So set the constant term in the piecewise\n  // polynomial to a0 = (min_radius - eps * max_radius) * sqrt(4*pi).\n  // The remaining coefficients a1, a2, a3 will be chosen as a random\n  // small factor of a0, to ensure that these terms don't make the map\n  // non-invertible.\n  std::uniform_real_distribution<> eps_dis{0.2, 0.8};\n  std::uniform_real_distribution<> higher_coef_dis{-0.01, 0.01};\n  const double a0{(*min_radius - eps_dis(*generator) * *max_radius) /\n                  (0.25 * M_2_SQRTPI)};\n  const std::array<DataVector, 4> initial_coefficients{\n      {{{a0}},\n       {{higher_coef_dis(*generator) * a0}},\n       {{higher_coef_dis(*generator) * a0}},\n       {{higher_coef_dis(*generator) * a0}}}};\n  std::uniform_real_distribution<> dt_dis{0.1, 0.5};\n  const double initial_time{*time - dt_dis(*generator)};\n  const double expiration_time{*time + dt_dis(*generator)};\n  (*functions_of_time)[f_of_t_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<3>>(\n          initial_time, initial_coefficients, expiration_time);\n}\n\n// Generates the map, time, and a FunctionOfTime, but hides internal details\n// (min radius, max radius, and center) when not needed\ntemplate <bool InteriorMap>\nvoid generate_map_time_and_f_of_time(\n    const gsl::not_null<domain::CoordinateMaps::TimeDependent::\n                            SphericalCompression<InteriorMap>*>\n        map,\n    const gsl::not_null<double*> time,\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        functions_of_time,\n    const gsl::not_null<std::mt19937*> generator,\n    const bool bad_max_radius = false, const bool missing_f_of_t = false) {\n  double min_radius{std::numeric_limits<double>::signaling_NaN()};\n  double max_radius{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> center{};\n  generate_map_time_and_f_of_time<InteriorMap>(\n      map, time, functions_of_time, make_not_null(&min_radius),\n      make_not_null(&max_radius), make_not_null(&center), generator,\n      bad_max_radius, missing_f_of_t);\n}\n\nbool random_bool(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_int_distribution<> bool_dis{0, 1};\n  return static_cast<bool>(bool_dis(*generator));\n}\n\ntemplate <bool InteriorMap>\nvoid test_out_of_bounds(const gsl::not_null<std::mt19937*> generator) {\n  domain::CoordinateMaps::TimeDependent::SphericalCompression<InteriorMap>\n      map{};\n  double time{std::numeric_limits<double>::signaling_NaN()};\n  std::unordered_map<std::string,\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  double min_radius{std::numeric_limits<double>::signaling_NaN()};\n  double max_radius{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> center{};\n  generate_map_time_and_f_of_time(\n      make_not_null(&map), make_not_null(&time),\n      make_not_null(&functions_of_time), make_not_null(&min_radius),\n      make_not_null(&max_radius), make_not_null(&center), generator);\n\n  // A helper that returns a point given a radius\n  auto point = points_in_random_direction_generator(generator, center);\n\n  std::uniform_real_distribution<> rad_dis(\n      std::numeric_limits<double>::epsilon(),\n      1.0 - std::numeric_limits<double>::epsilon());\n  double radius = std::numeric_limits<double>::signaling_NaN();\n  if constexpr (InteriorMap) {\n    // Choose a radius that's out of bounds\n    radius = min_radius + rad_dis(*generator) * max_radius +\n             std::numeric_limits<double>::epsilon();\n  } else {\n    // Randomly choose on which side the radius is out of bounds\n    if (random_bool(generator)) {\n      radius = max_radius + rad_dis(*generator) * min_radius +\n               std::numeric_limits<double>::epsilon();\n    } else {\n      radius = rad_dis(*generator) * min_radius -\n               std::numeric_limits<double>::epsilon();\n    }\n  }\n  map(point(radius), time, functions_of_time);\n  std::stringstream message;\n  message << \"radius should have been out of bounds, but \"\n          << \"radius == \" << radius << \", min_radius == \" << min_radius\n          << \", max_radius == \" << max_radius\n          << \", InteriorMap == \" << InteriorMap;\n  ERROR(message.str());\n}\n\ntemplate <bool InteriorMap>\nvoid test_out_of_bounds_inverse(const gsl::not_null<std::mt19937*> generator) {\n  domain::CoordinateMaps::TimeDependent::SphericalCompression<InteriorMap>\n      map{};\n  double time{std::numeric_limits<double>::signaling_NaN()};\n  std::unordered_map<std::string,\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  double min_radius{std::numeric_limits<double>::signaling_NaN()};\n  double max_radius{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> center{};\n  generate_map_time_and_f_of_time(\n      make_not_null(&map), make_not_null(&time),\n      make_not_null(&functions_of_time), make_not_null(&min_radius),\n      make_not_null(&max_radius), make_not_null(&center), generator);\n\n  auto point = points_in_random_direction_generator(generator, center);\n\n  // Get lambda_y needed to determine the bounds for the target radius\n  const std::string f_of_t_name{\"ExpansionFactor\"};\n  const double lambda_y{functions_of_time.at(f_of_t_name)->func(time)[0][0] *\n                        0.25 * M_2_SQRTPI};\n\n  std::uniform_real_distribution<> rad_dis(\n      std::numeric_limits<double>::epsilon(),\n      1.0 - std::numeric_limits<double>::epsilon());\n  double target_radius = std::numeric_limits<double>::signaling_NaN();\n  if constexpr (InteriorMap) {\n    // Choose a radius that's out of bounds\n    target_radius = min_radius - lambda_y + rad_dis(*generator) * max_radius +\n                    std::numeric_limits<double>::epsilon();\n  } else {\n    // Randomly choose on which side the radius is out of bounds\n    if (random_bool(generator)) {\n      target_radius = max_radius + rad_dis(*generator) * min_radius +\n                      std::numeric_limits<double>::epsilon();\n    } else {\n      target_radius = (min_radius - lambda_y) * (1.0 - rad_dis(*generator)) -\n                      std::numeric_limits<double>::epsilon();\n    }\n  }\n  CHECK_FALSE(\n      map.inverse(point(target_radius), time, functions_of_time).has_value());\n}\n}  // namespace\n\nnamespace domain {\nnamespace {\ntemplate <bool InteriorMap>\nvoid test_suite(const gsl::not_null<std::mt19937*> generator) {\n  INFO(\"Suite\");\n\n  // Create the map, select a time, and create a FunctionsOfTime\n  CoordinateMaps::TimeDependent::SphericalCompression<InteriorMap> map{};\n  double time{std::numeric_limits<double>::signaling_NaN()};\n  std::unordered_map<std::string,\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  double min_radius{std::numeric_limits<double>::signaling_NaN()};\n  double max_radius{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> center{};\n  generate_map_time_and_f_of_time(\n      make_not_null(&map), make_not_null(&time),\n      make_not_null(&functions_of_time), make_not_null(&min_radius),\n      make_not_null(&max_radius), make_not_null(&center), generator);\n  CAPTURE(time);\n  CAPTURE(min_radius);\n  CAPTURE(max_radius);\n\n  // Check map against a suite of functions in\n  // tests/Unit/Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\n  std::uniform_real_distribution<> theta_dis(0, M_PI);\n  std::uniform_real_distribution<> phi_dis(0, 2.0 * M_PI);\n\n  const double theta = theta_dis(*generator);\n  CAPTURE(theta);\n  const double phi = phi_dis(*generator);\n  CAPTURE(phi);\n\n  // The Jacobian test computes finite-difference derivatives using a distance\n  // of 1.e-4, so keep all test points much farther away from the boundaries\n  // and the origin than that. Also ensure all points are within some finite\n  // maximum radius, which here is taken to be ten times the max radius.\n  constexpr double distance_from_boundaries{5.e-3};\n  double lower_rad = min_radius + distance_from_boundaries;\n  double upper_rad = max_radius - distance_from_boundaries;\n  if constexpr (InteriorMap) {\n    lower_rad = distance_from_boundaries;\n    upper_rad = min_radius - distance_from_boundaries;\n  }\n  std::uniform_real_distribution<> radius_dis(lower_rad, upper_rad);\n\n  const double radius = radius_dis(*generator);\n  CAPTURE(radius);\n\n  // Select a random point. Make sure that the point is not too close to the\n  // junctions where the Jacobian becomes discontinuous, to ensure that the\n  // jacobian test passes\n  const std::array<double, 3> random_point{\n      {radius * sin(theta) * cos(phi) + center[0],\n       radius * sin(theta) * sin(phi) + center[1],\n       radius * cos(theta) + center[2]}};\n\n  const auto test_helper = [&random_point, &time,\n                            &functions_of_time](const auto& map_to_test) {\n    test_serialization(map_to_test);\n    CHECK_FALSE(map_to_test != map_to_test);\n\n    test_coordinate_map_argument_types(map_to_test, random_point, time,\n                                       functions_of_time);\n    test_frame_velocity(map_to_test, random_point, time, functions_of_time);\n    test_jacobian(map_to_test, random_point, time, functions_of_time);\n    test_inv_jacobian(map_to_test, random_point, time, functions_of_time);\n    test_inverse_map(map_to_test, random_point, time, functions_of_time);\n  };\n  test_helper(map);\n\n  const auto check_map_equality = [&random_point, &time, &functions_of_time](\n                                      const auto& map_one,\n                                      const auto& map_two) {\n    tnsr::I<double, 3, Frame::BlockLogical> source_point{};\n    for (size_t i = 0; i < 3; ++i) {\n      source_point.get(i) = gsl::at(random_point, i);\n    }\n    CHECK_ITERABLE_APPROX(map_one(source_point, time, functions_of_time),\n                          map_two(source_point, time, functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        map_one.jacobian(source_point, time, functions_of_time),\n        map_two.jacobian(source_point, time, functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        map_one.inv_jacobian(source_point, time, functions_of_time),\n        map_two.inv_jacobian(source_point, time, functions_of_time));\n  };\n  const auto map2 = serialize_and_deserialize(map);\n  check_map_equality(\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map),\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map2));\n  test_helper(map2);\n}\n\ntemplate <bool InteriorMap>\nvoid test_map(const gsl::not_null<std::mt19937*> generator) {\n  INFO(\"Map\");\n  // Create a map, choose a time, and create a FunctionOfTime.\n  // Set up the map\n  CoordinateMaps::TimeDependent::SphericalCompression<InteriorMap> map{};\n  double time{std::numeric_limits<double>::signaling_NaN()};\n  std::unordered_map<std::string,\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  double min_radius{std::numeric_limits<double>::signaling_NaN()};\n  double max_radius{std::numeric_limits<double>::signaling_NaN()};\n  std::array<double, 3> center{std::numeric_limits<double>::signaling_NaN()};\n  generate_map_time_and_f_of_time(\n      make_not_null(&map), make_not_null(&time),\n      make_not_null(&functions_of_time), make_not_null(&min_radius),\n      make_not_null(&max_radius), make_not_null(&center), generator);\n\n  auto point = points_in_random_direction_generator(generator, center);\n\n  // A helper that checks if two points are scaled by the same factor\n  // or not\n  auto check_point_scale_factors =\n      [&center, &map, &time, &functions_of_time](\n          const std::array<double, 3>& orig_point_1,\n          const std::array<double, 3>& orig_point_2,\n          const bool check_if_equal) {\n        const std::array<double, 3> mapped_point_1{\n            map(orig_point_1, time, functions_of_time)};\n        const std::array<double, 3> mapped_point_2{\n            map(orig_point_2, time, functions_of_time)};\n        Approx custom_approx = Approx::custom().epsilon(1.0e-9).scale(1.0);\n        auto scale_factor_component =\n            [&center](const std::array<double, 3>& orig_point,\n                      const std::array<double, 3>& mapped_point,\n                      const size_t i) {\n              return (gsl::at(orig_point, i) - gsl::at(mapped_point, i)) /\n                     (gsl::at(orig_point, i) - gsl::at(center, i));\n            };\n        for (size_t i = 0; i < 3; ++i) {\n          if (check_if_equal) {\n            CHECK(scale_factor_component(orig_point_1, mapped_point_1, i) ==\n                  custom_approx(\n                      scale_factor_component(orig_point_2, mapped_point_2, i)));\n          } else {\n            CHECK(scale_factor_component(orig_point_1, mapped_point_1, i) !=\n                  custom_approx(\n                      scale_factor_component(orig_point_2, mapped_point_2, i)));\n          }\n        }\n      };\n\n  // Set up distributions for choosing radii in the three regions:\n  // smaller than min_radius, larger than max_radius, and in between\n  constexpr double eps{0.01};\n\n  double lower_radius = min_radius + eps;\n  double upper_radius = max_radius - eps;\n  if constexpr (InteriorMap) {\n    lower_radius = eps;\n    upper_radius = min_radius - eps;\n  }\n  std::uniform_real_distribution<> radius_dis{lower_radius, upper_radius};\n  if constexpr (InteriorMap) {\n    // In the interior, two random points should change radius by the same\n    // factor, and that factor is the same as for a point exactly at\n    // the minimum radius\n    check_point_scale_factors(point(radius_dis(*generator)),\n                              point(radius_dis(*generator)), true);\n    check_point_scale_factors(point(radius_dis(*generator)), point(min_radius),\n                              true);\n  } else {\n    // In the middle, two points should only move radially, but by\n    // a different amount\n    check_point_scale_factors(point(radius_dis(*generator)),\n                              point(radius_dis(*generator)), false);\n  }\n\n  // Check that a point is invertible with the supplied functions_of_time\n  CHECK(map.inverse(map(point(radius_dis(*generator)), time, functions_of_time),\n                    time, functions_of_time)\n            .has_value());\n\n  // Check that a point is not invertible if the function of time is\n  // too big or too small\n  std::uniform_real_distribution<> higher_coef_dis{-0.01, 0.01};\n  const double a0{(min_radius - max_radius - 0.5) / (0.25 * M_2_SQRTPI)};\n  const std::array<DataVector, 4> initial_coefficients{\n      {{{a0}},\n       {{higher_coef_dis(*generator) * a0}},\n       {{higher_coef_dis(*generator) * a0}},\n       {{higher_coef_dis(*generator) * a0}}}};\n  const double b0{(min_radius + 0.5) / (0.25 * M_2_SQRTPI)};\n  const std::array<DataVector, 4> initial_coefficients_b{\n      {{{b0}},\n       {{higher_coef_dis(*generator) * b0}},\n       {{higher_coef_dis(*generator) * b0}},\n       {{higher_coef_dis(*generator) * b0}}}};\n  std::uniform_real_distribution<> dt_dis{0.1, 0.5};\n  const double initial_time{time - dt_dis(*generator)};\n  const double expiration_time{time + dt_dis(*generator)};\n\n  const std::string f_of_t_name{\"ExpansionFactor\"};\n  functions_of_time[f_of_t_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<3>>(\n          initial_time, initial_coefficients, expiration_time);\n  CHECK_FALSE(\n      map.inverse(map(point(radius_dis(*generator)), time, functions_of_time),\n                  time, functions_of_time)\n          .has_value());\n  functions_of_time[f_of_t_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<3>>(\n          initial_time, initial_coefficients_b, expiration_time);\n  CHECK_FALSE(\n      map.inverse(map(point(radius_dis(*generator)), time, functions_of_time),\n                  time, functions_of_time)\n          .has_value());\n}\n\ntemplate <bool InteriorMap>\nvoid test_is_identity(const gsl::not_null<std::mt19937*> generator) {\n  INFO(\"Is identity\");\n  CoordinateMaps::TimeDependent::SphericalCompression<InteriorMap> map{};\n  double time{std::numeric_limits<double>::signaling_NaN()};\n  std::unordered_map<std::string,\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  generate_map_time_and_f_of_time(make_not_null(&map), make_not_null(&time),\n                                  make_not_null(&functions_of_time), generator);\n  CHECK(not map.is_identity());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.SphericalCompression\",\n                  \"[Domain][Unit]\") {\n  MAKE_GENERATOR(gen);\n  test_suite<true>(make_not_null(&gen));\n  test_map<true>(make_not_null(&gen));\n  test_is_identity<true>(make_not_null(&gen));\n  test_out_of_bounds_inverse<true>(make_not_null(&gen));\n  test_suite<false>(make_not_null(&gen));\n  test_map<false>(make_not_null(&gen));\n  test_is_identity<false>(make_not_null(&gen));\n  test_out_of_bounds_inverse<false>(make_not_null(&gen));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([&gen]() {\n        CoordinateMaps::TimeDependent::SphericalCompression<false> map{};\n        double time{std::numeric_limits<double>::signaling_NaN()};\n        std::unordered_map<\n            std::string,\n            std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n            functions_of_time{};\n        generate_map_time_and_f_of_time(make_not_null(&map),\n                                        make_not_null(&time),\n                                        make_not_null(&functions_of_time),\n                                        make_not_null(&gen), true, false);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"max_radius must be greater\"));\n#endif\n\n  CHECK_THROWS_WITH(\n      ([&gen]() {\n        if (random_bool(make_not_null(&gen))) {\n          test_out_of_bounds<true>(make_not_null(&gen));\n        } else {\n          test_out_of_bounds<false>(make_not_null(&gen));\n        }\n      }()),\n      Catch::Matchers::ContainsSubstring(\"not in expected range\"));\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/CoordinateMaps/TimeDependent/Test_Translation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace domain {\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_translation() {\n  MAKE_GENERATOR(gen);\n  // define vars for FunctionOfTime::PiecewisePolynomial f(t) = t**2.\n  double t = -1.0;\n  const double dt = 0.6;\n  const double final_time = 4.0;\n  constexpr size_t deriv_order = 3;\n  const double amplitude = 1.0;\n  const double width = 100.0;\n  const double inner_radius = 1.0;\n  const double outer_radius = 40.0;\n  std::array<double, 1> gauss_center{0.};\n  const MathFunctions::Gaussian<1, Frame::Inertial> gaussian{amplitude, width,\n                                                             gauss_center};\n\n  const std::array<DataVector, deriv_order + 1> init_func{\n      {{Dim, 1.0}, {Dim, -2.0}, {Dim, 2.0}, {Dim, 0.0}}};\n\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n  std::unordered_map<std::string, FoftPtr> f_of_t_list{};\n  f_of_t_list[\"translation\"] =\n      std::make_unique<Polynomial>(t, init_func, final_time + dt);\n\n  const FoftPtr& f_of_t = f_of_t_list.at(\"translation\");\n\n  UniformCustomDistribution<double> dist_double{-5.0, 5.0};\n  UniformCustomDistribution<double> far_dist_double{40.0, 50.0};\n  std::array<double, Dim> point_xi{};\n  std::array<DataVector, Dim> point_xi_dv{};\n  std::array<double, Dim> center{};\n  std::array<double, Dim> far_point_xi{};\n  fill_with_random_values(make_not_null(&point_xi), make_not_null(&gen),\n                          make_not_null(&dist_double));\n  fill_with_random_values(make_not_null(&center), make_not_null(&gen),\n                          make_not_null(&dist_double));\n  fill_with_random_values(make_not_null(&far_point_xi), make_not_null(&gen),\n                          make_not_null(&far_dist_double));\n  for (size_t i = 0; i < Dim; i++) {\n    auto points = make_with_random_values<DataVector>(\n        make_not_null(&gen), make_not_null(&dist_double), DataVector(5));\n    gsl::at(point_xi_dv, i) = points;\n  }\n  const CoordinateMaps::TimeDependent::Translation<Dim> translation_map{\n      \"translation\"};\n  const CoordinateMaps::TimeDependent::Translation<Dim>\n      piecewise_translation_map{\"translation\", inner_radius, outer_radius};\n  const CoordinateMaps::TimeDependent::Translation<Dim> radial_translation_map{\n      \"translation\",\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(\n          amplitude, width, gauss_center),\n      center};\n  const auto check_names = [](const auto& names) {\n    CHECK(names.size() == 1);\n    CHECK(names.count(\"translation\") == 1);\n  };\n  check_names(translation_map.function_of_time_names());\n  check_names(radial_translation_map.function_of_time_names());\n  // test serialized/deserialized map\n  MathFunctions::register_derived_with_charm();\n  const auto translation_map_deserialized =\n      serialize_and_deserialize(translation_map);\n  const auto radial_translation_map_deserialized =\n      serialize_and_deserialize(radial_translation_map);\n  const auto piecewise_translation_map_deserialized =\n      serialize_and_deserialize(piecewise_translation_map);\n  check_names(translation_map_deserialized.function_of_time_names());\n  check_names(radial_translation_map_deserialized.function_of_time_names());\n\n  while (t < final_time) {\n    std::array<double, Dim> translation{};\n    std::array<double, Dim> distance_to_center{};\n    double radius = 0;\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(translation, i) = square(t);\n      distance_to_center[i] = point_xi[i] - center[i];\n      radius += square(distance_to_center[i]);\n    }\n    radius = sqrt(radius);\n    const double piecewise_radius = magnitude(point_xi);\n    const DataVector piecewise_radius_dv = magnitude(point_xi_dv);\n    const double far_piecewise_radius = magnitude(far_point_xi);\n    const double radial_falloff_factor =\n        (outer_radius - piecewise_radius) / (outer_radius - inner_radius);\n    std::array<double, Dim> frame_vel{};\n    std::array<double, Dim> radial_frame_vel{};\n    std::array<double, Dim> inspiral_frame_vel{};\n    std::array<double, Dim> far_inspiral_frame_vel{0.};\n    for (size_t i = 0; i < Dim; i++) {\n      gsl::at(frame_vel, i) = f_of_t->func_and_deriv(t)[1][i];\n      gsl::at(radial_frame_vel, i) =\n          f_of_t->func_and_deriv(t)[1][i] * gaussian(radius);\n      if (piecewise_radius <= inner_radius) {\n        gsl::at(inspiral_frame_vel, i) = f_of_t->func_and_deriv(t)[1][i];\n      } else {\n        gsl::at(inspiral_frame_vel, i) =\n            f_of_t->func_and_deriv(t)[1][i] * radial_falloff_factor;\n      }\n    }\n    Approx custom_approx = Approx::custom().epsilon(1.e-9);\n    CHECK_ITERABLE_APPROX(translation_map(point_xi, t, f_of_t_list),\n                          point_xi + translation);\n    if (piecewise_radius <= inner_radius) {\n      CHECK_ITERABLE_APPROX(piecewise_translation_map(point_xi, t, f_of_t_list),\n                            point_xi + translation);\n      CHECK_ITERABLE_APPROX(piecewise_translation_map\n                                .inverse(point_xi + translation, t, f_of_t_list)\n                                .value(),\n                            point_xi);\n      CHECK_ITERABLE_APPROX(\n          piecewise_translation_map_deserialized(point_xi, t, f_of_t_list),\n          point_xi + translation);\n      CHECK_ITERABLE_APPROX(piecewise_translation_map_deserialized\n                                .inverse(point_xi + translation, t, f_of_t_list)\n                                .value(),\n                            point_xi);\n    } else if (piecewise_radius > inner_radius and\n               piecewise_radius < outer_radius) {\n      CHECK_ITERABLE_APPROX(piecewise_translation_map(point_xi, t, f_of_t_list),\n                            point_xi + translation * radial_falloff_factor);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          piecewise_translation_map\n              .inverse(point_xi + translation * radial_falloff_factor, t,\n                       f_of_t_list)\n              .value(),\n          point_xi, custom_approx);\n      CHECK_ITERABLE_APPROX(\n          piecewise_translation_map_deserialized(point_xi, t, f_of_t_list),\n          point_xi + translation * radial_falloff_factor);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          piecewise_translation_map_deserialized\n              .inverse(point_xi + translation * radial_falloff_factor, t,\n                       f_of_t_list)\n              .value(),\n          point_xi, custom_approx);\n    }\n\n    CHECK_ITERABLE_APPROX(\n        piecewise_translation_map(far_point_xi, t, f_of_t_list), far_point_xi);\n    CHECK_ITERABLE_APPROX(\n        piecewise_translation_map.inverse(far_point_xi, t, f_of_t_list).value(),\n        far_point_xi);\n    CHECK_ITERABLE_APPROX(\n        piecewise_translation_map_deserialized(far_point_xi, t, f_of_t_list),\n        far_point_xi);\n    CHECK_ITERABLE_APPROX(piecewise_translation_map_deserialized\n                              .inverse(far_point_xi, t, f_of_t_list)\n                              .value(),\n                          far_point_xi);\n    CHECK_ITERABLE_APPROX(radial_translation_map(point_xi, t, f_of_t_list),\n                          point_xi + (translation * gaussian(radius)));\n    CHECK_ITERABLE_APPROX(\n        translation_map.inverse(point_xi + translation, t, f_of_t_list).value(),\n        point_xi);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        radial_translation_map\n            .inverse(point_xi + (translation * gaussian(radius)), t,\n                     f_of_t_list)\n            .value(),\n        point_xi, custom_approx);\n    CHECK_ITERABLE_APPROX(\n        translation_map.frame_velocity(point_xi, t, f_of_t_list), frame_vel);\n    CHECK_ITERABLE_APPROX(\n        radial_translation_map.frame_velocity(point_xi, t, f_of_t_list),\n        radial_frame_vel);\n    CHECK_ITERABLE_APPROX(\n        piecewise_translation_map.frame_velocity(point_xi, t, f_of_t_list),\n        inspiral_frame_vel);\n    CHECK_ITERABLE_APPROX(\n        piecewise_translation_map.frame_velocity(far_point_xi, t, f_of_t_list),\n        far_inspiral_frame_vel);\n    CHECK_ITERABLE_APPROX(piecewise_translation_map_deserialized.frame_velocity(\n                              point_xi, t, f_of_t_list),\n                          inspiral_frame_vel);\n    CHECK_ITERABLE_APPROX(piecewise_translation_map_deserialized.frame_velocity(\n                              far_point_xi, t, f_of_t_list),\n                          far_inspiral_frame_vel);\n    CHECK_ITERABLE_APPROX(\n        radial_translation_map_deserialized(point_xi, t, f_of_t_list),\n        point_xi + (translation * gaussian(radius)));\n    CHECK_ITERABLE_APPROX(translation_map_deserialized\n                              .inverse(point_xi + translation, t, f_of_t_list)\n                              .value(),\n                          point_xi);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        radial_translation_map_deserialized\n            .inverse(point_xi + (translation * gaussian(radius)), t,\n                     f_of_t_list)\n            .value(),\n        point_xi, custom_approx);\n    CHECK_ITERABLE_APPROX(\n        translation_map_deserialized.frame_velocity(point_xi, t, f_of_t_list),\n        frame_vel);\n    CHECK_ITERABLE_APPROX(radial_translation_map_deserialized.frame_velocity(\n                              point_xi, t, f_of_t_list),\n                          radial_frame_vel);\n\n    test_jacobian(translation_map, point_xi, t, f_of_t_list);\n    test_inv_jacobian(translation_map, point_xi, t, f_of_t_list);\n    test_jacobian(translation_map_deserialized, point_xi, t, f_of_t_list);\n    test_inv_jacobian(translation_map_deserialized, point_xi, t, f_of_t_list);\n    test_jacobian(radial_translation_map, point_xi, t, f_of_t_list);\n    test_inv_jacobian(radial_translation_map, point_xi, t, f_of_t_list);\n    test_jacobian(radial_translation_map_deserialized, point_xi, t,\n                  f_of_t_list);\n    test_inv_jacobian(radial_translation_map_deserialized, point_xi, t,\n                      f_of_t_list);\n    // This is for an edge case numerical derivative function used in the\n    // jacobian, it calls the operator with tiny steps forward and backward on\n    // the order of x1 = x + dx, x2 = x1 + dx and x3 = x2 + dx where dx = 1e-4\n    // so it moves the point_xi up to 3e-4 forward and backward and calls the\n    // operator with each different value. When it does that, it can cross the\n    // inner radius where the translation is different causing the numerical\n    // jacobian to be computed in the wrong region. However, when it calls the\n    // jacobian, it still uses the original point_xi so there's a mixup between\n    // which region it's in.\n    if (piecewise_radius <= inner_radius * .99 or\n        piecewise_radius >= inner_radius * 1.01) {\n      test_jacobian(piecewise_translation_map, point_xi, t, f_of_t_list);\n      test_inv_jacobian(piecewise_translation_map, point_xi, t, f_of_t_list);\n      test_jacobian(piecewise_translation_map_deserialized, point_xi, t,\n                    f_of_t_list);\n      test_inv_jacobian(piecewise_translation_map_deserialized, point_xi, t,\n                        f_of_t_list);\n    }\n    // Checking if any radius in the datavector is within the range above.\n    bool jac_flag = true;\n    for (size_t i = 0; i < gsl::at(point_xi_dv, 0).size(); i++) {\n      if (gsl::at(piecewise_radius_dv, i) > inner_radius * .99 and\n          gsl::at(piecewise_radius_dv, i) < inner_radius * 1.01) {\n        jac_flag = false;\n      }\n    }\n    if (jac_flag) {\n      test_jacobian(piecewise_translation_map, point_xi_dv, t, f_of_t_list);\n      test_inv_jacobian(piecewise_translation_map, point_xi_dv, t, f_of_t_list);\n      test_jacobian(piecewise_translation_map_deserialized, point_xi_dv, t,\n                    f_of_t_list);\n      test_inv_jacobian(piecewise_translation_map_deserialized, point_xi_dv, t,\n                        f_of_t_list);\n    }\n    // Same issue as above, but for the outer boundary.\n    if (far_piecewise_radius >= outer_radius * 1.01) {\n      test_jacobian(piecewise_translation_map, far_point_xi, t, f_of_t_list);\n      test_inv_jacobian(piecewise_translation_map, far_point_xi, t,\n                        f_of_t_list);\n      test_jacobian(piecewise_translation_map_deserialized, far_point_xi, t,\n                    f_of_t_list);\n      test_inv_jacobian(piecewise_translation_map_deserialized, far_point_xi, t,\n                        f_of_t_list);\n    }\n    t += dt;\n  }\n\n  // Check inequivalence operator\n  CHECK_FALSE(translation_map != translation_map);\n  CHECK_FALSE(translation_map_deserialized != translation_map_deserialized);\n  CHECK_FALSE(radial_translation_map != radial_translation_map);\n  CHECK_FALSE(radial_translation_map_deserialized !=\n              radial_translation_map_deserialized);\n  CHECK_FALSE(piecewise_translation_map != piecewise_translation_map);\n  CHECK_FALSE(piecewise_translation_map_deserialized !=\n              piecewise_translation_map_deserialized);\n\n  // Check serialization\n  CHECK(translation_map == translation_map_deserialized);\n  CHECK_FALSE(translation_map != translation_map_deserialized);\n\n  test_coordinate_map_argument_types(translation_map, point_xi, t, f_of_t_list);\n  CHECK(radial_translation_map == radial_translation_map_deserialized);\n  CHECK_FALSE(radial_translation_map != radial_translation_map_deserialized);\n\n  test_coordinate_map_argument_types(radial_translation_map, point_xi, t,\n                                     f_of_t_list);\n  CHECK(piecewise_translation_map == piecewise_translation_map_deserialized);\n  CHECK_FALSE(piecewise_translation_map !=\n              piecewise_translation_map_deserialized);\n  test_coordinate_map_argument_types(piecewise_translation_map, point_xi, t,\n                                     f_of_t_list);\n  CHECK(not CoordinateMaps::TimeDependent::Translation<Dim>{}.is_identity());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinateMaps.TimeDependent.Translation\",\n                  \"[Domain][Unit]\") {\n  test_translation<1>();\n  test_translation<2>();\n  test_translation<3>();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DomainCreators\")\n\nset(LIBRARY_SOURCES\n  Test_AlignedLattice.cpp\n  Test_AngularCylinder.cpp\n  Test_AngularDisk.cpp\n  Test_BinaryCompactObject.cpp\n  Test_Brick.cpp\n  Test_CartoonCylinder.cpp\n  Test_CartoonSphere1D.cpp\n  Test_CartoonSphere2D.cpp\n  Test_Cylinder.cpp\n  Test_CylindricalBinaryCompactObject.cpp\n  Test_Disk.cpp\n  Test_ExpandOverBlocks.cpp\n  Test_FrustalCloak.cpp\n  Test_Interval.cpp\n  Test_NonconformingSphericalShells.cpp\n  Test_Rectangle.cpp\n  Test_RotatedBricks.cpp\n  Test_RotatedIntervals.cpp\n  Test_RotatedRectangles.cpp\n  Test_Sphere.cpp\n  Test_SphericalShells.cpp\n  Test_Tags.cpp\n  )\n\nadd_subdirectory(TimeDependentOptions)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Domain\n  DomainBoundaryConditions\n  DomainBoundaryConditionsHelpers\n  DomainCreators\n  DomainHelpers\n  GeneralRelativity\n  H5\n  Informer\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  )\n\nadd_subdirectory(Python)\nadd_subdirectory(TimeDependence)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.Python.BinaryCompactObject\"\n  Test_BinaryCompactObject.py\n  \"Unit;Domain\"\n  PyDomainCreators\n)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.Python.Brick\"\n  Test_Brick.py\n  \"Unit;Domain\"\n  PyDomainCreators)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.Python.Cylinder\"\n  Test_Cylinder.py\n  \"Unit;Domain\"\n  PyDomainCreators)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.Python.Interval\"\n  Test_Interval.py\n  \"Unit;Domain\"\n  PyDomainCreators)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.Python.Rectangle\"\n  Test_Rectangle.py\n  \"Unit;Domain\"\n  PyDomainCreators)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.Python.Sphere\"\n  Test_Sphere.py\n  \"Unit;Domain\"\n  PyDomainCreators)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Python/Test_BinaryCompactObject.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators import BinaryCompactObject, DomainCreator3D\nfrom spectre.Domain.Creators.TimeDependentOptions import (\n    BinaryCompactObjectTimeDependentOptions,\n    RotationMapOptions,\n)\n\n\nclass TestBinaryCompactObject(unittest.TestCase):\n    def test_construction(self):\n        binary_compact_object_no_time_dependence = BinaryCompactObject(\n            inner_radius_a=0.5,\n            outer_radius_a=2.0,\n            x_coord_a=5.0,\n            excise_a=True,\n            use_logarithmic_map_a=True,\n            inner_radius_b=0.5,\n            outer_radius_b=2.0,\n            x_coord_b=-5.0,\n            excise_b=True,\n            use_logarithmic_map_b=True,\n            center_of_mass_offset=[0.1, 0.2],\n            envelope_radius=50.0,\n            outer_radius=600.0,\n            cube_scale=1.2,\n            initial_refinement=1,\n            initial_number_of_grid_points=5,\n            use_equiangular_map=True,\n            radial_partitioning_outer_shell=[],\n            opening_angle_in_degrees=120.0,\n            time_dependent_options=None,\n        )\n        rotation_map = RotationMapOptions([[0.0, 0.0, 0.0, 1.0]], 100.0)\n        bco_time_dependent_options = BinaryCompactObjectTimeDependentOptions(\n            0.0,\n            None,\n            rotation_map,\n            None,\n            None,\n            None,\n            None,\n            None,\n        )\n        binary_compact_object_time_dependence = BinaryCompactObject(\n            inner_radius_a=0.5,\n            outer_radius_a=2.0,\n            x_coord_a=5.0,\n            excise_a=True,\n            use_logarithmic_map_a=True,\n            inner_radius_b=0.5,\n            outer_radius_b=2.0,\n            x_coord_b=-5.0,\n            excise_b=True,\n            use_logarithmic_map_b=True,\n            center_of_mass_offset=[0.1, 0.2],\n            envelope_radius=50.0,\n            outer_radius=600.0,\n            cube_scale=1.2,\n            initial_refinement=1,\n            initial_number_of_grid_points=5,\n            use_equiangular_map=True,\n            radial_partitioning_outer_shell=[],\n            opening_angle_in_degrees=120.0,\n            time_dependent_options=bco_time_dependent_options,\n        )\n        self.assertIsInstance(\n            binary_compact_object_no_time_dependence, DomainCreator3D\n        )\n        self.assertIsInstance(\n            binary_compact_object_time_dependence, DomainCreator3D\n        )\n        self.assertNotEqual(\n            binary_compact_object_time_dependence,\n            binary_compact_object_no_time_dependence,\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Python/Test_Brick.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators import Brick\n\n\nclass TestBrick(unittest.TestCase):\n    def test_construction(self):\n        brick = Brick(\n            lower_bounds=[1.0, 0.0, 2.0],\n            upper_bounds=[2.0, 1.0, 3.0],\n            is_periodic=[False, False, False],\n            initial_refinement_levels=[1, 0, 1],\n            initial_num_points=[3, 4, 2],\n        )\n        domain = brick.create_domain()\n        self.assertFalse(domain.is_time_dependent())\n        self.assertEqual(brick.block_names(), [\"Brick\"])\n        self.assertEqual(brick.block_groups(), {\"Brick\": {\"Brick\"}})\n        self.assertEqual(brick.initial_extents(), [[3, 4, 2]])\n        self.assertEqual(brick.initial_refinement_levels(), [[1, 0, 1]])\n        self.assertEqual(brick.functions_of_time(), {})\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Python/Test_Cylinder.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators import Cylinder, DomainCreator3D\n\n\nclass TestCylinder(unittest.TestCase):\n    def test_construction(self):\n        cylinder = Cylinder(\n            inner_radius=1.0,\n            outer_radius=2.0,\n            lower_bound=0.0,\n            upper_bound=1.0,\n            is_periodic_in_z=False,\n            initial_refinement=1,\n            initial_number_of_grid_points=[3, 4, 5],\n            use_equiangular_map=False,\n        )\n        self.assertIsInstance(cylinder, DomainCreator3D)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Python/Test_Interval.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.CoordinateMaps import Distribution\nfrom spectre.Domain.Creators import DomainCreator1D, Interval\n\n\nclass TestInterval(unittest.TestCase):\n    def test_construction(self):\n        interval = Interval(\n            lower_bounds=[1.0],\n            upper_bounds=[2.0],\n            is_periodic=[False],\n            initial_refinement_levels=[1],\n            initial_num_points=[3],\n        )\n        self.assertIsInstance(interval, DomainCreator1D)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Python/Test_Rectangle.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators import DomainCreator2D, Rectangle\n\n\nclass TestRectangle(unittest.TestCase):\n    def test_construction(self):\n        rectangle = Rectangle(\n            lower_bounds=[1.0, 0.0],\n            upper_bounds=[2.0, 1.0],\n            is_periodic=[False, False],\n            initial_refinement_levels=[1, 0],\n            initial_num_points=[3, 4],\n        )\n        self.assertIsInstance(rectangle, DomainCreator2D)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Python/Test_Sphere.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators import DomainCreator3D, Sphere\n\n\nclass TestSphere(unittest.TestCase):\n    def test_construction(self):\n        sphere = Sphere(\n            inner_radius=1.0,\n            outer_radius=2.0,\n            inner_cube_sphericity=0.0,\n            initial_refinement=1,\n            initial_number_of_grid_points=3,\n            use_equiangular_map=False,\n        )\n        self.assertIsInstance(sphere, DomainCreator3D)\n        shell = Sphere(\n            inner_radius=1.0,\n            outer_radius=2.0,\n            excise=True,\n            initial_refinement=1,\n            initial_number_of_grid_points=3,\n            use_equiangular_map=True,\n        )\n        self.assertIsInstance(shell, DomainCreator3D)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_AlignedLattice.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <memory>\n#include <sstream>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain {\nnamespace {\ntemplate <size_t VolumeDim>\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition() {\n  return std::make_unique<TestHelpers::domain::BoundaryConditions::\n                              TestBoundaryCondition<VolumeDim>>(\n      Direction<VolumeDim>::upper_xi(), 100);\n}\n\ntemplate <size_t VolumeDim>\nauto make_domain_creator(std::string opt_string,\n                         const bool use_boundary_condition) {\n  if (use_boundary_condition) {\n    opt_string +=\n        \"  BoundaryConditions:\\n\"\n        \"    - TestBoundaryCondition:\\n\"\n        \"        Direction: upper-xi\\n\"\n        \"        BlockId: 100\\n\";\n    for (size_t d = 1; d < VolumeDim; ++d) {\n      opt_string +=\n          \"    - TestBoundaryCondition:\\n\"\n          \"        Direction: lower-xi\\n\"\n          \"        BlockId: 100\\n\";\n    }\n    return TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<VolumeDim>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                VolumeDim, domain::creators::AlignedLattice<VolumeDim>>>(\n        opt_string);\n  } else {\n    return TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<VolumeDim>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<\n                VolumeDim, domain::creators::AlignedLattice<VolumeDim>>>(\n        opt_string);\n  }\n}\n\ntemplate <size_t Dim>\nvoid check_block_names(const Domain<Dim>& domain,\n                       const std::array<std::vector<double>, Dim>& bounds) {\n  const tnsr::I<double, Dim, Frame::BlockLogical> corner(make_array<Dim>(-1.0));\n  for (const auto& block : domain.blocks()) {\n    std::ostringstream expected_name{};\n    expected_name << \"Block(\";\n    const auto mapped_corner = block.stationary_map()(corner);\n    for (size_t d = 0; d < Dim; ++d) {\n      if (d > 0) {\n        expected_name << \",\";\n      }\n      for (size_t i = 0; i < gsl::at(bounds, d).size(); ++i) {\n        if (equal_within_roundoff(mapped_corner.get(d),\n                                  gsl::at(bounds, d)[i])) {\n          expected_name << i;\n          break;\n        }\n      }\n    }\n    expected_name << \")\";\n    CAPTURE(mapped_corner);\n    CAPTURE(bounds);\n    CHECK(block.name() == expected_name.str());\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.AlignedLattice\", \"[Domain][Unit]\") {\n  domain::creators::register_derived_with_charm();\n  TestHelpers::domain::BoundaryConditions::register_derived_with_charm();\n\n  for (const bool use_boundary_condition : {true, false}) {\n    CAPTURE(use_boundary_condition);\n    const auto domain_creator_1d = make_domain_creator<1>(\n        \"AlignedLattice:\\n\"\n        \"  BlockBounds: [[0.1, 2.6, 5.1, 5.2, 7.2]]\\n\" +\n            std::string{use_boundary_condition ? \"\"\n                                               : \"  IsPeriodicIn: [false]\\n\"} +\n            \"  InitialGridPoints: [3]\\n\"\n            \"  InitialLevels: [2]\\n\"\n            \"  RefinedLevels: []\\n\"\n            \"  RefinedGridPoints: []\\n\"\n            \"  BlocksToExclude: []\\n\",\n        use_boundary_condition);\n    const auto* aligned_blocks_creator_1d =\n        dynamic_cast<const creators::AlignedLattice<1>*>(\n            domain_creator_1d.get());\n    TestHelpers::domain::creators::test_domain_creator(\n        *aligned_blocks_creator_1d, use_boundary_condition);\n    CHECK(domain_creator_1d->grid_anchors().empty());\n    check_block_names(domain_creator_1d->create_domain(),\n                      {{{0.1, 2.6, 5.1, 5.2, 7.2}}});\n\n    const auto domain_creator_2d = make_domain_creator<2>(\n        \"AlignedLattice:\\n\"\n        \"  BlockBounds: [[0.1, 2.6, 5.1], [-0.4, 3.2, 6.2, 8.9]]\\n\" +\n            std::string{use_boundary_condition\n                            ? \"\"\n                            : \"  IsPeriodicIn: [false, false]\\n\"} +\n            \"  InitialGridPoints: [3, 4]\\n\"\n            \"  InitialLevels: [2, 1]\\n\"\n            \"  RefinedLevels: []\\n\"\n            \"  RefinedGridPoints: []\\n\"\n            \"  BlocksToExclude: []\\n\",\n        use_boundary_condition);\n    const auto* aligned_blocks_creator_2d =\n        dynamic_cast<const creators::AlignedLattice<2>*>(\n            domain_creator_2d.get());\n    TestHelpers::domain::creators::test_domain_creator(\n        *aligned_blocks_creator_2d, use_boundary_condition);\n    check_block_names(domain_creator_2d->create_domain(),\n                      {{{0.1, 2.6, 5.1}, {-0.4, 3.2, 6.2, 8.9}}});\n\n    const auto domain_creator_3d = make_domain_creator<3>(\n        \"AlignedLattice:\\n\"\n        \"  BlockBounds: [[0.1, 2.6, 5.1], [-0.4, 3.2, 6.2], [-0.2, 3.2]]\\n\" +\n            std::string{use_boundary_condition\n                            ? \"\"\n                            : \"  IsPeriodicIn: [false, false, false]\\n\"} +\n            \"  InitialGridPoints: [3, 4, 5]\\n\"\n            \"  InitialLevels: [2, 1, 0]\\n\"\n            \"  RefinedLevels: []\\n\"\n            \"  RefinedGridPoints: []\\n\"\n            \"  BlocksToExclude: []\\n\",\n        use_boundary_condition);\n    const auto* aligned_blocks_creator_3d =\n        dynamic_cast<const creators::AlignedLattice<3>*>(\n            domain_creator_3d.get());\n    TestHelpers::domain::creators::test_domain_creator(\n        *aligned_blocks_creator_3d, use_boundary_condition);\n    check_block_names(domain_creator_3d->create_domain(),\n                      {{{0.1, 2.6, 5.1}, {-0.4, 3.2, 6.2}, {-0.2, 3.2}}});\n\n    const auto cubical_shell_domain = make_domain_creator<3>(\n        \"AlignedLattice:\\n\"\n        \"  BlockBounds: [[0.1, 2.6, 5.1, 6.0], [-0.4, 3.2, 6.2, 7.0], \"\n        \"[-0.2, 3.2, 4.0, 5.2]]\\n\" +\n            std::string{use_boundary_condition\n                            ? \"\"\n                            : \"  IsPeriodicIn: [false, false, false]\\n\"} +\n            \"  InitialGridPoints: [3, 4, 5]\\n\"\n            \"  InitialLevels: [2, 1, 0]\\n\"\n            \"  RefinedLevels: []\\n\"\n            \"  RefinedGridPoints: []\\n\"\n            \"  BlocksToExclude: [[1, 1, 1]]\\n\",\n        use_boundary_condition);\n    const auto* cubical_shell_creator_3d =\n        dynamic_cast<const creators::AlignedLattice<3>*>(\n            cubical_shell_domain.get());\n    TestHelpers::domain::creators::test_domain_creator(\n        *cubical_shell_creator_3d, use_boundary_condition);\n    check_block_names(\n        cubical_shell_domain->create_domain(),\n        {{{0.1, 2.6, 5.1, 6.0}, {-0.4, 3.2, 6.2, 7.0}, {-0.2, 3.2, 4.0, 5.2}}});\n\n    const auto unit_cubical_shell_domain = make_domain_creator<3>(\n        \"AlignedLattice:\\n\"\n        \"  BlockBounds: [[-1.5, -0.5, 0.5, 1.5], [-1.5, -0.5, 0.5, 1.5], \"\n        \"[-1.5, -0.5, 0.5, 1.5]]\\n\" +\n            std::string{use_boundary_condition\n                            ? \"\"\n                            : \"  IsPeriodicIn: [false, false, false]\\n\"} +\n            \"  InitialGridPoints: [5, 5, 5]\\n\"\n            \"  InitialLevels: [1, 1, 1]\\n\"\n            \"  RefinedLevels: []\\n\"\n            \"  RefinedGridPoints: []\\n\"\n            \"  BlocksToExclude: [[1, 1, 1]]\\n\",\n        use_boundary_condition);\n    const auto* unit_cubical_shell_creator_3d =\n        dynamic_cast<const creators::AlignedLattice<3>*>(\n            unit_cubical_shell_domain.get());\n    TestHelpers::domain::creators::test_domain_creator(\n        *unit_cubical_shell_creator_3d, use_boundary_condition);\n    check_block_names(unit_cubical_shell_domain->create_domain(),\n                      {{{-1.5, -0.5, 0.5, 1.5},\n                        {-1.5, -0.5, 0.5, 1.5},\n                        {-1.5, -0.5, 0.5, 1.5}}});\n  }\n\n  const auto domain_creator_2d_periodic = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<2>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithoutBoundaryConditions<\n              2, domain::creators::AlignedLattice<2>>>(\n      \"AlignedLattice:\\n\"\n      \"  BlockBounds: [[0.1, 2.6, 5.1], [-0.4, 3.2, 6.2, 8.9]]\\n\"\n      \"  IsPeriodicIn: [false, true]\\n\"\n      \"  InitialGridPoints: [3, 4]\\n\"\n      \"  InitialLevels: [2, 1]\\n\"\n      \"  RefinedLevels: []\\n\"\n      \"  RefinedGridPoints: []\\n\"\n      \"  BlocksToExclude: []\\n\");\n  const auto* aligned_blocks_creator_2d_periodic =\n      dynamic_cast<const creators::AlignedLattice<2>*>(\n          domain_creator_2d_periodic.get());\n  TestHelpers::domain::creators::test_domain_creator(\n      *aligned_blocks_creator_2d_periodic, false, true);\n  check_block_names(domain_creator_2d_periodic->create_domain(),\n                    {{{0.1, 2.6, 5.1}, {-0.4, 3.2, 6.2, 8.9}}});\n\n  const auto domain_creator_3d_periodic = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<3>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithoutBoundaryConditions<\n              3, domain::creators::AlignedLattice<3>>>(\n      \"AlignedLattice:\\n\"\n      \"  BlockBounds: [[0.1, 2.6, 5.1], [-0.4, 3.2, 6.2], [-0.2, 3.2]]\\n\"\n      \"  IsPeriodicIn: [false, true, false]\\n\"\n      \"  InitialGridPoints: [3, 4, 5]\\n\"\n      \"  InitialLevels: [2, 1, 0]\\n\"\n      \"  RefinedLevels: []\\n\"\n      \"  RefinedGridPoints: []\\n\"\n      \"  BlocksToExclude: []\\n\");\n  const auto* aligned_blocks_creator_3d_periodic =\n      dynamic_cast<const creators::AlignedLattice<3>*>(\n          domain_creator_3d_periodic.get());\n  TestHelpers::domain::creators::test_domain_creator(\n      *aligned_blocks_creator_3d_periodic, false, true);\n  check_block_names(domain_creator_3d_periodic->create_domain(),\n                    {{{0.1, 2.6, 5.1}, {-0.4, 3.2, 6.2}, {-0.2, 3.2}}});\n\n  {\n    // Expected domain refinement:\n    // 23 23 67\n    // 23 45 67\n    // 23 XX 45\n    const auto refined_domain = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<2>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<\n                2, domain::creators::AlignedLattice<2>>>(\n        \"AlignedLattice:\\n\"\n        \"  BlockBounds: [[70, 71, 72, 73], [90, 91, 92, 93]]\\n\"\n        \"  IsPeriodicIn: [false, false]\\n\"\n        \"  InitialGridPoints: [2, 3]\\n\"\n        \"  InitialLevels: [0, 0]\\n\"\n        \"  BlocksToExclude: [[1, 0]]\\n\"\n        \"  RefinedLevels: []\\n\"\n        \"  RefinedGridPoints:\\n\"\n        \"  - LowerCornerIndex: [1, 0]\\n\"\n        \"    UpperCornerIndex: [3, 2]\\n\"\n        \"    Refinement: [4, 5]\\n\"\n        \"  - LowerCornerIndex: [2, 1]\\n\"\n        \"    UpperCornerIndex: [3, 3]\\n\"\n        \"    Refinement: [6, 7]\");\n    std::unordered_set<\n        std::pair<std::vector<double>, std::array<size_t, 2>>,\n        boost::hash<std::pair<std::vector<double>, std::array<size_t, 2>>>>\n        expected_blocks{{{70.0, 90.0}, {{2, 3}}}, {{72.0, 90.0}, {{4, 5}}},\n                        {{70.0, 91.0}, {{2, 3}}}, {{71.0, 91.0}, {{4, 5}}},\n                        {{72.0, 91.0}, {{6, 7}}}, {{70.0, 92.0}, {{2, 3}}},\n                        {{71.0, 92.0}, {{2, 3}}}, {{72.0, 92.0}, {{6, 7}}}};\n    const auto domain = TestHelpers::domain::creators::test_domain_creator(\n        *refined_domain, false);\n\n    const auto& blocks = domain.blocks();\n    const auto extents = refined_domain->initial_extents();\n    REQUIRE(blocks.size() == extents.size());\n    for (size_t i = 0; i < blocks.size(); ++i) {\n      const auto location =\n          blocks[i]\n              .stationary_map()(\n                  tnsr::I<double, 2, Frame::BlockLogical>{{{-1.0, -1.0}}})\n              .get_vector_of_data()\n              .second;\n      INFO(\"Unexpected block\");\n      CAPTURE(location);\n      CAPTURE(extents[i]);\n      CHECK(expected_blocks.erase({location, extents[i]}) == 1);\n    }\n    CAPTURE(expected_blocks);\n    CHECK(expected_blocks.empty());\n    check_block_names(domain,\n                      {{{70.0, 71.0, 72.0, 73}, {90.0, 91.0, 92.0, 93.0}}});\n  }\n\n  {\n    // Expected domain refinement:\n    // 25 25 46\n    // 25 35 46\n    // 25 XX 35\n    const auto refined_domain = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<2>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<\n                2, domain::creators::AlignedLattice<2>>>(\n        \"AlignedLattice:\\n\"\n        \"  BlockBounds: [[70, 71, 72, 73], [90, 91, 92, 93]]\\n\"\n        \"  IsPeriodicIn: [false, false]\\n\"\n        \"  InitialGridPoints: [10, 10]\\n\"\n        \"  InitialLevels: [2, 5]\\n\"\n        \"  BlocksToExclude: [[1, 0]]\\n\"\n        \"  RefinedGridPoints: []\\n\"\n        \"  RefinedLevels:\\n\"\n        \"  - LowerCornerIndex: [1, 0]\\n\"\n        \"    UpperCornerIndex: [3, 2]\\n\"\n        \"    Refinement: [3, 5]\\n\"\n        \"  - LowerCornerIndex: [2, 1]\\n\"\n        \"    UpperCornerIndex: [3, 3]\\n\"\n        \"    Refinement: [4, 6]\");\n    std::unordered_set<\n        std::pair<std::vector<double>, std::array<size_t, 2>>,\n        boost::hash<std::pair<std::vector<double>, std::array<size_t, 2>>>>\n        expected_blocks{{{70.0, 90.0}, {{2, 5}}}, {{72.0, 90.0}, {{3, 5}}},\n                        {{70.0, 91.0}, {{2, 5}}}, {{71.0, 91.0}, {{3, 5}}},\n                        {{72.0, 91.0}, {{4, 6}}}, {{70.0, 92.0}, {{2, 5}}},\n                        {{71.0, 92.0}, {{2, 5}}}, {{72.0, 92.0}, {{4, 6}}}};\n    const auto domain = TestHelpers::domain::creators::test_domain_creator(\n        *refined_domain, false);\n    const auto refinement_levels = refined_domain->initial_refinement_levels();\n\n    const auto& blocks = domain.blocks();\n    REQUIRE(blocks.size() == refinement_levels.size());\n    for (size_t i = 0; i < blocks.size(); ++i) {\n      const auto location =\n          blocks[i]\n              .stationary_map()(\n                  tnsr::I<double, 2, Frame::BlockLogical>{{{-1.0, -1.0}}})\n              .get_vector_of_data()\n              .second;\n      INFO(\"Unexpected block\");\n      CAPTURE(location);\n      CAPTURE(refinement_levels[i]);\n      CHECK(expected_blocks.erase({location, refinement_levels[i]}) == 1);\n    }\n    CAPTURE(expected_blocks);\n    CHECK(expected_blocks.empty());\n    check_block_names(domain,\n                      {{{70.0, 71.0, 72.0, 73}, {90.0, 91.0, 92.0, 93.0}}});\n  }\n\n  CHECK_THROWS_WITH(\n      creators::AlignedLattice<3>({{{{-1.5, -0.5, 0.5, 1.5}},\n                                    {{1.5, -0.5, 0.5, 1.5}},\n                                    {{-1.5, -0.5, 0.5, 1.5}}}},\n                                  {{1, 1, 1}}, {{5, 5, 5}}, {}, {},\n                                  {{{{1, 1, 1}}}}, {{true, false, false}},\n                                  Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot exclude blocks as well as have periodic boundary\"));\n  CHECK_THROWS_WITH(\n      creators::AlignedLattice<3>({{{{-1.5, -0.5, 0.5, 1.5}},\n                                    {{1.5, -0.5, 0.5, 1.5}},\n                                    {{-1.5, -0.5, 0.5, 1.5}}}},\n                                  {{1, 1, 1}}, {{5, 5, 5}}, {}, {},\n                                  {{{{1, 1, 1}}}}, {{false, true, false}},\n                                  Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot exclude blocks as well as have periodic boundary\"));\n  CHECK_THROWS_WITH(\n      creators::AlignedLattice<3>({{{{-1.5, -0.5, 0.5, 1.5}},\n                                    {{1.5, -0.5, 0.5, 1.5}},\n                                    {{-1.5, -0.5, 0.5, 1.5}}}},\n                                  {{1, 1, 1}}, {{5, 5, 5}}, {}, {},\n                                  {{{{1, 1, 1}}}}, {{true, false, true}},\n                                  Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot exclude blocks as well as have periodic boundary\"));\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_AngularCylinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/PolarToCartesian.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/AngularCylinder.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n\nnamespace domain {\nnamespace {\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::lower_xi(), 0);\n}\n\ntemplate <typename... FuncsOfTime>\nvoid test_angular_cylinder_construction(\n    const creators::AngularCylinder& angular_cylinder,\n    const double outer_radius, const double lower_z_bound,\n    const double upper_z_bound, const std::vector<double>& radial_partitioning,\n    const std::vector<double>& partitioning_in_z,\n    const std::vector<domain::CoordinateMaps::Distribution>& distribution_in_z,\n    const std::vector<std::array<size_t, 3>>& expected_extents,\n    const std::vector<std::array<size_t, 3>>& expected_refinement_level,\n    const std::vector<DirectionMap<3, BlockNeighbors<3>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<3>>>&\n        expected_external_boundaries,\n    const bool is_periodic_in_z = false,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time = {},\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::Grid, Frame::Inertial, 3>>>& expected_grid_to_inertial_maps = {},\n    const bool expect_boundary_conditions = false,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      angular_cylinder, expect_boundary_conditions, is_periodic_in_z);\n  CHECK(angular_cylinder.grid_anchors().empty());\n\n  // Calculate expected dimensions\n  const size_t num_radial_blocks = 1 + radial_partitioning.size();\n  const size_t num_layers = 1 + partitioning_in_z.size();\n\n  // Verify block names\n  std::vector<std::string> expected_block_names;\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    const std::string layer_prefix =\n        num_layers > 1 ? \"Layer\" + std::to_string(layer) : \"\";\n\n    // Center disk\n    expected_block_names.emplace_back(layer_prefix + \"CenterDisk\");\n\n    // Shells\n    for (size_t shell = 1; shell < num_radial_blocks; ++shell) {\n      expected_block_names.emplace_back(layer_prefix + \"Shell\" +\n                                        std::to_string(shell - 1));\n    }\n  }\n  CHECK(angular_cylinder.block_names() == expected_block_names);\n\n  // Verify block groups\n  const auto block_groups = angular_cylinder.block_groups();\n\n  // Per-layer groups (only if num_layers > 1)\n  if (num_layers > 1) {\n    for (size_t layer = 0; layer < num_layers; ++layer) {\n      const std::string layer_name = \"Layer\" + std::to_string(layer);\n      CHECK(block_groups.contains(layer_name));\n\n      std::unordered_set<std::string> expected_layer_blocks;\n      for (size_t radial = 0; radial < num_radial_blocks; ++radial) {\n        expected_layer_blocks.insert(\n            expected_block_names[layer * num_radial_blocks + radial]);\n      }\n      CHECK(block_groups.at(layer_name) == expected_layer_blocks);\n    }\n  }\n\n  // Global groups\n  CHECK(block_groups.contains(\"InnerDisks\"));\n  std::unordered_set<std::string> expected_center_blocks;\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    expected_center_blocks.insert(\n        expected_block_names[layer * num_radial_blocks]);\n  }\n  CHECK(block_groups.at(\"InnerDisks\") == expected_center_blocks);\n\n  if (num_radial_blocks > 1) {\n    CHECK(block_groups.contains(\"Shells\"));\n    std::unordered_set<std::string> expected_shell_blocks;\n    for (size_t layer = 0; layer < num_layers; ++layer) {\n      for (size_t shell = 1; shell < num_radial_blocks; ++shell) {\n        expected_shell_blocks.insert(\n            expected_block_names[layer * num_radial_blocks + shell]);\n      }\n    }\n    CHECK(block_groups.at(\"Shells\") == expected_shell_blocks);\n  } else {\n    CHECK(not block_groups.contains(\"Shells\"));\n  }\n\n  CHECK(angular_cylinder.initial_extents() == expected_extents);\n  CHECK(angular_cylinder.initial_refinement_levels() ==\n        expected_refinement_level);\n\n  // Create expected coordinate maps\n  using Affine = CoordinateMaps::Affine;\n  using Identity1D = CoordinateMaps::Identity<1>;\n  using Interval = CoordinateMaps::Interval;\n  using ProductMap3D =\n      CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Interval>;\n  using PolarToCartesian = CoordinateMaps::PolarToCartesian;\n  using PolarProduct =\n      CoordinateMaps::ProductOf2Maps<PolarToCartesian, Identity1D>;\n\n  auto coord_maps = make_vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>();\n\n  // Create coordinate maps for each block\n  for (size_t layer = 0; layer < num_layers; ++layer) {\n    const double z_lower =\n        (layer == 0) ? lower_z_bound : partitioning_in_z[layer - 1];\n    const double z_upper =\n        (layer == num_layers - 1) ? upper_z_bound : partitioning_in_z[layer];\n\n    for (size_t radial = 0; radial < num_radial_blocks; ++radial) {\n      const double inner_radius =\n          (radial == 0) ? 0.0 : radial_partitioning[radial - 1];\n      const double current_outer_radius = (radial == num_radial_blocks - 1)\n                                              ? outer_radius\n                                              : radial_partitioning[radial];\n\n      coord_maps.emplace_back(\n          make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n              ProductMap3D{\n                  Affine{-1.0, 1.0, inner_radius, current_outer_radius},\n                  Identity1D{},\n                  Interval{-1.0, 1.0, z_lower, z_upper,\n                           distribution_in_z[layer]}},\n              PolarProduct{PolarToCartesian{}, Identity1D{}}));\n    }\n  }\n\n  test_domain_construction(domain, expected_block_neighbors,\n                           expected_external_boundaries, coord_maps, 10.0,\n                           angular_cylinder.functions_of_time(),\n                           expected_grid_to_inertial_maps);\n  TestHelpers::domain::creators::test_functions_of_time(\n      angular_cylinder, expected_functions_of_time, initial_expiration_times);\n}\n\nvoid test_angular_cylinder_single_block() {\n  INFO(\"AngularCylinder single block (no partitioning)\");\n\n  const double outer_radius = 2.0;\n  const double lower_z_bound = -1.0;\n  const double upper_z_bound = 1.0;\n  const std::vector<double> radial_partitioning{};\n  const std::vector<double> partitioning_in_z{};\n  const size_t initial_cylinder_theta_grid_points = 5;\n  const size_t initial_cylinder_z_grid_points = 4;\n  const std::vector<std::array<size_t, 3>>\n      initial_hollow_cylinder_grid_points{};\n  const std::vector<domain::CoordinateMaps::Distribution> distribution_in_z{\n      domain::CoordinateMaps::Distribution::Linear};\n\n  // Calculate expected extents for center disk\n  const size_t theta_M = initial_cylinder_theta_grid_points / 2;\n  const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n  const std::vector<std::array<size_t, 3>> expected_extents{\n      {{disk_n_r, initial_cylinder_theta_grid_points,\n        initial_cylinder_z_grid_points}}};\n\n  const std::vector<std::array<size_t, 3>> refinement_level{{{0, 0, 2}}};\n\n  // Single block has no neighbors\n  const std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_neighbors{{}};\n\n  // Single block has external boundaries in z and radial directions\n  const std::vector<std::unordered_set<Direction<3>>> expected_externals{\n      {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n       Direction<3>::upper_zeta()}};\n\n  {\n    INFO(\"With boundary conditions\");\n    const creators::AngularCylinder angular_cylinder{\n        outer_radius,\n        lower_z_bound,\n        upper_z_bound,\n        radial_partitioning,\n        partitioning_in_z,\n        initial_cylinder_theta_grid_points,\n        initial_cylinder_z_grid_points,\n        initial_hollow_cylinder_grid_points,\n        distribution_in_z,\n        2_st,\n        nullptr,\n        create_boundary_condition(),\n        create_boundary_condition(),\n        create_boundary_condition()};\n\n    test_angular_cylinder_construction(\n        angular_cylinder, outer_radius, lower_z_bound, upper_z_bound,\n        radial_partitioning, partitioning_in_z, distribution_in_z,\n        expected_extents, refinement_level, expected_neighbors,\n        expected_externals, false, {}, {}, true);\n  }\n}\n\nvoid test_angular_cylinder_multi_radial() {\n  INFO(\"AngularCylinder multiple radial blocks\");\n\n  const double outer_radius = 3.0;\n  const double lower_z_bound = 0.0;\n  const double upper_z_bound = 2.0;\n  const std::vector<double> radial_partitioning{1.0, 2.0};\n  const std::vector<double> partitioning_in_z{};\n  const size_t initial_cylinder_theta_grid_points = 7;\n  const size_t initial_cylinder_z_grid_points = 6;\n  const std::vector<std::array<size_t, 3>> initial_hollow_cylinder_grid_points{\n      {{4, 7, 5}}, {{5, 9, 6}}};\n  const std::vector<domain::CoordinateMaps::Distribution> distribution_in_z{\n      domain::CoordinateMaps::Distribution::Linear};\n\n  // Calculate expected extents\n  const size_t theta_M = initial_cylinder_theta_grid_points / 2;\n  const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n  const std::vector<std::array<size_t, 3>> expected_extents{\n      {{disk_n_r, initial_cylinder_theta_grid_points,\n        initial_cylinder_z_grid_points}},\n      {{4, 7, 5}},\n      {{5, 9, 6}}};\n\n  const std::vector<std::array<size_t, 3>> refinement_level{{{0, 0, 1}}};\n\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n\n  const std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_neighbors{\n      {{Direction<3>::upper_xi(), {1, aligned}}},\n      {{Direction<3>::lower_xi(), {0, aligned}},\n       {Direction<3>::upper_xi(), {2, aligned}}},\n      {{Direction<3>::lower_xi(), {1, aligned}}}};\n\n  const std::vector<std::unordered_set<Direction<3>>> expected_externals{\n      {Direction<3>::lower_zeta(), Direction<3>::upper_zeta()},\n      {Direction<3>::lower_zeta(), Direction<3>::upper_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n       Direction<3>::upper_zeta()}};\n\n  const creators::AngularCylinder angular_cylinder{\n      outer_radius,\n      lower_z_bound,\n      upper_z_bound,\n      radial_partitioning,\n      partitioning_in_z,\n      initial_cylinder_theta_grid_points,\n      initial_cylinder_z_grid_points,\n      initial_hollow_cylinder_grid_points,\n      distribution_in_z,\n      1_st,\n      nullptr,\n      create_boundary_condition(),\n      create_boundary_condition(),\n      create_boundary_condition()};\n\n  test_angular_cylinder_construction(\n      angular_cylinder, outer_radius, lower_z_bound, upper_z_bound,\n      radial_partitioning, partitioning_in_z, distribution_in_z,\n      expected_extents,\n      {refinement_level[0], refinement_level[0], refinement_level[0]},\n      expected_neighbors, expected_externals, false, {}, {}, true);\n}\n\nvoid test_angular_cylinder_multi_layer() {\n  INFO(\"AngularCylinder multiple layers\");\n\n  const double outer_radius = 2.0;\n  const double lower_z_bound = -2.0;\n  const double upper_z_bound = 2.0;\n  const std::vector<double> radial_partitioning{1.0};\n  const std::vector<double> partitioning_in_z{0.0};\n  const size_t initial_cylinder_theta_grid_points = 5;\n  const size_t initial_cylinder_z_grid_points = 4;\n  const std::array<size_t, 3> single_hollow_spec{{3, 5, 5}};\n  const std::vector<domain::CoordinateMaps::Distribution> distribution_in_z{\n      domain::CoordinateMaps::Distribution::Linear,\n      domain::CoordinateMaps::Distribution::Linear};\n\n  const size_t theta_M = initial_cylinder_theta_grid_points / 2;\n  const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n  const std::vector<std::array<size_t, 3>> expected_extents{\n      {{disk_n_r, initial_cylinder_theta_grid_points,\n        initial_cylinder_z_grid_points}},\n      {{3, 5, 5}},\n      {{disk_n_r, initial_cylinder_theta_grid_points,\n        initial_cylinder_z_grid_points}},\n      {{3, 5, 5}}};\n\n  const std::vector<std::array<size_t, 3>> refinement_level{{{0, 0, 0}}};\n\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n\n  const std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_neighbors{\n      {{Direction<3>::upper_xi(), {1, aligned}},\n       {Direction<3>::upper_zeta(), {2, aligned}}},\n      {{Direction<3>::lower_xi(), {0, aligned}},\n       {Direction<3>::upper_zeta(), {3, aligned}}},\n      {{Direction<3>::upper_xi(), {3, aligned}},\n       {Direction<3>::lower_zeta(), {0, aligned}}},\n      {{Direction<3>::lower_xi(), {2, aligned}},\n       {Direction<3>::lower_zeta(), {1, aligned}}}};\n\n  const std::vector<std::unordered_set<Direction<3>>> expected_externals{\n      {Direction<3>::lower_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::lower_zeta()},\n      {Direction<3>::upper_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::upper_zeta()}};\n\n  const creators::AngularCylinder angular_cylinder{\n      outer_radius,\n      lower_z_bound,\n      upper_z_bound,\n      radial_partitioning,\n      partitioning_in_z,\n      initial_cylinder_theta_grid_points,\n      initial_cylinder_z_grid_points,\n      single_hollow_spec,\n      distribution_in_z,\n      std::vector<size_t>{0, 1},\n      nullptr,\n      create_boundary_condition(),\n      create_boundary_condition(),\n      create_boundary_condition()};\n\n  test_angular_cylinder_construction(\n      angular_cylinder, outer_radius, lower_z_bound, upper_z_bound,\n      radial_partitioning, partitioning_in_z, distribution_in_z,\n      expected_extents, {{{0, 0, 0}}, {{0, 0, 0}}, {{0, 0, 1}}, {{0, 0, 1}}},\n      expected_neighbors, expected_externals, false, {}, {}, true);\n}\n\nvoid test_angular_cylinder_periodic_z() {\n  INFO(\"AngularCylinder with periodic Z\");\n\n  const double outer_radius = 1.5;\n  const double lower_z_bound = 0.0;\n  const double upper_z_bound = 1.0;\n  const std::vector<double> radial_partitioning{};\n  const std::vector<double> partitioning_in_z{0.5};\n  const size_t initial_cylinder_theta_grid_points = 3;\n  const size_t initial_cylinder_z_grid_points = 3;\n  const std::vector<std::array<size_t, 3>>\n      initial_hollow_cylinder_grid_points{};\n  const std::vector<domain::CoordinateMaps::Distribution> distribution_in_z{\n      domain::CoordinateMaps::Distribution::Linear,\n      domain::CoordinateMaps::Distribution::Linear};\n\n  const size_t theta_M = initial_cylinder_theta_grid_points / 2;\n  const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n  const std::vector<std::array<size_t, 3>> expected_extents{\n      {{disk_n_r, initial_cylinder_theta_grid_points,\n        initial_cylinder_z_grid_points}},\n      {{disk_n_r, initial_cylinder_theta_grid_points,\n        initial_cylinder_z_grid_points}}};\n\n  const std::vector<std::array<size_t, 3>> refinement_level{{{0, 0, 3}}};\n\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n\n  const std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_neighbors{\n      {{Direction<3>::upper_zeta(), {1, aligned}},\n       {Direction<3>::lower_zeta(), {1, aligned}}},\n      {{Direction<3>::lower_zeta(), {0, aligned}},\n       {Direction<3>::upper_zeta(), {0, aligned}}}};\n\n  const std::vector<std::unordered_set<Direction<3>>> expected_externals{\n      {Direction<3>::upper_xi()}, {Direction<3>::upper_xi()}};\n\n  auto periodic_bc = std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                          TestPeriodicBoundaryCondition<3>>();\n  auto periodic_bc2 = std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                           TestPeriodicBoundaryCondition<3>>();\n\n  const creators::AngularCylinder angular_cylinder{\n      outer_radius,\n      lower_z_bound,\n      upper_z_bound,\n      radial_partitioning,\n      partitioning_in_z,\n      initial_cylinder_theta_grid_points,\n      initial_cylinder_z_grid_points,\n      initial_hollow_cylinder_grid_points,\n      distribution_in_z,\n      std::vector<size_t>{3, 2},\n      nullptr,\n      std::move(periodic_bc),\n      std::move(periodic_bc2),\n      nullptr};\n\n  test_angular_cylinder_construction(\n      angular_cylinder, outer_radius, lower_z_bound, upper_z_bound,\n      radial_partitioning, partitioning_in_z, distribution_in_z,\n      expected_extents, {{{0, 0, 3}}, {{0, 0, 2}}}, expected_neighbors,\n      expected_externals, true);\n}\n\nvoid test_angular_cylinder_errors() {\n  INFO(\"AngularCylinder error conditions\");\n\n  const double outer_radius = 2.0;\n  const double lower_z_bound = 0.0;\n  const double upper_z_bound = 1.0;\n  const size_t initial_cylinder_theta_grid_points = 5;\n  const size_t initial_cylinder_z_grid_points = 4;\n  const std::vector<std::array<size_t, 3>>\n      initial_hollow_cylinder_grid_points_empty{};\n  const std::vector<std::array<size_t, 3>> initial_hollow_cylinder_grid_points{\n      {4, 7, 5}};\n\n  // Test outer_radius <= 0\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          0.0, lower_z_bound, upper_z_bound, {}, {},\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points_empty,\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr, false,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"OuterRadius must be positive\"));\n\n  // Test lower_z_bound >= upper_z_bound\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, 1.0, 0.0, {}, {}, initial_cylinder_theta_grid_points,\n          initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points_empty,\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr, false,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"LowerZBound must be less than\"));\n\n  // Test distribution_in_z size mismatch\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {}, {0.5},  // Two layers\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points_empty,\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr, false,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify a 'DistributionInZ' for every layer\"));\n\n  // Test first distribution not Linear\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {}, {},\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points_empty,\n          {domain::CoordinateMaps::Distribution::Logarithmic}, 0_st, nullptr,\n          false, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"must be 'Linear' for the lowermost layer\"));\n\n  // Test radial partitioning <= 0\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {0.0}, {},\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points,\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr, false,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Radial partitions must be positive\"));\n\n  // Test odd center cylinder theta\n  CHECK_THROWS_WITH(creators::AngularCylinder(\n                        outer_radius, lower_z_bound, upper_z_bound, {}, {}, 6,\n                        initial_cylinder_z_grid_points,  // Even number\n                        initial_hollow_cylinder_grid_points_empty,\n                        {domain::CoordinateMaps::Distribution::Linear}, 0_st,\n                        nullptr, false, Options::Context{false, {}, 1, 1}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The number of angular grid points must be odd\"));\n\n  // Test odd shell theta (single specification)\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {1.5}, {},\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          std::array<size_t, 3>{{4, 6, 5}},  // Even theta\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr, false,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The number of angular grid points must be odd\"));\n\n  // Test odd shell theta (vector specification)\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {1.5}, {},\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          std::vector<std::array<size_t, 3>>{{4, 6, 5}},  // Even theta\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr, false,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The number of angular grid points must be odd\"));\n\n  // Test None boundary conditions\n  auto none_bc = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestNoneBoundaryCondition<3>>();\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {}, {},\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points_empty,\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr,\n          std::move(none_bc), nullptr, nullptr),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported\"));\n\n  // Test mismatched periodic boundary conditions\n  auto periodic_bc = std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                          TestPeriodicBoundaryCondition<3>>();\n  auto regular_bc = create_boundary_condition();\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {}, {},\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points_empty,\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr,\n          std::move(periodic_bc), std::move(regular_bc), nullptr),\n      Catch::Matchers::ContainsSubstring(\"Either both lower and upper\"));\n\n  // Test periodic mantle boundary condition\n  auto periodic_mantle_bc =\n      std::make_unique<TestHelpers::domain::BoundaryConditions::\n                           TestPeriodicBoundaryCondition<3>>();\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {}, {},\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points_empty,\n          {domain::CoordinateMaps::Distribution::Linear}, 0_st, nullptr,\n          nullptr, nullptr, std::move(periodic_mantle_bc)),\n      Catch::Matchers::ContainsSubstring(\n          \"A cylinder can't have periodic boundary\"));\n\n  // Test nullptr mantle boundary condition but setting z\n  auto regular_z_lower_bc = create_boundary_condition();\n  auto regular_z_upper_bc = create_boundary_condition();\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(outer_radius, lower_z_bound, upper_z_bound, {},\n                                {}, initial_cylinder_theta_grid_points,\n                                initial_cylinder_z_grid_points,\n                                initial_hollow_cylinder_grid_points_empty,\n                                {domain::CoordinateMaps::Distribution::Linear},\n                                0_st, nullptr, std::move(regular_z_lower_bc),\n                                std::move(regular_z_upper_bc), nullptr),\n      Catch::Matchers::ContainsSubstring(\n          \"Mantle boundary condition is not set, but lower is\"));\n\n  // Test InitialRefinementInZ size mismatch\n  CHECK_THROWS_WITH(\n      creators::AngularCylinder(\n          outer_radius, lower_z_bound, upper_z_bound, {}, {0.5},  // Two layers\n          initial_cylinder_theta_grid_points, initial_cylinder_z_grid_points,\n          initial_hollow_cylinder_grid_points_empty,\n          {domain::CoordinateMaps::Distribution::Linear,\n           domain::CoordinateMaps::Distribution::Linear},\n          std::vector<size_t>{1}, nullptr, false,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"InitialRefinementInZ must have one entry per layer\"));\n}\n\nvoid test_angular_cylinder_factory() {\n  INFO(\"AngularCylinder factory tests\");\n\n  {\n    INFO(\"Factory with boundary conditions\");\n    const auto angular_cylinder = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                3, domain::creators::AngularCylinder>>(\n        \"AngularCylinder:\\n\"\n        \"  OuterRadius: 1.5\\n\"\n        \"  LowerZBound: -1.0\\n\"\n        \"  UpperZBound: 1.0\\n\"\n        \"  RadialPartitioning: []\\n\"\n        \"  PartitioningInZ: []\\n\"\n        \"  InitialCylinderThetaGridPoints: 3\\n\"\n        \"  InitialCylinderZGridPoints: 3\\n\"\n        \"  InitialHollowCylinderGridPoints: []\\n\"\n        \"  DistributionInZ: [Linear]\\n\"\n        \"  InitialRefinementInZ: 1\\n\"\n        \"  TimeDependence: None\\n\"\n        \"  BoundaryConditions:\\n\"\n        \"    LowerZ:\\n\"\n        \"      TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\"\n        \"    UpperZ:\\n\"\n        \"      TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\"\n        \"    Mantle:\\n\"\n        \"      TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\");\n\n    const auto* cylinder_creator =\n        dynamic_cast<const creators::AngularCylinder*>(angular_cylinder.get());\n    CHECK(cylinder_creator->block_names() ==\n          std::vector<std::string>{\"CenterDisk\"});\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.AngularCylinder\", \"[Domain][Unit]\") {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  test_angular_cylinder_single_block();\n  test_angular_cylinder_multi_radial();\n  test_angular_cylinder_multi_layer();\n  test_angular_cylinder_periodic_z();\n  test_angular_cylinder_errors();\n  test_angular_cylinder_factory();\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_AngularDisk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/PolarToCartesian.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/AngularDisk.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n\nnamespace domain {\nnamespace {\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<2>>(\n      Direction<2>::lower_xi(), 0);\n}\n\ntemplate <typename... FuncsOfTime>\nvoid test_angular_disk_construction(\n    const creators::AngularDisk& angular_disk, const double outer_radius,\n    const std::vector<double>& radial_partitioning,\n    const std::vector<std::array<size_t, 2>>& expected_extents,\n    const std::vector<std::array<size_t, 2>>& expected_refinement_level,\n    const std::vector<DirectionMap<2, BlockNeighbors<2>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<2>>>&\n        expected_external_boundaries,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time = {},\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::Grid, Frame::Inertial, 2>>>& expected_grid_to_inertial_maps = {},\n    const bool expect_boundary_conditions = false,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      angular_disk, expect_boundary_conditions);\n  CHECK(angular_disk.grid_anchors().empty());\n\n  // Verify block names\n  const size_t num_blocks = 1 + radial_partitioning.size();\n  std::vector<std::string> expected_block_names;\n  expected_block_names.emplace_back(\"InnerDisk\");\n  for (size_t i = 1; i < num_blocks; ++i) {\n    expected_block_names.emplace_back(\"Shell\" + std::to_string(i - 1));\n  }\n  CHECK(angular_disk.block_names() == expected_block_names);\n\n  // Verify block groups\n  const auto block_groups = angular_disk.block_groups();\n  CHECK(block_groups.contains(\"InnerDisk\"));\n  CHECK(block_groups.at(\"InnerDisk\") ==\n        std::unordered_set<std::string>{\"InnerDisk\"});\n\n  if (num_blocks > 1) {\n    CHECK(block_groups.contains(\"Shells\"));\n    std::unordered_set<std::string> expected_shell_names;\n    for (size_t i = 1; i < num_blocks; ++i) {\n      expected_shell_names.insert(\"Shell\" + std::to_string(i - 1));\n    }\n    CHECK(block_groups.at(\"Shells\") == expected_shell_names);\n  }\n\n  CHECK(angular_disk.initial_extents() == expected_extents);\n  CHECK(angular_disk.initial_refinement_levels() == expected_refinement_level);\n\n  // Create expected coordinate maps\n  using Affine = CoordinateMaps::Affine;\n  using Identity2D = CoordinateMaps::Identity<1>;\n  using ProductMap = CoordinateMaps::ProductOf2Maps<Affine, Identity2D>;\n  using PolarToCartesian = CoordinateMaps::PolarToCartesian;\n\n  auto coord_maps = make_vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 2>>>();\n\n  // Center block\n  double inner_radius = 0.0;\n  double current_outer_radius =\n      radial_partitioning.empty() ? outer_radius : radial_partitioning[0];\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          ProductMap{Affine{-1.0, 1.0, inner_radius, current_outer_radius},\n                     Identity2D{}},\n          PolarToCartesian{}));\n\n  // Shell blocks\n  for (size_t i = 1; i < num_blocks; ++i) {\n    inner_radius = radial_partitioning[i - 1];\n    current_outer_radius =\n        (i == num_blocks - 1) ? outer_radius : radial_partitioning[i];\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            ProductMap{Affine{-1.0, 1.0, inner_radius, current_outer_radius},\n                       Identity2D{}},\n            PolarToCartesian{}));\n  }\n\n  test_domain_construction(domain, expected_block_neighbors,\n                           expected_external_boundaries, coord_maps, 10.0,\n                           angular_disk.functions_of_time(),\n                           expected_grid_to_inertial_maps);\n  TestHelpers::domain::creators::test_functions_of_time(\n      angular_disk, expected_functions_of_time, initial_expiration_times);\n}\n\nvoid test_angular_disk_single_block() {\n  INFO(\"AngularDisk single block (no radial partitioning)\");\n\n  const double outer_radius = 2.0;\n  const std::vector<double> radial_partitioning{};\n  const size_t initial_disk_theta_grid_points = 5;  // Must be odd\n  const std::vector<std::array<size_t, 2>> initial_annulus_grid_points{};\n  const std::vector<std::array<size_t, 2>> refinement_level{{0, 0}};\n\n  // Calculate expected extents for center disk\n  const size_t theta_M = initial_disk_theta_grid_points / 2;\n  const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n  const std::vector<std::array<size_t, 2>> expected_extents{\n      {{disk_n_r, initial_disk_theta_grid_points}}};\n\n  // Single block has no neighbors\n  const std::vector<DirectionMap<2, BlockNeighbors<2>>> expected_neighbors{{}};\n\n  // Single block has all external boundaries\n  const std::vector<std::unordered_set<Direction<2>>> expected_externals{\n      {Direction<2>::upper_xi()}};\n\n  {\n    INFO(\"Without boundary conditions\");\n    const creators::AngularDisk angular_disk{\n        outer_radius, radial_partitioning, initial_disk_theta_grid_points,\n        initial_annulus_grid_points};  // Uses all defaults\n\n    test_angular_disk_construction(\n        angular_disk, outer_radius, radial_partitioning, expected_extents,\n        refinement_level, expected_neighbors, expected_externals);\n  }\n\n  {\n    INFO(\"With boundary conditions\");\n    const creators::AngularDisk angular_disk{outer_radius,\n                                             radial_partitioning,\n                                             initial_disk_theta_grid_points,\n                                             initial_annulus_grid_points,\n                                             nullptr,\n                                             create_boundary_condition()};\n\n    test_angular_disk_construction(\n        angular_disk, outer_radius, radial_partitioning, expected_extents,\n        refinement_level, expected_neighbors, expected_externals, {}, {}, true);\n  }\n}\n\nvoid test_angular_disk_multi_block() {\n  INFO(\"AngularDisk multi-block (with radial partitioning)\");\n\n  const double outer_radius = 3.0;\n  const std::vector<double> radial_partitioning{1.0, 2.0};\n  const size_t initial_disk_theta_grid_points = 7;  // Must be odd\n  const std::vector<std::array<size_t, 2>> initial_annulus_grid_points{\n      {{4, 7}}, {{5, 9}}};  // Grid points for each shell\n  const std::vector<std::array<size_t, 2>> refinement_level{{0, 0}};\n\n  // Calculate expected extents\n  const size_t theta_M = initial_disk_theta_grid_points / 2;\n  const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n  const std::vector<std::array<size_t, 2>> expected_extents{\n      {{disk_n_r, initial_disk_theta_grid_points}},  // Center\n      {{4, 7}},                                      // Shell0\n      {{5, 9}}                                       // Shell1\n  };\n\n  const OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n\n  const std::vector<DirectionMap<2, BlockNeighbors<2>>> expected_neighbors{\n      {{Direction<2>::upper_xi(), {1, aligned}}},\n      {{Direction<2>::lower_xi(), {0, aligned}},\n       {Direction<2>::upper_xi(), {2, aligned}}},\n      {{Direction<2>::lower_xi(), {1, aligned}}}};\n\n  const std::vector<std::unordered_set<Direction<2>>> expected_externals{\n      {}, {}, {Direction<2>::upper_xi()}};\n\n  {\n    INFO(\"Multi-block without boundary conditions\");\n    const creators::AngularDisk angular_disk{\n        outer_radius,\n        radial_partitioning,\n        initial_disk_theta_grid_points,\n        initial_annulus_grid_points};\n\n    test_angular_disk_construction(\n        angular_disk, outer_radius, radial_partitioning, expected_extents,\n        {refinement_level[0], refinement_level[0], refinement_level[0]},\n        expected_neighbors, expected_externals);\n  }\n\n  {\n    INFO(\"Multi-block with boundary conditions\");\n    const creators::AngularDisk angular_disk{outer_radius,\n                                             radial_partitioning,\n                                             initial_disk_theta_grid_points,\n                                             initial_annulus_grid_points,\n                                             nullptr,\n                                             create_boundary_condition()};\n\n    test_angular_disk_construction(\n        angular_disk, outer_radius, radial_partitioning, expected_extents,\n        {refinement_level[0], refinement_level[0], refinement_level[0]},\n        expected_neighbors, expected_externals, {}, {}, true);\n  }\n}\n\nvoid test_angular_disk_single_annulus_spec() {\n  INFO(\"AngularDisk with single annulus grid specification applied to all\");\n\n  const double outer_radius = 2.5;\n  const std::vector<double> radial_partitioning{1.5};\n  const size_t initial_disk_theta_grid_points = 9;\n  const std::array<size_t, 2> single_annulus_spec{\n      {6, 11}};  // Will be applied to all shells\n  const std::vector<std::array<size_t, 2>> refinement_level{{0, 0}};\n\n  const size_t theta_M = initial_disk_theta_grid_points / 2;\n  const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n  const std::vector<std::array<size_t, 2>> expected_extents{\n      {{disk_n_r, initial_disk_theta_grid_points}}, {{6, 11}}};\n\n  const OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n  const std::vector<DirectionMap<2, BlockNeighbors<2>>> expected_neighbors{\n      {{Direction<2>::upper_xi(), {1, aligned}}},\n      {{Direction<2>::lower_xi(), {0, aligned}}}};\n\n  const std::vector<std::unordered_set<Direction<2>>> expected_externals{\n      {}, {Direction<2>::upper_xi()}};\n\n  const creators::AngularDisk angular_disk{outer_radius, radial_partitioning,\n                                           initial_disk_theta_grid_points,\n                                           single_annulus_spec};\n\n  test_angular_disk_construction(angular_disk, outer_radius,\n                                 radial_partitioning, expected_extents,\n                                 {refinement_level[0], refinement_level[0]},\n                                 expected_neighbors, expected_externals);\n}\n\nvoid test_angular_disk_errors() {\n  INFO(\"AngularDisk error conditions\");\n\n  const double outer_radius = 2.0;\n  const size_t initial_disk_theta_grid_points = 5;\n  const std::vector<std::array<size_t, 2>> initial_annulus_grid_points_empty{};\n  const std::vector<std::array<size_t, 2>> initial_annulus_grid_points{{4, 7}};\n\n  // Test unsorted radial partitioning\n  CHECK_THROWS_WITH(\n      creators::AngularDisk(outer_radius, {1.5, 1.0},\n                            initial_disk_theta_grid_points,\n                            initial_annulus_grid_points, nullptr, nullptr,\n                            Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify radial partitioning in ascending order\"));\n\n  // Test radial partition >= outer_radius\n  CHECK_THROWS_WITH(\n      creators::AngularDisk(outer_radius, {2.0}, initial_disk_theta_grid_points,\n                            initial_annulus_grid_points, nullptr, nullptr,\n                            Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Last radial partition must be smaller than the outer radius\"));\n\n  // Test duplicate radial partitioning values\n  CHECK_THROWS_WITH(\n      creators::AngularDisk(outer_radius, {1.0, 1.0},\n                            initial_disk_theta_grid_points,\n                            initial_annulus_grid_points, nullptr, nullptr,\n                            Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Radial partitioning contains duplicate element\"));\n\n  // Test outer boundary <= 0\n  CHECK_THROWS_WITH(\n      creators::AngularDisk(-1.0, {1.5}, initial_disk_theta_grid_points,\n                            initial_annulus_grid_points, nullptr, nullptr,\n                            Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Must have a positive outer radius\"));\n\n  // Test radial partitioning <= 0\n  CHECK_THROWS_WITH(\n      creators::AngularDisk(outer_radius, {0.0}, initial_disk_theta_grid_points,\n                            initial_annulus_grid_points, nullptr, nullptr,\n                            Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Radial partitions must be positive\"));\n\n  // Test odd disk theta\n  CHECK_THROWS_WITH(creators::AngularDisk(\n                        outer_radius, {1.5}, 6, initial_annulus_grid_points,\n                        nullptr, nullptr, Options::Context{false, {}, 1, 1}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The number of angular grid points must be odd\"));\n\n  // Test odd shell theta\n  CHECK_THROWS_WITH(\n      creators::AngularDisk(outer_radius, {1.5}, initial_disk_theta_grid_points,\n                            std::vector<std::array<size_t, 2>>{{4, 4}}, nullptr,\n                            nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The number of angular grid points must be odd\"));\n\n  // Test None boundary condition\n  auto none_bc = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestNoneBoundaryCondition<2>>();\n  CHECK_THROWS_WITH(creators::AngularDisk(\n                        outer_radius, {}, initial_disk_theta_grid_points,\n                        initial_annulus_grid_points_empty, nullptr,\n                        std::move(none_bc), Options::Context{false, {}, 1, 1}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"None boundary condition is not supported\"));\n\n  // Test periodic boundary condition (not allowed for disk)\n  auto periodic_bc = std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                          TestPeriodicBoundaryCondition<2>>();\n  CHECK_THROWS_WITH(\n      creators::AngularDisk(outer_radius, {}, initial_disk_theta_grid_points,\n                            initial_annulus_grid_points_empty, nullptr,\n                            std::move(periodic_bc),\n                            Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Cannot have periodic boundary\"));\n\n  // Test annulus grid points size mismatch\n  CHECK_THROWS_WITH(\n      creators::AngularDisk(\n          outer_radius, {1.0, 1.5}, initial_disk_theta_grid_points,\n          std::vector<std::array<size_t, 2>>{\n              {4, 7}},  // Only one spec for two shells\n          nullptr, nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"must be one larger than\"));\n}\n\nvoid test_angular_disk_factory() {\n  INFO(\"AngularDisk factory tests\");\n\n  {\n    INFO(\"Factory without boundary conditions\");\n    const auto angular_disk =\n        TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<2>,\n                                     TestHelpers::domain::BoundaryConditions::\n                                         MetavariablesWithoutBoundaryConditions<\n                                             2, domain::creators::AngularDisk>>(\n            \"AngularDisk:\\n\"\n            \"  OuterRadius: 2.5\\n\"\n            \"  RadialPartitioning: [1.0]\\n\"\n            \"  InitialDiskThetaGridPoints: 7\\n\"\n            \"  InitialAnnulusGridPoints: [4, 9]\\n\"\n            \"  TimeDependence: None\\n\");\n\n    const auto* disk_creator =\n        dynamic_cast<const creators::AngularDisk*>(angular_disk.get());\n\n    // Test basic properties\n    CHECK(disk_creator->block_names() ==\n          std::vector<std::string>{\"InnerDisk\", \"Shell0\"});\n\n    const size_t theta_M = 7 / 2;\n    const size_t disk_n_r = theta_M / 2 + 1 + theta_M % 2;\n    CHECK(disk_creator->initial_extents() ==\n          std::vector<std::array<size_t, 2>>{{{disk_n_r, 7}}, {{4, 9}}});\n  }\n\n  {\n    INFO(\"Factory with boundary conditions\");\n    const auto angular_disk =\n        TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<2>,\n                                     TestHelpers::domain::BoundaryConditions::\n                                         MetavariablesWithBoundaryConditions<\n                                             2, domain::creators::AngularDisk>>(\n            \"AngularDisk:\\n\"\n            \"  OuterRadius: 3.0\\n\"\n            \"  RadialPartitioning: []\\n\"\n            \"  InitialDiskThetaGridPoints: 5\\n\"\n            \"  InitialAnnulusGridPoints: []\\n\"\n            \"  TimeDependence: None\\n\"\n            \"  BoundaryCondition:\\n\"\n            \"    TestBoundaryCondition:\\n\"\n            \"      Direction: lower-xi\\n\"\n            \"      BlockId: 0\\n\");\n\n    const auto* disk_creator =\n        dynamic_cast<const creators::AngularDisk*>(angular_disk.get());\n    CHECK(disk_creator->block_names() ==\n          std::vector<std::string>{\"InnerDisk\"});\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.AngularDisk\", \"[Domain][Unit]\") {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  test_angular_disk_single_block();\n  test_angular_disk_multi_block();\n  test_angular_disk_single_annulus_spec();\n  test_angular_disk_errors();\n  test_angular_disk_factory();\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_BinaryCompactObject.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::FunctionsOfTime {\nclass FunctionOfTime;\n}  // namespace domain::FunctionsOfTime\n\nnamespace {\nusing ExpirationTimeMap = std::unordered_map<std::string, double>;\nusing Object = domain::creators::BinaryCompactObject<false>::Object;\nusing CartesianCubeAtXCoord =\n    domain::creators::BinaryCompactObject<false>::CartesianCubeAtXCoord;\nusing Excision = domain::creators::BinaryCompactObject<false>::Excision;\nusing Distribution = domain::CoordinateMaps::Distribution;\n\ntemplate <size_t Dim, bool WithBoundaryConditions>\nstruct Metavariables {\n  using system = tmpl::conditional_t<WithBoundaryConditions,\n                                     TestHelpers::domain::BoundaryConditions::\n                                         SystemWithBoundaryConditions<Dim>,\n                                     TestHelpers::domain::BoundaryConditions::\n                                         SystemWithoutBoundaryConditions<Dim>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<3>,\n                   tmpl::list<::domain::creators::BinaryCompactObject<false>>>>;\n  };\n};\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_inner_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::lower_zeta(), 50);\n}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_outer_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::upper_zeta(), 50);\n}\n\nvoid test_connectivity() {\n  MAKE_GENERATOR(gen);\n\n  // ObjectA:\n  constexpr double inner_radius_objectA = 0.3;\n  constexpr double outer_radius_objectA = 1.0;\n  constexpr double xcoord_objectA = 3.0;\n\n  // ObjectB:\n  constexpr double inner_radius_objectB = 0.5;\n  constexpr double outer_radius_objectB = 1.0;\n  constexpr double xcoord_objectB = -3.0;\n\n  // Center of mass offset:\n  constexpr std::array<double, 2> center_of_mass_offset{{0.1, 0.2}};\n\n  // Envelope:\n  constexpr double envelope_radius = 25.5;\n\n  // Outer shell:\n  constexpr double outer_radius = 32.4;\n\n  // Cube length array with and without offsets:\n  const std::array<double, 2> cube_scales = {{1.0, 1.5}};\n\n  // Misc.:\n  constexpr size_t grid_points = 3;\n  constexpr bool use_equiangular_map = true;\n\n  for (const auto& cube_scale : cube_scales) {\n    for (const auto& [with_boundary_conditions, excise_interiorA,\n                      excise_interiorB, opening_angle,\n                      radial_distribution_envelope,\n                      radial_distribution_outer_shell] :\n         random_sample<5>(\n             cartesian_product(\n                 make_array(true, false), make_array(true, false),\n                 make_array(true, false), make_array(60.0, 90.0, 120.0),\n                 make_array(Distribution::Linear, Distribution::Projective,\n                            Distribution::Logarithmic),\n                 make_array(Distribution::Linear, Distribution::Logarithmic,\n                            Distribution::Inverse)),\n             make_not_null(&gen))) {\n      CAPTURE(with_boundary_conditions);\n      CAPTURE(excise_interiorA);\n      CAPTURE(excise_interiorB);\n      CAPTURE(opening_angle);\n      CAPTURE(cube_scale);\n      CAPTURE(inner_radius_objectA);\n      CAPTURE(outer_radius_objectA);\n      CAPTURE(inner_radius_objectB);\n      CAPTURE(outer_radius_objectB);\n      std::variant<Distribution, std::vector<Distribution>>\n          variant_radial_distribution_outer_shells =\n              radial_distribution_outer_shell;\n      if (radial_distribution_outer_shell == Distribution::Linear) {\n        variant_radial_distribution_outer_shells = std::vector{\n            radial_distribution_outer_shell, radial_distribution_outer_shell};\n      }\n      const bool multiple_outer_shells =\n          std::holds_alternative<std::vector<Distribution>>(\n              variant_radial_distribution_outer_shells);\n      const std::vector<double> radial_partitioning_outer_shells =\n          multiple_outer_shells\n              ? std::vector<double>{0.5 * (envelope_radius + outer_radius)}\n              : std::vector<double>{};\n      std::unordered_map<std::string, std::array<size_t, 3>> refinement{\n          {\"ObjectAShell\", {{1, 1, 1}}},\n          {\"ObjectACube\", {{1, 1, 1}}},\n          {\"ObjectBShell\", {{1, 1, 1}}},\n          {\"ObjectBCube\", {{1, 1, 1}}},\n          {\"Envelope\", {{1, 1, 1}}},\n          // Add some radial refinement in outer shell\n          {\"OuterShell0\", {{1, 1, 4}}}};\n      if (multiple_outer_shells) {\n        refinement[\"OuterShell1\"] = std::array{1_st, 1_st, 2_st};\n      }\n      if (not excise_interiorA) {\n        refinement[\"ObjectAInterior\"] = std::array<size_t, 3>{{1, 1, 1}};\n      }\n      if (not excise_interiorB) {\n        refinement[\"ObjectBInterior\"] = std::array<size_t, 3>{{1, 1, 1}};\n      }\n      CAPTURE(radial_partitioning_outer_shells);\n      CAPTURE(variant_radial_distribution_outer_shells);\n      CAPTURE(radial_distribution_envelope);\n      const domain::creators::BinaryCompactObject binary_compact_object{\n          Object{inner_radius_objectA, outer_radius_objectA, xcoord_objectA,\n                 excise_interiorA ? std::make_optional(Excision{\n                                        with_boundary_conditions\n                                            ? create_inner_boundary_condition()\n                                            : nullptr})\n                                  : std::nullopt,\n                 false},\n          Object{inner_radius_objectB, outer_radius_objectB, xcoord_objectB,\n                 excise_interiorB ? std::make_optional(Excision{\n                                        with_boundary_conditions\n                                            ? create_inner_boundary_condition()\n                                            : nullptr})\n                                  : std::nullopt,\n                 false},\n          center_of_mass_offset,\n          envelope_radius,\n          outer_radius,\n          (excise_interiorA and excise_interiorB and opening_angle == 90)\n              ? cube_scale\n              : cube_scales[0],\n          refinement,\n          grid_points,\n          use_equiangular_map,\n          radial_distribution_envelope,\n          radial_partitioning_outer_shells,\n          variant_radial_distribution_outer_shells,\n          opening_angle,\n          std::nullopt,\n          with_boundary_conditions ? create_outer_boundary_condition()\n                                   : nullptr};\n\n      const auto domain = TestHelpers::domain::creators::test_domain_creator(\n          binary_compact_object, with_boundary_conditions);\n\n      std::vector<std::string> expected_block_names{\n          \"ObjectAShellUpperZ\",    \"ObjectAShellLowerZ\",\n          \"ObjectAShellUpperY\",    \"ObjectAShellLowerY\",\n          \"ObjectAShellUpperX\",    \"ObjectAShellLowerX\",\n          \"ObjectACubeUpperZ\",     \"ObjectACubeLowerZ\",\n          \"ObjectACubeUpperY\",     \"ObjectACubeLowerY\",\n          \"ObjectACubeUpperX\",     \"ObjectACubeLowerX\",\n          \"ObjectBShellUpperZ\",    \"ObjectBShellLowerZ\",\n          \"ObjectBShellUpperY\",    \"ObjectBShellLowerY\",\n          \"ObjectBShellUpperX\",    \"ObjectBShellLowerX\",\n          \"ObjectBCubeUpperZ\",     \"ObjectBCubeLowerZ\",\n          \"ObjectBCubeUpperY\",     \"ObjectBCubeLowerY\",\n          \"ObjectBCubeUpperX\",     \"ObjectBCubeLowerX\",\n          \"EnvelopeUpperZLeft\",    \"EnvelopeUpperZRight\",\n          \"EnvelopeLowerZLeft\",    \"EnvelopeLowerZRight\",\n          \"EnvelopeUpperYLeft\",    \"EnvelopeUpperYRight\",\n          \"EnvelopeLowerYLeft\",    \"EnvelopeLowerYRight\",\n          \"EnvelopeUpperX\",        \"EnvelopeLowerX\",\n          \"OuterShell0UpperZLeft\", \"OuterShell0UpperZRight\",\n          \"OuterShell0LowerZLeft\", \"OuterShell0LowerZRight\",\n          \"OuterShell0UpperYLeft\", \"OuterShell0UpperYRight\",\n          \"OuterShell0LowerYLeft\", \"OuterShell0LowerYRight\",\n          \"OuterShell0UpperX\",     \"OuterShell0LowerX\"};\n      std::unordered_map<std::string, std::unordered_set<std::string>>\n          expected_block_groups{\n              {\"ObjectAShell\",\n               {\"ObjectAShellLowerZ\", \"ObjectAShellUpperX\",\n                \"ObjectAShellLowerX\", \"ObjectAShellUpperY\",\n                \"ObjectAShellUpperZ\", \"ObjectAShellLowerY\"}},\n              {\"ObjectBShell\",\n               {\"ObjectBShellLowerZ\", \"ObjectBShellUpperX\",\n                \"ObjectBShellLowerX\", \"ObjectBShellUpperY\",\n                \"ObjectBShellUpperZ\", \"ObjectBShellLowerY\"}},\n              {\"ObjectACube\",\n               {\"ObjectACubeLowerY\", \"ObjectACubeLowerZ\", \"ObjectACubeUpperY\",\n                \"ObjectACubeUpperX\", \"ObjectACubeUpperZ\", \"ObjectACubeLowerX\"}},\n              {\"ObjectBCube\",\n               {\"ObjectBCubeLowerY\", \"ObjectBCubeLowerZ\", \"ObjectBCubeUpperY\",\n                \"ObjectBCubeUpperX\", \"ObjectBCubeUpperZ\", \"ObjectBCubeLowerX\"}},\n              {\"Envelope\",\n               {\"EnvelopeUpperZRight\", \"EnvelopeUpperX\", \"EnvelopeLowerZLeft\",\n                \"EnvelopeLowerZRight\", \"EnvelopeUpperYRight\",\n                \"EnvelopeLowerYRight\", \"EnvelopeUpperYLeft\",\n                \"EnvelopeUpperZLeft\", \"EnvelopeLowerYLeft\", \"EnvelopeLowerX\"}},\n              {\"OuterShell0\",\n               {\"OuterShell0UpperZLeft\", \"OuterShell0UpperZRight\",\n                \"OuterShell0LowerZLeft\", \"OuterShell0LowerZRight\",\n                \"OuterShell0UpperYLeft\", \"OuterShell0UpperYRight\",\n                \"OuterShell0LowerYLeft\", \"OuterShell0LowerYRight\",\n                \"OuterShell0UpperX\", \"OuterShell0LowerX\"}}};\n      if (multiple_outer_shells) {\n        const std::vector<std::string> shell_1_names = {\n            \"OuterShell1UpperZLeft\", \"OuterShell1UpperZRight\",\n            \"OuterShell1LowerZLeft\", \"OuterShell1LowerZRight\",\n            \"OuterShell1UpperYLeft\", \"OuterShell1UpperYRight\",\n            \"OuterShell1LowerYLeft\", \"OuterShell1LowerYRight\",\n            \"OuterShell1UpperX\",     \"OuterShell1LowerX\"};\n        expected_block_groups[\"OuterShell1\"] = std::unordered_set<std::string>{\n            shell_1_names.begin(), shell_1_names.end()};\n        expected_block_names.insert(expected_block_names.end(),\n                                    shell_1_names.begin(), shell_1_names.end());\n      }\n      if (not excise_interiorA) {\n        expected_block_names.emplace_back(\"ObjectAInterior\");\n      }\n      if (not excise_interiorB) {\n        expected_block_names.emplace_back(\"ObjectBInterior\");\n      }\n      CHECK(binary_compact_object.block_names() == expected_block_names);\n      CHECK(binary_compact_object.block_groups() == expected_block_groups);\n      std::unordered_map<std::string, ExcisionSphere<3>>\n          expected_excision_spheres{};\n      if (excise_interiorA) {\n        expected_excision_spheres.emplace(\n            \"ExcisionSphereA\",\n            ExcisionSphere<3>{inner_radius_objectA,\n                              tnsr::I<double, 3, Frame::Grid>{\n                                  {xcoord_objectA, center_of_mass_offset[0],\n                                   center_of_mass_offset[1]}},\n                              {{0, Direction<3>::lower_zeta()},\n                               {1, Direction<3>::lower_zeta()},\n                               {2, Direction<3>::lower_zeta()},\n                               {3, Direction<3>::lower_zeta()},\n                               {4, Direction<3>::lower_zeta()},\n                               {5, Direction<3>::lower_zeta()}}});\n      }\n      if (excise_interiorB) {\n        expected_excision_spheres.emplace(\n            \"ExcisionSphereB\",\n            ExcisionSphere<3>{inner_radius_objectB,\n                              tnsr::I<double, 3, Frame::Grid>{\n                                  {xcoord_objectB, center_of_mass_offset[0],\n                                   center_of_mass_offset[1]}},\n                              {{12, Direction<3>::lower_zeta()},\n                               {13, Direction<3>::lower_zeta()},\n                               {14, Direction<3>::lower_zeta()},\n                               {15, Direction<3>::lower_zeta()},\n                               {16, Direction<3>::lower_zeta()},\n                               {17, Direction<3>::lower_zeta()}}});\n      }\n      CHECK(domain.excision_spheres() == expected_excision_spheres);\n\n      if (with_boundary_conditions) {\n        using PeriodicBc = TestHelpers::domain::BoundaryConditions::\n            TestPeriodicBoundaryCondition<3>;\n        CHECK_THROWS_WITH(\n            domain::creators::BinaryCompactObject(\n                Object{\n                    inner_radius_objectA, outer_radius_objectA, xcoord_objectA,\n                    excise_interiorA ? std::make_optional(Excision{\n                                           create_inner_boundary_condition()})\n                                     : std::nullopt,\n                    false},\n                domain::creators::BinaryCompactObject<false>::Object{\n                    inner_radius_objectB, outer_radius_objectB, xcoord_objectB,\n                    excise_interiorB ? std::make_optional(Excision{\n                                           create_inner_boundary_condition()})\n                                     : std::nullopt,\n                    false},\n                center_of_mass_offset, envelope_radius, outer_radius,\n                (excise_interiorA and excise_interiorB) ? cube_scale\n                                                        : cube_scales[0],\n                refinement, grid_points, use_equiangular_map,\n                radial_distribution_envelope, radial_partitioning_outer_shells,\n                variant_radial_distribution_outer_shells, opening_angle,\n                std::nullopt, std::make_unique<PeriodicBc>(),\n                Options::Context{false, {}, 1, 1}),\n            Catch::Matchers::ContainsSubstring(\n                \"Cannot have periodic boundary \"\n                \"conditions with a binary domain\"));\n        if (excise_interiorA or excise_interiorB) {\n          CHECK_THROWS_WITH(\n              domain::creators::BinaryCompactObject(\n                  Object{inner_radius_objectA, outer_radius_objectA,\n                         xcoord_objectA,\n                         excise_interiorA ? std::make_optional(Excision{\n                                                std::make_unique<PeriodicBc>()})\n                                          : std::nullopt,\n                         false},\n                  Object{inner_radius_objectB, outer_radius_objectB,\n                         xcoord_objectB,\n                         excise_interiorB ? std::make_optional(Excision{\n                                                std::make_unique<PeriodicBc>()})\n                                          : std::nullopt,\n                         false},\n                  center_of_mass_offset, envelope_radius, outer_radius,\n                  (excise_interiorA and excise_interiorB) ? cube_scale\n                                                          : cube_scales[0],\n                  refinement, grid_points, use_equiangular_map,\n                  radial_distribution_envelope,\n                  radial_partitioning_outer_shells,\n                  variant_radial_distribution_outer_shells, opening_angle,\n                  std::nullopt, create_outer_boundary_condition(),\n                  Options::Context{false, {}, 1, 1}),\n              Catch::Matchers::ContainsSubstring(\n                  \"Cannot have periodic boundary \"\n                  \"conditions with a binary domain\"));\n          CHECK_THROWS_WITH(\n              domain::creators::BinaryCompactObject(\n                  Object{inner_radius_objectA, outer_radius_objectA,\n                         xcoord_objectA,\n                         excise_interiorA\n                             ? std::make_optional(\n                                   Excision{create_inner_boundary_condition()})\n                             : std::nullopt,\n                         false},\n                  Object{inner_radius_objectB, outer_radius_objectB,\n                         xcoord_objectB,\n                         excise_interiorB\n                             ? std::make_optional(\n                                   Excision{create_inner_boundary_condition()})\n                             : std::nullopt,\n                         false},\n                  center_of_mass_offset, envelope_radius, outer_radius,\n                  (excise_interiorA and excise_interiorB) ? cube_scale\n                                                          : cube_scales[0],\n                  refinement, grid_points, use_equiangular_map,\n                  radial_distribution_envelope,\n                  radial_partitioning_outer_shells,\n                  variant_radial_distribution_outer_shells, opening_angle,\n                  std::nullopt, nullptr, Options::Context{false, {}, 1, 1}),\n              Catch::Matchers::ContainsSubstring(\n                  \"Must specify either both inner and outer boundary \"\n                  \"conditions or neither.\"));\n          CHECK_THROWS_WITH(\n              domain::creators::BinaryCompactObject(\n                  Object{inner_radius_objectA, outer_radius_objectA,\n                         xcoord_objectA,\n                         excise_interiorA\n                             ? std::make_optional(Excision{nullptr})\n                             : std::nullopt,\n                         false},\n                  Object{inner_radius_objectB, outer_radius_objectB,\n                         xcoord_objectB,\n                         excise_interiorB\n                             ? std::make_optional(Excision{nullptr})\n                             : std::nullopt,\n                         false},\n                  center_of_mass_offset, envelope_radius, outer_radius,\n                  (excise_interiorA and excise_interiorB) ? cube_scale\n                                                          : cube_scales[0],\n                  refinement, grid_points, use_equiangular_map,\n                  radial_distribution_envelope,\n                  radial_partitioning_outer_shells,\n                  variant_radial_distribution_outer_shells, opening_angle,\n                  std::nullopt, create_outer_boundary_condition(),\n                  Options::Context{false, {}, 1, 1}),\n              Catch::Matchers::ContainsSubstring(\n                  \"Must specify either both inner and outer boundary \"\n                  \"conditions or neither.\"));\n        }\n      }\n    }\n  }\n}\n\nstd::string stringize(const bool t) { return t ? \"true\" : \"false\"; }\n\nstd::string create_option_string(\n    const bool excise_A, const bool excise_B, const bool add_time_dependence,\n    const bool use_logarithmic_map_AB, const bool use_equiangular_map,\n    const size_t additional_refinement_outer,\n    const size_t additional_refinement_A, const size_t additional_refinement_B,\n    const double opening_angle, const bool add_boundary_condition) {\n  const std::string cube_scale =\n      (excise_A and excise_B and opening_angle == 90) ? \"1.5\" : \"1.0\";\n  const std::string time_dependence{\n      add_time_dependence\n          ? (\"  TimeDependentMaps:\\n\"\n             \"    GridCenters:\\n\"\n             \"      ScaleInspiralRateBy: 0.8\\n\"\n             \"      SpecEvolutionParametersPerlFile: \"s +\n             unit_test_src_path() +\n             \"/../InputFiles/GrMhd/GhValenciaDivClean/\"\n             \"EvolutionParameters.perl\\n\"\n             \"    InitialTime: 1.0\\n\"\n             \"    ExpansionMap: \\n\"\n             \"      InitialValues: [1.0, -0.1, 0.0]\\n\"\n             \"      AsymptoticVelocityOuterBoundary: -0.1\\n\"\n             \"      DecayTimescaleOuterBoundary: 5.0\\n\"\n             \"    RotationMap:\\n\"\n             \"      InitialAngularVelocity: [0.0, 0.0, -0.2]\\n\"\n             \"    TranslationMap:\\n\"\n             \"      InitialValues: [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0], \"\n             \"      [0.0, 0.0, 0.0]]\\n\"s\n             \"    SkewMap:\\n\"\n             \"      InitialValuesY: [0.0, -0.01, 0.0]\\n\"\n             \"      InitialValuesZ: [0.0, -0.01, 0.0]\\n\" +\n             (excise_A ? \"    ShapeMapA:\\n\"\n                         \"      LMax: 8\\n\"\n                         \"      CoefficientTruncationLimit: 0.\\n\"\n                         \"      InitialValues: Spherical\\n\"\n                         \"      SizeInitialValues: [0.0, -0.1, 0.01]\\n\"\n                         \"      TransitionEndsAtCube: false\\n\"s\n                       : \"    ShapeMapA: None\\n\"s) +\n             (excise_B ? \"    ShapeMapB:\\n\"\n                         \"      LMax: 8\\n\"\n                         \"      CoefficientTruncationLimit: 0.\\n\"\n                         \"      InitialValues: Spherical\\n\"\n                         \"      SizeInitialValues: [0.0, -0.2, 0.02]\\n\"\n                         \"      TransitionEndsAtCube: true\"s\n                       : \"    ShapeMapB: None\"s))\n          : \"  TimeDependentMaps: None\"};\n  const std::string interior_A{\n      add_boundary_condition\n          ? std::string{\"    Interior:\\n\" +\n                        std::string{excise_A\n                                        ? \"      ExciseWithBoundaryCondition:\\n\"\n                                          \"        TestBoundaryCondition:\\n\"\n                                          \"          Direction: lower-zeta\\n\"\n                                          \"          BlockId: 50\\n\"\n                                        : \"      Auto\\n\"}}\n          : \"    ExciseInterior: \" + stringize(excise_A) + \"\\n\"};\n  const std::string interior_B{\n      add_boundary_condition\n          ? std::string{\"    Interior:\\n\" +\n                        std::string{excise_B\n                                        ? \"      ExciseWithBoundaryCondition:\\n\"\n                                          \"        TestBoundaryCondition:\\n\"\n                                          \"          Direction: lower-zeta\\n\"\n                                          \"          BlockId: 50\\n\"\n                                        : \"      Auto\\n\"}}\n          : \"    ExciseInterior: \" + stringize(excise_B) + \"\\n\"};\n  const std::string outer_boundary_condition{\n      add_boundary_condition ? std::string{\"    BoundaryCondition:\\n\"\n                                           \"      TestBoundaryCondition:\\n\"\n                                           \"        Direction: upper-zeta\\n\"\n                                           \"        BlockId: 50\\n\"}\n                             : \"\"};\n  return \"BinaryCompactObject:\\n\"\n         \"  ObjectA:\\n\"\n         \"    InnerRadius: 1.0\\n\"\n         \"    OuterRadius: 2.0\\n\"\n         \"    XCoord: 3.0\\n\" +\n         interior_A +\n         \"    UseLogarithmicMap: \" + stringize(use_logarithmic_map_AB) +\n         \"\\n\"\n         \"  ObjectB:\\n\"\n         \"    InnerRadius: 0.2\\n\"\n         \"    OuterRadius: 1.0\\n\"\n         \"    XCoord: -2.0\\n\" +\n         interior_B +\n         \"    UseLogarithmicMap: \" + stringize(use_logarithmic_map_AB) +\n         \"\\n\"\n         \"  CenterOfMassOffset: [0.1, 0.2]\\n\"\n         \"  Envelope:\\n\"\n         \"    Radius: 22.0\\n\"\n         \"    RadialDistribution: Projective\\n\"\n         \"  OuterShell:\\n\"\n         \"    Radius: 25.0\\n\"\n         \"    RadialPartitioning: [\" +\n         (excise_B ? \"23.5\" : \"\") +\n         \"]\\n\"\n         \"    RadialDistribution: \" +\n         (excise_B ? \"[Logarithmic, Linear]\" : \"Linear\") + \"\\n\" +\n         \"    OpeningAngle: \" + std::to_string(opening_angle) + \"\\n\" +\n         outer_boundary_condition + \"  InitialRefinement:\\n\" +\n         (excise_A ? \"\" : \"    ObjectAInterior: [1, 1, 1]\\n\") +\n         (excise_B ? \"\" : \"    ObjectBInterior: [1, 1, 1]\\n\") +\n         \"    ObjectAShell: [1, 1, \" +\n         std::to_string(1 + additional_refinement_A) +\n         \"]\\n\"\n         \"    ObjectBShell: [1, 1, \" +\n         std::to_string(1 + additional_refinement_B) +\n         \"]\\n\"\n         \"    ObjectACube: [1, 1, 1]\\n\"\n         \"    ObjectBCube: [1, 1, 1]\\n\"\n         \"    Envelope: [1, 1, 1]\\n\"\n         \"    OuterShell0: [1, 1, \" +\n         std::to_string(1 + additional_refinement_outer) + \"]\\n\" +\n         (excise_B ? \"    OuterShell1: [1, 1, 0]\\n\" : \"\") +\n         \"  InitialGridPoints: 3\\n\" + \"  CubeScale: \" + cube_scale +\n         \"\\n\"\n         \"  UseEquiangularMap: \" +\n         stringize(use_equiangular_map) + \"\\n\" + time_dependence;\n}\n\nvoid test_bns_domain_with_cubes() {\n  INFO(\"BNS domain with cubes\");\n\n  MAKE_GENERATOR(gen);\n\n  // ObjectA:\n  constexpr double xcoord_objectA = 3.0;\n\n  // ObjectB:\n  constexpr double xcoord_objectB = -3.0;\n\n  // CoM offset:\n  constexpr std::array<double, 2> center_of_mass_offset{{0.1, 0.2}};\n\n  // Envelope:\n  constexpr double envelope_radius = 25.5;\n  constexpr auto radial_distribution_envelope = Distribution::Projective;\n\n  // Outer shell:\n  constexpr double outer_radius = 32.4;\n  constexpr double opening_angle = 90.0;\n\n  // Cube length array with and without offsets:\n  const std::array<double, 2> cube_scales = {{1.0, 1.5}};\n\n  // Misc.:\n  constexpr size_t grid_points = 3;\n  constexpr bool use_equiangular_map = false;\n  const auto radial_distribution_outer_shell = Distribution::Inverse;\n  constexpr bool with_boundary_conditions = true;\n\n  std::unordered_map<std::string, std::array<size_t, 3>> refinement{\n      {\"ObjectA\", {{1, 1, 1}}},\n      {\"ObjectB\", {{1, 1, 1}}},\n      {\"Envelope\", {{1, 1, 1}}},\n      {\"OuterShell0\", {{1, 1, 4}}},\n      {\"OuterShell1\", {{1, 1, 2}}}};\n\n  for (const auto cube_scale : cube_scales) {\n    const domain::creators::BinaryCompactObject binary_compact_object{\n        CartesianCubeAtXCoord{xcoord_objectA},\n        CartesianCubeAtXCoord{xcoord_objectB},\n        center_of_mass_offset,\n        envelope_radius,\n        outer_radius,\n        cube_scale,\n        refinement,\n        grid_points,\n        use_equiangular_map,\n        radial_distribution_envelope,\n        std::vector<double>{0.49 * (envelope_radius + outer_radius)},\n        radial_distribution_outer_shell,\n        opening_angle,\n        std::nullopt,\n        create_outer_boundary_condition()};\n\n    const auto domain = TestHelpers::domain::creators::test_domain_creator(\n        binary_compact_object, with_boundary_conditions);\n\n    std::vector<std::string> expected_block_names{\"ObjectA\",\n                                                  \"ObjectB\",\n                                                  \"EnvelopeUpperZLeft\",\n                                                  \"EnvelopeUpperZRight\",\n                                                  \"EnvelopeLowerZLeft\",\n                                                  \"EnvelopeLowerZRight\",\n                                                  \"EnvelopeUpperYLeft\",\n                                                  \"EnvelopeUpperYRight\",\n                                                  \"EnvelopeLowerYLeft\",\n                                                  \"EnvelopeLowerYRight\",\n                                                  \"EnvelopeUpperX\",\n                                                  \"EnvelopeLowerX\",\n                                                  \"OuterShell0UpperZLeft\",\n                                                  \"OuterShell0UpperZRight\",\n                                                  \"OuterShell0LowerZLeft\",\n                                                  \"OuterShell0LowerZRight\",\n                                                  \"OuterShell0UpperYLeft\",\n                                                  \"OuterShell0UpperYRight\",\n                                                  \"OuterShell0LowerYLeft\",\n                                                  \"OuterShell0LowerYRight\",\n                                                  \"OuterShell0UpperX\",\n                                                  \"OuterShell0LowerX\",\n                                                  \"OuterShell1UpperZLeft\",\n                                                  \"OuterShell1UpperZRight\",\n                                                  \"OuterShell1LowerZLeft\",\n                                                  \"OuterShell1LowerZRight\",\n                                                  \"OuterShell1UpperYLeft\",\n                                                  \"OuterShell1UpperYRight\",\n                                                  \"OuterShell1LowerYLeft\",\n                                                  \"OuterShell1LowerYRight\",\n                                                  \"OuterShell1UpperX\",\n                                                  \"OuterShell1LowerX\"};\n    std::unordered_map<std::string, std::unordered_set<std::string>>\n        expected_block_groups{\n            {\"Envelope\",\n             {\"EnvelopeUpperZRight\", \"EnvelopeUpperX\", \"EnvelopeLowerZLeft\",\n              \"EnvelopeLowerZRight\", \"EnvelopeUpperYRight\",\n              \"EnvelopeLowerYRight\", \"EnvelopeUpperYLeft\", \"EnvelopeUpperZLeft\",\n              \"EnvelopeLowerYLeft\", \"EnvelopeLowerX\"}},\n            {\"OuterShell0\",\n             {\"OuterShell0UpperZLeft\", \"OuterShell0UpperZRight\",\n              \"OuterShell0LowerZLeft\", \"OuterShell0LowerZRight\",\n              \"OuterShell0UpperYLeft\", \"OuterShell0UpperYRight\",\n              \"OuterShell0LowerYLeft\", \"OuterShell0LowerYRight\",\n              \"OuterShell0UpperX\", \"OuterShell0LowerX\"}},\n            {\"OuterShell1\",\n             {\"OuterShell1UpperZLeft\", \"OuterShell1UpperZRight\",\n              \"OuterShell1LowerZLeft\", \"OuterShell1LowerZRight\",\n              \"OuterShell1UpperYLeft\", \"OuterShell1UpperYRight\",\n              \"OuterShell1LowerYLeft\", \"OuterShell1LowerYRight\",\n              \"OuterShell1UpperX\", \"OuterShell1LowerX\"}}};\n    CHECK(binary_compact_object.block_names() == expected_block_names);\n    CHECK(binary_compact_object.block_groups() == expected_block_groups);\n  }\n}\n\nvoid test_bbh_time_dependent_factory(const bool with_boundary_conditions,\n                                     const bool with_time_dependence,\n                                     const bool excise_B) {\n  INFO(\"BBH time dependent factory\");\n  CAPTURE(with_boundary_conditions);\n  CAPTURE(with_time_dependence);\n  CAPTURE(excise_B);\n  const auto binary_compact_object = [&with_boundary_conditions,\n                                      &with_time_dependence, &excise_B]() {\n    if (with_boundary_conditions) {\n      return TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<3>,\n                                          Metavariables<3, true>>(\n          create_option_string(true, excise_B, with_time_dependence, false,\n                               true, 0, 0, 0, 120.0, with_boundary_conditions));\n    } else {\n      return TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<3>,\n                                          Metavariables<3, false>>(\n          create_option_string(true, excise_B, with_time_dependence, false,\n                               true, 0, 0, 0, 120.0, with_boundary_conditions));\n    }\n  }();\n\n  const std::vector<double> times_to_check{{1., 2.3}};\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      *binary_compact_object, with_boundary_conditions, false, times_to_check);\n\n  const auto& excision_spheres = domain.excision_spheres();\n\n  std::unordered_map<std::string, ExcisionSphere<3>>\n      expected_excision_spheres{};\n  expected_excision_spheres.emplace(\n      \"ExcisionSphereA\",\n      ExcisionSphere<3>{1.0,\n                        tnsr::I<double, 3, Frame::Grid>{{3.0, 0.1, 0.2}},\n                        {{0, Direction<3>::lower_zeta()},\n                         {1, Direction<3>::lower_zeta()},\n                         {2, Direction<3>::lower_zeta()},\n                         {3, Direction<3>::lower_zeta()},\n                         {4, Direction<3>::lower_zeta()},\n                         {5, Direction<3>::lower_zeta()}}});\n  if (with_time_dependence) {\n    // Unfortunately, it'd be very hard to create the grid to inertial map\n    // necessary for the excision outside of the TimeDependentOptions class so\n    // we just clone the actual one here.\n    expected_excision_spheres.at(\"ExcisionSphereA\")\n        .inject_time_dependent_maps(excision_spheres.at(\"ExcisionSphereA\")\n                                        .moving_mesh_grid_to_inertial_map()\n                                        .get_clone());\n  }\n  if (excise_B) {\n    expected_excision_spheres.emplace(\n        \"ExcisionSphereB\",\n        ExcisionSphere<3>{0.2,\n                          tnsr::I<double, 3, Frame::Grid>{{-2.0, 0.1, 0.2}},\n                          {{12, Direction<3>::lower_zeta()},\n                           {13, Direction<3>::lower_zeta()},\n                           {14, Direction<3>::lower_zeta()},\n                           {15, Direction<3>::lower_zeta()},\n                           {16, Direction<3>::lower_zeta()},\n                           {17, Direction<3>::lower_zeta()}}});\n    if (with_time_dependence) {\n      // Unfortunately, it'd be very hard to create the grid to inertial map\n      // necessary for the excision outside of the TimeDependentOptions class so\n      // we just clone the actual one here.\n      expected_excision_spheres.at(\"ExcisionSphereB\")\n          .inject_time_dependent_maps(excision_spheres.at(\"ExcisionSphereB\")\n                                          .moving_mesh_grid_to_inertial_map()\n                                          .get_clone());\n    }\n  }\n\n  CHECK(excision_spheres == expected_excision_spheres);\n\n  const auto check_excision_sphere_map =\n      [&binary_compact_object](const ExcisionSphere<3>& excision_sphere) {\n        const double time = 1.0;\n        // Taken from option string above\n        const auto functions_of_time =\n            binary_compact_object->functions_of_time();\n        const auto& center = excision_sphere.center();\n        const auto& map = excision_sphere.moving_mesh_grid_to_inertial_map();\n\n        const auto mapped_point = map(center, time, functions_of_time);\n\n        // Should be same point\n        for (size_t i = 0; i < 3; i++) {\n          CHECK(center.get(i) == approx(mapped_point.get(i)));\n        }\n      };\n\n  if (with_time_dependence) {\n    check_excision_sphere_map(excision_spheres.at(\"ExcisionSphereA\"));\n    if (excise_B) {\n      check_excision_sphere_map(excision_spheres.at(\"ExcisionSphereB\"));\n    }\n    const auto functions_of_time = binary_compact_object->functions_of_time();\n    CHECK(functions_of_time.find(\"GridCenters\") != functions_of_time.end());\n  }\n}\n\nvoid test_binary_factory() {\n  MAKE_GENERATOR(gen);\n  const auto check_impl = [](const std::string& opt_string,\n                             const bool with_boundary_conditions) {\n    const auto binary_compact_object = [&opt_string,\n                                        &with_boundary_conditions]() {\n      if (with_boundary_conditions) {\n        return TestHelpers::test_option_tag<\n            domain::OptionTags::DomainCreator<3>, Metavariables<3, true>>(\n            opt_string);\n      } else {\n        return TestHelpers::test_option_tag<\n            domain::OptionTags::DomainCreator<3>, Metavariables<3, false>>(\n            opt_string);\n      }\n    }();\n    TestHelpers::domain::creators::test_domain_creator(\n        *binary_compact_object, with_boundary_conditions);\n  };\n  for (const auto& [excise_A, excise_B, use_log_maps, use_equiangular_map,\n                    additional_refinement_outer, additional_refinement_A,\n                    additional_refinement_B, opening_angle,\n                    with_boundary_conds] :\n       random_sample<5>(\n           cartesian_product(make_array(true, false), make_array(true, false),\n                             make_array(true, false), make_array(true, false),\n                             make_array(0_st, 1_st), make_array(0_st, 2_st),\n                             make_array(0_st, 3_st),\n                             make_array(60.0, 90.0, 120.0),\n                             make_array(true, false)),\n           make_not_null(&gen))) {\n    if (use_log_maps and not(excise_A and excise_B)) {\n      // Log maps in the object interiors only work with excisions\n      continue;\n    }\n    check_impl(create_option_string(\n                   excise_A, excise_B, false, use_log_maps,\n                   opening_angle == 90.0 ? use_equiangular_map : true,\n                   additional_refinement_outer, additional_refinement_A,\n                   additional_refinement_B, opening_angle, with_boundary_conds),\n               with_boundary_conds);\n  }\n}\n\nvoid test_parse_errors() {\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.5, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.3, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{20.0},\n          Distribution::Linear, 120.0, std::nullopt,\n          create_outer_boundary_condition(), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"First radial partition must be larger than the envelope radius\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.5, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.3, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{40.0},\n          Distribution::Linear, 120.0, std::nullopt,\n          create_outer_boundary_condition(), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Last radial partition must be smaller than the outer radius\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.5, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.3, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{28.0, 28.0},\n          Distribution::Linear, 120.0, std::nullopt,\n          create_outer_boundary_condition(), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Radial partitioning contains duplicate element\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.5, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.3, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{28.0, 29.0},\n          std::vector{Distribution::Linear}, 120.0, std::nullopt,\n          create_outer_boundary_condition(), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify a 'RadialDistribution' for every spherical shell.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.5, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.3, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The x-coordinate of ObjectA's center is expected to be positive.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.5, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.3, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The x-coordinate of ObjectB's center is expected to be negative.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 1.0, 8.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.5, 1.0, -7.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The radius for the enveloping cube is too \"\n          \"small! The Frustums will be malformed.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 1.0, 8.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.5, 1.0, -7.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 0.5, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The cube length should be greater than or equal to the initial \"\n          \"separation between the two objects.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{1.5, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"ObjectB's inner radius must be less than its outer radius.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{3.3, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.5, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"ObjectA's inner radius must be less than its outer radius.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 1.0, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.5, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"ObjectA's outer radius is too large for the given separation,  try \"\n          \"using 0.8\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.5, 1.0, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"ObjectB's outer radius is too large for the given separation,  try \"\n          \"using 0.8\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.5, 0.8, -1.0, std::nullopt, true},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, true, 6_st,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Using a logarithmically spaced radial grid in the \"\n          \"part of Layer 1 enveloping Object B requires excising the interior \"\n          \"of Object B\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 0.8, 1.0, std::nullopt, true},\n          Object{0.5, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Using a logarithmically spaced radial grid in the \"\n          \"part of Layer 1 enveloping Object A requires excising the interior \"\n          \"of Object A\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 0.8, 1.0, false, false},\n          Object{0.5, 0.8, -1.0, false, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.2, 2_st, 6_st),\n      Catch::Matchers::ContainsSubstring(\n          \"A filled object cannot be offset within its cube.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.5, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0,\n          std::vector<std::array<size_t, 3>>{}, 6_st, true,\n          Distribution::Projective, std::vector<double>{}, Distribution::Linear,\n          120.0, std::nullopt, create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Invalid 'InitialRefinement'\"));\n  CHECK_THROWS_WITH(\n      domain::creators::BinaryCompactObject(\n          Object{0.3, 0.8, 1.0, {{create_inner_boundary_condition()}}, false},\n          Object{0.5, 0.8, -1.0, {{create_inner_boundary_condition()}}, false},\n          std::array<double, 2>{{0.1, 0.2}}, 25.5, 32.4, 1.0, 2_st,\n          std::vector<std::array<size_t, 3>>{}, true, Distribution::Projective,\n          std::vector<double>{}, Distribution::Linear, 120.0, std::nullopt,\n          create_outer_boundary_condition(), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Invalid 'InitialGridPoints'\"));\n  // Note: the boundary condition-related parse errors are checked in the\n  // test_connectivity function.\n}\n\ntemplate <domain::ObjectLabel Object>\nusing HardcodedShape =\n    domain::creators::time_dependent_options::ShapeMapOptions<true, Object>;\n\nvoid test_kerr_horizon_conforming() {\n  INFO(\n      \"Check that inner radius is deformed to constant Boyer-Lindquist radius\");\n  const double mass_A = 0.8;\n  const double mass_B = 1.2;\n  const std::array<double, 3> spin_A{{0.0, 0.0, 0.9}};\n  const std::array<double, 3> spin_B{{0.0, 0.2, 0.4}};\n  const double r_plus_A = mass_A * (1. + sqrt(1. - dot(spin_A, spin_A)));\n  const double r_plus_B = mass_B * (1. + sqrt(1. - dot(spin_B, spin_B)));\n  const double inner_radius_A = r_plus_A;\n  const double inner_radius_B = 0.89 * r_plus_B;\n  const double x_pos_A = 8;\n  const double x_pos_B = -8;\n  const double y_offset = 0.1;\n  const double z_offset = 0.2;\n  domain::creators::BinaryCompactObject domain_creator{\n      Object{inner_radius_A, 4., x_pos_A, true, true},\n      Object{inner_radius_B, 4., x_pos_B, true, true},\n      std::array<double, 2>{{0.1, 0.2}},\n      40.,\n      200.,\n      1.0,\n      0_st,\n      6_st,\n      true,\n      Distribution::Projective,\n      std::vector<double>{},\n      Distribution::Inverse,\n      120.,\n      domain::creators::bco::TimeDependentMapOptions<false>{\n          0., std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n          HardcodedShape<domain::ObjectLabel::A>{\n              32_st, domain::creators::time_dependent_options::\n                         KerrSchildFromBoyerLindquist{mass_A, spin_A}},\n          HardcodedShape<domain::ObjectLabel::B>{\n              32_st, domain::creators::time_dependent_options::\n                         KerrSchildFromBoyerLindquist{mass_B, spin_B}},\n          std::nullopt}};\n  const auto domain = domain_creator.create_domain();\n  const auto functions_of_time = domain_creator.functions_of_time();\n  // Set up coordinates on an ellipsoid of constant Boyer-Lindquist radius\n  const size_t num_points = 10;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist_phi{0., 2. * M_PI};\n  std::uniform_real_distribution<double> dist_theta{0., M_PI};\n  const std::array<DataVector, 2> theta_phi{\n      {make_with_random_values<DataVector>(\n           make_not_null(&gen), make_not_null(&dist_theta), num_points),\n       make_with_random_values<DataVector>(\n           make_not_null(&gen), make_not_null(&dist_phi), num_points)}};\n  const auto radius_A =\n      get(gr::Solutions::kerr_schild_radius_from_boyer_lindquist(\n          inner_radius_A, theta_phi, mass_A, spin_A));\n  const auto radius_B =\n      get(gr::Solutions::kerr_schild_radius_from_boyer_lindquist(\n          inner_radius_B, theta_phi, mass_B, spin_B));\n  tnsr::I<DataVector, 3> x_A{};\n  tnsr::I<DataVector, 3> x_B{};\n  get<0>(x_A) =\n      x_pos_A + radius_A * sin(get<0>(theta_phi)) * cos(get<1>(theta_phi));\n  get<1>(x_A) =\n      y_offset + radius_A * sin(get<0>(theta_phi)) * sin(get<1>(theta_phi));\n  get<2>(x_A) = z_offset + radius_A * cos(get<0>(theta_phi));\n  get<0>(x_B) =\n      x_pos_B + radius_B * sin(get<0>(theta_phi)) * cos(get<1>(theta_phi));\n  get<1>(x_B) =\n      y_offset + radius_B * sin(get<0>(theta_phi)) * sin(get<1>(theta_phi));\n  get<2>(x_B) = z_offset + radius_B * cos(get<0>(theta_phi));\n  // Map the coordinates through the domain. They should lie at the lower zeta\n  // boundary of their block.\n  const auto x_logical_A =\n      block_logical_coordinates(domain, x_A, 0., functions_of_time);\n  const auto x_logical_B =\n      block_logical_coordinates(domain, x_B, 0., functions_of_time);\n  for (size_t i = 0; i < num_points; ++i) {\n    {\n      CAPTURE(x_logical_A[i]);\n      REQUIRE(x_logical_A[i].has_value());\n      CHECK(get<2>(x_logical_A[i]->data) == approx(-1.));\n    }\n    {\n      CAPTURE(x_logical_B[i]);\n      REQUIRE(x_logical_B[i].has_value());\n      CHECK(get<2>(x_logical_B[i]->data) == approx(-1.));\n    }\n  }\n}\n\n}  // namespace\n\n// [[TimeOut, 45]]\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.BinaryCompactObject\",\n                  \"[Domain][Unit]\") {\n  test_connectivity();\n  test_bns_domain_with_cubes();\n  for (const auto& [with_bc, add_time_dep, excise_B] :\n       cartesian_product(make_array(true, false), make_array(true, false),\n                         make_array(true, false))) {\n    test_bbh_time_dependent_factory(with_bc, add_time_dep, excise_B);\n  }\n  test_binary_factory();\n  test_parse_errors();\n  test_kerr_horizon_conforming();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_Brick.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\nusing Affine = CoordinateMaps::Affine;\nusing Affine3D = CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\nusing Translation3D = CoordinateMaps::TimeDependent::Translation<3>;\n\ntemplate <typename... FuncsOfTime>\nvoid test_brick_construction(\n    const creators::Brick& brick, const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound,\n    const std::vector<std::array<size_t, 3>>& expected_extents,\n    const std::vector<std::array<size_t, 3>>& expected_refinement_level,\n    const std::vector<DirectionMap<3, BlockNeighbors<3>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<3>>>&\n        expected_external_boundaries,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time = {},\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::Grid, Frame::Inertial, 3>>>& expected_grid_to_inertial_maps = {},\n    const bool expect_boundary_conditions = false,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      brick, expect_boundary_conditions);\n  CHECK(brick.grid_anchors().empty());\n\n  CHECK(brick.initial_extents() == expected_extents);\n  CHECK(brick.initial_refinement_levels() == expected_refinement_level);\n  CHECK(brick.block_names() == std::vector<std::string>{\"Brick\"});\n  const auto block_groups = brick.block_groups();\n  CHECK(block_groups.contains(\"Brick\"));\n  CHECK(block_groups.at(\"Brick\") == std::unordered_set<std::string>{\"Brick\"});\n\n  test_domain_construction(\n      domain, expected_block_neighbors, expected_external_boundaries,\n      make_vector(make_coordinate_map_base<\n                  Frame::BlockLogical,\n                  tmpl::conditional_t<sizeof...(FuncsOfTime) == 0,\n                                      Frame::Inertial, Frame::Grid>>(\n          Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                   Affine{-1., 1., lower_bound[2], upper_bound[2]}})),\n      10.0, brick.functions_of_time(), expected_grid_to_inertial_maps);\n\n  TestHelpers::domain::creators::test_functions_of_time(\n      brick, expected_functions_of_time, initial_expiration_times);\n}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::lower_xi(), 2);\n}\n\nvoid test_brick() {\n  INFO(\"Brick\");\n  const std::vector<std::array<size_t, 3>> grid_points{{{4, 6, 3}}};\n  const std::vector<std::array<size_t, 3>> refinement_level{{{3, 2, 4}}};\n  const std::array<double, 3> lower_bound{{-1.2, 3.0, 2.5}};\n  const std::array<double, 3> upper_bound{{0.8, 5.0, 3.0}};\n  const OrientationMap<3> aligned_orientation =\n      OrientationMap<3>::create_aligned();\n  const auto periodic_bc =\n      TestHelpers::domain::BoundaryConditions::TestPeriodicBoundaryCondition<\n          3>{};\n\n  {\n    INFO(\"Not periodic, no boundary conditions\");\n    const creators::Brick brick{lower_bound, upper_bound, refinement_level[0],\n                                grid_points[0],\n                                std::array<bool, 3>{{false, false, false}}};\n    test_brick_construction(brick, lower_bound, upper_bound, grid_points,\n                            refinement_level,\n                            std::vector<DirectionMap<3, BlockNeighbors<3>>>{{}},\n                            std::vector<std::unordered_set<Direction<3>>>{\n                                {{Direction<3>::lower_xi()},\n                                 {Direction<3>::upper_xi()},\n                                 {Direction<3>::lower_eta()},\n                                 {Direction<3>::upper_eta()},\n                                 {Direction<3>::lower_zeta()},\n                                 {Direction<3>::upper_zeta()}}});\n  }\n  {\n    INFO(\"Not periodic, with boundary conditions\");\n    const creators::Brick brick_boundary_condition{\n        lower_bound,\n        upper_bound,\n        refinement_level[0],\n        grid_points[0],\n        {{{{create_boundary_condition(), create_boundary_condition()}},\n          {{create_boundary_condition(), create_boundary_condition()}},\n          {{create_boundary_condition(), create_boundary_condition()}}}}};\n    test_brick_construction(brick_boundary_condition, lower_bound, upper_bound,\n                            grid_points, refinement_level,\n                            std::vector<DirectionMap<3, BlockNeighbors<3>>>{{}},\n                            std::vector<std::unordered_set<Direction<3>>>{\n                                {{Direction<3>::lower_xi()},\n                                 {Direction<3>::upper_xi()},\n                                 {Direction<3>::lower_eta()},\n                                 {Direction<3>::upper_eta()},\n                                 {Direction<3>::lower_zeta()},\n                                 {Direction<3>::upper_zeta()}}},\n                            {}, {}, true);\n  }\n  {\n    INFO(\"Periodic in x\");\n    test_brick_construction(\n        creators::Brick{lower_bound, upper_bound, refinement_level[0],\n                        grid_points[0],\n                        std::array<bool, 3>{{true, false, false}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(), {0, aligned_orientation}},\n             {Direction<3>::upper_xi(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_eta()},\n             {Direction<3>::upper_eta()},\n             {Direction<3>::lower_zeta()},\n             {Direction<3>::upper_zeta()}}});\n    test_brick_construction(\n        creators::Brick{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{create_boundary_condition(), create_boundary_condition()}},\n              {{create_boundary_condition(), create_boundary_condition()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(), {0, aligned_orientation}},\n             {Direction<3>::upper_xi(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_eta()},\n             {Direction<3>::upper_eta()},\n             {Direction<3>::lower_zeta()},\n             {Direction<3>::upper_zeta()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"Periodic in y\");\n    test_brick_construction(\n        creators::Brick{lower_bound, upper_bound, refinement_level[0],\n                        grid_points[0],\n                        std::array<bool, 3>{{false, true, false}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()},\n             {Direction<3>::upper_xi()},\n             {Direction<3>::lower_zeta()},\n             {Direction<3>::upper_zeta()}}});\n    test_brick_construction(\n        creators::Brick{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{create_boundary_condition(), create_boundary_condition()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{create_boundary_condition(), create_boundary_condition()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()},\n             {Direction<3>::upper_xi()},\n             {Direction<3>::lower_zeta()},\n             {Direction<3>::upper_zeta()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"Periodic in z\");\n    test_brick_construction(\n        creators::Brick{lower_bound, upper_bound, refinement_level[0],\n                        grid_points[0],\n                        std::array<bool, 3>{{false, false, true}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_zeta(), {0, aligned_orientation}},\n             {Direction<3>::upper_zeta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()},\n             {Direction<3>::upper_xi()},\n             {Direction<3>::lower_eta()},\n             {Direction<3>::upper_eta()}}});\n    test_brick_construction(\n        creators::Brick{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{create_boundary_condition(), create_boundary_condition()}},\n              {{create_boundary_condition(), create_boundary_condition()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_zeta(), {0, aligned_orientation}},\n             {Direction<3>::upper_zeta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()},\n             {Direction<3>::upper_xi()},\n             {Direction<3>::lower_eta()},\n             {Direction<3>::upper_eta()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"Test periodic in xy\");\n    test_brick_construction(\n        creators::Brick{lower_bound, upper_bound, refinement_level[0],\n                        grid_points[0],\n                        std::array<bool, 3>{{true, true, false}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(), {0, aligned_orientation}},\n             {Direction<3>::upper_xi(), {0, aligned_orientation}},\n             {Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_zeta()}, {Direction<3>::upper_zeta()}}});\n    test_brick_construction(\n        creators::Brick{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{create_boundary_condition(), create_boundary_condition()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(), {0, aligned_orientation}},\n             {Direction<3>::upper_xi(), {0, aligned_orientation}},\n             {Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_zeta()}, {Direction<3>::upper_zeta()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"Test periodic in yz\");\n    const creators::Brick periodic_yz_brick{\n        lower_bound, upper_bound, refinement_level[0], grid_points[0],\n        std::array<bool, 3>{{false, true, true}}};\n    test_brick_construction(\n        periodic_yz_brick, lower_bound, upper_bound, grid_points,\n        refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}},\n             {Direction<3>::lower_zeta(), {0, aligned_orientation}},\n             {Direction<3>::upper_zeta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{{\n            {Direction<3>::lower_xi()},\n            {Direction<3>::upper_xi()},\n        }});\n    test_brick_construction(\n        creators::Brick{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{create_boundary_condition(), create_boundary_condition()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}},\n             {Direction<3>::lower_zeta(), {0, aligned_orientation}},\n             {Direction<3>::upper_zeta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()}, {Direction<3>::upper_xi()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"Test periodic in xz\");\n    const creators::Brick periodic_xz_brick{\n        lower_bound, upper_bound, refinement_level[0], grid_points[0],\n        std::array<bool, 3>{{true, false, true}}};\n    test_brick_construction(\n        periodic_xz_brick, lower_bound, upper_bound, grid_points,\n        refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(), {0, aligned_orientation}},\n             {Direction<3>::upper_xi(), {0, aligned_orientation}},\n             {Direction<3>::lower_zeta(), {0, aligned_orientation}},\n             {Direction<3>::upper_zeta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_eta()}, {Direction<3>::upper_eta()}}});\n    test_brick_construction(\n        creators::Brick{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{create_boundary_condition(), create_boundary_condition()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(), {0, aligned_orientation}},\n             {Direction<3>::upper_xi(), {0, aligned_orientation}},\n             {Direction<3>::lower_zeta(), {0, aligned_orientation}},\n             {Direction<3>::upper_zeta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_eta()}, {Direction<3>::upper_eta()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"Test periodic in xyz\");\n    const creators::Brick periodic_xyz_brick{\n        lower_bound, upper_bound, refinement_level[0], grid_points[0],\n        std::array<bool, 3>{{true, true, true}}};\n    test_brick_construction(\n        periodic_xyz_brick, lower_bound, upper_bound, grid_points,\n        refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(), {0, aligned_orientation}},\n             {Direction<3>::upper_xi(), {0, aligned_orientation}},\n             {Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}},\n             {Direction<3>::lower_zeta(), {0, aligned_orientation}},\n             {Direction<3>::upper_zeta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{{}});\n    test_brick_construction(\n        creators::Brick{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(), {0, aligned_orientation}},\n             {Direction<3>::upper_xi(), {0, aligned_orientation}},\n             {Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}},\n             {Direction<3>::lower_zeta(), {0, aligned_orientation}},\n             {Direction<3>::upper_zeta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{{}}, {}, {}, true);\n  }\n\n  // Test serialization of the map\n  creators::register_derived_with_charm();\n\n  const auto base_map =\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                   Affine{-1., 1., lower_bound[2], upper_bound[2]}});\n  are_maps_equal(make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n                     Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                              Affine{-1., 1., lower_bound[2], upper_bound[2]}}),\n                 *serialize_and_deserialize(base_map));\n\n  {\n    INFO(\"Parse error tests\");\n    const TestHelpers::domain::BoundaryConditions::TestNoneBoundaryCondition<3>\n        none_bc{};\n    CHECK_THROWS_WITH(\n        creators::Brick(\n            lower_bound, upper_bound, refinement_level[0], grid_points[0],\n            {{{{none_bc.get_clone(), none_bc.get_clone()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}}}},\n            {}, nullptr, Options::Context{false, {}, 1, 1}),\n        Catch::Matchers::ContainsSubstring(\n            \"None boundary condition is not supported. If you would like an \"\n            \"outflow-type boundary condition, you must use that.\"));\n  }\n}\n\nvoid test_brick_factory() {\n  const std::string boundary_conditions{\n      \"  BoundaryConditions:\\n\"\n      \"    - TestBoundaryCondition:\\n\"\n      \"        Direction: upper-zeta\\n\"\n      \"        BlockId: 2\\n\"\n      \"    - TestBoundaryCondition:\\n\"\n      \"        Direction: upper-zeta\\n\"\n      \"        BlockId: 2\\n\"\n      \"    - TestBoundaryCondition:\\n\"\n      \"        Direction: upper-zeta\\n\"\n      \"        BlockId: 2\\n\"};\n  {\n    INFO(\"Brick factory time independent, no boundary condition\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<3, domain::creators::Brick>>(\n        \"Brick:\\n\"\n        \"  LowerBound: [0,0,0]\\n\"\n        \"  UpperBound: [1,2,3]\\n\"\n        \"  Distribution: [Linear, Linear, Linear]\\n\"\n        \"  IsPeriodicIn: [True,False,True]\\n\"\n        \"  InitialGridPoints: [3,4,3]\\n\"\n        \"  InitialRefinement: [2,3,2]\\n\"\n        \"  TimeDependence: None\\n\");\n    const auto* brick_creator =\n        dynamic_cast<const creators::Brick*>(domain_creator.get());\n    test_brick_construction(\n        *brick_creator, {{0., 0., 0.}}, {{1., 2., 3.}}, {{{3, 4, 3}}},\n        {{{2, 3, 2}}},\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::upper_xi(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::lower_zeta(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::upper_zeta(),\n              {0, OrientationMap<3>::create_aligned()}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_eta()}, {Direction<3>::upper_eta()}}});\n  }\n  {\n    INFO(\"Brick factory time independent, with boundary condition\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<3, domain::creators::Brick>>(\n        \"Brick:\\n\"\n        \"  LowerBound: [0,0,0]\\n\"\n        \"  UpperBound: [1,2,3]\\n\"\n        \"  Distribution: [Linear, Linear, Linear]\\n\"\n        \"  InitialGridPoints: [3,4,3]\\n\"\n        \"  InitialRefinement: [2,3,2]\\n\"\n        \"  TimeDependence: None\\n\" +\n        boundary_conditions);\n    const auto* brick_creator =\n        dynamic_cast<const creators::Brick*>(domain_creator.get());\n    test_brick_construction(*brick_creator, {{0., 0., 0.}}, {{1., 2., 3.}},\n                            {{{3, 4, 3}}}, {{{2, 3, 2}}}, {{}},\n                            std::vector<std::unordered_set<Direction<3>>>{\n                                {{Direction<3>::lower_xi()},\n                                 {Direction<3>::upper_xi()},\n                                 {Direction<3>::lower_eta()},\n                                 {Direction<3>::upper_eta()},\n                                 {Direction<3>::lower_zeta()},\n                                 {Direction<3>::upper_zeta()}}},\n                            {}, {}, true);\n  }\n  {\n    INFO(\"Brick factory time dependent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<3, domain::creators::Brick>>(\n        \"Brick:\\n\"\n        \"  LowerBound: [0,0,0]\\n\"\n        \"  UpperBound: [1,2,3]\\n\"\n        \"  Distribution: [Linear, Linear, Linear]\\n\"\n        \"  IsPeriodicIn: [True,False,True]\\n\"\n        \"  InitialGridPoints: [3,4,3]\\n\"\n        \"  InitialRefinement: [2,3,2]\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3, -0.3, 0.5]\\n\");\n    const auto* brick_creator =\n        dynamic_cast<const creators::Brick*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, -0.3, 0.5}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_brick_construction(\n        *brick_creator, {{0., 0., 0.}}, {{1., 2., 3.}}, {{{3, 4, 3}}},\n        {{{2, 3, 2}}},\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::upper_xi(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::lower_zeta(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::upper_zeta(),\n              {0, OrientationMap<3>::create_aligned()}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_eta()}, {Direction<3>::upper_eta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{3, 0.0}, velocity, {3, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation3D{f_of_t_name}));\n    // with expiration times\n    test_brick_construction(\n        *brick_creator, {{0., 0., 0.}}, {{1., 2., 3.}}, {{{3, 4, 3}}},\n        {{{2, 3, 2}}},\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_xi(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::upper_xi(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::lower_zeta(),\n              {0, OrientationMap<3>::create_aligned()}},\n             {Direction<3>::upper_zeta(),\n              {0, OrientationMap<3>::create_aligned()}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_eta()}, {Direction<3>::upper_eta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{3, 0.0}, velocity, {3, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation3D{f_of_t_name}),\n        {}, initial_expiration_times);\n  }\n  {\n    INFO(\"Brick factory time dependent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<3, domain::creators::Brick>>(\n        \"Brick:\\n\"\n        \"  LowerBound: [0,0,0]\\n\"\n        \"  UpperBound: [1,2,3]\\n\"\n        \"  Distribution: [Linear, Linear, Linear]\\n\"\n        \"  InitialGridPoints: [3,4,3]\\n\"\n        \"  InitialRefinement: [2,3,2]\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3, -0.3, 0.5]\\n\" +\n        boundary_conditions);\n    const auto* brick_creator =\n        dynamic_cast<const creators::Brick*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, -0.3, 0.5}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_brick_construction(\n        *brick_creator, {{0., 0., 0.}}, {{1., 2., 3.}}, {{{3, 4, 3}}},\n        {{{2, 3, 2}}}, {{}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()},\n             {Direction<3>::upper_xi()},\n             {Direction<3>::lower_eta()},\n             {Direction<3>::upper_eta()},\n             {Direction<3>::lower_zeta()},\n             {Direction<3>::upper_zeta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{3, 0.0}, velocity, {3, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation3D{f_of_t_name}),\n        true);\n    // with expiration times\n    test_brick_construction(\n        *brick_creator, {{0., 0., 0.}}, {{1., 2., 3.}}, {{{3, 4, 3}}},\n        {{{2, 3, 2}}}, {{}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()},\n             {Direction<3>::upper_xi()},\n             {Direction<3>::lower_eta()},\n             {Direction<3>::upper_eta()},\n             {Direction<3>::lower_zeta()},\n             {Direction<3>::upper_zeta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{3, 0.0}, velocity, {3, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation3D{f_of_t_name}),\n        true, initial_expiration_times);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.Brick\", \"[Domain][Unit]\") {\n  test_brick();\n  test_brick_factory();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_CartoonCylinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/CartoonCylinder.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n\nnamespace domain {\nnamespace {\nusing Affine = CoordinateMaps::Affine;\nusing Identity1D = CoordinateMaps::Identity<1>;\nusing cartoon_cylinder_map =\n    CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity1D>;\nusing Translation3D = CoordinateMaps::TimeDependent::Translation<3>;\n\ntemplate <typename... FuncsOfTime>\nvoid test_cylinder_construction(\n    const creators::CartoonCylinder& cylinder,\n    const std::array<double, 2>& lower_bounds,\n    const std::array<double, 2>& upper_bounds,\n    const std::vector<std::array<size_t, 3>>& expected_extents,\n    const std::vector<std::array<size_t, 3>>& expected_refinement_level,\n    const std::vector<DirectionMap<3, BlockNeighbors<3>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<3>>>&\n        expected_external_boundaries,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time = {},\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::Grid, Frame::Inertial, 3>>>& expected_grid_to_inertial_maps = {},\n    const bool expect_boundary_conditions = false,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      cylinder, expect_boundary_conditions);\n  CHECK(cylinder.grid_anchors().empty());\n\n  CHECK(cylinder.block_names() == std::vector<std::string>{\"CartoonCylinder\"});\n  const auto block_groups = cylinder.block_groups();\n  CHECK(block_groups.contains(\"CartoonCylinder\"));\n  CHECK(block_groups.at(\"CartoonCylinder\") ==\n        std::unordered_set<std::string>{\"CartoonCylinder\"});\n\n  CHECK(cylinder.initial_extents() == expected_extents);\n  CHECK(cylinder.initial_refinement_levels() == expected_refinement_level);\n\n  test_domain_construction(\n      domain, expected_block_neighbors, expected_external_boundaries,\n      make_vector(\n          make_coordinate_map_base<\n              Frame::BlockLogical,\n              tmpl::conditional_t<sizeof...(FuncsOfTime) == 0, Frame::Inertial,\n                                  Frame::Grid>>(cartoon_cylinder_map{\n              Affine{-1., 1., lower_bounds[0], upper_bounds[0]},\n              Affine{-1., 1., lower_bounds[1], upper_bounds[1]},\n              Identity1D{}})),\n      10.0, cylinder.functions_of_time(), expected_grid_to_inertial_maps);\n  TestHelpers::domain::creators::test_functions_of_time(\n      cylinder, expected_functions_of_time, initial_expiration_times);\n}\n\nvoid test_cylinder() {\n  INFO(\"CartoonCylinder\");\n  const std::vector<std::array<size_t, 2>> grid_points_creator{{{4, 6}}};\n  const std::vector<std::array<size_t, 2>> refinement_level_creator{{{3, 2}}};\n  const std::vector<std::array<size_t, 3>> grid_points{{{4, 6, 1}}};\n  const std::vector<std::array<size_t, 3>> refinement_level{{{3, 2, 0}}};\n  const std::array<double, 2> lower_bounds{{0.0, -3.0}};\n  const std::array<double, 2> invalid_lower_bounds{{-0.3, 1.0}};\n  const std::array<double, 2> upper_bounds{{0.8, 5.0}};\n  const OrientationMap<3> aligned_orientation =\n      OrientationMap<3>::create_aligned();\n  const TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>\n      test_bc{Direction<3>::lower_xi(), 0};\n  const TestHelpers::domain::BoundaryConditions::TestPeriodicBoundaryCondition<\n      3>\n      periodic_bc{};\n  const TestHelpers::domain::BoundaryConditions::TestNoneBoundaryCondition<3>\n      none_bc{};\n  // to avoid clang-tidy complaining about memory leak\n  using bc_type = std::array<\n      std::array<std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>,\n                 2>,\n      2>;\n\n  {\n    INFO(\"CartoonCylinder, non-periodic\");\n    std::vector<DirectionMap<\n        3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n        expected_boundary_conditions{1};\n    for (const auto& direction : Direction<3>::all_directions()) {\n      expected_boundary_conditions[0][direction] = test_bc.get_clone();\n    }\n    bc_type bc_array{{{{test_bc.get_clone(), test_bc.get_clone()}},\n                      {{test_bc.get_clone(), test_bc.get_clone()}}}};\n    test_cylinder_construction(\n        creators::CartoonCylinder{lower_bounds,\n                                  upper_bounds,\n                                  refinement_level_creator[0],\n                                  grid_points_creator[0],\n                                  {},\n                                  nullptr,\n                                  std::move(bc_array)},\n        lower_bounds, upper_bounds, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{{}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()},\n             {Direction<3>::upper_xi()},\n             {Direction<3>::lower_eta()},\n             {Direction<3>::upper_eta()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"CartoonCylinder, periodic in y\");\n    bc_type bc_array{{{{test_bc.get_clone(), test_bc.get_clone()}},\n                      {{periodic_bc.get_clone(), periodic_bc.get_clone()}}}};\n    test_cylinder_construction(\n        creators::CartoonCylinder{lower_bounds,\n                                  upper_bounds,\n                                  refinement_level_creator[0],\n                                  grid_points_creator[0],\n                                  {},\n                                  nullptr,\n                                  std::move(bc_array)},\n        lower_bounds, upper_bounds, grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::lower_eta(), {0, aligned_orientation}},\n             {Direction<3>::upper_eta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi()}, {Direction<3>::upper_xi()}}},\n        {}, {}, true);\n  }\n  bc_type none_bc_arr{{{{none_bc.get_clone(), none_bc.get_clone()}},\n                       {{test_bc.get_clone(), test_bc.get_clone()}}}};\n  CHECK_THROWS_WITH(\n      creators::CartoonCylinder(\n          lower_bounds, upper_bounds, refinement_level_creator[0],\n          grid_points_creator[0], {}, nullptr, std::move(none_bc_arr),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\"));\n  bc_type periodic_bc_arr{{{{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n                           {{test_bc.get_clone(), test_bc.get_clone()}}}};\n  CHECK_THROWS_WITH(\n      creators::CartoonCylinder(\n          lower_bounds, upper_bounds, refinement_level_creator[0],\n          grid_points_creator[0], {}, nullptr, std::move(periodic_bc_arr),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions in the x dimension.\"));\n  bc_type test_bc_arr{{{{test_bc.get_clone(), test_bc.get_clone()}},\n                       {{test_bc.get_clone(), test_bc.get_clone()}}}};\n  CHECK_THROWS_WITH(\n      creators::CartoonCylinder(\n          invalid_lower_bounds, upper_bounds, refinement_level_creator[0],\n          grid_points_creator[0], {}, nullptr, std::move(test_bc_arr),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The lower bound for the x dimension must be >= 0\"));\n}\n\nvoid test_cylinder_factory() {\n  // For non-periodic domains:\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      expected_boundary_conditions{1};\n  for (const auto& direction : Direction<3>::all_directions()) {\n    expected_boundary_conditions[0][direction] = std::make_unique<\n        TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n        Direction<3>::lower_xi(), 0);\n  }\n  const std::vector<std::unordered_set<Direction<3>>>\n      expected_external_boundaries{\n          {Direction<3>::lower_xi(), Direction<3>::upper_xi(),\n           Direction<3>::lower_eta(), Direction<3>::upper_eta(),\n           Direction<3>::lower_zeta(), Direction<3>::upper_zeta()}};\n\n  // for periodic domains:\n  const std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_neighbors{\n      {{Direction<3>::lower_eta(), {0, OrientationMap<3>::create_aligned()}},\n       {Direction<3>::upper_eta(), {0, OrientationMap<3>::create_aligned()}}}};\n\n  {\n    INFO(\"CartoonCylinder factory time independent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                3, domain::creators::CartoonCylinder>>(\n        \"CartoonCylinder:\\n\"\n        \"  LowerBounds: [0,0]\\n\"\n        \"  UpperBounds: [1,2]\\n\"\n        \"  Distributions: [Linear, Linear]\\n\"\n        \"  InitialGridPoints: [3,4]\\n\"\n        \"  InitialRefinement: [2,3]\\n\"\n        \"  TimeDependence: None\\n\"\n        \"  BoundaryConditions:\\n\"\n        \"    - TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\"\n        \"    - TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\");\n    const auto* cylinder_creator =\n        dynamic_cast<const creators::CartoonCylinder*>(domain_creator.get());\n    test_cylinder_construction(\n        *cylinder_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4, 1}}}, {{{2, 3, 0}}},\n        {{}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi(), Direction<3>::upper_xi(),\n              Direction<3>::lower_eta(), Direction<3>::upper_eta()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"CartoonCylinder factory time dependent, with boundary conditions\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                3, domain::creators::CartoonCylinder>>(\n        \"CartoonCylinder:\\n\"\n        \"  LowerBounds: [0,0]\\n\"\n        \"  UpperBounds: [1,2]\\n\"\n        \"  Distributions: [Linear, Linear]\\n\"\n        \"  InitialGridPoints: [3,4]\\n\"\n        \"  InitialRefinement: [2,3]\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3, -0.3, 0.5]\\n\"\n        \"  BoundaryConditions:\\n\"\n        \"    - TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\"\n        \"    - TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\");\n    const auto* cylinder_creator =\n        dynamic_cast<const creators::CartoonCylinder*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, -0.3, 0.5}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_cylinder_construction(\n        *cylinder_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4, 1}}}, {{{2, 3, 0}}},\n        {{}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi(), Direction<3>::upper_xi(),\n              Direction<3>::lower_eta(), Direction<3>::upper_eta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{3, 0.0}, velocity, {3, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation3D{f_of_t_name}),\n        true);\n    // with expiration times\n    test_cylinder_construction(\n        *cylinder_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4, 1}}}, {{{2, 3, 0}}},\n        {{}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {{Direction<3>::lower_xi(), Direction<3>::upper_xi(),\n              Direction<3>::lower_eta(), Direction<3>::upper_eta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{3, 0.0}, velocity, {3, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation3D{f_of_t_name}),\n        true, initial_expiration_times);\n  }\n}  // namespace domain\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.CartoonCylinder\", \"[Domain][Unit]\") {\n  test_cylinder();\n  test_cylinder_factory();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_CartoonSphere1D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/CartoonSphere1D.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition(const bool outer) {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      outer ? Direction<3>::upper_xi() : Direction<3>::lower_xi(), 50);\n}\n\ntemplate <typename T>\nstd::string stringize(const std::vector<T>& t) {\n  std::string result = get_output(t).replace(0, 1, \"[\");\n  return result.replace(result.length() - 1, 1, \"]\");\n}\n\nstd::string option_string(\n    const double inner_radial_bound, const double outer_radial_bound,\n    const size_t radial_refinement, const size_t radial_extents,\n    const std::vector<double>& radial_partitioning,\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution,\n    const bool time_dependent, const bool with_boundary_conditions) {\n  const std::string time_dependent_option =\n      time_dependent ? \"  TimeDependence:\\n\"\n                       \"    UniformTranslation:\\n\"\n                       \"      InitialTime: 1.0\\n\"\n                       \"      Velocity: [2.3, -0.3, 0.5]\\n\"\n                     : \"  TimeDependence: None\\n\";\n  const std::string inner_bc_option = with_boundary_conditions\n                                          ? \"  InnerBoundaryCondition:\\n\"\n                                            \"    TestBoundaryCondition:\\n\"\n                                            \"      Direction: lower-xi\\n\"\n                                            \"      BlockId: 50\\n\"\n                                          : \"\";\n  const std::string outer_bc_option = with_boundary_conditions\n                                          ? \"  OuterBoundaryCondition:\\n\"\n                                            \"    TestBoundaryCondition:\\n\"\n                                            \"      Direction: upper-xi\\n\"\n                                            \"      BlockId: 50\\n\"\n                                          : \"\";\n  return \"CartoonSphere1D:\\n\"\n         \"  InnerRadius: \" +\n         std::to_string(inner_radial_bound) +\n         \"\\n\"\n         \"  OuterRadius: \" +\n         std::to_string(outer_radial_bound) + \"\\n\" +\n         \"  InitialRadialRefinement: \" + std::to_string(radial_refinement) +\n         \"\\n\"\n         \"  InitialNumberOfRadialGridPoints: \" +\n         std::to_string(radial_extents) +\n         \"\\n\"\n         \"  RadialPartitioning: \" +\n         stringize(radial_partitioning) +\n         \"\\n\"\n         \"  RadialDistributions: \" +\n         (radial_distribution.size() == 1 ? get_output(radial_distribution[0])\n                                          : stringize(radial_distribution)) +\n         \"\\n\" + time_dependent_option + inner_bc_option + outer_bc_option;\n}\n\nvoid test_parse_errors() {\n  INFO(\"CartoonSphere1D check throws\");\n  const double lower_bound = 1.0;\n  const double upper_bound = 2.0;\n  const size_t radial_refinement = 2;\n  const std::vector<size_t> radial_refinement_high = {{1, 1}};\n  const size_t radial_extents = 5;\n  const std::vector<size_t> radial_extents_high = {{2, 3}};\n  const std::vector<double> radial_partitioning = {};\n  const std::vector<double> radial_partitioning_unordered = {\n      {1.5 * lower_bound, 1.1 * lower_bound}};\n  const std::vector<double> radial_partitioning_low = {\n      {0.5 * lower_bound, 1.1 * lower_bound}};\n  const std::vector<double> radial_partitioning_high = {\n      {2.1 * upper_bound, 2.2 * upper_bound}};\n  const std::vector<domain::CoordinateMaps::Distribution> radial_distribution{\n      domain::CoordinateMaps::Distribution::Linear};\n  const std::vector<domain::CoordinateMaps::Distribution>\n      radial_distribution_too_many{\n          domain::CoordinateMaps::Distribution::Linear,\n          domain::CoordinateMaps::Distribution::Logarithmic};\n\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, 0.5 * lower_bound, radial_refinement, radial_extents,\n          radial_partitioning, radial_distribution, nullptr, nullptr, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Inner radius must be smaller than outer radius\"));\n\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning_unordered, radial_distribution, nullptr, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify radial partitioning in ascending order.\"));\n\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning_low, radial_distribution, nullptr, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"First radial partition must be larger than the inner\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning_high, radial_distribution, nullptr, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Last radial partition must be smaller than the outer\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning, radial_distribution_too_many, nullptr, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify a 'RadialDistribution' for every spherical shell. You\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement_high, radial_extents,\n          radial_partitioning, radial_distribution, nullptr, nullptr, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"must be the same size as RadialDistributions \"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents_high,\n          radial_partitioning, radial_distribution, nullptr, nullptr, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"must be the same size as RadialDistributions \"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning, radial_distribution, nullptr,\n          create_boundary_condition(false), nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Must specify either both inner and outer boundary conditions \"\n          \"or neither.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning, radial_distribution, nullptr,\n          create_boundary_condition(false),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions with CartoonSphere1D\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning, radial_distribution, nullptr,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          create_boundary_condition(true), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions with CartoonSphere1D\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning, radial_distribution, nullptr,\n          create_boundary_condition(false),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CartoonSphere1D(\n          lower_bound, upper_bound, radial_refinement, radial_extents,\n          radial_partitioning, radial_distribution, nullptr,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          create_boundary_condition(true), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n}\n\nvoid test_cartoon_sphere_construction(\n    const domain::creators::CartoonSphere1D& cartoon_sphere,\n    const double lower_bound, const double upper_bound,\n    const std::vector<double>& radial_partitioning = {},\n    const bool expect_boundary_conditions = true,\n    const std::vector<double>& times = {0.},\n    const std::array<double, 3>& velocity = {{0., 0., 0.}}) {\n  // check consistency of domain\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      cartoon_sphere, expect_boundary_conditions, false, times);\n\n  const auto& blocks = domain.blocks();\n  const auto block_names = cartoon_sphere.block_names();\n  const size_t num_blocks = blocks.size();\n  CAPTURE(num_blocks);\n  const auto all_boundary_conditions =\n      cartoon_sphere.external_boundary_conditions();\n  const auto functions_of_time = cartoon_sphere.functions_of_time();\n\n  // Check total number of external boundaries\n  const size_t num_shells = radial_partitioning.size() + 1;\n  CHECK(num_blocks == num_shells);\n  const size_t num_external_boundaries =\n      alg::accumulate(blocks, 0_st, [](const size_t count, const auto& block) {\n        return count + block.external_boundaries().size();\n      });\n  CHECK(num_external_boundaries == 2);\n\n  std::vector<double> expected_radii = radial_partitioning;\n  expected_radii.insert(expected_radii.begin(), lower_bound);\n  expected_radii.emplace_back(upper_bound);\n\n  for (size_t block_id = 0; block_id < num_blocks; ++block_id) {\n    CAPTURE(block_id);\n    const auto& block = blocks[block_id];\n    const ElementMap<3, Frame::Inertial> inertial_element_map{\n        ElementId<3>{block_id}, block};\n    {\n      INFO(\"Radius of random point on lower face\");\n      const tnsr::I<double, 3, Frame::ElementLogical> x_logical{{{-1.0, 0, 0}}};\n      for (const double current_time : times) {\n        CAPTURE(current_time);\n        auto x_inertial =\n            inertial_element_map(x_logical, current_time, functions_of_time);\n        const double delta_t = current_time - 1.0;\n        for (size_t i = 0; i < 3; ++i) {\n          x_inertial.get(i) -= gsl::at(velocity, i) * delta_t;\n        }\n        CHECK(get(magnitude(x_inertial)) == approx(expected_radii[block_id]));\n      }\n    }\n    {\n      INFO(\"Radius of random point on upper face\");\n      const tnsr::I<double, 3, Frame::ElementLogical> x_logical{{{1.0, 0, 0}}};\n      for (const double current_time : times) {\n        CAPTURE(current_time);\n        auto x_inertial =\n            inertial_element_map(x_logical, current_time, functions_of_time);\n        const double delta_t = current_time - 1.0;\n        for (size_t i = 0; i < 3; ++i) {\n          x_inertial.get(i) -= gsl::at(velocity, i) * delta_t;\n        }\n        CHECK(get(magnitude(x_inertial)) ==\n              approx(expected_radii[block_id + 1]));\n      }\n    }\n    {\n      INFO(\"External boundaries\");\n      const auto& external_boundaries = block.external_boundaries();\n      if (num_blocks == 1) {\n        CHECK(external_boundaries.size() == 2);\n        CHECK(alg::found(external_boundaries, Direction<3>::lower_xi()));\n        CHECK(alg::found(external_boundaries, Direction<3>::upper_xi()));\n      } else if (block_id == 0) {\n        CHECK(external_boundaries.size() == 1);\n        CHECK(alg::found(external_boundaries, Direction<3>::lower_xi()));\n      } else if (block_id == num_blocks - 1) {\n        CHECK(external_boundaries.size() == 1);\n        CHECK(alg::found(external_boundaries, Direction<3>::upper_xi()));\n      } else {\n        CHECK(external_boundaries.empty());\n      }\n    }\n    if (expect_boundary_conditions) {\n      INFO(\"Boundary conditions\");\n      const auto& boundary_conditions = all_boundary_conditions[block_id];\n      for (const auto& direction : block.external_boundaries()) {\n        CAPTURE(direction);\n        const auto& boundary_condition =\n            dynamic_cast<const TestHelpers::domain::BoundaryConditions::\n                             TestBoundaryCondition<3>&>(\n                *boundary_conditions.at(direction));\n        CHECK(boundary_condition.direction() == direction);\n      }\n    }\n  }\n}\n\ntemplate <typename Generator>\nvoid test_sphere(const gsl::not_null<Generator*> gen) {\n  const double lower_bound = 1.0;\n  const double upper_bound = 2.0;\n  const size_t radial_refinement = 3;\n  const size_t radial_extents = 5;\n  const double upper_minus_lower = upper_bound - lower_bound;\n  const std::array<std::vector<double>, 3> radial_partitioning{\n      {{},\n       {0.5 * (lower_bound + upper_bound)},\n       {lower_bound + 0.3 * upper_minus_lower,\n        lower_bound + 0.6 * upper_minus_lower}}};\n  const std::array<std::vector<domain::CoordinateMaps::Distribution>, 3>\n      radial_distributions{{{domain::CoordinateMaps::Distribution::Linear},\n                            {domain::CoordinateMaps::Distribution::Linear,\n                             domain::CoordinateMaps::Distribution::Logarithmic},\n                            {domain::CoordinateMaps::Distribution::Linear}}};\n\n  const std::array<double, 3> velocity{{2.3, -0.3, 0.5}};\n  const std::vector<double> times{1., 10.};\n  for (auto [index, time_dependent, with_boundary_conditions] :\n       random_sample<5>(\n           cartesian_product(make_array(0_st, 1_st, 2_st),\n                             make_array(true, false), make_array(true, false)),\n           gen)) {\n    CAPTURE(time_dependent);\n    CAPTURE(with_boundary_conditions);\n    CAPTURE(gsl::at(radial_partitioning, index));\n    CAPTURE(gsl::at(radial_distributions, index));\n    domain::creators::CartoonSphere1D::RadialDistributions::type\n        radial_distributions_variant;\n    if (gsl::at(radial_distributions, index).size() == 1) {\n      radial_distributions_variant = gsl::at(radial_distributions, index)[0];\n    } else {\n      radial_distributions_variant = gsl::at(radial_distributions, index);\n    }\n\n    domain::creators::CartoonSphere1D::TimeDependence::type time_dependency{};\n\n    auto translation_velocity = std::array<double, 3>{{0., 0., 0.}};\n\n    if (time_dependent) {\n      time_dependency = std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3, 0>>(\n          1.0, velocity);\n      translation_velocity = velocity;\n    }\n\n    const domain::creators::CartoonSphere1D cartoon_sphere{\n        lower_bound,\n        upper_bound,\n        radial_refinement,\n        radial_extents,\n        gsl::at(radial_partitioning, index),\n        radial_distributions_variant,\n        std::move(time_dependency),\n        with_boundary_conditions ? create_boundary_condition(false) : nullptr,\n        with_boundary_conditions ? create_boundary_condition(true) : nullptr};\n    test_cartoon_sphere_construction(\n        cartoon_sphere, lower_bound, upper_bound,\n        gsl::at(radial_partitioning, index), with_boundary_conditions,\n        time_dependent ? times : std::vector<double>{1.}, translation_velocity);\n    TestHelpers::domain::creators::test_creation(\n        option_string(lower_bound, upper_bound, radial_refinement,\n                      radial_extents, gsl::at(radial_partitioning, index),\n                      gsl::at(radial_distributions, index), time_dependent,\n                      with_boundary_conditions),\n        cartoon_sphere, with_boundary_conditions);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.CartoonSphere1D\", \"[Domain][Unit]\") {\n  MAKE_GENERATOR(gen);\n  domain::creators::time_dependence::register_derived_with_charm();\n  test_parse_errors();\n  test_sphere(make_not_null(&gen));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_CartoonSphere2D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <numpy/npy_common.h>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/CartoonSphere2D.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain {\nnamespace {\n\ntemplate <typename... FuncsOfTime>\nvoid test_sphere_construction(\n    const creators::CartoonSphere2D& sphere, const double inner_radius,\n    const double outer_radius,\n    const std::vector<std::array<size_t, 2>>& expected_refinement_levels_param,\n    const std::vector<std::array<size_t, 2>>& expected_extents_param,\n    const std::vector<double>& expected_radial_partitioning,\n    const bool use_equiangular_map,\n    const std::variant<domain::creators::detail::Excision,\n                       domain::creators::detail::InnerSquare>\n        interior,\n    const bool expect_boundary_conditions = false,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time = {},\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::Grid, Frame::Inertial, 3>>>& expected_grid_to_inertial_maps = {},\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      sphere, expect_boundary_conditions, false, std::vector<double>{5.0});\n  const bool fill_interior =\n      std::holds_alternative<domain::creators::detail::InnerSquare>(interior);\n  const size_t num_shells = expected_radial_partitioning.size() + 1;\n  const size_t num_blocks = num_shells * 3 + (fill_interior ? 1 : 0);\n  CHECK(sphere.grid_anchors().empty());\n\n  const auto block_groups = sphere.block_groups();\n  std::vector<std::string> expected_block_names{};\n  expected_block_names.reserve(num_blocks);\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      expected_block_groups{num_shells};\n  for (size_t i = 0; i < num_shells; ++i) {\n    const std::string shell = \"Shell\" + std::to_string(i);\n    expected_block_groups[shell];\n    expected_block_names.push_back(shell + \"_LowerY\");\n    expected_block_groups[shell].insert(expected_block_names.back());\n    expected_block_names.push_back(shell + \"_UpperX\");\n    expected_block_groups[shell].insert(expected_block_names.back());\n    expected_block_names.push_back(shell + \"_UpperY\");\n    expected_block_groups[shell].insert(expected_block_names.back());\n  }\n  if (fill_interior) {\n    const std::string shell = \"Shell\" + std::to_string(num_shells - 1);\n    expected_block_names.emplace_back(shell + \"_HalfSquare\");\n    expected_block_groups[shell].insert(expected_block_names.back());\n  }\n  CHECK(sphere.block_names() == expected_block_names);\n  for (auto& [key, value] : expected_block_groups) {\n    CHECK(block_groups.at(key) == value);\n  }\n\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n  const OrientationMap<3> turn_ccw(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_eta(), Direction<3>::upper_xi(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> half_turn(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> turn_cw(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n       Direction<3>::upper_zeta()}});\n  std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_neighbors{\n      num_blocks};\n  for (size_t i = 0; i < num_shells; ++i) {\n    // between this shell\n    expected_neighbors[3 * i + 0].emplace(std::pair(\n        Direction<3>::upper_xi(), BlockNeighbors<3>(3 * i + 1, half_turn)));\n\n    expected_neighbors[3 * i + 1].emplace(std::pair(\n        Direction<3>::upper_xi(), BlockNeighbors<3>(3 * i + 0, half_turn)));\n    expected_neighbors[3 * i + 1].emplace(std::pair(\n        Direction<3>::lower_xi(), BlockNeighbors<3>(3 * i + 2, aligned)));\n\n    expected_neighbors[3 * i + 2].emplace(std::pair(\n        Direction<3>::upper_xi(), BlockNeighbors<3>(3 * i + 1, aligned)));\n\n    // +r direction\n    if (i != 0) {\n      expected_neighbors[3 * i + 0].emplace(\n          std::pair(Direction<3>::lower_eta(),\n                    BlockNeighbors<3>((i - 1) * 3 + 0, aligned)));\n      expected_neighbors[3 * i + 1].emplace(\n          std::pair(Direction<3>::upper_eta(),\n                    BlockNeighbors<3>((i - 1) * 3 + 1, aligned)));\n      expected_neighbors[3 * i + 2].emplace(\n          std::pair(Direction<3>::upper_eta(),\n                    BlockNeighbors<3>((i - 1) * 3 + 2, aligned)));\n    }\n    // -r direction\n    if (i != num_shells - 1) {\n      expected_neighbors[3 * i + 0].emplace(\n          std::pair(Direction<3>::upper_eta(),\n                    BlockNeighbors<3>((i + 1) * 3 + 0, aligned)));\n      expected_neighbors[3 * i + 1].emplace(\n          std::pair(Direction<3>::lower_eta(),\n                    BlockNeighbors<3>((i + 1) * 3 + 1, aligned)));\n      expected_neighbors[3 * i + 2].emplace(\n          std::pair(Direction<3>::lower_eta(),\n                    BlockNeighbors<3>((i + 1) * 3 + 2, aligned)));\n    } else if (fill_interior) {\n      expected_neighbors[3 * i + 0].emplace(std::pair(\n          Direction<3>::upper_eta(), BlockNeighbors<3>(3 * i + 3, aligned)));\n      expected_neighbors[3 * i + 1].emplace(std::pair(\n          Direction<3>::lower_eta(), BlockNeighbors<3>(3 * i + 3, turn_ccw)));\n      expected_neighbors[3 * i + 2].emplace(std::pair(\n          Direction<3>::lower_eta(), BlockNeighbors<3>(3 * i + 3, aligned)));\n\n      expected_neighbors[3 * i + 3].emplace(std::pair(\n          Direction<3>::lower_eta(), BlockNeighbors<3>(3 * i + 0, aligned)));\n      expected_neighbors[3 * i + 3].emplace(std::pair(\n          Direction<3>::upper_xi(), BlockNeighbors<3>(3 * i + 1, turn_cw)));\n      expected_neighbors[3 * i + 3].emplace(std::pair(\n          Direction<3>::upper_eta(), BlockNeighbors<3>(3 * i + 2, aligned)));\n    }\n  }\n\n  std::vector<std::unordered_set<Direction<3>>> expected_external_boundaries{\n      num_blocks};\n  expected_external_boundaries[0].emplace(Direction<3>::lower_eta());\n  expected_external_boundaries[1].emplace(Direction<3>::upper_eta());\n  expected_external_boundaries[2].emplace(Direction<3>::upper_eta());\n\n  for (size_t i = 0; i < num_shells; ++i) {\n    expected_external_boundaries[3 * i + 0].emplace(Direction<3>::lower_xi());\n    expected_external_boundaries[3 * i + 2].emplace(Direction<3>::lower_xi());\n  }\n  if (fill_interior) {\n    expected_external_boundaries[num_blocks - 1].emplace(\n        Direction<3>::lower_xi());\n  } else {\n    expected_external_boundaries[(num_shells - 1) * 3 + 0].emplace(\n        Direction<3>::upper_eta());\n    expected_external_boundaries[(num_shells - 1) * 3 + 1].emplace(\n        Direction<3>::lower_eta());\n    expected_external_boundaries[(num_shells - 1) * 3 + 2].emplace(\n        Direction<3>::lower_eta());\n  }\n\n  std::vector<std::array<size_t, 2>> expected_extents_temp{};\n  std::vector<std::array<size_t, 3>> expected_extents{};\n  expected_extents_temp.reserve(num_blocks);\n  expected_extents.reserve(num_blocks);\n  if (fill_interior) {\n    expected_extents_temp.push_back(expected_extents_param[0]);\n  }\n  for (size_t i = 0; i < num_shells; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_extents_temp.push_back(expected_extents_param[i]);\n    }\n  }\n  std::reverse(expected_extents_temp.begin(), expected_extents_temp.end());\n  std::transform(expected_extents_temp.begin(), expected_extents_temp.end(),\n                 std::back_inserter(expected_extents),\n                 [](const std::array<size_t, 2>& arr) -> std::array<size_t, 3> {\n                   return {arr[1], arr[0], 1};\n                 });\n  if (fill_interior) {\n    expected_extents.back()[1] = expected_extents.back()[0];\n  }\n  CHECK(sphere.initial_extents() == expected_extents);\n\n  std::vector<std::array<size_t, 2>> expected_refinement_levels_temp{};\n  std::vector<std::array<size_t, 3>> expected_refinement_levels{};\n  expected_refinement_levels_temp.reserve(num_blocks);\n  expected_refinement_levels.reserve(num_blocks);\n  if (fill_interior) {\n    expected_refinement_levels_temp.push_back(\n        expected_refinement_levels_param[0]);\n  }\n  for (size_t i = 0; i < num_shells; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_refinement_levels_temp.push_back(\n          expected_refinement_levels_param[i]);\n    }\n  }\n  std::reverse(expected_refinement_levels_temp.begin(),\n               expected_refinement_levels_temp.end());\n  size_t n = 0;\n  std::transform(\n      expected_refinement_levels_temp.begin(),\n      expected_refinement_levels_temp.end(),\n      std::back_inserter(expected_refinement_levels),\n      [&n](const std::array<size_t, 2>& arr) -> std::array<size_t, 3> {\n        const size_t shift = (n % 3 == 0 or n % 3 == 2) and arr[1] != 0 ? 1 : 0;\n        ++n;\n        return {arr[1] - shift, arr[0], 0};\n      });\n  if (fill_interior) {\n    expected_refinement_levels.back()[1] =\n        expected_refinement_levels_temp.back()[1];\n    expected_refinement_levels.back()[0] =\n        expected_refinement_levels.back()[1] - 1;\n  }\n  CHECK(sphere.initial_refinement_levels() == expected_refinement_levels);\n\n  using Identity1D = CoordinateMaps::Identity<1>;\n  using Wedge2DMap = CoordinateMaps::Wedge<2>;\n  using Wedge3DPrism =\n      domain::CoordinateMaps::ProductOf2Maps<Wedge2DMap, Identity1D>;\n  using Affine = CoordinateMaps::Affine;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using Equiangular = CoordinateMaps::Equiangular;\n  using Equiangular2D =\n      CoordinateMaps::ProductOf2Maps<Equiangular, Equiangular>;\n  using Rotation3D = CoordinateMaps::DiscreteRotation<3>;\n\n  using TargetFrame = tmpl::conditional_t<sizeof...(FuncsOfTime) == 0,\n                                          Frame::Inertial, Frame::Grid>;\n  std::vector<\n      std::unique_ptr<CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>>\n      coord_maps{};\n  coord_maps.reserve(num_blocks);\n\n  for (size_t i = 0; i < num_shells; ++i) {\n    const bool on_inner = i == num_shells - 1;\n    const double inner_radius_i =\n        on_inner\n            ? inner_radius\n            : expected_radial_partitioning[expected_radial_partitioning.size() -\n                                           1 - i];\n    const double outer_radius_i =\n        i == 0\n            ? outer_radius\n            : expected_radial_partitioning[expected_radial_partitioning.size() -\n                                           i];\n    const double inner_sphericity = fill_interior and on_inner ? 0.0 : 1.0;\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n            Rotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::lower_eta(), Direction<3>::upper_xi(),\n                 Direction<3>::upper_zeta()}}}},\n            Wedge3DPrism{\n                Wedge2DMap{\n                    inner_radius_i, outer_radius_i, inner_sphericity, 1.0,\n                    OrientationMap<2>{std::array<Direction<2>, 2>{\n                        {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                    use_equiangular_map,\n                    domain::CoordinateMaps::Wedge<2>::WedgeHalves::UpperOnly},\n                Identity1D{}}));\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n            Rotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n                 Direction<3>::upper_zeta()}}}},\n            Wedge3DPrism{\n                Wedge2DMap{\n                    inner_radius_i, outer_radius_i, inner_sphericity, 1.0,\n                    OrientationMap<2>{std::array<Direction<2>, 2>{\n                        {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                    use_equiangular_map},\n                Identity1D{}}));\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n            Rotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n                 Direction<3>::upper_zeta()}}}},\n            Wedge3DPrism{\n                Wedge2DMap{\n                    inner_radius_i, outer_radius_i, inner_sphericity, 1.0,\n                    OrientationMap<2>{std::array<Direction<2>, 2>{\n                        {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                    use_equiangular_map,\n                    domain::CoordinateMaps::Wedge<2>::WedgeHalves::LowerOnly},\n                Identity1D{}}));\n  }\n  if (fill_interior) {\n    if (use_equiangular_map) {\n      coord_maps.emplace_back(\n          make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n              CoordinateMaps::ProductOf2Maps<Equiangular2D, Identity1D>{\n                  Equiangular2D{\n                      Equiangular(-3.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                                  inner_radius / sqrt(2.0)),\n                      Equiangular(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                                  inner_radius / sqrt(2.0))},\n                  Identity1D{}}));\n    } else {\n      coord_maps.emplace_back(\n          make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n              CoordinateMaps::ProductOf2Maps<Affine2D, Identity1D>{\n                  Affine2D{Affine(-1.0, 1.0, 0.0, inner_radius / sqrt(2.0)),\n                           Affine(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                                  inner_radius / sqrt(2.0))},\n                  Identity1D{}}));\n    }\n  }\n  test_domain_construction(\n      domain, expected_neighbors, expected_external_boundaries, coord_maps, 9.0,\n      sphere.functions_of_time(), expected_grid_to_inertial_maps);\n  TestHelpers::domain::creators::test_functions_of_time(\n      sphere, expected_functions_of_time, initial_expiration_times);\n}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition1() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::upper_eta(), 1);\n}\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition2() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::lower_xi(), 2);\n}\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition3() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::lower_eta(), 3);\n}\n\nvoid test_sphere_boundaries() {\n  INFO(\"CartoonSphere2D boundaries\");\n  const double inner_radius = 1.0;\n  const double outer_radius = 2.0;\n  const std::array<size_t, 2> refinement_level_arr{{2, 3}};\n  const std::vector<std::array<size_t, 2>> refinement_level_vec{\n      {4, 4}, {3, 5}, {2, 6}};\n  const std::vector<double> radial_partitioning{{1.3, 1.8}};\n  const std::vector<double> radial_partitioning_empty{};\n  const std::array<size_t, 2> grid_points_arr{{4, 4}};\n  const std::vector<std::array<size_t, 2>> grid_points_vec{\n      {4, 4}, {5, 3}, {6, 7}};\n  const domain::creators::detail::InnerSquare fill_center{0.0};\n\n  {\n    INFO(\"No BC, no refinement, equiangular\");\n    const creators::CartoonSphere2D sphere{inner_radius,\n                                           outer_radius,\n                                           refinement_level_arr,\n                                           grid_points_arr,\n                                           radial_partitioning_empty,\n                                           true,\n                                           fill_center};\n    test_sphere_construction(\n        sphere, inner_radius, outer_radius,\n        std::vector<std::array<size_t, 2>>{1, refinement_level_arr},\n        std::vector<std::array<size_t, 2>>{1, grid_points_arr},\n        radial_partitioning_empty, true, fill_center);\n  }\n  {\n    INFO(\"No BC, no refinement, affine\");\n    const creators::CartoonSphere2D sphere{inner_radius,\n                                           outer_radius,\n                                           refinement_level_arr,\n                                           grid_points_arr,\n                                           radial_partitioning_empty,\n                                           false,\n                                           fill_center};\n    test_sphere_construction(\n        sphere, inner_radius, outer_radius,\n        std::vector<std::array<size_t, 2>>{1, refinement_level_arr},\n        std::vector<std::array<size_t, 2>>{1, grid_points_arr},\n        radial_partitioning_empty, false, fill_center);\n  }\n  {\n    INFO(\"No BC, with refinement and extents array\");\n    const creators::CartoonSphere2D sphere{inner_radius,\n                                           outer_radius,\n                                           refinement_level_vec,\n                                           grid_points_arr,\n                                           radial_partitioning,\n                                           true,\n                                           fill_center};\n    test_sphere_construction(\n        sphere, inner_radius, outer_radius, refinement_level_vec,\n        std::vector<std::array<size_t, 2>>{3, grid_points_arr},\n        radial_partitioning, true, fill_center);\n  }\n  {\n    INFO(\"No BC, with refinement and refinement array\");\n    const creators::CartoonSphere2D sphere{inner_radius,\n                                           outer_radius,\n                                           refinement_level_arr,\n                                           grid_points_vec,\n                                           radial_partitioning,\n                                           true,\n                                           fill_center};\n    test_sphere_construction(\n        sphere, inner_radius, outer_radius,\n        std::vector<std::array<size_t, 2>>{3, refinement_level_arr},\n        grid_points_vec, radial_partitioning, true, fill_center);\n  }\n  {\n    INFO(\"With BC, no excision\");\n    const creators::CartoonSphere2D sphere{inner_radius,\n                                           outer_radius,\n                                           refinement_level_vec,\n                                           grid_points_vec,\n                                           radial_partitioning,\n                                           true,\n                                           fill_center,\n                                           nullptr,\n                                           create_boundary_condition1(),\n                                           create_boundary_condition2()};\n    test_sphere_construction(sphere, inner_radius, outer_radius,\n                             refinement_level_vec, grid_points_vec,\n                             radial_partitioning, true, fill_center, true);\n  }\n  {\n    INFO(\"With BC, with excision\");\n    domain::creators::detail::Excision excise1{create_boundary_condition3()};\n    domain::creators::detail::Excision excise2{create_boundary_condition3()};\n    const creators::CartoonSphere2D sphere{inner_radius,\n                                           outer_radius,\n                                           refinement_level_vec,\n                                           grid_points_vec,\n                                           radial_partitioning,\n                                           true,\n                                           std::move(excise1),\n                                           nullptr,\n                                           create_boundary_condition1(),\n                                           create_boundary_condition2()};\n    test_sphere_construction(\n        sphere, inner_radius, outer_radius, refinement_level_vec,\n        grid_points_vec, radial_partitioning, true, std::move(excise2), true);\n  }\n  {\n    INFO(\"With BC, with excision and no partitioning\");\n    domain::creators::detail::Excision excise1{create_boundary_condition3()};\n    domain::creators::detail::Excision excise2{create_boundary_condition3()};\n    const creators::CartoonSphere2D sphere{inner_radius,\n                                           outer_radius,\n                                           refinement_level_arr,\n                                           grid_points_arr,\n                                           radial_partitioning_empty,\n                                           true,\n                                           std::move(excise1),\n                                           nullptr,\n                                           create_boundary_condition1(),\n                                           create_boundary_condition2()};\n    test_sphere_construction(\n        sphere, inner_radius, outer_radius,\n        std::vector<std::array<size_t, 2>>{1, refinement_level_arr},\n        std::vector<std::array<size_t, 2>>{1, grid_points_arr},\n        radial_partitioning_empty, true, std::move(excise2), true);\n  }\n}\n\nvoid test_sphere_factory() {\n  INFO(\"CartoonSphere2D factory\");\n  using Translation3D = CoordinateMaps::TimeDependent::Translation<3>;\n  const auto sphere = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<3>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithoutBoundaryConditions<\n              3, domain::creators::CartoonSphere2D>>(\n      \"CartoonSphere2D:\\n\"\n      \"  InnerRadius: 1.0\\n\"\n      \"  OuterRadius: 5.0\\n\"\n      \"  InitialRefinement:\\n\"\n      \"    - [3, 4]\\n\"\n      \"    - [3, 3]\\n\"\n      \"    - [2, 4]\\n\"\n      \"  InitialGridPoints: [2,3]\\n\"\n      \"  RadialPartitioning: [3.5, 4.5]\\n\"\n      \"  UseEquiangularMap: true\\n\"\n      \"  Interior:\\n\"\n      \"    FillWithSphericity: 0.0\\n\"\n      \"  TimeDependence: None\\n\");\n  const double inner_radius = 1.0;\n  const double outer_radius = 5.0;\n  const std::vector<std::array<size_t, 2>> refinement_levels{\n      {3, 4}, {3, 3}, {2, 4}};\n  const std::vector<std::array<size_t, 2>> grid_points{3, {2, 3}};\n  const std::vector<double> radial_partition{{3.5, 4.5}};\n  const domain::creators::detail::InnerSquare fill_center{0.0};\n  test_sphere_construction(\n      dynamic_cast<const creators::CartoonSphere2D&>(*sphere), inner_radius,\n      outer_radius, refinement_levels, grid_points, radial_partition, true,\n      fill_center);\n\n  const auto sphere_boundary_conditions = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<3>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithBoundaryConditions<\n              3, domain::creators::CartoonSphere2D>>(\n      \"CartoonSphere2D:\\n\"\n      \"  InnerRadius: 1.0\\n\"\n      \"  OuterRadius: 5.0\\n\"\n      \"  InitialRefinement:\\n\"\n      \"    - [3, 4]\\n\"\n      \"    - [3, 3]\\n\"\n      \"    - [2, 4]\\n\"\n      \"  InitialGridPoints: [2,3]\\n\"\n      \"  RadialPartitioning: [3.5, 4.5]\\n\"\n      \"  UseEquiangularMap: false\\n\"\n      \"  TimeDependence: None\\n\"\n      \"  Interior:\\n\"\n      \"    ExciseWithBoundaryCondition:\\n\"\n      \"      TestBoundaryCondition:\\n\"\n      \"        Direction: lower-eta\\n\"\n      \"        BlockId: 3\\n\"\n      \"  YAxisBoundaryCondition:\\n\"\n      \"    TestBoundaryCondition:\\n\"\n      \"      Direction: upper-eta\\n\"\n      \"      BlockId: 1\\n\"\n      \"  OuterBoundaryCondition:\\n\"\n      \"    TestBoundaryCondition:\\n\"\n      \"      Direction: lower-xi\\n\"\n      \"      BlockId: 2\\n\");\n  domain::creators::detail::Excision excise1{create_boundary_condition3()};\n  test_sphere_construction(dynamic_cast<const creators::CartoonSphere2D&>(\n                               *sphere_boundary_conditions),\n                           inner_radius, outer_radius, refinement_levels,\n                           grid_points, radial_partition, false,\n                           std::move(excise1), true);\n\n  INFO(\"With TimeDependent Map\");\n  const auto sphere_time_dependent = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<3>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithBoundaryConditions<\n              3, domain::creators::CartoonSphere2D>>(\n      \"CartoonSphere2D:\\n\"\n      \"  InnerRadius: 1.0\\n\"\n      \"  OuterRadius: 5.0\\n\"\n      \"  InitialRefinement:\\n\"\n      \"    - [3, 4]\\n\"\n      \"    - [3, 3]\\n\"\n      \"    - [2, 4]\\n\"\n      \"  InitialGridPoints: [2,3]\\n\"\n      \"  RadialPartitioning: [3.5, 4.5]\\n\"\n      \"  UseEquiangularMap: false\\n\"\n      \"  TimeDependence:\\n\"\n      \"    UniformTranslation:\\n\"\n      \"      InitialTime: 2.3\\n\"\n      \"      Velocity: [1.1, -0.1, 0.3]\\n\"\n      \"  Interior:\\n\"\n      \"    ExciseWithBoundaryCondition:\\n\"\n      \"      TestBoundaryCondition:\\n\"\n      \"        Direction: lower-eta\\n\"\n      \"        BlockId: 3\\n\"\n      \"  YAxisBoundaryCondition:\\n\"\n      \"    TestBoundaryCondition:\\n\"\n      \"      Direction: upper-eta\\n\"\n      \"      BlockId: 1\\n\"\n      \"  OuterBoundaryCondition:\\n\"\n      \"    TestBoundaryCondition:\\n\"\n      \"      Direction: lower-xi\\n\"\n      \"      BlockId: 2\\n\");\n  domain::creators::detail::Excision excise2{create_boundary_condition3()};\n  domain::creators::detail::Excision excise3{create_boundary_condition3()};\n  const double initial_time = 2.3;\n  const DataVector velocity{{1.1, -0.1, 0.3}};\n  const std::string f_of_t_name = \"Translation\";\n  std::unordered_map<std::string, double> initial_expiration_times{};\n  initial_expiration_times[f_of_t_name] = 9.0;\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n      time_map_vec{};\n  time_map_vec.reserve(9);\n  for (size_t i = 0; i < 9; ++i) {\n    time_map_vec.push_back(\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation3D{f_of_t_name}));\n  }\n  // without expiration times\n  test_sphere_construction(\n      dynamic_cast<const creators::CartoonSphere2D&>(*sphere_time_dependent),\n      inner_radius, outer_radius, refinement_levels, grid_points,\n      radial_partition, false, std::move(excise2), true,\n      std::make_tuple(\n          std::pair<std::string,\n                    domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n              f_of_t_name,\n              {initial_time,\n               std::array<DataVector, 3>{{{3, 0.0}, velocity, {3, 0.0}}},\n               std::numeric_limits<double>::infinity()}}),\n      time_map_vec);\n  // with expiration times\n  test_sphere_construction(\n      dynamic_cast<const creators::CartoonSphere2D&>(*sphere_time_dependent),\n      inner_radius, outer_radius, refinement_levels, grid_points,\n      radial_partition, false, std::move(excise3), true,\n      std::make_tuple(\n          std::pair<std::string,\n                    domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n              f_of_t_name,\n              {initial_time,\n               std::array<DataVector, 3>{{{3, 0.0}, velocity, {3, 0.0}}},\n               initial_expiration_times[f_of_t_name]}}),\n      time_map_vec, initial_expiration_times);\n}\n\nvoid test_sphere_errors() {\n  INFO(\"CartoonSphere2D testing errors\");\n  const double inner_radius = 1.0;\n  const double high_inner_radius = 3.0;\n  const double outer_radius = 2.0;\n  const std::vector<std::array<size_t, 2>> refinement_level_vec{\n      {3, 4}, {3, 5}, {4, 6}};\n  const std::vector<std::array<size_t, 2>> refinement_level_short{{3, 4},\n                                                                  {3, 2}};\n  const std::vector<std::array<size_t, 2>> grid_points_vec{\n      {4, 4}, {5, 3}, {6, 7}};\n  const std::vector<double> radial_partitioning{{1.3, 1.8}};\n  const std::vector<double> radial_partitioning_unordered{{1.6, 1.3}};\n  const std::vector<double> radial_partitioning_low{{0.8, 1.3}};\n  const std::vector<double> radial_partitioning_high{{1.6, 3.3}};\n  const domain::creators::detail::InnerSquare fill_center{0.0};\n\n  CHECK_THROWS_WITH(\n      creators::CartoonSphere2D(\n          high_inner_radius, outer_radius, refinement_level_vec,\n          grid_points_vec, radial_partitioning, false, fill_center, nullptr,\n          nullptr, nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Inner radius must be smaller than \"\n                                         \"outer radius, but inner radius is \"));\n  CHECK_THROWS_WITH(\n      creators::CartoonSphere2D(\n          inner_radius, outer_radius, refinement_level_vec, grid_points_vec,\n          radial_partitioning_unordered, false, fill_center, nullptr, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify radial partitioning in ascending order.\"));\n  CHECK_THROWS_WITH(\n      creators::CartoonSphere2D(\n          inner_radius, outer_radius, refinement_level_vec, grid_points_vec,\n          radial_partitioning_low, false, fill_center, nullptr, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"First radial partition must be larger than the inner\"));\n  CHECK_THROWS_WITH(\n      creators::CartoonSphere2D(\n          inner_radius, outer_radius, refinement_level_vec, grid_points_vec,\n          radial_partitioning_high, false, fill_center, nullptr, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Last radial partition must be smaller than the outer\"));\n  CHECK_THROWS_WITH(\n      creators::CartoonSphere2D(\n          inner_radius, outer_radius, refinement_level_short, grid_points_vec,\n          radial_partitioning, false, fill_center, nullptr, nullptr, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"InitialRefinement must be one larger \"\n                                         \"than RadialPartitioning (size\"));\n  CHECK_THROWS_WITH(\n      creators::CartoonSphere2D(\n          inner_radius, outer_radius, refinement_level_vec, grid_points_vec,\n          radial_partitioning, false, fill_center, nullptr,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Must specify either both inner and outer boundary conditions \"));\n  CHECK_THROWS_WITH(\n      creators::CartoonSphere2D(\n          inner_radius, outer_radius, refinement_level_vec, grid_points_vec,\n          radial_partitioning, false, fill_center, nullptr,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like an \"));\n  CHECK_THROWS_WITH(\n      creators::CartoonSphere2D(\n          inner_radius, outer_radius, refinement_level_vec, grid_points_vec,\n          radial_partitioning, false, fill_center, nullptr,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions on a 2D sphere.\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.CartoonSphere2D\", \"[Domain][Unit]\") {\n  test_sphere_boundaries();\n  test_sphere_factory();\n  test_sphere_errors();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_Cylinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/Cylinder.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain {\nnamespace {\nusing PeriodicBc =\n    TestHelpers::domain::BoundaryConditions::TestPeriodicBoundaryCondition<3>;\nusing NoneBc =\n    TestHelpers::domain::BoundaryConditions::TestNoneBoundaryCondition<3>;\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_lower_z_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::lower_zeta(), 50);\n}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_upper_z_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::upper_zeta(), 50);\n}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_mantle_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::upper_xi(), 50);\n}\n\nstd::string boundary_conditions_string(const bool is_periodic_in_z) {\n  return \"  BoundaryConditions:\\n\"\n         \"    LowerZ:\\n\" +\n         std::string{is_periodic_in_z ? \"      Periodic\\n\"\n                                      : \"      TestBoundaryCondition:\\n\"\n                                        \"        Direction: lower-zeta\\n\"\n                                        \"        BlockId: 50\\n\"} +\n         \"    UpperZ:\\n\" +\n         std::string{is_periodic_in_z ? \"      Periodic\\n\"\n                                      : \"      TestBoundaryCondition:\\n\"\n                                        \"        Direction: upper-zeta\\n\"\n                                        \"        BlockId: 50\\n\"} +\n         \"    Mantle:\\n\"\n         \"      TestBoundaryCondition:\\n\"\n         \"        Direction: upper-xi\\n\"\n         \"        BlockId: 50\\n\";\n}\n\nvoid test_cylinder_construction(\n    const creators::Cylinder& cylinder, const double inner_radius,\n    const double outer_radius, const double lower_z_bound,\n    const double upper_z_bound, const bool is_periodic_in_z,\n    const std::vector<std::array<size_t, 3>>& expected_extents,\n    const std::vector<std::array<size_t, 3>>& expected_refinement_level,\n    const bool use_equiangular_map,\n    const bool expect_boundary_conditions = false) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      cylinder, expect_boundary_conditions, is_periodic_in_z);\n  CHECK(cylinder.grid_anchors().empty());\n\n  const OrientationMap<3> aligned_orientation =\n      OrientationMap<3>::create_aligned();\n  const OrientationMap<3> quarter_turn_ccw(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_eta(), Direction<3>::upper_xi(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> half_turn(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> quarter_turn_cw(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n       Direction<3>::upper_zeta()}});\n  std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_block_neighbors{};\n  std::vector<std::unordered_set<Direction<3>>> expected_external_boundaries{};\n  using TargetFrame = Frame::Inertial;\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>>\n      coord_maps{};\n  if (not is_periodic_in_z) {\n    expected_block_neighbors = std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n        {{Direction<3>::upper_xi(), {1, aligned_orientation}},\n         {Direction<3>::upper_eta(), {2, quarter_turn_ccw}},\n         {Direction<3>::lower_xi(), {3, half_turn}},\n         {Direction<3>::lower_eta(), {4, quarter_turn_cw}}},\n        {{Direction<3>::lower_eta(), {4, aligned_orientation}},\n         {Direction<3>::upper_eta(), {2, aligned_orientation}},\n         {Direction<3>::lower_xi(), {0, aligned_orientation}}},\n        {{Direction<3>::lower_eta(), {1, aligned_orientation}},\n         {Direction<3>::upper_eta(), {3, aligned_orientation}},\n         {Direction<3>::lower_xi(), {0, quarter_turn_cw}}},\n        {{Direction<3>::lower_eta(), {2, aligned_orientation}},\n         {Direction<3>::upper_eta(), {4, aligned_orientation}},\n         {Direction<3>::lower_xi(), {0, half_turn}}},\n        {{Direction<3>::lower_eta(), {3, aligned_orientation}},\n         {Direction<3>::upper_eta(), {1, aligned_orientation}},\n         {Direction<3>::lower_xi(), {0, quarter_turn_ccw}}}};\n    expected_external_boundaries =\n        std::vector<std::unordered_set<Direction<3>>>{\n            {Direction<3>::upper_zeta(), Direction<3>::lower_zeta()},\n            {{Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n              Direction<3>::lower_zeta()}},\n            {{Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n              Direction<3>::lower_zeta()}},\n            {{Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n              Direction<3>::lower_zeta()}},\n            {{Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n              Direction<3>::lower_zeta()}}};\n  } else {\n    expected_block_neighbors = std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n        {{Direction<3>::upper_xi(), {1, aligned_orientation}},\n         {Direction<3>::upper_eta(), {2, quarter_turn_ccw}},\n         {Direction<3>::lower_xi(), {3, half_turn}},\n         {Direction<3>::lower_eta(), {4, quarter_turn_cw}},\n         {Direction<3>::lower_zeta(), {0, aligned_orientation}},\n         {Direction<3>::upper_zeta(), {0, aligned_orientation}}},\n        {{Direction<3>::lower_eta(), {4, aligned_orientation}},\n         {Direction<3>::upper_eta(), {2, aligned_orientation}},\n         {Direction<3>::lower_xi(), {0, aligned_orientation}},\n         {Direction<3>::lower_zeta(), {1, aligned_orientation}},\n         {Direction<3>::upper_zeta(), {1, aligned_orientation}}},\n        {{Direction<3>::lower_eta(), {1, aligned_orientation}},\n         {Direction<3>::upper_eta(), {3, aligned_orientation}},\n         {Direction<3>::lower_xi(), {0, quarter_turn_cw}},\n         {Direction<3>::lower_zeta(), {2, aligned_orientation}},\n         {Direction<3>::upper_zeta(), {2, aligned_orientation}}},\n        {{Direction<3>::lower_eta(), {2, aligned_orientation}},\n         {Direction<3>::upper_eta(), {4, aligned_orientation}},\n         {Direction<3>::lower_xi(), {0, half_turn}},\n         {Direction<3>::lower_zeta(), {3, aligned_orientation}},\n         {Direction<3>::upper_zeta(), {3, aligned_orientation}}},\n        {{Direction<3>::lower_eta(), {3, aligned_orientation}},\n         {Direction<3>::upper_eta(), {1, aligned_orientation}},\n         {Direction<3>::lower_xi(), {0, quarter_turn_ccw}},\n         {Direction<3>::lower_zeta(), {4, aligned_orientation}},\n         {Direction<3>::upper_zeta(), {4, aligned_orientation}}}};\n\n    expected_external_boundaries =\n        std::vector<std::unordered_set<Direction<3>>>{\n            {},\n            {{Direction<3>::upper_xi()}},\n            {{Direction<3>::upper_xi()}},\n            {{Direction<3>::upper_xi()}},\n            {{Direction<3>::upper_xi()}}};\n  }\n\n  CHECK(cylinder.initial_extents() == expected_extents);\n  CHECK(cylinder.initial_refinement_levels() == expected_refinement_level);\n  using TargetFrame = Frame::Inertial;\n  using Interval = CoordinateMaps::Interval;\n  using Interval3D =\n      CoordinateMaps::ProductOf3Maps<Interval, Interval, Interval>;\n  using Wedge2D = CoordinateMaps::Wedge<2>;\n  using Wedge3DPrism = CoordinateMaps::ProductOf2Maps<Wedge2D, Interval>;\n  using Distribution = domain::CoordinateMaps::Distribution;\n  const auto angular_distribution =\n      use_equiangular_map ? Distribution::Equiangular : Distribution::Linear;\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          Interval3D{Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval{-1.0, 1.0, lower_z_bound, upper_z_bound,\n                              Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, outer_radius, 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, upper_z_bound,\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, outer_radius, 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, upper_z_bound,\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, outer_radius, 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, upper_z_bound,\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, outer_radius, 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, upper_z_bound,\n                   Distribution::Linear}}));\n\n  test_domain_construction(domain, expected_block_neighbors,\n                           expected_external_boundaries, coord_maps);\n}\n\nvoid test_cylinder_no_refinement() {\n  INFO(\"Cylinder, no refinement\");\n  const std::vector radial_distribution{\n      domain::CoordinateMaps::Distribution::Linear};\n  const std::vector distribution_in_z{\n      domain::CoordinateMaps::Distribution::Linear};\n  for (const auto& [with_boundary_conditions, equiangular_map, periodic_in_z] :\n       cartesian_product(make_array(true, false), make_array(true, false),\n                         make_array(true, false))) {\n    CAPTURE(with_boundary_conditions);\n    CAPTURE(equiangular_map);\n    CAPTURE(periodic_in_z);\n    const double inner_radius = 1.0;\n    const double outer_radius = 2.0;\n    const double lower_z_bound = -2.5;\n    const double upper_z_bound = 5.0;\n    const size_t refinement_level = 2;\n    const std::array<size_t, 3> grid_points{{4, 4, 3}};\n\n    const auto cylinder =\n        with_boundary_conditions\n            ? creators::Cylinder{inner_radius,\n                                 outer_radius,\n                                 lower_z_bound,\n                                 upper_z_bound,\n                                 periodic_in_z\n                                     ? std::make_unique<PeriodicBc>()\n                                     : create_lower_z_boundary_condition(),\n                                 periodic_in_z\n                                     ? std::make_unique<PeriodicBc>()\n                                     : create_upper_z_boundary_condition(),\n                                 create_mantle_boundary_condition(),\n                                 refinement_level,\n                                 grid_points,\n                                 equiangular_map,\n                                 {},\n                                 {}}\n            : creators::Cylinder{inner_radius,\n                                 outer_radius,\n                                 lower_z_bound,\n                                 upper_z_bound,\n                                 periodic_in_z,\n                                 refinement_level,\n                                 grid_points,\n                                 equiangular_map,\n                                 {},\n                                 {}};\n    const auto domain = TestHelpers::domain::creators::test_domain_creator(\n        cylinder, with_boundary_conditions, periodic_in_z);\n\n    CHECK(cylinder.block_names() == std::vector<std::string>{\"InnerCube\",\n                                                             \"East\", \"North\",\n                                                             \"West\", \"South\"});\n    CHECK(cylinder.block_groups() ==\n          std::unordered_map<std::string, std::unordered_set<std::string>>{\n              {\"Wedges\", {\"East\", \"North\", \"West\", \"South\"}}});\n\n    test_cylinder_construction(\n        cylinder, inner_radius, outer_radius, lower_z_bound, upper_z_bound,\n        periodic_in_z, {5, grid_points}, {5, make_array<3>(refinement_level)},\n        equiangular_map, with_boundary_conditions);\n\n    const std::string opt_string{\n        \"Cylinder:\\n\"\n        \"  InnerRadius: 1.0\\n\"\n        \"  OuterRadius: 2.0\\n\"\n        \"  LowerZBound: -2.5\\n\"\n        \"  UpperZBound: 5.0\\n\"\n        \"  InitialRefinement: 2\\n\"\n        \"  InitialGridPoints: [4,4,3]\\n\"\n        \"  UseEquiangularMap: \" +\n        std::string{equiangular_map ? \"true\" : \"false\"} +\n        \"\\n\"\n        \"  RadialPartitioning: []\\n\"\n        \"  PartitioningInZ: []\\n\"\n        \"  RadialDistribution: [Linear]\\n\"\n        \"  DistributionInZ: [Linear]\\n\" +\n        std::string{with_boundary_conditions\n                        ? boundary_conditions_string(periodic_in_z)\n                        : \"  IsPeriodicInZ: \" +\n                              std::string{periodic_in_z ? \"true\" : \"false\"} +\n                              \"\\n\"}};\n\n    const auto cylinder_factory =\n        [&opt_string,\n         captured_with_boundary_conditions = with_boundary_conditions]() {\n          if (captured_with_boundary_conditions) {\n            return TestHelpers::test_option_tag<\n                domain::OptionTags::DomainCreator<3>,\n                TestHelpers::domain::BoundaryConditions::\n                    MetavariablesWithBoundaryConditions<\n                        3, domain::creators::Cylinder>>(opt_string);\n          } else {\n            return TestHelpers::test_option_tag<\n                domain::OptionTags::DomainCreator<3>,\n                TestHelpers::domain::BoundaryConditions::\n                    MetavariablesWithoutBoundaryConditions<\n                        3, domain::creators::Cylinder>>(opt_string);\n          }\n        }();\n    test_cylinder_construction(\n        dynamic_cast<const creators::Cylinder&>(*cylinder_factory),\n        inner_radius, outer_radius, lower_z_bound, upper_z_bound, periodic_in_z,\n        {5, grid_points}, {5, make_array<3>(refinement_level)}, equiangular_map,\n        with_boundary_conditions);\n\n    if (with_boundary_conditions) {\n      CHECK_THROWS_WITH(\n          creators::Cylinder(\n              inner_radius, outer_radius, lower_z_bound, upper_z_bound,\n              periodic_in_z ? std::make_unique<PeriodicBc>()\n                            : create_lower_z_boundary_condition(),\n              periodic_in_z ? std::make_unique<PeriodicBc>()\n                            : create_upper_z_boundary_condition(),\n              std::make_unique<PeriodicBc>(), refinement_level, grid_points,\n              equiangular_map, {}, {}, radial_distribution, distribution_in_z,\n              Options::Context{false, {}, 1, 1}),\n          Catch::Matchers::ContainsSubstring(\n              \"A Cylinder can't have periodic boundary conditions in the \"\n              \"radial direction.\"));\n      CHECK_THROWS_WITH(\n          creators::Cylinder(inner_radius, outer_radius, lower_z_bound,\n                             upper_z_bound, std::make_unique<PeriodicBc>(),\n                             create_lower_z_boundary_condition(),\n                             create_mantle_boundary_condition(),\n                             refinement_level, grid_points, equiangular_map, {},\n                             {}, radial_distribution, distribution_in_z,\n                             Options::Context{false, {}, 1, 1}),\n          Catch::Matchers::ContainsSubstring(\n              \"Either both lower and upper z-boundary condition must be \"\n              \"periodic, or neither.\"));\n      CHECK_THROWS_WITH(\n          creators::Cylinder(inner_radius, outer_radius, lower_z_bound,\n                             upper_z_bound, create_lower_z_boundary_condition(),\n                             std::make_unique<PeriodicBc>(),\n                             create_mantle_boundary_condition(),\n                             refinement_level, grid_points, equiangular_map, {},\n                             {}, radial_distribution, distribution_in_z,\n                             Options::Context{false, {}, 1, 1}),\n          Catch::Matchers::ContainsSubstring(\n              \"Either both lower and upper z-boundary condition must be \"\n              \"periodic, or neither.\"));\n      CHECK_THROWS_WITH(\n          creators::Cylinder(\n              inner_radius, outer_radius, lower_z_bound, upper_z_bound,\n              std::make_unique<NoneBc>(), create_upper_z_boundary_condition(),\n              create_mantle_boundary_condition(), refinement_level, grid_points,\n              equiangular_map, {}, {}, radial_distribution, distribution_in_z,\n              Options::Context{false, {}, 1, 1}),\n          Catch::Matchers::ContainsSubstring(\n              \"None boundary condition is not supported. If you would like \"\n              \"an outflow-type boundary condition, you must use that.\"));\n      CHECK_THROWS_WITH(\n          creators::Cylinder(\n              inner_radius, outer_radius, lower_z_bound, upper_z_bound,\n              create_lower_z_boundary_condition(), std::make_unique<NoneBc>(),\n              create_mantle_boundary_condition(), refinement_level, grid_points,\n              equiangular_map, {}, {}, radial_distribution, distribution_in_z,\n              Options::Context{false, {}, 1, 1}),\n          Catch::Matchers::ContainsSubstring(\n              \"None boundary condition is not supported. If you would like \"\n              \"an outflow-type boundary condition, you must use that.\"));\n      CHECK_THROWS_WITH(\n          creators::Cylinder(inner_radius, outer_radius, lower_z_bound,\n                             upper_z_bound, create_lower_z_boundary_condition(),\n                             create_upper_z_boundary_condition(),\n                             std::make_unique<NoneBc>(), refinement_level,\n                             grid_points, equiangular_map, {}, {},\n                             radial_distribution, distribution_in_z,\n                             Options::Context{false, {}, 1, 1}),\n          Catch::Matchers::ContainsSubstring(\n              \"None boundary condition is not supported. If you would like \"\n              \"an outflow-type boundary condition, you must use that.\"));\n    }\n  }\n}\n\nvoid test_refined_cylinder_boundaries(\n    const bool use_equiangular_map,\n    const domain::CoordinateMaps::Distribution outer_radial_distribution,\n    const domain::CoordinateMaps::Distribution uppermost_distribution_in_z) {\n  INFO(\"Refined Cylinder boundaries\");\n  CAPTURE(use_equiangular_map);\n  // definition of an arbitrary refined cylinder\n  const double inner_radius = 0.3;\n  const double outer_radius = 1.0;\n  const double lower_z_bound = -1.5;\n  const double upper_z_bound = 5.0;\n  const std::vector<double> radial_partitioning = {0.7};\n  const std::vector<double> partitioning_in_z = {1.5};\n  const std::array<size_t, 3> refinement_level{1, 2, 3};\n  const std::array<size_t, 3> expected_wedge_extents{{5, 4, 3}};\n  std::vector<std::array<size_t, 3>> expected_refinement_level{\n      (1 + 4 * (1 + radial_partitioning.size())) *\n          (1 + partitioning_in_z.size()),\n      refinement_level};\n  // The central cubes share refinement level in x and y direction\n  expected_refinement_level[0][0] = expected_refinement_level[0][1];\n  expected_refinement_level[9][0] = expected_refinement_level[9][1];\n  const creators::Cylinder refined_cylinder{\n      inner_radius,\n      outer_radius,\n      lower_z_bound,\n      upper_z_bound,\n      create_lower_z_boundary_condition(),\n      create_upper_z_boundary_condition(),\n      create_mantle_boundary_condition(),\n      refinement_level,\n      expected_wedge_extents,\n      use_equiangular_map,\n      radial_partitioning,\n      partitioning_in_z,\n      {domain::CoordinateMaps::Distribution::Linear, outer_radial_distribution},\n      {domain::CoordinateMaps::Distribution::Linear,\n       uppermost_distribution_in_z}};\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      refined_cylinder, true);\n\n  CHECK(refined_cylinder.block_names() ==\n        std::vector<std::string>{\n            \"Layer0InnerCube\", \"Layer0Shell0East\", \"Layer0Shell0North\",\n            \"Layer0Shell0West\", \"Layer0Shell0South\", \"Layer0Shell1East\",\n            \"Layer0Shell1North\", \"Layer0Shell1West\", \"Layer0Shell1South\",\n            \"Layer1InnerCube\", \"Layer1Shell0East\", \"Layer1Shell0North\",\n            \"Layer1Shell0West\", \"Layer1Shell0South\", \"Layer1Shell1East\",\n            \"Layer1Shell1North\", \"Layer1Shell1West\", \"Layer1Shell1South\"});\n  CHECK(refined_cylinder.block_groups() ==\n        std::unordered_map<std::string, std::unordered_set<std::string>>{\n            {\"InnerCubes\", {\"Layer0InnerCube\", \"Layer1InnerCube\"}},\n            {\"Layer0\",\n             {\"Layer0InnerCube\", \"Layer0Shell0East\", \"Layer0Shell0North\",\n              \"Layer0Shell0West\", \"Layer0Shell0South\", \"Layer0Shell1East\",\n              \"Layer0Shell1North\", \"Layer0Shell1West\", \"Layer0Shell1South\"}},\n            {\"Layer1\",\n             {\"Layer1InnerCube\", \"Layer1Shell0East\", \"Layer1Shell0North\",\n              \"Layer1Shell0West\", \"Layer1Shell0South\", \"Layer1Shell1East\",\n              \"Layer1Shell1North\", \"Layer1Shell1West\", \"Layer1Shell1South\"}},\n            {\"Shell0\",\n             {\"Layer0Shell0East\", \"Layer0Shell0North\", \"Layer0Shell0West\",\n              \"Layer0Shell0South\", \"Layer1Shell0East\", \"Layer1Shell0North\",\n              \"Layer1Shell0West\", \"Layer1Shell0South\"}},\n            {\"Shell1\",\n             {\"Layer0Shell1East\", \"Layer0Shell1North\", \"Layer0Shell1West\",\n              \"Layer0Shell1South\", \"Layer1Shell1East\", \"Layer1Shell1North\",\n              \"Layer1Shell1West\", \"Layer1Shell1South\"}},\n            {\"Layer0Shell0Wedges\",\n             {\"Layer0Shell0East\", \"Layer0Shell0North\", \"Layer0Shell0West\",\n              \"Layer0Shell0South\"}},\n            {\"Layer1Shell0Wedges\",\n             {\"Layer1Shell0East\", \"Layer1Shell0North\", \"Layer1Shell0West\",\n              \"Layer1Shell0South\"}},\n            {\"Layer0Shell1Wedges\",\n             {\"Layer0Shell1East\", \"Layer0Shell1North\", \"Layer0Shell1West\",\n              \"Layer0Shell1South\"}},\n            {\"Layer1Shell1Wedges\",\n             {\"Layer1Shell1East\", \"Layer1Shell1North\", \"Layer1Shell1West\",\n              \"Layer1Shell1South\"}}});\n\n  const OrientationMap<3> aligned_orientation =\n      OrientationMap<3>::create_aligned();\n  const OrientationMap<3> quarter_turn_ccw(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_eta(), Direction<3>::upper_xi(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> half_turn(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> quarter_turn_cw(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n       Direction<3>::upper_zeta()}});\n  std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_block_neighbors{};\n  std::vector<std::unordered_set<Direction<3>>> expected_external_boundaries{};\n  using TargetFrame = Frame::Inertial;\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>>\n      coord_maps{};\n  // This specific domain consists of two stacked discs with 9 blocks each.\n  expected_block_neighbors = std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n      // Block 0 - layer 0 center 0\n      {{Direction<3>::upper_xi(), {1, aligned_orientation}},\n       {Direction<3>::upper_eta(), {2, quarter_turn_ccw}},\n       {Direction<3>::lower_xi(), {3, half_turn}},\n       {Direction<3>::lower_eta(), {4, quarter_turn_cw}},\n       {Direction<3>::upper_zeta(), {0 + 9, aligned_orientation}}},\n      // Block 1 - layer 0 east 1\n      {{Direction<3>::lower_eta(), {4, aligned_orientation}},\n       {Direction<3>::upper_eta(), {2, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0, aligned_orientation}},\n       {Direction<3>::upper_xi(), {1 + 4, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {1 + 9, aligned_orientation}}},\n      // Block 2 - layer 0 north 2\n      {{Direction<3>::lower_eta(), {1, aligned_orientation}},\n       {Direction<3>::upper_eta(), {3, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0, quarter_turn_cw}},\n       {Direction<3>::upper_xi(), {2 + 4, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {2 + 9, aligned_orientation}}},\n      // Block 3 - layer 0 west 3\n      {{Direction<3>::lower_eta(), {2, aligned_orientation}},\n       {Direction<3>::upper_eta(), {4, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0, half_turn}},\n       {Direction<3>::upper_xi(), {3 + 4, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {3 + 9, aligned_orientation}}},\n      // Block 4 - layer 0 south 4\n      {{Direction<3>::lower_eta(), {3, aligned_orientation}},\n       {Direction<3>::upper_eta(), {1, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0, quarter_turn_ccw}},\n       {Direction<3>::upper_xi(), {4 + 4, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {4 + 9, aligned_orientation}}},\n      // Block 5 - layer 0 east 5\n      {{Direction<3>::lower_eta(), {8, aligned_orientation}},\n       {Direction<3>::upper_eta(), {6, aligned_orientation}},\n       {Direction<3>::lower_xi(), {5 - 4, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {5 + 9, aligned_orientation}}},\n      // Block 6 - layer 0 north 6\n      {{Direction<3>::lower_eta(), {5, aligned_orientation}},\n       {Direction<3>::upper_eta(), {7, aligned_orientation}},\n       {Direction<3>::lower_xi(), {6 - 4, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {6 + 9, aligned_orientation}}},\n      // Block 7 - layer 0 west 7\n      {{Direction<3>::lower_eta(), {6, aligned_orientation}},\n       {Direction<3>::upper_eta(), {8, aligned_orientation}},\n       {Direction<3>::lower_xi(), {7 - 4, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {7 + 9, aligned_orientation}}},\n      // Block 8 - layer 0 south 8\n      {{Direction<3>::lower_eta(), {7, aligned_orientation}},\n       {Direction<3>::upper_eta(), {5, aligned_orientation}},\n       {Direction<3>::lower_xi(), {8 - 4, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {8 + 9, aligned_orientation}}},\n      // Block 9 - layer 1 center 0\n      {{Direction<3>::upper_xi(), {1 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {2 + 9, quarter_turn_ccw}},\n       {Direction<3>::lower_xi(), {3 + 9, half_turn}},\n       {Direction<3>::lower_eta(), {4 + 9, quarter_turn_cw}},\n       {Direction<3>::lower_zeta(), {9 - 9, aligned_orientation}}},\n      // Block 10 - layer 1 east 1\n      {{Direction<3>::lower_eta(), {4 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {2 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0 + 9, aligned_orientation}},\n       {Direction<3>::upper_xi(), {1 + 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {10 - 9, aligned_orientation}}},\n      // Block 11 - layer 1 north 2\n      {{Direction<3>::lower_eta(), {1 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {3 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0 + 9, quarter_turn_cw}},\n       {Direction<3>::upper_xi(), {2 + 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {11 - 9, aligned_orientation}}},\n      // Block 12 - layer 1 west 3\n      {{Direction<3>::lower_eta(), {2 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {4 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0 + 9, half_turn}},\n       {Direction<3>::upper_xi(), {3 + 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {12 - 9, aligned_orientation}}},\n      // Block 13 - layer 1 south 4\n      {{Direction<3>::lower_eta(), {3 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {1 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0 + 9, quarter_turn_ccw}},\n       {Direction<3>::upper_xi(), {4 + 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {13 - 9, aligned_orientation}}},\n      // Block 14 - layer 1 east 5\n      {{Direction<3>::lower_eta(), {8 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {6 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {5 - 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {14 - 9, aligned_orientation}}},\n      // Block 15 - layer 1 north 6\n      {{Direction<3>::lower_eta(), {5 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {7 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {6 - 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {15 - 9, aligned_orientation}}},\n      // Block 16 - layer 1 west 7\n      {{Direction<3>::lower_eta(), {6 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {8 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {7 - 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {16 - 9, aligned_orientation}}},\n      // Block 17 - layer 1 south 8\n      {{Direction<3>::lower_eta(), {7 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {5 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {8 - 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {17 - 9, aligned_orientation}}}};\n\n  expected_external_boundaries = std::vector<std::unordered_set<Direction<3>>>{\n      {Direction<3>::lower_zeta()},\n      {Direction<3>::lower_zeta()},\n      {Direction<3>::lower_zeta()},\n      {Direction<3>::lower_zeta()},\n      {Direction<3>::lower_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::lower_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::lower_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::lower_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::lower_zeta()},\n      {Direction<3>::upper_zeta()},\n      {Direction<3>::upper_zeta()},\n      {Direction<3>::upper_zeta()},\n      {Direction<3>::upper_zeta()},\n      {Direction<3>::upper_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::upper_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::upper_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::upper_zeta()},\n      {Direction<3>::upper_xi(), Direction<3>::upper_zeta()}};\n\n  const std::vector<std::array<size_t, 3>>& expected_extents{\n      {{expected_wedge_extents[1], expected_wedge_extents[1],\n        expected_wedge_extents[2]}},\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      {{expected_wedge_extents[1], expected_wedge_extents[1],\n        expected_wedge_extents[2]}},\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents};\n\n  CHECK(refined_cylinder.initial_extents() == expected_extents);\n  CHECK(refined_cylinder.initial_refinement_levels() ==\n        expected_refinement_level);\n  using TargetFrame = Frame::Inertial;\n  using Interval = CoordinateMaps::Interval;\n  using Interval3D =\n      CoordinateMaps::ProductOf3Maps<Interval, Interval, Interval>;\n  using Wedge2D = CoordinateMaps::Wedge<2>;\n  using Wedge3DPrism = CoordinateMaps::ProductOf2Maps<Wedge2D, Interval>;\n  const auto use_both_halves = Wedge2D::WedgeHalves::Both;\n  using Distribution = domain::CoordinateMaps::Distribution;\n  const auto angular_distribution =\n      use_equiangular_map ? Distribution::Equiangular : Distribution::Linear;\n  // in this section, the coord_map is filled same as with the cylinder;\n  // first the first shell with radial boundaries\n  // (inner_radius, radial_partitioning.at(0)) and circularity changing from 0\n  // to 1 is generated, secondly a further shell with radial boundaries\n  // (radial_partitioning.at(0), outer_radius) and uniform circularity is added.\n  // this is then repeated for (lower_z_bound, partitioning_in_z.at(0)) and\n  // (partitioning_in_z.at(0), upper_z_bound)\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          Interval3D{Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                              Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map, use_both_halves,\n                  outer_radial_distribution},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map, use_both_halves,\n                  outer_radial_distribution},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map, use_both_halves,\n                  outer_radial_distribution},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map, use_both_halves,\n                  outer_radial_distribution},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   Distribution::Linear}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          Interval3D{Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                              uppermost_distribution_in_z, lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   uppermost_distribution_in_z, lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   uppermost_distribution_in_z, lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   uppermost_distribution_in_z, lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   uppermost_distribution_in_z, lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map, use_both_halves,\n                  outer_radial_distribution},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   uppermost_distribution_in_z, lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map, use_both_halves,\n                  outer_radial_distribution},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   uppermost_distribution_in_z, lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map, use_both_halves,\n                  outer_radial_distribution},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   uppermost_distribution_in_z, lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map, use_both_halves,\n                  outer_radial_distribution},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   uppermost_distribution_in_z, lower_z_bound}}));\n\n  test_domain_construction(domain, expected_block_neighbors,\n                           expected_external_boundaries, coord_maps);\n}\n\nvoid test_refined_cylinder_periodic_boundaries(const bool use_equiangular_map) {\n  INFO(\"Refined Cylinder periodic boundaries\");\n  CAPTURE(use_equiangular_map);\n  // definition of an arbitrary refined cylinder\n  const double inner_radius = 0.3;\n  const double outer_radius = 1.0;\n  const double lower_z_bound = -1.5;\n  const double upper_z_bound = 5.0;\n  const std::vector<double> radial_partitioning = {0.7};\n  const std::vector<double> partitioning_in_z = {1.5};\n  const std::vector<domain::CoordinateMaps::Distribution> radial_distribution{\n      domain::CoordinateMaps::Distribution::Linear,\n      domain::CoordinateMaps::Distribution::Linear};\n  const std::vector<domain::CoordinateMaps::Distribution> distribution_in_z{\n      domain::CoordinateMaps::Distribution::Linear,\n      domain::CoordinateMaps::Distribution::Linear};\n  const size_t refinement_level = 2;\n  const std::array<size_t, 3> expected_wedge_extents{{5, 4, 3}};\n  const std::vector<std::array<size_t, 3>> expected_refinement_level{\n      (1 + 4 * (1 + radial_partitioning.size())) *\n          (1 + partitioning_in_z.size()),\n      make_array<3>(refinement_level)};\n  const creators::Cylinder refined_cylinder{inner_radius,\n                                            outer_radius,\n                                            lower_z_bound,\n                                            upper_z_bound,\n                                            std::make_unique<PeriodicBc>(),\n                                            std::make_unique<PeriodicBc>(),\n                                            create_mantle_boundary_condition(),\n                                            refinement_level,\n                                            expected_wedge_extents,\n                                            use_equiangular_map,\n                                            radial_partitioning,\n                                            partitioning_in_z,\n                                            radial_distribution,\n                                            distribution_in_z};\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      refined_cylinder, true, true);\n\n  const OrientationMap<3> aligned_orientation =\n      OrientationMap<3>::create_aligned();\n  const OrientationMap<3> quarter_turn_ccw(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_eta(), Direction<3>::upper_xi(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> half_turn(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n       Direction<3>::upper_zeta()}});\n  const OrientationMap<3> quarter_turn_cw(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n       Direction<3>::upper_zeta()}});\n  std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_block_neighbors{};\n  std::vector<std::unordered_set<Direction<3>>> expected_external_boundaries{};\n  using TargetFrame = Frame::Inertial;\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>>\n      coord_maps{};\n  expected_block_neighbors = std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n      // Block 0 - layer 0 center 0\n      {{Direction<3>::upper_xi(), {1, aligned_orientation}},\n       {Direction<3>::upper_eta(), {2, quarter_turn_ccw}},\n       {Direction<3>::lower_xi(), {3, half_turn}},\n       {Direction<3>::lower_eta(), {4, quarter_turn_cw}},\n       {Direction<3>::lower_zeta(), {0 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {0 + 9, aligned_orientation}}},\n      // Block 1 - layer 0 east 1\n      {{Direction<3>::lower_eta(), {4, aligned_orientation}},\n       {Direction<3>::upper_eta(), {2, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0, aligned_orientation}},\n       {Direction<3>::upper_xi(), {1 + 4, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {1 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {1 + 9, aligned_orientation}}},\n      // Block 2 - layer 0 north 2\n      {{Direction<3>::lower_eta(), {1, aligned_orientation}},\n       {Direction<3>::upper_eta(), {3, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0, quarter_turn_cw}},\n       {Direction<3>::upper_xi(), {2 + 4, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {2 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {2 + 9, aligned_orientation}}},\n      // Block 3 - layer 0 west 3\n      {{Direction<3>::lower_eta(), {2, aligned_orientation}},\n       {Direction<3>::upper_eta(), {4, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0, half_turn}},\n       {Direction<3>::upper_xi(), {3 + 4, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {3 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {3 + 9, aligned_orientation}}},\n      // Block 4 - layer 0 south 4\n      {{Direction<3>::lower_eta(), {3, aligned_orientation}},\n       {Direction<3>::upper_eta(), {1, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0, quarter_turn_ccw}},\n       {Direction<3>::upper_xi(), {4 + 4, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {4 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {4 + 9, aligned_orientation}}},\n      // Block 5 - layer 0 east 5\n      {{Direction<3>::lower_eta(), {8, aligned_orientation}},\n       {Direction<3>::upper_eta(), {6, aligned_orientation}},\n       {Direction<3>::lower_xi(), {5 - 4, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {5 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {5 + 9, aligned_orientation}}},\n      // Block 6 - layer 0 north 6\n      {{Direction<3>::lower_eta(), {5, aligned_orientation}},\n       {Direction<3>::upper_eta(), {7, aligned_orientation}},\n       {Direction<3>::lower_xi(), {6 - 4, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {6 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {6 + 9, aligned_orientation}}},\n      // Block 7 - layer 0 west 7\n      {{Direction<3>::lower_eta(), {6, aligned_orientation}},\n       {Direction<3>::upper_eta(), {8, aligned_orientation}},\n       {Direction<3>::lower_xi(), {7 - 4, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {7 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {7 + 9, aligned_orientation}}},\n      // Block 8 - layer 0 south 8\n      {{Direction<3>::lower_eta(), {7, aligned_orientation}},\n       {Direction<3>::upper_eta(), {5, aligned_orientation}},\n       {Direction<3>::lower_xi(), {8 - 4, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {8 + 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {8 + 9, aligned_orientation}}},\n      // Block 9 - layer 1 center 0\n      {{Direction<3>::upper_xi(), {1 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {2 + 9, quarter_turn_ccw}},\n       {Direction<3>::lower_xi(), {3 + 9, half_turn}},\n       {Direction<3>::lower_eta(), {4 + 9, quarter_turn_cw}},\n       {Direction<3>::lower_zeta(), {9 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {9 - 9, aligned_orientation}}},\n      // Block 10 - layer 1 east 1\n      {{Direction<3>::lower_eta(), {4 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {2 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0 + 9, aligned_orientation}},\n       {Direction<3>::upper_xi(), {1 + 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {10 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {10 - 9, aligned_orientation}}},\n      // Block 11 - layer 1 north 2\n      {{Direction<3>::lower_eta(), {1 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {3 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0 + 9, quarter_turn_cw}},\n       {Direction<3>::upper_xi(), {2 + 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {11 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {11 - 9, aligned_orientation}}},\n      // Block 12 - layer 1 west 3\n      {{Direction<3>::lower_eta(), {2 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {4 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0 + 9, half_turn}},\n       {Direction<3>::upper_xi(), {3 + 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {12 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {12 - 9, aligned_orientation}}},\n      // Block 13 - layer 1 south 4\n      {{Direction<3>::lower_eta(), {3 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {1 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {0 + 9, quarter_turn_ccw}},\n       {Direction<3>::upper_xi(), {4 + 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {13 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {13 - 9, aligned_orientation}}},\n      // Block 14 - layer 1 east 5\n      {{Direction<3>::lower_eta(), {8 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {6 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {5 - 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {14 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {14 - 9, aligned_orientation}}},\n      // Block 15 - layer 1 north 6\n      {{Direction<3>::lower_eta(), {5 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {7 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {6 - 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {15 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {15 - 9, aligned_orientation}}},\n      // Block 16 - layer 1 west 7\n      {{Direction<3>::lower_eta(), {6 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {8 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {7 - 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {16 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {16 - 9, aligned_orientation}}},\n      // Block 17 - layer 1 south 8\n      {{Direction<3>::lower_eta(), {7 + 9, aligned_orientation}},\n       {Direction<3>::upper_eta(), {5 + 9, aligned_orientation}},\n       {Direction<3>::lower_xi(), {8 - 4 + 9, aligned_orientation}},\n       {Direction<3>::lower_zeta(), {17 - 9, aligned_orientation}},\n       {Direction<3>::upper_zeta(), {17 - 9, aligned_orientation}}}};\n\n  expected_external_boundaries =\n      std::vector<std::unordered_set<Direction<3>>>{{},\n                                                    {},\n                                                    {},\n                                                    {},\n                                                    {},\n                                                    {Direction<3>::upper_xi()},\n                                                    {Direction<3>::upper_xi()},\n                                                    {Direction<3>::upper_xi()},\n                                                    {Direction<3>::upper_xi()},\n                                                    {},\n                                                    {},\n                                                    {},\n                                                    {},\n                                                    {},\n                                                    {Direction<3>::upper_xi()},\n                                                    {Direction<3>::upper_xi()},\n                                                    {Direction<3>::upper_xi()},\n                                                    {Direction<3>::upper_xi()}};\n\n  const std::vector<std::array<size_t, 3>>& expected_extents{\n      {{expected_wedge_extents[1], expected_wedge_extents[1],\n        expected_wedge_extents[2]}},\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      {{expected_wedge_extents[1], expected_wedge_extents[1],\n        expected_wedge_extents[2]}},\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents};\n\n  CHECK(refined_cylinder.initial_extents() == expected_extents);\n  CHECK(refined_cylinder.initial_refinement_levels() ==\n        expected_refinement_level);\n  using TargetFrame = Frame::Inertial;\n  using Interval = CoordinateMaps::Interval;\n  using Interval3D =\n      CoordinateMaps::ProductOf3Maps<Interval, Interval, Interval>;\n  using Wedge2D = CoordinateMaps::Wedge<2>;\n  using Wedge3DPrism = CoordinateMaps::ProductOf2Maps<Wedge2D, Interval>;\n  using Distribution = domain::CoordinateMaps::Distribution;\n  const auto angular_distribution =\n      use_equiangular_map ? Distribution::Equiangular : Distribution::Linear;\n  // in this section, the coord_map is filled same as with the cylinder;\n  // first the first shell with radial boundaries\n  // (inner_radius, radial_partitioning.at(0)) and circularity changing from 0\n  // to 1 is generated, secondly a further shell with radial boundaries\n  // (radial_partitioning.at(0), outer_radius) and uniform circularity is added.\n  // this is then repeated for (lower_z_bound, partitioning_in_z.at(0)) and\n  // (partitioning_in_z.at(0), upper_z_bound)\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          Interval3D{Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                              distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, lower_z_bound, partitioning_in_z.at(0),\n                   distribution_in_z.at(0), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          Interval3D{Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                              inner_radius / sqrt(2.0), angular_distribution),\n                     Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                              distribution_in_z.at(1), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   distribution_in_z.at(1), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   distribution_in_z.at(1), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   distribution_in_z.at(1), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{inner_radius, radial_partitioning.at(0), 0.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   distribution_in_z.at(1), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   distribution_in_z.at(1), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   distribution_in_z.at(1), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   distribution_in_z.at(1), lower_z_bound}}));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(Wedge3DPrism{\n          Wedge2D{radial_partitioning.at(0), outer_radius, 1.0, 1.0,\n                  OrientationMap<2>{std::array<Direction<2>, 2>{\n                      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                  use_equiangular_map},\n          Interval{-1.0, 1.0, partitioning_in_z.at(0), upper_z_bound,\n                   distribution_in_z.at(1), lower_z_bound}}));\n\n  test_domain_construction(domain, expected_block_neighbors,\n                           expected_external_boundaries, coord_maps);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.Cylinder\", \"[Domain][Unit]\") {\n  test_cylinder_no_refinement();\n\n  {\n    INFO(\"Test for Cylinder with one additional layer and shell\");\n    test_refined_cylinder_boundaries(\n        true, domain::CoordinateMaps::Distribution::Linear,\n        domain::CoordinateMaps::Distribution::Linear);\n    test_refined_cylinder_boundaries(\n        true, domain::CoordinateMaps::Distribution::Logarithmic,\n        domain::CoordinateMaps::Distribution::Linear);\n    test_refined_cylinder_boundaries(\n        true, domain::CoordinateMaps::Distribution::Inverse,\n        domain::CoordinateMaps::Distribution::Linear);\n    test_refined_cylinder_boundaries(\n        false, domain::CoordinateMaps::Distribution::Linear,\n        domain::CoordinateMaps::Distribution::Linear);\n    test_refined_cylinder_boundaries(\n        false, domain::CoordinateMaps::Distribution::Logarithmic,\n        domain::CoordinateMaps::Distribution::Linear);\n    test_refined_cylinder_boundaries(\n        false, domain::CoordinateMaps::Distribution::Inverse,\n        domain::CoordinateMaps::Distribution::Linear);\n    test_refined_cylinder_boundaries(\n        true, domain::CoordinateMaps::Distribution::Linear,\n        domain::CoordinateMaps::Distribution::Logarithmic);\n    test_refined_cylinder_boundaries(\n        true, domain::CoordinateMaps::Distribution::Logarithmic,\n        domain::CoordinateMaps::Distribution::Logarithmic);\n    test_refined_cylinder_boundaries(\n        true, domain::CoordinateMaps::Distribution::Inverse,\n        domain::CoordinateMaps::Distribution::Logarithmic);\n    test_refined_cylinder_boundaries(\n        false, domain::CoordinateMaps::Distribution::Linear,\n        domain::CoordinateMaps::Distribution::Logarithmic);\n    test_refined_cylinder_boundaries(\n        false, domain::CoordinateMaps::Distribution::Logarithmic,\n        domain::CoordinateMaps::Distribution::Logarithmic);\n    test_refined_cylinder_boundaries(\n        false, domain::CoordinateMaps::Distribution::Inverse,\n        domain::CoordinateMaps::Distribution::Logarithmic);\n    test_refined_cylinder_periodic_boundaries(true);\n    test_refined_cylinder_periodic_boundaries(false);\n  }\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_CylindricalBinaryCompactObject.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <iomanip>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <sstream>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Creators/CylindricalBinaryCompactObject.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing ExpirationTimeMap = std::unordered_map<std::string, double>;\nusing CylBCO = ::domain::creators::CylindricalBinaryCompactObject;\nusing TimeDepOptions = domain::creators::bco::TimeDependentMapOptions<true>;\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_inner_boundary_condition() {\n  // TestHelpers::domain::BoundaryConditions::TestBoundaryCondition\n  // takes a direction and a block_id in its constructor.  In this\n  // test, these parameters are not used for anything (other than when\n  // comparing different BoundaryCondition objects using operator==)\n  // and they have no relationship with any actual boundary directions\n  // or block_ids in the Domain.  So we fill them with arbitrarily\n  // chosen values, making sure that the parameters are chosen\n  // differently for the inner and outer boundary conditions.\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::upper_xi(), 463);\n}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_outer_boundary_condition() {\n  // TestHelpers::domain::BoundaryConditions::TestBoundaryCondition\n  // takes a direction and a block_id in its constructor.  In this\n  // test, these parameters are not used for anything (other than when\n  // comparing different BoundaryCondition objects using operator==)\n  // and they have no relationship with any actual boundary directions\n  // or block_ids in the Domain.  So we fill them with arbitrarily\n  // chosen values, making sure that the parameters are chosen\n  // differently for the inner and outer boundary conditions.\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::upper_eta(), 314);\n}\n\nstd::pair<std::vector<std::string>,\n          std::unordered_map<std::string, std::unordered_set<std::string>>>\nblock_names_and_groups(const bool include_inner_sphere_A,\n                       const bool include_inner_sphere_B,\n                       const bool include_outer_sphere) {\n  std::vector<std::string> block_names{\n      \"CAFilledCylinderCenter\", \"CAFilledCylinderEast\",\n      \"CAFilledCylinderNorth\",  \"CAFilledCylinderWest\",\n      \"CAFilledCylinderSouth\",  \"CACylinderEast\",\n      \"CACylinderNorth\",        \"CACylinderWest\",\n      \"CACylinderSouth\",        \"EAFilledCylinderCenter\",\n      \"EAFilledCylinderEast\",   \"EAFilledCylinderNorth\",\n      \"EAFilledCylinderWest\",   \"EAFilledCylinderSouth\",\n      \"EACylinderEast\",         \"EACylinderNorth\",\n      \"EACylinderWest\",         \"EACylinderSouth\",\n      \"EBFilledCylinderCenter\", \"EBFilledCylinderEast\",\n      \"EBFilledCylinderNorth\",  \"EBFilledCylinderWest\",\n      \"EBFilledCylinderSouth\",  \"EBCylinderEast\",\n      \"EBCylinderNorth\",        \"EBCylinderWest\",\n      \"EBCylinderSouth\",        \"MAFilledCylinderCenter\",\n      \"MAFilledCylinderEast\",   \"MAFilledCylinderNorth\",\n      \"MAFilledCylinderWest\",   \"MAFilledCylinderSouth\",\n      \"MBFilledCylinderCenter\", \"MBFilledCylinderEast\",\n      \"MBFilledCylinderNorth\",  \"MBFilledCylinderWest\",\n      \"MBFilledCylinderSouth\",  \"CBFilledCylinderCenter\",\n      \"CBFilledCylinderEast\",   \"CBFilledCylinderNorth\",\n      \"CBFilledCylinderWest\",   \"CBFilledCylinderSouth\",\n      \"CBCylinderEast\",         \"CBCylinderNorth\",\n      \"CBCylinderWest\",         \"CBCylinderSouth\"};\n  std::unordered_map<std::string, std::unordered_set<std::string>> block_groups{\n      {\"Outer\",\n       {{\"CAFilledCylinderCenter\", \"CBCylinderEast\", \"CAFilledCylinderEast\",\n         \"CAFilledCylinderNorth\", \"CBFilledCylinderNorth\", \"CACylinderEast\",\n         \"CBFilledCylinderEast\", \"CAFilledCylinderSouth\", \"CACylinderNorth\",\n         \"CAFilledCylinderWest\", \"CACylinderWest\", \"CACylinderSouth\",\n         \"CBFilledCylinderCenter\", \"CBFilledCylinderWest\",\n         \"CBFilledCylinderSouth\", \"CBCylinderNorth\", \"CBCylinderWest\",\n         \"CBCylinderSouth\"}}},\n      {\"InnerA\",\n       {\"EAFilledCylinderCenter\", \"MAFilledCylinderNorth\", \"EACylinderSouth\",\n        \"EAFilledCylinderSouth\", \"EACylinderNorth\", \"EACylinderWest\",\n        \"MAFilledCylinderCenter\", \"EAFilledCylinderNorth\",\n        \"EAFilledCylinderWest\", \"MAFilledCylinderSouth\", \"EACylinderEast\",\n        \"MAFilledCylinderEast\", \"EAFilledCylinderEast\",\n        \"MAFilledCylinderWest\"}},\n      {\"InnerB\",\n       {\"EBFilledCylinderEast\", \"MBFilledCylinderEast\", \"EBFilledCylinderSouth\",\n        \"EBFilledCylinderNorth\", \"EBFilledCylinderWest\", \"EBCylinderEast\",\n        \"MBFilledCylinderWest\", \"EBCylinderNorth\", \"EBCylinderSouth\",\n        \"EBCylinderWest\", \"MBFilledCylinderCenter\", \"EBFilledCylinderCenter\",\n        \"MBFilledCylinderNorth\", \"MBFilledCylinderSouth\"}}};\n\n  if (include_inner_sphere_A) {\n    block_names.insert(\n        block_names.end(),\n        {\"InnerSphereEAFilledCylinderCenter\", \"InnerSphereEAFilledCylinderEast\",\n         \"InnerSphereEAFilledCylinderNorth\", \"InnerSphereEAFilledCylinderWest\",\n         \"InnerSphereEAFilledCylinderSouth\",\n         \"InnerSphereMAFilledCylinderCenter\", \"InnerSphereMAFilledCylinderEast\",\n         \"InnerSphereMAFilledCylinderNorth\", \"InnerSphereMAFilledCylinderWest\",\n         \"InnerSphereMAFilledCylinderSouth\", \"InnerSphereEACylinderEast\",\n         \"InnerSphereEACylinderNorth\", \"InnerSphereEACylinderWest\",\n         \"InnerSphereEACylinderSouth\"});\n    block_groups.insert(\n        {\"InnerSphereA\",\n         {\"InnerSphereEAFilledCylinderCenter\",\n          \"InnerSphereEAFilledCylinderEast\", \"InnerSphereEAFilledCylinderNorth\",\n          \"InnerSphereEAFilledCylinderWest\", \"InnerSphereEAFilledCylinderSouth\",\n          \"InnerSphereMAFilledCylinderCenter\",\n          \"InnerSphereMAFilledCylinderEast\", \"InnerSphereMAFilledCylinderNorth\",\n          \"InnerSphereMAFilledCylinderWest\", \"InnerSphereMAFilledCylinderSouth\",\n          \"InnerSphereEACylinderEast\", \"InnerSphereEACylinderNorth\",\n          \"InnerSphereEACylinderWest\", \"InnerSphereEACylinderSouth\"}});\n  }\n  if (include_inner_sphere_B) {\n    block_names.insert(\n        block_names.end(),\n        {\"InnerSphereEBFilledCylinderCenter\", \"InnerSphereEBFilledCylinderEast\",\n         \"InnerSphereEBFilledCylinderNorth\", \"InnerSphereEBFilledCylinderWest\",\n         \"InnerSphereEBFilledCylinderSouth\",\n         \"InnerSphereMBFilledCylinderCenter\", \"InnerSphereMBFilledCylinderEast\",\n         \"InnerSphereMBFilledCylinderNorth\", \"InnerSphereMBFilledCylinderWest\",\n         \"InnerSphereMBFilledCylinderSouth\", \"InnerSphereEBCylinderEast\",\n         \"InnerSphereEBCylinderNorth\", \"InnerSphereEBCylinderWest\",\n         \"InnerSphereEBCylinderSouth\"});\n    block_groups.insert(\n        {\"InnerSphereB\",\n         {\"InnerSphereEBFilledCylinderCenter\",\n          \"InnerSphereEBFilledCylinderEast\", \"InnerSphereEBFilledCylinderNorth\",\n          \"InnerSphereEBFilledCylinderWest\", \"InnerSphereEBFilledCylinderSouth\",\n          \"InnerSphereMBFilledCylinderCenter\",\n          \"InnerSphereMBFilledCylinderEast\", \"InnerSphereMBFilledCylinderNorth\",\n          \"InnerSphereMBFilledCylinderWest\", \"InnerSphereMBFilledCylinderSouth\",\n          \"InnerSphereEBCylinderEast\", \"InnerSphereEBCylinderNorth\",\n          \"InnerSphereEBCylinderWest\", \"InnerSphereEBCylinderSouth\"}});\n  }\n  if (include_outer_sphere) {\n    block_names.insert(\n        block_names.end(),\n        {\"OuterSphereCAFilledCylinderCenter\", \"OuterSphereCAFilledCylinderEast\",\n         \"OuterSphereCAFilledCylinderNorth\", \"OuterSphereCAFilledCylinderWest\",\n         \"OuterSphereCAFilledCylinderSouth\",\n         \"OuterSphereCBFilledCylinderCenter\", \"OuterSphereCBFilledCylinderEast\",\n         \"OuterSphereCBFilledCylinderNorth\", \"OuterSphereCBFilledCylinderWest\",\n         \"OuterSphereCBFilledCylinderSouth\", \"OuterSphereCACylinderEast\",\n         \"OuterSphereCACylinderNorth\", \"OuterSphereCACylinderWest\",\n         \"OuterSphereCACylinderSouth\", \"OuterSphereCBCylinderEast\",\n         \"OuterSphereCBCylinderNorth\", \"OuterSphereCBCylinderWest\",\n         \"OuterSphereCBCylinderSouth\"});\n    block_groups.insert(\n        {\"OuterSphere\",\n         {\"OuterSphereCAFilledCylinderCenter\",\n          \"OuterSphereCAFilledCylinderEast\", \"OuterSphereCAFilledCylinderNorth\",\n          \"OuterSphereCAFilledCylinderWest\", \"OuterSphereCAFilledCylinderSouth\",\n          \"OuterSphereCBFilledCylinderCenter\",\n          \"OuterSphereCBFilledCylinderEast\", \"OuterSphereCBFilledCylinderNorth\",\n          \"OuterSphereCBFilledCylinderWest\", \"OuterSphereCBFilledCylinderSouth\",\n          \"OuterSphereCACylinderEast\", \"OuterSphereCACylinderNorth\",\n          \"OuterSphereCACylinderWest\", \"OuterSphereCACylinderSouth\",\n          \"OuterSphereCBCylinderEast\", \"OuterSphereCBCylinderNorth\",\n          \"OuterSphereCBCylinderWest\", \"OuterSphereCBCylinderSouth\"}});\n  }\n\n  return std::make_pair(block_names, block_groups);\n}\n\nstd::string stringize(const bool t) { return t ? \"true\" : \"false\"; }\n\nstatic constexpr int precision = std::numeric_limits<double>::max_digits10;\n\nstd::string stringize(const double t) {\n  std::stringstream ss{};\n  ss << std::setprecision(precision) << t;\n  return ss.str();\n}\n\nstd::string stringize(const std::array<double, 3>& t) {\n  std::stringstream result{};\n  result << std::setprecision(precision) << \"[\";\n  bool first = true;\n  for (const auto& item : t) {\n    if (not first) {\n      result << \", \";\n    }\n    result << item;\n    first = false;\n  }\n  result << \"]\";\n  return result.str();\n}\n\nstd::string create_option_string(\n    const bool add_time_dependence,\n    const bool with_additional_outer_radial_refinement,\n    const bool with_additional_grid_points, const bool include_outer_sphere,\n    const bool include_inner_sphere_A, const bool include_inner_sphere_B,\n    const bool add_boundary_condition, const bool use_equiangular_map,\n    const std::array<double, 3>& center_objectA,\n    const std::array<double, 3>& center_objectB,\n    const double inner_radius_objectA, const double inner_radius_objectB,\n    const double outer_radius) {\n  const std::string time_dependence{\n      add_time_dependence ? (\"  TimeDependentMaps:\\n\"\n                             \"    GridCenters:\\n\"\n                             \"      ScaleInspiralRateBy: 0.8\\n\"\n                             \"      SpecEvolutionParametersPerlFile: \"s +\n                             unit_test_src_path() +\n                             \"/../InputFiles/GrMhd/GhValenciaDivClean/\"\n                             \"EvolutionParameters.perl\\n\"\n                             \"    InitialTime: 1.0\\n\"\n                             \"    ExpansionMap: None\\n\"\n                             \"    RotationMap:\\n\"\n                             \"      InitialAngularVelocity: [0.0, 0.0, -0.2]\\n\"\n                             \"    TranslationMap: None\\n\"\n                             \"    SkewMap: None\\n\"\n                             \"    ShapeMapA:\\n\"\n                             \"      LMax: 8\\n\"\n                             \"      CoefficientTruncationLimit: 0.\\n\"\n                             \"      InitialValues: Spherical\\n\"\n                             \"      SizeInitialValues: [1.1, 0.0, 0.0]\\n\"\n                             \"    ShapeMapB:\\n\"\n                             \"      LMax: 8\\n\"\n                             \"      CoefficientTruncationLimit: 0.\\n\"\n                             \"      InitialValues: Spherical\\n\"\n                             \"      SizeInitialValues: [1.2, 0.0, 0.0]\\n\")\n                          : \"  TimeDependentMaps: None\\n\"};\n\n  const std::string boundary_conditions{\n      add_boundary_condition ? std::string{\"  BoundaryConditions:\\n\"\n                                           \"    InnerBoundary:\\n\"\n                                           \"      TestBoundaryCondition:\\n\"\n                                           \"        Direction: upper-xi\\n\"\n                                           \"        BlockId: 463\\n\"\n                                           \"    OuterBoundary:\\n\"\n                                           \"      TestBoundaryCondition:\\n\"\n                                           \"        Direction: upper-eta\\n\"\n                                           \"        BlockId: 314\\n\"}\n                             : \"\"};\n\n  const auto initial_structure =\n      [&include_outer_sphere, &include_inner_sphere_A, &include_inner_sphere_B](\n          const bool include_extra, const size_t value) {\n        const std::string same = \"[\" + get_output(value) + \",\" +\n                                 get_output(value) + \",\" + get_output(value) +\n                                 \"]\";\n        const std::string one_more = \"[\" + get_output(value + 1) + \",\" +\n                                     get_output(value) + \",\" +\n                                     get_output(value) + \"]\";\n        std::string result{};\n        if (include_extra) {\n          result += \"\\n    Outer: \" + one_more;\n          result += \"\\n    InnerA: \" + same;\n          result += \"\\n    InnerB: \" + one_more;\n          if (include_outer_sphere) {\n            result += \"\\n    OuterSphere: \" + one_more;\n          }\n          if (include_inner_sphere_A) {\n            result += \"\\n    InnerSphereA: \" + same;\n          }\n          if (include_inner_sphere_B) {\n            result += \"\\n    InnerSphereB: \" + same;\n          }\n        } else {\n          result = \" \" + get_output(value);\n        }\n        return result;\n      };\n\n  return \"CylindricalBinaryCompactObject:\"\n         \"\\n  CenterA: \" +\n         stringize(center_objectA) +\n         \"\\n  RadiusA: \" + stringize(inner_radius_objectA) +\n         \"\\n  CenterB: \" + stringize(center_objectB) +\n         \"\\n  RadiusB: \" + stringize(inner_radius_objectB) +\n         \"\\n  OuterRadius: \" + stringize(outer_radius) +\n         \"\\n  UseEquiangularMap: \" + stringize(use_equiangular_map) +\n         \"\\n  IncludeInnerSphereA: \" + stringize(include_inner_sphere_A) +\n         \"\\n  IncludeInnerSphereB: \" + stringize(include_inner_sphere_B) +\n         \"\\n  IncludeOuterSphere: \" + stringize(include_outer_sphere) +\n         \"\\n  InitialRefinement:\" +\n         initial_structure(with_additional_outer_radial_refinement, 1) +\n         \"\\n  InitialGridPoints:\" +\n         initial_structure(with_additional_grid_points, 3) + \"\\n\" +\n         time_dependence + boundary_conditions;\n}\n\nTimeDepOptions construct_time_dependent_options() {\n  constexpr double expected_time = 1.0;  // matches InitialTime: 1.0 above\n\n  // Matches RotationMap:InitialAngularVelocity above\n  const DataVector initial_angular_velocity{{0.0, 0.0, -0.2}};\n\n  // Hardcoded in CylindricalBinaryCompactObject.cpp\n  const std::array<DataVector, 1> initial_quaternion_coefs{\n      {{1.0, 0.0, 0.0, 0.0}}};\n\n  // Matches SizeMap{A,B}::InitialValues above\n  const std::array<DataVector, 4> initial_size_A_coefs{\n      {{1.1}, {0.0}, {0.0}, {0.0}}};\n  const std::array<DataVector, 4> initial_size_B_coefs{\n      {{1.2}, {0.0}, {0.0}, {0.0}}};\n\n  // No expansion map options because of above option string\n  return TimeDepOptions{\n      expected_time,\n      std::nullopt,\n      domain::creators::time_dependent_options::RotationMapOptions<false>{\n          std::array{initial_angular_velocity[0], initial_angular_velocity[1],\n                     initial_angular_velocity[2]}},\n      std::nullopt,\n      std::nullopt,\n      domain::creators::time_dependent_options::ShapeMapOptions<\n          false, domain::ObjectLabel::A>{\n          8_st,\n          std::nullopt,\n          {{initial_size_A_coefs[0][0], initial_size_A_coefs[1][0],\n            initial_size_A_coefs[1][0]}}},\n      domain::creators::time_dependent_options::ShapeMapOptions<\n          false, domain::ObjectLabel::B>{\n          8_st,\n          std::nullopt,\n          {{initial_size_B_coefs[0][0], initial_size_B_coefs[1][0],\n            initial_size_B_coefs[1][0]}}},\n      std::nullopt};\n}\n\nvoid test_construction(\n    const CylBCO& creator, const bool with_boundary_conditions,\n    const bool include_inner_sphere_A, const bool include_inner_sphere_B,\n    const bool include_outer_sphere, const double inner_radius_objectA,\n    const double inner_radius_objectB, const double outer_radius,\n    const std::array<double, 3>& center_objectA,\n    const std::array<double, 3>& center_objectB,\n    const std::vector<double>& times_to_check) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      creator, with_boundary_conditions, false, times_to_check);\n\n  const auto& [block_names, block_groups] = block_names_and_groups(\n      include_inner_sphere_A, include_inner_sphere_B, include_outer_sphere);\n\n  CHECK(creator.block_names() == block_names);\n  CHECK(creator.block_groups() == block_groups);\n\n  const auto& grid_anchors = creator.grid_anchors();\n  CHECK(grid_anchors.size() == 3);\n  const auto& grid_anchor_center_a = grid_anchors.at(\"CenterA\");\n  const auto& grid_anchor_center_b = grid_anchors.at(\"CenterB\");\n  const auto& grid_anchor_center = grid_anchors.at(\"Center\");\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(grid_anchor_center_a.get(i) == center_objectA.at(i));\n    CHECK(grid_anchor_center_b.get(i) == center_objectB.at(i));\n    CHECK_ITERABLE_APPROX(grid_anchor_center.get(i),\n                          0.5 * (center_objectA.at(i) + center_objectB.at(i)));\n  }\n\n  CHECK(domain.excision_spheres().size() == 2);\n  const auto& excision_sphere_a =\n      domain.excision_spheres().at(\"ExcisionSphereA\");\n  CHECK(excision_sphere_a.radius() == inner_radius_objectA);\n  const auto& excision_sphere_b =\n      domain.excision_spheres().at(\"ExcisionSphereB\");\n  CHECK(excision_sphere_b.radius() == inner_radius_objectB);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(excision_sphere_a.center().get(i) == center_objectA.at(i));\n    CHECK(excision_sphere_b.center().get(i) == center_objectB.at(i));\n  }\n  const auto& blocks = domain.blocks();\n  const auto& block = blocks[0];\n  CHECK(block.is_time_dependent() == excision_sphere_a.is_time_dependent());\n  CHECK(block.is_time_dependent() == excision_sphere_b.is_time_dependent());\n\n  if (block.is_time_dependent()) {\n    // Taken from option string above\n    const double initial_time = 1.0;\n    const double time = 2.0;\n    const double z_angle = (time - initial_time) * -0.2;\n    const auto functions_of_time = creator.functions_of_time();\n    const auto& center_a = excision_sphere_a.center();\n    const auto& center_b = excision_sphere_b.center();\n    const auto& map_a = excision_sphere_a.moving_mesh_grid_to_inertial_map();\n    const auto& map_b = excision_sphere_b.moving_mesh_grid_to_inertial_map();\n\n    const auto mapped_point_a = map_a(center_a, time, functions_of_time);\n    const auto mapped_point_b = map_b(center_b, time, functions_of_time);\n\n    // Just a rotation\n    const tnsr::I<double, 3> expected_mapped_point_a{\n        {get<0>(center_a) * cos(z_angle) - get<1>(center_a) * sin(z_angle),\n         get<0>(center_a) * sin(z_angle) + get<1>(center_a) * cos(z_angle),\n         get<2>(center_a)}};\n    const tnsr::I<double, 3> expected_mapped_point_b{\n        {get<0>(center_b) * cos(z_angle) - get<1>(center_b) * sin(z_angle),\n         get<0>(center_b) * sin(z_angle) + get<1>(center_b) * cos(z_angle),\n         get<2>(center_b)}};\n\n    CHECK_ITERABLE_APPROX(expected_mapped_point_a, mapped_point_a);\n    CHECK_ITERABLE_APPROX(expected_mapped_point_b, mapped_point_b);\n\n    // Check that blocks have expected time dependent maps\n    constexpr double xi_min = 0.25;\n    const double expected_xi = std::max(\n        xi_min, std::abs(center_objectA[0]) / (std::abs(center_objectA[0]) +\n                                               std::abs(center_objectB[0])));\n\n    // Build expected time dependent maps to check against\n    const double expected_cut_spheres_offset_factor = 0.99;\n    const double expected_cutting_plane =\n        expected_cut_spheres_offset_factor *\n        ((1.0 - expected_xi) * center_objectB[0] +\n         expected_xi * center_objectA[0]);\n    const double expected_outer_radius_A =\n        inner_radius_objectA +\n        0.5 * (std::abs(expected_cutting_plane - center_objectA[0]) -\n               inner_radius_objectA);\n    const double expected_outer_radius_B =\n        inner_radius_objectB +\n        0.5 * (std::abs(expected_cutting_plane - center_objectB[0]) -\n               inner_radius_objectB);\n    const double expected_inner_common_radius =\n        3.0 * (center_objectA[0] - center_objectB[0]);\n    TimeDepOptions time_dep_options = construct_time_dependent_options();\n    time_dep_options.build_maps(\n        std::array{center_objectA, center_objectB}, std::nullopt, std::nullopt,\n        std::array{expected_cutting_plane,\n                   0.5 * (center_objectA[1] + center_objectB[1]),\n                   0.5 * (center_objectA[2] + center_objectB[2])},\n        std::array{inner_radius_objectA, expected_outer_radius_A},\n        std::array{inner_radius_objectB, expected_outer_radius_B}, false, false,\n        expected_inner_common_radius, outer_radius);\n\n    for (size_t i = 0; i < blocks.size(); i++) {\n      const std::string block_name = gsl::at(block_names, i);\n      if (block_groups.at(\"InnerSphereA\").contains(block_name)) {\n        const auto& moving_mesh_grid_to_inertial_map =\n            blocks[i].moving_mesh_grid_to_inertial_map();\n        const auto& moving_mesh_grid_to_distorted_map =\n            blocks[i].moving_mesh_grid_to_distorted_map();\n        const auto& moving_mesh_distorted_to_inertial_map =\n            blocks[i].moving_mesh_distorted_to_inertial_map();\n\n        const auto& expected_grid_to_inertial_map =\n            time_dep_options.grid_to_inertial_map<domain::ObjectLabel::A>(true,\n                                                                          true);\n        const auto& expected_grid_to_distorted_block_maps =\n            time_dep_options.grid_to_distorted_map<domain::ObjectLabel::A>(\n                true);\n        const auto& expected_distorted_to_inertial_map =\n            time_dep_options.distorted_to_inertial_map<domain::ObjectLabel::A>(\n                true, true);\n\n        CHECK(moving_mesh_grid_to_inertial_map ==\n              *expected_grid_to_inertial_map);\n        CHECK(moving_mesh_grid_to_distorted_map ==\n              *expected_grid_to_distorted_block_maps);\n        CHECK(moving_mesh_distorted_to_inertial_map ==\n              *expected_distorted_to_inertial_map);\n\n      } else if (block_groups.at(\"InnerSphereB\").contains(block_name)) {\n        const auto& moving_mesh_grid_to_inertial_map =\n            blocks[i].moving_mesh_grid_to_inertial_map();\n        const auto& moving_mesh_grid_to_distorted_map =\n            blocks[i].moving_mesh_grid_to_distorted_map();\n        const auto& moving_mesh_distorted_to_inertial_map =\n            blocks[i].moving_mesh_distorted_to_inertial_map();\n\n        const auto& expected_grid_to_inertial_map =\n            time_dep_options.grid_to_inertial_map<domain::ObjectLabel::B>(true,\n                                                                          true);\n        const auto& expected_grid_to_distorted_block_maps =\n            time_dep_options.grid_to_distorted_map<domain::ObjectLabel::B>(\n                true);\n        const auto& expected_distorted_to_inertial_map =\n            time_dep_options.distorted_to_inertial_map<domain::ObjectLabel::B>(\n                true, true);\n\n        CHECK(moving_mesh_grid_to_inertial_map ==\n              *expected_grid_to_inertial_map);\n        CHECK(moving_mesh_grid_to_distorted_map ==\n              *expected_grid_to_distorted_block_maps);\n        CHECK(moving_mesh_distorted_to_inertial_map ==\n              *expected_distorted_to_inertial_map);\n\n      } else if (block_groups.at(\"OuterSphere\").contains(block_name)) {\n        const auto& moving_mesh_grid_to_inertial_map =\n            blocks[i].moving_mesh_grid_to_inertial_map();\n        const auto& expected_grid_to_inertial_map =\n            time_dep_options.grid_to_inertial_map<domain::ObjectLabel::None>(\n                false, false);\n        CHECK(moving_mesh_grid_to_inertial_map ==\n              *expected_grid_to_inertial_map);\n      } else {\n        const auto& moving_mesh_grid_to_inertial_map =\n            blocks[i].moving_mesh_grid_to_inertial_map();\n        const auto& expected_grid_to_inertial_map =\n            time_dep_options.grid_to_inertial_map<domain::ObjectLabel::None>(\n                false, true);\n        CHECK(moving_mesh_grid_to_inertial_map ==\n              *expected_grid_to_inertial_map);\n      }\n    }\n  }\n}\n\nvoid test_parse_errors() {\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, 1.0, 0.4, false, false, false,\n          1.0, false, 1_st, 3_st, std::nullopt,\n          create_inner_boundary_condition(), create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"OuterRadius is too small\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{-2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, 1.0, 0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt,\n          create_inner_boundary_condition(), create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The x-coordinate of the input CenterA is expected to be positive\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {5.0, 0.05, 0.0}, 1.0, 0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt,\n          create_inner_boundary_condition(), create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The x-coordinate of the input CenterB is expected to be negative\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, -1.0, 0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt,\n          create_inner_boundary_condition(), create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"RadiusA and RadiusB are expected \"\n                                         \"to be positive\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, 1.0, -0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt,\n          create_inner_boundary_condition(), create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"RadiusA and RadiusB are expected \"\n                                         \"to be positive\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, 0.15, 0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt,\n          create_inner_boundary_condition(), create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"RadiusA should not be smaller than RadiusB\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {-1.0, 0.05, 0.0}, 1.0, 0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt,\n          create_inner_boundary_condition(), create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"We expect |x_A| <= |x_B|\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{4.0, 0.0, 0.0}}, {-4.0, 0.0, 0.0}, 1.0, 1.0, false, false, false,\n          25.0, false, 1_st, 3_st,\n          TimeDepOptions{\n              0.0, std::nullopt,\n              domain::creators::time_dependent_options::RotationMapOptions<\n                  false>{std::array{0.0, 0.0, 0.0}},\n              std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n              std::nullopt},\n          create_inner_boundary_condition(), create_outer_boundary_condition(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"To use the CylindricalBBH domain with time-dependent maps\"));\n  // Boundary condition errors\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, 1.0, 0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt,\n          create_inner_boundary_condition(),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Cannot have periodic boundary \"\n                                         \"conditions with a binary domain\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, 1.0, 0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          create_outer_boundary_condition(), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"Cannot have periodic boundary \"\n                                         \"conditions with a binary domain\"));\n  CHECK_THROWS_WITH(\n      domain::creators::CylindricalBinaryCompactObject(\n          {{2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, 1.0, 0.4, false, false, false,\n          25.0, false, 1_st, 3_st, std::nullopt, nullptr,\n          create_outer_boundary_condition(), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Must specify either both inner and outer boundary \"\n          \"conditions or neither.\"));\n  CHECK_THROWS_WITH(domain::creators::CylindricalBinaryCompactObject(\n                        {{2.0, 0.05, 0.0}}, {-5.0, 0.05, 0.0}, 1.0, 0.4, false,\n                        false, false, 25.0, false, 1_st, 3_st, std::nullopt,\n                        create_inner_boundary_condition(), nullptr,\n                        Options::Context{false, {}, 1, 1}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Must specify either both inner and outer boundary \"\n                        \"conditions or neither.\"));\n}\n\n// This matches the structure in the option string\nstd::unordered_map<std::string, std::array<size_t, 3>> make_initial_structure(\n    const size_t initial_value, const bool include_inner_sphere_A,\n    const bool include_inner_sphere_B, const bool include_outer_sphere) {\n  std::unordered_map<std::string, std::array<size_t, 3>> initial_map;\n  const std::array<size_t, 3> same{initial_value, initial_value, initial_value};\n  const std::array<size_t, 3> one_more{initial_value + 1, initial_value,\n                                       initial_value};\n  initial_map[\"Outer\"] = one_more;\n  initial_map[\"InnerA\"] = same;\n  initial_map[\"InnerB\"] = one_more;\n  if (include_inner_sphere_A) {\n    initial_map[\"InnerSphereA\"] = same;\n  }\n  if (include_inner_sphere_B) {\n    initial_map[\"InnerSphereB\"] = same;\n  }\n  if (include_outer_sphere) {\n    initial_map[\"OuterSphere\"] = one_more;\n  }\n\n  return initial_map;\n}\n\nvoid test_cylindrical_bbh() {\n  MAKE_GENERATOR(gen);\n\n  const std::vector<double> times_to_check{{1.0, 2.3}};\n\n  constexpr size_t refinement = 1;\n  constexpr size_t grid_points = 3;\n\n  const double separation = 9.0;\n  const double y_offset = 0.05;\n\n  // When we add sphere_e support we will make the following\n  // loop go over {true, false}\n  const bool with_sphere_e = false;\n  for (auto [include_outer_sphere, include_inner_sphere_A,\n             include_inner_sphere_B, use_equiangular_map,\n             with_additional_outer_radial_refinement,\n             with_additional_grid_points, with_time_dependence,\n             with_control_systems, with_boundary_conditions] :\n       random_sample<5>(\n           cartesian_product(make_array(true, false), make_array(true, false),\n                             make_array(true, false), make_array(true, false),\n                             make_array(true, false), make_array(true, false),\n                             make_array(true, false), make_array(true, false),\n                             make_array(true, false)),\n           make_not_null(&gen))) {\n    CAPTURE(with_sphere_e);\n    CAPTURE(include_outer_sphere);\n    CAPTURE(use_equiangular_map);\n    CAPTURE(with_boundary_conditions);\n    CAPTURE(with_additional_outer_radial_refinement);\n    CAPTURE(with_additional_grid_points);\n    CAPTURE(with_time_dependence);\n    if (with_time_dependence) {\n      include_inner_sphere_A = true;\n      include_inner_sphere_B = true;\n      include_outer_sphere = true;\n    } else {\n      // With no time dependence, can't have control systems\n      with_control_systems = false;\n    }\n    CAPTURE(include_inner_sphere_A);\n    CAPTURE(include_inner_sphere_B);\n    CAPTURE(with_control_systems);\n\n    const double outer_radius = include_outer_sphere ? 100.0 : 30.0;\n    const double mass_ratio = with_sphere_e ? 4 : 1.2;\n    // Set centers so that the Newtonian COM is at the origin,\n    // except offset slightly in the y direction.\n    const std::array<double, 3> center_objectA = {\n        separation / (1.0 + mass_ratio), y_offset, 0.0};\n    const std::array<double, 3> center_objectB = {\n        -separation * mass_ratio / (1.0 + mass_ratio), y_offset, 0.0};\n\n    // Set radius = 2m for each object.\n    // Formula comes from assuming m_A+m_B=1.\n    const double inner_radius_objectA = 2.0 * mass_ratio / (1.0 + mass_ratio);\n    const double inner_radius_objectB = 2.0 / (1.0 + mass_ratio);\n\n    CylBCO::InitialRefinement::type initial_refinement{};\n    CylBCO::InitialGridPoints::type initial_grid_points{};\n\n    if (with_additional_outer_radial_refinement) {\n      initial_refinement =\n          make_initial_structure(refinement, include_inner_sphere_A,\n                                 include_inner_sphere_B, include_outer_sphere);\n    } else {\n      initial_refinement = refinement;\n    }\n    if (with_additional_grid_points) {\n      initial_grid_points =\n          make_initial_structure(grid_points, include_inner_sphere_A,\n                                 include_inner_sphere_B, include_outer_sphere);\n    } else {\n      initial_grid_points = grid_points;\n    }\n\n    CylBCO cyl_binary_compact_object{};\n    std::optional<TimeDepOptions> time_dep_opts{};\n    if (with_time_dependence) {\n      time_dep_opts = construct_time_dependent_options();\n    }\n    cyl_binary_compact_object = CylBCO{\n        center_objectA,\n        center_objectB,\n        inner_radius_objectA,\n        inner_radius_objectB,\n        include_inner_sphere_A,\n        include_inner_sphere_B,\n        include_outer_sphere,\n        outer_radius,\n        use_equiangular_map,\n        initial_refinement,\n        initial_grid_points,\n        std::move(time_dep_opts),\n        with_boundary_conditions ? create_inner_boundary_condition() : nullptr,\n        with_boundary_conditions ? create_outer_boundary_condition() : nullptr};\n\n    test_construction(cyl_binary_compact_object, with_boundary_conditions,\n                      include_inner_sphere_A, include_inner_sphere_B,\n                      include_outer_sphere, inner_radius_objectA,\n                      inner_radius_objectB, outer_radius, center_objectA,\n                      center_objectB, times_to_check);\n    TestHelpers::domain::creators::test_creation(\n        create_option_string(\n            with_time_dependence, with_additional_outer_radial_refinement,\n            with_additional_grid_points, include_outer_sphere,\n            include_inner_sphere_A, include_inner_sphere_B,\n            with_boundary_conditions, use_equiangular_map, center_objectA,\n            center_objectB, inner_radius_objectA, inner_radius_objectB,\n            outer_radius),\n        cyl_binary_compact_object, with_boundary_conditions);\n  }\n}\n}  // namespace\n\n// [[TimeOut, 80]]\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.CylindricalBinaryCompactObject\",\n                  \"[Domain][Unit]\") {\n  test_cylindrical_bbh();\n  test_parse_errors();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_Disk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/Disk.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_disk_construction(\n    const creators::Disk& disk, const double inner_radius,\n    const double outer_radius,\n    const std::array<size_t, 2>& expected_wedge_extents,\n    const std::vector<std::array<size_t, 2>>& expected_refinement_level,\n    const bool use_equiangular_map,\n    const bool expect_boundary_conditions = false) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      disk, expect_boundary_conditions);\n  CHECK(disk.grid_anchors().empty());\n  CHECK(disk.block_names() == std::vector<std::string>{\"UpperX\", \"UpperY\",\n                                                       \"LowerX\", \"LowerY\",\n                                                       \"CenterSquare\"});\n  const auto block_groups = disk.block_groups();\n  CHECK(block_groups.contains(\"Shell0\"));\n  CHECK(block_groups.contains(\"CenterSquare\"));\n  CHECK(\n      block_groups.at(\"Shell0\") ==\n      std::unordered_set<std::string>{\"UpperX\", \"UpperY\", \"LowerX\", \"LowerY\"});\n\n  const OrientationMap<2> aligned_orientation =\n      OrientationMap<2>::create_aligned();\n  const OrientationMap<2> quarter_turn_ccw(std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}});\n  const OrientationMap<2> half_turn(std::array<Direction<2>, 2>{\n      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}});\n  const OrientationMap<2> quarter_turn_cw(std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}});\n  const std::vector<DirectionMap<2, BlockNeighbors<2>>>\n      expected_block_neighbors{\n          {{Direction<2>::lower_eta(), {3, aligned_orientation}},\n           {Direction<2>::upper_eta(), {1, aligned_orientation}},\n           {Direction<2>::lower_xi(), {4, aligned_orientation}}},\n          {{Direction<2>::lower_eta(), {0, aligned_orientation}},\n           {Direction<2>::upper_eta(), {2, aligned_orientation}},\n           {Direction<2>::lower_xi(), {4, quarter_turn_cw}}},\n          {{Direction<2>::lower_eta(), {1, aligned_orientation}},\n           {Direction<2>::upper_eta(), {3, aligned_orientation}},\n           {Direction<2>::lower_xi(), {4, half_turn}}},\n          {{Direction<2>::lower_eta(), {2, aligned_orientation}},\n           {Direction<2>::upper_eta(), {0, aligned_orientation}},\n           {Direction<2>::lower_xi(), {4, quarter_turn_ccw}}},\n          {{Direction<2>::upper_xi(), {0, aligned_orientation}},\n           {Direction<2>::upper_eta(), {1, quarter_turn_ccw}},\n           {Direction<2>::lower_xi(), {2, half_turn}},\n           {Direction<2>::lower_eta(), {3, quarter_turn_cw}}}};\n  const std::vector<std::unordered_set<Direction<2>>>\n      expected_external_boundaries{{{Direction<2>::upper_xi()}},\n                                   {{Direction<2>::upper_xi()}},\n                                   {{Direction<2>::upper_xi()}},\n                                   {{Direction<2>::upper_xi()}},\n                                   {}};\n\n  const std::vector<std::array<size_t, 2>>& expected_extents{\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      expected_wedge_extents,\n      {{expected_wedge_extents[1], expected_wedge_extents[1]}}};\n\n  CHECK(disk.initial_extents() == expected_extents);\n  CHECK(disk.initial_refinement_levels() == expected_refinement_level);\n  using TargetFrame = Frame::Inertial;\n  using Wedge2DMap = CoordinateMaps::Wedge<2>;\n  using Affine = CoordinateMaps::Affine;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using Equiangular = CoordinateMaps::Equiangular;\n  using Equiangular2D =\n      CoordinateMaps::ProductOf2Maps<Equiangular, Equiangular>;\n\n  auto coord_maps = make_vector_coordinate_map_base<Frame::BlockLogical,\n                                                    TargetFrame>(\n      Wedge2DMap{inner_radius, outer_radius, 0.0, 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}},\n                 use_equiangular_map},\n      Wedge2DMap{inner_radius, outer_radius, 0.0, 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}},\n                 use_equiangular_map},\n      Wedge2DMap{inner_radius, outer_radius, 0.0, 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n                 use_equiangular_map},\n      Wedge2DMap{inner_radius, outer_radius, 0.0, 1.0,\n                 OrientationMap<2>{std::array<Direction<2>, 2>{\n                     {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}},\n                 use_equiangular_map});\n\n  if (use_equiangular_map) {\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n            Equiangular2D{\n                Equiangular(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                            inner_radius / sqrt(2.0)),\n                Equiangular(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                            inner_radius / sqrt(2.0))}));\n  } else {\n    coord_maps.emplace_back(\n        make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n            Affine2D{Affine(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                            inner_radius / sqrt(2.0)),\n                     Affine(-1.0, 1.0, -1.0 * inner_radius / sqrt(2.0),\n                            inner_radius / sqrt(2.0))}));\n  }\n  test_domain_construction(domain, expected_block_neighbors,\n                           expected_external_boundaries, coord_maps);\n}\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<2>>(\n      Direction<2>::lower_xi(), 2);\n}\n\nvoid test_disk_boundaries_equiangular() {\n  INFO(\"Disk boundaries equiangular\");\n  const double inner_radius = 1.0;\n  const double outer_radius = 2.0;\n  const size_t refinement_level = 2;\n  const std::array<size_t, 2> grid_points{{4, 4}};\n\n  const creators::Disk disk{inner_radius, outer_radius, refinement_level,\n                            grid_points, true};\n  test_disk_construction(disk, inner_radius, outer_radius, grid_points,\n                         {5, make_array<2>(refinement_level)}, true);\n\n  const creators::Disk disk_boundary_conditions{\n      inner_radius, outer_radius, refinement_level,\n      grid_points,  true,         create_boundary_condition()};\n  test_disk_construction(disk_boundary_conditions, inner_radius, outer_radius,\n                         grid_points, {5, make_array<2>(refinement_level)},\n                         true, true);\n}\n\nvoid test_disk_factory_equiangular() {\n  INFO(\"Disk factory equiangular\");\n  const auto disk = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<2>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithoutBoundaryConditions<2, domain::creators::Disk>>(\n      \"Disk:\\n\"\n      \"  InnerRadius: 1\\n\"\n      \"  OuterRadius: 3\\n\"\n      \"  InitialRefinement: 2\\n\"\n      \"  InitialGridPoints: [2,3]\\n\"\n      \"  UseEquiangularMap: true\\n\");\n\n  const double inner_radius = 1.0;\n  const double outer_radius = 3.0;\n  const size_t refinement_level = 2;\n  const std::array<size_t, 2> grid_points{{2, 3}};\n  test_disk_construction(dynamic_cast<const creators::Disk&>(*disk),\n                         inner_radius, outer_radius, grid_points,\n                         {5, make_array<2>(refinement_level)}, true);\n\n  const auto disk_boundary_conditions = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<2>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithBoundaryConditions<2, domain::creators::Disk>>(\n      \"Disk:\\n\"\n      \"  InnerRadius: 1\\n\"\n      \"  OuterRadius: 3\\n\"\n      \"  InitialRefinement: 2\\n\"\n      \"  InitialGridPoints: [2,3]\\n\"\n      \"  UseEquiangularMap: true\\n\"\n      \"  BoundaryCondition:\\n\"\n      \"    TestBoundaryCondition:\\n\"\n      \"      Direction: lower-xi\\n\"\n      \"      BlockId: 2\\n\");\n  test_disk_construction(\n      dynamic_cast<const creators::Disk&>(*disk_boundary_conditions),\n      inner_radius, outer_radius, grid_points,\n      {5, make_array<2>(refinement_level)}, true, true);\n}\n\nvoid test_disk_boundaries_equidistant() {\n  INFO(\"Disk boundaries equidistant\");\n  const double inner_radius = 1.0;\n  const double outer_radius = 2.0;\n  const size_t refinement_level = 2;\n  const std::array<size_t, 2> grid_points{{4, 4}};\n\n  const creators::Disk disk{inner_radius, outer_radius, refinement_level,\n                            grid_points, false};\n  test_disk_construction(disk, inner_radius, outer_radius, grid_points,\n                         {5, make_array<2>(refinement_level)}, false);\n\n  const creators::Disk disk_boundary_conditions{\n      inner_radius, outer_radius, refinement_level,\n      grid_points,  false,        create_boundary_condition()};\n  test_disk_construction(disk_boundary_conditions, inner_radius, outer_radius,\n                         grid_points, {5, make_array<2>(refinement_level)},\n                         false, true);\n\n  CHECK_THROWS_WITH(\n      creators::Disk(inner_radius, outer_radius, refinement_level, grid_points,\n                     false,\n                     std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                          TestPeriodicBoundaryCondition<2>>(),\n                     Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions on a disk\"));\n  CHECK_THROWS_WITH(\n      creators::Disk(inner_radius, outer_radius, refinement_level, grid_points,\n                     false,\n                     std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                          TestNoneBoundaryCondition<2>>(),\n                     Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\"));\n}\n\nvoid test_disk_factory_equidistant() {\n  INFO(\"Disk factory equidistant\");\n  const auto disk = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<2>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithoutBoundaryConditions<2, domain::creators::Disk>>(\n      \"Disk:\\n\"\n      \"  InnerRadius: 1\\n\"\n      \"  OuterRadius: 3\\n\"\n      \"  InitialRefinement: 2\\n\"\n      \"  InitialGridPoints: [2,3]\\n\"\n      \"  UseEquiangularMap: false\\n\");\n\n  const double inner_radius = 1.0;\n  const double outer_radius = 3.0;\n  const size_t refinement_level = 2;\n  const std::array<size_t, 2> grid_points{{2, 3}};\n  test_disk_construction(dynamic_cast<const creators::Disk&>(*disk),\n                         inner_radius, outer_radius, grid_points,\n                         {5, make_array<2>(refinement_level)}, false);\n\n  const auto disk_boundary_conditions = TestHelpers::test_option_tag<\n      domain::OptionTags::DomainCreator<2>,\n      TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithBoundaryConditions<2, domain::creators::Disk>>(\n      \"Disk:\\n\"\n      \"  InnerRadius: 1\\n\"\n      \"  OuterRadius: 3\\n\"\n      \"  InitialRefinement: 2\\n\"\n      \"  InitialGridPoints: [2,3]\\n\"\n      \"  UseEquiangularMap: false\\n\"\n      \"  BoundaryCondition:\\n\"\n      \"    TestBoundaryCondition:\\n\"\n      \"      Direction: lower-xi\\n\"\n      \"      BlockId: 2\\n\");\n  test_disk_construction(\n      dynamic_cast<const creators::Disk&>(*disk_boundary_conditions),\n      inner_radius, outer_radius, grid_points,\n      {5, make_array<2>(refinement_level)}, false, true);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.Disk\", \"[Domain][Unit]\") {\n  test_disk_boundaries_equiangular();\n  test_disk_factory_equiangular();\n  test_disk_boundaries_equidistant();\n  test_disk_factory_equidistant();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_ExpandOverBlocks.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <variant>\n#include <vector>\n\n#include \"Domain/Creators/ExpandOverBlocks.tpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace domain {\ntemplate <typename T, size_t Dim>\nvoid test_expand_over_blocks(\n    const std::variant<T, std::array<T, Dim>, std::vector<std::array<T, Dim>>>&\n        input_value,\n    const size_t num_blocks,\n    const std::vector<std::array<T, Dim>>& expected_expanded_value) {\n  CHECK(std::visit(ExpandOverBlocks<std::array<T, Dim>>{num_blocks},\n                   input_value) == expected_expanded_value);\n}\n\nstruct Base {\n  virtual ~Base() = default;\n  virtual std::unique_ptr<Base> get_clone() const = 0;\n};\n\nstruct Derived : Base {\n  explicit Derived(const size_t local_id) : id{local_id} {}\n  std::unique_ptr<Base> get_clone() const override {\n    return std::make_unique<Derived>(*this);\n  }\n  size_t id;\n};\n\nSPECTRE_TEST_CASE(\"Unit.Domain.ExpandOverBlocks\", \"[Domain][Unit]\") {\n  test_expand_over_blocks<size_t, 1>(size_t{2}, 3, {3, {2}});\n  test_expand_over_blocks<size_t, 1>(std::array<size_t, 1>{2}, 3, {3, {2}});\n  test_expand_over_blocks<size_t, 1>(\n      std::vector<std::array<size_t, 1>>{{2}, {3}, {4}}, 3, {{2}, {3}, {4}});\n  test_expand_over_blocks<size_t, 2>(size_t{2}, 3, {3, {2, 2}});\n  test_expand_over_blocks<size_t, 2>(std::array<size_t, 2>{2, 3}, 3,\n                                     {3, {2, 3}});\n  test_expand_over_blocks<size_t, 2>(\n      std::vector<std::array<size_t, 2>>{{2, 3}, {3, 4}, {4, 5}}, 3,\n      {{2, 3}, {3, 4}, {4, 5}});\n  test_expand_over_blocks<size_t, 3>(size_t{2}, 3, {3, {2, 2, 2}});\n  test_expand_over_blocks<size_t, 3>(std::array<size_t, 3>{2, 3, 4}, 3,\n                                     {3, {2, 3, 4}});\n  test_expand_over_blocks<size_t, 3>(\n      std::vector<std::array<size_t, 3>>{{2, 3, 4}, {3, 4, 5}, {4, 5, 6}}, 3,\n      {{2, 3, 4}, {3, 4, 5}, {4, 5, 6}});\n  CHECK_THROWS_WITH(\n      (ExpandOverBlocks<std::array<size_t, 1>>{3}(\n          std::vector<std::array<size_t, 1>>{{2}, {3}})),\n      Catch::Matchers::ContainsSubstring(\n          \"You supplied 2 values, but the domain creator has 3 blocks.\"));\n  {\n    // [expand_over_blocks_example]\n    static constexpr size_t Dim = 3;\n    const size_t num_blocks = 3;\n    // This is an example for a variant that represents the distribution of\n    // initial refinement levels, which might be parsed from options:\n    using InitialRefinementOptionType =\n        std::variant<size_t, std::array<size_t, Dim>,\n                     std::vector<std::array<size_t, Dim>>>;\n    // In this example the user specified a single number:\n    const InitialRefinementOptionType initial_refinement_from_options{\n        size_t{2}};\n    try {\n      // Invoke `ExpandOverBlocks`:\n      const auto initial_refinement =\n          std::visit(ExpandOverBlocks<std::array<size_t, Dim>>{num_blocks},\n                     initial_refinement_from_options);\n      // Since a single number was specified, we expect the vector over blocks\n      // is homogeneously and isotropically filled with that number:\n      std::vector<std::array<size_t, Dim>> expected_initial_refinement{\n          num_blocks, {2, 2, 2}};\n      CHECK(initial_refinement == expected_initial_refinement);\n    } catch (const std::length_error& error) {\n      // This would be a `PARSE_ERROR` in an option-parsing context\n      ERROR(\"Invalid 'InitialRefinement': \" << error.what());\n    }\n    // [expand_over_blocks_example]\n  }\n  {\n    // [expand_over_blocks_named_example]\n    static constexpr size_t Dim = 2;\n    // In this example we name the blocks, representing a cubed-sphere domain:\n    const std::vector<std::string> block_names{\"InnerCube\", \"East\", \"North\",\n                                               \"West\", \"South\"};\n    // The blocks can also be grouped:\n    const std::unordered_map<std::string, std::unordered_set<std::string>>\n        block_groups{{\"Wedges\", {\"East\", \"North\", \"West\", \"South\"}}};\n    // Now we can expand values over blocks by giving their names. This can also\n    // be used with a std::variant like in the other example.\n    ExpandOverBlocks<std::array<size_t, Dim>> expand{block_names, block_groups};\n    CHECK(expand({{\"West\", {{3, 4}}},\n                  {\"InnerCube\", {{2, 3}}},\n                  {\"South\", {{3, 4}}},\n                  {\"North\", {{5, 6}}},\n                  {\"East\", {{1, 2}}}}) ==\n          std::vector<std::array<size_t, Dim>>{\n              {{2, 3}}, {{1, 2}}, {{5, 6}}, {{3, 4}}, {{3, 4}}});\n    // Instead of naming all blocks individually we can also name groups:\n    CHECK(expand({{\"InnerCube\", {{2, 3}}}, {\"Wedges\", {{3, 4}}}}) ==\n          std::vector<std::array<size_t, Dim>>{\n              {{2, 3}}, {{3, 4}}, {{3, 4}}, {{3, 4}}, {{3, 4}}});\n    // [expand_over_blocks_named_example]\n    CHECK_THROWS_WITH(expand({{\"InnerCube\", {{2, 3}}}, {\"East\", {{3, 4}}}}),\n                      Catch::Matchers::ContainsSubstring(\n                          \"You supplied 2 values, but the domain creator has 5 \"\n                          \"blocks: InnerCube, East, North, West, South\"));\n    CHECK_THROWS_WITH(expand({{{\"Wedges\", {{3, 4}}}}}),\n                      Catch::Matchers::ContainsSubstring(\n                          \"You supplied 4 values, but the domain creator has 5 \"\n                          \"blocks: InnerCube, East, North, West, South\"));\n    CHECK_THROWS_WITH(\n        expand({{\"Wedges\", {{3, 4}}}, {\"East\", {{3, 4}}}}),\n        Catch::Matchers::ContainsSubstring(\n            \"Duplicate block name 'East' (expanded from 'Wedges').\"));\n    CHECK_THROWS_WITH(expand({{\"noblock\", {{3, 4}}}, {\"Wedges\", {{3, 4}}}}),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Value for block 'InnerCube' is missing. Did \"\n                          \"you misspell its name?\"));\n  }\n  {\n    INFO(\"Expand non-array\");\n    ExpandOverBlocks<std::string> expand{3};\n    CHECK(expand(std::string{\"A\"}) == std::vector<std::string>{3, \"A\"});\n  }\n  {\n    INFO(\"Expand unique_ptr\");\n    ExpandOverBlocks<std::unique_ptr<Base>> expand{{\"A\", \"B\"}};\n    {\n      const auto expanded = expand(std::make_unique<Derived>(size_t{1}));\n      CHECK(expanded.size() == 2);\n      for (const auto& v : expanded) {\n        const auto v_derived = dynamic_cast<const Derived*>(v.get());\n        CHECK(v_derived != nullptr);\n        CHECK(v_derived->id == 1);\n      }\n    }\n    {\n      std::vector<std::unique_ptr<Base>> original{};\n      original.emplace_back(std::make_unique<Derived>(size_t{1}));\n      original.emplace_back(std::make_unique<Derived>(size_t{2}));\n      const auto expanded = expand(original);\n      CHECK(expanded.size() == 2);\n      auto v_derived = dynamic_cast<const Derived*>(expanded[0].get());\n      CHECK(v_derived != nullptr);\n      CHECK(v_derived->id == 1);\n      v_derived = dynamic_cast<const Derived*>(expanded[1].get());\n      CHECK(v_derived != nullptr);\n      CHECK(v_derived->id == 2);\n    }\n    {\n      std::unordered_map<std::string, std::unique_ptr<Base>> original{};\n      original.emplace(\"A\", std::make_unique<Derived>(size_t{1}));\n      original.emplace(\"B\", std::make_unique<Derived>(size_t{2}));\n      const auto expanded = expand(original);\n      CHECK(expanded.size() == 2);\n      auto v_derived = dynamic_cast<const Derived*>(expanded[0].get());\n      CHECK(v_derived != nullptr);\n      CHECK(v_derived->id == 1);\n      v_derived = dynamic_cast<const Derived*>(expanded[1].get());\n      CHECK(v_derived != nullptr);\n      CHECK(v_derived->id == 2);\n    }\n  }\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_FrustalCloak.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/FrustalCloak.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::upper_zeta(), 50);\n}\n\nstd::string boundary_conditions_string() {\n  return {\n      \"  BoundaryCondition:\\n\"\n      \"    TestBoundaryCondition:\\n\"\n      \"      Direction: upper-zeta\\n\"\n      \"      BlockId: 50\\n\"};\n}\n\nvoid test_factory() {\n  for (const bool with_boundary_conditions : {true, false}) {\n    const std::string opt_string{\n        \"FrustalCloak:\\n\"\n        \"  InitialRefinement: 3\\n\"\n        \"  InitialGridPoints: [2,3]\\n\"\n        \"  UseEquiangularMap: true\\n\"\n        \"  ProjectionFactor: 0.3\\n\"\n        \"  LengthInnerCube: 15.5\\n\"\n        \"  LengthOuterCube: 42.4\\n\"\n        \"  OriginPreimage: [0.2,0.3,-0.1]\\n\" +\n        std::string{with_boundary_conditions ? boundary_conditions_string()\n                                             : \"\"}};\n    const auto frustal_cloak = [&opt_string, &with_boundary_conditions]() {\n      if (with_boundary_conditions) {\n        return TestHelpers::test_option_tag<\n            domain::OptionTags::DomainCreator<3>,\n            TestHelpers::domain::BoundaryConditions::\n                MetavariablesWithBoundaryConditions<\n                    3, domain::creators::FrustalCloak>>(opt_string);\n      } else {\n        return TestHelpers::test_option_tag<\n            domain::OptionTags::DomainCreator<3>,\n            TestHelpers::domain::BoundaryConditions::\n                MetavariablesWithoutBoundaryConditions<\n                    3, domain::creators::FrustalCloak>>(opt_string);\n      }\n    }();\n    TestHelpers::domain::creators::test_domain_creator(\n        *frustal_cloak, with_boundary_conditions);\n  }\n}\n\nvoid test_connectivity() {\n  const size_t refinement = 1;\n  const std::array<size_t, 2> grid_points = {{6, 5}};\n  const double projective_scale_factor = 0.3;\n  const double length_inner_cube = 15.5;\n  const double length_outer_cube = 42.4;\n  const std::array<double, 3> origin_preimage = {{1.3, 0.2, -3.1}};\n\n  for (const auto& [with_boundary_conditions, use_equiangular_map] :\n       cartesian_product(make_array(true, false), make_array(true, false))) {\n    CAPTURE(with_boundary_conditions);\n    CAPTURE(use_equiangular_map);\n    const domain::creators::FrustalCloak frustal_cloak{\n        refinement,\n        grid_points,\n        use_equiangular_map,\n        projective_scale_factor,\n        length_inner_cube,\n        length_outer_cube,\n        origin_preimage,\n        with_boundary_conditions ? create_boundary_condition() : nullptr};\n    TestHelpers::domain::creators::test_domain_creator(\n        frustal_cloak, with_boundary_conditions);\n    CHECK(frustal_cloak.grid_anchors().empty());\n  }\n\n  CHECK_THROWS_WITH(\n      domain::creators::FrustalCloak(\n          refinement, grid_points, true, projective_scale_factor,\n          length_inner_cube, length_outer_cube, origin_preimage,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions with a frustal cloak\"));\n  CHECK_THROWS_WITH(\n      domain::creators::FrustalCloak(\n          refinement, grid_points, true, projective_scale_factor,\n          length_inner_cube, length_outer_cube, origin_preimage,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.FrustalCloak\",\n                  \"[Domain][Unit]\") {\n  TestHelpers::domain::BoundaryConditions::register_derived_with_charm();\n  test_factory();\n  test_connectivity();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_Interval.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test_interval_construction(const creators::Interval& domain_creator,\n                                const bool expect_boundary_conditions,\n                                const bool is_periodic,\n                                const std::array<double, 1>& lower_bound,\n                                const std::array<double, 1>& upper_bound,\n                                const std::vector<double>& times = {0.},\n                                const std::array<double, 1>& velocity = {\n                                    {0.}}) {\n  // Generic tests\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      domain_creator, expect_boundary_conditions, is_periodic, times);\n  TestHelpers::domain::BoundaryConditions::test_boundary_conditions(\n      domain_creator.external_boundary_conditions());\n  CHECK(domain_creator.grid_anchors().empty());\n\n  // Interval-specific tests\n  CHECK(domain_creator.block_names() == std::vector<std::string>{\"Interval\"});\n  const auto block_groups = domain_creator.block_groups();\n  CHECK(block_groups.contains(\"Interval\"));\n  CHECK(block_groups.at(\"Interval\") ==\n        std::unordered_set<std::string>{\"Interval\"});\n  const auto& blocks = domain.blocks();\n  CHECK(blocks.size() == 1);\n  CHECK(domain.excision_spheres().empty());\n\n  const auto& block = blocks[0];\n  CHECK(block.name() == \"Interval\");\n  const auto& external_boundaries = block.external_boundaries();\n  CHECK(external_boundaries.size() == (is_periodic ? 0 : 2));\n\n  // Check that the block boundaries have the expected coordinates\n  tnsr::I<DataVector, 1> expected_block_boundaries{2_st};\n  const auto functions_of_time = domain_creator.functions_of_time();\n  for (const double time : times) {\n    CAPTURE(time);\n    get<0>(expected_block_boundaries)[0] = lower_bound[0] + time * velocity[0];\n    get<0>(expected_block_boundaries)[1] = upper_bound[0] + time * velocity[0];\n    const auto block_logical_coords = block_logical_coordinates(\n        domain, expected_block_boundaries, time, functions_of_time);\n    CHECK(block_logical_coords[0]->id.get_index() == 0);\n    CHECK(get<0>(block_logical_coords[0]->data) == -1.);\n    CHECK(block_logical_coords[1]->id.get_index() == 0);\n    CHECK(get<0>(block_logical_coords[1]->data) == 1.);\n  }\n}\n\nstd::string option_string(const CoordinateMaps::Distribution distribution,\n                          const std::optional<double>& singularity,\n                          const bool is_periodic, const bool time_dependent,\n                          const bool with_boundary_conditions) {\n  const std::string time_dep_options = time_dependent\n                                           ? \"  TimeDependence:\\n\"\n                                             \"    UniformTranslation:\\n\"\n                                             \"      InitialTime: 0.\\n\"\n                                             \"      Velocity: [2.3]\\n\"\n                                           : \"  TimeDependence: None\\n\";\n  const std::string bc_options =\n      with_boundary_conditions\n          ? (\"  BoundaryConditions:\\n\"\n             \"    - Lower:\\n\" +\n             std::string{is_periodic ? \"        Periodic\\n\"\n                                     : \"        TestBoundaryCondition:\\n\"\n                                       \"          Direction: lower-xi\\n\"\n                                       \"          BlockId: 0\\n\"} +\n             \"      Upper:\\n\" +\n             std::string{is_periodic ? \"        Periodic\\n\"\n                                     : \"        TestBoundaryCondition:\\n\"\n                                       \"          Direction: upper-xi\\n\"\n                                       \"          BlockId: 0\\n\"})\n          : (\"  IsPeriodicIn: [\" + std::string{is_periodic ? \"True\" : \"False\"} +\n             \"]\\n\");\n  return \"Interval:\\n\"\n         \"  LowerBound: [-1.2]\\n\"\n         \"  UpperBound: [0.8]\\n\" +\n         (singularity.has_value()\n              ? (\"  Distribution:\\n\"\n                 \"    - \" +\n                 get_output(distribution) +\n                 \":\\n\"\n                 \"        SingularityPosition: \" +\n                 std::to_string(*singularity))\n              : (\"  Distribution: [\" + get_output(distribution) + \"]\")) +\n         \"\\n\"\n         \"  InitialGridPoints: [4]\\n\"\n         \"  InitialRefinement: [3]\\n\" +\n         time_dep_options + bc_options;\n}\n\nvoid test_interval() {\n  INFO(\"Interval\");\n  const std::vector<std::array<size_t, 1>> grid_points{{{4}}};\n  const std::vector<std::array<size_t, 1>> refinement_level{{{3}}};\n  const std::array<double, 1> lower_bound{{-1.2}};\n  const std::array<double, 1> upper_bound{{0.8}};\n\n  const auto lower_boundary_condition = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<1>>(\n      Direction<1>::lower_xi(), 0);\n  const auto upper_boundary_condition = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<1>>(\n      Direction<1>::upper_xi(), 0);\n  const auto periodic_boundary_condition =\n      std::make_unique<TestHelpers::domain::BoundaryConditions::\n                           TestPeriodicBoundaryCondition<1>>();\n\n  const auto distributions =\n      make_array(CoordinateMaps::Distribution::Linear,\n                 CoordinateMaps::Distribution::Logarithmic);\n  const std::array<std::optional<double>, 2> singularities{{std::nullopt, -2.}};\n\n  const std::array<double, 1> velocity{{2.5}};\n  const auto time_dependence = std::make_unique<\n      domain::creators::time_dependence::UniformTranslation<1>>(0., velocity);\n  const std::vector<double> times{0., 1.5};\n\n  for (const auto& [is_periodic, distribution_index, time_dependent] :\n       cartesian_product(make_array(true, false), make_array(0, 1),\n                         make_array(true, false))) {\n    CAPTURE(is_periodic);\n    const auto& distribution = gsl::at(distributions, distribution_index);\n    const auto& singularity = gsl::at(singularities, distribution_index);\n    CAPTURE(distribution);\n    CAPTURE(singularity);\n    CAPTURE(time_dependent);\n\n    const creators::Interval interval{\n        lower_bound,\n        upper_bound,\n        refinement_level[0],\n        grid_points[0],\n        {{is_periodic}},\n        {{{distribution, singularity}}},\n        time_dependent ? time_dependence->get_clone() : nullptr};\n    test_interval_construction(\n        interval, false, is_periodic, lower_bound, upper_bound, times,\n        time_dependent ? velocity : std::array<double, 1>{{0.}});\n    TestHelpers::domain::creators::test_creation(\n        option_string(distribution, singularity, is_periodic, time_dependent,\n                      false),\n        interval, false);\n\n    const creators::Interval interval_with_bc{\n        lower_bound,\n        upper_bound,\n        refinement_level[0],\n        grid_points[0],\n        {{{{is_periodic ? periodic_boundary_condition->get_clone()\n                        : lower_boundary_condition->get_clone(),\n            is_periodic ? periodic_boundary_condition->get_clone()\n                        : upper_boundary_condition->get_clone()}}}},\n        {{{distribution, singularity}}},\n        time_dependent ? time_dependence->get_clone() : nullptr};\n    test_interval_construction(\n        interval_with_bc, true, is_periodic, lower_bound, upper_bound, times,\n        time_dependent ? velocity : std::array<double, 1>{{0.}});\n    TestHelpers::domain::creators::test_creation(\n        option_string(distribution, singularity, is_periodic, time_dependent,\n                      true),\n        interval_with_bc, true);\n  }\n}\n\nvoid test_parse_errors() {\n  INFO(\"Test parse errors\");\n\n  const std::array<double, 1> lower_bound{{-1.2}};\n  const std::array<double, 1> upper_bound{{0.8}};\n  const std::vector<std::array<size_t, 1>> refinement_level{{{3}}};\n  const std::vector<std::array<size_t, 1>> grid_points{{{4}}};\n\n  const auto lower_boundary_condition = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<1>>(\n      Direction<1>::lower_xi(), 0);\n  const auto upper_boundary_condition = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<1>>(\n      Direction<1>::upper_xi(), 0);\n  const auto periodic_boundary_condition =\n      std::make_unique<TestHelpers::domain::BoundaryConditions::\n                           TestPeriodicBoundaryCondition<1>>();\n\n  CHECK_THROWS_WITH(\n      creators::Interval(\n          lower_bound, upper_bound, refinement_level[0], grid_points[0],\n          {{{{lower_boundary_condition->get_clone(),\n              periodic_boundary_condition->get_clone()}}}},\n          {{{CoordinateMaps::Distribution::Linear, std::nullopt}}}, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Periodic boundary conditions must be applied for both upper and \"\n          \"lower direction\"));\n  CHECK_THROWS_WITH(\n      creators::Interval(\n          lower_bound, upper_bound, refinement_level[0], grid_points[0],\n          {{{{periodic_boundary_condition->get_clone(),\n              lower_boundary_condition->get_clone()}}}},\n          {{{CoordinateMaps::Distribution::Linear, std::nullopt}}}, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Periodic boundary conditions must be applied for both upper and \"\n          \"lower direction\"));\n  CHECK_THROWS_WITH(\n      creators::Interval(\n          lower_bound, upper_bound, refinement_level[0], grid_points[0],\n          {{{{lower_boundary_condition->get_clone(),\n              std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                   TestNoneBoundaryCondition<3>>()}}}},\n          {{{CoordinateMaps::Distribution::Linear, std::nullopt}}}, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\"));\n  CHECK_THROWS_WITH(\n      creators::Interval(\n          lower_bound, upper_bound, refinement_level[0], grid_points[0],\n          {{{{std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                   TestNoneBoundaryCondition<3>>(),\n              lower_boundary_condition->get_clone()}}}},\n          {{{CoordinateMaps::Distribution::Linear, std::nullopt}}}, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.Interval\", \"[Domain][Unit]\") {\n  test_interval();\n  test_parse_errors();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_NonconformingSphericalShells.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Creators/NonconformingSphericalShells.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition(const bool outer) {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      outer ? Direction<3>::upper_xi() : Direction<3>::lower_zeta(), 50);\n}\n\nstd::string option_string(\n    const double inner_radius, const double interface_radius,\n    const double outer_radius, const size_t radial_refinement,\n    const size_t angular_refinement, const size_t radial_extents,\n    const size_t spherical_harmonic_l, const size_t angular_extents,\n    const bool with_boundary_conditions) {\n  const std::string inner_bc_option = with_boundary_conditions\n                                          ? \"  InnerBoundaryCondition:\\n\"\n                                            \"    TestBoundaryCondition:\\n\"\n                                            \"      Direction: lower-xi\\n\"\n                                            \"      BlockId: 50\\n\"\n                                          : \"\";\n  const std::string outer_bc_option = with_boundary_conditions\n                                          ? \"  OuterBoundaryCondition:\\n\"\n                                            \"    TestBoundaryCondition:\\n\"\n                                            \"      Direction: upper-xi\\n\"\n                                            \"      BlockId: 50\\n\"\n                                          : \"\";\n  return \"NonconformingSphericalShells:\\n\"\n         \"  InnerRadius: \" +\n         std::to_string(inner_radius) +\n         \"\\n\"\n         \"  InterfaceRadius: \" +\n         std::to_string(interface_radius) +\n         \"\\n\"\n         \"  OuterRadius: \" +\n         std::to_string(outer_radius) + \"\\n\" +\n         \"  InitialRadialRefinement: \" + std::to_string(radial_refinement) +\n         \"\\n\"\n         \"  InitialAngularRefinementOfWedges: \" +\n         std::to_string(angular_refinement) +\n         \"\\n\"\n         \"  InitialNumberOfRadialGridPoints: \" +\n         std::to_string(radial_extents) +\n         \"\\n\"\n         \"  InitialSphericalHarmonicL: \" +\n         std::to_string(spherical_harmonic_l) +\n         \"\\n\"\n         \"  InitialNumberOfAngularGridPointsOfWedges: \" +\n         std::to_string(angular_extents) + \"\\n\" + inner_bc_option +\n         outer_bc_option;\n}\n\nvoid test_parse_errors() {\n  INFO(\"NonconformingSphericalShells check throws\");\n  const double inner_radius = 1.9;\n  const double interface_radius = 2.4;\n  const double outer_radius = 2.9;\n  const size_t radial_refinement = 0;\n  const size_t angular_refinement = 1;\n  const size_t radial_extents = 12;\n  const size_t l = 9;\n  const size_t angular_extents = 11;\n\n  CHECK_THROWS_WITH(\n      domain::creators::NonconformingSphericalShells(\n          inner_radius, 0.5 * inner_radius, outer_radius, radial_refinement,\n          angular_refinement, radial_extents, l, angular_extents, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Inner radius must be smaller than interface radius\"));\n\n  CHECK_THROWS_WITH(\n      domain::creators::NonconformingSphericalShells(\n          inner_radius, 1.5 * outer_radius, outer_radius, radial_refinement,\n          angular_refinement, radial_extents, l, angular_extents, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Interface radius must be smaller than outer radius\"));\n\n  CHECK_THROWS_WITH(\n      domain::creators::NonconformingSphericalShells(\n          inner_radius, interface_radius, outer_radius, radial_refinement,\n          angular_refinement, radial_extents, l, angular_extents,\n          create_boundary_condition(false), nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Must specify either both inner and outer boundary conditions \"\n          \"or neither.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::NonconformingSphericalShells(\n          inner_radius, interface_radius, outer_radius, radial_refinement,\n          angular_refinement, radial_extents, l, angular_extents,\n          create_boundary_condition(false),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions with \"\n          \"NonconformingSphericalShells\"));\n  CHECK_THROWS_WITH(\n      domain::creators::NonconformingSphericalShells(\n          inner_radius, interface_radius, outer_radius, radial_refinement,\n          angular_refinement, radial_extents, l, angular_extents,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          create_boundary_condition(true), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions with \"\n          \"NonconformingSphericalShells\"));\n  CHECK_THROWS_WITH(\n      domain::creators::NonconformingSphericalShells(\n          inner_radius, interface_radius, outer_radius, radial_refinement,\n          angular_refinement, radial_extents, l, angular_extents,\n          create_boundary_condition(false),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::NonconformingSphericalShells(\n          inner_radius, interface_radius, outer_radius, radial_refinement,\n          angular_refinement, radial_extents, l, angular_extents,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          create_boundary_condition(true), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n}\n\ntemplate <typename Generator>\nvoid test_nonconforming_spherical_shells_construction(\n    const gsl::not_null<Generator*> gen,\n    const domain::creators::NonconformingSphericalShells& creator,\n    const double inner_radius, const double interface_radius,\n    const double outer_radius, const bool expect_boundary_conditions = true) {\n  // check consistency of domain\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      creator, expect_boundary_conditions);\n  const auto& grid_anchors = creator.grid_anchors();\n  CHECK(grid_anchors.size() == 1);\n  CHECK(grid_anchors.count(\"Center\") == 1);\n  CHECK(grid_anchors.at(\"Center\") ==\n        tnsr::I<double, 3, Frame::Grid>{std::array{0.0, 0.0, 0.0}});\n\n  const auto& blocks = domain.blocks();\n  const auto block_names = creator.block_names();\n  const size_t num_blocks = blocks.size();\n  CAPTURE(num_blocks);\n  const auto all_boundary_conditions = creator.external_boundary_conditions();\n  const auto functions_of_time = creator.functions_of_time();\n\n  // Check total number of external boundaries\n  const size_t num_external_boundaries =\n      alg::accumulate(blocks, 0_st, [](const size_t count, const auto& block) {\n        return count + block.external_boundaries().size();\n      });\n  CHECK(num_external_boundaries == 7);\n\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  for (size_t block_id = 0; block_id < num_blocks - 1; ++block_id) {\n    CAPTURE(block_id);\n    const auto& block = blocks[block_id];\n    const ElementMap<3, Frame::Grid> grid_element_map{ElementId<3>{block_id},\n                                                      block};\n    const ElementMap<3, Frame::Inertial> inertial_element_map{\n        ElementId<3>{block_id}, block};\n    {\n      INFO(\"Radius of random point on lower face of cubed shells\");\n      const tnsr::I<double, 3, Frame::ElementLogical> x_logical{\n          {{xi_distribution(*gen), xi_distribution(*gen), -1.0}}};\n      auto x_inertial = inertial_element_map(x_logical);\n      CHECK(get(magnitude(x_inertial)) == approx(inner_radius));\n    }\n    {\n      INFO(\"Radius of random point on upper face of cubed shells\");\n      const tnsr::I<double, 3, Frame::ElementLogical> x_logical{\n          {{xi_distribution(*gen), xi_distribution(*gen), 1.0}}};\n      auto x_inertial = inertial_element_map(x_logical);\n      CHECK(get(magnitude(x_inertial)) == approx(interface_radius));\n    }\n    {\n      INFO(\"External boundaries of cubed shells\");\n      const auto& external_boundaries = block.external_boundaries();\n      CHECK(external_boundaries.size() == 1);\n      CHECK(alg::found(external_boundaries, Direction<3>::lower_zeta()));\n    }\n    if (expect_boundary_conditions) {\n      INFO(\"Boundary conditions of cubed shells\");\n      const auto& boundary_conditions = all_boundary_conditions[block_id];\n      for (const auto& direction : block.external_boundaries()) {\n        CAPTURE(direction);\n        const auto& boundary_condition =\n            dynamic_cast<const TestHelpers::domain::BoundaryConditions::\n                             TestBoundaryCondition<3>&>(\n                *boundary_conditions.at(direction));\n        CHECK(boundary_condition.direction() == direction);\n      }\n    }\n  }\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::uniform_real_distribution<> theta_distribution(0.0, M_PI);\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  const auto& block = blocks[6];\n  const ElementMap<3, Frame::Grid> grid_element_map{ElementId<3>{6}, block};\n  const ElementMap<3, Frame::Inertial> inertial_element_map{ElementId<3>{6},\n                                                            block};\n  {\n    INFO(\"Radius of random point on lower face of spherical shell\");\n    const tnsr::I<double, 3, Frame::ElementLogical> x_logical{\n        {{-1.0, theta_distribution(*gen), phi_distribution(*gen)}}};\n    auto x_inertial = inertial_element_map(x_logical);\n    CHECK(get(magnitude(x_inertial)) == approx(interface_radius));\n  }\n  {\n    INFO(\"Radius of random point on upper face of spherical shell\");\n    const tnsr::I<double, 3, Frame::ElementLogical> x_logical{\n        {{1.0, theta_distribution(*gen), phi_distribution(*gen)}}};\n    auto x_inertial = inertial_element_map(x_logical);\n    CHECK(get(magnitude(x_inertial)) == approx(outer_radius));\n  }\n  {\n    INFO(\"External boundaries of spherical shell\");\n    const auto& external_boundaries = block.external_boundaries();\n    CHECK(external_boundaries.size() == 1);\n    CHECK(alg::found(external_boundaries, Direction<3>::upper_xi()));\n  }\n  if (expect_boundary_conditions) {\n    INFO(\"Boundary conditions of spherical shell\");\n    const auto& boundary_conditions = all_boundary_conditions[6];\n    for (const auto& direction : block.external_boundaries()) {\n      CAPTURE(direction);\n      const auto& boundary_condition =\n          dynamic_cast<const TestHelpers::domain::BoundaryConditions::\n                           TestBoundaryCondition<3>&>(\n              *boundary_conditions.at(direction));\n      CHECK(boundary_condition.direction() == direction);\n    }\n  }\n}\n\ntemplate <typename Generator>\nvoid test(const gsl::not_null<Generator*> gen) {\n  const double inner_radius = 1.0;\n  const double interface_radius = 1.5;\n  const double outer_radius = 2.0;\n  const size_t radial_refinement = 3;\n  const size_t angular_refinement = 2;\n  const size_t radial_extents = 5;\n  const size_t l = 6;\n  const size_t angular_extents = 7;\n  for (const bool with_boundary_conditions : {true, false}) {\n    CAPTURE(with_boundary_conditions);\n    const domain::creators::NonconformingSphericalShells creator{\n        inner_radius,\n        interface_radius,\n        outer_radius,\n        radial_refinement,\n        angular_refinement,\n        radial_extents,\n        l,\n        angular_extents,\n        with_boundary_conditions ? create_boundary_condition(false) : nullptr,\n        with_boundary_conditions ? create_boundary_condition(true) : nullptr};\n    test_nonconforming_spherical_shells_construction(\n        gen, creator, inner_radius, interface_radius, outer_radius,\n        with_boundary_conditions);\n    TestHelpers::domain::creators::test_creation(\n        option_string(inner_radius, interface_radius, outer_radius,\n                      radial_refinement, angular_refinement, radial_extents, l,\n                      angular_extents, with_boundary_conditions),\n        creator, with_boundary_conditions);\n  }\n}\n}  // namespace\n\n// [[TimeOut, 15]]\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.NonconformingSphericalShells\",\n                  \"[Domain][Unit]\") {\n  MAKE_GENERATOR(gen);\n  domain::creators::time_dependence::register_derived_with_charm();\n  test_parse_errors();\n  test(make_not_null(&gen));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_Rectangle.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain {\nnamespace {\nusing Affine = CoordinateMaps::Affine;\nusing Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Translation2D = CoordinateMaps::TimeDependent::Translation<2>;\n\ntemplate <typename... FuncsOfTime>\nvoid test_rectangle_construction(\n    const creators::Rectangle& rectangle,\n    const std::array<double, 2>& lower_bound,\n    const std::array<double, 2>& upper_bound,\n    const std::vector<std::array<size_t, 2>>& expected_extents,\n    const std::vector<std::array<size_t, 2>>& expected_refinement_level,\n    const std::vector<DirectionMap<2, BlockNeighbors<2>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<2>>>&\n        expected_external_boundaries,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time = {},\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::Grid, Frame::Inertial, 2>>>& expected_grid_to_inertial_maps = {},\n    const bool expect_boundary_conditions = false,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      rectangle, expect_boundary_conditions);\n  CHECK(rectangle.grid_anchors().empty());\n\n  CHECK(rectangle.block_names() == std::vector<std::string>{\"Rectangle\"});\n  const auto block_groups = rectangle.block_groups();\n  CHECK(block_groups.contains(\"Rectangle\"));\n  CHECK(block_groups.at(\"Rectangle\") ==\n        std::unordered_set<std::string>{\"Rectangle\"});\n\n  CHECK(rectangle.initial_extents() == expected_extents);\n  CHECK(rectangle.initial_refinement_levels() == expected_refinement_level);\n\n  test_domain_construction(\n      domain, expected_block_neighbors, expected_external_boundaries,\n      make_vector(make_coordinate_map_base<\n                  Frame::BlockLogical,\n                  tmpl::conditional_t<sizeof...(FuncsOfTime) == 0,\n                                      Frame::Inertial, Frame::Grid>>(\n          Affine2D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]}})),\n      10.0, rectangle.functions_of_time(), expected_grid_to_inertial_maps);\n  TestHelpers::domain::creators::test_functions_of_time(\n      rectangle, expected_functions_of_time, initial_expiration_times);\n}\n\nvoid test_rectangle() {\n  INFO(\"Rectangle\");\n  const std::vector<std::array<size_t, 2>> grid_points{{{4, 6}}};\n  const std::vector<std::array<size_t, 2>> refinement_level{{{3, 2}}};\n  const std::array<double, 2> lower_bound{{-1.2, 3.0}};\n  const std::array<double, 2> upper_bound{{0.8, 5.0}};\n  const OrientationMap<2> aligned_orientation =\n      OrientationMap<2>::create_aligned();\n  const TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<2>\n      test_bc{Direction<2>::lower_xi(), 0};\n  const TestHelpers::domain::BoundaryConditions::TestPeriodicBoundaryCondition<\n      2>\n      periodic_bc{};\n  const TestHelpers::domain::BoundaryConditions::TestNoneBoundaryCondition<2>\n      none_bc{};\n\n  {\n    INFO(\"Rectangle, non-periodic no boundary conditions\");\n    test_rectangle_construction(\n        {lower_bound, upper_bound, refinement_level[0], grid_points[0],\n         std::array<bool, 2>{{false, false}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<2, BlockNeighbors<2>>>{{}},\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_xi()},\n             {Direction<2>::upper_xi()},\n             {Direction<2>::lower_eta()},\n             {Direction<2>::upper_eta()}}});\n  }\n\n  {\n    INFO(\"Rectangle, non-periodic with boundary conditions\");\n    std::vector<DirectionMap<\n        2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n        expected_boundary_conditions{1};\n    for (const auto& direction : Direction<2>::all_directions()) {\n      expected_boundary_conditions[0][direction] = test_bc.get_clone();\n    }\n    test_rectangle_construction(\n        domain::creators::Rectangle{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{test_bc.get_clone(), test_bc.get_clone()}},\n              {{test_bc.get_clone(), test_bc.get_clone()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<2, BlockNeighbors<2>>>{{}},\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_xi()},\n             {Direction<2>::upper_xi()},\n             {Direction<2>::lower_eta()},\n             {Direction<2>::upper_eta()}}},\n        {}, {}, true);\n  }\n\n  {\n    INFO(\"Rectangle, periodic in x no boundary conditions\");\n    test_rectangle_construction(\n        domain::creators::Rectangle{lower_bound, upper_bound,\n                                    refinement_level[0], grid_points[0],\n                                    std::array<bool, 2>{{true, false}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<2, BlockNeighbors<2>>>{\n            {{Direction<2>::lower_xi(), {0, aligned_orientation}},\n             {Direction<2>::upper_xi(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_eta()}, {Direction<2>::upper_eta()}}});\n  }\n\n  {\n    INFO(\"Rectangle, periodic in y no boundary conditions\");\n    test_rectangle_construction(\n        domain::creators::Rectangle{lower_bound, upper_bound,\n                                    refinement_level[0], grid_points[0],\n                                    std::array<bool, 2>{{false, true}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<2, BlockNeighbors<2>>>{\n            {{Direction<2>::lower_eta(), {0, aligned_orientation}},\n             {Direction<2>::upper_eta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_xi()}, {Direction<2>::upper_xi()}}});\n  }\n\n  {\n    INFO(\"Rectangle, periodic in xy no boundary conditions\");\n    test_rectangle_construction(\n        domain::creators::Rectangle{lower_bound, upper_bound,\n                                    refinement_level[0], grid_points[0],\n                                    std::array<bool, 2>{{true, true}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<2, BlockNeighbors<2>>>{\n            {{Direction<2>::lower_xi(), {0, aligned_orientation}},\n             {Direction<2>::upper_xi(), {0, aligned_orientation}},\n             {Direction<2>::lower_eta(), {0, aligned_orientation}},\n             {Direction<2>::upper_eta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<2>>>{{}});\n  }\n\n  {\n    INFO(\"Rectangle, periodic in xy with boundary conditions\");\n    test_rectangle_construction(\n        domain::creators::Rectangle{\n            lower_bound,\n            upper_bound,\n            refinement_level[0],\n            grid_points[0],\n            {{{{periodic_bc.get_clone(), periodic_bc.get_clone()}},\n              {{periodic_bc.get_clone(), periodic_bc.get_clone()}}}}},\n        lower_bound, upper_bound, grid_points, refinement_level,\n        std::vector<DirectionMap<2, BlockNeighbors<2>>>{\n            {{Direction<2>::lower_xi(), {0, aligned_orientation}},\n             {Direction<2>::upper_xi(), {0, aligned_orientation}},\n             {Direction<2>::lower_eta(), {0, aligned_orientation}},\n             {Direction<2>::upper_eta(), {0, aligned_orientation}}}},\n        std::vector<std::unordered_set<Direction<2>>>{{}}, {}, {}, true);\n  }\n  CHECK_THROWS_WITH(\n      creators::Rectangle(lower_bound, upper_bound, refinement_level[0],\n                          grid_points[0],\n                          {{{{none_bc.get_clone(), none_bc.get_clone()}},\n                            {{test_bc.get_clone(), test_bc.get_clone()}}}},\n                          {}, nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like an \"\n          \"outflow-type boundary condition, you must use that.\"));\n}\n\nvoid test_rectangle_factory() {\n  // For non-periodic domains:\n  std::vector<DirectionMap<\n      2, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      expected_boundary_conditions{1};\n  for (const auto& direction : Direction<2>::all_directions()) {\n    expected_boundary_conditions[0][direction] = std::make_unique<\n        TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<2>>(\n        Direction<2>::lower_xi(), 0);\n  }\n  const std::vector<std::unordered_set<Direction<2>>>\n      expected_external_boundaries{\n          {Direction<2>::lower_xi(), Direction<2>::upper_xi(),\n           Direction<2>::lower_eta(), Direction<2>::upper_eta()}};\n\n  // for periodic domains:\n  const std::vector<DirectionMap<2, BlockNeighbors<2>>> expected_neighbors{\n      {{Direction<2>::lower_xi(), {0, OrientationMap<2>::create_aligned()}},\n       {Direction<2>::upper_xi(), {0, OrientationMap<2>::create_aligned()}}}};\n\n  {\n    INFO(\"Rectangle factory time independent, no boundary condition\");\n    const auto domain_creator =\n        TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<2>,\n                                     TestHelpers::domain::BoundaryConditions::\n                                         MetavariablesWithoutBoundaryConditions<\n                                             2, domain::creators::Rectangle>>(\n            \"Rectangle:\\n\"\n            \"  LowerBound: [0,0]\\n\"\n            \"  UpperBound: [1,2]\\n\"\n            \"  Distribution: [Linear, Linear]\\n\"\n            \"  IsPeriodicIn: [True,False]\\n\"\n            \"  InitialGridPoints: [3,4]\\n\"\n            \"  InitialRefinement: [2,3]\\n\"\n            \"  TimeDependence: None\\n\");\n    const auto* rectangle_creator =\n        dynamic_cast<const creators::Rectangle*>(domain_creator.get());\n    test_rectangle_construction(\n        *rectangle_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4}}}, {{{2, 3}}},\n        expected_neighbors,\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_eta()}, {Direction<2>::upper_eta()}}});\n  }\n  {\n    INFO(\"Rectangle factory time independent, with boundary condition\");\n    const auto domain_creator =\n        TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<2>,\n                                     TestHelpers::domain::BoundaryConditions::\n                                         MetavariablesWithBoundaryConditions<\n                                             2, domain::creators::Rectangle>>(\n            \"Rectangle:\\n\"\n            \"  LowerBound: [0,0]\\n\"\n            \"  UpperBound: [1,2]\\n\"\n            \"  Distribution: [Linear, Linear]\\n\"\n            \"  InitialGridPoints: [3,4]\\n\"\n            \"  InitialRefinement: [2,3]\\n\"\n            \"  TimeDependence: None\\n\"\n            \"  BoundaryConditions:\\n\"\n            \"    - TestBoundaryCondition:\\n\"\n            \"        Direction: lower-xi\\n\"\n            \"        BlockId: 0\\n\"\n            \"    - TestBoundaryCondition:\\n\"\n            \"        Direction: lower-xi\\n\"\n            \"        BlockId: 0\\n\");\n    const auto* rectangle_creator =\n        dynamic_cast<const creators::Rectangle*>(domain_creator.get());\n    test_rectangle_construction(\n        *rectangle_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4}}}, {{{2, 3}}},\n        {{}},\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_xi(), Direction<2>::upper_xi(),\n              Direction<2>::lower_eta(), Direction<2>::upper_eta()}}},\n        {}, {}, true);\n  }\n  {\n    INFO(\"Rectangle factory time dependent\");\n    const auto domain_creator =\n        TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<2>,\n                                     TestHelpers::domain::BoundaryConditions::\n                                         MetavariablesWithoutBoundaryConditions<\n                                             2, domain::creators::Rectangle>>(\n            \"Rectangle:\\n\"\n            \"  LowerBound: [0,0]\\n\"\n            \"  UpperBound: [1,2]\\n\"\n            \"  Distribution: [Linear, Linear]\\n\"\n            \"  IsPeriodicIn: [True,False]\\n\"\n            \"  InitialGridPoints: [3,4]\\n\"\n            \"  InitialRefinement: [2,3]\\n\"\n            \"  TimeDependence:\\n\"\n            \"    UniformTranslation:\\n\"\n            \"      InitialTime: 1.0\\n\"\n            \"      Velocity: [2.3, -0.3]\\n\");\n    const auto* rectangle_creator =\n        dynamic_cast<const creators::Rectangle*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, -0.3}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_rectangle_construction(\n        *rectangle_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4}}}, {{{2, 3}}},\n        expected_neighbors,\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_eta()}, {Direction<2>::upper_eta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{2, 0.0}, velocity, {2, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation2D{f_of_t_name}));\n    // with expiration times\n    test_rectangle_construction(\n        *rectangle_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4}}}, {{{2, 3}}},\n        expected_neighbors,\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_eta()}, {Direction<2>::upper_eta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{2, 0.0}, velocity, {2, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation2D{f_of_t_name}),\n        false, initial_expiration_times);\n  }\n  {\n    INFO(\"Rectangle factory time dependent, with boundary conditions\");\n    const auto domain_creator =\n        TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<2>,\n                                     TestHelpers::domain::BoundaryConditions::\n                                         MetavariablesWithBoundaryConditions<\n                                             2, domain::creators::Rectangle>>(\n            \"Rectangle:\\n\"\n            \"  LowerBound: [0,0]\\n\"\n            \"  UpperBound: [1,2]\\n\"\n            \"  Distribution: [Linear, Linear]\\n\"\n            \"  InitialGridPoints: [3,4]\\n\"\n            \"  InitialRefinement: [2,3]\\n\"\n            \"  TimeDependence:\\n\"\n            \"    UniformTranslation:\\n\"\n            \"      InitialTime: 1.0\\n\"\n            \"      Velocity: [2.3, -0.3]\\n\"\n            \"  BoundaryConditions:\\n\"\n            \"    - TestBoundaryCondition:\\n\"\n            \"        Direction: lower-xi\\n\"\n            \"        BlockId: 0\\n\"\n            \"    - TestBoundaryCondition:\\n\"\n            \"        Direction: lower-xi\\n\"\n            \"        BlockId: 0\\n\");\n    const auto* rectangle_creator =\n        dynamic_cast<const creators::Rectangle*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, -0.3}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_rectangle_construction(\n        *rectangle_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4}}}, {{{2, 3}}},\n        {{}},\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_xi(), Direction<2>::upper_xi(),\n              Direction<2>::lower_eta(), Direction<2>::upper_eta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{2, 0.0}, velocity, {2, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation2D{f_of_t_name}),\n        true);\n    // with expiration times\n    test_rectangle_construction(\n        *rectangle_creator, {{0., 0.}}, {{1., 2.}}, {{{3, 4}}}, {{{2, 3}}},\n        {{}},\n        std::vector<std::unordered_set<Direction<2>>>{\n            {{Direction<2>::lower_xi(), Direction<2>::upper_xi(),\n              Direction<2>::lower_eta(), Direction<2>::upper_eta()}}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{2, 0.0}, velocity, {2, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation2D{f_of_t_name}),\n        true, initial_expiration_times);\n  }\n}\n\nvoid test_rectangle_factory_parse_errors() {\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_option_tag<domain::OptionTags::DomainCreator<2>,\n                                    TestHelpers::domain::BoundaryConditions::\n                                        MetavariablesWithoutBoundaryConditions<\n                                            2, domain::creators::Rectangle>>(\n          \"Rectangle:\\n\"\n          \"  LowerBound: [-0.5,-1.0]\\n\"\n          \"  UpperBound: [-0.5,1.0]\\n\"\n          \"  Distribution: [Linear, Linear]\\n\"\n          \"  IsPeriodicIn: [False,False]\\n\"\n          \"  InitialGridPoints: [4,4]\\n\"\n          \"  InitialRefinement: [1,1]\\n\"\n          \"  TimeDependence: None\\n\")),\n      Catch::Matchers::ContainsSubstring(\n          \"must be strictly smaller than upper bound (-0.5) in dimension 0\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.Rectangle\", \"[Domain][Unit]\") {\n  test_rectangle();\n  test_rectangle_factory();\n  test_rectangle_factory_parse_errors();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_RotatedBricks.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/RotatedBricks.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain {\nnamespace {\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition() {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      Direction<3>::upper_zeta(), 2);\n}\n\ntemplate <typename... FuncsOfTime>\nvoid test_rotated_bricks_construction(\n    const creators::RotatedBricks& rotated_bricks,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& midpoint,\n    const std::array<double, 3>& upper_bound,\n    const std::vector<std::array<size_t, 3>>& expected_extents,\n    const std::vector<std::array<size_t, 3>>& expected_refinement_level,\n    const std::vector<DirectionMap<3, BlockNeighbors<3>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<3>>>&\n        expected_external_boundaries,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time,\n    const std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>&\n        expected_grid_to_inertial_maps,\n    const bool expect_boundary_conditions = false,\n    const bool is_periodic = false,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const std::vector<double> times{1.};\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      rotated_bricks, expect_boundary_conditions, is_periodic, times);\n  CHECK(rotated_bricks.grid_anchors().empty());\n\n  CHECK(rotated_bricks.initial_extents() == expected_extents);\n  CHECK(rotated_bricks.initial_refinement_levels() ==\n        expected_refinement_level);\n  CHECK(rotated_bricks.block_names() ==\n        make_vector(\"Block(0,0,0)\"s, \"Block(1,0,0)\"s, \"Block(0,1,0)\"s,\n                    \"Block(1,1,0)\"s, \"Block(0,0,1)\"s, \"Block(1,0,1)\"s,\n                    \"Block(0,1,1)\"s, \"Block(1,1,1)\"s));\n\n  using Affine = CoordinateMaps::Affine;\n  using Affine3D = CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  using DiscreteRotation3D = CoordinateMaps::DiscreteRotation<3>;\n  using TargetFrame =\n      tmpl::conditional_t<sizeof...(FuncsOfTime) == 0, Frame::Inertial,\n                          Frame::Grid>;\n\n  const Affine lower_x_map(-1.0, 1.0, lower_bound[0], midpoint[0]);\n  const Affine upper_x_map(-1.0, 1.0, midpoint[0], upper_bound[0]);\n  const Affine lower_y_map(-1.0, 1.0, lower_bound[1], midpoint[1]);\n  const Affine upper_y_map(-1.0, 1.0, midpoint[1], upper_bound[1]);\n  const Affine lower_z_map(-1.0, 1.0, lower_bound[2], midpoint[2]);\n  const Affine upper_z_map(-1.0, 1.0, midpoint[2], upper_bound[2]);\n\n  std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, TargetFrame, 3>>>\n      coord_maps;\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          Affine3D(lower_x_map, lower_y_map, lower_z_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_zeta(), Direction<3>::upper_eta(),\n               Direction<3>::lower_xi()}}}},\n          Affine3D(upper_x_map, lower_y_map, lower_z_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n               Direction<3>::lower_eta()}}}},\n          Affine3D(lower_x_map, upper_y_map, lower_z_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_zeta(), Direction<3>::lower_xi(),\n               Direction<3>::lower_eta()}}}},\n          Affine3D(upper_x_map, upper_y_map, lower_z_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n               Direction<3>::upper_zeta()}}}},\n          Affine3D(lower_x_map, lower_y_map, upper_z_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_eta(), Direction<3>::lower_zeta(),\n               Direction<3>::lower_xi()}}}},\n          Affine3D(upper_x_map, lower_y_map, upper_z_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation3D{OrientationMap<3>{std::array<Direction<3>, 3>{\n              {Direction<3>::upper_zeta(), Direction<3>::lower_xi(),\n               Direction<3>::lower_eta()}}}},\n          Affine3D(lower_x_map, upper_y_map, upper_z_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          Affine3D(upper_x_map, upper_y_map, upper_z_map)));\n\n  test_domain_construction(domain, expected_block_neighbors,\n                           expected_external_boundaries, coord_maps, 10.0,\n                           rotated_bricks.functions_of_time(),\n                           expected_grid_to_inertial_maps);\n  TestHelpers::domain::creators::test_functions_of_time(\n      rotated_bricks, expected_functions_of_time, initial_expiration_times);\n}\n\nvoid test_rotated_bricks() {\n  INFO(\"Rotated bricks\");\n  const std::vector<std::array<size_t, 3>> grid_points{\n      {{4, 2, 5}}, {{5, 2, 1}}, {{4, 5, 3}}, {{3, 5, 1}},\n      {{2, 4, 6}}, {{6, 1, 2}}, {{3, 6, 4}}, {{1, 3, 6}}};\n  const std::vector<std::array<size_t, 3>> refinement_level{\n      {{0, 1, 2}}, {{2, 1, 0}}, {{0, 2, 1}}, {{1, 2, 0}},\n      {{1, 0, 2}}, {{2, 0, 1}}, {{1, 2, 0}}, {{0, 1, 2}}};\n  const std::array<double, 3> lower_bound{{-1.3, -3.0, 2.0}};\n  const std::array<double, 3> midpoint{{-0.6, 0.3, 3.2}};\n  const std::array<double, 3> upper_bound{{0.8, 3.0, 4.7}};\n  const OrientationMap<3> rotation_F{std::array<Direction<3>, 3>{\n      {Direction<3>::upper_zeta(), Direction<3>::upper_eta(),\n       Direction<3>::lower_xi()}}};\n  const OrientationMap<3> rotation_R{std::array<Direction<3>, 3>{\n      {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n       Direction<3>::lower_eta()}}};\n  const OrientationMap<3> rotation_U{std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n       Direction<3>::upper_zeta()}}};\n  const OrientationMap<3> rotation_R_then_U{std::array<Direction<3>, 3>{\n      {Direction<3>::lower_eta(), Direction<3>::lower_zeta(),\n       Direction<3>::upper_xi()}}};\n  const OrientationMap<3> rotation_F_then_U{std::array<Direction<3>, 3>{\n      {Direction<3>::lower_zeta(), Direction<3>::upper_xi(),\n       Direction<3>::lower_eta()}}};\n  for (const bool with_boundary_conditions : {true, false}) {\n    CAPTURE(with_boundary_conditions);\n    const creators::RotatedBricks rotated_bricks = [&]() {\n      if (with_boundary_conditions) {\n        return creators::RotatedBricks{\n            lower_bound,\n            midpoint,\n            upper_bound,\n            {{refinement_level[0][0], refinement_level[0][1],\n              refinement_level[0][2]}},\n            {{{{grid_points[0][0], grid_points[1][2]}},\n              {{grid_points[0][1], grid_points[2][2]}},\n              {{grid_points[0][2], grid_points[4][2]}}}},\n            create_boundary_condition(),\n            nullptr};\n      } else {\n        return creators::RotatedBricks{\n            lower_bound,\n            midpoint,\n            upper_bound,\n            {{refinement_level[0][0], refinement_level[0][1],\n              refinement_level[0][2]}},\n            {{{{grid_points[0][0], grid_points[1][2]}},\n              {{grid_points[0][1], grid_points[2][2]}},\n              {{grid_points[0][2], grid_points[4][2]}}}},\n            {{false, false, false}},\n            nullptr};\n      }\n    }();\n    test_rotated_bricks_construction(\n        rotated_bricks, lower_bound, midpoint, upper_bound, grid_points,\n        refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::upper_xi(), {1, rotation_F}},\n             {Direction<3>::upper_eta(), {2, rotation_R}},\n             {Direction<3>::upper_zeta(), {4, rotation_U}}},\n            {{Direction<3>::lower_xi(), {5, rotation_R.inverse_map()}},\n             {Direction<3>::upper_eta(), {3, rotation_U}},\n             {Direction<3>::lower_zeta(), {0, rotation_F.inverse_map()}}},\n            {{Direction<3>::upper_xi(), {3, rotation_F}},\n             {Direction<3>::lower_eta(), {6, rotation_F}},\n             {Direction<3>::lower_zeta(), {0, rotation_R.inverse_map()}}},\n            {{Direction<3>::upper_xi(), {1, rotation_U.inverse_map()}},\n             {Direction<3>::lower_eta(), {7, rotation_R_then_U}},\n             {Direction<3>::lower_zeta(), {2, rotation_F.inverse_map()}}},\n            {{Direction<3>::lower_xi(), {6, rotation_R}},\n             {Direction<3>::upper_eta(), {5, rotation_F}},\n             {Direction<3>::lower_zeta(), {0, rotation_U.inverse_map()}}},\n            {{Direction<3>::upper_xi(), {1, rotation_R}},\n             {Direction<3>::lower_eta(), {4, rotation_F.inverse_map()}},\n             {Direction<3>::lower_zeta(), {7, rotation_F_then_U}}},\n            {{Direction<3>::upper_xi(), {4, rotation_R.inverse_map()}},\n             {Direction<3>::upper_eta(), {2, rotation_F.inverse_map()}},\n             {Direction<3>::upper_zeta(), {7, rotation_R_then_U}}},\n            {{Direction<3>::lower_xi(), {6, rotation_R_then_U.inverse_map()}},\n             {Direction<3>::lower_eta(), {5, rotation_F_then_U.inverse_map()}},\n             {Direction<3>::lower_zeta(),\n              {3, rotation_R_then_U.inverse_map()}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n             Direction<3>::lower_zeta()},\n            {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n             Direction<3>::upper_zeta()},\n            {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n             Direction<3>::upper_zeta()},\n            {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n             Direction<3>::upper_zeta()},\n            {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n             Direction<3>::upper_zeta()},\n            {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n             Direction<3>::upper_zeta()},\n            {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n             Direction<3>::lower_zeta()},\n            {Direction<3>::upper_xi(), Direction<3>::upper_eta(),\n             Direction<3>::upper_zeta()}},\n        std::tuple<>{}, {}, with_boundary_conditions);\n\n    const creators::RotatedBricks rotated_periodic_bricks = [&]() {\n      if (with_boundary_conditions) {\n        return creators::RotatedBricks{\n            lower_bound,\n            midpoint,\n            upper_bound,\n            {{refinement_level[0][0], refinement_level[0][1],\n              refinement_level[0][2]}},\n            {{{{grid_points[0][0], grid_points[1][2]}},\n              {{grid_points[0][1], grid_points[2][2]}},\n              {{grid_points[0][2], grid_points[4][2]}}}},\n            std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                 TestPeriodicBoundaryCondition<3>>(),\n            nullptr};\n      } else {\n        return creators::RotatedBricks{\n            lower_bound,\n            midpoint,\n            upper_bound,\n            {{refinement_level[0][0], refinement_level[0][1],\n              refinement_level[0][2]}},\n            {{{{grid_points[0][0], grid_points[1][2]}},\n              {{grid_points[0][1], grid_points[2][2]}},\n              {{grid_points[0][2], grid_points[4][2]}}}},\n            {{true, true, true}},\n            nullptr};\n      }\n    }();\n    test_rotated_bricks_construction(\n        rotated_periodic_bricks, lower_bound, midpoint, upper_bound,\n        grid_points, refinement_level,\n        std::vector<DirectionMap<3, BlockNeighbors<3>>>{\n            {{Direction<3>::upper_xi(), {1, rotation_F}},\n             {Direction<3>::upper_eta(), {2, rotation_R}},\n             {Direction<3>::upper_zeta(), {4, rotation_U}},\n             {Direction<3>::lower_xi(), {1, rotation_F}},\n             {Direction<3>::lower_eta(), {2, rotation_R}},\n             {Direction<3>::lower_zeta(), {4, rotation_U}}},\n            {{Direction<3>::lower_xi(), {5, rotation_R.inverse_map()}},\n             {Direction<3>::upper_eta(), {3, rotation_U}},\n             {Direction<3>::lower_zeta(), {0, rotation_F.inverse_map()}},\n             {Direction<3>::upper_xi(), {5, rotation_R.inverse_map()}},\n             {Direction<3>::lower_eta(), {3, rotation_U}},\n             {Direction<3>::upper_zeta(), {0, rotation_F.inverse_map()}}},\n            {{Direction<3>::upper_xi(), {3, rotation_F}},\n             {Direction<3>::lower_eta(), {6, rotation_F}},\n             {Direction<3>::lower_zeta(), {0, rotation_R.inverse_map()}},\n             {Direction<3>::lower_xi(), {3, rotation_F}},\n             {Direction<3>::upper_eta(), {6, rotation_F}},\n             {Direction<3>::upper_zeta(), {0, rotation_R.inverse_map()}}},\n            {{Direction<3>::upper_xi(), {1, rotation_U.inverse_map()}},\n             {Direction<3>::lower_eta(), {7, rotation_R_then_U}},\n             {Direction<3>::lower_zeta(), {2, rotation_F.inverse_map()}},\n             {Direction<3>::lower_xi(), {1, rotation_U.inverse_map()}},\n             {Direction<3>::upper_eta(), {7, rotation_R_then_U}},\n             {Direction<3>::upper_zeta(), {2, rotation_F.inverse_map()}}},\n            {{Direction<3>::lower_xi(), {6, rotation_R}},\n             {Direction<3>::upper_eta(), {5, rotation_F}},\n             {Direction<3>::lower_zeta(), {0, rotation_U.inverse_map()}},\n             {Direction<3>::upper_xi(), {6, rotation_R}},\n             {Direction<3>::lower_eta(), {5, rotation_F}},\n             {Direction<3>::upper_zeta(), {0, rotation_U.inverse_map()}}},\n            {{Direction<3>::upper_xi(), {1, rotation_R}},\n             {Direction<3>::lower_eta(), {4, rotation_F.inverse_map()}},\n             {Direction<3>::lower_zeta(), {7, rotation_F_then_U}},\n             {Direction<3>::lower_xi(), {1, rotation_R}},\n             {Direction<3>::upper_eta(), {4, rotation_F.inverse_map()}},\n             {Direction<3>::upper_zeta(), {7, rotation_F_then_U}}},\n            {{Direction<3>::upper_xi(), {4, rotation_R.inverse_map()}},\n             {Direction<3>::upper_eta(), {2, rotation_F.inverse_map()}},\n             {Direction<3>::upper_zeta(), {7, rotation_R_then_U}},\n             {Direction<3>::lower_xi(), {4, rotation_R.inverse_map()}},\n             {Direction<3>::lower_eta(), {2, rotation_F.inverse_map()}},\n             {Direction<3>::lower_zeta(), {7, rotation_R_then_U}}},\n            {{Direction<3>::upper_xi(), {6, rotation_R_then_U.inverse_map()}},\n             {Direction<3>::upper_eta(), {5, rotation_F_then_U.inverse_map()}},\n             {Direction<3>::upper_zeta(), {3, rotation_R_then_U.inverse_map()}},\n             {Direction<3>::lower_xi(), {6, rotation_R_then_U.inverse_map()}},\n             {Direction<3>::lower_eta(), {5, rotation_F_then_U.inverse_map()}},\n             {Direction<3>::lower_zeta(),\n              {3, rotation_R_then_U.inverse_map()}}}},\n        std::vector<std::unordered_set<Direction<3>>>{\n            {}, {}, {}, {}, {}, {}, {}, {}},\n        std::tuple<>{}, {}, with_boundary_conditions, true);\n  }\n\n  CHECK_THROWS_WITH(\n      creators::RotatedBricks(\n          lower_bound, midpoint, upper_bound,\n          {{refinement_level[0][0], refinement_level[0][1],\n            refinement_level[0][2]}},\n          {{{{grid_points[0][0], grid_points[1][2]}},\n            {{grid_points[0][1], grid_points[2][2]}},\n            {{grid_points[0][2], grid_points[4][2]}}}},\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n}\n\nvoid test_rotated_bricks_factory() {\n  INFO(\"Rotated bricks factory\");\n  domain::creators::time_dependence::register_derived_with_charm();\n  const OrientationMap<3> rotation_F{std::array<Direction<3>, 3>{\n      {Direction<3>::upper_zeta(), Direction<3>::upper_eta(),\n       Direction<3>::lower_xi()}}};\n  const OrientationMap<3> rotation_R{std::array<Direction<3>, 3>{\n      {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n       Direction<3>::lower_eta()}}};\n  const OrientationMap<3> rotation_U{std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_xi(),\n       Direction<3>::upper_zeta()}}};\n  const OrientationMap<3> rotation_R_then_U{std::array<Direction<3>, 3>{\n      {Direction<3>::lower_eta(), Direction<3>::lower_zeta(),\n       Direction<3>::upper_xi()}}};\n  const OrientationMap<3> rotation_F_then_U{std::array<Direction<3>, 3>{\n      {Direction<3>::lower_zeta(), Direction<3>::upper_xi(),\n       Direction<3>::lower_eta()}}};\n  const std::vector<DirectionMap<3, BlockNeighbors<3>>>\n      expected_block_neighbors_nonperiodic{\n          {{Direction<3>::upper_xi(), {1, rotation_F}},\n           {Direction<3>::upper_eta(), {2, rotation_R}},\n           {Direction<3>::upper_zeta(), {4, rotation_U}}},\n          {{Direction<3>::lower_xi(), {5, rotation_R.inverse_map()}},\n           {Direction<3>::upper_eta(), {3, rotation_U}},\n           {Direction<3>::lower_zeta(), {0, rotation_F.inverse_map()}}},\n          {{Direction<3>::upper_xi(), {3, rotation_F}},\n           {Direction<3>::lower_eta(), {6, rotation_F}},\n           {Direction<3>::lower_zeta(), {0, rotation_R.inverse_map()}}},\n          {{Direction<3>::upper_xi(), {1, rotation_U.inverse_map()}},\n           {Direction<3>::lower_eta(), {7, rotation_R_then_U}},\n           {Direction<3>::lower_zeta(), {2, rotation_F.inverse_map()}}},\n          {{Direction<3>::lower_xi(), {6, rotation_R}},\n           {Direction<3>::upper_eta(), {5, rotation_F}},\n           {Direction<3>::lower_zeta(), {0, rotation_U.inverse_map()}}},\n          {{Direction<3>::upper_xi(), {1, rotation_R}},\n           {Direction<3>::lower_eta(), {4, rotation_F.inverse_map()}},\n           {Direction<3>::lower_zeta(), {7, rotation_F_then_U}}},\n          {{Direction<3>::upper_xi(), {4, rotation_R.inverse_map()}},\n           {Direction<3>::upper_eta(), {2, rotation_F.inverse_map()}},\n           {Direction<3>::upper_zeta(), {7, rotation_R_then_U}}},\n          {{Direction<3>::lower_xi(), {6, rotation_R_then_U.inverse_map()}},\n           {Direction<3>::lower_eta(), {5, rotation_F_then_U.inverse_map()}},\n           {Direction<3>::lower_zeta(), {3, rotation_R_then_U.inverse_map()}}}};\n  const std::vector<std::unordered_set<Direction<3>>>\n      expected_external_boundaries_nonperiodic{\n          {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n           Direction<3>::lower_zeta()},\n          {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::lower_xi(), Direction<3>::upper_eta(),\n           Direction<3>::upper_zeta()},\n          {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n           Direction<3>::lower_zeta()},\n          {Direction<3>::upper_xi(), Direction<3>::upper_eta(),\n           Direction<3>::upper_zeta()}};\n  const std::vector<std::array<size_t, 3>> expected_extents{\n      {{3, 1, 5}}, {{5, 1, 2}}, {{3, 5, 4}}, {{4, 5, 2}},\n      {{1, 3, 6}}, {{6, 2, 1}}, {{4, 6, 3}}, {{2, 4, 6}}};\n  const std::vector<std::array<size_t, 3>> expected_refinement{\n      {{2, 1, 0}}, {{0, 1, 2}}, {{2, 0, 1}}, {{1, 0, 2}},\n      {{1, 2, 0}}, {{0, 2, 1}}, {{1, 0, 2}}, {{2, 1, 0}}};\n\n  for (const bool with_boundary_conditions : {true, false}) {\n    CAPTURE(with_boundary_conditions);\n    const std::string opt_string{\n        \"RotatedBricks:\\n\"\n        \"  LowerBound: [0.1, -0.4, -0.2]\\n\"\n        \"  Midpoint:   [2.6, 3.2, 1.7]\\n\"\n        \"  UpperBound: [5.1, 6.2, 3.2]\\n\"\n        \"  InitialGridPoints: [[3,2],[1,4],[5,6]]\\n\"\n        \"  InitialRefinement: [2,1,0]\\n\" +\n        std::string{with_boundary_conditions\n                        ? \"  BoundaryCondition:\\n\"\n                          \"    TestBoundaryCondition:\\n\"\n                          \"      Direction: upper-zeta\\n\"\n                          \"      BlockId: 2\\n\"\n                        : \"  IsPeriodicIn: [false, false, false]\\n\"} +\n        \"  TimeDependence: None\\n\"};\n    const auto domain_creator = [&opt_string, with_boundary_conditions]() {\n      if (with_boundary_conditions) {\n        return TestHelpers::test_option_tag<\n            domain::OptionTags::DomainCreator<3>,\n            TestHelpers::domain::BoundaryConditions::\n                MetavariablesWithBoundaryConditions<\n                    3, domain::creators::RotatedBricks>>(opt_string);\n      } else {\n        return TestHelpers::test_option_tag<\n            domain::OptionTags::DomainCreator<3>,\n            TestHelpers::domain::BoundaryConditions::\n                MetavariablesWithoutBoundaryConditions<\n                    3, domain::creators::RotatedBricks>>(opt_string);\n      }\n    }();\n    const auto* rotated_bricks_creator =\n        dynamic_cast<const creators::RotatedBricks*>(domain_creator.get());\n    test_rotated_bricks_construction(\n        *rotated_bricks_creator, {{0.1, -0.4, -0.2}}, {{2.6, 3.2, 1.7}},\n        {{5.1, 6.2, 3.2}}, expected_extents, expected_refinement,\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic, std::tuple<>{}, {},\n        with_boundary_conditions);\n  }\n  {\n    INFO(\"No boundary condition, time dependent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<\n                3, domain::creators::RotatedBricks>>(\n        \"RotatedBricks:\\n\"\n        \"  LowerBound: [0.1, -0.4, -0.2]\\n\"\n        \"  Midpoint:   [2.6, 3.2, 1.7]\\n\"\n        \"  UpperBound: [5.1, 6.2, 3.2]\\n\"\n        \"  InitialGridPoints: [[3,2],[1,4],[5,6]]\\n\"\n        \"  InitialRefinement: [2,1,0]\\n\"\n        \"  IsPeriodicIn: [false, false, false]\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3, 0.5, 0.1]\\n\");\n    const auto* rotated_bricks_creator =\n        dynamic_cast<const creators::RotatedBricks*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, 0.5, 0.1}};\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_rotated_bricks_construction(\n        *rotated_bricks_creator, {{0.1, -0.4, -0.2}}, {{2.6, 3.2, 1.7}},\n        {{5.1, 6.2, 3.2}}, expected_extents, expected_refinement,\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{\n                     {{0.0, 0.0, 0.0}, velocity, {0.0, 0.0, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name}));\n    // with expiration times\n    test_rotated_bricks_construction(\n        *rotated_bricks_creator, {{0.1, -0.4, -0.2}}, {{2.6, 3.2, 1.7}},\n        {{5.1, 6.2, 3.2}}, expected_extents, expected_refinement,\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{\n                     {{0.0, 0.0, 0.0}, velocity, {0.0, 0.0, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name}),\n        false, false, initial_expiration_times);\n  }\n  {\n    INFO(\"With boundary condition, time dependent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<3>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                3, domain::creators::RotatedBricks>>(\n        \"RotatedBricks:\\n\"\n        \"  LowerBound: [0.1, -0.4, -0.2]\\n\"\n        \"  Midpoint:   [2.6, 3.2, 1.7]\\n\"\n        \"  UpperBound: [5.1, 6.2, 3.2]\\n\"\n        \"  InitialGridPoints: [[3,2],[1,4],[5,6]]\\n\"\n        \"  InitialRefinement: [2,1,0]\\n\"\n        \"  BoundaryCondition:\\n\"\n        \"    TestBoundaryCondition:\\n\"\n        \"      Direction: upper-zeta\\n\"\n        \"      BlockId: 2\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3, 0.5, 0.1]\\n\");\n    const auto* rotated_bricks_creator =\n        dynamic_cast<const creators::RotatedBricks*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, 0.5, 0.1}};\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_rotated_bricks_construction(\n        *rotated_bricks_creator, {{0.1, -0.4, -0.2}}, {{2.6, 3.2, 1.7}},\n        {{5.1, 6.2, 3.2}}, expected_extents, expected_refinement,\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{\n                     {{0.0, 0.0, 0.0}, velocity, {0.0, 0.0, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name}),\n        true);\n    // with expiration times\n    test_rotated_bricks_construction(\n        *rotated_bricks_creator, {{0.1, -0.4, -0.2}}, {{2.6, 3.2, 1.7}},\n        {{5.1, 6.2, 3.2}}, expected_extents, expected_refinement,\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{\n                     {{0.0, 0.0, 0.0}, velocity, {0.0, 0.0, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<3>{f_of_t_name}),\n        true, false, initial_expiration_times);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.RotatedBricks\", \"[Domain][Unit]\") {\n  test_rotated_bricks();\n  test_rotated_bricks_factory();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_RotatedIntervals.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/RotatedIntervals.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <typename... FuncsOfTime>\nvoid test_rotated_intervals_construction(\n    const creators::RotatedIntervals& rotated_intervals,\n    const std::array<double, 1>& lower_bound,\n    const std::array<double, 1>& midpoint,\n    const std::array<double, 1>& upper_bound,\n    const std::vector<std::array<size_t, 1>>& expected_extents,\n    const std::vector<std::array<size_t, 1>>& expected_refinement_level,\n    const std::vector<DirectionMap<1, BlockNeighbors<1>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<1>>>&\n        expected_external_boundaries,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time,\n    const std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 1>>>&\n        expected_grid_to_inertial_maps,\n    const bool expect_boundary_conditions = false,\n    const bool is_periodic = false,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const std::vector<double> times{1.};\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      rotated_intervals, expect_boundary_conditions, is_periodic, times);\n  CHECK(rotated_intervals.grid_anchors().empty());\n\n  CHECK(rotated_intervals.initial_extents() == expected_extents);\n  CHECK(rotated_intervals.initial_refinement_levels() ==\n        expected_refinement_level);\n  CHECK(rotated_intervals.block_names() ==\n        make_vector(\"Block(0)\"s, \"Block(1)\"s));\n\n  test_domain_construction(\n      domain, expected_block_neighbors, expected_external_boundaries,\n      make_vector(\n          make_coordinate_map_base<\n              Frame::BlockLogical,\n              tmpl::conditional_t<sizeof...(FuncsOfTime) == 0, Frame::Inertial,\n                                  Frame::Grid>>(\n              CoordinateMaps::Affine{-1., 1., lower_bound[0], midpoint[0]}),\n          make_coordinate_map_base<\n              Frame::BlockLogical,\n              tmpl::conditional_t<sizeof...(FuncsOfTime) == 0, Frame::Inertial,\n                                  Frame::Grid>>(\n              CoordinateMaps::DiscreteRotation<1>{OrientationMap<1>{\n                  std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}}}},\n              CoordinateMaps::Affine{-1., 1., midpoint[0], upper_bound[0]})),\n      10.0, rotated_intervals.functions_of_time(),\n      expected_grid_to_inertial_maps);\n  TestHelpers::domain::creators::test_functions_of_time(\n      rotated_intervals, expected_functions_of_time, initial_expiration_times);\n}\n\nvoid test_rotated_intervals() {\n  INFO(\"Rotated intervals\");\n  const std::vector<std::array<size_t, 1>> grid_points{{{4}}, {{2}}};\n  const std::vector<std::array<size_t, 1>> refinement_level{{{0}}, {{0}}};\n  const std::array<double, 1> lower_bound{{-1.2}};\n  const std::array<double, 1> midpoint{{-0.6}};\n  const std::array<double, 1> upper_bound{{0.8}};\n  const OrientationMap<1> flipped{\n      std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}}};\n\n  const auto check_nonperiodic =\n      [&lower_bound, &midpoint, &upper_bound, &grid_points, &refinement_level,\n       &flipped](const creators::RotatedIntervals& rotated_intervals,\n                 const bool expect_bcs) {\n        test_rotated_intervals_construction(\n            rotated_intervals, lower_bound, midpoint, upper_bound, grid_points,\n            refinement_level,\n            std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n                {{Direction<1>::upper_xi(), {1, flipped}}},\n                {{Direction<1>::upper_xi(), {0, flipped}}}},\n            std::vector<std::unordered_set<Direction<1>>>{\n                {Direction<1>::lower_xi()}, {Direction<1>::lower_xi()}},\n            std::tuple<>{}, {}, expect_bcs);\n      };\n\n  const auto check_periodic =\n      [&lower_bound, &midpoint, &upper_bound, &grid_points, &refinement_level,\n       &flipped](const creators::RotatedIntervals& periodic_rotated_intervals,\n                 const bool expect_bcs) {\n        test_rotated_intervals_construction(\n            periodic_rotated_intervals, lower_bound, midpoint, upper_bound,\n            grid_points, refinement_level,\n            std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n                {{Direction<1>::lower_xi(), {1, flipped}},\n                 {Direction<1>::upper_xi(), {1, flipped}}},\n                {{Direction<1>::lower_xi(), {0, flipped}},\n                 {Direction<1>::upper_xi(), {0, flipped}}}},\n            std::vector<std::unordered_set<Direction<1>>>{{}, {}},\n            std::tuple<>{}, {}, expect_bcs, true);\n      };\n\n  {\n    INFO(\"Check non-periodic via array of bools\");\n    check_nonperiodic({lower_bound,\n                       midpoint,\n                       upper_bound,\n                       refinement_level[0],\n                       {{{{grid_points[0][0], grid_points[1][0]}}}},\n                       std::array<bool, 1>{{false}},\n                       nullptr},\n                      false);\n  }\n\n  {\n    INFO(\"Check periodic via array of bools\");\n    check_periodic({lower_bound,\n                    midpoint,\n                    upper_bound,\n                    refinement_level[0],\n                    {{{{grid_points[0][0], grid_points[1][0]}}}},\n                    std::array<bool, 1>{{true}},\n                    nullptr},\n                   false);\n  }\n\n  // Test with boundary conditions\n  const auto lower_bc = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<1>>(\n      Direction<1>::lower_xi(), 0);\n  const auto upper_bc = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<1>>(\n      Direction<1>::upper_xi(), 1);\n  {\n    INFO(\"Check non-periodic with boundary conditions\");\n    check_nonperiodic({lower_bound,\n                       midpoint,\n                       upper_bound,\n                       refinement_level[0],\n                       {{{{grid_points[0][0], grid_points[1][0]}}}},\n                       lower_bc->get_clone(),\n                       upper_bc->get_clone(),\n                       nullptr},\n                      true);\n  }\n\n  const std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      periodic = std::make_unique<TestHelpers::domain::BoundaryConditions::\n                                      TestPeriodicBoundaryCondition<1>>();\n  {\n    INFO(\"Check periodic via boundary conditions\");\n    check_periodic({lower_bound,\n                    midpoint,\n                    upper_bound,\n                    refinement_level[0],\n                    {{{{grid_points[0][0], grid_points[1][0]}}}},\n                    periodic->get_clone(),\n                    periodic->get_clone(),\n                    nullptr},\n                   true);\n  }\n\n  // Test parse error\n  CHECK_THROWS_WITH(\n      creators::RotatedIntervals(\n          lower_bound, midpoint, upper_bound, refinement_level[0],\n          {{{{grid_points[0][0], grid_points[1][0]}}}}, lower_bc->get_clone(),\n          periodic->get_clone(), nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Both the upper and lower boundary condition \"\n          \"must be set to periodic if\"));\n  CHECK_THROWS_WITH(\n      creators::RotatedIntervals(\n          lower_bound, midpoint, upper_bound, refinement_level[0],\n          {{{{grid_points[0][0], grid_points[1][0]}}}}, periodic->get_clone(),\n          lower_bc->get_clone(), nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Both the upper and lower boundary condition \"\n          \"must be set to periodic if\"));\n  CHECK_THROWS_WITH(\n      creators::RotatedIntervals(\n          lower_bound, midpoint, upper_bound, refinement_level[0],\n          {{{{grid_points[0][0], grid_points[1][0]}}}},\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          lower_bc->get_clone(), nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n  CHECK_THROWS_WITH(\n      creators::RotatedIntervals(\n          lower_bound, midpoint, upper_bound, refinement_level[0],\n          {{{{grid_points[0][0], grid_points[1][0]}}}}, lower_bc->get_clone(),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n}\n\nvoid test_rotated_intervals_factory() {\n  const OrientationMap<1> flipped{\n      std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}}};\n  const auto lower_bc = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<1>>(\n      Direction<1>::lower_xi(), 0);\n  const auto upper_bc = std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<1>>(\n      Direction<1>::upper_xi(), 1);\n  const std::vector<std::unordered_set<Direction<1>>>\n      expected_external_boundaries{{Direction<1>::lower_xi()},\n                                   {Direction<1>::lower_xi()}};\n  {\n    INFO(\"Rotated intervals factory time independent, no boundary condition\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<1>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<\n                1, domain::creators::RotatedIntervals>>(\n        \"RotatedIntervals:\\n\"\n        \"  LowerBound: [0.0]\\n\"\n        \"  Midpoint:   [0.5]\\n\"\n        \"  UpperBound: [1.0]\\n\"\n        \"  IsPeriodicIn: [True]\\n\"\n        \"  InitialGridPoints: [[3,2]]\\n\"\n        \"  InitialRefinement: [2]\\n\"\n        \"  TimeDependence: None\\n\");\n    const auto* rotated_intervals_creator =\n        dynamic_cast<const creators::RotatedIntervals*>(domain_creator.get());\n    test_rotated_intervals_construction(\n        *rotated_intervals_creator, {{0.0}}, {{0.5}}, {{1.0}}, {{{3}}, {{2}}},\n        {{{2}}, {{2}}},\n        std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n            {{Direction<1>::lower_xi(), {1, flipped}},\n             {Direction<1>::upper_xi(), {1, flipped}}},\n            {{Direction<1>::lower_xi(), {0, flipped}},\n             {Direction<1>::upper_xi(), {0, flipped}}}},\n        std::vector<std::unordered_set<Direction<1>>>{{}, {}}, {}, {}, false,\n        true);\n  }\n  {\n    INFO(\"Rotated intervals factory time independent, with boundary condition\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<1>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                1, domain::creators::RotatedIntervals>>(\n        \"RotatedIntervals:\\n\"\n        \"  LowerBound: [0.0]\\n\"\n        \"  Midpoint:   [0.5]\\n\"\n        \"  UpperBound: [1.0]\\n\"\n        \"  InitialGridPoints: [[3,2]]\\n\"\n        \"  InitialRefinement: [2]\\n\"\n        \"  TimeDependence: None\\n\"\n        \"  BoundaryConditions:\\n\"\n        \"    LowerBoundary:\\n\"\n        \"      TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\"\n        \"    UpperBoundary:\\n\"\n        \"      TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 1\\n\");\n    const auto* rotated_intervals_creator =\n        dynamic_cast<const creators::RotatedIntervals*>(domain_creator.get());\n    test_rotated_intervals_construction(\n        *rotated_intervals_creator, {{0.0}}, {{0.5}}, {{1.0}}, {{{3}}, {{2}}},\n        {{{2}}, {{2}}},\n        std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n            {{Direction<1>::upper_xi(), {1, flipped}}},\n            {{Direction<1>::upper_xi(), {0, flipped}}}},\n        expected_external_boundaries, {}, {}, true);\n  }\n  {\n    INFO(\"Rotated intervals factory time dependent, no boundary condition\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<1>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<\n                1, domain::creators::RotatedIntervals>>(\n        \"RotatedIntervals:\\n\"\n        \"  LowerBound: [0.0]\\n\"\n        \"  Midpoint:   [0.5]\\n\"\n        \"  UpperBound: [1.0]\\n\"\n        \"  IsPeriodicIn: [True]\\n\"\n        \"  InitialGridPoints: [[3,2]]\\n\"\n        \"  InitialRefinement: [2]\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3]\\n\");\n    const auto* rotated_intervals_creator =\n        dynamic_cast<const creators::RotatedIntervals*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_rotated_intervals_construction(\n        *rotated_intervals_creator, {{0.0}}, {{0.5}}, {{1.0}}, {{{3}}, {{2}}},\n        {{{2}}, {{2}}},\n        std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n            {{Direction<1>::lower_xi(), {1, flipped}},\n             {Direction<1>::upper_xi(), {1, flipped}}},\n            {{Direction<1>::lower_xi(), {0, flipped}},\n             {Direction<1>::upper_xi(), {0, flipped}}}},\n        std::vector<std::unordered_set<Direction<1>>>{{}, {}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{0.0}, velocity, {0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<1>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<1>{f_of_t_name}),\n        false, true);\n    // with expiration times\n    test_rotated_intervals_construction(\n        *rotated_intervals_creator, {{0.0}}, {{0.5}}, {{1.0}}, {{{3}}, {{2}}},\n        {{{2}}, {{2}}},\n        std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n            {{Direction<1>::lower_xi(), {1, flipped}},\n             {Direction<1>::upper_xi(), {1, flipped}}},\n            {{Direction<1>::lower_xi(), {0, flipped}},\n             {Direction<1>::upper_xi(), {0, flipped}}}},\n        std::vector<std::unordered_set<Direction<1>>>{{}, {}},\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{0.0}, velocity, {0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<1>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<1>{f_of_t_name}),\n        false, true, initial_expiration_times);\n  }\n  {\n    INFO(\"Rotated intervals factory time dependent, with boundary condition\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<1>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                1, domain::creators::RotatedIntervals>>(\n        \"RotatedIntervals:\\n\"\n        \"  LowerBound: [0.0]\\n\"\n        \"  Midpoint:   [0.5]\\n\"\n        \"  UpperBound: [1.0]\\n\"\n        \"  InitialGridPoints: [[3,2]]\\n\"\n        \"  InitialRefinement: [2]\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3]\\n\"\n        \"  BoundaryConditions:\\n\"\n        \"    LowerBoundary:\\n\"\n        \"      TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 0\\n\"\n        \"    UpperBoundary:\\n\"\n        \"      TestBoundaryCondition:\\n\"\n        \"        Direction: lower-xi\\n\"\n        \"        BlockId: 1\\n\");\n    const auto* rotated_intervals_creator =\n        dynamic_cast<const creators::RotatedIntervals*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_rotated_intervals_construction(\n        *rotated_intervals_creator, {{0.0}}, {{0.5}}, {{1.0}}, {{{3}}, {{2}}},\n        {{{2}}, {{2}}},\n        std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n            {{Direction<1>::upper_xi(), {1, flipped}}},\n            {{Direction<1>::upper_xi(), {0, flipped}}}},\n        expected_external_boundaries,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{0.0}, velocity, {0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<1>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<1>{f_of_t_name}),\n        true);\n    // with expiration times\n    test_rotated_intervals_construction(\n        *rotated_intervals_creator, {{0.0}}, {{0.5}}, {{1.0}}, {{{3}}, {{2}}},\n        {{{2}}, {{2}}},\n        std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n            {{Direction<1>::upper_xi(), {1, flipped}}},\n            {{Direction<1>::upper_xi(), {0, flipped}}}},\n        expected_external_boundaries,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{0.0}, velocity, {0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<1>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<1>{f_of_t_name}),\n        true, false, initial_expiration_times);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.RotatedIntervals\", \"[Domain][Unit]\") {\n  test_rotated_intervals();\n  test_rotated_intervals_factory();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_RotatedRectangles.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/RotatedRectangles.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <typename... FuncsOfTime>\nvoid test_rotated_rectangles_construction(\n    const creators::RotatedRectangles& rotated_rectangles,\n    const std::array<double, 2>& lower_bound,\n    const std::array<double, 2>& midpoint,\n    const std::array<double, 2>& upper_bound,\n    const std::vector<std::array<size_t, 2>>& expected_extents,\n    const std::vector<std::array<size_t, 2>>& expected_refinement_level,\n    const std::vector<DirectionMap<2, BlockNeighbors<2>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<2>>>&\n        expected_external_boundaries,\n    const std::tuple<std::pair<std::string, FuncsOfTime>...>&\n        expected_functions_of_time,\n    const std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 2>>>&\n        expected_grid_to_inertial_maps,\n    const bool expect_boundary_conditions = false,\n    const bool is_periodic = false,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const std::vector<double> times{1.};\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      rotated_rectangles, expect_boundary_conditions, is_periodic, times);\n  CHECK(rotated_rectangles.grid_anchors().empty());\n\n  CHECK(rotated_rectangles.initial_extents() == expected_extents);\n  CHECK(rotated_rectangles.initial_refinement_levels() ==\n        expected_refinement_level);\n  CHECK(\n      rotated_rectangles.block_names() ==\n      make_vector(\"Block(0,0)\"s, \"Block(1,0)\"s, \"Block(0,1)\"s, \"Block(1,1)\"s));\n\n  using Affine = CoordinateMaps::Affine;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using DiscreteRotation2D = CoordinateMaps::DiscreteRotation<2>;\n  using TargetFrame =\n      tmpl::conditional_t<sizeof...(FuncsOfTime) == 0, Frame::Inertial,\n                          Frame::Grid>;\n\n  const Affine lower_x_map(-1.0, 1.0, lower_bound[0], midpoint[0]);\n  const Affine upper_x_map(-1.0, 1.0, midpoint[0], upper_bound[0]);\n  const Affine lower_y_map(-1.0, 1.0, lower_bound[1], midpoint[1]);\n  const Affine upper_y_map(-1.0, 1.0, midpoint[1], upper_bound[1]);\n  std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, TargetFrame, 2>>>\n      coord_maps;\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          Affine2D(lower_x_map, lower_y_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation2D{OrientationMap<2>{std::array<Direction<2>, 2>{\n              {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}}},\n          Affine2D(upper_x_map, lower_y_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation2D{OrientationMap<2>{std::array<Direction<2>, 2>{\n              {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}}},\n          Affine2D(lower_x_map, upper_y_map)));\n  coord_maps.emplace_back(\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          DiscreteRotation2D{OrientationMap<2>{std::array<Direction<2>, 2>{\n              {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}}},\n          Affine2D(upper_x_map, upper_y_map)));\n  test_domain_construction(domain, expected_block_neighbors,\n                           expected_external_boundaries, coord_maps, 10.0,\n                           rotated_rectangles.functions_of_time(),\n                           expected_grid_to_inertial_maps);\n  TestHelpers::domain::creators::test_functions_of_time(\n      rotated_rectangles, expected_functions_of_time, initial_expiration_times);\n}\n\nvoid test_rotated_rectangles() {\n  INFO(\"Rotated rectangles\");\n  const std::vector<std::array<size_t, 2>> grid_points{\n      {{4, 2}}, {{1, 2}}, {{3, 4}}, {{3, 1}}};\n  const std::vector<std::array<size_t, 2>> refinement_level{\n      {{0, 1}}, {{0, 1}}, {{1, 0}}, {{1, 0}}};\n  const std::array<double, 2> lower_bound{{-1.2, -2.0}};\n  const std::array<double, 2> midpoint{{-0.6, 0.2}};\n  const std::array<double, 2> upper_bound{{0.8, 3.0}};\n  const OrientationMap<2> half_turn{std::array<Direction<2>, 2>{\n      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}};\n  const OrientationMap<2> quarter_turn_cw{std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}};\n  const OrientationMap<2> quarter_turn_ccw{std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}};\n\n  const creators::RotatedRectangles rotated_rectangles{\n      lower_bound,\n      midpoint,\n      upper_bound,\n      {{refinement_level[0][0], refinement_level[0][1]}},\n      {{{{grid_points[0][0], grid_points[1][0]}},\n        {{grid_points[0][1], grid_points[2][0]}}}},\n      {{false, false}},\n      nullptr};\n  test_rotated_rectangles_construction(\n      rotated_rectangles, lower_bound, midpoint, upper_bound, grid_points,\n      refinement_level,\n      std::vector<DirectionMap<2, BlockNeighbors<2>>>{\n          {{Direction<2>::upper_xi(), {1, half_turn}},\n           {Direction<2>::upper_eta(), {2, quarter_turn_ccw}}},\n          {{Direction<2>::upper_xi(), {0, half_turn}},\n           {Direction<2>::lower_eta(), {3, quarter_turn_ccw}}},\n          {{Direction<2>::lower_xi(), {0, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {3, half_turn}}},\n          {{Direction<2>::upper_xi(), {1, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {2, half_turn}}}},\n      std::vector<std::unordered_set<Direction<2>>>{\n          {Direction<2>::lower_xi(), Direction<2>::lower_eta()},\n          {Direction<2>::lower_xi(), Direction<2>::upper_eta()},\n          {Direction<2>::upper_xi(), Direction<2>::upper_eta()},\n          {Direction<2>::lower_xi(), Direction<2>::upper_eta()}},\n      std::tuple<>{}, {});\n\n  const creators::RotatedRectangles rotated_rectangles_boundary_conditions{\n      lower_bound,\n      midpoint,\n      upper_bound,\n      {{refinement_level[0][0], refinement_level[0][1]}},\n      {{{{grid_points[0][0], grid_points[1][0]}},\n        {{grid_points[0][1], grid_points[2][0]}}}},\n      std::make_unique<\n          TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<2>>(\n          Direction<2>::lower_xi(), 2),\n      nullptr};\n\n  test_rotated_rectangles_construction(\n      rotated_rectangles_boundary_conditions, lower_bound, midpoint,\n      upper_bound, grid_points, refinement_level,\n      std::vector<DirectionMap<2, BlockNeighbors<2>>>{\n          {{Direction<2>::upper_xi(), {1, half_turn}},\n           {Direction<2>::upper_eta(), {2, quarter_turn_ccw}}},\n          {{Direction<2>::upper_xi(), {0, half_turn}},\n           {Direction<2>::lower_eta(), {3, quarter_turn_ccw}}},\n          {{Direction<2>::lower_xi(), {0, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {3, half_turn}}},\n          {{Direction<2>::upper_xi(), {1, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {2, half_turn}}}},\n      std::vector<std::unordered_set<Direction<2>>>{\n          {Direction<2>::lower_xi(), Direction<2>::lower_eta()},\n          {Direction<2>::lower_xi(), Direction<2>::upper_eta()},\n          {Direction<2>::upper_xi(), Direction<2>::upper_eta()},\n          {Direction<2>::lower_xi(), Direction<2>::upper_eta()}},\n      std::tuple<>{}, {}, true);\n\n  const creators::RotatedRectangles rotated_periodic_rectangles{\n      lower_bound,\n      midpoint,\n      upper_bound,\n      {{refinement_level[0][0], refinement_level[0][1]}},\n      {{{{grid_points[0][0], grid_points[1][0]}},\n        {{grid_points[0][1], grid_points[2][0]}}}},\n      {{true, true}},\n      nullptr};\n  test_rotated_rectangles_construction(\n      rotated_periodic_rectangles, lower_bound, midpoint, upper_bound,\n      grid_points, refinement_level,\n      std::vector<DirectionMap<2, BlockNeighbors<2>>>{\n          {{Direction<2>::upper_xi(), {1, half_turn}},\n           {Direction<2>::upper_eta(), {2, quarter_turn_ccw}},\n           {Direction<2>::lower_xi(), {1, half_turn}},\n           {Direction<2>::lower_eta(), {2, quarter_turn_ccw}}},\n          {{Direction<2>::upper_xi(), {0, half_turn}},\n           {Direction<2>::lower_eta(), {3, quarter_turn_ccw}},\n           {Direction<2>::lower_xi(), {0, half_turn}},\n           {Direction<2>::upper_eta(), {3, quarter_turn_ccw}}},\n          {{Direction<2>::lower_xi(), {0, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {3, half_turn}},\n           {Direction<2>::upper_xi(), {0, quarter_turn_cw}},\n           {Direction<2>::upper_eta(), {3, half_turn}}},\n          {{Direction<2>::upper_xi(), {1, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {2, half_turn}},\n           {Direction<2>::lower_xi(), {1, quarter_turn_cw}},\n           {Direction<2>::upper_eta(), {2, half_turn}}}},\n      std::vector<std::unordered_set<Direction<2>>>{{}, {}, {}, {}},\n      std::tuple<>{}, {}, false, true);\n\n  const creators::RotatedRectangles\n      periodic_rotated_rectangles_boundary_conditions{\n          lower_bound,\n          midpoint,\n          upper_bound,\n          {{refinement_level[0][0], refinement_level[0][1]}},\n          {{{{grid_points[0][0], grid_points[1][0]}},\n            {{grid_points[0][1], grid_points[2][0]}}}},\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<2>>(),\n          nullptr};\n  test_rotated_rectangles_construction(\n      periodic_rotated_rectangles_boundary_conditions, lower_bound, midpoint,\n      upper_bound, grid_points, refinement_level,\n      std::vector<DirectionMap<2, BlockNeighbors<2>>>{\n          {{Direction<2>::upper_xi(), {1, half_turn}},\n           {Direction<2>::upper_eta(), {2, quarter_turn_ccw}},\n           {Direction<2>::lower_xi(), {1, half_turn}},\n           {Direction<2>::lower_eta(), {2, quarter_turn_ccw}}},\n          {{Direction<2>::upper_xi(), {0, half_turn}},\n           {Direction<2>::lower_eta(), {3, quarter_turn_ccw}},\n           {Direction<2>::lower_xi(), {0, half_turn}},\n           {Direction<2>::upper_eta(), {3, quarter_turn_ccw}}},\n          {{Direction<2>::lower_xi(), {0, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {3, half_turn}},\n           {Direction<2>::upper_xi(), {0, quarter_turn_cw}},\n           {Direction<2>::upper_eta(), {3, half_turn}}},\n          {{Direction<2>::upper_xi(), {1, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {2, half_turn}},\n           {Direction<2>::lower_xi(), {1, quarter_turn_cw}},\n           {Direction<2>::upper_eta(), {2, half_turn}}}},\n      std::vector<std::unordered_set<Direction<2>>>{{}, {}, {}, {}},\n      std::tuple<>{}, {}, true, true);\n\n  CHECK_THROWS_WITH(\n      creators::RotatedRectangles(\n          lower_bound, midpoint, upper_bound,\n          {{refinement_level[0][0], refinement_level[0][1]}},\n          {{{{grid_points[0][0], grid_points[1][0]}},\n            {{grid_points[0][1], grid_points[2][0]}}}},\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<2>>(),\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n}\n\nvoid test_rotated_rectangles_factory() {\n  INFO(\"Rotated rectangles factory\");\n  domain::creators::time_dependence::register_derived_with_charm();\n  const OrientationMap<2> half_turn{std::array<Direction<2>, 2>{\n      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}};\n  const OrientationMap<2> quarter_turn_cw{std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}};\n  const OrientationMap<2> quarter_turn_ccw{std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}};\n  const std::vector<DirectionMap<2, BlockNeighbors<2>>>\n      expected_block_neighbors_nonperiodic{\n          {{Direction<2>::upper_xi(), {1, half_turn}},\n           {Direction<2>::upper_eta(), {2, quarter_turn_ccw}}},\n          {{Direction<2>::upper_xi(), {0, half_turn}},\n           {Direction<2>::lower_eta(), {3, quarter_turn_ccw}}},\n          {{Direction<2>::lower_xi(), {0, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {3, half_turn}}},\n          {{Direction<2>::upper_xi(), {1, quarter_turn_cw}},\n           {Direction<2>::lower_eta(), {2, half_turn}}}};\n  const std::vector<std::unordered_set<Direction<2>>>\n      expected_external_boundaries_nonperiodic{\n          {Direction<2>::lower_xi(), Direction<2>::lower_eta()},\n          {Direction<2>::lower_xi(), Direction<2>::upper_eta()},\n          {Direction<2>::upper_xi(), Direction<2>::upper_eta()},\n          {Direction<2>::lower_xi(), Direction<2>::upper_eta()}};\n\n  {\n    INFO(\"No boundary condition, time independent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<2>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<\n                2, domain::creators::RotatedRectangles>>(\n        \"RotatedRectangles:\\n\"\n        \"  LowerBound: [0.1, -0.4]\\n\"\n        \"  Midpoint:   [2.6, 3.2]\\n\"\n        \"  UpperBound: [5.1, 6.2]\\n\"\n        \"  IsPeriodicIn: [false, false]\\n\"\n        \"  InitialGridPoints: [[3,2],[1,4]]\\n\"\n        \"  InitialRefinement: [2,1]\\n\"\n        \"  TimeDependence: None\\n\");\n    const auto* rotated_rectangles_creator =\n        dynamic_cast<const creators::RotatedRectangles*>(domain_creator.get());\n    test_rotated_rectangles_construction(\n        *rotated_rectangles_creator, {{0.1, -0.4}}, {{2.6, 3.2}}, {{5.1, 6.2}},\n        {{{3, 1}}, {{2, 1}}, {{4, 3}}, {{4, 2}}},\n        {{{2, 1}}, {{2, 1}}, {{1, 2}}, {{1, 2}}},\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic, std::tuple<>{}, {});\n  }\n  {\n    INFO(\"With boundary condition, time independent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<2>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                2, domain::creators::RotatedRectangles>>(\n        \"RotatedRectangles:\\n\"\n        \"  LowerBound: [0.1, -0.4]\\n\"\n        \"  Midpoint:   [2.6, 3.2]\\n\"\n        \"  UpperBound: [5.1, 6.2]\\n\"\n        \"  InitialGridPoints: [[3,2],[1,4]]\\n\"\n        \"  InitialRefinement: [2,1]\\n\"\n        \"  BoundaryCondition:\\n\"\n        \"    TestBoundaryCondition:\\n\"\n        \"      Direction: lower-xi\\n\"\n        \"      BlockId: 2\\n\"\n        \"  TimeDependence: None\\n\");\n    const auto* rotated_rectangles_creator =\n        dynamic_cast<const creators::RotatedRectangles*>(domain_creator.get());\n    test_rotated_rectangles_construction(\n        *rotated_rectangles_creator, {{0.1, -0.4}}, {{2.6, 3.2}}, {{5.1, 6.2}},\n        {{{3, 1}}, {{2, 1}}, {{4, 3}}, {{4, 2}}},\n        {{{2, 1}}, {{2, 1}}, {{1, 2}}, {{1, 2}}},\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic, std::tuple<>{}, {}, true);\n  }\n  {\n    INFO(\"No boundary condition, time dependent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<2>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithoutBoundaryConditions<\n                2, domain::creators::RotatedRectangles>>(\n        \"RotatedRectangles:\\n\"\n        \"  LowerBound: [0.1, -0.4]\\n\"\n        \"  Midpoint:   [2.6, 3.2]\\n\"\n        \"  UpperBound: [5.1, 6.2]\\n\"\n        \"  IsPeriodicIn: [false, false]\\n\"\n        \"  InitialGridPoints: [[3,2],[1,4]]\\n\"\n        \"  InitialRefinement: [2,1]\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3, 0.5]\\n\");\n    const auto* rotated_rectangles_creator =\n        dynamic_cast<const creators::RotatedRectangles*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, 0.5}};\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_rotated_rectangles_construction(\n        *rotated_rectangles_creator, {{0.1, -0.4}}, {{2.6, 3.2}}, {{5.1, 6.2}},\n        {{{3, 1}}, {{2, 1}}, {{4, 3}}, {{4, 2}}},\n        {{{2, 1}}, {{2, 1}}, {{1, 2}}, {{1, 2}}},\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{0.0, 0.0}, velocity, {0.0, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name}));\n    // with expiration times\n    test_rotated_rectangles_construction(\n        *rotated_rectangles_creator, {{0.1, -0.4}}, {{2.6, 3.2}}, {{5.1, 6.2}},\n        {{{3, 1}}, {{2, 1}}, {{4, 3}}, {{4, 2}}},\n        {{{2, 1}}, {{2, 1}}, {{1, 2}}, {{1, 2}}},\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{0.0, 0.0}, velocity, {0.0, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name}),\n        false, false, initial_expiration_times);\n  }\n  {\n    INFO(\"With boundary condition, time dependent\");\n    const auto domain_creator = TestHelpers::test_option_tag<\n        domain::OptionTags::DomainCreator<2>,\n        TestHelpers::domain::BoundaryConditions::\n            MetavariablesWithBoundaryConditions<\n                2, domain::creators::RotatedRectangles>>(\n        \"RotatedRectangles:\\n\"\n        \"  LowerBound: [0.1, -0.4]\\n\"\n        \"  Midpoint:   [2.6, 3.2]\\n\"\n        \"  UpperBound: [5.1, 6.2]\\n\"\n        \"  InitialGridPoints: [[3,2],[1,4]]\\n\"\n        \"  InitialRefinement: [2,1]\\n\"\n        \"  BoundaryCondition:\\n\"\n        \"    TestBoundaryCondition:\\n\"\n        \"      Direction: lower-xi\\n\"\n        \"      BlockId: 2\\n\"\n        \"  TimeDependence:\\n\"\n        \"    UniformTranslation:\\n\"\n        \"      InitialTime: 1.0\\n\"\n        \"      Velocity: [2.3, 0.5]\\n\");\n    const auto* rotated_rectangles_creator =\n        dynamic_cast<const creators::RotatedRectangles*>(domain_creator.get());\n    const double initial_time = 1.0;\n    const DataVector velocity{{2.3, 0.5}};\n    const std::string f_of_t_name = \"Translation\";\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    initial_expiration_times[f_of_t_name] = 10.0;\n    // without expiration times\n    test_rotated_rectangles_construction(\n        *rotated_rectangles_creator, {{0.1, -0.4}}, {{2.6, 3.2}}, {{5.1, 6.2}},\n        {{{3, 1}}, {{2, 1}}, {{4, 3}}, {{4, 2}}},\n        {{{2, 1}}, {{2, 1}}, {{1, 2}}, {{1, 2}}},\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{0.0, 0.0}, velocity, {0.0, 0.0}}},\n                 std::numeric_limits<double>::infinity()}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name}),\n        true);\n    // with expiration times\n    test_rotated_rectangles_construction(\n        *rotated_rectangles_creator, {{0.1, -0.4}}, {{2.6, 3.2}}, {{5.1, 6.2}},\n        {{{3, 1}}, {{2, 1}}, {{4, 3}}, {{4, 2}}},\n        {{{2, 1}}, {{2, 1}}, {{1, 2}}, {{1, 2}}},\n        expected_block_neighbors_nonperiodic,\n        expected_external_boundaries_nonperiodic,\n        std::make_tuple(\n            std::pair<std::string,\n                      domain::FunctionsOfTime::PiecewisePolynomial<2>>{\n                f_of_t_name,\n                {initial_time,\n                 std::array<DataVector, 3>{{{0.0, 0.0}, velocity, {0.0, 0.0}}},\n                 initial_expiration_times[f_of_t_name]}}),\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name},\n            CoordinateMaps::TimeDependent::Translation<2>{f_of_t_name}),\n        true, false, initial_expiration_times);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.RotatedRectangles\", \"[Domain][Unit]\") {\n  test_rotated_rectangles();\n  test_rotated_rectangles_factory();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_Sphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/BulgedCube.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace domain {\nnamespace {\nusing Translation3D = CoordinateMaps::TimeDependent::Translation<3>;\nusing Interior =\n    std::variant<creators::Sphere::Excision, creators::Sphere::InnerCube>;\n\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition(const bool outer) {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      outer ? Direction<3>::upper_zeta() : Direction<3>::lower_zeta(), 50);\n}\n\nInterior copy_interior(const Interior& interior,\n                       const bool with_boundary_conditions) {\n  if (std::holds_alternative<creators::Sphere::InnerCube>(interior)) {\n    return std::get<creators::Sphere::InnerCube>(interior);\n  } else {\n    return creators::Sphere::Excision{\n        with_boundary_conditions ? create_boundary_condition(false) : nullptr};\n  }\n}\n\nstd::string stringize(const bool t) { return t ? \"true\" : \"false\"; }\n\ntemplate <typename T>\nstd::string stringize(const std::vector<T>& t) {\n  std::string result = \"[\";\n  bool first = true;\n  for (const auto& item : t) {\n    if (not first) {\n      result += \", \";\n    }\n    result += get_output(item);\n    first = false;\n  }\n  result += \"]\";\n  return result;\n}\n\nstd::string option_string(\n    const double inner_radius, const double outer_radius,\n    const Interior& interior, const size_t initial_refinement,\n    const std::array<size_t, 3> initial_extents, const bool equiangular,\n    const std::optional<creators::Sphere::EquatorialCompressionOptions>&\n        equatorial_compression,\n    const std::vector<double>& radial_partitioning,\n    const std::vector<CoordinateMaps::Distribution>& radial_distribution,\n    const ShellWedges which_wedges, const bool time_dependent,\n    const bool hard_coded_time_dependent_maps,\n    const bool with_boundary_conditions) {\n  const std::string interior_option =\n      [&interior, &with_boundary_conditions]() -> std::string {\n    if (std::holds_alternative<creators::Sphere::Excision>(interior)) {\n      if (with_boundary_conditions) {\n        return \"  Interior:\\n\"\n               \"    ExciseWithBoundaryCondition:\\n\"\n               \"      TestBoundaryCondition:\\n\"\n               \"        Direction: lower-zeta\\n\"\n               \"        BlockId: 50\\n\";\n      } else {\n        return \"  Interior: Excise\\n\";\n      }\n    } else {\n      const double sphericity =\n          std::get<creators::Sphere::InnerCube>(interior).sphericity;\n      return \"  Interior:\\n\"\n             \"    FillWithSphericity: \" +\n             std::to_string(sphericity) + \"\\n\";\n    }\n  }();\n  const std::string equatorial_compression_option =\n      equatorial_compression.has_value()\n          ? \"  EquatorialCompression:\\n\"\n            \"    AspectRatio: \" +\n                std::to_string(equatorial_compression->aspect_ratio) +\n                \"\\n\"\n                \"    IndexPolarAxis: \" +\n                std::to_string(equatorial_compression->index_polar_axis) + \"\\n\"\n          : \"  EquatorialCompression: None\\n\";\n  const std::string time_dependent_option =\n      time_dependent ? (hard_coded_time_dependent_maps\n                            ? \"  TimeDependentMaps:\\n\"\n                              \"    InitialTime: 1.0\\n\"\n                              \"    ShapeMap:\\n\"\n                              \"      LMax: 10\\n\"\n                              \"      CoefficientTruncationLimit: 0.\\n\"\n                              \"      InitialValues: Spherical\\n\"\n                              \"      SizeInitialValues: Auto\\n\"\n                              \"    RotationMap: None\\n\"\n                              \"    ExpansionMap: None\\n\"\n                              \"    TranslationMap:\\n\"\n                              \"      InitialValues: [[0.0, 0.0, 0.0],\"\n                              \" [0.001, -0.003, 0.005], [0.0, 0.0, 0.0]]\\n\"\n                              \"    TransitionRotScaleTrans: False\\n\"\n                            : \"  TimeDependentMaps:\\n\"\n                              \"    UniformTranslation:\\n\"\n                              \"      InitialTime: 1.0\\n\"\n                              \"      Velocity: [2.3, -0.3, 0.5]\\n\")\n                     : \"  TimeDependentMaps: None\\n\";\n  const std::string outer_bc_option = with_boundary_conditions\n                                          ? \"  OuterBoundaryCondition:\\n\"\n                                            \"    TestBoundaryCondition:\\n\"\n                                            \"      Direction: upper-zeta\\n\"\n                                            \"      BlockId: 50\\n\"\n                                          : \"\";\n  return \"Sphere:\\n\"\n         \"  InnerRadius: \" +\n         std::to_string(inner_radius) +\n         \"\\n\"\n         \"  OuterRadius: \" +\n         std::to_string(outer_radius) + \"\\n\" + interior_option +\n         \"  InitialRefinement: \" + std::to_string(initial_refinement) +\n         \"\\n\"\n         \"  InitialGridPoints: [\" +\n         std::to_string(initial_extents[0]) + \", \" +\n         std::to_string(initial_extents[1]) + \", \" +\n         std::to_string(initial_extents[2]) +\n         \"]\\n\"\n         \"  UseEquiangularMap: \" +\n         stringize(equiangular) + \"\\n\" + equatorial_compression_option +\n         \"  WhichWedges: \" + get_output(which_wedges) +\n         \"\\n\"\n         \"  RadialPartitioning: \" +\n         stringize(radial_partitioning) +\n         \"\\n\"\n         \"  RadialDistribution: \" +\n         (radial_distribution.size() == 1 ? get_output(radial_distribution[0])\n                                          : stringize(radial_distribution)) +\n         \"\\n\" + time_dependent_option + outer_bc_option;\n}\n\n// Calculate block logical coordinates of points residing on corners of the\n// inner cube or on radial block faces of wedges. The radial direction in 3D\n// wedges is the positive zeta direction. These coordinates will be used to\n// ensure the points lie on concentric spheres defined by either the inner\n// sphere, outer sphere, or radial partition parameters.\ntnsr::I<double, 3, Frame::ElementLogical> logical_coords(\n    const gsl::not_null<std::mt19937*> gen, const bool is_inner_cube,\n    const bool abuts_inner_cube) {\n  std::uniform_real_distribution<> real_dis(-1, 1);\n\n  const double rand_int_xi = (2.0 * (rand() % 2) - 1.0);\n  const double rand_int_eta = (2.0 * (rand() % 2) - 1.0);\n  const double rand_int_zeta = (2.0 * (rand() % 2) - 1.0);\n  const double rand_real_xi = real_dis(*gen);\n  const double rand_real_eta = real_dis(*gen);\n\n  double xi_logical_coord;\n  double eta_logical_coord;\n  // enforce coordinates either fall on the lower or\n  // upper zeta face of wedges\n  const double zeta_logical_coord = rand_int_zeta;\n\n  if (is_inner_cube) {\n    // inner cube only uses integer corners\n    xi_logical_coord = rand_int_xi;\n    eta_logical_coord = rand_int_eta;\n\n  } else if (abuts_inner_cube) {\n    // next to inner cube,\n    // corners only on lower face b/c of square inner cube neighbor\n    // face\n    xi_logical_coord = rand_int_xi;\n    eta_logical_coord = rand_int_eta;\n\n    // anywhere on upper zeta face b/c adjacent with spherical wedge\n    if (rand_int_zeta == 1) {\n      xi_logical_coord = rand_real_xi;\n      eta_logical_coord = rand_real_eta;\n    }\n  } else {\n    // adjacent to wedges\n    // everywhere on low or high face should lie on a sphere b/c\n    // neighbor with spherical wedge\n    xi_logical_coord = rand_real_xi;\n    eta_logical_coord = rand_real_eta;\n  }\n\n  return tnsr::I<double, 3, Frame::ElementLogical>{\n      {{xi_logical_coord, eta_logical_coord, zeta_logical_coord}}};\n}\n\ntemplate <typename Generator>\nvoid test_sphere_construction(\n    const gsl::not_null<Generator*> gen, const creators::Sphere& sphere,\n    const double inner_radius, const double outer_radius,\n    const bool fill_interior,\n    const std::vector<double> radial_partitioning = {},\n    const ShellWedges which_wedges = ShellWedges::All,\n    const bool expect_boundary_conditions = true,\n    const std::vector<double>& times = {0.},\n    const std::array<double, 3>& velocity = {{0., 0., 0.}}) {\n  // check consistency of domain\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      sphere, expect_boundary_conditions, false, times);\n  const auto& grid_anchors = sphere.grid_anchors();\n  CHECK(grid_anchors.size() == 1);\n  CHECK(grid_anchors.count(\"Center\") == 1);\n  CHECK(grid_anchors.at(\"Center\") ==\n        tnsr::I<double, 3, Frame::Grid>{std::array{0.0, 0.0, 0.0}});\n\n  const auto& blocks = domain.blocks();\n  const auto block_names = sphere.block_names();\n  const size_t num_blocks = blocks.size();\n  const size_t num_blocks_per_shell =\n      which_wedges == ShellWedges::All             ? 6\n      : which_wedges == ShellWedges::FourOnEquator ? 4\n                                                   : 1;\n  CAPTURE(num_blocks);\n  CAPTURE(num_blocks_per_shell);\n  const auto all_boundary_conditions = sphere.external_boundary_conditions();\n  const auto functions_of_time = sphere.functions_of_time();\n\n  // construct vector of inner radius, outer radius, and refinements levels\n  // where inertial block corners have to be located\n  std::vector<double> expected_corner_radii = radial_partitioning;\n  expected_corner_radii.insert(expected_corner_radii.begin(), inner_radius);\n  expected_corner_radii.emplace_back(outer_radius);\n\n  // Check total number of external boundaries\n  const size_t num_shells = radial_partitioning.size() + 1;\n  const size_t num_external_boundaries =\n      alg::accumulate(blocks, 0_st, [](const size_t count, const auto& block) {\n        return count + block.external_boundaries().size();\n      });\n  if (which_wedges == ShellWedges::All) {\n    CHECK(num_external_boundaries == (fill_interior ? 6 : 12));\n  } else if (which_wedges == ShellWedges::FourOnEquator) {\n    CHECK(num_external_boundaries ==\n          ((fill_interior ? 2 : 4) + 4 * (1 + num_shells * 2)));\n  } else if (which_wedges == ShellWedges::OneAlongMinusX) {\n    CHECK(num_external_boundaries ==\n          ((fill_interior ? 5 : 1) + 1 + num_shells * 4));\n  }\n\n  // verify if adjacent to inner cube\n  const auto abuts_inner_cube =\n      [&num_blocks](const auto& direction_and_neighbor) {\n        return *direction_and_neighbor.second.begin() == num_blocks - 1;\n      };\n\n  for (size_t block_id = 0; block_id < num_blocks; ++block_id) {\n    CAPTURE(block_id);\n    const auto& block = blocks[block_id];\n    const bool is_inner_cube = fill_interior and block_id == num_blocks - 1;\n    const ElementMap<3, Frame::Inertial> element_map{ElementId<3>{block_id},\n                                                     block};\n    {\n      INFO(\"Block boundaries are spherical\");\n      // This section tests if the logical coordinates of corners from all\n      // blocks (and points on upper wedge faces) lie on spherical shells\n      // specified by inner radius, radial partitions, or outer radius\n      //\n      // First, get the element-logical coordinates of a random block corner\n      const auto logical_block_corner = logical_coords(\n          gen, is_inner_cube,\n          fill_interior and alg::any_of(block.neighbors(), abuts_inner_cube));\n      for (const double current_time : times) {\n        CAPTURE(current_time);\n        // Map the logical block corner through the domain and undo the\n        // translation to get its distance from the center\n        auto inertial_block_corner =\n            element_map(logical_block_corner, current_time, functions_of_time);\n        const double delta_t = current_time - 1.0;\n        for (size_t i = 0; i < 3; ++i) {\n          inertial_block_corner.get(i) -= gsl::at(velocity, i) * delta_t;\n        }\n        const double corner_distance_from_origin =\n            get(magnitude(inertial_block_corner));\n        CAPTURE(corner_distance_from_origin);\n        CAPTURE(expected_corner_radii);\n        const auto match_demarcation =\n            [&corner_distance_from_origin](const double radius) {\n              return corner_distance_from_origin == approx(radius);\n            };\n        CHECK(alg::any_of(expected_corner_radii, match_demarcation));\n      }\n    }\n\n    if (which_wedges == ShellWedges::All) {\n      INFO(\"External boundaries\");\n      const auto& external_boundaries = block.external_boundaries();\n      CAPTURE(external_boundaries);\n      if (is_inner_cube) {\n        // Inner cube cannot have external boundaries\n        CHECK(external_boundaries.empty());\n      } else {\n        // Wedges can have 0, 1, or 2 external boundaries\n        std::unordered_set<size_t> allowed_num_external_boundaries{};\n        if (fill_interior) {\n          allowed_num_external_boundaries.insert(1);\n        } else {\n          allowed_num_external_boundaries.insert(2);\n        }\n        if (not radial_partitioning.empty()) {\n          allowed_num_external_boundaries.insert(0);\n          allowed_num_external_boundaries.insert(1);\n        }\n        CHECK(allowed_num_external_boundaries.count(\n                  external_boundaries.size()) == 1);\n      }\n      // All external boundaries must be radial\n      for (const Direction<3>& direction : external_boundaries) {\n        CAPTURE(direction);\n        if (fill_interior) {\n          // Stronger condition for filled sphere: all external boundaries\n          // must be upper zeta\n          CHECK(direction == Direction<3>::upper_zeta());\n        } else {\n          CHECK(direction.axis() == Direction<3>::Axis::Zeta);\n        }\n      }\n      // All angular neighbors must have the same external boundaries\n      if (not is_inner_cube) {\n        for (const auto& [direction, neighbor_id] : block.neighbors()) {\n          CAPTURE(direction);\n          if (direction.axis() != Direction<3>::Axis::Zeta) {\n            CHECK(blocks[*neighbor_id.begin()].external_boundaries() ==\n                  external_boundaries);\n          }\n        }\n      }\n    }\n\n    if (expect_boundary_conditions) {\n      INFO(\"Boundary conditions\");\n      const auto& boundary_conditions = all_boundary_conditions[block_id];\n      for (const auto& direction : block.external_boundaries()) {\n        CAPTURE(direction);\n        const auto& boundary_condition =\n            dynamic_cast<const TestHelpers::domain::BoundaryConditions::\n                             TestBoundaryCondition<3>&>(\n                *boundary_conditions.at(direction));\n        CHECK(boundary_condition.direction() == direction);\n      }\n    }\n  }  // block loop\n}  // test_sphere_construction()\n\n// ensure CHECK_THROWS_WITH calls are properly captured\nvoid test_parse_errors() {\n  INFO(\"Sphere check throws\");\n  const double inner_radius = 1.0;\n  const double outer_radius = 2.0;\n  const creators::Sphere::InnerCube inner_cube{0.0};\n  const size_t refinement = 2;\n  const std::array<size_t, 3> initial_extents{{4, 5, 6}};\n  const bool use_equiangular_map = true;\n  const std::optional<creators::Sphere::EquatorialCompressionOptions>\n      equatorial_compression = std::nullopt;\n  const std::vector<double> radial_partitioning = {};\n  const std::vector<double> radial_partitioning_unordered = {\n      {1.5 * inner_radius, 1.1 * inner_radius}};\n  const std::vector<double> radial_partitioning_low = {\n      {0.5 * inner_radius, 1.1 * inner_radius}};\n  const std::vector<double> radial_partitioning_high = {\n      {2.1 * outer_radius, 2.2 * outer_radius}};\n  const std::vector<domain::CoordinateMaps::Distribution> radial_distribution{\n      domain::CoordinateMaps::Distribution::Linear};\n  const std::vector<domain::CoordinateMaps::Distribution>\n      radial_distribution_too_many{\n          domain::CoordinateMaps::Distribution::Linear,\n          domain::CoordinateMaps::Distribution::Logarithmic};\n  const std::vector<domain::CoordinateMaps::Distribution>\n      radial_distribution_inner_log{\n          domain::CoordinateMaps::Distribution::Logarithmic};\n  const ShellWedges which_wedges = ShellWedges::All;\n\n  CHECK_THROWS_WITH(\n      creators::Sphere(inner_radius, 0.5 * inner_radius, inner_cube, refinement,\n                       initial_extents, use_equiangular_map,\n                       equatorial_compression, radial_partitioning,\n                       radial_distribution, which_wedges, std::nullopt, nullptr,\n                       Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Inner radius must be smaller than outer radius\"));\n\n  CHECK_THROWS_WITH(\n      creators::Sphere(inner_radius, outer_radius, inner_cube, refinement,\n                       initial_extents, use_equiangular_map,\n                       equatorial_compression, radial_partitioning_unordered,\n                       radial_distribution, which_wedges, std::nullopt, nullptr,\n                       Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify radial partitioning in ascending order.\"));\n\n  CHECK_THROWS_WITH(\n      creators::Sphere(inner_radius, outer_radius, inner_cube, refinement,\n                       initial_extents, use_equiangular_map,\n                       equatorial_compression, radial_partitioning_low,\n                       radial_distribution, which_wedges, std::nullopt, nullptr,\n                       Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"First radial partition must be larger than the inner\"));\n  CHECK_THROWS_WITH(\n      creators::Sphere(inner_radius, outer_radius, inner_cube, refinement,\n                       initial_extents, use_equiangular_map,\n                       equatorial_compression, radial_partitioning_high,\n                       radial_distribution, which_wedges, std::nullopt, nullptr,\n                       Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Last radial partition must be smaller than the outer\"));\n  CHECK_THROWS_WITH(\n      creators::Sphere(inner_radius, outer_radius, inner_cube, refinement,\n                       initial_extents, use_equiangular_map,\n                       equatorial_compression, radial_partitioning,\n                       radial_distribution_too_many, which_wedges, std::nullopt,\n                       nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify a 'RadialDistribution' for every spherical shell. You\"));\n  CHECK_THROWS_WITH(\n      creators::Sphere(\n          inner_radius, outer_radius, inner_cube, refinement, initial_extents,\n          use_equiangular_map, equatorial_compression, radial_partitioning,\n          radial_distribution_inner_log, which_wedges, std::nullopt, nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The 'RadialDistribution' must be 'Linear' for the\"));\n\n  CHECK_THROWS_WITH(\n      creators::Sphere(\n          inner_radius, outer_radius, inner_cube, refinement, initial_extents,\n          use_equiangular_map, equatorial_compression, radial_partitioning,\n          radial_distribution, which_wedges, std::nullopt,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions with a Sphere\"));\n  CHECK_THROWS_WITH(\n      creators::Sphere(\n          inner_radius, outer_radius, inner_cube, refinement, initial_extents,\n          use_equiangular_map, equatorial_compression, radial_partitioning,\n          radial_distribution, which_wedges, std::nullopt,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n}\n\ntemplate <typename Generator>\nvoid test_sphere(const gsl::not_null<Generator*> gen) {\n  const double inner_radius = 1.0;\n  const double outer_radius = 2.0;\n  const size_t initial_refinement = 3;\n  const std::array<size_t, 3> initial_extents{{4, 5, 6}};\n\n  const std::array<\n      std::variant<creators::Sphere::Excision, creators::Sphere::InnerCube>, 3>\n      interiors{{creators::Sphere::InnerCube{0.0},\n                 creators::Sphere::InnerCube{0.7},\n                 creators::Sphere::Excision{}}};\n  const std::array<\n      std::optional<creators::Sphere::EquatorialCompressionOptions>, 2>\n      equatorial_compressions{\n          {std::nullopt,\n           creators::Sphere::EquatorialCompressionOptions{0.5, 2}}};\n  const double outer_minus_inner = outer_radius - inner_radius;\n  const std::array<std::vector<double>, 3> radial_partitioning{\n      {{},\n       {0.5 * (inner_radius + outer_radius)},\n       {inner_radius + 0.3 * outer_minus_inner,\n        inner_radius + 0.6 * outer_minus_inner}}};\n  const std::array<std::vector<domain::CoordinateMaps::Distribution>, 3>\n      radial_distribution{{{domain::CoordinateMaps::Distribution::Linear},\n                           {domain::CoordinateMaps::Distribution::Linear,\n                            domain::CoordinateMaps::Distribution::Logarithmic},\n                           {domain::CoordinateMaps::Distribution::Linear}}};\n\n  const std::array<double, 3> velocity{{2.3, -0.3, 0.5}};\n  const size_t l_max = 10;\n  const std::vector<double> times{1., 10.};\n\n  for (auto [interior_index, equiangular, equatorial_compression, index,\n             which_wedges, time_dependent, use_hard_coded_time_dep_options,\n             with_boundary_conditions] :\n       random_sample<5>(\n           cartesian_product(\n               make_array(0_st, 1_st, 2_st), make_array(false, true),\n               equatorial_compressions, make_array(0_st, 1_st, 2_st),\n               make_array(ShellWedges::All, ShellWedges::FourOnEquator,\n                          ShellWedges::OneAlongMinusX),\n               make_array(true, false), make_array(true, false),\n               make_array(true, false)),\n           gen)) {\n    const auto& interior = interiors[interior_index];\n    const bool fill_interior =\n        std::holds_alternative<creators::Sphere::InnerCube>(interior);\n    if (equatorial_compression.has_value() and fill_interior) {\n      continue;\n    }\n    CAPTURE(fill_interior);\n    CAPTURE(equiangular);\n    CAPTURE(equatorial_compression.has_value());  // Whether map is present.\n    CAPTURE(which_wedges);\n    CAPTURE(time_dependent);\n    CAPTURE(with_boundary_conditions);\n    // If we aren't time dependent, just set the hard coded option to false to\n    // avoid ambiguity\n    if (not time_dependent) {\n      use_hard_coded_time_dep_options = false;\n    }\n    CAPTURE(use_hard_coded_time_dep_options);\n\n    // If we are using hard coded maps, we need at least two shells (or one\n    // radial partition) for the translation map.\n    auto array_index = (use_hard_coded_time_dep_options and\n                                gsl::at(radial_partitioning, index).empty()\n                            ? index + 1\n                            : index);\n    CAPTURE(gsl::at(radial_partitioning, array_index));\n    CAPTURE(gsl::at(radial_distribution, array_index));\n\n    if (which_wedges != ShellWedges::All and with_boundary_conditions) {\n      continue;\n    }\n\n    creators::Sphere::RadialDistribution::type radial_distribution_variant;\n    if (gsl::at(radial_distribution, array_index).size() == 1) {\n      radial_distribution_variant =\n          gsl::at(radial_distribution, array_index)[0];\n    } else {\n      radial_distribution_variant = gsl::at(radial_distribution, array_index);\n    }\n\n    std::optional<creators::Sphere::TimeDepOptionType> time_dependent_options{};\n\n    auto translation_velocity = std::array<double, 3>{{0., 0., 0.}};\n\n    if (time_dependent) {\n      if (use_hard_coded_time_dep_options) {\n        using namespace domain::creators::time_dependent_options;  // NOLINT\n        translation_velocity = std::array<double, 3>{{0.001, -0.003, 0.005}};\n        time_dependent_options = creators::sphere::TimeDependentMapOptions{\n            1.0,\n            ShapeMapOptions<false, domain::ObjectLabel::None>{l_max,\n                                                              std::nullopt},\n            std::nullopt,\n            std::nullopt,\n            TranslationMapOptions<3>{std::array{\n                std::array<double, 3>{0.0, 0.0, 0.0}, translation_velocity,\n                std::array<double, 3>{0.0, 0.0, 0.0}}},\n            false};\n      } else {\n        time_dependent_options = std::make_unique<\n            domain::creators::time_dependence::UniformTranslation<3, 0>>(\n            1.0, velocity);\n        translation_velocity = velocity;\n      }\n    }\n\n    const creators::Sphere sphere{\n        inner_radius,\n        outer_radius,\n        copy_interior(interior, with_boundary_conditions),\n        initial_refinement,\n        initial_extents,\n        equiangular,\n        equatorial_compression,\n        gsl::at(radial_partitioning, array_index),\n        radial_distribution_variant,\n        which_wedges,\n        std::move(time_dependent_options),\n        with_boundary_conditions ? create_boundary_condition(true) : nullptr};\n    test_sphere_construction(\n        gen, sphere, inner_radius, outer_radius, fill_interior,\n        gsl::at(radial_partitioning, array_index), which_wedges,\n        with_boundary_conditions,\n        time_dependent ? times : std::vector<double>{1.}, translation_velocity);\n    TestHelpers::domain::creators::test_creation(\n        option_string(inner_radius, outer_radius, interior, initial_refinement,\n                      initial_extents, equiangular, equatorial_compression,\n                      gsl::at(radial_partitioning, array_index),\n                      gsl::at(radial_distribution, array_index), which_wedges,\n                      time_dependent, use_hard_coded_time_dep_options,\n                      with_boundary_conditions),\n        sphere, with_boundary_conditions);\n  }\n}\n\nvoid test_shape_distortion_general(\n    const double time,\n    domain::creators::Sphere::TimeDepOptionType time_dependent_options,\n    const double deformed_radius, const bool fill_interior,\n    const tnsr::I<DataVector, 3>& x) {\n  using Sphere = domain::creators::Sphere;\n  const Sphere domain_creator{\n      fill_interior ? 0.5 * deformed_radius : deformed_radius,\n      10.,\n      fill_interior ? Interior{Sphere::InnerCube{0.0}}\n                    : Interior{Sphere::Excision{}},\n      0_st,\n      6_st,\n      true,\n      std::nullopt,\n      {fill_interior ? deformed_radius : 4.},\n      domain::CoordinateMaps::Distribution::Linear,\n      ShellWedges::All,\n      std::move(time_dependent_options)};\n  const auto domain = domain_creator.create_domain();\n  const auto functions_of_time = domain_creator.functions_of_time();\n  // Map the coordinates through the domain. They should lie at the lower zeta\n  // boundary of their block.\n  const auto x_logical =\n      block_logical_coordinates(domain, x, time, functions_of_time);\n  for (size_t i = 0; i < get<0>(x).size(); ++i) {\n    CAPTURE(x_logical[i]);\n    REQUIRE(x_logical[i].has_value());\n    CHECK(abs(get<2>(x_logical[i]->data)) == approx(1.));\n  }\n}\n\nvoid test_shape_distortion() {\n  domain::creators::Sphere::TimeDepOptionType time_dependent_options{};\n\n  // Set up theta phis\n  const size_t l_max = 16;\n  const ylm::Spherepack ylm{l_max, l_max};\n  const std::array<DataVector, 2> theta_phi = ylm.theta_phi_points();\n\n  const double time = 0.7;\n  const double mass = 0.8;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.9}};\n  const double r_plus = mass * (1. + sqrt(1. - dot(spin, spin)));\n  const double inner_radius = r_plus;\n\n  const DataVector radius =\n      get(gr::Solutions::kerr_schild_radius_from_boyer_lindquist(\n          inner_radius, theta_phi, mass, spin));\n  // Set up coordinates on an ellipsoid of constant Boyer-Lindquist radius\n  tnsr::I<DataVector, 3> x{};\n  get<0>(x) = radius * sin(get<0>(theta_phi)) * cos(get<1>(theta_phi));\n  get<1>(x) = radius * sin(get<0>(theta_phi)) * sin(get<1>(theta_phi));\n  get<2>(x) = radius * cos(get<0>(theta_phi));\n\n  {\n    INFO(\n        \"Check that inner radius is deformed to constant Boyer-Lindquist \"\n        \"radius\");\n\n    // Time dependence\n    time_dependent_options = std::make_unique<\n        domain::creators::time_dependence::Shape<domain::ObjectLabel::None>>(\n        time, l_max, mass, spin, std::array<double, 3>{{0., 0., 0.}}, 0.0,\n        inner_radius, 4.);\n\n    test_shape_distortion_general(time, std::move(time_dependent_options),\n                                  inner_radius, false, x);\n\n    // KerrSchild-BoyerLindquist. Use same x as above\n    for (const bool fill_interior : {true, false}) {\n      CAPTURE(fill_interior);\n      time_dependent_options =\n          domain::creators::sphere::TimeDependentMapOptions{\n              time,\n              domain::creators::time_dependent_options::ShapeMapOptions<\n                  false, domain::ObjectLabel::None>{\n                  l_max, domain::creators::time_dependent_options::\n                             KerrSchildFromBoyerLindquist{mass, spin}},\n              std::nullopt,\n              std::nullopt,\n              std::nullopt,\n              not fill_interior};\n      test_shape_distortion_general(time, std::move(time_dependent_options),\n                                    inner_radius, fill_interior, x);\n    }\n  }\n\n  {\n    INFO(\"Check reading in Ylms from file\");\n    const std::string h5_filename{\"StrahlkorperCoefsFile.h5\"};\n    const std::string subfile_name{\"Ylm_coefs\"};\n    if (file_system::check_if_file_exists(h5_filename)) {\n      file_system::rm(h5_filename, true);\n    }\n    ylm::Strahlkorper<Frame::Distorted> strahlkorper{l_max, l_max, radius,\n                                                     std::array{0.0, 0.0, 0.0}};\n    std::vector<std::string> legend{};\n    std::vector<double> data{};\n    ylm::fill_ylm_legend_and_data(make_not_null(&legend), make_not_null(&data),\n                                  strahlkorper, time, l_max);\n    {\n      h5::H5File<h5::AccessType::ReadWrite> test_file{h5_filename, true};\n      auto& subfile = test_file.insert<h5::Dat>(\"/\" + subfile_name, legend);\n      subfile.append(data);\n      test_file.close_current_object();\n    }\n\n    time_dependent_options = domain::creators::sphere::TimeDependentMapOptions{\n        time,\n        domain::creators::time_dependent_options::ShapeMapOptions<\n            false, domain::ObjectLabel::None>{\n            l_max,\n            domain::creators::time_dependent_options::YlmsFromFile{\n                h5_filename, std::vector{subfile_name}, time, std::nullopt,\n                false},\n            // Constructing a strahlkorper from collocation radii will not\n            // exactly match the collocation points (see Strahlkorper\n            // constructor docs). For this reason, the 00 coef we calculate is\n            // not exact. This is why we just hard code the proper value here.\n            // If you change l_max, this value must also change.\n            std::array{-4.6442771561420703730e-01, 0.0, 0.0}},\n        std::nullopt,\n        std::nullopt,\n        std::nullopt,\n        true};\n\n    test_shape_distortion_general(time, std::move(time_dependent_options),\n                                  inner_radius, false, x);\n\n    if (file_system::check_if_file_exists(h5_filename)) {\n      file_system::rm(h5_filename, true);\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 15]]\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.Sphere\", \"[Domain][Unit]\") {\n  MAKE_GENERATOR(gen);\n  domain::creators::time_dependence::register_derived_with_charm();\n  test_parse_errors();\n  test_sphere(make_not_null(&gen));\n  test_shape_distortion();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_SphericalShells.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/SphericalShells.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Context.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\nstd::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\ncreate_boundary_condition(const bool outer) {\n  return std::make_unique<\n      TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>>(\n      outer ? Direction<3>::upper_xi() : Direction<3>::lower_xi(), 50);\n}\n\ntemplate <typename T>\nstd::string stringize(const std::vector<T>& t) {\n  std::string result = get_output(t).replace(0, 1, \"[\");\n  return result.replace(result.length() - 1, 1, \"]\");\n}\n\nstd::string option_string(\n    const double inner_radius, const double outer_radius,\n    const size_t radial_refinement, const size_t radial_extents,\n    const size_t spherical_harmonic_l,\n    const std::vector<double>& radial_partitioning,\n    const std::vector<domain::CoordinateMaps::Distribution>&\n        radial_distribution,\n    const bool time_dependent, const bool hard_coded_time_dependent_maps,\n    const bool with_boundary_conditions) {\n  const std::string time_dependent_option =\n      time_dependent ? (hard_coded_time_dependent_maps\n                            ? \"  TimeDependentMaps:\\n\"\n                              \"    InitialTime: 1.0\\n\"\n                              \"    ShapeMap:\\n\"\n                              \"      LMax: 10\\n\"\n                              \"      InitialValues: Spherical\\n\"\n                              \"      CoefficientTruncationLimit: 0.0\\n\"\n                              \"      SizeInitialValues: Auto\\n\"\n                              \"    RotationMap: None\\n\"\n                              \"    ExpansionMap: None\\n\"\n                              \"    TranslationMap:\\n\"\n                              \"      InitialValues: [[0.0, 0.0, 0.0],\"\n                              \" [0.001, -0.003, 0.005], [0.0, 0.0, 0.0]]\\n\"\n                              \"    TransitionRotScaleTrans: False\\n\"\n                            : \"  TimeDependentMaps:\\n\"\n                              \"    UniformTranslation:\\n\"\n                              \"      InitialTime: 1.0\\n\"\n                              \"      Velocity: [2.3, -0.3, 0.5]\\n\")\n                     : \"  TimeDependentMaps: None\\n\";\n  const std::string inner_bc_option = with_boundary_conditions\n                                          ? \"  InnerBoundaryCondition:\\n\"\n                                            \"    TestBoundaryCondition:\\n\"\n                                            \"      Direction: lower-xi\\n\"\n                                            \"      BlockId: 50\\n\"\n                                          : \"\";\n  const std::string outer_bc_option = with_boundary_conditions\n                                          ? \"  OuterBoundaryCondition:\\n\"\n                                            \"    TestBoundaryCondition:\\n\"\n                                            \"      Direction: upper-xi\\n\"\n                                            \"      BlockId: 50\\n\"\n                                          : \"\";\n  return \"SphericalShells:\\n\"\n         \"  InnerRadius: \" +\n         std::to_string(inner_radius) +\n         \"\\n\"\n         \"  OuterRadius: \" +\n         std::to_string(outer_radius) + \"\\n\" +\n         \"  InitialRadialRefinement: \" + std::to_string(radial_refinement) +\n         \"\\n\"\n         \"  InitialNumberOfRadialGridPoints: \" +\n         std::to_string(radial_extents) +\n         \"\\n\"\n         \"  InitialSphericalHarmonicL: \" +\n         std::to_string(spherical_harmonic_l) +\n         \"\\n\"\n         \"  RadialPartitioning: \" +\n         stringize(radial_partitioning) +\n         \"\\n\"\n         \"  RadialDistribution: \" +\n         (radial_distribution.size() == 1 ? get_output(radial_distribution[0])\n                                          : stringize(radial_distribution)) +\n         \"\\n\" + time_dependent_option + inner_bc_option + outer_bc_option;\n}\n\nvoid test_parse_errors() {\n  INFO(\"SphericalShells check throws\");\n  const double inner_radius = 1.0;\n  const double outer_radius = 2.0;\n  const size_t radial_refinement = 2;\n  const size_t radial_extents = 5;\n  const size_t l = 6;\n  const std::vector<double> radial_partitioning = {};\n  const std::vector<double> radial_partitioning_unordered = {\n      {1.5 * inner_radius, 1.1 * inner_radius}};\n  const std::vector<double> radial_partitioning_low = {\n      {0.5 * inner_radius, 1.1 * inner_radius}};\n  const std::vector<double> radial_partitioning_high = {\n      {2.1 * outer_radius, 2.2 * outer_radius}};\n  const std::vector<domain::CoordinateMaps::Distribution> radial_distribution{\n      domain::CoordinateMaps::Distribution::Linear};\n  const std::vector<domain::CoordinateMaps::Distribution>\n      radial_distribution_too_many{\n          domain::CoordinateMaps::Distribution::Linear,\n          domain::CoordinateMaps::Distribution::Logarithmic};\n  const std::vector<domain::CoordinateMaps::Distribution>\n      radial_distribution_inner_log{\n          domain::CoordinateMaps::Distribution::Logarithmic};\n\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, 0.5 * inner_radius, radial_refinement, radial_extents,\n          l, radial_partitioning, radial_distribution, std::nullopt, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Inner radius must be smaller than outer radius\"));\n\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning_unordered, radial_distribution, std::nullopt,\n          nullptr, nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify radial partitioning in ascending order.\"));\n\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning_low, radial_distribution, std::nullopt, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"First radial partition must be larger than the inner\"));\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning_high, radial_distribution, std::nullopt, nullptr,\n          nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Last radial partition must be smaller than the outer\"));\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning, radial_distribution_too_many, std::nullopt,\n          nullptr, nullptr, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify a 'RadialDistribution' for every spherical shell. You\"));\n\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning, radial_distribution, std::nullopt,\n          create_boundary_condition(false), nullptr,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Must specify either both inner and outer boundary conditions \"\n          \"or neither.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning, radial_distribution, std::nullopt,\n          create_boundary_condition(false),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions with SphericalShells\"));\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning, radial_distribution, std::nullopt,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestPeriodicBoundaryCondition<3>>(),\n          create_boundary_condition(true), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot have periodic boundary conditions with SphericalShells\"));\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning, radial_distribution, std::nullopt,\n          create_boundary_condition(false),\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n  CHECK_THROWS_WITH(\n      domain::creators::SphericalShells(\n          inner_radius, outer_radius, radial_refinement, radial_extents, l,\n          radial_partitioning, radial_distribution, std::nullopt,\n          std::make_unique<TestHelpers::domain::BoundaryConditions::\n                               TestNoneBoundaryCondition<3>>(),\n          create_boundary_condition(true), Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"None boundary condition is not supported. If you would like \"\n          \"an outflow-type boundary condition, you must use that.\"));\n}\n\ntemplate <typename Generator>\nvoid test_spherical_shells_construction(\n    const gsl::not_null<Generator*> gen,\n    const domain::creators::SphericalShells& spherical_shells,\n    const double inner_radius, const double outer_radius,\n    const std::vector<double>& radial_partitioning = {},\n    const bool expect_boundary_conditions = true,\n    const std::vector<double>& times = {0.},\n    const std::array<double, 3>& velocity = {{0., 0., 0.}}) {\n  // check consistency of domain\n  const auto domain = TestHelpers::domain::creators::test_domain_creator(\n      spherical_shells, expect_boundary_conditions, false, times);\n  const auto& grid_anchors = spherical_shells.grid_anchors();\n  CHECK(grid_anchors.size() == 1);\n  CHECK(grid_anchors.count(\"Center\") == 1);\n  CHECK(grid_anchors.at(\"Center\") ==\n        tnsr::I<double, 3, Frame::Grid>{std::array{0.0, 0.0, 0.0}});\n\n  const auto& blocks = domain.blocks();\n  const auto block_names = spherical_shells.block_names();\n  const size_t num_blocks = blocks.size();\n  CAPTURE(num_blocks);\n  const auto all_boundary_conditions =\n      spherical_shells.external_boundary_conditions();\n  const auto functions_of_time = spherical_shells.functions_of_time();\n\n  // Check total number of external boundaries\n  const size_t num_shells = radial_partitioning.size() + 1;\n  CHECK(num_blocks == num_shells);\n  const size_t num_external_boundaries =\n      alg::accumulate(blocks, 0_st, [](const size_t count, const auto& block) {\n        return count + block.external_boundaries().size();\n      });\n  CHECK(num_external_boundaries == 2);\n\n  std::vector<double> expected_radii = radial_partitioning;\n  expected_radii.insert(expected_radii.begin(), inner_radius);\n  expected_radii.emplace_back(outer_radius);\n\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::uniform_real_distribution<> theta_distribution(0.0, M_PI);\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t block_id = 0; block_id < num_blocks; ++block_id) {\n    CAPTURE(block_id);\n    const auto& block = blocks[block_id];\n    const ElementMap<3, Frame::Grid> grid_element_map{ElementId<3>{block_id},\n                                                      block};\n    const ElementMap<3, Frame::Inertial> inertial_element_map{\n        ElementId<3>{block_id}, block};\n    {\n      INFO(\"Radius of random point on lower face\");\n      const tnsr::I<double, 3, Frame::ElementLogical> x_logical{\n          {{-1.0, theta_distribution(*gen), phi_distribution(*gen)}}};\n      for (const double current_time : times) {\n        CAPTURE(current_time);\n        auto x_inertial =\n            inertial_element_map(x_logical, current_time, functions_of_time);\n        const double delta_t = current_time - 1.0;\n        for (size_t i = 0; i < 3; ++i) {\n          x_inertial.get(i) -= gsl::at(velocity, i) * delta_t;\n        }\n        CHECK(get(magnitude(x_inertial)) == approx(expected_radii[block_id]));\n      }\n    }\n    {\n      INFO(\"Radius of random point on upper face\");\n      const double r = expected_radii[block_id + 1];\n      const double theta = theta_distribution(*gen);\n      const double phi = phi_distribution(*gen);\n      const tnsr::I<double, 3, Frame::ElementLogical> x_logical{\n          {{1.0, theta, phi}}};\n      const tnsr::I<double, 3, Frame::Grid> expected_x_grid{\n          {{r * sin(theta) * cos(phi), r * sin(theta) * sin(phi),\n            r * cos(theta)}}};\n      for (const double current_time : times) {\n        CAPTURE(current_time);\n        const auto x_grid =\n            grid_element_map(x_logical, current_time, functions_of_time);\n        CAPTURE(x_grid);\n        CHECK_ITERABLE_APPROX(x_grid, expected_x_grid);\n        auto x_inertial =\n            inertial_element_map(x_logical, current_time, functions_of_time);\n        const double delta_t = current_time - 1.0;\n        for (size_t i = 0; i < 3; ++i) {\n          x_inertial.get(i) -= gsl::at(velocity, i) * delta_t;\n        }\n        CHECK(get(magnitude(x_inertial)) ==\n              approx(expected_radii[block_id + 1]));\n      }\n    }\n    {\n      INFO(\"External boundaries\");\n      const auto& external_boundaries = block.external_boundaries();\n      if (num_blocks == 1) {\n        CHECK(external_boundaries.size() == 2);\n        CHECK(alg::found(external_boundaries, Direction<3>::lower_xi()));\n        CHECK(alg::found(external_boundaries, Direction<3>::upper_xi()));\n      } else if (block_id == 0) {\n        CHECK(external_boundaries.size() == 1);\n        CHECK(alg::found(external_boundaries, Direction<3>::lower_xi()));\n      } else if (block_id == num_blocks - 1) {\n        CHECK(external_boundaries.size() == 1);\n        CHECK(alg::found(external_boundaries, Direction<3>::upper_xi()));\n      } else {\n        CHECK(external_boundaries.empty());\n      }\n    }\n    if (expect_boundary_conditions) {\n      INFO(\"Boundary conditions\");\n      const auto& boundary_conditions = all_boundary_conditions[block_id];\n      for (const auto& direction : block.external_boundaries()) {\n        CAPTURE(direction);\n        const auto& boundary_condition =\n            dynamic_cast<const TestHelpers::domain::BoundaryConditions::\n                             TestBoundaryCondition<3>&>(\n                *boundary_conditions.at(direction));\n        CHECK(boundary_condition.direction() == direction);\n      }\n    }\n  }\n}\n\ntemplate <typename Generator>\nvoid test_sphere(const gsl::not_null<Generator*> gen) {\n  const double inner_radius = 1.0;\n  const double outer_radius = 2.0;\n  const size_t radial_refinement = 3;\n  const size_t radial_extents = 5;\n  const size_t l = 6;\n  const double outer_minus_inner = outer_radius - inner_radius;\n  const std::array<std::vector<double>, 3> radial_partitioning{\n      {{},\n       {0.5 * (inner_radius + outer_radius)},\n       {inner_radius + 0.3 * outer_minus_inner,\n        inner_radius + 0.6 * outer_minus_inner}}};\n  const std::array<std::vector<domain::CoordinateMaps::Distribution>, 3>\n      radial_distribution{{{domain::CoordinateMaps::Distribution::Linear},\n                           {domain::CoordinateMaps::Distribution::Linear,\n                            domain::CoordinateMaps::Distribution::Logarithmic},\n                           {domain::CoordinateMaps::Distribution::Linear}}};\n\n  const std::array<double, 3> velocity{{2.3, -0.3, 0.5}};\n  const size_t l_max = 10;\n  const std::vector<double> times{1., 10.};\n  for (auto [index, time_dependent, use_hard_coded_time_dep_options,\n             with_boundary_conditions] :\n       random_sample<5>(\n           cartesian_product(make_array(0_st, 1_st, 2_st),\n                             make_array(true, false), make_array(true, false),\n                             make_array(true, false)),\n           gen)) {\n    CAPTURE(time_dependent);\n    CAPTURE(with_boundary_conditions);\n    // If we aren't time dependent, just set the hard coded option to false to\n    // avoid ambiguity\n    if (not time_dependent) {\n      use_hard_coded_time_dep_options = false;\n    }\n    CAPTURE(use_hard_coded_time_dep_options);\n    // If we are using hard coded maps, we need at least two shells (or one\n    // radial partition) for the translation map.\n    auto array_index = (use_hard_coded_time_dep_options and\n                                gsl::at(radial_partitioning, index).empty()\n                            ? index + 1\n                            : index);\n    CAPTURE(gsl::at(radial_partitioning, array_index));\n    CAPTURE(gsl::at(radial_distribution, array_index));\n    domain::creators::SphericalShells::RadialDistribution::type\n        radial_distribution_variant;\n    if (gsl::at(radial_distribution, array_index).size() == 1) {\n      radial_distribution_variant =\n          gsl::at(radial_distribution, array_index)[0];\n    } else {\n      radial_distribution_variant = gsl::at(radial_distribution, array_index);\n    }\n\n    std::optional<domain::creators::SphericalShells::TimeDepOptionType>\n        time_dependent_options{};\n\n    auto translation_velocity = std::array<double, 3>{{0., 0., 0.}};\n\n    if (time_dependent) {\n      if (use_hard_coded_time_dep_options) {\n        using namespace domain::creators::time_dependent_options;  // NOLINT\n        translation_velocity = std::array<double, 3>{{0.001, -0.003, 0.005}};\n        time_dependent_options =\n            domain::creators::sphere::TimeDependentMapOptions{\n                1.0,\n                ShapeMapOptions<false, domain::ObjectLabel::None>{l_max,\n                                                                  std::nullopt},\n                std::nullopt,\n                std::nullopt,\n                TranslationMapOptions<3>{std::array{\n                    std::array<double, 3>{0.0, 0.0, 0.0}, translation_velocity,\n                    std::array<double, 3>{0.0, 0.0, 0.0}}},\n                false};\n      } else {\n        time_dependent_options = std::make_unique<\n            domain::creators::time_dependence::UniformTranslation<3, 0>>(\n            1.0, velocity);\n        translation_velocity = velocity;\n      }\n    }\n\n    const domain::creators::SphericalShells spherical_shells{\n        inner_radius,\n        outer_radius,\n        radial_refinement,\n        radial_extents,\n        l,\n        gsl::at(radial_partitioning, array_index),\n        radial_distribution_variant,\n        std::move(time_dependent_options),\n        with_boundary_conditions ? create_boundary_condition(false) : nullptr,\n        with_boundary_conditions ? create_boundary_condition(true) : nullptr};\n    test_spherical_shells_construction(\n        gen, spherical_shells, inner_radius, outer_radius,\n        gsl::at(radial_partitioning, array_index), with_boundary_conditions,\n        time_dependent ? times : std::vector<double>{1.}, translation_velocity);\n    TestHelpers::domain::creators::test_creation(\n        option_string(\n            inner_radius, outer_radius, radial_refinement, radial_extents, l,\n            gsl::at(radial_partitioning, array_index),\n            gsl::at(radial_distribution, array_index), time_dependent,\n            use_hard_coded_time_dep_options, with_boundary_conditions),\n        spherical_shells, with_boundary_conditions);\n  }\n}\n\nvoid test_shape_distortion_general(\n    const double time,\n    domain::creators::SphericalShells::TimeDepOptionType time_dependent_options,\n    const double deformed_radius, const tnsr::I<DataVector, 3>& x) {\n  const domain::creators::SphericalShells domain_creator{\n      deformed_radius,\n      10.,\n      0_st,\n      5_st,\n      6_st,\n      {4.},\n      domain::CoordinateMaps::Distribution::Linear,\n      std::move(time_dependent_options)};\n  const auto domain = domain_creator.create_domain();\n  const auto functions_of_time = domain_creator.functions_of_time();\n  // Map the coordinates through the domain. They should lie at the lower xi\n  // boundary of their block.\n  const auto x_logical =\n      block_logical_coordinates(domain, x, time, functions_of_time);\n  for (size_t i = 0; i < get<0>(x).size(); ++i) {\n    CAPTURE(x_logical[i]);\n    REQUIRE(x_logical[i].has_value());\n    CHECK(get<0>(x_logical[i]->data) == approx(-1.));\n  }\n}\n\nvoid test_shape_distortion() {\n  domain::creators::SphericalShells::TimeDepOptionType time_dependent_options{};\n\n  // Set up theta phis\n  const size_t l_max = 16;\n  const ylm::Spherepack ylm{l_max, l_max};\n  const std::array<DataVector, 2> theta_phi = ylm.theta_phi_points();\n\n  const double time = 0.7;\n  const double mass = 0.8;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.9}};\n  const double r_plus = mass * (1. + sqrt(1. - dot(spin, spin)));\n  const double inner_radius = r_plus;\n\n  const DataVector radius =\n      get(gr::Solutions::kerr_schild_radius_from_boyer_lindquist(\n          inner_radius, theta_phi, mass, spin));\n  // Set up coordinates on an ellipsoid of constant Boyer-Lindquist radius\n  tnsr::I<DataVector, 3> x{};\n  get<0>(x) = radius * sin(get<0>(theta_phi)) * cos(get<1>(theta_phi));\n  get<1>(x) = radius * sin(get<0>(theta_phi)) * sin(get<1>(theta_phi));\n  get<2>(x) = radius * cos(get<0>(theta_phi));\n  {\n    INFO(\n        \"Check that inner radius is deformed to constant Boyer-Lindquist \"\n        \"radius\");\n\n    // Time dependence\n    time_dependent_options = std::make_unique<\n        domain::creators::time_dependence::Shape<domain::ObjectLabel::None>>(\n        time, l_max, mass, spin, std::array<double, 3>{{0., 0., 0.}}, 0.0,\n        inner_radius, 4.);\n\n    test_shape_distortion_general(time, std::move(time_dependent_options),\n                                  inner_radius, x);\n\n    // KerrSchild-BoyerLindquist. Use same x as above\n    time_dependent_options = domain::creators::sphere::TimeDependentMapOptions{\n        time,\n        domain::creators::time_dependent_options::ShapeMapOptions<\n            false, domain::ObjectLabel::None>{\n            l_max, domain::creators::time_dependent_options::\n                       KerrSchildFromBoyerLindquist{mass, spin}},\n        std::nullopt,\n        std::nullopt,\n        std::nullopt,\n        true};\n    test_shape_distortion_general(time, std::move(time_dependent_options),\n                                  inner_radius, x);\n  }\n\n  {\n    INFO(\"Check reading in Ylms from file\");\n    const std::string h5_filename{\"S2_StrahlkorperCoefsFile.h5\"};\n    const std::string subfile_name{\"Ylm_coefs\"};\n    if (file_system::check_if_file_exists(h5_filename)) {\n      file_system::rm(h5_filename, true);\n    }\n    const ylm::Strahlkorper<Frame::Distorted> strahlkorper{\n        l_max, l_max, radius, std::array{0.0, 0.0, 0.0}};\n    std::vector<std::string> legend{};\n    std::vector<double> data{};\n    ylm::fill_ylm_legend_and_data(make_not_null(&legend), make_not_null(&data),\n                                  strahlkorper, time, l_max);\n    {\n      h5::H5File<h5::AccessType::ReadWrite> test_file{h5_filename, true};\n      auto& subfile = test_file.insert<h5::Dat>(\"/\" + subfile_name, legend);\n      subfile.append(data);\n      test_file.close_current_object();\n    }\n\n    time_dependent_options = domain::creators::sphere::TimeDependentMapOptions{\n        time,\n        domain::creators::time_dependent_options::ShapeMapOptions<\n            false, domain::ObjectLabel::None>{\n            l_max,\n            domain::creators::time_dependent_options::YlmsFromFile{\n                h5_filename, std::vector{subfile_name}, time, std::nullopt,\n                false},\n            // Constructing a strahlkorper from collocation radii will not\n            // exactly match the collocation points (see Strahlkorper\n            // constructor docs). For this reason, the 00 coef we calculate is\n            // not exact. This is why we just hard code the proper value here.\n            // If you change l_max, this value must also change.\n            std::array{-4.6442771561420703730e-01, 0.0, 0.0}},\n        std::nullopt,\n        std::nullopt,\n        std::nullopt,\n        true};\n\n    test_shape_distortion_general(time, std::move(time_dependent_options),\n                                  inner_radius, x);\n\n    if (file_system::check_if_file_exists(h5_filename)) {\n      file_system::rm(h5_filename, true);\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 15]]\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.SphericalShells\", \"[Domain][Unit]\") {\n  MAKE_GENERATOR(gen);\n  domain::creators::time_dependence::register_derived_with_charm();\n  test_parse_errors();\n  test_sphere(make_not_null(&gen));\n  test_shape_distortion();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/FunctionsOfTime/OptionTags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <size_t Dim>\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<Tags::Domain<Dim>>(\"Domain\");\n  TestHelpers::db::test_simple_tag<Tags::InitialExtents<Dim>>(\"InitialExtents\");\n  TestHelpers::db::test_simple_tag<Tags::InitialRefinementLevels<Dim>>(\n      \"InitialRefinementLevels\");\n  TestHelpers::db::test_simple_tag<Tags::ExternalBoundaryConditions<Dim>>(\n      \"ExternalBoundaryConditions\");\n}\n\nvoid test_center_tags() {\n  TestHelpers::db::test_simple_tag<Tags::ObjectCenter<ObjectLabel::A>>(\n      \"ObjectCenterA\");\n  TestHelpers::db::test_simple_tag<Tags::ObjectCenter<ObjectLabel::B>>(\n      \"ObjectCenterB\");\n\n  using Object = domain::creators::BinaryCompactObject<false>::Object;\n\n  const std::unique_ptr<DomainCreator<3>> domain_creator =\n      std::make_unique<domain::creators::BinaryCompactObject<false>>(\n          Object{0.2, 5.0, 8.0, true, true}, Object{0.6, 4.0, -5.5, true, true},\n          std::array<double, 2>{{0.1, 0.2}}, 100.0, 500.0, 1.0, 1_st, 5_st);\n\n  const auto grid_center_A =\n      Tags::ObjectCenter<ObjectLabel::A>::create_from_options(domain_creator);\n  const auto grid_center_B =\n      Tags::ObjectCenter<ObjectLabel::B>::create_from_options(domain_creator);\n\n  CHECK(grid_center_A == tnsr::I<double, 3, Frame::Grid>{{8.0, 0.1, 0.2}});\n  CHECK(grid_center_B == tnsr::I<double, 3, Frame::Grid>{{-5.5, 0.1, 0.2}});\n\n  const std::unique_ptr<DomainCreator<3>> creator_no_excision =\n      std::make_unique<domain::creators::Brick>(\n          std::array{0.0, 0.0, 0.0}, std::array{1.0, 1.0, 1.0},\n          std::array{0_st, 0_st, 0_st}, std::array{2_st, 2_st, 2_st},\n          std::array{false, false, false});\n\n  CHECK_THROWS_WITH(\n      Tags::ObjectCenter<ObjectLabel::B>::create_from_options(\n          creator_no_excision),\n      Catch::Matchers::ContainsSubstring(\n          \" is not in the domain creators grid anchors but is needed \"\n          \"to generate the ObjectCenter\"));\n}\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n};\n\nvoid test_functions_of_time() {\n  TestHelpers::db::test_simple_tag<Tags::FunctionsOfTimeInitialize>(\n      \"FunctionsOfTime\");\n\n  CHECK(std::is_same_v<\n        Tags::FunctionsOfTimeInitialize::option_tags<Metavariables>,\n        tmpl::list<\n            domain::OptionTags::DomainCreator<Metavariables::volume_dim>>>);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.Tags\", \"[Unit][Domain]\") {\n  test_simple_tags<1>();\n  test_simple_tags<2>();\n  test_simple_tags<3>();\n\n  test_center_tags();\n\n  test_functions_of_time();\n}\n}  // namespace\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependence/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DomainTimeDependence\")\n\nset(LIBRARY_SOURCES\n  Test_CubicScale.cpp\n  Test_None.cpp\n  Test_RotationAboutZAxis.cpp\n  Test_ScalingAndZRotation.cpp\n  Test_Shape.cpp\n  Test_SphericalCompression.cpp\n  Test_UniformTranslation.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  DataStructuresHelpers\n  DomainHelpers\n  DomainStructure\n  DomainTimeDependence\n  GeneralRelativity\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependence/Test_CubicScale.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/Creators/TimeDependence/CubicScale.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependence/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependence {\nnamespace {\ntemplate <size_t MeshDim>\nusing CubicScaleMap =\n    domain::CoordinateMaps::TimeDependent::CubicScale<MeshDim>;\n\ntemplate <size_t MeshDim>\nusing CoordMap =\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, CubicScaleMap<MeshDim>>;\n\ntemplate <size_t MeshDim>\nCoordMap<MeshDim> create_coord_map(\n    const double outer_boundary,\n    const std::array<std::string, 2>& f_of_t_names) {\n  return CoordMap<MeshDim>{\n      CubicScaleMap<MeshDim>{outer_boundary, f_of_t_names[0], f_of_t_names[1]}};\n}\n\ntemplate <size_t MeshDim>\nvoid test_impl(\n    const std::unique_ptr<TimeDependence<MeshDim>>& time_dep_unique_ptr,\n    const double initial_time, const double outer_boundary,\n    const bool use_linear_scaling,\n    const std::array<double, 2>& initial_expansion,\n    const std::array<double, 2>& velocity,\n    const std::array<double, 2>& acceleration,\n    const std::array<std::string, 2>& f_of_t_names) {\n  MAKE_GENERATOR(gen);\n  CAPTURE(initial_time);\n  CAPTURE(outer_boundary);\n  CAPTURE(use_linear_scaling);\n  CAPTURE(initial_expansion);\n  CAPTURE(velocity);\n  CAPTURE(acceleration);\n  CAPTURE(f_of_t_names);\n\n  CHECK_FALSE(time_dep_unique_ptr->is_none());\n\n  // We downcast to the expected derived class to make sure that factory\n  // creation worked correctly. In order to maximize code reuse this check is\n  // done here as opposed to separately elsewhere.\n  const auto* const time_dep =\n      dynamic_cast<const CubicScale<MeshDim>*>(time_dep_unique_ptr.get());\n  REQUIRE(time_dep != nullptr);\n\n  // Test coordinate maps\n  UniformCustomDistribution<size_t> dist_size_t{1, 10};\n  const size_t num_blocks = dist_size_t(gen);\n  CAPTURE(num_blocks);\n\n  const auto expected_block_map =\n      create_coord_map<MeshDim>(outer_boundary, f_of_t_names);\n\n  const auto block_maps =\n      time_dep_unique_ptr->block_maps_grid_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps) {\n    const auto* const block_map =\n        dynamic_cast<const CoordMap<MeshDim>*>(block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map);\n  }\n\n  // Check that maps involving distorted frame are empty.\n  CHECK(time_dep_unique_ptr->block_maps_grid_to_distorted(1)[0].get() ==\n        nullptr);\n  CHECK(time_dep_unique_ptr->block_maps_distorted_to_inertial(1)[0].get() ==\n        nullptr);\n\n  // Test functions of time without expiration times\n  {\n    const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n    bool f_of_t_correct_number =\n        use_linear_scaling ? functions_of_time.size() == 1\n                           : functions_of_time.size() == f_of_t_names.size();\n    REQUIRE(f_of_t_correct_number);\n    for (const auto& f_of_t_name : f_of_t_names) {\n      CHECK(functions_of_time.count(f_of_t_name) == 1);\n      CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n            std::numeric_limits<double>::infinity());\n    }\n  }\n  // Test functions of time with expiration times\n  {\n    const double init_expr_time = 5.0;\n    std::unordered_map<std::string, double> init_expr_times{};\n    for (auto& name : f_of_t_names) {\n      init_expr_times[name] = init_expr_time;\n    }\n    const auto functions_of_time =\n        time_dep_unique_ptr->functions_of_time(init_expr_times);\n    bool f_of_t_correct_number =\n        use_linear_scaling ? functions_of_time.size() == 1\n                           : functions_of_time.size() == f_of_t_names.size();\n    REQUIRE(f_of_t_correct_number);\n    for (const auto& f_of_t_name : f_of_t_names) {\n      CHECK(functions_of_time.count(f_of_t_name) == 1);\n      CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n            init_expr_time);\n    }\n  }\n\n  const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n\n  // For a random point at a random time check that the values agree. This is to\n  // check that the internals were assigned the correct function of times.\n  TIME_DEPENDENCE_GENERATE_COORDS(make_not_null(&gen), MeshDim, -1.0, 1.0);\n\n  for (const auto& block_map : block_maps) {\n    // We've checked equivalence above\n    // (CHECK(*black_map == expected_block_map);), but have sometimes been\n    // burned by incorrect operator== implementations so we check that the\n    // mappings behave as expected.\n    const double time_offset = dist(gen) + 1.2;\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(grid_coords_dv, initial_time + time_offset,\n                           functions_of_time),\n        (*block_map)(grid_coords_dv, initial_time + time_offset,\n                     functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(grid_coords_double, initial_time + time_offset,\n                           functions_of_time),\n        (*block_map)(grid_coords_double, initial_time + time_offset,\n                     functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        *expected_block_map.inverse(inertial_coords_double,\n                                    initial_time + time_offset,\n                                    functions_of_time),\n        *block_map->inverse(inertial_coords_double, initial_time + time_offset,\n                            functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(\n            grid_coords_dv, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(grid_coords_dv, initial_time + time_offset,\n                                functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(grid_coords_double, initial_time + time_offset,\n                                functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(grid_coords_dv, initial_time + time_offset,\n                                    functions_of_time),\n        block_map->jacobian(grid_coords_dv, initial_time + time_offset,\n                            functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->jacobian(grid_coords_double, initial_time + time_offset,\n                            functions_of_time));\n  }\n}\n\ntemplate <size_t MeshDim>\nvoid test(const bool use_linear_scaling) {\n  const double initial_time = 1.3;\n  const double outer_boundary = 10.4;\n  const std::array<double, 2> initial_expansion{{1.0, 1.0}};\n  const std::array<double, 2> velocity{{-0.1, 0.0}};\n  const std::array<double, 2> acceleration{{-0.05, 0.0}};\n  // These names must match the hard coded ones in CubicScale\n  const std::string f_of_t_name0 =\n      \"CubicScale\"s + (use_linear_scaling ? \"\" : \"A\");\n  const std::string f_of_t_name1 =\n      \"CubicScale\"s + (use_linear_scaling ? \"\" : \"B\");\n\n  const std::unique_ptr<\n      domain::creators::time_dependence::TimeDependence<MeshDim>>\n      time_dep = std::make_unique<CubicScale<MeshDim>>(\n          initial_time, outer_boundary, use_linear_scaling, initial_expansion,\n          velocity, acceleration);\n  test_impl(time_dep, initial_time, outer_boundary, use_linear_scaling,\n            initial_expansion, velocity, acceleration,\n            {f_of_t_name0, f_of_t_name1});\n  test_impl(time_dep->get_clone(), initial_time, outer_boundary,\n            use_linear_scaling, initial_expansion, velocity, acceleration,\n            {f_of_t_name0, f_of_t_name1});\n\n  const std::string linear_scaling = use_linear_scaling\n                                         ? \"  UseLinearScaling: true\\n\"\n                                         : \"  UseLinearScaling: false\\n\";\n  test_impl(\n      TestHelpers::test_creation<std::unique_ptr<TimeDependence<MeshDim>>>(\n          \"CubicScale:\\n\"\n          \"  InitialTime: 1.3\\n\"\n          \"  OuterBoundary: 10.4\\n\" +\n          linear_scaling +\n          \"  InitialExpansion: [1.0, 1.0]\\n\"\n          \"  Velocity: [-0.1, 0.0]\\n\"\n          \"  Acceleration: [-0.05, 0.0]\\n\"),\n      initial_time, outer_boundary, use_linear_scaling, initial_expansion,\n      velocity, acceleration, {f_of_t_name0, f_of_t_name1});\n\n  INFO(\"Check equivalence operators\");\n  CubicScale<MeshDim> cubic_scale0{1.0,          2.0,           false,\n                                   {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale1{1.2,          2.0,           false,\n                                   {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale2{1.0,          3.0,           false,\n                                   {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale3{1.0,          2.0,           true,\n                                   {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale4{1.0,          2.0,           false,\n                                   {{3.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale5{1.0,          2.0,           false,\n                                   {{1.0, 0.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale6{1.0,          2.0,           false,\n                                   {{1.0, 2.0}}, {{-2.0, 3.0}}, {{0.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale7{1.0,          2.0,           false,\n                                   {{1.0, 2.0}}, {{-1.0, 7.0}}, {{0.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale8{1.0,          2.0,           false,\n                                   {{1.0, 2.0}}, {{-1.0, 3.0}}, {{3.2, 0.9}}};\n  CubicScale<MeshDim> cubic_scale9{1.0,          2.0,           false,\n                                   {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 10.9}}};\n  CubicScale<MeshDim> cubic_scale10{1.0,          2.0,           true,\n                                    {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n\n  CHECK(cubic_scale0 == cubic_scale0);\n  CHECK_FALSE(cubic_scale0 != cubic_scale0);\n  CHECK(cubic_scale0 != cubic_scale1);\n  CHECK_FALSE(cubic_scale0 == cubic_scale1);\n  CHECK(cubic_scale0 != cubic_scale2);\n  CHECK_FALSE(cubic_scale0 == cubic_scale2);\n  CHECK(cubic_scale0 != cubic_scale3);\n  CHECK_FALSE(cubic_scale0 == cubic_scale3);\n  CHECK(cubic_scale0 != cubic_scale4);\n  CHECK_FALSE(cubic_scale0 == cubic_scale4);\n  CHECK(cubic_scale0 != cubic_scale5);\n  CHECK_FALSE(cubic_scale0 == cubic_scale5);\n  CHECK(cubic_scale0 != cubic_scale6);\n  CHECK_FALSE(cubic_scale0 == cubic_scale6);\n  CHECK(cubic_scale0 != cubic_scale7);\n  CHECK_FALSE(cubic_scale0 == cubic_scale7);\n  CHECK(cubic_scale0 != cubic_scale8);\n  CHECK_FALSE(cubic_scale0 == cubic_scale8);\n  CHECK(cubic_scale0 != cubic_scale9);\n  CHECK_FALSE(cubic_scale0 == cubic_scale9);\n  CHECK(cubic_scale0 != cubic_scale10);\n  CHECK_FALSE(cubic_scale0 == cubic_scale10);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependence.CubicScale\",\n                  \"[Domain][Unit]\") {\n  test<1>(true);\n  test<2>(true);\n  test<3>(true);\n  test<1>(false);\n  test<2>(false);\n  test<3>(false);\n}\n}  // namespace\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependence/Test_None.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"Domain/Creators/TimeDependence/None.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependence {\n\nnamespace {\n\ntemplate <size_t MeshDim>\nvoid test() {\n  const std::unique_ptr<TimeDependence<MeshDim>> time_dep =\n      std::make_unique<None<MeshDim>>();\n  CHECK(time_dep != nullptr);\n  CHECK(time_dep->is_none());\n\n  const std::unique_ptr<TimeDependence<MeshDim>> time_dep_clone =\n      time_dep->get_clone();\n  CHECK(time_dep_clone != nullptr);\n\n  const std::unique_ptr<TimeDependence<MeshDim>> time_dep_factory =\n      TestHelpers::test_creation<std::unique_ptr<TimeDependence<MeshDim>>>(\n          \"None\\n\");\n  CHECK(time_dep_factory != nullptr);\n\n  CHECK(None<MeshDim>{} == None<MeshDim>{});\n  CHECK_FALSE(None<MeshDim>{} != None<MeshDim>{});\n\n  CHECK_THROWS_WITH(\n      (time_dep->block_maps_grid_to_inertial(5)),\n      Catch::Matchers::ContainsSubstring(\n          \"The 'block_maps_grid_to_inertial' function of the 'None'\"));\n  CHECK_THROWS_WITH(\n      (time_dep->block_maps_grid_to_distorted(5)),\n      Catch::Matchers::ContainsSubstring(\n          \"The 'block_maps_grid_to_distorted' function of the 'None'\"));\n  CHECK_THROWS_WITH(\n      (time_dep->block_maps_distorted_to_inertial(5)),\n      Catch::Matchers::ContainsSubstring(\n          \"The 'block_maps_distorted_to_inertial' function of the\"));\n  CHECK_THROWS_WITH((time_dep->functions_of_time()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The 'functions_of_time' function of the 'None'\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependence.None\",\n                  \"[Domain][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependence/Test_RotationAboutZAxis.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/Creators/TimeDependence/RotationAboutZAxis.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependence/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependence {\n\nnamespace {\nusing Identity = domain::CoordinateMaps::Identity<1>;\nusing Rotation = domain::CoordinateMaps::TimeDependent::Rotation<2>;\n\ntemplate <size_t MeshDim>\nusing ConcreteMap = tmpl::conditional_t<\n    MeshDim == 2, domain::CoordinateMap<Frame::Grid, Frame::Inertial, Rotation>,\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                          domain::CoordinateMaps::TimeDependent::ProductOf2Maps<\n                              Rotation, Identity>>>;\n\ntemplate <size_t MeshDim>\nConcreteMap<MeshDim> create_coord_map(const std::string& f_of_t_name);\n\ntemplate <>\nConcreteMap<2> create_coord_map<2>(const std::string& f_of_t_name) {\n  return ConcreteMap<2>{{Rotation{f_of_t_name}}};\n}\n\ntemplate <>\nConcreteMap<3> create_coord_map<3>(const std::string& f_of_t_name) {\n  return ConcreteMap<3>{{Rotation{f_of_t_name}, Identity{}}};\n}\n\ntemplate <size_t MeshDim>\nvoid test(const std::unique_ptr<TimeDependence<MeshDim>>& time_dep_unique_ptr,\n          const double initial_time, const std::string& f_of_t_name) {\n  MAKE_GENERATOR(gen);\n  CAPTURE(initial_time);\n  CAPTURE(f_of_t_name);\n\n  CHECK_FALSE(time_dep_unique_ptr->is_none());\n\n  // We downcast to the expected derived class to make sure that factory\n  // creation worked correctly. In order to maximize code reuse this check is\n  // done here as opposed to separately elsewhere.\n  const auto* const time_dep = dynamic_cast<const RotationAboutZAxis<MeshDim>*>(\n      time_dep_unique_ptr.get());\n  REQUIRE(time_dep != nullptr);\n\n  // Test coordinate maps\n  UniformCustomDistribution<size_t> dist_size_t{1, 10};\n  const size_t num_blocks = dist_size_t(gen);\n  CAPTURE(num_blocks);\n\n  const auto expected_block_map = create_coord_map<MeshDim>(f_of_t_name);\n\n  const auto block_maps =\n      time_dep_unique_ptr->block_maps_grid_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps) {\n    const auto* const block_map =\n        dynamic_cast<const ConcreteMap<MeshDim>*>(block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map);\n  }\n\n  // Check that maps involving distorted frame are empty.\n  CHECK(time_dep_unique_ptr->block_maps_grid_to_distorted(1)[0].get() ==\n        nullptr);\n  CHECK(time_dep_unique_ptr->block_maps_distorted_to_inertial(1)[0].get() ==\n        nullptr);\n\n  // Test functions of time without expiration times\n  {\n    const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n    REQUIRE(functions_of_time.size() == 1);\n    CHECK(functions_of_time.count(f_of_t_name) == 1);\n    CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n          std::numeric_limits<double>::infinity());\n  }\n  // Test functions of time with expiration times\n  {\n    const double init_expr_time = 5.0;\n    std::unordered_map<std::string, double> init_expr_times{};\n    init_expr_times[f_of_t_name] = init_expr_time;\n    const auto functions_of_time =\n        time_dep_unique_ptr->functions_of_time(init_expr_times);\n    REQUIRE(functions_of_time.size() == 1);\n    CHECK(functions_of_time.count(f_of_t_name) == 1);\n    CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n          init_expr_time);\n  }\n\n  const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n\n  // For a random point at a random time check that the values agree. This is to\n  // check that the internals were assigned the correct function of times.\n  TIME_DEPENDENCE_GENERATE_COORDS(make_not_null(&gen), MeshDim, -1.0, 1.0);\n\n  for (const auto& block_map : block_maps) {\n    // We've checked equivalence above\n    // (CHECK(*block_map == expected_block_map);), but have sometimes been\n    // burned by incorrect operator== implementations so we check that the\n    // mappings behave as expected.\n    const double time_offset = dist(gen) + 1.2;\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(grid_coords_dv, initial_time + time_offset,\n                           functions_of_time),\n        (*block_map)(grid_coords_dv, initial_time + time_offset,\n                     functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(grid_coords_double, initial_time + time_offset,\n                           functions_of_time),\n        (*block_map)(grid_coords_double, initial_time + time_offset,\n                     functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        *expected_block_map.inverse(inertial_coords_double,\n                                    initial_time + time_offset,\n                                    functions_of_time),\n        *block_map->inverse(inertial_coords_double, initial_time + time_offset,\n                            functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(\n            grid_coords_dv, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(grid_coords_dv, initial_time + time_offset,\n                                functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(grid_coords_double, initial_time + time_offset,\n                                functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(grid_coords_dv, initial_time + time_offset,\n                                    functions_of_time),\n        block_map->jacobian(grid_coords_dv, initial_time + time_offset,\n                            functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->jacobian(grid_coords_double, initial_time + time_offset,\n                            functions_of_time));\n  }\n}\n\nvoid test_equivalence() {\n  {\n    RotationAboutZAxis<2> ur0{1.0, 2.0, 0.1, 0.08};\n    RotationAboutZAxis<2> ur1{1.2, 2.0, 0.3, 0.07};\n    RotationAboutZAxis<2> ur2{1.0, 3.0, 0.3, 0.08};\n    CHECK(ur0 == ur0);\n    CHECK_FALSE(ur0 != ur0);\n    CHECK(ur0 != ur1);\n    CHECK_FALSE(ur0 == ur1);\n    CHECK(ur0 != ur2);\n    CHECK_FALSE(ur0 == ur2);\n  }\n  {\n    RotationAboutZAxis<3> ur0{1.0, 2.0, 0.1, 0.08};\n    RotationAboutZAxis<3> ur1{1.2, 2.0, 0.3, 0.07};\n    RotationAboutZAxis<3> ur2{1.0, 3.0, 0.3, 0.08};\n    CHECK(ur0 == ur0);\n    CHECK_FALSE(ur0 != ur0);\n    CHECK(ur0 != ur1);\n    CHECK_FALSE(ur0 == ur1);\n    CHECK(ur0 != ur2);\n    CHECK_FALSE(ur0 == ur2);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependence.RotationAboutZAxis\",\n                  \"[Domain][Unit]\") {\n  const double initial_time = 1.3;\n  constexpr double angle = 1.5;\n  constexpr double angular_velocity = 2.4;\n  constexpr double angular_acceleration = 0.27;\n  // This name must match the hard coded one in RotationAboutZAxis\n  const std::string f_of_t_name = \"Rotation\";\n  {\n    // 2d\n    const std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n        time_dep = std::make_unique<RotationAboutZAxis<2>>(\n            initial_time, angle, angular_velocity, angular_acceleration);\n    test(time_dep, initial_time, f_of_t_name);\n    test(time_dep->get_clone(), initial_time, f_of_t_name);\n\n    test(TestHelpers::test_creation<std::unique_ptr<TimeDependence<2>>>(\n             \"RotationAboutZAxis:\\n\"\n             \"  InitialTime: 1.3\\n\"\n             \"  InitialAngle: 1.5\\n\"\n             \"  InitialAngularVelocity: 2.4\\n\"\n             \"  InitialAngularAcceleration: 0.27\\n\"),\n         initial_time, f_of_t_name);\n  }\n\n  {\n    // 3d\n    const std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dep = std::make_unique<RotationAboutZAxis<3>>(\n            initial_time, angle, angular_velocity, angular_acceleration);\n    test(time_dep, initial_time, f_of_t_name);\n    test(time_dep->get_clone(), initial_time, f_of_t_name);\n\n    test(TestHelpers::test_creation<std::unique_ptr<TimeDependence<3>>>(\n             \"RotationAboutZAxis:\\n\"\n             \"  InitialTime: 1.3\\n\"\n             \"  InitialAngle: 1.5\\n\"\n             \"  InitialAngularVelocity: 2.4\\n\"\n             \"  InitialAngularAcceleration: 0.27\\n\"),\n         initial_time, f_of_t_name);\n  }\n\n  test_equivalence();\n}\n}  // namespace\n\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependence/Test_ScalingAndZRotation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/Creators/TimeDependence/GenerateCoordinateMap.hpp\"\n#include \"Domain/Creators/TimeDependence/ScalingAndZRotation.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependence/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependence {\n\nnamespace {\nusing Identity = domain::CoordinateMaps::Identity<1>;\nusing Rotation = domain::CoordinateMaps::TimeDependent::Rotation<2>;\ntemplate <size_t MeshDim>\nusing CubicScaleMap =\n    domain::CoordinateMaps::TimeDependent::CubicScale<MeshDim>;\n\ntemplate <size_t MeshDim>\nusing GridToInertialMap = detail::generate_coordinate_map_t<\n    Frame::Grid, Frame::Inertial,\n    tmpl::list<CubicScaleMap<MeshDim>,\n               tmpl::conditional_t<MeshDim == 2, Rotation,\n                                   domain::CoordinateMaps::TimeDependent::\n                                       ProductOf2Maps<Rotation, Identity>>>>;\n\ntemplate <size_t MeshDim>\nGridToInertialMap<MeshDim> create_grid_to_inertial_map(\n    const double outer_boundary,\n    const std::array<std::string, 3>& f_of_t_names);\n\ntemplate <>\nGridToInertialMap<2> create_grid_to_inertial_map<2>(\n    const double outer_boundary,\n    const std::array<std::string, 3>& f_of_t_names) {\n  return GridToInertialMap<2>{\n      CubicScaleMap<2>{outer_boundary, f_of_t_names[0], f_of_t_names[1]},\n      Rotation{f_of_t_names[2]}};\n}\n\ntemplate <>\nGridToInertialMap<3> create_grid_to_inertial_map<3>(\n    const double outer_boundary,\n    const std::array<std::string, 3>& f_of_t_names) {\n  return GridToInertialMap<3>{\n      CubicScaleMap<3>{outer_boundary, f_of_t_names[0], f_of_t_names[1]},\n      {Rotation{f_of_t_names[2]}, Identity{}}};\n}\n\ntemplate <size_t MeshDim>\nusing GridToDistortedMap =\n    detail::generate_coordinate_map_t<Frame::Grid, Frame::Distorted,\n                                      tmpl::list<CubicScaleMap<MeshDim>>>;\n\ntemplate <size_t MeshDim>\nGridToDistortedMap<MeshDim> create_grid_to_distorted_map(\n    const double outer_boundary,\n    const std::array<std::string, 3>& f_of_t_names) {\n  return GridToDistortedMap<MeshDim>{\n      CubicScaleMap<MeshDim>{outer_boundary, f_of_t_names[0], f_of_t_names[1]}};\n}\n\ntemplate <size_t MeshDim>\nusing DistortedToInertialMap = detail::generate_coordinate_map_t<\n    Frame::Distorted, Frame::Inertial,\n    tmpl::list<tmpl::conditional_t<MeshDim == 2, Rotation,\n                                   domain::CoordinateMaps::TimeDependent::\n                                       ProductOf2Maps<Rotation, Identity>>>>;\n\ntemplate <size_t MeshDim>\nDistortedToInertialMap<MeshDim> create_distorted_to_inertial_map(\n    const std::array<std::string, 3>& f_of_t_names);\n\ntemplate <>\nDistortedToInertialMap<2> create_distorted_to_inertial_map<2>(\n    const std::array<std::string, 3>& f_of_t_names) {\n  return DistortedToInertialMap<2>{Rotation{f_of_t_names[2]}};\n}\n\ntemplate <>\nDistortedToInertialMap<3> create_distorted_to_inertial_map<3>(\n    const std::array<std::string, 3>& f_of_t_names) {\n  return DistortedToInertialMap<3>{{Rotation{f_of_t_names[2]}, Identity{}}};\n}\n\ntemplate <size_t MeshDim>\nvoid test_impl(\n    const std::unique_ptr<TimeDependence<MeshDim>>& time_dep_unique_ptr,\n    const double initial_time, const double outer_boundary,\n    const bool use_linear_scaling,\n    const std::array<std::string, 3>& f_of_t_names) {\n  MAKE_GENERATOR(gen);\n  CAPTURE(initial_time);\n  CAPTURE(outer_boundary);\n  CAPTURE(use_linear_scaling);\n  CAPTURE(f_of_t_names);\n\n  CHECK_FALSE(time_dep_unique_ptr->is_none());\n\n  // We downcast to the expected derived class to make sure that factory\n  // creation worked correctly. In order to maximize code reuse this check is\n  // done here as opposed to separately elsewhere.\n  const auto* const time_dep =\n      dynamic_cast<const ScalingAndZRotation<MeshDim>*>(\n          time_dep_unique_ptr.get());\n  REQUIRE(time_dep != nullptr);\n\n  // Test coordinate maps\n  UniformCustomDistribution<size_t> dist_size_t{1, 10};\n  const size_t num_blocks = dist_size_t(gen);\n  CAPTURE(num_blocks);\n\n  const auto expected_grid_to_inertial_map =\n      create_grid_to_inertial_map<MeshDim>(outer_boundary, f_of_t_names);\n\n  const auto block_maps_grid_to_inertial =\n      time_dep_unique_ptr->block_maps_grid_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_grid_to_inertial) {\n    const auto* const block_map =\n        dynamic_cast<const GridToInertialMap<MeshDim>*>(\n            block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_grid_to_inertial_map);\n  }\n\n  const auto expected_grid_to_distorted_map =\n      create_grid_to_distorted_map<MeshDim>(outer_boundary, f_of_t_names);\n\n  const auto block_maps_grid_to_distorted =\n      time_dep_unique_ptr->block_maps_grid_to_distorted(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_grid_to_distorted) {\n    const auto* const block_map =\n        dynamic_cast<const GridToDistortedMap<MeshDim>*>(\n            block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_grid_to_distorted_map);\n  }\n\n  const auto expected_distorted_to_inertial_map =\n      create_distorted_to_inertial_map<MeshDim>(f_of_t_names);\n\n  const auto block_maps_distorted_to_inertial =\n      time_dep_unique_ptr->block_maps_distorted_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_distorted_to_inertial) {\n    const auto* const block_map =\n        dynamic_cast<const DistortedToInertialMap<MeshDim>*>(\n            block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_distorted_to_inertial_map);\n  }\n\n  // Test functions of time without expiration times\n  {\n    const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n    bool f_of_t_correct_number =\n        use_linear_scaling ? functions_of_time.size() == f_of_t_names.size() - 1\n                           : functions_of_time.size() == f_of_t_names.size();\n    REQUIRE(f_of_t_correct_number);\n    for (const auto& f_of_t_name : f_of_t_names) {\n      CHECK(functions_of_time.count(f_of_t_name) == 1);\n      CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n            std::numeric_limits<double>::infinity());\n    }\n  }\n  // Test functions of time with expiration times\n  {\n    const double init_expr_time = 5.0;\n    std::unordered_map<std::string, double> init_expr_times{};\n    for (auto& name : f_of_t_names) {\n      init_expr_times[name] = init_expr_time;\n    }\n    const auto functions_of_time =\n        time_dep_unique_ptr->functions_of_time(init_expr_times);\n    bool f_of_t_correct_number =\n        use_linear_scaling ? functions_of_time.size() == f_of_t_names.size() - 1\n                           : functions_of_time.size() == f_of_t_names.size();\n    REQUIRE(f_of_t_correct_number);\n    for (const auto& f_of_t_name : f_of_t_names) {\n      CHECK(functions_of_time.count(f_of_t_name) == 1);\n      CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n            init_expr_time);\n    }\n  }\n\n  const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n\n  // For a random point at a random time check that the values agree. This is to\n  // check that the internals were assigned the correct function of times.\n  TIME_DEPENDENCE_GENERATE_COORDS(make_not_null(&gen), MeshDim, -1.0, 1.0);\n\n  for (const auto& block_map : block_maps_grid_to_inertial) {\n    // We've checked equivalence above\n    // (CHECK(*block_map == expected_grid_to_inertial_map);), but have sometimes\n    // been burned by incorrect operator== implementations so we check that the\n    // mappings behave as expected.\n    const double time_offset = dist(gen) + 1.2;\n    CHECK_ITERABLE_APPROX(\n        expected_grid_to_inertial_map(\n            grid_coords_dv, initial_time + time_offset, functions_of_time),\n        (*block_map)(grid_coords_dv, initial_time + time_offset,\n                     functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_grid_to_inertial_map(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        (*block_map)(grid_coords_double, initial_time + time_offset,\n                     functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        *expected_grid_to_inertial_map.inverse(inertial_coords_double,\n                                               initial_time + time_offset,\n                                               functions_of_time),\n        *block_map->inverse(inertial_coords_double, initial_time + time_offset,\n                            functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_grid_to_inertial_map.inv_jacobian(\n            grid_coords_dv, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(grid_coords_dv, initial_time + time_offset,\n                                functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_grid_to_inertial_map.inv_jacobian(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(grid_coords_double, initial_time + time_offset,\n                                functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_grid_to_inertial_map.jacobian(\n            grid_coords_dv, initial_time + time_offset, functions_of_time),\n        block_map->jacobian(grid_coords_dv, initial_time + time_offset,\n                            functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_grid_to_inertial_map.jacobian(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->jacobian(grid_coords_double, initial_time + time_offset,\n                            functions_of_time));\n  }\n}\n\ntemplate <size_t MeshDim>\nvoid test_equivalence() {\n  ScalingAndZRotation<MeshDim> map0{\n      1.0, 2.4, 2.0, false, {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map1{\n      1.2, 2.4, 2.0, false, {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map2{\n      1.0, 2.4, 3.0, false, {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map3{\n      1.0, 2.4, 2.0, true, {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map4{\n      1.0, 2.4, 2.0, false, {{3.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map5{\n      1.0, 2.4, 2.0, false, {{1.0, 0.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map6{\n      1.0, 2.4, 2.0, false, {{1.0, 2.0}}, {{-2.0, 3.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map7{\n      1.0, 2.4, 2.0, false, {{1.0, 2.0}}, {{-1.0, 7.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map8{\n      1.0, 2.4, 2.0, false, {{1.0, 2.0}}, {{-1.0, 3.0}}, {{3.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map9{\n      1.0, 2.4, 2.0, false, {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 10.9}}};\n  ScalingAndZRotation<MeshDim> map10{\n      1.0, 2.4, 2.0, true, {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n  ScalingAndZRotation<MeshDim> map11{\n      1.0, 2.5, 2.0, false, {{1.0, 2.0}}, {{-1.0, 3.0}}, {{0.2, 0.9}}};\n\n  CHECK(map0 == map0);\n  CHECK_FALSE(map0 != map0);\n  CHECK(map0 != map1);\n  CHECK_FALSE(map0 == map1);\n  CHECK(map0 != map2);\n  CHECK_FALSE(map0 == map2);\n  CHECK(map0 != map3);\n  CHECK_FALSE(map0 == map3);\n  CHECK(map0 != map4);\n  CHECK_FALSE(map0 == map4);\n  CHECK(map0 != map5);\n  CHECK_FALSE(map0 == map5);\n  CHECK(map0 != map6);\n  CHECK_FALSE(map0 == map6);\n  CHECK(map0 != map7);\n  CHECK_FALSE(map0 == map7);\n  CHECK(map0 != map8);\n  CHECK_FALSE(map0 == map8);\n  CHECK(map0 != map9);\n  CHECK_FALSE(map0 == map9);\n  CHECK(map0 != map10);\n  CHECK_FALSE(map0 == map10);\n  CHECK(map0 != map11);\n  CHECK_FALSE(map0 == map11);\n}\n\n\ntemplate <size_t MeshDim>\nvoid test(const bool use_linear_scaling) {\n  const double initial_time = 1.3;\n  const double angular_velocity = 2.4;\n  const double outer_boundary = 10.4;\n  const std::array<double, 2> initial_expansion{{1.0, 1.0}};\n  const std::array<double, 2> velocity{{-0.1, 0.0}};\n  const std::array<double, 2> acceleration{{-0.05, 0.0}};\n  // These names must match the hard coded ones in Test_ScalingAndZRotation\n  const std::string f_of_t_name0 =\n      \"CubicScale\"s + (use_linear_scaling ? \"\" : \"A\");\n  const std::string f_of_t_name1 =\n      \"CubicScale\"s + (use_linear_scaling ? \"\" : \"B\");\n  // This name must match the hard coded one in RotationAboutZAxis\n  const std::string f_of_t_name2 = \"Rotation\";\n\n  const std::unique_ptr<\n      domain::creators::time_dependence::TimeDependence<MeshDim>>\n      time_dep = std::make_unique<ScalingAndZRotation<MeshDim>>(\n          initial_time, angular_velocity, outer_boundary, use_linear_scaling,\n          initial_expansion, velocity, acceleration);\n  test_impl(time_dep, initial_time, outer_boundary, use_linear_scaling,\n            {f_of_t_name0, f_of_t_name1, f_of_t_name2});\n  test_impl(time_dep->get_clone(), initial_time, outer_boundary,\n            use_linear_scaling, {f_of_t_name0, f_of_t_name1, f_of_t_name2});\n  const std::string linear_scaling = use_linear_scaling\n                                         ? \"  UseLinearScaling: true\\n\"\n                                         : \"  UseLinearScaling: false\\n\";\n  test_impl(\n      TestHelpers::test_creation<std::unique_ptr<TimeDependence<MeshDim>>>(\n          \"ScalingAndZRotation:\\n\"\n          \"  InitialTime: 1.3\\n\"\n          \"  AngularVelocity: 2.4\\n\"\n          \"  OuterBoundary: 10.4\\n\" +\n          linear_scaling +\n          \"  InitialExpansion: [1.0, 1.0]\\n\"\n          \"  Velocity: [-0.1, 0.0]\\n\"\n          \"  Acceleration: [-0.05, 0.0]\\n\"),\n      initial_time, outer_boundary, use_linear_scaling,\n      {f_of_t_name0, f_of_t_name1, f_of_t_name2});\n\n  test_equivalence<MeshDim>();\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Domain.Creators.TimeDependence.ScalingAndZRotation\",\n    \"[Domain][Unit]\") {\n  test<2>(true);\n  test<3>(true);\n  test<2>(false);\n  test<3>(false);\n}\n}  // namespace\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependence/Test_Shape.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/Creators/TimeDependence/Shape.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependence/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependence {\n\nnamespace {\nusing Shape = Shape<domain::ObjectLabel::None>;\n\nusing ShapeMap = domain::CoordinateMaps::TimeDependent::Shape;\nusing Identity = domain::CoordinateMaps::Identity<3>;\n\ntemplate <typename SourceFrame, typename TargetFrame>\nusing ConcreteMapSimple = domain::CoordinateMap<\n    SourceFrame, TargetFrame,\n    tmpl::conditional_t<std::is_same_v<SourceFrame, Frame::Grid>, ShapeMap,\n                        Identity>>;\n\nusing ConcreteMapCombined =\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, ShapeMap>;\n\nusing Transition =\n    domain::CoordinateMaps::ShapeMapTransitionFunctions::SphereTransition;\n\ntemplate <typename SourceFrame, typename TargetFrame>\nConcreteMapSimple<SourceFrame, TargetFrame> create_coord_map(\n    const std::string& f_of_t_name, const size_t l_max,\n    const std::array<double, 3>& center,\n    const double coefficient_truncation_limit,\n    std::unique_ptr<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                        ShapeMapTransitionFunction>\n        transition_func) {\n  (void)l_max;\n  if constexpr (std::is_same_v<SourceFrame, Frame::Grid>) {\n    return ConcreteMapSimple<SourceFrame, TargetFrame>{\n        {ShapeMap{center, coefficient_truncation_limit,\n                  transition_func->get_clone(), f_of_t_name}}};\n  } else {\n    (void)f_of_t_name;\n    (void)l_max;\n    (void)center;\n    (void)coefficient_truncation_limit;\n    (void)transition_func;\n    return ConcreteMapSimple<SourceFrame, TargetFrame>{Identity{}};\n  }\n}\n\nConcreteMapCombined create_coord_map_combined(\n    const std::string& f_of_t_name, const size_t l_max,\n    const std::array<double, 3>& center,\n    const double coefficient_truncation_limit,\n    std::unique_ptr<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                        ShapeMapTransitionFunction>\n        transition_func) {\n  (void)l_max;\n  return ConcreteMapCombined{ShapeMap{center, coefficient_truncation_limit,\n                                      transition_func->get_clone(),\n                                      f_of_t_name}};\n}\n\ntemplate <typename Frame>\nstd::array<double, 3> r_theta_phi(const tnsr::I<double, 3, Frame>& input) {\n  const double input_rho = sqrt(square(input.get(0)) + square(input.get(1)));\n  return {{magnitude(input).get(), atan2(input_rho, input.get(2)),\n           atan2(input.get(1), input.get(0))}};\n}\n\n// Takes an input point in Frame::Grid and an output point in Frame::Inertial\n// produced by a Shape CoordinateMap and compares it to an expected output\n// point corresponding to what one would obtain if an analytic mapping were\n// used instead of an expansion over spherical harmonics, as is used by the\n// Shape map. For an `l_max` of 16, this test can be expected to pass for any\n// random point mapped for a Shape map with dim'less spin magnitude < 0.65.\ntemplate <typename SourceFrame, typename TargetFrame>\nvoid test_r_theta_phi(const tnsr::I<double, 3, SourceFrame>& input,\n                      const tnsr::I<double, 3, TargetFrame>& output,\n                      const double inner_radius, const double outer_radius,\n                      const double mass, const std::array<double, 3>& spin,\n                      const std::array<double, 3>& center) {\n  auto input_centered =\n      make_with_value<tnsr::I<double, 3, SourceFrame>>(input, 0.0);\n  auto output_centered =\n      make_with_value<tnsr::I<double, 3, TargetFrame>>(output, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    input_centered.get(i) = input.get(i) - gsl::at(center, i);\n    output_centered.get(i) = output.get(i) - gsl::at(center, i);\n  }\n  const auto input_centered_spherical = r_theta_phi(input_centered);\n  const auto output_centered_spherical = r_theta_phi(output_centered);\n  const std::array<double, 2> input_theta_phi = {\n      {input_centered_spherical[1], input_centered_spherical[2]}};\n  CHECK(input_theta_phi[0] == approx(output_centered_spherical[1]));\n  CHECK(input_theta_phi[1] == approx(output_centered_spherical[2]));\n  const double kerr_schild_radius =\n      gr::Solutions::kerr_schild_radius_from_boyer_lindquist(\n          inner_radius, input_theta_phi, mass, spin)\n          .get();\n  const double radius = magnitude(input_centered).get();\n  const double transition_factor =\n      std::make_unique<Transition>(Transition{inner_radius, outer_radius})\n          ->operator()(std::array{radius, 0.0, 0.0}, std::nullopt);\n  const double expected_output_centered_spherical =\n      input_centered_spherical[0] *\n      (1.0 + transition_factor * (kerr_schild_radius - inner_radius));\n  CHECK(output_centered_spherical[0] ==\n        approx(expected_output_centered_spherical));\n}\n\ntemplate <typename SourceFrame, typename TargetFrame, typename Generator,\n          typename BlockMaps, typename ExpectedBlockMap>\nvoid test_maps(const gsl::not_null<Generator*> gen,\n               const std::unique_ptr<TimeDependence<3>>& time_dep_unique_ptr,\n               const double initial_time, const double inner_radius,\n               const double outer_radius, const double mass,\n               const std::array<double, 3>& spin,\n               const std::array<double, 3>& center, const BlockMaps& block_maps,\n               const ExpectedBlockMap& expected_block_map) {\n  // These are just so we can capture the frames\n  const std::string source_frame = pretty_type::name<SourceFrame>();\n  const std::string target_frame = pretty_type::name<TargetFrame>();\n  CAPTURE(source_frame);\n  CAPTURE(target_frame);\n  const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n\n  // For a random point at a random time check that the values agree. This is\n  // to check that the internals were assigned the correct function of times.\n  // The points are are drawn from the positive x/y/z quadrant such that their\n  // radii lie between inner_radius and outer_radius.\n  TIME_DEPENDENCE_GENERATE_COORDS(gen, 3, inner_radius / sqrt(3.0),\n                                  outer_radius / sqrt(3.0));\n  TIME_DEPENDENCE_GENERATE_DISTORTED_COORDS(gen, dist, 3);\n\n  tnsr::I<DataVector, 3, SourceFrame> source_coords_dv;\n  tnsr::I<double, 3, SourceFrame> source_coords_double;\n  if constexpr (std::is_same_v<SourceFrame, Frame::Grid>) {\n    source_coords_dv = grid_coords_dv;\n    source_coords_double = grid_coords_double;\n  } else {\n    source_coords_dv = distorted_coords_dv;\n    source_coords_double = distorted_coords_double;\n  }\n\n  tnsr::I<double, 3, TargetFrame> target_coords_double;\n  if constexpr (std::is_same_v<TargetFrame, Frame::Inertial>) {\n    target_coords_double = inertial_coords_double;\n  } else {\n    target_coords_double = distorted_coords_double;\n  }\n\n  for (const auto& block_map : block_maps) {\n    // We've checked equivalence above\n    // (CHECK(*block_map == expected_block_map);), but have sometimes been\n    // burned by incorrect operator== implementations so we check that the\n    // mappings behave as expected.\n    const double check_time = initial_time + dist(*gen) + 1.2;\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(source_coords_dv, check_time, functions_of_time),\n        (*block_map)(source_coords_dv, check_time, functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(source_coords_double, check_time, functions_of_time),\n        (*block_map)(source_coords_double, check_time, functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        *expected_block_map.inverse(target_coords_double, check_time,\n                                    functions_of_time),\n        *block_map->inverse(target_coords_double, check_time,\n                            functions_of_time));\n\n    CHECK_ITERABLE_APPROX(expected_block_map.inv_jacobian(\n                              source_coords_dv, check_time, functions_of_time),\n                          block_map->inv_jacobian(source_coords_dv, check_time,\n                                                  functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(source_coords_double, check_time,\n                                        functions_of_time),\n        block_map->inv_jacobian(source_coords_double, check_time,\n                                functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(source_coords_dv, check_time,\n                                    functions_of_time),\n        block_map->jacobian(source_coords_dv, check_time, functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(source_coords_double, check_time,\n                                    functions_of_time),\n        block_map->jacobian(source_coords_double, check_time,\n                            functions_of_time));\n    // If this is the case, then the map is the identity so we don't need to\n    // test this\n    if constexpr (not std::is_same_v<SourceFrame, Frame::Distorted>) {\n      // Initialize a non spherical shape and make sure the values match\n      // kerr map takes mass and spin, gives radius at different theta phis\n      // get modal coefficients from SPHEREPACK (ylms corresponding to r(theta,\n      // phi) keep in mind how this interacts with r==0, consider an assert.\n      const auto output_point =\n          (*block_map)(source_coords_double, check_time, functions_of_time);\n      test_r_theta_phi(source_coords_double, output_point, inner_radius,\n                       outer_radius, mass, spin, center);\n    }\n    // Avoid compiler warnings\n    (void)mass;\n    (void)spin;\n    (void)center;\n  }\n}\n\nvoid test(const std::unique_ptr<TimeDependence<3>>& time_dep_unique_ptr,\n          const double initial_time, const std::string& f_of_t_name,\n          const size_t l_max,\n          std::unique_ptr<domain::CoordinateMaps::ShapeMapTransitionFunctions::\n                              ShapeMapTransitionFunction>\n              transition_func,\n          const double coefficient_truncation_limit, const double inner_radius,\n          const double outer_radius, const double mass,\n          const std::array<double, 3>& spin,\n          const std::array<double, 3>& center) {\n  MAKE_GENERATOR(gen);\n  CAPTURE(initial_time);\n  CAPTURE(f_of_t_name);\n  CAPTURE(l_max);\n  CAPTURE(spin);\n  CAPTURE(center);\n\n  CHECK_FALSE(time_dep_unique_ptr->is_none());\n\n  // We downcast to the expected derived class to make sure that factory\n  // creation worked correctly. In order to maximize code reuse this check is\n  // done here as opposed to separately elsewhere.\n  const auto* const time_dep =\n      dynamic_cast<const Shape*>(time_dep_unique_ptr.get());\n  REQUIRE(time_dep != nullptr);\n\n  // Test coordinate maps\n  UniformCustomDistribution<size_t> dist_size_t{1, 10};\n  const size_t num_blocks = dist_size_t(gen);\n  CAPTURE(num_blocks);\n  const auto expected_block_map_grid_to_inertial = create_coord_map_combined(\n      f_of_t_name, l_max, center, coefficient_truncation_limit,\n      transition_func->get_clone());\n  const auto block_maps_grid_to_inertial =\n      time_dep_unique_ptr->block_maps_grid_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_grid_to_inertial) {\n    const auto* const block_map =\n        dynamic_cast<const ConcreteMapCombined*>(block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map_grid_to_inertial);\n  }\n\n  test_maps<Frame::Grid, Frame::Inertial>(\n      make_not_null(&gen), time_dep_unique_ptr, initial_time, inner_radius,\n      outer_radius, mass, spin, center, block_maps_grid_to_inertial,\n      expected_block_map_grid_to_inertial);\n\n  const auto expected_block_map_grid_to_distorted =\n      create_coord_map<Frame::Grid, Frame::Distorted>(\n          f_of_t_name, l_max, center, coefficient_truncation_limit,\n          transition_func->get_clone());\n  const auto block_maps_grid_to_distorted =\n      time_dep_unique_ptr->block_maps_grid_to_distorted(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_grid_to_distorted) {\n    const auto* const block_map =\n        dynamic_cast<const ConcreteMapSimple<Frame::Grid, Frame::Distorted>*>(\n            block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map_grid_to_distorted);\n  }\n\n  test_maps<Frame::Grid, Frame::Distorted>(\n      make_not_null(&gen), time_dep_unique_ptr, initial_time, inner_radius,\n      outer_radius, mass, spin, center, block_maps_grid_to_distorted,\n      expected_block_map_grid_to_distorted);\n\n  const auto expected_block_map_distorted_to_inertial =\n      create_coord_map<Frame::Distorted, Frame::Inertial>(\n          f_of_t_name, l_max, center, coefficient_truncation_limit,\n          transition_func->get_clone());\n  const auto block_maps_distorted_to_inertial =\n      time_dep_unique_ptr->block_maps_distorted_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_distorted_to_inertial) {\n    const auto* const block_map = dynamic_cast<\n        const ConcreteMapSimple<Frame::Distorted, Frame::Inertial>*>(\n        block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map_distorted_to_inertial);\n  }\n\n  test_maps<Frame::Distorted, Frame::Inertial>(\n      make_not_null(&gen), time_dep_unique_ptr, initial_time, inner_radius,\n      outer_radius, mass, spin, center, block_maps_distorted_to_inertial,\n      expected_block_map_distorted_to_inertial);\n\n  // Test functions of time without expiration times\n  {\n    const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n    REQUIRE(functions_of_time.size() == 2);\n    CHECK(functions_of_time.count(f_of_t_name) == 1);\n    CHECK(functions_of_time.count(\"Size\") == 1);\n    CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n          std::numeric_limits<double>::infinity());\n  }\n  // Test functions of time with expiration times\n  {\n    const double init_expr_time = 5.0;\n    std::unordered_map<std::string, double> init_expr_times{};\n    init_expr_times[f_of_t_name] = init_expr_time;\n    const auto functions_of_time =\n        time_dep_unique_ptr->functions_of_time(init_expr_times);\n    REQUIRE(functions_of_time.size() == 2);\n    CHECK(functions_of_time.count(f_of_t_name) == 1);\n    CHECK(functions_of_time.count(\"Size\") == 1);\n    CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n          init_expr_time);\n  }\n}\n\nvoid test_all() {\n  constexpr double initial_time{1.3};\n  // l_max of 16 needed to pass tests using `approx` as tests\n  // compare ylm output with analytic solution.\n  constexpr size_t l_max{16};\n  constexpr double mass{1.0};\n  constexpr double coefficient_truncation_limit{0.0};\n  const std::array<double, 3> spin{{0.1, 0.4, -0.5}};\n  const std::array<double, 3> center{{-0.02, 0.013, 0.024}};\n  const double inner_radius = 2.0;\n  const double outer_radius = 100;\n  const Transition sphere_transition{inner_radius, outer_radius};\n  // This name must match the hard coded one in Shape\n  const std::string f_of_t_name = \"Shape\";\n\n  const std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n      time_dep = std::make_unique<Shape>(initial_time, l_max, mass, spin,\n                                         center, coefficient_truncation_limit,\n                                         inner_radius, outer_radius);\n  test(time_dep, initial_time, f_of_t_name, l_max,\n       std::make_unique<Transition>(sphere_transition),\n       coefficient_truncation_limit, inner_radius, outer_radius, mass, spin,\n       center);\n  test(time_dep->get_clone(), initial_time, f_of_t_name, l_max,\n       std::make_unique<Transition>(sphere_transition),\n       coefficient_truncation_limit, inner_radius, outer_radius, mass, spin,\n       center);\n\n  test(TestHelpers::test_creation<std::unique_ptr<TimeDependence<3>>>(\n           \"Shape:\\n\"\n           \"  InitialTime: 1.3\\n\"\n           \"  LMax: 16\\n\"\n           \"  Mass: 1.0\\n\"\n           \"  Spin: [0.1, 0.4, -0.5]\\n\"\n           \"  Center: [-0.02, 0.013, 0.024]\\n\"\n           \"  CoefficientTruncationLimit: 0.0\\n\"\n           \"  InnerRadius: 2.0\\n\"\n           \"  OuterRadius: 100.0\\n\"),\n       initial_time, f_of_t_name, l_max,\n       std::make_unique<Transition>(sphere_transition),\n       coefficient_truncation_limit, inner_radius, outer_radius, mass, spin,\n       center);\n}\n\nvoid test_equivalence() {\n  const double mass = 1.0;\n  const std::array<double, 3> spin1 = {{0.0, 0.5, 0.1}};\n  const std::array<double, 3> spin2 = {{0.3, 0.2, 0.0}};\n  const std::array<double, 3> center1 = {{-0.2, 1.3, 2.4}};\n  const std::array<double, 3> center2 = {{0.2, -1.3, 2.4}};\n  const double inner_radius = 1.0;\n  const double outer_radius1 = 10.0;\n  const double outer_radius2 = 20.0;\n  const double coefficient_truncation_limit1 = 0.0;\n  const double coefficient_truncation_limit2 = 1.0e-4;\n\n  Shape sc0{1.0,          4,\n            mass,         spin1,\n            center1,      coefficient_truncation_limit1,\n            inner_radius, outer_radius1};\n  Shape sc1{1.0,          4,\n            mass,         spin1,\n            center1,      coefficient_truncation_limit1,\n            inner_radius, outer_radius1};\n  Shape sc2{1.0,          4,\n            mass,         spin2,\n            center1,      coefficient_truncation_limit1,\n            inner_radius, outer_radius1};\n  Shape sc3{1.0,          4,\n            mass,         spin2,\n            center1,      coefficient_truncation_limit1,\n            inner_radius, outer_radius1};\n  Shape sc4{1.0,          4,\n            mass,         spin1,\n            center2,      coefficient_truncation_limit2,\n            inner_radius, outer_radius2};\n  Shape sc5{1.0,          4,\n            mass,         spin1,\n            center2,      coefficient_truncation_limit2,\n            inner_radius, outer_radius2};\n  Shape sc6{1.0,          4,\n            mass,         spin2,\n            center2,      coefficient_truncation_limit2,\n            inner_radius, outer_radius2};\n  Shape sc7{1.0,          4,\n            mass,         spin2,\n            center2,      coefficient_truncation_limit2,\n            inner_radius, outer_radius2};\n\n  CHECK(sc0 == sc0);\n  CHECK_FALSE(sc0 != sc0);\n  CHECK(sc0 == sc1);\n  CHECK_FALSE(sc0 != sc1);\n  CHECK(sc0 != sc2);\n  CHECK_FALSE(sc0 == sc2);\n  CHECK(sc0 != sc3);\n  CHECK_FALSE(sc0 == sc3);\n  CHECK(sc0 != sc4);\n  CHECK_FALSE(sc0 == sc4);\n  CHECK(sc0 != sc5);\n  CHECK_FALSE(sc0 == sc5);\n  CHECK(sc0 != sc6);\n  CHECK_FALSE(sc0 == sc6);\n  CHECK(sc0 != sc7);\n  CHECK_FALSE(sc0 == sc7);\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<std::unique_ptr<TimeDependence<3>>>(\n          \"Shape:\\n\"\n          \"  InitialTime: 1.3\\n\"\n          \"  LMax: 4\\n\"\n          \"  CoefficientTruncationLimit: 0.0\\n\"\n          \"  Mass: 1.0\\n\"\n          \"  Spin: [0.0, 1.0, 0.1]\\n\"\n          \"  Center: [-0.01, 0.02, 0.01]\\n\"\n          \"  InnerRadius: 1.0\\n\"\n          \"  OuterRadius: 10.0\\n\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to create a Shape TimeDependence, but the \"\n          \"magnitude of the spin\"));\n\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<std::unique_ptr<TimeDependence<3>>>(\n          \"Shape:\\n\"\n          \"  InitialTime: 1.3\\n\"\n          \"  LMax: 4\\n\"\n          \"  CoefficientTruncationLimit: 0.0\\n\"\n          \"  Mass: 1.0\\n\"\n          \"  Spin: [0.0, 1.0, 0.1]\\n\"\n          \"  Center: [-0.01, 0.02, 0.01]\\n\"\n          \"  InnerRadius: 10.0\\n\"\n          \"  OuterRadius: 1.0\\n\"),\n      Catch::Matchers::ContainsSubstring(\n          \"The maximum radius must be greater than the minimum radius\"));\n\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<std::unique_ptr<TimeDependence<3>>>(\n          \"Shape:\\n\"\n          \"  InitialTime: 1.3\\n\"\n          \"  CoefficientTruncationLimit: 0.0\\n\"\n          \"  LMax: 4\\n\"\n          \"  Mass: -1.0\\n\"\n          \"  Spin: [0.0, 1.0, 0.1]\\n\"\n          \"  Center: [-0.01, 0.02, 0.01]\\n\"\n          \"  InnerRadius: 1.0\\n\"\n          \"  OuterRadius: 10.0\\n\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to create a Shape TimeDependence, but the mass\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependence.Shape\",\n                  \"[Domain][Unit]\") {\n  test_equivalence();\n  test_errors();\n\n  test_all();\n}\n\n}  // namespace\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependence/Test_SphericalCompression.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/Creators/TimeDependence/SphericalCompression.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependence/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependence {\n\nnamespace {\nusing SphericalCompressionMap =\n    domain::CoordinateMaps::TimeDependent::SphericalCompression<false>;\n\nusing ConcreteMap = domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                          SphericalCompressionMap>;\n\nConcreteMap create_coord_map(const std::string& f_of_t_name,\n                             const double min_radius, const double max_radius,\n                             const std::array<double, 3>& center) {\n  return ConcreteMap{\n      {SphericalCompressionMap{f_of_t_name, min_radius, max_radius, center}}};\n}\n\nvoid test(const std::unique_ptr<TimeDependence<3>>& time_dep_unique_ptr,\n          const double initial_time, const std::string& f_of_t_name,\n          const double min_radius, const double max_radius,\n          const std::array<double, 3>& center) {\n  MAKE_GENERATOR(gen);\n  CAPTURE(initial_time);\n  CAPTURE(f_of_t_name);\n  CAPTURE(min_radius);\n  CAPTURE(max_radius);\n  CAPTURE(center);\n\n  CHECK_FALSE(time_dep_unique_ptr->is_none());\n\n  // We downcast to the expected derived class to make sure that factory\n  // creation worked correctly. In order to maximize code reuse this check is\n  // done here as opposed to separately elsewhere.\n  const auto* const time_dep =\n      dynamic_cast<const SphericalCompression*>(time_dep_unique_ptr.get());\n  REQUIRE(time_dep != nullptr);\n\n  // Test coordinate maps\n  UniformCustomDistribution<size_t> dist_size_t{1, 10};\n  const size_t num_blocks = dist_size_t(gen);\n  CAPTURE(num_blocks);\n\n  const auto expected_block_map =\n      create_coord_map(f_of_t_name, min_radius, max_radius, center);\n\n  const auto block_maps =\n      time_dep_unique_ptr->block_maps_grid_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps) {\n    const auto* const block_map =\n        dynamic_cast<const ConcreteMap*>(block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map);\n  }\n\n  // Check that maps involving distorted frame are empty.\n  CHECK(time_dep_unique_ptr->block_maps_grid_to_distorted(1)[0].get() ==\n        nullptr);\n  CHECK(time_dep_unique_ptr->block_maps_distorted_to_inertial(1)[0].get() ==\n        nullptr);\n\n  // Test functions of time without expiration times\n  {\n    const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n    REQUIRE(functions_of_time.size() == 1);\n    CHECK(functions_of_time.count(f_of_t_name) == 1);\n    CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n          std::numeric_limits<double>::infinity());\n  }\n  // Test functions of time with expiration times\n  {\n    const double init_expr_time = 5.0;\n    std::unordered_map<std::string, double> init_expr_times{};\n    init_expr_times[f_of_t_name] = init_expr_time;\n    const auto functions_of_time =\n        time_dep_unique_ptr->functions_of_time(init_expr_times);\n    REQUIRE(functions_of_time.size() == 1);\n    CHECK(functions_of_time.count(f_of_t_name) == 1);\n    CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n          init_expr_time);\n  }\n\n  const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n\n  // For a random point at a random time check that the values agree. This is\n  // to check that the internals were assigned the correct function of times.\n  // The points are are drawn from the positive x/y/z quadrant in a that\n  // guarantees that they are in the region where the map is valid (min_radius\n  // <= radius <= max_radius), provided that min_radius is sufficiently small\n  // compared to max_radius. This could be generalized, but in practice,\n  // this map will not be used in cases where min_radius and max_radius\n  // are very close to each other.\n  TIME_DEPENDENCE_GENERATE_COORDS(make_not_null(&gen), 3, min_radius * 1.001,\n                                  max_radius * 0.5);\n\n  for (const auto& block_map : block_maps) {\n    // We've checked equivalence above\n    // (CHECK(*block_map == expected_block_map);), but have sometimes been\n    // burned by incorrect operator== implementations so we check that the\n    // mappings behave as expected.\n    const double time_offset = dist(gen) + 1.2;\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(grid_coords_dv, initial_time + time_offset,\n                           functions_of_time),\n        (*block_map)(grid_coords_dv, initial_time + time_offset,\n                     functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(grid_coords_double, initial_time + time_offset,\n                           functions_of_time),\n        (*block_map)(grid_coords_double, initial_time + time_offset,\n                     functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        *expected_block_map.inverse(inertial_coords_double,\n                                    initial_time + time_offset,\n                                    functions_of_time),\n        *block_map->inverse(inertial_coords_double, initial_time + time_offset,\n                            functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(\n            grid_coords_dv, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(grid_coords_dv, initial_time + time_offset,\n                                functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(grid_coords_double, initial_time + time_offset,\n                                functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(grid_coords_dv, initial_time + time_offset,\n                                    functions_of_time),\n        block_map->jacobian(grid_coords_dv, initial_time + time_offset,\n                            functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(\n            grid_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->jacobian(grid_coords_double, initial_time + time_offset,\n                            functions_of_time));\n  }\n}\n\nvoid test_equivalence() {\n  SphericalCompression sc0{1.0, 0.4, 4.0, {{-0.2, 1.3, 2.4}}, 3.5, -4.6, 5.7};\n  SphericalCompression sc1{1.0, 0.4, 4.0, {{-0.2, 1.3, 2.4}}, 3.5, -4.6, 5.7};\n  SphericalCompression sc2{1.0, 0.3, 4.0, {{-0.2, 1.3, 2.4}}, 3.5, -4.6, 5.7};\n  SphericalCompression sc3{1.0, 0.4, 4.1, {{-0.2, 1.3, 2.4}}, 3.5, -4.6, 5.7};\n  SphericalCompression sc4{1.0, 0.4, 4.0, {{0.2, 1.3, 2.4}}, 3.5, -4.6, 5.7};\n  SphericalCompression sc5{1.0, 0.4, 4.0, {{-0.2, 1.3, 2.4}}, 3.8, -4.6, 5.7};\n  SphericalCompression sc6{1.0, 0.4, 4.0, {{-0.2, 1.3, 2.4}}, 3.5, 4.6, 5.7};\n  SphericalCompression sc7{1.0, 0.4, 4.0, {{-0.2, 1.3, 2.4}}, 3.5, -4.6, 5.9};\n\n  CHECK(sc0 == sc0);\n  CHECK_FALSE(sc0 != sc0);\n  CHECK(sc0 == sc1);\n  CHECK_FALSE(sc0 != sc1);\n  CHECK(sc0 != sc2);\n  CHECK_FALSE(sc0 == sc2);\n  CHECK(sc0 != sc3);\n  CHECK_FALSE(sc0 == sc3);\n  CHECK(sc0 != sc4);\n  CHECK_FALSE(sc0 == sc4);\n  CHECK(sc0 != sc5);\n  CHECK_FALSE(sc0 == sc5);\n  CHECK(sc0 != sc6);\n  CHECK_FALSE(sc0 == sc6);\n  CHECK(sc0 != sc7);\n  CHECK_FALSE(sc0 == sc7);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependence.SphericalCompression\",\n                  \"[Domain][Unit]\") {\n  constexpr double initial_time{1.3};\n  constexpr double min_radius{0.4};\n  constexpr double max_radius{4.0};\n  const std::array<double, 3> center{{-0.02, 0.013, 0.024}};\n  constexpr double initial_value{1.0};\n  constexpr double initial_velocity{-0.1};\n  constexpr double initial_acceleration{0.01};\n  // This name must match the hard coded one in SphericalCompression\n  const std::string f_of_t_name = \"Size\";\n\n  const std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n      time_dep = std::make_unique<SphericalCompression>(\n          initial_time, min_radius, max_radius, center, initial_value,\n          initial_velocity, initial_acceleration);\n  test(time_dep, initial_time, f_of_t_name, min_radius, max_radius, center);\n  test(time_dep->get_clone(), initial_time, f_of_t_name, min_radius, max_radius,\n       center);\n\n  test(TestHelpers::test_creation<std::unique_ptr<TimeDependence<3>>>(\n           \"SphericalCompression:\\n\"\n           \"  InitialTime: 1.3\\n\"\n           \"  MinRadius: 0.4\\n\"\n           \"  MaxRadius: 4.0\\n\"\n           \"  Center: [-0.02, 0.013, 0.024]\\n\"\n           \"  InitialValue: 1.0\\n\"\n           \"  InitialVelocity: -0.1\\n\"\n           \"  InitialAcceleration: 0.01\\n\"),\n       initial_time, f_of_t_name, min_radius, max_radius, center);\n\n  test_equivalence();\n\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<std::unique_ptr<TimeDependence<3>>>(\n          \"SphericalCompression:\\n\"\n          \"  InitialTime: 1.3\\n\"\n          \"  MinRadius: 4.0\\n\"\n          \"  MaxRadius: 0.4\\n\"\n          \"  Center: [-0.01, 0.02, 0.01]\\n\"\n          \"  InitialValue: 3.5\\n\"\n          \"  InitialVelocity: -4.6\\n\"\n          \"  InitialAcceleration: 5.7\\n\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to create a SphericalCompression TimeDependence\"));\n}\n}  // namespace\n\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependence/Test_UniformTranslation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependence/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain::creators::time_dependence {\n\nnamespace {\ntemplate <size_t MeshDim>\nusing Translation = domain::CoordinateMaps::TimeDependent::Translation<MeshDim>;\n\ntemplate <size_t MeshDim, typename InputFrame = Frame::Grid,\n          typename OutputFrame = Frame::Inertial>\nusing ConcreteMapSimple =\n    domain::CoordinateMap<InputFrame, OutputFrame, Translation<MeshDim>>;\n\ntemplate <size_t MeshDim>\nusing ConcreteMapCombined =\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, Translation<MeshDim>,\n                          Translation<MeshDim>>;\n\ntemplate <size_t MeshDim, typename InputFrame = Frame::Grid,\n          typename OutputFrame = Frame::Inertial>\nConcreteMapSimple<MeshDim, InputFrame, OutputFrame> create_coord_map(\n    const std::string& f_of_t_name) {\n  return ConcreteMapSimple<MeshDim, InputFrame, OutputFrame>{\n      Translation<MeshDim>{f_of_t_name}};\n}\n\ntemplate <size_t MeshDim>\nConcreteMapCombined<MeshDim> create_coord_map_grid_to_inertial(\n    const std::string& f_of_t_grid_to_distorted,\n    const std::string& f_of_t_distorted_to_inertial) {\n  return ConcreteMapCombined<MeshDim>{\n      Translation<MeshDim>{f_of_t_grid_to_distorted},\n      Translation<MeshDim>{f_of_t_distorted_to_inertial}};\n}\n\ntemplate <size_t MeshDim, typename InputFrame, typename OutputFrame,\n          typename BlockMaps, typename BlockMap, typename Gen>\nvoid test_maps(\n    const std::unique_ptr<TimeDependence<MeshDim>>& time_dep_unique_ptr,\n    const double initial_time,\n    const tnsr::I<DataVector, MeshDim, InputFrame>& input_coords_dv,\n    const tnsr::I<double, MeshDim, InputFrame>& input_coords_double,\n    const tnsr::I<double, MeshDim, OutputFrame>& output_coords_double,\n    const BlockMaps& block_maps, const BlockMap& expected_block_map,\n    std::uniform_real_distribution<double>& dist, Gen& gen) {\n  const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n\n  for (const auto& block_map : block_maps) {\n    // We've checked equivalence above\n    // (CHECK(*black_map == expected_block_map);), but have sometimes been\n    // burned by incorrect operator== implementations so we check that the\n    // mappings behave as expected.\n    const double time_offset = dist(gen) + 1.2;\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(input_coords_dv, initial_time + time_offset,\n                           functions_of_time),\n        (*block_map)(input_coords_dv, initial_time + time_offset,\n                     functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map(input_coords_double, initial_time + time_offset,\n                           functions_of_time),\n        (*block_map)(input_coords_double, initial_time + time_offset,\n                     functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        *expected_block_map.inverse(output_coords_double,\n                                    initial_time + time_offset,\n                                    functions_of_time),\n        *block_map->inverse(output_coords_double, initial_time + time_offset,\n                            functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(\n            input_coords_dv, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(input_coords_dv, initial_time + time_offset,\n                                functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.inv_jacobian(\n            input_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->inv_jacobian(input_coords_double, initial_time + time_offset,\n                                functions_of_time));\n\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(input_coords_dv, initial_time + time_offset,\n                                    functions_of_time),\n        block_map->jacobian(input_coords_dv, initial_time + time_offset,\n                            functions_of_time));\n    CHECK_ITERABLE_APPROX(\n        expected_block_map.jacobian(\n            input_coords_double, initial_time + time_offset, functions_of_time),\n        block_map->jacobian(input_coords_double, initial_time + time_offset,\n                            functions_of_time));\n  }\n}\n\ntemplate <size_t MeshDim>\nvoid test_common(\n    const std::unique_ptr<TimeDependence<MeshDim>>& time_dep_unique_ptr) {\n  CHECK_FALSE(time_dep_unique_ptr->is_none());\n\n  // We downcast to the expected derived class to make sure that factory\n  // creation worked correctly. In order to maximize code reuse this check is\n  // done here as opposed to separately elsewhere.\n  const auto* const time_dep = dynamic_cast<const UniformTranslation<MeshDim>*>(\n      time_dep_unique_ptr.get());\n  REQUIRE(time_dep != nullptr);\n}\n\ntemplate <size_t MeshDim>\nvoid test_f_of_time(\n    const std::unique_ptr<TimeDependence<MeshDim>>& time_dep_unique_ptr,\n    const std::vector<std::string>& f_of_t_names) {\n  // Test without expiration times\n  const auto functions_of_time = time_dep_unique_ptr->functions_of_time();\n  REQUIRE(functions_of_time.size() == f_of_t_names.size());\n  for (const auto& f_of_t_name : f_of_t_names) {\n    CHECK(functions_of_time.count(f_of_t_name) == 1);\n    CHECK(functions_of_time.at(f_of_t_name)->time_bounds()[1] ==\n          std::numeric_limits<double>::infinity());\n  }\n\n  // Test with expiration times\n  const double init_expr_time = 5.0;\n  std::unordered_map<std::string, double> init_expr_times{};\n  for (const auto& f_of_t_name : f_of_t_names) {\n    init_expr_times[f_of_t_name] = init_expr_time;\n  }\n  const auto functions_of_time_with_expr_times =\n      time_dep_unique_ptr->functions_of_time(init_expr_times);\n  REQUIRE(functions_of_time_with_expr_times.size() == f_of_t_names.size());\n  for (const auto& f_of_t_name : f_of_t_names) {\n    CHECK(functions_of_time_with_expr_times.count(f_of_t_name) == 1);\n    CHECK(functions_of_time_with_expr_times.at(f_of_t_name)->time_bounds()[1] ==\n          init_expr_time);\n  }\n}\n\ntemplate <size_t MeshDim>\nvoid test(const std::unique_ptr<TimeDependence<MeshDim>>& time_dep_unique_ptr,\n          const double initial_time, const std::string& f_of_t_name) {\n  MAKE_GENERATOR(gen);\n  CAPTURE(initial_time);\n  CAPTURE(f_of_t_name);\n\n  test_common(time_dep_unique_ptr);\n\n  // Test coordinate maps\n  UniformCustomDistribution<size_t> dist_size_t{1, 10};\n  const size_t num_blocks = dist_size_t(gen);\n  CAPTURE(num_blocks);\n\n  const auto expected_block_map = create_coord_map<MeshDim>(f_of_t_name);\n\n  const auto block_maps =\n      time_dep_unique_ptr->block_maps_grid_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps) {\n    const auto* const block_map =\n        dynamic_cast<const ConcreteMapSimple<MeshDim>*>(\n            block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map);\n  }\n\n  // Check that maps involving distorted frame are empty.\n  CHECK(time_dep_unique_ptr->block_maps_grid_to_distorted(1)[0].get() ==\n        nullptr);\n  CHECK(time_dep_unique_ptr->block_maps_distorted_to_inertial(1)[0].get() ==\n        nullptr);\n\n  test_f_of_time(time_dep_unique_ptr, {f_of_t_name});\n\n  // For a random point at a random time check that the values agree. This is\n  // to check that the internals were assigned the correct function of times.\n  TIME_DEPENDENCE_GENERATE_COORDS(make_not_null(&gen), MeshDim, -1.0, 1.0);\n\n  test_maps(time_dep_unique_ptr, initial_time, grid_coords_dv,\n            grid_coords_double, inertial_coords_double, block_maps,\n            expected_block_map, dist, gen);\n}\n\ntemplate <size_t MeshDim>\nvoid test_with_distorted_frame(\n    const std::unique_ptr<TimeDependence<MeshDim>>& time_dep_unique_ptr,\n    const double initial_time, const std::string& f_of_t_grid_to_distorted,\n    const std::string& f_of_t_distorted_to_inertial) {\n  MAKE_GENERATOR(gen);\n  CAPTURE(initial_time);\n  CAPTURE(f_of_t_grid_to_distorted);\n  CAPTURE(f_of_t_distorted_to_inertial);\n\n  test_common(time_dep_unique_ptr);\n\n  // Test coordinate maps\n  UniformCustomDistribution<size_t> dist_size_t{1, 10};\n  const size_t num_blocks = dist_size_t(gen);\n  CAPTURE(num_blocks);\n\n  const auto expected_block_map_grid_to_inertial =\n      create_coord_map_grid_to_inertial<MeshDim>(f_of_t_grid_to_distorted,\n                                                 f_of_t_distorted_to_inertial);\n  const auto block_maps_grid_to_inertial =\n      time_dep_unique_ptr->block_maps_grid_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_grid_to_inertial) {\n    const auto* const block_map =\n        dynamic_cast<const ConcreteMapCombined<MeshDim>*>(\n            block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map_grid_to_inertial);\n  }\n\n  const auto expected_block_map_grid_to_distorted =\n      create_coord_map<MeshDim, Frame::Grid, Frame::Distorted>(\n          f_of_t_grid_to_distorted);\n  const auto block_maps_grid_to_distorted =\n      time_dep_unique_ptr->block_maps_grid_to_distorted(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_grid_to_distorted) {\n    const auto* const block_map = dynamic_cast<\n        const ConcreteMapSimple<MeshDim, Frame::Grid, Frame::Distorted>*>(\n        block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map_grid_to_distorted);\n  }\n\n  const auto expected_block_map_distorted_to_inertial =\n      create_coord_map<MeshDim, Frame::Distorted, Frame::Inertial>(\n          f_of_t_distorted_to_inertial);\n  const auto block_maps_distorted_to_inertial =\n      time_dep_unique_ptr->block_maps_distorted_to_inertial(num_blocks);\n  for (const auto& block_map_unique_ptr : block_maps_distorted_to_inertial) {\n    const auto* const block_map = dynamic_cast<\n        const ConcreteMapSimple<MeshDim, Frame::Distorted, Frame::Inertial>*>(\n        block_map_unique_ptr.get());\n    REQUIRE(block_map != nullptr);\n    CHECK(*block_map == expected_block_map_distorted_to_inertial);\n  }\n\n  test_f_of_time(time_dep_unique_ptr,\n                 {f_of_t_grid_to_distorted, f_of_t_distorted_to_inertial});\n\n  // For a random point at a random time check that the values agree. This is\n  // to check that the internals were assigned the correct function of times.\n  TIME_DEPENDENCE_GENERATE_COORDS(make_not_null(&gen), MeshDim, -1.0, 1.0);\n\n  test_maps(time_dep_unique_ptr, initial_time, grid_coords_dv,\n            grid_coords_double, inertial_coords_double,\n            block_maps_grid_to_inertial, expected_block_map_grid_to_inertial,\n            dist, gen);\n\n  TIME_DEPENDENCE_GENERATE_DISTORTED_COORDS(make_not_null(&gen), dist, MeshDim);\n\n  test_maps(time_dep_unique_ptr, initial_time, grid_coords_dv,\n            grid_coords_double, distorted_coords_double,\n            block_maps_grid_to_distorted, expected_block_map_grid_to_distorted,\n            dist, gen);\n\n  test_maps(time_dep_unique_ptr, initial_time, distorted_coords_dv,\n            distorted_coords_double, inertial_coords_double,\n            block_maps_distorted_to_inertial,\n            expected_block_map_distorted_to_inertial, dist, gen);\n}\n\nvoid test_equivalence() {\n  {\n    UniformTranslation<1> ut0{1.0, {{2.0}}};\n    UniformTranslation<1> ut1{1.2, {{2.0}}};\n    UniformTranslation<1> ut2{1.0, {{3.0}}};\n    CHECK(ut0 == ut0);\n    CHECK_FALSE(ut0 != ut0);\n    CHECK(ut0 != ut1);\n    CHECK_FALSE(ut0 == ut1);\n    CHECK(ut0 != ut2);\n    CHECK_FALSE(ut0 == ut2);\n  }\n  {\n    UniformTranslation<2> ut0{1.0, {{2.0, 4.0}}};\n    UniformTranslation<2> ut1{1.2, {{2.0, 4.0}}};\n    UniformTranslation<2> ut2{1.0, {{3.0, 4.0}}};\n    UniformTranslation<2> ut3{1.0, {{2.0, 5.0}}};\n    CHECK(ut0 == ut0);\n    CHECK_FALSE(ut0 != ut0);\n    CHECK(ut0 != ut1);\n    CHECK_FALSE(ut0 == ut1);\n    CHECK(ut0 != ut2);\n    CHECK_FALSE(ut0 == ut2);\n    CHECK(ut0 != ut3);\n    CHECK_FALSE(ut0 == ut3);\n  }\n  {\n    UniformTranslation<3> ut0{1.0, {{2.0, 4.0, 6.0}}};\n    UniformTranslation<3> ut1{1.2, {{2.0, 4.0, 6.0}}};\n    UniformTranslation<3> ut2{1.0, {{3.0, 4.0, 6.0}}};\n    UniformTranslation<3> ut3{1.0, {{2.0, 5.0, 6.0}}};\n    UniformTranslation<3> ut4{1.0, {{2.0, 4.0, 7.0}}};\n    CHECK(ut0 == ut0);\n    CHECK_FALSE(ut0 != ut0);\n    CHECK(ut0 != ut1);\n    CHECK_FALSE(ut0 == ut1);\n    CHECK(ut0 != ut2);\n    CHECK_FALSE(ut0 == ut2);\n    CHECK(ut0 != ut3);\n    CHECK_FALSE(ut0 == ut3);\n    CHECK(ut0 != ut4);\n    CHECK_FALSE(ut0 == ut4);\n  }\n\n  // With distorted frame\n  {\n    UniformTranslation<3> ut0{1.0, {{2.0, 4.0, 6.0}}, {{1.0, 2.0, 3.0}}};\n    UniformTranslation<3> ut1{1.0, {{2.0, 4.0, 6.0}}};\n    UniformTranslation<3> ut2{1.0, {{3.0, 4.0, 6.0}}, {{1.0, 2.0, 3.0}}};\n    UniformTranslation<3> ut3{1.0, {{2.0, 5.0, 6.0}}, {{1.0, 2.0, 3.0}}};\n    UniformTranslation<3> ut4{1.0, {{2.0, 4.0, 6.0}}, {{1.0, 3.0, 2.0}}};\n    CHECK(ut0 == ut0);\n    CHECK_FALSE(ut0 != ut0);\n    CHECK(ut0 != ut1);\n    CHECK_FALSE(ut0 == ut1);\n    CHECK(ut0 != ut2);\n    CHECK_FALSE(ut0 == ut2);\n    CHECK(ut0 != ut3);\n    CHECK_FALSE(ut0 == ut3);\n    CHECK(ut0 != ut4);\n    CHECK_FALSE(ut0 == ut4);\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_with_distorted_frame_driver(\n    const std::array<double, Dim>& velocity_grid_to_distorted,\n    const std::array<double, Dim>& velocity_distorted_to_inertial) {\n  const double initial_time = 1.3;\n\n  // This name must match the hard coded one in UniformTranslation\n  const std::string f_of_t_grid_to_distorted = \"Translation\";\n  const std::string f_of_t_distorted_to_inertial =\n      \"TranslationDistortedToInertial\";\n  const std::unique_ptr<domain::creators::time_dependence::TimeDependence<Dim>>\n      time_dep = std::make_unique<UniformTranslation<Dim>>(\n          initial_time, velocity_grid_to_distorted,\n          velocity_distorted_to_inertial);\n  test_with_distorted_frame(time_dep, initial_time, f_of_t_grid_to_distorted,\n                            f_of_t_distorted_to_inertial);\n  test_with_distorted_frame(time_dep->get_clone(), initial_time,\n                            f_of_t_grid_to_distorted,\n                            f_of_t_distorted_to_inertial);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependence.UniformTranslation\",\n                  \"[Domain][Unit]\") {\n  const double initial_time = 1.3;\n\n  {\n    // 1d\n    const std::array<double, 1> velocity{{2.4}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    const std::unique_ptr<domain::creators::time_dependence::TimeDependence<1>>\n        time_dep =\n            std::make_unique<UniformTranslation<1>>(initial_time, velocity);\n    test(time_dep, initial_time, f_of_t_name);\n    test(time_dep->get_clone(), initial_time, f_of_t_name);\n\n    test(TestHelpers::test_creation<std::unique_ptr<TimeDependence<1>>>(\n             \"UniformTranslation:\\n\"\n             \"  InitialTime: 1.3\\n\"\n             \"  Velocity: [2.4]\\n\"),\n         initial_time, f_of_t_name);\n  }\n\n  {\n    // 2d\n    const std::array<double, 2> velocity{{2.4, 3.1}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    const std::unique_ptr<domain::creators::time_dependence::TimeDependence<2>>\n        time_dep =\n            std::make_unique<UniformTranslation<2>>(initial_time, velocity);\n    test(time_dep, initial_time, f_of_t_name);\n    test(time_dep->get_clone(), initial_time, f_of_t_name);\n\n    test(TestHelpers::test_creation<std::unique_ptr<TimeDependence<2>>>(\n             \"UniformTranslation:\\n\"\n             \"  InitialTime: 1.3\\n\"\n             \"  Velocity: [2.4, 3.1]\\n\"),\n         initial_time, f_of_t_name);\n  }\n\n  {\n    // 3d\n    const std::array<double, 3> velocity{{2.4, 3.1, -1.2}};\n    // This name must match the hard coded one in UniformTranslation\n    const std::string f_of_t_name = \"Translation\";\n    const std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dep =\n            std::make_unique<UniformTranslation<3>>(initial_time, velocity);\n    test(time_dep, initial_time, f_of_t_name);\n    test(time_dep->get_clone(), initial_time, f_of_t_name);\n\n    test(TestHelpers::test_creation<std::unique_ptr<TimeDependence<3>>>(\n             \"UniformTranslation:\\n\"\n             \"  InitialTime: 1.3\\n\"\n             \"  Velocity: [2.4, 3.1, -1.2]\\n\"),\n         initial_time, f_of_t_name);\n  }\n\n  test_with_distorted_frame_driver<1>({{2.4}}, {{3.3}});\n  test_with_distorted_frame_driver<2>({{2.4, 3.1}}, {{3.3, 1.6}});\n  test_with_distorted_frame_driver<3>({{2.4, 3.1, 1.3}}, {{3.3, 1.6, -1.4}});\n  test_equivalence();\n}\n}  // namespace\n\n}  // namespace domain::creators::time_dependence\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  TimeDependentOptions/Test_BinaryCompactObject.cpp\n  TimeDependentOptions/Test_ExpansionMap.cpp\n  TimeDependentOptions/Test_FromVolumeFile.cpp\n  TimeDependentOptions/Test_GridCenters.cpp\n  TimeDependentOptions/Test_RotationMap.cpp\n  TimeDependentOptions/Test_ShapeMap.cpp\n  TimeDependentOptions/Test_Skew.cpp\n  TimeDependentOptions/Test_Sphere.cpp\n  TimeDependentOptions/Test_TranslationMap.cpp\n  PARENT_SCOPE)\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.TimeDependentOptions.Python.BinaryCompactObject\"\n  Test_BinaryCompactObject.py\n  \"Unit;Domain\"\n  PyDomainTimeDependentOptions\n)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.TimeDependentOptions.Python.ExpansionMap\"\n  Test_ExpansionMap.py\n  \"Unit;Domain\"\n  PyDomainTimeDependentOptions\n)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.TimeDependentOptions.Python.GridCenters\"\n  Test_GridCenters.py\n  \"Unit;Domain\"\n  PyDomainTimeDependentOptions\n)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.TimeDependentOptions.Python.RotationMap\"\n  Test_RotationMap.py\n  \"Unit;Domain\"\n  PyDomainTimeDependentOptions\n)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.TimeDependentOptions.Python.ShapeMap\"\n  Test_ShapeMap.py\n  \"Unit;Domain\"\n  PyDomainTimeDependentOptions\n)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.TimeDependentOptions.Python.SkewMap\"\n  Test_SkewMap.py\n  \"Unit;Domain\"\n  PyDomainTimeDependentOptions\n)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Creators.TimeDependentOptions.Python.TranslationMap\"\n  Test_TranslationMap.py\n  \"Unit;Domain\"\n  PyDomainTimeDependentOptions\n)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Python/Test_BinaryCompactObject.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators.TimeDependentOptions import (\n    BinaryCompactObjectTimeDependentOptions,\n    ExpansionMapOptions,\n    KerrSchildFromBoyerLindquist,\n    RotationMapOptions,\n    ShapeMapOptions,\n    SkewMapOptions,\n    TranslationMapOptions,\n)\n\n\nclass TestBinaryCompactObjectTimeDependentOptions(unittest.TestCase):\n    def test_construction(self):\n        expansion_map = ExpansionMapOptions([1.0, 1e-4, 0.0], 100.0, 1e-6)\n        rotation_map = RotationMapOptions([[0.0, 0.0, 0.0, 1.0]], 100.0)\n        translation_map = TranslationMapOptions(\n            [[1.0, -1.0, 0.5], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]\n        )\n        shape_map_a = ShapeMapOptions[\"A\"](\n            10, KerrSchildFromBoyerLindquist(0.66, [0.0, 0.0, 0.99]), None\n        )\n        shape_map_b = ShapeMapOptions[\"B\"](\n            10, KerrSchildFromBoyerLindquist(0.33, [0.0, 0.0, -0.99]), None\n        )\n        skew_map = SkewMapOptions([0.0, 40.0, 50.0], [0.0, 10.0, 0.0])\n        binary_compact_object_one_map = BinaryCompactObjectTimeDependentOptions(\n            0.0,\n            None,\n            None,\n            translation_map,\n            None,\n            None,\n            None,\n            None,\n        )\n        binary_compact_object_all_maps = (\n            BinaryCompactObjectTimeDependentOptions(\n                0.0,\n                expansion_map,\n                rotation_map,\n                translation_map,\n                skew_map,\n                shape_map_a,\n                shape_map_b,\n                None,\n            )\n        )\n        self.assertNotEqual(binary_compact_object_one_map, None)\n        self.assertNotEqual(binary_compact_object_all_maps, None)\n        self.assertNotEqual(\n            binary_compact_object_one_map, binary_compact_object_all_maps\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Python/Test_ExpansionMap.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators.TimeDependentOptions import ExpansionMapOptions\n\n\nclass TestExpansionMap(unittest.TestCase):\n    def test_construction(self):\n        expansion_map = ExpansionMapOptions([1.0, 1e-4, 0.0], 100.0, 1e-6)\n        self.assertNotEqual(expansion_map, None)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Python/Test_GridCenters.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nfrom spectre.Domain.Creators.TimeDependentOptions import GridCentersOptions\nfrom spectre.Informer import unit_test_src_path\n\n\nclass TestGridCenters(unittest.TestCase):\n    def test_construction(self):\n        grid_centers = GridCentersOptions(\n            os.path.join(\n                str(unit_test_src_path()),\n                \"../InputFiles/GrMhd/GhValenciaDivClean/\",\n                \"EvolutionParameters.perl\",\n            ),\n            2.0,\n        )\n        self.assertNotEqual(grid_centers, None)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Python/Test_RotationMap.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators.TimeDependentOptions import RotationMapOptions\n\n\nclass TestRotationMap(unittest.TestCase):\n    def test_construction(self):\n        rotation_map = RotationMapOptions([[0.0, 0.0, 0.0, 1.0]], 100.0)\n        self.assertNotEqual(rotation_map, None)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Python/Test_ShapeMap.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators.TimeDependentOptions import (\n    KerrSchildFromBoyerLindquist,\n    ShapeMapOptions,\n)\n\n\nclass TestShapeMap(unittest.TestCase):\n    def test_construction(self):\n        shape_map_A = ShapeMapOptions[\"A\"](\n            10, KerrSchildFromBoyerLindquist(1.0, [0.0, 0.0, 0.99])\n        )\n        shape_map_B = ShapeMapOptions[\"B\"](\n            10, KerrSchildFromBoyerLindquist(1.0, [0.0, 0.0, 0.99])\n        )\n        shape_map_C = ShapeMapOptions[\"C\"](\n            10, KerrSchildFromBoyerLindquist(1.0, [0.0, 0.0, 0.99])\n        )\n        shape_map_no_object = ShapeMapOptions[\"None\"](\n            10, KerrSchildFromBoyerLindquist(1.0, [0.0, 0.0, 0.99])\n        )\n        self.assertNotEqual(shape_map_A, None)\n        self.assertNotEqual(shape_map_B, None)\n        self.assertNotEqual(shape_map_C, None)\n        self.assertNotEqual(shape_map_no_object, None)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Python/Test_SkewMap.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators.TimeDependentOptions import SkewMapOptions\n\n\nclass TestSkewMap(unittest.TestCase):\n    def test_construction(self):\n        skew_map = SkewMapOptions([0.0, 40.0, 50.0], [0.0, 10.0, 0.0])\n        self.assertNotEqual(skew_map, None)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Python/Test_TranslationMap.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain.Creators.TimeDependentOptions import TranslationMapOptions\n\n\nclass TestTranslationMap(unittest.TestCase):\n    def test_construction(self):\n        translation_map = TranslationMapOptions(\n            [[1.0, -1.0, 0.5], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]\n        )\n        self.assertNotEqual(translation_map, None)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_BinaryCompactObject.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/SkewMap.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/IntegratedFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain::creators::bco {\ntemplate <bool IsCylindrical>\nusing ExpMapOptions =\n    typename TimeDependentMapOptions<IsCylindrical>::ExpansionMapOptionType;\ntemplate <bool IsCylindrical>\nusing RotMapOptions =\n    typename TimeDependentMapOptions<IsCylindrical>::RotationMapOptionType;\ntemplate <bool IsCylindrical>\nusing TransMapOptions =\n    typename TimeDependentMapOptions<IsCylindrical>::TranslationMapOptionType;\ntemplate <bool IsCylindrical>\nusing SkewMapOptions =\n    typename TimeDependentMapOptions<IsCylindrical>::SkewMapOptionType;\ntemplate <bool IsCylindrical>\nusing ShapeMapAOptions = typename TimeDependentMapOptions<\n    IsCylindrical>::template ShapeMapOptionType<domain::ObjectLabel::A>;\ntemplate <bool IsCylindrical>\nusing ShapeMapBOptions = typename TimeDependentMapOptions<\n    IsCylindrical>::template ShapeMapOptionType<domain::ObjectLabel::B>;\ntemplate <bool IsCylindrical>\nusing GridCentersOptions =\n    typename TimeDependentMapOptions<IsCylindrical>::GridCentersOptionType;\nnamespace {\n// Test produce_all_maps for 1-4 maps\nusing Expansion = domain::CoordinateMaps::TimeDependent::CubicScale<3>;\nusing Rotation = domain::CoordinateMaps::TimeDependent::Rotation<3>;\nusing Shape = domain::CoordinateMaps::TimeDependent::Shape;\nusing Identity = domain::CoordinateMaps::Identity<3>;\n\nusing combo_maps_1 =\n    detail::produce_all_maps<Frame::Grid, Frame::Inertial, Expansion>;\nusing combo_maps_1_expected = tmpl::list<detail::gi_map<Expansion>>;\nusing combo_maps_2 =\n    detail::produce_all_maps<Frame::Grid, Frame::Inertial, Expansion, Rotation>;\nusing combo_maps_2_expected =\n    // clang-format off\n    tmpl::list<detail::gi_map<Expansion>,\n               detail::gi_map<Rotation>,\n               detail::gi_map<Expansion, Rotation>>;\n// clang-format on\nusing combo_maps_3 = detail::produce_all_maps<Frame::Grid, Frame::Inertial,\n                                              Shape, Expansion, Rotation>;\nusing combo_maps_3_expected =\n    // clang-format off\n    tmpl::list<detail::gi_map<Expansion>,\n               detail::gi_map<Rotation>,\n               detail::gi_map<Shape>,\n               detail::gi_map<Shape, Expansion>,\n               detail::gi_map<Shape, Rotation>,\n               detail::gi_map<Expansion, Rotation>,\n               detail::gi_map<Shape, Expansion, Rotation>>;\n// clang-format on\nusing combo_maps_4 =\n    detail::produce_all_maps<Frame::Grid, Frame::Inertial, Shape, Expansion,\n                             Rotation, Identity>;\nusing combo_maps_4_expected =\n    // clang-format off\n    tmpl::list<detail::gi_map<Expansion>,\n               detail::gi_map<Rotation>,\n               detail::gi_map<Shape>,\n               detail::gi_map<Identity>,\n               detail::gi_map<Shape, Expansion>,\n               detail::gi_map<Shape, Rotation>,\n               detail::gi_map<Shape, Identity>,\n               detail::gi_map<Expansion, Rotation>,\n               detail::gi_map<Expansion, Identity>,\n               detail::gi_map<Rotation, Identity>,\n               detail::gi_map<Shape, Expansion, Rotation>,\n               detail::gi_map<Shape, Expansion, Identity>,\n               detail::gi_map<Shape, Rotation, Identity>,\n               detail::gi_map<Expansion, Rotation, Identity>,\n               detail::gi_map<Shape, Expansion, Rotation, Identity>>;\n// clang-format on\n\ntemplate <typename List1, typename List2>\nconstexpr bool check_maps_list_v =\n    std::is_same_v<tmpl::list_difference<List1, List2>, tmpl::list<>>;\n\nstatic_assert(check_maps_list_v<combo_maps_1, combo_maps_1_expected>);\nstatic_assert(check_maps_list_v<combo_maps_1_expected, combo_maps_1>);\nstatic_assert(check_maps_list_v<combo_maps_2, combo_maps_2_expected>);\nstatic_assert(check_maps_list_v<combo_maps_2_expected, combo_maps_2>);\nstatic_assert(check_maps_list_v<combo_maps_3, combo_maps_3_expected>);\nstatic_assert(check_maps_list_v<combo_maps_3_expected, combo_maps_3>);\nstatic_assert(check_maps_list_v<combo_maps_4, combo_maps_4_expected>);\nstatic_assert(check_maps_list_v<combo_maps_4_expected, combo_maps_4>);\n\ntemplate <bool IsCylindrical>\nvoid test(const bool include_expansion, const bool include_rotation,\n          const bool include_translation, const bool include_skew,\n          const bool include_shape_a, const bool include_shape_b,\n          const bool transition_ends_at_cube_A,\n          const bool transition_ends_at_cube_B,\n          const bool include_grid_centers) {\n  CAPTURE(IsCylindrical);\n  CAPTURE(include_expansion);\n  CAPTURE(include_rotation);\n  CAPTURE(include_translation);\n  CAPTURE(include_skew);\n  CAPTURE(include_shape_a);\n  CAPTURE(include_shape_b);\n  CAPTURE(include_grid_centers);\n  ExpMapOptions<IsCylindrical> exp_map_options{};\n  RotMapOptions<IsCylindrical> rot_map_options{};\n  TransMapOptions<IsCylindrical> trans_map_options{};\n  SkewMapOptions<IsCylindrical> skew_map_options{};\n  ShapeMapAOptions<IsCylindrical> shape_map_a_options{};\n  ShapeMapBOptions<IsCylindrical> shape_map_b_options{};\n  GridCentersOptions<IsCylindrical> grid_centers_options{};\n\n  const std::array<double, 3> exp_values{1.0, 0.0, 0.0};\n  const double exp_outer_boundary_velocity = -0.01;\n  const double exp_outer_boundary_timescale = 25.0;\n  if (include_expansion) {\n    exp_map_options = time_dependent_options::ExpansionMapOptions<false>{\n        exp_values, exp_outer_boundary_timescale, exp_outer_boundary_velocity};\n  }\n\n  const std::array<double, 3> angular_velocity{0.2, -0.4, 0.6};\n  if (include_rotation) {\n    rot_map_options =\n        time_dependent_options::RotationMapOptions<false>{angular_velocity};\n  }\n\n  const std::array<std::array<double, 3>, 3> translation_values = {\n      std::array{1.0, 0.5, -1.0}, std::array{0.3, -0.1, -0.2},\n      std::array{0.0, 0.0, 0.0}};\n  if (include_translation) {\n    trans_map_options = TransMapOptions<IsCylindrical>{translation_values};\n  }\n\n  const std::array<std::array<double, 3>, 2> skew_values{\n      std::array{-0.1, 0.0, 0.0}, std::array{-0.1, 0.0, 0.0}};\n  if (include_skew) {\n    skew_map_options =\n        time_dependent_options::SkewMapOptions{skew_values[0], skew_values[1]};\n  }\n\n  const std::array<double, 3> size_A_values{0.9, 0.08, 0.007};\n  const size_t l_max_A = 8;\n  if (include_shape_a) {\n    shape_map_a_options =\n        IsCylindrical\n            ? time_dependent_options::ShapeMapOptions<\n                  not IsCylindrical, domain::ObjectLabel::A>{l_max_A,\n                                                             std::nullopt,\n                                                             size_A_values}\n            : time_dependent_options::ShapeMapOptions<not IsCylindrical,\n                                                      domain::ObjectLabel::A>{\n                  l_max_A, std::nullopt, size_A_values, 0.,\n                  transition_ends_at_cube_A};\n  }\n\n  const std::array<double, 3> size_B_values{-0.001, -0.02, -0.3};\n  const size_t l_max_B = 10;\n  if (include_shape_b) {\n    shape_map_b_options =\n        IsCylindrical\n            ? time_dependent_options::ShapeMapOptions<\n                  not IsCylindrical, domain::ObjectLabel::B>{l_max_B,\n                                                             std::nullopt,\n                                                             size_B_values}\n            : time_dependent_options::ShapeMapOptions<not IsCylindrical,\n                                                      domain::ObjectLabel::B>{\n                  l_max_B, std::nullopt, size_B_values, 0.,\n                  transition_ends_at_cube_B};\n  }\n\n  if (include_grid_centers) {\n    grid_centers_options = typename GridCentersOptions<\n        IsCylindrical>::value_type{\n        unit_test_src_path() +\n            \"/../InputFiles/GrMhd/GhValenciaDivClean/EvolutionParameters.perl\",\n        1.0};\n  }\n\n  const double initial_time = 1.5;\n  if ((not include_expansion) and (not include_rotation) and\n      (not include_translation) and (not include_skew) and\n      (not include_shape_a) and (not include_shape_b) and\n      (not include_grid_centers)) {\n    CHECK_THROWS_WITH(\n        (TimeDependentMapOptions<IsCylindrical>{\n            initial_time, exp_map_options, rot_map_options, trans_map_options,\n            skew_map_options, shape_map_a_options, shape_map_b_options,\n            grid_centers_options}),\n        Catch::Matchers::ContainsSubstring(\n            \"Time dependent map options were specified, but all options \"\n            \"were 'None'. If you don't want time dependent maps, specify \"\n            \"'None' for the TimeDependentMapOptions. If you want time \"\n            \"dependent maps, specify options for at least one map.\"));\n    return;\n  }\n\n  TimeDependentMapOptions<IsCylindrical> time_dep_options{\n      initial_time,        exp_map_options,     rot_map_options,\n      trans_map_options,   skew_map_options,    shape_map_a_options,\n      shape_map_b_options, grid_centers_options};\n\n  CHECK(time_dep_options.has_distorted_frame_options(domain::ObjectLabel::A) ==\n        include_shape_a);\n  CHECK(time_dep_options.has_distorted_frame_options(domain::ObjectLabel::B) ==\n        include_shape_b);\n\n  std::unordered_map<std::string, double> expiration_times{\n      {TimeDependentMapOptions<IsCylindrical>::expansion_name, 10.0},\n      {TimeDependentMapOptions<IsCylindrical>::rotation_name,\n       std::numeric_limits<double>::infinity()},\n      {TimeDependentMapOptions<IsCylindrical>::translation_name,\n       std::numeric_limits<double>::infinity()},\n      {TimeDependentMapOptions<IsCylindrical>::skew_name,\n       std::numeric_limits<double>::infinity()},\n      {TimeDependentMapOptions<IsCylindrical>::grid_centers_name,\n       std::numeric_limits<double>::infinity()},\n      {gsl::at(TimeDependentMapOptions<IsCylindrical>::size_names, 0), 15.5},\n      {gsl::at(TimeDependentMapOptions<IsCylindrical>::size_names, 1),\n       std::numeric_limits<double>::infinity()},\n      {gsl::at(TimeDependentMapOptions<IsCylindrical>::shape_names, 0),\n       std::numeric_limits<double>::infinity()},\n      {gsl::at(TimeDependentMapOptions<IsCylindrical>::shape_names, 1), 19.1}};\n\n  using ExpFoT = domain::FunctionsOfTime::PiecewisePolynomial<2>;\n  using ExpBdryFoT = domain::FunctionsOfTime::FixedSpeedCubic;\n  using RotFoT = domain::FunctionsOfTime::QuaternionFunctionOfTime<3>;\n  using TransFoT = domain::FunctionsOfTime::PiecewisePolynomial<2>;\n  using SkewFoT = domain::FunctionsOfTime::PiecewisePolynomial<2>;\n  using SizeFoT = domain::FunctionsOfTime::PiecewisePolynomial<3>;\n  using ShapeFoT = ExpFoT;\n  using GridCentersFoT = domain::FunctionsOfTime::PiecewisePolynomial<2>;\n  ExpFoT expansion{\n      initial_time,\n      std::array<DataVector, 3>{\n          {{gsl::at(exp_values, 0)}, {gsl::at(exp_values, 1)}, {0.0}}},\n      expiration_times.at(\n          TimeDependentMapOptions<IsCylindrical>::expansion_name)};\n  ExpBdryFoT expansion_outer_boundary{1.0, initial_time,\n                                      exp_outer_boundary_velocity,\n                                      exp_outer_boundary_timescale};\n  RotFoT rotation{\n      initial_time, std::array<DataVector, 1>{DataVector{1.0, 0.0, 0.0, 0.0}},\n      std::array<DataVector, 4>{\n          {{3, 0.0}, DataVector{angular_velocity}, {3, 0.0}, {3, 0.0}}},\n      expiration_times.at(\n          TimeDependentMapOptions<IsCylindrical>::rotation_name)};\n  TransFoT translation{\n      initial_time,\n      std::array{DataVector{translation_values[0]},\n                 DataVector{translation_values[1]},\n                 DataVector{translation_values[2]}},\n      expiration_times.at(\n          TimeDependentMapOptions<IsCylindrical>::translation_name)};\n  SkewFoT skew{\n      initial_time,\n      std::array{DataVector{skew_values[0][0], skew_values[1][0]},\n                 DataVector{skew_values[0][1], skew_values[1][1]},\n                 DataVector{skew_values[0][2], skew_values[1][2]}},\n      expiration_times.at(TimeDependentMapOptions<IsCylindrical>::skew_name)};\n  SizeFoT size_A{initial_time,\n                 std::array<DataVector, 4>{{{gsl::at(size_A_values, 0)},\n                                            {gsl::at(size_A_values, 1)},\n                                            {gsl::at(size_A_values, 2)},\n                                            {0.0}}},\n                 expiration_times.at(gsl::at(\n                     TimeDependentMapOptions<IsCylindrical>::size_names, 0))};\n  SizeFoT size_B{initial_time,\n                 std::array<DataVector, 4>{{{gsl::at(size_B_values, 0)},\n                                            {gsl::at(size_B_values, 1)},\n                                            {gsl::at(size_B_values, 2)},\n                                            {0.0}}},\n                 expiration_times.at(gsl::at(\n                     TimeDependentMapOptions<IsCylindrical>::size_names, 1))};\n  const DataVector shape_A_zeros{\n      ylm::Spherepack::spectral_size(l_max_A, l_max_A), 0.0};\n  const DataVector shape_B_zeros{\n      ylm::Spherepack::spectral_size(l_max_B, l_max_B), 0.0};\n  ShapeFoT shape_A{\n      initial_time,\n      std::array<DataVector, 3>{shape_A_zeros, shape_A_zeros, shape_A_zeros},\n      expiration_times.at(\n          gsl::at(TimeDependentMapOptions<IsCylindrical>::shape_names, 0))};\n  ShapeFoT shape_B{\n      initial_time,\n      std::array<DataVector, 3>{shape_B_zeros, shape_B_zeros, shape_B_zeros},\n      expiration_times.at(\n          gsl::at(TimeDependentMapOptions<IsCylindrical>::shape_names, 1))};\n\n  GridCentersFoT grid_centers{};\n  if (include_grid_centers) {\n    grid_centers = GridCentersFoT{\n        initial_time, grid_centers_options.value().initial_values,\n        expiration_times.at(\n            TimeDependentMapOptions<IsCylindrical>::grid_centers_name)};\n  }\n\n  const std::array<std::array<double, 3>, 2> centers{\n      std::array{5.0, -0.01, -0.02}, std::array{-5.0, -0.01, -0.02}};\n  const std::optional<std::array<double, 3>> cube_a_center =\n      IsCylindrical ? std::optional<std::array<double, 3>>{}\n                    : std::array<double, 3>{{4.25, -0.3, 0.2}};\n  const std::optional<std::array<double, 3>> cube_b_center =\n      IsCylindrical ? std::optional<std::array<double, 3>>{}\n                    : std::array<double, 3>{{-4.25, 0.4, -0.15}};\n  const double domain_envelope_radius = 15.0;\n  const double domain_outer_radius = 20.0;\n\n  const auto anchors = create_grid_anchors(centers[0], centers[1]);\n  const std::array<double, 3> center_of_mass = 0.5 * (centers[0] + centers[1]);\n  CHECK(anchors.count(\"Center\") == 1);\n  CHECK(anchors.count(\"CenterA\") == 1);\n  CHECK(anchors.count(\"CenterB\") == 1);\n  CHECK(anchors.at(\"Center\") ==\n        tnsr::I<double, 3, Frame::Grid>(center_of_mass));\n  CHECK(anchors.at(\"CenterA\") == tnsr::I<double, 3, Frame::Grid>(centers[0]));\n  CHECK(anchors.at(\"CenterB\") == tnsr::I<double, 3, Frame::Grid>(centers[1]));\n\n  for (const auto& [excise_A, excise_B] :\n       cartesian_product(make_array(true, false), make_array(true, false))) {\n    CAPTURE(excise_A);\n    CAPTURE(excise_B);\n    using RadiiType = std::optional<std::array<double, IsCylindrical ? 2 : 3>>;\n    RadiiType inner_outer_radii_A{};\n    RadiiType inner_outer_radii_B{};\n\n    if constexpr (IsCylindrical) {\n      inner_outer_radii_A = std::array{0.8, 3.2};\n    } else {\n      inner_outer_radii_A = std::array{0.8, 1.4, 3.2};\n    }\n    if constexpr (IsCylindrical) {\n      inner_outer_radii_B = std::array{0.5, 2.1};\n    } else {\n      inner_outer_radii_B = std::array{0.5, 0.9, 2.1};\n    }\n    const auto build_maps = [&time_dep_options, &centers, &center_of_mass,\n                             &inner_outer_radii_A, &inner_outer_radii_B,\n                             &domain_envelope_radius, &domain_outer_radius,\n                             &cube_a_center, &cube_b_center,\n                             filled_A = not excise_A,\n                             filled_B = not excise_B]() {\n      time_dep_options.build_maps(centers, cube_a_center, cube_b_center,\n                                  center_of_mass, inner_outer_radii_A,\n                                  inner_outer_radii_B, filled_A, filled_B,\n                                  domain_envelope_radius, domain_outer_radius);\n    };\n\n    if constexpr (not IsCylindrical) {\n      if (include_shape_a and\n          (not excise_A and not transition_ends_at_cube_A)) {\n        CHECK_THROWS_WITH(build_maps(),\n                          Catch::Matchers::ContainsSubstring(\n                              \"If the object is filled, the transition must \"\n                              \"end at the cube.\"));\n        continue;\n      }\n      if (include_shape_a and not excise_A) {\n        CHECK_THROWS_WITH(\n            build_maps(),\n            Catch::Matchers::ContainsSubstring(\n                \"the centers of the inner and outer object are different\"));\n        continue;\n      }\n      if (include_shape_b and\n          (not excise_B and not transition_ends_at_cube_B)) {\n        CHECK_THROWS_WITH(build_maps(),\n                          Catch::Matchers::ContainsSubstring(\n                              \"If the object is filled, the transition must \"\n                              \"end at the cube.\"));\n        continue;\n      }\n      if (include_shape_b and not excise_B) {\n        CHECK_THROWS_WITH(\n            build_maps(),\n            Catch::Matchers::ContainsSubstring(\n                \"the centers of the inner and outer object are different\"));\n        continue;\n      }\n    }\n\n    // Now for each object, either we have included shape map options and\n    // excision info, or we didn't include either. (i.e. include_shape_map_?\n    // and excise_? are both true or both false) so it's safe to build the\n    // maps now\n    build_maps();\n\n    using IncludeDistortedMapType = typename TimeDependentMapOptions<\n        IsCylindrical>::IncludeDistortedMapType;\n    IncludeDistortedMapType include_distorted_map_A{};\n    IncludeDistortedMapType include_distorted_map_B{};\n    if constexpr (IsCylindrical) {\n      include_distorted_map_A = include_shape_a;\n      include_distorted_map_B = include_shape_b;\n    } else {\n      include_distorted_map_A =\n          include_shape_a ? std::make_optional(0_st) : std::nullopt;\n      include_distorted_map_B =\n          include_shape_b ? std::make_optional(0_st) : std::nullopt;\n    }\n\n    if ((not include_rotation) and (not include_expansion) and\n        (not include_translation) and (not include_skew) and\n        (not include_shape_a)) {\n      CHECK(time_dep_options\n                .template grid_to_inertial_map<domain::ObjectLabel::A>(\n                    include_distorted_map_A, true) == nullptr);\n      continue;\n    }\n    if ((not include_rotation) and (not include_expansion) and\n        (not include_translation) and (not include_skew) and\n        (not include_shape_b)) {\n      CHECK(time_dep_options\n                .template grid_to_inertial_map<domain::ObjectLabel::B>(\n                    include_distorted_map_B, true) == nullptr);\n      continue;\n    }\n\n    const auto grid_to_distorted_map_A =\n        time_dep_options.template grid_to_distorted_map<domain::ObjectLabel::A>(\n            include_distorted_map_A);\n    const auto grid_to_distorted_map_B =\n        time_dep_options.template grid_to_distorted_map<domain::ObjectLabel::B>(\n            include_distorted_map_B);\n    const auto grid_to_inertial_map_A =\n        time_dep_options.template grid_to_inertial_map<domain::ObjectLabel::A>(\n            include_distorted_map_A, true);\n    const auto grid_to_inertial_map_B =\n        time_dep_options.template grid_to_inertial_map<domain::ObjectLabel::B>(\n            include_distorted_map_B, true);\n    const auto grid_to_inertial_map_excision_A =\n        time_dep_options.template grid_to_inertial_map<domain::ObjectLabel::A>(\n            include_distorted_map_A, true, true);\n    const auto grid_to_inertial_map_excision_B =\n        time_dep_options.template grid_to_inertial_map<domain::ObjectLabel::B>(\n            include_distorted_map_B, true, true);\n    // Even though the distorted to inertial map is not tied to a specific\n    // object, we use `excise_?` to determine if the distorted map is\n    // included just for testing.\n    const auto distorted_to_inertial_map_A =\n        time_dep_options\n            .template distorted_to_inertial_map<domain::ObjectLabel::A>(\n                include_distorted_map_A, true);\n    const auto distorted_to_inertial_map_B =\n        time_dep_options\n            .template distorted_to_inertial_map<domain::ObjectLabel::B>(\n                include_distorted_map_B, true);\n\n    // All of these maps are tested individually. Rather than going through\n    // the effort of coming up with a source coordinate and calculating\n    // analytically what we would get after it's mapped, we just check whether\n    // it's supposed to be a nullptr and if it's not that it's not the\n    // identity and that the jacobians are time dependent.\n    const auto check_map = [](const auto& map, const bool is_null,\n                              const bool is_identity) {\n      if (is_null) {\n        CHECK(map == nullptr);\n      } else {\n        REQUIRE(map != nullptr);\n        CHECK(map->is_identity() == is_identity);\n        CHECK(map->inv_jacobian_is_time_dependent() != is_identity);\n        CHECK(map->jacobian_is_time_dependent() != is_identity);\n      }\n    };\n    {\n      INFO(\"grid_to_distorted_map_A\");\n      check_map(grid_to_distorted_map_A, not include_shape_a, false);\n    }\n    {\n      INFO(\"grid_to_distorted_map_B\");\n      check_map(grid_to_distorted_map_B, not include_shape_b, false);\n    }\n    {\n      INFO(\"grid_to_inertial_map_A\");\n      check_map(grid_to_inertial_map_A, false, false);\n    }\n    {\n      INFO(\"grid_to_inertial_map_B\");\n      check_map(grid_to_inertial_map_B, false, false);\n    }\n    {\n      INFO(\"grid_to_inertial_map_excision_A\");\n      check_map(grid_to_inertial_map_excision_A, false, false);\n    }\n    {\n      INFO(\"grid_to_inertial_map_excision_B\");\n      check_map(grid_to_inertial_map_excision_B, false, false);\n    }\n    {\n      INFO(\"distorted_to_inertial_map_A\");\n      check_map(distorted_to_inertial_map_A, not include_shape_a,\n                (not include_rotation) and (not include_expansion) and\n                    (not include_translation) and (not include_skew) and\n                    include_shape_a);\n    }\n    {\n      INFO(\"distorted_to_inertial_map_B\");\n      check_map(distorted_to_inertial_map_B, not include_shape_b,\n                (not include_rotation) and (not include_expansion) and\n                    (not include_translation) and (not include_skew) and\n                    include_shape_b);\n    }\n    // Test functions of time\n    const auto functions_of_time =\n        time_dep_options.create_functions_of_time(expiration_times);\n    if (include_expansion) {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::expansion_name) == 1);\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<\n                    IsCylindrical>::expansion_outer_boundary_name) == 1);\n      CHECK(dynamic_cast<ExpFoT&>(\n                *functions_of_time\n                     .at(TimeDependentMapOptions<IsCylindrical>::expansion_name)\n                     .get()) == expansion);\n      CHECK(dynamic_cast<ExpBdryFoT&>(\n                *functions_of_time\n                     .at(TimeDependentMapOptions<\n                         IsCylindrical>::expansion_outer_boundary_name)\n                     .get()) == expansion_outer_boundary);\n    } else {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::expansion_name) == 0);\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<\n                    IsCylindrical>::expansion_outer_boundary_name) == 0);\n    }\n    if (include_rotation) {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::rotation_name) == 1);\n      CHECK(dynamic_cast<RotFoT&>(\n                *functions_of_time\n                     .at(TimeDependentMapOptions<IsCylindrical>::rotation_name)\n                     .get()) == rotation);\n    } else {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::rotation_name) == 0);\n    }\n    if (include_translation) {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::translation_name) == 1);\n      CHECK(\n          dynamic_cast<TransFoT&>(\n              *functions_of_time\n                   .at(TimeDependentMapOptions<IsCylindrical>::translation_name)\n                   .get()) == translation);\n    } else {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::translation_name) == 0);\n    }\n    if (include_skew) {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::skew_name) == 1);\n      CHECK(dynamic_cast<TransFoT&>(\n                *functions_of_time\n                     .at(TimeDependentMapOptions<IsCylindrical>::skew_name)\n                     .get()) == skew);\n    } else {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::skew_name) == 0);\n    }\n    if (include_shape_a) {\n      CHECK(functions_of_time.count(gsl::at(\n                TimeDependentMapOptions<IsCylindrical>::size_names, 0)) == 1);\n      CHECK(functions_of_time.count(gsl::at(\n                TimeDependentMapOptions<IsCylindrical>::shape_names, 0)) == 1);\n      CHECK(dynamic_cast<SizeFoT&>(\n                *functions_of_time\n                     .at(gsl::at(\n                         TimeDependentMapOptions<IsCylindrical>::size_names, 0))\n                     .get()) == size_A);\n      CHECK(\n          dynamic_cast<ShapeFoT&>(\n              *functions_of_time\n                   .at(gsl::at(\n                       TimeDependentMapOptions<IsCylindrical>::shape_names, 0))\n                   .get()) == shape_A);\n    } else {\n      CHECK(functions_of_time.count(gsl::at(\n                TimeDependentMapOptions<IsCylindrical>::size_names, 0)) == 0);\n      CHECK(functions_of_time.count(gsl::at(\n                TimeDependentMapOptions<IsCylindrical>::shape_names, 0)) == 0);\n    }\n    if (include_shape_b) {\n      CHECK(functions_of_time.count(gsl::at(\n                TimeDependentMapOptions<IsCylindrical>::size_names, 1)) == 1);\n      CHECK(functions_of_time.count(gsl::at(\n                TimeDependentMapOptions<IsCylindrical>::shape_names, 1)) == 1);\n      CHECK(dynamic_cast<SizeFoT&>(\n                *functions_of_time\n                     .at(gsl::at(\n                         TimeDependentMapOptions<IsCylindrical>::size_names, 1))\n                     .get()) == size_B);\n      CHECK(\n          dynamic_cast<ShapeFoT&>(\n              *functions_of_time\n                   .at(gsl::at(\n                       TimeDependentMapOptions<IsCylindrical>::shape_names, 1))\n                   .get()) == shape_B);\n    } else {\n      CHECK(functions_of_time.count(gsl::at(\n                TimeDependentMapOptions<IsCylindrical>::size_names, 1)) == 0);\n      CHECK(functions_of_time.count(gsl::at(\n                TimeDependentMapOptions<IsCylindrical>::shape_names, 1)) == 0);\n    }\n    if (include_grid_centers) {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::grid_centers_name) ==\n            1);\n      CHECK(dynamic_cast<GridCentersFoT&>(\n                *functions_of_time\n                     .at(TimeDependentMapOptions<\n                         IsCylindrical>::grid_centers_name)\n                     .get()) == grid_centers);\n    } else {\n      CHECK(functions_of_time.count(\n                TimeDependentMapOptions<IsCylindrical>::grid_centers_name) ==\n            0);\n    }\n  }\n}\n\ntemplate <bool IsCylindrical>\nvoid check_names() {\n  INFO(\"Check names\");\n  // These are hard-coded so this is just a regression test\n  CHECK(TimeDependentMapOptions<IsCylindrical>::expansion_name == \"Expansion\"s);\n  CHECK(TimeDependentMapOptions<IsCylindrical>::expansion_outer_boundary_name ==\n        \"ExpansionOuterBoundary\"s);\n  CHECK(TimeDependentMapOptions<IsCylindrical>::rotation_name == \"Rotation\"s);\n  CHECK(TimeDependentMapOptions<IsCylindrical>::translation_name ==\n        \"Translation\"s);\n  CHECK(TimeDependentMapOptions<IsCylindrical>::skew_name == \"Skew\"s);\n  CHECK(TimeDependentMapOptions<IsCylindrical>::size_names ==\n        std::array{\"SizeA\"s, \"SizeB\"s});\n  CHECK(TimeDependentMapOptions<IsCylindrical>::shape_names ==\n        std::array{\"ShapeA\"s, \"ShapeB\"s});\n  CHECK(TimeDependentMapOptions<IsCylindrical>::grid_centers_name ==\n        \"GridCenters\"s);\n}\n\ntemplate <bool IsCylindrical>\nvoid test_errors() {\n  INFO(\"Test errors\");\n  CAPTURE(IsCylindrical);\n  CHECK_THROWS_WITH(\n      (TimeDependentMapOptions<IsCylindrical>{\n          1.0, std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n          time_dependent_options::ShapeMapOptions<\n              not IsCylindrical, domain::ObjectLabel::A>{1, {}},\n          time_dependent_options::ShapeMapOptions<\n              not IsCylindrical, domain::ObjectLabel::B>{8, {}},\n          std::nullopt}),\n      Catch::Matchers::ContainsSubstring(\"Initial LMax must be 2 or greater\"));\n  CHECK_THROWS_WITH(\n      (TimeDependentMapOptions<IsCylindrical>{\n          1.0, std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n          time_dependent_options::ShapeMapOptions<\n              not IsCylindrical, domain::ObjectLabel::A>{6, {}},\n          time_dependent_options::ShapeMapOptions<\n              not IsCylindrical, domain::ObjectLabel::B>{0, {}},\n          std::nullopt}),\n      Catch::Matchers::ContainsSubstring(\"Initial LMax must be 2 or greater\"));\n  CHECK_THROWS_WITH(\n      (TimeDependentMapOptions<IsCylindrical>{\n          1.0, std::nullopt, std::nullopt, std::nullopt, std::nullopt,\n          std::nullopt, std::nullopt, std::nullopt}),\n      Catch::Matchers::ContainsSubstring(\n          \"Time dependent map options were \"\n          \"specified, but all options were 'None'.\"));\n  using RadiiType = std::array<double, IsCylindrical ? 2 : 3>;\n  RadiiType radii{};\n  if constexpr (IsCylindrical) {\n    radii = std::array{0.1, 1.0};\n  } else {\n    radii = std::array{0.1, 0.5, 1.0};\n  }\n  if (IsCylindrical) {\n    CHECK_THROWS_WITH(\n        ([&radii]() {\n          TimeDependentMapOptions<IsCylindrical> time_dep_opts{\n              1.0,\n              std::nullopt,\n              std::nullopt,\n              std::nullopt,\n              std::nullopt,\n              time_dependent_options::ShapeMapOptions<not IsCylindrical,\n                                                      domain::ObjectLabel::A>{\n                  8, std::nullopt},\n              std::nullopt,\n              std::nullopt};\n          time_dep_opts.build_maps(\n              std::array{std::array{5.0, 0.0, 0.0}, std::array{-5.0, 0.0, 0.0}},\n              {{7.5, 0.0, 0.0}}, {{-7.5, 0.0, 0.0}}, std::array{0.0, 0.0, 0.0},\n              radii, radii, false, false, 25.0, 100.0);\n        }()),\n        Catch::Matchers::ContainsSubstring(\n            \"When using the CylindricalBinaryCompactObject domain creator, \"\n            \"the excision centers cannot be offset.\"));\n  }\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      TimeDependentMapOptions<IsCylindrical>{}.has_distorted_frame_options(\n          domain::ObjectLabel::None),\n      Catch::Matchers::ContainsSubstring(\n          \"object label for TimeDependentMapOptions must be either A or B\"));\n#endif\n}\n\nvoid test_worldtube_fots() {\n  const std::array<double, 3> size_a_opts{{1.3, 1.2, 1.1}};\n  const std::array<double, 3> size_b_opts{{1.4, 1.5, 1.6}};\n  const double initial_time = 0.0;\n\n  const double initial_expansion = 1.2;\n  const double initial_expansion_deriv = 2.2;\n\n  const TimeDependentMapOptions<false> worldtube_options{\n      initial_time,\n      time_dependent_options::ExpansionMapOptions<false>{\n          std::array{initial_expansion, initial_expansion_deriv, 0.0}, 1.0,\n          1.0},\n      time_dependent_options::RotationMapOptions<false>{\n          std::array{0.0, 0.0, 1.0}},\n      std::nullopt,\n      std::nullopt,\n      time_dependent_options::ShapeMapOptions<true, domain::ObjectLabel::A>{\n          2, {}, std::make_optional(size_a_opts)},\n      time_dependent_options::ShapeMapOptions<true, domain::ObjectLabel::B>{\n          2, {}, std::make_optional(size_b_opts)},\n      std::nullopt};\n  const auto fots = worldtube_options.create_functions_of_time<true>({});\n  CHECK(not fots.contains(\"Translation\"));\n\n  CHECK(fots.contains(\"Rotation\"));\n  const auto& rotation_fot = fots.at(\"Rotation\");\n  CHECK(dynamic_cast<domain::FunctionsOfTime::IntegratedFunctionOfTime*>(\n      &*rotation_fot));\n  CHECK(rotation_fot->time_bounds() ==\n        std::array<double, 2>{{initial_time, initial_time + 1e-10}});\n  const DataVector rotation_value{1., 0., 0., 0.};\n  const DataVector rotation_deriv_value{0., 0., 0., 0.5};\n  CHECK_ITERABLE_APPROX(rotation_fot->func(initial_time)[0], rotation_value);\n  CHECK_ITERABLE_APPROX(rotation_fot->func_and_deriv(initial_time)[1],\n                        rotation_deriv_value);\n\n  CHECK(fots.contains(\"Expansion\"));\n  const auto& expansion_fot = fots.at(\"Expansion\");\n  CHECK(dynamic_cast<domain::FunctionsOfTime::IntegratedFunctionOfTime*>(\n      &*expansion_fot));\n  CHECK(expansion_fot->time_bounds() ==\n        std::array<double, 2>{{initial_time, initial_time + 1e-10}});\n  const DataVector expansion_value{initial_expansion};\n  const DataVector expansion_deriv_value{initial_expansion_deriv};\n  CHECK_ITERABLE_APPROX(expansion_fot->func(initial_time)[0], expansion_value);\n  CHECK_ITERABLE_APPROX(expansion_fot->func_and_deriv(initial_time)[1],\n                        expansion_deriv_value);\n\n  CHECK(fots.contains(\"ExpansionOuterBoundary\"));\n  const auto& boundary_expansion = fots.at(\"ExpansionOuterBoundary\");\n  CHECK(dynamic_cast<domain::FunctionsOfTime::FixedSpeedCubic*>(\n      &*boundary_expansion));\n  CHECK(boundary_expansion->time_bounds() ==\n        std::array<double, 2>{\n            {initial_time, std::numeric_limits<double>::infinity()}});\n\n  CHECK(fots.contains(\"SizeA\"));\n  const auto& size_a_fot = fots.at(\"SizeA\");\n  CHECK(dynamic_cast<domain::FunctionsOfTime::IntegratedFunctionOfTime*>(\n      &*size_a_fot));\n  CHECK(size_a_fot->time_bounds() ==\n        std::array<double, 2>{{initial_time, initial_time + 1e-10}});\n  const DataVector size_a_value{size_a_opts[0]};\n  const DataVector size_a_deriv_value{size_a_opts[1]};\n  CHECK_ITERABLE_APPROX(size_a_fot->func(initial_time)[0], size_a_value);\n  CHECK_ITERABLE_APPROX(size_a_fot->func_and_deriv(initial_time)[1],\n                        size_a_deriv_value);\n\n  CHECK(fots.contains(\"SizeB\"));\n  const auto& size_b_fot = fots.at(\"SizeB\");\n  CHECK(dynamic_cast<domain::FunctionsOfTime::IntegratedFunctionOfTime*>(\n      &*size_b_fot));\n  CHECK(size_b_fot->time_bounds() ==\n        std::array<double, 2>{{initial_time, initial_time + 1e-10}});\n  const DataVector size_b_value{size_b_opts[0]};\n  const DataVector size_b_deriv_value{size_b_opts[1]};\n  CHECK_ITERABLE_APPROX(size_b_fot->func(initial_time)[0], size_b_value);\n  CHECK_ITERABLE_APPROX(size_b_fot->func_and_deriv(initial_time)[1],\n                        size_b_deriv_value);\n\n  CHECK(fots.contains(\"ShapeA\"));\n  const auto& shape_a_fot = fots.at(\"ShapeA\");\n  CHECK(dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n      &*shape_a_fot));\n  CHECK(shape_a_fot->time_bounds() ==\n        std::array<double, 2>{\n            {initial_time, std::numeric_limits<double>::infinity()}});\n\n  CHECK(fots.contains(\"ShapeB\"));\n  const auto& shape_b_fot = fots.at(\"ShapeB\");\n  CHECK(dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n      &*shape_b_fot));\n  CHECK(shape_b_fot->time_bounds() ==\n        std::array<double, 2>{\n            {initial_time, std::numeric_limits<double>::infinity()}});\n}\n\n}  // namespace\n\n// [[TimeOut, 45]]\nSPECTRE_TEST_CASE(\n    \"Unit.Domain.Creators.TimeDependentOptions.BinaryCompactObject\",\n    \"[Domain][Unit]\") {\n  for (const auto& [include_expansion, include_rotation, include_translation,\n                    include_skew, include_shape_a, include_shape_b,\n                    transition_ends_at_cube_A, transition_ends_at_cube_B,\n                    include_grid_centers] :\n       cartesian_product(make_array(true, false), make_array(true, false),\n                         make_array(true, false), make_array(true, false),\n                         make_array(true, false), make_array(true, false),\n                         make_array(true, false), make_array(true, false),\n                         make_array(true, false))) {\n    test<false>(include_expansion, include_rotation, include_translation,\n                include_skew, include_shape_a, include_shape_b,\n                transition_ends_at_cube_A, transition_ends_at_cube_B,\n                include_grid_centers);\n    // Filter out cases that are not valid for the cylindrical domain\n    if (transition_ends_at_cube_A or transition_ends_at_cube_B) {\n      continue;\n    }\n    test<true>(include_expansion, include_rotation, include_translation,\n               include_skew, include_shape_a, include_shape_b,\n               transition_ends_at_cube_A, transition_ends_at_cube_B,\n               include_grid_centers);\n  }\n  check_names<true>();\n  check_names<false>();\n  test_errors<true>();\n  test_errors<false>();\n  test_worldtube_fots();\n}\n}  // namespace domain::creators::bco\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_ExpansionMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <sstream>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependent/TestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nnamespace {\nconstexpr double infinity = std::numeric_limits<double>::infinity();\nvoid test_expansion_map_options() {\n  {\n    INFO(\"None\");\n    const auto expansion_map_options =\n        TestHelpers::test_option_tag<ExpansionMapOptions<false>>(\"None\");\n\n    CHECK(not expansion_map_options.has_value());\n  }\n\n  const auto non_settle_fots = []<bool AllowSettleFoTs>() {\n    INFO(\"Hardcoded AllowSettleFots = \" + std::to_string(AllowSettleFoTs) +\n         \", Non-Settle Fots\");\n    const auto expansion_map_options =\n        TestHelpers::test_option_tag<ExpansionMapOptions<AllowSettleFoTs>>(\n            \"InitialValues: [1.0, 2.0, 3.0]\\n\"\n            \"DecayTimescaleOuterBoundary: 50\\n\"\n            \"AsymptoticVelocityOuterBoundary: -1e-5\");\n\n    REQUIRE(expansion_map_options.has_value());\n    CHECK(std::holds_alternative<ExpansionMapOptions<AllowSettleFoTs>>(\n        expansion_map_options.value()));\n\n    const auto& hardcoded_options =\n        std::get<ExpansionMapOptions<AllowSettleFoTs>>(\n            expansion_map_options.value());\n\n    const std::array expected_values{DataVector{1.0}, DataVector{2.0},\n                                     DataVector{3.0}};\n    CHECK(hardcoded_options.initial_values == expected_values);\n    CHECK(hardcoded_options.initial_values_outer_boundary ==\n          std::array{DataVector{1.0}, DataVector{0.0}, DataVector{0.0}});\n    CHECK(hardcoded_options.decay_timescale_outer_boundary == 50.0);\n    CHECK_FALSE(hardcoded_options.decay_timescale.has_value());\n    CHECK(hardcoded_options.asymptotic_velocity_outer_boundary ==\n          std::optional{-1.e-5});\n\n    const auto expansion_fots =\n        get_expansion(expansion_map_options.value(), 0.3, 2.9);\n    const auto& expansion_ptr = expansion_fots.at(\"Expansion\");\n    const auto& expansion_outer_boundary_ptr =\n        expansion_fots.at(\"ExpansionOuterBoundary\");\n\n    const auto* expansion =\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n            expansion_ptr.get());\n    const auto* expansion_outer_boundary =\n        dynamic_cast<domain::FunctionsOfTime::FixedSpeedCubic*>(\n            expansion_outer_boundary_ptr.get());\n\n    REQUIRE(expansion != nullptr);\n    REQUIRE(expansion_outer_boundary != nullptr);\n\n    CHECK(expansion->time_bounds() == std::array{0.3, 2.9});\n    CHECK(expansion->func_and_2_derivs(0.3) == expected_values);\n    CHECK(expansion_outer_boundary->time_bounds() == std::array{0.3, infinity});\n    CHECK(expansion_outer_boundary->decay_timescale() == 50.0);\n    CHECK(expansion_outer_boundary->velocity() == -1e-5);\n  };\n\n  non_settle_fots.template operator()<false>();\n  non_settle_fots.template operator()<true>();\n\n  {\n    INFO(\"Hardcoded AllowSettleFots = true, Settle Fots\");\n    const auto expansion_map_options =\n        TestHelpers::test_option_tag<ExpansionMapOptions<true>>(\n            \"InitialValues: [1.0, 2.0, 3.0]\\n\"\n            \"InitialValuesOuterBoundary: [4.0, 5.0, 6.0]\\n\"\n            \"DecayTimescaleOuterBoundary: 50\\n\"\n            \"DecayTimescale: 40\\n\");\n\n    REQUIRE(expansion_map_options.has_value());\n    CHECK(std::holds_alternative<ExpansionMapOptions<true>>(\n        expansion_map_options.value()));\n\n    const auto& hardcoded_options =\n        std::get<ExpansionMapOptions<true>>(expansion_map_options.value());\n\n    const std::array expected_values{DataVector{1.0}, DataVector{2.0},\n                                     DataVector{3.0}};\n    CHECK(hardcoded_options.initial_values == expected_values);\n    CHECK(hardcoded_options.initial_values_outer_boundary ==\n          std::array{DataVector{4.0}, DataVector{5.0}, DataVector{6.0}});\n    CHECK(hardcoded_options.decay_timescale_outer_boundary == 50.0);\n    CHECK(hardcoded_options.decay_timescale == std::optional{40.0});\n    CHECK_FALSE(\n        hardcoded_options.asymptotic_velocity_outer_boundary.has_value());\n\n    const auto expansion_fots =\n        get_expansion(expansion_map_options.value(), 0.3, 2.9);\n    const auto& expansion_ptr = expansion_fots.at(\"Expansion\");\n    const auto& expansion_outer_boundary_ptr =\n        expansion_fots.at(\"ExpansionOuterBoundary\");\n\n    const auto* expansion =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstant*>(\n            expansion_ptr.get());\n    const auto* expansion_outer_boundary =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstant*>(\n            expansion_outer_boundary_ptr.get());\n\n    REQUIRE(expansion != nullptr);\n    REQUIRE(expansion_outer_boundary != nullptr);\n\n    CHECK(expansion->time_bounds() == std::array{0.3, infinity});\n    CHECK(expansion->func_and_2_derivs(0.3) == expected_values);\n    CHECK(expansion_outer_boundary->time_bounds() == std::array{0.3, infinity});\n  }\n\n  const std::string filename{\"Commencement.h5\"};\n  const std::string subfile_name{\"VolumeData\"};\n\n  domain::FunctionsOfTimeMap functions_of_time{};\n  functions_of_time[\"Expansion\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0, std::array{DataVector{1.0}, DataVector{2.0}, DataVector{3.0}},\n          100.0);\n  functions_of_time[\"ExpansionOuterBoundary\"] =\n      std::make_unique<domain::FunctionsOfTime::FixedSpeedCubic>(0.0, 0.0,\n                                                                 -1e-5, 50.0);\n\n  {\n    INFO(\"FromVolumeFile non-Settle FoTs\");\n\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n\n    TestHelpers::domain::creators::write_volume_data(filename, subfile_name,\n                                                     0.3, functions_of_time);\n\n    const auto expansion_map_options =\n        TestHelpers::test_option_tag<ExpansionMapOptions<false>>(\n            \"H5Filename: Commencement.h5\\n\"\n            \"SubfileName: VolumeData\");\n\n    REQUIRE(expansion_map_options.has_value());\n    CHECK(\n        std::holds_alternative<FromVolumeFile>(expansion_map_options.value()));\n\n    const auto expansion_fots =\n        get_expansion(expansion_map_options.value(), 0.3, 100.0);\n    const auto& expansion_ptr = expansion_fots.at(\"Expansion\");\n    const auto& expansion_outer_boundary_ptr =\n        expansion_fots.at(\"ExpansionOuterBoundary\");\n\n    const auto* expansion =\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n            expansion_ptr.get());\n    const auto* expansion_outer_boundary =\n        dynamic_cast<domain::FunctionsOfTime::FixedSpeedCubic*>(\n            expansion_outer_boundary_ptr.get());\n\n    REQUIRE(expansion != nullptr);\n    REQUIRE(expansion_outer_boundary != nullptr);\n\n    CHECK(expansion->time_bounds() == std::array{0.3, 100.0});\n    CHECK(expansion->func_and_2_derivs(0.3) ==\n          functions_of_time.at(\"Expansion\")->func_and_2_derivs(0.3));\n    CHECK(\n        expansion_outer_boundary->func_and_2_derivs(0.3) ==\n        functions_of_time.at(\"ExpansionOuterBoundary\")->func_and_2_derivs(0.3));\n    CHECK(expansion_outer_boundary->time_bounds() == std::array{0.0, infinity});\n    CHECK(expansion_outer_boundary->decay_timescale() == 50.0);\n    CHECK(expansion_outer_boundary->velocity() == -1e-5);\n  }\n  {\n    INFO(\"FromVolumeFile Settle FoTs\");\n\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n\n    functions_of_time[\"Expansion\"] =\n        std::make_unique<domain::FunctionsOfTime::SettleToConstant>(\n            std::array{DataVector{1.0}, DataVector{2.0}, DataVector{3.0}}, 0.0,\n            100.0);\n    functions_of_time[\"ExpansionOuterBoundary\"] =\n        std::make_unique<domain::FunctionsOfTime::SettleToConstant>(\n            std::array{DataVector{3.0}, DataVector{4.0}, DataVector{6.0}}, 0.0,\n            100.0);\n\n    TestHelpers::domain::creators::write_volume_data(filename, subfile_name,\n                                                     0.3, functions_of_time);\n\n    const auto expansion_map_options =\n        TestHelpers::test_option_tag<ExpansionMapOptions<true>>(\n            \"H5Filename: Commencement.h5\\n\"\n            \"SubfileName: VolumeData\");\n\n    REQUIRE(expansion_map_options.has_value());\n    CHECK(\n        std::holds_alternative<FromVolumeFile>(expansion_map_options.value()));\n\n    const auto expansion_fots =\n        get_expansion(expansion_map_options.value(), 0.3, 100.0);\n    const auto& expansion_ptr = expansion_fots.at(\"Expansion\");\n    const auto& expansion_outer_boundary_ptr =\n        expansion_fots.at(\"ExpansionOuterBoundary\");\n\n    const auto* expansion =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstant*>(\n            expansion_ptr.get());\n    const auto* expansion_outer_boundary =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstant*>(\n            expansion_outer_boundary_ptr.get());\n\n    REQUIRE(expansion != nullptr);\n    REQUIRE(expansion_outer_boundary != nullptr);\n\n    CHECK(expansion->time_bounds() == std::array{0.0, infinity});\n    CHECK(expansion->func_and_2_derivs(0.3) ==\n          functions_of_time.at(\"Expansion\")->func_and_2_derivs(0.3));\n    CHECK(\n        expansion_outer_boundary->func_and_2_derivs(0.3) ==\n        functions_of_time.at(\"ExpansionOuterBoundary\")->func_and_2_derivs(0.3));\n    CHECK(expansion_outer_boundary->time_bounds() == std::array{0.0, infinity});\n  }\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependentOptions.ExpansionMap\",\n                  \"[Domain][Unit]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  test_expansion_map_options();\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_FromVolumeFile.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <catch2/matchers/catch_matchers.hpp>\n#include <catch2/matchers/catch_matchers_string.hpp>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependent/TestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test(const std::string& function_of_time_name) {\n  const std::string filename{\"HorseRadish.h5\"};\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  const std::string subfile_name{\"VolumeData\"};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[function_of_time_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0,\n          std::array{DataVector{3, 0.0}, DataVector{3, 1.0},\n                     DataVector{3, 0.0}},\n          100.0);\n\n  const double time = 50.0;\n\n  TestHelpers::domain::creators::write_volume_data(filename, subfile_name, time,\n                                                   functions_of_time);\n\n  const auto from_volume_file = TestHelpers::test_creation<\n      domain::creators::time_dependent_options::FromVolumeFile>(\n      \"H5Filename: \" + filename + \"\\nSubfileName: \" + subfile_name);\n\n  const domain::FunctionsOfTimeMap fot_from_file =\n      from_volume_file.retrieve_function_of_time({function_of_time_name}, time);\n\n  std::array<DataVector, 3> expected_values{\n      DataVector{3, 1.0 * time}, DataVector{3, 1.0}, DataVector{3, 0.0}};\n\n  CHECK(dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>&>(\n            *fot_from_file.at(function_of_time_name)) ==\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>&>(\n            *functions_of_time.at(function_of_time_name)));\n\n  const auto from_volume_file_replay =\n      domain::creators::time_dependent_options::FromVolumeFile(\n          filename, subfile_name, true);\n\n  const domain::FunctionsOfTimeMap fot_from_file_replay =\n      from_volume_file.retrieve_function_of_time({function_of_time_name}, time);\n\n  CHECK(dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>&>(\n            *fot_from_file_replay.at(function_of_time_name)) ==\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>&>(\n            *functions_of_time.at(function_of_time_name)));\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n\nvoid test_errors() {\n  const std::string filename{\"HorseRadishErrors.h5\"};\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  std::string subfile_name{\"VolumeData\"};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{filename};\n    h5_file.insert<h5::VolumeData>(subfile_name);\n  }\n\n  using FromVolumeFile =\n      domain::creators::time_dependent_options::FromVolumeFile;\n\n  auto from_volume_file = TestHelpers::test_creation<FromVolumeFile>(\n      \"H5Filename: \" + filename + \"\\nSubfileName: \" + subfile_name);\n\n  // Need new subfile to write to\n  subfile_name += \"0\";\n  TestHelpers::domain::creators::write_volume_data(filename, subfile_name, 0.0);\n\n  from_volume_file = TestHelpers::test_creation<FromVolumeFile>(\n      \"H5Filename: \" + filename + \"\\nSubfileName: \" + subfile_name);\n\n  CHECK_THROWS_WITH(\n      (from_volume_file.retrieve_function_of_time({\"Expansion\"}, 0.0)),\n      Catch::Matchers::ContainsSubstring(\n          \"There are no functions of time in the subfile \"));\n\n  subfile_name += \"0\";\n  from_volume_file = TestHelpers::test_creation<FromVolumeFile>(\n      \"H5Filename: \" + filename + \"\\nSubfileName: \" + subfile_name);\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"Translation\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0,\n          std::array{DataVector{3, 0.0}, DataVector{3, 1.0},\n                     DataVector{3, 0.0}},\n          100.0);\n\n  TestHelpers::domain::creators::write_volume_data(filename, subfile_name, 0.0,\n                                                   functions_of_time);\n\n  CHECK_THROWS_WITH(\n      (from_volume_file.retrieve_function_of_time({\"Expansion\"}, 1.0)),\n      Catch::Matchers::ContainsSubstring(\"No observation with value \"));\n  CHECK_THROWS_WITH(\n      (from_volume_file.retrieve_function_of_time({\"Expansion\"}, 0.0)),\n      Catch::Matchers::ContainsSubstring(\n          \"No function of time named Expansion in the subfile \"));\n\n  subfile_name += \"0\";\n  from_volume_file = TestHelpers::test_creation<FromVolumeFile>(\n      \"H5Filename: \" + filename + \"\\nSubfileName: \" + subfile_name);\n  functions_of_time.clear();\n  functions_of_time[\"Expansion\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0,\n          std::array{DataVector{3, 0.0}, DataVector{3, 1.0},\n                     DataVector{3, 0.0}},\n          1.0);\n\n  TestHelpers::domain::creators::write_volume_data(filename, subfile_name, 10.0,\n                                                   functions_of_time);\n\n  CHECK_THROWS_WITH(\n      (from_volume_file.retrieve_function_of_time({\"Expansion\"}, 10.0)),\n      Catch::Matchers::ContainsSubstring(\"Expansion: The requested time\") and\n          Catch::Matchers::ContainsSubstring(\n              \"is out of the range of the function of time\"));\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependentOptions.FromVolumeFile\",\n                  \"[Unit][Domain]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  test(\"Translation\");\n  test_errors();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_GridCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/GridCenters.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain::creators::time_dependent_options {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependentOptions.GridCenters\",\n                  \"[Domain][Unit]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  {\n    const auto grid_centers_options =\n        TestHelpers::test_option_tag<GridCentersOptions>(\n            \"SpecEvolutionParametersPerlFile: \" + unit_test_src_path() +\n            \"../InputFiles/GrMhd/GhValenciaDivClean/EvolutionParameters.perl\\n\"\n            \"ScaleInspiralRateBy: Auto\\n\");\n    REQUIRE(grid_centers_options.has_value());\n    CHECK(grid_centers_options->initial_values ==\n          std::array{DataVector{16.1996, 3.59764e-5, 0.0, -16.2, 0.0, 0.0},\n                     DataVector{-0.00008095, 0.0, 0.0, 0.00008095, 0.0, 0.0},\n                     DataVector{0.0, 0.0, 0.0, 0.0, 0.0, 0.0}});\n    CHECK(not grid_centers_options->scale_inspiral_rate_by.has_value());\n  }\n\n  {\n    const auto grid_centers_options =\n        TestHelpers::test_option_tag<GridCentersOptions>(\n            \"SpecEvolutionParametersPerlFile: \" + unit_test_src_path() +\n            \"../InputFiles/GrMhd/GhValenciaDivClean/EvolutionParameters.perl\\n\"\n            \"ScaleInspiralRateBy: 0.9\\n\");\n    REQUIRE(grid_centers_options.has_value());\n    CHECK(grid_centers_options->initial_values ==\n          std::array{DataVector{16.1996, 3.59764e-5, 0.0, -16.2, 0.0, 0.0},\n                     DataVector{-0.00008095 * 0.9, 0.0, 0.0, 0.00008095 * 0.9,\n                                0.0, 0.0},\n                     DataVector{0.0, 0.0, 0.0, 0.0, 0.0, 0.0}});\n    CHECK(grid_centers_options->scale_inspiral_rate_by.value() == 0.9);\n  }\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_RotationMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <sstream>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependent/TestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nnamespace {\nconstexpr double infinity = std::numeric_limits<double>::infinity();\nvoid test_rotation_map_options() {\n  {\n    INFO(\"None\");\n    const auto rotation_map_options =\n        TestHelpers::test_option_tag<RotationMapOptions<false>>(\"None\");\n\n    CHECK(not rotation_map_options.has_value());\n  }\n  const auto non_settle_fots = []<bool AllowSettleFoTs>() {\n    INFO(\"Hardcoded AllowSettleFots = \" + std::to_string(AllowSettleFoTs) +\n         \", Non-Settle Fots\");\n    const auto rotation_map_options =\n        TestHelpers::test_option_tag<RotationMapOptions<AllowSettleFoTs>>(\n            \"InitialAngularVelocity: [0.0, 0.4, 0.1]\\n\");\n\n    REQUIRE(rotation_map_options.has_value());\n    CHECK(std::holds_alternative<RotationMapOptions<AllowSettleFoTs>>(\n        rotation_map_options.value()));\n\n    const auto& hardcoded_options =\n        std::get<RotationMapOptions<AllowSettleFoTs>>(\n            rotation_map_options.value());\n\n    CHECK(hardcoded_options.quaternions ==\n          std::array{DataVector{1.0, 0.0, 0.0, 0.0}, DataVector{4, 0.0},\n                     DataVector{4, 0.0}});\n    CHECK(hardcoded_options.angles ==\n          std::array{DataVector{3, 0.0}, DataVector{0.0, 0.4, 0.1},\n                     DataVector{3, 0.0}, DataVector{3, 0.0}});\n    CHECK_FALSE(hardcoded_options.decay_timescale.has_value());\n\n    const auto rotation_ptr =\n        get_rotation(rotation_map_options.value(), 0.3, 2.9);\n\n    const auto* rotation =\n        dynamic_cast<domain::FunctionsOfTime::QuaternionFunctionOfTime<3>*>(\n            rotation_ptr.get());\n\n    REQUIRE(rotation != nullptr);\n\n    CHECK(rotation->time_bounds() == std::array{0.3, 2.9});\n    CHECK(rotation->func(0.3)[0] == DataVector{1.0, 0.0, 0.0, 0.0});\n  };\n\n  non_settle_fots.template operator()<false>();\n  non_settle_fots.template operator()<true>();\n\n  {\n    INFO(\"Hardcoded AllowSettleFots = true, Settle Fots\");\n    const auto rotation_map_options =\n        TestHelpers::test_option_tag<RotationMapOptions<true>>(\n            \"InitialQuaternions: [[1.0, 0.0, 0.0, 0.0]]\\n\"\n            \"DecayTimescale: 40\\n\");\n\n    REQUIRE(rotation_map_options.has_value());\n    CHECK(std::holds_alternative<RotationMapOptions<true>>(\n        rotation_map_options.value()));\n\n    const auto& hardcoded_options =\n        std::get<RotationMapOptions<true>>(rotation_map_options.value());\n\n    const std::array expected_values{DataVector{1.0}, DataVector{2.0},\n                                     DataVector{3.0}};\n    CHECK(hardcoded_options.quaternions ==\n          std::array{DataVector{1.0, 0.0, 0.0, 0.0}, DataVector{4, 0.0},\n                     DataVector{4, 0.0}});\n    CHECK(hardcoded_options.angles == make_array<4>(DataVector{3, 0.0}));\n    CHECK(hardcoded_options.decay_timescale == std::optional{40.0});\n\n    const auto rotation_ptr =\n        get_rotation(rotation_map_options.value(), 0.3, 2.9);\n\n    const auto* rotation =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstantQuaternion*>(\n            rotation_ptr.get());\n\n    REQUIRE(rotation != nullptr);\n\n    CHECK(rotation->time_bounds() == std::array{0.3, infinity});\n    CHECK(rotation->func(0.3)[0] == DataVector{1.0, 0.0, 0.0, 0.0});\n  }\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"Rotation\"] =\n      std::make_unique<domain::FunctionsOfTime::QuaternionFunctionOfTime<3>>(\n          0.0, std::array{DataVector{1.0, 0.0, 0.0, 0.0}},\n          std::array{DataVector{3, 0.1}, DataVector{3, 0.2}, DataVector{3, 0.3},\n                     DataVector{3, 0.4}},\n          100.0);\n\n  const std::string filename{\"GoatCheese.h5\"};\n  const std::string subfile_name{\"VolumeData\"};\n\n  {\n    INFO(\"FromVolumeFile non-Settle FoTs\");\n\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n\n    TestHelpers::domain::creators::write_volume_data(filename, subfile_name,\n                                                     0.3, functions_of_time);\n\n    const auto rotation_map_options =\n        TestHelpers::test_option_tag<RotationMapOptions<false>>(\n            \"H5Filename: GoatCheese.h5\\n\"\n            \"SubfileName: VolumeData\");\n\n    REQUIRE(rotation_map_options.has_value());\n    CHECK(std::holds_alternative<FromVolumeFile>(rotation_map_options.value()));\n\n    const auto rotation_ptr =\n        get_rotation(rotation_map_options.value(), 0.3, 100.0);\n\n    const auto* rotation =\n        dynamic_cast<domain::FunctionsOfTime::QuaternionFunctionOfTime<3>*>(\n            rotation_ptr.get());\n\n    REQUIRE(rotation != nullptr);\n\n    CHECK(rotation->time_bounds() == std::array{0.3, 100.0});\n    CHECK(rotation->func_and_2_derivs(0.3) ==\n          functions_of_time.at(\"Rotation\")->func_and_2_derivs(0.3));\n  }\n  {\n    INFO(\"FromVolumeFile Settle FoTs\");\n\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n\n    functions_of_time[\"Rotation\"] =\n        std::make_unique<domain::FunctionsOfTime::SettleToConstantQuaternion>(\n            std::array{DataVector{1.0, 0.0, 0.0, 0.0}, DataVector{4, 2.0},\n                       DataVector{4, 3.0}},\n            0.0, 100.0);\n\n    TestHelpers::domain::creators::write_volume_data(filename, subfile_name,\n                                                     0.3, functions_of_time);\n\n    const auto rotation_map_options =\n        TestHelpers::test_option_tag<RotationMapOptions<true>>(\n            \"H5Filename: GoatCheese.h5\\n\"\n            \"SubfileName: VolumeData\");\n\n    REQUIRE(rotation_map_options.has_value());\n    CHECK(std::holds_alternative<FromVolumeFile>(rotation_map_options.value()));\n\n    const auto rotation_ptr =\n        get_rotation(rotation_map_options.value(), 0.3, 100.0);\n\n    const auto* rotation =\n        dynamic_cast<domain::FunctionsOfTime::SettleToConstantQuaternion*>(\n            rotation_ptr.get());\n\n    REQUIRE(rotation != nullptr);\n\n    CHECK(rotation->time_bounds() == std::array{0.0, infinity});\n    CHECK(rotation->func_and_2_derivs(0.3) ==\n          functions_of_time.at(\"Rotation\")->func_and_2_derivs(0.3));\n  }\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependentOptions.RotationMap\",\n                  \"[Domain][Unit]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  test_rotation_map_options();\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_ShapeMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <fstream>\n#include <iomanip>\n#include <ios>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependent/TestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nnamespace {\nvoid test_kerr_schild_boyer_lindquist() {\n  const auto kerr_schild_boyer_lindquist = TestHelpers::test_creation<\n      domain::creators::time_dependent_options::KerrSchildFromBoyerLindquist>(\n      \"Mass: 1.7\\n\"\n      \"Spin: [0.45, 0.12, 0.34]\");\n  CHECK(kerr_schild_boyer_lindquist.mass == 1.7);\n  CHECK(kerr_schild_boyer_lindquist.spin == std::array{0.45, 0.12, 0.34});\n}\n\nvoid test_ylms_from_file() {\n  const auto ylms_from_file = TestHelpers::test_creation<\n      domain::creators::time_dependent_options::YlmsFromFile>(\n      \"H5Filename: TotalEclipseOfTheHeart.h5\\n\"\n      \"SubfileNames:\\n\"\n      \"  - Ylm_coefs\\n\"\n      \"  - dt_Ylm_coefs\\n\"\n      \"MatchTime: 1.7\\n\"\n      \"MatchTimeEpsilon: 1.0e-14\\n\"\n      \"CheckFrame: True\\n\"\n      \"SetL1CoefsToZero: False\");\n  CHECK(ylms_from_file.h5_filename == \"TotalEclipseOfTheHeart.h5\");\n  CHECK(ylms_from_file.subfile_names ==\n        std::vector<std::string>{\"Ylm_coefs\", \"dt_Ylm_coefs\"});\n  CHECK(ylms_from_file.match_time == 1.7);\n  CHECK(ylms_from_file.match_time_epsilon.has_value());\n  CHECK(ylms_from_file.match_time_epsilon.value() == 1.0e-14);\n  CHECK_FALSE(ylms_from_file.set_l1_coefs_to_zero);\n}\n\ntemplate <typename Generator>\nvoid test_funcs(const gsl::not_null<Generator*> generator) {\n  const double inner_radius = 0.5;\n  const size_t l_max = 8;\n  {\n    INFO(\"None\");\n    const auto shape_map_options = TestHelpers::test_option_tag<\n        ShapeMapOptions<false, domain::ObjectLabel::None>>(\"None\");\n\n    CHECK(not shape_map_options.has_value());\n  }\n  {\n    INFO(\"Spherical\");\n    const auto shape_map_options = TestHelpers::test_option_tag<\n        ShapeMapOptions<false, domain::ObjectLabel::None>>(\n        \"LMax: 8\\n\"\n        \"CoefficientTruncationLimit: 0.0\\n\"\n        \"InitialValues: Spherical\\n\"\n        \"SizeInitialValues: Auto\\n\");\n\n    REQUIRE(shape_map_options.has_value());\n    REQUIRE(std::holds_alternative<\n            ShapeMapOptions<false, domain::ObjectLabel::None>>(\n        shape_map_options.value()));\n\n    const FunctionsOfTimeMap shape_and_size = get_shape_and_size(\n        shape_map_options.value(), 0.1, 0.8, 0.9, inner_radius);\n\n    CHECK(shape_and_size.at(\"Shape\")->time_bounds() == std::array{0.1, 0.8});\n    CHECK(shape_and_size.at(\"Size\")->time_bounds() == std::array{0.1, 0.9});\n\n    CHECK(shape_and_size.at(\"Shape\")->func_and_2_derivs(0.1) ==\n          make_array<3>(\n              DataVector{ylm::Spherepack::spectral_size(l_max, l_max), 0.0}));\n    CHECK(shape_and_size.at(\"Size\")->func_and_2_derivs(0.1) ==\n          make_array<3>(DataVector{0.0}));\n  }\n  // We choose a Schwarzschild BH so all coefs are zero and it's easy to check\n  {\n    INFO(\"KerrSchild\");\n    const auto shape_map_options = TestHelpers::test_option_tag<\n        domain::creators::time_dependent_options::ShapeMapOptions<\n            false, domain::ObjectLabel::None>>(\n        \"LMax: 8\\n\"\n        \"CoefficientTruncationLimit: 0.0\\n\"\n        \"InitialValues:\\n\"\n        \"  Mass: 1.0\\n\"\n        \"  Spin: [0.0, 0.0, 0.0]\\n\"\n        \"SizeInitialValues: [0.5, 1.0, 2.4]\");\n\n    REQUIRE(shape_map_options.has_value());\n    REQUIRE(std::holds_alternative<\n            ShapeMapOptions<false, domain::ObjectLabel::None>>(\n        shape_map_options.value()));\n\n    const FunctionsOfTimeMap shape_and_size = get_shape_and_size(\n        shape_map_options.value(), 0.1, 0.8, 0.9, inner_radius);\n\n    CHECK(shape_and_size.at(\"Shape\")->time_bounds() == std::array{0.1, 0.8});\n    CHECK(shape_and_size.at(\"Size\")->time_bounds() == std::array{0.1, 0.9});\n\n    CHECK(shape_and_size.at(\"Shape\")->func_and_2_derivs(0.1) ==\n          make_array<3>(\n              DataVector{ylm::Spherepack::spectral_size(l_max, l_max), 0.0}));\n    CHECK(shape_and_size.at(\"Size\")->func_and_2_derivs(0.1) ==\n          std::array{DataVector{0.5}, DataVector{1.0}, DataVector{2.4}});\n  }\n  {\n    const std::string test_filename{\"TotalEclipseOfTheHeart.h5\"};\n    if (file_system::check_if_file_exists(test_filename)) {\n      file_system::rm(test_filename, true);\n    }\n    const std::vector<std::string> subfile_names{\"Ylm_coefs\", \"dt_Ylm_coefs\"};\n    const std::string volume_subfile_name{\"VolumeData\"};\n    const double time = 1.7;\n    // Purposefully larger than the LMax in the options so that the\n    // Strahlkorpers will be restricted\n    const size_t file_l_max = 10;\n    std::uniform_real_distribution<double> distribution(0.1, 2.0);\n    using Frame = ::Frame::Inertial;\n    std::array<ylm::Strahlkorper<Frame>, 3> strahlkorpers{};\n    std::vector<std::string> legend{};\n    std::vector<double> data{};\n\n    // Scoped to close h5 file\n    {\n      h5::H5File<h5::AccessType::ReadWrite> test_file(test_filename, true);\n      for (size_t i = 0; i < 3; i++) {\n        legend.clear();\n        data.clear();\n        const auto radius = make_with_random_values<DataVector>(\n            generator, distribution,\n            DataVector(ylm::Spherepack::physical_size(file_l_max, file_l_max),\n                       std::numeric_limits<double>::signaling_NaN()));\n        if (i < 2) {\n          gsl::at(strahlkorpers, i) = ylm::Strahlkorper<Frame>(\n              file_l_max, file_l_max, radius, std::array{0.0, 0.0, 0.0});\n        } else {\n          gsl::at(strahlkorpers, i) = ylm::Strahlkorper<Frame>(\n              file_l_max, inner_radius, std::array{0.0, 0.0, 0.0});\n          continue;\n        }\n\n        ylm::fill_ylm_legend_and_data(\n            make_not_null(&legend), make_not_null(&(data)),\n            gsl::at(strahlkorpers, i), time, file_l_max);\n\n        auto& file = test_file.insert<h5::Dat>(\"/\" + subfile_names[i], legend);\n        file.append(data);\n        test_file.close_current_object();\n      }\n    }\n\n    // Take advantage of the funcs and write those to the volume file\n    const std::string shape_name{\"ShapeB\"};\n    const std::string size_name{\"SizeB\"};\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time{};\n    {\n      INFO(\"YlmsFromFile specify size\");\n      const auto shape_map_options = TestHelpers::test_option_tag<\n          domain::creators::time_dependent_options::ShapeMapOptions<\n              true, domain::ObjectLabel::B>>(\n          \"LMax: 8\\n\"\n          \"CoefficientTruncationLimit: 0.0\\n\"\n          \"InitialValues:\\n\"\n          \"  H5Filename: TotalEclipseOfTheHeart.h5\\n\"\n          \"  SubfileNames:\\n\"\n          \"    - Ylm_coefs\\n\"\n          \"    - dt_Ylm_coefs\\n\"\n          \"  MatchTime: 1.7\\n\"\n          \"  MatchTimeEpsilon: Auto\\n\"\n          \"  SetL1CoefsToZero: True\\n\"\n          \"  CheckFrame: False\\n\"\n          \"SizeInitialValues: [1.1, 2.2, 3.3]\\n\"\n          \"TransitionEndsAtCube: True\");\n\n      REQUIRE(shape_map_options.has_value());\n      REQUIRE(\n          std::holds_alternative<ShapeMapOptions<true, domain::ObjectLabel::B>>(\n              shape_map_options.value()));\n\n      const FunctionsOfTimeMap shape_and_size = get_shape_and_size(\n          shape_map_options.value(), 0.1, 0.8, 0.9, inner_radius);\n\n      CHECK(shape_and_size.at(shape_name)->time_bounds() ==\n            std::array{0.1, 0.8});\n      CHECK(shape_and_size.at(size_name)->time_bounds() ==\n            std::array{0.1, 0.9});\n\n      const auto shape_funcs =\n          shape_and_size.at(shape_name)->func_and_2_derivs(0.1);\n      const auto size_funcs =\n          shape_and_size.at(size_name)->func_and_2_derivs(0.1);\n\n      ylm::SpherepackIterator iter{l_max, l_max};\n      ylm::SpherepackIterator file_iter{file_l_max, file_l_max};\n      for (size_t i = 0; i < shape_funcs.size(); i++) {\n        const size_t expected_size =\n            ylm::Spherepack::spectral_size(l_max, l_max);\n        // Make sure they were restricted properly\n        CHECK(gsl::at(shape_funcs, i).size() == expected_size);\n\n        // Loop pointwise so we only check the coefficients that matter\n        for (size_t l = 0; l <= l_max; l++) {\n          for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n            CAPTURE(l);\n            CAPTURE(m);\n            const double expected_value =\n                l < 2 ? 0.0\n                      : -1.0 * gsl::at(strahlkorpers, i)\n                                   .coefficients()[file_iter.set(l, m)()];\n            CHECK(gsl::at(shape_funcs, i)[iter.set(l, m)()] == expected_value);\n          }\n        }\n      }\n      CHECK(size_funcs ==\n            std::array{DataVector{1.1}, DataVector{2.2}, DataVector{3.3}});\n\n      functions_of_time[shape_name] =\n          shape_and_size.at(shape_name)->get_clone();\n      functions_of_time[size_name] = shape_and_size.at(size_name)->get_clone();\n\n      // For the FromVolumeFile test later on\n      TestHelpers::domain::creators::write_volume_data(\n          test_filename, volume_subfile_name, 0.2, functions_of_time);\n    }\n\n    // Already checked that shape funcs are correct. Here just check that size\n    // funcs were automatically set to correct values\n    {\n      INFO(\"YlmsFromFile auto size\");\n      const auto shape_map_options = TestHelpers::test_option_tag<\n          domain::creators::time_dependent_options::ShapeMapOptions<\n              true, domain::ObjectLabel::B>>(\n          \"LMax: 8\\n\"\n          \"CoefficientTruncationLimit: 0.0\\n\"\n          \"InitialValues:\\n\"\n          \"  H5Filename: TotalEclipseOfTheHeart.h5\\n\"\n          \"  SubfileNames:\\n\"\n          \"    - Ylm_coefs\\n\"\n          \"    - dt_Ylm_coefs\\n\"\n          \"  MatchTime: 1.7\\n\"\n          \"  MatchTimeEpsilon: Auto\\n\"\n          \"  SetL1CoefsToZero: True\\n\"\n          \"  CheckFrame: False\\n\"\n          \"SizeInitialValues: Auto\\n\"\n          \"TransitionEndsAtCube: true\");\n\n      REQUIRE(shape_map_options.has_value());\n      REQUIRE(\n          std::holds_alternative<ShapeMapOptions<true, domain::ObjectLabel::B>>(\n              shape_map_options.value()));\n\n      const FunctionsOfTimeMap shape_and_size = get_shape_and_size(\n          shape_map_options.value(), 0.1, 0.8, 0.9, inner_radius);\n\n      CHECK_ITERABLE_APPROX(\n          shape_and_size.at(size_name)->func_and_2_derivs(0.1),\n          (std::array{\n              DataVector{(inner_radius - ylm::Spherepack::average(\n                                             strahlkorpers[0].coefficients())) *\n                         2.0 * sqrt(M_PI)},\n              DataVector{(inner_radius - ylm::Spherepack::average(\n                                             strahlkorpers[1].coefficients())) *\n                         2.0 * sqrt(M_PI)},\n              DataVector{0.0}}));\n    }\n\n    {\n      INFO(\"FromVolumeFile\");\n      const auto shape_map_options = TestHelpers::test_option_tag<\n          domain::creators::time_dependent_options::ShapeMapOptions<\n              false, domain::ObjectLabel::B>>(\n          \"LMax: 6\\n\"\n          \"CoefficientTruncationLimit: 0.0\\n\"\n          \"TransitionEndsAtCube: false\\n\"\n          \"H5Filename: TotalEclipseOfTheHeart.h5\\n\"\n          \"SubfileName: VolumeData\\n\");\n\n      REQUIRE(shape_map_options.has_value());\n      REQUIRE(std::holds_alternative<\n              FromVolumeFileShapeSize<domain::ObjectLabel::B>>(\n          shape_map_options.value()));\n\n      CHECK(std::get<FromVolumeFileShapeSize<domain::ObjectLabel::B>>(\n                shape_map_options.value())\n                .l_max == 6);\n\n      const FunctionsOfTimeMap shape_and_size = get_shape_and_size(\n          shape_map_options.value(), 0.2, 1.1, 1.2, inner_radius);\n\n      CHECK(shape_and_size.at(shape_name)->time_bounds() ==\n            std::array{0.2, 1.1});\n      CHECK(shape_and_size.at(size_name)->time_bounds() ==\n            std::array{0.2, 1.2});\n\n      const auto shape = shape_and_size.at(shape_name)->func_and_2_derivs(0.2);\n      const auto expected_shape = functions_of_time.at(shape_name)\n                                      ->create_at_time(0.2, 1.1)\n                                      ->func_and_2_derivs(0.2);\n\n      // We restricted from LMax = 8 to LMax = 6\n      const size_t expected_l_max = l_max - 2;\n      const size_t expected_size =\n          ylm::Spherepack::spectral_size(expected_l_max, expected_l_max);\n      ylm::SpherepackIterator file_iter{l_max, l_max};\n      ylm::SpherepackIterator iter{expected_l_max, expected_l_max};\n\n      for (size_t i = 0; i < 3; i++) {\n        CAPTURE(i);\n        CHECK(gsl::at(shape, i).size() == expected_size);\n\n        // Loop pointwise so we only check the coefficients that matter\n        for (size_t l = 0; l <= expected_l_max; l++) {\n          CAPTURE(l);\n          for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n            CAPTURE(m);\n            CHECK(gsl::at(shape, i)[iter.set(l, m)()] ==\n                  gsl::at(expected_shape, i)[file_iter.set(l, m)()]);\n          }\n        }\n      }\n      CHECK(shape_and_size.at(size_name)->func_and_2_derivs(0.2) ==\n            functions_of_time.at(size_name)->func_and_2_derivs(0.2));\n    }\n\n    if (file_system::check_if_file_exists(test_filename)) {\n      file_system::rm(test_filename, true);\n    }\n  }\n  {\n    INFO(\"YlmsFromSpec\");\n    const std::string test_filename{\"PartialEclipseOfTheHeart.dat\"};\n    if (file_system::check_if_file_exists(test_filename)) {\n      file_system::rm(test_filename, true);\n    }\n    const double time = 1.7;\n    // Purposefully larger than the LMax in the options so that the\n    // Strahlkorpers will be restricted\n    const size_t file_l_max = 10;\n    const std::uniform_real_distribution<double> distribution(0.1, 2.0);\n    using Frame = ::Frame::Inertial;\n    ylm::Strahlkorper<Frame> strahlkorper{};\n    std::vector<std::string> legend{};\n    std::vector<double> data{};\n\n    // Scoped to close dat file\n    {\n      std::fstream test_file{};\n      test_file.open(test_filename, std::ios_base::out);\n      test_file << std::scientific << std::setprecision(16);\n      legend.clear();\n      data.clear();\n      const auto radius = make_with_random_values<DataVector>(\n          generator, distribution,\n          ylm::Spherepack::physical_size(file_l_max, file_l_max));\n      strahlkorper = ylm::Strahlkorper<Frame>(file_l_max, file_l_max, radius,\n                                              std::array{0.0, 0.0, 0.0});\n\n      ylm::fill_ylm_legend_and_data(make_not_null(&legend),\n                                    make_not_null(&(data)), strahlkorper, time,\n                                    file_l_max);\n\n      test_file << \"# This is a random comment\\n\";\n      // These columns won't be exactly what is in the SpEC dat file, but they\n      // will be close enough. Also SpEC starts columns numbers from 1\n      size_t col_num = 1;\n      for (const std::string& col : legend) {\n        // SpEC doesn't write Lmax\n        if (col == \"Lmax\") {\n          continue;\n        }\n\n        test_file << \"# [\" << col_num << \"] = \" << col << \"\\n\";\n        col_num++;\n      }\n      test_file << \"# Another random comment to show\\n\";\n\n      // Write three rows of same data\n      for (size_t i = 0; i < 3; i++) {\n        // Time and center\n        test_file << time + static_cast<double>(i) << \" \" << 0.0 << \" \" << 0.0\n                  << \" \" << 0.0;\n        // Skip writing time, center, and Lmax\n        for (size_t j = 5; j < data.size(); j++) {\n          test_file << \" \" << data[j];\n        }\n        test_file << \"\\n\";\n      }\n    }\n\n    {\n      const auto shape_map_options = TestHelpers::test_option_tag<\n          domain::creators::time_dependent_options::ShapeMapOptions<\n              false, domain::ObjectLabel::None>>(\n          \"LMax: 8\\n\"\n          \"CoefficientTruncationLimit: 0.0\\n\"\n          \"InitialValues:\\n\"\n          \"  DatFilename: PartialEclipseOfTheHeart.dat\\n\"\n          \"  MatchTime: 2.7\\n\"\n          \"  MatchTimeEpsilon: Auto\\n\"\n          \"  SetL1CoefsToZero: True\\n\"\n          \"SizeInitialValues: Auto\");\n\n      REQUIRE(shape_map_options.has_value());\n      REQUIRE(std::holds_alternative<\n              ShapeMapOptions<false, domain::ObjectLabel::None>>(\n          shape_map_options.value()));\n\n      const FunctionsOfTimeMap shape_and_size = get_shape_and_size(\n          shape_map_options.value(), 0.2, 1.1, 1.2, inner_radius);\n\n      const auto shape_funcs =\n          shape_and_size.at(\"Shape\")->func_and_2_derivs(0.2);\n      const auto size_funcs = shape_and_size.at(\"Size\")->func_and_2_derivs(0.2);\n\n      ylm::SpherepackIterator iter{l_max, l_max};\n      ylm::SpherepackIterator file_iter{file_l_max, file_l_max};\n      for (size_t i = 0; i < shape_funcs.size(); i++) {\n        const size_t expected_size =\n            ylm::Spherepack::spectral_size(l_max, l_max);\n        // Make sure they were restricted properly\n        CHECK(gsl::at(shape_funcs, i).size() == expected_size);\n\n        if (i > 0) {\n          CHECK(gsl::at(shape_funcs, i) == DataVector{expected_size, 0.0});\n          continue;\n        }\n\n        // Loop pointwise so we only check the coefficients that matter\n        for (size_t l = 0; l <= l_max; l++) {\n          for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n            CAPTURE(l);\n            CAPTURE(m);\n            const double expected_value =\n                l < 2\n                    ? 0.0\n                    : -1.0 * strahlkorper.coefficients()[file_iter.set(l, m)()];\n            CHECK(gsl::at(shape_funcs, i)[iter.set(l, m)()] == expected_value);\n          }\n        }\n      }\n      CHECK(size_funcs ==\n            std::array{DataVector{-1.0 * strahlkorper.coefficients()[0] *\n                                  sqrt(0.5 * M_PI)},\n                       DataVector{0.0}, DataVector{0.0}});\n    }\n\n    {\n      const auto shape_map_options = TestHelpers::test_option_tag<\n          domain::creators::time_dependent_options::ShapeMapOptions<\n              false, domain::ObjectLabel::None>>(\n          \"LMax: 8\\n\"\n          \"CoefficientTruncationLimit: 0.0\\n\"\n          \"InitialValues:\\n\"\n          \"  DatFilename: PartialEclipseOfTheHeart.dat\\n\"\n          \"  MatchTime: 10000.0\\n\"\n          \"  MatchTimeEpsilon: Auto\\n\"\n          \"  SetL1CoefsToZero: True\\n\"\n          \"SizeInitialValues: Auto\");\n      CHECK_THROWS_WITH(\n          (get_shape_and_size(shape_map_options.value(), 0.2, 1.1, 1.2,\n                              inner_radius)),\n          Catch::Matchers::ContainsSubstring(\n              \"Unable to find requested time 1\") and\n              Catch::Matchers::ContainsSubstring(\n                  \"in SpEC dat file PartialEclipseOfTheHeart.dat\"));\n    }\n\n    if (file_system::check_if_file_exists(test_filename)) {\n      file_system::rm(test_filename, true);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependentOptions.ShapeMap\",\n                  \"[Domain][Unit]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  MAKE_GENERATOR(generator);\n  test_kerr_schild_boyer_lindquist();\n  test_ylms_from_file();\n  test_funcs(make_not_null(&generator));\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_Skew.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <sstream>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/SkewMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependent/TestHelpers.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nnamespace {\nvoid test_skew_map_options() {\n  {\n    INFO(\"None\");\n    const auto skew_map_options =\n        TestHelpers::test_option_tag<SkewMapOptions>(\"None\");\n\n    CHECK(not skew_map_options.has_value());\n  }\n  {\n    INFO(\"Hardcoded options\");\n    const auto skew_map_options = TestHelpers::test_option_tag<\n        domain::creators::time_dependent_options::SkewMapOptions>(\n        \"InitialValuesY: [0.1, -0.2, 0.3]\\n\"\n        \"InitialValuesZ: [-0.4, 0.5, -0.6]\\n\");\n\n    REQUIRE(skew_map_options.has_value());\n    CHECK(std::holds_alternative<SkewMapOptions>(skew_map_options.value()));\n    const auto& hard_coded_options =\n        std::get<SkewMapOptions>(skew_map_options.value());\n    CHECK(hard_coded_options.initial_angles_y == std::array{0.1, -0.2, 0.3});\n    CHECK(hard_coded_options.initial_angles_z == std::array{-0.4, 0.5, -0.6});\n\n    const auto skew_ptr = get_skew(skew_map_options.value(), 0.1, 65.8);\n\n    const auto* skew =\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n            skew_ptr.get());\n\n    CHECK(skew != nullptr);\n\n    CHECK(skew->time_bounds() == std::array{0.1, 65.8});\n    CHECK(skew->func_and_2_derivs(0.1) == std::array{\n                                              DataVector{0.1, -0.4},\n                                              DataVector{-0.2, 0.5},\n                                              DataVector{0.3, -0.6},\n                                          });\n  }\n  {\n    INFO(\"FromVolumeFile\");\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time{};\n    functions_of_time[\"Skew\"] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n            0.0,\n            std::array{DataVector{2, 1.0}, DataVector{2, 2.0},\n                       DataVector{2, 3.0}},\n            100.0);\n    const std::string filename{\"Wildfires.h5\"};\n    const std::string subfile_name{\"VolumeData\"};\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n\n    TestHelpers::domain::creators::write_volume_data(filename, subfile_name,\n                                                     0.1, functions_of_time);\n\n    const auto skew_map_options = TestHelpers::test_option_tag<\n        domain::creators::time_dependent_options::SkewMapOptions>(\n        \"H5Filename: \" + filename + \"\\nSubfileName: \" + subfile_name);\n\n    REQUIRE(skew_map_options.has_value());\n    CHECK(std::holds_alternative<FromVolumeFile>(skew_map_options.value()));\n\n    const auto skew_ptr = get_skew(skew_map_options.value(), 0.1, 65.8);\n\n    const auto* skew =\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n            skew_ptr.get());\n\n    CHECK(skew != nullptr);\n\n    CHECK(skew->time_bounds() == std::array{0.1, 65.8});\n    CHECK_ITERABLE_APPROX(skew->func_and_2_derivs(0.3),\n                          functions_of_time.at(\"Skew\")->func_and_2_derivs(0.3));\n\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependentOptions.SkewMap\",\n                  \"[Domain][Unit]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  test_skew_map_options();\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_Sphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Frame {\nstruct Grid;\nstruct Distorted;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace domain::creators::sphere {\nnamespace {\n// nullopt implies no shape map at all\nstd::string create_option_string(const std::optional<bool> use_non_zero_shape) {\n  return \"InitialTime: 1.5\\n\"\n         \"ShapeMap:\" +\n         (use_non_zero_shape.has_value()\n              ? \"\\n\"\n                \"  LMax: 8\\n\"\n                \"  CoefficientTruncationLimit: 0.\\n\"\n                \"  SizeInitialValues: Auto\\n\"\n                \"  InitialValues:\" +\n                    (use_non_zero_shape.value() ? \"\\n\"\n                                                  \"    Mass: 1.0\\n\"\n                                                  \"    Spin: [0.0, 0.0, 0.0]\\n\"s\n                                                : \" Spherical\\n\"s)\n              : \" None\\n\") +\n         \"RotationMap: None\\n\"\n         \"ExpansionMap: None\\n\"\n         \"TranslationMap:\\n\"\n         \"  InitialValues: [[0.1, -3.2, 1.1], [-0.3, 0.5, -0.7],\"\n         \" [0.1, -0.4, 0.02]]\\n\"\n         \"TransitionRotScaleTrans: True\"s;\n}\n// nullopt implies no shape map at all\nvoid test(const std::optional<bool> use_non_zero_shape) {\n  CAPTURE(use_non_zero_shape);\n  const double initial_time = 1.5;\n  const size_t l_max = 8;\n\n  auto time_dep_options = TestHelpers::test_creation<TimeDependentMapOptions>(\n      create_option_string(use_non_zero_shape));\n\n  CHECK(time_dep_options.using_distorted_frame() ==\n        use_non_zero_shape.has_value());\n\n  std::unordered_map<std::string, double> expiration_times{\n      {TimeDependentMapOptions::shape_name,\n       use_non_zero_shape.has_value()\n           ? 15.5\n           : std::numeric_limits<double>::infinity()},\n      {TimeDependentMapOptions::size_name,\n       std::numeric_limits<double>::infinity()},\n      {TimeDependentMapOptions::translation_name,\n       std::numeric_limits<double>::infinity()}};\n\n  // These are hard-coded so this is just a regression test\n  CHECK(TimeDependentMapOptions::size_name == \"Size\"s);\n  CHECK(TimeDependentMapOptions::shape_name == \"Shape\"s);\n  CHECK(TimeDependentMapOptions::rotation_name == \"Rotation\"s);\n  CHECK(TimeDependentMapOptions::expansion_name == \"Expansion\"s);\n  CHECK(TimeDependentMapOptions::expansion_outer_boundary_name ==\n        \"ExpansionOuterBoundary\"s);\n  CHECK(TimeDependentMapOptions::translation_name == \"Translation\"s);\n\n  using PP2 = domain::FunctionsOfTime::PiecewisePolynomial<2>;\n  using PP3 = domain::FunctionsOfTime::PiecewisePolynomial<3>;\n  DataVector size_func{1, 0.0};\n  DataVector size_deriv{1, 0.0};\n  DataVector size_2nd_deriv{1, 0.0};\n  PP3 size{\n      initial_time,\n      std::array<DataVector, 4>{{size_func, size_deriv, size_2nd_deriv, {0.0}}},\n      expiration_times.at(TimeDependentMapOptions::size_name)};\n  const DataVector shape_zeros{ylm::Spherepack::spectral_size(l_max, l_max),\n                               0.0};\n  PP2 shape_all_zero{\n      initial_time,\n      std::array<DataVector, 3>{shape_zeros, shape_zeros, shape_zeros},\n      expiration_times.at(TimeDependentMapOptions::shape_name)};\n\n  DataVector initial_translation_center{{0.1, -3.2, 1.1}};\n  DataVector initial_velocity{{-0.3, 0.5, -0.7}};\n  DataVector initial_translation_acceleration{{0.1, -0.4, 0.02}};\n  PP2 translation_non_zero{\n      initial_time,\n      std::array<DataVector, 3>{{initial_translation_center, initial_velocity,\n                                 initial_translation_acceleration}},\n      expiration_times.at(TimeDependentMapOptions::translation_name)};\n\n  const std::array<double, 3> center{-5.0, -0.01, -0.02};\n  const double inner_radius = 0.5;\n  const std::vector<double> radial_partitions{2.1, 3.1};\n  const double outer_radius = 3.9;\n\n  // All of these maps are tested individually. Rather than going through\n  // the effort of coming up with a source coordinate and calculating\n  // analytically what we would get after it's mapped, we just check that\n  // whether it's supposed to be a nullptr and if it's not, if it's the\n  // identity and that the jacobians are time dependent.\n  const auto check_map = [](const auto& map, const bool is_null,\n                            const bool is_identity) {\n    if (is_null) {\n      CHECK(map == nullptr);\n    } else {\n      REQUIRE(map != nullptr);\n      CHECK(map->is_identity() == is_identity);\n      CHECK(map->inv_jacobian_is_time_dependent() == not is_identity);\n      CHECK(map->jacobian_is_time_dependent() == not is_identity);\n    }\n  };\n\n  {\n    INFO(\"Excised\");\n    time_dep_options.build_maps(center, /* filled */ false, inner_radius,\n                                radial_partitions, outer_radius);\n    // Inner shell with shape\n    check_map(time_dep_options.grid_to_distorted_map(0, false, 6),\n              not use_non_zero_shape.has_value(), false);\n    check_map(time_dep_options.distorted_to_inertial_map(0, false, 6),\n              not use_non_zero_shape.has_value(), false);\n    check_map(time_dep_options.grid_to_inertial_map(0, false, false, 6), false,\n              false);\n    // Shell without shape\n    check_map(time_dep_options.grid_to_distorted_map(6, false, 6), true, false);\n    check_map(time_dep_options.distorted_to_inertial_map(6, false, 6), true,\n              false);\n    check_map(time_dep_options.grid_to_inertial_map(6, false, false, 6), false,\n              false);\n    // Inner S2 shell with shape\n    check_map(time_dep_options.grid_to_distorted_map(0, false, 1),\n              not use_non_zero_shape.has_value(), false);\n    check_map(time_dep_options.distorted_to_inertial_map(0, false, 1),\n              not use_non_zero_shape.has_value(), false);\n    check_map(time_dep_options.grid_to_inertial_map(0, false, false, 1), false,\n              false);\n    // S2 Shell without shape\n    check_map(time_dep_options.grid_to_distorted_map(1, false, 1), true, false);\n    check_map(time_dep_options.distorted_to_inertial_map(1, false, 1), true,\n              false);\n    check_map(time_dep_options.grid_to_inertial_map(1, false, false, 1), false,\n              false);\n  }\n  {\n    INFO(\"Filled\");\n    time_dep_options.build_maps(center, /* filled */ true, inner_radius,\n                                radial_partitions, outer_radius);\n    // Inner shell: cube to sphere\n    check_map(time_dep_options.grid_to_distorted_map(0, false, 6),\n              not use_non_zero_shape.has_value(), false);\n    check_map(time_dep_options.distorted_to_inertial_map(0, false, 6),\n              not use_non_zero_shape.has_value(), false);\n    check_map(time_dep_options.grid_to_inertial_map(0, false, false, 6), false,\n              false);\n    // Shape rolloff region\n    check_map(time_dep_options.grid_to_distorted_map(6, false, 6),\n              not use_non_zero_shape.has_value(), false);\n    check_map(time_dep_options.distorted_to_inertial_map(6, false, 6),\n              not use_non_zero_shape.has_value(), false);\n    check_map(time_dep_options.grid_to_inertial_map(0, false, false, 6), false,\n              false);\n    // Shell without shape\n    check_map(time_dep_options.grid_to_distorted_map(12, false, 6), true,\n              false);\n    check_map(time_dep_options.distorted_to_inertial_map(12, false, 6), true,\n              false);\n    check_map(time_dep_options.grid_to_inertial_map(12, false, false, 6), false,\n              false);\n    // Inner cube\n    check_map(time_dep_options.grid_to_distorted_map(12, true, 6), true, false);\n    check_map(time_dep_options.distorted_to_inertial_map(12, true, 6), true,\n              false);\n    check_map(time_dep_options.grid_to_inertial_map(12, false, true, 6), false,\n              false);\n  }\n\n  const auto functions_of_time =\n      time_dep_options.create_functions_of_time(expiration_times);\n\n  if (use_non_zero_shape.has_value()) {\n    CHECK(\n        dynamic_cast<PP3&>(\n            *functions_of_time.at(TimeDependentMapOptions::size_name).get()) ==\n        size);\n    // This will always be zero based on our options above. Just easier to check\n    // that way\n    CHECK(\n        dynamic_cast<PP2&>(\n            *functions_of_time.at(TimeDependentMapOptions::shape_name).get()) ==\n        shape_all_zero);\n\n    CHECK(dynamic_cast<PP2&>(\n              *functions_of_time.at(TimeDependentMapOptions::translation_name)\n                   .get()) == translation_non_zero);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependentOptions.Sphere\",\n                  \"[Domain][Unit]\") {\n  test({true});\n  test({false});\n  test(std::nullopt);\n}\n}  // namespace domain::creators::sphere\n"
  },
  {
    "path": "tests/Unit/Domain/Creators/TimeDependentOptions/Test_TranslationMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <sstream>\n#include <string>\n#include <variant>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/FromVolumeFile.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/Creators/TimeDependent/TestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain::creators::time_dependent_options {\nnamespace {\ntemplate <size_t Dim>\nstd::string make_array_str(const double value) {\n  std::stringstream ss{};\n  ss << \"[\" << value;\n  if constexpr (Dim > 1) {\n    ss << \", \" << value;\n    if constexpr (Dim > 2) {\n      ss << \", \" << value;\n    }\n  }\n  ss << \"]\";\n\n  return ss.str();\n}\n\ntemplate <size_t Dim>\nvoid test_translation_map_options() {\n  {\n    INFO(\"None\");\n    const auto translation_map_options =\n        TestHelpers::test_option_tag<TranslationMapOptions<Dim>>(\"None\");\n\n    CHECK(not translation_map_options.has_value());\n  }\n  {\n    INFO(\"Hardcoded options\");\n    const auto translation_map_options = TestHelpers::test_option_tag<\n        domain::creators::time_dependent_options::TranslationMapOptions<Dim>>(\n        \"InitialValues: [\" + make_array_str<Dim>(1.0) + \",\" +\n        make_array_str<Dim>(2.0) + \",\" + make_array_str<Dim>(3.0) + \"]\");\n\n    REQUIRE(translation_map_options.has_value());\n    CHECK(std::holds_alternative<TranslationMapOptions<Dim>>(\n        translation_map_options.value()));\n    const std::array initial_values{DataVector{Dim, 1.0}, DataVector{Dim, 2.0},\n                                    DataVector{Dim, 3.0}};\n    CHECK(std::get<TranslationMapOptions<Dim>>(translation_map_options.value())\n              .initial_values == initial_values);\n\n    const auto translation_ptr =\n        get_translation(translation_map_options.value(), 0.1, 65.8);\n\n    const auto* translation =\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n            translation_ptr.get());\n\n    CHECK(translation != nullptr);\n\n    CHECK(translation->time_bounds() == std::array{0.1, 65.8});\n    CHECK(translation->func_and_2_derivs(0.1) == initial_values);\n  }\n  {\n    INFO(\"FromVolumeFile\");\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time{};\n    functions_of_time[\"Translation\"] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n            0.0,\n            std::array{DataVector{Dim, 1.0}, DataVector{Dim, 2.0},\n                       DataVector{Dim, 3.0}},\n            100.0);\n    const std::string filename{\"NewFinalActualFinal2Final.h5\"};\n    const std::string subfile_name{\"VolumeData\"};\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n\n    TestHelpers::domain::creators::write_volume_data(filename, subfile_name,\n                                                     0.1, functions_of_time);\n\n    const auto translation_map_options = TestHelpers::test_option_tag<\n        domain::creators::time_dependent_options::TranslationMapOptions<Dim>>(\n        \"H5Filename: \" + filename + \"\\nSubfileName: \" + subfile_name);\n\n    REQUIRE(translation_map_options.has_value());\n    CHECK(std::holds_alternative<FromVolumeFile>(\n        translation_map_options.value()));\n    const std::array initial_values{DataVector{Dim, 1.0}, DataVector{Dim, 2.0},\n                                    DataVector{Dim, 3.0}};\n\n    const auto translation_ptr =\n        get_translation(translation_map_options.value(), 0.1, 65.8);\n\n    const auto* translation =\n        dynamic_cast<domain::FunctionsOfTime::PiecewisePolynomial<2>*>(\n            translation_ptr.get());\n\n    CHECK(translation != nullptr);\n\n    CHECK(translation->time_bounds() == std::array{0.1, 65.8});\n    CHECK_ITERABLE_APPROX(\n        translation->func_and_2_derivs(0.3),\n        functions_of_time.at(\"Translation\")->func_and_2_derivs(0.3));\n\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Creators.TimeDependentOptions.TranslationMap\",\n                  \"[Domain][Unit]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  test_translation_map_options<1>();\n  test_translation_map_options<2>();\n  test_translation_map_options<3>();\n}\n}  // namespace domain::creators::time_dependent_options\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_FunctionsOfTime\")\n\nset(LIBRARY_SOURCES\n  Test_FixedSpeedCubic.cpp\n  Test_FunctionsOfTimeAreReady.cpp\n  Test_IntegratedFunctionOfTime.cpp\n  Test_ThreadsafeList.cpp\n  Test_OutputTimeBounds.cpp\n  Test_PiecewisePolynomial.cpp\n  Test_QuaternionFunctionOfTime.cpp\n  Test_QuaternionHelpers.cpp\n  Test_SettleToConstant.cpp\n  Test_SettleToConstantQuaternion.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Actions\n  DataStructures\n  DomainCreators\n  FunctionsOfTime\n  H5\n  Informer\n  Options\n  SerializationHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/IntegratedFunctionOfTime.serializations",
    "content": "version 0:YM2jukUU1RIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAACEAAAAAAAAAEQAAAAAAAAAhAAAAAAAAAAARAAAAAAAAAAAAAAAAAAADgPwEAAAAAAAAAAAA=\nversion 1:YM2jukUU1RIBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAACEAAAAAAAAAEQAAAAAAAAAhAAAAAAAAAAARAAAAAAAAAAAAAAAAAAADgPwEA\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/PiecewisePolynomial.serializations",
    "content": "version 2:HTxPg0lAGawCAAAAAAAAAAAAAAAAABxAAgAAAAAAAAAAAAAAAAAAQAEAAAAAAAAAAAAAAAAACEABAAAAAAAAAAAAAAAAABBAAAAAAAAAFEABAAAAAAAAAAAAAAAAAC5AAQAAAAAAAAAAAAAAAAAYQA==\nversion 3:HTxPg0lAGawDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAHEABAAAAAAAAAAAAAAAAAC5AAQAAAAAAAAAAAAAAAAAYQAAAAAAAAAAUQAEAAAAAAAAAAAAAAAAACEABAAAAAAAAAAAAAAAAABBAAQ==\nversion 4:HTxPg0lAGawEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAHEABAAAAAAAAAAAAAAAAAC5AAQAAAAAAAAAAAAAAAAAYQAAAAAAAAAAUQAEAAAAAAAAAAAAAAAAACEABAAAAAAAAAAAAAAAAABBAAQAAAAAAAAAA\nversion 5:HTxPg0lAGawFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAHEABAAAAAAAAAAAAAAAAAC5AAQAAAAAAAAAAAAAAAAAYQAAAAAAAAAAUQAEAAAAAAAAAAAAAAAAACEABAAAAAAAAAAAAAAAAABBAAQ==\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/QuaternionFunctionOfTime.serializations",
    "content": "version 3:QgotNJTEM38DAAAAAAAAAAIAAAAAAAAAAAAAAAAA8D8CAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9CtejcD0OQAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzMzMz4z8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC+nxov3SQCQAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD0K16NwPQ5AAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwIAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzMzMz4z8EAAAAAAAAAGyeACURE9s/AAAAAAAAAAAAAAAAAAAAAFOgCbvd/uw/\nversion 3, PiecewisePolynomial 3:QgotNJTEM38DAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL6fGi/dJAJAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPQrXo3A9DkADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMzMzPjPwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPQrXo3A9DkADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAADwPwIAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzMzMz4z8EAAAAAAAAAGyeACURE9s/AAAAAAAAAAAAAAAAAAAAAFOgCbvd/uw/\nversion 4:QgotNJTEM38EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D9sngAlERPbPwAAAAAAAAAAAAAAAAAAAABToAm73f7sPwAzMzMzMzPjPwAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL6fGi/dJAJAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPQrXo3A9DkADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMzMzPjPwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPQrXo3A9DkADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=\nversion 5:QgotNJTEM38FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D9sngAlERPbPwAAAAAAAAAAAAAAAAAAAABToAm73f7sPwAzMzMzMzPjPwAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL6fGi/dJAJAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPQrXo3A9DkADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMzMzPjPwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPQrXo3A9DkADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAA\nversion 6:QgotNJTEM38GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D9sngAlERPbPwAAAAAAAAAAAAAAAAAAAABToAm73f7sPwAzMzMzMzPjPwAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL6fGi/dJAJAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPQrXo3A9DkADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMzMzPjPwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPQrXo3A9DkADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE=\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_FixedSpeedCubic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test(\n    const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>& f_of_t,\n    const double initial_function_value, const double initial_time,\n    const double velocity, const double decay_timescale) {\n  // Check that at t == initial time, f == initial_function_value,\n  // df = 0, and d2f = 0\n  const auto lambdas0 = f_of_t->func_and_2_derivs(initial_time);\n  CHECK(approx(lambdas0[0][0]) == initial_function_value);\n  CHECK(approx(lambdas0[1][0]) == 0.0);\n  CHECK(approx(lambdas0[2][0]) == 0.0);\n\n  const auto lambdas1 = f_of_t->func_and_deriv(initial_time);\n  CHECK(approx(lambdas1[0][0]) == initial_function_value);\n  CHECK(approx(lambdas1[1][0]) == 0.0);\n\n  const auto lambdas2 = f_of_t->func(initial_time);\n  CHECK(approx(lambdas2[0][0]) == initial_function_value);\n\n  // Check that asymptotic values approach df == velocity, d2f == 0\n  const auto lambdas3 = f_of_t->func_and_2_derivs(decay_timescale * 1.0e7);\n  CHECK(approx(lambdas3[1][0]) == velocity);\n  CHECK(approx(lambdas3[2][0]) == 0.0);\n\n  const auto lambdas4 = f_of_t->func_and_deriv(decay_timescale * 1.0e7);\n  CHECK(approx(lambdas4[1][0]) == velocity);\n\n  // Check some time in between where we can easily calculate the answer by\n  // hand. Choosing dt = 1.0 is helpful because 1^n = 1\n  const double check_time = initial_time + 1.0;\n  const double square_decay_timescale = square(decay_timescale);\n  const double denom = square_decay_timescale + 1.0;\n  const auto lambdas5 = f_of_t->func_and_2_derivs(check_time);\n  CHECK(approx(lambdas5[0][0]) ==\n        initial_function_value + velocity * 1.0 / denom);\n  CHECK(approx(lambdas5[1][0]) ==\n        velocity * (3.0 * square_decay_timescale + 1.0) / square(denom));\n  CHECK(approx(lambdas5[2][0]) == 2.0 * velocity * square_decay_timescale *\n                                      (3.0 * square_decay_timescale - 1.0) /\n                                      cube(denom));\n\n  // test time_bounds function\n  const auto t_bounds = f_of_t->time_bounds();\n  CHECK(t_bounds[0] == initial_time);\n  CHECK(t_bounds[1] == std::numeric_limits<double>::infinity());\n  CHECK(f_of_t->expiration_after(check_time) ==\n        std::numeric_limits<double>::infinity());\n\n  const auto* fixed_speed_cubic =\n      dynamic_cast<const domain::FunctionsOfTime::FixedSpeedCubic*>(\n          f_of_t.get());\n\n  CHECK(fixed_speed_cubic->velocity() == velocity);\n  CHECK(fixed_speed_cubic->decay_timescale() == decay_timescale);\n\n  {\n    INFO(\"Test stream operator.\");\n    CHECK(get_output(*fixed_speed_cubic) ==\n          \"FixedSpeedCubic(t=10, f=1, v=0.4, tau^2=25)\");\n  }\n\n  CHECK(dynamic_cast<const domain::FunctionsOfTime::FixedSpeedCubic&>(\n            *f_of_t->get_clone()) ==\n        dynamic_cast<const domain::FunctionsOfTime::FixedSpeedCubic&>(\n            *f_of_t->create_at_time(0.0, 0.0)));\n}\n\nvoid test_function(const double initial_function_value,\n                   const double initial_time, const double velocity,\n                   const double decay_timescale) {\n  INFO(\"Test with base class construction.\");\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t =\n      std::make_unique<domain::FunctionsOfTime::FixedSpeedCubic>(\n          initial_function_value, initial_time, velocity, decay_timescale);\n  test(f_of_t, initial_function_value, initial_time, velocity, decay_timescale);\n\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t2 =\n      serialize_and_deserialize(f_of_t);\n  test(f_of_t2, initial_function_value, initial_time, velocity,\n       decay_timescale);\n\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t3 =\n      f_of_t->get_clone();\n  test(f_of_t3, initial_function_value, initial_time, velocity,\n       decay_timescale);\n}\n\nvoid test_operator(const double initial_function_value,\n                   const double initial_time, const double velocity,\n                   const double decay_timescale) {\n  INFO(\"Test operator==\");\n  using FixedSpeedCubic = domain::FunctionsOfTime::FixedSpeedCubic;\n  CHECK(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                        decay_timescale} ==\n        FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                        decay_timescale});\n  CHECK_FALSE(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                              decay_timescale} !=\n              FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                              decay_timescale});\n\n  CHECK(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                        decay_timescale} !=\n        FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                        2.0 * decay_timescale});\n  CHECK_FALSE(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                              decay_timescale} ==\n              FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                              2.0 * decay_timescale});\n\n  CHECK(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                        decay_timescale} !=\n        FixedSpeedCubic{initial_function_value, initial_time, 2.0 * velocity,\n                        decay_timescale});\n  CHECK_FALSE(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                              decay_timescale} ==\n              FixedSpeedCubic{initial_function_value, initial_time,\n                              2.0 * velocity, decay_timescale});\n\n  CHECK(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                        decay_timescale} !=\n        FixedSpeedCubic{initial_function_value, 2.0 * initial_time, velocity,\n                        decay_timescale});\n  CHECK_FALSE(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                              decay_timescale} ==\n              FixedSpeedCubic{initial_function_value, 2.0 * initial_time,\n                              velocity, decay_timescale});\n\n  CHECK(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                        decay_timescale} !=\n        FixedSpeedCubic{2.0 * initial_function_value, initial_time, velocity,\n                        decay_timescale});\n  CHECK_FALSE(FixedSpeedCubic{initial_function_value, initial_time, velocity,\n                              decay_timescale} ==\n              FixedSpeedCubic{2.0 * initial_function_value, initial_time,\n                              velocity, decay_timescale});\n}\n\nvoid test_errors() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        constexpr double initial_function_value = 1.0;\n        constexpr double initial_time = 0.0;\n        constexpr double velocity = -0.1;\n        constexpr double decay_timescale = 0.0;\n        const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t =\n            std::make_unique<domain::FunctionsOfTime::FixedSpeedCubic>(\n                initial_function_value, initial_time, velocity,\n                decay_timescale);\n        test(f_of_t, initial_function_value, initial_time, velocity,\n             decay_timescale);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"FixedSpeedCubic denominator should not be zero\"));\n#endif\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double initial_function_value = 1.0;\n        const double initial_time = 0.0;\n        const double velocity = -0.1;\n        const double decay_timescale = 0.0;\n        const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t =\n            std::make_unique<domain::FunctionsOfTime::FixedSpeedCubic>(\n                initial_function_value, initial_time, velocity,\n                decay_timescale);\n\n        const double update_time = 1.0;\n        const DataVector updated_deriv{};\n        const double next_expr_time = 2.0;\n        f_of_t->update(update_time, updated_deriv, next_expr_time);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Cannot update this FunctionOfTime\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.FixedSpeedCubic\",\n                  \"[Domain][Unit]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  constexpr double initial_function_value{1.0};\n  constexpr double initial_time{10.0};\n  constexpr double velocity{0.4};\n  constexpr double decay_timescale{5.0};\n\n  test_function(initial_function_value, initial_time, velocity,\n                decay_timescale);\n  test_operator(initial_function_value, initial_time, velocity,\n                decay_timescale);\n  test_errors();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_FunctionsOfTimeAreReady.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockDistributedObject.hpp\"\n#include \"Framework/MockRuntimeSystem.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing FunctionMap = domain::Tags::FunctionsOfTimeInitialize::type;\n\nstruct OtherFunctionsOfTime : db::SimpleTag {\n  using type = FunctionMap;\n};\n\nstruct UpdateFoT {\n  static void apply(const gsl::not_null<FunctionMap*> functions,\n                    const std::string& name, const double expiration) {\n    const double current_expiration = functions->at(name)->time_bounds()[1];\n    // Update value doesn't matter\n    (*functions)\n        .at(name)\n        ->update(current_expiration, DataVector{0.0}, expiration);\n  }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nsize_t simple_action_no_args_call_count = 0;\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nsize_t threaded_action_no_args_call_count = 0;\n\nstruct SimpleActionNoArgs {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    ++simple_action_no_args_call_count;\n  }\n};\n\nstruct ThreadedActionNoArgs {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/) {\n    ++threaded_action_no_args_call_count;\n  }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nsize_t simple_action_args_call_count = 0;\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nsize_t threaded_action_args_call_count = 0;\n\nstruct SimpleActionArgs {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const size_t size,\n                    const DataVector& some_data) {\n    ++simple_action_args_call_count;\n    CHECK(some_data.size() == size);\n  }\n};\n\nstruct ThreadedActionArgs {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const size_t size, const DataVector& some_data) {\n    ++threaded_action_args_call_count;\n    CHECK(some_data.size() == size);\n  }\n};\n\ntemplate <typename Metavariables, size_t Index>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using simple_tags_from_options = tmpl::list<Tags::Time>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize, OtherFunctionsOfTime>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Testing,\n      tmpl::list<domain::Actions::CheckFunctionsOfTimeAreReady<3>>>>;\n};\n\ntemplate <typename Metavariables, size_t Index>\nstruct EmptyComponent : Component<Metavariables, Index> {\n  using mutable_global_cache_tags = tmpl::list<>;\n};\nstruct EmptyMetavars {\n  using component_list = tmpl::list<EmptyComponent<EmptyMetavars, 0_st>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables, 0_st>,\n                                    Component<Metavariables, 1_st>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTimeAreReady\", \"[Domain][Unit]\") {\n  register_classes_with_charm<\n      domain::FunctionsOfTime::PiecewisePolynomial<2>>();\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\n  using component0 = Component<Metavariables, 0_st>;\n  using component1 = Component<Metavariables, 1_st>;\n  const component0* const component_0_p = nullptr;\n  const component1* const component_1_p = nullptr;\n  // Not used when not using nodegroup element components\n  constexpr size_t dim = std::numeric_limits<size_t>::max();\n\n  const std::array<DataVector, 3> fot_init{{{0.0}, {0.0}, {0.0}}};\n  FunctionMap functions_of_time{};\n  functions_of_time[\"FunctionA\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0, fot_init, 1.0);\n  functions_of_time[\"FunctionB\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0, fot_init, 1.0);\n\n  FunctionMap other_functions_of_time{};\n  other_functions_of_time[\"OtherA\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0, fot_init, 0.1);\n  other_functions_of_time[\"OtherB\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0, fot_init, 0.1);\n\n  MockRuntimeSystem runner{\n      {}, {std::move(functions_of_time), std::move(other_functions_of_time)}};\n  ActionTesting::emplace_array_component<component0>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0, 2.0);\n  ActionTesting::emplace_array_component<component1>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0, 2.0);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& cache = ActionTesting::cache<component0>(runner, 0);\n\n  // Test the algorithm callback free function.  For this section of the test,\n  // the \"standard\" tags domain::Tags::FunctionsOfTime is ready, but this code\n  // should never examine it.\n  {\n    INFO(\"Testing perform algorithm callback support\");\n    // Neither function ready\n    CHECK(not domain::functions_of_time_are_ready_algorithm_callback<\n          OtherFunctionsOfTime, dim>(cache, 0, component_0_p, 0.5,\n                                     std::nullopt));\n    CHECK(not domain::functions_of_time_are_ready_algorithm_callback<\n          OtherFunctionsOfTime, dim>(cache, 0, component_0_p, 0.5,\n                                     std::unordered_set{\"OtherA\"s}));\n\n    // Make OtherA ready\n    Parallel::mutate<OtherFunctionsOfTime, UpdateFoT>(cache, \"OtherA\"s, 123.0);\n\n    CHECK(domain::functions_of_time_are_ready_algorithm_callback<\n          OtherFunctionsOfTime, dim>(cache, 0, component_0_p, 0.5,\n                                     std::unordered_set{\"OtherA\"s}));\n    CHECK(not domain::functions_of_time_are_ready_algorithm_callback<\n          OtherFunctionsOfTime, dim>(cache, 0, component_0_p, 0.5,\n                                     std::unordered_set{\"OtherA\"s, \"OtherB\"s}));\n\n    // Make OtherB ready\n    Parallel::mutate<OtherFunctionsOfTime, UpdateFoT>(cache, \"OtherB\"s, 456.0);\n\n    CHECK(domain::functions_of_time_are_ready_algorithm_callback<\n          OtherFunctionsOfTime, dim>(cache, 0, component_0_p, 0.5,\n                                     std::unordered_set{\"OtherA\"s, \"OtherB\"s}));\n    CHECK(domain::functions_of_time_are_ready_algorithm_callback<\n          OtherFunctionsOfTime, dim>(cache, 0, component_0_p, 0.5,\n                                     std::nullopt));\n  }\n\n  // Test the action.  This should automatically look at\n  // domain::Tags::FunctionsOfTime.\n  {\n    // Neither function ready\n    CHECK(not ActionTesting::next_action_if_ready<component0>(\n        make_not_null(&runner), 0));\n\n    // Make OtherA ready\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n        cache, \"FunctionA\"s, 5.0);\n\n    CHECK(not ActionTesting::next_action_if_ready<component0>(\n        make_not_null(&runner), 0));\n\n    // Make OtherB ready\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n        cache, \"FunctionB\"s, 10.0);\n\n    CHECK(ActionTesting::next_action_if_ready<component0>(\n        make_not_null(&runner), 0));\n  }\n\n  // Test simple action callback free function\n  {\n    INFO(\"Testing simple action support\");\n    DataVector data{6, 0.0};\n    const size_t size = data.size();\n\n    // No callbacks should be registered\n    CHECK(domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionNoArgs>(\n        cache, 0, component_0_p, 5.0, std::nullopt));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 0);\n    CHECK(simple_action_no_args_call_count == 0_st);\n    CHECK(domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionArgs>(\n        cache, 0, component_0_p, 5.0, std::nullopt, size, data));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 0);\n    CHECK(simple_action_args_call_count == 0_st);\n\n    // Again no callbacks should be registered\n    CHECK(domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionNoArgs>(\n        cache, 0, component_0_p, 6.0, std::unordered_set{\"FunctionB\"s}));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 0);\n    CHECK(simple_action_no_args_call_count == 0_st);\n    CHECK(domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionArgs>(\n        cache, 0, component_0_p, 6.0, std::unordered_set{\"FunctionB\"s}, size,\n        data));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 0);\n    CHECK(simple_action_args_call_count == 0_st);\n\n    // Evaluate at time when A isn't ready. Can't have two different\n    // callbacks on same component so we use different components\n    CHECK(not domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionNoArgs>(\n        cache, 0, component_0_p, 6.0, std::unordered_set{\"FunctionA\"s}));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 0);\n    CHECK(simple_action_no_args_call_count == 0_st);\n    CHECK(not domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionArgs>(\n        cache, 0, component_1_p, 6.0, std::unordered_set{\"FunctionA\"s}, size,\n        data));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component1>(runner,\n                                                                     0) == 0);\n    CHECK(simple_action_args_call_count == 0_st);\n\n    // Make FunctionA valid again\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n        cache, \"FunctionA\"s, 10.0);\n    // Both actions should have been queued\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 1);\n    CHECK(ActionTesting::number_of_queued_simple_actions<component1>(runner,\n                                                                     0) == 1);\n    CHECK(simple_action_no_args_call_count == 0_st);\n    CHECK(simple_action_args_call_count == 0_st);\n    ActionTesting::invoke_queued_simple_action<component0>(\n        make_not_null(&runner), 0);\n    // Only one ran\n    CHECK(simple_action_no_args_call_count == 1_st);\n    CHECK(simple_action_args_call_count == 0_st);\n    ActionTesting::invoke_queued_simple_action<component1>(\n        make_not_null(&runner), 0);\n    // Both ran\n    CHECK(simple_action_no_args_call_count == 1_st);\n    CHECK(simple_action_args_call_count == 1_st);\n    CHECK(domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionNoArgs>(\n        cache, 0, component_0_p, 6.0, std::unordered_set{\"FunctionA\"s}));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 0);\n\n    // Evaluate at time when neither are ready.\n    CHECK(not domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionNoArgs>(\n        cache, 0, component_0_p, 11.0, std::nullopt));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 0);\n    CHECK(simple_action_no_args_call_count == 1_st);\n    CHECK(not domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionArgs>(\n        cache, 0, component_1_p, 11.0, std::nullopt, size, data));\n    CHECK(ActionTesting::number_of_queued_simple_actions<component1>(runner,\n                                                                     0) == 0);\n    CHECK(simple_action_args_call_count == 1_st);\n\n    // Make A valid\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n        cache, \"FunctionA\"s, 15.0);\n    // Both actions should have been queued\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 1);\n    CHECK(ActionTesting::number_of_queued_simple_actions<component1>(runner,\n                                                                     0) == 1);\n    CHECK(simple_action_no_args_call_count == 1_st);\n    CHECK(simple_action_args_call_count == 1_st);\n    ActionTesting::invoke_queued_simple_action<component0>(\n        make_not_null(&runner), 0);\n    CHECK(simple_action_no_args_call_count == 2_st);\n    CHECK(simple_action_args_call_count == 1_st);\n    ActionTesting::invoke_queued_simple_action<component1>(\n        make_not_null(&runner), 0);\n    CHECK(simple_action_no_args_call_count == 2_st);\n    CHECK(simple_action_args_call_count == 2_st);\n\n    // Make B valid. Nothing should have happened\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n        cache, \"FunctionB\"s, 15.0);\n    CHECK(ActionTesting::number_of_queued_simple_actions<component0>(runner,\n                                                                     0) == 0);\n    CHECK(ActionTesting::number_of_queued_simple_actions<component1>(runner,\n                                                                     0) == 0);\n    // No actions should have run\n    CHECK(simple_action_no_args_call_count == 2_st);\n    CHECK(simple_action_args_call_count == 2_st);\n  }\n\n  // Test threaded action callback free function\n  {\n    INFO(\"Testing threaded action support\");\n    DataVector data{6, 0.0};\n    const size_t size = data.size();\n\n    // No callbacks should be registered\n    CHECK(domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionNoArgs>(\n        cache, 0, component_0_p, 5.0, std::nullopt));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 0);\n    CHECK(threaded_action_no_args_call_count == 0_st);\n    CHECK(domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionArgs>(\n        cache, 0, component_0_p, 5.0, std::nullopt, size, data));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 0);\n    CHECK(threaded_action_args_call_count == 0_st);\n\n    // Again no callbacks should be registered\n    CHECK(domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionNoArgs>(\n        cache, 0, component_0_p, 6.0, std::unordered_set{\"FunctionB\"s}));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 0);\n    CHECK(threaded_action_no_args_call_count == 0_st);\n    CHECK(domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionArgs>(\n        cache, 0, component_0_p, 6.0, std::unordered_set{\"FunctionB\"s}, size,\n        data));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 0);\n    CHECK(threaded_action_args_call_count == 0_st);\n\n    // Evaluate at time when A isn't ready. Can't have two different\n    // callbacks on same component so we use different components\n    CHECK(not domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionNoArgs>(\n        cache, 0, component_0_p, 16.0, std::unordered_set{\"FunctionA\"s}));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 0);\n    CHECK(threaded_action_no_args_call_count == 0_st);\n    CHECK(not domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionArgs>(\n        cache, 0, component_1_p, 16.0, std::unordered_set{\"FunctionA\"s}, size,\n        data));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component1>(runner,\n                                                                       0) == 0);\n    CHECK(threaded_action_args_call_count == 0_st);\n\n    // Make FunctionA valid again\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n        cache, \"FunctionA\"s, 20.0);\n    // Both actions should have been queued\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 1);\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component1>(runner,\n                                                                       0) == 1);\n    CHECK(threaded_action_no_args_call_count == 0_st);\n    CHECK(threaded_action_args_call_count == 0_st);\n    ActionTesting::invoke_queued_threaded_action<component0>(\n        make_not_null(&runner), 0);\n    // Only one ran\n    CHECK(threaded_action_no_args_call_count == 1_st);\n    CHECK(threaded_action_args_call_count == 0_st);\n    ActionTesting::invoke_queued_threaded_action<component1>(\n        make_not_null(&runner), 0);\n    // Both ran\n    CHECK(threaded_action_no_args_call_count == 1_st);\n    CHECK(threaded_action_args_call_count == 1_st);\n    CHECK(domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionNoArgs>(\n        cache, 0, component_0_p, 16.0, std::unordered_set{\"FunctionA\"s}));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 0);\n\n    // Evaluate at time when neither are ready.\n    CHECK(not domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionNoArgs>(\n        cache, 0, component_0_p, 21.0, std::nullopt));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 0);\n    CHECK(threaded_action_no_args_call_count == 1_st);\n    CHECK(not domain::functions_of_time_are_ready_threaded_action_callback<\n          domain::Tags::FunctionsOfTime, ThreadedActionArgs>(\n        cache, 0, component_1_p, 21.0, std::nullopt, size, data));\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component1>(runner,\n                                                                       0) == 0);\n    CHECK(threaded_action_args_call_count == 1_st);\n\n    // Make A valid\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n        cache, \"FunctionA\"s, 25.0);\n    // Both actions should have been queued\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 1);\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component1>(runner,\n                                                                       0) == 1);\n    CHECK(threaded_action_no_args_call_count == 1_st);\n    CHECK(threaded_action_args_call_count == 1_st);\n    ActionTesting::invoke_queued_threaded_action<component0>(\n        make_not_null(&runner), 0);\n    CHECK(threaded_action_no_args_call_count == 2_st);\n    CHECK(threaded_action_args_call_count == 1_st);\n    ActionTesting::invoke_queued_threaded_action<component1>(\n        make_not_null(&runner), 0);\n    CHECK(threaded_action_no_args_call_count == 2_st);\n    CHECK(threaded_action_args_call_count == 2_st);\n\n    // Make B valid. Nothing should have happened\n    Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n        cache, \"FunctionB\"s, 25.0);\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component0>(runner,\n                                                                       0) == 0);\n    CHECK(ActionTesting::number_of_queued_threaded_actions<component1>(runner,\n                                                                       0) == 0);\n    // No actions should have run\n    CHECK(threaded_action_no_args_call_count == 2_st);\n    CHECK(threaded_action_args_call_count == 2_st);\n  }\n\n  // No FoTs in the cache\n  {\n    using EmptyMockRuntimeSystem =\n        ActionTesting::MockRuntimeSystem<EmptyMetavars>;\n    using empty_comp = EmptyComponent<EmptyMetavars, 0_st>;\n    const empty_comp* const empty_component_p = nullptr;\n\n    EmptyMockRuntimeSystem empty_runner{{}};\n    ActionTesting::emplace_array_component<empty_comp>(\n        make_not_null(&empty_runner), ActionTesting::NodeId{0},\n        ActionTesting::LocalCoreId{0}, 0, 2.0);\n\n    auto& empty_cache = ActionTesting::cache<empty_comp>(empty_runner, 0);\n    CHECK(domain::functions_of_time_are_ready_algorithm_callback<\n          domain::Tags::FunctionsOfTime, dim>(empty_cache, 0, empty_component_p,\n                                              6.0));\n    CHECK(domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionNoArgs>(\n        empty_cache, 0, empty_component_p, 6.0, std::nullopt));\n    CHECK(domain::functions_of_time_are_ready_simple_action_callback<\n          domain::Tags::FunctionsOfTime, SimpleActionArgs>(\n        empty_cache, 0, empty_component_p, 6.0, std::nullopt, 0_st,\n        DataVector{}));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_IntegratedFunctionOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/IntegratedFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Utilities/Serialization/Versioning.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\nvoid test(const gsl::not_null<FunctionsOfTime::FunctionOfTime*> f_of_t,\n          const double dt, const DataVector& positions,\n          const DataVector& velocities, const bool rotation) {\n  auto* f_of_t_derived =\n      dynamic_cast<FunctionsOfTime::IntegratedFunctionOfTime*>(f_of_t.get());\n  const FunctionsOfTime::IntegratedFunctionOfTime f_of_t_derived_copy =\n      *f_of_t_derived;\n  CHECK(*f_of_t_derived == f_of_t_derived_copy);\n  for (size_t i = 0; i < positions.size() - 1; ++i) {\n    const double t = static_cast<double>(i) * dt;\n    const auto pos_and_vel = f_of_t->func_and_deriv(t);\n    const auto pos = f_of_t->func(t);\n\n    if (rotation) {\n      const DataVector quaternion{cos(positions.at(i) / 2.), 0., 0.,\n                                  sin(positions.at(i) / 2.)};\n      const DataVector dt_quaternion{\n          -sin(positions.at(i) / 2.) * velocities.at(i) / 2., 0., 0.,\n          cos(positions.at(i) / 2.) * velocities.at(i) / 2.};\n      CHECK_ITERABLE_APPROX(pos_and_vel[0], quaternion);\n      CHECK_ITERABLE_APPROX(pos_and_vel[1], dt_quaternion);\n      CHECK_ITERABLE_APPROX(pos[0], quaternion);\n    } else {\n      CHECK(approx(pos_and_vel[0][0]) == positions.at(i));\n      CHECK(approx(pos_and_vel[1][0]) == velocities.at(i));\n      CHECK(approx(pos[0][0]) == positions.at(i));\n    }\n    const auto time_bounds = f_of_t->time_bounds();\n    CHECK(time_bounds[0] == approx(std::max(0., t - 99.5 * dt)));\n    CHECK(time_bounds[1] == approx(t + 0.5 * dt));\n\n    f_of_t_derived->update(time_bounds[1],\n                           DataVector{positions[i + 1], velocities[i + 1]},\n                           time_bounds[1] + dt);\n    CHECK(f_of_t->expiration_after(t) == approx(t + 0.5 * dt));\n    CHECK(*f_of_t_derived != f_of_t_derived_copy);\n  }\n\n  const size_t index = positions.size() - 8;\n  const auto new_time = static_cast<double>(index) * dt;\n  const auto new_expiration = new_time + dt;\n  const auto copy_at_time = f_of_t->create_at_time(new_time, new_expiration);\n\n  CAPTURE(rotation);\n  const double check_time = new_time;\n  CHECK_ITERABLE_APPROX(copy_at_time->func(check_time),\n                        f_of_t_derived->func(check_time));\n  CHECK_ITERABLE_APPROX(copy_at_time->func_and_deriv(check_time),\n                        f_of_t_derived->func_and_deriv(check_time));\n  const auto t_bounds = copy_at_time->time_bounds();\n  CHECK(t_bounds == std::array{new_time, new_expiration});\n\n  const auto old_value = f_of_t->func_and_deriv(new_time);\n  const auto old_bounds = f_of_t->time_bounds();\n  f_of_t->truncate_at_time(new_time);\n  CHECK(f_of_t->time_bounds()[0] > old_bounds[0]);\n  CHECK(f_of_t->time_bounds()[0] <= new_time);\n  CHECK(f_of_t->time_bounds()[1] == old_bounds[1]);\n  CHECK(f_of_t->func_and_deriv(new_time) == old_value);\n}\n\nvoid test_serialization_versioning() {\n  using Integrated = domain::FunctionsOfTime::IntegratedFunctionOfTime;\n  register_classes_with_charm<Integrated>();\n  const std::unique_ptr<FunctionsOfTime::FunctionOfTime> fot(\n      std::make_unique<Integrated>(2.0, std::array<double, 2>{0., 0.5}, 2.5,\n                                   false));\n  fot->update(2.5, {{2.5, 3.}}, 3.0);\n\n  TestHelpers::serialization::test_versioning<Integrated>(\n      \"Domain/FunctionsOfTime/IntegratedFunctionOfTime.serializations\",\n      \"version 1\", fot);\n}\n\nvoid test_out_of_order_update() {\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> func(\n      std::make_unique<domain::FunctionsOfTime::IntegratedFunctionOfTime>(\n          0.0, std::array<double, 2>{0., 0.5}, 0.5, false));\n\n  func->update(2.5, {{2.5, 3.}}, 3.0);\n  func->update(1.5, {{1.5, 2.}}, 2.0);\n\n  CHECK_THROWS_WITH(\n      func->func(0.7),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n  CHECK_THROWS_WITH(\n      func->func(1.3),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n  CHECK_THROWS_WITH(\n      func->func(1.9),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n\n  func->update(0.5, {{0.5, 1.0}}, 1.0);\n\n  // This one should pass now\n  CHECK(func->func_and_deriv(0.7) ==\n        std::array<DataVector, 2>{DataVector{0.5}, DataVector{1.}});\n  CHECK_THROWS_WITH(\n      func->func(1.3),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n  CHECK_THROWS_WITH(\n      func->func(1.9),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n\n  func->update(2.0, {{2., 2.5}}, 2.5);\n\n  CHECK_THROWS_WITH(\n      func->func(1.3),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n  CHECK_THROWS_WITH(\n      func->func(1.9),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n\n  func->update(1.0, {{1., 1.5}}, 1.5);\n\n  CHECK(func->func_and_deriv(1.3) ==\n        std::array<DataVector, 2>{DataVector{1.}, DataVector{1.5}});\n  CHECK(func->func_and_deriv(1.9) ==\n        std::array<DataVector, 2>{DataVector{1.5}, DataVector{2.}});\n  CHECK(func->func_and_deriv(2.2) ==\n        std::array<DataVector, 2>{DataVector{2.}, DataVector{2.5}});\n  CHECK(func->func_and_deriv(2.8) ==\n        std::array<DataVector, 2>{DataVector{2.5}, DataVector{3.}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.IntegratedFunctionOfTime\",\n                  \"[Domain][Unit]\") {\n  FunctionsOfTime::register_derived_with_charm();\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist(-10., 10.);\n  const size_t used_for_size = 300;\n  const auto random_positions = make_with_random_values<DataVector>(\n      make_not_null(&gen), make_not_null(&dist), used_for_size);\n  const auto random_velocities = make_with_random_values<DataVector>(\n      make_not_null(&gen), make_not_null(&dist), used_for_size);\n  double t = 0.0;\n  const double dt = 0.6;\n  const std::array<double, 2> init_func{random_positions[0],\n                                        random_velocities[0]};\n  {\n    INFO(\"Test no rotation with simple construction.\");\n    FunctionsOfTime::IntegratedFunctionOfTime f_of_t(t, init_func, t + 0.5 * dt,\n                                                     false);\n    FunctionsOfTime::IntegratedFunctionOfTime f_of_t2 =\n        serialize_and_deserialize(f_of_t);\n    test(make_not_null(&f_of_t), dt, random_positions, random_velocities,\n         false);\n    test(make_not_null(&f_of_t2), dt, random_positions, random_velocities,\n         false);\n  }\n  {\n    INFO(\"Test rotation with simple construction.\");\n    FunctionsOfTime::IntegratedFunctionOfTime f_of_t(t, init_func, t + 0.5 * dt,\n                                                     true);\n    FunctionsOfTime::IntegratedFunctionOfTime f_of_t2 =\n        serialize_and_deserialize(f_of_t);\n    test(make_not_null(&f_of_t), dt, random_positions, random_velocities, true);\n    test(make_not_null(&f_of_t2), dt, random_positions, random_velocities,\n         true);\n  }\n  {\n    INFO(\"Test no rotation with base class construction.\");\n    std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t1 =\n        std::make_unique<FunctionsOfTime::IntegratedFunctionOfTime>(\n            t, init_func, t + 0.5 * dt, false);\n    std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t2 =\n        serialize_and_deserialize(f_of_t1);\n    std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t3 =\n        f_of_t1->get_clone();\n    test(make_not_null(f_of_t1.get()), dt, random_positions, random_velocities,\n         false);\n    test(make_not_null(f_of_t2.get()), dt, random_positions, random_velocities,\n         false);\n    test(make_not_null(f_of_t3.get()), dt, random_positions, random_velocities,\n         false);\n  }\n  {\n    INFO(\"Test rotation with base class construction.\");\n    std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t1 =\n        std::make_unique<FunctionsOfTime::IntegratedFunctionOfTime>(\n            t, init_func, t + 0.5 * dt, true);\n    std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t2 =\n        serialize_and_deserialize(f_of_t1);\n    std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t3 =\n        f_of_t1->get_clone();\n    test(make_not_null(f_of_t1.get()), dt, random_positions, random_velocities,\n         true);\n    test(make_not_null(f_of_t2.get()), dt, random_positions, random_velocities,\n         true);\n    test(make_not_null(f_of_t3.get()), dt, random_positions, random_velocities,\n         true);\n  }\n  test_serialization_versioning();\n  test_out_of_order_update();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_OutputTimeBounds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <iomanip>\n#include <memory>\n#include <pup.h>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/OutputTimeBounds.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nclass TestFoT : public domain::FunctionsOfTime::FunctionOfTime {\n public:\n  TestFoT() = default;\n\n// clang-tidy: google-runtime-references\n// clang-tidy: cppcoreguidelines-owning-memory,-warnings-as-errors\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestFoT);  // NOLINT\n#pragma GCC diagnostic pop\n\n  explicit TestFoT(CkMigrateMessage* /*unused*/) {}\n\n  auto get_clone() const -> std::unique_ptr<FunctionOfTime> override {\n    return std::make_unique<TestFoT>(*this);\n  }\n\n  TestFoT(const double lower_bound, const double upper_bound)\n      : lower_bound_(lower_bound), upper_bound_(upper_bound) {}\n\n  std::array<double, 2> time_bounds() const override {\n    return {lower_bound_, upper_bound_};\n  }\n\n  double expiration_after(const double /*time*/) const override { ERROR(\"\"); }\n\n  void truncate_at_time(const double /*time*/) override { ERROR(\"\"); }\n\n  std::array<DataVector, 1> func(const double /*t*/) const override {\n    ERROR(\"\");\n  }\n  std::array<DataVector, 2> func_and_deriv(const double /*t*/) const override {\n    ERROR(\"\");\n  }\n  std::array<DataVector, 3> func_and_2_derivs(\n      const double /*t*/) const override {\n    ERROR(\"\");\n  }\n\n  void pup(PUP::er& p) override {\n    domain::FunctionsOfTime::FunctionOfTime::pup(p);\n    p | lower_bound_;\n    p | upper_bound_;\n  }\n\n private:\n  double lower_bound_{};\n  double upper_bound_{};\n};\n\nPUP::able::PUP_ID TestFoT::my_PUP_ID = 0;  // NOLINT\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.OutputTimeBounds\",\n                  \"[Unit][Domain]\") {\n  register_classes_with_charm<TestFoT>();\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  functions_of_time[\"Aang\"] = std::make_unique<TestFoT>(0.0, 1.0);\n  functions_of_time[\"Sokka\"] = std::make_unique<TestFoT>(2.0, 3.0);\n  functions_of_time[\"Katara\"] = std::make_unique<TestFoT>(10.0, 20.0);\n  functions_of_time[\"Toph\"] = std::make_unique<TestFoT>(-3.0, 0.0);\n\n  serialize_and_deserialize(functions_of_time);\n\n  const std::string result =\n      domain::FunctionsOfTime::output_time_bounds(functions_of_time);\n  const std::string aang_line =\n      \" Aang: (0.0000000000000000e+00,1.0000000000000000e+00)\\n\";\n  const std::string sokka_line =\n      \" Sokka: (2.0000000000000000e+00,3.0000000000000000e+00)\\n\";\n  const std::string katara_line =\n      \" Katara: (1.0000000000000000e+01,2.0000000000000000e+01)\\n\";\n  const std::string toph_line =\n      \" Toph: (-3.0000000000000000e+00,0.0000000000000000e+00)\\n\";\n  const std::string title_line = \"FunctionsOfTime time bounds:\\n\";\n\n  // Since it's an unordered map, we can only check that the string contains\n  // what we expect\n  CHECK(result.find(title_line) != std::string::npos);\n  CHECK(result.find(aang_line) != std::string::npos);\n  CHECK(result.find(sokka_line) != std::string::npos);\n  CHECK(result.find(katara_line) != std::string::npos);\n  CHECK(result.find(toph_line) != std::string::npos);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_PiecewisePolynomial.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Utilities/Serialization/Versioning.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <size_t DerivOrder>\nvoid test(const gsl::not_null<FunctionsOfTime::FunctionOfTime*> f_of_t,\n          const gsl::not_null<FunctionsOfTime::PiecewisePolynomial<DerivOrder>*>\n              f_of_t_derived,\n          double t, const double dt, const double final_time) {\n  const FunctionsOfTime::PiecewisePolynomial<DerivOrder> f_of_t_derived_copy =\n      *f_of_t_derived;\n  CHECK(*f_of_t_derived == f_of_t_derived_copy);\n  while (t < final_time) {\n    const auto lambdas_all = f_of_t->func_and_all_derivs(t);\n    CHECK(approx(lambdas_all[0][0]) == cube(t));\n    CHECK(approx(lambdas_all[0][1]) == square(t));\n    CHECK(approx(lambdas_all[1][0]) == 3.0 * square(t));\n    CHECK(approx(lambdas_all[1][1]) == 2.0 * t);\n    CHECK(approx(lambdas_all[2][0]) == 6.0 * t);\n    CHECK(approx(lambdas_all[2][1]) == 2.0);\n    CHECK(approx(lambdas_all[3][0]) == 6.0);\n    CHECK(approx(lambdas_all[3][1]) == 0.0);\n\n    const auto lambdas0 = f_of_t->func_and_2_derivs(t);\n    CHECK(approx(lambdas0[0][0]) == cube(t));\n    CHECK(approx(lambdas0[0][1]) == square(t));\n    CHECK(approx(lambdas0[1][0]) == 3.0 * square(t));\n    CHECK(approx(lambdas0[1][1]) == 2.0 * t);\n    CHECK(approx(lambdas0[2][0]) == 6.0 * t);\n    CHECK(approx(lambdas0[2][1]) == 2.0);\n\n    const auto lambdas1 = f_of_t->func_and_deriv(t);\n    CHECK(approx(lambdas1[0][0]) == cube(t));\n    CHECK(approx(lambdas1[0][1]) == square(t));\n    CHECK(approx(lambdas1[1][0]) == 3.0 * square(t));\n    CHECK(approx(lambdas1[1][1]) == 2.0 * t);\n\n    const auto lambdas2 = f_of_t->func(t);\n    CHECK(approx(lambdas2[0][0]) == cube(t));\n    CHECK(approx(lambdas2[0][1]) == square(t));\n\n    t += dt;\n    f_of_t_derived->update(t, {6.0, 0.0}, t + dt);\n    CHECK(f_of_t->expiration_after(t) == t + dt);\n    CHECK(f_of_t->expiration_after(t + 0.5 * dt) == t + dt);\n    CHECK(f_of_t->expiration_after(t - 0.5 * dt) == t);\n    CHECK(*f_of_t_derived != f_of_t_derived_copy);\n  }\n  // test time_bounds function\n  const auto t_bounds = f_of_t->time_bounds();\n  CHECK(t_bounds[0] == 0.0);\n  CHECK(t_bounds[1] == 4.8);\n  CHECK(f_of_t->expiration_after(0.0) == dt);\n\n  const double new_time = 1.0;\n  const double new_expiration = 2.1;\n  const auto copy_at_time = f_of_t->create_at_time(new_time, new_expiration);\n  const auto new_t_bounds = copy_at_time->time_bounds();\n  CHECK(new_t_bounds[0] == new_time);\n  CHECK(new_t_bounds[1] == new_expiration);\n}\n\ntemplate <size_t DerivOrder>\nvoid test_non_const_deriv(\n    const gsl::not_null<FunctionsOfTime::FunctionOfTime*> f_of_t,\n    const gsl::not_null<FunctionsOfTime::PiecewisePolynomial<DerivOrder>*>\n        f_of_t_derived,\n    double t, const double dt, const double final_time) {\n  const FunctionsOfTime::PiecewisePolynomial<DerivOrder> f_of_t_derived_copy =\n      *f_of_t_derived;\n  CHECK(*f_of_t_derived == f_of_t_derived_copy);\n  while (t < final_time) {\n    t += dt;\n    f_of_t_derived->update(t, {3.0 + t}, t + dt);\n    CHECK(*f_of_t_derived != f_of_t_derived_copy);\n  }\n  t *= 1.0 + std::numeric_limits<double>::epsilon();\n  const auto lambdas_all = f_of_t->func_and_all_derivs(t);\n  CHECK(approx(lambdas_all[0][0]) == 33.948);\n  CHECK(approx(lambdas_all[1][0]) == 19.56);\n  CHECK(approx(lambdas_all[2][0]) == 7.2);\n  const auto lambdas0 = f_of_t->func_and_2_derivs(t);\n  CHECK(approx(lambdas0[0][0]) == 33.948);\n  CHECK(approx(lambdas0[1][0]) == 19.56);\n  CHECK(approx(lambdas0[2][0]) == 7.2);\n  const auto lambdas1 = f_of_t->func_and_deriv(t);\n  CHECK(approx(lambdas1[0][0]) == 33.948);\n  CHECK(approx(lambdas1[1][0]) == 19.56);\n  const auto lambdas2 = f_of_t->func(t);\n  CHECK(approx(lambdas2[0][0]) == 33.948);\n\n  CHECK(lambdas0.size() == 3);\n  CHECK(lambdas1.size() == 2);\n  CHECK(lambdas2.size() == 1);\n}\n\ntemplate <size_t DerivOrder>\nvoid test_within_roundoff(const FunctionsOfTime::FunctionOfTime& f_of_t) {\n  const auto lambdas_all = f_of_t.func_and_all_derivs(1.0 - 5.0e-16);\n  CHECK(approx(lambdas_all[0][0]) == 1.0);\n  CHECK(approx(lambdas_all[1][0]) == 3.0);\n  CHECK(approx(lambdas_all[2][0]) == 6.0);\n  CHECK(approx(lambdas_all[3][0]) == 6.0);\n  CHECK(approx(lambdas_all[0][1]) == 1.0);\n  CHECK(approx(lambdas_all[1][1]) == 2.0);\n  CHECK(approx(lambdas_all[2][1]) == 2.0);\n  CHECK(approx(lambdas_all[3][1]) == 0.0);\n  const auto lambdas0 = f_of_t.func_and_2_derivs(1.0 - 5.0e-16);\n  CHECK(approx(lambdas0[0][0]) == 1.0);\n  CHECK(approx(lambdas0[1][0]) == 3.0);\n  CHECK(approx(lambdas0[2][0]) == 6.0);\n  CHECK(approx(lambdas0[0][1]) == 1.0);\n  CHECK(approx(lambdas0[1][1]) == 2.0);\n  CHECK(approx(lambdas0[2][1]) == 2.0);\n  const auto lambdas1 = f_of_t.func_and_deriv(1.0 - 5.0e-16);\n  CHECK(approx(lambdas1[0][0]) == 1.0);\n  CHECK(approx(lambdas1[1][0]) == 3.0);\n  CHECK(approx(lambdas1[0][1]) == 1.0);\n  CHECK(approx(lambdas1[1][1]) == 2.0);\n  const auto lambdas2 = f_of_t.func(1.0 - 5.0e-16);\n  CHECK(approx(lambdas2[0][0]) == 1.0);\n  CHECK(approx(lambdas2[0][1]) == 1.0);\n}\n\nclass quartic {\n public:\n  quartic(const std::array<DataVector, 5> coeffs) : coeffs_(coeffs) {}\n  template <size_t MaxDerivReturned>\n  std::array<DataVector, MaxDerivReturned + 1> func_and_derivs(double t) const {\n    if constexpr (0 == MaxDerivReturned) {\n      return {{coeffs_[0] +\n               t * (coeffs_[1] +\n                    t * (coeffs_[2] + t * (coeffs_[3] + t * coeffs_[4])))}};\n    } else if constexpr (1 == MaxDerivReturned) {\n      return {\n          {coeffs_[0] +\n               t * (coeffs_[1] +\n                    t * (coeffs_[2] + t * (coeffs_[3] + t * coeffs_[4]))),\n           coeffs_[1] + t * (2.0 * coeffs_[2] +\n                             t * (3.0 * coeffs_[3] + t * 4.0 * coeffs_[4]))}};\n    } else {\n      static_assert(2 == MaxDerivReturned, \"Unimplemented\");\n      return {\n          {coeffs_[0] +\n               t * (coeffs_[1] +\n                    t * (coeffs_[2] + t * (coeffs_[3] + t * coeffs_[4]))),\n           coeffs_[1] + t * (2.0 * coeffs_[2] +\n                             t * (3.0 * coeffs_[3] + t * 4.0 * coeffs_[4])),\n           2.0 * coeffs_[2] + t * (6.0 * coeffs_[3] + t * 12.0 * coeffs_[4])}};\n    }\n  }\n\n private:\n  std::array<DataVector, 5> coeffs_;\n};\n\ntemplate <size_t MaxDeriv>\nvoid check_func_and_derivs(\n    const FunctionsOfTime::PiecewisePolynomial<MaxDeriv>& f_of_t,\n    const quartic& q) {\n  const double t = 0.5;\n\n  const auto& do_check = [&](const auto& f_of_t_to_check) {\n    CHECK_ITERABLE_APPROX(f_of_t_to_check.func(t), q.func_and_derivs<0>(t));\n    CHECK_ITERABLE_APPROX(f_of_t_to_check.func_and_deriv(t),\n                          q.func_and_derivs<1>(t));\n    CHECK_ITERABLE_APPROX(f_of_t_to_check.func_and_2_derivs(t),\n                          q.func_and_derivs<2>(t));\n    auto func_and_all_derivs = f_of_t_to_check.func_and_all_derivs(t);\n    auto func_and_2_derivs = q.func_and_derivs<2>(t);\n    CHECK(func_and_all_derivs.size() == MaxDeriv + 1);\n    for (size_t i = 0;\n         i < std::min(func_and_all_derivs.size(), func_and_2_derivs.size());\n         i++) {\n      CHECK_ITERABLE_APPROX(func_and_all_derivs[i],\n                            gsl::at(func_and_2_derivs, i));\n    }\n  };\n\n  do_check(f_of_t);\n\n  test_copy_semantics(f_of_t);\n  auto f_of_t_copy = f_of_t;\n  test_move_semantics(std::move(f_of_t_copy), f_of_t);\n\n  const auto copy_at_time = f_of_t.create_at_time(0.4, 1.1);\n\n  do_check(*copy_at_time);\n}\n\nvoid test_func_and_derivs() {\n  const double t0 = 0.0;\n  const double tx = 1.0;\n  DataVector c0{0.125, 0.25};\n  DataVector c1{0.375, 0.5};\n  DataVector c2{0.625, 0.75};\n  DataVector c3{0.875, 1.125};\n  DataVector c4{1.25, 1.375};\n  DataVector zero{0.0, 0.0};\n  check_func_and_derivs(\n      FunctionsOfTime::PiecewisePolynomial<0>(t0, {{c0}}, tx),\n      quartic(std::array<DataVector, 5>{{c0, zero, zero, zero, zero}}));\n  check_func_and_derivs(\n      FunctionsOfTime::PiecewisePolynomial<1>(t0, {{c0, c1}}, tx),\n      quartic(std::array<DataVector, 5>{{c0, c1, zero, zero, zero}}));\n  check_func_and_derivs(\n      FunctionsOfTime::PiecewisePolynomial<2>(t0, {{c0, c1, 2.0 * c2}}, tx),\n      quartic(std::array<DataVector, 5>{{c0, c1, c2, zero, zero}}));\n  check_func_and_derivs(\n      FunctionsOfTime::PiecewisePolynomial<3>(\n          t0, {{c0, c1, 2.0 * c2, 6.0 * c3}}, tx),\n      quartic(std::array<DataVector, 5>{{c0, c1, c2, c3, zero}}));\n  check_func_and_derivs(\n      FunctionsOfTime::PiecewisePolynomial<4>(\n          t0, {{c0, c1, 2.0 * c2, 6.0 * c3, 24.0 * c4}}, tx),\n      quartic(std::array<DataVector, 5>{{c0, c1, c2, c3, c4}}));\n}\n\nvoid test_serialization_versioning() {\n  using Poly = FunctionsOfTime::PiecewisePolynomial<1>;\n  register_classes_with_charm<Poly>();\n  const std::unique_ptr<FunctionsOfTime::FunctionOfTime> poly(\n      std::make_unique<Poly>(2.0, std::array{DataVector{3.0}, DataVector{4.0}},\n                             5.0));\n  poly->update(5.0, {6.0}, 7.0);\n\n  TestHelpers::serialization::test_versioning<Poly>(\n      \"Domain/FunctionsOfTime/PiecewisePolynomial.serializations\", \"version 5\",\n      poly);\n}\n\nvoid test_out_of_order_update() {\n  using Poly = FunctionsOfTime::PiecewisePolynomial<0>;\n  register_classes_with_charm<Poly>();\n\n  const std::unique_ptr<FunctionsOfTime::FunctionOfTime> poly(\n      std::make_unique<Poly>(2.0, std::array{DataVector{3.0}}, 5.0));\n\n  poly->update(25.0, {8.0}, 30.0);\n  poly->update(15.0, {6.0}, 20.0);\n\n  CHECK_THROWS_WITH(\n      poly->func(7.0),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 7.0\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n  CHECK_THROWS_WITH(\n      poly->func(13.0),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n  CHECK_THROWS_WITH(\n      poly->func(19.0),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.90\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n\n  poly->update(5.0, {4.0}, 10.0);\n\n  // This one should pass now\n  CHECK(poly->func(7.0) == std::array{DataVector{4.0}});\n  CHECK_THROWS_WITH(\n      poly->func(13.0),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n  CHECK_THROWS_WITH(\n      poly->func(19.0),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.90\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n\n  poly->update(20.0, {7.0}, 25.0);\n\n  CHECK_THROWS_WITH(\n      poly->func(13.0),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n  CHECK_THROWS_WITH(\n      poly->func(19.0),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.90\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n\n  poly->update(10.0, {5.0}, 15.0);\n\n  CHECK(poly->func(13.0) == std::array{DataVector{5.0}});\n  CHECK(poly->func(19.0) == std::array{DataVector{6.0}});\n  CHECK(poly->func(22.0) == std::array{DataVector{7.0}});\n  CHECK(poly->func(28.0) == std::array{DataVector{8.0}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.PiecewisePolynomial\",\n                  \"[Domain][Unit]\") {\n  FunctionsOfTime::register_derived_with_charm();\n  test_func_and_derivs();\n  {\n    INFO(\"Core test\");\n    double t = 0.0;\n    const double dt = 0.6;\n    const double final_time = 4.0;\n    constexpr size_t deriv_order = 3;\n\n    // test two component system (x**3 and x**2)\n    const std::array<DataVector, deriv_order + 1> init_func{\n        {{0.0, 0.0}, {0.0, 0.0}, {0.0, 2.0}, {6.0, 0.0}}};\n    {\n      INFO(\"Test with simple construction.\");\n      FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(t, init_func,\n                                                               t + dt);\n      FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t2 =\n          serialize_and_deserialize(f_of_t);\n\n      test(make_not_null(&f_of_t), make_not_null(&f_of_t), t, dt, final_time);\n      test(make_not_null(&f_of_t2), make_not_null(&f_of_t2), t, dt, final_time);\n    }\n    {\n      INFO(\"Test with base class construction.\");\n      std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t =\n          std::make_unique<FunctionsOfTime::PiecewisePolynomial<deriv_order>>(\n              t, init_func, t + dt);\n      std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t2 =\n          serialize_and_deserialize(f_of_t);\n      std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t3 =\n          f_of_t->get_clone();\n\n      test(make_not_null(f_of_t.get()),\n           make_not_null(\n               dynamic_cast<FunctionsOfTime::PiecewisePolynomial<deriv_order>*>(\n                   f_of_t.get())),\n           t, dt, final_time);\n      test(make_not_null(f_of_t2.get()),\n           make_not_null(\n               dynamic_cast<FunctionsOfTime::PiecewisePolynomial<deriv_order>*>(\n                   f_of_t2.get())),\n           t, dt, final_time);\n      test(make_not_null(f_of_t3.get()),\n           make_not_null(\n               dynamic_cast<FunctionsOfTime::PiecewisePolynomial<deriv_order>*>(\n                   f_of_t3.get())),\n           t, dt, final_time);\n    }\n  }\n\n  {\n    INFO(\"Non-constant derivative test.\");\n    double t = 0.0;\n    const double dt = 0.6;\n    const double final_time = 4.0;\n    constexpr size_t deriv_order = 2;\n\n    // initally x**2, but update with non-constant 2nd deriv\n    const std::array<DataVector, deriv_order + 1> init_func{\n        {{0.0}, {0.0}, {2.0}}};\n    {\n      INFO(\"Test with simple construction.\");\n      FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(t, init_func,\n                                                               t + dt);\n      FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t2 =\n          serialize_and_deserialize(f_of_t);\n      test_non_const_deriv(make_not_null(&f_of_t), make_not_null(&f_of_t), t,\n                           dt, final_time);\n      test_non_const_deriv(make_not_null(&f_of_t2), make_not_null(&f_of_t2), t,\n                           dt, final_time);\n    }\n    {\n      INFO(\"Test with base class construction.\");\n      std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t =\n          std::make_unique<FunctionsOfTime::PiecewisePolynomial<deriv_order>>(\n              t, init_func, t + dt);\n      std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t2 =\n          serialize_and_deserialize(f_of_t);\n      std::unique_ptr<FunctionsOfTime::FunctionOfTime> f_of_t3 =\n          f_of_t->get_clone();\n\n      test_non_const_deriv(\n          make_not_null(f_of_t.get()),\n          make_not_null(\n              dynamic_cast<FunctionsOfTime::PiecewisePolynomial<deriv_order>*>(\n                  f_of_t.get())),\n          t, dt, final_time);\n      test_non_const_deriv(\n          make_not_null(f_of_t2.get()),\n          make_not_null(\n              dynamic_cast<FunctionsOfTime::PiecewisePolynomial<deriv_order>*>(\n                  f_of_t2.get())),\n          t, dt, final_time);\n      test_non_const_deriv(\n          make_not_null(f_of_t3.get()),\n          make_not_null(\n              dynamic_cast<FunctionsOfTime::PiecewisePolynomial<deriv_order>*>(\n                  f_of_t3.get())),\n          t, dt, final_time);\n    }\n  }\n  {\n    INFO(\"Test within roundoff.\");\n    constexpr size_t deriv_order = 3;\n    const std::array<DataVector, deriv_order + 1> init_func{\n        {{1.0, 1.0}, {3.0, 2.0}, {6.0, 2.0}, {6.0, 0.0}}};\n\n    FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(1.0, init_func,\n                                                             2.0);\n    FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t2 =\n        serialize_and_deserialize(f_of_t);\n\n    f_of_t.update(2.0, {12.0, 0.0}, 2.1);\n    f_of_t2.update(2.0, {12.0, 0.0}, 2.1);\n\n    test_within_roundoff<deriv_order>(f_of_t);\n    test_within_roundoff<deriv_order>(f_of_t2);\n\n    INFO(\"Test stream operator.\");\n\n    CHECK(get_output(FunctionsOfTime::PiecewisePolynomial<deriv_order>{}) ==\n          \"backlog=()\");\n    CHECK(get_output(f_of_t) ==\n          \"t=1: (1,1) (3,2) (3,1) (1,0)\\n\"\n          \"t=2: (8,4) (12,4) (6,1) (2,0)\\n\"\n          \"backlog=()\");\n  }\n  {\n    INFO(\"Test evaluation at update time.\");\n    FunctionsOfTime::PiecewisePolynomial<0> f_of_t(1.0, {{{1.0, 2.0}}}, 2.0);\n\n    CHECK(f_of_t.func(2.0)[0] == DataVector{1.0, 2.0});\n    f_of_t.update(2.0, {3.0, 4.0}, 2.1);\n    CHECK(f_of_t.func(2.0)[0] == DataVector{1.0, 2.0});\n  }\n  {\n    INFO(\"Test truncate_at_time\");\n    constexpr size_t deriv_order = 3;\n    const std::array<DataVector, deriv_order + 1> init_func{\n        {{1.0, 1.0}, {3.0, 2.0}, {6.0, 2.0}, {6.0, 0.0}}};\n\n    FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(1.0, init_func,\n                                                             2.0);\n    f_of_t.update(2.0, {12.0, 0.0}, 2.1);\n    f_of_t.update(2.1, {8.0, 9.0}, 2.7);\n    f_of_t.update(2.7, {1.0, 6.0}, 3.5);\n\n    const double truncate_time = 2.5;\n    const auto old_value = f_of_t.func_and_deriv(truncate_time);\n    const auto old_bounds = f_of_t.time_bounds();\n    f_of_t.truncate_at_time(truncate_time);\n    CHECK(f_of_t.time_bounds()[0] > old_bounds[0]);\n    CHECK(f_of_t.time_bounds()[0] <= truncate_time);\n    CHECK(f_of_t.time_bounds()[1] == old_bounds[1]);\n    CHECK(f_of_t.func_and_deriv(truncate_time) == old_value);\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        // two component system (x**3 and x**2)\n        constexpr size_t deriv_order = 3;\n        const std::array<DataVector, deriv_order + 1> init_func{\n            {{0.0, 0.0}, {0.0, 0.0}, {0.0, 2.0}, {6.0, 0.0}}};\n        FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(0.0, init_func,\n                                                                 2.1);\n        f_of_t.update(2.1, {6.0, 0.0}, 1.9);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Expiration time 1.\") and\n          Catch::Matchers::ContainsSubstring(\" is not after update time 2.1\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        // two component system (x**3 and x**2)\n        constexpr size_t deriv_order = 3;\n        const std::array<DataVector, deriv_order + 1> init_func{\n            {{0.0, 0.0}, {0.0, 0.0}, {0.0, 2.0}, {6.0, 0.0}}};\n        FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(0.0, init_func,\n                                                                 2.0);\n        f_of_t.update(1.5, {6.0, 0.0}, 3.0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Attempted to update at time 1.5\") and\n          Catch::Matchers::ContainsSubstring(\n              \" which is earlier than the expiration time 2.0\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        // two component system (x**3 and x**2)\n        constexpr size_t deriv_order = 3;\n        const std::array<DataVector, deriv_order + 1> init_func{\n            {{0.0, 0.0}, {0.0, 0.0}, {0.0, 2.0}, {6.0, 0.0}}};\n        FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(0.0, init_func,\n                                                                 1.0);\n        f_of_t.update(1.0, {6.0, 0.0, 0.0}, 1.1);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"the number of components trying to be updated (3) does not match \"\n          \"the number of components (2) in the PiecewisePolynomial.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        // two component system (x**3 and x**2)\n        constexpr size_t deriv_order = 3;\n        const std::array<DataVector, deriv_order + 1> init_func{\n            {{1.0, 1.0}, {3.0, 2.0}, {6.0, 2.0}, {6.0, 0.0}}};\n        FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(1.0, init_func,\n                                                                 2.0);\n        f_of_t.update(2.0, {6.0, 0.0}, 2.1);\n        f_of_t.func(0.5);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Requested time 5.0\") and\n          Catch::Matchers::ContainsSubstring(\" precedes earliest time 1\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        // two component system (x**3 and x**2)\n        constexpr size_t deriv_order = 3;\n        const std::array<DataVector, deriv_order + 1> init_func{\n            {{1.0, 1.0}, {3.0, 2.0}, {6.0, 2.0}, {6.0, 0.0}}};\n        FunctionsOfTime::PiecewisePolynomial<deriv_order> f_of_t(1.0, init_func,\n                                                                 2.0);\n        f_of_t.func(2.2);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 2.2\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 2\"));\n\n  test_serialization_versioning();\n  test_out_of_order_update();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_QuaternionFunctionOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <catch2/matchers/catch_matchers.hpp>\n#include <catch2/matchers/catch_matchers_string.hpp>\n#include <cmath>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Utilities/Serialization/Versioning.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test_serialization_versioning() {\n  using QuatFoT = domain::FunctionsOfTime::QuaternionFunctionOfTime<2>;\n  register_classes_with_charm<QuatFoT>();\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> func(\n      std::make_unique<QuatFoT>(\n          0.0, std::array<DataVector, 1>{DataVector{{1.0, 0.0, 0.0, 0.0}}},\n          std::array<DataVector, 3>{DataVector{3, 0.0},\n                                    DataVector{0.0, 0.0, 3.78},\n                                    DataVector{3, 0.0}},\n          0.6));\n  func->update(0.6, DataVector{3, 0.0}, 1.0);\n\n  TestHelpers::serialization::test_versioning<QuatFoT>(\n      \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.serializations\",\n      \"version 6\", func);\n}\n\nvoid test_out_of_order_update() {\n  using QuatFoT = domain::FunctionsOfTime::QuaternionFunctionOfTime<2>;\n  register_classes_with_charm<QuatFoT>();\n  const DataVector quat_dv{1.0, 0.0, 0.0, 0.0};\n  const DataVector zero_dv{3, 0.0};\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> func(\n      std::make_unique<QuatFoT>(\n          0.0, std::array<DataVector, 1>{quat_dv},\n          std::array<DataVector, 3>{zero_dv, zero_dv, zero_dv}, 0.5));\n\n  func->update(2.5, zero_dv, 3.0);\n  func->update(1.5, zero_dv, 2.0);\n\n  CHECK_THROWS_WITH(\n      func->func(0.7),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n  CHECK_THROWS_WITH(\n      func->func(1.3),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n  CHECK_THROWS_WITH(\n      func->func(1.9),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 5\"));\n\n  func->update(0.5, zero_dv, 1.0);\n\n  // This one should pass now\n  CHECK(func->func(0.7) == std::array{quat_dv});\n  CHECK_THROWS_WITH(\n      func->func(1.3),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n  CHECK_THROWS_WITH(\n      func->func(1.9),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n\n  func->update(2.0, zero_dv, 2.5);\n\n  CHECK_THROWS_WITH(\n      func->func(1.3),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 1.30\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n  CHECK_THROWS_WITH(\n      func->func(1.9),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 1.0\"));\n\n  func->update(1.0, zero_dv, 1.5);\n\n  CHECK(func->func(1.3) == std::array{quat_dv});\n  CHECK(func->func(1.9) == std::array{quat_dv});\n  CHECK(func->func(2.2) == std::array{quat_dv});\n  CHECK(func->func(2.8) == std::array{quat_dv});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.QuaternionFunctionOfTime\",\n                  \"[Unit][Domain]\") {\n  {\n    INFO(\"QuaternionFunctionOfTime: Time bounds\");\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2> qfot{\n        0.0, std::array<DataVector, 1>{DataVector{4, 0.0}},\n        std::array<DataVector, 3>{DataVector{3, 0.0}, DataVector{3, 0.0},\n                                  DataVector{3, 0.0}},\n        0.5};\n    CHECK(qfot.time_bounds() == std::array<double, 2>({0.0, 0.5}));\n  }\n\n  {\n    INFO(\"QuaternionFunctionOfTime: Check output\");\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2> qfot{\n        -0.1, std::array<DataVector, 1>{DataVector{{1.0, 0.0, 0.0, 0.0}}},\n        std::array<DataVector, 3>{DataVector{{0.0, 0.0, -0.3}},\n                                  DataVector{0.0, 0.0, 0.145},\n                                  DataVector{3, 0.0}},\n        0.5};\n\n    CHECK(get_output(domain::FunctionsOfTime::QuaternionFunctionOfTime<2>{}) ==\n          \"Quaternion:\\n\"\n          \"backlog=()\\n\"\n          \"Angle:\\n\"\n          \"backlog=()\");\n    const std::string expected_output =\n        \"Quaternion:\\n\"\n        \"t=-0.1: (1,0,0,0)\\n\"\n        \"backlog=()\\n\"\n        \"Angle:\\n\"\n        \"t=-0.1: (0,0,-0.3) (0,0,0.145) (0,0,0)\\n\"\n        \"backlog=()\";\n\n    const std::string output = get_output(qfot);\n    CHECK(output == expected_output);\n  }\n\n  {\n    INFO(\"QuaternionFunctionOfTime: Internal PiecewisePolynomial\");\n    DataVector init_omega{0.0, 0.0, 3.78};\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2> qfot{\n        0.0, std::array<DataVector, 1>{DataVector{{1.0, 0.0, 0.0, 0.0}}},\n        std::array<DataVector, 3>{DataVector{3, 0.0}, init_omega,\n                                  DataVector{3, 0.0}},\n        0.6};\n    domain::FunctionsOfTime::PiecewisePolynomial<2> pp{\n        0.0,\n        std::array<DataVector, 3>{DataVector{3, 0.0}, init_omega,\n                                  DataVector{3, 0.0}},\n        0.6};\n    qfot.update(0.6, DataVector{3, 0.0}, 1.0);\n    pp.update(0.6, DataVector{3, 0.0}, 1.0);\n\n    CHECK(qfot.angle_func(0.4) == pp.func(0.4));\n    CHECK(qfot.angle_func_and_deriv(0.4) == pp.func_and_deriv(0.4));\n    CHECK(qfot.angle_func_and_2_derivs(0.4) == pp.func_and_2_derivs(0.4));\n  }\n\n  {\n    INFO(\"QuaternionFunctionOfTime: pup, cloning, extra functions, copy/move\");\n    DataVector init_omega{0.0, 0.0, 1.0};\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2> qfot{\n        0.0, std::array<DataVector, 1>{DataVector{{1.0, 0.0, 0.0, 0.0}}},\n        std::array<DataVector, 3>{DataVector{3, 0.0}, init_omega,\n                                  DataVector{3, 0.0}},\n        2.5};\n    // Different expiration time to check comparison operators\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2> qfot2{\n        0.0, std::array<DataVector, 1>{DataVector{{1.0, 0.0, 0.0, 0.0}}},\n        std::array<DataVector, 3>{DataVector{3, 0.0}, init_omega,\n                                  DataVector{3, 0.0}},\n        3.0};\n\n    auto qfot_ptr = qfot.get_clone();\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2>\n        qfot_serialized_deserialized = serialize_and_deserialize(qfot);\n\n    CHECK(qfot == qfot_serialized_deserialized);\n    CHECK(qfot != qfot2);\n\n    std::array<DataVector, 1> expected_func{\n        DataVector{{cos(1.0), 0.0, 0.0, sin(1.0)}}};\n    std::array<DataVector, 2> expected_func_and_deriv{\n        DataVector{{cos(1.0), 0.0, 0.0, sin(1.0)}},\n        DataVector{{-0.5 * sin(1.0), 0.0, 0.0, 0.5 * cos(1.0)}}};\n\n    const auto do_check = [&](const auto& qfot_to_check,\n                              const auto& ptr_to_check,\n                              const auto& serialized_to_check) {\n      CHECK_ITERABLE_APPROX(qfot_to_check.quat_func(2.0), expected_func);\n      CHECK_ITERABLE_APPROX(qfot_to_check.quat_func_and_deriv(2.0),\n                            expected_func_and_deriv);\n      CHECK_ITERABLE_APPROX(qfot_to_check.func(2.0), expected_func);\n      CHECK_ITERABLE_APPROX(qfot_to_check.func_and_deriv(2.0),\n                            expected_func_and_deriv);\n      CHECK_ITERABLE_APPROX(ptr_to_check->func(2.0), qfot_to_check.func(2.0));\n      CHECK_ITERABLE_APPROX(ptr_to_check->func_and_deriv(2.0),\n                            qfot_to_check.func_and_deriv(2.0));\n      CHECK_ITERABLE_APPROX(serialized_to_check.func(2.0),\n                            qfot_to_check.func(2.0));\n      CHECK_ITERABLE_APPROX(serialized_to_check.func_and_deriv(2.0),\n                            qfot_to_check.func_and_deriv(2.0));\n    };\n\n    do_check(qfot, qfot_ptr, qfot_serialized_deserialized);\n\n    CHECK_THROWS_WITH(\n        qfot.quat_func_and_3_derivs(2.0),\n        Catch::Matchers::ContainsSubstring(\n            \"Need more angle derivs to compute the third derivative of the \"\n            \"quaternion. Currently only have 2\"));\n\n    test_copy_semantics(qfot);\n    test_move_semantics(std::move(qfot_serialized_deserialized), qfot);\n\n    const auto copy_at_time = qfot.create_at_time(0.7, 2.8);\n    const auto clone_copy_at_time = copy_at_time->get_clone();\n    const auto copy_serialized = serialize_and_deserialize(\n        dynamic_cast<\n            const domain::FunctionsOfTime::QuaternionFunctionOfTime<2>&>(\n            *copy_at_time));\n\n    do_check(dynamic_cast<\n                 const domain::FunctionsOfTime::QuaternionFunctionOfTime<2>&>(\n                 *copy_at_time),\n             clone_copy_at_time, copy_serialized);\n  }\n\n  {\n    INFO(\"QuaternionFunctionOfTime: Constant omega\");\n    double t = 0.0;\n    double expir_time = 0.5;\n    const double omega_z = 1.3333;\n    DataVector init_quat{1.0, 0.0, 0.0, 0.0};\n    DataVector init_angle{0.0, 0.0, 0.0};\n    DataVector init_omega{0.0, 0.0, omega_z};\n    DataVector init_dtomega{0.0, 0.0, 0.0};\n\n    // Construct QuaternionFunctionOfTime\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2> qfot{\n        t, std::array<DataVector, 1>{init_quat},\n        std::array<DataVector, 3>{init_angle, init_omega, init_dtomega},\n        expir_time};\n\n    // Update stored PiecewisePolynomial with 0 2nd derivative so it's\n    // constant. This will automatically update the stored quaternions as well\n    const double time_step = 0.5;\n    for (int i = 0; i < 15; i++) {\n      t += time_step;\n      expir_time += time_step;\n      qfot.update(t, DataVector{3, 0.0}, expir_time);\n      CHECK(qfot.expiration_after(t) == expir_time);\n      CHECK(qfot.expiration_after(t + 0.5 * time_step) == expir_time);\n      CHECK(qfot.expiration_after(t - 0.5 * time_step) == t);\n    }\n    CHECK(qfot.expiration_after(0.0) == time_step);\n\n    // Get the quaternion and 2 derivatives at a certain time.\n    double check_time = 5.398;\n    const std::array<DataVector, 3> quat_func_and_2_derivs =\n        qfot.quat_func_and_2_derivs(check_time);\n    const std::array<DataVector, 3> quat_func_and_2_derivs2 =\n        qfot.func_and_2_derivs(check_time);\n    for (size_t i = 0; i < 3; i++) {\n      CHECK_ITERABLE_APPROX(gsl::at(quat_func_and_2_derivs, i),\n                            gsl::at(quat_func_and_2_derivs2, i));\n    }\n\n    // Analytic solution for constant omega\n    // quat = ( cos(omega*t/2), 0, 0, sin(omega*t/2) )\n    DataVector a_quat{{cos(0.5 * omega_z * check_time), 0.0, 0.0,\n                       sin(0.5 * omega_z * check_time)}};\n    DataVector a_dtquat{{-0.5 * omega_z * sin(0.5 * omega_z * check_time), 0.0,\n                         0.0, 0.5 * omega_z * cos(0.5 * omega_z * check_time)}};\n    DataVector a_dt2quat{\n        {-0.25 * omega_z * omega_z * cos(0.5 * omega_z * check_time), 0.0, 0.0,\n         -0.25 * omega_z * omega_z * sin(0.5 * omega_z * check_time)}};\n\n    // Compare analytic solution to numerical\n    Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n    {\n      INFO(\"  Compare quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[0], a_quat,\n                                   custom_approx);\n    }\n    {\n      INFO(\"  Compare derivative of quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[1], a_dtquat,\n                                   custom_approx);\n    }\n    {\n      INFO(\"  Compare second derivative of quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[2], a_dt2quat,\n                                   custom_approx);\n    }\n  }\n\n  {\n    INFO(\"QuaternionFunctionOfTime: Linear Omega\");\n    double t = 0.0;\n    double expir_time = 0.5;\n    // phi(t) = fac1 * t^2 + fac2 * t + fac3\n    const double fac1 = 0.25;\n    const double fac2 = 0.5;\n    const double fac3 = 0.0;\n    // omega(t) = 2*fac1 * t + fac2\n    // dtomega(t) = 2 * fac1 (constant)\n    // dt2omega(t) = 0.0\n\n    DataVector init_quat{1.0, 0.0, 0.0, 0.0};\n    DataVector init_angle{3, 0.0};\n    DataVector init_omega{{0.0, 0.0, fac2}};\n    DataVector init_dtomega{{0.0, 0.0, 2.0 * fac1}};\n    // Construct QuaternionFunctionOfTime\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2> qfot{\n        t, std::array<DataVector, 1>{init_quat},\n        std::array<DataVector, 3>{init_angle, init_omega, init_dtomega},\n        expir_time};\n\n    // Update internal PiecewisePolynomial with constant 2nd derivative so\n    // omega is linear. This will automatically update the stored quaternions as\n    // well\n    const double time_step = 0.5;\n    for (int i = 0; i < 15; i++) {\n      t += time_step;\n      expir_time += time_step;\n      qfot.update(t, DataVector{{0.0, 0.0, 2.0 * fac1}}, expir_time);\n      CHECK(qfot.expiration_after(t) == expir_time);\n      CHECK(qfot.expiration_after(t + 0.5 * time_step) == expir_time);\n      CHECK(qfot.expiration_after(t - 0.5 * time_step) == t);\n    }\n    CHECK(qfot.expiration_after(0.0) == time_step);\n\n    // Get the quaternion and 2 derivatives at a certain time.\n    double check_time = 5.398;\n    const std::array<DataVector, 3> quat_func_and_2_derivs =\n        qfot.quat_func_and_2_derivs(check_time);\n    const std::array<DataVector, 3> quat_func_and_2_derivs2 =\n        qfot.func_and_2_derivs(check_time);\n    for (size_t i = 0; i < 3; i++) {\n      CHECK_ITERABLE_APPROX(gsl::at(quat_func_and_2_derivs, i),\n                            gsl::at(quat_func_and_2_derivs2, i));\n    }\n\n    // phi(t) = fac1 * t^2 + fac2 * t + fac3\n    const double phi =\n        fac1 * check_time * check_time + fac2 * check_time + fac3;\n    // omega(t) = 2*fac1 * t + fac2\n    const double omega = 2 * fac1 * check_time + fac2;\n    const double dtomega = 2 * fac1;\n\n    DataVector a_quat{{cos(0.5 * phi), 0.0, 0.0, sin(0.5 * phi)}};\n    DataVector a_dtquat{\n        {-0.5 * a_quat[3] * omega, 0.0, 0.0, 0.5 * a_quat[0] * omega}};\n    DataVector a_dt2quat{{-0.5 * (a_dtquat[3] * omega + a_quat[3] * dtomega),\n                          0.0, 0.0,\n                          0.5 * (a_dtquat[0] * omega + a_quat[0] * dtomega)}};\n\n    // Compare analytic solution to numerical\n    Approx custom_approx = Approx::custom().epsilon(5.0e-12).scale(1.0);\n    {\n      INFO(\"  Compare quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[0], a_quat,\n                                   custom_approx);\n    }\n    {\n      INFO(\"  Compare derivative of quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[1], a_dtquat,\n                                   custom_approx);\n    }\n    {\n      INFO(\"  Compare second derivative of quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[2], a_dt2quat,\n                                   custom_approx);\n    }\n  }\n\n  {\n    INFO(\"QuaternionFunctionOfTime: Quadratic Omega\");\n    double t = 0.0;\n    double expir_time = 0.5;\n    // phi(t) = fac1 * t^3 + fac2 * t^2 + fac3 * t + fac4;\n    const double fac1 = 0.2;\n    const double fac2 = 0.3;\n    const double fac3 = 0.4;\n    const double fac4 = 0.0;\n    // omega(t) = 3*fac1 * t^2 + 2*fac2 * t + fac3\n    // dtomega(t) = 6*fac1 * t + 2* fac2\n    // dt2omega(t) = 6 * fac1 (constant)\n\n    DataVector init_quat{1.0, 0.0, 0.0, 0.0};\n    DataVector init_angle{3, 0.0};\n    DataVector init_omega{{0.0, 0.0, fac3}};\n    DataVector init_dtomega{{0.0, 0.0, 2.0 * fac2}};\n    DataVector init_dt2omega{{0.0, 0.0, 6.0 * fac1}};\n    // Construct QuaternionFunctionOfTime\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<3> qfot{\n        t, std::array<DataVector, 1>{init_quat},\n        std::array<DataVector, 4>{init_angle, init_omega, init_dtomega,\n                                  init_dt2omega},\n        expir_time};\n\n    // Update PiecewisePolynomial with constant 3rd derivative so omega is\n    // quadratic. This will automatically update the stored quaternions as\n    // well\n    const double time_step = 0.5;\n    for (int i = 0; i < 15; i++) {\n      t += time_step;\n      expir_time += time_step;\n      qfot.update(t, DataVector{{0.0, 0.0, 6.0 * fac1}}, expir_time);\n      CHECK(qfot.expiration_after(t) == expir_time);\n      CHECK(qfot.expiration_after(t + 0.5 * time_step) == expir_time);\n      CHECK(qfot.expiration_after(t - 0.5 * time_step) == t);\n    }\n    CHECK(qfot.expiration_after(0.0) == time_step);\n\n    // Get the quaternion and 3 derivatives at a certain time.\n    double check_time = 5.398;\n    const std::array<DataVector, 3> quat_func_and_2_derivs =\n        qfot.quat_func_and_2_derivs(check_time);\n    const std::array<DataVector, 3> quat_func_and_2_derivs2 =\n        qfot.func_and_2_derivs(check_time);\n    for (size_t i = 0; i < 3; i++) {\n      CHECK_ITERABLE_APPROX(gsl::at(quat_func_and_2_derivs, i),\n                            gsl::at(quat_func_and_2_derivs2, i));\n    }\n\n    // phi(t) = fac1 * t^3 + fac2 * t^2 + fac3 * t + fac4;\n    const double phi = fac1 * check_time * check_time * check_time +\n                       fac2 * check_time * check_time + fac3 * check_time +\n                       fac4;\n    // omega(t) = 3*fac1 * t^2 + 2*fac2 * t + fac3\n    // dtomega(t) = 6*fac1 * t + 2* fac2\n    // dt2omega(t) = 6 * fac1 (constant)\n    const double omega =\n        3 * fac1 * check_time * check_time + 2 * fac2 * check_time + fac3;\n    const double dtomega = 6 * fac1 * check_time + 2 * fac2;\n    const double dt2omega = 6.0 * fac1;\n\n    DataVector a_quat{{cos(0.5 * phi), 0.0, 0.0, sin(0.5 * phi)}};\n    DataVector a_dtquat{\n        {-0.5 * a_quat[3] * omega, 0.0, 0.0, 0.5 * a_quat[0] * omega}};\n    DataVector a_dt2quat{{-0.5 * (a_dtquat[3] * omega + a_quat[3] * dtomega),\n                          0.0, 0.0,\n                          0.5 * (a_dtquat[0] * omega + a_quat[0] * dtomega)}};\n    DataVector a_dt3quat{\n        {-0.5 * (a_dt2quat[3] * omega + 2.0 * a_dtquat[3] * dtomega +\n                 a_quat[3] * dt2omega),\n         0.0, 0.0,\n         0.5 * (a_dt2quat[0] * omega + 2.0 * a_dtquat[0] * dtomega +\n                a_quat[0] * dt2omega)}};\n\n    // Compare analytic solution to numerical\n    Approx custom_approx = Approx::custom().epsilon(5.0e-12).scale(1.0);\n    {\n      INFO(\"  Compare quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[0], a_quat,\n                                   custom_approx);\n    }\n    {\n      INFO(\"  Compare derivative of quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[1], a_dtquat,\n                                   custom_approx);\n    }\n    {\n      INFO(\"  Compare second derivative of quaternion\");\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_2_derivs[2], a_dt2quat,\n                                   custom_approx);\n    }\n    {\n      INFO(\"  Compare third derivative of quaternion\");\n      const std::array<DataVector, 4> quat_func_and_3_derivs =\n          qfot.quat_func_and_3_derivs(check_time);\n      CHECK_ITERABLE_CUSTOM_APPROX(quat_func_and_3_derivs[3], a_dt3quat,\n                                   custom_approx);\n    }\n  }\n  {\n    INFO(\"QuaternionFunctionOfTime: No updates\");\n    const double initial_time = 0.0;\n    const double final_time = 100.0;\n\n    DataVector init_quat{1.0, 0.0, 0.0, 0.0};\n    // We use zero because we are only concerned about checking the quaternion\n    // at late times when it hasn't been updated\n    DataVector three_zero{3, 0.0};\n    // Construct QuaternionFunctionOfTime\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<3> qfot{\n        initial_time, std::array<DataVector, 1>{init_quat},\n        std::array<DataVector, 4>{three_zero, three_zero, three_zero,\n                                  three_zero},\n        std::numeric_limits<double>::infinity()};\n\n    const std::array<DataVector, 3> quat_func_and_2_derivs =\n        qfot.func_and_2_derivs(final_time);\n\n    DataVector four_zero{4, 0.0};\n    {\n      INFO(\"  Compare quaternion\");\n      CHECK_ITERABLE_APPROX(quat_func_and_2_derivs[0], init_quat);\n    }\n    {\n      INFO(\"  Compare derivative of quaternion\");\n      CHECK_ITERABLE_APPROX(quat_func_and_2_derivs[1], four_zero);\n    }\n    {\n      INFO(\"  Compare second derivative of quaternion\");\n      CHECK_ITERABLE_APPROX(quat_func_and_2_derivs[2], four_zero);\n    }\n  }\n\n  {\n    INFO(\"truncate_at_time\");\n    const DataVector init_omega{0.0, 0.0, 3.78};\n    domain::FunctionsOfTime::QuaternionFunctionOfTime<2> qfot{\n        0.0, std::array<DataVector, 1>{DataVector{{1.0, 0.0, 0.0, 0.0}}},\n        std::array<DataVector, 3>{DataVector{3, 0.0}, init_omega,\n                                  DataVector{3, 0.0}},\n        0.6};\n    qfot.update(0.6, DataVector{3, 4.0}, 1.0);\n    qfot.update(1.0, DataVector{3, 6.0}, 1.8);\n    qfot.update(1.8, DataVector{3, 2.0}, 2.5);\n    qfot.update(2.5, DataVector{3, 1.0}, 3.5);\n\n    const double truncate_time = 2.0;\n    const auto old_value = qfot.func_and_deriv(truncate_time);\n    const auto old_angle = qfot.angle_func_and_deriv(truncate_time);\n    const auto old_bounds = qfot.time_bounds();\n    qfot.truncate_at_time(truncate_time);\n    CHECK(qfot.time_bounds()[0] > old_bounds[0]);\n    CHECK(qfot.time_bounds()[0] <= truncate_time);\n    CHECK(qfot.time_bounds()[1] == old_bounds[1]);\n    CHECK(qfot.func_and_deriv(truncate_time) == old_value);\n    CHECK(qfot.angle_func_and_deriv(truncate_time) == old_angle);\n  }\n\n  test_serialization_versioning();\n  test_out_of_order_update();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_QuaternionHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <boost/math/quaternion.hpp>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.QuaternionHelpers\",\n                  \"[Unit][Domain]\") {\n  using quaternion = boost::math::quaternion<double>;\n\n  quaternion quat1(1.0, 2.0, 3.0, -4.0);\n  DataVector dv{1.0, 2.0, 3.0, -4.0};\n  DataVector dv_short{9.9, 9.8, 9.7};\n\n  DataVector dv2 = quaternion_to_datavector(quat1);\n  CHECK(dv2 == dv);\n\n  quaternion quat2 = datavector_to_quaternion(dv2);\n  CHECK(quat2 == quat1);\n\n  quaternion quat3 = datavector_to_quaternion(dv_short);\n  CHECK(quat3 == quaternion{0.0, 9.9, 9.8, 9.7});\n\n  normalize_quaternion(make_not_null(&quat2));\n  CHECK(norm(quat2) == approx(1.0));\n  CHECK(abs(quat2) == approx(1.0));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_SettleToConstant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstant.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test(\n    const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>& f_of_t,\n    const double match_time, const double f_t0, const double dtf_t0,\n    const double d2tf_t0, const double A) {\n  // check that values agree at the matching time\n  const auto lambdas0 = f_of_t->func_and_2_derivs(match_time);\n  CHECK(approx(lambdas0[0][0]) == f_t0);\n  CHECK(approx(lambdas0[1][0]) == dtf_t0);\n  CHECK(approx(lambdas0[2][0]) == d2tf_t0);\n\n  const auto lambdas1 = f_of_t->func_and_deriv(match_time);\n  CHECK(approx(lambdas1[0][0]) == f_t0);\n  CHECK(approx(lambdas1[1][0]) == dtf_t0);\n\n  const auto lambdas2 = f_of_t->func(match_time);\n  CHECK(approx(lambdas2[0][0]) == f_t0);\n\n  // check that asymptotic values approach f = A = const.\n  const auto lambdas3 = f_of_t->func_and_2_derivs(1.0e5);\n  CHECK(approx(lambdas3[0][0]) == A);\n  CHECK(approx(lambdas3[1][0]) == 0.0);\n  CHECK(approx(lambdas3[2][0]) == 0.0);\n\n  const auto lambdas4 = f_of_t->func_and_deriv(1.0e5);\n  CHECK(approx(lambdas4[0][0]) == A);\n  CHECK(approx(lambdas4[1][0]) == 0.0);\n\n  const auto lambdas5 = f_of_t->func(1.0e5);\n  CHECK(approx(lambdas5[0][0]) == A);\n\n  // test time_bounds function\n  const auto t_bounds = f_of_t->time_bounds();\n  CHECK(t_bounds[0] == 10.0);\n  CHECK(t_bounds[1] == std::numeric_limits<double>::infinity());\n  CHECK(f_of_t->expiration_after(match_time) ==\n        std::numeric_limits<double>::infinity());\n\n  CHECK(dynamic_cast<const domain::FunctionsOfTime::SettleToConstant&>(\n            *f_of_t->get_clone()) ==\n        dynamic_cast<const domain::FunctionsOfTime::SettleToConstant&>(\n            *f_of_t->create_at_time(0.0, 0.0)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.SettleToConstant\",\n                  \"[Domain][Unit]\") {\n  using SettleToConstant = domain::FunctionsOfTime::SettleToConstant;\n\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  const double match_time = 10.0;\n  const double decay_time = 5.0;\n  // set init_func to f(t) = 5 + 2t + 3t^2\n  const double f_t0 = 5.0 + 2.0 * match_time + 3.0 * square(match_time);\n  const double dtf_t0 = 2.0 + 6.0 * match_time;\n  const double d2tf_t0 = 6.0;\n  // compute coefficients for check\n  const double C = -(decay_time * d2tf_t0 + dtf_t0);\n  const double B = decay_time * (C - dtf_t0);\n  const double A = f_t0 - B;\n  const std::array<DataVector, 3> init_func{{{f_t0}, {dtf_t0}, {d2tf_t0}}};\n\n  INFO(\"Test with base class construction.\");\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t =\n      std::make_unique<domain::FunctionsOfTime::SettleToConstant>(\n          init_func, match_time, decay_time);\n  test(f_of_t, match_time, f_t0, dtf_t0, d2tf_t0, A);\n\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t2 =\n      serialize_and_deserialize(f_of_t);\n  test(f_of_t2, match_time, f_t0, dtf_t0, d2tf_t0, A);\n\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t3 =\n      f_of_t->get_clone();\n  test(f_of_t3, match_time, f_t0, dtf_t0, d2tf_t0, A);\n\n  {\n    INFO(\"Test operator==\");\n    CHECK(SettleToConstant{init_func, match_time, decay_time} ==\n          SettleToConstant{init_func, match_time, decay_time});\n    CHECK_FALSE(SettleToConstant{init_func, match_time, decay_time} !=\n                SettleToConstant{init_func, match_time, decay_time});\n\n    CHECK(SettleToConstant{init_func, match_time, decay_time} !=\n          SettleToConstant{init_func, match_time, 2.0 * decay_time});\n    CHECK_FALSE(SettleToConstant{init_func, match_time, decay_time} ==\n                SettleToConstant{init_func, match_time, 2.0 * decay_time});\n\n    CHECK(SettleToConstant{init_func, match_time, decay_time} !=\n          SettleToConstant{init_func, 2.0 * match_time, decay_time});\n    CHECK_FALSE(SettleToConstant{init_func, match_time, decay_time} ==\n                SettleToConstant{init_func, 2.0 * match_time, decay_time});\n\n    CHECK(SettleToConstant{init_func, match_time, decay_time} !=\n          SettleToConstant{{{init_func[0] + 1.0, init_func[1], init_func[2]}},\n                           match_time,\n                           decay_time});\n    CHECK_FALSE(\n        SettleToConstant{init_func, match_time, decay_time} ==\n        SettleToConstant{{{init_func[0] + 1.0, init_func[1], init_func[2]}},\n                         match_time,\n                         decay_time});\n\n    CHECK(SettleToConstant{init_func, match_time, decay_time} !=\n          SettleToConstant{{{init_func[0], init_func[1] + 1.0, init_func[2]}},\n                           match_time,\n                           decay_time});\n    CHECK_FALSE(\n        SettleToConstant{init_func, match_time, decay_time} ==\n        SettleToConstant{{{init_func[0], init_func[1] + 1.0, init_func[2]}},\n                         match_time,\n                         decay_time});\n\n    CHECK(SettleToConstant{init_func, match_time, decay_time} !=\n          SettleToConstant{{{init_func[0], init_func[1], init_func[2] + 1.0}},\n                           match_time,\n                           decay_time});\n    CHECK_FALSE(\n        SettleToConstant{init_func, match_time, decay_time} ==\n        SettleToConstant{{{init_func[0], init_func[1], init_func[2] + 1.0}},\n                         match_time,\n                         decay_time});\n  }\n\n  CHECK_THROWS_WITH(\n      (domain::FunctionsOfTime::SettleToConstant{\n          {{DataVector{1, 0.0}, DataVector{1, 0.0}, DataVector{1, 0.0}}},\n          10.0,\n          1.0}\n           .update(1.0, DataVector{}, 2.0)),\n      Catch::Matchers::ContainsSubstring(\"Cannot update this FunctionOfTime.\"));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_SettleToConstantQuaternion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test(\n    const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>& f_of_t,\n    const double match_time, const double decay_time, const DataVector& f_t0,\n    const DataVector& dtf_t0, const DataVector& d2tf_t0, const DataVector& A) {\n  // check that values agree at the matching time\n  const auto lambdas0 = f_of_t->func_and_2_derivs(match_time);\n  CHECK_ITERABLE_APPROX(lambdas0[0], f_t0);\n  CHECK_ITERABLE_APPROX(lambdas0[1], dtf_t0);\n  CHECK_ITERABLE_APPROX(lambdas0[2], d2tf_t0);\n\n  const auto lambdas1 = f_of_t->func_and_deriv(match_time);\n  CHECK_ITERABLE_APPROX(lambdas1[0], f_t0);\n  CHECK_ITERABLE_APPROX(lambdas1[1], dtf_t0);\n\n  const auto lambdas2 = f_of_t->func(match_time);\n  CHECK_ITERABLE_APPROX(lambdas2[0], f_t0);\n\n  // check that asymptotic values approach f = A/|A| = const.\n  const auto lambdas3 = f_of_t->func_and_2_derivs(1.0e5);\n  const DataVector zero{0.0, 0.0, 0.0, 0.0};\n  const DataVector normalized_A = A / norm(A);\n  CHECK_ITERABLE_APPROX(lambdas3[0], normalized_A);\n  CHECK_ITERABLE_APPROX(lambdas3[1], zero);\n  CHECK_ITERABLE_APPROX(lambdas3[2], zero);\n\n  const auto lambdas4 = f_of_t->func_and_deriv(1.0e5);\n  CHECK_ITERABLE_APPROX(lambdas4[0], normalized_A);\n  CHECK_ITERABLE_APPROX(lambdas4[1], zero);\n\n  const auto lambdas5 = f_of_t->func(1.0e5);\n  CHECK_ITERABLE_APPROX(lambdas5[0], normalized_A);\n\n  // Check that function of time remains a unit quaternion\n  const auto lambdas6 = f_of_t->func(match_time + decay_time * 0.34);\n  const double lambdas6_norm = norm(lambdas6[0]);\n  CHECK(approx(lambdas6_norm) == 1.0);\n\n  // test time_bounds function\n  const auto t_bounds = f_of_t->time_bounds();\n  CHECK(t_bounds[0] == match_time);\n  CHECK(t_bounds[1] == std::numeric_limits<double>::infinity());\n  CHECK(f_of_t->expiration_after(match_time) ==\n        std::numeric_limits<double>::infinity());\n\n  CHECK(\n      dynamic_cast<const domain::FunctionsOfTime::SettleToConstantQuaternion&>(\n          *f_of_t->get_clone()) ==\n      dynamic_cast<const domain::FunctionsOfTime::SettleToConstantQuaternion&>(\n          *f_of_t->create_at_time(0.0, 0.0)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.SettleToConstantQuaternion\",\n                  \"[Domain][Unit]\") {\n  using SettleToConstantQuaternion =\n      domain::FunctionsOfTime::SettleToConstantQuaternion;\n\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  const double match_time = 10.0;\n  const double decay_time = 5.0;\n  // Choose for an initial quaternion\n  // q = (cos \\theta/2, \\hat{n} \\sin \\theta/2), where\n  // \\theta = \\Omega(t-t_0) + \\alpha(t-t_0)^2/2 + \\phi\n  // \\hat{n}_x = \\cos \\Phi\n  // \\hat{n}_y = 0\n  // \\hat{n}_z = \\sin \\Phi\n  // \\Phi = \\omega(t-t_0) + \\varphi\n  // This is an accelerating, precessing rotation.\n  // For the constants, choose\n  // \\Omega = 0.1, \\alpha=0.05, \\omega = 0.01, \\phi = 0.2, \\varphi = 0.4\n  const DataVector f_t0{0.9950041652780258, 0.09195266597143172, 0.0,\n                        0.03887696361761665};\n  const DataVector dtf_t0{-0.004991670832341408, 0.04543420663922331, 0.0,\n                          0.02029317029135289};\n  const DataVector d2tf_t0{-0.004983345829365769, 0.022284938333541247, 0.0,\n                           0.01050220123592147};\n\n  // compute coefficients for check\n  const DataVector C = -(decay_time * d2tf_t0 + dtf_t0);\n  const DataVector B = decay_time * (C - dtf_t0);\n  const DataVector A = f_t0 - B;\n  const std::array<DataVector, 3> init_func{{f_t0, dtf_t0, d2tf_t0}};\n\n  INFO(\"Test with base class construction.\");\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t =\n      std::make_unique<domain::FunctionsOfTime::SettleToConstantQuaternion>(\n          init_func, match_time, decay_time);\n  test(f_of_t, match_time, decay_time, f_t0, dtf_t0, d2tf_t0, A);\n\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t2 =\n      serialize_and_deserialize(f_of_t);\n  test(f_of_t2, match_time, decay_time, f_t0, dtf_t0, d2tf_t0, A);\n\n  const std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime> f_of_t3 =\n      f_of_t->get_clone();\n  test(f_of_t3, match_time, decay_time, f_t0, dtf_t0, d2tf_t0, A);\n\n  {\n    INFO(\"Test operator==\");\n    CHECK(SettleToConstantQuaternion{init_func, match_time, decay_time} ==\n          SettleToConstantQuaternion{init_func, match_time, decay_time});\n    CHECK_FALSE(SettleToConstantQuaternion{init_func, match_time, decay_time} !=\n                SettleToConstantQuaternion{init_func, match_time, decay_time});\n\n    CHECK(SettleToConstantQuaternion{init_func, match_time, decay_time} !=\n          SettleToConstantQuaternion{init_func, match_time, 2.0 * decay_time});\n    CHECK_FALSE(\n        SettleToConstantQuaternion{init_func, match_time, decay_time} ==\n        SettleToConstantQuaternion{init_func, match_time, 2.0 * decay_time});\n\n    CHECK(SettleToConstantQuaternion{init_func, match_time, decay_time} !=\n          SettleToConstantQuaternion{init_func, 2.0 * match_time, decay_time});\n    CHECK_FALSE(\n        SettleToConstantQuaternion{init_func, match_time, decay_time} ==\n        SettleToConstantQuaternion{init_func, 2.0 * match_time, decay_time});\n\n    // Test == for different initial quaternions; both must be unit\n    // quaternions.\n    const DataVector another_f_t0{0.9928086358538663, 0.10830981865238282, 0.0,\n                                  0.050990153534512375};\n    const DataVector another_dtf_t0{-0.008379854510224357, 0.062163306368871823,\n                                    0.0, 0.031117684009928405};\n    const DataVector another_d2tf_t0{\n        -0.008096991912484768, 0.022871837603575258, 0.0, 0.01291837713635158};\n    const std::array<DataVector, 3> another_init_func{\n        {another_f_t0, another_dtf_t0, another_d2tf_t0}};\n    CHECK(\n        SettleToConstantQuaternion{init_func, match_time, decay_time} !=\n        SettleToConstantQuaternion{another_init_func, match_time, decay_time});\n    CHECK_FALSE(\n        SettleToConstantQuaternion{init_func, match_time, decay_time} ==\n        SettleToConstantQuaternion{another_init_func, match_time, decay_time});\n\n    CHECK(SettleToConstantQuaternion{init_func, match_time, decay_time} !=\n          SettleToConstantQuaternion{\n              {{init_func[0], init_func[1] + 1.0, init_func[2]}},\n              match_time,\n              decay_time});\n    CHECK_FALSE(SettleToConstantQuaternion{init_func, match_time, decay_time} ==\n                SettleToConstantQuaternion{\n                    {{init_func[0], init_func[1] + 1.0, init_func[2]}},\n                    match_time,\n                    decay_time});\n\n    CHECK(SettleToConstantQuaternion{init_func, match_time, decay_time} !=\n          SettleToConstantQuaternion{\n              {{init_func[0], init_func[1], init_func[2] + 1.0}},\n              match_time,\n              decay_time});\n    CHECK_FALSE(SettleToConstantQuaternion{init_func, match_time, decay_time} ==\n                SettleToConstantQuaternion{\n                    {{init_func[0], init_func[1], init_func[2] + 1.0}},\n                    match_time,\n                    decay_time});\n  }\n\n  CHECK_THROWS_WITH((domain::FunctionsOfTime::SettleToConstantQuaternion{\n                        {{DataVector{1.0, 0.0, 0.0, 0.0, 0.0},\n                          DataVector{1.0, 0.0, 0.0, 0.0, 0.0},\n                          DataVector{1.0, 0.0, 0.0, 0.0, 0.0}}},\n                        10.0,\n                        1.0}\n                         .update(1.0, DataVector{}, 2.0)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"stored as DataVectors of size 4, not\"));\n  CHECK_THROWS_WITH(\n      (domain::FunctionsOfTime::SettleToConstantQuaternion{\n          {{DataVector{4.0, 0.0, 0.0, 0.0}, DataVector{1.0, 0.0, 0.0, 0.0},\n            DataVector{1.0, 0.0, 0.0, 0.0}}},\n          10.0,\n          1.0}\n           .update(1.0, DataVector{}, 2.0)),\n      Catch::Matchers::ContainsSubstring(\n          \"should be a quaternion with a norm of 1.0, not\"));\n  CHECK_THROWS_WITH(\n      (domain::FunctionsOfTime::SettleToConstantQuaternion{\n          {{DataVector{1.0, 0.0, 0.0, 0.0}, DataVector{1.0, 0.0, 0.0, 0.0},\n            DataVector{1.0, 0.0, 0.0, 0.0}}},\n          10.0,\n          1.0}\n           .update(1.0, DataVector{}, 2.0)),\n      Catch::Matchers::ContainsSubstring(\"Cannot update this FunctionOfTime.\"));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace domain {\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.Tags\", \"[Domain][Unit]\") {\n  TestHelpers::db::test_simple_tag<Tags::FunctionsOfTime>(\"FunctionsOfTime\");\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/FunctionsOfTime/Test_ThreadsafeList.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <utility>\n\n#include \"Domain/FunctionsOfTime/ThreadsafeList.hpp\"\n#include \"Domain/FunctionsOfTime/ThreadsafeList.tpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FunctionsOfTime.ThreadsafeList\",\n                  \"[Unit][Domain]\") {\n  using domain::FunctionsOfTime::FunctionOfTimeHelpers::ThreadsafeList;\n  ThreadsafeList<int> list(1.0);\n  CHECK(list.initial_time() == 1.0);\n  CHECK(list.expiration_time() == 1.0);\n\n  CHECK(list == ThreadsafeList<int>(1.0));\n  CHECK_FALSE(list != ThreadsafeList<int>(1.0));\n  CHECK(list != ThreadsafeList<int>(2.0));\n  CHECK_FALSE(list == ThreadsafeList<int>(2.0));\n\n  test_copy_semantics(list);\n  test_serialization(list);\n\n  list.insert(1.0, 5, 3.0);\n  CHECK(list.initial_time() == 1.0);\n  CHECK(list.expiration_time() == 3.0);\n  CHECK(list.expiration_after(1.0) == 3.0);\n  CHECK(list.expiration_after(2.0) == 3.0);\n  {\n    const auto entry = list(2.0);\n    CHECK(entry.update == 1.0);\n    CHECK(entry.data == 5);\n    CHECK(entry.expiration == 3.0);\n    CHECK(list(1.0) == entry);\n    CHECK(list(3.0) == entry);\n  }\n  CHECK(list != ThreadsafeList<int>(1.0));\n  CHECK_FALSE(list == ThreadsafeList<int>(1.0));\n\n  test_copy_semantics(list);\n\n  {\n    ThreadsafeList<int> list2(1.0);\n    ThreadsafeList<int> list3(1.0);\n    list2.insert(1.0, 5, 4.0);\n    list3.insert(1.0, 4, 3.0);\n    CHECK(list != list2);\n    CHECK_FALSE(list == list2);\n    CHECK(list != list3);\n    CHECK_FALSE(list == list3);\n  }\n\n  ThreadsafeList<int> list_copy{};\n  {\n    auto list_temp1 = serialize_and_deserialize(list);\n    CHECK(list_temp1 == list);\n    CHECK_FALSE(list_temp1 != list);\n    auto list_temp2 = std::move(list_temp1);\n    CHECK(list_temp2 == list);\n    CHECK_FALSE(list_temp2 != list);\n    list_copy = std::move(list_temp2);\n    CHECK(list_copy == list);\n    CHECK_FALSE(list_copy != list);\n  }\n\n  list.insert(3.0, 7, 5.0);\n  CHECK(list.initial_time() == 1.0);\n  CHECK(list.expiration_time() == 5.0);\n  CHECK(list.expiration_after(1.0) == 3.0);\n  CHECK(list.expiration_after(2.0) == 3.0);\n  CHECK(list.expiration_after(3.0) == 5.0);\n  CHECK(list.expiration_after(4.0) == 5.0);\n  {\n    const auto entry = list(2.0);\n    CHECK(entry.update == 1.0);\n    CHECK(entry.data == 5);\n    CHECK(entry.expiration == 3.0);\n    CHECK(list(1.0) == entry);\n    CHECK(list(3.0) == entry);\n  }\n  {\n    const auto entry = list(4.0);\n    CHECK(entry.update == 3.0);\n    CHECK(entry.data == 7);\n    CHECK(entry.expiration == 5.0);\n    CHECK(list(5.0) == entry);\n  }\n  CHECK(list(list.initial_time()) ==\n        list(list.initial_time() * (1.0 - 1.0e-15)));\n\n  CHECK(list != ThreadsafeList<int>(1.0));\n  CHECK_FALSE(list == ThreadsafeList<int>(1.0));\n  CHECK(list != list_copy);\n  CHECK_FALSE(list == list_copy);\n\n  test_copy_semantics(list);\n\n  {\n    const ThreadsafeList<int> list2(1.0);\n    CHECK(list2.begin() == list2.end());\n    CHECK_FALSE(list2.begin() != list2.end());\n  }\n  // Test iterators\n  {\n    decltype(list)::iterator it = list.begin();\n    CHECK(it == list.begin());\n    CHECK_FALSE(it != list.begin());\n    decltype(list)::iterator it2 = it;\n    CHECK(it2 == it);\n    CHECK_FALSE(it2 != it);\n    CHECK(*it == list(4.0));\n    CHECK(it->update == list(4.0).update);\n    {\n      const auto& ret = ++it;\n      CHECK(&ret == &it);\n    }\n    CHECK(it != it2);\n    CHECK_FALSE(it == it2);\n    CHECK(*it == list(2.0));\n    {\n      const auto old_it2 = it2;\n      CHECK(it2++ == old_it2);\n    }\n    CHECK(it == it2);\n    CHECK_FALSE(it != it2);\n    ++it;\n    CHECK(it == list.end());\n    CHECK_FALSE(it != list.end());\n  }\n\n  {\n    ThreadsafeList<int> truncate_list(1.0);\n    truncate_list.insert(1.0, 0, 3.0);\n    truncate_list.insert(3.0, 0, 5.0);\n    const auto untruncated = truncate_list;\n    truncate_list.truncate_to_length(10);\n    CHECK(truncate_list == untruncated);\n    truncate_list.truncate_to_length(2);\n    CHECK(truncate_list == untruncated);\n    truncate_list.truncate_to_length(1);\n    CHECK(std::distance(truncate_list.begin(), truncate_list.end()) == 1);\n    CHECK(truncate_list.initial_time() == 3.0);\n  }\n  {\n    ThreadsafeList<int> truncate_list(1.0);\n    truncate_list.insert(1.0, 0, 3.0);\n    truncate_list.insert(3.0, 0, 5.0);\n    truncate_list.truncate_to_length(0);\n    CHECK(truncate_list.initial_time() == 5.0);\n    CHECK(truncate_list.begin() == truncate_list.end());\n    truncate_list.truncate_to_length(1);\n    CHECK(truncate_list.initial_time() == 5.0);\n    CHECK(truncate_list.begin() == truncate_list.end());\n  }\n  {\n    ThreadsafeList<int> truncate_list(1.0);\n    truncate_list.insert(1.0, 0, 3.0);\n    truncate_list.insert(3.0, 0, 5.0);\n    truncate_list.clear();\n    CHECK(truncate_list.initial_time() == 5.0);\n    CHECK(truncate_list.begin() == truncate_list.end());\n    truncate_list.clear();\n    CHECK(truncate_list.initial_time() == 5.0);\n    CHECK(truncate_list.begin() == truncate_list.end());\n  }\n  {\n    ThreadsafeList<int> truncate_list(1.0);\n    truncate_list.insert(1.0, 0, 3.0);\n    truncate_list.insert(3.0, 0, 5.0);\n    truncate_list.insert(5.0, 0, 7.0);\n    const auto untruncated = truncate_list;\n    truncate_list.truncate_at_time(0.0);\n    CHECK(truncate_list == untruncated);\n    truncate_list.truncate_at_time(1.0);\n    CHECK(truncate_list == untruncated);\n    truncate_list.truncate_at_time(2.0);\n    CHECK(truncate_list == untruncated);\n    truncate_list.truncate_at_time(3.0);\n    CHECK(truncate_list == untruncated);\n    // These keep more data than obviously necessary to keep the\n    // non-thread-safe truncation of the internal list away from the\n    // area used by lookups.\n    truncate_list.truncate_at_time(4.0);\n    CHECK(truncate_list == untruncated);\n    truncate_list.truncate_at_time(5.0);\n    CHECK(truncate_list == untruncated);\n    truncate_list.truncate_at_time(6.0);\n    CHECK(std::distance(truncate_list.begin(), truncate_list.end()) == 2);\n    CHECK(truncate_list.initial_time() == 3.0);\n  }\n\n  {\n    ThreadsafeList<int> infinite_list(0.0);\n    infinite_list.insert(0.0, 1, std::numeric_limits<double>::infinity());\n    CHECK(infinite_list.expiration_after(\n              std::numeric_limits<double>::infinity()) ==\n          std::numeric_limits<double>::infinity());\n    CHECK(infinite_list.expiration_after(1.0) ==\n          std::numeric_limits<double>::infinity());\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        ThreadsafeList<int> error(1.0);\n        error.insert(2.0, 1, 3.0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Tried to insert at time 2\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is not the old expiration time 1\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        ThreadsafeList<int> error(1.0);\n        error.insert(1.0, 1, 3.0);\n        error.insert(4.0, 1, 3.0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Tried to insert at time 4\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is not the old expiration time 3\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        ThreadsafeList<int> error(2.0);\n        error.insert(2.0, 1, 1.0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Expiration time 1\") and\n          Catch::Matchers::ContainsSubstring(\" is not after update time 2\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        ThreadsafeList<int> error(2.0);\n        error.insert(2.0, 1, 2.0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Expiration time 2\") and\n          Catch::Matchers::ContainsSubstring(\" is not after update time 2\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        ThreadsafeList<int> error(2.0);\n        // Probably get the same error without the insert, but\n        // \"Attempt to access an empty function of time.\" would also\n        // be acceptable in that case.\n        error.insert(2.0, 1, 3.0);\n        error(1.0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Requested time 1\") and\n          Catch::Matchers::ContainsSubstring(\" precedes earliest time 2\"));\n  CHECK_THROWS_WITH(([]() { ThreadsafeList<int>(1.0)(1.0); }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Attempt to access an empty function of time.\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        ThreadsafeList<int> error(1.0);\n        error.insert(1.0, 1, 3.0);\n        error(4.0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 4\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 3\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        ThreadsafeList<int> error(1.0);\n        error.insert(1.0, 1, 3.0);\n        error.expiration_after(3.0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Attempt to evaluate at time 3\") and\n          Catch::Matchers::ContainsSubstring(\n              \", which is after the expiration time 3\"));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/JacobianDiagnostic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\n\n\ndef jacobian_diagnostic(analytic_jacobian, numeric_jacobian_transpose):\n    analytic_sum = np.abs(analytic_jacobian).sum(axis=0)\n    numeric_sum = np.abs(numeric_jacobian_transpose).sum(axis=1)\n    jac_diag = 1.0 - analytic_sum / numeric_sum\n    return jac_diag\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/NormalDotFlux.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef normal_dot_flux(normal, flux_tensor):\n    return np.einsum(\"i,i...\", normal, flux_tensor)\n\n\ndef normal_times_flux(normal, flux_tensor):\n    return np.tensordot(normal, flux_tensor, axes=0)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.BlockAndElementLogicalCoordinates\"\n  Test_BlockAndElementLogicalCoordinates.py\n  \"Unit;Domain\"\n  PyDomain)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.Direction\"\n  Test_Direction.py\n  \"Unit;Domain\"\n  PyDomain)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.Domain\"\n  Test_Domain.py\n  \"Unit;Domain\"\n  PyDomain)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.ElementId\"\n  Test_ElementId.py\n  \"Unit;Domain\"\n  PyDomain)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.ElementMap\"\n  Test_ElementMap.py\n  \"Unit;Domain\"\n  PyDomain)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.FunctionsOfTime\"\n  Test_FunctionsOfTime.py\n  \"Unit;Domain\"\n  PyDomain)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.JacobianDiagnostic\"\n  Test_JacobianDiagnostic.py\n  \"Unit;Domain\"\n  PyDomain)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.RadiallyCompressedCoordinates\"\n  Test_RadiallyCompressedCoordinates.py\n  \"Unit;Domain\"\n  PyDomain)\n\nspectre_add_python_bindings_test(\n  \"Unit.Domain.Python.SegmentId\"\n  Test_SegmentId.py\n  \"Unit;Domain\"\n  PyDomain)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_BlockAndElementLogicalCoordinates.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Frame, tnsr\nfrom spectre.Domain import (\n    ElementId,\n    block_logical_coordinates,\n    deserialize_domain,\n    deserialize_functions_of_time,\n    element_logical_coordinates,\n)\nfrom spectre.Informer import unit_test_src_path\n\n\nclass TestBlockAndElementLogicalCoordinates(unittest.TestCase):\n    def test_element_logical_coordinates(self):\n        block_logical_coords = tnsr.I[float, 3, Frame.BlockLogical](\n            [0.0, -0.5, 0.25]\n        )\n        self.assertIsNone(\n            element_logical_coordinates(\n                block_logical_coords, ElementId[3](\"[B0,(L1I0,L1I1,L1I1)]\")\n            )\n        )\n        logical_coords = np.array(\n            element_logical_coordinates(\n                block_logical_coords, ElementId[3](\"[B0,(L1I0,L1I0,L1I1)]\")\n            )\n        )\n        npt.assert_allclose(logical_coords, [1.0, 0.0, -0.5])\n\n    def test_block_and_element_logical_coordinates(self):\n        volfile_name = os.path.join(\n            unit_test_src_path(), \"Visualization/Python/VolTestData0.h5\"\n        )\n        with spectre_h5.H5File(volfile_name, \"r\") as open_h5_file:\n            volfile = open_h5_file.get_vol(\"/element_data\")\n            obs_id = volfile.list_observation_ids()[0]\n            serialized_domain = volfile.get_domain()\n            serialized_fot = volfile.get_functions_of_time(obs_id)\n\n        # This domain is [0, 2 pi]^3\n        domain = deserialize_domain[3](serialized_domain)\n        functions_of_time = deserialize_functions_of_time(serialized_fot)\n        block_logical_coords = block_logical_coordinates(\n            domain,\n            tnsr.I[DataVector, 3, Frame.Inertial](3 * [DataVector([np.pi])]),\n            time=0.0,\n            functions_of_time=functions_of_time,\n        )\n\n        element_id = ElementId[3](\"[B0,(L0I0,L0I0,L0I0)]\")\n        logical_coords = element_logical_coordinates(\n            [element_id], block_logical_coords\n        )\n        self.assertEqual(len(logical_coords), 1)\n        self.assertEqual(\n            logical_coords[element_id].element_logical_coords,\n            tnsr.I[DataVector, 3, Frame.ElementLogical](\n                3 * [DataVector([0.0])]\n            ),\n        )\n        self.assertEqual(logical_coords[element_id].offsets, [0])\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_Direction.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain import Direction, Side\n\n\nclass TestDirection(unittest.TestCase):\n    def test_construction(self):\n        direction = Direction[1](dimension=0, side=Side.Lower)\n        self.assertEqual(direction, Direction[1].lower_xi())\n        self.assertNotEqual(direction, Direction[1].upper_xi())\n        direction = Direction[2](dimension=1, side=Side.Upper)\n        self.assertEqual(direction, Direction[2].upper_eta())\n        self.assertNotEqual(direction, Direction[2].lower_xi())\n        direction = Direction[3](dimension=2, side=Side.Upper)\n        self.assertEqual(direction, Direction[3].upper_zeta())\n        self.assertNotEqual(direction, Direction[3].lower_zeta())\n\n    def test_repr(self):\n        self.assertEqual(repr(Direction[1].lower_xi()), \"-0\")\n        self.assertEqual(repr(Direction[2].upper_eta()), \"+1\")\n        self.assertEqual(repr(Direction[3].upper_zeta()), \"+2\")\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_Domain.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Frame, tnsr\nfrom spectre.Domain import (\n    deserialize_domain,\n    deserialize_functions_of_time,\n    serialize_domain,\n)\nfrom spectre.Domain.Creators import Interval\nfrom spectre.Informer import unit_test_src_path\n\n\nclass TestDomain(unittest.TestCase):\n    def test_serialize_and_deserialize(self):\n        domain = Interval(\n            lower_bounds=[0.0],\n            upper_bounds=[1.0],\n            initial_refinement_levels=[1],\n            initial_num_points=[4],\n            is_periodic=[False],\n        ).create_domain()\n        self.assertEqual(\n            deserialize_domain[1](serialize_domain(domain)), domain\n        )\n\n    def test_deserialize_from_file(self):\n        volfile_name = os.path.join(\n            unit_test_src_path(), \"Visualization/Python/VolTestData0.h5\"\n        )\n        with spectre_h5.H5File(volfile_name, \"r\") as open_h5_file:\n            volfile = open_h5_file.get_vol(\"/element_data\")\n            obs_id = volfile.list_observation_ids()[0]\n            time = volfile.get_observation_value(obs_id)\n            serialized_domain = volfile.get_domain()\n            serialized_fot = volfile.get_functions_of_time(obs_id)\n\n        domain = deserialize_domain[3](serialized_domain)\n        self.assertEqual(domain.dim, 3)\n        self.assertTrue(domain.is_time_dependent())\n        for block_id, block in enumerate(domain.blocks):\n            self.assertEqual(block.id, block_id)\n        domain.blocks[0].name == \"\"\n        self.assertEqual(len(domain.block_groups), 0)\n\n        # Check coordinate maps. The domain is [0, 2 pi]^3, so the block-logical\n        # coord (0, 0, 0) should map to (pi,) * 3\n        block = domain.blocks[0]\n        functions_of_time = deserialize_functions_of_time(serialized_fot)\n        logical_coord = tnsr.I[DataVector, 3, Frame.BlockLogical](\n            num_points=1, fill=0.0\n        )\n        self.assertTrue(block.is_time_dependent())\n        self.assertFalse(block.has_distorted_frame())\n        grid_coord = block.moving_mesh_logical_to_grid_map(logical_coord)\n        inertial_coord = block.moving_mesh_grid_to_inertial_map(\n            grid_coord, time, functions_of_time\n        )\n        npt.assert_allclose(inertial_coord, [[np.pi]] * 3)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_ElementId.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain import ElementId, SegmentId, Side\n\n\nclass TestElementId(unittest.TestCase):\n    def test_construction(self):\n        element_id = ElementId[1](block_id=1)\n        self.assertEqual(element_id.block_id, 1)\n        self.assertEqual(element_id.grid_index, 0)\n        self.assertEqual(element_id.refinement_levels, [0])\n        self.assertEqual(element_id.segment_ids, [SegmentId(0, 0)])\n        element_id = ElementId[1](block_id=1, segment_ids=[SegmentId(1, 0)])\n        self.assertEqual(element_id.block_id, 1)\n        self.assertEqual(element_id.grid_index, 0)\n        self.assertEqual(element_id.refinement_levels, [1])\n        self.assertEqual(element_id.segment_ids, [SegmentId(1, 0)])\n        element_id = ElementId[2](\n            block_id=2, segment_ids=[SegmentId(0, 0), SegmentId(2, 3)]\n        )\n        self.assertEqual(element_id.block_id, 2)\n        self.assertEqual(element_id.grid_index, 0)\n        self.assertEqual(element_id.refinement_levels, [0, 2])\n        self.assertEqual(ElementId[2](\"[B2,(L0I0,L2I3)]\"), element_id)\n\n    def test_repr(self):\n        self.assertEqual(repr(ElementId[1](0)), \"[B0,(L0I0)]\")\n        self.assertEqual(\n            repr(ElementId[2](2, [SegmentId(0, 0), SegmentId(1, 0)])),\n            \"[B2,(L0I0,L1I0)]\",\n        )\n        self.assertEqual(\n            repr(\n                ElementId[3](\n                    1, [SegmentId(1, 0), SegmentId(0, 0), SegmentId(1, 1)]\n                )\n            ),\n            \"[B1,(L1I0,L0I0,L1I1)]\",\n        )\n\n    def test_members(self):\n        parent = ElementId[1](0, [SegmentId(1, 0)])\n        child_lower = ElementId[1](0, [SegmentId(2, 0)])\n        child_upper = ElementId[1](0, [SegmentId(2, 1)])\n        self.assertEqual(\n            parent.id_of_child(dim=0, side=Side.Lower), child_lower\n        )\n        self.assertEqual(\n            parent.id_of_child(dim=0, side=Side.Upper), child_upper\n        )\n        self.assertEqual(child_lower.id_of_parent(dim=0), parent)\n        self.assertEqual(child_upper.id_of_parent(dim=0), parent)\n\n    def test_equality(self):\n        self.assertEqual(\n            ElementId[1](0, [SegmentId(1, 0)]),\n            ElementId[1](0, [SegmentId(1, 0)]),\n        )\n        self.assertNotEqual(\n            ElementId[1](0, [SegmentId(1, 0)]),\n            ElementId[1](0, [SegmentId(1, 1)]),\n        )\n\n    def test_comparison(self):\n        element1 = ElementId[1](0, [SegmentId(1, 0)])\n        element2 = ElementId[1](0, [SegmentId(1, 1)])\n        self.assertEqual(sorted([element2, element1]), [element1, element2])\n\n    def test_hash(self):\n        self.assertEqual(\n            hash(ElementId[1](0, [SegmentId(1, 0)])),\n            hash(ElementId[1](0, [SegmentId(1, 0)])),\n        )\n        self.assertNotEqual(\n            hash(ElementId[1](0, [SegmentId(1, 0)])),\n            hash(ElementId[1](0, [SegmentId(1, 1)])),\n        )\n\n    def test_to_short_id(self):\n        # Same segments, different block → same short_id\n        elem_b0 = ElementId[3](\n            0, [SegmentId(2, 3), SegmentId(1, 0), SegmentId(1, 1)]\n        )\n        elem_b5 = ElementId[3](\n            5, [SegmentId(2, 3), SegmentId(1, 0), SegmentId(1, 1)]\n        )\n        self.assertEqual(elem_b0.to_short_id(), elem_b5.to_short_id())\n        # Different segments → different short_id\n        elem_diff = ElementId[3](\n            0, [SegmentId(2, 2), SegmentId(1, 0), SegmentId(1, 1)]\n        )\n        self.assertNotEqual(elem_b0.to_short_id(), elem_diff.to_short_id())\n\n    def test_external_boundary_id(self):\n        self.assertEqual(\n            ElementId[1].external_boundary_id(),\n            ElementId[1].external_boundary_id(),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_ElementMap.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Frame, tnsr\nfrom spectre.Domain import (\n    ElementId,\n    ElementMap,\n    deserialize_domain,\n    deserialize_functions_of_time,\n)\nfrom spectre.Domain.CoordinateMaps import (\n    CoordinateMapElementLogicalToInertial1D,\n)\nfrom spectre.Domain.Creators import Interval\nfrom spectre.Informer import unit_test_src_path\n\n\nclass TestElementMap(unittest.TestCase):\n    def test_element_map(self):\n        # Full domain is [0, 2]\n        domain = Interval(\n            lower_bounds=[0.0],\n            upper_bounds=[2.0],\n            is_periodic=[False],\n            initial_refinement_levels=[1],\n            initial_num_points=[3],\n        ).create_domain()\n        # Element is [0, 1]\n        element_id = ElementId[1](\"[B0,(L1I0)]\")\n        element_map = ElementMap(element_id, domain)\n        self.assertIsInstance(\n            element_map, CoordinateMapElementLogicalToInertial1D\n        )\n        self.assertFalse(element_map.is_identity())\n        # Logical coords are [-1, 1]\n        xi_data = np.random.rand(1, 4) * 2.0 - 1.0\n        xi = tnsr.I[DataVector, 1, Frame.ElementLogical](xi_data)\n        npt.assert_allclose(element_map(xi), (np.array(xi) + 1.0) / 2.0)\n        npt.assert_allclose(\n            element_map.jacobian(xi).get(0, 0), np.ones(4) * 0.5\n        )\n        npt.assert_allclose(\n            element_map.inv_jacobian(xi).get(0, 0), np.ones(4) * 2.0\n        )\n        # Test inverse\n        target_point_inside = tnsr.I[float, 1, Frame.Inertial](fill=0.0)\n        self.assertAlmostEqual(\n            element_map.inverse(target_point_inside).get(0), -1.0\n        )\n        # Inverses aren't currently guaranteed to return None outside the source\n        # interval, so here the inertial coordinate 1.5 is mapped to a logical\n        # coordinate of 2. (that's halfway into the next element to the right).\n        target_point_outside = tnsr.I[float, 1, Frame.Inertial](fill=1.5)\n        self.assertAlmostEqual(\n            element_map.inverse(target_point_outside).get(0), 2.0\n        )\n\n    def test_inertial_coordinates(self):\n        volfile_name = os.path.join(\n            unit_test_src_path(), \"Visualization/Python/VolTestData0.h5\"\n        )\n        with spectre_h5.H5File(volfile_name, \"r\") as open_h5_file:\n            volfile = open_h5_file.get_vol(\"/element_data\")\n            obs_id = volfile.list_observation_ids()[0]\n            domain = deserialize_domain[3](volfile.get_domain())\n            functions_of_time = deserialize_functions_of_time(\n                volfile.get_functions_of_time(obs_id)\n            )\n\n        element_id = ElementId[3](\"[B0,(L1I0,L0I0,L0I0)]\")\n        element_logical_coords = tnsr.I[DataVector, 3, Frame.ElementLogical](\n            3 * [DataVector([-1.0, 1.0])]\n        )\n\n        element_map = ElementMap(element_id, domain)\n        inertial_coords = np.asarray(\n            element_map(\n                element_logical_coords,\n                time=0.0,\n                functions_of_time=functions_of_time,\n            )\n        )\n\n        # Domain is [0, 2 pi]^3, and split in two in dimension 0\n        npt.assert_allclose(\n            inertial_coords,\n            np.array([[0.0, np.pi], [0.0, 2 * np.pi], [0.0, 2 * np.pi]]),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_FunctionsOfTime.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport math\nimport os\nimport unittest\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.Domain import (\n    PiecewisePolynomial2,\n    PiecewisePolynomial3,\n    QuaternionFunctionOfTime,\n    deserialize_functions_of_time,\n    serialize_functions_of_time,\n)\nfrom spectre.Informer import unit_test_src_path\n\n\nclass TestFunctionsOfTime(unittest.TestCase):\n    def test_deserialize(self):\n        volfile_name = os.path.join(\n            unit_test_src_path(), \"Visualization/Python/VolTestData0.h5\"\n        )\n        with spectre_h5.H5File(volfile_name, \"r\") as open_h5_file:\n            volfile = open_h5_file.get_vol(\"/element_data\")\n            obs_id = volfile.list_observation_ids()[0]\n            serialized_fot = volfile.get_functions_of_time(obs_id)\n\n        functions_of_time = deserialize_functions_of_time(serialized_fot)\n        translation = functions_of_time[\"Translation\"]\n        self.assertEqual(translation.time_bounds(), [0.0, math.inf])\n        self.assertEqual(translation.func(0.0), [DataVector(size=3, fill=0.0)])\n        self.assertEqual(\n            translation.func_and_deriv(0.0), 2 * [DataVector(size=3, fill=0.0)]\n        )\n        self.assertEqual(\n            translation.func_and_2_derivs(0.0),\n            3 * [DataVector(size=3, fill=0.0)],\n        )\n\n    def test_serialize(self):\n        translation_fot = PiecewisePolynomial2(\n            time=0.0,\n            initial_func_and_derivs=3 * [DataVector(size=3, fill=0.0)],\n            expiration_time=math.inf,\n        )\n        rotation_fot = QuaternionFunctionOfTime(\n            time=0.0,\n            initial_quat_func=[DataVector(size=4, fill=1.0)],\n            initial_angle_func=4 * [DataVector(size=3, fill=0.0)],\n            expiration_time=math.inf,\n        )\n        expansion_fot = PiecewisePolynomial3(\n            time=0.0,\n            initial_func_and_derivs=4 * [DataVector(size=1, fill=0.0)],\n            expiration_time=math.inf,\n        )\n        expansion_outer_fot = PiecewisePolynomial3(\n            time=0.0,\n            initial_func_and_derivs=4 * [DataVector(size=1, fill=0.0)],\n            expiration_time=math.inf,\n        )\n        not_serialized_fots = {\n            \"Expansion\": expansion_fot,\n            \"ExpansionOuterBoundary\": expansion_outer_fot,\n            \"Rotation\": rotation_fot,\n            \"Translation\": translation_fot,\n        }\n\n        serialized_fots = serialize_functions_of_time(not_serialized_fots)\n        deserialized_fots = deserialize_functions_of_time(serialized_fots)\n        self.assertEqual(\n            not_serialized_fots[\"Expansion\"].func_and_2_derivs(0.0),\n            deserialized_fots[\"Expansion\"].func_and_2_derivs(0.0),\n        )\n        self.assertEqual(\n            not_serialized_fots[\"ExpansionOuterBoundary\"].func_and_2_derivs(\n                0.0\n            ),\n            deserialized_fots[\"ExpansionOuterBoundary\"].func_and_2_derivs(0.0),\n        )\n        self.assertEqual(\n            not_serialized_fots[\"Rotation\"].func_and_2_derivs(0.0),\n            deserialized_fots[\"Rotation\"].func_and_2_derivs(0.0),\n        )\n        self.assertEqual(\n            not_serialized_fots[\"Rotation\"].quat_func_and_2_derivs(0.0),\n            deserialized_fots[\"Rotation\"].quat_func_and_2_derivs(0.0),\n        )\n        self.assertEqual(\n            not_serialized_fots[\"Translation\"].func_and_2_derivs(0.0),\n            deserialized_fots[\"Translation\"].func_and_2_derivs(0.0),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_JacobianDiagnostic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Frame, Jacobian, tnsr\nfrom spectre.Domain import jacobian_diagnostic\nfrom spectre.Spectral import Basis, Mesh, Quadrature, collocation_points\n\n\ndef affine_map(x):\n    return 2.0 * x\n\n\nclass TestJacobianDiagnostic(unittest.TestCase):\n    def test_jacobian_diagnostic(self):\n        mesh = Mesh[1](4, Basis.Legendre, Quadrature.GaussLobatto)\n        x = collocation_points(mesh)\n        mapped_coordinates = tnsr.I[DataVector, 1, Frame.Grid]([affine_map(x)])\n        jac = Jacobian[DataVector, 1, Frame.Grid](num_points=4, fill=2.0)\n\n        jac_diag = jacobian_diagnostic(jac, mapped_coordinates, mesh)\n        expected_jac_diag = tnsr.I[DataVector, 1, Frame.ElementLogical](\n            num_points=4, fill=0.0\n        )\n        npt.assert_allclose(jac_diag, expected_jac_diag)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_RadiallyCompressedCoordinates.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import tnsr\nfrom spectre.Domain import radially_compressed_coordinates\nfrom spectre.Domain.CoordinateMaps import Distribution\n\n\nclass TestRadiallyCompressedCoordinates(unittest.TestCase):\n    def test_radially_compressed_coordinates(self):\n        inner_radius = 1.0\n        outer_radius = 1.0e6\n        expected_compressed_outer_radius = 6.0\n        x = np.array([[inner_radius, outer_radius], [0.0, 0.0], [0.0, 0.0]])\n        x_compressed = radially_compressed_coordinates(\n            x,\n            inner_radius=1.0,\n            outer_radius=1.0e6,\n            compression=Distribution.Logarithmic,\n        )\n        npt.assert_allclose(\n            x_compressed[0], [inner_radius, expected_compressed_outer_radius]\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_SegmentId.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.Domain import SegmentId\n\n\nclass TestSegmentId(unittest.TestCase):\n    def test_construction(self):\n        segment_id = SegmentId(refinement_level=1, index=0)\n        self.assertEqual(segment_id.refinement_level, 1)\n        self.assertEqual(segment_id.index, 0)\n\n    def test_repr(self):\n        self.assertEqual(repr(SegmentId(1, 0)), \"L1I0\")\n\n    def test_equality(self):\n        self.assertEqual(SegmentId(1, 0), SegmentId(1, 0))\n        self.assertNotEqual(SegmentId(1, 0), SegmentId(1, 1))\n        self.assertNotEqual(SegmentId(2, 0), SegmentId(1, 0))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Python/Test_StrahlkorperTransformations.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Domain import (\n    deserialize_domain,\n    deserialize_functions_of_time,\n    strahlkorper_in_inertial_frame,\n)\nfrom spectre.Informer import unit_test_src_path\nfrom spectre.SphericalHarmonics import Frame, Strahlkorper\n\n\nclass TestStrahlkorperTransformations(unittest.TestCase):\n    def test_strahlkorper_in_different_frame(self):\n        volfile_name = os.path.join(\n            unit_test_src_path(), \"Visualization/Python/VolTestData0.h5\"\n        )\n        with spectre_h5.H5File(volfile_name, \"r\") as open_h5_file:\n            volfile = open_h5_file.get_vol(\"/element_data\")\n            obs_id = volfile.list_observation_ids()[0]\n            domain = deserialize_domain[3](volfile.get_domain())\n            functions_of_time = deserialize_functions_of_time(\n                volfile.get_functions_of_time(obs_id)\n            )\n\n        strahlkorper_grid = Strahlkorper[Frame.Grid](\n            l_max=2, radius=0.5, center=[0.5, 0.5, 0.5]\n        )\n        strahlkorper_inertial = strahlkorper_in_inertial_frame(\n            strahlkorper_grid,\n            domain=domain,\n            functions_of_time=functions_of_time,\n            time=0.0,\n        )\n        self.assertAlmostEqual(strahlkorper_inertial.average_radius, 0.5)\n        strahlkorper_inertial = strahlkorper_in_inertial_frame_aligned(\n            strahlkorper_grid,\n            domain=domain,\n            functions_of_time=functions_of_time,\n            time=0.0,\n        )\n        self.assertAlmostEqual(strahlkorper_inertial.average_radius, 0.5)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DomainStructure\")\n\nset(LIBRARY_SOURCES\n  Test_BlockGroups.cpp\n  Test_BlockId.cpp\n  Test_BlockNeighbors.cpp\n  Test_ChildSize.cpp\n  Test_CreateInitialMesh.cpp\n  Test_Direction.cpp\n  Test_DirectionalId.cpp\n  Test_Element.cpp\n  Test_ElementId.cpp\n  Test_FaceType.cpp\n  Test_HasBoundary.cpp\n  Test_Hypercube.cpp\n  Test_IndexToSliceAt.cpp\n  Test_InitialElementIds.cpp\n  Test_NeighborIsConforming.cpp\n  Test_Neighbors.cpp\n  Test_ObjectLabel.cpp\n  Test_OrientationMap.cpp\n  Test_OrientationMapHelpers.cpp\n  Test_SegmentId.cpp\n  Test_Side.cpp\n  Test_Topology.cpp\n  Test_TrimMap.cpp\n  Test_ZCurve.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DomainHelpers\n  DomainStructure\n  SerializationHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/OrientationMap1d.serializations",
    "content": "version 1:AAAAAAAAAABIAQ==\nversion 2:CJA=\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/OrientationMap2d.serializations",
    "content": "version 1:AAAAAAAAAABISQE=\nversion 2:mJA=\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/OrientationMap3d.serializations",
    "content": "version 1:AAAAAAAAAABISUoB\nversion 2:mJo=\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_BlockGroups.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Structure/BlockGroups.hpp\"\n\nnamespace domain {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.BlockGroups\", \"[Domain][Unit]\") {\n  const std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups{\n          {\"Group1\", {\"Block1\", \"Block2\"}},\n          {\"Group2\", {\"Block3\", \"Block4\"}},\n      };\n  CHECK(block_is_in_group(\"Block1\", \"Group1\", block_groups));\n  CHECK(block_is_in_group(\"Block2\", \"Group1\", block_groups));\n  CHECK(not block_is_in_group(\"Block3\", \"Group1\", block_groups));\n  CHECK(not block_is_in_group(\"Block1\", \"Group2\", block_groups));\n\n  const std::vector<std::string> all_block_names{\"Block1\", \"Block2\", \"Block3\",\n                                                 \"Block4\"};\n  const std::unordered_set<std::string> expanded_block_names{\"Block1\", \"Block3\",\n                                                             \"Block4\"};\n  CHECK(expand_block_groups_to_block_names({\"Block1\", \"Group2\"},\n                                           all_block_names, block_groups) ==\n        expanded_block_names);\n  CHECK_THROWS_WITH(\n      expand_block_groups_to_block_names({\"NoBlock\", \"Group2\"}, all_block_names,\n                                         block_groups),\n      Catch::Matchers::ContainsSubstring(\n          \"The block or group 'NoBlock' is not one of the block names or \"\n          \"groups of the domain.\"));\n  CHECK(block_ids_from_names({\"Block1\", \"Group2\"}, all_block_names,\n                             block_groups) == std::vector<size_t>{0, 2, 3});\n  CHECK(block_ids_from_names({\"Block1\", \"Block2\"}, all_block_names,\n                             block_groups) == std::vector<size_t>{0, 1});\n  CHECK(block_ids_from_names({\"Block2\", \"Block1\"}, all_block_names,\n                             block_groups) == std::vector<size_t>{0, 1});\n  CHECK(block_ids_from_names({\"Block1\", \"Group1\"}, all_block_names,\n                             block_groups) == std::vector<size_t>{0, 1});\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_BlockId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Domain/Structure/BlockId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.BlockId\", \"[Domain][Unit]\") {\n  domain::BlockId id{10};\n  CHECK(id.get_index() == 10);\n  CHECK(id == domain::BlockId{10});\n  CHECK(id != domain::BlockId{7});\n  CHECK(get_output(id) == \"[10]\");\n  CHECK(id++ == domain::BlockId{10});\n  CHECK(id == domain::BlockId{11});\n  CHECK(id-- == domain::BlockId{11});\n  CHECK(id == domain::BlockId{10});\n  CHECK(&id == &(++id));\n  CHECK(id == domain::BlockId{11});\n  CHECK(&id == &(--id));\n  CHECK(id == domain::BlockId{10});\n\n  // Test PUP\n  test_serialization(id);\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_BlockNeighbors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.BlockNeighbors\", \"[Domain][Unit]\") {\n  // Test default constructor, only used for Charm++ serialization so no CHECK\n  // calls:\n  BlockNeighbors<3> test_block_neighbor;\n\n  // Test constructor:\n  OrientationMap<3> custom_orientation(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::upper_zeta(),\n       Direction<3>::upper_xi()}});\n  BlockNeighbors<3> custom_neighbor(0, custom_orientation);\n  CHECK(*(custom_neighbor.begin()) == 0);\n  CHECK(custom_neighbor.orientation(0)(Direction<3>::upper_xi()) ==\n        Direction<3>::upper_eta());\n  CHECK(custom_neighbor.orientation(0)(Direction<3>::upper_eta()) ==\n        Direction<3>::upper_zeta());\n  CHECK(custom_neighbor.orientation(0)(Direction<3>::upper_zeta()) ==\n        Direction<3>::upper_xi());\n\n  // Test output operator:\n  CHECK(get_output(custom_neighbor) ==\n        \"Ids = (0); orientations = ([0,(+1, +2, +0)]); conforming = true\");\n\n  // Test comparison operator:\n  CHECK(test_block_neighbor != custom_neighbor);\n  CHECK(custom_neighbor == custom_neighbor);\n\n  // Test serialization:\n  test_serialization(custom_neighbor);\n\n  // Test semantics:\n  const auto custom_copy = custom_neighbor;\n  test_copy_semantics(custom_neighbor);\n  // clang-tidy: std::move does nothing\n  test_move_semantics(std::move(custom_neighbor), custom_copy);  // NOLINT\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_ChildSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n\nnamespace domain {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.ChildSize\", \"[Domain][Unit]\") {\n  CHECK(child_size({0, 0}, {0, 0}) == Spectral::SegmentSize::Full);\n  CHECK(child_size({1, 0}, {0, 0}) == Spectral::SegmentSize::LowerHalf);\n  CHECK(child_size({1, 1}, {0, 0}) == Spectral::SegmentSize::UpperHalf);\n  CHECK(child_size({1, 1}, {1, 1}) == Spectral::SegmentSize::Full);\n  CHECK(child_size<0>({}, {}).empty());\n  CHECK(\n      child_size<1>({{{2, 3}}}, {{{1, 1}}}) ==\n      std::array<Spectral::SegmentSize, 1>{{Spectral::SegmentSize::UpperHalf}});\n  CHECK(child_size<2>({{{0, 0}, {1, 0}}}, {{{0, 0}, {0, 0}}}) ==\n        std::array<Spectral::SegmentSize, 2>{\n            {Spectral::SegmentSize::Full, Spectral::SegmentSize::LowerHalf}});\n  CHECK(child_size<3>({{{1, 1}, {1, 1}, {2, 2}}}, {{{0, 0}, {1, 1}, {1, 1}}}) ==\n        std::array<Spectral::SegmentSize, 3>{\n            {Spectral::SegmentSize::UpperHalf, Spectral::SegmentSize::Full,\n             Spectral::SegmentSize::LowerHalf}});\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH((child_size({1, 1}, {1, 0})),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Segment id 'L1I0' is not the parent of 'L1I1'.\"));\n#endif\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_CreateInitialMesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.CreateInitialMesh\", \"[Domain][Unit]\") {\n  const ElementId<1> element_id_1d{0};\n  const ElementId<2> element_id_2d{0};\n  const ElementId<3> element_id_3d{0};\n  {\n    INFO(\"From Element\");\n    const Element<1> interval(element_id_1d, {});\n    const Element<2> rectangle(element_id_2d, {});\n    const Element<3> brick(element_id_3d, {});\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3}}}, interval, i1_basis, i1_quadrature) ==\n              Mesh<1>{3, i1_basis, i1_quadrature});\n        CHECK(create_initial_mesh({{{3, 2}}}, rectangle, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<2>{{{3, 2}}, i1_basis, i1_quadrature});\n        CHECK(create_initial_mesh({{{3, 2, 4}}}, brick, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<3>{{{3, 2, 4}}, i1_basis, i1_quadrature});\n      }\n    }\n#ifdef SPECTRE_DEBUG\n    CHECK_THROWS_WITH(\n        create_initial_mesh({{{3}}}, interval, Spectral::Basis::Fourier,\n                            Spectral::Quadrature::Gauss),\n        Catch::Matchers::ContainsSubstring(\"Invalid I1 Basis\"));\n    CHECK_THROWS_WITH(\n        create_initial_mesh({{{3}}}, interval, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::Equiangular),\n        Catch::Matchers::ContainsSubstring(\"Invalid I1 Quadrature\"));\n#endif  // SPECTRE_DEBUG\n  }\n  {\n    INFO(\"From Block and ElementId\");\n    const Block<1> interval(nullptr, 0, {});\n    const Block<2> rectangle(nullptr, 0, {});\n    const Block<3> brick(nullptr, 0, {});\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3}}}, interval, element_id_1d, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<1>{3, i1_basis, i1_quadrature});\n        CHECK(create_initial_mesh({{{3, 2}}}, rectangle, element_id_2d,\n                                  i1_basis, i1_quadrature) ==\n              Mesh<2>{{{3, 2}}, i1_basis, i1_quadrature});\n        CHECK(create_initial_mesh({{{3, 2, 4}}}, brick, element_id_3d, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<3>{{{3, 2, 4}}, i1_basis, i1_quadrature});\n      }\n    }\n  }\n  {\n    INFO(\"Another element\");\n    const Element<1> interval(ElementId<1>{1}, {});\n    const Element<2> rectangle(ElementId<2>{1}, {});\n    const Element<3> brick(ElementId<3>{1}, {});\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3}}, {{2}}}, interval, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<1>{2, i1_basis, i1_quadrature});\n        CHECK(create_initial_mesh({{{3, 3}}, {{2, 2}}}, rectangle, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<2>{2, i1_basis, i1_quadrature});\n        CHECK(create_initial_mesh({{{3, 3, 3}}, {{2, 2, 2}}}, brick, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<3>{2, i1_basis, i1_quadrature});\n      }\n    }\n  }\n  {\n    INFO(\"annulus\");\n    const Element<2> annulus(element_id_2d, {}, domain::topologies::annulus);\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(\n            create_initial_mesh({{{3, 4}}}, annulus, i1_basis, i1_quadrature) ==\n            Mesh<2>{\n                {{3, 4}},\n                std::array{i1_basis, Spectral::Basis::Fourier},\n                std::array{i1_quadrature, Spectral::Quadrature::Equiangular}});\n      }\n    }\n  }\n  {\n    INFO(\"disk\");\n    const Block<2> disk(nullptr, 0, {}, \"\", domain::topologies::disk);\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3, 4}}}, disk, element_id_2d, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<2>{{{3, 4}},\n                      std::array{Spectral::Basis::ZernikeB2,\n                                 Spectral::Basis::ZernikeB2},\n                      std::array{Spectral::Quadrature::GaussRadauUpper,\n                                 Spectral::Quadrature::Equiangular}});\n      }\n    }\n  }\n  {\n    INFO(\"cylindrical_shell\");\n    const Element<3> cylindrical_shell(element_id_3d, {},\n                                       domain::topologies::cylindrical_shell);\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(\n            create_initial_mesh({{{3, 2, 4}}}, cylindrical_shell, i1_basis,\n                                i1_quadrature) ==\n            Mesh<3>{{{3, 2, 4}},\n                    std::array{i1_basis, Spectral::Basis::Fourier, i1_basis},\n                    std::array{i1_quadrature, Spectral::Quadrature::Equiangular,\n                               i1_quadrature}});\n      }\n    }\n  }\n  {\n    INFO(\"full_cylinder\");\n    const Block<3> full_cylinder(nullptr, 0, {}, \"\",\n                                 domain::topologies::full_cylinder);\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3, 2, 4}}}, full_cylinder, element_id_3d,\n                                  i1_basis, i1_quadrature) ==\n              Mesh<3>{{{3, 2, 4}},\n                      std::array{Spectral::Basis::ZernikeB2,\n                                 Spectral::Basis::ZernikeB2, i1_basis},\n                      std::array{Spectral::Quadrature::GaussRadauUpper,\n                                 Spectral::Quadrature::Equiangular,\n                                 i1_quadrature}});\n      }\n    }\n  }\n  {\n    INFO(\"spheriical_shell\");\n    const Element<3> spherical_shell(element_id_3d, {},\n                                     domain::topologies::spherical_shell);\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3, 2, 4}}}, spherical_shell, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<3>{{{3, 2, 4}},\n                      std::array{i1_basis, Spectral::Basis::SphericalHarmonic,\n                                 Spectral::Basis::SphericalHarmonic},\n                      std::array{i1_quadrature, Spectral::Quadrature::Gauss,\n                                 Spectral::Quadrature::Equiangular}});\n      }\n    }\n  }\n  {\n    INFO(\"full_sphere\");\n    const Block<3> full_sphere(nullptr, 0, {}, \"\",\n                               domain::topologies::full_sphere);\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3, 2, 4}}}, full_sphere, element_id_3d,\n                                  i1_basis, i1_quadrature) ==\n              Mesh<3>{{{3, 2, 4}},\n                      std::array{Spectral::Basis::ZernikeB3,\n                                 Spectral::Basis::ZernikeB3,\n                                 Spectral::Basis::ZernikeB3},\n                      std::array{Spectral::Quadrature::GaussRadauUpper,\n                                 Spectral::Quadrature::Gauss,\n                                 Spectral::Quadrature::Equiangular}});\n      }\n    }\n  }\n  {\n    INFO(\"cartoon_sphere\");\n    const Element<3> cartoon_sphere(element_id_3d, {},\n                                    domain::topologies::cartoon_sphere);\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3, 1, 1}}}, cartoon_sphere, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<3>{{{3, 1, 1}},\n                      std::array{i1_basis, Spectral::Basis::Cartoon,\n                                 Spectral::Basis::Cartoon},\n                      std::array{i1_quadrature,\n                                 Spectral::Quadrature::SphericalSymmetry,\n                                 Spectral::Quadrature::SphericalSymmetry}});\n      }\n    }\n  }\n  {\n    INFO(\"cartoon_cylinder\");\n    const Element<3> cartoon_cylinder(element_id_3d, {},\n                                      domain::topologies::cartoon_cylinder);\n    for (const auto& i1_basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto& i1_quadrature :\n           {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n        CHECK(create_initial_mesh({{{3, 2, 1}}}, cartoon_cylinder, i1_basis,\n                                  i1_quadrature) ==\n              Mesh<3>{{{3, 2, 1}},\n                      std::array{i1_basis, i1_basis, Spectral::Basis::Cartoon},\n                      std::array{i1_quadrature, i1_quadrature,\n                                 Spectral::Quadrature::AxialSymmetry}});\n      }\n    }\n  }\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      create_initial_mesh(\n          {{{3, 4}}}, Block<2>{nullptr, 0, {}, \"\", domain::topologies::disk},\n          ElementId<2>{0, {{SegmentId{1, 0}, SegmentId{0, 0}}}},\n          Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto),\n      Catch::Matchers::ContainsSubstring(\n          \"Splitting Topology::B2Radial is not yet supported\"));\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_Direction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <string>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid test_construction_1d() {\n  CHECK(sizeof(Direction<1>) == 1);\n  auto upper_xi_1 = Direction<1>::upper_xi();\n  CHECK(upper_xi_1.opposite() == Direction<1>::lower_xi());\n  CHECK(upper_xi_1.opposite().sign() == -1.0);\n  CHECK(upper_xi_1.axis() == Direction<1>::Axis::Xi);\n  test_serialization(upper_xi_1);\n\n  auto lower_xi_1 = Direction<1>(0, Side::Lower);\n  CHECK(lower_xi_1 == Direction<1>::lower_xi());\n  CHECK(lower_xi_1.axis() == Direction<1>::Axis::Xi);\n  CHECK(lower_xi_1.side() == Side::Lower);\n  test_serialization(lower_xi_1);\n}\n\nvoid test_construction_2d() {\n  CHECK(sizeof(Direction<2>) == 1);\n  auto upper_xi_2 = Direction<2>(0, Side::Upper);\n  CHECK(upper_xi_2 == Direction<2>::upper_xi());\n  CHECK(upper_xi_2.axis() == Direction<2>::Axis::Xi);\n  CHECK(upper_xi_2.side() == Side::Upper);\n  test_serialization(upper_xi_2);\n\n  auto upper_eta_2 = Direction<2>(1, Side::Upper);\n  CHECK(upper_eta_2 == Direction<2>::upper_eta());\n  CHECK(upper_eta_2.axis() == Direction<2>::Axis::Eta);\n  CHECK(upper_eta_2.side() == Side::Upper);\n  test_serialization(upper_eta_2);\n\n  auto lower_xi_2 = Direction<2>(0, Side::Lower);\n  CHECK(lower_xi_2 == Direction<2>::lower_xi());\n  CHECK(lower_xi_2.axis() == Direction<2>::Axis::Xi);\n  CHECK(lower_xi_2.side() == Side::Lower);\n  test_serialization(lower_xi_2);\n\n  auto lower_eta_2 = Direction<2>(1, Side::Lower);\n  CHECK(lower_eta_2 == Direction<2>::lower_eta());\n  CHECK(lower_eta_2.axis() == Direction<2>::Axis::Eta);\n  CHECK(lower_eta_2.side() == Side::Lower);\n  test_serialization(lower_eta_2);\n}\n\nvoid test_construction_3d() {\n  CHECK(sizeof(Direction<3>) == 1);\n  auto upper_xi = Direction<3>(0, Side::Upper);\n  CHECK(upper_xi == Direction<3>::upper_xi());\n  CHECK(upper_xi.axis() == Direction<3>::Axis::Xi);\n  CHECK(upper_xi.side() == Side::Upper);\n  test_serialization(upper_xi);\n\n  auto upper_eta = Direction<3>(1, Side::Upper);\n  CHECK(upper_eta == Direction<3>::upper_eta());\n  CHECK(upper_eta.axis() == Direction<3>::Axis::Eta);\n  CHECK(upper_eta.side() == Side::Upper);\n  test_serialization(upper_eta);\n\n  auto lower_xi = Direction<3>(0, Side::Lower);\n  CHECK(lower_xi == Direction<3>::lower_xi());\n  CHECK(lower_xi.axis() == Direction<3>::Axis::Xi);\n  CHECK(lower_xi.side() == Side::Lower);\n  test_serialization(lower_xi);\n\n  auto lower_eta = Direction<3>(1, Side::Lower);\n  CHECK(lower_eta == Direction<3>::lower_eta());\n  CHECK(lower_eta.axis() == Direction<3>::Axis::Eta);\n  CHECK(lower_eta.side() == Side::Lower);\n  test_serialization(lower_eta);\n\n  auto upper_zeta = Direction<3>(2, Side::Upper);\n  CHECK(upper_zeta == Direction<3>::upper_zeta());\n  CHECK(upper_zeta.axis() == Direction<3>::Axis::Zeta);\n  CHECK(upper_zeta.side() == Side::Upper);\n  test_serialization(upper_zeta);\n\n  auto lower_zeta = Direction<3>(2, Side::Lower);\n  CHECK(lower_zeta == Direction<3>::lower_zeta());\n  CHECK(lower_zeta.axis() == Direction<3>::Axis::Zeta);\n  CHECK(lower_zeta.side() == Side::Lower);\n  test_serialization(lower_zeta);\n}\n\nvoid test_semantics() {\n  check_cmp(Direction<1>::lower_xi(), Direction<1>::upper_xi());\n  check_cmp(Direction<2>::lower_xi(), Direction<2>::upper_xi());\n  check_cmp(Direction<2>::upper_xi(), Direction<2>::lower_eta());\n  check_cmp(Direction<2>::lower_eta(), Direction<2>::upper_eta());\n  check_cmp(Direction<3>::lower_xi(), Direction<3>::upper_xi());\n  check_cmp(Direction<3>::upper_xi(), Direction<3>::lower_eta());\n  check_cmp(Direction<3>::lower_eta(), Direction<3>::upper_eta());\n  check_cmp(Direction<3>::upper_eta(), Direction<3>::lower_zeta());\n  check_cmp(Direction<3>::lower_zeta(), Direction<3>::upper_zeta());\n  CHECK(std::is_sorted(Direction<1>::all_directions().begin(),\n                       Direction<1>::all_directions().end()));\n  CHECK(std::is_sorted(Direction<2>::all_directions().begin(),\n                       Direction<2>::all_directions().end()));\n  CHECK(std::is_sorted(Direction<3>::all_directions().begin(),\n                       Direction<3>::all_directions().end()));\n}\n\nvoid test_helper_functions() {\n  auto upper_xi_3 = Direction<3>::upper_xi();\n  CHECK(upper_xi_3.axis() == Direction<3>::Axis::Xi);\n  CHECK(upper_xi_3.side() == Side::Upper);\n\n  auto lower_xi_3 = Direction<3>::lower_xi();\n  CHECK(lower_xi_3.axis() == Direction<3>::Axis::Xi);\n  CHECK(lower_xi_3.side() == Side::Lower);\n\n  auto upper_eta_3 = Direction<3>::upper_eta();\n  CHECK(upper_eta_3.axis() == Direction<3>::Axis::Eta);\n  CHECK(upper_eta_3.side() == Side::Upper);\n\n  auto lower_eta_3 = Direction<3>::lower_eta();\n  CHECK(lower_eta_3.axis() == Direction<3>::Axis::Eta);\n  CHECK(lower_eta_3.side() == Side::Lower);\n\n  auto upper_zeta_3 = Direction<3>::upper_zeta();\n  CHECK(upper_zeta_3.axis() == Direction<3>::Axis::Zeta);\n  CHECK(upper_zeta_3.side() == Side::Upper);\n\n  auto lower_zeta_3 = Direction<3>::lower_zeta();\n  CHECK(lower_zeta_3.axis() == Direction<3>::Axis::Zeta);\n  CHECK(lower_zeta_3.side() == Side::Lower);\n}\n\nvoid test_std_hash() {\n  size_t upper_xi_1_hash = std::hash<Direction<1>>{}(Direction<1>::upper_xi());\n  size_t lower_xi_1_hash = std::hash<Direction<1>>{}(Direction<1>::lower_xi());\n\n  size_t upper_xi_2_hash = std::hash<Direction<2>>{}(Direction<2>::upper_xi());\n  size_t lower_xi_2_hash = std::hash<Direction<2>>{}(Direction<2>::lower_xi());\n  size_t upper_eta_2_hash =\n      std::hash<Direction<2>>{}(Direction<2>::upper_eta());\n  size_t lower_eta_2_hash =\n      std::hash<Direction<2>>{}(Direction<2>::lower_eta());\n\n  size_t upper_xi_3_hash = std::hash<Direction<3>>{}(Direction<3>::upper_xi());\n  size_t lower_xi_3_hash = std::hash<Direction<3>>{}(Direction<3>::lower_xi());\n  size_t upper_eta_3_hash =\n      std::hash<Direction<3>>{}(Direction<3>::upper_eta());\n  size_t lower_eta_3_hash =\n      std::hash<Direction<3>>{}(Direction<3>::lower_eta());\n  size_t upper_zeta_3_hash =\n      std::hash<Direction<3>>{}(Direction<3>::upper_zeta());\n  size_t lower_zeta_3_hash =\n      std::hash<Direction<3>>{}(Direction<3>::lower_zeta());\n\n  CHECK(upper_xi_1_hash != lower_xi_1_hash);\n  std::array<size_t, 4> direction_2 = {{upper_xi_2_hash, lower_xi_2_hash,\n                                       upper_eta_2_hash, lower_eta_2_hash}};\n  for (size_t i = 0; i < direction_2.size(); i++) {\n    for (size_t j = i + 1; j < direction_2.size(); j++) {\n      CHECK(gsl::at(direction_2, i) != gsl::at(direction_2, j));\n    }\n  }\n  std::array<size_t, 6> direction_3 = {{upper_xi_3_hash,   lower_xi_3_hash,\n                                       upper_eta_3_hash,  lower_eta_3_hash,\n                                       upper_zeta_3_hash, lower_zeta_3_hash}};\n  for (size_t i = 0; i < direction_3.size(); i++) {\n    for (size_t j = i + 1; j < direction_3.size(); j++) {\n      CHECK(gsl::at(direction_3, i) != gsl::at(direction_3, j));\n    }\n  }\n}\n\nvoid test_direction_hash() {\n  CHECK(DirectionHash<1>{}(Direction<1>::lower_xi()) == 0);\n  CHECK(DirectionHash<1>{}(Direction<1>::upper_xi()) == 1);\n\n  CHECK(DirectionHash<2>{}(Direction<2>::lower_xi()) == 0);\n  CHECK(DirectionHash<2>{}(Direction<2>::upper_xi()) == 1);\n  CHECK(DirectionHash<2>{}(Direction<2>::lower_eta()) == 2);\n  CHECK(DirectionHash<2>{}(Direction<2>::upper_eta()) == 3);\n\n  CHECK(DirectionHash<3>{}(Direction<3>::lower_xi()) == 0);\n  CHECK(DirectionHash<3>{}(Direction<3>::upper_xi()) == 1);\n  CHECK(DirectionHash<3>{}(Direction<3>::lower_eta()) == 2);\n  CHECK(DirectionHash<3>{}(Direction<3>::upper_eta()) == 3);\n  CHECK(DirectionHash<3>{}(Direction<3>::lower_zeta()) == 4);\n  CHECK(DirectionHash<3>{}(Direction<3>::upper_zeta()) == 5);\n}\n\nvoid test_output() {\n  auto upper_xi = Direction<1>(0, Side::Upper);\n  CHECK(get_output(upper_xi) == \"+0\");\n\n  auto lower_xi = Direction<2>(0, Side::Lower);\n  CHECK(get_output(lower_xi) == \"-0\");\n\n  auto upper_eta = Direction<2>(1, Side::Upper);\n  CHECK(get_output(upper_eta) == \"+1\");\n\n  auto lower_eta = Direction<2>(1, Side::Lower);\n  CHECK(get_output(lower_eta) == \"-1\");\n\n  auto upper_xi_3 = Direction<3>(0, Side::Upper);\n  CHECK(get_output(upper_xi_3) == \"+0\");\n\n  auto lower_xi_3 = Direction<3>(0, Side::Lower);\n  CHECK(get_output(lower_xi_3) == \"-0\");\n\n  auto upper_eta_3 = Direction<3>(1, Side::Upper);\n  CHECK(get_output(upper_eta_3) == \"+1\");\n\n  auto lower_eta_3 = Direction<3>(1, Side::Lower);\n  CHECK(get_output(lower_eta_3) == \"-1\");\n\n  auto upper_zeta_3 = Direction<3>(2, Side::Upper);\n  CHECK(get_output(upper_zeta_3) == \"+2\");\n\n  auto lower_zeta_3 = Direction<3>(2, Side::Lower);\n  CHECK(get_output(lower_zeta_3) == \"-2\");\n}\n\ntemplate <size_t Dim>\nvoid test_uninitialized() {\n  const Direction<Dim> uninitialized{};\n  CHECK(uninitialized.side() == Side::Uninitialized);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(uninitialized.dimension(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use an uninitialized Direction\"));\n  CHECK_THROWS_WITH(uninitialized.axis(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use an uninitialized Direction\"));\n  CHECK_THROWS_WITH(\n      uninitialized.sign(),\n      Catch::Matchers::ContainsSubstring(\n          \"sign() is only defined for Side::Lower and Side::Upper\"));\n  CHECK_THROWS_WITH(uninitialized.opposite(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use an uninitialized Direction\"));\n#endif  // SPECTRE_DEBUG\n  CHECK(uninitialized.bits() == 0);\n  test_serialization(uninitialized);\n  CHECK(get_output(uninitialized) == \"Uninitialized\");\n}\n\ntemplate <size_t Dim>\nvoid test_self() {\n  const Direction<Dim> self = Direction<Dim>::self();\n  CHECK(self.dimension() == 0);\n  CHECK(self.axis() == Direction<Dim>::Axis::Xi);\n  CHECK(self.side() == Side::Self);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      self.sign(),\n      Catch::Matchers::ContainsSubstring(\n          \"sign() is only defined for Side::Lower and Side::Upper\"));\n#endif  // SPECTRE_DEBUG\n  CHECK(self.opposite() == self);\n  CHECK(self.bits() == 0b00001100);\n  test_serialization(self);\n  CHECK(get_output(self) == \"Self\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.Direction\", \"[Domain][Unit]\") {\n  test_construction_1d();\n  test_construction_2d();\n  test_construction_3d();\n  test_semantics();\n  test_helper_functions();\n  test_std_hash();\n  test_direction_hash();\n  test_output();\n  test_uninitialized<1>();\n  test_uninitialized<2>();\n  test_uninitialized<3>();\n  test_self<1>();\n  test_self<2>();\n  test_self<3>();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      (Direction<1>(1, Side::Upper)),\n      Catch::Matchers::ContainsSubstring(\"dim = 1, but must be less than 1\"));\n  CHECK_THROWS_WITH(\n      (Direction<2>(2, Side::Upper)),\n      Catch::Matchers::ContainsSubstring(\"dim = 2, but must be less than 2\"));\n  CHECK_THROWS_WITH(\n      (Direction<3>(3, Side::Upper)),\n      Catch::Matchers::ContainsSubstring(\"dim = 3, but must be less than 3\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_DirectionalId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test_retrieval() {\n  const auto check = []<size_t Dim>(const Direction<Dim>& direction,\n                                    const ElementId<Dim>& element_id) {\n    const auto check_impl = [&element_id,\n                             &direction](const DirectionalId<Dim>& did) {\n      CHECK(did.id() == element_id);\n      CHECK(hash_value(did.id()) == hash_value(element_id));\n      CHECK(hash_value(did) != hash_value(element_id));\n      CHECK(did.direction() == direction);\n      CHECK(hash_value(did.direction()) == hash_value(direction));\n      CHECK(hash_value(did) != hash_value(direction));\n    };\n    const DirectionalId did0{direction, element_id};\n    check_impl(did0);\n    const DirectionalId did1 = serialize_and_deserialize(did0);\n    check_impl(did1);\n  };\n  const std::array uls{Side::Upper, Side::Lower};\n  std::vector element_ids_1d{\n      ElementId<1>{0, {{{0, 0}}}}, ElementId<1>{0, {{{1, 0}}}},\n      ElementId<1>{0, {{{1, 1}}}}, ElementId<1>{0, {{{2, 0}}}},\n      ElementId<1>{0, {{{2, 1}}}}, ElementId<1>{0, {{{2, 3}}}}};\n  for (const auto ul : uls) {\n    INFO(\"Test 1d\");\n    for (size_t dim = 0; dim < 1; ++dim) {\n      for (const auto& element_id : element_ids_1d) {\n        check(Direction<1>(dim, ul), element_id);\n      }\n    }\n  }\n\n  const std::vector element_ids_2d{\n      ElementId<2>{0, {{{0, 0}, {0, 0}}}}, ElementId<2>{0, {{{1, 0}, {0, 0}}}},\n      ElementId<2>{0, {{{0, 0}, {1, 0}}}}, ElementId<2>{0, {{{1, 0}, {1, 0}}}},\n      ElementId<2>{0, {{{1, 1}, {1, 0}}}}, ElementId<2>{0, {{{1, 0}, {1, 1}}}},\n      ElementId<2>{0, {{{2, 0}, {1, 0}}}}, ElementId<2>{0, {{{2, 3}, {1, 0}}}},\n      ElementId<2>{0, {{{1, 0}, {2, 0}}}}, ElementId<2>{0, {{{1, 0}, {2, 3}}}},\n      ElementId<2>{0, {{{2, 1}, {1, 0}}}}, ElementId<2>{0, {{{1, 0}, {2, 2}}}},\n      ElementId<2>{0, {{{2, 0}, {2, 0}}}}, ElementId<2>{0, {{{2, 0}, {2, 2}}}},\n      ElementId<2>{0, {{{2, 2}, {2, 0}}}}, ElementId<2>{0, {{{2, 3}, {2, 0}}}},\n      ElementId<2>{0, {{{2, 0}, {2, 3}}}}, ElementId<2>{0, {{{2, 3}, {2, 3}}}}};\n  for (const auto ul : uls) {\n    INFO(\"Test 2d\");\n    for (size_t dim = 0; dim < 2; ++dim) {\n      for (const auto& element_id : element_ids_2d) {\n        check(Direction<2>(dim, ul), element_id);\n      }\n    }\n  }\n\n  const std::vector element_ids_3d{ElementId<3>{0, {{{0, 0}, {0, 0}, {0, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {0, 0}, {0, 0}}}},\n                                   ElementId<3>{0, {{{0, 0}, {1, 0}, {0, 0}}}},\n                                   ElementId<3>{0, {{{0, 0}, {0, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 1}, {0, 0}, {0, 0}}}},\n                                   ElementId<3>{0, {{{0, 0}, {1, 1}, {0, 0}}}},\n                                   ElementId<3>{0, {{{0, 0}, {0, 0}, {1, 1}}}},\n                                   ElementId<3>{0, {{{1, 0}, {1, 0}, {0, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {0, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {1, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 1}, {1, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {1, 1}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {1, 0}, {1, 1}}}},\n                                   ElementId<3>{0, {{{2, 0}, {1, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {2, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {1, 0}, {2, 0}}}},\n                                   ElementId<3>{0, {{{2, 3}, {1, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {2, 3}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {1, 0}, {2, 3}}}},\n                                   ElementId<3>{0, {{{2, 1}, {1, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {2, 1}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {1, 0}, {2, 1}}}},\n                                   ElementId<3>{0, {{{2, 1}, {2, 0}, {1, 0}}}},\n                                   ElementId<3>{0, {{{2, 1}, {1, 0}, {2, 0}}}},\n                                   ElementId<3>{0, {{{2, 0}, {2, 1}, {1, 0}}}},\n                                   ElementId<3>{0, {{{1, 0}, {2, 1}, {2, 0}}}},\n                                   ElementId<3>{0, {{{2, 0}, {1, 0}, {2, 1}}}},\n                                   ElementId<3>{0, {{{1, 0}, {2, 0}, {2, 1}}}},\n                                   ElementId<3>{0, {{{2, 0}, {2, 0}, {2, 1}}}},\n                                   ElementId<3>{0, {{{2, 1}, {2, 0}, {2, 1}}}},\n                                   ElementId<3>{0, {{{2, 0}, {2, 1}, {2, 1}}}},\n                                   ElementId<3>{0, {{{2, 1}, {2, 1}, {2, 0}}}},\n                                   ElementId<3>{0, {{{2, 1}, {2, 1}, {2, 1}}}}};\n  for (const auto ul : uls) {\n    INFO(\"Test 3d\");\n    for (size_t dim = 0; dim < 3; ++dim) {\n      for (const auto& element_id : element_ids_3d) {\n        check(Direction<3>(dim, ul), element_id);\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.DirectionalId\", \"[Domain][Unit]\") {\n  test_retrieval();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_Element.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <functional>\n#include <unordered_set>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nvoid check_element_work(const typename Element<VolumeDim>::Neighbors_t&\n                            neighbors_in_largest_dimension,\n                        const size_t expected_number_of_neighbors) {\n  const ElementId<VolumeDim> id{5};\n  const Element<VolumeDim> element(id, neighbors_in_largest_dimension);\n\n  CHECK(element.id() == id);\n  CHECK(element.neighbors() == neighbors_in_largest_dimension);\n  CHECK(element.number_of_neighbors() == expected_number_of_neighbors);\n  for (const auto& direction : Direction<VolumeDim>::all_directions()) {\n    // The highest spatial dimension has neighbors; else, external boundary.\n    if (direction.dimension() == VolumeDim - 1) {\n      CHECK_FALSE(element.external_boundaries().contains(direction));\n      CHECK(element.internal_boundaries().contains(direction));\n      CHECK(element.neighbors().contains(direction));\n      CHECK(element.face_types().at(direction) ==\n            domain::FaceType::ConformingAligned);\n    } else {\n      CHECK(element.external_boundaries().contains(direction));\n      CHECK_FALSE(element.internal_boundaries().contains(direction));\n      CHECK_FALSE(element.neighbors().contains(direction));\n      CHECK(element.face_types().at(direction) == domain::FaceType::External);\n    }\n  }\n  CHECK(element.neighbors().size() == element.internal_boundaries().size());\n  CHECK(element.internal_boundaries().size() +\n            element.external_boundaries().size() ==\n        2 * VolumeDim);\n  CHECK(element == element);\n  CHECK_FALSE(element != element);\n\n  const Element<VolumeDim> element_diff_id(ElementId<VolumeDim>(3),\n                                           neighbors_in_largest_dimension);\n  CHECK(element != element_diff_id);\n  CHECK_FALSE(element == element_diff_id);\n\n  const Element<VolumeDim> element_diff_neighbors(\n      id, typename Element<VolumeDim>::Neighbors_t{\n              {Direction<VolumeDim>::lower_xi(),\n               neighbors_in_largest_dimension.at(\n                   Direction<VolumeDim>(VolumeDim - 1, Side::Upper))}});\n  CHECK(element != element_diff_neighbors);\n  CHECK_FALSE(element == element_diff_neighbors);\n\n  CHECK(get_output(element) == \"Element \" + get_output(element.id()) +\n                                   \":\\n\"\n                                   \"  Topology: \" +\n                                   get_output(element.topologies()) +\n                                   \"\\n\"\n                                   \"  Neighbors: \" +\n                                   get_output(element.neighbors()) +\n                                   \"\\n\"\n                                   \"  External boundaries: \" +\n                                   get_output(element.external_boundaries()) +\n                                   \"\\n\"\n                                   \"  Face types: \" +\n                                   get_output(element.face_types()) + \"\\n\");\n\n  test_serialization(element);\n}\n\nvoid check_element_1d() {\n  const Neighbors<1> neighbors_lower_xi(\n      std::unordered_set<ElementId<1>>{ElementId<1>(7)},\n      OrientationMap<1>::create_aligned());\n  const Neighbors<1> neighbors_upper_xi(\n      std::unordered_set<ElementId<1>>{ElementId<1>(7)},\n      OrientationMap<1>::create_aligned());\n  const typename Element<1>::Neighbors_t xi_neighbors{\n      {Direction<1>::lower_xi(), neighbors_lower_xi},\n      {Direction<1>::upper_xi(), neighbors_upper_xi}};\n  check_element_work<1>(xi_neighbors, 2);\n}\n\nvoid check_element_2d() {\n  const SegmentId root_segment{0, 0};\n  const Neighbors<2> neighbors_lower_eta(\n      std::unordered_set<ElementId<2>>{\n          ElementId<2>(7,\n                       {{root_segment.id_of_child(Side::Lower), root_segment}}),\n          ElementId<2>(\n              7, {{root_segment.id_of_child(Side::Upper), root_segment}})},\n      OrientationMap<2>::create_aligned());\n  const Neighbors<2> neighbors_upper_eta(\n      std::unordered_set<ElementId<2>>{\n          ElementId<2>(8,\n                       {{root_segment.id_of_child(Side::Lower), root_segment}}),\n          ElementId<2>(\n              8, {{root_segment.id_of_child(Side::Upper), root_segment}})},\n      OrientationMap<2>::create_aligned());\n  const typename Element<2>::Neighbors_t eta_neighbors{\n      {Direction<2>::lower_eta(), neighbors_lower_eta},\n      {Direction<2>::upper_eta(), neighbors_upper_eta}};\n  check_element_work<2>(eta_neighbors, 4);\n}\n\nvoid check_element_3d() {\n  const SegmentId root_segment{0, 0};\n  const Neighbors<3> neighbors_lower_zeta(\n      std::unordered_set<ElementId<3>>{\n          ElementId<3>(7,\n                       {{root_segment.id_of_child(Side::Lower),\n                         root_segment.id_of_child(Side::Lower), root_segment}}),\n          ElementId<3>(7,\n                       {{root_segment.id_of_child(Side::Lower),\n                         root_segment.id_of_child(Side::Upper), root_segment}}),\n          ElementId<3>(7,\n                       {{root_segment.id_of_child(Side::Upper),\n                         root_segment.id_of_child(Side::Lower), root_segment}}),\n          ElementId<3>(\n              7, {{root_segment.id_of_child(Side::Upper),\n                   root_segment.id_of_child(Side::Upper), root_segment}})},\n      OrientationMap<3>::create_aligned());\n  const Neighbors<3> neighbors_upper_zeta(\n      std::unordered_set<ElementId<3>>{\n          ElementId<3>(8,\n                       {{root_segment.id_of_child(Side::Lower),\n                         root_segment.id_of_child(Side::Lower), root_segment}}),\n          ElementId<3>(8,\n                       {{root_segment.id_of_child(Side::Lower),\n                         root_segment.id_of_child(Side::Upper), root_segment}}),\n          ElementId<3>(8,\n                       {{root_segment.id_of_child(Side::Upper),\n                         root_segment.id_of_child(Side::Lower), root_segment}}),\n          ElementId<3>(\n              8, {{root_segment.id_of_child(Side::Upper),\n                   root_segment.id_of_child(Side::Upper), root_segment}})},\n      OrientationMap<3>::create_aligned());\n  const typename Element<3>::Neighbors_t zeta_neighbors{\n      {Direction<3>::lower_zeta(), neighbors_lower_zeta},\n      {Direction<3>::upper_zeta(), neighbors_upper_zeta}};\n  check_element_work<3>(zeta_neighbors, 8);\n}\n\nvoid check_spherical_shell() {\n  const Element<3> spherical_shell(\n      ElementId<3>{5}, DirectionMap<3, Neighbors<3>>{},\n      std::array{domain::Topology::I1, domain::Topology::S2Colatitude,\n                 domain::Topology::S2Longitude});\n  CHECK(spherical_shell.external_boundaries().size() == 2);\n  CHECK(\n      spherical_shell.external_boundaries().contains(Direction<3>::lower_xi()));\n  CHECK(\n      spherical_shell.external_boundaries().contains(Direction<3>::upper_xi()));\n  CHECK(spherical_shell.neighbors().empty());\n  for (const auto& direction : Direction<3>::all_directions()) {\n    if (direction.dimension() == 0) {\n      CHECK(spherical_shell.external_boundaries().contains(direction));\n      CHECK_FALSE(spherical_shell.internal_boundaries().contains(direction));\n      CHECK_FALSE(spherical_shell.neighbors().contains(direction));\n      CHECK(spherical_shell.face_types().at(direction) ==\n            domain::FaceType::External);\n    } else {\n      CHECK_FALSE(spherical_shell.external_boundaries().contains(direction));\n      CHECK_FALSE(spherical_shell.internal_boundaries().contains(direction));\n      CHECK_FALSE(spherical_shell.neighbors().contains(direction));\n      CHECK(spherical_shell.face_types().at(direction) ==\n            domain::FaceType::Topological);\n    }\n  }\n}\n\nvoid check_assert() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const Neighbors<1> element_neighbors(\n            std::unordered_set<ElementId<1>>{ElementId<1>{3}},\n            OrientationMap<1>::create_aligned());\n        const DirectionMap<1, Neighbors<1>> neighbors{\n            {Direction<1>::lower_xi(), element_neighbors}};\n        const Element<1> loop(ElementId<1>{2}, neighbors,\n                              std::array{domain::Topology::S1});\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot specify a neighbor in a direction with no boundary\"));\n#endif\n}\n\nvoid test_nonconforming_blocks() {\n  const OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n  std::vector<Block<2>> blocks;\n  blocks.emplace_back(\n      nullptr, 0,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::upper_xi(),\n           BlockNeighbors<2>{\n               {1, 2, 3, 4},\n               {{1, aligned}, {2, aligned}, {3, aligned}, {4, aligned}},\n               false}}},\n      \"Annulus\", std::array{domain::Topology::I1, domain::Topology::S1});\n  blocks.emplace_back(\n      nullptr, 1,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::lower_xi(),\n           BlockNeighbors<2>{{0}, {{0, aligned}}, false}},\n          {Direction<2>::lower_eta(), BlockNeighbors<2>{2, aligned}},\n          {Direction<2>::upper_eta(), BlockNeighbors<2>{4, aligned}}},\n      \"North\", std::array{domain::Topology::I1, domain::Topology::I1});\n  blocks.emplace_back(\n      nullptr, 2,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::lower_xi(),\n           BlockNeighbors<2>{{0}, {{0, aligned}}, false}},\n          {Direction<2>::lower_eta(), BlockNeighbors<2>{3, aligned}},\n          {Direction<2>::upper_eta(), BlockNeighbors<2>{1, aligned}}},\n      \"East\", std::array{domain::Topology::I1, domain::Topology::I1});\n  blocks.emplace_back(\n      nullptr, 3,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::lower_xi(),\n           BlockNeighbors<2>{{0}, {{0, aligned}}, false}},\n          {Direction<2>::lower_eta(), BlockNeighbors<2>{4, aligned}},\n          {Direction<2>::upper_eta(), BlockNeighbors<2>{2, aligned}}},\n      \"South\", std::array{domain::Topology::I1, domain::Topology::I1});\n  blocks.emplace_back(\n      nullptr, 4,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::lower_xi(),\n           BlockNeighbors<2>{{0}, {{0, aligned}}, false}},\n          {Direction<2>::lower_eta(), BlockNeighbors<2>{1, aligned}},\n          {Direction<2>::upper_eta(), BlockNeighbors<2>{3, aligned}}},\n      \"West\", std::array{domain::Topology::I1, domain::Topology::I1});\n  const std::vector<std::array<size_t, 2>> initial_refinement_levels{\n      std::array{2_st, 0_st}, std::array{0_st, 1_st}, std::array{0_st, 1_st},\n      std::array{0_st, 1_st}, std::array{0_st, 1_st}};\n  const ElementId<2> annulus_id{0,\n                                std::array{SegmentId{2, 3}, SegmentId{0, 0}}};\n  const ElementId<2> wedge_id{1, std::array{SegmentId{0, 0}, SegmentId{1, 0}}};\n  const auto annulus = domain::create_initial_element(\n      annulus_id, blocks, initial_refinement_levels);\n  CHECK(annulus.number_of_neighbors() == 9);\n  CHECK_FALSE(annulus.external_boundaries().contains(Direction<2>::lower_xi()));\n  CHECK(annulus.internal_boundaries().contains(Direction<2>::lower_xi()));\n  CHECK(annulus.neighbors().contains(Direction<2>::lower_xi()));\n  CHECK(annulus.face_types().at(Direction<2>::lower_xi()) ==\n        domain::FaceType::ConformingAligned);\n  CHECK_FALSE(annulus.external_boundaries().contains(Direction<2>::upper_xi()));\n  CHECK(annulus.internal_boundaries().contains(Direction<2>::upper_xi()));\n  CHECK(annulus.neighbors().contains(Direction<2>::upper_xi()));\n  CHECK(annulus.face_types().at(Direction<2>::upper_xi()) ==\n        domain::FaceType::MultipleNonconforming);\n  CHECK_FALSE(\n      annulus.external_boundaries().contains(Direction<2>::lower_eta()));\n  CHECK_FALSE(\n      annulus.internal_boundaries().contains(Direction<2>::lower_eta()));\n  CHECK_FALSE(annulus.neighbors().contains(Direction<2>::lower_eta()));\n  CHECK(annulus.face_types().at(Direction<2>::lower_eta()) ==\n        domain::FaceType::Topological);\n  CHECK_FALSE(\n      annulus.external_boundaries().contains(Direction<2>::upper_eta()));\n  CHECK_FALSE(\n      annulus.internal_boundaries().contains(Direction<2>::upper_eta()));\n  CHECK_FALSE(annulus.neighbors().contains(Direction<2>::upper_eta()));\n  CHECK(annulus.face_types().at(Direction<2>::upper_eta()) ==\n        domain::FaceType::Topological);\n\n  const auto wedge = domain::create_initial_element(wedge_id, blocks,\n                                                    initial_refinement_levels);\n  CHECK(wedge.number_of_neighbors() == 3);\n  CHECK_FALSE(wedge.external_boundaries().contains(Direction<2>::lower_xi()));\n  CHECK(wedge.internal_boundaries().contains(Direction<2>::lower_xi()));\n  CHECK(wedge.neighbors().contains(Direction<2>::lower_xi()));\n  CHECK(wedge.face_types().at(Direction<2>::lower_xi()) ==\n        domain::FaceType::SingleNonconforming);\n  CHECK(wedge.external_boundaries().contains(Direction<2>::upper_xi()));\n  CHECK_FALSE(wedge.internal_boundaries().contains(Direction<2>::upper_xi()));\n  CHECK_FALSE(wedge.neighbors().contains(Direction<2>::upper_xi()));\n  CHECK(wedge.face_types().at(Direction<2>::upper_xi()) ==\n        domain::FaceType::External);\n  CHECK_FALSE(wedge.external_boundaries().contains(Direction<2>::lower_eta()));\n  CHECK(wedge.internal_boundaries().contains(Direction<2>::lower_eta()));\n  CHECK(wedge.neighbors().contains(Direction<2>::lower_eta()));\n  CHECK(wedge.face_types().at(Direction<2>::lower_eta()) ==\n        domain::FaceType::ConformingAligned);\n  CHECK_FALSE(wedge.external_boundaries().contains(Direction<2>::upper_eta()));\n  CHECK(wedge.internal_boundaries().contains(Direction<2>::upper_eta()));\n  CHECK(wedge.neighbors().contains(Direction<2>::upper_eta()));\n  CHECK(wedge.face_types().at(Direction<2>::upper_eta()) ==\n        domain::FaceType::ConformingAligned);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.Element\", \"[Domain][Unit]\") {\n  check_element_1d();\n  check_element_2d();\n  check_element_3d();\n  check_spherical_shell();\n  check_assert();\n  test_nonconforming_blocks();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_ElementId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstring>\n#include <functional>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstruct DirectionTester : public ElementId<Dim> {\n  DirectionTester(const Direction<Dim>& direction, const ElementId<Dim>& id)\n      : ElementId<Dim>(direction, id) {}\n};\n\ntemplate <size_t VolumeDim>\nvoid test_placement_new_and_hashing_impl(\n    const size_t block1, const std::array<SegmentId, VolumeDim>& segments1,\n    const size_t grid1, const size_t block2,\n    const std::array<SegmentId, VolumeDim>& segments2, const size_t grid2) {\n  using Hash = std::hash<ElementId<VolumeDim>>;\n\n  const ElementId<VolumeDim> id1(block1, segments1, grid1);\n  const ElementId<VolumeDim> id2(block2, segments2, grid2);\n\n  ElementId<VolumeDim> test_id1{};\n  ElementId<VolumeDim> test_id2{};\n  // Check for nondeterminacy due to previous memory state.\n  std::memset(&test_id1, 0, sizeof(test_id1));\n#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 7)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wclass-memaccess\"\n#endif  // defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 7)\n  std::memset(&test_id2, 255, sizeof(test_id2));\n#if defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 7)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) && !defined(__clang__) && (__GNUC__ > 7)\n  new (&test_id1) ElementId<VolumeDim>(id1);\n  new (&test_id2) ElementId<VolumeDim>(id2);\n\n  CHECK((test_id1 == test_id2) == (id1 == id2));\n  CHECK((test_id1 != test_id2) == (id1 != id2));\n  CHECK((Hash{}(test_id1) == Hash{}(test_id2)) == (id1 == id2));\n\n  for (const auto& dir : Direction<VolumeDim>::all_directions()) {\n    const DirectionTester<VolumeDim> dir_test_id1{dir, test_id1};\n    CHECK(static_cast<const ElementId<VolumeDim>&>(dir_test_id1) == test_id1);\n    CHECK(Hash{}(dir_test_id1) == Hash{}(test_id1));\n  }\n}\n\nvoid test_placement_new_and_hashing() {\n  const std::array<size_t, 3> blocks{{0, 1, 4}};\n  const std::array<SegmentId, 4> segments{{{0, 0}, {1, 0}, {1, 1}, {8, 4}}};\n  const std::array<size_t, 3> grids{{0, 1, 3}};\n\n  for (const auto& block1 : blocks) {\n    for (const auto& block2 : blocks) {\n      for (const auto& grid1 : grids) {\n        for (const auto& grid2 : grids) {\n          for (const auto& segment10 : segments) {\n            for (const auto& segment20 : segments) {\n              test_placement_new_and_hashing_impl<1>(\n                  block1, {{segment10}}, grid1, block2, {{segment20}}, grid2);\n              for (const auto& segment11 : segments) {\n                for (const auto& segment21 : segments) {\n                  test_placement_new_and_hashing_impl<2>(\n                      block1, {{segment10, segment11}}, grid1, block2,\n                      {{segment20, segment21}}, grid2);\n                  for (const auto& segment12 : segments) {\n                    for (const auto& segment22 : segments) {\n                      test_placement_new_and_hashing_impl<3>(\n                          block1, {{segment10, segment11, segment12}}, grid1,\n                          block2, {{segment20, segment21, segment22}}, grid2);\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_element_id() {\n  // Test retrieval functions:\n  auto segment_ids = std::array<SegmentId, 3>(\n      {{SegmentId(2, 3), SegmentId(1, 0), SegmentId(1, 1)}});\n  ElementId<3> block_2_3d(2, segment_ids);\n  CHECK(block_2_3d.block_id() == 2);\n  CHECK(block_2_3d.segment_ids() == segment_ids);\n  CHECK(block_2_3d.segment_id(0) == segment_ids[0]);\n  CHECK(block_2_3d.segment_id(1) == segment_ids[1]);\n  CHECK(block_2_3d.segment_id(2) == segment_ids[2]);\n  CHECK(block_2_3d.grid_index() == 0);\n  CHECK(block_2_3d.refinement_levels() == std::array{2_st, 1_st, 1_st});\n  CHECK(ElementId<2>{31, {{{6, 3}, {9, 67}}}}.refinement_levels() ==\n        std::array{6_st, 9_st});\n  CHECK(ElementId<1>{4, {{{4, 7}}}}.refinement_levels() == std::array{4_st});\n\n  // Test parent and child operations:\n  const auto check_parent_and_child = [](const ElementId<3>& id) {\n    for (size_t dim = 0; dim < 3; dim++) {\n      CHECK(id == id.id_of_child(dim, Side::Lower).id_of_parent(dim));\n      CHECK(id == id.id_of_child(dim, Side::Upper).id_of_parent(dim));\n      if (0 == id.segment_id(dim).index() % 2) {\n        CHECK(id == id.id_of_parent(dim).id_of_child(dim, Side::Lower));\n      } else {\n        CHECK(id == id.id_of_parent(dim).id_of_child(dim, Side::Upper));\n      }\n    }\n  };\n  check_parent_and_child(block_2_3d);\n  check_parent_and_child({3, segment_ids, 4});\n\n  // Test equality operator:\n  ElementId<3> element_one(1);\n  ElementId<3> element_two(1);\n  ElementId<3> element_three(2);\n  ElementId<3> element_four(4);\n  ElementId<3> element_five(4, 0);\n  ElementId<3> element_six(4, 1);\n  CHECK(element_one == element_two);\n  CHECK(element_two != element_three);\n  CHECK(element_two != element_four);\n  CHECK(element_three != block_2_3d);\n  CHECK(element_five == element_four);\n  CHECK(element_six != element_four);\n\n  // Test comparison operator\n  check_cmp(ElementId<1>{1, {{{1, 0}}}, 0}, ElementId<1>{1, {{{1, 1}}}, 0});\n  check_cmp(ElementId<1>{0, {{{1, 0}}}, 0}, ElementId<1>{1, {{{1, 0}}}, 0});\n  check_cmp(ElementId<1>{1, {{{0, 0}}}, 0}, ElementId<1>{1, {{{1, 0}}}, 0});\n  check_cmp(ElementId<1>{1, {{{1, 0}}}, 0}, ElementId<1>{1, {{{1, 0}}}, 1});\n  check_cmp(ElementId<2>{1, {{{1, 0}, {2, 1}}}, 0},\n            ElementId<2>{1, {{{1, 0}, {2, 2}}}, 0});\n  check_cmp(ElementId<2>{1, {{{1, 0}, {2, 1}}}, 0},\n            ElementId<2>{1, {{{1, 1}, {2, 1}}}, 0});\n  check_cmp(ElementId<2>{1, {{{1, 0}, {2, 1}}}, 0},\n            ElementId<2>{1, {{{1, 1}, {1, 1}}}, 0});\n  check_cmp(ElementId<2>{1, {{{0, 0}, {2, 1}}}, 0},\n            ElementId<2>{1, {{{1, 0}, {1, 1}}}, 0});\n  check_cmp(ElementId<2>{1, {{{1, 1}, {1, 1}}}, 0},\n            ElementId<2>{1, {{{1, 1}, {2, 1}}}, 0});\n  check_cmp(ElementId<2>{0, {{{1, 0}, {2, 1}}}, 0},\n            ElementId<2>{1, {{{1, 0}, {2, 1}}}, 0});\n  check_cmp(ElementId<2>{1, {{{1, 0}, {2, 1}}}, 0},\n            ElementId<2>{1, {{{1, 0}, {2, 1}}}, 1});\n  check_cmp(ElementId<3>{1, {{{1, 0}, {2, 1}, {1, 0}}}, 0},\n            ElementId<3>{1, {{{1, 0}, {2, 2}, {1, 0}}}, 0});\n  check_cmp(ElementId<3>{1, {{{1, 0}, {2, 1}, {1, 0}}}, 0},\n            ElementId<3>{1, {{{1, 1}, {2, 1}, {1, 0}}}, 0});\n  check_cmp(ElementId<3>{1, {{{1, 0}, {2, 1}, {1, 0}}}, 0},\n            ElementId<3>{1, {{{1, 0}, {2, 1}, {1, 1}}}, 0});\n  check_cmp(ElementId<3>{0, {{{1, 0}, {2, 1}, {1, 0}}}, 0},\n            ElementId<3>{1, {{{1, 0}, {2, 1}, {1, 0}}}, 0});\n  check_cmp(ElementId<3>{1, {{{1, 0}, {2, 1}, {1, 0}}}, 0},\n            ElementId<3>{1, {{{1, 0}, {2, 1}, {1, 0}}}, 1});\n\n  // Test pup operations:\n  test_serialization(element_one);\n  test_serialization(element_six);\n\n  // Test output operator:\n  CHECK(get_output(block_2_3d) == \"[B2,(L2I3,L1I0,L1I1)]\");\n  CHECK(get_output(element_six) == \"[B4,(L0I0,L0I0,L0I0),G1]\");\n  CHECK(ElementId<3>{\"[B2,(L2I3,L1I0,L1I1)]\"} == block_2_3d);\n  CHECK(ElementId<3>{\"[B4,(L0I0,L0I0,L0I0),G1]\"} == element_six);\n  CHECK(ElementId<1>{\"[B1,(L2I3)]\"} == ElementId<1>{1, {{{2, 3}}}});\n  CHECK(ElementId<1>{\"[B1,(L2I3),G2]\"} == ElementId<1>{1, {{{2, 3}}}, 2});\n  CHECK(ElementId<1>{\"[B10,(L2I3)]\"} == ElementId<1>{10, {{{2, 3}}}});\n  CHECK(ElementId<2>{\"[B2,(L1I1,L2I0)]\"} ==\n        ElementId<2>{2, {{{1, 1}, {2, 0}}}});\n  CHECK(ElementId<2>{\"[B2,(L1I1,L2I0),G12]\"} ==\n        ElementId<2>{2, {{{1, 1}, {2, 0}}}, 12});\n  CHECK(ElementId<2>{\"[B52,(L12I133,L6I38)]\"} ==\n        ElementId<2>{52, {{{12, 133}, {6, 38}}}});\n  CHECK_THROWS_WITH(ElementId<1>(\"somegrid\"),\n                    Catch::Matchers::ContainsSubstring(\"Invalid grid name\"));\n  CHECK_THROWS_WITH(ElementId<2>(\"[B0,(L1I0)]\"),\n                    Catch::Matchers::ContainsSubstring(\"Invalid grid name\"));\n  CHECK_THROWS_WITH(ElementId<2>(\"[B0]\"),\n                    Catch::Matchers::ContainsSubstring(\"Invalid grid name\"));\n  CHECK_THROWS_WITH(ElementId<3>(\"L1I0,L2I1,L2I0\"),\n                    Catch::Matchers::ContainsSubstring(\"Invalid grid name\"));\n\n  CHECK(ElementId<3>::external_boundary_id().block_id() ==\n        two_to_the(ElementId<3>::block_id_bits) - 1);\n  CHECK(ElementId<3>::external_boundary_id().grid_index() == 0);\n\n  const ElementId<3> element1(0);\n  const ElementId<3> element2{0, 1};\n  const ElementId<3> element3(1);\n  const ElementId<3> element4{1, 1};\n  const ElementId<3> element5{0, {{{1, 0}, {2, 0}, {1, 0}}}};\n  const ElementId<3> element6{0, {{{1, 0}, {2, 0}, {1, 0}}}, 1};\n  const ElementId<3> element7{0, {{{1, 0}, {2, 1}, {1, 0}}}};\n  const ElementId<3> element8{0, {{{1, 0}, {2, 1}, {1, 0}}}, 1};\n  const ElementId<3> element9{1, {{{1, 0}, {2, 0}, {1, 0}}}};\n  const ElementId<3> element10{1, {{{1, 0}, {2, 0}, {1, 0}}}, 1};\n  const ElementId<3> element11{1, {{{1, 0}, {2, 1}, {1, 0}}}};\n  const ElementId<3> element12{1, {{{1, 0}, {2, 1}, {1, 0}}}, 1};\n\n  CHECK(is_zeroth_element(element1));\n  CHECK_FALSE(is_zeroth_element(element1, {1}));\n  CHECK(is_zeroth_element(element2));\n  CHECK_FALSE(is_zeroth_element(element2, {0}));\n  CHECK(is_zeroth_element(element2, {1}));\n  CHECK(is_zeroth_element(element5));\n  CHECK_FALSE(is_zeroth_element(element5, {1}));\n  CHECK(is_zeroth_element(element6));\n  CHECK_FALSE(is_zeroth_element(element6, {0}));\n  CHECK(is_zeroth_element(element6, {1}));\n  // Do this just so we don't duplicate the same two checks over and over for\n  // all elements that aren't the zeroth element\n  const std::vector<ElementId<3>> not_zeroth_elements{\n      {element3, element4, element7, element8, element9, element10, element11,\n       element12}};\n  for (const auto& element : not_zeroth_elements) {\n    CHECK_FALSE(is_zeroth_element(element));\n    CHECK_FALSE(is_zeroth_element(element, {1}));\n  }\n\n  CHECK(ElementId<1>{0, {{{0, 0}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<1>{0, {{{1, 0}}}}.number_of_block_boundaries() == 1);\n  CHECK(ElementId<1>{0, {{{1, 1}}}}.number_of_block_boundaries() == 1);\n  CHECK(ElementId<1>{0, {{{2, 0}}}}.number_of_block_boundaries() == 1);\n  CHECK(ElementId<1>{0, {{{2, 1}}}}.number_of_block_boundaries() == 0);\n  CHECK(ElementId<1>{0, {{{2, 3}}}}.number_of_block_boundaries() == 1);\n\n  CHECK(ElementId<2>{0, {{{0, 0}, {0, 0}}}}.number_of_block_boundaries() == 4);\n  CHECK(ElementId<2>{0, {{{1, 0}, {0, 0}}}}.number_of_block_boundaries() == 3);\n  CHECK(ElementId<2>{0, {{{0, 0}, {1, 0}}}}.number_of_block_boundaries() == 3);\n  CHECK(ElementId<2>{0, {{{1, 0}, {1, 0}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{1, 1}, {1, 0}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{1, 0}, {1, 1}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{2, 0}, {1, 0}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{2, 3}, {1, 0}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{1, 0}, {2, 0}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{1, 0}, {2, 3}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{2, 1}, {1, 0}}}}.number_of_block_boundaries() == 1);\n  CHECK(ElementId<2>{0, {{{1, 0}, {2, 2}}}}.number_of_block_boundaries() == 1);\n  CHECK(ElementId<2>{0, {{{2, 0}, {2, 0}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{2, 0}, {2, 2}}}}.number_of_block_boundaries() == 1);\n  CHECK(ElementId<2>{0, {{{2, 2}, {2, 0}}}}.number_of_block_boundaries() == 1);\n  CHECK(ElementId<2>{0, {{{2, 3}, {2, 0}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{2, 0}, {2, 3}}}}.number_of_block_boundaries() == 2);\n  CHECK(ElementId<2>{0, {{{2, 3}, {2, 3}}}}.number_of_block_boundaries() == 2);\n\n  CHECK(ElementId<3>{0, {{{0, 0}, {0, 0}, {0, 0}}}}\n            .number_of_block_boundaries() == 6);\n  CHECK(ElementId<3>{0, {{{1, 0}, {0, 0}, {0, 0}}}}\n            .number_of_block_boundaries() == 5);\n  CHECK(ElementId<3>{0, {{{0, 0}, {1, 0}, {0, 0}}}}\n            .number_of_block_boundaries() == 5);\n  CHECK(ElementId<3>{0, {{{0, 0}, {0, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 5);\n  CHECK(ElementId<3>{0, {{{1, 1}, {0, 0}, {0, 0}}}}\n            .number_of_block_boundaries() == 5);\n  CHECK(ElementId<3>{0, {{{0, 0}, {1, 1}, {0, 0}}}}\n            .number_of_block_boundaries() == 5);\n  CHECK(ElementId<3>{0, {{{0, 0}, {0, 0}, {1, 1}}}}\n            .number_of_block_boundaries() == 5);\n  CHECK(ElementId<3>{0, {{{1, 0}, {1, 0}, {0, 0}}}}\n            .number_of_block_boundaries() == 4);\n  CHECK(ElementId<3>{0, {{{1, 0}, {0, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 4);\n  CHECK(ElementId<3>{0, {{{1, 0}, {1, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{1, 1}, {1, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{1, 0}, {1, 1}, {1, 0}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{1, 0}, {1, 0}, {1, 1}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{2, 0}, {1, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{1, 0}, {2, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{1, 0}, {1, 0}, {2, 0}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{2, 3}, {1, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{1, 0}, {2, 3}, {1, 0}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{1, 0}, {1, 0}, {2, 3}}}}\n            .number_of_block_boundaries() == 3);\n  CHECK(ElementId<3>{0, {{{2, 1}, {1, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{1, 0}, {2, 1}, {1, 0}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{1, 0}, {1, 0}, {2, 1}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{2, 1}, {2, 0}, {1, 0}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{2, 1}, {1, 0}, {2, 0}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{2, 0}, {2, 1}, {1, 0}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{1, 0}, {2, 1}, {2, 0}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{2, 0}, {1, 0}, {2, 1}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{1, 0}, {2, 0}, {2, 1}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{2, 0}, {2, 0}, {2, 1}}}}\n            .number_of_block_boundaries() == 2);\n  CHECK(ElementId<3>{0, {{{2, 1}, {2, 0}, {2, 1}}}}\n            .number_of_block_boundaries() == 1);\n  CHECK(ElementId<3>{0, {{{2, 0}, {2, 1}, {2, 1}}}}\n            .number_of_block_boundaries() == 1);\n  CHECK(ElementId<3>{0, {{{2, 1}, {2, 1}, {2, 0}}}}\n            .number_of_block_boundaries() == 1);\n  CHECK(ElementId<3>{0, {{{2, 1}, {2, 1}, {2, 1}}}}\n            .number_of_block_boundaries() == 0);\n\n  CHECK(overlapping(element1, element1));\n  CHECK(overlapping(element1, element2));\n  CHECK(not overlapping(element1, element3));\n  CHECK(overlapping(element1, element5));\n  CHECK(not overlapping(element5, element7));\n}\n\ntemplate <size_t VolumeDim>\nvoid test_serialization() {\n  constexpr size_t volume_dim = VolumeDim;\n\n  // Generate random element IDs so we test the full range of possible values\n  MAKE_GENERATOR(gen);\n  std::uniform_int_distribution<size_t> dist_block_id(\n      0, two_to_the(ElementId<VolumeDim>::block_id_bits) - 1);\n  std::uniform_int_distribution<size_t> dist_grid_index(\n      0, two_to_the(ElementId<VolumeDim>::grid_index_bits) - 1);\n  std::uniform_int_distribution<size_t> dist_refinement(\n      0, ElementId<VolumeDim>::max_refinement_level);\n  const auto random_segment_id = [&gen, &dist_refinement]() -> SegmentId {\n    const size_t refinement = dist_refinement(gen);\n    std::uniform_int_distribution<size_t> dist_index(\n        0, two_to_the(refinement) - 1);\n    return {refinement, dist_index(gen)};\n  };\n  const auto random_segment_ids =\n      [&random_segment_id]() -> std::array<SegmentId, VolumeDim> {\n    if constexpr (VolumeDim == 1) {\n      return {{random_segment_id()}};\n    } else if constexpr (VolumeDim == 2) {\n      return {{random_segment_id(), random_segment_id()}};\n    } else {\n      return {{random_segment_id(), random_segment_id(), random_segment_id()}};\n    }\n  };\n\n  const ElementId<volume_dim> unused_id(0);\n  CHECK(size_of_object_in_bytes(unused_id) == 8);\n  for (size_t i = 0; i < 100; ++i) {\n    ElementId<volume_dim> element_id{dist_block_id(gen), random_segment_ids(),\n                                     dist_grid_index(gen)};\n    CAPTURE(element_id);\n\n    // Test serialization\n    const auto serialized_id = serialize_and_deserialize(element_id);\n    CHECK(serialized_id == element_id);\n\n    // The following checks that ElementId can be used as a Charm array\n    // index\n    Parallel::ArrayIndex<ElementId<volume_dim>> array_index(element_id);\n    CHECK(element_id == array_index.get_index());\n    // now check pupping the ArrayIndex works...\n    const auto serialized_array_index =\n        serialize<Parallel::ArrayIndex<ElementId<volume_dim>>>(array_index);\n    PUP::fromMem reader(serialized_array_index.data());\n    Parallel::ArrayIndex<ElementId<volume_dim>> deserialized_array_index(\n        unused_id);\n    reader | deserialized_array_index;\n    CHECK(array_index == deserialized_array_index);\n    CHECK(element_id == deserialized_array_index.get_index());\n\n    // Check roundtrip to string representation and back\n    CHECK(ElementId<VolumeDim>(get_output(element_id)) == element_id);\n  }\n}\nvoid test_to_short_id() {\n  // Same segments, different block_id → same short_id\n  const ElementId<3> elem_b0{0, {{{2, 3}, {1, 0}, {1, 1}}}};\n  const ElementId<3> elem_b5{5, {{{2, 3}, {1, 0}, {1, 1}}}};\n  CHECK(elem_b0.to_short_id() == elem_b5.to_short_id());\n\n  // Same segments, different grid_index → same short_id\n  const ElementId<3> elem_g0{0, {{{2, 3}, {1, 0}, {1, 1}}}, 0};\n  const ElementId<3> elem_g3{0, {{{2, 3}, {1, 0}, {1, 1}}}, 3};\n  CHECK(elem_g0.to_short_id() == elem_g3.to_short_id());\n\n  // Different segments → different short_id\n  const ElementId<3> elem_a{0, {{{2, 3}, {1, 0}, {1, 1}}}};\n  const ElementId<3> elem_b{0, {{{2, 2}, {1, 0}, {1, 1}}}};\n  CHECK(elem_a.to_short_id() != elem_b.to_short_id());\n\n  // Verify value matches expected bit-shift\n  // The raw 64-bit representation shifted right by 16 should equal to_short_id\n  uint64_t raw = 0;\n  std::memcpy(&raw, &elem_b0, sizeof(raw));\n  CHECK(elem_b0.to_short_id() == (raw >> 16));\n\n  // Also test 1D and 2D\n  const ElementId<1> elem_1d_a{0, {{{3, 5}}}};\n  const ElementId<1> elem_1d_b{7, {{{3, 5}}}};\n  CHECK(elem_1d_a.to_short_id() == elem_1d_b.to_short_id());\n\n  const ElementId<2> elem_2d_a{0, {{{2, 1}, {3, 5}}}};\n  const ElementId<2> elem_2d_b{3, {{{2, 1}, {3, 5}}}, 2};\n  CHECK(elem_2d_a.to_short_id() == elem_2d_b.to_short_id());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.ElementId\", \"[Domain][Unit]\") {\n  test_element_id();\n  test_placement_new_and_hashing();\n  test_serialization<1>();\n  test_serialization<2>();\n  test_serialization<3>();\n  test_to_short_id();\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ElementId<1>(two_to_the(ElementId<1>::block_id_bits)),\n      Catch::Matchers::ContainsSubstring(\"Block id out of bounds\"));\n  CHECK_THROWS_WITH(\n      ElementId<1>(0, {{{0, 0}}}, two_to_the(ElementId<1>::grid_index_bits)),\n      Catch::Matchers::ContainsSubstring(\"Grid index out of bounds\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_FaceType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/Structure/FaceType.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.FaceType\", \"[Domain][Unit]\") {\n  CHECK(get_output(domain::FaceType::Uninitialized) == \"Uninitialized\");\n  CHECK(get_output(domain::FaceType::External) == \"External\");\n  CHECK(get_output(domain::FaceType::Topological) == \"Topological\");\n  CHECK(get_output(domain::FaceType::ConformingAligned) == \"ConformingAligned\");\n  CHECK(get_output(domain::FaceType::ConformingUnaligned) ==\n        \"ConformingUnaligned\");\n  CHECK(get_output(domain::FaceType::SingleNonconforming) ==\n        \"SingleNonconforming\");\n  CHECK(get_output(domain::FaceType::MultipleNonconforming) ==\n        \"MultipleNonconforming\");\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_HasBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/Structure/HasBoundary.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n\nnamespace {\nvoid test() {\n  for (const auto side : std::array{Side::Lower, Side::Upper}) {\n    CHECK(domain::has_boundary(domain::Topology::I1, side));\n    CHECK_FALSE(domain::has_boundary(domain::Topology::S1, side));\n    CHECK_FALSE(domain::has_boundary(domain::Topology::S2Colatitude, side));\n    CHECK_FALSE(domain::has_boundary(domain::Topology::S2Longitude, side));\n    CHECK_FALSE(domain::has_boundary(domain::Topology::B2Angular, side));\n    CHECK_FALSE(domain::has_boundary(domain::Topology::B3Colatitude, side));\n    CHECK_FALSE(domain::has_boundary(domain::Topology::B3Longitude, side));\n    CHECK_FALSE(domain::has_boundary(domain::Topology::CartoonSphere, side));\n    CHECK_FALSE(domain::has_boundary(domain::Topology::CartoonCylinder, side));\n  }\n  CHECK_FALSE(domain::has_boundary(domain::Topology::B2Radial, Side::Lower));\n  CHECK(domain::has_boundary(domain::Topology::B2Radial, Side::Upper));\n  CHECK_FALSE(domain::has_boundary(domain::Topology::B3Radial, Side::Lower));\n  CHECK(domain::has_boundary(domain::Topology::B3Radial, Side::Upper));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.HasBoundary\", \"[Domain][Unit]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_Hypercube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"Domain/Structure/Hypercube.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <size_t HypercubeDim, size_t ElementDim, size_t ExpectedSize>\nvoid test_hypercube_iterator(\n    std::array<HypercubeElement<ElementDim, HypercubeDim>, ExpectedSize>\n        expected_elements) {\n  CAPTURE(ElementDim);\n  CAPTURE(HypercubeDim);\n  HypercubeElementsIterator<ElementDim, HypercubeDim> elements_iterator{};\n  static_assert(ExpectedSize > 0);\n  static_assert(elements_iterator.size() == ExpectedSize);\n  CHECK(elements_iterator ==\n        HypercubeElementsIterator<ElementDim, HypercubeDim>::begin());\n  CHECK(*elements_iterator ==\n        *HypercubeElementsIterator<ElementDim, HypercubeDim>::begin());\n  CHECK(elements_iterator !=\n        HypercubeElementsIterator<ElementDim, HypercubeDim>::end());\n  size_t i = 0;\n  for (const auto element : elements_iterator) {\n    CAPTURE(i);\n    CAPTURE(element);\n    CHECK(element == gsl::at(expected_elements, i));\n    ++i;\n  }\n  CHECK(i == ExpectedSize);\n  // Test postfix operator\n  elements_iterator =\n      HypercubeElementsIterator<ElementDim, HypercubeDim>::begin();\n  const auto previous_iterator = elements_iterator++;\n  CHECK(previous_iterator ==\n        HypercubeElementsIterator<ElementDim, HypercubeDim>::begin());\n  CHECK(elements_iterator ==\n        ++HypercubeElementsIterator<ElementDim, HypercubeDim>::begin());\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.Hypercube\", \"[Domain][Unit]\") {\n  {\n    const Vertex<0> point{};\n    CHECK(get_output(point) == \"Vertex0D[(),()]\");\n    CHECK(point.dimensions_in_parent() == std::array<size_t, 0>{});\n    CHECK(point.index() == std::array<Side, 0>{});\n    CHECK(point == Vertex<0>{});\n    const Vertex<1> left{Side::Lower};\n    CHECK(get_output(left) == \"Vertex1D[(),(Lower)]\");\n    CHECK(left.index() == std::array<Side, 1>{{Side::Lower}});\n    CHECK(left.side() == Side::Lower);\n    CHECK(left == Vertex<1>{Side::Lower});\n    CHECK(left != Vertex<1>{Side::Upper});\n    const Vertex<2> lower_right{Side::Lower, Side::Upper};\n    CHECK(get_output(lower_right) == \"Vertex2D[(),(Lower,Upper)]\");\n    CHECK(lower_right.index() ==\n          std::array<Side, 2>{{Side::Lower, Side::Upper}});\n    CHECK(lower_right == Vertex<2>{{{Side::Lower, Side::Upper}}});\n    CHECK(lower_right != Vertex<2>{{{Side::Upper, Side::Upper}}});\n  }\n  {\n    const Edge<2> south{0, {{Side::Lower}}};\n    CHECK(get_output(south) == \"Edge2D[(0),(Lower)]\");\n    CHECK(south.dimensions_in_parent() == std::array<size_t, 1>{{0}});\n    CHECK(south.dimension_in_parent() == 0);\n    CHECK(south.index() == std::array<Side, 1>{{Side::Lower}});\n    CHECK(south.side_in_parent_dimension(1) == Side::Lower);\n    CHECK(south.side() == Side::Lower);\n    CHECK(south == Edge<2>{0, {{Side::Lower}}});\n    CHECK(south != Edge<2>{1, {{Side::Lower}}});\n    CHECK(south != Edge<2>{0, {{Side::Upper}}});\n    const Edge<2> north{0, {{Side::Upper}}};\n    CHECK(get_output(north) == \"Edge2D[(0),(Upper)]\");\n    CHECK(north.dimensions_in_parent() == std::array<size_t, 1>{{0}});\n    CHECK(north.dimension_in_parent() == 0);\n    CHECK(north.index() == std::array<Side, 1>{{Side::Upper}});\n    CHECK(north.side_in_parent_dimension(1) == Side::Upper);\n    CHECK(north.side() == Side::Upper);\n    const Edge<2> west{1, {{Side::Lower}}};\n    CHECK(get_output(west) == \"Edge2D[(1),(Lower)]\");\n    CHECK(west.dimensions_in_parent() == std::array<size_t, 1>{{1}});\n    CHECK(west.dimension_in_parent() == 1);\n    CHECK(west.index() == std::array<Side, 1>{{Side::Lower}});\n    CHECK(west.side_in_parent_dimension(0) == Side::Lower);\n    const Edge<2> east{1, {{Side::Upper}}};\n    CHECK(get_output(east) == \"Edge2D[(1),(Upper)]\");\n    CHECK(east.dimensions_in_parent() == std::array<size_t, 1>{{1}});\n    CHECK(east.dimension_in_parent() == 1);\n    CHECK(east.index() == std::array<Side, 1>{{Side::Upper}});\n    CHECK(east.side_in_parent_dimension(0) == Side::Upper);\n    CHECK(east.side() == Side::Upper);\n  }\n  {\n    const Edge<3> top_left{1, {{Side::Lower, Side::Upper}}};\n    CHECK(get_output(top_left) == \"Edge3D[(1),(Lower,Upper)]\");\n    CHECK(top_left.dimensions_in_parent() == std::array<size_t, 1>{{1}});\n    CHECK(top_left.dimension_in_parent() == 1);\n    CHECK(top_left.index() == std::array<Side, 2>{{Side::Lower, Side::Upper}});\n    CHECK(top_left.side_in_parent_dimension(0) == Side::Lower);\n    CHECK(top_left.side_in_parent_dimension(2) == Side::Upper);\n    CHECK(top_left == Edge<3>{1, {{Side::Lower, Side::Upper}}});\n    CHECK(top_left != Edge<3>{0, {{Side::Lower, Side::Upper}}});\n    CHECK(top_left != Edge<3>{1, {{Side::Lower, Side::Lower}}});\n    const Edge<3> top_front{0, {{Side::Lower, Side::Upper}}};\n    CHECK(get_output(top_front) == \"Edge3D[(0),(Lower,Upper)]\");\n    CHECK(top_front.dimensions_in_parent() == std::array<size_t, 1>{{0}});\n    CHECK(top_front.dimension_in_parent() == 0);\n    CHECK(top_front.index() == std::array<Side, 2>{{Side::Lower, Side::Upper}});\n    CHECK(top_front.side_in_parent_dimension(1) == Side::Lower);\n    CHECK(top_front.side_in_parent_dimension(2) == Side::Upper);\n    const Edge<3> front_left{2, {{Side::Lower, Side::Upper}}};\n    CHECK(get_output(front_left) == \"Edge3D[(2),(Lower,Upper)]\");\n    CHECK(front_left.dimensions_in_parent() == std::array<size_t, 1>{{2}});\n    CHECK(front_left.dimension_in_parent() == 2);\n    CHECK(front_left.index() ==\n          std::array<Side, 2>{{Side::Lower, Side::Upper}});\n    CHECK(front_left.side_in_parent_dimension(0) == Side::Lower);\n    CHECK(front_left.side_in_parent_dimension(1) == Side::Upper);\n  }\n  {\n    const Face<3> top{{{0, 1}}, {{Side::Upper}}};\n    CHECK(get_output(top) == \"Face3D[(0,1),(Upper)]\");\n    CHECK(top.dimensions_in_parent() == std::array<size_t, 2>{{0, 1}});\n    CHECK(top.index() == std::array<Side, 1>{{Side::Upper}});\n    CHECK(top.side_in_parent_dimension(2) == Side::Upper);\n    CHECK(top.side() == Side::Upper);\n    CHECK(top == Face<3>{{{0, 1}}, {{Side::Upper}}});\n    CHECK(top != Face<3>{{{0, 2}}, {{Side::Upper}}});\n    CHECK(top != Face<3>{{{0, 1}}, {{Side::Lower}}});\n    const Face<3> top_rotated{{{1, 0}}, {{Side::Upper}}};\n    CHECK(top_rotated == top);\n    const Face<3> left{{{1, 2}}, {{Side::Lower}}};\n    CHECK(get_output(left) == \"Face3D[(1,2),(Lower)]\");\n    CHECK(left.dimensions_in_parent() == std::array<size_t, 2>{{1, 2}});\n    CHECK(left.index() == std::array<Side, 1>{{Side::Lower}});\n    CHECK(left.side_in_parent_dimension(0) == Side::Lower);\n    CHECK(left.side() == Side::Lower);\n    const Face<3> left_rotated{{{2, 1}}, {{Side::Lower}}};\n    CHECK(left_rotated == left);\n    const Face<3> front{{{0, 2}}, {{Side::Lower}}};\n    CHECK(get_output(front) == \"Face3D[(0,2),(Lower)]\");\n    CHECK(front.dimensions_in_parent() == std::array<size_t, 2>{{0, 2}});\n    CHECK(front.index() == std::array<Side, 1>{{Side::Lower}});\n    CHECK(front.side_in_parent_dimension(1) == Side::Lower);\n    CHECK(front.side() == Side::Lower);\n    const Face<3> front_rotated{{{2, 0}}, {{Side::Lower}}};\n    CHECK(front_rotated == front);\n  }\n  {\n    const Cell<3> cube{};\n    CHECK(get_output(cube) == \"Cell3D[(0,1,2),()]\");\n    CHECK(cube.dimensions_in_parent() == std::array<size_t, 3>{{0, 1, 2}});\n    CHECK(cube.index() == std::array<Side, 0>{});\n    const Cell<3> cube_rotated{{{1, 2, 0}}, {}};\n    CHECK(cube_rotated == cube);\n  }\n  {\n    INFO(\"Hypercube iterator\");\n    // 0D: .\n    // -> 1 vertex\n    test_hypercube_iterator<0, 0, 1>({{Vertex<0>{}}});\n    // 1D: -\n    // -> 2 vertices and 1 edge\n    test_hypercube_iterator<1, 0, 2>(\n        {{Vertex<1>{Side::Lower}, Vertex<1>{Side::Upper}}});\n    test_hypercube_iterator<1, 1, 1>({{Edge<1>{}}});\n    // 2D:\n    // +---+\n    // |   |\n    // +---+\n    // -> 4 vertices, 4 edges and 1 face\n    test_hypercube_iterator<2, 0, 4>({{Vertex<2>{Side::Lower, Side::Lower},\n                                       Vertex<2>{Side::Upper, Side::Lower},\n                                       Vertex<2>{Side::Lower, Side::Upper},\n                                       Vertex<2>{Side::Upper, Side::Upper}}});\n    test_hypercube_iterator<2, 1, 4>(\n        {{Edge<2>{0, {{Side::Lower}}}, Edge<2>{0, {{Side::Upper}}},\n          Edge<2>{1, {{Side::Lower}}}, Edge<2>{1, {{Side::Upper}}}}});\n    test_hypercube_iterator<2, 2, 1>({{Face<2>{}}});\n    // 3D:\n    //    +---+\n    //  /   / |\n    // +---+  +\n    // |   | /\n    // +---+\n    // -> 8 vertices, 12 edges, 6 faces and 1 cell\n    test_hypercube_iterator<3, 0, 8>(\n        {{Vertex<3>{Side::Lower, Side::Lower, Side::Lower},\n          Vertex<3>{Side::Upper, Side::Lower, Side::Lower},\n          Vertex<3>{Side::Lower, Side::Upper, Side::Lower},\n          Vertex<3>{Side::Upper, Side::Upper, Side::Lower},\n          Vertex<3>{Side::Lower, Side::Lower, Side::Upper},\n          Vertex<3>{Side::Upper, Side::Lower, Side::Upper},\n          Vertex<3>{Side::Lower, Side::Upper, Side::Upper},\n          Vertex<3>{Side::Upper, Side::Upper, Side::Upper}}});\n    test_hypercube_iterator<3, 1, 12>(\n        {{Edge<3>{0, {{Side::Lower, Side::Lower}}},\n          Edge<3>{0, {{Side::Upper, Side::Lower}}},\n          Edge<3>{0, {{Side::Lower, Side::Upper}}},\n          Edge<3>{0, {{Side::Upper, Side::Upper}}},\n          Edge<3>{1, {{Side::Lower, Side::Lower}}},\n          Edge<3>{1, {{Side::Upper, Side::Lower}}},\n          Edge<3>{1, {{Side::Lower, Side::Upper}}},\n          Edge<3>{1, {{Side::Upper, Side::Upper}}},\n          Edge<3>{2, {{Side::Lower, Side::Lower}}},\n          Edge<3>{2, {{Side::Upper, Side::Lower}}},\n          Edge<3>{2, {{Side::Lower, Side::Upper}}},\n          Edge<3>{2, {{Side::Upper, Side::Upper}}}}});\n    test_hypercube_iterator<3, 2, 6>({{Face<3>{{{0, 1}}, {{Side::Lower}}},\n                                       Face<3>{{{0, 1}}, {{Side::Upper}}},\n                                       Face<3>{{{0, 2}}, {{Side::Lower}}},\n                                       Face<3>{{{0, 2}}, {{Side::Upper}}},\n                                       Face<3>{{{1, 2}}, {{Side::Lower}}},\n                                       Face<3>{{{1, 2}}, {{Side::Upper}}}}});\n    test_hypercube_iterator<3, 3, 1>({{Cell<3>{}}});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_IndexToSliceAt.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.IndexToSliceAt\", \"[Domain][Unit]\") {\n  const Index<2> extents{{{2, 5}}};\n  CHECK(index_to_slice_at(extents, Direction<2>::lower_xi()) == 0);\n  CHECK(index_to_slice_at(extents, Direction<2>::lower_xi(), 1) == 1);\n  CHECK(index_to_slice_at(extents, Direction<2>::upper_xi()) == 1);\n  CHECK(index_to_slice_at(extents, Direction<2>::upper_xi(), 1) == 0);\n  CHECK(index_to_slice_at(extents, Direction<2>::lower_eta()) == 0);\n  CHECK(index_to_slice_at(extents, Direction<2>::lower_eta(), 1) == 1);\n  CHECK(index_to_slice_at(extents, Direction<2>::upper_eta()) == 4);\n  CHECK(index_to_slice_at(extents, Direction<2>::upper_eta(), 1) == 3);\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_InitialElementIds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/rational.hpp>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nvoid test_initial_element_ids(\n    const std::vector<ElementId<VolumeDim>>& element_ids,\n    const std::vector<std::array<size_t, VolumeDim>>& initial_refinement_levels,\n    size_t grid_index) {\n  size_t expected_number_of_elements = 0;\n  for (const auto& initial_refinement_levels_of_block :\n       initial_refinement_levels) {\n    size_t expected_number_of_elements_in_block = 1;\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      expected_number_of_elements_in_block *=\n          two_to_the(gsl::at(initial_refinement_levels_of_block, d));\n    }\n    expected_number_of_elements += expected_number_of_elements_in_block;\n  }\n  CHECK(expected_number_of_elements == element_ids.size());\n  const boost::rational<size_t> expected_logical_volume_of_blocks(\n      initial_refinement_levels.size());\n  boost::rational<size_t> logical_volume_of_blocks(0);\n  for (const auto& element_id : element_ids) {\n    logical_volume_of_blocks += fraction_of_block_volume(element_id);\n    CHECK(element_id.grid_index() == grid_index);\n  }\n  CHECK(expected_logical_volume_of_blocks == logical_volume_of_blocks);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.InitialElementIds\", \"[Domain][Unit]\") {\n  const std::vector<std::array<size_t, 1>> initial_refinement_levels_1d{{{2}},\n                                                                        {{3}}};\n  const auto element_ids_1d = initial_element_ids(initial_refinement_levels_1d);\n  test_initial_element_ids(element_ids_1d, initial_refinement_levels_1d, 0);\n  test_initial_element_ids(initial_element_ids(initial_refinement_levels_1d, 3),\n                           initial_refinement_levels_1d, 3);\n\n  const std::vector<std::array<size_t, 2>> initial_refinement_levels_2d{\n      {{2, 0}}, {{3, 1}}};\n  const auto element_ids_2d = initial_element_ids(initial_refinement_levels_2d);\n  test_initial_element_ids(element_ids_2d, initial_refinement_levels_2d, 0);\n  test_initial_element_ids(initial_element_ids(initial_refinement_levels_2d, 3),\n                           initial_refinement_levels_2d, 3);\n\n  const std::vector<std::array<size_t, 3>> initial_refinement_levels_3d{\n      {{4, 2, 1}}, {{0, 3, 2}}};\n  const auto element_ids_3d = initial_element_ids(initial_refinement_levels_3d);\n  test_initial_element_ids(element_ids_3d, initial_refinement_levels_3d, 0);\n  test_initial_element_ids(initial_element_ids(initial_refinement_levels_3d, 3),\n                           initial_refinement_levels_3d, 3);\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_NeighborIsConforming.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/NeighborIsConforming.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n\nnamespace {\nvoid test_1d() {\n  for (const auto& direction : Direction<1>::all_directions()) {\n    for (const auto& orientation :\n         std::array{OrientationMap<1>::create_aligned(),\n                    OrientationMap<1>(std::array{Direction<1>::lower_xi()})}) {\n      CHECK(neighbor_is_conforming(std::array{domain::Topology::I1},\n                                   std::array{domain::Topology::I1}, direction,\n                                   orientation));\n    }\n  }\n}\n\nvoid test_2d() {\n  const OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n  const OrientationMap<2> quarter_turn_ccw(std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}});\n  const OrientationMap<2> half_turn(std::array<Direction<2>, 2>{\n      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}});\n  const OrientationMap<2> quarter_turn_cw(std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}});\n\n  for (const auto& direction : Direction<2>::all_directions()) {\n    for (const auto& orientation :\n         std::array{aligned, quarter_turn_ccw, half_turn, quarter_turn_cw}) {\n      CHECK(neighbor_is_conforming(domain::topologies::hypercube<2>,\n                                   domain::topologies::hypercube<2>, direction,\n                                   orientation));\n    }\n  }\n\n  CHECK(neighbor_is_conforming(domain::topologies::disk,\n                               domain::topologies::annulus,\n                               Direction<2>::upper_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::annulus,\n                               domain::topologies::disk,\n                               Direction<2>::lower_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::annulus,\n                               domain::topologies::annulus,\n                               Direction<2>::lower_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::annulus,\n                               domain::topologies::annulus,\n                               Direction<2>::upper_xi(), aligned));\n\n  for (const auto& orientation :\n       std::array{aligned, quarter_turn_ccw, half_turn, quarter_turn_cw}) {\n    CHECK_FALSE(neighbor_is_conforming(domain::topologies::disk,\n                                       domain::topologies::hypercube<2>,\n                                       Direction<2>::upper_xi(), orientation));\n    CHECK_FALSE(neighbor_is_conforming(domain::topologies::annulus,\n                                       domain::topologies::hypercube<2>,\n                                       Direction<2>::lower_xi(), orientation));\n    CHECK_FALSE(neighbor_is_conforming(domain::topologies::annulus,\n                                       domain::topologies::hypercube<2>,\n                                       Direction<2>::upper_xi(), orientation));\n  }\n}\n\nvoid test_3d() {\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n  CHECK(neighbor_is_conforming(domain::topologies::spherical_shell,\n                               domain::topologies::spherical_shell,\n                               Direction<3>::lower_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::spherical_shell,\n                               domain::topologies::full_sphere,\n                               Direction<3>::lower_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::spherical_shell,\n                               domain::topologies::spherical_shell,\n                               Direction<3>::upper_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::full_sphere,\n                               domain::topologies::spherical_shell,\n                               Direction<3>::upper_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::full_cylinder,\n                               domain::topologies::full_cylinder,\n                               Direction<3>::lower_zeta(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::full_cylinder,\n                               domain::topologies::full_cylinder,\n                               Direction<3>::upper_zeta(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                               domain::topologies::cylindrical_shell,\n                               Direction<3>::lower_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                               domain::topologies::cylindrical_shell,\n                               Direction<3>::upper_xi(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                               domain::topologies::cylindrical_shell,\n                               Direction<3>::lower_zeta(), aligned));\n  CHECK(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                               domain::topologies::cylindrical_shell,\n                               Direction<3>::upper_zeta(), aligned));\n\n  const OrientationMap<3> radial_xi_to_zeta(std::array<Direction<3>, 3>{\n      Direction<3>::upper_zeta(), Direction<3>::self(), Direction<3>::self()});\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::spherical_shell, domain::topologies::hypercube<3>,\n      Direction<3>::lower_xi(), radial_xi_to_zeta));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::spherical_shell, domain::topologies::hypercube<3>,\n      Direction<3>::upper_xi(), radial_xi_to_zeta));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::full_sphere, domain::topologies::hypercube<3>,\n      Direction<3>::upper_xi(), radial_xi_to_zeta));\n\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::spherical_shell, domain::topologies::full_cylinder,\n      Direction<3>::lower_xi(), radial_xi_to_zeta));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::spherical_shell, domain::topologies::full_cylinder,\n      Direction<3>::upper_xi(), radial_xi_to_zeta));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::full_sphere, domain::topologies::full_cylinder,\n      Direction<3>::upper_xi(), radial_xi_to_zeta));\n\n  const OrientationMap<3> radial_aligned(std::array<Direction<3>, 3>{\n      Direction<3>::upper_xi(), Direction<3>::self(), Direction<3>::self()});\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::spherical_shell,\n                                     domain::topologies::cylindrical_shell,\n                                     Direction<3>::lower_xi(), radial_aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::spherical_shell,\n                                     domain::topologies::cylindrical_shell,\n                                     Direction<3>::upper_xi(), radial_aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::full_sphere,\n                                     domain::topologies::cylindrical_shell,\n                                     Direction<3>::upper_xi(), radial_aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::spherical_shell,\n                                     domain::topologies::cylindrical_shell,\n                                     Direction<3>::lower_xi(),\n                                     radial_xi_to_zeta));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::spherical_shell,\n                                     domain::topologies::cylindrical_shell,\n                                     Direction<3>::upper_xi(),\n                                     radial_xi_to_zeta));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::full_sphere, domain::topologies::cylindrical_shell,\n      Direction<3>::upper_xi(), radial_xi_to_zeta));\n\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::full_cylinder,\n                                     domain::topologies::hypercube<3>,\n                                     Direction<3>::upper_xi(), aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::full_cylinder,\n                                     domain::topologies::hypercube<3>,\n                                     Direction<3>::lower_zeta(), aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::full_cylinder,\n                                     domain::topologies::hypercube<3>,\n                                     Direction<3>::upper_zeta(), aligned));\n  const OrientationMap<3> radial_zeta_to_xi(std::array<Direction<3>, 3>{\n      Direction<3>::self(), Direction<3>::self(), Direction<3>::upper_xi()});\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::full_cylinder, domain::topologies::spherical_shell,\n      Direction<3>::lower_zeta(), radial_zeta_to_xi));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::full_cylinder, domain::topologies::spherical_shell,\n      Direction<3>::upper_zeta(), radial_zeta_to_xi));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::full_cylinder, domain::topologies::full_sphere,\n      Direction<3>::lower_zeta(), radial_zeta_to_xi));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::full_cylinder, domain::topologies::full_sphere,\n      Direction<3>::upper_zeta(), radial_zeta_to_xi));\n  CHECK(neighbor_is_conforming(domain::topologies::full_cylinder,\n                               domain::topologies::cylindrical_shell,\n                               Direction<3>::upper_xi(), aligned));\n  const OrientationMap<3> full_to_shell(std::array<Direction<3>, 3>{\n      Direction<3>::lower_zeta(), Direction<3>::upper_eta(),\n      Direction<3>::upper_xi()});\n  CHECK(neighbor_is_conforming(domain::topologies::full_cylinder,\n                               domain::topologies::cylindrical_shell,\n                               Direction<3>::upper_xi(), full_to_shell));\n\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::hypercube<3>,\n                                     Direction<3>::lower_xi(), aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::hypercube<3>,\n                                     Direction<3>::upper_xi(), aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::hypercube<3>,\n                                     Direction<3>::lower_zeta(), aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::hypercube<3>,\n                                     Direction<3>::upper_zeta(), aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::spherical_shell,\n                                     Direction<3>::lower_xi(), radial_aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::spherical_shell,\n                                     Direction<3>::upper_xi(), radial_aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::spherical_shell,\n                                     Direction<3>::lower_zeta(),\n                                     radial_zeta_to_xi));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::spherical_shell,\n                                     Direction<3>::upper_zeta(),\n                                     radial_zeta_to_xi));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::full_sphere,\n                                     Direction<3>::lower_xi(), radial_aligned));\n  CHECK_FALSE(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                                     domain::topologies::full_sphere,\n                                     Direction<3>::upper_xi(), radial_aligned));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::cylindrical_shell, domain::topologies::full_sphere,\n      Direction<3>::lower_zeta(), radial_zeta_to_xi));\n  CHECK_FALSE(neighbor_is_conforming(\n      domain::topologies::cylindrical_shell, domain::topologies::full_sphere,\n      Direction<3>::upper_zeta(), radial_zeta_to_xi));\n  CHECK(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                               domain::topologies::full_cylinder,\n                               Direction<3>::lower_xi(), aligned));\n  const OrientationMap<3> shell_to_full(std::array<Direction<3>, 3>{\n      Direction<3>::upper_zeta(), Direction<3>::upper_eta(),\n      Direction<3>::lower_xi()});\n  CHECK(neighbor_is_conforming(domain::topologies::cylindrical_shell,\n                               domain::topologies::full_cylinder,\n                               Direction<3>::upper_zeta(), shell_to_full));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.NeighborIsConforming\",\n                  \"[Domain][Unit]\") {\n  test_1d();\n  test_2d();\n  test_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_Neighbors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <functional>\n#include <string>\n#include <unordered_set>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace {\nvoid check_neighbors_1d() {\n  // Test default constructor, only used for Charm++ serialization so no CHECK\n  // calls:\n  Neighbors<1> test_neighbors;\n\n  // Test constructor:\n  OrientationMap<1> custom_orientation(\n      std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}});\n  const std::unordered_set<ElementId<1>> custom_id{\n      ElementId<1>(2, {{SegmentId(2, 3)}})};\n  Neighbors<1> custom_neighbors(custom_id, custom_orientation);\n\n  // Test size\n  CHECK(custom_neighbors.size() == 1);\n\n  // Test add_ids\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([&test_neighbors, &custom_id]() {\n        test_neighbors.add_ids(custom_id);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Some of the added Ids\"));\n#endif\n  Neighbors<1> empty_neighbors{{}, {{2, custom_orientation}}, true};\n  CHECK(empty_neighbors.size() == 0);\n  empty_neighbors.add_ids(custom_id);\n  CHECK(empty_neighbors.size() == 1);\n\n  // Test set_ids_to:\n  const std::unordered_set<ElementId<1>> other_custom_id{\n      ElementId<1>(2, {{SegmentId(1, 1)}})};\n  custom_neighbors.set_ids_to(other_custom_id);\n  CHECK(custom_neighbors.size() == 1);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(([&custom_neighbors]() {\n                      const std::unordered_set<ElementId<1>> wrong_block_id{\n                          ElementId<1>(0, {{SegmentId(2, 1)}})};\n                      custom_neighbors.set_ids_to(wrong_block_id);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\"Some of the Ids\"));\n#endif\n\n  // Test serialization:\n  test_serialization(custom_neighbors);\n\n  // Test comparison:\n  CHECK(test_neighbors == test_neighbors);\n  CHECK(custom_neighbors == custom_neighbors);\n  CHECK(test_neighbors != custom_neighbors);\n\n  // Test iterators:\n  test_iterators(custom_neighbors);\n}\n\nvoid check_neighbors_2d() {\n  // Test default constructor, only used for Charm++ serialization so no CHECK\n  // calls:\n  Neighbors<2> test_neighbors;\n\n  // Test constructor:\n  OrientationMap<2> custom_orientation(std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}});\n  const std::unordered_set<ElementId<2>> custom_id{\n      ElementId<2>(2, {{SegmentId(2, 3), SegmentId(1, 0)}})};\n  Neighbors<2> custom_neighbors(custom_id, custom_orientation);\n\n  // Test size\n  CHECK(custom_neighbors.size() == 1);\n\n  // Test add_ids:\n  const std::unordered_set<ElementId<2>> other_custom_id{\n      ElementId<2>(2, {{SegmentId(2, 2), SegmentId(1, 0)}})};\n  custom_neighbors.add_ids(other_custom_id);\n  CHECK(custom_neighbors.size() == 2);\n\n  // Test set_ids_to:\n  custom_neighbors.set_ids_to(custom_id);\n  CHECK(custom_neighbors.size() == 1);\n\n  // Test serialization:\n  test_serialization(custom_neighbors);\n\n  // Test comparison:\n  CHECK(test_neighbors == test_neighbors);\n  CHECK(custom_neighbors == custom_neighbors);\n  CHECK(test_neighbors != custom_neighbors);\n\n  // Test iterators:\n  test_iterators(custom_neighbors);\n\n  // Test semantics:\n  const auto custom_copy = custom_neighbors;\n  test_copy_semantics(test_neighbors);\n  test_move_semantics(std::move(custom_neighbors), custom_copy);\n}\n\nvoid check_neighbors_3d() {\n  // Test default constructor, only used for Charm++ serialization so no CHECK\n  // calls:\n  Neighbors<3> test_neighbors;\n\n  // Test constructor:\n  OrientationMap<3> custom_orientation(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::upper_zeta(),\n       Direction<3>::upper_xi()}});\n  const std::unordered_set<ElementId<3>> custom_ids{\n      ElementId<3>(2, {{SegmentId(2, 3), SegmentId(1, 0), SegmentId(1, 1)}}),\n      ElementId<3>(2, {{SegmentId(2, 2), SegmentId(1, 1), SegmentId(1, 0)}}),\n      ElementId<3>(2, {{SegmentId(2, 1), SegmentId(1, 0), SegmentId(1, 1)}})};\n  Neighbors<3> custom_neighbors(custom_ids, custom_orientation);\n\n  // Test size\n  CHECK(custom_neighbors.size() == 3);\n\n  // Test output\n  CHECK(get_output(custom_neighbors) ==\n        \"Ids = \"\n        \"([B2,(L2I1,L1I0,L1I1)],[B2,(L2I2,L1I1,L1I0)],[B2,(L2I3,L1I0,L1I1)]); \"\n        \"orientations = ([2,(+1, +2, +0)]); conforming = true\");\n\n  // Test add_ids\n  const std::unordered_set<ElementId<3>> other_custom_id{\n      ElementId<3>(2, {{SegmentId(2, 3), SegmentId(1, 1), SegmentId(1, 1)}})};\n  custom_neighbors.add_ids(other_custom_id);\n  CHECK(custom_neighbors.size() == 4);\n\n  CHECK(get_output(custom_neighbors) ==\n        \"Ids = \"\n        \"([B2,(L2I1,L1I0,L1I1)],[B2,(L2I2,L1I1,L1I0)],[B2,(L2I3,L1I0,L1I1)],[\"\n        \"B2,(L2I3,L1I1,L1I1)]); \"\n        \"orientations = ([2,(+1, +2, +0)]); conforming = true\");\n\n  // Test set_ids_to:\n  custom_neighbors.set_ids_to(custom_ids);\n  CHECK(custom_neighbors.size() == 3);\n\n  CHECK(get_output(custom_neighbors) ==\n        \"Ids = \"\n        \"([B2,(L2I1,L1I0,L1I1)],[B2,(L2I2,L1I1,L1I0)],[B2,(L2I3,L1I0,L1I1)]); \"\n        \"orientations = ([2,(+1, +2, +0)]); conforming = true\");\n\n  // Test serialization:\n  test_serialization(custom_neighbors);\n\n  // Test comparison:\n  CHECK(test_neighbors == test_neighbors);\n  CHECK(custom_neighbors == custom_neighbors);\n  CHECK(test_neighbors != custom_neighbors);\n\n  // Test iterators:\n  test_iterators(custom_neighbors);\n\n  // Test semantics:\n  const auto custom_copy = custom_neighbors;\n  test_copy_semantics(test_neighbors);\n  test_move_semantics(std::move(custom_neighbors), custom_copy);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.Neighbors\", \"[Domain][Unit]\") {\n  check_neighbors_1d();\n  check_neighbors_2d();\n  check_neighbors_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_ObjectLabel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.ObjectLabel\", \"[Domain][Unit]\") {\n  CHECK(name(domain::ObjectLabel::A) == \"A\");\n  CHECK(get_output(domain::ObjectLabel::A) == \"A\");\n  CHECK(name(domain::ObjectLabel::B) == \"B\");\n  CHECK(get_output(domain::ObjectLabel::B) == \"B\");\n  CHECK(name(domain::ObjectLabel::C) == \"C\");\n  CHECK(get_output(domain::ObjectLabel::C) == \"C\");\n  CHECK(name(domain::ObjectLabel::None) == \"\");\n  CHECK(get_output(domain::ObjectLabel::None) == \"\");\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_OrientationMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Helpers/Utilities/Serialization/Versioning.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\n\nvoid test_1d() {\n  CHECK(sizeof(OrientationMap<1>) == 2);\n  // Test constructors:\n\n  OrientationMap<1> aligned_orientation = OrientationMap<1>::create_aligned();\n  CHECK(aligned_orientation.is_aligned());\n  CHECK(get_output(aligned_orientation) == \"(+0)\");\n  OrientationMap<1> custom1(\n      std::array<Direction<1>, 1>{{Direction<1>::upper_xi()}});\n  CHECK(custom1.is_aligned());\n  OrientationMap<1> custom2(\n      std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}});\n  CHECK_FALSE(custom2.is_aligned());\n\n  TestHelpers::serialization::test_versioning<OrientationMap<1>>(\n      \"Domain/Structure/OrientationMap1d.serializations\", \"version 2\", custom1);\n\n  // Test if OrientationMap can encode a 1D parallel/antiparallel.\n  std::array<Direction<1>, 1> block1_directions{{Direction<1>::upper_xi()}};\n  std::array<Direction<1>, 1> block2_directions{{Direction<1>::lower_xi()}};\n  OrientationMap<1> parallel_orientation(block1_directions, block1_directions);\n  OrientationMap<1> antiparallel_orientation(block1_directions,\n                                             block2_directions);\n  std::array<SegmentId, 1> segment_ids{{SegmentId(2, 1)}};\n  std::array<SegmentId, 1> expected_antiparallel_segment_ids{{SegmentId(2, 2)}};\n  CHECK(parallel_orientation(segment_ids) == segment_ids);\n  CHECK(antiparallel_orientation(segment_ids) ==\n        expected_antiparallel_segment_ids);\n  const Mesh<1> mesh(4, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  CHECK(parallel_orientation(mesh) == mesh);\n  CHECK(antiparallel_orientation(mesh) == mesh);\n  CHECK(std::array<int, 1>{{1}} ==\n        parallel_orientation.permute_from_neighbor(std::array<int, 1>{{1}}));\n  CHECK(\n      std::array<int, 1>{{1}} ==\n      antiparallel_orientation.permute_from_neighbor(std::array<int, 1>{{1}}));\n\n  CHECK(get_output(parallel_orientation) == \"(+0)\");\n  CHECK(get_output(antiparallel_orientation) == \"(-0)\");\n\n  // Test comparison:\n  CHECK(custom1 != custom2);\n  CHECK(custom1 == parallel_orientation);\n\n  // Test semantics:\n  const auto custom_copy = custom1;\n  test_copy_semantics(custom2);\n  // clang-tidy: std::move of trivially-copyable type has no effect.\n  test_move_semantics(std::move(custom1), custom_copy);  // NOLINT\n\n  // Test serialization:\n  test_serialization(custom2);\n\n  // Test inverse:\n  CHECK(aligned_orientation.inverse_map() == aligned_orientation);\n  CHECK(custom2.inverse_map() == custom2);\n}\n\nvoid test_2d() {\n  CHECK(sizeof(OrientationMap<2>) == 2);\n  // Test constructors:\n  OrientationMap<2> aligned_orientation = OrientationMap<2>::create_aligned();\n  CHECK(aligned_orientation.is_aligned());\n  CHECK(get_output(aligned_orientation) == \"(+0, +1)\");\n  OrientationMap<2> custom1(std::array<Direction<2>, 2>{\n      {Direction<2>::upper_xi(), Direction<2>::upper_eta()}});\n  CHECK(custom1.is_aligned());\n  OrientationMap<2> custom2(std::array<Direction<2>, 2>{\n      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}});\n  CHECK_FALSE(custom2.is_aligned());\n\n  TestHelpers::serialization::test_versioning<OrientationMap<2>>(\n      \"Domain/Structure/OrientationMap2d.serializations\", \"version 2\", custom1);\n\n  // Test if OrientationMap can encode a 2D rotated.\n  const auto& upper_xi = Direction<2>::upper_xi();\n  const auto& upper_eta = Direction<2>::upper_eta();\n  const auto& lower_xi = Direction<2>::lower_xi();\n  const auto& lower_eta = Direction<2>::lower_eta();\n\n  // Note: the naming convention used here gives the directions in\n  // the host block which map to the pos_xi and pos_eta directions\n  // in the neighbor block, respectively.\n  // For example, \"rotated2d_neg_eta_neg_xi\"would indicate that the neg_eta\n  // direction in the host block maps to the pos_xi direction in the\n  // neighbor block, and neg_xi direction in the host block maps to the\n  // pos_eta direction in the neighbor block.\n  std::array<Direction<2>, 2> block_directions1{{upper_xi, upper_eta}};\n  std::array<Direction<2>, 2> block_directions2{{lower_xi, lower_eta}};\n  OrientationMap<2> rotated2d_neg_xi_neg_eta(block_directions2,\n                                             block_directions1);\n\n  std::array<Direction<2>, 2> block_directions3{{upper_xi, upper_eta}};\n  std::array<Direction<2>, 2> block_directions4{{lower_eta, upper_xi}};\n  OrientationMap<2> rotated2d_neg_eta_pos_xi(block_directions4,\n                                             block_directions3);\n\n  std::array<Direction<2>, 2> block_directions5{{upper_xi, upper_eta}};\n  std::array<Direction<2>, 2> block_directions6{{upper_eta, lower_xi}};\n  OrientationMap<2> rotated2d_pos_eta_neg_xi(block_directions6,\n                                             block_directions5);\n\n  std::array<Direction<2>, 2> block_directions7{{upper_xi, upper_eta}};\n  std::array<Direction<2>, 2> block_directions8{{upper_xi, upper_eta}};\n  OrientationMap<2> rotated2d_pos_xi_pos_eta(block_directions7,\n                                             block_directions8);\n\n  std::array<SegmentId, 2> segment_ids{{SegmentId(2, 1), SegmentId(3, 5)}};\n\n  std::array<SegmentId, 2> expected_neg_xi_neg_eta_segment_ids{\n      {SegmentId(2, 2), SegmentId(3, 2)}};\n  std::array<SegmentId, 2> expected_neg_eta_pos_xi_segment_ids{\n      {SegmentId(3, 2), SegmentId(2, 1)}};\n  std::array<SegmentId, 2> expected_pos_eta_neg_xi_segment_ids{\n      {SegmentId(3, 5), SegmentId(2, 2)}};\n\n  CHECK_FALSE(rotated2d_neg_eta_pos_xi.is_aligned());\n  CHECK_FALSE(rotated2d_neg_xi_neg_eta.is_aligned());\n  CHECK_FALSE(rotated2d_pos_eta_neg_xi.is_aligned());\n  CHECK(rotated2d_pos_xi_pos_eta.is_aligned());\n\n  // Check mapped(size_t dimension) function\n  CHECK(rotated2d_neg_xi_neg_eta(0) == 0);\n  CHECK(rotated2d_neg_xi_neg_eta(1) == 1);\n  CHECK(rotated2d_neg_eta_pos_xi(0) == 1);\n  CHECK(rotated2d_neg_eta_pos_xi(1) == 0);\n  CHECK(rotated2d_pos_eta_neg_xi(0) == 1);\n  CHECK(rotated2d_pos_eta_neg_xi(1) == 0);\n\n  // Check mapped(Direction<2> direction) function\n  CHECK(rotated2d_neg_xi_neg_eta(upper_xi) == lower_xi);\n  CHECK(rotated2d_neg_xi_neg_eta(upper_eta) == lower_eta);\n  CHECK(rotated2d_neg_xi_neg_eta(lower_xi) == upper_xi);\n  CHECK(rotated2d_neg_xi_neg_eta(lower_eta) == upper_eta);\n\n  CHECK(rotated2d_neg_eta_pos_xi(upper_xi) == upper_eta);\n  CHECK(rotated2d_neg_eta_pos_xi(upper_eta) == lower_xi);\n  CHECK(rotated2d_neg_eta_pos_xi(lower_xi) == lower_eta);\n  CHECK(rotated2d_neg_eta_pos_xi(lower_eta) == upper_xi);\n  CHECK(rotated2d_pos_eta_neg_xi(upper_xi) == lower_eta);\n  CHECK(rotated2d_pos_eta_neg_xi(upper_eta) == upper_xi);\n  CHECK(rotated2d_pos_eta_neg_xi(lower_xi) == upper_eta);\n  CHECK(rotated2d_pos_eta_neg_xi(lower_eta) == lower_xi);\n\n  // Check mapped(std::array<SegmentIds, VolumeDim> segment_ids)\n  CHECK(rotated2d_neg_eta_pos_xi(segment_ids) ==\n        expected_neg_eta_pos_xi_segment_ids);\n  CHECK(rotated2d_neg_xi_neg_eta(segment_ids) ==\n        expected_neg_xi_neg_eta_segment_ids);\n  CHECK(rotated2d_pos_eta_neg_xi(segment_ids) ==\n        expected_pos_eta_neg_xi_segment_ids);\n\n  // Check mapped(Mesh<2> mesh)\n  const Mesh<2> input_mesh(\n      {{3, 4}}, {{Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}});\n  const Mesh<2> flipped_mesh(\n      {{4, 3}}, {{Spectral::Basis::Chebyshev, Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}});\n  CHECK(rotated2d_pos_xi_pos_eta(input_mesh) == input_mesh);\n  CHECK(rotated2d_neg_xi_neg_eta(input_mesh) == input_mesh);\n  CHECK(rotated2d_neg_eta_pos_xi(input_mesh) == flipped_mesh);\n  CHECK(rotated2d_pos_eta_neg_xi(input_mesh) == flipped_mesh);\n\n  // Check permute_from_neighbor(std::array<T, 2> array)\n  const std::array<int, 2> input_array{{1, -3}};\n  const std::array<int, 2> flipped_array{{-3, 1}};\n  CHECK(rotated2d_neg_xi_neg_eta.permute_from_neighbor(input_array) ==\n        input_array);\n  CHECK(rotated2d_neg_eta_pos_xi.permute_from_neighbor(input_array) ==\n        flipped_array);\n  CHECK(rotated2d_pos_eta_neg_xi.permute_from_neighbor(input_array) ==\n        flipped_array);\n\n  // The naming convention used in this test:\n  // \"neg_eta_pos_xi\" means that -1 in the host maps to +0,\n  // and that +0 in the host maps to +1, in the neighbor.\n  // For the output operator, the directions that correspond\n  // to the +0 and +1 directions in the host are outputted.\n  // This means we expect neg_eta_pos_xi to output (+1, -0).\n  CHECK(get_output(rotated2d_neg_eta_pos_xi) == \"(+1, -0)\");\n  CHECK(get_output(rotated2d_pos_xi_pos_eta) == \"(+0, +1)\");\n\n  // Test comparison operators:\n  CHECK(rotated2d_neg_eta_pos_xi != rotated2d_pos_eta_neg_xi);\n  CHECK(rotated2d_neg_eta_pos_xi == rotated2d_neg_eta_pos_xi);\n\n  // Test semantics:\n  const auto rotated_copy = rotated2d_neg_eta_pos_xi;\n  test_copy_semantics(rotated2d_pos_eta_neg_xi);\n  // clang-tidy: std::move of trivially-copyable type has no effect.\n  test_move_semantics(std::move(rotated2d_neg_eta_pos_xi),  // NOLINT\n                      rotated_copy);\n\n  // Test serialization:\n  test_serialization(rotated_copy);\n\n  // Test inverse:\n  CHECK(aligned_orientation.inverse_map() == aligned_orientation);\n  CHECK(\n      OrientationMap<2>(std::array<Direction<2>, 2>{{Direction<2>::lower_eta(),\n                                                     Direction<2>::upper_xi()}})\n          .inverse_map() ==\n      OrientationMap<2>(std::array<Direction<2>, 2>{\n          {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}));\n  CHECK(custom1.inverse_map().inverse_map() == custom1);\n  CHECK(custom2.inverse_map().inverse_map() == custom2);\n}\n\nvoid test_3d() {\n  CHECK(sizeof(OrientationMap<3>) == 2);\n  // Test constructors:\n  OrientationMap<3> aligned_orientation = OrientationMap<3>::create_aligned();\n  CHECK(aligned_orientation.is_aligned());\n  CHECK(get_output(aligned_orientation) == \"(+0, +1, +2)\");\n  OrientationMap<3> custom1(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_xi(), Direction<3>::upper_eta(),\n       Direction<3>::upper_zeta()}});\n  CHECK(custom1.is_aligned());\n  OrientationMap<3> custom2(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n       Direction<3>::lower_zeta()}});\n  CHECK_FALSE(custom2.is_aligned());\n\n  TestHelpers::serialization::test_versioning<OrientationMap<3>>(\n      \"Domain/Structure/OrientationMap3d.serializations\", \"version 2\", custom1);\n\n  // Test if OrientationMap can encode a 3D Flipped.\n  const auto& upper_xi = Direction<3>::upper_xi();\n  const auto& upper_eta = Direction<3>::upper_eta();\n  const auto& upper_zeta = Direction<3>::upper_zeta();\n  const auto& lower_xi = Direction<3>::lower_xi();\n  const auto& lower_eta = Direction<3>::lower_eta();\n  const auto& lower_zeta = Direction<3>::lower_zeta();\n\n  std::array<Direction<3>, 3> block_directions1{\n      {upper_xi, upper_eta, upper_zeta}};\n  std::array<Direction<3>, 3> block_directions2{\n      {lower_xi, lower_eta, lower_zeta}};\n  OrientationMap<3> custom_orientation(block_directions1, block_directions2);\n  CHECK(custom_orientation(upper_xi) == lower_xi);\n  CHECK(custom_orientation(upper_eta) == lower_eta);\n  CHECK(custom_orientation(upper_zeta) == lower_zeta);\n\n  Direction<3> direction(Direction<3>::Axis::Zeta, Side::Upper);\n  std::array<SegmentId, 3> segment_ids{\n      {SegmentId(2, 1), SegmentId(3, 1), SegmentId(3, 3)}};\n\n  std::array<SegmentId, 3> flipped_ids{\n      {SegmentId(2, 2), SegmentId(3, 6), SegmentId(3, 4)}};\n  CHECK(custom_orientation(2) == 2);\n  CHECK(custom_orientation(direction) == direction.opposite());\n  CHECK(custom_orientation(segment_ids) == flipped_ids);\n  CHECK_FALSE(custom_orientation.is_aligned());\n  OrientationMap<3> second_aligned_orientation(block_directions1,\n                                               block_directions1);\n  CHECK(second_aligned_orientation.is_aligned());\n  CHECK(get_output(custom_orientation) == \"(-0, -1, -2)\");\n  CHECK(get_output(second_aligned_orientation) == \"(+0, +1, +2)\");\n\n  // Test comparison operators:\n  CHECK(custom_orientation != second_aligned_orientation);\n  CHECK(custom_orientation == custom_orientation);\n\n  // Test semantics:\n  const auto custom_copy = custom_orientation;\n  test_copy_semantics(second_aligned_orientation);\n  // clang-tidy: std::move of trivially-copyable type has no effect.\n  test_move_semantics(std::move(custom_orientation), custom_copy);  // NOLINT\n\n  // Test serialzation:\n  test_serialization(custom2);\n\n  // Test inverse:\n  CHECK(aligned_orientation.inverse_map() == aligned_orientation);\n  OrientationMap<3> custom3{std::array<Direction<3>, 3>{\n      {Direction<3>::lower_eta(), Direction<3>::lower_zeta(),\n       Direction<3>::upper_xi()}}};\n  OrientationMap<3> custom4{std::array<Direction<3>, 3>{\n      {Direction<3>::upper_zeta(), Direction<3>::lower_xi(),\n       Direction<3>::lower_eta()}}};\n  CHECK(custom3.inverse_map() == custom4);\n  CHECK(custom1.inverse_map().inverse_map() == custom1);\n  CHECK(custom2.inverse_map().inverse_map() == custom2);\n\n  // Test permutations and mesh\n  const Mesh<3> mesh{{{3, 4, 5}},\n                     Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  CHECK(custom3(mesh) == Mesh<3>({{5, 3, 4}}, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto));\n  CHECK(custom4(mesh) == Mesh<3>({{4, 5, 3}}, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto));\n  CHECK(custom3.permute_to_neighbor(std::array<int, 3>{{4, -8, 12}}) ==\n        std::array<int, 3>{{12, 4, -8}});\n  CHECK(custom4.permute_to_neighbor(std::array<int, 3>{{4, -8, 12}}) ==\n        std::array<int, 3>{{-8, 12, 4}});\n  CHECK(std::array<int, 3>{{-8, 12, 4}} ==\n        custom3.permute_from_neighbor(std::array<int, 3>{{4, -8, 12}}));\n  CHECK(std::array<int, 3>{{12, 4, -8}} ==\n        custom4.permute_from_neighbor(std::array<int, 3>{{4, -8, 12}}));\n}\n\n\nvoid test_errors() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      OrientationMap<2>(std::array<Direction<2>, 2>{\n          {Direction<2>::upper_xi(), Direction<2>::lower_xi()}}),\n      Catch::Matchers::ContainsSubstring(\n          \"This OrientationMap fails to map Directions one-to-one.\"));\n\n  CHECK_THROWS_WITH(\n      OrientationMap<2>(\n          std::array<Direction<2>, 2>{\n              {Direction<2>::upper_xi(), Direction<2>::lower_xi()}},\n          std::array<Direction<2>, 2>{\n              {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}),\n      Catch::Matchers::ContainsSubstring(\n          \"This OrientationMap fails to map Directions one-to-one.\"));\n\n  CHECK_THROWS_WITH(\n      OrientationMap<3>(\n          std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                       Direction<3>::lower_eta(),\n                                       Direction<3>::lower_zeta()}},\n          std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                       Direction<3>::upper_eta(),\n                                       Direction<3>::lower_eta()}}),\n      Catch::Matchers::ContainsSubstring(\n          \"This OrientationMap fails to map Directions one-to-one.\"));\n\n  CHECK_THROWS_WITH(OrientationMap<3>{}.is_aligned(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use a default-constructed OrientationMap\"));\n  CHECK_THROWS_WITH(OrientationMap<3>{}(0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use a default-constructed OrientationMap\"));\n  CHECK_THROWS_WITH(OrientationMap<3>{}(Direction<3>::lower_xi()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use a default-constructed OrientationMap\"));\n  CHECK_THROWS_WITH(OrientationMap<3>{}(std::array<SegmentId, 3>{}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use a default-constructed OrientationMap\"));\n  CHECK_THROWS_WITH(OrientationMap<3>{}(Mesh<3>{}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use a default-constructed OrientationMap\"));\n  CHECK_THROWS_WITH(OrientationMap<3>{}.inverse_map(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot use a default-constructed OrientationMap\"));\n\n#endif\n}\n\nvoid test_all_orientations() {\n  for (OrientationMapIterator<2> map_i{}; map_i; ++map_i) {\n    const std::array<double, 2> original_point{{0.5, -2.0}};\n    const std::array<double, 2> new_point =\n        discrete_rotation(map_i(), original_point);\n    for (size_t d = 0; d < 2; d++) {\n      CHECK(gsl::at(new_point, d) ==\n            (map_i()(Direction<2>{d, Side::Upper}).side() == Side::Upper\n                 ? gsl::at(original_point, map_i()(d))\n                 : -1.0 * gsl::at(original_point, map_i()(d))));\n    }\n  }\n  for (OrientationMapIterator<3> map_i{}; map_i; ++map_i) {\n    const std::array<double, 3> original_point{{0.5, -2.0, 1.5}};\n    const std::array<double, 3> new_point =\n        discrete_rotation(map_i(), original_point);\n    for (size_t d = 0; d < 3; d++) {\n      CHECK(gsl::at(new_point, d) ==\n            (map_i()(Direction<3>{d, Side::Upper}).side() == Side::Upper\n                 ? gsl::at(original_point, map_i()(d))\n                 : -1.0 * gsl::at(original_point, map_i()(d))));\n    }\n  }\n}\n\nvoid test_rotation() {\n  const OrientationMap<1> rotation1(\n      std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}});\n  const std::array<DataVector, 1> test_points1{\n      {DataVector{-1.0, 1.0, 0.7, 0.0}}};\n  const std::array<DataVector, 1> expected_rotated_points1{\n      {DataVector{1.0, -1.0, -0.7, 0.0}}};\n  CHECK(discrete_rotation(rotation1, test_points1) == expected_rotated_points1);\n  CHECK(discrete_rotation(rotation1, std::array<double, 1>{{-0.2}}) ==\n        std::array<double, 1>{{0.2}});\n\n  const OrientationMap<2> rotation2(std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::upper_xi()}});\n  const std::array<DataVector, 2> test_points2{\n      {DataVector{-1.0, 1.0, 0.7, 0.0}, DataVector{0.25, 1.0, -0.2, 0.0}}};\n  const std::array<DataVector, 2> expected_rotated_points2{\n      {DataVector{0.25, 1.0, -0.2, 0.0}, DataVector{-1.0, 1.0, 0.7, 0.0}}};\n  CHECK(discrete_rotation(rotation2, test_points2) == expected_rotated_points2);\n  CHECK(discrete_rotation(rotation2, std::array<double, 2>{{-1.0, 0.5}}) ==\n        std::array<double, 2>{{0.5, -1.0}});\n\n  const OrientationMap<3> rotation3(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_zeta(),\n       Direction<3>::lower_xi()}});\n  const std::array<DataVector, 3> test_points3{\n      {DataVector{-1.0, 1.0, 0.7, 0.0}, DataVector{0.25, 1.0, -0.2, 0.0},\n       DataVector{0.0, -0.5, 0.4, 0.0}}};\n  const std::array<DataVector, 3> expected_rotated_points3{\n      {DataVector{0.25, 1.0, -0.2, 0.0}, DataVector{0.0, 0.5, -0.4, 0.0},\n       DataVector{1.0, -1.0, -0.7, 0.0}}};\n  CHECK(discrete_rotation(rotation3, test_points3) == expected_rotated_points3);\n  CHECK(discrete_rotation(rotation3, std::array<double, 3>{{-1.0, 0.5, 1.0}}) ==\n        std::array<double, 3>{{0.5, -1.0, 1.0}});\n}\n\nvoid test_reference_wrapper() {\n  const OrientationMap<3> rotation(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_zeta(),\n       Direction<3>::lower_xi()}});\n\n  // This test will check that these points are not modified.\n  DataVector x_points{-1.0, 1.0, 0.7, 0.0};\n  DataVector y_points{0.25, 1.0, -0.2, 0.0};\n  DataVector z_points{0.0, -0.5, 0.4, 0.0};\n\n  // These variables are not passed to any functions;\n  // they will not be modified by construction.\n  // clang-tidy: local copy is never modified\n  const DataVector x_points_proof = x_points;  // NOLINT\n  const DataVector y_points_proof = y_points;  // NOLINT\n  const DataVector z_points_proof = z_points;  // NOLINT\n\n  // References to the points to be tested:\n  const auto ref_x_points = std::cref(x_points);\n  const auto ref_y_points = std::cref(y_points);\n  const auto ref_z_points = std::cref(z_points);\n\n  // Array of references to the points to be tested.\n  const std::array<const std::reference_wrapper<const DataVector>, 3>\n      test_points{{ref_x_points, ref_y_points, ref_z_points}};\n\n  // The value of new_points is irrelevant to this test.\n  auto new_points = discrete_rotation(rotation, test_points);\n  CHECK(test_points[0].get() == x_points_proof);\n  CHECK(test_points[1].get() == y_points_proof);\n  CHECK(test_points[2].get() == z_points_proof);\n\n  const DataVector new_pt{0.0, 0.5, -0.4, 0.0};\n  new_points[0] = new_pt;\n  new_points[1] = new_pt;\n  new_points[2] = new_pt;\n\n  // Check that modifying new_points does not modify the test points.\n  CHECK(test_points[0].get() == x_points_proof);\n  CHECK(test_points[1].get() == y_points_proof);\n  CHECK(test_points[2].get() == z_points_proof);\n}\n\nvoid test_only_radial_orientation() {\n  const OrientationMap<3> shell_to_wedge(std::array<Direction<3>, 3>{\n      Direction<3>::upper_zeta(), Direction<3>::self(), Direction<3>::self()});\n  const OrientationMap<3> wedge_to_shell(std::array<Direction<3>, 3>{\n      Direction<3>::self(), Direction<3>::self(), Direction<3>::upper_xi()});\n  const OrientationMap<3> radially_aligned(std::array<Direction<3>, 3>{\n      Direction<3>::upper_xi(), Direction<3>::self(), Direction<3>::self()});\n  CHECK_FALSE(shell_to_wedge.is_aligned());\n  CHECK_FALSE(wedge_to_shell.is_aligned());\n  CHECK_FALSE(radially_aligned.is_aligned());\n  CHECK(shell_to_wedge(0) == 2);\n  CHECK(wedge_to_shell(2) == 0);\n  CHECK(radially_aligned(0) == 0);\n  CHECK(shell_to_wedge(Direction<3>::upper_xi()) == Direction<3>::upper_zeta());\n  CHECK(shell_to_wedge(Direction<3>::lower_xi()) == Direction<3>::lower_zeta());\n  CHECK(shell_to_wedge(Direction<3>::lower_eta()) == Direction<3>::self());\n  CHECK(wedge_to_shell(Direction<3>::upper_zeta()) == Direction<3>::upper_xi());\n  CHECK(wedge_to_shell(Direction<3>::lower_zeta()) == Direction<3>::lower_xi());\n  CHECK(radially_aligned(Direction<3>::upper_xi()) == Direction<3>::upper_xi());\n  CHECK(radially_aligned(Direction<3>::lower_xi()) == Direction<3>::lower_xi());\n  CHECK(shell_to_wedge.inverse_map() == wedge_to_shell);\n  CHECK(shell_to_wedge == wedge_to_shell.inverse_map());\n  CHECK(radially_aligned.inverse_map() == radially_aligned);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(shell_to_wedge(1),\n                    Catch::Matchers::ContainsSubstring(\n                        \"There is no corresponding dimension\"));\n\n  CHECK_THROWS_WITH(\n      shell_to_wedge(\n          std::array{SegmentId{1, 0}, SegmentId{2, 1}, SegmentId{3, 2}}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot re-orient all SegmentIds for this Orientation\"));\n  CHECK_THROWS_WITH(\n      shell_to_wedge(Mesh<3>{\n          {4, 5, 9},\n          {Spectral::Basis::Chebyshev, Spectral::Basis::SphericalHarmonic,\n           Spectral::Basis::SphericalHarmonic},\n          {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n           Spectral::Quadrature::Equiangular}}),\n      Catch::Matchers::ContainsSubstring(\n          \"There is no corresponding dimension\"));\n  CHECK_THROWS_WITH(\n      shell_to_wedge.permute_to_neighbor(std::array{1.0, 2.0, 3.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"There is no corresponding dimension\"));\n  CHECK_THROWS_WITH(\n      shell_to_wedge.permute_from_neighbor(std::array{1.0, 2.0, 3.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"There is no corresponding dimension\"));\n  CHECK_THROWS_WITH(\n      discrete_rotation(shell_to_wedge, std::array{1.0, 2.0, 3.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot define discrete rotation for this OrientationMap\"));\n  CHECK_THROWS_WITH(\n      discrete_rotation_jacobian(shell_to_wedge),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot define discrete rotation for this OrientationMap\"));\n  CHECK_THROWS_WITH(\n      discrete_rotation_inverse_jacobian(shell_to_wedge),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot define discrete rotation for this OrientationMap\"));\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.OrientationMap\", \"[Domain][Unit]\") {\n  test_1d();\n  test_2d();\n  test_3d();\n\n  test_all_orientations();\n  test_rotation();\n  test_reference_wrapper();\n\n  test_only_radial_orientation();\n  test_errors();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_OrientationMapHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\nusing namespace std::complex_literals;\n\ntemplate <typename DataType>\nstruct ScalarTensor : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType, size_t SpatialDim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataType, SpatialDim, Frame::Inertial>;\n};\n\nvoid test_1d_orient_variables() {\n  // Note: only test the DataVector implementation in 1d since all the others\n  // forward to it. Just want to make sure it's publicly visible.\n  const Index<1> extents{4};\n  Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 1>>> vars(\n      extents.product());\n  get(get<ScalarTensor<DataVector>>(vars)) = DataVector{{1.0, 2.0, 3.0, 4.0}};\n  get<0>(get<Vector<DataVector, 1>>(vars)) = DataVector{{0.5, 0.6, 0.7, 0.8}};\n  const std::vector<double> vars_vector(vars.data(), vars.data() + vars.size());\n  const DataVector vars_datavector(const_cast<double*>(vars.data()),\n                                   vars.size());\n\n  // Check aligned case\n  {\n    const auto oriented_vars =\n        orient_variables(vars, extents, OrientationMap<1>::create_aligned());\n    CHECK(oriented_vars == vars);\n\n    const std::vector<double> oriented_vars_vector = orient_variables(\n        vars_vector, extents, OrientationMap<1>::create_aligned());\n    CHECK(oriented_vars_vector == vars_vector);\n\n    DataVector oriented_vars_datavector(vars_datavector.size());\n    orient_variables(make_not_null(&oriented_vars_datavector), vars_datavector,\n                     extents, OrientationMap<1>::create_aligned());\n    CHECK(oriented_vars_datavector == vars_datavector);\n  }\n\n  // Check anti-aligned case\n  {\n    const OrientationMap<1> orientation_map(\n        std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}});\n    const auto oriented_vars = orient_variables(vars, extents, orientation_map);\n    Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 1>>>\n        expected_vars(extents.product());\n    get(get<ScalarTensor<DataVector>>(expected_vars)) =\n        DataVector{{4.0, 3.0, 2.0, 1.0}};\n    get<0>(get<Vector<DataVector, 1>>(expected_vars)) =\n        DataVector{{0.8, 0.7, 0.6, 0.5}};\n    CHECK(oriented_vars == expected_vars);\n\n    const std::vector<double> oriented_vars_vector =\n        orient_variables(vars_vector, extents, orientation_map);\n    const std::vector<double> expected_vars_vector(\n        expected_vars.data(), expected_vars.data() + expected_vars.size());\n    CHECK(oriented_vars_vector == expected_vars_vector);\n\n    DataVector oriented_vars_datavector(vars_datavector.size());\n    orient_variables(make_not_null(&oriented_vars_datavector), vars_datavector,\n                     extents, orientation_map);\n    DataVector expected_vars_datavector(vars_datavector.size());\n    for (size_t i = 0; i < vars_datavector.size(); ++i) {\n      expected_vars_datavector[i] = expected_vars_vector[i];\n    }\n    CHECK(oriented_vars_datavector == expected_vars_datavector);\n  }\n\n#ifdef SPECTRE_DEBUG\n  {\n    const OrientationMap<1> orientation_map(\n        std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}});\n    DataVector oriented_vars_datavector{};\n    CHECK_THROWS_WITH(\n        orient_variables(make_not_null(&oriented_vars_datavector),\n                         vars_datavector, extents, {}),\n        Catch::Matchers::ContainsSubstring(\"Result should have size\"));\n\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n    const DataVector vars_datavector_bad_size(const_cast<double*>(vars.data()),\n                                              vars.size() - 1);\n    oriented_vars_datavector = DataVector{vars.size() - 1};\n    CHECK_THROWS_WITH(orient_variables(make_not_null(&oriented_vars_datavector),\n                                       vars_datavector_bad_size, extents,\n                                       OrientationMap<1>::create_aligned()),\n                      Catch::Matchers::ContainsSubstring(\n                          \"The size of the variables must be divisible by the \"\n                          \"number of grid points. Number of grid points: \"));\n  }\n#endif  // SPECTRE_DEBUG\n}\n\n// Test one case by hand. This test case is redundant with (though not\n// identical to) one of the cases hit by `test_2d_orient_variables`. However, by\n// writing it out by hand, we provide a sanity check and a clearer example of\n// how to use `orient_variables`.\nvoid test_2d_orient_variables_simple_case_by_hand() {\n  const OrientationMap<2> orientation_map(std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}});\n  const auto extents = Index<2>{2, 3};\n\n  Variables<tmpl::list<ScalarTensor<DataVector>>> vars(6);\n  Variables<tmpl::list<ScalarTensor<ComplexDataVector>>> vars_complex(6);\n  get(get<ScalarTensor<DataVector>>(vars)) =\n      DataVector{{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}};\n  get(get<ScalarTensor<ComplexDataVector>>(vars_complex)) = ComplexDataVector{\n      {1.0 + 4.0i, 2.0 + 3.0i, 3.0 + 2.0i, 4.0 + 1.0i, 5.0 + 0.0i, 6.0 - 1.0i}};\n  const auto oriented_vars = orient_variables(vars, extents, orientation_map);\n  const auto oriented_vars_complex =\n      orient_variables(vars_complex, extents, orientation_map);\n\n  Variables<tmpl::list<ScalarTensor<DataVector>>> expected_vars(6);\n  Variables<tmpl::list<ScalarTensor<ComplexDataVector>>> expected_vars_complex(\n      6);\n  get(get<ScalarTensor<DataVector>>(expected_vars)) =\n      DataVector{{2.0, 4.0, 6.0, 1.0, 3.0, 5.0}};\n  get(get<ScalarTensor<ComplexDataVector>>(expected_vars_complex)) =\n      ComplexDataVector{{2.0 + 3.0i, 4.0 + 1.0i, 6.0 - 1.0i, 1.0 + 4.0i,\n                         3.0 + 2.0i, 5.0 + 0.0i}};\n  CHECK(oriented_vars == expected_vars);\n  CHECK(oriented_vars_complex == expected_vars_complex);\n\n  const std::vector<double> vars_vector(vars.data(), vars.data() + vars.size());\n  const std::vector<std::complex<double>> vars_vector_complex(\n      vars_complex.data(), vars_complex.data() + vars_complex.size());\n  const std::vector<double> oriented_vars_vector =\n      orient_variables(vars_vector, extents, orientation_map);\n  const std::vector<std::complex<double>> oriented_vars_vector_complex =\n      orient_variables(vars_vector_complex, extents, orientation_map);\n  const std::vector<double> expected_vars_vector(\n      expected_vars.data(), expected_vars.data() + expected_vars.size());\n  const std::vector<std::complex<double>> expected_vars_vector_complex(\n      expected_vars_complex.data(),\n      expected_vars_complex.data() + expected_vars_complex.size());\n  CHECK(oriented_vars_vector == expected_vars_vector);\n  CHECK(oriented_vars_vector_complex == expected_vars_vector_complex);\n\n  const DataVector vars_dv(vars.data(), vars.size());\n  const ComplexDataVector vars_cdv(vars_complex.data(), vars_complex.size());\n  const DataVector oriented_vars_dv =\n      orient_variables(vars_dv, extents, orientation_map);\n  const ComplexDataVector oriented_vars_cdv =\n      orient_variables(vars_cdv, extents, orientation_map);\n  const DataVector expected_vars_dv(expected_vars.data(), expected_vars.size());\n  const ComplexDataVector expected_vars_cdv(expected_vars_complex.data(),\n                                            expected_vars_complex.size());\n  CHECK(oriented_vars_dv == expected_vars_dv);\n  CHECK(oriented_vars_cdv == expected_vars_cdv);\n}\n\n// Test orient_variables using a general orientation.\n// The challenge for the general test is to easily create the expected oriented\n// tensor, ideally without using the same algorithm that is used in\n// `orient_variables`. We do this by computing the coordinates of a rectangular\n// grid using two different coordinate maps and orientations of the grid,\n// this provides both the input tensor and the expected output tensor.\nvoid test_2d_with_orientation(const OrientationMap<2>& orientation_map) {\n  const auto extents = Index<2>{3, 4};\n  const auto mesh = Mesh<2>(extents.indices(), Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto);\n  const auto affine =\n      Affine2D{Affine(-1.0, 1.0, 2.3, 4.5), Affine(-1.0, 1.0, 0.8, 3.1)};\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          affine);\n  const auto logical_coords = logical_coordinates(mesh);\n\n  Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 2>>> vars(\n      extents.product());\n  // Fill ScalarTensor with x-coordinate values\n  get(get<ScalarTensor<DataVector>>(vars)) = get<0>(map(logical_coords));\n  get<Vector<DataVector, 2>>(vars) = map(logical_coordinates(mesh));\n  const auto oriented_vars = orient_variables(vars, extents, orientation_map);\n\n  Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 2>>>\n      expected_vars(extents.product());\n  const std::array<DataVector, 2> oriented_logical_coords = discrete_rotation(\n      orientation_map,\n      std::array{get<0>(logical_coordinates(orientation_map(mesh))),\n                 get<1>(logical_coordinates(orientation_map(mesh)))});\n  const auto oriented_mapped_coords = map(\n      tnsr::I<DataVector, 2, Frame::ElementLogical>{oriented_logical_coords});\n  get(get<ScalarTensor<DataVector>>(expected_vars)) = oriented_mapped_coords[0];\n  get<0>(get<Vector<DataVector, 2>>(expected_vars)) = oriented_mapped_coords[0];\n  get<1>(get<Vector<DataVector, 2>>(expected_vars)) = oriented_mapped_coords[1];\n  CHECK(oriented_vars == expected_vars);\n\n  const std::vector<double> vars_vector(vars.data(), vars.data() + vars.size());\n  const std::vector<double> oriented_vars_vector =\n      orient_variables(vars_vector, extents, orientation_map);\n  const std::vector<double> expected_vars_vector(\n      expected_vars.data(), expected_vars.data() + expected_vars.size());\n  CHECK(oriented_vars_vector == expected_vars_vector);\n\n  const DataVector vars_dv(vars.data(), vars.size());\n  const DataVector oriented_vars_dv =\n      orient_variables(vars_dv, extents, orientation_map);\n  const DataVector expected_vars_dv(expected_vars.data(), expected_vars.size());\n  CHECK(oriented_vars_dv == expected_vars_dv);\n\n#ifdef SPECTRE_DEBUG\n  {\n    DataVector vars_datavector{vars_vector.size()};\n    std::copy(vars_vector.begin(), vars_vector.end(), vars_datavector.begin());\n    DataVector oriented_vars_datavector{};\n    CHECK_THROWS_WITH(\n        orient_variables(make_not_null(&oriented_vars_datavector),\n                         vars_datavector, extents, orientation_map),\n        Catch::Matchers::ContainsSubstring(\"Result should have size\"));\n\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n    const DataVector vars_datavector_bad_size(const_cast<double*>(vars.data()),\n                                              vars.size() - 1);\n    oriented_vars_datavector = DataVector{vars.size() - 1};\n    CHECK_THROWS_WITH(\n        orient_variables(make_not_null(&oriented_vars_datavector),\n                         vars_datavector_bad_size, extents, orientation_map),\n        Catch::Matchers::ContainsSubstring(\n            \"The size of the variables must be divisible by the \"\n            \"number of grid points. Number of grid points: \"));\n  }\n#endif  // SPECTRE_DEBUG\n}\n\nvoid test_2d_orient_variables() {\n  size_t number_of_orientations_checked = 0;\n  auto dimensions = make_array(0_st, 1_st);\n  do {\n    CAPTURE(dimensions);\n    for (const auto& side_1 : {Side::Lower, Side::Upper}) {\n      const auto dir_1 = Direction<2>(dimensions[0], side_1);\n      for (const auto& side_2 : {Side::Lower, Side::Upper}) {\n        const auto dir_2 = Direction<2>(dimensions[1], side_2);\n        const OrientationMap<2> orientation_map(\n            std::array<Direction<2>, 2>{{dir_1, dir_2}});\n        CAPTURE(orientation_map);\n        test_2d_with_orientation(orientation_map);\n        number_of_orientations_checked++;\n      }\n    }\n  } while (std::next_permutation(dimensions.begin(), dimensions.end()));\n  CHECK(number_of_orientations_checked == 8);\n}\n\n// Test one case by hand. This test case is redundant with (though not\n// identical to) one of the cases hit by `test_3d_orient_variables`. However, by\n// writing it out by hand, we provide a sanity check and a clearer example of\n// how to use `orient_variables`.\nvoid test_3d_orient_variables_simple_case_by_hand() {\n  const OrientationMap<3> orientation_map(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_zeta(), Direction<3>::upper_eta(),\n       Direction<3>::upper_xi()}});\n  const auto extents = Index<3>{2, 3, 4};\n\n  Variables<tmpl::list<ScalarTensor<DataVector>>> vars(24);\n  get(get<ScalarTensor<DataVector>>(vars)) = DataVector{\n      {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,  9.0,  10.0, 11.0,\n       12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0}};\n  const auto oriented_vars = orient_variables(vars, extents, orientation_map);\n\n  Variables<tmpl::list<ScalarTensor<DataVector>>> expected_vars(24);\n  get(get<ScalarTensor<DataVector>>(expected_vars)) = DataVector{\n      {0.0, 6.0, 12.0, 18.0, 2.0, 8.0, 14.0, 20.0, 4.0, 10.0, 16.0, 22.0,\n       1.0, 7.0, 13.0, 19.0, 3.0, 9.0, 15.0, 21.0, 5.0, 11.0, 17.0, 23.0}};\n  CHECK(oriented_vars == expected_vars);\n\n  const std::vector<double> vars_vector(vars.data(), vars.data() + vars.size());\n  const std::vector<double> oriented_vars_vector =\n      orient_variables(vars_vector, extents, orientation_map);\n  const std::vector<double> expected_vars_vector(\n      expected_vars.data(), expected_vars.data() + expected_vars.size());\n  CHECK(oriented_vars_vector == expected_vars_vector);\n\n  const DataVector vars_dv(vars.data(), vars.size());\n  const DataVector oriented_vars_dv =\n      orient_variables(vars_dv, extents, orientation_map);\n  const DataVector expected_vars_dv(expected_vars.data(), expected_vars.size());\n  CHECK(oriented_vars_dv == expected_vars_dv);\n}\n\n// Test orient_variables using a general orientation.\n// The challenge for the general test is to easily create the expected oriented\n// tensor, ideally without using the same algorithm that is used in\n// `orient_variables`. We do this by computing the coordinates of a rectangular\n// grid using two different coordinate maps and orientations of the grid,\n// this provides both the input tensor and the expected output tensor.\nvoid test_3d_with_orientation(const OrientationMap<3>& orientation_map) {\n  const auto extents = Index<3>{2, 3, 4};\n  const auto mesh = Mesh<3>(extents.indices(), Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto);\n  const auto affine =\n      Affine3D{Affine(-1.0, 1.0, 2.3, 4.5), Affine(-1.0, 1.0, 0.8, 3.1),\n               Affine(-1.0, 1.0, -4.8, -3.9)};\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          affine);\n  const auto logical_coords = logical_coordinates(mesh);\n\n  Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 3>>> vars(\n      extents.product());\n  // Fill ScalarTensor<DataVector> with x-coordinate values\n  get(get<ScalarTensor<DataVector>>(vars)) = get<0>(map(logical_coords));\n  get<Vector<DataVector, 3>>(vars) = map(logical_coords);\n  const auto oriented_vars = orient_variables(vars, extents, orientation_map);\n\n  Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 3>>>\n      expected_vars(extents.product());\n  const std::array<DataVector, 3> oriented_logical_coords = discrete_rotation(\n      orientation_map,\n      std::array{get<0>(logical_coordinates(orientation_map(mesh))),\n                 get<1>(logical_coordinates(orientation_map(mesh))),\n                 get<2>(logical_coordinates(orientation_map(mesh)))});\n  const auto oriented_mapped_coords = map(\n      tnsr::I<DataVector, 3, Frame::ElementLogical>{oriented_logical_coords});\n  get(get<ScalarTensor<DataVector>>(expected_vars)) = oriented_mapped_coords[0];\n  get<0>(get<Vector<DataVector, 3>>(expected_vars)) = oriented_mapped_coords[0];\n  get<1>(get<Vector<DataVector, 3>>(expected_vars)) = oriented_mapped_coords[1];\n  get<2>(get<Vector<DataVector, 3>>(expected_vars)) = oriented_mapped_coords[2];\n  CHECK(oriented_vars == expected_vars);\n\n#ifdef SPECTRE_DEBUG\n  {\n    DataVector vars_datavector{vars.size()};\n    std::copy(vars.data(), vars.data() + vars.size(), vars_datavector.begin());\n    DataVector oriented_vars_datavector{};\n    CHECK_THROWS_WITH(\n        orient_variables(make_not_null(&oriented_vars_datavector),\n                         vars_datavector, extents, orientation_map),\n        Catch::Matchers::ContainsSubstring(\"Result should have size\"));\n\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n    const DataVector vars_datavector_bad_size(const_cast<double*>(vars.data()),\n                                              vars.size() - 1);\n    oriented_vars_datavector = DataVector{vars.size() - 1};\n    CHECK_THROWS_WITH(\n        orient_variables(make_not_null(&oriented_vars_datavector),\n                         vars_datavector_bad_size, extents, orientation_map),\n        Catch::Matchers::ContainsSubstring(\n            \"The size of the variables must be divisible by the \"\n            \"number of grid points. Number of grid points: \"));\n  }\n#endif  // SPECTRE_DEBUG\n}\n\nvoid test_3d_orient_variables() {\n  size_t number_of_orientations_checked = 0;\n  auto dimensions = make_array(0_st, 1_st, 2_st);\n  do {\n    CAPTURE(dimensions);\n    for (const auto& side_1 : {Side::Lower, Side::Upper}) {\n      const auto dir_1 = Direction<3>(dimensions[0], side_1);\n      for (const auto& side_2 : {Side::Lower, Side::Upper}) {\n        const auto dir_2 = Direction<3>(dimensions[1], side_2);\n        for (const auto& side_3 : {Side::Lower, Side::Upper}) {\n          const auto dir_3 = Direction<3>(dimensions[2], side_3);\n          const OrientationMap<3> orientation_map(\n              std::array<Direction<3>, 3>{{dir_1, dir_2, dir_3}});\n          CAPTURE(orientation_map);\n          test_3d_with_orientation(orientation_map);\n          number_of_orientations_checked++;\n        }\n      }\n    }\n  } while (std::next_permutation(dimensions.begin(), dimensions.end()));\n  CHECK(number_of_orientations_checked == 48);\n}\n\ntemplate <typename TagsList, size_t Dim>\nvoid check_vector(const Variables<TagsList>& vars,\n                  const Variables<TagsList>& expected_vars,\n                  const Index<Dim - 1>& slice_extents, const size_t sliced_dim,\n                  const OrientationMap<Dim>& orientation_map) {\n  // NOLINTNEXTLINE\n  const std::vector<double> vars_vector{vars.data(), vars.data() + vars.size()};\n  const auto oriented_vars_vector = orient_variables_on_slice(\n      vars_vector, slice_extents, sliced_dim, orientation_map);\n  const std::vector<double> expected_vars_vector{\n      // NOLINTNEXTLINE\n      expected_vars.data(), expected_vars.data() + expected_vars.size()};\n  CHECK(oriented_vars_vector == expected_vars_vector);\n\n  const DataVector vars_dv(const_cast<double*>(vars.data()), vars.size());\n  const DataVector oriented_vars_dv = orient_variables_on_slice(\n      vars_dv, slice_extents, sliced_dim, orientation_map);\n  const DataVector expected_vars_dv(const_cast<double*>(expected_vars.data()),\n                                    expected_vars.size());\n  CHECK(oriented_vars_dv == expected_vars_dv);\n}\n\n// Test 0D slice of a 1D element\nvoid test_0d_orient_variables_on_slice() {\n  const Index<0> slice_extents{1};\n  Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 1>>> vars(\n      slice_extents.product());\n  get(get<ScalarTensor<DataVector>>(vars)) = DataVector{{-0.5}};\n  get<0>(get<Vector<DataVector, 1>>(vars)) = DataVector{{1.0}};\n  const auto slice_mesh = Mesh<0>{};\n  for (const auto& side : {Side::Lower, Side::Upper}) {\n    CAPTURE(side);\n    const OrientationMap<1> orientation_map(\n        std::array<Direction<1>, 1>{{Direction<1>(0, side)}});\n    CHECK(slice_mesh ==\n          orient_mesh_on_slice(slice_mesh, 0_st, orientation_map));\n    const auto oriented_vars =\n        orient_variables_on_slice(vars, slice_extents, 0, orientation_map);\n    // 1D boundary is a point, so no change expected\n    CHECK(oriented_vars == vars);\n    check_vector(vars, oriented_vars, slice_extents, 0, orientation_map);\n  }\n}\n\nvoid test_1d_slice_with_orientation(const OrientationMap<2>& orientation_map) {\n  const auto slice_extents = Index<1>{4};\n  const auto slice_mesh =\n      Mesh<1>(slice_extents.indices(), Spectral::Basis::Legendre,\n              Spectral::Quadrature::GaussLobatto);\n  const auto affine = Affine(-1.0, 1.0, 2.3, 4.5);\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          affine);\n\n  for (const size_t sliced_dim : {0_st, 1_st}) {\n    CAPTURE(sliced_dim);\n    CHECK(slice_mesh ==\n          orient_mesh_on_slice(slice_mesh, sliced_dim, orientation_map));\n    // Because `orientation_map` transforms between directions in the volume, we\n    // make a new OrientationMap that transforms between directions on the\n    // slices. In the case of a 1D slice, this is a 1D OrientationMap.\n    const size_t remaining_dim = 1 - sliced_dim;\n    const auto slice_orientation_map =\n        OrientationMap<1>(std::array<Direction<1>, 1>({{Direction<1>(\n            0, orientation_map(Direction<2>(remaining_dim, Side::Upper))\n                   .side())}}));\n    const auto map_oriented =\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n            domain::CoordinateMaps::DiscreteRotation<1>{slice_orientation_map},\n            affine);\n\n    Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 1>>> vars(\n        slice_extents.product());\n    // Fill ScalarTensor<DataVector> with x-coordinate values\n    get(get<ScalarTensor<DataVector>>(vars)) =\n        get<0>(map(logical_coordinates(slice_mesh)));\n    get<Vector<DataVector, 1>>(vars) = map(logical_coordinates(slice_mesh));\n    const auto oriented_vars = orient_variables_on_slice(\n        vars, slice_extents, sliced_dim, orientation_map);\n\n    Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 1>>>\n        expected_vars(slice_extents.product());\n    get(get<ScalarTensor<DataVector>>(expected_vars)) = get<0>(\n        map_oriented(logical_coordinates(slice_orientation_map(slice_mesh))));\n    get<Vector<DataVector, 1>>(expected_vars) =\n        map_oriented(logical_coordinates(slice_orientation_map(slice_mesh)));\n    CHECK(oriented_vars == expected_vars);\n\n    check_vector(vars, oriented_vars, slice_extents, sliced_dim,\n                 orientation_map);\n\n#ifdef SPECTRE_DEBUG\n    {\n      DataVector vars_datavector{vars.size()};\n      std::copy(vars.data(), vars.data() + vars.size(),\n                vars_datavector.begin());\n      DataVector oriented_vars_datavector{};\n      CHECK_THROWS_WITH(\n          orient_variables_on_slice(make_not_null(&oriented_vars_datavector),\n                                    vars_datavector, slice_extents, sliced_dim,\n                                    orientation_map),\n          Catch::Matchers::ContainsSubstring(\"Result should have size\"));\n\n      const DataVector vars_datavector_bad_size(\n          const_cast<double*>(vars.data()), vars.size() - 1);\n      oriented_vars_datavector = DataVector{vars.size() - 1};\n      CHECK_THROWS_WITH(\n          orient_variables_on_slice(make_not_null(&oriented_vars_datavector),\n                                    vars_datavector_bad_size, slice_extents,\n                                    sliced_dim, orientation_map),\n          Catch::Matchers::ContainsSubstring(\n              \"The size of the variables must be divisible by the \"\n              \"number of grid points. Number of grid points: \"));\n    }\n#endif  // SPECTRE_DEBUG\n  }\n}\n\n// Test 1D slice of a 2D element\nvoid test_1d_orient_variables_on_slice() {\n  size_t number_of_orientations_checked = 0;\n  auto dimensions = make_array(0_st, 1_st);\n  do {\n    CAPTURE(dimensions);\n    for (const auto& side_1 : {Side::Lower, Side::Upper}) {\n      const auto dir_1 = Direction<2>(dimensions[0], side_1);\n      for (const auto& side_2 : {Side::Lower, Side::Upper}) {\n        const auto dir_2 = Direction<2>(dimensions[1], side_2);\n        const OrientationMap<2> orientation_map(\n            std::array<Direction<2>, 2>{{dir_1, dir_2}});\n        CAPTURE(orientation_map);\n        test_1d_slice_with_orientation(orientation_map);\n        number_of_orientations_checked++;\n      }\n    }\n  } while (std::next_permutation(dimensions.begin(), dimensions.end()));\n  CHECK(number_of_orientations_checked == 8);\n}\n\nvoid test_2d_slice_with_orientation(const OrientationMap<3>& orientation_map) {\n  const auto volume_extents = Index<3>{3, 4, 5};\n  const auto volume_mesh =\n      Mesh<3>(volume_extents.indices(),\n              {{Spectral::Basis::Chebyshev, Spectral::Basis::Legendre,\n                Spectral::Basis::FiniteDifference}},\n              {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n                Spectral::Quadrature::CellCentered}});\n  const auto oriented_mesh = orientation_map(volume_mesh);\n  const auto affine =\n      Affine2D{Affine(-1.0, 1.0, 2.3, 4.5), Affine(-1.0, 1.0, 0.8, 3.1)};\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          affine);\n\n  for (const size_t sliced_dim : {0_st, 1_st, 2_st}) {\n    CAPTURE(sliced_dim);\n    const auto slice_mesh = volume_mesh.slice_away(sliced_dim);\n    const auto& slice_extents = slice_mesh.extents();\n    CAPTURE(slice_mesh);\n    CAPTURE(slice_extents);\n    const auto oriented_sliced_dim = orientation_map(sliced_dim);\n    const auto expected_mesh = oriented_mesh.slice_away(oriented_sliced_dim);\n    CHECK(expected_mesh ==\n          orient_mesh_on_slice(slice_mesh, sliced_dim, orientation_map));\n\n    // Because `orientation_map` transforms between directions in the volume, we\n    // make a new OrientationMap that transforms between directions on the\n    // slices. In the case of a 2D slice, this is a 2D OrientationMap.\n    const auto slice_orientation_map = [&orientation_map, &sliced_dim]() {\n      const auto dims_of_slice =\n          (sliced_dim == 0 ? make_array(1_st, 2_st)\n                           : (sliced_dim == 1 ? make_array(0_st, 2_st)\n                                              : make_array(0_st, 1_st)));\n      const auto neighbor_dims_of_slice = make_array(\n          orientation_map(dims_of_slice[0]), orientation_map(dims_of_slice[1]));\n      const bool neighbor_axes_transposed =\n          (neighbor_dims_of_slice[1] < neighbor_dims_of_slice[0]);\n      const size_t neighbor_first_slice_dim = neighbor_axes_transposed ? 1 : 0;\n      const size_t neighbor_second_slice_dim = neighbor_axes_transposed ? 0 : 1;\n\n      return OrientationMap<2>(std::array<Direction<2>, 2>(\n          {{Direction<2>(\n                neighbor_first_slice_dim,\n                orientation_map(Direction<3>(dims_of_slice[0], Side::Upper))\n                    .side()),\n            Direction<2>(\n                neighbor_second_slice_dim,\n                orientation_map(Direction<3>(dims_of_slice[1], Side::Upper))\n                    .side())}}));\n    }();\n\n    const auto logical_coords = logical_coordinates(slice_mesh);\n    const std::array<DataVector, 2> oriented_logical_coords = discrete_rotation(\n        slice_orientation_map,\n        std::array{\n            get<0>(logical_coordinates(slice_orientation_map(slice_mesh))),\n            get<1>(logical_coordinates(slice_orientation_map(slice_mesh)))});\n    const auto oriented_mapped_coords = map(\n        tnsr::I<DataVector, 2, Frame::ElementLogical>{oriented_logical_coords});\n\n    Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 2>>> vars(\n        slice_extents.product());\n    // Fill ScalarTensor<DataVector> with x-coordinate values\n    get(get<ScalarTensor<DataVector>>(vars)) =\n        get<0>(map(logical_coordinates(slice_mesh)));\n    get<Vector<DataVector, 2>>(vars) = map(logical_coordinates(slice_mesh));\n    const auto oriented_vars = orient_variables_on_slice(\n        vars, slice_extents, sliced_dim, orientation_map);\n\n    Variables<tmpl::list<ScalarTensor<DataVector>, Vector<DataVector, 2>>>\n        expected_vars(slice_extents.product());\n    get(get<ScalarTensor<DataVector>>(expected_vars)) =\n        oriented_mapped_coords[0];\n    get<0>(get<Vector<DataVector, 2>>(expected_vars)) =\n        oriented_mapped_coords[0];\n    get<1>(get<Vector<DataVector, 2>>(expected_vars)) =\n        oriented_mapped_coords[1];\n    CHECK_VARIABLES_APPROX(oriented_vars, expected_vars);\n\n    check_vector(vars, oriented_vars, slice_extents, sliced_dim,\n                 orientation_map);\n\n#ifdef SPECTRE_DEBUG\n    {\n      DataVector vars_datavector{vars.size()};\n      std::copy(vars.data(), vars.data() + vars.size(),\n                vars_datavector.begin());\n      DataVector oriented_vars_datavector{};\n      CHECK_THROWS_WITH(\n          orient_variables_on_slice(make_not_null(&oriented_vars_datavector),\n                                    vars_datavector, slice_extents, sliced_dim,\n                                    orientation_map),\n          Catch::Matchers::ContainsSubstring(\"Result should have size\"));\n\n      const DataVector vars_datavector_bad_size(\n          const_cast<double*>(vars.data()), vars.size() - 1);\n      oriented_vars_datavector = DataVector{vars.size() - 1};\n      CHECK_THROWS_WITH(\n          orient_variables_on_slice(make_not_null(&oriented_vars_datavector),\n                                    vars_datavector_bad_size, slice_extents,\n                                    sliced_dim, orientation_map),\n          Catch::Matchers::ContainsSubstring(\n              \"The size of the variables must be divisible by the \"\n              \"number of grid points. Number of grid points: \"));\n    }\n#endif  // SPECTRE_DEBUG\n  }\n}\n\n// Test 2D slice of a 3D element\nvoid test_2d_orient_variables_on_slice() {\n  size_t number_of_orientations_checked = 0;\n  auto dimensions = make_array(0_st, 1_st, 2_st);\n  do {\n    CAPTURE(dimensions);\n    for (const auto& side_1 : {Side::Lower, Side::Upper}) {\n      const auto dir_1 = Direction<3>(dimensions[0], side_1);\n      for (const auto& side_2 : {Side::Lower, Side::Upper}) {\n        const auto dir_2 = Direction<3>(dimensions[1], side_2);\n        for (const auto& side_3 : {Side::Lower, Side::Upper}) {\n          const auto dir_3 = Direction<3>(dimensions[2], side_3);\n          const OrientationMap<3> orientation_map(\n              std::array<Direction<3>, 3>{{dir_1, dir_2, dir_3}});\n          CAPTURE(orientation_map);\n          test_2d_slice_with_orientation(orientation_map);\n          number_of_orientations_checked++;\n        }\n      }\n    }\n  } while (std::next_permutation(dimensions.begin(), dimensions.end()));\n  CHECK(number_of_orientations_checked == 48);\n}\n\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.OrientationMapHelpers\",\n                  \"[Domain][Unit]\") {\n  SECTION(\"Testing orient_variables\") {\n    test_1d_orient_variables();\n    test_2d_orient_variables();\n    test_3d_orient_variables();\n    test_2d_orient_variables_simple_case_by_hand();\n    test_3d_orient_variables_simple_case_by_hand();\n  }\n\n  SECTION(\"Testing orient_variables_on_slice\") {\n    // Each of these tests closely follows the corresponding volume test\n    // e.g., test_1d_orient_variables_on_slice -> test_2d_orient_variables\n    test_0d_orient_variables_on_slice();\n    test_1d_orient_variables_on_slice();\n    test_2d_orient_variables_on_slice();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_SegmentId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <unordered_set>\n\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace {\nvoid test_errors() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(SegmentId(3, 8), Catch::Matchers::ContainsSubstring(\n                                         \"index = 8, refinement_level = 3\"));\n  CHECK_THROWS_WITH(\n      SegmentId(0, 0).id_of_parent(),\n      Catch::Matchers::ContainsSubstring(\"on root refinement level!\"));\n  CHECK_THROWS_WITH(\n      SegmentId(0, 0).id_of_sibling(),\n      Catch::Matchers::ContainsSubstring(\n          \"The segment on the root refinement level has no sibling\"));\n  CHECK_THROWS_WITH(\n      SegmentId(0, 0).id_of_abutting_nibling(),\n      Catch::Matchers::ContainsSubstring(\n          \"The segment on the root refinement level has no abutting nibling\"));\n  CHECK_THROWS_WITH(\n      SegmentId(0, 0).side_of_sibling(),\n      Catch::Matchers::ContainsSubstring(\n          \"The segment on the root refinement level has no sibling\"));\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.SegmentId\", \"[Domain][Unit]\") {\n  // Test equality operator:\n  SegmentId segment_one(4, 3);\n  SegmentId segment_two(4, 3);\n  SegmentId segment_three(4, 0);\n  SegmentId segment_four(5, 4);\n  CHECK(segment_one == segment_two);\n  CHECK(segment_two != segment_three);\n  CHECK(segment_two != segment_four);\n\n  // Test pup operations:\n  test_serialization(segment_one);\n\n  // Test parent and child operations:\n  for (size_t level = 0; level < 5; ++level) {\n    const double segment_length = 2.0 / two_to_the(level);\n    double midpoint = -1.0 + 0.5 * segment_length;\n    for (size_t segment_index = 0; segment_index < two_to_the(level);\n         ++segment_index) {\n      SegmentId id(level, segment_index);\n      CHECK(id.midpoint() == midpoint);\n      CHECK((id.endpoint(Side::Upper) + id.endpoint(Side::Lower)) / 2. ==\n            midpoint);\n      CHECK(id.endpoint(Side::Upper) - id.endpoint(Side::Lower) ==\n            segment_length);\n      midpoint += segment_length;\n      CHECK(id == id.id_of_child(Side::Lower).id_of_parent());\n      CHECK(id == id.id_of_child(Side::Upper).id_of_parent());\n      CHECK(overlapping(id, id));\n      if (0 != level) {\n        CHECK(overlapping(id, id.id_of_parent()));\n        CHECK(overlapping(id.id_of_parent(), id));\n        const Side side_of_parent =\n            0 == segment_index % 2 ? Side::Lower : Side::Upper;\n        CHECK(id == id.id_of_parent().id_of_child(side_of_parent));\n        CHECK(id.id_of_sibling() ==\n              id.id_of_parent().id_of_child(opposite(side_of_parent)));\n        CHECK_FALSE(overlapping(id, id.id_of_sibling()));\n      }\n      CHECK(id.id_of_child(Side::Lower).id_of_sibling() ==\n            id.id_of_child(Side::Upper));\n      CHECK(id.id_of_child(Side::Upper).id_of_sibling() ==\n            id.id_of_child(Side::Lower));\n      CHECK(id.id_of_child(Side::Lower).id_of_abutting_nibling() ==\n            id.id_of_child(Side::Upper).id_of_child(Side::Lower));\n      CHECK(id.id_of_child(Side::Upper).id_of_abutting_nibling() ==\n            id.id_of_child(Side::Lower).id_of_child(Side::Upper));\n      CHECK(id.id_of_child(Side::Lower).side_of_sibling() == Side::Upper);\n      CHECK(id.id_of_child(Side::Upper).side_of_sibling() == Side::Lower);\n    }\n  }\n\n  // Test retrieval functions:\n  SegmentId level_2_index_3(2, 3);\n  CHECK(level_2_index_3.refinement_level() == 2);\n  CHECK(level_2_index_3.index() == 3);\n\n  // Test output operator:\n  SegmentId level_3_index_2(3, 2);\n  CHECK(get_output(level_3_index_2) == \"L3I2\");\n\n  {\n    INFO(\"Hash\");\n    CHECK(std::unordered_set<SegmentId>{SegmentId{2, 3}, SegmentId{2, 3},\n                                        SegmentId{3, 2}} ==\n          std::unordered_set<SegmentId>{SegmentId{3, 2}, SegmentId{2, 3}});\n  }\n\n  test_errors();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_Side.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Domain/Structure/Side.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.Side\", \"[Domain][Unit]\") {\n  Side side_lower = Side::Lower;\n  CHECK(opposite(side_lower) == Side::Upper);\n  CHECK(opposite(opposite(side_lower)) == Side::Lower);\n  CHECK(opposite(Side::Self) == Side::Self);\n  CHECK(get_output(side_lower) == \"Lower\");\n  CHECK(get_output(Side::Upper) == \"Upper\");\n  CHECK(get_output(Side::Uninitialized) == \"Uninitialized\");\n  CHECK(get_output(Side::Self) == \"Self\");\n  CHECK_THROWS_WITH(opposite(Side::Uninitialized),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot get the opposite of Side::Uninitialized\"));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_Topology.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.Topology\", \"[Domain][Unit]\") {\n  CHECK(get_output(domain::Topology::Uninitialized) == \"Uninitialized\");\n  CHECK(get_output(domain::Topology::I1) == \"I1\");\n  CHECK(get_output(domain::Topology::S1) == \"S1\");\n  CHECK(get_output(domain::Topology::S2Colatitude) == \"S2Colatitude\");\n  CHECK(get_output(domain::Topology::S2Longitude) == \"S2Longitude\");\n  CHECK(get_output(domain::Topology::B2Radial) == \"B2Radial\");\n  CHECK(get_output(domain::Topology::B2Angular) == \"B2Angular\");\n  CHECK(get_output(domain::Topology::B3Radial) == \"B3Radial\");\n  CHECK(get_output(domain::Topology::B3Colatitude) == \"B3Colatitude\");\n  CHECK(get_output(domain::Topology::B3Longitude) == \"B3Longitude\");\n  CHECK(get_output(domain::Topology::CartoonSphere) == \"CartoonSphere\");\n  CHECK(get_output(domain::Topology::CartoonCylinder) == \"CartoonCylinder\");\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_TrimMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/TrimMap.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_remove_nonexistent_neighbors() {\n  DirectionalIdMap<Dim, int> map_to_trim{};\n\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    ElementId<Dim> id{i + 1, {}};\n    neighbors[gsl::at(Direction<Dim>::all_directions(), i)] =\n        Neighbors<Dim>{{id}, OrientationMap<Dim>::create_aligned()};\n    map_to_trim[DirectionalId<Dim>{gsl::at(Direction<Dim>::all_directions(), i),\n                                   id}] = i * i + 10;  // Assign some number\n  }\n  const Element<Dim> element{ElementId<Dim>{0, {}}, neighbors};\n  // Add extra neighbors that will be removed\n  for (size_t i = 0; i < 2 * Dim and Dim > 1; ++i) {\n    ElementId<Dim> id{i + 100, {}};\n    map_to_trim[DirectionalId<Dim>{gsl::at(Direction<Dim>::all_directions(), i),\n                                   id}] = i * i + 1000;  // Assign some number\n  }\n  REQUIRE(map_to_trim.size() == (Dim == 1 ? 2 : 4) * Dim);\n  domain::remove_nonexistent_neighbors(make_not_null(&map_to_trim), element);\n\n  // Check that all expected neighbors are there\n  for (const auto& [direction, neighbors_in_direction] : element.neighbors()) {\n    for (const auto& neighbor : neighbors_in_direction) {\n      CHECK(map_to_trim.contains(DirectionalId<Dim>{direction, neighbor}));\n    }\n  }\n  CHECK(map_to_trim.size() == 2 * Dim);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Structure.TrimMap\", \"[Domain][Unit]\") {\n  test_remove_nonexistent_neighbors<1>();\n  test_remove_nonexistent_neighbors<2>();\n  test_remove_nonexistent_neighbors<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Structure/Test_ZCurve.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/ZCurve.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\n// Test the Z-curve index computed for ElementIds in 1D. Tests\n// domain::z_curve_index.\nvoid test_z_curve_index_1d() {\n  // The Z-curve does not depend on the block ID or grid index, so the choice of\n  // these two values for this test is arbitrary\n  const size_t block_id = 0;\n  const size_t grid_index = 0;\n\n  // Create SegmentIds from refinement 0 to 2\n  // l0i0 refers to refinement 0, at index 0\n  SegmentId l0i0(0, 0);\n\n  SegmentId l1i0(1, 0);\n  SegmentId l1i1(1, 1);\n\n  SegmentId l2i0(2, 0);\n  SegmentId l2i1(2, 1);\n  SegmentId l2i2(2, 2);\n  SegmentId l2i3(2, 3);\n\n  // Create ElementIds with 1D SegmentIds from refinement 0 to 2\n  const ElementId<1> e_l0x0(block_id, make_array<1>(l0i0), grid_index);\n\n  const ElementId<1> e_l1x0(block_id, make_array<1>(l1i0), grid_index);\n  const ElementId<1> e_l1x1(block_id, make_array<1>(l1i1), grid_index);\n\n  const ElementId<1> e_l2x0(block_id, make_array<1>(l2i0), grid_index);\n  const ElementId<1> e_l2x1(block_id, make_array<1>(l2i1), grid_index);\n  const ElementId<1> e_l2x2(block_id, make_array<1>(l2i2), grid_index);\n  const ElementId<1> e_l2x3(block_id, make_array<1>(l2i3), grid_index);\n\n  // Check that ElementIds are mapped to their correct Z-curve index\n  CHECK(domain::z_curve_index(e_l0x0) == 0);\n\n  CHECK(domain::z_curve_index(e_l1x0) == 0);\n  CHECK(domain::z_curve_index(e_l1x1) == 1);\n\n  CHECK(domain::z_curve_index(e_l2x0) == 0);\n  CHECK(domain::z_curve_index(e_l2x1) == 1);\n  CHECK(domain::z_curve_index(e_l2x2) == 2);\n  CHECK(domain::z_curve_index(e_l2x3) == 3);\n}\n\n// Test the Z-curve index computed for ElementIds in 2D. Tests\n// domain::z_curve_index.\nvoid test_z_curve_index_2d() {\n  // The Z-curve does not depend on the block ID or grid index, so the choice of\n  // these two values for this test is arbitrary\n  const size_t block_id = 0;\n  const size_t grid_index = 0;\n\n  // Create SegmentIds from refinement 0 to 2\n  // l0i0 refers to refinement 0, at index 0\n  SegmentId l0i0(0, 0);\n\n  SegmentId l1i0(1, 0);\n  SegmentId l1i1(1, 1);\n\n  SegmentId l2i0(2, 0);\n  SegmentId l2i1(2, 1);\n  SegmentId l2i2(2, 2);\n  SegmentId l2i3(2, 3);\n\n  // Create ElementIds with 2D SegmentIds from refinement 0 to 2 and check that\n  // ElementIds are mapped to their correct Z-curve index\n  const ElementId<2> e_l0x0_l0y0(block_id, make_array(l0i0, l0i0), grid_index);\n  CHECK(domain::z_curve_index(e_l0x0_l0y0) == 0);\n\n  const ElementId<2> e_l1x0_l0y0(block_id, make_array(l1i0, l0i0), grid_index);\n  const ElementId<2> e_l1x1_l0y0(block_id, make_array(l1i1, l0i0), grid_index);\n  CHECK(domain::z_curve_index(e_l1x0_l0y0) == 0);\n  CHECK(domain::z_curve_index(e_l1x1_l0y0) == 1);\n\n  const ElementId<2> e_l0x0_l1y0(block_id, make_array(l0i0, l1i0), grid_index);\n  const ElementId<2> e_l0x0_l1y1(block_id, make_array(l0i0, l1i1), grid_index);\n  CHECK(domain::z_curve_index(e_l0x0_l1y0) == 0);\n  CHECK(domain::z_curve_index(e_l0x0_l1y1) == 1);\n\n  const ElementId<2> e_l1x0_l1y0(block_id, make_array(l1i0, l1i0), grid_index);\n  const ElementId<2> e_l1x1_l1y0(block_id, make_array(l1i1, l1i0), grid_index);\n  const ElementId<2> e_l1x0_l1y1(block_id, make_array(l1i0, l1i1), grid_index);\n  CHECK(domain::z_curve_index(e_l1x0_l1y0) == 0);\n  CHECK(domain::z_curve_index(e_l1x1_l1y0) == 1);\n  CHECK(domain::z_curve_index(e_l1x0_l1y1) == 2);\n\n  const ElementId<2> e_l2x0_l0y0(block_id, make_array(l2i0, l0i0), grid_index);\n  const ElementId<2> e_l2x1_l0y0(block_id, make_array(l2i1, l0i0), grid_index);\n  const ElementId<2> e_l2x2_l0y0(block_id, make_array(l2i2, l0i0), grid_index);\n  const ElementId<2> e_l2x3_l0y0(block_id, make_array(l2i3, l0i0), grid_index);\n  CHECK(domain::z_curve_index(e_l2x0_l0y0) == 0);\n  CHECK(domain::z_curve_index(e_l2x1_l0y0) == 1);\n  CHECK(domain::z_curve_index(e_l2x2_l0y0) == 2);\n  CHECK(domain::z_curve_index(e_l2x3_l0y0) == 3);\n\n  const ElementId<2> e_l2x0_l1y0(block_id, make_array(l2i0, l1i0), grid_index);\n  const ElementId<2> e_l2x1_l1y0(block_id, make_array(l2i1, l1i0), grid_index);\n  const ElementId<2> e_l2x2_l1y0(block_id, make_array(l2i2, l1i0), grid_index);\n  const ElementId<2> e_l2x3_l1y0(block_id, make_array(l2i3, l1i0), grid_index);\n  CHECK(domain::z_curve_index(e_l2x0_l1y0) == 0);\n  CHECK(domain::z_curve_index(e_l2x1_l1y0) == 2);\n  CHECK(domain::z_curve_index(e_l2x2_l1y0) == 4);\n  CHECK(domain::z_curve_index(e_l2x3_l1y0) == 6);\n\n  const ElementId<2> e_l2x0_l1y1(block_id, make_array(l2i0, l1i1), grid_index);\n  const ElementId<2> e_l2x1_l1y1(block_id, make_array(l2i1, l1i1), grid_index);\n  const ElementId<2> e_l2x2_l1y1(block_id, make_array(l2i2, l1i1), grid_index);\n  const ElementId<2> e_l2x3_l1y1(block_id, make_array(l2i3, l1i1), grid_index);\n  CHECK(domain::z_curve_index(e_l2x0_l1y1) == 1);\n  CHECK(domain::z_curve_index(e_l2x1_l1y1) == 3);\n  CHECK(domain::z_curve_index(e_l2x2_l1y1) == 5);\n  CHECK(domain::z_curve_index(e_l2x3_l1y1) == 7);\n\n  const ElementId<2> e_l0x0_l2y0(block_id, make_array(l0i0, l2i0), grid_index);\n  const ElementId<2> e_l0x0_l2y1(block_id, make_array(l0i0, l2i1), grid_index);\n  const ElementId<2> e_l0x0_l2y2(block_id, make_array(l0i0, l2i2), grid_index);\n  const ElementId<2> e_l0x0_l2y3(block_id, make_array(l0i0, l2i3), grid_index);\n  CHECK(domain::z_curve_index(e_l0x0_l2y0) == 0);\n  CHECK(domain::z_curve_index(e_l0x0_l2y1) == 1);\n  CHECK(domain::z_curve_index(e_l0x0_l2y2) == 2);\n  CHECK(domain::z_curve_index(e_l0x0_l2y3) == 3);\n\n  const ElementId<2> e_l1x0_l2y0(block_id, make_array(l1i0, l2i0), grid_index);\n  const ElementId<2> e_l1x0_l2y1(block_id, make_array(l1i0, l2i1), grid_index);\n  const ElementId<2> e_l1x0_l2y2(block_id, make_array(l1i0, l2i2), grid_index);\n  const ElementId<2> e_l1x0_l2y3(block_id, make_array(l1i0, l2i3), grid_index);\n  CHECK(domain::z_curve_index(e_l1x0_l2y0) == 0);\n  CHECK(domain::z_curve_index(e_l1x0_l2y1) == 2);\n  CHECK(domain::z_curve_index(e_l1x0_l2y2) == 4);\n  CHECK(domain::z_curve_index(e_l1x0_l2y3) == 6);\n\n  const ElementId<2> e_l1x1_l2y0(block_id, make_array(l1i1, l2i0), grid_index);\n  const ElementId<2> e_l1x1_l2y1(block_id, make_array(l1i1, l2i1), grid_index);\n  const ElementId<2> e_l1x1_l2y2(block_id, make_array(l1i1, l2i2), grid_index);\n  const ElementId<2> e_l1x1_l2y3(block_id, make_array(l1i1, l2i3), grid_index);\n  CHECK(domain::z_curve_index(e_l1x1_l2y0) == 1);\n  CHECK(domain::z_curve_index(e_l1x1_l2y1) == 3);\n  CHECK(domain::z_curve_index(e_l1x1_l2y2) == 5);\n  CHECK(domain::z_curve_index(e_l1x1_l2y3) == 7);\n\n  const ElementId<2> e_l2x0_l2y0(block_id, make_array(l2i0, l2i0), grid_index);\n  const ElementId<2> e_l2x1_l2y0(block_id, make_array(l2i1, l2i0), grid_index);\n  const ElementId<2> e_l2x2_l2y0(block_id, make_array(l2i2, l2i0), grid_index);\n  const ElementId<2> e_l2x3_l2y0(block_id, make_array(l2i3, l2i0), grid_index);\n  const ElementId<2> e_l2x0_l2y1(block_id, make_array(l2i0, l2i1), grid_index);\n  const ElementId<2> e_l2x1_l2y1(block_id, make_array(l2i1, l2i1), grid_index);\n  const ElementId<2> e_l2x2_l2y1(block_id, make_array(l2i2, l2i1), grid_index);\n  const ElementId<2> e_l2x3_l2y1(block_id, make_array(l2i3, l2i1), grid_index);\n  const ElementId<2> e_l2x0_l2y2(block_id, make_array(l2i0, l2i2), grid_index);\n  const ElementId<2> e_l2x1_l2y2(block_id, make_array(l2i1, l2i2), grid_index);\n  const ElementId<2> e_l2x2_l2y2(block_id, make_array(l2i2, l2i2), grid_index);\n  const ElementId<2> e_l2x3_l2y2(block_id, make_array(l2i3, l2i2), grid_index);\n  const ElementId<2> e_l2x0_l2y3(block_id, make_array(l2i0, l2i3), grid_index);\n  const ElementId<2> e_l2x1_l2y3(block_id, make_array(l2i1, l2i3), grid_index);\n  const ElementId<2> e_l2x2_l2y3(block_id, make_array(l2i2, l2i3), grid_index);\n  const ElementId<2> e_l2x3_l2y3(block_id, make_array(l2i3, l2i3), grid_index);\n  CHECK(domain::z_curve_index(e_l2x0_l2y0) == 0);\n  CHECK(domain::z_curve_index(e_l2x1_l2y0) == 1);\n  CHECK(domain::z_curve_index(e_l2x2_l2y0) == 4);\n  CHECK(domain::z_curve_index(e_l2x3_l2y0) == 5);\n  CHECK(domain::z_curve_index(e_l2x0_l2y1) == 2);\n  CHECK(domain::z_curve_index(e_l2x1_l2y1) == 3);\n  CHECK(domain::z_curve_index(e_l2x2_l2y1) == 6);\n  CHECK(domain::z_curve_index(e_l2x3_l2y1) == 7);\n  CHECK(domain::z_curve_index(e_l2x0_l2y2) == 8);\n  CHECK(domain::z_curve_index(e_l2x1_l2y2) == 9);\n  CHECK(domain::z_curve_index(e_l2x2_l2y2) == 12);\n  CHECK(domain::z_curve_index(e_l2x3_l2y2) == 13);\n  CHECK(domain::z_curve_index(e_l2x0_l2y3) == 10);\n  CHECK(domain::z_curve_index(e_l2x1_l2y3) == 11);\n  CHECK(domain::z_curve_index(e_l2x2_l2y3) == 14);\n  CHECK(domain::z_curve_index(e_l2x3_l2y3) == 15);\n}\n\n// Test the Z-curve index computed for ElementIds in 3D. Tests\n// domain::z_curve_index.\nvoid test_z_curve_index_3d() {\n  // The Z-curve does not depend on the block ID or grid index, so the choice of\n  // these two values for this test is arbitrary\n  const size_t block_id = 0;\n  const size_t grid_index = 0;\n\n  // Create SegmentIds for 3D test case. Since the 1D and 2D test cases are\n  // exhaustive and exhaustively testing 3D in the same way is a lot to hard\n  // code, we instead test one interesting 3D case. Having the highest\n  // refinement (z ref = 4) at least 2 levels higher than the next highest\n  // refinement (x ref = 2) is an important test case where the highest bits\n  // of the Z-curve index come from one dimension, not an interleaving of more\n  // than one dimension's segment index bits. More concretely, the bits for the\n  // Z-curve index will be z3 z2 z1 x1 z0 x0, where the z3 z2 bits are not\n  // interleaved with another dimension's segment index.\n  const size_t x_refinement = 2;\n  const size_t y_refinement = 0;\n  const size_t z_refinement = 4;\n\n  SegmentId x0(x_refinement, 0);\n  SegmentId x1(x_refinement, 1);\n  SegmentId x2(x_refinement, 2);\n  SegmentId x3(x_refinement, 3);\n\n  SegmentId y0(y_refinement, 0);\n\n  SegmentId z0(z_refinement, 0);\n  SegmentId z1(z_refinement, 1);\n  SegmentId z2(z_refinement, 2);\n  SegmentId z3(z_refinement, 3);\n  SegmentId z4(z_refinement, 4);\n  SegmentId z5(z_refinement, 5);\n  SegmentId z6(z_refinement, 6);\n  SegmentId z7(z_refinement, 7);\n  SegmentId z8(z_refinement, 8);\n  SegmentId z9(z_refinement, 9);\n  SegmentId z10(z_refinement, 10);\n  SegmentId z11(z_refinement, 11);\n  SegmentId z12(z_refinement, 12);\n  SegmentId z13(z_refinement, 13);\n  SegmentId z14(z_refinement, 14);\n  SegmentId z15(z_refinement, 15);\n\n  // Create ElementIds with 3D SegmentIds from refinement 0 to 2 and check that\n  // ElementIds are mapped to their correct Z-curve index\n  // e_000 denotes segment indices x=0, y=0, z=0\n  const ElementId<3> e_000(block_id, make_array(x0, y0, z0), grid_index);\n  CHECK(domain::z_curve_index(e_000) == 0);\n  const ElementId<3> e_100(block_id, make_array(x1, y0, z0), grid_index);\n  CHECK(domain::z_curve_index(e_100) == 1);\n  const ElementId<3> e_200(block_id, make_array(x2, y0, z0), grid_index);\n  CHECK(domain::z_curve_index(e_200) == 4);\n  const ElementId<3> e_300(block_id, make_array(x3, y0, z0), grid_index);\n  CHECK(domain::z_curve_index(e_300) == 5);\n\n  const ElementId<3> e_001(block_id, make_array(x0, y0, z1), grid_index);\n  CHECK(domain::z_curve_index(e_001) == 2);\n  const ElementId<3> e_101(block_id, make_array(x1, y0, z1), grid_index);\n  CHECK(domain::z_curve_index(e_101) == 3);\n  const ElementId<3> e_201(block_id, make_array(x2, y0, z1), grid_index);\n  CHECK(domain::z_curve_index(e_201) == 6);\n  const ElementId<3> e_301(block_id, make_array(x3, y0, z1), grid_index);\n  CHECK(domain::z_curve_index(e_301) == 7);\n\n  const ElementId<3> e_002(block_id, make_array(x0, y0, z2), grid_index);\n  CHECK(domain::z_curve_index(e_002) == 8);\n  const ElementId<3> e_102(block_id, make_array(x1, y0, z2), grid_index);\n  CHECK(domain::z_curve_index(e_102) == 9);\n  const ElementId<3> e_202(block_id, make_array(x2, y0, z2), grid_index);\n  CHECK(domain::z_curve_index(e_202) == 12);\n  const ElementId<3> e_302(block_id, make_array(x3, y0, z2), grid_index);\n  CHECK(domain::z_curve_index(e_302) == 13);\n\n  const ElementId<3> e_003(block_id, make_array(x0, y0, z3), grid_index);\n  CHECK(domain::z_curve_index(e_003) == 10);\n  const ElementId<3> e_103(block_id, make_array(x1, y0, z3), grid_index);\n  CHECK(domain::z_curve_index(e_103) == 11);\n  const ElementId<3> e_203(block_id, make_array(x2, y0, z3), grid_index);\n  CHECK(domain::z_curve_index(e_203) == 14);\n  const ElementId<3> e_303(block_id, make_array(x3, y0, z3), grid_index);\n  CHECK(domain::z_curve_index(e_303) == 15);\n\n  const ElementId<3> e_004(block_id, make_array(x0, y0, z4), grid_index);\n  CHECK(domain::z_curve_index(e_004) == 16);\n  const ElementId<3> e_104(block_id, make_array(x1, y0, z4), grid_index);\n  CHECK(domain::z_curve_index(e_104) == 17);\n  const ElementId<3> e_204(block_id, make_array(x2, y0, z4), grid_index);\n  CHECK(domain::z_curve_index(e_204) == 20);\n  const ElementId<3> e_304(block_id, make_array(x3, y0, z4), grid_index);\n  CHECK(domain::z_curve_index(e_304) == 21);\n\n  const ElementId<3> e_005(block_id, make_array(x0, y0, z5), grid_index);\n  CHECK(domain::z_curve_index(e_005) == 18);\n  const ElementId<3> e_105(block_id, make_array(x1, y0, z5), grid_index);\n  CHECK(domain::z_curve_index(e_105) == 19);\n  const ElementId<3> e_205(block_id, make_array(x2, y0, z5), grid_index);\n  CHECK(domain::z_curve_index(e_205) == 22);\n  const ElementId<3> e_305(block_id, make_array(x3, y0, z5), grid_index);\n  CHECK(domain::z_curve_index(e_305) == 23);\n\n  const ElementId<3> e_006(block_id, make_array(x0, y0, z6), grid_index);\n  CHECK(domain::z_curve_index(e_006) == 24);\n  const ElementId<3> e_106(block_id, make_array(x1, y0, z6), grid_index);\n  CHECK(domain::z_curve_index(e_106) == 25);\n  const ElementId<3> e_206(block_id, make_array(x2, y0, z6), grid_index);\n  CHECK(domain::z_curve_index(e_206) == 28);\n  const ElementId<3> e_306(block_id, make_array(x3, y0, z6), grid_index);\n  CHECK(domain::z_curve_index(e_306) == 29);\n\n  const ElementId<3> e_007(block_id, make_array(x0, y0, z7), grid_index);\n  CHECK(domain::z_curve_index(e_007) == 26);\n  const ElementId<3> e_107(block_id, make_array(x1, y0, z7), grid_index);\n  CHECK(domain::z_curve_index(e_107) == 27);\n  const ElementId<3> e_207(block_id, make_array(x2, y0, z7), grid_index);\n  CHECK(domain::z_curve_index(e_207) == 30);\n  const ElementId<3> e_307(block_id, make_array(x3, y0, z7), grid_index);\n  CHECK(domain::z_curve_index(e_307) == 31);\n\n  const ElementId<3> e_008(block_id, make_array(x0, y0, z8), grid_index);\n  CHECK(domain::z_curve_index(e_008) == 32);\n  const ElementId<3> e_108(block_id, make_array(x1, y0, z8), grid_index);\n  CHECK(domain::z_curve_index(e_108) == 33);\n  const ElementId<3> e_208(block_id, make_array(x2, y0, z8), grid_index);\n  CHECK(domain::z_curve_index(e_208) == 36);\n  const ElementId<3> e_308(block_id, make_array(x3, y0, z8), grid_index);\n  CHECK(domain::z_curve_index(e_308) == 37);\n\n  const ElementId<3> e_009(block_id, make_array(x0, y0, z9), grid_index);\n  CHECK(domain::z_curve_index(e_009) == 34);\n  const ElementId<3> e_109(block_id, make_array(x1, y0, z9), grid_index);\n  CHECK(domain::z_curve_index(e_109) == 35);\n  const ElementId<3> e_209(block_id, make_array(x2, y0, z9), grid_index);\n  CHECK(domain::z_curve_index(e_209) == 38);\n  const ElementId<3> e_309(block_id, make_array(x3, y0, z9), grid_index);\n  CHECK(domain::z_curve_index(e_309) == 39);\n\n  const ElementId<3> e_0010(block_id, make_array(x0, y0, z10), grid_index);\n  CHECK(domain::z_curve_index(e_0010) == 40);\n  const ElementId<3> e_1010(block_id, make_array(x1, y0, z10), grid_index);\n  CHECK(domain::z_curve_index(e_1010) == 41);\n  const ElementId<3> e_2010(block_id, make_array(x2, y0, z10), grid_index);\n  CHECK(domain::z_curve_index(e_2010) == 44);\n  const ElementId<3> e_3010(block_id, make_array(x3, y0, z10), grid_index);\n  CHECK(domain::z_curve_index(e_3010) == 45);\n\n  const ElementId<3> e_0011(block_id, make_array(x0, y0, z11), grid_index);\n  CHECK(domain::z_curve_index(e_0011) == 42);\n  const ElementId<3> e_1011(block_id, make_array(x1, y0, z11), grid_index);\n  CHECK(domain::z_curve_index(e_1011) == 43);\n  const ElementId<3> e_2011(block_id, make_array(x2, y0, z11), grid_index);\n  CHECK(domain::z_curve_index(e_2011) == 46);\n  const ElementId<3> e_3011(block_id, make_array(x3, y0, z11), grid_index);\n  CHECK(domain::z_curve_index(e_3011) == 47);\n\n  const ElementId<3> e_0012(block_id, make_array(x0, y0, z12), grid_index);\n  CHECK(domain::z_curve_index(e_0012) == 48);\n  const ElementId<3> e_1012(block_id, make_array(x1, y0, z12), grid_index);\n  CHECK(domain::z_curve_index(e_1012) == 49);\n  const ElementId<3> e_2012(block_id, make_array(x2, y0, z12), grid_index);\n  CHECK(domain::z_curve_index(e_2012) == 52);\n  const ElementId<3> e_3012(block_id, make_array(x3, y0, z12), grid_index);\n  CHECK(domain::z_curve_index(e_3012) == 53);\n\n  const ElementId<3> e_0013(block_id, make_array(x0, y0, z13), grid_index);\n  CHECK(domain::z_curve_index(e_0013) == 50);\n  const ElementId<3> e_1013(block_id, make_array(x1, y0, z13), grid_index);\n  CHECK(domain::z_curve_index(e_1013) == 51);\n  const ElementId<3> e_2013(block_id, make_array(x2, y0, z13), grid_index);\n  CHECK(domain::z_curve_index(e_2013) == 54);\n  const ElementId<3> e_3013(block_id, make_array(x3, y0, z13), grid_index);\n  CHECK(domain::z_curve_index(e_3013) == 55);\n\n  const ElementId<3> e_0014(block_id, make_array(x0, y0, z14), grid_index);\n  CHECK(domain::z_curve_index(e_0014) == 56);\n  const ElementId<3> e_1014(block_id, make_array(x1, y0, z14), grid_index);\n  CHECK(domain::z_curve_index(e_1014) == 57);\n  const ElementId<3> e_2014(block_id, make_array(x2, y0, z14), grid_index);\n  CHECK(domain::z_curve_index(e_2014) == 60);\n  const ElementId<3> e_3014(block_id, make_array(x3, y0, z14), grid_index);\n  CHECK(domain::z_curve_index(e_3014) == 61);\n\n  const ElementId<3> e_0015(block_id, make_array(x0, y0, z15), grid_index);\n  CHECK(domain::z_curve_index(e_0015) == 58);\n  const ElementId<3> e_1015(block_id, make_array(x1, y0, z15), grid_index);\n  CHECK(domain::z_curve_index(e_1015) == 59);\n  const ElementId<3> e_2015(block_id, make_array(x2, y0, z15), grid_index);\n  CHECK(domain::z_curve_index(e_2015) == 62);\n  const ElementId<3> e_3015(block_id, make_array(x3, y0, z15), grid_index);\n  CHECK(domain::z_curve_index(e_3015) == 63);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.ZCurve\", \"[Domain][Unit]\") {\n  // Test transforming ElementId to Z-curve index\n  test_z_curve_index_1d();\n  test_z_curve_index_2d();\n  test_z_curve_index_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DomainTags\")\n\nset(LIBRARY_SOURCES\n  Test_ElementDistribution.cpp\n  Test_Faces.cpp\n  Test_NeighborMesh.cpp\n  Test_SurfaceJacobian.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainHelpers\n  DomainStructure\n  Parallel\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Domain/Tags/Test_ElementDistribution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <string>\n\n#include \"Domain/ElementDistribution.hpp\"\n#include \"Domain/Tags/ElementDistribution.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/Tags/Parallelization.hpp\"\n\nnamespace {\ntemplate <bool UseLTS>\nstruct TestMetavars {\n  static constexpr bool local_time_stepping = UseLTS;\n};\n\ntemplate <bool UseLTS>\nstd::optional<domain::ElementWeight> make_option(\n    const std::string& option_string) {\n  return TestHelpers::test_option_tag<domain::OptionTags::ElementDistribution,\n                                      TestMetavars<UseLTS>>(option_string);\n}\n\nstd::optional<domain::ElementWeight> make_option_without_lts_metavars(\n    const std::string& option_string) {\n  return TestHelpers::test_option_tag<domain::OptionTags::ElementDistribution>(\n      option_string);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Tags.ElementDistribution\", \"[Unit][Domain]\") {\n  TestHelpers::db::test_simple_tag<domain::Tags::ElementDistribution>(\n      \"ElementDistribution\");\n  CHECK(make_option<true>(\"Uniform\") ==\n        std::optional{domain::ElementWeight::Uniform});\n  CHECK(make_option<true>(\"NumGridPoints\") ==\n        std::optional{domain::ElementWeight::NumGridPoints});\n  CHECK(make_option<true>(\"NumGridPointsAndGridSpacing\") ==\n        std::optional{domain::ElementWeight::NumGridPointsAndGridSpacing});\n  CHECK(make_option<true>(\"RoundRobin\") == std::nullopt);\n\n  CHECK(make_option<false>(\"Uniform\") ==\n        std::optional{domain::ElementWeight::Uniform});\n  CHECK(make_option<false>(\"NumGridPoints\") ==\n        std::optional{domain::ElementWeight::NumGridPoints});\n  CHECK_THROWS_WITH(make_option<false>(\"NumGridPointsAndGridSpacing\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"When not using local time stepping\") and\n                        Catch::Matchers::ContainsSubstring(\n                            \"Please choose another element distribution.\"));\n  CHECK(make_option<false>(\"RoundRobin\") == std::nullopt);\n\n  CHECK(make_option_without_lts_metavars(\"Uniform\") ==\n        std::optional{domain::ElementWeight::Uniform});\n  CHECK(make_option_without_lts_metavars(\"NumGridPoints\") ==\n        std::optional{domain::ElementWeight::NumGridPoints});\n  CHECK_THROWS_WITH(\n      make_option_without_lts_metavars(\"NumGridPointsAndGridSpacing\"),\n      Catch::Matchers::ContainsSubstring(\n          \"When not using local time stepping\") and\n          Catch::Matchers::ContainsSubstring(\n              \"Please choose another element distribution.\"));\n  CHECK(make_option_without_lts_metavars(\"RoundRobin\") == std::nullopt);\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Tags/Test_Faces.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain {\n\nnamespace {\n\nusing namespace std::complex_literals;\n\ntemplate <typename DataType, size_t Dim>\nstruct FacesTestCompute\n    : Tags::Faces<\n          Dim, ::Tags::Variables<tmpl::list<::Tags::TempScalar<0, DataType>>>>,\n      db::ComputeTag {\n  using base = Tags::Faces<\n      Dim, ::Tags::Variables<tmpl::list<::Tags::TempScalar<0, DataType>>>>;\n  using argument_tags = tmpl::list<>;\n  static void function(\n      const gsl::not_null<DirectionMap<\n          Dim, Variables<tmpl::list<::Tags::TempScalar<0, DataType>>>>*>\n          vars_on_faces) {\n    (*vars_on_faces)[Direction<Dim>::lower_xi()] =\n        Variables<tmpl::list<::Tags::TempScalar<0, DataType>>>{3, 1.};\n    if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n      (*vars_on_faces)[Direction<Dim>::lower_xi()] += ComplexDataVector(3, 2.i);\n    }\n  }\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Tags.Faces\", \"[Unit][Domain]\") {\n  constexpr size_t Dim = 1;\n  TestHelpers::db::test_prefix_tag<Tags::Faces<Dim, ::Tags::TempScalar<0>>>(\n      \"Faces(TempTensor0)\");\n  static_assert(\n      std::is_same_v<\n          make_faces_tags<\n              Dim, tmpl::list<::Tags::TempScalar<0>, ::Tags::TempScalar<1>>,\n              tmpl::list<::Tags::TempScalar<1>>>,\n          tmpl::list<Tags::Faces<Dim, ::Tags::TempScalar<0>>,\n                     ::Tags::TempScalar<1>>>);\n  using vars_tag = ::Tags::Variables<tmpl::list<::Tags::TempScalar<0>>>;\n  using vars_on_faces_tag = Tags::Faces<Dim, vars_tag>;\n  using Vars = Variables<tmpl::list<::Tags::TempScalar<0>>>;\n  using scalar_on_faces_tag = Tags::Faces<Dim, ::Tags::TempScalar<0>>;\n  {\n    INFO(\"Subitems\");\n    auto box = db::create<db::AddSimpleTags<vars_on_faces_tag>>(\n        DirectionMap<Dim, Vars>{});\n    CHECK(db::get<scalar_on_faces_tag>(box).empty());\n    db::mutate<vars_on_faces_tag>(\n        [](const gsl::not_null<DirectionMap<Dim, Vars>*> vars_on_faces) {\n          vars_on_faces->emplace(Direction<Dim>::lower_xi(),\n                                 Vars{size_t{3}, 0.});\n        },\n        make_not_null(&box));\n    CHECK(db::get<scalar_on_faces_tag>(box).at(Direction<Dim>::lower_xi()) ==\n          Scalar<DataVector>{size_t{3}, 0.});\n    db::mutate<scalar_on_faces_tag>(\n        [](const gsl::not_null<DirectionMap<Dim, Scalar<DataVector>>*>\n               scalar_on_faces) {\n          get(scalar_on_faces->at(Direction<Dim>::lower_xi())) = 1.;\n        },\n        make_not_null(&box));\n    CHECK(db::get<vars_on_faces_tag>(box).at(Direction<Dim>::lower_xi()) ==\n          Vars{size_t{3}, 1.});\n  }\n  {\n    INFO(\"Compute-subitems\");\n    auto box =\n        db::create<db::AddSimpleTags<>,\n                   db::AddComputeTags<FacesTestCompute<DataVector, Dim>>>();\n    CHECK(get(db::get<scalar_on_faces_tag>(box).at(\n              Direction<Dim>::lower_xi())) == DataVector{size_t{3}, 1.});\n  }\n  {\n    INFO(\"Compute-subitems complex\");\n    auto box = db::create<\n        db::AddSimpleTags<>,\n        db::AddComputeTags<FacesTestCompute<ComplexDataVector, Dim>>>();\n    CHECK(\n        get(db::get<Tags::Faces<Dim, ::Tags::TempScalar<0, ComplexDataVector>>>(\n                box)\n                .at(Direction<Dim>::lower_xi())) ==\n        ComplexDataVector{size_t{3}, 1. + 2.i});\n  }\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Tags/Test_NeighborMesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Tags.NeighborMesh\", \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<domain::Tags::NeighborMesh<1>>(\n      \"NeighborMesh\");\n  TestHelpers::db::test_simple_tag<domain::Tags::NeighborMesh<2>>(\n      \"NeighborMesh\");\n  TestHelpers::db::test_simple_tag<domain::Tags::NeighborMesh<3>>(\n      \"NeighborMesh\");\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Tags/Test_SurfaceJacobian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags/SurfaceJacobian.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace domain {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Tags.SurfaceJacobian\", \"[Unit][Domain]\") {\n  TestHelpers::db::test_simple_tag<\n      Tags::DetSurfaceJacobian<Frame::ElementLogical, Frame::Inertial>>(\n      \"DetSurfaceJacobian\");\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_AreaElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/KerrHorizonConforming.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain {\nnamespace {\n\n// Checks the euclidean and curved area element against the magnitude of the\n// unnormalized normal vector.\nvoid test_area_element_against_normal_vector() {\n  using TargetFrame = Frame::Inertial;\n  using inv_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, 3, TargetFrame>;\n  using sqrt_det_spatial_metric_tag =\n      gr::Tags::SqrtDetSpatialMetric<DataVector>;\n  const auto element_map_3d = ElementMap<3, TargetFrame>(\n      ElementId<3>(0),\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          CoordinateMaps::Wedge(1., 2., 1., 1.,\n                                OrientationMap<3>::create_aligned(), true)));\n\n  gr::Solutions::Minkowski<3> minkowski{};\n\n  const Mesh<2> interface_mesh{8, Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n  const Direction<3> direction{0, Side::Lower};\n  const auto logical_coords =\n      interface_logical_coordinates(interface_mesh, direction);\n  const auto inverse_jacobian = element_map_3d.inv_jacobian(logical_coords);\n  const auto det_volume = determinant(inverse_jacobian);\n  const auto face_normal =\n      unnormalized_face_normal(interface_mesh, element_map_3d, direction);\n  const auto inertial_coords = element_map_3d(logical_coords);\n\n  // test euclidean area element\n  const auto result_1_euclidean =\n      euclidean_area_element(inverse_jacobian, det_volume, direction);\n  const auto result_2_euclidean =\n      euclidean_area_element(inverse_jacobian, direction);\n  const auto face_normal_mag_euclidean = magnitude(face_normal);\n  const DataVector face_normal_mag_corrected_euclidean =\n      get(face_normal_mag_euclidean) / get(det_volume);\n  CHECK_ITERABLE_APPROX(get(result_1_euclidean), get(result_2_euclidean));\n  CHECK_ITERABLE_APPROX(face_normal_mag_corrected_euclidean,\n                        get(result_1_euclidean));\n\n  // test curved area element: Minkowski\n  const auto minkowski_vars = minkowski.variables(\n      inertial_coords, 0.,\n      tmpl::list<inv_spatial_metric_tag, sqrt_det_spatial_metric_tag>{});\n  const auto inv_spatial_metric_minkowski =\n      get<inv_spatial_metric_tag>(minkowski_vars);\n  const auto sqrt_det_spatial_metric =\n      get<sqrt_det_spatial_metric_tag>(minkowski_vars);\n  const auto result_1_minkowski =\n      area_element(inverse_jacobian, det_volume, direction,\n                   inv_spatial_metric_minkowski, sqrt_det_spatial_metric);\n  const auto result_2_minkowski =\n      area_element(inverse_jacobian, direction, inv_spatial_metric_minkowski,\n                   sqrt_det_spatial_metric);\n  CHECK_ITERABLE_APPROX(get(result_1_minkowski), get(result_2_minkowski));\n  CHECK_ITERABLE_APPROX(face_normal_mag_corrected_euclidean,\n                        get(result_1_minkowski));\n}\n\n// A test function sin^2(theta) * cos^2(phi) which evaluates to 4/3 * pi * R^2\n// when integrated over a sphere with radius R.\nDataVector test_function(const tnsr::I<DataVector, 3>& inertial_coords) {\n  const DataVector theta =\n      atan2(hypot(get<0>(inertial_coords), get<1>(inertial_coords)),\n            get<2>(inertial_coords));\n  const DataVector phi =\n      atan2(get<1>(inertial_coords), get<0>(inertial_coords));\n  return sin(theta) * sin(theta) * cos(phi) * cos(phi);\n}\n\n// Integrates the area and a test function over the excision surface of a shell\nvoid test_sphere_integral() {\n  for (const auto& [inner_radius, ref_level] : cartesian_product(\n           make_array(1e-4, 1e-2, 1.4), std::array<size_t, 3>{{0, 1, 2}})) {\n    domain::creators::Sphere shell{\n        inner_radius, 3.,    domain::creators::Sphere::Excision{},\n        ref_level,    10_st, true};\n    const auto shell_domain = shell.create_domain();\n    const auto& blocks = shell_domain.blocks();\n    const auto& initial_ref_levels = shell.initial_refinement_levels();\n    const auto element_ids = initial_element_ids(initial_ref_levels);\n    const auto excision_sphere =\n        shell_domain.excision_spheres().at(\"ExcisionSphere\");\n    const Mesh<2> face_mesh{10, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n    double sphere_surface = 0.;\n    double test_func_integral = 0.;\n    for (const auto& element_id : element_ids) {\n      const auto& current_block = blocks.at(element_id.block_id());\n      const auto element_abutting_direction =\n          excision_sphere.abutting_direction(element_id);\n      if (element_abutting_direction.has_value()) {\n        const auto face_logical_coords = interface_logical_coordinates(\n            face_mesh, element_abutting_direction.value());\n        const ElementMap logical_to_grid_map(\n            element_id, current_block.stationary_map().get_clone());\n        const auto inv_jacobian =\n            logical_to_grid_map.inv_jacobian(face_logical_coords);\n        const auto inertial_coords = logical_to_grid_map(face_logical_coords);\n        const auto det_jacobian = ::determinant(inv_jacobian);\n        const auto det = euclidean_area_element(\n            inv_jacobian, det_jacobian, element_abutting_direction.value());\n        sphere_surface += definite_integral(get(det), face_mesh);\n        test_func_integral += definite_integral(\n            test_function(inertial_coords) * get(det), face_mesh);\n      }\n    }\n    auto custom_approx = Approx::custom().epsilon(1e-10);\n    CHECK(sphere_surface ==\n          custom_approx(4. * M_PI * inner_radius * inner_radius));\n    CHECK(test_func_integral ==\n          custom_approx(4. / 3. * M_PI * inner_radius * inner_radius));\n  }\n}\n\n// checks that the integral over the horizon of a Kerr black hole\n// corresponds to the expected area\nvoid test_kerr_area() {\n  for (const auto& [ref_level, mass, spin] : cartesian_product(\n           std::array<size_t, 3>{{0, 1}}, make_array(0.001, 1., 100.),\n           std::array<std::array<double, 3>, 3>{{make_array(0., 0., 0.),\n                                                 make_array(0.1, 0.2, 0.3),\n                                                 make_array(0., 0., 0.99)}})) {\n    const double horizon_radius = mass * (1 + sqrt(1 - dot(spin, spin)));\n    domain::creators::Sphere shell{horizon_radius,\n                                   10. * horizon_radius,\n                                   domain::creators::Sphere::Excision{},\n                                   ref_level,\n                                   size_t(10),\n                                   true};\n    const auto shell_domain = shell.create_domain();\n    const auto& blocks = shell_domain.blocks();\n    const auto& initial_ref_levels = shell.initial_refinement_levels();\n    const auto element_ids = initial_element_ids(initial_ref_levels);\n    const auto excision_sphere =\n        shell_domain.excision_spheres().at(\"ExcisionSphere\");\n    const Mesh<2> face_mesh{10, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n    // Shell does not distort to conform to the horizon in Kerr-Schild\n    // coordinates for spinning black holes, so we append the horizon\n    // conforming map\n    const auto kerr_conforming_map =\n        CoordinateMap<Frame::Grid, Frame::Inertial,\n                      domain::CoordinateMaps::KerrHorizonConforming>(\n            domain::CoordinateMaps::KerrHorizonConforming{mass, spin});\n    gr::Solutions::KerrSchild kerr_schild{mass, spin, {0., 0., 0.}};\n    double horizon_area = 0.;\n    for (const auto& element_id : element_ids) {\n      const auto& current_block = blocks.at(element_id.block_id());\n      const auto element_abutting_direction =\n          excision_sphere.abutting_direction(element_id);\n      if (element_abutting_direction.has_value()) {\n        const auto face_logical_coords = interface_logical_coordinates(\n            face_mesh, element_abutting_direction.value());\n        const domain::CoordinateMaps::Composition logical_to_inertial_map{\n            domain::element_to_block_logical_map(element_id),\n            current_block.stationary_map().get_to_grid_frame(),\n            kerr_conforming_map.get_clone()};\n        const auto inverse_jacobian =\n            logical_to_inertial_map.inv_jacobian(face_logical_coords);\n        const auto spacetime_vars = kerr_schild.variables(\n            logical_to_inertial_map(face_logical_coords), 0.,\n            tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                       gr::Tags::SqrtDetSpatialMetric<DataVector>>{});\n        const auto curved_area_element = area_element(\n            inverse_jacobian, element_abutting_direction.value(),\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(spacetime_vars),\n            get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(spacetime_vars));\n        horizon_area += definite_integral(get(curved_area_element), face_mesh);\n      }\n    }\n    auto custom_approx = Approx::custom().epsilon(1e-8);\n    CHECK(horizon_area == custom_approx(8. * M_PI * mass * horizon_radius));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.AreaElement\", \"[Domain][Unit]\") {\n  test_area_element_against_normal_vector();\n  test_sphere_integral();\n  test_kerr_area();\n}\n\n}  // namespace\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_Block.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\n\ntemplate <size_t Dim>\nvoid test_block_time_independent() {\n  CAPTURE(Dim);\n  PUPable_reg(SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       CoordinateMaps::Identity<Dim>>));\n\n  using coordinate_map = CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       CoordinateMaps::Identity<Dim>>;\n  const coordinate_map identity_map{CoordinateMaps::Identity<Dim>{}};\n\n  Block<Dim> original_block(identity_map.get_clone(), 7, {}, \"Identity\");\n  CHECK_FALSE(original_block.is_time_dependent());\n\n  const auto check_block = [](const Block<Dim>& block) {\n    // Test external boundaries:\n    CHECK((block.external_boundaries().size()) == 2 * Dim);\n\n    // Test neighbors:\n    CHECK((block.neighbors().size()) == 0);\n\n    // Test id:\n    CHECK((block.id()) == 7);\n    CHECK(block.name() == \"Identity\");\n    CHECK(block.topologies() == make_array<Dim>(domain::Topology::I1));\n\n    // Test that the block's coordinate_map is Identity:\n    const auto& map = block.stationary_map();\n    const tnsr::I<double, Dim, Frame::BlockLogical> xi(1.0);\n    const tnsr::I<double, Dim, Frame::Inertial> x(1.0);\n    CHECK(map(xi) == x);\n    CHECK(map.inverse(x).value() == xi);\n  };\n\n  check_block(original_block);\n  check_block(serialize_and_deserialize(original_block));\n\n  // Test PUP\n  test_serialization(original_block);\n\n  // Test move semantics:\n  const Block<Dim> block_copy(identity_map.get_clone(), 7, {}, \"Identity\");\n  test_move_semantics(std::move(original_block), block_copy);\n}\n\ntemplate <size_t Dim>\nusing Translation = domain::CoordinateMaps::TimeDependent::Translation<Dim>;\n\ntemplate <size_t VolumeDim>\nauto make_translation_map() {\n  return domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n      Translation<VolumeDim>{\"Translation\"});\n}\n\ntemplate <size_t VolumeDim>\nauto make_translation_grid_to_distorted_map() {\n  return domain::make_coordinate_map<Frame::Grid, Frame::Distorted>(\n      Translation<VolumeDim>{\"TranslationGridToDistorted\"});\n}\n\ntemplate <size_t VolumeDim>\nauto make_translation_distorted_to_inertial_map() {\n  return domain::make_coordinate_map<Frame::Distorted, Frame::Inertial>(\n      Translation<VolumeDim>{\"TranslationDistortedToInertial\"});\n}\n\ntemplate <size_t Dim>\nvoid test_block_time_dependent() {\n  using TranslationDimD =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, Translation<Dim>>;\n  using logical_to_grid_coordinate_map =\n      CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                    CoordinateMaps::Identity<Dim>>;\n  using grid_to_inertial_coordinate_map = TranslationDimD;\n  PUPable_reg(logical_to_grid_coordinate_map);\n  PUPable_reg(SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                       CoordinateMaps::Identity<Dim>>));\n  PUPable_reg(grid_to_inertial_coordinate_map);\n  const logical_to_grid_coordinate_map identity_map{\n      CoordinateMaps::Identity<Dim>{}};\n  const grid_to_inertial_coordinate_map translation_map =\n      make_translation_map<Dim>();\n  Block<Dim> original_block(identity_map.get_clone(), 7, {});\n  CHECK_FALSE(original_block.is_time_dependent());\n  original_block.inject_time_dependent_map(translation_map.get_clone());\n  CHECK(original_block.is_time_dependent());\n\n  const auto check_block = [](const Block<Dim>& block) {\n    const double time = 2.0;\n\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time{};\n\n    functions_of_time[\"Translation\"] =\n        std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n            0.0,\n            std::array<DataVector, 3>{{{Dim, 0.0}, {Dim, 1.0}, {Dim, 0.0}}},\n            2.5);\n\n    // Test external boundaries:\n    CHECK((block.external_boundaries().size()) == 2 * Dim);\n\n    // Test neighbors:\n    CHECK((block.neighbors().size()) == 0);\n\n    // Test id:\n    CHECK((block.id()) == 7);\n    CHECK(block.topologies() == make_array<Dim>(domain::Topology::I1));\n\n    // Test that the block's coordinate_map is Identity:\n    const auto& grid_to_inertial_map = block.moving_mesh_grid_to_inertial_map();\n    const auto& logical_to_grid_map = block.moving_mesh_logical_to_grid_map();\n    const tnsr::I<double, Dim, Frame::BlockLogical> xi(1.0);\n    const tnsr::I<double, Dim, Frame::Inertial> x(1.0 + time);\n    CHECK(grid_to_inertial_map(logical_to_grid_map(xi), time,\n                               functions_of_time) == x);\n    CHECK(logical_to_grid_map\n              .inverse(grid_to_inertial_map.inverse(x, time, functions_of_time)\n                           .value())\n              .value() == xi);\n  };\n\n  check_block(original_block);\n  check_block(serialize_and_deserialize(original_block));\n\n  // Test PUP\n  test_serialization(original_block);\n\n  // Test move semantics:\n  Block<Dim> block_copy(identity_map.get_clone(), 7, {});\n  block_copy.inject_time_dependent_map(translation_map.get_clone());\n  test_move_semantics(std::move(original_block), block_copy);\n}\n\ntemplate <size_t Dim>\nvoid test_block_time_dependent_distorted() {\n  using TranslationDimD =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, Translation<Dim>>;\n  using TranslationGridDistortedDimD =\n      domain::CoordinateMap<Frame::Grid, Frame::Distorted, Translation<Dim>>;\n  using TranslationDistortedInertialDimD =\n      domain::CoordinateMap<Frame::Distorted, Frame::Inertial,\n                            Translation<Dim>>;\n\n  using logical_to_grid_coordinate_map =\n      CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                    CoordinateMaps::Identity<Dim>>;\n\n  using grid_to_inertial_coordinate_map = TranslationDimD;\n  using grid_to_distorted_coordinate_map = TranslationGridDistortedDimD;\n  using distorted_to_inertial_coordinate_map = TranslationDistortedInertialDimD;\n\n  PUPable_reg(SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                       CoordinateMaps::Identity<Dim>>));\n  PUPable_reg(grid_to_inertial_coordinate_map);\n\n  PUPable_reg(logical_to_grid_coordinate_map);\n  PUPable_reg(SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                       CoordinateMaps::Identity<Dim>>));\n  PUPable_reg(grid_to_distorted_coordinate_map);\n  PUPable_reg(distorted_to_inertial_coordinate_map);\n\n  const logical_to_grid_coordinate_map identity_map{\n      CoordinateMaps::Identity<Dim>{}};\n  const grid_to_inertial_coordinate_map translation_map =\n      make_translation_map<Dim>();\n  const grid_to_distorted_coordinate_map translation_grid_to_distorted_map =\n      make_translation_grid_to_distorted_map<Dim>();\n  const distorted_to_inertial_coordinate_map\n      translation_distorted_to_inertial_map =\n          make_translation_distorted_to_inertial_map<Dim>();\n  Block<Dim> original_block(identity_map.get_clone(), 7, {});\n  CHECK_FALSE(original_block.is_time_dependent());\n  original_block.inject_time_dependent_map(\n      translation_map.get_clone(),\n      translation_grid_to_distorted_map.get_clone(),\n      translation_distorted_to_inertial_map.get_clone());\n  CHECK(original_block.is_time_dependent());\n\n  const auto check_block = [](const Block<Dim>& block) {\n    const double time = 2.0;\n\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time{};\n\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time_grid_to_distorted{};\n\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time_distorted_to_inertial{};\n\n    functions_of_time[\"Translation\"] =\n        std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n            0.0,\n            std::array<DataVector, 3>{{{Dim, 0.0}, {Dim, 3.0}, {Dim, 0.0}}},\n            5.0);\n\n    functions_of_time_grid_to_distorted[\"TranslationGridToDistorted\"] =\n        std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n            0.0,\n            std::array<DataVector, 3>{{{Dim, 0.0}, {Dim, 1.0}, {Dim, 0.0}}},\n            5.0);\n\n    functions_of_time_distorted_to_inertial[\"TranslationDistortedToInertial\"] =\n        std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n            0.0,\n            std::array<DataVector, 3>{{{Dim, 0.0}, {Dim, 2.0}, {Dim, 0.0}}},\n            5.0);\n\n    // Test external boundaries:\n    CHECK((block.external_boundaries().size()) == 2 * Dim);\n\n    // Test neighbors:\n    CHECK((block.neighbors().size()) == 0);\n\n    // Test id:\n    CHECK((block.id()) == 7);\n    CHECK(block.topologies() == make_array<Dim>(domain::Topology::I1));\n\n    // Test that the block's coordinate_map is Identity:\n    const auto& grid_to_inertial_map = block.moving_mesh_grid_to_inertial_map();\n    const auto& grid_to_distorted_map =\n        block.moving_mesh_grid_to_distorted_map();\n    const auto& distorted_to_inertial_map =\n        block.moving_mesh_distorted_to_inertial_map();\n    const auto& logical_to_grid_map = block.moving_mesh_logical_to_grid_map();\n    const tnsr::I<double, Dim, Frame::BlockLogical> xi(1.0);\n    const tnsr::I<double, Dim, Frame::Inertial> x(1.0 + 3.0 * time);\n\n    const auto& result_grid = logical_to_grid_map(xi);\n    const auto& result_distorted = grid_to_distorted_map(\n        result_grid, time, functions_of_time_grid_to_distorted);\n    const auto& result_inertial = distorted_to_inertial_map(\n        result_distorted, time, functions_of_time_distorted_to_inertial);\n    CHECK(result_inertial == x);\n\n    CHECK(logical_to_grid_map\n              .inverse(grid_to_inertial_map.inverse(x, time, functions_of_time)\n                           .value())\n              .value() == xi);\n  };\n\n  check_block(original_block);\n  check_block(serialize_and_deserialize(original_block));\n\n  // Test PUP\n  test_serialization(original_block);\n\n  // Test move semantics:\n  Block<Dim> block_copy(identity_map.get_clone(), 7, {});\n  block_copy.inject_time_dependent_map(\n      translation_map.get_clone(),\n      translation_grid_to_distorted_map.get_clone(),\n      translation_distorted_to_inertial_map.get_clone());\n  test_move_semantics(std::move(original_block), block_copy);\n}\n\nvoid test_spherical_shell() {\n  const Block<3> spherical_shell(nullptr, 4,\n                                 DirectionMap<3, BlockNeighbors<3>>{}, \"Shell\",\n                                 domain::topologies::spherical_shell);\n  CHECK(spherical_shell.external_boundaries().size() == 2);\n  CHECK(\n      spherical_shell.external_boundaries().contains(Direction<3>::lower_xi()));\n  CHECK(\n      spherical_shell.external_boundaries().contains(Direction<3>::upper_xi()));\n  CHECK(spherical_shell.neighbors().empty());\n}\n\nvoid test_cylindrical_shell() {\n  const Block<3> cylindrical_shell(\n      nullptr, 4, DirectionMap<3, BlockNeighbors<3>>{}, \"CylindricalShell\",\n      domain::topologies::cylindrical_shell);\n  CHECK(cylindrical_shell.external_boundaries().size() == 4);\n  CHECK(cylindrical_shell.external_boundaries().contains(\n      Direction<3>::lower_xi()));\n  CHECK(cylindrical_shell.external_boundaries().contains(\n      Direction<3>::upper_xi()));\n  CHECK(cylindrical_shell.external_boundaries().contains(\n      Direction<3>::lower_zeta()));\n  CHECK(cylindrical_shell.external_boundaries().contains(\n      Direction<3>::upper_zeta()));\n  CHECK(cylindrical_shell.neighbors().empty());\n}\n\nvoid test_full_cylinder() {\n  const Block<3> full_cylinder(nullptr, 4, DirectionMap<3, BlockNeighbors<3>>{},\n                               \"Cylinder\", domain::topologies::full_cylinder);\n  CHECK(full_cylinder.external_boundaries().size() == 3);\n  CHECK(full_cylinder.external_boundaries().contains(Direction<3>::upper_xi()));\n  CHECK(\n      full_cylinder.external_boundaries().contains(Direction<3>::lower_zeta()));\n  CHECK(\n      full_cylinder.external_boundaries().contains(Direction<3>::upper_zeta()));\n  CHECK(full_cylinder.neighbors().empty());\n}\n\nvoid test_errors() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const BlockNeighbors<1> block_neighbors(\n            1, OrientationMap<1>::create_aligned());\n        const DirectionMap<1, BlockNeighbors<1>> neighbors{\n            {Direction<1>::lower_xi(), block_neighbors}};\n        const Block<1> loop(nullptr, 2, neighbors, \"Loop\",\n                            std::array{domain::Topology::S1});\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot specify a neighbor in a direction with no boundary\"));\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Block\", \"[Domain][Unit]\") {\n  test_spherical_shell();\n  test_cylindrical_shell();\n  test_full_cylinder();\n  test_errors();\n  test_block_time_independent<1>();\n  test_block_time_independent<2>();\n  test_block_time_independent<3>();\n\n  test_block_time_dependent<1>();\n  test_block_time_dependent<2>();\n  test_block_time_dependent<3>();\n\n  test_block_time_dependent_distorted<1>();\n  test_block_time_dependent_distorted<2>();\n  test_block_time_dependent_distorted<3>();\n\n  // Create DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>\n\n  // Each BlockNeighbors is an id and an OrientationMap:\n  const BlockNeighbors<2> block_neighbor1(\n      1, OrientationMap<2>(std::array<Direction<2>, 2>{\n             {Direction<2>::upper_xi(), Direction<2>::upper_eta()}}));\n  const BlockNeighbors<2> block_neighbor2(\n      2, OrientationMap<2>(std::array<Direction<2>, 2>{\n             {Direction<2>::lower_xi(), Direction<2>::upper_eta()}}));\n  const DirectionMap<2, BlockNeighbors<2>> neighbors{\n      {Direction<2>::upper_xi(), block_neighbor1},\n      {Direction<2>::lower_eta(), block_neighbor2}};\n  using coordinate_map = CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       CoordinateMaps::Identity<2>>;\n  const coordinate_map identity_map{CoordinateMaps::Identity<2>{}};\n  const Block<2> block(identity_map.get_clone(), 3, neighbors, \"Identity\");\n\n  // Test external boundaries:\n  CHECK((block.external_boundaries().size()) == 2);\n\n  // Test neighbors:\n  CHECK((block.neighbors().size()) == 2);\n\n  // Test id:\n  CHECK((block.id()) == 3);\n  CHECK(block.name() == \"Identity\");\n  CHECK(block.topologies() == make_array<2>(domain::Topology::I1));\n\n  // Test output:\n  CHECK(get_output(block) ==\n        \"Block 3 (Identity):\\n\"\n        \"Topology: (I1,I1)\\n\"\n        \"Neighbors: \"\n        \"([+0,Ids = (1); orientations = ([1,(+0, +1)]); conforming = true],\"\n        \"[-1,Ids = (2); orientations = ([2,(-0, +1)]); conforming = true])\\n\"\n        \"External boundaries: (+1,-0)\\n\"\n        \"Is time dependent: false\");\n\n  // Test comparison:\n  CHECK(block == block);\n  {\n    const Block<2> rhs(identity_map.get_clone(), 3, {}, \"Identity\");\n    CHECK(block != rhs);\n  }\n  {\n    const Block<2> rhs(identity_map.get_clone(), 3, neighbors, \"BlockyBlock\");\n    CHECK(block != rhs);\n  }\n  {\n    const Block<2> rhs(identity_map.get_clone(), 0, neighbors, \"Identity\");\n    CHECK(block != rhs);\n  }\n  {\n    const DirectionMap<2, BlockNeighbors<2>> annulus_neighbors{\n        {Direction<2>::upper_xi(), block_neighbor1}};\n\n    const Block<2> rhs(identity_map.get_clone(), 3, annulus_neighbors,\n                       \"Identity\",\n                       {{domain::Topology::I1, domain::Topology::S1}});\n    CHECK(rhs.topologies() ==\n          std::array{domain::Topology::I1, domain::Topology::S1});\n    CHECK(rhs.external_boundaries().size() == 1);\n    CHECK(rhs.neighbors().size() == 1);\n    CHECK(block != rhs);\n  }\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_BlockAndElementLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <random>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IdPair.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/SphericalShells.hpp\"\n#include \"Domain/Creators/TimeDependence/TimeDependence.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/BlockId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/ElementSearchTree.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\n\nvoid test_element_logical_coordinates() {\n  CHECK(element_logical_coordinates(\n            tnsr::I<double, 1, Frame::BlockLogical>{{{0.5}}},\n            ElementId<1>{0, {{{1, 0}}}}) == std::nullopt);\n  CHECK(element_logical_coordinates(\n            tnsr::I<double, 1, Frame::BlockLogical>{{{0.5}}},\n            ElementId<1>{0, {{{1, 1}}}}) ==\n        tnsr::I<double, 1, Frame::ElementLogical>{{{0.}}});\n  CHECK(element_logical_coordinates(\n            tnsr::I<double, 1, Frame::BlockLogical>{{{0.}}},\n            ElementId<1>{0, {{{1, 0}}}}) ==\n        tnsr::I<double, 1, Frame::ElementLogical>{{{1.}}});\n  CHECK(element_logical_coordinates(\n            tnsr::I<double, 1, Frame::BlockLogical>{{{0.}}},\n            ElementId<1>{0, {{{1, 1}}}}) ==\n        tnsr::I<double, 1, Frame::ElementLogical>{{{-1.}}});\n  CHECK(element_logical_coordinates(\n            tnsr::I<double, 2, Frame::BlockLogical>{{{-0.5, -0.5}}},\n            ElementId<2>{0, {{{1, 0}, {1, 0}}}}) ==\n        tnsr::I<double, 2, Frame::ElementLogical>{{{0., 0.}}});\n  CHECK(element_logical_coordinates(\n            tnsr::I<double, 2, Frame::BlockLogical>{{{-0.5, -0.5}}},\n            ElementId<2>{0, {{{1, 0}, {1, 1}}}}) == std::nullopt);\n  CHECK(element_logical_coordinates(\n            tnsr::I<double, 2, Frame::BlockLogical>{{{0., 0.5}}},\n            ElementId<2>{0, {{{1, 0}, {1, 1}}}}) ==\n        tnsr::I<double, 2, Frame::ElementLogical>{{{1., 0.}}});\n  CHECK(element_logical_coordinates(\n            tnsr::I<double, 3, Frame::BlockLogical>{{{0., 0.5, 1.}}},\n            ElementId<3>{0, {{{1, 0}, {1, 1}, {1, 1}}}}) ==\n        tnsr::I<double, 3, Frame::ElementLogical>{{{1., 0., 1.}}});\n\n  {\n    INFO(\"With ElementSearchTree\");\n    const std::vector<ElementId<2>> element_ids{\n        // Element layout in block-logical coordinates:\n        //        xi -->\n        //        -1     0       1\n        // eta  -1  -------------\n        //  |      |  0  |   2   |\n        //  v    0 |-------------|\n        //         |  1  | 3 | 4 |\n        //       1  -------------\n        ElementId<2>{0, {{{1, 0}, {1, 0}}}},  // 0\n        ElementId<2>{0, {{{1, 0}, {1, 1}}}},  // 1\n        ElementId<2>{0, {{{1, 1}, {1, 0}}}},  // 2\n        ElementId<2>{0, {{{2, 2}, {1, 1}}}},  // 3\n        ElementId<2>{0, {{{2, 3}, {1, 1}}}},  // 4\n        // Other block\n        ElementId<2>{1, {{{1, 0}, {1, 0}}}}};\n    const auto search_tree = domain::index_element_ids(element_ids);\n    CHECK(element_logical_coordinates<2>(\n              {domain::BlockId{0},\n               tnsr::I<double, 2, Frame::BlockLogical>{{{0.25, 0.5}}}},\n              search_tree) ==\n          std::make_pair(\n              element_ids[3],\n              tnsr::I<double, 2, Frame::ElementLogical>{{{0.0, 0.0}}}));\n    CHECK(element_logical_coordinates<2>(\n              {domain::BlockId{0},\n               tnsr::I<double, 2, Frame::BlockLogical>{{{-0.5, 0.5}}}},\n              search_tree) ==\n          std::make_pair(\n              element_ids[1],\n              tnsr::I<double, 2, Frame::ElementLogical>{{{0.0, 0.0}}}));\n    CHECK(element_logical_coordinates<2>(\n              {domain::BlockId{1},\n               tnsr::I<double, 2, Frame::BlockLogical>{{{-0.5, -0.5}}}},\n              search_tree) ==\n          std::make_pair(\n              element_ids[5],\n              tnsr::I<double, 2, Frame::ElementLogical>{{{0.0, 0.0}}}));\n    CHECK(element_logical_coordinates<2>(\n              {domain::BlockId{0},\n               tnsr::I<double, 2, Frame::BlockLogical>{{{-1.0, -0.5}}}},\n              search_tree) ==\n          std::make_pair(\n              element_ids[0],\n              tnsr::I<double, 2, Frame::ElementLogical>{{{-1.0, 0.0}}}));\n    CHECK(element_logical_coordinates<2>(\n              {domain::BlockId{0},\n               tnsr::I<double, 2, Frame::BlockLogical>{{{0.0, 0.0}}}},\n              search_tree) ==\n          std::make_pair(\n              // Multiple matches, result should be the first one inserted\n              element_ids[0],\n              tnsr::I<double, 2, Frame::ElementLogical>{{{1.0, 1.0}}}));\n    CHECK_FALSE(element_logical_coordinates<2>(\n                    {domain::BlockId{1},\n                     tnsr::I<double, 2, Frame::BlockLogical>{{{0.5, 0.5}}}},\n                    search_tree)\n                    .has_value());\n    CHECK_FALSE(element_logical_coordinates<2>(\n                    {domain::BlockId{2},\n                     tnsr::I<double, 2, Frame::BlockLogical>{{{-0.5, -0.5}}}},\n                    search_tree)\n                    .has_value());\n  }\n\n  {\n    // Benchmark with and without search tree\n    // - Result on Apple M2 Pro chip (Nils Vu, May 2025):\n    //   4.7 ms without search tree vs 0.2 ms with search tree (mean time per\n    //   sample)\n    const auto element_ids =\n        initial_element_ids<3>({{{3, 3, 3}}, {{2, 2, 2}}, {{1, 1, 1}}});\n    const auto search_tree = domain::index_element_ids(element_ids);\n    const size_t num_points = 100;\n    std::vector<\n        IdPair<domain::BlockId, tnsr::I<double, 3, Frame::BlockLogical>>>\n        target_points;\n    target_points.reserve(num_points);\n    std::uniform_int_distribution<size_t> dist_blocks(0, 3);\n    std::uniform_real_distribution<double> dist_points(-1.0, 1.0);\n    MAKE_GENERATOR(gen);\n    for (size_t i = 0; i < num_points; ++i) {\n      target_points.push_back(\n          {domain::BlockId{dist_blocks(gen)},\n           tnsr::I<double, 3, Frame::BlockLogical>{\n               {dist_points(gen), dist_points(gen), dist_points(gen)}}});\n    }\n    BENCHMARK(\"element_logical_coordinates without search tree\") {\n      for (const auto& target_point : target_points) {\n        element_logical_coordinates(element_ids, {target_point});\n      }\n    };\n    BENCHMARK(\"element_logical_coordinates with search tree\") {\n      for (const auto& target_point : target_points) {\n        element_logical_coordinates(target_point, search_tree);\n      }\n    };\n  }\n}\n\ntemplate <size_t Dim>\nvoid fuzzy_test_block_and_element_logical_coordinates(\n    const Domain<Dim>& domain,\n    const std::vector<std::array<size_t, Dim>>& refinement_levels,\n    const size_t n_pts) {\n  const auto all_element_ids = initial_element_ids<Dim>(refinement_levels);\n\n  // Random element_id for each point.\n  const auto element_ids = [&all_element_ids, &n_pts]() {\n    std::uniform_int_distribution<size_t> ran(0, all_element_ids.size() - 1);\n    MAKE_GENERATOR(gen);\n    std::vector<ElementId<Dim>> ids(n_pts);\n    for (size_t s = 0; s < n_pts; ++s) {\n      ids[s] = all_element_ids[ran(gen)];\n    }\n    return ids;\n  }();\n  CAPTURE(element_ids);\n\n  // Random element logical coords for each point\n  const auto element_coords = [&n_pts]() {\n    // Assumes logical coords go from -1 to 1.\n    std::uniform_real_distribution<double> ran(-1.0, 1.0);\n    MAKE_GENERATOR(gen);\n    std::vector<tnsr::I<double, Dim, Frame::ElementLogical>> coords(n_pts);\n    for (size_t s = 0; s < n_pts; ++s) {\n      for (size_t d = 0; d < Dim; ++d) {\n        coords[s].get(d) = ran(gen);\n      }\n    }\n    return coords;\n  }();\n  CAPTURE(element_coords);\n\n  // Compute expected map of element_ids to ElementLogicalCoordHolders.\n  // This is just re-organizing and re-bookkeeping element_ids and\n  // element_coords into the same structure that will be returned by\n  // the function we are testing.\n  const auto expected_coord_holders = [&element_ids, &element_coords,\n                                       &n_pts]() {\n    // This is complicated because we don't know ahead of time\n    // how many points are in each element.  So we do a first pass\n    // filling a structure that you can easily push_back to.\n    struct coords_plus_offset {\n      std::vector<std::array<double, Dim>> coords;\n      std::vector<size_t> offsets;\n    };\n    std::unordered_map<ElementId<Dim>, coords_plus_offset> coords_plus_offsets;\n    for (size_t s = 0; s < n_pts; ++s) {\n      auto new_coords = make_array<Dim>(0.0);\n      for (size_t d = 0; d < Dim; ++d) {\n        gsl::at(new_coords, d) = element_coords[s].get(d);\n      }\n      auto pos = coords_plus_offsets.find(element_ids[s]);\n      if (pos == coords_plus_offsets.end()) {\n        coords_plus_offsets.emplace(element_ids[s],\n                                    coords_plus_offset{{new_coords}, {{s}}});\n      } else {\n        pos->second.coords.push_back(new_coords);\n        pos->second.offsets.push_back(s);\n      }\n    }\n\n    // The second pass fills the desired structure.\n    std::unordered_map<ElementId<Dim>, ElementLogicalCoordHolder<Dim>> holders;\n    for (const auto& coord_holder : coords_plus_offsets) {\n      const size_t num_grid_pts = coord_holder.second.offsets.size();\n      tnsr::I<DataVector, Dim, Frame::ElementLogical> coords(num_grid_pts);\n      for (size_t s = 0; s < num_grid_pts; ++s) {\n        for (size_t d = 0; d < Dim; ++d) {\n          coords.get(d)[s] = gsl::at(coord_holder.second.coords[s], d);\n        }\n      }\n      holders.emplace(\n          coord_holder.first,\n          ElementLogicalCoordHolder<Dim>{coords, coord_holder.second.offsets});\n    }\n    return holders;\n  }();\n\n  // Transform element_coords to inertial coords\n  const auto inertial_coords = [&n_pts, &domain, &element_ids,\n                                &element_coords]() {\n    tnsr::I<DataVector, Dim, Frame::Inertial> coords(n_pts);\n    for (size_t s = 0; s < n_pts; ++s) {\n      const auto& my_block = domain.blocks()[element_ids[s].block_id()];\n      if (my_block.is_time_dependent()) {\n        ERROR(\"Only support time-independent blocks in this test.\");\n      }\n      ElementMap<Dim, Frame::Inertial> map{\n          element_ids[s], my_block.stationary_map().get_clone()};\n      const auto coord_one_point = map(element_coords[s]);\n      for (size_t d = 0; d < Dim; ++d) {\n        coords.get(d)[s] = coord_one_point.get(d);\n      }\n    }\n    return coords;\n  }();\n\n  const auto block_logical_result =\n      block_logical_coordinates(domain, inertial_coords);\n  test_serialization(block_logical_result);\n\n  for (size_t s = 0; s < n_pts; ++s) {\n    CHECK(block_logical_result[s].value().id.get_index() ==\n          element_ids[s].block_id());\n    // We don't know block logical coordinates here, so we can't\n    // test them.\n  }\n\n  // Test versus all the element_ids.\n  const auto element_logical_result =\n      element_logical_coordinates(all_element_ids, block_logical_result);\n\n  for (const auto& expected_holder_pair : expected_coord_holders) {\n    const auto pos = element_logical_result.find(expected_holder_pair.first);\n    CHECK(pos != element_logical_result.end());\n    if (pos != element_logical_result.end()) {\n      const auto& holder = pos->second;\n      using ::operator<<;\n      CHECK(holder.offsets == expected_holder_pair.second.offsets);\n      CHECK_ITERABLE_APPROX(holder.element_logical_coords,\n                            expected_holder_pair.second.element_logical_coords);\n    }\n  }\n\n  // Make sure every element in element_logical_result is also\n  // in expected_coord_holders.\n  for (const auto& holder_pair : element_logical_result) {\n    const auto pos = expected_coord_holders.find(holder_pair.first);\n    CHECK(pos != expected_coord_holders.end());\n  }\n}\n\ntemplate <size_t Dim>\nvoid fuzzy_test_block_and_element_logical_coordinates_unrefined(\n    const Domain<Dim>& domain, const size_t n_pts,\n    const double time = std::numeric_limits<double>::signaling_NaN(),\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time = std::unordered_map<\n            std::string,\n            std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{}) {\n  const size_t n_blocks = domain.blocks().size();\n\n  // Random block_id for each point.\n  const auto block_ids = [&n_pts, &n_blocks]() {\n    std::uniform_int_distribution<size_t> ran(0, n_blocks - 1);\n    MAKE_GENERATOR(gen);\n    std::vector<size_t> ids(n_pts);\n    for (size_t s = 0; s < n_pts; ++s) {\n      ids[s] = ran(gen);\n    }\n    return ids;\n  }();\n  CAPTURE(block_ids);\n\n  // Random block logical coords for each point\n  // (block logical coords == element logical coords)\n  const auto block_coords = [&n_pts]() {\n    // Assumes logical coords go from -1 to 1.\n    std::uniform_real_distribution<double> ran(-1.0, 1.0);\n    MAKE_GENERATOR(gen);\n    std::vector<tnsr::I<double, Dim, Frame::BlockLogical>> coords(n_pts);\n    for (size_t s = 0; s < n_pts; ++s) {\n      for (size_t d = 0; d < Dim; ++d) {\n        coords[s].get(d) = ran(gen);\n      }\n    }\n    return coords;\n  }();\n  CAPTURE(block_coords);\n\n  // Map to inertial coords\n  const auto inertial_coords = [&n_pts, &domain, &block_ids, &block_coords,\n                                &time, &functions_of_time]() {\n    tnsr::I<DataVector, Dim, Frame::Inertial> coords(n_pts);\n    for (size_t s = 0; s < n_pts; ++s) {\n      tnsr::I<double, Dim, Frame::Inertial> coord_one_point{};\n      if (domain.blocks()[block_ids[s]].is_time_dependent()) {\n        coord_one_point =\n            domain.blocks()[block_ids[s]].moving_mesh_grid_to_inertial_map()(\n                domain.blocks()[block_ids[s]].moving_mesh_logical_to_grid_map()(\n                    block_coords[s]),\n                time, functions_of_time);\n      } else {\n        coord_one_point =\n            domain.blocks()[block_ids[s]].stationary_map()(block_coords[s]);\n      }\n      for (size_t d = 0; d < Dim; ++d) {\n        coords.get(d)[s] = coord_one_point.get(d);\n      }\n    }\n    return coords;\n  }();\n\n  auto block_logical_result = block_logical_coordinates(\n      domain, inertial_coords, time, functions_of_time);\n  test_serialization(block_logical_result);\n\n  for (size_t s = 0; s < n_pts; ++s) {\n    CHECK(block_logical_result[s].value().id.get_index() == block_ids[s]);\n    CHECK_ITERABLE_APPROX(block_logical_result[s].value().data,\n                          block_coords[s]);\n  }\n\n  // Map to distorted coords\n  // For this test, we test distorted coords only if the first block has\n  // a distorted frame.  For this test, either all blocks have a distorted\n  // frame or none of them do.\n  if (domain.blocks().begin()->has_distorted_frame()) {\n    const auto distorted_coords = [&n_pts, &domain, &block_ids, &block_coords,\n                                   &time, &functions_of_time]() {\n      tnsr::I<DataVector, Dim, Frame::Distorted> coords(n_pts);\n      for (size_t s = 0; s < n_pts; ++s) {\n        tnsr::I<double, Dim, Frame::Distorted> coord_one_point{};\n        if (domain.blocks()[block_ids[s]].is_time_dependent()) {\n          coord_one_point =\n              domain.blocks()[block_ids[s]].moving_mesh_grid_to_distorted_map()(\n                  domain.blocks()[block_ids[s]]\n                      .moving_mesh_logical_to_grid_map()(block_coords[s]),\n                  time, functions_of_time);\n        } else {\n          // time-independent maps have identical distorted and inertial frames.\n          const tnsr::I<double, Dim, Frame::Inertial> coord_one_point_inertial =\n              domain.blocks()[block_ids[s]].stationary_map()(block_coords[s]);\n          for (size_t d = 0; d < Dim; ++d) {\n            coord_one_point.get(d) = coord_one_point_inertial.get(d);\n          }\n        }\n        for (size_t d = 0; d < Dim; ++d) {\n          coords.get(d)[s] = coord_one_point.get(d);\n        }\n      }\n      return coords;\n    }();\n\n    block_logical_result = block_logical_coordinates(domain, distorted_coords,\n                                                     time, functions_of_time);\n    test_serialization(block_logical_result);\n    for (size_t s = 0; s < n_pts; ++s) {\n      CHECK(block_logical_result[s].value().id.get_index() == block_ids[s]);\n      CHECK_ITERABLE_APPROX(block_logical_result[s].value().data,\n                            block_coords[s]);\n    }\n  }\n\n  // Map to grid coords\n  const auto grid_coords = [&n_pts, &domain, &block_ids, &block_coords]() {\n    tnsr::I<DataVector, Dim, Frame::Grid> coords(n_pts);\n    for (size_t s = 0; s < n_pts; ++s) {\n      tnsr::I<double, Dim, Frame::Grid> coord_one_point{};\n      if (domain.blocks()[block_ids[s]].is_time_dependent()) {\n        // logical to grid map is time-independent.\n        coord_one_point =\n            domain.blocks()[block_ids[s]].moving_mesh_logical_to_grid_map()(\n                block_coords[s]);\n      } else {\n        // time-independent maps have identical grid and inertial frames.\n        const tnsr::I<double, Dim, Frame::Inertial> coord_one_point_inertial =\n            domain.blocks()[block_ids[s]].stationary_map()(block_coords[s]);\n        for (size_t d = 0; d < Dim; ++d) {\n          coord_one_point.get(d) = coord_one_point_inertial.get(d);\n        }\n      }\n      for (size_t d = 0; d < Dim; ++d) {\n        coords.get(d)[s] = coord_one_point.get(d);\n      }\n    }\n    return coords;\n  }();\n\n  block_logical_result =\n      block_logical_coordinates(domain, grid_coords, time, functions_of_time);\n  test_serialization(block_logical_result);\n  for (size_t s = 0; s < n_pts; ++s) {\n    CHECK(block_logical_result[s].value().id.get_index() == block_ids[s]);\n    CHECK_ITERABLE_APPROX(block_logical_result[s].value().data,\n                          block_coords[s]);\n  }\n}\n\nvoid fuzzy_test_block_and_element_logical_coordinates_shell(\n    const DomainCreator<3>& shell, const size_t n_pts) {\n  const auto domain = shell.create_domain();\n  fuzzy_test_block_and_element_logical_coordinates_unrefined(domain, n_pts);\n  fuzzy_test_block_and_element_logical_coordinates(\n      domain, shell.initial_refinement_levels(), n_pts);\n}\n\nvoid fuzzy_test_block_and_element_logical_coordinates_time_dependent_brick(\n    const size_t n_pts) {\n  const auto uniform_translation =\n      domain::creators::time_dependence::UniformTranslation<3>(\n          0.0, {{0.1, 0.2, 0.3}});\n  const domain::creators::Brick brick(\n      {{-0.1, -0.2, -0.3}}, {{0.1, 0.2, 0.3}}, {{0, 0, 0}}, {{3, 3, 3}},\n      {{false, false, false}}, {}, uniform_translation.get_clone());\n  const auto domain = brick.create_domain();\n  const auto functions_of_time = uniform_translation.functions_of_time();\n  // Test at two different times.\n  fuzzy_test_block_and_element_logical_coordinates_unrefined(domain, n_pts, 0.0,\n                                                             functions_of_time);\n  fuzzy_test_block_and_element_logical_coordinates_unrefined(domain, n_pts, 0.1,\n                                                             functions_of_time);\n}\n\nvoid fuzzy_test_block_and_element_logical_coordinates_distorted_brick(\n    const size_t n_pts) {\n  const auto uniform_translation =\n      domain::creators::time_dependence::UniformTranslation<3>(\n          0.0, {{0.1, 0.2, 0.3}}, {{-0.2, -0.1, -0.2}});\n  const domain::creators::Brick brick(\n      {{-0.1, -0.2, -0.3}}, {{0.1, 0.2, 0.3}}, {{0, 0, 0}}, {{3, 3, 3}},\n      {{false, false, false}}, {}, uniform_translation.get_clone());\n  const auto domain = brick.create_domain();\n  const auto functions_of_time = uniform_translation.functions_of_time();\n  // Test at two different times.\n  fuzzy_test_block_and_element_logical_coordinates_unrefined(domain, n_pts, 0.0,\n                                                             functions_of_time);\n  fuzzy_test_block_and_element_logical_coordinates_unrefined(domain, n_pts, 0.1,\n                                                             functions_of_time);\n}\n\nvoid fuzzy_test_block_and_element_logical_coordinates3(const size_t n_pts) {\n  Domain<3> domain(maps_for_rectilinear_domains<Frame::Inertial>(\n                       Index<3>{2, 2, 2},\n                       std::array<std::vector<double>, 3>{\n                           {{0.0, 0.5, 1.0}, {0.0, 0.5, 1.0}, {0.0, 0.5, 1.0}}},\n                       {Index<3>{}}),\n                   corners_for_rectilinear_domains(Index<3>{2, 2, 2}));\n  fuzzy_test_block_and_element_logical_coordinates_unrefined(domain, n_pts);\n  fuzzy_test_block_and_element_logical_coordinates(domain,\n                                                   {{{0, 1, 2}},\n                                                    {{2, 2, 2}},\n                                                    {{2, 1, 1}},\n                                                    {{0, 2, 1}},\n                                                    {{2, 2, 2}},\n                                                    {{2, 2, 2}},\n                                                    {{1, 2, 0}},\n                                                    {{2, 0, 1}}},\n                                                   n_pts);\n}\n\nvoid fuzzy_test_block_and_element_logical_coordinates2(const size_t n_pts) {\n  Domain<2> domain(maps_for_rectilinear_domains<Frame::Inertial>(\n                       Index<2>{2, 3},\n                       std::array<std::vector<double>, 2>{\n                           {{0.0, 0.5, 1.0}, {0.0, 0.33, 0.66, 1.0}}},\n                       {Index<2>{}}),\n                   corners_for_rectilinear_domains(Index<2>{2, 3}));\n  fuzzy_test_block_and_element_logical_coordinates_unrefined(domain, n_pts);\n  fuzzy_test_block_and_element_logical_coordinates(\n      domain, {{{0, 1}}, {{2, 1}}, {{2, 2}}, {{3, 2}}, {{0, 0}}, {{2, 0}}},\n      n_pts);\n}\n\nvoid fuzzy_test_block_and_element_logical_coordinates1(const size_t n_pts) {\n  Domain<1> domain(\n      maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<1>{2}, std::array<std::vector<double>, 1>{{{0.0, 0.5, 1.0}}},\n          {Index<1>{}}),\n      corners_for_rectilinear_domains(Index<1>{2}));\n  fuzzy_test_block_and_element_logical_coordinates_unrefined(domain, n_pts);\n  fuzzy_test_block_and_element_logical_coordinates(domain, {{{0}}, {{3}}},\n                                                   n_pts);\n}\n\ntemplate <size_t Dim>\nvoid test_block_and_element_logical_coordinates(\n    const Domain<Dim>& domain,\n    const std::vector<std::array<double, Dim>>& x_inertial,\n    const std::vector<size_t>& expected_block_ids,\n    const std::vector<std::array<double, Dim>>& expected_block_logical,\n    const std::vector<ElementId<Dim>>& element_ids,\n    const std::vector<ElementId<Dim>>& expected_ids,\n    const std::vector<std::vector<size_t>>& expected_offset,\n    const std::vector<std::vector<std::array<double, Dim>>>&\n        expected_element_logical) {\n  tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords(x_inertial.size());\n  std::vector<tnsr::I<double, Dim, Frame::BlockLogical>>\n      expected_logical_coords(x_inertial.size());\n  for (size_t s = 0; s < x_inertial.size(); ++s) {\n    for (size_t d = 0; d < Dim; ++d) {\n      inertial_coords.get(d)[s] = gsl::at(x_inertial[s], d);\n      expected_logical_coords[s].get(d) = gsl::at(expected_block_logical[s], d);\n    }\n  }\n\n  std::vector<tnsr::I<double, Dim, Frame::BlockLogical>>\n      block_logical_single_point_result(x_inertial.size());\n  std::vector<size_t> block_order;\n  for (size_t i = 0; i < x_inertial.size(); i++) {\n    tnsr::I<double, Dim, Frame::Inertial> inertial_coords_double{};\n    for (size_t d = 0; d < Dim; d++) {\n      inertial_coords_double.get(d) = inertial_coords.get(d)[i];\n    }\n    if (i > 0) {\n      // Reset the block order so we always find the same block. In the first\n      // iteration the block order should be initially set to the list of blocks\n      // in the domain.\n      alg::iota(block_order, 0_st);\n    }\n    auto inv_point = block_logical_coordinates_single_point(\n        inertial_coords_double, domain, 0., {}, make_not_null(&block_order));\n    REQUIRE(inv_point.has_value());\n    // Check that the block order was updated\n    const size_t block_id = inv_point.value().id.get_index();\n    CHECK(block_order.size() == domain.blocks().size());\n    CHECK(block_order.front() == block_id);\n    // Also check the other overload\n    CHECK(block_logical_coordinates_single_point(inertial_coords_double,\n                                                 domain.blocks()[block_id]) ==\n          inv_point.value().data);\n    block_logical_single_point_result[i] = std::move(inv_point.value().data);\n  }\n\n  const auto block_logical_result =\n      block_logical_coordinates(domain, inertial_coords);\n  const auto block_logical_result_with_order = block_logical_coordinates(\n      domain, inertial_coords, std::numeric_limits<double>::signaling_NaN(), {},\n      make_not_null(&block_order));\n  for (size_t s = 0; s < x_inertial.size(); ++s) {\n    CHECK(block_logical_result[s].value().id.get_index() ==\n          expected_block_ids[s]);\n    CHECK_ITERABLE_APPROX(block_logical_result[s].value().data,\n                          expected_logical_coords[s]);\n    CHECK_ITERABLE_APPROX(block_logical_single_point_result[s],\n                          expected_logical_coords[s]);\n    // Check the version with block order. It is no longer guaranteed that the\n    // smallest block ID is chosen, so the logical coordinates at the boundary\n    // may differ by a sign.\n    for (size_t d=0; d < Dim; ++d) {\n      CHECK_ITERABLE_APPROX(\n          abs(block_logical_result_with_order[s].value().data.get(d)),\n          abs(expected_logical_coords[s].get(d)));\n    }\n  }\n\n  test_serialization(block_logical_result);\n\n  const auto element_logical_result =\n      element_logical_coordinates(element_ids, block_logical_result);\n\n  std::vector<tnsr::I<DataVector, Dim, Frame::ElementLogical>>\n      expected_elem_logical;\n  for (const auto& coord : expected_element_logical) {\n    tnsr::I<DataVector, Dim, Frame::ElementLogical> dum(coord.size());\n    for (size_t s = 0; s < coord.size(); ++s) {\n      for (size_t d = 0; d < Dim; ++d) {\n        dum.get(d)[s] = gsl::at(coord[s], d);\n      }\n    }\n    expected_elem_logical.emplace_back(std::move(dum));\n  }\n\n  for (size_t s = 0; s < expected_ids.size(); ++s) {\n    const auto pos = element_logical_result.find(expected_ids[s]);\n    INFO(expected_ids[s]);\n    CHECK(pos != element_logical_result.end());\n    if (pos != element_logical_result.end()) {\n      const auto& holder = pos->second;\n      CHECK(holder.offsets == expected_offset[s]);\n      CHECK_ITERABLE_APPROX(holder.element_logical_coords,\n                            expected_elem_logical[s]);\n    }\n  }\n  // Make sure we got all the elements\n  for (const auto& holder_pair : element_logical_result) {\n    INFO(holder_pair.first);\n    const auto pos =\n        std::find(expected_ids.begin(), expected_ids.end(), holder_pair.first);\n    CHECK(pos != expected_ids.end());\n  }\n}\n\nvoid test_block_and_element_logical_coordinates1() {\n  Domain<1> domain(\n      maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<1>{2}, std::array<std::vector<double>, 1>{{{0.0, 0.5, 1.0}}},\n          {Index<1>{}}),\n      corners_for_rectilinear_domains(Index<1>{2}));\n\n  std::vector<std::array<double, 1>> x_inertial{\n      {{0.1}},\n      {{0.8}},\n      {{0.5 + 1000.0 * std::numeric_limits<double>::epsilon()}},\n      {{0.5}}};\n  std::vector<size_t> expected_block_ids{0, 1, 1, 0};\n  std::vector<std::array<double, 1>> expected_x_logical{\n      {{-0.6}},\n      {{0.2}},\n      {{4000.0 * std::numeric_limits<double>::epsilon() - 1.0}},\n      {{1.0}}\n      // The last result is 1.0 because it checks the lower block_id first.\n  };\n\n  // Create some Elements.  I (Mark) did this by hand.\n  auto element_ids = initial_element_ids<1>({{{2}}, {{3}}});\n\n  // I (Mark) computed these expected quantities by hand, given the\n  // points above and the choices of elements.\n  std::vector<size_t> expected_id_indices{{0, 8, 4, 3}};\n  std::vector<ElementId<1>> expected_ids;\n  expected_ids.reserve(expected_id_indices.size());\n  for (const auto& index : expected_id_indices) {\n    expected_ids.push_back(element_ids[index]);\n  }\n\n  std::vector<std::vector<size_t>> expected_offset{\n      std::vector<size_t>{0}, std::vector<size_t>{1}, std::vector<size_t>{2},\n      std::vector<size_t>{3}};\n  std::vector<std::vector<std::array<double, 1>>> expected_elem_log{\n      std::vector<std::array<double, 1>>{{{0.6}}},\n      std::vector<std::array<double, 1>>{{{0.6}}},\n      std::vector<std::array<double, 1>>{\n          {{32000.0 * std::numeric_limits<double>::epsilon() - 1.0}}},\n      std::vector<std::array<double, 1>>{{{1.0}}}};\n\n  test_block_and_element_logical_coordinates(\n      domain, x_inertial, expected_block_ids, expected_x_logical, element_ids,\n      expected_ids, expected_offset, expected_elem_log);\n}\n\nvoid test_block_logical_coordinates1fail() {\n  Domain<1> domain(\n      maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<1>{2}, std::array<std::vector<double>, 1>{{{0.0, 0.5, 1.0}}},\n          {Index<1>{}}),\n      corners_for_rectilinear_domains(Index<1>{2}));\n\n  std::vector<std::array<double, 1>> x_inertial{\n      {{0.1}}, {{1.1}}, {{-0.2}}, {{0.5}}};\n  tnsr::I<DataVector, 1, Frame::Inertial> inertial_coords(x_inertial.size());\n  for (size_t s = 0; s < x_inertial.size(); ++s) {\n    for (size_t d = 0; d < 1; ++d) {\n      inertial_coords.get(d)[s] = gsl::at(x_inertial[s], d);\n    }\n  }\n  const auto block_logical_result =\n      block_logical_coordinates(domain, inertial_coords);\n  // points 1.1 and -0.2 are not in any block. They correspond to\n  // indices 1 and 2 in the list of points, so they should be cast\n  // to 'false'.\n  CHECK(block_logical_result[0]);\n  CHECK_FALSE(block_logical_result[1]);\n  CHECK_FALSE(block_logical_result[2]);\n  CHECK(block_logical_result[3]);\n}\n\nvoid test_block_and_element_logical_coordinates3() {\n  Domain<3> domain(maps_for_rectilinear_domains<Frame::Inertial>(\n                       Index<3>{2, 2, 2},\n                       std::array<std::vector<double>, 3>{\n                           {{0.0, 0.5, 1.0}, {0.0, 0.5, 1.0}, {0.0, 0.5, 1.0}}},\n                       {Index<3>{}}),\n                   corners_for_rectilinear_domains(Index<3>{2, 2, 2}));\n\n  std::vector<std::array<double, 3>> x_inertial{\n      {{0.1, 0.1, 0.1}},   {{0.05, 0.05, 0.05}}, {{0.24, 0.24, 0.24}},\n      {{0.9, 0.24, 0.24}}, {{0.24, 0.8, 0.24}},  {{0.9, 0.8, 0.24}},\n      {{0.1, 0.1, 0.7}},   {{0.1, 0.8, 0.7}},    {{0.7, 0.2, 0.7}},\n      {{0.9, 0.9, 0.9}},   {{0.5, 0.75, 1.0}}};\n  // The last point above lies on the boundary of a block and of an\n  // element.  block_logical_coordinates should pick the smallest\n  // block_id that it lies on.\n  std::vector<size_t> expected_block_ids{{0, 0, 0, 1, 2, 3, 4, 6, 5, 7, 6}};\n  std::vector<std::array<double, 3>> expected_x_logical{\n      {{-0.6, -0.6, -0.6}},  {{-0.8, -0.8, -0.8}},  {{-0.04, -0.04, -0.04}},\n      {{0.6, -0.04, -0.04}}, {{-0.04, 0.2, -0.04}}, {{0.6, 0.2, -0.04}},\n      {{-0.6, -0.6, -0.2}},  {{-0.6, 0.2, -0.2}},   {{-0.2, -0.2, -0.2}},\n      {{0.6, 0.6, 0.6}},     {{1.0, 0.0, 1.0}}};\n\n  // Create some Elements.  I (Mark) did this by hand.\n  auto element_ids = initial_element_ids<3>(4, {{0, 1, 2}});\n  auto element_ids_0 = initial_element_ids<3>(0, {{2, 2, 2}});\n  auto element_ids_1 = initial_element_ids<3>(1, {{2, 1, 1}});\n  auto element_ids_2 = initial_element_ids<3>(2, {{0, 2, 1}});\n  auto element_ids_3 = initial_element_ids<3>(3, {{2, 2, 2}});\n  auto element_ids_6 = initial_element_ids<3>(6, {{2, 2, 2}});\n  auto element_ids_5 = initial_element_ids<3>(5, {{1, 2, 0}});\n  auto element_ids_7 = initial_element_ids<3>(7, {{2, 0, 1}});\n  std::copy(element_ids_0.begin(), element_ids_0.end(),\n            std::back_inserter(element_ids));\n  std::copy(element_ids_1.begin(), element_ids_1.end(),\n            std::back_inserter(element_ids));\n  std::copy(element_ids_2.begin(), element_ids_2.end(),\n            std::back_inserter(element_ids));\n  std::copy(element_ids_3.begin(), element_ids_3.end(),\n            std::back_inserter(element_ids));\n  std::copy(element_ids_6.begin(), element_ids_6.end(),\n            std::back_inserter(element_ids));\n  std::copy(element_ids_5.begin(), element_ids_5.end(),\n            std::back_inserter(element_ids));\n  std::copy(element_ids_7.begin(), element_ids_7.end(),\n            std::back_inserter(element_ids));\n\n  // I (Mark) computed these expected quantities by hand, given the\n  // points above and the choices of elements.\n  std::vector<size_t> expected_id_indices{\n      {1, 8, 29, 84, 92, 153, 169, 225, 239, 219}};\n  std::vector<ElementId<3>> expected_ids;\n  expected_ids.reserve(expected_id_indices.size());\n  for (const auto& index : expected_id_indices) {\n    expected_ids.push_back(element_ids[index]);\n  }\n\n  // The last point above is on an element boundary;\n  // element_logical_coordinates should choose the first element in\n  // the list of elements it is passed.\n  std::vector<std::vector<size_t>> expected_offset{\n      std::vector<size_t>{6}, std::vector<size_t>{0, 1}, std::vector<size_t>{2},\n      std::vector<size_t>{3}, std::vector<size_t>{4},    std::vector<size_t>{5},\n      std::vector<size_t>{7}, std::vector<size_t>{8},    std::vector<size_t>{9},\n      std::vector<size_t>{10}};\n  std::vector<std::vector<std::array<double, 3>>> expected_elem_log{\n      std::vector<std::array<double, 3>>{{{-0.6, -0.2, 0.2}}},\n      std::vector<std::array<double, 3>>{{{0.6, 0.6, 0.6}},\n                                         {{-0.2, -0.2, -0.2}}},\n      std::vector<std::array<double, 3>>{{{0.84, 0.84, 0.84}}},\n      std::vector<std::array<double, 3>>{{{-0.6, 0.92, 0.92}}},\n      std::vector<std::array<double, 3>>{{{-0.04, -0.2, 0.92}}},\n      std::vector<std::array<double, 3>>{{{-0.6, -0.2, 0.84}}},\n      std::vector<std::array<double, 3>>{{{0.6, -0.2, 0.2}}},\n      std::vector<std::array<double, 3>>{{{0.6, 0.2, -0.2}}},\n      std::vector<std::array<double, 3>>{{{-0.6, 0.6, 0.2}}},\n      std::vector<std::array<double, 3>>{{{1.0, -1.0, 1.0}}}};\n\n  test_block_and_element_logical_coordinates(\n      domain, x_inertial, expected_block_ids, expected_x_logical, element_ids,\n      expected_ids, expected_offset, expected_elem_log);\n\n  {\n    INFO(\"Shuffled test\");\n    MAKE_GENERATOR(gen);\n    std::shuffle(element_ids.begin(), element_ids.end(), gen);\n\n    test_block_and_element_logical_coordinates(\n        domain, x_inertial, expected_block_ids, expected_x_logical, element_ids,\n        expected_ids, expected_offset, expected_elem_log);\n  }\n}\n\nvoid test_element_ids_are_uniquely_determined() {\n  const size_t xi_level = 2;\n  const size_t eta_level = 3;\n  const size_t zeta_level = 4;\n  auto element_ids =\n      initial_element_ids<3>(0, {{xi_level, eta_level, zeta_level}});\n\n  // Points are located at the end- and mid-points of each segment\n  // thus giving a set of points at the centers of each element as\n  // well as on their corners, edges, and faces\n  const size_t n_xi_segments = two_to_the(xi_level);\n  const size_t n_eta_segments = two_to_the(eta_level);\n  const size_t n_zeta_segments = two_to_the(zeta_level);\n  const size_t n_pts = (2 * n_xi_segments + 1) * (2 * n_eta_segments + 1) *\n                       (2 * n_zeta_segments + 1);\n  std::vector<BlockLogicalCoords<3>> block_logical_points{n_pts, std::nullopt};\n  const double xi_stride = 1.0 / n_xi_segments;\n  const double eta_stride = 1.0 / n_eta_segments;\n  const double zeta_stride = 1.0 / n_zeta_segments;\n  for (size_t i = 0; i <= 2 * n_xi_segments; ++i) {\n    for (size_t j = 0; j <= 2 * n_eta_segments; ++j) {\n      for (size_t k = 0; k <= 2 * n_zeta_segments; ++k) {\n        block_logical_points[k + (2 * n_zeta_segments + 1) *\n                                     (j + (2 * n_eta_segments + 1) * i)] =\n            IdPair<domain::BlockId,\n                   tnsr::I<double, 3, typename Frame::BlockLogical>>{\n                domain::BlockId{0},\n                tnsr::I<double, 3, typename Frame::BlockLogical>{\n                    {{-1.0 + i * xi_stride, -1.0 + j * eta_stride,\n                      -1.0 + k * zeta_stride}}}};\n      }\n    }\n  }\n\n  const auto result_unshuffled =\n      element_logical_coordinates(element_ids, block_logical_points);\n\n  MAKE_GENERATOR(gen);\n  std::shuffle(element_ids.begin(), element_ids.end(), gen);\n\n  const auto result_shuffled =\n      element_logical_coordinates(element_ids, block_logical_points);\n\n  size_t points_found = 0;\n  for (const auto& [id, coord_holder] : result_unshuffled) {\n    INFO(id);\n    points_found += coord_holder.offsets.size();\n    const auto& shuffled_coord_holder = result_shuffled.at(id);\n    CHECK(coord_holder.element_logical_coords ==\n          shuffled_coord_holder.element_logical_coords);\n    CHECK(coord_holder.offsets == shuffled_coord_holder.offsets);\n  }\n  CHECK(points_found == n_pts);\n}\n\nvoid test_block_logical_coordinates_with_roundoff_error() {\n  const auto shell = domain::creators::Sphere(\n      1., 3., domain::creators::Sphere::Excision{}, 0_st, 3_st, true);\n  const auto domain = shell.create_domain();\n\n  // Use this as roundoff error\n  const double eps = 1e-14;\n\n  // Choose some points on block boundaries with roundoff-error noise _in radial\n  // coords_ r, theta, phi (just to make them easier to choose)\n  std::vector<std::array<double, 3>> points{};\n  std::vector<size_t> expected_block_ids{};\n  // Block piercing z has ID 0, block piercing x has ID 4. Phi=0, Theta=Pi/4 is\n  // on their shared boundary. See also WedgeOrientations.png in the Sphere\n  // docs.\n  // - Safely in block 0\n  points.push_back({{2., M_PI_4 - 0.01, 0.}});\n  expected_block_ids.push_back(0);\n  // - Safely in block 4\n  points.push_back({{2., M_PI_4 + 0.01, 0.}});\n  expected_block_ids.push_back(4);\n  // - Exactly on their boundary: should resolve to block 0\n  points.push_back({{2., M_PI_4, 0.}});\n  expected_block_ids.push_back(0);\n  // - Away from the boundary by roundoff: should resolve to block 0\n  points.push_back({{2., M_PI_4 + eps, 0.}});\n  expected_block_ids.push_back(0);\n  points.push_back({{2., M_PI_4 - eps, 0.}});\n  expected_block_ids.push_back(0);\n  // - A bit further away from the boundary\n  points.push_back({{2., M_PI_4 + 10 * eps, 0.}});\n  expected_block_ids.push_back(4);\n  points.push_back({{2., M_PI_4 - 10 * eps, 0.}});\n  expected_block_ids.push_back(0);\n\n  // Transform to a Tensor of DataVectors\n  tnsr::I<DataVector, 3> inertial_coords{points.size()};\n  for (size_t i = 0; i < points.size(); ++i) {\n    const auto& [r, theta, phi] = points[i];\n    get<0>(inertial_coords)[i] = r * cos(phi) * sin(theta);\n    get<1>(inertial_coords)[i] = r * sin(phi) * sin(theta);\n    get<2>(inertial_coords)[i] = r * cos(theta);\n  }\n\n  const auto block_logical_coords =\n      block_logical_coordinates(domain, inertial_coords);\n  for (size_t i = 0; i < points.size(); ++i) {\n    const auto& point = points[i];\n    CAPTURE(point);\n    const auto& result = block_logical_coords[i];\n    CAPTURE(result);\n    REQUIRE(result.has_value());\n    CHECK(result->id.get_index() == expected_block_ids[i]);\n  }\n  // See also WedgeOrientations.png in the Sphere docs.\n  CHECK(get<0>(block_logical_coords[0]->data) < 1.0);\n  CHECK(get<1>(block_logical_coords[1]->data) < 1.0);\n  CHECK(get<0>(block_logical_coords[2]->data) == 1.0);\n  CHECK(get<0>(block_logical_coords[3]->data) == 1.0);\n  CHECK(get<0>(block_logical_coords[4]->data) == 1.0);\n  CHECK(get<1>(block_logical_coords[5]->data) < 1.0);\n  CHECK(get<0>(block_logical_coords[6]->data) < 1.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.BlockAndElementLogicalCoords\",\n                  \"[Domain][Unit]\") {\n  test_element_logical_coordinates();\n  test_block_and_element_logical_coordinates1();\n  test_block_and_element_logical_coordinates3();\n  fuzzy_test_block_and_element_logical_coordinates3(20);\n  fuzzy_test_block_and_element_logical_coordinates2(20);\n  fuzzy_test_block_and_element_logical_coordinates1(20);\n  fuzzy_test_block_and_element_logical_coordinates1(0);\n  const auto sphere = domain::creators::Sphere(\n      1.5, 2.5, domain::creators::Sphere::Excision{}, 2_st, 1_st, true);\n  fuzzy_test_block_and_element_logical_coordinates_shell(sphere, 20);\n  const auto spherical_shells =\n      domain::creators::SphericalShells(1.5, 2.5, 2_st, 2_st, 2_st);\n  fuzzy_test_block_and_element_logical_coordinates_shell(sphere, 20);\n  fuzzy_test_block_and_element_logical_coordinates_time_dependent_brick(20);\n  fuzzy_test_block_and_element_logical_coordinates_distorted_brick(20);\n  test_block_logical_coordinates1fail();\n  test_element_ids_are_uniquely_determined();\n  test_block_logical_coordinates_with_roundoff_error();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_CoordinatesTag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\nstruct BlockLogical;\n}  // namespace Frame\n\nnamespace domain {\nnamespace {\ntemplate <size_t Dim, typename T>\nvoid test_coordinates_compute_item(const Mesh<Dim>& mesh, T map) {\n  using map_tag = Tags::ElementMap<Dim, Frame::Grid>;\n  const auto box = db::create<\n      db::AddSimpleTags<Tags::Mesh<Dim>, map_tag>,\n      db::AddComputeTags<\n          Tags::LogicalCoordinates<Dim>,\n          Tags::MappedCoordinates<\n              map_tag, Tags::Coordinates<Dim, Frame::ElementLogical>>>>(\n      mesh,\n      ElementMap<Dim, Frame::Grid>(\n          ElementId<Dim>(0),\n          make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(map)));\n  CHECK_ITERABLE_APPROX(\n      (db::get<Tags::Coordinates<Dim, Frame::Grid>>(box)),\n      (make_coordinate_map<Frame::ElementLogical, Frame::Grid>(map)(\n          db::get<Tags::Coordinates<Dim, Frame::ElementLogical>>(box))));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordinatesTag\", \"[Unit][Domain]\") {\n  using Affine = CoordinateMaps::Affine;\n  using Affine2d = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using Affine3d = CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\n  test_coordinates_compute_item(\n      Mesh<1>{5, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto},\n      Affine{-1.0, 1.0, -0.3, 0.7});\n  test_coordinates_compute_item(\n      Mesh<2>{{{5, 7}},\n              Spectral::Basis::Legendre,\n              Spectral::Quadrature::GaussLobatto},\n      Affine2d{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55}});\n  test_coordinates_compute_item(\n      Mesh<3>{{{5, 6, 9}},\n              Spectral::Basis::Legendre,\n              Spectral::Quadrature::GaussLobatto},\n      Affine3d{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n               Affine{-1.0, 1.0, 2.3, 2.8}});\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_CoordsToDifferentFrame.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordsToDifferentFrame.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/Shape.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/StrahlkorperTransformations.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <typename SrcFrame, typename DestFrame>\nvoid test_coords_to_different_frame() {\n  const tnsr::I<DataVector, 3, SrcFrame> src_center{DataVector{0.03}};\n\n  const std::vector<double> radial_partitioning{};\n  const std::vector<domain::CoordinateMaps::Distribution> radial_distribution{\n      domain::CoordinateMaps::Distribution::Linear};\n\n  const domain::creators::Sphere domain_creator{\n      0.001,\n      10.0,\n      domain::creators::Sphere::Excision{},\n      1_st,\n      5_st,\n      false,\n      std::nullopt,\n      radial_partitioning,\n      radial_distribution,\n      ShellWedges::All,\n      std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          0.0, std::array<double, 3>({{0.0, 0.0, 0.0}}),\n          std::array<double, 3>({{0.01, 0.02, 0.03}}))};\n\n  const Domain<3> domain = domain_creator.create_domain();\n  const auto functions_of_time = domain_creator.functions_of_time();\n\n  const double time = 0.5;\n  tnsr::I<DataVector, 3, DestFrame> dest_center{DataVector{0.0}};\n\n  coords_to_different_frame(make_not_null(&dest_center), src_center, domain,\n                            functions_of_time, time);\n\n  tnsr::I<DataVector, 3, DestFrame> expected_center{DataVector{0.0}};\n\n  if constexpr (std::is_same_v<SrcFrame, ::Frame::Inertial>) {\n    expected_center[0] = src_center[0] - 0.005;\n    expected_center[1] = src_center[1] - 0.01;\n    expected_center[2] = src_center[2] - 0.015;\n  } else if (std::is_same_v<DestFrame, ::Frame::Inertial>) {\n    expected_center[0] = src_center[0] + 0.005;\n    expected_center[1] = src_center[1] + 0.01;\n    expected_center[2] = src_center[2] + 0.015;\n  } else {\n    expected_center[0] = src_center[0];\n    expected_center[1] = src_center[1];\n    expected_center[2] = src_center[2];\n  }\n  CHECK(expected_center == dest_center);\n}\nSPECTRE_TEST_CASE(\"Unit.Domain.CoordsToDifferentFrame\", \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  test_coords_to_different_frame<Frame::Grid, Frame::Inertial>();\n  test_coords_to_different_frame<Frame::Inertial, Frame::Distorted>();\n  test_coords_to_different_frame<Frame::Inertial, Frame::Grid>();\n  test_coords_to_different_frame<Frame::Grid, Frame::Inertial>();\n  test_coords_to_different_frame<Frame::Grid, Frame::Distorted>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/Test_CreateInitialElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <functional>\n#include <initializer_list>\n#include <memory>\n#include <pup.h>\n#include <tuple>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\nvoid test_create_initial_element(\n    const ElementId<2>& element_id, const std::vector<Block<2>>& blocks,\n    const std::vector<std::array<size_t, 2>>& refinement_levels,\n    const DirectionMap<2, Neighbors<2>>& expected_neighbors,\n    const std::array<domain::Topology, 2>& topologies =\n        domain::topologies::hypercube<2>) {\n  const auto created_element =\n      domain::create_initial_element(element_id, blocks, refinement_levels);\n  const Element<2> expected_element{element_id, expected_neighbors, topologies};\n  CHECK(created_element == expected_element);\n}\n\nvoid test_h_refinement() {\n  const auto make_check = [](const ElementId<3>& self_id,\n                             const Direction<3>& neighbor_direction) {\n    return [self_id, neighbor_direction](\n               const OrientationMap<3>& neighbor_orientation,\n               const std::array<size_t, 3>& neighbor_refinement,\n               const std::unordered_set<ElementId<3>>& expected_neighbors) {\n      CAPTURE(neighbor_orientation);\n      CAPTURE(neighbor_refinement);\n      std::vector<Block<3>> blocks;\n      blocks.emplace_back(\n          Block<3>(domain::make_coordinate_map_base<Frame::BlockLogical,\n                                                    Frame::Inertial>(\n                       domain::CoordinateMaps::Identity<3>{}),\n                   0, {{neighbor_direction, {1, neighbor_orientation}}}));\n      blocks.emplace_back(\n          Block<3>(domain::make_coordinate_map_base<Frame::BlockLogical,\n                                                    Frame::Inertial>(\n                       domain::CoordinateMaps::Identity<3>{}),\n                   1,\n                   {{neighbor_orientation(neighbor_direction).opposite(),\n                     {1, neighbor_orientation.inverse_map()}}}));\n      const std::vector<std::array<size_t, 3>> refinement_levels{\n          {{1, 1, 1}}, neighbor_refinement};\n\n      const auto refined_neighbors =\n          domain::create_initial_element(self_id, blocks, refinement_levels)\n              .neighbors()\n              .at(neighbor_direction)\n              .ids();\n      CHECK(refined_neighbors == expected_neighbors);\n    };\n  };\n\n  const auto check_upper =\n      make_check({0, {{{1, 1}, {1, 0}, {1, 1}}}}, Direction<3>::upper_xi());\n  const auto check_lower =\n      make_check({0, {{{1, 0}, {1, 0}, {1, 1}}}}, Direction<3>::lower_xi());\n\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n  const OrientationMap<3> rotated{\n      {{Direction<3>::upper_zeta(), Direction<3>::upper_xi(),\n        Direction<3>::upper_eta()}}};\n  const OrientationMap<3> reflected{\n      {{Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n        Direction<3>::upper_zeta()}}};\n\n  // Same tangential refinement\n  check_upper(aligned, {{1, 1, 1}}, {{1, {{{1, 0}, {1, 0}, {1, 1}}}}});\n  check_lower(aligned, {{1, 1, 1}}, {{1, {{{1, 1}, {1, 0}, {1, 1}}}}});\n  check_upper(rotated, {{1, 1, 1}}, {{1, {{{1, 0}, {1, 1}, {1, 0}}}}});\n  check_lower(rotated, {{1, 1, 1}}, {{1, {{{1, 0}, {1, 1}, {1, 1}}}}});\n  check_upper(reflected, {{1, 1, 1}}, {{1, {{{1, 1}, {1, 1}, {1, 1}}}}});\n  check_lower(reflected, {{1, 1, 1}}, {{1, {{{1, 0}, {1, 1}, {1, 1}}}}});\n\n  check_upper(aligned, {{0, 1, 1}}, {{1, {{{0, 0}, {1, 0}, {1, 1}}}}});\n  check_lower(aligned, {{0, 1, 1}}, {{1, {{{0, 0}, {1, 0}, {1, 1}}}}});\n  check_upper(rotated, {{1, 1, 0}}, {{1, {{{1, 0}, {1, 1}, {0, 0}}}}});\n  check_lower(rotated, {{1, 1, 0}}, {{1, {{{1, 0}, {1, 1}, {0, 0}}}}});\n  check_upper(reflected, {{0, 1, 1}}, {{1, {{{0, 0}, {1, 1}, {1, 1}}}}});\n  check_lower(reflected, {{0, 1, 1}}, {{1, {{{0, 0}, {1, 1}, {1, 1}}}}});\n\n  check_upper(aligned, {{2, 1, 1}}, {{1, {{{2, 0}, {1, 0}, {1, 1}}}}});\n  check_lower(aligned, {{2, 1, 1}}, {{1, {{{2, 3}, {1, 0}, {1, 1}}}}});\n  check_upper(rotated, {{1, 1, 2}}, {{1, {{{1, 0}, {1, 1}, {2, 0}}}}});\n  check_lower(rotated, {{1, 1, 2}}, {{1, {{{1, 0}, {1, 1}, {2, 3}}}}});\n  check_upper(reflected, {{2, 1, 1}}, {{1, {{{2, 3}, {1, 1}, {1, 1}}}}});\n  check_lower(reflected, {{2, 1, 1}}, {{1, {{{2, 0}, {1, 1}, {1, 1}}}}});\n\n  // Larger in both dimensions\n  check_upper(aligned, {{1, 0, 0}}, {{1, {{{1, 0}, {0, 0}, {0, 0}}}}});\n  check_lower(aligned, {{1, 0, 0}}, {{1, {{{1, 1}, {0, 0}, {0, 0}}}}});\n  check_upper(rotated, {{0, 0, 1}}, {{1, {{{0, 0}, {0, 0}, {1, 0}}}}});\n  check_lower(rotated, {{0, 0, 1}}, {{1, {{{0, 0}, {0, 0}, {1, 1}}}}});\n  check_upper(reflected, {{1, 0, 0}}, {{1, {{{1, 1}, {0, 0}, {0, 0}}}}});\n  check_lower(reflected, {{1, 0, 0}}, {{1, {{{1, 0}, {0, 0}, {0, 0}}}}});\n\n  check_upper(aligned, {{0, 0, 0}}, {{1, {{{0, 0}, {0, 0}, {0, 0}}}}});\n  check_lower(aligned, {{0, 0, 0}}, {{1, {{{0, 0}, {0, 0}, {0, 0}}}}});\n  check_upper(rotated, {{0, 0, 0}}, {{1, {{{0, 0}, {0, 0}, {0, 0}}}}});\n  check_lower(rotated, {{0, 0, 0}}, {{1, {{{0, 0}, {0, 0}, {0, 0}}}}});\n  check_upper(reflected, {{0, 0, 0}}, {{1, {{{0, 0}, {0, 0}, {0, 0}}}}});\n  check_lower(reflected, {{0, 0, 0}}, {{1, {{{0, 0}, {0, 0}, {0, 0}}}}});\n\n  check_upper(aligned, {{2, 0, 0}}, {{1, {{{2, 0}, {0, 0}, {0, 0}}}}});\n  check_lower(aligned, {{2, 0, 0}}, {{1, {{{2, 3}, {0, 0}, {0, 0}}}}});\n  check_upper(rotated, {{0, 0, 2}}, {{1, {{{0, 0}, {0, 0}, {2, 0}}}}});\n  check_lower(rotated, {{0, 0, 2}}, {{1, {{{0, 0}, {0, 0}, {2, 3}}}}});\n  check_upper(reflected, {{2, 0, 0}}, {{1, {{{2, 3}, {0, 0}, {0, 0}}}}});\n  check_lower(reflected, {{2, 0, 0}}, {{1, {{{2, 0}, {0, 0}, {0, 0}}}}});\n\n  // Smaller in both dimensions\n  check_upper(aligned, {{1, 2, 2}},\n              {{1, {{{1, 0}, {2, 0}, {2, 2}}}},\n               {1, {{{1, 0}, {2, 0}, {2, 3}}}},\n               {1, {{{1, 0}, {2, 1}, {2, 2}}}},\n               {1, {{{1, 0}, {2, 1}, {2, 3}}}}});\n  check_lower(aligned, {{1, 2, 2}},\n              {{1, {{{1, 1}, {2, 0}, {2, 2}}}},\n               {1, {{{1, 1}, {2, 0}, {2, 3}}}},\n               {1, {{{1, 1}, {2, 1}, {2, 2}}}},\n               {1, {{{1, 1}, {2, 1}, {2, 3}}}}});\n  check_upper(rotated, {{2, 2, 1}},\n              {{1, {{{2, 0}, {2, 2}, {1, 0}}}},\n               {1, {{{2, 0}, {2, 3}, {1, 0}}}},\n               {1, {{{2, 1}, {2, 2}, {1, 0}}}},\n               {1, {{{2, 1}, {2, 3}, {1, 0}}}}});\n  check_lower(rotated, {{2, 2, 1}},\n              {{1, {{{2, 0}, {2, 2}, {1, 1}}}},\n               {1, {{{2, 0}, {2, 3}, {1, 1}}}},\n               {1, {{{2, 1}, {2, 2}, {1, 1}}}},\n               {1, {{{2, 1}, {2, 3}, {1, 1}}}}});\n  check_upper(reflected, {{1, 2, 2}},\n              {{1, {{{1, 1}, {2, 3}, {2, 2}}}},\n               {1, {{{1, 1}, {2, 3}, {2, 3}}}},\n               {1, {{{1, 1}, {2, 2}, {2, 2}}}},\n               {1, {{{1, 1}, {2, 2}, {2, 3}}}}});\n  check_lower(reflected, {{1, 2, 2}},\n              {{1, {{{1, 0}, {2, 3}, {2, 2}}}},\n               {1, {{{1, 0}, {2, 3}, {2, 3}}}},\n               {1, {{{1, 0}, {2, 2}, {2, 2}}}},\n               {1, {{{1, 0}, {2, 2}, {2, 3}}}}});\n\n  check_upper(aligned, {{0, 2, 2}},\n              {{1, {{{0, 0}, {2, 0}, {2, 2}}}},\n               {1, {{{0, 0}, {2, 0}, {2, 3}}}},\n               {1, {{{0, 0}, {2, 1}, {2, 2}}}},\n               {1, {{{0, 0}, {2, 1}, {2, 3}}}}});\n  check_lower(aligned, {{0, 2, 2}},\n              {{1, {{{0, 0}, {2, 0}, {2, 2}}}},\n               {1, {{{0, 0}, {2, 0}, {2, 3}}}},\n               {1, {{{0, 0}, {2, 1}, {2, 2}}}},\n               {1, {{{0, 0}, {2, 1}, {2, 3}}}}});\n  check_upper(rotated, {{2, 2, 0}},\n              {{1, {{{2, 0}, {2, 2}, {0, 0}}}},\n               {1, {{{2, 0}, {2, 3}, {0, 0}}}},\n               {1, {{{2, 1}, {2, 2}, {0, 0}}}},\n               {1, {{{2, 1}, {2, 3}, {0, 0}}}}});\n  check_lower(rotated, {{2, 2, 0}},\n              {{1, {{{2, 0}, {2, 2}, {0, 0}}}},\n               {1, {{{2, 0}, {2, 3}, {0, 0}}}},\n               {1, {{{2, 1}, {2, 2}, {0, 0}}}},\n               {1, {{{2, 1}, {2, 3}, {0, 0}}}}});\n  check_upper(reflected, {{0, 2, 2}},\n              {{1, {{{0, 0}, {2, 3}, {2, 2}}}},\n               {1, {{{0, 0}, {2, 3}, {2, 3}}}},\n               {1, {{{0, 0}, {2, 2}, {2, 2}}}},\n               {1, {{{0, 0}, {2, 2}, {2, 3}}}}});\n  check_lower(reflected, {{0, 2, 2}},\n              {{1, {{{0, 0}, {2, 3}, {2, 2}}}},\n               {1, {{{0, 0}, {2, 3}, {2, 3}}}},\n               {1, {{{0, 0}, {2, 2}, {2, 2}}}},\n               {1, {{{0, 0}, {2, 2}, {2, 3}}}}});\n\n  check_upper(aligned, {{2, 2, 2}},\n              {{1, {{{2, 0}, {2, 0}, {2, 2}}}},\n               {1, {{{2, 0}, {2, 0}, {2, 3}}}},\n               {1, {{{2, 0}, {2, 1}, {2, 2}}}},\n               {1, {{{2, 0}, {2, 1}, {2, 3}}}}});\n  check_lower(aligned, {{2, 2, 2}},\n              {{1, {{{2, 3}, {2, 0}, {2, 2}}}},\n               {1, {{{2, 3}, {2, 0}, {2, 3}}}},\n               {1, {{{2, 3}, {2, 1}, {2, 2}}}},\n               {1, {{{2, 3}, {2, 1}, {2, 3}}}}});\n  check_upper(rotated, {{2, 2, 2}},\n              {{1, {{{2, 0}, {2, 2}, {2, 0}}}},\n               {1, {{{2, 0}, {2, 3}, {2, 0}}}},\n               {1, {{{2, 1}, {2, 2}, {2, 0}}}},\n               {1, {{{2, 1}, {2, 3}, {2, 0}}}}});\n  check_lower(rotated, {{2, 2, 2}},\n              {{1, {{{2, 0}, {2, 2}, {2, 3}}}},\n               {1, {{{2, 0}, {2, 3}, {2, 3}}}},\n               {1, {{{2, 1}, {2, 2}, {2, 3}}}},\n               {1, {{{2, 1}, {2, 3}, {2, 3}}}}});\n  check_upper(reflected, {{2, 2, 2}},\n              {{1, {{{2, 3}, {2, 3}, {2, 2}}}},\n               {1, {{{2, 3}, {2, 3}, {2, 3}}}},\n               {1, {{{2, 3}, {2, 2}, {2, 2}}}},\n               {1, {{{2, 3}, {2, 2}, {2, 3}}}}});\n  check_lower(reflected, {{2, 2, 2}},\n              {{1, {{{2, 0}, {2, 3}, {2, 2}}}},\n               {1, {{{2, 0}, {2, 3}, {2, 3}}}},\n               {1, {{{2, 0}, {2, 2}, {2, 2}}}},\n               {1, {{{2, 0}, {2, 2}, {2, 3}}}}});\n\n  // Larger in one dimension\n  check_upper(aligned, {{1, 0, 1}}, {{1, {{{1, 0}, {0, 0}, {1, 1}}}}});\n  check_lower(aligned, {{1, 0, 1}}, {{1, {{{1, 1}, {0, 0}, {1, 1}}}}});\n  check_upper(rotated, {{0, 1, 1}}, {{1, {{{0, 0}, {1, 1}, {1, 0}}}}});\n  check_lower(rotated, {{0, 1, 1}}, {{1, {{{0, 0}, {1, 1}, {1, 1}}}}});\n  check_upper(reflected, {{1, 0, 1}}, {{1, {{{1, 1}, {0, 0}, {1, 1}}}}});\n  check_lower(reflected, {{1, 0, 1}}, {{1, {{{1, 0}, {0, 0}, {1, 1}}}}});\n\n  check_upper(aligned, {{0, 0, 1}}, {{1, {{{0, 0}, {0, 0}, {1, 1}}}}});\n  check_lower(aligned, {{0, 0, 1}}, {{1, {{{0, 0}, {0, 0}, {1, 1}}}}});\n  check_upper(rotated, {{0, 1, 0}}, {{1, {{{0, 0}, {1, 1}, {0, 0}}}}});\n  check_lower(rotated, {{0, 1, 0}}, {{1, {{{0, 0}, {1, 1}, {0, 0}}}}});\n  check_upper(reflected, {{0, 0, 1}}, {{1, {{{0, 0}, {0, 0}, {1, 1}}}}});\n  check_lower(reflected, {{0, 0, 1}}, {{1, {{{0, 0}, {0, 0}, {1, 1}}}}});\n\n  check_upper(aligned, {{2, 0, 1}}, {{1, {{{2, 0}, {0, 0}, {1, 1}}}}});\n  check_lower(aligned, {{2, 0, 1}}, {{1, {{{2, 3}, {0, 0}, {1, 1}}}}});\n  check_upper(rotated, {{0, 1, 2}}, {{1, {{{0, 0}, {1, 1}, {2, 0}}}}});\n  check_lower(rotated, {{0, 1, 2}}, {{1, {{{0, 0}, {1, 1}, {2, 3}}}}});\n  check_upper(reflected, {{2, 0, 1}}, {{1, {{{2, 3}, {0, 0}, {1, 1}}}}});\n  check_lower(reflected, {{2, 0, 1}}, {{1, {{{2, 0}, {0, 0}, {1, 1}}}}});\n\n  // Smaller in one dimension\n  check_upper(\n      aligned, {{1, 2, 1}},\n      {{1, {{{1, 0}, {2, 0}, {1, 1}}}}, {1, {{{1, 0}, {2, 1}, {1, 1}}}}});\n  check_lower(\n      aligned, {{1, 2, 1}},\n      {{1, {{{1, 1}, {2, 0}, {1, 1}}}}, {1, {{{1, 1}, {2, 1}, {1, 1}}}}});\n  check_upper(\n      rotated, {{2, 1, 1}},\n      {{1, {{{2, 0}, {1, 1}, {1, 0}}}}, {1, {{{2, 1}, {1, 1}, {1, 0}}}}});\n  check_lower(\n      rotated, {{2, 1, 1}},\n      {{1, {{{2, 0}, {1, 1}, {1, 1}}}}, {1, {{{2, 1}, {1, 1}, {1, 1}}}}});\n  check_upper(\n      reflected, {{1, 2, 1}},\n      {{1, {{{1, 1}, {2, 3}, {1, 1}}}}, {1, {{{1, 1}, {2, 2}, {1, 1}}}}});\n  check_lower(\n      reflected, {{1, 2, 1}},\n      {{1, {{{1, 0}, {2, 3}, {1, 1}}}}, {1, {{{1, 0}, {2, 2}, {1, 1}}}}});\n\n  check_upper(\n      aligned, {{0, 2, 1}},\n      {{1, {{{0, 0}, {2, 0}, {1, 1}}}}, {1, {{{0, 0}, {2, 1}, {1, 1}}}}});\n  check_lower(\n      aligned, {{0, 2, 1}},\n      {{1, {{{0, 0}, {2, 0}, {1, 1}}}}, {1, {{{0, 0}, {2, 1}, {1, 1}}}}});\n  check_upper(\n      rotated, {{2, 1, 0}},\n      {{1, {{{2, 0}, {1, 1}, {0, 0}}}}, {1, {{{2, 1}, {1, 1}, {0, 0}}}}});\n  check_lower(\n      rotated, {{2, 1, 0}},\n      {{1, {{{2, 0}, {1, 1}, {0, 0}}}}, {1, {{{2, 1}, {1, 1}, {0, 0}}}}});\n  check_upper(\n      reflected, {{0, 2, 1}},\n      {{1, {{{0, 0}, {2, 3}, {1, 1}}}}, {1, {{{0, 0}, {2, 2}, {1, 1}}}}});\n  check_lower(\n      reflected, {{0, 2, 1}},\n      {{1, {{{0, 0}, {2, 3}, {1, 1}}}}, {1, {{{0, 0}, {2, 2}, {1, 1}}}}});\n\n  check_upper(\n      aligned, {{2, 2, 1}},\n      {{1, {{{2, 0}, {2, 0}, {1, 1}}}}, {1, {{{2, 0}, {2, 1}, {1, 1}}}}});\n  check_lower(\n      aligned, {{2, 2, 1}},\n      {{1, {{{2, 3}, {2, 0}, {1, 1}}}}, {1, {{{2, 3}, {2, 1}, {1, 1}}}}});\n  check_upper(\n      rotated, {{2, 1, 2}},\n      {{1, {{{2, 0}, {1, 1}, {2, 0}}}}, {1, {{{2, 1}, {1, 1}, {2, 0}}}}});\n  check_lower(\n      rotated, {{2, 1, 2}},\n      {{1, {{{2, 0}, {1, 1}, {2, 3}}}}, {1, {{{2, 1}, {1, 1}, {2, 3}}}}});\n  check_upper(\n      reflected, {{2, 2, 1}},\n      {{1, {{{2, 3}, {2, 3}, {1, 1}}}}, {1, {{{2, 3}, {2, 2}, {1, 1}}}}});\n  check_lower(\n      reflected, {{2, 2, 1}},\n      {{1, {{{2, 0}, {2, 3}, {1, 1}}}}, {1, {{{2, 0}, {2, 2}, {1, 1}}}}});\n\n  // Larger in one dimension and smaller in another dimension\n  check_upper(\n      aligned, {{1, 2, 0}},\n      {{1, {{{1, 0}, {2, 0}, {0, 0}}}}, {1, {{{1, 0}, {2, 1}, {0, 0}}}}});\n  check_lower(\n      aligned, {{1, 2, 0}},\n      {{1, {{{1, 1}, {2, 0}, {0, 0}}}}, {1, {{{1, 1}, {2, 1}, {0, 0}}}}});\n  check_upper(\n      rotated, {{2, 0, 1}},\n      {{1, {{{2, 0}, {0, 0}, {1, 0}}}}, {1, {{{2, 1}, {0, 0}, {1, 0}}}}});\n  check_lower(\n      rotated, {{2, 0, 1}},\n      {{1, {{{2, 0}, {0, 0}, {1, 1}}}}, {1, {{{2, 1}, {0, 0}, {1, 1}}}}});\n  check_upper(\n      reflected, {{1, 2, 0}},\n      {{1, {{{1, 1}, {2, 3}, {0, 0}}}}, {1, {{{1, 1}, {2, 2}, {0, 0}}}}});\n  check_lower(\n      reflected, {{1, 2, 0}},\n      {{1, {{{1, 0}, {2, 3}, {0, 0}}}}, {1, {{{1, 0}, {2, 2}, {0, 0}}}}});\n\n  check_upper(\n      aligned, {{0, 2, 0}},\n      {{1, {{{0, 0}, {2, 0}, {0, 0}}}}, {1, {{{0, 0}, {2, 1}, {0, 0}}}}});\n  check_lower(\n      aligned, {{0, 2, 0}},\n      {{1, {{{0, 0}, {2, 0}, {0, 0}}}}, {1, {{{0, 0}, {2, 1}, {0, 0}}}}});\n  check_upper(\n      rotated, {{2, 0, 0}},\n      {{1, {{{2, 0}, {0, 0}, {0, 0}}}}, {1, {{{2, 1}, {0, 0}, {0, 0}}}}});\n  check_lower(\n      rotated, {{2, 0, 0}},\n      {{1, {{{2, 0}, {0, 0}, {0, 0}}}}, {1, {{{2, 1}, {0, 0}, {0, 0}}}}});\n  check_upper(\n      reflected, {{0, 2, 0}},\n      {{1, {{{0, 0}, {2, 3}, {0, 0}}}}, {1, {{{0, 0}, {2, 2}, {0, 0}}}}});\n  check_lower(\n      reflected, {{0, 2, 0}},\n      {{1, {{{0, 0}, {2, 3}, {0, 0}}}}, {1, {{{0, 0}, {2, 2}, {0, 0}}}}});\n\n  check_upper(\n      aligned, {{2, 2, 0}},\n      {{1, {{{2, 0}, {2, 0}, {0, 0}}}}, {1, {{{2, 0}, {2, 1}, {0, 0}}}}});\n  check_lower(\n      aligned, {{2, 2, 0}},\n      {{1, {{{2, 3}, {2, 0}, {0, 0}}}}, {1, {{{2, 3}, {2, 1}, {0, 0}}}}});\n  check_upper(\n      rotated, {{2, 0, 2}},\n      {{1, {{{2, 0}, {0, 0}, {2, 0}}}}, {1, {{{2, 1}, {0, 0}, {2, 0}}}}});\n  check_lower(\n      rotated, {{2, 0, 2}},\n      {{1, {{{2, 0}, {0, 0}, {2, 3}}}}, {1, {{{2, 1}, {0, 0}, {2, 3}}}}});\n  check_upper(\n      reflected, {{2, 2, 0}},\n      {{1, {{{2, 3}, {2, 3}, {0, 0}}}}, {1, {{{2, 3}, {2, 2}, {0, 0}}}}});\n  check_lower(\n      reflected, {{2, 2, 0}},\n      {{1, {{{2, 0}, {2, 3}, {0, 0}}}}, {1, {{{2, 0}, {2, 2}, {0, 0}}}}});\n\n  // Larger perpendicular refinement\n  const auto check_perpendicular_refinement_upper =\n      make_check({0, {{{3, 7}, {0, 0}, {0, 0}}}}, Direction<3>::upper_xi());\n  const auto check_perpendicular_refinement_lower =\n      make_check({0, {{{3, 0}, {0, 0}, {0, 0}}}}, Direction<3>::lower_xi());\n\n  check_perpendicular_refinement_upper(aligned, {{1, 0, 0}},\n                                       {{1, {{{1, 0}, {0, 0}, {0, 0}}}}});\n  check_perpendicular_refinement_lower(aligned, {{1, 0, 0}},\n                                       {{1, {{{1, 1}, {0, 0}, {0, 0}}}}});\n  check_perpendicular_refinement_upper(rotated, {{0, 0, 1}},\n                                       {{1, {{{0, 0}, {0, 0}, {1, 0}}}}});\n  check_perpendicular_refinement_lower(rotated, {{0, 0, 1}},\n                                       {{1, {{{0, 0}, {0, 0}, {1, 1}}}}});\n  check_perpendicular_refinement_upper(reflected, {{1, 0, 0}},\n                                       {{1, {{{1, 1}, {0, 0}, {0, 0}}}}});\n  check_perpendicular_refinement_lower(reflected, {{1, 0, 0}},\n                                       {{1, {{{1, 0}, {0, 0}, {0, 0}}}}});\n\n  check_perpendicular_refinement_upper(aligned, {{5, 0, 0}},\n                                       {{1, {{{5, 0}, {0, 0}, {0, 0}}}}});\n  check_perpendicular_refinement_lower(aligned, {{5, 0, 0}},\n                                       {{1, {{{5, 31}, {0, 0}, {0, 0}}}}});\n  check_perpendicular_refinement_upper(rotated, {{0, 0, 5}},\n                                       {{1, {{{0, 0}, {0, 0}, {5, 0}}}}});\n  check_perpendicular_refinement_lower(rotated, {{0, 0, 5}},\n                                       {{1, {{{0, 0}, {0, 0}, {5, 31}}}}});\n  check_perpendicular_refinement_upper(reflected, {{5, 0, 0}},\n                                       {{1, {{{5, 31}, {0, 0}, {0, 0}}}}});\n  check_perpendicular_refinement_lower(reflected, {{5, 0, 0}},\n                                       {{1, {{{5, 0}, {0, 0}, {0, 0}}}}});\n}\n\nvoid test_nonconforming_blocks() {\n  const OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n  std::vector<Block<2>> blocks;\n  blocks.emplace_back(\n      nullptr, 0,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::upper_xi(),\n           BlockNeighbors<2>{\n               {1, 2, 3, 4},\n               {{1, aligned}, {2, aligned}, {3, aligned}, {4, aligned}},\n               false}}},\n      \"Annulus\", std::array{domain::Topology::I1, domain::Topology::S1});\n  blocks.emplace_back(\n      nullptr, 1,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::lower_xi(), BlockNeighbors<2>{0, aligned}},\n          {Direction<2>::lower_eta(), BlockNeighbors<2>{2, aligned}},\n          {Direction<2>::upper_eta(), BlockNeighbors<2>{4, aligned}}},\n      \"North\", std::array{domain::Topology::I1, domain::Topology::I1});\n  blocks.emplace_back(\n      nullptr, 2,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::lower_xi(), BlockNeighbors<2>{0, aligned}},\n          {Direction<2>::lower_eta(), BlockNeighbors<2>{3, aligned}},\n          {Direction<2>::upper_eta(), BlockNeighbors<2>{1, aligned}}},\n      \"East\", std::array{domain::Topology::I1, domain::Topology::I1});\n  blocks.emplace_back(\n      nullptr, 3,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::lower_xi(), BlockNeighbors<2>{0, aligned}},\n          {Direction<2>::lower_eta(), BlockNeighbors<2>{4, aligned}},\n          {Direction<2>::upper_eta(), BlockNeighbors<2>{2, aligned}}},\n      \"South\", std::array{domain::Topology::I1, domain::Topology::I1});\n  blocks.emplace_back(\n      nullptr, 4,\n      DirectionMap<2, BlockNeighbors<2>>{\n          {Direction<2>::lower_xi(), BlockNeighbors<2>{0, aligned}},\n          {Direction<2>::lower_eta(), BlockNeighbors<2>{1, aligned}},\n          {Direction<2>::upper_eta(), BlockNeighbors<2>{3, aligned}}},\n      \"West\", std::array{domain::Topology::I1, domain::Topology::I1});\n  const std::vector<std::array<size_t, 2>> initial_refinement_levels{\n      std::array{2_st, 0_st}, std::array{0_st, 1_st}, std::array{0_st, 1_st},\n      std::array{0_st, 1_st}, std::array{0_st, 1_st}};\n  const ElementId<2> annulus_u{0, std::array{SegmentId{2, 3}, SegmentId{0, 0}}};\n  const ElementId<2> annulus_m{0, std::array{SegmentId{2, 2}, SegmentId{0, 0}}};\n  const ElementId<2> north_l{1, std::array{SegmentId{0, 0}, SegmentId{1, 0}}};\n  const ElementId<2> north_u{1, std::array{SegmentId{0, 0}, SegmentId{1, 1}}};\n  const ElementId<2> east_l{2, std::array{SegmentId{0, 0}, SegmentId{1, 0}}};\n  const ElementId<2> east_u{2, std::array{SegmentId{0, 0}, SegmentId{1, 1}}};\n  const ElementId<2> south_l{3, std::array{SegmentId{0, 0}, SegmentId{1, 0}}};\n  const ElementId<2> south_u{3, std::array{SegmentId{0, 0}, SegmentId{1, 1}}};\n  const ElementId<2> west_l{4, std::array{SegmentId{0, 0}, SegmentId{1, 0}}};\n  const ElementId<2> west_u{4, std::array{SegmentId{0, 0}, SegmentId{1, 1}}};\n  test_create_initial_element(\n      annulus_u, blocks, initial_refinement_levels,\n      {{Direction<2>::lower_xi(), Neighbors<2>{annulus_m, aligned}},\n       {Direction<2>::upper_xi(),\n        Neighbors<2>{{north_l, north_u, east_l, east_u, south_l, south_u,\n                      west_l, west_u},\n                     {{1, aligned}, {2, aligned}, {3, aligned}, {4, aligned}},\n                     false}}},\n      domain::topologies::annulus);\n  test_create_initial_element(\n      north_l, blocks, initial_refinement_levels,\n      {{Direction<2>::lower_xi(), Neighbors<2>{annulus_u, aligned}},\n       {Direction<2>::lower_eta(), Neighbors<2>{east_u, aligned}},\n       {Direction<2>::upper_eta(), Neighbors<2>{north_u, aligned}}},\n      domain::topologies::hypercube<2>);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.CreateInitialElement\", \"[Domain][Unit]\") {\n  OrientationMap<2> aligned(\n      make_array(Direction<2>::upper_xi(), Direction<2>::upper_eta()));\n  OrientationMap<2> unaligned(\n      make_array(Direction<2>::lower_eta(), Direction<2>::upper_xi()));\n  std::vector<Block<2>> blocks;\n  blocks.emplace_back(Block<2>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<2>{}),\n      0,\n      {{Direction<2>::upper_xi(), BlockNeighbors<2>{1, aligned}},\n       {Direction<2>::upper_eta(), BlockNeighbors<2>{2, unaligned}}}));\n  blocks.emplace_back(Block<2>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<2>{}),\n      1, {{Direction<2>::lower_xi(), BlockNeighbors<2>{0, aligned}}}));\n  blocks.emplace_back(Block<2>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<2>{}),\n      2,\n      {{Direction<2>::lower_xi(),\n        BlockNeighbors<2>{0, unaligned.inverse_map()}}}));\n  std::vector<std::array<size_t, 2>> refinement{{{2, 3}}, {{2, 3}}, {{3, 2}}};\n\n  // interior element\n  test_create_initial_element(\n      ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{3, 4}}}}, blocks, refinement,\n      {{Direction<2>::upper_xi(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{3, 4}}}}},\n                     aligned}},\n       {Direction<2>::lower_xi(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 1}, SegmentId{3, 4}}}}},\n                     aligned}},\n       {Direction<2>::upper_eta(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{3, 5}}}}},\n                     aligned}},\n       {Direction<2>::lower_eta(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{3, 3}}}}},\n                     aligned}}});\n\n  // element on external boundary\n  test_create_initial_element(\n      ElementId<2>{0, {{SegmentId{2, 0}, SegmentId{3, 0}}}}, blocks, refinement,\n      {{Direction<2>::upper_xi(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 1}, SegmentId{3, 0}}}}},\n                     aligned}},\n       {Direction<2>::upper_eta(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 0}, SegmentId{3, 1}}}}},\n                     aligned}}});\n\n  // element bounding aligned neighbor block\n  test_create_initial_element(\n      ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{3, 4}}}}, blocks, refinement,\n      {{Direction<2>::upper_xi(),\n        Neighbors<2>{{ElementId<2>{1, {{SegmentId{2, 0}, SegmentId{3, 4}}}}},\n                     aligned}},\n       {Direction<2>::lower_xi(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{3, 4}}}}},\n                     aligned}},\n       {Direction<2>::upper_eta(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{3, 5}}}}},\n                     aligned}},\n       {Direction<2>::lower_eta(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{3, 3}}}}},\n                     aligned}}});\n\n  // element bounding unaligned neighbor block\n  test_create_initial_element(\n      ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{3, 7}}}}, blocks, refinement,\n      {{Direction<2>::upper_xi(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{3, 7}}}}},\n                     aligned}},\n       {Direction<2>::lower_xi(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 1}, SegmentId{3, 7}}}}},\n                     aligned}},\n       {Direction<2>::upper_eta(),\n        Neighbors<2>{{ElementId<2>{2, {{SegmentId{3, 0}, SegmentId{2, 1}}}}},\n                     unaligned}},\n       {Direction<2>::lower_eta(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{3, 6}}}}},\n                     aligned}}});\n\n  // element bounding both neighbor blocks\n  test_create_initial_element(\n      ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{3, 7}}}}, blocks, refinement,\n      {{Direction<2>::upper_xi(),\n        Neighbors<2>{{ElementId<2>{1, {{SegmentId{2, 0}, SegmentId{3, 7}}}}},\n                     aligned}},\n       {Direction<2>::lower_xi(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{3, 7}}}}},\n                     aligned}},\n       {Direction<2>::upper_eta(),\n        Neighbors<2>{{ElementId<2>{2, {{SegmentId{3, 0}, SegmentId{2, 0}}}}},\n                     unaligned}},\n       {Direction<2>::lower_eta(),\n        Neighbors<2>{{ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{3, 6}}}}},\n                     aligned}}});\n\n  {\n    // element with a non-zero grid index\n    const size_t grid_index = 3;\n    test_create_initial_element(\n        ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{3, 4}}}, grid_index},\n        blocks, refinement,\n        {{Direction<2>::upper_xi(),\n          Neighbors<2>{\n              {ElementId<2>{\n                  0, {{SegmentId{2, 3}, SegmentId{3, 4}}}, grid_index}},\n              aligned}},\n         {Direction<2>::lower_xi(),\n          Neighbors<2>{\n              {ElementId<2>{\n                  0, {{SegmentId{2, 1}, SegmentId{3, 4}}}, grid_index}},\n              aligned}},\n         {Direction<2>::upper_eta(),\n          Neighbors<2>{\n              {ElementId<2>{\n                  0, {{SegmentId{2, 2}, SegmentId{3, 5}}}, grid_index}},\n              aligned}},\n         {Direction<2>::lower_eta(),\n          Neighbors<2>{\n              {ElementId<2>{\n                  0, {{SegmentId{2, 2}, SegmentId{3, 3}}}, grid_index}},\n              aligned}}});\n  }\n\n  test_h_refinement();\n  test_nonconforming_blocks();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_Domain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Shape.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/ShapeMapTransitionFunction.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/SphereTransition.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/ShapeMapTransitionFunctions/Wedge.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/ExpandOverBlocks.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/BinaryCompactObject.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain {\nnamespace {\n\nvoid test_1d_domains() {\n  using Translation = domain::CoordinateMaps::TimeDependent::Translation<1>;\n  using TranslationGridDistorted =\n      domain::CoordinateMap<Frame::Grid, Frame::Distorted, Translation>;\n  using TranslationDistortedInertial =\n      domain::CoordinateMap<Frame::Distorted, Frame::Inertial, Translation>;\n  {\n    using LogicalToGridCoordinateMap =\n        CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                      CoordinateMaps::Identity<1>>;\n\n    using GridToInertialCoordinateMap =\n        domain::CoordinateMap<Frame::Grid, Frame::Inertial, Translation>;\n    using GridToDistortedCoordinateMap = TranslationGridDistorted;\n    using DistortedToInertialCoordinateMap = TranslationDistortedInertial;\n\n    PUPable_reg(SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                         CoordinateMaps::Identity<1>>));\n    PUPable_reg(GridToInertialCoordinateMap);\n\n    PUPable_reg(LogicalToGridCoordinateMap);\n    PUPable_reg(SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                         CoordinateMaps::Identity<1>>));\n    PUPable_reg(GridToDistortedCoordinateMap);\n    PUPable_reg(DistortedToInertialCoordinateMap);\n\n    PUPable_reg(SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                         CoordinateMaps::Affine>));\n    PUPable_reg(SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                         CoordinateMaps::Affine>));\n    PUPable_reg(\n        SINGLE_ARG(CoordinateMap<Frame::Grid, Frame::Inertial, Translation>));\n\n    // Test construction of two intervals which have anti-aligned logical axes.\n    Domain<1> domain_from_corners(\n        make_vector<std::unique_ptr<\n            CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 1>>>(\n            std::make_unique<CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                           CoordinateMaps::Affine>>(\n                make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n                    CoordinateMaps::Affine{-1., 1., -2., 0.})),\n            std::make_unique<CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                           CoordinateMaps::Affine>>(\n                make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n                    CoordinateMaps::Affine{-1., 1., 0., 2.}))),\n        std::vector<std::array<size_t, 2>>{{{1, 2}}, {{3, 2}}}, {}, {},\n        {\"Left\", \"Right\"}, {{\"All\", {\"Left\", \"Right\"}}});\n    CHECK(domain_from_corners.blocks()[0].name() == \"Left\");\n    CHECK(domain_from_corners.blocks()[1].name() == \"Right\");\n    CHECK(domain_from_corners.block_names() ==\n          std::vector<std::string>{std::string{\"Left\"}, std::string{\"Right\"}});\n    CHECK(domain_from_corners.block_groups().at(\"All\") ==\n          std::unordered_set<std::string>{\"Left\", \"Right\"});\n\n    Domain<1> domain_no_corners(\n        make_vector<std::unique_ptr<\n            CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 1>>>(\n            std::make_unique<CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                           CoordinateMaps::Affine>>(\n                make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n                    CoordinateMaps::Affine{-1., 1., -2., 0.})),\n            std::make_unique<CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                           CoordinateMaps::Affine>>(\n                make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n                    CoordinateMaps::Affine{-1., 1., 2., 0.}))),\n        {{\"ExcisionSphere\",\n          ExcisionSphere<1>{1.0, tnsr::I<double, 1, Frame::Grid>{0.0}, {}}}},\n        {\"Left\", \"Right\"}, {{\"All\", {\"Left\", \"Right\"}}});\n    CHECK_FALSE(domain_no_corners.is_time_dependent());\n    CHECK(domain_no_corners.blocks()[0].name() == \"Left\");\n    CHECK(domain_no_corners.blocks()[1].name() == \"Right\");\n    CHECK(domain_no_corners.block_names() ==\n          std::vector<std::string>{std::string{\"Left\"}, std::string{\"Right\"}});\n    CHECK(domain_no_corners.block_groups().at(\"All\") ==\n          std::unordered_set<std::string>{\"Left\", \"Right\"});\n\n    test_serialization(domain_no_corners);\n\n    const OrientationMap<1> unaligned_orientation{{{Direction<1>::lower_xi()}},\n                                                  {{Direction<1>::upper_xi()}}};\n\n    const std::vector<DirectionMap<1, BlockNeighbors<1>>> expected_neighbors{\n        {{Direction<1>::upper_xi(),\n          BlockNeighbors<1>{1, unaligned_orientation}}},\n        {{Direction<1>::upper_xi(),\n          BlockNeighbors<1>{0, unaligned_orientation}}}};\n\n    const std::vector<std::unordered_set<Direction<1>>> expected_boundaries{\n        {Direction<1>::lower_xi()}, {Direction<1>::lower_xi()}};\n\n    const auto expected_stationary_maps = make_vector(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            CoordinateMaps::Affine{-1., 1., -2., 0.}),\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            CoordinateMaps::Affine{-1., 1., 0., 2.}));\n\n    const auto expected_stationary_maps_no_corners = make_vector(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            CoordinateMaps::Affine{-1., 1., -2., 0.}),\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            CoordinateMaps::Affine{-1., 1., 2., 0.}));\n\n    const GridToDistortedCoordinateMap translation_grid_to_distorted_map =\n        domain::make_coordinate_map<Frame::Grid, Frame::Distorted>(\n            Translation{\"TranslationGridToDistorted\"});\n    const DistortedToInertialCoordinateMap\n        translation_distorted_to_inertial_map =\n            domain::make_coordinate_map<Frame::Distorted, Frame::Inertial>(\n                Translation{\"TranslationDistortedToInertial\"});\n\n    test_domain_construction(domain_from_corners, expected_neighbors,\n                             expected_boundaries, expected_stationary_maps);\n\n    test_domain_construction(serialize_and_deserialize(domain_from_corners),\n                             expected_neighbors, expected_boundaries,\n                             expected_stationary_maps);\n\n    test_domain_construction(domain_no_corners, expected_neighbors,\n                             expected_boundaries,\n                             expected_stationary_maps_no_corners);\n\n    test_domain_construction(serialize_and_deserialize(domain_no_corners),\n                             expected_neighbors, expected_boundaries,\n                             expected_stationary_maps_no_corners);\n\n    // Test injection of a translation map.\n    REQUIRE(domain_from_corners.blocks().size() == 2);\n    REQUIRE(domain_no_corners.blocks().size() == 2);\n    domain_from_corners.inject_time_dependent_map_for_block(\n        0,\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation{\"Translation0\"}),\n        translation_grid_to_distorted_map.get_clone(),\n        translation_distorted_to_inertial_map.get_clone());\n    domain_from_corners.inject_time_dependent_map_for_block(\n        1,\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation{\"Translation1\"}),\n        translation_grid_to_distorted_map.get_clone(),\n        translation_distorted_to_inertial_map.get_clone());\n\n    domain_no_corners.inject_time_dependent_map_for_block(\n        0,\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation{\"Translation0\"}),\n        translation_grid_to_distorted_map.get_clone(),\n        translation_distorted_to_inertial_map.get_clone());\n    domain_no_corners.inject_time_dependent_map_for_block(\n        1,\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation{\"Translation1\"}),\n        translation_grid_to_distorted_map.get_clone(),\n        translation_distorted_to_inertial_map.get_clone());\n    domain_no_corners.inject_time_dependent_map_for_excision_sphere(\n        \"ExcisionSphere\",\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation{\"Translation0\"}));\n#ifdef SPECTRE_DEBUG\n    CHECK_THROWS_WITH(\n        domain_no_corners.inject_time_dependent_map_for_excision_sphere(\n            \"NonExistentExcisionSphere\",\n            make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n                Translation{\"Translation0\"})),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot inject time dependent maps into excision \"\n            \"sphere 'NonExistentExcisionSphere'\"));\n#endif\n    CHECK(domain_no_corners.is_time_dependent());\n\n    // Excision spheres\n    const auto& excision_spheres_corners =\n        domain_from_corners.excision_spheres();\n    CHECK(excision_spheres_corners.empty());\n    const auto& excision_spheres_no_corners =\n        domain_no_corners.excision_spheres();\n    CHECK(excision_spheres_no_corners.size() == 1);\n    CHECK(excision_spheres_no_corners.count(\"ExcisionSphere\") == 1);\n    CHECK(excision_spheres_no_corners.at(\"ExcisionSphere\").is_time_dependent());\n\n    const auto expected_logical_to_grid_maps =\n        make_vector(make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                        CoordinateMaps::Affine{-1., 1., -2., 0.}),\n                    make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                        CoordinateMaps::Affine{-1., 1., 0., 2.}));\n    const auto expected_logical_to_grid_maps_no_corners =\n        make_vector(make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                        CoordinateMaps::Affine{-1., 1., -2., 0.}),\n                    make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                        CoordinateMaps::Affine{-1., 1., 2., 0.}));\n    const auto expected_grid_to_inertial_maps =\n        make_vector_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            Translation{\"Translation0\"}, Translation{\"Translation1\"});\n\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time{};\n    functions_of_time[\"Translation0\"] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n            1.0, std::array<DataVector, 3>{{{0.0}, {2.3}, {0.0}}}, 10.0);\n    functions_of_time[\"Translation1\"] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n            1.0, std::array<DataVector, 3>{{{0.0}, {5.3}, {0.0}}}, 10.0);\n\n    test_domain_construction(domain_from_corners, expected_neighbors,\n                             expected_boundaries, expected_logical_to_grid_maps,\n                             10.0, functions_of_time,\n                             expected_grid_to_inertial_maps);\n    test_domain_construction(serialize_and_deserialize(domain_from_corners),\n                             expected_neighbors, expected_boundaries,\n                             expected_logical_to_grid_maps, 10.0,\n                             functions_of_time, expected_grid_to_inertial_maps);\n\n    test_domain_construction(domain_no_corners, expected_neighbors,\n                             expected_boundaries,\n                             expected_logical_to_grid_maps_no_corners, 10.0,\n                             functions_of_time, expected_grid_to_inertial_maps);\n    test_domain_construction(serialize_and_deserialize(domain_no_corners),\n                             expected_neighbors, expected_boundaries,\n                             expected_logical_to_grid_maps_no_corners, 10.0,\n                             functions_of_time, expected_grid_to_inertial_maps);\n\n    // Test construction from a vector of blocks\n    auto vector_of_blocks = [&expected_neighbors]() {\n      std::vector<Block<1>> vec;\n      vec.emplace_back(Block<1>{\n          std::make_unique<CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                         CoordinateMaps::Affine>>(\n              make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n                  CoordinateMaps::Affine{-1., 1., -2., 0.})),\n          0, expected_neighbors[0]});\n      vec.emplace_back(Block<1>{\n          std::make_unique<CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                         CoordinateMaps::Affine>>(\n              make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n                  CoordinateMaps::Affine{-1., 1., 0., 2.})),\n          1, expected_neighbors[1]});\n      return vec;\n    }();\n\n    test_domain_construction(Domain<1>{std::move(vector_of_blocks)},\n                             expected_neighbors, expected_boundaries,\n                             expected_stationary_maps);\n\n    CHECK(get_output(domain_from_corners) ==\n          \"Domain with 2 blocks:\\n\" +\n              get_output(domain_from_corners.blocks()[0]) + \"\\n\" +\n              get_output(domain_from_corners.blocks()[1]) + \"\\n\" +\n              \"Excision spheres:\\n\" +\n              get_output(domain_from_corners.excision_spheres()) + \"\\n\");\n\n    CHECK(get_output(domain_no_corners) ==\n          \"Domain with 2 blocks:\\n\" +\n              get_output(domain_no_corners.blocks()[0]) + \"\\n\" +\n              get_output(domain_no_corners.blocks()[1]) + \"\\n\" +\n              \"Excision spheres:\\n\" +\n              get_output(domain_no_corners.excision_spheres()) + \"\\n\");\n  }\n\n  {\n    // Test construction of a periodic domain\n    const auto expected_maps = make_vector(\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            CoordinateMaps::Affine{-1., 1., -2., 2.}));\n\n    const Domain<1> domain{\n        make_vector<std::unique_ptr<\n            CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 1>>>(\n            std::make_unique<CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                           CoordinateMaps::Affine>>(\n                make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n                    CoordinateMaps::Affine{-1., 1., -2., 2.}))),\n        std::vector<std::array<size_t, 2>>{{{1, 2}}},\n        std::vector<PairOfFaces>{{{1}, {2}}}};\n\n    test_serialization(domain);\n\n    const auto expected_neighbors = []() {\n      OrientationMap<1> orientation{{{Direction<1>::lower_xi()}},\n                                    {{Direction<1>::lower_xi()}}};\n      return std::vector<DirectionMap<1, BlockNeighbors<1>>>{\n          {{Direction<1>::lower_xi(), BlockNeighbors<1>{0, orientation}},\n           {Direction<1>::upper_xi(), BlockNeighbors<1>{0, orientation}}}};\n    }();\n\n    test_domain_construction(domain, expected_neighbors,\n                             std::vector<std::unordered_set<Direction<1>>>{1},\n                             expected_maps);\n  }\n}\n\nvoid test_1d_rectilinear_domains() {\n  {\n    INFO(\"Aligned domain.\");\n    const std::vector<std::unordered_set<Direction<1>>>\n        expected_external_boundaries{\n            {{Direction<1>::lower_xi()}}, {}, {{Direction<1>::upper_xi()}}};\n    const auto domain = rectilinear_domain<1>(\n        Index<1>{3}, std::array<std::vector<double>, 1>{{{0.0, 1.0, 2.0, 3.0}}},\n        {}, {}, {{false}}, {}, true);\n    const OrientationMap<1> aligned = OrientationMap<1>::create_aligned();\n    std::vector<DirectionMap<1, BlockNeighbors<1>>> expected_block_neighbors{\n        {{Direction<1>::upper_xi(), {1, aligned}}},\n        {{Direction<1>::lower_xi(), {0, aligned}},\n         {Direction<1>::upper_xi(), {2, aligned}}},\n        {{Direction<1>::lower_xi(), {1, aligned}}}};\n    for (size_t i = 0; i < domain.blocks().size(); i++) {\n      CAPTURE(i);\n      CHECK(domain.blocks()[i].external_boundaries() ==\n            expected_external_boundaries[i]);\n      CHECK(domain.blocks()[i].neighbors() == expected_block_neighbors[i]);\n    }\n  }\n  {\n    INFO(\"Antialigned domain.\");\n    const OrientationMap<1> aligned = OrientationMap<1>::create_aligned();\n    const OrientationMap<1> antialigned{\n        std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}}};\n    const std::vector<std::unordered_set<Direction<1>>>\n        expected_external_boundaries{\n            {{Direction<1>::lower_xi()}}, {}, {{Direction<1>::upper_xi()}}};\n\n    const auto domain = rectilinear_domain<1>(\n        Index<1>{3}, std::array<std::vector<double>, 1>{{{0.0, 1.0, 2.0, 3.0}}},\n        {}, std::vector<OrientationMap<1>>{aligned, antialigned, aligned},\n        {{false}}, {}, true);\n    std::vector<DirectionMap<1, BlockNeighbors<1>>> expected_block_neighbors{\n        {{Direction<1>::upper_xi(), {1, antialigned}}},\n        {{Direction<1>::lower_xi(), {2, antialigned}},\n         {Direction<1>::upper_xi(), {0, antialigned}}},\n        {{Direction<1>::lower_xi(), {1, antialigned}}}};\n    for (size_t i = 0; i < domain.blocks().size(); i++) {\n      INFO(i);\n      CHECK(domain.blocks()[i].external_boundaries() ==\n            expected_external_boundaries[i]);\n      CHECK(domain.blocks()[i].neighbors() == expected_block_neighbors[i]);\n    }\n  }\n}\n\nvoid test_2d_rectilinear_domains() {\n  const OrientationMap<2> half_turn{std::array<Direction<2>, 2>{\n      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}};\n  const OrientationMap<2> quarter_turn_cw{std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}};\n  const OrientationMap<2> quarter_turn_ccw{std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}};\n  auto orientations_of_all_blocks =\n      std::vector<OrientationMap<2>>{4, OrientationMap<2>::create_aligned()};\n  orientations_of_all_blocks[1] = half_turn;\n  orientations_of_all_blocks[2] = quarter_turn_cw;\n  orientations_of_all_blocks[3] = quarter_turn_ccw;\n\n  const std::vector<std::unordered_set<Direction<2>>>\n      expected_external_boundaries{\n          {{Direction<2>::lower_xi(), Direction<2>::lower_eta()}},\n          {{Direction<2>::upper_eta(), Direction<2>::lower_xi()}},\n          {{Direction<2>::lower_xi(), Direction<2>::lower_eta()}},\n          {{Direction<2>::upper_xi(), Direction<2>::lower_eta()}}};\n\n  const auto domain = rectilinear_domain<2>(\n      Index<2>{2, 2},\n      std::array<std::vector<double>, 2>{{{0.0, 1.0, 2.0}, {0.0, 1.0, 2.0}}},\n      {}, orientations_of_all_blocks);\n  std::vector<DirectionMap<2, BlockNeighbors<2>>> expected_block_neighbors{\n      {{Direction<2>::upper_xi(), {1, half_turn}},\n       {Direction<2>::upper_eta(), {2, quarter_turn_cw}}},\n      {{Direction<2>::upper_xi(), {0, half_turn}},\n       {Direction<2>::lower_eta(), {3, quarter_turn_cw}}},\n      {{Direction<2>::upper_xi(), {0, quarter_turn_ccw}},\n       {Direction<2>::upper_eta(), {3, half_turn}}},\n      {{Direction<2>::lower_xi(), {1, quarter_turn_ccw}},\n       {Direction<2>::upper_eta(), {2, half_turn}}}};\n  for (size_t i = 0; i < domain.blocks().size(); i++) {\n    INFO(i);\n    CHECK(domain.blocks()[i].external_boundaries() ==\n          expected_external_boundaries[i]);\n    CHECK(domain.blocks()[i].neighbors() == expected_block_neighbors[i]);\n  }\n}\n\nvoid test_3d_rectilinear_domains() {\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n  const OrientationMap<3> quarter_turn_cw_xi{std::array<Direction<3>, 3>{\n      {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n       Direction<3>::lower_eta()}}};\n  auto orientations_of_all_blocks =\n      std::vector<OrientationMap<3>>{aligned, quarter_turn_cw_xi};\n\n  const std::vector<std::unordered_set<Direction<3>>>\n      expected_external_boundaries{\n          {{Direction<3>::lower_xi(), Direction<3>::lower_eta(),\n            Direction<3>::upper_eta(), Direction<3>::lower_zeta(),\n            Direction<3>::upper_zeta()}},\n          {{Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n            Direction<3>::upper_eta(), Direction<3>::lower_zeta(),\n            Direction<3>::upper_zeta()}}};\n\n  const auto domain =\n      rectilinear_domain<3>(Index<3>{2, 1, 1},\n                            std::array<std::vector<double>, 3>{\n                                {{0.0, 1.0, 2.0}, {0.0, 1.0}, {0.0, 1.0}}},\n                            {}, orientations_of_all_blocks);\n  std::vector<DirectionMap<3, BlockNeighbors<3>>> expected_block_neighbors{\n      {{Direction<3>::upper_xi(), {1, quarter_turn_cw_xi}}},\n      {{Direction<3>::lower_xi(), {0, quarter_turn_cw_xi.inverse_map()}}}};\n  for (size_t i = 0; i < domain.blocks().size(); i++) {\n    INFO(i);\n    CHECK(domain.blocks()[i].external_boundaries() ==\n          expected_external_boundaries[i]);\n    CHECK(domain.blocks()[i].neighbors() == expected_block_neighbors[i]);\n  }\n}\n\n// The version testing requires an old version of the TimeDependentMapOptions\n// to properly test if we can deserialize a domain from an H5 file.\ntemplate <bool IsCylindrical, domain::ObjectLabel Object>\nstruct ShapeMapOptions {\n  using type = Options::Auto<ShapeMapOptions, Options::AutoLabel::None>;\n  static std::string name() { return \"ShapeMap\" + get_output(Object); }\n  static constexpr Options::String help = {\n      \"Options for a time-dependent distortion (shape) map about the \"\n      \"specified object. Specify 'None' to not use this map.\"};\n\n  struct LMax {\n    using type = size_t;\n    static constexpr Options::String help = {\n        \"LMax used for the number of spherical harmonic coefficients of the \"\n        \"distortion map. Currently, all coefficients are initialized to \"\n        \"zero.\"};\n  };\n\n  struct SizeInitialValues {\n    using type = std::array<double, 3>;\n    static constexpr Options::String help = {\n        \"Initial value and two derivatives of the size map.\"};\n  };\n\n  struct TransitionEndsAtCube {\n    using type = bool;\n    static constexpr Options::String help = {\n        \"If 'true', the shape map transition function will be 0 at the cubical \"\n        \"boundary around the object. If 'false' the transition function will \"\n        \"be 0 at the outer radius of the inner sphere around the object\"};\n  };\n\n  using common_options = tmpl::list<LMax, SizeInitialValues>;\n\n  using options = tmpl::conditional_t<\n      IsCylindrical, common_options,\n      tmpl::push_back<common_options, TransitionEndsAtCube>>;\n\n  size_t l_max{};\n  std::array<double, 3> initial_size_values{};\n  bool transition_ends_at_cube{false};\n};\n\nnamespace detail {\n// Convenience type alias\ntemplate <typename... Maps>\nusing gi_map = domain::CoordinateMap<Frame::Grid, Frame::Inertial, Maps...>;\ntemplate <typename... Maps>\nusing gd_map = domain::CoordinateMap<Frame::Grid, Frame::Distorted, Maps...>;\ntemplate <typename... Maps>\nusing di_map =\n    domain::CoordinateMap<Frame::Distorted, Frame::Inertial, Maps...>;\n\ntemplate <typename List>\nstruct power_set {\n  using rest = typename power_set<tmpl::pop_front<List>>::type;\n  using type = tmpl::append<\n      rest, tmpl::transform<rest, tmpl::lazy::push_front<\n                                      tmpl::_1, tmpl::pin<tmpl::front<List>>>>>;\n};\n\ntemplate <>\nstruct power_set<tmpl::list<>> {\n  using type = tmpl::list<tmpl::list<>>;\n};\n\ntemplate <typename SourceFrame, typename TargetFrame, typename Maps>\nusing produce_all_maps_helper =\n    tmpl::wrap<tmpl::push_front<Maps, SourceFrame, TargetFrame>,\n               domain::CoordinateMap>;\ntemplate <typename SourceFrame, typename TargetFrame, typename... Maps>\nusing produce_all_maps = tmpl::transform<\n    tmpl::remove<typename power_set<tmpl::list<Maps...>>::type, tmpl::list<>>,\n    tmpl::bind<produce_all_maps_helper, tmpl::pin<SourceFrame>,\n               tmpl::pin<TargetFrame>, tmpl::_1>>;\n}  // namespace detail\n\ntemplate <bool IsCylindrical>\nstruct TestTimeDependentMapOptions {\n private:\n  template <typename SourceFrame, typename TargetFrame>\n  using MapType =\n      std::unique_ptr<CoordinateMapBase<SourceFrame, TargetFrame, 3>>;\n  // Time-dependent maps\n  using Expansion = CoordinateMaps::TimeDependent::CubicScale<3>;\n  using Rotation = CoordinateMaps::TimeDependent::Rotation<3>;\n  using Shape = CoordinateMaps::TimeDependent::Shape;\n  using Identity = CoordinateMaps::Identity<3>;\n\n  // Maps\n  std::optional<Expansion> expansion_map_{};\n  std::optional<Rotation> rotation_map_{};\n  using ShapeMapType =\n      tmpl::conditional_t<IsCylindrical, std::array<std::optional<Shape>, 2>,\n                          std::array<std::array<std::optional<Shape>, 6>, 2>>;\n  ShapeMapType shape_maps_{};\n\n public:\n  using maps_list = tmpl::append<\n      // We need this odd-one-out Identity map because all maps are optional to\n      // specify. It's possible to specify a shape map, but no expansion or\n      // rotation map. So we have a grid to distorted map, and need an identity\n      // distorted to inertial map. We don't need an identity grid to distorted\n      // map because if a user requests the grid to distorted frame map with a\n      // distorted frame, but didn't specify shape map options, an error occurs.\n      tmpl::list<detail::di_map<Identity>>,\n      detail::produce_all_maps<Frame::Grid, Frame::Inertial, Shape, Expansion,\n                               Rotation>,\n      detail::produce_all_maps<Frame::Grid, Frame::Distorted, Shape>,\n      detail::produce_all_maps<Frame::Distorted, Frame::Inertial, Expansion,\n                               Rotation>>;\n\n  struct InitialTime {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The initial time of the functions of time\"};\n  };\n\n  struct ExpansionMapOptions {\n    using type = Options::Auto<ExpansionMapOptions, Options::AutoLabel::None>;\n    static std::string name() { return \"ExpansionMap\"; }\n    static constexpr Options::String help = {\n        \"Options for the expansion map. Specify 'None' to not use this map.\"};\n    struct InitialValues {\n      using type = std::array<double, 2>;\n      static constexpr Options::String help = {\n          \"Initial value and deriv of expansion.\"};\n    };\n    struct AsymptoticVelocityOuterBoundary {\n      using type = double;\n      static constexpr Options::String help = {\n          \"The asymptotic velocity of the outer boundary.\"};\n    };\n    struct DecayTimescaleOuterBoundaryVelocity {\n      using type = double;\n      static constexpr Options::String help = {\n          \"The timescale for how fast the outer boundary velocity approaches \"\n          \"its asymptotic value.\"};\n    };\n    using options = tmpl::list<InitialValues, AsymptoticVelocityOuterBoundary,\n                               DecayTimescaleOuterBoundaryVelocity>;\n\n    std::array<double, 2> initial_values{\n        std::numeric_limits<double>::signaling_NaN(),\n        std::numeric_limits<double>::signaling_NaN()};\n    double outer_boundary_velocity{\n        std::numeric_limits<double>::signaling_NaN()};\n    double outer_boundary_decay_time{\n        std::numeric_limits<double>::signaling_NaN()};\n  };\n\n  struct RotationMapOptions {\n    using type = Options::Auto<RotationMapOptions, Options::AutoLabel::None>;\n    static std::string name() { return \"RotationMap\"; }\n    static constexpr Options::String help = {\n        \"Options for a time-dependent rotation map about an arbitrary axis. \"\n        \"Specify 'None' to not use this map.\"};\n\n    struct InitialAngularVelocity {\n      using type = std::array<double, 3>;\n      static constexpr Options::String help = {\"The initial angular velocity.\"};\n    };\n\n    using options = tmpl::list<InitialAngularVelocity>;\n\n    std::array<double, 3> initial_angular_velocity{};\n  };\n\n  template <ObjectLabel Object>\n  using ShapeMapOptions = ShapeMapOptions<IsCylindrical, Object>;\n\n  using options =\n      tmpl::list<InitialTime, ExpansionMapOptions, RotationMapOptions,\n                 ShapeMapOptions<ObjectLabel::A>,\n                 ShapeMapOptions<ObjectLabel::B>>;\n  static constexpr Options::String help{\n      \"The options for all time dependent maps in a binary compact object \"\n      \"domain. Specify 'None' to not use any time dependent maps.\"};\n\n  // Names are public because they need to be used when constructing maps in the\n  // BCO domain creators themselves\n  inline static const std::string expansion_name{\"Expansion\"};\n  inline static const std::string expansion_outer_boundary_name{\n      \"ExpansionOuterBoundary\"};\n  inline static const std::string rotation_name{\"Rotation\"};\n  inline static const std::array<std::string, 2> size_names{{\"SizeA\", \"SizeB\"}};\n  inline static const std::array<std::string, 2> shape_names{\n      {\"ShapeA\", \"ShapeB\"}};\n\n private:\n  static size_t get_index(const ObjectLabel object) {\n    ASSERT(object == ObjectLabel::A or object == ObjectLabel::B,\n           \"object label for TimeDependentMapOptions must be either A or B, not\"\n               << object);\n    return object == ObjectLabel::A ? 0_st : 1_st;\n  }\n\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  std::optional<ExpansionMapOptions> expansion_map_options_{};\n  std::optional<RotationMapOptions> rotation_options_{};\n  std::optional<ShapeMapOptions<ObjectLabel::A>> shape_options_A_{};\n  std::optional<ShapeMapOptions<ObjectLabel::B>> shape_options_B_{};\n  std::array<std::optional<double>, 2> inner_radii_{};\n\n public:\n  TestTimeDependentMapOptions() = default;\n\n  TestTimeDependentMapOptions(\n      double initial_time,\n      std::optional<ExpansionMapOptions> expansion_map_options,\n      std::optional<RotationMapOptions> rotation_options,\n      std::optional<ShapeMapOptions<ObjectLabel::A>> shape_options_A,\n      std::optional<ShapeMapOptions<ObjectLabel::B>> shape_options_B,\n      const Options::Context& context = {})\n      : initial_time_(initial_time),\n        expansion_map_options_(expansion_map_options),\n        rotation_options_(rotation_options),\n        shape_options_A_(shape_options_A),\n        shape_options_B_(shape_options_B) {\n    if (not(expansion_map_options_.has_value() or\n            rotation_options_.has_value() or shape_options_A_.has_value() or\n            shape_options_B_.has_value())) {\n      PARSE_ERROR(context,\n                  \"Time dependent map options were specified, but all options \"\n                  \"were 'None'. If you don't want time dependent maps, specify \"\n                  \"'None' for the TimeDependentMapOptions. If you want time \"\n                  \"dependent maps, specify options for at least one map.\");\n    }\n\n    const auto check_l_max = [&context](const auto& shape_option,\n                                        const ObjectLabel label) {\n      if (shape_option.has_value() and shape_option.value().l_max <= 1) {\n        PARSE_ERROR(context, \"Initial LMax for object \"\n                                 << label << \" must be 2 or greater but is \"\n                                 << shape_option.value().l_max << \" instead.\");\n      }\n    };\n\n    check_l_max(shape_options_A_, ObjectLabel::A);\n    check_l_max(shape_options_B_, ObjectLabel::B);\n  }\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<FunctionsOfTime::FunctionOfTime>>\n  create_functions_of_time(const std::unordered_map<std::string, double>&\n                               initial_expiration_times) const {\n    std::unordered_map<std::string,\n                       std::unique_ptr<FunctionsOfTime::FunctionOfTime>>\n        result{};\n\n    // Get existing function of time names that are used for the maps and assign\n    // their initial expiration time to infinity (i.e. not expiring)\n    std::unordered_map<std::string, double> expiration_times{\n        {expansion_name, std::numeric_limits<double>::infinity()},\n        {rotation_name, std::numeric_limits<double>::infinity()},\n        {gsl::at(size_names, 0), std::numeric_limits<double>::infinity()},\n        {gsl::at(size_names, 1), std::numeric_limits<double>::infinity()},\n        {gsl::at(shape_names, 0), std::numeric_limits<double>::infinity()},\n        {gsl::at(shape_names, 1), std::numeric_limits<double>::infinity()}};\n\n    // If we have control systems, overwrite these expiration times with the\n    // ones supplied by the control system\n    for (const auto& [name, expr_time] : initial_expiration_times) {\n      expiration_times[name] = expr_time;\n    }\n\n    // ExpansionMap FunctionOfTime for the function \\f$a(t)\\f$ in the\n    // domain::CoordinateMaps::TimeDependent::CubicScale map\n    if (expansion_map_options_.has_value()) {\n      result[expansion_name] =\n          std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n              initial_time_,\n              std::array<DataVector, 3>{\n                  {{gsl::at(expansion_map_options_.value().initial_values, 0)},\n                   {gsl::at(expansion_map_options_.value().initial_values, 1)},\n                   {0.0}}},\n              expiration_times.at(expansion_name));\n\n      // ExpansionMap FunctionOfTime for the function \\f$b(t)\\f$ in the\n      // domain::CoordinateMaps::TimeDependent::CubicScale map\n      result[expansion_outer_boundary_name] =\n          std::make_unique<FunctionsOfTime::FixedSpeedCubic>(\n              1.0, initial_time_,\n              expansion_map_options_.value().outer_boundary_velocity,\n              expansion_map_options_.value().outer_boundary_decay_time);\n    }\n\n    // RotationMap FunctionOfTime for the rotation angles about each\n    // axis.  The initial rotation angles don't matter as we never\n    // actually use the angles themselves. We only use their derivatives\n    // (omega) to determine map parameters. In theory we could determine\n    // each initial angle from the input axis-angle representation, but\n    // we don't need to.\n    if (rotation_options_.has_value()) {\n      result[rotation_name] = std::make_unique<\n          FunctionsOfTime::QuaternionFunctionOfTime<3>>(\n          initial_time_,\n          std::array<DataVector, 1>{DataVector{1.0, 0.0, 0.0, 0.0}},\n          std::array<DataVector, 4>{\n              {{3, 0.0},\n               {gsl::at(rotation_options_.value().initial_angular_velocity, 0),\n                gsl::at(rotation_options_.value().initial_angular_velocity, 1),\n                gsl::at(rotation_options_.value().initial_angular_velocity, 2)},\n               {3, 0.0},\n               {3, 0.0}}},\n          expiration_times.at(rotation_name));\n    }\n\n    for (size_t i = 0; i < shape_names.size(); i++) {\n      if (i == 0 ? shape_options_A_.has_value()\n                 : shape_options_B_.has_value()) {\n        const auto make_initial_size_values = [](const auto& lambda_options) {\n          return std::array<DataVector, 4>{\n              {{gsl::at(lambda_options.value().initial_size_values, 0)},\n               {gsl::at(lambda_options.value().initial_size_values, 1)},\n               {gsl::at(lambda_options.value().initial_size_values, 2)},\n               {0.0}}};\n        };\n\n        const size_t initial_l_max = i == 0 ? shape_options_A_.value().l_max\n                                            : shape_options_B_.value().l_max;\n        const std::array<DataVector, 4> initial_size_values =\n            i == 0 ? make_initial_size_values(shape_options_A_)\n                   : make_initial_size_values(shape_options_B_);\n        const DataVector shape_zeros{\n            ylm::Spherepack::spectral_size(initial_l_max, initial_l_max), 0.0};\n\n        result[gsl::at(shape_names, i)] =\n            std::make_unique<FunctionsOfTime::PiecewisePolynomial<2>>(\n                initial_time_,\n                std::array<DataVector, 3>{shape_zeros, shape_zeros,\n                                          shape_zeros},\n                expiration_times.at(gsl::at(shape_names, i)));\n        result[gsl::at(size_names, i)] =\n            std::make_unique<FunctionsOfTime::PiecewisePolynomial<3>>(\n                initial_time_, initial_size_values,\n                expiration_times.at(gsl::at(size_names, i)));\n      }\n\n      return result;\n    }\n  }\n\n  void build_maps(\n      const std::array<std::array<double, 3>, 2>& centers,\n      const std::optional<std::array<double, IsCylindrical ? 2 : 3>>&\n          object_A_radii,\n      const std::optional<std::array<double, IsCylindrical ? 2 : 3>>&\n          object_B_radii,\n      double domain_outer_radius) {\n    if (expansion_map_options_.has_value()) {\n      expansion_map_ = Expansion{domain_outer_radius, expansion_name,\n                                 expansion_outer_boundary_name};\n    }\n    if (rotation_options_.has_value()) {\n      rotation_map_ = Rotation{rotation_name};\n    }\n\n    for (size_t i = 0; i < 2; i++) {\n      const auto& radii_opt = i == 0 ? object_A_radii : object_B_radii;\n      if (radii_opt.has_value()) {\n        const auto& radii = radii_opt.value();\n        if (not(i == 0 ? shape_options_A_.has_value()\n                       : shape_options_B_.has_value())) {\n          ERROR_NO_TRACE(\n              \"Trying to build the shape map for object \"\n              << (i == 0 ? ObjectLabel::A : ObjectLabel::B)\n              << \", but no time dependent map options were specified \"\n                 \"for that object.\");\n        }\n        // Store the inner radii for creating functions of time\n        gsl::at(inner_radii_, i) = radii[0];\n\n        const double coefficient_truncation_limit = 0.;\n\n        std::unique_ptr<CoordinateMaps::ShapeMapTransitionFunctions::\n                            ShapeMapTransitionFunction>\n            transition_func{};\n\n        // Currently, we don't support different transition functions for the\n        // cylindrical domain\n        if constexpr (IsCylindrical) {\n          transition_func = std::make_unique<\n              CoordinateMaps::ShapeMapTransitionFunctions::SphereTransition>(\n              radii[0], radii[1]);\n\n          gsl::at(shape_maps_, i) =\n              Shape{gsl::at(centers, i), coefficient_truncation_limit,\n                    std::move(transition_func), gsl::at(shape_names, i),\n                    gsl::at(size_names, i)};\n        } else {\n          // These must match the order of orientations_for_sphere_wrappings()\n          // in DomainHelpers.hpp\n          const std::array<size_t, 6> axes{2, 2, 1, 1, 0, 0};\n\n          const bool transition_ends_at_cube =\n              i == 0 ? shape_options_A_->transition_ends_at_cube\n                     : shape_options_B_->transition_ends_at_cube;\n\n          const double inner_sphericity = 1.0;\n          const double outer_sphericity = transition_ends_at_cube ? 0.0 : 1.0;\n\n          const double inner_radius = radii[0];\n          const double outer_radius =\n              transition_ends_at_cube ? radii[2] : radii[1];\n\n          for (size_t j = 0; j < axes.size(); j++) {\n            transition_func = std::make_unique<\n                CoordinateMaps::ShapeMapTransitionFunctions::Wedge>(\n                inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n                gsl::at(axes, j));\n\n            gsl::at(gsl::at(shape_maps_, i), j) =\n                Shape{gsl::at(centers, i), coefficient_truncation_limit,\n                      std::move(transition_func), gsl::at(shape_names, i),\n                      gsl::at(size_names, i)};\n          }\n        }\n\n      } else if (i == 0 ? shape_options_A_.has_value()\n                        : shape_options_B_.has_value()) {\n        ERROR_NO_TRACE(\n            \"No excision was specified for object \"\n            << (i == 0 ? ObjectLabel::A : ObjectLabel::B)\n            << \", but ShapeMap options were specified for that object.\");\n      }\n    }\n  }\n\n  bool has_distorted_frame_options(ObjectLabel object) const {\n    ASSERT(object == ObjectLabel::A or object == ObjectLabel::B,\n           \"object label for TimeDependentMapOptions must be either A or B, not\"\n               << object);\n    return object == ObjectLabel::A ? shape_options_A_.has_value()\n                                    : shape_options_B_.has_value();\n  }\n\n  using IncludeDistortedMapType =\n      tmpl::conditional_t<IsCylindrical, bool, std::optional<size_t>>;\n\n  template <ObjectLabel Object>\n  MapType<Frame::Distorted, Frame::Inertial> distorted_to_inertial_map(\n      const IncludeDistortedMapType& include_distorted_map) const {\n    bool block_has_shape_map = false;\n\n    if constexpr (IsCylindrical) {\n      block_has_shape_map = include_distorted_map;\n    } else {\n      const bool transition_ends_at_cube =\n          Object == ObjectLabel::A ? shape_options_A_->transition_ends_at_cube\n                                   : shape_options_B_->transition_ends_at_cube;\n      block_has_shape_map =\n          include_distorted_map.has_value() and\n          (transition_ends_at_cube or include_distorted_map.value() < 6);\n    }\n\n    if (block_has_shape_map) {\n      if (expansion_map_.has_value() and rotation_map_.has_value()) {\n        return std::make_unique<detail::di_map<Expansion, Rotation>>(\n            expansion_map_.value(), rotation_map_.value());\n      } else if (expansion_map_.has_value()) {\n        return std::make_unique<detail::di_map<Expansion>>(\n            expansion_map_.value());\n      } else if (rotation_map_.has_value()) {\n        return std::make_unique<detail::di_map<Rotation>>(\n            rotation_map_.value());\n      } else {\n        return std::make_unique<detail::di_map<Identity>>(Identity{});\n      }\n    } else {\n      return nullptr;\n    }\n  }\n\n  template <ObjectLabel Object>\n  MapType<Frame::Grid, Frame::Distorted> grid_to_distorted_map(\n      const IncludeDistortedMapType& include_distorted_map) const {\n    bool block_has_shape_map = false;\n\n    if constexpr (IsCylindrical) {\n      block_has_shape_map = include_distorted_map;\n    } else {\n      const bool transition_ends_at_cube =\n          Object == ObjectLabel::A ? shape_options_A_->transition_ends_at_cube\n                                   : shape_options_B_->transition_ends_at_cube;\n      block_has_shape_map =\n          include_distorted_map.has_value() and\n          (transition_ends_at_cube or include_distorted_map.value() < 6);\n    }\n\n    if (block_has_shape_map) {\n      const size_t index = get_index(Object);\n      const std::optional<Shape>* shape{};\n      if constexpr (IsCylindrical) {\n        shape = &gsl::at(shape_maps_, index);\n      } else {\n        if (include_distorted_map.value() >= 12) {\n          ERROR(\n              \"Invalid 'include_distorted_map' argument. Max value allowed is \"\n              \"11, but it is \"\n              << include_distorted_map.value());\n        }\n        shape = &gsl::at(gsl::at(shape_maps_, index),\n                         include_distorted_map.value() % 6);\n      }\n      if (not shape->has_value()) {\n        ERROR(\n            \"Requesting grid to distorted map with distorted frame but shape \"\n            \"map \"\n            \"options were not specified.\");\n      }\n      return std::make_unique<detail::gd_map<Shape>>(shape->value());\n    } else {\n      return nullptr;\n    }\n  }\n\n  template <ObjectLabel Object>\n  MapType<Frame::Grid, Frame::Inertial> grid_to_inertial_map(\n      const IncludeDistortedMapType& include_distorted_map) const {\n    bool block_has_shape_map = false;\n\n    if constexpr (IsCylindrical) {\n      block_has_shape_map = include_distorted_map;\n    } else {\n      const bool transition_ends_at_cube =\n          Object == ObjectLabel::A ? shape_options_A_->transition_ends_at_cube\n                                   : shape_options_B_->transition_ends_at_cube;\n      block_has_shape_map =\n          include_distorted_map.has_value() and\n          (transition_ends_at_cube or include_distorted_map.value() < 6);\n    }\n\n    if (block_has_shape_map) {\n      const size_t index = get_index(Object);\n      const std::optional<Shape>* shape{};\n      if constexpr (IsCylindrical) {\n        shape = &gsl::at(shape_maps_, index);\n      } else {\n        if (include_distorted_map.value() >= 12) {\n          ERROR(\n              \"Invalid 'include_distorted_map' argument. Max value allowed is \"\n              \"11, but it is \"\n              << include_distorted_map.value());\n        }\n        shape = &gsl::at(gsl::at(shape_maps_, index),\n                         include_distorted_map.value() % 6);\n      }\n      if (not shape->has_value()) {\n        ERROR(\n            \"Requesting grid to inertial map with distorted frame but shape \"\n            \"map \"\n            \"options were not specified.\");\n      }\n      if (expansion_map_.has_value() and rotation_map_.has_value()) {\n        return std::make_unique<detail::gi_map<Shape, Expansion, Rotation>>(\n            shape->value(), expansion_map_.value(), rotation_map_.value());\n      } else if (expansion_map_.has_value()) {\n        return std::make_unique<detail::gi_map<Shape, Expansion>>(\n            shape->value(), expansion_map_.value());\n      } else if (rotation_map_.has_value()) {\n        return std::make_unique<detail::gi_map<Shape, Rotation>>(\n            shape->value(), rotation_map_.value());\n      } else {\n        return std::make_unique<detail::gi_map<Shape>>(shape->value());\n      }\n    } else {\n      if (expansion_map_.has_value() and rotation_map_.has_value()) {\n        return std::make_unique<detail::gi_map<Expansion, Rotation>>(\n            expansion_map_.value(), rotation_map_.value());\n      } else if (expansion_map_.has_value()) {\n        return std::make_unique<detail::gi_map<Expansion>>(\n            expansion_map_.value());\n      } else if (rotation_map_.has_value()) {\n        return std::make_unique<detail::gi_map<Rotation>>(\n            rotation_map_.value());\n      } else {\n        return nullptr;\n      }\n    }\n  }\n};\n\n// We can't call the DomainCreator because they aren't serialized and can change\n// at any time. What we actually want to test is that we can deserialize a\n// domain and have it be exactly what we expect. Not whatever the latest version\n// of the DomainCreator makes. For this, we copy a lot of code from\n// BinaryCompactObject because the binary domain is complicated (which is a good\n// test of versioning because of so many moving parts)\nDomain<3> create_serialized_domain() {\n  std::vector<std::string> block_names{};\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      block_groups{};\n\n  static std::array<std::string, 6> wedge_directions{\n      \"UpperZ\", \"LowerZ\", \"UpperY\", \"LowerY\", \"UpperX\", \"LowerX\"};\n  const auto add_object_region = [&block_names, &block_groups](\n                                     const std::string& object_name,\n                                     const std::string& region_name) {\n    for (const std::string& wedge_direction : wedge_directions) {\n      const std::string block_name =\n          // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n          object_name + region_name + wedge_direction;\n      block_names.push_back(block_name);\n      block_groups[object_name + region_name].insert(block_name);\n    }\n  };\n  const auto add_outer_region =\n      [&block_names, &block_groups](const std::string& region_name) {\n        for (const std::string& wedge_direction : wedge_directions) {\n          for (const std::string& leftright : {\"Left\"s, \"Right\"s}) {\n            if ((wedge_direction == \"UpperX\" and leftright == \"Left\") or\n                (wedge_direction == \"LowerX\" and leftright == \"Right\")) {\n              // The outer regions are divided in half perpendicular to the\n              // x-axis at x=0. Therefore, the left side only has a block in\n              // negative x-direction, and the right side only has one in\n              // positive x-direction.\n              continue;\n            }\n            // NOLINTNEXTLINE(performance-inefficient-string-concatenation)\n            const std::string block_name =\n                region_name + wedge_direction +\n                (wedge_direction == \"UpperX\" or wedge_direction == \"LowerX\"\n                     ? \"\"\n                     : leftright);\n            block_names.push_back(block_name);\n            block_groups[region_name].insert(block_name);\n          }\n        }\n      };\n  add_object_region(\"ObjectA\", \"Shell\");  // 6 blocks\n  add_object_region(\"ObjectA\", \"Cube\");   // 6 blocks\n  add_object_region(\"ObjectB\", \"Shell\");  // 6 blocks\n  add_object_region(\"ObjectB\", \"Cube\");   // 6 blocks\n  add_outer_region(\"Envelope\");           // 10 blocks\n  add_outer_region(\"OuterShell\");         // 10 blocks\n\n  // Expand initial refinement and number of grid points over all blocks\n  const ExpandOverBlocks<std::array<size_t, 3>> expand_over_blocks{\n      block_names, block_groups};\n\n  using BCO = creators::BinaryCompactObject<false>;\n\n  const BCO::InitialRefinement::type initial_refinement_variant{1_st};\n  const BCO::InitialGridPoints::type initial_grid_points_variant{3_st};\n  std::vector<std::array<size_t, 3>> initial_refinement =\n      std::visit(expand_over_blocks, initial_refinement_variant);\n  std::vector<std::array<size_t, 3>> initial_grid_points =\n      std::visit(expand_over_blocks, initial_grid_points_variant);\n\n  const std::vector<CoordinateMaps::Distribution> radial_distribution{\n      CoordinateMaps::Distribution::Logarithmic};\n  const CoordinateMaps::Distribution radial_distribution_envelope =\n      CoordinateMaps::Distribution::Linear;\n  const CoordinateMaps::Distribution radial_distribution_outer_shell =\n      CoordinateMaps::Distribution::Linear;\n\n  using Maps = std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>;\n  using Affine = CoordinateMaps::Affine;\n  using Identity2D = CoordinateMaps::Identity<2>;\n  using Translation = CoordinateMaps::ProductOf2Maps<Affine, Identity2D>;\n\n  const creators::BinaryCompactObject<false>::Object object_A{\n      0.45825, 6., 7.683, true, true};\n  const creators::BinaryCompactObject<false>::Object object_B{\n      0.45825, 6., -7.683, true, true};\n\n  const double x_coord_a = object_A.x_coord;\n  const double x_coord_b = object_B.x_coord;\n  const double envelope_radius = 100.0;\n  const double outer_radius = 300.0;\n  const double inner_sphericity_A = 1.0;\n  const double inner_sphericity_B = 1.0;\n  const bool use_equiangular_map = true;\n  const double translation = 0.5 * (x_coord_a + x_coord_b);\n  const double length_inner_cube = abs(x_coord_a - x_coord_b);\n  const double opening_angle = M_PI / 2.0;\n  const double tan_half_opening_angle = tan(0.5 * opening_angle);\n  const double length_outer_cube =\n      2.0 * envelope_radius / sqrt(2.0 + square(tan_half_opening_angle));\n\n  Maps maps{};\n\n  const Translation translation_A{\n      Affine{-1.0, 1.0, -1.0 + x_coord_a, 1.0 + x_coord_a}, Identity2D{}};\n  const Translation translation_B{\n      Affine{-1.0, 1.0, -1.0 + x_coord_b, 1.0 + x_coord_b}, Identity2D{}};\n\n  Maps maps_center_A =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 3>(\n          sph_wedge_coordinate_maps(object_A.inner_radius,\n                                    object_A.outer_radius, inner_sphericity_A,\n                                    1.0, use_equiangular_map, std::nullopt,\n                                    false, {}, radial_distribution),\n          translation_A);\n  Maps maps_cube_A =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 3>(\n          sph_wedge_coordinate_maps(object_A.outer_radius,\n                                    sqrt(3.0) * 0.5 * length_inner_cube, 1.0,\n                                    0.0, use_equiangular_map),\n          translation_A);\n  std::move(maps_center_A.begin(), maps_center_A.end(),\n            std::back_inserter(maps));\n  std::move(maps_cube_A.begin(), maps_cube_A.end(), std::back_inserter(maps));\n\n  Maps maps_center_B =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 3>(\n          sph_wedge_coordinate_maps(object_B.inner_radius,\n                                    object_B.outer_radius, inner_sphericity_B,\n                                    1.0, use_equiangular_map, std::nullopt,\n                                    false, {}, radial_distribution),\n          translation_B);\n  Maps maps_cube_B =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 3>(\n          sph_wedge_coordinate_maps(object_B.outer_radius,\n                                    sqrt(3.0) * 0.5 * length_inner_cube, 1.0,\n                                    0.0, use_equiangular_map),\n          translation_B);\n  std::move(maps_center_B.begin(), maps_center_B.end(),\n            std::back_inserter(maps));\n  std::move(maps_cube_B.begin(), maps_cube_B.end(), std::back_inserter(maps));\n\n  Maps maps_frustums =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 3>(\n          frustum_coordinate_maps(\n              length_inner_cube, length_outer_cube, use_equiangular_map,\n              use_equiangular_map, {{-translation, 0.0, 0.0}},\n              radial_distribution_envelope,\n              radial_distribution_envelope ==\n                      CoordinateMaps::Distribution::Projective\n                  ? std::optional<double>(length_inner_cube / length_outer_cube)\n                  : std::nullopt,\n              1.0, opening_angle));\n  std::move(maps_frustums.begin(), maps_frustums.end(),\n            std::back_inserter(maps));\n\n  Maps maps_outer_shell =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial, 3>(\n          sph_wedge_coordinate_maps(envelope_radius, outer_radius, 1.0, 1.0,\n                                    use_equiangular_map, std::nullopt, true, {},\n                                    {radial_distribution_outer_shell},\n                                    ShellWedges::All, opening_angle));\n  std::move(maps_outer_shell.begin(), maps_outer_shell.end(),\n            std::back_inserter(maps));\n\n  std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};\n  excision_spheres.emplace(\n      \"ExcisionSphereA\",\n      ExcisionSphere<3>{object_A.inner_radius,\n                        tnsr::I<double, 3, Frame::Grid>{{x_coord_a, 0.0, 0.0}},\n                        {{0, Direction<3>::lower_zeta()},\n                         {1, Direction<3>::lower_zeta()},\n                         {2, Direction<3>::lower_zeta()},\n                         {3, Direction<3>::lower_zeta()},\n                         {4, Direction<3>::lower_zeta()},\n                         {5, Direction<3>::lower_zeta()}}});\n  excision_spheres.emplace(\n      \"ExcisionSphereB\",\n      ExcisionSphere<3>{object_B.inner_radius,\n                        tnsr::I<double, 3, Frame::Grid>{{x_coord_b, 0.0, 0.0}},\n                        {{12, Direction<3>::lower_zeta()},\n                         {13, Direction<3>::lower_zeta()},\n                         {14, Direction<3>::lower_zeta()},\n                         {15, Direction<3>::lower_zeta()},\n                         {16, Direction<3>::lower_zeta()},\n                         {17, Direction<3>::lower_zeta()}}});\n\n  Domain<3> domain{std::move(maps), std::move(excision_spheres), block_names,\n                   block_groups};\n\n  // We have IsCylindrical = true even though this is the rectangular domain\n  // because the cylindrical domain uses the SphereTransition transition\n  // function for the shape map which is what is serialized. The rectangular\n  // domain uses the Wedge transition function.\n  using TimeDepOps = TestTimeDependentMapOptions<true>;\n  TimeDepOps time_dependent_options{\n      0.,\n      TimeDepOps::ExpansionMapOptions{\n          {{1.0, -4.6148457646200002e-05}}, -1.0e-6, 50.},\n      TimeDepOps::RotationMapOptions{{0.0, 0.0, 1.5264577062000000e-02}},\n      TimeDepOps::ShapeMapOptions<ObjectLabel::A>{8, {0., 0., 0.}},\n      TimeDepOps::ShapeMapOptions<ObjectLabel::B>{8, {0., 0., 0.}}};\n\n  const std::optional<std::array<double, 2>> inner_outer_radii_A =\n      std::array{object_A.inner_radius, object_A.outer_radius};\n  const std::optional<std::array<double, 2>> inner_outer_radii_B =\n      std::array{object_B.inner_radius, object_B.outer_radius};\n  const std::array<std::array<double, 3>, 2> centers{\n      std::array{x_coord_a, 0.0, 0.0}, std::array{x_coord_b, 0.0, 0.0}};\n\n  time_dependent_options.build_maps(centers, inner_outer_radii_A,\n                                    inner_outer_radii_B, outer_radius);\n\n  const size_t number_of_blocks = 44;\n\n  std::vector<\n      std::unique_ptr<CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>>\n      grid_to_inertial_block_maps{number_of_blocks};\n  std::vector<\n      std::unique_ptr<CoordinateMapBase<Frame::Grid, Frame::Distorted, 3>>>\n      grid_to_distorted_block_maps{number_of_blocks};\n  std::vector<\n      std::unique_ptr<CoordinateMapBase<Frame::Distorted, Frame::Inertial, 3>>>\n      distorted_to_inertial_block_maps{number_of_blocks};\n\n  grid_to_inertial_block_maps[number_of_blocks - 1] =\n      time_dependent_options.grid_to_inertial_map<ObjectLabel::None>(false);\n\n  grid_to_inertial_block_maps[0] =\n      time_dependent_options.grid_to_inertial_map<ObjectLabel::A>(true);\n  grid_to_distorted_block_maps[0] =\n      time_dependent_options.grid_to_distorted_map<ObjectLabel::A>(true);\n  distorted_to_inertial_block_maps[0] =\n      time_dependent_options.distorted_to_inertial_map<ObjectLabel::A>(true);\n\n  const size_t first_block_object_B = 12;\n  grid_to_inertial_block_maps[first_block_object_B] =\n      time_dependent_options.grid_to_inertial_map<ObjectLabel::B>(true);\n  grid_to_distorted_block_maps[first_block_object_B] =\n      time_dependent_options.grid_to_distorted_map<ObjectLabel::B>(true);\n  distorted_to_inertial_block_maps[first_block_object_B] =\n      time_dependent_options.distorted_to_inertial_map<ObjectLabel::B>(true);\n\n  for (size_t block = 1; block < number_of_blocks - 1; ++block) {\n    if (block < 6) {\n      // We always have a grid to inertial map. We may or may not have maps to\n      // the distorted frame.\n      grid_to_inertial_block_maps[block] =\n          grid_to_inertial_block_maps[0]->get_clone();\n      if (grid_to_distorted_block_maps[0] != nullptr) {\n        grid_to_distorted_block_maps[block] =\n            grid_to_distorted_block_maps[0]->get_clone();\n        distorted_to_inertial_block_maps[block] =\n            distorted_to_inertial_block_maps[0]->get_clone();\n      }\n    } else if (block == first_block_object_B) {\n      continue;  // already initialized\n    } else if (block > first_block_object_B and\n               block < first_block_object_B + 6) {\n      // We always have a grid to inertial map. We may or may not have maps to\n      // the distorted frame.\n      grid_to_inertial_block_maps[block] =\n          grid_to_inertial_block_maps[first_block_object_B]->get_clone();\n      if (grid_to_distorted_block_maps[first_block_object_B] != nullptr) {\n        grid_to_distorted_block_maps[block] =\n            grid_to_distorted_block_maps[first_block_object_B]->get_clone();\n        distorted_to_inertial_block_maps[block] =\n            distorted_to_inertial_block_maps[first_block_object_B]->get_clone();\n      }\n    } else {\n      grid_to_inertial_block_maps[block] =\n          grid_to_inertial_block_maps[number_of_blocks - 1]->get_clone();\n    }\n  }\n\n  for (size_t block = 0; block < number_of_blocks; ++block) {\n    domain.inject_time_dependent_map_for_block(\n        block, std::move(grid_to_inertial_block_maps[block]),\n        std::move(grid_to_distorted_block_maps[block]),\n        std::move(distorted_to_inertial_block_maps[block]));\n  }\n\n  return domain;\n}\n\nvoid test_versioning() {\n  // Check that we can deserialize the domain stored in this old file\n  creators::register_derived_with_charm();\n  FunctionsOfTime::register_derived_with_charm();\n  h5::H5File<h5::AccessType::ReadOnly> h5file{unit_test_src_path() +\n                                              \"/Domain/SerializedDomain.h5\"};\n  const auto& volfile = h5file.get<h5::VolumeData>(\"/element_data\");\n  const size_t obs_id = volfile.list_observation_ids().front();\n  const auto serialized_domain = *volfile.get_domain();\n  const auto domain = deserialize<Domain<3>>(serialized_domain.data());\n  const Domain<3> expected_domain = create_serialized_domain();\n  CHECK(domain == expected_domain);\n  // Also check that we can deserialize the functions of time.\n  const auto serialized_fot = *volfile.get_functions_of_time(obs_id);\n  const auto functions_of_time = deserialize<std::unordered_map<\n      std::string, std::unique_ptr<FunctionsOfTime::FunctionOfTime>>>(\n      serialized_fot.data());\n  CHECK(functions_of_time.count(\"Rotation\") == 1);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Domain\", \"[Domain][Unit]\") {\n  {\n    INFO(\"Equality operator\");\n    Domain<1> lhs{\n        make_vector(\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                CoordinateMaps::Affine{-1., 1., -2., 0.})),\n        {},\n        {\"Block0\"},\n        {{\"All\", {\"Block0\"}}}};\n    {\n      Domain<1> rhs{\n          make_vector(\n              make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                  CoordinateMaps::Affine{-1., 1., -2., 0.})),\n          {},\n          {\"Block1\"},\n          {{\"All\", {\"Block0\"}}}};\n      CHECK_FALSE(lhs == rhs);\n    }\n    {\n      Domain<1> rhs{\n          make_vector(\n              make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                  CoordinateMaps::Affine{-1., 1., -2., 0.})),\n          {},\n          {\"Block0\"},\n          {}};\n      CHECK_FALSE(lhs == rhs);\n    }\n  }\n\n  test_1d_domains();\n  test_1d_rectilinear_domains();\n  test_2d_rectilinear_domains();\n  test_3d_rectilinear_domains();\n  test_versioning();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      Domain<1>(\n          make_vector(\n              make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                  CoordinateMaps::Affine{-1., 1., -1., 1.}),\n              make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                  CoordinateMaps::Affine{-1., 1., -1., 1.})),\n          std::vector<std::array<size_t, 2>>{{{1, 2}}}),\n      Catch::Matchers::ContainsSubstring(\n          \"Must pass same number of maps as block corner sets\"));\n#endif\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_DomainHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/EquatorialCompression.hpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace domain {\nnamespace {\n\nvoid test_periodic_same_block() {\n  const std::vector<std::array<size_t, 8>> corners_of_all_blocks{\n      {{0, 1, 2, 3, 4, 5, 6, 7}}, {{8, 9, 10, 11, 0, 1, 2, 3}}};\n  std::vector<DirectionMap<3, BlockNeighbors<3>>> neighbors_of_all_blocks;\n  set_internal_boundaries<3>(&neighbors_of_all_blocks, corners_of_all_blocks);\n\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n  for (const auto& [_, orientation] :\n       neighbors_of_all_blocks[0][Direction<3>::lower_zeta()].orientations()) {\n    CHECK(orientation == aligned);\n  }\n\n  const PairOfFaces x_faces{{1, 3, 5, 7}, {0, 2, 4, 6}};\n\n  const std::vector<PairOfFaces> identifications{x_faces};\n  set_identified_boundaries<3>(identifications, corners_of_all_blocks,\n                               &neighbors_of_all_blocks);\n  for (const auto& [_, orientation] :\n       neighbors_of_all_blocks[0][Direction<3>::upper_xi()].orientations()) {\n    CHECK(orientation == aligned);\n  }\n\n  const std::vector<DirectionMap<3, BlockNeighbors<3>>>\n      expected_block_neighbors{{{Direction<3>::upper_xi(), {0, aligned}},\n                                {Direction<3>::lower_xi(), {0, aligned}},\n                                {Direction<3>::lower_zeta(), {1, aligned}}},\n                               {{Direction<3>::upper_zeta(), {0, aligned}}}};\n\n  CHECK(neighbors_of_all_blocks == expected_block_neighbors);\n}\n\nvoid test_periodic_different_blocks() {\n  const std::vector<std::array<size_t, 8>> corners_of_all_blocks{\n      {{0, 1, 2, 3, 4, 5, 6, 7}}, {{8, 9, 10, 11, 0, 1, 2, 3}}};\n  std::vector<DirectionMap<3, BlockNeighbors<3>>> neighbors_of_all_blocks;\n  set_internal_boundaries<3>(&neighbors_of_all_blocks, corners_of_all_blocks);\n\n  const OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n  for (const auto& [_, orientation] :\n       neighbors_of_all_blocks[0][Direction<3>::lower_zeta()].orientations()) {\n    CHECK(orientation == aligned);\n  }\n\n  const PairOfFaces x_faces_on_different_blocks{{1, 3, 5, 7}, {8, 10, 0, 2}};\n\n  const std::vector<PairOfFaces> identifications{x_faces_on_different_blocks};\n  set_identified_boundaries<3>(identifications, corners_of_all_blocks,\n                               &neighbors_of_all_blocks);\n  for (const auto& [_, orientation] :\n       neighbors_of_all_blocks[0][Direction<3>::upper_xi()].orientations()) {\n    CHECK(orientation == aligned);\n  }\n  const std::vector<DirectionMap<3, BlockNeighbors<3>>>\n      expected_block_neighbors{{{Direction<3>::upper_xi(), {1, aligned}},\n                                {Direction<3>::lower_zeta(), {1, aligned}}},\n                               {{Direction<3>::lower_xi(), {0, aligned}},\n                                {Direction<3>::upper_zeta(), {0, aligned}}}};\n\n  CHECK(neighbors_of_all_blocks == expected_block_neighbors);\n}\n\nstd::vector<CoordinateMaps::Wedge<3>> test_wedge_map_generation(\n    double inner_radius, double outer_radius, double inner_sphericity,\n    double outer_sphericity, bool use_equiangular_map,\n    std::optional<std::pair<double, std::array<double, 3>>> offset_options,\n    bool use_half_wedges = false) {\n  using Wedge3DMap = CoordinateMaps::Wedge<3>;\n\n  if (use_half_wedges and not offset_options.has_value()) {\n    using Halves = Wedge3DMap::WedgeHalves;\n    return make_vector(\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity, OrientationMap<3>::create_aligned(),\n                   use_equiangular_map, Halves::LowerOnly},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity, OrientationMap<3>::create_aligned(),\n                   use_equiangular_map, Halves::UpperOnly},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n                        Direction<3>::lower_zeta()}}},\n                   use_equiangular_map, Halves::LowerOnly},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n                        Direction<3>::lower_zeta()}}},\n                   use_equiangular_map, Halves::UpperOnly},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n                        Direction<3>::lower_eta()}}},\n                   use_equiangular_map, Halves::LowerOnly},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n                        Direction<3>::lower_eta()}}},\n                   use_equiangular_map, Halves::UpperOnly},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map, Halves::LowerOnly},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map, Halves::UpperOnly},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_zeta(), Direction<3>::upper_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::lower_zeta(), Direction<3>::lower_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map});\n  } else if (not use_half_wedges and not offset_options.has_value()) {\n    return make_vector(\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity, OrientationMap<3>::create_aligned(),\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n                        Direction<3>::lower_zeta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n                        Direction<3>::lower_eta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_zeta(), Direction<3>::upper_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, outer_radius, inner_sphericity,\n                   outer_sphericity,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::lower_zeta(), Direction<3>::lower_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map});\n  } else if (use_half_wedges and offset_options.has_value()) {\n    using Halves = Wedge3DMap::WedgeHalves;\n    return make_vector(\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>::create_aligned(), use_equiangular_map,\n                   Halves::LowerOnly},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>::create_aligned(), use_equiangular_map,\n                   Halves::UpperOnly},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n                        Direction<3>::lower_zeta()}}},\n                   use_equiangular_map, Halves::LowerOnly},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n                        Direction<3>::lower_zeta()}}},\n                   use_equiangular_map, Halves::UpperOnly},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n                        Direction<3>::lower_eta()}}},\n                   use_equiangular_map, Halves::LowerOnly},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n                        Direction<3>::lower_eta()}}},\n                   use_equiangular_map, Halves::UpperOnly},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map, Halves::LowerOnly},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map, Halves::UpperOnly},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_zeta(), Direction<3>::upper_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::lower_zeta(), Direction<3>::lower_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map});\n  } else {\n    return make_vector(\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>::create_aligned(), use_equiangular_map},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n                        Direction<3>::lower_zeta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n                        Direction<3>::lower_eta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_zeta(), Direction<3>::upper_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map},\n        Wedge3DMap{inner_radius, std::nullopt, offset_options.value().first,\n                   offset_options.value().second,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::lower_zeta(), Direction<3>::lower_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map});\n  }\n}\n\nvoid test_wedge_map_generation_against_domain_helpers(\n    double inner_radius, double outer_radius, double inner_sphericity,\n    double outer_sphericity, bool use_equiangular_map,\n    std::optional<std::pair<double, std::array<double, 3>>> offset_options,\n    bool use_half_wedges = false) {\n  const auto expected_coord_maps = test_wedge_map_generation(\n      inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n      use_equiangular_map, offset_options, use_half_wedges);\n  const auto maps = sph_wedge_coordinate_maps(\n      inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n      use_equiangular_map, offset_options, use_half_wedges);\n  CHECK(maps == expected_coord_maps);\n}\n\nvoid test_wedge_errors() {\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double inner_radius = 0.5;\n        const double outer_radius = 2.0;\n        const double inner_sphericity = 1.0;\n        const double outer_sphericity = 1.0;\n        const bool use_equiangular_map = true;\n        const bool use_half_wedges = true;\n        const std::vector<domain::CoordinateMaps::Distribution>\n            radial_distribution{\n                domain::CoordinateMaps::Distribution::Logarithmic};\n        const ShellWedges which_wedges = ShellWedges::FourOnEquator;\n        static_cast<void>(sph_wedge_coordinate_maps(\n            inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n            use_equiangular_map, std::nullopt, use_half_wedges, {},\n            radial_distribution, which_wedges));\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"If we are using half wedges we must also be using \"\n          \"ShellWedges::All.\"));\n}\n\nvoid test_six_wedge_directions_equiangular() {\n  INFO(\"Default six wedge directions equiangular\");\n  const double inner_radius = 1.2;\n  const double outer_radius = 2.7;\n  const double inner_sphericity = 0.8;\n  const double outer_sphericity = 0.6;\n  const bool use_equiangular_map = true;\n  test_wedge_map_generation_against_domain_helpers(\n      inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n      use_equiangular_map, std::nullopt);\n}\n\nvoid test_six_wedge_directions_equidistant() {\n  INFO(\"Default six wedge directions equidistant\");\n  const double inner_radius = 0.8;\n  const double outer_radius = 7.1;\n  const double inner_sphericity = 0.2;\n  const double outer_sphericity = 0.4;\n  const bool use_equiangular_map = false;\n  test_wedge_map_generation_against_domain_helpers(\n      inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n      use_equiangular_map, std::nullopt);\n}\n\nvoid test_six_wedge_directions_with_offset() {\n  INFO(\"Default six wedge directions with an offset\");\n  const double inner_radius = 1.2;\n  const double outer_radius = 2.7;\n  const double inner_sphericity = 1.0;\n  const double outer_sphericity = 0.0;\n  const std::pair<double, std::array<double, 3>> offset_options{\n      {4.0}, {{2.0, 0.0, 0.0}}};\n  const bool use_equiangular_map = true;\n  test_wedge_map_generation_against_domain_helpers(\n      inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n      use_equiangular_map, offset_options);\n}\n\nvoid test_ten_wedge_directions_equiangular() {\n  INFO(\"Ten wedge directions equiangular\");\n  const double inner_radius = 0.2;\n  const double outer_radius = 2.2;\n  const double inner_sphericity = 0.0;\n  const double outer_sphericity = 1.0;\n  const bool use_equiangular_map = true;\n  const bool use_half_wedges = true;\n  test_wedge_map_generation_against_domain_helpers(\n      inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n      use_equiangular_map, std::nullopt, use_half_wedges);\n}\n\nvoid test_ten_wedge_directions_equidistant() {\n  INFO(\"Ten wedge directions equidistant\");\n  const double inner_radius = 0.2;\n  const double outer_radius = 29.2;\n  const double inner_sphericity = 0.01;\n  const double outer_sphericity = 0.99;\n  const bool use_equiangular_map = false;\n  const bool use_half_wedges = true;\n  test_wedge_map_generation_against_domain_helpers(\n      inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n      use_equiangular_map, std::nullopt, use_half_wedges);\n}\n\nvoid test_ten_wedge_directions_with_offset() {\n  INFO(\"Default six wedge directions with an offset\");\n  const double inner_radius = 1.4;\n  const double outer_radius = 3.5;\n  const double inner_sphericity = 1.0;\n  const double outer_sphericity = 0.0;\n  const std::pair<double, std::array<double, 3>> offset_options{\n      {6.0}, {{2.4, 0.0, 0.0}}};\n  const bool use_equiangular_map = false;\n  const bool use_half_wedges = true;\n  test_wedge_map_generation_against_domain_helpers(\n      inner_radius, outer_radius, inner_sphericity, outer_sphericity,\n      use_equiangular_map, offset_options, use_half_wedges);\n}\n\nvoid test_wedge_map_generation() {\n  test_six_wedge_directions_equiangular();\n  test_six_wedge_directions_equidistant();\n  test_six_wedge_directions_with_offset();\n  test_ten_wedge_directions_equiangular();\n  test_ten_wedge_directions_equidistant();\n  test_ten_wedge_directions_with_offset();\n}\n\nvoid test_all_frustum_directions() {\n  using FrustumMap = CoordinateMaps::Frustum;\n  // half of the length of the inner cube in the binary compact object domain:\n  const double lower = 1.7;\n  // half of the length of the outer cube in the binary compact object domain:\n  const double top = 5.2;\n  const std::array<double, 3> origin_preimage{{0.2, 0.3, -0.1}};\n  const auto displacement1 = discrete_rotation(\n      OrientationMap<3>::create_aligned().inverse_map(), origin_preimage);\n  const auto displacement2 = discrete_rotation(\n      OrientationMap<3>::create_aligned().inverse_map(), origin_preimage);\n  const auto displacement3 = discrete_rotation(\n      OrientationMap<3>{\n          std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                       Direction<3>::lower_eta(),\n                                       Direction<3>::lower_zeta()}}}\n          .inverse_map(),\n      origin_preimage);\n  const auto displacement4 = discrete_rotation(\n      OrientationMap<3>{\n          std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                       Direction<3>::lower_eta(),\n                                       Direction<3>::lower_zeta()}}}\n          .inverse_map(),\n      origin_preimage);\n  const auto displacement5 = discrete_rotation(\n      OrientationMap<3>{\n          std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                       Direction<3>::upper_zeta(),\n                                       Direction<3>::lower_eta()}}}\n          .inverse_map(),\n      origin_preimage);\n  const auto displacement6 = discrete_rotation(\n      OrientationMap<3>{\n          std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                       Direction<3>::upper_zeta(),\n                                       Direction<3>::lower_eta()}}}\n          .inverse_map(),\n      origin_preimage);\n  const auto displacement7 = discrete_rotation(\n      OrientationMap<3>{\n          std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                       Direction<3>::lower_zeta(),\n                                       Direction<3>::upper_eta()}}}\n          .inverse_map(),\n      origin_preimage);\n  const auto displacement8 = discrete_rotation(\n      OrientationMap<3>{\n          std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                       Direction<3>::lower_zeta(),\n                                       Direction<3>::upper_eta()}}}\n          .inverse_map(),\n      origin_preimage);\n  const auto displacement9 = discrete_rotation(\n      OrientationMap<3>{\n          std::array<Direction<3>, 3>{{Direction<3>::upper_zeta(),\n                                       Direction<3>::upper_xi(),\n                                       Direction<3>::upper_eta()}}}\n          .inverse_map(),\n      origin_preimage);\n  const auto displacement10 = discrete_rotation(\n      OrientationMap<3>{\n          std::array<Direction<3>, 3>{{Direction<3>::lower_zeta(),\n                                       Direction<3>::lower_xi(),\n                                       Direction<3>::upper_eta()}}}\n          .inverse_map(),\n      origin_preimage);\n\n  const CoordinateMaps::Distribution radial_distribution =\n      CoordinateMaps::Distribution::Projective;\n  const double sphericity = 0.;\n  for (const bool use_equiangular_map : {true, false}) {\n    const double stretch = tan(0.5 * M_PI_2);\n    const auto expected_coord_maps = make_vector(\n        FrustumMap{\n            {{{{-2.0 * lower - displacement1[0], -lower - displacement1[1]}},\n              {{-displacement1[0], lower - displacement1[1]}},\n              {{-stretch * top, -top}},\n              {{0.0, top}}}},\n            lower - displacement1[2],\n            top,\n            OrientationMap<3>::create_aligned(),\n            use_equiangular_map,\n            use_equiangular_map,\n            radial_distribution,\n            std::nullopt,\n            sphericity,\n            -1.},\n        FrustumMap{\n            {{{{-displacement2[0], -lower - displacement2[1]}},\n              {{2.0 * lower - displacement2[0], lower - displacement2[1]}},\n              {{0.0, -top}},\n              {{stretch * top, top}}}},\n            lower - displacement2[2],\n            top,\n            OrientationMap<3>::create_aligned(),\n            use_equiangular_map,\n            use_equiangular_map,\n            radial_distribution,\n            std::nullopt,\n            sphericity,\n            1.},\n        FrustumMap{\n            {{{{-2.0 * lower - displacement3[0], -lower - displacement3[1]}},\n              {{-displacement3[0], lower - displacement3[1]}},\n              {{-stretch * top, -top}},\n              {{0.0, top}}}},\n            lower - displacement3[2],\n            top,\n            OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n                 Direction<3>::lower_zeta()}}},\n            use_equiangular_map,\n            use_equiangular_map,\n            radial_distribution,\n            std::nullopt,\n            sphericity,\n            -1.},\n        FrustumMap{\n            {{{{-displacement4[0], -lower - displacement4[1]}},\n              {{2.0 * lower - displacement4[0], lower - displacement4[1]}},\n              {{0.0, -top}},\n              {{stretch * top, top}}}},\n            lower - displacement4[2],\n            top,\n            OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n                 Direction<3>::lower_zeta()}}},\n            use_equiangular_map,\n            use_equiangular_map,\n            radial_distribution,\n            std::nullopt,\n            sphericity,\n            1.},\n        FrustumMap{\n            {{{{-2.0 * lower - displacement5[0], -lower - displacement5[1]}},\n              {{-displacement5[0], lower - displacement5[1]}},\n              {{-stretch * top, -top}},\n              {{0.0, top}}}},\n            lower - displacement5[2],\n            top,\n            OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n                 Direction<3>::lower_eta()}}},\n            use_equiangular_map,\n            use_equiangular_map,\n            radial_distribution,\n            std::nullopt,\n            sphericity,\n            -1.},\n        FrustumMap{\n            {{{{-displacement6[0], -lower - displacement6[1]}},\n              {{2.0 * lower - displacement6[0], lower - displacement6[1]}},\n              {{0.0, -top}},\n              {{stretch * top, top}}}},\n            lower - displacement6[2],\n            top,\n            OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::upper_xi(), Direction<3>::upper_zeta(),\n                 Direction<3>::lower_eta()}}},\n            use_equiangular_map,\n            use_equiangular_map,\n            radial_distribution,\n            std::nullopt,\n            sphericity,\n            1.},\n        FrustumMap{\n            {{{{-2.0 * lower - displacement7[0], -lower - displacement7[1]}},\n              {{-displacement7[0], lower - displacement7[1]}},\n              {{-stretch * top, -top}},\n              {{0.0, top}}}},\n            lower - displacement7[2],\n            top,\n            OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                 Direction<3>::upper_eta()}}},\n            use_equiangular_map,\n            use_equiangular_map,\n            radial_distribution,\n            std::nullopt,\n            sphericity,\n            -1.},\n        FrustumMap{\n            {{{{-displacement8[0], -lower - displacement8[1]}},\n              {{2.0 * lower - displacement8[0], lower - displacement8[1]}},\n              {{0.0, -top}},\n              {{stretch * top, top}}}},\n            lower - displacement8[2],\n            top,\n            OrientationMap<3>{std::array<Direction<3>, 3>{\n                {Direction<3>::upper_xi(), Direction<3>::lower_zeta(),\n                 Direction<3>::upper_eta()}}},\n            use_equiangular_map,\n            use_equiangular_map,\n            radial_distribution,\n            std::nullopt,\n            sphericity,\n            1.},\n        // Frustum on right half in the +x direction\n        FrustumMap{{{{{-lower - displacement9[0], -lower - displacement9[1]}},\n                     {{lower - displacement9[0], lower - displacement9[1]}},\n                     {{-top, -top}},\n                     {{top, top}}}},\n                   2.0 * lower - displacement9[2],\n                   stretch * top,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::upper_zeta(), Direction<3>::upper_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map,\n                   use_equiangular_map,\n                   radial_distribution},\n        // Frustum on left half in the -x direction\n        FrustumMap{{{{{-lower - displacement10[0], -lower - displacement10[1]}},\n                     {{lower - displacement10[0], lower - displacement10[1]}},\n                     {{-top, -top}},\n                     {{top, top}}}},\n                   2.0 * lower - displacement10[2],\n                   stretch * top,\n                   OrientationMap<3>{std::array<Direction<3>, 3>{\n                       {Direction<3>::lower_zeta(), Direction<3>::lower_xi(),\n                        Direction<3>::upper_eta()}}},\n                   use_equiangular_map,\n                   use_equiangular_map,\n                   radial_distribution});\n\n    const auto maps = frustum_coordinate_maps(\n        2.0 * lower, 2.0 * top, use_equiangular_map, use_equiangular_map,\n        origin_preimage, CoordinateMaps::Distribution::Projective);\n    CHECK(maps == expected_coord_maps);\n  }\n}\n\nvoid test_frustrum_errors() {\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double length_inner_cube = 0.9;\n        const double length_outer_cube = 1.5;\n        const bool use_equiangular_map = true;\n        const std::array<double, 3> origin_preimage = {{0.0, 0.0, 0.0}};\n        static_cast<void>(frustum_coordinate_maps(\n            length_inner_cube, length_outer_cube, use_equiangular_map,\n            use_equiangular_map, origin_preimage));\n      }()),\n      Catch::Matchers::ContainsSubstring(\"The outer cube\") &&\n          Catch::Matchers::ContainsSubstring(\n              \" is too small! The inner cubes\") &&\n          Catch::Matchers::ContainsSubstring(\n              \" will pierce the surface of the outer cube.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double length_inner_cube = 1.0;\n        const double length_outer_cube = 3.0;\n        const bool use_equiangular_map = true;\n        const std::array<double, 3> origin_preimage = {{0.6, 0.0, 0.0}};\n        static_cast<void>(frustum_coordinate_maps(\n            length_inner_cube, length_outer_cube, use_equiangular_map,\n            use_equiangular_map, origin_preimage));\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The current choice for `origin_preimage` results in the \"\n          \"inner cubes piercing the surface of the outer cube.\"));\n}\n\nvoid test_shell_graph() {\n  std::vector<std::array<size_t, 8>> expected_corners = {\n      // Shell on left-hand side:\n      {{5, 6, 7, 8, 13, 14, 15, 16}} /*+z*/,\n      {{3, 4, 1, 2, 11, 12, 9, 10}} /*-z*/,\n      {{7, 8, 3, 4, 15, 16, 11, 12}} /*+y*/,\n      {{1, 2, 5, 6, 9, 10, 13, 14}} /*-y*/,\n      {{2, 4, 6, 8, 10, 12, 14, 16}} /*+x*/,\n      {{3, 1, 7, 5, 11, 9, 15, 13}} /*-x*/};\n  const auto generated_corners = corners_for_radially_layered_domains(1, false);\n  for (size_t i = 0; i < expected_corners.size(); i++) {\n    INFO(i);\n    CHECK(generated_corners[i] == expected_corners[i]);\n  }\n  CHECK(generated_corners == expected_corners);\n}\n\nvoid test_sphere_graph() {\n  std::vector<std::array<size_t, 8>> expected_corners = {\n      // Shell on left-hand side:\n      {{5, 6, 7, 8, 13, 14, 15, 16}} /*+z*/,\n      {{3, 4, 1, 2, 11, 12, 9, 10}} /*-z*/,\n      {{7, 8, 3, 4, 15, 16, 11, 12}} /*+y*/,\n      {{1, 2, 5, 6, 9, 10, 13, 14}} /*-y*/,\n      {{2, 4, 6, 8, 10, 12, 14, 16}} /*+x*/,\n      {{3, 1, 7, 5, 11, 9, 15, 13}} /*-x*/,\n      {{1, 2, 3, 4, 5, 6, 7, 8}} /*central block*/};\n  const auto generated_corners = corners_for_radially_layered_domains(1, true);\n  for (size_t i = 0; i < expected_corners.size(); i++) {\n    INFO(i);\n    CHECK(generated_corners[i] == expected_corners[i]);\n  }\n  CHECK(generated_corners == expected_corners);\n}\n\nstd::vector<std::array<size_t, 8>> expected_bbh_corners() {\n  return {// Shell on left-hand side:\n          {{5, 6, 7, 8, 13, 14, 15, 16}} /*+z*/,\n          {{3, 4, 1, 2, 11, 12, 9, 10}} /*-z*/,\n          {{7, 8, 3, 4, 15, 16, 11, 12}} /*+y*/,\n          {{1, 2, 5, 6, 9, 10, 13, 14}} /*-y*/,\n          {{2, 4, 6, 8, 10, 12, 14, 16}} /*+x*/,\n          {{3, 1, 7, 5, 11, 9, 15, 13}} /*-x*/,\n          // Cube on left-hand side:\n          {{13, 14, 15, 16, 21, 22, 23, 24}} /*+z*/,\n          {{11, 12, 9, 10, 19, 20, 17, 18}} /*-z*/,\n          {{15, 16, 11, 12, 23, 24, 19, 20}} /*+y*/,\n          {{9, 10, 13, 14, 17, 18, 21, 22}} /*-y*/,\n          {{10, 12, 14, 16, 18, 20, 22, 24}} /*+x*/,\n          {{11, 9, 15, 13, 19, 17, 23, 21}} /*-x*/,\n          // Shell on right-hand side:\n          {{45, 46, 47, 48, 53, 54, 55, 56}} /*+z*/,\n          {{43, 44, 41, 42, 51, 52, 49, 50}} /*-z*/,\n          {{47, 48, 43, 44, 55, 56, 51, 52}} /*+y*/,\n          {{41, 42, 45, 46, 49, 50, 53, 54}} /*-y*/,\n          {{42, 44, 46, 48, 50, 52, 54, 56}} /*+x*/,\n          {{43, 41, 47, 45, 51, 49, 55, 53}} /*-x*/,\n          // Cube on right-hand side:\n          {{53, 54, 55, 56, 22, 62, 24, 64}} /*+z*/,\n          {{51, 52, 49, 50, 20, 60, 18, 58}} /*-z*/,\n          {{55, 56, 51, 52, 24, 64, 20, 60}} /*+y*/,\n          {{49, 50, 53, 54, 18, 58, 22, 62}} /*-y*/,\n          {{50, 52, 54, 56, 58, 60, 62, 64}} /*+x*/,\n          {{51, 49, 55, 53, 20, 18, 24, 22}} /*-x*/,\n          // Frustums on both sides:\n          {{21, 22, 23, 24, 29, 30, 31, 32}} /*+zL*/,\n          {{22, 62, 24, 64, 30, 70, 32, 72}} /*+zR*/,\n          {{19, 20, 17, 18, 27, 28, 25, 26}} /*-zL*/,\n          {{20, 60, 18, 58, 28, 68, 26, 66}} /*-zR*/,\n          {{23, 24, 19, 20, 31, 32, 27, 28}} /*+yL*/,\n          {{24, 64, 20, 60, 32, 72, 28, 68}} /*+yR*/,\n          {{17, 18, 21, 22, 25, 26, 29, 30}} /*-yL*/,\n          {{18, 58, 22, 62, 26, 66, 30, 70}} /*-yR*/,\n          {{58, 60, 62, 64, 66, 68, 70, 72}} /*+xR*/,\n          {{19, 17, 23, 21, 27, 25, 31, 29}} /*-xL*/,\n          // Outermost Shell in the wave-zone:\n          {{29, 30, 31, 32, 37, 38, 39, 40}} /*+zL*/,\n          {{30, 70, 32, 72, 38, 78, 40, 80}} /*+zR*/,\n          {{27, 28, 25, 26, 35, 36, 33, 34}} /*-zL*/,\n          {{28, 68, 26, 66, 36, 76, 34, 74}} /*-zR*/,\n          {{31, 32, 27, 28, 39, 40, 35, 36}} /*+yL*/,\n          {{32, 72, 28, 68, 40, 80, 36, 76}} /*+yR*/,\n          {{25, 26, 29, 30, 33, 34, 37, 38}} /*-yL*/,\n          {{26, 66, 30, 70, 34, 74, 38, 78}} /*-yR*/,\n          {{66, 68, 70, 72, 74, 76, 78, 80}} /*+xR*/,\n          {{27, 25, 31, 29, 35, 33, 39, 37}} /*-xL*/};\n}\n\nvoid test_bbh_corners() {\n  const auto generated_corners =\n      corners_for_biradially_layered_domains(2, 2, false, false);\n  for (size_t i = 0; i < expected_bbh_corners().size(); i++) {\n    INFO(i);\n    CHECK(generated_corners[i] == expected_bbh_corners()[i]);\n  }\n  CHECK(generated_corners == expected_bbh_corners());\n}\n\nvoid test_nsbh_corners() {\n  std::vector<std::array<size_t, 8>> expected_corners = expected_bbh_corners();\n  expected_corners.push_back(std::array<size_t, 8>{{1, 2, 3, 4, 5, 6, 7, 8}});\n  const auto generated_corners =\n      corners_for_biradially_layered_domains(2, 2, true, false);\n  for (size_t i = 0; i < expected_corners.size(); i++) {\n    INFO(i);\n    CHECK(generated_corners[i] == expected_corners[i]);\n  }\n  CHECK(generated_corners == expected_corners);\n}\n\nvoid test_bhns_corners() {\n  std::vector<std::array<size_t, 8>> expected_corners = expected_bbh_corners();\n  expected_corners.push_back(\n      std::array<size_t, 8>{{41, 42, 43, 44, 45, 46, 47, 48}});\n  const auto generated_corners =\n      corners_for_biradially_layered_domains(2, 2, false, true);\n  for (size_t i = 0; i < expected_corners.size(); i++) {\n    INFO(i);\n    CHECK(generated_corners[i] == expected_corners[i]);\n  }\n  CHECK(generated_corners == expected_corners);\n}\n\nvoid test_bns_corners() {\n  std::vector<std::array<size_t, 8>> expected_corners = expected_bbh_corners();\n  expected_corners.push_back(std::array<size_t, 8>{{1, 2, 3, 4, 5, 6, 7, 8}});\n  expected_corners.push_back(\n      std::array<size_t, 8>{{41, 42, 43, 44, 45, 46, 47, 48}});\n  const auto generated_corners =\n      corners_for_biradially_layered_domains(2, 2, true, true);\n  for (size_t i = 0; i < expected_corners.size(); i++) {\n    INFO(i);\n    CHECK(generated_corners[i] == expected_corners[i]);\n  }\n  CHECK(generated_corners == expected_corners);\n}\n\nvoid test_vci_1d() {\n  VolumeCornerIterator<1> vci{};\n  CHECK(vci);\n  CHECK(vci() == std::array<Side, 1>{{Side::Lower}});\n  CHECK(vci.coords_of_corner() == std::array<double, 1>{{-1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 1>{{Side::Upper}});\n  CHECK(vci.coords_of_corner() == std::array<double, 1>{{1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<1>, 1>{{Direction<1>::upper_xi()}});\n  ++vci;\n  CHECK(not vci);\n\n  VolumeCornerIterator<1> vci2{Index<1>{2}, Index<1>{7}};\n  CHECK(vci2);\n  CHECK(vci2.global_corner_number() == 2);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 3);\n  ++vci2;\n  CHECK(not vci2);\n\n  // Check setup_from_local_corner_number\n  VolumeCornerIterator<1> vci3{1};\n  CHECK(vci3() == std::array<Side, 1>{{Side::Upper}});\n  CHECK(vci3.coords_of_corner() == std::array<double, 1>{{1.0}});\n  CHECK(vci3.directions_of_corner() ==\n        std::array<Direction<1>, 1>{{Direction<1>::upper_xi()}});\n}\n\nvoid test_vci_2d() {\n  VolumeCornerIterator<2> vci{};\n  CHECK(vci);\n  CHECK(vci() == std::array<Side, 2>{{Side::Lower, Side::Lower}});\n  CHECK(vci.coords_of_corner() == std::array<double, 2>{{-1.0, -1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<2>, 2>{\n            {Direction<2>::lower_xi(), Direction<2>::lower_eta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 2>{{Side::Upper, Side::Lower}});\n  CHECK(vci.coords_of_corner() == std::array<double, 2>{{1.0, -1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<2>, 2>{\n            {Direction<2>::upper_xi(), Direction<2>::lower_eta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 2>{{Side::Lower, Side::Upper}});\n  CHECK(vci.coords_of_corner() == std::array<double, 2>{{-1.0, 1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<2>, 2>{\n            {Direction<2>::lower_xi(), Direction<2>::upper_eta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 2>{{Side::Upper, Side::Upper}});\n  CHECK(vci.coords_of_corner() == std::array<double, 2>{{1.0, 1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<2>, 2>{\n            {Direction<2>::upper_xi(), Direction<2>::upper_eta()}});\n  ++vci;\n  CHECK(not vci);\n\n  VolumeCornerIterator<2> vci2{Index<2>{1, 2}, Index<2>{3, 4}};\n  CHECK(vci2);\n  CHECK(vci2.global_corner_number() == 7);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 8);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 10);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 11);\n  ++vci2;\n  CHECK(not vci2);\n\n  // Check setup_from_local_corner_number\n  VolumeCornerIterator<2> vci3{2};\n  CHECK(vci3() == std::array<Side, 2>{{Side::Lower, Side::Upper}});\n  CHECK(vci3.coords_of_corner() == std::array<double, 2>{{-1.0, 1.0}});\n  CHECK(vci3.directions_of_corner() ==\n        std::array<Direction<2>, 2>{\n            {Direction<2>::lower_xi(), Direction<2>::upper_eta()}});\n}\n\nvoid test_vci_3d() {\n  VolumeCornerIterator<3> vci{};\n  CHECK(vci);\n  CHECK(vci() == std::array<Side, 3>{{Side::Lower, Side::Lower, Side::Lower}});\n  CHECK(vci.coords_of_corner() == std::array<double, 3>{{-1.0, -1.0, -1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::lower_xi(),\n                                     Direction<3>::lower_eta(),\n                                     Direction<3>::lower_zeta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 3>{{Side::Upper, Side::Lower, Side::Lower}});\n  CHECK(vci.coords_of_corner() == std::array<double, 3>{{1.0, -1.0, -1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                     Direction<3>::lower_eta(),\n                                     Direction<3>::lower_zeta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 3>{{Side::Lower, Side::Upper, Side::Lower}});\n  CHECK(vci.coords_of_corner() == std::array<double, 3>{{-1.0, 1.0, -1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::lower_xi(),\n                                     Direction<3>::upper_eta(),\n                                     Direction<3>::lower_zeta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 3>{{Side::Upper, Side::Upper, Side::Lower}});\n  CHECK(vci.coords_of_corner() == std::array<double, 3>{{1.0, 1.0, -1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                     Direction<3>::upper_eta(),\n                                     Direction<3>::lower_zeta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 3>{{Side::Lower, Side::Lower, Side::Upper}});\n  CHECK(vci.coords_of_corner() == std::array<double, 3>{{-1.0, -1.0, 1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::lower_xi(),\n                                     Direction<3>::lower_eta(),\n                                     Direction<3>::upper_zeta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 3>{{Side::Upper, Side::Lower, Side::Upper}});\n  CHECK(vci.coords_of_corner() == std::array<double, 3>{{1.0, -1.0, 1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                     Direction<3>::lower_eta(),\n                                     Direction<3>::upper_zeta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 3>{{Side::Lower, Side::Upper, Side::Upper}});\n  CHECK(vci.coords_of_corner() == std::array<double, 3>{{-1.0, 1.0, 1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::lower_xi(),\n                                     Direction<3>::upper_eta(),\n                                     Direction<3>::upper_zeta()}});\n  ++vci;\n  CHECK(vci() == std::array<Side, 3>{{Side::Upper, Side::Upper, Side::Upper}});\n  CHECK(vci.coords_of_corner() == std::array<double, 3>{{1.0, 1.0, 1.0}});\n  CHECK(vci.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                     Direction<3>::upper_eta(),\n                                     Direction<3>::upper_zeta()}});\n  ++vci;\n  CHECK(not vci);\n\n  VolumeCornerIterator<3> vci2{Index<3>{1, 1, 1}, Index<3>{4, 4, 4}};\n  CHECK(vci2);\n  CHECK(vci2.global_corner_number() == 21);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 22);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 25);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 26);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 37);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 38);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 41);\n  ++vci2;\n  CHECK(vci2.global_corner_number() == 42);\n  ++vci2;\n  CHECK(not vci2);\n\n  // Check setup_from_local_corner_number\n  VolumeCornerIterator<3> vci3{5};\n  CHECK(vci3() == std::array<Side, 3>{{Side::Upper, Side::Lower, Side::Upper}});\n  CHECK(vci3.coords_of_corner() == std::array<double, 3>{{1.0, -1.0, 1.0}});\n  CHECK(vci3.directions_of_corner() ==\n        std::array<Direction<3>, 3>{{Direction<3>::upper_xi(),\n                                     Direction<3>::lower_eta(),\n                                     Direction<3>::upper_zeta()}});\n}\n\nvoid test_volume_corner_iterator() {\n  test_vci_1d();\n  test_vci_2d();\n  test_vci_3d();\n}\n\nvoid test_fci_1d() {\n  FaceCornerIterator<1> fci{Direction<1>::upper_xi()};\n  CHECK(fci);\n  CHECK(fci.volume_index() == 1);\n  CHECK(fci.face_index() == 0);\n  ++fci;\n  CHECK(not fci);\n\n  FaceCornerIterator<1> fci2{Direction<1>::lower_xi()};\n  CHECK(fci2);\n  CHECK(fci2.volume_index() == 0);\n  CHECK(fci2.face_index() == 0);\n  ++fci2;\n  CHECK(not fci2);\n}\n\nvoid test_fci_2d() {\n  FaceCornerIterator<2> fci{Direction<2>::upper_xi()};\n  CHECK(fci);\n  CHECK(fci.volume_index() == 1);\n  CHECK(fci.face_index() == 0);\n  ++fci;\n  CHECK(fci.volume_index() == 3);\n  CHECK(fci.face_index() == 1);\n  ++fci;\n  CHECK(not fci);\n\n  FaceCornerIterator<2> fci2{Direction<2>::lower_xi()};\n  CHECK(fci2);\n  CHECK(fci2.volume_index() == 0);\n  CHECK(fci2.face_index() == 0);\n  ++fci2;\n  CHECK(fci2.volume_index() == 2);\n  CHECK(fci2.face_index() == 1);\n  ++fci2;\n  CHECK(not fci2);\n\n  FaceCornerIterator<2> fci3{Direction<2>::upper_eta()};\n  CHECK(fci3);\n  CHECK(fci3.volume_index() == 2);\n  CHECK(fci3.face_index() == 0);\n  ++fci3;\n  CHECK(fci3.volume_index() == 3);\n  CHECK(fci3.face_index() == 1);\n  ++fci3;\n  CHECK(not fci3);\n\n  FaceCornerIterator<2> fci4{Direction<2>::lower_eta()};\n  CHECK(fci4);\n  CHECK(fci4.volume_index() == 0);\n  CHECK(fci4.face_index() == 0);\n  ++fci4;\n  CHECK(fci4.volume_index() == 1);\n  CHECK(fci4.face_index() == 1);\n  ++fci4;\n  CHECK(not fci4);\n}\nvoid test_fci_3d() {\n  FaceCornerIterator<3> fci{Direction<3>::upper_xi()};\n  CHECK(fci);\n  CHECK(fci.volume_index() == 1);\n  CHECK(fci.face_index() == 0);\n  ++fci;\n  CHECK(fci.volume_index() == 3);\n  CHECK(fci.face_index() == 1);\n  ++fci;\n  CHECK(fci.volume_index() == 5);\n  CHECK(fci.face_index() == 2);\n  ++fci;\n  CHECK(fci.volume_index() == 7);\n  CHECK(fci.face_index() == 3);\n  ++fci;\n  CHECK(not fci);\n\n  FaceCornerIterator<3> fci2{Direction<3>::lower_xi()};\n  CHECK(fci2);\n  CHECK(fci2.volume_index() == 0);\n  CHECK(fci2.face_index() == 0);\n  ++fci2;\n  CHECK(fci2.volume_index() == 2);\n  CHECK(fci2.face_index() == 1);\n  ++fci2;\n  CHECK(fci2.volume_index() == 4);\n  CHECK(fci2.face_index() == 2);\n  ++fci2;\n  CHECK(fci2.volume_index() == 6);\n  CHECK(fci2.face_index() == 3);\n  ++fci2;\n  CHECK(not fci2);\n\n  FaceCornerIterator<3> fci3{Direction<3>::upper_eta()};\n  CHECK(fci3);\n  CHECK(fci3.volume_index() == 2);\n  CHECK(fci3.face_index() == 0);\n  ++fci3;\n  CHECK(fci3.volume_index() == 3);\n  CHECK(fci3.face_index() == 1);\n  ++fci3;\n  CHECK(fci3.volume_index() == 6);\n  CHECK(fci3.face_index() == 2);\n  ++fci3;\n  CHECK(fci3.volume_index() == 7);\n  CHECK(fci3.face_index() == 3);\n  ++fci3;\n  CHECK(not fci3);\n\n  FaceCornerIterator<3> fci4{Direction<3>::lower_eta()};\n  CHECK(fci4);\n  CHECK(fci4.volume_index() == 0);\n  CHECK(fci4.face_index() == 0);\n  ++fci4;\n  CHECK(fci4.volume_index() == 1);\n  CHECK(fci4.face_index() == 1);\n  ++fci4;\n  CHECK(fci4.volume_index() == 4);\n  CHECK(fci4.face_index() == 2);\n  ++fci4;\n  CHECK(fci4.volume_index() == 5);\n  CHECK(fci4.face_index() == 3);\n  ++fci4;\n  CHECK(not fci4);\n\n  FaceCornerIterator<3> fci5{Direction<3>::upper_zeta()};\n  CHECK(fci5);\n  CHECK(fci5.volume_index() == 4);\n  CHECK(fci5.face_index() == 0);\n  ++fci5;\n  CHECK(fci5.volume_index() == 5);\n  CHECK(fci5.face_index() == 1);\n  ++fci5;\n  CHECK(fci5.volume_index() == 6);\n  CHECK(fci5.face_index() == 2);\n  ++fci5;\n  CHECK(fci5.volume_index() == 7);\n  CHECK(fci5.face_index() == 3);\n  ++fci5;\n  CHECK(not fci5);\n\n  FaceCornerIterator<3> fci6{Direction<3>::lower_zeta()};\n  CHECK(fci6);\n  CHECK(fci6.volume_index() == 0);\n  CHECK(fci6.face_index() == 0);\n  ++fci6;\n  CHECK(fci6.volume_index() == 1);\n  CHECK(fci6.face_index() == 1);\n  ++fci6;\n  CHECK(fci6.volume_index() == 2);\n  CHECK(fci6.face_index() == 2);\n  ++fci6;\n  CHECK(fci6.volume_index() == 3);\n  CHECK(fci6.face_index() == 3);\n  ++fci6;\n  CHECK(not fci6);\n}\n\nvoid test_face_corner_iterator() {\n  test_fci_1d();\n  test_fci_2d();\n  test_fci_3d();\n}\n\nvoid test_indices_for_rectilinear_domains() {\n  std::vector<Index<1>> indices_for_a_1d_road{Index<1>{0}, Index<1>{1},\n                                              Index<1>{2}};\n  std::vector<Index<2>> indices_for_a_2d_vertical_tower{\n      Index<2>{0, 0}, Index<2>{0, 1}, Index<2>{0, 2}};\n  std::vector<Index<2>> indices_for_a_2d_horizontal_wall{\n      Index<2>{0, 0}, Index<2>{1, 0}, Index<2>{2, 0}};\n  std::vector<Index<2>> indices_for_a_2d_field{Index<2>{0, 0}, Index<2>{1, 0},\n                                               Index<2>{0, 1}, Index<2>{1, 1}};\n  std::vector<Index<2>> indices_for_a_2d_net_of_a_cube{\n      Index<2>{1, 0}, Index<2>{1, 1}, Index<2>{0, 2},\n      Index<2>{1, 2}, Index<2>{2, 2}, Index<2>{1, 3}};\n  std::vector<Index<3>> indices_for_a_3d_cube{\n      Index<3>{0, 0, 0}, Index<3>{1, 0, 0}, Index<3>{0, 1, 0},\n      Index<3>{1, 1, 0}, Index<3>{0, 0, 1}, Index<3>{1, 0, 1},\n      Index<3>{0, 1, 1}, Index<3>{1, 1, 1}};\n  std::vector<Index<3>> indices_for_a_rubiks_cube_with_hole{\n      Index<3>{0, 0, 0}, Index<3>{1, 0, 0}, Index<3>{2, 0, 0},\n      Index<3>{0, 1, 0}, Index<3>{1, 1, 0}, Index<3>{2, 1, 0},\n      Index<3>{0, 2, 0}, Index<3>{1, 2, 0}, Index<3>{2, 2, 0},\n      Index<3>{0, 0, 1}, Index<3>{1, 0, 1}, Index<3>{2, 0, 1},\n      Index<3>{0, 1, 1},\n      /*central block is skipped!*/\n      Index<3>{2, 1, 1}, Index<3>{0, 2, 1}, Index<3>{1, 2, 1},\n      Index<3>{2, 2, 1}, Index<3>{0, 0, 2}, Index<3>{1, 0, 2},\n      Index<3>{2, 0, 2}, Index<3>{0, 1, 2}, Index<3>{1, 1, 2},\n      Index<3>{2, 1, 2}, Index<3>{0, 2, 2}, Index<3>{1, 2, 2},\n      Index<3>{2, 2, 2}};\n\n  CHECK(indices_for_rectilinear_domains(Index<1>{3}) == indices_for_a_1d_road);\n  CHECK(indices_for_rectilinear_domains(Index<2>{1, 3}) ==\n        indices_for_a_2d_vertical_tower);\n  CHECK(indices_for_rectilinear_domains(Index<2>{3, 1}) ==\n        indices_for_a_2d_horizontal_wall);\n  CHECK(indices_for_rectilinear_domains(Index<2>{2, 2}) ==\n        indices_for_a_2d_field);\n  CHECK(indices_for_rectilinear_domains(\n            Index<2>{3, 4},\n            std::vector<Index<2>>{Index<2>{0, 0}, Index<2>{2, 0},\n                                  Index<2>{0, 1}, Index<2>{2, 1},\n                                  Index<2>{0, 3}, Index<2>{2, 3}}) ==\n        indices_for_a_2d_net_of_a_cube);\n  CHECK(indices_for_rectilinear_domains(Index<3>{2, 2, 2}) ==\n        indices_for_a_3d_cube);\n  CHECK(indices_for_rectilinear_domains(\n            Index<3>{3, 3, 3}, std::vector<Index<3>>{Index<3>{1, 1, 1}}) ==\n        indices_for_a_rubiks_cube_with_hole);\n}\n\nvoid test_block_names_for_rectilinear_domains() {\n  const auto names_for_a_1d_road =\n      make_vector(\"Block(0)\"s, \"Block(1)\"s, \"Block(2)\"s);\n  const auto names_for_a_2d_vertical_tower =\n      make_vector(\"Block(0,0)\"s, \"Block(0,1)\"s, \"Block(0,2)\"s);\n  const auto names_for_a_2d_horizontal_wall =\n      make_vector(\"Block(0,0)\"s, \"Block(1,0)\"s, \"Block(2,0)\"s);\n  const auto names_for_a_2d_field =\n      make_vector(\"Block(0,0)\"s, \"Block(1,0)\"s, \"Block(0,1)\"s, \"Block(1,1)\"s);\n  const auto names_for_a_2d_net_of_a_cube =\n      make_vector(\"Block(1,0)\"s, \"Block(1,1)\"s, \"Block(0,2)\"s, \"Block(1,2)\"s,\n                  \"Block(2,2)\"s, \"Block(1,3)\"s);\n  const auto names_for_a_3d_cube = make_vector(\n      \"Block(0,0,0)\"s, \"Block(1,0,0)\"s, \"Block(0,1,0)\"s, \"Block(1,1,0)\"s,\n      \"Block(0,0,1)\"s, \"Block(1,0,1)\"s, \"Block(0,1,1)\"s, \"Block(1,1,1)\"s);\n  const auto names_for_a_rubiks_cube_with_hole = make_vector(\n      \"Block(0,0,0)\"s, \"Block(1,0,0)\"s, \"Block(2,0,0)\"s, \"Block(0,1,0)\"s,\n      \"Block(1,1,0)\"s, \"Block(2,1,0)\"s, \"Block(0,2,0)\"s, \"Block(1,2,0)\"s,\n      \"Block(2,2,0)\"s, \"Block(0,0,1)\"s, \"Block(1,0,1)\"s, \"Block(2,0,1)\"s,\n      \"Block(0,1,1)\"s,\n      /*central block is skipped!*/\n      \"Block(2,1,1)\"s, \"Block(0,2,1)\"s, \"Block(1,2,1)\"s, \"Block(2,2,1)\"s,\n      \"Block(0,0,2)\"s, \"Block(1,0,2)\"s, \"Block(2,0,2)\"s, \"Block(0,1,2)\"s,\n      \"Block(1,1,2)\"s, \"Block(2,1,2)\"s, \"Block(0,2,2)\"s, \"Block(1,2,2)\"s,\n      \"Block(2,2,2)\"s);\n\n  CHECK(block_names_for_rectilinear_domains(Index<1>{3}) ==\n        names_for_a_1d_road);\n  CHECK(block_names_for_rectilinear_domains(Index<2>{1, 3}) ==\n        names_for_a_2d_vertical_tower);\n  CHECK(block_names_for_rectilinear_domains(Index<2>{3, 1}) ==\n        names_for_a_2d_horizontal_wall);\n  CHECK(block_names_for_rectilinear_domains(Index<2>{2, 2}) ==\n        names_for_a_2d_field);\n  CHECK(block_names_for_rectilinear_domains(\n            Index<2>{3, 4},\n            std::vector<Index<2>>{Index<2>{0, 0}, Index<2>{2, 0},\n                                  Index<2>{0, 1}, Index<2>{2, 1},\n                                  Index<2>{0, 3}, Index<2>{2, 3}}) ==\n        names_for_a_2d_net_of_a_cube);\n  CHECK(block_names_for_rectilinear_domains(Index<3>{2, 2, 2}) ==\n        names_for_a_3d_cube);\n  CHECK(block_names_for_rectilinear_domains(\n            Index<3>{3, 3, 3}, std::vector<Index<3>>{Index<3>{1, 1, 1}}) ==\n        names_for_a_rubiks_cube_with_hole);\n}\n\nvoid test_corners_for_rectilinear_domains() {\n  std::vector<std::array<size_t, 2>> corners_for_a_1d_road{\n      {{0, 1}}, {{1, 2}}, {{2, 3}}};\n  std::vector<std::array<size_t, 4>> corners_for_a_2d_vertical_tower{\n      {{0, 1, 2, 3}}, {{2, 3, 4, 5}}, {{4, 5, 6, 7}}};\n  std::vector<std::array<size_t, 4>> corners_for_a_2d_horizontal_wall{\n      {{0, 1, 4, 5}}, {{1, 2, 5, 6}}, {{2, 3, 6, 7}}};\n  std::vector<std::array<size_t, 4>> corners_for_a_2d_field{\n      {{0, 1, 3, 4}}, {{1, 2, 4, 5}}, {{3, 4, 6, 7}}, {{4, 5, 7, 8}}};\n  // This 2D net of a cube does not have its boundaries identified.\n  std::vector<std::array<size_t, 4>> corners_for_a_2d_net_of_a_cube{\n      {{1, 2, 5, 6}},    {{5, 6, 9, 10}},    {{8, 9, 12, 13}},\n      {{9, 10, 13, 14}}, {{10, 11, 14, 15}}, {{13, 14, 17, 18}}};\n  std::vector<std::array<size_t, 8>> corners_for_a_3d_cube{\n      {{0, 1, 3, 4, 9, 10, 12, 13}},      {{1, 2, 4, 5, 10, 11, 13, 14}},\n      {{3, 4, 6, 7, 12, 13, 15, 16}},     {{4, 5, 7, 8, 13, 14, 16, 17}},\n      {{9, 10, 12, 13, 18, 19, 21, 22}},  {{10, 11, 13, 14, 19, 20, 22, 23}},\n      {{12, 13, 15, 16, 21, 22, 24, 25}}, {{13, 14, 16, 17, 22, 23, 25, 26}}};\n  std::vector<std::array<size_t, 8>> corners_for_a_rubiks_cube_with_hole{\n      {{0, 1, 4, 5, 16, 17, 20, 21}},\n      {{1, 2, 5, 6, 17, 18, 21, 22}},\n      {{2, 3, 6, 7, 18, 19, 22, 23}},\n      {{4, 5, 8, 9, 20, 21, 24, 25}},\n      {{5, 6, 9, 10, 21, 22, 25, 26}},\n      {{6, 7, 10, 11, 22, 23, 26, 27}},\n      {{8, 9, 12, 13, 24, 25, 28, 29}},\n      {{9, 10, 13, 14, 25, 26, 29, 30}},\n      {{10, 11, 14, 15, 26, 27, 30, 31}},\n\n      {{16, 17, 20, 21, 32, 33, 36, 37}},\n      {{17, 18, 21, 22, 33, 34, 37, 38}},\n      {{18, 19, 22, 23, 34, 35, 38, 39}},\n      {{20, 21, 24, 25, 36, 37, 40, 41}},\n      /*central block is skipped!*/\n      {{22, 23, 26, 27, 38, 39, 42, 43}},\n      {{24, 25, 28, 29, 40, 41, 44, 45}},\n      {{25, 26, 29, 30, 41, 42, 45, 46}},\n      {{26, 27, 30, 31, 42, 43, 46, 47}},\n\n      {{32, 33, 36, 37, 48, 49, 52, 53}},\n      {{33, 34, 37, 38, 49, 50, 53, 54}},\n      {{34, 35, 38, 39, 50, 51, 54, 55}},\n      {{36, 37, 40, 41, 52, 53, 56, 57}},\n      {{37, 38, 41, 42, 53, 54, 57, 58}},\n      {{38, 39, 42, 43, 54, 55, 58, 59}},\n      {{40, 41, 44, 45, 56, 57, 60, 61}},\n      {{41, 42, 45, 46, 57, 58, 61, 62}},\n      {{42, 43, 46, 47, 58, 59, 62, 63}}};\n\n  CHECK(corners_for_rectilinear_domains(Index<1>{3}) == corners_for_a_1d_road);\n  CHECK(corners_for_rectilinear_domains(Index<2>{1, 3}) ==\n        corners_for_a_2d_vertical_tower);\n  CHECK(corners_for_rectilinear_domains(Index<2>{3, 1}) ==\n        corners_for_a_2d_horizontal_wall);\n  CHECK(corners_for_rectilinear_domains(Index<2>{2, 2}) ==\n        corners_for_a_2d_field);\n  CHECK(corners_for_rectilinear_domains(\n            Index<2>{3, 4},\n            std::vector<Index<2>>{Index<2>{0, 0}, Index<2>{2, 0},\n                                  Index<2>{0, 1}, Index<2>{2, 1},\n                                  Index<2>{0, 3}, Index<2>{2, 3}}) ==\n        corners_for_a_2d_net_of_a_cube);\n  CHECK(corners_for_rectilinear_domains(Index<3>{2, 2, 2}) ==\n        corners_for_a_3d_cube);\n  CHECK(corners_for_rectilinear_domains(\n            Index<3>{3, 3, 3}, std::vector<Index<3>>{Index<3>{1, 1, 1}}) ==\n        corners_for_a_rubiks_cube_with_hole);\n}\n\nvoid test_discrete_rotation_corner_numbers() {\n  CHECK(std::array<size_t, 2>{{0, 1}} ==\n        discrete_rotation(OrientationMap<1>{std::array<Direction<1>, 1>{\n                              {Direction<1>::upper_xi()}}},\n                          std::array<size_t, 2>{{0, 1}}));\n  CHECK(std::array<size_t, 2>{{1, 0}} ==\n        discrete_rotation(OrientationMap<1>{std::array<Direction<1>, 1>{\n                              {Direction<1>::lower_xi()}}},\n                          std::array<size_t, 2>{{0, 1}}));\n\n  CHECK(std::array<size_t, 4>{{1, 4, 0, 3}} ==\n        discrete_rotation(\n            OrientationMap<2>(std::array<Direction<2>, 2>{\n                {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}),\n            std::array<size_t, 4>{{0, 1, 3, 4}}));\n  CHECK(std::array<size_t, 4>{{4, 0, 5, 1}} ==\n        discrete_rotation(\n            OrientationMap<2>(std::array<Direction<2>, 2>{\n                {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}),\n            std::array<size_t, 4>{{0, 1, 4, 5}}));\n  CHECK(std::array<size_t, 4>{{3, 1, 2, 0}} ==\n        discrete_rotation(\n            OrientationMap<2>(std::array<Direction<2>, 2>{\n                {Direction<2>::lower_eta(), Direction<2>::lower_xi()}}),\n            std::array<size_t, 4>{{0, 1, 2, 3}}));\n\n  CHECK(std::array<size_t, 8>{{9, 0, 12, 3, 10, 1, 13, 4}} ==\n        discrete_rotation(\n            OrientationMap<3>(std::array<Direction<3>, 3>{\n                {Direction<3>::upper_zeta(), Direction<3>::upper_eta(),\n                 Direction<3>::lower_xi()}}),\n            std::array<size_t, 8>{{0, 1, 3, 4, 9, 10, 12, 13}}));\n  CHECK(std::array<size_t, 8>{{10, 13, 9, 12, 1, 4, 0, 3}} ==\n        discrete_rotation(\n            OrientationMap<3>(std::array<Direction<3>, 3>{\n                {Direction<3>::lower_eta(), Direction<3>::upper_xi(),\n                 Direction<3>::lower_zeta()}}),\n            std::array<size_t, 8>{{0, 1, 3, 4, 9, 10, 12, 13}}));\n  CHECK(std::array<size_t, 8>{{12, 3, 13, 4, 9, 0, 10, 1}} ==\n        discrete_rotation(\n            OrientationMap<3>(std::array<Direction<3>, 3>{\n                {Direction<3>::upper_eta(), Direction<3>::lower_zeta(),\n                 Direction<3>::lower_xi()}}),\n            std::array<size_t, 8>{{0, 1, 3, 4, 9, 10, 12, 13}}));\n}\n\nvoid test_maps_for_rectilinear_domains() {\n  using Affine = CoordinateMaps::Affine;\n  using Affine2D = CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using Affine3D = CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  using Equiangular = CoordinateMaps::Equiangular;\n  using Equiangular2D =\n      CoordinateMaps::ProductOf2Maps<Equiangular, Equiangular>;\n  using Equiangular3D =\n      CoordinateMaps::ProductOf3Maps<Equiangular, Equiangular, Equiangular>;\n\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 1>>>\n      affine_maps_1d = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<1>{3},\n          std::array<std::vector<double>, 1>{{{0.0, 0.5, 1.7, 2.0}}},\n          {Index<1>{0}}, {}, false);\n  const auto expected_affine_maps_1d =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine{-1., 1., 0.5, 1.7}, Affine{-1., 1., 1.7, 2.0});\n  for (size_t i = 0; i < affine_maps_1d.size(); i++) {\n    CHECK(*affine_maps_1d[i] == *expected_affine_maps_1d[i]);\n  }\n\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 1>>>\n      equiangular_maps_1d = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<1>{3},\n          std::array<std::vector<double>, 1>{{{0.0, 0.5, 1.7, 2.0}}},\n          {Index<1>{1}}, {}, true);\n  const auto expected_equiangular_maps_1d =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Equiangular{-1., 1., 0.0, 0.5}, Equiangular{-1., 1., 1.7, 2.0});\n  for (size_t i = 0; i < equiangular_maps_1d.size(); i++) {\n    CHECK(*equiangular_maps_1d[i] == *expected_equiangular_maps_1d[i]);\n  }\n\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 2>>>\n      affine_maps_2d = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<2>{3, 2},\n          std::array<std::vector<double>, 2>{\n              {{0.0, 0.5, 1.7, 2.0}, {0.0, 1.0, 2.0}}},\n          {Index<2>{}}, {}, false);\n  const auto expected_affine_maps_2d =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine2D{Affine{-1., 1., 0.0, 0.5}, Affine{-1., 1., 0.0, 1.0}},\n          Affine2D{Affine{-1., 1., 0.5, 1.7}, Affine{-1., 1., 0.0, 1.0}},\n          Affine2D{Affine{-1., 1., 1.7, 2.0}, Affine{-1., 1., 0.0, 1.0}},\n          Affine2D{Affine{-1., 1., 0.0, 0.5}, Affine{-1., 1., 1.0, 2.0}},\n          Affine2D{Affine{-1., 1., 0.5, 1.7}, Affine{-1., 1., 1.0, 2.0}},\n          Affine2D{Affine{-1., 1., 1.7, 2.0}, Affine{-1., 1., 1.0, 2.0}});\n  for (size_t i = 0; i < affine_maps_2d.size(); i++) {\n    CHECK(*affine_maps_2d[i] == *expected_affine_maps_2d[i]);\n  }\n\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 2>>>\n      equiangular_maps_2d = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<2>{3, 2},\n          std::array<std::vector<double>, 2>{\n              {{0.0, 0.5, 1.7, 2.0}, {0.0, 1.0, 2.0}}},\n          {Index<2>{2, 1}}, {}, true);\n  const auto expected_equiangular_maps_2d =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Equiangular2D{Equiangular{-1., 1., 0.0, 0.5},\n                        Equiangular{-1., 1., 0.0, 1.0}},\n          Equiangular2D{Equiangular{-1., 1., 0.5, 1.7},\n                        Equiangular{-1., 1., 0.0, 1.0}},\n          Equiangular2D{Equiangular{-1., 1., 1.7, 2.0},\n                        Equiangular{-1., 1., 0.0, 1.0}},\n          Equiangular2D{Equiangular{-1., 1., 0.0, 0.5},\n                        Equiangular{-1., 1., 1.0, 2.0}},\n          Equiangular2D{Equiangular{-1., 1., 0.5, 1.7},\n                        Equiangular{-1., 1., 1.0, 2.0}});\n  for (size_t i = 0; i < equiangular_maps_2d.size(); i++) {\n    CHECK(*equiangular_maps_2d[i] == *expected_equiangular_maps_2d[i]);\n  }\n\n  // [show_maps_for_rectilinear_domains]\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>\n      affine_maps_3d = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<3>{2, 2, 1},\n          std::array<std::vector<double>, 3>{\n              {{0.0, 0.5, 2.0}, {0.0, 1.0, 2.0}, {-0.4, 0.3}}},\n          {Index<3>{}}, {}, false);\n  // [show_maps_for_rectilinear_domains]\n  const auto expected_affine_maps_3d =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D{Affine{-1., 1., 0.0, 0.5}, Affine{-1., 1., 0.0, 1.0},\n                   Affine{-1., 1., -0.4, 0.3}},\n          Affine3D{Affine{-1., 1., 0.5, 2.0}, Affine{-1., 1., 0.0, 1.0},\n                   Affine{-1., 1., -0.4, 0.3}},\n          Affine3D{Affine{-1., 1., 0.0, 0.5}, Affine{-1., 1., 1.0, 2.0},\n                   Affine{-1., 1., -0.4, 0.3}},\n          Affine3D{Affine{-1., 1., 0.5, 2.0}, Affine{-1., 1., 1.0, 2.0},\n                   Affine{-1., 1., -0.4, 0.3}});\n  for (size_t i = 0; i < affine_maps_3d.size(); i++) {\n    CHECK(*affine_maps_3d[i] == *expected_affine_maps_3d[i]);\n  }\n\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>\n      equiangular_maps_3d = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<3>{2, 2, 1},\n          std::array<std::vector<double>, 3>{\n              {{0.0, 0.5, 2.0}, {0.0, 1.0, 2.0}, {-0.4, 0.3}}},\n          {Index<3>{0, 0, 0}}, {}, true);\n  const auto expected_equiangular_maps_3d =\n      make_vector_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Equiangular3D{Equiangular{-1., 1., 0.5, 2.0},\n                        Equiangular{-1., 1., 0.0, 1.0},\n                        Equiangular{-1., 1., -0.4, 0.3}},\n          Equiangular3D{Equiangular{-1., 1., 0.0, 0.5},\n                        Equiangular{-1., 1., 1.0, 2.0},\n                        Equiangular{-1., 1., -0.4, 0.3}},\n          Equiangular3D{Equiangular{-1., 1., 0.5, 2.0},\n                        Equiangular{-1., 1., 1.0, 2.0},\n                        Equiangular{-1., 1., -0.4, 0.3}});\n  for (size_t i = 0; i < equiangular_maps_3d.size(); i++) {\n    CHECK(*equiangular_maps_3d[i] == *expected_equiangular_maps_3d[i]);\n  }\n}\n\nvoid test_set_cartesian_periodic_boundaries_1() {\n  // 9x9 cube with the central block removed. Periodic is xi.\n  const std::vector<std::unordered_set<Direction<3>>>\n      expected_external_boundaries{\n          {{Direction<3>::lower_zeta(), Direction<3>::lower_eta()}},\n          {{Direction<3>::lower_zeta(), Direction<3>::lower_eta()}},\n          {{Direction<3>::lower_zeta(), Direction<3>::lower_eta()}},\n          {{Direction<3>::lower_zeta()}},\n          {{Direction<3>::lower_zeta(), Direction<3>::upper_zeta()}},\n          {{Direction<3>::lower_zeta()}},\n          {{Direction<3>::lower_zeta(), Direction<3>::upper_eta()}},\n          {{Direction<3>::lower_zeta(), Direction<3>::upper_eta()}},\n          {{Direction<3>::lower_zeta(), Direction<3>::upper_eta()}},\n          {{Direction<3>::lower_eta()}},\n          {{Direction<3>::lower_eta(), Direction<3>::upper_eta()}},\n          {{Direction<3>::lower_eta()}},\n          {},\n          {},\n          {{Direction<3>::upper_eta()}},\n          {{Direction<3>::upper_eta(), Direction<3>::lower_eta()}},\n          {{Direction<3>::upper_eta()}},\n          {{Direction<3>::upper_zeta(), Direction<3>::lower_eta()}},\n          {{Direction<3>::upper_zeta(), Direction<3>::lower_eta()}},\n          {{Direction<3>::upper_zeta(), Direction<3>::lower_eta()}},\n          {{Direction<3>::upper_zeta()}},\n          {{Direction<3>::upper_zeta(), Direction<3>::lower_zeta()}},\n          {{Direction<3>::upper_zeta()}},\n          {{Direction<3>::upper_zeta(), Direction<3>::upper_eta()}},\n          {{Direction<3>::upper_zeta(), Direction<3>::upper_eta()}},\n          {{Direction<3>::upper_zeta(), Direction<3>::upper_eta()}}};\n  const auto domain = rectilinear_domain<3>(\n      Index<3>{3, 3, 3},\n      std::array<std::vector<double>, 3>{\n          {{0.0, 1.0, 2.0, 3.0}, {0.0, 1.0, 2.0, 3.0}, {0.0, 1.0, 2.0, 3.0}}},\n      {Index<3>{1, 1, 1}}, {}, std::array<bool, 3>{{true, false, false}}, {});\n\n  // Issue 1018 describes a memory bug that is caused by incorrect indexing\n  // in the function `maps_for_rectilinear_domains` called through the function\n  // `rectilinear_domain`. When called through `rectilinear_domain`, the empty\n  // `orientations_of_all_blocks` vector passed to `rectilinear_domain`\n  // triggers the construction of a vector filled with default-constructed\n  // `OrientationMaps` which is then passed to `maps_for_rectilinear_domains`.\n  // In `maps_for_rectilinear_domains` the non-empty vector of `OrientationMaps`\n  // is then indexed into using `block_orientation_index`.\n  // The maps created in this fashion are then compared to the maps created by\n  // calling the `maps_for_rectilinear_domains` directly, with an empty vector\n  // of `OrientationMaps`. In this case no indexing is done, so if an incorrect\n  // map is created via incorrect indexing, the maps created by the two\n  // function calls will differ.\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 3>>>\n      expected_coordinate_maps = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<3>{3, 3, 3},\n          std::array<std::vector<double>, 3>{{{0.0, 1.0, 2.0, 3.0},\n                                              {0.0, 1.0, 2.0, 3.0},\n                                              {0.0, 1.0, 2.0, 3.0}}},\n          {Index<3>{1, 1, 1}}, {});\n\n  for (size_t i = 0; i < domain.blocks().size(); i++) {\n    CAPTURE(i);\n    CHECK(domain.blocks()[i].external_boundaries() ==\n          expected_external_boundaries[i]);\n    CHECK(domain.blocks()[i].stationary_map() == *expected_coordinate_maps[i]);\n  }\n}\n\nvoid test_set_cartesian_periodic_boundaries_2() {\n  const auto rotation = OrientationMap<2>{std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}};\n  auto orientations_of_all_blocks =\n      std::vector<OrientationMap<2>>{4, OrientationMap<2>::create_aligned()};\n  orientations_of_all_blocks[0] = rotation;\n  const std::vector<std::unordered_set<Direction<2>>>\n      expected_external_boundaries{{{Direction<2>::upper_xi()}},\n                                   {{Direction<2>::lower_eta()}},\n                                   {{Direction<2>::upper_eta()}},\n                                   {{Direction<2>::upper_eta()}}};\n\n  const auto domain = rectilinear_domain<2>(\n      Index<2>{2, 2},\n      std::array<std::vector<double>, 2>{{{0.0, 1.0, 2.0}, {0.0, 1.0, 2.0}}},\n      {}, orientations_of_all_blocks, std::array<bool, 2>{{true, false}}, {},\n      false);\n\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 2>>>\n      expected_coordinate_maps = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<2>{2, 2},\n          std::array<std::vector<double>, 2>{\n              {{0.0, 1.0, 2.0}, {0.0, 1.0, 2.0}}},\n          {}, orientations_of_all_blocks, false);\n\n  for (size_t i = 0; i < domain.blocks().size(); i++) {\n    CAPTURE(i);\n    CHECK(domain.blocks()[i].external_boundaries() ==\n          expected_external_boundaries[i]);\n    CHECK(domain.blocks()[i].stationary_map() == *expected_coordinate_maps[i]);\n  }\n}\n\nvoid test_set_cartesian_periodic_boundaries_3() {\n  const OrientationMap<2> flipped{std::array<Direction<2>, 2>{\n      {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}};\n  const OrientationMap<2> quarter_turn_cw{std::array<Direction<2>, 2>{\n      {Direction<2>::upper_eta(), Direction<2>::lower_xi()}}};\n  const OrientationMap<2> quarter_turn_ccw{std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}};\n  auto orientations_of_all_blocks =\n      std::vector<OrientationMap<2>>{4, OrientationMap<2>::create_aligned()};\n  orientations_of_all_blocks[1] = flipped;\n  orientations_of_all_blocks[2] = quarter_turn_cw;\n  orientations_of_all_blocks[3] = quarter_turn_ccw;\n  const std::vector<std::unordered_set<Direction<2>>>\n      expected_external_boundaries{{}, {}, {}, {}};\n\n  const auto domain = rectilinear_domain<2>(\n      Index<2>{2, 2},\n      std::array<std::vector<double>, 2>{{{0.0, 1.0, 2.0}, {0.0, 1.0, 2.0}}},\n      {}, orientations_of_all_blocks, std::array<bool, 2>{{true, true}}, {},\n      false);\n\n  const std::vector<DirectionMap<2, BlockNeighbors<2>>>\n      expected_block_neighbors{\n          {{Direction<2>::upper_xi(), {1, flipped}},\n           {Direction<2>::upper_eta(), {2, quarter_turn_cw}},\n           {Direction<2>::lower_xi(), {1, flipped}},\n           {Direction<2>::lower_eta(), {2, quarter_turn_cw}}},\n          {{Direction<2>::upper_xi(), {0, flipped}},\n           {Direction<2>::lower_eta(), {3, quarter_turn_cw}},\n           {Direction<2>::lower_xi(), {0, flipped}},\n           {Direction<2>::upper_eta(), {3, quarter_turn_cw}}},\n          {{Direction<2>::upper_xi(), {0, quarter_turn_ccw}},\n           {Direction<2>::upper_eta(), {3, flipped}},\n           {Direction<2>::lower_xi(), {0, quarter_turn_ccw}},\n           {Direction<2>::lower_eta(), {3, flipped}}},\n          {{Direction<2>::lower_xi(), {1, quarter_turn_ccw}},\n           {Direction<2>::upper_eta(), {2, flipped}},\n           {Direction<2>::upper_xi(), {1, quarter_turn_ccw}},\n           {Direction<2>::lower_eta(), {2, flipped}}}};\n  const std::vector<std::unique_ptr<\n      CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, 2>>>\n      expected_coordinate_maps = maps_for_rectilinear_domains<Frame::Inertial>(\n          Index<2>{2, 2},\n          std::array<std::vector<double>, 2>{\n              {{0.0, 1.0, 2.0}, {0.0, 1.0, 2.0}}},\n          {}, orientations_of_all_blocks, false);\n\n  for (size_t i = 0; i < domain.blocks().size(); i++) {\n    CAPTURE(i);\n    CHECK(domain.blocks()[i].external_boundaries() ==\n          expected_external_boundaries[i]);\n    CHECK(domain.blocks()[i].stationary_map() == *expected_coordinate_maps[i]);\n    CHECK(domain.blocks()[i].neighbors() == expected_block_neighbors[i]);\n  }\n}\n\nvoid test_which_wedges() {\n  CHECK(get_output(ShellWedges::All) == \"All\");\n  CHECK(get_output(ShellWedges::FourOnEquator) == \"FourOnEquator\");\n  CHECK(get_output(ShellWedges::OneAlongMinusX) == \"OneAlongMinusX\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.DomainHelpers\", \"[Domain][Unit]\") {\n  test_periodic_same_block();\n  test_periodic_different_blocks();\n  test_wedge_map_generation();\n  test_wedge_errors();\n  test_all_frustum_directions();\n  test_frustrum_errors();\n  test_shell_graph();\n  test_sphere_graph();\n  test_bbh_corners();\n  test_nsbh_corners();\n  test_bhns_corners();\n  test_bns_corners();\n  test_volume_corner_iterator();\n  test_face_corner_iterator();\n  test_indices_for_rectilinear_domains();\n  test_block_names_for_rectilinear_domains();\n  test_corners_for_rectilinear_domains();\n  test_discrete_rotation_corner_numbers();\n  test_maps_for_rectilinear_domains();\n  test_set_cartesian_periodic_boundaries_1();\n  test_set_cartesian_periodic_boundaries_2();\n  test_set_cartesian_periodic_boundaries_3();\n  test_which_wedges();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_DomainTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\n\ntemplate <typename DataType, size_t SpatialDim>\ntnsr::II<DataType, SpatialDim> random_inv_spatial_metric(\n    const gsl::not_null<std::mt19937*> generator,\n    const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-0.05, 0.05);\n  auto inv_spatial_metric =\n      make_with_random_values<tnsr::II<DataType, SpatialDim>>(\n          generator, make_not_null(&distribution), used_for_size);\n  for (size_t d = 0; d < SpatialDim; ++d) {\n    inv_spatial_metric.get(d, d) += 1.0;\n  }\n  return inv_spatial_metric;\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_euclidean_basis_vector(const DataType& used_for_size) {\n  for (const auto& direction : Direction<SpatialDim>::all_directions()) {\n    auto expected =\n        make_with_value<tnsr::i<DataType, SpatialDim>>(used_for_size, 0.0);\n    expected.get(direction.axis()) =\n        make_with_value<DataType>(used_for_size, direction.sign());\n\n    CHECK_ITERABLE_APPROX((euclidean_basis_vector(direction, used_for_size)),\n                          std::move(expected));\n  }\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_unit_basis_form(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  const auto inv_spatial_metric =\n      random_inv_spatial_metric<DataType, SpatialDim>(make_not_null(&generator),\n                                                      used_for_size);\n  for (const auto& direction : Direction<SpatialDim>::all_directions()) {\n    const auto basis_form = unit_basis_form(direction, inv_spatial_metric);\n    auto expected = euclidean_basis_vector(direction, used_for_size);\n    const DataType norm = get(magnitude(expected, inv_spatial_metric));\n    for (size_t d = 0; d < SpatialDim; ++d) {\n      expected.get(d) /= norm;\n    }\n    CHECK_ITERABLE_APPROX(basis_form, expected);\n    CHECK_ITERABLE_APPROX(get(magnitude(expected, inv_spatial_metric)),\n                          make_with_value<DataType>(used_for_size, 1.0));\n  }\n}\n\n}  //  namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.TestHelpers.BasisVector\", \"[Unit][Domain]\") {\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_euclidean_basis_vector, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_unit_basis_form, (1, 2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_ElementDistribution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementDistribution.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Structure/ZCurve.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n// Test the weighting done by `domain::get_element_costs` for a uniform cost\n// function\nvoid test_uniform_cost_function() {\n  // AlignedLattice with differently-refined Elements\n  const auto domain_creator = domain::creators::AlignedLattice<2>(\n      {{{{70, 71, 72, 73}}, {{90, 92, 95, 99}}}}, {{2, 5}}, {{3, 3}},\n      {{{{{1, 0}}, {{3, 2}}, {{3, 5}}}, {{{2, 1}}, {{3, 3}}, {{4, 6}}}}},\n      {{{{{1, 0}}, {{3, 2}}, {{4, 5}}}, {{{2, 1}}, {{3, 3}}, {{6, 7}}}}}, {});\n\n  const auto domain = domain_creator.create_domain();\n  const auto& blocks = domain.blocks();\n\n  const auto costs = domain::get_element_costs(\n      blocks, domain_creator.initial_refinement_levels(),\n      domain_creator.initial_extents(), domain::ElementWeight::Uniform,\n      std::nullopt, std::nullopt);\n\n  for (const auto& element_id_and_cost : costs) {\n    CHECK(element_id_and_cost.second == 1.0);\n  }\n}\n\n// Test the weighting done by `domain::get_element_costs` for weighted cost\n// functions\nvoid test_weighted_cost_function(const domain::ElementWeight element_weight) {\n  const auto domain_creator1 = domain::creators::AlignedLattice<3>(\n      {{{{0.0, 1.0, 2.0}}, {{0.0, 1.0}}, {{0.0, 1.0}}}}, {{2, 1, 0}},\n      {{4, 4, 4}}, {}, {}, {});\n  const auto domain1 = domain_creator1.create_domain();\n  const auto& blocks1 = domain1.blocks();\n\n  // Block size and grid points are the same as blocks in `domain1`, but\n  // refinement levels are different\n  const auto domain_creator2 = domain::creators::AlignedLattice<3>(\n      {{{{0.0, 1.0}}, {{0.0, 1.0}}, {{0.0, 1.0}}}}, {{2, 3, 2}}, {{4, 4, 4}},\n      {}, {}, {});\n  const auto domain2 = domain_creator2.create_domain();\n  const auto& blocks2 = domain2.blocks();\n\n  // Block size and refinement levels are the same as blocks in `domain1`, but\n  // grid points are different\n  const auto domain_creator3 = domain::creators::AlignedLattice<3>(\n      {{{{0.0, 1.0}}, {{0.0, 1.0}}, {{0.0, 1.0}}}}, {{2, 1, 0}}, {{4, 3, 2}},\n      {}, {}, {});\n  const auto domain3 = domain_creator3.create_domain();\n  const auto& blocks3 = domain2.blocks();\n\n  const auto costs1 = domain::get_element_costs(\n      blocks1, domain_creator1.initial_refinement_levels(),\n      domain_creator1.initial_extents(), element_weight,\n      Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n\n  const auto costs2 = domain::get_element_costs(\n      blocks2, domain_creator2.initial_refinement_levels(),\n      domain_creator2.initial_extents(), element_weight,\n      Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n\n  const auto costs3 = domain::get_element_costs(\n      blocks3, domain_creator3.initial_refinement_levels(),\n      domain_creator3.initial_extents(), element_weight,\n      Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n\n  // check that all elements in each domain have the same cost\n\n  auto elemental_cost_it1 = costs1.begin();\n  const double elemental_cost1 = elemental_cost_it1->second;\n  elemental_cost_it1++;\n  while (elemental_cost_it1 != costs1.end()) {\n    CHECK(elemental_cost1 == approx(elemental_cost_it1->second));\n    elemental_cost_it1++;\n  }\n\n  auto elemental_cost_it2 = costs2.begin();\n  const double elemental_cost2 = elemental_cost_it2->second;\n  elemental_cost_it2++;\n  while (elemental_cost_it2 != costs1.end()) {\n    CHECK(elemental_cost2 == approx(elemental_cost_it2->second));\n    elemental_cost_it2++;\n  }\n\n  auto elemental_cost_it3 = costs3.begin();\n  const double elemental_cost3 = elemental_cost_it3->second;\n  elemental_cost_it3++;\n  while (elemental_cost_it3 != costs3.end()) {\n    CHECK(elemental_cost3 == approx(elemental_cost_it3->second));\n    elemental_cost_it3++;\n  }\n\n  if (element_weight == domain::ElementWeight::NumGridPoints) {\n    // check that varying refinement doesn't affect the cost\n    CHECK(elemental_cost2 == elemental_cost1);\n  } else {\n    // element_weight == domain::ElementWeight::NumGridPointsAndGridSpacing\n\n    // The highest refinement for the first test domain is 2 while the highest\n    // refinement for the second test domain is 3, grid points held constant.\n    // Since the minimum grid spacing of the second domain is half the minimum\n    // grid spacing of the first and since\n    // elemental cost = (# of grid points) / sqrt(min grid spacing), the\n    // elemental cost of the second domain should be a factor of sqrt(2) the\n    // cost.\n    CHECK(elemental_cost2 == approx(sqrt(2.0) * elemental_cost1));\n  }\n\n  // The minimum grid spacing for the first and third domain are equal, but the\n  // number of grid points in an element in the first is 64 while the number of\n  // grid points in an element in the third is 24. Since elemental cost for\n  // either domain::Elementweight::NumGridPoints or\n  // domain::Elementweight::NumGridPointsAndGridSpacing should only scale by\n  // the # of grid points, the elemental cost of the third domain should be a\n  // factor of 24/64 = 3/8 the cost of the first domain.\n  CHECK(elemental_cost3 == elemental_cost1 * 3.0 / 8.0);\n}\n\n// Test the processor distribution logic of the\n// `domain::BlockZCurveProcDistribution` constructor for an unweighted element\n// distribution\ntemplate <size_t Dim>\nvoid test_uniform_element_distribution_construction(\n    const DomainCreator<Dim>& domain_creator,\n    const size_t number_of_procs_with_elements,\n    const std::unordered_set<size_t>& global_procs_to_ignore = {}) {\n  const auto domain = domain_creator.create_domain();\n  const auto& blocks = domain.blocks();\n  const auto initial_refinement_levels =\n      domain_creator.initial_refinement_levels();\n  const auto initial_extents = domain_creator.initial_extents();\n\n  const size_t num_blocks = blocks.size();\n  size_t num_elements = 0;\n  std::vector<size_t> num_elements_by_block(num_blocks, 0);\n  for (size_t i = 0; i < num_blocks; i++) {\n    size_t num_elements_this_block =\n        two_to_the(gsl::at(initial_refinement_levels[i], 0));\n    for (size_t j = 1; j < Dim; j++) {\n      num_elements_this_block *=\n          two_to_the(gsl::at(initial_refinement_levels[i], j));\n    }\n    num_elements_by_block[i] = num_elements_this_block;\n    num_elements += num_elements_this_block;\n  }\n\n  const auto costs = domain::get_element_costs(\n      blocks, initial_refinement_levels, initial_extents,\n      domain::ElementWeight::Uniform, std::nullopt, std::nullopt);\n\n  const domain::BlockZCurveProcDistribution<Dim> element_distribution(\n      costs, number_of_procs_with_elements, blocks, initial_refinement_levels,\n      initial_extents, global_procs_to_ignore);\n  const auto proc_map = element_distribution.block_element_distribution();\n\n  const size_t total_procs =\n      number_of_procs_with_elements + global_procs_to_ignore.size();\n  std::vector<size_t> num_elements_by_proc(total_procs, 0);\n  std::vector<size_t> actual_num_elements_by_block_in_dist(num_blocks, 0);\n  size_t actual_num_elements_in_dist = 0;\n  for (size_t block_number = 0; block_number < proc_map.size();\n       block_number++) {\n    for (const auto& proc_allowance : proc_map[block_number]) {\n      const size_t proc_number = proc_allowance.first;\n      const size_t element_allowance = proc_allowance.second;\n      num_elements_by_proc[proc_number] += element_allowance;\n      actual_num_elements_by_block_in_dist[block_number] += element_allowance;\n    }\n    // check that the number of elements in this block accounted for in the\n    // distribution matches the expected number of total elements for this block\n    CHECK(actual_num_elements_by_block_in_dist[block_number] ==\n          num_elements_by_block[block_number]);\n    actual_num_elements_in_dist +=\n        actual_num_elements_by_block_in_dist[block_number];\n  }\n  // check that the number of elements accounted for in the distribution matches\n  // the expected number of total elements\n  CHECK(actual_num_elements_in_dist == num_elements);\n\n  size_t lowest_proc_with_elements = 0;\n  while (global_procs_to_ignore.count(lowest_proc_with_elements) == 1) {\n    lowest_proc_with_elements++;\n  }\n  const size_t num_elements_on_lowest_proc =\n      num_elements_by_proc[lowest_proc_with_elements];\n\n  size_t num_elements_so_far = num_elements_on_lowest_proc;\n  size_t proc_num = lowest_proc_with_elements + 1;\n  while (proc_num < total_procs and num_elements_so_far < num_elements) {\n    const size_t num_elements_this_proc = num_elements_by_proc[proc_num];\n    if (global_procs_to_ignore.count(proc_num) == 1) {\n      CHECK(num_elements_this_proc == 0);\n    } else {\n      // check that the distribution is near-uniform\n      CHECK((num_elements_this_proc == num_elements_on_lowest_proc or\n             num_elements_this_proc == num_elements_on_lowest_proc + 1 or\n             num_elements_this_proc == num_elements_on_lowest_proc - 1));\n    }\n    num_elements_so_far += num_elements_this_proc;\n    proc_num++;\n  }\n\n  // check that any remainder of processors we didn't need do indeed have 0\n  // elements assigned to them\n  while (proc_num < total_procs) {\n    CHECK(num_elements_by_proc[proc_num] == 0);\n    proc_num++;\n  }\n}\n\n// Test the processor distribution logic of the\n// `domain::BlockZCurveProcDistribution` constructor for weighted element\n// distributions\ntemplate <size_t Dim>\nvoid test_weighted_element_distribution_construction(\n    const domain::ElementWeight element_weight,\n    const DomainCreator<Dim>& domain_creator,\n    const size_t number_of_procs_with_elements,\n    const std::unordered_set<size_t>& global_procs_to_ignore = {}) {\n  const auto domain = domain_creator.create_domain();\n  const auto& blocks = domain.blocks();\n  const auto initial_refinement_levels =\n      domain_creator.initial_refinement_levels();\n  const auto initial_extents = domain_creator.initial_extents();\n\n  const size_t num_blocks = blocks.size();\n  size_t num_elements = 0;\n  std::vector<size_t> num_elements_by_block(num_blocks, 0);\n  for (size_t i = 0; i < num_blocks; i++) {\n    size_t num_elements_this_block =\n        two_to_the(gsl::at(initial_refinement_levels[i], 0));\n    for (size_t j = 1; j < Dim; j++) {\n      num_elements_this_block *=\n          two_to_the(gsl::at(initial_refinement_levels[i], j));\n    }\n    num_elements_by_block[i] = num_elements_this_block;\n    num_elements += num_elements_this_block;\n  }\n\n  const auto costs = domain::get_element_costs(\n      blocks, initial_refinement_levels, initial_extents, element_weight,\n      Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n\n  const domain::BlockZCurveProcDistribution<Dim> element_distribution(\n      costs, number_of_procs_with_elements, blocks, initial_refinement_levels,\n      initial_extents, global_procs_to_ignore);\n  const auto proc_map = element_distribution.block_element_distribution();\n\n  const size_t total_procs =\n      number_of_procs_with_elements + global_procs_to_ignore.size();\n  std::vector<size_t> num_elements_by_proc(total_procs, 0);\n  std::vector<size_t> actual_num_elements_by_block_in_dist(num_blocks, 0);\n  size_t actual_num_elements_in_dist = 0;\n  for (size_t block_number = 0; block_number < proc_map.size();\n       block_number++) {\n    for (const auto& proc_allowance : proc_map[block_number]) {\n      const size_t proc_number = proc_allowance.first;\n      const size_t element_allowance = proc_allowance.second;\n      num_elements_by_proc[proc_number] += element_allowance;\n      actual_num_elements_by_block_in_dist[block_number] += element_allowance;\n    }\n    // check that the number of elements in this block accounted for in the\n    // distribution matches the expected number of total elements for this block\n    CHECK(actual_num_elements_by_block_in_dist[block_number] ==\n          num_elements_by_block[block_number]);\n    actual_num_elements_in_dist +=\n        actual_num_elements_by_block_in_dist[block_number];\n  }\n  // check that the number of elements accounted for in the distribution matches\n  // the expected number of total elements\n  CHECK(actual_num_elements_in_dist == num_elements);\n\n  std::vector<std::vector<ElementId<Dim>>> initial_element_ids_by_block(\n      num_blocks);\n  for (size_t i = 0; i < num_blocks; i++) {\n    const size_t num_elements_this_block = two_to_the(alg::accumulate(\n        initial_refinement_levels[i], 0_st, std::plus<size_t>()));\n    initial_element_ids_by_block[i].reserve(num_elements_this_block);\n    initial_element_ids_by_block[i] =\n        initial_element_ids(blocks[i].id(), initial_refinement_levels[i]);\n    alg::sort(initial_element_ids_by_block[i],\n              [](const ElementId<Dim>& lhs, const ElementId<Dim>& rhs) {\n                return domain::z_curve_index(lhs) < domain::z_curve_index(rhs);\n              });\n  }\n\n  double total_cost = 0.0;\n  for (const auto& element_id_and_cost : costs) {\n    total_cost += element_id_and_cost.second;\n  }\n\n  // one flattened vector instead of vectors by Block\n  std::vector<double> costs_flattened(num_elements);\n  size_t cost_index = 0;\n  for (size_t i = 0; i < num_blocks; i++) {\n    const size_t num_elements_this_block =\n        initial_element_ids_by_block[i].size();\n    for (size_t j = 0; j < num_elements_this_block; j++) {\n      const ElementId<Dim>& element_id = initial_element_ids_by_block[i][j];\n      costs_flattened[cost_index] = costs.at(element_id);\n      cost_index++;\n    }\n  }\n\n  cost_index = 0;\n  double cost_remaining = total_cost;\n  size_t procs_skipped = 0;\n  size_t proc_num = 0;\n  // check that we distributed the right number of elements to each proc based\n  // on the sum of their costs in Z-curve index order\n  while (proc_num < total_procs) {\n    if (global_procs_to_ignore.count(proc_num)) {\n      procs_skipped++;\n      proc_num++;\n      continue;\n    }\n\n    if (cost_index < num_elements) {\n      // if we haven't accounted for all elements yet, we should still have cost\n      // left to account for\n      CHECK(cost_remaining <= total_cost);\n    } else {\n      // if we've already accounted for all the elements, we shouldn't have any\n      // cost left to account for, and it's the case that more procs were\n      // requested than could be used, e.g. in the case of less elements than\n      // procs\n      Approx custom_approx = Approx::custom().epsilon(1.0e-9).scale(1.0);\n      CHECK(cost_remaining == custom_approx(0.0));\n      break;\n    }\n\n    // the average cost per proc that we're aiming for\n    const double target_proc_cost =\n        cost_remaining /\n        (number_of_procs_with_elements - proc_num + procs_skipped);\n\n    // total cost on the processor before adding the cost of the final element\n    // assigned to this proc\n    double proc_cost_without_final_element = 0.0;\n    const size_t num_elements_this_proc = num_elements_by_proc[proc_num];\n    // add up costs of all elements but the final one to add\n    for (size_t j = 0; j < num_elements_this_proc - 1; j++) {\n      const double this_cost = costs_flattened[cost_index + j];\n      proc_cost_without_final_element += this_cost;\n    }\n\n    // the cost of all of the elements assigned to this proc\n    const double proc_cost_with_final_element =\n        proc_cost_without_final_element +\n        costs_flattened[cost_index + num_elements_this_proc - 1];\n\n    const double diff_without_final_element =\n        abs(proc_cost_without_final_element - target_proc_cost);\n\n    const double diff_with_final_element =\n        abs(proc_cost_with_final_element - target_proc_cost);\n\n    // if the elements assigned to this proc have a cost that is over the target\n    // cost per proc, make sure that either it's because only one element is\n    // being assigned to the proc or this cost is closer to the target than if\n    // we omitted the final element, i.e. check that it's better to keep the\n    // final element than to not\n    if (proc_cost_with_final_element > target_proc_cost) {\n      CHECK((num_elements_this_proc == 1 or\n             diff_with_final_element == approx(diff_without_final_element) or\n             diff_with_final_element < diff_without_final_element));\n    }\n\n    if (cost_index + num_elements_this_proc < num_elements) {\n      // total cost on the processor if we were to add the cost of the next\n      // element (one additional than the number chosen)\n      const double proc_cost_with_extra_element =\n          proc_cost_with_final_element +\n          costs_flattened[cost_index + num_elements_this_proc];\n      const double diff_with_extra_element =\n          abs(proc_cost_with_extra_element - target_proc_cost);\n\n      // if it appears better to add one more element, check that it's because\n      // the distance from the target cost with or without the additional\n      // element is about the same\n      if (diff_with_extra_element < diff_with_final_element) {\n        Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n        CHECK(diff_with_extra_element ==\n              custom_approx(diff_with_final_element));\n      }\n    }\n\n    cost_index += num_elements_this_proc;\n    cost_remaining -= proc_cost_with_final_element;\n    proc_num++;\n  }\n\n  // check that any remainder of processors we didn't need do indeed have 0\n  // elements assigned to them\n  while (proc_num < total_procs) {\n    CHECK(num_elements_by_proc[proc_num] == 0);\n    proc_num++;\n  }\n}\n\n// Test the retrieval of the assigned processor that is done by\n// `domain::BlockZCurveProcDistribution::get_proc_for_element`\ntemplate <size_t Dim>\nvoid test_proc_retrieval(\n    const domain::ElementWeight element_weight,\n    const DomainCreator<Dim>& domain_creator,\n    const size_t number_of_procs_with_elements,\n    const std::unordered_set<size_t>& global_procs_to_ignore = {}) {\n  const auto domain = domain_creator.create_domain();\n  const auto& blocks = domain.blocks();\n  const auto initial_refinement_levels =\n      domain_creator.initial_refinement_levels();\n  const auto initial_extents = domain_creator.initial_extents();\n\n  const size_t num_blocks = blocks.size();\n  std::vector<std::vector<ElementId<Dim>>> element_ids_in_z_curve_order(\n      num_blocks);\n  for (size_t i = 0; i < num_blocks; i++) {\n    element_ids_in_z_curve_order[i] =\n        initial_element_ids(i, gsl::at(initial_refinement_levels, i), 0);\n    alg::sort(element_ids_in_z_curve_order[i],\n              [](const ElementId<Dim>& lhs, const ElementId<Dim>& rhs) {\n                return domain::z_curve_index(lhs) < domain::z_curve_index(rhs);\n              });\n  }\n\n  const auto costs = domain::get_element_costs(\n      blocks, initial_refinement_levels, initial_extents, element_weight,\n      Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n\n  const domain::BlockZCurveProcDistribution<Dim> element_distribution(\n      costs, number_of_procs_with_elements, blocks, initial_refinement_levels,\n      initial_extents, global_procs_to_ignore);\n  const auto proc_map = element_distribution.block_element_distribution();\n\n  const size_t total_number_of_procs =\n      number_of_procs_with_elements + global_procs_to_ignore.size();\n\n  // whether or not we've assigned elements to a proc\n  std::vector<bool> proc_hit(total_number_of_procs, false);\n\n  size_t highest_proc_assigned = 0;\n  for (size_t i = 0; i < num_blocks; i++) {\n    size_t element_index = 0;\n    const std::vector<std::pair<size_t, size_t>>& proc_map_this_block =\n        proc_map[i];\n    const size_t num_procs_this_block = proc_map_this_block.size();\n\n    for (size_t j = 0; j < num_procs_this_block; j++) {\n      const size_t expected_proc = proc_map_this_block[j].first;\n      const size_t proc_allowance = proc_map_this_block[j].second;\n\n      for (size_t k = 0; k < proc_allowance; k++) {\n        const size_t actual_proc = element_distribution.get_proc_for_element(\n            element_ids_in_z_curve_order[i][element_index]);\n        // check that the correct processor is returned for the `ElementId`\n        CHECK(actual_proc == expected_proc);\n        proc_hit[actual_proc] = true;\n        if (highest_proc_assigned < actual_proc) {\n          highest_proc_assigned = actual_proc;\n        }\n      }\n      element_index += proc_allowance;\n    }\n  }\n\n  // check that ignored procs were indeed skipped and that all other procs\n  // up to the highest one assigned were hit\n  for (size_t i = 0; i < highest_proc_assigned + 1; i++) {\n    if (global_procs_to_ignore.count(i) == 0) {\n      CHECK(proc_hit[i]);\n    } else {\n      CHECK(not proc_hit[i]);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.ElementDistribution\", \"[Domain][Unit]\") {\n  // Test cost functions\n  test_uniform_cost_function();\n  test_weighted_cost_function(domain::ElementWeight::NumGridPoints);\n  test_weighted_cost_function(\n      domain::ElementWeight::NumGridPointsAndGridSpacing);\n\n  // Inputs for testing `BlockZCurveProcDistribution`\n\n  // 1D, single block\n  const auto lattice_1d = domain::creators::AlignedLattice<1>(\n      {{{{0.0, 1.0}}}}, {{4}}, {{6}}, {}, {}, {});\n  // 2D\n  const auto lattice_2d = domain::creators::AlignedLattice<2>(\n      {{{{0.0, 0.3}}, {{0.0, 0.8, 2.5, 4.9}}}}, {{2, 3}}, {{4, 5}}, {}, {}, {});\n  // 3D\n  const auto lattice_3d = domain::creators::AlignedLattice<3>(\n      {{{{0.0, 0.6}}, {{0.0, 0.4, 0.7}}, {{0.0, 0.3}}}}, {{2, 1, 3}},\n      {{5, 4, 5}}, {}, {}, {});\n\n  // Test element distribution construction logic\n\n  // uniform distribution, single proc, single block\n  test_uniform_element_distribution_construction(lattice_1d, 1);\n  // uniform distribution, multiple procs, multiple blocks\n  test_uniform_element_distribution_construction(\n      lattice_2d, 20, std::unordered_set<size_t>{4, 20});\n  // weighted distribution, multiple procs\n  test_weighted_element_distribution_construction(\n      domain::ElementWeight::NumGridPointsAndGridSpacing, lattice_3d, 22,\n      std::unordered_set<size_t>{3, 4});\n  // weighted distribution, more procs than elements to distribute\n  test_weighted_element_distribution_construction(\n      domain::ElementWeight::NumGridPoints, lattice_2d, 100,\n      std::unordered_set<size_t>{0, 9});\n\n  // Test processor retrieval with ignored processors\n  test_proc_retrieval(domain::ElementWeight::NumGridPointsAndGridSpacing,\n                      lattice_2d, 19, std::unordered_set<size_t>{0, 8, 9, 21});\n  // Test processor retrieval when there are more processors requested than\n  // `Element`s in the domain\n  test_proc_retrieval(domain::ElementWeight::NumGridPointsAndGridSpacing,\n                      lattice_2d, 100, std::unordered_set<size_t>{17});\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_ElementMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace domain {\nnamespace {\nusing DV = DataVector;\nusing Affine = CoordinateMaps::Affine;\n\ntemplate <size_t Dim, typename S, typename T, typename U>\nvoid test_element_impl(\n    bool test_inverse, const ElementId<Dim>& element_id, const S& affine_map,\n    const T& first_map, const U& second_map,\n    const tnsr::I<double, Dim, Frame::ElementLogical>& logical_point_double,\n    const tnsr::I<DV, Dim, Frame::ElementLogical>& logical_point_dv) {\n  PUPable_reg(\n      SINGLE_ARG(CoordinateMap<Frame::BlockLogical, Frame::Inertial, T, U>));\n  const auto composed_map =\n      make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          affine_map, first_map, second_map);\n\n  ElementMap element_map{\n      element_id,\n      make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          first_map, second_map)};\n  ElementMap element_map_deserialized = serialize_and_deserialize(element_map);\n\n  CHECK(element_map(logical_point_dv) == composed_map(logical_point_dv));\n  CHECK(element_map(logical_point_double) ==\n        composed_map(logical_point_double));\n  CHECK(element_map_deserialized(logical_point_dv) ==\n        composed_map(logical_point_dv));\n  CHECK(element_map_deserialized(logical_point_double) ==\n        composed_map(logical_point_double));\n\n  const tnsr::I<double, Dim, Frame::Inertial> inertial_point_double =\n      composed_map(logical_point_double);\n  const tnsr::I<DV, Dim, Frame::Inertial> inertial_point_dv =\n      composed_map(logical_point_dv);\n\n  if (test_inverse) {\n    CHECK(element_map.inverse(inertial_point_double) ==\n          composed_map.inverse(inertial_point_double).value());\n    CHECK(element_map_deserialized.inverse(inertial_point_double) ==\n          composed_map.inverse(inertial_point_double).value());\n  }\n\n  const auto composed_map_inv_jacobian_dv =\n      composed_map.inv_jacobian(logical_point_dv);\n  CHECK_ITERABLE_APPROX(element_map.inv_jacobian(logical_point_dv),\n                        composed_map_inv_jacobian_dv);\n  const auto composed_map_inv_jacobian_double =\n      composed_map.inv_jacobian(logical_point_double);\n  CHECK_ITERABLE_APPROX(element_map.inv_jacobian(logical_point_double),\n                        composed_map_inv_jacobian_double);\n  CHECK_ITERABLE_APPROX(element_map_deserialized.inv_jacobian(logical_point_dv),\n                        composed_map_inv_jacobian_dv);\n  CHECK_ITERABLE_APPROX(\n      element_map_deserialized.inv_jacobian(logical_point_double),\n      composed_map_inv_jacobian_double);\n\n#ifdef SPECTRE_AUTODIFF\n  CHECK_ITERABLE_APPROX(\n      element_map.inv_hessian(logical_point_dv),\n      composed_map.inv_hessian(logical_point_dv, composed_map_inv_jacobian_dv));\n  CHECK_ITERABLE_APPROX(\n      element_map.inv_hessian(logical_point_double),\n      composed_map.inv_hessian(logical_point_double,\n                               composed_map_inv_jacobian_double));\n  CHECK_ITERABLE_APPROX(\n      element_map_deserialized.inv_hessian(logical_point_dv),\n      composed_map.inv_hessian(logical_point_dv, composed_map_inv_jacobian_dv));\n  CHECK_ITERABLE_APPROX(\n      element_map_deserialized.inv_hessian(logical_point_double),\n      composed_map.inv_hessian(logical_point_double,\n                               composed_map_inv_jacobian_double));\n#endif  // SPECTRE_AUTODIFF\n\n  CHECK_ITERABLE_APPROX(element_map.jacobian(logical_point_dv),\n                        composed_map.jacobian(logical_point_dv));\n  CHECK_ITERABLE_APPROX(element_map.jacobian(logical_point_double),\n                        composed_map.jacobian(logical_point_double));\n  CHECK_ITERABLE_APPROX(element_map_deserialized.jacobian(logical_point_dv),\n                        composed_map.jacobian(logical_point_dv));\n  CHECK_ITERABLE_APPROX(element_map_deserialized.jacobian(logical_point_double),\n                        composed_map.jacobian(logical_point_double));\n\n  CHECK(element_map.block_map() ==\n        *(make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            first_map, second_map)));\n}\n\ntemplate <size_t Dim>\nvoid test_element_map();\n\ntemplate <>\nvoid test_element_map<1>() {\n  auto segment_ids = std::array<SegmentId, 1>({{SegmentId(2, 3)}});\n  ElementId<1> element_id(0, segment_ids);\n  const tnsr::I<double, 1, Frame::ElementLogical> logical_point_double{0.0};\n  const tnsr::I<DV, 1, Frame::ElementLogical> logical_point_dv(\n      DV{-1.0, -0.5, 0.0, 0.5, 1.0});\n\n  const Affine affine_map{-1.0, 1.0, 0.5, 1.0};\n  const Affine first_map{-1.0, 1.0, 2.0, 8.0};\n\n  // Aligned maps\n  test_element_impl(true, element_id, affine_map, first_map,\n                    Affine{2.0, 8.0, -2.0, -1.0}, logical_point_double,\n                    logical_point_dv);\n\n  // Flip axis in second map\n  test_element_impl(true, element_id, affine_map, first_map,\n                    Affine{2.0, 8.0, 2.0, -1.0}, logical_point_double,\n                    logical_point_dv);\n}\n\ntemplate <>\nvoid test_element_map<2>() {\n  using Rotate = CoordinateMaps::Rotation<2>;\n  using Wedge2D = CoordinateMaps::Wedge<2>;\n\n  auto segment_ids =\n      std::array<SegmentId, 2>({{SegmentId(2, 3), SegmentId(1, 0)}});\n  ElementId<2> element_id(0, segment_ids);\n\n  const tnsr::I<double, 2, Frame::ElementLogical> logical_point_double(\n      std::array<double, 2>{{-0.5, 0.5}});\n  const tnsr::I<DV, 2, Frame::ElementLogical> logical_point_dv(\n      std::array<DV, 2>{\n          {DV{-1.0, 0.5, 0.0, 0.5, 1.0}, DV{-1.0, 0.5, 0.0, 0.5, 1.0}}});\n\n  const CoordinateMaps::ProductOf2Maps<Affine, Affine> affine_map(\n      Affine{-1.0, 1.0, 0.5, 1.0}, Affine{-1.0, 1.0, -1.0, 0.0});\n  const auto first_map = Rotate(2.);\n\n  // Test with two rotations\n  test_element_impl(true, element_id, affine_map, first_map, Rotate(1.8472),\n                    logical_point_double, logical_point_dv);\n\n  // test with a rotation and a wedge\n  test_element_impl(\n      false, element_id, affine_map, first_map,\n      Wedge2D(3., 7., 0.0, 1.0,\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_xi(), Direction<2>::lower_eta()}}},\n              false),\n      logical_point_double, logical_point_dv);\n}\n\ntemplate <>\nvoid test_element_map<3>() {\n  using Rotate = CoordinateMaps::Rotation<3>;\n\n  auto segment_ids = std::array<SegmentId, 3>(\n      {{SegmentId(2, 3), SegmentId(1, 0), SegmentId(2, 1)}});\n  ElementId<3> element_id(0, segment_ids);\n\n  const tnsr::I<double, 3, Frame::ElementLogical> logical_point_double{\n      {{0.78, 0.12, -0.37}}};\n  const tnsr::I<DataVector, 3, Frame::ElementLogical> logical_point_dv{\n      {{DV{-1.0, 0.5, 0.0, 0.5, 1.0}, DV{-1.0, 0.5, 0.0, 0.5, 1.0},\n        DV{-1.0, 0.5, 0.0, 0.5, 1.0}}}};\n\n  const CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine> affine_map(\n      Affine{-1.0, 1.0, 0.5, 1.0}, Affine{-1.0, 1.0, -1.0, 0.0},\n      Affine{-1.0, 1.0, -0.5, 0.0});\n  const auto first_map = Rotate{M_PI_4, M_PI_4, M_PI_2};\n\n  // test with 2 rotations\n  test_element_impl(true, element_id, affine_map, first_map,\n                    Rotate{M_PI_2, M_PI_4, M_PI_4}, logical_point_double,\n                    logical_point_dv);\n\n  // test with rotation and wedge\n  test_element_impl(\n      true, element_id, affine_map, first_map,\n      CoordinateMaps::Wedge<3>{3.0, 7.0, 0.8, 0.9,\n                               OrientationMap<3>::create_aligned(), true},\n      logical_point_double, logical_point_dv);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.ElementMap\", \"[Unit][Domain]\") {\n  test_element_map<1>();\n  test_element_map<2>();\n  test_element_map<3>();\n\n  {\n    INFO(\"Test with Block\");\n    const ElementId<1> element_id{0, {{{2, 3}}}};\n    const Affine block_map{-1.0, 1.0, 2.0, 8.0};\n    const tnsr::I<double, 1, Frame::ElementLogical> xi{{{1.}}};\n    Block<1> block{\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            block_map),\n        0,\n        {}};\n    {\n      INFO(\"Time-independent\");\n      {\n        INFO(\"Grid frame\");\n        ElementMap<1, Frame::Grid> element_map{element_id, block};\n        ElementMap<1, Frame::Grid> expected_element_map{\n            element_id,\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                block_map)};\n        CHECK(element_map(xi) == expected_element_map(xi));\n      }\n      {\n        INFO(\"Inertial frame\");\n        ElementMap<1, Frame::Inertial> element_map{element_id, block};\n        ElementMap<1, Frame::Inertial> expected_element_map{\n            element_id,\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                block_map)};\n        CHECK(element_map(xi) == expected_element_map(xi));\n      }\n    }\n    {\n      INFO(\"Time-dependent\");\n      const domain::CoordinateMaps::TimeDependent::Translation<1>\n          grid_to_inertial_map{\"Translation\"};\n      block.inject_time_dependent_map(\n          make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n              grid_to_inertial_map));\n      {\n        INFO(\"Grid frame\");\n        ElementMap<1, Frame::Grid> element_map{element_id, block};\n        ElementMap<1, Frame::Grid> expected_element_map{\n            element_id,\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                block_map)};\n        CHECK(element_map(xi) == expected_element_map(xi));\n      }\n      {\n        INFO(\"Inertial frame\");\n        ElementMap<1, Frame::Inertial> element_map{element_id, block};\n        ElementMap<1, Frame::Inertial> expected_element_map{\n            element_id,\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n                block_map, grid_to_inertial_map)};\n        const double time = 1.;\n        domain::FunctionsOfTimeMap functions_of_time{};\n        functions_of_time[\"Translation\"] =\n            std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<1>>(\n                time, std::array<DataVector, 2>{{{1, 2.}, {1, 0.}}},\n                std::numeric_limits<double>::infinity());\n        CHECK(element_map(xi, time, functions_of_time) ==\n              expected_element_map(xi, time, functions_of_time));\n        CHECK(get<0>(element_map(xi, time, functions_of_time)) == 10.);\n      }\n    }\n  }\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_ElementSearchTree.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/ElementSearchTree.hpp\"\n\nnamespace domain {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.ElementSearchTree\", \"[Domain][Unit]\") {\n  // [element_search_tree_example]\n  const std::vector<ElementId<2>> element_ids{\n      // Element layout in block-logical coordinates:\n      //        xi -->\n      //        -1     0       1\n      // eta  -1  -------------\n      //  |      |  0  |   2   |\n      //  v    0 |-------------|\n      //         |  1  | 3 | 4 |\n      //       1  -------------\n      ElementId<2>{0, {{{1, 0}, {1, 0}}}},  // 0\n      ElementId<2>{0, {{{1, 0}, {1, 1}}}},  // 1\n      ElementId<2>{0, {{{1, 1}, {1, 0}}}},  // 2\n      ElementId<2>{0, {{{2, 2}, {1, 1}}}},  // 3\n      ElementId<2>{0, {{{2, 3}, {1, 1}}}}   // 4\n  };\n  const ElementSearchTree<2> search_tree(element_ids.begin(),\n                                         element_ids.end());\n  std::vector<ElementId<2>> search_result(\n      search_tree.begin_covers(\n          tnsr::I<double, 2, Frame::BlockLogical>{{{0.5, -0.5}}}),\n      search_tree.end_covers());\n  CHECK(search_result.size() == 1);\n  CHECK(search_result[0] == element_ids[2]);\n  // [element_search_tree_example]\n  search_result.assign(\n      search_tree.begin_covers(\n          tnsr::I<double, 2, Frame::BlockLogical>{{{0.0, -0.5}}}),\n      search_tree.end_covers());\n  CHECK(search_result.size() == 2);\n  CHECK(search_result[0] == element_ids[0]);\n  CHECK(search_result[1] == element_ids[2]);\n\n  {\n    INFO(\"Multiple blocks\");\n    std::vector<ElementId<2>> more_element_ids(element_ids);\n    more_element_ids.push_back(ElementId<2>{1, {{{1, 0}, {1, 0}}}});\n    const auto search_trees = domain::index_element_ids<2>(more_element_ids);\n    CHECK(search_trees.size() == 2);\n    CHECK(search_trees.at(0).size() == 5);\n    CHECK(search_trees.at(1).size() == 1);\n    search_result.assign(\n        search_trees.at(0).begin_covers(\n            tnsr::I<double, 2, Frame::BlockLogical>{{{0.5, -0.5}}}),\n        search_trees.at(0).end_covers());\n    CHECK(search_result.size() == 1);\n    CHECK(search_result[0] == element_ids[2]);\n    search_result.assign(\n        search_trees.at(1).begin_covers(\n            tnsr::I<double, 2, Frame::BlockLogical>{{{-0.5, -0.5}}}),\n        search_trees.at(1).end_covers());\n    CHECK(search_result.size() == 1);\n    CHECK(search_result[0] == more_element_ids.back());\n    search_result.assign(\n        search_trees.at(1).begin_covers(\n            tnsr::I<double, 2, Frame::BlockLogical>{{{0.5, -0.5}}}),\n        search_trees.at(1).end_covers());\n    CHECK(search_result.empty());\n  }\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_ElementToBlockLogicalMap.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace domain {\n\nSPECTRE_TEST_CASE(\"Unit.Domain.ElementToBlockLogicalMap\", \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> logical_dist{-1., 1.};\n\n  {\n    INFO(\"1D\");\n    const ElementId<1> element_id{0, {{{1, 0}}}};\n    const auto map = element_to_block_logical_map(element_id);\n\n    const auto xi =\n        make_with_random_values<tnsr::I<DataVector, 1, Frame::ElementLogical>>(\n            make_not_null(&generator), make_not_null(&logical_dist),\n            DataVector(5));\n    const auto x = (*map)(xi);\n    const auto jacobian = map->jacobian(xi);\n    const auto inv_jacobian = map->inv_jacobian(xi);\n    CHECK_ITERABLE_APPROX(get<0>(x), get<0>(xi) * 0.5 - 0.5);\n    CHECK_ITERABLE_APPROX((get<0, 0>(jacobian)), DataVector(5, 0.5));\n    CHECK_ITERABLE_APPROX((get<0, 0>(inv_jacobian)), DataVector(5, 2.));\n    const auto x_target = tnsr::I<double, 1, Frame::BlockLogical>{{{0.}}};\n    const auto inv = map->inverse(x_target);\n    REQUIRE(inv.has_value());\n    CHECK(get<0>(*inv) == approx(1.));\n  }\n\n  {\n    INFO(\"2D\");\n    const ElementId<2> element_id{0, {{{1, 0}, {0, 0}}}};\n    const auto map = element_to_block_logical_map(element_id);\n\n    const auto xi =\n        make_with_random_values<tnsr::I<DataVector, 2, Frame::ElementLogical>>(\n            make_not_null(&generator), make_not_null(&logical_dist),\n            DataVector(5));\n    const auto x = (*map)(xi);\n    const auto jacobian = map->jacobian(xi);\n    const auto inv_jacobian = map->inv_jacobian(xi);\n    CHECK_ITERABLE_APPROX(get<0>(x), get<0>(xi) * 0.5 - 0.5);\n    CHECK_ITERABLE_APPROX(get<1>(x), get<1>(xi));\n    CHECK_ITERABLE_APPROX((get<0, 0>(jacobian)), DataVector(5, 0.5));\n    CHECK_ITERABLE_APPROX((get<0, 0>(inv_jacobian)), DataVector(5, 2.));\n    CHECK_ITERABLE_APPROX((get<1, 1>(jacobian)), DataVector(5, 1.));\n    CHECK_ITERABLE_APPROX((get<1, 1>(inv_jacobian)), DataVector(5, 1.));\n    CHECK_ITERABLE_APPROX((get<1, 0>(jacobian)), DataVector(5, 0.));\n    CHECK_ITERABLE_APPROX((get<1, 0>(inv_jacobian)), DataVector(5, 0.));\n    const auto x_target = tnsr::I<double, 2, Frame::BlockLogical>{{{0., -1.}}};\n    const auto inv = map->inverse(x_target);\n    REQUIRE(inv.has_value());\n    CHECK(get<0>(*inv) == approx(1.));\n    CHECK(get<1>(*inv) == approx(-1.));\n  }\n\n  {\n    INFO(\"3D\");\n    const ElementId<3> element_id{0, {{{1, 0}, {0, 0}, {2, 1}}}};\n    const auto map = element_to_block_logical_map(element_id);\n\n    const auto xi =\n        make_with_random_values<tnsr::I<DataVector, 3, Frame::ElementLogical>>(\n            make_not_null(&generator), make_not_null(&logical_dist),\n            DataVector(5));\n    const auto x = (*map)(xi);\n    const auto jacobian = map->jacobian(xi);\n    const auto inv_jacobian = map->inv_jacobian(xi);\n    CHECK_ITERABLE_APPROX(get<0>(x), get<0>(xi) * 0.5 - 0.5);\n    CHECK_ITERABLE_APPROX(get<1>(x), get<1>(xi));\n    CHECK_ITERABLE_APPROX(get<2>(x), get<2>(xi) * 0.25 - 0.25);\n    CHECK_ITERABLE_APPROX((get<0, 0>(jacobian)), DataVector(5, 0.5));\n    CHECK_ITERABLE_APPROX((get<0, 0>(inv_jacobian)), DataVector(5, 2.));\n    CHECK_ITERABLE_APPROX((get<1, 1>(jacobian)), DataVector(5, 1.));\n    CHECK_ITERABLE_APPROX((get<1, 1>(inv_jacobian)), DataVector(5, 1.));\n    CHECK_ITERABLE_APPROX((get<2, 2>(jacobian)), DataVector(5, 0.25));\n    CHECK_ITERABLE_APPROX((get<2, 2>(inv_jacobian)), DataVector(5, 4.));\n    CHECK_ITERABLE_APPROX((get<1, 0>(jacobian)), DataVector(5, 0.));\n    CHECK_ITERABLE_APPROX((get<1, 0>(inv_jacobian)), DataVector(5, 0.));\n    CHECK_ITERABLE_APPROX((get<2, 0>(jacobian)), DataVector(5, 0.));\n    CHECK_ITERABLE_APPROX((get<2, 0>(inv_jacobian)), DataVector(5, 0.));\n    CHECK_ITERABLE_APPROX((get<2, 1>(jacobian)), DataVector(5, 0.));\n    CHECK_ITERABLE_APPROX((get<2, 1>(inv_jacobian)), DataVector(5, 0.));\n    const auto x_target =\n        tnsr::I<double, 3, Frame::BlockLogical>{{{0., -1., -0.5}}};\n    const auto inv = map->inverse(x_target);\n    REQUIRE(inv.has_value());\n    CHECK(get<0>(*inv) == approx(1.));\n    CHECK(get<1>(*inv) == approx(-1.));\n    CHECK(get<2>(*inv) == approx(-1.));\n  }\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_ExcisionSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nvoid check_excision_sphere_work(\n    const double radius, const tnsr::I<double, VolumeDim, Frame::Grid> center,\n    const std::unordered_map<size_t, Direction<VolumeDim>>&\n        abutting_directions) {\n  const ExcisionSphere<VolumeDim> excision_sphere(radius, center,\n                                                  abutting_directions);\n\n  CHECK_FALSE(excision_sphere.is_time_dependent());\n  CHECK(excision_sphere.radius() == radius);\n  CHECK(excision_sphere.center() == center);\n  CHECK(excision_sphere.abutting_directions() == abutting_directions);\n  CHECK(excision_sphere == excision_sphere);\n  CHECK_FALSE(excision_sphere != excision_sphere);\n\n  const double diff_radius = 0.001;\n  const ExcisionSphere<VolumeDim> excision_sphere_diff_radius(\n      diff_radius, center, abutting_directions);\n  CHECK(excision_sphere != excision_sphere_diff_radius);\n  CHECK_FALSE(excision_sphere == excision_sphere_diff_radius);\n\n  CHECK(get_output(excision_sphere) ==\n        \"ExcisionSphere:\\n\"\n        \"  Radius: \" +\n            get_output(excision_sphere.radius()) +\n            \"\\n\"\n            \"  Center: \" +\n            get_output(excision_sphere.center()) +\n            \"\\n\"\n            \"  Abutting directions: \" +\n            get_output(excision_sphere.abutting_directions()) + \"\\n\");\n\n  test_serialization(excision_sphere);\n}\n\nvoid check_excision_sphere_1d() {\n  const double radius = 1.2;\n  const tnsr::I<double, 1, Frame::Grid> center{{5.4}};\n  check_excision_sphere_work<1>(\n      radius, center,\n      {{0, Direction<1>::lower_xi()}, {2, Direction<1>::upper_xi()}});\n}\n\nvoid check_excision_sphere_2d() {\n  const double radius = 4.2;\n  const tnsr::I<double, 2, Frame::Grid> center{{5.4, -2.3}};\n  check_excision_sphere_work<2>(\n      radius, center,\n      {{0, Direction<2>::lower_eta()}, {2, Direction<2>::upper_xi()}});\n}\n\nvoid check_excision_sphere_3d() {\n  const double radius = 5.2;\n  const tnsr::I<double, 3, Frame::Grid> center{{5.4, -2.3, 9.0}};\n  check_excision_sphere_work<3>(\n      radius, center,\n      {{0, Direction<3>::lower_xi()}, {2, Direction<3>::upper_zeta()}});\n}\n\nvoid test_abutting_direction_shell() {\n  const double inner_radius = 1.3;\n  for (const auto& ref_level : std::array<size_t, 3>{{0, 1, 2}}) {\n    // shell without radial partition (blocks have 2 outer boundaries)\n    domain::creators::Sphere shell_plain{\n        inner_radius, 3.,   domain::creators::Sphere::Excision{},\n        ref_level,    4_st, true};\n    // shell with radial partition (blocks have 1 outer boundary)\n    domain::creators::Sphere shell_partitioned{\n        inner_radius,\n        3.,\n        domain::creators::Sphere::Excision{},\n        ref_level,\n        4_st,\n        true,\n        std::nullopt,\n        {2.},\n        {std::vector{{domain::CoordinateMaps::Distribution::Linear,\n                      domain::CoordinateMaps::Distribution::Linear}}}};\n    std::vector<domain::creators::Sphere> shells{};\n    shells.push_back(std::move(shell_plain));\n    shells.push_back(std::move(shell_partitioned));\n    for (const auto& shell : shells) {\n      const auto shell_domain = shell.create_domain();\n      const auto& blocks = shell_domain.blocks();\n      const auto& initial_ref_levels = shell.initial_refinement_levels();\n      const auto element_ids = initial_element_ids(initial_ref_levels);\n      const auto excision_sphere =\n          shell_domain.excision_spheres().at(\"ExcisionSphere\");\n      const auto& abutting_directions = excision_sphere.abutting_directions();\n      size_t num_excision_neighbors = 0;\n      const Mesh<2> face_mesh{10, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n      for (const auto& element_id : element_ids) {\n        const auto& current_block = blocks.at(element_id.block_id());\n        const auto element_abutting_direction =\n            excision_sphere.abutting_direction(element_id);\n        if (element_abutting_direction.has_value()) {\n          CHECK(abutting_directions.count(element_id.block_id()));\n          CHECK(element_abutting_direction.value() ==\n                abutting_directions.at(element_id.block_id()));\n          const auto face_logical_coords = interface_logical_coordinates(\n              face_mesh, element_abutting_direction.value());\n          const auto logical_to_grid_map = ElementMap(\n              element_id, current_block.stationary_map().get_clone());\n          const auto grid_coords = logical_to_grid_map(face_logical_coords);\n          const auto radii = magnitude(grid_coords);\n          CHECK_ITERABLE_APPROX(radii.get(),\n                                DataVector(radii.get().size(), inner_radius));\n          num_excision_neighbors++;\n        }\n      }\n      CHECK(num_excision_neighbors == 6 * pow(4, ref_level));\n    }\n  }\n}\n\nvoid test_error() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ExcisionSphere<3>(-2.0, tnsr::I<double, 3, Frame::Grid>{{3.4, 1.2, -0.9}},\n                        {}),\n      Catch::Matchers::ContainsSubstring(\n          \"The ExcisionSphere must have a radius greater than zero.\"));\n#endif\n}\n\nvoid test_time_dependent_maps() {\n  using TranslationMap = domain::CoordinateMaps::TimeDependent::Translation<3>;\n  PUPable_reg(SINGLE_ARG(\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap>));\n  std::unique_ptr<domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>\n      time_dep_map = std::make_unique<\n          domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap>>(\n          TranslationMap{\"DuckDuckGoose\"});\n\n  ExcisionSphere<3> excision_sphere{\n      1.0, tnsr::I<double, 3, Frame::Grid>{{0.0, 0.0, 0.0}}, {}};\n\n  CHECK_FALSE(excision_sphere.is_time_dependent());\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(excision_sphere.moving_mesh_grid_to_inertial_map(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Trying to access grid to inertial map \"\n                        \"from ExcisionSphere, but it has \"\n                        \"not been set.\"));\n#endif\n\n  excision_sphere.inject_time_dependent_maps(time_dep_map->get_clone());\n  test_serialization(excision_sphere);\n  CHECK(excision_sphere.is_time_dependent());\n  CHECK(excision_sphere.moving_mesh_grid_to_inertial_map() == *time_dep_map);\n  test_copy_semantics(excision_sphere);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.ExcisionSphere\", \"[Domain][Unit]\") {\n  check_excision_sphere_1d();\n  check_excision_sphere_2d();\n  check_excision_sphere_3d();\n  test_abutting_direction_shell();\n  test_error();\n  test_time_dependent_maps();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_FaceNormal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/InterfaceComputeTags.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <size_t Dim>\nstruct Directions : db::SimpleTag {\n  static constexpr size_t volume_dim = Dim;\n  static std::string name() { return \"Directions\"; }\n  using type = std::unordered_set<Direction<Dim>>;\n};\n\ntemplate <typename Map>\nvoid check(const Map& map,\n           const std::array<std::array<double, Map::dim>, Map::dim>& expected) {\n  const Mesh<Map::dim - 1> mesh{3, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n  const auto num_grid_points = mesh.number_of_grid_points();\n  for (size_t d = 0; d < Map::dim; ++d) {\n    const auto upper_normal = unnormalized_face_normal(\n        mesh, map, Direction<Map::dim>(d, Side::Upper));\n    const auto lower_normal = unnormalized_face_normal(\n        mesh, map, Direction<Map::dim>(d, Side::Lower));\n    for (size_t i = 0; i < 2; ++i) {\n      CHECK_ITERABLE_APPROX(\n          upper_normal.get(i),\n          DataVector(num_grid_points, gsl::at(gsl::at(expected, d), i)));\n      CHECK_ITERABLE_APPROX(\n          lower_normal.get(i),\n          DataVector(num_grid_points, -gsl::at(gsl::at(expected, d), i)));\n    }\n  }\n}\n\ntemplate <size_t Dim>\nstd::unordered_set<Direction<Dim>> get_directions();\n\ntemplate <>\nstd::unordered_set<Direction<1>> get_directions<1>() {\n  return std::unordered_set<Direction<1>>{Direction<1>::upper_xi()};\n}\n\ntemplate <>\nstd::unordered_set<Direction<2>> get_directions<2>() {\n  return std::unordered_set<Direction<2>>{Direction<2>::upper_xi(),\n                                          Direction<2>::lower_eta()};\n}\n\ntemplate <>\nstd::unordered_set<Direction<3>> get_directions<3>() {\n  return std::unordered_set<Direction<3>>{Direction<3>::upper_xi(),\n                                          Direction<3>::lower_eta(),\n                                          Direction<3>::lower_zeta()};\n}\n\ntemplate <size_t Dim>\nvoid check_time_dependent(\n    const ElementId<Dim>& element_id,\n    const ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n    const std::unique_ptr<CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>>&\n        grid_to_inertial_map,\n    const std::unique_ptr<\n        CoordinateMapBase<Frame::ElementLogical, Frame::Inertial, Dim>>&\n        logical_to_inertial_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  const Mesh<Dim - 1> interface_mesh{3, Spectral::Basis::Legendre,\n                                     Spectral::Quadrature::GaussLobatto};\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto upper_normal = unnormalized_face_normal(\n        interface_mesh, logical_to_grid_map, *grid_to_inertial_map, time,\n        functions_of_time, Direction<Dim>(d, Side::Upper));\n    const auto lower_normal = unnormalized_face_normal(\n        interface_mesh, logical_to_grid_map, *grid_to_inertial_map, time,\n        functions_of_time, Direction<Dim>(d, Side::Lower));\n\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>\n        inv_jacobian_upper = logical_to_inertial_map->inv_jacobian(\n            interface_logical_coordinates(interface_mesh,\n                                          Direction<Dim>(d, Side::Upper)),\n            time, functions_of_time);\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>\n        inv_jacobian_lower = logical_to_inertial_map->inv_jacobian(\n            interface_logical_coordinates(interface_mesh,\n                                          Direction<Dim>(d, Side::Lower)),\n            time, functions_of_time);\n\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_APPROX(upper_normal.get(i),\n                            DataVector{inv_jacobian_upper.get(d, i)});\n      CHECK_ITERABLE_APPROX(lower_normal.get(i),\n                            DataVector{-inv_jacobian_lower.get(d, i)});\n    }\n  }\n\n  // Now check the compute items\n  const auto box = db::create<\n      db::AddSimpleTags<Tags::Element<Dim>, Directions<Dim>, Tags::Mesh<Dim>,\n                        Tags::ElementMap<Dim, Frame::Grid>,\n                        CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                            Frame::Inertial>,\n                        ::Tags::Time, Tags::FunctionsOfTimeInitialize>,\n      db::AddComputeTags<\n          Tags::BoundaryDirectionsExteriorCompute<Dim>,\n          Tags::InterfaceCompute<Directions<Dim>, Tags::Direction<Dim>>,\n          Tags::InterfaceCompute<Directions<Dim>, Tags::InterfaceMesh<Dim>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<Dim>,\n                                 Tags::Direction<Dim>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<Dim>,\n                                 Tags::InterfaceMesh<Dim>>,\n          Tags::InterfaceCompute<\n              Tags::BoundaryDirectionsExterior<Dim>,\n              Tags::UnnormalizedFaceNormalMovingMeshCompute<Dim>>,\n          Tags::InterfaceCompute<\n              Directions<Dim>,\n              Tags::UnnormalizedFaceNormalMovingMeshCompute<Dim>>>>(\n      Element<Dim>(element_id, {}), get_directions<Dim>(),\n      Mesh<Dim>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto},\n      ElementMap<Dim, Frame::Grid>(element_id,\n                                   logical_to_grid_map.block_map().get_clone()),\n      grid_to_inertial_map->get_clone(), time,\n      clone_unique_ptrs(functions_of_time));\n\n  TestHelpers::db::test_compute_tag<\n      Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<2>,\n                             Tags::UnnormalizedFaceNormalMovingMeshCompute<2>>>(\n      \"BoundaryDirectionsExterior<UnnormalizedFaceNormal>\"s);\n\n  for (const auto& direction_and_face_normal :\n       db::get<Tags::Interface<Tags::BoundaryDirectionsExterior<Dim>,\n                               Tags::UnnormalizedFaceNormal<Dim>>>(box)) {\n    auto expected_normal = unnormalized_face_normal(\n        interface_mesh, logical_to_grid_map, *grid_to_inertial_map, time,\n        functions_of_time, direction_and_face_normal.first);\n    for (size_t i = 0; i < expected_normal.size(); ++i) {\n      expected_normal.get(i) *= -1.0;\n    }\n\n    CHECK_ITERABLE_APPROX(expected_normal, direction_and_face_normal.second);\n  }\n\n  for (const auto& direction_and_face_normal : db::get<\n           Tags::Interface<Directions<Dim>, Tags::UnnormalizedFaceNormal<Dim>>>(\n           box)) {\n    const auto expected_normal = unnormalized_face_normal(\n        interface_mesh, logical_to_grid_map, *grid_to_inertial_map, time,\n        functions_of_time, direction_and_face_normal.first);\n\n    CHECK_ITERABLE_APPROX(expected_normal, direction_and_face_normal.second);\n  }\n}\n\nvoid test_face_normal_coordinate_map() {\n  INFO(\"Test coordinate map\");\n  // [face_normal_example]\n  const Mesh<0> mesh_0d;\n  const auto map_1d = make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n      CoordinateMaps::Affine(-1.0, 1.0, -3.0, 7.0));\n  const auto normal_1d_lower =\n      unnormalized_face_normal(mesh_0d, map_1d, Direction<1>::lower_xi());\n  // [face_normal_example]\n\n  CHECK(normal_1d_lower.get(0) == DataVector(1, -0.2));\n\n  const auto normal_1d_upper =\n      unnormalized_face_normal(mesh_0d, map_1d, Direction<1>::upper_xi());\n\n  CHECK(normal_1d_upper.get(0) == DataVector(1, 0.2));\n\n  check(make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n            CoordinateMaps::Rotation<2>(atan2(4., 3.))),\n        {{{{0.6, 0.8}}, {{-0.8, 0.6}}}});\n\n  check(make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n            CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                           CoordinateMaps::Rotation<2>>(\n                {-1., 1., 2., 7.}, CoordinateMaps::Rotation<2>(atan2(4., 3.)))),\n        {{{{0.4, 0., 0.}}, {{0., 0.6, 0.8}}, {{0., -0.8, 0.6}}}});\n}\n\ntemplate <typename TargetFrame>\nvoid test_face_normal_element_map() {\n  const Mesh<0> mesh_0d;\n  const auto map_1d = ElementMap<1, TargetFrame>(\n      ElementId<1>{0},\n      make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n          CoordinateMaps::Affine(-1.0, 1.0, -3.0, 7.0)));\n  const auto normal_1d_lower =\n      unnormalized_face_normal(mesh_0d, map_1d, Direction<1>::lower_xi());\n\n  CHECK(normal_1d_lower.get(0) == DataVector(1, -0.2));\n\n  const auto normal_1d_upper =\n      unnormalized_face_normal(mesh_0d, map_1d, Direction<1>::upper_xi());\n\n  CHECK(normal_1d_upper.get(0) == DataVector(1, 0.2));\n\n  check(ElementMap<2, TargetFrame>(\n            ElementId<2>(0),\n            make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n                CoordinateMaps::Rotation<2>(atan2(4., 3.)))),\n        {{{{0.6, 0.8}}, {{-0.8, 0.6}}}});\n\n  check(ElementMap<3, TargetFrame>(\n            ElementId<3>(0),\n            make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n                CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                               CoordinateMaps::Rotation<2>>(\n                    {-1., 1., 2., 7.},\n                    CoordinateMaps::Rotation<2>(atan2(4., 3.))))),\n        {{{{0.4, 0., 0.}}, {{0., 0.6, 0.8}}, {{0., -0.8, 0.6}}}});\n}\n\nvoid test_face_normal_moving_mesh() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n\n  const std::array<double, 4> times_to_check{{0.0, 0.3, 1.1, 7.8}};\n  const double outer_boundary = 10.0;\n  std::array<std::string, 2> functions_of_time_names{\n      {\"ExpansionA\", \"ExpansionB\"}};\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<2>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n  std::unordered_map<std::string, FoftPtr> functions_of_time{};\n  const std::array<DataVector, 3> init_func_a{{{1.0}, {-0.1}, {0.0}}};\n  const std::array<DataVector, 3> init_func_b{{{1.0}, {0.0}, {0.0}}};\n  const double initial_time = 0.0;\n  const double expiration_time = 10.0;\n  functions_of_time[\"ExpansionA\"] =\n      std::make_unique<Polynomial>(initial_time, init_func_a, expiration_time);\n  functions_of_time[\"ExpansionB\"] =\n      std::make_unique<Polynomial>(initial_time, init_func_b, expiration_time);\n\n  {\n    INFO(\"1d\");\n    const ElementId<1> element_id{0};\n    const ElementMap<1, Frame::Grid> logical_to_grid_map(\n        element_id, make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                        CoordinateMaps::Affine(-1.0, 1.0, 2.0, 7.8)));\n    const auto grid_to_inertial_map =\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::CubicScale<1>{\n                outer_boundary, functions_of_time_names[0],\n                functions_of_time_names[1]});\n    const auto logical_to_inertial_map =\n        make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n            CoordinateMaps::Affine(-1.0, 1.0, 2.0, 7.8),\n            CoordinateMaps::TimeDependent::CubicScale<1>{\n                outer_boundary, functions_of_time_names[0],\n                functions_of_time_names[1]});\n\n    for (const double time : times_to_check) {\n      check_time_dependent(element_id, logical_to_grid_map,\n                           grid_to_inertial_map, logical_to_inertial_map, time,\n                           functions_of_time);\n    }\n  }\n  {\n    INFO(\"2d\");\n    const ElementId<2> element_id{0};\n    const ElementMap<2, Frame::Grid> logical_to_grid_map(\n        element_id, make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                        CoordinateMaps::Rotation<2>(atan2(4., 3.))));\n    const auto grid_to_inertial_map =\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::CubicScale<2>{\n                outer_boundary, functions_of_time_names[0],\n                functions_of_time_names[1]});\n    const auto logical_to_inertial_map =\n        make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n            CoordinateMaps::Rotation<2>(atan2(4., 3.)),\n            CoordinateMaps::TimeDependent::CubicScale<2>{\n                outer_boundary, functions_of_time_names[0],\n                functions_of_time_names[1]});\n\n    for (const double time : times_to_check) {\n      check_time_dependent(element_id, logical_to_grid_map,\n                           grid_to_inertial_map, logical_to_inertial_map, time,\n                           functions_of_time);\n    }\n  }\n  {\n    INFO(\"3d\");\n    const ElementId<3> element_id{0};\n    const ElementMap<3, Frame::Grid> logical_to_grid_map(\n        element_id,\n        make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n            CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                           CoordinateMaps::Rotation<2>>(\n                {-1., 1., 2., 7.},\n                CoordinateMaps::Rotation<2>(atan2(4., 3.)))));\n    const auto grid_to_inertial_map =\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            CoordinateMaps::TimeDependent::CubicScale<3>{\n                outer_boundary, functions_of_time_names[0],\n                functions_of_time_names[1]});\n    const auto logical_to_inertial_map =\n        make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n            CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                           CoordinateMaps::Rotation<2>>(\n                {-1., 1., 2., 7.}, CoordinateMaps::Rotation<2>(atan2(4., 3.))),\n            CoordinateMaps::TimeDependent::CubicScale<3>{\n                outer_boundary, functions_of_time_names[0],\n                functions_of_time_names[1]});\n\n    for (const double time : times_to_check) {\n      check_time_dependent(element_id, logical_to_grid_map,\n                           grid_to_inertial_map, logical_to_inertial_map, time,\n                           functions_of_time);\n    }\n  }\n}\n\nvoid test_compute_item() {\n  INFO(\"Testing compute item\");\n  const auto box = db::create<\n      db::AddSimpleTags<Tags::Element<2>, Directions<2>, Tags::Mesh<2>,\n                        Tags::ElementMap<2>>,\n      db::AddComputeTags<\n          Tags::BoundaryDirectionsExteriorCompute<2>,\n          Tags::InterfaceCompute<Directions<2>, Tags::Direction<2>>,\n          Tags::InterfaceCompute<Directions<2>, Tags::InterfaceMesh<2>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<2>,\n                                 Tags::Direction<2>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<2>,\n                                 Tags::InterfaceMesh<2>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<2>,\n                                 Tags::UnnormalizedFaceNormalCompute<2>>,\n          Tags::InterfaceCompute<Directions<2>,\n                                 Tags::UnnormalizedFaceNormalCompute<2>>>>(\n      Element<2>(ElementId<2>(0), {}),\n      std::unordered_set<Direction<2>>{Direction<2>::upper_xi(),\n                                       Direction<2>::lower_eta()},\n      Mesh<2>{2, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto},\n      ElementMap<2, Frame::Inertial>(\n          ElementId<2>(0),\n          make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n              CoordinateMaps::Rotation<2>(atan2(4., 3.)))));\n\n  TestHelpers::db::test_compute_tag<\n      Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<2>,\n                             Tags::UnnormalizedFaceNormalCompute<2>>>(\n      \"BoundaryDirectionsExterior<UnnormalizedFaceNormal>\"s);\n\n  std::unordered_map<Direction<2>, tnsr::i<DataVector, 2>> expected;\n  expected[Direction<2>::upper_xi()] =\n      tnsr::i<DataVector, 2>{{{{0.6, 0.6}, {0.8, 0.8}}}};\n  expected[Direction<2>::lower_eta()] =\n      tnsr::i<DataVector, 2>{{{{0.8, 0.8}, {-0.6, -0.6}}}};\n\n  std::unordered_map<Direction<2>, tnsr::i<DataVector, 2>>\n      expected_external_normal;\n  expected_external_normal[Direction<2>::lower_xi()] =\n      tnsr::i<DataVector, 2>{{{{0.6, 0.6}, {0.8, 0.8}}}};\n  expected_external_normal[Direction<2>::upper_xi()] =\n      tnsr::i<DataVector, 2>{{{{-0.6, -0.6}, {-0.8, -0.8}}}};\n  expected_external_normal[Direction<2>::lower_eta()] =\n      tnsr::i<DataVector, 2>{{{{-0.8, -0.8}, {0.6, 0.6}}}};\n  expected_external_normal[Direction<2>::upper_eta()] =\n      tnsr::i<DataVector, 2>{{{{0.8, 0.8}, {-0.6, -0.6}}}};\n\n  CHECK_ITERABLE_APPROX(\n      (get<Tags::Interface<Directions<2>, Tags::UnnormalizedFaceNormal<2>>>(\n          box)),\n      expected);\n  CHECK_ITERABLE_APPROX(\n      (get<Tags::Interface<Tags::BoundaryDirectionsExterior<2>,\n                           Tags::UnnormalizedFaceNormal<2>>>(box)),\n      expected_external_normal);\n\n  // Now test the external normals with a non-affine map, to ensure that the\n  // exterior face normal is the inverted interior one\n  const auto box_with_non_affine_map = db::create<\n      db::AddSimpleTags<Tags::Element<2>, Tags::Mesh<2>, Tags::ElementMap<2>>,\n      db::AddComputeTags<\n          Tags::BoundaryDirectionsExteriorCompute<2>,\n          Tags::BoundaryDirectionsInteriorCompute<2>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<2>,\n                                 Tags::Direction<2>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<2>,\n                                 Tags::InterfaceMesh<2>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsExterior<2>,\n                                 Tags::UnnormalizedFaceNormalCompute<2>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsInterior<2>,\n                                 Tags::Direction<2>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsInterior<2>,\n                                 Tags::InterfaceMesh<2>>,\n          Tags::InterfaceCompute<Tags::BoundaryDirectionsInterior<2>,\n                                 Tags::UnnormalizedFaceNormalCompute<2>>>>(\n      Element<2>(ElementId<2>(0), {}),\n      Mesh<2>{2, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto},\n      ElementMap<2, Frame::Inertial>(\n          ElementId<2>(0),\n          make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n              CoordinateMaps::Wedge<2>(1., 2., 0., 1.,\n                                       OrientationMap<2>::create_aligned(),\n                                       false))));\n\n  auto invert = [](std::unordered_map<Direction<2>, tnsr::i<DataVector, 2>>\n                       map_of_vectors) {\n    for (auto& vector : map_of_vectors) {\n      for (auto& dv : vector.second) {\n        dv *= -1.;\n      }\n    }\n    return map_of_vectors;\n  };\n\n  CHECK((db::get<Tags::Interface<Tags::BoundaryDirectionsExterior<2>,\n                                 Tags::UnnormalizedFaceNormal<2>>>(\n            box_with_non_affine_map)) ==\n        (invert(db::get<Tags::Interface<Tags::BoundaryDirectionsInterior<2>,\n                                        Tags::UnnormalizedFaceNormal<2>>>(\n            box_with_non_affine_map))));\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid test_tags() {\n  TestHelpers::db::test_simple_tag<Tags::UnnormalizedFaceNormal<Dim, Frame>>(\n      \"UnnormalizedFaceNormal\");\n  TestHelpers::db::test_compute_tag<\n      Tags::UnnormalizedFaceNormalCompute<Dim, Frame>>(\n      \"UnnormalizedFaceNormal\");\n  if constexpr (std::is_same_v<Frame, ::Frame::Inertial>) {\n    TestHelpers::db::test_compute_tag<\n        Tags::UnnormalizedFaceNormalMovingMeshCompute<Dim>>(\n        \"UnnormalizedFaceNormal\");\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FaceNormal\", \"[Unit][Domain]\") {\n  test_face_normal_coordinate_map();\n  test_face_normal_element_map<Frame::Inertial>();\n  test_face_normal_element_map<Frame::Grid>();\n  test_face_normal_moving_mesh();\n  test_compute_item();\n  test_tags<1, Frame::Inertial>();\n  test_tags<2, Frame::Inertial>();\n  test_tags<3, Frame::Inertial>();\n  test_tags<1, Frame::Grid>();\n  test_tags<2, Frame::Grid>();\n  test_tags<3, Frame::Grid>();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_FlatLogicalMetric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/FlatLogicalMetric.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nauto make_affine_coord_map() {\n  using AffineMap = domain::CoordinateMaps::Affine;\n  if constexpr (Dim == 1) {\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap>{{-1., 1., 0., M_PI}};\n  } else if constexpr (Dim == 2) {\n    using AffineMap2D =\n        domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap2D>{\n        {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI_2}}};\n  } else {\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap3D>{\n        {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI_2}, {-1., 1., 0., M_PI_4}}};\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_flat_logical_metric() {\n  CAPTURE(Dim);\n  const Mesh<Dim> mesh{3, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const size_t num_points = mesh.number_of_grid_points();\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto coord_map = make_affine_coord_map<Dim>();\n  const auto inertial_coords = coord_map(logical_coords);\n  const auto jacobian = coord_map.jacobian(logical_coords);\n  const auto inv_jacobian = coord_map.inv_jacobian(logical_coords);\n  tnsr::ii<DataVector, Dim, Frame::ElementLogical> flat_logical_metric{\n      num_points};\n  domain::flat_logical_metric(make_not_null(&flat_logical_metric), jacobian);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      CHECK_ITERABLE_APPROX(\n          flat_logical_metric.get(i, j),\n          DataVector(num_points, i == j ? square(M_PI_2 / pow(2, i)) : 0.));\n    }\n  }\n  {\n    INFO(\"Test the compute tag\");\n    TestHelpers::db::test_compute_tag<\n        domain::Tags::FlatLogicalMetricCompute<Dim>>(\"FlatLogicalMetric\");\n    const auto box = db::create<\n        db::AddSimpleTags<domain::Tags::InverseJacobian<\n            Dim, Frame::ElementLogical, Frame::Inertial>>,\n        db::AddComputeTags<domain::Tags::FlatLogicalMetricCompute<Dim>>>(\n        inv_jacobian);\n    CHECK_ITERABLE_APPROX(get<domain::Tags::FlatLogicalMetric<Dim>>(box),\n                          flat_logical_metric);\n  }\n}\n\nvoid test_equiangular() {\n  INFO(\"Equiangular\");\n  // Construct a 2D equiangular Wedge map\n  const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                              domain::CoordinateMaps::Wedge<2>>\n      coord_map{{1., 2., 1., 1., OrientationMap<2>::create_aligned(), true}};\n  using CoordAxis = domain::CoordinateMaps::detail::WedgeCoordOrientation<2>;\n  // Set up a grid\n  const Mesh<2> mesh{6, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const size_t num_points = mesh.number_of_grid_points();\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto jacobian = coord_map.jacobian(logical_coords);\n  tnsr::ii<DataVector, 2, Frame::ElementLogical> flat_logical_metric{\n      num_points};\n  domain::flat_logical_metric(make_not_null(&flat_logical_metric), jacobian);\n  // Check that the flat logical metric is constant along the angular direction\n  const DataVector& angular_data =\n      get<CoordAxis::polar_coord, CoordAxis::polar_coord>(flat_logical_metric);\n  CAPTURE(angular_data);\n  for (size_t slice = 0; slice < mesh.extents(CoordAxis::radial_coord);\n       ++slice) {\n    double constant_value = std::numeric_limits<double>::signaling_NaN();\n    bool first = true;\n    for (SliceIterator si(mesh.extents(), CoordAxis::radial_coord, slice); si;\n         ++si) {\n      if (first) {\n        constant_value = angular_data[si.volume_offset()];\n        first = false;\n      } else {\n        CHECK(angular_data[si.volume_offset()] == approx(constant_value));\n      }\n    }\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.FlatLogicalMetric\", \"[Unit][Domain]\") {\n  test_flat_logical_metric<1>();\n  test_flat_logical_metric<2>();\n  test_flat_logical_metric<3>();\n  test_equiangular();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_InterfaceHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/InterfaceHelpers.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n\nnamespace domain {\nnamespace {\nstruct SomeNumber : db::SimpleTag {\n  using type = double;\n  static std::string name() { return \"SomeNumber\"; }\n};\nstruct SomeVolumeArgument : db::SimpleTag {\n  using type = double;\n  static std::string name() { return \"SomeVolumeArgument\"; }\n};\n\n// [interface_invokable_example]\nstruct ComputeSomethingOnInterface {\n  using argument_tags = tmpl::list<SomeNumber, SomeVolumeArgument>;\n  using volume_tags = tmpl::list<SomeVolumeArgument>;\n  static double apply(const double& some_number_on_interface,\n                      const double& volume_argument, const double factor) {\n    return factor * some_number_on_interface + volume_argument;\n  }\n};\n// [interface_invokable_example]\n\nstruct ComputeWithTemplateParameters {\n  using argument_tags = tmpl::list<SomeNumber>;\n  template <typename Arg>\n  static double apply(const Arg& arg) {\n    return arg;\n  }\n};\n\nstruct ComputeWithVoidReturnType {\n  using argument_tags = tmpl::list<SomeNumber>;\n  template <typename Arg>\n  static void apply(const Arg& arg, const gsl::not_null<double*> result) {\n    *result += arg;\n  }\n};\n\ntemplate <size_t Dim, typename DirectionsTag>\nvoid test_interface_apply(\n    const Element<Dim>& element,\n    const std::unordered_set<Direction<Dim>>& directions,\n    const std::unordered_map<Direction<Dim>, double>& number_on_interfaces,\n    const std::unordered_map<Direction<Dim>, double>&\n        expected_result_on_interfaces) {\n  // Construct DataBox that holds the test data\n  const auto box =\n      db::create<db::AddSimpleTags<Tags::Element<Dim>, DirectionsTag,\n                                   Tags::Interface<DirectionsTag, SomeNumber>,\n                                   SomeVolumeArgument>>(\n          element, directions, number_on_interfaces, 1.);\n  // Test applying a function to the interface and give an example\n  // [interface_apply_example]\n  const auto computed_number_on_interfaces =\n      interface_apply<DirectionsTag, tmpl::list<SomeNumber, SomeVolumeArgument>,\n                      tmpl::list<SomeVolumeArgument>>(\n          [](const double some_number_on_interface,\n             const double volume_argument, const double factor) {\n            return factor * some_number_on_interface + volume_argument;\n          },\n          box, 2.);\n  CHECK(computed_number_on_interfaces.size() ==\n        expected_result_on_interfaces.size());\n  for (const auto& direction_and_expected_result :\n       expected_result_on_interfaces) {\n    CHECK(\n        computed_number_on_interfaces.at(direction_and_expected_result.first) ==\n        direction_and_expected_result.second);\n  }\n  // [interface_apply_example]\n\n  // Test overload that takes a stateless invokable\n  // [interface_apply_example_stateless]\n  const auto computed_numbers_with_struct =\n      interface_apply<DirectionsTag, ComputeSomethingOnInterface>(box, 2.);\n  // [interface_apply_example_stateless]\n  CHECK(computed_numbers_with_struct == computed_number_on_interfaces);\n\n  {\n    // Test an invokable with template parameters\n    const auto computed_numbers =\n        interface_apply<DirectionsTag, ComputeWithTemplateParameters>(box);\n    for (const auto& direction_and_expected_number : number_on_interfaces) {\n      CHECK(computed_numbers.at(direction_and_expected_number.first) ==\n            direction_and_expected_number.second);\n    }\n  }\n  {\n    // Test an invokable with `void` return value\n    size_t face_count = 0;\n    interface_apply<DirectionsTag, tmpl::list<>, tmpl::list<>>(\n        [&face_count]() { face_count += 1; }, box);\n    CHECK(face_count == expected_result_on_interfaces.size());\n    double sum = 0;\n    interface_apply<DirectionsTag, ComputeWithVoidReturnType>(\n        box, make_not_null(&sum));\n    double expected_sum = 0;\n    for (const auto& direction_and_number : number_on_interfaces) {\n      expected_sum += direction_and_number.second;\n    }\n    CHECK(sum == expected_sum);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.InterfaceHelpers\", \"[Unit][Domain]\") {\n  test_interface_apply<1, Tags::InternalDirections<1>>(\n      // Reference element has one internal direction:\n      // [ X | ]-> xi\n      {{0, {{{1, 0}}}},\n       {{Direction<1>::upper_xi(),\n         {{{0, {{{1, 1}}}}}, OrientationMap<1>::create_aligned()}}}},\n      {Direction<1>::upper_xi()}, {{Direction<1>::upper_xi(), 2.}},\n      {{Direction<1>::upper_xi(), 5.}});\n  test_interface_apply<1, Tags::InternalDirections<1>>(\n      // Reference element has no internal directions:\n      // [ X ]-> xi\n      {{0, {{{0, 0}}}}, {}}, {}, {}, {});\n  test_interface_apply<1, Tags::BoundaryDirectionsInterior<1>>(\n      // Reference element has two boundary directions:\n      // [ X ]-> xi\n      {{0, {{{0, 0}}}}, {}},\n      {Direction<1>::lower_xi(), Direction<1>::upper_xi()},\n      {{Direction<1>::lower_xi(), 2.}, {Direction<1>::upper_xi(), 3.}},\n      {{Direction<1>::lower_xi(), 5.}, {Direction<1>::upper_xi(), 7.}});\n  test_interface_apply<2, Tags::InternalDirections<2>>(\n      // Reference element has one internal directions:\n      // ^ eta\n      // +-+-+\n      // |X| |\n      // +-+-+> xi\n      {{0, {{{1, 0}, {0, 0}}}},\n       {{Direction<2>::upper_xi(),\n         {{{0, {{{1, 1}, {0, 0}}}}}, OrientationMap<2>::create_aligned()}}}},\n      {Direction<2>::upper_xi()}, {{Direction<2>::upper_xi(), 2.}},\n      {{Direction<2>::upper_xi(), 5.}});\n}\n}  // namespace\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_InterfaceItems.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/InterfaceComputeTags.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <size_t N>\nstruct NoCopy {\n  NoCopy() = default;\n  NoCopy(const NoCopy&) = delete;\n  NoCopy(NoCopy&&) = default;\n  NoCopy& operator=(const NoCopy&) = delete;\n  NoCopy& operator=(NoCopy&&) = default;\n  ~NoCopy() = default;\n};\n\nnamespace TestTags {\nstruct Int : db::SimpleTag {\n  using type = int;\n};\n\nstruct Double : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <size_t N>\nstruct NoCopy : db::SimpleTag {\n  using type = domain::NoCopy<N>;\n};\n\ntemplate <typename Tag>\nstruct Negate : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = typename Tag::type;\n};\n\ntemplate <typename Tag>\nstruct NegateCompute : Negate<Tag>, db::ComputeTag {\n  using base = Negate<Tag>;\n  static constexpr void function(\n      const gsl::not_null<typename Tag::type*> result,\n      const typename Tag::type& x) {\n    *result = -x;\n  }\n  using argument_tags = tmpl::list<Tag>;\n  using return_type = typename Tag::type;\n};\n\nstruct NegateDoubleAddInt : db::SimpleTag {\n  using type = double;\n};\n\nstruct NegateDoubleAddIntCompute : NegateDoubleAddInt, db::ComputeTag {\n  using base = NegateDoubleAddInt;\n  using return_type = double;\n  static constexpr void function(const gsl::not_null<double*> result,\n                                 const double x, const int y) {\n    *result = -x + y;\n  }\n  using argument_tags = tmpl::list<Double, Int>;\n  using volume_tags = tmpl::list<Int>;\n};\n\nstruct IntCompute : db::ComputeTag, Int {\n  static constexpr void function(const gsl::not_null<int*> result,\n                                 const int x) {\n    *result = x + 3;\n  }\n  using argument_tags = tmpl::list<Int>;\n  using volume_tags = tmpl::list<Int>;\n  using base = Int;\n  using return_type = int;\n};\n\ntemplate <size_t VolumeDim>\nstruct ComplexItem : db::SimpleTag {\n  using type = std::pair<int, double>;\n};\n\ntemplate <size_t VolumeDim>\nstruct ComplexItemCompute : ComplexItem<VolumeDim>, db::ComputeTag {\n  static constexpr void function(\n      const gsl::not_null<std::pair<int, double>*> result, const int i,\n      const double d, const domain::NoCopy<1>& /*unused*/,\n      const domain::NoCopy<2>& /*unused*/) {\n    *result = std::make_pair(i, d);\n  }\n  using argument_tags = tmpl::list<Int, Double, NoCopy<1>, NoCopy<2>>;\n  using volume_tags = tmpl::list<Int, NoCopy<1>>;\n  using base = ComplexItem<VolumeDim>;\n  using return_type = std::pair<int, double>;\n};\n\ntemplate <typename>\nstruct TemplatedDirections : db::SimpleTag {\n  static constexpr size_t volume_dim = 3;\n  using type = std::unordered_set<Direction<3>>;\n};\n}  // namespace TestTags\n\nvoid test_interface_items() {\n  constexpr size_t dim = 3;\n  using internal_directions = Tags::InternalDirections<dim>;\n  using boundary_directions_interior = Tags::BoundaryDirectionsInterior<dim>;\n  using templated_directions = TestTags::TemplatedDirections<int>;\n  const Neighbors<3> neighbors{ElementId<3>{1},\n                               OrientationMap<3>::create_aligned()};\n  Element<dim> element{ElementId<3>(0),\n                       {{Direction<dim>::lower_xi(), neighbors},\n                        {Direction<dim>::upper_xi(), neighbors},\n                        {Direction<dim>::upper_zeta(), neighbors}}};\n\n  std::unordered_map<Direction<dim>, NoCopy<2>> internal_nocopy_map_item;\n  internal_nocopy_map_item.emplace(Direction<dim>::lower_xi(), NoCopy<2>{});\n  internal_nocopy_map_item.emplace(Direction<dim>::upper_xi(), NoCopy<2>{});\n  internal_nocopy_map_item.emplace(Direction<dim>::upper_zeta(), NoCopy<2>{});\n  std::unordered_map<Direction<dim>, NoCopy<2>> external_nocopy_map_item;\n  external_nocopy_map_item.emplace(Direction<dim>::lower_eta(), NoCopy<2>{});\n  external_nocopy_map_item.emplace(Direction<dim>::upper_eta(), NoCopy<2>{});\n  external_nocopy_map_item.emplace(Direction<dim>::lower_zeta(), NoCopy<2>{});\n  const auto box = db::create<\n      db::AddSimpleTags<\n          Tags::Element<dim>, TestTags::Int,\n          Tags::Interface<internal_directions, TestTags::Double>,\n          Tags::Interface<boundary_directions_interior, TestTags::Double>,\n          TestTags::NoCopy<1>,\n          Tags::Interface<internal_directions, TestTags::NoCopy<2>>,\n          Tags::Interface<boundary_directions_interior, TestTags::NoCopy<2>>,\n          templated_directions,\n          Tags::Interface<templated_directions, TestTags::Double>>,\n      db::AddComputeTags<\n          Tags::InternalDirectionsCompute<dim>,\n          Tags::InterfaceCompute<internal_directions, Tags::Direction<dim>>,\n          TestTags::NegateCompute<TestTags::Int>,\n          Tags::InterfaceCompute<internal_directions, TestTags::IntCompute>,\n          Tags::InterfaceCompute<internal_directions,\n                                 TestTags::NegateCompute<TestTags::Double>>,\n          Tags::InterfaceCompute<internal_directions,\n                                 TestTags::NegateDoubleAddIntCompute>,\n          Tags::InterfaceCompute<internal_directions,\n                                 TestTags::ComplexItemCompute<dim>>,\n          Tags::InterfaceCompute<templated_directions, Tags::Direction<dim>>,\n          Tags::InterfaceCompute<templated_directions,\n                                 TestTags::NegateCompute<TestTags::Double>>,\n          Tags::InterfaceCompute<templated_directions,\n                                 TestTags::NegateDoubleAddIntCompute>,\n          Tags::BoundaryDirectionsInteriorCompute<dim>,\n          Tags::InterfaceCompute<boundary_directions_interior,\n                                 Tags::Direction<dim>>,\n          Tags::InterfaceCompute<boundary_directions_interior,\n                                 TestTags::IntCompute>,\n          Tags::InterfaceCompute<boundary_directions_interior,\n                                 TestTags::NegateCompute<TestTags::Double>>,\n          Tags::InterfaceCompute<boundary_directions_interior,\n                                 TestTags::NegateDoubleAddIntCompute>,\n          Tags::InterfaceCompute<boundary_directions_interior,\n                                 TestTags::ComplexItemCompute<dim>>,\n          Tags::BoundaryDirectionsExteriorCompute<dim>>>(\n      std::move(element), 5,\n      std::unordered_map<Direction<dim>, double>{\n          {Direction<dim>::lower_xi(), 1.5},\n          {Direction<dim>::upper_xi(), 2.5},\n          {Direction<dim>::upper_zeta(), 3.5}},\n      std::unordered_map<Direction<dim>, double>{\n          {Direction<dim>::lower_eta(), 10.5},\n          {Direction<dim>::upper_eta(), 20.5},\n          {Direction<dim>::lower_zeta(), 30.5}},\n      NoCopy<1>{}, std::move(internal_nocopy_map_item),\n      std::move(external_nocopy_map_item),\n      std::unordered_set<Direction<dim>>{Direction<dim>::upper_xi()},\n      std::unordered_map<Direction<dim>, double>{\n          {Direction<dim>::upper_xi(), 4.5}});\n\n  CHECK(get<Tags::BoundaryDirectionsInterior<dim>>(box) ==\n        std::unordered_set<Direction<dim>>{Direction<dim>::lower_eta(),\n                                           Direction<dim>::upper_eta(),\n                                           Direction<dim>::lower_zeta()});\n\n  CHECK(\n      (get<Tags::Interface<internal_directions, Tags::Direction<dim>>>(box)) ==\n      (std::unordered_map<Direction<dim>, Direction<dim>>{\n          {Direction<dim>::lower_xi(), Direction<dim>::lower_xi()},\n          {Direction<dim>::upper_xi(), Direction<dim>::upper_xi()},\n          {Direction<dim>::upper_zeta(), Direction<dim>::upper_zeta()}}));\n\n  CHECK(get<Tags::BoundaryDirectionsInterior<dim>>(box) ==\n        get<Tags::BoundaryDirectionsExterior<dim>>(box));\n\n  CHECK(get<TestTags::Negate<TestTags::Int>>(box) == -5);\n\n  CHECK((get<Tags::Interface<templated_directions, TestTags::Double>>(box)) ==\n        (std::unordered_map<Direction<dim>, double>{\n            {Direction<dim>::upper_xi(), 4.5}}));\n\n  CHECK((get<Tags::Interface<internal_directions,\n                             TestTags::Negate<TestTags::Double>>>(box)) ==\n        (std::unordered_map<Direction<dim>, double>{\n            {Direction<dim>::lower_xi(), -1.5},\n            {Direction<dim>::upper_xi(), -2.5},\n            {Direction<dim>::upper_zeta(), -3.5}}));\n\n  CHECK(\n      (get<Tags::Interface<internal_directions, TestTags::NegateDoubleAddInt>>(\n          box)) == (std::unordered_map<Direction<dim>, double>{\n                       {Direction<dim>::lower_xi(), -1.5 + 5.0},\n                       {Direction<dim>::upper_xi(), -2.5 + 5.0},\n                       {Direction<dim>::upper_zeta(), -3.5 + 5.0}}));\n\n  CHECK((get<Tags::Interface<boundary_directions_interior,\n                             TestTags::Negate<TestTags::Double>>>(box)) ==\n        (std::unordered_map<Direction<dim>, double>{\n            {Direction<dim>::lower_eta(), -10.5},\n            {Direction<dim>::upper_eta(), -20.5},\n            {Direction<dim>::lower_zeta(), -30.5}}));\n\n  CHECK((get<Tags::Interface<boundary_directions_interior,\n                             TestTags::NegateDoubleAddInt>>(box)) ==\n        (std::unordered_map<Direction<dim>, double>{\n            {Direction<dim>::lower_eta(), -10.5 + 5.0},\n            {Direction<dim>::upper_eta(), -20.5 + 5.0},\n            {Direction<dim>::lower_zeta(), -30.5 + 5.0}}));\n\n  CHECK((get<Tags::Interface<internal_directions, TestTags::Int>>(box)) ==\n        (std::unordered_map<Direction<dim>, int>{\n            {Direction<dim>::lower_xi(), 8},\n            {Direction<dim>::upper_xi(), 8},\n            {Direction<dim>::upper_zeta(), 8}}));\n\n  CHECK((get<Tags::Interface<boundary_directions_interior, TestTags::Int>>(\n            box)) == (std::unordered_map<Direction<dim>, int>{\n                         {Direction<dim>::lower_eta(), 8},\n                         {Direction<dim>::upper_eta(), 8},\n                         {Direction<dim>::lower_zeta(), 8}}));\n\n  CHECK((get<Tags::Interface<internal_directions, TestTags::ComplexItem<dim>>>(\n            box)) ==\n        (std::unordered_map<Direction<dim>, std::pair<int, double>>{\n            {Direction<dim>::lower_xi(), {5, 1.5}},\n            {Direction<dim>::upper_xi(), {5, 2.5}},\n            {Direction<dim>::upper_zeta(), {5, 3.5}}}));\n\n  CHECK((get<typename Tags::InterfaceCompute<\n             boundary_directions_interior,\n             TestTags::ComplexItemCompute<dim>>::base>(box)) ==\n        (std::unordered_map<Direction<dim>, std::pair<int, double>>{\n            {Direction<dim>::lower_eta(), {5, 10.5}},\n            {Direction<dim>::upper_eta(), {5, 20.5}},\n            {Direction<dim>::lower_zeta(), {5, 30.5}}}));\n\n  CHECK((get<Tags::Interface<templated_directions,\n                             TestTags::Negate<TestTags::Double>>>(box)) ==\n        (std::unordered_map<Direction<dim>, double>{\n            {Direction<dim>::upper_xi(), -4.5}}));\n\n  CHECK(\n      (get<Tags::Interface<templated_directions, TestTags::NegateDoubleAddInt>>(\n          box)) == (std::unordered_map<Direction<dim>, double>{\n                       {Direction<dim>::upper_xi(), -4.5 + 5.0}}));\n}\n\nconstexpr size_t dim = 2;\n\nstruct Dirs : db::SimpleTag {\n  static constexpr size_t volume_dim = dim;\n  using type = std::unordered_set<Direction<dim>>;\n};\n\nstruct DirsCompute : Dirs, db::ComputeTag {\n  static void function(\n      const gsl::not_null<std::unordered_set<Direction<dim>>*> result) {\n    *result = std::unordered_set<Direction<dim>>{Direction<dim>::lower_xi(),\n                                                 Direction<dim>::upper_eta()};\n  }\n  using argument_tags = tmpl::list<>;\n  using base = Dirs;\n  using return_type = std::unordered_set<Direction<dim>>;\n};\n\ntemplate <size_t N>\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static constexpr bool should_be_sliced_to_boundary =\n      N == 3 or N == 30 or  // sliced_simple_item_tag below\n      N == 2 or N == 20;    // sliced_compute_item_tag below\n};\n\ntemplate <size_t N>\nstruct VarPlusFive : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t N>\nstruct VarPlusFiveCompute : VarPlusFive<N>, db::ComputeTag {\n  static void function(const gsl::not_null<Scalar<DataVector>*> result,\n                       const Scalar<DataVector>& var) {\n    *result = Scalar<DataVector>{get(var) + 5.0};\n  }\n  using argument_tags = tmpl::list<Var<N>>;\n  using base = VarPlusFive<N>;\n  using return_type = Scalar<DataVector>;\n};\n\ntemplate <size_t VolumeDim>\nstruct Compute : db::SimpleTag {\n  using type = Variables<tmpl::list<Var<VolumeDim>, Var<10 * VolumeDim>>>;\n};\n\ntemplate <size_t VolumeDim>\nstruct ComputeCompute : Compute<VolumeDim>, db::ComputeTag {\n  using base = Compute<VolumeDim>;\n  using return_type =\n      Variables<tmpl::list<Var<VolumeDim>, Var<10 * VolumeDim>>>;\n  static void function(const gsl::not_null<return_type*> result,\n                       const Mesh<VolumeDim>& mesh) {\n    *result = Variables<tmpl::list<Var<VolumeDim>, Var<10 * VolumeDim>>>(\n        mesh.number_of_grid_points(), VolumeDim);\n    get(get<Var<10 * VolumeDim>>(*result)) *= 5.0;\n  }\n  using argument_tags = tmpl::list<Tags::Mesh<VolumeDim>>;\n};\n\ntemplate <size_t N>\nauto make_interface_variables(const DataVector& value_xi,\n                              const DataVector& value_eta) {\n  const auto make = [](const DataVector& value) {\n    Variables<tmpl::list<Var<N>>> v(value.size());\n    get(get<Var<N>>(v)) = value;\n    return v;\n  };\n  std::unordered_map<Direction<dim>, Variables<tmpl::list<Var<N>>>> ret;\n  ret.emplace(Direction<dim>::lower_xi(), make(value_xi));\n  ret.emplace(Direction<dim>::upper_eta(), make(value_eta));\n  return ret;\n}\n\ntemplate <size_t N0, size_t N1>\nauto make_interface_variables(const DataVector& value_xi0,\n                              const DataVector& value_xi1,\n                              const DataVector& value_eta0,\n                              const DataVector& value_eta1) {\n  const auto make = [](const DataVector& value0, const DataVector& value1) {\n    Variables<tmpl::list<Var<N0>, Var<N1>>> v(value0.size());\n    get(get<Var<N0>>(v)) = value0;\n    get(get<Var<N1>>(v)) = value1;\n    return v;\n  };\n  std::unordered_map<Direction<dim>, Variables<tmpl::list<Var<N0>, Var<N1>>>>\n      ret;\n  ret.emplace(Direction<dim>::lower_xi(), make(value_xi0, value_xi1));\n  ret.emplace(Direction<dim>::upper_eta(), make(value_eta0, value_eta1));\n  return ret;\n}\n\nauto make_interface_tensor(DataVector value_xi, DataVector value_eta) {\n  std::unordered_map<Direction<dim>, Scalar<DataVector>> ret;\n  ret.emplace(Direction<dim>::lower_xi(),\n              Scalar<DataVector>(std::move(value_xi)));\n  ret.emplace(Direction<dim>::upper_eta(),\n              Scalar<DataVector>(std::move(value_eta)));\n  return ret;\n}\n\nvoid test_interface_subitems() {\n  const Mesh<dim> mesh{\n      {{4, 3}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n  const DataVector boundary_vars_xi{10., 11., 12.};\n  const DataVector boundary_vars_eta{20., 21., 22., 23.};\n\n  const auto volume_var = [&mesh]() {\n    Variables<tmpl::list<Var<2>>> volume_data(mesh.number_of_grid_points());\n    for (size_t i = 0; i < mesh.number_of_grid_points(); ++i) {\n      get(get<Var<2>>(volume_data))[i] = i;\n    }\n    return volume_data;\n  }();\n\n  const auto volume_tensor = [&mesh]() {\n    DataVector result(mesh.number_of_grid_points());\n    for (size_t i = 0; i < mesh.number_of_grid_points(); ++i) {\n      result[i] = i;\n    }\n    return Scalar<DataVector>{result};\n  }();\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Tags::Mesh<dim>, ::Tags::Variables<tmpl::list<Var<2>>>, Var<3>,\n          Tags::Interface<Dirs, ::Tags::Variables<tmpl::list<Var<0>>>>>,\n      db::AddComputeTags<\n          DirsCompute, VarPlusFiveCompute<3>,\n          Tags::InterfaceCompute<Dirs, Tags::Direction<dim>>,\n          Tags::InterfaceCompute<Dirs, Tags::InterfaceMesh<dim>>,\n          Tags::InterfaceCompute<Dirs, ComputeCompute<1>>,\n          Tags::Slice<Dirs, ::Tags::Variables<tmpl::list<Var<2>>>>,\n          Tags::Slice<Dirs, Var<3>>, Tags::Slice<Dirs, VarPlusFive<3>>>>(\n      mesh, volume_var, volume_tensor,\n      make_interface_variables<0>(boundary_vars_xi, boundary_vars_eta));\n\n  CHECK((db::get<Tags::Interface<Dirs, Var<0>>>(box)) ==\n        make_interface_tensor(boundary_vars_xi, boundary_vars_eta));\n  CHECK((db::get<Tags::Interface<Dirs, Var<1>>>(box)) ==\n        make_interface_tensor({1., 1., 1.}, {1., 1., 1., 1.}));\n  CHECK((db::get<Tags::Interface<Dirs, Var<10>>>(box)) ==\n        make_interface_tensor({5., 5., 5.}, {5., 5., 5., 5.}));\n  CHECK((db::get<Tags::Interface<Dirs, Var<2>>>(box)) ==\n        make_interface_tensor({0., 4., 8.}, {8., 9., 10., 11.}));\n  CHECK((db::get<Tags::Interface<Dirs, Var<3>>>(box)) ==\n        make_interface_tensor({0., 4., 8.}, {8., 9., 10., 11.}));\n  CHECK((db::get<Tags::Interface<Dirs, VarPlusFive<3>>>(box)) ==\n        make_interface_tensor({5., 9., 13.}, {13., 14., 15., 16.}));\n\n  TestHelpers::db::test_compute_tag<::Tags::Subitem<\n      Tags::Interface<Dirs, Var<2>>,\n      Tags::Slice<Dirs, ::Tags::Variables<tmpl::list<Var<2>>>>>>(\n      \"Interface<Dirs, Var>\");\n\n  TestHelpers::db::test_compute_tag<\n      ::Tags::Subitem<Tags::Interface<Dirs, Var<1>>,\n                      Tags::InterfaceCompute<Dirs, ComputeCompute<1>>>>(\n      \"Interface<Dirs, Var>\");\n\n  db::mutate<Tags::Interface<Dirs, Var<0>>>(\n      [](const auto boundary_tensor) {\n        get(boundary_tensor->at(Direction<dim>::lower_xi())) *= 3.;\n      },\n      make_not_null(&box));\n  CHECK((db::get<Tags::Interface<Dirs, Var<0>>>(box)) ==\n        make_interface_tensor(3. * boundary_vars_xi, boundary_vars_eta));\n}\n\nusing simple_item_tag = ::Tags::Variables<tmpl::list<Var<0>>>;\nusing compute_item_tag = ComputeCompute<1>;\nusing sliced_simple_item_tag = ::Tags::Variables<tmpl::list<Var<3>>>;\n\nvoid test_interface_slice(){\n  const Mesh<dim> mesh{\n      {{4, 3}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n  auto volume_tensor = [&mesh]() {\n    DataVector result(mesh.number_of_grid_points());\n    for (size_t i = 0; i < mesh.number_of_grid_points(); ++i) {\n      result[i] = 2.0 * static_cast<double>(i);\n    }\n    return Scalar<DataVector>{result};\n  }();\n  typename sliced_simple_item_tag::type volume_vars(\n      mesh.number_of_grid_points());\n  get<Var<3>>(volume_vars).get() =\n      DataVector{0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11.};\n  const DataVector boundary_vars_xi{10., 11., 12.};\n  const DataVector boundary_vars_eta{20., 21., 22., 23.};\n\n  ElementMap<2, Frame::Inertial> element_map(\n      ElementId<2>(0),\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Rotation<2>(atan2(4., 3.))));\n\n  const std::unordered_map<Direction<dim>, Mesh<dim - 1>>\n      expected_interface_mesh{\n          {Direction<dim>::lower_xi(),\n           Mesh<dim - 1>{3, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto}},\n          {Direction<dim>::upper_eta(),\n           Mesh<dim - 1>{4, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto}}};\n\n  const auto expected_boundary_coords = [&expected_interface_mesh,\n                                         &element_map]() {\n    std::unordered_map<Direction<dim>, tnsr::I<DataVector, dim>> coords{};\n    typename DirsCompute::return_type directions;\n    DirsCompute::function(make_not_null(&directions));\n    for (const auto& direction : directions) {\n      coords[direction] = element_map(interface_logical_coordinates(\n          expected_interface_mesh.at(direction), direction));\n    }\n    return coords;\n  }();\n\n  auto box = db::create<\n      db::AddSimpleTags<Tags::Mesh<dim>, Tags::ElementMap<dim>,\n                        sliced_simple_item_tag, Var<4>,\n                        Tags::Interface<Dirs, simple_item_tag>>,\n      db::AddComputeTags<\n          DirsCompute, ComputeCompute<2>, VarPlusFiveCompute<4>,\n          Tags::InterfaceCompute<Dirs, Tags::Direction<dim>>,\n          Tags::InterfaceCompute<Dirs, Tags::InterfaceMesh<dim>>,\n          Tags::InterfaceCompute<Dirs, compute_item_tag>,\n          Tags::InterfaceCompute<Dirs, Tags::BoundaryCoordinates<dim>>,\n          Tags::Slice<Dirs, Compute<2>>,\n          Tags::Slice<Dirs, sliced_simple_item_tag>, Tags::Slice<Dirs, Var<4>>,\n          Tags::Slice<Dirs, VarPlusFive<4>>>>(\n      mesh, std::move(element_map), std::move(volume_vars),\n      std::move(volume_tensor),\n      make_interface_variables<0>(boundary_vars_xi, boundary_vars_eta));\n\n  TestHelpers::db::test_simple_tag<Tags::Interface<Dirs, simple_item_tag>>(\n      \"Interface<Dirs, Variables(Var)>\");\n  TestHelpers::db::test_compute_tag<\n      Tags::InterfaceCompute<Dirs, Tags::InterfaceMesh<dim>>>(\n      \"Interface<Dirs, Mesh>\");\n  TestHelpers::db::test_compute_tag<\n      Tags::InterfaceCompute<Dirs, Tags::BoundaryCoordinates<dim>>>(\n      \"Interface<Dirs, InertialCoordinates>\");\n  TestHelpers::db::test_compute_tag<Tags::Slice<Dirs, Var<4>>>(\n      \"Interface<Dirs, Var>\");\n  TestHelpers::db::test_compute_tag<\n      Tags::InterfaceCompute<Dirs, Tags::Direction<dim>>>(\n      \"Interface<Dirs, Direction>\");\n  TestHelpers::db::test_compute_tag<Tags::InterfaceMesh<dim>>(\"Mesh\");\n  TestHelpers::db::test_compute_tag<Tags::BoundaryCoordinates<dim>>(\n      \"InertialCoordinates\");\n\n  CHECK((db::get<Tags::Interface<Dirs, simple_item_tag>>(box)) ==\n        make_interface_variables<0>(boundary_vars_xi, boundary_vars_eta));\n  CHECK((db::get<Tags::Interface<Dirs, Tags::Mesh<dim - 1>>>(box)) ==\n        expected_interface_mesh);\n  CHECK(db::get<Tags::Interface<Dirs, Tags::Coordinates<dim, Frame::Inertial>>>(\n            box) == expected_boundary_coords);\n  CHECK((db::get<typename Tags::InterfaceCompute<Dirs, compute_item_tag>::base>(\n            box)) ==\n        (make_interface_variables<1, 10>({1., 1., 1.}, {5., 5., 5.},\n                                         {1., 1., 1., 1.}, {5., 5., 5., 5.})));\n  CHECK((db::get<Tags::Interface<Dirs, Compute<2>>>(box)) ==\n        (make_interface_variables<2, 20>({2., 2., 2.}, {10., 10., 10.},\n                                         {2., 2., 2., 2.},\n                                         {10., 10., 10., 10.})));\n  CHECK((db::get<Tags::Interface<Dirs, sliced_simple_item_tag>>(box)) ==\n        make_interface_variables<3>({0., 4., 8.}, {8., 9., 10., 11.}));\n  CHECK((db::get<Tags::Interface<Dirs, Var<4>>>(box)) ==\n        make_interface_tensor({0., 8., 16.}, {16., 18., 20., 22.}));\n  CHECK((db::get<Tags::Interface<Dirs, VarPlusFive<4>>>(box)) ==\n        make_interface_tensor({5., 13., 21.}, {21., 23., 25., 27.}));\n}\n\ntemplate <size_t Dim>\nstruct Directions : db::SimpleTag {\n  static constexpr size_t volume_dim = Dim;\n  static std::string name() { return \"Directions\"; }\n  using type = std::unordered_set<Direction<Dim>>;\n};\n\ntemplate <size_t Dim>\nstd::unordered_set<Direction<Dim>> get_directions();\n\ntemplate <>\nstd::unordered_set<Direction<1>> get_directions<1>() {\n  return std::unordered_set<Direction<1>>{Direction<1>::upper_xi()};\n}\n\ntemplate <>\nstd::unordered_set<Direction<2>> get_directions<2>() {\n  return std::unordered_set<Direction<2>>{Direction<2>::upper_xi(),\n                                          Direction<2>::lower_eta()};\n}\n\ntemplate <>\nstd::unordered_set<Direction<3>> get_directions<3>() {\n  return std::unordered_set<Direction<3>>{Direction<3>::upper_xi(),\n                                          Direction<3>::lower_eta(),\n                                          Direction<3>::lower_zeta()};\n}\n\ntemplate <size_t Dim>\nvoid test_boundary_coordinates_moving_mesh_impl(\n    const ElementId<Dim>& element_id,\n    const ElementMap<Dim, Frame::Grid>& logical_to_grid_map,\n    const std::unique_ptr<CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>>&\n        grid_to_inertial_map,\n    const std::unique_ptr<\n        CoordinateMapBase<Frame::ElementLogical, Frame::Inertial, Dim>>&\n        logical_to_inertial_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  CAPTURE(Dim);\n  const Mesh<Dim> mesh{3, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  auto box = db::create<\n      db::AddSimpleTags<Tags::Mesh<Dim>, Directions<Dim>,\n                        Tags::ElementMap<Dim, Frame::Grid>,\n                        CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                            Frame::Inertial>,\n                        ::Tags::Time, Tags::FunctionsOfTimeInitialize>,\n      db::AddComputeTags<\n          Tags::InterfaceCompute<Directions<Dim>, Tags::Direction<Dim>>,\n          Tags::InterfaceCompute<Directions<Dim>, Tags::InterfaceMesh<Dim>>,\n          Tags::InterfaceCompute<Directions<Dim>,\n                                 Tags::BoundaryCoordinates<Dim, true>>>>(\n      mesh, get_directions<Dim>(),\n      ElementMap<Dim, Frame::Grid>(element_id,\n                                   logical_to_grid_map.block_map().get_clone()),\n      grid_to_inertial_map->get_clone(), time,\n      clone_unique_ptrs(functions_of_time));\n  for (const auto& direction_and_coordinates :\n       db::get<Tags::Interface<Directions<Dim>,\n                               Tags::Coordinates<Dim, Frame::Inertial>>>(box)) {\n    const auto& direction = direction_and_coordinates.first;\n    CHECK_ITERABLE_APPROX(\n        (*logical_to_inertial_map)(\n            interface_logical_coordinates(\n                mesh.slice_away(direction.dimension()), direction),\n            time, functions_of_time),\n        direction_and_coordinates.second);\n  }\n}\n\nvoid test_boundary_coordinates_moving_mesh() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n\n  const std::array<double, 4> times_to_check{{0.0, 0.3, 1.1, 7.8}};\n  const double outer_boundary = 10.0;\n  std::array<std::string, 2> functions_of_time_names{\n      {\"ExpansionA\", \"ExpansionB\"}};\n  using Polynomial = domain::FunctionsOfTime::PiecewisePolynomial<2>;\n  using FoftPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n  std::unordered_map<std::string, FoftPtr> functions_of_time{};\n  const std::array<DataVector, 3> init_func_a{{{1.0}, {-0.1}, {0.0}}};\n  const std::array<DataVector, 3> init_func_b{{{1.0}, {0.0}, {0.0}}};\n  const double initial_time = 0.0;\n  const double expiration_time = 10.0;\n  functions_of_time[\"ExpansionA\"] =\n      std::make_unique<Polynomial>(initial_time, init_func_a, expiration_time);\n  functions_of_time[\"ExpansionB\"] =\n      std::make_unique<Polynomial>(initial_time, init_func_b, expiration_time);\n\n  const auto perform_checks = [&functions_of_time, &times_to_check](\n                                  const auto& element_id,\n                                  const auto& time_independent_map,\n                                  const auto& time_dependent_map) {\n    INFO(std::decay_t<decltype(element_id)>::volume_dim);\n    const ElementMap<std::decay_t<decltype(element_id)>::volume_dim,\n                     Frame::Grid>\n        logical_to_grid_map(\n            element_id,\n            make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                time_independent_map));\n    const auto grid_to_inertial_map =\n        make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            time_dependent_map);\n    const auto logical_to_inertial_map =\n        make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n            time_independent_map, time_dependent_map);\n\n    for (const double time : times_to_check) {\n      test_boundary_coordinates_moving_mesh_impl(\n          element_id, logical_to_grid_map, grid_to_inertial_map,\n          logical_to_inertial_map, time, functions_of_time);\n    }\n  };\n\n  perform_checks(ElementId<1>(0), CoordinateMaps::Affine{-1.0, 1.0, 2.0, 7.8},\n                 CoordinateMaps::TimeDependent::CubicScale<1>{\n                     outer_boundary, functions_of_time_names[0],\n                     functions_of_time_names[1]});\n  perform_checks(ElementId<2>(0), CoordinateMaps::Rotation<2>{atan2(4., 3.)},\n                 CoordinateMaps::TimeDependent::CubicScale<2>{\n                     outer_boundary, functions_of_time_names[0],\n                     functions_of_time_names[1]});\n  perform_checks(\n      ElementId<3>(0),\n      CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                     CoordinateMaps::Rotation<2>>{\n          {-1., 1., 2., 7.}, CoordinateMaps::Rotation<2>(atan2(4., 3.))},\n      CoordinateMaps::TimeDependent::CubicScale<3>{outer_boundary,\n                                                   functions_of_time_names[0],\n                                                   functions_of_time_names[1]});\n}\n\nstruct SimpleBase : db::SimpleTag {\n  using type = int;\n};\n\nstruct ComputeBase : db::SimpleTag {\n  using type = double;\n};\n\nstruct ComputeDerived : ComputeBase, db::ComputeTag {\n  using base = ComputeBase;\n  using return_type = double;\n  static void function(const gsl::not_null<double*> result, const int& arg) {\n    *result = arg + 1.5;\n  }\n  using argument_tags = tmpl::list<SimpleBase>;\n};\n\nvoid test_interface_base_tags() {\n  const auto interface = [](const auto xi_value, const auto eta_value) {\n    return std::unordered_map<Direction<2>, std::decay_t<decltype(xi_value)>>{\n        {Direction<2>::lower_xi(), xi_value},\n        {Direction<2>::upper_eta(), eta_value}};\n  };\n  const auto box = db::create<\n      db::AddSimpleTags<Tags::Interface<Dirs, SimpleBase>>,\n      db::AddComputeTags<DirsCompute,\n                         Tags::InterfaceCompute<Dirs, ComputeDerived>>>(\n      interface(4, 5));\n  CHECK(get<Tags::Interface<Dirs, SimpleBase>>(box) == interface(4, 5));\n  CHECK(get<Tags::Interface<Dirs, ComputeBase>>(box) == interface(5.5, 6.5));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.InterfaceItems\", \"[Unit][Domain]\") {\n  test_interface_items();\n  test_interface_subitems();\n  test_interface_slice();\n  test_boundary_coordinates_moving_mesh();\n  test_interface_base_tags();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_InterfaceLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace domain {\nSPECTRE_TEST_CASE(\"Unit.Domain.InterfaceLogicalCoordinates\", \"[Domain][Unit]\") {\n  using Affine2d = CoordinateMaps::ProductOf2Maps<CoordinateMaps::Affine,\n                                                  CoordinateMaps::Affine>;\n  using Affine3d = CoordinateMaps::ProductOf3Maps<\n      CoordinateMaps::Affine, CoordinateMaps::Affine, CoordinateMaps::Affine>;\n\n  const CoordinateMaps::Affine x_map{-1.0, 1.0, -3.0, 7.0};\n  const CoordinateMaps::Affine y_map{-1.0, 1.0, -13.0, 47.0};\n  const CoordinateMaps::Affine z_map{-1.0, 1.0, -32.0, 74.0};\n\n  const auto map_1d = make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n      CoordinateMaps::Affine{x_map});\n  const auto map_2d = make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n      Affine2d{x_map, y_map});\n  const auto map_3d = make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n      Affine3d{x_map, y_map, z_map});\n\n  const Mesh<0> mesh_1d_xbdry;\n\n  const auto x_1d_lb = map_1d(\n      interface_logical_coordinates(mesh_1d_xbdry, Direction<1>::lower_xi()));\n\n  CHECK(x_1d_lb[0][0] == -3.0);\n\n  const auto x_1d_ub = map_1d(\n      interface_logical_coordinates(mesh_1d_xbdry, Direction<1>::upper_xi()));\n\n  CHECK(x_1d_ub[0][0] == 7.0);\n\n  const Mesh<1> mesh_2d_xbdry{3, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n\n  const auto x_2d_lb_xi = map_2d(\n      interface_logical_coordinates(mesh_2d_xbdry, Direction<2>::lower_xi()));\n\n  CHECK(x_2d_lb_xi[0][0] == -3.0);\n  CHECK(x_2d_lb_xi[0][1] == -3.0);\n  CHECK(x_2d_lb_xi[0][2] == -3.0);\n\n  CHECK(x_2d_lb_xi[1][0] == -13.0);\n  CHECK(x_2d_lb_xi[1][1] == 17.0);\n  CHECK(x_2d_lb_xi[1][2] == 47.0);\n\n  const auto x_2d_ub_xi = map_2d(\n      interface_logical_coordinates(mesh_2d_xbdry, Direction<2>::upper_xi()));\n\n  CHECK(x_2d_ub_xi[0][0] == 7.0);\n  CHECK(x_2d_ub_xi[0][1] == 7.0);\n  CHECK(x_2d_ub_xi[0][2] == 7.0);\n\n  CHECK(x_2d_ub_xi[1][0] == -13.0);\n  CHECK(x_2d_ub_xi[1][1] == 17.0);\n  CHECK(x_2d_ub_xi[1][2] == 47.0);\n\n  const Mesh<1> mesh_2d_ybdry{2, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n\n  const auto x_2d_lb_eta = map_2d(\n      interface_logical_coordinates(mesh_2d_ybdry, Direction<2>::lower_eta()));\n\n  CHECK(x_2d_lb_eta[0][0] == -3.0);\n  CHECK(x_2d_lb_eta[0][1] == 7.0);\n\n  CHECK(x_2d_lb_eta[1][0] == -13.0);\n  CHECK(x_2d_lb_eta[1][1] == -13.0);\n\n  const auto x_2d_ub_eta = map_2d(\n      interface_logical_coordinates(mesh_2d_ybdry, Direction<2>::upper_eta()));\n\n  CHECK(x_2d_ub_eta[0][0] == -3.0);\n  CHECK(x_2d_ub_eta[0][1] == 7.0);\n\n  CHECK(x_2d_ub_eta[1][0] == 47.0);\n  CHECK(x_2d_ub_eta[1][1] == 47.0);\n\n  const Mesh<2> mesh_3d_xbdry{\n      {{3, 2}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n  const auto x_3d_lb_xi = map_3d(\n      interface_logical_coordinates(mesh_3d_xbdry, Direction<3>::lower_xi()));\n\n  CHECK(x_3d_lb_xi[0][0] == -3.0);\n  CHECK(x_3d_lb_xi[0][1] == -3.0);\n  CHECK(x_3d_lb_xi[0][2] == -3.0);\n  CHECK(x_3d_lb_xi[0][3] == -3.0);\n  CHECK(x_3d_lb_xi[0][4] == -3.0);\n  CHECK(x_3d_lb_xi[0][5] == -3.0);\n\n  CHECK(x_3d_lb_xi[1][0] == -13.0);\n  CHECK(x_3d_lb_xi[1][1] == 17.0);\n  CHECK(x_3d_lb_xi[1][2] == 47.0);\n  CHECK(x_3d_lb_xi[1][3] == -13.0);\n  CHECK(x_3d_lb_xi[1][4] == 17.0);\n  CHECK(x_3d_lb_xi[1][5] == 47.0);\n\n  CHECK(x_3d_lb_xi[2][0] == -32.0);\n  CHECK(x_3d_lb_xi[2][1] == -32.0);\n  CHECK(x_3d_lb_xi[2][2] == -32.0);\n  CHECK(x_3d_lb_xi[2][3] == 74.0);\n  CHECK(x_3d_lb_xi[2][4] == 74.0);\n  CHECK(x_3d_lb_xi[2][5] == 74.0);\n\n  const auto x_3d_ub_xi = map_3d(\n      interface_logical_coordinates(mesh_3d_xbdry, Direction<3>::upper_xi()));\n\n  CHECK(x_3d_ub_xi[0][0] == 7.0);\n  CHECK(x_3d_ub_xi[0][1] == 7.0);\n  CHECK(x_3d_ub_xi[0][2] == 7.0);\n  CHECK(x_3d_ub_xi[0][3] == 7.0);\n  CHECK(x_3d_ub_xi[0][4] == 7.0);\n  CHECK(x_3d_ub_xi[0][5] == 7.0);\n\n  CHECK(x_3d_ub_xi[1][0] == -13.0);\n  CHECK(x_3d_ub_xi[1][1] == 17.0);\n  CHECK(x_3d_ub_xi[1][2] == 47.0);\n  CHECK(x_3d_ub_xi[1][3] == -13.0);\n  CHECK(x_3d_ub_xi[1][4] == 17.0);\n  CHECK(x_3d_ub_xi[1][5] == 47.0);\n\n  CHECK(x_3d_ub_xi[2][0] == -32.0);\n  CHECK(x_3d_ub_xi[2][1] == -32.0);\n  CHECK(x_3d_ub_xi[2][2] == -32.0);\n  CHECK(x_3d_ub_xi[2][3] == 74.0);\n  CHECK(x_3d_ub_xi[2][4] == 74.0);\n  CHECK(x_3d_ub_xi[2][5] == 74.0);\n\n  const Mesh<2> mesh_3d_ybdry{\n      {{5, 2}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n  const auto x_3d_lb_eta = map_3d(\n      interface_logical_coordinates(mesh_3d_ybdry, Direction<3>::lower_eta()));\n\n  CHECK(x_3d_lb_eta[0][0] == -3.0);\n  CHECK(x_3d_lb_eta[0][2] == 2.0);\n  CHECK(x_3d_lb_eta[0][4] == 7.0);\n  CHECK(x_3d_lb_eta[0][5] == -3.0);\n  CHECK(x_3d_lb_eta[0][7] == 2.0);\n  CHECK(x_3d_lb_eta[0][9] == 7.0);\n\n  CHECK(x_3d_lb_eta[1][0] == -13.0);\n  CHECK(x_3d_lb_eta[1][2] == -13.0);\n  CHECK(x_3d_lb_eta[1][4] == -13.0);\n  CHECK(x_3d_lb_eta[1][5] == -13.0);\n  CHECK(x_3d_lb_eta[1][7] == -13.0);\n  CHECK(x_3d_lb_eta[1][9] == -13.0);\n\n  CHECK(x_3d_lb_eta[2][0] == -32.0);\n  CHECK(x_3d_lb_eta[2][2] == -32.0);\n  CHECK(x_3d_lb_eta[2][4] == -32.0);\n  CHECK(x_3d_lb_eta[2][5] == 74.0);\n  CHECK(x_3d_lb_eta[2][7] == 74.0);\n  CHECK(x_3d_lb_eta[2][9] == 74.0);\n\n  const auto x_3d_ub_eta = map_3d(\n      interface_logical_coordinates(mesh_3d_ybdry, Direction<3>::upper_eta()));\n\n  CHECK(x_3d_ub_eta[0][0] == -3.0);\n  CHECK(x_3d_ub_eta[0][2] == 2.0);\n  CHECK(x_3d_ub_eta[0][4] == 7.0);\n  CHECK(x_3d_ub_eta[0][5] == -3.0);\n  CHECK(x_3d_ub_eta[0][7] == 2.0);\n  CHECK(x_3d_ub_eta[0][9] == 7.0);\n\n  CHECK(x_3d_ub_eta[1][0] == 47.0);\n  CHECK(x_3d_ub_eta[1][2] == 47.0);\n  CHECK(x_3d_ub_eta[1][4] == 47.0);\n  CHECK(x_3d_ub_eta[1][5] == 47.0);\n  CHECK(x_3d_ub_eta[1][7] == 47.0);\n  CHECK(x_3d_ub_eta[1][9] == 47.0);\n\n  CHECK(x_3d_ub_eta[2][0] == -32.0);\n  CHECK(x_3d_ub_eta[2][2] == -32.0);\n  CHECK(x_3d_ub_eta[2][4] == -32.0);\n  CHECK(x_3d_ub_eta[2][5] == 74.0);\n  CHECK(x_3d_ub_eta[2][7] == 74.0);\n  CHECK(x_3d_ub_eta[2][9] == 74.0);\n\n  // [interface_logical_coordinates_example]\n  const Mesh<2> mesh_3d_zbdry{\n      {{5, 3}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n  const auto x_3d_lb_zeta = map_3d(\n      interface_logical_coordinates(mesh_3d_zbdry, Direction<3>::lower_zeta()));\n  // [interface_logical_coordinates_example]\n\n  CHECK(x_3d_lb_zeta[0][0] == -3.0);\n  CHECK(x_3d_lb_zeta[0][2] == 2.0);\n  CHECK(x_3d_lb_zeta[0][4] == 7.0);\n  CHECK(x_3d_lb_zeta[0][5] == -3.0);\n  CHECK(x_3d_lb_zeta[0][7] == 2.0);\n  CHECK(x_3d_lb_zeta[0][9] == 7.0);\n  CHECK(x_3d_lb_zeta[0][10] == -3.0);\n  CHECK(x_3d_lb_zeta[0][12] == 2.0);\n  CHECK(x_3d_lb_zeta[0][14] == 7.0);\n\n  CHECK(x_3d_lb_zeta[1][0] == -13.0);\n  CHECK(x_3d_lb_zeta[1][2] == -13.0);\n  CHECK(x_3d_lb_zeta[1][4] == -13.0);\n  CHECK(x_3d_lb_zeta[1][5] == 17.0);\n  CHECK(x_3d_lb_zeta[1][7] == 17.0);\n  CHECK(x_3d_lb_zeta[1][9] == 17.0);\n  CHECK(x_3d_lb_zeta[1][10] == 47.0);\n  CHECK(x_3d_lb_zeta[1][12] == 47.0);\n  CHECK(x_3d_lb_zeta[1][14] == 47.0);\n\n  CHECK(x_3d_lb_zeta[2][0] == -32.0);\n  CHECK(x_3d_lb_zeta[2][2] == -32.0);\n  CHECK(x_3d_lb_zeta[2][4] == -32.0);\n  CHECK(x_3d_lb_zeta[2][5] == -32.0);\n  CHECK(x_3d_lb_zeta[2][7] == -32.0);\n  CHECK(x_3d_lb_zeta[2][9] == -32.0);\n  CHECK(x_3d_lb_zeta[2][10] == -32.0);\n  CHECK(x_3d_lb_zeta[2][12] == -32.0);\n  CHECK(x_3d_lb_zeta[2][14] == -32.0);\n\n  const auto x_3d_ub_zeta = map_3d(\n      interface_logical_coordinates(mesh_3d_zbdry, Direction<3>::upper_zeta()));\n\n  CHECK(x_3d_ub_zeta[0][0] == -3.0);\n  CHECK(x_3d_ub_zeta[0][2] == 2.0);\n  CHECK(x_3d_ub_zeta[0][4] == 7.0);\n  CHECK(x_3d_ub_zeta[0][5] == -3.0);\n  CHECK(x_3d_ub_zeta[0][7] == 2.0);\n  CHECK(x_3d_ub_zeta[0][9] == 7.0);\n  CHECK(x_3d_ub_zeta[0][10] == -3.0);\n  CHECK(x_3d_ub_zeta[0][12] == 2.0);\n  CHECK(x_3d_ub_zeta[0][14] == 7.0);\n\n  CHECK(x_3d_ub_zeta[1][0] == -13.0);\n  CHECK(x_3d_ub_zeta[1][2] == -13.0);\n  CHECK(x_3d_ub_zeta[1][4] == -13.0);\n  CHECK(x_3d_ub_zeta[1][5] == 17.0);\n  CHECK(x_3d_ub_zeta[1][7] == 17.0);\n  CHECK(x_3d_ub_zeta[1][9] == 17.0);\n  CHECK(x_3d_ub_zeta[1][10] == 47.0);\n  CHECK(x_3d_ub_zeta[1][12] == 47.0);\n  CHECK(x_3d_ub_zeta[1][14] == 47.0);\n\n  CHECK(x_3d_ub_zeta[2][0] == 74.0);\n  CHECK(x_3d_ub_zeta[2][2] == 74.0);\n  CHECK(x_3d_ub_zeta[2][4] == 74.0);\n  CHECK(x_3d_ub_zeta[2][5] == 74.0);\n  CHECK(x_3d_ub_zeta[2][7] == 74.0);\n  CHECK(x_3d_ub_zeta[2][9] == 74.0);\n  CHECK(x_3d_ub_zeta[2][10] == 74.0);\n  CHECK(x_3d_ub_zeta[2][12] == 74.0);\n  CHECK(x_3d_ub_zeta[2][14] == 74.0);\n\n  const Mesh<3> spherical_mesh{\n      {{4_st, 5_st, 9_st}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n        Spectral::Basis::SphericalHarmonic}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n        Spectral::Quadrature::Equiangular}}};\n  const auto volume_x_logical = logical_coordinates(spherical_mesh);\n  const Mesh<2> face_mesh = spherical_mesh.slice_away(0);\n  const auto lower_face_x_logical =\n      interface_logical_coordinates(face_mesh, Direction<3>::lower_xi());\n  const auto lower_sliced_volume_x_logical =\n      data_on_slice(volume_x_logical, spherical_mesh.extents(), 0_st, 0_st);\n  CHECK_ITERABLE_APPROX(lower_face_x_logical, lower_sliced_volume_x_logical);\n  const auto upper_face_x_logical =\n      interface_logical_coordinates(face_mesh, Direction<3>::upper_xi());\n  const auto upper_sliced_volume_x_logical =\n      data_on_slice(volume_x_logical, spherical_mesh.extents(), 0_st, 3_st);\n  CHECK_ITERABLE_APPROX(upper_face_x_logical, upper_sliced_volume_x_logical);\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_JacobianDiagnostic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Equiangular.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/JacobianDiagnostic.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nElementMap<Dim, Frame::Grid> jac_diag_map_that_fits();\n\ntemplate <>\nElementMap<1, Frame::Grid> jac_diag_map_that_fits() {\n  constexpr size_t dim = 1;\n  const auto segment_ids = std::array<SegmentId, dim>({{SegmentId(0, 0)}});\n  const ElementId<dim> element_id(0, segment_ids);\n  const domain::CoordinateMaps::Affine map{-1.0, 1.0, 0.4, 5.5};\n  return ElementMap<dim, Frame::Grid>{\n      element_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(map)};\n}\n\ntemplate <>\nElementMap<2, Frame::Grid> jac_diag_map_that_fits() {\n  constexpr size_t dim = 2;\n  const auto segment_ids = std::array<SegmentId, dim>({{SegmentId(0, 0)}});\n  const ElementId<dim> element_id(0, segment_ids);\n  const domain::CoordinateMaps::Rotation<2> map{M_PI_4};\n  return ElementMap<dim, Frame::Grid>{\n      element_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(map)};\n}\n\ntemplate <>\nElementMap<3, Frame::Grid> jac_diag_map_that_fits() {\n  constexpr size_t dim = 3;\n  const auto segment_ids = std::array<SegmentId, dim>({{SegmentId(0, 0)}});\n  const ElementId<dim> element_id(0, segment_ids);\n  const domain::CoordinateMaps::Rotation<3> map{M_PI_4, M_PI_2, M_PI_2};\n  return ElementMap<dim, Frame::Grid>{\n      element_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(map)};\n}\n\ntemplate <size_t Dim>\nElementMap<Dim, Frame::Grid> jac_diag_generic_map();\n\ntemplate <>\nElementMap<1, Frame::Grid> jac_diag_generic_map() {\n  constexpr size_t dim = 1;\n  const auto segment_ids = std::array<SegmentId, dim>({{SegmentId(0, 0)}});\n  const ElementId<dim> element_id(0, segment_ids);\n  const domain::CoordinateMaps::Equiangular map{-1.0, 1.0, 0.4, 5.5};\n  return ElementMap<dim, Frame::Grid>{\n      element_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(map)};\n}\n\ntemplate <>\nElementMap<2, Frame::Grid> jac_diag_generic_map() {\n  constexpr size_t dim = 2;\n  const auto segment_ids = std::array<SegmentId, dim>({{SegmentId(0, 0)}});\n  const ElementId<dim> element_id(0, segment_ids);\n  using Equiangular = domain::CoordinateMaps::Equiangular;\n  using Equiangular2D =\n      domain::CoordinateMaps::ProductOf2Maps<Equiangular, Equiangular>;\n  const Equiangular2D map{{-1.0, 1.0, 0.4, 5.5}, {-1.0, 1.0, -4.4, -0.2}};\n  return ElementMap<dim, Frame::Grid>{\n      element_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(map)};\n}\n\ntemplate <>\nElementMap<3, Frame::Grid> jac_diag_generic_map() {\n  constexpr size_t dim = 3;\n  const auto segment_ids = std::array<SegmentId, dim>({{SegmentId(0, 0)}});\n  const ElementId<dim> element_id(0, segment_ids);\n  using Equiangular = domain::CoordinateMaps::Equiangular;\n  using Equiangular3D =\n      domain::CoordinateMaps::ProductOf3Maps<Equiangular, Equiangular,\n                                             Equiangular>;\n  const Equiangular3D map{\n      {-1.0, 1.0, 0.4, 5.5}, {-1.0, 1.0, -4.4, -0.2}, {-1.0, 1.0, -5.0, 3.0}};\n  return ElementMap<dim, Frame::Grid>{\n      element_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(map)};\n}\n\ntemplate <size_t Dim, bool UseGenericMap>\nvoid test_jacobian_diagnostic_databox() {\n  if constexpr (UseGenericMap) {\n    TestHelpers::db::test_compute_tag<\n        domain::Tags::JacobianDiagnosticCompute<Dim, Frame::Grid>>(\n        \"JacobianDiagnostic\");\n  }\n\n  auto setup_databox = [](const size_t points_per_dimension) {\n    std::array<size_t, Dim> extents{};\n    for (size_t i = 0; i < Dim; ++i) {\n      extents[i] = points_per_dimension;\n    }\n    Mesh<Dim> mesh{extents, Spectral::Basis::Legendre,\n                   Spectral::Quadrature::GaussLobatto};\n    auto logical_coords = logical_coordinates(mesh);\n    auto map = UseGenericMap ? jac_diag_generic_map<Dim>()\n                             : jac_diag_map_that_fits<Dim>();\n\n    return db::create<\n        tmpl::list<domain::Tags::ElementMap<Dim, Frame::Grid>,\n                   domain::Tags::Coordinates<Dim, Frame::ElementLogical>,\n                   domain::Tags::Mesh<Dim>>,\n        db::AddComputeTags<\n            domain::Tags::MappedCoordinates<\n                domain::Tags::ElementMap<Dim, Frame::Grid>,\n                domain::Tags::Coordinates<Dim, Frame::ElementLogical>>,\n            domain::Tags::InverseJacobianCompute<\n                domain::Tags::ElementMap<Dim, Frame::Grid>,\n                domain::Tags::Coordinates<Dim, Frame::ElementLogical>>,\n            domain::Tags::JacobianCompute<Dim, Frame::ElementLogical,\n                                          Frame::Grid>,\n            domain::Tags::JacobianDiagnosticCompute<Dim, Frame::Grid>>>(\n        std::move(map), std::move(logical_coords), std::move(mesh));\n  };\n\n  const auto box = setup_databox(5);\n  const tnsr::i<DataVector, Dim, typename Frame::ElementLogical> jac_diag =\n      db::get<domain::Tags::JacobianDiagnostic<Dim>>(box);\n\n  if constexpr (UseGenericMap == false) {\n    const tnsr::i<DataVector, Dim, typename Frame::ElementLogical>\n        expected_jac_diag{jac_diag.begin()->size(), 0.0};\n    // Map fits in the resolution provided, so the diagnostic should be\n    // roundoff\n    for (size_t i_hat = 0; i_hat < Dim; ++i_hat) {\n      CHECK_ITERABLE_APPROX(jac_diag.get(i_hat), expected_jac_diag.get(i_hat));\n    }\n    return;\n  }\n\n  const auto box_high = setup_databox(7);\n  const tnsr::i<DataVector, Dim, typename Frame::ElementLogical> jac_diag_high =\n      db::get<domain::Tags::JacobianDiagnostic<Dim>>(box_high);\n  for (size_t i_hat = 0; i_hat < Dim; ++i_hat) {\n    // Check that higher resolution leads to significantly smaller diagnostic\n    CHECK(max(abs(jac_diag_high.get(i_hat))) <\n          0.125 * max(abs(jac_diag.get(i_hat))));\n  }\n\n  const auto jac =\n      db::get<domain::Tags::Jacobian<Dim, Frame::ElementLogical, Frame::Grid>>(\n          box);\n  const auto mapped_coords =\n      db::get<domain::Tags::Coordinates<Dim, Frame::Grid>>(box);\n  const auto mesh = db::get<domain::Tags::Mesh<Dim>>(box);\n  const auto jac_diag_by_value =\n      domain::jacobian_diagnostic(jac, mapped_coords, mesh);\n  CHECK_ITERABLE_APPROX(jac_diag, jac_diag_by_value);\n}\n\ntemplate <size_t Dim, typename Fr>\nvoid test_jacobian_diagnostic_random() {\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(\n          const gsl::not_null<\n              tnsr::i<DataVector, Dim, typename Frame::ElementLogical>*>,\n          const Jacobian<DataVector, Dim, typename Frame::ElementLogical, Fr>&,\n          const TensorMetafunctions::prepend_spatial_index<\n              tnsr::I<DataVector, Dim, Fr>, Dim, UpLo::Lo,\n              typename Frame::ElementLogical>&)>(\n          &domain::jacobian_diagnostic<Dim, Fr>),\n      \"JacobianDiagnostic\", {\"jacobian_diagnostic\"}, {{{-1.0, 1.0}}},\n      DataVector(5));\n}\n}  // namespace\nSPECTRE_TEST_CASE(\"Unit.Domain.JacobianDiagnostic\", \"[Domain][Unit]\") {\n  TestHelpers::db::test_simple_tag<domain::Tags::JacobianDiagnostic<1>>(\n      \"JacobianDiagnostic\");\n  TestHelpers::db::test_simple_tag<domain::Tags::JacobianDiagnostic<2>>(\n      \"JacobianDiagnostic\");\n  TestHelpers::db::test_simple_tag<domain::Tags::JacobianDiagnostic<3>>(\n      \"JacobianDiagnostic\");\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\"Domain/\"};\n\n  test_jacobian_diagnostic_databox<1, false>();\n  test_jacobian_diagnostic_databox<2, false>();\n  test_jacobian_diagnostic_databox<3, false>();\n  test_jacobian_diagnostic_databox<1, true>();\n  test_jacobian_diagnostic_databox<2, true>();\n  test_jacobian_diagnostic_databox<3, true>();\n\n  test_jacobian_diagnostic_random<1, Frame::Grid>();\n  test_jacobian_diagnostic_random<2, Frame::Grid>();\n  test_jacobian_diagnostic_random<3, Frame::Grid>();\n  test_jacobian_diagnostic_random<1, Frame::Inertial>();\n  test_jacobian_diagnostic_random<2, Frame::Inertial>();\n  test_jacobian_diagnostic_random<3, Frame::Inertial>();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_MinimumGridSpacing.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/MinimumGridSpacing.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename Frame>\nvoid check(const Matrix& transform, const double squeeze_factor) {\n  CAPTURE(transform);\n  const double large_segment = 3.0;\n  const double small_segment = 1.7;\n\n  Index<Dim> extents{};\n  Index<Dim> cells{};\n  for (size_t d = 0; d < Dim; ++d) {\n    extents[d] = 4 + d;\n    cells[d] = 3 + d;\n  }\n  for (IndexIterator<Dim> small_cell(cells); small_cell; ++small_cell) {\n    // Fill coordinates\n    tnsr::I<DataVector, Dim, Frame> untransformed_coordinates(\n        extents.product());\n    for (IndexIterator<Dim> point(extents); point; ++point) {\n      for (size_t d = 0; d < Dim; ++d) {\n        double& coordinate =\n            untransformed_coordinates.get(d)[point.collapsed_index()];\n        coordinate = large_segment * (*point)[d];\n        if ((*point)[d] > (*small_cell)[d]) {\n          coordinate -= large_segment - small_segment;\n        }\n      }\n    }\n    tnsr::I<DataVector, Dim, Frame> coordinates(extents.product(), 0.);\n    for (size_t d = 0; d < Dim; ++d) {\n      for (size_t d2 = 0; d2 < Dim; ++d2) {\n        coordinates.get(d) +=\n            transform(d, d2) * untransformed_coordinates.get(d2);\n      }\n    }\n\n    CHECK(minimum_grid_spacing(extents, coordinates) ==\n          approx(small_segment * squeeze_factor));\n  }\n}\n\ntemplate <typename Frame>\nvoid check_frame() {\n  check<1, Frame>(Matrix{{1.}}, 1.);\n  check<1, Frame>(Matrix{{-1.}}, 1.);\n  check<1, Frame>(Matrix{{0.1}}, 0.1);\n  check<1, Frame>(Matrix{{-0.1}}, 0.1);\n\n  check<2, Frame>(Matrix{{1.0, 0.0}, {0.0, 1.0}}, 1.0);\n  check<2, Frame>(Matrix{{1.0, 0.0}, {0.0, -1.0}}, 1.0);\n  check<2, Frame>(Matrix{{0.0, 1.0}, {1.0, 0.0}}, 1.0);\n  check<2, Frame>(Matrix{{0.5, 0.0}, {0.0, 1.0}}, 0.5);\n  check<2, Frame>(Matrix{{1.0, 0.0}, {0.0, 0.5}}, 0.5);\n  // Rotated grid\n  check<2, Frame>(Matrix{{1.0, 1.0}, {1.0, -1.0}}, sqrt(2.0));\n  // Triangular grid\n  check<2, Frame>(Matrix{{1.0, 0.5}, {0.0, sqrt(0.75)}}, 1.0);\n  // Squashed grid\n  check<2, Frame>(Matrix{{1.0, 1.0}, {0.0, 0.2}}, 0.2);\n  check<2, Frame>(Matrix{{1.0, -1.0}, {0.0, 0.2}}, 0.2);\n\n  check<3, Frame>(Matrix{{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}},\n                  1.0);\n  check<3, Frame>(Matrix{{-1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}},\n                  1.0);\n  check<3, Frame>(Matrix{{1.0, 0.0, 0.0}, {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0}},\n                  1.0);\n  check<3, Frame>(Matrix{{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, -1.0}},\n                  1.0);\n  check<3, Frame>(Matrix{{0.5, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}},\n                  0.5);\n  check<3, Frame>(Matrix{{1.0, 0.0, 0.0}, {0.0, 0.5, 1.0}, {0.0, 0.0, 1.0}},\n                  0.5);\n  check<3, Frame>(Matrix{{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 0.5}},\n                  0.5);\n  check<3, Frame>(\n      Matrix{{1.0, 1.0, -1.0}, {1.0, -1.0, 1.0}, {-1.0, 1.0, 1.0}}, sqrt(3.));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.MinimumGridSpacing\", \"[Domain][Unit]\") {\n  TestHelpers::db::test_simple_tag<\n      domain::Tags::MinimumGridSpacing<3, Frame::Inertial>>(\n      \"MinimumGridSpacing\");\n  TestHelpers::db::test_compute_tag<\n      domain::Tags::MinimumGridSpacingCompute<3, Frame::Inertial>>(\n      \"MinimumGridSpacing\");\n  check_frame<Frame::Grid>();\n  check_frame<Frame::Inertial>();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_RadiallyCompressedCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/RadiallyCompressedCoordinates.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n\nnamespace domain {\nnamespace {\n\nvoid test_radially_compressed_coordinates(\n    const CoordinateMaps::Distribution radial_distribution) {\n  CAPTURE(radial_distribution);\n  MAKE_GENERATOR(gen);\n  const double inner_radius = 10.;\n  const double outer_radius = 1.e9;\n  const double expected_compressed_outer_radius = 90.;\n  // Helper function that maps inertial to compressed radius for a single point\n  const auto compressed_radius =\n      [&inner_radius, &outer_radius,\n       &radial_distribution](const double inertial_radius) -> double {\n    return get<0>(radially_compressed_coordinates(\n        tnsr::I<double, 3, Frame::Inertial>{{{inertial_radius, 0., 0.}}},\n        inner_radius, outer_radius, radial_distribution));\n  };\n  {\n    INFO(\"Inner radius is identical\");\n    CHECK(compressed_radius(inner_radius) == approx(inner_radius));\n  }\n  {\n    INFO(\"Interior is identical\");\n    CHECK(compressed_radius(inner_radius / 2.) == approx(inner_radius / 2.));\n  }\n  {\n    INFO(\"Outer radius is compressed\");\n    CHECK(compressed_radius(outer_radius) ==\n          approx(expected_compressed_outer_radius));\n  }\n  {\n    INFO(\"Consistency with Wedge map\");\n    // Check that grid points are distributed linearly in radially compressed\n    // coordinates if the compression is consistent with the Wedge map\n    const auto wedge_map =\n        CoordinateMaps::Wedge<3>{inner_radius,\n                                 outer_radius,\n                                 1.0,\n                                 1.0,\n                                 OrientationMap<3>::create_aligned(),\n                                 true,\n                                 CoordinateMaps::Wedge<3>::WedgeHalves::Both,\n                                 radial_distribution};\n    // Set up a point at a fraction of the radial distance covered by the wedge\n    std::uniform_real_distribution<double> dist_fraction(0., 1.);\n    const double radial_fraction = dist_fraction(gen);\n    const double r_logical = -1. + radial_fraction * 2.;\n    const double r_inertial =\n        wedge_map(std::array<double, 3>{{0., 0., r_logical}})[2];\n    const double r_compressed = compressed_radius(r_inertial);\n    const double expected_r_compressed =\n        inner_radius +\n        radial_fraction * (expected_compressed_outer_radius - inner_radius);\n    CHECK(r_compressed == approx(expected_r_compressed));\n  }\n  {\n    INFO(\"Coordinates preserve angles\");\n    const size_t num_angles = 6;\n    // extend below inner radius to check identity there\n    std::uniform_real_distribution<double> dist_r(inner_radius / 2.,\n                                                  outer_radius);\n    std::uniform_real_distribution<double> dist_theta(0., M_PI);\n    std::uniform_real_distribution<double> dist_phi(-M_PI, M_PI);\n    const auto r = make_with_random_values<DataVector>(\n        make_not_null(&gen), make_not_null(&dist_r), DataVector(5));\n    const auto theta = make_with_random_values<DataVector>(\n        make_not_null(&gen), make_not_null(&dist_theta),\n        DataVector(num_angles));\n    const auto phi = make_with_random_values<DataVector>(\n        make_not_null(&gen), make_not_null(&dist_phi), DataVector(num_angles));\n    for (size_t i = 0; i < r.size(); ++i) {\n      tnsr::I<DataVector, 3, Frame::Inertial> x{num_angles};\n      get<0>(x) = r[i] * cos(phi) * sin(theta);\n      get<1>(x) = r[i] * sin(phi) * sin(theta);\n      get<2>(x) = r[i] * cos(theta);\n      const auto x_compressed = radially_compressed_coordinates(\n          x, inner_radius, outer_radius, radial_distribution);\n      const auto r_compressed = get(magnitude(x_compressed));\n      // Check all compressed radii are the same\n      CHECK_ITERABLE_APPROX(r_compressed,\n                            DataVector(num_angles, compressed_radius(r[i])));\n      // Check the angles didn't change\n      const auto theta_compressed =\n          atan2(hypot(get<0>(x_compressed), get<1>(x_compressed)),\n                get<2>(x_compressed));\n      const auto phi_compressed =\n          atan2(get<1>(x_compressed), get<0>(x_compressed));\n      CHECK_ITERABLE_APPROX(theta_compressed, theta);\n      CHECK_ITERABLE_APPROX(phi_compressed, phi);\n      // Also check some more consistency\n      if (r[i] <= inner_radius) {\n        CHECK(r_compressed[i] == approx(r[i]));\n      } else {\n        CHECK(r_compressed[i] < r[i]);\n        CHECK(r_compressed[i] <= expected_compressed_outer_radius + 1.e-14);\n      }\n    }\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.RadiallyCompressedCoordinates\",\n                  \"[Domain][Unit]\") {\n  TestHelpers::db::test_simple_tag<\n      Tags::RadiallyCompressedCoordinates<3, Frame::Inertial>>(\n      \"RadiallyCompressedCoordinates\");\n  TestHelpers::db::test_compute_tag<\n      Tags::RadiallyCompressedCoordinatesCompute<3, Frame::Inertial>>(\n      \"RadiallyCompressedCoordinates\");\n  test_radially_compressed_coordinates(\n      CoordinateMaps::Distribution::Logarithmic);\n  test_radially_compressed_coordinates(CoordinateMaps::Distribution::Inverse);\n}\n\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_SizeOfElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test_1d() {\n  INFO(\"1d\");\n  auto map =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Affine(-1.0, 1.0, 0.3, 1.2));\n  const ElementId<1> element_id(0, {{{2, 3}}});\n  const ElementMap<1, Frame::Inertial> element_map(element_id, std::move(map));\n  const auto size = size_of_element(element_map);\n  // for this affine map, expected size = width of block / number of elements\n  const auto size_expected = make_array<1>(0.225);\n  CHECK_ITERABLE_APPROX(size, size_expected);\n}\n\nvoid test_2d() {\n  INFO(\"2d\");\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  auto map =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine2D{Affine(-1.0, 1.0, 0.3, 0.4), Affine(-1.0, 1.0, -0.5, 1.2)},\n          domain::CoordinateMaps::DiscreteRotation<2>(\n              OrientationMap<2>{std::array<Direction<2>, 2>{\n                  {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}}));\n  const ElementId<2> element_id(0, {{{1, 1}, {2, 0}}});\n  const ElementMap<2, Frame::Inertial> element_map(element_id, std::move(map));\n  const auto size = size_of_element(element_map);\n  const auto size_expected = make_array(0.05, 0.425);\n  CHECK_ITERABLE_APPROX(size, size_expected);\n}\n\nvoid test_3d() {\n  INFO(\"3d\");\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  auto map =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D{Affine(-1.0, 1.0, 0.3, 0.4), Affine(-1.0, 1.0, -0.5, 1.2),\n                   Affine(-1.0, 1.0, 12.0, 12.5)},\n          domain::CoordinateMaps::Rotation<3>(0.7, 2.3, -0.4));\n  const ElementId<3> element_id(0, {{{3, 5}, {1, 0}, {2, 3}}});\n  const ElementMap<3, Frame::Inertial> element_map(element_id, std::move(map));\n  const auto size = size_of_element(element_map);\n  const auto size_expected = make_array(0.0125, 0.85, 0.125);\n  CHECK_ITERABLE_APPROX(size, size_expected);\n}\n\ntemplate <size_t Dim>\nusing CubicScale = domain::CoordinateMaps::TimeDependent::CubicScale<Dim>;\n\nstd::unordered_map<std::string,\n                   std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\nmake_single_expansion_functions_of_time() {\n  const double initial_time = 0.0;\n  const double expiration_time = 10.0;\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time.insert(std::make_pair(\n      \"Expansion\",\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time, std::array<DataVector, 3>{{{0.0}, {1.0}, {0.0}}},\n          expiration_time)));\n  return functions_of_time;\n}\n\nvoid test_1d_moving_mesh(const std::array<double, 4>& times_to_check) {\n  INFO(\"1d with moving mesh\");\n  auto map = domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n      domain::CoordinateMaps::Affine(-1.0, 1.0, 0.3, 1.2));\n  const ElementId<1> element_id(0, {{{2, 3}}});\n  const ElementMap<1, Frame::Grid> logical_to_grid_map(element_id,\n                                                       std::move(map));\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          CubicScale<1>{10.0, \"Expansion\", \"Expansion\"});\n  const std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time = make_single_expansion_functions_of_time();\n\n  for (const double time : times_to_check) {\n    const auto element_size = size_of_element(\n        logical_to_grid_map, grid_to_inertial_map, time, functions_of_time);\n    // for this affine map, expected size = width of block / number of\n    // elements\n    const auto expected_size_of_element = make_array<1>(0.225 * time);\n    CHECK_ITERABLE_APPROX(element_size, expected_size_of_element);\n  }\n}\n\nvoid test_2d_moving_mesh(const std::array<double, 4>& times_to_check) {\n  INFO(\"2d with moving mesh\");\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  auto map = domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n      Affine2D{Affine(-1.0, 1.0, 0.3, 0.4), Affine(-1.0, 1.0, -0.5, 1.2)},\n      domain::CoordinateMaps::DiscreteRotation<2>(\n          OrientationMap<2>{std::array<Direction<2>, 2>{\n              {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}}));\n  const ElementId<2> element_id(0, {{{1, 1}, {2, 0}}});\n  const ElementMap<2, Frame::Grid> logical_to_grid_map(element_id,\n                                                       std::move(map));\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          CubicScale<2>{10.0, \"Expansion\", \"Expansion\"});\n  const std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time = make_single_expansion_functions_of_time();\n\n  for (const double time : times_to_check) {\n    const auto element_size = size_of_element(\n        logical_to_grid_map, grid_to_inertial_map, time, functions_of_time);\n    const auto expected_size_of_element = make_array(0.05, 0.425) * time;\n    CHECK_ITERABLE_APPROX(element_size, expected_size_of_element);\n  }\n}\n\nvoid test_3d_moving_mesh(const std::array<double, 4>& times_to_check) {\n  INFO(\"3d with moving mesh\");\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  auto map = domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n      Affine3D{Affine(-1.0, 1.0, 0.3, 0.4), Affine(-1.0, 1.0, -0.5, 1.2),\n               Affine(-1.0, 1.0, 12.0, 12.5)},\n      domain::CoordinateMaps::Rotation<3>(0.7, 2.3, -0.4));\n  const ElementId<3> element_id(0, {{{3, 5}, {1, 0}, {2, 3}}});\n  const ElementMap<3, Frame::Grid> logical_to_grid_map(element_id,\n                                                       std::move(map));\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          CubicScale<3>{10.0, \"Expansion\", \"Expansion\"});\n  const std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time = make_single_expansion_functions_of_time();\n\n  for (const double time : times_to_check) {\n    const auto element_size = size_of_element(\n        logical_to_grid_map, grid_to_inertial_map, time, functions_of_time);\n    const auto expected_size_of_element =\n        make_array(0.0125, 0.85, 0.125) * time;\n    CHECK_ITERABLE_APPROX(element_size, expected_size_of_element);\n  }\n}\n\nvoid test_compute_tag(const std::array<double, 4>& times_to_check) {\n  INFO(\"compute tag in 2d, with moving mesh\");\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  auto map = domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n      Affine2D{Affine(-1.0, 1.0, 0.3, 0.4), Affine(-1.0, 1.0, -0.5, 1.2)},\n      domain::CoordinateMaps::DiscreteRotation<2>(\n          OrientationMap<2>{std::array<Direction<2>, 2>{\n              {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}}));\n  const ElementId<2> element_id(0, {{{1, 1}, {2, 0}}});\n  ElementMap<2, Frame::Grid> logical_to_grid_map(element_id, std::move(map));\n  auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          CubicScale<2>{10.0, \"Expansion\", \"Expansion\"});\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time = make_single_expansion_functions_of_time();\n\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::ElementMap<2, Frame::Grid>,\n                        domain::CoordinateMaps::Tags::CoordinateMap<\n                            2, Frame::Grid, Frame::Inertial>,\n                        ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize>,\n      db::AddComputeTags<domain::Tags::SizeOfElementCompute<2>>>(\n      std::move(logical_to_grid_map), std::move(grid_to_inertial_map), 0.0,\n      std::move(functions_of_time));\n\n  for (const double time : times_to_check) {\n    db::mutate<::Tags::Time>(\n        [time](const gsl::not_null<double*> time_ptr) { *time_ptr = time; },\n        make_not_null(&box));\n    const auto expected_size_of_element = make_array(0.05, 0.425) * time;\n    CHECK_ITERABLE_APPROX(db::get<domain::Tags::SizeOfElement<2>>(box),\n                          expected_size_of_element);\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_tags() {\n  TestHelpers::db::test_simple_tag<domain::Tags::SizeOfElement<Dim>>(\n      \"SizeOfElement\");\n  TestHelpers::db::test_compute_tag<domain::Tags::SizeOfElementCompute<Dim>>(\n      \"SizeOfElement\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.SizeOfElement\", \"[Domain][Unit]\") {\n  test_1d();\n  test_2d();\n  test_3d();\n\n  const std::array<double, 4> times_to_check{{0.0, 0.3, 1.1, 7.8}};\n  test_1d_moving_mesh(times_to_check);\n  test_2d_moving_mesh(times_to_check);\n  test_3d_moving_mesh(times_to_check);\n  test_compute_tag(times_to_check);\n\n  test_tags<1>();\n  test_tags<2>();\n  test_tags<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Domain/Test_StrahlkorperTransformations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/Shape.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/StrahlkorperTransformations.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <bool Aligned, typename SrcFrame, typename DestFrame>\nvoid test_strahlkorper_in_different_frame() {\n  const size_t grid_points_each_dimension = 5;\n\n  // Set up a Strahlkorper corresponding to a Schwarzschild hole of\n  // mass 1, in the source frame.\n  // Center the Strahlkorper at (0.03,0.02,0.01) so that we test a\n  // nonzero center.\n  const std::array<double, 3> strahlkorper_src_center = {0.03, 0.02, 0.01};\n  const size_t l_max = 8;\n  const ylm::Strahlkorper<SrcFrame> strahlkorper_src(l_max, 2.0,\n                                                     strahlkorper_src_center);\n\n  // Create a Domain.\n  // We choose a spherical shell domain extending from radius 1.9M to\n  // 2.9M, so that the Strahlkorper is inside the domain. It gives a\n  // narrow domain so that we don't need a large number of grid points\n  // to resolve the horizon (which would make the test slower).\n  std::vector<double> radial_partitioning{};\n  std::vector<domain::CoordinateMaps::Distribution> radial_distribution{\n      domain::CoordinateMaps::Distribution::Linear};\n  std::unique_ptr<domain::creators::Sphere> domain_creator;\n  if constexpr (Aligned) {\n    domain_creator.reset(new domain::creators::Sphere(\n        1.9, 2.9, domain::creators::Sphere::Excision{}, 1_st,\n        grid_points_each_dimension, false, std::nullopt, radial_partitioning,\n        radial_distribution, ShellWedges::All,\n        // Choose time dependence to be centered at the strahlkorper center.\n        std::make_unique<domain::creators::time_dependence::Shape<\n            domain::ObjectLabel::None>>(\n            0.0, l_max, 1.0, std::array<double, 3>{{0.1, 0.2, 0.3}},\n            strahlkorper_src_center, 0., 2.0, 12.0)));\n  } else {\n    domain_creator.reset(new domain::creators::Sphere(\n        1.9, 2.9, domain::creators::Sphere::Excision{}, 1_st,\n        grid_points_each_dimension, false, std::nullopt, radial_partitioning,\n        radial_distribution, ShellWedges::All,\n        std::make_unique<\n            domain::creators::time_dependence::UniformTranslation<3>>(\n            0.0, std::array<double, 3>({{0.0, 0.0, 0.0}}),\n            std::array<double, 3>({{0.01, 0.02, 0.03}}))));\n  }\n  Domain<3> domain = domain_creator->create_domain();\n  const auto functions_of_time = domain_creator->functions_of_time();\n\n  // Compute strahlkorper in the destination frame.\n  const double time = 0.5;\n  ylm::Strahlkorper<DestFrame> strahlkorper_dest{};\n  if constexpr (Aligned) {\n    strahlkorper_in_different_frame_aligned(make_not_null(&strahlkorper_dest),\n                                            strahlkorper_src, domain,\n                                            functions_of_time, time);\n\n  } else {\n    strahlkorper_in_different_frame(make_not_null(&strahlkorper_dest),\n                                    strahlkorper_src, domain, functions_of_time,\n                                    time);\n  }\n\n  // Now compare.\n  std::unique_ptr<ylm::Strahlkorper<DestFrame>> strahlkorper_expected;\n  if constexpr (Aligned) {\n    const ylm::Spherepack ylm{l_max, l_max};\n    const DataVector new_radius =\n        get(gr::Solutions::kerr_schild_radius_from_boyer_lindquist(\n            2.0, ylm.theta_phi_points(), 1.0,\n            std::array<double, 3>{{0.1, 0.2, 0.3}}));\n    strahlkorper_expected.reset(new ylm::Strahlkorper<DestFrame>(\n        l_max, l_max, new_radius, strahlkorper_src_center));\n  } else if constexpr (std::is_same_v<SrcFrame, ::Frame::Inertial>) {\n    strahlkorper_expected.reset(new ylm::Strahlkorper<DestFrame>(\n        l_max, 2.0,\n        {{strahlkorper_src_center[0] - 0.005, strahlkorper_src_center[1] - 0.01,\n          strahlkorper_src_center[2] - 0.015}}));\n  } else {\n    strahlkorper_expected.reset(new ylm::Strahlkorper<DestFrame>(\n        l_max, 2.0,\n        {{strahlkorper_src_center[0] + 0.005, strahlkorper_src_center[1] + 0.01,\n          strahlkorper_src_center[2] + 0.015}}));\n  }\n  CHECK_ITERABLE_APPROX(strahlkorper_expected->physical_center(),\n                        strahlkorper_dest.physical_center());\n  CHECK_ITERABLE_APPROX(strahlkorper_expected->coefficients(),\n                        strahlkorper_dest.coefficients());\n}\n\ntemplate <bool IsTimeDependent, typename SrcFrame>\nvoid test_strahlkorper_coords_in_different_frame() {\n  const size_t grid_points_each_dimension = 5;\n\n  // Set up a Strahlkorper corresponding to a Schwarzschild hole of\n  // mass 1, in the source frame.\n  // Center the Strahlkorper at (0.03,0.02,0.01) so that we test a\n  // nonzero center.\n  const std::array<double, 3> strahlkorper_src_center = {0.03, 0.02, 0.01};\n  const size_t l_max = 8;\n  const ylm::Strahlkorper<SrcFrame> strahlkorper_src(l_max, 2.0,\n                                                     strahlkorper_src_center);\n\n  // Create a Domain.\n  // We choose a spherical shell domain extending from radius 1.9M to\n  // 2.9M, so that the Strahlkorper is inside the domain. It gives a\n  // narrow domain so that we don't need a large number of grid points\n  // to resolve the horizon (which would make the test slower).\n  std::vector<double> radial_partitioning{};\n  std::vector<domain::CoordinateMaps::Distribution> radial_distribution{\n      domain::CoordinateMaps::Distribution::Linear};\n\n  std::unique_ptr<DomainCreator<3>> domain_creator;\n  if constexpr (IsTimeDependent) {\n    // In computing the time_dependence, make sure that src-to-inertial\n    // velocity is (0.01,0.02,0.03) to agree with the analytic checks\n    // below.  If src is distorted frame, then grid-to-distorted\n    // velocity doesn't matter for the value of the check (but does matter\n    // in terms of which points are in which blocks).\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dependence;\n    if constexpr (std::is_same_v<SrcFrame, ::Frame::Grid>) {\n      time_dependence = std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          0.0, std::array<double, 3>({{0.01, 0.02, 0.03}}));\n    } else {\n      static_assert(std::is_same_v<SrcFrame, ::Frame::Distorted>,\n                    \"Src frame must be Distorted if it is not Grid\");\n      time_dependence = std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          0.0, std::array<double, 3>({{-0.02, -0.01, -0.01}}),\n          std::array<double, 3>({{0.01, 0.02, 0.03}}));\n    }\n    domain_creator = std::make_unique<domain::creators::Sphere>(\n        1.9, 2.9, domain::creators::Sphere::Excision{}, 1_st,\n        grid_points_each_dimension, false, std::nullopt, radial_partitioning,\n        radial_distribution, ShellWedges::All, std::move(time_dependence));\n  } else {\n    domain_creator = std::make_unique<domain::creators::Sphere>(\n        1.9, 2.9, domain::creators::Sphere::Excision{}, 1_st,\n        grid_points_each_dimension, false, std::nullopt, radial_partitioning,\n        radial_distribution, ShellWedges::All);\n  }\n  Domain<3> domain = domain_creator->create_domain();\n  const auto functions_of_time = domain_creator->functions_of_time();\n\n  // Compute strahlkorper coords in the inertial frame.\n  const double time = 0.5;\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coords{};\n\n  strahlkorper_coords_in_different_frame(make_not_null(&inertial_coords),\n                                         strahlkorper_src, domain,\n                                         functions_of_time, time);\n\n  // Now compare with expected result, which is the src-frame coords of\n  // the Strahlkorper translated by (0.005,0.01,0.015).\n  const auto src_coords =\n      ylm::cartesian_coords(strahlkorper_src, ylm::radius(strahlkorper_src),\n                            ylm::rhat(ylm::theta_phi(strahlkorper_src)));\n  if constexpr (IsTimeDependent) {\n    CHECK_ITERABLE_APPROX(get<0>(src_coords) + 0.005, get<0>(inertial_coords));\n    CHECK_ITERABLE_APPROX(get<1>(src_coords) + 0.01, get<1>(inertial_coords));\n    CHECK_ITERABLE_APPROX(get<2>(src_coords) + 0.015, get<2>(inertial_coords));\n  } else {\n    CHECK_ITERABLE_APPROX(get<0>(src_coords), get<0>(inertial_coords));\n    CHECK_ITERABLE_APPROX(get<1>(src_coords), get<1>(inertial_coords));\n    CHECK_ITERABLE_APPROX(get<2>(src_coords), get<2>(inertial_coords));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.StrahlkorperTransformations\", \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  test_strahlkorper_in_different_frame<false, Frame::Grid, Frame::Inertial>();\n  test_strahlkorper_in_different_frame<false, Frame::Inertial,\n                                       Frame::Distorted>();\n  test_strahlkorper_in_different_frame<false, Frame::Inertial, Frame::Grid>();\n  test_strahlkorper_in_different_frame<true, Frame::Grid, Frame::Inertial>();\n  test_strahlkorper_in_different_frame<true, Frame::Grid, Frame::Distorted>();\n  test_strahlkorper_coords_in_different_frame<true, Frame::Grid>();\n  test_strahlkorper_coords_in_different_frame<true, Frame::Distorted>();\n  test_strahlkorper_coords_in_different_frame<false, Frame::Distorted>();\n}\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace domain {\nnamespace {\ntemplate <size_t Dim>\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<Tags::Element<Dim>>(\"Element\");\n  TestHelpers::db::test_simple_tag<Tags::Mesh<Dim>>(\"Mesh\");\n  TestHelpers::db::test_simple_tag<Tags::ElementMap<Dim>>(\n      \"ElementMap(Inertial)\");\n  TestHelpers::db::test_simple_tag<Tags::ElementMap<Dim, Frame::Grid>>(\n      \"ElementMap(Grid)\");\n  TestHelpers::db::test_simple_tag<Tags::Coordinates<Dim, Frame::Grid>>(\n      \"GridCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      Tags::Coordinates<Dim, Frame::ElementLogical>>(\n      \"ElementLogicalCoordinates\");\n  TestHelpers::db::test_simple_tag<Tags::Coordinates<Dim, Frame::Inertial>>(\n      \"InertialCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      Tags::InverseJacobian<Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"InverseJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>>(\n      \"DetInvJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::DetJacobian<Frame::ElementLogical, Frame::Inertial>>(\n      \"DetJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::DetTimesInvJacobian<Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"DetTimesInvJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_simple_tag<Tags::InternalDirections<Dim>>(\n      \"InternalDirections\");\n  TestHelpers::db::test_simple_tag<Tags::BoundaryDirectionsInterior<Dim>>(\n      \"BoundaryDirectionsInterior\");\n  TestHelpers::db::test_simple_tag<Tags::BoundaryDirectionsExterior<Dim>>(\n      \"BoundaryDirectionsExterior\");\n  TestHelpers::db::test_simple_tag<Tags::Direction<Dim>>(\"Direction\");\n  TestHelpers::db::test_simple_tag<\n      Tags::Jacobian<Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"Jacobian(ElementLogical,Inertial)\");\n}\n\ntemplate <size_t Dim>\nElementMap<Dim, Frame::Grid> element_map();\n\ntemplate <>\nElementMap<1, Frame::Grid> element_map() {\n  constexpr size_t dim = 1;\n  const auto segment_ids = std::array<SegmentId, dim>({{SegmentId(2, 3)}});\n  const CoordinateMaps::Affine first_map{-3.0, 8.7, 0.4, 5.5};\n  const CoordinateMaps::Affine second_map{1.0, 8.0, -2.5, -1.0};\n  const ElementId<dim> element_id(0, segment_ids);\n  return ElementMap<dim, Frame::Grid>{\n      element_id, make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                      first_map, second_map)};\n}\n\ntemplate <>\nElementMap<2, Frame::Grid> element_map() {\n  constexpr size_t dim = 2;\n  const auto segment_ids =\n      std::array<SegmentId, dim>({{SegmentId(2, 3), SegmentId(1, 0)}});\n  const CoordinateMaps::Rotation<dim> first_map(1.6);\n  const CoordinateMaps::Rotation<dim> second_map(3.1);\n  const ElementId<dim> element_id(0, segment_ids);\n  return ElementMap<dim, Frame::Grid>{\n      element_id, make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                      first_map, second_map)};\n}\n\ntemplate <>\nElementMap<3, Frame::Grid> element_map() {\n  constexpr size_t dim = 3;\n  const auto segment_ids = std::array<SegmentId, dim>(\n      {{SegmentId(2, 3), SegmentId(1, 0), SegmentId(2, 1)}});\n  const CoordinateMaps::Rotation<dim> first_map{M_PI_4, M_PI_4, M_PI_2};\n  const CoordinateMaps::Rotation<dim> second_map{M_PI_4, M_PI_2, M_PI_2};\n  const ElementId<dim> element_id(0, segment_ids);\n  return ElementMap<dim, Frame::Grid>{\n      element_id, make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                      first_map, second_map)};\n}\n\ntemplate <size_t Dim>\nvoid test_compute_tags() {\n  TestHelpers::db::test_compute_tag<Tags::InverseJacobianCompute<\n      Tags::ElementMap<Dim>, Tags::Coordinates<Dim, Frame::ElementLogical>>>(\n      \"InverseJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_compute_tag<\n      Tags::DetInvJacobianCompute<Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"DetInvJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_compute_tag<Tags::InternalDirectionsCompute<Dim>>(\n      \"InternalDirections\");\n  TestHelpers::db::test_compute_tag<\n      Tags::BoundaryDirectionsInteriorCompute<Dim>>(\n      \"BoundaryDirectionsInterior\");\n  TestHelpers::db::test_compute_tag<\n      Tags::BoundaryDirectionsExteriorCompute<Dim>>(\n      \"BoundaryDirectionsExterior\");\n  TestHelpers::db::test_compute_tag<Tags::MappedCoordinates<\n      Tags::ElementMap<Dim>, Tags::Coordinates<Dim, Frame::ElementLogical>>>(\n      \"InertialCoordinates\");\n  TestHelpers::db::test_compute_tag<\n      Tags::JacobianCompute<Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"Jacobian(ElementLogical,Inertial)\");\n\n  auto map = element_map<Dim>();\n  const tnsr::I<DataVector, Dim, Frame::ElementLogical> logical_coords(\n      make_array<Dim>(DataVector{-1.0, -0.5, 0.0, 0.5, 1.0}));\n  const auto expected_inv_jacobian = map.inv_jacobian(logical_coords);\n  const auto expected_jacobian = map.jacobian(logical_coords);\n\n  const auto box = db::create<\n      tmpl::list<Tags::ElementMap<Dim, Frame::Grid>,\n                 Tags::Coordinates<Dim, Frame::ElementLogical>>,\n      db::AddComputeTags<\n          Tags::InverseJacobianCompute<\n              Tags::ElementMap<Dim, Frame::Grid>,\n              Tags::Coordinates<Dim, Frame::ElementLogical>>,\n          Tags::DetInvJacobianCompute<Dim, Frame::ElementLogical, Frame::Grid>,\n          Tags::JacobianCompute<Dim, Frame::ElementLogical, Frame::Grid>>>(\n      std::move(map), logical_coords);\n  CHECK_ITERABLE_APPROX(\n      (db::get<Tags::InverseJacobian<Dim, Frame::ElementLogical, Frame::Grid>>(\n          box)),\n      expected_inv_jacobian);\n  CHECK_ITERABLE_APPROX(\n      (db::get<Tags::DetInvJacobian<Frame::ElementLogical, Frame::Grid>>(box)),\n      determinant(expected_inv_jacobian));\n  CHECK_ITERABLE_APPROX(\n      (db::get<Tags::Jacobian<Dim, Frame::ElementLogical, Frame::Grid>>(box)),\n      expected_jacobian);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.Tags\", \"[Unit][Domain]\") {\n  test_simple_tags<1>();\n  test_simple_tags<2>();\n  test_simple_tags<3>();\n\n  test_compute_tags<1>();\n  test_compute_tags<2>();\n  test_compute_tags<3>();\n}\n}  // namespace\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Domain/Test_TagsCharacteristicSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceComputeTags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsCharacteristicSpeeds.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstruct CharSpeeds : db::SimpleTag {\n  using type = std::array<DataVector, 4>;\n};\n\ntemplate <size_t Dim>\nstruct CharSpeedsCompute : CharSpeeds<Dim>, db::ComputeTag {\n  using base = CharSpeeds<Dim>;\n  using return_type = std::array<DataVector, 4>;\n\n  static void function(\n      const gsl::not_null<std::array<DataVector, 4>*> result,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& inertial_coords) {\n    gsl::at(*result, 0) = inertial_coords.get(0);\n    for (size_t i = 1; i < 4; ++i) {\n      gsl::at(*result, i) = inertial_coords.get(0) + 2.0 * i;\n    }\n  }\n  using argument_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n};\n\ntemplate <size_t Dim>\nstruct Directions : db::SimpleTag {\n  static constexpr size_t volume_dim = Dim;\n  static std::string name() { return \"Directions\"; }\n  using type = std::unordered_set<Direction<Dim>>;\n};\n\ntemplate <size_t Dim>\nstd::unordered_set<Direction<Dim>> get_directions();\n\ntemplate <>\nstd::unordered_set<Direction<1>> get_directions<1>() {\n  return std::unordered_set<Direction<1>>{Direction<1>::upper_xi()};\n}\n\ntemplate <>\nstd::unordered_set<Direction<2>> get_directions<2>() {\n  return std::unordered_set<Direction<2>>{Direction<2>::upper_xi(),\n                                          Direction<2>::lower_eta()};\n}\n\ntemplate <>\nstd::unordered_set<Direction<3>> get_directions<3>() {\n  return std::unordered_set<Direction<3>>{Direction<3>::upper_xi(),\n                                          Direction<3>::lower_eta(),\n                                          Direction<3>::lower_zeta()};\n}\n\ntemplate <size_t Dim, bool MeshIsMoving>\nvoid test_tags() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n\n  TestHelpers::db::test_compute_tag<\n      domain::Tags::CharSpeedCompute<CharSpeedsCompute<Dim>, Dim>>(\n      \"CharSpeeds\");\n\n  using simple_tags = db::AddSimpleTags<\n      Directions<Dim>,\n      domain::Tags::Interface<Directions<Dim>, domain::Tags::MeshVelocity<Dim>>,\n      domain::Tags::Interface<Directions<Dim>,\n                              domain::Tags::Coordinates<Dim, Frame::Inertial>>,\n      domain::Tags::Interface<\n          Directions<Dim>,\n          Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim>>>>;\n\n  using compute_tags = db::AddComputeTags<domain::Tags::InterfaceCompute<\n      Directions<Dim>,\n      domain::Tags::CharSpeedCompute<CharSpeedsCompute<Dim>, Dim>>>;\n\n  const DataVector used_for_size(5);\n\n  std::unordered_map<Direction<Dim>,\n                     std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>>\n      mesh_velocity{};\n  std::unordered_map<Direction<Dim>, tnsr::I<DataVector, Dim, Frame::Inertial>>\n      coordinates{};\n  std::unordered_map<Direction<Dim>, tnsr::i<DataVector, Dim, Frame::Inertial>>\n      normals{};\n  for (const auto& direction : get_directions<Dim>()) {\n    if (MeshIsMoving) {\n      mesh_velocity[direction] =\n          make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n              make_not_null(&generator), make_not_null(&dist), used_for_size);\n    } else {\n      mesh_velocity[direction] = std::nullopt;\n    }\n    coordinates[direction] =\n        make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n            make_not_null(&generator), make_not_null(&dist), used_for_size);\n    normals[direction] =\n        make_with_random_values<tnsr::i<DataVector, Dim, Frame::Inertial>>(\n            make_not_null(&generator), make_not_null(&dist), used_for_size);\n  }\n\n  const auto box = db::create<simple_tags, compute_tags>(\n      get_directions<Dim>(), mesh_velocity, coordinates, normals);\n\n  std::unordered_map<Direction<Dim>, std::array<DataVector, 4>>\n      expected_char_speeds{};\n  for (const auto& direction : get_directions<Dim>()) {\n    CharSpeedsCompute<Dim>::function(\n        make_not_null(&expected_char_speeds[direction]),\n        coordinates[direction]);\n    if (MeshIsMoving) {\n      const Scalar<DataVector> normal_dot_velocity =\n          dot_product(normals[direction], *(mesh_velocity[direction]));\n      for (size_t i = 0; i < expected_char_speeds[direction].size(); ++i) {\n        gsl::at(expected_char_speeds[direction], i) -= get(normal_dot_velocity);\n      }\n    }\n\n    CHECK_ITERABLE_APPROX(\n        (db::get<domain::Tags::Interface<Directions<Dim>, CharSpeeds<Dim>>>(box)\n             .at(direction)),\n        expected_char_speeds.at(direction));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.TagsCharacteristicSpeeds\", \"[Unit][Actions]\") {\n  test_tags<1, true>();\n  test_tags<2, true>();\n  test_tags<3, true>();\n\n  test_tags<1, false>();\n  test_tags<2, false>();\n  test_tags<3, false>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Domain/Test_TagsTimeDependent.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_tags() {\n  TestHelpers::db::test_simple_tag<\n      domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(\n      \"CoordinatesMeshVelocityAndJacobians\");\n  TestHelpers::db::test_compute_tag<\n      domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<\n          domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                      Frame::Inertial>>>(\n      \"CoordinatesMeshVelocityAndJacobians\");\n  TestHelpers::db::test_compute_tag<\n      domain::Tags::InertialFromGridCoordinatesCompute<Dim>>(\n      \"InertialCoordinates\");\n  TestHelpers::db::test_compute_tag<\n      domain::Tags::ElementToInertialInverseJacobian<Dim>>(\n      \"InverseJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_compute_tag<\n      domain::Tags::GridToInertialInverseJacobian<Dim>>(\n      \"InverseJacobian(Grid,Inertial)\");\n  TestHelpers::db::test_simple_tag<domain::Tags::MeshVelocity<Dim>>(\n      \"MeshVelocity\");\n  TestHelpers::db::test_compute_tag<\n      domain::Tags::InertialMeshVelocityCompute<Dim>>(\"MeshVelocity\");\n  TestHelpers::db::test_simple_tag<domain::Tags::DivMeshVelocity>(\n      \"div(MeshVelocity)\");\n}\n\nusing TranslationMap = domain::CoordinateMaps::TimeDependent::Translation<1>;\nusing TranslationMap2d = domain::CoordinateMaps::TimeDependent::Translation<2>;\nusing TranslationMap3d = domain::CoordinateMaps::TimeDependent::Translation<3>;\nusing AffineMap = domain::CoordinateMaps::Affine;\nusing AffineMap2d =\n    domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\nusing AffineMap3d =\n    domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n\ntemplate <size_t MeshDim>\nusing ConcreteMap = tmpl::conditional_t<\n    MeshDim == 1,\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap,\n                          AffineMap>,\n    tmpl::conditional_t<MeshDim == 2,\n                        domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                              TranslationMap2d, AffineMap2d>,\n                        domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                              TranslationMap3d, AffineMap3d>>>;\n\ntemplate <size_t MeshDim>\nConcreteMap<MeshDim> create_coord_map(const std::string& f_of_t_name);\n\ntemplate <>\nConcreteMap<1> create_coord_map<1>(const std::string& f_of_t_name) {\n  return ConcreteMap<1>{TranslationMap{f_of_t_name},\n                        AffineMap{-1.0, 1.0, 2.0, 7.2}};\n}\n\ntemplate <>\nConcreteMap<2> create_coord_map<2>(const std::string& f_of_t_name) {\n  return ConcreteMap<2>{\n      {TranslationMap2d{f_of_t_name}},\n      {AffineMap{-1.0, 1.0, -2.0, 2.2}, AffineMap{-1.0, 1.0, 2.0, 7.2}}};\n}\n\ntemplate <>\nConcreteMap<3> create_coord_map<3>(const std::string& f_of_t_name) {\n  return ConcreteMap<3>{\n      {TranslationMap3d{f_of_t_name}},\n      {AffineMap{-1.0, 1.0, -2.0, 2.2}, AffineMap{-1.0, 1.0, 2.0, 7.2},\n       AffineMap{-1.0, 1.0, 1.0, 3.5}}};\n}\n\ntemplate <size_t Dim, bool IsTimeDependent>\nvoid test() {\n  using simple_tags = db::AddSimpleTags<\n      Tags::Time, domain::Tags::Coordinates<Dim, Frame::Grid>,\n      domain::Tags::InverseJacobian<Dim, Frame::ElementLogical, Frame::Grid>,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                  Frame::Inertial>>;\n  using compute_tags = db::AddComputeTags<\n      domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<\n          domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                      Frame::Inertial>>,\n      domain::Tags::InertialFromGridCoordinatesCompute<Dim>,\n      domain::Tags::ElementToInertialInverseJacobian<Dim>,\n      domain::Tags::GridToInertialInverseJacobian<Dim>,\n      domain::Tags::InertialMeshVelocityCompute<Dim>>;\n\n  MAKE_GENERATOR(gen);\n  const DataVector velocity{Dim, 1.2};\n  const double initial_time = 0.0;\n  const double expiration_time = 5.0;\n  const std::string function_of_time_name = \"Translation\";\n\n  UniformCustomDistribution<double> dist(-10.0, 10.0);\n\n  const size_t num_pts = Dim * 5;\n\n  tnsr::I<DataVector, Dim, Frame::Grid> grid_coords{num_pts};\n  fill_with_random_values(make_not_null(&grid_coords), make_not_null(&gen),\n                          make_not_null(&dist));\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Grid>\n      element_to_grid_inverse_jacobian{num_pts};\n  fill_with_random_values(make_not_null(&element_to_grid_inverse_jacobian),\n                          make_not_null(&gen), make_not_null(&dist));\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[function_of_time_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time,\n          std::array<DataVector, 3>{{{Dim, 0.0}, velocity, {Dim, 0.0}}},\n          expiration_time);\n\n  using MapPtr = std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>>;\n  const MapPtr grid_to_inertial_map =\n      IsTimeDependent ? MapPtr{std::make_unique<ConcreteMap<Dim>>(\n                            create_coord_map<Dim>(function_of_time_name))}\n                      : MapPtr{std::make_unique<domain::CoordinateMap<\n                            Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<Dim>>>()};\n\n  const double time = 3.0;\n  auto box = db::create<simple_tags, compute_tags>(\n      time, grid_coords, element_to_grid_inverse_jacobian,\n      std::move(functions_of_time), grid_to_inertial_map->get_clone());\n\n  const auto check_helper = [&box, &element_to_grid_inverse_jacobian,\n                             &grid_coords, &grid_to_inertial_map,\n                             num_pts](const double expected_time) {\n    if (IsTimeDependent) {\n      const tnsr::I<DataVector, Dim, Frame::Inertial> expected_coords =\n          (*grid_to_inertial_map)(grid_coords, expected_time,\n                                  db::get<domain::Tags::FunctionsOfTime>(box));\n\n      const InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>\n          expected_inv_jacobian_grid_to_inertial =\n              grid_to_inertial_map->inv_jacobian(\n                  grid_coords, expected_time,\n                  db::get<domain::Tags::FunctionsOfTime>(box));\n\n      InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n          expected_inv_jacobian{num_pts};\n\n      for (size_t logical_i = 0; logical_i < Dim; ++logical_i) {\n        for (size_t inertial_i = 0; inertial_i < Dim; ++inertial_i) {\n          expected_inv_jacobian.get(logical_i, inertial_i) =\n              element_to_grid_inverse_jacobian.get(logical_i, 0) *\n              expected_inv_jacobian_grid_to_inertial.get(0, inertial_i);\n          for (size_t grid_i = 1; grid_i < Dim; ++grid_i) {\n            expected_inv_jacobian.get(logical_i, inertial_i) +=\n                element_to_grid_inverse_jacobian.get(logical_i, grid_i) *\n                expected_inv_jacobian_grid_to_inertial.get(grid_i, inertial_i);\n          }\n        }\n      }\n\n      REQUIRE(\n          db::get<domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(box)\n              .has_value());\n\n      for (size_t i = 0; i < Dim; ++i) {\n        // Check that the `const_cast`s and set_data_ref inside the compute\n        // tag functions worked correctly\n        CHECK(db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box)\n                  .get(i)\n                  .data() ==\n              std::get<0>(\n                  *db::get<\n                      domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(\n                      box))\n                  .get(i)\n                  .data());\n      }\n      CHECK_ITERABLE_APPROX(\n          (db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box)),\n          expected_coords);\n\n      for (size_t i = 0;\n           i < db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                     Frame::Inertial>>(box)\n                   .size();\n           ++i) {\n        CHECK(\n            db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                  Frame::Inertial>>(box)[i]\n                .data() !=\n            std::get<1>(*db::get<\n                        domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(\n                box))[i]\n                .data());\n      }\n      CHECK_ITERABLE_APPROX(\n          (db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                 Frame::Inertial>>(box)),\n          expected_inv_jacobian);\n\n      const auto expected_coords_mesh_velocity_jacobians =\n          grid_to_inertial_map->coords_frame_velocity_jacobians(\n              db::get<domain::Tags::Coordinates<Dim, Frame::Grid>>(box),\n              db::get<::Tags::Time>(box),\n              db::get<domain::Tags::FunctionsOfTime>(box));\n\n      for (size_t i = 0; i < Dim; ++i) {\n        // Check that the `const_cast`s and set_data_ref inside the compute\n        // tag functions worked correctly\n        CHECK(db::get<domain::Tags::MeshVelocity<Dim>>(box)->get(i).data() ==\n              std::get<3>(\n                  *db::get<\n                      domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(\n                      box))\n                  .get(i)\n                  .data());\n      }\n      CHECK_ITERABLE_APPROX(\n          db::get<domain::Tags::MeshVelocity<Dim>>(box).value(),\n          std::get<3>(expected_coords_mesh_velocity_jacobians));\n      for (size_t i = 0;\n           i < db::get<domain::Tags::InverseJacobian<Dim, Frame::Grid,\n                                                     Frame::Inertial>>(box)\n                   .size();\n           ++i) {\n        // Check that the `const_cast`s and set_data_ref inside the\n        // compute tag functions worked correctly\n        CHECK(\n            db::get<domain::Tags::InverseJacobian<Dim, Frame::Grid,\n                                                  Frame::Inertial>>(box)[i]\n                .data() ==\n            std::get<1>(*db::get<\n                        domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(\n                box))[i]\n                .data());\n      }\n      CHECK_ITERABLE_APPROX(\n          (db::get<\n              domain::Tags::InverseJacobian<Dim, Frame::Grid, Frame::Inertial>>(\n              box)),\n          std::get<1>(expected_coords_mesh_velocity_jacobians));\n    } else {\n      tnsr::I<DataVector, Dim, Frame::Inertial> expected_coords{num_pts};\n      for (size_t i = 0; i < Dim; ++i) {\n        expected_coords[i] = grid_coords[i];\n      }\n\n      InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n          expected_inv_jacobian{num_pts};\n      // The Grid->Inertial Jacobian is currently just the identity\n      for (size_t i = 0; i < expected_inv_jacobian.size(); ++i) {\n        expected_inv_jacobian[i] = element_to_grid_inverse_jacobian[i];\n      }\n\n      for (size_t i = 0; i < Dim; ++i) {\n        // Check that the `const_cast`s and set_data_ref inside the\n        // compute tag functions worked correctly\n        CHECK(db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box)\n                  .get(i)\n                  .data() ==\n              db::get<domain::Tags::Coordinates<Dim, Frame::Grid>>(box)\n                  .get(i)\n                  .data());\n      }\n      CHECK_ITERABLE_APPROX(\n          (db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box)),\n          expected_coords);\n\n      for (size_t i = 0;\n           i < db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                     Frame::Inertial>>(box)\n                   .size();\n           ++i) {\n        // Check that the `const_cast`s and set_data_ref inside the\n        // compute tag functions worked correctly\n        CHECK(db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                    Frame::Inertial>>(box)[i]\n                  .data() ==\n              db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                    Frame::Grid>>(box)[i]\n                  .data());\n      }\n      CHECK_ITERABLE_APPROX(\n          (db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                 Frame::Inertial>>(box)),\n          expected_inv_jacobian);\n\n      CHECK_FALSE(db::get<domain::Tags::MeshVelocity<Dim>>(box));\n      CHECK_THROWS_WITH(\n          (db::get<\n              domain::Tags::InverseJacobian<Dim, Frame::Grid, Frame::Inertial>>(\n              box)),\n          Catch::Matchers::ContainsSubstring(\n              \"Should not request Grid to Inertial jacobian for a \"\n              \"non-moving mesh \"\n              \"because it is the identity.\"));\n    }\n  };\n  check_helper(3.0);\n\n  db::mutate<Tags::Time>(\n      [](const gsl::not_null<double*> local_time) { *local_time = 4.5; },\n      make_not_null(&box));\n  check_helper(4.5);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Domain.TagsTimeDependent\", \"[Unit][Actions]\") {\n  test_tags<1>();\n  test_tags<2>();\n  test_tags<3>();\n\n  test<1, true>();\n  test<2, true>();\n  test<3, true>();\n\n  test<1, false>();\n  test<2, false>();\n  test<3, false>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Elliptic/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticActions\")\n\nset(LIBRARY_SOURCES\n  Test_InitializeAnalyticSolution.cpp\n  Test_InitializeBackgroundFields.cpp\n  Test_InitializeFields.cpp\n  Test_InitializeFixedSources.cpp\n  Test_RunEventsAndTriggers.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  AnalyticSolutions\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  Elliptic\n  EllipticDg\n  ErrorHandling\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Actions/Test_InitializeAnalyticSolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/InitializeAnalyticSolution.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\nstruct NoAnalyticSolution : elliptic::analytic_data::Background {\n  NoAnalyticSolution() = default;\n  explicit NoAnalyticSolution(CkMigrateMessage* m)\n      : elliptic::analytic_data::Background(m) {}\n  WRAPPED_PUPable_decl_template(NoAnalyticSolution);\n\n  // Does _not_ provide variables for all system fields\n};\n\nPUP::able::PUP_ID NoAnalyticSolution::my_PUP_ID = 0;  // NOLINT\n\nstruct AnalyticSolution : elliptic::analytic_data::AnalyticSolution {\n  AnalyticSolution() = default;\n  explicit AnalyticSolution(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  WRAPPED_PUPable_decl_template(AnalyticSolution);\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<AnalyticSolution>(*this);\n  }\n\n  static tuples::TaggedTuple<ScalarFieldTag> variables(\n      const tnsr::I<DataVector, 1>& x, tmpl::list<ScalarFieldTag> /*meta*/) {\n    Scalar<DataVector> solution{2. * get<0>(x)};\n    return {std::move(solution)};\n  }\n};\n\nPUP::able::PUP_ID AnalyticSolution::my_PUP_ID = 0;  // NOLINT\n#pragma GCC diagnostic pop\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              tmpl::list<domain::Tags::Mesh<1>,\n                         domain::Tags::Coordinates<1, Frame::Inertial>>>>>,\n\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<elliptic::Actions::InitializeOptionalAnalyticSolution<\n              1,\n              elliptic::Tags::Background<elliptic::analytic_data::Background>,\n              tmpl::list<ScalarFieldTag>,\n              elliptic::analytic_data::AnalyticSolution>>>>;\n};\n\nstruct Metavariables {\n  using element_array = ElementArray<Metavariables>;\n  using const_global_cache_tags = tmpl::list<\n      elliptic::Tags::Background<elliptic::analytic_data::Background>>;\n  using component_list = tmpl::list<element_array>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<elliptic::analytic_data::Background,\n                             tmpl::list<AnalyticSolution, NoAnalyticSolution>>,\n                  tmpl::pair<elliptic::analytic_data::AnalyticSolution,\n                             tmpl::list<AnalyticSolution>>>;\n  };\n};\n\nvoid test_initialize_analytic_solution(\n    const tnsr::I<DataVector, 1>& inertial_coords,\n    const Scalar<DataVector>& expected_solution) {\n  using metavariables = Metavariables;\n  using element_array = typename metavariables::element_array;\n\n  register_factory_classes_with_charm<metavariables>();\n\n  const auto initialize_analytic_solution =\n      [&inertial_coords](auto analytic_solution_or_data) {\n        ActionTesting::MockRuntimeSystem<metavariables> runner{\n            {std::move(analytic_solution_or_data)}};\n        const ElementId<1> element_id{0};\n        ActionTesting::emplace_component_and_initialize<element_array>(\n            &runner, element_id, {Mesh<1>{}, inertial_coords});\n        ActionTesting::set_phase(make_not_null(&runner),\n                                 Parallel::Phase::Testing);\n        for (size_t i = 0; i < 2; ++i) {\n          ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                    element_id);\n        }\n        return ActionTesting::get_databox_tag<\n            element_array,\n            ::Tags::AnalyticSolutions<tmpl::list<ScalarFieldTag>>>(\n            runner, element_id);\n      };\n\n  {\n    INFO(\"Analytic solution is available\");\n    const auto analytic_solutions =\n        initialize_analytic_solution(std::make_unique<AnalyticSolution>());\n    REQUIRE(analytic_solutions.has_value());\n    CHECK_ITERABLE_APPROX(get(get<::Tags::detail::AnalyticImpl<ScalarFieldTag>>(\n                              *analytic_solutions)),\n                          get(expected_solution));\n  }\n  {\n    INFO(\"No analytic solution is available\");\n    const auto no_analytic_solutions =\n        initialize_analytic_solution(std::make_unique<NoAnalyticSolution>());\n    CHECK_FALSE(no_analytic_solutions.has_value());\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Actions.InitializeAnalyticSolution\",\n                  \"[Unit][Elliptic][Actions]\") {\n  static_assert(\n      tt::assert_conforms_to_v<\n          elliptic::Actions::InitializeOptionalAnalyticSolution<\n              1,\n              elliptic::Tags::Background<elliptic::analytic_data::Background>,\n              tmpl::list<ScalarFieldTag>,\n              elliptic::analytic_data::AnalyticSolution>,\n          amr::protocols::Projector>);\n  test_initialize_analytic_solution(\n      tnsr::I<DataVector, 1>{{{{1., 2., 3., 4.}}}},\n      Scalar<DataVector>{{{{2., 4., 6., 8.}}}});\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Actions/Test_InitializeBackgroundFields.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/InitializeBackgroundFields.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct BackgroundFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct System {\n  using background_fields = tmpl::list<BackgroundFieldTag>;\n};\n\nstruct Background {\n  static tuples::TaggedTuple<BackgroundFieldTag> variables(\n      const tnsr::I<DataVector, 1>& x, const Mesh<1>& /*mesh*/,\n      const InverseJacobian<DataVector, 1, Frame::ElementLogical,\n                            Frame::Inertial>&\n      /*inv_jacobian*/,\n      tmpl::list<BackgroundFieldTag> /*meta*/) {\n    return {Scalar<DataVector>{get<0>(x)}};\n  }\n  // NOLINTNEXTLINE\n  void pup(PUP::er& /*p*/) {}\n};\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<1>>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<\n                     Parallel::Phase::Initialization,\n                     tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<\n                                    domain::Tags::InitialRefinementLevels<1>,\n                                    domain::Tags::InitialExtents<1>>>,\n                                elliptic::dg::Actions::InitializeDomain<1>>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Testing,\n                     tmpl::list<::elliptic::Actions::InitializeBackgroundFields<\n                         typename Metavariables::system,\n                         elliptic::Tags::Background<Background>>>>>;\n};\n\nstruct Metavariables {\n  using system = System;\n  using component_list = tmpl::list<ElementArray<Metavariables>>;\n  using const_global_cache_tags =\n      tmpl::list<elliptic::Tags::Background<Background>>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Actions.InitializeBackgroundFields\",\n                  \"[Unit][Elliptic][Actions]\") {\n  domain::creators::register_derived_with_charm();\n  // Which element we work with does not matter for this test\n  const ElementId<1> element_id{0, {{SegmentId{2, 1}}}};\n  const domain::creators::Interval domain_creator{\n      {{-0.5}}, {{1.5}}, {{2}}, {{4}}};\n\n  using element_array = ElementArray<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {std::make_unique<Background>(), domain_creator.create_domain(),\n       domain_creator.functions_of_time(), Spectral::Quadrature::GaussLobatto}};\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      &runner, element_id,\n      {domain_creator.initial_refinement_levels(),\n       domain_creator.initial_extents()});\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  const auto get_tag = [&runner, &element_id ](auto tag_v) -> const auto& {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  const auto& inertial_coords =\n      get_tag(domain::Tags::Coordinates<1, Frame::Inertial>{});\n  CHECK(get(get_tag(BackgroundFieldTag{})) == get<0>(inertial_coords));\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Actions/Test_InitializeFields.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/InitializeFields.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct System {\n  using primal_fields = tmpl::list<ScalarFieldTag>;\n};\n\nstruct InitialGuess : elliptic::analytic_data::InitialGuess {\n  InitialGuess() = default;\n  explicit InitialGuess(CkMigrateMessage* m)\n      : elliptic::analytic_data::InitialGuess(m) {}\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(InitialGuess);  // NOLINT\n#pragma GCC diagnostic pop\n\n  // NOLINTBEGIN(readability-convert-member-functions-to-static)\n  // [initial_guess_vars_fct]\n  tuples::TaggedTuple<ScalarFieldTag> variables(  // NOLINT\n      const tnsr::I<DataVector, 1>& x,\n      tmpl::list<ScalarFieldTag> /*meta*/) const {\n    // [initial_guess_vars_fct]\n    Scalar<DataVector> scalar_field{2. * get<0>(x)};\n    return {std::move(scalar_field)};\n  }\n  // NOLINTEND(readability-convert-member-functions-to-static)\n};\n\nPUP::able::PUP_ID InitialGuess::my_PUP_ID = 0;  // NOLINT\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<1>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n                         tmpl::list<domain::Tags::InitialRefinementLevels<1>,\n                                    domain::Tags::InitialExtents<1>>>,\n                     elliptic::dg::Actions::InitializeDomain<1>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<elliptic::Actions::InitializeFields<\n                                 typename Metavariables::system,\n                                 elliptic::Tags::InitialGuess<\n                                     elliptic::analytic_data::InitialGuess>>>>>;\n};\n\nstruct Metavariables {\n  using system = System;\n  using component_list = tmpl::list<ElementArray<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<\n      elliptic::Tags::InitialGuess<elliptic::analytic_data::InitialGuess>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<elliptic::analytic_data::InitialGuess,\n                             tmpl::list<InitialGuess>>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Actions.InitializeFields\",\n                  \"[Unit][Elliptic][Actions]\") {\n  domain::creators::register_derived_with_charm();\n  register_factory_classes_with_charm<Metavariables>();\n  // Which element we work with does not matter for this test\n  const ElementId<1> element_id{0, {{SegmentId{2, 1}}}};\n  const domain::creators::Interval domain_creator{\n      {{-0.5}}, {{1.5}}, {{2}}, {{4}}};\n\n  using element_array = ElementArray<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {std::make_unique<InitialGuess>(), domain_creator.create_domain(),\n       domain_creator.functions_of_time(), Spectral::Quadrature::GaussLobatto}};\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      &runner, element_id,\n      {domain_creator.initial_refinement_levels(),\n       domain_creator.initial_extents()});\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  const auto get_tag = [&runner, &element_id ](auto tag_v) -> const auto& {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  // Test against the expression implemented above\n  const auto& inertial_coords =\n      get_tag(domain::Tags::Coordinates<1, Frame::Inertial>{});\n  CHECK(get(get_tag(ScalarFieldTag{})) == 2. * get<0>(inertial_coords));\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Actions/Test_InitializeFixedSources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/InitializeFixedSources.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct System {\n  static constexpr size_t volume_dim = 1;\n  using primal_fields = tmpl::list<ScalarFieldTag>;\n};\n\nstruct Background : elliptic::analytic_data::Background {\n  Background() = default;\n  explicit Background(CkMigrateMessage* m)\n      : elliptic::analytic_data::Background(m) {}\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(Background);  // NOLINT\n#pragma GCC diagnostic pop\n\n  // NOLINTBEGIN(readability-convert-member-functions-to-static)\n  // [background_vars_fct]\n  tuples::TaggedTuple<Tags::FixedSource<ScalarFieldTag>> variables(  // NOLINT\n      const tnsr::I<DataVector, 1>& x,\n      tmpl::list<Tags::FixedSource<ScalarFieldTag>> /*meta*/) const {\n    // [background_vars_fct]\n    return {Scalar<DataVector>{get<0>(x)}};\n  }\n  // NOLINTEND(readability-convert-member-functions-to-static)\n};\n\nPUP::able::PUP_ID Background::my_PUP_ID = 0;  // NOLINT\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<1>>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<\n                     Parallel::Phase::Initialization,\n                     tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<\n                                    domain::Tags::InitialRefinementLevels<1>,\n                                    domain::Tags::InitialExtents<1>>>,\n                                elliptic::dg::Actions::InitializeDomain<1>>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Testing,\n                     tmpl::list<elliptic::Actions::InitializeFixedSources<\n                         typename Metavariables::system,\n                         elliptic::Tags::Background<\n                             elliptic::analytic_data::Background>>>>>;\n};\n\nstruct Metavariables {\n  using system = System;\n  using component_list = tmpl::list<ElementArray<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<\n      elliptic::Tags::Background<elliptic::analytic_data::Background>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<elliptic::analytic_data::Background,\n                             tmpl::list<Background>>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Actions.InitializeFixedSources\",\n                  \"[Unit][Elliptic][Actions]\") {\n  using background_tag =\n      elliptic::Tags::Background<elliptic::analytic_data::Background>;\n  static_assert(\n      tt::assert_conforms_to_v<\n          elliptic::Actions::InitializeFixedSources<System, background_tag>,\n          amr::protocols::Projector>);\n\n  domain::creators::register_derived_with_charm();\n  register_factory_classes_with_charm<Metavariables>();\n  // Which element we work with does not matter for this test\n  const ElementId<1> element_id{0, {{SegmentId{2, 1}}}};\n  const domain::creators::Interval domain_creator{\n      {{-0.5}}, {{1.5}}, {{2}}, {{4}}};\n\n  using element_array = ElementArray<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      tuples::TaggedTuple<background_tag, domain::Tags::Domain<1>,\n                          domain::Tags::FunctionsOfTimeInitialize,\n                          elliptic::dg::Tags::Massive,\n                          elliptic::dg::Tags::Quadrature>{\n          std::make_unique<Background>(), domain_creator.create_domain(),\n          domain_creator.functions_of_time(), false,\n          Spectral::Quadrature::GaussLobatto}};\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      &runner, element_id,\n      {domain_creator.initial_refinement_levels(),\n       domain_creator.initial_extents()});\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  const auto get_tag = [&runner, &element_id](auto tag_v) -> const auto& {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  const auto& inertial_coords =\n      get_tag(domain::Tags::Coordinates<1, Frame::Inertial>{});\n  CHECK(get(get_tag(::Tags::FixedSource<ScalarFieldTag>{})) ==\n        get<0>(inertial_coords));\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Actions/Test_RunEventsAndTriggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"Elliptic/Actions/RunEventsAndTriggers.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TestEvent : public Event {\n public:\n  explicit TestEvent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestEvent);  // NOLINT\n#pragma GCC diagnostic pop\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  TestEvent() = default;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  void operator()(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ArrayIndex& /*array_index*/,\n                  const Component* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    CHECK(observation_value.name == \"IterationId(Label)\");\n    CHECK(observation_value.value == 10.0);\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n};\n\nPUP::able::PUP_ID TestEvent::my_PUP_ID = 0;  // NOLINT\n\nstruct Label {};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags = tmpl::list<Convergence::Tags::IterationId<Label>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<elliptic::Actions::RunEventsAndTriggers<\n                                 Convergence::Tags::IterationId<Label>>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Event, tmpl::list<TestEvent>>,\n                  tmpl::pair<Trigger, Triggers::logical_triggers>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.RunEventsAndTriggers\", \"[Unit][Elliptic]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  EventsAndTriggers::Storage events_and_triggers_input;\n  events_and_triggers_input.push_back(\n      {std::make_unique<Triggers::Always>(),\n       make_vector<std::unique_ptr<Event>>(std::make_unique<TestEvent>())});\n\n  using my_component = Component<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {EventsAndTriggers(std::move(events_and_triggers_input))}};\n  ActionTesting::emplace_component_and_initialize<my_component>(&runner, 0,\n                                                                {10_st});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<my_component>(make_not_null(&runner), 0);\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Tags/Test_BoundaryFields.cpp\n  Test_AnalyticSolution.cpp\n  Test_BoundaryCondition.cpp\n  Test_BoundaryConditionType.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainBoundaryConditions\n  DomainStructure\n  Elliptic\n  Options\n  Parallel\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/BoundaryConditions/Tags/Test_BoundaryFields.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/BoundaryConditions/Tags/BoundaryFields.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.BoundaryConditions.BoundaryFields\",\n                  \"[Unit][Elliptic]\") {\n  static constexpr size_t Dim = 2;\n  using vars_tag = ::Tags::Variables<tmpl::list<::Tags::TempScalar<0>>>;\n  using fluxes_tag = ::Tags::Variables<tmpl::list<::Tags::TempI<0, Dim>>>;\n  TestHelpers::db::test_compute_tag<\n      elliptic::Tags::BoundaryFieldsCompute<Dim, vars_tag>>(\n      \"Faces(Variables(TempTensor0))\");\n  TestHelpers::db::test_compute_tag<\n      elliptic::Tags::BoundaryFluxesCompute<Dim, vars_tag, fluxes_tag>>(\n      \"Faces(Variables(NormalDotFlux(TempTensor0)))\");\n  {\n    const Mesh<Dim> mesh{3, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto};\n    const Element<Dim> element{\n        ElementId<Dim>{0},\n        {{Direction<Dim>::upper_xi(),\n          {{ElementId<Dim>{1}}, OrientationMap<Dim>::create_aligned()}},\n         {Direction<Dim>::lower_eta(),\n          {{ElementId<Dim>{1}}, OrientationMap<Dim>::create_aligned()}},\n         {Direction<Dim>::upper_eta(),\n          {{ElementId<Dim>{1}}, OrientationMap<Dim>::create_aligned()}}}};\n    tnsr::i<DataVector, Dim> face_normal{size_t{3}, 0.};\n    get<0>(face_normal) = -1.;\n    DirectionMap<Dim, tnsr::i<DataVector, Dim>> face_normals{\n        {Direction<Dim>::lower_xi(), std::move(face_normal)}};\n    Variables<tmpl::list<::Tags::TempScalar<0>>> vars{size_t{9}, 0.};\n    auto& var = get(get<::Tags::TempScalar<0>>(vars));\n    std::iota(var.begin(), var.end(), 1.);\n    Variables<tmpl::list<::Tags::TempI<0, Dim>>> fluxes{size_t{9}, 0.};\n    for (size_t d = 0; d < Dim; ++d) {\n      auto& flux = get<::Tags::TempI<0, Dim>>(fluxes).get(d);\n      std::iota(flux.begin(), flux.end(), 10. * static_cast<double>(d + 1));\n    }\n    const auto box = db::create<\n        db::AddSimpleTags<\n            vars_tag, fluxes_tag, domain::Tags::Mesh<Dim>,\n            domain::Tags::Element<Dim>,\n            domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>>,\n        db::AddComputeTags<\n            elliptic::Tags::BoundaryFieldsCompute<Dim, vars_tag>,\n            elliptic::Tags::BoundaryFluxesCompute<Dim, vars_tag, fluxes_tag>>>(\n        std::move(vars), std::move(fluxes), mesh, element,\n        std::move(face_normals));\n    CHECK(get(get<domain::Tags::Faces<Dim, ::Tags::TempScalar<0>>>(box).at(\n              Direction<Dim>::lower_xi())) == DataVector{1., 4., 7.});\n    CHECK(get(get<domain::Tags::Faces<\n                  Dim, ::Tags::NormalDotFlux<::Tags::TempScalar<0>>>>(box)\n                  .at(Direction<Dim>::lower_xi())) ==\n          DataVector{-10., -13., -16.});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/BoundaryConditions/Test_AnalyticSolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::BoundaryConditions {\n\nnamespace {\n\ntemplate <size_t N>\nstruct ScalarFieldTag : db::SimpleTag {\n  static std::string name() { return \"Field\" + std::to_string(N); }\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim, size_t N>\nstruct FluxTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using primal_fields = tmpl::list<ScalarFieldTag<1>, ScalarFieldTag<2>>;\n  using primal_fluxes = tmpl::list<FluxTag<Dim, 1>, FluxTag<Dim, 2>>;\n};\n\ntemplate <size_t Dim>\nusing test_tags = tmpl::list<ScalarFieldTag<1>, ScalarFieldTag<2>,\n                             FluxTag<Dim, 1>, FluxTag<Dim, 2>>;\n\ntemplate <size_t Dim>\nstruct TestSolution : elliptic::analytic_data::AnalyticSolution {\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\"A solution.\"};\n  TestSolution() = default;\n  TestSolution(const TestSolution&) = default;\n  TestSolution& operator=(const TestSolution&) = default;\n  TestSolution(TestSolution&&) = default;\n  TestSolution& operator=(TestSolution&&) = default;\n  ~TestSolution() override = default;\n  explicit TestSolution(CkMigrateMessage* m)\n      : elliptic::analytic_data::AnalyticSolution(m) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TestSolution);  // NOLINT\n\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<TestSolution>(*this);\n  }\n\n  tuples::tagged_tuple_from_typelist<test_tags<Dim>> variables(\n      const tnsr::I<DataVector, Dim>& x, test_tags<Dim> /*meta*/) const {\n    // Create arbitrary analytic solution data\n    Variables<test_tags<Dim>> result{x.begin()->size()};\n    std::iota(result.data(), result.data() + result.size(), 1.);\n    return {get<ScalarFieldTag<1>>(result), get<ScalarFieldTag<2>>(result),\n            get<FluxTag<Dim, 1>>(result), get<FluxTag<Dim, 2>>(result)};\n  }\n};\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID TestSolution<Dim>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<elliptic::BoundaryConditions::BoundaryCondition<Dim>,\n                   tmpl::list<elliptic::BoundaryConditions::AnalyticSolution<\n                       System<Dim>>>>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution,\n                   tmpl::list<TestSolution<Dim>>>>;\n  };\n};\n\ntemplate <size_t Dim>\nvoid test_analytic_solution() {\n  CAPTURE(Dim);\n  // Test factory-creation\n  register_factory_classes_with_charm<Metavariables<Dim>>();\n  const auto created = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<BoundaryCondition<Dim>>,\n                                 Metavariables<Dim>>(\n          \"AnalyticSolution:\\n\"\n          \"  Solution: TestSolution\\n\"\n          \"  Field1: Dirichlet\\n\"\n          \"  Field2: Neumann\"));\n  REQUIRE(dynamic_cast<const AnalyticSolution<System<Dim>>*>(created.get()) !=\n          nullptr);\n  const auto& boundary_condition =\n      dynamic_cast<const AnalyticSolution<System<Dim>>&>(*created);\n  {\n    INFO(\"Properties\");\n    CHECK(boundary_condition.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              elliptic::BoundaryConditionType::Dirichlet,\n              elliptic::BoundaryConditionType::Neumann});\n  }\n  {\n    INFO(\"Test applying the boundary conditions\");\n    const Mesh<Dim> volume_mesh{3, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n    const auto direction = Direction<Dim>::lower_xi();\n    const auto face_mesh = volume_mesh.slice_away(direction.dimension());\n    const size_t face_num_points = face_mesh.number_of_grid_points();\n    tnsr::I<DataVector, Dim> face_inertial_coords{face_num_points, 0.};\n    tnsr::i<DataVector, Dim> face_normal{face_num_points, 0.};\n    get<0>(face_normal) = -2.;\n    const auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables<Dim>>,\n        domain::Tags::Faces<Dim,\n                            domain::Tags::Coordinates<Dim, Frame::Inertial>>,\n        domain::Tags::Faces<Dim, domain::Tags::FaceNormal<Dim>>>>(\n        Metavariables<Dim>{},\n        DirectionMap<Dim, tnsr::I<DataVector, Dim>>{\n            {direction, face_inertial_coords}},\n        DirectionMap<Dim, tnsr::i<DataVector, Dim>>{{direction, face_normal}});\n    Variables<tmpl::list<ScalarFieldTag<1>, ScalarFieldTag<2>,\n                         ::Tags::NormalDotFlux<ScalarFieldTag<1>>,\n                         ::Tags::NormalDotFlux<ScalarFieldTag<2>>>>\n        vars{face_num_points, std::numeric_limits<double>::max()};\n    const tnsr::i<DataVector, Dim> deriv_scalar{\n        face_num_points, std::numeric_limits<double>::signaling_NaN()};\n    // Inhomogeneous boundary conditions\n    elliptic::apply_boundary_condition<\n        false, void, tmpl::list<AnalyticSolution<System<Dim>>>>(\n        boundary_condition, box, direction,\n        make_not_null(&get<ScalarFieldTag<1>>(vars)),\n        make_not_null(&get<ScalarFieldTag<2>>(vars)),\n        make_not_null(&get<::Tags::NormalDotFlux<ScalarFieldTag<1>>>(vars)),\n        make_not_null(&get<::Tags::NormalDotFlux<ScalarFieldTag<2>>>(vars)),\n        deriv_scalar, deriv_scalar);\n    // Imposed Dirichlet conditions on field 1\n    const auto expected_dirichlet_field = []() -> DataVector {\n      if constexpr (Dim == 1) {\n        return {1.};\n      } else if constexpr (Dim == 2) {\n        return {1., 2., 3.};\n      } else if constexpr (Dim == 3) {\n        return {1., 2., 3., 4., 5., 6., 7., 8., 9.};\n      }\n    }();\n    CHECK_ITERABLE_APPROX(get(get<ScalarFieldTag<1>>(vars)),\n                          expected_dirichlet_field);\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::NormalDotFlux<ScalarFieldTag<1>>>(vars)),\n        SINGLE_ARG(\n            DataVector{face_num_points, std::numeric_limits<double>::max()}));\n    // Imposed Neumann conditions on field 2\n    const auto expected_neumann_field = []() -> DataVector {\n      if constexpr (Dim == 1) {\n        return {-8.};\n      } else if constexpr (Dim == 2) {\n        return {-26., -28., -30.};\n      } else if constexpr (Dim == 3) {\n        return {-92., -94., -96., -98., -100., -102., -104., -106., -108.};\n      }\n    }();\n    CHECK_ITERABLE_APPROX(\n        get(get<ScalarFieldTag<2>>(vars)),\n        SINGLE_ARG(\n            DataVector{face_num_points, std::numeric_limits<double>::max()}));\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::NormalDotFlux<ScalarFieldTag<2>>>(vars)),\n        expected_neumann_field);\n    // Homogeneous (linearized) boundary conditions\n    elliptic::apply_boundary_condition<\n        true, void, tmpl::list<AnalyticSolution<System<Dim>>>>(\n        boundary_condition, box, direction,\n        make_not_null(&get<ScalarFieldTag<1>>(vars)),\n        make_not_null(&get<ScalarFieldTag<2>>(vars)),\n        make_not_null(&get<::Tags::NormalDotFlux<ScalarFieldTag<1>>>(vars)),\n        make_not_null(&get<::Tags::NormalDotFlux<ScalarFieldTag<2>>>(vars)),\n        deriv_scalar, deriv_scalar);\n    // Imposed Dirichlet conditions on field 1\n    CHECK_ITERABLE_APPROX(get(get<ScalarFieldTag<1>>(vars)),\n                          SINGLE_ARG(DataVector{face_num_points, 0.}));\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::NormalDotFlux<ScalarFieldTag<1>>>(vars)),\n        SINGLE_ARG(\n            DataVector{face_num_points, std::numeric_limits<double>::max()}));\n    // Imposed Neumann conditions on field 2\n    CHECK_ITERABLE_APPROX(\n        get(get<ScalarFieldTag<2>>(vars)),\n        SINGLE_ARG(\n            DataVector{face_num_points, std::numeric_limits<double>::max()}));\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::NormalDotFlux<ScalarFieldTag<2>>>(vars)),\n        SINGLE_ARG(DataVector{face_num_points, 0.}));\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.BoundaryConditions.AnalyticSolution\",\n                  \"[Unit][Elliptic]\") {\n  test_analytic_solution<1>();\n  test_analytic_solution<2>();\n  test_analytic_solution<3>();\n}\n\n}  // namespace elliptic::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/BoundaryConditions/Test_BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::BoundaryConditions {\n\nnamespace {\n\nstruct ArgumentTag : db::SimpleTag {\n  using type = int;\n};\n\nstruct NonlinearArgumentTag : db::SimpleTag {\n  using type = int;\n};\n\nstruct VolumeArgumentTag : db::SimpleTag {\n  using type = bool;\n};\n\nclass TestBoundaryCondition : public BoundaryCondition<1> {\n private:\n  using Base = BoundaryCondition<1>;\n\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = \"halp\";\n\n  TestBoundaryCondition() = default;\n  TestBoundaryCondition(const TestBoundaryCondition&) = default;\n  TestBoundaryCondition(TestBoundaryCondition&&) = default;\n  TestBoundaryCondition& operator=(const TestBoundaryCondition&) = default;\n  TestBoundaryCondition& operator=(TestBoundaryCondition&&) = default;\n  ~TestBoundaryCondition() override = default;\n  explicit TestBoundaryCondition(CkMigrateMessage* m) : Base(m) {}\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestBoundaryCondition);\n#pragma GCC diagnostic pop\n\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<TestBoundaryCondition>(*this);\n  }\n\n  using argument_tags =\n      tmpl::list<ArgumentTag, VolumeArgumentTag, NonlinearArgumentTag>;\n  using volume_tags = tmpl::list<VolumeArgumentTag>;\n\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return {elliptic::BoundaryConditionType::Dirichlet};\n  }\n\n  // [example_poisson_fields]\n  static void apply(const gsl::not_null<Scalar<DataVector>*> field,\n                    const gsl::not_null<Scalar<DataVector>*> n_dot_flux,\n                    const int arg_on_face, const bool arg_from_volume,\n                    const int arg_nonlinear) {\n    // [example_poisson_fields]\n    CHECK(arg_on_face == 1);\n    CHECK(arg_from_volume);\n    CHECK(arg_nonlinear == 2);\n    std::fill(field->begin(), field->end(), 1.);\n    std::fill(n_dot_flux->begin(), n_dot_flux->end(), 2.);\n  }\n\n  using argument_tags_linearized = tmpl::list<ArgumentTag, VolumeArgumentTag>;\n  using volume_tags_linearized = tmpl::list<VolumeArgumentTag>;\n\n  static void apply_linearized(\n      const gsl::not_null<Scalar<DataVector>*> field_correction,\n      const gsl::not_null<Scalar<DataVector>*> n_dot_flux_correction,\n      const int arg_on_face, const bool arg_from_volume) {\n    CHECK(arg_on_face == 1);\n    CHECK(arg_from_volume);\n    std::fill(field_correction->begin(), field_correction->end(), 3.);\n    std::fill(n_dot_flux_correction->begin(), n_dot_flux_correction->end(), 4.);\n  }\n};\n\nPUP::able::PUP_ID TestBoundaryCondition::my_PUP_ID = 0;\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.BoundaryConditions.Base\", \"[Unit][Elliptic]\") {\n  // Factory-create a boundary condition and cast down to derived class\n  const auto created = TestHelpers::test_factory_creation<\n      BoundaryCondition<1>, TestBoundaryCondition>(\"TestBoundaryCondition\");\n  const auto& boundary_condition =\n      dynamic_cast<const TestBoundaryCondition&>(*created);\n\n  CHECK(created->boundary_condition_types() ==\n        std::vector<elliptic::BoundaryConditionType>{\n            elliptic::BoundaryConditionType::Dirichlet});\n\n  // Test applying boundary conditions\n  const auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Faces<1, ArgumentTag>, VolumeArgumentTag,\n                        domain::Tags::Faces<1, NonlinearArgumentTag>>>(\n      DirectionMap<1, int>{{Direction<1>::lower_xi(), 1}}, true,\n      DirectionMap<1, int>{{Direction<1>::lower_xi(), 2}});\n  const size_t num_points = 3;\n  Scalar<DataVector> boundary_field{num_points};\n  Scalar<DataVector> boundary_n_dot_flux{num_points};\n  // Nonlinear\n  elliptic::apply_boundary_condition<false, void,\n                                     tmpl::list<TestBoundaryCondition>>(\n      boundary_condition, box, Direction<1>::lower_xi(),\n      make_not_null(&boundary_field), make_not_null(&boundary_n_dot_flux));\n  CHECK(get(boundary_field) == DataVector{num_points, 1.});\n  CHECK(get(boundary_n_dot_flux) == DataVector{num_points, 2.});\n  // Linear\n  elliptic::apply_boundary_condition<true, void,\n                                     tmpl::list<TestBoundaryCondition>>(\n      boundary_condition, box, Direction<1>::lower_xi(),\n      make_not_null(&boundary_field), make_not_null(&boundary_n_dot_flux));\n  CHECK(get(boundary_field) == DataVector{num_points, 3.});\n  CHECK(get(boundary_n_dot_flux) == DataVector{num_points, 4.});\n}\n\n}  // namespace elliptic::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/BoundaryConditions/Test_BoundaryConditionType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace elliptic {\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.BoundaryConditionType\", \"[Unit][Elliptic]\") {\n  CHECK(get_output(BoundaryConditionType::Dirichlet) == \"Dirichlet\");\n  CHECK(get_output(BoundaryConditionType::Neumann) == \"Neumann\");\n  CHECK(TestHelpers::test_creation<BoundaryConditionType>(\"Dirichlet\") ==\n        BoundaryConditionType::Dirichlet);\n  CHECK(TestHelpers::test_creation<BoundaryConditionType>(\"Neumann\") ==\n        BoundaryConditionType::Neumann);\n\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<elliptic::BoundaryConditionType>(\"nil\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"nil\\\" to elliptic::BoundaryConditionType.\"));\n}\n\n}  // namespace elliptic\n"
  },
  {
    "path": "tests/Unit/Elliptic/BoundaryConditions/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/BoundaryConditions/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct FieldTag {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.BoundaryConditions.Tags\", \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_prefix_tag<\n      elliptic::Tags::BoundaryConditionType<FieldTag>>(\n      \"BoundaryConditionType(FieldTag)\");\n  TestHelpers::db::test_simple_tag<\n      elliptic::Tags::BoundaryConditionTypes<tmpl::list<FieldTag>>>(\n      \"BoundaryConditionTypes\");\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Actions)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(DiscontinuousGalerkin)\nadd_subdirectory(Python)\nadd_subdirectory(SubdomainPreconditioners)\nadd_subdirectory(Systems)\nadd_subdirectory(Triggers)\nadd_subdirectory(Utilities)\n\nset(LIBRARY \"Test_Elliptic\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataBoxTestHelpers\n  Elliptic\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticDgActions\")\n\nset(LIBRARY_SOURCES\n  Test_InitializeDomain.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  EllipticDg\n  Parallel\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/Actions/Test_InitializeDomain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim, typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<Dim>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              tmpl::list<domain::Tags::InitialRefinementLevels<Dim>,\n                         domain::Tags::InitialExtents<Dim>>>>>,\n\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<::elliptic::dg::Actions::InitializeDomain<Dim>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  using component_list = tmpl::list<ElementArray<Dim, Metavariables>>;\n};\n\nvoid test_initialize_domain(const Spectral::Quadrature quadrature) {\n  {\n    INFO(\"1D\");\n    // Reference element:\n    // [ |X| | ]-> xi\n    const ElementId<1> element_id{0, {{SegmentId{2, 1}}}};\n    const domain::creators::Interval domain_creator{\n        {{-0.5}},\n        {{1.5}},\n        {{2}},\n        {{4}},\n        {{false}},\n        {},\n        // Time dependence evaluates to a translation of +1 at t=0\n        std::make_unique<\n            domain::creators::time_dependence::UniformTranslation<1>>(\n            -1., std::array<double, 1>{{1.}})};\n\n    using metavariables = Metavariables<1>;\n    using element_array = ElementArray<1, metavariables>;\n\n    ActionTesting::MockRuntimeSystem<metavariables> runner{\n        {domain_creator.create_domain(), domain_creator.functions_of_time(),\n         quadrature}};\n    ActionTesting::emplace_component_and_initialize<element_array>(\n        &runner, element_id, {domain_creator.initial_refinement_levels(),\n                              domain_creator.initial_extents()});\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n    for (size_t i = 0; i < 2; ++i) {\n      ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                element_id);\n    }\n    const auto get_tag = [&runner, &element_id](auto tag_v) -> decltype(auto) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                                element_id);\n    };\n\n    CHECK(get_tag(domain::Tags::Mesh<1>{}) ==\n          Mesh<1>{4, Spectral::Basis::Legendre, quadrature});\n    CHECK(get_tag(domain::Tags::Element<1>{}) ==\n          Element<1>{element_id,\n                     {{Direction<1>::lower_xi(),\n                       {{ElementId<1>{0, {{SegmentId{2, 0}}}}},\n                        OrientationMap<1>::create_aligned()}},\n                      {Direction<1>::upper_xi(),\n                       {{ElementId<1>{0, {{SegmentId{2, 2}}}}},\n                        OrientationMap<1>::create_aligned()}}}});\n    const auto& element_map = get_tag(domain::Tags::ElementMap<1>{});\n    const auto& functions_of_time = get_tag(domain::Tags::FunctionsOfTime{});\n    const tnsr::I<DataVector, 1, Frame::ElementLogical>\n        logical_coords_for_element_map{{{{-1., -0.5, 0., 0.1, 1.}}}};\n    const auto inertial_coords_from_element_map =\n        element_map(logical_coords_for_element_map, 0., functions_of_time);\n    const tnsr::I<DataVector, 1, Frame::Inertial> expected_inertial_coords{\n        {{{1., 1.125, 1.25, 1.275, 1.5}}}};\n    CHECK_ITERABLE_APPROX(get<0>(inertial_coords_from_element_map),\n                          get<0>(expected_inertial_coords));\n    const auto& logical_coords =\n        get_tag(domain::Tags::Coordinates<1, Frame::ElementLogical>{});\n    CHECK(get<0>(logical_coords) ==\n          Spectral::collocation_points(\n              Mesh<1>{4, Spectral::Basis::Legendre, quadrature}));\n    const auto& inertial_coords =\n        get_tag(domain::Tags::Coordinates<1, Frame::Inertial>{});\n    CHECK(inertial_coords ==\n          element_map(logical_coords, 0., functions_of_time));\n    const auto& inverse_jacobian =\n        get_tag(domain::Tags::InverseJacobian<1, Frame::ElementLogical,\n                                              Frame::Inertial>{});\n    CHECK(inverse_jacobian ==\n          element_map.inv_jacobian(logical_coords, 0., functions_of_time));\n    const auto& det_inv_jacobian = get_tag(\n        domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>{});\n    CHECK(det_inv_jacobian == determinant(inverse_jacobian));\n  }\n  {\n    INFO(\"2D\");\n    // Reference element:\n    // ^ eta\n    // +-+-+-+-+> xi\n    // | |X| | |\n    // +-+-+-+-+\n    const ElementId<2> element_id{0, {{SegmentId{2, 1}, SegmentId{0, 0}}}};\n    const domain::creators::Rectangle domain_creator{\n        {{-0.5, 0.}}, {{1.5, 2.}}, {{2, 0}}, {{4, 3}}, {{false, false}}};\n    // Register the coordinate map for serialization\n    PUPable_reg(\n        SINGLE_ARG(domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                         domain::CoordinateMaps::ProductOf2Maps<\n                                             domain::CoordinateMaps::Affine,\n                                             domain::CoordinateMaps::Affine>>));\n\n    using metavariables = Metavariables<2>;\n    using element_array = ElementArray<2, metavariables>;\n\n    ActionTesting::MockRuntimeSystem<metavariables> runner{\n        {domain_creator.create_domain(), domain_creator.functions_of_time(),\n         quadrature}};\n    ActionTesting::emplace_component_and_initialize<element_array>(\n        &runner, element_id, {domain_creator.initial_refinement_levels(),\n                              domain_creator.initial_extents()});\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n    for (size_t i = 0; i < 2; ++i) {\n      ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                element_id);\n    }\n    const auto get_tag = [&runner, &element_id](auto tag_v) -> decltype(auto) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                                element_id);\n    };\n\n    CHECK(get_tag(domain::Tags::Mesh<2>{}) ==\n          Mesh<2>{{{4, 3}}, Spectral::Basis::Legendre, quadrature});\n    CHECK(get_tag(domain::Tags::Element<2>{}) ==\n          Element<2>{element_id,\n                     {{Direction<2>::lower_xi(),\n                       {{ElementId<2>{0, {{SegmentId{2, 0}, SegmentId{0, 0}}}}},\n                        OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_xi(),\n                       {{ElementId<2>{0, {{SegmentId{2, 2}, SegmentId{0, 0}}}}},\n                        OrientationMap<2>::create_aligned()}}}});\n    const auto& element_map = get_tag(domain::Tags::ElementMap<2>{});\n    const tnsr::I<DataVector, 2, Frame::ElementLogical>\n        logical_coords_for_element_map{\n            {{{-1., -0.5, 0., 0.1, 1.}, {-1., -0.5, 0., 0.1, 1.}}}};\n    const auto inertial_coords_from_element_map =\n        element_map(logical_coords_for_element_map);\n    const tnsr::I<DataVector, 2, Frame::Inertial> expected_inertial_coords{\n        {{{0., 0.125, 0.25, 0.275, 0.5}, {0., 0.5, 1., 1.1, 2.}}}};\n    CHECK_ITERABLE_APPROX(get<0>(inertial_coords_from_element_map),\n                          get<0>(expected_inertial_coords));\n    CHECK_ITERABLE_APPROX(get<1>(inertial_coords_from_element_map),\n                          get<1>(expected_inertial_coords));\n    const auto& logical_coords =\n        get_tag(domain::Tags::Coordinates<2, Frame::ElementLogical>{});\n    const auto& inertial_coords =\n        get_tag(domain::Tags::Coordinates<2, Frame::Inertial>{});\n    CHECK(inertial_coords == element_map(logical_coords));\n    const auto& inverse_jacobian =\n        get_tag(domain::Tags::InverseJacobian<2, Frame::ElementLogical,\n                                              Frame::Inertial>{});\n    CHECK(inverse_jacobian == element_map.inv_jacobian(logical_coords));\n    const auto& det_inv_jacobian = get_tag(\n        domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>{});\n    CHECK(det_inv_jacobian == determinant(inverse_jacobian));\n  }\n  {\n    INFO(\"3D\");\n    const ElementId<3> element_id{\n        0, {{SegmentId{2, 1}, SegmentId{0, 0}, SegmentId{1, 1}}}};\n    const domain::creators::Brick domain_creator{{{-0.5, 0., -1.}},\n                                                 {{1.5, 2., 3.}},\n                                                 {{2, 0, 1}},\n                                                 {{4, 3, 2}},\n                                                 {{false, false, false}}};\n    // Register the coordinate map for serialization\n    PUPable_reg(SINGLE_ARG(\n        domain::CoordinateMap<\n            Frame::BlockLogical, Frame::Inertial,\n            domain::CoordinateMaps::ProductOf3Maps<\n                domain::CoordinateMaps::Affine, domain::CoordinateMaps::Affine,\n                domain::CoordinateMaps::Affine>>));\n\n    using metavariables = Metavariables<3>;\n    using element_array = ElementArray<3, metavariables>;\n    ActionTesting::MockRuntimeSystem<metavariables> runner{\n        {domain_creator.create_domain(), domain_creator.functions_of_time(),\n         quadrature}};\n    ActionTesting::emplace_component_and_initialize<element_array>(\n        &runner, element_id, {domain_creator.initial_refinement_levels(),\n                              domain_creator.initial_extents()});\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n    for (size_t i = 0; i < 2; ++i) {\n      ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                element_id);\n    }\n    const auto get_tag = [&runner, &element_id](auto tag_v) -> decltype(auto) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                                element_id);\n    };\n\n    CHECK(get_tag(domain::Tags::Mesh<3>{}) ==\n          Mesh<3>{{{4, 3, 2}}, Spectral::Basis::Legendre, quadrature});\n    CHECK(get_tag(domain::Tags::Element<3>{}) ==\n          Element<3>{\n              element_id,\n              {{Direction<3>::lower_xi(),\n                {{ElementId<3>{\n                     0, {{SegmentId{2, 0}, SegmentId{0, 0}, SegmentId{1, 1}}}}},\n                 OrientationMap<3>::create_aligned()}},\n               {Direction<3>::upper_xi(),\n                {{ElementId<3>{\n                     0, {{SegmentId{2, 2}, SegmentId{0, 0}, SegmentId{1, 1}}}}},\n                 OrientationMap<3>::create_aligned()}},\n               {Direction<3>::lower_zeta(),\n                {{ElementId<3>{\n                     0, {{SegmentId{2, 1}, SegmentId{0, 0}, SegmentId{1, 0}}}}},\n                 OrientationMap<3>::create_aligned()}}}});\n    const auto& element_map = get_tag(domain::Tags::ElementMap<3>{});\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>\n        logical_coords_for_element_map{{{{-1., -0.5, 0., 0.1, 1.},\n                                         {-1., -0.5, 0., 0.1, 1.},\n                                         {-1., -0.5, 0., 0.1, 1.}}}};\n    const auto inertial_coords_from_element_map =\n        element_map(logical_coords_for_element_map);\n    const tnsr::I<DataVector, 3, Frame::Inertial> expected_inertial_coords{\n        {{{0., 0.125, 0.25, 0.275, 0.5},\n          {0., 0.5, 1., 1.1, 2.},\n          {1., 1.5, 2., 2.1, 3.}}}};\n    CHECK_ITERABLE_APPROX(get<0>(inertial_coords_from_element_map),\n                          get<0>(expected_inertial_coords));\n    CHECK_ITERABLE_APPROX(get<1>(inertial_coords_from_element_map),\n                          get<1>(expected_inertial_coords));\n    CHECK_ITERABLE_APPROX(get<2>(inertial_coords_from_element_map),\n                          get<2>(expected_inertial_coords));\n    const auto& logical_coords =\n        get_tag(domain::Tags::Coordinates<3, Frame::ElementLogical>{});\n    const auto& inertial_coords =\n        get_tag(domain::Tags::Coordinates<3, Frame::Inertial>{});\n    CHECK(inertial_coords == element_map(logical_coords));\n    const auto& inverse_jacobian =\n        get_tag(domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                              Frame::Inertial>{});\n    CHECK(inverse_jacobian == element_map.inv_jacobian(logical_coords));\n    const auto& det_inv_jacobian = get_tag(\n        domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>{});\n    CHECK(det_inv_jacobian == determinant(inverse_jacobian));\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelDG.InitializeDomain\", \"[Unit][Actions]\") {\n  domain::creators::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  test_initialize_domain(Spectral::Quadrature::GaussLobatto);\n  test_initialize_domain(Spectral::Quadrature::Gauss);\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticDG\")\n\nset(LIBRARY_SOURCES\n  Test_DgOperator.cpp\n  Test_Penalty.cpp\n  Test_Tags.cpp\n  Test_LargeOuterRadius.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  AmrCriteria\n  AnalyticSolutions\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainCreators\n  DomainStructure\n  Elliptic\n  EllipticDg\n  ErrorHandling\n  Parallel\n  ParallelAmr\n  Poisson\n  PoissonSolutions\n  Spectral\n  Utilities\n  Xcts\n  XctsSolutions\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(SubdomainOperator)\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/SubdomainOperator/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticDgSubdomainOperator\")\n\nset(LIBRARY_SOURCES\n  Test_SubdomainOperator.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstitutiveRelations\n  DataStructures\n  Domain\n  DomainBoundaryConditions\n  DomainCreators\n  DomainStructure\n  Elasticity\n  ElasticityActions\n  ElasticityBoundaryConditions\n  Elliptic\n  EllipticDg\n  EllipticDgSubdomainOperator\n  ErrorHandling\n  Parallel\n  ParallelSchwarz\n  Poisson\n  PoissonBoundaryConditions\n  Spectral\n  Utilities\n  ScalarSelfForce\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/SubdomainOperator/Test_SubdomainOperator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <blaze/math/DynamicMatrix.h>\n#include <blaze/math/DynamicVector.h>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/Cylinder.hpp\"\n#include \"Domain/Creators/Disk.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/ExpandOverBlocks.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/RotatedBricks.hpp\"\n#include \"Domain/Creators/RotatedIntervals.hpp\"\n#include \"Domain/Creators/RotatedRectangles.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Actions/InitializeBackgroundFields.hpp\"\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/ApplyOperator.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/InitializeSubdomain.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/SubdomainOperator.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/SubdomainOperator/Tags.hpp\"\n#include \"Elliptic/Systems/Elasticity/Actions/InitializeConstitutiveRelation.hpp\"\n#include \"Elliptic/Systems/Elasticity/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/FirstOrderSystem.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/BuildMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/SetData.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/EvaluateRefinementCriteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Random.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Actions/CommunicateOverlapFields.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing namespace std::complex_literals;\n\n// The tests in this file check that the subdomain operator is equivalent to\n// applying the full DG-operator to a domain where all points outside the\n// subdomain are zero. This should hold for every element in the domain, at any\n// refinement level (h and p) and for any number of overlap points.\n//\n// We use the action-testing framework for these tests because then we can have\n// the InitializeSubdomain action do the tedious job of constructing the\n// geometry. This has the added benefit that we test the subdomain operator is\n// consistent with the InitializeSubdomain action.\n\nstruct DummyOptionsGroup {};\n\ntemplate <typename SubdomainOperatorType>\nstruct SubdomainOperatorTag : db::SimpleTag {\n  using type = SubdomainOperatorType;\n};\n\ntemplate <typename Tag>\nstruct DgOperatorAppliedTo : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\ntemplate <typename Tag>\nstruct Operand : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\ntemplate <size_t Dim, typename Fields>\nstruct SubdomainDataTag : db::SimpleTag {\n  using type = LinearSolver::Schwarz::ElementCenteredSubdomainData<Dim, Fields>;\n};\n\ntemplate <size_t Dim, typename Fields>\nstruct SubdomainOperatorAppliedToDataTag : db::SimpleTag {\n  using type = LinearSolver::Schwarz::ElementCenteredSubdomainData<\n      Dim, db::wrap_tags_in<DgOperatorAppliedTo, Fields>>;\n};\n\nstruct OverrideBoundaryConditionsTag : db::SimpleTag {\n  using type = bool;\n};\n\ntemplate <typename System>\nstd::unique_ptr<typename System::boundary_conditions_base>\nmake_boundary_condition(\n    const elliptic::BoundaryConditionType boundary_condition_type) {\n  return std::make_unique<\n      elliptic::BoundaryConditions::AnalyticSolution<System>>(\n      nullptr, boundary_condition_type);\n}\n\n// Generate some random element-centered subdomain data on each element\ntemplate <typename SubdomainOperator, typename Fields>\nstruct InitializeRandomSubdomainData {\n  using simple_tags =\n      tmpl::list<SubdomainDataTag<SubdomainOperator::volume_dim, Fields>>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    using SubdomainData = typename SubdomainDataTag<Dim, Fields>::type;\n\n    db::mutate<SubdomainDataTag<Dim, Fields>>(\n        [](const auto subdomain_data, const auto& mesh, const auto& element,\n           const auto& all_overlap_meshes, const auto& all_overlap_extents) {\n          MAKE_GENERATOR(gen);\n          UniformCustomDistribution<double> dist{-1., 1.};\n          subdomain_data->element_data =\n              make_with_random_values<typename SubdomainData::ElementData>(\n                  make_not_null(&gen), make_not_null(&dist),\n                  mesh.number_of_grid_points());\n          for (const auto& [direction, neighbors] : element.neighbors()) {\n            for (const auto& neighbor_id : neighbors) {\n              const auto& orientation = neighbors.orientation(neighbor_id);\n              const auto overlap_id =\n                  DirectionalId<Dim>{direction, neighbor_id};\n              const size_t overlap_extent = all_overlap_extents.at(overlap_id);\n              if (overlap_extent == 0) {\n                continue;\n              }\n              subdomain_data->overlap_data.emplace(\n                  overlap_id,\n                  make_with_random_values<typename SubdomainData::OverlapData>(\n                      make_not_null(&gen), make_not_null(&dist),\n                      LinearSolver::Schwarz::overlap_num_points(\n                          all_overlap_meshes.at(overlap_id).extents(),\n                          overlap_extent, orientation(direction).dimension())));\n            }\n          }\n        },\n        make_not_null(&box), db::get<domain::Tags::Mesh<Dim>>(box),\n        db::get<domain::Tags::Element<Dim>>(box),\n        db::get<LinearSolver::Schwarz::Tags::Overlaps<domain::Tags::Mesh<Dim>,\n                                                      Dim, DummyOptionsGroup>>(\n            box),\n        db::get<LinearSolver::Schwarz::Tags::Overlaps<\n            elliptic::dg::subdomain_operator::Tags::ExtrudingExtent, Dim,\n            DummyOptionsGroup>>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Generate random data for background fields. Not using a RNG because\n// evaluating the background on the same coordinates should return the same\n// data. Instead, we just pick some arbitrary combination of the coordinate\n// values.\ntemplate <size_t Dim>\nstruct RandomBackground : elliptic::analytic_data::Background {\n  RandomBackground() = default;\n  explicit RandomBackground(CkMigrateMessage* m)\n      : elliptic::analytic_data::Background(m) {}\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(RandomBackground);  // NOLINT\n#pragma GCC diagnostic pop\n\n  // NOLINTBEGIN(readability-convert-member-functions-to-static)\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(  // NOLINT\n      const tnsr::I<DataVector, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    return {variables(x, RequestedTags{})...};\n  }\n  // [background_vars_fct_derivs]\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(  // NOLINT\n      const tnsr::I<DataVector, Dim>& x, const Mesh<Dim>& /*mesh*/,\n      const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                            Frame::Inertial>&\n      /*inv_jacobian*/,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    // [background_vars_fct_derivs]\n    return variables(x, tmpl::list<RequestedTags...>{});\n  }\n  // NOLINTEND(readability-convert-member-functions-to-static)\n  static tnsr::II<DataVector, Dim> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      gr::Tags::InverseSpatialMetric<DataVector, Dim> /*meta*/) {\n    tnsr::II<DataVector, Dim> inv_metric{x.begin()->size()};\n    const DataVector r = get(magnitude(x));\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        inv_metric.get(i, j) = x.get(i) * x.get(j) / (1. + square(r));\n      }\n      inv_metric.get(i, i) += 1.;\n    }\n    return inv_metric;\n  }\n  static tnsr::i<DataVector, Dim> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      gr::Tags::SpatialChristoffelSecondKindContracted<DataVector,\n                                                       Dim> /*meta*/) {\n    tnsr::i<DataVector, Dim> result{x.begin()->size()};\n    const DataVector r = get(magnitude(x));\n    for (size_t i = 0; i < Dim; ++i) {\n      result.get(i) = x.get(i) / (1. + r);\n    }\n    return result;\n  }\n  // ScalarSelfForce background to test complex numbers\n  static Scalar<ComplexDataVector> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      ScalarSelfForce::Tags::Alpha /*meta*/) {\n    Scalar<ComplexDataVector> result{x.begin()->size()};\n    const DataVector r = get(magnitude(x));\n    get(result) = r;\n    return result;\n  }\n  static Scalar<ComplexDataVector> variables(\n      const tnsr::I<DataVector, Dim>& x, ScalarSelfForce::Tags::Beta /*meta*/) {\n    Scalar<ComplexDataVector> result{x.begin()->size()};\n    const DataVector r = get(magnitude(x));\n    get(result) = r + 1i * square(r);\n    return result;\n  }\n  static tnsr::i<ComplexDataVector, Dim> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      ScalarSelfForce::Tags::Gamma /*meta*/) {\n    tnsr::i<ComplexDataVector, Dim> result{x.begin()->size()};\n    const DataVector r = get(magnitude(x));\n    for (size_t i = 0; i < Dim; ++i) {\n      result.get(i) = x.get(i) / (1. + r) + 1i * square(r);\n    }\n    return result;\n  }\n};\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID RandomBackground<Dim>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <typename SubdomainOperator, typename Fields>\nstruct ApplySubdomainOperator {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& subdomain_data = db::get<SubdomainDataTag<Dim, Fields>>(box);\n\n    // Override boundary conditions\n    using system = typename SubdomainOperator::system;\n    using BoundaryConditionsBase = typename system::boundary_conditions_base;\n    std::unique_ptr<BoundaryConditionsBase> bc_override{};\n    std::unordered_map<std::pair<size_t, Direction<Dim>>,\n                       const BoundaryConditionsBase&,\n                       boost::hash<std::pair<size_t, Direction<Dim>>>>\n        override_boundary_conditions{};\n    if (db::get<OverrideBoundaryConditionsTag>(box)) {\n      bc_override = make_boundary_condition<system>(\n          elliptic::BoundaryConditionType::Dirichlet);\n      for (const auto& block :\n           db::get<domain::Tags::Domain<Dim>>(box).blocks()) {\n        for (const auto& direction : block.external_boundaries()) {\n          override_boundary_conditions.emplace(\n              std::make_pair(block.id(), direction), *bc_override);\n        }\n      }\n    }\n\n    // Apply the subdomain operator\n    const auto& subdomain_operator =\n        db::get<SubdomainOperatorTag<SubdomainOperator>>(box);\n    auto subdomain_result = make_with_value<\n        typename SubdomainOperatorAppliedToDataTag<Dim, Fields>::type>(\n        subdomain_data, 0.);\n    subdomain_operator(make_not_null(&subdomain_result), subdomain_data, box,\n                       override_boundary_conditions);\n\n    // Store result in the DataBox for checks\n    db::mutate<SubdomainOperatorAppliedToDataTag<Dim, Fields>>(\n        [&subdomain_result](const auto subdomain_operator_applied_to_data) {\n          *subdomain_operator_applied_to_data = std::move(subdomain_result);\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename SubdomainOperator, typename Fields>\nstruct TestSubdomainOperatorMatrix {\n  using ValueType = typename Variables<Fields>::value_type;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            size_t Dim, typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& subdomain_data = db::get<SubdomainDataTag<Dim, Fields>>(box);\n\n    // Build the explicit matrix representation of the subdomain operator\n    // Note: This would be an interesting unit of work to benchmark.\n    const auto& subdomain_operator =\n        db::get<SubdomainOperatorTag<SubdomainOperator>>(box);\n    const size_t operator_size = subdomain_data.size();\n    blaze::DynamicMatrix<ValueType, blaze::columnMajor> operator_matrix{\n        operator_size, operator_size};\n    auto operand_buffer =\n        make_with_value<std::decay_t<decltype(subdomain_data)>>(subdomain_data,\n                                                                0.);\n    auto result_buffer = make_with_value<\n        typename SubdomainOperatorAppliedToDataTag<Dim, Fields>::type>(\n        subdomain_data, 0.);\n    ::LinearSolver::Serial::build_matrix(\n        make_not_null(&operator_matrix), make_not_null(&operand_buffer),\n        make_not_null(&result_buffer), subdomain_operator,\n        std::forward_as_tuple(box));\n\n    // Check the matrix is equivalent to the operator by applying it to the\n    // data. We need to do the matrix multiplication on a contiguous buffer\n    // because the `ElementCenteredSubdomainData` is not contiguous.\n    blaze::DynamicVector<ValueType> contiguous_operand(operator_size);\n    std::copy(subdomain_data.begin(), subdomain_data.end(),\n              contiguous_operand.begin());\n    const blaze::DynamicVector<ValueType> contiguous_result =\n        operator_matrix * contiguous_operand;\n    std::copy(contiguous_result.begin(), contiguous_result.end(),\n              result_buffer.begin());\n    const auto& expected_operator_applied_to_data =\n        db::get<SubdomainOperatorAppliedToDataTag<Dim, Fields>>(box);\n    const Approx custom_approx = Approx::custom().epsilon(1.e-8).scale(1.0);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        result_buffer, expected_operator_applied_to_data, custom_approx);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename Metavariables, typename System, typename SubdomainOperator,\n          typename ExtraInitActions>\nstruct ElementArray {\n  static constexpr size_t Dim = SubdomainOperator::volume_dim;\n  static constexpr bool has_background_fields =\n      not std::is_same_v<typename System::background_fields, tmpl::list<>>;\n\n  // We prefix the system fields with an \"operand\" tag to make sure the\n  // subdomain operator works with prefixed variables\n  using fields_tag = ::Tags::Variables<\n      db::wrap_tags_in<Operand, typename System::primal_fields>>;\n  using fluxes_tag = ::Tags::Variables<\n      db::wrap_tags_in<Operand, typename System::primal_fluxes>>;\n  using operator_applied_to_fields_tag =\n      db::add_tag_prefix<DgOperatorAppliedTo, fields_tag>;\n  using subdomain_operator_applied_to_fields_tag =\n      SubdomainOperatorAppliedToDataTag<Dim, typename fields_tag::tags_list>;\n\n  using dg_operator =\n      ::elliptic::dg::Actions::DgOperator<System, true, fields_tag, fluxes_tag,\n                                          operator_applied_to_fields_tag>;\n\n  using background_tag =\n      elliptic::Tags::Background<elliptic::analytic_data::Background>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<Dim>, background_tag>;\n\n  // These tags are communicated on subdomain overlaps to initialize the\n  // subdomain geometry. AMR updates these tags, so we have to communicate them\n  // after each AMR step.\n  using subdomain_init_tags =\n      tmpl::list<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                 domain::Tags::NeighborMesh<Dim>>;\n  using init_subdomain_action =\n      ::elliptic::dg::subdomain_operator::Actions::InitializeSubdomain<\n          System, background_tag, DummyOptionsGroup, false>;\n  using init_random_subdomain_data_action =\n      InitializeRandomSubdomainData<SubdomainOperator,\n                                    typename fields_tag::tags_list>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n                         tmpl::list<domain::Tags::InitialRefinementLevels<Dim>,\n                                    domain::Tags::InitialExtents<Dim>,\n                                    SubdomainOperatorTag<SubdomainOperator>,\n                                    subdomain_operator_applied_to_fields_tag,\n                                    OverrideBoundaryConditionsTag>>,\n                     ::elliptic::dg::Actions::InitializeDomain<Dim>,\n                     ::elliptic::dg::Actions::initialize_operator<\n                         System, background_tag>,\n                     Initialization::Actions::InitializeItems<\n                         ::amr::Initialization::Initialize<Dim, metavariables>>,\n                     ExtraInitActions, Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<\n              // Init subdomain\n              LinearSolver::Schwarz::Actions::SendOverlapFields<\n                  subdomain_init_tags, DummyOptionsGroup, false,\n                  typename dg_operator::temporal_id_tag>,\n              LinearSolver::Schwarz::Actions::ReceiveOverlapFields<\n                  Dim, subdomain_init_tags, DummyOptionsGroup, false,\n                  typename dg_operator::temporal_id_tag>,\n              init_subdomain_action, init_random_subdomain_data_action,\n              Parallel::Actions::TerminatePhase,\n              // Full DG operator\n              typename dg_operator::apply_actions,\n              Parallel::Actions::TerminatePhase,\n              // Subdomain operator\n              ApplySubdomainOperator<SubdomainOperator,\n                                     typename fields_tag::tags_list>,\n              TestSubdomainOperatorMatrix<SubdomainOperator,\n                                          typename fields_tag::tags_list>,\n              Parallel::Actions::TerminatePhase>>>;\n};\n\ntemplate <typename Metavariables>\nstruct AmrComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using component_being_mocked = ::amr::Component<Metavariables>;\n  using const_global_cache_tags =\n      tmpl::list<::amr::Criteria::Tags::Criteria, ::amr::Tags::Policies,\n                 ::amr::Tags::AmrBlocks<Metavariables::volume_dim>,\n                 logging::Tags::Verbosity<::amr::OptionTags::AmrGroup>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<>>>>>;\n};\n\ntemplate <typename System, typename SubdomainOperator,\n          typename ExtraInitActions>\nstruct Metavariables {\n  using element_array =\n      ElementArray<Metavariables, System, SubdomainOperator, ExtraInitActions>;\n  using amr_component = AmrComponent<Metavariables>;\n  using component_list = tmpl::list<element_array, amr_component>;\n  using const_global_cache_tags = tmpl::list<>;\n  static constexpr size_t volume_dim = System::volume_dim;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            elliptic::BoundaryConditions::BoundaryCondition<volume_dim>,\n            tmpl::list<elliptic::BoundaryConditions::AnalyticSolution<System>>>,\n        tmpl::pair<elliptic::analytic_data::Background,\n                   tmpl::list<RandomBackground<volume_dim>>>,\n        tmpl::pair<::amr::Criterion, tmpl::list<::amr::Criteria::Random<\n                                         amr::Criteria::Type::p>>>>;\n  };\n  template <typename Tag>\n  using overlaps_tag =\n      LinearSolver::Schwarz::Tags::Overlaps<Tag, volume_dim, DummyOptionsGroup>;\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = ElementArray<Metavariables, System, SubdomainOperator,\n                                       ExtraInitActions>;\n    using projectors = tmpl::flatten<tmpl::list<\n        ::amr::projectors::DefaultInitialize<tmpl::append<\n            tmpl::list<domain::Tags::InitialExtents<volume_dim>,\n                       domain::Tags::InitialRefinementLevels<volume_dim>,\n                       SubdomainOperatorTag<SubdomainOperator>,\n                       typename element_array::\n                           subdomain_operator_applied_to_fields_tag,\n                       typename element_array::fields_tag>,\n            // Tags communicated on subdomain overlaps. No need to project\n            // these during AMR because they will be communicated.\n            db::wrap_tags_in<overlaps_tag,\n                             typename element_array::subdomain_init_tags>,\n            // Tags initialized on subdomains. No need to project these during\n            // AMR because they will get re-initialized after communication.\n            typename element_array::init_subdomain_action::simple_tags,\n            typename element_array::init_random_subdomain_data_action::\n                simple_tags>>,\n        ::amr::projectors::CopyFromCreatorOrLeaveAsIs<\n            OverrideBoundaryConditionsTag,\n            // Work around a segfault because this tag isn't handled\n            // correctly by the testing framework\n            Parallel::Tags::GlobalCache<Metavariables>>,\n        elliptic::dg::ProjectGeometry<volume_dim>,\n        elliptic::dg::Actions::amr_projectors<\n            System, typename element_array::background_tag>,\n        typename element_array::dg_operator::amr_projectors, ExtraInitActions>>;\n    static constexpr bool keep_coarse_grids = false;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n// The test should work for any elliptic system. For systems with fluxes or\n// sources that take arguments out of the DataBox this test can insert actions\n// that initialize those arguments.\ntemplate <typename System, typename ExtraInitActions = tmpl::list<>,\n          size_t Dim = System::volume_dim>\nvoid test_subdomain_operator(\n    const DomainCreator<Dim>& domain_creator,\n    const bool use_massive_dg_operator = true,\n    const Spectral::Quadrature quadrature = Spectral::Quadrature::GaussLobatto,\n    const bool override_boundary_conditions = false,\n    // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n    const std::optional<size_t> max_overlap = 3,\n    const double penalty_parameter = 1.2) {\n  CAPTURE(Dim);\n  CAPTURE(penalty_parameter);\n  CAPTURE(use_massive_dg_operator);\n\n  using SubdomainOperator =\n      elliptic::dg::subdomain_operator::SubdomainOperator<System,\n                                                          DummyOptionsGroup>;\n\n  using metavariables =\n      Metavariables<System, SubdomainOperator, ExtraInitActions>;\n  using element_array = typename metavariables::element_array;\n\n  using fields_tag = typename element_array::fields_tag;\n  using operator_applied_to_fields_tag =\n      typename element_array::operator_applied_to_fields_tag;\n  using subdomain_data_tag =\n      SubdomainDataTag<Dim, typename fields_tag::tags_list>;\n  using subdomain_operator_applied_to_fields_tag =\n      typename element_array::subdomain_operator_applied_to_fields_tag;\n\n  register_factory_classes_with_charm<metavariables>();\n\n  // Select randomly which iteration of the loops below perform expensive tests.\n  // If max_overlap=nullopt then a full-element overlap is tested and the random\n  // selection is disabled.\n  MAKE_GENERATOR(gen);\n  std::uniform_int_distribution<size_t> dist_select_overlap(\n      0, max_overlap.value_or(0));\n  const size_t rnd_overlap =\n      max_overlap.has_value() ? dist_select_overlap(gen) : 0;\n\n  // The test should hold for any number of overlap points\n  for (size_t overlap = 0; overlap <= max_overlap.value_or(0); overlap++) {\n    CAPTURE(overlap);\n\n    // Re-create the domain in every iteration of this loop because it's not\n    // copyable\n    auto domain = domain_creator.create_domain();\n    auto boundary_conditions = domain_creator.external_boundary_conditions();\n    const auto initial_ref_levs = domain_creator.initial_refinement_levels();\n    const auto initial_extents = domain_creator.initial_extents();\n    const auto element_ids = ::initial_element_ids(initial_ref_levs);\n    CAPTURE(element_ids.size());\n\n    // Configure AMR criteria\n    std::vector<std::unique_ptr<::amr::Criterion>> amr_criteria{};\n    amr_criteria.push_back(\n        std::make_unique<::amr::Criteria::Random<amr::Criteria::Type::p>>(\n            std::unordered_map<::amr::Flag, size_t>{\n                // h-refinement is not supported yet in the action testing\n                // framework\n                {::amr::Flag::IncreaseResolution, 1},\n                {::amr::Flag::DoNothing, 1}}));\n\n    ActionTesting::MockRuntimeSystem<metavariables> runner{tuples::TaggedTuple<\n        domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTimeInitialize,\n        domain::Tags::ExternalBoundaryConditions<Dim>,\n        elliptic::Tags::Background<elliptic::analytic_data::Background>,\n        LinearSolver::Schwarz::Tags::MaxOverlap<DummyOptionsGroup>,\n        logging::Tags::Verbosity<DummyOptionsGroup>,\n        elliptic::dg::Tags::PenaltyParameter, elliptic::dg::Tags::Massive,\n        elliptic::dg::Tags::Quadrature, elliptic::dg::Tags::Formulation,\n        ::amr::Criteria::Tags::Criteria, ::amr::Tags::AmrBlocks<Dim>,\n        ::amr::Tags::Policies,\n        logging::Tags::Verbosity<::amr::OptionTags::AmrGroup>>{\n        std::move(domain), domain_creator.functions_of_time(),\n        std::move(boundary_conditions),\n        std::make_unique<RandomBackground<Dim>>(),\n        max_overlap.has_value() ? std::optional<size_t>(overlap) : std::nullopt,\n        ::Verbosity::Verbose, penalty_parameter, use_massive_dg_operator,\n        quadrature, ::dg::Formulation::StrongInertial, std::move(amr_criteria),\n        std::nullopt,\n        ::amr::Policies{::amr::Isotropy::Anisotropic, ::amr::Limits{}, true,\n                        true},\n        ::Verbosity::Debug}};\n\n    // Initialize all elements, generating random subdomain data\n    for (const auto& element_id : element_ids) {\n      CAPTURE(element_id);\n      ActionTesting::emplace_component_and_initialize<element_array>(\n          &runner, element_id,\n          {initial_ref_levs, initial_extents, SubdomainOperator{},\n           typename subdomain_operator_applied_to_fields_tag::type{},\n           override_boundary_conditions});\n      while (\n          not ActionTesting::get_terminate<element_array>(runner, element_id)) {\n        ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                  element_id);\n      }\n    }\n    // DataBox shortcuts\n    const auto get_tag = [&runner](const ElementId<Dim>& local_element_id,\n                                   auto tag_v) -> decltype(auto) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      return ActionTesting::get_databox_tag<element_array, tag>(\n          runner, local_element_id);\n    };\n    const auto set_tag = [&runner](const ElementId<Dim>& local_element_id,\n                                   auto tag_v, const auto& value) {\n      using tag = std::decay_t<decltype(tag_v)>;\n      ActionTesting::simple_action<element_array,\n                                   ::Actions::SetData<tmpl::list<tag>>>(\n          make_not_null(&runner), local_element_id, value);\n    };\n\n    const auto test_subdomain_operator_equals_dg_operator = [&runner,\n                                                             &element_ids,\n                                                             &overlap,\n                                                             &rnd_overlap, &gen,\n                                                             &get_tag,\n                                                             &set_tag]() {\n      ActionTesting::set_phase(make_not_null(&runner),\n                               Parallel::Phase::Testing);\n      for (const auto& element_id : element_ids) {\n        ActionTesting::next_action_if_ready<element_array>(\n            make_not_null(&runner), element_id);\n      }\n      for (const auto& element_id : element_ids) {\n        while (not ActionTesting::get_terminate<element_array>(runner,\n                                                               element_id) and\n               ActionTesting::next_action_if_ready<element_array>(\n                   make_not_null(&runner), element_id)) {\n        }\n      }\n\n      // For selection of expensive tests\n      std::uniform_int_distribution<size_t> dist_select_subdomain_center(\n          0, element_ids.size() - 1);\n      const auto& rnd_subdomain_center = dist_select_subdomain_center(gen);\n      size_t subdomain_center_id = 0;\n\n      // Take each element as the subdomain-center in turn\n      for (const auto& subdomain_center : element_ids) {\n        CAPTURE(subdomain_center);\n\n        // First, reset the data on all elements to zero\n        for (const auto& element_id : element_ids) {\n          set_tag(element_id, fields_tag{},\n                  typename fields_tag::type{\n                      get_tag(element_id, domain::Tags::Mesh<Dim>{})\n                          .number_of_grid_points(),\n                      0.});\n        }\n\n        // Set data on the central element and its neighbors to the subdomain\n        // data\n        const auto& subdomain_data =\n            get_tag(subdomain_center, subdomain_data_tag{});\n        const auto& all_overlap_extents =\n            get_tag(subdomain_center,\n                    LinearSolver::Schwarz::Tags::Overlaps<\n                        elliptic::dg::subdomain_operator::Tags::ExtrudingExtent,\n                        Dim, DummyOptionsGroup>{});\n        const auto& central_element =\n            get_tag(subdomain_center, domain::Tags::Element<Dim>{});\n        set_tag(subdomain_center, fields_tag{}, subdomain_data.element_data);\n        for (const auto& [overlap_id, overlap_data] :\n             subdomain_data.overlap_data) {\n          const auto& direction = overlap_id.direction();\n          const auto& neighbor_id = overlap_id.id();\n          const auto direction_from_neighbor =\n              central_element.neighbors().at(direction).orientation(\n                  neighbor_id)(direction.opposite());\n          set_tag(\n              neighbor_id, fields_tag{},\n              LinearSolver::Schwarz::extended_overlap_data(\n                  overlap_data,\n                  get_tag(neighbor_id, domain::Tags::Mesh<Dim>{}).extents(),\n                  all_overlap_extents.at(overlap_id), direction_from_neighbor));\n        }\n\n        // Run actions to compute the full DG-operator\n        for (const auto& element_id : element_ids) {\n          CAPTURE(element_id);\n          runner.template mock_distributed_objects<element_array>()\n              .at(element_id)\n              .force_next_action_to_be(5);\n          runner.template mock_distributed_objects<element_array>()\n              .at(element_id)\n              .set_terminate(false);\n          while (not ActionTesting::get_terminate<element_array>(runner,\n                                                                 element_id) and\n                 ActionTesting::next_action_if_ready<element_array>(\n                     make_not_null(&runner), element_id)) {\n          }\n        }\n        // Break here so all elements have sent mortar data before receiving it\n        for (const auto& element_id : element_ids) {\n          CAPTURE(element_id);\n          while (not ActionTesting::get_terminate<element_array>(runner,\n                                                                 element_id) and\n                 ActionTesting::next_action_if_ready<element_array>(\n                     make_not_null(&runner), element_id)) {\n          }\n        }\n\n        // Invoke ApplySubdomainOperator action only on the subdomain center\n        ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                  subdomain_center);\n\n        // Test that the subdomain operator and the full DG-operator computed\n        // the same result within the subdomain\n        const auto& subdomain_result = get_tag(\n            subdomain_center, subdomain_operator_applied_to_fields_tag{});\n        Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n        CHECK_VARIABLES_CUSTOM_APPROX(\n            subdomain_result.element_data,\n            get_tag(subdomain_center, operator_applied_to_fields_tag{}),\n            custom_approx);\n        REQUIRE(subdomain_result.overlap_data.size() ==\n                subdomain_data.overlap_data.size());\n        for (const auto& [overlap_id, overlap_result] :\n             subdomain_result.overlap_data) {\n          CAPTURE(overlap_id);\n          const auto& direction = overlap_id.direction();\n          const auto& neighbor_id = overlap_id.id();\n          const auto direction_from_neighbor =\n              central_element.neighbors().at(direction).orientation(\n                  neighbor_id)(direction.opposite());\n          const auto expected_overlap_result =\n              LinearSolver::Schwarz::data_on_overlap(\n                  get_tag(neighbor_id, operator_applied_to_fields_tag{}),\n                  get_tag(neighbor_id, domain::Tags::Mesh<Dim>{}).extents(),\n                  all_overlap_extents.at(overlap_id), direction_from_neighbor);\n          CHECK_VARIABLES_CUSTOM_APPROX(overlap_result, expected_overlap_result,\n                                        custom_approx);\n        }\n\n        // Now build the matrix representation of the subdomain operator\n        // explicitly, and apply it to the data to make sure the matrix is\n        // equivalent to the matrix-free operator. This is important to test\n        // because the subdomain operator includes optimizations for when it is\n        // invoked on sparse data, i.e. data that is mostly zero, which is the\n        // case when building it explicitly column-by-column.\n        if (overlap == rnd_overlap and\n            subdomain_center_id == rnd_subdomain_center) {\n          ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                    subdomain_center);\n        }\n        ++subdomain_center_id;\n      }  // loop over subdomain centers\n    };\n\n    test_subdomain_operator_equals_dg_operator();\n\n    INFO(\"Run AMR!\");\n    const auto invoke_all_simple_actions = [&runner, &element_ids]() {\n      bool quiescence = false;\n      while (not quiescence) {\n        quiescence = true;\n        for (const auto& element_id : element_ids) {\n          while (not ActionTesting::is_simple_action_queue_empty<element_array>(\n              runner, element_id)) {\n            ActionTesting::invoke_queued_simple_action<element_array>(\n                make_not_null(&runner), element_id);\n            quiescence = false;\n          }\n        }\n      }\n    };\n    using amr_component = typename metavariables::amr_component;\n    ActionTesting::emplace_singleton_component<amr_component>(\n        make_not_null(&runner), ActionTesting::NodeId{0},\n        ActionTesting::LocalCoreId{0});\n    auto& cache = ActionTesting::cache<amr_component>(runner, 0);\n    Parallel::simple_action<::amr::Actions::EvaluateRefinementCriteria>(\n        Parallel::get_parallel_component<element_array>(cache));\n    invoke_all_simple_actions();\n    Parallel::simple_action<::amr::Actions::AdjustDomain>(\n        Parallel::get_parallel_component<element_array>(cache));\n    invoke_all_simple_actions();\n\n    // Test again after AMR\n    test_subdomain_operator_equals_dg_operator();\n  }  // loop over overlaps\n}\n\n// Add a constitutive relation for elasticity systems to the DataBox\ntemplate <size_t Dim>\nstruct InitializeConstitutiveRelation\n    : tt::ConformsTo<::amr::protocols::Projector> {\n public:  // Iterative action\n  using simple_tags =\n      tmpl::list<Elasticity::Tags::ConstitutiveRelationPerBlock<Dim>>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<InitializeConstitutiveRelation>(make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // amr::protocols::Projector\n  using return_tags = simple_tags;\n  using argument_tags = tmpl::list<domain::Tags::Domain<Dim>>;\n  template <typename... AmrData>\n  static void apply(\n      const gsl::not_null<std::vector<std::unique_ptr<\n          Elasticity::ConstitutiveRelations::ConstitutiveRelation<Dim>>>*>\n          constitutive_relations,\n      const Domain<Dim>& domain, const AmrData&... /*amr_data*/) {\n    const domain::ExpandOverBlocks<std::unique_ptr<\n        Elasticity::ConstitutiveRelations::ConstitutiveRelation<Dim>>>\n        expand_over_blocks{domain.blocks().size()};\n    *constitutive_relations = expand_over_blocks(\n        std::make_unique<\n            Elasticity::ConstitutiveRelations::IsotropicHomogeneous<Dim>>(1.,\n                                                                          2.));\n  }\n};\n\n}  // namespace\n\n// This test constructs a selection of domains and tests the subdomain operator\n// for _every_ element in those domains and for a range of overlaps. We increase\n// the timeout for the test because going over so many elements is relatively\n// expensive but also very important to ensure that the subdomain operator\n// handles all of these geometries correctly.\n// [[TimeOut, 40]]\nSPECTRE_TEST_CASE(\"Unit.Elliptic.DG.SubdomainOperator\", \"[Unit][Elliptic]\") {\n  domain::creators::register_derived_with_charm();\n  {\n    INFO(\"Rectilinear and aligned\");\n    {\n      INFO(\"1D\");\n      using system = Poisson::FirstOrderSystem<1, Poisson::Geometry::Curved>;\n      const domain::creators::Interval domain_creator{\n          {{-2.}},\n          {{2.}},\n          {{1}},\n          {{3}},\n          {{{{make_boundary_condition<system>(\n                  elliptic::BoundaryConditionType::Dirichlet),\n              make_boundary_condition<system>(\n                  elliptic::BoundaryConditionType::Neumann)}}}}};\n      for (const auto& [use_massive_dg_operator, quadrature, max_overlap] :\n           cartesian_product(\n               make_array(false, true),\n               make_array(Spectral::Quadrature::GaussLobatto,\n                          Spectral::Quadrature::Gauss),\n               std::array<std::optional<size_t>, 2>{{3, std::nullopt}})) {\n        test_subdomain_operator<system>(domain_creator, use_massive_dg_operator,\n                                        quadrature, false, max_overlap);\n      }\n    }\n    {\n      INFO(\"2D\");\n      using system = Poisson::FirstOrderSystem<2, Poisson::Geometry::Curved>;\n      const auto dirichlet_bc = make_boundary_condition<system>(\n          elliptic::BoundaryConditionType::Dirichlet);\n      const domain::creators::Rectangle domain_creator{\n          {{-2., 0.}},\n          {{2., 1.}},\n          {{1, 1}},\n          {{3, 3}},\n          {{{{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n            {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}}}}};\n      test_subdomain_operator<system>(domain_creator);\n      test_subdomain_operator<system>(domain_creator, true,\n                                      Spectral::Quadrature::GaussLobatto, true);\n    }\n    {\n      INFO(\"3D\");\n      using system = Poisson::FirstOrderSystem<3, Poisson::Geometry::Curved>;\n      const auto dirichlet_bc = make_boundary_condition<system>(\n          elliptic::BoundaryConditionType::Dirichlet);\n      const domain::creators::Brick domain_creator{\n          {{-2., 0., -1.}},\n          {{2., 1., 1.}},\n          {{1, 0, 1}},\n          {{3, 3, 3}},\n          {{{{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n            {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n            {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}}}}};\n      test_subdomain_operator<system>(domain_creator);\n    }\n  }\n  {\n    INFO(\"Rotated\");\n    {\n      INFO(\"1D\");\n      using system = Poisson::FirstOrderSystem<1, Poisson::Geometry::Curved>;\n      const domain::creators::RotatedIntervals domain_creator{\n          {{-2.}},\n          {{0.}},\n          {{2.}},\n          {{1}},\n          {{{{3, 3}}}},\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Dirichlet),\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Neumann),\n          nullptr};\n      test_subdomain_operator<system>(domain_creator);\n    }\n    {\n      INFO(\"2D\");\n      using system = Poisson::FirstOrderSystem<2, Poisson::Geometry::Curved>;\n      const domain::creators::RotatedRectangles domain_creator{\n          {{-2., 0.}},\n          {{0., 0.5}},\n          {{2., 1.}},\n          {{1, 1}},\n          {{{{3, 3}}, {{3, 3}}}},\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Dirichlet),\n          nullptr};\n      test_subdomain_operator<system>(domain_creator);\n    }\n    {\n      INFO(\"2D flat-cartesian\");\n      using system =\n          Poisson::FirstOrderSystem<2, Poisson::Geometry::FlatCartesian>;\n      const domain::creators::RotatedRectangles domain_creator{\n          {{-2., 0.}},\n          {{0., 0.5}},\n          {{2., 1.}},\n          {{1, 1}},\n          {{{{3, 3}}, {{3, 3}}}},\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Dirichlet),\n          nullptr};\n      test_subdomain_operator<system>(domain_creator);\n    }\n    {\n      INFO(\"3D\");\n      using system = Poisson::FirstOrderSystem<3, Poisson::Geometry::Curved>;\n      const domain::creators::RotatedBricks domain_creator{\n          {{-2., 0., -1.}},\n          {{0., 0.5, 0.}},\n          {{2., 1., 1.}},\n          {{1, 0, 0}},\n          {{{{3, 3}}, {{3, 3}}, {{3, 3}}}},\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Dirichlet),\n          nullptr};\n      test_subdomain_operator<system>(domain_creator);\n    }\n  }\n  {\n    INFO(\"Refined\");\n    {\n      INFO(\"1D\");\n      using system = Poisson::FirstOrderSystem<1, Poisson::Geometry::Curved>;\n      //  |-B0-|--B1---|\n      //  [oooo|ooo|ooo]-> xi\n      //  ^    ^   ^   ^\n      // -2    0   1   2\n      const auto dirichlet_bc = make_boundary_condition<system>(\n          elliptic::BoundaryConditionType::Dirichlet);\n      const domain::creators::AlignedLattice<1> domain_creator{\n          {{{-2., 0., 2.}}},\n          {{0}},\n          {{3}},\n          {{{{1}}, {{2}}, {{1}}}},  // Refine once in block 1\n          {{{{0}}, {{1}}, {{4}}}},  // Increase num points in block 0\n          {},\n          {{{{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}}}}};\n      test_subdomain_operator<system>(domain_creator);\n    }\n    {\n      INFO(\"2D\");\n      using system = Poisson::FirstOrderSystem<2, Poisson::Geometry::Curved>;\n      //   -2    0   2\n      // -2 +----+---+> xi\n      //    |oooo|ooo|\n      //    |    |ooo|\n      //    |    |ooo|\n      // -1 |oooo+---+\n      //    |    |ooo|\n      //    |    |ooo|\n      //    |oooo|ooo|\n      //  0 +----+---+\n      //    |ooo |ooo|\n      //    |ooo |ooo|\n      //    |ooo |ooo|\n      //  2 +----+---+\n      //    v eta\n      const auto dirichlet_bc = make_boundary_condition<system>(\n          elliptic::BoundaryConditionType::Dirichlet);\n      const domain::creators::AlignedLattice<2> domain_creator{\n          // Start with 4 unrefined blocks\n          {{{-2., 0., 2.}, {-2., 0., 2.}}},\n          {{0, 0}},\n          {{3, 3}},\n          // Refine once in eta in upper-right block in sketch above\n          {{{{1, 0}}, {{2, 1}}, {{0, 1}}}},\n          // Increase num points in xi in upper-left block in sketch above\n          {{{{0, 0}}, {{1, 1}}, {{4, 3}}}},\n          {},\n          {{{{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n            {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}}}}};\n      test_subdomain_operator<system>(domain_creator);\n    }\n    {\n      INFO(\"3D\");\n      using system = Poisson::FirstOrderSystem<3, Poisson::Geometry::Curved>;\n      const auto dirichlet_bc = make_boundary_condition<system>(\n          elliptic::BoundaryConditionType::Dirichlet);\n      const domain::creators::AlignedLattice<3> domain_creator{\n          {{{-2., 0., 2.}, {-2., 0., 2.}, {-2., 0., 2.}}},\n          {{0, 0, 0}},\n          {{3, 3, 3}},\n          {{{{1, 0, 0}}, {{2, 1, 1}}, {{0, 1, 1}}}},\n          {{{{0, 0, 0}}, {{1, 1, 1}}, {{4, 3, 2}}}},\n          {},\n          {{{{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n            {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n            {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}}}}};\n      test_subdomain_operator<system>(domain_creator);\n    }\n  }\n  {\n    INFO(\"Curved mesh\");\n    {\n      INFO(\"2D\");\n      using system = Poisson::FirstOrderSystem<2, Poisson::Geometry::Curved>;\n      const domain::creators::Disk domain_creator{\n          0.5,\n          2.,\n          1,\n          {{3, 4}},\n          false,\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Dirichlet)};\n      test_subdomain_operator<system>(domain_creator);\n    }\n    {\n      INFO(\"3D\");\n      using system = Poisson::FirstOrderSystem<3, Poisson::Geometry::Curved>;\n      const domain::creators::Cylinder domain_creator{\n          0.5,\n          2.,\n          0.,\n          2.,\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Dirichlet),\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Dirichlet),\n          make_boundary_condition<system>(\n              elliptic::BoundaryConditionType::Dirichlet),\n          size_t{0},\n          std::array<size_t, 3>{{3, 4, 2}},\n          false};\n      for (const auto& [use_massive_dg_operator, quadrature] :\n           cartesian_product(make_array(false, true),\n                             make_array(Spectral::Quadrature::GaussLobatto,\n                                        Spectral::Quadrature::Gauss))) {\n        test_subdomain_operator<system>(domain_creator, use_massive_dg_operator,\n                                        quadrature);\n      }\n    }\n  }\n  {\n    INFO(\"System with fluxes args\");\n    using system = Elasticity::FirstOrderSystem<3>;\n    const auto dirichlet_bc = make_boundary_condition<system>(\n        elliptic::BoundaryConditionType::Dirichlet);\n    const domain::creators::Brick domain_creator{\n        {{-2., 0., -1.}},\n        {{2., 1., 1.}},\n        {{1, 1, 1}},\n        {{3, 3, 3}},\n        {{{{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n          {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n          {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}}}}};\n    test_subdomain_operator<system,\n                            tmpl::list<InitializeConstitutiveRelation<3>>>(\n        domain_creator);\n  }\n  {\n    INFO(\"Complex-valued system\");\n    using system = ScalarSelfForce::FirstOrderSystem;\n    const auto dirichlet_bc = make_boundary_condition<system>(\n        elliptic::BoundaryConditionType::Dirichlet);\n    const domain::creators::Rectangle domain_creator{\n        {{-50., -1.}},\n        {{350., 1.}},\n        {{1, 1}},\n        {{3, 3}},\n        {{{{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}},\n          {{dirichlet_bc->get_clone(), dirichlet_bc->get_clone()}}}}};\n    test_subdomain_operator<system>(domain_creator);\n    for (const auto& [quadrature, max_overlap] : cartesian_product(\n             make_array(Spectral::Quadrature::GaussLobatto,\n                        Spectral::Quadrature::Gauss),\n             std::array<std::optional<size_t>, 2>{{3, std::nullopt}})) {\n      test_subdomain_operator<system>(domain_creator, true, quadrature, false,\n                                      std::nullopt);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/Test_DgOperator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Elliptic/Actions/InitializeAnalyticSolution.hpp\"\n#include \"Elliptic/Actions/InitializeFixedSources.hpp\"\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/ApplyOperator.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/SetData.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/EvaluateRefinementCriteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Random.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Lorentzian.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/ProductOfSinusoids.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\n\ntemplate <typename Tag>\nstruct DgOperatorAppliedTo : db::SimpleTag, db::PrefixTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\ntemplate <typename Tag>\nstruct Var : db::SimpleTag, db::PrefixTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n// Label to indicate the start of the apply-operator actions\nstruct ApplyOperatorStart {};\n\ntemplate <typename System, bool Linearized, typename Metavariables>\nstruct ElementArray {\n  static constexpr size_t Dim = System::volume_dim;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<Dim>>;\n\n  // Wrap fields in a prefix to make sure this works\n  using primal_vars = db::wrap_tags_in<Var, typename System::primal_fields>;\n  using primal_fluxes_vars =\n      db::wrap_tags_in<Var, typename System::primal_fluxes>;\n  using vars_tag = ::Tags::Variables<primal_vars>;\n  using primal_fluxes_vars_tag = ::Tags::Variables<primal_fluxes_vars>;\n  using operator_applied_to_vars_tag =\n      ::Tags::Variables<db::wrap_tags_in<DgOperatorAppliedTo, primal_vars>>;\n  using dg_operator =\n      ::elliptic::dg::Actions::DgOperator<System, Linearized, vars_tag,\n                                          primal_fluxes_vars_tag,\n                                          operator_applied_to_vars_tag>;\n  // Don't wrap the fixed sources in the `Var` prefix because typically we want\n  // to impose inhomogeneous boundary conditions on the un-prefixed vars, i.e.\n  // not necessarily the vars we apply the linearized operator to\n  using fixed_sources_tag = ::Tags::Variables<\n      db::wrap_tags_in<::Tags::FixedSource, typename System::primal_fields>>;\n  using analytic_solution_tag =\n      ::Tags::AnalyticSolution<typename metavariables::analytic_solution>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n                         tmpl::list<domain::Tags::InitialRefinementLevels<Dim>,\n                                    domain::Tags::InitialExtents<Dim>>>,\n                     ::elliptic::dg::Actions::InitializeDomain<Dim>,\n                     ::elliptic::Actions::InitializeOptionalAnalyticSolution<\n                         Dim, analytic_solution_tag,\n                         tmpl::append<typename System::primal_fields,\n                                      typename System::primal_fluxes>,\n                         typename metavariables::analytic_solution>,\n                     ::elliptic::Actions::InitializeFixedSources<\n                         System, analytic_solution_tag>,\n                     ::elliptic::dg::Actions::initialize_operator<System>,\n                     Initialization::Actions::InitializeItems<\n                         ::amr::Initialization::Initialize<Dim, metavariables>>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<::Actions::MutateApply<\n                         ::elliptic::dg::Actions::\n                             ImposeInhomogeneousBoundaryConditionsOnSource<\n                                 System, fixed_sources_tag>>,\n                     ::Actions::Label<ApplyOperatorStart>,\n                     typename dg_operator::apply_actions,\n                     Parallel::Actions::TerminatePhase>>>;\n};\n\ntemplate <typename Metavariables>\nstruct AmrComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using component_being_mocked = ::amr::Component<Metavariables>;\n  using const_global_cache_tags =\n      tmpl::list<::amr::Criteria::Tags::Criteria,\n                 amr::Tags::AmrBlocks<Metavariables::volume_dim>,\n                 ::amr::Tags::Policies,\n                 logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<>>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct ProjectMetavars : tt::ConformsTo<::amr::protocols::Projector> {\n  using return_tags = tmpl::list<\n      // Work around a segfault because this tag isn't handled\n      // correctly by the testing framework\n      Parallel::Tags::GlobalCache<Metavariables>>;\n  using argument_tags = tmpl::list<>;\n  template <typename... AmrData>\n  static void apply(\n      const gsl::not_null<Parallel::GlobalCache<Metavariables>**> /*cache*/,\n      const AmrData&... /*amr_data*/) {}\n};\n\ntemplate <typename System, bool Linearized, typename AnalyticSolution>\nstruct Metavariables {\n  static constexpr size_t volume_dim = System::volume_dim;\n  using analytic_solution = AnalyticSolution;\n  using element_array = ElementArray<System, Linearized, Metavariables>;\n  using amr_component = AmrComponent<Metavariables>;\n  using component_list = tmpl::list<element_array, amr_component>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::AnalyticSolution<AnalyticSolution>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            elliptic::BoundaryConditions::BoundaryCondition<System::volume_dim>,\n            tmpl::list<elliptic::BoundaryConditions::AnalyticSolution<System>>>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution,\n                   tmpl::list<AnalyticSolution>>,\n        tmpl::pair<AnalyticSolution, tmpl::list<AnalyticSolution>>,\n        tmpl::pair<::amr::Criterion, tmpl::list<::amr::Criteria::Random<\n                                         amr::Criteria::Type::p>>>>;\n  };\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = ElementArray<System, Linearized, Metavariables>;\n    using projectors = tmpl::flatten<tmpl::list<\n        ProjectMetavars<Metavariables>,\n        ::amr::projectors::DefaultInitialize<\n            tmpl::list<domain::Tags::InitialExtents<volume_dim>,\n                       domain::Tags::InitialRefinementLevels<volume_dim>,\n                       typename element_array::vars_tag>>,\n        elliptic::dg::ProjectGeometry<volume_dim>,\n        elliptic::Actions::InitializeFixedSources<\n            System, typename element_array::analytic_solution_tag>,\n        ::elliptic::Actions::InitializeOptionalAnalyticSolution<\n            volume_dim, typename element_array::analytic_solution_tag,\n            tmpl::append<typename System::primal_fields,\n                         typename System::primal_fluxes>,\n            analytic_solution>,\n        elliptic::dg::Actions::amr_projectors<\n            System, typename element_array::analytic_solution_tag>,\n        typename element_array::dg_operator::amr_projectors>>;\n    static constexpr bool keep_coarse_grids = false;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\ntemplate <size_t Dim>\nstruct ModifiedPoissonSystem\n    : Poisson::FirstOrderSystem<Dim, Poisson::Geometry::FlatCartesian> {\n  struct modify_boundary_data {\n    using argument_tags = tmpl::list<\n        domain::Tags::Element<Dim>,\n        ::Tags::Mortars<domain::Tags::Coordinates<Dim, Frame::Inertial>, Dim>>;\n    static void apply(\n        const gsl::not_null<Scalar<DataVector>*> field,\n        const gsl::not_null<Scalar<DataVector>*> normal_dot_flux,\n        const DirectionalId<Dim>& mortar_id, const Element<Dim>& element,\n        const DirectionalIdMap<Dim, tnsr::I<DataVector, Dim, Frame::Inertial>>&\n            all_mortar_coords) {\n      // Assuming that in the region x > 0.5 (block 1) we decompose the field as\n      // u = u_R + u_P with u_P = 2x + 3y\n      const auto& element_id = element.id();\n      const bool element_is_modified = element_id.block_id() == 1;\n      const bool neighbor_is_modified = mortar_id.id().block_id() == 1;\n      if (element_is_modified == neighbor_is_modified) {\n        return;\n      }\n      const auto& x = all_mortar_coords.at(mortar_id);\n      const DataVector singular_field = 2. * get<0>(x) + 3. * get<1>(x);\n      const auto& direction = mortar_id.direction();\n      // Assuming rectilinear grid to simplify the normal vector\n      const double singular_normal_dot_flux =\n          (direction.dimension() == 0 ? 2. : 3.) * direction.sign();\n      // In modified elements, the field is u_R = u - u_P\n      const double sign = element_is_modified ? -1. : 1.;\n      get(*field) += sign * singular_field;\n      // Minus sign because we are modifying the _received_ fluxes, which were\n      // computed using the neighbor's face normal\n      get(*normal_dot_flux) -= sign * singular_normal_dot_flux;\n    }\n  };\n};\n\ntemplate <size_t Dim>\nstruct ModifiedPoissonSolution : Poisson::Solutions::ProductOfSinusoids<Dim> {\n  using Poisson::Solutions::ProductOfSinusoids<Dim>::ProductOfSinusoids;\n  std::unique_ptr<elliptic::analytic_data::AnalyticSolution> get_clone()\n      const override {\n    return std::make_unique<ModifiedPoissonSolution>(*this);\n  }\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(ModifiedPoissonSolution);  // NOLINT\n\n  template <typename... RequestedTags>\n  tuples::TaggedTuple<RequestedTags...> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      tmpl::list<RequestedTags...> /*meta*/) const {\n    auto vars = Poisson::Solutions::ProductOfSinusoids<Dim>::variables(\n        x, tmpl::list<RequestedTags...>{});\n    // Using coordinates to determine in which block we are. This only works for\n    // Gauss points, because GaussLobatto points are duplicate on boundaries.\n    const bool element_is_modified = get<0>(x)[0] > 0.5;\n    if (not element_is_modified) {\n      return vars;\n    }\n    const DataVector singular_field = 2. * get<0>(x) + 3. * get<1>(x);\n    if constexpr (tmpl::list_contains_v<tmpl::list<RequestedTags...>,\n                                        Poisson::Tags::Field<DataVector>>) {\n      get(get<Poisson::Tags::Field<DataVector>>(vars)) -= singular_field;\n    }\n    using deriv_field = ::Tags::deriv<Poisson::Tags::Field<DataVector>,\n                                      tmpl::size_t<Dim>, Frame::Inertial>;\n    if constexpr (tmpl::list_contains_v<tmpl::list<RequestedTags...>,\n                                        deriv_field>) {\n      get<0>(get<deriv_field>(vars)) -= 2.;\n      get<1>(get<deriv_field>(vars)) -= 3.;\n    }\n    using flux_tag = ::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<Dim>, Frame::Inertial>;\n    if constexpr (tmpl::list_contains_v<tmpl::list<RequestedTags...>,\n                                        flux_tag>) {\n      get<0>(get<flux_tag>(vars)) -= 2.;\n      get<1>(get<flux_tag>(vars)) -= 3.;\n    }\n    return vars;\n  }\n};\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID ModifiedPoissonSolution<Dim>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <\n    typename System, bool Linearized, typename AnalyticSolution,\n    size_t Dim = System::volume_dim,\n    typename Metavars = Metavariables<System, Linearized, AnalyticSolution>,\n    typename ElementArray = typename Metavars::element_array>\nvoid test_dg_operator(\n    const DomainCreator<Dim>& domain_creator, const double penalty_parameter,\n    const bool use_massive_dg_operator, const Spectral::Quadrature quadrature,\n    const ::dg::Formulation dg_formulation,\n    const AnalyticSolution& analytic_solution,\n    const Approx& analytic_solution_aux_approx,\n    const Approx& analytic_solution_operator_approx,\n    const std::vector<std::tuple<\n        std::unordered_map<ElementId<Dim>,\n                           typename ElementArray::vars_tag::type>,\n        std::unordered_map<ElementId<Dim>,\n                           typename ElementArray::primal_fluxes_vars_tag::type>,\n        std::unordered_map<\n            ElementId<Dim>,\n            typename ElementArray::operator_applied_to_vars_tag::type>>>&\n        tests_data,\n    const bool test_amr = false) {\n  CAPTURE(penalty_parameter);\n  CAPTURE(use_massive_dg_operator);\n  CAPTURE(quadrature);\n  CAPTURE(dg_formulation);\n\n  using element_array = ElementArray;\n  using vars_tag = typename element_array::vars_tag;\n  using primal_fluxes_vars_tag = typename element_array::primal_fluxes_vars_tag;\n  using operator_applied_to_vars_tag =\n      typename element_array::operator_applied_to_vars_tag;\n  using fixed_sources_tag = typename element_array::fixed_sources_tag;\n  using Vars = typename vars_tag::type;\n  using PrimalFluxesVars = typename primal_fluxes_vars_tag::type;\n  using OperatorAppliedToVars = typename operator_applied_to_vars_tag::type;\n\n  register_factory_classes_with_charm<Metavars>();\n\n  // Get a list of all elements in the domain\n  auto domain = domain_creator.create_domain();\n  auto boundary_conditions = domain_creator.external_boundary_conditions();\n  const auto initial_ref_levs = domain_creator.initial_refinement_levels();\n  const auto initial_extents = domain_creator.initial_extents();\n  std::unordered_set<ElementId<Dim>> all_element_ids{};\n  for (const auto& block : domain.blocks()) {\n    auto block_element_ids =\n        initial_element_ids(block.id(), initial_ref_levs[block.id()]);\n    for (auto& element_id : block_element_ids) {\n      all_element_ids.insert(std::move(element_id));\n    }\n  }\n\n  // Configure AMR criteria so we _increase_ (never decrease) resolution\n  std::vector<std::unique_ptr<::amr::Criterion>> amr_criteria{};\n  amr_criteria.push_back(\n      std::make_unique<::amr::Criteria::Random<amr::Criteria::Type::p>>(\n          std::unordered_map<::amr::Flag, size_t>{\n              // h-refinement is not supported yet in the\n              // action testing framework\n              {::amr::Flag::IncreaseResolution, 1},\n              {::amr::Flag::DoNothing, 1}}));\n\n  ActionTesting::MockRuntimeSystem<Metavars> runner{tuples::TaggedTuple<\n      domain::Tags::Domain<Dim>, domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::ExternalBoundaryConditions<Dim>,\n      ::elliptic::dg::Tags::PenaltyParameter, ::elliptic::dg::Tags::Massive,\n      ::elliptic::dg::Tags::Quadrature, ::elliptic::dg::Tags::Formulation,\n      ::Tags::AnalyticSolution<AnalyticSolution>,\n      ::amr::Criteria::Tags::Criteria, amr::Tags::AmrBlocks<Dim>,\n      ::amr::Tags::Policies,\n      logging::Tags::Verbosity<::amr::OptionTags::AmrGroup>>{\n      std::move(domain), domain_creator.functions_of_time(),\n      std::move(boundary_conditions), penalty_parameter,\n      use_massive_dg_operator, quadrature, dg_formulation, analytic_solution,\n      std::move(amr_criteria), std::nullopt,\n      ::amr::Policies{::amr::Isotropy::Anisotropic, ::amr::Limits{}, true,\n                      true},\n      ::Verbosity::Debug}};\n\n  // DataBox shortcuts\n  const auto get_tag =\n      [&runner](auto tag_v,\n                const ElementId<Dim>& local_element_id) -> const auto& {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              local_element_id);\n  };\n  const auto set_tag = [&runner](auto tag_v, auto value,\n                                 const ElementId<Dim>& local_element_id) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    ActionTesting::simple_action<element_array,\n                                 ::Actions::SetData<tmpl::list<tag>>>(\n        make_not_null(&runner), local_element_id, std::move(value));\n  };\n\n  // Initialize all elements in the domain\n  for (const auto& element_id : all_element_ids) {\n    ActionTesting::emplace_component_and_initialize<element_array>(\n        &runner, element_id, {initial_ref_levs, initial_extents});\n    while (\n        not ActionTesting::get_terminate<element_array>(runner, element_id)) {\n      ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                element_id);\n    }\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  if constexpr (Linearized) {\n    INFO(\n        \"Test imposing inhomogeneous boundary conditions on source, i.e. b -= \"\n        \"A(x=0)\");\n    for (const auto& element_id : all_element_ids) {\n      ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                element_id);\n    }\n    // The analytic-solution test below uses the fixed sources that were\n    // modified here.\n  }\n\n  const auto apply_operator_and_check_result =\n      [&runner, &all_element_ids, &get_tag, &set_tag](\n          const std::unordered_map<ElementId<Dim>, Vars>& all_vars,\n          const std::unordered_map<ElementId<Dim>, PrimalFluxesVars>&\n              all_expected_primal_fluxes_vars,\n          const std::unordered_map<ElementId<Dim>, OperatorAppliedToVars>&\n              all_expected_operator_applied_to_vars,\n          const Approx& custom_aux_approx = approx,\n          const Approx& custom_operator_approx = approx) {\n        // Set variables data on central element and on its neighbors\n        for (const auto& element_id : all_element_ids) {\n          const auto vars = all_vars.find(element_id);\n          if (vars == all_vars.end()) {\n            const size_t num_points =\n                get_tag(domain::Tags::Mesh<Dim>{}, element_id)\n                    .number_of_grid_points();\n            set_tag(vars_tag{}, Vars{num_points, 0.}, element_id);\n          } else {\n            set_tag(vars_tag{}, vars->second, element_id);\n          }\n        }\n\n        // Apply DG operator\n        // 1. Prepare elements and send data\n        for (const auto& element_id : all_element_ids) {\n          runner.template force_next_action_to_be<\n              element_array, ::Actions::Label<ApplyOperatorStart>>(element_id);\n          runner.template mock_distributed_objects<element_array>()\n              .at(element_id)\n              .set_terminate(false);\n          while (not ActionTesting::get_terminate<element_array>(runner,\n                                                                 element_id) and\n                 ActionTesting::next_action_if_ready<element_array>(\n                     make_not_null(&runner), element_id)) {\n          }\n        }\n        // 2. Receive data and apply operator\n        for (const auto& element_id : all_element_ids) {\n          while (not ActionTesting::get_terminate<element_array>(runner,\n                                                                 element_id)) {\n            ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                      element_id);\n          }\n        }\n\n        // Check result\n        {\n          INFO(\"Auxiliary variables\");\n          for (const auto& [element_id, expected_primal_fluxes_vars] :\n               all_expected_primal_fluxes_vars) {\n            CAPTURE(element_id);\n            const auto& inertial_coords = get_tag(\n                domain::Tags::Coordinates<Dim, Frame::Inertial>{}, element_id);\n            CAPTURE(inertial_coords);\n            const auto& vars = get_tag(vars_tag{}, element_id);\n            CAPTURE(vars);\n            const auto& primal_fluxes_vars =\n                get_tag(primal_fluxes_vars_tag{}, element_id);\n            // Working around a bug in GCC 7.3 that fails to handle the\n            // structured bindings here\n            const auto& local_expected_primal_fluxes_vars =\n                expected_primal_fluxes_vars;\n            CHECK_VARIABLES_CUSTOM_APPROX(primal_fluxes_vars,\n                                          local_expected_primal_fluxes_vars,\n                                          custom_aux_approx);\n          }\n        }\n        {\n          INFO(\"Operator applied to variables\");\n          for (const auto& [element_id, expected_operator_applied_to_vars] :\n               all_expected_operator_applied_to_vars) {\n            CAPTURE(element_id);\n            const auto& inertial_coords = get_tag(\n                domain::Tags::Coordinates<Dim, Frame::Inertial>{}, element_id);\n            CAPTURE(inertial_coords);\n            const auto& vars = get_tag(vars_tag{}, element_id);\n            CAPTURE(vars);\n            const auto& operator_applied_to_vars =\n                get_tag(operator_applied_to_vars_tag{}, element_id);\n            // Working around a bug in GCC 7.3 that fails to handle the\n            // structured bindings here\n            const auto& local_expected_operator_applied_to_vars =\n                expected_operator_applied_to_vars;\n            CHECK_VARIABLES_CUSTOM_APPROX(\n                operator_applied_to_vars,\n                local_expected_operator_applied_to_vars,\n                custom_operator_approx);\n          }\n        }\n      };\n  {\n    INFO(\"Test that A(0) = 0\");\n    std::unordered_map<ElementId<Dim>, PrimalFluxesVars>\n        all_zero_primal_fluxes{};\n    std::unordered_map<ElementId<Dim>, OperatorAppliedToVars>\n        all_zero_operator_vars{};\n    for (const auto& element_id : all_element_ids) {\n      const size_t num_points = get_tag(domain::Tags::Mesh<Dim>{}, element_id)\n                                    .number_of_grid_points();\n      all_zero_primal_fluxes[element_id] = PrimalFluxesVars{num_points, 0.};\n      all_zero_operator_vars[element_id] =\n          OperatorAppliedToVars{num_points, 0.};\n    }\n    apply_operator_and_check_result({}, all_zero_primal_fluxes,\n                                    all_zero_operator_vars);\n  }\n  const auto test_analytic_solution =\n      [&analytic_solution, &all_element_ids, &get_tag,\n       &apply_operator_and_check_result, &analytic_solution_aux_approx,\n       &analytic_solution_operator_approx]() {\n        INFO(\"Test A(x) = b with analytic solution\");\n        std::unordered_map<ElementId<Dim>, Vars> analytic_primal_vars{};\n        std::unordered_map<ElementId<Dim>, PrimalFluxesVars>\n            analytic_primal_fluxes{};\n        std::unordered_map<ElementId<Dim>, OperatorAppliedToVars>\n            analytic_fixed_source_with_inhom_bc{};\n        for (const auto& element_id : all_element_ids) {\n          const auto& inertial_coords = get_tag(\n              domain::Tags::Coordinates<Dim, Frame::Inertial>{}, element_id);\n          analytic_primal_vars[element_id] =\n              variables_from_tagged_tuple(analytic_solution.variables(\n                  inertial_coords, typename System::primal_fields{}));\n          analytic_primal_fluxes[element_id] =\n              variables_from_tagged_tuple(analytic_solution.variables(\n                  inertial_coords, typename System::primal_fluxes{}));\n          analytic_fixed_source_with_inhom_bc[element_id] =\n              get_tag(fixed_sources_tag{}, element_id);\n        }\n        apply_operator_and_check_result(\n            analytic_primal_vars, analytic_primal_fluxes,\n            analytic_fixed_source_with_inhom_bc, analytic_solution_aux_approx,\n            analytic_solution_operator_approx);\n      };\n  test_analytic_solution();\n  {\n    INFO(\"Test A(x) = b with custom x and b\");\n    for (const auto& test_data : tests_data) {\n      std::apply(apply_operator_and_check_result, test_data);\n    }\n  }\n\n  // Stop here if we're not testing AMR\n  if (not test_amr) {\n    return;\n  }\n\n  INFO(\"Run AMR!\");\n  const auto invoke_all_simple_actions = [&runner, &all_element_ids]() {\n    bool quiescence = false;\n    while (not quiescence) {\n      quiescence = true;\n      for (const auto& element_id : all_element_ids) {\n        while (not ActionTesting::is_simple_action_queue_empty<element_array>(\n            runner, element_id)) {\n          ActionTesting::invoke_queued_simple_action<element_array>(\n              make_not_null(&runner), element_id);\n          quiescence = false;\n        }\n      }\n    }\n  };\n  using amr_component = typename Metavars::amr_component;\n  ActionTesting::emplace_singleton_component<amr_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0});\n  auto& cache = ActionTesting::cache<amr_component>(runner, 0);\n  Parallel::simple_action<::amr::Actions::EvaluateRefinementCriteria>(\n      Parallel::get_parallel_component<element_array>(cache));\n  invoke_all_simple_actions();\n  Parallel::simple_action<::amr::Actions::AdjustDomain>(\n      Parallel::get_parallel_component<element_array>(cache));\n  invoke_all_simple_actions();\n  // h-refinement is not supported yet in the action testing framework\n  // // Invoke `CreateChild` on AMR component\n  // while (not ActionTesting::is_simple_action_queue_empty<amr_component>(\n  //              runner, 0)) {\n  //   ActionTesting::invoke_queued_simple_action<amr_component>(\n  //       make_not_null(&runner), 0);\n  // }\n  // // Update list of element IDs\n  // all_element_ids.clear();\n  // for (const auto& [element_id, element] :\n  //      runner.template mock_distributed_objects<element_array>()) {\n  //   (void)element;\n  //   all_element_ids.insert(element_id);\n  // }\n  // CAPTURE(all_element_ids);\n  // Go to the start of the testing phase to run the\n  // `ImposeInhomogeneousBoundaryConditionsOnSource` action again\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  if constexpr (Linearized) {\n    for (const auto& element_id : all_element_ids) {\n      ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                                element_id);\n    }\n  }\n\n  // Test analytic solution again after AMR. It should still pass because we\n  // only refined, never coarsened.\n  test_analytic_solution();\n}\n\n}  // namespace\n\n// [[TimeOut, 60]]\nSPECTRE_TEST_CASE(\"Unit.Elliptic.DG.Operator\", \"[Unit][Elliptic]\") {\n  domain::creators::register_derived_with_charm();\n  // This is what the tests below check:\n  //\n  // - The DG operator passes some basic consistency checks, e.g. A(0) = 0.\n  // - It is a numerical approximation of the system equation, i.e. A(x) = b for\n  //   an analytic solution x and corresponding fixed source b.\n  // - It produces an expected output from a random (but hard-coded) set of\n  //   variables. This is an important regression test, i.e. it ensures the\n  //   output does not change with optimizations etc.\n  // - It runs on various domain geometries. Currently only a few rectilinear\n  //   domains are tested, but this can be expanded.\n  // - The free functions, actions and initialization actions are compatible.\n  //\n  // Notes:\n  //\n  // - Relative tolerances for analytic-solution tests include a\n  //   problem-dependent scale. The scale is set by the analytic solution and\n  //   has an additional penalty factor C * p^2 / h for the DG operator, where C\n  //   is the penalty parameter, p is the number of grid points and h is the\n  //   size of an element (in the least-resolved dimension).\n  // - Hard-coded numbers in regression tests are always compared with default\n  //   precision (1.e-14 or so).\n  const double penalty_parameter = 1.5;\n  {\n    INFO(\"1D rectilinear\");\n    using system =\n        Poisson::FirstOrderSystem<1, Poisson::Geometry::FlatCartesian>;\n    const Poisson::Solutions::ProductOfSinusoids<1> analytic_solution{{{M_PI}}};\n    {\n      INFO(\"Regression tests\");\n      // Domain decomposition:\n      // [ | | | ]-> xi\n      const domain::creators::Interval domain_creator{\n          {{-0.5}},\n          {{1.5}},\n          {{2}},\n          {{4}},\n          {{{{std::make_unique<\n                  elliptic::BoundaryConditions::AnalyticSolution<system>>(\n                  analytic_solution.get_clone(),\n                  elliptic::BoundaryConditionType::Dirichlet),\n              std::make_unique<\n                  elliptic::BoundaryConditions::AnalyticSolution<system>>(\n                  analytic_solution.get_clone(),\n                  elliptic::BoundaryConditionType::Neumann)}}}}};\n      const ElementId<1> left_id{0, {{{2, 0}}}};\n      const ElementId<1> midleft_id{0, {{{2, 1}}}};\n      const ElementId<1> midright_id{0, {{{2, 2}}}};\n      const ElementId<1> right_id{0, {{{2, 3}}}};\n      using Vars = Variables<tmpl::list<Var<Poisson::Tags::Field<DataVector>>>>;\n      using PrimalFluxes = Variables<\n          tmpl::list<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                      tmpl::size_t<1>, Frame::Inertial>>>>;\n      using OperatorVars = Variables<tmpl::list<\n          DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>>;\n      Vars vars_rnd_left{4};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_left)) =\n          DataVector{0.6964691855978616, 0.28613933495037946,\n                     0.2268514535642031, 0.5513147690828912};\n      Vars vars_rnd_midleft{4};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_midleft)) =\n          DataVector{0.7194689697855631, 0.42310646012446096,\n                     0.9807641983846155, 0.6848297385848633};\n      Vars vars_rnd_midright{4};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_midright)) =\n          DataVector{0.48093190148436094, 0.3921175181941505,\n                     0.3431780161508694, 0.7290497073840416};\n      Vars vars_rnd_right{4};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_right)) =\n          DataVector{0.4385722446796244, 0.05967789660956835,\n                     0.3980442553304314, 0.7379954057320357};\n      PrimalFluxes expected_primal_fluxes_rnd_left{4};\n      get<0>(get<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<1>, Frame::Inertial>>>(\n          expected_primal_fluxes_rnd_left)) =\n          DataVector{-4.027188081328469, -1.9207736184862605,\n                     1.3653213194134493, 3.3207435803332324};\n      PrimalFluxes expected_primal_fluxes_rnd_right{4};\n      get<0>(get<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<1>, Frame::Inertial>>>(\n          expected_primal_fluxes_rnd_right)) =\n          DataVector{-5.281316261986065, -0.5513540594510129,\n                     2.6634207403536934, 1.9071387227305365};\n      OperatorVars expected_operator_vars_rnd_left{4};\n      get(get<DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>(\n          expected_operator_vars_rnd_left)) =\n          DataVector{1384.5953530154895, 41.55242721423977, -41.54933376905333,\n                     -32.40787768105913};\n      OperatorVars expected_operator_vars_rnd_right{4};\n      get(get<DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>(\n          expected_operator_vars_rnd_right)) =\n          DataVector{-215.34463496424186, -37.924582769790135,\n                     2.1993037260176997, 51.85418137198471};\n      const std::vector<\n          std::tuple<std::unordered_map<ElementId<1>, Vars>,\n                     std::unordered_map<ElementId<1>, PrimalFluxes>,\n                     std::unordered_map<ElementId<1>, OperatorVars>>>\n          regression_test_data{\n              {{{left_id, std::move(vars_rnd_left)},\n                {midleft_id, std::move(vars_rnd_midleft)},\n                {midright_id, std::move(vars_rnd_midright)},\n                {right_id, std::move(vars_rnd_right)}},\n               {{left_id, std::move(expected_primal_fluxes_rnd_left)},\n                {right_id, std::move(expected_primal_fluxes_rnd_right)}},\n               {{left_id, std::move(expected_operator_vars_rnd_left)},\n                {right_id, std::move(expected_operator_vars_rnd_right)}}}};\n      // Large tolerances for the comparison to the analytic solution because\n      // this regression test runs at very low resolution. Below is another\n      // analytic-solution test at higher resolution. The hard-coded numbers\n      // are compared at higher (default) precision.\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(3.e-2).scale(M_PI);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(3.e-2).scale(M_PI * penalty_parameter *\n                                                square(4) / 0.5);\n      // Numbers should be identical for the different formulations on affine\n      // coordinate maps\n      for (const auto dg_formulation :\n           make_array(::dg::Formulation::StrongInertial,\n                      ::dg::Formulation::StrongLogical,\n                      ::dg::Formulation::WeakInertial)) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, false,\n            Spectral::Quadrature::GaussLobatto, dg_formulation,\n            analytic_solution, analytic_solution_aux_approx,\n            analytic_solution_operator_approx, regression_test_data);\n      }\n    }\n    {\n      INFO(\"Higher-resolution analytic-solution tests\");\n      const domain::creators::Interval domain_creator{\n          {{-0.5}},\n          {{1.5}},\n          {{1}},\n          {{12}},\n          {{{{std::make_unique<\n                  elliptic::BoundaryConditions::AnalyticSolution<system>>(\n                  analytic_solution.get_clone(),\n                  elliptic::BoundaryConditionType::Dirichlet),\n              std::make_unique<\n                  elliptic::BoundaryConditions::AnalyticSolution<system>>(\n                  analytic_solution.get_clone(),\n                  elliptic::BoundaryConditionType::Neumann)}}}}};\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(1.e-8).scale(M_PI);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(1.e-8).scale(M_PI * penalty_parameter *\n                                                square(12));\n      for (const auto& [use_massive_dg_operator, quadrature] :\n           cartesian_product(make_array(true, false),\n                             make_array(Spectral::Quadrature::GaussLobatto,\n                                        Spectral::Quadrature::Gauss))) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, use_massive_dg_operator,\n            quadrature, ::dg::Formulation::StrongInertial, analytic_solution,\n            analytic_solution_aux_approx, analytic_solution_operator_approx, {},\n            true);\n      }\n    }\n  }\n  {\n    INFO(\"1D complex\");\n    using system =\n        Poisson::FirstOrderSystem<1, Poisson::Geometry::FlatCartesian,\n                                  ComplexDataVector>;\n    const Poisson::Solutions::ProductOfSinusoids<1, ComplexDataVector>\n        analytic_solution{{{M_PI}}};\n    const domain::creators::Interval domain_creator{\n        {{-0.5}},\n        {{1.5}},\n        {{1}},\n        {{12}},\n        {{{{std::make_unique<\n                elliptic::BoundaryConditions::AnalyticSolution<system>>(\n                analytic_solution.get_clone(),\n                elliptic::BoundaryConditionType::Dirichlet),\n            std::make_unique<\n                elliptic::BoundaryConditions::AnalyticSolution<system>>(\n                analytic_solution.get_clone(),\n                elliptic::BoundaryConditionType::Neumann)}}}}};\n    Approx analytic_solution_aux_approx =\n        Approx::custom().epsilon(1.e-8).scale(M_PI);\n    Approx analytic_solution_operator_approx =\n        Approx::custom().epsilon(1.e-8).scale(M_PI * penalty_parameter *\n                                              square(12));\n    for (const auto& [use_massive_dg_operator, quadrature] :\n         cartesian_product(make_array(true, false),\n                           make_array(Spectral::Quadrature::GaussLobatto,\n                                      Spectral::Quadrature::Gauss))) {\n      test_dg_operator<system, true>(\n          domain_creator, penalty_parameter, use_massive_dg_operator,\n          quadrature, ::dg::Formulation::StrongInertial, analytic_solution,\n          analytic_solution_aux_approx, analytic_solution_operator_approx, {},\n          true);\n    }\n  }\n  {\n    INFO(\"2D rectilinear\");\n    using system =\n        Poisson::FirstOrderSystem<2, Poisson::Geometry::FlatCartesian>;\n    const Poisson::Solutions::ProductOfSinusoids<2> analytic_solution{\n        {{M_PI, M_PI}}};\n    {\n      INFO(\"Regression tests\");\n      const auto dirichlet_bc =\n          elliptic::BoundaryConditions::AnalyticSolution<system>{\n              analytic_solution.get_clone(),\n              elliptic::BoundaryConditionType::Dirichlet};\n      // Domain decomposition:\n      // ^ eta\n      // +-+-+> xi\n      // | | |\n      // +-+-+\n      // | | |\n      // +-+-+\n      const domain::creators::Rectangle domain_creator{\n          {{-0.5, 0.}},\n          {{1.5, 1.}},\n          {{1, 1}},\n          {{3, 2}},\n          {{{{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n            {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}}}}};\n      const ElementId<2> northwest_id{0, {{{1, 0}, {1, 1}}}};\n      const ElementId<2> southwest_id{0, {{{1, 0}, {1, 0}}}};\n      const ElementId<2> northeast_id{0, {{{1, 1}, {1, 1}}}};\n      using Vars = Variables<tmpl::list<Var<Poisson::Tags::Field<DataVector>>>>;\n      using PrimalFluxes = Variables<\n          tmpl::list<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                      tmpl::size_t<2>, Frame::Inertial>>>>;\n      using OperatorVars = Variables<tmpl::list<\n          DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>>;\n      Vars vars_rnd_northwest{6};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_northwest)) =\n          DataVector{0.9807641983846155,  0.6848297385848633,\n                     0.48093190148436094, 0.3921175181941505,\n                     0.3431780161508694,  0.7290497073840416};\n      Vars vars_rnd_southwest{6};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_southwest)) =\n          DataVector{0.6964691855978616, 0.28613933495037946,\n                     0.2268514535642031, 0.5513147690828912,\n                     0.7194689697855631, 0.42310646012446096};\n      Vars vars_rnd_northeast{6};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_northeast)) =\n          DataVector{0.5315513738418384, 0.5318275870968661,\n                     0.6344009585513211, 0.8494317940777896,\n                     0.7244553248606352, 0.6110235106775829};\n      PrimalFluxes expected_primal_fluxes_rnd{6};\n      get<0>(get<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<2>, Frame::Inertial>>>(\n          expected_primal_fluxes_rnd)) = DataVector{\n          -0.683905542298754,  -0.49983229690025466, -0.3157590515017552,\n          -0.5326901973630155, 0.3369321891898911,   1.2065545757427976};\n      get<1>(get<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<2>, Frame::Inertial>>>(\n          expected_primal_fluxes_rnd)) = DataVector{\n          -1.1772933603809301, -0.6833034448679879, 0.4962356117993614,\n          -1.1772933603809301, -0.6833034448679879, 0.4962356117993614};\n      OperatorVars expected_operator_vars_rnd{6};\n      get(get<DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>(\n          expected_operator_vars_rnd)) =\n          DataVector{164.82044058319110, 9.68580366789113,  -2.370110768729976,\n                     91.310963425225239, 30.31342257245155, 56.03237239864831};\n      const std::vector<\n          std::tuple<std::unordered_map<ElementId<2>, Vars>,\n                     std::unordered_map<ElementId<2>, PrimalFluxes>,\n                     std::unordered_map<ElementId<2>, OperatorVars>>>\n          regression_test_data{\n              {{{northwest_id, std::move(vars_rnd_northwest)},\n                {southwest_id, std::move(vars_rnd_southwest)},\n                {northeast_id, std::move(vars_rnd_northeast)}},\n               {{northwest_id, std::move(expected_primal_fluxes_rnd)}},\n               {{northwest_id, std::move(expected_operator_vars_rnd)}}}};\n      // Large tolerances for the comparison to the analytic solution because\n      // this regression test runs at very low resolution. Below is another\n      // analytic-solution test at higher resolution. The hard-coded numbers\n      // are compared at higher (default) precision.\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(7.e-1).scale(M_PI);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(7.e-1).scale(M_PI * penalty_parameter *\n                                                square(3));\n      // Numbers should be identical for the different formulations on affine\n      // coordinate maps\n      for (const auto dg_formulation :\n           make_array(::dg::Formulation::StrongInertial,\n                      ::dg::Formulation::StrongLogical,\n                      ::dg::Formulation::WeakInertial)) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, false,\n            Spectral::Quadrature::GaussLobatto, dg_formulation,\n            analytic_solution, analytic_solution_aux_approx,\n            analytic_solution_operator_approx, regression_test_data);\n      }\n    }\n    {\n      INFO(\"Higher-resolution analytic-solution tests\");\n      const auto dirichlet_bc =\n          elliptic::BoundaryConditions::AnalyticSolution<system>{\n              analytic_solution.get_clone(),\n              elliptic::BoundaryConditionType::Dirichlet};\n      const domain::creators::Rectangle domain_creator{\n          {{-0.5, 0.}},\n          {{1.5, 1.}},\n          {{1, 1}},\n          {{12, 12}},\n          {{{{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n            {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}}}}};\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(1.e-8).scale(M_PI);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(1.e-8).scale(M_PI * penalty_parameter *\n                                                square(12));\n      for (const auto& [use_massive_dg_operator, quadrature] :\n           cartesian_product(make_array(true, false),\n                             make_array(Spectral::Quadrature::GaussLobatto,\n                                        Spectral::Quadrature::Gauss))) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, use_massive_dg_operator,\n            quadrature, ::dg::Formulation::StrongInertial, analytic_solution,\n            analytic_solution_aux_approx, analytic_solution_operator_approx, {},\n            true);\n      }\n    }\n  }\n  {\n    INFO(\"3D rectilinear\");\n    using system =\n        Poisson::FirstOrderSystem<3, Poisson::Geometry::FlatCartesian>;\n    const Poisson::Solutions::ProductOfSinusoids<3> analytic_solution{\n        {{M_PI, M_PI, M_PI}}};\n    {\n      INFO(\"Regression tests\");\n      const auto dirichlet_bc =\n          elliptic::BoundaryConditions::AnalyticSolution<system>{\n              analytic_solution.get_clone(),\n              elliptic::BoundaryConditionType::Dirichlet};\n      const domain::creators::Brick domain_creator{\n          {{-0.5, 0., -1.}},\n          {{1.5, 1., 3.}},\n          {{1, 1, 1}},\n          {{2, 3, 4}},\n          {{{{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n            {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n            {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}}}}};\n      const ElementId<3> self_id{0, {{{1, 0}, {1, 0}, {1, 0}}}};\n      const ElementId<3> neighbor_id_xi{0, {{{1, 1}, {1, 0}, {1, 0}}}};\n      const ElementId<3> neighbor_id_eta{0, {{{1, 0}, {1, 1}, {1, 0}}}};\n      const ElementId<3> neighbor_id_zeta{0, {{{1, 0}, {1, 0}, {1, 1}}}};\n      using Vars = Variables<tmpl::list<Var<Poisson::Tags::Field<DataVector>>>>;\n      using PrimalFluxes = Variables<\n          tmpl::list<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                      tmpl::size_t<3>, Frame::Inertial>>>>;\n      using OperatorVars = Variables<tmpl::list<\n          DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>>;\n      Vars vars_rnd_self{24};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_self)) =\n          DataVector{\n              0.6964691855978616, 0.28613933495037946, 0.2268514535642031,\n              0.5513147690828912, 0.7194689697855631,  0.42310646012446096,\n              0.9807641983846155, 0.6848297385848633,  0.48093190148436094,\n              0.3921175181941505, 0.3431780161508694,  0.7290497073840416,\n              0.4385722446796244, 0.05967789660956835, 0.3980442553304314,\n              0.7379954057320357, 0.18249173045349998, 0.17545175614749253,\n              0.5315513738418384, 0.5318275870968661,  0.6344009585513211,\n              0.8494317940777896, 0.7244553248606352,  0.6110235106775829};\n      Vars vars_rnd_neighbor_xi{24};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_neighbor_xi)) =\n          DataVector{\n              0.15112745234808023, 0.39887629272615654,  0.24085589772362448,\n              0.34345601404832493, 0.5131281541990022,   0.6666245501640716,\n              0.10590848505681383, 0.13089495066408074,  0.32198060646830806,\n              0.6615643366662437,  0.8465062252707221,   0.5532573447967134,\n              0.8544524875245048,  0.3848378112757611,   0.31678789711837974,\n              0.3542646755916785,  0.17108182920509907,  0.8291126345018904,\n              0.3386708459143266,  0.5523700752940731,   0.578551468108833,\n              0.5215330593973323,  0.002688064574320692, 0.98834541928282};\n      Vars vars_rnd_neighbor_eta{24};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_neighbor_eta)) =\n          DataVector{\n              0.5194851192598093, 0.6128945257629677,  0.12062866599032374,\n              0.8263408005068332, 0.6030601284109274,  0.5450680064664649,\n              0.3427638337743084, 0.3041207890271841,  0.4170222110247016,\n              0.6813007657927966, 0.8754568417951749,  0.5104223374780111,\n              0.6693137829622723, 0.5859365525622129,  0.6249035020955999,\n              0.6746890509878248, 0.8423424376202573,  0.08319498833243877,\n              0.7636828414433382, 0.243666374536874,   0.19422296057877086,\n              0.5724569574914731, 0.09571251661238711, 0.8853268262751396};\n      Vars vars_rnd_neighbor_zeta{24};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_neighbor_zeta)) =\n          DataVector{\n              0.7224433825702216,  0.3229589138531782,  0.3617886556223141,\n              0.22826323087895561, 0.29371404638882936, 0.6309761238544878,\n              0.09210493994507518, 0.43370117267952824, 0.4308627633296438,\n              0.4936850976503062,  0.425830290295828,   0.3122612229724653,\n              0.4263513069628082,  0.8933891631171348,  0.9441600182038796,\n              0.5018366758843366,  0.6239529517921112,  0.11561839507929572,\n              0.3172854818203209,  0.4148262119536318,  0.8663091578833659,\n              0.2504553653965067,  0.48303426426270435, 0.985559785610705};\n      OperatorVars expected_operator_vars_rnd{24};\n      get(get<DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>(\n          expected_operator_vars_rnd)) = DataVector{\n          490.4639148694344,  218.0462676038626,   40.25819450876480,\n          83.251427119123945, 176.2577516255026,   -15.624072102187602,\n          586.8311752841304,  391.1570577346553,   30.26559434437542,\n          13.73062841146597,  -15.356914932182377, 100.25724641399877,\n          266.84155600862254, 48.507637035283153,  15.93132587805462,\n          23.036282532293448, -149.02371403160321, -142.12061361346408,\n          314.95846885876898, 329.52673640830761,  46.245499085300622,\n          70.51403818198222,  45.264231133848220,  90.061981328724073};\n      const std::vector<\n          std::tuple<std::unordered_map<ElementId<3>, Vars>,\n                     std::unordered_map<ElementId<3>, PrimalFluxes>,\n                     std::unordered_map<ElementId<3>, OperatorVars>>>\n          regression_test_data{\n              {{{self_id, std::move(vars_rnd_self)},\n                {neighbor_id_xi, std::move(vars_rnd_neighbor_xi)},\n                {neighbor_id_eta, std::move(vars_rnd_neighbor_eta)},\n                {neighbor_id_zeta, std::move(vars_rnd_neighbor_zeta)}},\n               {},\n               {{self_id, std::move(expected_operator_vars_rnd)}}}};\n      // Large tolerances for the comparison to the analytic solution because\n      // this regression test runs at very low resolution. Below is another\n      // analytic-solution test at higher resolution. The hard-coded numbers\n      // are compared at higher (default) precision.\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(8.e-1).scale(M_PI);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(8.e-1).scale(M_PI * penalty_parameter *\n                                                square(4) / 2.);\n      // Numbers should be identical for the different formulations on affine\n      // coordinate maps\n      for (const auto dg_formulation :\n           make_array(::dg::Formulation::StrongInertial,\n                      ::dg::Formulation::StrongLogical,\n                      ::dg::Formulation::WeakInertial)) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, false,\n            Spectral::Quadrature::GaussLobatto, dg_formulation,\n            analytic_solution, analytic_solution_aux_approx,\n            analytic_solution_operator_approx, regression_test_data);\n      }\n    }\n    {\n      INFO(\"Higher-resolution analytic-solution tests\");\n      const auto dirichlet_bc =\n          elliptic::BoundaryConditions::AnalyticSolution<system>{\n              analytic_solution.get_clone(),\n              elliptic::BoundaryConditionType::Dirichlet};\n      const domain::creators::Brick domain_creator{\n          {{-0.5, 0., -1.}},\n          {{1.5, 1., 3.}},\n          {{1, 1, 1}},\n          {{12, 12, 12}},\n          {{{{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n            {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n            {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}}}}};\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(1.e-4).scale(M_PI);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(1.e-4).scale(M_PI * penalty_parameter *\n                                                square(12) / 2.);\n      for (const auto& [use_massive_dg_operator, quadrature] :\n           cartesian_product(make_array(true, false),\n                             make_array(Spectral::Quadrature::GaussLobatto,\n                                        Spectral::Quadrature::Gauss))) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, use_massive_dg_operator,\n            quadrature, ::dg::Formulation::StrongInertial, analytic_solution,\n            analytic_solution_aux_approx, analytic_solution_operator_approx, {},\n            true);\n      }\n    }\n  }\n  {\n    INFO(\"2D with h-nonconforming boundary\");\n    using system =\n        Poisson::FirstOrderSystem<2, Poisson::Geometry::FlatCartesian>;\n    const Poisson::Solutions::ProductOfSinusoids<2> analytic_solution{\n        {{M_PI, M_PI}}};\n    {\n      INFO(\"Regression tests\");\n      // Domain decomposition:\n      // ^ eta\n      // +-+-+> xi\n      // | | |\n      // +-+ |\n      // | | |\n      // +-+-+\n      const auto dirichlet_bc =\n          elliptic::BoundaryConditions::AnalyticSolution<system>{\n              analytic_solution.get_clone(),\n              elliptic::BoundaryConditionType::Dirichlet};\n      const domain::creators::AlignedLattice<2> domain_creator{\n          // Start with 2 unrefined blocks\n          {{{0., 0.25, 0.5}, {0., 0.5}}},\n          {{0, 0}},\n          {{3, 2}},\n          // Refine once in eta in left block in sketch above\n          {{{{0, 0}}, {{1, 1}}, {{0, 1}}}},\n          {},\n          {},\n          {{{{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n            {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}}}}};\n      const ElementId<2> lowerleft_id{0, {{{0, 0}, {1, 0}}}};\n      const ElementId<2> upperleft_id{0, {{{0, 0}, {1, 1}}}};\n      const ElementId<2> right_id{1, {{{0, 0}, {0, 0}}}};\n      using Vars = Variables<tmpl::list<Var<Poisson::Tags::Field<DataVector>>>>;\n      using PrimalFluxes = Variables<\n          tmpl::list<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                      tmpl::size_t<2>, Frame::Inertial>>>>;\n      using OperatorVars = Variables<tmpl::list<\n          DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>>;\n      Vars vars_rnd_lowerleft{6};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_lowerleft)) =\n          DataVector{0.9807641983846155,  0.6848297385848633,\n                     0.48093190148436094, 0.3921175181941505,\n                     0.3431780161508694,  0.7290497073840416};\n      Vars vars_rnd_upperleft{6};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_upperleft)) =\n          DataVector{0.6964691855978616, 0.28613933495037946,\n                     0.2268514535642031, 0.5513147690828912,\n                     0.7194689697855631, 0.42310646012446096};\n      Vars vars_rnd_right{6};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_right)) =\n          DataVector{0.5315513738418384, 0.5318275870968661,\n                     0.6344009585513211, 0.8494317940777896,\n                     0.7244553248606352, 0.6110235106775829};\n      PrimalFluxes expected_primal_fluxes_rnd_lowerleft{6};\n      get<0>(get<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<2>, Frame::Inertial>>>(\n          expected_primal_fluxes_rnd_lowerleft)) = DataVector{\n          -2.73562216919501644, -1.99932918760101819, -1.26303620600701905,\n          -2.13076078945206238, 1.34772875675956438,  4.82621830297119114};\n      get<1>(get<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<2>, Frame::Inertial>>>(\n          expected_primal_fluxes_rnd_lowerleft)) = DataVector{\n          -2.35458672076185982, -1.36660688973597555, 0.99247122359872275,\n          -2.35458672076185982, -1.36660688973597555, 0.99247122359872275};\n      PrimalFluxes expected_primal_fluxes_rnd_right{6};\n      get<0>(get<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<2>, Frame::Inertial>>>(\n          expected_primal_fluxes_rnd_right)) = DataVector{\n          -0.40697892675748815, 0.41139833883793075, 1.22977560443334877,\n          -1.04599037387364335, -0.9536331336008268, -0.86127589332801024};\n      get<1>(get<Var<::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                                  tmpl::size_t<2>, Frame::Inertial>>>(\n          expected_primal_fluxes_rnd_right)) = DataVector{\n          0.6357608404719024, 0.38525547552753836, -0.04675489574747638,\n          0.6357608404719024, 0.38525547552753836, -0.04675489574747638};\n      OperatorVars expected_operator_vars_rnd_lowerleft{6};\n      get(get<DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>(\n          expected_operator_vars_rnd_lowerleft)) = DataVector{\n          13.52385143255982491, 6.82929107083524833, 0.04527695301629676,\n          4.39834760568600736,  0.65041277314590373, 0.78222246779709226};\n      OperatorVars expected_operator_vars_rnd_right{6};\n      get(get<DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>(\n          expected_operator_vars_rnd_right)) = DataVector{\n          1.60618450837350557, 4.71949148249203443, 15.72408761869980509,\n          4.06376669069456398, 5.88578668691303086, 15.11012286654655945};\n      const std::vector<\n          std::tuple<std::unordered_map<ElementId<2>, Vars>,\n                     std::unordered_map<ElementId<2>, PrimalFluxes>,\n                     std::unordered_map<ElementId<2>, OperatorVars>>>\n          regression_test_data{\n              {{{lowerleft_id, std::move(vars_rnd_lowerleft)},\n                {upperleft_id, std::move(vars_rnd_upperleft)},\n                {right_id, std::move(vars_rnd_right)}},\n               {{lowerleft_id, std::move(expected_primal_fluxes_rnd_lowerleft)},\n                {right_id, std::move(expected_primal_fluxes_rnd_right)}},\n               {{lowerleft_id, std::move(expected_operator_vars_rnd_lowerleft)},\n                {right_id, std::move(expected_operator_vars_rnd_right)}}}};\n      // Large tolerances for the comparison to the analytic solution because\n      // this regression test runs at very low resolution. Below is another\n      // analytic-solution test at higher resolution. The hard-coded numbers\n      // are compared at higher (default) precision.\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(7.e-1).scale(M_PI);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(7.e-1).scale(M_PI * penalty_parameter *\n                                                square(3));\n      // Numbers should be identical for the strong formulations\n      for (const auto dg_formulation :\n           make_array(::dg::Formulation::StrongInertial,\n                      ::dg::Formulation::StrongLogical)) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, true,\n            Spectral::Quadrature::GaussLobatto, dg_formulation,\n            analytic_solution, analytic_solution_aux_approx,\n            analytic_solution_operator_approx, regression_test_data);\n      }\n    }\n    {\n      INFO(\"Higher-resolution analytic-solution tests\");\n      const auto dirichlet_bc =\n          elliptic::BoundaryConditions::AnalyticSolution<system>{\n              analytic_solution.get_clone(),\n              elliptic::BoundaryConditionType::Dirichlet};\n      const domain::creators::AlignedLattice<2> domain_creator{\n          {{{0., 0.25, 0.5}, {0., 0.5}}},\n          {{1, 1}},\n          {{12, 12}},\n          {{{{0, 0}}, {{1, 1}}, {{1, 2}}}},\n          {},\n          {},\n          {{{{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n            {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}}}}};\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(1.e-11).scale(M_PI);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(1.e-11).scale(M_PI * penalty_parameter *\n                                                 square(12));\n      for (const auto& [massive, quadrature, dg_formulation] :\n           cartesian_product(make_array(true, false),\n                             make_array(Spectral::Quadrature::GaussLobatto,\n                                        Spectral::Quadrature::Gauss),\n                             make_array(::dg::Formulation::StrongInertial,\n                                        ::dg::Formulation::WeakInertial))) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, massive, quadrature,\n            dg_formulation, analytic_solution, analytic_solution_aux_approx,\n            analytic_solution_operator_approx, {}, true);\n      }\n    }\n  }\n  {\n    INFO(\"2D complex\");\n    using system =\n        Poisson::FirstOrderSystem<2, Poisson::Geometry::FlatCartesian,\n                                  ComplexDataVector>;\n    const Poisson::Solutions::ProductOfSinusoids<2, ComplexDataVector>\n        analytic_solution{{{M_PI, M_PI}}};\n    const auto dirichlet_bc =\n        elliptic::BoundaryConditions::AnalyticSolution<system>{\n            analytic_solution.get_clone(),\n            elliptic::BoundaryConditionType::Dirichlet};\n    const domain::creators::AlignedLattice<2> domain_creator{\n        {{{0., 0.25, 0.5}, {0., 0.5}}},\n        {{1, 1}},\n        {{12, 12}},\n        {{{{0, 0}}, {{1, 1}}, {{1, 2}}}},\n        {},\n        {},\n        {{{{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n          {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}}}}};\n    Approx analytic_solution_aux_approx =\n        Approx::custom().epsilon(1.e-11).scale(M_PI);\n    Approx analytic_solution_operator_approx =\n        Approx::custom().epsilon(1.e-11).scale(M_PI * penalty_parameter *\n                                               square(12));\n    for (const auto& [massive, quadrature, dg_formulation] :\n         cartesian_product(make_array(true, false),\n                           make_array(Spectral::Quadrature::GaussLobatto,\n                                      Spectral::Quadrature::Gauss),\n                           make_array(::dg::Formulation::StrongInertial,\n                                      ::dg::Formulation::WeakInertial))) {\n      test_dg_operator<system, true>(\n          domain_creator, penalty_parameter, massive, quadrature,\n          dg_formulation, analytic_solution, analytic_solution_aux_approx,\n          analytic_solution_operator_approx, {}, true);\n    }\n  }\n  {\n    INFO(\"3D sphere\");\n    using system =\n        Poisson::FirstOrderSystem<3, Poisson::Geometry::FlatCartesian>;\n    const Poisson::Solutions::Lorentzian<3> analytic_solution{0.};\n    {\n      INFO(\"Regression tests\");\n      const domain::creators::Sphere domain_creator{\n          1.,\n          3.,\n          domain::creators::Sphere::InnerCube{0.},\n          0_st,\n          3_st,\n          true,\n          {},\n          {},\n          domain::CoordinateMaps::Distribution::Linear,\n          ShellWedges::All,\n          std::nullopt,\n          std::make_unique<\n              elliptic::BoundaryConditions::AnalyticSolution<system>>(\n              analytic_solution.get_clone(),\n              elliptic::BoundaryConditionType::Dirichlet)};\n      const ElementId<3> center_id{6};\n      const ElementId<3> wedge_id{0};\n      using Vars = Variables<tmpl::list<Var<Poisson::Tags::Field<DataVector>>>>;\n      using OperatorVars = Variables<tmpl::list<\n          DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>>;\n      Vars vars_rnd_center{27};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_center)) =\n          DataVector{\n              0.09649087905837017, 0.8726189695670388,  0.2550997406970058,\n              0.9371182951233519,  0.6633191884394125,  0.7910467053506253,\n              0.8976248825657923,  0.8253597552079079,  0.2614810810491418,\n              0.37703802268313935, 0.18811315681728424, 0.9286960528710335,\n              0.7799863844018025,  0.29158508058745125, 0.3078187493178257,\n              0.11582259489772695, 0.9504180903537383,  0.6290805984444998,\n              0.4156900249686264,  0.6322967693109368,  0.17729543326351738,\n              0.30252861325858027, 0.0700623995180415,  0.7014453661500705,\n              0.09301082210249478, 0.2507901753830596,  0.7325836921946847};\n      Vars vars_rnd_wedge{27};\n      get(get<Var<Poisson::Tags::Field<DataVector>>>(vars_rnd_wedge)) =\n          DataVector{\n              0.33924615234165656, 0.8449024556165292,  0.4541579237278579,\n              0.8205705845059463,  0.35547255892487073, 0.015009371066981636,\n              0.9211805795375337,  0.4002407787315444,  0.0024764405702156767,\n              0.8731417034578796,  0.5033095307249483,  0.3680070578003968,\n              0.176441382310489,   0.6746744554291877,  0.6668359104611603,\n              0.8208960531956108,  0.6243130189945111,  0.19954959587642307,\n              0.7370176442442482,  0.4477495010295536,  0.8282274072733592,\n              0.9199607746918792,  0.9635732092068038,  0.3504092315426307,\n              0.11524312515866864, 0.17417975746930436, 0.5870281973363631};\n      OperatorVars expected_operator_vars_rnd_center{27, 0.};\n      get(get<DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>(\n          expected_operator_vars_rnd_center)) =\n          DataVector{0.13900947487894668719,   2.8012452842448594126,\n                     0.70606635684663743291,   3.0110762308109988439,\n                     2.2653131088752034294,    2.4389044697636719228,\n                     2.8069304514918704818,    2.5816683614488340481,\n                     0.63609994355853216597,   1.0496348030246254179,\n                     0.060338632695005833817,  2.9890473588327037824,\n                     2.5568233674986675652,    0.39929198048901121121,\n                     0.71163724792443083800,   0.023791492919156290164,\n                     3.3146326096778735426,    1.9211926341751801584,\n                     0.77620065471560839576,   0.91371751068503725968,\n                     -0.033856352764628408480, -0.22189988085469286583,\n                     -0.98838426761034614554,  2.0433388956068792019,\n                     -0.76756810709326805942,  -0.037296848003322793930,\n                     2.2419306171966684182};\n      OperatorVars expected_operator_vars_rnd_wedge{27, 0.};\n      get(get<DgOperatorAppliedTo<Var<Poisson::Tags::Field<DataVector>>>>(\n          expected_operator_vars_rnd_wedge)) =\n          DataVector{0.16499268635017833029, 1.8644717241195962742,\n                     0.50779733947485694578, 1.8863114030787757613,\n                     -2.7740067251377729107, -1.4130776603409314074,\n                     1.9928746337497433849,  0.63283835581422620553,\n                     -1.0343599185085030623, 5.8966821182729969308,\n                     5.5504203240642899786,  2.6011964191689305181,\n                     3.3040790531837145316,  20.091810674690414373,\n                     8.0212451646648474934,  5.1144424758479178905,\n                     6.8301727856302010267,  1.0869453410405538474,\n                     5.2284593589347760911,  9.3539358392378026963,\n                     5.7609110245159351749,  19.038783549101548687,\n                     95.952139513786036673,  8.0688375547259152398,\n                     0.69551590983732591855, 4.0994207069118830944,\n                     4.2071397513902919485};\n      // Large tolerances for the comparison to the analytic solution because\n      // this regression test runs at very low resolution. Below is another\n      // analytic-solution test at higher resolution. The hard-coded numbers\n      // are compared at higher (default) precision.\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(0.3).scale(1.);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(0.3).scale(1.);\n      test_dg_operator<system, true>(\n          domain_creator, penalty_parameter, true,\n          Spectral::Quadrature::GaussLobatto, ::dg::Formulation::StrongInertial,\n          analytic_solution, analytic_solution_aux_approx,\n          analytic_solution_operator_approx,\n          {{{{center_id, std::move(vars_rnd_center)},\n             {wedge_id, std::move(vars_rnd_wedge)}},\n            {},\n            {{center_id, std::move(expected_operator_vars_rnd_center)},\n             {wedge_id, std::move(expected_operator_vars_rnd_wedge)}}}});\n    }\n    {\n      INFO(\"Higher-resolution analytic-solution tests\");\n      const domain::creators::Sphere domain_creator{\n          1.,\n          3.,\n          domain::creators::Sphere::InnerCube{0.},\n          0_st,\n          12_st,\n          true,\n          {},\n          {},\n          domain::CoordinateMaps::Distribution::Linear,\n          ShellWedges::All,\n          std::nullopt,\n          std::make_unique<\n              elliptic::BoundaryConditions::AnalyticSolution<system>>(\n              analytic_solution.get_clone(),\n              elliptic::BoundaryConditionType::Dirichlet)};\n      Approx analytic_solution_aux_approx =\n          Approx::custom().epsilon(1.e-4).scale(1.);\n      Approx analytic_solution_operator_approx =\n          Approx::custom().epsilon(1.e-4).scale(1.);\n      for (const auto& [quadrature, dg_formulation] :\n           cartesian_product(make_array(Spectral::Quadrature::GaussLobatto,\n                                        Spectral::Quadrature::Gauss),\n                             make_array(::dg::Formulation::StrongInertial,\n                                        ::dg::Formulation::StrongLogical,\n                                        ::dg::Formulation::WeakInertial))) {\n        test_dg_operator<system, true>(\n            domain_creator, penalty_parameter, true, quadrature, dg_formulation,\n            analytic_solution, analytic_solution_aux_approx,\n            analytic_solution_operator_approx, {}, true);\n      }\n    }\n  }\n  {\n    INFO(\"3D complex\");\n    using system =\n        Poisson::FirstOrderSystem<3, Poisson::Geometry::FlatCartesian,\n                                  ComplexDataVector>;\n    const Poisson::Solutions::Lorentzian<3, ComplexDataVector>\n        analytic_solution{0., M_PI_2};\n    const domain::creators::Sphere domain_creator{\n        1.,\n        3.,\n        domain::creators::Sphere::InnerCube{0.},\n        0_st,\n        12_st,\n        true,\n        {},\n        {},\n        domain::CoordinateMaps::Distribution::Linear,\n        ShellWedges::All,\n        std::nullopt,\n        std::make_unique<\n            elliptic::BoundaryConditions::AnalyticSolution<system>>(\n            analytic_solution.get_clone(),\n            elliptic::BoundaryConditionType::Dirichlet)};\n    Approx analytic_solution_aux_approx =\n        Approx::custom().epsilon(1.e-4).scale(1.);\n    Approx analytic_solution_operator_approx =\n        Approx::custom().epsilon(1.e-4).scale(1.);\n    for (const auto& [quadrature, dg_formulation] :\n         cartesian_product(make_array(Spectral::Quadrature::GaussLobatto,\n                                      Spectral::Quadrature::Gauss),\n                           make_array(::dg::Formulation::StrongInertial,\n                                      ::dg::Formulation::StrongLogical,\n                                      ::dg::Formulation::WeakInertial))) {\n      test_dg_operator<system, true>(\n          domain_creator, penalty_parameter, true, quadrature, dg_formulation,\n          analytic_solution, analytic_solution_aux_approx,\n          analytic_solution_operator_approx, {}, true);\n    }\n  }\n  {\n    INFO(\"2D with modified boundary data\");\n    using system = ModifiedPoissonSystem<2>;\n    const ModifiedPoissonSolution<2> analytic_solution{{{M_PI, M_PI}}};\n    const auto dirichlet_bc =\n        elliptic::BoundaryConditions::AnalyticSolution<system>{\n            analytic_solution.get_clone(),\n            elliptic::BoundaryConditionType::Dirichlet};\n    // In block 1 (x > 0.5) we decompose u = U_R + u_P with u_P = 2x + 3y and\n    // solve for u_R.\n    const domain::creators::AlignedLattice<2> domain_creator{\n        {{{0., 0.5, 1.}, {0., 1.}}},\n        {{1, 1}},\n        {{12, 12}},\n        {},\n        {},\n        {},\n        {{{{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}},\n          {{dirichlet_bc.get_clone(), dirichlet_bc.get_clone()}}}}};\n    Approx analytic_solution_aux_approx =\n        Approx::custom().epsilon(1.e-4).scale(1.);\n    Approx analytic_solution_operator_approx =\n        Approx::custom().epsilon(1.e-4).scale(1.);\n    for (const auto& [dg_formulation, massive] :\n         cartesian_product(make_array(::dg::Formulation::StrongInertial,\n                                      ::dg::Formulation::StrongLogical,\n                                      ::dg::Formulation::WeakInertial),\n                           make_array(true, false))) {\n      test_dg_operator<system, true>(\n          domain_creator, penalty_parameter, massive,\n          Spectral::Quadrature::Gauss, dg_formulation, analytic_solution,\n          analytic_solution_aux_approx, analytic_solution_operator_approx, {},\n          true);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/Test_LargeOuterRadius.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgOperator.hpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/WeakDivergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Lorentzian.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.DG.LargeOuterRadius\", \"[Unit][Elliptic]\") {\n  // This test works for the StrongLogical and WeakInertial formulations, but\n  // breaks for the StrongInertial formulation.\n  const ::dg::Formulation formulation = ::dg::Formulation::StrongLogical;\n  // The test also breaks if the constant is set to 1. _and_ the numeric first\n  // derivative is used. It works fine if the analytic first derivative is used\n  // or if the constant is set to 0.\n  const Poisson::Solutions::Lorentzian<3> solution{/* plus_constant */ 1.};\n  const bool use_numeric_first_deriv = false;\n  // Set up the grid\n  using Wedge3D = domain::CoordinateMaps::Wedge<3>;\n  const double inner_radius = 1e2;\n  CAPTURE(inner_radius);\n  const double outer_radius = 1e9;\n  CAPTURE(outer_radius);\n  auto block_map =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Wedge3D{inner_radius, outer_radius, 1., 1.,\n                  OrientationMap<3>::create_aligned(), true,\n                  Wedge3D::WedgeHalves::Both,\n                  domain::CoordinateMaps::Distribution::Inverse});\n  const ElementId<3> element_id{0, {{{2, 0}, {2, 0}, {0, 0}}}};\n  CAPTURE(element_id);\n  const Mesh<3> mesh{12, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  CAPTURE(mesh);\n\n  // Quantify grid stretching by looking at the midpoint of the element\n  const ElementMap<3, Frame::Inertial> element_map{element_id,\n                                                   std::move(block_map)};\n  const auto midpoint_radius = magnitude(\n      element_map(tnsr::I<double, 3, Frame::ElementLogical>{{{0., 0., 0.}}}));\n  CAPTURE(midpoint_radius);\n  Approx custom_approx = Approx::custom().epsilon(1.0e-6).scale(1.0);\n  const size_t num_points = mesh.number_of_grid_points();\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto inertial_coords = element_map(logical_coords);\n  const auto inv_jacobian = element_map.inv_jacobian(logical_coords);\n  const auto det_jacobian = determinant(element_map.jacobian(logical_coords));\n\n  // Take first derivative\n  using Field = Poisson::Tags::Field<DataVector>;\n  using FieldDeriv = ::Tags::deriv<Poisson::Tags::Field<DataVector>,\n                                   tmpl::size_t<3>, Frame::Inertial>;\n  using Flux = ::Tags::Flux<Poisson::Tags::Field<DataVector>, tmpl::size_t<3>,\n                            Frame::Inertial>;\n  using FixedSource = ::Tags::FixedSource<Field>;\n  const auto vars = solution.variables(\n      inertial_coords, tmpl::list<Field, FieldDeriv, FixedSource>{});\n  const auto& u = get<Field>(vars);\n  const auto& du_analytic = get<FieldDeriv>(vars);\n  const auto& f = get<FixedSource>(vars);\n  const auto du_numeric = partial_derivative(u, mesh, inv_jacobian);\n  CHECK_ITERABLE_CUSTOM_APPROX(du_numeric, du_analytic, custom_approx);\n\n  // Take second derivative\n  CAPTURE(formulation);\n  const bool massive = true;\n  CAPTURE(massive);\n  tnsr::I<DataVector, 3> flux{num_points};\n  Poisson::flat_cartesian_fluxes(\n      make_not_null(&flux), use_numeric_first_deriv ? du_numeric : du_analytic);\n  if (formulation == ::dg::Formulation::StrongInertial) {\n    Scalar<DataVector> ddu{num_points, 0.};\n    divergence(make_not_null(&ddu), flux, mesh, inv_jacobian);\n    DataVector lhs = -get(ddu);\n    DataVector rhs = get(f);\n    // Massive scheme multiplies by Jacobian determinant\n    if (massive) {\n      lhs *= get(det_jacobian);\n      rhs *= get(det_jacobian);\n    }\n    CHECK_ITERABLE_CUSTOM_APPROX(lhs, rhs, custom_approx);\n  } else if (formulation == ::dg::Formulation::StrongLogical) {\n    Scalar<DataVector> ddu{num_points, 0.};\n    tnsr::I<DataVector, 3, Frame::ElementLogical> logical_flux =\n        transform::first_index_to_different_frame(flux, inv_jacobian);\n    for (size_t d = 0; d < 3; ++d) {\n      logical_flux.get(d) *= get(det_jacobian);\n    }\n    logical_divergence(make_not_null(&ddu), logical_flux, mesh);\n    DataVector lhs = -get(ddu);\n    DataVector rhs = get(f);\n    // Massive scheme multiplies by Jacobian determinant\n    if (massive) {\n      ::dg::apply_mass_matrix(make_not_null(&lhs), mesh);\n      ::dg::apply_mass_matrix(make_not_null(&rhs), mesh);\n      rhs *= get(det_jacobian);\n    } else {\n      lhs /= get(det_jacobian);\n    }\n    CHECK_ITERABLE_CUSTOM_APPROX(lhs, rhs, custom_approx);\n  } else {\n    auto det_times_inv_jacobian = inv_jacobian;\n    for (auto& component : det_times_inv_jacobian) {\n      component *= get(det_jacobian);\n    }\n    Variables<tmpl::list<Flux>> fluxes{mesh.number_of_grid_points()};\n    get<Flux>(fluxes) = flux;\n    Variables<tmpl::list<Field>> lhs{mesh.number_of_grid_points()};\n    weak_divergence(make_not_null(&lhs), fluxes, mesh, det_times_inv_jacobian);\n    DataVector rhs = get(f);\n    if (massive) {\n      ::dg::apply_mass_matrix(make_not_null(&lhs), mesh);\n      ::dg::apply_mass_matrix(make_not_null(&rhs), mesh);\n      rhs *= get(det_jacobian);\n    } else {\n      lhs /= get(det_jacobian);\n    }\n    // Add boundary corrections\n    for (const auto direction : Direction<3>::all_directions()) {\n      CAPTURE(direction);\n      // Compute face geometry\n      const auto face_mesh = mesh.slice_away(direction.dimension());\n      const size_t face_num_points = face_mesh.number_of_grid_points();\n      const auto face_logical_coords =\n          interface_logical_coordinates(face_mesh, direction);\n      const auto face_inertial_coords = element_map(face_logical_coords);\n      auto face_normal =\n          unnormalized_face_normal(face_mesh, element_map, direction);\n      const auto face_normal_magnitude = magnitude(face_normal);\n      for (size_t d = 0; d < 3; ++d) {\n        face_normal.get(d) /= get(face_normal_magnitude);\n      }\n      auto face_jacobian = face_normal_magnitude;\n      const auto det_jacobian_face =\n          determinant(element_map.jacobian(face_logical_coords));\n      get(face_jacobian) *= get(det_jacobian_face);\n      // Compute boundary correction\n      const auto vars_on_face =\n          solution.variables(face_inertial_coords, tmpl::list<FieldDeriv>{});\n      const auto& du_on_face = get<FieldDeriv>(vars_on_face);\n      Variables<tmpl::list<Flux>> fluxes_on_face{face_num_points};\n      Poisson::flat_cartesian_fluxes(make_not_null(&get<Flux>(fluxes_on_face)),\n                                     du_on_face);\n      auto boundary_correction =\n          normal_dot_flux<tmpl::list<Field>>(face_normal, fluxes_on_face);\n      ASSERT(massive, \"Only massive scheme supported here\");\n      ::dg::apply_mass_matrix(make_not_null(&boundary_correction), face_mesh);\n      boundary_correction *= get(face_jacobian);\n      boundary_correction *= -1.;\n      if (mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto) {\n        add_slice_to_data(make_not_null(&lhs), boundary_correction,\n                          mesh.extents(), direction.dimension(),\n                          index_to_slice_at(mesh.extents(), direction));\n      } else {\n        ::dg::lift_boundary_terms_gauss_points(\n            make_not_null(&lhs), boundary_correction, mesh, direction);\n      }\n    }\n    CHECK_ITERABLE_CUSTOM_APPROX(get(get<Field>(lhs)), rhs, custom_approx);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/Test_Penalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Penalty.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.DG.Penalty\", \"[Unit][Elliptic]\") {\n  const size_t num_points = 3;\n  const DataVector element_size{1., 2., 3.};\n  const double penalty_parameter = 1.5;\n  const DataVector expected_penalty{13.5, 6.75, 4.5};\n  CHECK_ITERABLE_APPROX(\n      elliptic::dg::penalty(element_size, num_points, penalty_parameter),\n      expected_penalty);\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/DiscontinuousGalerkin/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace elliptic::dg {\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.DG.Tags\", \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<Tags::PenaltyParameter>(\"PenaltyParameter\");\n  TestHelpers::db::test_simple_tag<Tags::PenaltyFactor>(\"PenaltyFactor\");\n  TestHelpers::db::test_simple_tag<Tags::Massive>(\"Massive\");\n  TestHelpers::db::test_simple_tag<Tags::Quadrature>(\"Quadrature\");\n  TestHelpers::db::test_simple_tag<Tags::Formulation>(\"Formulation\");\n}\n\n}  // namespace elliptic::dg\n"
  },
  {
    "path": "tests/Unit/Elliptic/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Elliptic.Python.ReadH5\"\n  \"Test_ReadH5.py\"\n  \"unit;elliptic;python\"\n  None\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Python/Test_ReadH5.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Domain import ElementId\nfrom spectre.Elliptic.ReadH5 import read_matrix\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Spectral import Basis, Mesh, Quadrature\n\n\nclass TestReadH5(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Elliptic/Python/ReadH5\"\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_read_matrix(self):\n        filename = os.path.join(self.test_dir, \"TestMatrix.h5\")\n        subfile_name = \"/Matrix\"\n        num_vars = 2\n        num_points = 3\n        num_elements = 2\n        mesh = Mesh[1](num_points, Basis.Legendre, Quadrature.GaussLobatto)\n        size = num_vars * num_points * num_elements\n        expected_matrix = np.random.rand(size, size)\n        # Write matrix to file\n        with spectre_h5.H5File(filename, \"w\") as h5file:\n            volfile = h5file.insert_vol(subfile_name, version=0)\n            for col in range(size):\n                volume_data = [\n                    spectre_h5.ElementVolumeData(\n                        element_id=ElementId[1](0),\n                        components=[\n                            spectre_h5.TensorComponent(\n                                \"Variable_0\", expected_matrix[:3, col]\n                            ),\n                            spectre_h5.TensorComponent(\n                                \"Variable_1\", expected_matrix[3:6, col]\n                            ),\n                        ],\n                        mesh=mesh,\n                    ),\n                    spectre_h5.ElementVolumeData(\n                        element_id=ElementId[1](1),\n                        components=[\n                            spectre_h5.TensorComponent(\n                                \"Variable_0\", expected_matrix[6:9, col]\n                            ),\n                            spectre_h5.TensorComponent(\n                                \"Variable_1\", expected_matrix[9:12, col]\n                            ),\n                        ],\n                        mesh=mesh,\n                    ),\n                ]\n                volfile.write_volume_data(\n                    observation_id=col,\n                    observation_value=col,\n                    elements=volume_data,\n                )\n                npt.assert_array_equal(\n                    volfile.get_tensor_component(col, \"Variable_1\").data,\n                    np.concatenate(\n                        [expected_matrix[3:6, col], expected_matrix[9:12, col]]\n                    ),\n                )\n        # Test reading matrix\n        with spectre_h5.H5File(filename, \"r\") as h5file:\n            volfile = h5file.get_vol(subfile_name)\n            matrix = read_matrix(volfile)\n        npt.assert_array_equal(matrix, expected_matrix)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Elliptic/SubdomainPreconditioners/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticSubdomainPreconditioners\")\n\nset(LIBRARY_SOURCES\n  Test_MinusLaplacian.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainStructure\n  EllipticSubdomainPreconditioners\n  Options\n  Parallel\n  ParallelSchwarz\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/SubdomainPreconditioners/Test_MinusLaplacian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <tuple>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/MinusLaplacian.hpp\"\n#include \"Elliptic/SubdomainPreconditioners/RegisterDerived.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename DataType, size_t Dim>\nstruct VectorFieldTag : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim>;\n};\ntemplate <size_t Dim>\nusing PoissonSubdomainData =\n    LinearSolver::Schwarz::ElementCenteredSubdomainData<\n        Dim, tmpl::list<Poisson::Tags::Field<DataVector>>>;\nstruct OptionsGroup {};\n\ntemplate <size_t Dim>\nstruct BoundaryCondition\n    : elliptic::BoundaryConditions::BoundaryCondition<Dim> {\n  explicit BoundaryCondition(\n      std::vector<elliptic::BoundaryConditionType> bc_types)\n      : bc_types_(std::move(bc_types)) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BoundaryCondition);\n  std::unique_ptr<domain::BoundaryConditions::BoundaryCondition> get_clone()\n      const override {\n    return std::make_unique<BoundaryCondition>(*this);\n  }\n  std::vector<elliptic::BoundaryConditionType> boundary_condition_types()\n      const override {\n    return bc_types_;\n  }\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    elliptic::BoundaryConditions::BoundaryCondition<Dim>::pup(p);\n    p | bc_types_;\n  }\n\n private:\n  std::vector<elliptic::BoundaryConditionType> bc_types_;\n};\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID BoundaryCondition<Dim>::my_PUP_ID = 0;  // NOLINT\n\n// We don't actually solve the subdomain operator in this test, because it is\n// implemented and tested elsewhere. Instead, we test the solver is invoked\n// correctly for every tensor component.\ntemplate <size_t Dim>\nstruct TestSolver {\n  using options = tmpl::list<>;\n  static constexpr Options::String help = \"halp\";\n\n  template <typename LinearOperator, typename... OperatorArgs>\n  Convergence::HasConverged solve(\n      const gsl::not_null<PoissonSubdomainData<Dim>*>\n          initial_guess_in_solution_out,\n      const LinearOperator& /*linear_operator*/,\n      const PoissonSubdomainData<Dim>& source,\n      const std::tuple<OperatorArgs...>& operator_args) const {\n    // Check the initial guess for each component is sized correctly and zero\n    for (size_t i = 0; i < source.element_data.size(); ++i) {\n      CHECK(initial_guess_in_solution_out->element_data.data()[i] == 0.);\n    }\n    for (const auto& [overlap_id, source_data] : source.overlap_data) {\n      for (size_t i = 0; i < source_data.size(); ++i) {\n        CHECK(initial_guess_in_solution_out->overlap_data.at(overlap_id)\n                  .data()[i] == 0.);\n      }\n    }\n    // Check the boundary conditions\n    const auto& boundary_conditions = get<1>(operator_args);\n    std::map<std::pair<size_t, Direction<Dim>>, elliptic::BoundaryConditionType>\n        local_bc_types{};\n    for (const auto& [boundary_id, bc] : boundary_conditions) {\n      const auto robin_bc =\n          dynamic_cast<const Poisson::BoundaryConditions::Robin<Dim>*>(&bc);\n      REQUIRE(robin_bc != nullptr);\n      CHECK(robin_bc->constant() == 0.);\n      CHECK(((robin_bc->dirichlet_weight() == 1. and\n              robin_bc->neumann_weight() == 0.) or\n             (robin_bc->dirichlet_weight() == 0. and\n              robin_bc->neumann_weight() == 1.)));\n      local_bc_types.emplace(boundary_id,\n                             robin_bc->neumann_weight() == 1.\n                                 ? elliptic::BoundaryConditionType::Neumann\n                                 : elliptic::BoundaryConditionType::Dirichlet);\n    }\n    bc_types.push_back(std::move(local_bc_types));\n    // Keep track of the sources so they can be checked\n    sources.push_back(source);\n    return {0, 0};\n  }\n  void reset() {\n    sources.clear();\n    bc_types.clear();\n    was_reset = true;\n  }\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::vector<PoissonSubdomainData<Dim>> sources{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::vector<std::map<std::pair<size_t, Direction<Dim>>,\n                               elliptic::BoundaryConditionType>>\n      bc_types{};\n  bool was_reset{false};\n};\n\ntemplate <size_t Dim, typename FullData, typename Tag>\nvoid check_component(const PoissonSubdomainData<Dim>& poisson_data,\n                     const FullData& expected_data, Tag /*meta*/,\n                     const size_t component, const bool check_imag = false) {\n  const auto& expected_element_data =\n      get<std::decay_t<Tag>>(expected_data.element_data)[component];\n  CHECK(get(get<Poisson::Tags::Field<DataVector>>(poisson_data.element_data)) ==\n        (check_imag ? DataVector(imag(expected_element_data))\n                    : DataVector(real(expected_element_data))));\n  for (const auto& [overlap_id, data] : expected_data.overlap_data) {\n    const auto& expected_overlap_data = get<std::decay_t<Tag>>(data)[component];\n    CHECK(get(get<Poisson::Tags::Field<DataVector>>(\n              poisson_data.overlap_data.at(overlap_id))) ==\n          (check_imag ? DataVector(imag(expected_overlap_data))\n                      : DataVector(real(expected_overlap_data))));\n  }\n}\n\ntemplate <size_t Dim>\nauto make_block_map() {\n  return domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n      domain::CoordinateMaps::Identity<Dim>{});\n}\n\nauto make_databox_with_boundary_conditions() {\n  constexpr size_t Dim = 2;\n  // Subdomain geometry:\n  //\n  //             D / D / D\n  //              vvvvv\n  //             +---+-+\n  // N / D / D > |   | |    (Block 0)\n  //             +---+-+\n  // D / N / N > |   |      (Block 1)\n  //             +---+\n  //\n  // - Two blocks, three tensor components (a scalar and a vector). Block 0 has\n  //   an external boundary at the top with Dirichlet conditions for all\n  //   components, and an external boundary to the left with Neumann conditions\n  //   for the scalar and Dirichlet conditions for the vector. Block 1 has an\n  //   external boundary to the left with the reverse.\n  // - The subdomain is centered on the top-left element of the domain.\n  //   Horizontally, it overlaps with another element of Block 0 toward the\n  //   right. Vertically, the element spans the entire Block 0 and the subdomain\n  //   overlaps with an element of Block 1. The domain extends further toward\n  //   the right and the bottom.\n  // - We have two distinct boundary condition signatures: (D, N, D) for the\n  //   the scalar, and (D, D, N) for both vector components, where the three\n  //   entries in the signature refer to the three external boundaries in the\n  //   order (Block 0 top, Block 0 left, Block 1 left).\n\n  // Boundary conditions\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{2};\n  boundary_conditions[0][Direction<Dim>::upper_eta()] =\n      std::make_unique<BoundaryCondition<Dim>>(BoundaryCondition<Dim>{\n          {3, elliptic::BoundaryConditionType::Dirichlet}});\n  boundary_conditions[0][Direction<Dim>::lower_xi()] =\n      std::make_unique<BoundaryCondition<Dim>>(\n          BoundaryCondition<Dim>{{elliptic::BoundaryConditionType::Neumann,\n                                  elliptic::BoundaryConditionType::Dirichlet,\n                                  elliptic::BoundaryConditionType::Dirichlet}});\n  boundary_conditions[1][Direction<Dim>::lower_xi()] =\n      std::make_unique<BoundaryCondition<Dim>>(\n          BoundaryCondition<Dim>{{elliptic::BoundaryConditionType::Dirichlet,\n                                  elliptic::BoundaryConditionType::Neumann,\n                                  elliptic::BoundaryConditionType::Neumann}});\n  // only needed for completeness, not used in test\n  boundary_conditions[0][Direction<Dim>::upper_xi()] =\n      std::make_unique<BoundaryCondition<Dim>>(BoundaryCondition<Dim>{\n          {3, elliptic::BoundaryConditionType::Dirichlet}});\n  boundary_conditions[1][Direction<Dim>::upper_xi()] =\n      std::make_unique<BoundaryCondition<Dim>>(BoundaryCondition<Dim>{\n          {3, elliptic::BoundaryConditionType::Dirichlet}});\n  boundary_conditions[1][Direction<Dim>::lower_eta()] =\n      std::make_unique<BoundaryCondition<Dim>>(BoundaryCondition<Dim>{\n          {3, elliptic::BoundaryConditionType::Dirichlet}});\n  // Blocks\n  Block<Dim> block_0{make_block_map<Dim>(),\n                     0,\n                     {{Direction<Dim>::lower_eta(),\n                       {1, OrientationMap<Dim>::create_aligned()}}}};\n  Block<Dim> block_1{make_block_map<Dim>(),\n                     1,\n                     {{Direction<Dim>::upper_eta(),\n                       {0, OrientationMap<Dim>::create_aligned()}}}};\n  // Domain\n  std::vector<Block<Dim>> blocks{};\n  blocks.emplace_back(std::move(block_0));\n  blocks.emplace_back(std::move(block_1));\n  Domain<Dim> domain{std::move(blocks)};\n  // Refinement\n  const std::vector<std::array<size_t, Dim>> refinement{{{2, 0}}, {{1, 1}}};\n  // Elements\n  const ElementId<Dim> central_element_id{0, {{{2, 0}, {0, 0}}}};\n  const ElementId<Dim> right_element_id{0, {{{2, 1}, {0, 0}}}};\n  const ElementId<Dim> bottom_element_id{1, {{{1, 0}, {1, 1}}}};\n  // NOLINTNEXTLINE(misc-const-correctness)\n  Element<Dim> central_element = domain::create_initial_element(\n      central_element_id, domain.blocks(), refinement);\n  Element<Dim> right_element = domain::create_initial_element(\n      right_element_id, domain.blocks(), refinement);\n  Element<Dim> bottom_element = domain::create_initial_element(\n      bottom_element_id, domain.blocks(), refinement);\n  // Subdomain\n  LinearSolver::Schwarz::OverlapMap<Dim, Element<Dim>> overlap_elements{\n      {{Direction<Dim>::upper_xi(), right_element_id},\n       std::move(right_element)},\n      {{Direction<Dim>::lower_eta(), bottom_element_id},\n       std::move(bottom_element)}};\n  return db::create<tmpl::list<\n      domain::Tags::ExternalBoundaryConditions<Dim>, domain::Tags::Element<Dim>,\n      LinearSolver::Schwarz::Tags::Overlaps<domain::Tags::Element<Dim>, Dim,\n                                            OptionsGroup>>>(\n      std::move(boundary_conditions), std::move(central_element),\n      std::move(overlap_elements));\n}\n\nauto make_databox_without_boundary_conditions() {\n  constexpr size_t Dim = 2;\n  Block<Dim> block{make_block_map<Dim>(), 0, {}};\n  std::vector<Block<Dim>> blocks{};\n  blocks.emplace_back(std::move(block));\n  Domain<Dim> domain{std::move(blocks)};\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      boundary_conditions{domain.blocks().size()};\n  const std::vector<std::array<size_t, Dim>> refinement{{{2, 2}}};\n  const ElementId<Dim> central_element_id{0, {{{2, 1}, {2, 1}}}};\n  const ElementId<Dim> right_element_id{0, {{{2, 2}, {2, 1}}}};\n  // NOLINTNEXTLINE(misc-const-correctness)\n  Element<Dim> central_element = domain::create_initial_element(\n      central_element_id, domain.blocks(), refinement);\n  Element<Dim> right_element = domain::create_initial_element(\n      right_element_id, domain.blocks(), refinement);\n  LinearSolver::Schwarz::OverlapMap<Dim, Element<Dim>> overlap_elements{\n      {{Direction<Dim>::upper_xi(), right_element_id},\n       std::move(right_element)}};\n  return db::create<tmpl::list<\n      domain::Tags::ExternalBoundaryConditions<Dim>, domain::Tags::Element<Dim>,\n      LinearSolver::Schwarz::Tags::Overlaps<domain::Tags::Element<Dim>, Dim,\n                                            OptionsGroup>>>(\n      std::move(boundary_conditions), std::move(central_element),\n      std::move(overlap_elements));\n}\n}  // namespace\n\nnamespace elliptic::subdomain_preconditioners {\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.SubdomainPreconditioners.MinusLaplacian\",\n                  \"[Unit][Elliptic]\") {\n  {\n    constexpr size_t Dim = 2;\n    using LinearSolverType = ::LinearSolver::Serial::LinearSolver<tmpl::list<\n        Registrars::MinusLaplacian<Dim, OptionsGroup, TestSolver<Dim>>>>;\n    register_derived_classes_with_charm<LinearSolverType>();\n    const auto created =\n        TestHelpers::test_creation<std::unique_ptr<LinearSolverType>>(\n            \"MinusLaplacian:\\n\"\n            \"  Solver:\\n\"\n            \"  BoundaryConditions: Auto\\n\");\n    REQUIRE(\n        dynamic_cast<const MinusLaplacian<Dim, OptionsGroup, TestSolver<Dim>>*>(\n            created.get()) != nullptr);\n    const auto serialized = serialize_and_deserialize(created);\n    auto cloned = serialized->get_clone();\n    auto& minus_laplacian =\n        dynamic_cast<MinusLaplacian<Dim, OptionsGroup, TestSolver<Dim>>&>(\n            *cloned);\n    // The \"real\" linear operator is unused, because the solve approximates it\n    // with a Laplacian\n    const NoSuchType linear_operator{};\n    // Manufacture a source with multiple tensors\n    using SubdomainData = LinearSolver::Schwarz::ElementCenteredSubdomainData<\n        Dim, tmpl::list<ScalarFieldTag<DataVector>,\n                        VectorFieldTag<DataVector, Dim>>>;\n    SubdomainData source{};\n    source.element_data.initialize(5);\n    source\n        .overlap_data[DirectionalId<Dim>{Direction<Dim>::lower_xi(),\n                                         ElementId<Dim>{0}}]\n        .initialize(3);\n    std::iota(source.begin(), source.end(), 1.);\n    // Apply the solver\n    auto initial_guess_in_solution_out =\n        make_with_value<SubdomainData>(source, 0.);\n    using BcSignature = typename std::decay_t<\n        decltype(minus_laplacian)>::BoundaryConditionsSignature;\n    {\n      INFO(\"Subdomain with no external boundaries\");\n      minus_laplacian.solve(\n          make_not_null(&initial_guess_in_solution_out), linear_operator,\n          source, std::make_tuple(make_databox_without_boundary_conditions()));\n      // Test the solver was applied to every tensor component in turn\n      const auto& solver = minus_laplacian.solver();\n      REQUIRE(solver.sources.size() == 3);\n      check_component(solver.sources[0], source, ScalarFieldTag<DataVector>{},\n                      0);\n      check_component(solver.sources[1], source,\n                      VectorFieldTag<DataVector, Dim>{}, 0);\n      check_component(solver.sources[2], source,\n                      VectorFieldTag<DataVector, Dim>{}, 1);\n      CHECK(solver.bc_types[0].empty());\n      CHECK(solver.bc_types[1].empty());\n      CHECK(solver.bc_types[2].empty());\n      CHECK(minus_laplacian.cached_solvers().empty());\n    }\n    minus_laplacian.reset();\n    {\n      INFO(\"Complex variables\");\n      using SubdomainDataComplex =\n          LinearSolver::Schwarz::ElementCenteredSubdomainData<\n              Dim, tmpl::list<ScalarFieldTag<ComplexDataVector>,\n                              VectorFieldTag<ComplexDataVector, Dim>>>;\n      SubdomainDataComplex source_complex{};\n      source_complex.element_data.initialize(5);\n      source_complex\n          .overlap_data[DirectionalId<Dim>{Direction<Dim>::lower_xi(),\n                                           ElementId<Dim>{0}}]\n          .initialize(3);\n      std::iota(source_complex.begin(), source_complex.end(), 1.);\n      // Apply the solver\n      auto initial_guess_in_solution_out_complex =\n          make_with_value<SubdomainDataComplex>(source_complex, 0.);\n      minus_laplacian.solve(\n          make_not_null(&initial_guess_in_solution_out_complex),\n          linear_operator, source,\n          std::make_tuple(make_databox_without_boundary_conditions()));\n      // Test the solver was applied to every tensor component in turn, for both\n      // the real and imaginary part\n      const auto& solver = minus_laplacian.solver();\n      REQUIRE(solver.sources.size() == 6);  // 3 components, real and imaginary\n      check_component(solver.sources[0], source_complex,\n                      ScalarFieldTag<ComplexDataVector>{}, 0, false);\n      check_component(solver.sources[1], source_complex,\n                      ScalarFieldTag<ComplexDataVector>{}, 0, true);\n      check_component(solver.sources[2], source_complex,\n                      VectorFieldTag<ComplexDataVector, Dim>{}, 0, false);\n      check_component(solver.sources[3], source_complex,\n                      VectorFieldTag<ComplexDataVector, Dim>{}, 0, true);\n      check_component(solver.sources[4], source_complex,\n                      VectorFieldTag<ComplexDataVector, Dim>{}, 1, false);\n      check_component(solver.sources[5], source_complex,\n                      VectorFieldTag<ComplexDataVector, Dim>{}, 1, true);\n      CHECK(solver.bc_types.size() == 6);\n      for (const auto& bc_types : solver.bc_types) {\n        CHECK(bc_types.empty());\n      }\n      CHECK(minus_laplacian.cached_solvers().empty());\n    }\n    minus_laplacian.reset();\n    {\n      INFO(\"Subdomain with multiple boundary conditions\");\n      minus_laplacian.solve(\n          make_not_null(&initial_guess_in_solution_out), linear_operator,\n          source, std::make_tuple(make_databox_with_boundary_conditions()));\n      // Test the solver was applied to every tensor component in turn\n      // - A solver for each unique boundary-condition configuration should have\n      //   been created and cached\n      const auto& cached_solvers = minus_laplacian.cached_solvers();\n      REQUIRE(cached_solvers.size() == 2);\n      // - The solver for (D, N, D) should be used for the scalar\n      const BcSignature signature_dnd{\n          {{0, Direction<Dim>::upper_eta()},\n           elliptic::BoundaryConditionType::Dirichlet},\n          {{0, Direction<Dim>::lower_xi()},\n           elliptic::BoundaryConditionType::Neumann},\n          {{1, Direction<Dim>::lower_xi()},\n           elliptic::BoundaryConditionType::Dirichlet}};\n      REQUIRE(cached_solvers.at(signature_dnd).sources.size() == 1);\n      check_component(cached_solvers.at(signature_dnd).sources[0], source,\n                      ScalarFieldTag<DataVector>{}, 0);\n      CHECK(cached_solvers.at(signature_dnd).bc_types[0] == signature_dnd);\n      // - The solver for (D, D, N) should be used for both vector components\n      const BcSignature signature_ddn{\n          {{0, Direction<Dim>::upper_eta()},\n           elliptic::BoundaryConditionType::Dirichlet},\n          {{0, Direction<Dim>::lower_xi()},\n           elliptic::BoundaryConditionType::Dirichlet},\n          {{1, Direction<Dim>::lower_xi()},\n           elliptic::BoundaryConditionType::Neumann}};\n      REQUIRE(cached_solvers.at(signature_ddn).sources.size() == 2);\n      check_component(cached_solvers.at(signature_ddn).sources[0], source,\n                      VectorFieldTag<DataVector, Dim>{}, 0);\n      check_component(cached_solvers.at(signature_ddn).sources[1], source,\n                      VectorFieldTag<DataVector, Dim>{}, 1);\n      CHECK(cached_solvers.at(signature_ddn).bc_types[0] == signature_ddn);\n      CHECK(cached_solvers.at(signature_ddn).bc_types[1] == signature_ddn);\n      // - The factory-constructed solver is not invoked, because it is only\n      //   used as a template for each unique boundary-condition configuration\n      CHECK(minus_laplacian.solver().sources.empty());\n    }\n    {\n      INFO(\"Explicitly specified boundary condition\");\n      const auto minus_laplacian_dirichlet = TestHelpers::test_creation<\n          MinusLaplacian<Dim, OptionsGroup, TestSolver<Dim>>>(\n          \"Solver:\\n\"\n          \"BoundaryConditions: Dirichlet\\n\");\n      minus_laplacian_dirichlet.solve(\n          make_not_null(&initial_guess_in_solution_out), linear_operator,\n          source, std::make_tuple(make_databox_with_boundary_conditions()));\n      // All boundary conditions are Dirichlet, so we should have only a single\n      // cached solver\n      const auto& cached_solvers = minus_laplacian_dirichlet.cached_solvers();\n      REQUIRE(cached_solvers.size() == 1);\n      // The solver for (D, D, D) should be used for all components\n      const BcSignature signature_ddd{\n          {{0, Direction<Dim>::upper_eta()},\n           elliptic::BoundaryConditionType::Dirichlet},\n          {{0, Direction<Dim>::lower_xi()},\n           elliptic::BoundaryConditionType::Dirichlet},\n          {{1, Direction<Dim>::lower_xi()},\n           elliptic::BoundaryConditionType::Dirichlet}};\n      const auto& cached_solver = cached_solvers.at(signature_ddd);\n      REQUIRE(cached_solver.sources.size() == 3);\n      check_component(cached_solver.sources[0], source,\n                      ScalarFieldTag<DataVector>{}, 0);\n      check_component(cached_solver.sources[1], source,\n                      VectorFieldTag<DataVector, Dim>{}, 0);\n      check_component(cached_solver.sources[2], source,\n                      VectorFieldTag<DataVector, Dim>{}, 1);\n      CHECK(cached_solver.bc_types[0] == signature_ddd);\n      CHECK(cached_solver.bc_types[1] == signature_ddd);\n      CHECK(cached_solver.bc_types[2] == signature_ddd);\n      CHECK(minus_laplacian_dirichlet.solver().sources.empty());\n    }\n  }\n  {\n    INFO(\"Factory-create the solver\");\n    constexpr size_t Dim = 2;\n    using LinearSolverType = ::LinearSolver::Serial::LinearSolver<\n        tmpl::list<Registrars::MinusLaplacian<Dim, OptionsGroup>>>;\n    register_derived_classes_with_charm<LinearSolverType>();\n    elliptic::subdomain_preconditioners::register_derived_with_charm();\n    const auto created =\n        TestHelpers::test_creation<std::unique_ptr<LinearSolverType>>(\n            \"MinusLaplacian:\\n\"\n            \"  Solver:\\n\"\n            \"    ExplicitInverse:\\n\"\n            \"      WriteMatrixToFile: None\\n\"\n            \"  BoundaryConditions: Auto\");\n    const auto serialized = serialize_and_deserialize(created);\n    const auto cloned = serialized->get_clone();\n    REQUIRE(dynamic_cast<const MinusLaplacian<Dim, OptionsGroup>*>(\n                cloned.get()) != nullptr);\n    const auto& minus_laplacian =\n        dynamic_cast<const MinusLaplacian<Dim, OptionsGroup>&>(*cloned);\n    const auto& solver = minus_laplacian.solver();\n    REQUIRE(dynamic_cast<const LinearSolver::Serial::ExplicitInverse<\n                double, typename MinusLaplacian<\n                            Dim, OptionsGroup>::solver_type::registrars>*>(\n                &solver) != nullptr);\n  }\n  {\n    INFO(\"Resetting\");\n    MinusLaplacian<1, OptionsGroup, TestSolver<1>> resetting_solver{};\n    CHECK_FALSE(resetting_solver.solver().was_reset);\n    resetting_solver.reset();\n    CHECK(resetting_solver.solver().was_reset);\n  }\n}\n\n}  // namespace elliptic::subdomain_preconditioners\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/BnsInitialData/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_BnsInitialDataBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_StarSurface.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainStructure\n  Elliptic\n  IrrotationalBnsBoundaryConditions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/BnsInitialData/BoundaryConditions/StarSurface.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef star_surface_normal_dot_flux(\n    velocity_potential,\n    velocity_potential_gradient,\n    lapse,\n    rotational_shift,\n    euler_enthalpy_constnant,\n    normal,\n):\n    return (\n        euler_enthalpy_constnant\n        / lapse**2\n        * np.einsum(\"i,i\", rotational_shift, normal)\n    )\n\n\ndef star_surface_normal_dot_flux_linearized(field_correction):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/BnsInitialData/BoundaryConditions/Test_StarSurface.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/BnsInitialData/BoundaryConditions/StarSurface.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace BnsInitialData::BoundaryConditions {\n\nnamespace {\n// the velocity potential does not matter\ntemplate <bool Linearized>\nvoid apply_star_surface_boundary_condition_specific(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_auxilliary_velocity,\n    Scalar<DataVector> velocity_potential = Scalar<DataVector>{\n        DataVector{1.0, 2.0, 3.0}}) {\n  const StarSurface boundary_condition{{}};\n  const tnsr::i<DataVector, 3> velocity_potential_gradient{\n      {DataVector{1.0, 1.0, 1.0}, DataVector{0.0, 0.0, 0.0},\n       DataVector{0.0, 0.0, 0.0}}};\n  const Scalar<DataVector> lapse{DataVector{1.0, 1.5, 1.2}};\n  const tnsr::I<DataVector, 3> rotational_shift{{DataVector{1.0, 1.0, 1.0},\n                                                 DataVector{0.0, 0.0, 0.0},\n                                                 DataVector{0.0, 0.0, 0.0}}};\n  const auto x = tnsr::I<DataVector, 3>{{DataVector{1.0, 1.0, 1.0},\n                                         DataVector{0.0, 0.0, 0.0},\n                                         DataVector{0.0, 0.0, 0.0}}};\n\n  const auto direction = Direction<3>::lower_xi();\n  const tnsr::i<DataVector, 3> normal{{DataVector{1.0, 0.0, 0.0},\n                                       DataVector{0.0, 1.0, 0.0},\n                                       DataVector{0.0, 0.0, 1.0}}};\n  const double euler_enthalpy_constant = 1.0;\n  const auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Faces<3, domain::Tags::Coordinates<3, Frame::Inertial>>,\n      domain::Tags::Faces<3, domain::Tags::FaceNormal<3>>,\n      domain::Tags::Faces<3, gr::Tags::Lapse<DataVector>>,\n      domain::Tags::Faces<3, BnsInitialData::Tags::RotationalShift<DataVector>>,\n      BnsInitialData::Tags::EulerEnthalpyConstant>>(\n      DirectionMap<3, tnsr::I<DataVector, 3>>{{direction, x}},\n      DirectionMap<3, tnsr::i<DataVector, 3>>{{direction, normal}},\n      DirectionMap<3, Scalar<DataVector>>{{direction, lapse}},\n      DirectionMap<3, tnsr::I<DataVector, 3>>{{direction, rotational_shift}},\n      euler_enthalpy_constant);\n  elliptic::apply_boundary_condition<Linearized, void, tmpl::list<StarSurface>>(\n      boundary_condition, box, Direction<3>::lower_xi(),\n      make_not_null(&velocity_potential), n_dot_auxilliary_velocity,\n      velocity_potential_gradient);\n}\n\nvoid apply_star_surface_boundary_condition_generic(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_auxilliary_velocity,\n    Scalar<DataVector> velocity_potential,\n    const tnsr::i<DataVector, 3>& velocity_potential_gradient,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3>& rotational_shift,\n    const double euler_enthalpy_constant,\n    const tnsr::i<DataVector, 3>& normal) {\n  const StarSurface boundary_condition{{}};\n\n  const auto x = tnsr::I<DataVector, 3>{{DataVector{1.0, 1.0, 1.0},\n                                         DataVector{0.0, 0.0, 0.0},\n                                         DataVector{0.0, 0.0, 0.0}}};\n\n  const auto direction = Direction<3>::lower_xi();\n\n  const auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Faces<3, domain::Tags::Coordinates<3, Frame::Inertial>>,\n      domain::Tags::Faces<3, domain::Tags::FaceNormal<3>>,\n      domain::Tags::Faces<3, gr::Tags::Lapse<DataVector>>,\n      domain::Tags::Faces<3, BnsInitialData::Tags::RotationalShift<DataVector>>,\n      BnsInitialData::Tags::EulerEnthalpyConstant>>(\n      DirectionMap<3, tnsr::I<DataVector, 3>>{{direction, x}},\n      DirectionMap<3, tnsr::i<DataVector, 3>>{{direction, normal}},\n      DirectionMap<3, Scalar<DataVector>>{{direction, lapse}},\n      DirectionMap<3, tnsr::I<DataVector, 3>>{{direction, rotational_shift}},\n      euler_enthalpy_constant);\n  elliptic::apply_boundary_condition<false, void, tmpl::list<StarSurface>>(\n      boundary_condition, box, Direction<3>::lower_xi(),\n      make_not_null(&velocity_potential), n_dot_auxilliary_velocity,\n      velocity_potential_gradient);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.BnsInitialData.BoundaryConditions.StarSurface\",\n                  \"[Unit][Elliptic]\") {\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<3>, StarSurface>(\n      \"StarSurface:\");\n  REQUIRE(dynamic_cast<const StarSurface*>(created.get()) != nullptr);\n  const auto& boundary_condition = dynamic_cast<const StarSurface&>(*created);\n  {\n    {\n      INFO(\"Semantics\");\n      test_serialization(boundary_condition);\n      test_copy_semantics(boundary_condition);\n      auto move_boundary_condition = boundary_condition;\n      test_move_semantics(std::move(move_boundary_condition),\n                          boundary_condition);\n    }\n    {\n      Scalar<DataVector> n_dot_auxilliary_velocity{};\n      Scalar<DataVector> n_dot_auxilliary_velocity_for_linearized{};\n      {\n        apply_star_surface_boundary_condition_specific<true>(\n            make_not_null(&n_dot_auxilliary_velocity_for_linearized));\n        CHECK(get(n_dot_auxilliary_velocity_for_linearized) ==\n              DataVector{3, 0.0});\n      }\n      {\n        Scalar<DataVector> velocity_potential{DataVector{1.0, 2.0, 3.0}};\n        const tnsr::i<DataVector, 3> velocity_potential_gradient{\n            {DataVector{1.0, 1.0, 1.0}, DataVector{0.0, 0.0, 0.0},\n             DataVector{0.0, 0.0, 0.0}}};\n        const Scalar<DataVector> lapse{DataVector{1.0, 1.5, 1.2}};\n        const tnsr::I<DataVector, 3> rotational_shift{\n            {DataVector{1.0, 1.0, 1.0}, DataVector{0.0, 0.0, 0.0},\n             DataVector{0.0, 0.0, 0.0}}};\n        const tnsr::i<DataVector, 3> normal{{DataVector{1.0, 0.0, 0.0},\n                                             DataVector{0.0, 1.0, 0.0},\n                                             DataVector{0.0, 0.0, 1.0}}};\n        const double euler_enthalpy_constant = 1.0;\n        apply_star_surface_boundary_condition_specific<false>(\n            make_not_null(&n_dot_auxilliary_velocity));\n        CHECK(get(n_dot_auxilliary_velocity) ==\n              euler_enthalpy_constant / square(get(lapse)) *\n                  (rotational_shift.get(0) * normal.get(0) +\n                   rotational_shift.get(1) * normal.get(1) +\n                   rotational_shift.get(2) * normal.get(2)));\n      }\n    }\n    {\n      // Compare to python implementation\n      pypp::SetupLocalPythonEnvironment local_python_env(\n          \"Elliptic/Systems/BnsInitialData/BoundaryConditions/\");\n      pypp::check_with_random_values<6>(\n          &apply_star_surface_boundary_condition_generic, \"StarSurface\",\n          {\"star_surface_normal_dot_flux\"},\n          {{{0.0, 1.0},\n            {0.0, 1.0},\n            {.9, 1.1},\n            {-0.5, 0.5},\n            {0.0, 0.1},\n            {0.0, 1.5}}},\n          DataVector{3});\n    }\n  }\n}\n}  // namespace BnsInitialData::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/BnsInitialData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_BnsInitialData\")\n\nset(LIBRARY_SOURCES\n  Test_Equations.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  EllipticTestHelpers\n  BnsInitialData\n  Utilities\n  )\n\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/BnsInitialData/Equations.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef potential_fluxes(\n    rotational_shift_stress, inv_spatial_metric, auxiliary_velocity\n):\n    return np.einsum(\n        \"ij,j\", inv_spatial_metric - rotational_shift_stress, auxiliary_velocity\n    )\n\n\ndef fluxes_on_face(\n    face_normal, face_normal_vector, rotational_shift_stress, velocity_potential\n):\n    return velocity_potential * (\n        face_normal_vector\n        - np.einsum(\"ij,j\", rotational_shift_stress, face_normal)\n    )\n\n\ndef add_potential_sources(\n    log_deriv_lapse_over_specific_enthalpy,\n    christoffel_contracted,\n    flux_for_potential,\n):\n    return -np.einsum(\n        \"i,i\",\n        christoffel_contracted + log_deriv_lapse_over_specific_enthalpy,\n        flux_for_potential,\n    )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/BnsInitialData/Test_Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/BnsInitialData/Equations.hpp\"\n#include \"Elliptic/Systems/BnsInitialData/FirstOrderSystem.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"Helpers/Elliptic/FirstOrderSystem.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace helpers = TestHelpers::elliptic;\n\nnamespace {\nvoid test_equations(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(&BnsInitialData::potential_fluxes,\n                                    \"Equations\", {\"potential_fluxes\"},\n                                    {{{0., 1.}}}, used_for_size);\n  pypp::check_with_random_values<1>(&BnsInitialData::fluxes_on_face,\n                                    \"Equations\", {\"fluxes_on_face\"},\n                                    {{{0., 1.}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      &BnsInitialData::add_potential_sources, \"Equations\",\n      {\"add_potential_sources\"}, {{{0., 1.}}}, used_for_size, 1.e-12, {}, 0.);\n}\n\nvoid test_computers(const DataVector& used_for_size) {\n  using system = BnsInitialData::FirstOrderSystem;\n  static_assert(\n      tt::assert_conforms_to_v<system, elliptic::protocols::FirstOrderSystem>);\n  helpers::test_first_order_fluxes_computer<system>(used_for_size);\n  helpers::test_first_order_sources_computer<system>(used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.BnsInitialData\", \"[Unit][Elliptic]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Elliptic/Systems/BnsInitialData\"};\n\n  DataVector used_for_size{5};\n  test_equations(used_for_size);\n  test_computers(used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/BnsInitialData/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Systems/BnsInitialData/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.BnsInitialData.Tags\",\n                  \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<\n      BnsInitialData::Tags::VelocityPotential<DataVector>>(\n      std::string{\"VelocityPotential\"});\n  TestHelpers::db::test_simple_tag<\n      BnsInitialData::Tags::RotationalShift<DataVector>>(\n      std::string{\"RotationalShift\"});\n  TestHelpers::db::test_simple_tag<\n      BnsInitialData::Tags::RotationalShiftStress<DataVector>>(\n      std::string{\"RotationalShiftStress\"});\n  TestHelpers::db::test_simple_tag<\n      BnsInitialData::Tags::DerivLogLapseTimesDensityOverSpecificEnthalpy<\n          DataVector>>(\n      std::string{\"DerivLogLapseTimesDensityOverSpecificEnthalpy\"});\n  TestHelpers::db::test_simple_tag<\n      BnsInitialData::Tags::SpatialRotationalKillingVector<DataVector>>(\n      std::string{\"SpatialRotationalKillingVector\"});\n  TestHelpers::db::test_simple_tag<\n      BnsInitialData::Tags::DerivSpatialRotationalKillingVector<DataVector>>(\n      std::string{\"DerivSpatialRotationalKillingVector\"});\n  TestHelpers::db::test_simple_tag<BnsInitialData::Tags::EulerEnthalpyConstant>(\n      std::string{\"EulerEnthalpyConstant\"});\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticSystems\")\n\nset(LIBRARY_SOURCES\n  Test_GetSourcesComputer.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  EllipticSystems\n  )\n\nadd_subdirectory(Elasticity)\nadd_subdirectory(BnsInitialData)\nadd_subdirectory(Poisson)\nadd_subdirectory(Punctures)\nadd_subdirectory(ScalarGaussBonnet)\nadd_subdirectory(SelfForce)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ElasticityActions\")\n\nset(LIBRARY_SOURCES\n  Test_InitializeConstitutiveRelation.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstitutiveRelations\n  DataStructures\n  Domain\n  ElasticityActions\n  Elliptic\n  Elasticity\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/Actions/Test_InitializeConstitutiveRelation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Creators/Cylinder.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Elasticity/Actions/InitializeConstitutiveRelation.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Factory.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity {\nnamespace {\nconstexpr size_t Dim = 3;\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using const_global_cache_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<\n              domain::Tags::Element<Dim>, amr::Tags::ChildIds<Dim>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::InitializeConstitutiveRelation<Dim>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<ElementArray<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<Dim>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        ConstitutiveRelations::ConstitutiveRelation<Dim>,\n        ConstitutiveRelations::standard_constitutive_relations<Dim>>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elasticity.Actions.InitializeConstitutiveRelation\",\n                  \"[Unit][Elliptic][Actions]\") {\n  domain::creators::register_derived_with_charm();\n  register_factory_classes_with_charm<Metavariables>();\n  // A cylinder with two layers in z-direction\n  const std::unique_ptr<DomainCreator<Dim>> domain_creator =\n      std::make_unique<domain::creators::Cylinder>(\n          1., 3., 0., 10., false, 1_st, 3_st, false, std::vector<double>{},\n          std::vector<double>{2.},\n          std::vector<domain::CoordinateMaps::Distribution>{\n              domain::CoordinateMaps::Distribution::Linear},\n          std::vector<domain::CoordinateMaps::Distribution>{\n              domain::CoordinateMaps::Distribution::Linear,\n              domain::CoordinateMaps::Distribution::Linear});\n  const ElementId<Dim> element_id_layer1{0, {{{1, 0}, {1, 0}, {1, 0}}}};\n  const ElementId<Dim> element_id_layer2{5, {{{1, 0}, {1, 0}, {1, 0}}}};\n  // A different material in each layer\n  using ConstRelPtr =\n      std::unique_ptr<ConstitutiveRelations::ConstitutiveRelation<Dim>>;\n  std::unordered_map<std::string, ConstRelPtr> material_layers{};\n  material_layers[\"Layer0\"] =\n      std::make_unique<ConstitutiveRelations::IsotropicHomogeneous<Dim>>(1.,\n                                                                         2.);\n  material_layers[\"Layer1\"] =\n      std::make_unique<ConstitutiveRelations::IsotropicHomogeneous<Dim>>(3.,\n                                                                         4.);\n  const typename OptionTags::ConstitutiveRelationPerBlock<Dim>::type\n      material_layers_variant(std::move(material_layers));\n\n  auto material_per_block =\n      Tags::ConstitutiveRelationPerBlock<Dim>::create_from_options(\n          domain_creator, material_layers_variant);\n  auto material_block_groups =\n      Tags::MaterialBlockGroups<Dim>::create_from_options(\n          material_layers_variant);\n  REQUIRE(material_block_groups ==\n          std::unordered_set<std::string>{\"Layer0\", \"Layer1\"});\n\n  using element_array = ElementArray<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      tuples::TaggedTuple<domain::Tags::Domain<Dim>,\n                          Tags::ConstitutiveRelationPerBlock<Dim>,\n                          Tags::MaterialBlockGroups<Dim>>{\n          domain_creator->create_domain(), std::move(material_per_block),\n          std::move(material_block_groups)}};\n  for (const auto& element_id : {element_id_layer1, element_id_layer2}) {\n    ActionTesting::emplace_component_and_initialize<element_array>(\n        &runner, element_id,\n        {Element<Dim>{element_id, {}}, std::unordered_set<ElementId<Dim>>{}});\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  for (const auto& element_id : {element_id_layer1, element_id_layer2}) {\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              element_id);\n  }\n\n  const auto get_tag =\n      [&runner](auto tag_v, const ElementId<Dim>& element_id) -> const auto& {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  const auto check_material = [&get_tag](const ElementId<Dim>& element_id,\n                                         const double expected_bulk_modulus) {\n    const auto& material =\n        dynamic_cast<const ConstitutiveRelations::IsotropicHomogeneous<Dim>&>(\n            get_tag(Tags::ConstitutiveRelation<Dim>{}, element_id));\n    CHECK(material.bulk_modulus() == expected_bulk_modulus);\n  };\n\n  check_material(element_id_layer1, 1.);\n  check_material(element_id_layer2, 3.);\n}\n}  // namespace Elasticity\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ElasticityBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_LaserBeam.cpp\n  Test_Zero.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainStructure\n  ElasticityBoundaryConditions\n  ElasticitySolutions\n  Elliptic\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/BoundaryConditions/LaserBeam.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom numpy import exp, pi, sqrt\n\n\ndef normal_dot_minus_stress(x, n, beam_width):\n    n /= np.linalg.norm(n)\n    r = sqrt(np.linalg.norm(x) ** 2 - np.dot(x, n) ** 2)\n    beam_profile = exp(-((r / beam_width) ** 2)) / pi / beam_width**2\n    return np.tensordot(-n, beam_profile, axes=0)\n\n\ndef normal_dot_minus_stress_linearized(x, n, beam_width):\n    return np.zeros(3)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/BoundaryConditions/Test_LaserBeam.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Elasticity/BoundaryConditions/LaserBeam.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/HalfSpaceMirror.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity::BoundaryConditions {\n\nnamespace {\ntemplate <bool Linearized>\nvoid apply_boundary_condition(\n    const gsl::not_null<tnsr::I<DataVector, 3>*> n_dot_minus_stress,\n    const tnsr::I<DataVector, 3>& x, tnsr::i<DataVector, 3> face_normal,\n    const double beam_width) {\n  const LaserBeam laser_beam{beam_width};\n  const auto direction = Direction<3>::lower_xi();\n  // Normalize the randomly-generated face normal\n  const auto face_normal_magnitude = magnitude(face_normal);\n  for (size_t d = 0; d < 3; ++d) {\n    face_normal.get(d) /= get(face_normal_magnitude);\n  }\n  const auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Faces<3, domain::Tags::Coordinates<3, Frame::Inertial>>,\n      domain::Tags::Faces<3, domain::Tags::FaceNormal<3>>>>(\n      DirectionMap<3, tnsr::I<DataVector, 3>>{{direction, x}},\n      DirectionMap<3, tnsr::i<DataVector, 3>>{{direction, face_normal}});\n  tnsr::I<DataVector, 3> displacement{\n      x.begin()->size(), std::numeric_limits<double>::signaling_NaN()};\n  tnsr::iJ<DataVector, 3> deriv_displacement{\n      x.begin()->size(), std::numeric_limits<double>::signaling_NaN()};\n  elliptic::apply_boundary_condition<Linearized, void, tmpl::list<LaserBeam>>(\n      laser_beam, box, direction, make_not_null(&displacement),\n      n_dot_minus_stress, deriv_displacement);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elasticity.BoundaryConditions.LaserBeam\",\n                  \"[Unit][Elliptic]\") {\n  const double beam_width = 2.;\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<3>, LaserBeam>(\n      \"LaserBeam:\\n\"\n      \"  BeamWidth: 2.\");\n  REQUIRE(dynamic_cast<const LaserBeam*>(created.get()) != nullptr);\n  const auto& laser_beam = dynamic_cast<const LaserBeam&>(*created);\n\n  {\n    INFO(\"Semantics\");\n    test_serialization(laser_beam);\n    test_copy_semantics(laser_beam);\n    auto move_laser_beam = laser_beam;\n    test_move_semantics(std::move(move_laser_beam), laser_beam);\n  }\n  {\n    INFO(\"Properties\");\n    CHECK(laser_beam.beam_width() == beam_width);\n    CHECK(laser_beam.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              3, elliptic::BoundaryConditionType::Neumann});\n  }\n\n  // Test applying the boundary conditions\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"Elliptic/Systems/Elasticity/BoundaryConditions/\");\n  pypp::check_with_random_values<3>(&apply_boundary_condition<false>,\n                                    \"LaserBeam\", {\"normal_dot_minus_stress\"},\n                                    {{{-2., 2.}, {-1., 1.}, {0.5, 2.}}},\n                                    DataVector{3});\n  pypp::check_with_random_values<3>(\n      &apply_boundary_condition<true>, \"LaserBeam\",\n      {\"normal_dot_minus_stress_linearized\"},\n      {{{-2., 2.}, {-1., 1.}, {0.5, 2.}}}, DataVector{3});\n\n  {\n    INFO(\"Consistency with half-space mirror solution\");\n    const DataVector used_for_size{5};\n    // Choose an arbitrary set of points on the z=0 surface of the mirror\n    MAKE_GENERATOR(generator);\n    std::uniform_real_distribution<> dist(-2. * beam_width, 2. * beam_width);\n    auto x = make_with_random_values<tnsr::I<DataVector, 3>>(\n        make_not_null(&generator), make_not_null(&dist), used_for_size);\n    get<2>(x) = 0.;\n    // Choose a constitutive relation with arbitrary parameters\n    const ConstitutiveRelations::IsotropicHomogeneous<3> constitutive_relation{\n        1., 2.};\n    // Get the surface stress from the half-space mirror solution.\n    // Disable relative tolerance because we compare with an absolute tolerance\n    // in the test below.\n    const Solutions::HalfSpaceMirror half_space_mirror{\n        beam_width, constitutive_relation, 350, 1e-12, 0.};\n    const auto minus_stress_solution = get<Tags::MinusStress<3>>(\n        half_space_mirror.variables(x, tmpl::list<Tags::MinusStress<3>>{}));\n    // Get the stress normal to the surface. The solution assumes the material\n    // extends in positive z-direction, so the normal is (0, 0, -1)\n    tnsr::I<DataVector, 3> n_dot_minus_stress_solution{used_for_size.size()};\n    get<0>(n_dot_minus_stress_solution) = -get<2, 0>(minus_stress_solution);\n    get<1>(n_dot_minus_stress_solution) = -get<2, 1>(minus_stress_solution);\n    get<2>(n_dot_minus_stress_solution) = -get<2, 2>(minus_stress_solution);\n    auto face_normal = make_with_value<tnsr::i<DataVector, 3>>(x, 0.);\n    get<2>(face_normal) = -1;\n    // Shift the plane where we evaluate the boundary condition along the\n    // z-direction, just because it shouldn't affect the result and it might\n    // catch issues with computing the coordinate distance\n    get<2>(x) += 2.;\n    // Compare to the boundary conditions\n    tnsr::I<DataVector, 3> displacement{\n        used_for_size.size(), std::numeric_limits<double>::signaling_NaN()};\n    tnsr::I<DataVector, 3> n_dot_minus_stress{\n        used_for_size.size(), std::numeric_limits<double>::signaling_NaN()};\n    const tnsr::iJ<DataVector, 3> deriv_displacement{\n        used_for_size.size(), std::numeric_limits<double>::signaling_NaN()};\n    laser_beam.apply(make_not_null(&displacement),\n                     make_not_null(&n_dot_minus_stress), deriv_displacement, x,\n                     face_normal);\n    for (size_t d = 0; d < 3; ++d) {\n      CAPTURE(d);\n      CHECK_ITERABLE_APPROX(n_dot_minus_stress.get(d),\n                            n_dot_minus_stress_solution.get(d));\n    }\n  }\n}\n\n}  // namespace Elasticity::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/BoundaryConditions/Test_Zero.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Elasticity/BoundaryConditions/Zero.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Elasticity::BoundaryConditions {\n\nSPECTRE_TEST_CASE(\"Unit.Elasticity.BoundaryConditions.Zero\",\n                  \"[Unit][Elliptic]\") {\n  // Test factory-creation\n  using Fixed = Zero<2, elliptic::BoundaryConditionType::Dirichlet>;\n  using Free = Zero<2, elliptic::BoundaryConditionType::Neumann>;\n  const auto created_fixed = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<2>, Fixed>(\"Fixed\");\n  const auto created_free = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<2>, Free>(\"Free\");\n\n  // Test semantics\n  REQUIRE(dynamic_cast<const Fixed*>(created_fixed.get()) != nullptr);\n  REQUIRE(dynamic_cast<const Free*>(created_free.get()) != nullptr);\n  const auto& fixed = dynamic_cast<const Fixed&>(*created_fixed);\n  const auto& free = dynamic_cast<const Free&>(*created_free);\n  {\n    INFO(\"Semantics\");\n    test_serialization(fixed);\n    test_serialization(free);\n    test_copy_semantics(fixed);\n    test_copy_semantics(free);\n    auto move_fixed = fixed;\n    auto move_free = free;\n    test_move_semantics(std::move(move_fixed), fixed);\n    test_move_semantics(std::move(move_free), free);\n  }\n  {\n    INFO(\"Properties\");\n    CHECK(fixed.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              2, elliptic::BoundaryConditionType::Dirichlet});\n    CHECK(free.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              2, elliptic::BoundaryConditionType::Neumann});\n  }\n\n  // Test applying the boundary conditions\n  const auto box = db::create<db::AddSimpleTags<>>();\n  {\n    INFO(\"Dirichlet\");\n    tnsr::I<DataVector, 2> displacement{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::I<DataVector, 2> n_dot_minus_stress{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::iJ<DataVector, 2> deriv_displacement{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    elliptic::apply_boundary_condition<false, void, tmpl::list<Fixed>>(\n        fixed, box, Direction<2>::lower_xi(), make_not_null(&displacement),\n        make_not_null(&n_dot_minus_stress), deriv_displacement);\n    CHECK_ITERABLE_APPROX(get<0>(displacement), SINGLE_ARG(DataVector{3, 0.}));\n    CHECK_ITERABLE_APPROX(get<1>(displacement), SINGLE_ARG(DataVector{3, 0.}));\n  }\n  {\n    INFO(\"Linearized Dirichlet\");\n    tnsr::I<DataVector, 2> displacement{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::I<DataVector, 2> n_dot_minus_stress{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::iJ<DataVector, 2> deriv_displacement{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    elliptic::apply_boundary_condition<true, void, tmpl::list<Fixed>>(\n        fixed, box, Direction<2>::lower_xi(), make_not_null(&displacement),\n        make_not_null(&n_dot_minus_stress), deriv_displacement);\n    CHECK_ITERABLE_APPROX(get<0>(displacement), SINGLE_ARG(DataVector{3, 0.}));\n    CHECK_ITERABLE_APPROX(get<1>(displacement), SINGLE_ARG(DataVector{3, 0.}));\n  }\n  {\n    INFO(\"Neumann\");\n    tnsr::I<DataVector, 2> displacement{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::I<DataVector, 2> n_dot_minus_stress{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::iJ<DataVector, 2> deriv_displacement{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    elliptic::apply_boundary_condition<false, void, tmpl::list<Free>>(\n        free, box, Direction<2>::lower_xi(), make_not_null(&displacement),\n        make_not_null(&n_dot_minus_stress), deriv_displacement);\n    CHECK_ITERABLE_APPROX(get<0>(n_dot_minus_stress),\n                          SINGLE_ARG(DataVector{3, 0.}));\n    CHECK_ITERABLE_APPROX(get<1>(n_dot_minus_stress),\n                          SINGLE_ARG(DataVector{3, 0.}));\n  }\n  {\n    INFO(\"Linearized Neumann\");\n    tnsr::I<DataVector, 2> displacement{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::I<DataVector, 2> n_dot_minus_stress{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::iJ<DataVector, 2> deriv_displacement{\n        3_st, std::numeric_limits<double>::signaling_NaN()};\n    elliptic::apply_boundary_condition<true, void, tmpl::list<Free>>(\n        free, box, Direction<2>::lower_xi(), make_not_null(&displacement),\n        make_not_null(&n_dot_minus_stress), deriv_displacement);\n    CHECK_ITERABLE_APPROX(get<0>(n_dot_minus_stress),\n                          SINGLE_ARG(DataVector{3, 0.}));\n    CHECK_ITERABLE_APPROX(get<1>(n_dot_minus_stress),\n                          SINGLE_ARG(DataVector{3, 0.}));\n  }\n}\n\n}  // namespace Elasticity::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Elasticity\")\n\nset(LIBRARY_SOURCES\n  Test_Equations.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstitutiveRelations\n  DataStructures\n  Domain\n  Elliptic\n  Elasticity\n  LinearOperators\n  Utilities\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/Equations.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef constitutive_relation_2d(strain, bulk_modulus, shear_modulus):\n    lame_constant = bulk_modulus - 2.0 / 3.0 * shear_modulus\n    return (\n        -2.0\n        * shear_modulus\n        * lame_constant\n        / (lame_constant + 2.0 * shear_modulus)\n        * np.trace(strain)\n        * np.eye(2)\n        - 2.0 * shear_modulus * strain\n    )\n\n\ndef constitutive_relation_3d(strain, bulk_modulus, shear_modulus):\n    lame_constant = bulk_modulus - 2.0 / 3.0 * shear_modulus\n    return -2.0 * shear_modulus * strain - lame_constant * np.trace(\n        strain\n    ) * np.eye(3)\n\n\ndef primal_fluxes_2d(\n    deriv_displacement, coordinates, bulk_modulus, shear_modulus\n):\n    strain = 0.5 * (deriv_displacement + deriv_displacement.T)\n    return -constitutive_relation_2d(strain, bulk_modulus, shear_modulus)\n\n\ndef primal_fluxes_3d(\n    deriv_displacement, coordinates, bulk_modulus, shear_modulus\n):\n    strain = 0.5 * (deriv_displacement + deriv_displacement.T)\n    return -constitutive_relation_3d(strain, bulk_modulus, shear_modulus)\n\n\ndef add_curved_sources(christoffel_second_kind, christoffel_contracted, stress):\n    return -np.einsum(\"i,ij\", christoffel_contracted, stress) - np.einsum(\n        \"ijk,jk\", christoffel_second_kind, stress\n    )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/Test_Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Elasticity/Actions/InitializeConstitutiveRelation.hpp\"\n#include \"Elliptic/Systems/Elasticity/Equations.hpp\"\n#include \"Elliptic/Systems/Elasticity/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid primal_fluxes(\n    // This wrapper function is needed to construct a constitutive relation for\n    // the random values of its parameters.\n    const gsl::not_null<tnsr::II<DataVector, Dim, Frame::Inertial>*>\n        minus_stress,\n    const tnsr::iJ<DataVector, Dim, Frame::Inertial>& deriv_displacement,\n    const tnsr::I<DataVector, Dim>& coordinates, const double bulk_modulus,\n    const double shear_modulus) {\n  Elasticity::ConstitutiveRelations::IsotropicHomogeneous<Dim>\n      constitutive_relation{bulk_modulus, shear_modulus};\n  Elasticity::primal_fluxes<Dim>(minus_stress, deriv_displacement,\n                                 std::move(constitutive_relation), coordinates);\n}\n\ntemplate <size_t Dim>\nvoid test_equations(const DataVector& used_for_size) {\n  pypp::check_with_random_values<4>(\n      &primal_fluxes<Dim>, \"Equations\",\n      {MakeString{} << \"primal_fluxes_\" << Dim << \"d\"},\n      {{{-1., 1.}, {-1., 1.}, {0., 1.}, {0., 1.}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      &Elasticity::add_curved_sources<Dim>, \"Equations\", {\"add_curved_sources\"},\n      {{{-1., 1.}}}, used_for_size, 1.e-12, {}, 0.);\n}\n\ntemplate <size_t Dim>\nvoid test_computers(const DataVector& used_for_size) {\n  static_assert(\n      tt::assert_conforms_to_v<Elasticity::FirstOrderSystem<Dim>,\n                               elliptic::protocols::FirstOrderSystem>);\n  using field_tag = Elasticity::Tags::Displacement<Dim>;\n  using deriv_field_tag = ::Tags::deriv<Elasticity::Tags::Displacement<Dim>,\n                                        tmpl::size_t<Dim>, Frame::Inertial>;\n  using field_flux_tag = Elasticity::Tags::MinusStress<Dim>;\n  using constitutive_relation_tag =\n      Elasticity::Tags::ConstitutiveRelationPerBlock<Dim>;\n  using coordinates_tag = domain::Tags::Coordinates<Dim, Frame::Inertial>;\n  const size_t num_points = used_for_size.size();\n  {\n    INFO(\"Fluxes\" << Dim << \"D\");\n    const Elasticity::ConstitutiveRelations::IsotropicHomogeneous<Dim>\n        constitutive_relation{1., 2.};\n    std::vector<std::unique_ptr<\n        Elasticity::ConstitutiveRelations::ConstitutiveRelation<Dim>>>\n        constitutive_relations{};\n    constitutive_relations.push_back(constitutive_relation.get_clone());\n    auto box = db::create<\n        db::AddSimpleTags<field_tag, deriv_field_tag, field_flux_tag,\n                          constitutive_relation_tag, coordinates_tag>>(\n        tnsr::I<DataVector, Dim>{num_points, 1.},\n        tnsr::iJ<DataVector, Dim>{num_points, 2.},\n        tnsr::II<DataVector, Dim>{num_points,\n                                  std::numeric_limits<double>::signaling_NaN()},\n        std::move(constitutive_relations),\n        tnsr::I<DataVector, Dim>{num_points, 6.});\n\n    const Elasticity::Fluxes<Dim> fluxes_computer{};\n    using argument_tags = typename Elasticity::Fluxes<Dim>::argument_tags;\n\n    db::mutate_apply<tmpl::list<field_flux_tag>, argument_tags>(\n        fluxes_computer, make_not_null(&box), ElementId<Dim>{0},\n        get<field_tag>(box), get<deriv_field_tag>(box));\n    auto expected_field_flux = tnsr::II<DataVector, Dim>{\n        num_points, std::numeric_limits<double>::signaling_NaN()};\n    Elasticity::primal_fluxes(make_not_null(&expected_field_flux),\n                              get<deriv_field_tag>(box), constitutive_relation,\n                              get<coordinates_tag>(box));\n    CHECK(get<field_flux_tag>(box) == expected_field_flux);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Elasticity\", \"[Unit][Elliptic]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Elliptic/Systems/Elasticity\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_equations, (2, 3));\n  CHECK_FOR_DATAVECTORS(test_computers, (2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Elasticity/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Elasticity.Tags\", \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<Elasticity::Tags::Displacement<1>>(\n      \"Displacement\");\n  TestHelpers::db::test_simple_tag<Elasticity::Tags::Strain<1>>(\"Strain\");\n  TestHelpers::db::test_simple_tag<Elasticity::Tags::Stress<1>>(\"Stress\");\n  TestHelpers::db::test_simple_tag<Elasticity::Tags::MinusStress<1>>(\n      \"MinusStress\");\n  TestHelpers::db::test_simple_tag<Elasticity::Tags::PotentialEnergyDensity<1>>(\n      \"PotentialEnergyDensity\");\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Poisson/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_PoissonBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_Robin.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainStructure\n  Elliptic\n  PoissonBoundaryConditions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Poisson/BoundaryConditions/Robin.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef dirichlet_field(dirichlet_weight, constant, used_for_size):\n    return constant / dirichlet_weight\n\n\ndef dirichlet_field_linearized(dirichlet_weight, constant, used_for_size):\n    return 0.0\n\n\ndef neumann_normal_dot_field_gradient(\n    field, dirichlet_weight, neumann_weight, constant\n):\n    return (constant - dirichlet_weight * field) / neumann_weight\n\n\ndef neumann_normal_dot_field_gradient_linearized(\n    field_correction, dirichlet_weight, neumann_weight, constant\n):\n    return -dirichlet_weight / neumann_weight * field_correction\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Poisson/BoundaryConditions/Test_Robin.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Poisson/BoundaryConditions/Robin.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::BoundaryConditions {\n\nnamespace {\ntemplate <bool Linearized>\nvoid apply_dirichlet_boundary_condition(\n    const gsl::not_null<Scalar<DataVector>*> field,\n    const double dirichlet_weight, const double constant,\n    const Scalar<DataVector>& used_for_size) {\n  const Robin<1> boundary_condition{dirichlet_weight, 0., constant};\n  const auto box = db::create<db::AddSimpleTags<>>();\n  auto n_dot_field_gradient = make_with_value<Scalar<DataVector>>(\n      *field, std::numeric_limits<double>::signaling_NaN());\n  const tnsr::i<DataVector, 1> field_gradient{\n      used_for_size.begin()->size(),\n      std::numeric_limits<double>::signaling_NaN()};\n  elliptic::apply_boundary_condition<Linearized, void, tmpl::list<Robin<1>>>(\n      boundary_condition, box, Direction<1>::lower_xi(), field,\n      make_not_null(&n_dot_field_gradient), field_gradient);\n}\ntemplate <bool Linearized>\nvoid apply_neumann_boundary_condition(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient,\n    Scalar<DataVector> field, const double dirichlet_weight,\n    const double neumann_weight, const double constant) {\n  const Robin<1> boundary_condition{dirichlet_weight, neumann_weight, constant};\n  const auto box = db::create<db::AddSimpleTags<>>();\n  const tnsr::i<DataVector, 1> field_gradient{\n      field.begin()->size(), std::numeric_limits<double>::signaling_NaN()};\n  elliptic::apply_boundary_condition<Linearized, void, tmpl::list<Robin<1>>>(\n      boundary_condition, box, Direction<1>::lower_xi(), make_not_null(&field),\n      n_dot_field_gradient, field_gradient);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Poisson.BoundaryConditions.Robin\", \"[Unit][Elliptic]\") {\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<1>, Robin<1>>(\n      \"Robin:\\n\"\n      \"  DirichletWeight: 2.\\n\"\n      \"  NeumannWeight: 3.\\n\"\n      \"  Constant: 4.\");\n  REQUIRE(dynamic_cast<const Robin<1>*>(created.get()) != nullptr);\n  const auto& boundary_condition = dynamic_cast<const Robin<1>&>(*created);\n  {\n    INFO(\"Properties\");\n    CHECK(boundary_condition.dirichlet_weight() == 2.);\n    CHECK(boundary_condition.neumann_weight() == 3.);\n    CHECK(boundary_condition.constant() == 4.);\n    CHECK(boundary_condition.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              1, elliptic::BoundaryConditionType::Neumann});\n    Robin<2> dirichlet_type_robin{2., 0., 4.};\n    CHECK(dirichlet_type_robin.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              1, elliptic::BoundaryConditionType::Dirichlet});\n  }\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Random-value tests\");\n    pypp::SetupLocalPythonEnvironment local_python_env(\n        \"Elliptic/Systems/Poisson/BoundaryConditions/\");\n    pypp::check_with_random_values<3>(\n        &apply_dirichlet_boundary_condition<false>, \"Robin\",\n        {\"dirichlet_field\"}, {{{0.5, 2.}, {-1., 1.}, {-1., 1.}}},\n        DataVector{3});\n    pypp::check_with_random_values<3>(&apply_dirichlet_boundary_condition<true>,\n                                      \"Robin\", {\"dirichlet_field_linearized\"},\n                                      {{{0.5, 2.}, {-1., 1.}, {-1., 1.}}},\n                                      DataVector{3});\n    pypp::check_with_random_values<4>(\n        &apply_neumann_boundary_condition<false>, \"Robin\",\n        {\"neumann_normal_dot_field_gradient\"},\n        {{{-1., 1.}, {0.5, 2.}, {-1., 1.}, {-1., 1.}}}, DataVector{3});\n    pypp::check_with_random_values<4>(\n        &apply_neumann_boundary_condition<true>, \"Robin\",\n        {\"neumann_normal_dot_field_gradient_linearized\"},\n        {{{-1., 1.}, {0.5, 2.}, {-1., 1.}, {-1., 1.}}}, DataVector{3});\n  }\n}\n\n}  // namespace Poisson::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Poisson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Poisson\")\n\nset(LIBRARY_SOURCES\n  Test_Equations.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  EllipticTestHelpers\n  Poisson\n  Utilities\n  )\n\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Poisson/Equations.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef flat_cartesian_fluxes(field_gradient):\n    return field_gradient\n\n\ndef curved_fluxes(inv_spatial_metric, field_gradient):\n    return np.einsum(\"ij,j\", inv_spatial_metric, field_gradient)\n\n\ndef add_curved_sources(christoffel_contracted, field_flux):\n    return -np.einsum(\"i,i\", christoffel_contracted, field_flux)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Poisson/Test_Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Equations.hpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/Elliptic/FirstOrderSystem.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace helpers = TestHelpers::elliptic;\n\nnamespace {\n\ntemplate <typename DataType, size_t Dim>\nvoid test_equations(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      &Poisson::flat_cartesian_fluxes<DataType, Dim>, \"Equations\",\n      {\"flat_cartesian_fluxes\"}, {{{0., 1.}}}, used_for_size);\n  pypp::check_with_random_values<1>(&Poisson::curved_fluxes<DataType, Dim>,\n                                    \"Equations\", {\"curved_fluxes\"},\n                                    {{{0., 1.}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      &Poisson::add_curved_sources<DataType, Dim>, \"Equations\",\n      {\"add_curved_sources\"}, {{{0., 1.}}}, used_for_size, 1.e-12, {}, 0.);\n}\n\ntemplate <typename DataType, size_t Dim, Poisson::Geometry BackgroundGeometry>\nvoid test_computers(const DataVector& used_for_size) {\n  CAPTURE(Dim);\n  using system = Poisson::FirstOrderSystem<Dim, BackgroundGeometry, DataType>;\n  static_assert(\n      tt::assert_conforms_to_v<system, elliptic::protocols::FirstOrderSystem>);\n  helpers::test_first_order_fluxes_computer<system>(used_for_size);\n  helpers::test_first_order_sources_computer<system>(used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Poisson\", \"[Unit][Elliptic]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Elliptic/Systems/Poisson\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_equations, (DataVector, ComplexDataVector),\n                        (1, 2, 3));\n  CHECK_FOR_DATAVECTORS(\n      test_computers, (DataVector, ComplexDataVector), (1, 2, 3),\n      (Poisson::Geometry::FlatCartesian, Poisson::Geometry::Curved));\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Poisson/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Poisson.Tags\", \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<Poisson::Tags::Field<DataVector>>(\"Field\");\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/AmrCriteria/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_PuncturesAmrCriteria\")\n\nset(LIBRARY_SOURCES\n  Test_RefineAtPunctures.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Amr\n  AmrCriteria\n  DataStructures\n  DomainCreators\n  DomainStructure\n  Options\n  Parallel\n  PuncturesAmrCriteria\n  PuncturesAnalyticData\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/AmrCriteria/Test_RefineAtPunctures.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Elliptic/Systems/Punctures/AmrCriteria/RefineAtPunctures.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Punctures/MultiplePunctures.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures::AmrCriteria {\n\nnamespace {\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        amr::Criterion, tmpl::list<Punctures::AmrCriteria::RefineAtPunctures>>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Punctures.AmrCriteria.RefineAtPunctures\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto created =\n      TestHelpers::test_creation<std::unique_ptr<amr::Criterion>,\n                                 Metavariables>(\"RefineAtPunctures\");\n  REQUIRE(dynamic_cast<const RefineAtPunctures*>(created.get()) != nullptr);\n  const auto& criterion = serialize_and_deserialize(\n      dynamic_cast<const RefineAtPunctures&>(*created));\n\n  {\n    INFO(\"Evaluate\");\n    using background_tag =\n        elliptic::Tags::Background<elliptic::analytic_data::Background>;\n    using MultiplePunctures = Punctures::AnalyticData::MultiplePunctures;\n    using Puncture = Punctures::AnalyticData::Puncture;\n\n    const domain::creators::Brick domain_creator{{{0., 0., 0.}},\n                                                 {{1., 1., 1.}},\n                                                 {{2, 2, 2}},\n                                                 {{3, 3, 3}},\n                                                 {{false, false, false}}};\n    const Puncture puncture_within{// Position within an element\n                                   {{0.2, 0.2, 0.2}},\n                                   // Mass, spin, etc are irrelevant\n                                   1.,\n                                   {{0.1, 0.2, 0.3}},\n                                   {{0.4, 0.5, 0.6}}};\n    const Puncture puncture_boundary{// Position on an element boundary\n                                     {{0.5, 0.5, 0.5}},\n                                     // Mass, spin, etc are irrelevant\n                                     1.,\n                                     {{0.1, 0.2, 0.3}},\n                                     {{0.4, 0.5, 0.6}}};\n    auto databox =\n        db::create<tmpl::list<background_tag, domain::Tags::Domain<3>>>(\n            std::unique_ptr<elliptic::analytic_data::Background>(\n                std::make_unique<MultiplePunctures>(\n                    std::vector<Puncture>{puncture_within, puncture_boundary})),\n            domain_creator.create_domain());\n    ObservationBox<\n        tmpl::list<>,\n        db::DataBox<tmpl::list<background_tag, domain::Tags::Domain<3>>>>\n        box{make_not_null(&databox)};\n    Parallel::GlobalCache<Metavariables> empty_cache{};\n\n    {\n      INFO(\"Element with puncture within\");\n      const ElementId<3> element_id{0, {{{2, 0}, {2, 0}, {2, 0}}}};\n      const auto expected_flags = make_array<3>(amr::Flag::Split);\n      auto flags = criterion.evaluate(box, empty_cache, element_id);\n      CHECK(flags == expected_flags);\n    }\n    {\n      INFO(\"Elements with puncture on boundary\");\n      const auto expected_flags = make_array<3>(amr::Flag::Split);\n      for (const auto& element_id :\n           {ElementId<3>{0, {{{2, 1}, {2, 1}, {2, 1}}}},\n            ElementId<3>{0, {{{2, 2}, {2, 1}, {2, 1}}}},\n            ElementId<3>{0, {{{2, 1}, {2, 2}, {2, 1}}}},\n            ElementId<3>{0, {{{2, 2}, {2, 2}, {2, 1}}}},\n            ElementId<3>{0, {{{2, 1}, {2, 1}, {2, 2}}}},\n            ElementId<3>{0, {{{2, 2}, {2, 1}, {2, 2}}}},\n            ElementId<3>{0, {{{2, 1}, {2, 2}, {2, 2}}}},\n            ElementId<3>{0, {{{2, 2}, {2, 2}, {2, 2}}}}}) {\n        CAPTURE(element_id);\n        auto flags = criterion.evaluate(box, empty_cache, element_id);\n        CHECK(flags == expected_flags);\n      }\n    }\n    {\n      INFO(\"Elements without puncture\");\n      const auto expected_flags = make_array<3>(amr::Flag::DoNothing);\n      for (const auto& element_id :\n           {ElementId<3>{0, {{{2, 1}, {2, 0}, {2, 0}}}},\n            ElementId<3>{0, {{{2, 3}, {2, 3}, {2, 3}}}},\n            ElementId<3>{0, {{{2, 3}, {2, 0}, {2, 0}}}},\n            ElementId<3>{0, {{{2, 0}, {2, 3}, {2, 1}}}}}) {\n        auto flags = criterion.evaluate(box, empty_cache, element_id);\n        CHECK(flags == expected_flags);\n      }\n    }\n  }\n}\n\n}  // namespace Punctures::AmrCriteria\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_PuncturesBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_Flatness.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainStructure\n  Elliptic\n  PuncturesBoundaryConditions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/BoundaryConditions/Flatness.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef normal_dot_field_gradient(field, x):\n    return -field / np.linalg.norm(x)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/BoundaryConditions/Test_Flatness.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Punctures/BoundaryConditions/Flatness.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures::BoundaryConditions {\n\nnamespace {\ntemplate <bool Linearized>\nvoid apply_boundary_condition(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_field_gradient,\n    Scalar<DataVector> field, tnsr::I<DataVector, 3> x) {\n  const Flatness boundary_condition{};\n  const auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Faces<3, domain::Tags::Coordinates<3, Frame::Inertial>>>>(\n      DirectionMap<3, tnsr::I<DataVector, 3>>{\n          {Direction<3>::upper_xi(), std::move(x)}});\n  const tnsr::i<DataVector, 3> field_gradient{\n      field.begin()->size(), std::numeric_limits<double>::signaling_NaN()};\n  // grad(u) will be set by the boundary condition\n  elliptic::apply_boundary_condition<Linearized, void, tmpl::list<Flatness>>(\n      boundary_condition, box, Direction<3>::upper_xi(), make_not_null(&field),\n      n_dot_field_gradient, field_gradient);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Punctures.BoundaryConditions.Flatness\",\n                  \"[Unit][Elliptic]\") {\n  // Test factory-creation\n  const auto created =\n      TestHelpers::test_factory_creation<\n          elliptic::BoundaryConditions::BoundaryCondition<3>, Flatness>(\n          \"Flatness\")\n          ->get_clone();\n  REQUIRE(dynamic_cast<const Flatness*>(created.get()) != nullptr);\n  const auto& boundary_condition = dynamic_cast<const Flatness&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Properties\");\n    CHECK(boundary_condition.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              elliptic::BoundaryConditionType::Neumann});\n  }\n  {\n    INFO(\"Random-value tests\");\n    pypp::SetupLocalPythonEnvironment local_python_env(\n        \"Elliptic/Systems/Punctures/BoundaryConditions/\");\n    pypp::check_with_random_values<2>(&apply_boundary_condition<false>,\n                                      \"Flatness\", {\"normal_dot_field_gradient\"},\n                                      {{{-1., 1.}, {-1., 1.}}}, DataVector{3});\n    pypp::check_with_random_values<2>(&apply_boundary_condition<true>,\n                                      \"Flatness\", {\"normal_dot_field_gradient\"},\n                                      {{{-1., 1.}, {-1., 1.}}}, DataVector{3});\n  }\n}\n\n}  // namespace Punctures::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Punctures\")\n\nset(LIBRARY_SOURCES\n  Test_Equations.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructuresHelpers\n  Punctures\n  )\n\nadd_subdirectory(AmrCriteria)\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/Equations.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef sources(alpha, beta, field):\n    return -beta * (alpha * (1.0 + field) + 1.0) ** (-7)\n\n\ndef linearized_sources(alpha, beta, field, field_correction):\n    return (\n        7.0\n        * alpha\n        * beta\n        * (alpha * (1.0 + field) + 1.0) ** (-8)\n        * field_correction\n    )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/Test_Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Punctures/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Punctures/Sources.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/Elliptic/FirstOrderSystem.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\n\nvoid test_equations(const DataVector& used_for_size) {\n  const double eps = 1.e-12;\n  const auto seed = std::random_device{}();\n  const double fill_result_tensors = 0.;\n  pypp::check_with_random_values<3>(\n      &Punctures::add_sources, \"Equations\", {\"sources\"},\n      {{{-1., 1.}, {-1., 1.}, {-1., 1.}}}, used_for_size, eps, seed,\n      fill_result_tensors);\n  pypp::check_with_random_values<4>(\n      &Punctures::add_linearized_sources, \"Equations\", {\"linearized_sources\"},\n      {{{-1., 1.}, {-1., 1.}, {-1., 1.}, {-1., 1.}}}, used_for_size, eps, seed,\n      fill_result_tensors);\n}\n\nvoid test_computers(const DataVector& used_for_size) {\n  using system = Punctures::FirstOrderSystem;\n  static_assert(\n      tt::assert_conforms_to_v<system, elliptic::protocols::FirstOrderSystem>);\n  TestHelpers::elliptic::test_first_order_fluxes_computer<system>(\n      used_for_size);\n  TestHelpers::elliptic::test_first_order_sources_computer<system, false>(\n      used_for_size);\n  TestHelpers::elliptic::test_first_order_sources_computer<system, true>(\n      used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Punctures\", \"[Unit][Elliptic]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Elliptic/Systems/Punctures\"};\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  test_equations(dv);\n  test_computers(dv);\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Punctures/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Systems/Punctures/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace Punctures {\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Punctures.Tags\", \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<Tags::Field>(\"Field\");\n  TestHelpers::db::test_simple_tag<Tags::Alpha>(\"Alpha\");\n  TestHelpers::db::test_simple_tag<Tags::Beta>(\"Beta\");\n  TestHelpers::db::test_simple_tag<Tags::TracelessConformalExtrinsicCurvature>(\n      \"TracelessConformalExtrinsicCurvature\");\n}\n\n}  // namespace Punctures\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/ScalarGaussBonnet/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarGaussBonnetBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_DoNothing.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Elliptic\n  ScalarGaussBonnet\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/ScalarGaussBonnet/BoundaryConditions/Test_DoNothing.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/BoundaryConditions/DoNothing.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace sgb::BoundaryConditions {\n\nSPECTRE_TEST_CASE(\"Unit.ScalarGaussBonnet.BoundaryConditions.DoNothing\",\n                  \"[Unit][Elliptic]\") {\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<3>, DoNothing>(\n      \"DoNothing\");\n  REQUIRE(dynamic_cast<const DoNothing*>(created.get()) != nullptr);\n  const auto& boundary_condition = dynamic_cast<const DoNothing&>(*created);\n  // Test basic requirements for boundary conditions\n  test_serialization(boundary_condition);\n  test_copy_semantics(boundary_condition);\n  auto move_boundary_condition = boundary_condition;\n  test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n}\n}  // namespace sgb::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/ScalarGaussBonnet/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarGaussBonnet\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  Test_Equations.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  EllipticTestHelpers\n  ScalarGaussBonnet\n  Utilities\n  )\n\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/ScalarGaussBonnet/Equations.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef curved_fluxes(\n    inv_spatial_metric, shift, lapse, conformal_factor, field_gradient\n):\n    metric_term = np.einsum(\"ij,j\", inv_spatial_metric, field_gradient) / (\n        conformal_factor\n    ) ** (4)\n    shift_term = (\n        shift * np.einsum(\"i,i\", shift, field_gradient) / (lapse) ** (2)\n    )\n    return metric_term - shift_term\n\n\ndef face_fluxes(\n    inv_spatial_metric, shift, lapse, conformal_factor, face_normal, field\n):\n    metric_term = np.einsum(\"ij,j\", inv_spatial_metric, face_normal) / (\n        conformal_factor\n    ) ** (4)\n    shift_term = shift * np.einsum(\"i,i\", shift, face_normal) / (lapse) ** (2)\n    return (metric_term - shift_term) * field\n\n\ndef GB_source_term(\n    epsilon2,\n    epsilon4,\n    weyl_electric_scalar,\n    weyl_magnetic_scalar,\n    field,\n):\n    gauss_bonnet_scalar = 8 * (weyl_electric_scalar - weyl_magnetic_scalar)\n    coupling_function = (\n        epsilon2 * field / 4 + epsilon4 * field * field * field / 4\n    )\n    return -coupling_function * gauss_bonnet_scalar\n\n\ndef linearized_GB_source_term(\n    epsilon2,\n    epsilon4,\n    weyl_electric_scalar,\n    weyl_magnetic_scalar,\n    field,\n    field_correction,\n):\n    gauss_bonnet_scalar = 8 * (weyl_electric_scalar - weyl_magnetic_scalar)\n    linearized_coupling_function = (\n        epsilon2 * field_correction / 4\n        + 3 * epsilon4 * field * field * field_correction / 4\n    )\n    return -gauss_bonnet_scalar * linearized_coupling_function\n\n\ndef add_curved_sources(\n    conformal_christoffel_contracted,\n    field_flux,\n    deriv_lapse,\n    lapse,\n    conformal_factor,\n    conformal_factor_deriv,\n):\n    christoffel_term = -np.einsum(\n        \"i,i\", conformal_christoffel_contracted, field_flux\n    )\n    lapse_term = -np.einsum(\"i,i\", deriv_lapse, field_flux) / lapse\n    conformal_factor_term = (\n        -6\n        * np.einsum(\"i,i\", conformal_factor_deriv, field_flux)\n        / conformal_factor\n    )\n    return christoffel_term + lapse_term + conformal_factor_term\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/ScalarGaussBonnet/Test_Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/Equations.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/FirstOrderSystem.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/Elliptic/FirstOrderSystem.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace helpers = TestHelpers::elliptic;\n\nnamespace {\n\nvoid test_equations(const DataVector& used_for_size) {\n  const double eps = 1.e-12;\n  const auto seed = std::random_device{}();\n  const double fill_result_tensors = 0.;\n  pypp::check_with_random_values<1>(\n      &sgb::curved_fluxes, \"Equations\", {\"curved_fluxes\"}, {{{0., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<1>(\n      &sgb::face_fluxes, \"Equations\", {\"face_fluxes\"}, {{{0., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<1>(\n      &sgb::add_curved_sources, \"Equations\", {\"add_curved_sources\"},\n      {{{0., 1.}}}, used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<1>(\n      &sgb::add_GB_terms, \"Equations\", {\"GB_source_term\"}, {{{0., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<1>(\n      &sgb::add_linearized_GB_terms, \"Equations\", {\"linearized_GB_source_term\"},\n      {{{0., 1.}}}, used_for_size, eps, seed, fill_result_tensors);\n}\n\nvoid test_computers(const DataVector& used_for_size) {\n  using system = sgb::FirstOrderSystem;\n  static_assert(\n      tt::assert_conforms_to_v<system, elliptic::protocols::FirstOrderSystem>);\n  helpers::test_first_order_fluxes_computer<system>(used_for_size);\n  helpers::test_first_order_sources_computer<system>(used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.ScalarGaussBonnet\",\n                  \"[Unit][Elliptic]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Elliptic/Systems/ScalarGaussBonnet\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  test_equations(dv);\n  test_computers(dv);\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/ScalarGaussBonnet/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Systems/ScalarGaussBonnet/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.ScalarGaussBonnet.Tags\",\n                  \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<sgb::Tags::Epsilon2>(\"Epsilon2\");\n  TestHelpers::db::test_simple_tag<sgb::Tags::Epsilon4>(\"Epsilon4\");\n  TestHelpers::db::test_simple_tag<sgb::Tags::RolloffLocation>(\n      \"RolloffLocation\");\n  TestHelpers::db::test_simple_tag<sgb::Tags::RolloffRate>(\"RolloffRate\");\n  TestHelpers::db::test_simple_tag<sgb::Tags::Psi>(\"Psi\");\n  TestHelpers::db::test_simple_tag<sgb::Tags::RolledOffShift>(\"RolledOffShift\");\n  TestHelpers::db::test_simple_tag<sgb::Tags::PiWithRolledOffShift>(\n      \"PiWithRolledOffShift\");\n  CHECK(TestHelpers::test_option_tag<sgb::OptionTags::Epsilon2>(\"31.01\") ==\n        31.01);\n  CHECK(TestHelpers::test_option_tag<sgb::OptionTags::Epsilon4>(\"5.08\") ==\n        5.08);\n  CHECK(TestHelpers::test_option_tag<sgb::OptionTags::RolloffLocation>(\n            \"4.05\") == 4.05);\n  CHECK(TestHelpers::test_option_tag<sgb::OptionTags::RolloffRate>(\"28.1\") ==\n        28.1);\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(Scalar)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GrSelfForceActions\")\n\nset(LIBRARY_SOURCES\n  Test_InitializeEffectiveSource.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  Elliptic\n  EllipticDg\n  Options\n  Parallel\n  GrSelfForceActions\n  GrSelfForceAnalyticData\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/Actions/Test_InitializeEffectiveSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Actions/InitializeEffectiveSource.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/FirstOrderSystem.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::Actions {\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<2>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<2>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<\n              ActionTesting::InitializeDataBox<tmpl::list<\n                  domain::Tags::InitialRefinementLevels<2>,\n                  domain::Tags::InitialExtents<2>,\n                  ::Tags::Mortars<domain::Tags::Coordinates<2, Frame::Inertial>,\n                                  2>>>,\n              elliptic::dg::Actions::InitializeDomain<2>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<InitializeEffectiveSource<\n                                 GrSelfForce::FirstOrderSystem,\n                                 elliptic::Tags::Background<\n                                     elliptic::analytic_data::Background>>>>>;\n};\n\nstruct Metavariables {\n  using system = GrSelfForce::FirstOrderSystem;\n  using component_list = tmpl::list<ElementArray<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<elliptic::analytic_data::Background,\n                   tmpl::list<GrSelfForce::AnalyticData::CircularOrbit>>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GrSelfForce.Actions.InitializeEffectiveSource\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  using background_tag =\n      elliptic::Tags::Background<elliptic::analytic_data::Background>;\n  using element_array = ElementArray<Metavariables>;\n  using field_tag = GrSelfForce::Tags::MMode;\n\n  domain::creators::register_derived_with_charm();\n  register_factory_classes_with_charm<Metavariables>();\n  const domain::creators::AlignedLattice<2> domain_creator{\n      {{{-20., 0., 20.}, {0., M_PI}}}, {{2, 2}}, {{4, 4}}, {}, {}, {}};\n  const auto element_regularized =\n      ElementId<2>{1, {{SegmentId{2, 0}, SegmentId{2, 1}}}};\n  const auto element_unregularized =\n      ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{2, 1}}}};\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      tuples::TaggedTuple<background_tag, domain::Tags::Domain<2>,\n                          domain::Tags::FunctionsOfTimeInitialize,\n                          elliptic::dg::Tags::Massive,\n                          elliptic::dg::Tags::Quadrature>{\n          std::make_unique<GrSelfForce::AnalyticData::CircularOrbit>(1.0, 0.0,\n                                                                     6.0, 1),\n          domain_creator.create_domain(), domain_creator.functions_of_time(),\n          false, Spectral::Quadrature::GaussLobatto}};\n  const ::Tags::Mortars<domain::Tags::Coordinates<2, Frame::Inertial>, 2>::type\n      empty_mortar_coords{};\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      &runner, element_regularized,\n      {domain_creator.initial_refinement_levels(),\n       domain_creator.initial_extents(), empty_mortar_coords});\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      &runner, element_unregularized,\n      {domain_creator.initial_refinement_levels(),\n       domain_creator.initial_extents(), empty_mortar_coords});\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            element_regularized);\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            element_unregularized);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            element_regularized);\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            element_unregularized);\n\n  const auto get_tag = [&runner](const auto& element_id,\n                                 auto tag_v) -> const auto& {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n  const auto check_is_zero = [](const auto& components) {\n    return alg::all_of(components, [](const auto& value) {\n      return alg::all_of(value, [](const auto& v) { return v == 0.; });\n    });\n  };\n\n  CHECK(get_tag(element_regularized, Tags::FieldIsRegularized{}) == true);\n  // Values of the effective source and singular field are tested in\n  // `Test_CircularOrbit.cpp`.\n  CHECK_FALSE(check_is_zero(\n      get_tag(element_regularized, ::Tags::FixedSource<field_tag>{})));\n  CHECK_FALSE(\n      check_is_zero(get_tag(element_regularized, Tags::SingularField{})));\n\n  CHECK(get_tag(element_unregularized, Tags::FieldIsRegularized{}) == false);\n  CHECK(check_is_zero(\n      get_tag(element_unregularized, ::Tags::FixedSource<field_tag>{})));\n  CHECK(check_is_zero(get_tag(element_unregularized, Tags::SingularField{})));\n}\n\n}  // namespace GrSelfForce::Actions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GrSelfForceAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_CircularOrbit.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Utilities\n  GrSelfForce\n  GrSelfForceAnalyticData\n  Domain\n  DomainCreators\n  LinearOperators\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/Test_CircularOrbit.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Equations.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::AnalyticData {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GrSelfForce.CircularOrbit\",\n                  \"[PointwiseFunctions][Unit]\") {\n  // This test checks both the self-force equations and the effective source\n  // computation in a very robust way: it ensures that the elliptic operator\n  // applied to the singular field gives the effective source.\n  // This is done numerically on a rectangular grid in (r_*, theta) near\n  // the puncture.\n  const double theta_offset = M_PI / 8.;\n  const double delta_theta = M_PI / 40.;\n  const double rstar_offset = 0.;\n  const double delta_rstar = 5.;\n  const size_t npoints = 20;\n  const domain::creators::Rectangle domain_creator{\n      {{rstar_offset, M_PI_2 + theta_offset}},\n      {{rstar_offset + delta_rstar, M_PI_2 + theta_offset + delta_theta}},\n      {{0, 0}},\n      {{npoints, npoints}},\n      {{false, false}}};\n  const auto domain = domain_creator.create_domain();\n  const auto& block = domain.blocks()[0];\n  const ElementId<2> element_id{0};\n  const ElementMap<2, Frame::Inertial> element_map{element_id, block};\n  const Mesh<2> mesh{npoints, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::Gauss};\n  const auto xi = logical_coordinates(mesh);\n  const auto x = element_map(xi);\n  const auto inv_jacobian = element_map.inv_jacobian(xi);\n  const auto& r_star = get<0>(x);\n  const auto& theta = get<1>(x);\n  CAPTURE(min(r_star));\n  CAPTURE(max(r_star));\n  CAPTURE(min(theta));\n  CAPTURE(max(theta));\n\n  // Get the analytic fields\n  for (int m_mode_number = 0; m_mode_number < 3; ++m_mode_number) {\n    CAPTURE(m_mode_number);\n    const auto circular_orbit = CircularOrbit{1., 0.9, 6., m_mode_number};\n    CAPTURE(circular_orbit.puncture_position());\n    const auto background =\n        circular_orbit.variables(x, CircularOrbit::background_tags{});\n    const auto& alpha = get<Tags::Alpha>(background);\n    const auto& beta = get<Tags::Beta>(background);\n    const auto& gamma_rstar = get<Tags::GammaRstar>(background);\n    const auto& gamma_theta = get<Tags::GammaTheta>(background);\n    const auto vars = circular_orbit.variables(x, CircularOrbit::source_tags{});\n    const auto& singular_field = get<Tags::SingularField>(vars);\n    const auto& deriv_singular_field = get<\n        ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>>(\n        vars);\n    const auto& effective_source = get<::Tags::FixedSource<Tags::MMode>>(vars);\n\n    // Take numeric derivative\n    const auto numeric_deriv_singular_field =\n        partial_derivative(singular_field, mesh, inv_jacobian);\n    const Approx custom_approx = Approx::custom().epsilon(1.e-10).scale(1.);\n    for (size_t i = 0; i < deriv_singular_field.size(); ++i) {\n      CAPTURE(i);\n      CHECK_ITERABLE_CUSTOM_APPROX(numeric_deriv_singular_field[i],\n                                   deriv_singular_field[i], custom_approx);\n    }\n\n    Variables<\n        tmpl::list<::Tags::Flux<Tags::MMode, tmpl::size_t<2>, Frame::Inertial>>>\n        fluxes{mesh.number_of_grid_points()};\n    auto& flux_singular_field =\n        get<::Tags::Flux<Tags::MMode, tmpl::size_t<2>, Frame::Inertial>>(\n            fluxes);\n    GrSelfForce::Fluxes::apply(make_not_null(&flux_singular_field), alpha, {},\n                               deriv_singular_field);\n    auto divs = divergence(fluxes, mesh, inv_jacobian);\n    auto& scalar_eqn = get<::Tags::div<\n        ::Tags::Flux<Tags::MMode, tmpl::size_t<2>, Frame::Inertial>>>(divs);\n    for (size_t i = 0; i < scalar_eqn.size(); ++i) {\n      scalar_eqn[i] *= -1.;\n    }\n    GrSelfForce::Sources::apply(make_not_null(&scalar_eqn), beta, gamma_rstar,\n                                gamma_theta, singular_field,\n                                deriv_singular_field,\n                                flux_singular_field);\n    for (size_t i = 0; i < scalar_eqn.size(); ++i) {\n      CAPTURE(i);\n      CHECK_ITERABLE_CUSTOM_APPROX(scalar_eqn[i], -effective_source[i],\n                                   custom_approx);\n    }\n  }\n}\n\n}  // namespace GrSelfForce::AnalyticData\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GrSelfForceBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_Angular.cpp\n  Test_None.cpp\n  Test_Sommerfeld.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainStructure\n  Elliptic\n  LinearOperators\n  GrSelfForceBoundaryConditions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Test_Angular.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Angular.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Factory.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\nSPECTRE_TEST_CASE(\"Unit.GrSelfForce.BoundaryConditions.Angular\",\n                  \"[Unit][Elliptic]\") {\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<2>, Angular>(\n      \"Angular:\\n\"\n      \"  MModeNumber: 3\\n\");\n  REQUIRE(dynamic_cast<const Angular*>(created.get()) != nullptr);\n  const auto& boundary_condition = dynamic_cast<const Angular&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Properties\");\n    CHECK(boundary_condition.m_mode_number() == 3);\n    CHECK(boundary_condition.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              elliptic::BoundaryConditionType::Dirichlet});\n  }\n  {\n    INFO(\"Apply boundary condition\");\n    const DataVector used_for_size(5);\n    auto field = make_with_value<tnsr::aa<ComplexDataVector, 3>>(\n        used_for_size, std::numeric_limits<double>::signaling_NaN());\n    auto n_dot_field_gradient = make_with_value<tnsr::aa<ComplexDataVector, 3>>(\n        used_for_size, std::numeric_limits<double>::signaling_NaN());\n    using GradTensorType = TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3>, 2, UpLo::Lo, Frame::Inertial>;\n    auto deriv_field = make_with_value<GradTensorType>(\n        used_for_size, std::numeric_limits<double>::signaling_NaN());\n    const auto box = db::create<db::AddSimpleTags<>>();\n    elliptic::apply_boundary_condition<false, void,\n                                       standard_boundary_conditions>(\n        boundary_condition, box, Direction<1>::lower_xi(),\n        make_not_null(&field), make_not_null(&n_dot_field_gradient),\n        deriv_field);\n    auto expected_value =\n        make_with_value<tnsr::aa<ComplexDataVector, 3>>(used_for_size, 0.);\n    CHECK_ITERABLE_APPROX(field, expected_value);\n  }\n}\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Test_None.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Factory.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/None.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\nSPECTRE_TEST_CASE(\"Unit.GrSelfForce.BoundaryConditions.None\",\n                  \"[Unit][Elliptic]\") {\n  // 1. Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<2>, None>(\"None\");\n  REQUIRE(dynamic_cast<const None*>(created.get()) != nullptr);\n  const auto& boundary_condition = dynamic_cast<const None&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Apply boundary condition\");\n    const DataVector used_for_size(5);\n    auto field =\n        make_with_value<tnsr::aa<ComplexDataVector, 3>>(used_for_size, 1.2);\n    auto n_dot_field_gradient =\n        make_with_value<tnsr::aa<ComplexDataVector, 3>>(used_for_size, 3.4);\n    using GradTensorType = TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3>, 2, UpLo::Lo, Frame::Inertial>;\n    auto deriv_field = make_with_value<GradTensorType>(used_for_size, 5.6);\n    const auto box = db::create<db::AddSimpleTags<>>();\n    using local_none_list = tmpl::list<None>;\n    elliptic::apply_boundary_condition<false, void, local_none_list>(\n        boundary_condition, box, Direction<2>::lower_xi(),\n        make_not_null(&field), make_not_null(&n_dot_field_gradient),\n        deriv_field);\n    for (size_t i = 0; i < field.size(); ++i) {\n      CHECK(field[i] == ComplexDataVector(5, 1.2));\n      CHECK(n_dot_field_gradient[i] == ComplexDataVector(5, 3.4));\n    }\n  }\n}\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Test_Sommerfeld.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Factory.hpp\"\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace GrSelfForce::BoundaryConditions {\n\nSPECTRE_TEST_CASE(\"Unit.GrSelfForce.BoundaryConditions.Sommerfeld\",\n                  \"[Unit][Elliptic]\") {\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<2>, Sommerfeld>(\n      \"Sommerfeld:\\n\"\n      \"  BlackHoleMass: 1.1\\n\"\n      \"  BlackHoleSpin: 0.5\\n\"\n      \"  OrbitalRadius: 10.0\\n\"\n      \"  MModeNumber: 2\\n\");\n  REQUIRE(dynamic_cast<const Sommerfeld*>(created.get()) != nullptr);\n  const auto& boundary_condition = dynamic_cast<const Sommerfeld&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Properties\");\n    CHECK(boundary_condition.black_hole_mass() == 1.1);\n    CHECK(boundary_condition.black_hole_spin() == 0.5);\n    CHECK(boundary_condition.orbital_radius() == 10.0);\n    CHECK(boundary_condition.m_mode_number() == 2);\n    CHECK(boundary_condition.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              elliptic::BoundaryConditionType::Neumann});\n  }\n  {\n    INFO(\"Apply boundary condition\");\n    const DataVector used_for_size(5);\n    auto field =\n        make_with_value<tnsr::aa<ComplexDataVector, 3>>(used_for_size, 1.);\n    auto n_dot_field_gradient = make_with_value<tnsr::aa<ComplexDataVector, 3>>(\n        used_for_size, std::numeric_limits<double>::signaling_NaN());\n    using GradTensorType = TensorMetafunctions::prepend_spatial_index<\n        tnsr::aa<ComplexDataVector, 3>, 2, UpLo::Lo, Frame::Inertial>;\n    auto deriv_field = make_with_value<GradTensorType>(\n        used_for_size, std::numeric_limits<double>::signaling_NaN());\n    const auto box = db::create<db::AddSimpleTags<>>();\n    elliptic::apply_boundary_condition<false, void,\n                                       standard_boundary_conditions>(\n        boundary_condition, box, Direction<1>::lower_xi(),\n        make_not_null(&field), make_not_null(&n_dot_field_gradient),\n        deriv_field);\n    const int m_mode = 2;\n    const double M = 1.1;\n    const double r0 = 10.0;\n    const double a = 0.5 * M;\n    const double omega = m_mode / (a + sqrt(cube(r0) / M));\n    tnsr::aa<ComplexDataVector, 3> expected_value{used_for_size.size()};\n    for (auto& component : expected_value) {\n      component = std::complex<double>(0., omega);\n    }\n    CHECK_ITERABLE_APPROX(n_dot_field_gradient, expected_value);\n  }\n}\n\n}  // namespace GrSelfForce::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GrSelfForce\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  GrSelfForce\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(AnalyticData)\nadd_subdirectory(BoundaryConditions)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/GeneralRelativity/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Systems/SelfForce/GeneralRelativity/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace GrSelfForce {\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Xcts.Tags\", \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<Tags::MMode>(\"MMode\");\n  TestHelpers::db::test_simple_tag<Tags::Alpha>(\"Alpha\");\n  TestHelpers::db::test_simple_tag<Tags::Beta>(\"Beta\");\n  TestHelpers::db::test_simple_tag<Tags::GammaRstar>(\"GammaRstar\");\n  TestHelpers::db::test_simple_tag<Tags::GammaTheta>(\"GammaTheta\");\n  TestHelpers::db::test_simple_tag<Tags::FieldIsRegularized>(\n      \"FieldIsRegularized\");\n  TestHelpers::db::test_simple_tag<Tags::SingularField>(\"SingularField\");\n  TestHelpers::db::test_simple_tag<Tags::BoyerLindquistRadius>(\n      \"BoyerLindquistRadius\");\n}\n\n}  // namespace GrSelfForce\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarSelfForceActions\")\n\nset(LIBRARY_SOURCES\n  Test_InitializeEffectiveSource.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  Elliptic\n  EllipticDg\n  Options\n  Parallel\n  ScalarSelfForceActions\n  ScalarSelfForceAnalyticData\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/Actions/Test_InitializeEffectiveSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <memory>\n#include <optional>\n\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/AnalyticSolution.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Actions/InitializeDomain.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Actions/InitializeEffectiveSource.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/FirstOrderSystem.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::Actions {\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<2>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<2>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<\n              ActionTesting::InitializeDataBox<tmpl::list<\n                  domain::Tags::InitialRefinementLevels<2>,\n                  domain::Tags::InitialExtents<2>,\n                  ::Tags::Mortars<domain::Tags::Coordinates<2, Frame::Inertial>,\n                                  2>>>,\n              elliptic::dg::Actions::InitializeDomain<2>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<InitializeEffectiveSource<\n                                 ScalarSelfForce::FirstOrderSystem,\n                                 elliptic::Tags::Background<\n                                     elliptic::analytic_data::Background>>>>>;\n};\n\nstruct Metavariables {\n  using system = ScalarSelfForce::FirstOrderSystem;\n  using component_list = tmpl::list<ElementArray<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<elliptic::analytic_data::Background,\n                   tmpl::list<ScalarSelfForce::AnalyticData::CircularOrbit>>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarSelfForce.Actions.InitializeEffectiveSource\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  using background_tag =\n      elliptic::Tags::Background<elliptic::analytic_data::Background>;\n  using element_array = ElementArray<Metavariables>;\n  using field_tag = ScalarSelfForce::Tags::MMode;\n\n  domain::creators::register_derived_with_charm();\n  register_factory_classes_with_charm<Metavariables>();\n  const domain::creators::AlignedLattice<2> domain_creator{\n      {{{-20., 0., 20.}, {-1., 1.}}}, {{2, 2}}, {{4, 4}}, {}, {}, {}};\n  const auto element_regularized =\n      ElementId<2>{1, {{SegmentId{2, 0}, SegmentId{2, 1}}}};\n  const auto element_unregularized =\n      ElementId<2>{0, {{SegmentId{2, 3}, SegmentId{2, 1}}}};\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      tuples::TaggedTuple<background_tag, domain::Tags::Domain<2>,\n                          domain::Tags::FunctionsOfTimeInitialize,\n                          elliptic::dg::Tags::Massive,\n                          elliptic::dg::Tags::Quadrature>{\n          std::make_unique<ScalarSelfForce::AnalyticData::CircularOrbit>(\n              1.0, 0.0, 6.0, 1, std::nullopt, false),\n          domain_creator.create_domain(), domain_creator.functions_of_time(),\n          false, Spectral::Quadrature::GaussLobatto}};\n  const ::Tags::Mortars<domain::Tags::Coordinates<2, Frame::Inertial>, 2>::type\n      empty_mortar_coords{};\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      &runner, element_regularized,\n      {domain_creator.initial_refinement_levels(),\n       domain_creator.initial_extents(), empty_mortar_coords});\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      &runner, element_unregularized,\n      {domain_creator.initial_refinement_levels(),\n       domain_creator.initial_extents(), empty_mortar_coords});\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            element_regularized);\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            element_unregularized);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            element_regularized);\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            element_unregularized);\n\n  const auto get_tag = [&runner](const auto& element_id,\n                                 auto tag_v) -> const auto& {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n  const auto check_is_zero = [](const auto& values) {\n    return alg::all_of(values, [](const auto& value) { return value == 0.0; });\n  };\n\n  CHECK(get_tag(element_regularized, Tags::FieldIsRegularized{}) == true);\n  // Values of the effective source and singular field are tested in\n  // `Test_CircularOrbit.cpp`.\n  CHECK_FALSE(check_is_zero(\n      get(get_tag(element_regularized, ::Tags::FixedSource<field_tag>{}))));\n  CHECK_FALSE(\n      check_is_zero(get(get_tag(element_regularized, Tags::SingularField{}))));\n\n  CHECK(get_tag(element_unregularized, Tags::FieldIsRegularized{}) == false);\n  CHECK(check_is_zero(\n      get(get_tag(element_unregularized, ::Tags::FixedSource<field_tag>{}))));\n  CHECK(check_is_zero(\n      get(get_tag(element_unregularized, Tags::SingularField{}))));\n}\n\n}  // namespace ScalarSelfForce::Actions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/AmrCriteria/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarSelfForceAmrCriteria\")\n\nset(LIBRARY_SOURCES\n  Test_RefineAtPunctures.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Amr\n  AmrCriteria\n  DataStructures\n  DomainCreators\n  DomainStructure\n  Options\n  Parallel\n  ScalarSelfForceAmrCriteria\n  ScalarSelfForceAnalyticData\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/AmrCriteria/Test_RefineAtPunctures.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AmrCriteria/RefineAtPuncture.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::AmrCriteria {\n\nnamespace {\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 2;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<amr::Criterion,\n                   tmpl::list<ScalarSelfForce::AmrCriteria::RefineAtPuncture>>>;\n  };\n};\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\nstruct OtherBackground : elliptic::analytic_data::Background,\n                         elliptic::analytic_data::InitialGuess {\n  OtherBackground() = default;\n  explicit OtherBackground(CkMigrateMessage* /*m*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(OtherBackground);\n};\n\nPUP::able::PUP_ID OtherBackground::my_PUP_ID = 0;  // NOLINT\n#pragma GCC diagnostic pop\n\nvoid test_criterion(\n    std::unique_ptr<elliptic::analytic_data::Background> background) {\n  const auto created =\n      TestHelpers::test_creation<std::unique_ptr<amr::Criterion>,\n                                 Metavariables>(\"RefineAtPuncture\");\n  REQUIRE(dynamic_cast<const RefineAtPuncture*>(created.get()) != nullptr);\n  const auto& criterion = serialize_and_deserialize(\n      dynamic_cast<const RefineAtPuncture&>(*created));\n\n  {\n    INFO(\"Evaluate\");\n    using background_tag =\n        elliptic::Tags::Background<elliptic::analytic_data::Background>;\n\n    const domain::creators::Rectangle domain_creator{\n        {{10., 0.}}, {{15., 1.}}, {{2, 2}}, {{3, 3}}, {{false, false}}};\n    auto databox =\n        db::create<tmpl::list<background_tag, domain::Tags::Domain<2>>>(\n            std::move(background), domain_creator.create_domain());\n    const ObservationBox<\n        tmpl::list<>,\n        db::DataBox<tmpl::list<background_tag, domain::Tags::Domain<2>>>>\n        box{make_not_null(&databox)};\n    Parallel::GlobalCache<Metavariables> empty_cache{};\n\n    {\n      INFO(\"Element with puncture within\");\n      const ElementId<2> element_id{0, {{{2, 2}, {2, 0}}}};\n      const auto expected_flags = make_array<2>(amr::Flag::Split);\n      auto flags = criterion.evaluate(box, empty_cache, element_id);\n      CHECK(flags == expected_flags);\n    }\n    {\n      INFO(\"Elements without puncture\");\n      const auto expected_flags = make_array<2>(amr::Flag::DoNothing);\n      for (const auto& element_id : {ElementId<2>{0, {{{2, 1}, {2, 0}}}},\n                                     ElementId<2>{0, {{{2, 3}, {2, 3}}}},\n                                     ElementId<2>{0, {{{2, 3}, {2, 0}}}},\n                                     ElementId<2>{0, {{{2, 0}, {2, 3}}}}}) {\n        auto flags = criterion.evaluate(box, empty_cache, element_id);\n        CHECK(flags == expected_flags);\n      }\n    }\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarSelfForce.AmrCriteria.RefineAtPuncture\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  test_criterion(std::make_unique<ScalarSelfForce::AnalyticData::CircularOrbit>(\n      // Only orbital radius is relevant for the test\n      1.0, 0.5, /* orbital radius */ 10.0, 2, std::nullopt, false));\n  CHECK_THROWS_WITH(test_criterion(std::make_unique<OtherBackground>()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"RefineAtPuncture only works with 'CircularOrbit'.\"));\n}\n\n}  // namespace ScalarSelfForce::AmrCriteria\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/AnalyticData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarSelfForceAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_CircularOrbit.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Utilities\n  ScalarSelfForce\n  ScalarSelfForceAnalyticData\n  Domain\n  DomainCreators\n  LinearOperators\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/AnalyticData/Test_CircularOrbit.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/AnalyticData/CircularOrbit.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Equations.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::AnalyticData {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarSelfForce.CircularOrbit\",\n                  \"[PointwiseFunctions][Unit]\") {\n  // This test checks both the self-force equations and the effective source\n  // computation in a very robust way: it ensures that the elliptic operator\n  // applied to the singular field gives the effective source.\n  // This is done numerically on a rectangular grid in (r_*, cos(theta)) near\n  // the puncture.\n  const double costheta_offset = 0.1;\n  const double delta_costheta = 0.2;\n  const double rstar_offset = 0.;\n  const double delta_rstar = 5.;\n  const size_t npoints = 20;\n  const domain::creators::Rectangle domain_creator{\n      {{rstar_offset, costheta_offset}},\n      {{rstar_offset + delta_rstar, costheta_offset + delta_costheta}},\n      {{0, 0}},\n      {{npoints, npoints}},\n      {{false, false}}};\n  const auto domain = domain_creator.create_domain();\n  const auto& block = domain.blocks()[0];\n  const ElementId<2> element_id{0};\n  const ElementMap<2, Frame::Inertial> element_map{element_id, block};\n  const Mesh<2> mesh{npoints, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto xi = logical_coordinates(mesh);\n  const auto x = element_map(xi);\n  const auto inv_jacobian = element_map.inv_jacobian(xi);\n  const auto& r_star = get<0>(x);\n  const auto& cos_theta = get<1>(x);\n  CAPTURE(min(r_star));\n  CAPTURE(max(r_star));\n  CAPTURE(min(cos_theta));\n  CAPTURE(max(cos_theta));\n\n  // Get the analytic fields\n  for (const bool impose_equatorial_symmetry : {true, false}) {\n    CAPTURE(impose_equatorial_symmetry);\n    for (int m_mode_number = 0; m_mode_number < 3; ++m_mode_number) {\n      CAPTURE(m_mode_number);\n      const auto circular_orbit = CircularOrbit{1.,\n                                                0.9,\n                                                6.,\n                                                m_mode_number,\n                                                {{-25., -5., 20., 40.}},\n                                                impose_equatorial_symmetry};\n      CAPTURE(circular_orbit.puncture_position());\n      const auto background =\n          circular_orbit.variables(x, CircularOrbit::background_tags{});\n      const auto& alpha = get<Tags::Alpha>(background);\n      const auto& beta = get<Tags::Beta>(background);\n      const auto& gamma = get<Tags::Gamma>(background);\n      const auto vars =\n          circular_orbit.variables(x, CircularOrbit::source_tags{});\n      const auto& singular_field = get<Tags::SingularField>(vars);\n      const auto& deriv_singular_field = get<\n          ::Tags::deriv<Tags::SingularField, tmpl::size_t<2>, Frame::Inertial>>(\n          vars);\n      const auto& effective_source =\n          get<::Tags::FixedSource<Tags::MMode>>(vars);\n\n      // Take numeric derivative\n      const auto numeric_deriv_singular_field =\n          partial_derivative(singular_field, mesh, inv_jacobian);\n      const Approx custom_approx = Approx::custom().epsilon(1.e-10).scale(1.);\n      for (size_t i = 0; i < deriv_singular_field.size(); ++i) {\n        CAPTURE(i);\n        CHECK_ITERABLE_CUSTOM_APPROX(numeric_deriv_singular_field[i],\n                                     deriv_singular_field[i], custom_approx);\n      }\n\n      tnsr::I<ComplexDataVector, 2> flux_singular_field{};\n      ScalarSelfForce::Fluxes::apply(make_not_null(&flux_singular_field), alpha,\n                                     {}, deriv_singular_field);\n      auto scalar_eqn = divergence(flux_singular_field, mesh, inv_jacobian);\n      get(scalar_eqn) *= -1.;\n      ScalarSelfForce::Sources::apply(make_not_null(&scalar_eqn), beta, gamma,\n                                      singular_field, deriv_singular_field,\n                                      flux_singular_field);\n      // Minus sign is from the definition of the effective source:\n      //   \\psi = \\psi_R + \\psi_P = 0\n      // where \\psi_R is the regular part and \\psi_P is the singular part\n      //   => -\\Delta \\psi_R = \\Delta \\psi_P = S_eff\n      // where -Delta represents the elliptic operator. So the effective source\n      // for the regular part is is the negative of the elliptic operator\n      // acting on the singular part.\n      CHECK_ITERABLE_CUSTOM_APPROX(get(scalar_eqn), -get(effective_source),\n                                   custom_approx);\n    }\n  }\n}\n}  // namespace ScalarSelfForce::AnalyticData\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarSelfForceBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_Sommerfeld.cpp\n  Test_None.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainStructure\n  Elliptic\n  LinearOperators\n  ScalarSelfForceBoundaryConditions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Test_None.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Factory.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/None.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::BoundaryConditions {\n\nSPECTRE_TEST_CASE(\"Unit.ScalarSelfForce.BoundaryConditions.None\",\n                  \"[Unit][Elliptic]\") {\n  // 1. Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<2>, None>(\"None\");\n  REQUIRE(dynamic_cast<const None*>(created.get()) != nullptr);\n  const auto& boundary_condition = dynamic_cast<const None&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Apply boundary condition\");\n    const DataVector used_for_size(5);\n    auto field = make_with_value<Scalar<ComplexDataVector>>(used_for_size, 1.2);\n    auto n_dot_field_gradient = make_with_value<Scalar<ComplexDataVector>>(\n        used_for_size, 3.4);\n    const tnsr::i<ComplexDataVector, 2> deriv_field{\n        used_for_size.size(), 5.6};\n    const auto box = db::create<db::AddSimpleTags<>>();\n    using local_none_list = tmpl::list<None>;\n    elliptic::apply_boundary_condition<\n        false, void, local_none_list>(\n        boundary_condition, box, Direction<2>::lower_xi(),\n        make_not_null(&field), make_not_null(&n_dot_field_gradient),\n        deriv_field);\n    CHECK(get(field) == ComplexDataVector(5, 1.2));\n    CHECK(get(n_dot_field_gradient) == ComplexDataVector(5, 3.4));\n  }\n}\n\n}  // namespace ScalarSelfForce::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Test_Sommerfeld.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Factory.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarSelfForce::BoundaryConditions {\nSPECTRE_TEST_CASE(\"Unit.ScalarSelfForce.BoundaryConditions.Sommerfeld\",\n                  \"[Unit][Elliptic]\") {\n  using BC = elliptic::BoundaryConditions::BoundaryCondition<2>;\n  const std::string base_sommerfeld =\n        \"Sommerfeld:\\n\"\n        \"  BlackHoleMass: 1.0\\n\"\n        \"  BlackHoleSpin: 0.5\\n\"\n        \"  OrbitalRadius: 10.0\\n\"\n        \"  MModeNumber: 2\\n\";\n  const Direction<2> direction = Direction<2>::lower_xi();\n  const size_t num_pts = 5;\n\n  {\n    INFO(\"Apply boundary condition: Order 1\");\n    const auto created = TestHelpers::test_factory_creation<BC, Sommerfeld>(\n        base_sommerfeld + \"  HyperboloidalSlicing: False\\n  Order: 1\");\n    const auto& boundary_condition = dynamic_cast<const Sommerfeld&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Properties\");\n    CHECK(boundary_condition.black_hole_mass() == 1.0);\n    CHECK(boundary_condition.black_hole_spin() == 0.5);\n    CHECK(boundary_condition.orbital_radius() == 10.0);\n    CHECK(boundary_condition.m_mode_number() == 2);\n    CHECK(boundary_condition.hyperboloidal_slicing() == false);\n    CHECK(boundary_condition.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              elliptic::BoundaryConditionType::Neumann});\n  }\n    auto field = make_with_value<Scalar<ComplexDataVector>>(num_pts, 1.0);\n    auto n_dot_grad = make_with_value<Scalar<ComplexDataVector>>(num_pts, 0.0);\n    const DirectionMap<2, Scalar<ComplexDataVector>> beta_map{\n        {direction, make_with_value<Scalar<ComplexDataVector>>(num_pts, 0.0)}};\n    const DirectionMap<2, tnsr::i<ComplexDataVector, 2>> gamma_map{\n        {direction,\n         make_with_value<tnsr::i<ComplexDataVector, 2>>(num_pts, 0.0)}};\n\n    const auto box = db::create<db::AddSimpleTags<\n        domain::Tags::Faces<2, Tags::Beta>,\n        domain::Tags::Faces<2, Tags::Gamma>>>(\n        beta_map, gamma_map);\n    elliptic::apply_boundary_condition<\n        false, void, standard_boundary_conditions>(\n        boundary_condition, box, direction,\n        make_not_null(&field), make_not_null(&n_dot_grad),\n        tnsr::i<ComplexDataVector, 2>{num_pts, 0.0});\n    const ComplexDataVector expected{\n        num_pts, std::complex<double>(0., 0.06226111848298833)};\n    CHECK_ITERABLE_APPROX(get(n_dot_grad), expected);\n  }\n\n  {\n    INFO(\"Apply boundary condition: Order 2\");\n    const auto created = TestHelpers::test_factory_creation<BC, Sommerfeld>(\n        base_sommerfeld + \"  HyperboloidalSlicing: True\\n  Order: 2\");\n    const auto& boundary_condition = dynamic_cast<const Sommerfeld&>(*created);\n    auto field = make_with_value<Scalar<ComplexDataVector>>(num_pts, 1.0);\n    auto n_dot_grad = make_with_value<Scalar<ComplexDataVector>>(num_pts, 0.0);\n    auto beta = make_with_value<\n        Scalar<ComplexDataVector>>(num_pts, std::complex<double>(0.5, 0.0));\n    auto gamma = make_with_value<tnsr::i<ComplexDataVector, 2>>(num_pts, 0.0);\n    get<0>(gamma) = ComplexDataVector{num_pts, {2.0, 0.0}};\n    const auto box = db::create<db::AddSimpleTags<\n        domain::Tags::Faces<2, Tags::Beta>,\n        domain::Tags::Faces<2, Tags::Gamma>>>(\n            DirectionMap<2, Scalar<ComplexDataVector>>{\n                {direction, std::move(beta)}},\n            DirectionMap<2, tnsr::i<ComplexDataVector, 2>>{\n                {direction, std::move(gamma)}});\n    elliptic::apply_boundary_condition<\n        false, void, standard_boundary_conditions>(\n        boundary_condition, box, direction,\n        make_not_null(&field), make_not_null(&n_dot_grad),\n        tnsr::i<ComplexDataVector, 2>{num_pts, 0.0});\n    const ComplexDataVector expected(\n        num_pts, std::complex<double>(-0.25, 0.0));\n    CHECK_ITERABLE_APPROX(get(n_dot_grad), expected);\n  }\n}\n\n}  // namespace ScalarSelfForce::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarSelfForce\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  DomainCreators\n  DomainStructure\n  Options\n  ScalarSelfForce\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(AmrCriteria)\nadd_subdirectory(AnalyticData)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(Events)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarSelfForceEvents\")\n\nset(LIBRARY_SOURCES\n  Test_ObserveSelfForce.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  Elliptic\n  Framework\n  Interpolation\n  LinearOperators\n  Observer\n  ObserverHelpers\n  Parallel\n  ScalarSelfForceAnalyticData\n  ScalarSelfForceEvents\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/Events/Test_ObserveSelfForce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Events/ObserveSelfForce.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct MockContributeReductionData {\n  using ReductionData =\n      ScalarSelfForce::Events::ObserveSelfForce<>::ReductionData;\n\n  struct Results {\n    observers::ObservationId observation_id{};\n    std::string subfile_name{};\n    std::vector<std::string> legend{};\n    double iteration_id{};\n    size_t num_grid_points{};\n    size_t num_contributing_elements{};\n    std::vector<double> self_force{};\n  };\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static Results results;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static std::optional<ReductionData> combined_reduction_data;\n\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex, typename... Ts>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationId& observation_id,\n                    Parallel::ArrayComponentId /*sender_array_id*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& legend,\n                    Parallel::ReductionData<Ts...>&& local_reduction_data) {\n    if (not MockContributeReductionData::combined_reduction_data.has_value()) {\n      MockContributeReductionData::combined_reduction_data.emplace(\n          std::move(local_reduction_data));\n    } else {\n      MockContributeReductionData::combined_reduction_data->combine(\n          std::move(local_reduction_data));\n    }\n    auto reduction_data = *MockContributeReductionData::combined_reduction_data;\n    reduction_data.finalize();\n    results.observation_id = observation_id;\n    results.subfile_name = subfile_name;\n    results.legend = legend;\n    results.iteration_id = std::get<0>(reduction_data.data());\n    results.num_grid_points = std::get<1>(reduction_data.data());\n    results.num_contributing_elements = std::get<2>(reduction_data.data());\n    results.self_force = {\n        std::get<3>(reduction_data.data()), std::get<4>(reduction_data.data()),\n        std::get<5>(reduction_data.data()), std::get<6>(reduction_data.data())};\n  }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nMockContributeReductionData::Results MockContributeReductionData::results{};\nstd::optional<MockContributeReductionData::ReductionData>\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    MockContributeReductionData::combined_reduction_data{};\n\ntemplate <typename Metavariables>\nstruct ElementComponent {\n  using component_being_mocked = void;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<2>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverComponent {\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::ContributeReductionData>;\n  using with_these_simple_actions = tmpl::list<MockContributeReductionData>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<ElementComponent<Metavariables>,\n                                    MockObserverComponent<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        Event, tmpl::list<ScalarSelfForce::Events::ObserveSelfForce<>>>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarSelfForce.Events.ObserveSelfForce\",\n                  \"[Unit][Elliptic]\") {\n  const ScalarSelfForce::AnalyticData::CircularOrbit circular_orbit{\n      1.0, 0.0, 10.0, 2, std::nullopt, false};\n\n  const double puncture_r_star = get<0>(circular_orbit.puncture_position());\n  const domain::creators::Rectilinear<2> domain_creator{\n      {{puncture_r_star - 1.0, -0.5}},\n      {{puncture_r_star + 1.0, 0.5}},\n      {{0, 0}},\n      {{4, 4}}};\n  const auto domain = domain_creator.create_domain();\n  const Mesh<2> mesh{4_st, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::Gauss};\n  const ElementId<2> element_id_a{0, {{{1, 0}, {0, 0}}}};\n  const ElementId<2> element_id_b{0, {{{1, 1}, {0, 0}}}};\n\n  using mock_observer_writer = MockObserverComponent<Metavariables>;\n  using element_component = ElementComponent<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_group_component<mock_observer_writer>(&runner);\n  ActionTesting::emplace_component<element_component>(&runner, element_id_a);\n  ActionTesting::emplace_component<element_component>(&runner, element_id_b);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Just testing that the event runs and writes the expected data. Actual\n  // numbers are tested in the scalar self-force input-file test.\n  const Scalar<ComplexDataVector> field{mesh.number_of_grid_points(), 0.};\n  const ScalarSelfForce::Events::ObserveSelfForce event{};\n  for (const auto& element_id : std::array{element_id_a, element_id_b}) {\n    const ElementMap<2, Frame::Inertial> element_map{\n        element_id, domain.blocks()[element_id.block_id()]};\n    const auto inv_jacobian =\n        element_map.inv_jacobian(logical_coordinates(mesh));\n    auto box = db::create<tmpl::list<\n        elliptic::Tags::Background<elliptic::analytic_data::Background>,\n        domain::Tags::Domain<2>, domain::Tags::Mesh<2>,\n        domain::Tags::InverseJacobian<2, Frame::ElementLogical,\n                                      Frame::Inertial>,\n        ScalarSelfForce::Tags::MMode>>(\n        std::unique_ptr<elliptic::analytic_data::Background>(\n            std::make_unique<ScalarSelfForce::AnalyticData::CircularOrbit>(\n                circular_orbit)),\n        domain_creator.create_domain(), mesh, inv_jacobian, field);\n    auto obs_box =\n        make_observation_box<db::AddComputeTags<>>(make_not_null(&box));\n    event.run(make_not_null(&obs_box),\n              ActionTesting::cache<element_component>(runner, element_id),\n              element_id, std::add_pointer_t<element_component>{},\n              {\"Iteration\", 1.0});\n  }\n\n  runner.template invoke_queued_simple_action<mock_observer_writer>(0);\n  runner.template invoke_queued_simple_action<mock_observer_writer>(0);\n  CHECK(runner.template is_simple_action_queue_empty<mock_observer_writer>(0));\n\n  const auto& results = MockContributeReductionData::results;\n  CHECK(results.observation_id.value() == 1.0);\n  CHECK(results.observation_id.observation_key() ==\n        observers::ObservationKey(\"SelfForce.dat\"));\n  CHECK(results.subfile_name == \"SelfForce.dat\");\n  CHECK(results.legend ==\n        std::vector<std::string>{\"IterationId\", \"NumGridPoints\",\n                                 \"NumContributingElements\", \"Re(SelfForce_r)\",\n                                 \"Im(SelfForce_r)\", \"Re(SelfForce_theta)\",\n                                 \"Im(SelfForce_theta)\"});\n  CHECK(results.iteration_id == 1.0);\n  CHECK(results.num_grid_points == 2 * mesh.number_of_grid_points());\n  CHECK(results.num_contributing_elements == 2_st);\n  CHECK(results.self_force == std::vector<double>{0.0, 0.0, 0.0, 0.0});\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/SelfForce/Scalar/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n#include <vector>\n\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Elliptic/Systems/SelfForce/Scalar/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace ScalarSelfForce {\nnamespace {\n\nvoid test_null_slicing_tag_logic() {\n  INFO(\"Testing NullSlicingBlocks structure\");\n  const std::array<std::vector<double>, 2> coords{\n      {{0.0, 0.25, 0.5}, {0.0, 0.5}}};\n  using Region = domain::creators::RefinementRegion<2>;\n  const std::unique_ptr<DomainCreator<2>> lattice =\n      std::make_unique<domain::creators::AlignedLattice<2>>(\n          std::array<std::vector<double>, 2>{{{0., 0.25, 0.5}, {0., 0.5}}},\n          std::array<size_t, 2>{{0, 0}}, std::array<size_t, 2>{{3, 2}},\n          std::vector<Region>{Region{{0, 0}, {1, 1}, {0, 1}}},\n          std::vector<Region>{}, std::vector<std::array<size_t, 2>>{},\n          std::array<bool, 2>{{false, false}}, Options::Context{});\n\n  const std::vector<std::string> blocks{\"Block(0,0)\", \"Block(1,0)\"};\n  const std::vector<size_t> result =\n      Tags::NullSlicingBlocks::create_from_options(blocks, lattice);\n  const std::vector<size_t> expected{0, 1};\n  CHECK(result == expected);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.ScalarSelfForce.Tags\",\n                  \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<Tags::MMode>(\"MMode\");\n  TestHelpers::db::test_simple_tag<Tags::Alpha>(\"Alpha\");\n  TestHelpers::db::test_simple_tag<Tags::Beta>(\"Beta\");\n  TestHelpers::db::test_simple_tag<Tags::Gamma>(\"Gamma\");\n  TestHelpers::db::test_simple_tag<Tags::FieldIsRegularized>(\n      \"FieldIsRegularized\");\n  TestHelpers::db::test_simple_tag<Tags::SingularField>(\"SingularField\");\n  TestHelpers::db::test_simple_tag<Tags::BoyerLindquistRadius>(\n      \"BoyerLindquistRadius\");\n  TestHelpers::db::test_simple_tag<Tags::BoostFunction>(\"BoostFunction\");\n  TestHelpers::db::test_simple_tag<Tags::BoostFunctionDeriv>(\n      \"BoostFunctionDeriv\");\n\n  test_null_slicing_tag_logic();\n}\n\n}  // namespace ScalarSelfForce\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Test_GetSourcesComputer.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <type_traits>\n\n#include \"Elliptic/Systems/GetSourcesComputer.hpp\"\n\nnamespace elliptic {\nnamespace {\nstruct LinearSources {\n  using argument_tags = tmpl::list<int>;\n};\nstruct NonlinearSources {\n  using argument_tags = tmpl::list<double>;\n};\n\nstruct LinearSystem {\n  using sources_computer = LinearSources;\n};\n\nstruct NonlinearSystem {\n  using sources_computer = NonlinearSources;\n  using sources_computer_linearized = LinearSources;\n};\n\nstruct NoSourcesSystem {\n  using sources_computer = void;\n  using sources_computer_linearized = void;\n};\n\nstatic_assert(\n    std::is_same_v<get_sources_computer<LinearSystem, false>, LinearSources>);\nstatic_assert(std::is_same_v<get_sources_argument_tags<LinearSystem, false>,\n                             tmpl::list<int>>);\nstatic_assert(\n    std::is_same_v<get_sources_computer<LinearSystem, true>, LinearSources>);\nstatic_assert(std::is_same_v<get_sources_argument_tags<LinearSystem, true>,\n                             tmpl::list<int>>);\nstatic_assert(std::is_same_v<get_sources_computer<NonlinearSystem, false>,\n                             NonlinearSources>);\nstatic_assert(std::is_same_v<get_sources_argument_tags<NonlinearSystem, false>,\n                             tmpl::list<double>>);\nstatic_assert(\n    std::is_same_v<get_sources_computer<NonlinearSystem, true>, LinearSources>);\nstatic_assert(std::is_same_v<get_sources_argument_tags<NoSourcesSystem, false>,\n                             tmpl::list<>>);\nstatic_assert(std::is_same_v<get_sources_argument_tags<NoSourcesSystem, true>,\n                             tmpl::list<>>);\n}  // namespace\n}  // namespace elliptic\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/BoundaryConditions/ApparentHorizon.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom numpy import abs, sqrt\n\n\ndef make_metric_riemannian(inv_conformal_metric):\n    for i in range(3):\n        for j in range(i):\n            inv_conformal_metric[i, j] *= 1.0e-2\n            inv_conformal_metric[j, i] *= 1.0e-2\n        inv_conformal_metric[i, i] = abs(inv_conformal_metric[i, i])\n\n\ndef make_spherical_face_normal(x, inv_conformal_metric):\n    proper_radius = sqrt(np.einsum(\"ij,i,j\", inv_conformal_metric, x, x))\n    return -x / proper_radius\n\n\n# This is h^ij nabla_i s_j (all quantities conformal) in Eq. 7.12 in\n# Harald's thesis https://arxiv.org/abs/gr-qc/0510016\ndef projected_normal_gradient(\n    conformal_unit_normal,\n    x,\n    inv_conformal_metric,\n    conformal_christoffel_second_kind,\n):\n    conformal_unit_normal_raised = np.einsum(\n        \"ij,j\", inv_conformal_metric, conformal_unit_normal\n    )\n    inv_conformal_surface_metric = inv_conformal_metric - np.einsum(\n        \"i,j->ij\", conformal_unit_normal_raised, conformal_unit_normal_raised\n    )\n    # Assuming here that the surface is a coordinate-sphere\n    euclidean_radius = np.linalg.norm(x)\n    unnormalized_face_normal = x / euclidean_radius\n    magnitude_of_face_normal = sqrt(\n        np.einsum(\n            \"ij,i,j\",\n            inv_conformal_metric,\n            unnormalized_face_normal,\n            unnormalized_face_normal,\n        )\n    )  # r_curved / r_flat\n    deriv_unnormalized_face_normal = (\n        np.identity(3) / euclidean_radius\n        - np.einsum(\"i,j->ij\", x, x) / euclidean_radius**3\n    )\n    # The term with the derivative of the magnitude vanishes when projected on\n    # the surface metric, so it's omitted here\n    conformal_unit_normal_gradient = (\n        deriv_unnormalized_face_normal / magnitude_of_face_normal\n        - np.einsum(\n            \"i,ijk\", conformal_unit_normal, conformal_christoffel_second_kind\n        )\n    )\n    return np.einsum(\n        \"ij,ij\", inv_conformal_surface_metric, conformal_unit_normal_gradient\n    )\n\n\n# This function implements the apparent-horizon boundary condition, Eq. 7.12 in\n# Harald's thesis https://arxiv.org/abs/gr-qc/0510016\ndef normal_dot_conformal_factor_gradient(\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    n_dot_longitudinal_shift_excess,\n    center,\n    spin,\n    x,\n    extrinsic_curvature_trace,\n    shift_background,\n    longitudinal_shift_background,\n    inv_conformal_metric,\n    conformal_christoffel_second_kind,\n):\n    x -= center\n    make_metric_riemannian(inv_conformal_metric)\n    conformal_unit_normal = -make_spherical_face_normal(x, inv_conformal_metric)\n    local_projected_normal_gradient = projected_normal_gradient(\n        conformal_unit_normal,\n        x,\n        inv_conformal_metric,\n        conformal_christoffel_second_kind,\n    )\n    lapse = (lapse_times_conformal_factor_minus_one + 1.0) / (\n        conformal_factor_minus_one + 1.0\n    )\n    n_dot_longitudinal_shift = n_dot_longitudinal_shift_excess + np.einsum(\n        \"i,ij\", -conformal_unit_normal, longitudinal_shift_background\n    )\n    J = 2.0 / 3.0 * extrinsic_curvature_trace - 0.5 / lapse * np.einsum(\n        \"i,i\", -conformal_unit_normal, n_dot_longitudinal_shift\n    )\n    return (\n        0.25\n        * (conformal_factor_minus_one + 1.0)\n        * (\n            local_projected_normal_gradient\n            - (conformal_factor_minus_one + 1.0) ** 2 * J\n        )\n    )\n\n\ndef normal_dot_conformal_factor_gradient_flat_cartesian(*args, **kwargs):\n    return normal_dot_conformal_factor_gradient(\n        *args,\n        inv_conformal_metric=np.identity(3),\n        conformal_christoffel_second_kind=np.zeros((3, 3, 3)),\n        **kwargs,\n    )\n\n\ndef normal_dot_conformal_factor_gradient_correction(\n    conformal_factor_correction,\n    lapse_times_conformal_factor_correction,\n    n_dot_longitudinal_shift_excess_correction,\n    center,\n    spin,\n    x,\n    extrinsic_curvature_trace,\n    longitudinal_shift_background,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    n_dot_longitudinal_shift_excess,\n    inv_conformal_metric,\n    conformal_christoffel_second_kind,\n):\n    x -= center\n    make_metric_riemannian(inv_conformal_metric)\n    conformal_unit_normal = -make_spherical_face_normal(x, inv_conformal_metric)\n    local_projected_normal_gradient = projected_normal_gradient(\n        conformal_unit_normal,\n        x,\n        inv_conformal_metric,\n        conformal_christoffel_second_kind,\n    )\n    lapse = (lapse_times_conformal_factor_minus_one + 1.0) / (\n        conformal_factor_minus_one + 1.0\n    )\n    n_dot_longitudinal_shift = n_dot_longitudinal_shift_excess + np.einsum(\n        \"i,ij\", -conformal_unit_normal, longitudinal_shift_background\n    )\n    J = 2.0 / 3.0 * extrinsic_curvature_trace - 0.5 / lapse * np.einsum(\n        \"i,i\", -conformal_unit_normal, n_dot_longitudinal_shift\n    )\n    J_correction = -0.5 * (\n        (\n            conformal_factor_correction\n            / (lapse_times_conformal_factor_minus_one + 1.0)\n            - (conformal_factor_minus_one + 1.0)\n            / (lapse_times_conformal_factor_minus_one + 1.0) ** 2\n            * lapse_times_conformal_factor_correction\n        )\n        * np.einsum(\"i,i\", -conformal_unit_normal, n_dot_longitudinal_shift)\n        + 1.0\n        / lapse\n        * np.einsum(\n            \"i,i\",\n            -conformal_unit_normal,\n            n_dot_longitudinal_shift_excess_correction,\n        )\n    )\n    return 0.25 * (\n        conformal_factor_correction * local_projected_normal_gradient\n        - 3.0\n        * (conformal_factor_minus_one + 1.0) ** 2\n        * conformal_factor_correction\n        * J\n        - (conformal_factor_minus_one + 1.0) ** 3 * J_correction\n    )\n\n\ndef normal_dot_conformal_factor_gradient_correction_flat_cartesian(\n    *args, **kwargs\n):\n    return normal_dot_conformal_factor_gradient_correction(\n        *args,\n        inv_conformal_metric=np.identity(3),\n        conformal_christoffel_second_kind=np.zeros((3, 3, 3)),\n        **kwargs,\n    )\n\n\n# This is the homogeneous-Neumann boundary condition on the lapse\ndef normal_dot_lapse_times_conformal_factor_gradient(*args, **kwargs):\n    return 0.0\n\n\n# This function implements the quasi-equilibrium condition on the shift, i.e.\n# Eq. 7.14 in Harald's thesis https://arxiv.org/abs/gr-qc/0510016\ndef shift_excess(\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    n_dot_longitudinal_shift_excess,\n    center,\n    spin,\n    x,\n    extrinsic_curvature_trace,\n    shift_background,\n    longitudinal_shift_background,\n    inv_conformal_metric,\n    conformal_christoffel_second_kind,\n):\n    x -= center\n    make_metric_riemannian(inv_conformal_metric)\n    conformal_unit_normal = -make_spherical_face_normal(x, inv_conformal_metric)\n    conformal_unit_normal_raised = np.einsum(\n        \"ij,j\", inv_conformal_metric, conformal_unit_normal\n    )\n    shift_orthogonal = (lapse_times_conformal_factor_minus_one + 1.0) / (\n        conformal_factor_minus_one + 1.0\n    ) ** 3\n    shift_parallel = np.cross(spin, x)\n    return (\n        shift_orthogonal * conformal_unit_normal_raised\n        + shift_parallel\n        - shift_background\n    )\n\n\ndef shift_excess_flat_cartesian(*args, **kwargs):\n    return shift_excess(\n        *args,\n        inv_conformal_metric=np.identity(3),\n        conformal_christoffel_second_kind=np.zeros((3, 3, 3)),\n        **kwargs,\n    )\n\n\ndef shift_excess_correction(\n    conformal_factor_correction,\n    lapse_times_conformal_factor_correction,\n    n_dot_longitudinal_shift_excess_correction,\n    center,\n    spin,\n    x,\n    extrinsic_curvature_trace,\n    longitudinal_shift_background,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    n_dot_longitudinal_shift_excess,\n    inv_conformal_metric,\n    conformal_christoffel_second_kind,\n):\n    x -= center\n    make_metric_riemannian(inv_conformal_metric)\n    conformal_unit_normal = -make_spherical_face_normal(x, inv_conformal_metric)\n    conformal_unit_normal_raised = np.einsum(\n        \"ij,j\", inv_conformal_metric, conformal_unit_normal\n    )\n    shift_orthogonal_correction = (\n        lapse_times_conformal_factor_correction\n        / (conformal_factor_minus_one + 1.0) ** 3\n        - 3.0\n        * (lapse_times_conformal_factor_minus_one + 1.0)\n        / (conformal_factor_minus_one + 1.0) ** 4\n        * conformal_factor_correction\n    )\n    return shift_orthogonal_correction * conformal_unit_normal_raised\n\n\ndef shift_excess_correction_flat_cartesian(*args, **kwargs):\n    return shift_excess_correction(\n        *args,\n        inv_conformal_metric=np.identity(3),\n        conformal_christoffel_second_kind=np.zeros((3, 3, 3)),\n        **kwargs,\n    )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_XctsBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_ApparentHorizon.cpp\n  Test_Flatness.cpp\n  Test_Robin.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainStructure\n  Elliptic\n  GeneralRelativity\n  GeneralRelativitySolutions\n  LinearOperators\n  Utilities\n  XctsBoundaryConditions\n  XctsSolutions\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/BoundaryConditions/Robin.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom numpy import abs, sqrt\nfrom PointwiseFunctions.Xcts.LongitudinalOperator import (\n    longitudinal_operator_flat_cartesian,\n)\n\n\ndef robin_boundary_condition_scalar(field, x):\n    r = np.linalg.norm(x)\n    return -field / r\n\n\ndef robin_boundary_condition_shift(shift, deriv_shift, x, face_normal):\n    proj = np.eye(3) - np.outer(face_normal, face_normal)\n    r = np.linalg.norm(x)\n    deriv_shift = (\n        np.einsum(\"ik,kj->ij\", proj, deriv_shift)\n        - np.outer(face_normal, shift) / r\n    )\n    strain = (deriv_shift + deriv_shift.T) / 2.0\n    return np.einsum(\n        \"i,ij\", face_normal, longitudinal_operator_flat_cartesian(strain)\n    )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/BoundaryConditions/Test_ApparentHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/KerrHorizonConforming.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/IndexToSliceAt.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/Faces.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/ApparentHorizon.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/Binary.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialGuess.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::BoundaryConditions {\n\nnamespace {\n\nusing KerrSchild = Xcts::Solutions::WrappedGr<gr::Solutions::KerrSchild>;\n\n// Make a metric approximately Riemannian\nvoid make_metric_riemannian(\n    const gsl::not_null<tnsr::II<DataVector, 3>*> inv_conformal_metric) {\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < i; ++j) {\n      inv_conformal_metric->get(i, j) *= 1.e-2;\n    }\n    inv_conformal_metric->get(i, i) = abs(inv_conformal_metric->get(i, i));\n  }\n}\n\n// Generate a face normal pretending the surface is a coordinate sphere\nstd::tuple<tnsr::i<DataVector, 3>, tnsr::ij<DataVector, 3>, Scalar<DataVector>>\nmake_spherical_face_normal(\n    tnsr::I<DataVector, 3> x, const std::array<double, 3>& center,\n    const tnsr::II<DataVector, 3>& inv_conformal_metric) {\n  for (size_t d = 0; d < 3; ++d) {\n    x.get(d) -= gsl::at(center, d);\n  }\n  Scalar<DataVector> proper_radius{x.begin()->size(), 0.};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      get(proper_radius) +=\n          inv_conformal_metric.get(i, j) * x.get(i) * x.get(j);\n    }\n  }\n  get(proper_radius) = sqrt(get(proper_radius));\n  tnsr::i<DataVector, 3> face_normal{x.begin()->size()};\n  get<0>(face_normal) = -get<0>(x) / get(proper_radius);\n  get<1>(face_normal) = -get<1>(x) / get(proper_radius);\n  get<2>(face_normal) = -get<2>(x) / get(proper_radius);\n  tnsr::ij<DataVector, 3> deriv_unnormalized_face_normal{x.begin()->size(), 0.};\n  get<0, 0>(deriv_unnormalized_face_normal) = -1.;\n  get<1, 1>(deriv_unnormalized_face_normal) = -1.;\n  get<2, 2>(deriv_unnormalized_face_normal) = -1.;\n  return {std::move(face_normal), std::move(deriv_unnormalized_face_normal),\n          std::move(proper_radius)};\n}\nstd::tuple<tnsr::i<DataVector, 3>, tnsr::ij<DataVector, 3>, Scalar<DataVector>>\nmake_spherical_face_normal_flat_cartesian(tnsr::I<DataVector, 3> x,\n                                          const std::array<double, 3>& center) {\n  for (size_t d = 0; d < 3; ++d) {\n    x.get(d) -= gsl::at(center, d);\n  }\n  Scalar<DataVector> euclidean_radius = magnitude(x);\n  tnsr::i<DataVector, 3> face_normal{x.begin()->size()};\n  get<0>(face_normal) = -get<0>(x) / get(euclidean_radius);\n  get<1>(face_normal) = -get<1>(x) / get(euclidean_radius);\n  get<2>(face_normal) = -get<2>(x) / get(euclidean_radius);\n  tnsr::ij<DataVector, 3> deriv_unnormalized_face_normal{x.begin()->size(), 0.};\n  get<0, 0>(deriv_unnormalized_face_normal) = -1.;\n  get<1, 1>(deriv_unnormalized_face_normal) = -1.;\n  get<2, 2>(deriv_unnormalized_face_normal) = -1.;\n  return {std::move(face_normal), std::move(deriv_unnormalized_face_normal),\n          std::move(euclidean_radius)};\n}\n\ntemplate <Xcts::Geometry ConformalGeometry, bool Linearized, typename... Args>\nvoid apply_boundary_condition_impl(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n    Scalar<DataVector> conformal_factor,\n    Scalar<DataVector> lapse_times_conformal_factor,\n    tnsr::I<DataVector, 3> n_dot_longitudinal_shift_excess,\n    const std::array<double, 3>& center, const std::array<double, 3>& rotation,\n    Args&&... args) {\n  const ApparentHorizon<ConformalGeometry> boundary_condition{\n      center, rotation, std::nullopt, std::nullopt};\n  const auto direction = Direction<3>::lower_xi();\n  const auto box = db::create<domain::make_faces_tags<\n      3,\n      tmpl::conditional_t<\n          Linearized,\n          typename ApparentHorizon<ConformalGeometry>::argument_tags_linearized,\n          typename ApparentHorizon<ConformalGeometry>::argument_tags>,\n      tmpl::conditional_t<\n          Linearized,\n          typename ApparentHorizon<ConformalGeometry>::volume_tags_linearized,\n          typename ApparentHorizon<ConformalGeometry>::volume_tags>>>(\n      DirectionMap<3, std::decay_t<decltype(args)>>{\n          {direction, std::move(args)}}...);\n  const size_t num_points = conformal_factor.begin()->size();\n  tnsr::i<DataVector, 3> deriv_conformal_factor{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  tnsr::i<DataVector, 3> deriv_lapse_times_conformal_factor{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  tnsr::iJ<DataVector, 3> deriv_shift_excess{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  elliptic::apply_boundary_condition<\n      Linearized, void, tmpl::list<ApparentHorizon<ConformalGeometry>>>(\n      boundary_condition, box, direction, make_not_null(&conformal_factor),\n      make_not_null(&lapse_times_conformal_factor), shift_excess,\n      n_dot_conformal_factor_gradient,\n      n_dot_lapse_times_conformal_factor_gradient,\n      make_not_null(&n_dot_longitudinal_shift_excess), deriv_conformal_factor,\n      deriv_lapse_times_conformal_factor, deriv_shift_excess);\n}\n\nvoid apply_boundary_condition(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess,\n    const std::array<double, 3>& center, const std::array<double, 3>& rotation,\n    const tnsr::I<DataVector, 3>& x,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::I<DataVector, 3>& shift_background,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n    tnsr::II<DataVector, 3> inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) {\n  make_metric_riemannian(make_not_null(&inv_conformal_metric));\n  auto [face_normal, deriv_unnormalized_face_normal, face_normal_magnitude] =\n      make_spherical_face_normal(x, center, inv_conformal_metric);\n  apply_boundary_condition_impl<Xcts::Geometry::Curved, false>(\n      n_dot_conformal_factor_gradient,\n      n_dot_lapse_times_conformal_factor_gradient, shift_excess,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      n_dot_longitudinal_shift_excess, center, rotation, std::move(face_normal),\n      std::move(deriv_unnormalized_face_normal),\n      std::move(face_normal_magnitude), x, extrinsic_curvature_trace,\n      shift_background, longitudinal_shift_background,\n      std::move(inv_conformal_metric), conformal_christoffel_second_kind);\n}\n\nvoid apply_boundary_condition_flat_cartesian(\n    const gsl::not_null<Scalar<DataVector>*> n_dot_conformal_factor_gradient,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess,\n    const std::array<double, 3>& center, const std::array<double, 3>& rotation,\n    const tnsr::I<DataVector, 3>& x,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::I<DataVector, 3>& shift_background,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background) {\n  auto [face_normal, deriv_unnormalized_face_normal, face_normal_magnitude] =\n      make_spherical_face_normal_flat_cartesian(x, center);\n  apply_boundary_condition_impl<Xcts::Geometry::FlatCartesian, false>(\n      n_dot_conformal_factor_gradient,\n      n_dot_lapse_times_conformal_factor_gradient, shift_excess,\n      conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      n_dot_longitudinal_shift_excess, center, rotation, std::move(face_normal),\n      std::move(deriv_unnormalized_face_normal),\n      std::move(face_normal_magnitude), x, extrinsic_curvature_trace,\n      shift_background, longitudinal_shift_background);\n}\n\nvoid apply_boundary_condition_linearized(\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_conformal_factor_gradient_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess_correction,\n    const std::array<double, 3>& center, const std::array<double, 3>& rotation,\n    const tnsr::I<DataVector, 3>& x,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess,\n    tnsr::II<DataVector, 3> inv_conformal_metric,\n    const tnsr::Ijj<DataVector, 3>& conformal_christoffel_second_kind) {\n  make_metric_riemannian(make_not_null(&inv_conformal_metric));\n  auto [face_normal, deriv_unnormalized_face_normal, face_normal_magnitude] =\n      make_spherical_face_normal(x, center, inv_conformal_metric);\n  apply_boundary_condition_impl<Xcts::Geometry::Curved, true>(\n      n_dot_conformal_factor_gradient_correction,\n      n_dot_lapse_times_conformal_factor_gradient_correction,\n      shift_excess_correction, conformal_factor_correction,\n      lapse_times_conformal_factor_correction,\n      n_dot_longitudinal_shift_excess_correction, center, rotation,\n      std::move(face_normal), std::move(deriv_unnormalized_face_normal),\n      std::move(face_normal_magnitude), x, extrinsic_curvature_trace,\n      longitudinal_shift_background, conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one, n_dot_longitudinal_shift_excess,\n      std::move(inv_conformal_metric), conformal_christoffel_second_kind);\n}\n\nvoid apply_boundary_condition_linearized_flat_cartesian(\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_conformal_factor_gradient_correction,\n    const gsl::not_null<Scalar<DataVector>*>\n        n_dot_lapse_times_conformal_factor_gradient_correction,\n    const gsl::not_null<tnsr::I<DataVector, 3>*> shift_excess_correction,\n    const Scalar<DataVector>& conformal_factor_correction,\n    const Scalar<DataVector>& lapse_times_conformal_factor_correction,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess_correction,\n    const std::array<double, 3>& center, const std::array<double, 3>& rotation,\n    const tnsr::I<DataVector, 3>& x,\n    const Scalar<DataVector>& extrinsic_curvature_trace,\n    const tnsr::II<DataVector, 3>& longitudinal_shift_background,\n    const Scalar<DataVector>& conformal_factor_minus_one,\n    const Scalar<DataVector>& lapse_times_conformal_factor_minus_one,\n    const tnsr::I<DataVector, 3>& n_dot_longitudinal_shift_excess) {\n  auto [face_normal, deriv_unnormalized_face_normal, face_normal_magnitude] =\n      make_spherical_face_normal_flat_cartesian(x, center);\n  apply_boundary_condition_impl<Xcts::Geometry::FlatCartesian, true>(\n      n_dot_conformal_factor_gradient_correction,\n      n_dot_lapse_times_conformal_factor_gradient_correction,\n      shift_excess_correction, conformal_factor_correction,\n      lapse_times_conformal_factor_correction,\n      n_dot_longitudinal_shift_excess_correction, center, rotation,\n      std::move(face_normal), std::move(deriv_unnormalized_face_normal),\n      std::move(face_normal_magnitude), x, extrinsic_curvature_trace,\n      longitudinal_shift_background, conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one, n_dot_longitudinal_shift_excess);\n}\n\nstruct FactoryMetavars {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<elliptic::BoundaryConditions::BoundaryCondition<3>,\n                   tmpl::list<ApparentHorizon<Xcts::Geometry::Curved>>>,\n        tmpl::pair<elliptic::analytic_data::InitialGuess,\n                   tmpl::list<KerrSchild>>>;\n  };\n};\n\nvoid test_creation(\n    const std::array<double, 3>& center, const std::array<double, 3>& rotation,\n    const std::optional<KerrSchild>& solution_for_lapse,\n    const std::optional<KerrSchild>& solution_for_negative_expansion,\n    const std::string& option_string) {\n  INFO(\"Test factory-creation\");\n  register_factory_classes_with_charm<FactoryMetavars>();\n  const auto created = TestHelpers::test_creation<\n      std::unique_ptr<elliptic::BoundaryConditions::BoundaryCondition<3>>,\n      FactoryMetavars>(option_string);\n  const auto serialized = serialize_and_deserialize(created);\n  REQUIRE(dynamic_cast<const ApparentHorizon<Xcts::Geometry::Curved>*>(\n              serialized.get()) != nullptr);\n  const auto& boundary_condition =\n      dynamic_cast<const ApparentHorizon<Xcts::Geometry::Curved>&>(*serialized);\n  {\n    INFO(\"Properties\");\n    CHECK(boundary_condition.center() == center);\n    CHECK(boundary_condition.rotation() == rotation);\n    CHECK(boundary_condition.solution_for_lapse().has_value() ==\n          solution_for_lapse.has_value());\n    if (solution_for_lapse.has_value()) {\n      const auto kerrschild = dynamic_cast<const KerrSchild*>(\n          boundary_condition.solution_for_lapse().value().get());\n      REQUIRE(kerrschild != nullptr);\n      CHECK(*kerrschild == *solution_for_lapse);\n    }\n    CHECK(boundary_condition.solution_for_negative_expansion().has_value() ==\n          solution_for_negative_expansion.has_value());\n    if (solution_for_negative_expansion.has_value()) {\n      const auto kerrschild = dynamic_cast<const KerrSchild*>(\n          boundary_condition.solution_for_negative_expansion().value().get());\n      REQUIRE(kerrschild != nullptr);\n      CHECK(*kerrschild == *solution_for_negative_expansion);\n    }\n    CHECK(boundary_condition.boundary_condition_types() ==\n          std::vector<elliptic::BoundaryConditionType>{\n              elliptic::BoundaryConditionType::Neumann,\n              solution_for_lapse.has_value()\n                  ? elliptic::BoundaryConditionType::Dirichlet\n                  : elliptic::BoundaryConditionType::Neumann,\n              elliptic::BoundaryConditionType::Dirichlet,\n              elliptic::BoundaryConditionType::Dirichlet,\n              elliptic::BoundaryConditionType::Dirichlet});\n  }\n}\n\nvoid test_with_random_values() {\n  INFO(\"Random-value tests\");\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"Elliptic/Systems/Xcts/BoundaryConditions/\");\n  pypp::check_with_random_values<11>(\n      &apply_boundary_condition, \"ApparentHorizon\",\n      {\"normal_dot_conformal_factor_gradient\",\n       \"normal_dot_lapse_times_conformal_factor_gradient\", \"shift_excess\"},\n      {{{-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      DataVector{3});\n  pypp::check_with_random_values<9>(\n      &apply_boundary_condition_flat_cartesian, \"ApparentHorizon\",\n      {\"normal_dot_conformal_factor_gradient_flat_cartesian\",\n       \"normal_dot_lapse_times_conformal_factor_gradient\",\n       \"shift_excess_flat_cartesian\"},\n      {{{-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      DataVector{3});\n  pypp::check_with_random_values<13>(\n      &apply_boundary_condition_linearized, \"ApparentHorizon\",\n      {\"normal_dot_conformal_factor_gradient_correction\",\n       \"normal_dot_lapse_times_conformal_factor_gradient\",\n       \"shift_excess_correction\"},\n      {{{-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      DataVector{3});\n  pypp::check_with_random_values<11>(\n      &apply_boundary_condition_linearized_flat_cartesian, \"ApparentHorizon\",\n      {\"normal_dot_conformal_factor_gradient_correction_flat_cartesian\",\n       \"normal_dot_lapse_times_conformal_factor_gradient\",\n       \"shift_excess_correction_flat_cartesian\"},\n      {{{-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.}}},\n      DataVector{3});\n}\n\nvoid test_consistency_with_kerr(const bool compute_expansion,\n                                const bool superposed_lapse_bc_condition) {\n  INFO(\"Consistency with Kerr solution\");\n  CAPTURE(compute_expansion);\n  CAPTURE(superposed_lapse_bc_condition);\n  const double mass = 0.45;\n  const std::array<double, 3> center{{8., 0., 0.}};\n  const std::array<double, 3> dimensionless_spin{{0., 0., 0.8}};\n  const double horizon_kerrschild_radius =\n      mass * (1. + sqrt(1. - dot(dimensionless_spin, dimensionless_spin)));\n  CAPTURE(horizon_kerrschild_radius);\n  // Eq. (8) in https://arxiv.org/abs/1506.01689\n  std::array<double, 3> rotation =\n      -0.5 * dimensionless_spin / horizon_kerrschild_radius;\n  CAPTURE(rotation);\n  const KerrSchild solution{mass, dimensionless_spin,\n                            std::array<double, 3>{{0., 0., 0.}}};\n\n  // Set up the lapse boundary condition\n  using Binary =\n      Xcts::AnalyticData::Binary<elliptic::analytic_data::AnalyticSolution,\n                                 Xcts::Solutions::all_analytic_solutions>;\n  auto lapse_bc_condition =\n      [&]() -> std::unique_ptr<elliptic::analytic_data::InitialGuess> {\n    if (superposed_lapse_bc_condition) {\n      return std::make_unique<Binary>(\n          std::array<double, 2>{{-8., 8.}}, std::array<double, 2>{{0., 0.}},\n          std::make_unique<KerrSchild>(solution),\n          std::make_unique<KerrSchild>(solution), 0.015, 0.,\n          std::array<double, 3>{{0., 0., 0.}}, std::array<double, 2>{{4., 4.}});\n    } else {\n      return std::make_unique<KerrSchild>(solution);\n    }\n  }();\n\n  const ApparentHorizon<Xcts::Geometry::Curved> kerr_horizon{\n      center, rotation, std::move(lapse_bc_condition),\n      // Check with and without the negative-expansion condition. Either the\n      // expansion is _computed_ to be zero from the Kerr solution at the\n      // horizon, or it is just set to zero.\n      compute_expansion\n          ? std::make_optional(std::make_unique<KerrSchild>(solution))\n          : std::nullopt};\n\n  // Set up a wedge with a Kerr-horizon-conforming inner surface\n  const Mesh<3> mesh{6, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const size_t num_points = mesh.number_of_grid_points();\n  const domain::CoordinateMaps::Wedge<3> wedge_map{\n      horizon_kerrschild_radius,\n      2. * horizon_kerrschild_radius,\n      1.,\n      1.,\n      OrientationMap<3>::create_aligned(),\n      false};\n  const domain::CoordinateMaps::KerrHorizonConforming horizon_map{\n      mass, dimensionless_spin};\n  const auto coord_map =\n      domain::make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n          wedge_map, horizon_map);\n  // Set up a mesh so we can numerically differentiate the Jacobian\n  const auto logical_coords = logical_coordinates(mesh);\n  tnsr::I<DataVector, 3> inertial_coords = (*coord_map)(logical_coords);\n  get<0>(inertial_coords) += center[0];\n  const auto inv_jacobian = coord_map->inv_jacobian(logical_coords);\n  const auto deriv_inv_jac =\n      partial_derivative(inv_jacobian, mesh, inv_jacobian);\n  // Coords on the face\n  const auto direction = Direction<3>::lower_zeta();\n  const size_t slice_index = index_to_slice_at(mesh.extents(), direction);\n  const auto x = data_on_slice(inertial_coords, mesh.extents(),\n                               direction.dimension(), slice_index);\n  auto x_centered = x;\n  get<0>(x_centered) -= center[0];\n\n  // Make sure the face is indeed horizon conforming\n  const std::array<DataVector, 2> theta_phi{\n      {atan2(sqrt(square(get<0>(x_centered)) + square(get<1>(x_centered))),\n             get<2>(x_centered)),\n       atan2(get<1>(x_centered), get<0>(x_centered))}};\n  const DataVector expected_horizon_radii = get(\n      gr::Solutions::kerr_horizon_radius(theta_phi, mass, dimensionless_spin));\n  CHECK_ITERABLE_APPROX(get(magnitude(x_centered)), expected_horizon_radii);\n\n  // Get background fields from the solution\n  const auto background_fields = solution.variables(\n      x_centered,\n      tmpl::list<\n          Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n          Tags::ConformalChristoffelSecondKind<DataVector, 3, Frame::Inertial>,\n          gr::Tags::TraceExtrinsicCurvature<DataVector>,\n          Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n          Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n              DataVector, 3, Frame::Inertial>>{});\n  const auto& inv_conformal_metric =\n      get<Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>(\n          background_fields);\n  // Set up the face normal on the horizon surface. It points _into_ the\n  // horizon because the computational domain fills the space outside of it\n  // and the normal always points away from the computational domain.\n  const Mesh<2> face_mesh = mesh.slice_away(direction.dimension());\n  const size_t face_num_points = face_mesh.number_of_grid_points();\n  auto face_normal = unnormalized_face_normal(face_mesh, *coord_map, direction);\n  const auto face_normal_magnitude =\n      magnitude(face_normal, inv_conformal_metric);\n  for (size_t d = 0; d < 3; ++d) {\n    face_normal.get(d) /= get(face_normal_magnitude);\n  }\n  tnsr::ij<DataVector, 3> deriv_unnormalized_face_normal{face_num_points};\n  const auto deriv_inv_jac_on_face = data_on_slice(\n      deriv_inv_jac, mesh.extents(), direction.dimension(), slice_index);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      deriv_unnormalized_face_normal.get(i, j) =\n          direction.sign() *\n          deriv_inv_jac_on_face.get(i, direction.dimension(), j);\n    }\n  }\n\n  // Retrieve the expected surface vars and fluxes from the solution\n  auto surface_vars_expected = variables_from_tagged_tuple(solution.variables(\n      x_centered,\n      tmpl::list<Tags::ConformalFactorMinusOne<DataVector>,\n                 Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n                 Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>{}));\n  const auto surface_fluxes_expected =\n      variables_from_tagged_tuple(solution.variables(\n          x_centered,\n          tmpl::list<\n              ::Tags::Flux<Tags::ConformalFactorMinusOne<DataVector>,\n                           tmpl::size_t<3>, Frame::Inertial>,\n              ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n                           tmpl::size_t<3>, Frame::Inertial>,\n              Tags::LongitudinalShiftExcess<DataVector, 3,\n                                            Frame::Inertial>>{}));\n  Variables<tmpl::list<\n      ::Tags::NormalDotFlux<Tags::ConformalFactorMinusOne<DataVector>>,\n      ::Tags::NormalDotFlux<\n          Tags::LapseTimesConformalFactorMinusOne<DataVector>>,\n      ::Tags::NormalDotFlux<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>>>\n      n_dot_surface_fluxes_expected{num_points};\n  normal_dot_flux(make_not_null(&n_dot_surface_fluxes_expected), face_normal,\n                  surface_fluxes_expected);\n  tnsr::i<DataVector, 3> deriv_conformal_factor{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  tnsr::i<DataVector, 3> deriv_lapse_times_conformal_factor{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  tnsr::iJ<DataVector, 3> deriv_shift_excess{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  // Apply the boundary conditions, passing garbage for the data that the\n  // boundary conditions are expected to fill\n  auto surface_vars = surface_vars_expected;\n  auto n_dot_surface_fluxes = n_dot_surface_fluxes_expected;\n  get(get<::Tags::NormalDotFlux<Tags::ConformalFactorMinusOne<DataVector>>>(\n      n_dot_surface_fluxes)) = std::numeric_limits<double>::signaling_NaN();\n  get<0>(get<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>(surface_vars)) =\n      std::numeric_limits<double>::signaling_NaN();\n  get<1>(get<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>(surface_vars)) =\n      std::numeric_limits<double>::signaling_NaN();\n  get<2>(get<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>(surface_vars)) =\n      std::numeric_limits<double>::signaling_NaN();\n  kerr_horizon.apply(\n      make_not_null(\n          &get<Tags::ConformalFactorMinusOne<DataVector>>(surface_vars)),\n      make_not_null(&get<Tags::LapseTimesConformalFactorMinusOne<DataVector>>(\n          surface_vars)),\n      make_not_null(&get<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>(\n          surface_vars)),\n      make_not_null(\n          &get<\n              ::Tags::NormalDotFlux<Tags::ConformalFactorMinusOne<DataVector>>>(\n              n_dot_surface_fluxes)),\n      make_not_null(&get<::Tags::NormalDotFlux<\n                        Tags::LapseTimesConformalFactorMinusOne<DataVector>>>(\n          n_dot_surface_fluxes)),\n      make_not_null(&get<::Tags::NormalDotFlux<\n                        Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>>(\n          n_dot_surface_fluxes)),\n      deriv_conformal_factor, deriv_lapse_times_conformal_factor,\n      deriv_shift_excess, face_normal, deriv_unnormalized_face_normal,\n      face_normal_magnitude, x,\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(background_fields),\n      get<Tags::ShiftBackground<DataVector, 3, Frame::Inertial>>(\n          background_fields),\n      get<Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n          DataVector, 3, Frame::Inertial>>(background_fields),\n      get<Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>(\n          background_fields),\n      get<Tags::ConformalChristoffelSecondKind<DataVector, 3, Frame::Inertial>>(\n          background_fields));\n  // Check the result.\n  if (superposed_lapse_bc_condition) {\n    const auto& binary =\n        dynamic_cast<const Binary&>(*kerr_horizon.solution_for_lapse().value());\n    get<Tags::LapseTimesConformalFactorMinusOne<DataVector>>(\n        surface_vars_expected) =\n        get<Tags::LapseTimesConformalFactorMinusOne<DataVector>>(\n            binary.variables(\n                x, tmpl::list<\n                       Tags::LapseTimesConformalFactorMinusOne<DataVector>>{}));\n  }\n  CHECK_VARIABLES_APPROX(surface_vars, surface_vars_expected);\n  CHECK_VARIABLES_APPROX(n_dot_surface_fluxes, n_dot_surface_fluxes_expected);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Xcts.BoundaryConditions.ApparentHorizon\",\n                  \"[Unit][Elliptic]\") {\n  test_creation({{1., 2., 3.}}, {{0.1, 0.2, 0.3}}, std::nullopt, std::nullopt,\n                \"ApparentHorizon:\\n\"\n                \"  Center: [1., 2., 3.]\\n\"\n                \"  Rotation: [0.1, 0.2, 0.3]\\n\"\n                \"  Lapse: Auto\\n\"\n                \"  NegativeExpansion: None\\n\");\n  test_creation({{1., 2., 3.}}, {{0.1, 0.2, 0.3}},\n                {Xcts::Solutions::WrappedGr<gr::Solutions::KerrSchild>{\n                    2.3, std::array<double, 3>{{0.4, 0.5, 0.6}},\n                    std::array<double, 3>{{0., 0., 0.}},\n                    std::array<double, 3>{{0., 0., 0.}}}},\n                {Xcts::Solutions::WrappedGr<gr::Solutions::KerrSchild>{\n                    3.4, std::array<double, 3>{{0.3, 0.2, 0.1}},\n                    std::array<double, 3>{{0., 0., 0.}},\n                    std::array<double, 3>{{0., 0., 0.}}}},\n                \"ApparentHorizon:\\n\"\n                \"  Center: [1., 2., 3.]\\n\"\n                \"  Rotation: [0.1, 0.2, 0.3]\\n\"\n                \"  Lapse:\\n\"\n                \"    KerrSchild:\\n\"\n                \"      Mass: 2.3\\n\"\n                \"      Spin: [0.4, 0.5, 0.6]\\n\"\n                \"      Center: [0., 0., 0.]\\n\"\n                \"      Velocity: [0., 0., 0.]\\n\"\n                \"  NegativeExpansion:\\n\"\n                \"    KerrSchild:\\n\"\n                \"      Mass: 3.4\\n\"\n                \"      Spin: [0.3, 0.2, 0.1]\\n\"\n                \"      Center: [0., 0., 0.]\\n\"\n                \"      Velocity: [0., 0., 0.]\\n\");\n  test_with_random_values();\n  test_consistency_with_kerr(false, false);\n  test_consistency_with_kerr(true, false);\n  test_consistency_with_kerr(false, true);\n  test_consistency_with_kerr(true, true);\n}\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/BoundaryConditions/Test_Flatness.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/Flatness.hpp\"\n#include \"Elliptic/Systems/Xcts/FluxesAndSources.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::BoundaryConditions {\n\nnamespace {\ntemplate <Xcts::Equations EnabledEquations, bool Linearized>\nvoid test_flatness(const Flatness<EnabledEquations>& boundary_condition) {\n  const size_t num_points = 3;\n  const auto box = db::create<db::AddSimpleTags<>>();\n  Scalar<DataVector> conformal_factor_minus_one{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  Scalar<DataVector> n_dot_conformal_factor_gradient{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  tnsr::i<DataVector, 3> conformal_factor_gradient{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  if constexpr (EnabledEquations == Xcts::Equations::Hamiltonian) {\n    elliptic::apply_boundary_condition<Linearized, void,\n                                       tmpl::list<Flatness<EnabledEquations>>>(\n        boundary_condition, box, Direction<3>::lower_xi(),\n        make_not_null(&conformal_factor_minus_one),\n        make_not_null(&n_dot_conformal_factor_gradient),\n        conformal_factor_gradient);\n  } else {\n    Scalar<DataVector> lapse_times_conformal_factor_minus_one{\n        num_points, std::numeric_limits<double>::signaling_NaN()};\n    Scalar<DataVector> n_dot_lapse_times_conformal_factor_gradient{\n        num_points, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::i<DataVector, 3> lapse_times_conformal_factor_gradient{\n        num_points, std::numeric_limits<double>::signaling_NaN()};\n    if constexpr (EnabledEquations == Xcts::Equations::HamiltonianAndLapse) {\n      elliptic::apply_boundary_condition<\n          Linearized, void, tmpl::list<Flatness<EnabledEquations>>>(\n          boundary_condition, box, Direction<3>::lower_xi(),\n          make_not_null(&conformal_factor_minus_one),\n          make_not_null(&lapse_times_conformal_factor_minus_one),\n          make_not_null(&n_dot_conformal_factor_gradient),\n          make_not_null(&n_dot_lapse_times_conformal_factor_gradient),\n          conformal_factor_gradient, lapse_times_conformal_factor_gradient);\n    } else {\n      tnsr::I<DataVector, 3> shift_excess{\n          num_points, std::numeric_limits<double>::signaling_NaN()};\n      tnsr::I<DataVector, 3> n_dot_longitudinal_shift_excess{\n          num_points, std::numeric_limits<double>::signaling_NaN()};\n      tnsr::iJ<DataVector, 3> deriv_shift_excess{\n          num_points, std::numeric_limits<double>::signaling_NaN()};\n      elliptic::apply_boundary_condition<\n          Linearized, void, tmpl::list<Flatness<EnabledEquations>>>(\n          boundary_condition, box, Direction<3>::lower_xi(),\n          make_not_null(&conformal_factor_minus_one),\n          make_not_null(&lapse_times_conformal_factor_minus_one),\n          make_not_null(&shift_excess),\n          make_not_null(&n_dot_conformal_factor_gradient),\n          make_not_null(&n_dot_lapse_times_conformal_factor_gradient),\n          make_not_null(&n_dot_longitudinal_shift_excess),\n          conformal_factor_gradient, lapse_times_conformal_factor_gradient,\n          deriv_shift_excess);\n      CHECK(get<0>(shift_excess) == DataVector(num_points, 0.));\n      CHECK(get<1>(shift_excess) == DataVector(num_points, 0.));\n      CHECK(get<2>(shift_excess) == DataVector(num_points, 0.));\n    }\n    CHECK(get(lapse_times_conformal_factor_minus_one) ==\n          DataVector(num_points, 0.));\n  }\n  CHECK(get(conformal_factor_minus_one) == DataVector(num_points, 0.));\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nvoid test_suite() {\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<3>,\n      Flatness<EnabledEquations>>(\"Flatness\");\n  REQUIRE(dynamic_cast<const Flatness<EnabledEquations>*>(created.get()) !=\n          nullptr);\n  const auto& boundary_condition =\n      dynamic_cast<const Flatness<EnabledEquations>&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Properties\");\n    if constexpr (EnabledEquations == Xcts::Equations::Hamiltonian) {\n      CHECK(boundary_condition.boundary_condition_types() ==\n            std::vector<elliptic::BoundaryConditionType>{\n                1, elliptic::BoundaryConditionType::Dirichlet});\n    } else if constexpr (EnabledEquations ==\n                         Xcts::Equations::HamiltonianAndLapse) {\n      CHECK(boundary_condition.boundary_condition_types() ==\n            std::vector<elliptic::BoundaryConditionType>{\n                2, elliptic::BoundaryConditionType::Dirichlet});\n    } else if constexpr (EnabledEquations ==\n                         Xcts::Equations::HamiltonianLapseAndShift) {\n      CHECK(boundary_condition.boundary_condition_types() ==\n            std::vector<elliptic::BoundaryConditionType>{\n                5, elliptic::BoundaryConditionType::Dirichlet});\n    }\n  }\n  test_flatness<EnabledEquations, false>(boundary_condition);\n  test_flatness<EnabledEquations, true>(boundary_condition);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Xcts.BoundaryConditions.Flatness\", \"[Unit][Elliptic]\") {\n  test_suite<Xcts::Equations::Hamiltonian>();\n  test_suite<Xcts::Equations::HamiltonianAndLapse>();\n  test_suite<Xcts::Equations::HamiltonianLapseAndShift>();\n}\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/BoundaryConditions/Test_Robin.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/FaceNormal.hpp\"\n#include \"Elliptic/BoundaryConditions/ApplyBoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Elliptic/BoundaryConditions/BoundaryConditionType.hpp\"\n#include \"Elliptic/Systems/Xcts/BoundaryConditions/Robin.hpp\"\n#include \"Elliptic/Systems/Xcts/FluxesAndSources.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"PointwiseFunctions/Xcts/LongitudinalOperator.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::BoundaryConditions {\n\nnamespace {\n\nconst std::string py_module{\"Elliptic.Systems.Xcts.BoundaryConditions.Robin\"};\n\ntnsr::i<DataVector, 3> make_spherical_face_normal_flat_cartesian(\n    tnsr::I<DataVector, 3> x, const std::array<double, 3>& center) {\n  for (size_t d = 0; d < 3; ++d) {\n    x.get(d) -= gsl::at(center, d);\n  }\n  Scalar<DataVector> euclidean_radius = magnitude(x);\n  tnsr::i<DataVector, 3> face_normal{x.begin()->size()};\n  get<0>(face_normal) = -get<0>(x) / get(euclidean_radius);\n  get<1>(face_normal) = -get<1>(x) / get(euclidean_radius);\n  get<2>(face_normal) = -get<2>(x) / get(euclidean_radius);\n  return face_normal;\n}\n\ntemplate <Xcts::Equations EnabledEquations, bool Linearized>\nvoid test_robin(const Robin<EnabledEquations>& boundary_condition) {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const size_t num_points = 3;\n  const auto direction = Direction<3>::upper_zeta();\n  const std::array<double, 3> center{{0., 0., 0.}};\n  const auto x = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&gen), make_not_null(&dist), num_points);\n  const auto face_normal = make_spherical_face_normal_flat_cartesian(x, center);\n  const auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Faces<3, domain::Tags::Coordinates<3, Frame::Inertial>>,\n      domain::Tags::Faces<3, domain::Tags::FaceNormal<3, Frame::Inertial>>>>(\n      DirectionMap<3, tnsr::I<DataVector, 3>>{{direction, x}},\n      DirectionMap<3, tnsr::i<DataVector, 3>>{{direction, face_normal}});\n  auto conformal_factor_minus_one = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&gen), make_not_null(&dist), num_points);\n  Scalar<DataVector> n_dot_conformal_factor_gradient{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  tnsr::i<DataVector, 3> deriv_conformal_factor{\n      num_points, std::numeric_limits<double>::signaling_NaN()};\n  if constexpr (EnabledEquations == Xcts::Equations::Hamiltonian) {\n    elliptic::apply_boundary_condition<Linearized, void,\n                                       tmpl::list<Robin<EnabledEquations>>>(\n        boundary_condition, box, direction,\n        make_not_null(&conformal_factor_minus_one),\n        make_not_null(&n_dot_conformal_factor_gradient),\n        deriv_conformal_factor);\n  } else {\n    auto lapse_times_conformal_factor_minus_one =\n        make_with_random_values<Scalar<DataVector>>(\n            make_not_null(&gen), make_not_null(&dist), num_points);\n    Scalar<DataVector> n_dot_lapse_times_conformal_factor_gradient{\n        num_points, std::numeric_limits<double>::signaling_NaN()};\n    tnsr::i<DataVector, 3> deriv_lapse_times_conformal_factor{\n        num_points, std::numeric_limits<double>::signaling_NaN()};\n    if constexpr (EnabledEquations == Xcts::Equations::HamiltonianAndLapse) {\n      elliptic::apply_boundary_condition<Linearized, void,\n                                         tmpl::list<Robin<EnabledEquations>>>(\n          boundary_condition, box, direction,\n          make_not_null(&conformal_factor_minus_one),\n          make_not_null(&lapse_times_conformal_factor_minus_one),\n          make_not_null(&n_dot_conformal_factor_gradient),\n          make_not_null(&n_dot_lapse_times_conformal_factor_gradient),\n          deriv_conformal_factor, deriv_lapse_times_conformal_factor);\n    } else {\n      auto shift_excess = make_with_random_values<tnsr::I<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist), num_points);\n      auto deriv_shift_excess =\n          make_with_random_values<tnsr::iJ<DataVector, 3>>(\n              make_not_null(&gen), make_not_null(&dist), num_points);\n      tnsr::II<DataVector, 3> longitudinal_shift_excess{\n          num_points, std::numeric_limits<double>::signaling_NaN()};\n      Xcts::longitudinal_operator_flat_cartesian(\n          make_not_null(&longitudinal_shift_excess), deriv_shift_excess);\n      tnsr::I<DataVector, 3> n_dot_longitudinal_shift_excess{\n          num_points, std::numeric_limits<double>::signaling_NaN()};\n      normal_dot_flux(make_not_null(&n_dot_longitudinal_shift_excess),\n                      face_normal, longitudinal_shift_excess);\n      elliptic::apply_boundary_condition<Linearized, void,\n                                         tmpl::list<Robin<EnabledEquations>>>(\n          boundary_condition, box, direction,\n          make_not_null(&conformal_factor_minus_one),\n          make_not_null(&lapse_times_conformal_factor_minus_one),\n          make_not_null(&shift_excess),\n          make_not_null(&n_dot_conformal_factor_gradient),\n          make_not_null(&n_dot_lapse_times_conformal_factor_gradient),\n          make_not_null(&n_dot_longitudinal_shift_excess),\n          deriv_conformal_factor, deriv_lapse_times_conformal_factor,\n          deriv_shift_excess);\n      const auto expected_n_dot_longitudinal_shift_excess =\n          pypp::call<tnsr::I<DataVector, 3>>(\n              py_module, \"robin_boundary_condition_shift\", shift_excess,\n              deriv_shift_excess, x, face_normal);\n      CHECK_ITERABLE_APPROX(get<0>(n_dot_longitudinal_shift_excess),\n                            get<0>(expected_n_dot_longitudinal_shift_excess));\n      CHECK_ITERABLE_APPROX(get<1>(n_dot_longitudinal_shift_excess),\n                            get<1>(expected_n_dot_longitudinal_shift_excess));\n      CHECK_ITERABLE_APPROX(get<2>(n_dot_longitudinal_shift_excess),\n                            get<2>(expected_n_dot_longitudinal_shift_excess));\n    }\n    const auto expected_n_dot_lapse_times_conformal_factor_gradient =\n        pypp::call<Scalar<DataVector>>(\n            py_module, \"robin_boundary_condition_scalar\",\n            lapse_times_conformal_factor_minus_one, x);\n    CHECK_ITERABLE_APPROX(\n        get(n_dot_lapse_times_conformal_factor_gradient),\n        get(expected_n_dot_lapse_times_conformal_factor_gradient));\n  }\n  const auto expected_n_dot_conformal_factor_gradient =\n      pypp::call<Scalar<DataVector>>(py_module,\n                                     \"robin_boundary_condition_scalar\",\n                                     conformal_factor_minus_one, x);\n  CHECK_ITERABLE_APPROX(get(n_dot_conformal_factor_gradient),\n                        get(expected_n_dot_conformal_factor_gradient));\n}\n\ntemplate <Xcts::Equations EnabledEquations>\nvoid test_suite() {\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::BoundaryConditions::BoundaryCondition<3>,\n      Robin<EnabledEquations>>(\"Robin\");\n  REQUIRE(dynamic_cast<const Robin<EnabledEquations>*>(created.get()) !=\n          nullptr);\n  const auto& boundary_condition =\n      dynamic_cast<const Robin<EnabledEquations>&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(boundary_condition);\n    test_copy_semantics(boundary_condition);\n    auto move_boundary_condition = boundary_condition;\n    test_move_semantics(std::move(move_boundary_condition), boundary_condition);\n  }\n  {\n    INFO(\"Properties\");\n    if constexpr (EnabledEquations == Xcts::Equations::Hamiltonian) {\n      CHECK(boundary_condition.boundary_condition_types() ==\n            std::vector<elliptic::BoundaryConditionType>{\n                1, elliptic::BoundaryConditionType::Neumann});\n    } else if constexpr (EnabledEquations ==\n                         Xcts::Equations::HamiltonianAndLapse) {\n      CHECK(boundary_condition.boundary_condition_types() ==\n            std::vector<elliptic::BoundaryConditionType>{\n                2, elliptic::BoundaryConditionType::Neumann});\n    } else if constexpr (EnabledEquations ==\n                         Xcts::Equations::HamiltonianLapseAndShift) {\n      CHECK(boundary_condition.boundary_condition_types() ==\n            std::vector<elliptic::BoundaryConditionType>{\n                5, elliptic::BoundaryConditionType::Neumann});\n    }\n  }\n  test_robin<EnabledEquations, false>(boundary_condition);\n  test_robin<EnabledEquations, true>(boundary_condition);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Xcts.BoundaryConditions.Robin\", \"[Unit][Elliptic]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"\");\n  test_suite<Xcts::Equations::Hamiltonian>();\n  test_suite<Xcts::Equations::HamiltonianAndLapse>();\n  test_suite<Xcts::Equations::HamiltonianLapseAndShift>();\n}\n\n}  // namespace Xcts::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Xcts\")\n\nset(LIBRARY_SOURCES\n  Test_Equations.cpp\n  Test_HydroQuantities.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  Domain\n  Elliptic\n  Hydro\n  InitialDataUtilities\n  Options\n  Parallel\n  Utilities\n  Xcts\n  XctsAnalyticData\n  XctsSolutions\n  )\n\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(Events)\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/Equations.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom numpy import pi\n\n\ndef hamiltonian_sources(\n    conformal_energy_density,\n    extrinsic_curvature_trace,\n    conformal_factor_minus_one,\n    conformal_matter_scale=0,\n):\n    return (\n        conformal_factor_minus_one + 1.0\n    ) ** 5 * extrinsic_curvature_trace**2 / 12.0 - 2.0 * pi * (\n        conformal_factor_minus_one + 1.0\n    ) ** (\n        5 - conformal_matter_scale\n    ) * conformal_energy_density\n\n\ndef hamiltonian_sources_conf(*args, **kwargs):\n    return hamiltonian_sources(*args, conformal_matter_scale=6, **kwargs)\n\n\ndef linearized_hamiltonian_sources(\n    conformal_energy_density,\n    extrinsic_curvature_trace,\n    conformal_factor_minus_one,\n    conformal_factor_correction,\n    conformal_matter_scale=0,\n):\n    return (\n        5.0\n        * (conformal_factor_minus_one + 1.0) ** 4\n        * extrinsic_curvature_trace**2\n        / 12.0\n        - 2.0\n        * pi\n        * (5.0 - conformal_matter_scale)\n        * (conformal_factor_minus_one + 1.0) ** (4 - conformal_matter_scale)\n        * conformal_energy_density\n    ) * conformal_factor_correction\n\n\ndef linearized_hamiltonian_sources_conf(*args, **kwargs):\n    return linearized_hamiltonian_sources(\n        *args, conformal_matter_scale=6, **kwargs\n    )\n\n\ndef distortion_hamiltonian_sources(\n    longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    conformal_factor_minus_one,\n):\n    return (\n        -1.0\n        / 32.0\n        * (conformal_factor_minus_one + 1.0) ** 5\n        * longitudinal_shift_minus_dt_conformal_metric_over_lapse_square\n    )\n\n\ndef linearized_distortion_hamiltonian_sources(\n    longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n    conformal_factor_minus_one,\n    conformal_factor_correction,\n):\n    return (\n        -5.0\n        / 32.0\n        * (conformal_factor_minus_one + 1.0) ** 4\n        * conformal_factor_correction\n        * longitudinal_shift_minus_dt_conformal_metric_over_lapse_square\n    )\n\n\ndef curved_hamiltonian_or_lapse_sources(\n    conformal_ricci_scalar, field, constant\n):\n    return (field + constant) * conformal_ricci_scalar / 8.0\n\n\ndef lapse_sources(\n    conformal_energy_density,\n    conformal_stress_trace,\n    extrinsic_curvature_trace,\n    dt_extrinsic_curvature_trace,\n    shift_dot_deriv_extrinsic_curvature_trace,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_matter_scale=0,\n):\n    return (lapse_times_conformal_factor_minus_one + 1.0) * (\n        conformal_factor_minus_one + 1.0\n    ) ** 4 * (\n        5.0 / 12.0 * extrinsic_curvature_trace**2\n        + 2.0\n        * pi\n        * (conformal_energy_density + 2.0 * conformal_stress_trace)\n        / (conformal_factor_minus_one + 1.0) ** conformal_matter_scale\n    ) + (\n        conformal_factor_minus_one + 1.0\n    ) ** 5 * (\n        shift_dot_deriv_extrinsic_curvature_trace - dt_extrinsic_curvature_trace\n    )\n\n\ndef lapse_sources_conf(*args, **kwargs):\n    return lapse_sources(*args, conformal_matter_scale=6, **kwargs)\n\n\ndef linearized_lapse_sources(\n    conformal_energy_density,\n    conformal_stress_trace,\n    extrinsic_curvature_trace,\n    dt_extrinsic_curvature_trace,\n    shift_dot_deriv_extrinsic_curvature_trace,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_correction,\n    lapse_times_conformal_factor_correction,\n    conformal_matter_scale=0,\n):\n    return (\n        (\n            4.0\n            * (lapse_times_conformal_factor_minus_one + 1.0)\n            * (conformal_factor_minus_one + 1.0) ** 3\n            * conformal_factor_correction\n            + (conformal_factor_minus_one + 1.0) ** 4\n            * lapse_times_conformal_factor_correction\n        )\n        * 5.0\n        / 12.0\n        * extrinsic_curvature_trace**2\n        + (\n            (4.0 - conformal_matter_scale)\n            * (lapse_times_conformal_factor_minus_one + 1.0)\n            * (conformal_factor_minus_one + 1.0) ** (3 - conformal_matter_scale)\n            * conformal_factor_correction\n            + (conformal_factor_minus_one + 1.0) ** (4 - conformal_matter_scale)\n            * lapse_times_conformal_factor_correction\n        )\n        * 2.0\n        * pi\n        * (conformal_energy_density + 2.0 * conformal_stress_trace)\n        + 5.0\n        * (conformal_factor_minus_one + 1.0) ** 4\n        * conformal_factor_correction\n        * (\n            shift_dot_deriv_extrinsic_curvature_trace\n            - dt_extrinsic_curvature_trace\n        )\n    )\n\n\ndef linearized_lapse_sources_conf(*args, **kwargs):\n    return linearized_lapse_sources(*args, conformal_matter_scale=6, **kwargs)\n\n\ndef distortion_hamiltonian_sources_with_lapse(\n    longitudinal_shift_minus_dt_conformal_metric_square,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n):\n    return (\n        -1.0\n        / 32.0\n        * (conformal_factor_minus_one + 1.0) ** 7\n        / (lapse_times_conformal_factor_minus_one + 1.0) ** 2\n        * longitudinal_shift_minus_dt_conformal_metric_square\n    )\n\n\ndef linearized_distortion_hamiltonian_sources_with_lapse(\n    longitudinal_shift_minus_dt_conformal_metric_square,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_correction,\n    lapse_times_conformal_factor_correction,\n):\n    return (\n        -1.0\n        / 32.0\n        * (\n            7.0\n            * (conformal_factor_minus_one + 1.0) ** 6\n            * conformal_factor_correction\n            / (lapse_times_conformal_factor_minus_one + 1.0) ** 2\n            - 2.0\n            * (conformal_factor_minus_one + 1.0) ** 7\n            / (lapse_times_conformal_factor_minus_one + 1.0) ** 3\n            * lapse_times_conformal_factor_correction\n        )\n        * longitudinal_shift_minus_dt_conformal_metric_square\n    )\n\n\ndef distortion_lapse_sources(\n    longitudinal_shift_minus_dt_conformal_metric_square,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n):\n    return (\n        7.0\n        / 32.0\n        * (conformal_factor_minus_one + 1.0) ** 6\n        / (lapse_times_conformal_factor_minus_one + 1.0)\n        * longitudinal_shift_minus_dt_conformal_metric_square\n    )\n\n\ndef linearized_distortion_lapse_sources(\n    longitudinal_shift_minus_dt_conformal_metric_square,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_correction,\n    lapse_times_conformal_factor_correction,\n):\n    return (\n        7.0\n        / 32.0\n        * (\n            6.0\n            * (conformal_factor_minus_one + 1.0) ** 5\n            / (lapse_times_conformal_factor_minus_one + 1.0)\n            * conformal_factor_correction\n            - (conformal_factor_minus_one + 1.0) ** 6\n            / (lapse_times_conformal_factor_minus_one + 1.0) ** 2\n            * lapse_times_conformal_factor_correction\n        )\n        * longitudinal_shift_minus_dt_conformal_metric_square\n    )\n\n\ndef momentum_sources(\n    conformal_momentum_density,\n    extrinsic_curvature_trace_gradient,\n    conformal_metric,\n    inv_conformal_metric,\n    minus_div_dt_conformal_metric,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_flux,\n    lapse_times_conformal_factor_flux,\n    longitudinal_shift,\n    conformal_matter_scale=0,\n):\n    return (\n        np.einsum(\n            \"ij,jk,k\",\n            longitudinal_shift,\n            conformal_metric,\n            lapse_times_conformal_factor_flux\n            / (lapse_times_conformal_factor_minus_one + 1.0)\n            - 7.0 * conformal_factor_flux / (conformal_factor_minus_one + 1.0),\n        )\n        - minus_div_dt_conformal_metric\n        + 4.0\n        / 3.0\n        * (lapse_times_conformal_factor_minus_one + 1.0)\n        / (conformal_factor_minus_one + 1.0)\n        * np.einsum(\n            \"ij,j\", inv_conformal_metric, extrinsic_curvature_trace_gradient\n        )\n        + 16.0\n        * pi\n        * (lapse_times_conformal_factor_minus_one + 1.0)\n        * (conformal_factor_minus_one + 1.0) ** (3 - conformal_matter_scale)\n        * conformal_momentum_density\n    )\n\n\ndef momentum_sources_conf(*args, **kwargs):\n    return momentum_sources(*args, conformal_matter_scale=6, **kwargs)\n\n\ndef flat_cartesian_momentum_sources(\n    conformal_momentum_density,\n    extrinsic_curvature_trace_gradient,\n    *args,\n    **kwargs,\n):\n    return momentum_sources(\n        conformal_momentum_density,\n        extrinsic_curvature_trace_gradient,\n        np.identity(3),\n        np.identity(3),\n        *args,\n        **kwargs,\n    )\n\n\ndef flat_cartesian_momentum_sources_conf(*args, **kwargs):\n    return flat_cartesian_momentum_sources(\n        *args, conformal_matter_scale=6, **kwargs\n    )\n\n\ndef linearized_momentum_sources(\n    conformal_momentum_density,\n    extrinsic_curvature_trace_gradient,\n    conformal_metric,\n    inv_conformal_metric,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_flux,\n    lapse_times_conformal_factor_flux,\n    longitudinal_shift,\n    conformal_factor_correction,\n    lapse_times_conformal_factor_correction,\n    shift_correction,\n    conformal_factor_flux_correction,\n    lapse_times_conformal_factor_flux_correction,\n    longitudinal_shift_correction,\n    conformal_matter_scale=0,\n):\n    return (\n        np.einsum(\n            \"ij,jk,k\",\n            longitudinal_shift,\n            conformal_metric,\n            (\n                lapse_times_conformal_factor_flux_correction\n                / (lapse_times_conformal_factor_minus_one + 1.0)\n                - lapse_times_conformal_factor_flux\n                / ((lapse_times_conformal_factor_minus_one + 1.0) ** 2)\n                * lapse_times_conformal_factor_correction\n            )\n            - 7.0\n            * (\n                conformal_factor_flux_correction\n                / (conformal_factor_minus_one + 1.0)\n                - conformal_factor_flux\n                / (conformal_factor_minus_one + 1.0) ** 2\n                * conformal_factor_correction\n            ),\n        )\n        + np.einsum(\n            \"ij,jk,k\",\n            longitudinal_shift_correction,\n            conformal_metric,\n            lapse_times_conformal_factor_flux\n            / (lapse_times_conformal_factor_minus_one + 1.0)\n            - 7.0 * conformal_factor_flux / (conformal_factor_minus_one + 1.0),\n        )\n        + 4.0\n        / 3.0\n        * (\n            lapse_times_conformal_factor_correction\n            / (conformal_factor_minus_one + 1.0)\n            - (lapse_times_conformal_factor_minus_one + 1.0)\n            / (conformal_factor_minus_one + 1.0) ** 2\n            * conformal_factor_correction\n        )\n        * np.einsum(\n            \"ij,j\", inv_conformal_metric, extrinsic_curvature_trace_gradient\n        )\n        + 16.0\n        * pi\n        * (\n            (3.0 - conformal_matter_scale)\n            * (lapse_times_conformal_factor_minus_one + 1.0)\n            * (conformal_factor_minus_one + 1.0) ** (2 - conformal_matter_scale)\n            * conformal_factor_correction\n            + (conformal_factor_minus_one + 1.0) ** (3 - conformal_matter_scale)\n            * lapse_times_conformal_factor_correction\n        )\n        * conformal_momentum_density\n    )\n\n\ndef linearized_momentum_sources_conf(*args, **kwargs):\n    return linearized_momentum_sources(\n        *args, conformal_matter_scale=6, **kwargs\n    )\n\n\ndef flat_cartesian_linearized_momentum_sources(\n    momentum_density, extrinsic_curvature_trace_gradient, *args, **kwargs\n):\n    return linearized_momentum_sources(\n        momentum_density,\n        extrinsic_curvature_trace_gradient,\n        np.identity(3),\n        np.identity(3),\n        *args,\n        **kwargs,\n    )\n\n\ndef flat_cartesian_linearized_momentum_sources_conf(*args, **kwargs):\n    return flat_cartesian_linearized_momentum_sources(\n        *args, conformal_matter_scale=6, **kwargs\n    )\n\n\ndef distortion_hamiltonian_sources_full(\n    momentum_density,\n    extrinsic_curvature_trace_gradient,\n    conformal_metric,\n    inv_conformal_metric,\n    minus_div_dt_conformal_metric,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_flux,\n    lapse_times_conformal_factor_flux,\n    longitudinal_shift,\n):\n    return (\n        -1.0\n        / 32.0\n        * (conformal_factor_minus_one + 1.0) ** 7\n        / (lapse_times_conformal_factor_minus_one + 1.0) ** 2\n        * np.einsum(\n            \"ij,kl,ik,jl\",\n            conformal_metric,\n            conformal_metric,\n            longitudinal_shift,\n            longitudinal_shift,\n        )\n    )\n\n\ndef flat_cartesian_distortion_hamiltonian_sources_full(\n    momentum_density, extrinsic_curvature_trace_gradient, *args, **kwargs\n):\n    return distortion_hamiltonian_sources_full(\n        momentum_density,\n        extrinsic_curvature_trace_gradient,\n        np.identity(3),\n        np.identity(3),\n        *args,\n        **kwargs,\n    )\n\n\ndef linearized_distortion_hamiltonian_sources_full(\n    momentum_density,\n    extrinsic_curvature_trace_gradient,\n    conformal_metric,\n    inv_conformal_metric,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_flux,\n    lapse_times_conformal_factor_flux,\n    longitudinal_shift,\n    conformal_factor_correction,\n    lapse_times_conformal_factor_correction,\n    shift_correction,\n    conformal_factor_flux_correction,\n    lapse_times_conformal_factor_flux_correction,\n    longitudinal_shift_correction,\n):\n    longitudinal_shift_square = np.einsum(\n        \"ij,kl,ik,jl\",\n        conformal_metric,\n        conformal_metric,\n        longitudinal_shift,\n        longitudinal_shift,\n    )\n    longitudinal_shift_dot_correction = np.einsum(\n        \"ij,kl,ik,jl\",\n        conformal_metric,\n        conformal_metric,\n        longitudinal_shift,\n        longitudinal_shift_correction,\n    )\n    return (\n        -1.0\n        / 32.0\n        * (\n            7.0\n            * (conformal_factor_minus_one + 1.0) ** 6\n            / (lapse_times_conformal_factor_minus_one + 1.0) ** 2\n            * longitudinal_shift_square\n            * conformal_factor_correction\n            - 2.0\n            * (conformal_factor_minus_one + 1.0) ** 7\n            / (lapse_times_conformal_factor_minus_one + 1.0) ** 3\n            * longitudinal_shift_square\n            * lapse_times_conformal_factor_correction\n            + 2.0\n            * (conformal_factor_minus_one + 1.0) ** 7\n            / (lapse_times_conformal_factor_minus_one + 1.0) ** 2\n            * longitudinal_shift_dot_correction\n        )\n    )\n\n\ndef flat_cartesian_linearized_distortion_hamiltonian_sources_full(\n    momentum_density,\n    extrinsic_curvature_trace_gradient,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    *args,\n    **kwargs,\n):\n    return linearized_distortion_hamiltonian_sources_full(\n        momentum_density,\n        extrinsic_curvature_trace_gradient,\n        np.identity(3),\n        np.identity(3),\n        conformal_factor_minus_one,\n        lapse_times_conformal_factor_minus_one,\n        *args,\n        **kwargs,\n    )\n\n\ndef distortion_lapse_sources_with_shift(\n    momentum_density,\n    extrinsic_curvature_trace_gradient,\n    conformal_metric,\n    inv_conformal_metric,\n    minus_div_dt_conformal_metric,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_flux,\n    lapse_times_conformal_factor_flux,\n    longitudinal_shift,\n):\n    return (\n        7.0\n        / 32.0\n        * (conformal_factor_minus_one + 1.0) ** 6\n        / (lapse_times_conformal_factor_minus_one + 1.0)\n        * np.einsum(\n            \"ij,kl,ik,jl\",\n            conformal_metric,\n            conformal_metric,\n            longitudinal_shift,\n            longitudinal_shift,\n        )\n    )\n\n\ndef flat_cartesian_distortion_lapse_sources_with_shift(\n    momentum_density, extrinsic_curvature_trace_gradient, *args, **kwargs\n):\n    return distortion_lapse_sources_with_shift(\n        momentum_density,\n        extrinsic_curvature_trace_gradient,\n        np.identity(3),\n        np.identity(3),\n        *args,\n        **kwargs,\n    )\n\n\ndef linearized_distortion_lapse_sources_with_shift(\n    momentum_density,\n    extrinsic_curvature_trace_gradient,\n    conformal_metric,\n    inv_conformal_metric,\n    conformal_factor_minus_one,\n    lapse_times_conformal_factor_minus_one,\n    conformal_factor_flux,\n    lapse_times_conformal_factor_flux,\n    longitudinal_shift,\n    conformal_factor_correction,\n    lapse_times_conformal_factor_correction,\n    shift_correction,\n    conformal_factor_flux_correction,\n    lapse_times_conformal_factor_flux_correction,\n    longitudinal_shift_correction,\n):\n    longitudinal_shift_square = np.einsum(\n        \"ij,kl,ik,jl\",\n        conformal_metric,\n        conformal_metric,\n        longitudinal_shift,\n        longitudinal_shift,\n    )\n    longitudinal_shift_dot_correction = np.einsum(\n        \"ij,kl,ik,jl\",\n        conformal_metric,\n        conformal_metric,\n        longitudinal_shift,\n        longitudinal_shift_correction,\n    )\n    return 7.0 / 32.0 * (\n        6.0\n        * (conformal_factor_minus_one + 1.0) ** 5\n        / (lapse_times_conformal_factor_minus_one + 1.0)\n        * longitudinal_shift_square\n        * conformal_factor_correction\n        - (conformal_factor_minus_one + 1.0) ** 6\n        / (lapse_times_conformal_factor_minus_one + 1.0) ** 2\n        * longitudinal_shift_square\n        * lapse_times_conformal_factor_correction\n        + 2.0\n        * (conformal_factor_minus_one + 1.0) ** 6\n        / (lapse_times_conformal_factor_minus_one + 1.0)\n        * longitudinal_shift_dot_correction\n    ) + (conformal_factor_minus_one + 1.0) ** 5 * np.einsum(\n        \"i,i\", shift_correction, extrinsic_curvature_trace_gradient\n    )\n\n\ndef flat_cartesian_linearized_distortion_lapse_sources_with_shift(\n    momentum_density, extrinsic_curvature_trace_gradient, *args, **kwargs\n):\n    return linearized_distortion_lapse_sources_with_shift(\n        momentum_density,\n        extrinsic_curvature_trace_gradient,\n        np.identity(3),\n        np.identity(3),\n        *args,\n        **kwargs,\n    )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_XctsEvents\")\n\nset(LIBRARY_SOURCES\n  Test_ObserveAdmIntegrals.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainStructure\n  Elliptic\n  GeneralRelativitySolutions\n  LinearOperators\n  SpecialRelativity\n  XctsEvents\n  XctsSolutions\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/Events/Test_ObserveAdmIntegrals.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Elliptic/Systems/Xcts/Events/ObserveAdmIntegrals.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/SpecialRelativity/LorentzBoostMatrix.hpp\"\n#include \"PointwiseFunctions/Xcts/ExtrinsicCurvature.hpp\"\n\nnamespace {\n\n/**\n * This functions tests the ADM integrals using a boosted Schwarzschild\n * solution in isotropic coordinates. To do this, we consider two frames:\n * - Inertial frame (unbarred): the one in which we have a black hole moving\n * with a `boost_speed`; and\n * - BH frame (barred): the one in which the black hole is stationary.\n *\n * Note that the Schwarzschild solution only applies in the barred frame.\n * Here's an outline of the steps taken in this function:\n * 1. Define inertial coordinates;\n * 2. Transform inertial coordinates into barred coordinates;\n * 3. Using barred coordinates, measure the Schwarzschild spacetime metric;\n * 4. Transform the barred spacetime metric into the inertial frame; and\n * 5. Decompose metric into inertial variables and compute integrals.\n */\nvoid test_local_adm_integrals(const double& distance,\n                              const std::vector<double>& prev_distances) {\n  // Define black hole parameters.\n  const double mass = 1;\n  const double boost_speed = 0.;\n  const double lorentz_factor = 1. / sqrt(1. - square(boost_speed));\n  const std::array<double, 3> boost_velocity{{0., 0., boost_speed}};\n\n  // Get Schwarzschild solution in isotropic coordinates.\n  const Xcts::Solutions::Schwarzschild solution(\n      mass, Xcts::Solutions::SchwarzschildCoordinates::Isotropic);\n\n  // Get Lorentz boost matrix.\n  // Note that `-boost_velocity` gives us a matrix in which the upper index is\n  // barred. That is, it converts vectors into the barred frame and one-forms\n  // into the inertial frame.\n  const auto boost_matrix = sr::lorentz_boost_matrix(-boost_velocity);\n\n  // Set up domain.\n  const size_t h_refinement = 1;\n  const size_t p_refinement = 6;\n  domain::creators::Sphere shell{\n      /* inner_radius */ 2 * mass,\n      /* outer_radius */ distance,\n      /* interior */ domain::creators::Sphere::Excision{},\n      /* initial_refinement */ h_refinement,\n      /* initial_number_of_grid_points */ p_refinement + 1,\n      /* use_equiangular_map */ true,\n      /* equatorial_compression */ {},\n      /* radial_partitioning */ prev_distances,\n      /* radial_distribution */ domain::CoordinateMaps::Distribution::Inverse};\n  const auto shell_domain = shell.create_domain();\n  const auto& blocks = shell_domain.blocks();\n  const auto& initial_ref_levels = shell.initial_refinement_levels();\n  const auto element_ids = initial_element_ids(initial_ref_levels);\n  const Mesh<3> mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> face_mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  // Initialize \"reduced\" integrals.\n  Scalar<double> total_adm_mass;\n  total_adm_mass.get() = 0.;\n  tnsr::I<double, 3> total_adm_linear_momentum;\n  Scalar<double> total_adm_angular_momentum_z;\n  total_adm_angular_momentum_z.get() = 0.;\n  tnsr::I<double, 3> total_center_of_mass;\n  for (int I = 0; I < 3; I++) {\n    total_adm_linear_momentum.get(I) = 0.;\n    total_center_of_mass.get(I) = 0.;\n  }\n\n  // Compute integral by summing over each element.\n  for (const auto& element_id : element_ids) {\n    // Get element information.\n    const auto current_element =\n        domain::create_initial_element(element_id, blocks, initial_ref_levels);\n    const auto& current_block = blocks.at(element_id.block_id());\n    const ElementMap<3, Frame::Inertial> logical_to_inertial_map(\n        element_id, current_block.stationary_map().get_clone());\n\n    // Get inertial coordinates.\n    const auto logical_coords = logical_coordinates(mesh);\n    const auto inertial_coords = logical_to_inertial_map(logical_coords);\n    const auto inv_jacobian =\n        logical_to_inertial_map.inv_jacobian(logical_coords);\n\n    // Transform coordinates into the barred frame.\n    auto barred_coords =\n        make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(\n            inertial_coords, 0.0);\n    for (int i = 0; i < 3; i++) {\n      for (int j = 0; j < 3; j++) {\n        barred_coords.get(i) +=\n            boost_matrix.get(i + 1, j + 1) * inertial_coords.get(j);\n      }\n    }\n\n    // Get barred spacetime variables.\n    const auto barred_spacetime_vars = solution.variables(\n        barred_coords,\n        tmpl::list<gr::Tags::Lapse<DataVector>,\n                   gr::Tags::Shift<DataVector, 3, Frame::Inertial>,\n                   gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>>{});\n    const auto& barred_lapse =\n        get<gr::Tags::Lapse<DataVector>>(barred_spacetime_vars);\n    const auto& barred_shift =\n        get<gr::Tags::Shift<DataVector, 3, Frame::Inertial>>(\n            barred_spacetime_vars);\n    const auto& barred_spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>>(\n            barred_spacetime_vars);\n\n    // Construct barred spacetime metric.\n    tnsr::aa<DataVector, 3, Frame::Inertial> barred_spacetime_metric;\n    gr::spacetime_metric(make_not_null(&barred_spacetime_metric),\n                         barred_lapse, barred_shift, barred_spatial_metric);\n\n    // Transform spacetime metric into the inertial frame.\n    auto spacetime_metric =\n        make_with_value<tnsr::aa<DataVector, 3, Frame::Inertial>>(\n            inertial_coords, 0.0);\n    for (int a = 0; a < 4; a++) {\n      for (int b = 0; b <= a; b++) {\n        for (int c = 0; c < 4; c++) {\n          for (int d = 0; d < 4; d++) {\n            spacetime_metric.get(a, b) += boost_matrix.get(c, a) *\n                                          boost_matrix.get(d, b) *\n                                          barred_spacetime_metric.get(c, d);\n          }\n        }\n      }\n    }\n\n    // Do the 3+1 decomposition.\n    const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n    const auto& inv_spatial_metric =\n        determinant_and_inverse(spatial_metric).second;\n    const auto shift = gr::shift(spacetime_metric, inv_spatial_metric);\n    const auto lapse = gr::lapse(shift, spacetime_metric);\n\n    // Do the conformal decomposition.\n    // Note that we choose the same conformal factor as the one used for the\n    // Schwarzschild solution in isotropic coordinates. Other conformal factors\n    // could also work.\n    const auto solution_conformal_factor = solution.variables(\n        barred_coords,\n        tmpl::list<Xcts::Tags::ConformalFactor<DataVector>,\n                   Xcts::Tags::ConformalFactorMinusOne<DataVector>>{});\n    const auto& conformal_factor =\n        get<Xcts::Tags::ConformalFactor<DataVector>>(solution_conformal_factor);\n    const auto& conformal_factor_minus_one =\n        get<Xcts::Tags::ConformalFactor<DataVector>>(solution_conformal_factor);\n    const auto conformal_metric = tenex::evaluate<ti::i, ti::j>(\n        spatial_metric(ti::i, ti::j) / pow<4>(conformal_factor()));\n    const auto inv_conformal_metric = tenex::evaluate<ti::I, ti::J>(\n        inv_spatial_metric(ti::I, ti::J) * pow<4>(conformal_factor()));\n\n    // Compute spatial derivatives.\n    const auto deriv_shift = partial_derivative(shift, mesh, inv_jacobian);\n    const auto deriv_spatial_metric =\n        partial_derivative(spatial_metric, mesh, inv_jacobian);\n    const auto deriv_conformal_factor =\n        partial_derivative(conformal_factor, mesh, inv_jacobian);\n    const auto deriv_conformal_metric =\n        partial_derivative(conformal_metric, mesh, inv_jacobian);\n\n    // Compute conformal Christoffel symbols.\n    const auto conformal_christoffel_second_kind = gr::christoffel_second_kind(\n        deriv_conformal_metric, inv_conformal_metric);\n    const auto conformal_christoffel_contracted = tenex::evaluate<ti::i>(\n        conformal_christoffel_second_kind(ti::J, ti::i, ti::j));\n\n    // Define variables that appear in the formulas of dt_spatial_metric.\n    const auto& x = get<0>(inertial_coords);\n    const auto& y = get<1>(inertial_coords);\n    const auto& z = get<2>(inertial_coords);\n    const auto barred_r =\n        sqrt(square(x) + square(y) + square(lorentz_factor * z));\n\n    // Compute spatial metric time derivative.\n    // Note that these formulas were derived in a Mathematica notebook\n    // specifically for this problem. Here, we are evaluating them at t = 0.\n    auto dt_spatial_metric =\n        make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(\n            inertial_coords, 0.0);\n    dt_spatial_metric.get(0, 0) =\n        (2 * mass * boost_speed * z * cube(1 + mass / barred_r)) /\n        ((1 - square(boost_speed)) * cube(barred_r));\n    dt_spatial_metric.get(1, 1) =\n        (2 * mass * boost_speed * z * cube(1 + mass / barred_r)) /\n        ((1 - square(boost_speed)) * cube(barred_r));\n    dt_spatial_metric.get(2, 2) =\n        (2 * mass * boost_speed * z * cube(1 + mass / barred_r)) /\n            (square(1 - square(boost_speed)) * cube(barred_r)) +\n        (2 * mass * cube(boost_speed) * z * cube(1 + mass / barred_r)) /\n            (square(1 - square(boost_speed)) * cube(barred_r));\n\n    // Compute extrinsic curvature and its trace.\n    const auto extrinsic_curvature =\n        gr::extrinsic_curvature(lapse, shift, deriv_shift, spatial_metric,\n                                dt_spatial_metric, deriv_spatial_metric);\n    const auto trace_extrinsic_curvature = tenex::evaluate(\n        inv_spatial_metric(ti::I, ti::J) * extrinsic_curvature(ti::i, ti::j));\n\n    // Compute face normals (related to the conformal metric)\n    auto lower_conformal_face_normal = unnormalized_face_normal(\n        face_mesh, logical_to_inertial_map, Direction<3>::lower_zeta());\n    const auto& lower_face_inv_conformal_metric =\n        dg::project_tensor_to_boundary(inv_conformal_metric, mesh,\n                                       Direction<3>::lower_zeta());\n    const auto lower_face_normal_magnitude =\n        magnitude(lower_conformal_face_normal, lower_face_inv_conformal_metric);\n    auto upper_conformal_face_normal = unnormalized_face_normal(\n        face_mesh, logical_to_inertial_map, Direction<3>::upper_zeta());\n    const auto& upper_face_inv_conformal_metric =\n        dg::project_tensor_to_boundary(inv_conformal_metric, mesh,\n                                       Direction<3>::upper_zeta());\n    const auto upper_face_normal_magnitude =\n        magnitude(upper_conformal_face_normal, upper_face_inv_conformal_metric);\n    for (size_t d = 0; d < 3; ++d) {\n      lower_conformal_face_normal.get(d) /= get(lower_face_normal_magnitude);\n      upper_conformal_face_normal.get(d) /= get(upper_face_normal_magnitude);\n    }\n    const DirectionMap<3, tnsr::i<DataVector, 3>> conformal_face_normals(\n        {std::make_pair(Direction<3>::lower_zeta(),\n                        lower_conformal_face_normal),\n         std::make_pair(Direction<3>::upper_zeta(),\n                        upper_conformal_face_normal)});\n\n    // Compute local integrals.\n    Scalar<double> local_adm_mass;\n    tnsr::I<double, 3> local_adm_linear_momentum;\n    Scalar<double> local_adm_angular_momentum_z;\n    tnsr::I<double, 3> local_center_of_mass;\n    Events::local_adm_integrals(\n        make_not_null(&local_adm_mass),\n        make_not_null(&local_adm_linear_momentum),\n        make_not_null(&local_adm_angular_momentum_z),\n        make_not_null(&local_center_of_mass), conformal_factor,\n        conformal_factor_minus_one, deriv_conformal_factor, conformal_metric,\n        inv_conformal_metric, conformal_christoffel_second_kind,\n        conformal_christoffel_contracted, spatial_metric, inv_spatial_metric,\n        extrinsic_curvature, trace_extrinsic_curvature, inertial_coords,\n        inv_jacobian, mesh, current_element, conformal_face_normals);\n    total_adm_mass.get() += get(local_adm_mass);\n    total_adm_angular_momentum_z.get() += get(local_adm_angular_momentum_z);\n    for (int I = 0; I < 3; I++) {\n      total_adm_linear_momentum.get(I) += local_adm_linear_momentum.get(I);\n      total_center_of_mass.get(I) += local_center_of_mass.get(I);\n    }\n  }\n\n  // Check result\n  auto custom_approx = Approx::custom().epsilon(10. / distance).scale(1.0);\n  CHECK(get(total_adm_mass) == custom_approx(lorentz_factor * mass));\n  CHECK(get<0>(total_adm_linear_momentum) == custom_approx(0.));\n  CHECK(get<1>(total_adm_linear_momentum) == custom_approx(0.));\n  CHECK(get<2>(total_adm_linear_momentum) ==\n        custom_approx(lorentz_factor * mass * boost_speed));\n  CHECK(get(total_adm_angular_momentum_z) == custom_approx(0.));\n  CHECK(get<0>(total_center_of_mass) == custom_approx(0.));\n  CHECK(get<1>(total_center_of_mass) == custom_approx(0.));\n  CHECK(get<2>(total_center_of_mass) == custom_approx(0.));\n}\n\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Xcts.ObserveAdmIntegrals\",\n                  \"[Unit][PointwiseFunctions]\") {\n  // Test convergence with distance\n  std::vector<double> prev_distances = {};\n  for (const double distance : std::array<double, 3>{{1.e3, 1.e4, 1.e5}}) {\n    test_local_adm_integrals(distance, prev_distances);\n    prev_distances.push_back(distance);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/Test_Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Protocols/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Xcts/Equations.hpp\"\n#include \"Elliptic/Systems/Xcts/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Elliptic/FirstOrderSystem.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/FunctionInfo.hpp\"\n\nnamespace {\n\nvoid test_equations(const DataVector& used_for_size) {\n  const double eps = 1.e-12;\n  const auto seed = std::random_device{}();\n  const double fill_result_tensors = 0.;\n  pypp::check_with_random_values<3>(\n      &Xcts::add_hamiltonian_sources<0>, \"Equations\", {\"hamiltonian_sources\"},\n      {{{0., 1.}, {-1., 1.}, {-0.5, 0.5}}}, used_for_size, eps, seed,\n      fill_result_tensors);\n  pypp::check_with_random_values<3>(\n      &Xcts::add_hamiltonian_sources<6>, \"Equations\",\n      {\"hamiltonian_sources_conf\"}, {{{0., 1.}, {-1., 1.}, {-0.5, 0.5}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<4>(\n      &Xcts::add_linearized_hamiltonian_sources<0>, \"Equations\",\n      {\"linearized_hamiltonian_sources\"},\n      {{{0., 1.}, {-1., 1.}, {-0.5, 0.5}, {-1., 1.}}}, used_for_size, eps, seed,\n      fill_result_tensors);\n  pypp::check_with_random_values<4>(\n      &Xcts::add_linearized_hamiltonian_sources<6>, \"Equations\",\n      {\"linearized_hamiltonian_sources_conf\"},\n      {{{0., 1.}, {-1., 1.}, {-0.5, 0.5}, {-1., 1.}}}, used_for_size, eps, seed,\n      fill_result_tensors);\n  pypp::check_with_random_values<2>(\n      &Xcts::add_distortion_hamiltonian_sources, \"Equations\",\n      {\"distortion_hamiltonian_sources\"}, {{{-1., 1.}, {-0.5, 0.5}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<3>(\n      &Xcts::add_linearized_distortion_hamiltonian_sources, \"Equations\",\n      {\"linearized_distortion_hamiltonian_sources\"},\n      {{{-1., 1.}, {-0.5, 0.5}, {-1., 1.}}}, used_for_size, eps, seed,\n      fill_result_tensors);\n  pypp::check_with_random_values<3>(\n      &Xcts::add_curved_hamiltonian_or_lapse_sources, \"Equations\",\n      {\"curved_hamiltonian_or_lapse_sources\"},\n      {{{-1., 1.}, {-0.5, 0.5}, {0., 1.}}}, used_for_size, eps, seed,\n      fill_result_tensors);\n  pypp::check_with_random_values<7>(\n      &Xcts::add_lapse_sources<0>, \"Equations\", {\"lapse_sources\"},\n      {{{0., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<7>(\n      &Xcts::add_lapse_sources<6>, \"Equations\", {\"lapse_sources_conf\"},\n      {{{0., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<9>(&Xcts::add_linearized_lapse_sources<0>,\n                                    \"Equations\", {\"linearized_lapse_sources\"},\n                                    {{{0., 1.},\n                                      {-1., 1.},\n                                      {-1., 1.},\n                                      {-1., 1.},\n                                      {-1., 1.},\n                                      {-0.5, 0.5},\n                                      {-0.5, 0.5},\n                                      {-1., 1.},\n                                      {-1., 1.}}},\n                                    used_for_size, eps, seed,\n                                    fill_result_tensors);\n  pypp::check_with_random_values<9>(\n      &Xcts::add_linearized_lapse_sources<6>, \"Equations\",\n      {\"linearized_lapse_sources_conf\"},\n      {{{0., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<3>(\n      &Xcts::add_distortion_hamiltonian_and_lapse_sources, \"Equations\",\n      {\"distortion_hamiltonian_sources_with_lapse\", \"distortion_lapse_sources\"},\n      {{{-1., 1.}, {-0.5, 0.5}, {-0.5, 0.5}}}, used_for_size, eps, seed,\n      fill_result_tensors);\n  pypp::check_with_random_values<5>(\n      &Xcts::add_linearized_distortion_hamiltonian_and_lapse_sources,\n      \"Equations\",\n      {\"linearized_distortion_hamiltonian_sources_with_lapse\",\n       \"linearized_distortion_lapse_sources\"},\n      {{{-1., 1.}, {-0.5, 0.5}, {-0.5, 0.5}, {-1., 1.}, {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<8>(\n      &Xcts::add_flat_cartesian_momentum_sources<0>, \"Equations\",\n      {\"flat_cartesian_distortion_hamiltonian_sources_full\",\n       \"flat_cartesian_distortion_lapse_sources_with_shift\",\n       \"flat_cartesian_momentum_sources\"},\n      {{{-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<8>(\n      &Xcts::add_flat_cartesian_momentum_sources<6>, \"Equations\",\n      {\"flat_cartesian_distortion_hamiltonian_sources_full\",\n       \"flat_cartesian_distortion_lapse_sources_with_shift\",\n       \"flat_cartesian_momentum_sources_conf\"},\n      {{{-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<10>(\n      &Xcts::add_curved_momentum_sources<0>, \"Equations\",\n      {\"distortion_hamiltonian_sources_full\",\n       \"distortion_lapse_sources_with_shift\", \"momentum_sources\"},\n      {{{-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<10>(\n      &Xcts::add_curved_momentum_sources<6>, \"Equations\",\n      {\"distortion_hamiltonian_sources_full\",\n       \"distortion_lapse_sources_with_shift\", \"momentum_sources_conf\"},\n      {{{-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<13>(\n      &Xcts::add_flat_cartesian_linearized_momentum_sources<0>, \"Equations\",\n      {\"flat_cartesian_linearized_distortion_hamiltonian_sources_full\",\n       \"flat_cartesian_linearized_distortion_lapse_sources_with_shift\",\n       \"flat_cartesian_linearized_momentum_sources\"},\n      {{{-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<13>(\n      &Xcts::add_flat_cartesian_linearized_momentum_sources<6>, \"Equations\",\n      {\"flat_cartesian_linearized_distortion_hamiltonian_sources_full\",\n       \"flat_cartesian_linearized_distortion_lapse_sources_with_shift\",\n       \"flat_cartesian_linearized_momentum_sources_conf\"},\n      {{{-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<15>(\n      &Xcts::add_curved_linearized_momentum_sources<0>, \"Equations\",\n      {\"linearized_distortion_hamiltonian_sources_full\",\n       \"linearized_distortion_lapse_sources_with_shift\",\n       \"linearized_momentum_sources\"},\n      {{{-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n  pypp::check_with_random_values<15>(\n      &Xcts::add_curved_linearized_momentum_sources<6>, \"Equations\",\n      {\"linearized_distortion_hamiltonian_sources_full\",\n       \"linearized_distortion_lapse_sources_with_shift\",\n       \"linearized_momentum_sources_conf\"},\n      {{{-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-0.5, 0.5},\n        {-0.5, 0.5},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.},\n        {-1., 1.}}},\n      used_for_size, eps, seed, fill_result_tensors);\n}\n\ntemplate <Xcts::Equations EnabledEquations, Xcts::Geometry ConformalGeometry,\n          int ConformalMatterScale>\nvoid test_computers(const DataVector& used_for_size) {\n  CAPTURE(EnabledEquations);\n  CAPTURE(ConformalGeometry);\n  CAPTURE(ConformalMatterScale);\n  using system = Xcts::FirstOrderSystem<EnabledEquations, ConformalGeometry,\n                                        ConformalMatterScale>;\n  static_assert(\n      tt::assert_conforms_to_v<system, elliptic::protocols::FirstOrderSystem>);\n  TestHelpers::elliptic::test_first_order_fluxes_computer<system>(\n      used_for_size);\n  TestHelpers::elliptic::test_first_order_sources_computer<system, false>(\n      used_for_size);\n  TestHelpers::elliptic::test_first_order_sources_computer<system, true>(\n      used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Xcts\", \"[Unit][Elliptic]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"Elliptic/Systems/Xcts\"};\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  test_equations(dv);\n  CHECK_FOR_DATAVECTORS(\n      test_computers,\n      (Xcts::Equations::Hamiltonian, Xcts::Equations::HamiltonianAndLapse,\n       Xcts::Equations::HamiltonianLapseAndShift),\n      (Xcts::Geometry::FlatCartesian, Xcts::Geometry::Curved), (0, 6, 8));\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/Test_HydroQuantities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/Systems/Xcts/HydroQuantities.hpp\"\n#include \"Elliptic/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/Binary.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/CommonVariables.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/TovStar.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/LowerSpatialFourVelocity.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Background.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts {\nnamespace {\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using analytic_solutions_and_data = tmpl::push_back<\n        Xcts::Solutions::all_analytic_solutions,\n        Xcts::AnalyticData::Binary<elliptic::analytic_data::AnalyticSolution,\n                                   Xcts::Solutions::all_analytic_solutions>>;\n    using factory_classes =\n        tmpl::map<tmpl::pair<elliptic::analytic_data::Background,\n                             analytic_solutions_and_data>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Xcts.HydroQuantities\",\n                  \"[Unit][Elliptic]\") {\n  using hydro_tags = AnalyticData::hydro_tags<DataVector>;\n  using spatial_metric_tag = gr::Tags::SpatialMetric<DataVector, 3>;\n  const Solutions::TovStar tov_star{\n      1.e-3, std::make_unique<EquationsOfState::PolytropicFluid<true>>(1., 2.),\n      RelativisticEuler::Solutions::TovCoordinates::Schwarzschild};\n  const double outer_radius = tov_star.radial_solution().outer_radius();\n  const tnsr::I<DataVector, 3> x{\n      {{{outer_radius / 2., outer_radius * 2.}, {0., 0.}, {0., 0.}}}};\n  const auto spatial_metric = get<spatial_metric_tag>(\n      tov_star.variables(x, tmpl::list<spatial_metric_tag>{}));\n  const auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Coordinates<3, Frame::Inertial>,\n          gr::Tags::SpatialMetric<DataVector, 3>,\n          elliptic::Tags::Background<elliptic::analytic_data::Background>,\n          Parallel::Tags::MetavariablesImpl<Metavariables>>,\n      db::AddComputeTags<Tags::HydroQuantitiesCompute<hydro_tags>,\n                         hydro::Tags::LowerSpatialFourVelocityCompute>>(\n      x, spatial_metric,\n      std::unique_ptr<elliptic::analytic_data::Background>(\n          std::make_unique<Solutions::TovStar>(tov_star)),\n      Metavariables{});\n  const auto expected_vars = tov_star.variables(x, hydro_tags{});\n  tmpl::for_each<hydro_tags>([&box, &expected_vars](const auto tag_v) {\n    using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n    CHECK_ITERABLE_APPROX(db::get<tag>(box), get<tag>(expected_vars));\n  });\n  const auto u_i = db::get<\n      hydro::Tags::LowerSpatialFourVelocity<DataVector, 3, Frame::Inertial>>(\n      box);\n  const auto expected_u_i =\n      make_with_value<tnsr::i<DataVector, 3>>(DataVector(2), 0.);\n  CHECK_ITERABLE_APPROX(u_i, expected_u_i);\n}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "tests/Unit/Elliptic/Systems/Xcts/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n\nstruct DataVector;\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nnamespace Xcts {\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Systems.Xcts.Tags\", \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<Tags::ConformalFactor<DataVector>>(\n      \"ConformalFactor\");\n  TestHelpers::db::test_simple_tag<Tags::LapseTimesConformalFactor<DataVector>>(\n      \"LapseTimesConformalFactor\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ShiftStrain<DataVector, 3, Frame::Inertial>>(\"ShiftStrain\");\n  TestHelpers::db::test_prefix_tag<\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>, 2>>(\n      \"Conformal(EnergyDensity)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ConformalMetric<DataVector, 3, Frame::Inertial>>(\n      \"Conformal(SpatialMetric)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ShiftBackground<DataVector, 3, Frame::Inertial>>(\"ShiftBackground\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>(\"ShiftExcess\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ShiftStrain<DataVector, 3, Frame::Inertial>>(\"ShiftStrain\");\n  TestHelpers::db::test_simple_tag<\n      Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>>(\n      \"LongitudinalShiftExcess\");\n  TestHelpers::db::test_simple_tag<\n      Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,\n                                                              Frame::Inertial>>(\n      \"LongitudinalShiftBackgroundMinusDtConformalMetric\");\n  TestHelpers::db::test_simple_tag<\n      Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataVector>>(\n      \"LongitudinalShiftMinusDtConformalMetricSquare\");\n  TestHelpers::db::test_simple_tag<\n      Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<DataVector>>(\n      \"LongitudinalShiftMinusDtConformalMetricOverLapseSquare\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ShiftDotDerivExtrinsicCurvatureTrace<DataVector>>(\n      \"ShiftDotDerivExtrinsicCurvatureTrace\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ConformalChristoffelFirstKind<DataVector, 3, Frame::Inertial>>(\n      \"ConformalChristoffelFirstKind\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ConformalChristoffelSecondKind<DataVector, 3, Frame::Inertial>>(\n      \"ConformalChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ConformalChristoffelContracted<DataVector, 3, Frame::Inertial>>(\n      \"ConformalChristoffelContracted\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ConformalRicciTensor<DataVector, 3, Frame::Inertial>>(\n      \"ConformalRicciTensor\");\n  TestHelpers::db::test_simple_tag<Tags::ConformalRicciScalar<DataVector>>(\n      \"ConformalRicciScalar\");\n}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "tests/Unit/Elliptic/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct Dummy {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Tags\", \"[Unit][Elliptic]\") {\n  TestHelpers::db::test_simple_tag<elliptic::Tags::Background<Dummy>>(\n      \"Background\");\n  TestHelpers::db::test_simple_tag<elliptic::Tags::InitialGuess<Dummy>>(\n      \"InitialGuess\");\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Triggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticTriggers\")\n\nset(LIBRARY_SOURCES\n  Test_EveryNIterations.cpp\n  Test_HasConverged.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  DataStructures\n  Elliptic\n  EventsAndTriggers\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Triggers/Test_EveryNIterations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <initializer_list>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Elliptic/Triggers/EveryNIterations.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct OptionsGroup {};\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation :\n      tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        Trigger,\n        tmpl::list<elliptic::Triggers::EveryNIterations<OptionsGroup>>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Triggers.EveryNIterations\",\n                  \"[Unit][Elliptic]\") {\n  register_classes_with_charm<\n      elliptic::Triggers::EveryNIterations<OptionsGroup>>();\n\n  const auto trigger =\n      TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n          \"EveryNIterations:\\n\"\n          \"  N: 3\\n\"\n          \"  Offset: 5\");\n\n  const auto sent_trigger = serialize_and_deserialize(trigger);\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Convergence::Tags::IterationId<OptionsGroup>>>(\n      Metavariables{}, size_t{0});\n  for (const bool expected :\n       {false, false, false, false, false, true, false, false, true, false}) {\n    CHECK(sent_trigger->is_triggered(box) == expected);\n    db::mutate<Convergence::Tags::IterationId<OptionsGroup>>(\n        [](const gsl::not_null<size_t*> iteration_id) { (*iteration_id)++; },\n        make_not_null(&box));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Triggers/Test_HasConverged.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Elliptic/Triggers/HasConverged.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct OptionsGroup {};\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        Trigger, tmpl::list<elliptic::Triggers::HasConverged<OptionsGroup>>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elliptic.Triggers.HasConverged\", \"[Unit][Elliptic]\") {\n  register_classes_with_charm<elliptic::Triggers::HasConverged<OptionsGroup>>();\n\n  const auto created =\n      TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n          \"HasConverged\");\n  const auto serialized = serialize_and_deserialize(created);\n  const auto& trigger = *serialized;\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Convergence::Tags::HasConverged<OptionsGroup>>>(\n      Metavariables{}, Convergence::HasConverged{});\n  CHECK_FALSE(trigger.is_triggered(box));\n  db::mutate<Convergence::Tags::HasConverged<OptionsGroup>>(\n      [](const gsl::not_null<Convergence::HasConverged*> has_converged) {\n        *has_converged = Convergence::HasConverged{0, 0};\n      },\n      make_not_null(&box));\n  CHECK(trigger.is_triggered(box));\n}\n"
  },
  {
    "path": "tests/Unit/Elliptic/Utilities/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EllipticUtilities\")\n\nset(LIBRARY_SOURCES\n  Test_ApplyAt.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainStructure\n  Elliptic\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Elliptic/Utilities/Test_ApplyAt.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <map>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Elliptic/Utilities/ApplyAt.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace elliptic::util {\n\nnamespace {\n\n// [apply_at_tags]\nstruct MapTag : db::SimpleTag {\n  using type = std::map<int, std::string>;\n};\nstruct NonMapTag : db::SimpleTag {\n  using type = int;\n};\nstruct NestedMapTag : db::SimpleTag {\n  using type = std::map<int, std::unordered_map<std::string, bool>>;\n};\nstruct DirectionMapTag : db::SimpleTag {\n  using type = DirectionMap<1, bool>;\n};\n// [apply_at_tags]\n\nvoid test_apply_at() {\n  const auto check = [](const auto& box) {\n    // [apply_at_example]\n    apply_at<tmpl::list<MapTag, NonMapTag, NestedMapTag>,\n             tmpl::list<NonMapTag>>(\n        [](const std::string& arg1, const int arg0,\n           const std::unordered_map<std::string, bool>& arg2) {\n          CHECK(arg0 == 1);\n          CHECK(arg1 == \"A\");\n          CHECK(arg2.at(\"key\") == true);\n        },\n        box, 0);\n    // [apply_at_example]\n    apply_at<tmpl::list<NestedMapTag, NonMapTag>, tmpl::list<NonMapTag>>(\n        [](const bool arg0, const int arg1) {\n          CHECK(arg0 == true);\n          CHECK(arg1 == 1);\n        },\n        box, std::make_tuple(0, \"key\"));\n    apply_at<tmpl::list<DirectionMapTag>, tmpl::list<>>(\n        [](const bool arg0) { CHECK(arg0); }, box, Direction<1>::lower_xi());\n  };\n  check(db::create<\n        db::AddSimpleTags<MapTag, NonMapTag, NestedMapTag, DirectionMapTag>>(\n      std::map<int, std::string>{{0, \"A\"}}, 1,\n      std::map<int, std::unordered_map<std::string, bool>>{\n          {0, {{\"key\", true}}}},\n      DirectionMap<1, bool>{{Direction<1>::lower_xi(), true}}));\n  check(tuples::TaggedTuple<MapTag, NonMapTag, NestedMapTag, DirectionMapTag>(\n      std::map<int, std::string>{{0, \"A\"}}, 1,\n      std::map<int, std::unordered_map<std::string, bool>>{\n          {0, {{\"key\", true}}}},\n      DirectionMap<1, bool>{{Direction<1>::lower_xi(), true}}));\n}\n\nvoid test_mutate_apply_at() {\n  const auto check = [](auto box) {\n    // [mutate_apply_at_example]\n    mutate_apply_at<tmpl::list<MapTag>, tmpl::list<NonMapTag, NestedMapTag>,\n                    tmpl::list<NonMapTag>>(\n        [](const gsl::not_null<std::string*> mutate_arg, const int arg0,\n           const std::unordered_map<std::string, bool>& arg2) {\n          *mutate_arg = \"B\";\n          CHECK(arg0 == 1);\n          CHECK(arg2.at(\"key\") == true);\n        },\n        make_not_null(&box), 0);\n    // [mutate_apply_at_example]\n    CHECK(db::get<MapTag>(box) == std::map<int, std::string>{{0, \"B\"}});\n    mutate_apply_at<tmpl::list<NestedMapTag, NonMapTag>, tmpl::list<>,\n                    tmpl::list<NonMapTag>>(\n        [](const gsl::not_null<bool*> arg0, const gsl::not_null<int*> arg1) {\n          CHECK(*arg0 == true);\n          *arg0 = false;\n          CHECK(*arg1 == 1);\n          *arg1 = 2;\n        },\n        make_not_null(&box), std::make_tuple(0, \"key\"));\n    CHECK(db::get<NestedMapTag>(box) ==\n          std::map<int, std::unordered_map<std::string, bool>>{\n              {0, {{\"key\", false}}}});\n    CHECK(db::get<NonMapTag>(box) == 2);\n    mutate_apply_at<tmpl::list<DirectionMapTag>, tmpl::list<>, tmpl::list<>>(\n        [](const gsl::not_null<bool*> mutate_arg) {\n          CHECK(*mutate_arg);\n          *mutate_arg = false;\n        },\n        make_not_null(&box), Direction<1>::lower_xi());\n    CHECK(db::get<DirectionMapTag>(box) ==\n          DirectionMap<1, bool>{{Direction<1>::lower_xi(), false}});\n    struct Mutator {\n      static void apply(const gsl::not_null<bool*> mutate_arg) {\n        CHECK_FALSE(*mutate_arg);\n        *mutate_arg = true;\n      }\n    };\n    mutate_apply_at<tmpl::list<DirectionMapTag>, tmpl::list<>, tmpl::list<>>(\n        Mutator{}, make_not_null(&box), Direction<1>::lower_xi());\n    CHECK(db::get<DirectionMapTag>(box) ==\n          DirectionMap<1, bool>{{Direction<1>::lower_xi(), true}});\n  };\n  check(db::create<\n        db::AddSimpleTags<MapTag, NonMapTag, NestedMapTag, DirectionMapTag>>(\n      std::map<int, std::string>{}, 1,\n      std::map<int, std::unordered_map<std::string, bool>>{\n          {0, {{\"key\", true}}}},\n      DirectionMap<1, bool>{{Direction<1>::lower_xi(), true}}));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.DataBox.ApplyAt\",\n                  \"[Unit][DataStructures]\") {\n  test_apply_at();\n  test_mutate_apply_at();\n}\n\n}  // namespace elliptic::util\n"
  },
  {
    "path": "tests/Unit/Evolution/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EvolutionActions\")\n\nset(LIBRARY_SOURCES\n  Test_RunEventsAndDenseTriggers.cpp\n  Test_RunEventsAndTriggers.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  ErrorHandling\n  EventsAndDenseTriggers\n  Parallel\n  Time\n  Utilities\n  )\n\n"
  },
  {
    "path": "tests/Unit/Evolution/Actions/Test_RunEventsAndDenseTriggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/Actions/RunEventsAndDenseTriggers.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace {\nstruct EvolvedVar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nusing EvolvedVariables = Variables<tmpl::list<EvolvedVar>>;\n\ntemplate <typename T, typename Label = void>\nstruct PostprocessedVar : db::SimpleTag {\n  using type = T;\n};\n\nnamespace labels {\nstruct A;\nstruct B;\n}  // namespace labels\n\nusing extra_data =\n    tmpl::list<PostprocessedVar<Scalar<DataVector>, labels::A>,\n               PostprocessedVar<Scalar<DataVector>, labels::B>,\n               PostprocessedVar<Scalar<double>>, PostprocessedVar<std::string>>;\nusing all_data = tmpl::push_front<extra_data, ::Tags::Time, EvolvedVar>;\nusing DataTuple = tuples::tagged_tuple_from_typelist<all_data>;\n\nconst tuples::tagged_tuple_from_typelist<extra_data> initial_extra_data{\n    Scalar<DataVector>{{{{123.0}}}}, Scalar<DataVector>{{{{456.0}}}},\n    Scalar<double>{{{789.0}}}, \"Initial\"};\n\nclass TestTrigger : public DenseTrigger {\n public:\n  TestTrigger() = default;\n  explicit TestTrigger(CkMigrateMessage* const msg) : DenseTrigger(msg) {}\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestTrigger);  // NOLINT\n#pragma GCC diagnostic pop\n\n  // All triggers are evaluated once at the start of the evolution, so\n  // we have to handle that call and set up for triggering at the\n  // interesting time.\n  TestTrigger(const double init_time, const double trigger_time,\n              const std::optional<bool>& is_triggered,\n              const std::optional<double>& next_trigger)\n      : init_time_(init_time),\n        trigger_time_(trigger_time),\n        is_triggered_(is_triggered),\n        next_trigger_(next_trigger) {}\n\n  using is_triggered_return_tags = tmpl::list<>;\n  using is_triggered_argument_tags = tmpl::list<Tags::Time>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<bool> is_triggered(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/,\n      const double time) const {\n    if (time == trigger_time_) {\n      return is_triggered_;\n    } else {\n      // First initialization call.\n      CHECK(time == init_time_);\n      return false;\n    }\n  }\n\n  using next_check_time_return_tags = tmpl::list<>;\n  using next_check_time_argument_tags = tmpl::list<Tags::Time>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<double> next_check_time(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/,\n      const double time) const {\n    if (time == trigger_time_) {\n      return next_trigger_;\n    } else {\n      // First initialization call.\n      CHECK(time == init_time_);\n      return trigger_time_;\n    }\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    DenseTrigger::pup(p);\n    p | init_time_;\n    p | trigger_time_;\n    p | is_triggered_;\n    p | next_trigger_;\n  }\n\n private:\n  double init_time_ = std::numeric_limits<double>::signaling_NaN();\n  double trigger_time_ = std::numeric_limits<double>::signaling_NaN();\n  std::optional<bool> is_triggered_{};\n  std::optional<double> next_trigger_{};\n};\n\nPUP::able::PUP_ID TestTrigger::my_PUP_ID = 0;  // NOLINT\n\nstruct TestEvent : public Event {\n  TestEvent() = default;\n  explicit TestEvent(CkMigrateMessage* const /*msg*/) {}\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestEvent);  // NOLINT\n#pragma GCC diagnostic pop\n\n  explicit TestEvent(const bool needs_evolved_variables)\n      : needs_evolved_variables_(needs_evolved_variables) {}\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n\n  using return_tags = tmpl::list<>;\n  // Because of a poor choice in the argument order, operator() cannot\n  // take a parameter pack of arguments, so we pull out the objects\n  // ourselves.\n  using argument_tags = tmpl::list<Tags::DataBox>;\n\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename Component>\n  void operator()(const db::DataBox<DbTags>& box,\n                  Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ArrayIndex& /*array_index*/,\n                  const Component* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    tmpl::as_pack<all_data>([&](auto... tags_v) {\n      calls.emplace_back(db::get<tmpl::type_from<decltype(tags_v)>>(box)...);\n    });\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    // We use the triggers to control readiness for this test.\n    return true;\n  }\n\n  bool needs_evolved_variables() const override {\n    return needs_evolved_variables_;\n  }\n\n  // `modifications` are pairs of Tag{} and functional to compute tag\n  // from the evolved variables.\n  template <typename... Modifications>\n  static void check_calls(\n      const std::vector<std::pair<double, EvolvedVariables>>& expected,\n      Modifications... modifications) {\n    CAPTURE(get_output(expected));\n    CAPTURE(get_output(calls));\n    REQUIRE(calls.size() == expected.size());\n    for (size_t i = 0; i < expected.size(); ++i) {\n      CHECK(get<::Tags::Time>(calls[i]) == expected[i].first);\n      const auto& expected_evolved = get<EvolvedVar>(expected[i].second);\n\n      const auto modify = [&](auto tag_v, auto expected_value) {\n        using tag = decltype(tag_v);\n        [[maybe_unused]] const auto apply_modification =\n            [&](const auto& modification) {\n              if constexpr (std::is_same_v<decltype(modification.first), tag>) {\n                expected_value = std::decay_t<decltype(expected_value)>(\n                    modification.second(expected_evolved));\n              }\n              return 0;\n            };\n        expand_pack(apply_modification(modifications)...);\n        return expected_value;\n      };\n\n      CHECK(get<EvolvedVar>(calls[i]) ==\n            modify(EvolvedVar{}, expected_evolved));\n      tmpl::for_each<extra_data>([&](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK(get<tag>(calls[i]) ==\n              modify(tag{}, get<tag>(initial_extra_data)));\n      });\n    }\n    calls.clear();\n  }\n\n private:\n  bool needs_evolved_variables_ = false;\n\n  static std::vector<DataTuple> calls;\n};\n\nstd::vector<DataTuple> TestEvent::calls{};\n\nPUP::able::PUP_ID TestEvent::my_PUP_ID = 0;  // NOLINT\n\nstruct System {\n  using variables_tag = Tags::Variables<tmpl::list<EvolvedVar>>;\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using variables_tag = typename metavariables::system::variables_tag;\n  using simple_tags_from_options =\n      tmpl::push_front<extra_data, Tags::TimeStepId, Tags::TimeStep, Tags::Time,\n                       ::Tags::PreviousTriggerTime, variables_tag,\n                       Tags::HistoryEvolvedVariables<variables_tag>,\n                       ::Tags::EventsAndDenseTriggers,\n                       domain::Tags::NeighborMesh<1>, domain::Tags::Element<1>>;\n  using compute_tags = time_stepper_ref_tags<TimeStepper>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 tmpl::list<>, compute_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<evolution::Actions::RunEventsAndDenseTriggers<\n              typename Metavariables::postprocessors>>>>;\n};\n\ntemplate <typename Postprocessors>\nstruct Metavariables {\n  using postprocessors = Postprocessors;\n  using system = System;\n  using component_list = tmpl::list<Component<Metavariables>>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::ConcreteTimeStepper<TimeStepper>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DenseTrigger, tmpl::list<TestTrigger>>,\n                  tmpl::pair<Event, tmpl::list<TestEvent>>>;\n  };\n};\n\ntemplate <typename Metavariables>\nbool run_if_ready(\n    const gsl::not_null<ActionTesting::MockRuntimeSystem<Metavariables>*>\n        runner) {\n  using component = Component<Metavariables>;\n\n  const auto get_data = [&runner]() {\n    return tmpl::as_pack<all_data>([&runner](auto... tags_v) {\n      return DataTuple(\n          ActionTesting::get_databox_tag<component,\n                                         tmpl::type_from<decltype(tags_v)>>(\n              *runner, 0)...);\n    });\n  };\n\n  const auto data_before = get_data();\n  const bool was_ready =\n      ActionTesting::next_action_if_ready<component>(runner, 0);\n  const auto data_after = get_data();\n  CHECK(data_before == data_after);\n  return was_ready;\n}\n\ntemplate <typename TestCase>\nvoid test(const bool time_runs_forward) {\n  using metavars = typename TestCase::metavariables;\n  using MockRuntimeSystem = typename TestCase::MockRuntimeSystem;\n  using component = Component<metavars>;\n  using system = typename metavars::system;\n  using variables_tag = typename system::variables_tag;\n  using VarsType = typename variables_tag::type;\n  using DtVarsType =\n      typename db::add_tag_prefix<::Tags::dt, variables_tag>::type;\n  using History = TimeSteppers::History<VarsType>;\n\n  const Slab slab(0.0, 4.0);\n  const TimeStepId time_step_id(time_runs_forward, 0,\n                                slab.start() + slab.duration() / 2);\n  const TimeDelta exact_step_size =\n      (time_runs_forward ? 1 : -1) * slab.duration() / 4;\n  const double start_time = time_step_id.step_time().value();\n  const double step_size = exact_step_size.value();\n  const double step_center = start_time + 0.5 * step_size;\n  const double done_time = (time_runs_forward ? 1.0 : -1.0) *\n                           std::numeric_limits<double>::infinity();\n  const VarsType initial_vars{1, 8.0};\n  const VarsType stored_vars{1, 123.0};\n  const DtVarsType deriv_vars{1, 1.0};\n  const VarsType center_vars = initial_vars + 0.5 * step_size * deriv_vars;\n\n  const auto set_up_component =\n      [&deriv_vars, &exact_step_size, &initial_vars, &start_time, &stored_vars,\n       &time_step_id](\n          const gsl::not_null<MockRuntimeSystem*> runner,\n          const std::vector<std::tuple<double, std::optional<bool>,\n                                       std::optional<double>, bool>>&\n              triggers) {\n        History history(1);\n        history.insert(time_step_id, initial_vars, deriv_vars);\n\n        EventsAndDenseTriggers::ConstructionType events_and_dense_triggers{};\n        events_and_dense_triggers.reserve(triggers.size());\n        for (auto [trigger_time, is_triggered, next_trigger,\n                   needs_evolved_variables] : triggers) {\n          events_and_dense_triggers.push_back(\n              {std::make_unique<TestTrigger>(start_time, trigger_time,\n                                             is_triggered, next_trigger),\n               make_vector<std::unique_ptr<Event>>(\n                   std::make_unique<TestEvent>(needs_evolved_variables))});\n        }\n\n        tmpl::as_pack<extra_data>([&](auto... tags_v) {\n          ActionTesting::emplace_array_component_and_initialize<component>(\n              runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n              0, {}, time_step_id, exact_step_size, start_time,\n              std::optional<double>{}, stored_vars, std::move(history),\n              EventsAndDenseTriggers(std::move(events_and_dense_triggers)),\n              typename domain::Tags::NeighborMesh<1>::type{},\n              Element<1>{ElementId<1>{0}, {}},\n              get<tmpl::type_from<decltype(tags_v)>>(initial_extra_data)...);\n        });\n        ActionTesting::set_phase(runner, Parallel::Phase::Testing);\n      };\n\n  // Tests start here\n\n  // Nothing should happen in self-start\n  {\n    // This isn't a valid time for the trigger to reschedule to (it is\n    // in the past), but the triggers should be completely ignored in\n    // this check.\n    const double invalid_time = start_time - step_size;\n\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    set_up_component(&runner,\n                     {{invalid_time, std::nullopt, std::nullopt, false}});\n    {\n      auto& box =\n          ActionTesting::get_databox<component>(make_not_null(&runner), 0);\n      db::mutate<Tags::TimeStepId>(\n          [](const gsl::not_null<TimeStepId*> id) {\n            *id = TimeStepId(id->time_runs_forward(), -1, id->step_time());\n          },\n          make_not_null(&box));\n    }\n    CHECK(run_if_ready(make_not_null(&runner)));\n    TestEvent::check_calls({});\n  }\n\n  // No triggers\n  {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    set_up_component(&runner, {});\n    CHECK(run_if_ready(make_not_null(&runner)));\n    TestEvent::check_calls({});\n  }\n\n  // Triggers too far in the future\n  const auto check_not_reached = [&set_up_component, &start_time, &step_size](\n                                     const std::optional<bool>& is_triggered) {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    set_up_component(&runner, {{start_time + 1.5 * step_size, is_triggered,\n                                std::nullopt, false}});\n    CHECK(run_if_ready(make_not_null(&runner)));\n    TestEvent::check_calls({});\n  };\n  check_not_reached(std::nullopt);\n  check_not_reached(true);\n  check_not_reached(false);\n\n  // Triggers at the end of the step (should not run)\n  const auto check_step_end = [&set_up_component, &start_time, &step_size](\n                                  const std::optional<bool>& is_triggered) {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    set_up_component(\n        &runner, {{start_time + step_size, is_triggered, std::nullopt, false}});\n    CHECK(run_if_ready(make_not_null(&runner)));\n    TestEvent::check_calls({});\n  };\n  check_step_end(std::nullopt);\n  check_step_end(true);\n  check_step_end(false);\n\n  // Trigger isn't ready\n  {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    set_up_component(&runner, {{step_center, std::nullopt, start_time, false}});\n    CHECK(not run_if_ready(make_not_null(&runner)));\n    TestEvent::check_calls({});\n  }\n\n  // Variables not needed\n  const auto check_not_needed = [&](const bool reschedule) {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    const auto next_check =\n        reschedule ? std::optional{done_time} : std::nullopt;\n    set_up_component(&runner, {{step_center, true, next_check, false}});\n    CHECK(run_if_ready(make_not_null(&runner)) == reschedule);\n    TestEvent::check_calls({{step_center, stored_vars}});\n  };\n  check_not_needed(true);\n  check_not_needed(false);\n\n  // Variables not needed at initial time\n  const auto check_not_needed_initial = [&](const bool reschedule) {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    const auto next_check =\n        reschedule ? std::optional{done_time} : std::nullopt;\n    set_up_component(&runner, {{start_time, true, next_check, false}});\n    CHECK(run_if_ready(make_not_null(&runner)) == reschedule);\n    TestEvent::check_calls({{start_time, stored_vars}});\n  };\n  check_not_needed_initial(true);\n  check_not_needed_initial(false);\n\n  // Variables needed\n  const auto check_needed = [&](const bool reschedule) {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    const auto next_check =\n        reschedule ? std::optional{done_time} : std::nullopt;\n    set_up_component(&runner, {{step_center, true, next_check, true}});\n    TestCase::check_dense(&runner, reschedule, {{step_center, center_vars}});\n  };\n  check_needed(true);\n  check_needed(false);\n\n  // Variables needed at initial time\n  const auto check_needed_initial = [&](const bool reschedule) {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    const auto next_check =\n        reschedule ? std::optional{done_time} : std::nullopt;\n    set_up_component(&runner, {{start_time, true, next_check, true}});\n    TestCase::check_dense(&runner, reschedule, {{start_time, initial_vars}});\n  };\n  check_needed_initial(true);\n  check_needed_initial(false);\n\n  // Missing dense output data\n  const auto check_missing_dense_data = [&](const bool data_needed) {\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::Rk3HesthavenSsp>()}};\n    set_up_component(&runner, {{step_center, true, done_time, data_needed}});\n    {\n      auto& box =\n          ActionTesting::get_databox<component>(make_not_null(&runner), 0);\n      db::mutate<Tags::HistoryEvolvedVariables<variables_tag>>(\n          [&deriv_vars, &initial_vars, &time_step_id](\n              const gsl::not_null<History*> history,\n              const TimeStepper& time_stepper) {\n            *history = History(3);\n            history->insert(time_step_id, initial_vars, deriv_vars);\n            history->insert(\n                time_stepper.next_time_id(\n                    time_step_id,\n                    (time_step_id.time_runs_forward() ? 1 : -1) *\n                        time_step_id.step_time().slab().duration() / 4),\n                initial_vars, deriv_vars);\n          },\n          make_not_null(&box), db::get<Tags::TimeStepper<TimeStepper>>(box));\n    }\n    CHECK(run_if_ready(make_not_null(&runner)));\n    if (data_needed) {\n      TestEvent::check_calls({});\n    } else {\n      // If we don't need the data, it shouldn't matter whether it is missing.\n      TestEvent::check_calls({{step_center, stored_vars}});\n    }\n  };\n  check_missing_dense_data(true);\n  check_missing_dense_data(false);\n\n  // Multiple triggers\n  {\n    const double second_trigger = start_time + 0.75 * step_size;\n    MockRuntimeSystem runner{\n        {std::make_unique<TimeSteppers::AdamsBashforth>(1)}};\n    set_up_component(&runner, {{step_center, true, done_time, true},\n                               {second_trigger, true, done_time, true}});\n    TestCase::check_dense(\n        &runner, true,\n        {{step_center, center_vars},\n         {second_trigger, initial_vars + 0.75 * step_size * deriv_vars}});\n  }\n}\n\nnamespace test_postprocessors {\nstruct SetA {\n  using return_tags =\n      tmpl::list<PostprocessedVar<Scalar<DataVector>, labels::A>>;\n  using argument_tags = tmpl::list<EvolvedVar>;\n  static void apply(const gsl::not_null<Scalar<DataVector>*> postprocessed_a,\n                    const Scalar<DataVector>& evolved) {\n    get(*postprocessed_a) = 2.0 * get(evolved);\n  }\n};\n\nstruct SetAB {\n  using return_tags =\n      tmpl::list<PostprocessedVar<Scalar<DataVector>, labels::A>,\n                 PostprocessedVar<Scalar<DataVector>, labels::B>>;\n  using argument_tags = tmpl::list<EvolvedVar>;\n  static void apply(const gsl::not_null<Scalar<DataVector>*> postprocessed_a,\n                    const gsl::not_null<Scalar<DataVector>*> postprocessed_b,\n                    const Scalar<DataVector>& evolved) {\n    get(*postprocessed_a) = 3.0 * get(evolved);\n    get(*postprocessed_b) = 4.0 * get(evolved);\n  }\n};\n\nstruct SetDouble {\n  using return_tags = tmpl::list<PostprocessedVar<Scalar<double>>>;\n  using argument_tags = tmpl::list<EvolvedVar>;\n  static void apply(const gsl::not_null<Scalar<double>*> postprocessed_double,\n                    const Scalar<DataVector>& evolved) {\n    get(*postprocessed_double) = 5.0 * get(evolved)[0];\n  }\n\n  // Test is_ready\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static bool is_ready(\n      const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,\n      const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*component*/) {\n    return true;\n  }\n};\n\nstruct SetDoubleAndString {\n  using return_tags = tmpl::list<PostprocessedVar<Scalar<double>>,\n                                 PostprocessedVar<std::string>>;\n  using argument_tags = tmpl::list<EvolvedVar>;\n  static void apply(const gsl::not_null<Scalar<double>*> postprocessed_double,\n                    const gsl::not_null<std::string*> postprocessed_string,\n                    const Scalar<DataVector>& evolved) {\n    get(*postprocessed_double) = 6.0 * get(evolved)[0];\n    *postprocessed_string = \"Processed\";\n  }\n};\n\nstruct ModifyEvolved {\n  using return_tags = tmpl::list<EvolvedVar>;\n  using argument_tags = tmpl::list<>;\n  static void apply(const gsl::not_null<Scalar<DataVector>*> evolved) {\n    get(*evolved) *= -1.0;\n  }\n};\n\nstruct NotReady {\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n  static void apply() {}\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ParallelComponent>\n  static bool is_ready(\n      const gsl::not_null<db::DataBox<DbTagsList>*> /*box*/,\n      const gsl::not_null<tuples::TaggedTuple<InboxTags...>*> /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const ParallelComponent* const /*component*/) {\n    return false;\n  }\n};\n}  // namespace test_postprocessors\n\nnamespace test_cases {\nstruct NoPostprocessors {\n  using postprocessors = tmpl::list<>;\n  using metavariables = Metavariables<postprocessors>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavariables>;\n  static void check_dense(\n      const gsl::not_null<MockRuntimeSystem*> runner, const bool should_run,\n      const std::vector<std::pair<double, EvolvedVariables>>& expected_calls) {\n    CHECK(run_if_ready(runner) == should_run);\n    TestEvent::check_calls(expected_calls);\n  }\n};\n\nstruct NotReady {\n  using postprocessors = tmpl::list<test_postprocessors::NotReady>;\n  using metavariables = Metavariables<postprocessors>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavariables>;\n  static void check_dense(\n      const gsl::not_null<MockRuntimeSystem*> runner, const bool /*should_run*/,\n      const std::vector<\n          std::pair<double, EvolvedVariables>>& /*expected_calls*/) {\n    CHECK(not run_if_ready(runner));\n    TestEvent::check_calls({});\n  }\n};\n\nstruct PostprocessA {\n  using postprocessors =\n      tmpl::list<AlwaysReadyPostprocessor<test_postprocessors::SetA>>;\n  using metavariables = Metavariables<postprocessors>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavariables>;\n  static void check_dense(\n      const gsl::not_null<MockRuntimeSystem*> runner, const bool should_run,\n      const std::vector<std::pair<double, EvolvedVariables>>& expected_calls) {\n    CHECK(run_if_ready(runner) == should_run);\n    // We define the lambda out-of-line for nvcc compatibility\n    auto multiplier = [](const Scalar<DataVector>& v) { return 2.0 * get(v); };\n    TestEvent::check_calls(\n        expected_calls,\n        std::pair{PostprocessedVar<Scalar<DataVector>, labels::A>{},\n                  std::move(multiplier)});\n  }\n};\n\nstruct PostprocessAll {\n  // Test setting the same thing multiple times\n  using postprocessors = tmpl::list<\n      AlwaysReadyPostprocessor<test_postprocessors::SetAB>,\n      AlwaysReadyPostprocessor<test_postprocessors::SetA>,\n      AlwaysReadyPostprocessor<test_postprocessors::SetDoubleAndString>,\n      test_postprocessors::SetDouble>;\n  using metavariables = Metavariables<postprocessors>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavariables>;\n  static void check_dense(\n      const gsl::not_null<MockRuntimeSystem*> runner, const bool should_run,\n      const std::vector<std::pair<double, EvolvedVariables>>& expected_calls) {\n    CHECK(run_if_ready(runner) == should_run);\n    // We define the lambdas out-of-line for nvcc compatibility\n    auto multiplier0 = [](const Scalar<DataVector>& v) { return 2.0 * get(v); };\n    auto multiplier1 = [](const Scalar<DataVector>& v) { return 4.0 * get(v); };\n    auto multiplier2 = [](const Scalar<DataVector>& v) {\n      return 5.0 * get(v)[0];\n    };\n    auto multiplier3 = [](const Scalar<DataVector>& /*v*/) {\n      return \"Processed\";\n    };\n    TestEvent::check_calls(\n        expected_calls,\n        std::pair{PostprocessedVar<Scalar<DataVector>, labels::A>{},\n                  std::move(multiplier0)},\n        std::pair{PostprocessedVar<Scalar<DataVector>, labels::B>{},\n                  std::move(multiplier1)},\n        std::pair{PostprocessedVar<Scalar<double>>{}, std::move(multiplier2)},\n        std::pair{PostprocessedVar<std::string>{}, std::move(multiplier3)});\n  }\n};\n\nstruct PostprocessEvolved {\n  using postprocessors =\n      tmpl::list<AlwaysReadyPostprocessor<test_postprocessors::ModifyEvolved>,\n                 AlwaysReadyPostprocessor<test_postprocessors::SetA>>;\n  using metavariables = Metavariables<postprocessors>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavariables>;\n  static void check_dense(\n      const gsl::not_null<MockRuntimeSystem*> runner, const bool should_run,\n      const std::vector<std::pair<double, EvolvedVariables>>& expected_calls) {\n    CHECK(run_if_ready(runner) == should_run);\n    // We define the lambdas out-of-line for nvcc compatibility\n    auto helper0 = [](const Scalar<DataVector>& v) { return -get(v); };\n    auto helper1 = [](const Scalar<DataVector>& v) { return -2.0 * get(v); };\n    TestEvent::check_calls(\n        expected_calls, std::pair{EvolvedVar{}, std::move(helper0)},\n        std::pair{PostprocessedVar<Scalar<DataVector>, labels::A>{},\n                  std::move(helper1)});\n  }\n};\n}  // namespace test_cases\n\nEventsAndDenseTriggers make_events_and_dense_triggers() {\n  const Slab slab(0.0, 4.0);\n  const TimeStepId time_step_id(true, 0, slab.start() + slab.duration() / 2);\n  const TimeDelta exact_step_size = slab.duration() / 4;\n  const double start_time = time_step_id.step_time().value();\n  const double step_size = exact_step_size.value();\n  const double step_center = start_time + 0.5 * step_size;\n  const double second_trigger = start_time + 0.75 * step_size;\n  const double done_time = std::numeric_limits<double>::infinity();\n\n  EventsAndDenseTriggers::ConstructionType events_and_dense_triggers{};\n  const std::vector<\n      std::tuple<double, std::optional<bool>, std::optional<double>, bool>>\n      triggers{{step_center, true, done_time, true},\n               {second_trigger, true, done_time, true}};\n  events_and_dense_triggers.reserve(triggers.size());\n  for (auto [trigger_time, is_triggered, next_trigger,\n             needs_evolved_variables] : triggers) {\n    events_and_dense_triggers.push_back(\n        {std::make_unique<TestTrigger>(start_time, trigger_time, is_triggered,\n                                       next_trigger),\n         make_vector<std::unique_ptr<Event>>(\n             std::make_unique<TestEvent>(needs_evolved_variables))});\n  }\n  return EventsAndDenseTriggers(std::move(events_and_dense_triggers));\n}\n\nvoid test_p_refine() {\n  auto box = db::create<db::AddSimpleTags<::Tags::EventsAndDenseTriggers,\n                                          ::Tags::PreviousTriggerTime,\n                                          ::Tags::TimeStepId, ::Tags::Time>>(\n      make_events_and_dense_triggers(), std::optional<double>{},\n      TimeStepId(true, 5, Slab(0.0, 4.0).start()), 0.0);\n  const double next_trigger_time =\n      db::get_mutable_reference<::Tags::EventsAndDenseTriggers>(\n          make_not_null(&box))\n          .next_trigger(box);\n\n  const Element<1> element{ElementId<1>{0}, DirectionMap<1, Neighbors<1>>{}};\n  const Mesh<1> mesh{2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  db::mutate_apply<evolution::Actions::ProjectRunEventsAndDenseTriggers>(\n      make_not_null(&box), std::make_pair(mesh, element));\n\n  // There is no comparison operator for EventsAndDenseTriggers\n  CHECK(db::get_mutable_reference<::Tags::EventsAndDenseTriggers>(\n            make_not_null(&box))\n            .next_trigger(box) == next_trigger_time);\n  CHECK(db::get<::Tags::PreviousTriggerTime>(box) == std::nullopt);\n}\n\nvoid test_h_split() {\n  tuples::TaggedTuple<::Tags::EventsAndDenseTriggers> parent_items{\n      make_events_and_dense_triggers()};\n  auto box = db::create<db::AddSimpleTags<::Tags::EventsAndDenseTriggers,\n                                          ::Tags::PreviousTriggerTime,\n                                          ::Tags::TimeStepId, ::Tags::Time>>(\n      EventsAndDenseTriggers{}, std::optional<double>{},\n      TimeStepId(true, 5, Slab(0.0, 4.0).start()), 0.0);\n  const double next_trigger_time =\n      get<::Tags::EventsAndDenseTriggers>(parent_items).next_trigger(box);\n\n  db::mutate_apply<evolution::Actions::ProjectRunEventsAndDenseTriggers>(\n      make_not_null(&box), std::as_const(parent_items));\n\n  // There is no comparison operator for EventsAndDenseTriggers\n  CHECK(db::get_mutable_reference<::Tags::EventsAndDenseTriggers>(\n            make_not_null(&box))\n            .next_trigger(box) == next_trigger_time);\n  CHECK(db::get<::Tags::PreviousTriggerTime>(box) == std::nullopt);\n}\n\nvoid test_h_join() {\n  std::unordered_map<ElementId<1>,\n                     tuples::TaggedTuple<::Tags::EventsAndDenseTriggers>>\n      children_items{};\n  children_items.emplace(ElementId<1>(2, {{{2, 2}}}),\n                         make_events_and_dense_triggers());\n  children_items.emplace(ElementId<1>(2, {{{2, 3}}}),\n                         make_events_and_dense_triggers());\n  auto box = db::create<db::AddSimpleTags<::Tags::EventsAndDenseTriggers,\n                                          ::Tags::PreviousTriggerTime,\n                                          ::Tags::TimeStepId, ::Tags::Time>>(\n      EventsAndDenseTriggers{}, std::optional<double>{},\n      TimeStepId(true, 5, Slab(0.0, 4.0).start()), 0.0);\n  const double next_trigger_time =\n      get<::Tags::EventsAndDenseTriggers>(children_items.begin()->second)\n          .next_trigger(box);\n  for (auto& child_items : children_items) {\n    REQUIRE(get<::Tags::EventsAndDenseTriggers>(child_items.second)\n                .next_trigger(box) == next_trigger_time);\n  }\n\n  db::mutate_apply<evolution::Actions::ProjectRunEventsAndDenseTriggers>(\n      make_not_null(&box), std::as_const(children_items));\n\n  // There is no comparison operator for EventsAndDenseTriggers\n  CHECK(db::get_mutable_reference<::Tags::EventsAndDenseTriggers>(\n            make_not_null(&box))\n            .next_trigger(box) == next_trigger_time);\n  CHECK(db::get<::Tags::PreviousTriggerTime>(box) == std::nullopt);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.RunEventsAndDenseTriggers\",\n                  \"[Unit][Evolution][Actions]\") {\n  register_classes_with_charm<TimeSteppers::AdamsBashforth,\n                              TimeSteppers::Rk3HesthavenSsp>();\n  register_factory_classes_with_charm<Metavariables<tmpl::list<>>>();\n\n  for (const auto time_runs_forward : {true, false}) {\n    test<test_cases::NoPostprocessors>(time_runs_forward);\n    test<test_cases::NotReady>(time_runs_forward);\n    test<test_cases::PostprocessA>(time_runs_forward);\n    test<test_cases::PostprocessAll>(time_runs_forward);\n    test<test_cases::PostprocessEvolved>(time_runs_forward);\n  }\n  static_assert(tt::assert_conforms_to_v<\n                evolution::Actions::ProjectRunEventsAndDenseTriggers,\n                amr::protocols::Projector>);\n  test_p_refine();\n  test_h_split();\n  test_h_join();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Actions/Test_RunEventsAndTriggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Actions/RunEventsAndTriggers.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/Triggers/OnSubsteps.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TestEvent : public Event {\n public:\n  explicit TestEvent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestEvent);  // NOLINT\n#pragma GCC diagnostic pop\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using options = tmpl::list<>;\n  static constexpr Options::String help = \"\";\n\n  TestEvent() = default;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  void operator()(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ArrayIndex& /*array_index*/,\n                  const Component* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    CHECK(observation_value.name == \"Time\");\n    last_value.emplace(observation_value.value);\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n\n  static std::optional<double> last_value;\n};\n\nstd::optional<double> TestEvent::last_value{};\nPUP::able::PUP_ID TestEvent::my_PUP_ID = 0;  // NOLINT\n\ntemplate <typename Metavariables>\nstruct Component {\n  static constexpr bool local_time_stepping =\n      Metavariables::local_time_stepping;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags = tmpl::list<Tags::Time, Tags::TimeStepId>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::flatten<tmpl::list<\n              std::conditional_t<local_time_stepping,\n                                 evolution::Actions::RunEventsAndTriggers<\n                                     Triggers::WhenToCheck::AtSteps>,\n                                 tmpl::list<>>,\n              evolution::Actions::RunEventsAndTriggers<\n                  Triggers::WhenToCheck::AtSlabs>>>>>;\n};\n\ntemplate <bool LocalTimeStepping>\nstruct Metavariables {\n  static constexpr bool local_time_stepping = LocalTimeStepping;\n  using component_list = tmpl::list<Component<Metavariables>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Event, tmpl::list<TestEvent>>,\n        tmpl::pair<Trigger, tmpl::push_back<Triggers::logical_triggers,\n                                            ::Triggers::OnSubsteps>>>;\n  };\n};\n\ntemplate <bool LocalTimeStepping>\nvoid test(std::array<\n          ActionTesting::MockRuntimeSystem<Metavariables<LocalTimeStepping>>,\n          4>& runners) {\n  using my_component = Component<Metavariables<LocalTimeStepping>>;\n\n  for (size_t test_case = 0; test_case < 4; ++test_case) {\n    auto& runner = runners[test_case];\n    ActionTesting::emplace_component<my_component>(&runner, 0);\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  }\n\n  const auto test_all = [&runners](\n                            const TimeStepId& id,\n                            const std::optional<double> expected_always,\n                            const std::optional<double> expected_never,\n                            const std::optional<double> expected_always_sub,\n                            const std::optional<double> expected_never_sub) {\n    const std::array expected{expected_always, expected_never,\n                              expected_always_sub, expected_never_sub};\n    for (size_t test_case = 0; test_case < 4; ++test_case) {\n      auto& runner = runners[test_case];\n      auto& box =\n          ActionTesting::get_databox<my_component>(make_not_null(&runner), 0);\n      db::mutate<Tags::TimeStepId, Tags::Time>(\n          [&](const gsl::not_null<TimeStepId*> box_id,\n              const gsl::not_null<double*> box_time) {\n            *box_id = id;\n            *box_time = id.substep_time();\n          },\n          make_not_null(&box));\n\n      TestEvent::last_value.reset();\n      if constexpr (LocalTimeStepping) {\n        ActionTesting::next_action<my_component>(make_not_null(&runner), 0);\n      }\n      ActionTesting::next_action<my_component>(make_not_null(&runner), 0);\n      CHECK(TestEvent::last_value == expected[test_case]);\n    }\n  };\n\n  const Slab slab(1.0, 2.0);\n  const auto start = slab.start();\n  const auto step = slab.duration() / 2;\n  const auto center = start + step;\n\n  if constexpr (LocalTimeStepping) {\n    test_all(TimeStepId(true, 0, start), 1.0, {}, 1.0, {});\n    test_all(TimeStepId(true, 0, center), 1.5, {}, 1.5, {});\n    test_all(TimeStepId(true, 1, start), 1.0, {}, 1.0, {});\n    test_all(TimeStepId(true, 1, center), 1.5, {}, 1.5, {});\n    test_all(TimeStepId(true, -1, start), {}, {}, {}, {});\n    test_all(TimeStepId(true, -1, center), {}, {}, {}, {});\n    test_all(TimeStepId(true, 0, start, 1, step, 1.5), {}, {}, 1000001.0, {});\n    test_all(TimeStepId(true, 0, center, 1, step, 2.0), {}, {}, 1000001.5, {});\n    test_all(TimeStepId(true, 0, start, 2, step, 1.0), {}, {}, 2000001.0, {});\n    test_all(TimeStepId(true, 0, center, 2, step, 1.5), {}, {}, 2000001.5, {});\n  } else {\n    test_all(TimeStepId(true, 0, start), 1.0, {}, 1.0, {});\n    test_all(TimeStepId(true, 0, center), {}, {}, {}, {});\n    test_all(TimeStepId(true, 1, start), 1.0, {}, 1.0, {});\n    test_all(TimeStepId(true, 1, center), {}, {}, {}, {});\n    test_all(TimeStepId(true, -1, start), {}, {}, {}, {});\n    test_all(TimeStepId(true, -1, center), {}, {}, {}, {});\n    test_all(TimeStepId(true, 0, start, 1, step, 1.5), {}, {}, 1000001.0, {});\n    test_all(TimeStepId(true, 0, center, 1, step, 2.0), {}, {}, {}, {});\n    test_all(TimeStepId(true, 0, start, 2, step, 1.0), {}, {}, 2000001.0, {});\n    test_all(TimeStepId(true, 0, center, 2, step, 1.5), {}, {}, {}, {});\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.RunEventsAndTriggers\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  register_factory_classes_with_charm<Metavariables<true>>();\n  std::array gts_runners{\n      ActionTesting::MockRuntimeSystem<Metavariables<false>>{\n          TestHelpers::test_creation<EventsAndTriggers, Metavariables<false>>(\n              \"- Trigger: Always\\n\"\n              \"  Events:\\n\"\n              \"    - TestEvent\\n\")},\n      ActionTesting::MockRuntimeSystem<Metavariables<false>>{\n          TestHelpers::test_creation<EventsAndTriggers, Metavariables<false>>(\n              \"- Trigger:\\n\"\n              \"    Not: Always\\n\"\n              \"  Events:\\n\"\n              \"    - TestEvent\\n\")},\n      ActionTesting::MockRuntimeSystem<Metavariables<false>>{\n          TestHelpers::test_creation<EventsAndTriggers, Metavariables<false>>(\n              \"- Trigger:\\n\"\n              \"    OnSubsteps: Always\\n\"\n              \"  Events:\\n\"\n              \"    - TestEvent\\n\")},\n      ActionTesting::MockRuntimeSystem<Metavariables<false>>{\n          TestHelpers::test_creation<EventsAndTriggers, Metavariables<false>>(\n              \"- Trigger:\\n\"\n              \"    OnSubsteps:\\n\"\n              \"      Not: Always\\n\"\n              \"  Events:\\n\"\n              \"    - TestEvent\\n\")}};\n  test<false>(gts_runners);\n  std::array lts_runners{\n      ActionTesting::MockRuntimeSystem<Metavariables<true>>{tuples::TaggedTuple<\n          Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n          Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>>{\n          EventsAndTriggers{},\n          TestHelpers::test_creation<EventsAndTriggers, Metavariables<true>>(\n              \"- Trigger: Always\\n\"\n              \"  Events:\\n\"\n              \"    - TestEvent\\n\")}},\n      ActionTesting::MockRuntimeSystem<Metavariables<true>>{tuples::TaggedTuple<\n          Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n          Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>>{\n          EventsAndTriggers{},\n          TestHelpers::test_creation<EventsAndTriggers, Metavariables<true>>(\n              \"- Trigger:\\n\"\n              \"    Not: Always\\n\"\n              \"  Events:\\n\"\n              \"    - TestEvent\\n\")}},\n      ActionTesting::MockRuntimeSystem<Metavariables<true>>{tuples::TaggedTuple<\n          Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n          Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>>{\n          EventsAndTriggers{},\n          TestHelpers::test_creation<EventsAndTriggers, Metavariables<true>>(\n              \"- Trigger:\\n\"\n              \"    OnSubsteps: Always\\n\"\n              \"  Events:\\n\"\n              \"    - TestEvent\\n\")}},\n      ActionTesting::MockRuntimeSystem<Metavariables<true>>{tuples::TaggedTuple<\n          Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n          Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>>{\n          EventsAndTriggers{},\n          TestHelpers::test_creation<EventsAndTriggers, Metavariables<true>>(\n              \"- Trigger:\\n\"\n              \"    OnSubsteps:\\n\"\n              \"      Not: Always\\n\"\n              \"  Events:\\n\"\n              \"    - TestEvent\\n\")}}};\n  test<true>(lts_runners);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Ader/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Ader\")\n\nset(LIBRARY_SOURCES\n  Test_Matrices.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Ader\n  DataStructures\n  ErrorHandling\n  Spectral\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Ader/Test_Matrices.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"Evolution/Ader/Matrices.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\nvoid test_predictor_inverse_temporal_matrix() {\n  // The expected matrices were computed using a python implementation.\n  std::vector<Matrix> expected_matrices{\n      Matrix{{0.9999999999704987, -1.0000000000058982},\n             {1.0000000000058982, 1.0000000000245408}},\n\n      Matrix{{0.3333333333341427, -0.6666666666631862, 0.3333333333343741},\n             {0.3333333333338683, 0.8333333333302344, -0.16666666666642072},\n             {0.33333333333460546, 1.3333333333330097, 0.33333333333414294}},\n\n      Matrix{{0.16666666666598579, -0.3726779962463638, 0.37267799624435705,\n              -0.16666666666684973},\n             {0.16666666666511903, 0.4999999999988805, -0.18841586141621497,\n              0.07453559925100184},\n             {0.16666666666542224, 0.8550825280846519, 0.5000000000009217,\n              -0.07453559925086811},\n             {0.16666666666762078, 0.8333333333457615, 0.8333333333430486,\n              0.1666666666665209}},\n\n      Matrix{{0.09999999999999079, -0.2333333333296262, 0.26666666666992644,\n              -0.23333333332996198, 0.09999999999984038},\n             {0.100000000000526, 0.32222222222439767, -0.1380230820629782,\n              0.10400433198659419, -0.04285714285783839},\n             {0.10000000000046355, 0.5626183666492172, 0.4055555555563216,\n              -0.10567392219980562, 0.03749999999972064},\n             {0.10000000000049575, 0.5404401124615488, 0.7348484788858046,\n              0.3222222222235387, -0.04285714285777674},\n             {0.09999999999969783, 0.544444444443468, 0.7111111111063032,\n              0.5444444444436326, 0.09999999999999079}},\n\n      Matrix{{0.06666666666700752, -0.1588447788016382, 0.19232929696085765,\n              -0.19232929695910791, 0.15884477880415804, -0.0666666666668178},\n             {0.0666666666670148, 0.22257081148232563, -0.10098685881827471,\n              0.08649299947571226, -0.0677787383458266, 0.027979795608879355},\n             {0.06666666666687401, 0.39229919084571846, 0.310762521853379,\n              -0.09084920001658008, 0.058997819177094096, -0.02310851500410144},\n             {0.06666666666730482, 0.374537141438732, 0.5790409103889227,\n              0.3107625218493618, -0.0688842388598224, 0.02310851500329925},\n             {0.06666666666824704, 0.37958702798528104, 0.5490853938498073,\n              0.5751252195813528, 0.22257081148550922, -0.027979795609447904},\n             {0.0666666666678089, 0.37847495630399225, 0.5548583770442755,\n              0.5548583770377694, 0.3784749562994088, 0.06666666666599512}},\n\n      Matrix{{0.04761904761897748, -0.11481373058704851, 0.14338515915935313,\n              -0.15238095238129198, 0.14338515915910327, -0.1148137305869836,\n              0.04761904761908773},\n             {0.04761904761938801, 0.16222254749140472, -0.07595803696104785,\n              0.06903032288224813, -0.06139870799999678, 0.048010952613251855,\n              -0.019750021923025506},\n             {0.04761904761931034, 0.28739843169335644, 0.23968221441509313,\n              -0.07412324243657953, 0.05412775052222192, -0.03936755872370881,\n              0.015814563441896915},\n             {0.04761904761957719, 0.2735161565271106, 0.4525673743267387,\n              0.2676190476187939, -0.06562985535159956, 0.039189181645760046,\n              -0.014880952380883671},\n             {0.0476190476192627, 0.27806329653279865, 0.4252366783060526,\n              0.5111356870381575, 0.23968221441373858, -0.04870269388408819,\n              0.01581456344192448},\n             {0.04761904761949576, 0.2764341423701684, 0.4336750284379777,\n              0.4817887948895896, 0.4482343573993073, 0.16222254749163476,\n              -0.019750021923036633},\n             {0.04761904761927915, 0.27682604736289684, 0.4317453812093726,\n              0.4876190476158419, 0.4317453812098045, 0.2768260473622704,\n              0.04761904761897753}},\n\n      Matrix{{0.03571428571368002, -0.08674762803588493, 0.11037641642131896,\n              -0.12136997667797732, 0.12136997667092636, -0.11037641642704883,\n              0.08674762803220293, -0.03571428571430074},\n             {0.03571428571481557, 0.12320925643128415, -0.058809657980279706,\n              0.05527211524897814, -0.052172360298914255, 0.04621889588679081,\n              -0.035876373195608324, 0.014703689691768454},\n             {0.035714285714509166, 0.21896093735437683, 0.1884184890982874,\n              -0.060215849730803246, 0.046518086216605, -0.038088555372400336,\n              0.028548428326495918, -0.01155600304294998},\n             {0.035714285714654806, 0.20799483956744333, 0.35844468875618307,\n              0.2240865401892067, -0.057869269313596594, 0.038472630540354325,\n              -0.026652206226434463, 0.010509272878307713},\n             {0.03571428571443702, 0.21183010752353915, 0.33512938657112673,\n              0.4346137782569374, 0.22408654018289773, -0.04980132089670387,\n              0.028235713423931264, -0.010509272877884083},\n             {0.035714285714600745, 0.21022456271681644, 0.3434969621429396,\n              0.40521215939498245, 0.4334031934341878, 0.18841848910226483,\n              -0.03632547410913365, 0.011556003043384265},\n             {0.03571428571445321, 0.21086631462552144, 0.3403461327372401,\n              0.41466273366009015, 0.40715510070658134, 0.3544900143287909,\n              0.12320925642840279, -0.014703689691358351},\n             {0.035714285714217336, 0.2107042271428155, 0.34112269248168114,\n              0.4124587946546912, 0.4124587946536921, 0.34112269248400273,\n              0.21070422714244116, 0.0357142857147142}},\n\n      Matrix{\n          {0.027777777777904966, -0.06780186853265521, 0.08732740318801908,\n           -0.09809696322125326, 0.10158730158816573, -0.09809696322249856,\n           0.08732740318589581, -0.06780186853309741, 0.027777777777700685},\n          {0.02777777777791321, 0.0966365696695365, -0.046712218359015796,\n           0.044842610594161336, -0.04380703757628316, 0.04116365452614564,\n           -0.036132134919363276, 0.02785307304703796, -0.01138029017430888},\n          {0.027777777778017054, 0.1720871253965892, 0.1511582451407597,\n           -0.04933845587912496, 0.03939790154594345, -0.03412864478626619,\n           0.028804902061080596, -0.021780901782210788, 0.00883577101944848},\n          {0.027777777777333812, 0.1632724306350937, 0.2889104022893572,\n           0.18710314437337683, -0.049867149899055184, 0.03493339413795813,\n           -0.02704637147984173, 0.01966464558520257, -0.007865737255385497},\n          {0.027777777777190985, 0.1664698807289659, 0.2693037380120288,\n           0.3661044664383944, 0.19964852607839942, -0.046499343849742,\n           0.029113561291716536, -0.01951409259904683, 0.007595486111221578},\n          {0.027777777776984206, 0.16502993656987985, 0.27685688526085106,\n           0.339272894607053, 0.39262029945338117, 0.1871031443737516,\n           -0.039099888504309246, 0.02142215152179238, -0.007865737255285627},\n          {0.027777777776952575, 0.16570931907622902, 0.2735115882091492,\n           0.3493537127830036, 0.3644350496995086, 0.3645635238809584,\n           0.1511582451405937, -0.028158708096811175, 0.008835771019663296},\n          {0.02777777777686489, 0.16542006628597455, 0.27489364462744464,\n           0.3454542450810375, 0.3737069650253814, 0.3417752890167973,\n           0.2854737280766523, 0.09663656966959294, -0.011380290174234656},\n          {0.027777777776708507, 0.16549536155439248, 0.2745387124899078,\n           0.3464285109642727, 0.3715192743706812, 0.34642851096863286,\n           0.27453871250042855, 0.16549536156063824, 0.02777777777790497}},\n\n      Matrix{{0.022222222221906496, -0.054427523849716934, 0.07069328775990066,\n              -0.08055952715622082, 0.0853150711139464, -0.0853150711186775,\n              0.08055952715202327, -0.07069328776284448, 0.05442752384764724,\n              -0.022222222222238845},\n             {0.022222222222508053, 0.07776410653787505, -0.037920263982719374,\n              0.036927735421106736, -0.03688110044165266, 0.035870233838581414,\n              -0.033371150950317806, 0.029041632598949107,\n              -0.022260437990038125, 0.009073114585841167},\n             {0.022222222222298676, 0.13867457678353698, 0.12355578214264341,\n              -0.040915456857439574, 0.03338414627744741, -0.029896601950691586,\n              0.026692754011958673, -0.022720592684905134, 0.017214793613635156,\n              -0.006985488666996244},\n             {0.022222222222490935, 0.13146368216037643, 0.2368959169035338,\n              0.15713245295282371, -0.042769039406889815, 0.03092016133532309,\n              -0.02524262142739771, 0.020554926464678554, -0.015232616983223665,\n              0.006129965976055818},\n             {0.02222222222235726, 0.13413942728518716, 0.22038138305071794,\n              0.3091930999779255, 0.1748809917005159, -0.04194883312265133,\n              0.027569193013761623, -0.02052705637313009, 0.014598890364820791,\n              -0.005788275787891686},\n             {0.022222222222386626, 0.13288396881885056, 0.22700274737144358,\n              0.2854570248876443, 0.347266372082993, 0.1748809917048405,\n              -0.03813395052236873, 0.02292161008104799, -0.015010304765561245,\n              0.005788275788091677},\n             {0.022222222222203096, 0.13352485871349856, 0.2238350507907993,\n              0.2950630828820245, 0.3201536315888052, 0.34677476884578357,\n              0.15713245294851763, -0.03150721002937058, 0.01685605781234179,\n              -0.006129965975854242},\n             {0.0222222222220522, 0.133200325531795, 0.22538771252391648,\n              0.2906735745398945, 0.33061782731949735, 0.3209741507094684,\n              0.3076344956554706, 0.1235557821444346, -0.022477714228114308,\n              0.006985488667226348},\n             {0.022222222222032986, 0.13334420661771582, 0.2247110829768226,\n              0.29252217268729763, 0.3265028310660932, 0.3295875578955742,\n              0.28800661019426366, 0.233946232534877, 0.07776410653668284,\n              -0.009073114585665199},\n             {0.022222222221956987, 0.13330599084940398, 0.2248893420605291,\n              0.29204268367337277, 0.3275397611764274, 0.3275397611779948,\n              0.292042683675267, 0.22488934206468097, 0.13330599085097763,\n              0.022222222222427496}},\n\n      Matrix{\n          {0.01818181818190092, -0.044642473306814805, 0.05833599883233813,\n           -0.0671562769358682, 0.07222177017390477, -0.07388167388035201,\n           0.07222177017371924, -0.06715627693638936, 0.058335998831632006,\n           -0.044642473307234094, 0.01818181818179972},\n          {0.018181818181884465, 0.0638970457245323, -0.03135603900725852,\n           0.03084758641712686, -0.03128098998235485, 0.031114343070490727,\n           -0.02995440928211618, 0.0276068082006394, -0.023855190205522774,\n           0.018202618816853436, -0.007405022345450316},\n          {0.01818181818205217, 0.11406140492110083, 0.10267584998255241,\n           -0.03435447423964847, 0.028453895720033793, -0.026043682781403215,\n           0.024038667801963173, -0.02163836439715823, 0.018444921784627883,\n           -0.013970311904477681, 0.005666801272339664},\n          {0.018181818182030893, 0.10806720291792335, 0.1972989428227929,\n           0.13311496122315933, -0.036772860449869944, 0.02714975904172252,\n           -0.022877487172695835, 0.019653114415352748, -0.016327680141672216,\n           0.012199428064441502, -0.004922525897407123},\n          {0.018181818181917025, 0.1103255422268642, 0.18329165188708757,\n           0.2629369294006421, 0.15253047148107712, -0.03733312515688951,\n           0.025273649287223258, -0.01978086529580089, 0.01568365984831898,\n           -0.01144513703542942, 0.004577269590724688},\n          {0.018181818181904563, 0.1092383670653025, 0.18905062359811212,\n           0.24214296033922839, 0.304780237440625, 0.1591997068180093,\n           -0.035674438915052786, 0.022431883951549464, -0.01623686652981973,\n           0.011360139867655262, -0.004474431818129493},\n          {0.01818181818190467, 0.10981867534922649, 0.18617229957461756,\n           0.25092238842587977, 0.27978729367421024, 0.31895102195798775,\n           0.15253047148095408, -0.03179540627042421, 0.018564307536025667,\n           -0.0119520039130228, 0.00457726959073644},\n          {0.01818181818197332, 0.10949930040746432, 0.18770373634797785,\n           0.24657680803089058, 0.29020336758082194, 0.2930704813306262,\n           0.30409874085815697, 0.13311496122317826, -0.025922886615722045,\n           0.013631525554224474, -0.004922525897368947},\n          {0.018181818181879545, 0.10966868382235812, 0.18690677817786133,\n           0.24875559850311482, 0.28535011002786914, 0.30323427624102584,\n           0.2809348821100695, 0.2614717083466195, 0.10267584998211361,\n           -0.01836303300236528, 0.005666801272408237},\n          {0.01818181818176121, 0.10959147263171942, 0.18726620636554275,\n           0.24779245128544367, 0.287419323850867, 0.29919350191251687,\n           0.2887459045512219, 0.244551673069504, 0.19476705516873852,\n           0.06389704572454562, -0.007405022345380785},\n          {0.018181818181636864, 0.10961227326601175, 0.18716988177784596,\n           0.24804810426004392, 0.28687912477486166, 0.30021759545075083,\n           0.2868791247750567, 0.24804810426082946, 0.18716988177962288,\n           0.10961227326675485, 0.01818181818190091}},\n\n      Matrix{\n          {0.015151515151584232, -0.03727142812799102, 0.048923983331182815,\n           -0.05674349752500363, 0.06170256160971506, -0.0641264424372233,\n           0.06412644243863766, -0.06170256160829091, 0.05674349752595264,\n           -0.04892398333066516, 0.03727142812829069, -0.015151515151509181},\n          {0.01515151515156269, 0.053418016282432035, -0.026337819282717282,\n           0.02610589416801112, -0.02676512444739308, 0.027042533469780965,\n           -0.026626617339731436, 0.02538516813433355, -0.023212396318457083,\n           0.019943687676434244, -0.01516349690472017, 0.006159367185928767},\n          {0.0151515151515404, 0.09542754399508485, 0.08656311035886338,\n           -0.029186317651895763, 0.02443801639291171, -0.02271304271947984,\n           0.02142961206922456, -0.019938868676788502, 0.017966451862544062,\n           -0.015299804406795028, 0.011574811128851692, -0.004692349145804841},\n          {0.015151515151354199, 0.09037353349185437, 0.16660769069395492,\n           0.11382996645635492, -0.03178950176515335, 0.0238229674736676,\n           -0.020502170502257933, 0.01818197074102646, -0.015939020633403828,\n           0.013355917722877841, -0.010014743777602848, 0.004045721913440676},\n          {0.015151515151164962, 0.0922982058317993, 0.1546243868767208,\n           0.22545960572983662, 0.1332135591757906, -0.03308324097512993,\n           0.02285119371253928, -0.018429489372159796, 0.015376828409832734,\n           -0.012535386915515763, 0.009262446732696578, -0.003720565328308077},\n          {0.015151515150923093, 0.09135549819091596, 0.15963556234210327,\n           0.20726230500011994, 0.26732630859823997, 0.14327837802934304,\n           -0.03272425696964533, 0.021156361846805048, -0.016053057042708537,\n           0.012473365089010743, -0.008994846794050059, 0.003579933684984922},\n          {0.015151515150916147, 0.09187303429485524, 0.1570608851217073,\n           0.21515436955776315, 0.2446980525460716, 0.2889779827279105,\n           0.14327837803226115, -0.030629516600889424, 0.018653217997573468,\n           -0.013220401434395387, 0.00913534912807098, -0.003579933685119333},\n          {0.015151515151062427, 0.09157434237112415, 0.1584964506536467,\n           0.21106537005542692, 0.2545535774183351, 0.26430076401179425,\n           0.28874176507676125, 0.13321355917455283, -0.02688496867698337,\n           0.01536396050764859, -0.009765960113496116, 0.003720565328180566},\n          {0.015151515151322561, 0.09174713217719385, 0.15768235471670078,\n           0.21329592324369012, 0.24956930523155096, 0.27478451969443596,\n           0.2647051651686592, 0.2665894321970533, 0.11382996645817654,\n           -0.02169655200643409, 0.01126311293344462, -0.00404572191358638},\n          {0.015151515151495987, 0.09165248284445864, 0.15812299482041986,\n           0.21211514584329877, 0.2521054943727682, 0.26983527023924914,\n           0.2742586422525451, 0.24594656432223555, 0.2241215554759763,\n           0.08656311035819617, -0.01528580314126821, 0.004692349145641109},\n          {0.015151515151498793, 0.09169649916646774, 0.15791950920614115,\n           0.21265354766206537, 0.2509736514656601, 0.27196329010955084,\n           0.2704312756010187, 0.2529575112575845, 0.20946979001773183,\n           0.16442403353067048, 0.05341801628305097, -0.006159367186092625},\n          {0.015151515151414616, 0.09168451741272649, 0.1579747055645895,\n           0.21250841776306503, 0.2512756032035713, 0.2714052409185767,\n           0.2714052409196041, 0.25127560320440273, 0.212508417764308,\n           0.15797470556436372, 0.09168451741363809, 0.015151515151370839}}};\n\n  const auto perform_check =\n      [&expected_matrices](const size_t num_points) {\n        const Matrix& result = ader::dg::predictor_inverse_temporal_matrix<\n            Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>(\n            num_points);\n        Approx custom_approx = Approx::custom().epsilon(1e-10);\n        for (size_t i = 0; i < num_points; ++i) {\n          for (size_t j = 0; j < num_points; ++j) {\n            CHECK(expected_matrices[num_points - 2](i, j) ==\n                  custom_approx(result(i, j)));\n          }\n        }\n      };\n  for (size_t i = Spectral::minimum_number_of_points<\n           Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>;\n       i <=\n       std::min(Spectral::maximum_number_of_points<Spectral::Basis::Legendre>,\n                12_st);\n\n       /*The maximum number of allowed collocation points is given by\n       `Spectral::maximum_number_of_points<Spectral::Basis::Legendre>`, but\n       the matrices corresponding to numbers of points above 12 are not\n       tested, because we only derived the expected matrices up to 12 points.*/\n       ++i) {\n    perform_check(i);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Ader.Dg.Matrices\", \"[Unit][Ader]\") {\n  test_predictor_inverse_temporal_matrix();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EvolutionBoundaryConditions\")\n\nset(LIBRARY_SOURCES\n  Test_Type.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  EvolutionBoundaryConditions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/BoundaryConditions/Test_Type.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.BoundaryConditions.Type\",\n                  \"[Unit][Evolution]\") {\n  CHECK(get_output(evolution::BoundaryConditions::Type::Ghost) == \"Ghost\");\n  CHECK(get_output(evolution::BoundaryConditions::Type::TimeDerivative) ==\n        \"TimeDerivative\");\n  CHECK(\n      get_output(evolution::BoundaryConditions::Type::GhostAndTimeDerivative) ==\n      \"GhostAndTimeDerivative\");\n  CHECK(get_output(\n            evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds) ==\n        \"DemandOutgoingCharSpeeds\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Actions)\nadd_subdirectory(Ader)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(DgSubcell)\nadd_subdirectory(DiscontinuousGalerkin)\nadd_subdirectory(Imex)\nadd_subdirectory(Initialization)\nadd_subdirectory(Particles)\nadd_subdirectory(Ringdown)\nadd_subdirectory(Systems)\nadd_subdirectory(Triggers)\nadd_subdirectory(VariableFixing)\n\nset(LIBRARY \"Test_Evolution\")\n\nset(LIBRARY_SOURCES\n  Test_BoundaryCorrectionTags.cpp\n  Test_ComputeTags.cpp\n  Test_NumericInitialData.cpp\n  Test_Protocols.cpp\n  Test_TagsDomain.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  AnalyticData\n  AnalyticSolutions\n  Boost::boost\n  CoordinateMaps\n  DataStructures\n  DataStructuresHelpers\n  DataBoxTestHelpers\n  DgSubcell\n  DomainCreators\n  Domain\n  Evolution\n  FunctionsOfTime\n  InitialDataUtilities\n  LinearOperators\n  Options\n  Spectral\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Actions/Test_Initialize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Actions/Initialize.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostZoneInverseJacobian.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ReconstructionOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/StepsSinceTciCall.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciCallsSinceRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciGridHistory.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct SystemAnalyticSolution : public MarkAsAnalyticSolution,\n                                public evolution::initial_data::InitialData {\n  SystemAnalyticSolution() = default;\n  ~SystemAnalyticSolution() override = default;\n\n  explicit SystemAnalyticSolution(CkMigrateMessage* msg)\n      : evolution::initial_data::InitialData(msg) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(SystemAnalyticSolution);\n#pragma GCC diagnostic pop\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override {\n    return std::make_unique<SystemAnalyticSolution>(*this);\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<Var1> variables(const tnsr::I<DataVector, Dim>& x,\n                                      const double t,\n                                      tmpl::list<Var1> /*meta*/) const {\n    tuples::TaggedTuple<Var1> vars(x.get(0) + t);\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<Var1>(vars)) += x.get(d) + t;\n    }\n    return vars;\n  }\n\n  // clang-tidy: do not use references\n  void pup(PUP::er& /*p*/) override {}  // NOLINT\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID SystemAnalyticSolution::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = Tags::Variables<tmpl::list<Var1>>;\n  using flux_variables = tmpl::list<Var1>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using const_global_cache_tag_list = tmpl::list<>;\n\n  using initial_tags = tmpl::list<\n      Tags::Time, domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      domain::Tags::ElementMap<Dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                  Frame::Inertial>,\n      Tags::Variables<tmpl::list<Var1>>,\n      Tags::Variables<tmpl::list<::Tags::dt<Var1>>>,\n      ::Tags::HistoryEvolvedVariables<Tags::Variables<tmpl::list<Var1>>>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<\n          ActionTesting::InitializeDataBox<\n              initial_tags,\n              db::AddComputeTags<\n                  domain::Tags::MappedCoordinates<\n                      domain::Tags::ElementMap<Dim, Frame::Grid>,\n                      domain::Tags::Coordinates<Dim, Frame::ElementLogical>>,\n                  domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<\n                      domain::CoordinateMaps::Tags::CoordinateMap<\n                          Dim, Frame::Grid, Frame::Inertial>>,\n                  domain::Tags::InertialFromGridCoordinatesCompute<Dim>>>,\n          evolution::dg::subcell::Actions::SetSubcellGrid<Dim, System<Dim>,\n                                                          false>,\n\n          evolution::dg::subcell::Actions::SetAndCommunicateInitialRdmpData<\n              Dim, typename Metavariables::SetInitialRdmpData>,\n          evolution::dg::subcell::Actions::ComputeAndSendTciOnInitialGrid<\n              Dim, System<Dim>, typename Metavariables::FdInitialDataTci>,\n          evolution::dg::subcell::Actions::SetInitialGridFromTciData<\n              Dim, System<Dim>>>>>;\n};\n\ntemplate <size_t Dim, bool TciFails, bool SubcellEnabledOnExternalBoundary>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using analytic_solution = SystemAnalyticSolution;\n  using component_list = tmpl::list<Component<Dim, Metavariables>>;\n  using system = System<Dim>;\n  using analytic_variables_tags = typename system::variables_tag::tags_list;\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<evolution::initial_data::InitialData,\n                             tmpl::list<SystemAnalyticSolution>>>;\n  };\n\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled_at_external_boundary =\n        SubcellEnabledOnExternalBoundary;\n  };\n\n  struct FdInitialDataTci {\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    static bool invoked;\n\n    using return_tags = tmpl::list<>;\n    using argument_tags = tmpl::list<::Tags::Variables<tmpl::list<Var1>>,\n                                     domain::Tags::Mesh<volume_dim>>;\n\n    static std::tuple<int, evolution::dg::subcell::RdmpTciData> apply(\n        const Variables<tmpl::list<Var1>>& fd_vars,\n        const Mesh<volume_dim>& dg_mesh, const double persson_exponent,\n        const bool need_rdmp_data_only) {\n      CHECK(dg_mesh == Mesh<Dim>(5, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto));\n      CHECK(persson_exponent == 5.1);\n      evolution::dg::subcell::RdmpTciData rdmp_data{};\n      rdmp_data.max_variables_values = DataVector{max(get(get<Var1>(fd_vars)))};\n      rdmp_data.min_variables_values = DataVector{min(get(get<Var1>(fd_vars)))};\n      REQUIRE(not need_rdmp_data_only);\n      invoked = true;\n      return {static_cast<int>(TciFails), std::move(rdmp_data)};\n    }\n  };\n\n  struct SetInitialRdmpData {\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    static bool invoked;\n\n    using return_tags =\n        tmpl::list<evolution::dg::subcell::Tags::DataForRdmpTci>;\n    using argument_tags = tmpl::list<::Tags::Variables<tmpl::list<Var1>>>;\n\n    static void apply(\n        const gsl::not_null<evolution::dg::subcell::RdmpTciData*> rdmp_tci_data,\n        const Variables<tmpl::list<Var1>>& fd_vars) {\n      invoked = true;\n      evolution::dg::subcell::RdmpTciData rdmp_data{};\n      rdmp_data.max_variables_values = DataVector{max(get(get<Var1>(fd_vars)))};\n      rdmp_data.min_variables_values = DataVector{min(get(get<Var1>(fd_vars)))};\n      *rdmp_tci_data = std::move(rdmp_data);\n    }\n  };\n};\n\ntemplate <size_t Dim, bool TciFails, bool SubcellEnabledOnExternalBoundary>\nbool Metavariables<\n    Dim, TciFails,\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    SubcellEnabledOnExternalBoundary>::FdInitialDataTci::invoked = false;\ntemplate <size_t Dim, bool TciFails, bool SubcellEnabledOnExternalBoundary>\nbool Metavariables<Dim, TciFails, SubcellEnabledOnExternalBoundary>::\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    SetInitialRdmpData::invoked = false;\n\ntemplate <size_t Dim>\nclass TestCreator : public DomainCreator<Dim> {\n  Domain<Dim> create_domain() const override { return Domain<Dim>{}; }\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override {\n    return {};\n  }\n\n  std::vector<std::string> block_names() const override { return {\"Block0\"}; }\n\n  std::vector<std::array<size_t, Dim>> initial_extents() const override {\n    return {};\n  }\n\n  std::vector<std::array<size_t, Dim>> initial_refinement_levels()\n      const override {\n    return {};\n  }\n};\n\ntemplate <size_t Dim, bool TciFails, bool SubcellEnabledOnExternalBoundary>\nvoid test(const bool always_use_subcell, const bool interior_element,\n          const bool allow_subcell_in_block) {\n  CAPTURE(Dim);\n  CAPTURE(TciFails);\n  CAPTURE(SubcellEnabledOnExternalBoundary);\n  CAPTURE(always_use_subcell);\n  CAPTURE(interior_element);\n  CAPTURE(allow_subcell_in_block);\n  using metavars =\n      Metavariables<Dim, TciFails, SubcellEnabledOnExternalBoundary>;\n  using comp = Component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {std::unique_ptr<evolution::initial_data::InitialData>(\n           std::make_unique<SystemAnalyticSolution>()),\n       evolution::dg::subcell::SubcellOptions{\n           evolution::dg::subcell::SubcellOptions{\n               4.1, 1_st, 1.0e-3, 1.0e-4, always_use_subcell, false,\n               evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n               false,\n               allow_subcell_in_block\n                   ? std::optional<std::vector<std::string>>{}\n                   : std::optional{std::vector<std::string>{\"Block0\"}},\n               ::fd::DerivativeOrder::Two, 1, 1, 1},\n           TestCreator<Dim>{}}}};\n  metavars::FdInitialDataTci::invoked = false;\n  metavars::SetInitialRdmpData::invoked = false;\n\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const ElementId<Dim> self_id{0};\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  if (interior_element) {\n    size_t id_count = 1;\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      neighbors[direction] = Neighbors<Dim>{\n          {ElementId<Dim>{id_count}}, OrientationMap<Dim>::create_aligned()};\n      ++id_count;\n    }\n  }\n  const Element<Dim> element{self_id, neighbors};\n  const auto logical_coords = logical_coordinates(dg_mesh);\n  const auto make_element_map = [](const auto& element_id) {\n    return ElementMap<Dim, Frame::Grid>{\n        element_id,\n        domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n            domain::CoordinateMaps::Identity<Dim>{})};\n  };\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{});\n  const double initial_time = 1.3;\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, self_id,\n      {initial_time, dg_mesh, element, clone_unique_ptrs(functions_of_time),\n       logical_coords, make_element_map(self_id),\n       grid_to_inertial_map->get_clone(),\n       Variables<tmpl::list<Var1>>{subcell_mesh.number_of_grid_points()},\n       Variables<tmpl::list<::Tags::dt<Var1>>>{\n           subcell_mesh.number_of_grid_points()},\n       typename ::Tags::HistoryEvolvedVariables<\n           Tags::Variables<tmpl::list<Var1>>>::type{}});\n\n  if (interior_element) {\n    // Add neighboring elements into runner\n    for (const auto& [direction, neighbors_in_direction] :\n         element.neighbors()) {\n      DirectionMap<Dim, Neighbors<Dim>> neighbor_neighbors{};\n      neighbor_neighbors[direction.opposite()] =\n          Neighbors<Dim>{{self_id}, OrientationMap<Dim>::create_aligned()};\n      const auto neighbor_id = *neighbors_in_direction.begin();\n      // We use the time to get different solutions on the different neighbors\n      // since the analytic solution includes the time.\n      const double neighbor_time = initial_time + neighbor_id.block_id();\n\n      ActionTesting::emplace_array_component_and_initialize<comp>(\n          &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n          neighbor_id,\n          {neighbor_time, dg_mesh,\n           Element<Dim>{neighbor_id, neighbor_neighbors},\n           clone_unique_ptrs(functions_of_time), logical_coords,\n           make_element_map(neighbor_id), grid_to_inertial_map->get_clone(),\n           Variables<tmpl::list<Var1>>{subcell_mesh.number_of_grid_points()},\n           Variables<tmpl::list<::Tags::dt<Var1>>>{\n               subcell_mesh.number_of_grid_points()},\n           typename ::Tags::HistoryEvolvedVariables<\n               Tags::Variables<tmpl::list<Var1>>>::type{}});\n    }\n  }\n\n  REQUIRE(\n      ActionTesting::get_databox_tag<comp, typename System<Dim>::variables_tag>(\n          runner, self_id)\n          .number_of_grid_points() == subcell_mesh.number_of_grid_points());\n\n  // Invoke SetSubcellGrid action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n  for (size_t count = 1; interior_element and count <= 2 * Dim; ++count) {\n    ActionTesting::next_action<comp>(make_not_null(&runner),\n                                     ElementId<Dim>{count});\n  }\n  const auto active_grid_before_tci =\n      ActionTesting::get_databox_tag<comp,\n                                     evolution::dg::subcell::Tags::ActiveGrid>(\n          runner, self_id);\n  CHECK(active_grid_before_tci ==\n        (((element.external_boundaries().empty() or\n           SubcellEnabledOnExternalBoundary) and\n          allow_subcell_in_block)\n             ? evolution::dg::subcell::ActiveGrid::Subcell\n             : evolution::dg::subcell::ActiveGrid::Dg));\n\n  // Invoke SetAndCommunicateInitialRdmpData action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n  REQUIRE(metavars::SetInitialRdmpData::invoked);\n  REQUIRE_FALSE(metavars::FdInitialDataTci::invoked);\n  if (interior_element) {\n    for (size_t count = 1; count <= 2 * Dim; ++count) {\n      ActionTesting::next_action<comp>(make_not_null(&runner),\n                                       ElementId<Dim>{count});\n      if (count < 2 * Dim) {\n        REQUIRE(ActionTesting::get_next_action_index<comp>(runner, self_id) ==\n                3);\n        REQUIRE_FALSE(ActionTesting::next_action_if_ready<comp>(\n            make_not_null(&runner), self_id));\n        REQUIRE(ActionTesting::get_next_action_index<comp>(runner, self_id) ==\n                3);\n      }\n    }\n\n    // Check RDMP data was communicated correctly by looking at inboxes.\n    const auto& self_inbox = ActionTesting::get_inbox_tag<\n        comp, evolution::dg::subcell::Tags::InitialTciData<Dim>>(\n        make_not_null(&runner), self_id);\n    REQUIRE(self_inbox.size() == 1);\n    REQUIRE(self_inbox.find(0) != self_inbox.end());\n    const auto& self_inbox_at_time = self_inbox.at(0);\n    for (const auto& [direction, neighbors_in_direction] :\n         element.neighbors()) {\n      const DirectionalId<Dim> id{direction, *neighbors_in_direction.begin()};\n      CAPTURE(id);\n      REQUIRE(self_inbox_at_time.find(id) != self_inbox_at_time.end());\n      CHECK_FALSE(self_inbox_at_time.at(id).tci_status.has_value());\n      REQUIRE(self_inbox_at_time.at(id).initial_rdmp_data.has_value());\n      CHECK(\n          self_inbox_at_time.at(id)\n              .initial_rdmp_data.value()\n              .max_variables_values[0] ==\n          approx(((SubcellEnabledOnExternalBoundary and allow_subcell_in_block)\n                      ? 2.18888888888888888\n                      : 2.3) +\n                 neighbors_in_direction.begin()->block_id()));\n      CHECK(\n          self_inbox_at_time.at(id)\n              .initial_rdmp_data.value()\n              .min_variables_values[0] ==\n          approx(((SubcellEnabledOnExternalBoundary and allow_subcell_in_block)\n                      ? 0.41111111111111111\n                      : 0.3) +\n                 neighbors_in_direction.begin()->block_id()));\n    }\n  }\n  metavars::FdInitialDataTci::invoked = false;\n  metavars::SetInitialRdmpData::invoked = false;\n\n  REQUIRE(ActionTesting::get_next_action_index<comp>(runner, self_id) == 3);\n  // Invoke ComputeAndSendTciOnInitialGrid action on self_id\n  REQUIRE(ActionTesting::next_action_if_ready<comp>(make_not_null(&runner),\n                                                    self_id));\n  REQUIRE(ActionTesting::get_next_action_index<comp>(runner, self_id) == 4);\n  REQUIRE_FALSE(metavars::SetInitialRdmpData::invoked);\n  if (always_use_subcell or\n      ActionTesting::get_databox_tag<comp,\n                                     evolution::dg::subcell::Tags::ActiveGrid>(\n          runner, self_id) == evolution::dg::subcell::ActiveGrid::Dg) {\n    REQUIRE_FALSE(metavars::FdInitialDataTci::invoked);\n  } else {\n    REQUIRE(metavars::FdInitialDataTci::invoked);\n  }\n  if (interior_element) {\n    // Invoke ComputeAndSendTciOnInitialGrid action on the neighbors\n    for (size_t i = 0; i < neighbors.size(); ++i) {\n      CAPTURE(i + 1);\n      metavars::FdInitialDataTci::invoked = false;\n      metavars::SetInitialRdmpData::invoked = false;\n      ActionTesting::next_action<comp>(make_not_null(&runner),\n                                       ElementId<Dim>{i + 1});\n      REQUIRE_FALSE(metavars::SetInitialRdmpData::invoked);\n      if (not SubcellEnabledOnExternalBoundary or always_use_subcell or\n          ActionTesting::get_databox_tag<\n              comp, evolution::dg::subcell::Tags::ActiveGrid>(\n              runner, self_id) == evolution::dg::subcell::ActiveGrid::Dg) {\n        REQUIRE_FALSE(metavars::FdInitialDataTci::invoked);\n      } else {\n        REQUIRE(metavars::FdInitialDataTci::invoked);\n      }\n    }\n\n    // Check TCI status data was communicated correctly by looking at inboxes.\n    const auto& self_inbox = ActionTesting::get_inbox_tag<\n        comp, evolution::dg::subcell::Tags::InitialTciData<Dim>>(\n        make_not_null(&runner), self_id);\n    REQUIRE(self_inbox.size() == 1);\n    REQUIRE(self_inbox.find(1) != self_inbox.end());\n    const auto& self_inbox_at_time = self_inbox.at(1);\n    for (const auto& [direction, neighbors_in_direction] :\n         element.neighbors()) {\n      const DirectionalId<Dim> id{direction, *neighbors_in_direction.begin()};\n      CAPTURE(id);\n      REQUIRE(self_inbox_at_time.find(id) != self_inbox_at_time.end());\n      CHECK_FALSE(self_inbox_at_time.at(id).initial_rdmp_data.has_value());\n      REQUIRE(self_inbox_at_time.at(id).tci_status.has_value());\n      if (metavars::FdInitialDataTci::invoked) {\n        CHECK(self_inbox_at_time.at(id).tci_status.value() ==\n              static_cast<int>(TciFails));\n      } else {\n        CHECK(self_inbox_at_time.at(id).tci_status.value() == 0);\n      }\n    }\n  }\n\n  metavars::FdInitialDataTci::invoked = false;\n  metavars::SetInitialRdmpData::invoked = false;\n\n  REQUIRE(ActionTesting::get_next_action_index<comp>(runner, self_id) == 4);\n  // Invoke SetInitialGridFromTciData action on self_id\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n  REQUIRE_FALSE(metavars::FdInitialDataTci::invoked);\n  REQUIRE_FALSE(metavars::SetInitialRdmpData::invoked);\n\n  const auto active_grid =\n      ActionTesting::get_databox_tag<comp,\n                                     evolution::dg::subcell::Tags::ActiveGrid>(\n          runner, self_id);\n\n  if (SubcellEnabledOnExternalBoundary) {\n    CHECK(active_grid ==\n          (((TciFails or always_use_subcell) and allow_subcell_in_block)\n               ? evolution::dg::subcell::ActiveGrid::Subcell\n               : evolution::dg::subcell::ActiveGrid::Dg));\n  } else {\n    CHECK(active_grid == (((TciFails or always_use_subcell) and\n                           allow_subcell_in_block and interior_element)\n                              ? evolution::dg::subcell::ActiveGrid::Subcell\n                              : evolution::dg::subcell::ActiveGrid::Dg));\n  }\n\n  CHECK(\n      ActionTesting::get_databox_tag<comp, typename System<Dim>::variables_tag>(\n          runner, self_id)\n          .number_of_grid_points() ==\n      (active_grid == evolution::dg::subcell::ActiveGrid::Dg\n           ? dg_mesh.number_of_grid_points()\n           : subcell_mesh.number_of_grid_points()));\n\n  // Check that tags were added.\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::DidRollback>(runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::TciCallsSinceRollback>(runner,\n                                                                   self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::StepsSinceTciCall>(runner,\n                                                               self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::TciGridHistory>(runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::GhostZoneInverseJacobian<Dim>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::DataForRdmpTci>(runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp,\n        evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGrid<Dim>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp,\n        evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToGrid>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::fd::Tags::\n                  InverseJacobianLogicalToInertial<Dim>>(runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp,\n        evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToInertial>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp,\n        evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Grid>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Inertial>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::TciDecision>(runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::ReconstructionOrder<Dim>>(runner,\n                                                                      self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp,\n        evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp,\n        evolution::dg::subcell::Tags::InterpolatorsFromDgToNeighborFd<Dim>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp,\n        evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>>(\n      runner, self_id));\n  CHECK(ActionTesting::tag_is_retrievable<\n        comp, evolution::dg::subcell::Tags::ExtensionDirections<Dim>>(runner,\n                                                                      self_id));\n\n  // Check things have correct values.\n  CHECK(ActionTesting::get_databox_tag<\n            comp, evolution::dg::subcell::Tags::TciCallsSinceRollback>(\n            runner, self_id) == 0);\n  CHECK(ActionTesting::get_databox_tag<\n            comp, evolution::dg::subcell::Tags::StepsSinceTciCall>(\n            runner, self_id) == 0);\n  CHECK(ActionTesting::get_databox_tag<\n            comp, evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n            runner, self_id)\n            .size() ==\n        (interior_element ? Direction<Dim>::all_directions().size() : 0));\n  CHECK(\n      ActionTesting::get_databox_tag<comp,\n                                     evolution::dg::subcell::Tags::DidRollback>(\n          runner, self_id) == false);\n  CHECK(ActionTesting::get_databox_tag<comp,\n                                       evolution::dg::subcell::Tags::Mesh<Dim>>(\n            runner, self_id) == subcell_mesh);\n  for (const auto& [direction, neighbors_in_direction] : element.neighbors()) {\n    for (const auto& neighbor : neighbors_in_direction.ids()) {\n      CHECK(ActionTesting::get_databox_tag<\n                comp, evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n                runner, self_id)\n                .contains(DirectionalId<Dim>{direction, neighbor}));\n    }\n  }\n  const auto& subcell_inertial_coords = ActionTesting::get_databox_tag<\n      comp, evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Inertial>>(\n      runner, self_id);\n  const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n  for (size_t d = 0; d < Dim; ++d) {\n    CHECK(subcell_inertial_coords[d] == subcell_logical_coords[d]);\n  }\n  CHECK(not ActionTesting::get_databox_tag<\n                comp, evolution::dg::subcell::Tags::CellCenteredFlux<\n                          typename metavars::system::flux_variables, Dim>>(\n                runner, self_id)\n                .has_value());\n\n  // Update the variables with the latest in the DataBox\n  const auto& vars =\n      ActionTesting::get_databox_tag<comp, typename System<Dim>::variables_tag>(\n          runner, self_id);\n  Variables<tmpl::list<Var1>> expected_vars{};\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Subcell) {\n    expected_vars.initialize(subcell_mesh.number_of_grid_points());\n  } else {\n    expected_vars.initialize(dg_mesh.number_of_grid_points());\n  }\n  expected_vars.assign_subset(SystemAnalyticSolution{}.variables(\n      active_grid == evolution::dg::subcell::ActiveGrid::Subcell\n          ? ActionTesting::get_databox_tag<\n                comp, evolution::dg::subcell::Tags::Coordinates<\n                          Dim, Frame::Inertial>>(runner, self_id)\n          : ActionTesting::get_databox_tag<\n                comp, domain::Tags::Coordinates<Dim, Frame::Inertial>>(runner,\n                                                                       self_id),\n      ActionTesting::get_databox_tag<comp, ::Tags::Time>(runner, self_id),\n      tmpl::list<Var1>{}));\n  CHECK_ITERABLE_APPROX(get<Var1>(vars), get<Var1>(expected_vars));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Actions.Initialize\",\n                  \"[Evolution][Unit]\") {\n  register_classes_with_charm<\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                            domain::CoordinateMaps::Identity<1>>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                            domain::CoordinateMaps::Identity<2>>,\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                            domain::CoordinateMaps::Identity<3>>,\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<1>>,\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<2>>,\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<3>>>();\n  register_classes_with_charm<SystemAnalyticSolution>();\n  for (const bool always_use_subcell : {false, true}) {\n    for (const bool interior_element : {false, true}) {\n      for (const bool allow_subcell_in_block : {false, true}) {\n        test<1, true, false>(always_use_subcell, interior_element,\n                             allow_subcell_in_block);\n        test<1, false, false>(always_use_subcell, interior_element,\n                              allow_subcell_in_block);\n\n        test<1, true, true>(always_use_subcell, interior_element,\n                            allow_subcell_in_block);\n        test<1, false, true>(always_use_subcell, interior_element,\n                             allow_subcell_in_block);\n      }\n    }\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Actions/Test_ReconstructionCommunication.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <deque>\n#include <iterator>\n#include <memory>\n#include <unordered_set>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DgSubcell/Actions/ReconstructionCommunication.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/CombineVolumeGhostData.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SetInterpolators.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/MeshForGhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciGridHistory.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nclass DummyReconstructor {\n public:\n  static size_t ghost_zone_size() { return 2; }\n  void pup(PUP::er& /*p*/) {}\n};\n\nnamespace Tags {\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<DummyReconstructor>;\n};\n}  // namespace Tags\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n  using flux_variables = tmpl::list<Var1>;\n};\n\ntemplate <size_t Dim, typename Metavariables, bool UseNodegroupDgElements,\n          bool ExtraTesting = false>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n\n  using initial_tags = tmpl::flatten<tmpl::list<\n      Tags::Reconstructor, ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n      domain::Tags::Mesh<Dim>, evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::ActiveGrid, domain::Tags::Element<Dim>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n      evolution::dg::subcell::Tags::DataForRdmpTci,\n      evolution::dg::subcell::Tags::TciDecision,\n      evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>,\n      ::Tags::Variables<tmpl::list<Var1>>,\n      ::Tags::HistoryEvolvedVariables<::Tags::Variables<tmpl::list<Var1>>>,\n      evolution::dg::Tags::MortarInfo<Dim>,\n      evolution::dg::Tags::MortarMesh<Dim>,\n      evolution::dg::Tags::MortarData<Dim>,\n      evolution::dg::Tags::MortarNextTemporalId<Dim>,\n      domain::Tags::NeighborMesh<Dim>,\n      evolution::dg::subcell::Tags::MeshForGhostData<Dim>,\n      evolution::dg::subcell::Tags::CellCenteredFlux<tmpl::list<Var1>, Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>,\n      tmpl::conditional_t<\n          ExtraTesting,\n          tmpl::list<evolution::dg::subcell::Tags::\n                         InterpolatorsFromDgToNeighborFd<Dim>,\n                     ::domain::Tags::ElementMap<Dim, Frame::Grid>>,\n          tmpl::list<>>,\n      evolution::dg::subcell::Tags::ExtensionDirections<Dim>,\n      evolution::dg::subcell::Tags::SubcellOptions<Dim>>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::flatten<tmpl::list<\n          tmpl::list<ActionTesting::InitializeDataBox<initial_tags>>,\n          tmpl::conditional_t<ExtraTesting,\n                              tmpl::list<Actions::MutateApply<\n                                  evolution::dg::subcell::SetInterpolators<\n                                      Dim, Tags::Reconstructor>>>,\n                              tmpl::list<>>,\n          tmpl::list<evolution::dg::subcell::Actions::SendDataForReconstruction<\n                         Dim, typename Metavariables::GhostDataMutator,\n                         UseNodegroupDgElements>,\n                     evolution::dg::subcell::Actions::\n                         ReceiveAndSendDataForReconstruction<\n                             Dim, typename Metavariables::GhostDataMutator,\n                             UseNodegroupDgElements>,\n                     evolution::dg::subcell::Actions::\n                         ReceiveDataForReconstruction<Dim>>>>>>;\n};\n\ntemplate <size_t Dim, bool UseNodegroupDgElements, bool ExtraTesting = false>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<\n      component<Dim, Metavariables, UseNodegroupDgElements, ExtraTesting>>;\n  using system = System<Dim>;\n  using const_global_cache_tags = tmpl::flatten<tmpl::list<tmpl::conditional_t<\n      ExtraTesting, ::domain::Tags::Domain<Dim>, tmpl::list<>>>>;\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool ghost_zone_size_invoked;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool ghost_data_mutator_invoked;\n\n  struct SubcellOptions {\n    template <typename DbTagsList>\n    static constexpr size_t ghost_zone_size(\n        const db::DataBox<DbTagsList>& box) {\n      return db::get<Tags::Reconstructor>(box).ghost_zone_size();\n    }\n  };\n\n  struct GhostDataMutator {\n    using return_tags = tmpl::list<>;\n    using argument_tags = tmpl::list<::Tags::Variables<tmpl::list<Var1>>>;\n    static DataVector apply(const Variables<tmpl::list<Var1>>& vars,\n                            const size_t rdmp_size) {\n      CAPTURE(rdmp_size);\n      CAPTURE(Dim);\n      CAPTURE(vars.number_of_grid_points());\n      CHECK(\n          (rdmp_size == 0 or rdmp_size == Dim * vars.number_of_grid_points()));\n      DataVector buffer{vars.size() + rdmp_size};\n      ghost_data_mutator_invoked = true;\n      // make some trivial but testable modification\n      DataVector view{buffer.data(), vars.size()};\n      view = 2.0 * get(get<Var1>(vars));\n      return buffer;\n    }\n  };\n};\n\ntemplate <size_t Dim, bool UseNodegroupDgElements, bool ExtraTesting>\n// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim, UseNodegroupDgElements,\n                   ExtraTesting>::ghost_zone_size_invoked = false;\n// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)\ntemplate <size_t Dim, bool UseNodegroupDgElements, bool ExtraTesting>\n// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim, UseNodegroupDgElements,\n                   ExtraTesting>::ghost_data_mutator_invoked = false;\n// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)\n\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nvoid test(const bool use_cell_centered_flux) {\n  CAPTURE(Dim);\n  CAPTURE(use_cell_centered_flux);\n  using Interps = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n  using ExtensionDirs =\n      DirectionMap<Dim, interpolators_detail::ExtensionDirection<Dim>>;\n  using SubcellOptions = evolution::dg::subcell::SubcellOptions;\n\n  using metavars = Metavariables<Dim, UseNodegroupDgElements>;\n  metavars::ghost_zone_size_invoked = false;\n  metavars::ghost_data_mutator_invoked = false;\n  using comp = component<Dim, metavars, UseNodegroupDgElements>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{}};\n\n  const TimeStepId time_step_id{true, 1, Time{Slab{1.0, 2.0}, {0, 10}}};\n  const TimeStepId next_time_step_id{true, 1, Time{Slab{1.0, 2.0}, {1, 10}}};\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const evolution::dg::subcell::ActiveGrid active_grid =\n      evolution::dg::subcell::ActiveGrid::Subcell;\n\n  // ^ eta\n  // +-+-+> xi\n  // |X| |\n  // +-+-+\n  // | | |\n  // +-+-+\n  //\n  // The \"self_id\" for the element that we are considering is marked by an X in\n  // the diagram. We consider a configuration with one neighbor in the +xi\n  // direction (east_id), and (in 2d and 3d) one in the -eta (south_id)\n  // direction.\n  //\n  // In 1d there aren't any projections to test, and in 3d we only have 1\n  // element in the z-direction.\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  ElementId<Dim> self_id{};\n  ElementId<Dim> east_id{};\n  ElementId<Dim> south_id{};  // not used in 1d\n  OrientationMap<Dim> orientation = OrientationMap<Dim>::create_aligned();\n  // Note: in 2d and 3d it is the neighbor in the lower eta direction that has a\n  // non-trivial orientation.\n\n  if constexpr (Dim == 1) {\n    self_id = ElementId<Dim>{0, {{{1, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n  } else if constexpr (Dim == 2) {\n    orientation = OrientationMap<Dim>{\n        std::array{Direction<Dim>::lower_xi(), Direction<Dim>::lower_eta()}};\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{0, 0}, {0, 0}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, orientation};\n  } else {\n    static_assert(Dim == 3, \"Only implemented tests in 1, 2, and 3d\");\n    orientation = OrientationMap<Dim>{std::array{Direction<Dim>::lower_xi(),\n                                                 Direction<Dim>::lower_eta(),\n                                                 Direction<Dim>::upper_zeta()}};\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{0, 0}, {0, 0}, {0, 0}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, orientation};\n  }\n  const Element<Dim> element{self_id, neighbors};\n\n  using NeighborDataMap =\n      DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>;\n  NeighborDataMap neighbor_data{};\n  const DirectionalId<Dim> east_neighbor_id{Direction<Dim>::upper_xi(),\n                                            east_id};\n  // insert data from one of the neighbors to make sure the send actions clears\n  // it.\n  neighbor_data[east_neighbor_id] = {};\n\n  using evolved_vars_tags = tmpl::list<Var1>;\n  Variables<evolved_vars_tags> evolved_vars{\n      subcell_mesh.number_of_grid_points()};\n  // Set Var1 to the logical coords, just need some data\n  get(get<Var1>(evolved_vars)) = get<0>(logical_coordinates(subcell_mesh));\n  TimeSteppers::History<Variables<evolved_vars_tags>> time_stepper_history{};\n\n  using CellCenteredFluxTag =\n      evolution::dg::subcell::Tags::CellCenteredFlux<tmpl::list<Var1>, Dim>;\n  typename CellCenteredFluxTag::type cell_centered_flux{};\n  if (use_cell_centered_flux) {\n    cell_centered_flux = typename CellCenteredFluxTag::type{\n        subcell_mesh.number_of_grid_points()};\n    const auto logical_coords = logical_coordinates(subcell_mesh);\n    for (size_t i = 0; i < Dim; ++i) {\n      get<::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>(\n          cell_centered_flux.value())\n          .get(i) = logical_coords.get(i) + (static_cast<double>(i) + 1.0);\n    }\n  }\n\n  using MortarInfo = typename evolution::dg::Tags::MortarInfo<Dim>::type;\n  using MortarData = typename evolution::dg::Tags::MortarData<Dim>::type;\n  using MortarMesh = typename evolution::dg::Tags::MortarMesh<Dim>::type;\n  using MortarNextId =\n      typename evolution::dg::Tags::MortarNextTemporalId<Dim>::type;\n  MortarInfo mortar_info{};\n  MortarData mortar_data{};\n  MortarMesh mortar_mesh{};\n  MortarNextId mortar_next_id{};\n  mortar_info[east_neighbor_id] = evolution::dg::MortarInfo<Dim>{\n      {.time_stepping_policy = evolution::dg::TimeSteppingPolicy::EqualRate}};\n  mortar_data[east_neighbor_id] = {};\n  mortar_mesh[east_neighbor_id] = {dg_mesh.slice_away(0)};\n  mortar_next_id[east_neighbor_id] = {};\n  if constexpr (Dim > 1) {\n    const DirectionalId<Dim> south_neighbor_id{Direction<Dim>::lower_eta(),\n                                               south_id};\n    mortar_info[south_neighbor_id] = evolution::dg::MortarInfo<Dim>{\n        {.time_stepping_policy = evolution::dg::TimeSteppingPolicy::EqualRate}};\n    mortar_data[south_neighbor_id] = {};\n    mortar_mesh[south_neighbor_id] = {dg_mesh.slice_away(1)};\n    mortar_next_id[south_neighbor_id] = {};\n  }\n\n  size_t neighbor_tci_decision = 0;\n  typename evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>::type\n      neighbor_decision{};\n  for (const auto& [direction, neighbor_ids] : neighbors) {\n    (void)direction;\n    for (const auto& neighbor_id : neighbor_ids) {\n      neighbor_decision.insert(\n          std::pair{DirectionalId<Dim>{direction, neighbor_id}, 0});\n      // Initialize neighbors with garbage data. We won't ever run any actions\n      // on them, we just need to insert them to make sure things are sent to\n      // the right places. We'll check their inboxes directly.\n      ActionTesting::emplace_array_component_and_initialize<comp>(\n          &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n          neighbor_id,\n          {std::make_unique<DummyReconstructor>(),\n           time_step_id,\n           next_time_step_id,\n           Mesh<Dim>{},\n           Mesh<Dim>{},\n           active_grid,\n           Element<Dim>{},\n           NeighborDataMap{},\n           evolution::dg::subcell::RdmpTciData{},\n           neighbor_tci_decision,\n           typename evolution::dg::subcell::Tags::NeighborTciDecisions<\n               Dim>::type{},\n           Variables<evolved_vars_tags>{},\n           time_stepper_history,\n           MortarInfo{},\n           MortarMesh{},\n           MortarData{},\n           MortarNextId{},\n           typename domain::Tags::NeighborMesh<Dim>::type{},\n           typename evolution::dg::subcell::Tags::MeshForGhostData<Dim>::type{},\n           cell_centered_flux,\n           Interps{},\n           Interps{},\n           ExtensionDirs{},\n           SubcellOptions{}});\n      ++neighbor_tci_decision;\n    }\n  }\n  Interps fd_to_neighbor_fd_interpolants{};\n  Interps neighbor_dg_to_fd_interpolants{};\n  ExtensionDirs extension_directions{};\n  SubcellOptions subcell_options(\n      4.0, 1, 2.0e-3, 2.0e-4, false, false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n      std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1);\n  const int self_tci_decision = 100;\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, self_id,\n      {std::make_unique<DummyReconstructor>(), time_step_id, next_time_step_id,\n       dg_mesh, subcell_mesh, active_grid, element, neighbor_data,\n       // Explicitly set RDMP data since this would be set previously by the TCI\n       evolution::dg::subcell::RdmpTciData{{max(get(get<Var1>(evolved_vars)))},\n                                           {min(get(get<Var1>(evolved_vars)))}},\n       self_tci_decision, neighbor_decision, evolved_vars, time_stepper_history,\n       mortar_info, mortar_mesh, mortar_data, mortar_next_id,\n       typename domain::Tags::NeighborMesh<Dim>::type{},\n       typename evolution::dg::subcell::Tags::MeshForGhostData<Dim>::type{},\n       cell_centered_flux, fd_to_neighbor_fd_interpolants,\n       neighbor_dg_to_fd_interpolants, extension_directions, subcell_options});\n\n  using ghost_data_tag =\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>;\n  using rdmp_tci_data_tag = evolution::dg::subcell::Tags::DataForRdmpTci;\n  using ActionTesting::get_databox_tag;\n  CHECK(get_databox_tag<comp, ghost_data_tag>(runner, self_id).size() == 1);\n  CHECK(get_databox_tag<comp, ghost_data_tag>(runner, self_id)\n            .count(east_neighbor_id) == 1);\n\n  // Run the SendDataForReconstruction action on self_id\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  CHECK(get_databox_tag<comp, ghost_data_tag>(runner, self_id).empty());\n\n  // Check local RDMP data\n  const evolution::dg::subcell::RdmpTciData& rdmp_tci_data =\n      get_databox_tag<comp, rdmp_tci_data_tag>(runner, self_id);\n  REQUIRE(rdmp_tci_data.max_variables_values.size() == 1);\n  CHECK(rdmp_tci_data.max_variables_values[0] ==\n        max(get(get<Var1>(evolved_vars))));\n  REQUIRE(rdmp_tci_data.min_variables_values.size() == 1);\n  CHECK(rdmp_tci_data.min_variables_values[0] ==\n        min(get(get<Var1>(evolved_vars))));\n  // Check data sent to neighbors\n  const auto& directions_to_slice = element.internal_boundaries();\n  const size_t ghost_zone_size = 2;\n  const size_t rdmp_size = rdmp_tci_data.max_variables_values.size() +\n                           rdmp_tci_data.min_variables_values.size();\n  const DirectionMap<Dim, DataVector> all_sliced_data =\n      [&evolved_vars, &subcell_mesh, ghost_zone_size, &directions_to_slice,\n       &cell_centered_flux, &fd_to_neighbor_fd_interpolants]() {\n        (void)ghost_zone_size;\n        if (cell_centered_flux.has_value()) {\n          DataVector buffer{evolved_vars.size() +\n                            cell_centered_flux.value().size()};\n          std::copy(evolved_vars.data(),\n                    std::next(evolved_vars.data(),\n                              static_cast<std::ptrdiff_t>(evolved_vars.size())),\n                    buffer.data());\n          std::copy(cell_centered_flux.value().data(),\n                    std::next(cell_centered_flux.value().data(),\n                              static_cast<std::ptrdiff_t>(\n                                  cell_centered_flux.value().size())),\n                    std::next(buffer.data(), static_cast<std::ptrdiff_t>(\n                                                 evolved_vars.size())));\n          return evolution::dg::subcell::slice_data(\n              buffer, subcell_mesh.extents(), ghost_zone_size,\n              directions_to_slice, 0, fd_to_neighbor_fd_interpolants);\n        } else {\n          return evolution::dg::subcell::slice_data(\n              evolved_vars, subcell_mesh.extents(), ghost_zone_size,\n              directions_to_slice, 0, fd_to_neighbor_fd_interpolants);\n        }\n      }();\n  {\n    const auto& east_sliced_neighbor_data =\n        all_sliced_data.at(east_neighbor_id.direction());\n    DataVector expected_east_data{east_sliced_neighbor_data.size() + rdmp_size};\n    std::copy(east_sliced_neighbor_data.begin(),\n              east_sliced_neighbor_data.end(), expected_east_data.begin());\n    // We only multiple Var1 by 2, not the fluxes.\n    const size_t bound_for_vars_to_multiply =\n        east_sliced_neighbor_data.size() /\n        (cell_centered_flux.has_value() ? (1 + Dim) : 1_st);\n    for (size_t i = 0; i < bound_for_vars_to_multiply; i++) {\n      expected_east_data[i] *= 2.0;\n    }\n    std::copy(rdmp_tci_data.max_variables_values.cbegin(),\n              rdmp_tci_data.max_variables_values.cend(),\n              std::prev(expected_east_data.end(), static_cast<int>(rdmp_size)));\n    std::copy(\n        rdmp_tci_data.min_variables_values.cbegin(),\n        rdmp_tci_data.min_variables_values.cend(),\n        std::prev(expected_east_data.end(),\n                  static_cast<int>(rdmp_tci_data.min_variables_values.size())));\n\n    const auto& east_data =\n        ActionTesting::get_inbox_tag<\n            comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                      Dim, UseNodegroupDgElements>>(runner, east_id)\n            .messages;\n    const auto east_message =\n        alg::find_if(east_data.at(time_step_id), [&](const auto& entry) {\n          return entry.first ==\n                 DirectionalId<Dim>{Direction<Dim>::lower_xi(), self_id};\n        });\n    REQUIRE(east_message != east_data.at(time_step_id).end());\n    CHECK_ITERABLE_APPROX(expected_east_data,\n                          east_message->second.ghost_cell_data.value());\n    CHECK(east_message->second.tci_status == self_tci_decision);\n  }\n  if constexpr (Dim > 1) {\n    const auto direction = Direction<Dim>::lower_eta();\n\n    std::array<size_t, Dim> slice_extents{};\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(slice_extents, d) = subcell_mesh.extents(d);\n    }\n    gsl::at(slice_extents, direction.dimension()) = ghost_zone_size;\n\n    const auto& south_sliced_neighbor_data = all_sliced_data.at(direction);\n\n    DataVector expected_south_data{south_sliced_neighbor_data.size() +\n                                   rdmp_size};\n    DataVector expected_south_data_view{expected_south_data.data(),\n                                        south_sliced_neighbor_data.size()};\n    // Note: We do not orient the variables because that's currently handled\n    // by the interpolation code.\n    //\n    // orient_variables(make_not_null(&expected_south_data_view),\n    //                  all_sliced_data.at(direction),\n    //                  Index<Dim>{slice_extents},\n    //                  orientation);\n    expected_south_data_view = all_sliced_data.at(direction);\n    const size_t bound_for_vars_to_multiply =\n        expected_south_data_view.size() /\n        (cell_centered_flux.has_value() ? (1 + Dim) : 1_st);\n    for (size_t i = 0; i < bound_for_vars_to_multiply; i++) {\n      expected_south_data_view[i] *= 2.0;\n    }\n    std::copy(\n        rdmp_tci_data.max_variables_values.cbegin(),\n        rdmp_tci_data.max_variables_values.cend(),\n        std::prev(expected_south_data.end(), static_cast<int>(rdmp_size)));\n    std::copy(\n        rdmp_tci_data.min_variables_values.cbegin(),\n        rdmp_tci_data.min_variables_values.cend(),\n        std::prev(expected_south_data.end(),\n                  static_cast<int>(rdmp_tci_data.min_variables_values.size())));\n\n    const auto& south_data =\n        ActionTesting::get_inbox_tag<\n            comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                      Dim, UseNodegroupDgElements>>(runner, south_id)\n            .messages;\n    const auto south_message =\n        alg::find_if(south_data.at(time_step_id), [&](const auto& entry) {\n          return entry.first ==\n                 DirectionalId<Dim>{orientation(direction.opposite()), self_id};\n        });\n    REQUIRE(south_message != south_data.at(time_step_id).end());\n    CHECK(expected_south_data == south_message->second.ghost_cell_data.value());\n    CHECK(south_message->second.tci_status == self_tci_decision);\n  }\n\n  // Set the inbox data on self_id and then check that it gets processed\n  // correctly. We need to check both if a neighbor is doing DG or if a neighbor\n  // is doing subcell.\n  auto& self_inbox = ActionTesting::get_inbox_tag<\n      comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                Dim, UseNodegroupDgElements>>(make_not_null(&runner), self_id);\n\n  // Send data from east neighbor\n  DataVector east_ghost_cells_and_rdmp{};\n  {\n    const auto face_mesh = dg_mesh.slice_away(0);\n    east_ghost_cells_and_rdmp = DataVector{\n        subcell_mesh.slice_away(0).number_of_grid_points() * ghost_zone_size +\n        rdmp_size};\n    alg::iota(east_ghost_cells_and_rdmp, 0.0);\n    DataVector boundary_data{face_mesh.number_of_grid_points() * (2 + Dim)};\n    alg::iota(boundary_data, 1000.0);\n    const DirectionalId<Dim> east_dir_id{Direction<Dim>::upper_xi(), east_id};\n    evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n        Dim, UseNodegroupDgElements>::\n        insert_into_inbox(\n            make_not_null(&self_inbox), time_step_id,\n            std::pair{east_dir_id,\n                      evolution::dg::BoundaryData<Dim>{\n                          dg_mesh,\n                          // subcell_mesh because we are sending the\n                          // projected data right now.\n                          subcell_mesh, mortar_mesh.at(east_dir_id),\n                          east_ghost_cells_and_rdmp, boundary_data,\n                          next_time_step_id, -10}});\n  }\n  [[maybe_unused]] DataVector south_ghost_cells_and_rdmp{};\n  if constexpr (Dim > 1) {\n    south_ghost_cells_and_rdmp = DataVector{\n        subcell_mesh.slice_away(0).number_of_grid_points() * ghost_zone_size +\n        rdmp_size};\n    alg::iota(south_ghost_cells_and_rdmp, 10000.0);\n    *std::prev(south_ghost_cells_and_rdmp.end()) = -10.0;\n    evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n        Dim, UseNodegroupDgElements>::\n        insert_into_inbox(\n            make_not_null(&self_inbox), time_step_id,\n            std::pair{\n                DirectionalId<Dim>{Direction<Dim>::lower_eta(), south_id},\n                evolution::dg::BoundaryData<Dim>{\n                    dg_mesh,\n                    // subcell_mesh because we are sending the projected\n                    // data right now.\n                    subcell_mesh, std::nullopt, south_ghost_cells_and_rdmp,\n                    std::nullopt, next_time_step_id, -15}});\n  }\n\n  // Run the ReceiveAndSendDataForReconstruction action on self_id\n  // As for this test, we are using EnableExtensionDirections = false,\n  // This action will not do anything.\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  // Run the ReceiveDataForReconstruction action on self_id\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  // Check the received data was stored correctly\n  const auto& ghost_data_from_box =\n      get_databox_tag<comp, ghost_data_tag>(runner, self_id);\n  CHECK(rdmp_tci_data.max_variables_values.size() == 1);\n  CHECK(approx(rdmp_tci_data.max_variables_values[0]) ==\n        (Dim > 1 ? static_cast<double>(\n                       ghost_zone_size *\n                       subcell_mesh.slice_away(1).number_of_grid_points()) +\n                       10000.0\n                 : static_cast<double>(ghost_zone_size)));\n  CHECK(rdmp_tci_data.min_variables_values.size() == 1);\n  CHECK(approx(rdmp_tci_data.min_variables_values[0]) ==\n        (Dim > 1 ? -10.0 : min(get<0>(logical_coordinates(subcell_mesh)))));\n\n  REQUIRE(ghost_data_from_box.find(east_neighbor_id) !=\n          ghost_data_from_box.end());\n  const DataVector east_ghost_cells_and_rdmp_view{\n      east_ghost_cells_and_rdmp.data(),\n      east_ghost_cells_and_rdmp.size() - rdmp_size};\n  CHECK(ghost_data_from_box.find(east_neighbor_id)\n            ->second.neighbor_ghost_data_for_reconstruction() ==\n        east_ghost_cells_and_rdmp_view);\n  CHECK(\n      get_databox_tag<comp,\n                      evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n          runner, self_id)\n          .at(east_neighbor_id) == -10);\n  if constexpr (Dim > 1) {\n    const DirectionalId<Dim> south_neighbor_id{Direction<Dim>::lower_eta(),\n                                               south_id};\n    REQUIRE(ghost_data_from_box.find(south_neighbor_id) !=\n            ghost_data_from_box.end());\n    const DataVector south_ghost_cells_and_rdmp_view{\n        south_ghost_cells_and_rdmp.data(),\n        south_ghost_cells_and_rdmp.size() - rdmp_size};\n    CHECK(ghost_data_from_box.find(south_neighbor_id)\n              ->second.neighbor_ghost_data_for_reconstruction() ==\n          south_ghost_cells_and_rdmp_view);\n    CHECK(get_databox_tag<\n              comp, evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>>(\n              runner, self_id)\n              .at(south_neighbor_id) == -15);\n  }\n\n  // Check that we got a neighbor mesh from all neighbors.\n  size_t total_neighbors = 0;\n  const auto& neighbor_meshes =\n      get_databox_tag<comp, ::domain::Tags::NeighborMesh<Dim>>(\n          runner, self_id);\n  const auto& meshes_for_ghost_data = get_databox_tag<\n      comp, evolution::dg::subcell::Tags::MeshForGhostData<Dim>>(runner,\n                                                                 self_id);\n  for (const auto& [direction, neighbors_in_direction] : element.neighbors()) {\n    for (const auto& neighbor : neighbors_in_direction) {\n      const auto it =\n          neighbor_meshes.find(DirectionalId<Dim>{direction, neighbor});\n      REQUIRE(it != neighbor_meshes.end());\n      CHECK(it->second == dg_mesh);\n      const auto ghost_it =\n          meshes_for_ghost_data.find(DirectionalId<Dim>{direction, neighbor});\n      REQUIRE(ghost_it != meshes_for_ghost_data.end());\n      CHECK(ghost_it->second == subcell_mesh);\n      ++total_neighbors;\n    }\n  }\n  CHECK(neighbor_meshes.size() == total_neighbors);\n}\n\ntemplate <size_t Dim>\nElement<Dim> create_element(const std::vector<Block<Dim>>& blocks,\n                            const ElementId<Dim>& self_id,\n                            const std::vector<Direction<Dim>>& directions,\n                            const std::vector<ElementId<Dim>>& neighbor_ids) {\n  ASSERT(neighbor_ids.size() == directions.size(),\n         \"The number of neighbor IDs must match the number of directions.\");\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  const size_t self_block_id = self_id.block_id();\n  for (size_t i = 0; i < directions.size(); ++i) {\n    const auto& direction = directions[i];\n    const auto& neighbor_id = neighbor_ids[i];\n    neighbors[direction] = Neighbors<Dim>{\n        {neighbor_id},\n        self_block_id == neighbor_id.block_id()\n            ? OrientationMap<Dim>::create_aligned()\n            : blocks[self_block_id].neighbors().at(direction).orientation(\n                  neighbor_id.block_id())};\n  }\n  return Element<Dim>{self_id, std::move(neighbors)};\n}\n\nvoid add_rdmp_data_to_datavector(\n    gsl::not_null<DataVector*> data_to_fill, const size_t original_data_size,\n    const evolution::dg::subcell::RdmpTciData& rdmp_tci_data) {\n  const size_t rdmp_max_values_size = rdmp_tci_data.max_variables_values.size();\n\n  // Copy max_variables_values after the original data\n  std::copy(\n      rdmp_tci_data.max_variables_values.cbegin(),\n      rdmp_tci_data.max_variables_values.cend(),\n      data_to_fill->begin() + static_cast<std::ptrdiff_t>(original_data_size));\n\n  // Copy min_variables_values after max_variables_values\n  std::copy(\n      rdmp_tci_data.min_variables_values.cbegin(),\n      rdmp_tci_data.min_variables_values.cend(),\n      data_to_fill->begin() + static_cast<std::ptrdiff_t>(\n                                  original_data_size + rdmp_max_values_size));\n}\n\ntemplate <size_t Dim, bool UseNodegroupDgElements, typename MetaVars>\nDataVector compute_expected_ghost_data(\n    const ActionTesting::MockRuntimeSystem<MetaVars>& runner,\n    const Element<Dim>& element, const Direction<Dim>& direction,\n    const ::Mesh<3> subcell_mesh, const bool enable_extension,\n    const bool use_cell_centered_flux,\n    const Variables<tmpl::list<Var1>>& evolved_vars,\n    const evolution::dg::subcell::RdmpTciData& rdmp_tci_data,\n    const TimeStepId time_step_id, const size_t ghost_zone_size) {\n  using comp = component<Dim, MetaVars, UseNodegroupDgElements, true>;\n  using CellCenteredFluxTag =\n      evolution::dg::subcell::Tags::CellCenteredFlux<tmpl::list<Var1>, Dim>;\n  using ActionTesting::get_databox_tag;\n  const auto& element_id = element.id();\n  const auto& fd_to_neighbor_fd_interpolants = get_databox_tag<\n      comp, evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>(\n      runner, element_id);\n  const auto& extension_directions = get_databox_tag<\n      comp, evolution::dg::subcell::Tags::ExtensionDirections<Dim>>(runner,\n                                                                    element_id);\n  const size_t rdmp_size = rdmp_tci_data.max_variables_values.size() +\n                           rdmp_tci_data.min_variables_values.size();\n\n  if (enable_extension and extension_directions.contains(direction)) {\n    const auto& receiver_id = element.neighbors().at(direction).ids().begin();\n    const auto& extender_direction =\n        extension_directions.at(direction).direction_to_extend;\n    const auto& extender_id =\n        element.neighbors().at(extender_direction).ids().begin();\n    const auto& data_from_extender =\n        alg::find_if(\n            ActionTesting::get_inbox_tag<\n                comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                          Dim, UseNodegroupDgElements>>(runner, element_id)\n                .messages.at(time_step_id),\n            [&](const auto& entry) {\n              return entry.first ==\n                     DirectionalId<Dim>{extender_direction, *extender_id};\n            })\n            ->second.ghost_cell_data.value();\n\n    const DataVector relevant_ghost_data;\n    const size_t ghost_data_size = data_from_extender.size() - rdmp_size;\n    make_const_view(make_not_null(&relevant_ghost_data), data_from_extender, 0,\n                    ghost_data_size);\n\n    DataVector volume_data_to_slice = [&evolved_vars, use_cell_centered_flux,\n                                       &runner, &element_id]() {\n      if (use_cell_centered_flux) {\n        const auto& cell_centered_flux =\n            get_databox_tag<comp, CellCenteredFluxTag>(\n                runner, element_id);\n        DataVector buffer{evolved_vars.size() +\n                          cell_centered_flux.value().size()};\n        std::copy(evolved_vars.data(),\n                  std::next(evolved_vars.data(),\n                            static_cast<std::ptrdiff_t>(evolved_vars.size())),\n                  buffer.data());\n        std::copy(cell_centered_flux.value().data(),\n                  std::next(cell_centered_flux.value().data(),\n                            static_cast<std::ptrdiff_t>(\n                                cell_centered_flux.value().size())),\n                  std::next(buffer.data(),\n                            static_cast<std::ptrdiff_t>(evolved_vars.size())));\n        return buffer;\n      } else {\n        return get(get<Var1>(evolved_vars));\n      }\n    }();\n    for (size_t i = 0; i < evolved_vars.size(); ++i) {\n      volume_data_to_slice[i] *= 2.0;\n    }\n    const DataVector combined_data =\n        evolution::dg::subcell::combine_volume_ghost_data(\n            volume_data_to_slice, relevant_ghost_data, subcell_mesh.extents(),\n            ghost_zone_size, extender_direction);\n    DataVector exepected_ghost_data{ghost_data_size + rdmp_size};\n    auto result_span =\n        gsl::make_span(exepected_ghost_data.data(), ghost_data_size);\n    const auto& interpolant =\n        fd_to_neighbor_fd_interpolants\n            .at(DirectionalId<Dim>{direction, *receiver_id})\n            .value();\n    interpolant.interpolate(\n        make_not_null(&result_span),\n        gsl::make_span(combined_data.data(), combined_data.size()));\n    add_rdmp_data_to_datavector(make_not_null(&exepected_ghost_data),\n                                ghost_data_size, rdmp_tci_data);\n    return exepected_ghost_data;\n  } else {\n    std::unordered_set<Direction<Dim>> directions_to_slice{direction};\n    const DirectionMap<Dim, DataVector> sliced_volume_data =\n        [&evolved_vars, &subcell_mesh, ghost_zone_size, &directions_to_slice,\n         &fd_to_neighbor_fd_interpolants, &runner, use_cell_centered_flux,\n         &element_id]() {\n          if (use_cell_centered_flux) {\n            const auto& cell_centered_flux =\n                get_databox_tag<comp, CellCenteredFluxTag>(\n                    runner, element_id);\n            DataVector buffer{evolved_vars.size() +\n                              cell_centered_flux.value().size()};\n            std::copy(\n                evolved_vars.data(),\n                std::next(evolved_vars.data(),\n                          static_cast<std::ptrdiff_t>(evolved_vars.size())),\n                buffer.data());\n            std::copy(cell_centered_flux.value().data(),\n                      std::next(cell_centered_flux.value().data(),\n                                static_cast<std::ptrdiff_t>(\n                                    cell_centered_flux.value().size())),\n                      std::next(buffer.data(), static_cast<std::ptrdiff_t>(\n                                                   evolved_vars.size())));\n            return evolution::dg::subcell::slice_data(\n                buffer, subcell_mesh.extents(), ghost_zone_size,\n                directions_to_slice, 0, fd_to_neighbor_fd_interpolants);\n          } else {\n            return evolution::dg::subcell::slice_data(\n                evolved_vars, subcell_mesh.extents(), ghost_zone_size,\n                directions_to_slice, 0, fd_to_neighbor_fd_interpolants);\n          }\n        }();\n    const DataVector& sliced_data_for_direction =\n        sliced_volume_data.at(direction);\n    const size_t sliced_data_size = sliced_data_for_direction.size();\n    const size_t total_size = sliced_data_size + rdmp_size;\n    DataVector expected_ghost_data(total_size);\n\n    // Copy the sliced data into the beginning of the result\n    std::copy(sliced_data_for_direction.begin(),\n              sliced_data_for_direction.end(), expected_ghost_data.begin());\n    // We only multiple Var1 by 2, not the fluxes.\n    const size_t bound_for_vars_to_multiply =\n        sliced_data_size / (use_cell_centered_flux ? (1 + Dim) : 1_st);\n\n    for (size_t i = 0; i < bound_for_vars_to_multiply; i++) {\n      expected_ghost_data[i] *= 2.0;\n    }\n    add_rdmp_data_to_datavector(make_not_null(&expected_ghost_data),\n                                sliced_data_size, rdmp_tci_data);\n\n    return expected_ghost_data;\n  }\n}\n\n// Test ReceiveAndSendDataForReconstruction action\n// for both EnableExtensionDirections options.\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nvoid test_receive_and_send_data(const bool enable_extension,\n                                const bool use_cell_centered_flux) {\n  CAPTURE(Dim);\n  CAPTURE(enable_extension);\n  CAPTURE(use_cell_centered_flux);\n  // create Sphere domain, and 4 releveant elements.\n  domain::creators::register_derived_with_charm();\n\n  using Sphere = domain::creators::Sphere;\n  using Interior = std::variant<domain::creators::Sphere::Excision,\n                                domain::creators::Sphere::InnerCube>;\n  size_t refinement_level = 2;\n  const Sphere domain_creator{1.,\n                              5.,\n                              Interior{Sphere::Excision{}},\n                              refinement_level,\n                              6_st,\n                              true,\n                              std::nullopt,\n                              {},\n                              domain::CoordinateMaps::Distribution::Linear,\n                              ShellWedges::All,\n                              std::nullopt};\n  auto domain = domain_creator.create_domain();\n\n  using metavars = Metavariables<Dim, UseNodegroupDgElements, true>;\n  metavars::ghost_zone_size_invoked = false;\n  metavars::ghost_data_mutator_invoked = false;\n  using comp = component<Dim, metavars, UseNodegroupDgElements, true>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{std::move(domain_creator.create_domain())}};\n\n  const std::vector<Block<3>>& blocks = domain.blocks();\n  // Create the elements that we will use in the test.\n  // We creat a sender, an extender, a receiver, and a receiver extender.\n  // sender and recevier are in different blocks.\n  // extender and receiver extender are in the same block as sender and\n  // receiver, respectively. they provide the data that is sent to the\n  // sender and receiver such that the sender and receiver can\n  // perform the new action when enable_extension is true.\n  const auto sender =\n      create_element<3>(blocks, ElementId<3>{0, {{{2, 0}, {2, 0}, {2, 0}}}},\n                        {Direction<3>::upper_eta(), Direction<3>::lower_xi()},\n                        {ElementId<3>{0, {{{2, 0}, {2, 1}, {2, 0}}}},\n                         ElementId<3>{5, {{{2, 3}, {2, 3}, {2, 0}}}}});\n  const auto extender =\n      create_element<3>(blocks, ElementId<3>{0, {{{2, 0}, {2, 1}, {2, 0}}}},\n                        {Direction<3>::lower_eta()},\n                        {ElementId<3>{0, {{{2, 0}, {2, 0}, {2, 0}}}}});\n  const auto receiver =\n      create_element<3>(blocks, ElementId<3>{5, {{{2, 3}, {2, 3}, {2, 0}}}},\n                        {Direction<3>::upper_eta(), Direction<3>::lower_xi()},\n                        {ElementId<3>{0, {{{2, 0}, {2, 0}, {2, 0}}}},\n                         ElementId<3>{5, {{{2, 2}, {2, 3}, {2, 0}}}}});\n  const auto receiver_extender =\n      create_element<3>(blocks, ElementId<3>{5, {{{2, 2}, {2, 3}, {2, 0}}}},\n                        {Direction<3>::upper_xi()},\n                        {ElementId<3>{5, {{{2, 3}, {2, 3}, {2, 0}}}}});\n\n  const std::array<const Element<Dim>*, 4> elements{\n      &sender, &extender, &receiver, &receiver_extender};\n\n  // Create a map from ElementId to Element* for easy access\n  std::unordered_map<ElementId<Dim>, const Element<Dim>*> id_to_element;\n  for (const auto* element : elements) {\n    id_to_element[element->id()] = element;\n  }\n\n  using Interps = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n  using ExtensionDirs =\n      DirectionMap<Dim, interpolators_detail::ExtensionDirection<Dim>>;\n  using SubcellOptions = evolution::dg::subcell::SubcellOptions;\n  const SubcellOptions subcell_options(\n      4.0, 1, 2.0e-3, 2.0e-4, enable_extension, enable_extension,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n      std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1);\n  const ::Mesh<3> dg_mesh{6, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const ::Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  const TimeStepId time_step_id{true, 1, Time{Slab{1.0, 2.0}, {0, 10}}};\n  const TimeStepId next_time_step_id{true, 1, Time{Slab{1.0, 2.0}, {1, 10}}};\n  const evolution::dg::subcell::ActiveGrid active_grid =\n      evolution::dg::subcell::ActiveGrid::Subcell;\n  using MortarInfo = typename evolution::dg::Tags::MortarInfo<Dim>::type;\n  using MortarData = typename evolution::dg::Tags::MortarData<Dim>::type;\n  using MortarMesh = typename evolution::dg::Tags::MortarMesh<Dim>::type;\n  using MortarNextId =\n      typename evolution::dg::Tags::MortarNextTemporalId<Dim>::type;\n  using ActionTesting::get_databox_tag;\n  using evolved_vars_tags = tmpl::list<Var1>;\n  Variables<evolved_vars_tags> evolved_vars{\n      subcell_mesh.number_of_grid_points()};\n  // Set Var1 to the logical coords, just need some data\n  get(get<Var1>(evolved_vars)) = get<0>(logical_coordinates(subcell_mesh));\n  TimeSteppers::History<Variables<evolved_vars_tags>> time_stepper_history{};\n\n  using CellCenteredFluxTag =\n      evolution::dg::subcell::Tags::CellCenteredFlux<tmpl::list<Var1>, Dim>;\n  typename CellCenteredFluxTag::type cell_centered_flux{};\n  if (use_cell_centered_flux) {\n    cell_centered_flux = typename CellCenteredFluxTag::type{\n        subcell_mesh.number_of_grid_points()};\n    const auto logical_coords = logical_coordinates(subcell_mesh);\n    for (size_t i = 0; i < Dim; ++i) {\n      get<::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>(\n          cell_centered_flux.value())\n          .get(i) = logical_coords.get(i) + (static_cast<double>(i) + 1.0);\n    }\n  }\n  Interps fd_to_neighbor_fd_interpolants{};\n  Interps neighbor_dg_to_fd_interpolants{};\n  Interps dg_to_neighbor_fd_interpolants{};\n  ExtensionDirs extension_directions{};\n\n  const int self_tci_decision = 100;\n\n  for (const auto* element : elements) {\n    const auto& element_id = element->id();\n    DirectionalIdMap<Dim, evolution::dg::subcell::GhostData> neighbor_data{};\n    typename evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>::type\n        neighbor_decision{};\n\n    MortarInfo mortar_info{};\n    MortarData mortar_data{};\n    MortarMesh mortar_mesh{};\n    MortarNextId mortar_next_id{};\n\n    for (const auto& [direction, neighbors] : element->neighbors()) {\n      for (const auto& neighbor_id : neighbors.ids()) {\n        const DirectionalId<Dim> mortar_id{direction, neighbor_id};\n        neighbor_data[mortar_id] = evolution::dg::subcell::GhostData{};\n        neighbor_decision.insert(std::pair{mortar_id, 100});\n\n        mortar_info[mortar_id] = evolution::dg::MortarInfo<Dim>{\n            {.time_stepping_policy =\n                 evolution::dg::TimeSteppingPolicy::EqualRate}};\n        mortar_data[mortar_id] = {};\n        mortar_mesh[mortar_id] = dg_mesh.slice_away(direction.dimension());\n        mortar_next_id[mortar_id] = {};\n      }\n    }\n\n    ActionTesting::emplace_array_component_and_initialize<comp>(\n        &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n        element_id,\n        {std::make_unique<DummyReconstructor>(), time_step_id,\n         next_time_step_id, dg_mesh, subcell_mesh, active_grid, *element,\n         neighbor_data,\n         // Explicitly set RDMP data since this would be set previously by the\n         // TCI\n         evolution::dg::subcell::RdmpTciData{\n             {max(get(get<Var1>(evolved_vars)))},\n             {min(get(get<Var1>(evolved_vars)))}},\n         self_tci_decision, neighbor_decision, evolved_vars,\n         time_stepper_history, mortar_info, mortar_mesh, mortar_data,\n         mortar_next_id, typename domain::Tags::NeighborMesh<Dim>::type{},\n         typename evolution::dg::subcell::Tags::MeshForGhostData<Dim>::type{},\n         cell_centered_flux, fd_to_neighbor_fd_interpolants,\n         neighbor_dg_to_fd_interpolants, dg_to_neighbor_fd_interpolants,\n         ElementMap<3, Frame::Grid>{element_id, blocks[element_id.block_id()]},\n         extension_directions, subcell_options});\n  }\n\n  // SetInterpolator\n  for (const auto* element : elements) {\n    const auto& element_id = element->id();\n    ActionTesting::next_action<comp>(make_not_null(&runner), element_id);\n    if (not enable_extension) {\n      const size_t extension_directions_size =\n          get_databox_tag<\n              comp, evolution::dg::subcell::Tags::ExtensionDirections<Dim>>(\n              runner, element_id)\n              .size();\n      REQUIRE(extension_directions_size == 0);\n    }\n  }\n\n  // Let use generate expected sender_ghost_data_non_block_boundary\n  // and sender_ghost_data_block_boundary.\n  const size_t ghost_zone_size = 2;\n  using rdmp_tci_data_tag = evolution::dg::subcell::Tags::DataForRdmpTci;\n  const evolution::dg::subcell::RdmpTciData& rdmp_tci_data =\n      get_databox_tag<comp, rdmp_tci_data_tag>(runner, sender.id());\n\n  // SendDataForReconstruction\n  for (const auto* element : elements) {\n    const auto& element_id = element->id();\n    ActionTesting::next_action<comp>(make_not_null(&runner), element_id);\n  }\n  // Check that the inbox has the expected data size depending on the\n  // enable_extension option.\n  for (const auto* element : elements) {\n    const auto& element_id = element->id();\n    const size_t neighbor_size = element->neighbors().size();\n    const size_t extension_directions_size =\n        get_databox_tag<comp,\n                        evolution::dg::subcell::Tags::ExtensionDirections<Dim>>(\n            runner, element_id)\n            .size();\n    const size_t received_data_size =\n        ActionTesting::get_inbox_tag<\n            comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                      Dim, UseNodegroupDgElements>>(runner, element_id)\n            .messages.at(time_step_id)\n            .size();\n    REQUIRE(received_data_size == (neighbor_size - extension_directions_size));\n  }\n\n  // ReceiveAndSendDataForReconstruction\n  for (const auto* element : elements) {\n    const auto& element_id = element->id();\n    ActionTesting::next_action<comp>(make_not_null(&runner), element_id);\n  }\n  // The inbox size should match the neighbor size at this point for\n  // all elements, regardless of the enable_extension option.\n  // Compare the contents as well between the data in the inbox\n  // versus the expected data.\n  for (const auto* element : elements) {\n    const auto& element_id = element->id();\n    const size_t neighbor_size = element->neighbors().size();\n    const size_t received_data_size =\n        ActionTesting::get_inbox_tag<\n            comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                      Dim, UseNodegroupDgElements>>(runner, element_id)\n            .messages.at(time_step_id)\n            .size();\n    REQUIRE(received_data_size == neighbor_size);\n\n    for (const auto& [direction, neighbors] : element->neighbors()) {\n      // only one neighbor in each direction\n      const auto& neighbor_id = neighbors.ids().begin();\n      const DirectionalId<Dim> directional_id{direction, *neighbor_id};\n      const auto& orientation = neighbors.orientation(*neighbor_id);\n      const Direction<Dim> direction_from_neighbor =\n          orientation(direction.opposite());\n      const auto& neighbor_element = *(id_to_element.at(*neighbor_id));\n      const auto& ghost_data =\n          alg::find_if(\n              ActionTesting::get_inbox_tag<\n                  comp,\n                  evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                      Dim, UseNodegroupDgElements>>(runner, element_id)\n                  .messages.at(time_step_id),\n              [&](const auto& entry) { return entry.first == directional_id; })\n              ->second.ghost_cell_data.value();\n      const DataVector expected_ghost_data =\n          compute_expected_ghost_data<Dim, UseNodegroupDgElements>(\n              runner, neighbor_element, direction_from_neighbor, subcell_mesh,\n              enable_extension, use_cell_centered_flux, evolved_vars,\n              rdmp_tci_data, time_step_id, ghost_zone_size);\n      CHECK_ITERABLE_APPROX(expected_ghost_data, ghost_data);\n    }\n  }\n\n  // ReceiveDataForReconstruction\n  for (const auto* element : elements) {\n    const auto& element_id = element->id();\n    ActionTesting::next_action<comp>(make_not_null(&runner), element_id);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Actions.ReconstructionCommunication\",\n                  \"[Evolution][Unit]\") {\n  for (const bool use_cell_centered_flux : {false, true}) {\n    test<1, false>(use_cell_centered_flux);\n    test<2, false>(use_cell_centered_flux);\n    test<3, false>(use_cell_centered_flux);\n    test_receive_and_send_data<3, false>(false, use_cell_centered_flux);\n    test_receive_and_send_data<3, false>(true, use_cell_centered_flux);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Actions/Test_SelectNumericalMethod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/DgSubcell/Actions/Labels.hpp\"\n#include \"Evolution/DgSubcell/Actions/SelectNumericalMethod.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Use Actions::Label<DummyLabel> actions to space out the action list so we can\n// check that SelectNumericalMethod is jumping to the right locations.\ntemplate <size_t Index>\nstruct DummyLabel {};\n\ntemplate <typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tag_list = tmpl::list<>;\n\n  using initial_tags = tmpl::list<evolution::dg::subcell::Tags::ActiveGrid>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<\n          ActionTesting::InitializeDataBox<initial_tags>,\n          evolution::dg::subcell::Actions::SelectNumericalMethod,\n          Actions::Label<DummyLabel<0>>,\n          Actions::Label<evolution::dg::subcell::Actions::Labels::BeginDg>,\n          Actions::Label<DummyLabel<1>>,\n          Actions::Label<evolution::dg::subcell::Actions::Labels::BeginSubcell>,\n          Actions::Label<DummyLabel<2>>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<component<Metavariables>>;\n};\n\nvoid test(const evolution::dg::subcell::ActiveGrid active_grid) {\n  CAPTURE(active_grid);\n  using metavars = Metavariables;\n  using comp = component<metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{}};\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0,\n      {active_grid});\n\n  // Invoke the SelectNumericalMethod action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), 0);\n  // SelectNumericalMethod jumps to `index_of<Label>+1`\n  const size_t expected_next_action_index =\n      (active_grid == evolution::dg::subcell::ActiveGrid::Dg ? 4 : 6);\n  CHECK(ActionTesting::get_next_action_index<comp>(runner, 0) ==\n        expected_next_action_index);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Actions.SelectNumericalMethod\",\n                  \"[Evolution][Unit]\") {\n  test(evolution::dg::subcell::ActiveGrid::Dg);\n  test(evolution::dg::subcell::ActiveGrid::Subcell);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Actions/Test_TakeTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <deque>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Actions/TakeTimeStep.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using initial_tags = tmpl::list<\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      domain::Tags::ElementMap<Dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                  Frame::Inertial>,\n      evolution::dg::Tags::MortarData<Dim>>;\n\n  using initial_compute_tags = tmpl::list<\n      evolution::dg::subcell::Tags::LogicalCoordinatesCompute<Dim>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGridCompute<\n          ::domain::Tags::ElementMap<Dim, Frame::Grid>, Dim>,\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToGridCompute<\n          Dim>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<\n          ActionTesting::InitializeDataBox<initial_tags, initial_compute_tags>,\n          evolution::dg::subcell::fd::Actions::TakeTimeStep<\n              typename Metavariables::TimeDerivative>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using const_global_cache_tags = tmpl::list<>;\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool time_derivative_invoked;\n\n  struct TimeDerivative {\n    template <typename DbTagsList>\n    static void apply(const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n      time_derivative_invoked = true;\n      CHECK(db::get<evolution::dg::subcell::Tags::Mesh<Dim>>(*box) ==\n            Mesh<Dim>(5, Spectral::Basis::FiniteDifference,\n                      Spectral::Quadrature::CellCentered));\n      const auto inv_jacobian =\n          db::get<domain::Tags::ElementMap<Dim, Frame::Grid>>(*box)\n              .inv_jacobian(db::get<evolution::dg::subcell::Tags::Coordinates<\n                                Dim, Frame::ElementLogical>>(*box));\n    }\n  };\n};\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim>::time_derivative_invoked = false;\n\ntemplate <size_t Dim>\nauto make_grid_map() {\n  using domain::make_coordinate_map_base;\n  using domain::CoordinateMaps::Affine;\n  if constexpr (Dim == 1) {\n    return make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n        Affine(-1.0, 1.0, 2.0, 5.0));\n  } else if constexpr (Dim == 2) {\n    using domain::CoordinateMaps::ProductOf2Maps;\n    return make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n        ProductOf2Maps<Affine, Affine>(Affine(-1.0, 1.0, -1.0, -0.8),\n                                       Affine(-1.0, 1.0, -1.0, -0.8)),\n        domain::CoordinateMaps::Wedge<2>(\n            0.5, 0.75, 1.0, 1.0, OrientationMap<2>::create_aligned(), false));\n  } else {\n    using domain::CoordinateMaps::ProductOf3Maps;\n    return make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n        ProductOf3Maps<Affine, Affine, Affine>(Affine(-1.0, 1.0, -1.0, -0.8),\n                                               Affine(-1.0, 1.0, -1.0, -0.8),\n                                               Affine(-1.0, 1.0, 0.8, 1.0)),\n        domain::CoordinateMaps::Wedge<3>(\n            0.5, 0.75, 1.0, 1.0, OrientationMap<3>::create_aligned(), false));\n  }\n}\n\ntemplate <size_t Dim>\nauto make_inertial_map() {\n  return domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n      domain::CoordinateMaps::Identity<Dim>{});\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  using metavars = Metavariables<Dim>;\n  metavars::time_derivative_invoked = false;\n\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{}};\n\n  const Mesh<Dim> subcell_mesh{5, Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::CellCentered};\n  // Set up nonsense mortar data since we only need to check that it got\n  // cleared.\n  DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>> mortar_data{};\n  evolution::dg::MortarDataHolder<Dim> lower_xi_data{};\n  lower_xi_data.local().face_mesh = subcell_mesh.slice_away(0);\n  lower_xi_data.local().mortar_data = DataVector{1.1, 2.43, 7.8};\n  const DirectionalId<Dim> lower_id{Direction<Dim>::lower_xi(),\n                                    ElementId<Dim>{1}};\n  mortar_data[lower_id] = lower_xi_data;\n\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0,\n      {subcell_mesh,\n       ElementMap<Dim, Frame::Grid>{ElementId<Dim>{0}, make_grid_map<Dim>()},\n       make_inertial_map<Dim>(), mortar_data});\n\n  CHECK(ActionTesting::get_databox_tag<comp,\n                                       evolution::dg::Tags::MortarData<Dim>>(\n            runner, 0)\n            .at(lower_id)\n            .local()\n            .mortar_data.has_value());\n\n  // Invoke the TakeTimeStep action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), 0);\n\n  CHECK_FALSE(ActionTesting::get_databox_tag<\n                  comp, evolution::dg::Tags::MortarData<Dim>>(runner, 0)\n                  .at(lower_id)\n                  .local()\n                  .mortar_data.has_value());\n  CHECK(metavars::time_derivative_invoked);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Fd.Actions.TakeTimeStep\",\n                  \"[Evolution][Unit]\") {\n  using Affine = domain::CoordinateMaps::Affine;\n  PUPable_reg(SINGLE_ARG(\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Grid, Affine>));\n  PUPable_reg(SINGLE_ARG(domain::CoordinateMap<\n                         Frame::BlockLogical, Frame::Grid,\n                         domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>,\n                         domain::CoordinateMaps::Wedge<2>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<\n                 Frame::BlockLogical, Frame::Grid,\n                 domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>,\n                 domain::CoordinateMaps::Wedge<3>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<1>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<2>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<3>>));\n\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Actions/Test_TciAndRollback.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <deque>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndRollback.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/MeshForGhostData.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciGridHistory.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nclass DummyReconstructor {\n public:\n  static size_t ghost_zone_size() { return 2; }\n  void pup(PUP::er& /*p*/) {}\n};\n\nnamespace Tags {\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<DummyReconstructor>;\n};\n}  // namespace Tags\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct PrimVar1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim, bool HasPrims>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  static constexpr bool has_primitive_and_conservative_vars = HasPrims;\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n  using primitive_variables_tag = ::Tags::Variables<tmpl::list<PrimVar1>>;\n};\n\ntemplate <size_t>\nstruct DummyLabel;\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using initial_tags = tmpl::append<\n      tmpl::list<\n          Tags::Reconstructor, ::Tags::TimeStepId, domain::Tags::Mesh<Dim>,\n          evolution::dg::subcell::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n          evolution::dg::subcell::Tags::ActiveGrid,\n          evolution::dg::subcell::Tags::DidRollback,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n          evolution::dg::subcell::Tags::TciDecision,\n          evolution::dg::subcell::Tags::DataForRdmpTci,\n          evolution::dg::subcell::Tags::MeshForGhostData<Dim>,\n          ::Tags::Variables<tmpl::list<Var1>>,\n          ::Tags::HistoryEvolvedVariables<::Tags::Variables<tmpl::list<Var1>>>,\n          SelfStart::Tags::InitialValue<::Tags::Variables<tmpl::list<Var1>>>,\n          evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>,\n          evolution::dg::subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>>,\n      tmpl::conditional_t<\n          Metavariables::has_prims,\n          tmpl::list<::Tags::Variables<tmpl::list<PrimVar1>>,\n                     SelfStart::Tags::InitialValue<\n                         ::Tags::Variables<tmpl::list<PrimVar1>>>>,\n          tmpl::list<>>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<initial_tags>,\n                 evolution::dg::subcell::Actions::TciAndRollback<\n                     typename Metavariables::TciOnDgGrid>,\n                 Actions::Label<DummyLabel<0>>,\n                 Actions::Label<evolution::dg::subcell::Actions::Labels::\n                                    BeginSubcellAfterDgRollback>,\n                 Actions::Label<DummyLabel<1>>>>>;\n};\n\ntemplate <size_t Dim, bool HasPrims>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  static constexpr bool has_prims = HasPrims;\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using system = System<Dim, HasPrims>;\n  using analytic_variables_tags = typename system::variables_tag::tags_list;\n  using const_global_cache_tags =\n      tmpl::list<evolution::dg::subcell::Tags::SubcellOptions<Dim>>;\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool rdmp_fails;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool tci_fails;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool tci_invoked;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool expected_evolve_on_dg_after_tci_failure;\n\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled_at_external_boundary = false;\n\n    template <typename DbTagsList>\n    static constexpr size_t ghost_zone_size(\n        const db::DataBox<DbTagsList>& box) {\n      return db::get<Tags::Reconstructor>(box).ghost_zone_size();\n    }\n  };\n\n  struct TciOnDgGrid {\n    using return_tags = tmpl::list<>;\n    using argument_tags =\n        tmpl::list<::Tags::Variables<tmpl::list<Var1>>, domain::Tags::Mesh<Dim>,\n                   evolution::dg::subcell::Tags::Mesh<Dim>,\n                   evolution::dg::subcell::Tags::DataForRdmpTci,\n                   evolution::dg::subcell::Tags::SubcellOptions<Dim>>;\n\n    static std::tuple<int, evolution::dg::subcell::RdmpTciData> apply(\n        const Variables<tmpl::list<Var1>>& dg_vars, const Mesh<Dim>& dg_mesh,\n        const Mesh<Dim>& subcell_mesh,\n        const evolution::dg::subcell::RdmpTciData& past_rdmp_data,\n        const evolution::dg::subcell::SubcellOptions& subcell_options,\n        const double persson_exponent,\n        const bool evolve_on_dg_after_tci_failure) {\n      // match with global static variable in metavariables\n\n      // assign value of passed in variable\n      using metavars = Metavariables<Dim, HasPrims>;\n\n      Variables<tmpl::list<Var1>> projected_vars{\n          subcell_mesh.number_of_grid_points()};\n      evolution::dg::subcell::fd::project(\n          make_not_null(&projected_vars), dg_vars, dg_mesh,\n          evolution::dg::subcell::fd::mesh(dg_mesh).extents());\n      // Set RDMP TCI data\n      using std::max;\n      using std::min;\n      evolution::dg::subcell::RdmpTciData rdmp_data{};\n      rdmp_data.max_variables_values = DataVector{max(\n          max(get(get<Var1>(dg_vars))), max(get(get<Var1>(projected_vars))))};\n      rdmp_data.min_variables_values = DataVector{min(\n          min(get(get<Var1>(dg_vars))), min(get(get<Var1>(projected_vars))))};\n\n      CHECK(approx(persson_exponent) == 4.0);\n      CHECK(evolve_on_dg_after_tci_failure ==\n            metavars::expected_evolve_on_dg_after_tci_failure);\n      tci_invoked = true;\n      const bool rdmp_result =\n          static_cast<bool>(evolution::dg::subcell::rdmp_tci(\n              rdmp_data.max_variables_values, rdmp_data.min_variables_values,\n              past_rdmp_data.max_variables_values,\n              past_rdmp_data.min_variables_values,\n              subcell_options.rdmp_delta0(), subcell_options.rdmp_epsilon()));\n      const int decision = rdmp_result ? 10 : (tci_fails ? 5 : 0);\n      return {decision, std::move(rdmp_data)};\n    }\n  };\n};\n\ntemplate <size_t Dim, bool HasPrims>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim, HasPrims>::rdmp_fails = false;\ntemplate <size_t Dim, bool HasPrims>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim, HasPrims>::tci_fails = false;\ntemplate <size_t Dim, bool HasPrims>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim, HasPrims>::tci_invoked = false;\ntemplate <size_t Dim, bool HasPrims>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim, HasPrims>::expected_evolve_on_dg_after_tci_failure =\n    false;\n\ntemplate <size_t Dim>\nElement<Dim> create_element(const bool with_neighbors) {\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  if (with_neighbors) {\n    for (size_t i = 0; i < 2 * Dim; ++i) {\n      neighbors[gsl::at(Direction<Dim>::all_directions(), i)] = Neighbors<Dim>{\n          {ElementId<Dim>{i + 1, {}}}, OrientationMap<Dim>::create_aligned()};\n    }\n  }\n  return Element<Dim>{ElementId<Dim>{0, {}}, neighbors};\n}\n\ntemplate <size_t Dim>\nclass TestCreator : public DomainCreator<Dim> {\n  Domain<Dim> create_domain() const override { return Domain<Dim>{}; }\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override {\n    return {};\n  }\n\n  std::vector<std::string> block_names() const override {\n    return {\"Block0\", \"Block1\"};\n  }\n\n  std::vector<std::array<size_t, Dim>> initial_extents() const override {\n    return {};\n  }\n\n  std::vector<std::array<size_t, Dim>> initial_refinement_levels()\n      const override {\n    return {};\n  }\n};\n\ntemplate <size_t Dim, bool HasPrims>\nvoid test_impl(const bool rdmp_fails, const bool tci_fails,\n               const bool always_use_subcell, const bool self_starting,\n               const bool with_neighbors, const bool use_halo,\n               const bool neighbor_is_troubled,\n               const bool disable_subcell_in_block) {\n  CAPTURE(Dim);\n  CAPTURE(rdmp_fails);\n  CAPTURE(tci_fails);\n  CAPTURE(always_use_subcell);\n  CAPTURE(self_starting);\n  CAPTURE(with_neighbors);\n  CAPTURE(use_halo);\n  CAPTURE(neighbor_is_troubled);\n  CAPTURE(disable_subcell_in_block);\n\n  using Interps = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n  using metavars = Metavariables<Dim, HasPrims>;\n  metavars::rdmp_fails = rdmp_fails;\n  metavars::tci_fails = tci_fails;\n  metavars::tci_invoked = false;\n\n  // Sets neighboring block \"Block1\" to DG-only, if disable_subcell_in_block ==\n  // true.\n  using comp = component<Dim, metavars>;\n  const evolution::dg::subcell::SubcellOptions& subcell_options =\n      evolution::dg::subcell::SubcellOptions{\n          evolution::dg::subcell::SubcellOptions{\n              4.0, 1_st, 1.0e-3, 1.0e-4, always_use_subcell, false,\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n              use_halo,\n              disable_subcell_in_block\n                  ? std::optional{std::vector<std::string>{\"Block1\"}}\n                  : std::optional<std::vector<std::string>>{},\n              ::fd::DerivativeOrder::Two, 1, 1, 1},\n          TestCreator<Dim>{}};\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{subcell_options}};\n\n  const TimeStepId time_step_id{false, self_starting ? -1 : 1,\n                                Slab{1.0, 2.0}.end()};\n  const TimeDelta step_size{Slab{1.0, 2.0}, {-1, 10}};\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const Element<Dim> element = create_element<Dim>(with_neighbors);\n  const evolution::dg::subcell::ActiveGrid active_grid =\n      evolution::dg::subcell::ActiveGrid::Dg;\n\n  using GhostData = evolution::dg::subcell::GhostData;\n\n  DirectionalIdMap<Dim, GhostData> ghost_data{};\n\n  DirectionalIdMap<Dim, Mesh<Dim>> neighbor_meshes{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    REQUIRE(not neighbors.ids().empty());\n    const DirectionalId<Dim> directional_element_id{direction,\n                                                    *neighbors.ids().begin()};\n    if ((direction.side() == Side::Upper and direction.dimension() % 2 == 0) or\n        (direction.side() == Side::Lower and direction.dimension() % 2 != 0)) {\n      neighbor_meshes[directional_element_id] = dg_mesh;\n      ghost_data[directional_element_id] = GhostData{1};\n      DataVector& neighbor_data = ghost_data.at(directional_element_id)\n                                      .neighbor_ghost_data_for_reconstruction();\n      neighbor_data = DataVector{dg_mesh.number_of_grid_points()};\n      alg::iota(neighbor_data, subcell_mesh.number_of_grid_points() *\n                                   (2.0 * direction.dimension() + 1.0));\n    } else {\n      neighbor_meshes[directional_element_id] = subcell_mesh;\n      ghost_data[directional_element_id] = GhostData{1};\n      DataVector& neighbor_data = ghost_data.at(directional_element_id)\n                                      .neighbor_ghost_data_for_reconstruction();\n      neighbor_data = DataVector{dg_mesh.number_of_grid_points()};\n      alg::iota(neighbor_data, subcell_mesh.number_of_grid_points() *\n                                   (2.0 * direction.dimension() + 1.0));\n    }\n  }\n  // test FD/DG element neighbor disable_subcell_in_block\n  const bool bordering_dg_block = alg::any_of(\n      element.neighbors(),\n      [&subcell_options](const auto& direction_and_neighbor) {\n        const size_t first_block_id =\n            direction_and_neighbor.second.ids().begin()->block_id();\n        return alg::found(subcell_options.only_dg_block_ids(), first_block_id);\n      });\n\n  const bool self_block_dg_only =\n      alg::found(subcell_options.only_dg_block_ids(), element.id().block_id());\n\n  // assign value of passed in variable.  Used as a test in apply() above\n  metavars::expected_evolve_on_dg_after_tci_failure =\n      bordering_dg_block or self_block_dg_only;\n\n  const int tci_decision{-1};\n\n  // max and min of +-2 at last time level means reconstructed vars will be in\n  // limit\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{{2.0}, {-2.0}};\n  // Make a copy of the RDMP data because in the case where the TCI fails the\n  // RDMP TCI data in the DataBox shouldn't have changed.\n  const evolution::dg::subcell::RdmpTciData initial_rdmp_tci_data =\n      rdmp_tci_data;\n\n  using evolved_vars_tags = tmpl::list<Var1>;\n  using dt_evolved_vars_tags = db::wrap_tags_in<::Tags::dt, evolved_vars_tags>;\n  Variables<evolved_vars_tags> evolved_vars{dg_mesh.number_of_grid_points()};\n  // Set Var1 to the logical coords, since those are linear\n  get(get<Var1>(evolved_vars)) = get<0>(logical_coordinates(dg_mesh));\n  if (rdmp_fails) {\n    get(get<Var1>(evolved_vars))[0] = 100.0;\n  }\n  using prim_vars_tags = tmpl::list<PrimVar1>;\n  Variables<prim_vars_tags> prim_vars{dg_mesh.number_of_grid_points()};\n  get(get<PrimVar1>(prim_vars)) = get<0>(logical_coordinates(dg_mesh)) + 1000.0;\n\n  constexpr size_t history_size = 5;\n  constexpr size_t history_substeps = 3;\n  TimeSteppers::History<Variables<evolved_vars_tags>> time_stepper_history{\n      history_size};\n  {\n    Time step_time{Slab{1.0, 2.0}, {6, 10}};\n    for (size_t i = 0; i < history_size; ++i) {\n      step_time += step_size;\n      Variables<dt_evolved_vars_tags> dt_vars{dg_mesh.number_of_grid_points()};\n      get(get<::Tags::dt<Var1>>(dt_vars)) =\n          (i + 20.0) * get<0>(logical_coordinates(dg_mesh));\n      time_stepper_history.insert({false, 1, step_time}, i * evolved_vars,\n                                  dt_vars);\n    }\n    for (size_t i = 0; i < history_substeps; ++i) {\n      Variables<dt_evolved_vars_tags> dt_vars{dg_mesh.number_of_grid_points()};\n      get(get<::Tags::dt<Var1>>(dt_vars)) =\n          (i + 40.0) * get<0>(logical_coordinates(dg_mesh));\n      time_stepper_history.insert(\n          {false, 1, step_time, i + 1, step_size, step_time.value()},\n          -i * evolved_vars, dt_vars);\n    }\n    time_stepper_history.discard_value(time_stepper_history[2].time_step_id);\n    time_stepper_history.discard_value(\n        time_stepper_history.substeps()[1].time_step_id);\n  }\n  const Variables<evolved_vars_tags> vars = time_stepper_history.latest_value();\n\n  const bool did_rollback = false;\n  Variables<evolved_vars_tags> initial_value_evolved_vars{\n      dg_mesh.number_of_grid_points(), 1.0e8};\n  Variables<prim_vars_tags> initial_value_prim_vars{\n      dg_mesh.number_of_grid_points(), 1.0e10};\n\n  typename evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>::type\n      neighbor_decisions{};\n  neighbor_decisions.insert(std::pair{\n      DirectionalId<Dim>{Direction<Dim>::lower_xi(), ElementId<Dim>{10}},\n      neighbor_is_troubled ? 10 : 0});\n\n  if constexpr (HasPrims) {\n    ActionTesting::emplace_array_component_and_initialize<comp>(\n        &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0,\n        {std::make_unique<DummyReconstructor>(), time_step_id, dg_mesh,\n         subcell_mesh, element, active_grid, did_rollback, ghost_data,\n         tci_decision, rdmp_tci_data, neighbor_meshes, evolved_vars,\n         time_stepper_history, initial_value_evolved_vars, neighbor_decisions,\n         Interps{}, prim_vars, initial_value_prim_vars});\n  } else {\n    (void)prim_vars;\n    (void)initial_value_prim_vars;\n    ActionTesting::emplace_array_component_and_initialize<comp>(\n        &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0,\n        {std::make_unique<DummyReconstructor>(), time_step_id, dg_mesh,\n         subcell_mesh, element, active_grid, did_rollback, ghost_data,\n         tci_decision, rdmp_tci_data, neighbor_meshes, evolved_vars,\n         time_stepper_history, initial_value_evolved_vars, neighbor_decisions,\n         Interps{}});\n  }\n\n  // Invoke the TciAndRollback action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), 0);\n\n  const auto active_grid_from_box =\n      ActionTesting::get_databox_tag<comp,\n                                     evolution::dg::subcell::Tags::ActiveGrid>(\n          runner, 0);\n  const auto& active_vars_from_box =\n      ActionTesting::get_databox_tag<comp,\n                                     ::Tags::Variables<evolved_vars_tags>>(\n          runner, 0);\n  const auto& time_stepper_history_from_box = ActionTesting::get_databox_tag<\n      comp,\n      ::Tags::HistoryEvolvedVariables<::Tags::Variables<evolved_vars_tags>>>(\n      runner, 0);\n  const auto& did_rollback_from_box =\n      ActionTesting::get_databox_tag<comp,\n                                     evolution::dg::subcell::Tags::DidRollback>(\n          runner, 0);\n  const auto& initial_value_evolved_vars_from_box = get<0>(\n      ActionTesting::get_databox_tag<\n          comp,\n          SelfStart::Tags::InitialValue<::Tags::Variables<evolved_vars_tags>>>(\n          runner, 0));\n\n  const bool expected_rollback =\n      with_neighbors and ((always_use_subcell or rdmp_fails or tci_fails or\n                           (use_halo and neighbor_is_troubled)) and\n                          not disable_subcell_in_block);\n\n  if (expected_rollback) {\n    CHECK(active_grid_from_box == evolution::dg::subcell::ActiveGrid::Subcell);\n    CHECK(did_rollback_from_box);\n    CHECK(ActionTesting::get_next_action_index<comp>(runner, 0) == 4);\n\n    CHECK(ActionTesting::get_databox_tag<\n              comp, evolution::dg::subcell::Tags::DataForRdmpTci>(runner, 0) ==\n          initial_rdmp_tci_data);\n\n    CHECK(time_stepper_history_from_box.size() == history_size);\n    CHECK(time_stepper_history_from_box.substeps().size() ==\n          history_substeps - 1);\n    CHECK(time_stepper_history_from_box.integration_order() ==\n          time_stepper_history.integration_order());\n    {\n      const auto check_box_record = [&](const auto& original_record) {\n        const auto& record_from_box =\n            time_stepper_history_from_box[original_record.time_step_id];\n        CHECK(record_from_box.derivative ==\n              evolution::dg::subcell::fd::project(\n                  original_record.derivative, dg_mesh, subcell_mesh.extents()));\n        if (original_record.value.has_value()) {\n          CHECK(record_from_box.value ==\n                std::optional{evolution::dg::subcell::fd::project(\n                    *original_record.value, dg_mesh, subcell_mesh.extents())});\n        } else {\n          CHECK(not record_from_box.value.has_value());\n        }\n      };\n      for (const auto& original_record : time_stepper_history) {\n        check_box_record(original_record);\n      }\n      for (size_t i = 0; i < history_substeps - 1; ++i) {\n        check_box_record(time_stepper_history.substeps()[i]);\n      }\n    }\n\n    CHECK(active_vars_from_box == evolution::dg::subcell::fd::project(\n                                      vars, dg_mesh, subcell_mesh.extents()));\n\n    if (self_starting) {\n      CHECK(initial_value_evolved_vars_from_box ==\n            evolution::dg::subcell::fd::project(\n                initial_value_evolved_vars, dg_mesh, subcell_mesh.extents()));\n      if constexpr (HasPrims) {\n        const auto& initial_value_prim_vars_from_box =\n            get<0>(ActionTesting::get_databox_tag<\n                   comp, SelfStart::Tags::InitialValue<\n                             ::Tags::Variables<prim_vars_tags>>>(runner, 0));\n        CHECK(initial_value_prim_vars_from_box ==\n              evolution::dg::subcell::fd::project(\n                  initial_value_prim_vars, dg_mesh, subcell_mesh.extents()));\n      }\n    }\n\n    auto expected_ghost_data = ghost_data;\n    for (const auto& [directional_element_id, neighbor_mesh] :\n         neighbor_meshes) {\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n          make_not_null(&expected_ghost_data),\n          expected_ghost_data.at(directional_element_id)\n              .neighbor_ghost_data_for_reconstruction(),\n          0, directional_element_id, neighbor_mesh, element, subcell_mesh, 2,\n          Interps{});\n    }\n    const auto& ghost_data_for_reconstruction = ActionTesting::get_databox_tag<\n        comp, evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(\n        runner, 0);\n    for (const auto& [id, local_ghost_data] : expected_ghost_data) {\n      CHECK(ghost_data_for_reconstruction.contains(id));\n      CHECK_ITERABLE_APPROX(\n          (ghost_data_for_reconstruction.at(id)\n               .neighbor_ghost_data_for_reconstruction()),\n          local_ghost_data.neighbor_ghost_data_for_reconstruction());\n    }\n\n  } else {\n    CHECK(ActionTesting::get_next_action_index<comp>(runner, 0) == 2);\n    CHECK(active_grid_from_box == evolution::dg::subcell::ActiveGrid::Dg);\n    CHECK_FALSE(did_rollback_from_box);\n\n    const auto subcell_vars = evolution::dg::subcell::fd::project(\n        evolved_vars, dg_mesh,\n        evolution::dg::subcell::fd::mesh(dg_mesh).extents());\n    const evolution::dg::subcell::RdmpTciData expected_rdmp_data{\n        {std::max(max(get(get<Var1>(evolved_vars))),\n                  max(get(get<Var1>(subcell_vars))))},\n        {std::min(min(get(get<Var1>(evolved_vars))),\n                  min(get(get<Var1>(subcell_vars))))}};\n\n    CHECK(ActionTesting::get_databox_tag<\n              comp, evolution::dg::subcell::Tags::DataForRdmpTci>(runner, 0) ==\n          expected_rdmp_data);\n\n    CHECK(time_stepper_history_from_box.size() == time_stepper_history.size());\n    CHECK(time_stepper_history_from_box.substeps().size() ==\n          time_stepper_history.substeps().size());\n    CHECK(time_stepper_history_from_box.integration_order() ==\n          time_stepper_history.integration_order());\n    for (const auto& original_record : time_stepper_history) {\n      CHECK(time_stepper_history_from_box[original_record.time_step_id] ==\n            original_record);\n    }\n    for (const auto& original_record : time_stepper_history.substeps()) {\n      CHECK(time_stepper_history_from_box[original_record.time_step_id] ==\n            original_record);\n    }\n\n    CHECK(active_vars_from_box == evolved_vars);\n    if (self_starting) {\n      CHECK(initial_value_evolved_vars_from_box == initial_value_evolved_vars);\n      if constexpr (HasPrims) {\n        const auto& initial_value_prim_vars_from_box =\n            get<0>(ActionTesting::get_databox_tag<\n                   comp, SelfStart::Tags::InitialValue<\n                             ::Tags::Variables<prim_vars_tags>>>(runner, 0));\n        CHECK(initial_value_prim_vars_from_box == initial_value_prim_vars);\n      }\n    }\n\n    const auto& ghost_data_for_reconstruction = ActionTesting::get_databox_tag<\n        comp, evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(\n        runner, 0);\n    CHECK(ghost_data_for_reconstruction.empty());\n  }\n  CHECK(ActionTesting::get_databox_tag<\n            comp, evolution::dg::subcell::Tags::TciDecision>(runner, 0) ==\n        (metavars::tci_invoked ? (rdmp_fails ? 10 : (tci_fails ? 5 : 0)) : -1));\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  for (const auto& [rdmp_fails, tci_fails, always_use_subcell, self_starting,\n                    have_neighbors, use_halo, neighbor_is_troubled,\n                    disable_subcell_in_block] :\n       cartesian_product(make_array(false, true), make_array(false, true),\n                         make_array(false, true), make_array(false, true),\n                         make_array(false, true), make_array(false, true),\n                         make_array(false, true), make_array(false, true))) {\n    test_impl<Dim, true>(rdmp_fails, tci_fails, always_use_subcell,\n                         self_starting, have_neighbors, use_halo,\n                         neighbor_is_troubled, disable_subcell_in_block);\n    test_impl<Dim, false>(rdmp_fails, tci_fails, always_use_subcell,\n                          self_starting, have_neighbors, use_halo,\n                          neighbor_is_troubled, disable_subcell_in_block);\n  }\n}\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Actions.TciAndRollback\",\n                  \"[Evolution][Unit]\") {\n  // We test the following cases:\n  // 1. Test RDMP passes/fails (check TciMutator not called on failure)\n  // 2. Test always_use_subcells\n  // 3. Test TciMutator passes/fails\n  //\n  // Below is a list of quantities to verify were handled/set correctly by the\n  // action:\n  // - history projected with correct size (one fewer)\n  // - active vars become projection of latest in history\n  // - active_grid is correct\n  // - did_rollback is correct\n  // - if self-start check initial value (and prims) were projected\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Actions/Test_TciAndSwitchToDg.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <deque>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Actions/TciAndSwitchToDg.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/StepsSinceTciCall.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciCallsSinceRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciGridHistory.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = Tags::Variables<tmpl::list<Var1>>;\n  using flux_variables = tmpl::list<Var1>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using initial_tags = tmpl::list<\n      ::Tags::TimeStepId, domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::ActiveGrid,\n      evolution::dg::subcell::Tags::DidRollback,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n      evolution::dg::subcell::Tags::TciDecision,\n      evolution::dg::subcell::Tags::DataForRdmpTci,\n      evolution::dg::subcell::Tags::TciGridHistory,\n      evolution::dg::subcell::Tags::TciCallsSinceRollback,\n      evolution::dg::subcell::Tags::StepsSinceTciCall,\n      Tags::Variables<tmpl::list<Var1>>,\n      Tags::HistoryEvolvedVariables<Tags::Variables<tmpl::list<Var1>>>,\n      Tags::ConcreteTimeStepper<TimeStepper>,\n      evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>,\n      domain::Tags::Element<Dim>,\n      evolution::dg::subcell::Tags::CellCenteredFlux<\n          typename metavariables::system::flux_variables, Dim>>;\n  using compute_tags = time_stepper_ref_tags<TimeStepper>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<initial_tags, compute_tags>,\n                 evolution::dg::subcell::Actions::TciAndSwitchToDg<\n                     typename Metavariables::TciOnSubcellGrid>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using system = System<Dim>;\n  using analytic_variables_tags = typename system::variables_tag::tags_list;\n  using const_global_cache_tags =\n      tmpl::list<evolution::dg::subcell::Tags::SubcellOptions<Dim>>;\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool rdmp_fails;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool tci_fails;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool tci_invoked;\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static bool tci_rdmp_data_only;\n\n  struct TciOnSubcellGrid {\n    using return_tags = tmpl::list<>;\n    using argument_tags =\n        tmpl::list<Tags::Variables<tmpl::list<Var1>>,\n                   evolution::dg::subcell::Tags::DataForRdmpTci,\n                   evolution::dg::subcell::Tags::SubcellOptions<Dim>>;\n\n    static std::tuple<int, evolution::dg::subcell::RdmpTciData> apply(\n        const Variables<tmpl::list<Var1>>& subcell_vars,\n        const evolution::dg::subcell::RdmpTciData& past_rdmp_tci_data,\n        const evolution::dg::subcell::SubcellOptions& subcell_options,\n        const double persson_exponent, const bool need_rdmp_data_only) {\n      CHECK(approx(persson_exponent) == 5.0);  // Should be subcell_opts + 1\n      tci_invoked = true;\n      tci_rdmp_data_only = need_rdmp_data_only;\n\n      evolution::dg::subcell::RdmpTciData rdmp_data{};\n      rdmp_data.max_variables_values =\n          DataVector{max(get(get<Var1>(subcell_vars)))};\n      rdmp_data.min_variables_values =\n          DataVector{min(get(get<Var1>(subcell_vars)))};\n\n      // Now do RDMP check, reconstruct to DG solution, then check.\n      CHECK(evolution::dg::subcell::rdmp_tci(\n                rdmp_data.max_variables_values, rdmp_data.min_variables_values,\n                past_rdmp_tci_data.max_variables_values,\n                past_rdmp_tci_data.min_variables_values,\n                subcell_options.rdmp_delta0(),\n                subcell_options.rdmp_epsilon()) == rdmp_fails);\n\n      const int decision = rdmp_fails ? -10 : (tci_fails ? -5 : 0);\n      return {decision, rdmp_data};\n    }\n  };\n};\n\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim>::rdmp_fails = false;\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim>::tci_fails = false;\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim>::tci_invoked = false;\ntemplate <size_t Dim>\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool Metavariables<Dim>::tci_rdmp_data_only = false;\n\nstd::unique_ptr<TimeStepper> make_time_stepper(\n    const bool multistep_time_stepper) {\n  if (multistep_time_stepper) {\n    return std::make_unique<TimeSteppers::AdamsBashforth>(2);\n  } else {\n    return std::make_unique<TimeSteppers::Rk3HesthavenSsp>();\n  }\n}\n\ntemplate <size_t Dim>\nclass TestCreator : public DomainCreator<Dim> {\n  Domain<Dim> create_domain() const override { return Domain<Dim>{}; }\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override {\n    return {};\n  }\n\n  std::vector<std::string> block_names() const override { return {\"Block0\"}; }\n\n  std::vector<std::array<size_t, Dim>> initial_extents() const override {\n    return {};\n  }\n\n  std::vector<std::array<size_t, Dim>> initial_refinement_levels()\n      const override {\n    return {};\n  }\n};\n\ntemplate <size_t Dim>\nvoid test_impl(\n    const bool multistep_time_stepper, const bool rdmp_fails,\n    const bool tci_fails, const bool did_rollback,\n    const bool always_use_subcell, const bool self_starting,\n    const bool in_substep,\n    const evolution::dg::subcell::fd::ReconstructionMethod recons_method,\n    const bool use_halo, const bool neighbor_is_troubled,\n    const bool test_block_id_assert,\n    const size_t number_of_steps_between_tci_calls,\n    const size_t min_tci_calls_after_rollback,\n    const size_t minimum_clear_tcis) {\n  CAPTURE(Dim);\n  CAPTURE(multistep_time_stepper);\n  CAPTURE(rdmp_fails);\n  CAPTURE(tci_fails);\n  CAPTURE(did_rollback);\n  CAPTURE(always_use_subcell);\n  CAPTURE(self_starting);\n  CAPTURE(in_substep);\n  CAPTURE(recons_method);\n  CAPTURE(use_halo);\n  CAPTURE(neighbor_is_troubled);\n  CAPTURE(test_block_id_assert);\n  CAPTURE(number_of_steps_between_tci_calls);\n  CAPTURE(min_tci_calls_after_rollback);\n  CAPTURE(minimum_clear_tcis);\n  if (in_substep and multistep_time_stepper) {\n    ERROR(\"Can't both be taking a substep and using a multistep time stepper\");\n  }\n\n  using metavars = Metavariables<Dim>;\n  metavars::rdmp_fails = rdmp_fails;\n  metavars::tci_fails = tci_fails;\n  metavars::tci_invoked = false;\n  metavars::tci_rdmp_data_only = true;\n\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{evolution::dg::subcell::SubcellOptions{\n      evolution::dg::subcell::SubcellOptions{\n          4.0, 1_st, 1.0e-3, 1.0e-4, always_use_subcell, false, recons_method,\n          use_halo,\n          test_block_id_assert\n              ? std::optional{std::vector<std::string>{\"Block0\"}}\n              : std::optional<std::vector<std::string>>{},\n          ::fd::DerivativeOrder::Two, number_of_steps_between_tci_calls,\n          min_tci_calls_after_rollback, minimum_clear_tcis},\n      TestCreator<Dim>{}}}};\n\n  TimeStepId time_step_id{false, self_starting ? -1 : 1, Slab{1.0, 2.0}.end()};\n  const TimeDelta step_size{Slab{1.0, 2.0}, {-1, 10}};\n  if (in_substep) {\n    // We are in the middle of a time step with a substep method, so update\n    // time_step_id to signal it is in a substep.\n    time_step_id =\n        TimeStepId{false, 1, Slab{1.0, 2.0}.end(), 1, step_size, 1.1};\n  }\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const evolution::dg::subcell::ActiveGrid active_grid =\n      evolution::dg::subcell::ActiveGrid::Subcell;\n  const std::unique_ptr<TimeStepper> time_stepper =\n      make_time_stepper(multistep_time_stepper);\n\n  DirectionalIdMap<Dim, evolution::dg::subcell::GhostData> ghost_data{};\n\n  const int tci_decision{-1};  // default value\n\n  // max and min of +-2 at last time level means reconstructed vars will be in\n  // limit\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{{2.0}, {-2.0}};\n  std::deque<evolution::dg::subcell::ActiveGrid> tci_grid_history{};\n  for (size_t i = 0;\n       i < get<TimeSteppers::Tags::FixedOrder>(time_stepper->order()) and\n       (multistep_time_stepper or\n        (not multistep_time_stepper and minimum_clear_tcis > 1));\n       ++i) {\n    tci_grid_history.push_back(evolution::dg::subcell::ActiveGrid::Dg);\n  }\n\n  using evolved_vars_tags = tmpl::list<Var1>;\n  using dt_evolved_vars_tags = db::wrap_tags_in<Tags::dt, evolved_vars_tags>;\n  Variables<evolved_vars_tags> evolved_vars{\n      subcell_mesh.number_of_grid_points()};\n  // Set Var1 to the logical coords, since those are linear\n  get(get<Var1>(evolved_vars)) = get<0>(logical_coordinates(subcell_mesh));\n  if (rdmp_fails) {\n    get(get<Var1>(evolved_vars))[0] = 100.0;\n  }\n  TimeSteppers::History<Variables<evolved_vars_tags>> time_stepper_history{4};\n  {\n    constexpr size_t history_size = 5;\n    constexpr size_t history_substeps = 3;\n    Time step_time{Slab{1.0, 2.0}, {6, 10}};\n    for (size_t i = 0; i < history_size; ++i) {\n      step_time += step_size;\n      Variables<dt_evolved_vars_tags> dt_vars{\n          subcell_mesh.number_of_grid_points()};\n      get(get<Tags::dt<Var1>>(dt_vars)) =\n          (i + 20.0) * get<0>(logical_coordinates(subcell_mesh));\n      time_stepper_history.insert({false, 1, step_time}, i * evolved_vars,\n                                  dt_vars);\n    }\n    for (size_t i = 0; i < history_substeps; ++i) {\n      Variables<dt_evolved_vars_tags> dt_vars{\n          subcell_mesh.number_of_grid_points()};\n      get(get<Tags::dt<Var1>>(dt_vars)) =\n          (i + 40.0) * get<0>(logical_coordinates(subcell_mesh));\n      time_stepper_history.insert(\n          {false, 1, step_time, i + 1, step_size, step_time.value()},\n          -i * evolved_vars, dt_vars);\n    }\n    time_stepper_history.discard_value(time_stepper_history[2].time_step_id);\n    time_stepper_history.discard_value(\n        time_stepper_history.substeps()[1].time_step_id);\n  }\n  Variables<evolved_vars_tags> vars{subcell_mesh.number_of_grid_points()};\n  get(get<Var1>(vars)) =\n      (static_cast<double>(\n           get<TimeSteppers::Tags::FixedOrder>(time_stepper->order())) +\n       1.0) *\n      get<0>(logical_coordinates(subcell_mesh));\n\n  typename evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>::type\n      neighbor_decisions{};\n  neighbor_decisions.insert(std::pair{\n      DirectionalId<Dim>{Direction<Dim>::lower_xi(), ElementId<Dim>{10}},\n      neighbor_is_troubled ? 10 : 0});\n\n  // Set a large number of TCI calls to mock having just returned from FD.\n  const size_t tci_calls_since_rollback = 100;\n  const size_t steps_since_tci_call = 300;\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0,\n      {time_step_id, dg_mesh, subcell_mesh, active_grid, did_rollback,\n       ghost_data, tci_decision, rdmp_tci_data, tci_grid_history,\n       tci_calls_since_rollback, steps_since_tci_call, evolved_vars,\n       time_stepper_history, make_time_stepper(multistep_time_stepper),\n       neighbor_decisions, Element<Dim>{ElementId<Dim>{0}, {}},\n       typename evolution::dg::subcell::Tags::CellCenteredFlux<\n           typename metavars::system::flux_variables, Dim>::type::value_type{\n           subcell_mesh.number_of_grid_points()}});\n\n  // Invoke the TciAndSwitchToDg action on the runner\n  if (test_block_id_assert) {\n    CHECK_THROWS_WITH(\n        ActionTesting::next_action<comp>(make_not_null(&runner), 0),\n        Catch::Matchers::ContainsSubstring(\n            \"Should never use subcell on element \"));\n    return;\n  }\n  ActionTesting::next_action<comp>(make_not_null(&runner), 0);\n\n  const auto active_grid_from_box =\n      ActionTesting::get_databox_tag<comp,\n                                     evolution::dg::subcell::Tags::ActiveGrid>(\n          runner, 0);\n  const auto& active_vars_from_box =\n      ActionTesting::get_databox_tag<comp, Tags::Variables<evolved_vars_tags>>(\n          runner, 0);\n  const auto& time_stepper_history_from_box = ActionTesting::get_databox_tag<\n      comp, Tags::HistoryEvolvedVariables<Tags::Variables<evolved_vars_tags>>>(\n      runner, 0);\n  const auto& tci_grid_history_from_box = ActionTesting::get_databox_tag<\n      comp, evolution::dg::subcell::Tags::TciGridHistory>(runner, 0);\n  const auto& cell_centered_flux_from_box = ActionTesting::get_databox_tag<\n      comp, evolution::dg::subcell::Tags::CellCenteredFlux<\n                typename metavars::system::flux_variables, Dim>>(runner, 0);\n\n  // true if the TCI wasn't invoked at all because we are always using subcell,\n  // doing self-start, took a substep, or already did rollback from DG to FD.\n  const bool avoid_tci = always_use_subcell;\n  const bool avoid_switch_to_dg =\n      avoid_tci or self_starting or time_step_id.substep() != 0 or\n      did_rollback or\n      number_of_steps_between_tci_calls > steps_since_tci_call or\n      min_tci_calls_after_rollback > tci_calls_since_rollback;\n\n  CHECK_FALSE(ActionTesting::get_databox_tag<\n              comp, evolution::dg::subcell::Tags::DidRollback>(runner, 0));\n\n  CAPTURE(metavars::tci_rdmp_data_only);\n  CHECK((metavars::tci_rdmp_data_only or\n         min_tci_calls_after_rollback > tci_calls_since_rollback) ==\n        avoid_switch_to_dg);\n\n  if (avoid_tci) {\n    CHECK_FALSE(metavars::tci_invoked);\n  }\n\n  // Check ActiveGrid\n  if (avoid_switch_to_dg or rdmp_fails or tci_fails or\n      (use_halo and neighbor_is_troubled) or\n\n      (minimum_clear_tcis >\n           get<TimeSteppers::Tags::FixedOrder>(time_stepper->order()) and\n       not(not multistep_time_stepper and minimum_clear_tcis > 1))) {\n    CHECK(active_grid_from_box == evolution::dg::subcell::ActiveGrid::Subcell);\n    CHECK(cell_centered_flux_from_box.has_value());\n    if (avoid_switch_to_dg) {\n      CHECK(ActionTesting::get_databox_tag<\n                comp, evolution::dg::subcell::Tags::TciCallsSinceRollback>(\n                runner, 0) ==\n            (min_tci_calls_after_rollback > tci_calls_since_rollback and\n                     not metavars::tci_rdmp_data_only\n                 ? tci_calls_since_rollback + 1\n                 : tci_calls_since_rollback));\n    } else {\n      CHECK(ActionTesting::get_databox_tag<\n                comp, evolution::dg::subcell::Tags::TciCallsSinceRollback>(\n                runner, 0) == tci_calls_since_rollback + 1);\n    }\n    CHECK(\n        ActionTesting::get_databox_tag<\n            comp, evolution::dg::subcell::Tags::StepsSinceTciCall>(runner, 0) ==\n        (number_of_steps_between_tci_calls > steps_since_tci_call and\n                 not always_use_subcell\n             ? steps_since_tci_call + 1\n             : (always_use_subcell ? steps_since_tci_call : 0)));\n  } else {\n    CHECK(active_grid_from_box == evolution::dg::subcell::ActiveGrid::Dg);\n    CHECK(not cell_centered_flux_from_box.has_value());\n    // We switched to DG so we should have reset the TCI calls\n    CHECK(ActionTesting::get_databox_tag<\n              comp, evolution::dg::subcell::Tags::TciCallsSinceRollback>(\n              runner, 0) == 0);\n    CHECK(ActionTesting::get_databox_tag<\n              comp, evolution::dg::subcell::Tags::StepsSinceTciCall>(runner,\n                                                                     0) == 0);\n  }\n\n  if (not avoid_switch_to_dg) {\n    Variables<tmpl::list<Var1>> reconstructed_dg_vars{\n        dg_mesh.number_of_grid_points()};\n    evolution::dg::subcell::fd::reconstruct(\n        make_not_null(&reconstructed_dg_vars), evolved_vars, dg_mesh,\n        subcell_mesh.extents(), recons_method);\n    if (active_grid_from_box == evolution::dg::subcell::ActiveGrid::Dg) {\n      // Do swap because types are different\n      // auto reconstructed_active_vars = evolved_vars;\n      // swap(reconstructed_dg_vars, reconstructed_active_vars);\n      CHECK(reconstructed_dg_vars == active_vars_from_box);\n    }\n  }\n\n  if (active_grid_from_box == evolution::dg::subcell::ActiveGrid::Dg) {\n    CHECK(time_stepper_history_from_box.size() == time_stepper_history.size());\n    CHECK(time_stepper_history_from_box.substeps().size() ==\n          time_stepper_history.substeps().size());\n    CHECK(time_stepper_history_from_box.integration_order() ==\n          time_stepper_history.integration_order());\n    {\n      const auto check_box_record = [&](const auto& original_record) {\n        const auto& record_from_box =\n            time_stepper_history_from_box[original_record.time_step_id];\n        CHECK(record_from_box.derivative ==\n              evolution::dg::subcell::fd::reconstruct(\n                  original_record.derivative, dg_mesh, subcell_mesh.extents(),\n                  recons_method));\n        if (original_record.value.has_value()) {\n          CHECK(record_from_box.value ==\n                std::optional{evolution::dg::subcell::fd::reconstruct(\n                    *original_record.value, dg_mesh, subcell_mesh.extents(),\n                    recons_method)});\n        } else {\n          CHECK(not record_from_box.value.has_value());\n        }\n      };\n      for (const auto& original_record : time_stepper_history) {\n        check_box_record(original_record);\n      }\n      for (const auto& original_record : time_stepper_history.substeps()) {\n        check_box_record(original_record);\n      }\n    }\n    CHECK(tci_grid_history_from_box.empty());\n  } else {\n    // TCI failed\n    CHECK(time_stepper_history_from_box.size() == time_stepper_history.size());\n    CHECK(time_stepper_history_from_box.substeps().size() ==\n          time_stepper_history.substeps().size());\n    CHECK(time_stepper_history_from_box.integration_order() ==\n          time_stepper_history.integration_order());\n    for (const auto& original_record : time_stepper_history) {\n      CHECK(time_stepper_history_from_box[original_record.time_step_id] ==\n            original_record);\n    }\n    for (const auto& original_record : time_stepper_history.substeps()) {\n      CHECK(time_stepper_history_from_box[original_record.time_step_id] ==\n            original_record);\n    }\n    if (avoid_switch_to_dg) {\n      if (multistep_time_stepper or\n          (not multistep_time_stepper and minimum_clear_tcis > 1)) {\n        REQUIRE(not tci_grid_history_from_box.empty());\n        CHECK(tci_grid_history_from_box.front() ==\n              evolution::dg::subcell::ActiveGrid::Dg);\n      }\n    } else if (multistep_time_stepper) {\n      REQUIRE(not tci_grid_history_from_box.empty());\n      CHECK(tci_grid_history_from_box.front() ==\n            (minimum_clear_tcis > get<TimeSteppers::Tags::FixedOrder>(\n                                      time_stepper->order()) and\n                     not tci_fails and not rdmp_fails and\n                     not(neighbor_is_troubled and use_halo)\n                 ? evolution::dg::subcell::ActiveGrid::Dg\n                 : evolution::dg::subcell::ActiveGrid::Subcell));\n    } else {\n      REQUIRE(not tci_grid_history_from_box.empty());\n      CHECK(tci_grid_history_from_box.front() ==\n            evolution::dg::subcell::ActiveGrid::Subcell);\n    }\n    if (multistep_time_stepper) {\n      CHECK(tci_grid_history_from_box.size() ==\n            (minimum_clear_tcis > get<TimeSteppers::Tags::FixedOrder>(\n                                      time_stepper->order()) and\n                     not always_use_subcell and\n                     not metavars::tci_rdmp_data_only and\n                     min_tci_calls_after_rollback <= tci_calls_since_rollback\n                 ? minimum_clear_tcis - 1\n                 : get<TimeSteppers::Tags::FixedOrder>(time_stepper->order())));\n    }\n  }\n  CHECK(ActionTesting::get_databox_tag<\n            comp, evolution::dg::subcell::Tags::TciDecision>(runner, 0) ==\n        (avoid_switch_to_dg ? -1 : (rdmp_fails ? -10 : (tci_fails ? -5 : 0))));\n}\n\ntemplate <size_t Dim>\nvoid test() {\n#ifdef SPECTRE_DEBUG\n  bool tested_block_id_assert = false;\n#endif\n  for (const bool use_multistep_time_stepper : {true, false}) {\n    for (const bool rdmp_fails : {true, false}) {\n      for (const bool tci_fails : {false, true}) {\n        for (const bool did_rollback : {true, false}) {\n          for (const bool always_use_subcell : {false, true}) {\n            for (const bool self_starting : {false, true}) {\n              for (const bool use_halo : {false, true}) {\n                for (const bool neighbor_is_troubled : {false, true}) {\n                  for (const auto recons_method :\n                       {evolution::dg::subcell::fd::ReconstructionMethod::\n                            AllDimsAtOnce,\n                        evolution::dg::subcell::fd::ReconstructionMethod::\n                            DimByDim}) {\n                    for (const size_t number_of_steps_between_tci_calls :\n                         {1_st, 305_st}) {\n                      for (const size_t min_tci_calls_after_rollback :\n                           {1_st, 105_st}) {\n                        for (const size_t minimum_clear_tcis : {1_st, 4_st}) {\n                          if (Dim != 1 and\n                              (number_of_steps_between_tci_calls != 1 or\n                               min_tci_calls_after_rollback != 1 or\n                               minimum_clear_tcis != 1)) {\n                            // These parts don't depend on dimensionality and\n                            // just increase test time considerably.\n                            continue;\n                          }\n                          test_impl<Dim>(\n                              use_multistep_time_stepper, rdmp_fails, tci_fails,\n                              did_rollback, always_use_subcell, self_starting,\n                              false, recons_method, use_halo,\n                              neighbor_is_troubled, false,\n                              number_of_steps_between_tci_calls,\n                              min_tci_calls_after_rollback, minimum_clear_tcis);\n                          if (not use_multistep_time_stepper) {\n                            test_impl<Dim>(use_multistep_time_stepper,\n                                           rdmp_fails, tci_fails, did_rollback,\n                                           always_use_subcell, self_starting,\n                                           true, recons_method, use_halo,\n                                           neighbor_is_troubled, false,\n                                           number_of_steps_between_tci_calls,\n                                           min_tci_calls_after_rollback,\n                                           minimum_clear_tcis);\n                          }\n#ifdef SPECTRE_DEBUG\n                          if (not tested_block_id_assert) {\n                            test_impl<Dim>(use_multistep_time_stepper,\n                                           rdmp_fails, tci_fails, did_rollback,\n                                           always_use_subcell, self_starting,\n                                           false, recons_method, use_halo,\n                                           neighbor_is_troubled, true,\n                                           number_of_steps_between_tci_calls,\n                                           min_tci_calls_after_rollback,\n                                           minimum_clear_tcis);\n                            tested_block_id_assert = true;\n                          }\n#endif\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\n// [[TimeOut, 30]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Actions.TciAndSwitchToDg\",\n                  \"[Evolution][Unit]\") {\n  // 1. check that if we are in self-start nothing happens.\n  // 2. check if substep != 0, nothing happens.\n  // 3. Check if always_use_subcells.\n  // 4. Check if RDMP gets triggered, then tci_mutator is not called, and we\n  //    stay on subcell\n  // 5. Check if RDMP is not triggered, but tci_mutator is, we stay on subcell\n  // 6. check if RDMP & TCI not triggered, switch to DG.\n  // 7. check if DidRollBack=True, stay in subcell.\n  register_classes_with_charm<TimeSteppers::AdamsBashforth,\n                              TimeSteppers::Rk3HesthavenSsp>();\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DgSubcell\")\n\nset(LIBRARY_SOURCES\n  Actions/Test_Initialize.cpp\n  Actions/Test_ReconstructionCommunication.cpp\n  Actions/Test_SelectNumericalMethod.cpp\n  Actions/Test_TakeTimeStep.cpp\n  Actions/Test_TciAndRollback.cpp\n  Actions/Test_TciAndSwitchToDg.cpp\n  Test_ActiveGrid.cpp\n  Test_BackgroundGrVars.cpp\n  Test_CartesianFluxDivergence.cpp\n  Test_CellCenteredFlux.cpp\n  Test_CombineVolumeGhostData.cpp\n  Test_ComputeBoundaryTerms.cpp\n  Test_CorrectPackagedData.cpp\n  Test_DisableLts.cpp\n  Test_GetActiveTag.cpp\n  Test_GetTciDecision.cpp\n  Test_GhostData.cpp\n  Test_GhostZoneInverseJacobian.cpp\n  Test_GhostZoneLogicalCoordinates.cpp\n  Test_InitialTciData.cpp\n  Test_JacobianCompute.cpp\n  Test_Matrices.cpp\n  Test_Mesh.cpp\n  Test_MeshForGhostData.cpp\n  Test_NeighborRdmpAndVolumeData.cpp\n  Test_NeighborReconstructedFaceSolution.cpp\n  Test_NeighborTciDecision.cpp\n  Test_PerssonTci.cpp\n  Test_PrepareNeighborData.cpp\n  Test_Projection.cpp\n  Test_RdmpTci.cpp\n  Test_RdmpTciData.cpp\n  Test_ReceiveSubcellDataForDg.cpp\n  Test_Reconstruction.cpp\n  Test_ReconstructionMethod.cpp\n  Test_ReconstructionOrder.cpp\n  Test_SetInterpolators.cpp\n  Test_SliceData.cpp\n  Test_SliceTensor.cpp\n  Test_SliceVariable.cpp\n  Test_SubcellOptions.cpp\n  Test_Tags.cpp\n  Test_TwoMeshRdmpTci.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  CoordinateMaps\n  DataStructures\n  DataStructuresHelpers\n  DiscontinuousGalerkin\n  Domain\n  DomainCreators\n  DomainStructure\n  DomainTimeDependence\n  DgSubcell\n  DgSubcellHelpers\n  ErrorHandling\n  Evolution\n  FiniteDifference\n  GeneralRelativitySolutions\n  Hydro\n  Parallel\n  RelativisticEulerSolutions\n  Spectral\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_ActiveGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.ActiveGrid\", \"[Evolution][Unit]\") {\n  CHECK(\"Dg\" == get_output(evolution::dg::subcell::ActiveGrid::Dg));\n  CHECK(\"Subcell\" == get_output(evolution::dg::subcell::ActiveGrid::Subcell));\n  CHECK(TestHelpers::test_creation<evolution::dg::subcell::ActiveGrid>(\"Dg\") ==\n        evolution::dg::subcell::ActiveGrid::Dg);\n  CHECK(TestHelpers::test_creation<evolution::dg::subcell::ActiveGrid>(\n            \"Subcell\") == evolution::dg::subcell::ActiveGrid::Subcell);\n  TestHelpers::db::test_simple_tag<evolution::dg::subcell::Tags::ActiveGrid>(\n      \"ActiveGrid\");\n  CHECK(TestHelpers::test_option_tag<\n            evolution::dg::subcell::OptionTags::ActiveGrid>(\"Dg\") ==\n        evolution::dg::subcell::ActiveGrid::Dg);\n  CHECK(TestHelpers::test_option_tag<\n            evolution::dg::subcell::OptionTags::ActiveGrid>(\"Subcell\") ==\n        evolution::dg::subcell::ActiveGrid::Subcell);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_BackgroundGrVars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/BackgroundGrVars.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct MetavariablesForTest {\n  using component_list = tmpl::list<>;\n  using initial_data_list = tmpl::list<RelativisticEuler::Solutions::TovStar>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>>;\n  };\n};\n\nstruct SystemForTest {\n  static constexpr size_t volume_dim = 3;\n\n  // A disparate set of GR variables were chosen here to make sure that the\n  // action allocates and assigns metric variables without missing any tags\n  using spacetime_variables_tag = ::Tags::Variables<\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>>>;\n  using flux_spacetime_variables_tag =\n      ::Tags::Variables<tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                                   gr::Tags::SpatialMetric<DataVector, 3>>>;\n  using inverse_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, 3>;\n};\n\n// A free function returning a moving brick domain\ntemplate <bool MeshIsMoving>\ndomain::creators::Brick create_a_brick(const size_t num_dg_pts,\n                                       const double initial_time) {\n  auto time_dependence_ptr = [&]() {\n    if constexpr (MeshIsMoving) {\n      const std::array<double, 3> mesh_velocity{1, 2, 3};\n      return std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          initial_time, mesh_velocity);\n    } else {\n      return nullptr;\n    }\n  }();\n  const auto lower_bounds = make_array<3, double>(3.0);\n  const auto upper_bounds = make_array<3, double>(5.0);\n  const auto refinement_levels = make_array<3, size_t>(0);\n  return domain::creators::Brick(lower_bounds, upper_bounds, refinement_levels,\n                                 make_array<3, size_t>(num_dg_pts),\n                                 make_array<3, bool>(true), {},\n                                 std::move(time_dependence_ptr));\n}\n\n// A free function that creates and returns face-centered subcell mesh\nstd::array<Mesh<3>, 3> create_face_centered_meshes(\n    const Mesh<3> cell_centered_mesh) {\n  std::array<Mesh<3>, 3> face_centered_meshes{};\n  for (size_t dim = 0; dim < 3; ++dim) {\n    const auto basis = make_array<3>(cell_centered_mesh.basis(0));\n    auto quadrature = make_array<3>(cell_centered_mesh.quadrature(0));\n    auto extents = make_array<3>(cell_centered_mesh.extents(0));\n    gsl::at(extents, dim) = cell_centered_mesh.extents(0) + 1;\n    gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n    const Mesh<3> face_centered_mesh{extents, basis, quadrature};\n    gsl::at(face_centered_meshes, dim) = Mesh<3>{extents, basis, quadrature};\n  }\n  return face_centered_meshes;\n}\n\ntemplate <bool TestMovingMesh, bool ComputeOnlyOnRollback>\nvoid test(const gsl::not_null<std::mt19937*> gen, const bool did_rollback) {\n  //\n  // The test is done as follows :\n  //\n  // - Create a 3D element (brick) for the test. If `TestMovingMesh ` ==\n  //    `true`, the coordinate map of the brick is set to be time-dependent.\n  // - Use Kerr-Schild or TOV solution as the background metric, depending on\n  //   the type (compile or runtime) of initial data to test.\n  // - Create a box for running the `BackgroundGrVars` mutator.\n  // - Run the mutator at the initial time (=initialization phase), then check\n  //   the result\n  // - Change inertial coordinates and time to a random later moment, and test\n  //   the mutator again.\n  //\n\n  CAPTURE(TestMovingMesh);\n  CAPTURE(ComputeOnlyOnRollback);\n  CAPTURE(did_rollback);\n\n  const double initial_time = 0.5;\n  // make a (random) later time strictly different from the initial time\n  std::uniform_real_distribution<> distribution_time(1.0, 2.0);\n  const double later_time{\n      make_with_random_values<double>(gen, make_not_null(&distribution_time))};\n\n  // Create a 3D element [3.0, 5.0]^3  for the test\n  const size_t num_dg_pts = 3;\n  const auto brick = [&]() {\n    if constexpr (TestMovingMesh) {\n      return create_a_brick<true>(num_dg_pts, initial_time);\n    } else {\n      return create_a_brick<false>(num_dg_pts, initial_time);\n    }\n  }();\n\n  const auto domain = brick.create_domain();\n  const auto element_id = ElementId<3>{0};\n  Element<3> element = domain::create_initial_element(\n      element_id, domain.blocks(),\n      std::vector<std::array<size_t, 3>>{{0, 0, 0}});\n\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh<3>(dg_mesh);\n\n  const auto face_centered_meshes = create_face_centered_meshes(subcell_mesh);\n\n  const auto& block = domain.blocks()[element_id.block_id()];\n\n  const auto element_map = ElementMap<3, Frame::Grid>{\n      element_id, block.is_time_dependent()\n                      ? block.moving_mesh_logical_to_grid_map().get_clone()\n                      : block.stationary_map().get_to_grid_frame()};\n\n  std::unique_ptr<::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>\n      grid_to_inertial_map;\n  if (block.is_time_dependent()) {\n    grid_to_inertial_map = block.moving_mesh_grid_to_inertial_map().get_clone();\n  } else {\n    grid_to_inertial_map =\n        ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            ::domain::CoordinateMaps::Identity<3>{});\n  }\n\n  const auto compute_inertial_coords = [&grid_to_inertial_map, &element_map,\n                                        &brick](const Mesh<3> mesh,\n                                                const double time) {\n    return (*grid_to_inertial_map)(element_map(logical_coordinates(mesh)), time,\n                                   brick.functions_of_time());\n  };\n\n  const auto subcell_initial_inertial_coords =\n      compute_inertial_coords(subcell_mesh, initial_time);\n  const auto subcell_later_inertial_coords =\n      compute_inertial_coords(subcell_mesh, later_time);\n\n  std::array<tnsr::I<DataVector, 3, Frame::Inertial>, 3>\n      face_centered_initial_inertial_coords{};\n  std::array<tnsr::I<DataVector, 3, Frame::Inertial>, 3>\n      face_centered_later_inertial_coords{};\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(face_centered_initial_inertial_coords, i) =\n        compute_inertial_coords(gsl::at(face_centered_meshes, i), initial_time);\n    gsl::at(face_centered_later_inertial_coords, i) =\n        compute_inertial_coords(gsl::at(face_centered_meshes, i), later_time);\n  }\n\n  using gr_variables_tag =\n      ::Tags::Variables<SystemForTest::spacetime_variables_tag::tags_list>;\n  using inactive_gr_variables_tag =\n      evolution::dg::subcell::Tags::Inactive<gr_variables_tag>;\n  using subcell_face_gr_variables_tag =\n      evolution::dg::subcell::Tags::OnSubcellFaces<\n          typename SystemForTest::flux_spacetime_variables_tag, 3>;\n\n  const auto solution = []() {\n    return RelativisticEuler::Solutions::TovStar{\n        1.0e-3, EquationsOfState::PolytropicFluid<true>{100.0, 2.0}.get_clone(),\n        RelativisticEuler::Solutions::TovCoordinates::Schwarzschild};\n  }();\n\n  const auto dg_gr_vars = [&compute_inertial_coords, &dg_mesh, &initial_time,\n                           &solution]() {\n    gr_variables_tag::type gr_vars{dg_mesh.number_of_grid_points()};\n\n    gr_vars.assign_subset(evolution::Initialization::initial_data(\n        solution, compute_inertial_coords(dg_mesh, initial_time), initial_time,\n        typename gr_variables_tag::tags_list{}));\n\n    return gr_vars;\n  }();\n\n  auto box = [&block, &brick, &dg_gr_vars, &element, &element_id,\n              &grid_to_inertial_map, &initial_time, &solution,\n              &subcell_initial_inertial_coords, &subcell_mesh]() {\n    // Bug in GCC 13.2 where having this class created in-place below causes an\n    // internal compiler error\n    typename subcell_face_gr_variables_tag::type face_gr_vars{};\n    // Since we want to test that the BackgroundGrVars action properly\n    // initializes (allocate + assign) the background GR variables on\n    // cell-centered and face-centered coordinates, use an empty subcell GR\n    // variables objects here for creating a box. GR variables on DG mesh are\n    // considered as initialized.\n    return db::create<db::AddSimpleTags<\n        ::Tags::Time, domain::Tags::Domain<3>, domain::Tags::Element<3>,\n        domain::Tags::ElementMap<3, Frame::Grid>,\n        domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                    Frame::Inertial>,\n        domain::Tags::FunctionsOfTimeInitialize,\n        evolution::dg::subcell::Tags::Mesh<3>,\n        evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n        gr_variables_tag, inactive_gr_variables_tag,\n        subcell_face_gr_variables_tag,\n        evolution::dg::subcell::Tags::DidRollback,\n        evolution::initial_data::Tags::InitialData>>(\n        initial_time, brick.create_domain(), element,\n        ElementMap<3, Frame::Grid>{\n            element_id,\n            block.is_time_dependent()\n                ? block.moving_mesh_logical_to_grid_map().get_clone()\n                : block.stationary_map().get_to_grid_frame()},\n        std::move(grid_to_inertial_map),\n        clone_unique_ptrs(brick.functions_of_time()), subcell_mesh,\n        subcell_initial_inertial_coords, dg_gr_vars,\n        typename inactive_gr_variables_tag::type{}, face_gr_vars, false,\n        solution.get_clone());\n  }();\n\n  // Apply the mutator for initialization phase, and check that it has put\n  // correct values of GR variables in the box. In the initialization phase,\n  // `inactive_gr_variables_tag` and `subcell_face_gr_variables_tag` must be\n  // properly initialized.\n  db::mutate_apply<evolution::dg::subcell::BackgroundGrVars<\n      SystemForTest, MetavariablesForTest, ComputeOnlyOnRollback>>(\n      make_not_null(&box));\n\n  // Compute expected cell-centered and face-centered GR vars\n  const auto expected_initial_cell_centered_gr_vars =\n      solution.variables(subcell_initial_inertial_coords, initial_time,\n                         gr_variables_tag::tags_list{});\n  subcell_face_gr_variables_tag::type expected_initial_face_centered_gr_vars{};\n  for (size_t d = 0; d < 3; ++d) {\n    gsl::at(expected_initial_face_centered_gr_vars, d)\n        .initialize(gsl::at(face_centered_meshes, 0).number_of_grid_points());\n    gsl::at(expected_initial_face_centered_gr_vars, d)\n        .assign_subset(evolution::Initialization::initial_data(\n            solution, gsl::at(face_centered_initial_inertial_coords, d),\n            initial_time, subcell_face_gr_variables_tag::tag::tags_list{}));\n  }\n\n  // some helper functions\n  const auto check_cell_centered_vars = [&box](const auto expected_values,\n                                               const bool is_active) {\n    tmpl::for_each<gr_variables_tag::tags_list>(\n        [&box, &expected_values, &is_active](const auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n\n          const auto var_in_box = [&box, &is_active]() {\n            if (is_active) {\n              return get<tag>(get<gr_variables_tag>(box));\n            } else {\n              return get<evolution::dg::subcell::Tags::Inactive<tag>>(\n                  get<inactive_gr_variables_tag>(box));\n            }\n          }();\n          const auto var_expected = get<tag>(expected_values);\n\n          CHECK_ITERABLE_APPROX(var_in_box, var_expected);\n        });\n  };\n  const auto check_face_centered_vars = [&box](const auto expected_values) {\n    tmpl::for_each<subcell_face_gr_variables_tag::tag::tags_list>(\n        [&box, &expected_values](const auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          for (size_t d = 0; d < 3; ++d) {\n            const auto var_in_box =\n                get<tag>(gsl::at(get<subcell_face_gr_variables_tag>(box), d));\n            const auto var_expected = get<tag>(gsl::at(expected_values, d));\n\n            CHECK_ITERABLE_APPROX(var_in_box, var_expected);\n          }\n        });\n  };\n\n  // check results for the initialization phase\n  check_cell_centered_vars(expected_initial_cell_centered_gr_vars, false);\n  check_face_centered_vars(expected_initial_face_centered_gr_vars);\n\n  // Mutate time and inertial coords to those at t = `later_time`, mutate the\n  // `DidRollback` tag to `did_rollback`, and apply the mutator again.\n  db::mutate<::Tags::Time,\n             evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n             evolution::dg::subcell::Tags::DidRollback>(\n      [&later_time, &subcell_later_inertial_coords, &did_rollback](\n          const auto time_ptr, const auto inertial_coords_ptr,\n          const auto did_rollback_ptr) {\n        *time_ptr = later_time;\n        *inertial_coords_ptr = subcell_later_inertial_coords;\n        *did_rollback_ptr = did_rollback;\n      },\n      make_not_null(&box));\n\n  //\n  // Now, we have a number of different cases that need to be handled :\n  //\n  //  * If `TestMovingMesh ` == false :\n  //     this mutator should not change anything\n  //\n  //  * If `TestMovingMesh ` == true :\n  //\n  //    - if `did_rollback` == true :\n  //      This is the beginning of FD solve after rollback, before\n  //      SwapGrTags mutator is applied. Therefore\n  //      `inactive_gr_variables_tag` and `subcell_face_gr_variables_tag`\n  //      need to be modified.\n  //\n  //    - if `did_rollback` == false :\n  //\n  //       - if `ComputeOnlyOnRollback` == true :\n  //           This is when the element is doing FD and passing the\n  //           `Labels::BeginSubcellAfterDgRollback` label in the action list.\n  //           So this mutator should not change anything.\n  //\n  //       - if `ComputeOnlyOnRollback` == false :\n  //           This is when the element is doing FD and just started the FD\n  //           solve. In this case, `active_gr_variables_tag` and\n  //           `subcell_face_gr_variables_tag` need to be modified. We manually\n  //           swap GR tags in the test databox to mimic the situation.\n  //\n  if (TestMovingMesh and not did_rollback and not ComputeOnlyOnRollback) {\n    db::mutate<gr_variables_tag, inactive_gr_variables_tag>(\n        [](const auto active_gr_vars_ptr, const auto inactive_gr_vars_ptr) {\n          using std::swap;\n          swap(*active_gr_vars_ptr, *inactive_gr_vars_ptr);\n        },\n        make_not_null(&box));\n  }\n\n  db::mutate_apply<evolution::dg::subcell::BackgroundGrVars<\n      SystemForTest, MetavariablesForTest, ComputeOnlyOnRollback>>(\n      make_not_null(&box));\n\n  //\n  // Chcek the results. All the `if` branches below are organized in the same\n  // order as the cases presented above.\n  //\n  if constexpr (not TestMovingMesh) {\n    check_cell_centered_vars(expected_initial_cell_centered_gr_vars, false);\n    check_face_centered_vars(expected_initial_face_centered_gr_vars);\n  } else {\n    // Compute expected values of cell-centered and face-centered GR vars\n    const auto expected_later_cell_centered_gr_vars =\n        solution.variables(subcell_later_inertial_coords, later_time,\n                           gr_variables_tag::tags_list{});\n\n    subcell_face_gr_variables_tag::type expected_later_face_centered_gr_vars{};\n    for (size_t d = 0; d < 3; ++d) {\n      gsl::at(expected_later_face_centered_gr_vars, d)\n          .initialize(gsl::at(face_centered_meshes, 0).number_of_grid_points());\n      gsl::at(expected_later_face_centered_gr_vars, d)\n          .assign_subset(evolution::Initialization::initial_data(\n              solution, gsl::at(face_centered_later_inertial_coords, d),\n              later_time, subcell_face_gr_variables_tag::tag::tags_list{}));\n    }\n\n    if (did_rollback) {\n      check_cell_centered_vars(expected_later_cell_centered_gr_vars, false);\n      check_face_centered_vars(expected_later_face_centered_gr_vars);\n    } else {\n      if constexpr (ComputeOnlyOnRollback) {\n        check_cell_centered_vars(expected_initial_cell_centered_gr_vars, false);\n        check_face_centered_vars(expected_initial_face_centered_gr_vars);\n      } else {\n        check_cell_centered_vars(expected_later_cell_centered_gr_vars, true);\n        check_face_centered_vars(expected_later_face_centered_gr_vars);\n      }\n    }\n  }\n}\n\ntemplate <bool TestMovingMesh>\nvoid test_for_rollback(const gsl::not_null<std::mt19937*> gen) {\n  test<TestMovingMesh, true>(gen, true);\n  test<TestMovingMesh, true>(gen, false);\n\n  test<TestMovingMesh, false>(gen, true);\n  test<TestMovingMesh, false>(gen, false);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Actions.BackgroundGrVars\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n\n  test_for_rollback<true>(make_not_null(&gen));\n  test_for_rollback<true>(make_not_null(&gen));\n\n  test_for_rollback<false>(make_not_null(&gen));\n  test_for_rollback<false>(make_not_null(&gen));\n}\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_CartesianFluxDivergence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  const size_t num_pts_1d = 5;\n  const Index<Dim> subcell_extents{num_pts_1d};\n  for (size_t d = 0; d < Dim; ++d) {\n    CAPTURE(d);\n    auto extents = make_array<Dim>(num_pts_1d);\n    ++gsl::at(extents, d);\n    const auto basis = make_array<Dim>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<Dim>(Spectral::Quadrature::CellCentered);\n    gsl::at(quadrature, d) = Spectral::Quadrature::FaceCentered;\n    const Mesh<Dim> subcell_face_mesh{extents, basis, quadrature};\n\n    DataVector dt_var{subcell_extents.product(), 1.2};\n    const DataVector det_inv_jacobian{subcell_extents.product(), 5.0};\n    const auto logical_coords = logical_coordinates(subcell_face_mesh);\n    const double one_over_delta =\n        1.0 / (get<0>(logical_coords)[1] - get<0>(logical_coords)[0]);\n    const DataVector boundary_correction = 3.0 * logical_coords.get(d);\n    evolution::dg::subcell::add_cartesian_flux_divergence(\n        make_not_null(&dt_var), one_over_delta, det_inv_jacobian,\n        boundary_correction, subcell_extents, d);\n    const DataVector expected_dt_var{subcell_extents.product(),\n                                     det_inv_jacobian[0] * 3.0 + 1.2};\n    CHECK_ITERABLE_APPROX(dt_var, expected_dt_var);\n  }\n}\n\nvoid test_cartoon() {\n  // Helper function to create spatially varying inverse Jacobian\n  const auto create_varying_det_inv_jacobian =\n      [](const size_t num_points, const double base_value,\n         const double increment) -> DataVector {\n    DataVector result(num_points);\n    for (size_t i = 0; i < num_points; ++i) {\n      result[i] = base_value + increment * static_cast<double>(i);\n    }\n    return result;\n  };\n  // Helper function to compute expected result for a single cell\n  const auto compute_cell_contribution =\n      [](const double one_over_delta, const double inv_jac,\n         const double weight_lower, const double flux_lower,\n         const double weight_upper, const double flux_upper) -> double {\n    return one_over_delta * inv_jac *\n           (weight_upper * flux_upper - weight_lower * flux_lower);\n  };\n\n  const double time = 0.0;\n  const std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3d =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\n  const Affine x_map{-1.0, 1.0, 1.0, 3.0};\n  const Affine y_map{-1.0, 1.0, -1.0, 1.0};\n  const Affine z_map{-1.0, 1.0, -1.0, 1.0};\n\n  const auto block_to_inertial_coord_map =\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          Affine3d{x_map, y_map, z_map});\n  const Block<3> block{block_to_inertial_coord_map.get_clone(), 0, {}};\n  const ElementId<3> element_id{0};\n  const auto logical_to_grid_map =\n      ElementMap<3, Frame::Grid>{element_id, block};\n  const auto grid_to_inertial_map_ptr =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n  const auto& grid_to_inertial_map = *grid_to_inertial_map_ptr;\n\n  {\n    INFO(\"Spherical symmetry\");\n    const Index<3> subcell_extents{3, 1, 1};\n    const size_t dimension = 0;\n\n    const Mesh<3> volume_mesh{\n        subcell_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::Cartoon,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::SphericalSymmetry,\n         Spectral::Quadrature::SphericalSymmetry}};\n\n    const auto volume_logical_coords = logical_coordinates(volume_mesh);\n    const auto volume_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(volume_logical_coords), time, functions_of_time);\n\n    Index<3> face_extents = subcell_extents;\n    ++face_extents[dimension];\n    const Mesh<3> face_mesh{\n        face_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::Cartoon,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::FaceCentered,\n         Spectral::Quadrature::SphericalSymmetry,\n         Spectral::Quadrature::SphericalSymmetry}};\n\n    const auto face_logical_coords = logical_coordinates(face_mesh);\n    const auto face_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(face_logical_coords), time, functions_of_time);\n\n    const double one_over_delta = 1.0 / (get<0>(volume_logical_coords)[1] -\n                                         get<0>(volume_logical_coords)[0]);\n\n    // Test with quadratic flux F^x = x^2\n    DataVector boundary_correction(face_inertial_coords.get(0).size());\n    for (size_t i = 0; i < boundary_correction.size(); ++i) {\n      boundary_correction[i] = square(get<0>(face_inertial_coords)[i]);\n    }\n\n    DataVector dt_var(subcell_extents.product(), 0.0);\n    const DataVector det_inv_jacobian =\n        create_varying_det_inv_jacobian(subcell_extents.product(), 0.5, 0.2);\n\n    evolution::dg::subcell::add_cartoon_cartesian_flux_divergence(\n        make_not_null(&dt_var), one_over_delta, det_inv_jacobian,\n        boundary_correction, subcell_extents, dimension, volume_inertial_coords,\n        logical_to_grid_map, grid_to_inertial_map, time, functions_of_time);\n\n    // Compute expected results for spherical symmetry with F^x = x^2\n    DataVector expected_dt_var(3);\n\n    // Cell data: [x_vol, x_face_lower, x_face_upper]\n    const std::array<std::array<double, 3>, 3> cell_data = {{\n        {4.0 / 3.0, 1.0, 5.0 / 3.0},  // Cell 0\n        {2.0, 5.0 / 3.0, 7.0 / 3.0},  // Cell 1\n        {8.0 / 3.0, 7.0 / 3.0, 3.0}   // Cell 2\n    }};\n\n    for (size_t i = 0; i < 3; ++i) {\n      const double x_vol = gsl::at(cell_data, i)[0];\n      const double x_face_lower = gsl::at(cell_data, i)[1];\n      const double x_face_upper = gsl::at(cell_data, i)[2];\n\n      const double f_lower = square(x_face_lower);\n      const double f_upper = square(x_face_upper);\n      const double weight_lower = square(x_face_lower) / square(x_vol);\n      const double weight_upper = square(x_face_upper) / square(x_vol);\n\n      expected_dt_var[i] = compute_cell_contribution(\n          one_over_delta, det_inv_jacobian[i], weight_lower, f_lower,\n          weight_upper, f_upper);\n    }\n\n    CHECK_ITERABLE_APPROX(dt_var, expected_dt_var);\n  }\n  {\n    INFO(\"Axial symmetry - dimension 0\");\n    const Index<3> subcell_extents{2, 2, 1};\n    const size_t dimension = 0;\n\n    const Mesh<3> volume_mesh{\n        subcell_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::FiniteDifference,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::CellCentered, Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::AxialSymmetry}};\n\n    const auto volume_logical_coords = logical_coordinates(volume_mesh);\n    const auto volume_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(volume_logical_coords), time, functions_of_time);\n\n    // Face mesh\n    Index<3> face_extents = subcell_extents;\n    ++face_extents[dimension];\n    const Mesh<3> face_mesh{\n        face_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::FiniteDifference,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::FaceCentered, Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::AxialSymmetry}};\n\n    const auto face_logical_coords = logical_coordinates(face_mesh);\n    const auto face_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(face_logical_coords), time, functions_of_time);\n\n    const double one_over_delta = 1.0 / (get<0>(volume_logical_coords)[1] -\n                                         get<0>(volume_logical_coords)[0]);\n\n    // Test with flux F^x = x\n    const DataVector boundary_correction = get<0>(face_inertial_coords);\n\n    DataVector dt_var(subcell_extents.product(), 0.0);\n    const DataVector det_inv_jacobian =\n        create_varying_det_inv_jacobian(subcell_extents.product(), 0.8, 0.1);\n\n    evolution::dg::subcell::add_cartoon_cartesian_flux_divergence(\n        make_not_null(&dt_var), one_over_delta, det_inv_jacobian,\n        boundary_correction, subcell_extents, dimension, volume_inertial_coords,\n        logical_to_grid_map, grid_to_inertial_map, time, functions_of_time);\n\n    DataVector expected_dt_var(4);\n\n    // Cell (0,0): x_vol = 1.5, x_face = [1, 2], inv_jac = 0.8\n    const double x_vol_00 = 1.5;\n    const double weight_lower_00 = 1.0 / x_vol_00;\n    const double weight_upper_00 = 2.0 / x_vol_00;\n    expected_dt_var[0] = one_over_delta * det_inv_jacobian[0] *\n                         (weight_upper_00 * 2.0 - weight_lower_00 * 1.0);\n\n    // Cell (1,0): x_vol = 2.5, x_face = [2, 3], inv_jac = 0.9\n    const double x_vol_10 = 2.5;\n    const double weight_lower_10 = 2.0 / x_vol_10;\n    const double weight_upper_10 = 3.0 / x_vol_10;\n    expected_dt_var[1] = one_over_delta * det_inv_jacobian[1] *\n                         (weight_upper_10 * 3.0 - weight_lower_10 * 2.0);\n\n    // Cells (0,1) and (1,1): same x-coords but different det_inv_jacobian\n    expected_dt_var[2] = one_over_delta * det_inv_jacobian[2] *\n                         (weight_upper_00 * 2.0 - weight_lower_00 * 1.0);\n    expected_dt_var[3] = one_over_delta * det_inv_jacobian[3] *\n                         (weight_upper_10 * 3.0 - weight_lower_10 * 2.0);\n\n    CHECK_ITERABLE_APPROX(dt_var, expected_dt_var);\n  }\n  {\n    // Should behave like regular Cartesian\n    INFO(\"Axial symmetry - dimension 1\");\n    const Index<3> subcell_extents{2, 2, 1};\n    const size_t dimension = 1;\n\n    const Mesh<3> volume_mesh{\n        subcell_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::FiniteDifference,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::CellCentered, Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::AxialSymmetry}};\n\n    const auto volume_logical_coords = logical_coordinates(volume_mesh);\n    const auto volume_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(volume_logical_coords), time, functions_of_time);\n\n    Index<3> face_extents = subcell_extents;\n    ++face_extents[dimension];\n    const Mesh<3> face_mesh{\n        face_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::FiniteDifference,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::CellCentered, Spectral::Quadrature::FaceCentered,\n         Spectral::Quadrature::AxialSymmetry}};\n\n    const auto face_logical_coords = logical_coordinates(face_mesh);\n    const auto face_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(face_logical_coords), time, functions_of_time);\n\n    const double one_over_delta = 1.0 / (get<1>(volume_logical_coords)[2] -\n                                         get<1>(volume_logical_coords)[0]);\n\n    // Test with flux F^y = y² + x\n    DataVector boundary_correction(face_inertial_coords.get(1).size());\n    for (size_t i = 0; i < boundary_correction.size(); ++i) {\n      const double y_coord = get<1>(face_inertial_coords)[i];\n      const double x_coord = get<0>(face_inertial_coords)[i];\n      boundary_correction[i] = square(y_coord) + x_coord;\n    }\n\n    DataVector dt_var(subcell_extents.product(), 0.0);\n    const DataVector det_inv_jacobian =\n        create_varying_det_inv_jacobian(subcell_extents.product(), 0.6, 0.15);\n\n    evolution::dg::subcell::add_cartoon_cartesian_flux_divergence(\n        make_not_null(&dt_var), one_over_delta, det_inv_jacobian,\n        boundary_correction, subcell_extents, dimension, volume_inertial_coords,\n        logical_to_grid_map, grid_to_inertial_map, time, functions_of_time);\n\n    // For axial symmetry in y-direction: no coordinate weighting (standard\n    // finite difference)\n    DataVector expected_dt_var(4);\n\n    for (size_t vol_idx = 0; vol_idx < 4; ++vol_idx) {\n      const size_t i = vol_idx % 2;                   // x-index\n      const size_t j = vol_idx / 2;                   // y-index\n      const size_t face_lower_idx = i + j * 2;        // Lower y-face\n      const size_t face_upper_idx = i + (j + 1) * 2;  // Upper y-face\n\n      // No coordinate weighting in y-direction (weight = 1.0)\n      expected_dt_var[vol_idx] =\n          compute_cell_contribution(one_over_delta, det_inv_jacobian[vol_idx],\n                                    1.0, boundary_correction[face_lower_idx],\n                                    1.0, boundary_correction[face_upper_idx]);\n    }\n    CHECK_ITERABLE_APPROX(dt_var, expected_dt_var);\n  }\n}\n\nvoid test_validation_errors() {\n#ifdef SPECTRE_DEBUG\n  {\n    INFO(\"Test 1D validation errors\");\n    const Index<1> subcell_extents{3};\n    {\n      INFO(\"Test wrong dt_var size\");\n      DataVector dt_var_wrong_size(2, 0.0);  // Should be 3\n      const DataVector det_inv_jacobian(3, 1.0);\n      const DataVector boundary_correction(4, 1.0);\n      CHECK_THROWS_WITH(\n          evolution::dg::subcell::add_cartesian_flux_divergence(\n              make_not_null(&dt_var_wrong_size), 1.0, det_inv_jacobian,\n              boundary_correction, subcell_extents, 0),\n          Catch::Matchers::ContainsSubstring(\n              \"dt_var size 2 does not match expected volume points 3\"));\n    }\n    {\n      INFO(\"Test wrong det_inv_jacobian size\");\n      DataVector dt_var(3, 0.0);\n      const DataVector det_inv_jacobian_wrong_size(2, 1.0);  // Should be 3\n      const DataVector boundary_correction(4, 1.0);\n      CHECK_THROWS_WITH(\n          evolution::dg::subcell::add_cartesian_flux_divergence(\n              make_not_null(&dt_var), 1.0, det_inv_jacobian_wrong_size,\n              boundary_correction, subcell_extents, 0),\n          Catch::Matchers::ContainsSubstring(\"det_inv_jacobian size 2 does not \"\n                                             \"match expected volume points 3\"));\n    }\n    {\n      INFO(\"Test wrong boundary_correction size\");\n      DataVector dt_var(3, 0.0);\n      const DataVector det_inv_jacobian(3, 1.0);\n      const DataVector boundary_correction_wrong_size(3, 1.0);\n      CHECK_THROWS_WITH(evolution::dg::subcell::add_cartesian_flux_divergence(\n                            make_not_null(&dt_var), 1.0, det_inv_jacobian,\n                            boundary_correction_wrong_size, subcell_extents, 0),\n                        Catch::Matchers::ContainsSubstring(\n                            \"boundary_correction size 3 does not match \"\n                            \"expected face points 4\"));\n    }\n    {\n      INFO(\"Test invalid dimension\");\n      DataVector dt_var(3, 0.0);\n      const DataVector det_inv_jacobian(3, 1.0);\n      const DataVector boundary_correction(4, 1.0);\n      CHECK_THROWS_WITH(\n          evolution::dg::subcell::add_cartesian_flux_divergence(\n              make_not_null(&dt_var), 1.0, det_inv_jacobian,\n              boundary_correction, subcell_extents, 1),\n          Catch::Matchers::ContainsSubstring(\"dimension must be 0 but is 1\"));\n    }\n  }\n  {\n    INFO(\"Test 2D validation errors\");\n    const Index<2> subcell_extents{3, 2};  // 6 volume points\n    {\n      INFO(\"Test wrong boundary_correction size for dimension 0\");\n      DataVector dt_var(6, 0.0);\n      const DataVector det_inv_jacobian(6, 1.0);\n      const DataVector boundary_correction_wrong_size(\n          7, 1.0);  // Should be 8 (4x2)\n      CHECK_THROWS_WITH(evolution::dg::subcell::add_cartesian_flux_divergence(\n                            make_not_null(&dt_var), 1.0, det_inv_jacobian,\n                            boundary_correction_wrong_size, subcell_extents, 0),\n                        Catch::Matchers::ContainsSubstring(\n                            \"boundary_correction size 7 does not match \"\n                            \"expected face points 8\"));\n    }\n    {\n      INFO(\"Test invalid dimension\");\n      DataVector dt_var(6, 0.0);\n      const DataVector det_inv_jacobian(6, 1.0);\n      const DataVector boundary_correction(8, 1.0);\n      CHECK_THROWS_WITH(evolution::dg::subcell::add_cartesian_flux_divergence(\n                            make_not_null(&dt_var), 1.0, det_inv_jacobian,\n                            boundary_correction, subcell_extents, 2),\n                        Catch::Matchers::ContainsSubstring(\n                            \"dimension must be 0 or 1 but is 2\"));\n    }\n  }\n  {\n    INFO(\"Test 3D validation errors\");\n    const Index<3> subcell_extents{2, 2, 2};  // 8 volume points\n    {\n      INFO(\"Test invalid dimension\");\n      DataVector dt_var(8, 0.0);\n      const DataVector det_inv_jacobian(8, 1.0);\n      const DataVector boundary_correction(12, 1.0);  // 3x2x2 for dim 0\n      CHECK_THROWS_WITH(evolution::dg::subcell::add_cartesian_flux_divergence(\n                            make_not_null(&dt_var), 1.0, det_inv_jacobian,\n                            boundary_correction, subcell_extents, 3),\n                        Catch::Matchers::ContainsSubstring(\n                            \"dimension must be 0, 1, or 2 but is 3\"));\n    }\n  }\n#endif  // SPECTRE_DEBUG\n  const double time = 0.0;\n  const std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Identity = domain::CoordinateMaps::Identity<1>;\n  using Affine3d =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Identity, Identity>;\n\n  const Affine x_map{-1.0, 1.0, 1.0, 3.0};\n\n  const auto block_to_inertial_coord_map =\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Inertial>(\n          Affine3d{x_map, Identity{}, Identity{}});\n  const Block<3> block{block_to_inertial_coord_map.get_clone(), 0, {}};\n  const ElementId<3> element_id{0};\n  const auto logical_to_grid_map =\n      ElementMap<3, Frame::Grid>{element_id, block};\n  const auto grid_to_inertial_map_ptr =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n  const auto& grid_to_inertial_map = *grid_to_inertial_map_ptr;\n  {\n    INFO(\"Test cartoon - contains zero coordinate\");\n    const Index<3> subcell_extents{3, 1, 1};\n    const size_t dimension = 0;\n\n    // Create coordinates that contain x=0\n    tnsr::I<DataVector, 3, Frame::Inertial> coords_with_zero(\n        subcell_extents.product());\n    get<0>(coords_with_zero) = DataVector{0.0, 1.0, 2.0};  // Contains x=0\n    get<1>(coords_with_zero) = DataVector{0.0, 0.0, 0.0};\n    get<2>(coords_with_zero) = DataVector{0.0, 0.0, 0.0};\n\n    DataVector dt_var(subcell_extents.product(), 0.0);\n    const DataVector det_inv_jacobian(subcell_extents.product(), 1.0);\n    const DataVector boundary_correction(4, 1.0);\n\n    CHECK_THROWS_WITH(\n        evolution::dg::subcell::add_cartoon_cartesian_flux_divergence(\n            make_not_null(&dt_var), 1.0, det_inv_jacobian, boundary_correction,\n            subcell_extents, dimension, coords_with_zero, logical_to_grid_map,\n            grid_to_inertial_map, time, functions_of_time),\n        Catch::Matchers::ContainsSubstring(\"Element contains x=0 for subcell\"));\n  }\n#ifdef SPECTRE_DEBUG\n  {\n    INFO(\"Test cartoon - wrong z extent\");\n    const Index<3> wrong_subcell_extents{3, 1, 2};  // z should be 1\n    const size_t dimension = 0;\n\n    const Mesh<3> volume_mesh{\n        wrong_subcell_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::Cartoon,\n         Spectral::Basis::FiniteDifference},\n        {Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::SphericalSymmetry,\n         Spectral::Quadrature::CellCentered}};\n\n    const auto volume_logical_coords = logical_coordinates(volume_mesh);\n    const auto volume_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(volume_logical_coords), time, functions_of_time);\n\n    DataVector dt_var(wrong_subcell_extents.product(), 0.0);\n    const DataVector det_inv_jacobian(wrong_subcell_extents.product(), 1.0);\n    const DataVector boundary_correction(8, 1.0);  // 4x1x2\n\n    CHECK_THROWS_WITH(\n        evolution::dg::subcell::add_cartoon_cartesian_flux_divergence(\n            make_not_null(&dt_var), 1.0, det_inv_jacobian, boundary_correction,\n            wrong_subcell_extents, dimension, volume_inertial_coords,\n            logical_to_grid_map, grid_to_inertial_map, time, functions_of_time),\n        Catch::Matchers::ContainsSubstring(\n            \"Expecting extent = 1 in third dimension\"));\n  }\n  {\n    INFO(\"Test cartoon - wrong x extent\");\n    const Index<3> wrong_subcell_extents{1, 2, 1};  // x should not be 1\n    const size_t dimension = 0;\n\n    const Mesh<3> volume_mesh{\n        wrong_subcell_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::FiniteDifference,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::CellCentered, Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::AxialSymmetry}};\n\n    const auto volume_logical_coords = logical_coordinates(volume_mesh);\n    const auto volume_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(volume_logical_coords), time, functions_of_time);\n\n    DataVector dt_var(wrong_subcell_extents.product(), 0.0);\n    const DataVector det_inv_jacobian(wrong_subcell_extents.product(), 1.0);\n    const DataVector boundary_correction(4, 1.0);  // 2x2x1\n\n    CHECK_THROWS_WITH(\n        evolution::dg::subcell::add_cartoon_cartesian_flux_divergence(\n            make_not_null(&dt_var), 1.0, det_inv_jacobian, boundary_correction,\n            wrong_subcell_extents, dimension, volume_inertial_coords,\n            logical_to_grid_map, grid_to_inertial_map, time, functions_of_time),\n        Catch::Matchers::ContainsSubstring(\n            \"Expecting extent != 1 in first dimension\"));\n  }\n  {\n    INFO(\"Test cartoon - invalid dimension for spherical\");\n    const Index<3> subcell_extents{3, 1, 1};  // Spherical symmetry\n    const size_t invalid_dimension = 1;       // Should be 0 for spherical\n\n    const Mesh<3> volume_mesh{\n        subcell_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::Cartoon,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::SphericalSymmetry,\n         Spectral::Quadrature::SphericalSymmetry}};\n\n    const auto volume_logical_coords = logical_coordinates(volume_mesh);\n    const auto volume_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(volume_logical_coords), time, functions_of_time);\n\n    DataVector dt_var(subcell_extents.product(), 0.0);\n    const DataVector det_inv_jacobian(subcell_extents.product(), 1.0);\n    const DataVector boundary_correction(\n        6,\n        1.0);  // Should actually be different size but testing dimension first\n\n    CHECK_THROWS_WITH(\n        evolution::dg::subcell::add_cartoon_cartesian_flux_divergence(\n            make_not_null(&dt_var), 1.0, det_inv_jacobian, boundary_correction,\n            subcell_extents, invalid_dimension, volume_inertial_coords,\n            logical_to_grid_map, grid_to_inertial_map, time, functions_of_time),\n        Catch::Matchers::ContainsSubstring(\n            \"Using cartoon derivatives with spherical symmetry, expecting \"\n            \"dimension = 0\"));\n  }\n  {\n    INFO(\"Test cartoon - invalid dimension for axial\");\n    const Index<3> subcell_extents{3, 2, 1};  // Axial symmetry\n    const size_t invalid_dimension = 2;       // Should be 0 or 1 for axial\n\n    const Mesh<3> volume_mesh{\n        subcell_extents.indices(),\n        {Spectral::Basis::FiniteDifference, Spectral::Basis::FiniteDifference,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::CellCentered, Spectral::Quadrature::CellCentered,\n         Spectral::Quadrature::AxialSymmetry}};\n\n    const auto volume_logical_coords = logical_coordinates(volume_mesh);\n    const auto volume_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(volume_logical_coords), time, functions_of_time);\n\n    DataVector dt_var(subcell_extents.product(), 0.0);\n    const DataVector det_inv_jacobian(subcell_extents.product(), 1.0);\n    const DataVector boundary_correction(12, 1.0);\n\n    CHECK_THROWS_WITH(\n        evolution::dg::subcell::add_cartoon_cartesian_flux_divergence(\n            make_not_null(&dt_var), 1.0, det_inv_jacobian, boundary_correction,\n            subcell_extents, invalid_dimension, volume_inertial_coords,\n            logical_to_grid_map, grid_to_inertial_map, time, functions_of_time),\n        Catch::Matchers::ContainsSubstring(\n            \"Using cartoon derivatives with axial symmetry, expecting \"\n            \"dimension = 0 or 1\"));\n  }\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.FD.CartesianFluxDivergence\",\n                  \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n  test_cartoon();\n  test_validation_errors();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_CellCenteredFlux.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var3 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct TestConservativeSystem {\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1, Var2>>;\n  using flux_variables = tmpl::list<Var1, Var2>;\n};\n\nstruct TestMixedSystem {\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1, Var2, Var3>>;\n  using flux_variables = tmpl::list<Var1, Var2>;\n};\n\ntemplate <size_t Dim>\nstruct Fluxes {\n  using return_tags =\n      tmpl::list<::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::Tags::Flux<Var2, tmpl::size_t<Dim>, Frame::Inertial>>;\n  using argument_tags = tmpl::list<Var1, Var2>;\n\n  static void apply(const gsl::not_null<tnsr::I<DataVector, Dim>*> flux_var1,\n                    const gsl::not_null<tnsr::I<DataVector, Dim>*> flux_var2,\n                    const Scalar<DataVector>& var1,\n                    const Scalar<DataVector>& var2) {\n    for (size_t i = 0; i < Dim; ++i) {\n      flux_var1->get(i) = (1.0 + static_cast<double>(i)) * get(var1);\n      flux_var2->get(i) = 5.0 * (1.0 + static_cast<double>(i)) * get(var2);\n    }\n  }\n};\n\ntemplate <typename TestSystem, size_t Dim, bool ComputeOnlyOnRollback>\nvoid test(const fd::DerivativeOrder derivative_order, const bool did_rollback) {\n  CAPTURE(Dim);\n  CAPTURE(ComputeOnlyOnRollback);\n  CAPTURE(derivative_order);\n  CAPTURE(did_rollback);\n  using variables = typename TestSystem::variables_tag::tags_list;\n  using flux_variables = typename TestSystem::flux_variables;\n  using CellCenteredFluxTag =\n      evolution::dg::subcell::Tags::CellCenteredFlux<flux_variables, Dim>;\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh{9, Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::CellCentered};\n  const std::optional<tnsr::I<DataVector, Dim>> dg_mesh_velocity{};\n  Variables<variables> vars{subcell_mesh.number_of_grid_points()};\n  get(get<Var1>(vars)) = 1.0;\n  get(get<Var2>(vars)) = 2.0;\n\n  auto box =\n      db::create<tmpl::list<evolution::dg::subcell::Tags::DidRollback,\n                            evolution::dg::subcell::Tags::SubcellOptions<Dim>,\n                            evolution::dg::subcell::Tags::Mesh<Dim>,\n                            CellCenteredFluxTag, domain::Tags::Mesh<Dim>,\n                            domain::Tags::MeshVelocity<Dim, Frame::Inertial>,\n                            ::Tags::Variables<variables>>>(\n          did_rollback,\n          evolution::dg::subcell::SubcellOptions{\n              4.0, 1_st, 1.0e-3, 1.0e-4, false, false,\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n              std::nullopt, derivative_order, 1, 1, 1},\n          subcell_mesh, typename CellCenteredFluxTag::type{}, dg_mesh,\n          dg_mesh_velocity,\n          Variables<variables>{subcell_mesh.number_of_grid_points(), 1.0});\n\n  db::mutate_apply<evolution::dg::subcell::fd::CellCenteredFlux<\n      TestSystem, Fluxes<Dim>, Dim, ComputeOnlyOnRollback>>(\n      make_not_null(&box));\n  if (derivative_order != fd::DerivativeOrder::Two and\n      (not ComputeOnlyOnRollback or did_rollback)) {\n    REQUIRE(get<evolution::dg::subcell::Tags::CellCenteredFlux<flux_variables,\n                                                               Dim>>(box)\n                .has_value());\n    const auto& [flux1, flux2] = get<CellCenteredFluxTag>(box).value();\n    const auto& box_vars = get<::Tags::Variables<variables>>(box);\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK(flux1.get(i) == DataVector((1.0 + static_cast<double>(i)) *\n                                       get(get<Var1>(box_vars))));\n      CHECK(flux2.get(i) == DataVector(5.0 * (1.0 + static_cast<double>(i)) *\n                                       get(get<Var2>(box_vars))));\n    }\n  } else {\n    CHECK(not get<evolution::dg::subcell::Tags::CellCenteredFlux<flux_variables,\n                                                                 Dim>>(box)\n                  .has_value());\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.CellCenteredFlux\",\n                  \"[Evolution][Unit]\") {\n  using DO = fd::DerivativeOrder;\n  for (const DO derivative_order :\n       {DO::Two, DO::Four, DO::Six, DO::Eight, DO::Ten, DO::OneHigherThanRecons,\n        DO::OneHigherThanReconsButFiveToFour}) {\n    for (const bool did_rollback : {true, false}) {\n      test<TestConservativeSystem, 1, false>(derivative_order, did_rollback);\n      test<TestConservativeSystem, 2, false>(derivative_order, did_rollback);\n      test<TestConservativeSystem, 3, false>(derivative_order, did_rollback);\n\n      test<TestConservativeSystem, 1, true>(derivative_order, did_rollback);\n      test<TestConservativeSystem, 2, true>(derivative_order, did_rollback);\n      test<TestConservativeSystem, 3, true>(derivative_order, did_rollback);\n\n      test<TestMixedSystem, 1, false>(derivative_order, did_rollback);\n      test<TestMixedSystem, 2, false>(derivative_order, did_rollback);\n      test<TestMixedSystem, 3, false>(derivative_order, did_rollback);\n\n      test<TestMixedSystem, 1, true>(derivative_order, did_rollback);\n      test<TestMixedSystem, 2, true>(derivative_order, did_rollback);\n      test<TestMixedSystem, 3, true>(derivative_order, did_rollback);\n    }\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_CombineVolumeGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/CombineVolumeGhostData.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\nclass DummyReconstructor {\n public:\n  static size_t ghost_zone_size() { return 3; }\n};\n\ntemplate <size_t Dim>\nvoid test(bool test_lower) {\n  const auto subcell_mesh = evolution::dg::subcell::fd::mesh(::Mesh<Dim>{\n      {5}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto});\n  const auto element_logical_coords = logical_coordinates(subcell_mesh);\n\n  DataVector volume_data{Dim * element_logical_coords[0].size()};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < element_logical_coords[0].size(); ++j) {\n      volume_data[j + i * element_logical_coords[0].size()] =\n          element_logical_coords[i][j];\n    }\n  }\n  for (size_t i = 0; i < Dim; ++i) {\n    const Direction<Dim> direction_to_extend{\n        i, test_lower ? Side::Lower : Side::Upper};\n    const auto element_ghost_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, DummyReconstructor::ghost_zone_size(),\n            direction_to_extend);\n    DataVector ghost_data{Dim * element_ghost_coords[0].size()};\n    for (size_t j = 0; j < Dim; ++j) {\n      for (size_t k = 0; k < element_ghost_coords[0].size(); ++k) {\n        ghost_data[k + j * element_ghost_coords[0].size()] =\n            element_ghost_coords[j][k];\n      }\n    }\n    const DataVector combined_data =\n        evolution::dg::subcell::combine_volume_ghost_data(\n            volume_data, ghost_data, subcell_mesh.extents(),\n            DummyReconstructor::ghost_zone_size(), direction_to_extend);\n\n    auto new_extents = make_array<Dim>(subcell_mesh.extents(0));\n    gsl::at(new_extents, i) =\n        subcell_mesh.extents(i) + DummyReconstructor::ghost_zone_size();\n    const auto extended_mesh = ::Mesh<Dim>{new_extents, subcell_mesh.basis(),\n                                           subcell_mesh.quadrature()};\n    auto extended_logical_coords = logical_coordinates(extended_mesh);\n    const double rescale_factor =\n        static_cast<double>(subcell_mesh.extents(i) +\n                            DummyReconstructor::ghost_zone_size()) /\n        (subcell_mesh.extents(i));\n    double translation =\n        static_cast<double>(DummyReconstructor::ghost_zone_size()) /\n        (subcell_mesh.extents(i));\n    if (test_lower) {\n      translation *= -1.;\n    }\n    for (size_t l = 0; l < extended_logical_coords[0].size(); ++l) {\n      extended_logical_coords.get(i)[l] *= rescale_factor;\n      extended_logical_coords.get(i)[l] += translation;\n    }\n\n    DataVector expected_combined_data{Dim * extended_logical_coords[0].size()};\n    for (size_t n = 0; n < Dim; ++n) {\n      for (size_t m = 0; m < extended_logical_coords[0].size(); ++m) {\n        expected_combined_data[m + n * extended_logical_coords[0].size()] =\n            extended_logical_coords[n][m];\n      }\n    }\n    CAPTURE(direction_to_extend);\n    CAPTURE(test_lower);\n    CHECK_ITERABLE_APPROX(combined_data, expected_combined_data);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.CombineVolumeGhostData\",\n                  \"[Evolution][Unit]\") {\n  for (const bool test_lower : {false, true}) {\n    test<1>(test_lower);\n    test<2>(test_lower);\n    test<3>(test_lower);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_ComputeBoundaryTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/AsAccess.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ComputeBoundaryTerms.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Number : db::SimpleTag {\n  using type = int;\n};\n\nclass BoundaryCorrection {\n public:\n  explicit BoundaryCorrection(const double multiply) : multiply_(multiply) {}\n  using dg_boundary_terms_volume_tags = tmpl::list<Number>;\n  void dg_boundary_terms(\n      const gsl::not_null<Scalar<DataVector>*> correction_var1,\n      const Scalar<DataVector>& upper_var1,\n      const Scalar<DataVector>& lower_var1,\n      const dg::Formulation dg_formulation, const int number) const {\n    get(*correction_var1) = get(upper_var1) + multiply_ * get(lower_var1);\n    CHECK(dg_formulation == dg::Formulation::WeakInertial);\n    CHECK(number == 10);\n  }\n\n private:\n  double multiply_ = std::numeric_limits<double>::signaling_NaN();\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.ComputeBoundaryTerms\",\n                  \"[Evolution][Unit]\") {\n  const auto box =\n      db::create<db::AddSimpleTags<Number>, db::AddComputeTags<>>(10);\n  const BoundaryCorrection correction{5.0};\n  const size_t num_pts = 5;\n  Variables<tmpl::list<Var1>> corrections{num_pts};\n  Variables<tmpl::list<Var1>> upper{num_pts, 1.3};\n  Variables<tmpl::list<Var1>> lower{num_pts, 7.0};\n  evolution::dg::subcell::compute_boundary_terms(\n      make_not_null(&corrections), correction, upper, lower, db::as_access(box),\n      typename BoundaryCorrection::dg_boundary_terms_volume_tags{});\n  Variables<tmpl::list<Var1>> expected_corrections{num_pts, 1.3 + 5.0 * 7.0};\n  CHECK_VARIABLES_APPROX(corrections, expected_corrections);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_CorrectPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <numeric>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DgSubcell/CorrectPackagedData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim, bool SkipFirstVar>\nvoid test() {\n  CAPTURE(Dim);\n  CAPTURE(SkipFirstVar);\n  using Vars = Variables<tmpl::list<Var1, Var2<Dim>>>;\n  using SubcellFaceVars =\n      tmpl::conditional_t<SkipFirstVar, Variables<tmpl::list<Var2<Dim>>>, Vars>;\n  const Mesh<Dim> volume_dg_mesh{5, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> volume_subcell_mesh =\n      evolution::dg::subcell::fd::mesh(volume_dg_mesh);\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    neighbors[gsl::at(Direction<Dim>::all_directions(), i)] = Neighbors<Dim>{\n        {ElementId<Dim>{i + 1, {}}}, OrientationMap<Dim>::create_aligned()};\n  }\n  const Element<Dim> element{ElementId<Dim>{0, {}}, neighbors};\n  const TimeStepId time_step_id{true, 1, Time{Slab{1.1, 4.4}, {3, 10}}};\n\n  CAPTURE(volume_dg_mesh);\n  CAPTURE(volume_subcell_mesh);\n  for (size_t direction_to_check = 0; direction_to_check < Dim;\n       ++direction_to_check) {\n    CAPTURE(direction_to_check);\n    Index<Dim> volume_face_extents = volume_subcell_mesh.extents();\n    ++volume_face_extents[direction_to_check];\n    SubcellFaceVars lower_packaged_data{volume_face_extents.product()};\n    SubcellFaceVars upper_packaged_data{volume_face_extents.product()};\n    const auto set_volume_data = [&lower_packaged_data,\n                                  &upper_packaged_data]() {\n      std::iota(lower_packaged_data.data(),\n                lower_packaged_data.data() + lower_packaged_data.size(), 1.0);\n      std::iota(upper_packaged_data.data(),\n                upper_packaged_data.data() + upper_packaged_data.size(),\n                1.0 + upper_packaged_data.size());\n    };\n    set_volume_data();\n    // Save a copy of the lower/upper data so we can compare points in the\n    // volume/away from the interfaces.\n    const SubcellFaceVars interior_lower_packaged_data = lower_packaged_data;\n    const SubcellFaceVars interior_upper_packaged_data = upper_packaged_data;\n\n    DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>> mortar_data{};\n    const Direction<Dim> upper{direction_to_check, Side::Upper};\n    const Direction<Dim> lower{direction_to_check, Side::Lower};\n    const DirectionalId<Dim> upper_neighbor{\n        upper, *element.neighbors().at(upper).begin()};\n    const DirectionalId<Dim> lower_neighbor{\n        lower, *element.neighbors().at(lower).begin()};\n    evolution::dg::MortarDataHolder<Dim>& upper_mortar_data =\n        mortar_data[upper_neighbor] = {};\n    evolution::dg::MortarDataHolder<Dim>& lower_mortar_data =\n        mortar_data[lower_neighbor] = {};\n\n    const Mesh<Dim - 1> dg_face_mesh =\n        volume_dg_mesh.slice_away(direction_to_check);\n    const size_t dg_number_of_independent_components =\n        Vars::number_of_independent_components;\n    const auto upper_neighbor_data = [&dg_face_mesh, &upper_packaged_data]() {\n      DataVector result{dg_number_of_independent_components *\n                        dg_face_mesh.number_of_grid_points()};\n      std::iota(result.begin(), result.end(),\n                1.0 + 2.0 * upper_packaged_data.size());\n      return result;\n    }();\n    const auto lower_neighbor_data = [&dg_face_mesh, &upper_packaged_data,\n                                      &upper_neighbor_data]() {\n      DataVector result{dg_number_of_independent_components *\n                        dg_face_mesh.number_of_grid_points()};\n      std::iota(\n          result.begin(), result.end(),\n          1.0 + 2.0 * upper_packaged_data.size() + upper_neighbor_data.size());\n      return result;\n    }();\n\n    // Insert neighbor DG data.\n    upper_mortar_data.neighbor().face_mesh = dg_face_mesh;\n    upper_mortar_data.neighbor().mortar_mesh = dg_face_mesh;\n    upper_mortar_data.neighbor().mortar_data = upper_neighbor_data;\n    lower_mortar_data.neighbor().face_mesh = dg_face_mesh;\n    lower_mortar_data.neighbor().mortar_mesh = dg_face_mesh;\n    lower_mortar_data.neighbor().mortar_data = lower_neighbor_data;\n\n    const Mesh<Dim - 1> subcell_face_mesh =\n        volume_subcell_mesh.slice_away(direction_to_check);\n    const auto perform_face_check = [&dg_face_mesh, direction_to_check,\n                                     &subcell_face_mesh, &volume_face_extents](\n                                        const auto& face_dg_data,\n                                        const auto& volume_fd_packaged_data,\n                                        const size_t subcell_index) {\n      SubcellFaceVars dg_face_vars_data{dg_face_mesh.number_of_grid_points()};\n      std::copy(\n          std::next(\n              face_dg_data.begin(),\n              static_cast<std::ptrdiff_t>(\n                  SkipFirstVar ? dg_face_mesh.number_of_grid_points() : 0_st)),\n          face_dg_data.end(), dg_face_vars_data.data());\n      SubcellFaceVars subcell_face_vars_data{};\n      if constexpr (Dim > 1) {\n        subcell_face_vars_data = evolution::dg::subcell::fd::project(\n            dg_face_vars_data, dg_face_mesh, subcell_face_mesh.extents());\n      } else {\n        (void)subcell_face_mesh;\n        subcell_face_vars_data = dg_face_vars_data;\n      }\n      for (SliceIterator si(volume_face_extents, direction_to_check,\n                            subcell_index);\n           si; ++si) {\n        CAPTURE(si.volume_offset());\n        CAPTURE(si.slice_offset());\n        tmpl::for_each<typename SubcellFaceVars::tags_list>(\n            [&si, &subcell_face_vars_data,\n             &volume_fd_packaged_data](auto tag_v) {\n              using tag = tmpl::type_from<decltype(tag_v)>;\n              CAPTURE(pretty_type::name<tag>());\n              for (size_t storage_index = 0;\n                   storage_index < get<tag>(volume_fd_packaged_data).size();\n                   ++storage_index) {\n                CAPTURE(storage_index);\n                CHECK(get<tag>(volume_fd_packaged_data)[storage_index]\n                                                       [si.volume_offset()] ==\n                      approx(\n                          get<tag>(subcell_face_vars_data)[storage_index]\n                                                          [si.slice_offset()]));\n              }\n            });\n      }\n    };\n    const auto perform_interior_check =\n        [direction_to_check, &volume_face_extents](\n            const auto& expected_volume_packaged_data,\n            const SubcellFaceVars& volume_packaged_data,\n            const size_t start_index, const size_t one_past_end_index) {\n          for (size_t slice_index = start_index;\n               slice_index < one_past_end_index; ++slice_index) {\n            for (SliceIterator si(volume_face_extents, direction_to_check,\n                                  slice_index);\n                 si; ++si) {\n              tmpl::for_each<typename SubcellFaceVars::tags_list>(\n                  [&si, &expected_volume_packaged_data,\n                   &volume_packaged_data](auto tag_v) {\n                    using tag = tmpl::type_from<decltype(tag_v)>;\n                    CAPTURE(pretty_type::name<tag>());\n                    for (size_t storage_index = 0;\n                         storage_index < get<tag>(volume_packaged_data).size();\n                         ++storage_index) {\n                      CAPTURE(storage_index);\n                      CHECK(\n                          get<tag>(volume_packaged_data)[storage_index]\n                                                        [si.volume_offset()] ==\n                          approx(get<tag>(expected_volume_packaged_data)\n                                     [storage_index][si.volume_offset()]));\n                    }\n                  });\n            }\n          }\n        };\n\n    // Check with only remote data\n    evolution::dg::subcell::correct_package_data<false>(\n        make_not_null(&lower_packaged_data),\n        make_not_null(&upper_packaged_data), direction_to_check, element,\n        volume_subcell_mesh, mortar_data, SkipFirstVar ? 1_st : 0_st);\n\n    perform_face_check(upper_neighbor_data, upper_packaged_data,\n                       volume_face_extents[direction_to_check] - 1);\n    perform_face_check(lower_neighbor_data, lower_packaged_data, 0);\n    perform_interior_check(interior_upper_packaged_data, upper_packaged_data, 0,\n                           volume_face_extents[direction_to_check] - 1);\n    perform_interior_check(interior_lower_packaged_data, lower_packaged_data, 1,\n                           volume_face_extents[direction_to_check]);\n\n    // Reset volume data and then check that we can project both local and\n    // remote data (note the `true` template parameter to\n    // `correct_package_data`)\n    set_volume_data();\n\n    {\n      DataVector upper_local_data{dg_number_of_independent_components *\n                                  dg_face_mesh.number_of_grid_points()};\n      std::iota(upper_local_data.begin(), upper_local_data.end(), 1.0e6);\n      DataVector lower_local_data{dg_number_of_independent_components *\n                                  dg_face_mesh.number_of_grid_points()};\n      std::iota(lower_local_data.begin(), lower_local_data.end(), 1.0e7);\n      upper_mortar_data.local().face_mesh = dg_face_mesh;\n      upper_mortar_data.local().mortar_mesh = dg_face_mesh;\n      upper_mortar_data.local().mortar_data = upper_local_data;\n      lower_mortar_data.local().face_mesh = dg_face_mesh;\n      lower_mortar_data.local().mortar_mesh = dg_face_mesh;\n      lower_mortar_data.local().mortar_data = lower_local_data;\n\n      evolution::dg::subcell::correct_package_data<true>(\n          make_not_null(&lower_packaged_data),\n          make_not_null(&upper_packaged_data), direction_to_check, element,\n          volume_subcell_mesh, mortar_data, SkipFirstVar ? 1_st : 0_st);\n\n      perform_face_check(upper_neighbor_data, upper_packaged_data,\n                         volume_face_extents[direction_to_check] - 1);\n      perform_face_check(lower_neighbor_data, lower_packaged_data, 0);\n      perform_face_check(upper_local_data, lower_packaged_data,\n                         volume_face_extents[direction_to_check] - 1);\n      perform_face_check(lower_local_data, upper_packaged_data, 0);\n\n      perform_interior_check(interior_upper_packaged_data, upper_packaged_data,\n                             1, volume_face_extents[direction_to_check] - 1);\n      perform_interior_check(interior_lower_packaged_data, lower_packaged_data,\n                             1, volume_face_extents[direction_to_check] - 1);\n    }\n\n    // Check that if the local data is in the map but not requested to be\n    // overwritten then we don't overwrite it.\n    set_volume_data();\n\n    evolution::dg::subcell::correct_package_data<false>(\n        make_not_null(&lower_packaged_data),\n        make_not_null(&upper_packaged_data), direction_to_check, element,\n        volume_subcell_mesh, mortar_data, SkipFirstVar ? 1_st : 0_st);\n\n    perform_face_check(upper_neighbor_data, upper_packaged_data,\n                       volume_face_extents[direction_to_check] - 1);\n    perform_face_check(lower_neighbor_data, lower_packaged_data, 0);\n\n    perform_interior_check(interior_upper_packaged_data, upper_packaged_data, 0,\n                           volume_face_extents[direction_to_check] - 1);\n    perform_interior_check(interior_lower_packaged_data, lower_packaged_data, 1,\n                           volume_face_extents[direction_to_check]);\n  }  // for (size_t direction_to_check = 0; direction_to_check < Dim;\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.CorrectPackagedData\",\n                  \"[Evolution][Unit]\") {\n  test<1, false>();\n  test<1, true>();\n  test<2, false>();\n  test<2, true>();\n  test<3, false>();\n  test<3, true>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_DisableLts.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/AlignedLattice.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/DisableLts.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n#include \"Time/Tags/FixedLtsRatio.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const size_t block, const size_t segment) {\n  const bool dg_element = block == 0;\n\n  auto segments = make_array<Dim>(SegmentId(1, 0));\n  segments[0] = SegmentId(1, segment);\n  const ElementId<Dim> id(block, segments);\n\n  const domain::creators::AlignedLattice<Dim> domain_creator(\n      make_array<Dim>(make_vector(0.0, 1.0, 2.0)), make_array<Dim>(1_st),\n      make_array<Dim>(6_st), {}, {}, {});\n  const auto domain = domain_creator.create_domain();\n  const auto element = domain::create_initial_element(\n      id, domain.blocks(), domain_creator.initial_refinement_levels());\n\n  DirectionalIdMap<Dim, evolution::dg::MortarInfo<Dim>> initial_mortar_infos{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    for (const auto& neighbor : neighbors) {\n      initial_mortar_infos[{direction, neighbor}].time_stepping_policy() =\n          evolution::dg::TimeSteppingPolicy::Conservative;\n    }\n  }\n\n  const size_t ratio = 64;\n\n  // NOLINTNEXTLINE(misc-const-correctness)\n  evolution::dg::subcell::SubcellOptions subcell_opts(\n      evolution::dg::subcell::SubcellOptions(\n          4.0, 1, 2.0e-3, 2.0e-4, false, false,\n          evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n          make_vector<std::string>(MakeString{} << \"Block\" << Index<Dim>(0)),\n          fd::DerivativeOrder::Two, 1, 1, 1, 1, ratio),\n      domain_creator);\n\n  auto box = db::create<db::AddSimpleTags<\n      Tags::FixedLtsRatio, evolution::dg::Tags::MortarInfo<Dim>,\n      domain::Tags::Element<Dim>,\n      evolution::dg::subcell::Tags::SubcellOptions<Dim>>>(\n      std::optional<size_t>{}, std::move(initial_mortar_infos), element,\n      std::move(subcell_opts));\n\n  db::mutate_apply<evolution::dg::subcell::DisableLts<Dim>>(\n      make_not_null(&box));\n\n  CHECK(db::get<Tags::FixedLtsRatio>(box) ==\n        (dg_element ? std::nullopt : std::optional{ratio}));\n  const auto& mortar_infos = db::get<evolution::dg::Tags::MortarInfo<Dim>>(box);\n  CHECK(mortar_infos.size() == element.number_of_neighbors());\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    for (const auto& neighbor : neighbors) {\n      if (dg_element) {\n        CHECK(mortar_infos.at({direction, neighbor}).time_stepping_policy() ==\n              evolution::dg::TimeSteppingPolicy::Conservative);\n      } else {\n        CHECK(mortar_infos.at({direction, neighbor}).time_stepping_policy() ==\n              (neighbor.block_id() == 0\n                   ? evolution::dg::TimeSteppingPolicy::Conservative\n                   : evolution::dg::TimeSteppingPolicy::EqualRate));\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.DisableLts\", \"[Evolution][Unit]\") {\n  for (size_t block = 0; block < 2; ++block) {\n    for (size_t segment = 0; segment < 2; ++segment) {\n      test<1>(block, segment);\n      test<2>(block, segment);\n      test<3>(block, segment);\n    }\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_GetActiveTag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/GetActiveTag.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.GetActiveTag\", \"[Evolution][Unit]\") {\n  for (const auto active_grid : {evolution::dg::subcell::ActiveGrid::Dg,\n                                 evolution::dg::subcell::ActiveGrid::Subcell}) {\n    const auto box = db::create<db::AddSimpleTags<\n        domain::Tags::Coordinates<1, Frame::Inertial>,\n        evolution::dg::subcell::Tags::Coordinates<1, Frame::Inertial>,\n        evolution::dg::subcell::Tags::ActiveGrid>>(\n        tnsr::I<DataVector, 1>{10_st, 1.0}, tnsr::I<DataVector, 1>{20_st, 2.0},\n        active_grid);\n    if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n      CHECK(evolution::dg::subcell::get_active_tag<\n                domain::Tags::Coordinates<1, Frame::Inertial>,\n                evolution::dg::subcell::Tags::Coordinates<1, Frame::Inertial>>(\n                box) ==\n            db::get<domain::Tags::Coordinates<1, Frame::Inertial>>(box));\n      CHECK(evolution::dg::subcell::get_inactive_tag<\n                domain::Tags::Coordinates<1, Frame::Inertial>,\n                evolution::dg::subcell::Tags::Coordinates<1, Frame::Inertial>>(\n                box) ==\n            db::get<\n                evolution::dg::subcell::Tags::Coordinates<1, Frame::Inertial>>(\n                box));\n    } else {\n      CHECK(evolution::dg::subcell::get_active_tag<\n                domain::Tags::Coordinates<1, Frame::Inertial>,\n                evolution::dg::subcell::Tags::Coordinates<1, Frame::Inertial>>(\n                box) ==\n            db::get<\n                evolution::dg::subcell::Tags::Coordinates<1, Frame::Inertial>>(\n                box));\n      CHECK(evolution::dg::subcell::get_inactive_tag<\n                domain::Tags::Coordinates<1, Frame::Inertial>,\n                evolution::dg::subcell::Tags::Coordinates<1, Frame::Inertial>>(\n                box) ==\n            db::get<domain::Tags::Coordinates<1, Frame::Inertial>>(box));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_GetTciDecision.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/DgSubcell/GetTciDecision.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.GetTciDecision\",\n                  \"[Evolution][Unit]\") {\n  auto box = db::create<db::AddSimpleTags<Tags::TciDecision>>(10);\n  CHECK(get_tci_decision(box) == 10);\n  db::mutate<Tags::TciDecision>(\n      [](const gsl::not_null<int*> tci_decision_ptr) {\n        *tci_decision_ptr = -7;\n      },\n      make_not_null(&box));\n  CHECK(get_tci_decision(box) == -7);\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace {\nvoid test(const size_t number_of_buffers) {\n  std::uniform_real_distribution<double> dist(-1.0, 1.0);\n  MAKE_GENERATOR(gen);\n\n  constexpr size_t number_of_grid_points = 8;\n\n  CAPTURE(number_of_buffers);\n  CAPTURE(number_of_grid_points);\n\n  GhostData ghost_data{number_of_buffers};\n  std::vector<DataVector> all_local_data{number_of_buffers,\n                                         DataVector{number_of_grid_points}};\n  std::vector<DataVector> all_neighbor_data{number_of_buffers,\n                                            DataVector{number_of_grid_points}};\n\n  CHECK(ghost_data.total_number_of_buffers() == number_of_buffers);\n\n  for (size_t i = 0; i < number_of_buffers; i++) {\n    DataVector& local_data = all_local_data[i];\n    DataVector& neighbor_data = all_neighbor_data[i];\n    fill_with_random_values(make_not_null(&local_data), make_not_null(&gen),\n                            make_not_null(&dist));\n    fill_with_random_values(make_not_null(&neighbor_data), make_not_null(&gen),\n                            make_not_null(&dist));\n\n    std::string expected_output =\n        MakeString{} << \"LocalGhostData: \" << local_data << \"\\n\"\n                     << \"NeighborGhostDataForReconstruction: \" << neighbor_data\n                     << \"\\n\";\n\n    CHECK(ghost_data.local_ghost_data().size() == 0);\n    CHECK(ghost_data.neighbor_ghost_data_for_reconstruction().size() == 0);\n\n    ghost_data.local_ghost_data() = local_data;\n    ghost_data.neighbor_ghost_data_for_reconstruction() = neighbor_data;\n\n    CHECK(ghost_data.local_ghost_data() == local_data);\n    CHECK(ghost_data.neighbor_ghost_data_for_reconstruction() == neighbor_data);\n\n    CHECK(get_output(ghost_data) == expected_output);\n\n    CHECK(ghost_data.current_buffer_index() == i);\n    ghost_data.next_buffer();\n    // If we only have one buffer then the index will stay the same. Otherwise\n    // it'll change\n    if (number_of_buffers == 1) {\n      CHECK(ghost_data.current_buffer_index() == i);\n    } else {\n      CHECK_FALSE(ghost_data.current_buffer_index() == i);\n    }\n  }\n\n  // Make sure we're back at the beginning\n  CHECK(ghost_data.current_buffer_index() == 0);\n\n  for (size_t i = 0; i < number_of_buffers; i++) {\n    DataVector previous_local_data = ghost_data.local_ghost_data();\n    DataVector previous_neighbor_data =\n        ghost_data.neighbor_ghost_data_for_reconstruction();\n\n    ghost_data.next_buffer();\n\n    // If we only have one buffer, make sure the data is the same. Otherwise\n    // check that the data is different\n    if (number_of_buffers == 1) {\n      CHECK(ghost_data.local_ghost_data() == previous_local_data);\n      CHECK(ghost_data.neighbor_ghost_data_for_reconstruction() ==\n            previous_neighbor_data);\n    } else {\n      CHECK_FALSE(ghost_data.local_ghost_data() == previous_local_data);\n      CHECK_FALSE(ghost_data.neighbor_ghost_data_for_reconstruction() ==\n                  previous_neighbor_data);\n    }\n  }\n\n  test_serialization(ghost_data);\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      GhostData{0},\n      Catch::Matchers::ContainsSubstring(\n          \"The GhostData class must be constructed with at least one buffer.\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.GhostData\", \"[Unit][Evolution]\") {\n  for (size_t i = 1; i < 5; i++) {\n    test(i);\n  }\n  test_errors();\n}\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_GhostZoneInverseJacobian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Frustum.hpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneInverseJacobian.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostZoneInverseJacobian.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nusing CoordinateMap =\n    tmpl::conditional_t<Dim == 3, domain::CoordinateMaps::Frustum,\n                        domain::CoordinateMaps::Identity<Dim>>;\n\nclass DummyReconstructor {\n public:\n  static size_t ghost_zone_size() { return 3; }\n};\n\nnamespace Tags {\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<DummyReconstructor>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nvoid test() {\n  // Since the mutator relies on already tested functions to compute each of the\n  // quantities stored in the tag, we just test to make sure that they were\n  // properly stored in the `GhostZoneInverseJacobian` tag upon its mutation\n  CAPTURE(Dim);\n\n  // Assemble an Element and its ElementMap\n  const Mesh<Dim> dg_mesh{3, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using neighbor_tags = tmpl::list<\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Grid>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGrid<Dim>>;\n\n  const ElementId<Dim> element_id{0};\n  const Element<Dim> element{element_id, {}};\n\n  CoordinateMap<Dim> coordinate_map;\n  if constexpr (Dim == 3) {\n    const std::array<std::array<double, 2>, 4> face_vertices{\n        {{{-5., -5.}}, {{5., 5.}}, {{-3., -3.}}, {{3., 3.}}}};\n    coordinate_map = domain::CoordinateMaps::Frustum(\n        face_vertices, -4., 4., OrientationMap<3>::create_aligned());\n  } else {\n    coordinate_map = domain::CoordinateMaps::Identity<Dim>();\n  }\n  auto element_map = ElementMap<Dim, Frame::Grid>(\n      element_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          coordinate_map));\n\n  // Mutate the tag using the mutator\n  auto box = db::create<db::AddSimpleTags<\n      evolution::dg::subcell::Tags::GhostZoneInverseJacobian<Dim>,\n      ::domain::Tags::Element<Dim>, evolution::dg::subcell::Tags::Mesh<Dim>,\n      ::domain::Tags::ElementMap<Dim, Frame::Grid>, Tags::Reconstructor>>(\n      typename evolution::dg::subcell::Tags::GhostZoneInverseJacobian<\n          Dim>::type{},\n      element, subcell_mesh, std::move(element_map),\n      std::make_unique<DummyReconstructor>());\n  db::mutate_apply<evolution::dg::subcell::GhostZoneInverseJacobian<\n      Dim, Tags::Reconstructor>>(make_not_null(&box));\n\n  // Compare to computed outcome direction-wise\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    const auto logical_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, DummyReconstructor::ghost_zone_size(), direction);\n    const auto grid_coords =\n        db::get<::domain::Tags::ElementMap<Dim, Frame::Grid>>(box)(\n            logical_coords);\n    const auto inv_jacobian =\n        db::get<::domain::Tags::ElementMap<Dim, Frame::Grid>>(box).inv_jacobian(\n            logical_coords);\n\n    const Variables<neighbor_tags> ghost_zone_inverse_jacobian =\n        db::get<evolution::dg::subcell::Tags::GhostZoneInverseJacobian<Dim>>(\n            box)\n            .at(direction);\n    const auto test_grid_coords =\n        get<evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Grid>>(\n            ghost_zone_inverse_jacobian);\n    const auto test_inv_jacobian = get<\n        evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGrid<Dim>>(\n        ghost_zone_inverse_jacobian);\n\n    CHECK_ITERABLE_APPROX(grid_coords, test_grid_coords);\n    CHECK_ITERABLE_APPROX(inv_jacobian, test_inv_jacobian);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.FD.GhostZoneInverseJacobian\",\n                  \"[Parallel][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_GhostZoneLogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace evolution::dg::subcell::fd {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  // Create a subcell mesh\n  const Mesh<Dim> dg_mesh{3, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = mesh(dg_mesh);\n  const Index<Dim> subcell_extents = subcell_mesh.extents();\n\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    const size_t dim_direction = direction.dimension();\n\n    // iterate ghost zone size up to the maximum possible value (mesh extent to\n    // the direction)\n    for (size_t ghost_zone_size = 1;\n         ghost_zone_size <= subcell_extents[dim_direction]; ++ghost_zone_size) {\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical> ghost_zone_coords =\n          ghost_zone_logical_coordinates(subcell_mesh, ghost_zone_size,\n                                         direction);\n\n      // grid extents of ghost zone\n      Index<Dim> ghost_zone_extents{subcell_extents};\n      ghost_zone_extents[dim_direction] = ghost_zone_size;\n\n      // Check the computed ghost zone coords slice-by-slice.\n      // First we get the outermost slice of subcell (volume) logical\n      // coordinates to copy the coordinate components that remain same. i.e.\n      // For 3D if `direction` is along x-axis, y and z components would remain\n      // same as volume slice. (*)\n      auto expected_coordinates = slice_tensor_for_subcell(\n          logical_coordinates(subcell_mesh), subcell_extents, 1, direction, {});\n\n      for (size_t i_slice = 0; i_slice < ghost_zone_size; ++i_slice) {\n        auto ghost_zone_coords_ith_slice = data_on_slice(\n            ghost_zone_coords, ghost_zone_extents, dim_direction, i_slice);\n\n        // (*) Here we tweak the components that need to be shifted.\n        // - Since subcell mesh is cell-centered, it has logical coordinate\n        //   values [-0.8, -0.4, 0, 0.4, 0.8]. Depending on the `direction`,\n        //   we choose either -0.8 or 0.8 as a fiducial point.\n        // - Grid spacing for subcell mesh is 0.4\n        expected_coordinates.get(dim_direction) =\n            direction.side() == Side::Upper\n                ? 0.8 + 0.4 * (i_slice + 1)\n                : -0.8 - 0.4 * (ghost_zone_size - i_slice);\n\n        CHECK_ITERABLE_APPROX(expected_coordinates,\n                              ghost_zone_coords_ith_slice);\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Fd.GhostLogicalCoords\",\n                  \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n\n}  // namespace\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_InitialTciData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"Evolution/DgSubcell/InitialTciData.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/Tags/InitialTciData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  using Tag = Tags::InitialTciData<Dim>;\n  using Inbox = typename Tag::type;\n  Inbox inbox{};\n\n  const DirectionalId<Dim> id0{Direction<Dim>::lower_xi(), ElementId<Dim>{0}};\n  const DirectionalId<Dim> id1{Direction<Dim>::lower_xi(), ElementId<Dim>{1}};\n  const std::pair const_initial_tci_data{\n      id0, InitialTciData{{100}, {RdmpTciData{{1.0}, {-1.0}}}}};\n  Tag::insert_into_inbox(&inbox, 1, const_initial_tci_data);\n  std::pair initial_tci_data{\n      id1, InitialTciData{{200}, {RdmpTciData{{2.0}, {-2.0}}}}};\n  Tag::insert_into_inbox(&inbox, 1, std::move(initial_tci_data));\n\n  CHECK(inbox.at(1).at(id0).tci_status.value() == 100);\n  CHECK(inbox.at(1).at(id0).initial_rdmp_data.value() ==\n        RdmpTciData{{1.0}, {-1.0}});\n\n  CHECK(inbox.at(1).at(id1).tci_status.value() == 200);\n  CHECK(inbox.at(1).at(id1).initial_rdmp_data.value() ==\n        RdmpTciData{{2.0}, {-2.0}});\n\n  const std::string inbox_output = Tag::output_inbox(inbox, 1_st);\n  const std::string expected_inbox_output_v1 =\n      MakeString{} << \" InitialTciDataInbox:\\n\"\n                   << \"  Action number: 1\\n\"\n                   << \"   Key: \" << id0 << \", TCI: 100\\n\"\n                   << \"   Key: \" << id1 << \", TCI: 200\\n\";\n  const std::string expected_inbox_output_v2 =\n      MakeString{} << \" InitialTciDataInbox:\\n\"\n                   << \"  Action number: 1\\n\"\n                   << \"   Key: \" << id1 << \", TCI: 200\\n\"\n                   << \"   Key: \" << id0 << \", TCI: 100\\n\";\n  // The inner map between key and TCI is unordered so we have to check both\n  // possibilities\n  CHECK((inbox_output == expected_inbox_output_v1 or\n         inbox_output == expected_inbox_output_v2));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.InitialTciStatus\",\n                  \"[Evolution][Unit]\") {\n  {\n    const InitialTciData initial_tci_data{{}, {}};\n    const auto deserialize = serialize_and_deserialize(initial_tci_data);\n    CHECK_FALSE(deserialize.tci_status.has_value());\n    CHECK_FALSE(deserialize.initial_rdmp_data.has_value());\n  }\n  {\n    const InitialTciData initial_tci_data{{100}, {}};\n    const auto deserialize = serialize_and_deserialize(initial_tci_data);\n    CHECK(deserialize.tci_status.value() == 100);\n    CHECK_FALSE(deserialize.initial_rdmp_data.has_value());\n  }\n  {\n    const InitialTciData initial_tci_data{{}, {RdmpTciData{{1.0}, {-1.0}}}};\n    const auto deserialize = serialize_and_deserialize(initial_tci_data);\n    CHECK_FALSE(deserialize.tci_status.has_value());\n    CHECK(deserialize.initial_rdmp_data.value() == RdmpTciData{{1.0}, {-1.0}});\n  }\n  {\n    const InitialTciData initial_tci_data{{100}, {RdmpTciData{{1.0}, {-1.0}}}};\n    const auto deserialize = serialize_and_deserialize(initial_tci_data);\n    CHECK(deserialize.tci_status.value() == 100);\n    CHECK(deserialize.initial_rdmp_data.value() == RdmpTciData{{1.0}, {-1.0}});\n  }\n\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_JacobianCompute.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\nnamespace {\n\nvoid test() {\n  const ElementId<3> element_id{\n      0, {SegmentId{3, 4}, SegmentId{3, 4}, SegmentId{3, 7}}};\n\n  Block<3> block{\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{}),\n      0,\n      {}};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  ElementMap<3, Frame::Grid> element_map{\n      element_id, block.is_time_dependent()\n                      ? block.moving_mesh_logical_to_grid_map().get_clone()\n                      : block.stationary_map().get_to_grid_frame()};\n  const double time = 0.0;\n  const Mesh<3> subcell_mesh{2, Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n\n  using compute_tags = tmpl::list<\n      evolution::dg::subcell::Tags::LogicalCoordinatesCompute<3>,\n      ::domain::Tags::MappedCoordinates<\n          ::domain::Tags::ElementMap<3, Frame::Grid>,\n          evolution::dg::subcell::Tags::Coordinates<3, Frame::ElementLogical>,\n          evolution::dg::subcell::Tags::Coordinates>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGridCompute<\n          ::domain::Tags::ElementMap<3, Frame::Grid>, 3>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertialCompute<\n          ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                        Frame::Inertial>,\n          3>,\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToGridCompute<\n          3>,\n      evolution::dg::subcell::fd::Tags::\n          DetInverseJacobianLogicalToInertialCompute<\n              ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                            Frame::Inertial>,\n              3>>;\n#ifdef SPECTRE_AUTODIFF\n  using hessian_compute_tags = tmpl::list<\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToGridCompute<\n          ::domain::Tags::ElementMap<3, Frame::Grid>, 3>,\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertialCompute<\n          ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                        Frame::Inertial>,\n          3>>;\n#else\n  using hessian_compute_tags = tmpl::list<>;\n#endif  // SPECTRE_AUTODIFF\n\n  auto box = db::create<\n      db::AddSimpleTags<evolution::dg::subcell::Tags::Mesh<3>,\n                        domain::Tags::ElementMap<3, Frame::Grid>,\n                        domain::CoordinateMaps::Tags::CoordinateMap<\n                            3, Frame::Grid, Frame::Inertial>,\n                        ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize>,\n      db::AddComputeTags<tmpl::append<compute_tags, hessian_compute_tags>>>(\n      subcell_mesh, std::move(element_map),\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{}),\n      time, clone_unique_ptrs(functions_of_time));\n  TestHelpers::db::test_compute_tag<\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGridCompute<\n          ::domain::Tags::ElementMap<3, Frame::Grid>, 3>>(\n      \"InverseJacobian(Logical,Grid)\");\n  TestHelpers::db::test_compute_tag<\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToGridCompute<\n          3>>(\"Det(InverseJacobian(Logical,Grid))\");\n  TestHelpers::db::test_compute_tag<\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertialCompute<\n          ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                        Frame::Inertial>,\n          3>>(\"InverseJacobian(Logical,Inertial)\");\n  TestHelpers::db::test_compute_tag<\n      evolution::dg::subcell::fd::Tags::\n          DetInverseJacobianLogicalToInertialCompute<\n              ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                            Frame::Inertial>,\n              3>>(\"Det(InverseJacobian(Logical,Inertial))\");\n#ifdef SPECTRE_AUTODIFF\n  TestHelpers::db::test_compute_tag<\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToGridCompute<\n          ::domain::Tags::ElementMap<3, Frame::Grid>, 3>>(\n      \"InverseHessian(Logical,Grid)\");\n  TestHelpers::db::test_compute_tag<\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertialCompute<\n          ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                        Frame::Inertial>,\n          3>>(\"InverseHessian(Logical,Inertial)\");\n#endif  // SPECTRE_AUTODIFF\n\n  const auto& inv_jac_grid = db::get<\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGrid<3>>(box);\n  const auto& inv_jac_inertial = db::get<\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<3>>(\n      box);\n\n  // Check that the two jacobians in frames connected by an identity map are\n  // identical\n  for (size_t storage_index = 0; storage_index < inv_jac_inertial.size();\n       ++storage_index) {\n    CHECK_ITERABLE_APPROX(inv_jac_inertial[storage_index],\n                          inv_jac_grid[storage_index]);\n  }\n\n  const auto& det_inv_jac_grid = db::get<\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToGrid>(box);\n  const auto& det_inv_jac_inertial = db::get<\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToInertial>(\n      box);\n  CHECK_ITERABLE_APPROX(get(det_inv_jac_inertial), get(det_inv_jac_grid));\n\n#ifdef SPECTRE_AUTODIFF\n  const auto& inv_hess_grid =\n      db::get<evolution::dg::subcell::fd::Tags::InverseHessianLogicalToGrid<3>>(\n          box);\n  const auto& inv_hess_inertial = db::get<\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertial<3>>(\n      box);\n\n  // Check that the two hessians in frames connected by an identity map are\n  // identical\n  for (size_t storage_index = 0; storage_index < inv_hess_inertial.size();\n       ++storage_index) {\n    CHECK_ITERABLE_APPROX(inv_hess_inertial[storage_index],\n                          inv_hess_grid[storage_index]);\n  }\n#endif  // SPECTRE_AUTODIFF\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DgSubcell.JacobianCompute\",\n                  \"[Unit][Evolution]\") {\n  test();\n}\n}  // namespace\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_Matrices.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/Matrices.hpp\"\n#include \"Helpers/Evolution/DgSubcell/ProjectionTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace evolution::dg::subcell::fd {\nnamespace {\ntemplate <size_t MaxPts, size_t Dim, Spectral::Basis BasisType,\n          Spectral::Quadrature QuadratureType>\nvoid test_projection_matrix() {\n  CAPTURE(Dim);\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n\n  for (size_t num_pts_1d = std::max(\n           static_cast<size_t>(2),\n           Spectral::minimum_number_of_points<BasisType, QuadratureType>);\n       num_pts_1d < MaxPts + 1; ++num_pts_1d) {\n    CAPTURE(num_pts_1d);\n    const Mesh<Dim> dg_mesh{num_pts_1d, BasisType, QuadratureType};\n    const auto logical_coords = logical_coordinates(dg_mesh);\n    const size_t num_subcells_1d = 2 * num_pts_1d - 1;\n    CAPTURE(num_subcells_1d);\n    const Mesh<Dim> subcell_mesh(num_subcells_1d,\n                                 Spectral::Basis::FiniteDifference,\n                                 Spectral::Quadrature::CellCentered);\n    const size_t num_subcells = subcell_mesh.number_of_grid_points();\n    const DataVector nodal_coeffs =\n        TestHelpers::evolution::dg::subcell::cell_values(dg_mesh.extents(0) - 2,\n                                                         logical_coords);\n\n    Matrix empty{};\n    auto projection_mat = make_array<Dim>(std::cref(empty));\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(projection_mat, d) = std::cref(\n          projection_matrix(dg_mesh.slice_through(d), subcell_mesh.extents()[d],\n                            Spectral::Quadrature::CellCentered));\n    }\n    DataVector cell_centered_values(num_subcells, 0.0);\n    apply_matrices(make_not_null(&cell_centered_values), projection_mat,\n                   nodal_coeffs, dg_mesh.extents());\n\n    const DataVector expected_values =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            dg_mesh.extents(0) - 2, logical_coordinates(subcell_mesh));\n    CHECK_ITERABLE_APPROX(cell_centered_values, expected_values);\n\n    if constexpr (Dim == 1) {\n      // Check projecting ghost cells. Only do in 1d since the test becomes\n      // rather error-prone and tedious in higher dimensions, and the operation\n      // is dim-by-dim handled by apply_matrices.\n      for (size_t ghost_points = 2;\n           num_subcells_1d > 4 and\n           ghost_points <= std::min(5_st, num_subcells_1d - 2);\n           ++ghost_points) {\n        CAPTURE(ghost_points);\n        for (const Side side : {Side::Lower, Side::Upper}) {\n          CAPTURE(side);\n          CAPTURE(expected_values);\n          DataVector expected_ghost_values(ghost_points);\n          for (size_t i = 0; i < ghost_points; ++i) {\n            expected_ghost_values[i] =\n                expected_values[side == Side::Lower\n                                    ? i\n                                    : (num_subcells_1d - ghost_points + i)];\n          }\n          DataVector ghost_cell_centered_values(ghost_points, 0.0);\n          auto ghost_projection_mat = make_array<Dim>(std::cref(empty));\n          ghost_projection_mat[0] = std::cref(projection_matrix(\n              dg_mesh, subcell_mesh.extents(0), ghost_points, side));\n          apply_matrices(make_not_null(&ghost_cell_centered_values),\n                         ghost_projection_mat, nodal_coeffs, dg_mesh.extents());\n          CHECK_ITERABLE_APPROX(ghost_cell_centered_values,\n                                expected_ghost_values);\n        }\n      }\n    }\n  }\n#ifdef SPECTRE_DEBUG\n  if constexpr (Dim == 1) {\n    CHECK_THROWS_WITH(\n        projection_matrix(Mesh<1>{3, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto},\n                          5, 1, Side::Lower),\n        Catch::Matchers::ContainsSubstring(\"ghost_zone_size must be\"));\n    CHECK_THROWS_WITH(\n        projection_matrix(Mesh<1>{3, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto},\n                          5, 6, Side::Lower),\n        Catch::Matchers::ContainsSubstring(\"ghost_zone_size must be\"));\n    CHECK_THROWS_WITH(\n        projection_matrix(Mesh<1>{3, Spectral::Basis::Chebyshev,\n                                  Spectral::Quadrature::GaussLobatto},\n                          5, 1, Side::Lower),\n        Catch::Matchers::ContainsSubstring(\n            \"FD Subcell projection only supports Legendre basis\"));\n  }\n#endif\n}\n\ntemplate <size_t MaxPts, size_t Dim, size_t Face_Dim, Spectral::Basis BasisType,\n          Spectral::Quadrature QuadratureType>\nvoid test_projection_matrix_to_face() {\n  CAPTURE(Dim);\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n\n  for (size_t num_pts_1d = std::max(\n           static_cast<size_t>(2),\n           Spectral::minimum_number_of_points<BasisType, QuadratureType>);\n       num_pts_1d < MaxPts + 1; ++num_pts_1d) {\n    CAPTURE(num_pts_1d);\n    const Mesh<Dim> dg_mesh{num_pts_1d, BasisType, QuadratureType};\n    const auto logical_coords = logical_coordinates(dg_mesh);\n    const size_t num_subcells_1d_face = 2 * num_pts_1d;\n    const size_t num_subcells_1d_cell = 2 * num_pts_1d - 1;\n    CAPTURE(num_subcells_1d_face);\n    CAPTURE(num_subcells_1d_cell);\n\n    std::array<size_t, Dim> extents{};\n    std::array<Spectral::Basis, Dim> basis{};\n    std::array<Spectral::Quadrature, Dim> quadrature{};\n    for (size_t d = 0; d < Dim; d++) {\n      basis[d] = Spectral::Basis::FiniteDifference;\n      if (d == Face_Dim) {\n        extents[d] = num_subcells_1d_face;\n        quadrature[d] = Spectral::Quadrature::FaceCentered;\n      } else {\n        extents[d] = num_subcells_1d_cell;\n        quadrature[d] = Spectral::Quadrature::CellCentered;\n      }\n    }\n\n    const Mesh<Dim> subcell_mesh(extents, basis, quadrature);\n    const size_t num_subcells = subcell_mesh.number_of_grid_points();\n    const DataVector nodal_coeffs =\n        TestHelpers::evolution::dg::subcell::cell_values(dg_mesh.extents(0) - 2,\n                                                         logical_coords);\n\n    Matrix empty{};\n    auto projection_mat = make_array<Dim>(std::cref(empty));\n    for (size_t d = 0; d < Dim; ++d) {\n      if (d == Face_Dim) {\n        gsl::at(projection_mat, d) = std::cref(projection_matrix(\n            dg_mesh.slice_through(d), subcell_mesh.extents()[d],\n            Spectral::Quadrature::FaceCentered));\n      } else {\n        gsl::at(projection_mat, d) = std::cref(projection_matrix(\n            dg_mesh.slice_through(d), subcell_mesh.extents()[d],\n            Spectral::Quadrature::CellCentered));\n      }\n    }\n    DataVector subcell_values(num_subcells, 0.0);\n    apply_matrices(make_not_null(&subcell_values), projection_mat, nodal_coeffs,\n                   dg_mesh.extents());\n\n    const DataVector expected_values =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            dg_mesh.extents(0) - 2, logical_coordinates(subcell_mesh));\n    CHECK_ITERABLE_APPROX(subcell_values, expected_values);\n  }\n}\n\ntemplate <size_t MaxPts, size_t Dim, Spectral::Basis BasisType,\n          Spectral::Quadrature QuadratureType>\nvoid reconstruction_matrix(const double eps) {\n  CAPTURE(Dim);\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n  Approx local_approx = Approx::custom().epsilon(eps).scale(1.);\n\n  for (size_t num_pts_1d = std::max(\n           static_cast<size_t>(2),\n           Spectral::minimum_number_of_points<BasisType, QuadratureType>);\n       num_pts_1d < MaxPts + 1; ++num_pts_1d) {\n    CAPTURE(MaxPts);\n    CAPTURE(num_pts_1d);\n    const Mesh<Dim> dg_mesh{num_pts_1d, BasisType, QuadratureType};\n    const size_t num_pts = dg_mesh.number_of_grid_points();\n    const auto logical_coords = logical_coordinates(dg_mesh);\n    const size_t num_subcells_1d = 2 * num_pts_1d - 1;\n    const Mesh<Dim> subcell_mesh(num_subcells_1d,\n                                 Spectral::Basis::FiniteDifference,\n                                 Spectral::Quadrature::CellCentered);\n    // Our FD reconstruction scheme can integrate polynomials up to degree 6\n    // exactly. However, we want to verify that if we have more than 8 grid\n    // points on the DG grid that we still are able to recover the correct\n    // solution.\n    const DataVector expected_nodal_coeffs =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            std::min(dg_mesh.extents(0) - 2, 6_st), logical_coords);\n    const DataVector subcell_values =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            std::min(dg_mesh.extents(0) - 2, 6_st),\n            logical_coordinates(subcell_mesh));\n\n    const Matrix& single_recons =\n        subcell::fd::reconstruction_matrix(dg_mesh, subcell_mesh.extents());\n\n    DataVector reconstructed_nodal_coeffs(num_pts);\n    dgemv_('N', single_recons.rows(), single_recons.columns(), 1.0,\n           single_recons.data(), single_recons.spacing(), subcell_values.data(),\n           1, 0.0, reconstructed_nodal_coeffs.data(), 1);\n\n    CHECK_ITERABLE_CUSTOM_APPROX(expected_nodal_coeffs,\n                                 reconstructed_nodal_coeffs, local_approx);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Fd.ProjectionMatrix\",\n                  \"[Evolution][Unit]\") {\n  test_projection_matrix<10, 1, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto>();\n  test_projection_matrix<10, 1, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::Gauss>();\n\n  test_projection_matrix<10, 2, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto>();\n  test_projection_matrix<10, 2, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::Gauss>();\n\n  test_projection_matrix<5, 3, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto>();\n  test_projection_matrix<5, 3, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::Gauss>();\n  test_projection_matrix_to_face<10, 1, 0, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto>();\n  test_projection_matrix_to_face<10, 1, 0, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::Gauss>();\n  test_projection_matrix_to_face<5, 3, 0, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto>();\n  test_projection_matrix_to_face<5, 3, 0, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::Gauss>();\n  test_projection_matrix_to_face<5, 3, 1, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto>();\n  test_projection_matrix_to_face<5, 3, 1, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::Gauss>();\n  test_projection_matrix_to_face<5, 3, 2, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto>();\n  test_projection_matrix_to_face<5, 3, 2, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::Gauss>();\n}\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Fd.ReconstructionMatrix\",\n                  \"[Evolution][Unit]\") {\n  // Timeout is increased slightly so we can test the 3d 5 points per dim case.\n  // Normally the test completes in less than 2 seconds on debug builds.\n  // However, if ASAN is on, this time roughly doubles and we want to avoid\n  // timeouts there.\n  reconstruction_matrix<10, 1, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto>(1.0e-13);\n  reconstruction_matrix<10, 1, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::Gauss>(1.0e-13);\n\n  reconstruction_matrix<10, 2, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto>(1.0e-10);\n  reconstruction_matrix<10, 2, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::Gauss>(1.0e-10);\n\n  reconstruction_matrix<5, 3, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto>(1.0e-11);\n  reconstruction_matrix<4, 3, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::Gauss>(1.0e-11);\n}\n}  // namespace\n}  // namespace evolution::dg::subcell::fd\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_Mesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <sstream>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\nvoid print_comparison_point_computation() {\n  // Prints the number of grid points for FD calculated either as:\n  //   1. (2.0 / dg_min_spacing), effectively matching the grid spacing\n  //   2. (2 * num_dg_points - 1), which assumes the time stepper order\n  //      matches the spatial order. This assumption is usually wrong about\n  //      5th or 6th order in space.\n  //\n  // DG points | (2.0 / dg_min_spacing) | (2 * num_dg_points - 1)\n  //  5        |       5.79129          |     9\n  //  6        |       8.51264          |     11\n  //  7        |       11.7802          |     13\n  //  8        |       15.5933          |     15\n  //  9        |       19.9517          |     17\n  //  10       |       24.8552          |     19\n  //  11       |       30.3037          |     21\n  //  12       |       36.2972          |     23\n  //  13       |       42.8356          |     25\n  //  14       |       49.9189          |     27\n  //  15       |       57.5472          |     29\n  //\n  // Clearly at high-order DG we could have way more FD grid points to match\n  // the spatial grid spacing. It's not clear to Nils D. whether the right\n  // thing to do is to always match the DG grid spacing, or transition from\n  // one method to the other at some point.\n  std::stringstream ss;\n  for (size_t i = 5; i < 16; ++i) {\n    const Mesh<1> dg_mesh{i, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n    const DataVector& collocation_pts = Spectral::collocation_points(dg_mesh);\n    ss << dg_mesh.extents(0) << ' '\n       << 2.0 / std::abs(collocation_pts[1] - collocation_pts[0]) << \" \"\n       << (2 * dg_mesh.extents(0) - 1) << '\\n';\n  }\n  const std::string str = ss.str();\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)\n  std::printf(\"%s\\n\", str.c_str());\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nvoid test_mesh() {\n  constexpr size_t min_num_pts =\n      Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n  constexpr size_t max_num_pts = Spectral::maximum_number_of_points<BasisType>;\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::fd::dg_mesh(\n          Mesh<1>{2 * (max_num_pts - 1) - 1, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::CellCentered},\n          BasisType, QuadratureType),\n      Catch::Matchers::ContainsSubstring(\"The basis for computing the DG mesh \"\n                                         \"must be FiniteDifference but got \"));\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::fd::dg_mesh(\n          Mesh<1>{2 * (max_num_pts - 1) - 1, Spectral::Basis::FiniteDifference,\n                  Spectral::Quadrature::FaceCentered},\n          BasisType, QuadratureType),\n      Catch::Matchers::ContainsSubstring(\"The quadrature for computing the DG \"\n                                         \"mesh must be CellCentered but got \"));\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::fd::dg_mesh(\n          Mesh<1>{2 * (max_num_pts - 1) - 1, Spectral::Basis::FiniteDifference,\n                  Spectral::Quadrature::CellCentered},\n          Spectral::Basis::FiniteDifference, QuadratureType),\n      Catch::Matchers::ContainsSubstring(\n          \"The DG basis must be Legendre or Chebyshev but got \"));\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::fd::dg_mesh(\n          Mesh<1>{2 * (max_num_pts - 1) - 1, Spectral::Basis::FiniteDifference,\n                  Spectral::Quadrature::CellCentered},\n          BasisType, Spectral::Quadrature::FaceCentered),\n      Catch::Matchers::ContainsSubstring(\n          \"The DG quadrature for computing the DG mesh must be Gauss or \"\n          \"GaussLobatto but \"));\n#endif // SPECTRE_DEBUG\n\n  for (size_t i = min_num_pts; i < max_num_pts; ++i) {\n    CHECK(evolution::dg::subcell::fd::mesh(\n              Mesh<1>(i, BasisType, QuadratureType)) ==\n          Mesh<1>{2 * i - 1, Spectral::Basis::FiniteDifference,\n                  Spectral::Quadrature::CellCentered});\n    CHECK(evolution::dg::subcell::fd::mesh(\n              Mesh<2>(i, BasisType, QuadratureType)) ==\n          Mesh<2>{2 * i - 1, Spectral::Basis::FiniteDifference,\n                  Spectral::Quadrature::CellCentered});\n    CHECK(evolution::dg::subcell::fd::mesh(\n              Mesh<3>(i, BasisType, QuadratureType)) ==\n          Mesh<3>{2 * i - 1, Spectral::Basis::FiniteDifference,\n                  Spectral::Quadrature::CellCentered});\n\n    CHECK(evolution::dg::subcell::fd::dg_mesh(\n              Mesh<1>{2 * i - 1, Spectral::Basis::FiniteDifference,\n                      Spectral::Quadrature::CellCentered},\n              BasisType,\n              QuadratureType) == Mesh<1>(i, BasisType, QuadratureType));\n    CHECK(evolution::dg::subcell::fd::dg_mesh(\n              Mesh<2>{2 * i - 1, Spectral::Basis::FiniteDifference,\n                      Spectral::Quadrature::CellCentered},\n              BasisType,\n              QuadratureType) == Mesh<2>(i, BasisType, QuadratureType));\n    CHECK(evolution::dg::subcell::fd::dg_mesh(\n              Mesh<3>{2 * i - 1, Spectral::Basis::FiniteDifference,\n                      Spectral::Quadrature::CellCentered},\n              BasisType,\n              QuadratureType) == Mesh<3>(i, BasisType, QuadratureType));\n  }\n  CHECK(evolution::dg::subcell::fd::mesh(\n            Mesh<2>({{4, 6}}, BasisType, QuadratureType)) ==\n        Mesh<2>{{{7, 11}},\n                Spectral::Basis::FiniteDifference,\n                Spectral::Quadrature::CellCentered});\n  CHECK(evolution::dg::subcell::fd::mesh(\n            Mesh<3>({{4, 6, 7}}, BasisType, QuadratureType)) ==\n        Mesh<3>{{{7, 11, 13}},\n                Spectral::Basis::FiniteDifference,\n                Spectral::Quadrature::CellCentered});\n\n  CHECK(evolution::dg::subcell::fd::dg_mesh(\n            Mesh<2>{{{7, 11}},\n                    Spectral::Basis::FiniteDifference,\n                    Spectral::Quadrature::CellCentered},\n            BasisType,\n            QuadratureType) == Mesh<2>({{4, 6}}, BasisType, QuadratureType));\n  CHECK(evolution::dg::subcell::fd::dg_mesh(\n            Mesh<3>{{{7, 11, 13}},\n                    Spectral::Basis::FiniteDifference,\n                    Spectral::Quadrature::CellCentered},\n            BasisType,\n            QuadratureType) == Mesh<3>({{4, 6, 7}}, BasisType, QuadratureType));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.FD.Mesh\", \"[Evolution][Unit]\") {\n  test_mesh<Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>();\n  test_mesh<Spectral::Basis::Legendre, Spectral::Quadrature::Gauss>();\n  test_mesh<Spectral::Basis::Chebyshev, Spectral::Quadrature::GaussLobatto>();\n  test_mesh<Spectral::Basis::Chebyshev, Spectral::Quadrature::Gauss>();\n  print_comparison_point_computation();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_MeshForGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/DgSubcell/Tags/MeshForGhostData.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Tags.MeshForGhostData\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<\n      evolution::dg::subcell::Tags::MeshForGhostData<1>>(\"MeshForGhostData\");\n  TestHelpers::db::test_simple_tag<\n      evolution::dg::subcell::Tags::MeshForGhostData<2>>(\"MeshForGhostData\");\n  TestHelpers::db::test_simple_tag<\n      evolution::dg::subcell::Tags::MeshForGhostData<3>>(\"MeshForGhostData\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_NeighborRdmpAndVolumeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Matrices.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/NeighborRdmpAndVolumeData.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  using Interps = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n  // Have upper xi neighbor do DG and lower xi neighbor do FD. For eta do\n  // reverse, and zeta do same as xi.\n  const Mesh<Dim> dg_mesh{6, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const auto logical_fd_coords = logical_coordinates(subcell_mesh);\n  const size_t number_of_rdmp_vars = 2;\n  const size_t number_of_ghost_zones = 3;  // 5th order method\n\n  const DirectionalId<Dim> upper_xi_id{Direction<Dim>::upper_xi(),\n                                       ElementId<Dim>{1}};\n  const DirectionalId<Dim> lower_xi_id{Direction<Dim>::lower_xi(),\n                                       ElementId<Dim>{2}};\n\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  // aligned with neighbor so default construct\n  neighbors.insert(\n      std::pair{Direction<Dim>::upper_xi(),\n                Neighbors<Dim>{{upper_xi_id.id()},\n                               OrientationMap<Dim>::create_aligned()}});\n  if constexpr (Dim == 1) {\n    neighbors.insert(std::pair{\n        Direction<Dim>::lower_xi(),\n        Neighbors<Dim>{{lower_xi_id.id()},\n                       OrientationMap<Dim>{{{Direction<Dim>::lower_xi()}}}}});\n  } else if constexpr (Dim == 2) {\n    neighbors.insert(std::pair{\n        Direction<Dim>::lower_xi(),\n        Neighbors<Dim>{{lower_xi_id.id()},\n                       OrientationMap<Dim>{{{Direction<Dim>::lower_xi(),\n                                             Direction<Dim>::lower_eta()}}}}});\n    neighbors.insert(\n        std::pair{Direction<Dim>::upper_eta(),\n                  Neighbors<Dim>{{ElementId<Dim>{3}},\n                                 OrientationMap<Dim>::create_aligned()}});\n  } else if constexpr (Dim == 3) {\n    neighbors.insert(std::pair{\n        Direction<Dim>::lower_xi(),\n        Neighbors<Dim>{{lower_xi_id.id()},\n                       OrientationMap<Dim>{{{Direction<Dim>::lower_xi(),\n                                             Direction<Dim>::lower_eta(),\n                                             Direction<Dim>::upper_zeta()}}}}});\n    neighbors.insert(\n        std::pair{Direction<Dim>::upper_eta(),\n                  Neighbors<Dim>{{ElementId<Dim>{3}},\n                                 OrientationMap<Dim>::create_aligned()}});\n\n    neighbors.insert(std::pair{\n        Direction<Dim>::lower_zeta(),\n        Neighbors<Dim>{{ElementId<Dim>{4}},\n                       OrientationMap<Dim>{{{Direction<Dim>::lower_xi(),\n                                             Direction<Dim>::lower_eta(),\n                                             Direction<Dim>::upper_zeta()}}}}});\n    neighbors.insert(\n        std::pair{Direction<Dim>::upper_zeta(),\n                  Neighbors<Dim>{{ElementId<Dim>{5}},\n                                 OrientationMap<Dim>::create_aligned()}});\n  }\n  Interps neighbor_dg_to_fd_interpolants{};\n  {\n    const auto& orientation_map =\n        neighbors.at(lower_xi_id.direction()).orientation(lower_xi_id.id());\n    tnsr::I<DataVector, Dim, Frame::ElementLogical> oriented_logical_coords{};\n    for (size_t i = 0; i < Dim; ++i) {\n      oriented_logical_coords.get(i) = orient_variables(\n          logical_fd_coords.get(i), subcell_mesh.extents(), orientation_map);\n    }\n    const auto target_points = evolution::dg::subcell::slice_tensor_for_subcell(\n        oriented_logical_coords, subcell_mesh.extents(), number_of_ghost_zones,\n        orientation_map(lower_xi_id.direction()), {});\n    neighbor_dg_to_fd_interpolants[lower_xi_id] =\n        intrp::Irregular<Dim>{dg_mesh, target_points};\n  }\n  if constexpr (Dim == 3) {\n    const DirectionalId<Dim> lower_zeta_id{Direction<Dim>::lower_zeta(),\n                                           ElementId<Dim>{4}};\n    const auto& orientation_map =\n        neighbors.at(lower_zeta_id.direction()).orientation(lower_zeta_id.id());\n    tnsr::I<DataVector, Dim, Frame::ElementLogical> oriented_logical_coords{};\n    for (size_t i = 0; i < Dim; ++i) {\n      oriented_logical_coords.get(i) = orient_variables(\n          logical_fd_coords.get(i), subcell_mesh.extents(), orientation_map);\n    }\n    const auto target_points = evolution::dg::subcell::slice_tensor_for_subcell(\n        oriented_logical_coords, subcell_mesh.extents(), number_of_ghost_zones,\n        orientation_map(lower_zeta_id.direction()).opposite(), {});\n    neighbor_dg_to_fd_interpolants[lower_zeta_id] =\n        intrp::Irregular<Dim>{dg_mesh, target_points};\n  }\n\n  DataVector received_fd_data{subcell_mesh.number_of_grid_points() +\n                              2 * number_of_rdmp_vars};\n  alg::iota(received_fd_data, 0.0);\n  DataVector received_dg_data{dg_mesh.number_of_grid_points() +\n                              2 * number_of_rdmp_vars};\n  alg::iota(received_dg_data, *std::prev(received_fd_data.end()) + 1.0);\n\n  const Element<Dim> element{ElementId<Dim>{0}, neighbors};\n\n  DataVector expected_neighbor_data_from_upper_xi{received_fd_data.size() -\n                                                  2 * number_of_rdmp_vars};\n  std::copy(received_fd_data.begin(),\n            std::prev(received_fd_data.end(), 2 * number_of_rdmp_vars),\n            expected_neighbor_data_from_upper_xi.begin());\n  const DataVector expected_neighbor_data_from_lower_xi = [&dg_mesh, &neighbors,\n                                                           &number_of_rdmp_vars,\n                                                           &received_dg_data,\n                                                           &subcell_mesh,\n                                                           &lower_xi_id]() {\n    (void)number_of_rdmp_vars;  // workaround clang bug unused warning\n    // Need the view so the size is correct\n    const DataVector view_received_data(\n        received_dg_data.data(),\n        received_dg_data.size() - 2 * number_of_rdmp_vars);\n    DataVector oriented_data{view_received_data.size()};\n    orient_variables(make_not_null(&oriented_data), view_received_data,\n                     dg_mesh.extents(),\n                     neighbors.at(Direction<Dim>::lower_xi())\n                         .orientation(lower_xi_id.id())\n                         .inverse_map());\n    // We've now got the data in the local orientation, so now we need to\n    // project it to the ghost cells.\n    // Note: assume isotropic meshes\n    auto projection_matrices =\n        make_array<Dim>(std::cref(evolution::dg::subcell::fd::projection_matrix(\n            dg_mesh.slice_through(0), subcell_mesh.extents(0),\n            Spectral::Quadrature::CellCentered)));\n    projection_matrices[0] =\n        std::cref(evolution::dg::subcell::fd::projection_matrix(\n            dg_mesh.slice_through(0), subcell_mesh.extents(0),\n            number_of_ghost_zones, Side::Upper));\n\n    DataVector expected_data{subcell_mesh.extents().slice_away(0).product() *\n                             number_of_ghost_zones};\n    apply_matrices(make_not_null(&expected_data), projection_matrices,\n                   oriented_data, dg_mesh.extents());\n    return expected_data;\n  }();\n\n  DirectionalIdMap<Dim, evolution::dg::subcell::GhostData> neighbor_data{};\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{\n      DataVector{std::numeric_limits<double>::min(),\n                 std::numeric_limits<double>::min()},\n      DataVector{std::numeric_limits<double>::max(),\n                 std::numeric_limits<double>::max()}};\n  // Do upper-xi neighbor first. This is just aligned FD\n  evolution::dg::subcell::insert_neighbor_rdmp_and_volume_data(\n      make_not_null(&rdmp_tci_data), make_not_null(&neighbor_data),\n      received_fd_data, number_of_rdmp_vars, upper_xi_id,\n      subcell_mesh,  // neighbor mesh is the same as my mesh since both are\n                     // doing subcell\n      element, subcell_mesh, number_of_ghost_zones,\n      neighbor_dg_to_fd_interpolants);\n\n  const auto get_neighbor_data =\n      [&neighbor_data](const auto mortar_id) -> const DataVector& {\n    return neighbor_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n  };\n  {\n    DataVector expected_max_rdmp_tci_data{number_of_rdmp_vars};\n    DataVector expected_min_rdmp_tci_data{number_of_rdmp_vars};\n    std::copy(std::prev(received_fd_data.end(), 2 * number_of_rdmp_vars),\n              std::prev(received_fd_data.end(), number_of_rdmp_vars),\n              expected_max_rdmp_tci_data.begin());\n    std::copy(std::prev(received_fd_data.end(), number_of_rdmp_vars),\n              received_fd_data.end(), expected_min_rdmp_tci_data.begin());\n    CHECK(rdmp_tci_data.max_variables_values == expected_max_rdmp_tci_data);\n    CHECK(rdmp_tci_data.min_variables_values == expected_min_rdmp_tci_data);\n\n    REQUIRE(neighbor_data.size() == 1);\n    REQUIRE(neighbor_data.find(upper_xi_id) != neighbor_data.end());\n    CHECK(get_neighbor_data(upper_xi_id) ==\n          expected_neighbor_data_from_upper_xi);\n  }\n\n  // Do lower-xi neighbor. This is unaligned DG.\n  evolution::dg::subcell::insert_neighbor_rdmp_and_volume_data(\n      make_not_null(&rdmp_tci_data), make_not_null(&neighbor_data),\n      received_dg_data, number_of_rdmp_vars, lower_xi_id, dg_mesh, element,\n      subcell_mesh, number_of_ghost_zones, neighbor_dg_to_fd_interpolants);\n\n  {\n    DataVector expected_max_rdmp_tci_data{number_of_rdmp_vars};\n    DataVector expected_min_rdmp_tci_data{number_of_rdmp_vars};\n    std::copy(std::prev(received_dg_data.end(), 2 * number_of_rdmp_vars),\n              std::prev(received_dg_data.end(), number_of_rdmp_vars),\n              expected_max_rdmp_tci_data.begin());\n    std::copy(std::prev(received_fd_data.end(), number_of_rdmp_vars),\n              received_fd_data.end(), expected_min_rdmp_tci_data.begin());\n    CHECK(rdmp_tci_data.max_variables_values == expected_max_rdmp_tci_data);\n    CHECK(rdmp_tci_data.min_variables_values == expected_min_rdmp_tci_data);\n\n    REQUIRE(neighbor_data.size() == 2);\n    REQUIRE(neighbor_data.find(upper_xi_id) != neighbor_data.end());\n    REQUIRE(neighbor_data.find(lower_xi_id) != neighbor_data.end());\n    CHECK(get_neighbor_data(upper_xi_id) ==\n          expected_neighbor_data_from_upper_xi);\n    CHECK_ITERABLE_APPROX(get_neighbor_data(lower_xi_id),\n                          expected_neighbor_data_from_lower_xi);\n  }\n\n  if constexpr (Dim > 1) {\n    // Do upper-eta neighbor. This is aligned DG.\n    const DirectionalId<Dim> upper_eta_id{Direction<Dim>::upper_eta(),\n                                          ElementId<Dim>{3}};\n\n    DataVector aligned_received_dg_data{dg_mesh.number_of_grid_points() +\n                                        2 * number_of_rdmp_vars};\n    alg::iota(aligned_received_dg_data,\n              *std::prev(received_dg_data.end()) + 1.0);\n    evolution::dg::subcell::insert_neighbor_rdmp_and_volume_data(\n        make_not_null(&rdmp_tci_data), make_not_null(&neighbor_data),\n        aligned_received_dg_data, number_of_rdmp_vars, upper_eta_id, dg_mesh,\n        element, subcell_mesh, number_of_ghost_zones, {});\n\n    DataVector expected_max_rdmp_tci_data{number_of_rdmp_vars};\n    DataVector expected_min_rdmp_tci_data{number_of_rdmp_vars};\n    std::copy(\n        std::prev(aligned_received_dg_data.end(), 2 * number_of_rdmp_vars),\n        std::prev(aligned_received_dg_data.end(), number_of_rdmp_vars),\n        expected_max_rdmp_tci_data.begin());\n    std::copy(std::prev(received_fd_data.end(), number_of_rdmp_vars),\n              received_fd_data.end(), expected_min_rdmp_tci_data.begin());\n    CHECK(rdmp_tci_data.max_variables_values == expected_max_rdmp_tci_data);\n    CHECK(rdmp_tci_data.min_variables_values == expected_min_rdmp_tci_data);\n\n    REQUIRE(neighbor_data.size() == 3);\n    REQUIRE(neighbor_data.find(upper_xi_id) != neighbor_data.end());\n    REQUIRE(neighbor_data.find(lower_xi_id) != neighbor_data.end());\n    REQUIRE(neighbor_data.find(upper_eta_id) != neighbor_data.end());\n    CHECK(get_neighbor_data(upper_xi_id) ==\n          expected_neighbor_data_from_upper_xi);\n    CHECK_ITERABLE_APPROX(get_neighbor_data(lower_xi_id),\n                          expected_neighbor_data_from_lower_xi);\n\n    auto projection_matrices =\n        make_array<Dim>(std::cref(evolution::dg::subcell::fd::projection_matrix(\n            dg_mesh.slice_through(0), subcell_mesh.extents(0),\n            Spectral::Quadrature::CellCentered)));\n    projection_matrices[1] =\n        std::cref(evolution::dg::subcell::fd::projection_matrix(\n            dg_mesh.slice_through(0), subcell_mesh.extents(0),\n            number_of_ghost_zones, Side::Lower));\n\n    DataVector view_aligned_received_dg_data(aligned_received_dg_data.data(),\n                                             dg_mesh.number_of_grid_points());\n    DataVector expected_data{subcell_mesh.extents().slice_away(0).product() *\n                             number_of_ghost_zones};\n    apply_matrices(make_not_null(&expected_data), projection_matrices,\n                   view_aligned_received_dg_data, dg_mesh.extents());\n    CHECK(get_neighbor_data(upper_eta_id) == expected_data);\n  }\n\n  {\n    // Check that not inserting but updating in a direction that already has FD\n    // data does nothing. That is, even the pointer should stay the same.\n    const double* expected_pointer = get_neighbor_data(lower_xi_id).data();\n    evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n        make_not_null(&neighbor_data), get_neighbor_data(lower_xi_id),\n        number_of_rdmp_vars, lower_xi_id, subcell_mesh, element, subcell_mesh,\n        number_of_ghost_zones, neighbor_dg_to_fd_interpolants);\n    CHECK(get_neighbor_data(lower_xi_id).data() == expected_pointer);\n  }\n\n  if constexpr (Dim > 2) {\n    // Check that a neighbor being aligned DG and unaligned DG both work when\n    // not inserting.\n\n    // Do upper-zeta neighbor. This is aligned DG.\n    const DirectionalId<Dim> upper_zeta_id{Direction<Dim>::upper_zeta(),\n                                           ElementId<Dim>{5}};\n    DataVector aligned_received_dg_data{dg_mesh.number_of_grid_points()};\n    alg::iota(aligned_received_dg_data,\n              *std::prev(received_dg_data.end()) + 1.0);\n    neighbor_data[upper_zeta_id] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[upper_zeta_id].neighbor_ghost_data_for_reconstruction() =\n        aligned_received_dg_data;\n    evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n        make_not_null(&neighbor_data), get_neighbor_data(upper_zeta_id), 0,\n        upper_zeta_id, dg_mesh, element, subcell_mesh, number_of_ghost_zones,\n        neighbor_dg_to_fd_interpolants);\n\n    auto projection_matrices =\n        make_array<Dim>(std::cref(evolution::dg::subcell::fd::projection_matrix(\n            dg_mesh.slice_through(0), subcell_mesh.extents(0),\n            Spectral::Quadrature::CellCentered)));\n    projection_matrices[2] =\n        std::cref(evolution::dg::subcell::fd::projection_matrix(\n            dg_mesh.slice_through(0), subcell_mesh.extents(0),\n            number_of_ghost_zones, Side::Lower));\n\n    DataVector expected_data{subcell_mesh.extents().slice_away(0).product() *\n                             number_of_ghost_zones};\n    apply_matrices(make_not_null(&expected_data), projection_matrices,\n                   aligned_received_dg_data, dg_mesh.extents());\n    CHECK(get_neighbor_data(upper_zeta_id) == expected_data);\n\n    // Do lower-zeta neighbor. This is unaligned DG.\n    const DirectionalId<Dim> lower_zeta_id{Direction<Dim>::lower_zeta(),\n                                           ElementId<Dim>{4}};\n    DataVector unaligned_received_dg_data{dg_mesh.number_of_grid_points()};\n    alg::iota(unaligned_received_dg_data,\n              *std::prev(aligned_received_dg_data.end()) + 1.0);\n    neighbor_data[lower_zeta_id] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[lower_zeta_id].neighbor_ghost_data_for_reconstruction() =\n        unaligned_received_dg_data;\n\n    evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n        make_not_null(&neighbor_data), get_neighbor_data(lower_zeta_id), 0,\n        lower_zeta_id, dg_mesh, element, subcell_mesh, number_of_ghost_zones,\n        neighbor_dg_to_fd_interpolants);\n\n    projection_matrices[2] =\n        std::cref(evolution::dg::subcell::fd::projection_matrix(\n            dg_mesh.slice_through(0), subcell_mesh.extents(0),\n            number_of_ghost_zones, Side::Upper));\n\n    DataVector oriented_data{unaligned_received_dg_data.size()};\n    orient_variables(make_not_null(&oriented_data), unaligned_received_dg_data,\n                     dg_mesh.extents(),\n                     neighbors.at(Direction<Dim>::lower_zeta())\n                         .orientation(lower_zeta_id.id())\n                         .inverse_map());\n    apply_matrices(make_not_null(&expected_data), projection_matrices,\n                   oriented_data, dg_mesh.extents());\n    CHECK_ITERABLE_APPROX(get_neighbor_data(lower_zeta_id), expected_data);\n  }\n\n#ifdef SPECTRE_DEBUG\n  // Test ASSERTs\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_neighbor_rdmp_and_volume_data(\n          make_not_null(&rdmp_tci_data), make_not_null(&neighbor_data),\n          DataVector{}, number_of_rdmp_vars,\n          DirectionalId<Dim>{Direction<Dim>::upper_xi(), ElementId<Dim>{1}},\n          subcell_mesh, element, subcell_mesh, number_of_ghost_zones,\n          neighbor_dg_to_fd_interpolants),\n      Catch::Matchers::ContainsSubstring(\n          \"received_neighbor_subcell_data must be non-empty\"));\n\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<true>(\n          make_not_null(&neighbor_data), DataVector{}, number_of_rdmp_vars,\n          DirectionalId<Dim>{Direction<Dim>::upper_xi(), ElementId<Dim>{1}},\n          subcell_mesh, element, subcell_mesh, number_of_ghost_zones,\n          neighbor_dg_to_fd_interpolants),\n      Catch::Matchers::ContainsSubstring(\n          \"neighbor_subcell_data must be non-empty\"));\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n          make_not_null(&neighbor_data), DataVector{}, number_of_rdmp_vars,\n          DirectionalId<Dim>{Direction<Dim>::upper_xi(), ElementId<Dim>{1}},\n          subcell_mesh, element, subcell_mesh, number_of_ghost_zones,\n          neighbor_dg_to_fd_interpolants),\n      Catch::Matchers::ContainsSubstring(\n          \"neighbor_subcell_data must be non-empty\"));\n\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<true>(\n          make_not_null(&neighbor_data), received_fd_data, number_of_rdmp_vars,\n          upper_xi_id,\n          Mesh<Dim>{5, Spectral::Basis::FiniteDifference,\n                    Spectral::Quadrature::CellCentered},\n          element, subcell_mesh, number_of_ghost_zones,\n          neighbor_dg_to_fd_interpolants),\n      Catch::Matchers::ContainsSubstring(\n          \"must be the same if we are both doing subcell.\"));\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n          make_not_null(&neighbor_data), received_fd_data, number_of_rdmp_vars,\n          upper_xi_id,\n          Mesh<Dim>{5, Spectral::Basis::FiniteDifference,\n                    Spectral::Quadrature::CellCentered},\n          element, subcell_mesh, number_of_ghost_zones,\n          neighbor_dg_to_fd_interpolants),\n      Catch::Matchers::ContainsSubstring(\n          \"must be the same if we are both doing subcell.\"));\n\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<true>(\n          make_not_null(&neighbor_data), received_dg_data, number_of_rdmp_vars,\n          lower_xi_id, dg_mesh, element,\n          Mesh<Dim>{4, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::GaussLobatto},\n          number_of_ghost_zones, neighbor_dg_to_fd_interpolants),\n      Catch::Matchers::ContainsSubstring(\n          \"Neighbor subcell mesh computed from the neighbor DG mesh \"));\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n          make_not_null(&neighbor_data), received_dg_data, number_of_rdmp_vars,\n          lower_xi_id, dg_mesh, element,\n          Mesh<Dim>{4, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::GaussLobatto},\n          number_of_ghost_zones, neighbor_dg_to_fd_interpolants),\n      Catch::Matchers::ContainsSubstring(\n          \"Neighbor subcell mesh computed from the neighbor DG mesh \"));\n\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<true>(\n          make_not_null(&neighbor_data),\n          DataVector{2 * number_of_rdmp_vars + 1, 0.0}, number_of_rdmp_vars,\n          lower_xi_id, dg_mesh, element, subcell_mesh, number_of_ghost_zones,\n          {}),\n      Catch::Matchers::ContainsSubstring(\n          \"The number of DG volume grid points times the number of variables\"));\n  CHECK_THROWS_WITH(\n      evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n          make_not_null(&neighbor_data),\n          DataVector{2 * number_of_rdmp_vars + 1, 0.0}, number_of_rdmp_vars,\n          lower_xi_id, dg_mesh, element, subcell_mesh, number_of_ghost_zones,\n          {}),\n      Catch::Matchers::ContainsSubstring(\n          \"The number of DG volume grid points times the number of variables\"));\n\n  if constexpr (Dim > 1) {\n    Mesh<Dim> non_uniform_mesh{};\n    if constexpr (Dim == 2) {\n      non_uniform_mesh = Mesh<2>{{{4, 5}},\n                                 Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto};\n    } else if constexpr (Dim == 3) {\n      non_uniform_mesh = Mesh<3>{{{4, 5, 6}},\n                                 Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto};\n    }\n    CHECK_THROWS_WITH(\n        evolution::dg::subcell::insert_or_update_neighbor_volume_data<true>(\n            make_not_null(&neighbor_data), received_fd_data,\n            number_of_rdmp_vars,\n            DirectionalId<Dim>{Direction<Dim>::upper_xi(), ElementId<Dim>{1}},\n            non_uniform_mesh, element, subcell_mesh, number_of_ghost_zones,\n            neighbor_dg_to_fd_interpolants),\n        Catch::Matchers::ContainsSubstring(\n            \"The neighbor mesh must be uniform but is\"));\n    CHECK_THROWS_WITH(\n        evolution::dg::subcell::insert_or_update_neighbor_volume_data<false>(\n            make_not_null(&neighbor_data), received_fd_data,\n            number_of_rdmp_vars,\n            DirectionalId<Dim>{Direction<Dim>::upper_xi(), ElementId<Dim>{1}},\n            non_uniform_mesh, element, subcell_mesh, number_of_ghost_zones,\n            neighbor_dg_to_fd_interpolants),\n        Catch::Matchers::ContainsSubstring(\n            \"The neighbor mesh must be uniform but is\"));\n  }\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.NeighborRdmpAndVolumeData\",\n                  \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_NeighborReconstructedFaceSolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Access.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.hpp\"\n#include \"Evolution/DgSubcell/NeighborReconstructedFaceSolution.tpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nusing GhostDataMap = DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>;\ntemplate <size_t Dim>\nusing NeighborReconstructionMap = DirectionalIdMap<Dim, DataVector>;\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  struct SubcellOptions {\n    struct DgComputeSubcellNeighborPackagedData {\n      static NeighborReconstructionMap<Dim> apply(\n          const db::Access& box, const std::vector<DirectionalId<volume_dim>>&\n                                     mortars_to_reconstruct_to) {\n        const GhostDataMap<Dim>& ghost_data = db::get<\n            evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(box);\n\n        // We just simply copy over the data sent since it doesn't actually\n        // matter what we fill the packaged data with in the test, just that\n        // this function is called and that we can retrieve the correct data\n        // from the stored NeighborData.\n        NeighborReconstructionMap<Dim> neighbor_package_data{};\n        for (const auto& mortar_id : mortars_to_reconstruct_to) {\n          neighbor_package_data[mortar_id] =\n              ghost_data.at(mortar_id).neighbor_ghost_data_for_reconstruction();\n        }\n        return neighbor_package_data;\n      }\n    };\n  };\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  using metavars = Metavariables<Dim>;\n\n  GhostDataMap<Dim> ghost_data{};\n  const Mesh<Dim> dg_volume_mesh{2 + 2 * Dim, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim - 1> reconstructed_mesh = dg_volume_mesh.slice_away(0);\n\n  const Mesh<Dim - 1> mortar_mesh{2 + 2 * Dim + 1, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto};\n  DirectionalIdMap<Dim, evolution::dg::MortarDataHolder<Dim>> mortar_data_in{};\n  DirectionalIdMap<Dim, evolution::dg::MortarInfo<Dim>> mortar_info_in{};\n  for (size_t d = 0; d < Dim; ++d) {\n    const bool d_is_odd = (d % 2 != 0);\n    const DirectionalId<Dim> dg_id{\n        Direction<Dim>{d, d_is_odd ? Side::Lower : Side::Upper},\n        ElementId<Dim>{2 * d}};\n    const DirectionalId<Dim> fd_id{\n        Direction<Dim>{d, d_is_odd ? Side::Upper : Side::Lower},\n        ElementId<Dim>{2 * d + 1}};\n    mortar_data_in[dg_id].neighbor().mortar_mesh = mortar_mesh;\n    mortar_data_in[dg_id].neighbor().mortar_data =\n        DataVector(2 * Dim + 1, static_cast<double>(d) + 7.0);\n    mortar_data_in[fd_id];\n    ghost_data[fd_id] = evolution::dg::subcell::GhostData{1};\n    ghost_data[fd_id].neighbor_ghost_data_for_reconstruction() =\n        DataVector(2 * Dim + 1, static_cast<double>(d) + 4.0);\n    mortar_info_in[dg_id].time_stepping_policy() =\n        d == 2 ? evolution::dg::TimeSteppingPolicy::Conservative\n               : evolution::dg::TimeSteppingPolicy::EqualRate;\n    mortar_info_in[fd_id].time_stepping_policy() =\n        d == 2 ? evolution::dg::TimeSteppingPolicy::Conservative\n               : evolution::dg::TimeSteppingPolicy::EqualRate;\n  }\n\n  auto box = db::create<\n      tmpl::list<domain::Tags::Mesh<Dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n                 evolution::dg::Tags::MortarData<Dim>,\n                 evolution::dg::Tags::MortarInfo<Dim>>>(\n      dg_volume_mesh, std::move(ghost_data), std::move(mortar_data_in),\n      std::move(mortar_info_in));\n\n  evolution::dg::subcell::neighbor_reconstructed_face_solution<\n      Dim,\n      typename metavars::SubcellOptions::DgComputeSubcellNeighborPackagedData>(\n      make_not_null(&box));\n  const auto& mortar_data = db::get<evolution::dg::Tags::MortarData<Dim>>(box);\n  for (size_t d = 0; d < Dim; ++d) {\n    CAPTURE(d);\n    const bool d_is_odd = (d % 2 != 0);\n    const DirectionalId<Dim> dg_id{\n        Direction<Dim>{d, d_is_odd ? Side::Lower : Side::Upper},\n        ElementId<Dim>{2 * d}};\n    const DirectionalId<Dim> fd_id{\n        Direction<Dim>{d, d_is_odd ? Side::Upper : Side::Lower},\n        ElementId<Dim>{2 * d + 1}};\n    CAPTURE(dg_id);\n    CAPTURE(fd_id);\n    REQUIRE(mortar_data.contains(dg_id));\n    REQUIRE(mortar_data.contains(fd_id));\n    CHECK(mortar_data.at(dg_id).neighbor().mortar_data ==\n          std::optional(DataVector(2 * Dim + 1, static_cast<double>(d) + 7.0)));\n    CHECK(mortar_data.at(fd_id).neighbor().mortar_data ==\n          (d == 2 ? std::nullopt\n                  : std::optional(DataVector(2 * Dim + 1,\n                                             static_cast<double>(d) + 4.0))));\n    CHECK(mortar_data.at(dg_id).neighbor().mortar_mesh ==\n          std::optional(mortar_mesh));\n    CHECK(mortar_data.at(fd_id).neighbor().mortar_mesh ==\n          (d == 2 ? std::nullopt : std::optional(reconstructed_mesh)));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.NeighborReconstructedFaceSolution\",\n                  \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_NeighborTciDecision.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/NeighborTciDecision.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  using tag = subcell::Tags::NeighborTciDecisions<Dim>;\n  using Type = typename tag::type;\n  auto box = db::create<db::AddSimpleTags<tag>>(Type{});\n  using StorageType = evolution::dg::BoundaryData<Dim>;\n  StorageType neighbor_data{};\n  const DirectionalId<Dim> id_xi{Direction<Dim>::lower_xi(), ElementId<Dim>{0}};\n  neighbor_data.tci_status = 10;\n#ifdef SPECTRE_DEBUG\n  // check ASSERT for neighbors works\n  CHECK_THROWS_WITH(\n      neighbor_tci_decision(make_not_null(&box), id_xi, neighbor_data),\n      Catch::Matchers::ContainsSubstring(\n          \"The NeighborTciDecisions tag does not contain the neighbor\"));\n#endif\n  db::mutate<tag>(\n      [&id_xi](const auto neighbor_decisions_ptr) {\n        neighbor_decisions_ptr->insert(std::pair{id_xi, 0});\n      },\n      make_not_null(&box));\n  neighbor_tci_decision(make_not_null(&box), id_xi, neighbor_data);\n  CHECK(db::get<tag>(box).at(id_xi) == 10);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.NeighborTciDecision\",\n                  \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_PerssonTci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/PerssonTci.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nDataVector soln(const tnsr::I<DataVector, Dim, Frame::ElementLogical>& coords,\n                const size_t number_of_modes_per_dim,\n                const std::array<double, Dim>& highest_coeffs,\n                const std::array<double, Dim>& second_highest_coeffs) {\n  DataVector result =\n      Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n          1, get<0>(coords));\n  for (size_t d = 1; d < Dim; ++d) {\n    result += Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n        1, coords.get(d));\n  }\n\n  for (size_t d = 0; d < Dim; ++d) {\n    result += gsl::at(highest_coeffs, d) *\n              Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n                  number_of_modes_per_dim - 1, coords.get(d));\n    result += gsl::at(second_highest_coeffs, d) *\n              Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n                  number_of_modes_per_dim - 2, coords.get(d));\n  }\n  return result;\n}\n\ntemplate <size_t Dim, typename TagToCheck>\nvoid test_persson_impl(\n    const size_t num_pts_1d,\n    const std::array<double, Dim>& oscillatory_highest_coeffs,\n    const std::array<double, Dim>& oscillatory_second_highest_coeffs,\n    const size_t tensor_component_to_modify, const double persson_exponent,\n    const size_t persson_number_of_highest_modes,\n    const bool expected_tci_triggered) {\n  CAPTURE(Dim);\n  CAPTURE(db::tag_name<TagToCheck>());\n  CAPTURE(num_pts_1d);\n  CAPTURE(oscillatory_highest_coeffs);\n  CAPTURE(oscillatory_second_highest_coeffs);\n  CAPTURE(tensor_component_to_modify);\n  CAPTURE(persson_exponent);\n  CAPTURE(persson_number_of_highest_modes);\n  CAPTURE(expected_tci_triggered);\n  const Mesh<Dim> dg_mesh{num_pts_1d, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(dg_mesh);\n  const std::array<double, Dim> zero_spectral_coeffs = make_array<Dim>(0.0);\n\n  Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>> vars(\n      dg_mesh.number_of_grid_points());\n  if (tensor_component_to_modify == 0) {\n    get(get<Tags::Scalar>(vars)) =\n        soln(logical_coords, dg_mesh.extents(0), oscillatory_highest_coeffs,\n             oscillatory_second_highest_coeffs);\n  } else {\n    get(get<Tags::Scalar>(vars)) =\n        soln(logical_coords, dg_mesh.extents(0), zero_spectral_coeffs,\n             zero_spectral_coeffs);\n  }\n  for (size_t d = 0; d < Dim; ++d) {\n    if (tensor_component_to_modify == d + 1) {\n      get<Tags::Vector<Dim>>(vars).get(d) =\n          (static_cast<double>(d) + 0.3) *\n          soln(logical_coords, dg_mesh.extents(0), oscillatory_highest_coeffs,\n               oscillatory_second_highest_coeffs);\n    } else {\n      get<Tags::Vector<Dim>>(vars).get(d) =\n          (static_cast<double>(d) + 0.3) *\n          soln(logical_coords, dg_mesh.extents(0), zero_spectral_coeffs,\n               zero_spectral_coeffs);\n    }\n  }\n\n  CHECK(evolution::dg::subcell::persson_tci(\n            get<TagToCheck>(vars), dg_mesh, persson_exponent,\n            persson_number_of_highest_modes) == expected_tci_triggered);\n}\n\ntemplate <size_t Dim>\nvoid test_persson() {\n  const auto zero_spectral_coeffs = make_array<Dim>(0.0);\n  // We lower the maximum number of 1d points in 3d in order to reduce total\n  // test runtime.\n  const size_t maximum_number_of_points_1d =\n      Dim == 3 ? 7\n               : Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n  for (const double persson_exponent : {1.0, 10.0}) {\n    for (size_t num_pts_1d = 4; num_pts_1d < maximum_number_of_points_1d;\n         ++num_pts_1d) {\n      // Test all coeffs set to zero.\n      for (const size_t num_highest_modes : {1_st, 2_st}) {\n        test_persson_impl<Dim, Tags::Scalar>(\n            num_pts_1d, zero_spectral_coeffs, zero_spectral_coeffs, 0,\n            persson_exponent, num_highest_modes, false);\n        test_persson_impl<Dim, Tags::Vector<Dim>>(\n            num_pts_1d, zero_spectral_coeffs, zero_spectral_coeffs, 0,\n            persson_exponent, num_highest_modes, false);\n      }\n\n      // Test only the highest coeffs are zero but the second highest mode is\n      // excited.\n      for (size_t i = 0; i < Dim; ++i) {\n        std::array<double, Dim> second_highest_coeffs = make_array<Dim>(0.0);\n        gsl::at(second_highest_coeffs, i) = 0.05;\n\n        for (const size_t num_highest_modes : {1_st, 2_st}) {\n          // If the TCI is only looking at the highest mode, it should not\n          // trigger.\n          const bool should_trigger =\n              (persson_exponent == 10.0) and (num_highest_modes == 2_st);\n\n          // Test Scalar\n          test_persson_impl<Dim, Tags::Scalar>(\n              num_pts_1d, zero_spectral_coeffs, second_highest_coeffs, 0,\n              persson_exponent, num_highest_modes, should_trigger);\n          // Test no trigger if different tags are oscillatory\n          for (size_t j = 0; j < Dim; ++j) {\n            test_persson_impl<Dim, Tags::Scalar>(\n                num_pts_1d, zero_spectral_coeffs, second_highest_coeffs, j + 1,\n                persson_exponent, num_highest_modes, false);\n          }\n          // Test Vector\n          test_persson_impl<Dim, Tags::Vector<Dim>>(\n              num_pts_1d, zero_spectral_coeffs, second_highest_coeffs, 0,\n              persson_exponent, num_highest_modes, false);\n          for (size_t j = 0; j < Dim; ++j) {\n            test_persson_impl<Dim, Tags::Vector<Dim>>(\n                num_pts_1d, zero_spectral_coeffs, second_highest_coeffs, j + 1,\n                persson_exponent, num_highest_modes, should_trigger);\n          }\n        }\n      }\n\n      // Test highest coeffs are not zero.\n      for (size_t i = 0; i < Dim; ++i) {\n        std::array<double, Dim> highest_coeffs = make_array<Dim>(0.0);\n        gsl::at(highest_coeffs, i) = 0.05;\n\n        for (const size_t num_highest_modes : {1_st, 2_st}) {\n          const bool should_trigger = persson_exponent == 10.0;\n\n          // Test Scalar\n          test_persson_impl<Dim, Tags::Scalar>(\n              num_pts_1d, highest_coeffs, zero_spectral_coeffs, 0,\n              persson_exponent, num_highest_modes, should_trigger);\n          // Test no trigger if different tags are oscillatory\n          for (size_t j = 0; j < Dim; ++j) {\n            test_persson_impl<Dim, Tags::Scalar>(\n                num_pts_1d, highest_coeffs, zero_spectral_coeffs, j + 1,\n                persson_exponent, num_highest_modes, false);\n          }\n          // Test Vector\n          test_persson_impl<Dim, Tags::Vector<Dim>>(\n              num_pts_1d, highest_coeffs, zero_spectral_coeffs, 0,\n              persson_exponent, num_highest_modes, false);\n          for (size_t j = 0; j < Dim; ++j) {\n            test_persson_impl<Dim, Tags::Vector<Dim>>(\n                num_pts_1d, highest_coeffs, zero_spectral_coeffs, j + 1,\n                persson_exponent, num_highest_modes, should_trigger);\n          }\n        }\n      }\n    }\n  }\n}\n\n// [[TimeOut, 20]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Tci.Persson\", \"[Evolution][Unit]\") {\n  test_persson<1>();\n  test_persson<2>();\n  test_persson<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_PrepareNeighborData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <iterator>\n#include <optional>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/PrepareNeighborData.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nclass DummyReconstructor {\n public:\n  static size_t ghost_zone_size() { return 2; }\n};\n\nnamespace Tags {\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<DummyReconstructor>;\n};\n}  // namespace Tags\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n\n  struct system {\n    using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n    using flux_variables = tmpl::list<Var1>;\n  };\n\n  struct SubcellOptions {\n    template <typename DbTagsList>\n    static constexpr size_t ghost_zone_size(\n        const db::DataBox<DbTagsList>& box) {\n      return db::get<Tags::Reconstructor>(box).ghost_zone_size();\n    }\n\n    struct GhostVariables {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<typename system::variables_tag>;\n      template <typename T>\n      static DataVector apply(const T& dg_vars, const size_t rdmp_size) {\n        DataVector buffer{dg_vars.size() + rdmp_size};\n        Variables<tmpl::list<Var1>> subcell_vars_to_send{buffer.data(),\n                                                         dg_vars.size()};\n        get(get<Var1>(subcell_vars_to_send)) = 2.0 * get(get<Var1>(dg_vars));\n        return buffer;\n      }\n    };\n  };\n};\n\ntemplate <size_t Dim>\nElement<Dim> create_element() {\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    // only populate some directions with neighbors to test that we can handle\n    // that case correctly. This is needed for DG-subcell at external boundaries\n    if (i % 2 == 0) {\n      neighbors[gsl::at(Direction<Dim>::all_directions(), i)] = Neighbors<Dim>{\n          {ElementId<Dim>{i + 1, {}}}, OrientationMap<Dim>::create_aligned()};\n      if constexpr (Dim == 3) {\n        if (i == 2) {\n          neighbors[gsl::at(Direction<Dim>::all_directions(), i)] =\n              Neighbors<Dim>{\n                  {ElementId<Dim>{i + 1, {}}},\n                  OrientationMap<Dim>{std::array{\n                      Direction<Dim>::lower_xi(), Direction<Dim>::lower_eta(),\n                      Direction<Dim>::upper_zeta()}}};\n        }\n      }\n    }\n  }\n\n  return Element<Dim>{ElementId<Dim>{0, {}}, neighbors};\n}\n\ntemplate <size_t Dim>\nstd::vector<Direction<Dim>> expected_neighbor_directions() {\n  std::vector<Direction<Dim>> neighbor_directions{};\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    // only populate some directions with neighbors to test that we can handle\n    // that case correctly. This is needed for DG-subcell at external boundaries\n    if (i % 2 == 0) {\n      neighbor_directions.push_back(\n          gsl::at(Direction<Dim>::all_directions(), i));\n    }\n  }\n  return neighbor_directions;\n}\n\ntemplate <size_t Dim>\nDirectionalIdMap<Dim, Mesh<Dim>> compute_neighbor_meshes(\n    const Element<Dim>& element, const bool all_dg, const Mesh<Dim>& dg_mesh,\n    const Mesh<Dim>& subcell_mesh) {\n  DirectionalIdMap<Dim, Mesh<Dim>> result{};\n  bool already_set_fd = false;\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    for (const auto& neighbor : neighbors) {\n      if (not already_set_fd and not all_dg) {\n        result.insert(\n            std::pair{DirectionalId<Dim>{direction, neighbor}, subcell_mesh});\n        already_set_fd = true;\n      } else {\n        result.insert(\n            std::pair{DirectionalId<Dim>{direction, neighbor}, dg_mesh});\n      }\n    }\n  }\n  return result;\n}\n\n// TestCreator class needed for subcell options specified below\ntemplate <size_t Dim>\nclass TestCreator : public DomainCreator<Dim> {\n  Domain<Dim> create_domain() const override { return Domain<Dim>{}; }\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override {\n    return {};\n  }\n\n  std::vector<std::string> block_names() const override {\n    return {\"Block0\", \"Block1\"};\n  }\n\n  std::vector<std::array<size_t, Dim>> initial_extents() const override {\n    return {};\n  }\n\n  std::vector<std::array<size_t, Dim>> initial_refinement_levels()\n      const override {\n    return {};\n  }\n};\n\ntemplate <size_t Dim>\nvoid test(const bool all_neighbors_are_doing_dg,\n          const ::fd::DerivativeOrder fd_derivative_order) {\n  CAPTURE(all_neighbors_are_doing_dg);\n  CAPTURE(fd_derivative_order);\n  CAPTURE(Dim);\n  using Interps = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const Element<Dim> element = create_element<Dim>();\n\n  const auto neighbor_meshes = compute_neighbor_meshes(\n      element, all_neighbors_are_doing_dg, dg_mesh, subcell_mesh);\n\n  Variables<tmpl::list<Var1>> vars{dg_mesh.number_of_grid_points(), 0.0};\n  get(get<Var1>(vars)) = get<0>(logical_coordinates(dg_mesh));\n  using flux_tag = ::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>;\n  Variables<tmpl::list<flux_tag>> volume_fluxes{dg_mesh.number_of_grid_points(),\n                                                0.0};\n  for (size_t i = 0; i < Dim; ++i) {\n    get<flux_tag>(volume_fluxes).get(i) = logical_coordinates(dg_mesh).get(i);\n  }\n\n  const size_t ghost_zone_size = DummyReconstructor::ghost_zone_size();\n\n  const bool always_use_subcell = false;\n  const bool use_halo = false;\n\n  // set subcell options\n  const evolution::dg::subcell::SubcellOptions& subcell_options =\n      evolution::dg::subcell::SubcellOptions{\n          evolution::dg::subcell::SubcellOptions{\n              4.0, 1_st, 1.0e-3, 1.0e-4, always_use_subcell, false,\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n              use_halo,\n              all_neighbors_are_doing_dg\n                  ? std::optional{std::vector<std::string>{\"Block1\"}}\n                  : std::optional<std::vector<std::string>>{},\n              fd_derivative_order, 1, 1, 1},\n          TestCreator<Dim>{}};\n\n  Interps fd_to_fd_neighbor_interpolants{};\n  Interps dg_to_fd_neighbor_interpolants{};\n  if constexpr (Dim == 3) {\n    const auto direction = expected_neighbor_directions<Dim>()[1];\n    const auto& neighbor_element_id =\n        *element.neighbors().at(direction).begin();\n    const auto& orientation_map =\n        element.neighbors().at(direction).orientation(neighbor_element_id);\n    const auto logical_fd_coords = logical_coordinates(subcell_mesh);\n    tnsr::I<DataVector, Dim, Frame::ElementLogical> oriented_logical_coords{};\n    for (size_t i = 0; i < Dim; ++i) {\n      oriented_logical_coords.get(i) = orient_variables(\n          logical_fd_coords.get(i), subcell_mesh.extents(), orientation_map);\n    }\n    const auto target_points = evolution::dg::subcell::slice_tensor_for_subcell(\n        oriented_logical_coords, subcell_mesh.extents(), ghost_zone_size,\n        orientation_map(direction), {});\n    dg_to_fd_neighbor_interpolants[DirectionalId<Dim>{direction,\n                                                      neighbor_element_id}] =\n        intrp::Irregular<Dim>{dg_mesh, target_points};\n\n    fd_to_fd_neighbor_interpolants[DirectionalId<Dim>{direction,\n                                                      neighbor_element_id}] =\n        intrp::Irregular<Dim>{subcell_mesh, target_points};\n  }\n\n  auto box = db::create<tmpl::list<\n      Tags::Reconstructor, domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n      variables_tag, evolution::dg::subcell::Tags::DataForRdmpTci,\n      domain::Tags::NeighborMesh<Dim>,\n      evolution::dg::subcell::Tags::SubcellOptions<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromDgToNeighborFd<Dim>>>(\n      std::make_unique<DummyReconstructor>(), dg_mesh, subcell_mesh, element,\n      vars,\n      // Set RDMP data since it would've been calculated before already.\n      evolution::dg::subcell::RdmpTciData{{1.0}, {-1.0}}, neighbor_meshes,\n      subcell_options, fd_to_fd_neighbor_interpolants,\n      dg_to_fd_neighbor_interpolants);\n\n  std::optional<Mesh<Dim>> ghost_data_mesh{std::nullopt};\n  DirectionMap<Dim, DataVector> data_for_neighbors{};\n  evolution::dg::subcell::prepare_neighbor_data<Metavariables<Dim>>(\n      make_not_null(&data_for_neighbors), make_not_null(&ghost_data_mesh),\n      make_not_null(&box), volume_fluxes);\n\n  CHECK(ghost_data_mesh.value() ==\n        (all_neighbors_are_doing_dg ? dg_mesh : subcell_mesh));\n\n  const auto& rdmp_tci_data =\n      db::get<evolution::dg::subcell::Tags::DataForRdmpTci>(box);\n  CHECK_ITERABLE_APPROX(rdmp_tci_data.min_variables_values, DataVector{-1.0});\n  CHECK_ITERABLE_APPROX(rdmp_tci_data.max_variables_values, DataVector{1.0});\n\n  Variables<tmpl::list<Var1>> expected_vars = vars;\n  get(get<Var1>(expected_vars)) *= 2.0;\n\n  DirectionMap<Dim, DataVector> expected_neighbor_data{};\n\n  const bool need_fluxes = fd_derivative_order != ::fd::DerivativeOrder::Two;\n  if (all_neighbors_are_doing_dg) {\n    DataVector data{expected_vars.size() +\n                    (need_fluxes ? volume_fluxes.size() : 0)};\n    std::copy(get(get<Var1>(expected_vars)).begin(),\n              get(get<Var1>(expected_vars)).end(), data.begin());\n    if (need_fluxes) {\n      std::copy(volume_fluxes.data(),\n                std::next(volume_fluxes.data(),\n                          static_cast<std::ptrdiff_t>(volume_fluxes.size())),\n                std::next(data.begin(),\n                          static_cast<std::ptrdiff_t>(expected_vars.size())));\n    }\n\n    for (const auto& direction : expected_neighbor_directions<Dim>()) {\n      expected_neighbor_data.insert(std::pair{direction, data});\n    }\n  } else {\n    // Set all directions to false, enable the desired ones below\n    std::unordered_set<Direction<Dim>> directions_to_slice{};\n\n    REQUIRE(data_for_neighbors.size() == Dim);\n    const size_t num_ghost_points =\n        subcell_mesh.slice_away(0).number_of_grid_points() * ghost_zone_size;\n    for (const auto& direction : expected_neighbor_directions<Dim>()) {\n      REQUIRE(data_for_neighbors.contains(direction));\n      REQUIRE(data_for_neighbors.at(direction).size() ==\n              num_ghost_points * (need_fluxes ? (Dim + 1) : 1) + 2);\n      directions_to_slice.emplace(direction);\n    }\n\n    // do same operation as GhostDataToSlice\n    expected_neighbor_data = [&subcell_mesh, &directions_to_slice, &dg_mesh,\n                              &expected_vars, need_fluxes, &volume_fluxes,\n                              &ghost_zone_size]() {\n      if (need_fluxes) {\n        Variables<tmpl::list<Var1, flux_tag>> expected_var_and_flux{\n            expected_vars.number_of_grid_points()};\n        get<Var1>(expected_var_and_flux) = get<Var1>(expected_vars);\n        get<flux_tag>(expected_var_and_flux) = get<flux_tag>(volume_fluxes);\n        return evolution::dg::subcell::slice_data(\n            evolution::dg::subcell::fd::project(expected_var_and_flux, dg_mesh,\n                                                subcell_mesh.extents()),\n            subcell_mesh.extents(), ghost_zone_size, directions_to_slice, 0,\n            {});\n      } else {\n        return evolution::dg::subcell::slice_data(\n            evolution::dg::subcell::fd::project(expected_vars, dg_mesh,\n                                                subcell_mesh.extents()),\n            subcell_mesh.extents(), ghost_zone_size, directions_to_slice, 0,\n            {});\n      }\n    }();\n    if constexpr (Dim == 3) {\n      const auto direction = expected_neighbor_directions<Dim>()[1];\n      Index<Dim> slice_extents = subcell_mesh.extents();\n      slice_extents[direction.dimension()] = ghost_zone_size;\n      const auto& neighbor_element_id =\n          *element.neighbors().at(direction).begin();\n      expected_neighbor_data.at(direction) = orient_variables(\n          expected_neighbor_data.at(direction), slice_extents,\n          element.neighbors().at(direction).orientation(neighbor_element_id));\n    }\n  }\n\n  for (const auto& direction : expected_neighbor_directions<Dim>()) {\n    CAPTURE(direction);\n    const auto& data_in_direction = data_for_neighbors.at(direction);\n    CHECK_ITERABLE_APPROX(\n        expected_neighbor_data.at(direction),\n        (DataVector{const_cast<double*>(data_in_direction.data()),\n                    data_in_direction.size() - 2}));\n    CHECK(*std::prev(data_in_direction.end(), 2) == approx(1.0));\n    CHECK(*std::prev(data_in_direction.end(), 1) == approx(-1.0));\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.PrepareNeighborData\",\n                  \"[Evolution][Unit]\") {\n  for (const auto& [all_neighbors_are_doing_dg, fd_deriv_order] :\n       cartesian_product(std::array{true, false},\n                         std::array{::fd::DerivativeOrder::Two,\n                                    ::fd::DerivativeOrder::Four})) {\n    test<1>(all_neighbors_are_doing_dg, fd_deriv_order);\n    test<2>(all_neighbors_are_doing_dg, fd_deriv_order);\n    test<3>(all_neighbors_are_doing_dg, fd_deriv_order);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_Projection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Helpers/Evolution/DgSubcell/ProjectionTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\ntemplate <typename Tag>\nstruct Prefix : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = typename Tag::type;\n};\n\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t MaxPts, size_t Dim, Spectral::Basis BasisType,\n          Spectral::Quadrature QuadratureType>\nvoid test_project_fd() {\n  CAPTURE(Dim);\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n\n  for (size_t num_pts_1d = std::max(\n           static_cast<size_t>(2),\n           Spectral::minimum_number_of_points<BasisType, QuadratureType>);\n       num_pts_1d < MaxPts + 1; ++num_pts_1d) {\n    CAPTURE(num_pts_1d);\n    const Mesh<Dim> dg_mesh{num_pts_1d, BasisType, QuadratureType};\n    const auto logical_coords = logical_coordinates(dg_mesh);\n    const size_t num_subcells_1d = 2 * num_pts_1d - 1;\n    const Mesh<Dim> subcell_mesh(num_subcells_1d,\n                                 Spectral::Basis::FiniteDifference,\n                                 Spectral::Quadrature::CellCentered);\n    const DataVector nodal_coeffs =\n        TestHelpers::evolution::dg::subcell::cell_values(dg_mesh.extents(0) - 2,\n                                                         logical_coords);\n    const DataVector expected_subcell_values =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            dg_mesh.extents(0) - 2, logical_coordinates(subcell_mesh));\n    // Test projection of a DataVector\n    const DataVector subcell_values = evolution::dg::subcell::fd::project(\n        nodal_coeffs, dg_mesh, subcell_mesh.extents());\n    CHECK_ITERABLE_APPROX(subcell_values, expected_subcell_values);\n\n    // Test projection of a Variables using the return-by-value function\n    Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>> vars(\n        dg_mesh.number_of_grid_points());\n    get(get<Tags::Scalar>(vars)) = nodal_coeffs;\n    for (size_t d = 0; d < Dim; ++d) {\n      get<Tags::Vector<Dim>>(vars).get(d) = (d + 2.0) * nodal_coeffs;\n    }\n    Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>> subcell_vars =\n        evolution::dg::subcell::fd::project(vars, dg_mesh,\n                                            subcell_mesh.extents());\n\n    const auto check_each_field_in_vars =\n        [&expected_subcell_values](const auto& local_subcell_vars) {\n          CHECK_ITERABLE_APPROX(\n              get(get<tmpl::front<typename std::decay_t<decltype(\n                      local_subcell_vars)>::tags_list>>(local_subcell_vars)),\n              expected_subcell_values);\n          for (size_t d = 0; d < Dim; ++d) {\n            CHECK_ITERABLE_APPROX(\n                get<tmpl::back<typename std::decay_t<decltype(\n                    local_subcell_vars)>::tags_list>>(local_subcell_vars)\n                    .get(d),\n                (d + 2.0) * expected_subcell_values);\n          }\n        };\n    check_each_field_in_vars(subcell_vars);\n\n    // Check with the prefix on the subcell vars\n    Variables<\n        tmpl::list<Tags::Prefix<Tags::Scalar>, Tags::Prefix<Tags::Vector<Dim>>>>\n        prefixed_subcell_vars{subcell_mesh.number_of_grid_points()};\n    evolution::dg::subcell::fd::project(make_not_null(&prefixed_subcell_vars),\n                                        vars, dg_mesh, subcell_mesh.extents());\n    check_each_field_in_vars(prefixed_subcell_vars);\n\n    // Check with the prefix on the DG vars\n    Variables<\n        tmpl::list<Tags::Prefix<Tags::Scalar>, Tags::Prefix<Tags::Vector<Dim>>>>\n        prefixed_vars(dg_mesh.number_of_grid_points());\n    prefixed_vars = vars;\n    subcell_vars.initialize(0);\n    evolution::dg::subcell::fd::project(make_not_null(&subcell_vars),\n                                        prefixed_vars, dg_mesh,\n                                        subcell_mesh.extents());\n    check_each_field_in_vars(subcell_vars);\n\n    // Check with the prefix on the DG and subcell vars\n    prefixed_subcell_vars.initialize(0);\n    evolution::dg::subcell::fd::project(make_not_null(&prefixed_subcell_vars),\n                                        prefixed_vars, dg_mesh,\n                                        subcell_mesh.extents());\n    check_each_field_in_vars(prefixed_subcell_vars);\n  }\n}\n\ntemplate <size_t MaxPts, size_t Dim, size_t Face_Dim, Spectral::Basis BasisType,\n          Spectral::Quadrature QuadratureType>\nvoid test_project_on_face_fd() {\n  CAPTURE(Dim);\n  CAPTURE(Face_Dim);\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n\n  for (size_t num_pts_1d = std::max(\n           static_cast<size_t>(2),\n           Spectral::minimum_number_of_points<BasisType, QuadratureType>);\n       num_pts_1d < MaxPts + 1; ++num_pts_1d) {\n    CAPTURE(num_pts_1d);\n    const Mesh<Dim> dg_mesh{num_pts_1d, BasisType, QuadratureType};\n    const auto logical_coords = logical_coordinates(dg_mesh);\n    const size_t num_subcells_1d_face = 2 * num_pts_1d;\n    const size_t num_subcells_1d_cell = 2 * num_pts_1d - 1;\n    CAPTURE(num_subcells_1d_face);\n    CAPTURE(num_subcells_1d_cell);\n\n    std::array<size_t, Dim> extents{};\n    std::array<Spectral::Basis, Dim> basis{};\n    std::array<Spectral::Quadrature, Dim> quadrature{};\n    for (size_t d = 0; d < Dim; d++) {\n      basis[d] = Spectral::Basis::FiniteDifference;\n      if (d == Face_Dim) {\n        extents[d] = num_subcells_1d_face;\n        quadrature[d] = Spectral::Quadrature::FaceCentered;\n      } else {\n        extents[d] = num_subcells_1d_cell;\n        quadrature[d] = Spectral::Quadrature::CellCentered;\n      }\n    }\n\n    const Mesh<Dim> subcell_mesh(extents, basis, quadrature);\n    const DataVector nodal_coeffs =\n        TestHelpers::evolution::dg::subcell::cell_values(dg_mesh.extents(0) - 2,\n                                                         logical_coords);\n    const DataVector expected_subcell_values =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            dg_mesh.extents(0) - 2, logical_coordinates(subcell_mesh));\n    // Test projection of a DataVector\n    const DataVector subcell_values =\n        evolution::dg::subcell::fd::project_to_faces(\n            nodal_coeffs, dg_mesh, subcell_mesh.extents(), Face_Dim);\n    CHECK_ITERABLE_APPROX(subcell_values, expected_subcell_values);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Fd.Projection\", \"[Evolution][Unit]\") {\n  test_project_fd<10, 1, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto>();\n  test_project_fd<10, 1, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::Gauss>();\n\n  test_project_fd<10, 2, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto>();\n  test_project_fd<10, 2, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::Gauss>();\n\n  test_project_fd<5, 3, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto>();\n  test_project_fd<4, 3, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::Gauss>();\n  test_project_on_face_fd<10, 1, 0, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto>();\n  test_project_on_face_fd<10, 1, 0, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::Gauss>();\n  test_project_on_face_fd<5, 3, 0, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto>();\n  test_project_on_face_fd<4, 3, 0, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::Gauss>();\n  test_project_on_face_fd<5, 3, 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto>();\n  test_project_on_face_fd<4, 3, 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::Gauss>();\n  test_project_on_face_fd<5, 3, 2, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto>();\n  test_project_on_face_fd<4, 3, 2, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::Gauss>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_RdmpTci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nDataVector soln(const tnsr::I<DataVector, Dim, Frame::ElementLogical>& coords) {\n  DataVector result =\n      Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n          1, get<0>(coords));\n  for (size_t d = 1; d < Dim; ++d) {\n    result += Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n        1, coords.get(d));\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test_rdmp_impl(const DataVector& past_max_values,\n                    const DataVector& past_min_values, const double rdmp_delta0,\n                    const double rdmp_epsilon, const size_t num_pts_1d,\n                    const double rescale_dg_by, const double rescale_subcell_by,\n                    const int expected_tci_triggered) {\n  CAPTURE(Dim);\n  CAPTURE(num_pts_1d);\n  CAPTURE(rdmp_delta0);\n  CAPTURE(rdmp_epsilon);\n  CAPTURE(expected_tci_triggered);\n  CAPTURE(past_max_values);\n  CAPTURE(past_min_values);\n  const Mesh<Dim> dg_mesh{num_pts_1d, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh{2 * num_pts_1d - 1,\n                               Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::FaceCentered};\n  const auto logical_coords = logical_coordinates(dg_mesh);\n\n  Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>> dg_vars(\n      dg_mesh.number_of_grid_points());\n  get(get<Tags::Scalar>(dg_vars)) = soln(logical_coords);\n  for (size_t d = 0; d < Dim; ++d) {\n    get<Tags::Vector<Dim>>(dg_vars).get(d) = soln(logical_coords);\n  }\n\n  Variables<\n      tmpl::list<evolution::dg::subcell::Tags::Inactive<Tags::Scalar>,\n                 evolution::dg::subcell::Tags::Inactive<Tags::Vector<Dim>>>>\n      subcell_vars{evolution::dg::subcell::fd::project(dg_vars, dg_mesh,\n                                                       subcell_mesh.extents())};\n  subcell_vars *= rescale_subcell_by;\n  dg_vars *= rescale_dg_by;\n\n  CHECK(evolution::dg::subcell::rdmp_tci(\n            dg_vars, subcell_vars, past_max_values, past_min_values,\n            rdmp_delta0, rdmp_epsilon) == expected_tci_triggered);\n  // Swap DG and subcell being active/inactive to make sure there are no\n  // assumptions about active or inactive being DG or subcell.\n  using std::swap;\n  swap(dg_vars, subcell_vars);\n  CHECK(evolution::dg::subcell::rdmp_tci(\n            dg_vars, subcell_vars, past_max_values, past_min_values,\n            rdmp_delta0, rdmp_epsilon) == expected_tci_triggered);\n\n  // Check with only DataVector implementation\n  DataVector current_max_values{past_max_values.size()};\n  DataVector current_min_values{current_max_values.size()};\n  size_t component_index = 0;\n  tmpl::for_each<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>>(\n      [&component_index, &current_max_values, &current_min_values, &dg_vars,\n       &subcell_vars](auto tag_v) {\n        using std::max;\n        using std::min;\n\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const auto& dg_tensor = get<tag>(dg_vars);\n        const auto& subcell_tensor =\n            get<evolution::dg::subcell::Tags::Inactive<tag>>(subcell_vars);\n        const size_t number_of_components_in_tensor = dg_tensor.size();\n        for (size_t tensor_storage_index = 0;\n             tensor_storage_index < number_of_components_in_tensor;\n             ++tensor_storage_index) {\n          current_max_values[component_index] =\n              max(max(dg_tensor[tensor_storage_index]),\n                  max(subcell_tensor[tensor_storage_index]));\n          current_min_values[component_index] =\n              min(min(dg_tensor[tensor_storage_index]),\n                  min(subcell_tensor[tensor_storage_index]));\n          ++component_index;\n        }\n      });\n  CHECK(evolution::dg::subcell::rdmp_tci(current_max_values, current_min_values,\n                                         past_max_values, past_min_values,\n                                         rdmp_delta0, rdmp_epsilon) ==\n        expected_tci_triggered);\n}\n\ntemplate <size_t Dim>\nvoid test_rdmp() {\n  const DataVector past_max_values{Dim + 1, static_cast<double>(Dim)};\n  const DataVector past_min_values{Dim + 1, -static_cast<double>(Dim)};\n\n  // We lower the maximum number of 1d points in 3d in order to reduce total\n  // test runtime.\n  const size_t maximum_number_of_points_1d =\n      Dim == 3 ? 7\n               : Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n  for (size_t num_pts_1d = 2; num_pts_1d < maximum_number_of_points_1d;\n       ++num_pts_1d) {\n    test_rdmp_impl<Dim>(past_max_values, past_min_values, 1.0e-4, 1.0e-3,\n                        num_pts_1d, 1.0, 1.0, false);\n    for (size_t component_index = 0; component_index < Dim + 1;\n         ++component_index) {\n      CAPTURE(component_index);\n      auto local_past_max_values = past_max_values;\n      local_past_max_values[component_index] *= 0.5;\n      const int expected_tci_status = component_index + 1;\n\n      test_rdmp_impl<Dim>(local_past_max_values, past_min_values, 1.0e-4,\n                          1.0e-3, num_pts_1d, 1.0, 1.0, expected_tci_status);\n      // Rescale subcell values to be tiny, effectively just checking DG\n      test_rdmp_impl<Dim>(local_past_max_values, past_min_values, 1.0e-4,\n                          1.0e-3, num_pts_1d, 1.0, 1.0e-5, expected_tci_status);\n      // Rescale DG values to be tiny, effectively just checking subcell\n      test_rdmp_impl<Dim>(local_past_max_values, past_min_values, 1.0e-4,\n                          1.0e-3, num_pts_1d, 1.0e-5, 1.0, expected_tci_status);\n      // Rescale DG&subcell values to be tiny, so no trigger\n      test_rdmp_impl<Dim>(local_past_max_values, past_min_values, 1.0e-4,\n                          1.0e-3, num_pts_1d, 1.0e-5, 1.0e-5, false);\n\n      auto local_past_min_values = past_min_values;\n      local_past_min_values[component_index] *= 0.5;\n      test_rdmp_impl<Dim>(past_max_values, local_past_min_values, 1.0e-4,\n                          1.0e-3, num_pts_1d, 1.0, 1.0, expected_tci_status);\n      // Rescale subcell values to be tiny, effectively just checking DG\n      test_rdmp_impl<Dim>(past_max_values, local_past_min_values, 1.0e-4,\n                          1.0e-3, num_pts_1d, 1.0, 1.0e-5, expected_tci_status);\n      // Rescale DG values to be tiny, effectively just checking subcell\n      test_rdmp_impl<Dim>(past_max_values, local_past_min_values, 1.0e-4,\n                          1.0e-3, num_pts_1d, 1.0e-5, 1.0, expected_tci_status);\n      // Rescale DG&subcell values to be tiny, so no trigger\n      test_rdmp_impl<Dim>(past_max_values, local_past_min_values, 1.0e-4,\n                          1.0e-3, num_pts_1d, 1.0e-5, 1.0e-5, false);\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_rdmp_max_min() {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist{-1.0, 1.0};\n  const size_t num_pts = 10;\n  auto active_vars = make_with_random_values<\n      Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>>>(\n      make_not_null(&gen), make_not_null(&dist), num_pts);\n  auto inactive_vars = make_with_random_values<Variables<\n      tmpl::list<evolution::dg::subcell::Tags::Inactive<Tags::Scalar>,\n                 evolution::dg::subcell::Tags::Inactive<Tags::Vector<Dim>>>>>(\n      make_not_null(&gen), make_not_null(&dist), num_pts);\n  get<Dim - 1>(get<evolution::dg::subcell::Tags::Inactive<Tags::Vector<Dim>>>(\n      inactive_vars))[num_pts / 2] = 10.0;\n  get<Dim - 1>(get<evolution::dg::subcell::Tags::Inactive<Tags::Vector<Dim>>>(\n      inactive_vars))[num_pts / 2 + 1] = -10.0;\n  {\n    // Check that the inactive vars are actually ignored\n    const auto [max, min] =\n        evolution::dg::subcell::rdmp_max_min(active_vars, inactive_vars, false);\n    REQUIRE(max.size() == Dim + 1);\n    REQUIRE(min.size() == Dim + 1);\n    CHECK(max[Dim] < 10.0);\n    CHECK(min[Dim] > -10.0);\n  }\n  {\n    // Check that the inactive vars are included if requested\n    const auto [max, min] =\n        evolution::dg::subcell::rdmp_max_min(active_vars, inactive_vars, true);\n    REQUIRE(max.size() == Dim + 1);\n    REQUIRE(min.size() == Dim + 1);\n    CHECK(max[Dim] == 10.0);\n    CHECK(min[Dim] == -10.0);\n  }\n  get<Dim - 1>(get<Tags::Vector<Dim>>(active_vars))[num_pts / 2] = 100.0;\n  get<Dim - 1>(get<Tags::Vector<Dim>>(active_vars))[num_pts / 2 + 1] = -200.0;\n  {\n    // Check that the active vars are used for max and min\n    const auto [max, min] =\n        evolution::dg::subcell::rdmp_max_min(active_vars, inactive_vars, true);\n    REQUIRE(max.size() == Dim + 1);\n    REQUIRE(min.size() == Dim + 1);\n    CHECK(max[Dim] == 100.0);\n    CHECK(min[Dim] == -200.0);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Tci.Rdmp\", \"[Evolution][Unit]\") {\n  test_rdmp<1>();\n  test_rdmp<2>();\n  test_rdmp<3>();\n\n  test_rdmp_max_min<1>();\n  test_rdmp_max_min<2>();\n  test_rdmp_max_min<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_RdmpTciData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace {\nvoid test() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> dist{-100.0, 100.0};\n  DataVector max_vars{10};\n  DataVector min_vars{10};\n  fill_with_random_values(make_not_null(&max_vars), make_not_null(&gen),\n                          make_not_null(&dist));\n  fill_with_random_values(make_not_null(&min_vars), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  const RdmpTciData rdmp_tci_data{max_vars, min_vars};\n\n  // test equivalence\n  {\n    // clang-tidy: intentional copy\n    const auto rdmp_tci_data2 = rdmp_tci_data;  // NOLINT\n    CHECK(rdmp_tci_data == rdmp_tci_data2);\n    CHECK_FALSE(rdmp_tci_data != rdmp_tci_data2);\n  }\n  {\n    auto rdmp_tci_data2 = rdmp_tci_data;\n    rdmp_tci_data2.max_variables_values[0] = 200.0;\n    CHECK_FALSE(rdmp_tci_data2 == rdmp_tci_data);\n    CHECK(rdmp_tci_data2 != rdmp_tci_data);\n  }\n  {\n    auto rdmp_tci_data2 = rdmp_tci_data;\n    rdmp_tci_data2.min_variables_values[0] = 200.0;\n    CHECK_FALSE(rdmp_tci_data2 == rdmp_tci_data);\n    CHECK(rdmp_tci_data2 != rdmp_tci_data);\n  }\n\n  // Test stream operator\n  CHECK(get_output(rdmp_tci_data) ==\n        get_output(rdmp_tci_data.max_variables_values) + '\\n' +\n            get_output(rdmp_tci_data.min_variables_values));\n\n  // Test serialization\n  test_serialization(rdmp_tci_data);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.RdmpTciData\", \"[Evolution][Unit]\") {\n  test();\n}\n}  // namespace\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_ReceiveSubcellDataForDg.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/DgSubcell/ReceiveSubcellDataForDg.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/MeshForGhostData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nusing GhostDataMap = DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>;\n\ntemplate <size_t Dim>\nusing BoundaryData = evolution::dg::BoundaryData<Dim>;\n\ntemplate <size_t Dim>\nusing BoundaryDataMap = DirectionalIdMap<Dim, BoundaryData<Dim>>;\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n\n  evolution::dg::subcell::RdmpTciData rdmp_tci_data{};\n  rdmp_tci_data.max_variables_values = DataVector{1.0, 2.0};\n  rdmp_tci_data.min_variables_values = DataVector{-2.0, 0.1};\n  auto expected_rdmp_tci_data = rdmp_tci_data;\n  GhostDataMap<Dim> neighbor_data_map{};  // NOLINT(misc-const-correctness)\n  const Mesh<Dim> dg_volume_mesh{2 + 2 * Dim, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto};\n  auto box = db::create<\n      tmpl::list<evolution::dg::subcell::Tags::MeshForGhostData<Dim>,\n                 evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n                 evolution::dg::subcell::Tags::DataForRdmpTci>>(\n      DirectionalIdMap<Dim, Mesh<Dim>>{}, std::move(neighbor_data_map),\n      std::move(rdmp_tci_data));\n\n  const Mesh<Dim> fd_volume_mesh{2 + 2 * Dim + 1,\n                                 Spectral::Basis::FiniteDifference,\n                                 Spectral::Quadrature::CellCentered};\n  const Mesh<Dim - 1> mortar_mesh{2 + 2 * Dim + 1, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto};\n\n  const DirectionalId<Dim> dg_id{Direction<Dim>::upper_xi(), ElementId<Dim>{0}};\n  const DirectionalId<Dim> fd_id{Direction<Dim>::lower_xi(), ElementId<Dim>{1}};\n\n  DataVector fd_recons_and_rdmp_data(2 * Dim + 1 + 4, 4.0);\n  DataVector dg_recons_and_rdmp_data(2 * Dim + 1 + 4, 7.0);\n  for (size_t i = 0; i < 4; ++i) {\n    dg_recons_and_rdmp_data[2 * Dim + 1 + i] =\n        (i > 1 ? -1.0 : 1.0) * 7.0 * (static_cast<double>(i) + 5.0);\n    fd_recons_and_rdmp_data[2 * Dim + 1 + i] =\n        (i > 1 ? -1.0 : 1.0) * 7.0 * (static_cast<double>(i) + 50.0);\n  }\n  expected_rdmp_tci_data.max_variables_values =\n      max(expected_rdmp_tci_data.max_variables_values,\n          DataVector(&fd_recons_and_rdmp_data[2 * Dim + 1], 2));\n  expected_rdmp_tci_data.min_variables_values =\n      min(expected_rdmp_tci_data.min_variables_values,\n          DataVector(&fd_recons_and_rdmp_data[2 * Dim + 3], 2));\n  DataVector dg_flux_data(2 * Dim + 1);\n\n  evolution::dg::subcell::receive_subcell_data_for_dg<Dim>(\n      make_not_null(&box), dg_id,\n      BoundaryData<Dim>{dg_volume_mesh,\n                        dg_volume_mesh,\n                        mortar_mesh,\n                        dg_recons_and_rdmp_data,\n                        dg_flux_data,\n                        {},\n                        1});\n  evolution::dg::subcell::receive_subcell_data_for_dg<Dim>(\n      make_not_null(&box), fd_id,\n      BoundaryData<Dim>{dg_volume_mesh,\n                        fd_volume_mesh,\n                        std::nullopt,\n                        fd_recons_and_rdmp_data,\n                        std::nullopt,\n                        {},\n                        2});\n\n  CHECK(db::get<evolution::dg::subcell::Tags::DataForRdmpTci>(box) ==\n        expected_rdmp_tci_data);\n\n  const auto& ghost_meshes =\n      db::get<evolution::dg::subcell::Tags::MeshForGhostData<Dim>>(box);\n  const auto& reconstruction_data =\n      db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>>(\n          box);\n\n  REQUIRE(reconstruction_data.contains(dg_id));\n  CHECK(\n      reconstruction_data.at(dg_id).neighbor_ghost_data_for_reconstruction() ==\n      (DataVector{dg_recons_and_rdmp_data.data(),\n                  dg_recons_and_rdmp_data.size() - 4}));\n\n  REQUIRE(reconstruction_data.contains(fd_id));\n  CHECK(\n      reconstruction_data.at(fd_id).neighbor_ghost_data_for_reconstruction() ==\n      (DataVector{fd_recons_and_rdmp_data.data(),\n                  fd_recons_and_rdmp_data.size() - 4}));\n\n  REQUIRE(ghost_meshes.contains(dg_id));\n  CHECK(ghost_meshes.at(dg_id) == dg_volume_mesh);\n  REQUIRE(ghost_meshes.contains(fd_id));\n  CHECK(ghost_meshes.at(fd_id) == fd_volume_mesh);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.ReceiveSubcellDataForDg\",\n                  \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_Reconstruction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Helpers/Evolution/DgSubcell/ProjectionTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\ntemplate <typename Tag>\nstruct Prefix : db::PrefixTag, db::SimpleTag {\n  using tag = Tag;\n  using type = typename Tag::type;\n};\n\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\ntemplate <size_t Dim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nauto make_map() {\n  if constexpr (Dim == 1) {\n    using domain::make_coordinate_map_base;\n    using domain::CoordinateMaps::Affine;\n    return make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n        Affine(-1.0, 1.0, 2.0, 5.0));\n  } else if constexpr (Dim == 2) {\n    using domain::make_coordinate_map_base;\n    using domain::CoordinateMaps::Affine;\n    using domain::CoordinateMaps::ProductOf2Maps;\n    using Wedge2D = domain::CoordinateMaps::Wedge<2>;\n    return make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n        ProductOf2Maps<Affine, Affine>(Affine(-1.0, 1.0, -1.0, -0.8),\n                                       Affine(-1.0, 1.0, -1.0, -0.8)),\n        Wedge2D(0.5, 0.75, 1.0, 1.0, OrientationMap<Dim>::create_aligned(),\n                false));\n  } else {\n    using domain::make_coordinate_map_base;\n    using domain::CoordinateMaps::Affine;\n    using domain::CoordinateMaps::ProductOf3Maps;\n    using Wedge3D = domain::CoordinateMaps::Wedge<3>;\n    return make_coordinate_map_base<Frame::ElementLogical, Frame::Inertial>(\n        ProductOf3Maps<Affine, Affine, Affine>(Affine(-1.0, 1.0, -1.0, -0.8),\n                                               Affine(-1.0, 1.0, -1.0, -0.8),\n                                               Affine(-1.0, 1.0, 0.8, 1.0)),\n        Wedge3D(0.5, 0.75, 1.0, 1.0, OrientationMap<Dim>::create_aligned(),\n                false));\n  }\n}\n\ntemplate <size_t MaxPts, size_t Dim, Spectral::Basis BasisType,\n          Spectral::Quadrature QuadratureType>\nvoid test_reconstruct_fd(const std::vector<double>& eps,\n                         const evolution::dg::subcell::fd::ReconstructionMethod\n                             reconstruction_method) {\n  CAPTURE(Dim);\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n  CAPTURE(reconstruction_method);\n\n  // For FD reconstruction we need to set up a coordinate map since we need\n  // Jacobians to do the integration.\n  const auto coord_map_base_ptr = make_map<Dim>();\n  const auto& coord_map = *coord_map_base_ptr;\n\n  const size_t start_pt =\n      std::max(static_cast<size_t>(2),\n               Spectral::minimum_number_of_points<BasisType, QuadratureType>);\n  if (eps.size() != 1 and (eps.size() != MaxPts + 1 - start_pt)) {\n    ERROR(\"eps is the wrong size. Must be size 1 or size \"\n          << MaxPts + 1 - start_pt << \" but is \" << eps.size());\n  }\n  for (size_t num_pts_1d = start_pt; num_pts_1d < MaxPts + 1; ++num_pts_1d) {\n    const double local_eps =\n        eps.size() == 1 ? eps[0] : eps[num_pts_1d - start_pt];\n    Approx local_approx = Approx::custom().epsilon(local_eps).scale(1.);\n    CAPTURE(local_eps);\n    CAPTURE(num_pts_1d);\n    const Mesh<Dim> dg_mesh{num_pts_1d, BasisType, QuadratureType};\n    const auto inertial_coords = coord_map(logical_coordinates(dg_mesh));\n    const size_t num_subcells_1d = 2 * num_pts_1d - 1;\n    const Mesh<Dim> subcell_mesh(num_subcells_1d,\n                                 Spectral::Basis::FiniteDifference,\n                                 Spectral::Quadrature::CellCentered);\n\n    const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n    const auto subcell_inertial_coords = coord_map(subcell_logical_coords);\n\n    const auto dg_jac = coord_map.jacobian(logical_coordinates(dg_mesh));\n    const DataVector dg_det_jac = get(determinant(dg_jac));\n    const DataVector projected_det_jac = evolution::dg::subcell::fd::project(\n        dg_det_jac, dg_mesh, subcell_mesh.extents());\n\n    // Our FD reconstruction scheme can integrate polynomials up to degree 5\n    // exactly. However, we want to verify that if we have a higher degree\n    // polynomial that we can still reconstruct the solution exactly. That is,\n    // we want to verify that R(P(uJ))=uJ.\n    const DataVector expected_nodal_coeffs =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            std::min(dg_mesh.extents(0) - 2, 6_st), inertial_coords);\n\n    const DataVector subcell_values =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            std::min(dg_mesh.extents(0) - 2, 6_st), subcell_inertial_coords);\n\n    const DataVector reconstructed_datavector =\n        evolution::dg::subcell::fd::reconstruct(\n            subcell_values * projected_det_jac, dg_mesh, subcell_mesh.extents(),\n            reconstruction_method) /\n        dg_det_jac;\n\n    // Test reconstruction of a DataVector\n    CHECK_ITERABLE_CUSTOM_APPROX(reconstructed_datavector,\n                                 expected_nodal_coeffs, local_approx);\n\n    // Test reconstruction of a Variables\n    Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>> cell_values_vars(\n        subcell_mesh.number_of_grid_points());\n    get(get<Tags::Scalar>(cell_values_vars)) =\n        TestHelpers::evolution::dg::subcell::cell_values(\n            std::min(dg_mesh.extents(0) - 2, 6_st), subcell_inertial_coords) *\n        projected_det_jac;\n    for (size_t d = 0; d < Dim; ++d) {\n      get<Tags::Vector<Dim>>(cell_values_vars).get(d) =\n          (d + 2.0) *\n          TestHelpers::evolution::dg::subcell::cell_values(\n              std::min(dg_mesh.extents(0) - 2, 6_st), subcell_inertial_coords) *\n          projected_det_jac;\n    }\n\n    const auto check_each_field_in_vars = [&dg_det_jac, &expected_nodal_coeffs,\n                                           &local_approx](\n                                              const auto& local_dg_vars) {\n      REQUIRE(local_dg_vars.number_of_grid_points() ==\n              expected_nodal_coeffs.size());\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          DataVector(get(get<tmpl::front<typename std::decay_t<decltype(\n                             local_dg_vars)>::tags_list>>(local_dg_vars)) /\n                     dg_det_jac),\n          expected_nodal_coeffs, local_approx);\n      for (size_t d = 0; d < Dim; ++d) {\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            DataVector(get<tmpl::back<typename std::decay_t<decltype(\n                           local_dg_vars)>::tags_list>>(local_dg_vars)\n                           .get(d) /\n                       dg_det_jac),\n            (d + 2.0) * expected_nodal_coeffs, local_approx);\n      }\n    };\n\n    Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>> reconstructed_vars =\n        evolution::dg::subcell::fd::reconstruct(cell_values_vars, dg_mesh,\n                                                subcell_mesh.extents(),\n                                                reconstruction_method);\n    check_each_field_in_vars(reconstructed_vars);\n\n    // Check not_null with no prefix tags that we correctly resize the buffer.\n    reconstructed_vars.initialize(0);\n    evolution::dg::subcell::fd::reconstruct(\n        make_not_null(&reconstructed_vars), cell_values_vars, dg_mesh,\n        subcell_mesh.extents(), reconstruction_method);\n    check_each_field_in_vars(reconstructed_vars);\n\n    // Check with the prefix on the subcell vars\n    Variables<\n        tmpl::list<Tags::Prefix<Tags::Scalar>, Tags::Prefix<Tags::Vector<Dim>>>>\n        prefixed_cell_values_vars(subcell_mesh.number_of_grid_points());\n    prefixed_cell_values_vars = cell_values_vars;\n    evolution::dg::subcell::fd::reconstruct(\n        make_not_null(&reconstructed_vars), prefixed_cell_values_vars, dg_mesh,\n        subcell_mesh.extents(), reconstruction_method);\n    check_each_field_in_vars(reconstructed_vars);\n\n    // Check with prefix tag on the DG vars\n    Variables<\n        tmpl::list<Tags::Prefix<Tags::Scalar>, Tags::Prefix<Tags::Vector<Dim>>>>\n        prefixed_reconstructed_vars{dg_mesh.number_of_grid_points()};\n    evolution::dg::subcell::fd::reconstruct(\n        make_not_null(&prefixed_reconstructed_vars), cell_values_vars, dg_mesh,\n        subcell_mesh.extents(), reconstruction_method);\n    check_each_field_in_vars(prefixed_reconstructed_vars);\n\n    // Check with the prefix on the DG and subcell vars\n    prefixed_reconstructed_vars.initialize(0);\n    evolution::dg::subcell::fd::reconstruct(\n        make_not_null(&prefixed_reconstructed_vars), prefixed_cell_values_vars,\n        dg_mesh, subcell_mesh.extents(), reconstruction_method);\n    check_each_field_in_vars(prefixed_reconstructed_vars);\n  }\n}\n\n// [[TimeOut, 20]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Fd.Reconstruction\",\n                  \"[Evolution][Unit]\") {\n  for (const auto reconstruction_method :\n       {evolution::dg::subcell::fd::ReconstructionMethod::AllDimsAtOnce,\n        evolution::dg::subcell::fd::ReconstructionMethod::DimByDim}) {\n    test_reconstruct_fd<10, 1, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto>(\n        {1.0e-13}, reconstruction_method);\n    test_reconstruct_fd<10, 1, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::Gauss>({1.0e-13},\n                                                     reconstruction_method);\n\n    test_reconstruct_fd<8, 2, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto>(\n        {1.0e-14, 2.0e-13, 5.0e-7, 3.0e-7, 3.0e-8, 1.0e-9, 1.0e-10},\n        reconstruction_method);\n    test_reconstruct_fd<8, 2, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::Gauss>(\n        {1.0e-14, 1.0e-14, 5.0e-7, 3.0e-7, 3.0e-8, 1.0e-9, 1.0e-10},\n        reconstruction_method);\n\n    test_reconstruct_fd<6, 3, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto>(\n        {1.0e-14, 1.0e-12, 3.0e-6, 3.0e-7, 3.0e-8}, reconstruction_method);\n    test_reconstruct_fd<6, 3, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::Gauss>(\n        {1.0e-14, 1.0e-14, 1.0e-6, 3.0e-7, 3.0e-8}, reconstruction_method);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_ReconstructionMethod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <evolution::dg::subcell::fd::ReconstructionMethod ReconsMethod>\nvoid test_construct_from_options() {\n  const auto created = TestHelpers::test_creation<\n      evolution::dg::subcell::fd::ReconstructionMethod>(\n      get_output(ReconsMethod));\n  CHECK(created == ReconsMethod);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Fd.ReconstructionMethod\",\n                  \"[Evolution][Unit]\") {\n  using evolution::dg::subcell::fd::ReconstructionMethod;\n  CHECK(get_output(ReconstructionMethod::DimByDim) == \"DimByDim\");\n  CHECK(get_output(ReconstructionMethod::AllDimsAtOnce) == \"AllDimsAtOnce\");\n\n  test_construct_from_options<ReconstructionMethod::DimByDim>();\n  test_construct_from_options<ReconstructionMethod::AllDimsAtOnce>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_ReconstructionOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/ReconstructionOrder.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  namespace subcell = evolution::dg::subcell;\n  const Mesh<Dim> subcell_mesh{5, Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::CellCentered};\n\n  // Test first with no data\n  auto box =\n      db::create<db::AddSimpleTags<subcell::Tags::Mesh<Dim>,\n                                   subcell::Tags::ReconstructionOrder<Dim>>>(\n          subcell_mesh,\n          typename subcell::Tags::ReconstructionOrder<Dim>::type{});\n\n  subcell::store_reconstruction_order_in_databox(\n      make_not_null(&box),\n      std::optional<std::array<gsl::span<std::uint8_t>, Dim>>{});\n\n  CHECK(not db::get<subcell::Tags::ReconstructionOrder<Dim>>(box).has_value());\n\n  std::optional<std::array<std::vector<std::uint8_t>, Dim>> recons_data{\n      make_array<Dim>(std::vector<std::uint8_t>(\n          (subcell_mesh.extents(0) + 2) *\n          subcell_mesh.extents().slice_away(0).product()))};\n\n  std::optional<std::array<gsl::span<std::uint8_t>, Dim>> recons_order{\n      std::array<gsl::span<std::uint8_t>, Dim>{}};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < gsl::at(recons_data.value(), i).size(); ++j) {\n      gsl::at(recons_data.value(), i)[j] = static_cast<double>(i) + 5.0;\n    }\n    gsl::at(recons_order.value(), i) =\n        gsl::make_span(gsl::at(recons_data.value(), i));\n  }\n\n  subcell::store_reconstruction_order_in_databox(make_not_null(&box),\n                                                 recons_order);\n\n  REQUIRE(db::get<subcell::Tags::ReconstructionOrder<Dim>>(box).has_value());\n  for (size_t d = 0; d < Dim; ++d) {\n    CHECK(db::get<subcell::Tags::ReconstructionOrder<Dim>>(box).value().get(\n              d) == static_cast<double>(d) + 5.0);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.ReconstructionOrder\",\n                  \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_SetInterpolators.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/CombineVolumeGhostData.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SetInterpolators.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename TargetFrame>\nauto make_grid_map(const size_t id) {\n  CAPTURE(id);\n  REQUIRE((id == 0 or id == 1));\n  using domain::make_coordinate_map_base;\n  using domain::CoordinateMaps::Affine;\n  if constexpr (Dim == 1) {\n    return make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n        Affine(-1.0, 1.0, 2.0, id == 0 ? 5.0 : 0.0));\n  } else if constexpr (Dim == 2) {\n    using domain::CoordinateMaps::ProductOf2Maps;\n    return make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n        ProductOf2Maps<Affine, Affine>(\n            Affine(-1.0, 1.0, -1.0, id == 0 ? -0.3 : -1.3),\n            Affine(-1.0, 1.0, -1.0, -0.8)));\n  } else {\n    using domain::CoordinateMaps::ProductOf3Maps;\n    return make_coordinate_map_base<Frame::BlockLogical, TargetFrame>(\n        ProductOf3Maps<Affine, Affine, Affine>(\n            Affine(-1.0, 1.0, -1.0, id == 0 ? -0.5 : -1.2),\n            Affine(-1.0, 1.0, -1.0, -0.8), Affine(-1.0, 1.0, 0.8, 1.0)));\n  }\n}\n\nclass DummyReconstructor {\n public:\n  static size_t ghost_zone_size() { return 3; }\n};\n\nnamespace Tags {\nstruct Reconstructor : db::SimpleTag {\n  using type = std::unique_ptr<DummyReconstructor>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nvoid test(const bool enable_extension, const size_t fd_to_fd_interp_order) {\n  // Domain setup:\n  //   | lower_xi | element |\n  //\n  // No neighbors in upper xi or in eta/zeta.\n  // We currently can only use enable_extension =true\n  // with always_use_subcell = true.\n  evolution::dg::subcell::SubcellOptions subcell_options(\n      4.0, 1, 2.0e-3, 2.0e-4, enable_extension, enable_extension,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n      std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1, fd_to_fd_interp_order);\n\n  const ::Mesh<Dim> dg_mesh{6, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n  const ::Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  const ElementId<Dim> element_id{0};\n  const ElementId<Dim> lower_xi_element_id{1};\n  DirectionMap<Dim, Neighbors<Dim>> element_neighbors{};\n  const DirectionalId<Dim> lower_xi_id{Direction<Dim>::lower_xi(),\n                                       lower_xi_element_id};\n  OrientationMap<Dim> orientation = OrientationMap<Dim>::create_aligned();\n  if constexpr (Dim == 1) {\n    orientation = OrientationMap<Dim>{{{Direction<Dim>::lower_xi()}}};\n    element_neighbors.insert(\n        std::pair{Direction<Dim>::lower_xi(),\n                  Neighbors<Dim>{{lower_xi_id.id()}, orientation}});\n  } else if constexpr (Dim == 2) {\n    orientation = OrientationMap<Dim>{\n        {{Direction<Dim>::lower_xi(), Direction<Dim>::lower_eta()}}};\n    element_neighbors.insert(\n        std::pair{Direction<Dim>::lower_xi(),\n                  Neighbors<Dim>{{lower_xi_id.id()}, orientation}});\n  } else if constexpr (Dim == 3) {\n    orientation = OrientationMap<Dim>{\n        {{Direction<Dim>::lower_xi(), Direction<Dim>::lower_eta(),\n          Direction<Dim>::upper_zeta()}}};\n    element_neighbors.insert(\n        std::pair{Direction<Dim>::lower_xi(),\n                  Neighbors<Dim>{{lower_xi_id.id()}, orientation}});\n  }\n  const Element<Dim> element{element_id, element_neighbors};\n\n  DirectionMap<Dim, BlockNeighbors<Dim>> block0_neighbors{};\n  block0_neighbors[Direction<Dim>::lower_xi()] =\n      BlockNeighbors<Dim>{1, orientation};\n  Block<Dim> block0{make_grid_map<Dim, Frame::Inertial>(0), 0,\n                    block0_neighbors};\n  block0.inject_time_dependent_map(\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{}));\n\n  DirectionMap<Dim, BlockNeighbors<Dim>> block1_neighbors{};\n  block1_neighbors[Direction<Dim>::lower_xi()] =\n      BlockNeighbors<Dim>{0, orientation.inverse_map()};\n  Block<Dim> block1{make_grid_map<Dim, Frame::Inertial>(1), 1,\n                    block1_neighbors};\n  block1.inject_time_dependent_map(\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{}));\n  std::vector<Block<Dim>> blocks{2};\n  blocks[0] = std::move(block0);\n  blocks[1] = std::move(block1);\n\n  const auto element_logical_coords = logical_coordinates(dg_mesh);\n  tnsr::I<DataVector, Dim, Frame::BlockLogical> block_logical_coords{};\n  for (size_t i = 0; i < Dim; ++i) {\n    block_logical_coords[i] = element_logical_coords[i];\n  }\n\n  // NOLINTNEXTLINE(google-build-using-namespace)\n  using namespace evolution::dg::subcell::Tags;\n  auto box = db::create<\n      db::AddSimpleTags<\n          InterpolatorsFromFdToNeighborFd<Dim>,\n          InterpolatorsFromDgToNeighborFd<Dim>,\n          InterpolatorsFromNeighborDgToFd<Dim>, ExtensionDirections<Dim>,\n          ::domain::Tags::Element<Dim>, ::domain::Tags::Domain<Dim>,\n          domain::Tags::Mesh<Dim>, evolution::dg::subcell::Tags::Mesh<Dim>,\n          ::domain::Tags::ElementMap<Dim, Frame::Grid>, Tags::Reconstructor,\n          evolution::dg::subcell::Tags::SubcellOptions<Dim>>,\n      db::AddComputeTags<\n          domain::Tags::LogicalCoordinates<Dim>,\n          domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<Dim, Frame::Grid>,\n              domain::Tags::Coordinates<Dim, Frame::ElementLogical>>,\n          evolution::dg::subcell::Tags::LogicalCoordinatesCompute<Dim>,\n          domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<Dim, Frame::Grid>,\n              evolution::dg::subcell::Tags::Coordinates<Dim,\n                                                        Frame::ElementLogical>,\n              evolution::dg::subcell::Tags::Coordinates>>>(\n      typename InterpolatorsFromFdToNeighborFd<Dim>::type{},\n      typename InterpolatorsFromDgToNeighborFd<Dim>::type{},\n      typename InterpolatorsFromNeighborDgToFd<Dim>::type{},\n      typename ExtensionDirections<Dim>::type{}, element,\n      Domain<Dim>{std::move(blocks)}, dg_mesh, subcell_mesh,\n      ElementMap{element_id, make_grid_map<Dim, Frame::Grid>(0)},\n      std::make_unique<DummyReconstructor>(), subcell_options);\n  db::mutate_apply<\n      evolution::dg::subcell::SetInterpolators<Dim, Tags::Reconstructor>>(\n      make_not_null(&box));\n\n  // Check that the interpolators were set.\n  REQUIRE(db::get<InterpolatorsFromDgToNeighborFd<Dim>>(box).size() == 1);\n  REQUIRE(db::get<InterpolatorsFromDgToNeighborFd<Dim>>(box)\n              .at(lower_xi_id)\n              .has_value());\n  REQUIRE(db::get<InterpolatorsFromFdToNeighborFd<Dim>>(box).size() == 1);\n  REQUIRE(db::get<InterpolatorsFromFdToNeighborFd<Dim>>(box)\n              .at(lower_xi_id)\n              .has_value());\n  REQUIRE(db::get<InterpolatorsFromNeighborDgToFd<Dim>>(box).size() == 1);\n  REQUIRE(db::get<InterpolatorsFromNeighborDgToFd<Dim>>(box)\n              .at(lower_xi_id)\n              .has_value());\n  if (enable_extension) {\n    const Direction<Dim> direction_to_extend_v =\n        db::get<ExtensionDirections<Dim>>(box)\n            .at(Direction<Dim>::lower_xi())\n            .direction_to_extend;\n\n    const auto& element_coords_in_grid =\n        db::get<evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Grid>>(\n            box);\n\n    const auto element_ghost_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, DummyReconstructor::ghost_zone_size(),\n            direction_to_extend_v);\n    const ElementMap my_element_map{element_id,\n                                    make_grid_map<Dim, Frame::Grid>(0)};\n    // transform the ghost coords to grid\n    const auto element_ghost_coords_in_grid =\n        my_element_map(element_ghost_coords);\n\n    const size_t volume_size = element_coords_in_grid.get(0).size();\n    const size_t ghost_size = element_ghost_coords_in_grid.get(0).size();\n\n    DataVector volume_data{Dim * volume_size};\n    DataVector ghost_data{Dim * ghost_size};\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < volume_size; ++j) {\n        volume_data[j + i * volume_size] = element_coords_in_grid.get(i)[j];\n      }\n      for (size_t k = 0; k < ghost_size; ++k) {\n        ghost_data[k + i * ghost_size] = element_ghost_coords_in_grid.get(i)[k];\n      }\n    }\n    const DataVector combined_data =\n        evolution::dg::subcell::combine_volume_ghost_data(\n            volume_data, ghost_data, subcell_mesh.extents(),\n            DummyReconstructor::ghost_zone_size(), direction_to_extend_v);\n\n    // Compute the lower-xi neighbor expected ghost zones.\n    const auto lower_xi_logical_ghost_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, DummyReconstructor::ghost_zone_size(),\n            Direction<Dim>::lower_xi());\n    const ElementMap lower_xi_element_map{lower_xi_element_id,\n                                          make_grid_map<Dim, Frame::Grid>(1)};\n    const auto lower_xi_neighbor_grid_ghost_coords =\n        lower_xi_element_map(lower_xi_logical_ghost_coords);\n\n    DataVector expected_result{ghost_size * Dim};\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < ghost_size; ++j) {\n        expected_result[j + i * ghost_size] =\n            lower_xi_neighbor_grid_ghost_coords.get(i)[j];\n      }\n    }\n\n    DataVector result{ghost_size * Dim};\n    auto result_span = gsl::make_span(result.data(), result.size());\n    db::get<InterpolatorsFromFdToNeighborFd<Dim>>(box)\n        .at(lower_xi_id)\n        .value()\n        .interpolate(\n            make_not_null(&result_span),\n            gsl::make_span(combined_data.data(), combined_data.size()));\n\n    CHECK_ITERABLE_APPROX(expected_result, result);\n  }\n\n  else {\n    // Interpolate to neighbor ghost zones.\n    tnsr::I<DataVector, Dim, Frame::Grid> dg_to_ghost_zones{};\n    tnsr::I<DataVector, Dim, Frame::Grid> fd_to_ghost_zones{};\n    for (size_t i = 0; i < Dim; ++i) {\n      dg_to_ghost_zones.get(i) =\n          db::get<InterpolatorsFromDgToNeighborFd<Dim>>(box)\n              .at(lower_xi_id)\n              .value()\n              .interpolate(\n                  db::get<domain::Tags::Coordinates<Dim, Frame::Grid>>(box).get(\n                      i));\n      fd_to_ghost_zones.get(i) =\n          db::get<InterpolatorsFromFdToNeighborFd<Dim>>(box)\n              .at(lower_xi_id)\n              .value()\n              .interpolate(db::get<evolution::dg::subcell::Tags::Coordinates<\n                               Dim, Frame::Grid>>(box)\n                               .get(i));\n    }\n    // Compute the lower-xi neighbor expected ghost zones.\n    const auto lower_xi_logical_ghost_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, DummyReconstructor::ghost_zone_size(),\n            Direction<Dim>::lower_xi());\n    const ElementMap lower_xi_element_map{lower_xi_element_id,\n                                          make_grid_map<Dim, Frame::Grid>(1)};\n    const auto lower_xi_neighbor_grid_ghost_coords =\n        lower_xi_element_map(lower_xi_logical_ghost_coords);\n    // Check interpolation worked correctly.\n    CHECK_ITERABLE_APPROX(dg_to_ghost_zones,\n                          lower_xi_neighbor_grid_ghost_coords);\n    CHECK_ITERABLE_APPROX(fd_to_ghost_zones,\n                          lower_xi_neighbor_grid_ghost_coords);\n\n    // Check interpolating lower-xi DG data works to our FD ghost zones.\n    const auto lower_xi_dg_grid_coords =\n        lower_xi_element_map(element_logical_coords);\n    tnsr::I<DataVector, Dim, Frame::Grid> lower_xi_dg_to_ghost_zones{};\n    for (size_t i = 0; i < Dim; ++i) {\n      lower_xi_dg_to_ghost_zones.get(i) =\n          db::get<InterpolatorsFromNeighborDgToFd<Dim>>(box)\n              .at(lower_xi_id)\n              .value()\n              .interpolate(lower_xi_dg_grid_coords.get(i));\n    }\n    const auto expected_lower_xi_dg_to_ghost_zones =\n        db::get<domain::Tags::ElementMap<Dim, Frame::Grid>>(box)(\n            lower_xi_logical_ghost_coords);\n    CHECK_ITERABLE_APPROX(lower_xi_dg_to_ghost_zones,\n                          expected_lower_xi_dg_to_ghost_zones);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.SetInterpolators\",\n                  \"[Evolution][Unit]\") {\n  for (const bool enable_extension : {false, true}) {\n    for (size_t fd_interp_order = 1; fd_interp_order <= 3; ++fd_interp_order) {\n      test<1>(enable_extension, fd_interp_order);\n      test<2>(enable_extension, fd_interp_order);\n      test<3>(enable_extension, fd_interp_order);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_SliceData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <numeric>\n#include <optional>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace {\nnamespace Tags {\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nvoid check_slice(\n    const Index<Dim>& volume_extents, const Index<Dim>& slice_extents,\n    const Direction<Dim>& direction, const size_t fixed_index,\n    const size_t ghost_slice, const size_t component_index,\n    const Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>>& volume_vars,\n    const DirectionMap<Dim, DataVector>& sliced_data) {\n  const size_t volume_offset = component_index * volume_extents.product();\n  const size_t slice_offset = component_index * slice_extents.product();\n  CAPTURE(volume_offset);\n  CAPTURE(slice_offset);\n\n  for (SliceIterator\n           volume_it(volume_extents, direction.dimension(), fixed_index),\n       slice_it(slice_extents, direction.dimension(), ghost_slice);\n       volume_it; ++volume_it, ++slice_it) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(*(volume_vars.data() + volume_offset + volume_it.volume_offset()) ==\n          sliced_data.at(direction)[slice_offset + slice_it.volume_offset()]);\n  }\n}\n\ntemplate <>\nvoid check_slice<1>(\n    const Index<1>& volume_extents, const Index<1>& slice_extents,\n    const Direction<1>& direction, const size_t fixed_index,\n    const size_t ghost_slice, const size_t component_index,\n    const Variables<tmpl::list<Tags::Scalar, Tags::Vector<1>>>& volume_vars,\n    const DirectionMap<1, DataVector>& sliced_data) {\n  const size_t volume_offset = component_index * volume_extents.product();\n  const size_t slice_offset = component_index * slice_extents.product();\n  CAPTURE(volume_offset);\n  CAPTURE(slice_offset);\n  for (SliceIterator volume_it(volume_extents, direction.dimension(),\n                               fixed_index);\n       volume_it; ++volume_it) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(*(volume_vars.data() + volume_offset + volume_it.volume_offset()) ==\n          sliced_data.at(direction)[slice_offset + ghost_slice]);\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_slice_data(\n    const std::optional<Direction<Dim>> do_not_slice_in_direction) {\n  CAPTURE(do_not_slice_in_direction);\n  const size_t additional_buffer_size = 7;\n  CAPTURE(additional_buffer_size);\n  const auto& all_directions = Direction<Dim>::all_directions();\n  for (size_t number_of_ghost_points = 1; number_of_ghost_points < 5;\n       ++number_of_ghost_points) {\n    for (size_t num_pts_1d = 5; num_pts_1d < 7; ++num_pts_1d) {\n      const Index<Dim> extents{num_pts_1d};\n      Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>> volume_vars{\n          extents.product()};\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      std::iota(volume_vars.data(), volume_vars.data() + volume_vars.size(),\n                0.0);\n\n      std::unordered_set<Direction<Dim>> directions_to_slice(\n          std::begin(all_directions), std::end(all_directions));\n      if (do_not_slice_in_direction.has_value()) {\n        directions_to_slice.erase(*do_not_slice_in_direction);\n      }\n\n      {\n        DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>\n            fd_to_neighbor_fd_interpolants{};\n        for (const auto& direction : directions_to_slice) {\n          fd_to_neighbor_fd_interpolants[DirectionalId<Dim>{\n              direction, ElementId<Dim>{0}}] = std::nullopt;\n        }\n        const auto sliced_data = subcell::slice_data(\n            volume_vars, extents, number_of_ghost_points, directions_to_slice,\n            additional_buffer_size, fd_to_neighbor_fd_interpolants);\n#ifdef SPECTRE_DEBUG\n        disable_floating_point_exceptions();\n        for (const auto& [_, data_in_direction] : sliced_data) {\n          for (const double& at_point : data_in_direction) {\n            CHECK(std::isnan(at_point));\n          }\n        }\n        enable_floating_point_exceptions();\n#endif\n      }\n\n      const auto sliced_data =\n          subcell::slice_data(volume_vars, extents, number_of_ghost_points,\n                              directions_to_slice, additional_buffer_size, {});\n      for (const auto& direction : all_directions) {\n        CAPTURE(direction);\n        if (directions_to_slice.count(direction) == 1) {\n          REQUIRE(sliced_data.count(direction) == 1);\n          Index<Dim> subcell_slice_extents = extents;\n          subcell_slice_extents[direction.dimension()] = number_of_ghost_points;\n          CAPTURE(subcell_slice_extents);\n          CHECK(sliced_data.at(direction).size() ==\n                subcell_slice_extents.product() *\n                        Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>>::\n                            number_of_independent_components +\n                    additional_buffer_size);\n\n          for (size_t component_index = 0;\n               component_index < volume_vars.number_of_independent_components;\n               ++component_index) {\n            for (size_t ghost_slice = 0; ghost_slice < number_of_ghost_points;\n                 ++ghost_slice) {\n              const size_t fixed_index = direction.side() == Side::Lower\n                                             ? ghost_slice\n                                             : extents[direction.dimension()] +\n                                                   ghost_slice -\n                                                   number_of_ghost_points;\n              CAPTURE(number_of_ghost_points);\n              CAPTURE(num_pts_1d);\n              CAPTURE(direction);\n              CAPTURE(component_index);\n              CAPTURE(ghost_slice);\n              CAPTURE(fixed_index);\n              CAPTURE(volume_vars);\n              CAPTURE(subcell_slice_extents);\n              // The additional buffer for the sliced data is filled with NaNs\n              // in Debug mode so we can't capture them here\n#ifndef SPECTRE_DEBUG\n              CAPTURE(sliced_data.at(direction));\n#endif\n              check_slice(extents, subcell_slice_extents, direction,\n                          fixed_index, ghost_slice, component_index,\n                          volume_vars, sliced_data);\n            }\n          }\n        } else {\n          CHECK(sliced_data.count(direction) == 0);\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  test_slice_data<Dim>(std::nullopt);\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    test_slice_data(std::optional{direction});\n  }\n}\n\n// [[TimeOut, 8]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.SliceData\", \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_SliceTensor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <numeric>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/SliceTensor.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// types of Tensor to test\nnamespace Tags {\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct TensorI : db::SimpleTag {\n  using type = ::tnsr::I<DataVector, Dim>;\n};\n\ntemplate <size_t Dim>\nstruct TensorIJ : db::SimpleTag {\n  using type = ::tnsr::IJ<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nvoid test() {\n  for (size_t num_mesh_pts_1d = 3; num_mesh_pts_1d < 5; ++num_mesh_pts_1d) {\n    // ghost zone size iterates up to the maximum possible value (mesh extent of\n    // each dimension)\n    for (size_t num_ghost_pts = 1; num_ghost_pts <= num_mesh_pts_1d;\n         ++num_ghost_pts) {\n      const Index<Dim> volume_extents{num_mesh_pts_1d};\n\n      // Create volume tensors and assign values\n      Variables<\n          tmpl::list<Tags::Scalar, Tags::TensorI<Dim>, Tags::TensorIJ<Dim>>>\n          volume_vars{volume_extents.product()};\n      std::iota(volume_vars.data(), volume_vars.data() + volume_vars.size(),\n                0.0);\n\n      auto& volume_scalar = get<Tags::Scalar>(volume_vars);\n      auto& volume_tensor = get<Tags::TensorI<Dim>>(volume_vars);\n      auto& volume_tensor_IJ = get<Tags::TensorIJ<Dim>>(volume_vars);\n\n      for (const auto& direction : Direction<Dim>::all_directions()) {\n        {\n          DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>\n              fd_to_neighbor_fd_interpolants{};\n          fd_to_neighbor_fd_interpolants[DirectionalId<Dim>{\n              direction, ElementId<Dim>{0}}] = std::nullopt;\n          const Scalar<DataVector> expected_sliced_scalar{\n              volume_extents.slice_away(direction.dimension()).product() *\n                  num_ghost_pts,\n              10.0};\n          Scalar<DataVector> sliced_scalar = expected_sliced_scalar;\n          evolution::dg::subcell::slice_tensor_for_subcell(\n              make_not_null(&sliced_scalar), volume_scalar, volume_extents,\n              num_ghost_pts, direction, fd_to_neighbor_fd_interpolants);\n#ifdef SPECTRE_DEBUG\n          disable_floating_point_exceptions();\n          CHECK(get(sliced_scalar).size() ==\n                get(expected_sliced_scalar).size());\n          for (const double value_at_point : get(sliced_scalar)) {\n            CHECK(std::isnan(value_at_point));\n          }\n          enable_floating_point_exceptions();\n#endif\n        }\n\n        // slice data\n        auto sliced_scalar = evolution::dg::subcell::slice_tensor_for_subcell(\n            volume_scalar, volume_extents, num_ghost_pts, direction, {});\n        auto sliced_tensor = evolution::dg::subcell::slice_tensor_for_subcell(\n            volume_tensor, volume_extents, num_ghost_pts, direction, {});\n        auto sliced_tensor_IJ =\n            evolution::dg::subcell::slice_tensor_for_subcell(\n                volume_tensor_IJ, volume_extents, num_ghost_pts, direction, {});\n\n        Index<Dim> sliced_extents = volume_extents;\n        sliced_extents[direction.dimension()] = num_ghost_pts;\n\n        for (size_t i_ghost = 0; i_ghost < num_ghost_pts; ++i_ghost) {\n          const size_t fixed_index =\n              direction.side() == Side::Lower\n                  ? i_ghost\n                  : volume_extents[direction.dimension()] + i_ghost -\n                        num_ghost_pts;\n\n          // check the result\n          for (SliceIterator volume_it(volume_extents, direction.dimension(),\n                                       fixed_index),\n               slice_it(sliced_extents, direction.dimension(), i_ghost);\n               volume_it; ++volume_it, ++slice_it) {\n            CHECK(*(get(volume_scalar).data() + volume_it.volume_offset()) ==\n                  get(sliced_scalar)[slice_it.volume_offset()]);\n\n            for (size_t i_tnsr = 0; i_tnsr < volume_tensor.size(); ++i_tnsr) {\n              CHECK(*(volume_tensor.get(i_tnsr).data() +\n                      volume_it.volume_offset()) ==\n                    sliced_tensor.get(i_tnsr)[slice_it.volume_offset()]);\n            }\n            for (size_t i_tnsr = 0; i_tnsr < volume_tensor_IJ.size();\n                 ++i_tnsr) {\n              CHECK(*(volume_tensor_IJ[i_tnsr].data() +\n                      volume_it.volume_offset()) ==\n                    sliced_tensor_IJ[i_tnsr][slice_it.volume_offset()]);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.SliceTensor\", \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_SliceVariable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <numeric>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/SliceVariable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// types of Tensor to test\nnamespace Tags {\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct TensorI : db::SimpleTag {\n  using type = ::tnsr::I<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nvoid test_slice(\n    const Variables<tmpl::list<Tags::Scalar, Tags::TensorI<Dim>>>& volume_vars,\n    const Index<Dim>& volume_extents, size_t num_ghost_pts,\n    const Direction<Dim>& direction) {\n  {\n    DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>\n        fd_to_neighbor_fd_interpolants{};\n    fd_to_neighbor_fd_interpolants[DirectionalId<Dim>{\n        direction, ElementId<Dim>{0}}] = std::nullopt;\n    const Variables<tmpl::list<Tags::Scalar, Tags::TensorI<Dim>>>\n        expected_sliced_vars{\n            volume_extents.slice_away(direction.dimension()).product() *\n                num_ghost_pts,\n            10};\n    Variables<tmpl::list<Tags::Scalar, Tags::TensorI<Dim>>> sliced_vars{\n        expected_sliced_vars};\n    evolution::dg::subcell::slice_variable(\n        make_not_null(&sliced_vars), volume_vars, volume_extents, num_ghost_pts,\n        direction, fd_to_neighbor_fd_interpolants);\n#ifdef SPECTRE_DEBUG\n    disable_floating_point_exceptions();\n    CHECK(sliced_vars.size() == expected_sliced_vars.size());\n    for (size_t i = 0; i < sliced_vars.size(); ++i) {\n      CHECK(std::isnan(\n          *std::next(sliced_vars.data(), static_cast<std::ptrdiff_t>(i))));\n    }\n    CHECK(sliced_vars != expected_sliced_vars);\n    enable_floating_point_exceptions();\n#endif\n  }\n\n  // retrieve a sliced Variables object\n  auto sliced_vars = evolution::dg::subcell::slice_variable(\n      volume_vars, volume_extents, num_ghost_pts, direction, {});\n\n  Index<Dim> sliced_extents = volume_extents;\n  sliced_extents[direction.dimension()] = num_ghost_pts;\n\n  // for each ghost slices,\n  for (size_t i_ghost = 0; i_ghost < num_ghost_pts; ++i_ghost) {\n    // for each of Variables components ( Scalar x 1 + TensorI x Dim )\n    for (size_t component_index = 0;\n         component_index < volume_vars.number_of_independent_components;\n         ++component_index) {\n      const size_t fixed_index =\n          direction.side() == Side::Lower\n              ? i_ghost\n              : volume_extents[direction.dimension()] + i_ghost - num_ghost_pts;\n\n      const size_t volume_offset = component_index * volume_extents.product();\n      const size_t slice_offset = component_index * sliced_extents.product();\n\n      // check the result\n      for (SliceIterator\n               volume_it(volume_extents, direction.dimension(), fixed_index),\n           slice_it(sliced_extents, direction.dimension(), i_ghost);\n           volume_it; ++volume_it, ++slice_it) {\n        CHECK(\n            *(volume_vars.data() + volume_offset + volume_it.volume_offset()) ==\n            *(sliced_vars.data() + slice_offset + slice_it.volume_offset()));\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  for (size_t num_mesh_pts_1d = 3; num_mesh_pts_1d < 5; ++num_mesh_pts_1d) {\n    // ghost zone size iterates up to the maximum possible value (mesh extent of\n    // each dimension)\n    for (size_t num_ghost_pts = 1; num_ghost_pts <= num_mesh_pts_1d;\n         ++num_ghost_pts) {\n      const Index<Dim> volume_extents{num_mesh_pts_1d};\n\n      // Create volume tensors and assign values\n      Variables<tmpl::list<Tags::Scalar, Tags::TensorI<Dim>>> volume_vars{\n          volume_extents.product()};\n      std::iota(volume_vars.data(), volume_vars.data() + volume_vars.size(),\n                0.0);\n\n      for (const auto& direction : Direction<Dim>::all_directions()) {\n        // check slicing for each direction\n        test_slice(volume_vars, volume_extents, num_ghost_pts, direction);\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.SliceVariable\", \"[Evolution][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_SubcellOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"Domain/Creators/Cylinder.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace evolution::dg::subcell {\nnamespace {\nvoid test_impl(const std::vector<double>& expected_values,\n               const size_t incorrect_value_index) {\n  std::vector<double> values = expected_values;\n  values[incorrect_value_index] += incorrect_value_index == 1 ? 1.0 : 0.1;\n\n  const fd::ReconstructionMethod recons_method =\n      fd::ReconstructionMethod::AllDimsAtOnce;\n\n  CHECK(SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1) !=\n        SubcellOptions(values[0], static_cast<size_t>(values[1]), values[2],\n                       values[3], false, false, recons_method, false,\n                       std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK_FALSE(\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1) ==\n      SubcellOptions(values[0], static_cast<size_t>(values[1]), values[2],\n                     values[3], false, false, recons_method, false,\n                     std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n\n  CHECK(SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1) !=\n        SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], true, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK_FALSE(\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1) ==\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], true, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n\n  CHECK(SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1) !=\n        SubcellOptions(expected_values[0],\n                       static_cast<size_t>(expected_values[1]),\n                       expected_values[2], expected_values[3], false, false,\n                       fd::ReconstructionMethod::DimByDim, false, std::nullopt,\n                       ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK_FALSE(SubcellOptions(expected_values[0],\n                             static_cast<size_t>(expected_values[1]),\n                             expected_values[2], expected_values[3], false,\n                             false, recons_method, false, std::nullopt,\n                             ::fd::DerivativeOrder::Two, 1, 1, 1) ==\n              SubcellOptions(\n                  expected_values[0], static_cast<size_t>(expected_values[1]),\n                  expected_values[2], expected_values[3], false, false,\n                  fd::ReconstructionMethod::DimByDim, false, std::nullopt,\n                  ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK_FALSE(\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1) ==\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          true, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK_FALSE(\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Four, 1, 1, 1) ==\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK_FALSE(\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 2, 1, 1) ==\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK_FALSE(\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 2, 1) ==\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK_FALSE(\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 2) ==\n      SubcellOptions(\n          expected_values[0], static_cast<size_t>(expected_values[1]),\n          expected_values[2], expected_values[3], false, false, recons_method,\n          false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK(SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1, 1) ==\n        SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK(SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1, 2) !=\n        SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK(SubcellOptions(expected_values[0],\n                       static_cast<size_t>(expected_values[1]),\n                       expected_values[2], expected_values[3], false, false,\n                       recons_method, false, std::nullopt,\n                       ::fd::DerivativeOrder::Two, 1, 1, 1, 1, std::nullopt) ==\n        SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n  CHECK(SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1, 1, 8) !=\n        SubcellOptions(\n            expected_values[0], static_cast<size_t>(expected_values[1]),\n            expected_values[2], expected_values[3], false, false, recons_method,\n            false, std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1));\n}\n\ntemplate <bool LocalTimeStepping>\nstruct Metavariables {\n  static constexpr bool local_time_stepping = LocalTimeStepping;\n};\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.SubcellOptions\",\n                  \"[Evolution][Unit]\") {\n  const std::vector<double> expected_values{4.0, static_cast<double>(1_st),\n                                            2.0e-3, 2.0e-4};\n  for (size_t i = 0; i < expected_values.size(); ++i) {\n    test_impl(expected_values, i);\n  }\n\n  SubcellOptions options(expected_values[0],\n                         static_cast<size_t>(expected_values[1]),\n                         expected_values[2], expected_values[3], true, true,\n                         fd::ReconstructionMethod::DimByDim, true, std::nullopt,\n                         ::fd::DerivativeOrder::Four, 1, 1, 1, 2, 8);\n  const SubcellOptions deserialized_options =\n      serialize_and_deserialize(options);\n  CHECK(options == deserialized_options);\n\n  CHECK(options == TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                                Metavariables<true>>(\n                       \"TroubledCellIndicator:\\n\"\n                       \"  PerssonTci:\\n\"\n                       \"    Exponent: 4.0\\n\"\n                       \"    NumHighestModes: 1\\n\"\n                       \"  RdmpTci:\\n\"\n                       \"    Delta0: 2.0e-3\\n\"\n                       \"    Epsilon: 2.0e-4\\n\"\n                       \"  FdToDgTci:\\n\"\n                       \"    NumberOfStepsBetweenTciCalls: 1\\n\"\n                       \"    MinTciCallsAfterRollback: 1\\n\"\n                       \"    MinimumClearTcis: 1\\n\"\n                       \"  AlwaysUseSubcells: true\\n\"\n                       \"  EnableExtensionDirections: true\\n\"\n                       \"  UseHalo: true\\n\"\n                       \"  OnlyDgBlocksAndGroups: None\\n\"\n                       \"SubcellToDgReconstructionMethod: DimByDim\\n\"\n                       \"FiniteDifferenceDerivativeOrder: 4\\n\"\n                       \"FdInterpolationOrder: 2\\n\"\n                       \"LtsStepsPerSlab: 8\"));\n\n  INFO(\"Test with block names and groups\");\n  const domain::creators::Cylinder cylinder{2.0,   10.0, 1.0,  8.0,\n                                            false, 0_st, 5_st, false};\n  const std::string opts_no_blocks =\n      \"SubcellToDgReconstructionMethod: DimByDim\\n\"\n      \"FiniteDifferenceDerivativeOrder: 4\\n\"\n      \"FdInterpolationOrder: 2\\n\"\n      \"TroubledCellIndicator:\\n\"\n      \"  PerssonTci:\\n\"\n      \"    Exponent: 4.0\\n\"\n      \"    NumHighestModes: 1\\n\"\n      \"  RdmpTci:\\n\"\n      \"    Delta0: 2.0e-3\\n\"\n      \"    Epsilon: 2.0e-4\\n\"\n      \"  FdToDgTci:\\n\"\n      \"    NumberOfStepsBetweenTciCalls: 1\\n\"\n      \"    MinTciCallsAfterRollback: 1\\n\"\n      \"    MinimumClearTcis: 1\\n\"\n      \"  AlwaysUseSubcells: true\\n\"\n      \"  EnableExtensionDirections: true\\n\"\n      \"  UseHalo: true\\n\";\n  CHECK_THROWS_WITH(\n      SubcellOptions(TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                                  Metavariables<false>>(\n                         opts_no_blocks + \"  OnlyDgBlocksAndGroups: [blah]\\n\"),\n                     cylinder),\n      Catch::Matchers::ContainsSubstring(\"The block or group 'blah'\"));\n\n  CHECK(SubcellOptions{\n            TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                         Metavariables<false>>(\n                opts_no_blocks + \"  OnlyDgBlocksAndGroups: [InnerCube]\\n\"),\n            cylinder}\n            .only_dg_block_ids()\n            .size() == 1);\n  CHECK(SubcellOptions{\n            TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                         Metavariables<false>>(\n                opts_no_blocks + \"  OnlyDgBlocksAndGroups: [Wedges]\\n\"),\n            cylinder}\n            .only_dg_block_ids()\n            .size() == 4);\n\n  CHECK_THROWS_WITH((TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                                  Metavariables<true>>(\n                        opts_no_blocks + \"  OnlyDgBlocksAndGroups: [Wedges]\\n\"\n                                         \"LtsStepsPerSlab: 0\\n\")),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value 0 is below the lower bound of 1\"));\n  CHECK_THROWS_WITH((TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                                  Metavariables<true>>(\n                        opts_no_blocks + \"  OnlyDgBlocksAndGroups: [Wedges]\\n\"\n                                         \"LtsStepsPerSlab: 100000000000\\n\")),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value 100000000000 is above the upper bound of\"));\n  CHECK_THROWS_WITH((TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                                  Metavariables<true>>(\n                        opts_no_blocks + \"  OnlyDgBlocksAndGroups: [Wedges]\\n\"\n                                         \"LtsStepsPerSlab: 10\\n\")),\n                    Catch::Matchers::ContainsSubstring(\n                        \"LtsStepsPerSlab must be a power of 2\"));\n  CHECK(TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                     Metavariables<true>>(\n            opts_no_blocks + \"  OnlyDgBlocksAndGroups: [Wedges]\\n\"\n                             \"LtsStepsPerSlab: 16\\n\")\n            .lts_steps_per_slab() == 16);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_option_tag<OptionTags::SubcellOptions,\n                                    Metavariables<false>>(\n           opts_no_blocks + \"  OnlyDgBlocksAndGroups: [Wedges]\\n\")\n           .lts_steps_per_slab()),\n      Catch::Matchers::ContainsSubstring(\"lts_steps_per_slab in GTS\"));\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n}  // namespace evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/DataForRdmpTci.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostZoneInverseJacobian.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/MethodOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverMeshVelocity.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcells.hpp\"\n#include \"Evolution/DgSubcell/Tags/ReconstructionOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/StepsSinceTciCall.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciCallsSinceRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciGridHistory.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n\nnamespace subcell = evolution::dg::subcell;\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, 3, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nvoid test(const bool moving_mesh) {\n  TestHelpers::db::test_simple_tag<subcell::Tags::SubcellOptions<Dim>>(\n      \"SubcellOptions\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::Mesh<Dim>>(\"Subcell(Mesh)\");\n  TestHelpers::db::test_compute_tag<subcell::Tags::MeshCompute<Dim>>(\n      \"Subcell(Mesh)\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::GhostDataForReconstruction<Dim>>(\n      \"GhostDataForReconstruction\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::GhostZoneInverseJacobian<Dim>>(\"GhostZoneInverseJacobian\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::NeighborTciDecisions<Dim>>(\n      \"NeighborTciDecisions\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::DataForRdmpTci>(\n      \"DataForRdmpTci\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::Coordinates<Dim, Frame::ElementLogical>>(\n      \"ElementLogicalCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::Coordinates<Dim, Frame::Grid>>(\"GridCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::Coordinates<Dim, Frame::Inertial>>(\"InertialCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      subcell::fd::Tags::InverseJacobianLogicalToGrid<Dim>>(\n      \"InverseJacobian(Logical,Grid)\");\n  TestHelpers::db::test_simple_tag<\n      subcell::fd::Tags::InverseJacobianLogicalToInertial<Dim>>(\n      \"InverseJacobian(Logical,Inertial)\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::OnSubcellFaces<Var1, Dim>>(\n      \"OnSubcellFaces(Var1)\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::OnSubcellFaces<\n      ::Tags::Variables<tmpl::list<Var1, Var2>>, Dim>>(\n      \"OnSubcellFaces(Variables(Var1,Var2))\");\n  TestHelpers::db::test_simple_tag<\n      ::Events::Tags::ObserverCoordinates<Dim, Frame::Inertial>>(\n      \"InertialCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      ::Events::Tags::ObserverCoordinates<Dim, Frame::Grid>>(\"GridCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::CellCenteredFlux<tmpl::list<Var1, Var2>, Dim>>(\n      \"CellCenteredFlux\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::CellCenteredFlux<\n      tmpl::list<Var1, Var2>, Dim, Frame::Grid>>(\"CellCenteredFlux\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::ReconstructionOrder<Dim>>(\n      \"ReconstructionOrder\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::MethodOrder<Dim>>(\n      \"MethodOrder\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>(\n      \"InterpolatorsFromFdToNeighborFd\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::InterpolatorsFromDgToNeighborFd<Dim>>(\n      \"InterpolatorsFromDgToNeighborFd\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::InterpolatorsFromNeighborDgToFd<Dim>>(\n      \"InterpolatorsFromNeighborDgToFd\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::TciCallsSinceRollback>(\n      \"TciCallsSinceRollback\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::StepsSinceTciCall>(\n      \"StepsSinceTciCall\");\n\n  TestHelpers::db::test_compute_tag<\n      subcell::Tags::LogicalCoordinatesCompute<Dim>>(\n      \"ElementLogicalCoordinates\");\n  TestHelpers::db::test_compute_tag<subcell::Tags::InertialCoordinatesCompute<\n      ::domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                    Frame::Inertial>>>(\n      \"InertialCoordinates\");\n\n  TestHelpers::db::test_compute_tag<\n      subcell::fd::Tags::InverseJacobianLogicalToInertialCompute<\n          ::domain::CoordinateMaps::Tags::CoordinateMap<\n              Dim, Frame::ElementLogical, Frame::Inertial>,\n          Dim>>(\"InverseJacobian(Logical,Inertial)\");\n  TestHelpers::db::test_compute_tag<\n      subcell::Tags::ObserverInverseJacobianCompute<Dim, Frame::ElementLogical,\n                                                    Frame::Grid>>(\n      \"InverseJacobian(ElementLogical,Grid)\");\n  TestHelpers::db::test_compute_tag<\n      subcell::Tags::ObserverInverseJacobianCompute<Dim, Frame::ElementLogical,\n                                                    Frame::Inertial>>(\n      \"InverseJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_compute_tag<\n      subcell::Tags::ObserverInverseJacobianCompute<Dim, Frame::Grid,\n                                                    Frame::Inertial>>(\n      \"InverseJacobian(Grid,Inertial)\");\n\n  TestHelpers::db::test_compute_tag<\n      subcell::Tags::ObserverJacobianAndDetInvJacobianCompute<\n          Dim, Frame::ElementLogical, Frame::Grid>>(\n      \"Variables(DetInvJacobian(ElementLogical,Grid),Jacobian(ElementLogical,\"\n      \"Grid))\");\n  TestHelpers::db::test_compute_tag<\n      subcell::Tags::ObserverJacobianAndDetInvJacobianCompute<\n          Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"Variables(DetInvJacobian(ElementLogical,Inertial),Jacobian(\"\n      \"ElementLogical,Inertial))\");\n  TestHelpers::db::test_compute_tag<\n      subcell::Tags::ObserverJacobianAndDetInvJacobianCompute<Dim, Frame::Grid,\n                                                              Frame::Inertial>>(\n      \"Variables(DetInvJacobian(Grid,Inertial),Jacobian(Grid,Inertial))\");\n  TestHelpers::db::test_compute_tag<subcell::Tags::TciStatusCompute<Dim>>(\n      \"TciStatus\");\n  TestHelpers::db::test_compute_tag<subcell::Tags::MethodOrderCompute<Dim>>(\n      \"MethodOrder\");\n  TestHelpers::db::test_compute_tag<\n      subcell::Tags::ObserverMeshVelocityCompute<Dim>>(\"InertialMeshVelocity\");\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"translation\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          0.0, std::array<DataVector, 3>{{{Dim, 0.0}, {Dim, -4.3}, {Dim, 0.0}}},\n          100.0);\n  const auto grid_to_inertial_map =\n      moving_mesh\n          ? domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n                domain::CoordinateMaps::TimeDependent::Translation<Dim>{\n                    \"translation\"})\n          : domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n                domain::CoordinateMaps::Identity<Dim>{});\n\n  const int tci_decision = 22;  // some non-zero value\n  const double time = 1.3;\n  const Mesh<Dim> dg_mesh(4, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto);\n  using ReconsOrder = typename subcell::Tags::ReconstructionOrder<Dim>::type;\n  auto active_coords_box = db::create<\n      db::AddSimpleTags<domain::Tags::ElementMap<Dim, Frame::Grid>,\n                        domain::CoordinateMaps::Tags::CoordinateMap<\n                            Dim, Frame::Grid, Frame::Inertial>,\n                        domain::Tags::FunctionsOfTimeInitialize, ::Tags::Time,\n                        ::domain::Tags::Mesh<Dim>, subcell::Tags::ActiveGrid,\n                        subcell::Tags::TciDecision,\n                        subcell::Tags::ReconstructionOrder<Dim>,\n                        subcell::Tags::SubcellOptions<Dim>>,\n      db::AddComputeTags<\n          domain::Tags::LogicalCoordinates<Dim>,\n          domain::Tags::MappedCoordinates<\n              domain::Tags::ElementMap<Dim, Frame::Grid>,\n              domain::Tags::Coordinates<Dim, Frame::ElementLogical>>,\n          domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<\n              domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                          Frame::Inertial>>,\n          domain::Tags::InertialFromGridCoordinatesCompute<Dim>,\n          domain::Tags::InertialMeshVelocityCompute<Dim>,\n\n          subcell::Tags::MeshCompute<Dim>,\n          subcell::Tags::LogicalCoordinatesCompute<Dim>,\n          subcell::Tags::ObserverMeshCompute<Dim>,\n\n          domain::Tags::MappedCoordinates<\n              domain::Tags::ElementMap<Dim, Frame::Grid>,\n              evolution::dg::subcell::Tags::Coordinates<Dim,\n                                                        Frame::ElementLogical>,\n              evolution::dg::subcell::Tags::Coordinates>,\n          subcell::Tags::InertialCoordinatesCompute<\n              ::domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                            Frame::Inertial>>,\n\n          subcell::Tags::ObserverCoordinatesCompute<Dim, Frame::ElementLogical>,\n          subcell::Tags::ObserverCoordinatesCompute<Dim, Frame::Grid>,\n          subcell::Tags::ObserverCoordinatesCompute<Dim, Frame::Inertial>,\n\n          subcell::Tags::ObserverInverseJacobianCompute<\n              Dim, Frame::ElementLogical, Frame::Grid>,\n          subcell::Tags::ObserverInverseJacobianCompute<\n              Dim, Frame::ElementLogical, Frame::Inertial>,\n          subcell::Tags::ObserverInverseJacobianCompute<Dim, Frame::Grid,\n                                                        Frame::Inertial>,\n          subcell::Tags::ObserverJacobianAndDetInvJacobianCompute<\n              Dim, Frame::ElementLogical, Frame::Grid>,\n          subcell::Tags::ObserverJacobianAndDetInvJacobianCompute<\n              Dim, Frame::ElementLogical, Frame::Inertial>,\n          subcell::Tags::ObserverJacobianAndDetInvJacobianCompute<\n              Dim, Frame::Grid, Frame::Inertial>,\n          subcell::Tags::TciStatusCompute<Dim>,\n          subcell::Tags::MethodOrderCompute<Dim>,\n          subcell::Tags::ObserverMeshVelocityCompute<Dim>>>(\n      ElementMap<Dim, Frame::Grid>{\n          ElementId<Dim>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<Dim>{})},\n      grid_to_inertial_map->get_clone(), clone_unique_ptrs(functions_of_time),\n      time, dg_mesh, subcell::ActiveGrid::Dg, tci_decision, ReconsOrder{},\n      evolution::dg::subcell::SubcellOptions{\n          4.0,\n          1_st,\n          1.0e-7,\n          1.0e-7,\n          false,\n          false,\n          evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n          false,\n          {},\n          ::fd::DerivativeOrder::Two,\n          1,\n          1,\n          1});\n  const auto check_box = [&active_coords_box, &grid_to_inertial_map,\n                          &moving_mesh,\n                          &tci_decision](const Mesh<Dim>& expected_mesh) {\n    (void)tci_decision;  // Incorrect compiler warning.\n    CHECK(db::get<::Events::Tags::ObserverMesh<Dim>>(active_coords_box) ==\n          expected_mesh);\n    const auto expected_logical_coords = logical_coordinates(expected_mesh);\n    CHECK(db::get<\n              ::Events::Tags::ObserverCoordinates<Dim, Frame::ElementLogical>>(\n              active_coords_box) == expected_logical_coords);\n    const auto expected_grid_coords =\n        db::get<domain::Tags::ElementMap<Dim, Frame::Grid>>(active_coords_box)(\n            expected_logical_coords);\n    CHECK(db::get<::Events::Tags::ObserverCoordinates<Dim, Frame::Grid>>(\n              active_coords_box) == expected_grid_coords);\n    const auto expected_inertial_coords =\n        db::get<domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                            Frame::Inertial>>(\n            active_coords_box)(\n            expected_grid_coords, db::get<::Tags::Time>(active_coords_box),\n            db::get<domain::Tags::FunctionsOfTime>(active_coords_box));\n    CHECK(db::get<::Events::Tags::ObserverCoordinates<Dim, Frame::Inertial>>(\n              active_coords_box) == expected_inertial_coords);\n\n    const auto expected_inv_jac_logical_to_grid =\n        db::get<domain::Tags::ElementMap<Dim, Frame::Grid>>(active_coords_box)\n            .inv_jacobian(expected_logical_coords);\n    CHECK(db::get<::Events::Tags::ObserverInverseJacobian<\n              Dim, Frame::ElementLogical, Frame::Grid>>(active_coords_box) ==\n          expected_inv_jac_logical_to_grid);\n\n    const auto expected_inv_jac_grid_to_inertial =\n        db::get<domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                            Frame::Inertial>>(\n            active_coords_box)\n            .inv_jacobian(\n                expected_grid_coords, db::get<::Tags::Time>(active_coords_box),\n                db::get<domain::Tags::FunctionsOfTime>(active_coords_box));\n    CHECK(db::get<::Events::Tags::ObserverInverseJacobian<Dim, Frame::Grid,\n                                                          Frame::Inertial>>(\n              active_coords_box) == expected_inv_jac_grid_to_inertial);\n\n    InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n        expected_inv_jac_logical_to_inertial{};\n    for (size_t logical_i = 0; logical_i < Dim; ++logical_i) {\n      for (size_t inertial_i = 0; inertial_i < Dim; ++inertial_i) {\n        expected_inv_jac_logical_to_inertial.get(logical_i, inertial_i) =\n            expected_inv_jac_logical_to_grid.get(logical_i, 0) *\n            expected_inv_jac_grid_to_inertial.get(0, inertial_i);\n        for (size_t grid_i = 1; grid_i < Dim; ++grid_i) {\n          expected_inv_jac_logical_to_inertial.get(logical_i, inertial_i) +=\n              expected_inv_jac_logical_to_grid.get(logical_i, grid_i) *\n              expected_inv_jac_grid_to_inertial.get(grid_i, inertial_i);\n        }\n      }\n    }\n\n    CHECK(db::get<::Events::Tags::ObserverInverseJacobian<\n              Dim, Frame::ElementLogical, Frame::Inertial>>(\n              active_coords_box) == expected_inv_jac_logical_to_inertial);\n\n    const auto [expected_det_inv_jac_logical_to_grid,\n                expected_jac_logical_to_grid] =\n        determinant_and_inverse(expected_inv_jac_logical_to_grid);\n    const auto [expected_det_inv_jac_grid_to_inertial,\n                expected_jac_grid_to_inertial] =\n        determinant_and_inverse(expected_inv_jac_grid_to_inertial);\n    const auto [expected_det_inv_jac_logical_to_inertial,\n                expected_jac_logical_to_inertial] =\n        determinant_and_inverse(expected_inv_jac_logical_to_inertial);\n\n    CHECK(db::get<::Events::Tags::ObserverJacobian<Dim, Frame::ElementLogical,\n                                                   Frame::Grid>>(\n              active_coords_box) == expected_jac_logical_to_grid);\n    CHECK(db::get<::Events::Tags::ObserverDetInvJacobian<Frame::ElementLogical,\n                                                         Frame::Grid>>(\n              active_coords_box) == expected_det_inv_jac_logical_to_grid);\n\n    CHECK(db::get<::Events::Tags::ObserverJacobian<Dim, Frame::Grid,\n                                                   Frame::Inertial>>(\n              active_coords_box) == expected_jac_grid_to_inertial);\n    CHECK(db::get<::Events::Tags::ObserverDetInvJacobian<Frame::Grid,\n                                                         Frame::Inertial>>(\n              active_coords_box) == expected_det_inv_jac_grid_to_inertial);\n\n    CHECK(db::get<::Events::Tags::ObserverJacobian<Dim, Frame::ElementLogical,\n                                                   Frame::Inertial>>(\n              active_coords_box) == expected_jac_logical_to_inertial);\n    CHECK(db::get<::Events::Tags::ObserverDetInvJacobian<Frame::ElementLogical,\n                                                         Frame::Inertial>>(\n              active_coords_box) == expected_det_inv_jac_logical_to_inertial);\n    CHECK(db::get<subcell::Tags::TciStatus>(active_coords_box) ==\n          Scalar<DataVector>(expected_mesh.number_of_grid_points(),\n                             static_cast<double>(tci_decision)));\n    if (db::get<subcell::Tags::ActiveGrid>(active_coords_box) ==\n        subcell::ActiveGrid::Dg) {\n      REQUIRE(db::get<subcell::Tags::MethodOrder<Dim>>(active_coords_box)\n                  .has_value());\n      for (size_t i = 0; i < Dim; ++i) {\n        CHECK(db::get<subcell::Tags::MethodOrder<Dim>>(active_coords_box)\n                  .value()[i] ==\n              DataVector{expected_mesh.number_of_grid_points(),\n                         static_cast<double>(expected_mesh.extents(i))});\n      }\n    } else {\n      if (db::get<subcell::Tags::ReconstructionOrder<Dim>>(active_coords_box)\n              .has_value()) {\n        CHECK(db::get<subcell::Tags::MethodOrder<Dim>>(active_coords_box) ==\n              db::get<subcell::Tags::ReconstructionOrder<Dim>>(\n                  active_coords_box));\n      } else {\n        for (size_t i = 0; i < Dim; ++i) {\n          CHECK(db::get<subcell::Tags::MethodOrder<Dim>>(active_coords_box)\n                    .value()[i] ==\n                DataVector{expected_mesh.number_of_grid_points(),\n                           static_cast<double>(static_cast<int>(\n                               db::get<subcell::Tags::SubcellOptions<Dim>>(\n                                   active_coords_box)\n                                   .finite_difference_derivative_order()))});\n        }\n      }\n    }\n    if (moving_mesh) {\n      REQUIRE(\n          db::get<Events::Tags::ObserverMeshVelocity<Dim>>(active_coords_box)\n              .has_value());\n      const auto& mesh_velocity =\n          db::get<Events::Tags::ObserverMeshVelocity<Dim>>(active_coords_box)\n              .value();\n      const auto [coords, inv_jac, jac, expected_mesh_velocity] =\n          grid_to_inertial_map->coords_frame_velocity_jacobians(\n              db::get<::Events::Tags::ObserverCoordinates<Dim, Frame::Grid>>(\n                  active_coords_box),\n              db::get<::Tags::Time>(active_coords_box),\n              db::get<domain::Tags::FunctionsOfTime>(active_coords_box));\n      CHECK_ITERABLE_APPROX(mesh_velocity, expected_mesh_velocity);\n    } else {\n      CHECK_FALSE(\n          db::get<Events::Tags::ObserverMeshVelocity<Dim>>(active_coords_box)\n              .has_value());\n    }\n  };\n\n  check_box(db::get<domain::Tags::Mesh<Dim>>(active_coords_box));\n  db::mutate<subcell::Tags::ActiveGrid>(\n      [](const auto active_grid_ptr) {\n        *active_grid_ptr = subcell::ActiveGrid::Subcell;\n      },\n      make_not_null(&active_coords_box));\n  check_box(db::get<subcell::Tags::Mesh<Dim>>(active_coords_box));\n  db::mutate<subcell::Tags::ReconstructionOrder<Dim>>(\n      [](const auto recons_order_ptr, const size_t num_pts) {\n        (*recons_order_ptr) = ReconsOrder{num_pts};\n        for (size_t i = 0; i < Dim; ++i) {\n          recons_order_ptr->value()[i] = static_cast<double>(i) + 3.0;\n        }\n      },\n      make_not_null(&active_coords_box),\n      db::get<subcell::Tags::Mesh<Dim>>(active_coords_box)\n          .number_of_grid_points());\n  check_box(db::get<subcell::Tags::Mesh<Dim>>(active_coords_box));\n\n  TestHelpers::db::test_compute_tag<subcell::Tags::ObserverMeshCompute<Dim>>(\n      \"ObserverMesh\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Tags\", \"[Evolution][Unit]\") {\n  TestHelpers::db::test_simple_tag<subcell::Tags::ActiveGrid>(\"ActiveGrid\");\n  TestHelpers::db::test_simple_tag<\n      subcell::fd::Tags::DetInverseJacobianLogicalToGrid>(\n      \"Det(InverseJacobian(Logical,Grid))\");\n  TestHelpers::db::test_simple_tag<\n      subcell::fd::Tags::DetInverseJacobianLogicalToInertial>(\n      \"Det(InverseJacobian(Logical,Inertial))\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::DidRollback>(\"DidRollback\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::Inactive<Var1>>(\n      \"Inactive(Var1)\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::Inactive<::Tags::Variables<tmpl::list<Var1, Var2>>>>(\n      \"Variables(Inactive(Var1),Inactive(Var2))\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::OnSubcells<Var1>>(\n      \"OnSubcells(Var1)\");\n  TestHelpers::db::test_simple_tag<\n      subcell::Tags::OnSubcells<::Tags::Variables<tmpl::list<Var1, Var2>>>>(\n      \"Variables(OnSubcells(Var1),OnSubcells(Var2))\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::TciGridHistory>(\n      \"TciGridHistory\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::TciStatus>(\"TciStatus\");\n  TestHelpers::db::test_simple_tag<subcell::Tags::TciDecision>(\"TciDecision\");\n\n  for (const bool moving_mesh : {false, true}) {\n    test<1>(moving_mesh);\n    test<2>(moving_mesh);\n    test<3>(moving_mesh);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DgSubcell/Test_TwoMeshRdmpTci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/TwoMeshRdmpTci.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nDataVector soln(const tnsr::I<DataVector, Dim, Frame::ElementLogical>& coords,\n                const bool add_discontinuity) {\n  DataVector result =\n      Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n          1, get<0>(coords));\n  for (size_t d = 1; d < Dim; ++d) {\n    result += Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n        1, coords.get(d));\n  }\n\n  if (add_discontinuity) {\n    const double max_value = max(abs(result));\n    for (size_t i = 0; i < result.size() / 2; ++i) {\n      result[i] += 10.0 * max_value;\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test_two_mesh_rdmp_impl(const size_t num_pts_1d,\n                             const size_t tensor_component_to_modify,\n                             const double rdmp_delta0,\n                             const double rdmp_epsilon,\n                             const int expected_tci_triggered) {\n  CAPTURE(Dim);\n  CAPTURE(num_pts_1d);\n  CAPTURE(tensor_component_to_modify);\n  CAPTURE(rdmp_delta0);\n  CAPTURE(rdmp_epsilon);\n  CAPTURE(expected_tci_triggered);\n  const Mesh<Dim> dg_mesh{num_pts_1d, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh{2 * num_pts_1d - 1,\n                               Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::FaceCentered};\n  const auto logical_coords = logical_coordinates(dg_mesh);\n\n  Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>> dg_vars(\n      dg_mesh.number_of_grid_points());\n  get(get<Tags::Scalar>(dg_vars)) =\n      soln(logical_coords, tensor_component_to_modify == 0);\n  for (size_t d = 0; d < Dim; ++d) {\n    get<Tags::Vector<Dim>>(dg_vars).get(d) =\n        (d + 0.3) * soln(logical_coords, tensor_component_to_modify == d + 1);\n  }\n\n  const Variables<\n      tmpl::list<evolution::dg::subcell::Tags::Inactive<Tags::Scalar>,\n                 evolution::dg::subcell::Tags::Inactive<Tags::Vector<Dim>>>>\n      subcell_vars{evolution::dg::subcell::fd::project(dg_vars, dg_mesh,\n                                                       subcell_mesh.extents())};\n\n  CHECK(evolution::dg::subcell::two_mesh_rdmp_tci(dg_vars, subcell_vars,\n                                                  rdmp_delta0, rdmp_epsilon) ==\n        expected_tci_triggered);\n}\n\ntemplate <size_t Dim>\nvoid test_two_mesh_rdmp() {\n  // We lower the maximum number of 1d points in 3d in order to reduce total\n  // test runtime.\n  const size_t maximum_number_of_points_1d =\n      Dim == 3 ? 7\n               : Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n  for (size_t num_pts_1d = 4; num_pts_1d < maximum_number_of_points_1d;\n       ++num_pts_1d) {\n    test_two_mesh_rdmp_impl<Dim>(num_pts_1d, 0, 1.0e-4, 1.0e-3, 1);\n    test_two_mesh_rdmp_impl<Dim>(num_pts_1d, 0, 1.0e-4, 1.0e3, 0);\n    test_two_mesh_rdmp_impl<Dim>(num_pts_1d, std::numeric_limits<size_t>::max(),\n                                 1.0e-4, 1.0e-3, 0);\n\n    // Modify tensor components\n    for (size_t i = 1; i < Dim + 1; ++i) {\n      const int expected_tci_status = i + 1;\n\n      test_two_mesh_rdmp_impl<Dim>(num_pts_1d, i, 1.0e-4, 1.0e-3,\n                                   expected_tci_status);\n      test_two_mesh_rdmp_impl<Dim>(num_pts_1d, i, 1.0e-4, 1.0e3, 0);\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Subcell.Tci.TwoMeshRdmp\",\n                  \"[Evolution][Unit]\") {\n  test_two_mesh_rdmp<1>();\n  test_two_mesh_rdmp<2>();\n  test_two_mesh_rdmp<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Actions/Test_ApplyBoundaryCorrections.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <random>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/DgSubcell/Tags/TciStatus.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ApplyBoundaryCorrections.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/SystemType.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Rational.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace TestHelpers = TestHelpers::evolution::dg::Actions;\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n\nstruct VolumeTag : db::SimpleTag {\n  using type = int;\n};\n\ntemplate <size_t Dim>\nstruct BoundaryTerms final : public evolution::BoundaryCorrection {\n  struct MaxAbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n  explicit BoundaryTerms(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BoundaryTerms);  // NOLINT\n  BoundaryTerms() = default;\n  BoundaryTerms(const BoundaryTerms&) = default;\n  BoundaryTerms& operator=(const BoundaryTerms&) = default;\n  BoundaryTerms(BoundaryTerms&&) = default;\n  BoundaryTerms& operator=(BoundaryTerms&&) = default;\n  ~BoundaryTerms() override = default;\n\n  using variables_tags = tmpl::list<Var1, Var2<Dim>>;\n  using variables_tag = Tags::Variables<variables_tags>;\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override {\n    return std::make_unique<BoundaryTerms>(*this);\n  }\n\n  void pup(PUP::er& p) override {  // NOLINT\n    BoundaryCorrection::pup(p);\n  }\n\n  static constexpr bool need_normal_vector = false;\n\n  using dg_package_field_tags = tmpl::push_back<\n      tmpl::append<db::wrap_tags_in<::Tags::NormalDotFlux, variables_tags>,\n                   variables_tags>,\n      MaxAbsCharSpeed>;\n  using dg_boundary_terms_volume_tags = tmpl::list<VolumeTag>;\n\n  void dg_boundary_terms(\n      const gsl::not_null<Scalar<DataVector>*> boundary_correction_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_var2,\n      const Scalar<DataVector>& interior_normal_dot_flux_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          interior_normal_dot_flux_var2,\n      const Scalar<DataVector>& interior_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& interior_var2,\n      const Scalar<DataVector>& interior_max_abs_char_speed,\n      const Scalar<DataVector>& exterior_normal_dot_flux_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>&\n          exterior_normal_dot_flux_var2,\n      const Scalar<DataVector>& exterior_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& exterior_var2,\n      const Scalar<DataVector>& exterior_max_abs_char_speed,\n      const dg::Formulation dg_formulation, const int& volume_tag) const {\n    // extra minus sign on exterior normal dot flux because normal faces\n    // opposite direction\n    get(*boundary_correction_var1) =\n        0.5 *\n            ((dg_formulation == dg::Formulation::StrongInertial ? 1.0 : -1.0) *\n                 get(interior_normal_dot_flux_var1) -\n             get(exterior_normal_dot_flux_var1)) -\n        0.5 *\n            max(get(interior_max_abs_char_speed),\n                get(exterior_max_abs_char_speed)) *\n            (get(exterior_var1) - get(interior_var1));\n    for (size_t i = 0; i < Dim; ++i) {\n      boundary_correction_var2->get(i) =\n          0.5 * ((dg_formulation == dg::Formulation::StrongInertial ? 1.0\n                                                                    : -1.0) *\n                     interior_normal_dot_flux_var2.get(i) -\n                 exterior_normal_dot_flux_var2.get(i)) -\n          0.5 *\n              max(get(interior_max_abs_char_speed),\n                  get(exterior_max_abs_char_speed)) *\n              (exterior_var2.get(i) - interior_var2.get(i));\n    }\n    CHECK(volume_tag == 10);\n  }\n};\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID BoundaryTerms<Dim>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <bool LocalTimeStepping>\nstruct SetLocalMortarData {\n  template <typename DbTagsList, typename... InboxTags, typename ArrayIndex,\n            typename ActionList, typename ParallelComponent,\n            typename Metavariables>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {  // NOLINT\n    const auto& element =\n        db::get<domain::Tags::Element<Metavariables::volume_dim>>(box);\n    const auto& volume_mesh =\n        db::get<domain::Tags::Mesh<Metavariables::volume_dim>>(box);\n    const auto& mortar_meshes =\n        db::get<evolution::dg::Tags::MortarMesh<Metavariables::volume_dim>>(\n            box);\n    const auto& time_step_id = db::get<::Tags::TimeStepId>(box);\n    using mortar_tags_list = typename BoundaryTerms<\n        Metavariables::volume_dim>::dg_package_field_tags;\n    constexpr size_t number_of_dg_package_tags_components =\n        Variables<mortar_tags_list>::number_of_independent_components;\n\n    MAKE_GENERATOR(generator);\n    std::uniform_real_distribution<> dist_positive(0.5, 1.);\n\n    using CovectorAndMag = Variables<tmpl::list<\n        evolution::dg::Tags::MagnitudeOfNormal,\n        evolution::dg::Tags::NormalCovector<Metavariables::volume_dim>>>;\n    const Scalar<DataVector> det_inv_jacobian = determinant(\n        db::get<::domain::Tags::InverseJacobian<\n            Metavariables::volume_dim, Frame::ElementLogical, Frame::Inertial>>(\n            box));\n\n    for (const auto& [direction, neighbor_ids] : element.neighbors()) {\n      size_t count = 0;\n      const Mesh<Metavariables::volume_dim - 1> face_mesh =\n          volume_mesh.slice_away(direction.dimension());\n      CovectorAndMag covector_and_mag{face_mesh.number_of_grid_points()};\n      get<evolution::dg::Tags::MagnitudeOfNormal>(covector_and_mag) =\n          make_with_random_values<Scalar<DataVector>>(\n              make_not_null(&generator), make_not_null(&dist_positive),\n              face_mesh.number_of_grid_points());\n      db::mutate<evolution::dg::Tags::NormalCovectorAndMagnitude<\n          Metavariables::volume_dim>>(\n          [&covector_and_mag](const auto covector_and_mag_ptr,\n                              const auto& local_direction) {\n            (*covector_and_mag_ptr)[local_direction] = covector_and_mag;\n          },\n          make_not_null(&box), direction);\n\n      for (const auto& neighbor_id : neighbor_ids) {\n        DirectionalId<Metavariables::volume_dim> mortar_id{direction,\n                                                           neighbor_id};\n        const Mesh<Metavariables::volume_dim - 1>& mortar_mesh =\n            mortar_meshes.at(mortar_id);\n\n        // Provide data of the wrong size to make sure it is projected\n        // properly.\n        auto unprojected_mortar_extents = mortar_mesh.extents().indices();\n        if constexpr (not unprojected_mortar_extents.empty()) {\n          ++unprojected_mortar_extents[0];\n        }\n        const Mesh<Metavariables::volume_dim - 1> unprojected_mortar_mesh(\n            unprojected_mortar_extents, mortar_mesh.basis(),\n            mortar_mesh.quadrature());\n        DataVector type_erased_boundary_data_on_mortar{\n            unprojected_mortar_mesh.number_of_grid_points() *\n                number_of_dg_package_tags_components,\n            0.0};\n        alg::iota(type_erased_boundary_data_on_mortar,\n                  direction.dimension() +\n                      10 * static_cast<unsigned long>(direction.side()) +\n                      100 * count + 1000);\n\n        db::mutate<evolution::dg::Tags::MortarData<Metavariables::volume_dim>>(\n            [&face_mesh, &mortar_id, &unprojected_mortar_mesh,\n             &type_erased_boundary_data_on_mortar](const auto mortar_data_ptr) {\n              // when using local time stepping, we reset the local mortar data\n              // at the end of the SetLocalMortarData action since the\n              // ComputeTimeDerivative action would've moved the data into the\n              // boundary history.\n              mortar_data_ptr->at(mortar_id).local().face_mesh = face_mesh;\n              mortar_data_ptr->at(mortar_id).local().mortar_mesh =\n                  unprojected_mortar_mesh;\n              mortar_data_ptr->at(mortar_id).local().mortar_data =\n                  std::move(type_erased_boundary_data_on_mortar);\n            },\n            make_not_null(&box));\n        ++count;\n\n        const TimeStepId past_time_step_id{true, 3,\n                                           Time{Slab{0.2, 3.4}, {1, 4}}};\n        // In LTS, pass an incorrect slab end for the east element to\n        // simulate the slab size changing.  This previously caused\n        // a bug when a slab-size change happened at a time only\n        // needed on the remote side.\n        const auto remote_past_time_step_id =\n            LocalTimeStepping\n                ? direction == Direction<Metavariables::volume_dim>::upper_xi()\n                      ? TimeStepId{true, 3, Time{Slab{0.2, 1.3}, {0, 4}}}\n                      : past_time_step_id\n                : time_step_id;\n        db::mutate<evolution::dg::Tags::MortarNextTemporalId<\n            Metavariables::volume_dim>>(\n            [&mortar_id, &remote_past_time_step_id](\n                const auto mortar_next_temporal_id_ptr) {\n              mortar_next_temporal_id_ptr->at(mortar_id) =\n                  remote_past_time_step_id;\n            },\n            make_not_null(&box));\n        if (LocalTimeStepping) {\n          // We also need to set the local history one step back to get to 2nd\n          // order in time.\n          type_erased_boundary_data_on_mortar.destructive_resize(\n              mortar_mesh.number_of_grid_points() *\n              number_of_dg_package_tags_components);\n          alg::iota(type_erased_boundary_data_on_mortar,\n                    direction.dimension() +\n                        10 * static_cast<unsigned long>(direction.side()) +\n                        100 * count + 1000);\n          count++;\n          evolution::dg::MortarData<Metavariables::volume_dim>\n              past_mortar_data{};\n          past_mortar_data.face_mesh = face_mesh;\n          past_mortar_data.mortar_mesh = mortar_mesh;\n          past_mortar_data.mortar_data =\n              std::move(type_erased_boundary_data_on_mortar);\n          Scalar<DataVector> local_face_normal_magnitude{\n              face_mesh.number_of_grid_points()};\n          alg::iota(get(local_face_normal_magnitude),\n                    direction.dimension() +\n                        10 * static_cast<unsigned long>(direction.side()) +\n                        100 * count + 100000);\n          past_mortar_data.face_normal_magnitude = local_face_normal_magnitude;\n          if (volume_mesh.quadrature(direction.dimension()) ==\n              Spectral::Quadrature::Gauss) {\n            Scalar<DataVector> local_face_det_jacobian{\n                face_mesh.number_of_grid_points()};\n            alg::iota(get(local_face_det_jacobian),\n                      direction.dimension() +\n                          10 * static_cast<unsigned long>(direction.side()) +\n                          100 * count + 200000);\n            Scalar<DataVector> local_volume_det_inv_jacobian{\n                volume_mesh.number_of_grid_points()};\n            alg::iota(get(local_volume_det_inv_jacobian),\n                      direction.dimension() +\n                          10 * static_cast<unsigned long>(direction.side()) +\n                          100 * count + 300000);\n            past_mortar_data.volume_det_inv_jacobian =\n                local_volume_det_inv_jacobian;\n            past_mortar_data.volume_mesh = volume_mesh;\n            past_mortar_data.face_det_jacobian = local_face_det_jacobian;\n          }\n          db::mutate<evolution::dg::Tags::MortarData<Metavariables::volume_dim>,\n                     evolution::dg::Tags::MortarDataHistory<\n                         Metavariables::volume_dim>>(\n              [&det_inv_jacobian, &mortar_id, &volume_mesh, &past_mortar_data,\n               &past_time_step_id, &time_step_id](\n                  const auto mortar_data_ptr,\n                  const auto mortar_data_history_ptr,\n                  const Mesh<Metavariables::volume_dim>& mesh,\n                  const DirectionMap<Metavariables::volume_dim,\n                                     std::optional<Variables<tmpl::list<\n                                         evolution::dg::Tags::MagnitudeOfNormal,\n                                         evolution::dg::Tags::NormalCovector<\n                                             Metavariables::volume_dim>>>>>&\n                      normal_covector_and_magnitude) {\n                mortar_data_history_ptr->at(mortar_id).local().insert(\n                    past_time_step_id, 2, past_mortar_data);\n\n                // Now add the current data into the history.\n                evolution::dg::MortarData<Metavariables::volume_dim>&\n                    local_mortar_data = mortar_data_ptr->at(mortar_id).local();\n\n                const Scalar<DataVector>& face_normal_magnitude =\n                    get<evolution::dg::Tags::MagnitudeOfNormal>(\n                        *normal_covector_and_magnitude.at(\n                            mortar_id.direction()));\n\n                local_mortar_data.face_normal_magnitude = face_normal_magnitude;\n                if (mesh.quadrature(mortar_id.direction().dimension()) ==\n                    Spectral::Quadrature::Gauss) {\n                  const Scalar<DataVector> det_jacobian{\n                      DataVector{1.0 / get(det_inv_jacobian)}};\n                  Scalar<DataVector> face_det_jacobian{\n                      mesh.slice_away(mortar_id.direction().dimension())\n                          .number_of_grid_points()};\n                  const Matrix identity{};\n                  auto interpolation_matrices =\n                      make_array<Metavariables::volume_dim>(\n                          std::cref(identity));\n                  const std::pair<Matrix, Matrix>& matrices =\n                      Spectral::boundary_interpolation_matrices(\n                          mesh.slice_through(\n                              mortar_id.direction().dimension()));\n                  gsl::at(interpolation_matrices,\n                          mortar_id.direction().dimension()) =\n                      mortar_id.direction().side() == Side::Upper\n                          ? matrices.second\n                          : matrices.first;\n                  apply_matrices(make_not_null(&get(face_det_jacobian)),\n                                 interpolation_matrices, get(det_jacobian),\n                                 mesh.extents());\n                  local_mortar_data.volume_det_inv_jacobian = det_inv_jacobian;\n                  local_mortar_data.volume_mesh = volume_mesh;\n                  local_mortar_data.face_det_jacobian = face_det_jacobian;\n                }\n                mortar_data_history_ptr->at(mortar_id).local().insert(\n                    time_step_id, 2, std::move(local_mortar_data));\n                local_mortar_data = {};\n              },\n              make_not_null(&box),\n              db::get<domain::Tags::Mesh<Metavariables::volume_dim>>(box),\n              db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<\n                  Metavariables::volume_dim>>(box));\n        }\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <size_t Dim, TestHelpers::SystemType SystemType>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n\n  using variables_tag = Tags::Variables<tmpl::list<Var1, Var2<Dim>>>;\n  using flux_variables = tmpl::conditional_t<\n      SystemType == TestHelpers::SystemType::Conservative,\n      tmpl::list<Var1, Var2<Dim>>,\n      tmpl::conditional_t<SystemType ==\n                              TestHelpers::SystemType::Nonconservative,\n                          tmpl::list<>, tmpl::list<Var2<Dim>>>>;\n  using gradient_variables = tmpl::conditional_t<\n      SystemType == TestHelpers::SystemType::Conservative, tmpl::list<>,\n      tmpl::conditional_t<SystemType ==\n                              TestHelpers::SystemType::Nonconservative,\n                          tmpl::list<Var1, Var2<Dim>>, tmpl::list<Var1>>>;\n};\n\ntemplate <typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Metavariables::volume_dim>;\n  static constexpr bool local_time_stepping =\n      Metavariables::local_time_stepping;\n\n  using internal_directions =\n      domain::Tags::InternalDirections<Metavariables::volume_dim>;\n  using boundary_directions_interior =\n      domain::Tags::BoundaryDirectionsInterior<Metavariables::volume_dim>;\n\n  using simple_tags = tmpl::list<\n      VolumeTag, ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n      ::Tags::TimeStep, Tags::ConcreteTimeStepper<LtsTimeStepper>,\n      db::add_tag_prefix<::Tags::dt,\n                         typename Metavariables::system::variables_tag>,\n      typename Metavariables::system::variables_tag,\n      domain::Tags::Mesh<Metavariables::volume_dim>,\n      domain::Tags::Element<Metavariables::volume_dim>,\n      domain::Tags::Coordinates<Metavariables::volume_dim, Frame::Inertial>,\n      domain::Tags::InverseJacobian<Metavariables::volume_dim,\n                                    Frame::ElementLogical, Frame::Inertial>,\n      evolution::dg::Tags::Quadrature,\n      domain::Tags::NeighborMesh<Metavariables::volume_dim>>;\n  using compute_tags = tmpl::push_back<\n      time_stepper_ref_tags<LtsTimeStepper>,\n      domain::Tags::JacobianCompute<Metavariables::volume_dim,\n                                    Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::DetInvJacobianCompute<\n          Metavariables::volume_dim, Frame::ElementLogical, Frame::Inertial>>;\n\n  using lts_action = ::evolution::dg::Actions::ApplyLtsBoundaryCorrections<\n      Metavariables::volume_dim, Metavariables::use_nodegroup_dg_elements>;\n  using gts_action =\n      ::evolution::dg::Actions::ApplyBoundaryCorrectionsToTimeDerivative<\n          Metavariables::volume_dim, Metavariables::use_nodegroup_dg_elements>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<\n              ActionTesting::InitializeDataBox<simple_tags, compute_tags>,\n              ::evolution::dg::Initialization::Mortars<\n                  Metavariables::volume_dim>,\n              SetLocalMortarData<local_time_stepping>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<tmpl::conditional_t<local_time_stepping,\n                                         // Apply the incorrect action first to\n                                         // verify it doesn't do anything.\n                                         tmpl::list<gts_action, lts_action>,\n                                         tmpl::list<lts_action, gts_action>>>>>;\n};\n\ntemplate <size_t Dim, TestHelpers::SystemType SystemType,\n          bool LocalTimeStepping, bool UseNodegroupDgElements>\nstruct Metavariables {\n  static constexpr TestHelpers::SystemType system_type = SystemType;\n  static constexpr size_t volume_dim = Dim;\n  static constexpr bool local_time_stepping = LocalTimeStepping;\n  static constexpr bool use_nodegroup_dg_elements = UseNodegroupDgElements;\n  using system = System<Dim, SystemType>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<Dim>, domain::Tags::InitialExtents<Dim>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<evolution::BoundaryCorrection,\n                             tmpl::list<BoundaryTerms<Dim>>>>;\n  };\n\n  using component_list = tmpl::list<component<Metavariables>>;\n};\n\ntemplate <typename Tag, typename Metavariables, size_t Dim>\nconst auto& get_tag(\n    const ActionTesting::MockRuntimeSystem<Metavariables>& runner,\n    const ElementId<Dim>& self_id) {\n  return ActionTesting::get_databox_tag<component<Metavariables>, Tag>(runner,\n                                                                       self_id);\n}\n\ntemplate <size_t Dim, TestHelpers::SystemType SystemType,\n          bool UseLocalTimeStepping, bool UseNodegroupDgElements>\nvoid test_impl(const Spectral::Quadrature quadrature,\n               const ::dg::Formulation dg_formulation) {\n  CAPTURE(Dim);\n  CAPTURE(SystemType);\n  CAPTURE(quadrature);\n  CAPTURE(UseLocalTimeStepping);\n  using metavars = Metavariables<Dim, SystemType, UseLocalTimeStepping,\n                                 UseNodegroupDgElements>;\n  register_factory_classes_with_charm<metavars>();\n  using comp = component<metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  using variables_tag = typename metavars::system::variables_tag;\n  using variables_tags = typename variables_tag::tags_list;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  using dt_variables_tags = db::wrap_tags_in<::Tags::dt, variables_tags>;\n  using mortar_tags_list = typename BoundaryTerms<Dim>::dg_package_field_tags;\n\n  // Use a second-order time stepper so that we test the local\n  // Jacobian and normal magnitude history is handled correctly.  Use\n  // higher-order on the element doing nontrivial LTS to test that the\n  // correct TimeStepId is stored in the history, as at slab\n  // boundaries only the local TimeStepId is used for equal-order\n  // boundaries.\n  const size_t common_integration_order = 2;\n  const size_t east_integration_order = 3;\n  const TimeSteppers::AdamsBashforth time_stepper{std::nullopt};\n\n  // The reference element in 2d denoted by X below:\n  // ^ eta\n  // +-+-+> xi\n  // |X| |\n  // +-+-+\n  // | | |\n  // +-+-+\n  //\n  // The \"self_id\" for the element that we are considering is marked by an X in\n  // the diagram. We consider a configuration with one neighbor in the +xi\n  // direction (east_id), and (in 2d and 3d) one in the -eta (south_id)\n  // direction.\n  //\n  // In 1d there aren't any projections to test, and in 3d we only have 1\n  // element in the z-direction.\n  //\n  // We choose the east_id element to be running at a refinement of 2 in time\n  // relative to the self_id element.\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  ElementId<Dim> self_id{};\n  ElementId<Dim> east_id{};\n  ElementId<Dim> south_id{};  // not used in 1d\n  std::vector<DirectionalId<Dim>> order_to_send_neighbor_data_in{};\n\n  if constexpr (Dim == 1) {\n    self_id = ElementId<Dim>{0, {{{1, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n  } else if constexpr (Dim == 2) {\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{1, 0}, {0, 0}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, OrientationMap<Dim>::create_aligned()};\n  } else {\n    static_assert(Dim == 3, \"Only implemented tests in 1, 2, and 3d\");\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{1, 0}, {0, 0}, {0, 0}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, OrientationMap<Dim>::create_aligned()};\n  }\n  if constexpr (Dim > 1) {\n    order_to_send_neighbor_data_in.push_back(\n        DirectionalId<Dim>{Direction<Dim>::lower_eta(), south_id});\n  }\n  order_to_send_neighbor_data_in.push_back(\n      DirectionalId<Dim>{Direction<Dim>::upper_xi(), east_id});\n\n  const Element<Dim> element{self_id, neighbors};\n\n  std::vector<Block<Dim>> blocks{Dim == 1 ? 1 : 2};\n  if constexpr (Dim == 1) {\n    blocks[0] = Block<Dim>(nullptr, element.id().block_id(), {});\n  } else {\n    blocks[0] = Block<Dim>(nullptr, 0,\n                           {{Direction<Dim>::lower_eta(),\n                             {1, OrientationMap<Dim>::create_aligned()}}});\n    blocks[1] = Block<Dim>(nullptr, 1,\n                           {{Direction<Dim>::upper_eta(),\n                             {0, OrientationMap<Dim>::create_aligned()}}});\n  }\n  Domain<Dim> domain{std::move(blocks)};\n  MockRuntimeSystem runner{{std::move(domain),\n                            std::vector<std::array<size_t, Dim>>{\n                                make_array<Dim>(2_st), make_array<Dim>(3_st)},\n                            std::make_unique<BoundaryTerms<Dim>>(),\n                            dg_formulation}};\n\n  const size_t number_of_grid_points_per_dimension = 5;\n  const Mesh<Dim> mesh{number_of_grid_points_per_dimension,\n                       Spectral::Basis::Legendre, quadrature};\n  typename domain::Tags::NeighborMesh<Dim>::type neighbor_mesh{};\n  neighbor_mesh[{Direction<Dim>::upper_xi(), east_id}] = mesh;\n  if constexpr (Dim > 1) {\n    neighbor_mesh[{Direction<Dim>::lower_eta(), south_id}] = mesh;\n  }\n\n  // Set the Jacobian to not be the identity because otherwise bugs creep in\n  // easily.\n  ::InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inv_jac{mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < Dim; ++i) {\n    inv_jac.get(i, i) = 2.0;\n  }\n  auto det_inv_jacobian = determinant(inv_jac);\n  const auto jacobian = determinant_and_inverse(inv_jac).second;\n\n  // We don't need the Jacobian and map to be consistent since we are just\n  // checking that given a Jacobian, coordinates, etc., the correct terms are\n  // added to the evolution equations.\n  const auto logical_coords = logical_coordinates(mesh);\n  tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords{};\n  for (size_t i = 0; i < logical_coords.size(); ++i) {\n    inertial_coords[i] = logical_coords[i];\n  }\n\n  Variables<tmpl::list<::Tags::dt<Var1>, ::Tags::dt<Var2<Dim>>>>\n      dt_evolved_vars{mesh.number_of_grid_points(), 0.0};\n  Variables<tmpl::list<Var1, Var2<Dim>>> evolved_vars{\n      mesh.number_of_grid_points(), 0.0};\n\n  const TimeDelta time_step{Slab{0.2, 3.4}, {1, 4}};\n  const TimeStepId time_step_id{true, 3, Time{Slab{0.2, 3.4}, {2, 4}}};\n  const TimeStepId local_next_time_step_id{true, 3,\n                                           Time{Slab{0.2, 3.4}, {3, 4}}};\n  const std::vector<TimeStepId> east_id_time_steps{\n      {true, 3, Time{Slab{0.2, 3.4}, {0, 8}}},\n      {true, 3, Time{Slab{0.2, 3.4}, {2, 8}}},\n      {true, 3, Time{Slab{0.2, 3.4}, {4, 8}}},\n      {true, 3, Time{Slab{0.2, 3.4}, {5, 8}}},\n      {true, 3, Time{Slab{0.2, 3.4}, {6, 8}}}};\n  const std::vector<TimeStepId> east_id_next_time_steps{\n      {true, 3, Time{Slab{0.2, 3.4}, {2, 8}}},\n      {true, 3, Time{Slab{0.2, 3.4}, {4, 8}}},\n      {true, 3, Time{Slab{0.2, 3.4}, {5, 8}}},\n      {true, 3, Time{Slab{0.2, 3.4}, {6, 8}}},\n      {true, 3, Time{Slab{0.2, 3.4}, {7, 8}}}};\n\n  ActionTesting::emplace_component_and_initialize<comp>(\n      &runner, self_id,\n      {10, time_step_id, local_next_time_step_id, time_step,\n       std::make_unique<TimeSteppers::AdamsBashforth>(time_stepper),\n       dt_evolved_vars, evolved_vars, mesh, element, inertial_coords, inv_jac,\n       quadrature, neighbor_mesh});\n\n  // Initialize both the mortars\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n  // Set the local mortar data\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  // Start testing the actual dg::ApplyBoundaryCorrections action\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Make a copy of the mortar data so we can check against it locally\n  auto all_mortar_data =\n      get_tag<evolution::dg::Tags::MortarData<Dim>>(runner, self_id);\n  typename evolution::dg::Tags::MortarDataHistory<Dim>::type\n      mortar_data_history{};\n  if (UseLocalTimeStepping) {\n    // Copy local mortar data from all_mortar_data to mortar_data_history\n    mortar_data_history =\n        get_tag<evolution::dg::Tags::MortarDataHistory<Dim>>(runner, self_id);\n  }\n\n  // Check that the action for the wrong time-stepping mode runs\n  // successfully without any data having been received, and therefore\n  // presumably doesn't do anything.\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  // \"Send\" mortar data to element\n  const auto& mortar_meshes =\n      get_tag<evolution::dg::Tags::MortarMesh<Dim>>(runner, self_id);\n  using mortar_tags_list = typename BoundaryTerms<Dim>::dg_package_field_tags;\n  constexpr size_t number_of_dg_package_tags_components =\n      Variables<mortar_tags_list>::number_of_independent_components;\n  typename evolution::dg::subcell::Tags::NeighborTciDecisions<Dim>::type\n      neighbor_decision{};\n  int decision = 1;\n  for (const auto& direction_and_neighbor_id : order_to_send_neighbor_data_in) {\n    const auto& direction = direction_and_neighbor_id.direction();\n    const auto& neighbor_id = direction_and_neighbor_id.id();\n    CAPTURE(direction);\n    CAPTURE(neighbor_id);\n\n    size_t count = 0;\n    const Mesh<Dim - 1> face_mesh = mesh.slice_away(direction.dimension());\n    const auto insert_neighbor_data = [&all_mortar_data, &count, &decision,\n                                       &direction, &face_mesh,\n                                       &local_next_time_step_id, &mesh,\n                                       &mortar_data_history, &mortar_meshes,\n                                       &neighbor_decision, &neighbor_id,\n                                       &runner, &self_id](\n                                          const TimeStepId&\n                                              neighbor_time_step_id,\n                                          const TimeStepId&\n                                              neighbor_next_time_step_id,\n                                          const size_t integration_order) {\n      CAPTURE(neighbor_next_time_step_id);\n      DirectionalId<Dim> mortar_id{direction, neighbor_id};\n      const Mesh<Dim - 1>& mortar_mesh = mortar_meshes.at(mortar_id);\n\n      DataVector flux_data{mortar_mesh.number_of_grid_points() *\n                               number_of_dg_package_tags_components,\n                           0.0};\n      alg::iota(flux_data,\n                direction.dimension() +\n                    10 * static_cast<unsigned long>(direction.side()) +\n                    100 * count);\n      const evolution::dg::BoundaryData<Dim> data{\n          mesh,         std::nullopt,     mortar_mesh,\n          std::nullopt, {flux_data},      {neighbor_next_time_step_id},\n          decision,     integration_order};\n      neighbor_decision.insert(std::pair{mortar_id, decision});\n      ++decision;\n\n      runner.template mock_distributed_objects<comp>()\n          .at(self_id)\n          .template receive_data<\n              evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                  Dim, UseNodegroupDgElements>>(\n              neighbor_time_step_id,\n              std::pair{DirectionalId<Dim>{direction, neighbor_id}, data});\n      if (UseLocalTimeStepping) {\n        if (neighbor_time_step_id < local_next_time_step_id) {\n          evolution::dg::MortarData<Dim> nhbr_mortar_data{};\n          nhbr_mortar_data.face_mesh = face_mesh;\n          nhbr_mortar_data.mortar_mesh = mortar_mesh;\n          nhbr_mortar_data.mortar_data = flux_data;\n          mortar_data_history.at(mortar_id).remote().insert(\n              neighbor_time_step_id, integration_order,\n              std::move(nhbr_mortar_data));\n        }\n      } else {\n        all_mortar_data.at(mortar_id).neighbor().face_mesh = face_mesh;\n        all_mortar_data.at(mortar_id).neighbor().mortar_mesh = mortar_mesh;\n        all_mortar_data.at(mortar_id).neighbor().mortar_data = flux_data;\n      }\n      ++count;\n    };\n    if (neighbor_id == east_id and UseLocalTimeStepping) {\n      for (size_t east_id_time_steps_index = 0;\n           east_id_time_steps_index < east_id_next_time_steps.size();\n           ++east_id_time_steps_index) {\n        if (east_id_time_steps_index < east_id_next_time_steps.size() - 1) {\n          REQUIRE(not ActionTesting::next_action_if_ready<comp>(\n              make_not_null(&runner), self_id));\n        }\n        insert_neighbor_data(east_id_time_steps[east_id_time_steps_index],\n                             east_id_next_time_steps[east_id_time_steps_index],\n                             east_integration_order);\n      }\n    } else {\n      // Insert the mortar data (history) running at the same speed as the\n      // self_id.\n      REQUIRE(not ActionTesting::next_action_if_ready<comp>(\n          make_not_null(&runner), self_id));\n      if (UseLocalTimeStepping) {\n        // Insert the past time, since we are using a 2nd order time stepper.\n        const Time prev_time = time_step_id.step_time() - time_step;\n        insert_neighbor_data(TimeStepId{time_step_id.time_runs_forward(),\n                                        time_step_id.slab_number(), prev_time},\n                             time_step_id, common_integration_order);\n        REQUIRE(not ActionTesting::next_action_if_ready<comp>(\n            make_not_null(&runner), self_id));\n      }\n      insert_neighbor_data(time_step_id, local_next_time_step_id,\n                           common_integration_order);\n    }\n  }\n  // Check expected inboxes\n  REQUIRE(\n      runner\n          .template nonempty_inboxes<\n              comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                        Dim, UseNodegroupDgElements>>()\n          .size() == 1);\n\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  // Check the inboxes are empty when doing global time stepping\n  if (not UseLocalTimeStepping) {\n    REQUIRE(\n        runner\n            .template nonempty_inboxes<\n                comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                          Dim, UseNodegroupDgElements>>()\n            .empty());\n  } else {\n    CHECK(\n        runner\n            .template nonempty_inboxes<\n                comp, evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                          Dim, UseNodegroupDgElements>>()\n            .size() == 1);\n  }\n\n  // Now retrieve dt tag and check that values are correct\n  const auto& mortar_infos =\n      get_tag<evolution::dg::Tags::MortarInfo<Dim>>(runner, self_id);\n\n  Variables<dt_variables_tags> dt_boundary_correction_on_mortar{};\n  Variables<dt_variables_tags> dt_boundary_correction_projected_onto_face{};\n  Variables<dt_variables_tags> expected_dt_variables_volume{\n      mesh.number_of_grid_points(), 0.0};\n  const DirectionalId<Dim>* mortar_id_ptr = nullptr;\n\n  const auto compute_correction_coupling =\n      [&det_inv_jacobian, &dg_formulation, &dt_boundary_correction_on_mortar,\n       &dt_boundary_correction_projected_onto_face,\n       &expected_dt_variables_volume, &mesh, &mortar_id_ptr, &mortar_meshes,\n       &mortar_infos, &runner,\n       &self_id](const evolution::dg::MortarData<Dim>& local_mortar_data,\n                 const evolution::dg::MortarData<Dim>& neighbor_mortar_data)\n      -> Variables<db::wrap_tags_in<::Tags::dt, variables_tags>> {\n    const auto& mortar_id = *mortar_id_ptr;\n    const auto& direction = mortar_id.direction();\n    const auto& mortar_mesh = mortar_meshes.at(mortar_id);\n    const size_t dimension = direction.dimension();\n\n    const bool using_points_on_face =\n        mesh.quadrature(dimension) == Spectral::Quadrature::GaussLobatto or\n        mesh.quadrature(dimension) == Spectral::Quadrature::GaussRadauUpper;\n    if (UseLocalTimeStepping and not using_points_on_face) {\n      // This needs to be updated every call because the Jacobian may be\n      // time-dependent. In the case of time-independent maps and local\n      // time stepping we could first perform the integral on the\n      // boundaries, and then lift to the volume. This is left as a future\n      // optimization.\n      det_inv_jacobian = local_mortar_data.volume_det_inv_jacobian.value();\n    }\n\n    Variables<mortar_tags_list> local_data_on_mortar{\n        mortar_mesh.number_of_grid_points()};\n    Variables<mortar_tags_list> neighbor_data_on_mortar{\n        mortar_mesh.number_of_grid_points()};\n    const DataVector& local_data = *local_mortar_data.mortar_data;\n    const DataVector& neighbor_data = *neighbor_mortar_data.mortar_data;\n    std::copy(local_data.begin(), local_data.end(),\n              local_data_on_mortar.data());\n    std::copy(neighbor_data.begin(), neighbor_data.end(),\n              neighbor_data_on_mortar.data());\n\n    if (dt_boundary_correction_on_mortar.number_of_grid_points() !=\n        mortar_mesh.number_of_grid_points()) {\n      dt_boundary_correction_on_mortar.initialize(\n          mortar_mesh.number_of_grid_points());\n    }\n\n    // Compute boundary terms on the mortar\n    BoundaryTerms<Dim>{}.dg_boundary_terms(\n        make_not_null(&get<Tags::dt<Var1>>(dt_boundary_correction_on_mortar)),\n        make_not_null(\n            &get<Tags::dt<Var2<Dim>>>(dt_boundary_correction_on_mortar)),\n        get<Tags::NormalDotFlux<Var1>>(local_data_on_mortar),\n        get<Tags::NormalDotFlux<Var2<Dim>>>(local_data_on_mortar),\n        get<Var1>(local_data_on_mortar), get<Var2<Dim>>(local_data_on_mortar),\n        get<typename BoundaryTerms<Dim>::MaxAbsCharSpeed>(local_data_on_mortar),\n        get<Tags::NormalDotFlux<Var1>>(neighbor_data_on_mortar),\n        get<Tags::NormalDotFlux<Var2<Dim>>>(neighbor_data_on_mortar),\n        get<Var1>(neighbor_data_on_mortar),\n        get<Var2<Dim>>(neighbor_data_on_mortar),\n        get<typename BoundaryTerms<Dim>::MaxAbsCharSpeed>(\n            neighbor_data_on_mortar),\n        dg_formulation, 10);\n\n    // Project the boundary terms from the mortar to the face\n    const std::array<Spectral::SegmentSize, Dim - 1>& mortar_size =\n        mortar_infos.at(mortar_id).mortar_size();\n    const Mesh<Dim - 1> face_mesh = mesh.slice_away(dimension);\n\n    auto& dt_boundary_correction =\n        [&dt_boundary_correction_on_mortar,\n         &dt_boundary_correction_projected_onto_face, &face_mesh, &mortar_mesh,\n         &mortar_size]() -> Variables<dt_variables_tags>& {\n      if (Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)) {\n        dt_boundary_correction_projected_onto_face =\n            ::dg::project_from_mortar(dt_boundary_correction_on_mortar,\n                                      face_mesh, mortar_mesh, mortar_size);\n        return dt_boundary_correction_projected_onto_face;\n      }\n      return dt_boundary_correction_on_mortar;\n    }();\n\n    // Lift the boundary terms from the face into the volume\n    Scalar<DataVector> magnitude_of_face_normal{};\n    if (UseLocalTimeStepping) {\n      magnitude_of_face_normal =\n          local_mortar_data.face_normal_magnitude.value();\n    } else {\n      magnitude_of_face_normal = get<evolution::dg::Tags::MagnitudeOfNormal>(\n          *get_tag<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(\n               runner, self_id)\n               .at(direction));\n    }\n\n    if (using_points_on_face) {\n      // The lift_flux function lifts only on the slice, it does not add\n      // the contribution to the volume.\n      ::dg::lift_flux(make_not_null(&dt_boundary_correction),\n                      mesh.extents(dimension), magnitude_of_face_normal,\n                      mesh.basis(dimension));\n      if (UseLocalTimeStepping) {\n        return dt_boundary_correction;\n      } else {\n        // Add the flux contribution to the volume data\n        add_slice_to_data(make_not_null(&expected_dt_variables_volume),\n                          dt_boundary_correction, mesh.extents(), dimension,\n                          index_to_slice_at(mesh.extents(), direction));\n      }\n    } else {\n      if (UseLocalTimeStepping) {\n        const Scalar<DataVector>& face_det_jacobian =\n            local_mortar_data.face_det_jacobian.value();\n\n        Variables<db::wrap_tags_in<::Tags::dt, variables_tags>>\n            volume_dt_correction{mesh.number_of_grid_points(), 0.0};\n        ::dg::lift_boundary_terms_gauss_points(\n            make_not_null(&volume_dt_correction), det_inv_jacobian, mesh,\n            direction, dt_boundary_correction, magnitude_of_face_normal,\n            face_det_jacobian);\n        return volume_dt_correction;\n      } else {\n        // Project the volume det jacobian to the face\n        Scalar<DataVector> face_det_jacobian{face_mesh.number_of_grid_points()};\n        const Matrix identity{};\n        auto interpolation_matrices = make_array<Dim>(std::cref(identity));\n        const std::pair<Matrix, Matrix>& matrices =\n            Spectral::boundary_interpolation_matrices(\n                mesh.slice_through(direction.dimension()));\n        gsl::at(interpolation_matrices, direction.dimension()) =\n            direction.side() == Side::Upper ? matrices.second : matrices.first;\n        apply_matrices(make_not_null(&get(face_det_jacobian)),\n                       interpolation_matrices,\n                       DataVector{1.0 / get(det_inv_jacobian)}, mesh.extents());\n\n        // Lift from the Gauss points into the volume\n        ::dg::lift_boundary_terms_gauss_points(\n            make_not_null(&expected_dt_variables_volume), det_inv_jacobian,\n            mesh, direction, dt_boundary_correction, magnitude_of_face_normal,\n            face_det_jacobian);\n      }\n    }\n\n    ASSERT(not UseLocalTimeStepping,\n           \"We shouldn't be returning empty data when using local time \"\n           \"stepping. Some logic in the lambda this assert is in is bad. Might \"\n           \"be a missing return?\");\n    return {};\n  };\n\n  Variables<variables_tags> expected_evolved_variables{\n      mesh.number_of_grid_points(), 0.0};\n  if (UseLocalTimeStepping) {\n    for (auto& mortar_id_and_data : mortar_data_history) {\n      const auto& mortar_id = mortar_id_and_data.first;\n      const auto& direction = mortar_id.direction();\n      auto& mortar_data_hist = mortar_id_and_data.second;\n      const auto& mortar_mesh = mortar_meshes.at(mortar_id);\n      mortar_data_hist.local().for_each(\n          [&](const TimeStepId& /*id*/,\n              const gsl::not_null<evolution::dg::MortarData<Dim>*> data) {\n            return p_project_mortar_data(data, mortar_mesh);\n          });\n      mortar_data_hist.remote().for_each(\n          [&](const TimeStepId& /*id*/,\n              const gsl::not_null<evolution::dg::MortarData<Dim>*> data) {\n            return p_project_mortar_data(data, mortar_mesh);\n          });\n      mortar_id_ptr = &mortar_id;\n      const bool direction_uses_points_on_face =\n          mesh.quadrature(direction.dimension()) ==\n              Spectral::Quadrature::GaussLobatto or\n          mesh.quadrature(direction.dimension()) ==\n              Spectral::Quadrature::GaussRadauUpper;\n      Variables<variables_tags> lifted_volume_data{\n          direction_uses_points_on_face\n              ? mesh.slice_away(direction.dimension()).number_of_grid_points()\n              : mesh.number_of_grid_points(),\n          0.0};\n      time_stepper.add_boundary_delta(&lifted_volume_data, mortar_data_hist,\n                                      time_step, compute_correction_coupling);\n      if (direction_uses_points_on_face) {\n        // Add the flux contribution to the volume data\n        add_slice_to_data(make_not_null(&expected_evolved_variables),\n                          lifted_volume_data, mesh.extents(),\n                          direction.dimension(),\n                          index_to_slice_at(mesh.extents(), direction));\n      } else {\n        expected_evolved_variables += lifted_volume_data;\n      }\n    }\n\n    // dt_variables should be identically zero in both cases\n    CHECK(expected_dt_variables_volume ==\n          get_tag<dt_variables_tag>(runner, self_id));\n    tmpl::for_each<variables_tags>([&expected_evolved_variables, &runner,\n                                    &self_id](auto tag_v) {\n      using tag = tmpl::type_from<decltype(tag_v)>;\n      CHECK_ITERABLE_APPROX(get<tag>(get_tag<variables_tag>(runner, self_id)),\n                            get<tag>(expected_evolved_variables));\n    });\n  } else {\n    for (auto& [mortar_id, mortar_data] : all_mortar_data) {\n      if (mortar_id.id() == ElementId<Dim>::external_boundary_id()) {\n        continue;\n      }\n      const auto& mortar_mesh = mortar_meshes.at(mortar_id);\n      p_project_mortar_data(make_not_null(&mortar_data.local()), mortar_mesh);\n      p_project_mortar_data(make_not_null(&mortar_data.neighbor()),\n                            mortar_mesh);\n      mortar_id_ptr = &mortar_id;\n      compute_correction_coupling(mortar_data.local(), mortar_data.neighbor());\n    }\n    Approx custom_approx = Approx::custom().epsilon(5.e-11);\n    tmpl::for_each<dt_variables_tags>(\n        [&custom_approx, &expected_dt_variables_volume, &runner,\n         &self_id](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          CHECK_ITERABLE_CUSTOM_APPROX(\n              get<tag>(get_tag<dt_variables_tag>(runner, self_id)),\n              get<tag>(expected_dt_variables_volume), custom_approx);\n        });\n    CHECK(expected_evolved_variables ==\n          get_tag<variables_tag>(runner, self_id));\n  }\n\n  // Check neighbor meshes\n  size_t total_neighbors = 0;\n  const auto& neighbor_meshes =\n      get_tag<::domain::Tags::NeighborMesh<Dim>>(runner, self_id);\n  for (const auto& [direction, neighbors_in_direction] : element.neighbors()) {\n    for (const auto& neighbor : neighbors_in_direction) {\n      const auto it =\n          neighbor_meshes.find(DirectionalId<Dim>{direction, neighbor});\n      REQUIRE(it != neighbor_meshes.end());\n      CHECK(it->second == mesh);\n      ++total_neighbors;\n    }\n  }\n  CHECK(neighbor_meshes.size() == total_neighbors);\n}\n\ntemplate <size_t Dim, bool UseLocalTimeStepping,\n          TestHelpers::SystemType SystemType>\nvoid test() {\n  for (const auto dg_formulation :\n       {::dg::Formulation::StrongInertial, ::dg::Formulation::WeakInertial}) {\n    for (const auto quadrature :\n         {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n      test_impl<Dim, SystemType, UseLocalTimeStepping, false>(quadrature,\n                                                              dg_formulation);\n    }\n  }\n}\n\ntemplate <typename Metavariables>\nstruct ReceiveOrderComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n\n  using simple_tags = tmpl::list<\n      VolumeTag, ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n      ::Tags::TimeStep, Tags::ConcreteTimeStepper<LtsTimeStepper>,\n      typename Metavariables::system::variables_tag, domain::Tags::Mesh<1>,\n      domain::Tags::Element<1>, domain::Tags::NeighborMesh<1>>;\n  using compute_tags = tmpl::push_back<time_stepper_ref_tags<LtsTimeStepper>>;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<\n                     Parallel::Phase::Initialization,\n                     tmpl::list<ActionTesting::InitializeDataBox<simple_tags,\n                                                                 compute_tags>,\n                                ::evolution::dg::Initialization::Mortars<1>>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Testing,\n                     tmpl::list<::evolution::dg::Actions::\n                                    ApplyLtsBoundaryCorrections<1, false>>>>;\n};\n\nstruct ReceiveOrderMetavariables {\n  static constexpr bool local_time_stepping = true;\n  using system = System<1, TestHelpers::SystemType::Conservative>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<evolution::BoundaryCorrection,\n                                                 tmpl::list<BoundaryTerms<1>>>>;\n  };\n\n  using component_list =\n      tmpl::list<ReceiveOrderComponent<ReceiveOrderMetavariables>>;\n};\n\nvoid test_receive_order() {\n  using metavars = ReceiveOrderMetavariables;\n  using comp = ReceiveOrderComponent<metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  register_factory_classes_with_charm<metavars>();\n\n  MAKE_GENERATOR(gen);\n\n  Domain<1> domain{make_vector(Block<1>(nullptr, 0, {}))};\n  const Mesh<1> mesh(2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n\n  const ElementId<1> west_id{0, {{{2, 0}}}};\n  const ElementId<1> self_id{0, {{{2, 1}}}};\n  const ElementId<1> east_id{0, {{{2, 2}}}};\n\n  DirectionMap<1, Neighbors<1>> neighbors{};\n  neighbors[Direction<1>::lower_xi()] =\n      Neighbors<1>{{west_id}, OrientationMap<1>::create_aligned()};\n  neighbors[Direction<1>::upper_xi()] =\n      Neighbors<1>{{east_id}, OrientationMap<1>::create_aligned()};\n  const Element<1> element{self_id, std::move(neighbors)};\n\n  const DirectionalId west_mortar{Direction<1>::lower_xi(), west_id};\n  const DirectionalId east_mortar{Direction<1>::upper_xi(), east_id};\n\n  domain::Tags::NeighborMesh<1>::type neighbor_mesh{};\n  neighbor_mesh[west_mortar] = mesh;\n  neighbor_mesh[east_mortar] = mesh;\n\n  MockRuntimeSystem runner{{std::move(domain),\n                            std::make_unique<BoundaryTerms<1>>(),\n                            dg::Formulation::StrongInertial}};\n\n  const Slab slab(0.0, 1.0);\n  const TimeStepId time_step_id(true, 0, slab.start());\n  const TimeStepId& next_time_step_id = time_step_id;\n  const auto time_step = slab.duration();\n\n  std::vector<std::tuple<DirectionalId<1>, Rational, Rational>> messages{\n      {west_mortar, {0, 4}, {1, 4}}, {west_mortar, {1, 4}, {2, 4}},\n      {west_mortar, {2, 4}, {3, 4}}, {west_mortar, {3, 4}, {4, 4}},\n      {east_mortar, {0, 2}, {1, 2}}, {east_mortar, {1, 2}, {2, 2}}};\n  std::shuffle(messages.begin(), messages.end(), gen);\n\n  using variables_tag = metavars::system::variables_tag;\n  variables_tag::type evolved_vars(2, 0.0);\n\n  ActionTesting::emplace_component_and_initialize<comp>(\n      &runner, self_id,\n      {10, time_step_id, next_time_step_id, time_step,\n       std::make_unique<TimeSteppers::AdamsBashforth>(1), evolved_vars, mesh,\n       element, neighbor_mesh});\n\n  // Initialize the mortars\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  using mortar_tags_list = BoundaryTerms<1>::dg_package_field_tags;\n  constexpr size_t mortar_data_size =\n      Variables<mortar_tags_list>::number_of_independent_components;\n\n  db::mutate<Tags::Next<Tags::TimeStepId>,\n             evolution::dg::Tags::MortarDataHistory<1>>(\n      [&](const gsl::not_null<TimeStepId*> id,\n          const gsl::not_null<DirectionalIdMap<\n              1, TimeSteppers::BoundaryHistory<evolution::dg::MortarData<1>,\n                                               evolution::dg::MortarData<1>,\n                                               DataVector>>*>\n              mortar_history) {\n        *id = TimeStepId(true, 0, slab.end());\n\n        evolution::dg::MortarData<1> local_data{};\n        local_data.mortar_data.emplace(mortar_data_size, 0.0);\n        local_data.face_normal_magnitude.emplace(1_st, 1.0);\n        local_data.face_mesh.emplace();\n        local_data.mortar_mesh.emplace();\n\n        mortar_history->at(west_mortar)\n            .local()\n            .insert(time_step_id, 1, local_data);\n        mortar_history->at(east_mortar)\n            .local()\n            .insert(time_step_id, 1, local_data);\n      },\n      make_not_null(\n          &ActionTesting::get_databox<comp>(make_not_null(&runner), self_id)));\n\n  while (not messages.empty()) {\n    const auto [mortar_id, send_time, next_time] = messages.back();\n    messages.pop_back();\n\n    const Mesh<0> mortar_mesh{};\n    const DataVector flux_data{mortar_data_size, 0.0};\n    const evolution::dg::BoundaryData<1> data{\n        mesh,        std::nullopt,\n        mortar_mesh, std::nullopt,\n        {flux_data}, TimeStepId(true, 0, Time(slab, next_time)),\n        1,           1};\n\n    using inbox =\n        evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<1, false>;\n\n    Parallel::receive_data<inbox>(\n        runner.mock_distributed_objects<comp>().at(self_id),\n        TimeStepId(true, 0, Time(slab, send_time)), std::pair{mortar_id, data});\n\n    REQUIRE(ActionTesting::next_action_if_ready<comp>(\n                make_not_null(&runner), self_id) == messages.empty());\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.ApplyBoundaryCorrections\",\n                  \"[Unit][Evolution][Actions]\") {\n  PUPable_reg(TimeSteppers::AdamsBashforth);\n  tmpl::for_each<tmpl::integral_list<size_t, 1, 2, 3>>([](auto dim_v) {\n    tmpl::for_each<tmpl::integral_list<bool, false, true>>(\n        [&dim_v](auto lts_v) {\n          tmpl::for_each<tmpl::integral_list<\n              TestHelpers::SystemType, TestHelpers::SystemType::Conservative,\n              TestHelpers::SystemType::Nonconservative,\n              TestHelpers::SystemType::Mixed>>([&dim_v, &lts_v](auto system_v) {\n            (void)dim_v, (void)lts_v;\n            test<tmpl::type_from<decltype(dim_v)>::value,\n                 tmpl::type_from<decltype(lts_v)>::value,\n                 tmpl::type_from<decltype(system_v)>::value>();\n          });\n        });\n  });\n\n  test_receive_order();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Actions/Test_BoundaryConditions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/BoundaryConditionsImpl.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/SystemType.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// We use offsets and then fill the Variables with offset+DataVector index\n// This is a way to generate unique but known numbers\nconstexpr double offset_dt_evolved_vars = 1.0;\nconstexpr double offset_evolved_vars = 20.0;\nconstexpr double offset_temporaries = 50.0;\nconstexpr double offset_volume_fluxes = 200.0;\nconstexpr double offset_partial_derivs = 3000.0;\nconstexpr double offset_primitive_vars = 7000.0;\nconstexpr double offset_boundary_condition = 10000.0;\nconstexpr double offset_boundary_correction = 20000.0;\nconst std::array expected_velocities{1.2, -1.4, 0.3};\n\nnamespace Tags {\nstruct BoundaryCorrectionVolumeTag : db::SimpleTag {\n  using type = double;\n};\n\nstruct BoundaryConditionVolumeTag : db::SimpleTag {\n  using type = double;\n};\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n\nstruct Var3Squared : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct PrimVar1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct PrimVar2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nstruct InverseSpatialMetric : db::SimpleTag {\n  using type = tnsr::II<DataVector, Dim, Frame::Inertial>;\n};\n}  // namespace Tags\n\nusing SystemType = TestHelpers::evolution::dg::Actions::SystemType;\n\ntemplate <size_t Dim, bool HasPrims, SystemType SysType,\n          bool HasInverseSpatialMetric>\nstruct BoundaryTerms final : public evolution::BoundaryCorrection {\n  struct MaxAbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n  explicit BoundaryTerms(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BoundaryTerms);  // NOLINT\n  BoundaryTerms(const bool mesh_is_moving, const double sign_of_normal)\n      : mesh_is_moving_(mesh_is_moving), sign_of_normal_(sign_of_normal) {}\n  BoundaryTerms() = default;\n  BoundaryTerms(const BoundaryTerms&) = default;\n  BoundaryTerms& operator=(const BoundaryTerms&) = default;\n  BoundaryTerms(BoundaryTerms&&) = default;\n  BoundaryTerms& operator=(BoundaryTerms&&) = default;\n  ~BoundaryTerms() override = default;\n\n  using variables_tags = tmpl::list<Tags::Var1, Tags::Var2<Dim>>;\n  using variables_tag = ::Tags::Variables<variables_tags>;\n\n  std::unique_ptr<evolution::BoundaryCorrection> get_clone() const override {\n    return std::make_unique<BoundaryTerms>(*this);\n  }\n\n  void pup(PUP::er& p) override {  // NOLINT\n    BoundaryCorrection::pup(p);\n    p | mesh_is_moving_;\n    p | sign_of_normal_;\n  }\n\n  using dg_package_field_tags = tmpl::push_back<\n      tmpl::append<db::wrap_tags_in<::Tags::NormalDotFlux, variables_tags>,\n                   variables_tags>,\n      MaxAbsCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<Tags::Var3Squared>;\n  using dg_package_data_primitive_tags =\n      tmpl::conditional_t<HasPrims, tmpl::list<Tags::PrimVar1>, tmpl::list<>>;\n  using dg_package_data_volume_tags = tmpl::conditional_t<\n      HasPrims, tmpl::list<Tags::BoundaryCorrectionVolumeTag>, tmpl::list<>>;\n  using dg_boundary_terms_volume_tags = tmpl::conditional_t<\n      HasPrims, tmpl::list<Tags::BoundaryCorrectionVolumeTag>, tmpl::list<>>;\n\n  // Conservative system, flat background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    if (mesh_velocity.has_value()) {\n      REQUIRE(normal_dot_mesh_velocity.has_value());\n      CHECK_ITERABLE_APPROX(*normal_dot_mesh_velocity,\n                            dot_product(normal_covector, *mesh_velocity));\n    }\n\n    *out_normal_dot_flux_var1 = dot_product(flux_var1, normal_covector);\n    if (mesh_velocity.has_value()) {\n      get(*out_normal_dot_flux_var1) -=\n          get(var1) * get(dot_product(*mesh_velocity, normal_covector));\n    }\n    for (size_t i = 0; i < Dim; ++i) {\n      out_normal_dot_flux_var2->get(i) =\n          flux_var2.get(i, 0) * normal_covector.get(0);\n      if (mesh_velocity.has_value()) {\n        out_normal_dot_flux_var2->get(i) -=\n            var2.get(i) * get<0>(*mesh_velocity) * normal_covector.get(0);\n      }\n      for (size_t j = 1; j < Dim; ++j) {\n        out_normal_dot_flux_var2->get(i) +=\n            flux_var2.get(i, j) * normal_covector.get(j);\n        if (mesh_velocity.has_value()) {\n          out_normal_dot_flux_var2->get(i) -=\n              var2.get(i) * mesh_velocity->get(j) * normal_covector.get(j);\n        }\n      }\n    }\n    *out_var1 = var1;\n    *out_var2 = var2;\n\n    get(*max_abs_char_speed) = 2.0 * max(get(var3_squared));\n\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*max_abs_char_speed) += get(*normal_dot_mesh_velocity);\n    }\n    return max(get(*max_abs_char_speed));\n  }\n\n  // Conservative system, curved background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    CHECK_ITERABLE_APPROX(get(dot_product(normal_covector, normal_vector)),\n                          DataVector(get(var1).size(), 1.0));\n    return dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                           out_var1, out_var2, max_abs_char_speed, var1, var2,\n                           flux_var1, flux_var2, var3_squared, normal_covector,\n                           mesh_velocity, normal_dot_mesh_velocity);\n  }\n\n  // Conservative system with prim vars, flat background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n      const Scalar<DataVector>& prim_var1,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n\n      const double volume_number) const {\n    dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                    out_var1, out_var2, max_abs_char_speed, var1, var2,\n                    flux_var1, flux_var2, var3_squared, normal_covector,\n                    mesh_velocity, normal_dot_mesh_velocity);\n    get(*out_var1) += get(prim_var1) + volume_number;\n    if (mesh_velocity.has_value()) {\n      get(*out_normal_dot_flux_var1) -=\n          (get(prim_var1) + volume_number) *\n          get(dot_product(*mesh_velocity, normal_covector));\n    }\n    return max(get(*max_abs_char_speed));\n  }\n\n  // Conservative system with prim vars, curved background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n      const Scalar<DataVector>& prim_var1,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n\n      const double volume_number) const {\n    CHECK_ITERABLE_APPROX(get(dot_product(normal_covector, normal_vector)),\n                          DataVector(get(var1).size(), 1.0));\n    return dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                           out_var1, out_var2, max_abs_char_speed, var1, var2,\n                           flux_var1, flux_var2, var3_squared, prim_var1,\n                           normal_covector, mesh_velocity,\n                           normal_dot_mesh_velocity, volume_number);\n  }\n\n  // Nonconservative system, flat background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    if (mesh_velocity.has_value()) {\n      REQUIRE(normal_dot_mesh_velocity.has_value());\n      CHECK_ITERABLE_APPROX(*normal_dot_mesh_velocity,\n                            dot_product(normal_covector, *mesh_velocity));\n    }\n\n    get(*out_normal_dot_flux_var1) =\n        get(var1) + get(dot_product(var2, normal_covector));\n    if (mesh_velocity.has_value()) {\n      get(*out_normal_dot_flux_var1) -=\n          get(dot_product(*mesh_velocity, normal_covector));\n    }\n    for (size_t i = 0; i < Dim; ++i) {\n      out_normal_dot_flux_var2->get(i) =\n          normal_covector.get(i) * normal_covector.get(0) * var2.get(0);\n      if (mesh_velocity.has_value()) {\n        out_normal_dot_flux_var2->get(i) -=\n            var2.get(i) * get<0>(*mesh_velocity) * normal_covector.get(0);\n      }\n      for (size_t j = 1; j < Dim; ++j) {\n        out_normal_dot_flux_var2->get(i) +=\n            normal_covector.get(i) * normal_covector.get(j) * var2.get(j);\n        if (mesh_velocity.has_value()) {\n          out_normal_dot_flux_var2->get(i) -=\n              var2.get(i) * mesh_velocity->get(j) * normal_covector.get(j);\n        }\n      }\n    }\n    *out_var1 = var1;\n    *out_var2 = var2;\n\n    get(*max_abs_char_speed) = 2.0 * max(get(var3_squared));\n\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*max_abs_char_speed) += get(*normal_dot_mesh_velocity);\n    }\n    return max(get(*max_abs_char_speed));\n  }\n\n  // Nonconservative system, curved background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    CHECK_ITERABLE_APPROX(get(dot_product(normal_covector, normal_vector)),\n                          DataVector(get(var1).size(), 1.0));\n    return dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                           out_var1, out_var2, max_abs_char_speed, var1, var2,\n                           var3_squared, normal_covector, mesh_velocity,\n                           normal_dot_mesh_velocity);\n  }\n\n  // Mixed system, no prims, flat background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    if (mesh_velocity.has_value()) {\n      REQUIRE(normal_dot_mesh_velocity.has_value());\n      CHECK_ITERABLE_APPROX(*normal_dot_mesh_velocity,\n                            dot_product(normal_covector, *mesh_velocity));\n    }\n\n    get(*out_normal_dot_flux_var1) =\n        get(var1) + get(dot_product(var2, normal_covector));\n    if (mesh_velocity.has_value()) {\n      get(*out_normal_dot_flux_var1) -=\n          get(dot_product(*mesh_velocity, normal_covector));\n    }\n    for (size_t i = 0; i < Dim; ++i) {\n      out_normal_dot_flux_var2->get(i) =\n          flux_var2.get(i, 0) * normal_covector.get(0);\n      if (mesh_velocity.has_value()) {\n        out_normal_dot_flux_var2->get(i) -=\n            var2.get(i) * get<0>(*mesh_velocity) * normal_covector.get(0);\n      }\n      for (size_t j = 1; j < Dim; ++j) {\n        out_normal_dot_flux_var2->get(i) +=\n            flux_var2.get(i, j) * normal_covector.get(j);\n        if (mesh_velocity.has_value()) {\n          out_normal_dot_flux_var2->get(i) -=\n              var2.get(i) * mesh_velocity->get(j) * normal_covector.get(j);\n        }\n      }\n    }\n    *out_var1 = var1;\n    *out_var2 = var2;\n\n    get(*max_abs_char_speed) = 2.0 * max(get(var3_squared));\n\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*max_abs_char_speed) += get(*normal_dot_mesh_velocity);\n    }\n    return max(get(*max_abs_char_speed));\n  }\n\n  // Mixed system, no prims, curved background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    CHECK_ITERABLE_APPROX(get(dot_product(normal_covector, normal_vector)),\n                          DataVector(get(var1).size(), 1.0));\n    return dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                           out_var1, out_var2, max_abs_char_speed, var1, var2,\n                           flux_var2, var3_squared, normal_covector,\n                           mesh_velocity, normal_dot_mesh_velocity);\n  }\n\n  // Mixed system with prims, flat background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const Scalar<DataVector>& prim_var1,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n\n      const double volume_number) const {\n    dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                    out_var1, out_var2, max_abs_char_speed, var1, var2,\n                    flux_var2, var3_squared, normal_covector, mesh_velocity,\n                    normal_dot_mesh_velocity);\n    get(*out_var1) += get(prim_var1) + volume_number;\n    return max(get(*max_abs_char_speed));\n  }\n\n  // Mixed system with prims, curved background\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const Scalar<DataVector>& prim_var1,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n\n      const double volume_number) const {\n    CHECK_ITERABLE_APPROX(get(dot_product(normal_covector, normal_vector)),\n                          DataVector(get(var1).size(), 1.0));\n    return dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                           out_var1, out_var2, max_abs_char_speed, var1, var2,\n                           flux_var2, var3_squared, prim_var1, normal_covector,\n                           mesh_velocity, normal_dot_mesh_velocity,\n                           volume_number);\n  }\n\n  void dg_boundary_terms(\n      const gsl::not_null<Scalar<DataVector>*> boundary_correction_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_var2,\n      const Scalar<DataVector>& int_normal_dot_flux_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& int_normal_dot_flux_var2,\n      const Scalar<DataVector>& int_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& int_var2,\n      const Scalar<DataVector>& int_max_abs_char_speed,\n      const Scalar<DataVector>& ext_normal_dot_flux_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& ext_normal_dot_flux_var2,\n      const Scalar<DataVector>& ext_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& ext_var2,\n      const Scalar<DataVector>& ext_max_abs_char_speed,\n      const dg::Formulation formulation) const {\n    static_assert(Dim == 1,\n                  \"Flux dot normal assumes 1d, mostly because normal vector is \"\n                  \"assumed to be 1d.\");\n\n    get(*boundary_correction_var1) =\n        offset_boundary_correction *\n        (formulation == dg::Formulation::WeakInertial ? 2.0 : 1.0);\n    for (size_t i = 0; i < Dim; ++i) {\n      boundary_correction_var2->get(i) = offset_boundary_correction + 1.0 + i;\n    }\n    const size_t num_pts = get(int_var1).size();\n\n    const double mesh_velocity = mesh_is_moving_ ? 1.2 : 0.0;\n    const double normalization_factor =\n        HasInverseSpatialMetric ? sqrt(offset_temporaries + 1.0) : 1.0;\n    if (SysType == SystemType::Conservative) {\n      CHECK_ITERABLE_APPROX(\n          get(int_normal_dot_flux_var1),\n          DataVector(sign_of_normal_ / normalization_factor *\n                     (offset_volume_fluxes - mesh_velocity * get(int_var1))));\n    } else {\n      CHECK_ITERABLE_APPROX(\n          get(int_normal_dot_flux_var1),\n          DataVector(offset_evolved_vars +\n                     sign_of_normal_ / normalization_factor * get<0>(int_var2) -\n                     sign_of_normal_ / normalization_factor * mesh_velocity));\n    }\n\n    if (SysType == SystemType::Conservative) {\n      for (size_t i = 0; i < Dim; ++i) {\n        CHECK_ITERABLE_APPROX(\n            int_normal_dot_flux_var2.get(i),\n            DataVector(sign_of_normal_ / normalization_factor *\n                       (offset_volume_fluxes + 1.0 + i -\n                        mesh_velocity * int_var2.get(i))));\n      }\n    } else if (SysType == SystemType::Mixed) {\n      for (size_t i = 0; i < Dim; ++i) {\n        CHECK_ITERABLE_APPROX(\n            int_normal_dot_flux_var2.get(i),\n            DataVector(\n                sign_of_normal_ / normalization_factor *\n                (offset_volume_fluxes + i - mesh_velocity * int_var2.get(i))));\n      }\n    } else {\n      for (size_t i = 0; i < Dim; ++i) {\n        DataVector expected{num_pts, 0.0};\n        for (size_t j = 0; j < Dim; ++j) {\n          expected += int_var2.get(j) /\n                          square(normalization_factor)  // n_i n_j var2^j, bot\n                                                        // n_i = (\\pm 1) in 1d\n                      - sign_of_normal_ / normalization_factor *\n                            int_var2.get(i) * mesh_velocity;  // var2^i v^j n_j\n        }\n        CHECK_ITERABLE_APPROX(int_normal_dot_flux_var2.get(i), expected);\n      }\n    }\n    CHECK_ITERABLE_APPROX(\n        get(int_var1),\n        DataVector(num_pts,\n                   offset_evolved_vars +\n                       (HasPrims ? offset_primitive_vars + 3.5 : 0.0)));\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_APPROX(int_var2.get(i),\n                            DataVector(num_pts, offset_evolved_vars + 1.0 + i));\n    }\n    CHECK_ITERABLE_APPROX(\n        get(int_max_abs_char_speed),\n        DataVector(num_pts,\n                   2.0 * offset_temporaries +\n                       sign_of_normal_ / normalization_factor * mesh_velocity));\n\n    if (SysType == SystemType::Conservative) {\n      // The two comes from the dg_package_data also subtracting off the mesh\n      // velocity.\n      CHECK_ITERABLE_APPROX(\n          get(ext_normal_dot_flux_var1),\n          DataVector{-sign_of_normal_ / normalization_factor *\n                     (offset_boundary_condition + 1.0 + Dim -\n                      2 * mesh_velocity * offset_boundary_condition -\n                      (HasPrims ? mesh_velocity * (offset_boundary_condition +\n                                                   3.0 + 2 * Dim + 3.5)\n                                : 0.0))});\n    } else {\n      CHECK_ITERABLE_APPROX(\n          get(ext_normal_dot_flux_var1),\n          DataVector(\n              (offset_boundary_condition -\n               sign_of_normal_ / normalization_factor * get<0>(ext_var2) +\n               sign_of_normal_ / normalization_factor * mesh_velocity)));\n    }\n    if (SysType == SystemType::Conservative or SysType == SystemType::Mixed) {\n      for (size_t i = 0; i < Dim; ++i) {\n        CHECK_ITERABLE_APPROX(\n            ext_normal_dot_flux_var2.get(i),\n            DataVector(-sign_of_normal_ / normalization_factor *\n                       (offset_boundary_condition + 2.0 + Dim + i -\n                        2.0 * mesh_velocity * ext_var2.get(i))));\n      }\n    } else {\n      static_assert(Dim == 1);\n      CHECK_ITERABLE_APPROX(\n          get<0>(ext_normal_dot_flux_var2),\n          DataVector((get<0>(ext_var2) / square(normalization_factor) +\n                      sign_of_normal_ / normalization_factor *\n                          get<0>(ext_var2) * mesh_velocity)));\n    }\n    CHECK_ITERABLE_APPROX(\n        get(ext_var1),\n        DataVector(num_pts, offset_boundary_condition +\n                                (HasPrims ? offset_boundary_condition +\n                                                2.0 * Dim + 3.0 + 3.5\n                                          : 0.0)));\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_APPROX(\n          ext_var2.get(i),\n          DataVector(num_pts, offset_boundary_condition + 1.0 + i));\n    }\n    CHECK_ITERABLE_APPROX(\n        get(ext_max_abs_char_speed),\n        DataVector(num_pts,\n                   2.0 * (offset_boundary_condition + 2.0 + 2 * Dim) -\n                       sign_of_normal_ / normalization_factor * mesh_velocity));\n  }\n\n  void dg_boundary_terms(\n      const gsl::not_null<Scalar<DataVector>*> boundary_correction_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_var2,\n      const Scalar<DataVector>& int_normal_dot_flux_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& int_normal_dot_flux_var2,\n      const Scalar<DataVector>& int_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& int_var2,\n      const Scalar<DataVector>& int_max_abs_char_speed,\n      const Scalar<DataVector>& ext_normal_dot_flux_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& ext_normal_dot_flux_var2,\n      const Scalar<DataVector>& ext_var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& ext_var2,\n      const Scalar<DataVector>& ext_max_abs_char_speed,\n      const dg::Formulation formulation, const double& volume_number) const {\n    CHECK(volume_number == 3.5);\n    dg_boundary_terms(boundary_correction_var1, boundary_correction_var2,\n                      int_normal_dot_flux_var1, int_normal_dot_flux_var2,\n                      int_var1, int_var2, int_max_abs_char_speed,\n                      ext_normal_dot_flux_var1, ext_normal_dot_flux_var2,\n                      ext_var1, ext_var2, ext_max_abs_char_speed, formulation);\n  }\n\n private:\n  bool mesh_is_moving_{false};\n  double sign_of_normal_{0.0};\n};\n\ntemplate <size_t Dim, bool HasPrims, SystemType SysType,\n          bool HasInverseSpatialMetric>\nPUP::able::PUP_ID\n    // NOLINTNEXTLINE\n    BoundaryTerms<Dim, HasPrims, SysType, HasInverseSpatialMetric>::my_PUP_ID =\n        0;\n\n// Forward declare different boundary conditions.\n//\n// We template them on the system so we can test that we have access to all the\n// different tags that we should have access to.\ntemplate <typename System>\nclass DemandOutgoingCharSpeeds;\ntemplate <typename System>\nclass TimeDerivative;\ntemplate <typename System>\nclass Ghost;\ntemplate <typename System>\nclass GhostAndTimeDerivative;\n\ntemplate <typename System>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg)\n      : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\n  void pup(PUP::er& p) override {\n    domain::BoundaryConditions::BoundaryCondition::pup(p);\n  }\n};\n\ntemplate <typename System>\nclass DemandOutgoingCharSpeeds : public BoundaryCondition<System> {\n public:\n  DemandOutgoingCharSpeeds() = default;\n  explicit DemandOutgoingCharSpeeds(const bool mesh_is_moving)\n      : mesh_is_moving_(mesh_is_moving) {}\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg)\n      : BoundaryCondition<System>(msg) {}\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override {\n    return std::make_unique<DemandOutgoingCharSpeeds<System>>(*this);\n  }\n\n  static constexpr ::evolution::BoundaryConditions::Type bc_type =\n      ::evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override {\n    BoundaryCondition<System>::pup(p);\n    p | mesh_is_moving_;\n  }\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<Tags::Var1, Tags::Var2<System::volume_dim>>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<Tags::PrimVar1, Tags::PrimVar2<System::volume_dim>>;\n  using dg_interior_temporary_tags = tmpl::list<Tags::Var3Squared>;\n  using dg_interior_dt_vars_tags = tmpl::list<::Tags::dt<Tags::Var1>>;\n  using dg_gridless_tags = tmpl::list<Tags::BoundaryConditionVolumeTag>;\n\n  std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    CHECK(volume_number == 2.5);\n    const size_t num_pts = get(var1).size();\n    CHECK_ITERABLE_APPROX(get(var3_squared),\n                          DataVector(num_pts, offset_temporaries));\n    CHECK_ITERABLE_APPROX(get(var1), DataVector(num_pts, offset_evolved_vars));\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(var2.get(i),\n                            DataVector(num_pts, offset_evolved_vars + 1 + i));\n      for (size_t j = 0; j < num_pts; ++j) {\n        // Catch doesn't allow `CHECK(a or b) so we do `CHECK((a or b))` instead\n        if constexpr (System::volume_dim == 1) {\n          const double normalization_factor =\n              System::has_inverse_spatial_metric\n                  ? sqrt(offset_temporaries + 1.0)\n                  : 1.0;\n          CHECK((approx(outward_directed_normal_covector.get(i)[j]) ==\n                     1.0 / normalization_factor or\n                 approx(outward_directed_normal_covector.get(i)[j]) ==\n                     -1.0 / normalization_factor));\n        } else {\n          static_assert(not System::has_inverse_spatial_metric);\n          CHECK((approx(outward_directed_normal_covector.get(i)[j]) == 1.0 or\n                 approx(outward_directed_normal_covector.get(i)[j]) == -1.0 or\n                 approx(outward_directed_normal_covector.get(i)[j]) == 0.0));\n        }\n      }\n    }\n    CHECK_ITERABLE_APPROX(get(dt_var1),\n                          DataVector(num_pts, offset_dt_evolved_vars));\n    REQUIRE(face_mesh_velocity.has_value() == mesh_is_moving_);\n    if (mesh_is_moving_) {\n      for (size_t i = 0; i < System::volume_dim; ++i) {\n        CHECK_ITERABLE_APPROX(\n            face_mesh_velocity->get(i),\n            DataVector(num_pts, gsl::at(expected_velocities, i)));\n      }\n    }\n    return std::nullopt;\n  }\n\n  std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    dg_demand_outgoing_char_speeds(face_mesh_velocity,\n                                   outward_directed_normal_covector, var1, var2,\n                                   var3_squared, dt_var1, volume_number);\n    CHECK_ITERABLE_APPROX(get(dot_product(outward_directed_normal_covector,\n                                          outward_directed_normal_vector)),\n                          DataVector(get(var1).size(), 1.0));\n    return std::nullopt;\n  }\n\n  std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    dg_demand_outgoing_char_speeds(face_mesh_velocity,\n                                   outward_directed_normal_covector, var1, var2,\n                                   var3_squared, dt_var1, volume_number);\n    const size_t num_pts = get(var1).size();\n    CHECK_ITERABLE_APPROX(get(prim_var1),\n                          DataVector(num_pts, offset_primitive_vars));\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(prim_var2.get(i),\n                            DataVector(num_pts, offset_primitive_vars + 1 + i));\n    }\n    return std::nullopt;\n  }\n\n  std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    dg_demand_outgoing_char_speeds(\n        face_mesh_velocity, outward_directed_normal_covector, var1, var2,\n        prim_var1, prim_var2, var3_squared, dt_var1, volume_number);\n    CHECK_ITERABLE_APPROX(get(dot_product(outward_directed_normal_covector,\n                                          outward_directed_normal_vector)),\n                          DataVector(get(var1).size(), 1.0));\n    return std::nullopt;\n  }\n\n private:\n  bool mesh_is_moving_{false};\n};\n\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID DemandOutgoingCharSpeeds<System>::my_PUP_ID = 0;\n\ntemplate <typename System>\nclass Ghost : public BoundaryCondition<System> {\n public:\n  Ghost() = default;\n  explicit Ghost(const bool mesh_is_moving) : mesh_is_moving_(mesh_is_moving) {}\n  Ghost(Ghost&&) = default;\n  Ghost& operator=(Ghost&&) = default;\n  Ghost(const Ghost&) = default;\n  Ghost& operator=(const Ghost&) = default;\n  ~Ghost() override = default;\n\n  explicit Ghost(CkMigrateMessage* msg) : BoundaryCondition<System>(msg) {}\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, Ghost);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override {\n    return std::make_unique<Ghost<System>>(*this);\n  }\n\n  static constexpr ::evolution::BoundaryConditions::Type bc_type =\n      ::evolution::BoundaryConditions::Type::Ghost;\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override {\n    BoundaryCondition<System>::pup(p);\n    p | mesh_is_moving_;\n  }\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<Tags::Var1, Tags::Var2<System::volume_dim>>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<Tags::PrimVar1, Tags::PrimVar2<System::volume_dim>>;\n  using dg_interior_temporary_tags = tmpl::list<Tags::Var3Squared>;\n  using dg_interior_dt_vars_tags = tmpl::list<::Tags::dt<Tags::Var1>>;\n  using dg_gridless_tags = tmpl::list<Tags::BoundaryConditionVolumeTag>;\n\n  // Nonconservative system, flat background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    get(*out_var1) = offset_boundary_condition;\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      out_var2->get(i) =\n          offset_boundary_condition + 1.0 + static_cast<double>(i);\n    }\n    get(*out_var3_squared) = offset_boundary_condition + 1.0 +\n                             (2 + System::volume_dim) * System::volume_dim;\n\n    CHECK(volume_number == 2.5);\n    const size_t num_pts = get(var1).size();\n    CHECK_ITERABLE_APPROX(get(var3_squared),\n                          DataVector(num_pts, offset_temporaries));\n    CHECK_ITERABLE_APPROX(get(var1), DataVector(num_pts, offset_evolved_vars));\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(var2.get(i),\n                            DataVector(num_pts, offset_evolved_vars + 1 + i));\n      for (size_t j = 0; j < num_pts; ++j) {\n        // Catch doesn't allow `CHECK(a or b) so we do `CHECK((a or b))` instead\n        if constexpr (System::volume_dim == 1) {\n          const double normalization_factor =\n              System::has_inverse_spatial_metric\n                  ? sqrt(offset_temporaries + 1.0)\n                  : 1.0;\n          CHECK((approx(outward_directed_normal_covector.get(i)[j]) ==\n                     1.0 / normalization_factor or\n                 approx(outward_directed_normal_covector.get(i)[j]) ==\n                     -1.0 / normalization_factor));\n        } else {\n          static_assert(not System::has_inverse_spatial_metric);\n          CHECK((approx(outward_directed_normal_covector.get(i)[j]) == 1.0 or\n                 approx(outward_directed_normal_covector.get(i)[j]) == -1.0 or\n                 approx(outward_directed_normal_covector.get(i)[j]) == 0.0));\n        }\n      }\n    }\n    CHECK_ITERABLE_APPROX(get(dt_var1),\n                          DataVector(num_pts, offset_dt_evolved_vars));\n    REQUIRE(face_mesh_velocity.has_value() == mesh_is_moving_);\n    if (mesh_is_moving_) {\n      for (size_t i = 0; i < System::volume_dim; ++i) {\n        CHECK_ITERABLE_APPROX(\n            face_mesh_velocity->get(i),\n            DataVector(num_pts, gsl::at(expected_velocities, i)));\n      }\n    }\n    return std::nullopt;\n  }\n\n  // Nonconservative system, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    return dg_ghost(out_var1, out_var2, out_var3_squared, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, var3_squared,\n                    dt_var1, volume_number);\n  }\n\n  // Mixed conservative non-conservative system, no prims, flat background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    dg_ghost(out_var1, out_var2, out_var3_squared, face_mesh_velocity,\n             outward_directed_normal_covector, var1, var2, var3_squared,\n             dt_var1, volume_number);\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      for (size_t j = 0; j < System::volume_dim; ++j) {\n        flux_var2->get(i, j) = offset_boundary_condition + 1.0 +\n                               static_cast<double>(i + 2 * System::volume_dim);\n      }\n    }\n    return std::nullopt;\n  }\n\n  // Mixed conservative non-conservative system, no prims, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    return dg_ghost(out_var1, out_var2, flux_var2, out_var3_squared,\n                    face_mesh_velocity, outward_directed_normal_covector, var1,\n                    var2, var3_squared, dt_var1, volume_number);\n  }\n\n  // Mixed conservative non-conservative system, with prims, flat background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<Scalar<DataVector>*> out_prim_var1,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    dg_ghost(out_var1, out_var2, flux_var2, out_var3_squared,\n             face_mesh_velocity, outward_directed_normal_covector, var1, var2,\n             var3_squared, dt_var1, volume_number);\n    get(*out_prim_var1) = get(*out_var3_squared) + 1.0;\n    const size_t num_pts = get(var1).size();\n    CHECK_ITERABLE_APPROX(get(prim_var1),\n                          DataVector(num_pts, offset_primitive_vars));\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(prim_var2.get(i),\n                            DataVector(num_pts, offset_primitive_vars + 1 + i));\n    }\n    return std::nullopt;\n  }\n\n  // Mixed conservative non-conservative system, with prims, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<Scalar<DataVector>*> out_prim_var1,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    return dg_ghost(out_var1, out_var2, flux_var2, out_var3_squared,\n                    out_prim_var1, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, prim_var1,\n                    prim_var2, var3_squared, dt_var1, volume_number);\n  }\n\n  // Conservative system, no prims, flat background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var1,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    dg_ghost(out_var1, out_var2, flux_var2, out_var3_squared,\n             face_mesh_velocity, outward_directed_normal_covector, var1, var2,\n             var3_squared, dt_var1, volume_number);\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      flux_var1->get(i) = offset_boundary_condition + 1.0 +\n                          static_cast<double>(i + System::volume_dim);\n    }\n    return std::nullopt;\n  }\n\n  // Conservative system, no prims, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var1,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    dg_ghost(out_var1, out_var2, flux_var1, flux_var2, out_var3_squared,\n             face_mesh_velocity, outward_directed_normal_covector, var1, var2,\n             var3_squared, dt_var1, volume_number);\n    return std::nullopt;\n  }\n\n  // Conservative system, with prims\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var1,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<Scalar<DataVector>*> out_prim_var1,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    dg_ghost(out_var1, out_var2, flux_var1, flux_var2, out_var3_squared,\n             face_mesh_velocity, outward_directed_normal_covector, var1, var2,\n             var3_squared, dt_var1, volume_number);\n    get(*out_prim_var1) = get(*out_var3_squared) + 1.0;\n    const size_t num_pts = get(var1).size();\n    CHECK_ITERABLE_APPROX(get(prim_var1),\n                          DataVector(num_pts, offset_primitive_vars));\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(prim_var2.get(i),\n                            DataVector(num_pts, offset_primitive_vars + 1 + i));\n    }\n    return std::nullopt;\n  }\n\n  // Conservative system, with prims, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var1,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<Scalar<DataVector>*> out_prim_var1,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    return dg_ghost(out_var1, out_var2, flux_var1, flux_var2, out_var3_squared,\n                    out_prim_var1, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, prim_var1,\n                    prim_var2, var3_squared, dt_var1, volume_number);\n  }\n\n  // public so that GhostAndTimeDerivative can call into this\n  void check_normal_vector_set_inverse_spatial_metric(\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector) const {\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(outward_directed_normal_covector,\n                        outward_directed_normal_vector)),\n        DataVector(get<0>(outward_directed_normal_vector).size(), 1.0));\n    for (size_t i = 0; i < inv_spatial_metric->size(); ++i) {\n      (*inv_spatial_metric)[i] =\n          DataVector{get<0>(outward_directed_normal_vector).size(),\n                     (offset_temporaries + 1.0 + i)};\n    }\n  }\n\n private:\n  bool mesh_is_moving_{false};\n};\n\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID Ghost<System>::my_PUP_ID = 0;\n\ntemplate <typename System>\nclass TimeDerivative : public BoundaryCondition<System> {\n public:\n  TimeDerivative() = default;\n  TimeDerivative(const bool mesh_is_moving, const double expected_dt_var1)\n      : mesh_is_moving_(mesh_is_moving), expected_dt_var1_(expected_dt_var1) {}\n  TimeDerivative(TimeDerivative&&) = default;\n  TimeDerivative& operator=(TimeDerivative&&) = default;\n  TimeDerivative(const TimeDerivative&) = default;\n  TimeDerivative& operator=(const TimeDerivative&) = default;\n  ~TimeDerivative() override = default;\n\n  explicit TimeDerivative(CkMigrateMessage* msg)\n      : BoundaryCondition<System>(msg) {}\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, TimeDerivative);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override {\n    return std::make_unique<TimeDerivative<System>>(*this);\n  }\n\n  static constexpr ::evolution::BoundaryConditions::Type bc_type =\n      ::evolution::BoundaryConditions::Type::TimeDerivative;\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override {\n    BoundaryCondition<System>::pup(p);\n    p | mesh_is_moving_;\n    p | expected_dt_var1_;\n  }\n\n  using dg_interior_evolved_variables_tags =\n      tmpl::list<Tags::Var1, Tags::Var2<System::volume_dim>>;\n  using dg_interior_primitive_variables_tags =\n      tmpl::list<Tags::PrimVar1, Tags::PrimVar2<System::volume_dim>>;\n  using dg_interior_temporary_tags = tmpl::list<Tags::Var3Squared>;\n  using dg_interior_dt_vars_tags = tmpl::list<::Tags::dt<Tags::Var1>>;\n  using dg_interior_deriv_vars_tags = tmpl::conditional_t<\n      System::system_type == SystemType::Conservative, tmpl::list<>,\n      tmpl::list<::Tags::deriv<Tags::Var1, tmpl::size_t<System::volume_dim>,\n                               Frame::Inertial>>>;\n  using dg_gridless_tags = tmpl::list<Tags::BoundaryConditionVolumeTag>;\n\n  // Conservative, no prims, flat background\n  std::optional<std::string> dg_time_derivative(\n      const gsl::not_null<Scalar<DataVector>*> dt_correction_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          dt_correction_var2,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    CHECK(volume_number == 2.5);\n    const size_t num_pts = get(var1).size();\n    CHECK_ITERABLE_APPROX(get(var3_squared),\n                          DataVector(num_pts, offset_temporaries));\n    CHECK_ITERABLE_APPROX(get(var1), DataVector(num_pts, offset_evolved_vars));\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(var2.get(i),\n                            DataVector(num_pts, offset_evolved_vars + 1 + i));\n      for (size_t j = 0; j < num_pts; ++j) {\n        // Catch doesn't allow `CHECK(a or b) so we do `CHECK((a or b))` instead\n        if constexpr (System::volume_dim == 1) {\n          const double normalization_factor =\n              System::has_inverse_spatial_metric\n                  ? sqrt(offset_temporaries + 1.0)\n                  : 1.0;\n          CHECK((approx(outward_directed_normal_covector.get(i)[j]) ==\n                     1.0 / normalization_factor or\n                 approx(outward_directed_normal_covector.get(i)[j]) ==\n                     -1.0 / normalization_factor));\n        } else {\n          CHECK((approx(outward_directed_normal_covector.get(i)[j]) == 1.0 or\n                 approx(outward_directed_normal_covector.get(i)[j]) == -1.0 or\n                 approx(outward_directed_normal_covector.get(i)[j]) == 0.0));\n        }\n      }\n    }\n    CHECK_ITERABLE_APPROX(get(dt_var1), DataVector(num_pts, expected_dt_var1_));\n\n    REQUIRE(face_mesh_velocity.has_value() == mesh_is_moving_);\n    if (mesh_is_moving_) {\n      for (size_t i = 0; i < System::volume_dim; ++i) {\n        CHECK_ITERABLE_APPROX(\n            face_mesh_velocity->get(i),\n            DataVector(num_pts, gsl::at(expected_velocities, i)));\n      }\n    }\n    get(*dt_correction_var1) = offset_boundary_condition;\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      dt_correction_var2->get(i) =\n          offset_boundary_condition + 1.0 + static_cast<double>(i);\n    }\n    return std::nullopt;\n  }\n\n  // Conservative, no prims, curved background\n  std::optional<std::string> dg_time_derivative(\n      const gsl::not_null<Scalar<DataVector>*> dt_correction_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          dt_correction_var2,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    check_normal_vector(outward_directed_normal_covector,\n                        outward_directed_normal_vector);\n    return dg_time_derivative(dt_correction_var1, dt_correction_var2,\n                              face_mesh_velocity,\n                              outward_directed_normal_covector, var1, var2,\n                              var3_squared, dt_var1, volume_number);\n  }\n\n  // Mixed and non-conservative system, flat background\n  std::optional<std::string> dg_time_derivative(\n      const gsl::not_null<Scalar<DataVector>*> dt_correction_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          dt_correction_var2,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    dg_time_derivative(dt_correction_var1, dt_correction_var2,\n                       face_mesh_velocity, outward_directed_normal_covector,\n                       var1, var2, var3_squared, dt_var1, volume_number);\n    const size_t num_pts = get(var1).size();\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(d_var1.get(i),\n                            DataVector(num_pts, offset_partial_derivs + i));\n    }\n    return std::nullopt;\n  }\n\n  // Mixed and non-conservative system, curved background\n  std::optional<std::string> dg_time_derivative(\n      const gsl::not_null<Scalar<DataVector>*> dt_correction_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          dt_correction_var2,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    check_normal_vector(outward_directed_normal_covector,\n                        outward_directed_normal_vector);\n    return dg_time_derivative(dt_correction_var1, dt_correction_var2,\n                              face_mesh_velocity,\n                              outward_directed_normal_covector, var1, var2,\n                              var3_squared, dt_var1, d_var1, volume_number);\n  }\n\n  // Mixed system with primitive vars, flat background\n  std::optional<std::string> dg_time_derivative(\n      const gsl::not_null<Scalar<DataVector>*> dt_correction_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          dt_correction_var2,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    dg_time_derivative(dt_correction_var1, dt_correction_var2,\n                       face_mesh_velocity, outward_directed_normal_covector,\n                       var1, var2, prim_var1, prim_var2, var3_squared, dt_var1,\n                       volume_number);\n    // Sets the dt_correction again, but that's fine, values stay the same.\n    dg_time_derivative(dt_correction_var1, dt_correction_var2,\n                       face_mesh_velocity, outward_directed_normal_covector,\n                       var1, var2, var3_squared, dt_var1, d_var1,\n                       volume_number);\n    return std::nullopt;\n  }\n\n  // Mixed system with primitive vars, curved background\n  std::optional<std::string> dg_time_derivative(\n      const gsl::not_null<Scalar<DataVector>*> dt_correction_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          dt_correction_var2,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    check_normal_vector(outward_directed_normal_covector,\n                        outward_directed_normal_vector);\n    return dg_time_derivative(\n        dt_correction_var1, dt_correction_var2, face_mesh_velocity,\n        outward_directed_normal_covector, var1, var2, prim_var1, prim_var2,\n        var3_squared, dt_var1, d_var1, volume_number);\n  }\n\n  // Conservative system with primitive vars\n  std::optional<std::string> dg_time_derivative(\n      const gsl::not_null<Scalar<DataVector>*> dt_correction_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          dt_correction_var2,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    dg_time_derivative(dt_correction_var1, dt_correction_var2,\n                       face_mesh_velocity, outward_directed_normal_covector,\n                       var1, var2, var3_squared, dt_var1, volume_number);\n    const size_t num_pts = get(var1).size();\n    CHECK_ITERABLE_APPROX(get(prim_var1),\n                          DataVector(num_pts, offset_primitive_vars));\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(prim_var2.get(i),\n                            DataVector(num_pts, offset_primitive_vars + 1 + i));\n    }\n    return std::nullopt;\n  }\n\n  // Conservative system with primitive vars, curved background\n  std::optional<std::string> dg_time_derivative(\n      const gsl::not_null<Scalar<DataVector>*> dt_correction_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          dt_correction_var2,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    check_normal_vector(outward_directed_normal_covector,\n                        outward_directed_normal_vector);\n    dg_time_derivative(dt_correction_var1, dt_correction_var2,\n                       face_mesh_velocity, outward_directed_normal_covector,\n                       var1, var2, prim_var1, prim_var2, var3_squared, dt_var1,\n                       volume_number);\n    return std::nullopt;\n  }\n\n private:\n  void check_normal_vector(\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector) const {\n    CHECK_ITERABLE_APPROX(\n        get(dot_product(outward_directed_normal_covector,\n                        outward_directed_normal_vector)),\n        DataVector(get<0>(outward_directed_normal_vector).size(), 1.0));\n  }\n\n  bool mesh_is_moving_{false};\n  double expected_dt_var1_{std::numeric_limits<double>::signaling_NaN()};\n};\n\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID TimeDerivative<System>::my_PUP_ID = 0;\n\ntemplate <typename System>\nclass GhostAndTimeDerivative : public BoundaryCondition<System> {\n public:\n  GhostAndTimeDerivative() = default;\n  explicit GhostAndTimeDerivative(const bool mesh_is_moving)\n      : ghost_{mesh_is_moving},\n        time_derivative_{mesh_is_moving, offset_dt_evolved_vars} {}\n  GhostAndTimeDerivative(GhostAndTimeDerivative&&) = default;\n  GhostAndTimeDerivative& operator=(GhostAndTimeDerivative&&) = default;\n  GhostAndTimeDerivative(const GhostAndTimeDerivative&) = default;\n  GhostAndTimeDerivative& operator=(const GhostAndTimeDerivative&) = default;\n  ~GhostAndTimeDerivative() override = default;\n\n  explicit GhostAndTimeDerivative(CkMigrateMessage* msg)\n      : BoundaryCondition<System>(msg) {}\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, GhostAndTimeDerivative);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override {\n    return std::make_unique<GhostAndTimeDerivative<System>>(*this);\n  }\n\n  static constexpr ::evolution::BoundaryConditions::Type bc_type =\n      ::evolution::BoundaryConditions::Type::GhostAndTimeDerivative;\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& p) override {\n    BoundaryCondition<System>::pup(p);\n    p | ghost_;\n    p | time_derivative_;\n  }\n\n  using dg_interior_evolved_variables_tags =\n      typename Ghost<System>::dg_interior_evolved_variables_tags;\n  using dg_interior_primitive_variables_tags =\n      typename Ghost<System>::dg_interior_primitive_variables_tags;\n  using dg_interior_temporary_tags =\n      typename Ghost<System>::dg_interior_temporary_tags;\n  using dg_interior_dt_vars_tags =\n      typename Ghost<System>::dg_interior_dt_vars_tags;\n  using dg_interior_deriv_vars_tags =\n      typename TimeDerivative<System>::dg_interior_deriv_vars_tags;\n  using dg_gridless_tags = typename Ghost<System>::dg_gridless_tags;\n\n  // Nonconservative, flat background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    ghost_.dg_ghost(out_var1, out_var2, out_var3_squared, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, var3_squared,\n                    dt_var1, volume_number);\n    const size_t num_pts = get(var1).size();\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(d_var1.get(i),\n                            DataVector(num_pts, offset_partial_derivs + i));\n    }\n    return std::nullopt;\n  }\n\n  // Nonconservative, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    ghost_.check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    return dg_ghost(out_var1, out_var2, out_var3_squared, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, var3_squared,\n                    dt_var1, d_var1, volume_number);\n  }\n\n  // Mixed conservative non-conservative system, no prims, flat background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    ghost_.dg_ghost(out_var1, out_var2, flux_var2, out_var3_squared,\n                    face_mesh_velocity, outward_directed_normal_covector, var1,\n                    var2, var3_squared, dt_var1, volume_number);\n    const size_t num_pts = get(var1).size();\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(d_var1.get(i),\n                            DataVector(num_pts, offset_partial_derivs + i));\n    }\n    return std::nullopt;\n  }\n\n  // Mixed conservative non-conservative system, no prims, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    ghost_.check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    return dg_ghost(out_var1, out_var2, flux_var2, out_var3_squared,\n                    face_mesh_velocity, outward_directed_normal_covector, var1,\n                    var2, var3_squared, dt_var1, d_var1, volume_number);\n  }\n\n  // Mixed conservative non-conservative system, prims, flat background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<Scalar<DataVector>*> out_prim_var1,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    ghost_.dg_ghost(out_var1, out_var2, flux_var2, out_var3_squared,\n                    out_prim_var1, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, prim_var1,\n                    prim_var2, var3_squared, dt_var1, volume_number);\n    const size_t num_pts = get(var1).size();\n    for (size_t i = 0; i < System::volume_dim; ++i) {\n      CHECK_ITERABLE_APPROX(d_var1.get(i),\n                            DataVector(num_pts, offset_partial_derivs + i));\n    }\n    return std::nullopt;\n  }\n\n  // Mixed conservative non-conservative system, prims, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<Scalar<DataVector>*> out_prim_var1,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& d_var1,\n      const double volume_number) const {\n    ghost_.check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    return dg_ghost(out_var1, out_var2, flux_var2, out_var3_squared,\n                    out_prim_var1, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, prim_var1,\n                    prim_var2, var3_squared, dt_var1, d_var1, volume_number);\n  }\n\n  // Conservative system, no prims\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var1,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    ghost_.dg_ghost(out_var1, out_var2, flux_var1, flux_var2, out_var3_squared,\n                    face_mesh_velocity, outward_directed_normal_covector, var1,\n                    var2, var3_squared, dt_var1, volume_number);\n    return std::nullopt;\n  }\n\n  // Conservative system, no prims, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var1,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    ghost_.check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    dg_ghost(out_var1, out_var2, flux_var1, flux_var2, out_var3_squared,\n             face_mesh_velocity, outward_directed_normal_covector, var1, var2,\n             var3_squared, dt_var1, volume_number);\n    return std::nullopt;\n  }\n\n  // Conservative system, with prims\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var1,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<Scalar<DataVector>*> out_prim_var1,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    ghost_.dg_ghost(out_var1, out_var2, flux_var1, flux_var2, out_var3_squared,\n                    out_prim_var1, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, prim_var1,\n                    prim_var2, var3_squared, dt_var1, volume_number);\n    return std::nullopt;\n  }\n\n  // Conservative system, with prims, curved background\n  std::optional<std::string> dg_ghost(\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          out_var2,\n      const gsl::not_null<\n          tnsr::I<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var1,\n      const gsl::not_null<\n          tnsr::IJ<DataVector, System::volume_dim, Frame::Inertial>*>\n          flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var3_squared,\n      const gsl::not_null<Scalar<DataVector>*> out_prim_var1,\n      const gsl::not_null<\n          tnsr::II<DataVector, System::volume_dim, Frame::Inertial>*>\n          inv_spatial_metric,\n\n      const std::optional<tnsr::I<DataVector, System::volume_dim,\n                                  Frame::Inertial>>& face_mesh_velocity,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_covector,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>&\n          outward_directed_normal_vector,\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, System::volume_dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& prim_var1,\n      const tnsr::i<DataVector, System::volume_dim, Frame::Inertial>& prim_var2,\n      const Scalar<DataVector>& var3_squared, const Scalar<DataVector>& dt_var1,\n      const double volume_number) const {\n    ghost_.check_normal_vector_set_inverse_spatial_metric(\n        inv_spatial_metric, outward_directed_normal_covector,\n        outward_directed_normal_vector);\n    ghost_.dg_ghost(out_var1, out_var2, flux_var1, flux_var2, out_var3_squared,\n                    out_prim_var1, face_mesh_velocity,\n                    outward_directed_normal_covector, var1, var2, prim_var1,\n                    prim_var2, var3_squared, dt_var1, volume_number);\n    return std::nullopt;\n  }\n\n  template <typename... Args>\n  std::optional<std::string> dg_time_derivative(Args&&... args) const {\n    return time_derivative_.dg_time_derivative(std::forward<Args>(args)...);\n  }\n\n private:\n  Ghost<System> ghost_;\n  TimeDerivative<System> time_derivative_;\n};\n\ntemplate <typename System>\n// NOLINTNEXTLINE\nPUP::able::PUP_ID GhostAndTimeDerivative<System>::my_PUP_ID = 0;\n\ntemplate <bool AddTypeAlias, size_t Dim>\nstruct InverseSpatialMetricTagImpl {\n  using inverse_spatial_metric_tag = Tags::InverseSpatialMetric<Dim>;\n};\n\ntemplate <size_t Dim>\nstruct InverseSpatialMetricTagImpl<false, Dim> {};\n\ntemplate <size_t Dim, SystemType SysType, bool HasPrimitiveVariables,\n          bool HasInverseSpatialMetric>\nstruct System\n    : public InverseSpatialMetricTagImpl<HasInverseSpatialMetric, Dim> {\n  static constexpr SystemType system_type = SysType;\n  static constexpr bool has_primitive_and_conservative_vars =\n      HasPrimitiveVariables;\n  static constexpr size_t volume_dim = Dim;\n  static constexpr bool has_inverse_spatial_metric = HasInverseSpatialMetric;\n\n  using boundary_conditions_base = BoundaryCondition<System>;\n\n  using variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::Var1, Tags::Var2<Dim>>>;\n  using flux_variables = tmpl::conditional_t<\n      system_type == SystemType::Conservative,\n      tmpl::list<Tags::Var1, Tags::Var2<Dim>>,\n      tmpl::conditional_t<system_type == SystemType::Nonconservative,\n                          tmpl::list<>, tmpl::list<Tags::Var2<Dim>>>>;\n  using gradient_variables = tmpl::conditional_t<\n      system_type == SystemType::Conservative, tmpl::list<>,\n      tmpl::conditional_t<system_type == SystemType::Nonconservative,\n                          tmpl::list<Tags::Var1, Tags::Var2<Dim>>,\n                          tmpl::list<Tags::Var1>>>;\n  using primitive_variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::PrimVar1, Tags::PrimVar2<Dim>>>;\n\n  struct compute_volume_time_derivative_terms {\n    using temporary_tags = tmpl::append<\n        tmpl::list<Tags::Var3Squared>,\n        tmpl::conditional_t<HasInverseSpatialMetric,\n                            tmpl::list<Tags::InverseSpatialMetric<Dim>>,\n                            tmpl::list<>>>;\n  };\n};\n\n// Note: DemandOutgoingCharSpeeds is intentionally first so it gets applied\n// first. This makes the test easier because the other BCs modify the time\n// derivatives.\ntemplate <typename System>\nusing standard_boundary_conditions =\n    tmpl::list<DemandOutgoingCharSpeeds<System>, Ghost<System>,\n               TimeDerivative<System>, GhostAndTimeDerivative<System>,\n               domain::BoundaryConditions::Periodic<BoundaryCondition<System>>,\n               domain::BoundaryConditions::None<BoundaryCondition<System>>>;\n\ntemplate <typename System>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<BoundaryCondition<System>,\n                             standard_boundary_conditions<System>>>;\n  };\n};\n\ntemplate <typename TagsList>\nvoid fill_variables(const gsl::not_null<Variables<TagsList>*> variables,\n                    const double offset) {\n  double count = offset;\n  tmpl::for_each<TagsList>([&count, &variables](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    auto& tensor = get<tag>(*variables);\n    for (auto& tensor_component : tensor) {\n      tensor_component = count;\n      count += 1.0;\n    }\n  });\n}\n\n// Note: clang-8 wants us to capture Dim in the closures if we do `constexpr\n// size_t Dim =...`, but then GCC-7 fails to build. Assigning `Dim` as a\n// template parameter gets around that.\ntemplate <typename System, size_t Dim = System::volume_dim>\nvoid test_1d(const bool moving_mesh, const dg::Formulation formulation,\n             const Spectral::Quadrature quadrature) {\n  CAPTURE(moving_mesh);\n  CAPTURE(formulation);\n  CAPTURE(quadrature);\n  CAPTURE(System::has_primitive_and_conservative_vars);\n  CAPTURE(System::system_type);\n  // gcc-8 complains that there are no definitions for the destructors of None\n  // and Periodic. We can generate them via explicit instantiations or by just\n  // creating an empty dummy object.\n  [[maybe_unused]] const domain::BoundaryConditions::None<\n      BoundaryCondition<System>>\n      instantiate_none_for_gcc_8{};\n  [[maybe_unused]] const domain::BoundaryConditions::Periodic<\n      BoundaryCondition<System>>\n      instantiate_periodic_for_gcc_8{};\n  static_assert(System::volume_dim == 1);\n\n  using dt_variables_tag =\n      db::add_tag_prefix<::Tags::dt, typename System::variables_tag>;\n  const Mesh<Dim> mesh{5, Spectral::Basis::Legendre, quadrature};\n  const ElementId<Dim> self_id{0, {{{1, 0}}}};\n  const Element<Dim> element{self_id, {}};\n  ElementMap<Dim, Frame::Grid> element_map{\n      self_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<Dim>{})};\n  auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{});\n  const double time{1.2};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>> mesh_velocity{};\n  if (moving_mesh) {\n    const std::array<double, 3> velocities = {{1.2, -1.4, 0.3}};\n    mesh_velocity =\n        tnsr::I<DataVector, Dim, Frame::Inertial>{mesh.number_of_grid_points()};\n    for (size_t i = 0; i < Dim; ++i) {\n      mesh_velocity->get(i) = gsl::at(velocities, i);\n    }\n  }\n  // Set the Jacobian to not be the identity because otherwise bugs creep in\n  // easily.\n  ::InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inv_jacobian{mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < Dim; ++i) {\n    inv_jacobian.get(i, i) = 2.0;\n  }\n  const auto det_inv_jacobian = determinant(inv_jacobian);\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      normal_covector_and_magnitude{};\n  normal_covector_and_magnitude[Direction<Dim>::lower_xi()] = std::nullopt;\n  normal_covector_and_magnitude[Direction<Dim>::upper_xi()] = std::nullopt;\n  const double boundary_condition_volume_tag_number{2.5};\n  const double boundary_correction_volume_tag_number{3.5};\n\n  Variables<\n      db::wrap_tags_in<::Tags::dt, typename System::variables_tag::tags_list>>\n      dt_evolved_vars{mesh.number_of_grid_points()};\n  fill_variables(make_not_null(&dt_evolved_vars), offset_dt_evolved_vars);\n  Variables<typename System::variables_tag::tags_list> evolved_vars{\n      mesh.number_of_grid_points()};\n  fill_variables(make_not_null(&evolved_vars), offset_evolved_vars);\n  Variables<\n      typename System::compute_volume_time_derivative_terms::temporary_tags>\n      temporaries{mesh.number_of_grid_points()};\n  fill_variables(make_not_null(&temporaries), offset_temporaries);\n  Variables<db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,\n                             tmpl::size_t<Dim>, Frame::Inertial>>\n      volume_fluxes{mesh.number_of_grid_points()};\n  fill_variables(make_not_null(&volume_fluxes), offset_volume_fluxes);\n  Variables<db::wrap_tags_in<::Tags::deriv, typename System::gradient_variables,\n                             tmpl::size_t<Dim>, Frame::Inertial>>\n      partial_derivs{mesh.number_of_grid_points()};\n  fill_variables(make_not_null(&partial_derivs), offset_partial_derivs);\n  Variables<tmpl::conditional_t<\n      System::has_primitive_and_conservative_vars,\n      typename System::primitive_variables_tag::tags_list, tmpl::list<>>>\n      primitive_vars{mesh.number_of_grid_points()};\n  fill_variables(make_not_null(&primitive_vars), offset_primitive_vars);\n  const Variables<tmpl::conditional_t<\n      System::has_primitive_and_conservative_vars,\n      typename System::primitive_variables_tag::tags_list, tmpl::list<>>>*\n      primitive_vars_ptr =\n          System::has_primitive_and_conservative_vars ? &primitive_vars\n                                                      : nullptr;\n  constexpr bool has_prims = System::has_primitive_and_conservative_vars;\n  using BndryTerms = BoundaryTerms<Dim, has_prims, System::system_type,\n                                   System::has_inverse_spatial_metric>;\n\n  Domain<Dim> domain{};\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      external_boundary_conditions{1};\n  {\n    // For the initial tests, set the boundary conditions to:\n    // lower_xi: DemandOutgoingCharSpeeds\n    // upper_xi: DemandOutgoingCharSpeeds\n    external_boundary_conditions[0][Direction<Dim>::lower_xi()] =\n        std::make_unique<DemandOutgoingCharSpeeds<System>>(moving_mesh);\n    external_boundary_conditions[0][Direction<Dim>::upper_xi()] =\n        std::make_unique<DemandOutgoingCharSpeeds<System>>(moving_mesh);\n    domain = Domain<Dim>{make_vector(Block<Dim>{\n        domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<Dim>{}),\n        0,\n        {}})};\n    domain.inject_time_dependent_map_for_block(\n        0, grid_to_inertial_map->get_clone());\n  }\n\n  using simple_tags = tmpl::list<\n      Parallel::Tags::MetavariablesImpl<Metavariables<System>>,\n      domain::Tags::Domain<Dim>, domain::Tags::ExternalBoundaryConditions<Dim>,\n      domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n      domain::Tags::ElementMap<Dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                  Frame::Inertial>,\n      ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::MeshVelocity<Dim>,\n      domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                    Frame::Inertial>,\n      domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>,\n      typename System::variables_tag, dt_variables_tag,\n      Tags::BoundaryConditionVolumeTag, Tags::BoundaryCorrectionVolumeTag,\n      ::dg::Tags::Formulation>;\n  using compute_tags = tmpl::list<>;\n\n  auto box = db::create<simple_tags, compute_tags>(\n      Metavariables<System>{}, std::move(domain),\n      std::move(external_boundary_conditions), mesh, element,\n      std::move(element_map), grid_to_inertial_map->get_clone(), time,\n      clone_unique_ptrs(functions_of_time), mesh_velocity, inv_jacobian,\n      det_inv_jacobian, normal_covector_and_magnitude, evolved_vars,\n      dt_evolved_vars, boundary_condition_volume_tag_number,\n      boundary_correction_volume_tag_number, formulation);\n\n  {\n    INFO(\"DemandOutgoingCharSpeeds only\");\n    // DemandOutgoingCharSpeeds both sides, dt in volume shouldn't change.\n    evolution::dg::Actions::detail::\n        apply_boundary_conditions_on_all_external_faces<System, Dim>(\n            make_not_null(&box), BndryTerms{moving_mesh, 0.0}, temporaries,\n            volume_fluxes, partial_derivs, primitive_vars_ptr);\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::dt<Tags::Var1>>(box)),\n        DataVector(mesh.number_of_grid_points(), offset_dt_evolved_vars));\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_APPROX(get<::Tags::dt<Tags::Var2<Dim>>>(box).get(i),\n                            DataVector(mesh.number_of_grid_points(),\n                                       offset_dt_evolved_vars + 1 + i));\n    }\n  }\n\n  const auto expected_ghost_dt_correction = [&box, &formulation, &mesh](\n                                                const auto& ghost_direction) {\n    Variables<tmpl::list<::Tags::dt<Tags::Var1>, ::Tags::dt<Tags::Var2<Dim>>>>\n        expected_on_boundary{mesh.slice_away(ghost_direction.dimension())\n                                 .number_of_grid_points()};\n    get(get<::Tags::dt<Tags::Var1>>(expected_on_boundary)) =\n        offset_boundary_correction *\n        (formulation == dg::Formulation::WeakInertial ? 2.0 : 1.0);\n    for (size_t i = 0; i < Dim; ++i) {\n      get<::Tags::dt<Tags::Var2<Dim>>>(expected_on_boundary).get(i) =\n          offset_boundary_correction + 1.0 + i;\n    }\n    // lift into volume and add to volume time derivative\n    Variables<tmpl::list<::Tags::dt<Tags::Var1>, ::Tags::dt<Tags::Var2<Dim>>>>\n        expected_dt_volume_correction{mesh.number_of_grid_points(), 0.0};\n    const Scalar<DataVector>& volume_det_inv_jacobian = db::get<\n        domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>>(\n        box);\n    const Scalar<DataVector>& magnitude_of_interior_face_normal =\n        get<evolution::dg::Tags::MagnitudeOfNormal>(\n            *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(box)\n                 .at(ghost_direction));\n    if (mesh.quadrature(ghost_direction.dimension()) ==\n        Spectral::Quadrature::Gauss) {\n      Scalar<DataVector> face_det_inv_jacobian{\n          mesh.slice_away(ghost_direction.dimension()).number_of_grid_points()};\n      const Matrix identity{};\n      auto interpolation_matrices = make_array<Dim>(std::cref(identity));\n      const std::pair<Matrix, Matrix>& matrices =\n          Spectral::boundary_interpolation_matrices(\n              mesh.slice_through(ghost_direction.dimension()));\n      gsl::at(interpolation_matrices, ghost_direction.dimension()) =\n          ghost_direction.side() == Side::Upper ? matrices.second\n                                                : matrices.first;\n      apply_matrices(make_not_null(&get(face_det_inv_jacobian)),\n                     interpolation_matrices, get(volume_det_inv_jacobian),\n                     mesh.extents());\n      Scalar<DataVector> face_det_jacobian{1.0 / get(face_det_inv_jacobian)};\n\n      ::dg::lift_boundary_terms_gauss_points(\n          make_not_null(&expected_dt_volume_correction),\n          volume_det_inv_jacobian, mesh, ghost_direction, expected_on_boundary,\n          magnitude_of_interior_face_normal, face_det_jacobian);\n    } else {\n      ::dg::lift_flux(make_not_null(&expected_on_boundary),\n                      mesh.extents(ghost_direction.dimension()),\n                      magnitude_of_interior_face_normal,\n                      mesh.basis(ghost_direction.dimension()));\n      add_slice_to_data(make_not_null(&expected_dt_volume_correction),\n                        expected_on_boundary, mesh.extents(),\n                        ghost_direction.dimension(),\n                        index_to_slice_at(mesh.extents(), ghost_direction));\n    }\n    return expected_dt_volume_correction;\n  };\n\n  const auto check_outgoing_and_ghost = [&box, &dt_evolved_vars,\n                                         &expected_ghost_dt_correction,\n                                         &moving_mesh, &partial_derivs,\n                                         &primitive_vars_ptr, &temporaries,\n                                         &volume_fluxes](\n                                            const Direction<Dim>&\n                                                outgoing_direction) {\n    INFO(\"Ghost\");\n    CAPTURE(outgoing_direction);\n    db::mutate<domain::Tags::ExternalBoundaryConditions<Dim>, dt_variables_tag>(\n        [&moving_mesh, &outgoing_direction](const auto all_boundary_conditions,\n                                            const auto dt_vars_ptr) {\n          DirectionMap<Dim, std::unique_ptr<\n                                domain::BoundaryConditions::BoundaryCondition>>\n              boundary_conditions{};\n          boundary_conditions[outgoing_direction.opposite()] =\n              std::make_unique<Ghost<System>>(moving_mesh);\n          boundary_conditions[outgoing_direction] =\n              std::make_unique<DemandOutgoingCharSpeeds<System>>(moving_mesh);\n          (*all_boundary_conditions)[0] = std::move(boundary_conditions);\n\n          fill_variables(dt_vars_ptr, offset_dt_evolved_vars);\n        },\n        make_not_null(&box));\n    evolution::dg::Actions::detail::\n        apply_boundary_conditions_on_all_external_faces<System, Dim>(\n            make_not_null(&box),\n            BndryTerms{moving_mesh, outgoing_direction.opposite().sign()},\n            temporaries, volume_fluxes, partial_derivs, primitive_vars_ptr);\n\n    auto expected_dt_evolved_vars = dt_evolved_vars;\n    expected_dt_evolved_vars +=\n        expected_ghost_dt_correction(outgoing_direction.opposite());\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::dt<Tags::Var1>>(box)),\n        get(get<::Tags::dt<Tags::Var1>>(expected_dt_evolved_vars)));\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_APPROX(\n          get<::Tags::dt<Tags::Var2<Dim>>>(box).get(i),\n          get<::Tags::dt<Tags::Var2<Dim>>>(expected_dt_evolved_vars).get(i));\n    }\n  };\n  // DemandOutgoingCharSpeeds +xi, Ghost -xi\n  check_outgoing_and_ghost(Direction<Dim>::upper_xi());\n  // Ghost +xi, DemandOutgoingCharSpeeds -xi\n  check_outgoing_and_ghost(Direction<Dim>::lower_xi());\n\n  const auto expected_time_derivative_dt_correction = [&mesh](\n                                                          const auto&\n                                                              dt_direction) {\n    Variables<tmpl::list<::Tags::dt<Tags::Var1>, ::Tags::dt<Tags::Var2<Dim>>>>\n        expected_dt_volume_correction{mesh.number_of_grid_points(), 0.0};\n    const Mesh<Dim> mesh_gl{5, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n    Variables<\n        db::wrap_tags_in<::Tags::dt, typename System::variables_tag::tags_list>>\n        dt_correction_gl{mesh_gl.number_of_grid_points(), 0.0};\n    const size_t boundary_index =\n        dt_direction.side() == Side::Lower\n            ? 0\n            : mesh_gl.extents(dt_direction.dimension()) - 1;\n    get(get<::Tags::dt<Tags::Var1>>(dt_correction_gl))[boundary_index] +=\n        offset_boundary_condition;\n    for (size_t i = 0; i < Dim; ++i) {\n      get<::Tags::dt<Tags::Var2<Dim>>>(dt_correction_gl)\n          .get(i)[boundary_index] += offset_boundary_condition + 1.0 + i;\n    }\n    if (mesh.quadrature(dt_direction.dimension()) ==\n        Spectral::Quadrature::GaussLobatto) {\n      expected_dt_volume_correction += dt_correction_gl;\n    } else {\n      // Interpolate to Gauss mesh\n      expected_dt_volume_correction +=\n          intrp::RegularGrid<Dim>{mesh_gl, mesh}.interpolate(dt_correction_gl);\n    }\n    return expected_dt_volume_correction;\n  };\n\n  const auto check_outgoing_and_dt = [&box, &dt_evolved_vars,\n                                      &expected_time_derivative_dt_correction,\n                                      &moving_mesh, &partial_derivs,\n                                      &primitive_vars_ptr, &temporaries,\n                                      &volume_fluxes](const Direction<Dim>&\n                                                          outgoing_direction) {\n    INFO(\"TimeDerivative\");\n    CAPTURE(outgoing_direction);\n    db::mutate<domain::Tags::ExternalBoundaryConditions<Dim>, dt_variables_tag>(\n        [&moving_mesh, &outgoing_direction](const auto all_boundary_conditions,\n                                            const auto dt_vars_ptr) {\n          DirectionMap<Dim, std::unique_ptr<\n                                domain::BoundaryConditions::BoundaryCondition>>\n              boundary_conditions{};\n          boundary_conditions[outgoing_direction.opposite()] =\n              std::make_unique<TimeDerivative<System>>(moving_mesh,\n                                                       offset_dt_evolved_vars);\n          boundary_conditions[outgoing_direction] =\n              std::make_unique<DemandOutgoingCharSpeeds<System>>(moving_mesh);\n          (*all_boundary_conditions)[0] = std::move(boundary_conditions);\n\n          fill_variables(dt_vars_ptr, offset_dt_evolved_vars);\n        },\n        make_not_null(&box));\n    evolution::dg::Actions::detail::\n        apply_boundary_conditions_on_all_external_faces<System, Dim>(\n            make_not_null(&box),\n            BndryTerms{moving_mesh, outgoing_direction.opposite().sign()},\n            temporaries, volume_fluxes, partial_derivs, primitive_vars_ptr);\n\n    auto expected_dt_evolved_vars = dt_evolved_vars;\n    expected_dt_evolved_vars +=\n        expected_time_derivative_dt_correction(outgoing_direction.opposite());\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::dt<Tags::Var1>>(box)),\n        get(get<::Tags::dt<Tags::Var1>>(expected_dt_evolved_vars)));\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_APPROX(\n          get<::Tags::dt<Tags::Var2<Dim>>>(box).get(i),\n          get<::Tags::dt<Tags::Var2<Dim>>>(expected_dt_evolved_vars).get(i));\n    }\n  };\n  // DemandOutgoingCharSpeeds +xi, TimeDerivative -xi\n  check_outgoing_and_dt(Direction<Dim>::upper_xi());\n  // DemandOutgoingCharSpeeds -xi, TimeDerivative +xi\n  check_outgoing_and_dt(Direction<Dim>::lower_xi());\n\n  const auto check_ghost_and_dt_opposite =\n      [&box, &dt_evolved_vars, &expected_ghost_dt_correction,\n       &expected_time_derivative_dt_correction, &mesh, &moving_mesh,\n       &partial_derivs, &primitive_vars_ptr, &temporaries,\n       &volume_fluxes](const Direction<Dim>& ghost_direction) {\n        INFO(\"Ghost and TimeDerivative on opposite sides\");\n        CAPTURE(ghost_direction);\n        auto expected_dt_evolved_vars = dt_evolved_vars;\n        expected_dt_evolved_vars +=\n            expected_ghost_dt_correction(ghost_direction);\n\n        // Project to the boundary to figure out what will be the projected\n        // dt_var1 passed into the time derivative boundary condition. This is\n        // necessary because we apply _and lift_ the Ghost boundary correction\n        // before the TimeDerivative correction. This order is determined in the\n        // BoundaryCondition base class's `creatable_classes` typelist.\n        Variables<\n            tmpl::list<::Tags::dt<Tags::Var1>, ::Tags::dt<Tags::Var2<Dim>>>>\n            expected_dt_on_boundary{mesh.slice_away(ghost_direction.dimension())\n                                        .number_of_grid_points()};\n        ::dg::project_contiguous_data_to_boundary(\n            make_not_null(&expected_dt_on_boundary), expected_dt_evolved_vars,\n            mesh, ghost_direction.opposite());\n\n        db::mutate<domain::Tags::ExternalBoundaryConditions<Dim>,\n                   dt_variables_tag>(\n            [&expected_dt_var1 =\n                 get<::Tags::dt<Tags::Var1>>(expected_dt_on_boundary),\n             &moving_mesh, &ghost_direction](const auto all_boundary_conditions,\n                                             const auto dt_vars_ptr) {\n              DirectionMap<Dim,\n                           std::unique_ptr<\n                               domain::BoundaryConditions::BoundaryCondition>>\n                  boundary_conditions{};\n              boundary_conditions[ghost_direction.opposite()] =\n                  std::make_unique<TimeDerivative<System>>(\n                      moving_mesh, get(expected_dt_var1)[0]);\n              boundary_conditions[ghost_direction] =\n                  std::make_unique<Ghost<System>>(moving_mesh);\n              (*all_boundary_conditions)[0] = std::move(boundary_conditions);\n\n              fill_variables(dt_vars_ptr, offset_dt_evolved_vars);\n            },\n            make_not_null(&box));\n        evolution::dg::Actions::detail::\n            apply_boundary_conditions_on_all_external_faces<System, Dim>(\n                make_not_null(&box),\n                BndryTerms{moving_mesh, ghost_direction.sign()}, temporaries,\n                volume_fluxes, partial_derivs, primitive_vars_ptr);\n\n        expected_dt_evolved_vars +=\n            expected_time_derivative_dt_correction(ghost_direction.opposite());\n\n        CHECK_ITERABLE_APPROX(\n            get(get<::Tags::dt<Tags::Var1>>(box)),\n            get(get<::Tags::dt<Tags::Var1>>(expected_dt_evolved_vars)));\n        for (size_t i = 0; i < Dim; ++i) {\n          CHECK_ITERABLE_APPROX(\n              get<::Tags::dt<Tags::Var2<Dim>>>(box).get(i),\n              get<::Tags::dt<Tags::Var2<Dim>>>(expected_dt_evolved_vars)\n                  .get(i));\n        }\n      };\n  // Ghost +xi, TimeDerivative -xi\n  check_ghost_and_dt_opposite(Direction<Dim>::upper_xi());\n  // Ghost -xi, TimeDerivative +xi\n  check_ghost_and_dt_opposite(Direction<Dim>::lower_xi());\n\n  const auto check_ghost_and_dt_combined_bc =\n      [&box, &dt_evolved_vars, &expected_ghost_dt_correction,\n       &expected_time_derivative_dt_correction, &moving_mesh, &partial_derivs,\n       &primitive_vars_ptr, &temporaries,\n       &volume_fluxes](const Direction<Dim>& outgoing_direction) {\n        INFO(\"GhostAndTimeDerivative combined on one side\");\n        CAPTURE(outgoing_direction);\n        // Since the Ghost and TimeDerivative are applied in the same direction\n        // they both receive the dt_vars _without_ either boundary condition\n        // applied, which is different from way Ghost and TimeDerivative are\n        // applied in different directions.\n        db::mutate<domain::Tags::ExternalBoundaryConditions<Dim>,\n                   dt_variables_tag>(\n            [&moving_mesh, &outgoing_direction](\n                const auto all_boundary_conditions, const auto dt_vars_ptr) {\n              DirectionMap<Dim,\n                           std::unique_ptr<\n                               domain::BoundaryConditions::BoundaryCondition>>\n                  boundary_conditions{};\n              boundary_conditions[outgoing_direction.opposite()] =\n                  std::make_unique<GhostAndTimeDerivative<System>>(moving_mesh);\n              boundary_conditions[outgoing_direction] =\n                  std::make_unique<DemandOutgoingCharSpeeds<System>>(\n                      moving_mesh);\n              (*all_boundary_conditions)[0] = std::move(boundary_conditions);\n\n              fill_variables(dt_vars_ptr, offset_dt_evolved_vars);\n            },\n            make_not_null(&box));\n        evolution::dg::Actions::detail::\n            apply_boundary_conditions_on_all_external_faces<System, Dim>(\n                make_not_null(&box),\n                BndryTerms{moving_mesh, outgoing_direction.opposite().sign()},\n                temporaries, volume_fluxes, partial_derivs, primitive_vars_ptr);\n\n        auto expected_dt_evolved_vars = dt_evolved_vars;\n        expected_dt_evolved_vars += expected_time_derivative_dt_correction(\n            outgoing_direction.opposite());\n        expected_dt_evolved_vars +=\n            expected_ghost_dt_correction(outgoing_direction.opposite());\n\n        CHECK_ITERABLE_APPROX(\n            get(get<::Tags::dt<Tags::Var1>>(box)),\n            get(get<::Tags::dt<Tags::Var1>>(expected_dt_evolved_vars)));\n        for (size_t i = 0; i < Dim; ++i) {\n          CHECK_ITERABLE_APPROX(\n              get<::Tags::dt<Tags::Var2<Dim>>>(box).get(i),\n              get<::Tags::dt<Tags::Var2<Dim>>>(expected_dt_evolved_vars)\n                  .get(i));\n        }\n      };\n  // DemandOutgoingCharSpeeds +xi, GhostAndTimeDerivative -xi\n  check_ghost_and_dt_combined_bc(Direction<Dim>::upper_xi());\n  // DemandOutgoingCharSpeeds -xi, GhostAndTimeDerivative +xi\n  check_ghost_and_dt_combined_bc(Direction<Dim>::lower_xi());\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.ComputeTimeDerivative.BoundaryConditions\",\n                  \"[Unit][Evolution][Actions]\") {\n  // The test proceeds as follows:\n  //\n  // 1. prepare all the data in the DataBox\n  // 2. call the boundary condition function, which should apply the boundary\n  //    condition. We do so switching around the direction the boundary\n  //    condition is applied in, checking different ones on each side.\n  // 3. inside the boundary conditions we check we have received the expected\n  //    values of the different tags\n  // 4. we return pre-determined numbers so that we can check the time\n  //    derivatives changed in the expected way given the numbers.\n  //\n  // Notes:\n  // - the test is currently only in 1d, but most (if not all) places that need\n  //   generalization have a `static_assert(Dim == 1)`. Going to more dimensions\n  //   is straightforward but _extremely_ tedious.\n  for (const bool moving_mesh : {true, false}) {\n    for (const dg::Formulation formulation :\n         {dg::Formulation::WeakInertial, dg::Formulation::StrongInertial}) {\n      for (const Spectral::Quadrature quadrature :\n           {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}) {\n        // Second last template parameter on System:\n        // - true: has primitive variables\n        // - false: no primitive variables\n\n        // last template parameter on System being `false` means flat background\n        test_1d<System<1, SystemType::Conservative, false, false>>(\n            moving_mesh, formulation, quadrature);\n        test_1d<System<1, SystemType::Conservative, true, false>>(\n            moving_mesh, formulation, quadrature);\n\n        test_1d<System<1, SystemType::Nonconservative, false, false>>(\n            moving_mesh, formulation, quadrature);\n\n        test_1d<System<1, SystemType::Mixed, false, false>>(\n            moving_mesh, formulation, quadrature);\n        test_1d<System<1, SystemType::Mixed, true, false>>(\n            moving_mesh, formulation, quadrature);\n\n        // last template parameter on System being `true` means curved\n        // background\n        test_1d<System<1, SystemType::Conservative, false, true>>(\n            moving_mesh, formulation, quadrature);\n        test_1d<System<1, SystemType::Conservative, true, true>>(\n            moving_mesh, formulation, quadrature);\n\n        test_1d<System<1, SystemType::Nonconservative, false, true>>(\n            moving_mesh, formulation, quadrature);\n\n        test_1d<System<1, SystemType::Mixed, false, true>>(\n            moving_mesh, formulation, quadrature);\n        test_1d<System<1, SystemType::Mixed, true, true>>(\n            moving_mesh, formulation, quadrature);\n      }\n    }\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Actions/Test_ComputeTimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <charm++.h>\n#include <optional>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\nnamespace {\nnamespace WithInverseSpatialMetricTag {\ntemplate <size_t Dim>\nstruct InverseSpatialMetric : db::SimpleTag {\n  using type = tnsr::II<DataVector, Dim, Frame::Inertial>;\n};\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  using flux_variables = tmpl::list<Var1>;\n  using inverse_spatial_metric_tag = InverseSpatialMetric<Dim>;\n};\n\ntemplate <size_t Dim>\nstruct BoundaryTerms {\n  using dg_package_field_tags = tmpl::list<Var1>;\n\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n\n      const Scalar<DataVector>& var1,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    *out_normal_dot_flux_var1 = dot_product(normal_covector, flux_var1);\n    *out_var1 = var1;\n\n    const DataVector normal_magnitude =\n        sqrt(get(dot_product(normal_covector, normal_vector)));\n    CHECK_ITERABLE_APPROX(DataVector(normal_magnitude.size(), 1.0),\n                          normal_magnitude);\n\n    if (normal_dot_mesh_velocity.has_value()) {\n      return max(1.0 - get(*normal_dot_mesh_velocity));\n    }\n    return 1.0;\n  }\n};\n\ntemplate <size_t Dim>\nvoid test(const bool use_moving_mesh) {\n  constexpr size_t number_of_grid_points = 5;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> dist{0.0, 1.0};\n\n  Variables<tmpl::list<::Tags::NormalDotFlux<Var1>, Var1>> packaged_data{\n      number_of_grid_points};\n  BoundaryTerms<Dim> boundary_correction{};\n  Variables<\n      tmpl::list<Var1, ::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>,\n                 InverseSpatialMetric<Dim>,\n                 ::evolution::dg::Actions::detail::NormalVector<Dim>>>\n      projected_fields{number_of_grid_points};\n  fill_with_random_values(make_not_null(&projected_fields), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  tnsr::i<DataVector, Dim, Frame::Inertial> unit_normal_covector{\n      number_of_grid_points};\n  fill_with_random_values(make_not_null(&unit_normal_covector),\n                          make_not_null(&gen), make_not_null(&dist));\n  auto& normal_vector =\n      get<::evolution::dg::Actions::detail::NormalVector<Dim>>(\n          projected_fields);\n  for (size_t i = 0; i < Dim; ++i) {\n    normal_vector.get(i) = 0.0;\n    for (size_t j = 0; j < Dim; ++j) {\n      normal_vector.get(i) +=\n          unit_normal_covector.get(j) *\n          get<InverseSpatialMetric<Dim>>(projected_fields).get(i, j);\n    }\n  }\n  const DataVector magnitude_of_normal =\n      sqrt(get(dot_product(normal_vector, unit_normal_covector)));\n  for (size_t i = 0; i < Dim; ++i) {\n    normal_vector.get(i) /= magnitude_of_normal;\n    unit_normal_covector.get(i) /= magnitude_of_normal;\n  }\n\n  std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>> mesh_velocity;\n  std::optional<Scalar<DataVector>> normal_dot_mesh_velocity;\n  if (use_moving_mesh) {\n    mesh_velocity =\n        make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n            make_not_null(&gen), make_not_null(&dist), magnitude_of_normal);\n    normal_dot_mesh_velocity =\n        dot_product(unit_normal_covector, *mesh_velocity);\n  }\n\n  db::DataBox<tmpl::list<>> box{};\n  const double max_speed =\n      ::evolution::dg::Actions::detail::dg_package_data<System<Dim>>(\n          make_not_null(&packaged_data), boundary_correction, projected_fields,\n          unit_normal_covector, mesh_velocity, box, tmpl::list<>{},\n          tmpl::list<Var1,\n                     ::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>{});\n\n  Variables<tmpl::list<::Tags::NormalDotFlux<Var1>, Var1>>\n      expected_packaged_data{number_of_grid_points};\n  const double expected_max_speed = boundary_correction.dg_package_data(\n      make_not_null(&get<::Tags::NormalDotFlux<Var1>>(expected_packaged_data)),\n      make_not_null(&get<Var1>(expected_packaged_data)),\n      get<Var1>(projected_fields),\n      get<::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>(\n          projected_fields),\n      unit_normal_covector,\n      get<::evolution::dg::Actions::detail::NormalVector<Dim>>(\n          projected_fields),\n      mesh_velocity, normal_dot_mesh_velocity);\n\n  CHECK(max_speed == approx(expected_max_speed));\n  CHECK_ITERABLE_APPROX(\n      get<::Tags::NormalDotFlux<Var1>>(packaged_data),\n      get<::Tags::NormalDotFlux<Var1>>(expected_packaged_data));\n  CHECK_ITERABLE_APPROX(get<Var1>(packaged_data),\n                        get<Var1>(expected_packaged_data));\n}\n}  // namespace WithInverseSpatialMetricTag\n\ntemplate <SystemType system_type, bool UsePrims>\nvoid test_wrapper() {\n  test<system_type, UsePrims, 1>();\n  test<system_type, UsePrims, 2>();\n  test<system_type, UsePrims, 3>();\n}\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.ComputeTimeDerivative\",\n                  \"[Unit][Evolution][Actions]\") {\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<1>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<2>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<3>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<1>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<2>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       domain::CoordinateMaps::Identity<3>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                       domain::CoordinateMaps::Identity<1>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                       domain::CoordinateMaps::Identity<2>>));\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                                       domain::CoordinateMaps::Identity<3>>));\n\n  // The test is designed to test the `ComputeTimeDerivative` action for DG.\n  // This action does a lot:\n  //\n  // - compute partial derivatives as needed\n  // - compute the time derivative from\n  //   `System::compute_volume_time_derivative`. This includes fluxes, sources,\n  //   and nonconservative products.\n  // - adds moving mesh terms as needed.\n  // - compute flux divergence and add to the time derivative.\n  // - compute mortar data for internal boundaries.\n  //\n  // The action supports conservative systems, nonconservative systems, and\n  // mixed conservative-nonconservative systems.\n  //\n  // To test the action thoroughly we need to test a lot of different\n  // combinations:\n  //\n  // - system type (conservative/nonconservative/mixed) with the enum SystemType\n  // - 1d, 2d, 3d\n  // - whether the mesh is moving or not\n  //\n  // Note that because the test is quite expensive to build, we have split the\n  // compilation across multiple translation units by having the test be defined\n  // in ComputeTimeDerivativeImpl.tpp.\n  test_wrapper<SystemType::Conservative, false>();\n  test_wrapper<SystemType::Conservative, true>();\n  test_wrapper<SystemType::Nonconservative, false>();\n  test_wrapper<SystemType::Mixed, false>();\n  test_wrapper<SystemType::Mixed, true>();\n\n  for (const bool use_moving_mesh : {true, false}) {\n    WithInverseSpatialMetricTag::test<1>(use_moving_mesh);\n  }\n}\n}  // namespace\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Actions/Test_NormalCovectorAndMagnitude.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceComputeTags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t VolumeDim>\nauto make_affine_map();\n\ntemplate <>\nauto make_affine_map<1>() {\n  return domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n      Affine{-1.0, 1.0, -0.3, 0.7});\n}\n\ntemplate <>\nauto make_affine_map<2>() {\n  return domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n      Affine2D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55}});\n}\n\ntemplate <>\nauto make_affine_map<3>() {\n  return domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n      Affine3D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n               Affine{-1.0, 1.0, 2.3, 2.8}});\n}\n\nstruct ScaleZeroIndex : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <size_t Dim>\nstruct SimpleUnnormalizedFaceNormal\n    : db::ComputeTag,\n      domain::Tags::UnnormalizedFaceNormal<Dim> {\n  using base = domain::Tags::UnnormalizedFaceNormal<Dim>;\n  using return_type = typename base::type;\n  static void function(const gsl::not_null<return_type*> result,\n                       const double scale_zero_index,\n                       const Mesh<Dim - 1>& face_mesh,\n                       const Direction<Dim>& direction) {\n    for (size_t i = 0; i < Dim; ++i) {\n      result->get(i) =\n          DataVector{face_mesh.number_of_grid_points(),\n                     i == direction.dimension() ? 1.0 * i + 0.25 : 0.0};\n    }\n    result->get(0) *= scale_zero_index;\n  }\n  using argument_tags = tmpl::list<ScaleZeroIndex, domain::Tags::Mesh<Dim - 1>,\n                                   domain::Tags::Direction<Dim>>;\n  using volume_tags = tmpl::list<ScaleZeroIndex>;\n};\n\nstruct FlatSpaceSystem {};\n\ntemplate <size_t Dim>\nstruct InverseSpatialMetric : db::SimpleTag {\n  using type = tnsr::II<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nstruct SystemWithInverseMetric {\n  using inverse_spatial_metric_tag = InverseSpatialMetric<Dim>;\n};\n\ntemplate <size_t Dim>\nauto create_box(const size_t number_of_grid_points_per_dim,\n                const bool use_moving_mesh) {\n  using internal_directions = domain::Tags::InternalDirections<Dim>;\n  using simple_tags =\n      tmpl::list<ScaleZeroIndex, domain::Tags::Mesh<Dim>,\n                 domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                             Frame::Inertial>,\n                 domain::Tags::Element<Dim>,\n                 evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>;\n  using compute_tags =\n      tmpl::list<domain::Tags::InternalDirectionsCompute<Dim>,\n                 domain::Tags::InterfaceCompute<internal_directions,\n                                                domain::Tags::Direction<Dim>>,\n                 domain::Tags::InterfaceCompute<\n                     internal_directions, domain::Tags::InterfaceMesh<Dim>>,\n                 domain::Tags::InterfaceCompute<\n                     internal_directions, SimpleUnnormalizedFaceNormal<Dim>>>;\n\n  auto grid_to_inertial_map =\n      use_moving_mesh\n          ? make_affine_map<Dim>()\n          : domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n                domain::CoordinateMaps::Identity<Dim>{});\n\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  ElementId<Dim> self_id{};\n  ElementId<Dim> east_id{};\n  ElementId<Dim> south_id{};  // not used in 1d\n  ElementId<Dim> up_id{};  // not used in 1d or 2d\n\n  if constexpr (Dim == 1) {\n    self_id = ElementId<Dim>{0, {{{1, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n  } else if constexpr (Dim == 2) {\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{1, 0}, {0, 0}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, OrientationMap<Dim>::create_aligned()};\n  } else {\n    static_assert(Dim == 3, \"Only implemented tests in 1, 2, and 3d\");\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {2, 1}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}, {2, 1}}}};\n    south_id = ElementId<Dim>{1, {{{1, 0}, {0, 0}, {2, 1}}}};\n    up_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {2, 2}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::upper_zeta()] =\n        Neighbors<Dim>{{up_id}, OrientationMap<Dim>::create_aligned()};\n  }\n  const Element<Dim> element{self_id, neighbors};\n\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      normal_covector_quantities{};\n  for (const auto& [direction, local_neighbors] : element.neighbors()) {\n    (void)local_neighbors;\n    normal_covector_quantities[direction] = std::nullopt;\n  }\n\n  return db::create<simple_tags, compute_tags>(\n      1.0,\n      Mesh<Dim>{number_of_grid_points_per_dim, Spectral::Basis::Legendre,\n                Spectral::Quadrature::Gauss},\n      std::move(grid_to_inertial_map), std::move(element),\n      std::move(normal_covector_quantities));\n}\n\ntemplate <bool UseFlatSpace, size_t Dim, typename DbTagsList>\nvoid check_normal_covector_quantities(\n    const gsl::not_null<db::DataBox<DbTagsList>*> box) {\n  using field_face_tags = tmpl::conditional_t<\n      UseFlatSpace,\n      tmpl::list<evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>,\n      tmpl::list<evolution::dg::Actions::detail::OneOverNormalVectorMagnitude,\n                 evolution::dg::Actions::detail::NormalVector<Dim>,\n                 InverseSpatialMetric<Dim>>>;\n  using internal_directions = domain::Tags::InternalDirections<Dim>;\n  const auto& unnormalized_normal_covectors = db::get<domain::Tags::Interface<\n      internal_directions, domain::Tags::UnnormalizedFaceNormal<Dim>>>(*box);\n  for (const auto& [direction, neighbors] :\n       db::get<domain::Tags::Element<Dim>>(*box).neighbors()) {\n    (void)neighbors;\n    Variables<field_face_tags> fields_on_face{\n        db::get<domain::Tags::Interface<internal_directions,\n                                        domain::Tags::Mesh<Dim - 1>>>(*box)\n            .at(direction)\n            .number_of_grid_points()};\n    if constexpr (not UseFlatSpace) {\n      double temp = 0.1;\n      for (auto& component : get<InverseSpatialMetric<Dim>>(fields_on_face)) {\n        for (double& t : component) {\n          t = temp;\n          temp *= 1.1;\n        }\n      }\n    }\n\n    db::mutate<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(\n        &evolution::dg::Actions::detail::\n            unit_normal_vector_and_covector_and_magnitude<\n                tmpl::conditional_t<UseFlatSpace, FlatSpaceSystem,\n                                    SystemWithInverseMetric<Dim>>,\n                Dim, field_face_tags>,\n        box, make_not_null(&fields_on_face), direction,\n        unnormalized_normal_covectors,\n        db::get<domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                            Frame::Inertial>>(\n            *box));\n\n    if constexpr (UseFlatSpace) {\n      const auto& normal_covector =\n          get<evolution::dg::Tags::NormalCovector<Dim>>(\n              *get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box)\n                   .at(direction));\n      const DataVector expected_normal_magnitude{\n          fields_on_face.number_of_grid_points(), 1.0};\n      CHECK_ITERABLE_APPROX(expected_normal_magnitude,\n                            get(magnitude(normal_covector)));\n    } else {\n      const auto& normal_covector =\n          get<evolution::dg::Tags::NormalCovector<Dim>>(\n              *get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box)\n                   .at(direction));\n      CHECK_ITERABLE_APPROX(\n          (DataVector{fields_on_face.number_of_grid_points(), 1.0}),\n          sqrt(get(\n              dot_product(normal_covector, normal_covector,\n                          get<InverseSpatialMetric<Dim>>(fields_on_face)))));\n    }\n\n    const auto& magnitude_of_normal =\n        get(get<evolution::dg::Tags::MagnitudeOfNormal>(\n            *get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box).at(\n                direction)));\n    CHECK(min(magnitude_of_normal) > 0.0);\n\n    for (size_t i = 0; i < Dim; ++i) {\n      const auto& normal_covector_and_magnitude =\n          *get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(*box).at(\n              direction);\n      const auto& unnormalized_covector_in_dir =\n          unnormalized_normal_covectors.at(direction).get(i);\n      const DataVector expected_unnormalized_covector_in_dir{\n          get<evolution::dg::Tags::NormalCovector<Dim>>(\n              normal_covector_and_magnitude)\n              .get(i) *\n          get(get<evolution::dg::Tags::MagnitudeOfNormal>(\n              normal_covector_and_magnitude))};\n      CHECK_ITERABLE_APPROX(unnormalized_covector_in_dir,\n                            expected_unnormalized_covector_in_dir);\n    }\n  }\n}\n\ntemplate <size_t Dim, bool IsFlatSpace>\nvoid test(const bool use_moving_mesh) {\n  CAPTURE(Dim);\n  CAPTURE(use_moving_mesh);\n  CAPTURE(IsFlatSpace);\n  const size_t number_of_grid_points_per_dim = 3;\n  auto box = create_box<Dim>(number_of_grid_points_per_dim, use_moving_mesh);\n  check_normal_covector_quantities<IsFlatSpace, Dim>(make_not_null(&box));\n\n  if (use_moving_mesh) {\n    // Mutate the x component of the unnormalized normal vector to simulate\n    // moving mesh\n    db::mutate<ScaleZeroIndex>(\n        [](const gsl::not_null<double*> scale_zero_index) {\n          *scale_zero_index = 2.3;\n        },\n        make_not_null(&box));\n\n    check_normal_covector_quantities<IsFlatSpace, Dim>(make_not_null(&box));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.NormalCovectorAndMagnitude\",\n                  \"[Unit][Evolution][Actions]\") {\n  for (const auto use_moving_mesh : {true, false}) {\n    test<1, true>(use_moving_mesh);\n    test<2, true>(use_moving_mesh);\n    test<3, true>(use_moving_mesh);\n\n    test<1, false>(use_moving_mesh);\n    test<2, false>(use_moving_mesh);\n    test<3, false>(use_moving_mesh);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/BoundaryCorrectionsHelper.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    var1,\n    var2,\n    flux_var1,\n    flux_var2,\n    normal_covector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n    volume_double,\n):\n    if not isinstance(volume_double, float):\n        volume_double = volume_double[0]\n    if normal_dot_mesh_velocity is None:\n        abs_char_speed = np.abs(volume_double * var1)\n    else:\n        abs_char_speed = np.abs(volume_double * var1 - normal_dot_mesh_velocity)\n    return (\n        var1,\n        np.asarray(np.einsum(\"i,i->\", flux_var1, normal_covector)),\n        var2,\n        np.asarray(np.einsum(\"ij,j->i\", flux_var2, normal_covector)),\n        np.asarray(abs_char_speed),\n    )\n\n\ndef dg_package_data_curved(\n    var1,\n    var2,\n    flux_var1,\n    flux_var2,\n    normal_covector,\n    normal_vector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n    volume_double,\n):\n    if not isinstance(volume_double, float):\n        volume_double = volume_double[0]\n    if normal_dot_mesh_velocity is None:\n        abs_char_speed = np.abs(volume_double * var1)\n    else:\n        abs_char_speed = np.abs(volume_double * var1 - normal_dot_mesh_velocity)\n    return (\n        var1,\n        np.asarray(np.einsum(\"i,i->\", flux_var1, normal_covector)),\n        var2,\n        np.asarray(\n            np.einsum(\"ij,j->i\", flux_var2, normal_covector) + normal_vector\n        ),\n        np.asarray(abs_char_speed),\n    )\n\n\ndef dg_boundary_terms(\n    var1_int,\n    normal_dot_flux_var1_int,\n    var2_int,\n    normal_dot_flux_var2_int,\n    abs_char_speed_int,\n    var1_ext,\n    normal_dot_flux_var1_ext,\n    var2_ext,\n    normal_dot_flux_var2_ext,\n    abs_char_speed_ext,\n    use_strong_form,\n):\n    if use_strong_form:\n        return (\n            np.asarray(\n                -0.5 * (normal_dot_flux_var1_int + normal_dot_flux_var1_ext)\n                - 0.5\n                * np.maximum(abs_char_speed_int, abs_char_speed_ext)\n                * (var1_ext - var1_int)\n            ),\n            np.asarray(\n                -0.5 * (normal_dot_flux_var2_int + normal_dot_flux_var2_ext)\n                - 0.5\n                * np.maximum(abs_char_speed_int, abs_char_speed_ext)\n                * (var2_ext - var2_int)\n            ),\n        )\n    else:\n        return (\n            np.asarray(\n                0.5 * (normal_dot_flux_var1_int - normal_dot_flux_var1_ext)\n                - 0.5\n                * np.maximum(abs_char_speed_int, abs_char_speed_ext)\n                * (var1_ext - var1_int)\n            ),\n            np.asarray(\n                0.5 * (normal_dot_flux_var2_int - normal_dot_flux_var2_ext)\n                - 0.5\n                * np.maximum(abs_char_speed_int, abs_char_speed_ext)\n                * (var2_ext - var2_int)\n            ),\n        )\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Limiters)\nadd_subdirectory(Messages)\n\nset(LIBRARY \"Test_EvolutionDg\")\n\nset(LIBRARY_SOURCES\n  Actions/Test_ApplyBoundaryCorrections.cpp\n  Actions/Test_BoundaryConditions.cpp\n  Actions/Test_ComputeTimeDerivative.cpp\n  Actions/Test_NormalCovectorAndMagnitude.cpp\n  Initialization/Test_Mortars.cpp\n  Initialization/Test_QuadratureTag.cpp\n  Test_AtomicInboxBoundaryData.cpp\n  Test_BackgroundGrVars.cpp\n  Test_BoundaryCorrectionsHelper.cpp\n  Test_BoundaryData.cpp\n  Test_CleanMortarHistory.cpp\n  Test_InboxTags.cpp\n  Test_InterfaceDataPolicy.cpp\n  Test_InterpolatedBoundaryData.cpp\n  Test_MortarData.cpp\n  Test_MortarDataHolder.cpp\n  Test_MortarInfo.cpp\n  Test_MortarTags.cpp\n  Test_NormalVectorTags.cpp\n  Test_TimeSteppingPolicy.cpp\n  Test_UsingSubcell.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Amr\n  Boost::boost\n  CoordinateMaps\n  DataStructures\n  DataStructuresHelpers\n  DiscontinuousGalerkin\n  Domain\n  DomainCreators\n  DomainStructure\n  DomainTimeDependence\n  Evolution\n  EvolutionDgActionsHelpers\n  GeneralRelativitySolutions\n  Hydro\n  Options\n  RelativisticEulerSolutions\n  Spectral\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Initialization/Test_Mortars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/NonconformingSphericalShells.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InterfaceDataPolicy.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg {\nnamespace {\ntemplate <typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Metavariables::volume_dim>;\n\n  using internal_directions =\n      domain::Tags::InternalDirections<Metavariables::volume_dim>;\n  using boundary_directions_interior =\n      domain::Tags::BoundaryDirectionsInterior<Metavariables::volume_dim>;\n\n  using simple_tags =\n      tmpl::list<::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n                 domain::Tags::Element<Metavariables::volume_dim>,\n                 domain::Tags::Mesh<Metavariables::volume_dim>,\n                 domain::Tags::NeighborMesh<Metavariables::volume_dim>>;\n  using compute_tags = tmpl::list<>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<evolution::dg::Initialization::Mortars<\n                                 Metavariables::volume_dim>>>>;\n};\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim, bool LocalTimeStepping>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  static constexpr bool local_time_stepping = LocalTimeStepping;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<Dim>>;\n  struct system {\n    using variables_tag = ::Tags::Variables<tmpl::list<Var1, Var2<Dim>>>;\n  };\n\n  using component_list = tmpl::list<component<Metavariables>>;\n};\n\ntemplate <size_t Dim>\nusing mortar_data_history_type = typename Tags::MortarDataHistory<Dim>::type;\n\ntemplate <bool LocalTimeStepping, size_t Dim>\nvoid test_impl(\n    const std::vector<std::array<size_t, Dim>>& initial_extents,\n    const Element<Dim>& element, const TimeStepId& time_step_id,\n    const TimeStepId& next_time_step_id, const Spectral::Quadrature quadrature,\n    const DirectionalIdMap<Dim, Mesh<Dim>>& neighbor_mesh,\n    const ::dg::MortarMap<Dim, Mesh<Dim - 1>>& expected_mortar_meshes,\n    const ::dg::MortarMap<Dim, MortarInfo<Dim>>& expected_mortar_infos,\n    const DirectionMap<Dim, std::optional<Variables<tmpl::list<\n                                evolution::dg::Tags::MagnitudeOfNormal,\n                                evolution::dg::Tags::NormalCovector<Dim>>>>>&\n        expected_normal_covector_quantities,\n    std::optional<Domain<Dim>> domain = std::nullopt) {\n  using metavars = Metavariables<Dim, LocalTimeStepping>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  if (domain == std::nullopt) {\n    std::vector<Block<Dim>> blocks{};\n    blocks.reserve(initial_extents.size());\n    for (size_t block_id = 0; block_id < initial_extents.size(); ++block_id) {\n      blocks.emplace_back(nullptr, block_id,\n                          DirectionMap<Dim, BlockNeighbors<Dim>>{});\n    }\n    domain = Domain<Dim>{std::move(blocks)};\n  }\n  tuples::TaggedTuple<domain::Tags::Domain<Dim>> opts{\n      std::move(domain.value())};\n  MockRuntimeSystem runner{std::move(opts)};\n  ActionTesting::emplace_component_and_initialize<component<metavars>>(\n      &runner, element.id(),\n      {time_step_id, next_time_step_id, element,\n       domain::create_initial_mesh(initial_extents, element,\n                                   Spectral::Basis::Legendre, quadrature),\n       neighbor_mesh});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Run the Mortars initialization action\n  ActionTesting::next_action<component<metavars>>(make_not_null(&runner),\n                                                  element.id());\n\n  const auto get_tag = [&runner, &element](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<component<metavars>, tag>(\n        runner, element.id());\n  };\n\n  const auto& mortar_meshes = get_tag(Tags::MortarMesh<Dim>{});\n  CHECK(mortar_meshes == expected_mortar_meshes);\n  const auto& mortar_infos = get_tag(Tags::MortarInfo<Dim>{});\n  CHECK(mortar_infos == expected_mortar_infos);\n  const auto& mortar_data = get_tag(Tags::MortarData<Dim>{});\n  const auto& boundary_data_history = get_tag(Tags::MortarDataHistory<Dim>{});\n  for (const auto& mortar_id_and_mesh : expected_mortar_meshes) {\n    // Just make sure this exists, it is not expected to hold any data\n    CHECK(mortar_data.find(mortar_id_and_mesh.first) != mortar_data.end());\n    if (LocalTimeStepping) {\n      CHECK(boundary_data_history.find(mortar_id_and_mesh.first) !=\n            boundary_data_history.end());\n    }\n  }\n\n  const auto& mortar_next_temporal_ids =\n      get_tag(Tags::MortarNextTemporalId<Dim>{});\n  for (const auto& mortar_id_and_mesh : expected_mortar_meshes) {\n    const auto& mortar_id = mortar_id_and_mesh.first;\n    if (mortar_id.id() != ElementId<Dim>::external_boundary_id()) {\n      CHECK(mortar_next_temporal_ids.at(mortar_id) == next_time_step_id);\n    }\n  }\n\n  CHECK(get_tag(evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>{}) ==\n        expected_normal_covector_quantities);\n}\n\ntemplate <size_t Dim, bool LocalTimeStepping>\nstruct Test;\n\ntemplate <bool LocalTimeStepping>\nstruct Test<1, LocalTimeStepping> {\n  static void apply(const Spectral::Quadrature quadrature) {\n    INFO(\"1D\");\n    // Reference element is denoted by X, has one internal boundary and one\n    // external boundary:\n    //\n    // [X| | | ]-> xi\n\n    const ElementId<1> element_id{0, {{{2, 0}}}};\n    const ElementId<1> east_id{0, {{{2, 1}}}};\n    const std::vector initial_extents{make_array<1>(2_st)};\n\n    // We are working with 2 mortars here: a domain boundary at lower xi\n    // and an interface at upper xi.\n    const DirectionalId<1> interface_mortar_id{Direction<1>::upper_xi(),\n                                               east_id};\n\n    DirectionMap<1, Neighbors<1>> neighbors{};\n    DirectionalIdMap<1, Mesh<1>> neighbor_meshes{};\n    neighbors[Direction<1>::upper_xi()] =\n        Neighbors<1>{{east_id}, OrientationMap<1>::create_aligned()};\n    neighbor_meshes[interface_mortar_id] =\n        Mesh<1>{initial_extents[0], Spectral::Basis::Legendre, quadrature};\n    const Element<1> element{element_id, neighbors};\n    const TimeStepId time_step_id{true, 3, Time{Slab{0.2, 3.4}, {3, 100}}};\n    const TimeStepId next_time_step_id{true, 3, Time{Slab{0.2, 3.4}, {6, 100}}};\n\n    const ::dg::MortarMap<1, Mesh<0>> expected_mortar_meshes{\n        {interface_mortar_id, {}}};\n    const ::dg::MortarMap<1, MortarInfo<1>> expected_mortar_infos{\n        {interface_mortar_id,\n         MortarInfo<1>{\n             {.interface_data_policy =\n                  evolution::dg::InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy =\n                  LocalTimeStepping\n                      ? evolution::dg::TimeSteppingPolicy::Conservative\n                      : evolution::dg::TimeSteppingPolicy::EqualRate}}}};\n\n    const DirectionMap<\n        1, std::optional<\n               Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                    evolution::dg::Tags::NormalCovector<1>>>>>\n        expected_normal_covector_quantities{{Direction<1>::lower_xi(), {}},\n                                            {Direction<1>::upper_xi(), {}}};\n\n    test_impl<LocalTimeStepping>(initial_extents, element, time_step_id,\n                                 next_time_step_id, quadrature, neighbor_meshes,\n                                 expected_mortar_meshes, expected_mortar_infos,\n                                 expected_normal_covector_quantities);\n  }\n};\n\ntemplate <bool LocalTimeStepping>\nstruct Test<2, LocalTimeStepping> {\n  static void apply(const Spectral::Quadrature quadrature) {\n    INFO(\"2D\");\n    // Reference element is denoted by X, has two internal boundaries (east and\n    // south) and two external boundaries (west and north):\n    //\n    // ^ eta\n    // +-+-+> xi\n    // |X| |\n    // +-+-+\n    // | | |\n    // +-+-+\n\n    const ElementId<2> element_id{0, {{{1, 0}, {1, 1}}}};\n    const ElementId<2> east_id(0, {{SegmentId{1, 1}, SegmentId{1, 1}}});\n    const ElementId<2> south_id(0, {{SegmentId{1, 0}, SegmentId{1, 0}}});\n    const std::vector initial_extents{std::array{3_st, 2_st}};\n\n    // We are working with 4 mortars here: the domain boundary west and north,\n    // and interfaces south and east.\n    const DirectionalId<2> interface_mortar_id_east{Direction<2>::upper_xi(),\n                                                    east_id};\n    const DirectionalId<2> interface_mortar_id_south{Direction<2>::lower_eta(),\n                                                     south_id};\n\n    DirectionMap<2, Neighbors<2>> neighbors{};\n    neighbors[Direction<2>::upper_xi()] =\n        Neighbors<2>{{east_id}, OrientationMap<2>::create_aligned()};\n    neighbors[Direction<2>::lower_eta()] =\n        Neighbors<2>{{south_id}, OrientationMap<2>::create_aligned()};\n    DirectionalIdMap<2, Mesh<2>> neighbor_meshes{};\n    neighbor_meshes[interface_mortar_id_east] =\n        Mesh<2>{initial_extents[0], Spectral::Basis::Legendre, quadrature};\n    neighbor_meshes[interface_mortar_id_south] =\n        Mesh<2>{initial_extents[0], Spectral::Basis::Legendre, quadrature};\n\n    const Element<2> element{element_id, neighbors};\n    const TimeStepId time_step_id{true, 3, Time{Slab{0.2, 3.4}, {3, 100}}};\n    const TimeStepId next_time_step_id{true, 3, Time{Slab{0.2, 3.4}, {6, 100}}};\n\n    const ::dg::MortarMap<2, Mesh<1>> expected_mortar_meshes{\n        {interface_mortar_id_east,\n         Mesh<1>(2, Spectral::Basis::Legendre, quadrature)},\n        {interface_mortar_id_south,\n         Mesh<1>(3, Spectral::Basis::Legendre, quadrature)}};\n    ::dg::MortarMap<2, MortarInfo<2>> expected_mortar_infos{};\n    for (const auto& mortar_id_and_mesh : expected_mortar_meshes) {\n      expected_mortar_infos.emplace(\n          mortar_id_and_mesh.first,\n          MortarInfo<2>{\n              {.mortar_size = {{Spectral::SegmentSize::Full}},\n               .interface_data_policy =\n                   evolution::dg::InterfaceDataPolicy::CopyProject,\n               .time_stepping_policy =\n                   LocalTimeStepping\n                       ? evolution::dg::TimeSteppingPolicy::Conservative\n                       : evolution::dg::TimeSteppingPolicy::EqualRate}});\n    }\n\n    const DirectionMap<\n        2, std::optional<\n               Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                    evolution::dg::Tags::NormalCovector<2>>>>>\n        expected_normal_covector_quantities{{Direction<2>::lower_xi(), {}},\n                                            {Direction<2>::upper_xi(), {}},\n                                            {Direction<2>::lower_eta(), {}},\n                                            {Direction<2>::upper_eta(), {}}};\n\n    test_impl<LocalTimeStepping>(initial_extents, element, time_step_id,\n                                 next_time_step_id, quadrature, neighbor_meshes,\n                                 expected_mortar_meshes, expected_mortar_infos,\n                                 expected_normal_covector_quantities);\n  }\n};\n\ntemplate <bool LocalTimeStepping>\nstruct Test<3, LocalTimeStepping> {\n  static void apply(const Spectral::Quadrature quadrature) {\n    INFO(\"3D\");\n    // Neighboring elements in:\n    // - upper-xi (right id)\n    // - lower-eta (front id)\n    // - upper-zeta (top id)\n    //\n    // All other directions don't have neighbors.\n\n    const ElementId<3> element_id{\n        0, {{SegmentId{1, 1}, SegmentId{1, 1}, SegmentId{1, 0}}}};\n    const ElementId<3> right_id(\n        1, {{SegmentId{2, 1}, SegmentId{1, 0}, SegmentId{1, 1}}});\n    const ElementId<3> front_id(\n        0, {{SegmentId{1, 1}, SegmentId{1, 0}, SegmentId{1, 0}}});\n    const ElementId<3> top_id(\n        0, {{SegmentId{1, 1}, SegmentId{1, 1}, SegmentId{1, 1}}});\n    const std::vector initial_extents{std::array{2_st, 3_st, 4_st},\n                                      std::array{5_st, 6_st, 7_st}};\n\n    const DirectionalId<3> interface_mortar_id_right{Direction<3>::upper_xi(),\n                                                     right_id};\n    const DirectionalId<3> interface_mortar_id_front{Direction<3>::lower_eta(),\n                                                     front_id};\n    const DirectionalId<3> interface_mortar_id_top{Direction<3>::upper_zeta(),\n                                                   top_id};\n\n    DirectionMap<3, Neighbors<3>> neighbors{};\n    neighbors[Direction<3>::upper_xi()] =\n        Neighbors<3>{{right_id},\n                     OrientationMap<3>{{Direction<3>::upper_eta(),\n                                        Direction<3>::upper_zeta(),\n                                        Direction<3>::upper_xi()}}};\n    neighbors[Direction<3>::lower_eta()] =\n        Neighbors<3>{{front_id}, OrientationMap<3>::create_aligned()};\n    neighbors[Direction<3>::upper_zeta()] =\n        Neighbors<3>{{top_id}, OrientationMap<3>::create_aligned()};\n    DirectionalIdMap<3, Mesh<3>> neighbor_meshes{};\n    neighbor_meshes[interface_mortar_id_right] =\n        Mesh<3>{{{6_st, 7_st, 5_st}}, Spectral::Basis::Legendre, quadrature};\n    neighbor_meshes[interface_mortar_id_front] =\n        Mesh<3>{initial_extents[0], Spectral::Basis::Legendre, quadrature};\n    neighbor_meshes[interface_mortar_id_top] =\n        Mesh<3>{initial_extents[0], Spectral::Basis::Legendre, quadrature};\n\n    const Element<3> element{element_id, neighbors};\n    const TimeStepId time_step_id{true, 3, Time{Slab{0.2, 3.4}, {3, 100}}};\n    const TimeStepId next_time_step_id{true, 3, Time{Slab{0.2, 3.4}, {6, 100}}};\n\n    const ::dg::MortarMap<3, Mesh<2>> expected_mortar_meshes{\n        {interface_mortar_id_right,\n         Mesh<2>({{7, 5}}, Spectral::Basis::Legendre, quadrature)},\n        {interface_mortar_id_front,\n         Mesh<2>({{2, 4}}, Spectral::Basis::Legendre, quadrature)},\n        {interface_mortar_id_top,\n         Mesh<2>({{2, 3}}, Spectral::Basis::Legendre, quadrature)}};\n    const auto expected_time_stepping_policy =\n        LocalTimeStepping ? evolution::dg::TimeSteppingPolicy::Conservative\n                          : evolution::dg::TimeSteppingPolicy::EqualRate;\n    ::dg::MortarMap<3, MortarInfo<3>> expected_mortar_infos{};\n    expected_mortar_infos.emplace(\n        interface_mortar_id_right,\n        MortarInfo<3>{\n            {.mortar_size = {{Spectral::SegmentSize::Full,\n                              Spectral::SegmentSize::UpperHalf}},\n             .interface_data_policy =\n                 evolution::dg::InterfaceDataPolicy::OrientCopyProject,\n             .time_stepping_policy = expected_time_stepping_policy}});\n    expected_mortar_infos.emplace(\n        interface_mortar_id_front,\n        MortarInfo<3>{{.mortar_size = {{Spectral::SegmentSize::Full,\n                                        Spectral::SegmentSize::Full}},\n                       .interface_data_policy =\n                           evolution::dg::InterfaceDataPolicy::CopyProject,\n                       .time_stepping_policy = expected_time_stepping_policy}});\n    expected_mortar_infos.emplace(\n        interface_mortar_id_top,\n        MortarInfo<3>{{.mortar_size = {{Spectral::SegmentSize::Full,\n                                        Spectral::SegmentSize::Full}},\n                       .interface_data_policy =\n                           evolution::dg::InterfaceDataPolicy::CopyProject,\n                       .time_stepping_policy = expected_time_stepping_policy}});\n\n    const DirectionMap<\n        3, std::optional<\n               Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                    evolution::dg::Tags::NormalCovector<3>>>>>\n        expected_normal_covector_quantities{\n            {Direction<3>::lower_xi(), {}},   {Direction<3>::upper_xi(), {}},\n            {Direction<3>::lower_eta(), {}},  {Direction<3>::upper_eta(), {}},\n            {Direction<3>::lower_zeta(), {}}, {Direction<3>::upper_zeta(), {}}};\n\n    test_impl<LocalTimeStepping>(initial_extents, element, time_step_id,\n                                 next_time_step_id, quadrature, neighbor_meshes,\n                                 expected_mortar_meshes, expected_mortar_infos,\n                                 expected_normal_covector_quantities);\n  }\n};\n\ntemplate <bool LocalTimeStepping>\nvoid test_nonconforming_blocks() {\n  INFO(\"NonconformingSphericalShells\");\n  const auto creator = domain::creators::NonconformingSphericalShells(\n      2.0, 3.0, 4.0, 0, 0, 5, 7, 11, nullptr, nullptr);\n  auto domain = creator.create_domain();\n  const auto initial_refinement = creator.initial_refinement_levels();\n  const auto initial_extents = creator.initial_extents();\n  const ElementId<3> shell_id{6};\n  const Element<3> shell = domain::create_initial_element(\n      shell_id, domain.blocks(), initial_refinement);\n  const Mesh<3> shell_mesh = domain::create_initial_mesh(\n      initial_extents, shell, Spectral::Basis::Legendre,\n      Spectral::Quadrature::GaussLobatto);\n  const Mesh<2> shell_mortar_mesh = shell_mesh.slice_away(0_st);\n  const TimeStepId time_step_id{true, 3, Time{Slab{0.2, 3.4}, {3, 100}}};\n  const TimeStepId next_time_step_id{true, 3, Time{Slab{0.2, 3.4}, {6, 100}}};\n  const auto expected_time_stepping_policy =\n      LocalTimeStepping ? evolution::dg::TimeSteppingPolicy::Conservative\n                        : evolution::dg::TimeSteppingPolicy::EqualRate;\n  {\n    INFO(\"Test S2 shell\");\n    const auto& shell_neighbor_ids =\n        shell.neighbors().at(Direction<3>::lower_xi());\n    DirectionalIdMap<3, Mesh<3>> shell_neighbor_meshes{};\n    for (const auto id : shell_neighbor_ids) {\n      const DirectionalId<3> shell_neighbor_directional_id{\n          Direction<3>::lower_xi(), id};\n      const Element<3> neighbor_element = domain::create_initial_element(\n          id, domain.blocks(), initial_refinement);\n      const Mesh<3> neighbor_mesh = domain::create_initial_mesh(\n          initial_extents, neighbor_element, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto);\n      shell_neighbor_meshes[shell_neighbor_directional_id] = neighbor_mesh;\n    }\n    const DirectionalId<3> shell_mortar_id{Direction<3>::lower_xi(), shell_id};\n\n    ::dg::MortarMap<3, Mesh<2>> shell_expected_mortar_meshes{};\n    shell_expected_mortar_meshes[shell_mortar_id] = shell_mortar_mesh;\n    ::dg::MortarMap<3, MortarInfo<3>> shell_expected_mortar_infos{};\n    shell_expected_mortar_infos.emplace(\n        shell_mortar_id,\n        MortarInfo<3>{{.interface_data_policy =\n                           evolution::dg::InterfaceDataPolicy::\n                               NonconformingNeighborInterpolates,\n                       .time_stepping_policy = expected_time_stepping_policy}});\n    const DirectionMap<\n        3, std::optional<\n               Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                    evolution::dg::Tags::NormalCovector<3>>>>>\n        shell_expected_normal_covector_quantities{\n            {Direction<3>::lower_xi(), {}}, {Direction<3>::upper_xi(), {}}};\n    test_impl<LocalTimeStepping>(\n        initial_extents, shell, time_step_id, next_time_step_id,\n        Spectral::Quadrature::GaussLobatto, shell_neighbor_meshes,\n        shell_expected_mortar_meshes, shell_expected_mortar_infos,\n        shell_expected_normal_covector_quantities,\n        std::make_optional<Domain<3>>(std::move(domain)));\n  }\n  {\n    INFO(\"Test cubed sphere\");\n    domain = creator.create_domain();\n    const ElementId<3> element_id{2};\n    const Element<3> element = domain::create_initial_element(\n        element_id, domain.blocks(), initial_refinement);\n    const Mesh<3> volume_mesh = domain::create_initial_mesh(\n        initial_extents, element, Spectral::Basis::Legendre,\n        Spectral::Quadrature::GaussLobatto);\n    DirectionalIdMap<3, Mesh<3>> neighbor_meshes{};\n    ::dg::MortarMap<3, Mesh<2>> expected_mortar_meshes{};\n    ::dg::MortarMap<3, MortarInfo<3>> expected_mortar_infos{};\n    for (const auto& [direction, neighbors] : element.neighbors()) {\n      for (const auto& neighbor : neighbors) {\n        const DirectionalId<3> mortar_id{direction, neighbor};\n        const auto& neighbor_block = domain.blocks()[neighbor.block_id()];\n        const Element<3> neighbor_element = domain::create_initial_element(\n            neighbor, domain.blocks(), initial_refinement);\n        if (neighbors.are_conforming()) {\n          const auto& neighbor_orientation = neighbors.orientation(neighbor);\n          neighbor_meshes.emplace(\n              mortar_id,\n              neighbor_orientation.inverse_map()(::domain::create_initial_mesh(\n                  initial_extents, neighbor_block, neighbor,\n                  Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto)));\n        } else {\n          neighbor_meshes.emplace(mortar_id,\n                                  ::domain::create_initial_mesh(\n                                      initial_extents, neighbor_block, neighbor,\n                                      Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::GaussLobatto));\n        }\n        const Mesh<2> face_mesh = volume_mesh.slice_away(direction.dimension());\n        expected_mortar_meshes[mortar_id] = face_mesh;\n        const auto& neighbor_orientation = neighbors.orientation(neighbor);\n        if (direction == Direction<3>::upper_zeta()) {\n          expected_mortar_infos.emplace(\n              mortar_id,\n              MortarInfo<3>{\n                  {.interpolator =\n                       ::dg::MortarInterpolator<3>{element_id, mortar_id,\n                                                   domain, face_mesh,\n                                                   shell_mortar_mesh},\n                   .interface_data_policy = evolution::dg::InterfaceDataPolicy::\n                       NonconformingSelfInterpolates,\n                   .time_stepping_policy = expected_time_stepping_policy}});\n        } else {\n          expected_mortar_infos.emplace(\n              mortar_id,\n              MortarInfo<3>{\n                  {.mortar_size = {{Spectral::SegmentSize::Full,\n                                    Spectral::SegmentSize::Full}},\n                   .interface_data_policy =\n                       neighbor_orientation.is_aligned()\n                           ? InterfaceDataPolicy::CopyProject\n                           : InterfaceDataPolicy::OrientCopyProject,\n                   .time_stepping_policy = expected_time_stepping_policy}});\n        }\n      }\n    }\n    const DirectionMap<\n        3, std::optional<\n               Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                    evolution::dg::Tags::NormalCovector<3>>>>>\n        expected_normal_covector_quantities{\n            {Direction<3>::lower_xi(), {}},   {Direction<3>::upper_xi(), {}},\n            {Direction<3>::lower_eta(), {}},  {Direction<3>::upper_eta(), {}},\n            {Direction<3>::lower_zeta(), {}}, {Direction<3>::upper_zeta(), {}}};\n    test_impl<LocalTimeStepping>(\n        initial_extents, element, time_step_id, next_time_step_id,\n        Spectral::Quadrature::GaussLobatto, neighbor_meshes,\n        expected_mortar_meshes, expected_mortar_infos,\n        expected_normal_covector_quantities,\n        std::make_optional<Domain<3>>(std::move(domain)));\n  }\n}\n\ntemplate <size_t Dim>\nvoid check_mortar_data(const MortarData<Dim>& projected,\n                       const MortarData<Dim>& expected) {\n  CHECK(projected.mortar_mesh == expected.mortar_mesh);\n  CHECK(projected.face_mesh == expected.face_mesh);\n  CHECK(projected.volume_mesh == expected.volume_mesh);\n  if (projected.mortar_data.has_value()) {\n    CHECK_ITERABLE_APPROX(projected.mortar_data.value(),\n                          expected.mortar_data.value());\n  } else {\n    CHECK_FALSE(expected.mortar_data.has_value());\n  }\n  if (projected.face_normal_magnitude.has_value()) {\n    CHECK_ITERABLE_APPROX(projected.face_normal_magnitude.value(),\n                          expected.face_normal_magnitude.value());\n  } else {\n    CHECK_FALSE(expected.face_normal_magnitude.has_value());\n  }\n  if (projected.face_det_jacobian.has_value()) {\n    CHECK_ITERABLE_APPROX(projected.face_det_jacobian.value(),\n                          expected.face_det_jacobian.value());\n  } else {\n    CHECK_FALSE(expected.face_det_jacobian.has_value());\n  }\n  if (projected.volume_det_inv_jacobian.has_value()) {\n    CHECK_ITERABLE_APPROX(projected.volume_det_inv_jacobian.value(),\n                          expected.volume_det_inv_jacobian.value());\n  } else {\n    CHECK_FALSE(expected.volume_det_inv_jacobian.has_value());\n  }\n}\n\ntemplate <size_t Dim>\nvoid check_boundary_histories(\n    const ::dg::MortarMap<\n        Dim, TimeSteppers::BoundaryHistory<::evolution::dg::MortarData<Dim>,\n                                           ::evolution::dg::MortarData<Dim>,\n                                           DataVector>>& value,\n    const ::dg::MortarMap<\n        Dim, TimeSteppers::BoundaryHistory<::evolution::dg::MortarData<Dim>,\n                                           ::evolution::dg::MortarData<Dim>,\n                                           DataVector>>& expected) {\n  using HistMap = std::decay_t<decltype(value)>;\n  const auto compare_entries = [](const HistMap& a, const HistMap& b) {\n    for (const auto& [mortar, history_a] : a) {\n      CAPTURE(mortar);\n      const auto it = b.find(mortar);\n      REQUIRE(it != b.end());\n      const auto& history_b = it->second;\n      const auto local_a = history_a.local();\n      const auto local_b = history_b.local();\n      local_a.for_each([&](const TimeStepId& id,\n                           const ::evolution::dg::MortarData<Dim>& data) {\n        CHECK(local_a.integration_order(id) == local_b.integration_order(id));\n        check_mortar_data(data, local_b.data(id));\n      });\n      const auto remote_a = history_a.remote();\n      const auto remote_b = history_b.remote();\n      remote_a.for_each([&](const TimeStepId& id,\n                            const ::evolution::dg::MortarData<Dim>& data) {\n        CHECK(remote_a.integration_order(id) == remote_b.integration_order(id));\n        check_mortar_data(data, remote_b.data(id));\n      });\n    }\n  };\n  compare_entries(value, expected);\n  compare_entries(expected, value);\n}\n\ntemplate <size_t Dim, bool UsingLts>\nvoid test_p_refine(\n    ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>> mortar_data,\n    ::dg::MortarMap<Dim, Mesh<Dim - 1>> mortar_mesh,\n    ::dg::MortarMap<Dim, MortarInfo<Dim>> mortar_infos,\n    ::dg::MortarMap<Dim, TimeStepId> mortar_next_temporal_id,\n    DirectionMap<Dim, std::optional<Variables<tmpl::list<\n                          evolution::dg::Tags::MagnitudeOfNormal,\n                          evolution::dg::Tags::NormalCovector<Dim>>>>>\n        normal_covector_and_magnitude,\n    mortar_data_history_type<Dim> mortar_data_history,\n    const Mesh<Dim>& old_mesh, Mesh<Dim> new_mesh,\n    const Element<Dim>& old_element, Element<Dim> new_element,\n    ::dg::MortarMap<Dim, Mesh<Dim>> neighbor_meshes,\n    const TimeStepId& temporal_id,\n    const ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>&\n        expected_mortar_data,\n    const ::dg::MortarMap<Dim, Mesh<Dim - 1>>& expected_mortar_mesh,\n    const ::dg::MortarMap<Dim, MortarInfo<Dim>>& expected_mortar_infos,\n    const ::dg::MortarMap<Dim, TimeStepId>& expected_mortar_next_temporal_id,\n    const DirectionMap<Dim, std::optional<Variables<tmpl::list<\n                                evolution::dg::Tags::MagnitudeOfNormal,\n                                evolution::dg::Tags::NormalCovector<Dim>>>>>&\n        expected_normal_covector_and_magnitude,\n    const mortar_data_history_type<Dim>& expected_mortar_data_history) {\n  auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Domain<Dim>, domain::Tags::Mesh<Dim>,\n      domain::Tags::Element<Dim>, domain::Tags::NeighborMesh<Dim>,\n      ::Tags::TimeStepId, Tags::MortarData<Dim>, Tags::MortarMesh<Dim>,\n      Tags::MortarInfo<Dim>, Tags::MortarNextTemporalId<Dim>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>,\n      Tags::MortarDataHistory<Dim>>>(\n      Domain<Dim>{}, std::move(new_mesh), std::move(new_element),\n      std::move(neighbor_meshes), temporal_id, std::move(mortar_data),\n      std::move(mortar_mesh), std::move(mortar_infos),\n      std::move(mortar_next_temporal_id),\n      std::move(normal_covector_and_magnitude), std::move(mortar_data_history));\n\n  db::mutate_apply<\n      evolution::dg::Initialization::ProjectMortars<Dim, UsingLts>>(\n      make_not_null(&box), std::make_pair(old_mesh, old_element));\n\n  CHECK(db::get<Tags::MortarData<Dim>>(box) == expected_mortar_data);\n  CHECK(db::get<Tags::MortarMesh<Dim>>(box) == expected_mortar_mesh);\n  CHECK(db::get<Tags::MortarInfo<Dim>>(box) == expected_mortar_infos);\n  CHECK(db::get<Tags::MortarNextTemporalId<Dim>>(box) ==\n        expected_mortar_next_temporal_id);\n  CHECK(db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(box) ==\n        expected_normal_covector_and_magnitude);\n  check_boundary_histories(db::get<Tags::MortarDataHistory<Dim>>(box),\n                           expected_mortar_data_history);\n}\n\ntemplate <size_t Dim>\nElement<Dim> make_element();\n\ntemplate <>\nElement<1> make_element<1>() {\n  const ElementId<1> element_id{0, {{SegmentId{2, 0}}}};\n  const ElementId<1> neighbor_id{0, {{SegmentId{2, 1}}}};\n  DirectionMap<1, Neighbors<1>> neighbors{};\n  neighbors[Direction<1>::upper_xi()] =\n      Neighbors<1>{{neighbor_id}, OrientationMap<1>::create_aligned()};\n  return Element<1>{element_id, neighbors};\n}\n\ntemplate <>\nElement<2> make_element<2>() {\n  const ElementId<2> element_id{0, {{SegmentId{1, 0}, SegmentId{1, 1}}}};\n  const ElementId<2> east_id(0, {{SegmentId{1, 1}, SegmentId{1, 1}}});\n  const ElementId<2> south_id(0, {{SegmentId{1, 0}, SegmentId{1, 0}}});\n  DirectionMap<2, Neighbors<2>> neighbors{};\n  neighbors[Direction<2>::upper_xi()] =\n      Neighbors<2>{{east_id}, OrientationMap<2>::create_aligned()};\n  neighbors[Direction<2>::lower_eta()] =\n      Neighbors<2>{{south_id}, OrientationMap<2>::create_aligned()};\n  return Element<2>{element_id, neighbors};\n}\n\ntemplate <>\nElement<3> make_element<3>() {\n  const ElementId<3> element_id{\n      0, {{SegmentId{1, 0}, SegmentId{1, 1}, SegmentId{1, 0}}}};\n  const ElementId<3> right_id(\n      0, {{SegmentId{1, 1}, SegmentId{1, 1}, SegmentId{1, 0}}});\n  const ElementId<3> front_id(\n      0, {{SegmentId{1, 0}, SegmentId{1, 0}, SegmentId{1, 0}}});\n  const ElementId<3> top_id(\n      0, {{SegmentId{1, 0}, SegmentId{1, 1}, SegmentId{1, 1}}});\n  DirectionMap<3, Neighbors<3>> neighbors{};\n  neighbors[Direction<3>::upper_xi()] =\n      Neighbors<3>{{right_id}, OrientationMap<3>::create_aligned()};\n  neighbors[Direction<3>::lower_eta()] =\n      Neighbors<3>{{front_id}, OrientationMap<3>::create_aligned()};\n  neighbors[Direction<3>::upper_zeta()] =\n      Neighbors<3>{{top_id}, OrientationMap<3>::create_aligned()};\n  return Element<3>{element_id, neighbors};\n}\n\ntemplate <size_t Dim>\nvoid test_p_refine_gts() {\n  const Mesh<Dim> old_mesh{2, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  Mesh<Dim> new_mesh{3, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  Mesh<Dim> neighbor_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  const auto old_element = make_element<Dim>();\n  auto new_element = make_element<Dim>();\n  const TimeStepId next_temporal_id{true, 3, Time{Slab{0.2, 3.4}, {6, 100}}};\n\n  ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>> mortar_data{};\n  ::dg::MortarMap<Dim, Mesh<Dim - 1>> mortar_mesh{};\n  ::dg::MortarMap<Dim, MortarInfo<Dim>> mortar_infos{};\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      normal_covector_and_magnitude{};\n  mortar_data_history_type<Dim> mortar_data_history{};\n\n  ::dg::MortarMap<Dim, TimeStepId> mortar_next_temporal_ids{};\n  ::dg::MortarMap<Dim, Mesh<Dim>> neighbor_meshes{};\n  for (const auto& [direction, neighbors] : old_element.neighbors()) {\n    normal_covector_and_magnitude[direction] = std::nullopt;\n    for (const auto& neighbor : neighbors) {\n      const DirectionalId<Dim> mortar_id{direction, neighbor};\n      mortar_data.emplace(mortar_id, MortarDataHolder<Dim>{});\n      mortar_mesh.emplace(\n          mortar_id,\n          ::dg::mortar_mesh(old_mesh.slice_away(direction.dimension()),\n                            neighbor_mesh.slice_away(direction.dimension())));\n      const auto& neighbor_orientation = neighbors.orientation(neighbor);\n      mortar_infos.emplace(\n          mortar_id,\n          MortarInfo<Dim>{\n              {.mortar_size = ::dg::mortar_size(old_element.id(), neighbor,\n                                                direction.dimension(),\n                                                neighbor_orientation),\n               .interface_data_policy =\n                   neighbor_orientation.is_aligned()\n                       ? InterfaceDataPolicy::CopyProject\n                       : InterfaceDataPolicy::OrientCopyProject,\n               .time_stepping_policy = TimeSteppingPolicy::EqualRate}});\n      mortar_next_temporal_ids.emplace(mortar_id, next_temporal_id);\n      neighbor_meshes.emplace(mortar_id, neighbor_mesh);\n    }\n  }\n\n  ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>\n      expected_mortar_data{};\n  ::dg::MortarMap<Dim, Mesh<Dim - 1>> expected_mortar_mesh{};\n  ::dg::MortarMap<Dim, MortarInfo<Dim>> expected_mortar_infos{};\n  ::dg::MortarMap<Dim, TimeStepId> expected_mortar_next_temporal_ids{};\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      expected_normal_covector_and_magnitude{};\n  mortar_data_history_type<Dim> expected_mortar_data_history{};\n  for (const auto& [direction, neighbors] : new_element.neighbors()) {\n    expected_normal_covector_and_magnitude[direction] = std::nullopt;\n    for (const auto& neighbor : neighbors) {\n      const DirectionalId<Dim> mortar_id{direction, neighbor};\n      expected_mortar_data.emplace(mortar_id, MortarDataHolder<Dim>{});\n      expected_mortar_mesh.emplace(\n          mortar_id,\n          ::dg::mortar_mesh(new_mesh.slice_away(direction.dimension()),\n                            neighbor_mesh.slice_away(direction.dimension())));\n      const auto& neighbor_orientation = neighbors.orientation(neighbor);\n      expected_mortar_infos.emplace(\n          mortar_id,\n          MortarInfo<Dim>{\n              {.mortar_size = ::dg::mortar_size(new_element.id(), neighbor,\n                                                direction.dimension(),\n                                                neighbor_orientation),\n               .interface_data_policy =\n                   neighbor_orientation.is_aligned()\n                       ? evolution::dg::InterfaceDataPolicy::CopyProject\n                       : evolution::dg::InterfaceDataPolicy::OrientCopyProject,\n               .time_stepping_policy = TimeSteppingPolicy::EqualRate}});\n      expected_mortar_next_temporal_ids.emplace(mortar_id, next_temporal_id);\n    }\n  }\n  for (const auto& direction : new_element.external_boundaries()) {\n    normal_covector_and_magnitude[direction] = std::nullopt;\n    expected_normal_covector_and_magnitude[direction] = std::nullopt;\n  }\n\n  test_p_refine<Dim, false>(\n      std::move(mortar_data), std::move(mortar_mesh), std::move(mortar_infos),\n      std::move(mortar_next_temporal_ids),\n      std::move(normal_covector_and_magnitude), std::move(mortar_data_history),\n      old_mesh, std::move(new_mesh), old_element, std::move(new_element),\n      neighbor_meshes, next_temporal_id, expected_mortar_data,\n      expected_mortar_mesh, expected_mortar_infos,\n      expected_mortar_next_temporal_ids, expected_normal_covector_and_magnitude,\n      expected_mortar_data_history);\n}\n\n// The data arrays are set to linear functions, with element_size and\n// mortar_size are used to choose the domain the function is evaluated\n// on.  The mortar_size is relative to the size of the element, and at\n// least one of the two must be Full in each dimension.\ntemplate <size_t Dim>\nMortarData<Dim> make_mortar_data(\n    const Mesh<Dim - 1>& mortar_mesh, const Mesh<Dim - 1>& face_mesh,\n    const Mesh<Dim>& volume_mesh, const bool is_local_side, const double value,\n    const std::array<Spectral::SegmentSize, Dim>& element_size,\n    const std::array<Spectral::SegmentSize, Dim - 1>& mortar_size,\n    const size_t dimension) {\n  const auto linear_func =\n      []<size_t D>(const Mesh<D>& mesh,\n                   const std::array<Spectral::SegmentSize, D>& size) {\n        if constexpr (D == 0) {\n          return DataVector{1.0};\n        } else {\n          const auto coords = logical_coordinates(mesh);\n          DataVector linear(mesh.number_of_grid_points(), 0.0);\n          for (size_t i = 0; i < D; ++i) {\n            switch (gsl::at(size, i)) {\n              case Spectral::SegmentSize::LowerHalf:\n                linear += 0.5 * (coords.get(i) - 1.0);\n                break;\n              case Spectral::SegmentSize::UpperHalf:\n                linear += 0.5 * (coords.get(i) + 1.0);\n                break;\n              default:\n                ASSERT(gsl::at(size, i) == Spectral::SegmentSize::Full,\n                       \"Bad argument: \" << gsl::at(size, i));\n                linear += coords.get(i);\n                break;\n            }\n          }\n          return linear;\n        }\n      };\n\n  const auto face_size = all_but_specified_element_of(element_size, dimension);\n  auto absolute_mortar_size = mortar_size;\n  for (size_t i = 0; i < Dim - 1; ++i) {\n    if (gsl::at(face_size, i) != Spectral::SegmentSize::Full) {\n      ASSERT(gsl::at(mortar_size, i) == Spectral::SegmentSize::Full,\n             \"Can't represent a quarter segment.\");\n      gsl::at(absolute_mortar_size, i) = gsl::at(face_size, i);\n    }\n  }\n\n  MortarData<Dim> mortar_data;\n  mortar_data.mortar_data.emplace(\n      value * linear_func(mortar_mesh, absolute_mortar_size));\n  mortar_data.mortar_mesh.emplace(mortar_mesh);\n  if (is_local_side) {\n    mortar_data.face_normal_magnitude.emplace(\n        2.0 * value * linear_func(face_mesh, face_size));\n    mortar_data.face_det_jacobian.emplace(3.0 * value *\n                                          linear_func(face_mesh, face_size));\n    mortar_data.face_mesh.emplace(face_mesh);\n    mortar_data.volume_det_inv_jacobian.emplace(\n        4.0 * value * linear_func(volume_mesh, element_size));\n    mortar_data.volume_mesh.emplace(volume_mesh);\n  }\n  return mortar_data;\n}\n\ntemplate <size_t Dim>\nMortarData<Dim> make_mortar_data(const Mesh<Dim - 1>& mortar_mesh,\n                                 const Mesh<Dim - 1>& face_mesh,\n                                 const Mesh<Dim>& volume_mesh,\n                                 const bool is_local_side, const double value) {\n  return make_mortar_data<Dim>(\n      mortar_mesh, face_mesh, volume_mesh, is_local_side, value,\n      make_array<Dim>(Spectral::SegmentSize::Full),\n      make_array<Dim - 1>(Spectral::SegmentSize::Full),\n      // Dimension doesn't matter for full-size elements\n      0);\n}\n\ntemplate <size_t Dim>\nusing boundary_history_type =\n    typename mortar_data_history_type<Dim>::mapped_type;\n\ntemplate <size_t Dim>\nvoid test_p_refine_lts() {\n  const Mesh<Dim> old_mesh{4, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  Mesh<Dim> new_mesh{5, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> neighbor_mesh{3, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n\n  const auto old_element = make_element<Dim>();\n  auto new_element = make_element<Dim>();\n  const TimeStepId next_temporal_id{true, 3, Time{Slab{0.2, 3.4}, {6, 100}}};\n  const std::vector<TimeStepId> local_past_ids{\n      TimeStepId{true, 3, Time{Slab{0.2, 3.4}, {0, 100}}},\n      TimeStepId{true, 3, Time{Slab{0.2, 3.4}, {2, 100}}},\n      TimeStepId{true, 3, Time{Slab{0.2, 3.4}, {4, 100}}}};\n  const std::vector<TimeStepId> remote_past_ids{\n      TimeStepId{true, 3, Time{Slab{0.2, 3.4}, {0, 100}}},\n      TimeStepId{true, 3, Time{Slab{0.2, 3.4}, {3, 100}}}};\n\n  ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>> mortar_data{};\n  ::dg::MortarMap<Dim, Mesh<Dim - 1>> mortar_mesh{};\n  ::dg::MortarMap<Dim, MortarInfo<Dim>> mortar_infos{};\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      normal_covector_and_magnitude{};\n  mortar_data_history_type<Dim> mortar_data_history{};\n  ::dg::MortarMap<Dim, TimeStepId> mortar_next_temporal_ids{};\n  ::dg::MortarMap<Dim, Mesh<Dim>> neighbor_meshes{};\n  for (const auto& [direction, neighbors] : old_element.neighbors()) {\n    normal_covector_and_magnitude[direction] = std::nullopt;\n    for (const auto& neighbor : neighbors) {\n      const DirectionalId<Dim> mortar_id{direction, neighbor};\n      mortar_data.emplace(mortar_id, MortarDataHolder<Dim>{});\n      mortar_mesh.emplace(\n          mortar_id,\n          ::dg::mortar_mesh(old_mesh.slice_away(direction.dimension()),\n                            neighbor_mesh.slice_away(direction.dimension())));\n      const auto& neighbor_orientation = neighbors.orientation(neighbor);\n      mortar_infos.emplace(\n          mortar_id,\n          MortarInfo<Dim>{\n              {.mortar_size = ::dg::mortar_size(old_element.id(), neighbor,\n                                                direction.dimension(),\n                                                neighbor_orientation),\n               .interface_data_policy =\n                   neighbor_orientation.is_aligned()\n                       ? InterfaceDataPolicy::CopyProject\n                       : InterfaceDataPolicy::OrientCopyProject,\n               .time_stepping_policy = TimeSteppingPolicy::Conservative}});\n      mortar_next_temporal_ids.emplace(mortar_id, next_temporal_id);\n      neighbor_meshes.emplace(mortar_id, neighbor_mesh);\n      mortar_data_history.emplace(mortar_id, boundary_history_type<Dim>{});\n      for (size_t i = 0; i < 3; ++i) {\n        mortar_data_history.at(mortar_id).local().insert(\n            local_past_ids[i], 3,\n            make_mortar_data(mortar_mesh.at(mortar_id),\n                             old_mesh.slice_away(direction.dimension()),\n                             old_mesh, true, 5.0 + static_cast<double>(i)));\n      }\n      for (size_t i = 0; i < 2; ++i) {\n        mortar_data_history.at(mortar_id).remote().insert(\n            local_past_ids[i], 3,\n            make_mortar_data(mortar_mesh.at(mortar_id),\n                             neighbor_mesh.slice_away(direction.dimension()),\n                             neighbor_mesh, false,\n                             5.0 + static_cast<double>(i)));\n      }\n    }\n  }\n\n  ::dg::MortarMap<Dim, evolution::dg::MortarDataHolder<Dim>>\n      expected_mortar_data{};\n  ::dg::MortarMap<Dim, Mesh<Dim - 1>> expected_mortar_mesh{};\n  ::dg::MortarMap<Dim, MortarInfo<Dim>> expected_mortar_infos{};\n  ::dg::MortarMap<Dim, TimeStepId> expected_mortar_next_temporal_ids{};\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      expected_normal_covector_and_magnitude{};\n  mortar_data_history_type<Dim> expected_mortar_data_history{};\n  for (const auto& [direction, neighbors] : new_element.neighbors()) {\n    expected_normal_covector_and_magnitude[direction] = std::nullopt;\n    for (const auto& neighbor : neighbors) {\n      const DirectionalId<Dim> mortar_id{direction, neighbor};\n      expected_mortar_data.emplace(mortar_id, MortarDataHolder<Dim>{});\n      expected_mortar_mesh.emplace(\n          mortar_id,\n          ::dg::mortar_mesh(new_mesh.slice_away(direction.dimension()),\n                            neighbor_mesh.slice_away(direction.dimension())));\n      const auto& neighbor_orientation = neighbors.orientation(neighbor);\n      expected_mortar_infos.emplace(\n          mortar_id,\n          MortarInfo<Dim>{\n              {.mortar_size = ::dg::mortar_size(new_element.id(), neighbor,\n                                                direction.dimension(),\n                                                neighbor_orientation),\n               .interface_data_policy =\n                   neighbor_orientation.is_aligned()\n                       ? InterfaceDataPolicy::CopyProject\n                       : InterfaceDataPolicy::OrientCopyProject,\n               .time_stepping_policy = TimeSteppingPolicy::Conservative}});\n      expected_mortar_next_temporal_ids.emplace(mortar_id, next_temporal_id);\n      expected_mortar_data_history.emplace(mortar_id,\n                                           boundary_history_type<Dim>{});\n      // These use the old mortar_mesh, not expected_mortar_mesh,\n      // because mortar data is not projected during p-refinement.\n      for (size_t i = 0; i < 3; ++i) {\n        expected_mortar_data_history.at(mortar_id).local().insert(\n            local_past_ids[i], 3,\n            make_mortar_data(mortar_mesh.at(mortar_id),\n                             new_mesh.slice_away(direction.dimension()),\n                             new_mesh, true, 5.0 + static_cast<double>(i)));\n      }\n      for (size_t i = 0; i < 2; ++i) {\n        expected_mortar_data_history.at(mortar_id).remote().insert(\n            local_past_ids[i], 3,\n            make_mortar_data(mortar_mesh.at(mortar_id),\n                             neighbor_mesh.slice_away(direction.dimension()),\n                             neighbor_mesh, false,\n                             5.0 + static_cast<double>(i)));\n      }\n    }\n  }\n  for (const auto& direction : new_element.external_boundaries()) {\n    normal_covector_and_magnitude[direction] = std::nullopt;\n    expected_normal_covector_and_magnitude[direction] = std::nullopt;\n  }\n\n  test_p_refine<Dim, true>(\n      std::move(mortar_data), std::move(mortar_mesh), std::move(mortar_infos),\n      std::move(mortar_next_temporal_ids),\n      std::move(normal_covector_and_magnitude), std::move(mortar_data_history),\n      old_mesh, std::move(new_mesh), old_element, std::move(new_element),\n      neighbor_meshes, next_temporal_id, expected_mortar_data,\n      expected_mortar_mesh, expected_mortar_infos,\n      expected_mortar_next_temporal_ids, expected_normal_covector_and_magnitude,\n      expected_mortar_data_history);\n}\n\ntemplate <size_t Dim>\nMesh<Dim> lgl_mesh(const size_t uniform_extents) {\n  return {uniform_extents, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto};\n}\n\ntemplate <size_t Dim>\nMesh<Dim> lgl_mesh(const std::array<size_t, Dim>& extents) {\n  return {extents, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto};\n}\n\ntemplate <size_t NumMortars>\n::dg::MortarMap<2, evolution::dg::MortarDataHolder<2>> empty_mortar_data(\n    const std::array<DirectionalId<2>, NumMortars>& mortar_ids) {\n  ::dg::MortarMap<2, evolution::dg::MortarDataHolder<2>> mortar_data{};\n  for (const auto& mortar_id : mortar_ids) {\n    mortar_data.emplace(mortar_id, evolution::dg::MortarDataHolder<2>{});\n  }\n  return mortar_data;\n}\n\ntemplate <size_t NumMortars>\n::dg::MortarMap<2, TimeStepId> constant_next_temporal_ids(\n    const std::array<DirectionalId<2>, NumMortars>& mortar_ids,\n    const TimeStepId& temporal_id) {\n  ::dg::MortarMap<2, TimeStepId> next_temporal_ids{};\n  for (const auto& mortar_id : mortar_ids) {\n    next_temporal_ids.emplace(mortar_id, temporal_id);\n  }\n  return next_temporal_ids;\n}\n\ntemplate <size_t Dim>\nboundary_history_type<Dim> make_boundary_history(\n    const DirectionalId<Dim>& mortar_id, const Mesh<Dim>& volume_mesh,\n    const Mesh<Dim - 1>& mortar_mesh,\n    const std::array<Spectral::SegmentSize, Dim>& element_size,\n    const std::array<Spectral::SegmentSize, Dim - 1>& mortar_size,\n    const bool include_local, const bool include_remote) {\n  const TimeStepId history_temporal_id(true, 4, Slab(2.0, 3.0).start());\n  boundary_history_type<Dim> history{};\n  if (include_local) {\n    const auto face_mesh =\n        volume_mesh.slice_away(mortar_id.direction().dimension());\n    history.local().insert(\n        history_temporal_id, 1,\n        make_mortar_data<Dim>(\n            mortar_mesh, face_mesh, volume_mesh, true,\n            static_cast<double>(mortar_id.id().block_id()) + 1.0, element_size,\n            mortar_size, mortar_id.direction().dimension()));\n  }\n  if (include_remote) {\n    history.remote().insert(\n        history_temporal_id, 1,\n        make_mortar_data<Dim>(\n            mortar_mesh, {}, {}, false,\n            static_cast<double>(mortar_id.id().block_id()) + 2.0, element_size,\n            mortar_size, mortar_id.direction().dimension()));\n  }\n  return history;\n}\n\ntemplate <size_t NumMortars>\nTags::MortarDataHistory<2>::type make_boundary_histories(\n    const std::array<DirectionalId<2>, NumMortars>& mortar_ids,\n    const Mesh<2>& volume_mesh,\n    const ::dg::MortarMap<2, Mesh<1>>& mortar_meshes,\n    const std::array<Spectral::SegmentSize, 2>& element_size,\n    const ::dg::MortarMap<2, MortarInfo<2>>& mortar_infos,\n    const bool include_local, const bool include_remote) {\n  Tags::MortarDataHistory<2>::type histories{};\n  for (const auto& mortar_id : mortar_ids) {\n    histories.emplace(\n        mortar_id, make_boundary_history(\n                       mortar_id, volume_mesh, mortar_meshes.at(mortar_id),\n                       element_size, mortar_infos.at(mortar_id).mortar_size(),\n                       include_local, include_remote));\n  }\n  return histories;\n}\n\n// For h-refinement tests, we use a 2D element and have neighbors\n// change as:\n//\n//     +---+   eta        +---+\n//     |a  |   |          |   |\n//     |   |   +- xi      | p-|\n//     |   |              |ref|\n// +---+---+---+      +-+-+---+---+\n// |b  |   |c  |      | |f|   |g  |\n// |   | X |   |  =>  | | | X +---+\n// |   |   |   |      | | |   |h  |\n// +---+-+-+---+      +-+-+---+---+\n//     |d|e|              |i  |\n//     | | |              |   |\n//     | | |              |   |\n//     +-+-+              +---+\n//\n// Originally, a,b,c,d have mesh (3,4) and e has (5,6), all in the\n// coordinates of the central element.  After refining, a has (4,5), i\n// has (5,6), and the others are still (3,4).\n//\n// The central region can do nothing, p-refine, split, or join.\ntemplate <bool LocalTimeStepping>\nvoid test_h_refinement() {\n  using NormalVars =\n      Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                           evolution::dg::Tags::NormalCovector<2>>>;\n  using mortar_data_history_tag = Tags::MortarDataHistory<2>;\n\n  const DirectionalId<2> mortar_id_a(Direction<2>::upper_eta(),\n                                     ElementId<2>(0));\n  const DirectionalId<2> mortar_id_b(Direction<2>::lower_xi(), ElementId<2>(1));\n  const DirectionalId<2> mortar_id_c(Direction<2>::upper_xi(), ElementId<2>(3));\n  const DirectionalId<2> mortar_id_d(Direction<2>::lower_eta(),\n                                     ElementId<2>(4, {{{1, 0}, {0, 0}}}));\n  const DirectionalId<2> mortar_id_e(Direction<2>::lower_eta(),\n                                     ElementId<2>(4, {{{1, 1}, {0, 0}}}));\n  // rotated\n  const DirectionalId<2> mortar_id_f(Direction<2>::lower_xi(),\n                                     ElementId<2>(1, {{{0, 0}, {1, 1}}}));\n  const DirectionalId<2> mortar_id_g(Direction<2>::upper_xi(),\n                                     ElementId<2>(3, {{{0, 0}, {1, 1}}}));\n  const DirectionalId<2> mortar_id_h(Direction<2>::upper_xi(),\n                                     ElementId<2>(3, {{{0, 0}, {1, 0}}}));\n  const DirectionalId<2> mortar_id_i(Direction<2>::lower_eta(),\n                                     ElementId<2>(4));\n  const std::array orig_mortar_ids{mortar_id_a, mortar_id_b, mortar_id_c,\n                                   mortar_id_d, mortar_id_e};\n  const std::array refined_mortar_ids{mortar_id_a, mortar_id_f, mortar_id_g,\n                                      mortar_id_h, mortar_id_i};\n\n  const OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n  const OrientationMap<2> rotated{\n      std::array{Direction<2>::upper_eta(), Direction<2>::lower_xi()}};\n  const TimeStepId temporal_id(true, 5, Slab(3.0, 6.0).start());\n  const Mesh<2> orig_mesh = lgl_mesh<2>({{3, 4}});\n  const auto time_stepping_policy = LocalTimeStepping\n                                        ? TimeSteppingPolicy::Conservative\n                                        : TimeSteppingPolicy::EqualRate;\n\n  tuples::TaggedTuple<domain::Tags::Mesh<2>, domain::Tags::Element<2>,\n                      domain::Tags::NeighborMesh<2>, ::Tags::TimeStepId,\n                      Tags::MortarData<2>, Tags::MortarMesh<2>,\n                      Tags::MortarInfo<2>, Tags::MortarNextTemporalId<2>,\n                      evolution::dg::Tags::NormalCovectorAndMagnitude<2>,\n                      mortar_data_history_tag>\n      orig_single_items{};\n  {\n    const ElementId<2> id(2);\n\n    get<domain::Tags::Mesh<2>>(orig_single_items) = orig_mesh;\n\n    get<domain::Tags::Element<2>>(orig_single_items) = Element<2>(\n        id,\n        {{mortar_id_a.direction(), Neighbors<2>({mortar_id_a.id()}, rotated)},\n         {mortar_id_b.direction(), Neighbors<2>({mortar_id_b.id()}, rotated)},\n         {mortar_id_c.direction(), Neighbors<2>({mortar_id_c.id()}, aligned)},\n         {mortar_id_d.direction(),\n          Neighbors<2>({mortar_id_d.id(), mortar_id_e.id()}, aligned)}});\n\n    get<domain::Tags::NeighborMesh<2>>(orig_single_items) = {\n        {mortar_id_a, orig_mesh},\n        {mortar_id_b, orig_mesh},\n        {mortar_id_c, orig_mesh},\n        {mortar_id_d, orig_mesh},\n        {mortar_id_e, lgl_mesh<2>({{5, 6}})}};\n\n    get<::Tags::TimeStepId>(orig_single_items) = temporal_id;\n\n    get<Tags::MortarData<2>>(orig_single_items) =\n        empty_mortar_data(orig_mortar_ids);\n\n    get<Tags::MortarMesh<2>>(orig_single_items) = {\n        {mortar_id_a, lgl_mesh<1>(3)},\n        {mortar_id_b, lgl_mesh<1>(4)},\n        {mortar_id_c, lgl_mesh<1>(4)},\n        {mortar_id_d, lgl_mesh<1>(3)},\n        {mortar_id_e, lgl_mesh<1>(5)}};\n\n    get<Tags::MortarInfo<2>>(orig_single_items) = {\n        {mortar_id_a,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::OrientCopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_b,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::OrientCopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_c,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_d,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::LowerHalf}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_e,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::UpperHalf}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}}};\n\n    get<Tags::MortarNextTemporalId<2>>(orig_single_items) =\n        constant_next_temporal_ids(orig_mortar_ids, temporal_id);\n\n    // Values aren't used, except for checking that they haven't changed.\n    get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(\n        orig_single_items) = {{Direction<2>::upper_eta(), NormalVars(3, 1.0)},\n                              {Direction<2>::lower_xi(), NormalVars(4, 2.0)},\n                              {Direction<2>::upper_xi(), NormalVars(4, 3.0)},\n                              {Direction<2>::lower_eta(), NormalVars(3, 4.0)}};\n\n    if (LocalTimeStepping) {\n      get<mortar_data_history_tag>(orig_single_items) = make_boundary_histories(\n          orig_mortar_ids, orig_mesh,\n          get<Tags::MortarMesh<2>>(orig_single_items),\n          make_array<2>(Spectral::SegmentSize::Full),\n          get<Tags::MortarInfo<2>>(orig_single_items), true, true);\n    }\n  }\n\n  const auto refined_a_mesh = lgl_mesh<2>({{4, 5}});\n\n  const Element<2> refined_single_element(\n      get<domain::Tags::Element<2>>(orig_single_items).id(),\n      {{mortar_id_a.direction(), Neighbors<2>({mortar_id_a.id()}, rotated)},\n       {mortar_id_f.direction(), Neighbors<2>({mortar_id_f.id()}, rotated)},\n       {mortar_id_g.direction(),\n        Neighbors<2>({mortar_id_g.id(), mortar_id_h.id()}, aligned)},\n       {mortar_id_i.direction(), Neighbors<2>({mortar_id_i.id()}, aligned)}});\n\n  const auto& orig_neighbor_meshes =\n      get<domain::Tags::NeighborMesh<2>>(orig_single_items);\n  const ::dg::MortarMap<2, Mesh<2>> refined_single_neighbor_meshes{\n      {mortar_id_a, refined_a_mesh},\n      {mortar_id_f, orig_neighbor_meshes.at(mortar_id_b)},\n      {mortar_id_g, orig_neighbor_meshes.at(mortar_id_c)},\n      {mortar_id_h, orig_neighbor_meshes.at(mortar_id_c)},\n      {mortar_id_i, orig_neighbor_meshes.at(mortar_id_e)}};\n\n  const ::dg::MortarMap<2, MortarInfo<2>> expected_single_mortar_infos{\n      {mortar_id_a,\n       MortarInfo<2>{\n           {.mortar_size = {{Spectral::SegmentSize::Full}},\n            .interface_data_policy = InterfaceDataPolicy::OrientCopyProject,\n            .time_stepping_policy = time_stepping_policy}}},\n      {mortar_id_f,\n       MortarInfo<2>{\n           {.mortar_size = {{Spectral::SegmentSize::Full}},\n            .interface_data_policy = InterfaceDataPolicy::OrientCopyProject,\n            .time_stepping_policy = time_stepping_policy}}},\n      {mortar_id_g,\n       MortarInfo<2>{{.mortar_size = {{Spectral::SegmentSize::UpperHalf}},\n                      .interface_data_policy = InterfaceDataPolicy::CopyProject,\n                      .time_stepping_policy = time_stepping_policy}}},\n      {mortar_id_h,\n       MortarInfo<2>{{.mortar_size = {{Spectral::SegmentSize::LowerHalf}},\n                      .interface_data_policy = InterfaceDataPolicy::CopyProject,\n                      .time_stepping_policy = time_stepping_policy}}},\n      {mortar_id_i,\n       MortarInfo<2>{{.mortar_size = {{Spectral::SegmentSize::Full}},\n                      .interface_data_policy = InterfaceDataPolicy::CopyProject,\n                      .time_stepping_policy = time_stepping_policy}}}};\n\n  const DirectionMap<2, std::optional<NormalVars>>\n      empty_normal_covector_and_magnitude{\n          {Direction<2>::upper_eta(), std::nullopt},\n          {Direction<2>::lower_xi(), std::nullopt},\n          {Direction<2>::upper_xi(), std::nullopt},\n          {Direction<2>::lower_eta(), std::nullopt}};\n\n  {\n    INFO(\"No local refinement\");\n    auto box = tmpl::as_pack<decltype(orig_single_items)>(\n        [&]<typename... Tags>(tmpl::type_<Tags>... /*meta*/) {\n          return db::create<\n              db::AddSimpleTags<domain::Tags::Domain<2>, Tags...>>(\n              Domain<2>{}, get<Tags>(orig_single_items)...);\n        });\n\n    const auto& mortar_ids = refined_mortar_ids;\n    db::mutate<domain::Tags::Element<2>, domain::Tags::NeighborMesh<2>>(\n        [&](const gsl::not_null<Element<2>*> element,\n            const gsl::not_null<::dg::MortarMap<2, Mesh<2>>*> neighbor_meshes) {\n          *element = refined_single_element;\n          *neighbor_meshes = refined_single_neighbor_meshes;\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<\n        evolution::dg::Initialization::ProjectMortars<2, LocalTimeStepping>>(\n        make_not_null(&box),\n        std::pair(get<domain::Tags::Mesh<2>>(orig_single_items),\n                  get<domain::Tags::Element<2>>(orig_single_items)));\n\n    const ::dg::MortarMap<2, Mesh<1>> expected_mortar_meshes{\n        {mortar_id_a, lgl_mesh<1>(4)},\n        {mortar_id_f, lgl_mesh<1>(4)},\n        {mortar_id_g, lgl_mesh<1>(4)},\n        {mortar_id_h, lgl_mesh<1>(4)},\n        {mortar_id_i, lgl_mesh<1>(5)}};\n\n    mortar_data_history_tag::type expected_mortar_data_history{};\n    if (LocalTimeStepping) {\n      expected_mortar_data_history = make_boundary_histories(\n          refined_mortar_ids, orig_mesh, expected_mortar_meshes,\n          make_array<2>(Spectral::SegmentSize::Full),\n          expected_single_mortar_infos, true, false);\n      // No projection when no h-refinement\n      expected_mortar_data_history[mortar_id_a] =\n          get<mortar_data_history_tag>(orig_single_items).at(mortar_id_a);\n    }\n\n    CHECK(db::get<Tags::MortarData<2>>(box) == empty_mortar_data(mortar_ids));\n    CHECK(db::get<Tags::MortarMesh<2>>(box) == expected_mortar_meshes);\n    CHECK(db::get<Tags::MortarInfo<2>>(box) == expected_single_mortar_infos);\n    CHECK(db::get<Tags::MortarNextTemporalId<2>>(box) ==\n          constant_next_temporal_ids(mortar_ids, temporal_id));\n    CHECK(db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(box) ==\n          get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(\n              orig_single_items));\n    check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                             expected_mortar_data_history);\n  }\n\n  {\n    INFO(\"Local p-refinement\");\n    auto box = tmpl::as_pack<decltype(orig_single_items)>(\n        [&]<typename... Tags>(tmpl::type_<Tags>... /*meta*/) {\n          return db::create<\n              db::AddSimpleTags<domain::Tags::Domain<2>, Tags...>>(\n              Domain<2>{}, get<Tags>(orig_single_items)...);\n        });\n\n    const auto& mortar_ids = refined_mortar_ids;\n    const auto refined_mesh = lgl_mesh<2>({{4, 5}});\n    db::mutate<domain::Tags::Element<2>, domain::Tags::Mesh<2>,\n               domain::Tags::NeighborMesh<2>>(\n        [&](const gsl::not_null<Element<2>*> element,\n            const gsl::not_null<Mesh<2>*> mesh,\n            const gsl::not_null<::dg::MortarMap<2, Mesh<2>>*> neighbor_meshes) {\n          *element = refined_single_element;\n          *mesh = refined_mesh;\n          *neighbor_meshes = refined_single_neighbor_meshes;\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<\n        evolution::dg::Initialization::ProjectMortars<2, LocalTimeStepping>>(\n        make_not_null(&box),\n        std::pair(get<domain::Tags::Mesh<2>>(orig_single_items),\n                  get<domain::Tags::Element<2>>(orig_single_items)));\n\n    const ::dg::MortarMap<2, Mesh<1>> expected_mortar_meshes{\n        {mortar_id_a, lgl_mesh<1>(4)},\n        {mortar_id_f, lgl_mesh<1>(5)},\n        {mortar_id_g, lgl_mesh<1>(5)},\n        {mortar_id_h, lgl_mesh<1>(5)},\n        {mortar_id_i, lgl_mesh<1>(5)}};\n\n    mortar_data_history_tag::type expected_mortar_data_history{};\n    if (LocalTimeStepping) {\n      expected_mortar_data_history = make_boundary_histories(\n          refined_mortar_ids, refined_mesh, expected_mortar_meshes,\n          make_array<2>(Spectral::SegmentSize::Full),\n          expected_single_mortar_infos, true, false);\n\n      // No projection of mortar data when no h-refinement, but\n      // geometric data is projected.\n      expected_mortar_data_history[mortar_id_a] = make_boundary_history(\n          mortar_id_a, refined_mesh,\n          get<Tags::MortarMesh<2>>(orig_single_items).at(mortar_id_a),\n          make_array<2>(Spectral::SegmentSize::Full),\n          expected_single_mortar_infos.at(mortar_id_a).mortar_size(), true,\n          true);\n    }\n\n    CHECK(db::get<Tags::MortarData<2>>(box) == empty_mortar_data(mortar_ids));\n    CHECK(db::get<Tags::MortarMesh<2>>(box) == expected_mortar_meshes);\n    CHECK(db::get<Tags::MortarInfo<2>>(box) == expected_single_mortar_infos);\n    CHECK(db::get<Tags::MortarNextTemporalId<2>>(box) ==\n          constant_next_temporal_ids(mortar_ids, temporal_id));\n    CHECK(db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(box) ==\n          empty_normal_covector_and_magnitude);\n    check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                             expected_mortar_data_history);\n  }\n\n  const ElementId<2> id_nw(2, {{{1, 0}, {1, 1}}});\n  const ElementId<2> id_ne(2, {{{1, 1}, {1, 1}}});\n  const ElementId<2> id_sw(2, {{{1, 0}, {1, 0}}});\n  const ElementId<2> id_se(2, {{{1, 1}, {1, 0}}});\n  const auto element_size_nw = domain::child_size(\n      id_nw.segment_ids(), refined_single_element.id().segment_ids());\n  const auto element_size_ne = domain::child_size(\n      id_ne.segment_ids(), refined_single_element.id().segment_ids());\n  const auto element_size_sw = domain::child_size(\n      id_sw.segment_ids(), refined_single_element.id().segment_ids());\n  const auto element_size_se = domain::child_size(\n      id_se.segment_ids(), refined_single_element.id().segment_ids());\n  const DirectionalId<2> mortar_id_nw_ne(Direction<2>::upper_xi(), id_ne);\n  const DirectionalId<2> mortar_id_nw_sw(Direction<2>::lower_eta(), id_sw);\n  const DirectionalId<2> mortar_id_ne_nw(Direction<2>::lower_xi(), id_nw);\n  const DirectionalId<2> mortar_id_ne_se(Direction<2>::lower_eta(), id_se);\n  const DirectionalId<2> mortar_id_sw_se(Direction<2>::upper_xi(), id_se);\n  const DirectionalId<2> mortar_id_sw_nw(Direction<2>::upper_eta(), id_nw);\n  const DirectionalId<2> mortar_id_se_sw(Direction<2>::lower_xi(), id_sw);\n  const DirectionalId<2> mortar_id_se_ne(Direction<2>::upper_eta(), id_ne);\n\n  {\n    INFO(\"Join\");\n    auto box =\n        db::create<tmpl::push_front<decltype(orig_single_items)::tags_list,\n                                    domain::Tags::Domain<2>>>();\n\n    const auto& mortar_ids = refined_mortar_ids;\n    db::mutate<domain::Tags::Element<2>, domain::Tags::Mesh<2>,\n               domain::Tags::NeighborMesh<2>>(\n        [&](const gsl::not_null<Element<2>*> element,\n            const gsl::not_null<Mesh<2>*> mesh,\n            const gsl::not_null<::dg::MortarMap<2, Mesh<2>>*> neighbor_meshes) {\n          *element = refined_single_element;\n          *mesh = orig_mesh;\n          *neighbor_meshes = refined_single_neighbor_meshes;\n        },\n        make_not_null(&box));\n\n    using ChildItems =\n        tuples::TaggedTuple<::Tags::TimeStepId, mortar_data_history_tag>;\n    mortar_data_history_tag::type history_nw{};\n    mortar_data_history_tag::type history_ne{};\n    mortar_data_history_tag::type history_sw{};\n    mortar_data_history_tag::type history_se{};\n    if (LocalTimeStepping) {\n      // Only the mortar_size is used for constructing the histories\n      const auto dummy_mortar_infos = [](const auto& ids) {\n        ::dg::MortarMap<2, MortarInfo<2>> infos{};\n        for (const auto& id : ids) {\n          infos.emplace(\n              id,\n              MortarInfo<2>{{.mortar_size = {{Spectral::SegmentSize::Full}}}});\n        }\n        return infos;\n      };\n      const std::array mortar_ids_nw{mortar_id_a, mortar_id_b, mortar_id_nw_ne,\n                                     mortar_id_nw_sw};\n      const std::array mortar_ids_ne{mortar_id_a, mortar_id_ne_nw, mortar_id_c,\n                                     mortar_id_ne_se};\n      const std::array mortar_ids_sw{mortar_id_sw_nw, mortar_id_b,\n                                     mortar_id_sw_se, mortar_id_d};\n      const std::array mortar_ids_se{mortar_id_se_ne, mortar_id_se_sw,\n                                     mortar_id_c, mortar_id_e};\n      const ::dg::MortarMap<2, Mesh<1>> mortar_meshes_nw{\n          {mortar_id_a, lgl_mesh<1>(3)},\n          {mortar_id_b, lgl_mesh<1>(4)},\n          {mortar_id_nw_ne, lgl_mesh<1>(4)},\n          {mortar_id_nw_sw, lgl_mesh<1>(3)}};\n      const ::dg::MortarMap<2, Mesh<1>> mortar_meshes_ne{\n          {mortar_id_a, lgl_mesh<1>(3)},\n          {mortar_id_ne_nw, lgl_mesh<1>(4)},\n          {mortar_id_c, lgl_mesh<1>(4)},\n          {mortar_id_ne_se, lgl_mesh<1>(3)}};\n      const ::dg::MortarMap<2, Mesh<1>> mortar_meshes_sw{\n          {mortar_id_sw_nw, lgl_mesh<1>(3)},\n          {mortar_id_b, lgl_mesh<1>(4)},\n          {mortar_id_sw_se, lgl_mesh<1>(4)},\n          {mortar_id_d, lgl_mesh<1>(3)}};\n      const ::dg::MortarMap<2, Mesh<1>> mortar_meshes_se{\n          {mortar_id_se_ne, lgl_mesh<1>(3)},\n          {mortar_id_se_sw, lgl_mesh<1>(4)},\n          {mortar_id_c, lgl_mesh<1>(4)},\n          {mortar_id_e, lgl_mesh<1>(5)}};\n      history_nw = make_boundary_histories(\n          mortar_ids_nw, orig_mesh, mortar_meshes_nw, element_size_nw,\n          dummy_mortar_infos(mortar_ids_nw), true, true);\n      history_ne = make_boundary_histories(\n          mortar_ids_ne, orig_mesh, mortar_meshes_ne, element_size_ne,\n          dummy_mortar_infos(mortar_ids_ne), true, true);\n      history_sw = make_boundary_histories(\n          mortar_ids_sw, orig_mesh, mortar_meshes_sw, element_size_sw,\n          dummy_mortar_infos(mortar_ids_sw), true, true);\n      history_se = make_boundary_histories(\n          mortar_ids_se, orig_mesh, mortar_meshes_se, element_size_se,\n          dummy_mortar_infos(mortar_ids_se), true, true);\n    }\n    const std::unordered_map<ElementId<2>, ChildItems> children_items{\n        {id_nw, {temporal_id, std::move(history_nw)}},\n        {id_ne, {temporal_id, std::move(history_ne)}},\n        {id_sw, {temporal_id, std::move(history_sw)}},\n        {id_se, {temporal_id, std::move(history_se)}}};\n\n    db::mutate_apply<\n        evolution::dg::Initialization::ProjectMortars<2, LocalTimeStepping>>(\n        make_not_null(&box), children_items);\n\n    const ::dg::MortarMap<2, Mesh<1>> expected_mortar_meshes{\n        {mortar_id_a, lgl_mesh<1>(4)},\n        {mortar_id_f, lgl_mesh<1>(4)},\n        {mortar_id_g, lgl_mesh<1>(4)},\n        {mortar_id_h, lgl_mesh<1>(4)},\n        {mortar_id_i, lgl_mesh<1>(5)}};\n\n    mortar_data_history_tag::type expected_mortar_data_history{};\n    if (LocalTimeStepping) {\n      for (const auto& mortar_id : refined_mortar_ids) {\n        expected_mortar_data_history.emplace(mortar_id,\n                                             boundary_history_type<2>{});\n      }\n      expected_mortar_data_history[mortar_id_a] = make_boundary_history(\n          mortar_id_a, orig_mesh, expected_mortar_meshes.at(mortar_id_a),\n          make_array<2>(Spectral::SegmentSize::Full),\n          {{Spectral::SegmentSize::Full}}, false, true);\n    }\n\n    CHECK(db::get<Tags::MortarData<2>>(box) == empty_mortar_data(mortar_ids));\n    CHECK(db::get<Tags::MortarMesh<2>>(box) == expected_mortar_meshes);\n    CHECK(db::get<Tags::MortarInfo<2>>(box) == expected_single_mortar_infos);\n    CHECK(db::get<Tags::MortarNextTemporalId<2>>(box) ==\n          constant_next_temporal_ids(mortar_ids, temporal_id));\n    CHECK(db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(box) ==\n          empty_normal_covector_and_magnitude);\n    check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                             expected_mortar_data_history);\n  }\n\n  {\n    INFO(\"Split - nw\");\n    auto box =\n        db::create<tmpl::push_front<decltype(orig_single_items)::tags_list,\n                                    domain::Tags::Domain<2>>>();\n\n    const std::array mortar_ids{mortar_id_a, mortar_id_f, mortar_id_nw_ne,\n                                mortar_id_nw_sw};\n    db::mutate<domain::Tags::Element<2>, domain::Tags::Mesh<2>,\n               domain::Tags::NeighborMesh<2>>(\n        [&](const gsl::not_null<Element<2>*> element,\n            const gsl::not_null<Mesh<2>*> mesh,\n            const gsl::not_null<::dg::MortarMap<2, Mesh<2>>*> neighbor_meshes) {\n          *element = Element<2>(\n              id_nw, {{mortar_id_a.direction(),\n                       Neighbors<2>({mortar_id_a.id()}, rotated)},\n                      {mortar_id_f.direction(),\n                       Neighbors<2>({mortar_id_f.id()}, rotated)},\n                      {mortar_id_nw_ne.direction(),\n                       Neighbors<2>({mortar_id_nw_ne.id()}, aligned)},\n                      {mortar_id_nw_sw.direction(),\n                       Neighbors<2>({mortar_id_nw_sw.id()}, aligned)}});\n          *mesh = orig_mesh;\n          *neighbor_meshes = {\n              {mortar_id_a, refined_single_neighbor_meshes.at(mortar_id_a)},\n              {mortar_id_f, refined_single_neighbor_meshes.at(mortar_id_f)},\n              {mortar_id_nw_ne, orig_mesh},\n              {mortar_id_nw_sw, orig_mesh}};\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<\n        evolution::dg::Initialization::ProjectMortars<2, LocalTimeStepping>>(\n        make_not_null(&box), orig_single_items);\n\n    const ::dg::MortarMap<2, Mesh<1>> expected_mortar_meshes{\n        {mortar_id_a, lgl_mesh<1>(4)},\n        {mortar_id_f, lgl_mesh<1>(4)},\n        {mortar_id_nw_ne, lgl_mesh<1>(4)},\n        {mortar_id_nw_sw, lgl_mesh<1>(3)}};\n\n    const ::dg::MortarMap<2, MortarInfo<2>> expected_mortar_infos{\n        {mortar_id_a,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::OrientCopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_f,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::OrientCopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_nw_ne,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_nw_sw,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}}};\n\n    mortar_data_history_tag::type expected_mortar_data_history{};\n    if (LocalTimeStepping) {\n      for (const auto& mortar_id : mortar_ids) {\n        expected_mortar_data_history.emplace(mortar_id,\n                                             boundary_history_type<2>{});\n      }\n      expected_mortar_data_history[mortar_id_a] = make_boundary_history(\n          mortar_id_a, orig_mesh, expected_mortar_meshes.at(mortar_id_a),\n          element_size_nw, {{Spectral::SegmentSize::Full}}, false, true);\n    }\n\n    CHECK(db::get<Tags::MortarData<2>>(box) == empty_mortar_data(mortar_ids));\n    CHECK(db::get<Tags::MortarMesh<2>>(box) == expected_mortar_meshes);\n    CHECK(db::get<Tags::MortarInfo<2>>(box) == expected_mortar_infos);\n    CHECK(db::get<Tags::MortarNextTemporalId<2>>(box) ==\n          constant_next_temporal_ids(mortar_ids, temporal_id));\n    CHECK(db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(box) ==\n          empty_normal_covector_and_magnitude);\n    check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                             expected_mortar_data_history);\n  }\n\n  {\n    INFO(\"Split - ne\");\n    auto box =\n        db::create<tmpl::push_front<decltype(orig_single_items)::tags_list,\n                                    domain::Tags::Domain<2>>>();\n\n    const std::array mortar_ids{mortar_id_a, mortar_id_ne_nw, mortar_id_g,\n                                mortar_id_ne_se};\n    db::mutate<domain::Tags::Element<2>, domain::Tags::Mesh<2>,\n               domain::Tags::NeighborMesh<2>>(\n        [&](const gsl::not_null<Element<2>*> element,\n            const gsl::not_null<Mesh<2>*> mesh,\n            const gsl::not_null<::dg::MortarMap<2, Mesh<2>>*> neighbor_meshes) {\n          *element = Element<2>(\n              id_ne, {{mortar_id_a.direction(),\n                       Neighbors<2>({mortar_id_a.id()}, rotated)},\n                      {mortar_id_ne_nw.direction(),\n                       Neighbors<2>({mortar_id_ne_nw.id()}, aligned)},\n                      {mortar_id_g.direction(),\n                       Neighbors<2>({mortar_id_g.id()}, aligned)},\n                      {mortar_id_ne_se.direction(),\n                       Neighbors<2>({mortar_id_ne_se.id()}, aligned)}});\n          *mesh = orig_mesh;\n          *neighbor_meshes = {\n              {mortar_id_a, refined_single_neighbor_meshes.at(mortar_id_a)},\n              {mortar_id_ne_nw, orig_mesh},\n              {mortar_id_g, refined_single_neighbor_meshes.at(mortar_id_g)},\n              {mortar_id_ne_se, orig_mesh}};\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<\n        evolution::dg::Initialization::ProjectMortars<2, LocalTimeStepping>>(\n        make_not_null(&box), orig_single_items);\n\n    const ::dg::MortarMap<2, Mesh<1>> expected_mortar_meshes{\n        {mortar_id_a, lgl_mesh<1>(4)},\n        {mortar_id_ne_nw, lgl_mesh<1>(4)},\n        {mortar_id_g, lgl_mesh<1>(4)},\n        {mortar_id_ne_se, lgl_mesh<1>(3)}};\n\n    const ::dg::MortarMap<2, MortarInfo<2>> expected_mortar_infos{\n        {mortar_id_a,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::OrientCopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_ne_nw,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_g,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_ne_se,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}}};\n\n    mortar_data_history_tag::type expected_mortar_data_history{};\n    if (LocalTimeStepping) {\n      for (const auto& mortar_id : mortar_ids) {\n        expected_mortar_data_history.emplace(mortar_id,\n                                             boundary_history_type<2>{});\n      }\n      expected_mortar_data_history[mortar_id_a] = make_boundary_history(\n          mortar_id_a, orig_mesh, expected_mortar_meshes.at(mortar_id_a),\n          element_size_ne, {{Spectral::SegmentSize::Full}}, false, true);\n    }\n\n    CHECK(db::get<Tags::MortarData<2>>(box) == empty_mortar_data(mortar_ids));\n    CHECK(db::get<Tags::MortarMesh<2>>(box) == expected_mortar_meshes);\n    CHECK(db::get<Tags::MortarInfo<2>>(box) == expected_mortar_infos);\n    CHECK(db::get<Tags::MortarNextTemporalId<2>>(box) ==\n          constant_next_temporal_ids(mortar_ids, temporal_id));\n    CHECK(db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(box) ==\n          empty_normal_covector_and_magnitude);\n    check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                             expected_mortar_data_history);\n  }\n\n  {\n    INFO(\"Split - sw\");\n    auto box =\n        db::create<tmpl::push_front<decltype(orig_single_items)::tags_list,\n                                    domain::Tags::Domain<2>>>();\n\n    const std::array mortar_ids{mortar_id_sw_nw, mortar_id_f, mortar_id_sw_se,\n                                mortar_id_i};\n    db::mutate<domain::Tags::Element<2>, domain::Tags::Mesh<2>,\n               domain::Tags::NeighborMesh<2>>(\n        [&](const gsl::not_null<Element<2>*> element,\n            const gsl::not_null<Mesh<2>*> mesh,\n            const gsl::not_null<::dg::MortarMap<2, Mesh<2>>*> neighbor_meshes) {\n          *element = Element<2>(\n              id_sw, {{mortar_id_sw_nw.direction(),\n                       Neighbors<2>({mortar_id_sw_nw.id()}, aligned)},\n                      {mortar_id_f.direction(),\n                       Neighbors<2>({mortar_id_f.id()}, rotated)},\n                      {mortar_id_sw_se.direction(),\n                       Neighbors<2>({mortar_id_sw_se.id()}, aligned)},\n                      {mortar_id_i.direction(),\n                       Neighbors<2>({mortar_id_i.id()}, aligned)}});\n          *mesh = orig_mesh;\n          *neighbor_meshes = {\n              {mortar_id_sw_nw, orig_mesh},\n              {mortar_id_f, refined_single_neighbor_meshes.at(mortar_id_f)},\n              {mortar_id_sw_se, orig_mesh},\n              {mortar_id_i, refined_single_neighbor_meshes.at(mortar_id_i)}};\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<\n        evolution::dg::Initialization::ProjectMortars<2, LocalTimeStepping>>(\n        make_not_null(&box), orig_single_items);\n\n    const ::dg::MortarMap<2, Mesh<1>> expected_mortar_meshes{\n        {mortar_id_sw_nw, lgl_mesh<1>(3)},\n        {mortar_id_f, lgl_mesh<1>(4)},\n        {mortar_id_sw_se, lgl_mesh<1>(4)},\n        {mortar_id_i, lgl_mesh<1>(5)}};\n\n    const ::dg::MortarMap<2, MortarInfo<2>> expected_mortar_infos{\n        {mortar_id_sw_nw,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_f,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::OrientCopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_sw_se,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_i,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}}};\n\n    mortar_data_history_tag::type expected_mortar_data_history{};\n    if (LocalTimeStepping) {\n      for (const auto& mortar_id : mortar_ids) {\n        expected_mortar_data_history.emplace(mortar_id,\n                                             boundary_history_type<2>{});\n      }\n    }\n\n    CHECK(db::get<Tags::MortarData<2>>(box) == empty_mortar_data(mortar_ids));\n    CHECK(db::get<Tags::MortarMesh<2>>(box) == expected_mortar_meshes);\n    CHECK(db::get<Tags::MortarInfo<2>>(box) == expected_mortar_infos);\n    CHECK(db::get<Tags::MortarNextTemporalId<2>>(box) ==\n          constant_next_temporal_ids(mortar_ids, temporal_id));\n    CHECK(db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(box) ==\n          empty_normal_covector_and_magnitude);\n    check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                             expected_mortar_data_history);\n  }\n\n  {\n    INFO(\"Split - se\");\n    auto box =\n        db::create<tmpl::push_front<decltype(orig_single_items)::tags_list,\n                                    domain::Tags::Domain<2>>>();\n\n    const std::array mortar_ids{mortar_id_se_ne, mortar_id_se_sw, mortar_id_h,\n                                mortar_id_i};\n    db::mutate<domain::Tags::Element<2>, domain::Tags::Mesh<2>,\n               domain::Tags::NeighborMesh<2>>(\n        [&](const gsl::not_null<Element<2>*> element,\n            const gsl::not_null<Mesh<2>*> mesh,\n            const gsl::not_null<::dg::MortarMap<2, Mesh<2>>*> neighbor_meshes) {\n          *element = Element<2>(\n              id_se, {{mortar_id_se_ne.direction(),\n                       Neighbors<2>({mortar_id_se_ne.id()}, aligned)},\n                      {mortar_id_se_sw.direction(),\n                       Neighbors<2>({mortar_id_se_sw.id()}, aligned)},\n                      {mortar_id_g.direction(),\n                       Neighbors<2>({mortar_id_h.id()}, aligned)},\n                      {mortar_id_i.direction(),\n                       Neighbors<2>({mortar_id_i.id()}, aligned)}});\n          *mesh = orig_mesh;\n          *neighbor_meshes = {\n              {mortar_id_se_ne, orig_mesh},\n              {mortar_id_se_sw, orig_mesh},\n              {mortar_id_h, refined_single_neighbor_meshes.at(mortar_id_h)},\n              {mortar_id_i, refined_single_neighbor_meshes.at(mortar_id_i)}};\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<\n        evolution::dg::Initialization::ProjectMortars<2, LocalTimeStepping>>(\n        make_not_null(&box), orig_single_items);\n\n    const ::dg::MortarMap<2, Mesh<1>> expected_mortar_meshes{\n        {mortar_id_se_ne, lgl_mesh<1>(3)},\n        {mortar_id_se_sw, lgl_mesh<1>(4)},\n        {mortar_id_h, lgl_mesh<1>(4)},\n        {mortar_id_i, lgl_mesh<1>(5)}};\n\n    const ::dg::MortarMap<2, MortarInfo<2>> expected_mortar_infos{\n        {mortar_id_se_ne,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_se_sw,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_h,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}},\n        {mortar_id_i,\n         MortarInfo<2>{\n             {.mortar_size = {{Spectral::SegmentSize::Full}},\n              .interface_data_policy = InterfaceDataPolicy::CopyProject,\n              .time_stepping_policy = time_stepping_policy}}}};\n\n    mortar_data_history_tag::type expected_mortar_data_history{};\n    if (LocalTimeStepping) {\n      for (const auto& mortar_id : mortar_ids) {\n        expected_mortar_data_history.emplace(mortar_id,\n                                             boundary_history_type<2>{});\n      }\n    }\n\n    CHECK(db::get<Tags::MortarData<2>>(box) == empty_mortar_data(mortar_ids));\n    CHECK(db::get<Tags::MortarMesh<2>>(box) == expected_mortar_meshes);\n    CHECK(db::get<Tags::MortarInfo<2>>(box) == expected_mortar_infos);\n    CHECK(db::get<Tags::MortarNextTemporalId<2>>(box) ==\n          constant_next_temporal_ids(mortar_ids, temporal_id));\n    CHECK(db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<2>>(box) ==\n          empty_normal_covector_and_magnitude);\n    check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                             expected_mortar_data_history);\n  }\n}\n\nvoid test_h_refinement_mortar_sizes_local_impl(\n    const std::vector<SegmentId>& pre_xi, const std::vector<SegmentId>& pre_eta,\n    const std::vector<SegmentId>& post_xi,\n    const std::vector<SegmentId>& post_eta,\n    const OrientationMap<3>& orientation) {\n  using NormalVars =\n      Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                           evolution::dg::Tags::NormalCovector<3>>>;\n  using mortar_data_history_tag = Tags::MortarDataHistory<3>;\n\n  const ElementId<3> self_id(1, {{{1, 0}, {1, 0}, {0, 0}}});\n  const auto direction = Direction<3>::upper_zeta();\n  const Mesh<3> mesh = lgl_mesh<3>(4);\n  const Mesh<2> mortar_mesh = lgl_mesh<2>(4);\n  const TimeStepId time_step_id(true, 5, Slab(1.2, 3.4).start());\n\n  // Pre-refinement data\n  ::dg::MortarMap<3, evolution::dg::MortarDataHolder<3>> mortar_data{};\n  ::dg::MortarMap<3, Mesh<2>> mortar_meshes{};\n  ::dg::MortarMap<3, MortarInfo<3>> mortar_infos{};\n  ::dg::MortarMap<3, TimeStepId> mortar_next_temporal_ids{};\n  // NOLINTNEXTLINE(misc-const-correctness) - false positive - object is moved\n  DirectionMap<3, std::optional<NormalVars>> normal_covector_and_magnitude{\n      {direction, std::nullopt}};\n  mortar_data_history_tag::type mortar_data_history{};\n  std::unordered_set<ElementId<3>> old_neighbors{};\n  for (const auto& segment_xi : pre_xi) {\n    for (const auto& segment_eta : pre_eta) {\n      const ElementId<3> neighbor(\n          2, orientation(std::array{segment_xi, segment_eta, SegmentId{0, 0}}));\n      const DirectionalId mortar_id{direction, neighbor};\n      const auto mortar_size =\n          ::dg::mortar_size(self_id, neighbor, 2, orientation);\n      old_neighbors.emplace(neighbor);\n      mortar_data.emplace(mortar_id, evolution::dg::MortarDataHolder<3>{});\n      mortar_meshes.emplace(mortar_id, mortar_mesh);\n      mortar_infos.emplace(\n          mortar_id,\n          MortarInfo<3>{\n              {.mortar_size = mortar_size,\n               .interface_data_policy =\n                   orientation.is_aligned()\n                       ? InterfaceDataPolicy::CopyProject\n                       : InterfaceDataPolicy::OrientCopyProject,\n               .time_stepping_policy = TimeSteppingPolicy::Conservative}});\n      mortar_next_temporal_ids.emplace(mortar_id, time_step_id);\n      mortar_data_history.emplace(\n          mortar_id,\n          make_boundary_history(mortar_id, mesh, mortar_mesh,\n                                make_array<3>(Spectral::SegmentSize::Full),\n                                mortar_size, true, true));\n    }\n  }\n  const Element<3> old_element(\n      self_id,\n      {{direction, Neighbors<3>(std::move(old_neighbors), orientation)}});\n\n  // Post-refinement data\n  std::unordered_set<ElementId<3>> neighbors{};\n  ::dg::MortarMap<3, Mesh<3>> neighbor_meshes{};\n  mortar_data_history_tag::type expected_mortar_data_history{};\n  for (const auto& segment_xi : post_xi) {\n    for (const auto& segment_eta : post_eta) {\n      const ElementId<3> neighbor(\n          2, orientation(std::array{segment_xi, segment_eta, SegmentId{0, 0}}));\n      const DirectionalId mortar_id{direction, neighbor};\n      const auto mortar_size =\n          ::dg::mortar_size(self_id, neighbor, 2, orientation);\n      neighbors.emplace(neighbor);\n      neighbor_meshes.emplace(mortar_id, mesh);\n      expected_mortar_data_history.emplace(\n          mortar_id,\n          make_boundary_history(mortar_id, mesh, mortar_mesh,\n                                make_array<3>(Spectral::SegmentSize::Full),\n                                mortar_size, true,\n                                pre_xi == post_xi and pre_eta == post_eta));\n    }\n  }\n  // NOLINTNEXTLINE(misc-const-correctness) - false positive - object is moved\n  Element<3> element(\n      self_id, {{direction, Neighbors<3>(std::move(neighbors), orientation)}});\n\n  auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Domain<3>, Tags::MortarData<3>, Tags::MortarMesh<3>,\n      Tags::MortarInfo<3>, Tags::MortarNextTemporalId<3>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<3>,\n      mortar_data_history_tag, domain::Tags::Mesh<3>, domain::Tags::Element<3>,\n      domain::Tags::NeighborMesh<3>, ::Tags::TimeStepId>>(\n      Domain<3>{}, std::move(mortar_data), std::move(mortar_meshes),\n      std::move(mortar_infos), std::move(mortar_next_temporal_ids),\n      std::move(normal_covector_and_magnitude), std::move(mortar_data_history),\n      mesh, std::move(element), std::move(neighbor_meshes), time_step_id);\n\n  db::mutate_apply<evolution::dg::Initialization::ProjectMortars<3, true>>(\n      make_not_null(&box), std::pair(mesh, old_element));\n\n  check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                           expected_mortar_data_history);\n}\n\n// Test projections of local mortar data in different 3D mortar configurations.\nvoid test_h_refinement_mortar_sizes_local() {\n  // In each tangential dimension, neighbors can change (or not) in\n  // five ways, left-to-right for splitting, right-to-left for joining:\n  const std::vector<std::pair<std::vector<SegmentId>, std::vector<SegmentId>>>\n      dimension_cases{{{{0, 0}}, {{0, 0}}},\n                      {{{0, 0}}, {{1, 0}}},\n                      {{{1, 0}}, {{1, 0}}},\n                      {{{1, 0}}, {{2, 0}, {2, 1}}},\n                      {{{2, 0}, {2, 1}}, {{2, 0}, {2, 1}}}};\n  const std::vector<OrientationMap<3>> orientations{\n      OrientationMap<3>::create_aligned(),\n      OrientationMap<3>(std::array{Direction<3>::lower_xi(),\n                                   Direction<3>::lower_eta(),\n                                   Direction<3>::upper_zeta()}),\n      OrientationMap<3>(std::array{Direction<3>::upper_xi(),\n                                   Direction<3>::lower_eta(),\n                                   Direction<3>::lower_zeta()}),\n      OrientationMap<3>(std::array{Direction<3>::upper_eta(),\n                                   Direction<3>::upper_zeta(),\n                                   Direction<3>::upper_xi()})};\n\n  for (const auto& [large_xi, small_xi] : dimension_cases) {\n    for (const auto& [large_eta, small_eta] : dimension_cases) {\n      for (const auto& orientation : orientations) {\n        // Split\n        test_h_refinement_mortar_sizes_local_impl(large_xi, large_eta, small_xi,\n                                                  small_eta, orientation);\n        // Join\n        test_h_refinement_mortar_sizes_local_impl(small_xi, small_eta, large_xi,\n                                                  large_eta, orientation);\n      }\n    }\n  }\n}\n\nvoid test_h_refinement_mortar_sizes_remote_impl_split(\n    const SegmentId& pre_xi, const SegmentId& pre_eta, const SegmentId& post_xi,\n    const SegmentId& post_eta, const OrientationMap<3>& orientation) {\n  using mortar_data_history_tag = Tags::MortarDataHistory<3>;\n\n  const auto direction = Direction<3>::upper_zeta();\n  const Mesh<3> mesh = lgl_mesh<3>(4);\n  const Mesh<2> mortar_mesh = lgl_mesh<2>(4);\n  const TimeStepId time_step_id(true, 5, Slab(1.2, 3.4).start());\n\n  const std::array neighbor_segments{SegmentId{1, 0}, SegmentId{1, 1}};\n\n  const ElementId<3> parent_id(1, {{pre_xi, pre_eta, {0, 0}}});\n  const ElementId<3> self_id(1, {{post_xi, post_eta, {0, 0}}});\n  CAPTURE(parent_id);\n  CAPTURE(self_id);\n\n  // Pre-refinement data\n  mortar_data_history_tag::type parent_mortar_data_history{};\n  std::unordered_set<ElementId<3>> parent_neighbors{};\n  for (const auto& segment_xi : neighbor_segments) {\n    if (not overlapping(segment_xi, pre_xi)) {\n      continue;\n    }\n    for (const auto& segment_eta : neighbor_segments) {\n      if (not overlapping(segment_eta, pre_eta)) {\n        continue;\n      }\n      const ElementId<3> neighbor(\n          2, orientation(std::array{segment_xi, segment_eta, SegmentId{0, 0}}));\n      const DirectionalId mortar_id{direction, neighbor};\n      const auto mortar_size =\n          ::dg::mortar_size(parent_id, neighbor, 2, orientation);\n      parent_neighbors.emplace(neighbor);\n      parent_mortar_data_history.emplace(\n          mortar_id,\n          make_boundary_history(mortar_id, mesh, mortar_mesh,\n                                make_array<3>(Spectral::SegmentSize::Full),\n                                mortar_size, true, true));\n    }\n  }\n  Element<3> parent_element(\n      parent_id,\n      {{direction, Neighbors<3>(std::move(parent_neighbors), orientation)}});\n\n  const tuples::TaggedTuple<domain::Tags::Element<3>, ::Tags::TimeStepId,\n                            mortar_data_history_tag>\n      parent_items{std::move(parent_element), time_step_id,\n                   std::move(parent_mortar_data_history)};\n\n  // Post-refinement data\n  std::unordered_set<ElementId<3>> neighbors{};\n  ::dg::MortarMap<3, Mesh<3>> neighbor_meshes{};\n  mortar_data_history_tag::type expected_mortar_data_history{};\n  for (const auto& segment_xi : neighbor_segments) {\n    if (not overlapping(segment_xi, post_xi)) {\n      continue;\n    }\n    for (const auto& segment_eta : neighbor_segments) {\n      if (not overlapping(segment_eta, post_eta)) {\n        continue;\n      }\n      const ElementId<3> neighbor(\n          2, orientation(std::array{segment_xi, segment_eta, SegmentId{0, 0}}));\n      const DirectionalId mortar_id{direction, neighbor};\n      const auto element_size =\n          domain::child_size(self_id.segment_ids(), parent_id.segment_ids());\n      const auto mortar_size =\n          ::dg::mortar_size(self_id, neighbor, 2, orientation);\n      neighbors.emplace(neighbor);\n      neighbor_meshes.emplace(mortar_id, mesh);\n      expected_mortar_data_history.emplace(\n          mortar_id,\n          make_boundary_history(mortar_id, mesh, mortar_mesh, element_size,\n                                mortar_size, false, true));\n    }\n  }\n  // NOLINTNEXTLINE(misc-const-correctness) - false positive - object is moved\n  Element<3> element(\n      self_id, {{direction, Neighbors<3>(std::move(neighbors), orientation)}});\n\n  auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Domain<3>, Tags::MortarData<3>, Tags::MortarMesh<3>,\n      Tags::MortarInfo<3>, Tags::MortarNextTemporalId<3>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<3>,\n      mortar_data_history_tag, domain::Tags::Mesh<3>, domain::Tags::Element<3>,\n      domain::Tags::NeighborMesh<3>, ::Tags::TimeStepId>>(\n      Domain<3>{}, Tags::MortarData<3>::type{}, Tags::MortarMesh<3>::type{},\n      Tags::MortarInfo<3>::type{}, Tags::MortarNextTemporalId<3>::type{},\n      evolution::dg::Tags::NormalCovectorAndMagnitude<3>::type{},\n      mortar_data_history_tag::type{}, mesh, std::move(element),\n      std::move(neighbor_meshes), time_step_id);\n\n  db::mutate_apply<evolution::dg::Initialization::ProjectMortars<3, true>>(\n      make_not_null(&box), parent_items);\n\n  check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                           expected_mortar_data_history);\n}\n\nvoid test_h_refinement_mortar_sizes_remote_impl_join(\n    const std::vector<SegmentId>& pre_xi, const std::vector<SegmentId>& pre_eta,\n    const SegmentId& post_xi, const SegmentId& post_eta,\n    const OrientationMap<3>& orientation) {\n  using mortar_data_history_tag = Tags::MortarDataHistory<3>;\n\n  const auto direction = Direction<3>::upper_zeta();\n  const Mesh<3> mesh = lgl_mesh<3>(4);\n  const Mesh<2> mortar_mesh = lgl_mesh<2>(4);\n  const TimeStepId time_step_id(true, 5, Slab(1.2, 3.4).start());\n\n  const std::array neighbor_segments{SegmentId{1, 0}, SegmentId{1, 1}};\n\n  const ElementId<3> self_id(1, {{post_xi, post_eta, {0, 0}}});\n\n  // Pre-refinement data\n  using ChildItems =\n      tuples::TaggedTuple<::Tags::TimeStepId, mortar_data_history_tag>;\n  std::unordered_map<ElementId<3>, ChildItems> children_items{};\n  for (const auto& segment_xi : pre_xi) {\n    for (const auto& segment_eta : pre_eta) {\n      const ElementId<3> child_id(1, {{segment_xi, segment_eta, {0, 0}}});\n      const auto child_size =\n          domain::child_size(child_id.segment_ids(), self_id.segment_ids());\n      mortar_data_history_tag::type mortar_data_history{};\n      for (const auto& neighbor_xi : neighbor_segments) {\n        if (not overlapping(neighbor_xi, segment_xi)) {\n          continue;\n        }\n        for (const auto& neighbor_eta : neighbor_segments) {\n          if (not overlapping(neighbor_eta, segment_eta)) {\n            continue;\n          }\n          const ElementId<3> neighbor(\n              2, orientation(\n                     std::array{neighbor_xi, neighbor_eta, SegmentId{0, 0}}));\n          const DirectionalId mortar_id{direction, neighbor};\n          const auto mortar_size =\n              ::dg::mortar_size(child_id, neighbor, 2, orientation);\n          mortar_data_history.emplace(\n              mortar_id,\n              make_boundary_history(mortar_id, mesh, mortar_mesh, child_size,\n                                    mortar_size, true, true));\n        }\n      }\n      children_items.emplace(\n          child_id, ChildItems{time_step_id, std::move(mortar_data_history)});\n    }\n  }\n\n  // Post-refinement data\n  std::unordered_set<ElementId<3>> neighbors{};\n  ::dg::MortarMap<3, Mesh<3>> neighbor_meshes{};\n  mortar_data_history_tag::type expected_mortar_data_history{};\n  for (const auto& segment_xi : neighbor_segments) {\n    if (not overlapping(segment_xi, post_xi)) {\n      continue;\n    }\n    for (const auto& segment_eta : neighbor_segments) {\n      if (not overlapping(segment_eta, post_eta)) {\n        continue;\n      }\n      const ElementId<3> neighbor(\n          2, orientation(std::array{segment_xi, segment_eta, SegmentId{0, 0}}));\n      const DirectionalId mortar_id{direction, neighbor};\n      const auto mortar_size =\n          ::dg::mortar_size(self_id, neighbor, 2, orientation);\n      neighbors.emplace(neighbor);\n      neighbor_meshes.emplace(mortar_id, mesh);\n      expected_mortar_data_history.emplace(\n          mortar_id,\n          make_boundary_history(mortar_id, mesh, mortar_mesh,\n                                make_array<3>(Spectral::SegmentSize::Full),\n                                mortar_size, false, true));\n    }\n  }\n  // NOLINTNEXTLINE(misc-const-correctness) - false positive - object is moved\n  Element<3> element(\n      self_id, {{direction, Neighbors<3>(std::move(neighbors), orientation)}});\n\n  auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Domain<3>, Tags::MortarData<3>, Tags::MortarMesh<3>,\n      Tags::MortarInfo<3>, Tags::MortarNextTemporalId<3>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<3>,\n      mortar_data_history_tag, domain::Tags::Mesh<3>, domain::Tags::Element<3>,\n      domain::Tags::NeighborMesh<3>, ::Tags::TimeStepId>>(\n      Domain<3>{}, Tags::MortarData<3>::type{}, Tags::MortarMesh<3>::type{},\n      Tags::MortarInfo<3>::type{}, Tags::MortarNextTemporalId<3>::type{},\n      evolution::dg::Tags::NormalCovectorAndMagnitude<3>::type{},\n      mortar_data_history_tag::type{}, mesh, std::move(element),\n      std::move(neighbor_meshes), time_step_id);\n\n  db::mutate_apply<evolution::dg::Initialization::ProjectMortars<3, true>>(\n      make_not_null(&box), children_items);\n\n  check_boundary_histories(db::get<mortar_data_history_tag>(box),\n                           expected_mortar_data_history);\n}\n\n// Test projections of remote data in different 3D mortar configurations.\nvoid test_h_refinement_mortar_sizes_remote() {\n  // In each tangential dimension, our element can change (or not) in\n  // five ways, left-to-right for splitting, right-to-left for\n  // joining.  In each case, we will just check the mortar with the\n  // element with xi-eta segments {{1, 0}, {1, 0}}.\n  const std::vector<std::pair<SegmentId, std::vector<SegmentId>>>\n      dimension_cases{{{0, 0}, {{0, 0}}},\n                      {{0, 0}, {{1, 0}, {1, 1}}},\n                      {{1, 0}, {{1, 0}}},\n                      {{1, 0}, {{2, 0}, {2, 1}}},\n                      {{2, 0}, {{2, 0}}}};\n  const std::vector<OrientationMap<3>> orientations{\n      OrientationMap<3>::create_aligned(),\n      OrientationMap<3>(std::array{Direction<3>::lower_xi(),\n                                   Direction<3>::lower_eta(),\n                                   Direction<3>::upper_zeta()}),\n      OrientationMap<3>(std::array{Direction<3>::upper_xi(),\n                                   Direction<3>::lower_eta(),\n                                   Direction<3>::lower_zeta()}),\n      OrientationMap<3>(std::array{Direction<3>::upper_eta(),\n                                   Direction<3>::upper_zeta(),\n                                   Direction<3>::upper_xi()})};\n\n  for (const auto& [large_xi, small_xis] : dimension_cases) {\n    for (const auto& [large_eta, small_etas] : dimension_cases) {\n      if (small_xis.size() == 1 and small_etas.size() == 1) {\n        continue;\n      }\n      for (const auto& orientation : orientations) {\n        test_h_refinement_mortar_sizes_remote_impl_split(\n            large_xi, large_eta, small_xis.front(), small_etas.front(),\n            orientation);\n        test_h_refinement_mortar_sizes_remote_impl_join(\n            small_xis, small_etas, large_xi, large_eta, orientation);\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Initialization.Mortars\",\n                  \"[Unit][Evolution]\") {\n  for (const auto quadrature :\n       {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}) {\n    Test<1, true>::apply(quadrature);\n    Test<2, true>::apply(quadrature);\n    Test<3, true>::apply(quadrature);\n\n    Test<1, false>::apply(quadrature);\n    Test<2, false>::apply(quadrature);\n    Test<3, false>::apply(quadrature);\n  }\n\n  domain::creators::register_derived_with_charm();\n  test_nonconforming_blocks<false>();\n\n  static_assert(tt::assert_conforms_to_v<\n                evolution::dg::Initialization::ProjectMortars<1, false>,\n                amr::protocols::Projector>);\n  test_p_refine_gts<1>();\n  test_p_refine_gts<2>();\n  test_p_refine_gts<3>();\n  test_p_refine_lts<1>();\n  test_p_refine_lts<2>();\n  test_p_refine_lts<3>();\n  test_h_refinement<false>();\n  test_h_refinement<true>();\n  test_h_refinement_mortar_sizes_local();\n  test_h_refinement_mortar_sizes_remote();\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Initialization/Test_QuadratureTag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Initialization.QuadratureTag\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<evolution::dg::Tags::Quadrature>(\n      \"Quadrature\");\n  CHECK(TestHelpers::test_option_tag<evolution::dg::OptionTags::Quadrature>(\n            \"Gauss\") == Spectral::Quadrature::Gauss);\n  CHECK(TestHelpers::test_option_tag<evolution::dg::OptionTags::Quadrature>(\n            \"GaussLobatto\") == Spectral::Quadrature::GaussLobatto);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Limiters\")\n\nset(LIBRARY_SOURCES\n  Test_HwenoImpl.cpp\n  Test_Krivodonova.cpp\n  Test_LimiterActions.cpp\n  Test_LimiterActionsWithMinmod.cpp\n  Test_Minmod.cpp\n  Test_MinmodTci.cpp\n  Test_MinmodType.cpp\n  Test_SimpleWenoImpl.cpp\n  Test_Tags.cpp\n  Test_Weno.cpp\n  Test_WenoGridHelpers.cpp\n  Test_WenoOscillationIndicator.cpp\n  Test_WenoHelpers.cpp\n  Test_WenoType.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Actions\n  Boost::boost\n  Limiters\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_HwenoImpl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"Scalar\"; }\n};\n\ntemplate <size_t VolumeDim>\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, VolumeDim>;\n  static std::string name() { return \"Vector\"; }\n};\n\nvoid test_secondary_neighbors_to_exclude_from_fit() {\n  INFO(\"Testing Weno_detail::secondary_neighbors_to_exclude_from_fit\");\n  struct DummyPackage {\n    tuples::TaggedTuple<::Tags::Mean<ScalarTag>> means;\n  };\n  std::unordered_map<DirectionalId<2>, DummyPackage,\n                     boost::hash<DirectionalId<2>>>\n      dummy_neighbor_data;\n\n  const DirectionalId<2> lower_xi{Direction<2>::lower_xi(), ElementId<2>{1}};\n  const DirectionalId<2> upper_xi{Direction<2>::upper_xi(), ElementId<2>{2}};\n  const DirectionalId<2> lower_eta{Direction<2>::lower_eta(), ElementId<2>{3}};\n  const DirectionalId<2> upper_eta{Direction<2>::upper_eta(), ElementId<2>{4}};\n\n  get(get<::Tags::Mean<ScalarTag>>(dummy_neighbor_data[lower_xi].means)) = 1.;\n  get(get<::Tags::Mean<ScalarTag>>(dummy_neighbor_data[upper_xi].means)) = 2.;\n  get(get<::Tags::Mean<ScalarTag>>(dummy_neighbor_data[lower_eta].means)) = 3.;\n  get(get<::Tags::Mean<ScalarTag>>(dummy_neighbor_data[upper_eta].means)) = 3.;\n\n  const auto check_excluded_neighbors =\n      [&dummy_neighbor_data](\n          const double mean, const DirectionalId<2>& primary_neighbor,\n          const std::unordered_set<DirectionalId<2>,\n                                   boost::hash<DirectionalId<2>>>&\n              expected_excluded_neighbors) {\n        const size_t tensor_index = 0;\n        const auto excluded_neighbors_vector =\n            Limiters::Weno_detail::secondary_neighbors_to_exclude_from_fit<\n                ScalarTag>(mean, tensor_index, dummy_neighbor_data,\n                           primary_neighbor);\n        // The elements of `excluded_neighbors_vector` are ordered in an\n        // undefined way, because they are filled by looping over the\n        // unordered_map of neighbor data. To provide meaningful test\n        // comparisons, we move the data into an unordered_set. (A sort would\n        // also work here, if the Direction and ElementId classes were sortable,\n        // which they aren't.)\n        const std::unordered_set<DirectionalId<2>,\n                                 boost::hash<DirectionalId<2>>>\n            excluded_neighbors(excluded_neighbors_vector.begin(),\n                               excluded_neighbors_vector.end());\n        CHECK(excluded_neighbors == expected_excluded_neighbors);\n      };\n\n  check_excluded_neighbors(0., lower_xi, {{lower_eta, upper_eta}});\n  check_excluded_neighbors(0., upper_xi, {{lower_eta, upper_eta}});\n  check_excluded_neighbors(0., lower_eta, {{upper_eta}});\n  check_excluded_neighbors(0., upper_eta, {{lower_eta}});\n  check_excluded_neighbors(3., lower_xi, {{upper_xi}});\n  check_excluded_neighbors(3., upper_xi, {{lower_xi}});\n  check_excluded_neighbors(3., lower_eta, {{lower_xi}});\n  check_excluded_neighbors(3., upper_eta, {{lower_xi}});\n  check_excluded_neighbors(4., lower_xi, {{upper_xi}});\n  check_excluded_neighbors(4., upper_xi, {{lower_xi}});\n  check_excluded_neighbors(4., lower_eta, {{lower_xi}});\n  check_excluded_neighbors(4., upper_eta, {{lower_xi}});\n}\n\nvoid test_constrained_fit_1d(const Spectral::Quadrature quadrature =\n                                 Spectral::Quadrature::GaussLobatto) {\n  INFO(\"Testing Weno_detail::solve_constrained_fit in 1D\");\n  CAPTURE(quadrature);\n  using TagsList = tmpl::list<ScalarTag>;\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const auto mesh = Mesh<1>{{{3}}, Spectral::Basis::Legendre, quadrature};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  const DirectionalId<1> lower_xi_neighbor{Direction<1>::lower_xi(),\n                                           ElementId<1>{1}};\n  const DirectionalId<1> upper_xi_neighbor{Direction<1>::upper_xi(),\n                                           ElementId<1>{2}};\n\n  const auto local_data = [&logical_coords]() {\n    const auto& x = get<0>(logical_coords);\n    return DataVector{1. - 0.2 * x + 0.4 * square(x)};\n  }();\n\n  const auto lower_xi_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto x = get<0>(logical_coords) - 2.;\n    get(get<ScalarTag>(result)) = 4. - 0.5 * x - 0.1 * square(x);\n    return result;\n  }();\n\n  const auto upper_xi_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto x = get<0>(logical_coords) + 2.;\n    get(get<ScalarTag>(result)) = 1. - 0.2 * x + 0.1 * square(x);\n    return result;\n  }();\n\n  const auto make_tuple_of_means =\n      [&mesh](const Variables<TagsList>& vars) {\n        return tuples::TaggedTuple<::Tags::Mean<ScalarTag>>(\n            mean_value(get(get<ScalarTag>(vars)), mesh));\n      };\n\n  struct PackagedData {\n    tuples::TaggedTuple<::Tags::Mean<ScalarTag>> means;\n    Variables<TagsList> volume_data;\n    Mesh<1> mesh;\n  };\n  std::unordered_map<DirectionalId<1>, PackagedData,\n                     boost::hash<DirectionalId<1>>>\n      neighbor_data{};\n  neighbor_data[lower_xi_neighbor].means = make_tuple_of_means(lower_xi_vars);\n  neighbor_data[lower_xi_neighbor].volume_data = lower_xi_vars;\n  neighbor_data[lower_xi_neighbor].mesh = mesh;\n  neighbor_data[upper_xi_neighbor].means = make_tuple_of_means(upper_xi_vars);\n  neighbor_data[upper_xi_neighbor].volume_data = upper_xi_vars;\n  neighbor_data[upper_xi_neighbor].mesh = mesh;\n\n  // primary = lower_xi\n  // excluded = upper_xi\n  {\n    INFO(\"One excluded neighbor\");\n    const auto primary_neighbor = lower_xi_neighbor;\n    const std::vector<DirectionalId<1>> neighbors_to_exclude = {\n        upper_xi_neighbor};\n\n    DataVector constrained_fit;\n    Limiters::Weno_detail::solve_constrained_fit<ScalarTag>(\n        make_not_null(&constrained_fit), local_data, 0, mesh, element,\n        neighbor_data, primary_neighbor, neighbors_to_exclude);\n\n    // The expected coefficient values for the result of the constrained fit are\n    // found using Mathematica, using the following code (for Mathematica v10):\n    // qw3 = {1/3, 4/3, 1/3};  (* or Gauss point equivalent *)\n    // qx3 = {-1, 0, 1};       (* or Gauss point equivalent *)\n    // quad3[f_, dx_] := Sum[qw3[[qi]] f[qx3[[qi]] + dx], {qi, 1, 3}];\n    // trial[c0_, c1_, c2_][x_] := c0 + c1 x + c2 x^2;\n    // uLocal[x_] := 1 - 1/5 x + 2/5 x^2;\n    // uPrimary[x_] := 4 - 1/2 x - 1/10 x^2;\n    // primaryOptimizationTerm[c0_, c1_, c2_][x_] :=\n    //     (trial[c0, c1, c2][x] - uPrimary[x])^2;\n    // Minimize[\n    //     quad3[primaryOptimizationTerm[c0, c1, c2], -2],\n    //     quad3[trial[c0, c1, c2], 0] == quad3[uLocal, 0],\n    // {c0, c1, c2}\n    // ]\n    const auto expected = [&quadrature, &logical_coords]() {\n      const auto& x = get<0>(logical_coords);\n      const auto c =\n          (quadrature == Spectral::Quadrature::GaussLobatto)\n              ? std::array<double, 3>{{41. / 30., -31. / 10., -7. / 10.}}\n              : std::array<double, 3>{\n                    {803. / 579., -1247. / 386., -734. / 965.}};\n      return DataVector{c[0] + c[1] * x + c[2] * square(x)};\n    }();\n\n    // Fit procedure has somewhat larger error scale than default\n    // Error further increases when using Gauss points\n    const double gauss_tol =\n        (quadrature == Spectral::Quadrature::Gauss ? 10. : 1.);\n    Approx local_approx = Approx::custom().epsilon(gauss_tol * 1e-11).scale(1.);\n    CHECK_ITERABLE_CUSTOM_APPROX(constrained_fit, expected, local_approx);\n    // Verify that the constraint is in fact satisfied\n    CHECK(mean_value(constrained_fit, mesh) ==\n          local_approx(mean_value(local_data, mesh)));\n  }\n\n  // external = lower_xi\n  // primary = upper_xi\n  // excluded = {}\n  {\n    INFO(\"One external neighbor on lower_xi side\");\n    const Element<1> element_at_lower_xi_bdry{\n        ElementId<1>{0},\n        Element<1>::Neighbors_t{\n            // lower_xi is external boundary\n            {Direction<1>::upper_xi(),\n             TestHelpers::Limiters::make_neighbor_with_id<1>(1)}}};\n    auto neighbor_data_at_lower_xi_bdry = neighbor_data;\n    neighbor_data_at_lower_xi_bdry.erase(lower_xi_neighbor);\n\n    const auto primary_neighbor = upper_xi_neighbor;\n    const std::vector<DirectionalId<1>> neighbors_to_exclude = {};\n\n    DataVector constrained_fit;\n    Limiters::Weno_detail::solve_constrained_fit<ScalarTag>(\n        make_not_null(&constrained_fit), local_data, 0, mesh,\n        element_at_lower_xi_bdry, neighbor_data_at_lower_xi_bdry,\n        primary_neighbor, neighbors_to_exclude);\n\n    // Coefficients from Mathematica, using code similar to the one above.\n    const auto expected = [&quadrature, &logical_coords]() {\n      const auto& x = get<0>(logical_coords);\n      const auto c =\n          (quadrature == Spectral::Quadrature::GaussLobatto)\n              ? std::array<double, 3>{{929. / 850., -124. / 425., 103. / 850.}}\n              : std::array<double, 3>{\n                    {1054. / 965., -286. / 965., 119. / 965.}};\n      return DataVector{c[0] + c[1] * x + c[2] * square(x)};\n    }();\n\n    // Fit procedure has somewhat larger error scale than default\n    // Error further increases when using Gauss points\n    const double gauss_tol =\n        (quadrature == Spectral::Quadrature::Gauss ? 10. : 1.);\n    Approx local_approx = Approx::custom().epsilon(gauss_tol * 1e-11).scale(1.);\n    CHECK_ITERABLE_CUSTOM_APPROX(constrained_fit, expected, local_approx);\n    CHECK(mean_value(constrained_fit, mesh) ==\n          local_approx(mean_value(local_data, mesh)));\n  }\n\n  // external = upper_xi\n  // primary = lower_xi\n  // excluded = {}\n  {\n    INFO(\"One external neighbor on upper_xi side\");\n    const Element<1> element_at_upper_xi_bdry{\n        ElementId<1>{0},\n        Element<1>::Neighbors_t{\n            {Direction<1>::lower_xi(),\n             TestHelpers::Limiters::make_neighbor_with_id<1>(1)}\n            // upper_xi is external boundary\n        }};\n    auto neighbor_data_at_upper_xi_bdry = neighbor_data;\n    neighbor_data_at_upper_xi_bdry.erase(upper_xi_neighbor);\n\n    const auto primary_neighbor = lower_xi_neighbor;\n    const std::vector<DirectionalId<1>> neighbors_to_exclude = {};\n\n    DataVector constrained_fit;\n    Limiters::Weno_detail::solve_constrained_fit<ScalarTag>(\n        make_not_null(&constrained_fit), local_data, 0, mesh,\n        element_at_upper_xi_bdry, neighbor_data_at_upper_xi_bdry,\n        primary_neighbor, neighbors_to_exclude);\n\n    // This test case should produce the same fit as the first test case above,\n    // because in both cases the lower_xi neighbor is NOT part of the fit.\n    // In the earlier test case, the lower_xi neighbor was in the domain but was\n    // excluded from the fit; here, it is not even in the domain. So while this\n    // test is not an orthogonal test of the fitting itself, it is a useful test\n    // of the caching mechanism in the corner case of having a single\n    // neighboring element.\n    const auto expected = [&quadrature, &logical_coords]() {\n      const auto& x = get<0>(logical_coords);\n      const auto c =\n          (quadrature == Spectral::Quadrature::GaussLobatto)\n              ? std::array<double, 3>{{41. / 30., -31. / 10., -7. / 10.}}\n              : std::array<double, 3>{\n                    {803. / 579., -1247. / 386., -734. / 965.}};\n      return DataVector{c[0] + c[1] * x + c[2] * square(x)};\n    }();\n\n    // Fit procedure has somewhat larger error scale than default\n    // Error further increases when using Gauss points\n    const double gauss_tol =\n        (quadrature == Spectral::Quadrature::Gauss ? 10. : 1.);\n    Approx local_approx = Approx::custom().epsilon(gauss_tol * 1e-11).scale(1.);\n    CHECK_ITERABLE_CUSTOM_APPROX(constrained_fit, expected, local_approx);\n    CHECK(mean_value(constrained_fit, mesh) ==\n          local_approx(mean_value(local_data, mesh)));\n  }\n}\n\n// Test in 2D using a vector tensor, to test multiple components.\n// Multiple components becomes very tedious in 3D, so 3D will test a scalar.\nvoid test_constrained_fit_2d_vector(const Spectral::Quadrature quadrature =\n                                        Spectral::Quadrature::GaussLobatto) {\n  INFO(\"Testing Weno_detail::solve_constrained_fit in 2D\");\n  CAPTURE(quadrature);\n  using TagsList = tmpl::list<VectorTag<2>>;\n  const auto element = TestHelpers::Limiters::make_element<2>();\n  const auto mesh = Mesh<2>{{{4, 3}}, Spectral::Basis::Legendre, quadrature};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  const DirectionalId<2> lower_xi_neighbor{Direction<2>::lower_xi(),\n                                           ElementId<2>{1}};\n  const DirectionalId<2> upper_xi_neighbor{Direction<2>::upper_xi(),\n                                           ElementId<2>{2}};\n  const DirectionalId<2> lower_eta_neighbor{Direction<2>::lower_eta(),\n                                            ElementId<2>{3}};\n  const DirectionalId<2> upper_eta_neighbor{Direction<2>::upper_eta(),\n                                            ElementId<2>{4}};\n\n  const auto local_tensor = [&logical_coords]() {\n    const auto& x = get<0>(logical_coords);\n    const auto& y = get<1>(logical_coords);\n    return VectorTag<2>::type{{{DataVector{1. + 0.1 * x + 0.2 * y +\n                                           0.1 * x * y + 0.1 * x * square(y)},\n                                DataVector(x.size(), 2.)}}};\n  }();\n\n  const auto lower_xi_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto x = get<0>(logical_coords) - 2.;\n    const auto& y = get<1>(logical_coords);\n    get<0>(get<VectorTag<2>>(result)) = 2. + 0.2 * x - 0.1 * y;\n    get<1>(get<VectorTag<2>>(result)) = 1.;\n    return result;\n  }();\n\n  const auto upper_xi_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto x = get<0>(logical_coords) + 2.;\n    const auto& y = get<1>(logical_coords);\n    get<0>(get<VectorTag<2>>(result)) =\n        1. + 1. / 3. * x + 0.25 * y - 0.05 * square(x);\n    get<1>(get<VectorTag<2>>(result)) = -0.5;\n    return result;\n  }();\n\n  const auto lower_eta_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto& x = get<0>(logical_coords);\n    const auto y = get<1>(logical_coords) - 2.;\n    get<0>(get<VectorTag<2>>(result)) =\n        1. + 0.25 * x - 0.2 * square(y) + 0.1 * x * square(y);\n    get<1>(get<VectorTag<2>>(result)) =\n        1.2 + 0.5 * x - 0.1 * square(x) - 0.2 * y + 0.1 * square(x) * square(y);\n    return result;\n  }();\n\n  const auto upper_eta_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto& x = get<0>(logical_coords);\n    const auto y = get<1>(logical_coords) + 2.;\n    get<0>(get<VectorTag<2>>(result)) = 1. + 1. / 3. * x + 0.2 * y;\n    get<1>(get<VectorTag<2>>(result)) = 0.1;\n    return result;\n  }();\n\n  const auto make_tuple_of_means =\n      [&mesh](const Variables<TagsList>& vars) {\n        return tuples::TaggedTuple<::Tags::Mean<VectorTag<2>>>(\n            tnsr::I<double, 2>{\n                {{mean_value(get<0>(get<VectorTag<2>>(vars)), mesh),\n                  mean_value(get<1>(get<VectorTag<2>>(vars)), mesh)}}});\n      };\n\n  struct PackagedData {\n    tuples::TaggedTuple<::Tags::Mean<VectorTag<2>>> means;\n    Variables<TagsList> volume_data;\n    Mesh<2> mesh;\n  };\n  std::unordered_map<DirectionalId<2>, PackagedData,\n                     boost::hash<DirectionalId<2>>>\n      neighbor_data{};\n  neighbor_data[lower_xi_neighbor].means = make_tuple_of_means(lower_xi_vars);\n  neighbor_data[lower_xi_neighbor].volume_data = lower_xi_vars;\n  neighbor_data[lower_xi_neighbor].mesh = mesh;\n  neighbor_data[upper_xi_neighbor].means = make_tuple_of_means(upper_xi_vars);\n  neighbor_data[upper_xi_neighbor].volume_data = upper_xi_vars;\n  neighbor_data[upper_xi_neighbor].mesh = mesh;\n  neighbor_data[lower_eta_neighbor].means = make_tuple_of_means(lower_eta_vars);\n  neighbor_data[lower_eta_neighbor].volume_data = lower_eta_vars;\n  neighbor_data[lower_eta_neighbor].mesh = mesh;\n  neighbor_data[upper_eta_neighbor].means = make_tuple_of_means(upper_eta_vars);\n  neighbor_data[upper_eta_neighbor].volume_data = upper_eta_vars;\n  neighbor_data[upper_eta_neighbor].mesh = mesh;\n\n  // primary = lower_eta\n  // excluded = lower_xi\n  {\n    INFO(\"One excluded neighbor\");\n    const auto primary_neighbor = lower_eta_neighbor;\n    // In realistic uses, the calls to solve_constrained_fit for different\n    // tensor component would have different excluded neighbors. But for the\n    // test of the constrained fit itself, this is not important, and so we\n    // simplify by using the same excluded neighbors for each component.\n    const std::vector<DirectionalId<2>> neighbors_to_exclude = {\n        lower_xi_neighbor};\n\n    tnsr::I<DataVector, 2> constrained_fit{};\n    for (size_t tensor_index = 0; tensor_index < 2; ++tensor_index) {\n      Limiters::Weno_detail::solve_constrained_fit<VectorTag<2>>(\n          make_not_null(&(constrained_fit.get(tensor_index))),\n          local_tensor.get(tensor_index), tensor_index, mesh, element,\n          neighbor_data, primary_neighbor, neighbors_to_exclude);\n    }\n\n    // The expected coefficient values for the result of the constrained fit are\n    // found using Mathematica, using the following code (for Mathematica v10).\n    // This example computes the expected result for the vector x component; an\n    // analogous piece of code gives the expected result for the y component,\n    // but note that the neighbor to exclude changes in this case.\n    // qw3 = {1/3, 4/3, 1/3};                 (* or Gauss point equivalent *)\n    // qx3 = {-1, 0, 1};                      (* or Gauss point equivalent *)\n    // qw4 = {1/6, 5/6, 5/6, 1/6};            (* or Gauss point equivalent *)\n    // qx4 = {-1, -1/Sqrt[5], 1/Sqrt[5], 1};  (* or Gauss point equivalent *)\n    // quad43[f_, dx_, dy_] := Sum[\n    //     qw4[[qi]] qw3[[qj]] f[qx4[[qi]] + dx, qx3[[qj]] + dy],\n    //     {qi, 1, 4}, {qj, 1, 3}];\n    // trial[c0_, c1_, c2_, c3_, c4_, c5_, c6_, c7_, c8_, c9_, c10_, c11_][\n    //       x_, y_] :=\n    //     c0 + c1 x + c2 x^2 + c3 x^3 +\n    //     y (c4 + c5 x + c6 x^2 + c7 x^3) +\n    //     y^2 (c8 + c9 x + c10 x^2 + c11 x^3);\n    // uLocal[x_, y_] := 1 + 1/10 x + 1/5 y + 1/10 x y + 1/10 x y^2;\n    // uPrimary[x_, y_] := 1 + 1/4 x - 1/5 y^2 + 1/10 x y^2;\n    // uUpperXi[x_, y_] := 1 + 1/3 x + 1/4 y - 1/20 x^2;\n    // uUpperEta[x_, y_] := 1 + 1/3 x + 1/5 y;\n    // primaryOptimizationTerm[c0_, c1_, c2_, c3_, c4_, c5_, c6_, c7_, c8_,\n    //                         c9_, c10_, c11_][x_, y_] :=\n    //     (trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11][x, y]\n    //      - uPrimary[x, y])^2;\n    // Minimize[\n    //     quad43[primaryOptimizationTerm[c0, c1, c2, c3, c4, c5, c6, c7, c8,\n    //                                    c9, c10, c11],\n    //            0, -2]\n    //     + (quad43[trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11],\n    //               2, 0] - quad43[uUpperXi, 2, 0])^2\n    //     + (quad43[trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11],\n    //               0, 2] - quad43[uUpperEta, 0, 2])^2,\n    //     quad43[trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11],\n    //            0, 0] == quad43[uLocal, 0, 0],\n    // {c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11}\n    // ]\n    const auto expected = [&quadrature, &logical_coords]() {\n      const auto& x = get<0>(logical_coords);\n      const auto& y = get<1>(logical_coords);\n      // x-component coefficients\n      const auto c =\n          (quadrature == Spectral::Quadrature::GaussLobatto)\n              ? std::array<double, 12>{{6049013. / 5913219., 92367. / 360620.,\n                                        -1659. / 558961., -6083. / 558961.,\n                                        61831556. / 187251935., 42. / 6935.,\n                                        -126. / 42997., -462. / 42997.,\n                                        -2460491. / 37450387., 18283. / 180310.,\n                                        -378. / 558961., -1386. / 558961.}}\n              : std::array<double, 12>{\n                    {249938816573. / 244480323945., 405503. / 1579292.,\n                     -534. / 394823., -1246. / 107679.,\n                     26841345508. / 81493441315., 2790. / 394823.,\n                     -558. / 394823., -434. / 35893.,\n                     -5338984133. / 81493441315., 401573. / 3948230.,\n                     -135. / 394823., -105. / 35893.}};\n      // y-component coefficients\n      const auto d =\n          (quadrature == Spectral::Quadrature::GaussLobatto)\n              ? std::array<double, 12>{{3609701941. / 1685267415.,\n                                        120807. / 180310., -1018741. / 5589610.,\n                                        -168586. / 558961.,\n                                        -18411919. / 43211985., 1164. / 6935.,\n                                        -3492. / 42997., -12804. / 42997.,\n                                        -50666458. / 187251935., 3492. / 90155.,\n                                        454201. / 5589610., -38412. / 558961.}}\n              : std::array<double, 12>{\n                    {10921212941666. / 5134086802845., 3799721. / 5527522.,\n                     -3799721. / 27637610., -34532. / 107679.,\n                     -751998511637. / 1711362267615., 541260. / 2763761.,\n                     -108252. / 2763761., -12028. / 35893.,\n                     -31292971603. / 114090817841., 130950. / 2763761.,\n                     2501861. / 27637610., -2910. / 35893.}};\n      return tnsr::I<DataVector, 2>{\n          {{DataVector{\n                c[0] + c[1] * x + c[2] * square(x) + c[3] * cube(x) +\n                y * (c[4] + c[5] * x + c[6] * square(x) + c[7] * cube(x)) +\n                square(y) *\n                    (c[8] + c[9] * x + c[10] * square(x) + c[11] * cube(x))},\n            DataVector{\n                d[0] + d[1] * x + d[2] * square(x) + d[3] * cube(x) +\n                y * (d[4] + d[5] * x + d[6] * square(x) + d[7] * cube(x)) +\n                square(y) *\n                    (d[8] + d[9] * x + d[10] * square(x) + d[11] * cube(x))}}}};\n    }();\n\n    // Fit procedure has somewhat larger error scale than default\n    // Error further increases when using Gauss points\n    const double gauss_tol =\n        (quadrature == Spectral::Quadrature::Gauss ? 10. : 1.);\n    Approx local_approx = Approx::custom().epsilon(gauss_tol * 1e-10).scale(1.);\n    CHECK_ITERABLE_CUSTOM_APPROX(constrained_fit, expected, local_approx);\n    // Verify that the constraint is in fact satisfied\n    CHECK(mean_value(get<0>(constrained_fit), mesh) ==\n          local_approx(mean_value(get<0>(local_tensor), mesh)));\n    CHECK(mean_value(get<1>(constrained_fit), mesh) ==\n          local_approx(mean_value(get<1>(local_tensor), mesh)));\n  }\n\n  // external = lower_eta\n  // primary = lower_xi\n  // excluded = {upper_xi, upper_eta}\n  {\n    INFO(\"One external, two excluded neighbors\");\n    const Element<2> element_at_lower_eta_bdry{\n        ElementId<2>{0},\n        Element<2>::Neighbors_t{\n            {Direction<2>::lower_xi(),\n             TestHelpers::Limiters::make_neighbor_with_id<2>(0)},\n            {Direction<2>::upper_xi(),\n             TestHelpers::Limiters::make_neighbor_with_id<2>(1)},\n            // lower_eta is external boundary\n            {Direction<2>::upper_eta(),\n             TestHelpers::Limiters::make_neighbor_with_id<2>(4)}}};\n    auto neighbor_data_at_lower_eta_bdry = neighbor_data;\n    neighbor_data_at_lower_eta_bdry.erase(lower_eta_neighbor);\n\n    const auto primary_neighbor = lower_xi_neighbor;\n    const std::vector<DirectionalId<2>> neighbors_to_exclude = {\n        upper_xi_neighbor, upper_eta_neighbor};\n\n    tnsr::I<DataVector, 2> constrained_fit{};\n    for (size_t tensor_index = 0; tensor_index < 2; ++tensor_index) {\n      Limiters::Weno_detail::solve_constrained_fit<VectorTag<2>>(\n          make_not_null(&(constrained_fit.get(tensor_index))),\n          local_tensor.get(tensor_index), tensor_index, mesh,\n          element_at_lower_eta_bdry, neighbor_data_at_lower_eta_bdry,\n          primary_neighbor, neighbors_to_exclude);\n    }\n\n    // Coefficients from Mathematica, using code similar to the one above.\n    const auto expected = [&quadrature, &logical_coords]() {\n      const auto& x = get<0>(logical_coords);\n      const auto& y = get<1>(logical_coords);\n      const auto c =\n          (quadrature == Spectral::Quadrature::GaussLobatto)\n              ? std::array<double, 12>{{398. / 329., -1738. / 1645.,\n                                        -207. / 329., -33. / 329., -1. / 10.,\n                                        0., 0., 0., 0., 0., 0., 0.}}\n              : std::array<double, 12>{{4366. / 3581., -19294. / 17905.,\n                                        -2355. / 3581., -385. / 3581.,\n                                        -1. / 10., 0., 0., 0., 0., 0., 0., 0.}};\n      const auto d = (quadrature == Spectral::Quadrature::GaussLobatto)\n                         ? std::array<double, 12>{{589. / 329., 2067. / 1645.,\n                                                   207. / 329., 33. / 329., 0.,\n                                                   0., 0., 0., 0., 0., 0., 0.}}\n                         : std::array<double, 12>{\n                               {6377. / 3581., 4575. / 3581., 2355. / 3581.,\n                                385. / 3581., 0., 0., 0., 0., 0., 0., 0., 0.}};\n      return tnsr::I<DataVector, 2>{\n          {{DataVector{\n                c[0] + c[1] * x + c[2] * square(x) + c[3] * cube(x) +\n                y * (c[4] + c[5] * x + c[6] * square(x) + c[7] * cube(x)) +\n                square(y) *\n                    (c[8] + c[9] * x + c[10] * square(x) + c[11] * cube(x))},\n            DataVector{\n                d[0] + d[1] * x + d[2] * square(x) + d[3] * cube(x) +\n                y * (d[4] + d[5] * x + d[6] * square(x) + d[7] * cube(x)) +\n                square(y) *\n                    (d[8] + d[9] * x + d[10] * square(x) + d[11] * cube(x))}}}};\n    }();\n\n    // Fit procedure has somewhat larger error scale than default\n    // Error further increases when using Gauss points\n    const double gauss_tol =\n        (quadrature == Spectral::Quadrature::Gauss ? 100. : 1.);\n    Approx local_approx = Approx::custom().epsilon(gauss_tol * 1e-10).scale(1.);\n    CHECK_ITERABLE_CUSTOM_APPROX(constrained_fit, expected, local_approx);\n    // Verify that the constraint is in fact satisfied\n    CHECK(mean_value(get<0>(constrained_fit), mesh) ==\n          local_approx(mean_value(get<0>(local_tensor), mesh)));\n    CHECK(mean_value(get<1>(constrained_fit), mesh) ==\n          local_approx(mean_value(get<1>(local_tensor), mesh)));\n  }\n}\n\nvoid test_constrained_fit_3d(const Spectral::Quadrature quadrature =\n                                 Spectral::Quadrature::GaussLobatto) {\n  INFO(\"Testing Weno_detail::solve_constrained_fit in 3D\");\n  CAPTURE(quadrature);\n  using TagsList = tmpl::list<ScalarTag>;\n  struct PackagedData {\n    tuples::TaggedTuple<::Tags::Mean<ScalarTag>> means;\n    Variables<TagsList> volume_data;\n    Mesh<3> mesh;\n  };\n\n  const DirectionalId<3> lower_xi_neighbor{Direction<3>::lower_xi(),\n                                           ElementId<3>{1}};\n  const DirectionalId<3> upper_xi_neighbor{Direction<3>::upper_xi(),\n                                           ElementId<3>{2}};\n  const DirectionalId<3> lower_eta_neighbor{Direction<3>::lower_eta(),\n                                            ElementId<3>{3}};\n  const DirectionalId<3> upper_eta_neighbor{Direction<3>::upper_eta(),\n                                            ElementId<3>{4}};\n  const DirectionalId<3> lower_zeta_neighbor{Direction<3>::lower_zeta(),\n                                             ElementId<3>{5}};\n  const DirectionalId<3> upper_zeta_neighbor{Direction<3>::upper_zeta(),\n                                             ElementId<3>{6}};\n\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const auto mesh = Mesh<3>{{{3, 3, 4}}, Spectral::Basis::Legendre, quadrature};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  const auto local_data = [&logical_coords]() {\n    const auto& x = get<0>(logical_coords);\n    const auto& y = get<1>(logical_coords);\n    const auto& z = get<2>(logical_coords);\n    return DataVector{0.5 + 0.2 * x + 0.1 * square(y) * z};\n  }();\n\n  const auto lower_xi_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get(get<ScalarTag>(result)) = 1.2;\n    return result;\n  }();\n\n  const auto upper_xi_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get(get<ScalarTag>(result)) = 4.;\n    return result;\n  }();\n\n  const auto lower_eta_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get(get<ScalarTag>(result)) = 3.;\n    return result;\n  }();\n\n  const auto upper_eta_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get(get<ScalarTag>(result)) = 2.5;\n    return result;\n  }();\n\n  const auto lower_zeta_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get(get<ScalarTag>(result)) = 2.;\n    return result;\n  }();\n\n  const auto upper_zeta_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto& x = get<0>(logical_coords);\n    const auto& y = get<1>(logical_coords);\n    const auto z = get<2>(logical_coords) + 2.;\n    get(get<ScalarTag>(result)) =\n        1. + 0.25 * x + 0.1 * y * z + 0.5 * x * square(y) * z + 0.1 * cube(z);\n    return result;\n  }();\n\n  const auto make_tuple_of_means =\n      [&mesh](const Variables<TagsList>& vars) {\n        return tuples::TaggedTuple<::Tags::Mean<ScalarTag>>(\n            mean_value(get(get<ScalarTag>(vars)), mesh));\n      };\n\n  std::unordered_map<DirectionalId<3>, PackagedData,\n                     boost::hash<DirectionalId<3>>>\n      neighbor_data{};\n  neighbor_data[lower_xi_neighbor].means = make_tuple_of_means(lower_xi_vars);\n  neighbor_data[lower_xi_neighbor].volume_data = lower_xi_vars;\n  neighbor_data[lower_xi_neighbor].mesh = mesh;\n  neighbor_data[upper_xi_neighbor].means = make_tuple_of_means(upper_xi_vars);\n  neighbor_data[upper_xi_neighbor].volume_data = upper_xi_vars;\n  neighbor_data[upper_xi_neighbor].mesh = mesh;\n  neighbor_data[lower_eta_neighbor].means = make_tuple_of_means(lower_eta_vars);\n  neighbor_data[lower_eta_neighbor].volume_data = lower_eta_vars;\n  neighbor_data[lower_eta_neighbor].mesh = mesh;\n  neighbor_data[upper_eta_neighbor].means = make_tuple_of_means(upper_eta_vars);\n  neighbor_data[upper_eta_neighbor].volume_data = upper_eta_vars;\n  neighbor_data[upper_eta_neighbor].mesh = mesh;\n  neighbor_data[lower_zeta_neighbor].means =\n      make_tuple_of_means(lower_zeta_vars);\n  neighbor_data[lower_zeta_neighbor].volume_data = lower_zeta_vars;\n  neighbor_data[lower_zeta_neighbor].mesh = mesh;\n  neighbor_data[upper_zeta_neighbor].means =\n      make_tuple_of_means(upper_zeta_vars);\n  neighbor_data[upper_zeta_neighbor].volume_data = upper_zeta_vars;\n  neighbor_data[upper_zeta_neighbor].mesh = mesh;\n\n  // primary = upper_zeta\n  // excluded = lower_eta\n  {\n    INFO(\"One excluded neighbor\");\n    const auto primary_neighbor = upper_zeta_neighbor;\n    const std::vector<DirectionalId<3>> neighbors_to_exclude = {\n        lower_eta_neighbor};\n\n    DataVector constrained_fit;\n    Limiters::Weno_detail::solve_constrained_fit<ScalarTag>(\n        make_not_null(&constrained_fit), local_data, 0, mesh, element,\n        neighbor_data, primary_neighbor, neighbors_to_exclude);\n\n    // The expected coefficient values for the result of the constrained fit are\n    // found using Mathematica, using the following code (for Mathematica v10).\n    // qw3 = {1/3, 4/3, 1/3};                 (* or Gauss point equivalent *)\n    // qx3 = {-1, 0, 1};                      (* or Gauss point equivalent *)\n    // qw4 = {1/6, 5/6, 5/6, 1/6};            (* or Gauss point equivalent *)\n    // qx4 = {-1, -1/Sqrt[5], 1/Sqrt[5], 1};  (* or Gauss point equivalent *)\n    // quad334[f_, dx_, dy_, dz_] := Sum[\n    //     qw3[[qi]] qw3[[qj]] qw4[[qk]]\n    //     f[qx3[[qi]] + dx, qx3[[qj]] + dy, qx4[[qk]] + dz],\n    //     {qi, 1, 3}, {qj, 1, 3}, {qk, 1, 4}];\n    // trial[c0_, c1_, c2_, c3_, c4_, c5_, c6_, c7_, c8_, c9_, c10_, c11_,\n    //       c12_, c13_, c14_, c15_, c16_, c17_, c18_, c19_, c20_, c21_, c22_,\n    //       c23_, c24_, c25_, c26_, c27_, c28_, c29_, c30_, c31_, c32_, c33_,\n    //       c34_, c35_][x_, y_, z_] :=\n    //     c0 + c1 x + c2 x^2 + y (c3 + c4 x + c5 x^2) +\n    //                          y^2 (c6 + c7 x + c8 x^2) +\n    //     z (c9 + c10 x + c11 x^2 + y (c12 + c13 x + c14 x^2) +\n    //                               y^2 (c15 + c16 x + c17 x^2)) +\n    //     z^2 (c18 + c19 x + c20 x^2 + y (c21 + c22 x + c23 x^2) +\n    //                                  y^2 (c24 + c25 x + c26 x^2)) +\n    //     z^3 (c27 + c28 x + c29 x^2 + y (c30 + c31 x + c32 x^2) +\n    //                                  y^2 (c33 + c34 x + c35 x^2));\n    // uLocal[x_, y_, z_] := 1/2 + 1/5 x + 1/10 y^2 z;\n    // uPrimary[x_, y_, z_] := 1 + 1/4 x + 1/10 y z + 1/2 x y^2 z + 1/10 z^3;\n    // uLowerXi[x_, y_, z_] := 6/5;\n    // uUpperXi[x_, y_, z_] := 4;\n    // uUpperEta[x_, y_, z_] := 5/2;\n    // uLowerZeta[x_, y_, z_] := 2;\n    // primaryOptimizationTerm[c0_, c1_, c2_, c3_, c4_, c5_, c6_, c7_, c8_,\n    //                         c9_, c10_, c11_, c12_, c13_, c14_, c15_, c16_,\n    //                         c17_, c18_, c19_, c20_, c21_, c22_, c23_, c24_,\n    //                         c25_, c26_, c27_, c28_, c29_, c30_, c31_, c32_,\n    //                         c33_, c34_, c35_][x_, y_, z_] :=\n    //     (trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,\n    //            c12, c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, c23,\n    //            c24, c25, c26, c27, c28, c29, c30, c31, c32, c33, c34, c35][x,\n    //            y, z] - uPrimary[x, y, z])^2;\n    // Minimize[\n    //     quad334[primaryOptimizationTerm[c0, c1, c2, c3, c4, c5, c6, c7, c8,\n    //                                     c9, c10, c11, c12, c13, c14, c15,\n    //                                     c16, c17, c18, c19, c20, c21, c22,\n    //                                     c23, c24, c25, c26, c27, c28, c29,\n    //                                     c30, c31, c32, c33, c34, c35], 0, 0,\n    //                                     2]\n    //     + (quad334[trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,\n    //     c12,\n    //                      c13, c14, c15, c16, c17, c18, c19, c20, c21, c22,\n    //                      c23, c24, c25, c26, c27, c28, c29, c30, c31, c32,\n    //                      c33, c34, c35], -2, 0, 0] - quad334[uLowerXi, -2, 0,\n    //                      0])^2\n    //     + (quad334[trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,\n    //     c12,\n    //                      c13, c14, c15, c16, c17, c18, c19, c20, c21, c22,\n    //                      c23, c24, c25, c26, c27, c28, c29, c30, c31, c32,\n    //                      c33, c34, c35], 2, 0, 0] - quad334[uUpperXi, 2, 0,\n    //                      0])^2\n    //     + (quad334[trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,\n    //     c12,\n    //                      c13, c14, c15, c16, c17, c18, c19, c20, c21, c22,\n    //                      c23, c24, c25, c26, c27, c28, c29, c30, c31, c32,\n    //                      c33, c34, c35], 0, 2, 0] - quad334[uUpperEta, 0, 2,\n    //                      0])^2\n    //     + (quad334[trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,\n    //     c12,\n    //                      c13, c14, c15, c16, c17, c18, c19, c20, c21, c22,\n    //                      c23, c24, c25, c26, c27, c28, c29, c30, c31, c32,\n    //                      c33, c34, c35], 0, 0, -2] - quad334[uLowerZeta, 0,\n    //                      0, -2])^2,\n    //     quad334[trial[c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12,\n    //                   c13, c14, c15, c16, c17, c18, c19, c20, c21, c22, c23,\n    //                   c24, c25, c26, c27, c28, c29, c30, c31, c32, c33, c34,\n    //                   c35], 0, 0, 0] == quad334[uLocal, 0, 0, 0],\n    // {c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14,\n    //  c15, c16, c17, c18, c19, c20, c21, c22, c23, c24, c25, c26, c27,\n    //  c28, c29, c30, c31, c32, c33, c34, c35}\n    // ]\n    const auto expected = [&quadrature, &logical_coords]() {\n      const auto& x = get<0>(logical_coords);\n      const auto& y = get<1>(logical_coords);\n      const auto& z = get<2>(logical_coords);\n      const auto c =\n          (quadrature == Spectral::Quadrature::GaussLobatto)\n              ? std::array<double,\n                           36>{{7734190419582420551. / 62148673763526035437.,\n                                765121. / 1263364.,\n                                786240. / 1895041.,\n                                124800. / 1105441.,\n                                0.,\n                                0.,\n                                374400. / 1105441.,\n                                0.,\n                                0.,\n                                308447321809657439079. / 621486737635260354370.,\n                                -892944. / 1579205.,\n                                -6250608. / 9475205.,\n                                -878879. / 11054410.,\n                                0.,\n                                0.,\n                                -595296. / 1105441.,\n                                1. / 2.,\n                                0.,\n                                21514562148445593021. / 124297347527052070874.,\n                                89424. / 315841.,\n                                625968. / 1895041.,\n                                99360. / 1105441.,\n                                0.,\n                                0.,\n                                298080. / 1105441.,\n                                0.,\n                                0.,\n                                3655223967127444271. / 310743368817630177185.,\n                                -14256. / 315841.,\n                                -99792. / 1895041.,\n                                -15840. / 1105441.,\n                                0.,\n                                0.,\n                                -47520. / 1105441.,\n                                0.,\n                                0.}}\n              : std::array<double, 36>{\n                    {458679916884839601803. / 3920643646799006444317.,\n                     8269253. / 13751060.,\n                     4227552. / 10313281.,\n                     268416. / 5500417.,\n                     0.,\n                     0.,\n                     2013120. / 5500417.,\n                     0.,\n                     0.,\n                     4086360148702064037975. / 7841287293598012888634.,\n                     -395280. / 687553.,\n                     -6917400. / 10313281.,\n                     1108417. / 55004170.,\n                     0.,\n                     0.,\n                     -3294000. / 5500417.,\n                     1. / 2.,\n                     0.,\n                     1217573258478400072485. / 7841287293598012888634.,\n                     203472. / 687553.,\n                     3560760. / 10313281.,\n                     226080. / 5500417.,\n                     0.,\n                     0.,\n                     1695600. / 5500417.,\n                     0.,\n                     0.,\n                     3895938147037532473. / 254587249792143275605.,\n                     -33264. / 687553.,\n                     -52920. / 937571.,\n                     -36960. / 5500417.,\n                     0.,\n                     0.,\n                     -277200. / 5500417.,\n                     0.,\n                     0.}};\n      const DataVector term_z0 =\n          c[0] + c[1] * x + c[2] * square(x) +\n          y * (c[3] + c[4] * x + c[5] * square(x)) +\n          square(y) * (c[6] + c[7] * x + c[8] * square(x));\n      const DataVector term_z1 =\n          c[9] + c[10] * x + c[11] * square(x) +\n          y * (c[12] + c[13] * x + c[14] * square(x)) +\n          square(y) * (c[15] + c[16] * x + c[17] * square(x));\n      const DataVector term_z2 =\n          c[18] + c[19] * x + c[20] * square(x) +\n          y * (c[21] + c[22] * x + c[23] * square(x)) +\n          square(y) * (c[24] + c[25] * x + c[26] * square(x));\n      const DataVector term_z3 =\n          c[27] + c[28] * x + c[29] * square(x) +\n          y * (c[30] + c[31] * x + c[32] * square(x)) +\n          square(y) * (c[33] + c[34] * x + c[35] * square(x));\n      return DataVector{term_z0 + term_z1 * z + term_z2 * square(z) +\n                        term_z3 * cube(z)};\n    }();\n\n    // Fit procedure has somewhat larger error scale than default\n    Approx local_approx = Approx::custom().epsilon(1e-8).scale(1.);\n    CHECK_ITERABLE_CUSTOM_APPROX(constrained_fit, expected, local_approx);\n    // Verify that the constraint is in fact satisfied\n    CHECK(mean_value(constrained_fit, mesh) ==\n          local_approx(mean_value(local_data, mesh)));\n  }\n\n  // external = {lower_xi, lower_eta}\n  // primary = upper_xi\n  // excluded = {upper_eta, upper_zeta}\n  {\n    INFO(\"Two external, two excluded neighbors\");\n    const Element<3> element_two_bdries{\n        ElementId<3>{0},\n        Element<3>::Neighbors_t{\n            // lower_xi is external boundary\n            {Direction<3>::upper_xi(),\n             TestHelpers::Limiters::make_neighbor_with_id<3>(1)},\n            // lower_eta is external boundary\n            {Direction<3>::upper_eta(),\n             TestHelpers::Limiters::make_neighbor_with_id<3>(4)},\n            {Direction<3>::lower_zeta(),\n             TestHelpers::Limiters::make_neighbor_with_id<3>(5)},\n            {Direction<3>::upper_zeta(),\n             TestHelpers::Limiters::make_neighbor_with_id<3>(6)}}};\n    auto neighbor_data_two_bdries = neighbor_data;\n    neighbor_data_two_bdries.erase(lower_xi_neighbor);\n    neighbor_data_two_bdries.erase(lower_eta_neighbor);\n\n    const auto primary_neighbor = upper_xi_neighbor;\n    const std::vector<DirectionalId<3>> neighbors_to_exclude = {\n        upper_eta_neighbor, upper_zeta_neighbor};\n\n    DataVector constrained_fit;\n    Limiters::Weno_detail::solve_constrained_fit<ScalarTag>(\n        make_not_null(&constrained_fit), local_data, 0, mesh,\n        element_two_bdries, neighbor_data_two_bdries, primary_neighbor,\n        neighbors_to_exclude);\n\n    // Coefficients from Mathematica, using code similar to the one above.\n    const auto expected = [&quadrature, &logical_coords]() {\n      const auto& x = get<0>(logical_coords);\n      const auto& y = get<1>(logical_coords);\n      const auto& z = get<2>(logical_coords);\n      auto c = make_array<36>(0.);\n      if (quadrature == Spectral::Quadrature::GaussLobatto) {\n        c[0] = 139558567. / 190046570.;\n        c[1] = 306385833. / 95023285.;\n        c[2] = -70704423. / 95023285.;\n        c[9] = 88164. / 1117921.;\n        c[10] = -87048. / 1117921.;\n        c[11] = 20088. / 1117921.;\n        c[18] = 42660. / 1117921.;\n        c[19] = -42120. / 1117921.;\n        c[20] = 9720. / 1117921.;\n        c[27] = -156420. / 1117921.;\n        c[28] = 154440. / 1117921.;\n        c[29] = -35640. / 1117921.;\n      } else {\n        c[0] = 90824101. / 118534617.;\n        c[1] = 133513993. / 39511539.;\n        c[2] = -21534515. / 26341026.;\n        c[9] = 17800. / 204723.;\n        c[10] = -6200. / 68241.;\n        c[11] = 500. / 22747.;\n        c[18] = 3560. / 204723.;\n        c[19] = -1240. / 68241.;\n        c[20] = 100. / 22747.;\n        c[27] = -274120. / 1842507.;\n        c[28] = 95480. / 614169.;\n        c[29] = -7700. / 204723.;\n      }\n\n      const DataVector term_z0 =\n          c[0] + c[1] * x + c[2] * square(x) +\n          y * (c[3] + c[4] * x + c[5] * square(x)) +\n          square(y) * (c[6] + c[7] * x + c[8] * square(x));\n      const DataVector term_z1 =\n          c[9] + c[10] * x + c[11] * square(x) +\n          y * (c[12] + c[13] * x + c[14] * square(x)) +\n          square(y) * (c[15] + c[16] * x + c[17] * square(x));\n      const DataVector term_z2 =\n          c[18] + c[19] * x + c[20] * square(x) +\n          y * (c[21] + c[22] * x + c[23] * square(x)) +\n          square(y) * (c[24] + c[25] * x + c[26] * square(x));\n      const DataVector term_z3 =\n          c[27] + c[28] * x + c[29] * square(x) +\n          y * (c[30] + c[31] * x + c[32] * square(x)) +\n          square(y) * (c[33] + c[34] * x + c[35] * square(x));\n      return DataVector{term_z0 + term_z1 * z + term_z2 * square(z) +\n                        term_z3 * cube(z)};\n    }();\n\n    // Fit procedure has somewhat larger error scale than default\n    Approx local_approx = Approx::custom().epsilon(1e-8).scale(1.);\n    CHECK_ITERABLE_CUSTOM_APPROX(constrained_fit, expected, local_approx);\n    // Verify that the constraint is in fact satisfied\n    CHECK(mean_value(constrained_fit, mesh) ==\n          local_approx(mean_value(local_data, mesh)));\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid test_hweno_work(\n    const tnsr::I<DataVector, VolumeDim>& local_vector,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>, Variables<tmpl::list<VectorTag<VolumeDim>>>,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_vars,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        std::array<std::vector<DirectionalId<VolumeDim>>, VolumeDim>,\n        boost::hash<DirectionalId<VolumeDim>>>& expected_excluded_neighbors,\n    Approx local_approx = approx) {\n  struct PackagedData {\n    tuples::TaggedTuple<::Tags::Mean<VectorTag<VolumeDim>>> means;\n    Variables<tmpl::list<VectorTag<VolumeDim>>> volume_data;\n    Mesh<VolumeDim> mesh;\n  };\n\n  const auto make_tuple_of_means =\n      [&mesh](const Variables<tmpl::list<VectorTag<VolumeDim>>>& vars) {\n        tuples::TaggedTuple<::Tags::Mean<VectorTag<VolumeDim>>> result(\n            tnsr::I<double, VolumeDim>{});\n        for (size_t i = 0; i < VolumeDim; ++i) {\n          get<::Tags::Mean<VectorTag<VolumeDim>>>(result).get(i) =\n              mean_value(get<VectorTag<VolumeDim>>(vars).get(i), mesh);\n        }\n        return result;\n      };\n\n  std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_data{};\n  for (auto& neighbor_and_vars : neighbor_vars) {\n    const auto& neighbor = neighbor_and_vars.first;\n    const auto& vars = neighbor_and_vars.second;\n    neighbor_data[neighbor].means = make_tuple_of_means(vars);\n    neighbor_data[neighbor].volume_data = vars;\n    neighbor_data[neighbor].mesh = mesh;\n  }\n\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      modified_neighbor_solution_buffer{};\n  for (const auto& neighbor_and_data : neighbor_data) {\n    const auto& neighbor = neighbor_and_data.first;\n    modified_neighbor_solution_buffer.insert(\n        std::make_pair(neighbor, DataVector(mesh.number_of_grid_points())));\n  }\n\n  auto vector_to_limit = local_vector;\n  const double neighbor_linear_weight = 0.001;\n  Limiters::Weno_detail::hweno_impl<VectorTag<VolumeDim>>(\n      make_not_null(&modified_neighbor_solution_buffer),\n      make_not_null(&vector_to_limit), neighbor_linear_weight, mesh, element,\n      neighbor_data);\n\n  // Check data mean was preserved\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    CHECK(mean_value(vector_to_limit.get(i), mesh) ==\n          local_approx(mean_value(local_vector.get(i), mesh)));\n  }\n\n  auto expected_hweno = local_vector;\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      expected_neighbor_polynomials;\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    // Call solve_constrained_fit to make the expected neighbors for each\n    // component of the vector\n    for (auto& neighbor_and_excluded : expected_excluded_neighbors) {\n      const auto& primary_neighbor = neighbor_and_excluded.first;\n      const auto& neighbors_to_exclude =\n          gsl::at(neighbor_and_excluded.second, i);\n\n      DataVector& constrained_fit =\n          expected_neighbor_polynomials[primary_neighbor];\n      Limiters::Weno_detail::solve_constrained_fit<VectorTag<VolumeDim>>(\n          make_not_null(&constrained_fit), local_vector.get(i), i, mesh,\n          element, neighbor_data, primary_neighbor, neighbors_to_exclude);\n    }\n    Limiters::Weno_detail::reconstruct_from_weighted_sum(\n        make_not_null(&expected_hweno.get(i)), neighbor_linear_weight,\n        Limiters::Weno_detail::DerivativeWeight::PowTwoEllOverEllFactorial,\n        mesh, expected_neighbor_polynomials);\n  }\n\n  // Check limited fields as expected\n  CHECK_ITERABLE_CUSTOM_APPROX(vector_to_limit, expected_hweno, local_approx);\n}\n\nvoid test_hweno_impl_1d(const Spectral::Quadrature quadrature =\n                            Spectral::Quadrature::GaussLobatto) {\n  INFO(\"Testing hweno_impl in 1D\");\n  CAPTURE(quadrature);\n  using TagsList = tmpl::list<VectorTag<1>>;\n  const auto mesh = Mesh<1>{{{3}}, Spectral::Basis::Legendre, quadrature};\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const auto logical_coords = logical_coordinates(mesh);\n\n  const DirectionalId<1> lower_xi_neighbor{Direction<1>::lower_xi(),\n                                           ElementId<1>{1}};\n  const DirectionalId<1> upper_xi_neighbor{Direction<1>::upper_xi(),\n                                           ElementId<1>{2}};\n\n  const auto local_tensor = [&logical_coords]() {\n    const auto& x = get<0>(logical_coords);\n    return VectorTag<1>::type{{{DataVector{1. + 2.1 * x + 0.3 * square(x)}}}};\n  }();\n\n  const auto lower_xi_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto x = get<0>(logical_coords) - 2.;\n    get<0>(get<VectorTag<1>>(result)) = 4. - 0.5 * x - 0.1 * square(x);\n    return result;\n  }();\n\n  const auto upper_xi_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto x = get<0>(logical_coords) + 2.;\n    get<0>(get<VectorTag<1>>(result)) = 1. - 0.2 * x + 0.1 * square(x);\n    return result;\n  }();\n\n  Approx local_approx = Approx::custom().epsilon(1e-11).scale(1.);\n  test_hweno_work<1>(\n      local_tensor, mesh, element,\n      {std::make_pair(lower_xi_neighbor, lower_xi_vars),\n       std::make_pair(upper_xi_neighbor, upper_xi_vars)},\n      {std::make_pair(\n           lower_xi_neighbor,\n           make_array<1>(std::vector<DirectionalId<1>>{upper_xi_neighbor})),\n       std::make_pair(\n           upper_xi_neighbor,\n           make_array<1>(std::vector<DirectionalId<1>>{lower_xi_neighbor}))},\n      local_approx);\n}\n\nvoid test_hweno_impl_2d(const Spectral::Quadrature quadrature =\n                            Spectral::Quadrature::GaussLobatto) {\n  INFO(\"Testing hweno_impl in 2D\");\n  CAPTURE(quadrature);\n  using TagsList = tmpl::list<VectorTag<2>>;\n  const auto mesh = Mesh<2>{{{4, 3}}, Spectral::Basis::Legendre, quadrature};\n  const auto element = TestHelpers::Limiters::make_element<2>();\n  const auto logical_coords = logical_coordinates(mesh);\n\n  const DirectionalId<2> lower_xi_neighbor{Direction<2>::lower_xi(),\n                                           ElementId<2>{1}};\n  const DirectionalId<2> upper_xi_neighbor{Direction<2>::upper_xi(),\n                                           ElementId<2>{2}};\n  const DirectionalId<2> lower_eta_neighbor{Direction<2>::lower_eta(),\n                                            ElementId<2>{3}};\n  const DirectionalId<2> upper_eta_neighbor{Direction<2>::upper_eta(),\n                                            ElementId<2>{4}};\n\n  const auto local_tensor = [&logical_coords]() {\n    const auto& x = get<0>(logical_coords);\n    const auto& y = get<1>(logical_coords);\n    return VectorTag<2>::type{{{DataVector{1. + 0.1 * x + 0.2 * y +\n                                           0.1 * x * y + 0.1 * x * square(y)},\n                                DataVector(x.size(), 2.)}}};\n  }();\n\n  const auto lower_xi_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto x = get<0>(logical_coords) - 2.;\n    const auto& y = get<1>(logical_coords);\n    get<0>(get<VectorTag<2>>(result)) = 2. + 0.2 * x - 0.1 * y;\n    get<1>(get<VectorTag<2>>(result)) = 1.;\n    return result;\n  }();\n\n  const auto upper_xi_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto x = get<0>(logical_coords) + 2.;\n    const auto& y = get<1>(logical_coords);\n    get<0>(get<VectorTag<2>>(result)) =\n        1. + 1. / 3. * x + 0.25 * y - 0.05 * square(x);\n    get<1>(get<VectorTag<2>>(result)) = -0.5;\n    return result;\n  }();\n\n  const auto lower_eta_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto& x = get<0>(logical_coords);\n    const auto y = get<1>(logical_coords) - 2.;\n    get<0>(get<VectorTag<2>>(result)) =\n        1. + 0.25 * x - 0.2 * square(y) + 0.1 * x * square(y);\n    get<1>(get<VectorTag<2>>(result)) =\n        1.2 + 0.5 * x - 0.1 * square(x) - 0.2 * y + 0.1 * square(x) * square(y);\n    return result;\n  }();\n\n  const auto upper_eta_vars = [&mesh, &logical_coords]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    const auto& x = get<0>(logical_coords);\n    const auto y = get<1>(logical_coords) + 2.;\n    get<0>(get<VectorTag<2>>(result)) = 1. + 1. / 3. * x + 0.2 * y;\n    get<1>(get<VectorTag<2>>(result)) = 0.1;\n    return result;\n  }();\n\n  using DirKey = DirectionalId<2>;\n  Approx local_approx = Approx::custom().epsilon(1e-11).scale(1.);\n  test_hweno_work<2>(\n      local_tensor, mesh, element,\n      {std::make_pair(lower_xi_neighbor, lower_xi_vars),\n       std::make_pair(upper_xi_neighbor, upper_xi_vars),\n       std::make_pair(lower_eta_neighbor, lower_eta_vars),\n       std::make_pair(upper_eta_neighbor, upper_eta_vars)},\n      {std::make_pair(lower_xi_neighbor,\n                      std::array<std::vector<DirKey>, 2>{\n                          {std::vector<DirKey>{lower_eta_neighbor},\n                           std::vector<DirKey>{upper_xi_neighbor}}}),\n       std::make_pair(upper_xi_neighbor,\n                      std::array<std::vector<DirKey>, 2>{\n                          {std::vector<DirKey>{lower_eta_neighbor},\n                           std::vector<DirKey>{upper_eta_neighbor}}}),\n       std::make_pair(lower_eta_neighbor,\n                      std::array<std::vector<DirKey>, 2>{\n                          {std::vector<DirKey>{lower_xi_neighbor},\n                           std::vector<DirKey>{upper_xi_neighbor}}}),\n       std::make_pair(upper_eta_neighbor,\n                      std::array<std::vector<DirKey>, 2>{\n                          {std::vector<DirKey>{lower_eta_neighbor},\n                           std::vector<DirKey>{upper_xi_neighbor}}})},\n      local_approx);\n}\n\nvoid test_hweno_impl_3d(const Spectral::Quadrature quadrature =\n                            Spectral::Quadrature::GaussLobatto) {\n  INFO(\"Testing hweno_impl in 3D\");\n  CAPTURE(quadrature);\n  using TagsList = tmpl::list<VectorTag<3>>;\n  const auto mesh = Mesh<3>{{{3, 3, 4}}, Spectral::Basis::Legendre, quadrature};\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const auto logical_coords = logical_coordinates(mesh);\n\n  const DirectionalId<3> lower_xi_neighbor{Direction<3>::lower_xi(),\n                                           ElementId<3>{1}};\n  const DirectionalId<3> upper_xi_neighbor{Direction<3>::upper_xi(),\n                                           ElementId<3>{2}};\n  const DirectionalId<3> lower_eta_neighbor{Direction<3>::lower_eta(),\n                                            ElementId<3>{3}};\n  const DirectionalId<3> upper_eta_neighbor{Direction<3>::upper_eta(),\n                                            ElementId<3>{4}};\n  const DirectionalId<3> lower_zeta_neighbor{Direction<3>::lower_zeta(),\n                                             ElementId<3>{5}};\n  const DirectionalId<3> upper_zeta_neighbor{Direction<3>::upper_zeta(),\n                                             ElementId<3>{6}};\n\n  const auto local_tensor = [&logical_coords]() {\n    const auto& x = get<0>(logical_coords);\n    const auto& y = get<1>(logical_coords);\n    const auto& z = get<2>(logical_coords);\n    return VectorTag<3>::type{{{DataVector{-2. + 0.2 * y * square(z)},\n                                DataVector{0.8 - 0.1 * square(x) * z},\n                                DataVector{5. + 0.5 * x * y * z}}}};\n  }();\n\n  const auto lower_xi_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get<0>(get<VectorTag<3>>(result)) = 1.;\n    get<1>(get<VectorTag<3>>(result)) = 1.;\n    get<2>(get<VectorTag<3>>(result)) = 1.;\n    return result;\n  }();\n\n  const auto upper_xi_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get<0>(get<VectorTag<3>>(result)) = -8.1;\n    get<1>(get<VectorTag<3>>(result)) = -1.5;\n    get<2>(get<VectorTag<3>>(result)) = 2.5;\n    return result;\n  }();\n\n  const auto lower_eta_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get<0>(get<VectorTag<3>>(result)) = 0.7;\n    get<1>(get<VectorTag<3>>(result)) = 0.1;\n    get<2>(get<VectorTag<3>>(result)) = 10.3;\n    return result;\n  }();\n\n  const auto upper_eta_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get<0>(get<VectorTag<3>>(result)) = -3.9;\n    get<1>(get<VectorTag<3>>(result)) = 1.2;\n    get<2>(get<VectorTag<3>>(result)) = -0.3;\n    return result;\n  }();\n\n  const auto lower_zeta_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get<0>(get<VectorTag<3>>(result)) = -5.4;\n    get<1>(get<VectorTag<3>>(result)) = 0.1;\n    get<2>(get<VectorTag<3>>(result)) = 4.2;\n    return result;\n  }();\n\n  const auto upper_zeta_vars = [&mesh]() {\n    Variables<TagsList> result(mesh.number_of_grid_points());\n    get<0>(get<VectorTag<3>>(result)) = -2.3;\n    get<1>(get<VectorTag<3>>(result)) = -0.9;\n    get<2>(get<VectorTag<3>>(result)) = 1.1;\n    return result;\n  }();\n\n  using DirKey = DirectionalId<3>;\n  Approx local_approx = Approx::custom().epsilon(1e-11).scale(1.);\n  test_hweno_work<3>(\n      local_tensor, mesh, element,\n      {std::make_pair(lower_xi_neighbor, lower_xi_vars),\n       std::make_pair(upper_xi_neighbor, upper_xi_vars),\n       std::make_pair(lower_eta_neighbor, lower_eta_vars),\n       std::make_pair(upper_eta_neighbor, upper_eta_vars),\n       std::make_pair(lower_zeta_neighbor, lower_zeta_vars),\n       std::make_pair(upper_zeta_neighbor, upper_zeta_vars)},\n      {std::make_pair(\n           lower_xi_neighbor,\n           std::array<std::vector<DirKey>, 3>{\n               {std::vector<DirKey>{upper_xi_neighbor},\n                std::vector<DirKey>{upper_xi_neighbor},\n                std::vector<DirKey>{lower_eta_neighbor, upper_eta_neighbor}}}),\n       std::make_pair(\n           upper_xi_neighbor,\n           std::array<std::vector<DirKey>, 3>{\n               {std::vector<DirKey>{lower_zeta_neighbor},\n                std::vector<DirKey>{upper_zeta_neighbor},\n                std::vector<DirKey>{lower_eta_neighbor, upper_eta_neighbor}}}),\n       std::make_pair(lower_eta_neighbor,\n                      std::array<std::vector<DirKey>, 3>{\n                          {std::vector<DirKey>{upper_xi_neighbor},\n                           std::vector<DirKey>{upper_xi_neighbor},\n                           std::vector<DirKey>{upper_eta_neighbor}}}),\n       std::make_pair(upper_eta_neighbor,\n                      std::array<std::vector<DirKey>, 3>{\n                          {std::vector<DirKey>{upper_xi_neighbor},\n                           std::vector<DirKey>{upper_xi_neighbor},\n                           std::vector<DirKey>{lower_eta_neighbor}}}),\n       std::make_pair(\n           lower_zeta_neighbor,\n           std::array<std::vector<DirKey>, 3>{\n               {std::vector<DirKey>{upper_xi_neighbor},\n                std::vector<DirKey>{upper_xi_neighbor},\n                std::vector<DirKey>{lower_eta_neighbor, upper_eta_neighbor}}}),\n       std::make_pair(\n           upper_zeta_neighbor,\n           std::array<std::vector<DirKey>, 3>{\n               {std::vector<DirKey>{upper_xi_neighbor},\n                std::vector<DirKey>{upper_xi_neighbor},\n                std::vector<DirKey>{lower_eta_neighbor, upper_eta_neighbor}}})},\n      local_approx);\n}\n\n}  // namespace\n\n// [[Timeout, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.HwenoImpl\", \"[Limiters][Unit]\") {\n  test_secondary_neighbors_to_exclude_from_fit();\n\n  // These functions test the constrained fit algorithm.\n  // In particular, each function tests:\n  // - the typical case with one excluded neighbor\n  // - an atypical case with 1+ external boundaries and 1+ excluded neighbors\n  test_constrained_fit_1d();\n  test_constrained_fit_2d_vector();\n  test_constrained_fit_3d();\n\n  // It is difficult to test the HWENO algorithm without entirely reimplementing\n  // it. However, each of the main pieces ...\n  //  - Weno_detail::secondary_neighbors_to_exclude_from_fit\n  //  - Weno_detail::solve_constrained_fit\n  //  - Weno_detail::reconstruct_from_weighted_sum\n  // ... has a detailed independent test (in this file or in Test_WenoHelpers).\n  // So here we provide a simple test in which we manually list the neighbors to\n  // exclude, then call the constrained fit and reconstruction functions. We\n  // check that the hweno_impl results match this simple reconstruction. This\n  // tests that the different pieces are plugged together in the right way.\n  test_hweno_impl_1d();\n  test_hweno_impl_2d();\n  test_hweno_impl_3d();\n}\n\n// Separate the Gauss quadrature tests for two reasons:\n// 1. Generalizing the Hweno matrix static caches to handle both LGL and LG\n//    points in the same run would be somewhat tedious. By testing each basis\n//    in a separate SPECTRE_TEST_CASE, run in separate calls to the test\n//    executable, the static cache clashes are avoided.\n// 2. To keep the test case duration comfortably under the 2s time limit\n// [[Timeout, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.HwenoImpl.GaussQuadrature\",\n                  \"[Limiters][Unit]\") {\n  const auto gauss = Spectral::Quadrature::Gauss;\n  test_constrained_fit_1d(gauss);\n  test_constrained_fit_2d_vector(gauss);\n  test_constrained_fit_3d(gauss);\n\n  test_hweno_impl_1d(gauss);\n  test_hweno_impl_2d(gauss);\n  test_hweno_impl_3d(gauss);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_Krivodonova.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash/extensions.hpp>\n#include <cstddef>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Krivodonova.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Limiters {\n\n// The overall way of testing the Krivodonova limiter is to set the modal\n// coefficients directly, then compare to the expected result from the algorithm\n// for 3 collocation points in the documentation. In the 1D case it is easy\n// enough to test with 4 collocation points, so this is done instead. In order\n// to make sure the loops over tensor components work correctly we apply the\n// limiter both to a Scalar and to a tnsr::I<Dim>.\nnamespace {\ntemplate <size_t VolumeDim, typename PackagedData>\nusing NeighborData = std::unordered_map<DirectionalId<VolumeDim>, PackagedData,\n                                        boost::hash<DirectionalId<VolumeDim>>>;\n\ntemplate <size_t Identifier>\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim, size_t Identifier>\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n\nnamespace test_1d {\nvoid test_package_data(const size_t order) {\n  INFO(\"Testing package data\");\n  CAPTURE(order);\n  const Mesh<1> mesh(order + 1, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const DataVector& x = Spectral::collocation_points(mesh);\n  using Limiter = Krivodonova<1, tmpl::list<ScalarTag<0>, VectorTag<1, 0>>>;\n  Limiter krivodonova{};\n\n  Scalar<DataVector> tensor0(mesh.number_of_grid_points());\n  get(tensor0) =\n      Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(order,\n                                                                        x);\n  tnsr::I<DataVector, 1> tensor1(mesh.number_of_grid_points());\n  get<0>(tensor1) =\n      Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(order,\n                                                                        x) +\n      2.0 * Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n                order - 1, x);\n  Limiter::PackagedData packaged_data{};\n\n  // test no reorienting\n  {\n    krivodonova.package_data(make_not_null(&packaged_data), tensor0, tensor1,\n                             mesh, OrientationMap<1>::create_aligned());\n    ModalVector expected0(mesh.number_of_grid_points(), 0.0);\n    expected0[mesh.number_of_grid_points() - 1] = 1.0;\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::Modal<ScalarTag<0>>>(packaged_data.modal_volume_data)),\n        expected0);\n    ModalVector expected1(mesh.number_of_grid_points(), 0.0);\n    expected1[mesh.number_of_grid_points() - 1] = 1.0;\n    expected1[mesh.number_of_grid_points() - 2] = 2.0;\n    CHECK_ITERABLE_APPROX(SINGLE_ARG(get<0>(get<::Tags::Modal<VectorTag<1, 0>>>(\n                              packaged_data.modal_volume_data))),\n                          expected1);\n  }\n  // test reorienting\n  {\n    krivodonova.package_data(\n        make_not_null(&packaged_data), tensor0, tensor1, mesh,\n        {{{Direction<1>::upper_xi()}}, {{Direction<1>::lower_xi()}}});\n    ModalVector expected0(mesh.number_of_grid_points(), 0.0);\n    expected0[0] = 1.0;\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::Modal<ScalarTag<0>>>(packaged_data.modal_volume_data)),\n        expected0);\n    ModalVector expected1(mesh.number_of_grid_points(), 0.0);\n    expected1[0] = 1.0;\n    expected1[1] = 2.0;\n    CHECK_ITERABLE_APPROX(SINGLE_ARG(get<0>(get<::Tags::Modal<VectorTag<1, 0>>>(\n                              packaged_data.modal_volume_data))),\n                          expected1);\n  }\n}\n\nvoid test_limiting_two_neighbors() {\n  INFO(\"Testing applying limiter to coefficients\");\n  static constexpr size_t dim = 1;\n  const size_t order = 3;\n  const Mesh<dim> mesh(order + 1, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const size_t num_pts = mesh.number_of_grid_points();\n\n  using Limiter = Krivodonova<dim, tmpl::list<ScalarTag<0>, VectorTag<dim, 0>>>;\n  // Use non-unity but close alpha values to make the math easier but still\n  // test thoroughly.\n  Limiter krivodonova{\n      make_array<Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(\n          0.99)};\n\n  NeighborData<dim, typename Limiter::PackagedData> neighbor_data{};\n\n  const Element<dim> element(ElementId<dim>{0}, {});\n  // We don't care about the ElementId for these tests, just the direction.\n  Limiter::PackagedData& package_data_upper = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lower = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_xi(), ElementId<dim>{0}}];\n\n  package_data_upper.modal_volume_data.initialize(num_pts);\n  package_data_upper.mesh = mesh;\n  package_data_lower.modal_volume_data.initialize(num_pts);\n  package_data_lower.mesh = mesh;\n\n  Scalar<DataVector> nodal_scalar_data_to_limit(num_pts, 0.0);\n  tnsr::I<DataVector, dim> nodal_vector_data_to_limit(num_pts, 0.0);\n  DataVector expected(num_pts);\n  const auto helper =\n      [&element, &expected, &krivodonova, &mesh, &neighbor_data,\n       &package_data_lower, &package_data_upper, &nodal_scalar_data_to_limit,\n       &nodal_vector_data_to_limit](\n          const ModalVector& upper_coeffs, const ModalVector& initial_coeffs,\n          const ModalVector& lower_coeffs, const ModalVector& expected_coeffs) {\n        to_nodal_coefficients(&get(nodal_scalar_data_to_limit), initial_coeffs,\n                              mesh);\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_upper.modal_volume_data)) = upper_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_lower.modal_volume_data)) = lower_coeffs;\n        for (size_t i = 0; i < dim; ++i) {\n          to_nodal_coefficients(&nodal_vector_data_to_limit.get(i),\n                                initial_coeffs, mesh);\n          get<::Tags::Modal<VectorTag<dim, 0>>>(\n              package_data_upper.modal_volume_data)\n              .get(i) = upper_coeffs;\n          get<::Tags::Modal<VectorTag<dim, 0>>>(\n              package_data_lower.modal_volume_data)\n              .get(i) = lower_coeffs;\n        }\n        krivodonova(&nodal_scalar_data_to_limit, &nodal_vector_data_to_limit,\n                    element, mesh, neighbor_data);\n        to_nodal_coefficients(&expected, expected_coeffs, mesh);\n        CHECK_ITERABLE_APPROX(get(nodal_scalar_data_to_limit), expected);\n        for (size_t i = 0; i < dim; ++i) {\n          CHECK_ITERABLE_APPROX(nodal_vector_data_to_limit.get(i), expected);\n        }\n      };\n\n  // Note: Throughout the tests 1.0e-18 is used anywhere that we need a positive\n  // but zero coefficient, because it is below machine precision for O(1)\n  // numbers.\n\n  // Limit no coefficients because the highest coefficient doesn't need\n  // limiting.\n  helper({7.0, 3.0, 4.1, 0.0}, {3.0, -2.0, 2.0, 1.0}, {1.0, -5.0, 1.0e-18, 0.0},\n         {3.0, -2.0, 2.0, 1.0});\n\n  // Limit the highest coefficient only:\n  // Limit top coeff from upper neighbor\n  helper({7.0, 3.0, 2.5, 0.0}, {3.0, -2.0, 2.0, 1.0}, {1.0, -5.0, 1.0e-18, 0.0},\n         {3.0, -2.0, 2.0, 0.5 * 0.99});\n  // Limit top coeff from lower neighbor\n  helper({7.0, 3.0, 4.1, 0.0}, {3.0, -2.0, 2.0, 1.0}, {1.0, -5.0, 1.3, 0.0},\n         {3.0, -2.0, 2.0, 0.7 * 0.99});\n  // Zero top coeff from upper neighbor\n  helper({7.0, 3.0, 1.9, 0.0}, {3.0, -2.0, 2.0, 1.0}, {1.0, -5.0, 1.0e-18, 0.0},\n         {3.0, -2.0, 2.0, 0.0});\n  // Zero top coeff from lower neighbor\n  helper({7.0, 3.0, 4.1, 0.0}, {3.0, -2.0, 2.0, 1.0}, {1.0, -5.0, 5.3, 0.0},\n         {3.0, -2.0, 2.0, 0.0});\n\n  // Limit the top 2 coeffs:\n  // Limit top upper neighbor, limit second upper neighbor\n  helper({7.0, 3.0, 2.5, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, -5.0, 1.0e-18, 0.0},\n         {3.0, 2.0, 1.0 * 0.99, 0.5 * 0.99});\n  // Limit top upper neighbor, limit second lower neighbor\n  helper({7.0, 5.0, 2.5, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 1.1, 1.0e-18, 0.0},\n         {3.0, 2.0, 0.9 * 0.99, 0.5 * 0.99});\n  // Limit top upper neighbor, zero second upper neighbor\n  helper({7.0, 1.0, 2.5, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 1.1, 1.0e-18, 0.0},\n         {3.0, 2.0, 0.0, 0.5 * 0.99});\n  // Limit top upper neighbor, zero second lower neighbor\n  helper({7.0, 5.0, 2.5, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 2.1, 1.0e-18, 0.0},\n         {3.0, 2.0, 0.0, 0.5 * 0.99});\n  // Zero top upper neighbor, limit second upper neighbor\n  helper({7.0, 3.0, 1.9, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, -5.0, 1.0e-18, 0.0},\n         {3.0, 2.0, 1.0 * 0.99, 0.0});\n  // Zero top upper neighbor, limit second lower neighbor\n  helper({7.0, 5.0, 1.9, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 1.1, 1.0e-18, 0.0},\n         {3.0, 2.0, 0.9 * 0.99, 0.0});\n  // Zero top upper neighbor, zero second upper neighbor\n  helper({7.0, 1.0, 1.9, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 1.1, 1.0e-18, 0.0},\n         {3.0, 2.0, 0.0, 0.0});\n  // Zero top upper neighbor, zero second lower neighbor\n  helper({7.0, 5.0, 1.9, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 2.1, 1.0e-18, 0.0},\n         {3.0, 2.0, 0.0, 0.0});\n\n  // Limit top lower neighbor, limit second upper neighbor\n  helper({7.0, 3.0, 4.1, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, -5.0, 1.3, 0.0},\n         {3.0, 2.0, 1.0 * 0.99, 0.7 * 0.99});\n  // Limit top lower neighbor, limit second lower neighbor\n  helper({7.0, 5.0, 4.1, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 1.1, 1.3, 0.0},\n         {3.0, 2.0, 0.9 * 0.99, 0.7 * 0.99});\n  // Limit top lower neighbor, zero second upper neighbor\n  helper({7.0, 1.0, 4.1, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 1.1, 1.3, 0.0},\n         {3.0, 2.0, 0.0, 0.7 * 0.99});\n  // Limit top lower neighbor, zero second lower neighbor\n  helper({7.0, 5.0, 4.1, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 2.1, 1.3, 0.0},\n         {3.0, 2.0, 0.0, 0.7 * 0.99});\n  // Zero top lower neighbor, limit second upper neighbor\n  helper({7.0, 3.0, 4.1, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, -5.0, 3.0, 0.0},\n         {3.0, 2.0, 1.0 * 0.99, 0.0});\n  // Zero top lower neighbor, limit second lower neighbor\n  helper({7.0, 5.0, 4.1, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 1.1, 3.0, 0.0},\n         {3.0, 2.0, 0.9 * 0.99, 0.0});\n  // Zero top lower neighbor, zero second upper neighbor\n  helper({7.0, 1.0, 4.1, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 1.1, 3.0, 0.0},\n         {3.0, 2.0, 0.0, 0.0});\n  // Zero top lower neighbor, zero second lower neighbor\n  helper({7.0, 5.0, 4.1, 0.0}, {3.0, 2.0, 2.0, 1.0}, {-2.1, 2.1, 3.0, 0.0},\n         {3.0, 2.0, 0.0, 0.0});\n}\n\nvoid test_limiting_different_values_different_tensors() {\n  INFO(\"Testing different values for each tensor component\");\n  static constexpr size_t dim = 1;\n  const size_t order = 3;\n  const Mesh<dim> mesh(order + 1, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const size_t num_pts = mesh.number_of_grid_points();\n  using Limiter = Krivodonova<dim, tmpl::list<ScalarTag<0>, VectorTag<dim, 0>>>;\n  // Use non-unity but close alpha values to make the math easier but still\n  // test thoroughly.\n  Limiter krivodonova{\n      make_array<Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(\n          0.99)};\n\n  NeighborData<dim, typename Limiter::PackagedData> neighbor_data{};\n\n  const Element<dim> element(ElementId<dim>{0}, {});\n  // We don't care about the ElementId for these tests, just the direction.\n  Limiter::PackagedData& package_data_up_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_xi(), ElementId<dim>{0}}];\n\n  package_data_up_xi.modal_volume_data.initialize(num_pts);\n  package_data_up_xi.mesh = mesh;\n  package_data_lo_xi.modal_volume_data.initialize(num_pts);\n  package_data_lo_xi.mesh = mesh;\n\n  Scalar<DataVector> nodal_scalar_data_to_limit(num_pts, 0.0);\n  tnsr::I<DataVector, dim> nodal_vector_data_to_limit(num_pts, 0.0);\n  Scalar<DataVector> nodal_scalar_expected(num_pts, 0.0);\n  tnsr::I<DataVector, dim> nodal_vector_expected(num_pts, 0.0);\n\n  // Limit top coeff from upper neighbor\n  to_nodal_coefficients(&get(nodal_scalar_data_to_limit), {3.0, -2.0, 2.0, 1.0},\n                        mesh);\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_up_xi.modal_volume_data)) =\n      ModalVector{7.0, 3.0, 2.5, 0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_lo_xi.modal_volume_data)) =\n      ModalVector{1.0, -5.0, 1.0e-18, 0.0};\n  to_nodal_coefficients(&get(nodal_scalar_expected),\n                        {3.0, -2.0, 2.0, 0.5 * 0.99}, mesh);\n\n  // Limit top upper neighbor, limit second lower neighbor\n  to_nodal_coefficients(&nodal_vector_data_to_limit.get(0),\n                        {3.0, 2.0, 2.0, 1.0}, mesh);\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_xi.modal_volume_data)\n      .get(0) = ModalVector{7.0, 5.0, 2.5, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_xi.modal_volume_data)\n      .get(0) = ModalVector{-2.1, 1.1, 1.0e-18, 0.0};\n  to_nodal_coefficients(&nodal_vector_expected.get(0),\n                        {3.0, 2.0, 0.9 * 0.99, 0.5 * 0.99}, mesh);\n\n  krivodonova(&nodal_scalar_data_to_limit, &nodal_vector_data_to_limit, element,\n              mesh, neighbor_data);\n  CHECK_ITERABLE_APPROX(get(nodal_scalar_data_to_limit),\n                        get(nodal_scalar_expected));\n  for (size_t i = 0; i < dim; ++i) {\n    CAPTURE(i);\n    CHECK_ITERABLE_APPROX(nodal_vector_data_to_limit.get(i),\n                          nodal_vector_expected.get(i));\n  }\n}\n\nvoid run() {\n  INFO(\"Testing 1d limiter\");\n  for (size_t order = Spectral::minimum_number_of_points<\n           Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>;\n       order < Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n       ++order) {\n    test_package_data(order);\n  }\n  test_limiting_two_neighbors();\n  test_limiting_different_values_different_tensors();\n}\n}  // namespace test_1d\n\nnamespace test_2d {\nvoid test_package_data() {\n  INFO(\"Testing package data\");\n  static constexpr size_t dim = 2;\n  const Mesh<dim> mesh({{2, 3}},\n                       {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n                       {{Spectral::Quadrature::GaussLobatto,\n                         Spectral::Quadrature::GaussLobatto}});\n  CAPTURE(mesh);\n  using Limiter = Krivodonova<dim, tmpl::list<ScalarTag<0>, VectorTag<dim, 0>>>;\n  Limiter krivodonova{};\n\n  const ModalVector neighbor_modes{1.0, 2.0, 3.0, 4.0, 5.0, 6.0};\n  Scalar<DataVector> tensor0(mesh.number_of_grid_points());\n  to_nodal_coefficients(&get(tensor0), neighbor_modes, mesh);\n\n  tnsr::I<DataVector, dim> tensor1(mesh.number_of_grid_points());\n  for (size_t d = 0; d < dim; ++d) {\n    to_nodal_coefficients(&tensor1.get(d), (d + 2.0) * neighbor_modes, mesh);\n  }\n  Limiter::PackagedData packaged_data{};\n\n  // test no reorienting\n  {\n    krivodonova.package_data(make_not_null(&packaged_data), tensor0, tensor1,\n                             mesh, OrientationMap<2>::create_aligned());\n    CHECK(get(get<::Tags::Modal<ScalarTag<0>>>(\n              packaged_data.modal_volume_data)) == neighbor_modes);\n    for (size_t d = 0; d < dim; ++d) {\n      CHECK(\n          get<::Tags::Modal<VectorTag<dim, 0>>>(packaged_data.modal_volume_data)\n              .get(d) == ModalVector((d + 2.0) * neighbor_modes));\n    }\n  }\n  // test reorienting\n  {\n    krivodonova.package_data(\n        make_not_null(&packaged_data), tensor0, tensor1, mesh,\n        OrientationMap<2>{\n            {{Direction<2>::lower_eta(), Direction<2>::upper_xi()}}});\n    const ModalVector expected_modes{2.0, 4.0, 6.0, 1.0, 3.0, 5.0};\n    CHECK(get(get<::Tags::Modal<ScalarTag<0>>>(\n              packaged_data.modal_volume_data)) == expected_modes);\n    for (size_t d = 0; d < dim; ++d) {\n      CHECK(\n          get<::Tags::Modal<VectorTag<dim, 0>>>(packaged_data.modal_volume_data)\n              .get(d) == ModalVector((d + 2.0) * expected_modes));\n    }\n  }\n}\n\ntemplate <typename F>\nvoid test_limiting_2_2_coefficient(const F& helper) {\n  // Limit no coefficients because the highest coefficient doesn't need\n  // limiting.\n  helper({0.0, 1.0, 2.0, 3.0, 4.0, 7.0, 3.0, 4.1, 0.0},\n         {0.0, 1.0, 2.0, 3.0, 4.0, 7.0, 3.0, 4.1, 0.0},\n         {0.0, 1.0, 2.0, 3.0, 4.0, 3.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, 2.0, 3.0, 4.0, 1.0, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, 2.0, 3.0, 4.0, 1.0, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, 2.0, 3.0, 4.0, 3.0, -2.0, 2.0, 1.0});\n\n  // Limit (2,2) because +(1,2)\n  helper({0.0, 1.0, 3.0, 3.0, 3.0, 7.0, 3.0, 2.5, 0.0},\n         {0.0, 1.0, 3.0, 3.0, 3.0, 7.0, 3.0, 4.1, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 0.5 * 0.99});\n\n  // Limit (2,2) because +(2,1)\n  helper({0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, 3.0, 3.0, 3.0, 2.5, 3.0, 4.1, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 0.5 * 0.99});\n\n  // Limit (2,2) because -(1,2)\n  helper({0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.3, 0.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 0.7 * 0.99});\n\n  // Limit (2,2) because -(2,1)\n  helper({0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 0.7 * 0.99});\n\n  // Zero (2,2) because +(1,2)\n  helper({0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 1.9, 0.0},\n         {0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 0.0});\n\n  // Zero (2,2) because +(2,1)\n  helper({0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, 3.0, 3.0, 3.0, 1.9, 3.0, 4.1, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 0.0});\n\n  // Zero (2,2) because -(1,2)\n  helper({0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 5.3, 0.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 0.0});\n\n  // Zero (2,2) because -(2,1)\n  helper({0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, 3.0, 3.0, 3.0, 4.1, 3.0, 4.1, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 1.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -5.0, -5.0, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 1.0, -2.0, -2.0, -2.0, 2.0, -2.0, 2.0, 0.0});\n}\n\ntemplate <typename F>\nvoid test_limiting_2_1_coefficient(const F& helper) {\n  // Limit (2,1) because +(2,0)\n  helper({0.0, 7.0, 3.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 3.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 0.99, 3.0, 2.0, 0.0});\n\n  // Limit (2,1) because -(2,0)\n  helper({0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, 1.1, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, 1.1, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 0.9 * 0.99, 3.0, 2.0, 0.0});\n\n  // Zero (2,1) because +(2,0)\n  helper({0.0, 7.0, 1.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 1.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, 1.1, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, 1.1, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 0.0, 3.0, 2.0, 0.0});\n\n  // Zero (2,1) because -(2,0)\n  helper({0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, 2.1, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, 2.1, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 0.0, 3.0, 2.0, 0.0});\n\n  // Limit (2,1) because +(1,1)\n  helper({0.0, 7.0, 5.0, 7.0, 0.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 0.99 * 2.0, 3.0, 2.0, 0.0});\n\n  // Limit (2,1) because -(1,1)\n  helper({0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -2.1, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 0.99 * 0.1, 3.0, 2.0, 0.0});\n\n  // Zero (2,1) because +(1,1)\n  helper({0.0, 7.0, 5.0, 7.0, -3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 0.0, 3.0, 2.0, 0.0});\n\n  // Zero (2,1) because -(1,1)\n  helper({0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, 5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 0.0, 3.0, 2.0, 0.0});\n}\n\ntemplate <typename F>\nvoid test_limiting_1_2_coefficient(const F& helper) {\n  // Limit (1,2) because +(0,2)\n  helper({0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.99, 0.0});\n\n  // Limit (1,2) because -(0,2)\n  helper({0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, 2.1, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, 2.1, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.9 * 0.99, 0.0});\n\n  // Zero (1,2) because +(0,2)\n  helper({0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 2.9, 4.1, 0.0},\n         {0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 2.9, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.0, 0.0});\n\n  // Zero (1,2) because -(0,2)\n  helper({0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, 8.1, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, 8.1, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.0, 0.0});\n\n  // Limit (2,1) because +(1,1)\n  helper({0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 0.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.99 * 2.0, 0.0});\n\n  // Limit (2,1) because -(1,1)\n  helper({0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -4.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.99 * 2.0, 0.0});\n\n  // Zero (2,1) because +(1,1)\n  helper({0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, -3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.0, 0.0});\n\n  // Zero (2,1) because -(1,1)\n  helper({0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0},\n         {0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0, -2.1, -5.0, -2.1, -1.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.0, 0.0});\n}\n\ntemplate <typename F>\nvoid test_limiting_2_0_coefficient(const F& helper) {\n  // Limit (2,0) because +(1,0)\n  helper({5.0, 3.1, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 0.1 * 0.99, 3.0, 0.2, 2.0, 3.0, 0.99, 0.0});\n\n  // Limit (2,0) because -(1,0)\n  helper({5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, 2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 0.9 * 0.99, 3.0, 0.2, 2.0, 3.0, 0.99, 0.0});\n\n  // Zero (2,0) because +(1,0)\n  helper({5.0, 2.9, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 0.0, 3.0, 0.2, 2.0, 3.0, 0.99, 0.0});\n\n  // Zero (2,0) because -(1,0)\n  helper({5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, 3.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 0.0, 3.0, 0.2, 2.0, 3.0, 0.99, 0.0});\n}\n\ntemplate <typename F>\nvoid test_limiting_0_2_coefficient(const F& helper) {\n  // Limit (0,2) because +(0,1)\n  helper({5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 7.0, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 0.1 * 0.99, 0.99, 0.0});\n\n  // Limit (0,2) because -(0,1)\n  helper({5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, 2.2, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 0.8 * 0.99, 0.99, 0.0});\n\n  // Zero (0,2) because +(0,1)\n  helper({5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 7.0, 7.0, 2.9, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 0.0, 0.99, 0.0});\n\n  // Zero (0,2) because -(0,1)\n  helper({5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 7.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, 3.2, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 0.2, 2.0, 0.0, 0.99, 0.0});\n}\n\ntemplate <typename F>\nvoid test_limiting_1_1_coefficient(const F& helper) {\n  // Limit (1,1) because +(1,0)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 1.0 * 0.99, 3.0, 0.3 * 0.99, 2.0 * 0.99, 0.1 * 0.99, 0.99,\n          0.0});\n\n  // Limit (1,1) because -(1,0)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 4.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, 2.6, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 1.0 * 0.99, 3.0, 0.4 * 0.99, 2.0 * 0.99, 0.1 * 0.99, 0.99,\n          0.0});\n\n  // Limit (1,1) because +(0,1)\n  helper({5.0, 3.3, 7.0, 3.4, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 4.2, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 0.3 * 0.99, 3.0, 0.4 * 0.99, 2.0 * 0.99, 0.1 * 0.99, 0.99,\n          0.0});\n\n  // Limit (1,1) because -(0,1)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 4.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, 2.5, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 1.0 * 0.99, 3.0, 0.5 * 0.99, 2.0 * 0.99, 0.1 * 0.99, 0.99,\n          0.0});\n\n  // Zero (1,1) because +(1,0)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 2.9, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 1.0 * 0.99, 3.0, 0.0, 2.0 * 0.99, 0.1 * 0.99, 0.99, 0.0});\n\n  // Zero (1,1) because -(1,0)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 4.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, 3.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 1.0 * 0.99, 3.0, 0.0, 2.0 * 0.99, 0.1 * 0.99, 0.99, 0.0});\n\n  // Zero (1,1) because +(0,1)\n  helper({5.0, 3.3, 7.0, 2.9, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 4.2, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 0.3 * 0.99, 3.0, 0.0, 2.0 * 0.99, 0.1 * 0.99, 0.99, 0.0});\n\n  // Zero (1,1) because -(0,1)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 4.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, 4.5, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 3.0, 1.0 * 0.99, 3.0, 0.0, 2.0 * 0.99, 0.1 * 0.99, 0.99, 0.0});\n}\n\ntemplate <typename F>\nvoid test_limiting_1_0_coefficient(const F& helper) {\n  // Limit (1,0) because +(0,0)\n  helper({1.5, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 0.5 * 0.99, 1.0 * 0.99, 3.0, 0.3 * 0.99, 2.0 * 0.99, 0.1 * 0.99,\n          0.99, 0.0});\n\n  // Limit (1,0) because -(0,0)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {0.6, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 0.4 * 0.99, 1.0 * 0.99, 3.0, 0.3 * 0.99, 2.0 * 0.99, 0.1 * 0.99,\n          0.99, 0.0});\n\n  // Zero (1,0) because +(0,0)\n  helper({0.5, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 0.0, 1.0 * 0.99, 3.0, 0.3 * 0.99, 2.0 * 0.99, 0.1 * 0.99, 0.99,\n          0.0});\n\n  // Zero (1,0) because -(0,0)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {1.6, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 0.0, 1.0 * 0.99, 3.0, 0.3 * 0.99, 2.0 * 0.99, 0.1 * 0.99, 0.99,\n          0.0});\n}\n\ntemplate <typename F>\nvoid test_limiting_0_1_coefficient(const F& helper) {\n  // Limit (0,1) because +(0,0)\n  helper({1.4, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.5, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 0.4 * 0.99, 1.0 * 0.99, 0.5 * 0.99, 0.3 * 0.99, 2.0 * 0.99,\n          0.1 * 0.99, 0.99, 0.0});\n\n  // Limit (0,1) because -(0,0)\n  helper({0.9, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.7, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 0.0, 1.0 * 0.99, 0.3 * 0.99, 0.3 * 0.99, 2.0 * 0.99, 0.1 * 0.99,\n          0.99, 0.0});\n\n  // Zero (0,1) because +(0,0)\n  helper({1.4, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {0.5, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 0.4 * 0.99, 1.0 * 0.99, 0.0, 0.3 * 0.99, 2.0 * 0.99, 0.1 * 0.99,\n          0.99, 0.0});\n\n  // Zero (0,1) because -(0,0)\n  helper({5.0, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {5.0, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0},\n         {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0},\n         {0.4, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {1.7, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0},\n         {1.0, 0.6 * 0.99, 1.0 * 0.99, 0.0, 0.3 * 0.99, 2.0 * 0.99, 0.1 * 0.99,\n          0.99, 0.0});\n}\n\nvoid test_limiting_different_values_different_tensors() {\n  INFO(\"Testing different values for each tensor component\");\n  static constexpr size_t dim = 2;\n  const size_t order = 2;  // Use only 3 coefficients because more is tedious...\n  const Mesh<dim> mesh(order + 1, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const size_t num_pts = mesh.number_of_grid_points();\n  using Limiter = Krivodonova<dim, tmpl::list<ScalarTag<0>, VectorTag<dim, 0>>>;\n  // Use non-unity but close alpha values to make the math easier but still\n  // test thoroughly.\n  Limiter krivodonova{\n      make_array<Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(\n          0.99)};\n\n  NeighborData<dim, typename Limiter::PackagedData> neighbor_data{};\n\n  const Element<dim> element(ElementId<dim>{0}, {});\n  // We don't care about the ElementId for these tests, just the direction.\n  Limiter::PackagedData& package_data_up_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_up_eta = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_eta(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_eta = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_eta(), ElementId<dim>{0}}];\n\n  package_data_up_xi.modal_volume_data.initialize(num_pts);\n  package_data_up_xi.mesh = mesh;\n  package_data_lo_xi.modal_volume_data.initialize(num_pts);\n  package_data_lo_xi.mesh = mesh;\n  package_data_up_eta.modal_volume_data.initialize(num_pts);\n  package_data_up_eta.mesh = mesh;\n  package_data_lo_eta.modal_volume_data.initialize(num_pts);\n  package_data_lo_eta.mesh = mesh;\n\n  Scalar<DataVector> nodal_scalar_data_to_limit(num_pts, 0.0);\n  tnsr::I<DataVector, dim> nodal_vector_data_to_limit(num_pts, 0.0);\n  Scalar<DataVector> nodal_scalar_expected(num_pts, 0.0);\n  tnsr::I<DataVector, dim> nodal_vector_expected(num_pts, 0.0);\n\n  // Limit (1,0) because +(0,0)\n  to_nodal_coefficients(&get(nodal_scalar_data_to_limit),\n                        {1.0, 3.0, 2.0, 3.0, 1.0, 2.0, 3.0, 2.0, 1.0}, mesh);\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_up_xi.modal_volume_data)) =\n      ModalVector{1.5, 4.0, 7.0, 7.0, 3.0, 4.1, 4.0, 4.1, 0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_lo_xi.modal_volume_data)) =\n      ModalVector{-5.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_up_eta.modal_volume_data)) =\n      ModalVector{5.0, 3.3, 7.0, 3.1, 3.0, 4.1, 4.0, 4.1, 0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_lo_eta.modal_volume_data)) =\n      ModalVector{-5.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0};\n  to_nodal_coefficients(&get(nodal_scalar_expected),\n                        {1.0, 0.5 * 0.99, 1.0 * 0.99, 3.0, 0.3 * 0.99,\n                         2.0 * 0.99, 0.1 * 0.99, 0.99, 0.0},\n                        mesh);\n\n  // Zero (1,2) because +(0,2)\n  to_nodal_coefficients(&nodal_vector_data_to_limit.get(0),\n                        {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0}, mesh);\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_xi.modal_volume_data)\n      .get(0) = ModalVector{0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 2.9, 4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_xi.modal_volume_data)\n      .get(0) =\n      ModalVector{0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_eta.modal_volume_data)\n      .get(0) = ModalVector{0.0, 7.0, 7.0, 7.0, 3.0, 4.1, 2.9, 4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_eta.modal_volume_data)\n      .get(0) =\n      ModalVector{0.0, -2.1, -5.0, -2.1, -5.0, 5.3, -5.0, 1.0e-18, 0.0};\n  to_nodal_coefficients(&nodal_vector_expected.get(0),\n                        {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.0, 0.0}, mesh);\n\n  // Limit (2,1) because +(1,1)\n  to_nodal_coefficients(&nodal_vector_data_to_limit.get(1),\n                        {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 2.0, 1.0}, mesh);\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_xi.modal_volume_data)\n      .get(1) = ModalVector{0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_xi.modal_volume_data)\n      .get(1) =\n      ModalVector{0.0, -2.1, -5.0, -2.1, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_eta.modal_volume_data)\n      .get(1) = ModalVector{0.0, 7.0, 5.0, 7.0, 3.0, 4.1, 7.0, 4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_eta.modal_volume_data)\n      .get(1) =\n      ModalVector{0.0, -2.1, -5.0, -2.1, -4.0, 5.3, -5.0, 1.0e-18, 0.0};\n  to_nodal_coefficients(&nodal_vector_expected.get(1),\n                        {0.0, 3.0, 2.0, 3.0, -2.0, 2.0, 3.0, 0.99 * 2.0, 0.0},\n                        mesh);\n\n  krivodonova(&nodal_scalar_data_to_limit, &nodal_vector_data_to_limit, element,\n              mesh, neighbor_data);\n  CHECK_ITERABLE_APPROX(get(nodal_scalar_data_to_limit),\n                        get(nodal_scalar_expected));\n  for (size_t i = 0; i < dim; ++i) {\n    CAPTURE(i);\n    CHECK_ITERABLE_APPROX(nodal_vector_data_to_limit.get(i),\n                          nodal_vector_expected.get(i));\n  }\n}\n\nvoid run() {\n  INFO(\"Testing 2d limiter\");\n  test_package_data();\n\n  INFO(\"Testing applying limiter to coefficients\");\n  static constexpr size_t dim = 2;\n  const size_t order = 2;  // Use only 3 coefficients because more is tedious...\n  const Mesh<dim> mesh(order + 1, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const size_t num_pts = mesh.number_of_grid_points();\n  using Limiter = Krivodonova<dim, tmpl::list<ScalarTag<0>>>;\n  // Use non-unity but close alpha values to make the math easier but still\n  // test thoroughly.\n  Limiter krivodonova{\n      make_array<Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(\n          0.99)};\n\n  NeighborData<dim, typename Limiter::PackagedData> neighbor_data{};\n\n  const Element<dim> element(ElementId<dim>{0}, {});\n  // We don't care about the ElementId for these tests, just the direction.\n  Limiter::PackagedData& package_data_up_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_up_eta = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_eta(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_eta = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_eta(), ElementId<dim>{0}}];\n\n  package_data_up_xi.modal_volume_data.initialize(num_pts);\n  package_data_up_xi.mesh = mesh;\n  package_data_lo_xi.modal_volume_data.initialize(num_pts);\n  package_data_lo_xi.mesh = mesh;\n  package_data_up_eta.modal_volume_data.initialize(num_pts);\n  package_data_up_eta.mesh = mesh;\n  package_data_lo_eta.modal_volume_data.initialize(num_pts);\n  package_data_lo_eta.mesh = mesh;\n\n  Scalar<DataVector> nodal_scalar_data_to_limit(num_pts, 0.0);\n  DataVector expected(num_pts);\n  const auto helper =\n      [&element, &expected, &krivodonova, &mesh, &neighbor_data,\n       &package_data_lo_xi, &package_data_up_xi, &package_data_lo_eta,\n       &package_data_up_eta, &nodal_scalar_data_to_limit](\n          const ModalVector& up_xi_coeffs, const ModalVector& up_eta_coeffs,\n          const ModalVector& initial_coeffs, const ModalVector& lo_xi_coeffs,\n          const ModalVector& lo_eta_coeffs,\n          const ModalVector& expected_coeffs) {\n        to_nodal_coefficients(&get(nodal_scalar_data_to_limit), initial_coeffs,\n                              mesh);\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_up_xi.modal_volume_data)) = up_xi_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_lo_xi.modal_volume_data)) = lo_xi_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_up_eta.modal_volume_data)) = up_eta_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_lo_eta.modal_volume_data)) = lo_eta_coeffs;\n        krivodonova(&nodal_scalar_data_to_limit, element, mesh, neighbor_data);\n        to_nodal_coefficients(&expected, expected_coeffs, mesh);\n        CHECK_ITERABLE_APPROX(get(nodal_scalar_data_to_limit), expected);\n      };\n\n  // Map between 2D and 1D coefficients:\n  // [(0,0), (1,0), (2,0), (0,1), (1,1), (2,1), (0,2), (1,2), (2,2)]\n  //  (0,     1,     2,     3,     4,     5,     6,     7,     8)\n\n  test_limiting_2_2_coefficient(helper);\n  test_limiting_2_1_coefficient(helper);\n  test_limiting_1_2_coefficient(helper);\n  test_limiting_2_0_coefficient(helper);\n  test_limiting_0_2_coefficient(helper);\n  test_limiting_1_1_coefficient(helper);\n  test_limiting_1_0_coefficient(helper);\n  test_limiting_0_1_coefficient(helper);\n\n  test_limiting_different_values_different_tensors();\n}\n}  // namespace test_2d\n\nnamespace test_3d {\nvoid test_package_data() {\n  INFO(\"Testing package data\");\n  static constexpr size_t dim = 3;\n  const Mesh<dim> mesh(\n      {{2, 3, 4}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n        Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::GaussLobatto}});\n  CAPTURE(mesh);\n  const auto logical_x = logical_coordinates(mesh);\n  using Limiter = Krivodonova<dim, tmpl::list<ScalarTag<0>, VectorTag<dim, 0>>>;\n  Limiter krivodonova{};\n\n  const ModalVector neighbor_modes{\n      0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,  9.0,  10.0, 11.0,\n      12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0};\n  Scalar<DataVector> tensor0(mesh.number_of_grid_points());\n  to_nodal_coefficients(&get(tensor0), neighbor_modes, mesh);\n\n  tnsr::I<DataVector, dim> tensor1(mesh.number_of_grid_points());\n  for (size_t d = 0; d < dim; ++d) {\n    to_nodal_coefficients(&tensor1.get(d), (d + 2.0) * neighbor_modes, mesh);\n  }\n  Limiter::PackagedData packaged_data{};\n\n  // test no reorienting\n  {\n    krivodonova.package_data(make_not_null(&packaged_data), tensor0, tensor1,\n                             mesh, OrientationMap<3>::create_aligned());\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::Modal<ScalarTag<0>>>(packaged_data.modal_volume_data)),\n        neighbor_modes);\n    for (size_t d = 0; d < dim; ++d) {\n      CHECK_ITERABLE_APPROX(SINGLE_ARG(get<::Tags::Modal<VectorTag<dim, 0>>>(\n                                           packaged_data.modal_volume_data)\n                                           .get(d)),\n                            ModalVector((d + 2.0) * neighbor_modes));\n    }\n  }\n  // test reorienting\n  {\n    krivodonova.package_data(make_not_null(&packaged_data), tensor0, tensor1,\n                             mesh,\n                             OrientationMap<3>{{{Direction<3>::upper_zeta(),\n                                                 Direction<3>::upper_eta(),\n                                                 Direction<3>::upper_xi()}}});\n    const ModalVector expected_modes{\n        0.0, 6.0, 12.0, 18.0, 2.0, 8.0, 14.0, 20.0, 4.0, 10.0, 16.0, 22.0,\n        1.0, 7.0, 13.0, 19.0, 3.0, 9.0, 15.0, 21.0, 5.0, 11.0, 17.0, 23.0};\n    CHECK_ITERABLE_APPROX(\n        get(get<::Tags::Modal<ScalarTag<0>>>(packaged_data.modal_volume_data)),\n        expected_modes);\n    for (size_t d = 0; d < dim; ++d) {\n      CHECK_ITERABLE_APPROX(SINGLE_ARG(get<::Tags::Modal<VectorTag<dim, 0>>>(\n                                           packaged_data.modal_volume_data)\n                                           .get(d)),\n                            ModalVector((d + 2.0) * expected_modes));\n    }\n  }\n}\n\ntemplate <typename F>\nvoid test_limiting_2_2_2_coefficient(const F& helper) {\n  // Limit no coefficients because the highest coefficient doesn't need\n  // limiting.\n  helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 4.1,\n          18.0, 19.0, 20.0, 21.0, 22.0, 4.1,  3.0,  4.1,  0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 4.1,\n          18.0, 19.0, 20.0, 21.0, 22.0, 4.1,  3.0,  4.1,  0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 4.1,\n          18.0, 19.0, 20.0, 21.0, 22.0, 4.1,  3.0,  4.1,  0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 2.0,\n          18.0, 19.0, 20.0, 21.0, 22.0, 3.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     8.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 14.0,    15.0, 16.0,    1.0e-18,\n          18.0, 19.0, 20.0, 21.0, 22.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     8.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 14.0,    15.0, 16.0,    1.0e-18,\n          18.0, 19.0, 20.0, 21.0, 22.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     8.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 14.0,    15.0, 16.0,    1.0e-18,\n          18.0, 19.0, 20.0, 21.0, 22.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  8.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 2.0,\n          18.0, 19.0, 20.0, 21.0, 22.0, 3.0,  -2.0, 2.0,  1.0});\n\n  {  // Limit (2,2,2)\n    // Limit (2,2,2) because +(1,2,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n    // Limit (2,2,2) because +(2,1,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  2.5, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n    // Limit (2,2,2) because +(2,2,1)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 2.5,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n    // Limit (2,2,2) because -(1,2,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,  -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0, 1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.3,  0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.7 * 0.99});\n\n    // Limit (2,2,2) because -(2,1,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0, 15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.3,  -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.7 * 0.99});\n\n    // Limit (2,2,2) because -(2,2,1)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.3,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.7 * 0.99});\n  }\n\n  {  // Zero (2,2,2)\n    // Zero (2,2,2) because +(1,2,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  1.9, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.0});\n\n    // Zero (2,2,2) because +(2,1,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  1.9, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.0});\n\n    // Zero (2,2,2) because +(2,2,1)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 1.9,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.0});\n\n    // Zero (2,2,2) because -(1,2,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,  -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0, 1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 5.3,  0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.0});\n\n    // Zero (2,2,2) because -(2,1,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0, 15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 5.3,  -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.0});\n\n    // Zero (2,2,2) because -(1,2,2)\n    helper({0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0, 6.0,  7.0, 3.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, 3.0, 15.0, 3.0, 4.1,\n            18.0, 19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    5.3,\n            18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n           {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n            9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n            18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.0});\n  }\n}\n\ntemplate <typename F>\nvoid test_limiting_2_2_1_coefficient(const F& helper) {\n  // Limit (2,2,1) because +(1,2,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0,  3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, -1.1, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5,  0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.9 * 0.99,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,2,1) because +(1,2,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0,   3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, -16.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5,   0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,2,1) because -(1,2,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -3.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 1.0 * 0.99,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,2,1) because -(1,2,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, 16.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,2,1) because +(2,1,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0,  6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, -1.3, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1,  3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.7 * 0.99,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,2,1) because +(2,1,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0,   6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, -14.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1,   3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,2,1) because -(2,1,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.2,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.2 * 0.99,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,2,1) because -(2,1,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, 12.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,2,1) because +(2,2,0)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, -0.5,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 1.5 * 0.99,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,2,1) because +(2,2,0)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, -8.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,2,1) because -(2,2,0)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -3.8,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 1.8 * 0.99,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,2,1) because -(2,2,0)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 1.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -0.8,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99});\n}\n\ntemplate <typename F>\nvoid test_limiting_2_1_2_coefficient(const F& helper) {\n  // Limit (2,1,2) because +(1,1,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,   5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0,  2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, -1.15, 4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,         6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0,        15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.85 * 0.99, -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,1,2) because +(1,1,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,   5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0,  2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, -10.0, 4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,1,2) because -(1,1,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,   5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0,  -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -3.05, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,         6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0,        15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 1.05 * 0.99, -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,1,2) because -(1,1,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, 22.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,1,2) because +(2,0,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,   3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0,  12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, -1.15, -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,         6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0,        15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.85 * 0.99, -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,1,2) because +(2,0,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, -4.0, -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,1,2) because -(2,0,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0,  12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -2.25, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,         6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0,        15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.25 * 0.99, -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,1,2) because -(2,0,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, 7.0,  43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,1,2) because +(2,1,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0,   6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, -1.33, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1,   3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,         6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0,        15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.67 * 0.99, -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,1,2) because +(2,1,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0,  6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, -4.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1,  3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.0,  -2.0, 2.0,  0.5 * 0.99});\n\n  // Limit (2,1,2) because -(2,1,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -3.75,   31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,         6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0,        15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 1.75 * 0.99, -2.0, 2.0,  0.5 * 0.99});\n\n  // Zero (2,1,2) because -(2,1,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -1.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 0.0,  -2.0, 2.0,  0.5 * 0.99});\n}\n\ntemplate <typename F>\nvoid test_limiting_1_2_2_coefficient(const F& helper) {\n  // Limit (1,2,2) because +(0,2,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,   3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0,  3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, -0.64, 2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,         -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0,        2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 1.36 * 0.99, 0.5 * 0.99});\n\n  // Zero (1,2,2) because +(0,2,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, -8.0, 2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 0.0,  0.5 * 0.99});\n\n  // Limit (1,2,2) because -(0,2,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,   11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0,  -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -3.13, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,         -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0,        2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 1.13 * 0.99, 0.5 * 0.99});\n\n  // Zero (1,2,2) because -(0,2,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -1.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 0.0,  0.5 * 0.99});\n\n  // Limit (1,2,2) because +(1,1,2)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,   2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0,  3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, -1.08, 4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,         -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0,        2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 0.92 * 0.99, 0.5 * 0.99});\n\n  // Skipping other (1,1,2) cases since they add limited value to the test\n\n  // Limit (1,2,2) because +(1,2,1)\n  helper({0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0, 3.0,\n          9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, 3.0, 4.1,\n          18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5, 0.0},\n         {0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n          18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0,   3.0,\n          9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, -1.03, 4.1,\n          18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1,   0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n          18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n          9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n          18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,         -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0,        2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 0.97 * 0.99, 0.5 * 0.99});\n\n  // Skipping other (1,2,1) cases since they add limited value to the test\n}\n\ntemplate <typename F>\nvoid test_limiting_0_1_2_coefficient_permutations(const F& helper) {\n  // Limit (2,1,0) because -(2,0,0)\n  helper({0.0,   10.0,  2.0,  3.0,   8.0,  5.0, 60.0,  3.0, 3.0,\n          9.0,   100.0, 11.0, 120.0, 11.8, 2.0, -15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,   2.5, 0.0},\n         {0.0,   1.0,   5.0,   30.0,  40.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,   100.0, -11.0, 120.0, -13.0, 3.0, 15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,   -21.0, 3.0,   4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,   20.0, 3.0,   44.0,  4.0, 60.0,  -7.0,  3.0,\n          90.0, 100.0, 10.0, 120.0, -13.0, 3.0, -15.0, -1.03, 4.1,\n          18.0, 19.0,  3.0,  21.0,  3.0,   4.1, 3.0,   4.1,   0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,    -10.0,  2.0,  3.0,    -4.0, 5.0,     -60.0, 11.0,    -5.0,\n          9.0,    -100.0, 11.0, -120.0, 13.8, -5.0,    150.0, -8.0,    1.0e-18,\n          -180.0, 49.0,   -5.0, 210.0,  -5.0, 1.0e-18, -5.0,  1.0e-18, 0.0},\n         {0.0,    1.0,   1.5,  -30.0,  -40.0, 8.0,     6.0,  7.0,     -5.0,\n          9.0,    -10.0, 11.5, -120.0, 130.0, -5.0,    15.0, -5.0,    1.0e-18,\n          -180.0, 190.0, -5.0, 43.0,   -5.0,  1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,   1.0,    -20.0, 3.0,    -44.0, 8.0,     -60.0, 8.0,     -5.0,\n          -90.0, -100.0, 28.0,  -120.0, 14.0,  -5.0,    31.0,  -5.0,    1.0e-18,\n          18.0,  19.0,   -5.0,  21.0,   -5.0,  1.0e-18, -5.0,  1.0e-18, 0.0},\n\n         {0.0,  1.0,         2.0,         3.0,  4.0,         0.5 * 0.99,\n          6.0,  7.0,         -2.0,        9.0,  10.0,        11.0,\n          12.0, 13.0,        -0.5 * 0.99, 15.0, -1.0 * 0.99, 2.0,\n          18.0, 19.0,        -0.99,       21.0, -1.0 * 0.99, 2.0,\n          -2.0, 0.97 * 0.99, 0.5 * 0.99});\n\n  // Limit (1,2,0) because +(0,2,0)\n  helper({0.0,   10.0,  2.0,  3.0,   80.0, 5.0, 8.0,   3.0, 3.0,\n          9.0,   100.0, 11.0, 120.0, 11.8, 2.0, -15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,   2.5, 0.0},\n         {0.0,   1.0,   50.0,  30.0,  40.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,   100.0, -11.0, 120.0, -13.0, 3.0, 15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,   -21.0, 3.0,   4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,   20.0, 3.0,   44.0,  4.0, 60.0,  -7.0,  3.0,\n          90.0, 100.0, 10.0, 120.0, -13.0, 3.0, -15.0, -1.03, 4.1,\n          18.0, 19.0,  3.0,  21.0,  3.0,   4.1, 3.0,   4.1,   0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,    -10.0,  2.0,  3.0,    -4.0, 5.0,     -60.0, 11.0,    -5.0,\n          9.0,    -100.0, 11.0, -120.0, 13.8, -5.0,    150.0, -8.0,    1.0e-18,\n          -180.0, 49.0,   -5.0, 210.0,  -5.0, 1.0e-18, -5.0,  1.0e-18, 0.0},\n         {0.0,    1.0,   -8.0, -30.0,  -40.0, 8.0,     6.0,  7.0,     -5.0,\n          9.0,    -10.0, 11.5, -120.0, 130.0, -5.0,    15.0, -5.0,    1.0e-18,\n          -180.0, 190.0, -8.0, 43.0,   -5.0,  1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,   1.0,    -20.0, 3.0,    -44.0, 8.0,     -60.0, 8.0,     -5.0,\n          -90.0, -100.0, 28.0,  -120.0, 14.0,  -5.0,    31.0,  -5.0,    1.0e-18,\n          18.0,  19.0,   -5.0,  21.0,   -5.0,  1.0e-18, -5.0,  1.0e-18, 0.0},\n\n         {0.0,  1.0,         2.0,         3.0,  4.0,         5.0,\n          6.0,  2.0 * 0.99,  -2.0,        9.0,  10.0,        11.0,\n          12.0, 13.0,        -0.5 * 0.99, 15.0, -1.0 * 0.99, 2.0,\n          18.0, 19.0,        -0.99,       21.0, -1.0 * 0.99, 2.0,\n          -2.0, 0.97 * 0.99, 0.5 * 0.99});\n\n  // Limit (2,0,1) because +(2,0,0)\n  helper({0.0,   10.0,  2.0,  3.0,   80.0, 5.0, 60.0,  3.0, 3.0,\n          9.0,   100.0, 11.0, 120.0, 11.8, 2.0, -15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,   2.5, 0.0},\n         {0.0,   1.0,   50.0,  30.0,  40.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,   100.0, -11.0, 120.0, -13.0, 3.0, 15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,   -21.0, 3.0,   4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,   3.0,  3.0,   44.0,  4.0, 60.0,  -7.0,  3.0,\n          90.0, 100.0, 10.0, 120.0, -13.0, 3.0, -15.0, -1.03, 4.1,\n          18.0, 19.0,  3.0,  21.0,  3.0,   4.1, 3.0,   4.1,   0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,    -10.0,  2.0,  3.0,    -4.0, 5.0,     -60.0, 11.0,    -5.0,\n          9.0,    -100.0, 11.0, -120.0, 13.8, -5.0,    150.0, -8.0,    1.0e-18,\n          -180.0, 49.0,   -5.0, 210.0,  -5.0, 1.0e-18, -5.0,  1.0e-18, 0.0},\n         {0.0,    1.0,   -8.0, -30.0,  -40.0, 8.0,     6.0,  7.0,     -5.0,\n          9.0,    -10.0, 11.5, -120.0, 130.0, -5.0,    15.0, -5.0,    1.0e-18,\n          -180.0, 190.0, -8.0, 43.0,   -5.0,  1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,   1.0,    -20.0, 3.0,    -44.0, 8.0,     -60.0, 8.0,     -5.0,\n          -90.0, -100.0, 28.0,  -120.0, 14.0,  -5.0,    31.0,  -5.0,    1.0e-18,\n          18.0,  19.0,   -5.0,  21.0,   -5.0,  1.0e-18, -5.0,  1.0e-18, 0.0},\n\n         {0.0,  1.0,         2.0,         3.0,  4.0,         5.0,\n          6.0,  7.0,         -2.0,        9.0,  10.0,        1.0 * 0.99,\n          12.0, 13.0,        -0.5 * 0.99, 15.0, -1.0 * 0.99, 2.0,\n          18.0, 19.0,        -0.99,       21.0, -1.0 * 0.99, 2.0,\n          -2.0, 0.97 * 0.99, 0.5 * 0.99});\n\n  // Limit (1,0,2) because -(0,0,2)\n  helper({0.0,   10.0,  2.0,  3.0,   80.0, 5.0, 60.0,  3.0, 3.0,\n          9.0,   100.0, 11.0, 120.0, 11.8, 2.0, -15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,   2.5, 0.0},\n         {0.0,   1.0,   50.0,  30.0,  40.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,   100.0, -11.0, 120.0, -13.0, 3.0, 15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,   -21.0, 3.0,   4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,   20.0, 3.0,   44.0,  4.0, 60.0,  -7.0,  3.0,\n          90.0, 100.0, 10.0, 120.0, -13.0, 3.0, -15.0, -1.03, 4.1,\n          18.0, 19.0,  3.0,  21.0,  3.0,   4.1, 3.0,   4.1,   0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,  -10.0,  2.0,  3.0,    -4.0, 5.0,     -60.0, 11.0,    -5.0,\n          9.0,  -100.0, 11.0, -120.0, 13.8, -5.0,    150.0, -8.0,    1.0e-18,\n          14.0, 49.0,   -5.0, 210.0,  -5.0, 1.0e-18, -5.0,  1.0e-18, 0.0},\n         {0.0,    1.0,   -8.0, -30.0,  -40.0, 8.0,     6.0,  7.0,     -5.0,\n          9.0,    -10.0, 11.5, -120.0, 130.0, -5.0,    15.0, -5.0,    1.0e-18,\n          -180.0, 190.0, -8.0, 43.0,   -5.0,  1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,   1.0,    -20.0, 3.0,    -44.0, 8.0,     -60.0, 8.0,     -5.0,\n          -90.0, -100.0, 28.0,  -120.0, 14.0,  -5.0,    31.0,  -5.0,    1.0e-18,\n          18.0,  19.0,   -5.0,  21.0,   -5.0,  1.0e-18, -5.0,  1.0e-18, 0.0},\n\n         {0.0,  1.0,         2.0,         3.0,  4.0,         5.0,\n          6.0,  7.0,         -2.0,        9.0,  10.0,        11.0,\n          12.0, 13.0,        -0.5 * 0.99, 15.0, -1.0 * 0.99, 2.0,\n          18.0, 4.0 * 0.99,  -0.99,       21.0, -1.0 * 0.99, 2.0,\n          -2.0, 0.97 * 0.99, 0.5 * 0.99});\n\n  // Limit (0,2,1) because +(0,2,0)\n  helper({0.0,   10.0,  2.0,  3.0,   80.0, 5.0, 60.0,  3.0, 3.0,\n          9.0,   100.0, 11.0, 120.0, 11.8, 2.0, -15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,   2.5, 0.0},\n         {0.0,   1.0,   50.0,  30.0,  40.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,   100.0, -11.0, 120.0, -13.0, 3.0, 15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,   -21.0, 3.0,   4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,   20.0, 3.0,   44.0,  4.0, 8.0,   -7.0,  3.0,\n          90.0, 100.0, 10.0, 120.0, -13.0, 3.0, -15.0, -1.03, 4.1,\n          18.0, 19.0,  3.0,  21.0,  3.0,   4.1, 3.0,   4.1,   0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,    -10.0,  2.0,  3.0,    -4.0, 5.0,     -60.0, 11.0,    -5.0,\n          9.0,    -100.0, 11.0, -120.0, 13.8, -5.0,    150.0, -8.0,    1.0e-18,\n          -180.0, 49.0,   -5.0, 210.0,  -5.0, 1.0e-18, -5.0,  1.0e-18, 0.0},\n         {0.0,    1.0,   -8.0, -30.0,  -40.0, 8.0,     6.0,  7.0,     -5.0,\n          9.0,    -10.0, 11.5, -120.0, 130.0, -5.0,    15.0, -5.0,    1.0e-18,\n          -180.0, 190.0, -8.0, 43.0,   -5.0,  1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,   1.0,    -20.0, 3.0,    -44.0, 8.0,     -60.0, 8.0,     -5.0,\n          -90.0, -100.0, 28.0,  -120.0, 14.0,  -5.0,    31.0,  -5.0,    1.0e-18,\n          18.0,  19.0,   -5.0,  21.0,   -5.0,  1.0e-18, -5.0,  1.0e-18, 0.0},\n\n         {0.0,  1.0,         2.0,         3.0,        4.0,         5.0,\n          6.0,  7.0,         -2.0,        9.0,        10.0,        11.0,\n          12.0, 13.0,        -0.5 * 0.99, 2.0 * 0.99, -1.0 * 0.99, 2.0,\n          18.0, 19.0,        -0.99,       21.0,       -1.0 * 0.99, 2.0,\n          -2.0, 0.97 * 0.99, 0.5 * 0.99});\n\n  // Limit (0,1, 2) because +(0,0,2)\n  helper({0.0,   10.0,  2.0,  3.0,   80.0, 5.0, 60.0,  3.0, 3.0,\n          9.0,   100.0, 11.0, 120.0, 11.8, 2.0, -15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,   2.5, 0.0},\n         {0.0,  1.0,   50.0,  30.0,  40.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,  100.0, -11.0, 120.0, -13.0, 3.0, 15.0, 3.0, 4.1,\n          21.0, -19.0, 3.0,   -21.0, 3.0,   4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,   20.0, 3.0,   44.0,  4.0, 60.0,  -7.0,  3.0,\n          90.0, 100.0, 10.0, 120.0, -13.0, 3.0, -15.0, -1.03, 4.1,\n          18.0, 19.0,  3.0,  21.0,  3.0,   4.1, 3.0,   4.1,   0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,    -10.0,  2.0,  3.0,    -4.0, 5.0,     -60.0, 11.0,    -5.0,\n          9.0,    -100.0, 11.0, -120.0, 13.8, -5.0,    150.0, -8.0,    1.0e-18,\n          -180.0, 49.0,   -5.0, 210.0,  -5.0, 1.0e-18, -5.0,  1.0e-18, 0.0},\n         {0.0,    1.0,   -8.0, -30.0,  -40.0, 8.0,     6.0,  7.0,     -5.0,\n          9.0,    -10.0, 11.5, -120.0, 130.0, -5.0,    15.0, -5.0,    1.0e-18,\n          -180.0, 190.0, -8.0, 43.0,   -5.0,  1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,   1.0,    -20.0, 3.0,    -44.0, 8.0,     -60.0, 8.0,     -5.0,\n          -90.0, -100.0, 28.0,  -120.0, 14.0,  -5.0,    31.0,  -5.0,    1.0e-18,\n          18.0,  19.0,   -5.0,  21.0,   -5.0,  1.0e-18, -5.0,  1.0e-18, 0.0},\n\n         {0.0,  1.0,         2.0,         3.0,        4.0,         5.0,\n          6.0,  7.0,         -2.0,        9.0,        10.0,        11.0,\n          12.0, 13.0,        -0.5 * 0.99, 15.0,       -1.0 * 0.99, 2.0,\n          18.0, 19.0,        -0.99,       3.0 * 0.99, -1.0 * 0.99, 2.0,\n          -2.0, 0.97 * 0.99, 0.5 * 0.99});\n\n  // Don't limit (0,1,2) permutations to verify we stop correctly\n  helper({0.0,   10.0,  2.0,  3.0,   80.0, 5.0, 60.0,  3.0, 3.0,\n          9.0,   100.0, 11.0, 120.0, 11.8, 2.0, -15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,   2.5, 0.0},\n         {0.0,   1.0,   50.0,  30.0,  40.0,  2.0, 6.0,  7.0, 3.0,\n          9.0,   100.0, -11.0, 120.0, -13.0, 3.0, 15.0, 3.0, 4.1,\n          180.0, -19.0, 3.0,   -21.0, 3.0,   4.1, 3.0,  4.1, 0.0},\n         {0.0,  1.0,   20.0, 3.0,   44.0,  4.0, 60.0,  -7.0,  3.0,\n          90.0, 100.0, 10.0, 120.0, -13.0, 3.0, -15.0, -1.03, 4.1,\n          18.0, 19.0,  3.0,  21.0,  3.0,   4.1, 3.0,   4.1,   0.0},\n\n         {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n          9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n          18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n\n         {0.0,    -10.0,  2.0,  3.0,    -4.0, 5.0,     -60.0, 11.0,    -5.0,\n          9.0,    -100.0, 11.0, -120.0, 13.8, -5.0,    150.0, -8.0,    1.0e-18,\n          -180.0, 49.0,   -5.0, 210.0,  -5.0, 1.0e-18, -5.0,  1.0e-18, 0.0},\n         {0.0,    1.0,   -8.0, -30.0,  -40.0, 8.0,     6.0,  7.0,     -5.0,\n          9.0,    -10.0, 11.5, -120.0, 130.0, -5.0,    15.0, -5.0,    1.0e-18,\n          -180.0, 190.0, -8.0, 43.0,   -5.0,  1.0e-18, -5.0, 1.0e-18, 0.0},\n         {0.0,   1.0,    -20.0, 3.0,    -44.0, 8.0,     -60.0, 8.0,     -5.0,\n          -90.0, -100.0, 28.0,  -120.0, 14.0,  -5.0,    31.0,  -5.0,    1.0e-18,\n          18.0,  19.0,   -5.0,  21.0,   -5.0,  1.0e-18, -5.0,  1.0e-18, 0.0},\n\n         {0.0,  1.0,         2.0,         3.0,  4.0,         5.0,\n          6.0,  7.0,         -2.0,        9.0,  10.0,        11.0,\n          12.0, 13.0,        -0.5 * 0.99, 15.0, -1.0 * 0.99, 2.0,\n          18.0, 19.0,        -0.99,       21.0, -1.0 * 0.99, 2.0,\n          -2.0, 0.97 * 0.99, 0.5 * 0.99});\n}\n\nvoid test_limiting_different_values_different_tensors() {\n  INFO(\"Testing different values for each tensor component\");\n  static constexpr size_t dim = 3;\n  const size_t order = 2;  // Use only 3 coefficients because more is tedious...\n  const Mesh<dim> mesh(order + 1, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const size_t num_pts = mesh.number_of_grid_points();\n  const auto x = logical_coordinates(mesh);\n  using Limiter = Krivodonova<dim, tmpl::list<ScalarTag<0>, VectorTag<dim, 0>>>;\n  // Use non-unity but close alpha values to make the math easier but still\n  // test thoroughly.\n  Limiter krivodonova{\n      make_array<Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(\n          0.99)};\n\n  NeighborData<dim, typename Limiter::PackagedData> neighbor_data{};\n\n  const Element<dim> element(ElementId<dim>{0}, {});\n  // We don't care about the ElementId for these tests, just the direction.\n  Limiter::PackagedData& package_data_up_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_up_eta = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_eta(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_eta = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_eta(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_up_zeta =\n      neighbor_data[DirectionalId<dim>{Direction<dim>::upper_zeta(),\n                                       ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_zeta =\n      neighbor_data[DirectionalId<dim>{Direction<dim>::lower_zeta(),\n                                       ElementId<dim>{0}}];\n\n  package_data_up_xi.modal_volume_data.initialize(num_pts);\n  package_data_up_xi.mesh = mesh;\n  package_data_lo_xi.modal_volume_data.initialize(num_pts);\n  package_data_lo_xi.mesh = mesh;\n  package_data_up_eta.modal_volume_data.initialize(num_pts);\n  package_data_up_eta.mesh = mesh;\n  package_data_lo_eta.modal_volume_data.initialize(num_pts);\n  package_data_lo_eta.mesh = mesh;\n  package_data_up_zeta.modal_volume_data.initialize(num_pts);\n  package_data_up_zeta.mesh = mesh;\n  package_data_lo_zeta.modal_volume_data.initialize(num_pts);\n  package_data_lo_zeta.mesh = mesh;\n\n  Scalar<DataVector> nodal_scalar_data_to_limit(num_pts, 0.0);\n  tnsr::I<DataVector, dim> nodal_vector_data_to_limit(num_pts, 0.0);\n  Scalar<DataVector> nodal_scalar_expected(num_pts, 0.0);\n  tnsr::I<DataVector, dim> nodal_vector_expected(num_pts, 0.0);\n\n  // Limit (2,2,1) because +(1,2,1)\n  to_nodal_coefficients(&get(nodal_scalar_data_to_limit),\n                        {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n                         9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n                         18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n                        mesh);\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_up_xi.modal_volume_data)) =\n      ModalVector{0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,  3.0,  3.0,\n                  9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0, -1.1, 4.1,\n                  18.0, -19.0, 3.0,  21.0, 3.0,  4.1, 3.0,  2.5,  0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_lo_xi.modal_volume_data)) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n                  9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n                  18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_up_eta.modal_volume_data)) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n                  9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n                  18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(package_data_lo_eta.modal_volume_data)) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n                  9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n                  18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(\n      package_data_up_zeta.modal_volume_data)) =\n      ModalVector{0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n                  9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n                  18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0};\n  get(get<::Tags::Modal<ScalarTag<0>>>(\n      package_data_lo_zeta.modal_volume_data)) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n                  9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n                  18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  to_nodal_coefficients(\n      &get(nodal_scalar_expected),\n      {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n       9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 0.9 * 0.99,\n       18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  0.5 * 0.99},\n      mesh);\n\n  // Limit (2,1,2) because +(1,1,2)\n  to_nodal_coefficients(&nodal_vector_data_to_limit.get(0),\n                        {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n                         9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n                         18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n                        mesh);\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_xi.modal_volume_data)\n      .get(0) =\n      ModalVector{0.0,  1.0,   2.0,  3.0,  4.0,   5.0, 6.0,  3.0, 3.0,\n                  9.0,  10.0,  11.0, 12.0, 13.0,  2.0, 15.0, 3.0, 4.1,\n                  18.0, -19.0, 3.0,  21.0, -1.15, 4.1, 3.0,  2.5, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_xi.modal_volume_data)\n      .get(0) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n                  9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n                  18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_eta.modal_volume_data)\n      .get(0) = ModalVector{0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n                            9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n                            18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_eta.modal_volume_data)\n      .get(0) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n                  9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n                  18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_zeta.modal_volume_data)\n      .get(0) =\n      ModalVector{0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n                  9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n                  18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_zeta.modal_volume_data)\n      .get(0) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n                  9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n                  18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  to_nodal_coefficients(\n      &nodal_vector_expected.get(0),\n      {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,         6.0,  7.0,  -2.0,\n       9.0,  10.0, 11.0, 12.0, 13.0, -2.0,        15.0, -2.0, 2.0,\n       18.0, 19.0, -2.0, 21.0, -2.0, 0.85 * 0.99, -2.0, 2.0,  0.5 * 0.99},\n      mesh);\n\n  // Limit (1,2,2) because +(0,2,2)\n  to_nodal_coefficients(&nodal_vector_data_to_limit.get(1),\n                        {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n                         9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n                         18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n                        mesh);\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_xi.modal_volume_data)\n      .get(1) =\n      ModalVector{0.0,  1.0,   2.0,  3.0,  4.0,  5.0, 6.0,   3.0, 3.0,\n                  9.0,  10.0,  11.0, 12.0, 13.0, 2.0, 15.0,  3.0, 4.1,\n                  18.0, -19.0, 3.0,  21.0, 3.0,  4.1, -0.64, 2.5, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_xi.modal_volume_data)\n      .get(1) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  11.0,    -5.0,\n                  9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -8.0,    1.0e-18,\n                  18.0, 49.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_eta.modal_volume_data)\n      .get(1) = ModalVector{0.0,  1.0,  2.0,  3.0,   4.0,  2.0, 6.0,  7.0, 3.0,\n                            9.0,  10.0, 11.0, 12.0,  13.0, 3.0, 15.0, 3.0, 4.1,\n                            18.0, 19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,  4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_eta.modal_volume_data)\n      .get(1) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  8.0,     6.0,  7.0,     -5.0,\n                  9.0,  10.0, 11.0, 12.0, 13.0, -5.0,    15.0, -5.0,    1.0e-18,\n                  18.0, 19.0, -5.0, 43.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_zeta.modal_volume_data)\n      .get(1) =\n      ModalVector{0.0,  1.0,  2.0,   3.0,  4.0,  5.0, 6.0,   7.0, 3.0,\n                  9.0,  10.0, -11.0, 12.0, 13.0, 3.0, -15.0, 3.0, 4.1,\n                  18.0, 19.0, 3.0,   21.0, 3.0,  4.1, 3.0,   4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_zeta.modal_volume_data)\n      .get(1) =\n      ModalVector{0.0,  1.0,  2.0,  3.0,  4.0,  5.0,     6.0,  7.0,     -5.0,\n                  9.0,  10.0, 28.0, 12.0, 13.0, -5.0,    31.0, -5.0,    1.0e-18,\n                  18.0, 19.0, -5.0, 21.0, -5.0, 1.0e-18, -5.0, 1.0e-18, 0.0};\n  to_nodal_coefficients(\n      &nodal_vector_expected.get(1),\n      {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,         -2.0,\n       9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0,        2.0,\n       18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 1.36 * 0.99, 0.5 * 0.99},\n      mesh);\n\n  // Limit (2,1,0) because -(2,0,0)\n  to_nodal_coefficients(&nodal_vector_data_to_limit.get(2),\n                        {0.0,  1.0,  2.0,  3.0,  4.0,  5.0,  6.0,  7.0,  -2.0,\n                         9.0,  10.0, 11.0, 12.0, 13.0, -2.0, 15.0, -2.0, 2.0,\n                         18.0, 19.0, -2.0, 21.0, -2.0, 2.0,  -2.0, 2.0,  1.0},\n                        mesh);\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_xi.modal_volume_data)\n      .get(2) =\n      ModalVector{0.0,   10.0,  2.0,  3.0,   8.0,  5.0, 60.0,  3.0, 3.0,\n                  9.0,   100.0, 11.0, 120.0, 11.8, 2.0, -15.0, 3.0, 4.1,\n                  180.0, -19.0, 3.0,  -21.0, 3.0,  4.1, 3.0,   2.5, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_xi.modal_volume_data)\n      .get(2) = ModalVector{\n      0.0,    -10.0,  2.0,  3.0,    -4.0, 5.0,     -60.0, 11.0,    -5.0,\n      9.0,    -100.0, 11.0, -120.0, 13.8, -5.0,    150.0, -8.0,    1.0e-18,\n      -180.0, 49.0,   -5.0, 210.0,  -5.0, 1.0e-18, -5.0,  1.0e-18, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_eta.modal_volume_data)\n      .get(2) =\n      ModalVector{0.0,   1.0,   5.0,   30.0,  40.0,  2.0, 6.0,  7.0, 3.0,\n                  9.0,   100.0, -11.0, 120.0, -13.0, 3.0, 15.0, 3.0, 4.1,\n                  180.0, -19.0, 3.0,   -21.0, 3.0,   4.1, 3.0,  4.1, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_eta.modal_volume_data)\n      .get(2) = ModalVector{\n      0.0,    1.0,   1.5,  -30.0,  -40.0, 8.0,     6.0,  7.0,     -5.0,\n      9.0,    -10.0, 11.5, -120.0, 130.0, -5.0,    15.0, -5.0,    1.0e-18,\n      -180.0, 190.0, -5.0, 43.0,   -5.0,  1.0e-18, -5.0, 1.0e-18, 0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_up_zeta.modal_volume_data)\n      .get(2) =\n      ModalVector{0.0,  1.0,   20.0, 3.0,   44.0,  4.0, 60.0,  -7.0,  3.0,\n                  90.0, 100.0, 10.0, 120.0, -13.0, 3.0, -15.0, -1.03, 4.1,\n                  18.0, 19.0,  3.0,  21.0,  3.0,   4.1, 3.0,   4.1,   0.0};\n  get<::Tags::Modal<VectorTag<dim, 0>>>(package_data_lo_zeta.modal_volume_data)\n      .get(2) = ModalVector{\n      0.0,   1.0,    -20.0, 3.0,    -44.0, 8.0,     -60.0, 8.0,     -5.0,\n      -90.0, -100.0, 28.0,  -120.0, 14.0,  -5.0,    31.0,  -5.0,    1.0e-18,\n      18.0,  19.0,   -5.0,  21.0,   -5.0,  1.0e-18, -5.0,  1.0e-18, 0.0};\n  to_nodal_coefficients(\n      &nodal_vector_expected.get(2),\n      {0.0,  1.0,         2.0,         3.0,  4.0,         0.5 * 0.99,\n       6.0,  7.0,         -2.0,        9.0,  10.0,        11.0,\n       12.0, 13.0,        -0.5 * 0.99, 15.0, -1.0 * 0.99, 2.0,\n       18.0, 19.0,        -0.99,       21.0, -1.0 * 0.99, 2.0,\n       -2.0, 0.97 * 0.99, 0.5 * 0.99},\n      mesh);\n\n  krivodonova(&nodal_scalar_data_to_limit, &nodal_vector_data_to_limit, element,\n              mesh, neighbor_data);\n  CHECK_ITERABLE_APPROX(get(nodal_scalar_data_to_limit),\n                        get(nodal_scalar_expected));\n  for (size_t i = 0; i < dim; ++i) {\n    CAPTURE(i);\n    CHECK_ITERABLE_APPROX(nodal_vector_data_to_limit.get(i),\n                          nodal_vector_expected.get(i));\n  }\n}\n\nvoid run() {\n  INFO(\"Testing 3d limiter\");\n  test_package_data();\n\n  INFO(\"Testing applying limiter to coefficients\");\n  static constexpr size_t dim = 3;\n  const size_t order = 2;  // Use only 3 coefficients because more is tedious...\n  const Mesh<dim> mesh(order + 1, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const size_t num_pts = mesh.number_of_grid_points();\n  using Limiter = Krivodonova<dim, tmpl::list<ScalarTag<0>, VectorTag<dim, 0>>>;\n  // Use non-unity but close alpha values to make the math easier but still\n  // test thoroughly.\n  Limiter krivodonova{\n      make_array<Spectral::maximum_number_of_points<Spectral::Basis::Legendre>>(\n          0.99)};\n\n  NeighborData<dim, typename Limiter::PackagedData> neighbor_data{};\n\n  const Element<dim> element(ElementId<dim>{0}, {});\n  // We don't care about the ElementId for these tests, just the direction.\n  Limiter::PackagedData& package_data_up_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_xi = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_xi(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_up_eta = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::upper_eta(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_eta = neighbor_data[DirectionalId<dim>{\n      Direction<dim>::lower_eta(), ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_up_zeta =\n      neighbor_data[DirectionalId<dim>{Direction<dim>::upper_zeta(),\n                                       ElementId<dim>{0}}];\n  Limiter::PackagedData& package_data_lo_zeta =\n      neighbor_data[DirectionalId<dim>{Direction<dim>::lower_zeta(),\n                                       ElementId<dim>{0}}];\n\n  package_data_up_xi.modal_volume_data.initialize(num_pts);\n  package_data_up_xi.mesh = mesh;\n  package_data_lo_xi.modal_volume_data.initialize(num_pts);\n  package_data_lo_xi.mesh = mesh;\n  package_data_up_eta.modal_volume_data.initialize(num_pts);\n  package_data_up_eta.mesh = mesh;\n  package_data_lo_eta.modal_volume_data.initialize(num_pts);\n  package_data_lo_eta.mesh = mesh;\n  package_data_up_zeta.modal_volume_data.initialize(num_pts);\n  package_data_up_zeta.mesh = mesh;\n  package_data_lo_zeta.modal_volume_data.initialize(num_pts);\n  package_data_lo_zeta.mesh = mesh;\n\n  Scalar<DataVector> nodal_scalar_data_to_limit(num_pts, 0.0);\n  tnsr::I<DataVector, dim> nodal_vector_data_to_limit(num_pts, 0.0);\n  DataVector expected(num_pts);\n  const auto helper =\n      [&element, &expected, &krivodonova, &mesh, &neighbor_data,\n       &package_data_lo_xi, &package_data_up_xi, &package_data_lo_eta,\n       &package_data_up_eta, &package_data_lo_zeta, &package_data_up_zeta,\n       &nodal_scalar_data_to_limit, &nodal_vector_data_to_limit](\n          const ModalVector& up_xi_coeffs, const ModalVector& up_eta_coeffs,\n          const ModalVector& up_zeta_coeffs, const ModalVector& initial_coeffs,\n          const ModalVector& lo_xi_coeffs, const ModalVector& lo_eta_coeffs,\n          const ModalVector& lo_zeta_coeffs,\n          const ModalVector& expected_coeffs) {\n        to_nodal_coefficients(&get(nodal_scalar_data_to_limit), initial_coeffs,\n                              mesh);\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_up_xi.modal_volume_data)) = up_xi_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_lo_xi.modal_volume_data)) = lo_xi_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_up_eta.modal_volume_data)) = up_eta_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_lo_eta.modal_volume_data)) = lo_eta_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_up_zeta.modal_volume_data)) = up_zeta_coeffs;\n        get(get<::Tags::Modal<ScalarTag<0>>>(\n            package_data_lo_zeta.modal_volume_data)) = lo_zeta_coeffs;\n\n        for (size_t i = 0; i < dim; ++i) {\n          to_nodal_coefficients(&nodal_vector_data_to_limit.get(i),\n                                initial_coeffs, mesh);\n          get<::Tags::Modal<VectorTag<dim, 0>>>(\n              package_data_up_xi.modal_volume_data)\n              .get(i) = up_xi_coeffs;\n          get<::Tags::Modal<VectorTag<dim, 0>>>(\n              package_data_lo_xi.modal_volume_data)\n              .get(i) = lo_xi_coeffs;\n          get<::Tags::Modal<VectorTag<dim, 0>>>(\n              package_data_up_eta.modal_volume_data)\n              .get(i) = up_eta_coeffs;\n          get<::Tags::Modal<VectorTag<dim, 0>>>(\n              package_data_lo_eta.modal_volume_data)\n              .get(i) = lo_eta_coeffs;\n          get<::Tags::Modal<VectorTag<dim, 0>>>(\n              package_data_up_zeta.modal_volume_data)\n              .get(i) = up_zeta_coeffs;\n          get<::Tags::Modal<VectorTag<dim, 0>>>(\n              package_data_lo_zeta.modal_volume_data)\n              .get(i) = lo_zeta_coeffs;\n        }\n\n        krivodonova(&nodal_scalar_data_to_limit, &nodal_vector_data_to_limit,\n                    element, mesh, neighbor_data);\n        to_nodal_coefficients(&expected, expected_coeffs, mesh);\n        CHECK_ITERABLE_APPROX(get(nodal_scalar_data_to_limit), expected);\n        for (size_t i = 0; i < dim; ++i) {\n          CHECK_ITERABLE_APPROX(nodal_vector_data_to_limit.get(i), expected);\n        }\n      };\n\n  // Map between 3D and 1D coefficients:\n  // [(0,0,0), (1,0,0), (2,0,0), (0,1,0), (1,1,0), (2,1,0), (0,2,0), (1,2,0),\n  //  (0,       1,       2,       3,       4,       5,       6,       7,\n  //\n  //  (2,2,0), (0,0,1), (1,0,1), (2,0,1), (0,1,1), (1,1,1), (2,1,1), (0,2,1),\n  //   8,       9,       10,      11,      12,      13,      14,      15,\n  //\n  //  (1,2,1), (2,2,1), (0,0,2), (1,0,2), (2,0,2), (0,1,2), (1,1,2), (2,1,2),\n  //   16,      17,      18,      19,      20,      21,      22,      23,\n  //\n  //  (0,2,2), (1,2,2), (2,2,2)]\n  //   24,      25,      26)\n\n  test_limiting_2_2_2_coefficient(helper);\n  test_limiting_2_2_1_coefficient(helper);\n  test_limiting_2_1_2_coefficient(helper);\n  test_limiting_1_2_2_coefficient(helper);\n  test_limiting_0_1_2_coefficient_permutations(helper);\n\n  test_limiting_different_values_different_tensors();\n}\n}  // namespace test_3d\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.Krivodonova\",\n                  \"[Limiters][Unit]\") {\n  test_1d::run();\n  test_2d::run();\n  test_3d::run();\n}\n}  // namespace Limiters\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_LimiterActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <functional>\n#include <map>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/LimiterActions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TemporalId : db::SimpleTag {\n  using type = int;\n};\n\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr const size_t volume_dim = Dim;\n  using variables_tag = Tags::Variables<tmpl::list<Var>>;\n};\n\nclass DummyLimiterForTest {\n public:\n  // Data sent by the limiter to its neighbors\n  struct PackagedData {\n    double mean_;\n    Mesh<2> mesh_;\n  };\n  using package_argument_tags = tmpl::list<Var, domain::Tags::Mesh<2>>;\n  // We ignore clang-tidy and instead match the interface of the \"real\"\n  // limiter implementations, where the package_data function will generally\n  // not be static.\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  void package_data(const gsl::not_null<PackagedData*> packaged_data,\n                    const Scalar<DataVector>& var, const Mesh<2>& mesh,\n                    const OrientationMap<2>& orientation_map) const {\n    packaged_data->mean_ = mean_value(get(var), mesh);\n    packaged_data->mesh_ = orientation_map(mesh);\n  }\n\n  using limit_tags = tmpl::list<Var>;\n  using limit_argument_tags =\n      tmpl::list<domain::Tags::Mesh<2>, domain::Tags::Element<2>>;\n  void operator()(const gsl::not_null<typename Var::type*> var,\n                  const Mesh<2>& /*mesh*/, const Element<2>& /*element*/,\n                  const std::unordered_map<DirectionalId<2>,\n                                           DummyLimiterForTest::PackagedData,\n                                           boost::hash<DirectionalId<2>>>&\n                      neighbor_packaged_data) const {\n    // Zero the data as an easy check that the limiter got called\n    get(*var) = 0.;\n    for (const auto& data : neighbor_packaged_data) {\n      get(*var) += data.second.mean_;\n    }\n  }\n\n  void pup(const PUP::er& /*p*/) const {}\n};\n\nstruct LimiterTag : db::SimpleTag {\n  using type = DummyLimiterForTest;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using const_global_cache_tags = tmpl::list<LimiterTag>;\n  using simple_tags = db::AddSimpleTags<TemporalId, domain::Tags::Mesh<Dim>,\n                                        domain::Tags::Element<Dim>,\n                                        domain::Tags::ElementMap<Dim>, Var>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<\n                     Parallel::Phase::Initialization,\n                     tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Testing,\n                     tmpl::list<Limiters::Actions::SendData<Metavariables>,\n                                Limiters::Actions::Limit<Metavariables>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using limiter = LimiterTag;\n  using system = System<Dim>;\n  using temporal_id = TemporalId;\n  static constexpr bool local_time_stepping = false;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.LimiterActions.Generic\",\n                  \"[Unit][NumericalAlgorithms][Actions]\") {\n  using metavariables = Metavariables<2>;\n  using my_component = component<2, metavariables>;\n  using limiter_comm_tag =\n      Limiters::Tags::LimiterCommunicationTag<metavariables>;\n\n  const Mesh<2> mesh{\n      {{3, 4}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n  //      xi      Block       +- xi\n  //      |     0   |   1     |\n  // eta -+ +-------+-+-+---+ eta\n  //        |       |X| |   |\n  //        |       +-+-+   |\n  //        |       | | |   |\n  //        +-------+-+-+---+\n  // We run the actions on the indicated element.  The blocks are square.\n  const ElementId<2> self_id(1, {{{2, 0}, {1, 0}}});\n  const ElementId<2> west_id(0);\n  const ElementId<2> east_id(1, {{{2, 1}, {1, 0}}});\n  const ElementId<2> south_id(1, {{{2, 0}, {1, 1}}});\n\n  // OrientationMap from block 1 to block 0\n  const OrientationMap<2> block_orientation(\n      {{Direction<2>::upper_xi(), Direction<2>::upper_eta()}},\n      {{Direction<2>::lower_eta(), Direction<2>::lower_xi()}});\n\n  // Since we're lazy and use the same map for both blocks (the\n  // actions are only sensitive to the ElementMap, which does differ),\n  // we need to make the xi and eta maps line up along the block\n  // interface.\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  PUPable_reg(SINGLE_ARG(\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine2D>));\n  const Affine xi_map{-1., 1., 3., 7.};\n  const Affine eta_map{-1., 1., 7., 3.};\n\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine2D(xi_map, eta_map));\n\n  const struct {\n    std::unordered_map<Direction<2>, Scalar<DataVector>> var;\n  } test_data{\n      {{Direction<2>::lower_xi(),\n        Scalar<DataVector>(mesh.number_of_grid_points(), 5.)},\n       {Direction<2>::upper_xi(),\n        Scalar<DataVector>(mesh.number_of_grid_points(), 6.)},\n       {Direction<2>::upper_eta(),\n        Scalar<DataVector>(mesh.number_of_grid_points(), 7.)}},\n  };\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {DummyLimiterForTest{}}};\n\n  {\n    Element<2> element(\n        self_id, {{Direction<2>::lower_xi(), {{west_id}, block_orientation}},\n                  {Direction<2>::upper_xi(),\n                   {{east_id}, OrientationMap<2>::create_aligned()}},\n                  {Direction<2>::upper_eta(),\n                   {{south_id}, OrientationMap<2>::create_aligned()}}});\n    ActionTesting::emplace_component_and_initialize<my_component>(\n        &runner, self_id,\n        {0, mesh, element,\n         ElementMap<2, Frame::Inertial>(self_id, coordmap->get_clone()),\n         Scalar<DataVector>(mesh.number_of_grid_points(), 1234.)});\n  }\n\n  const auto emplace_neighbor = [&mesh, &self_id, &coordmap, &runner](\n                                    const ElementId<2>& id,\n                                    const Direction<2>& direction,\n                                    const OrientationMap<2>& orientation,\n                                    const Scalar<DataVector>& var) {\n    const Element<2> element(id, {{direction, {{self_id}, orientation}}});\n    auto map = ElementMap<2, Frame::Inertial>(id, coordmap->get_clone());\n    ActionTesting::emplace_component_and_initialize<my_component>(\n        &runner, id, {0, mesh, element, std::move(map), var});\n  };\n\n  emplace_neighbor(south_id, Direction<2>::lower_eta(),\n                   OrientationMap<2>::create_aligned(),\n                   test_data.var.at(Direction<2>::upper_eta()));\n  emplace_neighbor(east_id, Direction<2>::lower_xi(),\n                   OrientationMap<2>::create_aligned(),\n                   test_data.var.at(Direction<2>::upper_xi()));\n  emplace_neighbor(west_id, Direction<2>::lower_eta(),\n                   block_orientation.inverse_map(),\n                   test_data.var.at(Direction<2>::lower_xi()));\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Call SendDataForLimiter on self, sending data to neighbors\n  runner.next_action<my_component>(self_id);\n\n  // Here, we just check that messages are sent to the correct places.\n  // We do not check the contents. We will check the contents of received\n  // messages on self later\n  {\n    CHECK(runner.nonempty_inboxes<my_component, limiter_comm_tag>() ==\n          std::unordered_set<ElementId<2>>{west_id, east_id, south_id});\n    const auto check_sent_data = [&runner, &self_id](\n                                     const ElementId<2>& id,\n                                     const Direction<2>& direction) {\n      const auto& inboxes = runner.inboxes<my_component>();\n      const auto& inbox = tuples::get<limiter_comm_tag>(inboxes.at(id));\n      CHECK(inbox.size() == 1);\n      CHECK(inbox.count(0) == 1);\n      const auto& inbox_at_time = inbox.at(0);\n      CHECK(inbox_at_time.size() == 1);\n      CHECK(inbox_at_time.count({direction, self_id}) == 1);\n    };\n    check_sent_data(west_id, Direction<2>::lower_eta());\n    check_sent_data(east_id, Direction<2>::lower_xi());\n    check_sent_data(south_id, Direction<2>::lower_eta());\n  }\n\n  // Now we check ApplyLimiter\n  REQUIRE_FALSE(runner.next_action_if_ready<my_component>(self_id));\n  runner.next_action<my_component>(south_id);\n  REQUIRE_FALSE(runner.next_action_if_ready<my_component>(self_id));\n  runner.next_action<my_component>(east_id);\n  REQUIRE_FALSE(runner.next_action_if_ready<my_component>(self_id));\n  runner.next_action<my_component>(west_id);\n\n  // Here we check that the inbox is correctly filled with information from\n  // neighbors.\n  {\n    const auto check_inbox =\n        [&runner, &self_id](\n            const ElementId<2>& id, const Direction<2>& direction,\n            const double expected_mean_data, const Mesh<2>& expected_mesh) {\n          const auto received_package =\n              tuples::get<limiter_comm_tag>(\n                  runner.inboxes<my_component>().at(self_id))\n                  .at(0)\n                  .at(DirectionalId<2>{direction, id});\n          CHECK(received_package.mean_ == approx(expected_mean_data));\n          CHECK(received_package.mesh_ == expected_mesh);\n        };\n\n    const Mesh<2> rotated_mesh{{{4, 3}},\n                               Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n    check_inbox(west_id, Direction<2>::lower_xi(), 5., rotated_mesh);\n    check_inbox(east_id, Direction<2>::upper_xi(), 6., mesh);\n    check_inbox(south_id, Direction<2>::upper_eta(), 7., mesh);\n  }\n\n  // Now we run the ApplyLimiter action. We verify the pre- and post-limiting\n  // state of the variable being limited.\n  const auto& var_to_limit =\n      ActionTesting::get_databox_tag<my_component, Var>(runner, self_id);\n  CHECK_ITERABLE_APPROX(\n      var_to_limit, Scalar<DataVector>(mesh.number_of_grid_points(), 1234.));\n\n  runner.next_action<my_component>(self_id);\n\n  // Expected value: 18 = 5 + 6 + 7 from the three neighbors.\n  CHECK_ITERABLE_APPROX(var_to_limit,\n                        Scalar<DataVector>(mesh.number_of_grid_points(), 18.));\n\n  // Check that data for this time (t=0) was deleted from the inbox after the\n  // limiter call. Note that we only put in t=0 data, so the whole inbox should\n  // be empty.\n  CHECK(\n      tuples::get<limiter_comm_tag>(runner.inboxes<my_component>().at(self_id))\n          .empty());\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.LimiterActions.NoNeighbors\",\n                  \"[Unit][NumericalAlgorithms][Actions]\") {\n  using metavariables = Metavariables<2>;\n  using my_component = component<2, metavariables>;\n  using limiter_comm_tag =\n      Limiters::Tags::LimiterCommunicationTag<metavariables>;\n\n  const Mesh<2> mesh{\n      {{3, 4}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n  const ElementId<2> self_id(1, {{{2, 0}, {1, 0}}});\n  const Element<2> element(self_id, {});\n\n  auto input_var = Scalar<DataVector>(mesh.number_of_grid_points(), 1234.);\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  PUPable_reg(SINGLE_ARG(\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Affine2D>));\n  const Affine xi_map{-1., 1., 3., 7.};\n  const Affine eta_map{-1., 1., 7., 3.};\n  auto map = ElementMap<2, Frame::Inertial>(\n      self_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine2D(xi_map, eta_map)));\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {DummyLimiterForTest{}}};\n\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, self_id,\n      {0, mesh, element, std::move(map), std::move(input_var)});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Call SendDataForLimiter on self. Expect empty inboxes all around.\n  runner.next_action<my_component>(self_id);\n\n  CHECK(runner.nonempty_inboxes<my_component, limiter_comm_tag>().empty());\n  CHECK(\n      tuples::get<limiter_comm_tag>(runner.inboxes<my_component>().at(self_id))\n          .empty());\n\n  // Now we run the ApplyLimiter action, checking pre and post values.\n  const auto& var_to_limit =\n      ActionTesting::get_databox_tag<my_component, Var>(runner, self_id);\n  CHECK_ITERABLE_APPROX(\n      var_to_limit, Scalar<DataVector>(mesh.number_of_grid_points(), 1234.));\n\n  runner.next_action<my_component>(self_id);\n\n  CHECK_ITERABLE_APPROX(var_to_limit,\n                        Scalar<DataVector>(mesh.number_of_grid_points(), 0.));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_LimiterActionsWithMinmod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Minmod.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/LimiterActions.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TemporalId : db::SimpleTag {\n  using type = int;\n};\n\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr const size_t volume_dim = Dim;\n  using variables_tag = Tags::Variables<tmpl::list<Var>>;\n};\n\nstruct LimiterTag : db::SimpleTag {\n  using type = Limiters::Minmod<2, tmpl::list<Var>>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using const_global_cache_tags = tmpl::list<LimiterTag>;\n  using simple_tags = db::AddSimpleTags<\n      TemporalId, domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n      domain::Tags::ElementMap<Dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<2, Frame::Grid,\n                                                  Frame::Inertial>,\n      ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize, Var>;\n  using compute_tags = db::AddComputeTags<\n      ::domain::Tags::LogicalCoordinates<Dim>,\n      ::domain::Tags::MappedCoordinates<\n          ::domain::Tags::ElementMap<Dim, Frame::Grid>,\n          ::domain::Tags::Coordinates<Dim, Frame::ElementLogical>>,\n      domain::Tags::SizeOfElementCompute<Dim>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Limiters::Actions::SendData<Metavariables>,\n                     Limiters::Actions::Limit<Metavariables>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using limiter = LimiterTag;\n  using system = System<Dim>;\n  using temporal_id = TemporalId;\n  static constexpr bool local_time_stepping = false;\n};\n}  // namespace\n\n// This test checks that the Minmod limiter's interfaces and type aliases\n// succesfully integrate with the limiter actions. It does this by compiling\n// together the Minmod limiter and the actions, then making calls to the\n// SendData and the Limit actions. No checks are performed here that the limiter\n// and/or actions produce correct output: that is done in other tests.\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.LimiterActions.Minmod\",\n                  \"[Unit][NumericalAlgorithms][Actions]\") {\n  using metavariables = Metavariables<2>;\n  using my_component = component<2, metavariables>;\n\n  const Mesh<2> mesh{3, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const ElementId<2> self_id(1, {{{2, 0}, {1, 0}}});\n  const Element<2> element(self_id, {});\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using CubicScaleMap = domain::CoordinateMaps::TimeDependent::CubicScale<2>;\n  PUPable_reg(SINGLE_ARG(\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Grid, Affine2D>));\n  PUPable_reg(SINGLE_ARG(\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, CubicScaleMap>));\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  const Affine xi_map{-1., 1., 3., 7.};\n  const Affine eta_map{-1., 1., 7., 3.};\n  auto logical_to_grid_map = ElementMap<2, Frame::Grid>(\n      self_id,\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          Affine2D(xi_map, eta_map)));\n  std::unique_ptr<domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 2>>\n      grid_to_inertial_map =\n          domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n              CubicScaleMap{10.0, \"Expansion\", \"Expansion\"});\n\n  const double initial_time = 0.0;\n  const double expiration_time = 2.5;\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time.insert(std::make_pair(\n      \"Expansion\",\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time, std::array<DataVector, 3>{{{0.0}, {1.0}, {0.0}}},\n          expiration_time)));\n\n  auto var = Scalar<DataVector>(mesh.number_of_grid_points(), 1234.);\n\n  const double tvb_constant = 0.0;\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      Limiters::Minmod<2, tmpl::list<Var>>(Limiters::MinmodType::LambdaPi1,\n                                           tvb_constant)};\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, self_id,\n      {0, mesh, element, std::move(logical_to_grid_map),\n       std::move(grid_to_inertial_map), 1.0, std::move(functions_of_time),\n       std::move(var)});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // SendData\n  runner.next_action<my_component>(self_id);\n  // Limit\n  CHECK(runner.next_action_if_ready<my_component>(self_id));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_Minmod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <functional>\n#include <limits>\n#include <random>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Minmod.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"Scalar\"; }\n};\n\ntemplate <size_t VolumeDim>\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, VolumeDim>;\n  static std::string name() { return \"Vector\"; }\n};\n\nvoid test_minmod_option_parsing() {\n  INFO(\"Testing option parsing\");\n  const auto lambda_pi1 =\n      TestHelpers::test_creation<Limiters::Minmod<1, tmpl::list<ScalarTag>>>(\n          \"Type: LambdaPi1\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: False\");\n  const auto lambda_pi1_tvb =\n      TestHelpers::test_creation<Limiters::Minmod<1, tmpl::list<ScalarTag>>>(\n          \"Type: LambdaPi1\\n\"\n          \"TvbConstant: 1.0\\n\"\n          \"DisableForDebugging: False\");\n  const auto lambda_pi1_disabled =\n      TestHelpers::test_creation<Limiters::Minmod<1, tmpl::list<ScalarTag>>>(\n          \"Type: LambdaPi1\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: True\");\n  const auto muscl =\n      TestHelpers::test_creation<Limiters::Minmod<1, tmpl::list<ScalarTag>>>(\n          \"Type: Muscl\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: False\");\n\n  // Test operators == and !=\n  CHECK(lambda_pi1 == lambda_pi1);\n  CHECK(lambda_pi1 != lambda_pi1_tvb);\n  CHECK(lambda_pi1 != lambda_pi1_disabled);\n  CHECK(lambda_pi1 != muscl);\n\n  const auto lambda_pin_2d =\n      TestHelpers::test_creation<Limiters::Minmod<2, tmpl::list<ScalarTag>>>(\n          \"Type: LambdaPiN\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: False\");\n  const auto lambda_pin_3d = TestHelpers::test_creation<\n      Limiters::Minmod<3, tmpl::list<ScalarTag, VectorTag<3>>>>(\n      \"Type: LambdaPiN\\n\"\n      \"TvbConstant: 10.0\\n\"\n      \"DisableForDebugging: False\");\n\n  // Test that creation from options gives correct object\n  const Limiters::Minmod<1, tmpl::list<ScalarTag>> expected_lambda_pi1(\n      Limiters::MinmodType::LambdaPi1, 0.0);\n  const Limiters::Minmod<1, tmpl::list<ScalarTag>> expected_lambda_pi1_tvb(\n      Limiters::MinmodType::LambdaPi1, 1.0);\n  const Limiters::Minmod<1, tmpl::list<ScalarTag>> expected_lambda_pi1_disabled(\n      Limiters::MinmodType::LambdaPi1, 0.0, true);\n  const Limiters::Minmod<1, tmpl::list<ScalarTag>> expected_muscl(\n      Limiters::MinmodType::Muscl, 0.0);\n  const Limiters::Minmod<2, tmpl::list<ScalarTag>> expected_lambda_pin_2d(\n      Limiters::MinmodType::LambdaPiN, 0.0);\n  const Limiters::Minmod<3, tmpl::list<ScalarTag, VectorTag<3>>>\n      expected_lambda_pin_3d(Limiters::MinmodType::LambdaPiN, 10.0);\n\n  CHECK(lambda_pi1 == expected_lambda_pi1);\n  CHECK(lambda_pi1_tvb == expected_lambda_pi1_tvb);\n  CHECK(lambda_pi1_disabled == expected_lambda_pi1_disabled);\n  CHECK(muscl == expected_muscl);\n  CHECK(lambda_pin_2d == expected_lambda_pin_2d);\n  CHECK(lambda_pin_3d == expected_lambda_pin_3d);\n}\n\nvoid test_minmod_serialization() {\n  INFO(\"Testing serialization\");\n  const Limiters::Minmod<1, tmpl::list<ScalarTag>> minmod(\n      Limiters::MinmodType::LambdaPi1, 1.0);\n  test_serialization(minmod);\n}\n\ntemplate <size_t VolumeDim>\nvoid test_package_data_work(const Mesh<VolumeDim>& mesh,\n                            const OrientationMap<VolumeDim>& orientation_map) {\n  const DataVector used_for_size(mesh.number_of_grid_points());\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n\n  const auto input_scalar = make_with_random_values<ScalarTag::type>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n  const auto input_vector =\n      make_with_random_values<typename VectorTag<VolumeDim>::type>(\n          make_not_null(&generator), make_not_null(&dist), used_for_size);\n  const auto element_size =\n      make_with_random_values<std::array<double, VolumeDim>>(\n          make_not_null(&generator), make_not_null(&dist), 0.0);\n\n  using TagList = tmpl::list<ScalarTag, VectorTag<VolumeDim>>;\n  const double tvb_constant = 0.0;\n  const Limiters::Minmod<VolumeDim, TagList> minmod(\n      Limiters::MinmodType::LambdaPiN, tvb_constant);\n  typename Limiters::Minmod<VolumeDim, TagList>::PackagedData packaged_data{};\n\n  minmod.package_data(make_not_null(&packaged_data), input_scalar, input_vector,\n                      mesh, element_size, orientation_map);\n\n  CHECK(packaged_data.element_size ==\n        orientation_map.permute_from_neighbor(element_size));\n\n  // Means are just numbers and don't care about orientations\n  CHECK(get(get<::Tags::Mean<ScalarTag>>(packaged_data.means)) ==\n        mean_value(get(input_scalar), mesh));\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    CHECK(get<::Tags::Mean<VectorTag<VolumeDim>>>(packaged_data.means).get(i) ==\n          mean_value(input_vector.get(i), mesh));\n  }\n}\n\nvoid test_package_data_1d() {\n  INFO(\"Testing package_data in 1D\");\n  const Mesh<1> mesh(4, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n\n  const OrientationMap<1> orientation_aligned =\n      OrientationMap<1>::create_aligned();\n  test_package_data_work(mesh, orientation_aligned);\n\n  const OrientationMap<1> orientation_flipped(\n      std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}});\n  test_package_data_work(mesh, orientation_flipped);\n}\n\nvoid test_package_data_2d() {\n  INFO(\"Testing package_data in 2D\");\n  const Mesh<2> mesh({{4, 6}}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n\n  const OrientationMap<2> orientation_aligned =\n      OrientationMap<2>::create_aligned();\n  test_package_data_work(mesh, orientation_aligned);\n\n  const OrientationMap<2> orientation_rotated(std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}});\n  test_package_data_work(mesh, orientation_rotated);\n}\n\nvoid test_package_data_3d() {\n  INFO(\"Testing package_data in 3D\");\n  const Mesh<3> mesh({{4, 6, 3}}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n\n  const OrientationMap<3> orientation_aligned =\n      OrientationMap<3>::create_aligned();\n  test_package_data_work(mesh, orientation_aligned);\n\n  const OrientationMap<3> orientation_rotated(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_zeta(), Direction<3>::upper_xi(),\n       Direction<3>::lower_eta()}});\n  test_package_data_work(mesh, orientation_rotated);\n}\n\n// Helper function to wrap the allocation of the optimization buffers for the\n// troubled cell indicator function.\ntemplate <size_t VolumeDim>\nbool wrap_minmod_limited_slopes(\n    const gsl::not_null<double*> u_mean,\n    const gsl::not_null<std::array<double, VolumeDim>*> u_limited_slopes,\n    const Limiters::MinmodType minmod_type, const double tvb_constant,\n    const DataVector& u, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes) {\n  DataVector u_lin_buffer(mesh.number_of_grid_points());\n  Limiters::Minmod_detail::BufferWrapper<VolumeDim> buffer(mesh);\n  return Limiters::Minmod_detail::minmod_limited_slopes(\n      make_not_null(&u_lin_buffer), make_not_null(&buffer), u_mean,\n      u_limited_slopes, minmod_type, tvb_constant, u, mesh, element,\n      element_size, effective_neighbor_means, effective_neighbor_sizes);\n}\n\nauto make_two_neighbors(const double left, const double right) {\n  DirectionMap<1, double> result;\n  result[Direction<1>::lower_xi()] = left;\n  result[Direction<1>::upper_xi()] = right;\n  return result;\n}\n\nauto make_four_neighbors(const std::array<double, 4>& values) {\n  DirectionMap<2, double> result;\n  result[Direction<2>::lower_xi()] = values[0];\n  result[Direction<2>::upper_xi()] = values[1];\n  result[Direction<2>::lower_eta()] = values[2];\n  result[Direction<2>::upper_eta()] = values[3];\n  return result;\n}\n\nauto make_six_neighbors(const std::array<double, 6>& values) {\n  DirectionMap<3, double> result;\n  result[Direction<3>::lower_xi()] = values[0];\n  result[Direction<3>::upper_xi()] = values[1];\n  result[Direction<3>::lower_eta()] = values[2];\n  result[Direction<3>::upper_eta()] = values[3];\n  result[Direction<3>::lower_zeta()] = values[4];\n  result[Direction<3>::upper_zeta()] = values[5];\n  return result;\n}\n\n// Test that TCI detects a troubled cell when expected, and returns the correct\n// mean and reduced slopes.\ntemplate <size_t VolumeDim>\nvoid test_minmod_activates(\n    const Limiters::MinmodType minmod_type, const double tvb_constant,\n    const DataVector& input, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes,\n    const std::array<double, VolumeDim>& expected_slopes) {\n  double u_mean{};\n  std::array<double, VolumeDim> u_limited_slopes{};\n  const bool reduce_slopes = wrap_minmod_limited_slopes(\n      make_not_null(&u_mean), make_not_null(&u_limited_slopes), minmod_type,\n      tvb_constant, input, mesh, element, element_size,\n      effective_neighbor_means, effective_neighbor_sizes);\n  CHECK(reduce_slopes);\n  CHECK(u_mean == approx(mean_value(input, mesh)));\n  CHECK_ITERABLE_APPROX(u_limited_slopes, expected_slopes);\n}\n\n// Test that TCI does not detect a cell that is not troubled.\ntemplate <size_t VolumeDim>\nvoid test_minmod_does_not_activate(\n    const Limiters::MinmodType minmod_type, const double tvb_constant,\n    const DataVector& input, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes,\n    const std::array<double, VolumeDim>& original_slopes) {\n  double u_mean{};\n  std::array<double, VolumeDim> u_limited_slopes{};\n  const bool reduce_slopes = wrap_minmod_limited_slopes(\n      make_not_null(&u_mean), make_not_null(&u_limited_slopes), minmod_type,\n      tvb_constant, input, mesh, element, element_size,\n      effective_neighbor_means, effective_neighbor_sizes);\n  CHECK_FALSE(reduce_slopes);\n  if (minmod_type != Limiters::MinmodType::LambdaPiN) {\n    CHECK(u_mean == approx(mean_value(input, mesh)));\n    CHECK_ITERABLE_APPROX(u_limited_slopes, original_slopes);\n  }\n}\n\nvoid test_minmod_slopes_on_linear_function(\n    const size_t number_of_grid_points, const Limiters::MinmodType minmod_type,\n    const Spectral::Quadrature quadrature) {\n  INFO(\"Testing minmod_limited_slopes with linear function\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(minmod_type);\n  CAPTURE(quadrature);\n  const double tvb_constant = 0.0;\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const auto element_size = make_array<1>(2.0);\n\n  const auto test_activates = [&minmod_type, &tvb_constant, &mesh, &element,\n                               &element_size](const DataVector& local_input,\n                                              const double left,\n                                              const double right,\n                                              const double expected_slope) {\n    const auto expected_slopes = make_array<1>(expected_slope);\n    test_minmod_activates(minmod_type, tvb_constant, local_input, mesh, element,\n                          element_size, make_two_neighbors(left, right),\n                          make_two_neighbors(2.0, 2.0), expected_slopes);\n  };\n  const auto test_does_not_activate =\n      [&minmod_type, &tvb_constant, &mesh, &element, &element_size](\n          const DataVector& local_input, const double left, const double right,\n          const double original_slope) {\n        const auto original_slopes = make_array<1>(original_slope);\n        test_minmod_does_not_activate(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_two_neighbors(left, right), make_two_neighbors(2.0, 2.0),\n            original_slopes);\n      };\n\n  // With a MUSCL limiter, the largest allowed slope is half as big as for a\n  // LambdaPi1 or LambdaPiN limiter. We can re-use the same test cases by\n  // correspondingly scaling the slope:\n  const double muscl_slope_factor =\n      (minmod_type == Limiters::MinmodType::Muscl) ? 0.5 : 1.0;\n  const double eps = 100.0 * std::numeric_limits<double>::epsilon();\n\n  // Test a positive-slope function\n  {\n    const auto input = [&muscl_slope_factor, &mesh]() {\n      const auto coords = logical_coordinates(mesh);\n      return DataVector{3.6 + 1.2 * muscl_slope_factor * get<0>(coords)};\n    }();\n\n    // Steepness test\n    // Limiter does not reduce slope if (difference of means) > (local slope),\n    // but reduces slope if (difference of means) < (local slope)\n    test_does_not_activate(input, 2.0, 6.0, 1.2 * muscl_slope_factor);\n    test_does_not_activate(input, 2.4 - eps, 4.8 + eps,\n                           1.2 * muscl_slope_factor);\n    test_activates(input, 2.6, 6.0, 1.0 * muscl_slope_factor);\n    test_activates(input, 2.0, 4.0, 0.4 * muscl_slope_factor);\n    test_activates(input, 2.6, 4.0, 0.4 * muscl_slope_factor);\n\n    // Local extremum test\n    // Limiter flattens slope if both neighbors are above (below) the mean\n    test_activates(input, 1.0, 2.0, 0.0);\n    test_activates(input, 6.0, 9.0, 0.0);\n\n    // Oscillation test\n    // Limiter flattens slope if sign(difference of means) != sign(local slope)\n    test_activates(input, 3.9, 2.7, 0.0);\n  }\n\n  // Test a negative-slope function\n  {\n    const auto input = [&muscl_slope_factor, &mesh]() {\n      const auto coords = logical_coordinates(mesh);\n      return DataVector{-0.4 - 0.8 * muscl_slope_factor * get<0>(coords)};\n    }();\n\n    // Steepness test\n    test_does_not_activate(input, 0.9, -2.3, -0.8 * muscl_slope_factor);\n    test_does_not_activate(input, 0.4 + eps, -1.2 - eps,\n                           -0.8 * muscl_slope_factor);\n    test_activates(input, 0.2, -1.2, -0.6 * muscl_slope_factor);\n    test_activates(input, 0.4, -0.5, -0.1 * muscl_slope_factor);\n    test_activates(input, 0.2, -0.5, -0.1 * muscl_slope_factor);\n\n    // Local extremum test\n    test_activates(input, 1.3, -0.1, 0.0);\n    test_activates(input, -3.2, -0.8, 0.0);\n\n    // Oscillation test\n    test_activates(input, -2.3, 0.2, 0.0);\n  }\n}\n\nvoid test_minmod_slopes_on_quadratic_function(\n    const size_t number_of_grid_points, const Limiters::MinmodType minmod_type,\n    const Spectral::Quadrature quadrature) {\n  INFO(\"Testing minmod_limited_slopes with quadratic function\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(minmod_type);\n  CAPTURE(quadrature);\n  const double tvb_constant = 0.0;\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const auto element_size = make_array<1>(2.0);\n\n  const auto test_activates = [&minmod_type, &tvb_constant, &mesh, &element,\n                               &element_size](const DataVector& local_input,\n                                              const double left,\n                                              const double right,\n                                              const double expected_slope) {\n    const auto expected_slopes = make_array<1>(expected_slope);\n    test_minmod_activates(minmod_type, tvb_constant, local_input, mesh, element,\n                          element_size, make_two_neighbors(left, right),\n                          make_two_neighbors(2.0, 2.0), expected_slopes);\n  };\n  const auto test_does_not_activate =\n      [&minmod_type, &tvb_constant, &mesh, &element, &element_size](\n          const DataVector& local_input, const double left, const double right,\n          const double original_slope) {\n        const auto original_slopes = make_array<1>(original_slope);\n        test_minmod_does_not_activate(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_two_neighbors(left, right), make_two_neighbors(2.0, 2.0),\n            original_slopes);\n      };\n\n  const double muscl_slope_factor =\n      (minmod_type == Limiters::MinmodType::Muscl) ? 0.5 : 1.0;\n\n  const auto input = [&muscl_slope_factor, &mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    const auto& x = get<0>(coords);\n    // For easier testing, center the quadratic term on the grid: otherwise this\n    // term will affect the average slope on the element.\n    return DataVector{13.0 + 4.0 * muscl_slope_factor * x + 2.5 * square(x)};\n  }();\n  const double mean = mean_value(input, mesh);\n\n  // Steepness test\n  // Cases where slope is not steep enough to need limiting\n  test_does_not_activate(input, mean - 5.0, mean + 5.0,\n                         4.0 * muscl_slope_factor);\n  test_does_not_activate(input, mean - 4.01, mean + 4.01,\n                         4.0 * muscl_slope_factor);\n  // Cases where slope is too steep and needs to be reduced\n  test_activates(input, mean - 3.99, mean + 3.99, 3.99 * muscl_slope_factor);\n  test_activates(input, mean - 1.3, mean + 1.9, 1.3 * muscl_slope_factor);\n\n  // Local extremum test\n  test_activates(input, 9.4, 2.3, 0.0);\n  test_activates(input, 14.0, 18.2, 0.0);\n\n  // Oscillation test\n  test_activates(input, 14.0, 2.3, 0.0);\n}\n\nvoid test_minmod_slopes_with_tvb_correction(\n    const size_t number_of_grid_points, const Limiters::MinmodType minmod_type,\n    const Spectral::Quadrature quadrature) {\n  INFO(\"Testing minmod_limited_slopes with TVB correction\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(minmod_type);\n  CAPTURE(quadrature);\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const auto element_size = make_array<1>(2.0);\n\n  const auto test_activates = [&minmod_type, &mesh, &element, &element_size](\n                                  const double tvb_constant,\n                                  const DataVector& local_input,\n                                  const double left, const double right,\n                                  const double expected_slope) {\n    const auto expected_slopes = make_array<1>(expected_slope);\n    test_minmod_activates(minmod_type, tvb_constant, local_input, mesh, element,\n                          element_size, make_two_neighbors(left, right),\n                          make_two_neighbors(2.0, 2.0), expected_slopes);\n  };\n  const auto test_does_not_activate =\n      [&minmod_type, &mesh, &element, &element_size](\n          const double tvb_constant, const DataVector& local_input,\n          const double left, const double right, const double original_slope) {\n        const auto original_slopes = make_array<1>(original_slope);\n        test_minmod_does_not_activate(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_two_neighbors(left, right), make_two_neighbors(2.0, 2.0),\n            original_slopes);\n      };\n\n  // Slopes will be compared to m * h^2, where here h = 2\n  const double tvb_m0 = 0.0;\n  const double tvb_m1 = 1.0;\n  const double tvb_m2 = 2.0;\n\n  const auto input = [&mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    return DataVector{21.6 + 7.2 * get<0>(coords)};\n  }();\n\n  // The TVB constant sets a threshold slope, below which the solution will not\n  // be limited. We test this by increasing the TVB constant until the limiter\n  // stops activating / stops changing the solution.\n  const double left = (minmod_type == Limiters::MinmodType::Muscl) ? 8.4 : 15.0;\n  const double right =\n      (minmod_type == Limiters::MinmodType::Muscl) ? 34.8 : 30.0;\n  test_activates(tvb_m0, input, left, right, 6.6);\n  test_activates(tvb_m1, input, left, right, 6.6);\n  test_does_not_activate(tvb_m2, input, left, right, 7.2);\n}\n\n// Here we test the coupling of the LambdaPiN troubled cell detector with the\n// TVB constant value.\nvoid test_lambda_pin_troubled_cell_tvb_correction(\n    const Spectral::Quadrature quadrature) {\n  INFO(\"Testing minmod_limited_slopes with LambdaPiN-TVB correction\");\n  CAPTURE(quadrature);\n  const Mesh<1> mesh(4, Spectral::Basis::Legendre, quadrature);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto element_size = make_array<1>(2.0);\n\n  const auto test_activates = [&mesh, &element, &element_size](\n                                  const double tvb_constant,\n                                  const DataVector& local_input,\n                                  const double left, const double right,\n                                  const double expected_slope) {\n    const auto expected_slopes = make_array<1>(expected_slope);\n    test_minmod_activates(Limiters::MinmodType::LambdaPiN, tvb_constant,\n                          local_input, mesh, element, element_size,\n                          make_two_neighbors(left, right),\n                          make_two_neighbors(2.0, 2.0), expected_slopes);\n  };\n  const auto test_does_not_activate =\n      [&mesh, &element, &element_size](const double tvb_constant,\n                                       const DataVector& local_input,\n                                       const double left, const double right) {\n        // Because in this test the limiter is LambdaPiN, no slopes are\n        // returned, and no comparison is made. So set these to NaN\n        const auto original_slopes =\n            make_array<1>(std::numeric_limits<double>::signaling_NaN());\n        test_minmod_does_not_activate(\n            Limiters::MinmodType::LambdaPiN, tvb_constant, local_input, mesh,\n            element, element_size, make_two_neighbors(left, right),\n            make_two_neighbors(2.0, 2.0), original_slopes);\n      };\n\n  const double m0 = 0.0;\n  const double m1 = 1.0;\n  const double m2 = 2.0;\n\n  const auto input = [&mesh]() {\n    // We want a function that is somewhat step-like. But for the test to work\n    // using GaussLobatto _and_ Gauss points, we need to use a representable\n    // polynomial function.\n    //\n    // So, we find here the smooth cubic that equals 10*step_function on a\n    // 4-point LGL mesh. This has the form,\n    //   f(x) = 5 + A x + B x^3\n    // subject to the constraint f(x) = 10*step_function(x) on the LGL mesh,\n    // i.e.,\n    //   f(1) = 5 + A + B = 10\n    //   f(1/sqrt(5)) = 5 + A / sqrt(5) + B / sqrt(5)^3 = 10\n    // giving the coeffs:\n    //   A = 5 (sqrt(5)^3 - 1) / 4\n    //   B = 1 - A = 25 (1 - sqrt(5)) / 4\n    //\n    // Finally, we scale around the mean by a factor just under 1, to make sure\n    // that any roundoff does not lead to values outside the range [0, 10]. this\n    // is because the minmod algorithm has sudden behavior changes if thresholds\n    // are exceeded even by roundoff, and we want to use [0, 10] as bounds on\n    // the solution values.\n    const DataVector x = get<0>(logical_coordinates(mesh));\n    const double a = 1.25 * (cube(sqrt(5.0)) - 1.0);\n    const double b = 6.25 * (1.0 - sqrt(5.0));\n    const double nearly_one = 1.0 - 1e-14;\n    return DataVector{5.0 + nearly_one * (a * x + b * cube(x))};\n  }();\n\n  // Establish baseline m = 0 case; LambdaPiN normally avoids limiting when\n  // max(edge - mean) <= min(neighbor - mean)\n  test_does_not_activate(m0, input, 0.0, 10.0);\n  test_does_not_activate(m0, input, -0.3, 10.2);\n  // but does limit if max(edge - mean) > min(neighbor - mean)\n  test_activates(m0, input, 0.02, 10.0, 4.98);\n  test_activates(m0, input, 0.0, 9.99, 4.99);\n\n  // However, with a non-zero TVB constant, LambdaPiN should additionally avoid\n  // limiting when max(edge - mean) < TVB correction.\n  // We test first a case where the TVB correction is too small to affect\n  // the limiter action,\n  test_does_not_activate(m1, input, 0.0, 10.0);\n  test_does_not_activate(m1, input, -0.3, 10.2);\n  test_activates(m1, input, 0.02, 10.0, 4.98);\n  test_activates(m1, input, 0.0, 9.99, 4.99);\n\n  // And a case where the TVB correction enables LambdaPiN to avoid limiting\n  // (Note that the slope here is still too large to avoid limiting through\n  // the normal TVB tolerance.)\n  test_does_not_activate(m2, input, 0.0, 10.0);\n  test_does_not_activate(m2, input, -0.3, 10.2);\n  test_does_not_activate(m2, input, 0.02, 10.0);\n  test_does_not_activate(m2, input, 0.0, 9.99);\n}\n\nvoid test_minmod_slopes_at_boundary(const size_t number_of_grid_points,\n                                    const Limiters::MinmodType minmod_type,\n                                    const Spectral::Quadrature quadrature) {\n  INFO(\"Testing minmod_limited_slopes at boundary\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(minmod_type);\n  CAPTURE(quadrature);\n  const double tvb_constant = 0.0;\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n  const auto element_size = make_array<1>(2.0);\n\n  const double muscl_slope_factor =\n      (minmod_type == Limiters::MinmodType::Muscl) ? 0.5 : 1.0;\n  const auto input = [&muscl_slope_factor, &mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    return DataVector{1.2 * muscl_slope_factor * get<0>(coords)};\n  }();\n\n  // Test with element that has external lower-xi boundary\n  const auto element_at_lower_xi_boundary =\n      TestHelpers::Limiters::make_element<1>({{Direction<1>::lower_xi()}});\n  for (const double neighbor : {-1.21, -1.19, 0.0, 1.19, 1.21}) {\n    const double expected_slope =\n        std::min(std::max(0.0, neighbor), 1.2) * muscl_slope_factor;\n    if (neighbor > 1.2) {\n      test_minmod_does_not_activate(\n          minmod_type, tvb_constant, input, mesh, element_at_lower_xi_boundary,\n          element_size, {{std::make_pair(Direction<1>::upper_xi(), neighbor)}},\n          {{std::make_pair(Direction<1>::upper_xi(), element_size[0])}},\n          make_array<1>(expected_slope));\n    } else {\n      test_minmod_activates(\n          minmod_type, tvb_constant, input, mesh, element_at_lower_xi_boundary,\n          element_size, {{std::make_pair(Direction<1>::upper_xi(), neighbor)}},\n          {{std::make_pair(Direction<1>::upper_xi(), element_size[0])}},\n          make_array<1>(expected_slope));\n    }\n  }\n\n  // Test with element that has external upper-xi boundary\n  const auto element_at_upper_xi_boundary =\n      TestHelpers::Limiters::make_element<1>({{Direction<1>::upper_xi()}});\n  for (const double neighbor : {-1.21, -1.19, 0.0, 1.19, 1.21}) {\n    const double expected_slope =\n        std::min(std::max(0.0, -neighbor), 1.2) * muscl_slope_factor;\n    if (neighbor < -1.2) {\n      test_minmod_does_not_activate(\n          minmod_type, tvb_constant, input, mesh, element_at_upper_xi_boundary,\n          element_size, {{std::make_pair(Direction<1>::lower_xi(), neighbor)}},\n          {{std::make_pair(Direction<1>::lower_xi(), element_size[0])}},\n          make_array<1>(expected_slope));\n    } else {\n      test_minmod_activates(\n          minmod_type, tvb_constant, input, mesh, element_at_upper_xi_boundary,\n          element_size, {{std::make_pair(Direction<1>::lower_xi(), neighbor)}},\n          {{std::make_pair(Direction<1>::lower_xi(), element_size[0])}},\n          make_array<1>(expected_slope));\n    }\n  }\n}\n\nvoid test_minmod_slopes_with_different_size_neighbor(\n    const size_t number_of_grid_points, const Limiters::MinmodType minmod_type,\n    const Spectral::Quadrature quadrature) {\n  INFO(\"Testing minmod_limited_slopes with different size neighbors\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(minmod_type);\n  CAPTURE(quadrature);\n  const double tvb_constant = 0.0;\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const double dx = 1.0;\n  const auto element_size = make_array<1>(dx);\n\n  const auto test_activates =\n      [&minmod_type, &tvb_constant, &mesh, &element, &element_size](\n          const DataVector& local_input, const double left, const double right,\n          const double left_size, const double right_size,\n          const double expected_slope) {\n        const auto expected_slopes = make_array<1>(expected_slope);\n        test_minmod_activates(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_two_neighbors(left, right),\n            make_two_neighbors(left_size, right_size), expected_slopes);\n      };\n  const auto test_does_not_activate =\n      [&minmod_type, &tvb_constant, &mesh, &element, &element_size](\n          const DataVector& local_input, const double left, const double right,\n          const double left_size, const double right_size,\n          const double original_slope) {\n        const auto original_slopes = make_array<1>(original_slope);\n        test_minmod_does_not_activate(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_two_neighbors(left, right),\n            make_two_neighbors(left_size, right_size), original_slopes);\n      };\n\n  const double muscl_slope_factor =\n      (minmod_type == Limiters::MinmodType::Muscl) ? 0.5 : 1.0;\n  const double eps = 100.0 * std::numeric_limits<double>::epsilon();\n\n  const auto input = [&muscl_slope_factor, &mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    return DataVector{2.0 + 1.2 * muscl_slope_factor * get<0>(coords)};\n  }();\n\n  // Establish baseline using evenly-sized elements\n  test_does_not_activate(input, 0.8 - eps, 3.2 + eps, dx, dx,\n                         1.2 * muscl_slope_factor);\n\n  const double larger = 2.0 * dx;\n  const double smaller = 0.5 * dx;\n\n  // Larger neighbor with same mean => true reduction in slope => trigger\n  test_activates(input, 0.8 - eps, 3.2, dx, larger, 0.8 * muscl_slope_factor);\n  // Larger neighbor with larger mean => same slope => no trigger\n  test_does_not_activate(input, 0.8 - eps, 3.8 + eps, dx, larger,\n                         1.2 * muscl_slope_factor);\n\n  // Smaller neighbor with same mean => increased slope => no trigger\n  test_does_not_activate(input, 0.8 - eps, 3.2 + eps, dx, smaller,\n                         1.2 * muscl_slope_factor);\n  // Smaller neighbor with lower mean => same slope => no trigger\n  test_does_not_activate(input, 0.8 - eps, 2.9 + eps, dx, smaller,\n                         1.2 * muscl_slope_factor);\n\n  test_activates(input, 0.8, 3.2 + eps, larger, dx, 0.8 * muscl_slope_factor);\n  test_does_not_activate(input, 0.2 - eps, 3.2 + eps, larger, dx,\n                         1.2 * muscl_slope_factor);\n\n  test_does_not_activate(input, 0.8 - eps, 3.2 + eps, smaller, dx,\n                         1.2 * muscl_slope_factor);\n  test_does_not_activate(input, 1.1 - eps, 3.2 + eps, smaller, dx,\n                         1.2 * muscl_slope_factor);\n}\n\n// In 1D, test combinations of MinmodType, TVB constant, polynomial order, etc.\n// Check that each combination reduces the slopes as expected.\nvoid test_minmod_limited_slopes_1d() {\n  INFO(\"Testing minmod_limited_slopes in 1D\");\n  for (const auto quadrature :\n       {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n    for (const auto minmod_type :\n         {Limiters::MinmodType::LambdaPi1, Limiters::MinmodType::LambdaPiN,\n          Limiters::MinmodType::Muscl}) {\n      for (const auto num_grid_points : std::array<size_t, 2>{{2, 4}}) {\n        test_minmod_slopes_on_linear_function(num_grid_points, minmod_type,\n                                              quadrature);\n        test_minmod_slopes_with_tvb_correction(num_grid_points, minmod_type,\n                                               quadrature);\n        test_minmod_slopes_at_boundary(num_grid_points, minmod_type,\n                                       quadrature);\n        test_minmod_slopes_with_different_size_neighbor(\n            num_grid_points, minmod_type, quadrature);\n      }\n      // This test only makes sense with more than 2 grid points\n      test_minmod_slopes_on_quadratic_function(3, minmod_type, quadrature);\n      test_minmod_slopes_on_quadratic_function(4, minmod_type, quadrature);\n    }\n    // This test only makes sense with LambdaPiN, and is hardcoded for a\n    // particular number of grid points\n    test_lambda_pin_troubled_cell_tvb_correction(quadrature);\n  }\n}\n\n// In 2D, test that the slopes are correctly reduced dimension-by-dimension.\nvoid test_minmod_limited_slopes_2d_work(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const auto minmod_type = Limiters::MinmodType::LambdaPi1;\n  const double tvb_constant = 0.0;\n  const Mesh<2> mesh(3, Spectral::Basis::Legendre, quadrature);\n  const auto element = TestHelpers::Limiters::make_element<2>();\n  const auto element_size = make_array<2>(2.0);\n\n  const auto test_activates =\n      [&minmod_type, &tvb_constant, &mesh, &element, &element_size](\n          const DataVector& local_input,\n          const std::array<double, 4>& neighbor_means,\n          const std::array<double, 2>& expected_slopes) {\n        // Clang complains of unused lambda capture\n        (void)minmod_type;\n        test_minmod_activates(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_four_neighbors(neighbor_means),\n            make_four_neighbors(make_array<4>(2.0)), expected_slopes);\n      };\n  const auto test_does_not_activate =\n      [&minmod_type, &tvb_constant, &mesh, &element, &element_size](\n          const DataVector& local_input,\n          const std::array<double, 4>& neighbor_means,\n          const std::array<double, 2>& original_slopes) {\n        // Clang complains of unused lambda capture\n        (void)minmod_type;\n        test_minmod_does_not_activate(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_four_neighbors(neighbor_means),\n            make_four_neighbors(make_array<4>(2.0)), original_slopes);\n      };\n\n  const auto input = [&mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    const auto& x = get<0>(coords);\n    const auto& y = get<1>(coords);\n    return DataVector{3.0 + x + 2.0 * y + 0.1 * x * y};\n  }();\n\n  // Case with no activation\n  test_does_not_activate(input, {{1.9, 4.2, -0.5, 5.6}}, {{1.0, 2.0}});\n\n  // Limit because of xi-direction neighbors\n  test_activates(input, {{2.2, 4.2, -0.5, 5.6}}, {{0.8, 2.0}});\n  test_activates(input, {{1.9, 3.2, -0.5, 5.6}}, {{0.2, 2.0}});\n\n  // Limit because of eta-direction neighbors\n  test_activates(input, {{1.9, 4.2, 1.5, 5.6}}, {{1.0, 1.5}});\n  test_activates(input, {{1.9, 4.2, -0.5, 2.9}}, {{1.0, 0.0}});\n\n  // Limit for xi and eta directions\n  test_activates(input, {{2.2, 4.2, 1.5, 5.6}}, {{0.8, 1.5}});\n  test_activates(input, {{3.9, 4.2, -0.5, 2.9}}, {{0.0, 0.0}});\n}\n\nvoid test_minmod_limited_slopes_2d() {\n  INFO(\"Testing minmod_limited_slopes in 2D\");\n  test_minmod_limited_slopes_2d_work(Spectral::Quadrature::GaussLobatto);\n  test_minmod_limited_slopes_2d_work(Spectral::Quadrature::Gauss);\n}\n\n// In 3D, test that the slopes are correctly reduced dimension-by-dimension.\nvoid test_minmod_limited_slopes_3d_work(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const auto minmod_type = Limiters::MinmodType::LambdaPi1;\n  const double tvb_constant = 0.0;\n  const Mesh<3> mesh(3, Spectral::Basis::Legendre, quadrature);\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const auto element_size = make_array<3>(2.0);\n\n  const auto test_activates =\n      [&minmod_type, &tvb_constant, &mesh, &element, &element_size](\n          const DataVector& local_input,\n          const std::array<double, 6>& neighbor_means,\n          const std::array<double, 3>& expected_slopes) {\n        // Clang complains of unused lambda capture\n        (void)minmod_type;\n        test_minmod_activates(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_six_neighbors(neighbor_means),\n            make_six_neighbors(make_array<6>(2.0)), expected_slopes);\n      };\n  const auto test_does_not_activate =\n      [&minmod_type, &tvb_constant, &mesh, &element, &element_size](\n          const DataVector& local_input,\n          const std::array<double, 6>& neighbor_means,\n          const std::array<double, 3>& original_slopes) {\n        // Clang complains of unused lambda capture\n        (void)minmod_type;\n        test_minmod_does_not_activate(\n            minmod_type, tvb_constant, local_input, mesh, element, element_size,\n            make_six_neighbors(neighbor_means),\n            make_six_neighbors(make_array<6>(2.0)), original_slopes);\n      };\n\n  const auto input = [&mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    const auto& x = get<0>(coords);\n    const auto& y = get<1>(coords);\n    const auto& z = get<2>(coords);\n    return DataVector{2.0 - 1.6 * x + 0.4 * y + 0.4 * z + 0.1 * x * y -\n                      0.1 * x * z - 0.2 * y * z};\n  }();\n\n  // Case with no activation\n  test_does_not_activate(input, {{3.8, -0.1, 1.5, 2.7, 1.2, 2.5}},\n                         {{-1.6, 0.4, 0.4}});\n\n  // Limit because of xi-direction neighbors\n  test_activates(input, {{3.4, -0.1, 1.5, 2.7, 1.2, 2.5}}, {{-1.4, 0.4, 0.4}});\n  test_activates(input, {{3.8, 2.1, 1.5, 2.7, 1.2, 2.5}}, {{0.0, 0.4, 0.4}});\n\n  // Limit because of eta-direction neighbors\n  test_activates(input, {{3.8, -0.1, 1.9, 2.7, 1.2, 2.5}}, {{-1.6, 0.1, 0.4}});\n  test_activates(input, {{3.8, -0.1, 1.5, 2.3, 1.2, 2.5}}, {{-1.6, 0.3, 0.4}});\n\n  // Limit because of zeta-direction neighbors\n  test_activates(input, {{3.8, -0.1, 1.5, 2.7, 2.2, 2.5}}, {{-1.6, 0.4, 0.0}});\n  test_activates(input, {{3.8, -0.1, 1.5, 2.7, 1.2, 2.1}}, {{-1.6, 0.4, 0.1}});\n\n  // Limit for xi, eta, and zeta directions\n  test_activates(input, {{3.4, -0.1, 1.5, 2.3, 1.2, 2.1}}, {{-1.4, 0.3, 0.1}});\n  test_activates(input, {{3.8, 2.1, 2.1, 2.7, 2.2, 2.5}}, {{0.0, 0.0, 0.0}});\n}\n\nvoid test_minmod_limited_slopes_3d() {\n  INFO(\"Testing minmod_limited_slopes in 3D\");\n  test_minmod_limited_slopes_3d_work(Spectral::Quadrature::GaussLobatto);\n  test_minmod_limited_slopes_3d_work(Spectral::Quadrature::Gauss);\n}\n\n// Helper function for testing minmod_impl\ntemplate <size_t VolumeDim>\nvoid test_minmod_impl_work(\n    const tnsr::I<DataVector, VolumeDim>& input_vector,\n    const Mesh<VolumeDim>& mesh,\n    const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, VolumeDim>& element_size,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename Limiters::Minmod<\n            VolumeDim, tmpl::list<VectorTag<VolumeDim>>>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data,\n    const std::array<std::array<double, VolumeDim>, VolumeDim>&\n        target_vector_slope) {\n  auto vector_to_limit = input_vector;\n\n  // Minmod should preserve the mean, so expected = initial\n  const auto expected_vector_means = [&vector_to_limit, &mesh]() {\n    std::array<double, VolumeDim> means{};\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      gsl::at(means, d) = mean_value(vector_to_limit.get(d), mesh);\n    }\n    return means;\n  }();\n\n  const auto element = TestHelpers::Limiters::make_element<VolumeDim>();\n  const auto minmod_type = Limiters::MinmodType::LambdaPi1;\n  const double tvb_constant = 0.0;\n  DataVector u_lin_buffer(mesh.number_of_grid_points());\n  Limiters::Minmod_detail::BufferWrapper<VolumeDim> buffer(mesh);\n  const bool limiter_activated =\n      Limiters::Minmod_detail::minmod_impl<VolumeDim, VectorTag<VolumeDim>>(\n          make_not_null(&u_lin_buffer), make_not_null(&buffer),\n          make_not_null(&vector_to_limit), minmod_type, tvb_constant, mesh,\n          element, logical_coords, element_size, neighbor_data);\n\n  // Currently we use this helper in cases where the limiter activates. This\n  // check could be generalized if the expected outcome is not always true.\n  CHECK(limiter_activated);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    CHECK(mean_value(vector_to_limit.get(d), mesh) ==\n          approx(gsl::at(expected_vector_means, d)));\n  }\n\n  const auto expected_limiter_output =\n      [&logical_coords, &mesh](\n          const DataVector& input,\n          const std::array<double, VolumeDim> expected_slope) {\n        auto result =\n            make_with_value<DataVector>(input, mean_value(input, mesh));\n        for (size_t d = 0; d < VolumeDim; ++d) {\n          result += logical_coords.get(d) * gsl::at(expected_slope, d);\n        }\n        return result;\n      };\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    CHECK_ITERABLE_APPROX(\n        vector_to_limit.get(d),\n        expected_limiter_output(input_vector.get(d),\n                                gsl::at(target_vector_slope, d)));\n  }\n}\n\nvoid test_minmod_impl_1d_work(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const auto mesh = Mesh<1>(3, Spectral::Basis::Legendre, quadrature);\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto element_size = make_array<1>(0.5);\n  const auto true_slope = std::array<double, 1>{{2.0}};\n  const auto func =\n      [&true_slope](\n          const tnsr::I<DataVector, 1, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        return 1.0 + true_slope[0] * x + 3.3 * square(x);\n      };\n  const auto data = DataVector{func(logical_coords)};\n  const double mean = mean_value(data, mesh);\n  const auto input_vector = VectorTag<1>::type{data};\n\n  std::unordered_map<\n      DirectionalId<1>,\n      Limiters::Minmod<1, tmpl::list<VectorTag<1>>>::PackagedData,\n      boost::hash<DirectionalId<1>>>\n      neighbor_data{};\n  const std::array<DirectionalId<1>, 2> dir_keys = {\n      {{Direction<1>::lower_xi(), ElementId<1>(1)},\n       {Direction<1>::upper_xi(), ElementId<1>(2)}}};\n  neighbor_data[dir_keys[0]].element_size = element_size;\n  neighbor_data[dir_keys[1]].element_size = element_size;\n\n  // The vector x-component we treat as a smooth function: the limiter will\n  // linearize the data but not reduce its slope\n  const auto target_vector_slope =\n      std::array<std::array<double, 1>, 1>{{true_slope}};\n  get<Tags::Mean<VectorTag<1>>>(neighbor_data[dir_keys[0]].means) =\n      tnsr::I<double, 1>(mean - 2.0 * true_slope[0]);\n  get<Tags::Mean<VectorTag<1>>>(neighbor_data[dir_keys[1]].means) =\n      tnsr::I<double, 1>(mean + 2.0 * true_slope[0]);\n\n  test_minmod_impl_work(input_vector, mesh, logical_coords, element_size,\n                        neighbor_data, target_vector_slope);\n}\n\nvoid test_minmod_impl_1d() {\n  INFO(\"Testing minmod_impl in 1D\");\n  test_minmod_impl_1d_work(Spectral::Quadrature::GaussLobatto);\n  test_minmod_impl_1d_work(Spectral::Quadrature::Gauss);\n}\n\nvoid test_minmod_impl_2d_work(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  // We fill each vector component with the same volume data\n  const auto mesh = Mesh<2>(3, Spectral::Basis::Legendre, quadrature);\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto element_size = make_array(0.5, 1.0);\n  const auto true_slope = std::array<double, 2>{{2.0, -3.0}};\n  const auto& func =\n      [&true_slope](\n          const tnsr::I<DataVector, 2, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        return 1.0 + true_slope[0] * x + 3.3 * square(x) + true_slope[1] * y +\n               square(y);\n      };\n  const auto data = DataVector{func(logical_coords)};\n  const double mean = mean_value(data, mesh);\n  const auto input_vector = VectorTag<2>::type{data};\n\n  // We fill the neighbor mean data with different values for each vector\n  // component, so that each component is limited in a different way\n  std::unordered_map<\n      DirectionalId<2>,\n      Limiters::Minmod<2, tmpl::list<VectorTag<2>>>::PackagedData,\n      boost::hash<DirectionalId<2>>>\n      neighbor_data{};\n  const std::array<DirectionalId<2>, 4> dir_keys = {\n      {{Direction<2>::lower_xi(), ElementId<2>(1)},\n       {Direction<2>::upper_xi(), ElementId<2>(2)},\n       {Direction<2>::lower_eta(), ElementId<2>(3)},\n       {Direction<2>::upper_eta(), ElementId<2>(4)}}};\n  neighbor_data[dir_keys[0]].element_size = element_size;\n  neighbor_data[dir_keys[1]].element_size = element_size;\n  neighbor_data[dir_keys[2]].element_size = element_size;\n  neighbor_data[dir_keys[3]].element_size = element_size;\n\n  // - the x-component we treat as a smooth function: the limiter will linearize\n  //   the data but not reduce its slope\n  // - the y-component we treat as a shock in y-direction only\n  const auto target_vy_slope = std::array<double, 2>{{true_slope[0], -2.2}};\n  const auto target_vector_slope =\n      std::array<std::array<double, 2>, 2>{{true_slope, target_vy_slope}};\n  const auto neighbor_vx_slope =\n      std::array<double, 2>{{2.0 * true_slope[0], 2.0 * true_slope[1]}};\n  const auto neighbor_vy_slope =\n      std::array<double, 2>{{2.0 * true_slope[0], -2.2}};\n  const auto neighbor_vector_slope = std::array<std::array<double, 2>, 2>{\n      {neighbor_vx_slope, neighbor_vy_slope}};\n  const auto neighbor_vector_func = [&mean, &neighbor_vector_slope](\n                                        const size_t dim, const int sign) {\n    return tnsr::I<double, 2>{\n        {{mean + sign * gsl::at(neighbor_vector_slope[0], dim),\n          mean + sign * gsl::at(neighbor_vector_slope[1], dim)}}};\n  };\n  get<Tags::Mean<VectorTag<2>>>(neighbor_data[dir_keys[0]].means) =\n      neighbor_vector_func(0, -1);\n  get<Tags::Mean<VectorTag<2>>>(neighbor_data[dir_keys[1]].means) =\n      neighbor_vector_func(0, 1);\n  get<Tags::Mean<VectorTag<2>>>(neighbor_data[dir_keys[2]].means) =\n      neighbor_vector_func(1, -1);\n  get<Tags::Mean<VectorTag<2>>>(neighbor_data[dir_keys[3]].means) =\n      neighbor_vector_func(1, 1);\n\n  test_minmod_impl_work(input_vector, mesh, logical_coords, element_size,\n                        neighbor_data, target_vector_slope);\n}\n\n// Test that the minmod_impl limits a Scalar tensor in the x-direction only.\n// The VolumeDim may be greater than 1. This is a helper used for testing\n// minmod_impl with an h-refined domain that has multiple neighbors.\ntemplate <size_t VolumeDim>\nvoid test_minmod_impl_limits_in_x_only(\n    const ScalarTag::type& input, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, VolumeDim>& element_size,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename Limiters::Minmod<VolumeDim,\n                                  tmpl::list<ScalarTag>>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data,\n    const double expected_slope) {\n  auto input_to_limit = input;\n\n  const auto minmod_type = Limiters::MinmodType::LambdaPi1;\n  const double tvb_constant = 0.0;\n  DataVector u_lin_buffer(mesh.number_of_grid_points());\n  Limiters::Minmod_detail::BufferWrapper<VolumeDim> buffer(mesh);\n  const bool limiter_activated =\n      Limiters::Minmod_detail::minmod_impl<VolumeDim, ScalarTag>(\n          make_not_null(&u_lin_buffer), make_not_null(&buffer),\n          make_not_null(&input_to_limit), minmod_type, tvb_constant, mesh,\n          element, logical_coords, element_size, neighbor_data);\n\n  CHECK(limiter_activated);\n  const ScalarTag::type expected_output =\n      [&logical_coords, &mesh](const ScalarTag::type& in, const double slope) {\n        const double mean = mean_value(get(in), mesh);\n        return ScalarTag::type(mean + get<0>(logical_coords) * slope);\n      }(input, expected_slope);\n  CHECK_ITERABLE_APPROX(input_to_limit, expected_output);\n}\n\n// Make a 2D element with two neighbors in lower_xi, one neighbor in upper_xi.\n// Check that lower_xi data from two neighbors is correctly combined in the\n// limiting operation.\nvoid test_minmod_impl_two_lower_xi_neighbors() {\n  INFO(\"Testing minmod_impl in 2D with two neighbors in one direction\");\n  const SegmentId root_segment{0, 0};\n  const auto element = Element<2>{\n      ElementId<2>{0},\n      Element<2>::Neighbors_t{\n          {Direction<2>::lower_xi(),\n           {std::unordered_set<ElementId<2>>{\n                ElementId<2>(\n                    1, {{root_segment, root_segment.id_of_child(Side::Lower)}}),\n                ElementId<2>(1, {{root_segment,\n                                  root_segment.id_of_child(Side::Upper)}})},\n            OrientationMap<2>::create_aligned()}},\n          {Direction<2>::upper_xi(),\n           TestHelpers::Limiters::make_neighbor_with_id<2>(2)}}};\n  const auto mesh =\n      Mesh<2>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto logical_coords = logical_coordinates(mesh);\n  const double dx = 1.0;\n  const auto element_size = make_array<2>(dx);\n\n  const auto mean = 2.0;\n  const auto func = [&mean](const tnsr::I<DataVector, 2, Frame::ElementLogical>&\n                                coords) {\n    return mean + 1.2 * get<0>(coords);\n  };\n  const auto input = ScalarTag::type(func(logical_coords));\n\n  const auto make_neighbors =\n    [&dx,&root_segment](const double left1, const double left2,\n                        const double right, const double left1_size,\n                        const double left2_size) {\n    using Pack = Limiters::Minmod<2, tmpl::list<ScalarTag>>::PackagedData;\n    return std::unordered_map<DirectionalId<2>, Pack,\n                              boost::hash<DirectionalId<2>>>{\n        std::make_pair(\n            DirectionalId<2>{\n                Direction<2>::lower_xi(),\n                ElementId<2>(1, {{root_segment,\n                                  root_segment.id_of_child(Side::Lower)}})},\n            Pack{Scalar<double>(left1), make_array(left1_size, dx)}),\n        std::make_pair(\n            DirectionalId<2>{\n                Direction<2>::lower_xi(),\n                ElementId<2>(1, {{root_segment,\n                                  root_segment.id_of_child(Side::Upper)}})},\n            Pack{Scalar<double>(left2), make_array(left2_size, dx)}),\n        std::make_pair(\n            DirectionalId<2>{Direction<2>::upper_xi(), ElementId<2>(2)},\n            Pack{Scalar<double>(right), make_array<2>(dx)}),\n    };\n  };\n\n  // Make two left neighbors with different mean values\n  const auto neighbor_data_two_means =\n      make_neighbors(mean - 1.1, mean - 1.0, mean + 1.4, dx, dx);\n  // Effective neighbor mean (1.1 + 1.0) / 2.0 => 1.05\n  test_minmod_impl_limits_in_x_only(input, mesh, element, logical_coords,\n                                    element_size, neighbor_data_two_means,\n                                    1.05);\n\n  // Make two left neighbors with different means and sizes\n  const auto neighbor_data_two_sizes =\n      make_neighbors(mean - 1.1, mean - 1.0, mean + 1.4, dx, 0.5 * dx);\n  // Effective neighbor mean (1.1 + 1.0) / 2.0 => 1.05\n  // Average neighbor size (1.0 + 0.5) / 2.0 => 0.75\n  // Effective distance (0.75 + 1.0) / 2.0 => 0.875\n  test_minmod_impl_limits_in_x_only(input, mesh, element, logical_coords,\n                                    element_size, neighbor_data_two_sizes,\n                                    1.05 / 0.875);\n}\n\nvoid test_minmod_impl_2d() {\n  INFO(\"Testing minmod_impl in 2D\");\n  test_minmod_impl_2d_work(Spectral::Quadrature::GaussLobatto);\n  test_minmod_impl_2d_work(Spectral::Quadrature::Gauss);\n\n  test_minmod_impl_two_lower_xi_neighbors();\n}\n\nvoid test_minmod_impl_3d_work(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  // We fill each vector component with the same volume data\n  const auto mesh = Mesh<3>({{3, 3, 4}}, Spectral::Basis::Legendre, quadrature);\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto element_size = make_array(0.5, 1.0, 0.8);\n  const auto true_slope = std::array<double, 3>{{2.0, -3.0, 1.0}};\n  const auto func =\n      [&true_slope](\n          const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        const auto& z = get<2>(coords);\n        return 1.0 + true_slope[0] * x + 3.3 * square(x) + true_slope[1] * y +\n               square(y) + true_slope[2] * z - square(z);\n      };\n  const auto data = DataVector{func(logical_coords)};\n  const double mean = mean_value(data, mesh);\n  const auto input_vector = VectorTag<3>::type{data};\n\n  // We fill the neighbor mean data with different values for each vector\n  // component, so that each component is limited in a different way\n  std::unordered_map<\n      DirectionalId<3>,\n      Limiters::Minmod<3, tmpl::list<VectorTag<3>>>::PackagedData,\n      boost::hash<DirectionalId<3>>>\n      neighbor_data{};\n  const std::array<DirectionalId<3>, 6> dir_keys = {\n      {{Direction<3>::lower_xi(), ElementId<3>(1)},\n       {Direction<3>::upper_xi(), ElementId<3>(2)},\n       {Direction<3>::lower_eta(), ElementId<3>(3)},\n       {Direction<3>::upper_eta(), ElementId<3>(4)},\n       {Direction<3>::lower_zeta(), ElementId<3>(5)},\n       {Direction<3>::upper_zeta(), ElementId<3>(6)}}};\n  for (const auto& id_pair : dir_keys) {\n    neighbor_data[id_pair].element_size = element_size;\n  }\n\n  // - the x-component we treat as a smooth function: the limiter will linearize\n  //   the data but not reduce its slope\n  // - the y-component we treat as a shock in z-direction only\n  // - the z-component we treat as a local maximum, so neighbors < mean\n  const auto target_vy_slope =\n      std::array<double, 3>{{true_slope[0], true_slope[1], 0.1}};\n  const auto target_vz_slope = std::array<double, 3>{{0.0, 0.0, 0.0}};\n  const auto target_vector_slope = std::array<std::array<double, 3>, 3>{\n      {true_slope, target_vy_slope, target_vz_slope}};\n  const auto neighbor_vx_slope = std::array<double, 3>{\n      {2.0 * true_slope[0], 2.0 * true_slope[1], 2.0 * true_slope[2]}};\n  const auto neighbor_vy_slope =\n      std::array<double, 3>{{2.0 * true_slope[0], 2.0 * true_slope[1], 0.1}};\n  const auto neighbor_vector_slope = std::array<std::array<double, 3>, 2>{\n      {neighbor_vx_slope, neighbor_vy_slope}};\n  // This function generates the desired neighbor mean value\n  const auto neighbor_vector_func = [&mean, &neighbor_vector_slope](\n                                        const size_t dim, const int sign) {\n    return tnsr::I<double, 3>{\n        {{mean + sign * gsl::at(neighbor_vector_slope[0], dim),\n          mean + sign * gsl::at(neighbor_vector_slope[1], dim),\n          mean - 1.1 - dim - sign}}};  // arbitrary, but smaller than mean\n  };\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[0]].means) =\n      neighbor_vector_func(0, -1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[1]].means) =\n      neighbor_vector_func(0, 1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[2]].means) =\n      neighbor_vector_func(1, -1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[3]].means) =\n      neighbor_vector_func(1, 1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[4]].means) =\n      neighbor_vector_func(2, -1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[5]].means) =\n      neighbor_vector_func(2, 1);\n\n  test_minmod_impl_work(input_vector, mesh, logical_coords, element_size,\n                        neighbor_data, target_vector_slope);\n}\n\n// Similar to the 2D case, but in 3D and with 4 upper_xi neighbors\nvoid test_minmod_impl_four_upper_xi_neighbors() {\n  INFO(\"Testing minmod_impl in 3D with four neighbors in one direction\");\n  const SegmentId root_segment{0, 0};\n  const auto element = Element<3>{\n      ElementId<3>{0},\n      Element<3>::Neighbors_t{\n          {Direction<3>::lower_xi(),\n           TestHelpers::Limiters::make_neighbor_with_id<3>(1)},\n          {Direction<3>::upper_xi(),\n           {std::unordered_set<ElementId<3>>{\n                ElementId<3>(\n                    2, {root_segment, root_segment.id_of_child(Side::Lower),\n                        root_segment.id_of_child(Side::Lower)}),\n                ElementId<3>(\n                    2, {root_segment, root_segment.id_of_child(Side::Lower),\n                        root_segment.id_of_child(Side::Upper)}),\n                ElementId<3>(\n                    2, {root_segment, root_segment.id_of_child(Side::Upper),\n                        root_segment.id_of_child(Side::Lower)}),\n                ElementId<3>(\n                    2, {root_segment, root_segment.id_of_child(Side::Upper),\n                        root_segment.id_of_child(Side::Upper)})},\n            OrientationMap<3>::create_aligned()}}}};\n  const auto mesh =\n      Mesh<3>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto logical_coords = logical_coordinates(mesh);\n  const double dx = 1.0;\n  const auto element_size = make_array<3>(dx);\n\n  const auto mean = 2.0;\n  const auto func = [&mean](const tnsr::I<DataVector, 3, Frame::ElementLogical>&\n                                coords) {\n    return mean + 1.2 * get<0>(coords);\n  };\n  const auto input = ScalarTag::type(func(logical_coords));\n\n  const auto make_neighbors =\n    [&dx, &root_segment](const double left, const double right1,\n                         const double right2, const double right3,\n                         const double right4, const double right1_size,\n                         const double right2_size, const double right3_size,\n                         const double right4_size) {\n        using Pack = Limiters::Minmod<3, tmpl::list<ScalarTag>>::PackagedData;\n        return std::unordered_map<DirectionalId<3>, Pack,\n                                  boost::hash<DirectionalId<3>>>{\n            std::make_pair(\n                DirectionalId<3>{Direction<3>::lower_xi(), ElementId<3>(1)},\n                Pack{Scalar<double>(left), make_array<3>(dx)}),\n            std::make_pair(\n                DirectionalId<3>{\n                    Direction<3>::upper_xi(),\n                    ElementId<3>(\n                        2, {root_segment, root_segment.id_of_child(Side::Lower),\n                            root_segment.id_of_child(Side::Lower)})},\n                Pack{Scalar<double>(right1), make_array(right1_size, dx, dx)}),\n            std::make_pair(\n                DirectionalId<3>{\n                    Direction<3>::upper_xi(),\n                    ElementId<3>(\n                        2, {root_segment, root_segment.id_of_child(Side::Lower),\n                            root_segment.id_of_child(Side::Upper)})},\n                Pack{Scalar<double>(right2), make_array(right2_size, dx, dx)}),\n            std::make_pair(\n                DirectionalId<3>{\n                    Direction<3>::upper_xi(),\n                    ElementId<3>(\n                        2, {root_segment, root_segment.id_of_child(Side::Upper),\n                            root_segment.id_of_child(Side::Lower)})},\n                Pack{Scalar<double>(right3), make_array(right3_size, dx, dx)}),\n            std::make_pair(\n                DirectionalId<3>{\n                    Direction<3>::upper_xi(),\n                    ElementId<3>(\n                        2, {root_segment, root_segment.id_of_child(Side::Upper),\n                            root_segment.id_of_child(Side::Upper)})},\n                Pack{Scalar<double>(right4), make_array(right4_size, dx, dx)}),\n        };\n      };\n\n  // Make four right neighbors with different mean values\n  const auto neighbor_data_two_means =\n      make_neighbors(mean - 1.4, mean + 1.0, mean + 1.1, mean - 0.2, mean + 1.8,\n                     dx, dx, dx, dx);\n  // Effective neighbor mean (1.0 + 1.1 - 0.2 + 1.8) / 4.0 => 0.925\n  test_minmod_impl_limits_in_x_only(input, mesh, element, logical_coords,\n                                    element_size, neighbor_data_two_means,\n                                    0.925);\n\n  // Make four right neighbors with different means and sizes\n  const auto neighbor_data_two_sizes =\n      make_neighbors(mean - 1.4, mean + 1.0, mean + 1.1, mean - 0.2, mean + 1.8,\n                     dx, 0.5 * dx, 0.5 * dx, 0.5 * dx);\n  // Effective neighbor mean (1.0 + 1.1 - 0.2 + 1.8) / 4.0 => 0.925\n  // Average neighbor size (1.0 + 0.5 + 0.5 + 0.5) / 4.0 => 0.625\n  // Effective distance (0.625 + 1.0) / 2.0 => 0.8125\n  test_minmod_impl_limits_in_x_only(input, mesh, element, logical_coords,\n                                    element_size, neighbor_data_two_sizes,\n                                    0.925 / 0.8125);\n}\n\nvoid test_minmod_impl_3d() {\n  INFO(\"Testing minmod_impl in 3D\");\n  test_minmod_impl_3d_work(Spectral::Quadrature::GaussLobatto);\n  test_minmod_impl_3d_work(Spectral::Quadrature::Gauss);\n\n  test_minmod_impl_four_upper_xi_neighbors();\n}\n\n// Helper function for testing Minmod::operator()\n//\n// For simplicity, this helper is similar to test_minmod_impl_work above, except\n// that it calls the limiter on multiple tensors to check they are each handled\n// independently. This helper does not test the limiter using different\n// quadratures or configurations with h-refinement... that is left to the test\n// of minmod_impl.\ntemplate <size_t VolumeDim>\nvoid test_minmod_work(\n    const Scalar<DataVector>& input_scalar,\n    const tnsr::I<DataVector, VolumeDim>& input_vector,\n    const Mesh<VolumeDim>& mesh,\n    const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, VolumeDim>& element_size,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename Limiters::Minmod<\n            VolumeDim,\n            tmpl::list<ScalarTag, VectorTag<VolumeDim>>>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data,\n    const std::array<double, VolumeDim>& target_scalar_slope,\n    const std::array<std::array<double, VolumeDim>, VolumeDim>&\n        target_vector_slope) {\n  auto scalar_to_limit = input_scalar;\n  auto vector_to_limit = input_vector;\n\n  // Minmod should preserve the mean, so expected = initial\n  const double expected_scalar_mean = mean_value(get(scalar_to_limit), mesh);\n  const auto expected_vector_means = [&vector_to_limit, &mesh]() {\n    std::array<double, VolumeDim> means{};\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      gsl::at(means, d) = mean_value(vector_to_limit.get(d), mesh);\n    }\n    return means;\n  }();\n\n  const auto element = TestHelpers::Limiters::make_element<VolumeDim>();\n  const double tvb_constant = 0.0;\n  const Limiters::Minmod<VolumeDim, tmpl::list<ScalarTag, VectorTag<VolumeDim>>>\n      minmod(Limiters::MinmodType::LambdaPi1, tvb_constant);\n  const bool limiter_activated =\n      minmod(make_not_null(&scalar_to_limit), make_not_null(&vector_to_limit),\n             mesh, element, logical_coords, element_size, neighbor_data);\n\n  CHECK(limiter_activated);\n\n  CHECK(mean_value(get(scalar_to_limit), mesh) == approx(expected_scalar_mean));\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    CHECK(mean_value(vector_to_limit.get(d), mesh) ==\n          approx(gsl::at(expected_vector_means, d)));\n  }\n\n  const auto expected_limiter_output =\n      [&logical_coords, &mesh](\n          const DataVector& input,\n          const std::array<double, VolumeDim> expected_slope) {\n        auto result =\n            make_with_value<DataVector>(input, mean_value(input, mesh));\n        for (size_t d = 0; d < VolumeDim; ++d) {\n          result += logical_coords.get(d) * gsl::at(expected_slope, d);\n        }\n        return result;\n      };\n\n  CHECK_ITERABLE_APPROX(\n      get(scalar_to_limit),\n      expected_limiter_output(get(input_scalar), target_scalar_slope));\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    CHECK_ITERABLE_APPROX(\n        vector_to_limit.get(d),\n        expected_limiter_output(input_vector.get(d),\n                                gsl::at(target_vector_slope, d)));\n  }\n}\n\nvoid test_minmod_1d() {\n  INFO(\"Testing Minmod limiter in 1D\");\n  // This test checks that Minmod limits different tensor components\n  // independently\n  //\n  // We fill each local tensor component with the same volume data\n  const auto mesh =\n      Mesh<1>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto element_size = make_array<1>(0.5);\n  const auto true_slope = std::array<double, 1>{{2.0}};\n  const auto func =\n      [&true_slope](\n          const tnsr::I<DataVector, 1, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        return 1.0 + true_slope[0] * x + 3.3 * square(x);\n      };\n  const auto data = DataVector{func(logical_coords)};\n  const double mean = mean_value(data, mesh);\n  const auto input_scalar = ScalarTag::type{data};\n  const auto input_vector = VectorTag<1>::type{data};\n\n  // We fill the neighbor mean data with different values for each tensor\n  // component, so that each component is limited in a different way\n  std::unordered_map<\n      DirectionalId<1>,\n      Limiters::Minmod<1, tmpl::list<ScalarTag, VectorTag<1>>>::PackagedData,\n      boost::hash<DirectionalId<1>>>\n      neighbor_data{};\n  const std::array<DirectionalId<1>, 2> dir_keys = {\n      {{Direction<1>::lower_xi(), ElementId<1>(1)},\n       {Direction<1>::upper_xi(), ElementId<1>(2)}}};\n  neighbor_data[dir_keys[0]].element_size = element_size;\n  neighbor_data[dir_keys[1]].element_size = element_size;\n\n  // The scalar we treat as a shock: we want the slope to be reduced\n  const auto target_scalar_slope = std::array<double, 1>{{1.2}};\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[0]].means) =\n      Scalar<double>(mean - target_scalar_slope[0]);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[1]].means) =\n      Scalar<double>(mean + target_scalar_slope[0]);\n\n  // The vector x-component we treat as a smooth function: the limiter will\n  // linearize the data but not reduce its slope\n  const auto target_vector_slope =\n      std::array<std::array<double, 1>, 1>{{true_slope}};\n  get<Tags::Mean<VectorTag<1>>>(neighbor_data[dir_keys[0]].means) =\n      tnsr::I<double, 1>(mean - 2.0 * true_slope[0]);\n  get<Tags::Mean<VectorTag<1>>>(neighbor_data[dir_keys[1]].means) =\n      tnsr::I<double, 1>(mean + 2.0 * true_slope[0]);\n\n  test_minmod_work(input_scalar, input_vector, mesh, logical_coords,\n                   element_size, neighbor_data, target_scalar_slope,\n                   target_vector_slope);\n}\n\nvoid test_minmod_2d() {\n  INFO(\"Testing Minmod limiter in 2D\");\n  // This test checks that Minmod limits...\n  // - different tensor components independently\n  // - different dimensions independently\n  //\n  // We fill each local tensor component with the same volume data\n  const auto mesh =\n      Mesh<2>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto element_size = make_array(0.5, 1.0);\n  const auto true_slope = std::array<double, 2>{{2.0, -3.0}};\n  const auto& func =\n      [&true_slope](\n          const tnsr::I<DataVector, 2, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        return 1.0 + true_slope[0] * x + 3.3 * square(x) + true_slope[1] * y +\n               square(y);\n      };\n  const auto data = DataVector{func(logical_coords)};\n  const double mean = mean_value(data, mesh);\n  const auto input_scalar = ScalarTag::type{data};\n  const auto input_vector = VectorTag<2>::type{data};\n\n  // We fill the neighbor mean data with different values for each tensor\n  // component, so that each component is limited in a different way\n  std::unordered_map<\n      DirectionalId<2>,\n      Limiters::Minmod<2, tmpl::list<ScalarTag, VectorTag<2>>>::PackagedData,\n      boost::hash<DirectionalId<2>>>\n      neighbor_data{};\n  const std::array<DirectionalId<2>, 4> dir_keys = {\n      {{Direction<2>::lower_xi(), ElementId<2>(1)},\n       {Direction<2>::upper_xi(), ElementId<2>(2)},\n       {Direction<2>::lower_eta(), ElementId<2>(3)},\n       {Direction<2>::upper_eta(), ElementId<2>(4)}}};\n  neighbor_data[dir_keys[0]].element_size = element_size;\n  neighbor_data[dir_keys[1]].element_size = element_size;\n  neighbor_data[dir_keys[2]].element_size = element_size;\n  neighbor_data[dir_keys[3]].element_size = element_size;\n\n  // The scalar we treat as a 3D shock: we want each slope to be reduced\n  const auto target_scalar_slope = std::array<double, 2>{{1.2, -2.2}};\n  const auto neighbor_scalar_func = [&mean, &target_scalar_slope](\n                                        const size_t dim, const int sign) {\n    return Scalar<double>(mean + sign * gsl::at(target_scalar_slope, dim));\n  };\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[0]].means) =\n      neighbor_scalar_func(0, -1);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[1]].means) =\n      neighbor_scalar_func(0, 1);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[2]].means) =\n      neighbor_scalar_func(1, -1);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[3]].means) =\n      neighbor_scalar_func(1, 1);\n\n  // The vector we treat differently in each component, to check the limiter\n  // acts independently on each:\n  // - the x-component we treat as a smooth function: the limiter will linearize\n  //   the data but not reduce its slope\n  // - the y-component we treat as a shock in y-direction only\n  const auto target_vy_slope = std::array<double, 2>{{true_slope[0], -2.2}};\n  const auto target_vector_slope =\n      std::array<std::array<double, 2>, 2>{{true_slope, target_vy_slope}};\n  const auto neighbor_vx_slope =\n      std::array<double, 2>{{2.0 * true_slope[0], 2.0 * true_slope[1]}};\n  const auto neighbor_vy_slope =\n      std::array<double, 2>{{2.0 * true_slope[0], -2.2}};\n  const auto neighbor_vector_slope = std::array<std::array<double, 2>, 2>{\n      {neighbor_vx_slope, neighbor_vy_slope}};\n  const auto neighbor_vector_func = [&mean, &neighbor_vector_slope](\n                                        const size_t dim, const int sign) {\n    return tnsr::I<double, 2>{\n        {{mean + sign * gsl::at(neighbor_vector_slope[0], dim),\n          mean + sign * gsl::at(neighbor_vector_slope[1], dim)}}};\n  };\n  get<Tags::Mean<VectorTag<2>>>(neighbor_data[dir_keys[0]].means) =\n      neighbor_vector_func(0, -1);\n  get<Tags::Mean<VectorTag<2>>>(neighbor_data[dir_keys[1]].means) =\n      neighbor_vector_func(0, 1);\n  get<Tags::Mean<VectorTag<2>>>(neighbor_data[dir_keys[2]].means) =\n      neighbor_vector_func(1, -1);\n  get<Tags::Mean<VectorTag<2>>>(neighbor_data[dir_keys[3]].means) =\n      neighbor_vector_func(1, 1);\n\n  test_minmod_work(input_scalar, input_vector, mesh, logical_coords,\n                   element_size, neighbor_data, target_scalar_slope,\n                   target_vector_slope);\n}\n\nvoid test_minmod_3d() {\n  INFO(\"Testing Minmod limiter in 3D\");\n  // This test checks that Minmod limits...\n  // - different tensor components independently\n  // - different dimensions independently\n  //\n  // We fill each local tensor component with the same volume data\n  const auto mesh = Mesh<3>({{3, 3, 4}}, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto);\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto element_size = make_array(0.5, 1.0, 0.8);\n  const auto true_slope = std::array<double, 3>{{2.0, -3.0, 1.0}};\n  const auto func =\n      [&true_slope](\n          const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        const auto& z = get<2>(coords);\n        return 1.0 + true_slope[0] * x + 3.3 * square(x) + true_slope[1] * y +\n               square(y) + true_slope[2] * z - square(z);\n      };\n  const auto data = DataVector{func(logical_coords)};\n  const double mean = mean_value(data, mesh);\n  const auto input_scalar = ScalarTag::type{data};\n  const auto input_vector = VectorTag<3>::type{data};\n\n  // We fill the neighbor mean data with different values for each tensor\n  // component, so that each component is limited in a different way\n  std::unordered_map<\n      DirectionalId<3>,\n      Limiters::Minmod<3, tmpl::list<ScalarTag, VectorTag<3>>>::PackagedData,\n      boost::hash<DirectionalId<3>>>\n      neighbor_data{};\n  const std::array<DirectionalId<3>, 6> dir_keys = {\n      {{Direction<3>::lower_xi(), ElementId<3>(1)},\n       {Direction<3>::upper_xi(), ElementId<3>(2)},\n       {Direction<3>::lower_eta(), ElementId<3>(3)},\n       {Direction<3>::upper_eta(), ElementId<3>(4)},\n       {Direction<3>::lower_zeta(), ElementId<3>(5)},\n       {Direction<3>::upper_zeta(), ElementId<3>(6)}}};\n  for (const auto& id_pair : dir_keys) {\n    neighbor_data[id_pair].element_size = element_size;\n  }\n\n  // The scalar we treat as a 3D shock: we want each slope to be reduced\n  const auto target_scalar_slope = std::array<double, 3>{{1.2, -2.2, 0.1}};\n  // This function generates the desired neighbor mean value by extrapolating\n  // from the local mean value and the desired post-limiting slope\n  const auto neighbor_scalar_func = [&mean, &target_scalar_slope](\n                                        const size_t dim, const int sign) {\n    // The neighbor values are constructed according to\n    //   mean_neighbor = mean +/- target_slope * (center_distance / 2.0)\n    // which enables easy control of whether the local slope should be reduced\n    // by the limiter. This expresion simplifies in logical coordinates,\n    // because the center-to-center distance to the neighbor element is 2.0:\n    return Scalar<double>(mean + sign * gsl::at(target_scalar_slope, dim));\n  };\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[0]].means) =\n      neighbor_scalar_func(0, -1);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[1]].means) =\n      neighbor_scalar_func(0, 1);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[2]].means) =\n      neighbor_scalar_func(1, -1);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[3]].means) =\n      neighbor_scalar_func(1, 1);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[4]].means) =\n      neighbor_scalar_func(2, -1);\n  get<Tags::Mean<ScalarTag>>(neighbor_data[dir_keys[5]].means) =\n      neighbor_scalar_func(2, 1);\n\n  // The vector we treat differently in each component, to verify that the\n  // limiter acts independently on each:\n  // - the x-component we treat as a smooth function: the limiter will linearize\n  //   the data but not reduce its slope\n  // - the y-component we treat as a shock in z-direction only\n  // - the z-component we treat as a local maximum, so neighbors < mean\n  // For components/directions where we want no limiter action, the desired\n  // slope is just the input slope.\n  const auto target_vy_slope =\n      std::array<double, 3>{{true_slope[0], true_slope[1], 0.1}};\n  const auto target_vz_slope = std::array<double, 3>{{0.0, 0.0, 0.0}};\n  const auto target_vector_slope = std::array<std::array<double, 3>, 3>{\n      {true_slope, target_vy_slope, target_vz_slope}};\n  const auto neighbor_vx_slope = std::array<double, 3>{\n      {2.0 * true_slope[0], 2.0 * true_slope[1], 2.0 * true_slope[2]}};\n  const auto neighbor_vy_slope =\n      std::array<double, 3>{{2.0 * true_slope[0], 2.0 * true_slope[1], 0.1}};\n  const auto neighbor_vector_slope = std::array<std::array<double, 3>, 2>{\n      {neighbor_vx_slope, neighbor_vy_slope}};\n  // This function generates the desired neighbor mean value\n  const auto neighbor_vector_func = [&mean, &neighbor_vector_slope](\n                                        const size_t dim, const int sign) {\n    return tnsr::I<double, 3>{\n        {{mean + sign * gsl::at(neighbor_vector_slope[0], dim),\n          mean + sign * gsl::at(neighbor_vector_slope[1], dim),\n          mean - 1.1 - dim - sign}}};  // arbitrary, but smaller than mean\n  };\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[0]].means) =\n      neighbor_vector_func(0, -1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[1]].means) =\n      neighbor_vector_func(0, 1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[2]].means) =\n      neighbor_vector_func(1, -1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[3]].means) =\n      neighbor_vector_func(1, 1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[4]].means) =\n      neighbor_vector_func(2, -1);\n  get<Tags::Mean<VectorTag<3>>>(neighbor_data[dir_keys[5]].means) =\n      neighbor_vector_func(2, 1);\n\n  test_minmod_work(input_scalar, input_vector, mesh, logical_coords,\n                   element_size, neighbor_data, target_scalar_slope,\n                   target_vector_slope);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.Minmod\", \"[Limiters][Unit]\") {\n  test_minmod_option_parsing();\n  test_minmod_serialization();\n\n  test_package_data_1d();\n  test_package_data_2d();\n  test_package_data_3d();\n\n  // Test `minmod_limited_slopes`, the helper for `minmod_impl` that computes\n  // the limited (=> reduced) slopes when the TCI requests limiting. This is a\n  // detailed test of the helper in many configurations of function shape, grid\n  // resolution, TCI settings, and so on.\n  test_minmod_limited_slopes_1d();\n  test_minmod_limited_slopes_2d();\n  test_minmod_limited_slopes_3d();\n\n  // Test `minmod_impl`, which implements the Minmod limiter work for one\n  // tensor at a time. This is a test that the limited slopes are correctly used\n  // to linearize the data, and that each tensor component is limited\n  // independently.\n  //\n  // Note that these functions do not test in detail the slopes themselves. In\n  // other words, we trust that the above tests of `minmod_limited_slopes` pass.\n  test_minmod_impl_1d();\n  test_minmod_impl_2d();\n  test_minmod_impl_3d();\n\n  // Test everything together by applying the Minmod limiter to multiple\n  // tensors. This tests that `minmod_impl` is correctly applied to each tensor.\n  //\n  // Note that these functions do not test in detail the limiting procedure\n  // itself. In other words, we trust that the above tests of `minmod_impl`\n  // pass.\n  test_minmod_1d();\n  test_minmod_2d();\n  test_minmod_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_MinmodTci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <functional>\n#include <limits>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodTci.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\n\nauto make_two_neighbors(const double left, const double right) {\n  DirectionMap<1, double> result;\n  result[Direction<1>::lower_xi()] = left;\n  result[Direction<1>::upper_xi()] = right;\n  return result;\n}\n\nauto make_four_neighbors(const std::array<double, 4>& values) {\n  DirectionMap<2, double> result;\n  result[Direction<2>::lower_xi()] = values[0];\n  result[Direction<2>::upper_xi()] = values[1];\n  result[Direction<2>::lower_eta()] = values[2];\n  result[Direction<2>::upper_eta()] = values[3];\n  return result;\n}\n\nauto make_six_neighbors(const std::array<double, 6>& values) {\n  DirectionMap<3, double> result;\n  result[Direction<3>::lower_xi()] = values[0];\n  result[Direction<3>::upper_xi()] = values[1];\n  result[Direction<3>::lower_eta()] = values[2];\n  result[Direction<3>::upper_eta()] = values[3];\n  result[Direction<3>::lower_zeta()] = values[4];\n  result[Direction<3>::upper_zeta()] = values[5];\n  return result;\n}\n\n// Test that TCI detects a troubled cell when expected\ntemplate <size_t VolumeDim>\nvoid test_tci_detection(\n    const bool expected_detection, const double tvb_constant,\n    const DataVector& input, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_means,\n    const DirectionMap<VolumeDim, double>& effective_neighbor_sizes) {\n  Limiters::Minmod_detail::BufferWrapper<VolumeDim> buffer(mesh);\n  const bool detection = Limiters::Tci::tvb_minmod_indicator(\n      make_not_null(&buffer), tvb_constant, input, mesh, element, element_size,\n      effective_neighbor_means, effective_neighbor_sizes);\n  CHECK(detection == expected_detection);\n}\n\nvoid test_tci_on_linear_function(const size_t number_of_grid_points,\n                                 const Spectral::Quadrature quadrature) {\n  INFO(\"Testing TCI on linear function\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(quadrature);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n\n  // Lambda takes tvb_scale = tvb_constant * h^2, to facilitate specifying\n  // critical threshold values for testing\n  const auto test_tci = [&mesh, &element](\n                            const bool expected, const DataVector& input,\n                            const double left_mean, const double right_mean,\n                            const double tvb_scale) {\n    const double h = 1.2;\n    const double tvb_constant = tvb_scale / square(h);\n    const auto element_size = make_array<1>(h);\n    test_tci_detection(expected, tvb_constant, input, mesh, element,\n                       element_size, make_two_neighbors(left_mean, right_mean),\n                       make_two_neighbors(h, h));\n  };\n\n  // mean = 1.6\n  // delta_left = delta_right = 0.2\n  const DataVector input = [&mesh]() {\n    const auto x = get<0>(logical_coordinates(mesh));\n    return DataVector{1.6 + 0.2 * x};\n  }();\n\n  // Test trigger due to left, right neighbors\n  test_tci(false, input, 1.35, 1.85, 0.0);\n  test_tci(true, input, 1.45, 1.85, 0.0);\n  test_tci(true, input, 1.35, 1.75, 0.0);\n  test_tci(true, input, 1.45, 1.75, 0.0);\n\n  // Test trigger due to slope changing sign\n  test_tci(true, input, 1.7, 1.85, 0.0);\n  test_tci(true, input, 1.45, 1.5, 0.0);\n\n  // Test TVB can avoid the triggers\n  test_tci(true, input, 1.45, 1.75, 0.19);\n  test_tci(true, input, 1.7, 1.85, 0.19);\n  test_tci(true, input, 1.45, 1.5, 0.19);\n  test_tci(false, input, 1.45, 1.75, 0.21);\n  test_tci(false, input, 1.7, 1.85, 0.21);\n  test_tci(false, input, 1.45, 1.5, 0.21);\n}\n\nvoid test_tci_on_quadratic_function(const size_t number_of_grid_points,\n                                    const Spectral::Quadrature quadrature) {\n  INFO(\"Testing TCI on quadratic function\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(quadrature);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n\n  // Lambda takes tvb_scale = tvb_constant * h^2, to facilitate specifying\n  // critical threshold values for testing\n  const auto test_tci = [&mesh, &element](\n                            const bool expected, const DataVector& input,\n                            const double left_mean, const double right_mean,\n                            const double tvb_scale) {\n    const double h = 1.2;\n    const double tvb_constant = tvb_scale / square(h);\n    const auto element_size = make_array<1>(h);\n    test_tci_detection(expected, tvb_constant, input, mesh, element,\n                       element_size, make_two_neighbors(left_mean, right_mean),\n                       make_two_neighbors(h, h));\n  };\n\n  // mean = 1.5\n  // delta_left = 0.1\n  // delta_right = 0.3\n  const DataVector input = [&mesh]() {\n    const auto x = get<0>(logical_coordinates(mesh));\n    return DataVector{1.45 + 0.2 * x + 0.15 * square(x)};\n  }();\n\n  // Test trigger due to left, right neighbors\n  test_tci(false, input, 1.15, 1.85, 0.0);\n  test_tci(true, input, 1.25, 1.85, 0.0);\n  test_tci(true, input, 1.15, 1.75, 0.0);\n  test_tci(true, input, 1.25, 1.75, 0.0);\n\n  // Test TVB can avoid the trigger\n  test_tci(true, input, 1.25, 1.85, 0.11);\n  test_tci(true, input, 1.25, 1.85, 0.29);\n  test_tci(false, input, 1.25, 1.85, 0.31);\n\n  // mean = 1.5\n  // delta_left = -0.1\n  // delta_right = 0.3\n  const DataVector input2 = [&mesh]() {\n    const auto x = get<0>(logical_coordinates(mesh));\n    return DataVector{1.4 + 0.1 * x + 0.3 * square(x)};\n  }();\n\n  // Because left-to-mean and mean-to-right slopes have different signs,\n  // any TCI call with TVB=0 should trigger\n  test_tci(true, input2, 1.7, 1.9, 0.0);\n  test_tci(true, input2, 1.7, 1.3, 0.0);\n  test_tci(true, input2, 1.3, 1.7, 0.0);\n  test_tci(true, input2, 1.1, 1.9, 0.0);\n\n  // Conversely, because left-to-mean and mean-to-right slopes have different\n  // signs, calls with TVB>0 give results that depend on neighbor means\n  test_tci(true, input2, 1.7, 1.3, 0.11);\n  test_tci(true, input2, 1.7, 1.3, 0.29);\n  test_tci(false, input2, 1.7, 1.3, 0.31);\n\n  test_tci(true, input2, 1.3, 1.7, 0.11);\n  test_tci(true, input2, 1.3, 1.7, 0.29);\n  test_tci(false, input2, 1.3, 1.7, 0.31);\n\n  test_tci(false, input2, 1.1, 1.9, 0.11);\n  test_tci(false, input2, 1.1, 1.9, 0.29);\n  test_tci(false, input2, 1.1, 1.9, 0.31);\n}\n\nvoid test_tci_at_boundary(const size_t number_of_grid_points,\n                          const Spectral::Quadrature quadrature) {\n  INFO(\"Testing TCI at boundary\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(quadrature);\n  const double tvb_constant = 0.0;\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n  const auto element_size = make_array<1>(2.0);\n\n  const auto input = [&mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    return DataVector{1.2 * get<0>(coords)};\n  }();\n\n  // Test with element that has external lower-xi boundary\n  const auto element_at_lower_xi_boundary =\n      TestHelpers::Limiters::make_element<1>({{Direction<1>::lower_xi()}});\n  for (const double neighbor_mean : {-1.21, -1.19, 0.0, 1.19, 1.21}) {\n    const bool expected_tci = neighbor_mean < 1.2;\n    test_tci_detection(\n        expected_tci, tvb_constant, input, mesh, element_at_lower_xi_boundary,\n        element_size,\n        {{std::make_pair(Direction<1>::upper_xi(), neighbor_mean)}},\n        {{std::make_pair(Direction<1>::upper_xi(), element_size[0])}});\n  }\n\n  // Test with element that has external upper-xi boundary\n  const auto element_at_upper_xi_boundary =\n      TestHelpers::Limiters::make_element<1>({{Direction<1>::upper_xi()}});\n  for (const double neighbor_mean : {-1.21, -1.19, 0.0, 1.19, 1.21}) {\n    const bool expected_tci = neighbor_mean > -1.2;\n    test_tci_detection(\n        expected_tci, tvb_constant, input, mesh, element_at_upper_xi_boundary,\n        element_size,\n        {{std::make_pair(Direction<1>::lower_xi(), neighbor_mean)}},\n        {{std::make_pair(Direction<1>::lower_xi(), element_size[0])}});\n  }\n\n  const auto element_with_no_neighbors = TestHelpers::Limiters::make_element<1>(\n      {{Direction<1>::lower_xi(), Direction<1>::upper_xi()}});\n  test_tci_detection(false, tvb_constant, input, mesh,\n                     element_with_no_neighbors, element_size, {}, {});\n}\n\nvoid test_tci_with_different_size_neighbor(\n    const size_t number_of_grid_points, const Spectral::Quadrature quadrature) {\n  INFO(\"Testing TCI with neighboring elements of different size\");\n  CAPTURE(number_of_grid_points);\n  CAPTURE(quadrature);\n  const double tvb_constant = 0.0;\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const Mesh<1> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     quadrature);\n  const double dx = 1.0;\n  const auto element_size = make_array<1>(dx);\n\n  const auto test_tci = [&tvb_constant, &element, &mesh, &element_size](\n                            const bool expected_detection,\n                            const DataVector& local_input, const double left,\n                            const double right, const double left_size,\n                            const double right_size) {\n    test_tci_detection(expected_detection, tvb_constant, local_input, mesh,\n                       element, element_size, make_two_neighbors(left, right),\n                       make_two_neighbors(left_size, right_size));\n  };\n\n  const double eps = 100.0 * std::numeric_limits<double>::epsilon();\n\n  const auto input = [&mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    return DataVector{2.0 + 1.2 * get<0>(coords)};\n  }();\n\n  // Establish baseline using evenly-sized elements\n  test_tci(false, input, 0.8 - eps, 3.2 + eps, dx, dx);\n\n  const double larger = 2.0 * dx;\n  const double smaller = 0.5 * dx;\n\n  // Larger neighbor with same mean => true reduction in slope => trigger\n  test_tci(true, input, 0.8 - eps, 3.2, dx, larger);\n  // Larger neighbor with larger mean => same slope => no trigger\n  test_tci(false, input, 0.8 - eps, 3.8 + eps, dx, larger);\n\n  // Smaller neighbor with same mean => increased slope => no trigger\n  test_tci(false, input, 0.8 - eps, 3.2 + eps, dx, smaller);\n  // Smaller neighbor with lower mean => same slope => no trigger\n  test_tci(false, input, 0.8 - eps, 2.9 + eps, dx, smaller);\n\n  test_tci(true, input, 0.8, 3.2 + eps, larger, dx);\n  test_tci(false, input, 0.2 - eps, 3.2 + eps, larger, dx);\n\n  test_tci(false, input, 0.8 - eps, 3.2 + eps, smaller, dx);\n  test_tci(false, input, 1.1 - eps, 3.2 + eps, smaller, dx);\n}\n\n// In 1D, test combinations of TVB constant, polynomial order, etc.\n// Check that each combination has the expected TCI behavior.\nvoid test_tvb_minmod_tci_1d() {\n  INFO(\"Testing MinmodTci in 1D\");\n  for (const auto quadrature :\n       {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n    for (const auto num_grid_points : std::array<size_t, 2>{{2, 4}}) {\n      test_tci_on_linear_function(num_grid_points, quadrature);\n      test_tci_at_boundary(num_grid_points, quadrature);\n      test_tci_with_different_size_neighbor(num_grid_points, quadrature);\n    }\n    // This test only makes sense with more than 2 grid points\n    test_tci_on_quadratic_function(3, quadrature);\n    test_tci_on_quadratic_function(4, quadrature);\n  }\n}\n\nvoid test_tvb_minmod_tci_2d_impl(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const double tvb_constant = 0.0;\n  const auto element = TestHelpers::Limiters::make_element<2>();\n  const Mesh<2> mesh(3, Spectral::Basis::Legendre, quadrature);\n  const auto element_size = make_array<2>(2.0);\n\n  const auto test_tci =\n      [&tvb_constant, &element, &mesh, &element_size](\n          const bool expected_detection, const DataVector& local_input,\n          const std::array<double, 4>& neighbor_means) {\n        test_tci_detection(expected_detection, tvb_constant, local_input, mesh,\n                           element, element_size,\n                           make_four_neighbors(neighbor_means),\n                           make_four_neighbors(make_array<4>(2.0)));\n      };\n\n  const auto input = [&mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    const auto& x = get<0>(coords);\n    const auto& y = get<1>(coords);\n    return DataVector{3.0 + x + 2.0 * y + 0.1 * x * y};\n  }();\n\n  // Case with no activation\n  test_tci(false, input, {{1.9, 4.2, -0.5, 5.6}});\n\n  // Limit because of xi-direction neighbors\n  test_tci(true, input, {{2.2, 4.2, -0.5, 5.6}});\n  test_tci(true, input, {{1.9, 3.2, -0.5, 5.6}});\n\n  // Limit because of eta-direction neighbors\n  test_tci(true, input, {{1.9, 4.2, 1.5, 5.6}});\n  test_tci(true, input, {{1.9, 4.2, -0.5, 2.9}});\n\n  // Limit for xi and eta directions\n  test_tci(true, input, {{2.2, 4.2, 1.5, 5.6}});\n  test_tci(true, input, {{3.9, 4.2, -0.5, 2.9}});\n}\n\n// In 2D, test that the dimension-by-dimension application of the TCI works as\n// expected.\nvoid test_tvb_minmod_tci_2d() {\n  INFO(\"Testing MinmodTci in 2D\");\n  test_tvb_minmod_tci_2d_impl(Spectral::Quadrature::GaussLobatto);\n  test_tvb_minmod_tci_2d_impl(Spectral::Quadrature::Gauss);\n}\n\nvoid test_tvb_minmod_tci_3d_impl(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const double tvb_constant = 0.0;\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const Mesh<3> mesh(3, Spectral::Basis::Legendre, quadrature);\n  const auto element_size = make_array<3>(2.0);\n\n  const auto test_tci =\n      [&tvb_constant, &element, &mesh, &element_size](\n          const bool expected_detection, const DataVector& local_input,\n          const std::array<double, 6>& neighbor_means) {\n        test_tci_detection(expected_detection, tvb_constant, local_input, mesh,\n                           element, element_size,\n                           make_six_neighbors(neighbor_means),\n                           make_six_neighbors(make_array<6>(2.0)));\n      };\n\n  const auto input = [&mesh]() {\n    const auto coords = logical_coordinates(mesh);\n    const auto& x = get<0>(coords);\n    const auto& y = get<1>(coords);\n    const auto& z = get<2>(coords);\n    return DataVector{2.0 - 1.6 * x + 0.4 * y + 0.4 * z + 0.1 * x * y -\n                      0.1 * x * z - 0.2 * y * z};\n  }();\n\n  // Case with no activation\n  test_tci(false, input, {{3.8, -0.1, 1.5, 2.7, 1.2, 2.5}});\n\n  // Limit because of xi-direction neighbors\n  test_tci(true, input, {{3.4, -0.1, 1.5, 2.7, 1.2, 2.5}});\n  test_tci(true, input, {{3.8, 2.1, 1.5, 2.7, 1.2, 2.5}});\n\n  // Limit because of eta-direction neighbors\n  test_tci(true, input, {{3.8, -0.1, 1.9, 2.7, 1.2, 2.5}});\n  test_tci(true, input, {{3.8, -0.1, 1.5, 2.3, 1.2, 2.5}});\n\n  // Limit because of zeta-direction neighbors\n  test_tci(true, input, {{3.8, -0.1, 1.5, 2.7, 2.2, 2.5}});\n  test_tci(true, input, {{3.8, -0.1, 1.5, 2.7, 1.2, 2.1}});\n\n  // Limit for xi, eta, and zeta directions\n  test_tci(true, input, {{3.4, -0.1, 1.5, 2.3, 1.2, 2.1}});\n  test_tci(true, input, {{3.8, 2.1, 2.1, 2.7, 2.2, 2.5}});\n}\n\n// In 3D, test that the dimension-by-dimension application of the TCI works as\n// expected.\nvoid test_tvb_minmod_tci_3d() {\n  INFO(\"Testing MinmodTci in 3D\");\n  test_tvb_minmod_tci_3d_impl(Spectral::Quadrature::GaussLobatto);\n  test_tvb_minmod_tci_3d_impl(Spectral::Quadrature::Gauss);\n}\n\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"Scalar\"; }\n};\n\ntemplate <size_t VolumeDim>\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, VolumeDim>;\n  static std::string name() { return \"Vector\"; }\n};\n\nvoid test_tvb_minmod_tci_several_tensors() {\n  INFO(\"Testing MinmodTci action on several tensors\");\n  // Test that TCI returns true if just one component needs limiting, which\n  // we do by limiting a scalar and vector in 3D\n  const double tvb_constant = 0.0;\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const size_t number_of_grid_points = 2;\n  const Mesh<3> mesh(number_of_grid_points, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const auto element_size = make_array<3>(1.0);\n\n  const auto linear_data = [&mesh](const double mean, const double slope_x,\n                                   const double slope_y, const double slope_z) {\n    const auto coords = logical_coordinates(mesh);\n    const auto& x = get<0>(coords);\n    const auto& y = get<1>(coords);\n    const auto& z = get<2>(coords);\n    return DataVector{mean + slope_x * x + slope_y * y + slope_z * z};\n  };\n\n  Scalar<DataVector> local_scalar{linear_data(1.8, 1.4, 0.1, -0.2)};\n  tnsr::I<DataVector, 3> local_vector{\n      {{linear_data(-0.4, -0.3, 0.3, 1.0), linear_data(0.02, 0.01, 0.01, 0.2),\n        linear_data(2.3, 0.5, -0.3, -0.2)}}};\n\n  struct TestPackagedData {\n    tuples::TaggedTuple<::Tags::Mean<ScalarTag>, ::Tags::Mean<VectorTag<3>>>\n        means;\n    std::array<double, 3> element_size =\n        make_array<3>(std::numeric_limits<double>::signaling_NaN());\n  };\n  std::unordered_map<DirectionalId<3>, TestPackagedData,\n                     boost::hash<DirectionalId<3>>>\n      neighbor_data{};\n  TestPackagedData& lower_xi_neighbor = neighbor_data[DirectionalId<3>{\n      Direction<3>::lower_xi(), ElementId<3>(1)}];\n  TestPackagedData& upper_xi_neighbor = neighbor_data[DirectionalId<3>{\n      Direction<3>::upper_xi(), ElementId<3>(2)}];\n  TestPackagedData& lower_eta_neighbor = neighbor_data[DirectionalId<3>{\n      Direction<3>::lower_eta(), ElementId<3>(3)}];\n  TestPackagedData& upper_eta_neighbor = neighbor_data[DirectionalId<3>{\n      Direction<3>::upper_eta(), ElementId<3>(4)}];\n  TestPackagedData& lower_zeta_neighbor = neighbor_data[DirectionalId<3>{\n      Direction<3>::lower_zeta(), ElementId<3>(5)}];\n  TestPackagedData& upper_zeta_neighbor = neighbor_data[DirectionalId<3>{\n      Direction<3>::upper_zeta(), ElementId<3>(6)}];\n  lower_xi_neighbor.element_size = element_size;\n  upper_xi_neighbor.element_size = element_size;\n  lower_eta_neighbor.element_size = element_size;\n  upper_eta_neighbor.element_size = element_size;\n  lower_zeta_neighbor.element_size = element_size;\n  upper_zeta_neighbor.element_size = element_size;\n\n  // Case where neither tensor triggers limiting\n  get(get<::Tags::Mean<ScalarTag>>(lower_xi_neighbor.means)) = 0.3;\n  get(get<::Tags::Mean<ScalarTag>>(upper_xi_neighbor.means)) = 3.3;\n  get(get<::Tags::Mean<ScalarTag>>(lower_eta_neighbor.means)) = 1.6;\n  get(get<::Tags::Mean<ScalarTag>>(upper_eta_neighbor.means)) = 2.2;\n  get(get<::Tags::Mean<ScalarTag>>(lower_zeta_neighbor.means)) = 2.1;\n  get(get<::Tags::Mean<ScalarTag>>(upper_zeta_neighbor.means)) = 1.4;\n  get<0>(get<::Tags::Mean<VectorTag<3>>>(lower_xi_neighbor.means)) = 0.1;\n  get<0>(get<::Tags::Mean<VectorTag<3>>>(upper_xi_neighbor.means)) = -0.9;\n  get<0>(get<::Tags::Mean<VectorTag<3>>>(lower_eta_neighbor.means)) = -1.1;\n  get<0>(get<::Tags::Mean<VectorTag<3>>>(upper_eta_neighbor.means)) = 0.2;\n  get<0>(get<::Tags::Mean<VectorTag<3>>>(lower_zeta_neighbor.means)) = -1.8;\n  get<0>(get<::Tags::Mean<VectorTag<3>>>(upper_zeta_neighbor.means)) = 0.7;\n  get<1>(get<::Tags::Mean<VectorTag<3>>>(lower_xi_neighbor.means)) = 0.0;\n  get<1>(get<::Tags::Mean<VectorTag<3>>>(upper_xi_neighbor.means)) = 0.05;\n  get<1>(get<::Tags::Mean<VectorTag<3>>>(lower_eta_neighbor.means)) = -0.1;\n  get<1>(get<::Tags::Mean<VectorTag<3>>>(upper_eta_neighbor.means)) = 0.1;\n  get<1>(get<::Tags::Mean<VectorTag<3>>>(lower_zeta_neighbor.means)) = -0.7;\n  get<1>(get<::Tags::Mean<VectorTag<3>>>(upper_zeta_neighbor.means)) = 0.3;\n  get<2>(get<::Tags::Mean<VectorTag<3>>>(lower_xi_neighbor.means)) = 1.4;\n  get<2>(get<::Tags::Mean<VectorTag<3>>>(upper_xi_neighbor.means)) = 2.9;\n  get<2>(get<::Tags::Mean<VectorTag<3>>>(lower_eta_neighbor.means)) = 2.7;\n  get<2>(get<::Tags::Mean<VectorTag<3>>>(upper_eta_neighbor.means)) = 1.8;\n  get<2>(get<::Tags::Mean<VectorTag<3>>>(lower_zeta_neighbor.means)) = 3.1;\n  get<2>(get<::Tags::Mean<VectorTag<3>>>(upper_zeta_neighbor.means)) = 0.5;\n  const bool trigger_base_case =\n      Limiters::Tci::tvb_minmod_indicator<3, TestPackagedData, ScalarTag,\n                                          VectorTag<3>>(\n          tvb_constant, local_scalar, local_vector, mesh, element, element_size,\n          neighbor_data);\n  CHECK_FALSE(trigger_base_case);\n\n  // Case where the scalar triggers limiting\n  get(get<::Tags::Mean<ScalarTag>>(upper_xi_neighbor.means)) = 2.0;\n  const bool trigger_scalar =\n      Limiters::Tci::tvb_minmod_indicator<3, TestPackagedData, ScalarTag,\n                                          VectorTag<3>>(\n          tvb_constant, local_scalar, local_vector, mesh, element, element_size,\n          neighbor_data);\n  CHECK(trigger_scalar);\n\n  // Case where the vector x-component triggers limiting\n  get(get<::Tags::Mean<ScalarTag>>(upper_xi_neighbor.means)) = 3.3;\n  get<0>(get<::Tags::Mean<VectorTag<3>>>(lower_zeta_neighbor.means)) = -0.1;\n  const bool trigger_vector_x =\n      Limiters::Tci::tvb_minmod_indicator<3, TestPackagedData, ScalarTag,\n                                          VectorTag<3>>(\n          tvb_constant, local_scalar, local_vector, mesh, element, element_size,\n          neighbor_data);\n  CHECK(trigger_vector_x);\n\n  // Case where the vector y-component triggers limiting\n  get<0>(get<::Tags::Mean<VectorTag<3>>>(lower_zeta_neighbor.means)) = -1.8;\n  get<1>(get<::Tags::Mean<VectorTag<3>>>(upper_eta_neighbor.means)) = -0.2;\n  const bool trigger_vector_y =\n      Limiters::Tci::tvb_minmod_indicator<3, TestPackagedData, ScalarTag,\n                                          VectorTag<3>>(\n          tvb_constant, local_scalar, local_vector, mesh, element, element_size,\n          neighbor_data);\n  CHECK(trigger_vector_y);\n\n  // Case where the vector z-component triggers limiting\n  get<1>(get<::Tags::Mean<VectorTag<3>>>(upper_eta_neighbor.means)) = 0.1;\n  get<2>(get<::Tags::Mean<VectorTag<3>>>(lower_xi_neighbor.means)) = 1.9;\n  const bool trigger_vector_z =\n      Limiters::Tci::tvb_minmod_indicator<3, TestPackagedData, ScalarTag,\n                                          VectorTag<3>>(\n          tvb_constant, local_scalar, local_vector, mesh, element, element_size,\n          neighbor_data);\n  CHECK(trigger_vector_z);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.MinmodTci\", \"[Limiters][Unit]\") {\n  test_tvb_minmod_tci_1d();\n  test_tvb_minmod_tci_2d();\n  test_tvb_minmod_tci_3d();\n\n  test_tvb_minmod_tci_several_tensors();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_MinmodType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.MinmodType\", \"[Limiters][Unit]\") {\n  CHECK(Limiters::MinmodType::LambdaPi1 ==\n        TestHelpers::test_creation<Limiters::MinmodType>(\"LambdaPi1\"));\n  CHECK(Limiters::MinmodType::LambdaPiN ==\n        TestHelpers::test_creation<Limiters::MinmodType>(\"LambdaPiN\"));\n  CHECK(Limiters::MinmodType::Muscl ==\n        TestHelpers::test_creation<Limiters::MinmodType>(\"Muscl\"));\n\n  CHECK(get_output(Limiters::MinmodType::LambdaPi1) == \"LambdaPi1\");\n  CHECK(get_output(Limiters::MinmodType::LambdaPiN) == \"LambdaPiN\");\n  CHECK(get_output(Limiters::MinmodType::Muscl) == \"Muscl\");\n\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<Limiters::MinmodType>(\"BadType\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"BadType\\\" to MinmodType\"));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_SimpleWenoImpl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <functional>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/SimpleWenoImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t VolumeDim>\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, VolumeDim>;\n};\n\ntemplate <size_t VolumeDim, typename Tag>\nstruct DummyPackagedData {\n  Variables<tmpl::list<Tag>> volume_data;\n  tuples::TaggedTuple<::Tags::Mean<Tag>> means;\n  Mesh<VolumeDim> mesh;\n};\n\ntemplate <size_t VolumeDim>\nusing VariablesMap =\n    std::unordered_map<DirectionalId<VolumeDim>,\n                       Variables<tmpl::list<VectorTag<VolumeDim>>>,\n                       boost::hash<DirectionalId<VolumeDim>>>;\n\ntemplate <size_t VolumeDim>\nstd::unordered_map<DirectionalId<VolumeDim>,\n                   DummyPackagedData<VolumeDim, VectorTag<VolumeDim>>,\n                   boost::hash<DirectionalId<VolumeDim>>>\nmake_neighbor_data_from_neighbor_vars(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const VariablesMap<VolumeDim>& neighbor_vars) {\n  const auto make_tuple_of_means =\n      [&mesh](\n          const Variables<tmpl::list<VectorTag<VolumeDim>>>& vars_to_average) {\n        tuples::TaggedTuple<::Tags::Mean<VectorTag<VolumeDim>>> result;\n        for (size_t d = 0; d < VolumeDim; ++d) {\n          get<::Tags::Mean<VectorTag<VolumeDim>>>(result).get(d) = mean_value(\n              get<VectorTag<VolumeDim>>(vars_to_average).get(d), mesh);\n        }\n        return result;\n      };\n\n  std::unordered_map<DirectionalId<VolumeDim>,\n                     DummyPackagedData<VolumeDim, VectorTag<VolumeDim>>,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_data{};\n\n  for (const auto& neighbor : element.neighbors()) {\n    const auto dir = neighbor.first;\n    const auto id = *(neighbor.second.cbegin());\n    const auto dir_and_id = DirectionalId<VolumeDim>{dir, id};\n    neighbor_data[dir_and_id].volume_data = neighbor_vars.at(dir_and_id);\n    neighbor_data[dir_and_id].means =\n        make_tuple_of_means(neighbor_vars.at(dir_and_id));\n    neighbor_data[dir_and_id].mesh = mesh;\n  }\n\n  return neighbor_data;\n}\n\ntemplate <size_t VolumeDim>\nvoid test_simple_weno_work(\n    const tnsr::I<DataVector, VolumeDim>& local_data,\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::unordered_map<DirectionalId<VolumeDim>,\n                             DummyPackagedData<VolumeDim, VectorTag<VolumeDim>>,\n                             boost::hash<DirectionalId<VolumeDim>>>&\n        neighbor_data,\n    const VariablesMap<VolumeDim>& expected_neighbor_modified_vars,\n    Approx local_approx = approx) {\n  // First run some sanity checks on the input, and make sure the test function\n  // is being called in a reasonable way\n  if (element.neighbors().size() != neighbor_data.size()) {\n    ERROR(\"Different number of neighbors from element, neighbor_data\");\n  }\n  if (neighbor_data.size() != expected_neighbor_modified_vars.size()) {\n    ERROR(\"Different sizes for neighbor_data, expected_neighbor_modified_vars\");\n  }\n  for (const auto& neighbor : element.neighbors()) {\n    if (neighbor.second.ids().size() > 1) {\n      ERROR(\"Too many neighbors: h-refinement is not yet supported\");\n    }\n    const auto dir = neighbor.first;\n    const auto id = *(neighbor.second.cbegin());\n    const auto dir_and_id = DirectionalId<VolumeDim>{dir, id};\n    if (neighbor_data.find(dir_and_id) == neighbor_data.end()) {\n      ERROR(\"Missing neighbor_data at an internal boundary\");\n    }\n    if (expected_neighbor_modified_vars.find(dir_and_id) ==\n        expected_neighbor_modified_vars.end()) {\n      ERROR(\"Missing expected_neighbor_modified_vars at an internal boundary\");\n    }\n  }\n\n  // Buffers for simple WENO implementation\n  std::unordered_map<DirectionalId<VolumeDim>, intrp::RegularGrid<VolumeDim>,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      interpolator_buffer{};\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      modified_neighbor_solution_buffer{};\n  for (const auto& neighbor_and_data : neighbor_data) {\n    const auto& neighbor = neighbor_and_data.first;\n    modified_neighbor_solution_buffer.insert(\n        std::make_pair(neighbor, DataVector(mesh.number_of_grid_points())));\n  }\n\n  // WENO should preserve the mean, so expected means = initial means\n  const auto expected_vector_means = [&local_data, &mesh]() {\n    std::array<double, VolumeDim> means{};\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      gsl::at(means, d) = mean_value(local_data.get(d), mesh);\n    }\n    return means;\n  }();\n\n  auto vector_to_limit = local_data;\n  const double neighbor_linear_weight = 0.001;\n  // The \"tensor\" interface is a thin wrapper around the \"single component\"\n  // interface, so no need to test both overloads separately.\n  Limiters::Weno_detail::simple_weno_impl<VectorTag<VolumeDim>>(\n      make_not_null(&interpolator_buffer),\n      make_not_null(&modified_neighbor_solution_buffer),\n      make_not_null(&vector_to_limit), neighbor_linear_weight, mesh, element,\n      neighbor_data);\n\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    CHECK(mean_value(vector_to_limit.get(d), mesh) ==\n          approx(gsl::at(expected_vector_means, d)));\n  }\n\n  auto expected_vector = local_data;\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      expected_neighbor_polynomials;\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (auto& neighbor_and_vars : expected_neighbor_modified_vars) {\n      expected_neighbor_polynomials[neighbor_and_vars.first] =\n          get<VectorTag<VolumeDim>>(neighbor_and_vars.second).get(i);\n    }\n    Limiters::Weno_detail::reconstruct_from_weighted_sum(\n        make_not_null(&(expected_vector.get(i))), neighbor_linear_weight,\n        Limiters::Weno_detail::DerivativeWeight::PowTwoEll, mesh,\n        expected_neighbor_polynomials);\n  }\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_vector, vector_to_limit, local_approx);\n}\n\nvoid test_simple_weno_1d_impl(const Spectral::Quadrature quadrature,\n                              const std::unordered_set<Direction<1>>&\n                                  directions_of_external_boundaries = {}) {\n  CAPTURE(quadrature);\n  CAPTURE(directions_of_external_boundaries);\n  const auto mesh = Mesh<1>(3, Spectral::Basis::Legendre, quadrature);\n  const auto element =\n      TestHelpers::Limiters::make_element<1>(directions_of_external_boundaries);\n  const auto logical_coords = logical_coordinates(mesh);\n\n  // Functions to produce dummy data on each element\n  const auto make_center_tensor =\n      [](const tnsr::I<DataVector, 1, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        return tnsr::I<DataVector, 1>{{{0.4 * x - 0.1 * square(x)}}};\n      };\n  const auto make_lower_xi_vars =\n      [](const tnsr::I<DataVector, 1, Frame::ElementLogical>& coords,\n         const double offset = 0.0) {\n        const auto x = get<0>(coords) + offset;\n        Variables<tmpl::list<VectorTag<1>>> vars(x.size());\n        get<0>(get<VectorTag<1>>(vars)) = -0.1 + 0.3 * x - 0.1 * square(x);\n        return vars;\n      };\n  const auto make_upper_xi_vars =\n      [](const tnsr::I<DataVector, 1, Frame::ElementLogical>& coords,\n         const double offset = 0.0) {\n        const auto x = get<0>(coords) + offset;\n        Variables<tmpl::list<VectorTag<1>>> vars(x.size());\n        get<0>(get<VectorTag<1>>(vars)) = 0.6 * x - 0.3 * square(x);\n        return vars;\n      };\n\n  const auto local_data = make_center_tensor(logical_coords);\n  VariablesMap<1> neighbor_vars{};\n  VariablesMap<1> neighbor_modified_vars{};\n\n  const auto shift_vars_to_local_means =\n      [&mesh, &local_data](const Variables<tmpl::list<VectorTag<1>>>& input) {\n        auto result = input;\n        auto& v = get<VectorTag<1>>(result);\n        get<0>(v) +=\n            mean_value(get<0>(local_data), mesh) - mean_value(get<0>(v), mesh);\n        return result;\n      };\n\n  const auto make_neighbor_vars =\n      [&logical_coords, &directions_of_external_boundaries, &neighbor_vars,\n       &neighbor_modified_vars, &shift_vars_to_local_means](\n          const DirectionalId<1>& neighbor, const auto make_vars) {\n        if (not directions_of_external_boundaries.contains(\n                neighbor.direction())) {\n          const double offset =\n              (neighbor.direction().side() == Side::Lower ? -2.0 : 2.0);\n          neighbor_vars[neighbor] = make_vars(logical_coords, offset);\n          neighbor_modified_vars[neighbor] =\n              shift_vars_to_local_means(make_vars(logical_coords));\n        }\n      };\n\n  make_neighbor_vars(\n      DirectionalId<1>{Direction<1>::lower_xi(), ElementId<1>(1)},\n      make_lower_xi_vars);\n\n  make_neighbor_vars(\n      DirectionalId<1>{Direction<1>::upper_xi(), ElementId<1>(2)},\n      make_upper_xi_vars);\n\n  const auto neighbor_data =\n      make_neighbor_data_from_neighbor_vars(mesh, element, neighbor_vars);\n\n  test_simple_weno_work<1>(local_data, mesh, element, neighbor_data,\n                           neighbor_modified_vars);\n}\n\nvoid test_simple_weno_1d() {\n  INFO(\"Testing simple_weno_impl in 1D\");\n  const auto gl = Spectral::Quadrature::GaussLobatto;\n  const auto gauss = Spectral::Quadrature::Gauss;\n\n  test_simple_weno_1d_impl(gl);\n  test_simple_weno_1d_impl(gauss);\n\n  // Test with particular boundaries labeled as external. This is independent\n  // of the quadrature, since we only verify that we don't try to use a\n  // non-existent neighbor.\n  test_simple_weno_1d_impl(gl, {{Direction<1>::lower_xi()}});\n}\n\nvoid test_simple_weno_2d_impl(const Spectral::Quadrature quadrature,\n                              const std::unordered_set<Direction<2>>&\n                                  directions_of_external_boundaries = {}) {\n  CAPTURE(quadrature);\n  CAPTURE(directions_of_external_boundaries);\n  const auto mesh =\n      Mesh<2>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto element =\n      TestHelpers::Limiters::make_element<2>(directions_of_external_boundaries);\n  const auto logical_coords = logical_coordinates(mesh);\n\n  const auto make_center_tensor =\n      [](const tnsr::I<DataVector, 2, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        return tnsr::I<DataVector, 2>{\n            {{x + 2.5 * y,\n              0.1 + 0.2 * x - 0.4 * y + 0.3 * square(x) * square(y)}}};\n      };\n  const auto make_lower_xi_vars =\n      [](const tnsr::I<DataVector, 2, Frame::ElementLogical>& coords,\n         const double xi_offset = 0.0) {\n        const auto x = get<0>(coords) + xi_offset;\n        const auto& y = get<1>(coords);\n        Variables<tmpl::list<VectorTag<2>>> vars(x.size());\n        get<0>(get<VectorTag<2>>(vars)) = x + 2.5 * y;\n        get<1>(get<VectorTag<2>>(vars)) = 3.0 + 0.2 * y;\n        return vars;\n      };\n  const auto make_upper_xi_vars =\n      [](const tnsr::I<DataVector, 2, Frame::ElementLogical>& coords,\n         const double xi_offset = 0.0) {\n        const auto x = get<0>(coords) + xi_offset;\n        const auto& y = get<1>(coords);\n        Variables<tmpl::list<VectorTag<2>>> vars(x.size());\n        get<0>(get<VectorTag<2>>(vars)) = x + 2.5 * y;\n        get<1>(get<VectorTag<2>>(vars)) = -2.4 + square(x);\n        return vars;\n      };\n  const auto make_lower_eta_vars =\n      [](const tnsr::I<DataVector, 2, Frame::ElementLogical>& coords,\n         const double eta_offset = 0.0) {\n        const auto& x = get<0>(coords);\n        const auto y = get<1>(coords) + eta_offset;\n        Variables<tmpl::list<VectorTag<2>>> vars(x.size());\n        get<0>(get<VectorTag<2>>(vars)) = x + 2.5 * y;\n        get<1>(get<VectorTag<2>>(vars)) = 0.2 - y;\n        return vars;\n      };\n  const auto make_upper_eta_vars =\n      [](const tnsr::I<DataVector, 2, Frame::ElementLogical>& coords,\n         const double eta_offset = 0.0) {\n        const auto& x = get<0>(coords);\n        const auto y = get<1>(coords) + eta_offset;\n        Variables<tmpl::list<VectorTag<2>>> vars(x.size());\n        get<0>(get<VectorTag<2>>(vars)) = x + 2.5 * y;\n        get<1>(get<VectorTag<2>>(vars)) = 0.4 + 0.3 * x * square(y);\n        return vars;\n      };\n\n  const auto local_data = make_center_tensor(logical_coords);\n  VariablesMap<2> neighbor_vars{};\n  VariablesMap<2> neighbor_modified_vars{};\n\n  const auto shift_vars_to_local_means =\n      [&mesh, &local_data](const Variables<tmpl::list<VectorTag<2>>>& input) {\n        auto result = input;\n        auto& v = get<VectorTag<2>>(result);\n        get<0>(v) +=\n            mean_value(get<0>(local_data), mesh) - mean_value(get<0>(v), mesh);\n        get<1>(v) +=\n            mean_value(get<1>(local_data), mesh) - mean_value(get<1>(v), mesh);\n        return result;\n      };\n\n  const auto make_neighbor_vars =\n      [&logical_coords, &directions_of_external_boundaries, &neighbor_vars,\n       &neighbor_modified_vars, &shift_vars_to_local_means](\n          const DirectionalId<2>& neighbor, const auto make_vars) {\n        if (not directions_of_external_boundaries.contains(\n                neighbor.direction())) {\n          const double offset =\n              (neighbor.direction().side() == Side::Lower ? -2.0 : 2.0);\n          neighbor_vars[neighbor] = make_vars(logical_coords, offset);\n          neighbor_modified_vars[neighbor] =\n              shift_vars_to_local_means(make_vars(logical_coords));\n        }\n      };\n\n  make_neighbor_vars(\n      DirectionalId<2>{Direction<2>::lower_xi(), ElementId<2>(1)},\n      make_lower_xi_vars);\n\n  make_neighbor_vars(\n      DirectionalId<2>{Direction<2>::upper_xi(), ElementId<2>(2)},\n      make_upper_xi_vars);\n\n  make_neighbor_vars(\n      DirectionalId<2>{Direction<2>::lower_eta(), ElementId<2>(3)},\n      make_lower_eta_vars);\n\n  make_neighbor_vars(\n      DirectionalId<2>{Direction<2>::upper_eta(), ElementId<2>(4)},\n      make_upper_eta_vars);\n\n  const auto neighbor_data =\n      make_neighbor_data_from_neighbor_vars(mesh, element, neighbor_vars);\n\n  test_simple_weno_work<2>(local_data, mesh, element, neighbor_data,\n                           neighbor_modified_vars);\n}\n\nvoid test_simple_weno_2d() {\n  INFO(\"Testing simple_weno_impl in 2D\");\n  const auto gl = Spectral::Quadrature::GaussLobatto;\n  const auto gauss = Spectral::Quadrature::Gauss;\n\n  test_simple_weno_2d_impl(gl);\n  test_simple_weno_2d_impl(gauss);\n\n  // Test with particular boundaries labeled as external. This is independent\n  // of the quadrature, since we only verify that we don't try to use a\n  // non-existent neighbor.\n  test_simple_weno_2d_impl(gl, {{Direction<2>::lower_eta()}});\n  test_simple_weno_2d_impl(\n      gl, {{Direction<2>::lower_xi(), Direction<2>::lower_eta(),\n            Direction<2>::upper_eta()}});\n}\n\nvoid test_simple_weno_3d_impl(const Spectral::Quadrature quadrature,\n                              const std::unordered_set<Direction<3>>&\n                                  directions_of_external_boundaries = {}) {\n  CAPTURE(quadrature);\n  CAPTURE(directions_of_external_boundaries);\n  const auto mesh = Mesh<3>({{3, 4, 5}}, Spectral::Basis::Legendre, quadrature);\n  const auto element =\n      TestHelpers::Limiters::make_element<3>(directions_of_external_boundaries);\n  const auto logical_coords = logical_coordinates(mesh);\n\n  const auto make_center_tensor =\n      [](const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        const auto& z = get<2>(coords);\n        return tnsr::I<DataVector, 3>{\n            {{0.4 * x * y * z + square(z), z, x + square(y) + cube(z)}}};\n      };\n  const auto make_lower_xi_vars =\n      [](const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords,\n         const double xi_offset = 0.0) {\n        const auto x = get<0>(coords) + xi_offset;\n        const auto& y = get<1>(coords);\n        const auto& z = get<2>(coords);\n        Variables<tmpl::list<VectorTag<3>>> vars(x.size());\n        get<0>(get<VectorTag<3>>(vars)) = 0.4 * x * y * z + square(z);\n        get<1>(get<VectorTag<3>>(vars)) = 0.8 * z + 0.3 * x * y;\n        get<2>(get<VectorTag<3>>(vars)) = x + y;\n        return vars;\n      };\n  const auto make_upper_xi_vars =\n      [](const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords,\n         const double xi_offset = 0.0) {\n        const auto x = get<0>(coords) + xi_offset;\n        const auto& y = get<1>(coords);\n        const auto& z = get<2>(coords);\n        Variables<tmpl::list<VectorTag<3>>> vars(x.size());\n        get<0>(get<VectorTag<3>>(vars)) = 0.4 * x * y * z + square(z);\n        get<1>(get<VectorTag<3>>(vars)) = z + 0.1 * square(x);\n        get<2>(get<VectorTag<3>>(vars)) = y + square(x) * z;\n        return vars;\n      };\n  const auto make_lower_eta_vars =\n      [](const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords,\n         const double eta_offset = 0.0) {\n        const auto& x = get<0>(coords);\n        const auto y = get<1>(coords) + eta_offset;\n        const auto& z = get<2>(coords);\n        Variables<tmpl::list<VectorTag<3>>> vars(x.size());\n        get<0>(get<VectorTag<3>>(vars)) = 0.4 * x * y * z + square(z);\n        get<1>(get<VectorTag<3>>(vars)) = -0.1 * y + z;\n        get<2>(get<VectorTag<3>>(vars)) = -square(z);\n        return vars;\n      };\n  const auto make_upper_eta_vars =\n      [](const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords,\n         const double eta_offset = 0.0) {\n        const auto& x = get<0>(coords);\n        const auto y = get<1>(coords) + eta_offset;\n        const auto& z = get<2>(coords);\n        Variables<tmpl::list<VectorTag<3>>> vars(x.size());\n        get<0>(get<VectorTag<3>>(vars)) = 0.4 * x * y * z + square(z);\n        get<1>(get<VectorTag<3>>(vars)) = z + 0.4 * x * cube(z);\n        get<2>(get<VectorTag<3>>(vars)) = y * z + square(y) + cube(z);\n        return vars;\n      };\n  const auto make_lower_zeta_vars =\n      [](const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords,\n         const double zeta_offset = 0.0) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        const auto z = get<2>(coords) + zeta_offset;\n        Variables<tmpl::list<VectorTag<3>>> vars(x.size());\n        get<0>(get<VectorTag<3>>(vars)) = 0.4 * x * y * z + square(z);\n        get<1>(get<VectorTag<3>>(vars)) = 0.9 * z - 2. * x * z;\n        get<2>(get<VectorTag<3>>(vars)) = y + cube(z);\n        return vars;\n      };\n  const auto make_upper_zeta_vars =\n      [](const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords,\n         const double zeta_offset = 0.0) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        const auto z = get<2>(coords) + zeta_offset;\n        Variables<tmpl::list<VectorTag<3>>> vars(x.size());\n        get<0>(get<VectorTag<3>>(vars)) = 0.4 * x * y * z + square(z);\n        get<1>(get<VectorTag<3>>(vars)) = 1.3 * square(y) * square(z);\n        get<2>(get<VectorTag<3>>(vars)) = -x * y * z + square(y);\n        return vars;\n      };\n\n  const auto local_data = make_center_tensor(logical_coords);\n  VariablesMap<3> neighbor_vars{};\n  VariablesMap<3> neighbor_modified_vars{};\n\n  const auto shift_vars_to_local_means =\n      [&mesh, &local_data](const Variables<tmpl::list<VectorTag<3>>>& input) {\n        auto result = input;\n        auto& v = get<VectorTag<3>>(result);\n        get<0>(v) +=\n            mean_value(get<0>(local_data), mesh) - mean_value(get<0>(v), mesh);\n        get<1>(v) +=\n            mean_value(get<1>(local_data), mesh) - mean_value(get<1>(v), mesh);\n        get<2>(v) +=\n            mean_value(get<2>(local_data), mesh) - mean_value(get<2>(v), mesh);\n        return result;\n      };\n\n  const auto make_neighbor_vars =\n      [&logical_coords, &directions_of_external_boundaries, &neighbor_vars,\n       &neighbor_modified_vars, &shift_vars_to_local_means](\n          const DirectionalId<3>& neighbor, const auto make_vars) {\n        if (not directions_of_external_boundaries.contains(\n                neighbor.direction())) {\n          const double offset =\n              (neighbor.direction().side() == Side::Lower ? -2.0 : 2.0);\n          neighbor_vars[neighbor] = make_vars(logical_coords, offset);\n          neighbor_modified_vars[neighbor] =\n              shift_vars_to_local_means(make_vars(logical_coords));\n        }\n      };\n\n  make_neighbor_vars(\n      DirectionalId<3>{Direction<3>::lower_xi(), ElementId<3>(1)},\n      make_lower_xi_vars);\n\n  make_neighbor_vars(\n      DirectionalId<3>{Direction<3>::upper_xi(), ElementId<3>(2)},\n      make_upper_xi_vars);\n\n  make_neighbor_vars(\n      DirectionalId<3>{Direction<3>::lower_eta(), ElementId<3>(3)},\n      make_lower_eta_vars);\n\n  make_neighbor_vars(\n      DirectionalId<3>{Direction<3>::upper_eta(), ElementId<3>(4)},\n      make_upper_eta_vars);\n\n  make_neighbor_vars(\n      DirectionalId<3>{Direction<3>::lower_zeta(), ElementId<3>(5)},\n      make_lower_zeta_vars);\n\n  make_neighbor_vars(\n      DirectionalId<3>{Direction<3>::upper_zeta(), ElementId<3>(6)},\n      make_upper_zeta_vars);\n\n  const auto neighbor_data =\n      make_neighbor_data_from_neighbor_vars(mesh, element, neighbor_vars);\n\n  // The 3D Simple WENO solution has slightly larger numerical error, presumably\n  // arising from the 3D extrapolation\n  Approx custom_approx = Approx::custom().epsilon(1.e-11).scale(1.0);\n  test_simple_weno_work<3>(local_data, mesh, element, neighbor_data,\n                           neighbor_modified_vars, custom_approx);\n}\n\nvoid test_simple_weno_3d() {\n  INFO(\"Testing simple_weno_impl in 3D\");\n  const auto gl = Spectral::Quadrature::GaussLobatto;\n  const auto gauss = Spectral::Quadrature::Gauss;\n\n  test_simple_weno_3d_impl(gl);\n  test_simple_weno_3d_impl(gauss);\n\n  // Test with particular boundaries labeled as external. This is independent\n  // of the quadrature, since we only verify that we don't try to use a\n  // non-existent neighbor.\n  test_simple_weno_3d_impl(gl, {{Direction<3>::lower_zeta()}});\n  test_simple_weno_3d_impl(\n      gl, {{Direction<3>::lower_xi(), Direction<3>::upper_xi(),\n            Direction<3>::lower_eta(), Direction<3>::lower_zeta(),\n            Direction<3>::lower_zeta()}});\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.SimpleWenoImpl\",\n                  \"[Limiters][Unit]\") {\n  test_simple_weno_1d();\n  test_simple_weno_2d();\n  test_simple_weno_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct SomeType {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.Tags\", \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<Tags::Limiter<SomeType>>(\"Limiter\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_Weno.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/HwenoImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/SimpleWenoImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Weno.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"Scalar\"; }\n};\n\ntemplate <size_t VolumeDim>\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, VolumeDim>;\n  static std::string name() { return \"Vector\"; }\n};\n\nvoid test_weno_option_parsing() {\n  INFO(\"Testing option parsing\");\n\n  const auto hweno_1d =\n      TestHelpers::test_creation<Limiters::Weno<1, tmpl::list<ScalarTag>>>(\n          \"Type: Hweno\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: False\");\n  const auto hweno_1d_larger_weight =\n      TestHelpers::test_creation<Limiters::Weno<1, tmpl::list<ScalarTag>>>(\n          \"Type: Hweno\\n\"\n          \"NeighborWeight: 0.01\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: False\");\n  const auto hweno_1d_tvb =\n      TestHelpers::test_creation<Limiters::Weno<1, tmpl::list<ScalarTag>>>(\n          \"Type: Hweno\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 1.0\\n\"\n          \"DisableForDebugging: False\");\n  const auto hweno_1d_disabled =\n      TestHelpers::test_creation<Limiters::Weno<1, tmpl::list<ScalarTag>>>(\n          \"Type: Hweno\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: True\");\n  const auto simple_weno_1d =\n      TestHelpers::test_creation<Limiters::Weno<1, tmpl::list<ScalarTag>>>(\n          \"Type: SimpleWeno\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: False\");\n\n  // Check operators == and !=\n  CHECK(hweno_1d == hweno_1d);\n  CHECK(hweno_1d != hweno_1d_larger_weight);\n  CHECK(hweno_1d != hweno_1d_tvb);\n  CHECK(hweno_1d != hweno_1d_disabled);\n  CHECK(hweno_1d != simple_weno_1d);\n\n  const auto hweno_2d =\n      TestHelpers::test_creation<Limiters::Weno<2, tmpl::list<ScalarTag>>>(\n          \"Type: Hweno\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"DisableForDebugging: False\");\n  const auto hweno_3d_larger_weight = TestHelpers::test_creation<\n      Limiters::Weno<3, tmpl::list<ScalarTag, VectorTag<3>>>>(\n      \"Type: Hweno\\n\"\n      \"NeighborWeight: 0.01\\n\"\n      \"TvbConstant: 0.0\\n\"\n      \"DisableForDebugging: True\");\n\n  // Check that creation from options gives correct object\n  const Limiters::Weno<1, tmpl::list<ScalarTag>> expected_hweno_1d(\n      Limiters::WenoType::Hweno, 0.001, 0.0);\n  const Limiters::Weno<1, tmpl::list<ScalarTag>>\n      expected_hweno_1d_larger_weight(Limiters::WenoType::Hweno, 0.01, 0.0);\n  const Limiters::Weno<1, tmpl::list<ScalarTag>> expected_hweno_1d_tvb(\n      Limiters::WenoType::Hweno, 0.001, 1.0);\n  const Limiters::Weno<1, tmpl::list<ScalarTag>> expected_hweno_1d_disabled(\n      Limiters::WenoType::Hweno, 0.001, 0.0, true);\n  const Limiters::Weno<1, tmpl::list<ScalarTag>> expected_simple_weno_1d(\n      Limiters::WenoType::SimpleWeno, 0.001, 0.0);\n  const Limiters::Weno<2, tmpl::list<ScalarTag>> expected_hweno_2d(\n      Limiters::WenoType::Hweno, 0.001, 0.0);\n  const Limiters::Weno<3, tmpl::list<ScalarTag, VectorTag<3>>>\n      expected_hweno_3d_larger_weight(Limiters::WenoType::Hweno, 0.01, 0.0,\n                                      true);\n  CHECK(hweno_1d == expected_hweno_1d);\n  CHECK(hweno_1d_larger_weight == expected_hweno_1d_larger_weight);\n  CHECK(hweno_1d_tvb == expected_hweno_1d_tvb);\n  CHECK(hweno_1d_disabled == expected_hweno_1d_disabled);\n  CHECK(simple_weno_1d == expected_simple_weno_1d);\n  CHECK(hweno_2d == expected_hweno_2d);\n  CHECK(hweno_3d_larger_weight == expected_hweno_3d_larger_weight);\n}\n\nvoid test_weno_serialization() {\n  INFO(\"Testing serialization\");\n  const Limiters::Weno<1, tmpl::list<ScalarTag>> weno(Limiters::WenoType::Hweno,\n                                                      0.01, 1.0, true);\n  test_serialization(weno);\n}\n\ntemplate <size_t VolumeDim>\nvoid test_package_data_work(const Mesh<VolumeDim>& mesh,\n                            const OrientationMap<VolumeDim>& orientation_map) {\n  const DataVector used_for_size(mesh.number_of_grid_points());\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n\n  const auto input_scalar = make_with_random_values<ScalarTag::type>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n  const auto input_vector =\n      make_with_random_values<typename VectorTag<VolumeDim>::type>(\n          make_not_null(&generator), make_not_null(&dist), used_for_size);\n  const auto element_size =\n      make_with_random_values<std::array<double, VolumeDim>>(\n          make_not_null(&generator), make_not_null(&dist), 0.0);\n\n  using TagList = tmpl::list<ScalarTag, VectorTag<VolumeDim>>;\n  const double neighbor_linear_weight = 0.001;\n  const double tvb_constant = 0.0;\n  const Limiters::Weno<VolumeDim, TagList> weno(\n      Limiters::WenoType::Hweno, neighbor_linear_weight, tvb_constant);\n  typename Limiters::Weno<VolumeDim, TagList>::PackagedData packaged_data{};\n\n  weno.package_data(make_not_null(&packaged_data), input_scalar, input_vector,\n                    mesh, element_size, orientation_map);\n\n  const Variables<TagList> oriented_vars = [&mesh, &input_scalar, &input_vector,\n                                            &orientation_map]() {\n    Variables<TagList> input_vars(mesh.number_of_grid_points());\n    get<ScalarTag>(input_vars) = input_scalar;\n    get<VectorTag<VolumeDim>>(input_vars) = input_vector;\n    return orient_variables(input_vars, mesh.extents(), orientation_map);\n  }();\n  CHECK(packaged_data.volume_data == oriented_vars);\n  CHECK(packaged_data.mesh == orientation_map(mesh));\n  CHECK(packaged_data.element_size ==\n        orientation_map.permute_from_neighbor(element_size));\n\n  // Means are just numbers and don't care about orientations\n  CHECK(get(get<::Tags::Mean<ScalarTag>>(packaged_data.means)) ==\n        mean_value(get(input_scalar), mesh));\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    CHECK(get<::Tags::Mean<VectorTag<VolumeDim>>>(packaged_data.means).get(i) ==\n          mean_value(input_vector.get(i), mesh));\n  }\n}\n\nvoid test_package_data_1d() {\n  INFO(\"Testing package_data in 1D\");\n  const Mesh<1> mesh(4, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n\n  const OrientationMap<1> orientation_aligned =\n      OrientationMap<1>::create_aligned();\n  test_package_data_work(mesh, orientation_aligned);\n\n  const OrientationMap<1> orientation_flipped(\n      std::array<Direction<1>, 1>{{Direction<1>::lower_xi()}});\n  test_package_data_work(mesh, orientation_flipped);\n}\n\nvoid test_package_data_2d() {\n  INFO(\"Testing package_data in 2D\");\n  const Mesh<2> mesh({{4, 6}}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n\n  const OrientationMap<2> orientation_aligned =\n      OrientationMap<2>::create_aligned();\n  test_package_data_work(mesh, orientation_aligned);\n\n  const OrientationMap<2> orientation_rotated(std::array<Direction<2>, 2>{\n      {Direction<2>::lower_eta(), Direction<2>::upper_xi()}});\n  test_package_data_work(mesh, orientation_rotated);\n}\n\nvoid test_package_data_3d() {\n  INFO(\"Testing package_data in 3D\");\n  const Mesh<3> mesh({{4, 6, 3}}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n\n  const OrientationMap<3> orientation_aligned =\n      OrientationMap<3>::create_aligned();\n  test_package_data_work(mesh, orientation_aligned);\n\n  const OrientationMap<3> orientation_rotated(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_zeta(), Direction<3>::upper_xi(),\n       Direction<3>::lower_eta()}});\n  test_package_data_work(mesh, orientation_rotated);\n}\n\ntemplate <size_t VolumeDim>\nVariables<tmpl::list<ScalarTag, VectorTag<VolumeDim>>> make_local_vars(\n    const Mesh<VolumeDim>& mesh);\n\ntemplate <>\nVariables<tmpl::list<ScalarTag, VectorTag<1>>> make_local_vars(\n    const Mesh<1>& mesh) {\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto& x = get<0>(logical_coords);\n  Variables<tmpl::list<ScalarTag, VectorTag<1>>> vars(\n      mesh.number_of_grid_points());\n  get(get<ScalarTag>(vars)) = 1.0 - 2.0 * x + square(x);\n  get<0>(get<VectorTag<1>>(vars)) = 0.4 * x - 0.1 * square(x);\n  return vars;\n}\n\ntemplate <>\nVariables<tmpl::list<ScalarTag, VectorTag<2>>> make_local_vars(\n    const Mesh<2>& mesh) {\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto& x = get<0>(logical_coords);\n  const auto& y = get<1>(logical_coords);\n  Variables<tmpl::list<ScalarTag, VectorTag<2>>> vars(\n      mesh.number_of_grid_points());\n  get(get<ScalarTag>(vars)) = x + y - 0.5 * square(x) + 0.5 * square(y);\n  get<0>(get<VectorTag<2>>(vars)) = x + 2.5 * y;\n  get<1>(get<VectorTag<2>>(vars)) =\n      0.1 + 0.2 * x - 0.4 * y + 0.3 * square(x) * square(y);\n  return vars;\n}\n\ntemplate <>\nVariables<tmpl::list<ScalarTag, VectorTag<3>>> make_local_vars(\n    const Mesh<3>& mesh) {\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto& x = get<0>(logical_coords);\n  const auto& y = get<1>(logical_coords);\n  const auto& z = get<2>(logical_coords);\n  Variables<tmpl::list<ScalarTag, VectorTag<3>>> vars(\n      mesh.number_of_grid_points());\n  get(get<ScalarTag>(vars)) = x + y - 0.2 * z - y * z + x * y * square(z);\n  get<0>(get<VectorTag<3>>(vars)) = 0.4 * x * y * z + square(z);\n  get<1>(get<VectorTag<3>>(vars)) = 0.01 * x - 0.01 * y + z;\n  get<2>(get<VectorTag<3>>(vars)) = x + 0.2 * y + cube(z);\n  return vars;\n}\n\ntemplate <size_t VolumeDim>\nusing VariablesMap =\n    std::unordered_map<DirectionalId<VolumeDim>,\n                       Variables<tmpl::list<ScalarTag, VectorTag<VolumeDim>>>,\n                       boost::hash<DirectionalId<VolumeDim>>>;\n\ntemplate <size_t VolumeDim>\nstd::unordered_map<\n    DirectionalId<VolumeDim>,\n    typename Limiters::Weno<\n        VolumeDim, tmpl::list<ScalarTag, VectorTag<VolumeDim>>>::PackagedData,\n    boost::hash<DirectionalId<VolumeDim>>>\nmake_neighbor_data_from_neighbor_vars(\n    const Mesh<VolumeDim>& mesh, const Element<VolumeDim>& element,\n    const std::array<double, VolumeDim>& element_size,\n    const VariablesMap<VolumeDim>& neighbor_vars) {\n  const auto make_tuple_of_means =\n      [&mesh](const Variables<tmpl::list<ScalarTag, VectorTag<VolumeDim>>>&\n                  vars_to_average) {\n        tuples::TaggedTuple<::Tags::Mean<ScalarTag>,\n                            ::Tags::Mean<VectorTag<VolumeDim>>>\n            result;\n        get(get<::Tags::Mean<ScalarTag>>(result)) =\n            mean_value(get(get<ScalarTag>(vars_to_average)), mesh);\n        for (size_t d = 0; d < VolumeDim; ++d) {\n          get<::Tags::Mean<VectorTag<VolumeDim>>>(result).get(d) = mean_value(\n              get<VectorTag<VolumeDim>>(vars_to_average).get(d), mesh);\n        }\n        return result;\n      };\n\n  std::unordered_map<\n      DirectionalId<VolumeDim>,\n      typename Limiters::Weno<\n          VolumeDim, tmpl::list<ScalarTag, VectorTag<VolumeDim>>>::PackagedData,\n      boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_data{};\n\n  for (const auto& neighbor : element.neighbors()) {\n    const auto dir = neighbor.first;\n    const auto id = *(neighbor.second.cbegin());\n    const auto dir_and_id = DirectionalId<VolumeDim>{dir, id};\n    neighbor_data[dir_and_id].volume_data = neighbor_vars.at(dir_and_id);\n    neighbor_data[dir_and_id].means =\n        make_tuple_of_means(neighbor_vars.at(dir_and_id));\n    neighbor_data[dir_and_id].mesh = mesh;\n    neighbor_data[dir_and_id].element_size = element_size;\n  }\n\n  return neighbor_data;\n}\n\ntemplate <size_t VolumeDim>\nstd::unordered_map<\n    DirectionalId<VolumeDim>,\n    typename Limiters::Weno<\n        VolumeDim, tmpl::list<ScalarTag, VectorTag<VolumeDim>>>::PackagedData,\n    boost::hash<DirectionalId<VolumeDim>>>\nmake_neighbor_data(const Mesh<VolumeDim>& mesh,\n                   const Element<VolumeDim>& element,\n                   const std::array<double, VolumeDim>& element_size);\n\ntemplate <>\nstd::unordered_map<DirectionalId<1>,\n                   typename Limiters::Weno<\n                       1, tmpl::list<ScalarTag, VectorTag<1>>>::PackagedData,\n                   boost::hash<DirectionalId<1>>>\nmake_neighbor_data(const Mesh<1>& mesh, const Element<1>& element,\n                   const std::array<double, 1>& element_size) {\n  const auto make_lower_xi_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<1>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = 16.4;\n    get<0>(get<VectorTag<1>>(vars)) = 1.2;\n    return vars;\n  };\n  const auto make_upper_xi_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<1>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = -9.5;\n    get<0>(get<VectorTag<1>>(vars)) = 4.2;\n    return vars;\n  };\n\n  VariablesMap<1> neighbor_vars{};\n  const DirectionalId<1> lower_xi{Direction<1>::lower_xi(), ElementId<1>(1)};\n  const DirectionalId<1> upper_xi{Direction<1>::upper_xi(), ElementId<1>(2)};\n  neighbor_vars[lower_xi] = make_lower_xi_vars();\n  neighbor_vars[upper_xi] = make_upper_xi_vars();\n\n  return make_neighbor_data_from_neighbor_vars(mesh, element, element_size,\n                                               neighbor_vars);\n}\n\ntemplate <>\nstd::unordered_map<DirectionalId<2>,\n                   typename Limiters::Weno<\n                       2, tmpl::list<ScalarTag, VectorTag<2>>>::PackagedData,\n                   boost::hash<DirectionalId<2>>>\nmake_neighbor_data(const Mesh<2>& mesh, const Element<2>& element,\n                   const std::array<double, 2>& element_size) {\n  const auto make_lower_xi_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<2>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = -3.2;\n    get<0>(get<VectorTag<2>>(vars)) = -4.2;\n    get<1>(get<VectorTag<2>>(vars)) = -1.7;\n    return vars;\n  };\n  const auto make_upper_xi_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<2>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = 2.8;\n    get<0>(get<VectorTag<2>>(vars)) = 3.1;\n    get<1>(get<VectorTag<2>>(vars)) = 2.3;\n    return vars;\n  };\n  const auto make_lower_eta_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<2>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = -2.7;\n    get<0>(get<VectorTag<2>>(vars)) = 1.4;\n    get<1>(get<VectorTag<2>>(vars)) = 4.5;\n    return vars;\n  };\n  const auto make_upper_eta_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<2>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = 3.1;\n    get<0>(get<VectorTag<2>>(vars)) = 2.4;\n    get<1>(get<VectorTag<2>>(vars)) = -1.2;\n    return vars;\n  };\n\n  VariablesMap<2> neighbor_vars{};\n  const DirectionalId<2> lower_xi{Direction<2>::lower_xi(), ElementId<2>(1)};\n  const DirectionalId<2> upper_xi{Direction<2>::upper_xi(), ElementId<2>(2)};\n  const DirectionalId<2> lower_eta{Direction<2>::lower_eta(), ElementId<2>(3)};\n  const DirectionalId<2> upper_eta{Direction<2>::upper_eta(), ElementId<2>(4)};\n  neighbor_vars[lower_xi] = make_lower_xi_vars();\n  neighbor_vars[upper_xi] = make_upper_xi_vars();\n  neighbor_vars[lower_eta] = make_lower_eta_vars();\n  neighbor_vars[upper_eta] = make_upper_eta_vars();\n\n  return make_neighbor_data_from_neighbor_vars(mesh, element, element_size,\n                                               neighbor_vars);\n}\n\ntemplate <>\nstd::unordered_map<DirectionalId<3>,\n                   typename Limiters::Weno<\n                       3, tmpl::list<ScalarTag, VectorTag<3>>>::PackagedData,\n                   boost::hash<DirectionalId<3>>>\nmake_neighbor_data(const Mesh<3>& mesh, const Element<3>& element,\n                   const std::array<double, 3>& element_size) {\n  const auto make_lower_xi_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<3>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = -3.2;\n    get<0>(get<VectorTag<3>>(vars)) = 0.;\n    get<1>(get<VectorTag<3>>(vars)) = -0.1;\n    get<2>(get<VectorTag<3>>(vars)) = -2.1;\n    return vars;\n  };\n  const auto make_upper_xi_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<3>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = 3.8;\n    get<0>(get<VectorTag<3>>(vars)) = 0.;\n    get<1>(get<VectorTag<3>>(vars)) = 0.1;\n    get<2>(get<VectorTag<3>>(vars)) = 2.1;\n    return vars;\n  };\n  const auto make_lower_eta_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<3>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = -2.5;\n    get<0>(get<VectorTag<3>>(vars)) = 0.;\n    get<1>(get<VectorTag<3>>(vars)) = 0.1;\n    get<2>(get<VectorTag<3>>(vars)) = -0.4;\n    return vars;\n  };\n  const auto make_upper_eta_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<3>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = 2.3;\n    get<0>(get<VectorTag<3>>(vars)) = 0.;\n    get<1>(get<VectorTag<3>>(vars)) = -0.1;\n    get<2>(get<VectorTag<3>>(vars)) = 1.1;\n    return vars;\n  };\n  const auto make_lower_zeta_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<3>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = 0.41;\n    get<0>(get<VectorTag<3>>(vars)) = 0.;\n    get<1>(get<VectorTag<3>>(vars)) = -2.3;\n    ;\n    get<2>(get<VectorTag<3>>(vars)) = -8.2;\n    return vars;\n  };\n  const auto make_upper_zeta_vars = [&mesh]() {\n    Variables<tmpl::list<ScalarTag, VectorTag<3>>> vars(\n        mesh.number_of_grid_points());\n    get(get<ScalarTag>(vars)) = -0.42;\n    get<0>(get<VectorTag<3>>(vars)) = 0.;\n    get<1>(get<VectorTag<3>>(vars)) = 2.3;\n    get<2>(get<VectorTag<3>>(vars)) = 9.;\n    return vars;\n  };\n\n  VariablesMap<3> neighbor_vars{};\n  const DirectionalId<3> lower_xi{Direction<3>::lower_xi(), ElementId<3>(1)};\n  const DirectionalId<3> upper_xi{Direction<3>::upper_xi(), ElementId<3>(2)};\n  const DirectionalId<3> lower_eta{Direction<3>::lower_eta(), ElementId<3>(3)};\n  const DirectionalId<3> upper_eta{Direction<3>::upper_eta(), ElementId<3>(4)};\n  const DirectionalId<3> lower_zeta{Direction<3>::lower_zeta(),\n                                    ElementId<3>(5)};\n  const DirectionalId<3> upper_zeta{Direction<3>::upper_zeta(),\n                                    ElementId<3>(6)};\n  neighbor_vars[lower_xi] = make_lower_xi_vars();\n  neighbor_vars[upper_xi] = make_upper_xi_vars();\n  neighbor_vars[lower_eta] = make_lower_eta_vars();\n  neighbor_vars[upper_eta] = make_upper_eta_vars();\n  neighbor_vars[lower_zeta] = make_lower_zeta_vars();\n  neighbor_vars[upper_zeta] = make_upper_zeta_vars();\n\n  return make_neighbor_data_from_neighbor_vars(mesh, element, element_size,\n                                               neighbor_vars);\n}\n\ntemplate <size_t VolumeDim>\nvoid test_simple_weno(const std::array<size_t, VolumeDim>& extents) {\n  INFO(\"Testing simple WENO limiter\");\n  CAPTURE(VolumeDim);\n  const auto mesh = Mesh<VolumeDim>(extents, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::Gauss);\n  const auto element = TestHelpers::Limiters::make_element<VolumeDim>();\n  const auto element_size = make_array<VolumeDim>(1.2);\n\n  // Make local + neighbor data where the vector x-component triggers limiting\n  const auto local_vars = make_local_vars(mesh);\n  const auto neighbor_data = make_neighbor_data(mesh, element, element_size);\n\n  const double neighbor_linear_weight = 0.001;\n  const double tvb_constant = 0.0;\n  using Weno =\n      Limiters::Weno<VolumeDim, tmpl::list<ScalarTag, VectorTag<VolumeDim>>>;\n  const Weno simple_weno(Limiters::WenoType::SimpleWeno, neighbor_linear_weight,\n                         tvb_constant);\n\n  auto scalar = get<ScalarTag>(local_vars);\n  auto vector = get<VectorTag<VolumeDim>>(local_vars);\n  const bool activated =\n      simple_weno(make_not_null(&scalar), make_not_null(&vector), mesh, element,\n                  element_size, neighbor_data);\n\n  // Because simple WENO acts on each tensor component independently, only the\n  // vector x-component should be modified by the limiter\n  const auto& expected_scalar = get<ScalarTag>(local_vars);\n  auto expected_vector = get<VectorTag<VolumeDim>>(local_vars);\n  std::unordered_map<DirectionalId<VolumeDim>, intrp::RegularGrid<VolumeDim>,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      interpolator_buffer{};\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      modified_neighbor_solution_buffer{};\n  for (const auto& neighbor_and_data : neighbor_data) {\n    const auto& neighbor = neighbor_and_data.first;\n    modified_neighbor_solution_buffer.insert(\n        std::make_pair(neighbor, DataVector(mesh.number_of_grid_points())));\n  }\n  Limiters::Weno_detail::simple_weno_impl<VectorTag<VolumeDim>>(\n      make_not_null(&interpolator_buffer),\n      make_not_null(&modified_neighbor_solution_buffer),\n      make_not_null(&expected_vector), neighbor_linear_weight,\n      0,  // the x-component\n      mesh, element, neighbor_data);\n\n  CHECK(activated);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_scalar, scalar, approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_vector, vector, approx);\n\n  // Now call the limiter again, but this time use a non-zero TVB constant such\n  // that the TCI says limiting isn't needed\n  const double tvb_constant_larger = 2.0;\n  const Weno simple_weno_tvb(Limiters::WenoType::SimpleWeno,\n                             neighbor_linear_weight, tvb_constant_larger);\n\n  scalar = get<ScalarTag>(local_vars);\n  vector = get<VectorTag<VolumeDim>>(local_vars);\n  const bool activated_tvb =\n      simple_weno_tvb(make_not_null(&scalar), make_not_null(&vector), mesh,\n                      element, element_size, neighbor_data);\n\n  // expected_scalar is already set to local_vars\n  expected_vector = get<VectorTag<VolumeDim>>(local_vars);\n\n  CHECK_FALSE(activated_tvb);\n  CHECK_ITERABLE_APPROX(expected_scalar, scalar);\n  CHECK_ITERABLE_APPROX(expected_vector, vector);\n}\n\ntemplate <size_t VolumeDim>\nvoid test_hweno(const std::array<size_t, VolumeDim>& extents) {\n  INFO(\"Testing HWENO limiter\");\n  CAPTURE(VolumeDim);\n  const auto mesh = Mesh<VolumeDim>(extents, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto);\n  const auto element = TestHelpers::Limiters::make_element<VolumeDim>();\n  const auto element_size = make_array<VolumeDim>(1.2);\n\n  // Make local + neighbor data where the vector x-component triggers limiting\n  const auto local_vars = make_local_vars(mesh);\n  const auto neighbor_data = make_neighbor_data(mesh, element, element_size);\n\n  const double neighbor_linear_weight = 0.001;\n  const double tvb_constant = 0.0;\n  using Weno =\n      Limiters::Weno<VolumeDim, tmpl::list<ScalarTag, VectorTag<VolumeDim>>>;\n  const Weno hweno(Limiters::WenoType::Hweno, neighbor_linear_weight,\n                   tvb_constant);\n\n  auto scalar = get<ScalarTag>(local_vars);\n  auto vector = get<VectorTag<VolumeDim>>(local_vars);\n  const bool activated = hweno(make_not_null(&scalar), make_not_null(&vector),\n                               mesh, element, element_size, neighbor_data);\n\n  // Because HWENO acts on the whole solution, the scalar and all vector\n  // components should be modified by the limiter\n  auto expected_scalar = get<ScalarTag>(local_vars);\n  auto expected_vector = get<VectorTag<VolumeDim>>(local_vars);\n  std::unordered_map<DirectionalId<VolumeDim>, DataVector,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      modified_neighbor_solution_buffer{};\n  for (const auto& neighbor_and_data : neighbor_data) {\n    const auto& neighbor = neighbor_and_data.first;\n    modified_neighbor_solution_buffer.insert(\n        std::make_pair(neighbor, DataVector(mesh.number_of_grid_points())));\n  }\n  Limiters::Weno_detail::hweno_impl<ScalarTag>(\n      make_not_null(&modified_neighbor_solution_buffer),\n      make_not_null(&expected_scalar), neighbor_linear_weight, mesh, element,\n      neighbor_data);\n  Limiters::Weno_detail::hweno_impl<VectorTag<VolumeDim>>(\n      make_not_null(&modified_neighbor_solution_buffer),\n      make_not_null(&expected_vector), neighbor_linear_weight, mesh, element,\n      neighbor_data);\n\n  CHECK(activated);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_scalar, scalar, approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_vector, vector, approx);\n\n  // Now call the limiter again, but this time use a non-zero TVB constant such\n  // that the TCI says limiting isn't needed\n  const double tvb_constant_larger = 2.0;\n  const Weno hweno_tvb(Limiters::WenoType::Hweno, neighbor_linear_weight,\n                       tvb_constant_larger);\n\n  scalar = get<ScalarTag>(local_vars);\n  vector = get<VectorTag<VolumeDim>>(local_vars);\n  const bool activated_tvb =\n      hweno_tvb(make_not_null(&scalar), make_not_null(&vector), mesh, element,\n                element_size, neighbor_data);\n\n  expected_scalar = get<ScalarTag>(local_vars);\n  expected_vector = get<VectorTag<VolumeDim>>(local_vars);\n\n  CHECK_FALSE(activated_tvb);\n  CHECK_ITERABLE_APPROX(expected_scalar, scalar);\n  CHECK_ITERABLE_APPROX(expected_vector, vector);\n}\n\n}  // namespace\n\n// [[Timeout, 30]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.Weno\", \"[Limiters][Unit]\") {\n  test_weno_option_parsing();\n  test_weno_serialization();\n\n  test_package_data_1d();\n  test_package_data_2d();\n  test_package_data_3d();\n\n  // The simple WENO reconstruction is tested in Test_SimpleWenoImpl.cpp.\n  // There, both LGL and LG points are used. Here we only use Gauss points.\n  // Here we test that\n  // - the TCI correctly acts component-by-component\n  // - the limiter is indeed calling `simple_weno_impl`\n  test_simple_weno<1>({{3}});\n  test_simple_weno<2>({{3, 4}});\n  test_simple_weno<3>({{3, 4, 5}});\n\n  // The HWENO reconstruction is tested in Test_HwenoImpl.cpp.\n  // There, both LGL and LG points are used. Here we only use LGL points.\n  // Here we test that\n  // - the TCI correctly triggers limiting on all tensors at once\n  // - the limiter is indeed calling `hweno_impl`\n  test_hweno<1>({{3}});\n  test_hweno<2>({{3, 4}});\n  test_hweno<3>({{3, 4, 5}});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_WenoGridHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoGridHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\nvoid test_check_element_no_href() {\n  // Make a 2D element with neighbors:\n  // - lower_xi: 1 neighbor, same refinement level => true\n  // - upper_xi: 1 neighbor, same refinement level, with orientation => true\n  // - lower_eta: 2 neighbors => false\n  // - lower_eta: 1 neighbor, different refinement level => false\n  const ElementId<2> self_id(0, {{{3, 7}, {4, 2}}});\n  const ElementId<2> lower_xi_id(1, {{{3, 5}, {4, 11}}});\n  const ElementId<2> upper_xi_id(2, {{{4, 7}, {3, 0}}});\n  const ElementId<2> lower_eta_id_1(3, {{{4, 14}, {4, 2}}});\n  const ElementId<2> lower_eta_id_2(3, {{{4, 15}, {4, 2}}});\n  const ElementId<2> upper_eta_id(5, {{{3, 7}, {3, 2}}});\n\n  const Neighbors<2> lower_xi_neighbors({lower_xi_id},\n                                        OrientationMap<2>::create_aligned());\n  const Neighbors<2> upper_xi_neighbors(\n      {upper_xi_id},\n      OrientationMap<2>(std::array<Direction<2>, 2>{\n          {Direction<2>::lower_eta(), Direction<2>::upper_xi()}}));\n  const Neighbors<2> lower_eta_neighbors({lower_eta_id_1, lower_eta_id_2},\n                                         OrientationMap<2>::create_aligned());\n  const Neighbors<2> upper_eta_neighbors({upper_eta_id},\n                                         OrientationMap<2>::create_aligned());\n\n  const Element<2> element(\n      self_id, Element<2>::Neighbors_t{\n                   {Direction<2>::lower_xi(), lower_xi_neighbors},\n                   {Direction<2>::upper_xi(), upper_xi_neighbors},\n                   {Direction<2>::lower_eta(), lower_eta_neighbors},\n                   {Direction<2>::upper_eta(), upper_eta_neighbors}});\n\n  CHECK(Limiters::Weno_detail::\n            check_element_has_one_similar_neighbor_in_direction(\n                element, Direction<2>::lower_xi()));\n  CHECK(Limiters::Weno_detail::\n            check_element_has_one_similar_neighbor_in_direction(\n                element, Direction<2>::upper_xi()));\n  CHECK_FALSE(Limiters::Weno_detail::\n                  check_element_has_one_similar_neighbor_in_direction(\n                      element, Direction<2>::lower_eta()));\n  CHECK_FALSE(Limiters::Weno_detail::\n                  check_element_has_one_similar_neighbor_in_direction(\n                      element, Direction<2>::upper_eta()));\n}\n\ntemplate <size_t VolumeDim>\nvoid check_grid_point_transform_no_href(const Mesh<VolumeDim>& local_mesh,\n                                        const Mesh<VolumeDim>& neighbor_mesh,\n                                        const Element<VolumeDim>& element) {\n  const auto check =\n      [&local_mesh, &neighbor_mesh](\n          const std::array<DataVector, VolumeDim>& transformed_coords,\n          const bool local_mesh_provides_grid_points, const size_t dim,\n          const double offset) {\n        const Mesh<VolumeDim>& source_mesh =\n            (local_mesh_provides_grid_points ? local_mesh : neighbor_mesh);\n        for (size_t i = 0; i < VolumeDim; ++i) {\n          const DataVector source_coords =\n              get<0>(logical_coordinates(source_mesh.slice_through(i)));\n          if (i == dim) {\n            // Coordinates normal to the interface\n            const DataVector expected_coords = source_coords + offset;\n            CHECK(gsl::at(transformed_coords, i) == expected_coords);\n          } else {\n            // Coordinates parallel to the interface\n            if (neighbor_mesh.slice_through(i) == local_mesh.slice_through(i)) {\n              CHECK(gsl::at(transformed_coords, i).size() == 0);\n            } else {\n              const DataVector& expected_coords = source_coords;\n              CHECK(gsl::at(transformed_coords, i) == expected_coords);\n            }\n          }\n        }\n      };\n\n  for (size_t dim = 0; dim < VolumeDim; ++dim) {\n    const auto from_lower =\n        Limiters::Weno_detail::neighbor_grid_points_in_local_logical_coords(\n            local_mesh, neighbor_mesh, element,\n            Direction<VolumeDim>(dim, Side::Lower));\n    check(from_lower, false, dim, -2.);\n\n    const auto from_upper =\n        Limiters::Weno_detail::neighbor_grid_points_in_local_logical_coords(\n            local_mesh, neighbor_mesh, element,\n            Direction<VolumeDim>(dim, Side::Upper));\n    check(from_upper, false, dim, 2.);\n\n    const auto to_lower =\n        Limiters::Weno_detail::local_grid_points_in_neighbor_logical_coords(\n            local_mesh, neighbor_mesh, element,\n            Direction<VolumeDim>(dim, Side::Lower));\n    check(to_lower, true, dim, 2.);\n\n    const auto to_upper =\n        Limiters::Weno_detail::local_grid_points_in_neighbor_logical_coords(\n            local_mesh, neighbor_mesh, element,\n            Direction<VolumeDim>(dim, Side::Upper));\n    check(to_upper, true, dim, -2.);\n  }\n}\n\nvoid test_grid_helpers_1d() {\n  INFO(\"Testing WENO grid helpers in 1D\");\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const Mesh<1> mesh({{6}}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  check_grid_point_transform_no_href(mesh, mesh, element);\n\n  const Mesh<1> other_mesh({{6}}, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::Gauss);\n  check_grid_point_transform_no_href(mesh, other_mesh, element);\n}\n\nvoid test_grid_helpers_2d() {\n  INFO(\"Testing WENO grid helpers in 2D\");\n  const auto element = TestHelpers::Limiters::make_element<2>();\n  const Mesh<2> mesh({{5, 6}}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  check_grid_point_transform_no_href(mesh, mesh, element);\n\n  const Mesh<2> other_mesh({{4, 6}}, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto);\n  check_grid_point_transform_no_href(mesh, other_mesh, element);\n}\n\nvoid test_grid_helpers_3d() {\n  INFO(\"Testing WENO grid helpers in 3D\");\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const Mesh<3> mesh({{4, 5, 6}}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  check_grid_point_transform_no_href(mesh, mesh, element);\n\n  const Mesh<3> other_mesh({{4, 5, 3}}, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto);\n  check_grid_point_transform_no_href(mesh, other_mesh, element);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.Weno.GridHelpers\",\n                  \"[Limiters][Unit]\") {\n  test_check_element_no_href();\n  test_grid_helpers_1d();\n  test_grid_helpers_2d();\n  test_grid_helpers_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_WenoHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\nvoid test_reconstruction_1d_impl(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const double neighbor_linear_weight = 0.005;\n  const Mesh<1> mesh(5, Spectral::Basis::Legendre, quadrature);\n  const auto coords = logical_coordinates(mesh);\n\n  const auto evaluate_polynomial =\n      [&coords](const std::array<double, 5>& coeffs) {\n        const auto& x = get<0>(coords);\n        return DataVector{coeffs[0] + coeffs[1] * x + coeffs[2] * square(x) +\n                          coeffs[3] * cube(x) + coeffs[4] * pow<4>(x)};\n      };\n\n  DataVector local_data = evaluate_polynomial({{1., 2., 0., 0.5, 0.1}});\n  // WENO reconstruction should preserve the mean, so expected = initial\n  const double expected_local_mean = mean_value(local_data, mesh);\n\n  const auto shift_data_to_local_mean =\n      [&mesh, &expected_local_mean](const DataVector& neighbor_data) {\n        return neighbor_data + expected_local_mean -\n               mean_value(neighbor_data, mesh);\n      };\n\n  std::unordered_map<DirectionalId<1>, DataVector,\n                     boost::hash<DirectionalId<1>>>\n      neighbor_data{};\n  neighbor_data[DirectionalId<1>{Direction<1>::lower_xi(), ElementId<1>(1)}] =\n      shift_data_to_local_mean(evaluate_polynomial({{0., 1., 0., 1., 0.}}));\n  neighbor_data[DirectionalId<1>{Direction<1>::upper_xi(), ElementId<1>(2)}] =\n      shift_data_to_local_mean(evaluate_polynomial({{0., 0., 1., 1., 2.}}));\n\n  // Expected result computed in Mathematica by computing oscillation indicator\n  // as in oscillation_indicator tests, then WENO weights, then superposition.\n  const DataVector expected_reconstructed_data = evaluate_polynomial(\n      {{1.0000250662809542, 1.9987344134217362, 3.25819395292328e-7,\n        0.5006326303794342, 0.09987412556290375}});\n\n  Limiters::Weno_detail::reconstruct_from_weighted_sum(\n      make_not_null(&local_data), neighbor_linear_weight,\n      Limiters::Weno_detail::DerivativeWeight::Unity, mesh, neighbor_data);\n  CHECK(mean_value(local_data, mesh) == approx(expected_local_mean));\n  CHECK_ITERABLE_APPROX(local_data, expected_reconstructed_data);\n}\n\nvoid test_reconstruction_1d() {\n  INFO(\"Testing WENO reconstruction in 1D\");\n  test_reconstruction_1d_impl(Spectral::Quadrature::GaussLobatto);\n  test_reconstruction_1d_impl(Spectral::Quadrature::Gauss);\n}\n\nvoid test_reconstruction_2d_impl(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const double neighbor_linear_weight = 0.001;\n  const Mesh<2> mesh({{3, 3}}, Spectral::Basis::Legendre, quadrature);\n  const auto coords = logical_coordinates(mesh);\n\n  const auto evaluate_polynomial =\n      [&coords](const std::array<double, 9>& coeffs) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        return DataVector{\n            coeffs[0] + coeffs[1] * x + coeffs[2] * square(x) +\n            y * (coeffs[3] + coeffs[4] * x + coeffs[5] * square(x)) +\n            square(y) * (coeffs[6] + coeffs[7] * x + coeffs[8] * square(x))};\n      };\n\n  DataVector local_data =\n      evaluate_polynomial({{2., 1., 0., 1.5, 1., 0., 1., 0., 0.}});\n  // WENO reconstruction should preserve the mean, so expected = initial\n  const double expected_local_mean = mean_value(local_data, mesh);\n\n  const auto shift_data_to_local_mean =\n      [&mesh, &expected_local_mean](const DataVector& neighbor_data) {\n        return neighbor_data + expected_local_mean -\n               mean_value(neighbor_data, mesh);\n      };\n\n  std::unordered_map<DirectionalId<2>, DataVector,\n                     boost::hash<DirectionalId<2>>>\n      neighbor_data{};\n  neighbor_data[DirectionalId<2>{Direction<2>::lower_xi(), ElementId<2>(1)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{0., 1., 0., 0., 1., 1., 0., 0., 0}}));\n  neighbor_data[DirectionalId<2>{Direction<2>::upper_xi(), ElementId<2>(2)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{0., 0., 1., 1., 2., 1., 0., 1., 1.}}));\n  neighbor_data[DirectionalId<2>{Direction<2>::lower_eta(), ElementId<2>(3)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{1., 0., 0., 0., 0.5, 0., 0., 0., 0.5}}));\n  neighbor_data[DirectionalId<2>{Direction<2>::upper_eta(), ElementId<2>(4)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{1., 0., 0., 0.5, 1., 0., 0., 0., 0.}}));\n\n  // Expected result computed in Mathematica by computing oscillation indicator\n  // as in oscillation_indicator tests, then WENO weights, then superposition.\n  const DataVector expected_reconstructed_data = evaluate_polynomial(\n      {{2.010056442214612, 0.9705606381771584, 0.000026246579961852654,\n        1.4682459390241314, 0.9992393252122325, 0.0010535139634810797,\n        0.9695333707936392, 0.000026246579961852654, 0.0008131679476911193}});\n\n  Limiters::Weno_detail::reconstruct_from_weighted_sum(\n      make_not_null(&local_data), neighbor_linear_weight,\n      Limiters::Weno_detail::DerivativeWeight::Unity, mesh, neighbor_data);\n  CHECK(mean_value(local_data, mesh) == approx(expected_local_mean));\n  CHECK_ITERABLE_APPROX(local_data, expected_reconstructed_data);\n}\n\nvoid test_reconstruction_2d() {\n  INFO(\"Testing WENO reconstruction in 2D\");\n  test_reconstruction_2d_impl(Spectral::Quadrature::GaussLobatto);\n  test_reconstruction_2d_impl(Spectral::Quadrature::Gauss);\n}\n\nvoid test_reconstruction_3d_impl(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const double neighbor_linear_weight = 0.001;\n  const Mesh<3> mesh({{3, 3, 3}}, Spectral::Basis::Legendre, quadrature);\n  const auto coords = logical_coordinates(mesh);\n\n  // 3D case has so many modes... so we simplify by only setting 6 of them, the\n  // choice of modes to use here is arbitrary.\n  const auto evaluate_polynomial =\n      [&coords](const std::array<double, 6>& coeffs) {\n        const auto& x = get<0>(coords);\n        const auto& y = get<1>(coords);\n        const auto& z = get<2>(coords);\n        return DataVector{coeffs[0] + coeffs[1] * y + coeffs[2] * x * z +\n                          coeffs[3] * x * y * z + coeffs[4] * square(y) * z +\n                          coeffs[5] * square(x) * y * square(z)};\n      };\n\n  DataVector local_data = evaluate_polynomial({{1., 0.5, 0.5, 0.2, 0.2, 0.1}});\n  // WENO reconstruction should preserve the mean, so expected = initial\n  const double expected_local_mean = mean_value(local_data, mesh);\n\n  const auto shift_data_to_local_mean =\n      [&mesh, &expected_local_mean](const DataVector& neighbor_data) {\n        return neighbor_data + expected_local_mean -\n               mean_value(neighbor_data, mesh);\n      };\n\n  // We skip one neighbor, lower_eta, to simulate an external boundary\n  std::unordered_map<DirectionalId<3>, DataVector,\n                     boost::hash<DirectionalId<3>>>\n      neighbor_data{};\n  neighbor_data[DirectionalId<3>{Direction<3>::lower_xi(), ElementId<3>(1)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{0.3, 0.2, 0.2, 0., 0., 0.1}}));\n  neighbor_data[DirectionalId<3>{Direction<3>::upper_xi(), ElementId<3>(2)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{2.5, 1., 0., 0., 1., 1.}}));\n  neighbor_data[DirectionalId<3>{Direction<3>::upper_eta(), ElementId<3>(4)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{1., 0.5, 0.5, 0.2, 0.2, 0.2}}));\n  neighbor_data[DirectionalId<3>{Direction<3>::lower_zeta(), ElementId<3>(5)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{1., 0.2, 0., 0., 0., 0.}}));\n  neighbor_data[DirectionalId<3>{Direction<3>::upper_zeta(), ElementId<3>(6)}] =\n      shift_data_to_local_mean(\n          evaluate_polynomial({{0.1, 0., 0.5, 0.2, 0.2, 0.2}}));\n\n  // Expected result computed in Mathematica by computing oscillation indicator\n  // as in oscillation_indicator tests, then WENO weights, then superposition.\n  const DataVector expected_reconstructed_data = evaluate_polynomial(\n      {{1., 0.32663481881058243, 0.21186828015830592, 0.08447466846582166,\n        0.08447504580872492, 0.04260655032204396}});\n\n  Limiters::Weno_detail::reconstruct_from_weighted_sum(\n      make_not_null(&local_data), neighbor_linear_weight,\n      Limiters::Weno_detail::DerivativeWeight::Unity, mesh, neighbor_data);\n  CHECK(mean_value(local_data, mesh) == approx(expected_local_mean));\n  CHECK_ITERABLE_APPROX(local_data, expected_reconstructed_data);\n}\n\nvoid test_reconstruction_3d() {\n  INFO(\"Testing WENO reconstruction in 3D\");\n  test_reconstruction_3d_impl(Spectral::Quadrature::GaussLobatto);\n  test_reconstruction_3d_impl(Spectral::Quadrature::Gauss);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.Weno.Helpers\",\n                  \"[Limiters][Unit]\") {\n  test_reconstruction_1d();\n  test_reconstruction_2d();\n  test_reconstruction_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_WenoOscillationIndicator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoOscillationIndicator.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace {\n\nvoid test_derivative_weight() {\n  INFO(\"Testing DerivativeWeight\");\n\n  CHECK(get_output(Limiters::Weno_detail::DerivativeWeight::Unity) == \"Unity\");\n  CHECK(get_output(Limiters::Weno_detail::DerivativeWeight::PowTwoEll) ==\n        \"PowTwoEll\");\n  CHECK(\n      get_output(\n          Limiters::Weno_detail::DerivativeWeight::PowTwoEllOverEllFactorial) ==\n      \"PowTwoEllOverEllFactorial\");\n}\n\nvoid test_oscillation_indicator_1d_impl(const size_t number_of_points,\n                                        const Spectral::Quadrature quadrature) {\n  // Sanity check there are enough grid points to resolve test function\n  if (number_of_points < 5) {\n    ERROR(\"test_oscillation_indicator_1d_impl needs 5+ grid points\");\n  }\n  CAPTURE(number_of_points);\n  CAPTURE(quadrature);\n  const Mesh<1> mesh(number_of_points, Spectral::Basis::Legendre, quadrature);\n  const auto logical_coords = logical_coordinates(mesh);\n  const DataVector& x = get<0>(logical_coords);\n\n  const auto data = DataVector{1. + x - pow<4>(x)};\n  const auto indicator = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::Unity, data, mesh);\n\n  // Expected result computed in Mathematica:\n  // f[x_] := 1 + x - x^4\n  // w[i_] := 1\n  // Integrate[Sum[w[i] Evaluate[D[f[x], {x, i}]^2], {i, 1, 4}], {x, -1, 1}]\n  const double expected = 56006. / 35.;\n  CHECK(indicator == approx(expected));\n\n  // As above, but with derivative weights given by\n  // w[i_] := 2^(2 i - 1)\n  const auto indicator2 = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::PowTwoEll, data, mesh);\n  const double expected2 = 5607628. / 35.;\n  CHECK(indicator2 == approx(expected2));\n\n  // Again as above, but with derivative weights given by\n  // w[i_] := 2^(2 i - 1) / (i!)^2\n  const auto indicator3 = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::PowTwoEllOverEllFactorial, data,\n      mesh);\n  const double expected3 = 76196. / 105.;\n  CHECK(indicator3 == approx(expected3));\n}\n\nvoid test_oscillation_indicator_1d() {\n  INFO(\"Testing oscillation_indicator in 1D\");\n  // Call with multiple resolutions to verify that the caching of the indicator\n  // matrix correctly accounts for the input mesh\n  test_oscillation_indicator_1d_impl(5, Spectral::Quadrature::GaussLobatto);\n  test_oscillation_indicator_1d_impl(6, Spectral::Quadrature::GaussLobatto);\n  test_oscillation_indicator_1d_impl(5, Spectral::Quadrature::Gauss);\n  test_oscillation_indicator_1d_impl(6, Spectral::Quadrature::Gauss);\n}\n\nvoid test_oscillation_indicator_2d_impl(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const Mesh<2> mesh({{4, 5}}, Spectral::Basis::Legendre, quadrature);\n  const auto logical_coords = logical_coordinates(mesh);\n  const DataVector& x = get<0>(logical_coords);\n  const DataVector& y = get<1>(logical_coords);\n\n  const auto data =\n      DataVector{square(x) + cube(y) - 2.5 * x * y + square(x) * y};\n  const auto indicator = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::Unity, data, mesh);\n\n  // Expected result computed in Mathematica:\n  // g[x_, y_] := x^2 + y^3 - (5/2) x y + x^2 y\n  // w[i_] := 1\n  // Integrate[\n  //  Sum[w[i] w[j] Evaluate[D[g[x, y], {x, i}, {y, j}]^2],\n  //      {i, 1, 3}, {j, 1, 4}]\n  //   + Sum[w[i] w[0] Evaluate[D[g[x, y], {x, i}]^2], {i, 1, 3}]\n  //   + Sum[w[0] w[j] Evaluate[D[g[x, y], {y, j}]^2], {j, 1, 4}],\n  //  {x, -1, 1}, {y, -1, 1}]\n  const double expected = 2647. / 9.;\n  CHECK(indicator == approx(expected));\n\n  // w[i_] := 2^(2 i - 1)\n  const auto indicator2 = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::PowTwoEll, data, mesh);\n  const double expected2 = 26938. / 9.;\n  CHECK(indicator2 == approx(expected2));\n\n  // w[i_] := 2^(2 i - 1) / (i!)^2\n  const auto indicator3 = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::PowTwoEllOverEllFactorial, data,\n      mesh);\n  const double expected3 = 3178. / 9.;\n  CHECK(indicator3 == approx(expected3));\n}\n\nvoid test_oscillation_indicator_2d() {\n  INFO(\"Testing oscillation_indicator in 2D\");\n  test_oscillation_indicator_2d_impl(Spectral::Quadrature::GaussLobatto);\n  test_oscillation_indicator_2d_impl(Spectral::Quadrature::Gauss);\n}\n\nvoid test_oscillation_indicator_3d_impl(const Spectral::Quadrature quadrature) {\n  CAPTURE(quadrature);\n  const Mesh<3> mesh({{4, 3, 5}}, Spectral::Basis::Legendre, quadrature);\n  const auto logical_coords = logical_coordinates(mesh);\n  const DataVector& x = get<0>(logical_coords);\n  const DataVector& y = get<1>(logical_coords);\n  const DataVector& z = get<2>(logical_coords);\n\n  const auto data = DataVector{square(x) + 2. * y + z - 6. * cube(z) -\n                               3. * x * square(y) * cube(z) - x * y + y * z};\n  const auto indicator = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::Unity, data, mesh);\n\n  // Expected result computed in Mathematica:\n  // h[x_, y_, z_] := x^2 + 2 y + z - 6 z^3 - 3 x y^2 z^3 - x y + y z\n  // w[i_] := 1\n  // Integrate[\n  //  Sum[w[i] w[j] w[k] Evaluate[D[h[x, y, z], {x, i}, {y, j}, {z, k}]^2],\n  //      {i, 1, 3}, {j, 1, 2}, {k, 1, 4}]\n  //   + Sum[w[i] w[j] w[0] Evaluate[ D[h[x, y, z], {x, i}, {y, j}]^2],\n  //         {i, 1, 3}, {j, 1, 2}]\n  //   + Sum[w[i] w[0] w[k] Evaluate[ D[h[x, y, z], {x, i}, {z, k}]^2],\n  //         {i, 1, 3}, {k, 1, 4}]\n  //   + Sum[w[0] w[j] w[k] Evaluate[ D[h[x, y, z], {y, j}, {z, k}]^2],\n  //         {j, 1, 2}, {k, 1, 4}]\n  //   + Sum[w[i] w[0] w[0] Evaluate[ D[h[x, y, z], {x, i}]^2], {i, 1, 3}]\n  //   + Sum[w[0] w[j] w[0] Evaluate[ D[h[x, y, z], {y, j}]^2], {j, 1, 2}]\n  //   + Sum[w[0] w[0] w[k] Evaluate[ D[h[x, y, z], {z, k}]^2], {k, 1, 4}],\n  //  {x, -1, 1}, {y, -1, 1}, {z, -1, 1}]\n  const double expected = 3066352. / 75.;\n  CHECK(indicator == approx(expected));\n\n  // w[i_] := 2^(2 i - 1)\n  const auto indicator2 = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::PowTwoEll, data, mesh);\n  const double expected2 = 3611348444. / 525.;\n  CHECK(indicator2 == approx(expected2));\n\n  // w[i_] := 2^(2 i - 1) / (i!)^2\n  const auto indicator3 = Limiters::Weno_detail::oscillation_indicator(\n      Limiters::Weno_detail::DerivativeWeight::PowTwoEllOverEllFactorial, data,\n      mesh);\n  const double expected3 = 54886604. / 525.;\n  CHECK(indicator3 == approx(expected3));\n}\n\nvoid test_oscillation_indicator_3d() {\n  INFO(\"Testing oscillation_indicator in 3D\");\n  test_oscillation_indicator_3d_impl(Spectral::Quadrature::GaussLobatto);\n  test_oscillation_indicator_3d_impl(Spectral::Quadrature::Gauss);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.Weno.OscillationIndicator\",\n                  \"[Limiters][Unit]\") {\n  test_derivative_weight();\n\n  test_oscillation_indicator_1d();\n  test_oscillation_indicator_2d();\n  test_oscillation_indicator_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Limiters/Test_WenoType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Limiters.WenoType\", \"[Limiters][Unit]\") {\n  CHECK(Limiters::WenoType::Hweno ==\n        TestHelpers::test_creation<Limiters::WenoType>(\"Hweno\"));\n  CHECK(Limiters::WenoType::SimpleWeno ==\n        TestHelpers::test_creation<Limiters::WenoType>(\"SimpleWeno\"));\n\n  CHECK(get_output(Limiters::WenoType::Hweno) == \"Hweno\");\n  CHECK(get_output(Limiters::WenoType::SimpleWeno) == \"SimpleWeno\");\n\n  CHECK_THROWS_WITH((TestHelpers::test_creation<Limiters::WenoType>(\"BadType\")),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Failed to convert \\\"BadType\\\" to WenoType\"));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Messages/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DgMessages\")\n\nset(LIBRARY_SOURCES\n  Test_BoundaryMessage.cpp\n  Test_InboxTags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\" WITH_CHARM)\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructuresHelpers\n  DiscontinuousGalerkin\n  DomainStructure\n  Evolution\n  Spectral\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Messages/Test_BoundaryMessage.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <sstream>\n#include <string>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg {\nnamespace {\ntemplate <size_t Dim, typename Generator>\nvoid test_boundary_message(const gsl::not_null<Generator*> generator,\n                           const size_t subcell_size, const size_t dg_size) {\n  CAPTURE(Dim);\n  CAPTURE(subcell_size);\n  CAPTURE(dg_size);\n\n  const size_t total_size_with_data =\n      BoundaryMessage<Dim>::total_bytes_with_data(subcell_size, dg_size);\n  CHECK(total_size_with_data == sizeof(BoundaryMessage<Dim>) +\n                                    (subcell_size + dg_size) * sizeof(double));\n\n  const bool owning = false;\n  const bool enable_if_disabled = false;\n  const size_t sender_node = 2;\n  const size_t sender_core = 15;\n  const int tci_status = -3;\n  const size_t integration_order = 3;\n\n  const Slab current_slab{0.1, 0.5};\n  const Time current_time{current_slab, {0, 1}};\n  const TimeStepId current_time_id{true, 0, current_time};\n  const Slab next_slab{0.5, 0.9};\n  const Time next_time{next_slab, {0, 1}};\n  const TimeStepId next_time_id{true, 0, next_time};\n  const Direction<Dim> neighbor_direction{0, Side::Upper};\n  const ElementId<Dim> element_id{0};\n\n  const size_t extents = 4;\n  const Mesh<Dim> volume_mesh{extents, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim - 1> interface_mesh = volume_mesh.slice_away(0);\n\n  std::uniform_real_distribution<double> dist{-1.0, 1.0};\n  auto subcell_data = make_with_random_values<DataVector>(\n      generator, make_not_null(&dist), subcell_size);\n  auto dg_data = make_with_random_values<DataVector>(\n      generator, make_not_null(&dist), dg_size);\n  // Have to copy data so we have different pointers inside BoundaryMessage to\n  // different data that isn't deleted inside pack\n  DataVector copied_subcell_data = subcell_data;\n  DataVector copied_dg_data = dg_data;\n\n  BoundaryMessage<Dim>* boundary_message = new BoundaryMessage<Dim>(\n      subcell_size, dg_size, owning, enable_if_disabled, sender_node,\n      sender_core, tci_status, integration_order, current_time_id, next_time_id,\n      neighbor_direction, element_id, volume_mesh, interface_mesh,\n      subcell_size != 0 ? subcell_data.data() : nullptr,\n      dg_size != 0 ? dg_data.data() : nullptr);\n  // Since we expect the copied message to have owning = true because that's set\n  // in the pack() function, we set owning = true here\n  BoundaryMessage<Dim>* copied_boundary_message = new BoundaryMessage<Dim>(\n      subcell_size, dg_size, true, enable_if_disabled, sender_node, sender_core,\n      tci_status, integration_order, current_time_id, next_time_id,\n      neighbor_direction, element_id, volume_mesh, interface_mesh,\n      subcell_size != 0 ? copied_subcell_data.data() : nullptr,\n      dg_size != 0 ? copied_dg_data.data() : nullptr);\n\n  CHECK(subcell_data.size() == subcell_size);\n  CHECK(dg_data.size() == dg_size);\n\n  void* packed_message = BoundaryMessage<Dim>::pack(boundary_message);\n\n  BoundaryMessage<Dim>* unpacked_message =\n      BoundaryMessage<Dim>::unpack(packed_message);\n\n  CHECK(unpacked_message->owning);\n  CHECK(*copied_boundary_message == *unpacked_message);\n  CHECK_FALSE(*copied_boundary_message != *unpacked_message);\n\n  BoundaryMessage<Dim>* repacked_unpacked_message =\n      BoundaryMessage<Dim>::unpack(\n          BoundaryMessage<Dim>::pack(unpacked_message));\n\n  // Technically unpacked_message is now invalidated because we went through\n  // pack/unpack, but we are only concerned that the pointers are the same. We\n  // aren't using any data. These should be the same because packing an owning\n  // message doesn't do any new allocations, and the unpack function also\n  // doesn't do any new allocations, so the data shouldn't have moved\n  CHECK(unpacked_message == repacked_unpacked_message);\n}\n\nvoid test_output() {\n  const size_t subcell_size = 4;\n  const size_t dg_size = 3;\n\n  const bool owning = true;\n  const bool enable_if_disabled = false;\n  const size_t sender_node = 2;\n  const size_t sender_core = 15;\n  const int tci_status = -3;\n  const size_t integration_order = 3;\n\n  const Slab current_slab{0.1, 0.5};\n  const Time current_time{current_slab, {0, 1}};\n  const TimeStepId current_time_id{true, 0, current_time};\n  const Slab next_slab{0.5, 0.9};\n  const Time next_time{next_slab, {0, 1}};\n  const TimeStepId next_time_id{true, 0, next_time};\n  const Direction<2> neighbor_direction{0, Side::Upper};\n  const ElementId<2> element_id{0};\n\n  const size_t extents = 4;\n  const Mesh<2> volume_mesh{extents, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> interface_mesh = volume_mesh.slice_away(0);\n\n  DataVector subcell_data{0.1, 0.2, 0.3, 0.4};\n  DataVector dg_data{-0.3, -0.2, -0.1};\n\n  BoundaryMessage<2> message{subcell_size,\n                             dg_size,\n                             owning,\n                             enable_if_disabled,\n                             sender_node,\n                             sender_core,\n                             tci_status,\n                             integration_order,\n                             current_time_id,\n                             next_time_id,\n                             neighbor_direction,\n                             element_id,\n                             volume_mesh,\n                             interface_mesh,\n                             subcell_data.data(),\n                             dg_data.data()};\n\n  const std::string message_str = get_output(message);\n\n  std::stringstream ss;\n  ss << \"subcell_ghost_data_size = 4\\n\"\n     << \"dg_flux_data_size = 3\\n\"\n     << \"owning = true\\n\"\n     << \"enable_if_disabled = false\\n\"\n     << \"sender_node = 2\\n\"\n     << \"sender_core = 15\\n\"\n     << \"tci_status = -3\\n\"\n     << \"integration_order = 3\\n\"\n     // TimeStepIds have complicated output so don't try and hard code it, just\n     // use get_output\n     << \"current_time_ste_id = \" << get_output(current_time_id) << \"\\n\"\n     << \"next_time_ste_id = \" << get_output(next_time_id) << \"\\n\"\n     << \"neighbor_direction = +0\\n\"\n     << \"element_id = [B0,(L0I0,L0I0)]\\n\"\n     << \"volume_or_ghost_mesh = \"\n        \"[(4,4),(Legendre,Legendre),(GaussLobatto,GaussLobatto)]\\n\"\n     << \"interface_mesh = [(4),(Legendre),(GaussLobatto)]\\n\"\n     << \"subcell_ghost_data = (0.1,0.2,0.3,0.4)\\n\"\n     << \"dg_flux_data = (-0.3,-0.2,-0.1)\";\n\n  CHECK(message_str == ss.str());\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.BoundaryMessage\", \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(generator);\n\n  test_output();\n\n  std::uniform_int_distribution<size_t> size_dist{1, 10};\n  tmpl::for_each<tmpl::integral_list<size_t, 1, 2, 3>>(\n      [&generator, &size_dist](auto dim_t) {\n        constexpr size_t Dim =\n            tmpl::type_from<std::decay_t<decltype(dim_t)>>::value;\n        // Only subcell data\n        test_boundary_message<Dim>(make_not_null(&generator),\n                                   size_dist(generator), 0);\n        // Only dg data\n        test_boundary_message<Dim>(make_not_null(&generator), 0,\n                                   size_dist(generator));\n        // Both subcell and dg data\n        test_boundary_message<Dim>(make_not_null(&generator),\n                                   size_dist(generator), size_dist(generator));\n        // Neither subcell nor dg data. This isn't currently a use case, but we\n        // test it for completeness to ensure pack/unpack are doing the correct\n        // thing\n        test_boundary_message<Dim>(make_not_null(&generator), 0, 0);\n      });\n}\n}  // namespace\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Messages/Test_InboxTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg {\nnamespace {\n\ntemplate <size_t Dim>\nBoundaryMessage<Dim>* create_boundary_message(\n    const TimeStepId& current_time_step_id, const TimeStepId& next_time_step_id,\n    const DirectionalId<Dim>& key, const Mesh<Dim>& volume_mesh,\n    const Mesh<Dim - 1>& interface_mesh, std::optional<DataVector>& ghost_data,\n    std::optional<DataVector>& dg_data, const int tci_status,\n    const size_t integration_order) {\n  return new BoundaryMessage<Dim>(\n      ghost_data.value_or(DataVector{}).size(),  // subcell_ghost_data_size\n      dg_data.value_or(DataVector{}).size(),     // dg_flux_data_size\n      true,                                      // owning\n      false,                                     // enable_if_disabled\n      2,                                         // sender_node\n      12,                                        // sender_core\n      tci_status,                                // tci_status\n      integration_order,                         // integration_order\n      current_time_step_id,                      // current_time_step_id\n      next_time_step_id,                         // next_time_step_id\n      key.direction(),                           // neighbor_direction\n      key.id(),                                  // element_id\n      volume_mesh,                               // volume_or_ghost_mesh\n      interface_mesh,                            // interface_mesh\n      ghost_data.has_value() ? ghost_data.value().data()\n                             : nullptr,  // subcell_ghost_data\n      dg_data.has_value() ? dg_data.value().data() : nullptr  // dg_flux_data\n  );\n}\n\ntemplate <typename T>\nconst typename T::value_type::second_type& find(\n    const T& container, const typename T::value_type::first_type& key) {\n  const auto entry = alg::find_if(\n      container,\n      [&](const typename T::value_type& x) { return x.first == key; });\n  REQUIRE(entry != container.end());\n  return entry->second;\n}\n\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nvoid test_no_ghost_cells() {\n  static constexpr size_t number_of_components = 1 + Dim;\n  using bc_tag =\n      Tags::BoundaryCorrectionAndGhostCellsInbox<Dim, UseNodegroupDgElements>;\n  using bm_tag = Tags::BoundaryMessageInbox<Dim>;\n  using BcType = evolution::dg::BoundaryData<Dim>;\n  using BcInbox = typename bc_tag::type;\n  using BmInbox = typename bm_tag::type;\n\n  std::uniform_real_distribution<double> dist(-1.0, 2.3);\n  MAKE_GENERATOR(gen);\n  std::optional<DataVector> nullopt = std::nullopt;\n\n  const TimeStepId time_step_id_a{true, 3, Time{Slab{0.2, 3.4}, {3, 100}}};\n  const TimeStepId time_step_id_b{true, 4, Time{Slab{3.4, 5.4}, {13, 100}}};\n  const TimeStepId time_step_id_c{true, 5, Time{Slab{5.4, 6.4}, {17, 100}}};\n  const DirectionalId<Dim> nhbr_key{Direction<Dim>::lower_xi(),\n                                    ElementId<Dim>{1}};\n\n  BcInbox bc_inbox{};\n  BmInbox bm_inbox{};\n\n  BcType send_data_a{};\n  const Mesh<Dim> volume_mesh_a{5, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim - 1> mesh_a{5, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto};\n  send_data_a.volume_mesh_ghost_cell_data = volume_mesh_a;\n  send_data_a.boundary_correction_data =\n      DataVector{mesh_a.number_of_grid_points() * number_of_components, 0.0};\n  send_data_a.validity_range = time_step_id_a;\n  send_data_a.tci_status = 5;\n  send_data_a.integration_order = 3;\n  fill_with_random_values(\n      make_not_null(&send_data_a.boundary_correction_data.value()),\n      make_not_null(&gen), make_not_null(&dist));\n\n  BoundaryMessage<Dim>* boundary_message_a = create_boundary_message(\n      time_step_id_a, time_step_id_a, nhbr_key, volume_mesh_a, mesh_a, nullopt,\n      send_data_a.boundary_correction_data, send_data_a.tci_status,\n      send_data_a.integration_order);\n  BoundaryMessage<Dim>* boundary_message_a_compare = boundary_message_a;\n\n  bc_tag::insert_into_inbox(make_not_null(&bc_inbox), time_step_id_a,\n                            std::make_pair(nhbr_key, send_data_a));\n  bm_tag::insert_into_inbox(make_not_null(&bm_inbox), boundary_message_a);\n  bc_inbox.collect_messages();\n\n  CHECK((find(bc_inbox.messages.at(time_step_id_a), nhbr_key) == send_data_a));\n  // Check the values, not the pointers\n  CHECK(*(find(bm_inbox.at(time_step_id_a), nhbr_key).get()) ==\n        *boundary_message_a_compare);\n\n  BcType send_data_b{};\n  const Mesh<Dim> volume_mesh_b{7, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim - 1> mesh_b{7, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto};\n  send_data_b.volume_mesh_ghost_cell_data = volume_mesh_b;\n\n  send_data_b.boundary_correction_data =\n      DataVector{mesh_b.number_of_grid_points() * number_of_components, 0.0};\n  // Set the future time step to make sure the implementation doesn't mix the\n  // receive time ID and the validity range time ID\n  send_data_b.validity_range = time_step_id_c;\n  send_data_b.tci_status = 4;\n  send_data_b.integration_order = 2;\n  fill_with_random_values(\n      make_not_null(&send_data_b.boundary_correction_data.value()),\n      make_not_null(&gen), make_not_null(&dist));\n\n  BoundaryMessage<Dim>* boundary_message_b = create_boundary_message(\n      time_step_id_b, time_step_id_c, nhbr_key, volume_mesh_b, mesh_b, nullopt,\n      send_data_b.boundary_correction_data, send_data_b.tci_status,\n      send_data_b.integration_order);\n  BoundaryMessage<Dim>* boundary_message_b_compare = boundary_message_b;\n\n  bc_tag::insert_into_inbox(make_not_null(&bc_inbox), time_step_id_b,\n                            std::make_pair(nhbr_key, send_data_b));\n  bm_tag::insert_into_inbox(make_not_null(&bm_inbox),\n                            boundary_message_b_compare);\n  bc_inbox.collect_messages();\n\n  const std::string inbox_output = bc_tag::output_inbox(bc_inbox, 1_st);\n  const std::string expected_inbox_output =\n      MakeString{} << std::scientific << std::setprecision(16)\n                   << \" BoundaryCorrectionAndGhostCellInbox:\\n\"\n                   << \"  Current time: \" << time_step_id_a << \"\\n\"\n                   << \"   Key: \" << nhbr_key\n                   << \", next time: \" << time_step_id_a << \"\\n\"\n                   << \"  Current time: \" << time_step_id_b << \"\\n\"\n                   << \"   Key: \" << nhbr_key\n                   << \", next time: \" << time_step_id_c << \"\\n\";\n  CHECK(inbox_output == expected_inbox_output);\n\n  CHECK((find(bc_inbox.messages.at(time_step_id_a), nhbr_key) == send_data_a));\n  CHECK((find(bc_inbox.messages.at(time_step_id_b), nhbr_key) == send_data_b));\n  CHECK(*(find(bm_inbox.at(time_step_id_a), nhbr_key).get()) ==\n        *boundary_message_a_compare);\n  CHECK(*(find(bm_inbox.at(time_step_id_b), nhbr_key).get()) ==\n        *boundary_message_b_compare);\n\n  bc_inbox.messages.erase(time_step_id_a);\n  bm_inbox.erase(time_step_id_a);\n  CHECK(bc_inbox.messages.count(time_step_id_a) == 0);\n  CHECK(bm_inbox.count(time_step_id_a) == 0);\n\n  CHECK((find(bc_inbox.messages.at(time_step_id_b), nhbr_key) == send_data_b));\n  CHECK(*(find(bm_inbox.at(time_step_id_b), nhbr_key).get()) ==\n        *boundary_message_b_compare);\n  bc_inbox.messages.erase(time_step_id_b);\n  bm_inbox.erase(time_step_id_b);\n  CHECK(bc_inbox.messages.count(time_step_id_b) == 0);\n  CHECK(bm_inbox.count(time_step_id_b) == 0);\n}\n\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nvoid test_with_ghost_cells() {\n  static constexpr size_t number_of_components = 1 + Dim;\n  using bc_tag =\n      Tags::BoundaryCorrectionAndGhostCellsInbox<Dim, UseNodegroupDgElements>;\n  using bm_tag = Tags::BoundaryMessageInbox<Dim>;\n  using BcType = evolution::dg::BoundaryData<Dim>;\n  using BcInbox = typename bc_tag::type;\n  using BmInbox = typename bm_tag::type;\n\n  std::uniform_real_distribution<double> dist(-1.0, 2.3);\n  MAKE_GENERATOR(gen);\n  std::optional<DataVector> nullopt = std::nullopt;\n\n  const TimeStepId time_step_id_a{true, 3, Time{Slab{0.2, 3.4}, {3, 100}}};\n  const TimeStepId time_step_id_b{true, 4, Time{Slab{3.4, 5.4}, {13, 100}}};\n  const TimeStepId time_step_id_c{true, 5, Time{Slab{5.4, 6.4}, {17, 100}}};\n  const DirectionalId<Dim> nhbr_key{Direction<Dim>::lower_xi(),\n                                    ElementId<Dim>{1}};\n\n  BcInbox bc_inbox{};\n  BmInbox bm_inbox{};\n\n  // Send ghost cells first\n  BcType send_data_a{};\n  const Mesh<Dim> volume_mesh_a{5, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim - 1> mesh_a{5, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto};\n  send_data_a.volume_mesh_ghost_cell_data = volume_mesh_a;\n  send_data_a.ghost_cell_data =\n      DataVector{mesh_a.number_of_grid_points() * number_of_components, 0.0};\n  send_data_a.validity_range = time_step_id_a;\n  send_data_a.tci_status = 5;\n  send_data_a.integration_order = 3;\n  fill_with_random_values(make_not_null(&send_data_a.ghost_cell_data.value()),\n                          make_not_null(&gen), make_not_null(&dist));\n\n  BoundaryMessage<Dim>* boundary_message_a = create_boundary_message(\n      time_step_id_a, time_step_id_a, nhbr_key, volume_mesh_a, mesh_a,\n      send_data_a.ghost_cell_data, nullopt, send_data_a.tci_status,\n      send_data_a.integration_order);\n  BoundaryMessage<Dim>* boundary_message_a_compare = boundary_message_a;\n\n  bc_tag::insert_into_inbox(make_not_null(&bc_inbox), time_step_id_a,\n                            std::make_pair(nhbr_key, send_data_a));\n  bm_tag::insert_into_inbox(make_not_null(&bm_inbox), boundary_message_a);\n  bc_inbox.collect_messages();\n\n  CHECK((find(bc_inbox.messages.at(time_step_id_a), nhbr_key) == send_data_a));\n  // Check the values, not the pointers\n  CHECK(*(find(bm_inbox.at(time_step_id_a), nhbr_key).get()) ==\n        *boundary_message_a_compare);\n\n  BcType send_data_b{};\n  const Mesh<Dim> volume_mesh_b{7, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim - 1> mesh_b{7, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto};\n  send_data_b.volume_mesh_ghost_cell_data = volume_mesh_b;\n  send_data_b.ghost_cell_data =\n      DataVector{mesh_b.number_of_grid_points() * number_of_components, 0.0};\n  send_data_b.validity_range = time_step_id_b;\n  send_data_b.tci_status = 6;\n  send_data_b.integration_order = 4;\n  fill_with_random_values(make_not_null(&send_data_b.ghost_cell_data.value()),\n                          make_not_null(&gen), make_not_null(&dist));\n\n  BoundaryMessage<Dim>* boundary_message_b = create_boundary_message(\n      time_step_id_b, time_step_id_b, nhbr_key, volume_mesh_b, mesh_b,\n      send_data_b.ghost_cell_data, nullopt, send_data_b.tci_status,\n      send_data_b.integration_order);\n  BoundaryMessage<Dim>* boundary_message_b_compare = boundary_message_b;\n\n  bc_tag::insert_into_inbox(make_not_null(&bc_inbox), time_step_id_b,\n                            std::make_pair(nhbr_key, send_data_b));\n  bm_tag::insert_into_inbox(make_not_null(&bm_inbox), boundary_message_b);\n  bc_inbox.collect_messages();\n\n  CHECK((find(bc_inbox.messages.at(time_step_id_a), nhbr_key) == send_data_a));\n  CHECK((find(bc_inbox.messages.at(time_step_id_b), nhbr_key) == send_data_b));\n  CHECK(*(find(bm_inbox.at(time_step_id_a), nhbr_key).get()) ==\n        *boundary_message_a_compare);\n  CHECK(*(find(bm_inbox.at(time_step_id_b), nhbr_key).get()) ==\n        *boundary_message_b_compare);\n\n  bc_inbox.messages.erase(time_step_id_a);\n  bm_inbox.erase(time_step_id_a);\n  CHECK(bc_inbox.messages.count(time_step_id_a) == 0);\n  CHECK(bm_inbox.count(time_step_id_a) == 0);\n\n  CHECK((find(bc_inbox.messages.at(time_step_id_b), nhbr_key) == send_data_b));\n  CHECK(*(find(bm_inbox.at(time_step_id_b), nhbr_key).get()) ==\n        *boundary_message_b_compare);\n  bc_inbox.messages.erase(time_step_id_b);\n  bm_inbox.erase(time_step_id_b);\n  CHECK(bc_inbox.messages.count(time_step_id_b) == 0);\n  CHECK(bm_inbox.count(time_step_id_b) == 0);\n\n  // Check sending both ghost and flux data at once\n  BcType send_all_data_b = send_data_b;\n  send_all_data_b.boundary_correction_data = DataVector{\n      2 * mesh_b.number_of_grid_points() * number_of_components, 0.0};\n  send_all_data_b.validity_range = time_step_id_c;\n  send_data_b.tci_status = 5;\n  send_data_b.integration_order = 3;\n  fill_with_random_values(\n      make_not_null(&send_all_data_b.boundary_correction_data.value()),\n      make_not_null(&gen), make_not_null(&dist));\n\n  BoundaryMessage<Dim>* all_boundary_message_b = create_boundary_message(\n      time_step_id_b, time_step_id_c, nhbr_key, volume_mesh_b, mesh_b,\n      send_all_data_b.ghost_cell_data, send_all_data_b.boundary_correction_data,\n      send_all_data_b.tci_status, send_all_data_b.integration_order);\n  BoundaryMessage<Dim>* all_boundary_message_b_compare = all_boundary_message_b;\n\n  bc_tag::insert_into_inbox(make_not_null(&bc_inbox), time_step_id_b,\n                            std::make_pair(nhbr_key, send_all_data_b));\n  bm_tag::insert_into_inbox(make_not_null(&bm_inbox), all_boundary_message_b);\n  bc_inbox.collect_messages();\n\n  CHECK((find(bc_inbox.messages.at(time_step_id_b), nhbr_key) ==\n         send_all_data_b));\n  CHECK(*(find(bm_inbox.at(time_step_id_b), nhbr_key).get()) ==\n        *all_boundary_message_b_compare);\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  test_no_ghost_cells<Dim, false>();\n  test_with_ghost_cells<Dim, false>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.Messages.InboxTags\", \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_AtomicInboxBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <atomic>\n#include <cstddef>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/AtomicInboxBoundaryData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace evolution::dg {\nnamespace {\nvoid test_3d_helper(const Direction<3>& dir) {\n  constexpr size_t Dim = 3;\n  const size_t offset =\n      (dir.side() == Side::Lower ? 0 : 4) + 8 * dir.dimension();\n  CAPTURE(offset);\n  const auto swap_segments = [&dir](std::array<SegmentId, Dim> segment_ids)\n      -> std::array<SegmentId, Dim> {\n    if (dir.dimension() == 0) {\n      return segment_ids;\n    } else if (dir.dimension() == 1) {\n      return {{segment_ids[1], segment_ids[0], segment_ids[2]}};\n    } else {\n      return {{segment_ids[1], segment_ids[2], segment_ids[0]}};\n    }\n  };\n  for (size_t i = 0; i < 4; ++i) {\n    // Loop over the grid index in the dimension normal to the interface.\n\n    // 4 neighbors\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 0}, {2, 0}}})}}) ==\n          0 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 1}, {2, 0}}})}}) ==\n          1 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 0}, {2, 1}}})}}) ==\n          2 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 1}, {2, 1}}})}}) ==\n          3 + offset);\n\n    // 3 neighbors\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 0}, {2, 0}}})}}) ==\n          0 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 0}, {2, 1}}})}}) ==\n          2 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 1}, {1, 0}}})}}) ==\n          1 + offset);\n\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 0}, {1, 0}}})}}) ==\n          0 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 1}, {2, 0}}})}}) ==\n          1 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 1}, {2, 1}}})}}) ==\n          3 + offset);\n\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 0}, {2, 0}}})}}) ==\n          0 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 1}, {2, 0}}})}}) ==\n          1 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {1, 0}, {2, 1}}})}}) ==\n          2 + offset);\n\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {1, 0}, {2, 0}}})}}) ==\n          0 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 0}, {2, 1}}})}}) ==\n          2 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 1}, {2, 1}}})}}) ==\n          3 + offset);\n\n    // 2 neighbors\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 0}, {1, 0}}})}}) ==\n          0 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {2, 1}, {1, 0}}})}}) ==\n          1 + offset);\n\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {1, 0}, {2, 0}}})}}) ==\n          0 + offset);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {1, 0}, {2, 1}}})}}) ==\n          2 + offset);\n\n    // 1 neighbor\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              dir,\n              ElementId<Dim>{2, swap_segments({{{2, i}, {1, 0}, {1, 0}}})}}) ==\n          0 + offset);\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_serialization() {\n  const auto check_all_empty = [](const AtomicInboxBoundaryData<Dim>& data) {\n    for (size_t i = 0; i < data.boundary_data_in_directions.size(); ++i) {\n      CAPTURE(i);\n      CHECK(gsl::at(data.boundary_data_in_directions, i).empty());\n    }\n  };\n\n  AtomicInboxBoundaryData<Dim> data_works{};\n  data_works.number_of_neighbors = 10;\n  const auto data_works_out = serialize_and_deserialize(data_works);\n  CHECK(data_works_out.number_of_neighbors.load() == 10);\n  CHECK(data_works_out.message_count.load() == 0);\n  check_all_empty(data_works);\n\n  AtomicInboxBoundaryData<Dim> data_has_message_count{};\n  data_has_message_count.message_count = 5;\n  CHECK_THROWS_WITH(serialize_and_deserialize(data_has_message_count),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can only serialize AtomicInboxBoundaryData if there \"\n                        \"are no messages. \"));\n\n  for (size_t i = 0; i < data_works.boundary_data_in_directions.size(); ++i) {\n    AtomicInboxBoundaryData<Dim> data_has_queue{};\n    gsl::at(data_has_queue.boundary_data_in_directions, i).push({});\n    CHECK_THROWS_WITH(\n        serialize_and_deserialize(data_has_queue),\n        Catch::Matchers::ContainsSubstring(\n            \"We can only serialize empty StaticSpscQueues but the queue in \"));\n  }\n\n  std::unordered_map<int, AtomicInboxBoundaryData<Dim>> data_map{};\n  data_map[0] = AtomicInboxBoundaryData<Dim>{};\n  data_map.at(0).number_of_neighbors = 7;\n  const auto data_map_out = serialize_and_deserialize(data_map);\n  CHECK(data_map_out.size() == 1);\n  CHECK(data_map_out.at(0).number_of_neighbors.load(std::memory_order_acquire) =\n            7);\n  CHECK(data_map_out.at(0).message_count.load(std::memory_order_acquire) = 0);\n  check_all_empty(data_map_out.at(0));\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  static_assert(Dim < 4);\n  static_assert(evolution::dg::is_atomic_inbox_boundary_data_v<\n                AtomicInboxBoundaryData<Dim>>);\n  CAPTURE(Dim);\n  if constexpr (Dim == 1) {\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::lower_xi(), ElementId<Dim>{2}}) == 0);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::upper_xi(), ElementId<Dim>{2}}) == 1);\n  } else if constexpr (Dim == 2) {\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::lower_xi(), ElementId<Dim>{2}}) == 0);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::upper_xi(), ElementId<Dim>{2}}) == 2);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::lower_eta(), ElementId<Dim>{2}}) == 4 + 0);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::upper_eta(), ElementId<Dim>{2}}) == 4 + 2);\n\n    for (size_t i = 0; i < 4; ++i) {\n      // Loop over the grid index in the dimension normal to the interface.\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_xi(),\n                ElementId<Dim>{2, {{{2, i}, {1, 0}}}}}) == 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_xi(),\n                ElementId<Dim>{2, {{{2, i}, {2, 0}}}}}) == 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_xi(),\n                ElementId<Dim>{2, {{{2, i}, {2, 1}}}}}) == 1);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_xi(),\n                ElementId<Dim>{2, {{{2, i}, {2, 2}}}}}) == 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_xi(),\n                ElementId<Dim>{2, {{{2, i}, {2, 3}}}}}) == 1);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_xi(),\n                ElementId<Dim>{2, {{{2, i}, {1, 0}}}}}) == 2 + 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_xi(),\n                ElementId<Dim>{2, {{{2, i}, {2, 0}}}}}) == 2 + 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_xi(),\n                ElementId<Dim>{2, {{{2, i}, {2, 1}}}}}) == 2 + 1);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_xi(),\n                ElementId<Dim>{2, {{{2, i}, {2, 2}}}}}) == 2 + 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_xi(),\n                ElementId<Dim>{2, {{{2, i}, {2, 3}}}}}) == 2 + 1);\n\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_eta(),\n                ElementId<Dim>{2, {{{2, 0}, {2, i}}}}}) == 4 + 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_eta(),\n                ElementId<Dim>{2, {{{2, 1}, {2, i}}}}}) == 4 + 1);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_eta(),\n                ElementId<Dim>{2, {{{2, 2}, {2, i}}}}}) == 4 + 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::lower_eta(),\n                ElementId<Dim>{2, {{{2, 3}, {2, i}}}}}) == 4 + 1);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_eta(),\n                ElementId<Dim>{2, {{{2, 0}, {2, i}}}}}) == 4 + 2 + 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_eta(),\n                ElementId<Dim>{2, {{{2, 1}, {2, i}}}}}) == 4 + 2 + 1);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_eta(),\n                ElementId<Dim>{2, {{{2, 2}, {2, i}}}}}) == 4 + 2 + 0);\n      CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n                Direction<Dim>::upper_eta(),\n                ElementId<Dim>{2, {{{2, 3}, {2, i}}}}}) == 4 + 2 + 1);\n    }\n  } else {\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::lower_xi(), ElementId<Dim>{2}}) == 0);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::upper_xi(), ElementId<Dim>{2}}) == 4);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::lower_eta(), ElementId<Dim>{2}}) == 8 + 0);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::upper_eta(), ElementId<Dim>{2}}) == 8 + 4);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::lower_zeta(), ElementId<Dim>{2}}) == 16 + 0);\n    CHECK(AtomicInboxBoundaryData<Dim>::index(DirectionalId<Dim>{\n              Direction<Dim>::upper_zeta(), ElementId<Dim>{2}}) == 16 + 4);\n\n    test_3d_helper(Direction<Dim>::lower_xi());\n    test_3d_helper(Direction<Dim>::upper_xi());\n    test_3d_helper(Direction<Dim>::lower_eta());\n    test_3d_helper(Direction<Dim>::upper_eta());\n    test_3d_helper(Direction<Dim>::lower_zeta());\n    test_3d_helper(Direction<Dim>::upper_zeta());\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.AtomicInboxBoundaryData\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_BackgroundGrVars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BackgroundGrVars.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct MetavariablesForTest {\n  using component_list = tmpl::list<>;\n  using initial_data_list = tmpl::list<RelativisticEuler::Solutions::TovStar>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>>;\n  };\n};\n\nstruct SystemForTest {\n  static constexpr size_t volume_dim = 3;\n\n  // A disparate set of GR variables were chosen here to make sure that the\n  // action allocates and assigns metric variables without missing any tags\n  using spacetime_variables_tag = ::Tags::Variables<\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>>>;\n  using flux_spacetime_variables_tag =\n      ::Tags::Variables<tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                                   gr::Tags::SpatialMetric<DataVector, 3>>>;\n  using inverse_spatial_metric_tag =\n      gr::Tags::InverseSpatialMetric<DataVector, 3>;\n};\n\n// A free function returning a moving brick domain\ntemplate <bool MeshIsMoving>\ndomain::creators::Brick create_a_brick(const size_t num_dg_pts,\n                                       const double initial_time) {\n  auto time_dependence_ptr = [&]() {\n    if constexpr (MeshIsMoving) {\n      const std::array<double, 3> mesh_velocity{1, 2, 3};\n      return std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          initial_time, mesh_velocity);\n    } else {\n      return nullptr;\n    }\n  }();\n  const auto lower_bounds = make_array<3, double>(3.0);\n  const auto upper_bounds = make_array<3, double>(5.0);\n  const auto refinement_levels = make_array<3, size_t>(0);\n  return domain::creators::Brick(lower_bounds, upper_bounds, refinement_levels,\n                                 make_array<3, size_t>(num_dg_pts),\n                                 make_array<3, bool>(true), {},\n                                 std::move(time_dependence_ptr));\n}\n\ntemplate <bool TestMovingMesh>\nvoid test(const gsl::not_null<std::mt19937*> gen) {\n  // The test is done as follows :\n  //\n  // - Create a 3D element (brick) for the test. If `TestMovingMesh` ==\n  //    `true`, the coordinate map of the brick is set to be time-dependent.\n  // - Use Kerr-Schild or TOV solution as the background metric, depending on\n  //   the type (compile or runtime) of initial data to test.\n  // - Create a box for running the `BackgroundGrVars` mutator.\n  // - Run the mutator at the initial time, check results\n  // - Change inertial coordinates and time to a random later moment, and test\n  //    the mutator again.\n  //\n\n  const double initial_time = 0.5;\n  // make the random time strictly different from the initial time\n  std::uniform_real_distribution<> distribution_time(1.0, 2.0);\n  const double random_time{\n      make_with_random_values<double>(gen, make_not_null(&distribution_time))};\n\n  // Create a 3D element [3.0, 5.0]^3  for the test\n  const size_t num_dg_pts = 5;\n  const auto brick = [&]() {\n    if constexpr (TestMovingMesh) {\n      return create_a_brick<true>(num_dg_pts, initial_time);\n    } else {\n      return create_a_brick<false>(num_dg_pts, initial_time);\n    }\n  }();\n  const auto domain = brick.create_domain();\n  const auto element_id = ElementId<3>{0};\n  Element<3> element = domain::create_initial_element(\n      element_id, domain.blocks(),\n      std::vector<std::array<size_t, 3>>{{0, 0, 0}});\n\n  const Mesh<3> mesh{num_dg_pts, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  const auto compute_inertial_coords = [&brick, &domain, &element_id,\n                                        &mesh](const double time) {\n    const auto& block = domain.blocks()[element_id.block_id()];\n    const auto element_map = ElementMap<3, Frame::Grid>{\n        element_id, block.is_time_dependent()\n                        ? block.moving_mesh_logical_to_grid_map().get_clone()\n                        : block.stationary_map().get_to_grid_frame()};\n    std::unique_ptr<\n        ::domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>\n        grid_to_inertial_map;\n    if (block.is_time_dependent()) {\n      grid_to_inertial_map =\n          block.moving_mesh_grid_to_inertial_map().get_clone();\n    } else {\n      grid_to_inertial_map =\n          ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n              ::domain::CoordinateMaps::Identity<3>{});\n    }\n    return (*grid_to_inertial_map)(element_map(logical_coordinates(mesh)), time,\n                                   brick.functions_of_time());\n  };\n\n  const auto initial_inertial_coords = compute_inertial_coords(initial_time);\n\n  using gr_variables_tag =\n      ::Tags::Variables<tmpl::remove_duplicates<tmpl::append<\n          typename SystemForTest::spacetime_variables_tag::tags_list,\n          typename SystemForTest::flux_spacetime_variables_tag::tags_list,\n          tmpl::list<typename SystemForTest::inverse_spatial_metric_tag>>>>;\n\n  const auto solution = []() {\n    return RelativisticEuler::Solutions::TovStar{\n        1.0e-3, EquationsOfState::PolytropicFluid<true>{100.0, 2.0}.get_clone(),\n        RelativisticEuler::Solutions::TovCoordinates::Schwarzschild};\n  }();\n\n  // Note the argument `gr_variables_tag` when creating a box. Since we want to\n  // test that the dg::BackgroundGrVars mutator properly initializes (allocate +\n  // assign) the background GR variables, use an empty Variables object here for\n  // creation.\n  auto box = [&initial_time, &brick, &element, &mesh, &initial_inertial_coords,\n              &solution]() {\n    return db::create<db::AddSimpleTags<\n        ::Tags::Time, domain::Tags::Domain<3>, domain::Tags::Element<3>,\n        domain::Tags::Mesh<3>, domain::Tags::Coordinates<3, Frame::Inertial>,\n        gr_variables_tag, evolution::initial_data::Tags::InitialData>>(\n        initial_time, brick.create_domain(), element, mesh,\n        initial_inertial_coords, typename gr_variables_tag::type{},\n        solution.get_clone());\n  }();\n\n  // Apply the mutator for initialization phase, and check that it has put\n  // correct values of GR variables in the box.\n  db::mutate_apply<\n      evolution::dg::BackgroundGrVars<SystemForTest, MetavariablesForTest>>(\n      make_not_null(&box));\n\n  const auto expected_initial_gr_vars = solution.variables(\n      initial_inertial_coords, initial_time, gr_variables_tag::tags_list{});\n  tmpl::for_each<gr_variables_tag::tags_list>(\n      [&box, &expected_initial_gr_vars](const auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const auto& gr_vars_in_box = get<gr_variables_tag>(box);\n        CHECK_ITERABLE_APPROX(get<tag>(expected_initial_gr_vars),\n                              get<tag>(gr_vars_in_box));\n      });\n\n  // Mutate time and inertial coords to those at t = `random_time` and apply the\n  // mutator again.. Then check that the mutator has evaluated correct values of\n  // GR variables at a later random time.\n  const auto inertial_coords = compute_inertial_coords(random_time);\n  db::mutate<::Tags::Time, domain::Tags::Coordinates<3, Frame::Inertial>>(\n      [&random_time, &inertial_coords](const auto time_ptr,\n                                       const auto inertial_coords_ptr) {\n        *time_ptr = random_time;\n        *inertial_coords_ptr = inertial_coords;\n      },\n      make_not_null(&box));\n\n  db::mutate_apply<\n      evolution::dg::BackgroundGrVars<SystemForTest, MetavariablesForTest>>(\n      make_not_null(&box));\n\n  if constexpr (TestMovingMesh) {\n    const auto expected_gr_vars = solution.variables(\n        inertial_coords, random_time, gr_variables_tag::tags_list{});\n    tmpl::for_each<gr_variables_tag::tags_list>(\n        [&box, &expected_gr_vars](const auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          const auto& gr_vars_in_box = get<gr_variables_tag>(box);\n          CHECK_ITERABLE_APPROX(get<tag>(expected_gr_vars),\n                                get<tag>(gr_vars_in_box));\n        });\n  } else {\n    tmpl::for_each<gr_variables_tag::tags_list>(\n        [&box, &expected_initial_gr_vars](const auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          const auto& gr_vars_in_box = get<gr_variables_tag>(box);\n          CHECK_ITERABLE_APPROX(get<tag>(expected_initial_gr_vars),\n                                get<tag>(gr_vars_in_box));\n        });\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.BackgroundGrVars\", \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n\n  test<false>(make_not_null(&gen));\n  test<false>(make_not_null(&gen));\n  test<true>(make_not_null(&gen));\n  test<true>(make_not_null(&gen));\n}\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_BoundaryCorrectionsHelper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct VolumeDouble {\n  double value;\n};\n\nstruct VolumeDoubleConversion {\n  // convert to a std::array to test non-trivial type conversion\n  using unpacked_container = std::array<double, 1>;\n  using packed_container = VolumeDouble;\n  using packed_type = double;\n\n  static inline unpacked_container unpack(const packed_container packed,\n                                          const size_t /*grid_point_index*/) {\n    return {{packed.value}};\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container unpacked,\n                          const size_t /*grid_point_index*/) {\n    packed->value = unpacked[0];\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nnamespace Tags {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <typename Type>\nstruct VolumeDouble : db::SimpleTag {\n  using type = Type;\n};\n\ntemplate <size_t Dim>\nstruct InverseSpatialMetric : db::SimpleTag {\n  using type = tnsr::II<DataVector, Dim, Frame::Inertial>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim, bool IncludeTypeAlias>\nstruct InverseSpatialMetric {};\n\ntemplate <size_t Dim>\nstruct InverseSpatialMetric<Dim, true> {\n  using inverse_spatial_metric_tag = Tags::InverseSpatialMetric<Dim>;\n};\n\ntemplate <size_t Dim, bool CurvedBackground>\nstruct System : public InverseSpatialMetric<Dim, CurvedBackground> {\n  static constexpr bool is_in_flux_conservative_form = true;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = Dim;\n\n  using variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::Var1, Tags::Var2<Dim>>>;\n  using flux_variables = tmpl::list<Tags::Var1, Tags::Var2<Dim>>;\n  using gradient_variables = tmpl::list<>;\n  using sourced_variables = tmpl::list<>;\n\n  struct TimeDerivativeTerms {\n    using temporary_tags = tmpl::list<>;\n  };\n\n  using compute_volume_time_derivative_terms = TimeDerivativeTerms;\n};\n\nstruct CorrectionBase : public PUP::able {\n  CorrectionBase() = default;\n  CorrectionBase(const CorrectionBase&) = default;\n  CorrectionBase& operator=(const CorrectionBase&) = default;\n  CorrectionBase(CorrectionBase&&) = default;\n  CorrectionBase& operator=(CorrectionBase&&) = default;\n  ~CorrectionBase() override = default;\n\n  explicit CorrectionBase(CkMigrateMessage* msg) : PUP::able(msg) {}\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_abstract(CorrectionBase);  // NOLINT\n#pragma GCC diagnostic pop\n\n  virtual std::unique_ptr<CorrectionBase> get_clone() const = 0;\n};\n\ntemplate <size_t Dim, typename VolumeDoubleType>\nstruct Correction final : public CorrectionBase {\n private:\n  struct AbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n public:\n  using dg_package_field_tags =\n      tmpl::list<Tags::Var1, ::Tags::NormalDotFlux<Tags::Var1>, Tags::Var2<Dim>,\n                 ::Tags::NormalDotFlux<Tags::Var2<Dim>>, AbsCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<>;\n  using dg_package_data_volume_tags =\n      tmpl::list<Tags::VolumeDouble<VolumeDoubleType>>;\n  using dg_boundary_terms_volume_tags =\n      tmpl::list<Tags::VolumeDouble<VolumeDoubleType>>;\n\n  Correction() = default;\n  Correction(const Correction&) = default;\n  Correction& operator=(const Correction&) = default;\n  Correction(Correction&&) = default;\n  Correction& operator=(Correction&&) = default;\n  ~Correction() override = default;\n\n  std::unique_ptr<CorrectionBase> get_clone() const override {\n    return std::make_unique<Correction>(*this);\n  }\n\n  explicit Correction(CkMigrateMessage* msg) : CorrectionBase(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(Correction);  // NOLINT\n  void pup(PUP::er& p) override { CorrectionBase::pup(p); }\n\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> packaged_var1,\n      const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_var2,\n      const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n      const Scalar<DataVector>& var1,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& var2,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n      const tnsr::Ij<DataVector, Dim, Frame::Inertial>& flux_var2,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*mesh_velocity*/,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const VolumeDoubleType volume_double_in) const {\n    double volume_double = 0.0;\n    if constexpr (std::is_same_v<double, VolumeDoubleType>) {\n      volume_double = volume_double_in;\n    } else {\n      volume_double = volume_double_in.value;\n    }\n    *packaged_var1 = var1;\n    *packaged_var2 = var2;\n    dot_product(packaged_normal_dot_flux_var1, flux_var1, normal_covector);\n    for (size_t i = 0; i < Dim; ++i) {\n      packaged_normal_dot_flux_var2->get(i) =\n          flux_var2.get(i, 0) * get<0>(normal_covector);\n      for (size_t j = 1; j < Dim; ++j) {\n        packaged_normal_dot_flux_var2->get(i) +=\n            flux_var2.get(i, j) * normal_covector.get(j);\n      }\n    }\n\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*packaged_abs_char_speed) =\n          abs(volume_double * get(var1) - get(*normal_dot_mesh_velocity));\n    } else {\n      get(*packaged_abs_char_speed) = abs(volume_double * get(var1));\n    }\n    return max(get(*packaged_abs_char_speed));\n  }\n\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> packaged_var1,\n      const gsl::not_null<Scalar<DataVector>*> packaged_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_var2,\n      const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          packaged_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> packaged_abs_char_speed,\n      const Scalar<DataVector>& var1,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& var2,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n      const tnsr::Ij<DataVector, Dim, Frame::Inertial>& flux_var2,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const VolumeDoubleType volume_double_in) const {\n    const double max_speed = dg_package_data(\n        packaged_var1, packaged_normal_dot_flux_var1, packaged_var2,\n        packaged_normal_dot_flux_var2, packaged_abs_char_speed, var1, var2,\n        flux_var1, flux_var2, normal_covector, mesh_velocity,\n        normal_dot_mesh_velocity, volume_double_in);\n\n    // We add the normal vector to the flux just to verify that it is being\n    // used. This is total nonsense in terms of physics.\n    for (size_t i = 0; i < Dim; ++i) {\n      packaged_normal_dot_flux_var2->get(i) += normal_vector.get(i);\n    }\n    return max_speed;\n  }\n\n  void dg_boundary_terms(\n      const gsl::not_null<Scalar<DataVector>*> boundary_correction_var1,\n      const gsl::not_null<tnsr::i<DataVector, Dim, Frame::Inertial>*>\n          boundary_correction_var2,\n      const Scalar<DataVector>& var1_int,\n      const Scalar<DataVector>& normal_dot_flux_var1_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& var2_int,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_dot_flux_var2_int,\n      const Scalar<DataVector>& abs_char_speed_int,\n      const Scalar<DataVector>& var1_ext,\n      const Scalar<DataVector>& normal_dot_flux_var1_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& var2_ext,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_dot_flux_var2_ext,\n      const Scalar<DataVector>& abs_char_speed_ext,\n      const dg::Formulation dg_formulation,\n      const VolumeDoubleType volume_double_in) const {\n    // The below code is a Rusanov solver.\n    if (dg_formulation == dg::Formulation::WeakInertial) {\n      get(*boundary_correction_var1) =\n          0.5 *\n              (get(normal_dot_flux_var1_int) - get(normal_dot_flux_var1_ext)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (get(var1_ext) - get(var1_int));\n      for (size_t i = 0; i < Dim; ++i) {\n        boundary_correction_var2->get(i) =\n            0.5 * (normal_dot_flux_var2_int.get(i) -\n                   normal_dot_flux_var2_ext.get(i)) -\n            0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n                (var2_ext.get(i) - var2_int.get(i));\n      }\n    } else {\n      get(*boundary_correction_var1) =\n          -0.5 *\n              (get(normal_dot_flux_var1_int) + get(normal_dot_flux_var1_ext)) -\n          0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n              (get(var1_ext) - get(var1_int));\n      for (size_t i = 0; i < Dim; ++i) {\n        boundary_correction_var2->get(i) =\n            -0.5 * (normal_dot_flux_var2_int.get(i) +\n                    normal_dot_flux_var2_ext.get(i)) -\n            0.5 * max(get(abs_char_speed_int), get(abs_char_speed_ext)) *\n                (var2_ext.get(i) - var2_int.get(i));\n      }\n    }\n    if constexpr (std::is_same_v<double, VolumeDoubleType>) {\n      CHECK(volume_double_in == 2.3);\n    } else {\n      CHECK(volume_double_in.value == 2.3);\n    }\n  }\n};\n\ntemplate <size_t Dim, typename VolumeDoubleType>\nPUP::able::PUP_ID Correction<Dim, VolumeDoubleType>::my_PUP_ID = 0;\n\ntemplate <size_t Dim, bool CurvedBackground, typename VolumeDoubleType>\nvoid test_impl(const gsl::not_null<std::mt19937*> gen) {\n  PUPable_reg(SINGLE_ARG(Correction<Dim, VolumeDoubleType>));\n  const Correction<Dim, VolumeDoubleType> correction{};\n  const Mesh<Dim - 1> face_mesh{Dim * Dim, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::Gauss};\n\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<\n      System<Dim, CurvedBackground>>(\n      gen, correction, face_mesh,\n      tuples::TaggedTuple<Tags::VolumeDouble<VolumeDoubleType>>{\n          VolumeDoubleType{2.3}},\n      tuples::TaggedTuple<>{});\n\n  const std::string curved_suffix =\n      CurvedBackground ? std::string{\"_curved\"} : std::string{\"\"};\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      System<Dim, CurvedBackground>, tmpl::list<VolumeDoubleConversion>>(\n      gen, \"BoundaryCorrectionsHelper\", \"dg_package_data\" + curved_suffix,\n      \"dg_boundary_terms\", correction, face_mesh,\n      tuples::TaggedTuple<Tags::VolumeDouble<VolumeDoubleType>>{\n          VolumeDoubleType{2.3}},\n      tuples::TaggedTuple<>{});\n}\n\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen) {\n  test_impl<Dim, false, double>(gen);\n  test_impl<Dim, false, VolumeDouble>(gen);\n\n  test_impl<Dim, true, double>(gen);\n  test_impl<Dim, true, VolumeDouble>(gen);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.BoundaryCorrectionsHelper\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/DiscontinuousGalerkin/\"};\n  MAKE_GENERATOR(gen);\n\n  test<1>(make_not_null(&gen));\n  test<2>(make_not_null(&gen));\n  test<3>(make_not_null(&gen));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_BoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/BoundaryData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InterpolatedBoundaryData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace evolution::dg {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  const Mesh<Dim> volume_mesh{5, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::Gauss};\n  const Mesh<Dim> ghost_data_mesh{9, Spectral::Basis::FiniteDifference,\n                                  Spectral::Quadrature::CellCentered};\n  const Mesh<Dim - 1> mortar_mesh{6, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::Gauss};\n  const Time time{{0.0, 1.0}, {0, 1}};\n  const BoundaryData<Dim> data0{volume_mesh,\n                                ghost_data_mesh,\n                                mortar_mesh,\n                                DataVector{2, 2.3},\n                                DataVector{1, 4.4},\n                                TimeStepId{true, 1, time},\n                                7,\n                                3,\n                                std::nullopt};\n  CHECK(data0 == BoundaryData<Dim>{volume_mesh, ghost_data_mesh, mortar_mesh,\n                                   DataVector{2, 2.3}, DataVector{1, 4.4},\n                                   TimeStepId{true, 1, time}, 7, 3,\n                                   std::nullopt});\n  CHECK(\n      data0 !=\n      BoundaryData<Dim>{\n          Mesh<Dim>{6, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n          ghost_data_mesh, mortar_mesh, DataVector{2, 2.3}, DataVector{1, 4.4},\n          TimeStepId{true, 1, time}, 7, 3, std::nullopt});\n  CHECK(data0 !=\n        BoundaryData<Dim>{volume_mesh,\n                          Mesh<Dim>{11, Spectral::Basis::FiniteDifference,\n                                    Spectral::Quadrature::CellCentered},\n                          mortar_mesh, DataVector{2, 2.3}, DataVector{1, 4.4},\n                          TimeStepId{true, 1, time}, 7, 3, std::nullopt});\n  if constexpr (Dim > 1) {\n    CHECK(data0 != BoundaryData<Dim>{volume_mesh, ghost_data_mesh,\n                                     Mesh<Dim - 1>{2, Spectral::Basis::Legendre,\n                                                   Spectral::Quadrature::Gauss},\n                                     DataVector{2, 2.3}, DataVector{1, 4.4},\n                                     TimeStepId{true, 1, time}, 7, 3,\n                                     std::nullopt});\n  }\n  CHECK(data0 != BoundaryData<Dim>{volume_mesh, ghost_data_mesh, mortar_mesh,\n                                   DataVector{9, 2.3}, DataVector{1, 4.4},\n                                   TimeStepId{true, 1, time}, 7, 3,\n                                   std::nullopt});\n  CHECK(data0 != BoundaryData<Dim>{volume_mesh, ghost_data_mesh, mortar_mesh,\n                                   DataVector{2, 2.3}, DataVector{6, 4.4},\n                                   TimeStepId{true, 1, time}, 7, 3,\n                                   std::nullopt});\n  CHECK(data0 != BoundaryData<Dim>{volume_mesh, ghost_data_mesh, mortar_mesh,\n                                   DataVector{2, 2.3}, DataVector{1, 4.4},\n                                   TimeStepId{true, 2, time}, 7, 3,\n                                   std::nullopt});\n  CHECK(data0 != BoundaryData<Dim>{volume_mesh, ghost_data_mesh, mortar_mesh,\n                                   DataVector{2, 2.3}, DataVector{1, 4.4},\n                                   TimeStepId{true, 1, time}, 9, 3,\n                                   std::nullopt});\n  CHECK(data0 != BoundaryData<Dim>{volume_mesh, ghost_data_mesh, mortar_mesh,\n                                   DataVector{2, 2.3}, DataVector{1, 4.4},\n                                   TimeStepId{true, 2, time}, 7, 5,\n                                   std::nullopt});\n  CHECK(data0 !=\n        BoundaryData<Dim>{volume_mesh, ghost_data_mesh, mortar_mesh,\n                          DataVector{2, 2.3}, DataVector{1, 4.4},\n                          TimeStepId{true, 1, time}, 7, 3,\n                          InterpolatedBoundaryData<Dim>{\n                              {.data = DataVector{3, 1.0},\n                               .target_mesh = mortar_mesh,\n                               .offsets = std::vector{0_st, 2_st, 3_st}}}});\n  CHECK(get_output(data0) ==\n        std::string(\n            \"Volume mesh: \" + get_output(volume_mesh) +\n            \"\\nGhost mesh: \" + get_output(ghost_data_mesh) +\n            \"\\nBoundary correction mesh: \" + get_output(mortar_mesh) +\n            \"\\nGhost cell data: \" + get_output(DataVector{2, 2.3}) +\n            \"\\nBoundary correction data: \" + get_output(DataVector{1, 4.4}) +\n            \"\\nValidy range: \" + get_output(TimeStepId{true, 1, time}) +\n            \"\\nTCI status: 7\\nIntegration order: 3\\nInterpolated boundary \"\n            \"data: --\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.BoundaryData\", \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_CleanMortarHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/CleanMortarHistory.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.CleanMortarHistory\", \"[Unit][Evolution]\") {\n  const Slab slab(1., 3.);\n\n  TimeSteppers::BoundaryHistory<evolution::dg::MortarData<2>,\n                                evolution::dg::MortarData<2>, DataVector>\n      boundary_history{};\n  boundary_history.local().insert(TimeStepId(true, 0, slab.start()), 2, {});\n  boundary_history.local().insert(TimeStepId(true, 0, slab.end()), 2, {});\n  boundary_history.remote().insert(TimeStepId(true, 0, slab.start()), 2, {});\n  boundary_history.remote().insert(TimeStepId(true, 0, slab.end()), 2, {});\n  evolution::dg::Tags::MortarDataHistory<2>::type mortar_histories{};\n  evolution::dg::Tags::MortarInfo<2>::type mortar_infos{};\n  const std::array lts_mortars{\n      DirectionalId<2>{{Direction<2>::Axis::Xi, Side::Lower}, ElementId<2>{}},\n      DirectionalId<2>{{Direction<2>::Axis::Xi, Side::Upper}, ElementId<2>{}}};\n  const std::array gts_mortars{\n      DirectionalId<2>{{Direction<2>::Axis::Eta, Side::Lower}, ElementId<2>{}}};\n  for (const auto& mortar : lts_mortars) {\n    mortar_infos[mortar].time_stepping_policy() =\n        evolution::dg::TimeSteppingPolicy::Conservative;\n    mortar_histories.emplace(mortar, boundary_history);\n  }\n  for (const auto& mortar : gts_mortars) {\n    mortar_infos[mortar].time_stepping_policy() =\n        evolution::dg::TimeSteppingPolicy::EqualRate;\n    mortar_histories[mortar];\n  }\n\n  auto box =\n      db::create<db::AddSimpleTags<Tags::ConcreteTimeStepper<LtsTimeStepper>,\n                                   evolution::dg::Tags::MortarDataHistory<2>,\n                                   evolution::dg::Tags::MortarInfo<2>>,\n                 time_stepper_ref_tags<LtsTimeStepper>>(\n          static_cast<std::unique_ptr<LtsTimeStepper>>(\n              std::make_unique<TimeSteppers::AdamsBashforth>(2)),\n          std::move(mortar_histories), std::move(mortar_infos));\n\n  db::mutate_apply<evolution::dg::CleanMortarHistory<2>>(make_not_null(&box));\n\n  for (const auto& mortar : lts_mortars) {\n    CHECK(db::get<evolution::dg::Tags::MortarDataHistory<2>>(box)\n              .at(mortar)\n              .local()\n              .size() == 1);\n  }\n  for (const auto& mortar : gts_mortars) {\n    CHECK(db::get<evolution::dg::Tags::MortarDataHistory<2>>(box)\n              .at(mortar)\n              .local()\n              .empty());\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_InboxTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <unordered_set>\n#include <utility>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InboxTags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n\nnamespace {\nenum class SendType { GhostData, DgData, AllData };\n\ntemplate <size_t Dim>\nevolution::dg::BoundaryData<Dim> make_boundary_data(const int label,\n                                                    const TimeStepId& next_step,\n                                                    const SendType type) {\n  return {Mesh<Dim>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n          (type != SendType::DgData)\n              ? std::optional{Mesh<Dim>{3, Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::Gauss}}\n              : std::nullopt,\n          (type != SendType::GhostData)\n              ? std::optional{Mesh<Dim - 1>{5, Spectral::Basis::Legendre,\n                                            Spectral::Quadrature::Gauss}}\n              : std::nullopt,\n          (type == SendType::GhostData or type == SendType::AllData)\n              ? std::optional{DataVector{static_cast<double>(label)}}\n              : std::nullopt,\n          (type != SendType::GhostData)\n              ? std::optional{DataVector{static_cast<double>(-label)}}\n              : std::nullopt,\n          next_step,\n          0,\n          3,\n          std::nullopt};\n}\n\ntemplate <size_t Dim, bool UseNodegroupDgElements>\nvoid test() {\n  CAPTURE(Dim);\n  CAPTURE(UseNodegroupDgElements);\n\n  const Slab slab(1.2, 3.4);\n  const TimeStepId time_step_1(true, 5, slab.start());\n  const TimeStepId time_step_2 =\n      time_step_1.next_substep(slab.duration() / 2, 0.3);\n  const TimeStepId time_step_3 = time_step_2.next_step(slab.duration() / 2);\n  const TimeStepId time_step_4 =\n      time_step_3.next_substep(slab.duration() / 2, 0.3);\n\n  const ElementId<Dim> element_upper(2);\n  const ElementId<Dim> element_lower(4);\n  const DirectionalId<Dim> mortar_upper{Direction<Dim>::upper_xi(),\n                                        element_upper};\n  const DirectionalId<Dim> mortar_lower{Direction<Dim>::lower_xi(),\n                                        element_lower};\n\n  using Inbox = evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n      Dim, UseNodegroupDgElements>;\n\n  typename Inbox::type inbox{};\n\n  const auto data_upper_1 =\n      make_boundary_data<Dim>(0, time_step_2, SendType::AllData);\n  CHECK(not Inbox::insert_into_inbox(&inbox, time_step_1,\n                                     std::pair{mortar_upper, data_upper_1}));\n  inbox.collect_messages();\n  const auto data_lower_2 =\n      make_boundary_data<Dim>(1, time_step_3, SendType::DgData);\n  CHECK(not Inbox::insert_into_inbox(&inbox, time_step_2,\n                                     std::pair{mortar_lower, data_lower_2}));\n\n  CHECK(not inbox.set_missing_messages(3));\n\n  const auto data_lower_3 =\n      make_boundary_data<Dim>(2, time_step_4, SendType::DgData);\n  CHECK(not Inbox::insert_into_inbox(&inbox, time_step_3,\n                                     std::pair{mortar_lower, data_lower_3}));\n  const auto data_lower_1 =\n      make_boundary_data<Dim>(3, time_step_2, SendType::DgData);\n  CHECK(Inbox::insert_into_inbox(&inbox, time_step_1,\n                                 std::pair{mortar_lower, data_lower_1}));\n  const auto data_upper_3 =\n      make_boundary_data<Dim>(4, time_step_4, SendType::GhostData);\n  CHECK(not Inbox::insert_into_inbox(&inbox, time_step_3,\n                                     std::pair{mortar_upper, data_upper_3}));\n\n  inbox.collect_messages();\n\n  CHECK(inbox.messages.size() == 3);\n  {\n    const auto& time1_messages = inbox.messages.at(time_step_1);\n    CHECK(time1_messages.size() == 2);\n    CHECK(alg::find(time1_messages, std::pair{mortar_upper, data_upper_1}) !=\n          time1_messages.end());\n    CHECK(alg::find(time1_messages, std::pair{mortar_lower, data_lower_1}) !=\n          time1_messages.end());\n  }\n  {\n    const auto& time2_messages = inbox.messages.at(time_step_2);\n    CHECK(time2_messages.size() == 1);\n    CHECK(alg::find(time2_messages, std::pair{mortar_lower, data_lower_2}) !=\n          time2_messages.end());\n  }\n  {\n    const auto& time3_messages = inbox.messages.at(time_step_3);\n    CHECK(time3_messages.size() == 2);\n    CHECK(alg::find(time3_messages, std::pair{mortar_upper, data_upper_3}) !=\n          time3_messages.end());\n    CHECK(alg::find(time3_messages, std::pair{mortar_lower, data_lower_3}) !=\n          time3_messages.end());\n  }\n\n  const auto data_upper_2 =\n      make_boundary_data<Dim>(5, time_step_2, SendType::GhostData);\n  CHECK(not Inbox::insert_into_inbox(&inbox, time_step_2,\n                                     std::pair{mortar_upper, data_upper_3}));\n\n  CHECK(inbox.set_missing_messages(1));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.InboxTags\", \"[Unit][Evolution]\") {\n  test<1, false>();\n  test<2, false>();\n  test<3, false>();\n  test<1, true>();\n  test<2, true>();\n  test<3, true>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_InterfaceDataPolicy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/DiscontinuousGalerkin/InterfaceDataPolicy.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.InterfaceDataPolicy\",\n                  \"[Unit][Evolution]\") {\n  CHECK(get_output(evolution::dg::InterfaceDataPolicy::Uninitialized) ==\n        \"Uninitialized\");\n  CHECK(get_output(evolution::dg::InterfaceDataPolicy::CopyProject) ==\n        \"CopyProject\");\n  CHECK(get_output(evolution::dg::InterfaceDataPolicy::OrientCopyProject) ==\n        \"OrientCopyProject\");\n  CHECK(get_output(\n            evolution::dg::InterfaceDataPolicy::NonconformingBothInterpolate) ==\n        \"NonconformingBothInterpolate\");\n  CHECK(\n      get_output(\n          evolution::dg::InterfaceDataPolicy::NonconformingSelfInterpolates) ==\n      \"NonconformingSelfInterpolates\");\n  CHECK(get_output(evolution::dg::InterfaceDataPolicy::\n                       NonconformingNeighborInterpolates) ==\n        \"NonconformingNeighborInterpolates\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_InterpolatedBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InterpolatedBoundaryData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace evolution::dg {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  const DataVector interpolated_data{3, 1.0};\n  const Mesh<Dim - 1> mesh{3_st, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::Gauss};\n  const std::vector<size_t> offsets{0_st, 2_st, 3_st};\n  const InterpolatedBoundaryData<Dim> interpolated_boundary_data{\n      {.data = interpolated_data, .target_mesh = mesh, .offsets = offsets}};\n  CHECK(interpolated_boundary_data.boundary_data() == interpolated_data);\n  CHECK(interpolated_boundary_data.target_mesh() == mesh);\n  CHECK(interpolated_boundary_data.offsets() == offsets);\n  CHECK(get_output(interpolated_boundary_data) ==\n        std::string(\"boundary data = \" + get_output(interpolated_data) +\n                    \"\\ntarget mesh = \" + get_output(mesh) +\n                    \"\\noffsets = \" + get_output(offsets)));\n  const auto deserialized_interpolated_boundary_data =\n      serialize_and_deserialize(interpolated_boundary_data);\n  CHECK(interpolated_boundary_data == deserialized_interpolated_boundary_data);\n  const DataVector interpolated_data_2{3, 1.5};\n  const Mesh<Dim - 1> mesh_2{4_st, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::Gauss};\n  const std::vector<size_t> offsets_2{2_st, 5_st, 7_st};\n  const InterpolatedBoundaryData<Dim> interpolated_boundary_data_2{\n      {.data = interpolated_data_2, .target_mesh = mesh, .offsets = offsets}};\n  CHECK(interpolated_boundary_data != interpolated_boundary_data_2);\n  const InterpolatedBoundaryData<Dim> interpolated_boundary_data_3{\n      {.data = interpolated_data, .target_mesh = mesh_2, .offsets = offsets}};\n  if constexpr (Dim > 1) {\n    // All Mesh<0> are equivalent\n    CHECK(interpolated_boundary_data != interpolated_boundary_data_3);\n  }\n  const InterpolatedBoundaryData<Dim> interpolated_boundary_data_4{\n      {.data = interpolated_data, .target_mesh = mesh, .offsets = offsets_2}};\n  CHECK(interpolated_boundary_data != interpolated_boundary_data_4);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.InterpolatedBoundaryData\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_MortarData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg {\nnamespace {\ntemplate <size_t Dim>\nvoid assign_with_reference(const gsl::not_null<MortarData<Dim>*> mortar_data,\n                           const Mesh<Dim - 1>& mortar_mesh,\n                           const Mesh<Dim - 1>& face_mesh,\n                           const std::optional<DataVector>& data,\n                           const std::string& expected_output) {\n  if (data.has_value()) {\n    mortar_data->mortar_mesh = mortar_mesh;\n    mortar_data->face_mesh = face_mesh;\n    mortar_data->mortar_data = *data;\n    CHECK(mortar_data->mortar_data.has_value());\n  }\n\n  CHECK(get_output(*mortar_data) == expected_output);\n}\n\ntemplate <size_t Dim>\nvoid check_serialization(const gsl::not_null<MortarData<Dim>*> mortar_data) {\n  const auto deserialized_mortar_data = serialize_and_deserialize(*mortar_data);\n\n  CHECK(*mortar_data->mortar_data == *deserialized_mortar_data.mortar_data);\n\n  CHECK(*mortar_data == deserialized_mortar_data);\n  CHECK_FALSE(*mortar_data != deserialized_mortar_data);\n\n  CHECK(*mortar_data->mortar_data == *deserialized_mortar_data.mortar_data);\n}\n\ntemplate <size_t Dim>\nvoid test_global_time_stepping_usage() {\n  std::uniform_real_distribution<double> dist(-1.0, 2.3);\n  MAKE_GENERATOR(gen);\n  constexpr size_t number_of_components = 1 + Dim;\n\n  MortarData<Dim> local_mortar_data{};\n\n  const Mesh<Dim - 1> mortar_mesh{4, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::Gauss};\n\n  const Mesh<Dim - 1> local_mesh{4, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto};\n  DataVector local_data{\n      mortar_mesh.number_of_grid_points() * number_of_components, 0.0};\n  fill_with_random_values(make_not_null(&local_data), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  MortarData<Dim> neighbor_mortar_data{};\n\n  const Mesh<Dim - 1> neighbor_mesh{3, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::Gauss};\n  DataVector neighbor_data{\n      mortar_mesh.number_of_grid_points() * number_of_components, 0.0};\n  fill_with_random_values(make_not_null(&neighbor_data), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  std::string local_expected_output = MakeString{}\n                                      << \"Mortar data: \" << local_data << \"\\n\"\n                                      << \"Mortar mesh: \" << mortar_mesh << \"\\n\"\n                                      << \"Face normal magnitude: --\\n\"\n                                      << \"Face det(J): --\\n\"\n                                      << \"Face mesh: \" << local_mesh << \"\\n\"\n                                      << \"Volume det(invJ): --\\n\"\n                                      << \"Volume mesh: --\\n\";\n\n  std::string neighbor_expected_output =\n      MakeString{} << \"Mortar data: \" << neighbor_data << \"\\n\"\n                   << \"Mortar mesh: \" << mortar_mesh << \"\\n\"\n                   << \"Face normal magnitude: --\\n\"\n                   << \"Face det(J): --\\n\"\n                   << \"Face mesh: \" << neighbor_mesh << \"\\n\"\n                   << \"Volume det(invJ): --\\n\"\n                   << \"Volume mesh: --\\n\";\n\n  CHECK_FALSE(local_mortar_data.mortar_data.has_value());\n  CHECK_FALSE(neighbor_mortar_data.mortar_data.has_value());\n\n  assign_with_reference(make_not_null(&local_mortar_data), mortar_mesh,\n                        local_mesh, std::optional{local_data},\n                        local_expected_output);\n\n  assign_with_reference(make_not_null(&neighbor_mortar_data), mortar_mesh,\n                        neighbor_mesh, std::optional{neighbor_data},\n                        neighbor_expected_output);\n\n  check_serialization(make_not_null(&local_mortar_data));\n  check_serialization(make_not_null(&neighbor_mortar_data));\n}\n\ntemplate <size_t Dim>\nvoid test_local_time_stepping_usage(const bool use_gauss_points) {\n  CAPTURE(Dim);\n  CAPTURE(use_gauss_points);\n  std::uniform_real_distribution<double> dist(-1.0, 2.3);\n  MAKE_GENERATOR(gen);\n  constexpr size_t number_of_components = 1 + Dim;\n\n  MortarData<Dim> mortar_data{};\n  const Mesh<Dim - 1> mortar_mesh{4, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::Gauss};\n\n  const Mesh<Dim - 1> local_mesh{4, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto};\n  DataVector local_data{\n      mortar_mesh.number_of_grid_points() * number_of_components, 0.0};\n  fill_with_random_values(make_not_null(&local_data), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  std::string expected_output = MakeString{}\n                                << \"Mortar data: \" << local_data << \"\\n\"\n                                << \"Mortar mesh: \" << mortar_mesh << \"\\n\"\n                                << \"Face normal magnitude: --\\n\"\n                                << \"Face det(J): --\\n\"\n                                << \"Face mesh: \" << local_mesh << \"\\n\"\n                                << \"Volume det(invJ): --\\n\"\n                                << \"Volume mesh: --\\n\";\n\n  assign_with_reference(make_not_null(&mortar_data), mortar_mesh, local_mesh,\n                        std::optional{local_data}, expected_output);\n\n  const auto local_volume_det_inv_jacobian =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist),\n          mortar_mesh.number_of_grid_points() * 4);\n  const auto local_face_det_jacobian =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist),\n          mortar_mesh.number_of_grid_points());\n  const auto local_face_normal_magnitude =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist),\n          mortar_mesh.number_of_grid_points());\n\n  mortar_data.face_normal_magnitude = local_face_normal_magnitude;\n  if (use_gauss_points) {\n    mortar_data.volume_det_inv_jacobian = local_volume_det_inv_jacobian;\n    mortar_data.face_det_jacobian = local_face_det_jacobian;\n  }\n\n  const auto check_geometric_quantities = [&local_face_det_jacobian,\n                                           &local_volume_det_inv_jacobian,\n                                           &local_face_normal_magnitude,\n                                           use_gauss_points](\n                                              const auto& mortar_data_local) {\n    if (use_gauss_points) {\n      CHECK(mortar_data_local.face_det_jacobian == local_face_det_jacobian);\n      CHECK(mortar_data_local.volume_det_inv_jacobian ==\n            local_volume_det_inv_jacobian);\n    }\n    CHECK(mortar_data_local.face_normal_magnitude ==\n          local_face_normal_magnitude);\n  };\n\n  check_geometric_quantities(mortar_data);\n\n  // We don't use the check_serialization function from above because we didn't\n  // insert neighbor data here\n  const auto deserialized_mortar_data = serialize_and_deserialize(mortar_data);\n\n  CHECK(*mortar_data.mortar_data == *deserialized_mortar_data.mortar_data);\n  check_geometric_quantities(deserialized_mortar_data);\n\n  CHECK(mortar_data == deserialized_mortar_data);\n  CHECK_FALSE(mortar_data != deserialized_mortar_data);\n}\n\ntemplate <size_t Dim>\nstd::array<size_t, Dim> make_extents(const size_t first_extent) {\n  if constexpr (1 == Dim) {\n    return {first_extent};\n  } else if constexpr (2 == Dim) {\n    return {first_extent, first_extent + 1};\n  } else if constexpr (3 == Dim) {\n    return {first_extent, first_extent + 1, first_extent + 2};\n  } else {\n    return {};\n  }\n}\n\ntemplate <size_t Dim>\nvoid check_mortar_data(const MortarData<Dim>& projected,\n                       const MortarData<Dim>& expected) {\n  CHECK(projected.mortar_mesh == expected.mortar_mesh);\n  CHECK(projected.face_mesh == expected.face_mesh);\n  CHECK(projected.volume_mesh == expected.volume_mesh);\n  if (projected.mortar_data.has_value()) {\n    CHECK_ITERABLE_APPROX(projected.mortar_data.value(),\n                          expected.mortar_data.value());\n  } else {\n    CHECK_FALSE(expected.mortar_data.has_value());\n  }\n  if (projected.face_normal_magnitude.has_value()) {\n    CHECK_ITERABLE_APPROX(projected.face_normal_magnitude.value(),\n                          expected.face_normal_magnitude.value());\n  } else {\n    CHECK_FALSE(expected.face_normal_magnitude.has_value());\n  }\n  if (projected.face_det_jacobian.has_value()) {\n    CHECK_ITERABLE_APPROX(projected.face_det_jacobian.value(),\n                          expected.face_det_jacobian.value());\n  } else {\n    CHECK_FALSE(expected.face_det_jacobian.has_value());\n  }\n  if (projected.volume_det_inv_jacobian.has_value()) {\n    CHECK_ITERABLE_APPROX(projected.volume_det_inv_jacobian.value(),\n                          expected.volume_det_inv_jacobian.value());\n  } else {\n    CHECK_FALSE(expected.volume_det_inv_jacobian.has_value());\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_p_project() {\n  const Mesh<Dim> initial_volume_mesh{make_extents<Dim>(3),\n                                      Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::Gauss};\n  const Mesh<Dim - 1> initial_face_mesh =\n      initial_volume_mesh.slice_away(Dim - 1);\n  const Mesh<Dim - 1> initial_mortar_mesh = initial_volume_mesh.slice_away(0);\n  const Mesh<Dim> final_volume_mesh{make_extents<Dim>(4),\n                                    Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::Gauss};\n  const Mesh<Dim - 1> final_face_mesh = final_volume_mesh.slice_away(Dim - 1);\n  const Mesh<Dim - 1> final_mortar_mesh = final_volume_mesh.slice_away(0);\n  constexpr size_t number_of_components = 1 + Dim;\n  const DataVector initial_mortar_data{\n      initial_mortar_mesh.number_of_grid_points() * number_of_components, 1.0};\n  const Scalar<DataVector> initial_face_normal_magnitude{\n      DataVector{initial_face_mesh.number_of_grid_points(), 2.0}};\n  const Scalar<DataVector> initial_face_det_jacobian{\n      DataVector{initial_face_mesh.number_of_grid_points(), 3.0}};\n  const Scalar<DataVector> initial_volume_det_inv_jacobian{\n      DataVector{initial_volume_mesh.number_of_grid_points(), 4.0}};\n  const DataVector final_mortar_data{\n      final_mortar_mesh.number_of_grid_points() * number_of_components, 1.0};\n  const Scalar<DataVector> final_face_normal_magnitude{\n      DataVector{final_face_mesh.number_of_grid_points(), 2.0}};\n  const Scalar<DataVector> final_face_det_jacobian{\n      DataVector{final_face_mesh.number_of_grid_points(), 3.0}};\n  const Scalar<DataVector> final_volume_det_inv_jacobian{\n      DataVector{final_volume_mesh.number_of_grid_points(), 4.0}};\n  MortarData<Dim> only_mortar_data{std::optional(initial_mortar_data),\n                                   std::nullopt,\n                                   std::nullopt,\n                                   std::nullopt,\n                                   std::optional(initial_mortar_mesh),\n                                   std::nullopt,\n                                   std::nullopt};\n  CHECK(not p_project_geometric_data(make_not_null(&only_mortar_data),\n                                     final_face_mesh, final_volume_mesh));\n  check_mortar_data(only_mortar_data,\n                    MortarData<Dim>{std::optional(initial_mortar_data),\n                                    std::nullopt, std::nullopt, std::nullopt,\n                                    std::optional(initial_mortar_mesh),\n                                    std::nullopt, std::nullopt});\n  MortarData<Dim> only_mortar_data_2{std::optional(initial_mortar_data),\n                                     std::nullopt,\n                                     std::nullopt,\n                                     std::nullopt,\n                                     std::optional(initial_mortar_mesh),\n                                     std::nullopt,\n                                     std::nullopt};\n  CHECK(p_project_mortar_data(make_not_null(&only_mortar_data_2),\n                              final_mortar_mesh) == (Dim > 1));\n  check_mortar_data(only_mortar_data_2,\n                    MortarData<Dim>{std::optional(final_mortar_data),\n                                    std::nullopt, std::nullopt, std::nullopt,\n                                    std::optional(final_mortar_mesh),\n                                    std::nullopt, std::nullopt});\n  CHECK(not p_project_mortar_data(make_not_null(&only_mortar_data_2),\n                                  final_mortar_mesh));\n  check_mortar_data(only_mortar_data_2,\n                    MortarData<Dim>{std::optional(final_mortar_data),\n                                    std::nullopt, std::nullopt, std::nullopt,\n                                    std::optional(final_mortar_mesh),\n                                    std::nullopt, std::nullopt});\n  MortarData<Dim> only_gl_data{std::optional(initial_mortar_data),\n                               std::optional(initial_face_normal_magnitude),\n                               std::nullopt,\n                               std::nullopt,\n                               std::optional(initial_mortar_mesh),\n                               std::optional(initial_face_mesh),\n                               std::nullopt};\n  CHECK(p_project_geometric_data(make_not_null(&only_gl_data), final_face_mesh,\n                                 final_volume_mesh) == (Dim > 1));\n  check_mortar_data(\n      only_gl_data,\n      MortarData<Dim>{std::optional(initial_mortar_data),\n                      std::optional(final_face_normal_magnitude), std::nullopt,\n                      std::nullopt, std::optional(initial_mortar_mesh),\n                      std::optional(final_face_mesh), std::nullopt});\n  CHECK(not p_project_geometric_data(make_not_null(&only_gl_data),\n                                     final_face_mesh, final_volume_mesh));\n  check_mortar_data(\n      only_gl_data,\n      MortarData<Dim>{std::optional(initial_mortar_data),\n                      std::optional(final_face_normal_magnitude), std::nullopt,\n                      std::nullopt, std::optional(initial_mortar_mesh),\n                      std::optional(final_face_mesh), std::nullopt});\n  MortarData<Dim> g_data{std::optional(initial_mortar_data),\n                         std::optional(initial_face_normal_magnitude),\n                         std::optional(initial_face_det_jacobian),\n                         std::optional(initial_volume_det_inv_jacobian),\n                         std::optional(initial_mortar_mesh),\n                         std::optional(initial_face_mesh),\n                         std::optional(initial_volume_mesh)};\n  CHECK(p_project_geometric_data(make_not_null(&g_data), final_face_mesh,\n                                 final_volume_mesh));\n  check_mortar_data(\n      g_data, MortarData<Dim>{std::optional(initial_mortar_data),\n                              std::optional(final_face_normal_magnitude),\n                              std::optional(final_face_det_jacobian),\n                              std::optional(final_volume_det_inv_jacobian),\n                              std::optional(initial_mortar_mesh),\n                              std::optional(final_face_mesh),\n                              std::optional(final_volume_mesh)});\n  CHECK(not p_project_geometric_data(make_not_null(&g_data), final_face_mesh,\n                                     final_volume_mesh));\n  check_mortar_data(\n      g_data, MortarData<Dim>{std::optional(initial_mortar_data),\n                              std::optional(final_face_normal_magnitude),\n                              std::optional(final_face_det_jacobian),\n                              std::optional(final_volume_det_inv_jacobian),\n                              std::optional(initial_mortar_mesh),\n                              std::optional(final_face_mesh),\n                              std::optional(final_volume_mesh)});\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  test_global_time_stepping_usage<Dim>();\n  test_local_time_stepping_usage<Dim>(true);\n  test_local_time_stepping_usage<Dim>(false);\n  test_p_project<Dim>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.MortarData\", \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_MortarDataHolder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace evolution::dg {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  const Mesh<Dim> volume_mesh{4, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim - 1> face_mesh{4, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim - 1> mortar_mesh{5, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto};\n  constexpr size_t number_of_components = 1 + Dim;\n  DataVector local_mortar_data{\n      mortar_mesh.number_of_grid_points() * number_of_components, 2.0};\n  DataVector neighbor_mortar_data{\n      mortar_mesh.number_of_grid_points() * number_of_components, 3.0};\n  Scalar<DataVector> normal_magnitude{\n      DataVector{face_mesh.number_of_grid_points(), 4.0}};\n  Scalar<DataVector> det_j{DataVector{face_mesh.number_of_grid_points(), 5.0}};\n  Scalar<DataVector> det_inv_j{\n      DataVector{volume_mesh.number_of_grid_points(), 6.0}};\n  MortarDataHolder<Dim> holder{};\n  holder.local() = MortarData<Dim>{\n      local_mortar_data, normal_magnitude, det_j,      det_inv_j,\n      mortar_mesh,       face_mesh,        volume_mesh};\n  holder.neighbor() = MortarData<Dim>{\n      neighbor_mortar_data, std::nullopt, std::nullopt, std::nullopt,\n      mortar_mesh,          std::nullopt, std::nullopt};\n\n  std::string expected_output =\n      MakeString{} << \"Local mortar data:\\n\"\n                   << \"Mortar data: \" << local_mortar_data << \"\\n\"\n                   << \"Mortar mesh: \" << mortar_mesh << \"\\n\"\n                   << \"Face normal magnitude: \" << normal_magnitude << \"\\n\"\n                   << \"Face det(J): \" << det_j << \"\\n\"\n                   << \"Face mesh: \" << face_mesh << \"\\n\"\n                   << \"Volume det(invJ): \" << det_inv_j << \"\\n\"\n                   << \"Volume mesh: \" << volume_mesh << \"\\n\\n\"\n                   << \"Neighbor mortar data:\\n\"\n                   << \"Mortar data: \" << neighbor_mortar_data << \"\\n\"\n                   << \"Mortar mesh: \" << mortar_mesh << \"\\n\"\n                   << \"Face normal magnitude: --\\n\"\n                   << \"Face det(J): --\\n\"\n                   << \"Face mesh: --\\n\"\n                   << \"Volume det(invJ): --\\n\"\n                   << \"Volume mesh: --\\n\\n\";\n\n  CHECK(get_output(holder) == expected_output);\n  const auto deserialized_holder = serialize_and_deserialize(holder);\n  CHECK(holder == deserialized_holder);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.MortarDataHolder\", \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_MortarInfo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"Domain/Creators/NonconformingSphericalShells.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/InterfaceDataPolicy.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarInterpolator.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace evolution::dg {\nnamespace {\ntemplate <size_t Dim>\nvoid test(\n    const std::array<Spectral::SegmentSize, Dim - 1>& mortar_size,\n    const std::optional<::dg::MortarInterpolator<Dim>>& mortar_interpolator) {\n  const MortarInfo<Dim> nonconforming_mortar_info{\n      {.interpolator = mortar_interpolator,\n       .interface_data_policy =\n           InterfaceDataPolicy::NonconformingSelfInterpolates,\n       .time_stepping_policy = TimeSteppingPolicy::EqualRate}};\n  CHECK(nonconforming_mortar_info.interface_data_policy() ==\n        InterfaceDataPolicy::NonconformingSelfInterpolates);\n  CHECK(nonconforming_mortar_info.time_stepping_policy() ==\n        TimeSteppingPolicy::EqualRate);\n  CHECK(nonconforming_mortar_info.mortar_size() ==\n        make_array<Dim - 1>(Spectral::SegmentSize::Uninitialized));\n  CHECK(nonconforming_mortar_info.interpolator() == mortar_interpolator);\n  const auto deserialized_nonconforming_mortar_info =\n      serialize_and_deserialize(nonconforming_mortar_info);\n  CHECK(nonconforming_mortar_info == deserialized_nonconforming_mortar_info);\n  const MortarInfo<Dim> conforming_mortar_info{\n      {.mortar_size = mortar_size,\n       .interface_data_policy = InterfaceDataPolicy::CopyProject,\n       .time_stepping_policy = TimeSteppingPolicy::Conservative}};\n  CHECK(conforming_mortar_info.interface_data_policy() ==\n        InterfaceDataPolicy::CopyProject);\n  CHECK(conforming_mortar_info.time_stepping_policy() ==\n        TimeSteppingPolicy::Conservative);\n  CHECK(conforming_mortar_info.mortar_size() == mortar_size);\n  CHECK(conforming_mortar_info.interpolator() == std::nullopt);\n  const auto deserialized_conforming_mortar_info =\n      serialize_and_deserialize(conforming_mortar_info);\n  CHECK(conforming_mortar_info == deserialized_conforming_mortar_info);\n  CHECK(conforming_mortar_info != nonconforming_mortar_info);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.MortarInfo\", \"[Unit][Evolution]\") {\n  test<1>({{}}, std::nullopt);\n  test<2>({{Spectral::SegmentSize::Full}}, std::nullopt);\n  const auto creator = domain::creators::NonconformingSphericalShells(\n      2.0, 3.0, 4.0, 0, 0, 5, 8, 11, nullptr, nullptr);\n  const auto domain = creator.create_domain();\n  test<3>(\n      {{Spectral::SegmentSize::UpperHalf, Spectral::SegmentSize::LowerHalf}},\n      ::dg::MortarInterpolator{\n          ElementId<3>{0},\n          DirectionalId<3>{Direction<3>::upper_zeta(), ElementId<3>{6}}, domain,\n          Mesh<2>{11_st, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto},\n          Mesh<2>{std::array{8_st, 15_st},\n                  std::array{Spectral::Basis::SphericalHarmonic,\n                             Spectral::Basis::SphericalHarmonic},\n                  std::array{Spectral::Quadrature::Gauss,\n                             Spectral::Quadrature::Equiangular}}});\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_MortarTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace evolution::dg {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  TestHelpers::db::test_simple_tag<Tags::MortarData<Dim>>(\"MortarData\");\n  TestHelpers::db::test_simple_tag<Tags::MortarDataHistory<Dim>>(\n      \"MortarDataHistory\");\n  TestHelpers::db::test_simple_tag<Tags::MortarMesh<Dim>>(\"MortarMesh\");\n  TestHelpers::db::test_simple_tag<Tags::MortarInfo<Dim>>(\"MortarInfo\");\n  TestHelpers::db::test_simple_tag<Tags::MortarNextTemporalId<Dim>>(\n      \"MortarNextTemporalId\");\n  TestHelpers::db::test_simple_tag<Tags::BoundaryMessageFromInbox<Dim>>(\n      \"BoundaryMessageFromInbox\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.MortarTags\", \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace evolution::dg\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_NormalVectorTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace evolution::dg::Tags {\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  TestHelpers::db::test_simple_tag<MagnitudeOfNormal>(\"MagnitudeOfNormal\");\n  TestHelpers::db::test_simple_tag<NormalCovector<Dim>>(\"NormalCovector\");\n  TestHelpers::db::test_simple_tag<NormalCovectorAndMagnitude<Dim>>(\n      \"NormalCovectorAndMagnitude\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.InternalFaceTags\", \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace evolution::dg::Tags\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_TimeSteppingPolicy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.TimeSteppingPolicy\", \"[Unit][Evolution]\") {\n  CHECK(get_output(evolution::dg::TimeSteppingPolicy::Uninitialized) ==\n        \"Uninitialized\");\n  CHECK(get_output(evolution::dg::TimeSteppingPolicy::EqualRate) ==\n        \"EqualRate\");\n  CHECK(get_output(evolution::dg::TimeSteppingPolicy::Conservative) ==\n        \"Conservative\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/DiscontinuousGalerkin/Test_UsingSubcell.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/DiscontinuousGalerkin/UsingSubcell.hpp\"\n\nnamespace {\ntemplate <bool SubcellEnabled>\nstruct MetavarsSubcell {\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled = SubcellEnabled;\n  };\n};\n\nstruct MetavarsNoSubcell {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.UsingSubcell\", \"[Unit][Evolution]\") {\n  static_assert(not evolution::dg::using_subcell_v<MetavarsNoSubcell>);\n  static_assert(not evolution::dg::using_subcell_v<MetavarsSubcell<false>>);\n  static_assert(evolution::dg::using_subcell_v<MetavarsSubcell<true>>);\n\n  CHECK(not evolution::dg::using_subcell_v<MetavarsNoSubcell>);\n  CHECK(not evolution::dg::using_subcell_v<MetavarsSubcell<false>>);\n  CHECK(evolution::dg::using_subcell_v<MetavarsSubcell<true>>);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Actions/Test_DoImplicitStep.cpp\n  Actions/Test_RecordTimeStepperData.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Actions/Test_DoImplicitStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Imex/Actions/DoImplicitStep.hpp\"\n#include \"Evolution/Imex/Mode.hpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Evolution/Imex/Tags/Mode.hpp\"\n#include \"Evolution/Imex/Tags/SolveFailures.hpp\"\n#include \"Evolution/Imex/Tags/SolveTolerance.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/Evolution/Imex/DoImplicitStepSector.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/Heun2.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using simple_tags =\n      tmpl::push_front<typename Metavariables::system_tags,\n                       Tags::ConcreteTimeStepper<ImexTimeStepper>,\n                       Tags::TimeStep, ::Tags::Time,\n                       Tags::Next<Tags::TimeStepId>,\n                       typename Metavariables::system::variables_tag,\n                       imex::Tags::Mode, imex::Tags::SolveTolerance>;\n\n  using compute_tags = time_stepper_ref_tags<ImexTimeStepper>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<imex::Actions::DoImplicitStep<\n                                 typename Metavariables::system>>>>;\n};\n\ntemplate <typename System, typename SystemTags>\nstruct Metavariables {\n  using system = System;\n  using component_list = tmpl::list<Component<Metavariables>>;\n  using system_tags = SystemTags;\n};\n\nnamespace helpers = do_implicit_step_helpers;\n\nvoid test_basic_functionality() {\n  using metavariables = Metavariables<\n      helpers::System,\n      tmpl::list<imex::Tags::ImplicitHistory<helpers::Sector<helpers::Var1>>,\n                 imex::Tags::ImplicitHistory<helpers::Sector<helpers::Var2>>,\n                 imex::Tags::SolveFailures<helpers::Sector<helpers::Var1>>,\n                 imex::Tags::SolveFailures<helpers::Sector<helpers::Var2>>>>;\n  using component = Component<metavariables>;\n\n  const size_t number_of_grid_points = 5;\n\n  const Slab slab(1.0, 3.0);\n  const TimeStepId time_step_id(true, 0, slab.start());\n  const auto time_step = slab.duration();\n\n  helpers::System::variables_tag::type initial_vars(number_of_grid_points);\n  get(get<helpers::Var1>(initial_vars)) = 2.0;\n  get(get<helpers::Var2>(initial_vars)) = 3.0;\n\n  imex::Tags::ImplicitHistory<helpers::Sector<helpers::Var1>>::type history1(2);\n  history1.insert(time_step_id, decltype(history1)::no_value,\n                  -get(get<helpers::Var1>(initial_vars)));\n  imex::Tags::ImplicitHistory<helpers::Sector<helpers::Var2>>::type history2(2);\n  history2.insert(time_step_id, decltype(history2)::no_value,\n                  -get(get<helpers::Var2>(initial_vars)));\n  Scalar<DataVector> solve_failures1(DataVector(number_of_grid_points, 0.0));\n  Scalar<DataVector> solve_failures2(DataVector(number_of_grid_points, 0.0));\n\n  const double tolerance = 1.0e-10;\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {std::make_unique<TimeSteppers::Heun2>(), time_step,\n       time_step_id.substep_time(),\n       TimeSteppers::Heun2{}.next_time_id(time_step_id, time_step),\n       initial_vars, imex::Mode::Implicit, tolerance, std::move(history1),\n       std::move(history2), std::move(solve_failures1),\n       std::move(solve_failures2)});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  runner.next_action<component>(0);\n\n  const auto& box = ActionTesting::get_databox<component>(runner, 0);\n  CHECK(db::get<::Tags::Time>(box) == time_step_id.substep_time());\n  const auto& final_vars = db::get<helpers::System::variables_tag>(box);\n\n  const double dt = time_step.value();\n  const double step_factor = (1.0 - 0.5 * dt) / (1.0 + 0.5 * dt);\n\n  CHECK(get(get<helpers::Var1>(final_vars)) ==\n        step_factor * get(get<helpers::Var1>(initial_vars)));\n  CHECK(get(get<helpers::Var2>(final_vars)) ==\n        step_factor * get(get<helpers::Var2>(initial_vars)));\n}\n\nvoid test_nonautonomous() {\n  using sector = helpers::NonautonomousSector;\n  using Vars = helpers::NonautonomousSystem::variables_tag::type;\n  using history_tag = imex::Tags::ImplicitHistory<sector>;\n  using metavariables =\n      Metavariables<helpers::NonautonomousSystem,\n                    tmpl::list<history_tag, imex::Tags::SolveFailures<sector>>>;\n  using component = Component<metavariables>;\n\n  const size_t number_of_grid_points = 5;\n\n  const Slab slab(1.0, 3.0);\n  const TimeStepId time_step_id(true, 0, slab.start());\n  const auto time_step = slab.duration();\n\n  Vars initial_vars(number_of_grid_points);\n  get(get<helpers::Var1>(initial_vars)) = 2.0;\n\n  auto expected_var = get<helpers::Var1>(initial_vars);\n  // Function is dy/dt = t\n  get(expected_var) +=\n      0.5 * (square(slab.end().value()) - square(slab.start().value()));\n\n  history_tag::type history(2);\n  history.insert(time_step_id, decltype(history)::no_value,\n                 make_with_value<history_tag::type::DerivVars>(\n                     initial_vars, time_step_id.substep_time()));\n  Scalar<DataVector> solve_failures(DataVector(number_of_grid_points, 0.0));\n\n  const double tolerance = 1.0e-10;\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {std::make_unique<TimeSteppers::Heun2>(), time_step,\n       time_step_id.substep_time(),\n       TimeSteppers::Heun2{}.next_time_id(time_step_id, time_step),\n       initial_vars, imex::Mode::Implicit, tolerance, std::move(history),\n       std::move(solve_failures)});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  runner.next_action<component>(0);\n\n  const auto& box = ActionTesting::get_databox<component>(runner, 0);\n  CHECK(db::get<::Tags::Time>(box) == time_step_id.substep_time());\n  const auto& final_var = db::get<helpers::Var1>(box);\n  CHECK_ITERABLE_APPROX(final_var, expected_var);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Actions.DoImplicitStep\",\n                  \"[Unit][Evolution][Actions]\") {\n  register_classes_with_charm<TimeSteppers::Heun2>();\n  test_basic_functionality();\n  test_nonautonomous();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Actions/Test_RecordTimeStepperData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Imex/Actions/RecordTimeStepperData.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var3 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct OtherVar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Sector1 : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var1>;\n  using initial_guess = imex::GuessExplicitResult;\n\n  struct SolveAttempt {\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<Tags::Source<Var1>>;\n      using argument_tags = tmpl::list<OtherVar>;\n      static void apply(const gsl::not_null<Scalar<DataVector>*> source,\n                        const Scalar<DataVector>& other_var) {\n        get(*source) = get(other_var);\n      }\n    };\n\n    struct jacobian : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n                      tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<>;\n      static void apply();\n    };\n\n    using tags_from_evolution = tmpl::list<>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n  };\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\n\nstruct Sector2 : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var2, Var3>;\n  using initial_guess = imex::GuessExplicitResult;\n\n  struct InitialAttempt {\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<Tags::Source<Var3>, Tags::Source<Var2>>;\n      using argument_tags = tmpl::list<Var1>;\n      static void apply(const gsl::not_null<Scalar<DataVector>*> source3,\n                        const gsl::not_null<Scalar<DataVector>*> source2,\n                        const Scalar<DataVector>& var1) {\n        get(*source2) = 3.0 * get(var1);\n        get(*source3) = 5.0 * get(var1);\n      }\n    };\n\n    struct jacobian : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n                      tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<>;\n      static void apply();\n    };\n\n    using tags_from_evolution = tmpl::list<>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n  };\n\n  struct Fallback {\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<Tags::Source<Var3>, Tags::Source<Var2>>;\n      using argument_tags = tmpl::list<>;\n      static void apply(const gsl::not_null<Scalar<DataVector>*> /*source3*/,\n                        const gsl::not_null<Scalar<DataVector>*> /*source2*/) {\n        CHECK(false);\n      }\n    };\n\n    struct jacobian : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n                      tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<>;\n      static void apply();\n    };\n\n    using tags_from_evolution = tmpl::list<>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n  };\n  using solve_attempts = tmpl::list<InitialAttempt, Fallback>;\n};\n\nstruct System : tt::ConformsTo<imex::protocols::ImexSystem> {\n  using variables_tag = Tags::Variables<tmpl::list<Var1, Var2, Var3>>;\n  using implicit_sectors = tmpl::list<Sector1, Sector2>;\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using simple_tags =\n      db::AddSimpleTags<Tags::TimeStepId, System::variables_tag, OtherVar,\n                        imex::Tags::ImplicitHistory<Sector1>,\n                        imex::Tags::ImplicitHistory<Sector2>>;\n  using compute_tags = db::AddComputeTags<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<imex::Actions::RecordTimeStepperData<System>>>>;\n};\n\nstruct Metavariables {\n  using system = System;\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Actions.RecordTimeStepperData\",\n                  \"[Unit][Evolution][Actions]\") {\n  using component = Component<Metavariables>;\n\n  const size_t number_of_grid_points = 5;\n\n  const Slab slab(1., 3.);\n  const TimeStepId time_step_id(true, 0, slab.start());\n\n  System::variables_tag::type vars(number_of_grid_points);\n  get(get<Var1>(vars)) = 2.0;\n  get(get<Var2>(vars)) = 3.0;\n  get(get<Var3>(vars)) = 4.0;\n\n  const Scalar<DataVector> other_var(DataVector(number_of_grid_points, 5.0));\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {time_step_id, vars, other_var,\n       imex::Tags::ImplicitHistory<Sector1>::type(2),\n       imex::Tags::ImplicitHistory<Sector2>::type(2)});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  runner.next_action<component>(0);\n\n  const auto& box = ActionTesting::get_databox<component>(runner, 0);\n  const auto& history1 = db::get<imex::Tags::ImplicitHistory<Sector1>>(box);\n  const auto& history2 = db::get<imex::Tags::ImplicitHistory<Sector2>>(box);\n\n  CHECK(history1.size() == 1);\n  CHECK(history1[0].time_step_id == time_step_id);\n  CHECK(not history1[0].value.has_value());\n  CHECK(get<Tags::dt<Var1>>(history1[0].derivative) == other_var);\n\n  CHECK(history2.size() == 1);\n  CHECK(history2[0].time_step_id == time_step_id);\n  CHECK(not history2[0].value.has_value());\n  CHECK(get(get<Tags::dt<Var2>>(history2[0].derivative)) ==\n        3.0 * get(get<Var1>(vars)));\n  CHECK(get(get<Tags::dt<Var3>>(history2[0].derivative)) ==\n        5.0 * get(get<Var1>(vars)));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Imex\")\n\nset(LIBRARY_SOURCES\n  Test_CleanHistory.cpp\n  Test_GuessResult.cpp\n  Test_ImplicitDenseOutput.cpp\n  Test_Initialize.cpp\n  Test_Mode.cpp\n  Test_SolveImplicitSector.cpp\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(Tags)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  Framework\n  Imex\n  ImexHelpers\n  Utilities\n  )\n\nadd_dependencies(\n  ${LIBRARY}\n  module_GlobalCache\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Tags/Test_ImplicitHistory.cpp\n  Tags/Test_Jacobian.cpp\n  Tags/Test_Mode.cpp\n  Tags/Test_SolveFailures.cpp\n  Tags/Test_SolveTolerance.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Tags/Test_ImplicitHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\nstruct Sector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<>;\n  using initial_guess = imex::GuessExplicitResult;\n\n  struct SolveAttempt {\n    using tags_from_evolution = tmpl::list<>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<>;\n      static void apply();\n    };\n\n    struct jacobian : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n                      tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<>;\n      static void apply();\n    };\n  };\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Tags.ImplicitHistory\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<imex::Tags::ImplicitHistory<Sector>>(\n      \"ImplicitHistory\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Tags/Test_Jacobian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Imex/Tags/Jacobian.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nclass DataVector;\n\nnamespace {\nstruct Independent {\n  using type = tnsr::i<DataVector, 2>;\n};\n\nstruct Dependent {\n  using type = tnsr::ii<DataVector, 2>;\n};\n\nstruct Independent2;\nstruct Independent3;\nstruct Dependent2;\nstatic_assert(\n    std::is_same_v<\n        imex::jacobian_tags<tmpl::list<Independent, Independent2, Independent3>,\n                            tmpl::list<Dependent, Dependent2>>,\n        tmpl::list<imex::Tags::Jacobian<Independent, Dependent>,\n                   imex::Tags::Jacobian<Independent, Dependent2>,\n                   imex::Tags::Jacobian<Independent2, Dependent>,\n                   imex::Tags::Jacobian<Independent2, Dependent2>,\n                   imex::Tags::Jacobian<Independent3, Dependent>,\n                   imex::Tags::Jacobian<Independent3, Dependent2>>>);\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Tags.Jacobian\", \"[Unit][Evolution]\") {\n  using tag = imex::Tags::Jacobian<Independent, ::Tags::Source<Dependent>>;\n  TestHelpers::db::test_simple_tag<tag>(\"Jacobian\");\n  static_assert(std::is_same_v<tag::type, tnsr::Ijj<DataVector, 2>>);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Tags/Test_Mode.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Imex/Mode.hpp\"\n#include \"Evolution/Imex/Tags/Mode.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Tags.Mode\", \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<imex::Tags::Mode>(\"Mode\");\n  CHECK(TestHelpers::test_option_tag<imex::OptionTags::Mode>(\"SemiImplicit\") ==\n        imex::Mode::SemiImplicit);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Tags/Test_SolveFailures.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Tags/SolveFailures.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\nstruct Sector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<>;\n  using initial_guess = imex::GuessExplicitResult;\n\n  struct SolveAttempt {\n    using tags_from_evolution = tmpl::list<>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<>;\n      static void apply();\n    };\n\n    struct jacobian : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n                      tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<>;\n      static void apply();\n    };\n  };\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Tags.SolveFailures\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<imex::Tags::SolveFailures<Sector>>(\n      \"SolveFailures(Sector)\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Tags/Test_SolveTolerance.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Imex/Tags/SolveTolerance.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Tags.SolveTolerance\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<imex::Tags::SolveTolerance>(\n      \"SolveTolerance\");\n  CHECK(TestHelpers::test_option_tag<imex::OptionTags::SolveTolerance>(\n            \"0.125\") == 0.125);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Test_CleanHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Imex/CleanHistory.hpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Helpers/Evolution/Imex/DoImplicitStepSector.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/Heun2.hpp\"\n#include \"Time/TimeSteppers/ImexTimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Var>\nTimeSteppers::History<Variables<tmpl::list<Var>>> create_history() {\n  const Slab slab(1., 3.);\n  TimeSteppers::History<Variables<tmpl::list<Var>>> history{2};\n  history.insert(TimeStepId(true, 0, slab.start()), {5, 0.0}, {5, 0.0});\n  history.insert(\n      TimeStepId(true, 0, slab.start(), 1, slab.duration(), slab.end().value()),\n      {5, 0.0}, {5, 0.0});\n  history.insert(TimeStepId(true, 1, slab.end()), {5, 0.0}, {5, 0.0});\n  return history;\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.CleanHistory\", \"[Unit][Evolution]\") {\n  namespace helpers = do_implicit_step_helpers;\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Tags::ConcreteTimeStepper<ImexTimeStepper>,\n          imex::Tags::ImplicitHistory<helpers::Sector<helpers::Var1>>,\n          imex::Tags::ImplicitHistory<helpers::Sector<helpers::Var2>>>,\n      time_stepper_ref_tags<ImexTimeStepper>>(\n      static_cast<std::unique_ptr<ImexTimeStepper>>(\n          std::make_unique<TimeSteppers::Heun2>()),\n      create_history<helpers::Var1>(), create_history<helpers::Var2>());\n\n  db::mutate_apply<imex::CleanHistory<helpers::System>>(make_not_null(&box));\n\n  CHECK(\n      db::get<imex::Tags::ImplicitHistory<helpers::Sector<helpers::Var1>>>(box)\n          .size() == 1);\n  CHECK(\n      db::get<imex::Tags::ImplicitHistory<helpers::Sector<helpers::Var2>>>(box)\n          .size() == 1);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Test_GuessResult.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.GuessResult\", \"[Unit][Evolution]\") {\n  // Since the mutators don't have information about the implicit\n  // sector, they can't actually use anything in the DataBox.\n  auto box = db::create<db::AddSimpleTags<>>();\n\n  // Technically, the homogeneous terms should be a Variables, but\n  // there's nothing a mutator with no return tags can possibly do\n  // with them, so if it compiles with a dummy type it will work in\n  // real use.\n  struct Dummy {};\n  const std::vector<imex::GuessResult> result =\n      db::mutate_apply<imex::GuessExplicitResult>(make_not_null(&box), Dummy{},\n                                                  2.0);\n  CHECK(result.empty());\n\n  CHECK_THROWS_WITH(\n      db::mutate_apply<imex::NoJacobianBecauseSolutionIsAnalytic>(\n          make_not_null(&box)),\n      Catch::Matchers::ContainsSubstring(\n          \"initial guess did not return GuessResult::ExactSolution\"));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Test_ImplicitDenseOutput.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/ImplicitDenseOutput.hpp\"\n#include \"Evolution/Imex/Protocols/ImexSystem.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/Heun2.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Var>\nstruct Sector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var>;\n\n  struct initial_guess {\n    using return_tags = tmpl::list<Var>;\n    using argument_tags = tmpl::list<>;\n    static imex::GuessResult apply(\n        const gsl::not_null<Scalar<DataVector>*> var,\n        const Variables<tmpl::list<Var>>& inhomogeneous_terms,\n        const double implicit_weight) {\n      get(*var) = get(get<Var>(inhomogeneous_terms)) / (1.0 + implicit_weight);\n      return imex::GuessResult::ExactSolution;\n    }\n  };\n\n  struct SolveAttempt {\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<Tags::Source<Var>>;\n      using argument_tags = tmpl::list<Var>;\n      static void apply(const gsl::not_null<Scalar<DataVector>*> source,\n                        const Scalar<DataVector>& var) {\n        get(*source) = -get(var);\n      }\n    };\n\n    using jacobian = imex::NoJacobianBecauseSolutionIsAnalytic;\n\n    using tags_from_evolution = tmpl::list<>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n  };\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct System : tt::ConformsTo<imex::protocols::ImexSystem> {\n  using variables_tag = Tags::Variables<tmpl::list<Var1, Var2>>;\n  using implicit_sectors = tmpl::list<Sector<Var1>, Sector<Var2>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.ImplicitDenseOutput\",\n                  \"[Unit][Evolution][Actions]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist(-5.0, 5.0);\n\n  const Slab slab(3.0, 5.0);\n  const double test_time = 3.7;\n\n  const size_t number_of_grid_points = 5;\n  const auto initial_vars =\n      make_with_random_values<Variables<tmpl::list<Var1, Var2>>>(\n          make_not_null(&gen), make_not_null(&dist), number_of_grid_points);\n  const auto explicit_vars =\n      make_with_random_values<Variables<tmpl::list<Var1, Var2>>>(\n          make_not_null(&gen), make_not_null(&dist), number_of_grid_points);\n\n  TimeSteppers::History<Variables<tmpl::list<Var1, Var2>>> full_history(2);\n  TimeSteppers::History<Variables<tmpl::list<Var1>>> history1(2);\n  TimeSteppers::History<Variables<tmpl::list<Var2>>> history2(2);\n\n  // Values of past sources, arbitrary.\n  const auto insert_values = [&](const TimeStepId& time_step_id) {\n    const auto source = make_with_random_values<\n        Variables<tmpl::list<Tags::dt<Var1>, Tags::dt<Var2>>>>(\n        make_not_null(&gen), make_not_null(&dist), number_of_grid_points);\n    full_history.insert(time_step_id, initial_vars, -source);\n    history1.insert(time_step_id, decltype(history1)::no_value,\n                    -get(get<Tags::dt<Var1>>(source)));\n    history2.insert(time_step_id, decltype(history2)::no_value,\n                    -get(get<Tags::dt<Var2>>(source)));\n  };\n  insert_values(TimeStepId(true, 0, slab.start()));\n  insert_values(TimeStepId(true, 0, slab.start(), 1, slab.duration(),\n                           slab.end().value()));\n  // All RK steppers are implemented with FSAL bookkeeping, even\n  // though Heun2 doesn't use FSAL.\n  insert_values(TimeStepId(true, 1, slab.end()));\n\n  auto box = db::create<\n      db::AddSimpleTags<System::variables_tag,\n                        imex::Tags::ImplicitHistory<Sector<Var1>>,\n                        imex::Tags::ImplicitHistory<Sector<Var2>>,\n                        Tags::ConcreteTimeStepper<TimeStepper>, Tags::Time>,\n      time_stepper_ref_tags<TimeStepper>>(\n      explicit_vars, std::move(history1), std::move(history2),\n      static_cast<std::unique_ptr<TimeStepper>>(\n          std::make_unique<TimeSteppers::Heun2>()),\n      test_time);\n\n  tuples::TaggedTuple<> unused_inboxes{};\n  Parallel::GlobalCache<Metavariables> unused_cache{};\n  const int unused_array_index{};\n  const void* const unused_component{};\n  CHECK(imex::ImplicitDenseOutput<System>::is_ready(\n      make_not_null(&box), make_not_null(&unused_inboxes), unused_cache,\n      unused_array_index, unused_component));\n\n  auto expected = explicit_vars;\n  const bool output_succeeded =\n      db::get<Tags::TimeStepper<TimeStepper>>(box).dense_update_u(\n          make_not_null(&expected), full_history, test_time);\n  REQUIRE(output_succeeded);\n\n  db::mutate_apply<imex::ImplicitDenseOutput<System>>(make_not_null(&box));\n\n  CHECK_ITERABLE_APPROX(db::get<Var1>(box), get<Var1>(expected));\n  CHECK_ITERABLE_APPROX(db::get<Var2>(box), get<Var2>(expected));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Test_Initialize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Initialize.hpp\"\n#include \"Evolution/Imex/Protocols/ImexSystem.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Var>\nstruct Sector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var>;\n\n  struct initial_guess {\n    using return_tags = tmpl::list<Var>;\n    using argument_tags = tmpl::list<>;\n    static imex::GuessResult apply(\n        gsl::not_null<Scalar<DataVector>*> var,\n        const Variables<tmpl::list<Var>>& inhomogeneous_terms,\n        double implicit_weight);\n  };\n\n  struct SolveAttempt {\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<Tags::Source<Var>>;\n      using argument_tags = tmpl::list<Var>;\n      static void apply(gsl::not_null<Scalar<DataVector>*> source);\n    };\n\n    using jacobian = imex::NoJacobianBecauseSolutionIsAnalytic;\n\n    using tags_from_evolution = tmpl::list<>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n  };\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct System : tt::ConformsTo<imex::protocols::ImexSystem> {\n  using variables_tag = Tags::Variables<tmpl::list<Var1, Var2>>;\n  using implicit_sectors = tmpl::list<Sector<Var1>, Sector<Var2>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Initialize\", \"[Unit][Evolution]\") {\n  using initialize_imex = imex::Initialize<System>;\n  using explicit_history_tag =\n      Tags::HistoryEvolvedVariables<System::variables_tag>;\n  auto box =\n      db::create<db::AddSimpleTags<System::variables_tag, explicit_history_tag,\n                                   initialize_imex::simple_tags>>();\n  db::mutate<System::variables_tag, explicit_history_tag>(\n      [](const gsl::not_null<System::variables_tag::type*> variables,\n         const gsl::not_null<explicit_history_tag::type*> explicit_history) {\n        variables->initialize(5);\n        explicit_history->integration_order(3);\n      },\n      make_not_null(&box));\n\n  db::mutate_apply<initialize_imex>(make_not_null(&box));\n\n  CHECK(db::get<imex::Tags::ImplicitHistory<Sector<Var1>>>(box)\n            .integration_order() == 3);\n  CHECK(db::get<imex::Tags::ImplicitHistory<Sector<Var2>>>(box)\n            .integration_order() == 3);\n  CHECK(db::get<imex::Tags::SolveFailures<Sector<Var1>>>(box) ==\n        Scalar<DataVector>(DataVector(5, 0.0)));\n  CHECK(db::get<imex::Tags::SolveFailures<Sector<Var2>>>(box) ==\n        Scalar<DataVector>(DataVector(5, 0.0)));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Test_Mode.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Imex/Mode.hpp\"\n#include \"Framework/TestCreation.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.Mode\", \"[Unit][Evolution]\") {\n  CHECK(TestHelpers::test_creation<imex::Mode>(\"Implicit\") ==\n        imex::Mode::Implicit);\n  CHECK(TestHelpers::test_creation<imex::Mode>(\"SemiImplicit\") ==\n        imex::Mode::SemiImplicit);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Imex/Test_SolveImplicitSector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Mode.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSource.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSourceJacobian.hpp\"\n#include \"Evolution/Imex/SolveImplicitSector.hpp\"\n#include \"Evolution/Imex/SolveImplicitSector.tpp\"\n#include \"Evolution/Imex/Tags/ImplicitHistory.hpp\"\n#include \"Evolution/Imex/Tags/Jacobian.hpp\"\n#include \"Evolution/Imex/Tags/Mode.hpp\"\n#include \"Evolution/Imex/Tags/SolveFailures.hpp\"\n#include \"Evolution/Imex/Tags/SolveTolerance.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Imex/TestSector.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/Heun2.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Set temporarily to verify that the solver correctly skips most of\n// the work when the step is explicit.\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nbool performing_step_with_no_implicit_term = false;\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::II<DataVector, 2>;\n};\n\nstruct Var3 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 2>;\n};\n\nstruct NonTensor : db::SimpleTag {\n  using type = double;\n};\n\n// These next several tags aren't used in the calculation, just for\n// testing DataBox handling.\nstruct TensorFromEvolution : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nusing VariablesFromEvolution = Tags::Variables<tmpl::list<TensorFromEvolution>>;\n\nstruct TensorTemporary : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct VariablesTemporary : db::SimpleTag {\n  using type = Variables<tmpl::list<TensorTemporary>>;\n};\n\nstruct SomeComputeTagBase : db::SimpleTag {\n  using type = double;\n};\n\nstruct SomeComputeTag : SomeComputeTagBase, db::ComputeTag {\n  using base = SomeComputeTagBase;\n  using argument_tags =\n      tmpl::list<Var1, TensorFromEvolution, VariablesTemporary>;\n  static void function(\n      const gsl::not_null<double*> result, const Scalar<DataVector>& var1,\n      const Scalar<DataVector>& from_evolution,\n      const Variables<tmpl::list<TensorTemporary>>& temporary) {\n    // Check the initialization of the temporary Variables in the\n    // solver DataBox.  None of the mutators modify the object, so it\n    // should always have that state.\n    CHECK(temporary.number_of_grid_points() == 1);\n    CHECK(get(get<TensorTemporary>(temporary))[0] == 0.0);\n\n    // Check slicing\n    CHECK(get(from_evolution).size() == 1);\n    CHECK(get(from_evolution)[0] == 2.0 * get(var1)[0]);\n\n    *result = get(var1)[0] + 1.0;\n  }\n};\n\nenum class PrepId { Source, Jacobian, Shared };\n\nstruct RecordPreparersForTest : db::SimpleTag {\n  using type = std::array<std::pair<Var2::type, Var3::type>, 4>;\n};\n\ntemplate <PrepId Prep>\nstruct Preparer {\n  using return_tags = tmpl::list<RecordPreparersForTest>;\n  using argument_tags = tmpl::list<Var2, Var3>;\n\n  static void apply(\n      const gsl::not_null<RecordPreparersForTest::type*> prep_run_values,\n      const Var2::type& var2, const Var3::type& var3) {\n    CHECK(not performing_step_with_no_implicit_term);\n\n    std::pair current_values{var2, var3};\n    CHECK((*prep_run_values)[static_cast<size_t>(Prep)] != current_values);\n    (*prep_run_values)[static_cast<size_t>(Prep)] = std::move(current_values);\n  }\n};\n// End stuff only used for DataBox handling\n\nstruct AnalyticSolution {\n  using return_tags = tmpl::list<Var2, Var3>;\n  using argument_tags = tmpl::list<Var1, NonTensor>;\n  static std::vector<imex::GuessResult> apply(\n      const gsl::not_null<tnsr::II<DataVector, 2>*> var2,\n      const gsl::not_null<tnsr::I<DataVector, 2>*> var3,\n      const Scalar<DataVector>& var1, const double non_tensor,\n      const Variables<tmpl::list<Var2, Var3>>& inhomogeneous_terms,\n      const double implicit_weight) {\n    // Solution for source terms\n    // S[v2^ij] = v3^i v3^j - nt v2^ij\n    // S[v3^i] = -v1 v3^i\n\n    // Solving  v3^i = X - w v1 v3^i  gives  v3^i = X / (1 + w v1)\n    tenex::evaluate<ti::I>(var3, get<Var3>(inhomogeneous_terms)(ti::I) /\n                                     (1.0 + implicit_weight * var1()));\n    tenex::evaluate<ti::I, ti::J>(\n        var2, (get<Var2>(inhomogeneous_terms)(ti::I, ti::J) +\n               implicit_weight * (*var3)(ti::I) * (*var3)(ti::J)) /\n                  (1.0 + implicit_weight * non_tensor));\n    return {get(var1).size(), imex::GuessResult::ExactSolution};\n  }\n};\n\nstruct InitialGuess {\n  using return_tags = tmpl::list<Var2, Var3>;\n  using argument_tags = tmpl::list<>;\n  static std::vector<imex::GuessResult> apply(\n      const gsl::not_null<tnsr::II<DataVector, 2>*> var2,\n      const gsl::not_null<tnsr::I<DataVector, 2>*> var3,\n      const Variables<tmpl::list<Var2, Var3>>& /*inhomogeneous_terms*/,\n      const double /*implicit_weight*/) {\n    CHECK(not performing_step_with_no_implicit_term);\n\n    for (auto& component : *var2) {\n      component *= 2.0;\n    }\n    for (auto& component : *var3) {\n      component *= 3.0;\n    }\n    return {};\n  }\n};\n\n// [source]\nstruct Source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                tt::ConformsTo<::protocols::StaticReturnApplyable> {\n  using return_tags = tmpl::list<::Tags::Source<Var2>, ::Tags::Source<Var3>>;\n  using argument_tags = tmpl::list<Var1, Var2, Var3, NonTensor,\n                                   RecordPreparersForTest, SomeComputeTagBase>;\n\n  static void apply(const gsl::not_null<tnsr::II<DataVector, 2>*> source_var2,\n                    const gsl::not_null<tnsr::I<DataVector, 2>*> source_var3,\n                    const Scalar<DataVector>& var1,\n                    const tnsr::II<DataVector, 2>& var2,\n                    const tnsr::I<DataVector, 2>& var3, const double non_tensor,\n                    const RecordPreparersForTest::type& prep_run_values,\n                    const double compute_tag_value) {\n    // [source]\n    CHECK(not performing_step_with_no_implicit_term);\n\n    const std::pair current_values{var2, var3};\n    CHECK(prep_run_values[static_cast<size_t>(PrepId::Shared)] ==\n          current_values);\n    CHECK(prep_run_values[static_cast<size_t>(PrepId::Source)] ==\n          current_values);\n\n    CHECK(compute_tag_value == get(var1)[0] + 1.0);\n\n    work(source_var2, source_var3, var1, var2, var3, non_tensor);\n  }\n\n  // Used in the test below.  Not part of the IMEX interface.\n  static void work(const gsl::not_null<tnsr::II<DataVector, 2>*> source_var2,\n                   const gsl::not_null<tnsr::I<DataVector, 2>*> source_var3,\n                   const Scalar<DataVector>& var1,\n                   const tnsr::II<DataVector, 2>& var2,\n                   const tnsr::I<DataVector, 2>& var3,\n                   const double non_tensor) {\n    tenex::evaluate<ti::I, ti::J>(\n        source_var2,\n        var3(ti::I) * var3(ti::J) - non_tensor * var2(ti::I, ti::J));\n    tenex::evaluate<ti::I>(source_var3, -var1() * var3(ti::I));\n  }\n};\nstatic_assert(\n    tt::assert_conforms_to_v<Source, imex::protocols::ImplicitSource>);\n\n// [Jacobian]\nstruct Jacobian : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n                  tt::ConformsTo<::protocols::StaticReturnApplyable> {\n  using return_tags =\n      tmpl::list<imex::Tags::Jacobian<Var2, ::Tags::Source<Var2>>,\n                 imex::Tags::Jacobian<Var3, ::Tags::Source<Var2>>,\n                 imex::Tags::Jacobian<Var3, ::Tags::Source<Var3>>>;\n  using argument_tags =\n      tmpl::list<Var1, Var3, NonTensor, RecordPreparersForTest>;\n\n  static void apply(const gsl::not_null<tnsr::iiJJ<DataVector, 2>*> dvar2_dvar2,\n                    const gsl::not_null<tnsr::iJJ<DataVector, 2>*> dvar2_dvar3,\n                    const gsl::not_null<tnsr::iJ<DataVector, 2>*> dvar3_dvar3,\n                    const Scalar<DataVector>& var1,\n                    const tnsr::I<DataVector, 2>& var3, const double non_tensor,\n                    const RecordPreparersForTest::type& prep_run_values) {\n    // [Jacobian]\n    CHECK(not performing_step_with_no_implicit_term);\n\n    // We don't need var2 for anything else in this function.  Hard to\n    // imagine a way not taking one of the variables as an argument\n    // could break anything, but easy to test so we do it.\n    CHECK(prep_run_values[static_cast<size_t>(PrepId::Shared)].second == var3);\n    CHECK(prep_run_values[static_cast<size_t>(PrepId::Jacobian)].second ==\n          var3);\n\n    std::fill(dvar2_dvar3->begin(), dvar2_dvar3->end(), 0.0);\n    for (size_t i = 0; i < 2; ++i) {\n      dvar2_dvar2->get(i, i, i, i) = -non_tensor;\n      dvar2_dvar3->get(i, i, i) = 2.0 * var3.get(i);\n      dvar3_dvar3->get(i, i) = -get(var1);\n      for (size_t j = 0; j < i; ++j) {\n        dvar2_dvar2->get(i, j, i, j) = -non_tensor;\n        dvar2_dvar3->get(i, i, j) += var3.get(j);\n        dvar2_dvar3->get(j, i, j) += var3.get(i);\n      }\n    }\n  }\n};\nstatic_assert(tt::assert_conforms_to_v<\n              Jacobian, imex::protocols::ImplicitSourceJacobian>);\n\ntemplate <bool TestWithAnalyticSolution>\nstruct ImplicitSector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var2, Var3>;\n  using initial_guess = tmpl::conditional_t<TestWithAnalyticSolution,\n                                            AnalyticSolution, InitialGuess>;\n\n  struct SolveAttempt {\n    using tags_from_evolution =\n        tmpl::list<Var1, NonTensor, VariablesFromEvolution>;\n    using simple_tags = tmpl::list<RecordPreparersForTest, VariablesTemporary>;\n    using compute_tags = tmpl::list<SomeComputeTag>;\n\n    using source_prep =\n        tmpl::list<Preparer<PrepId::Shared>, Preparer<PrepId::Source>>;\n    using jacobian_prep =\n        tmpl::list<Preparer<PrepId::Shared>, Preparer<PrepId::Jacobian>>;\n\n    using source = Source;\n    using jacobian =\n        tmpl::conditional_t<TestWithAnalyticSolution,\n                            imex::NoJacobianBecauseSolutionIsAnalytic,\n                            Jacobian>;\n  };\n\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\nstatic_assert(tt::assert_conforms_to_v<ImplicitSector<false>,\n                                       imex::protocols::ImplicitSector>);\n\n// ::tensors doesn't depend on the template parameter\nusing sector_variables_tag = Tags::Variables<ImplicitSector<false>::tensors>;\nusing SectorVariables = sector_variables_tag::type;\n\ntuples::TaggedTuple<sector_variables_tag, Var1, NonTensor,\n                    VariablesFromEvolution>\narbitrary_test_values() {\n  SectorVariables explicit_values(1);\n  tnsr::II<DataVector, 2>& var2 = get<Var2>(explicit_values);\n  get<0, 0>(var2) = 3.0;\n  get<0, 1>(var2) = 4.0;\n  get<1, 1>(var2) = 5.0;\n  tnsr::I<DataVector, 2>& var3 = get<Var3>(explicit_values);\n  get<0>(var3) = 6.0;\n  get<1>(var3) = 7.0;\n\n  Scalar<DataVector> var1{};\n  get(var1) = DataVector{8.0};\n  const double non_tensor = 9.0;\n  Variables<tmpl::list<TensorFromEvolution>> test_variables(1);\n  get<TensorFromEvolution>(test_variables)[0] = 2.0 * get(var1)[0];\n  return {std::move(explicit_values), std::move(var1), non_tensor,\n          std::move(test_variables)};\n}\n\ntemplate <bool TestWithAnalyticSolution>\nvoid test_test_sector() {\n  using sector = ImplicitSector<TestWithAnalyticSolution>;\n  auto values = arbitrary_test_values();\n  TestHelpers::imex::test_sector<sector>(\n      1.0e-1, 1.0e-12, std::move(get<sector_variables_tag>(values)),\n      {std::move(get<Var1>(values)), get<NonTensor>(values),\n       std::move(get<VariablesFromEvolution>(values))});\n}\n\nvoid test_internal_jacobian_ordering() {\n  // This test doesn't make sense on the analytic solution version.\n  using sector = ImplicitSector<false>;\n  using solve_attempt = sector::SolveAttempt;\n\n  const Slab slab(0.0, 1.0);\n  TimeSteppers::History<SectorVariables> history(2);\n  history.insert(TimeStepId(true, 0, slab.start()), decltype(history)::no_value,\n                 db::prefix_variables<Tags::dt, SectorVariables>(1, 3.0));\n\n  auto values = arbitrary_test_values();\n\n  SectorVariables variables = std::move(get<sector_variables_tag>(values));\n\n  const imex::solve_implicit_sector_detail::ImplicitEquation<SectorVariables>\n      equation(make_not_null(&variables), TimeSteppers::Heun2{},\n               slab.duration(), history, std::tuple<>{}, tmpl::type_<sector>{});\n  const auto evolution_data = std::forward_as_tuple(\n      std::as_const(get<Var1>(values)), std::as_const(get<NonTensor>(values)),\n      std::as_const(get<VariablesFromEvolution>(values)));\n  imex::solve_implicit_sector_detail::ImplicitSolver<sector, solve_attempt>\n      solver(equation, evolution_data);\n  solver.set_index(0);\n  std::array<double, SectorVariables::number_of_independent_components>\n      initial_guess{};\n  SectorVariables(initial_guess.data(), initial_guess.size()) = variables;\n  const auto jacobian = solver.jacobian(initial_guess);\n\n  auto deriv_approx = Approx::custom().epsilon(1.0e-12);\n  // gsl_multiroot wants jacobian[i][j] = dfi/dxj\n  for (size_t j = 0; j < initial_guess.size(); ++j) {\n    const auto derivative =\n        numerical_derivative(solver, initial_guess, j, 1.0e-1);\n    for (size_t i = 0; i < initial_guess.size(); ++i) {\n      CHECK(gsl::at(gsl::at(jacobian, i), j) == deriv_approx(derivative[i]));\n    }\n  }\n}\n\ntemplate <bool TestWithAnalyticSolution>\nvoid test_solve_implicit_sector(const imex::Mode solve_mode) {\n  using sector = ImplicitSector<TestWithAnalyticSolution>;\n  // No solve is done with an analytic solution.\n  const bool doing_semi_implicit_solve =\n      solve_mode == imex::Mode::SemiImplicit and not TestWithAnalyticSolution;\n  // We handle v1 entirely explicitly and v2, v3 entirely implicitly.\n  // The evolution equations for the latter two (coded in `Source`\n  // above) are\n  // d/dt[v2^ij] = v3^i v3^j - nt v2^ij\n  // d/dt[v3^i] = -v1 v3^i\n\n  using variables_tag = Tags::Variables<tmpl::list<Var1, Var2, Var3>>;\n  using implicit_variables_source_tag =\n      Tags::Variables<tmpl::list<::Tags::Source<Var2>, ::Tags::Source<Var3>>>;\n  using DtImplicitVariables =\n      Variables<tmpl::list<::Tags::dt<Var2>, ::Tags::dt<Var3>>>;\n  using history_tag = imex::Tags::ImplicitHistory<sector>;\n\n  const size_t number_of_grid_points = 5;\n  const Slab slab(3.0, 5.0);\n  const TimeStepId initial_time_step_id(true, 0, slab.start());\n  const auto time_step = Slab(3.0, 5.0).duration() / 3;\n\n  MAKE_GENERATOR(gen);\n  // Keep values positive to prevent the denominators in the analytic\n  // solution from becoming small.\n  std::uniform_real_distribution<double> dist(0.0, 5.0);\n  const auto initial_vars = make_with_random_values<variables_tag::type>(\n      make_not_null(&gen), make_not_null(&dist), number_of_grid_points);\n\n  // Perform updates as if taking an explicit step.\n  const auto simulate_explicit_step = [&dist, &gen, &initial_vars](\n                                          const auto box,\n                                          const TimeStepId& time_step_id) {\n    db::mutate<history_tag, Var1, Var2, Var3, VariablesFromEvolution>(\n        [&dist, &gen, &initial_vars, &time_step_id](\n            const gsl::not_null<typename history_tag::type*> history,\n            const gsl::not_null<Var1::type*> var1,\n            const gsl::not_null<Var2::type*> var2,\n            const gsl::not_null<Var3::type*> var3,\n            const gsl::not_null<VariablesFromEvolution::type*> test_variables,\n            const NonTensor::type& non_tensor) {\n          implicit_variables_source_tag::type source_vars(number_of_grid_points,\n                                                          0.0);\n          Source::work(&get<Tags::Source<Var2>>(source_vars),\n                       &get<Tags::Source<Var3>>(source_vars), *var1, *var2,\n                       *var3, non_tensor);\n\n          history->insert(\n              time_step_id, history_tag::type::no_value,\n              source_vars\n                  .reference_with_different_prefixes<DtImplicitVariables>());\n          // Update the explicitly evolved variable.\n          fill_with_random_values(var1, make_not_null(&gen),\n                                  make_not_null(&dist));\n          // The explicit time derivative for var2 and var3 is\n          // zero, so the explicit integration will consider them\n          // constant and reset them to the initial value.\n          *var2 = get<Var2>(initial_vars);\n          *var3 = get<Var3>(initial_vars);\n          // This isn't evolved but we test obtaining it from the\n          // evolution box by checking for this value.\n          test_variables->initialize(get(*var1).size());\n          get(get<TensorFromEvolution>(*test_variables)) = 2.0 * get(*var1);\n        },\n        box, db::get<NonTensor>(*box));\n  };\n\n  const auto non_tensor = make_with_random_values<double>(make_not_null(&gen),\n                                                          make_not_null(&dist));\n  auto box = db::create<\n      db::AddSimpleTags<variables_tag, NonTensor, VariablesFromEvolution,\n                        Tags::ConcreteTimeStepper<ImexTimeStepper>,\n                        Tags::TimeStep, history_tag, imex::Tags::Mode,\n                        imex::Tags::SolveFailures<sector>,\n                        imex::Tags::SolveTolerance>,\n      time_stepper_ref_tags<ImexTimeStepper>>(\n      initial_vars, non_tensor, VariablesFromEvolution::type{},\n      static_cast<std::unique_ptr<ImexTimeStepper>>(\n          std::make_unique<TimeSteppers::Heun2>()),\n      time_step, typename history_tag::type{2}, solve_mode,\n      Scalar<DataVector>(DataVector(number_of_grid_points, 0.0)), 1.0e-10);\n\n  simulate_explicit_step(make_not_null(&box), initial_time_step_id);\n\n  auto guess = initial_vars;\n  InitialGuess::apply(&get<Var2>(guess), &get<Var3>(guess), {}, {});\n\n  db::mutate_apply<imex::SolveImplicitSector<variables_tag, sector>>(\n      make_not_null(&box));\n  db::mutate<history_tag>(\n      [](const gsl::not_null<typename history_tag::type*> history,\n         const TimeStepper& stepper) { stepper.clean_history(history); },\n      make_not_null(&box), db::get<Tags::TimeStepper<TimeStepper>>(box));\n\n  const double dt = time_step.value();\n  const auto final_vars = db::get<variables_tag>(box);\n  Var2::type expected_var2{};\n  Var3::type expected_var3{};\n  Var2::type expected_var2_final{};\n  if (not doing_semi_implicit_solve) {\n    // The first implicit substep for the Heun stepper is\n    // y(dt) = y(0) + dt/2 (d/d[y(0)] + d/dt[y(dt)])\n\n    // The analytic solution for the result of the first substep is\n    // v3^i(dt) = v3^i(0) (1 - dt/2 v1(0)) / (1 + dt/2 v1(dt))\n    // v2^ij(dt) = (v2^ij(0) (1 - dt/2 nt) +\n    //              + dt/2 (v3^i(0) v3^j(0) + v3^i(dt) v3^j(dt)))\n    //             / (1 + dt/2 nt)\n\n    tenex::evaluate<ti::I>(make_not_null(&expected_var3),\n                           (1.0 - 0.5 * dt * get<Var1>(initial_vars)()) /\n                               (1.0 + 0.5 * dt * get<Var1>(final_vars)()) *\n                               get<Var3>(initial_vars)(ti::I));\n    tenex::evaluate<ti::I, ti::J>(\n        make_not_null(&expected_var2),\n        ((1.0 - 0.5 * dt * non_tensor) * get<Var2>(initial_vars)(ti::I, ti::J) +\n         0.5 * dt *\n             (get<Var3>(initial_vars)(ti::I) * get<Var3>(initial_vars)(ti::J) +\n              expected_var3(ti::I) * expected_var3(ti::J))) /\n            (1.0 + 0.5 * dt * non_tensor));\n\n    // The second implicit substep is simpler, since it isn't actually\n    // implicit, and is in fact the same as the first substep.\n    expected_var2_final = expected_var2;\n  } else {\n    // For a semi-implicit method, we expand the source term around\n    // the initial guess:\n    //\n    // S(y(dt)) = S(guess + (y(dt) - guess))\n    //   ~= S(guess) + J(guess) (y(dt) - guess)\n    //\n    // For Heun's method\n    //\n    // y(dt) = y(0) + dt/2 (d/d[y(0)] + d/dt[y(dt)])\n    //\n    // this gives the equation\n    //\n    // [1 - dt/2 J(guess)] (y(dt) - guess)\n    //     = y(0) - guess + dt/2 (d/d[y(0)] + S(guess))\n    //\n    // with\n    //\n    // S[v2^ij] = v3^i v3^j - nt v2^ij\n    // S[v3^i] = -v1 v3^i\n    // J[v2^ij] : dS[v2^ij]/dv2^kl = -nt delta^i_k delta^j_l\n    //            dS[v2^ij]/dv3^k = v3^i delta^j_k + v3^j delta^i_k\n    // J[v3^i] : dS[v3^i]/dv2^jk = 0\n    //           dS[v3^i]/dv3^j = -v1 delta^i_j\n    //\n    // This is fairly easy to solve as (denoting the guess as g2 and g3):\n    //\n    // v3^i(dt) = (1 - dt/2 v1(0)) / (1 + dt/2 v1(dt)) v3^i(0)\n    //     (solved exactly as the source is linear)\n    //\n    // v2^ij(dt) =\n    //    (1 - dt/2 nt) / (1 + dt/2 nt) v2^ij(0)\n    //    + dt/2 / (1 + dt/2 nt) [\n    //         v3^i(0) v3^j(0) + g3^i v3^j(dt) + v3^i(dt) g3^j - g3^i g3^j]\n\n    tenex::evaluate<ti::I>(make_not_null(&expected_var3),\n                           (1.0 - 0.5 * dt * get<Var1>(initial_vars)()) /\n                               (1.0 + 0.5 * dt * get<Var1>(final_vars)()) *\n                               get<Var3>(initial_vars)(ti::I));\n    tenex::evaluate<ti::I, ti::J>(\n        make_not_null(&expected_var2),\n        ((1.0 - 0.5 * dt * non_tensor) * get<Var2>(initial_vars)(ti::I, ti::J) +\n         0.5 * dt *\n             (get<Var3>(initial_vars)(ti::I) * get<Var3>(initial_vars)(ti::J) +\n              get<Var3>(guess)(ti::I) * expected_var3(ti::J) +\n              expected_var3(ti::I) * get<Var3>(guess)(ti::J) -\n              get<Var3>(guess)(ti::I) * get<Var3>(guess)(ti::J))) /\n            (1.0 + 0.5 * dt * non_tensor));\n\n    // The second substep is explicit, so doesn't do a semi-implicit\n    // solve, but still uses the results from the first substep.  Var3\n    // is again exact.\n    tenex::evaluate<ti::I, ti::J>(\n        make_not_null(&expected_var2_final),\n        (1.0 - 0.5 * dt * non_tensor) * get<Var2>(initial_vars)(ti::I, ti::J) +\n            0.5 * dt *\n                (get<Var3>(initial_vars)(ti::I) *\n                     get<Var3>(initial_vars)(ti::J) +\n                 expected_var3(ti::I) * expected_var3(ti::J) -\n                 non_tensor * expected_var2(ti::I, ti::J)));\n  }\n  CHECK_ITERABLE_APPROX(get<Var2>(final_vars), expected_var2);\n  CHECK_ITERABLE_APPROX(get<Var3>(final_vars), expected_var3);\n\n  CHECK(db::get<history_tag>(box).size() == 1);\n  CHECK(db::get<history_tag>(box).substeps().empty());\n\n  simulate_explicit_step(make_not_null(&box),\n                         initial_time_step_id.next_substep(time_step, 1.0));\n  performing_step_with_no_implicit_term = true;\n  db::mutate_apply<imex::SolveImplicitSector<variables_tag, sector>>(\n      make_not_null(&box));\n  performing_step_with_no_implicit_term = false;\n  db::mutate<history_tag>(\n      [](const gsl::not_null<typename history_tag::type*> history,\n         const TimeStepper& stepper) { stepper.clean_history(history); },\n      make_not_null(&box), db::get<Tags::TimeStepper<TimeStepper>>(box));\n  CHECK_ITERABLE_APPROX(get<Var2>(db::get<variables_tag>(box)),\n                        expected_var2_final);\n  CHECK_ITERABLE_APPROX(get<Var3>(db::get<variables_tag>(box)), expected_var3);\n\n  CHECK(db::get<history_tag>(box).size() == 1);\n  CHECK(db::get<history_tag>(box).substeps().size() == 1);\n\n  // Take another substep just to test the history cleanup.\n  simulate_explicit_step(make_not_null(&box),\n                         initial_time_step_id.next_step(time_step));\n  db::mutate_apply<imex::SolveImplicitSector<variables_tag, sector>>(\n      make_not_null(&box));\n  db::mutate<history_tag>(\n      [](const gsl::not_null<typename history_tag::type*> history,\n         const TimeStepper& stepper) { stepper.clean_history(history); },\n      make_not_null(&box), db::get<Tags::TimeStepper<TimeStepper>>(box));\n\n  CHECK(db::get<history_tag>(box).size() == 1);\n  CHECK(db::get<history_tag>(box).substeps().empty());\n}\n\nstruct ResettingTestSector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var1>;\n  using initial_guess = imex::GuessExplicitResult;\n\n  struct SolveAttempt {\n    using tags_from_evolution = tmpl::list<Var2>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<::Tags::Source<Var1>>;\n      using argument_tags = tmpl::list<Var2>;\n\n      static void apply(const gsl::not_null<Scalar<DataVector>*> source_var1,\n                        const tnsr::II<DataVector, 2>& var2) {\n        get(*source_var1) = get<0, 0>(var2);\n      }\n    };\n\n    struct jacobian : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n                  tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<>;\n      using argument_tags = tmpl::list<>;\n\n      static void apply() {}\n    };\n  };\n\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\n\n// There was a bug where internal cached values did not clear properly\n// between points.\nvoid test_point_reseting() {\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n\n  const Slab slab(0.0, 2.0);\n  const auto time_step = slab.duration();\n\n  auto var2 = make_with_value<tnsr::II<DataVector, 2>>(2_st, 0.0);\n  get<0, 0>(var2) = DataVector{1.0, 2.0};\n\n  // Set the initial value and derivative to zero so we can ignore\n  // those terms in the time stepper equation.\n  variables_tag::type initial_value(2, 0.0);  // NOLINT(misc-const-correctness)\n  TimeSteppers::History<variables_tag::type> history(2);\n  history.insert(TimeStepId(true, 0, slab.start()), decltype(history)::no_value,\n                 db::prefix_variables<Tags::dt, variables_tag::type>(2, 0.0));\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Var2, variables_tag, imex::Tags::ImplicitHistory<ResettingTestSector>,\n          imex::Tags::Mode, Tags::ConcreteTimeStepper<ImexTimeStepper>,\n          Tags::TimeStep, imex::Tags::SolveFailures<ResettingTestSector>,\n          imex::Tags::SolveTolerance>,\n      time_stepper_ref_tags<ImexTimeStepper>>(\n      var2, std::move(initial_value), std::move(history),\n      imex::Mode::SemiImplicit,\n      static_cast<std::unique_ptr<ImexTimeStepper>>(\n          std::make_unique<TimeSteppers::Heun2>()),\n      time_step, Scalar<DataVector>(DataVector(2, 0.0)), 1.0e-10);\n  db::mutate_apply<\n      imex::SolveImplicitSector<variables_tag, ResettingTestSector>>(\n      make_not_null(&box));\n\n  // The equation being solved is: y(dt) = dt/2 source(y(dt))\n  // where: dt = 2, source(y) = var2(0, 0)\n  CHECK_ITERABLE_APPROX(get(get<Var1>(box)), (get<0, 0>(var2)));\n}\n\nstruct DesiredLevel : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct SectorWithFallback : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var1>;\n  using initial_guess = imex::GuessExplicitResult;\n\n  template <int Level>\n  struct SolveAttempt {\n    using tags_from_evolution = tmpl::list<DesiredLevel>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<::Tags::Source<Var1>>;\n      using argument_tags = tmpl::list<DesiredLevel>;\n\n      static void apply(const gsl::not_null<Scalar<DataVector>*> source_var1,\n                        const Scalar<DataVector>& desired_level) {\n        CHECK(get(desired_level)[0] <= Level);\n        get(*source_var1) = Level;\n      }\n    };\n\n    struct jacobian : tt::ConformsTo<imex::protocols::ImplicitSourceJacobian>,\n                  tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags =\n          tmpl::list<imex::Tags::Jacobian<Var1, ::Tags::Source<Var1>>>;\n      using argument_tags = tmpl::list<DesiredLevel>;\n\n      static void apply(const gsl::not_null<Scalar<DataVector>*> dvar1_dvar1,\n                        const Scalar<DataVector>& desired_level) {\n        if (get(desired_level)[0] == Level) {\n          get(*dvar1_dvar1) = 0.0;\n        } else {\n          // Cause a failure from a non-invertable y = y + constant\n          get(*dvar1_dvar1) = 1.0;\n        }\n      }\n    };\n  };\n\n  using solve_attempts =\n      tmpl::list<SolveAttempt<4>, SolveAttempt<3>, SolveAttempt<2>,\n                 SolveAttempt<1>, SolveAttempt<0>>;\n};\n\nvoid test_fallback() {\n  using sector = SectorWithFallback;\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n  using history_tag = imex::Tags::ImplicitHistory<sector>;\n\n  const Slab slab(0.0, 2.0);\n  const auto time_step = slab.duration();\n  const Scalar<DataVector> desired_level{{{{1.0, 3.0, 4.0, 4.0, 3.0}}}};\n  const size_t number_of_grid_points = get(desired_level).size();\n\n  // Set the initial value and derivative to zero so we can ignore\n  // those terms in the time stepper equation.\n  // NOLINTNEXTLINE(misc-const-correctness)\n  variables_tag::type initial_value(number_of_grid_points, 0.0);\n  TimeSteppers::History<variables_tag::type> history(2);\n  history.insert(TimeStepId(true, 0, slab.start()), decltype(history)::no_value,\n                 db::prefix_variables<Tags::dt, variables_tag::type>(\n                     number_of_grid_points, 0.0));\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          DesiredLevel, variables_tag, history_tag, imex::Tags::Mode,\n          Tags::ConcreteTimeStepper<ImexTimeStepper>, Tags::TimeStep,\n          imex::Tags::SolveFailures<sector>, imex::Tags::SolveTolerance>,\n      time_stepper_ref_tags<ImexTimeStepper>>(\n      desired_level, std::move(initial_value), std::move(history),\n      imex::Mode::SemiImplicit,\n      static_cast<std::unique_ptr<ImexTimeStepper>>(\n          std::make_unique<TimeSteppers::Heun2>()),\n      time_step, Scalar<DataVector>(DataVector(number_of_grid_points, 0.0)),\n      1.0e-10);\n\n  // Jacobian is only right when the template parameter and DataVector\n  // agree.  (This is also a test of test_sector will fallbacks.)\n  TestHelpers::imex::test_sector<sector, sector::SolveAttempt<2>>(\n      1.0e-1, 1.0e-14, db::get<variables_tag>(box),\n      {Scalar<DataVector>{{{{2.0}}}}});\n\n  db::mutate_apply<imex::SolveImplicitSector<variables_tag, sector>>(\n      make_not_null(&box));\n\n  // The equation being solved is: y(dt) = dt/2 source(y(dt))\n  // where: dt = 2, source(y) = desired_level\n  CHECK_ITERABLE_APPROX(get(get<Var1>(box)), get(desired_level));\n  CHECK(get(get<imex::Tags::SolveFailures<sector>>(box)) ==\n        4.0 - get(desired_level));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Imex.SolveImplicitSector\",\n                  \"[Unit][Evolution]\") {\n  test_test_sector<false>();\n  test_test_sector<true>();\n  test_internal_jacobian_ordering();\n  test_solve_implicit_sector<false>(imex::Mode::Implicit);\n  test_solve_implicit_sector<true>(imex::Mode::Implicit);\n  test_solve_implicit_sector<false>(imex::Mode::SemiImplicit);\n  test_solve_implicit_sector<true>(imex::Mode::SemiImplicit);\n  test_point_reseting();\n  test_fallback();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Initialization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EvolutionInitialization\")\n\nset(LIBRARY_SOURCES\n  Test_ConservativeSystem.cpp\n  Test_DgDomain.cpp\n  Test_Evolution.cpp\n  Test_NonconservativeSystem.cpp\n  Test_SetVariables.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Amr\n  AmrProjectors\n  DataStructures\n  Domain\n  Evolution\n  Hydro\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Initialization/Test_ConservativeSystem.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/ConservativeSystem.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"Var\"; }\n};\n\nstruct PrimVar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"PrimVar\"; }\n};\n\nstruct SystemAnalyticSolution : public MarkAsAnalyticSolution {\n  const EquationsOfState::PolytropicFluid<true> equation_of_state_{100.0, 2.0};\n  const auto& equation_of_state() const { return equation_of_state_; }\n\n  // NOLINTNEXTLINE\n  void pup(PUP::er& /*p*/) {}\n};\n\ntemplate <size_t Dim, bool HasPrimitiveAndConservativeVars>\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = true;\n  static constexpr bool has_primitive_and_conservative_vars =\n      HasPrimitiveAndConservativeVars;\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = Tags::Variables<tmpl::list<Var>>;\n  using primitive_variables_tag = Tags::Variables<tmpl::list<PrimVar>>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tag_list =\n      tmpl::list<typename Metavariables::equation_of_state_tag>;\n\n  using initial_tags = tmpl::list<domain::Tags::Mesh<Dim>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<initial_tags>,\n                 Initialization::Actions::ConservativeSystem<\n                     typename Metavariables::system>>>>;\n};\n\ntemplate <size_t Dim, bool HasPrimitives>\nstruct Metavariables {\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using system = System<Dim, HasPrimitives>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::AnalyticSolution<SystemAnalyticSolution>>;\n\n  struct equation_of_state_tag : db::SimpleTag {\n    using type = std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>;\n  };\n};\n\ntemplate <size_t Dim, typename Runner>\nvoid check_primitives(std::true_type /*has_prims*/, const Runner& runner,\n                      const size_t number_of_grid_points) {\n  using metavars = Metavariables<Dim, true>;\n  using comp = component<Dim, metavars>;\n  using prim_vars_tag = Tags::Variables<tmpl::list<PrimVar>>;\n  SystemAnalyticSolution system_analytic_solution{};\n  CHECK(ActionTesting::get_databox_tag<comp, prim_vars_tag>(runner, 0)\n            .number_of_grid_points() == number_of_grid_points);\n}\n\ntemplate <size_t Dim, typename Runner>\nvoid check_primitives(std::false_type /*has_prims*/, const Runner& /*runner*/,\n                      const size_t /*number_of_grid_points*/) {}\n\ntemplate <size_t Dim, bool HasPrimitives>\nvoid test() {\n  using metavars = Metavariables<Dim, HasPrimitives>;\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{SystemAnalyticSolution{}}};\n  Mesh<Dim> mesh{5, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n  ActionTesting::emplace_component_and_initialize<comp>(&runner, 0, {mesh});\n  // Invoke the ConservativeSystem action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), 0);\n  using vars_tag = Tags::Variables<tmpl::list<Var>>;\n  // The numerical value that the vars are set to is undefined, but the number\n  // of grid points must be correct.\n  CHECK(ActionTesting::get_databox_tag<comp, vars_tag>(runner, 0)\n            .number_of_grid_points() == mesh.number_of_grid_points());\n  check_primitives<Dim>(std::integral_constant<bool, HasPrimitives>{}, runner,\n                        mesh.number_of_grid_points());\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Initialization.ConservativeSystem\",\n                  \"[Unit][Evolution][Actions]\") {\n  test<1, true>();\n  test<2, true>();\n  test<3, true>();\n  test<1, false>();\n  test<2, false>();\n  test<3, false>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Initialization/Test_DgDomain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/NonconformingSphericalShells.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::Tags {\nstruct FunctionsOfTimeInitialize;\n}  // namespace control_system::Tags\n\nnamespace {\ntemplate <size_t MeshDim>\nusing TranslationMap =\n    domain::CoordinateMaps::TimeDependent::Translation<MeshDim>;\n\nusing AffineMap = domain::CoordinateMaps::Affine;\nusing AffineMap2d =\n    domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\nusing AffineMap3d =\n    domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n\ntemplate <size_t MeshDim, typename SourceFrame, typename TargetFrame>\nusing TimeIndependentMap = tmpl::conditional_t<\n    MeshDim == 1, domain::CoordinateMap<SourceFrame, TargetFrame, AffineMap>,\n    tmpl::conditional_t<\n        MeshDim == 2,\n        domain::CoordinateMap<SourceFrame, TargetFrame, AffineMap2d>,\n        domain::CoordinateMap<SourceFrame, TargetFrame, AffineMap3d>>>;\n\ntemplate <size_t MeshDim>\nusing TimeDependentMap = domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                               TranslationMap<MeshDim>>;\n\ntemplate <size_t MeshDim, typename SourceFrame, typename TargetFrame>\nstruct CreateAffineMap;\n\ntemplate <typename SourceFrame, typename TargetFrame>\nstruct CreateAffineMap<1, SourceFrame, TargetFrame> {\n  static TimeIndependentMap<1, SourceFrame, TargetFrame> apply() {\n    return TimeIndependentMap<1, SourceFrame, TargetFrame>{\n        AffineMap{-1.0, 1.0, 2.0, 7.2}};\n  }\n};\n\ntemplate <typename SourceFrame, typename TargetFrame>\nstruct CreateAffineMap<2, SourceFrame, TargetFrame> {\n  static TimeIndependentMap<2, SourceFrame, TargetFrame> apply() {\n    return TimeIndependentMap<2, SourceFrame, TargetFrame>{\n        {AffineMap{-1.0, 1.0, -2.0, 2.2}, AffineMap{-1.0, 1.0, 2.0, 7.2}}};\n  }\n};\n\ntemplate <typename SourceFrame, typename TargetFrame>\nstruct CreateAffineMap<3, SourceFrame, TargetFrame> {\n  static TimeIndependentMap<3, SourceFrame, TargetFrame> apply() {\n    return TimeIndependentMap<3, SourceFrame, TargetFrame>{\n        {AffineMap{-1.0, 1.0, -2.0, 2.2}, AffineMap{-1.0, 1.0, 2.0, 7.2},\n         AffineMap{-1.0, 1.0, 1.0, 3.5}}};\n  }\n};\n\ntemplate <size_t MeshDim, typename SourceFrame, typename TargetFrame>\nTimeIndependentMap<MeshDim, SourceFrame, TargetFrame> create_affine_map() {\n  return CreateAffineMap<MeshDim, SourceFrame, TargetFrame>::apply();\n}\n\ntemplate <size_t MeshDim>\nTimeDependentMap<MeshDim> create_translation_map(\n    const std::string& f_of_t_name) {\n  return TimeDependentMap<MeshDim>{TranslationMap<MeshDim>{f_of_t_name}};\n}\n\nnamespace Actions {\nstruct IncrementTime {\n  template <typename DataBox, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent,\n            typename ArrayIndex>\n  static Parallel::iterable_action_return_t apply(\n      DataBox& box, const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Tags::Time>(\n        [](const gsl::not_null<double*> time) { *time += 1.2; },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<dim>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<dim>>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n\n  using simple_tags =\n      db::AddSimpleTags<domain::Tags::InitialExtents<dim>,\n                        domain::Tags::InitialRefinementLevels<dim>,\n                        evolution::dg::Tags::Quadrature, Tags::Time>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Initialization::Actions::InitializeItems<\n                         evolution::dg::Initialization::Domain<metavariables>>,\n                     Actions::IncrementTime, Actions::IncrementTime>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n  static constexpr size_t volume_dim = Dim;\n};\n\ntemplate <size_t Dim, bool TimeDependent>\nvoid test(const Spectral::Quadrature quadrature) {\n  CAPTURE(Dim);\n  CAPTURE(TimeDependent);\n  CAPTURE(quadrature);\n  using metavars = Metavariables<Dim>;\n  using component = Component<metavars>;\n\n  static_assert(\n      std::is_same_v<typename evolution::dg::Initialization::Domain<\n                         metavars>::mutable_global_cache_tags,\n                     tmpl::list<::domain::Tags::FunctionsOfTimeInitialize>>);\n  static_assert(std::is_same_v<\n                typename evolution::dg::Initialization::Domain<\n                    metavars, true>::mutable_global_cache_tags,\n                tmpl::list<control_system::Tags::FunctionsOfTimeInitialize>>);\n\n  PUPable_reg(SINGLE_ARG(\n      TimeIndependentMap<Dim, Frame::BlockLogical, Frame::Inertial>));\n  PUPable_reg(\n      SINGLE_ARG(TimeIndependentMap<Dim, Frame::BlockLogical, Frame::Grid>));\n  PUPable_reg(SINGLE_ARG(\n      TimeIndependentMap<Dim, Frame::ElementLogical, Frame::Inertial>));\n  PUPable_reg(\n      SINGLE_ARG(TimeIndependentMap<Dim, Frame::ElementLogical, Frame::Grid>));\n  PUPable_reg(TimeDependentMap<Dim>);\n  PUPable_reg(domain::FunctionsOfTime::PiecewisePolynomial<2>);\n\n  std::array<size_t, Dim> neighbor_extents{};\n  alg::iota(neighbor_extents, 2_st);\n  const std::vector<std::array<size_t, Dim>> initial_extents{\n      make_array<Dim>(4_st), neighbor_extents};\n  const std::vector<std::array<size_t, Dim>> initial_refinement{\n      make_array<Dim>(0_st), make_array<Dim>(0_st)};\n  const size_t num_pts = pow<Dim>(4_st);\n  const DataVector velocity{Dim, 3.6};\n  const double initial_time = 0.0;\n  const double expiration_time = 2.5;\n  const std::string function_of_time_name = \"Translation\";\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time.insert(std::make_pair(\n      function_of_time_name,\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time,\n          std::array<DataVector, 3>{{{Dim, 0.0}, velocity, {Dim, 0.0}}},\n          expiration_time)));\n\n  auto neighbor_orientation = OrientationMap<Dim>::create_aligned();\n  Mesh<Dim> expected_neighbor_mesh(2_st, Spectral::Basis::Legendre, quadrature);\n  if constexpr (Dim == 2) {\n    neighbor_orientation = OrientationMap<2>(\n        std::array{Direction<2>::upper_eta(), Direction<2>::lower_xi()});\n    expected_neighbor_mesh =\n        Mesh<2>({{3_st, 2_st}}, Spectral::Basis::Legendre, quadrature);\n  } else if constexpr (Dim == 3) {\n    neighbor_orientation = OrientationMap<3>(\n        std::array{Direction<3>::upper_eta(), Direction<3>::upper_zeta(),\n                   Direction<3>::upper_xi()});\n    expected_neighbor_mesh =\n        Mesh<3>({{3_st, 4_st, 2_st}}, Spectral::Basis::Legendre, quadrature);\n  }\n\n  DirectionMap<Dim, BlockNeighbors<Dim>> block_neighbors{};\n  DirectionMap<Dim, BlockNeighbors<Dim>> other_block_neighbors{};\n  block_neighbors.emplace(Direction<Dim>::upper_xi(),\n                          BlockNeighbors<Dim>{1, neighbor_orientation});\n  other_block_neighbors.emplace(\n      neighbor_orientation(Direction<Dim>::lower_xi()),\n      BlockNeighbors<Dim>{0, neighbor_orientation.inverse_map()});\n\n  std::vector<Block<Dim>> blocks{2};\n  blocks[0] = Block<Dim>{\n      std::make_unique<\n          TimeIndependentMap<Dim, Frame::BlockLogical, Frame::Inertial>>(\n          create_affine_map<Dim, Frame::BlockLogical, Frame::Inertial>()),\n      0, block_neighbors};\n  blocks[1] = Block<Dim>{\n      std::make_unique<\n          TimeIndependentMap<Dim, Frame::BlockLogical, Frame::Inertial>>(\n          create_affine_map<Dim, Frame::BlockLogical, Frame::Inertial>()),\n      1, other_block_neighbors};\n  Domain<Dim> domain{std::move(blocks)};\n\n  if (TimeDependent) {\n    domain.inject_time_dependent_map_for_block(\n        0, std::make_unique<TimeDependentMap<Dim>>(\n               create_translation_map<Dim>(function_of_time_name)));\n  }\n\n  const ElementId<Dim> self_id(0);\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {std::move(domain)}, {std::move(clone_unique_ptrs(functions_of_time))}};\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, self_id,\n      {initial_extents, initial_refinement, quadrature, initial_time});\n  runner.set_phase(Parallel::Phase::Testing);\n  CHECK(ActionTesting::get_next_action_index<component>(runner, self_id) == 0);\n  ActionTesting::next_action<component>(make_not_null(&runner), self_id);\n\n  const auto& neighbor_mesh =\n      ActionTesting::get_databox_tag<component,\n                                     domain::Tags::NeighborMesh<Dim>>(runner,\n                                                                      self_id);\n  CHECK(neighbor_mesh.size() == 1);\n  CHECK(neighbor_mesh.at({Direction<Dim>::upper_xi(), ElementId<Dim>(1)}) ==\n        expected_neighbor_mesh);\n\n  // Set up data to be used for checking correctness\n  const auto logical_to_grid_map =\n      create_affine_map<Dim, Frame::ElementLogical, Frame::Grid>();\n  const auto grid_to_inertial_map =\n      create_translation_map<Dim>(function_of_time_name);\n  const auto& logical_coords = ActionTesting::get_databox_tag<\n      component, domain::Tags::Coordinates<Dim, Frame::ElementLogical>>(\n      runner, self_id);\n\n  const auto check_domain_tags_time_dependent = [&functions_of_time,\n                                                 &grid_to_inertial_map,\n                                                 &logical_coords,\n                                                 &logical_to_grid_map, num_pts,\n                                                 &runner, &self_id,\n                                                 &velocity](const double time) {\n    REQUIRE(ActionTesting::get_databox_tag<component, Tags::Time>(\n                runner, self_id) == time);\n    CHECK(ActionTesting::get_databox_tag<\n              component, domain::Tags::Coordinates<Dim, Frame::Inertial>>(\n              runner, self_id) ==\n          grid_to_inertial_map(logical_to_grid_map(logical_coords), time,\n                               functions_of_time));\n\n    const auto expected_grid_coords = logical_to_grid_map(logical_coords);\n    const tnsr::I<DataVector, Dim, Frame::Inertial> expected_coords =\n        grid_to_inertial_map(expected_grid_coords, time, functions_of_time);\n\n    CHECK(ActionTesting::get_databox_tag<\n              component, domain::Tags::Coordinates<Dim, Frame::Grid>>(\n              runner, self_id) == expected_grid_coords);\n\n    const auto expected_logical_to_grid_inv_jacobian =\n        logical_to_grid_map.inv_jacobian(logical_coords);\n\n    CHECK(\n        ActionTesting::get_databox_tag<\n            component, domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                     Frame::Grid>>(\n            runner, self_id) == expected_logical_to_grid_inv_jacobian);\n\n    const InverseJacobian<DataVector, Dim, Frame::Grid, Frame::Inertial>\n        expected_inv_jacobian_grid_to_inertial =\n            grid_to_inertial_map.inv_jacobian(expected_grid_coords, time,\n                                              functions_of_time);\n\n    InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n        expected_logical_to_inertial_inv_jacobian{num_pts};\n\n    for (size_t logical_i = 0; logical_i < Dim; ++logical_i) {\n      for (size_t inertial_i = 0; inertial_i < Dim; ++inertial_i) {\n        expected_logical_to_inertial_inv_jacobian.get(logical_i, inertial_i) =\n            expected_logical_to_grid_inv_jacobian.get(logical_i, 0) *\n            expected_inv_jacobian_grid_to_inertial.get(0, inertial_i);\n        for (size_t grid_i = 1; grid_i < Dim; ++grid_i) {\n          expected_logical_to_inertial_inv_jacobian.get(logical_i,\n                                                        inertial_i) +=\n              expected_logical_to_grid_inv_jacobian.get(logical_i, grid_i) *\n              expected_inv_jacobian_grid_to_inertial.get(grid_i, inertial_i);\n        }\n      }\n    }\n\n    REQUIRE(\n        ActionTesting::get_databox_tag<\n            component, domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(\n            runner, self_id)\n            .has_value());\n    const auto& coordinates_mesh_velocity_and_jacobians =\n        *ActionTesting::get_databox_tag<\n            component, domain::Tags::CoordinatesMeshVelocityAndJacobians<Dim>>(\n            runner, self_id);\n\n    for (size_t i = 0; i < Dim; ++i) {\n      // Check that the `const_cast`s and set_data_ref inside the compute tag\n      // functions worked correctly\n      CHECK(ActionTesting::get_databox_tag<\n                component, domain::Tags::Coordinates<Dim, Frame::Inertial>>(\n                runner, self_id)\n                .get(i)\n                .data() ==\n            std::get<0>(coordinates_mesh_velocity_and_jacobians).get(i).data());\n    }\n    CHECK_ITERABLE_APPROX(\n        (ActionTesting::get_databox_tag<\n            component, domain::Tags::Coordinates<Dim, Frame::Inertial>>(\n            runner, self_id)),\n        expected_coords);\n\n    for (size_t i = 0;\n         i < ActionTesting::get_databox_tag<\n                 component, domain::Tags::InverseJacobian<\n                                Dim, Frame::ElementLogical, Frame::Inertial>>(\n                 runner, self_id)\n                 .size();\n         ++i) {\n      CHECK(ActionTesting::get_databox_tag<\n                component, domain::Tags::InverseJacobian<\n                               Dim, Frame::ElementLogical, Frame::Inertial>>(\n                runner, self_id)[i]\n                .data() !=\n            std::get<1>(coordinates_mesh_velocity_and_jacobians)[i].data());\n    }\n    CHECK_ITERABLE_APPROX(\n        (ActionTesting::get_databox_tag<\n            component, domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                     Frame::Inertial>>(\n            runner, self_id)),\n        expected_logical_to_inertial_inv_jacobian);\n\n    const Scalar<DataVector> expected_logical_to_inertial_det_inv_jacobian =\n        determinant(expected_logical_to_inertial_inv_jacobian);\n    CHECK_ITERABLE_APPROX(\n        (ActionTesting::get_databox_tag<\n            component, domain::Tags::DetInvJacobian<Frame::ElementLogical,\n                                                    Frame::Inertial>>(runner,\n                                                                      self_id)),\n        expected_logical_to_inertial_det_inv_jacobian);\n\n    const auto expected_coords_mesh_velocity_jacobians =\n        grid_to_inertial_map.coords_frame_velocity_jacobians(\n            ActionTesting::get_databox_tag<\n                component, domain::Tags::Coordinates<Dim, Frame::Grid>>(\n                runner, self_id),\n            ActionTesting::get_databox_tag<component, ::Tags::Time>(runner,\n                                                                    self_id),\n            ActionTesting::get_databox_tag<component,\n                                           domain::Tags::FunctionsOfTime>(\n                runner, self_id));\n\n    for (size_t i = 0; i < Dim; ++i) {\n      // Check that the `const_cast`s and set_data_ref inside the compute tag\n      // functions worked correctly\n      CHECK(ActionTesting::get_databox_tag<component,\n                                           domain::Tags::MeshVelocity<Dim>>(\n                runner, self_id)\n                ->get(i)\n                .data() ==\n            std::get<3>(coordinates_mesh_velocity_and_jacobians).get(i).data());\n    }\n    CHECK_ITERABLE_APPROX(\n        (ActionTesting::get_databox_tag<component,\n                                        domain::Tags::MeshVelocity<Dim>>(\n             runner, self_id))\n            .value(),\n        std::get<3>(expected_coords_mesh_velocity_jacobians));\n\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_APPROX(\n          (ActionTesting::get_databox_tag<component,\n                                          domain::Tags::MeshVelocity<Dim>>(\n               runner, self_id)\n               .value()\n               .get(i)),\n          DataVector(num_pts, gsl::at(velocity, i)));\n    }\n  };\n\n  const auto check_domain_tags_time_independent = [&logical_coords,\n                                                   &logical_to_grid_map,\n                                                   &runner, &self_id](\n                                                      const double time) {\n    const auto logical_to_inertial_map =\n        create_affine_map<Dim, Frame::ElementLogical, Frame::Inertial>();\n    REQUIRE(ActionTesting::get_databox_tag<component, Tags::Time>(\n                runner, self_id) == time);\n    CHECK(ActionTesting::get_databox_tag<\n              component, domain::Tags::Coordinates<Dim, Frame::Inertial>>(\n              runner, self_id) == logical_to_inertial_map(logical_coords));\n\n    const auto expected_grid_coords = logical_to_grid_map(logical_coords);\n    CHECK(ActionTesting::get_databox_tag<\n              component, domain::Tags::Coordinates<Dim, Frame::Grid>>(\n              runner, self_id) == expected_grid_coords);\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK(ActionTesting::get_databox_tag<\n                component, domain::Tags::Coordinates<Dim, Frame::Inertial>>(\n                runner, self_id)\n                .get(i) == expected_grid_coords.get(i));\n    }\n\n    for (size_t i = 0; i < Dim; ++i) {\n      // Check that the `const_cast`s and set_data_ref inside the compute\n      // tag functions worked correctly\n      CHECK(ActionTesting::get_databox_tag<\n                component, domain::Tags::Coordinates<Dim, Frame::Inertial>>(\n                runner, self_id)\n                .get(i)\n                .data() ==\n            ActionTesting::get_databox_tag<\n                component, domain::Tags::Coordinates<Dim, Frame::Grid>>(runner,\n                                                                        self_id)\n                .get(i)\n                .data());\n    }\n\n    const auto expected_logical_to_grid_inv_jacobian =\n        logical_to_grid_map.inv_jacobian(logical_coords);\n\n    CHECK(\n        ActionTesting::get_databox_tag<\n            component, domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                     Frame::Grid>>(\n            runner, self_id) == expected_logical_to_grid_inv_jacobian);\n\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          Frame::Inertial>\n        expected_logical_to_inertial_inv_jacobian =\n            logical_to_inertial_map.inv_jacobian(logical_coords);\n\n    CHECK_ITERABLE_APPROX(\n        (ActionTesting::get_databox_tag<\n            component, domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                     Frame::Inertial>>(\n            runner, self_id)),\n        expected_logical_to_inertial_inv_jacobian);\n    for (size_t i = 0;\n         i < ActionTesting::get_databox_tag<\n                 component, domain::Tags::InverseJacobian<\n                                Dim, Frame::ElementLogical, Frame::Inertial>>(\n                 runner, self_id)\n                 .size();\n         ++i) {\n      // Check that the `const_cast`s and set_data_ref inside the compute\n      // tag functions worked correctly\n      CHECK(ActionTesting::get_databox_tag<\n                component, domain::Tags::InverseJacobian<\n                               Dim, Frame::ElementLogical, Frame::Inertial>>(\n                runner, self_id)[i]\n                .data() ==\n            ActionTesting::get_databox_tag<\n                component, domain::Tags::InverseJacobian<\n                               Dim, Frame::ElementLogical, Frame::Grid>>(\n                runner, self_id)[i]\n                .data());\n    }\n\n    const Scalar<DataVector> expected_logical_to_inertial_det_inv_jacobian =\n        determinant(expected_logical_to_inertial_inv_jacobian);\n    CHECK_ITERABLE_APPROX(\n        (ActionTesting::get_databox_tag<\n            component, domain::Tags::DetInvJacobian<Frame::ElementLogical,\n                                                    Frame::Inertial>>(runner,\n                                                                      self_id)),\n        expected_logical_to_inertial_det_inv_jacobian);\n\n    CHECK_FALSE(ActionTesting::get_databox_tag<component,\n                                               domain::Tags::MeshVelocity<Dim>>(\n                    runner, self_id)\n                    .has_value());\n    CHECK_FALSE(ActionTesting::get_databox_tag<component,\n                                               domain::Tags::DivMeshVelocity>(\n                    runner, self_id)\n                    .has_value());\n  };\n\n  if (TimeDependent) {\n    check_domain_tags_time_dependent(0.0);\n\n    ActionTesting::next_action<component>(make_not_null(&runner), self_id);\n    check_domain_tags_time_dependent(1.2);\n\n    ActionTesting::next_action<component>(make_not_null(&runner), self_id);\n    check_domain_tags_time_dependent(2.4);\n  } else {\n    check_domain_tags_time_independent(0.0);\n\n    ActionTesting::next_action<component>(make_not_null(&runner), self_id);\n    check_domain_tags_time_independent(1.2);\n\n    ActionTesting::next_action<component>(make_not_null(&runner), self_id);\n    check_domain_tags_time_independent(2.4);\n  }\n}\n\nvoid test_nonconforming_blocks() {\n  using metavars = Metavariables<3>;\n  using component = Component<metavars>;\n\n  const auto creator = domain::creators::NonconformingSphericalShells(\n      2.0, 3.0, 4.0, 0, 0, 5, 7, 11, nullptr, nullptr);\n  auto domain = creator.create_domain();\n  const auto initial_refinement = creator.initial_refinement_levels();\n  const auto initial_extents = creator.initial_extents();\n  const ElementId<3> shell_id{6};\n  const Mesh<3> shell_mesh{\n      std::array{5_st, 8_st, 15_st},\n      std::array{Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n                 Spectral::Basis::SphericalHarmonic},\n      std::array{Spectral::Quadrature::GaussLobatto,\n                 Spectral::Quadrature::Gauss,\n                 Spectral::Quadrature::Equiangular}};\n  const Mesh<3> cubed_sphere_mesh{std::array{11_st, 11_st, 5_st},\n                                  Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto};\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{{std::move(domain)}};\n  for (size_t block_id = 0; block_id < 7; ++block_id) {\n    ActionTesting::emplace_component_and_initialize<component>(\n        &runner, ElementId<3>{block_id},\n        {initial_extents, initial_refinement,\n         Spectral::Quadrature::GaussLobatto, 0.0});\n  }\n  runner.set_phase(Parallel::Phase::Testing);\n  for (size_t block_id = 0; block_id < 7; ++block_id) {\n    const ElementId<3> element_id{block_id};\n    CHECK(ActionTesting::get_next_action_index<component>(runner, element_id) ==\n          0);\n    ActionTesting::next_action<component>(make_not_null(&runner), element_id);\n  }\n  for (size_t block_id = 0; block_id < 6; ++block_id) {\n    const ElementId<3> element_id{block_id};\n    const auto& cubed_sphere_neighbor_mesh =\n        ActionTesting::get_databox_tag<component,\n                                       domain::Tags::NeighborMesh<3>>(\n            runner, element_id);\n    CHECK(cubed_sphere_neighbor_mesh.size() == 5);\n    CHECK(cubed_sphere_neighbor_mesh.at(\n              {Direction<3>::upper_zeta(), shell_id}) == shell_mesh);\n  }\n\n  const auto& shell_neighbor_mesh =\n      ActionTesting::get_databox_tag<component, domain::Tags::NeighborMesh<3>>(\n          runner, shell_id);\n  CHECK(shell_neighbor_mesh.size() == 6);\n  for (size_t block_id = 0; block_id < 6; ++block_id) {\n    CHECK(shell_neighbor_mesh.at(\n              {Direction<3>::lower_xi(), ElementId<3>(block_id)}) ==\n          cubed_sphere_mesh);\n  }\n}\n\nnamespace test_projectors {\nstruct TestMetavariables {\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<1>>;\n};\n\nusing items_type =\n    tuples::TaggedTuple<Parallel::Tags::GlobalCache<TestMetavariables>,\n                        ::domain::Tags::ElementMap<1, Frame::Grid>,\n                        ::domain::CoordinateMaps::Tags::CoordinateMap<\n                            1, Frame::Grid, Frame::Inertial>,\n                        ::domain::Tags::Element<1>>;\n\nusing TranslationMap = domain::CoordinateMaps::TimeDependent::Translation<1>;\nusing AffineMap = domain::CoordinateMaps::Affine;\ntemplate <typename TargetFrame>\nusing TimeIndependentMap =\n    domain::CoordinateMap<Frame::BlockLogical, TargetFrame, AffineMap>;\nusing TimeDependentMap =\n    domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap>;\nusing GridToInertialMap =\n    domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 1>;\n\ntemplate <typename TargetFrame>\nTimeIndependentMap<TargetFrame> create_affine_map() {\n  return TimeIndependentMap<TargetFrame>{AffineMap{-1.0, 1.0, 2.0, 7.2}};\n}\n\ntemplate <bool IsTimeDependent>\nParallel::GlobalCache<TestMetavariables> make_global_cache() {\n  std::vector<Block<1>> blocks{1};\n  blocks[0] = Block<1>{std::make_unique<TimeIndependentMap<Frame::Inertial>>(\n                           create_affine_map<Frame::Inertial>()),\n                       0,\n                       {}};\n  Domain<1> domain{std::move(blocks)};\n\n  if (IsTimeDependent) {\n    domain.inject_time_dependent_map_for_block(\n        0, std::make_unique<TimeDependentMap>(\n               TimeDependentMap(TranslationMap(\"Translation\"))));\n  }\n\n  tuples::TaggedTuple<domain::Tags::Domain<1>> const_global_cache_items(\n      std::move(domain));\n\n  return {std::move(const_global_cache_items)};\n}\n\ntemplate <bool IsTimeDependent>\nvoid check_maps(const ElementMap<1, Frame::Grid>& element_map,\n                const GridToInertialMap& grid_to_inertial_map) {\n  const auto expected_block_map = create_affine_map<Frame::Grid>();\n  CHECK(are_maps_equal(expected_block_map, element_map.block_map()));\n  if constexpr (IsTimeDependent) {\n    const auto expected_grid_to_inertial_map =\n        TimeDependentMap{TranslationMap(\"Translation\")};\n    CHECK(are_maps_equal(expected_grid_to_inertial_map, grid_to_inertial_map));\n  } else {\n    const auto expected_grid_to_inertial_map =\n        ::domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n            ::domain::CoordinateMaps::Identity<1>{});\n    CHECK(are_maps_equal(expected_grid_to_inertial_map, grid_to_inertial_map));\n  }\n}\n\ntemplate <bool IsTimeDependent>\nvoid test_p_refine() {\n  auto global_cache = make_global_cache<IsTimeDependent>();\n  const ElementId<1> element_id{0};\n  Element<1> element{element_id, DirectionMap<1, Neighbors<1>>{}};\n  const Domain<1>& domain = get<::domain::Tags::Domain<1>>(global_cache);\n  const auto& my_block = domain.blocks()[element_id.block_id()];\n  ElementMap<1, Frame::Grid> element_map{\n      element_id, my_block.is_time_dependent()\n                      ? my_block.moving_mesh_logical_to_grid_map().get_clone()\n                      : my_block.stationary_map().get_to_grid_frame()};\n  std::unique_ptr<GridToInertialMap> grid_to_inertial_map = nullptr;\n  if (my_block.is_time_dependent()) {\n    grid_to_inertial_map =\n        my_block.moving_mesh_grid_to_inertial_map().get_clone();\n  } else {\n    grid_to_inertial_map =\n        ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            ::domain::CoordinateMaps::Identity<1>{});\n  }\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::GlobalCache<TestMetavariables>,\n                        ::domain::Tags::ElementMap<1, Frame::Grid>,\n                        ::domain::CoordinateMaps::Tags::CoordinateMap<\n                            1, Frame::Grid, Frame::Inertial>,\n                        ::domain::Tags::Element<1>>,\n      tmpl::list<Parallel::Tags::FromGlobalCache<domain::Tags::Domain<1>,\n                                                 TestMetavariables>>>(\n      &global_cache, std::move(element_map), std::move(grid_to_inertial_map),\n      std::move(element));\n\n  const Mesh<1> mesh{2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  db::mutate_apply<evolution::dg::Initialization::ProjectDomain<1>>(\n      make_not_null(&box), std::make_pair(mesh, element));\n  check_maps<IsTimeDependent>(\n      db::get<::domain::Tags::ElementMap<1, Frame::Grid>>(box),\n      db::get<::domain::CoordinateMaps::Tags::CoordinateMap<1, Frame::Grid,\n                                                            Frame::Inertial>>(\n          box));\n}\n\ntemplate <bool IsTimeDependent>\nvoid test_split() {\n  auto global_cache = make_global_cache<IsTimeDependent>();\n\n  const ElementId<1> parent_id{0};\n  const ElementId<1> child_1_id{0, std::array{SegmentId{1, 0}}};\n  const ElementId<1> child_2_id{0, std::array{SegmentId{1, 1}}};\n\n  Element<1> parent{parent_id, DirectionMap<1, Neighbors<1>>{}};\n  const Domain<1>& domain = get<::domain::Tags::Domain<1>>(global_cache);\n  const auto& my_block = domain.blocks()[parent_id.block_id()];\n  ElementMap<1, Frame::Grid> element_map{\n      parent_id, my_block.is_time_dependent()\n                     ? my_block.moving_mesh_logical_to_grid_map().get_clone()\n                     : my_block.stationary_map().get_to_grid_frame()};\n  std::unique_ptr<GridToInertialMap> grid_to_inertial_map = nullptr;\n  if (my_block.is_time_dependent()) {\n    grid_to_inertial_map =\n        my_block.moving_mesh_grid_to_inertial_map().get_clone();\n  } else {\n    grid_to_inertial_map =\n        ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            ::domain::CoordinateMaps::Identity<1>{});\n  }\n  const items_type parent_items{&global_cache, std::move(element_map),\n                                std::move(grid_to_inertial_map),\n                                std::move(parent)};\n\n  Element<1> child_1{child_1_id,\n                     DirectionMap<1, Neighbors<1>>{std::pair{\n                         Direction<1>::upper_xi(),\n                         Neighbors<1>{std::unordered_set{child_2_id},\n                                      OrientationMap<1>::create_aligned()}}}};\n  auto child_1_box = db::create<\n      db::AddSimpleTags<Parallel::Tags::GlobalCache<TestMetavariables>,\n                        ::domain::Tags::ElementMap<1, Frame::Grid>,\n                        ::domain::CoordinateMaps::Tags::CoordinateMap<\n                            1, Frame::Grid, Frame::Inertial>,\n                        ::domain::Tags::Element<1>>,\n      tmpl::list<Parallel::Tags::FromGlobalCache<domain::Tags::Domain<1>,\n                                                 TestMetavariables>>>(\n      &global_cache, ElementMap<1, Frame::Grid>{},\n      std::unique_ptr<GridToInertialMap>{nullptr}, std::move(child_1));\n\n  db::mutate_apply<evolution::dg::Initialization::ProjectDomain<1>>(\n      make_not_null(&child_1_box), parent_items);\n  check_maps<IsTimeDependent>(\n      db::get<::domain::Tags::ElementMap<1, Frame::Grid>>(child_1_box),\n      db::get<::domain::CoordinateMaps::Tags::CoordinateMap<1, Frame::Grid,\n                                                            Frame::Inertial>>(\n          child_1_box));\n\n  Element<1> child_2{child_2_id,\n                     DirectionMap<1, Neighbors<1>>{std::pair{\n                         Direction<1>::lower_xi(),\n                         Neighbors<1>{std::unordered_set{child_1_id},\n                                      OrientationMap<1>::create_aligned()}}}};\n  auto child_2_box = db::create<\n      db::AddSimpleTags<Parallel::Tags::GlobalCache<TestMetavariables>,\n                        ::domain::Tags::ElementMap<1, Frame::Grid>,\n                        ::domain::CoordinateMaps::Tags::CoordinateMap<\n                            1, Frame::Grid, Frame::Inertial>,\n                        ::domain::Tags::Element<1>>,\n      tmpl::list<Parallel::Tags::FromGlobalCache<domain::Tags::Domain<1>,\n                                                 TestMetavariables>>>(\n      &global_cache, ElementMap<1, Frame::Grid>{},\n      std::unique_ptr<GridToInertialMap>{nullptr}, std::move(child_2));\n\n  db::mutate_apply<evolution::dg::Initialization::ProjectDomain<1>>(\n      make_not_null(&child_2_box), parent_items);\n  check_maps<IsTimeDependent>(\n      db::get<::domain::Tags::ElementMap<1, Frame::Grid>>(child_2_box),\n      db::get<::domain::CoordinateMaps::Tags::CoordinateMap<1, Frame::Grid,\n                                                            Frame::Inertial>>(\n          child_2_box));\n}\n\ntemplate <bool IsTimeDependent>\nvoid test_join() {\n  auto global_cache = make_global_cache<IsTimeDependent>();\n\n  const ElementId<1> parent_id{0};\n  const ElementId<1> child_1_id{0, std::array{SegmentId{1, 0}}};\n  const ElementId<1> child_2_id{0, std::array{SegmentId{1, 1}}};\n\n  Element<1> child_1{child_1_id,\n                     DirectionMap<1, Neighbors<1>>{std::pair{\n                         Direction<1>::upper_xi(),\n                         Neighbors<1>{std::unordered_set{child_2_id},\n                                      OrientationMap<1>::create_aligned()}}}};\n  Element<1> child_2{child_2_id,\n                     DirectionMap<1, Neighbors<1>>{std::pair{\n                         Direction<1>::lower_xi(),\n                         Neighbors<1>{std::unordered_set{child_1_id},\n                                      OrientationMap<1>::create_aligned()}}}};\n  const Domain<1>& domain = get<::domain::Tags::Domain<1>>(global_cache);\n  const auto& my_block = domain.blocks()[child_1_id.block_id()];\n  ElementMap<1, Frame::Grid> element_map_1{\n      child_1_id, my_block.is_time_dependent()\n                      ? my_block.moving_mesh_logical_to_grid_map().get_clone()\n                      : my_block.stationary_map().get_to_grid_frame()};\n  ElementMap<1, Frame::Grid> element_map_2{\n      child_2_id, my_block.is_time_dependent()\n                      ? my_block.moving_mesh_logical_to_grid_map().get_clone()\n                      : my_block.stationary_map().get_to_grid_frame()};\n  std::unique_ptr<GridToInertialMap> grid_to_inertial_map_1 = nullptr;\n  std::unique_ptr<GridToInertialMap> grid_to_inertial_map_2 = nullptr;\n  if (my_block.is_time_dependent()) {\n    grid_to_inertial_map_1 =\n        my_block.moving_mesh_grid_to_inertial_map().get_clone();\n    grid_to_inertial_map_2 =\n        my_block.moving_mesh_grid_to_inertial_map().get_clone();\n  } else {\n    grid_to_inertial_map_1 =\n        ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            ::domain::CoordinateMaps::Identity<1>{});\n    grid_to_inertial_map_2 =\n        ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n            ::domain::CoordinateMaps::Identity<1>{});\n  }\n  std::unordered_map<ElementId<1>, items_type> children_items;\n  children_items.emplace(\n      child_1_id,\n      items_type{&global_cache, std::move(element_map_1),\n                 std::move(grid_to_inertial_map_1), std::move(child_1)});\n  children_items.emplace(\n      child_2_id,\n      items_type{&global_cache, std::move(element_map_2),\n                 std::move(grid_to_inertial_map_2), std::move(child_2)});\n\n  Element<1> parent{parent_id, DirectionMap<1, Neighbors<1>>{}};\n  auto parent_box = db::create<\n      db::AddSimpleTags<Parallel::Tags::GlobalCache<TestMetavariables>,\n                        ::domain::Tags::ElementMap<1, Frame::Grid>,\n                        ::domain::CoordinateMaps::Tags::CoordinateMap<\n                            1, Frame::Grid, Frame::Inertial>,\n                        ::domain::Tags::Element<1>>,\n      tmpl::list<Parallel::Tags::FromGlobalCache<domain::Tags::Domain<1>,\n                                                 TestMetavariables>>>(\n      &global_cache, ElementMap<1, Frame::Grid>{},\n      std::unique_ptr<GridToInertialMap>{nullptr}, std::move(parent));\n  db::mutate_apply<evolution::dg::Initialization::ProjectDomain<1>>(\n      make_not_null(&parent_box), children_items);\n  check_maps<IsTimeDependent>(\n      db::get<::domain::Tags::ElementMap<1, Frame::Grid>>(parent_box),\n      db::get<::domain::CoordinateMaps::Tags::CoordinateMap<1, Frame::Grid,\n                                                            Frame::Inertial>>(\n          parent_box));\n}\n}  // namespace test_projectors\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Initialization.DgDomain\",\n                  \"[Parallel][Unit]\") {\n  for (const auto quadrature :\n       {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}) {\n    test<1, true>(quadrature);\n    test<2, true>(quadrature);\n    test<3, true>(quadrature);\n\n    test<1, false>(quadrature);\n    test<2, false>(quadrature);\n    test<3, false>(quadrature);\n  }\n\n  domain::creators::register_derived_with_charm();\n  test_nonconforming_blocks();\n\n  static_assert(\n      tt::assert_conforms_to_v<evolution::dg::Initialization::ProjectDomain<1>,\n                               amr::protocols::Projector>);\n  test_projectors::test_p_refine<true>();\n  test_projectors::test_p_refine<false>();\n  test_projectors::test_split<true>();\n  test_projectors::test_split<false>();\n  test_projectors::test_join<true>();\n  test_projectors::test_join<false>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Initialization/Test_Evolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <limits>\n#include <memory>\n#include <numeric>\n#include <optional>\n#include <random>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeSteppingPolicy.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Tags/ArrayIndex.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Variables.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/ChooseLtsStepSize.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/LimitIncrease.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/StepNumberWithinSlab.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Rational.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename TimeStepperType>\nstruct TestMetavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<StepChoosers::LimitIncrease>>>;\n  };\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::ConcreteTimeStepper<TimeStepperType>>;\n};\n\nvoid test_gts() {\n  const double initial_time = 1.5;\n  const double initial_dt = 0.5;\n  const double initial_slab_size = initial_dt;\n  std::unique_ptr<TimeStepper> time_stepper =\n      std::make_unique<TimeSteppers::AdamsBashforth>(5);\n\n  const Slab initial_slab =\n      Slab::with_duration_from_start(initial_time, initial_slab_size);\n  const Time time = initial_slab.start();\n  const TimeStepId expected_next_time_step_id = TimeStepId(\n      true, -static_cast<int64_t>(time_stepper->number_of_past_steps()), time);\n  const TimeDelta expected_time_step = time.slab().duration();\n\n  tuples::TaggedTuple<::Tags::ConcreteTimeStepper<TimeStepper>>\n      const_global_cache_items(std::move(time_stepper));\n\n  Parallel::GlobalCache<TestMetavariables<TimeStepper>> global_cache(\n      std::move(const_global_cache_items));\n  auto box = db::create<\n      db::AddSimpleTags<\n          Parallel::Tags::GlobalCache<TestMetavariables<TimeStepper>>,\n          ::Tags::Time, Initialization::Tags::InitialTimeDelta,\n          Initialization::Tags::InitialSlabSize<false>,\n          ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n          ::Tags::ChangeSlabSize::SlabSizeGoal>,\n      tmpl::list<Parallel::Tags::FromGlobalCache<\n          ::Tags::ConcreteTimeStepper<TimeStepper>,\n          TestMetavariables<TimeStepper>>>>(\n      &global_cache, initial_time, initial_dt, initial_slab_size, TimeStepId{},\n      TimeDelta{}, std::numeric_limits<double>::signaling_NaN());\n\n  db::mutate_apply<Initialization::TimeStepping<TestMetavariables<TimeStepper>,\n                                                TimeStepper>>(\n      make_not_null(&box));\n\n  CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) ==\n        expected_next_time_step_id);\n  CHECK(db::get<::Tags::TimeStep>(box) == expected_time_step);\n  CHECK(db::get<::Tags::ChangeSlabSize::SlabSizeGoal>(box) ==\n        initial_slab_size);\n}\n\nvoid test_lts() {\n  const double initial_time = 1.5;\n  const double initial_dt = 0.5;\n  const double initial_slab_size = 4.5;\n  std::unique_ptr<LtsTimeStepper> lts_time_stepper =\n      std::make_unique<TimeSteppers::AdamsBashforth>(5);\n\n  const Slab initial_slab =\n      Slab::with_duration_from_start(initial_time, initial_slab_size);\n  const Time time = initial_slab.start();\n  const TimeStepId expected_next_time_step_id = TimeStepId(\n      true, -static_cast<int64_t>(lts_time_stepper->number_of_past_steps()),\n      time);\n  const TimeDelta expected_time_step = choose_lts_step_size(time, initial_dt);\n\n  tuples::TaggedTuple<::Tags::ConcreteTimeStepper<LtsTimeStepper>>\n      const_global_cache_items(std::move(lts_time_stepper));\n\n  Parallel::GlobalCache<TestMetavariables<LtsTimeStepper>> global_cache(\n      std::move(const_global_cache_items));\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Parallel::Tags::GlobalCache<TestMetavariables<LtsTimeStepper>>,\n          ::Tags::Time, Initialization::Tags::InitialTimeDelta,\n          Initialization::Tags::InitialSlabSize<true>,\n          ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n          ::Tags::ChangeSlabSize::SlabSizeGoal>,\n      tmpl::list<Parallel::Tags::FromGlobalCache<\n          ::Tags::ConcreteTimeStepper<LtsTimeStepper>,\n          TestMetavariables<LtsTimeStepper>>>>(\n      &global_cache, initial_time, initial_dt, initial_slab_size, TimeStepId{},\n      TimeDelta{}, std::numeric_limits<double>::signaling_NaN());\n\n  db::mutate_apply<Initialization::TimeStepping<\n      TestMetavariables<LtsTimeStepper>, LtsTimeStepper>>(make_not_null(&box));\n\n  CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) ==\n        expected_next_time_step_id);\n  CHECK(db::get<::Tags::TimeStep>(box) == expected_time_step);\n  CHECK(db::get<::Tags::ChangeSlabSize::SlabSizeGoal>(box) ==\n        initial_slab_size);\n}\nusing items_type = tuples::TaggedTuple<\n    Parallel::Tags::ArrayIndex<ElementId<1>>, ::Tags::TimeStepId,\n    ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep, ::Tags::Time,\n    ::Tags::StepNumberWithinSlab, ::Tags::AdaptiveSteppingDiagnostics,\n    ::Tags::ChangeSlabSize::SlabSizeGoal>;\n\nusing parent_items_type = tuples::TaggedTuple<\n    Parallel::Tags::ArrayIndex<ElementId<1>>, ::Tags::TimeStepId,\n    ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep, ::Tags::Time,\n    ::Tags::StepNumberWithinSlab, ::Tags::AdaptiveSteppingDiagnostics,\n    ::Tags::ChangeSlabSize::SlabSizeGoal, ::amr::Tags::Info<1>>;\n\ntemplate <typename DbTagList>\nvoid check(const db::DataBox<DbTagList>& box,\n           const TimeStepId& expected_time_step_id,\n           const TimeStepId& expected_next_time_step_id,\n           const TimeDelta& expected_time_step, const double expected_time,\n           const uint64_t expected_step_number_within_slab,\n           const AdaptiveSteppingDiagnostics& expected_diagnostics,\n           const double expected_slab_size_goal) {\n  CHECK(db::get<::Tags::TimeStepId>(box) == expected_time_step_id);\n  CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) ==\n        expected_next_time_step_id);\n  CHECK(db::get<::Tags::TimeStep>(box) == expected_time_step);\n  CHECK(db::get<::Tags::Time>(box) == expected_time);\n  CHECK(db::get<::Tags::StepNumberWithinSlab>(box) ==\n        expected_step_number_within_slab);\n  CHECK(db::get<::Tags::AdaptiveSteppingDiagnostics>(box) ==\n        expected_diagnostics);\n  CHECK(db::get<::Tags::ChangeSlabSize::SlabSizeGoal>(box) ==\n        expected_slab_size_goal);\n}\n\nvoid test_p_refine() {\n  const ElementId<1> element_id{0};\n  const Element<1> element{element_id, DirectionMap<1, Neighbors<1>>{}};\n  const Mesh<1> mesh{2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Slab slab(0., 1.);\n  const Time start{slab.start()};\n  const TimeDelta time_step{slab.duration()};\n  const TimeStepId time_step_id{time_step.is_positive(), 8, start};\n  const TimeStepId next_time_step_id{time_step.is_positive(), 8,\n                                     start + time_step};\n  const double time = start.value();\n  const uint64_t step_number_within_slab{0};\n  const AdaptiveSteppingDiagnostics diagnostics{7, 2, 13, 4, 5};\n  const double slab_size_goal = 1.34;\n\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::ArrayIndex<ElementId<1>>, ::Tags::TimeStepId,\n      ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep, ::Tags::Time,\n      ::Tags::StepNumberWithinSlab, ::Tags::AdaptiveSteppingDiagnostics,\n      ::Tags::ChangeSlabSize::SlabSizeGoal>>(\n      element_id, time_step_id, next_time_step_id, time_step, time,\n      step_number_within_slab, diagnostics, slab_size_goal);\n\n  db::mutate_apply<Initialization::ProjectTimeStepping<1>>(\n      make_not_null(&box), std::make_pair(mesh, element));\n\n  check(box, time_step_id, next_time_step_id, time_step, time,\n        step_number_within_slab, diagnostics, slab_size_goal);\n}\n\nvoid test_split() {\n  const ElementId<1> parent_id{0};\n  const ElementId<1> child_1_id{0, std::array{SegmentId{1, 0}}};\n  const ElementId<1> child_2_id{0, std::array{SegmentId{1, 1}}};\n\n  const Slab slab(1., 1.5);\n  const Time start{slab.start()};\n  const TimeDelta time_step{slab.duration()};\n  const TimeStepId time_step_id{time_step.is_positive(), 8, start};\n  const TimeStepId next_time_step_id{time_step.is_positive(), 8,\n                                     start + time_step};\n  const double time = start.value();\n  const uint64_t step_number_within_slab{0};\n  const AdaptiveSteppingDiagnostics diagnostics{7, 2, 13, 4, 5};\n  const double slab_size_goal = 1.34;\n\n  const parent_items_type parent_items{\n      parent_id,\n      time_step_id,\n      next_time_step_id,\n      time_step,\n      time,\n      step_number_within_slab,\n      diagnostics,\n      slab_size_goal,\n      ::amr::Info<1>{std::array{::amr::Flag::Split}, Mesh<1>{}}};\n\n  auto child_1_box = db::create<db::AddSimpleTags<\n      Parallel::Tags::ArrayIndex<ElementId<1>>, ::Tags::TimeStepId,\n      ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep, ::Tags::Time,\n      ::Tags::StepNumberWithinSlab, ::Tags::AdaptiveSteppingDiagnostics,\n      ::Tags::ChangeSlabSize::SlabSizeGoal>>(\n      child_1_id, TimeStepId{}, TimeStepId{}, TimeDelta{}, 0.0,\n      std::numeric_limits<uint64_t>::max(), AdaptiveSteppingDiagnostics{},\n      std::numeric_limits<double>::signaling_NaN());\n\n  auto child_2_box = db::create<db::AddSimpleTags<\n      Parallel::Tags::ArrayIndex<ElementId<1>>, ::Tags::TimeStepId,\n      ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep, ::Tags::Time,\n      ::Tags::StepNumberWithinSlab, ::Tags::AdaptiveSteppingDiagnostics,\n      ::Tags::ChangeSlabSize::SlabSizeGoal>>(\n      child_2_id, TimeStepId{}, TimeStepId{}, TimeDelta{}, 0.0,\n      std::numeric_limits<uint64_t>::max(), AdaptiveSteppingDiagnostics{},\n      std::numeric_limits<double>::signaling_NaN());\n\n  db::mutate_apply<Initialization::ProjectTimeStepping<1>>(\n      make_not_null(&child_1_box), parent_items);\n\n  check(child_1_box, time_step_id, next_time_step_id, time_step, time,\n        step_number_within_slab, diagnostics, slab_size_goal);\n\n  db::mutate_apply<Initialization::ProjectTimeStepping<1>>(\n      make_not_null(&child_2_box), parent_items);\n\n  check(child_2_box, time_step_id, next_time_step_id, time_step, time,\n        step_number_within_slab, AdaptiveSteppingDiagnostics{7, 2, 0, 0, 0},\n        slab_size_goal);\n}\n\ntemplate <bool ForwardInTime>\nvoid test_join() {\n  const ElementId<1> parent_id{0};\n  const ElementId<1> child_1_id{0, std::array{SegmentId{1, 0}}};\n  const ElementId<1> child_2_id{0, std::array{SegmentId{1, 1}}};\n\n  const Slab slab_1(1., 1.5);\n  const Time start_1{ForwardInTime ? slab_1.start() : slab_1.end()};\n  const TimeDelta time_step_1{ForwardInTime ? slab_1.duration()\n                                            : -slab_1.duration()};\n  const TimeStepId time_step_id_1{time_step_1.is_positive(), 8, start_1};\n  const TimeStepId next_time_step_id_1{time_step_1.is_positive(), 8,\n                                       start_1 + time_step_1};\n  const double time_1 = start_1.value();\n  const uint64_t step_number_within_slab_1{0};\n  const AdaptiveSteppingDiagnostics diagnostics_1{7, 2, 13, 4, 5};\n  const double slab_size_goal_1 = 1.34;\n\n  const Slab slab_2(1., 1.5);\n  const Time start_2{ForwardInTime ? slab_2.start() : slab_2.end()};\n  const TimeDelta time_step_2{slab_2, Rational{ForwardInTime ? 1 : -1, 2}};\n  const TimeStepId time_step_id_2{time_step_2.is_positive(), 8, start_2};\n  const TimeStepId next_time_step_id_2{time_step_2.is_positive(), 8,\n                                       start_2 + time_step_2};\n  const double time_2 = start_2.value();\n  const uint64_t step_number_within_slab_2 = step_number_within_slab_1;\n  const AdaptiveSteppingDiagnostics diagnostics_2{7, 2, 27, 2, 8};\n  const double slab_size_goal_2 = slab_size_goal_1;\n\n  std::unordered_map<ElementId<1>, items_type> children_items;\n  children_items.emplace(\n      child_1_id,\n      items_type{child_1_id, time_step_id_1, next_time_step_id_1, time_step_1,\n                 time_1, step_number_within_slab_1, diagnostics_1,\n                 slab_size_goal_1});\n  children_items.emplace(\n      child_2_id,\n      items_type{child_2_id, time_step_id_2, next_time_step_id_2, time_step_2,\n                 time_2, step_number_within_slab_2, diagnostics_2,\n                 slab_size_goal_2});\n\n  auto parent_box = db::create<db::AddSimpleTags<\n      Parallel::Tags::ArrayIndex<ElementId<1>>, ::Tags::TimeStepId,\n      ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep, ::Tags::Time,\n      ::Tags::StepNumberWithinSlab, ::Tags::AdaptiveSteppingDiagnostics,\n      ::Tags::ChangeSlabSize::SlabSizeGoal>>(\n      parent_id, TimeStepId{}, TimeStepId{}, TimeDelta{}, 0.0,\n      std::numeric_limits<uint64_t>::max(), AdaptiveSteppingDiagnostics{},\n      std::numeric_limits<double>::signaling_NaN());\n\n  db::mutate_apply<Initialization::ProjectTimeStepping<1>>(\n      make_not_null(&parent_box), children_items);\n\n  check(parent_box, time_step_id_2, next_time_step_id_2, time_step_2, time_2,\n        step_number_within_slab_2, AdaptiveSteppingDiagnostics{7, 2, 40, 6, 13},\n        slab_size_goal_2);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Initialization.TimeStepping\",\n                  \"[Evolution][Unit]\") {\n  test_gts();\n  test_lts();\n  static_assert(tt::assert_conforms_to_v<Initialization::ProjectTimeStepping<1>,\n                                         amr::protocols::Projector>);\n  test_p_refine();\n  test_split();\n  test_join<true>();\n  test_join<false>();\n}\n\nnamespace time_stepper_history {\nusing VariablesType =\n    Variables<tmpl::list<TestHelpers::Tags::Scalar<DataVector>>>;\n\nusing DtVariablesType =\n    Variables<tmpl::list<::Tags::dt<TestHelpers::Tags::Scalar<DataVector>>>>;\n\ntemplate <size_t Dim>\nstruct TestSystem {\n  using variables_tag =\n      Tags::Variables<tmpl::list<TestHelpers::Tags::Scalar<DataVector>>>;\n};\n\ntemplate <size_t Dim>\nstruct TestMetavariables {\n  static constexpr size_t volume_dim = Dim;\n  using system = TestSystem<Dim>;\n};\n\ntemplate <typename T>\nT f(const T& x, const std::array<double, 3>& c) {\n  return c[0] + c[1] * x + c[2] * square(x);\n}\n\ntemplate <size_t Dim>\nVariablesType make_vars(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& x, const double t) {\n  const auto t_coeffs = std::array{0.5, 1.5, 2.5};\n  const auto number_of_points = get<0>(x).size();\n  VariablesType result{number_of_points, f(t, t_coeffs)};\n  const auto x_coeffs = std::array{0.75, -1.75, 2.75};\n  DataVector& s = get(get<TestHelpers::Tags::Scalar<DataVector>>(result));\n  s *= f(x[0], x_coeffs);\n  if constexpr (Dim > 1) {\n    const auto y_coeffs = std::array{-0.25, 1.25, -2.25};\n    s *= f(x[1], y_coeffs);\n  }\n  if constexpr (Dim > 2) {\n    const auto z_coeffs = std::array{0.125, -1.625, -2.875};\n    s *= f(x[2], z_coeffs);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nDtVariablesType make_dt_vars(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& x, const double t) {\n  const auto dt_coeffs = std::array{1.5, 5.0, 0.0};\n  const auto number_of_points = get<0>(x).size();\n  DtVariablesType result{number_of_points, f(t, dt_coeffs)};\n  const auto x_coeffs = std::array{0.75, -1.75, 2.75};\n  DataVector& s =\n      get(get<::Tags::dt<TestHelpers::Tags::Scalar<DataVector>>>(result));\n  s *= f(x[0], x_coeffs);\n  if constexpr (Dim > 1) {\n    const auto y_coeffs = std::array{-0.25, 1.25, -2.25};\n    s *= f(x[1], y_coeffs);\n  }\n  if constexpr (Dim > 2) {\n    const auto z_coeffs = std::array{0.125, -1.625, -2.875};\n    s *= f(x[2], z_coeffs);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test_initialization() {\n  const TimeSteppers::AdamsBashforth ab2{2};\n  const Mesh<Dim> mesh{3, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  DtVariablesType dt_vars{};\n  DtVariablesType expected_dt_vars{mesh.number_of_grid_points()};\n  TimeSteppers::History<VariablesType> history{};\n  TimeSteppers::History<VariablesType> expected_history{1};\n  Initialization::TimeStepperHistory<TestMetavariables<Dim>>::apply(\n      make_not_null(&dt_vars), make_not_null(&history), ab2, mesh);\n  CHECK(dt_vars.size() == expected_dt_vars.size());\n  CHECK(history == expected_history);\n}\n\nvoid check_history(\n    const TimeSteppers::History<VariablesType>& history,\n    const TimeSteppers::History<VariablesType>& expected_history) {\n  for (size_t i = 0; i < history.size(); ++i) {\n    CHECK(history[i].time_step_id == expected_history[i].time_step_id);\n    CHECK(history[i].value.has_value() ==\n          expected_history[i].value.has_value());\n    if (history[i].value.has_value()) {\n      CHECK_VARIABLES_APPROX(*history[i].value, *expected_history[i].value);\n    }\n    CHECK_VARIABLES_APPROX(history[i].derivative,\n                           expected_history[i].derivative);\n  }\n  const auto& substeps = history.substeps();\n  const auto& expected_substeps = expected_history.substeps();\n  for (size_t i = 0; i < substeps.size(); ++i) {\n    CHECK(substeps[i].time_step_id == expected_substeps[i].time_step_id);\n    CHECK(substeps[i].value.has_value() ==\n          expected_substeps[i].value.has_value());\n    if (substeps[i].value.has_value()) {\n      CHECK_VARIABLES_APPROX(*substeps[i].value, *expected_substeps[i].value);\n    }\n    CHECK_VARIABLES_APPROX(substeps[i].derivative,\n                           expected_substeps[i].derivative);\n  }\n}\n\ntemplate <size_t Dim>\nDirectionalIdMap<Dim, evolution::dg::MortarInfo<Dim>> make_mortar_info(\n    const Element<Dim>& element, const bool local_time_stepping) {\n  DirectionalIdMap<Dim, evolution::dg::MortarInfo<Dim>> info{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    for (const auto& neighbor : neighbors) {\n      info.emplace(\n          DirectionalId{direction, neighbor},\n          evolution::dg::MortarInfo<Dim>{\n              {.time_stepping_policy =\n                   local_time_stepping\n                       ? evolution::dg::TimeSteppingPolicy::Conservative\n                       : evolution::dg::TimeSteppingPolicy::EqualRate}});\n    }\n  }\n  ASSERT(not(local_time_stepping and info.empty()),\n         \"Can't do LTS with no neighbors\");\n  return info;\n}\n\ntemplate <size_t Dim>\nvoid check(const TimeSteppers::History<VariablesType>& original_history,\n           const TimeSteppers::History<VariablesType>& expected_history,\n           const Mesh<Dim>& new_mesh, const ElementId<Dim>& element_id,\n           const Mesh<Dim>& old_mesh, const Element<Dim>& element) {\n  DtVariablesType dt_vars{};\n  const auto original_time_step = Slab(1.2, 3.4).duration();\n  auto time_step = original_time_step;\n  const TimeSteppers::AdamsBashforth time_stepper(4);\n  const auto mortar_info = make_mortar_info(element, false);\n  const TimeStepId original_next_time_step_id(true, 7,\n                                              time_step.slab().start());\n  auto next_time_step_id = original_next_time_step_id;\n  TimeSteppers::History<VariablesType> history = original_history;\n  Initialization::ProjectTimeStepperHistory<TestMetavariables<Dim>>::apply(\n      make_not_null(&dt_vars), make_not_null(&history),\n      make_not_null(&next_time_step_id), make_not_null(&time_step), new_mesh,\n      element_id, time_stepper, mortar_info, std::make_pair(old_mesh, element));\n  CHECK(dt_vars.size() == new_mesh.number_of_grid_points());\n  CHECK(time_step == original_time_step);\n  CHECK(next_time_step_id == original_next_time_step_id);\n  check_history(history, expected_history);\n  Initialization::ProjectTimeStepperHistory<TestMetavariables<Dim>>::apply(\n      make_not_null(&dt_vars), make_not_null(&history),\n      make_not_null(&next_time_step_id), make_not_null(&time_step), old_mesh,\n      element_id, time_stepper, mortar_info, std::make_pair(new_mesh, element));\n  CHECK(dt_vars.size() == old_mesh.number_of_grid_points());\n  CHECK(time_step == original_time_step);\n  CHECK(next_time_step_id == original_next_time_step_id);\n  check_history(history, original_history);\n}\n\ntemplate <size_t Dim>\nvoid test_p_refine() {\n  const ElementId<Dim> element_id{0};\n  const Element<Dim> element{element_id, DirectionMap<Dim, Neighbors<Dim>>{}};\n  const Mesh<Dim> old_mesh{4, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  std::array<size_t, Dim> new_extents{};\n  std::iota(new_extents.begin(), new_extents.end(), 3_st);\n  const Mesh<Dim> new_mesh{new_extents, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  const auto x_old = logical_coordinates(old_mesh);\n  const auto x_new = logical_coordinates(new_mesh);\n  TimeSteppers::History<VariablesType> history{};\n  TimeSteppers::History<VariablesType> expected_history{};\n  check(history, expected_history, new_mesh, element_id, old_mesh, element);\n  const Slab slab(0.0, 1.0);\n  history.integration_order(4);\n  expected_history.integration_order(4);\n  TimeStepId time_step_id{true, 0, slab.start()};\n  double t = time_step_id.substep_time();\n  history.insert_initial(time_step_id, make_vars(x_old, t),\n                         make_dt_vars(x_old, t));\n  expected_history.insert_initial(time_step_id, make_vars(x_new, t),\n                                  make_dt_vars(x_new, t));\n  check(history, expected_history, new_mesh, element_id, old_mesh, element);\n  time_step_id =\n      TimeStepId{true, -1, slab.start() - Slab(-1.0, 0.0).duration() / 4};\n  t = time_step_id.substep_time();\n  history.insert_initial(time_step_id, make_vars(x_old, t),\n                         make_dt_vars(x_old, t));\n  expected_history.insert_initial(time_step_id, make_vars(x_new, t),\n                                  make_dt_vars(x_new, t));\n  check(history, expected_history, new_mesh, element_id, old_mesh, element);\n  time_step_id =\n      TimeStepId{true, -1, slab.start() - Slab(-1.0, 0.0).duration() / 2};\n  t = time_step_id.substep_time();\n  history.insert_initial(time_step_id, make_vars(x_old, t),\n                         make_dt_vars(x_old, t));\n  expected_history.insert_initial(time_step_id, make_vars(x_new, t),\n                                  make_dt_vars(x_new, t));\n  check(history, expected_history, new_mesh, element_id, old_mesh, element);\n  time_step_id = TimeStepId{true, 0, slab.start() + slab.duration() / 4};\n  t = time_step_id.substep_time();\n  history.insert(time_step_id, make_vars(x_old, t), make_dt_vars(x_old, t));\n  expected_history.insert(time_step_id, make_vars(x_new, t),\n                          make_dt_vars(x_new, t));\n  check(history, expected_history, new_mesh, element_id, old_mesh, element);\n  const auto step_time = history.back().time_step_id.step_time();\n  const auto step_size = slab.duration() / 4;\n  time_step_id =\n      TimeStepId{true, 0,         step_time,\n                 1,    step_size, (step_time + slab.duration() / 4).value()};\n  t = time_step_id.substep_time();\n  history.insert(time_step_id, make_vars(x_old, t), make_dt_vars(x_old, t));\n  expected_history.insert(time_step_id, make_vars(x_new, t),\n                          make_dt_vars(x_new, t));\n  check(history, expected_history, new_mesh, element_id, old_mesh, element);\n}\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  struct system {\n    using variables_tag =\n        Tags::Variables<tmpl::list<TestHelpers::Tags::Vector<DataVector>>>;\n  };\n};\n\nusing variables_tag = Metavariables::system::variables_tag;\nusing dt_variables_tag = db::add_tag_prefix<Tags::dt, variables_tag>;\n\ntemplate <size_t Label>\nstruct HistoryEntry : db::SimpleTag {\n  using type = variables_tag::type;\n};\n\ntemplate <size_t Label>\nstruct HistoryDeriv : db::SimpleTag {\n  using type = dt_variables_tag::type;\n};\n\nusing ElementData = tuples::TaggedTuple<\n    domain::Tags::Element<3>, evolution::dg::Tags::MortarInfo<3>,\n    domain::Tags::Mesh<3>, Tags::TimeStepId, Tags::TimeStep,\n    Tags::HistoryEvolvedVariables<variables_tag>, HistoryEntry<0>,\n    HistoryDeriv<0>, HistoryDeriv<1>, Tags::StepperErrors<variables_tag>>;\n\n// Data for elements for GTS tests\nElementData element_data(\n    const gsl::not_null<std::mt19937*> gen, const ElementId<3>& element_id,\n    const std::optional<std::pair<Direction<3>, ElementId<3>>>& neighbor,\n    const Mesh<3>& mesh, const TimeStepId& time_step_id0,\n    const TimeStepId& time_step_id1) {\n  DirectionMap<3, Neighbors<3>> neighbors{};\n  if (neighbor.has_value()) {\n    neighbors.emplace(\n        neighbor->first,\n        Neighbors<3>({neighbor->second}, OrientationMap<3>::create_aligned()));\n  }\n  Element<3> element(element_id, std::move(neighbors));\n  auto mortar_info = make_mortar_info(element, false);\n  std::uniform_real_distribution<double> dist(-1.0, 1.0);\n  auto value0 = make_with_random_values<variables_tag::type>(\n      gen, make_not_null(&dist), mesh.number_of_grid_points());\n  auto deriv0 = make_with_random_values<dt_variables_tag::type>(\n      gen, make_not_null(&dist), mesh.number_of_grid_points());\n  auto deriv1 = make_with_random_values<dt_variables_tag::type>(\n      gen, make_not_null(&dist), mesh.number_of_grid_points());\n  TimeSteppers::History<variables_tag::type> history(4);\n  history.insert(time_step_id0, value0, deriv0);\n  history.insert(time_step_id1, decltype(history)::no_value, deriv1);\n  return {std::move(element), std::move(mortar_info), mesh, time_step_id1,\n          time_step_id1.step_size(), std::move(history), std::move(value0),\n          std::move(deriv0), std::move(deriv1),\n          // Not used in GTS\n          Tags::StepperErrors<variables_tag>::type{}};\n}\n\nvoid compare_p_refine() {\n  MAKE_GENERATOR(gen);\n  const Slab slab(0.0, 1.0);\n  const TimeStepId time_step_id0(true, 1, slab.start());\n  const TimeStepId time_step_id1 =\n      time_step_id0.next_substep(slab.duration(), 0.5);\n  const TimeDelta time_step = slab.duration() / 32;\n  const TimeStepId next_time_step_id(true, 7, slab.start());\n  const ElementId<3> element_id(3, {});\n  const Mesh<3> old_mesh(4, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto);\n  const Mesh<3> new_mesh(5, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto);\n\n  const auto old_data = element_data(&gen, element_id, std::nullopt, old_mesh,\n                                     time_step_id0, time_step_id1);\n  auto box = db::create<\n      db::AddSimpleTags<::Tags::ConcreteTimeStepper<TimeStepper>,\n                        Parallel::Tags::ArrayIndex<ElementId<3>>,\n                        domain::Tags::Element<3>, domain::Tags::Mesh<3>,\n                        evolution::dg::Tags::MortarInfo<3>, dt_variables_tag,\n                        ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n                        Tags::HistoryEvolvedVariables<variables_tag>,\n                        HistoryEntry<0>, HistoryDeriv<0>, HistoryDeriv<1>>,\n      time_stepper_ref_tags<TimeStepper>>(\n      static_cast<std::unique_ptr<TimeStepper>>(\n          std::make_unique<TimeSteppers::AdamsBashforth>(4)),\n      element_id, get<domain::Tags::Element<3>>(old_data), new_mesh,\n      get<evolution::dg::Tags::MortarInfo<3>>(old_data),\n      dt_variables_tag::type{}, next_time_step_id, time_step,\n      get<Tags::HistoryEvolvedVariables<variables_tag>>(old_data),\n      get<HistoryEntry<0>>(old_data), get<HistoryDeriv<0>>(old_data),\n      get<HistoryDeriv<1>>(old_data));\n\n  // Compare with the Variables projector\n  db::mutate_apply<Initialization::ProjectTimeStepperHistory<Metavariables>>(\n      make_not_null(&box),\n      std::pair(old_mesh, get<domain::Tags::Element<3>>(old_data)));\n  db::mutate_apply<amr::projectors::ProjectVariables<\n      3, HistoryEntry<0>, HistoryDeriv<0>, HistoryDeriv<1>>>(\n      make_not_null(&box),\n      std::pair(old_mesh, get<domain::Tags::Element<3>>(old_data)));\n\n  CHECK(db::get<dt_variables_tag>(box).number_of_grid_points() ==\n        new_mesh.number_of_grid_points());\n  CHECK(db::get<::Tags::TimeStep>(box) == time_step);\n  CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) == next_time_step_id);\n  const auto& history =\n      db::get<Tags::HistoryEvolvedVariables<variables_tag>>(box);\n  CHECK(history.integration_order() == 4);\n  CHECK_VARIABLES_APPROX(history[time_step_id0].value.value(),\n                         db::get<HistoryEntry<0>>(box));\n  CHECK_VARIABLES_APPROX(history[time_step_id0].derivative,\n                         db::get<HistoryDeriv<0>>(box));\n  CHECK(not history[time_step_id1].value.has_value());\n  CHECK_VARIABLES_APPROX(history[time_step_id1].derivative,\n                         db::get<HistoryDeriv<1>>(box));\n}\n\nvoid compare_h_refine() {\n  MAKE_GENERATOR(gen);\n  const Slab slab(0.0, 1.0);\n  const TimeStepId time_step_id0(true, 1, slab.start());\n  const TimeStepId time_step_id1 =\n      time_step_id0.next_substep(slab.duration(), 0.5);\n  const TimeDelta time_step = slab.duration() / 32;\n  const TimeStepId next_time_step_id(true, 7, slab.start());\n  const ElementId<3> parent_id(3, {});\n  const ElementId<3> child0_id = parent_id.id_of_child(1, Side::Lower);\n  const ElementId<3> child1_id = parent_id.id_of_child(1, Side::Upper);\n  const Mesh<3> mesh(4, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n\n  const auto parent_data = element_data(&gen, parent_id, std::nullopt, mesh,\n                                        time_step_id0, time_step_id1);\n  const auto child0_data =\n      element_data(&gen, child0_id, {{Direction<3>::upper_eta(), child1_id}},\n                   mesh, time_step_id0, time_step_id1);\n  const auto child1_data =\n      element_data(&gen, child1_id, {{Direction<3>::lower_eta(), child0_id}},\n                   mesh, time_step_id0, time_step_id1);\n\n  {\n    auto box = db::create<\n        db::AddSimpleTags<::Tags::ConcreteTimeStepper<TimeStepper>,\n                          Parallel::Tags::ArrayIndex<ElementId<3>>,\n                          domain::Tags::Element<3>, domain::Tags::Mesh<3>,\n                          evolution::dg::Tags::MortarInfo<3>, dt_variables_tag,\n                          ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n                          Tags::HistoryEvolvedVariables<variables_tag>,\n                          HistoryEntry<0>, HistoryDeriv<0>, HistoryDeriv<1>>,\n        time_stepper_ref_tags<TimeStepper>>(\n        static_cast<std::unique_ptr<TimeStepper>>(\n            std::make_unique<TimeSteppers::AdamsBashforth>(4)),\n        child0_id, get<domain::Tags::Element<3>>(child0_data), mesh,\n        get<evolution::dg::Tags::MortarInfo<3>>(child0_data),\n        dt_variables_tag::type{}, next_time_step_id, time_step,\n        Tags::HistoryEvolvedVariables<variables_tag>::type{},\n        HistoryEntry<0>::type{}, HistoryDeriv<0>::type{},\n        HistoryDeriv<1>::type{});\n\n    // Compare with the Variables projector\n    db::mutate_apply<Initialization::ProjectTimeStepperHistory<Metavariables>>(\n        make_not_null(&box), parent_data);\n    db::mutate_apply<amr::projectors::ProjectVariables<\n        3, HistoryEntry<0>, HistoryDeriv<0>, HistoryDeriv<1>>>(\n        make_not_null(&box), parent_data);\n\n    CHECK(db::get<dt_variables_tag>(box).number_of_grid_points() ==\n          mesh.number_of_grid_points());\n    CHECK(db::get<::Tags::TimeStep>(box) == time_step);\n    CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) == next_time_step_id);\n    const auto& history =\n        db::get<Tags::HistoryEvolvedVariables<variables_tag>>(box);\n    CHECK(history.integration_order() == 4);\n    CHECK_VARIABLES_APPROX(history[time_step_id0].value.value(),\n                           db::get<HistoryEntry<0>>(box));\n    CHECK_VARIABLES_APPROX(history[time_step_id0].derivative,\n                           db::get<HistoryDeriv<0>>(box));\n    CHECK(not history[time_step_id1].value.has_value());\n    CHECK_VARIABLES_APPROX(history[time_step_id1].derivative,\n                           db::get<HistoryDeriv<1>>(box));\n  }\n\n  {\n    auto box = db::create<\n        db::AddSimpleTags<::Tags::ConcreteTimeStepper<TimeStepper>,\n                          Parallel::Tags::ArrayIndex<ElementId<3>>,\n                          domain::Tags::Element<3>, domain::Tags::Mesh<3>,\n                          evolution::dg::Tags::MortarInfo<3>, dt_variables_tag,\n                          ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n                          Tags::HistoryEvolvedVariables<variables_tag>,\n                          HistoryEntry<0>, HistoryDeriv<0>, HistoryDeriv<1>>,\n        time_stepper_ref_tags<TimeStepper>>(\n        static_cast<std::unique_ptr<TimeStepper>>(\n            std::make_unique<TimeSteppers::AdamsBashforth>(4)),\n        parent_id, get<domain::Tags::Element<3>>(parent_data), mesh,\n        get<evolution::dg::Tags::MortarInfo<3>>(parent_data),\n        dt_variables_tag::type{}, next_time_step_id, time_step,\n        Tags::HistoryEvolvedVariables<variables_tag>::type{},\n        HistoryEntry<0>::type{}, HistoryDeriv<0>::type{},\n        HistoryDeriv<1>::type{});\n\n    std::unordered_map<ElementId<3>, ElementData> children_data{};\n    children_data.emplace(child0_id, child0_data);\n    children_data.emplace(child1_id, child1_data);\n\n    // Compare with the Variables projector\n    db::mutate_apply<Initialization::ProjectTimeStepperHistory<Metavariables>>(\n        make_not_null(&box), children_data);\n    db::mutate_apply<amr::projectors::ProjectVariables<\n        3, HistoryEntry<0>, HistoryDeriv<0>, HistoryDeriv<1>>>(\n        make_not_null(&box), children_data);\n\n    CHECK(db::get<dt_variables_tag>(box).number_of_grid_points() ==\n          mesh.number_of_grid_points());\n    CHECK(db::get<::Tags::TimeStep>(box) == time_step);\n    CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) == next_time_step_id);\n    const auto& history =\n        db::get<Tags::HistoryEvolvedVariables<variables_tag>>(box);\n    CHECK(history.integration_order() == 4);\n    CHECK_VARIABLES_APPROX(history[time_step_id0].value.value(),\n                           db::get<HistoryEntry<0>>(box));\n    CHECK_VARIABLES_APPROX(history[time_step_id0].derivative,\n                           db::get<HistoryDeriv<0>>(box));\n    CHECK(not history[time_step_id1].value.has_value());\n    CHECK_VARIABLES_APPROX(history[time_step_id1].derivative,\n                           db::get<HistoryDeriv<1>>(box));\n  }\n}\n\nvoid compare_nonuniform_join() {\n  MAKE_GENERATOR(gen);\n  const Slab slab(0.0, 1.0);\n  const TimeStepId time_step_id0(true, 1, slab.start());\n  const TimeStepId time_step_id1 =\n      time_step_id0.next_substep(slab.duration(), 0.5);\n  const TimeDelta time_step = slab.duration() / 32;\n  const TimeStepId next_time_step_id(true, 7, slab.start());\n  const ElementId<3> parent_id(3, {});\n  const ElementId<3> child0_id = parent_id.id_of_child(1, Side::Lower);\n  const ElementId<3> child1_id = parent_id.id_of_child(1, Side::Upper);\n  const Mesh<3> child0_mesh(4, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto);\n  const Mesh<3> child1_mesh(3, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto);\n  const auto& parent_mesh = child0_mesh;\n\n  const auto parent_data = element_data(\n      &gen, parent_id, std::nullopt, parent_mesh, time_step_id0, time_step_id1);\n  const auto child0_data =\n      element_data(&gen, child0_id, {{Direction<3>::upper_eta(), child1_id}},\n                   child0_mesh, time_step_id0, time_step_id1);\n  const auto child1_data =\n      element_data(&gen, child1_id, {{Direction<3>::lower_eta(), child0_id}},\n                   child1_mesh, time_step_id0, time_step_id1);\n\n  auto box = db::create<\n      db::AddSimpleTags<::Tags::ConcreteTimeStepper<TimeStepper>,\n                        Parallel::Tags::ArrayIndex<ElementId<3>>,\n                        domain::Tags::Element<3>, domain::Tags::Mesh<3>,\n                        evolution::dg::Tags::MortarInfo<3>, dt_variables_tag,\n                        ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n                        Tags::HistoryEvolvedVariables<variables_tag>,\n                        HistoryEntry<0>, HistoryDeriv<0>, HistoryDeriv<1>>,\n      time_stepper_ref_tags<TimeStepper>>(\n      static_cast<std::unique_ptr<TimeStepper>>(\n          std::make_unique<TimeSteppers::AdamsBashforth>(4)),\n      parent_id, get<domain::Tags::Element<3>>(parent_data), parent_mesh,\n      get<evolution::dg::Tags::MortarInfo<3>>(parent_data),\n      dt_variables_tag::type{}, next_time_step_id, time_step,\n      Tags::HistoryEvolvedVariables<variables_tag>::type{},\n      HistoryEntry<0>::type{}, HistoryDeriv<0>::type{},\n      HistoryDeriv<1>::type{});\n\n  std::unordered_map<ElementId<3>, ElementData> children_data{};\n  children_data.emplace(child0_id, child0_data);\n  children_data.emplace(child1_id, child1_data);\n\n  // Compare with the Variables projector\n  db::mutate_apply<Initialization::ProjectTimeStepperHistory<Metavariables>>(\n      make_not_null(&box), children_data);\n  db::mutate_apply<amr::projectors::ProjectVariables<\n      3, HistoryEntry<0>, HistoryDeriv<0>, HistoryDeriv<1>>>(\n      make_not_null(&box), children_data);\n\n  CHECK(db::get<dt_variables_tag>(box).number_of_grid_points() ==\n        parent_mesh.number_of_grid_points());\n  CHECK(db::get<::Tags::TimeStep>(box) == time_step);\n  CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) == next_time_step_id);\n  const auto& history =\n      db::get<Tags::HistoryEvolvedVariables<variables_tag>>(box);\n  CHECK(history.integration_order() == 4);\n  CHECK_VARIABLES_APPROX(history[time_step_id0].value.value(),\n                         db::get<HistoryEntry<0>>(box));\n  CHECK_VARIABLES_APPROX(history[time_step_id0].derivative,\n                         db::get<HistoryDeriv<0>>(box));\n  CHECK(not history[time_step_id1].value.has_value());\n  CHECK_VARIABLES_APPROX(history[time_step_id1].derivative,\n                         db::get<HistoryDeriv<1>>(box));\n}\n\n// Data for old elements for LTS tests\ntemplate <size_t Dim>\ntuples::TaggedTuple<\n    domain::Tags::Element<Dim>, evolution::dg::Tags::MortarInfo<Dim>,\n    domain::Tags::Mesh<Dim>, ::Tags::HistoryEvolvedVariables<variables_tag>,\n    ::Tags::TimeStepId, ::Tags::TimeStep, ::Tags::StepperErrors<variables_tag>>\nold_h_refinement_items(\n    const ElementId<Dim>& element_id,\n    const std::optional<std::pair<Direction<Dim>, ElementId<Dim>>>& neighbor,\n    const Mesh<Dim>& mesh, const TimeStepId& time_step_id, const size_t order,\n    const TimeDelta& time_step,\n    const std::optional<double>& first_order_error) {\n  ::Tags::HistoryEvolvedVariables<variables_tag>::type old_history{order};\n  old_history.insert(time_step_id, variables_tag::type{},\n                     dt_variables_tag::type{});\n  std::optional<StepperErrorEstimate> error_estimate{};\n  if (first_order_error.has_value()) {\n    error_estimate.emplace(time_step_id.step_time(), time_step, order,\n                           std::numeric_limits<double>::signaling_NaN());\n    error_estimate->errors[0] = first_order_error;\n  }\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  if (neighbor.has_value()) {\n    neighbors.emplace(neighbor->first,\n                      Neighbors<Dim>({neighbor->second},\n                                     OrientationMap<Dim>::create_aligned()));\n  }\n  Element<Dim> element(element_id, std::move(neighbors));\n  auto mortar_info = make_mortar_info(element, neighbor.has_value());\n  return {std::move(element),\n          std::move(mortar_info),\n          mesh,\n          std::move(old_history),\n          time_step_id,\n          time_step,\n          std::array<std::optional<StepperErrorEstimate>, 2>{\n              {std::nullopt, std::move(error_estimate)}}};\n}\n\nTimeDelta test_h_refine_lts_split(\n    const TimeSteppers::AdamsBashforth& time_stepper, const size_t old_order,\n    const TimeDelta& old_time_step,\n    const std::optional<double>& first_order_error) {\n  const ElementId<3> parent_id(3, {});\n  const ElementId<3> child_id = parent_id.id_of_child(1, Side::Lower);\n  DirectionMap<3, Neighbors<3>> child_neighbors{};\n  child_neighbors.emplace(Direction<3>::upper_eta(),\n                          Neighbors<3>(parent_id.id_of_child(1, Side::Upper),\n                                       OrientationMap<3>::create_aligned()));\n  const Element<3> child_element{child_id, std::move(child_neighbors)};\n  const Mesh<3> mesh(5, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const TimeStepId time_step_id(true, 5, old_time_step.slab().start());\n  const TimeStepId old_next_id =\n      time_stepper.next_time_id(time_step_id, old_time_step);\n  using history_tag = ::Tags::HistoryEvolvedVariables<variables_tag>;\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Mesh<3>, Parallel::Tags::ArrayIndex<ElementId<3>>,\n          ::Tags::ConcreteTimeStepper<TimeStepper>,\n          evolution::dg::Tags::MortarInfo<3>, dt_variables_tag, history_tag,\n          ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep>,\n      time_stepper_ref_tags<TimeStepper>>(\n      mesh, child_id,\n      static_cast<std::unique_ptr<TimeStepper>>(\n          std::make_unique<TimeSteppers::AdamsBashforth>(time_stepper)),\n      make_mortar_info(child_element, true), dt_variables_tag::type{},\n      history_tag::type{}, old_next_id, old_time_step);\n\n  const auto parent_items =\n      old_h_refinement_items<3>(parent_id, std::nullopt, mesh, time_step_id,\n                                old_order, old_time_step, first_order_error);\n\n  db::mutate_apply<Initialization::ProjectTimeStepperHistory<Metavariables>>(\n      make_not_null(&box), parent_items);\n\n  CHECK(db::get<dt_variables_tag>(box).number_of_grid_points() ==\n        mesh.number_of_grid_points());\n  const auto& history = db::get<history_tag>(box);\n  CHECK(history.integration_order() == 1);\n  CHECK(history.empty());\n  const auto& new_time_step = db::get<::Tags::TimeStep>(box);\n  CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) ==\n        time_stepper.next_time_id(time_step_id, new_time_step));\n  return new_time_step;\n}\n\nTimeDelta test_h_refine_lts_join(\n    const TimeSteppers::AdamsBashforth& time_stepper, const size_t old_order0,\n    const TimeDelta& old_time_step0,\n    const std::optional<double>& first_order_error0, const size_t old_order1,\n    const TimeDelta& old_time_step1,\n    const std::optional<double>& first_order_error1) {\n  const ElementId<3> parent_id(3, {});\n  const Element<3> parent_element(parent_id, {});\n  const ElementId<3> child0_id = parent_id.id_of_child(1, Side::Lower);\n  const ElementId<3> child1_id = parent_id.id_of_child(1, Side::Upper);\n  const Mesh<3> mesh(5, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const TimeStepId time_step_id(true, 5, old_time_step0.slab().start());\n  const TimeDelta old_min_time_step = abs(old_time_step0) < abs(old_time_step1)\n                                          ? old_time_step0\n                                          : old_time_step1;\n  const TimeStepId next_time_step_id =\n      time_stepper.next_time_id(time_step_id, old_min_time_step);\n  using history_tag = ::Tags::HistoryEvolvedVariables<variables_tag>;\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Mesh<3>, Parallel::Tags::ArrayIndex<ElementId<3>>,\n          ::Tags::ConcreteTimeStepper<TimeStepper>,\n          evolution::dg::Tags::MortarInfo<3>, dt_variables_tag, history_tag,\n          ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep>,\n      time_stepper_ref_tags<TimeStepper>>(\n      mesh, parent_id,\n      static_cast<std::unique_ptr<TimeStepper>>(\n          std::make_unique<TimeSteppers::AdamsBashforth>(time_stepper)),\n      make_mortar_info(parent_element, false), dt_variables_tag::type{},\n      history_tag::type{}, next_time_step_id, old_min_time_step);\n\n  auto child0_items = old_h_refinement_items<3>(\n      child0_id, {{Direction<3>::upper_eta(), child1_id}}, mesh, time_step_id,\n      old_order0, old_time_step0, first_order_error0);\n  auto child1_items = old_h_refinement_items<3>(\n      child1_id, {{Direction<3>::lower_eta(), child0_id}}, mesh, time_step_id,\n      old_order1, old_time_step1, first_order_error1);\n  std::unordered_map<ElementId<3>, decltype(child0_items)> children_items{};\n  children_items.emplace(child0_id, std::move(child0_items));\n  children_items.emplace(child1_id, std::move(child1_items));\n\n  db::mutate_apply<Initialization::ProjectTimeStepperHistory<Metavariables>>(\n      make_not_null(&box), children_items);\n\n  CHECK(db::get<dt_variables_tag>(box).number_of_grid_points() ==\n        mesh.number_of_grid_points());\n  const auto& history = db::get<history_tag>(box);\n  CHECK(history.integration_order() == 1);\n  CHECK(history.empty());\n  const auto& new_time_step = db::get<::Tags::TimeStep>(box);\n  CHECK(db::get<::Tags::Next<::Tags::TimeStepId>>(box) ==\n        time_stepper.next_time_id(time_step_id, new_time_step));\n  return new_time_step;\n}\n\nvoid test_h_refine_lts() {\n  const Slab slab(2.0, 18.0);\n  const auto unit_step = slab.duration() / 16;\n\n  // Fixed-order is OK if no initialization is required\n  CHECK(test_h_refine_lts_split(TimeSteppers::AdamsBashforth(1), 1, unit_step,\n                                std::nullopt) == unit_step);\n  CHECK(test_h_refine_lts_join(TimeSteppers::AdamsBashforth(1), 1, unit_step,\n                               std::nullopt, 1, unit_step * 2,\n                               std::nullopt) == unit_step);\n\n  // Fixed-order with initialization not allowed\n  CHECK_THROWS_WITH(\n      test_h_refine_lts_split(TimeSteppers::AdamsBashforth(2), 2, unit_step,\n                              std::nullopt),\n      Catch::Matchers::ContainsSubstring(\"Cannot perform h-refinement with LTS \"\n                                         \"steppers requiring initialization.\"));\n  CHECK_THROWS_WITH(\n      test_h_refine_lts_join(TimeSteppers::AdamsBashforth(2), 2, unit_step,\n                             std::nullopt, 2, unit_step, std::nullopt),\n      Catch::Matchers::ContainsSubstring(\"Cannot perform h-refinement with LTS \"\n                                         \"steppers requiring initialization.\"));\n\n  // If already at the starting order, nothing should happen, and\n  // errors should not be required (case for the initial time)\n  CHECK(test_h_refine_lts_split(TimeSteppers::AdamsBashforth(std::nullopt), 1,\n                                unit_step, std::nullopt) == unit_step);\n  CHECK(test_h_refine_lts_join(TimeSteppers::AdamsBashforth(std::nullopt), 1,\n                               unit_step, std::nullopt, 1, unit_step * 2,\n                               std::nullopt) == unit_step);\n\n  // Errors are required if the order is changed.\n  CHECK_THROWS_WITH(\n      test_h_refine_lts_split(TimeSteppers::AdamsBashforth(std::nullopt), 2,\n                              unit_step, std::nullopt),\n      Catch::Matchers::ContainsSubstring(\"must use ErrorControl\"));\n  CHECK_THROWS_WITH(\n      test_h_refine_lts_join(TimeSteppers::AdamsBashforth(std::nullopt), 2,\n                             unit_step, std::nullopt, 2, unit_step,\n                             std::nullopt),\n      Catch::Matchers::ContainsSubstring(\"must use ErrorControl\"));\n\n  // If the order is changed, the step should be shrunk if the error is large\n  CHECK(test_h_refine_lts_split(TimeSteppers::AdamsBashforth(std::nullopt), 2,\n                                unit_step, 5.0) == unit_step / 8);\n  CHECK(test_h_refine_lts_join(TimeSteppers::AdamsBashforth(std::nullopt), 2,\n                               unit_step, 5.0, 4, unit_step * 2,\n                               5.0e-3) == unit_step / 8);\n  CHECK(test_h_refine_lts_join(TimeSteppers::AdamsBashforth(std::nullopt), 2,\n                               unit_step, 5.0e-3, 4, unit_step * 2,\n                               5.0) == unit_step / 4);\n\n  // Step size should not be increased, even if the error is small\n  CHECK(test_h_refine_lts_split(TimeSteppers::AdamsBashforth(std::nullopt), 2,\n                                unit_step, 1.0e-3) == unit_step);\n  CHECK(test_h_refine_lts_join(TimeSteppers::AdamsBashforth(std::nullopt), 2,\n                               unit_step, 1.0e-3, 2, unit_step * 2,\n                               1.0e-3) == unit_step);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Initialization.TimeStepperHistory\",\n                  \"[Evolution][Unit]\") {\n  test_initialization<1>();\n  test_initialization<2>();\n  test_initialization<3>();\n  static_assert(tt::assert_conforms_to_v<\n                Initialization::ProjectTimeStepperHistory<TestMetavariables<1>>,\n                amr::protocols::Projector>);\n  test_p_refine<1>();\n  test_p_refine<2>();\n  test_p_refine<3>();\n\n  compare_p_refine();\n  compare_h_refine();\n  compare_nonuniform_join();\n  test_h_refine_lts();\n}\n}  // namespace time_stepper_history\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Initialization/Test_NonconservativeSystem.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr bool is_in_flux_conservative_form = false;\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = Tags::Variables<tmpl::list<Var>>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tag_list = tmpl::list<>;\n\n  using initial_tags = tmpl::list<domain::Tags::Mesh<Dim>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<initial_tags>,\n                 Initialization::Actions::NonconservativeSystem<\n                     typename Metavariables::system>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using system = System<Dim>;\n  using const_global_cache_tag_list = tmpl::list<>;\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  using metavars = Metavariables<Dim>;\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{}};\n  Mesh<Dim> mesh{5, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n  ActionTesting::emplace_component_and_initialize<comp>(&runner, 0, {mesh});\n  // Invoke the NonconservativeSystem action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), 0);\n  using vars_tag = Tags::Variables<tmpl::list<Var>>;\n  // The numerical value that the vars are set to is undefined, but the number\n  // of grid points must be correct.\n  CHECK(ActionTesting::get_databox_tag<comp, vars_tag>(runner, 0)\n            .number_of_grid_points() == mesh.number_of_grid_points());\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Initialization.NonconservativeSystem\",\n                  \"[Unit][Evolution][Actions]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Initialization/Test_SetVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/CubicScale.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/SetVariables.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TimeId : db::SimpleTag {\n  using type = double;\n};\n\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct NonConservativeVar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct PrimVar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct EquationOfStateTag : db::SimpleTag {\n  using type = std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>;\n};\n\nstruct SystemAnalyticSolution : public MarkAsAnalyticSolution,\n                                public evolution::initial_data::InitialData {\n  SystemAnalyticSolution() = default;\n  ~SystemAnalyticSolution() override = default;\n\n  explicit SystemAnalyticSolution(CkMigrateMessage* msg)\n      : evolution::initial_data::InitialData(msg) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(SystemAnalyticSolution);\n#pragma GCC diagnostic pop\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override {\n    return std::make_unique<SystemAnalyticSolution>(*this);\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<Var, NonConservativeVar> variables(\n      const tnsr::I<DataVector, Dim>& x, const double t,\n      tmpl::list<Var, NonConservativeVar> /*meta*/) const {\n    tuples::TaggedTuple<Var, NonConservativeVar> vars(x.get(0), x.get(0));\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<Var>(vars)) += square(x.get(d)) + t;\n      get(get<NonConservativeVar>(vars)) += square(x.get(d)) / 5.0 - t;\n    }\n    return vars;\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<Var> variables(const tnsr::I<DataVector, Dim>& x,\n                                     const double t,\n                                     tmpl::list<Var> /*meta*/) const {\n    tuples::TaggedTuple<Var> vars(x.get(0) + t);\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<Var>(vars)) += x.get(d) + t;\n    }\n    return vars;\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<NonConservativeVar> variables(\n      const tnsr::I<DataVector, Dim>& x, const double t,\n      tmpl::list<NonConservativeVar> /*meta*/) const {\n    tuples::TaggedTuple<NonConservativeVar> vars(x.get(0));\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<NonConservativeVar>(vars)) += square(x.get(d)) / 5.0 - t;\n    }\n    return vars;\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<PrimVar> variables(const tnsr::I<DataVector, Dim>& x,\n                                         const double t,\n                                         tmpl::list<PrimVar> /*meta*/) const {\n    tuples::TaggedTuple<PrimVar> vars(2.0 * x.get(0) + t);\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<PrimVar>(vars)) += 2.0 * x.get(d) + t;\n    }\n    return vars;\n  }\n\n  // EoS just needs to be a dummy place holder\n  static auto equation_of_state() {\n    EquationsOfState::PolytropicFluid<true> equation_of_state_{100.0, 2.0};\n    return equation_of_state_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    evolution::initial_data::InitialData::pup(p);\n  }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID SystemAnalyticSolution::my_PUP_ID = 0;\n\nstruct SystemAnalyticData : public MarkAsAnalyticData,\n                            public evolution::initial_data::InitialData {\n  SystemAnalyticData() = default;\n  ~SystemAnalyticData() override = default;\n\n  explicit SystemAnalyticData(CkMigrateMessage* msg)\n      : evolution::initial_data::InitialData(msg) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(SystemAnalyticData);\n#pragma GCC diagnostic pop\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override {\n    return std::make_unique<SystemAnalyticData>(*this);\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<Var, NonConservativeVar> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      tmpl::list<Var, NonConservativeVar> /*meta*/) const {\n    tuples::TaggedTuple<Var, NonConservativeVar> vars(x.get(0), x.get(0));\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<Var>(vars)) += square(x.get(d));\n      get(get<NonConservativeVar>(vars)) += square(x.get(d)) / 5.0;\n    }\n    return vars;\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<Var> variables(const tnsr::I<DataVector, Dim>& x,\n                                     tmpl::list<Var> /*meta*/) const {\n    tuples::TaggedTuple<Var> vars(x.get(0));\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<Var>(vars)) += square(x.get(d));\n    }\n    return vars;\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<NonConservativeVar> variables(\n      const tnsr::I<DataVector, Dim>& x,\n      tmpl::list<NonConservativeVar> /*meta*/) const {\n    tuples::TaggedTuple<NonConservativeVar> vars(x.get(0));\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<NonConservativeVar>(vars)) += square(x.get(d)) / 5.0;\n    }\n    return vars;\n  }\n\n  template <size_t Dim>\n  tuples::TaggedTuple<PrimVar> variables(const tnsr::I<DataVector, Dim>& x,\n                                         tmpl::list<PrimVar> /*meta*/) const {\n    tuples::TaggedTuple<PrimVar> vars(2.0 * x.get(0));\n    for (size_t d = 1; d < Dim; ++d) {\n      get(get<PrimVar>(vars)) += square(2.0 * x.get(d));\n    }\n    return vars;\n  }\n  EquationsOfState::PolytropicFluid<true> equation_of_state_{100.0, 2.0};\n  // EoS just needs to be a dummy place holder\n  const auto& equation_of_state() const { return equation_of_state_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override { InitialData::pup(p); }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID SystemAnalyticData::my_PUP_ID = 0;\n\ntemplate <size_t Dim, bool HasPrimitiveAndConservativeVars>\nstruct System {\n  // is_in_flux_conservative_form is unused\n  static constexpr bool is_in_flux_conservative_form = false;\n  static constexpr bool has_primitive_and_conservative_vars =\n      HasPrimitiveAndConservativeVars;\n  using non_conservative_variables = tmpl::list<NonConservativeVar>;\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = Tags::Variables<tmpl::list<Var, NonConservativeVar>>;\n  using primitive_variables_tag = Tags::Variables<tmpl::list<PrimVar>>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tag_list = tmpl::list<>;\n\n  using initial_tags =\n      tmpl::list<Tags::Time,\n                 domain::Tags::FunctionsOfTimeInitialize,\n                 domain::Tags::Coordinates<Dim, Frame::ElementLogical>,\n                 domain::Tags::ElementMap<Dim, Frame::Grid>,\n                 domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                             Frame::Inertial>,\n                 Tags::Variables<tmpl::list<Var, NonConservativeVar>>,\n                 Tags::Variables<tmpl::list<PrimVar>>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<initial_tags>,\n                 evolution::Initialization::Actions::SetVariables<\n                     domain::Tags::Coordinates<Dim, Frame::ElementLogical>>>>>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nauto emplace_component(\n    const gsl::not_null<ActionTesting::MockRuntimeSystem<Metavariables>*>\n        runner,\n    const double initial_time, const double expiration_time) {\n  using comp = component<Dim, Metavariables>;\n\n  const auto logical_coords = logical_coordinates(Mesh<Dim>{\n      5, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto});\n  ElementMap<Dim, Frame::Grid> logical_to_grid_map{\n      ElementId<Dim>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<Dim>{})};\n  const std::string expansion_factor = \"Expansion\";\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::TimeDependent::CubicScale<Dim>{\n              10.0, expansion_factor, expansion_factor});\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time.insert(std::make_pair(\n      expansion_factor,\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time, std::array<DataVector, 3>{{{1.0}, {-0.1}, {0.0}}},\n          expiration_time)));\n  Variables<tmpl::list<Var, NonConservativeVar>> var(\n      get<0>(logical_coords).size(), 8.9999);\n  Variables<tmpl::list<PrimVar>> prim_var(get<0>(logical_coords).size(),\n                                          9.9999);\n  ActionTesting::emplace_component_and_initialize<comp>(\n      runner, 0,\n      {initial_time, clone_unique_ptrs(functions_of_time), logical_coords,\n       std::move(logical_to_grid_map), grid_to_inertial_map->get_clone(), var,\n       prim_var});\n  return (*grid_to_inertial_map)(\n      ActionTesting::get_databox_tag<\n          comp, domain::Tags::ElementMap<Dim, Frame::Grid>>(*runner,\n                                                            0)(logical_coords),\n      initial_time, functions_of_time);\n}\n\ntemplate <size_t Dim, bool HasPrimitives>\nstruct MetavariablesAnalyticSolution {\n  static constexpr size_t volume_dim = Dim;\n  using analytic_solution = SystemAnalyticSolution;\n  using component_list =\n      tmpl::list<component<Dim, MetavariablesAnalyticSolution>>;\n  using equation_of_state_tag = EquationOfStateTag;\n  using system = System<Dim, HasPrimitives>;\n  using analytic_variables_tags =\n      tmpl::conditional_t<HasPrimitives,\n                          typename system::primitive_variables_tag::tags_list,\n                          typename system::variables_tag::tags_list>;\n  using temporal_id = TimeId;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<evolution::initial_data::InitialData,\n                             tmpl::list<SystemAnalyticSolution>>>;\n  };\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n};\n\ntemplate <size_t Dim, bool HasPrimitives>\nvoid test_analytic_solution() {\n  using metavars = MetavariablesAnalyticSolution<Dim, HasPrimitives>;\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {std::unique_ptr<evolution::initial_data::InitialData>(\n          std::make_unique<SystemAnalyticSolution>())}};\n  const double initial_time = 1.3;\n  const double expiration_time = 2.5;\n  const auto inertial_coords = emplace_component<Dim>(\n      make_not_null(&runner), initial_time, expiration_time);\n  Variables<tmpl::list<Var, NonConservativeVar>> var(\n      get<0>(inertial_coords).size(), 8.9999);\n  Variables<tmpl::list<PrimVar>> prim_var(get<0>(inertial_coords).size(),\n                                          9.9999);\n\n  // Invoke the SetVariables action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), 0);\n  if (HasPrimitives) {\n    prim_var.assign_subset(SystemAnalyticSolution{}.variables(\n        inertial_coords, initial_time, tmpl::list<PrimVar>{}));\n    var.assign_subset(SystemAnalyticSolution{}.variables(\n        inertial_coords, initial_time, tmpl::list<NonConservativeVar>{}));\n  } else {\n    var.assign_subset(SystemAnalyticSolution{}.variables(\n        inertial_coords, initial_time, tmpl::list<Var, NonConservativeVar>{}));\n  }\n  CHECK(ActionTesting::get_databox_tag<comp, Var>(runner, 0) == get<Var>(var));\n  CHECK(ActionTesting::get_databox_tag<comp, NonConservativeVar>(runner, 0) ==\n        get<NonConservativeVar>(var));\n  CHECK(ActionTesting::get_databox_tag<comp, PrimVar>(runner, 0) ==\n        get<PrimVar>(prim_var));\n}\n\ntemplate <size_t Dim, bool HasPrimitives>\nstruct MetavariablesAnalyticData {\n  static constexpr size_t volume_dim = Dim;\n  using analytic_data = SystemAnalyticData;\n  using component_list = tmpl::list<component<Dim, MetavariablesAnalyticData>>;\n  using equation_of_state_tag = EquationOfStateTag;\n  using system = System<Dim, HasPrimitives>;\n  using analytic_variables_tags =\n      tmpl::conditional_t<HasPrimitives,\n                          typename system::primitive_variables_tag::tags_list,\n                          typename system::variables_tag::tags_list>;\n  using temporal_id = TimeId;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<evolution::initial_data::InitialData,\n                             tmpl::list<SystemAnalyticData>>>;\n  };\n  using const_global_cache_tags =\n      tmpl::list<evolution::initial_data::Tags::InitialData>;\n};\n\ntemplate <size_t Dim, bool HasPrimitives>\nvoid test_analytic_data() {\n  using metavars = MetavariablesAnalyticData<Dim, HasPrimitives>;\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner = []() {\n    return MockRuntimeSystem{\n        {std::unique_ptr<evolution::initial_data::InitialData>(\n            std::make_unique<SystemAnalyticData>())}};\n  }();\n  const double initial_time = 1.3;\n  const double expiration_time = 2.5;\n  const auto inertial_coords = emplace_component<Dim>(\n      make_not_null(&runner), initial_time, expiration_time);\n  Variables<tmpl::list<Var, NonConservativeVar>> var(\n      get<0>(inertial_coords).size(), 8.9999);\n  Variables<tmpl::list<PrimVar>> prim_var(get<0>(inertial_coords).size(),\n                                          9.9999);\n\n  // Invoke the SetVariables action on the runner\n  ActionTesting::next_action<comp>(make_not_null(&runner), 0);\n  if (HasPrimitives) {\n    prim_var.assign_subset(\n        SystemAnalyticData{}.variables(inertial_coords, tmpl::list<PrimVar>{}));\n    var.assign_subset(SystemAnalyticData{}.variables(\n        inertial_coords, tmpl::list<NonConservativeVar>{}));\n  } else {\n    var.assign_subset(SystemAnalyticData{}.variables(\n        inertial_coords, tmpl::list<Var, NonConservativeVar>{}));\n  }\n  CHECK(ActionTesting::get_databox_tag<comp, Var>(runner, 0) == get<Var>(var));\n  CHECK(ActionTesting::get_databox_tag<comp, NonConservativeVar>(runner, 0) ==\n        get<NonConservativeVar>(var));\n  CHECK(ActionTesting::get_databox_tag<comp, PrimVar>(runner, 0) ==\n        get<PrimVar>(prim_var));\n}\n\ntemplate <size_t Dim, bool HasPrimitives>\nvoid test_impl() {\n  // Test setting variables from analytic solution\n  test_analytic_solution<Dim, HasPrimitives>();\n  // Test setting variables from analytic data\n  test_analytic_data<Dim, HasPrimitives>();\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  register_classes_with_charm<\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                            domain::CoordinateMaps::Identity<Dim>>,\n      domain::CoordinateMap<\n          Frame::Grid, Frame::Inertial,\n          domain::CoordinateMaps::TimeDependent::CubicScale<Dim>>>();\n\n  test_impl<Dim, true>();\n  test_impl<Dim, false>();\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Initialization.SetVariables\",\n                  \"[Unit][Evolution][Actions]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  register_classes_with_charm<SystemAnalyticData, SystemAnalyticSolution>();\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Initialization/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Initialization.Tags\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<Initialization::Tags::InitialTimeDelta>(\n      \"InitialTimeDelta\");\n  TestHelpers::db::test_simple_tag<Initialization::Tags::InitialSlabSize<true>>(\n      \"InitialSlabSize\");\n  TestHelpers::db::test_simple_tag<\n      Initialization::Tags::InitialSlabSize<false>>(\"InitialSlabSize\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(MonteCarlo)\n\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_MonteCarlo\")\n\nset(LIBRARY_SOURCES\n  Test_CellCrossingTime.cpp\n  Test_CellVolume.cpp\n  Test_CommunicationTags.cpp\n  Test_EmitPackets.cpp\n  Test_EvolvePackets.cpp\n  Test_FluidCouplingAction.cpp\n  Test_ImplicitMonteCarloCorrections.cpp\n  Test_InitializeAction.cpp\n  Test_InterpolateOpacities.cpp\n  Test_InverseJacobianInertialToFluid.cpp\n  Test_NeutrinoInteractionTable.cpp\n  Test_Packet.cpp\n  Test_Scattering.cpp\n  Test_SwapGrTags.cpp\n  Test_TakeTimeStep.cpp\n  Test_TimeStepAction.cpp\n  )\n\nadd_test_library(\n  ${LIBRARY}\n  \"${LIBRARY_SOURCES}\"\n  \"\"\n  )\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DgSubcell\n  GeneralRelativity\n  GeneralRelativityHelpers\n  GeneralRelativitySolutions\n  H5\n  Hydro\n  HydroHelpers\n  Informer\n  MonteCarlo\n  MonteCarloSolutions\n  Time\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_CellCrossingTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CellCrossingTime.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.CellCrossingTime\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(generator);\n\n  const size_t dv_size_1d = 3;\n  const Mesh<3> mesh(dv_size_1d, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered);\n  const size_t dv_size = cube(dv_size_1d);\n  DataVector zero_dv(dv_size, 0.0);\n  const auto lapse =\n      TestHelpers::gr::random_lapse(make_not_null(&generator), zero_dv);\n  const auto shift =\n      TestHelpers::gr::random_shift<3>(make_not_null(&generator), zero_dv);\n  const auto spatial_metric = TestHelpers::gr::random_spatial_metric<3>(\n      make_not_null(&generator), zero_dv);\n  const auto det_and_inv = determinant_and_inverse(spatial_metric);\n  const auto& inv_spatial_metric = det_and_inv.second;\n\n  // Inertial coordinates (taken to be the same as the logical coordinates here,\n  // as the coordinate transformation plays no role in the calculation).\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coordinates =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(zero_dv, 0.0);\n  for (size_t iz = 0; iz < dv_size_1d; iz++) {\n    const double z_coord = -1.0 + (0.5 + static_cast<double>(iz)) /\n                                      static_cast<double>(dv_size_1d) * 2.0;\n    for (size_t iy = 0; iy < dv_size_1d; iy++) {\n      const double y_coord = -1.0 + (0.5 + static_cast<double>(iy)) /\n                                        static_cast<double>(dv_size_1d) * 2.0;\n      for (size_t ix = 0; ix < dv_size_1d; ix++) {\n        const double x_coord = -1.0 + (0.5 + static_cast<double>(ix)) /\n                                          static_cast<double>(dv_size_1d) * 2.0;\n        const size_t idx = ix + dv_size_1d * (iy + iz * dv_size_1d);\n        inertial_coordinates.get(0)[idx] = x_coord;\n        inertial_coordinates.get(1)[idx] = y_coord;\n        inertial_coordinates.get(2)[idx] = z_coord;\n      }\n    }\n  }\n  Scalar<DataVector> cell_crossing_time =\n      make_with_value<Scalar<DataVector>>(zero_dv, 0.0);\n  Particles::MonteCarlo::cell_light_crossing_time(&cell_crossing_time, mesh,\n                                                  inertial_coordinates, lapse,\n                                                  shift, inv_spatial_metric);\n\n  Scalar<DataVector> expected_cell_light_crossing_time =\n      make_with_value<Scalar<DataVector>>(zero_dv, 0.0);\n\n  for (size_t i = 0; i < dv_size; i++) {\n    for (size_t d = 0; d < 3; d++) {\n      const double max_light_speed_in_direction =\n          fabs(shift.get(d)[i]) +\n          sqrt(inv_spatial_metric.get(d, d)[i]) * get(lapse)[i];\n      const double min_crossing_time_in_direction =\n          2.0 / static_cast<double>(dv_size_1d) / max_light_speed_in_direction;\n      if (d == 0 || min_crossing_time_in_direction <\n                        get(expected_cell_light_crossing_time)[i]) {\n        get(expected_cell_light_crossing_time)[i] =\n            min_crossing_time_in_direction;\n      }\n    }\n  }\n  const double epsilon_approx = 1.e-13;\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      expected_cell_light_crossing_time, cell_crossing_time,\n      Approx::custom().epsilon(epsilon_approx).scale(1.0));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_CellVolume.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CellVolume.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloCellVolume\",\n                  \"[Unit][Evolution]\") {\n  const Mesh<3> mesh(3, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered);\n\n  const size_t dv_size = 27;\n  const DataVector zero_dv(dv_size, 0.0);\n  const Scalar<DataVector> lapse{DataVector(dv_size, 1.2)};\n  const Scalar<DataVector> sqrt_determinant_spatial_metric\n    {DataVector(dv_size, 0.9)};\n  const Scalar<DataVector> det_jacobian_logical_to_inertial\n    {DataVector(dv_size, 1.1)};\n  const double time_step = 0.6;\n\n  Scalar<DataVector> cell_proper_four_volume{DataVector(dv_size, 0.0)};\n  Scalar<DataVector> expected_cell_proper_four_volume{\n      DataVector(dv_size, 8.0 / 27.0 * 1.2 * 0.9 * 0.6 * 1.1)};\n  Particles::MonteCarlo::cell_proper_four_volume_finite_difference(\n      &cell_proper_four_volume, lapse, sqrt_determinant_spatial_metric,\n      time_step, mesh, det_jacobian_logical_to_inertial);\n\n  Scalar<DataVector> cell_inertial_three_volume{DataVector(dv_size, 0.0)};\n  Scalar<DataVector> expected_cell_inertial_three_volume{\n      DataVector(dv_size, 8.0 / 27.0 * 1.1)};\n  Particles::MonteCarlo::\n      cell_inertial_coordinate_three_volume_finite_difference(\n          &cell_inertial_three_volume, mesh, det_jacobian_logical_to_inertial);\n\n  CHECK(expected_cell_proper_four_volume == cell_proper_four_volume);\n  CHECK(expected_cell_inertial_three_volume == cell_inertial_three_volume);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_CommunicationTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunication.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunicationStep.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunicationTags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MortarData.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n  using flux_variables = tmpl::list<Var1>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n\n  using initial_tags = tmpl::list<\n      ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n      domain::Tags::Mesh<Dim>, evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      evolution::dg::subcell::Tags::ActiveGrid, domain::Tags::Element<Dim>,\n      Particles::MonteCarlo::Tags::McGhostZoneDataTag<Dim>,\n      Particles::MonteCarlo::Tags::PacketsOnElement,\n      ::Tags::Variables<tmpl::list<Var1>>,\n      Particles::MonteCarlo::Tags::MortarDataTag<Dim>,\n      hydro::Tags::RestMassDensity<DataVector>,\n      hydro::Tags::ElectronFraction<DataVector>,\n      hydro::Tags::Temperature<DataVector>,\n      Particles::MonteCarlo::Tags::CellLightCrossingTime<DataVector>,\n      Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<Dim>,\n      Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,\n      Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,\n      Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>,\n      domain::Tags::NeighborMesh<Dim>,\n      evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<\n          ActionTesting::InitializeDataBox<initial_tags>,\n          Particles::MonteCarlo::Actions::SendDataForMcCommunication<\n              Dim,\n              // No local time stepping\n              false, Particles::MonteCarlo::CommunicationStep::PreStep>,\n          Particles::MonteCarlo::Actions::ReceiveDataForMcCommunication<\n              Dim, Particles::MonteCarlo::CommunicationStep::PreStep>,\n          Particles::MonteCarlo::Actions::SendDataForMcCommunication<\n              Dim,\n              // No local time stepping\n              false, Particles::MonteCarlo::CommunicationStep::PostStep>,\n          Particles::MonteCarlo::Actions::ReceiveDataForMcCommunication<\n              Dim, Particles::MonteCarlo::CommunicationStep::PostStep>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using system = System<Dim>;\n  using const_global_cache_tags = tmpl::list<>;\n};\n\ntemplate <size_t Dim>\nvoid test_send_receive_actions() {\n  CAPTURE(Dim);\n\n  using Interps = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n  using metavars = Metavariables<Dim>;\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{}};\n\n  const TimeStepId time_step_id{true, 1, Time{Slab{1.0, 2.0}, {0, 10}}};\n  const TimeStepId next_time_step_id{true, 1, Time{Slab{1.0, 2.0}, {1, 10}}};\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const evolution::dg::subcell::ActiveGrid active_grid =\n      evolution::dg::subcell::ActiveGrid::Subcell;\n\n  // ^ eta\n  // +-+-+> xi\n  // |X| |\n  // +-+-+\n  // | | |\n  // +-+-+\n  //\n  // The \"self_id\" for the element that we are considering is marked by an X in\n  // the diagram. We consider a configuration with one neighbor in the +xi\n  // direction (east_id), and (in 2d and 3d) one in the -eta (south_id)\n  // direction.\n  //\n  // In 1d there aren't any projections to test, and in 3d we only have 1\n  // element in the z-direction.\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  ElementId<Dim> self_id{};\n  ElementId<Dim> east_id{};\n  // NOLINTNEXTLINE(misc-const-correctness)\n  ElementId<Dim> south_id{};  // not used in 1d\n  // NOLINTNEXTLINE(misc-const-correctness)\n  OrientationMap<Dim> orientation{};\n  // Note: in 2d and 3d it is the neighbor in the lower eta direction that has a\n  // non-trivial orientation.\n\n  if constexpr (Dim == 1) {\n    self_id = ElementId<Dim>{0, {{{1, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}}}};\n    neighbors[Direction<Dim>::upper_xi()] = Neighbors<Dim>{{east_id},\n      OrientationMap<Dim>::create_aligned()};\n  } else if constexpr (Dim == 2) {\n    orientation = OrientationMap<Dim>{\n        std::array{Direction<Dim>::lower_xi(), Direction<Dim>::lower_eta()}};\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{0, 0}, {0, 0}}}};\n    neighbors[Direction<Dim>::upper_xi()] = Neighbors<Dim>{{east_id},\n      OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, orientation};\n  } else {\n    static_assert(Dim == 3, \"Only implemented tests in 1, 2, and 3d\");\n    orientation = OrientationMap<Dim>{std::array{Direction<Dim>::lower_xi(),\n                                                 Direction<Dim>::lower_eta(),\n                                                 Direction<Dim>::upper_zeta()}};\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{0, 0}, {0, 0}, {0, 0}}}};\n    neighbors[Direction<Dim>::upper_xi()] = Neighbors<Dim>{{east_id},\n      OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, orientation};\n  }\n  const Element<Dim> element{self_id, neighbors};\n\n  using NeighborDataMap =\n      DirectionalIdMap<Dim, Particles::MonteCarlo::McGhostZoneData<Dim>>;\n  NeighborDataMap neighbor_data{};\n  const DirectionalId<Dim> east_neighbor_id{Direction<Dim>::upper_xi(),\n                                            east_id};\n  // insert data from one of the neighbors to make sure the send actions clears\n  // it.\n  neighbor_data[east_neighbor_id] = {};\n\n  using evolved_vars_tags = tmpl::list<Var1>;\n  Variables<evolved_vars_tags> evolved_vars{\n      subcell_mesh.number_of_grid_points()};\n\n  // Logical coordinates\n  const tnsr::I<DataVector, Dim, Frame::ElementLogical> mesh_coordinates =\n      logical_coordinates(subcell_mesh);\n\n  // Set Var1 to the logical coords, just need some data\n  get(get<Var1>(evolved_vars)) = get<0>(mesh_coordinates);\n  Scalar<DataVector> rest_mass_density(get<0>(mesh_coordinates));\n  Scalar<DataVector> electron_fraction(get<0>(mesh_coordinates) / 2.0);\n  Scalar<DataVector> temperature(get<0>(mesh_coordinates) * 3.5);\n  Scalar<DataVector> cell_light_crossing_time(get<0>(mesh_coordinates) * 2.0);\n  std::vector<Particles::MonteCarlo::Packet> packets_on_element{};\n\n  for (const auto& [direction, neighbor_ids] : neighbors) {\n    (void)direction;\n    for (const auto& neighbor_id : neighbor_ids) {\n      // Initialize neighbors with garbage data. We won't ever run any actions\n      // on them, we just need to insert them to make sure things are sent to\n      // the right places. We'll check their inboxes directly.\n      ActionTesting::emplace_array_component_and_initialize<comp>(\n          &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n          neighbor_id,\n          {time_step_id,\n           next_time_step_id,\n           Mesh<Dim>{},\n           Mesh<Dim>{},\n           tnsr::I<DataVector, Dim, Frame::ElementLogical>{},\n           active_grid,\n           Element<Dim>{},\n           NeighborDataMap{},\n           packets_on_element,\n           Variables<evolved_vars_tags>{},\n           typename Particles::MonteCarlo::Tags::MortarDataTag<Dim>::type{},\n           Scalar<DataVector>{},\n           Scalar<DataVector>{},\n           Scalar<DataVector>{},\n           Scalar<DataVector>{},\n           typename Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<\n               Dim>::type{},\n           Scalar<DataVector>{},\n           Scalar<DataVector>{},\n           tnsr::i<DataVector, Dim, Frame::Inertial>{},\n           typename domain::Tags::NeighborMesh<Dim>::type{},\n           Interps{}});\n    }\n  }\n\n  // Initialize MortarData as needed\n  Particles::MonteCarlo::MortarData<Dim> mortar_data{};\n  {\n    const size_t number_of_points_in_ghost_zone =\n      Dim > 1 ?\n      subcell_mesh.slice_away(0).number_of_grid_points() :\n      1;\n    mortar_data.rest_mass_density[east_neighbor_id] =\n        DataVector(number_of_points_in_ghost_zone, 0.1);\n    mortar_data.electron_fraction[east_neighbor_id] =\n        DataVector(number_of_points_in_ghost_zone, 0.1);\n    mortar_data.temperature[east_neighbor_id] =\n        DataVector(number_of_points_in_ghost_zone, 0.1);\n    mortar_data.cell_light_crossing_time[east_neighbor_id] =\n        DataVector(number_of_points_in_ghost_zone, 0.1);\n  }\n  if constexpr (Dim > 1) {\n    const DirectionalId<Dim> south_neighbor_id{Direction<Dim>::lower_eta(),\n                                               south_id};\n    const Mesh<Dim - 1> ghost_mesh = subcell_mesh.slice_away(1);\n    mortar_data.rest_mass_density[south_neighbor_id] =\n        DataVector(ghost_mesh.number_of_grid_points(), 0.1);\n    mortar_data.electron_fraction[south_neighbor_id] =\n        DataVector(ghost_mesh.number_of_grid_points(), 0.1);\n    mortar_data.temperature[south_neighbor_id] =\n        DataVector(ghost_mesh.number_of_grid_points(), 0.1);\n    mortar_data.cell_light_crossing_time[south_neighbor_id] =\n        DataVector(ghost_mesh.number_of_grid_points(), 0.1);\n  }\n\n  // Coupling data\n  const size_t ghost_zone_size = 1;\n  const auto& subcell_extents = subcell_mesh.extents();\n  auto extents_with_ghost = subcell_extents;\n  size_t mesh_size_with_ghost = 1;\n  for (size_t d = 0; d < Dim; d++) {\n    extents_with_ghost[d] = subcell_extents[d] + 2 * ghost_zone_size;\n    mesh_size_with_ghost *= extents_with_ghost[d];\n  }\n  const DataVector zero_dv_with_ghost(mesh_size_with_ghost, 0.0);\n  Scalar<DataVector> coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(zero_dv_with_ghost, 0.0);\n  Scalar<DataVector> coupling_tilde_rho_ye =\n      make_with_value<Scalar<DataVector>>(zero_dv_with_ghost, 0.0);\n  tnsr::i<DataVector, Dim> coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, Dim>>(zero_dv_with_ghost, 0.0);\n  alg::iota(get(coupling_tilde_tau), 1.0);\n  alg::iota(get(coupling_tilde_rho_ye), 2.0);\n  Particles::MonteCarlo::GhostZoneCouplingData<Dim> coupling_data{};\n  {\n    const size_t number_of_points_in_ghost_zone =\n        mesh_size_with_ghost / extents_with_ghost[0];\n    coupling_data.coupling_tilde_tau[east_neighbor_id] =\n        DataVector(number_of_points_in_ghost_zone, 0.1);\n    coupling_data.coupling_tilde_rho_ye[east_neighbor_id] =\n        DataVector(number_of_points_in_ghost_zone, 0.1);\n    coupling_data.coupling_tilde_s[east_neighbor_id] =\n        make_with_value<tnsr::i<DataVector, Dim>>(\n            coupling_data.coupling_tilde_tau[east_neighbor_id].value(), 0.1);\n  }\n  if constexpr (Dim > 1) {\n    const size_t number_of_points_in_ghost_zone =\n        mesh_size_with_ghost / extents_with_ghost[1];\n    const DirectionalId<Dim> south_neighbor_id{Direction<Dim>::lower_eta(),\n                                               south_id};\n    coupling_data.coupling_tilde_tau[south_neighbor_id] =\n        DataVector(number_of_points_in_ghost_zone, 0.1);\n    alg::iota(coupling_data.coupling_tilde_tau[south_neighbor_id].value(), 1.0);\n    coupling_data.coupling_tilde_rho_ye[south_neighbor_id] =\n        DataVector(number_of_points_in_ghost_zone, 0.1);\n    coupling_data.coupling_tilde_s[south_neighbor_id] =\n        make_with_value<tnsr::i<DataVector, Dim>>(\n            coupling_data.coupling_tilde_tau[east_neighbor_id].value(), 0.1);\n  }\n\n  const size_t species = 1;\n  const double number_of_neutrinos = 2.0;\n  const double t0 = 1.2;\n  const double x0 = 0.3;\n  const double y0 = 0.5;\n  const double z0 = -0.7;\n  const size_t index_of_closest_grid_point =\n      (Dim == 1) ? 5 : ((Dim == 2) ? 5 + 6 * 9 : 5 + 6 * 9 + 1 * 81);\n  const double p_upper_t0 = 1.1;\n  const double p_x0 = 0.9;\n  const double p_y0 = 0.7;\n  const double p_z0 = 0.1;\n  // Packet to be sent to east element\n  Particles::MonteCarlo::Packet packet_east(\n      species, number_of_neutrinos, index_of_closest_grid_point, t0, 1.3, y0,\n      z0, p_upper_t0, p_x0, p_y0, p_z0);\n  // Packet to be kept by current element\n  Particles::MonteCarlo::Packet packet_keep(\n      species, number_of_neutrinos, index_of_closest_grid_point, t0, x0, y0, z0,\n      p_upper_t0, p_x0, p_y0, p_z0);\n  // Packet to be deleted for being out of bounds\n  const Particles::MonteCarlo::Packet packet_delete(\n      species, number_of_neutrinos, index_of_closest_grid_point, t0, -1.3, y0,\n      z0, p_upper_t0, p_x0, p_y0, p_z0);\n  // Packet to be sent to south element\n  // Note that the packet extends outside of the current domain in both the y\n  // and z direction, but should be moved in the direction where it is the\n  // farthest from the boundary (y, here).\n  Particles::MonteCarlo::Packet packet_south(\n      species, number_of_neutrinos, index_of_closest_grid_point, t0, x0, -1.2,\n      -1.1, p_upper_t0, p_x0, p_y0, p_z0);\n  packets_on_element.push_back(packet_east);\n  packets_on_element.push_back(packet_keep);\n  packets_on_element.push_back(packet_delete);\n  if constexpr (Dim > 1) {\n    packets_on_element.push_back(packet_south);\n  }\n\n  Interps fd_to_neighbor_fd_interpolants{};\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, self_id,\n      {time_step_id,\n       next_time_step_id,\n       dg_mesh,\n       subcell_mesh,\n       mesh_coordinates,\n       active_grid,\n       element,\n       neighbor_data,\n       packets_on_element,\n       evolved_vars,\n       mortar_data,\n       rest_mass_density,\n       electron_fraction,\n       temperature,\n       cell_light_crossing_time,\n       coupling_data,\n       coupling_tilde_tau,\n       coupling_tilde_rho_ye,\n       coupling_tilde_s,\n       typename domain::Tags::NeighborMesh<Dim>::type{},\n       fd_to_neighbor_fd_interpolants});\n\n  using ghost_data_tag = Particles::MonteCarlo::Tags::McGhostZoneDataTag<Dim>;\n  using ActionTesting::get_databox_tag;\n  CHECK(get_databox_tag<comp, ghost_data_tag>(runner, self_id).size() == 1);\n  CHECK(get_databox_tag<comp, ghost_data_tag>(runner, self_id)\n            .count(east_neighbor_id) == 1);\n\n  // Run the SendDataForReconstruction action on self_id (PreStep)\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n  CHECK(get_databox_tag<comp, ghost_data_tag>(runner, self_id).empty());\n\n  // Check data sent to neighbors\n  const auto& directions_to_slice = element.internal_boundaries();\n  const DirectionMap<Dim, DataVector> all_sliced_data =\n      [&rest_mass_density, &electron_fraction, &temperature,\n       &cell_light_crossing_time, &subcell_mesh, ghost_zone_size,\n       &directions_to_slice, &fd_to_neighbor_fd_interpolants]() {\n        (void)ghost_zone_size;\n        const size_t dv_size = subcell_mesh.number_of_grid_points();\n        const size_t number_of_vars = 4;\n        DataVector buffer{dv_size * number_of_vars};\n        std::copy(\n            get(rest_mass_density).data(),\n            std::next(get(rest_mass_density).data(), static_cast<int>(dv_size)),\n            buffer.data());\n        std::copy(\n            get(electron_fraction).data(),\n            std::next(get(electron_fraction).data(), static_cast<int>(dv_size)),\n            std::next(buffer.data(), static_cast<int>(dv_size)));\n        std::copy(get(temperature).data(),\n                  std::next(get(temperature).data(), static_cast<int>(dv_size)),\n                  std::next(buffer.data(), static_cast<int>(dv_size * 2)));\n        std::copy(get(cell_light_crossing_time).data(),\n                  std::next(get(cell_light_crossing_time).data(),\n                            static_cast<int>(dv_size)),\n                  std::next(buffer.data(), static_cast<int>(dv_size * 3)));\n\n        return evolution::dg::subcell::slice_data(\n            buffer, subcell_mesh.extents(), ghost_zone_size,\n            directions_to_slice, 0, fd_to_neighbor_fd_interpolants);\n      }();\n\n  {\n    const auto& expected_east_data =\n        all_sliced_data.at(east_neighbor_id.direction());\n    const auto& east_data = ActionTesting::get_inbox_tag<\n        comp, Particles::MonteCarlo::McGhostZoneDataInboxTag<\n                  Dim, Particles::MonteCarlo::CommunicationStep::PreStep>>(\n        runner, east_id);\n    CHECK(east_data.at(time_step_id)\n              .at(DirectionalId<Dim>{Direction<Dim>::lower_xi(), self_id})\n              .ghost_zone_hydro_variables == expected_east_data);\n    CHECK(east_data.at(time_step_id)\n              .at(DirectionalId<Dim>{Direction<Dim>::lower_xi(), self_id})\n              .packets_entering_this_element == std::nullopt);\n  }\n\n  if constexpr (Dim > 1) {\n    const auto direction = Direction<Dim>::lower_eta();\n    const auto& expected_south_data = all_sliced_data.at(direction);\n    const auto& south_data = ActionTesting::get_inbox_tag<\n        comp, Particles::MonteCarlo::McGhostZoneDataInboxTag<\n                  Dim, Particles::MonteCarlo::CommunicationStep::PreStep>>(\n        runner, south_id);\n    CHECK(\n        south_data.at(time_step_id)\n            .at(DirectionalId<Dim>{orientation(direction.opposite()), self_id})\n            .ghost_zone_hydro_variables == expected_south_data);\n    CHECK(south_data.at(time_step_id)\n              .at(DirectionalId<Dim>{direction, self_id})\n              .packets_entering_this_element == std::nullopt);\n  }\n\n  // Set the inbox data on self_id and then check that it gets processed\n  // correctly.\n  auto& self_inbox_pre = ActionTesting::get_inbox_tag<\n      comp, Particles::MonteCarlo::McGhostZoneDataInboxTag<\n                Dim, Particles::MonteCarlo::CommunicationStep::PreStep>>(\n      make_not_null(&runner), self_id);\n  REQUIRE_FALSE(ActionTesting::next_action_if_ready<comp>(\n      make_not_null(&runner), self_id));\n\n  // Send data from east neighbor\n  DataVector east_ghost_cells{};\n  {\n    const size_t number_of_vars = 4;\n    east_ghost_cells =\n        DataVector{subcell_mesh.slice_away(0).number_of_grid_points() *\n                   ghost_zone_size * number_of_vars};\n    alg::iota(east_ghost_cells, 2.0);\n    Particles::MonteCarlo::McGhostZoneDataInboxTag<\n        Dim, Particles::MonteCarlo::CommunicationStep::PreStep>::\n        insert_into_inbox(\n            make_not_null(&self_inbox_pre), time_step_id,\n            std::pair{DirectionalId<Dim>{Direction<Dim>::upper_xi(), east_id},\n                      Particles::MonteCarlo::McGhostZoneData<Dim>{\n                          east_ghost_cells, std::nullopt}});\n    CHECK(self_inbox_pre.at(time_step_id).size() == 1);\n  }\n  // NOLINTNEXTLINE(misc-const-correctness)\n  [[maybe_unused]] DataVector south_ghost_cells{};\n\n  if constexpr (Dim > 1) {\n    REQUIRE_FALSE(ActionTesting::next_action_if_ready<comp>(\n        make_not_null(&runner), self_id));\n\n    const size_t number_of_vars = 4;\n    south_ghost_cells =\n        DataVector{subcell_mesh.slice_away(1).number_of_grid_points() *\n                   ghost_zone_size * number_of_vars};\n    alg::iota(south_ghost_cells, 10000.0);\n    *std::prev(south_ghost_cells.end()) = -10.0;\n    Particles::MonteCarlo::McGhostZoneDataInboxTag<\n        Dim, Particles::MonteCarlo::CommunicationStep::PreStep>::\n        insert_into_inbox(\n            make_not_null(&self_inbox_pre), time_step_id,\n            std::pair{DirectionalId<Dim>{Direction<Dim>::lower_eta(), south_id},\n                      Particles::MonteCarlo::McGhostZoneData<Dim>{\n                          south_ghost_cells, std::nullopt}});\n    CHECK(self_inbox_pre.at(time_step_id).size() == 2);\n  }\n\n  // Run the ReceiveDataForReconstruction action on self_id (PreStep)\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  // Check the received data was stored correctly\n  using mortar_data_tag = Particles::MonteCarlo::Tags::MortarDataTag<Dim>;\n  const auto& mortar_data_from_box =\n      get_databox_tag<comp, mortar_data_tag>(runner, self_id);\n\n  {\n    REQUIRE(mortar_data_from_box.rest_mass_density.find(east_neighbor_id) !=\n            mortar_data_from_box.rest_mass_density.end());\n    REQUIRE(mortar_data_from_box.electron_fraction.find(east_neighbor_id) !=\n            mortar_data_from_box.electron_fraction.end());\n    REQUIRE(mortar_data_from_box.temperature.find(east_neighbor_id) !=\n            mortar_data_from_box.temperature.end());\n    REQUIRE(\n        mortar_data_from_box.cell_light_crossing_time.find(east_neighbor_id) !=\n        mortar_data_from_box.cell_light_crossing_time.end());\n\n    const size_t number_of_east_points = east_ghost_cells.size() / 4;\n    const DataVector rest_mass_density_view{east_ghost_cells.data(),\n                                            number_of_east_points};\n    const DataVector electron_fraction_view{\n        east_ghost_cells.data() + number_of_east_points, number_of_east_points};\n    const DataVector temperature_view{\n        east_ghost_cells.data() + number_of_east_points * 2,\n        number_of_east_points};\n    const DataVector cell_light_crossing_time_view{\n        east_ghost_cells.data() + number_of_east_points * 3,\n        east_ghost_cells.size() - number_of_east_points * 3};\n\n    CHECK(\n        mortar_data_from_box.rest_mass_density.find(east_neighbor_id)->second ==\n        rest_mass_density_view);\n    CHECK(\n        mortar_data_from_box.electron_fraction.find(east_neighbor_id)->second ==\n        electron_fraction_view);\n    CHECK(mortar_data_from_box.temperature.find(east_neighbor_id)->second ==\n          temperature_view);\n    CHECK(mortar_data_from_box.cell_light_crossing_time.find(east_neighbor_id)\n              ->second == cell_light_crossing_time_view);\n  }\n  if constexpr (Dim > 1) {\n    const DirectionalId<Dim> south_neighbor_id{Direction<Dim>::lower_eta(),\n                                               south_id};\n    REQUIRE(mortar_data_from_box.rest_mass_density.find(south_neighbor_id) !=\n            mortar_data_from_box.rest_mass_density.end());\n    REQUIRE(mortar_data_from_box.electron_fraction.find(south_neighbor_id) !=\n            mortar_data_from_box.electron_fraction.end());\n    REQUIRE(mortar_data_from_box.temperature.find(south_neighbor_id) !=\n            mortar_data_from_box.temperature.end());\n    REQUIRE(\n        mortar_data_from_box.cell_light_crossing_time.find(south_neighbor_id) !=\n        mortar_data_from_box.cell_light_crossing_time.end());\n\n    const size_t number_of_south_points = south_ghost_cells.size() / 4;\n    const DataVector rest_mass_density_view{south_ghost_cells.data(),\n                                            number_of_south_points};\n    const DataVector electron_fraction_view{\n        south_ghost_cells.data() + number_of_south_points,\n        number_of_south_points};\n    const DataVector temperature_view{\n        south_ghost_cells.data() + number_of_south_points * 2,\n        number_of_south_points};\n    const DataVector cell_light_crossing_time_view{\n        south_ghost_cells.data() + number_of_south_points * 3,\n        south_ghost_cells.size() - number_of_south_points * 3};\n\n    CHECK(mortar_data_from_box.rest_mass_density.find(south_neighbor_id)\n              ->second == rest_mass_density_view);\n    CHECK(mortar_data_from_box.electron_fraction.find(south_neighbor_id)\n              ->second == electron_fraction_view);\n    CHECK(mortar_data_from_box.temperature.find(south_neighbor_id)->second ==\n          temperature_view);\n    CHECK(mortar_data_from_box.cell_light_crossing_time.find(south_neighbor_id)\n              ->second == cell_light_crossing_time_view);\n  }\n  {\n    const auto& packets_from_box =\n        get_databox_tag<comp, Particles::MonteCarlo::Tags::PacketsOnElement>(\n            runner, self_id);\n    CHECK(packets_from_box.size() == packets_on_element.size());\n  }\n\n  // Run the SendDataForReconstruction action on self_id (PostStep)\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n  CHECK(get_databox_tag<comp, ghost_data_tag>(runner, self_id).empty());\n\n  // Check data sent to neighbors (PostStep)\n  const DirectionMap<Dim, DataVector> all_sliced_data_post =\n      [&coupling_tilde_tau, &coupling_tilde_rho_ye, &coupling_tilde_s,\n       &extents_with_ghost, &mesh_size_with_ghost, ghost_zone_size,\n       &directions_to_slice, &fd_to_neighbor_fd_interpolants]() {\n        (void)ghost_zone_size;\n        const size_t number_of_components = 2 + Dim;\n        DataVector buffer{mesh_size_with_ghost * number_of_components};\n        std::copy(get(coupling_tilde_tau).data(),\n                  std::next(get(coupling_tilde_tau).data(),\n                            static_cast<int>(mesh_size_with_ghost)),\n                  buffer.data());\n        std::copy(\n            get(coupling_tilde_rho_ye).data(),\n            std::next(get(coupling_tilde_rho_ye).data(),\n                      static_cast<int>(mesh_size_with_ghost)),\n            std::next(buffer.data(), static_cast<int>(mesh_size_with_ghost)));\n        for (size_t d = 0; d < Dim; d++) {\n          std::copy(\n              coupling_tilde_s.get(d).data(),\n              std::next(coupling_tilde_s.get(d).data(),\n                        static_cast<int>(mesh_size_with_ghost)),\n              std::next(buffer.data(),\n                        static_cast<int>(mesh_size_with_ghost * (2 + d))));\n        }\n\n        return evolution::dg::subcell::slice_data(\n            buffer, extents_with_ghost, ghost_zone_size, directions_to_slice, 0,\n            fd_to_neighbor_fd_interpolants);\n      }();\n\n  {\n    const auto& expected_east_data =\n        all_sliced_data_post.at(east_neighbor_id.direction());\n    const auto& east_data = ActionTesting::get_inbox_tag<\n        comp, Particles::MonteCarlo::McGhostZoneDataInboxTag<\n                  Dim, Particles::MonteCarlo::CommunicationStep::PostStep>>(\n        runner, east_id);\n    CHECK(east_data.at(time_step_id)\n              .at(DirectionalId<Dim>{Direction<Dim>::lower_xi(), self_id})\n              .ghost_zone_hydro_variables == expected_east_data);\n    // Verify that the packets out of the element have been removed.\n    const auto& packets_from_box =\n        get_databox_tag<comp, Particles::MonteCarlo::Tags::PacketsOnElement>(\n            runner, self_id);\n    CHECK(packets_from_box.size() == 1);\n    CHECK(packets_from_box[0] == packet_keep);\n  }\n  if constexpr (Dim > 1) {\n    const auto direction = Direction<Dim>::lower_eta();\n    const auto& expected_south_data = all_sliced_data_post.at(direction);\n    const auto& south_data = ActionTesting::get_inbox_tag<\n        comp, Particles::MonteCarlo::McGhostZoneDataInboxTag<\n                  Dim, Particles::MonteCarlo::CommunicationStep::PostStep>>(\n        runner, south_id);\n    CHECK(\n        south_data.at(time_step_id)\n            .at(DirectionalId<Dim>{orientation(direction.opposite()), self_id})\n            .ghost_zone_hydro_variables == expected_south_data);\n  }\n\n  // Correct coordinates of packets sent east/south to get in the\n  // topological coordinate of their new element.\n  // This is the unprocessed inbox, so the index of the closest\n  // point is still the same as on the old element.\n  packet_east.coordinates[0] -= 2.0;\n  packet_south.coordinates[1] += 2.0;\n  // Current hack for edges/corners\n  if constexpr (Dim > 2){\n    packet_south.coordinates[2] = -2.0 - packet_south.coordinates[2];\n  }\n  {\n    const auto& east_data = ActionTesting::get_inbox_tag<\n        comp, Particles::MonteCarlo::McGhostZoneDataInboxTag<\n                  Dim, Particles::MonteCarlo::CommunicationStep::PostStep>>(\n        runner, east_id);\n    CHECK(east_data.at(time_step_id)\n              .at(DirectionalId<Dim>{Direction<Dim>::lower_xi(), self_id})\n              .packets_entering_this_element != std::nullopt);\n    CHECK(east_data.at(time_step_id)\n              .at(DirectionalId<Dim>{Direction<Dim>::lower_xi(), self_id})\n              .packets_entering_this_element.value()\n              .size() == 1);\n    const Particles::MonteCarlo::Packet received_packet =\n        east_data.at(time_step_id)\n            .at(DirectionalId<Dim>{Direction<Dim>::lower_xi(), self_id})\n            .packets_entering_this_element.value()[0];\n    CHECK(packet_east == received_packet);\n  }\n  if constexpr (Dim > 1) {\n    const auto direction = Direction<Dim>::lower_eta();\n    const auto& south_data = ActionTesting::get_inbox_tag<\n        comp, Particles::MonteCarlo::McGhostZoneDataInboxTag<\n                  Dim, Particles::MonteCarlo::CommunicationStep::PostStep>>(\n        runner, south_id);\n    CHECK(\n        south_data.at(time_step_id)\n            .at(DirectionalId<Dim>{orientation(direction.opposite()), self_id})\n            .packets_entering_this_element != std::nullopt);\n    CHECK(\n        south_data.at(time_step_id)\n            .at(DirectionalId<Dim>{orientation(direction.opposite()), self_id})\n            .packets_entering_this_element.value()\n            .size() == 1);\n    const Particles::MonteCarlo::Packet received_packet =\n        south_data.at(time_step_id)\n            .at(DirectionalId<Dim>{orientation(direction.opposite()), self_id})\n            .packets_entering_this_element.value()[0];\n    CHECK(packet_south == received_packet);\n  }\n\n  // Set the inbox data on self_id and then check that it gets processed\n  // correctly.\n  auto& self_inbox_post = ActionTesting::get_inbox_tag<\n      comp, Particles::MonteCarlo::McGhostZoneDataInboxTag<\n                Dim, Particles::MonteCarlo::CommunicationStep::PostStep>>(\n      make_not_null(&runner), self_id);\n  // Check that we are not ready to receive yet (Inboxes unfilled)\n  REQUIRE_FALSE(ActionTesting::next_action_if_ready<comp>(\n      make_not_null(&runner), self_id));\n\n  // Now calculates the correct index for the new points, as the\n  // processing of the Inbox resets the index of the closest grid point.\n  packet_east.index_of_closest_grid_point -= 4;\n  packet_south.index_of_closest_grid_point += 18;\n  if constexpr (Dim > 2) {\n    packet_south.index_of_closest_grid_point -= 81;\n  }\n\n  // Set up fake data coming from east neighbor\n  DataVector east_ghost_cells_post{};\n  {\n    const size_t number_of_points_in_ghost_zone =\n        mesh_size_with_ghost / extents_with_ghost[0];\n    const size_t number_of_vars = 2 + Dim;\n    east_ghost_cells_post = DataVector{number_of_points_in_ghost_zone *\n                                       ghost_zone_size * number_of_vars};\n    alg::iota(east_ghost_cells_post, 2.0);\n    const std::optional<std::vector<Particles::MonteCarlo::Packet>>\n        packets_from_east =\n            std::vector<Particles::MonteCarlo::Packet>{packet_east};\n    Particles::MonteCarlo::McGhostZoneDataInboxTag<\n        Dim, Particles::MonteCarlo::CommunicationStep::PostStep>::\n        insert_into_inbox(\n            make_not_null(&self_inbox_post), time_step_id,\n            std::pair{DirectionalId<Dim>{Direction<Dim>::upper_xi(), east_id},\n                      Particles::MonteCarlo::McGhostZoneData<Dim>{\n                          east_ghost_cells_post, packets_from_east}});\n    CHECK(self_inbox_post.at(time_step_id).size() == 1);\n  }\n  // Set up fake data coming from south neighbor\n  // NOLINTNEXTLINE(misc-const-correctness)\n  [[maybe_unused]] DataVector south_ghost_cells_post{};\n  if constexpr (Dim > 1) {\n    REQUIRE_FALSE(ActionTesting::next_action_if_ready<comp>(\n        make_not_null(&runner), self_id));\n\n    const size_t number_of_points_in_ghost_zone =\n        mesh_size_with_ghost / extents_with_ghost[1];\n    const size_t number_of_vars = 2 + Dim;\n    south_ghost_cells_post = DataVector{number_of_points_in_ghost_zone *\n                                        ghost_zone_size * number_of_vars};\n    alg::iota(south_ghost_cells_post, 2.0);\n    const std::optional<std::vector<Particles::MonteCarlo::Packet>>\n        packets_from_south =\n            std::vector<Particles::MonteCarlo::Packet>{packet_south};\n    Particles::MonteCarlo::McGhostZoneDataInboxTag<\n        Dim, Particles::MonteCarlo::CommunicationStep::PostStep>::\n        insert_into_inbox(\n            make_not_null(&self_inbox_post), time_step_id,\n            std::pair{DirectionalId<Dim>{Direction<Dim>::lower_eta(), south_id},\n                      Particles::MonteCarlo::McGhostZoneData<Dim>{\n                          south_ghost_cells_post, packets_from_south}});\n    CHECK(self_inbox_post.at(time_step_id).size() == 2);\n  }\n  // Run the ReceiveDataForReconstruction action on self_id (PostStep)\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  // Check the received data was stored correctly\n  using coupling_data_tag =\n      Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<Dim>;\n  const auto& coupling_data_from_box =\n      get_databox_tag<comp, coupling_data_tag>(runner, self_id);\n  {\n    REQUIRE(coupling_data_from_box.coupling_tilde_tau.find(east_neighbor_id) !=\n            coupling_data_from_box.coupling_tilde_tau.end());\n    REQUIRE(\n        coupling_data_from_box.coupling_tilde_rho_ye.find(east_neighbor_id) !=\n        coupling_data_from_box.coupling_tilde_rho_ye.end());\n    REQUIRE(coupling_data_from_box.coupling_tilde_s.find(east_neighbor_id) !=\n            coupling_data_from_box.coupling_tilde_s.end());\n\n    const size_t number_of_east_points =\n        east_ghost_cells_post.size() / (2 + Dim);\n    const DataVector coupling_tilde_tau_view{east_ghost_cells_post.data(),\n                                             number_of_east_points};\n    const DataVector coupling_tilde_rho_ye_view{\n        east_ghost_cells_post.data() + number_of_east_points,\n        number_of_east_points};\n    CHECK(coupling_data_from_box.coupling_tilde_tau.find(east_neighbor_id)\n              ->second == coupling_tilde_tau_view);\n    CHECK(coupling_data_from_box.coupling_tilde_rho_ye.find(east_neighbor_id)\n              ->second == coupling_tilde_rho_ye_view);\n    for (size_t d = 0; d < Dim; d++) {\n      const DataVector coupling_tilde_s_d_view{\n          east_ghost_cells_post.data() + number_of_east_points * (2 + d),\n          number_of_east_points};\n      CHECK(coupling_data_from_box.coupling_tilde_s.find(east_neighbor_id)\n                ->second.value()\n                .get(d) == coupling_tilde_s_d_view);\n    }\n  }\n  if constexpr (Dim > 1) {\n    const DirectionalId<Dim> south_neighbor_id{Direction<Dim>::lower_eta(),\n                                               south_id};\n    REQUIRE(coupling_data_from_box.coupling_tilde_tau.find(south_neighbor_id) !=\n            coupling_data_from_box.coupling_tilde_tau.end());\n    REQUIRE(\n        coupling_data_from_box.coupling_tilde_rho_ye.find(south_neighbor_id) !=\n        coupling_data_from_box.coupling_tilde_rho_ye.end());\n    REQUIRE(coupling_data_from_box.coupling_tilde_s.find(south_neighbor_id) !=\n            coupling_data_from_box.coupling_tilde_s.end());\n\n    const size_t number_of_south_points =\n        south_ghost_cells_post.size() / (2 + Dim);\n    const DataVector coupling_tilde_tau_view{south_ghost_cells_post.data(),\n                                             number_of_south_points};\n    const DataVector coupling_tilde_rho_ye_view{\n        south_ghost_cells_post.data() + number_of_south_points,\n        number_of_south_points};\n    CHECK(coupling_data_from_box.coupling_tilde_tau.find(south_neighbor_id)\n              ->second == coupling_tilde_tau_view);\n    CHECK(coupling_data_from_box.coupling_tilde_rho_ye.find(south_neighbor_id)\n              ->second == coupling_tilde_rho_ye_view);\n    for (size_t d = 0; d < Dim; d++) {\n      const DataVector coupling_tilde_s_d_view{\n          south_ghost_cells_post.data() + number_of_south_points * (2 + d),\n          number_of_south_points};\n      CHECK(coupling_data_from_box.coupling_tilde_s.find(south_neighbor_id)\n                ->second.value()\n                .get(d) == coupling_tilde_s_d_view);\n    }\n  }\n  // We should now have 3 packets (2 for Dim=1)\n  {\n    const auto& packets_from_box =\n        get_databox_tag<comp, Particles::MonteCarlo::Tags::PacketsOnElement>(\n            runner, self_id);\n    CHECK(packets_from_box.size() == (Dim > 1 ? 3 : 2));\n    CHECK(packets_from_box[0] == packet_keep);\n    CHECK(packets_from_box[1] == packet_east);\n    if constexpr (Dim > 1) {\n      CHECK(packets_from_box[2] == packet_south);\n    }\n  }\n  if constexpr (Dim == 3) {\n    // Check final values of coupling terms\n    // (1) Add values expected from east face\n    Index<2> extents_2d;\n    extents_2d[0] = extents_with_ghost[1];\n    extents_2d[1] = extents_with_ghost[2];\n    Index<3> index_volume_3d;\n    Index<2> index_slice_2d;\n    for (size_t i = 0; i < extents_with_ghost[1]; i++) {\n      for (size_t j = 0; j < extents_with_ghost[2]; j++) {\n        index_volume_3d[0] = extents_with_ghost[0] - 2;\n        index_volume_3d[1] = i;\n        index_volume_3d[2] = j;\n        index_slice_2d[0] = i;\n        index_slice_2d[1] = j;\n        const size_t index_volume =\n            collapsed_index(index_volume_3d, extents_with_ghost);\n        const size_t index_slice = collapsed_index(index_slice_2d, extents_2d);\n        get(coupling_tilde_tau)[index_volume] +=\n          (static_cast<double>(index_slice) + 2.0);\n      }\n    }\n    // South slice\n    extents_2d[0] = extents_with_ghost[0];\n    for (size_t i = 0; i < extents_with_ghost[0]; i++) {\n      for (size_t j = 0; j < extents_with_ghost[2]; j++) {\n        index_volume_3d[0] = i;\n        index_volume_3d[1] = 1;\n        index_volume_3d[2] = j;\n        index_slice_2d[0] = i;\n        index_slice_2d[1] = j;\n        const size_t index_volume =\n            collapsed_index(index_volume_3d, extents_with_ghost);\n        const size_t index_slice = collapsed_index(index_slice_2d, extents_2d);\n        get(coupling_tilde_tau)[index_volume] +=\n          (static_cast<double>(index_slice) + 2.0);\n      }\n    }\n    const auto& post_comm_coupling_tilde_tau = get_databox_tag<\n        comp, Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>>(\n        runner, self_id);\n    CHECK(post_comm_coupling_tilde_tau == coupling_tilde_tau);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarlo.CommunicationTags\",\n                  \"[Evolution][Unit]\") {\n  test_send_receive_actions<1>();\n  test_send_receive_actions<2>();\n  test_send_receive_actions<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_EmitPackets.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n\nusing hydro::units::nuclear::proton_mass;\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloEmission\",\n                  \"[Unit][Evolution]\") {\n  using Particles::MonteCarlo::Packet;\n\n  const double epsilon_approx = 1.e-13;\n  // Vector of MC packets\n  std::vector<Packet> all_packets = {};\n\n  const double time = 0.0;\n  const double time_step = 0.1;\n\n  MAKE_GENERATOR(generator);\n  const Mesh<3> mesh(3, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered);\n  const size_t num_ghost_zones = 0;\n\n  // Zero vector for tensor creations\n  DataVector zero_dv(27, 0.0);\n\n  std::array<std::array<DataVector, 2>, 2> emissivity_in_cells = {\n      std::array<DataVector, 2>{{zero_dv, zero_dv}},\n      std::array<DataVector, 2>{{zero_dv, zero_dv}}};\n  std::array<DataVector, 2> single_packet_energy = {zero_dv, zero_dv};\n  const std::array<double, 2> energy_at_bin_center = {2.0, 5.0};\n  InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      inverse_jacobian = make_with_value<\n          InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(\n          zero_dv, 0.0);\n  Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid> jacobian =\n      make_with_value<Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(\n          zero_dv, 0.0);\n  Scalar<DataVector> cell_proper_volume =\n    make_with_value<Scalar<DataVector> >(zero_dv, 2.0);\n\n  // Set data\n  for (size_t s = 0; s < 2; s++) {\n    for (size_t i = 0; i < zero_dv.size(); i++) {\n      gsl::at(single_packet_energy, s)[i] = 2.0;\n      for (size_t g = 0; g < 2; g++) {\n        if (i == 25) {\n          gsl::at(gsl::at(emissivity_in_cells, s), g)[i] =\n              static_cast<double>(2 - 2 * s);\n        }\n      }\n    }\n  }\n\n  // Set Jacobian to a Lorentz boost in the y-direction with W=2\n  const double v_boost = sqrt(3) / 2.0;\n  const double W_boost = 2.0;\n\n  jacobian.get(0, 0) = W_boost;\n  jacobian.get(0, 2) = -W_boost * v_boost;\n  jacobian.get(1, 1) = 1.0;\n  jacobian.get(2, 0) = -W_boost * v_boost;\n  jacobian.get(2, 2) = W_boost;\n  jacobian.get(3, 3) = 1.0;\n  inverse_jacobian.get(0, 0) = W_boost;\n  inverse_jacobian.get(0, 2) = W_boost * v_boost;\n  inverse_jacobian.get(1, 1) = 1.0;\n  inverse_jacobian.get(2, 0) = W_boost * v_boost;\n  inverse_jacobian.get(2, 2) = W_boost;\n  inverse_jacobian.get(3, 3) = 1.0;\n\n  tnsr::i<DataVector, 3, Frame::Inertial> lower_spatial_four_velocity =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(zero_dv, 0.0);\n  lower_spatial_four_velocity.get(1) = W_boost * v_boost;\n  Scalar<DataVector> lorentz_factor =\n      make_with_value<Scalar<DataVector>>(zero_dv, W_boost);\n\n  Scalar<DataVector> coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(zero_dv, 0.0);\n  Scalar<DataVector> coupling_rho_ye =\n      make_with_value<Scalar<DataVector>>(zero_dv, 0.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(zero_dv, 0.0);\n\n  // Run emission code\n  Particles::MonteCarlo::TemplatedLocalFunctions<2, 2> MonteCarloStruct;\n\n  MonteCarloStruct.emit_packets(\n      &all_packets, &generator, &coupling_tilde_tau, &coupling_tilde_s,\n      &coupling_rho_ye, time, time_step, mesh, num_ghost_zones,\n      emissivity_in_cells, single_packet_energy, energy_at_bin_center,\n      lorentz_factor, lower_spatial_four_velocity, jacobian, inverse_jacobian,\n      cell_proper_volume);\n\n  // Check some things\n  const size_t n_packets = all_packets.size();\n  CHECK(n_packets == 4);\n  for (size_t n = 0; n < n_packets; n++) {\n    // Check that -p_mu u^\\mu is the expected energy of the neutrinos\n    const double neutrino_energy =\n        W_boost * (all_packets[n].momentum_upper_t -\n                   all_packets[n].momentum.get(1) * v_boost);\n    CHECK(fabs(all_packets[n].number_of_neutrinos * neutrino_energy - 2.0) <\n          1.e-14);\n    CHECK(all_packets[n].index_of_closest_grid_point == 25);\n    CHECK(((all_packets[n].coordinates.get(0) >= -1.0 / 3.0) &&\n           (all_packets[n].coordinates.get(0) <= 1.0 / 3.0)));\n    CHECK((all_packets[n].coordinates.get(1) >= 1.0 / 3.0));\n    CHECK((all_packets[n].coordinates.get(2) >= 1.0 / 3.0));\n  }\n  CHECK(gsl::at(energy_at_bin_center, 0) == 2.0);\n\n  Scalar<DataVector> expected_coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(zero_dv, 0.0);\n  get(expected_coupling_tilde_tau)[25] = -16.0;\n  Scalar<DataVector> expected_coupling_rho_ye =\n      make_with_value<Scalar<DataVector>>(zero_dv, 0.0);\n  get(expected_coupling_rho_ye)[25] = -2.8 * proton_mass;\n  tnsr::i<DataVector, 3, Frame::Inertial> expected_coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(zero_dv, 0.0);\n  expected_coupling_tilde_s.get(1)[25] = -8.0 * sqrt(3.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      coupling_tilde_tau, expected_coupling_tilde_tau,\n      Approx::custom().epsilon(epsilon_approx).scale(1.0));\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      coupling_rho_ye, expected_coupling_rho_ye,\n      Approx::custom().epsilon(epsilon_approx).scale(1.0));\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      coupling_tilde_s, expected_coupling_tilde_s,\n      Approx::custom().epsilon(epsilon_approx).scale(1.0));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_EvolvePackets.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/EvolvePackets.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n\nusing hydro::units::nuclear::proton_mass;\n\nnamespace {\n\nvoid test_evolve_minkowski() {\n  const size_t mesh_1d_size = 2;\n  const Mesh<3> mesh(mesh_1d_size, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered);\n  const size_t num_ghost_zones = 1;\n  const size_t extended_mesh_1d_size = mesh_1d_size + 2 * num_ghost_zones;\n  const Mesh<3> extended_mesh(extended_mesh_1d_size,\n                              Spectral::Basis::FiniteDifference,\n                              Spectral::Quadrature::CellCentered);\n\n  MAKE_GENERATOR(generator);\n\n  const size_t dv_size = mesh.number_of_grid_points();\n  const size_t extended_dv_size = extended_mesh.number_of_grid_points();\n  const DataVector zero_dv(dv_size, 0.0);\n  DataVector extended_zero_dv(extended_dv_size, 0.0);\n  // Minkowski metric\n  Scalar<DataVector> lapse{DataVector(dv_size, 1.0)};\n  Scalar<DataVector> lorentz_factor{\n      DataVector{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}};\n\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  inv_spatial_metric.get(0, 0) = 1.0;\n  inv_spatial_metric.get(1, 1) = 1.0;\n  inv_spatial_metric.get(2, 2) = 1.0;\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  spatial_metric.get(0, 0) = 1.0;\n  spatial_metric.get(1, 1) = 1.0;\n  spatial_metric.get(2, 2) = 1.0;\n\n  tnsr::I<DataVector, 3, Frame::Inertial> shift =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> lower_spatial_four_velocity =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> d_lapse =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::iJ<DataVector, 3, Frame::Inertial> d_shift =\n      make_with_value<tnsr::iJ<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::iJJ<DataVector, 3, Frame::Inertial> d_inv_spatial_metric =\n      make_with_value<tnsr::iJJ<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n\n  // Mesh velocity set to std::null for now\n  const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>> mesh_velocity =\n      std::nullopt;\n\n  // Jacobian set to identity for now\n  InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      inverse_jacobian_inertial_to_fluid = make_with_value<\n          InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse,\n                                                                         0.0);\n  inverse_jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n  Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      jacobian_inertial_to_fluid = make_with_value<\n          Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse, 0.0);\n  jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n\n  // Logical to inertial inverse jacobian, also identity for now\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian =\n          make_with_value<InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                                          Frame::Inertial>>(lapse, 0.0);\n  inverse_jacobian.get(0, 0) = 1.0;\n  inverse_jacobian.get(1, 1) = 1.0;\n  inverse_jacobian.get(2, 2) = 1.0;\n\n  // Coordinates\n  tnsr::I<DataVector, 3, Frame::ElementLogical> mesh_coordinates =\n      make_with_value<tnsr::I<DataVector, 3, Frame::ElementLogical>>(lapse,\n                                                                     0.0);\n  mesh_coordinates.get(0) =\n      DataVector{-0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5};\n  mesh_coordinates.get(1) =\n      DataVector{-0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5};\n  mesh_coordinates.get(2) =\n      DataVector{-0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5};\n  Scalar<DataVector> cell_light_crossing_time =\n      make_with_value<Scalar<DataVector>>(zero_dv, 1.0);\n\n  Particles::MonteCarlo::Packet packet(1, 1.0, 0, 0.0, -0.75, -1.0, -1.0, 1.0,\n                                       1.0, 0.0, 0.0);\n  packet.renormalize_momentum(inv_spatial_metric, lapse);\n  CHECK(packet.momentum_upper_t == 1.0);\n\n  const std::array<double, 2> energy_at_bin_center = {2.0, 5.0};\n  std::array<std::array<DataVector, 2>, 2> absorption_opacity = {\n      std::array<DataVector, 2>{{extended_zero_dv, extended_zero_dv}},\n      std::array<DataVector, 2>{{extended_zero_dv, extended_zero_dv}}};\n  std::array<std::array<DataVector, 2>, 2> scattering_opacity = {\n      std::array<DataVector, 2>{{extended_zero_dv, extended_zero_dv}},\n      std::array<DataVector, 2>{{extended_zero_dv, extended_zero_dv}}};\n\n  // Set non-zero value that should never lead\n  // to interaction, to get non-zero interaction terms\n  // (the choices made for the minimum value of the\n  //  random number setting the time to next interaction\n  //  guarantees that for such low opacities, interactions\n  //  will not happen).\n  gsl::at(gsl::at(absorption_opacity, 1), 0) = 1.e-60;\n  gsl::at(gsl::at(scattering_opacity, 1), 0) = 1.e-59;\n\n  Scalar<DataVector> coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(extended_zero_dv, 0.0);\n  Scalar<DataVector> coupling_rho_ye =\n      make_with_value<Scalar<DataVector>>(extended_zero_dv, 0.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(extended_zero_dv,\n                                                               0.0);\n  std::vector<Particles::MonteCarlo::Packet> packets{packet};\n  Particles::MonteCarlo::TemplatedLocalFunctions<2, 2> MonteCarloStruct;\n  MonteCarloStruct.evolve_packets(\n      &packets, &generator, &coupling_tilde_tau, &coupling_tilde_s,\n      &coupling_rho_ye, 1.5, mesh, mesh_coordinates, num_ghost_zones,\n      absorption_opacity, scattering_opacity, energy_at_bin_center,\n      lorentz_factor, lower_spatial_four_velocity, lapse, shift, d_lapse,\n      d_shift, d_inv_spatial_metric, spatial_metric, inv_spatial_metric,\n      cell_light_crossing_time, mesh_velocity, inverse_jacobian,\n      jacobian_inertial_to_fluid, inverse_jacobian_inertial_to_fluid);\n  CHECK(packets[0].species == 1);\n  CHECK(packets[0].coordinates.get(0) == 0.75);\n  CHECK(packets[0].coordinates.get(1) == -1.0);\n  CHECK(packets[0].coordinates.get(2) == -1.0);\n  CHECK(packets[0].momentum.get(0) == 1.0);\n  CHECK(packets[0].momentum.get(1) == 0.0);\n  CHECK(packets[0].momentum.get(2) == 0.0);\n  CHECK(packets[0].time == 1.5);\n  CHECK(packets[0].index_of_closest_grid_point == 1);\n  // Check coupling terms against analytical expectations\n  // Note that the packet spends dt = 1.0 in cell 0 and\n  // dt=0.5 in cell 1 (due to the use of partial time steps)\n  // and that we need to convert these 'live points' index\n  // to the mesh with ghost zones.\n  const size_t ext_idx_0 =\n      num_ghost_zones + num_ghost_zones * extended_mesh_1d_size +\n      num_ghost_zones * extended_mesh_1d_size * extended_mesh_1d_size;\n  CHECK(fabs(get(coupling_tilde_tau)[ext_idx_0] - 1.0e-60) < 1.5e-75);\n  CHECK(fabs(coupling_tilde_s.get(0)[ext_idx_0] - 1.1e-59) < 1.65e-74);\n  CHECK(coupling_tilde_s.get(1)[ext_idx_0] == 0.0);\n  CHECK(coupling_tilde_s.get(2)[ext_idx_0] == 0.0);\n  CHECK(fabs(get(coupling_rho_ye)[ext_idx_0] + proton_mass * 1.0e-60) < 1.e-72);\n  CHECK(fabs(get(coupling_tilde_tau)[ext_idx_0 + 1] - 5.0e-61) < 1.5e-75);\n  CHECK(fabs(coupling_tilde_s.get(0)[ext_idx_0 + 1] - 5.5e-60) < 1.65e-74);\n  CHECK(coupling_tilde_s.get(1)[ext_idx_0 + 1] == 0.0);\n  CHECK(coupling_tilde_s.get(2)[ext_idx_0 + 1] == 0.0);\n  CHECK(fabs(get(coupling_rho_ye)[ext_idx_0 + 1] + proton_mass * 5.0e-61)\n    < 1.e-72);\n}\ntnsr::I<DataVector, 3, Frame::ElementLogical> spatial_coords_logical(\n   const Mesh<3>& mesh) {\n  const DataVector used_for_size(mesh.number_of_grid_points());\n  auto x = make_with_value<tnsr::I<DataVector, 3, Frame::ElementLogical>>(\n      used_for_size, 0.0);\n  const Index<3>& extents = mesh.extents();\n  Index<3> cur_extents{0, 0, 0};\n  for (cur_extents[0] = 0; cur_extents[0] < extents[0]; cur_extents[0]++) {\n    for (cur_extents[1] = 0; cur_extents[1] < extents[1]; cur_extents[1]++) {\n      for (cur_extents[2] = 0; cur_extents[2] < extents[2]; cur_extents[2]++) {\n        const size_t storage_index = mesh.storage_index(cur_extents);\n        x.get(0)[storage_index] =\n            -1.0 + 2.0 * (static_cast<double>(cur_extents[0]) + 0.5) /\n                       static_cast<double>(extents[0]);\n        x.get(1)[storage_index] =\n            -1.0 + 2.0 * (static_cast<double>(cur_extents[1]) + 0.5) /\n                       static_cast<double>(extents[1]);\n        x.get(2)[storage_index] =\n            -1.0 + 2.0 * (static_cast<double>(cur_extents[2]) + 0.5) /\n                       static_cast<double>(extents[2]);\n      }\n    }\n  }\n  return x;\n}\n  // inertial coordinates are set such that the element used for the\n  // evolution is centered on (6,0,0), and the jacobian matrix\n  // is the identity matrix.\ntnsr::I<DataVector, 3, Frame::Inertial> spatial_coords_inertial(\n    tnsr::I<DataVector, 3, Frame::ElementLogical> logical_coords) {\n  auto x = make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(\n      logical_coords.get(0), 0.0);\n  x.get(0) = logical_coords.get(0) + 6.0;\n  x.get(1) = logical_coords.get(1);\n  x.get(2) = logical_coords.get(2);\n  return x;\n}\n  // Evolve a single Monte-Carlo packet along Kerr geodesic\n  // with check that final position matches analytical expectations\nvoid test_evolve_kerr(const size_t mesh_size_1d) {\n  MAKE_GENERATOR(generator);\n\n  // Parameters for KerrSchild solution\n  const double mass = 1.01;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  const double t = 1.3;\n  // Evaluate solution\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  // Set domain and coordintes\n  const Mesh<3> mesh(mesh_size_1d, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered);\n  const size_t num_ghost_zones = 1;\n  const size_t extended_mesh_1d_size = mesh_size_1d + 2 * num_ghost_zones;\n  const Mesh<3> extended_mesh(extended_mesh_1d_size,\n                              Spectral::Basis::FiniteDifference,\n                              Spectral::Quadrature::CellCentered);\n  const size_t dv_size = mesh.number_of_grid_points();\n  const size_t extended_dv_size = extended_mesh.number_of_grid_points();\n  const DataVector zero_dv(dv_size, 0.0);\n  DataVector extended_zero_dv(extended_dv_size, 0.0);\n  const auto mesh_coordinates = spatial_coords_logical(mesh);\n  const auto inertial_coordinates = spatial_coords_inertial(mesh_coordinates);\n\n  // Mesh velocity set to std::null for now\n  const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>> mesh_velocity =\n      std::nullopt;\n\n  // Compute metric quantities\n  const auto vars = solution.variables(\n      inertial_coordinates, t,\n      typename gr::Solutions::KerrSchild::tags<DataVector, Frame::Inertial>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& deriv_lapse = get<typename gr::Solutions::KerrSchild::DerivLapse<\n      DataVector, Frame::Inertial>>(vars);\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, 3, Frame::Inertial>>(vars);\n  const auto& deriv_shift = get<typename gr::Solutions::KerrSchild::DerivShift<\n      DataVector, Frame::Inertial>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>>(vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>>(vars);\n  const Scalar<DataVector> cell_light_crossing_time =\n      make_with_value<Scalar<DataVector>>(zero_dv, 1.0);\n  const auto& deriv_spatial_metric =\n      get<typename gr::Solutions::KerrSchild::DerivSpatialMetric<\n          DataVector, Frame::Inertial>>(vars);\n\n  tnsr::iJJ<DataVector, 3, Frame::Inertial> deriv_inverse_spatial_metric =\n      make_with_value<tnsr::iJJ<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = i; j < 3; j++) {\n      for (size_t k = 0; k < 3; k++) {\n        for (size_t l = 0; l < 3; l++) {\n          for (size_t m = 0; m < 3; m++) {\n            deriv_inverse_spatial_metric.get(k, i, j) -=\n                inverse_spatial_metric.get(i, l) *\n                inverse_spatial_metric.get(j, m) *\n                deriv_spatial_metric.get(k, l, m);\n          }\n        }\n      }\n    }\n  }\n\n  // Fluid quantities\n  const Scalar<DataVector> lorentz_factor =\n      make_with_value<Scalar<DataVector>>(lapse, 1.0);\n  const tnsr::i<DataVector, 3, Frame::Inertial> lower_spatial_four_velocity =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n\n  // Initialize packet on the x-axis\n  const double dx = 2.0 / static_cast<double>(mesh_size_1d);\n  Particles::MonteCarlo::Packet packet(0, 1.0, 5, 0.0, 0.5, 0.0, 0.0, 1.0, 0.0,\n                                       1.0, 0.0);\n  // Self-consistency: update index of closest point and p^t\n  std::array<size_t, 3> closest_point_index_3d{0, 0, 0};\n  for (size_t d = 0; d < 3; d++) {\n    gsl::at(closest_point_index_3d, d) =\n        std::floor((packet.coordinates[d] - mesh_coordinates.get(d)[0])\n        /(2.0 /static_cast<double>( mesh_size_1d)) + 0.5);\n  }\n  const Index<3>& extents = mesh.extents();\n  packet.index_of_closest_grid_point =\n      closest_point_index_3d[0] + extents[0]\n         * (closest_point_index_3d[1] + extents[1] * closest_point_index_3d[2]);\n  packet.renormalize_momentum(inverse_spatial_metric, lapse);\n\n  // Jacobians set to identity for now\n  InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      inverse_jacobian_inertial_to_fluid = make_with_value<\n          InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse,\n                                                                         0.0);\n  inverse_jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n  Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n\n      jacobian_inertial_to_fluid = make_with_value<\n          Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse, 0.0);\n  jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n  // Logical to inertial inverse jacobian, also identity for now\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian =\n          make_with_value<InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                                          Frame::Inertial>>(lapse, 0.0);\n  inverse_jacobian.get(0, 0) = 1.0;\n  inverse_jacobian.get(1, 1) = 1.0;\n  inverse_jacobian.get(2, 2) = 1.0;\n\n  // Interaction rates\n  const std::array<double, 2> energy_at_bin_center = {2.0, 5.0};\n  const std::array<std::array<DataVector, 2>, 2> absorption_opacity = {\n      std::array<DataVector, 2>{{extended_zero_dv, extended_zero_dv}},\n      std::array<DataVector, 2>{{extended_zero_dv, extended_zero_dv}}};\n  const std::array<std::array<DataVector, 2>, 2> scattering_opacity = {\n      std::array<DataVector, 2>{{extended_zero_dv, extended_zero_dv}},\n      std::array<DataVector, 2>{{extended_zero_dv, extended_zero_dv}}};\n\n  Scalar<DataVector> coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(extended_zero_dv, 0.0);\n  Scalar<DataVector> coupling_rho_ye =\n      make_with_value<Scalar<DataVector>>(extended_zero_dv, 0.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(extended_zero_dv,\n                                                               0.0);\n\n  std::vector<Particles::MonteCarlo::Packet> packets{packet};\n  Particles::MonteCarlo::TemplatedLocalFunctions<2, 2> MonteCarloStruct;\n  // Getting CFL constant\n  const double cfl_constant = 0.25;\n  const double final_time = 1.0;\n  const double dt_step = cfl_constant * dx;\n  double current_time = 0.0;\n  packets[0].renormalize_momentum(inverse_spatial_metric, lapse);\n\n  //time evolution with step size dt\n  while (current_time < final_time) {\n   const double dt = std::min(dt_step, final_time - current_time);\n    MonteCarloStruct.evolve_packets(\n        &packets, &generator, &coupling_tilde_tau, &coupling_tilde_s,\n        &coupling_rho_ye, current_time + dt, mesh, mesh_coordinates,\n        num_ghost_zones, absorption_opacity, scattering_opacity,\n        energy_at_bin_center, lorentz_factor, lower_spatial_four_velocity,\n        lapse, shift, deriv_lapse, deriv_shift, deriv_inverse_spatial_metric,\n        spatial_metric, inverse_spatial_metric, cell_light_crossing_time,\n        mesh_velocity, inverse_jacobian, jacobian_inertial_to_fluid,\n        inverse_jacobian_inertial_to_fluid);\n    current_time += dt;\n  }\n  const double final_r = sqrt(pow(packets[0].coordinates.get(0) + 6.0, 2) +\n   pow(packets[0].coordinates.get(1), 2)+pow(packets[0].coordinates.get(2),2));\n\n  //Check r against geodesic evolution value obtained from python code\n  CHECK(std::abs(final_r - 6.298490) < 1e-2);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloEvolution\",\n                  \"[Unit][Evolution]\") {\n  test_evolve_minkowski();\n  test_evolve_kerr(15);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_FluidCouplingAction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Actions/FluidCouplingAction.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n  using flux_variables = tmpl::list<Var1>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n\n  using initial_tags = tmpl::list<\n      domain::Tags::Mesh<Dim>, evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::ActiveGrid, domain::Tags::Element<Dim>,\n      ::Tags::Variables<tmpl::list<Var1>>,\n      grmhd::ValenciaDivClean::Tags::TildeTau,\n      grmhd::ValenciaDivClean::Tags::TildeYe,\n      grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>,\n      Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,\n      Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,\n      Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>,\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToInertial>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<\n          ActionTesting::InitializeDataBox<initial_tags>,\n          Actions::MutateApply<Particles::MonteCarlo::FluidCouplingMutator>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using system = System<Dim>;\n\n  using const_global_cache_tags =\n      tmpl::list<hydro::Tags::GrmhdEquationOfState,\n                 Particles::MonteCarlo::Tags::InteractionRatesTable<4, 3>>;\n};\n\nvoid test_fluid_coupling() {\n  const size_t Dim = 3;\n\n  using metavars = Metavariables<Dim>;\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const evolution::dg::subcell::ActiveGrid active_grid =\n      evolution::dg::subcell::ActiveGrid::Subcell;\n  const size_t n_pts = subcell_mesh.number_of_grid_points();\n\n  const DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  const ElementId<Dim> self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {0, 0}}}};\n  const Element<Dim> element{self_id, neighbors};\n\n  using evolved_vars_tags = tmpl::list<Var1>;\n  Variables<evolved_vars_tags> evolved_vars{n_pts};\n  // Set Var1 to the logical coords, just need some data\n  get(get<Var1>(evolved_vars)) = get<0>(logical_coordinates(subcell_mesh));\n\n  const DataVector zero_dv(n_pts, 0.0);\n\n  // Fluid variables\n  Scalar<DataVector> tilde_tau{DataVector(n_pts, 0.1)};\n  Scalar<DataVector> tilde_ye{DataVector(n_pts, 0.1)};\n  tnsr::i<DataVector, 3, Frame::Inertial> tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(tilde_tau, 0.0);\n\n  // Coupling data\n  const auto& subcell_extents = subcell_mesh.extents();\n  const size_t mesh_size_1d = subcell_extents[0];\n  const size_t num_ghost_zones = 1;\n  const size_t mesh_size_with_ghost_1d = mesh_size_1d + 2 * num_ghost_zones;\n  size_t mesh_size_with_ghost = 1;\n  for (size_t d = 0; d < Dim; d++) {\n    mesh_size_with_ghost *= mesh_size_with_ghost_1d;\n  }\n  const DataVector zero_dv_with_ghost(mesh_size_with_ghost, 0.0);\n  Scalar<DataVector> coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(zero_dv_with_ghost, 0.0);\n  Scalar<DataVector> coupling_tilde_rho_ye =\n      make_with_value<Scalar<DataVector>>(zero_dv_with_ghost, 0.0);\n  tnsr::i<DataVector, Dim> coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, Dim>>(zero_dv_with_ghost, 0.0);\n  alg::iota(get(coupling_tilde_tau), 1.0);\n  alg::iota(get(coupling_tilde_rho_ye), 2.0);\n  alg::iota(coupling_tilde_s.get(0), 3.0);\n  alg::iota(coupling_tilde_s.get(1), 4.0);\n  alg::iota(coupling_tilde_s.get(2), 5.0);\n\n  // Jacobian for volume element\n  Scalar<DataVector> det_inverse_jacobian_logical_to_inertial(n_pts, 2.0);\n  const double cell_volume =\n      8.0 / static_cast<double>(subcell_mesh.number_of_grid_points()) / 2.0;\n\n  // Expected post-coupling values of fluid variables\n  Scalar<DataVector> expected_tilde_tau{DataVector(n_pts, 0.0)};\n  Scalar<DataVector> expected_tilde_ye{DataVector(n_pts, 0.0)};\n  tnsr::i<DataVector, 3, Frame::Inertial> expected_tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(tilde_tau, 0.0);\n  for (size_t i = 0; i < mesh_size_1d; i++) {\n    for (size_t j = 0; j < mesh_size_1d; j++) {\n      for (size_t k = 0; k < mesh_size_1d; k++) {\n        const size_t local_idx = i + mesh_size_1d * (j + k * mesh_size_1d);\n        const size_t extended_idx =\n            num_ghost_zones + i +\n            mesh_size_with_ghost_1d *\n                (num_ghost_zones + j +\n                 (num_ghost_zones + k) * mesh_size_with_ghost_1d);\n        get(expected_tilde_tau)[local_idx] =\n            get(tilde_tau)[local_idx] +\n            get(coupling_tilde_tau)[extended_idx] / cell_volume;\n        get(expected_tilde_ye)[local_idx] =\n            get(tilde_ye)[local_idx] +\n            get(coupling_tilde_rho_ye)[extended_idx] / cell_volume;\n        for (size_t d = 0; d < Dim; d++) {\n          expected_tilde_s.get(d)[local_idx] =\n              tilde_s.get(d)[local_idx] +\n              coupling_tilde_s.get(d)[extended_idx] / cell_volume;\n        }\n      }\n    }\n  }\n\n  MockRuntimeSystem runner{{}};\n\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, self_id,\n      {dg_mesh, subcell_mesh, active_grid, element, evolved_vars, tilde_tau,\n       tilde_ye, tilde_s, coupling_tilde_tau, coupling_tilde_rho_ye,\n       coupling_tilde_s, det_inverse_jacobian_logical_to_inertial});\n\n  // Run singe time step\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  const auto& tilde_tau_from_box =\n      ActionTesting::get_databox_tag<comp,\n                                     grmhd::ValenciaDivClean::Tags::TildeTau>(\n          runner, self_id);\n  const auto& tilde_ye_from_box =\n      ActionTesting::get_databox_tag<comp,\n                                     grmhd::ValenciaDivClean::Tags::TildeYe>(\n          runner, self_id);\n  const auto& tilde_s_from_box = ActionTesting::get_databox_tag<\n      comp, grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(runner,\n                                                                    self_id);\n  const auto& coupling_tilde_tau_from_box = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>>(runner,\n                                                                       self_id);\n  const auto& coupling_tilde_ye_from_box = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>>(\n      runner, self_id);\n  const auto& coupling_tilde_s_from_box = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>>(\n      runner, self_id);\n  CHECK_ITERABLE_APPROX(tilde_tau_from_box, expected_tilde_tau);\n  CHECK_ITERABLE_APPROX(tilde_ye_from_box, expected_tilde_ye);\n  CHECK_ITERABLE_APPROX(tilde_s_from_box, expected_tilde_s);\n  CHECK_ITERABLE_APPROX(get(coupling_tilde_tau_from_box), zero_dv_with_ghost);\n  CHECK_ITERABLE_APPROX(get(coupling_tilde_ye_from_box), zero_dv_with_ghost);\n  for (size_t d = 0; d < Dim; d++) {\n    CHECK_ITERABLE_APPROX(coupling_tilde_s_from_box.get(d), zero_dv_with_ghost);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloFluidCouplingAction\",\n                  \"[Unit][Evolution]\") {\n  test_fluid_coupling();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_ImplicitMonteCarloCorrections.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp\"\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloImplicitCorrections\",\n                  \"[Unit][Evolution]\") {\n  const size_t dv_size = 1;\n\n  DataVector zero_dv(dv_size, 0.0);\n  std::array<std::array<DataVector, 4>, 3> emission_in_cells = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 4>, 3> absorption_opacity = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 4>, 3> scattering_opacity = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 4>, 3> fraction_ka_to_ks = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n\n  Scalar<DataVector> baryon_density(dv_size, 1.619109365278362e-05);\n  Scalar<DataVector> temperature(dv_size, 10.0);\n  Scalar<DataVector> electron_fraction(dv_size, 0.06);\n  Scalar<DataVector> cell_light_crossing_time(dv_size, 1.1);\n  const double minimum_temperature = 0.0;\n\n  const std::string h5_file_name_nulib{\n      unit_test_src_path() +\n      \"Evolution/Particles/MonteCarlo/NuLib_TestTable.h5\"};\n  const Particles::MonteCarlo::NeutrinoInteractionTable<4, 3> interaction_table(\n      h5_file_name_nulib);\n\n  std::string h5_file_name_compose{\n      unit_test_src_path() +\n      \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n  EquationsOfState::Tabulated3D<true> equation_of_state(h5_file_name_compose,\n                                                        \"/dd2\");\n\n  const Mesh<3> mesh(1, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered);\n  Particles::MonteCarlo::TemplatedLocalFunctions<4, 3> MonteCarloStruct;\n  MonteCarloStruct.implicit_monte_carlo_interaction_rates(\n      &emission_in_cells, &absorption_opacity, &scattering_opacity,\n      &fraction_ka_to_ks, cell_light_crossing_time, electron_fraction,\n      baryon_density, temperature, minimum_temperature, interaction_table,\n      equation_of_state);\n\n  std::array<std::array<DataVector, 4>, 3> emission_in_cells_0 = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 4>, 3> absorption_opacity_0 = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 4>, 3> scattering_opacity_0 = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n  interaction_table.get_neutrino_matter_interactions(\n      &emission_in_cells_0, &absorption_opacity_0, &scattering_opacity_0,\n      electron_fraction, baryon_density, temperature,\n      minimum_temperature);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(absorption_opacity_0 + scattering_opacity_0,\n                               absorption_opacity + scattering_opacity,\n                               Approx::custom().epsilon(1.e-15).scale(1.0));\n  for (size_t ng = 0; ng < 4; ng++) {\n    for (size_t ns = 0; ns < 3; ns++) {\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          gsl::at(gsl::at(scattering_opacity, ns), ng),\n          gsl::at(gsl::at(scattering_opacity_0, ns), ng) +\n              gsl::at(gsl::at(absorption_opacity_0, ns), ng) *\n                  gsl::at(gsl::at(fraction_ka_to_ks, ns), ng),\n          Approx::custom().epsilon(1.e-15).scale(1.0));\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          gsl::at(gsl::at(emission_in_cells, ns), ng) /\n              gsl::at(gsl::at(absorption_opacity, ns), ng),\n          gsl::at(gsl::at(emission_in_cells_0, ns), ng) /\n              gsl::at(gsl::at(absorption_opacity_0, ns), ng),\n          Approx::custom().epsilon(1.e-15).scale(1.0));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_InitializeAction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Actions/InitializeMonteCarlo.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MonteCarloOptions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/HomogeneousSphere.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n// inertial coordinates set to logical coords here\ntnsr::I<DataVector, 3, Frame::Inertial> spatial_coords_inertial(\n    tnsr::I<DataVector, 3, Frame::ElementLogical> logical_coords) {\n  auto x = make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(\n      logical_coords.get(0), 0.0);\n  x.get(0) = logical_coords.get(0);\n  x.get(1) = logical_coords.get(1);\n  x.get(2) = logical_coords.get(2);\n  return x;\n}\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n  using hydro_variables_tag = ::Tags::Variables<hydro::grmhd_tags<DataVector>>;\n  using flux_variables = tmpl::list<Var1>;\n};\n\ntemplate <size_t Dim, typename Metavariables, size_t EnergyBins,\n          size_t NeutrinoSpecies>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n\n  using initial_tags = tmpl::list<\n      domain::Tags::Mesh<Dim>, evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::ActiveGrid, domain::Tags::Element<Dim>,\n      ::Tags::Variables<tmpl::list<Var1>>, ::Tags::Time,\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::Inertial>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<initial_tags>,\n                 Initialization::Actions::InitializeMCTags<\n                     System<Dim>, EnergyBins, NeutrinoSpecies, true>>>>;\n};\n\ntemplate <size_t Dim, size_t EnergyBins, size_t NeutrinoSpecies>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list =\n      tmpl::list<component<Dim, Metavariables, EnergyBins, NeutrinoSpecies>>;\n  using system = System<Dim>;\n\n  using initial_data_list =\n      RadiationTransport::MonteCarlo::Solutions::all_solutions;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>>;\n  };\n\n  using const_global_cache_tags = tmpl::list<\n      hydro::Tags::GrmhdEquationOfState,\n      Particles::MonteCarlo::Tags::MonteCarloOptions<NeutrinoSpecies>,\n      evolution::initial_data::Tags::InitialData>;\n};\n\nvoid test_initialize_monte_carlo() {\n  const size_t Dim = 3;\n  const size_t energy_bins = 4;\n  const size_t neutrino_species = 3;\n\n  register_classes_with_charm<EquationsOfState::Tabulated3D<true>>();\n  register_classes_with_charm<\n      Particles::MonteCarlo::MonteCarloOptions<neutrino_species>>();\n  register_classes_with_charm<\n      RadiationTransport::MonteCarlo::Solutions::HomogeneousSphere>();\n\n  // Fake EoS and NuLib tables\n  const std::string h5_file_name_compose{\n      unit_test_src_path() +\n      \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n  std::unique_ptr<EquationsOfState::EquationOfState<true, 3>>\n      equation_of_state_ptr =\n          std::make_unique<EquationsOfState::Tabulated3D<true>>(\n              h5_file_name_compose, \"/dd2\");\n\n  using metavars = Metavariables<Dim, energy_bins, neutrino_species>;\n  using comp = component<Dim, metavars, energy_bins, neutrino_species>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n\n  const double time = 0.0;\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const evolution::dg::subcell::ActiveGrid active_grid =\n      evolution::dg::subcell::ActiveGrid::Subcell;\n  const size_t n_pts = subcell_mesh.number_of_grid_points();\n\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  const ElementId<Dim> self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {0, 0}}}};\n  const ElementId<Dim> east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}, {0, 0}}}};\n  const ElementId<Dim> south_id = ElementId<Dim>{1, {{{0, 0}, {0, 0}, {0, 0}}}};\n  neighbors[Direction<Dim>::upper_xi()] =\n      Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n  const OrientationMap<Dim> orientation = OrientationMap<Dim>{\n      std::array{Direction<Dim>::lower_xi(), Direction<Dim>::lower_eta(),\n                 Direction<Dim>::upper_zeta()}};\n  neighbors[Direction<Dim>::lower_eta()] =\n      Neighbors<Dim>{{south_id}, orientation};\n  const Element<Dim> element{self_id, neighbors};\n\n  using evolved_vars_tags = tmpl::list<Var1>;\n  Variables<evolved_vars_tags> evolved_vars{n_pts};\n  // Set Var1 to the logical coords, just need some data\n  get(get<Var1>(evolved_vars)) = get<0>(logical_coordinates(subcell_mesh));\n\n  const DataVector zero_dv(n_pts, 0.0);\n\n  // Mesh with ghost zones, for size checking\n  const auto& subcell_extents = subcell_mesh.extents();\n  const size_t num_ghost_zones = 1;\n  size_t mesh_size_with_ghost = 1;\n  size_t mortar_data_size = 1;\n  size_t coupling_data_size = 1;\n  for (size_t d = 0; d < Dim; d++) {\n    mesh_size_with_ghost *= subcell_extents[d] + 2 * num_ghost_zones;\n    if (d < Dim - 1) {\n      mortar_data_size *= subcell_extents[d];\n      coupling_data_size *= subcell_extents[d] + 2 * num_ghost_zones;\n    }\n  }\n\n  // Grid coordinates on subcell mesh\n  const size_t mesh_size = subcell_extents[0];\n  CHECK(subcell_mesh.extents()[1] == mesh_size);\n  CHECK(subcell_mesh.extents()[2] == mesh_size);\n  tnsr::I<DataVector, 3, Frame::ElementLogical> mesh_coordinates =\n      logical_coordinates(subcell_mesh);\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coordinates =\n      spatial_coords_inertial(mesh_coordinates);\n\n  // Set up Monte Carlo options\n  std::array<double, neutrino_species> initial_packet_energy = {1.e-12, 1.e-12,\n                                                                1.e-12};\n  std::unique_ptr<Particles::MonteCarlo::MonteCarloOptions<neutrino_species>>\n      monte_carlo_options_ptr = std::make_unique<\n          Particles::MonteCarlo::MonteCarloOptions<neutrino_species>>(\n          initial_packet_energy);\n\n  // Set up mock initial data\n  std::unique_ptr<RadiationTransport::MonteCarlo::Solutions::HomogeneousSphere>\n      initial_data_ptr = std::make_unique<\n          RadiationTransport::MonteCarlo::Solutions::HomogeneousSphere>(\n          1.0, std::array<double, 2>{1.0, 0.1}, std::array<double, 2>{1.0, 0.1},\n          std::array<double, 2>{0.1, 0.05}, std::move(equation_of_state_ptr));\n\n  MockRuntimeSystem runner{{std::move(equation_of_state_ptr),\n                            std::move(monte_carlo_options_ptr),\n                            std::move(initial_data_ptr)}};\n\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, self_id,\n      {dg_mesh, subcell_mesh, active_grid, element, evolved_vars, time,\n       mesh_coordinates, inertial_coordinates});\n\n  // Run initialize action\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  // Check that expected variables have been created\n  const std::vector<Particles::MonteCarlo::Packet>& packets_from_box =\n      ActionTesting::get_databox_tag<\n          comp, Particles::MonteCarlo::Tags::PacketsOnElement>(runner, self_id);\n  CHECK(packets_from_box.empty());\n  const std::mt19937& generator = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::RandomNumberGenerator>(runner,\n                                                                self_id);\n  // Min value is always zero per the mersenner twister engine documentation;\n  // called as a simple use of the generator.\n  CHECK(generator.min() == 0);\n  const std::array<DataVector, neutrino_species>& desired_energy_at_emission =\n      ActionTesting::get_databox_tag<\n          comp, Particles::MonteCarlo::Tags::DesiredPacketEnergyAtEmission<\n                    neutrino_species>>(runner, self_id);\n  for (size_t s = 0; s < neutrino_species; s++) {\n    for (size_t i = 0; i < n_pts; i++) {\n      CHECK(gsl::at(desired_energy_at_emission, s)[i] ==\n            gsl::at(initial_packet_energy, s));\n    }\n  }\n  // Check size of the fluid variables\n  const Scalar<DataVector>& lorentz_factor =\n      ActionTesting::get_databox_tag<comp,\n                                     hydro::Tags::LorentzFactor<DataVector>>(\n          runner, self_id);\n  CHECK(get(lorentz_factor).size() == n_pts);\n  // Check size of coupling terms\n  const Scalar<DataVector>& coupling_tilde_tau = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>>(runner,\n                                                                       self_id);\n  CHECK(get(coupling_tilde_tau).size() == mesh_size_with_ghost);\n\n  using MortarData =\n      typename Particles::MonteCarlo::Tags::MortarDataTag<Dim>::type;\n  const MortarData& mortar_data = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::MortarDataTag<Dim>>(runner, self_id);\n  using CouplingData =\n      typename Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<Dim>::type;\n  const CouplingData& coupling_data = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::GhostZoneCouplingDataTag<Dim>>(\n      runner, self_id);\n  const DirectionalId<Dim> east_neighbor_id{Direction<Dim>::upper_xi(),\n                                            east_id};\n  const DirectionalId<Dim> south_neighbor_id{Direction<Dim>::lower_eta(),\n                                             south_id};\n  CHECK(\n      (mortar_data.temperature.find(east_neighbor_id)->second).value().size() ==\n      mortar_data_size);\n  CHECK((mortar_data.cell_light_crossing_time.find(south_neighbor_id)->second)\n            .value()\n            .size() == mortar_data_size);\n  CHECK((coupling_data.coupling_tilde_tau.find(east_neighbor_id)->second)\n            .value()\n            .size() == coupling_data_size);\n  CHECK((coupling_data.coupling_tilde_s.find(south_neighbor_id)->second)\n            .value()\n            .get(Dim - 1)\n            .size() == coupling_data_size);\n  using GhostZoneData =\n      typename Particles::MonteCarlo::Tags::McGhostZoneDataTag<Dim>::type;\n  const GhostZoneData& ghost_data_in_box = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::McGhostZoneDataTag<Dim>>(runner,\n                                                                  self_id);\n  CHECK(ghost_data_in_box.empty());\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloInitializeAction\",\n                  \"[Unit][Evolution]\") {\n  test_initialize_monte_carlo();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_InterpolateOpacities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloInterpolateOpacities\",\n                  \"[Unit][Evolution]\") {\n  const double eps_check = 1.e-14;\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> rng_uniform_zero_to_one(0.0, 1.0);\n\n  // Zero vector for tensor creations\n  DataVector zero_dv(8, 0.0);\n\n  std::array<std::array<DataVector, 2>, 3> absorption_opacity_table = {\n      std::array<DataVector, 2>{{zero_dv, zero_dv}},\n      std::array<DataVector, 2>{{zero_dv, zero_dv}},\n      std::array<DataVector, 2>{{zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 2>, 3> scattering_opacity_table = {\n      std::array<DataVector, 2>{{zero_dv, zero_dv}},\n      std::array<DataVector, 2>{{zero_dv, zero_dv}},\n      std::array<DataVector, 2>{{zero_dv, zero_dv}}};\n  const std::array<double, 2> energy_at_bin_center = {2.0, 5.0};\n\n  // Set random interaction rates\n  for (size_t s = 0; s < 3; s++) {\n    for (size_t i = 0; i < zero_dv.size(); i++) {\n      for (size_t g = 0; g < 2; g++) {\n        gsl::at(gsl::at(absorption_opacity_table, s), g)[i] =\n            rng_uniform_zero_to_one(generator);\n        gsl::at(gsl::at(scattering_opacity_table, s), g)[i] =\n            rng_uniform_zero_to_one(generator);\n      }\n    }\n  }\n\n  // Get interaction rates from MC code and directly here, compare results.\n  Particles::MonteCarlo::TemplatedLocalFunctions<2, 3> MonteCarloStruct;\n  double absorption_opacity = 0.0;\n  double scattering_opacity = 0.0;\n  double expected_absorption_opacity = 0.0;\n  double expected_scattering_opacity = 0.0;\n  double fluid_frame_energy = 0.0;\n  for (size_t p = 0; p < zero_dv.size(); p++) {\n    fluid_frame_energy = 1.0 + 5.0 * rng_uniform_zero_to_one(generator);\n    MonteCarloStruct.interpolate_opacities_at_fluid_energy(\n        &absorption_opacity, &scattering_opacity, fluid_frame_energy, p % 3, p,\n        absorption_opacity_table, scattering_opacity_table,\n        energy_at_bin_center);\n    // The actual calculation has a floor on the opacity, which\n    // the test might run into.\n    gsl::at(gsl::at(absorption_opacity_table, p % 3), 0)[p] =\n        std::max(gsl::at(gsl::at(absorption_opacity_table, p % 3), 0)[p],\n                 MonteCarloStruct.opacity_floor);\n    gsl::at(gsl::at(absorption_opacity_table, p % 3), 1)[p] =\n        std::max(gsl::at(gsl::at(absorption_opacity_table, p % 3), 1)[p],\n                 MonteCarloStruct.opacity_floor);\n    gsl::at(gsl::at(scattering_opacity_table, p % 3), 0)[p] =\n        std::max(gsl::at(gsl::at(scattering_opacity_table, p % 3), 0)[p],\n                 MonteCarloStruct.opacity_floor);\n    gsl::at(gsl::at(scattering_opacity_table, p % 3), 1)[p] =\n        std::max(gsl::at(gsl::at(scattering_opacity_table, p % 3), 1)[p],\n                 MonteCarloStruct.opacity_floor);\n    if (fluid_frame_energy <= gsl::at(energy_at_bin_center, 0)) {\n      expected_absorption_opacity =\n          gsl::at(gsl::at(absorption_opacity_table, p % 3), 0)[p];\n      expected_scattering_opacity =\n          gsl::at(gsl::at(scattering_opacity_table, p % 3), 0)[p];\n    } else {\n      if (fluid_frame_energy >= gsl::at(energy_at_bin_center, 1)) {\n        expected_absorption_opacity =\n            gsl::at(gsl::at(absorption_opacity_table, p % 3), 1)[p];\n        expected_scattering_opacity =\n            gsl::at(gsl::at(scattering_opacity_table, p % 3), 1)[p];\n      } else {\n        double coef = (fluid_frame_energy - gsl::at(energy_at_bin_center, 0)) /\n                      (gsl::at(energy_at_bin_center, 1) -\n                       gsl::at(energy_at_bin_center, 0));\n        expected_absorption_opacity =\n            log(gsl::at(gsl::at(absorption_opacity_table, p % 3), 1)[p]) *\n                coef +\n            log(gsl::at(gsl::at(absorption_opacity_table, p % 3), 0)[p]) *\n                (1.0 - coef);\n        expected_scattering_opacity =\n            log(gsl::at(gsl::at(scattering_opacity_table, p % 3), 1)[p]) *\n                coef +\n            log(gsl::at(gsl::at(scattering_opacity_table, p % 3), 0)[p]) *\n                (1.0 - coef);\n        expected_absorption_opacity = exp(expected_absorption_opacity);\n        expected_scattering_opacity = exp(expected_scattering_opacity);\n      }\n    }\n    CHECK(fabs(expected_absorption_opacity - absorption_opacity) < eps_check);\n    CHECK(fabs(expected_scattering_opacity - scattering_opacity) < eps_check);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_InverseJacobianInertialToFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/InverseJacobianInertialToFluidCompute.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Particles.MonteCarlo.InverseJacobianInertialToFluid\",\n    \"[Unit][Evolution]\") {\n  const DataVector used_for_size(5);\n  MAKE_GENERATOR(generator);\n\n  const double epsilon_approx = 5.e-13;\n\n  const auto lapse =\n      TestHelpers::gr::random_lapse(make_not_null(&generator), used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<3>(make_not_null(&generator),\n                                                      used_for_size);\n  const auto spatial_metric = TestHelpers::gr::random_spatial_metric<3>(\n      make_not_null(&generator), used_for_size);\n  const auto lorentz_factor = TestHelpers::hydro::random_lorentz_factor(\n      make_not_null(&generator), used_for_size);\n  const auto spatial_velocity =\n      TestHelpers::hydro::random_velocity<DataVector, 3>(\n          make_not_null(&generator), lorentz_factor, spatial_metric);\n\n  TestHelpers::db::test_compute_tag<\n      Particles::MonteCarlo::InverseJacobianInertialToFluidCompute>(\n      \"InverseJacobian(Inertial,Fluid)\");\n  const auto box = db::create<\n      db::AddSimpleTags<\n          hydro::Tags::LorentzFactor<DataVector>,\n          hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>,\n          gr::Tags::Lapse<DataVector>,\n          gr::Tags::Shift<DataVector, 3, Frame::Inertial>,\n          gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>>,\n      db::AddComputeTags<\n          Particles::MonteCarlo::InverseJacobianInertialToFluidCompute>>(\n      lorentz_factor, spatial_velocity, lapse, shift, spatial_metric);\n\n  const auto& inverse_jacobian =\n      db::get<domain::Tags::InverseJacobian<4, Frame::Inertial, Frame::Fluid>>(\n          box);\n  const tnsr::aa<DataVector, 3, Frame::Inertial> spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n\n  // Check that the time vector is u^mu\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      inverse_jacobian.get(0, 0), get(lorentz_factor) / get(lapse),\n      Approx::custom().epsilon(epsilon_approx).scale(1.0));\n  for (size_t d = 0; d < 3; d++) {\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        inverse_jacobian.get(d + 1, 0),\n        get(lorentz_factor) *\n            (spatial_velocity.get(d) - shift.get(d) / get(lapse)),\n        Approx::custom().epsilon(epsilon_approx).scale(1.0));\n  }\n\n  // Test that we have orthonormal tetrads\n  DataVector dot_product_error(used_for_size);\n  DataVector norm(used_for_size);\n  for (size_t a = 0; a < 4; a++) {\n    for (size_t b = a; b < 4; b++) {\n      dot_product_error = 0.0;\n      norm = 0.0;\n      for (size_t d = 0; d < 4; d++) {\n        for (size_t dd = 0; dd < 4; dd++) {\n          dot_product_error += spacetime_metric.get(d, dd) *\n                               inverse_jacobian.get(d, a) *\n                               inverse_jacobian.get(dd, b);\n          norm += abs(spacetime_metric.get(d, dd) *\n                      inverse_jacobian.get(d, a) *\n                      inverse_jacobian.get(dd, b));\n        }\n      }\n      dot_product_error -= (a == b ? (a == 0 ? -1.0 : 1.0) : 0.0);\n      dot_product_error = dot_product_error / norm;\n      norm = 0.0;\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          dot_product_error, norm,\n          Approx::custom().epsilon(epsilon_approx).scale(1.0));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_NeutrinoInteractionTable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n\nnamespace {\nvoid test_explicit_interaction_table() {\n  const std::vector<double> table_log_density{-10.0, 0.0};\n  const std::vector<double> table_log_temperature{-2.0, 0.0};\n  const std::vector<double> table_electron_fraction{0.05, 0.06};\n  const size_t n_points_table = 8;\n  const size_t EnergyBins = 2;\n  const size_t NeutrinoSpecies = 2;\n  const size_t number_of_vars = 3 * EnergyBins * NeutrinoSpecies;\n  const std::array<double, EnergyBins> table_neutrino_energies = {1.0, 3.0};\n  std::vector<double> table_data{};\n  table_data.resize(number_of_vars * n_points_table);\n  // Fill in fake data for interaction rates. Fastest moving index is density,\n  // then temperature, then electron fraction.\n  // Here, we set interaction rates that linearly grow in log(density) so\n  // we fill all 'high density' points of the table.\n  for (size_t p = 1; p < n_points_table; p += 2) {\n    // Emissivity\n    // nu_e\n    table_data[p * number_of_vars] = 1.0;\n    table_data[p * number_of_vars + 1] = 9.0;\n    // nu_a\n    table_data[p * number_of_vars + EnergyBins] = 0.5;\n    table_data[p * number_of_vars + EnergyBins + 1] = 4.5;\n    // Absorption\n    size_t shift = EnergyBins * NeutrinoSpecies;\n    // nu_e\n    table_data[p * number_of_vars + shift] = 1.0;\n    table_data[p * number_of_vars + 1 + shift] = 9.0;\n    // nu_a\n    table_data[p * number_of_vars + EnergyBins + shift] = 0.5;\n    table_data[p * number_of_vars + EnergyBins + 1 + shift] = 4.5;\n    // Scattering\n    shift = 2 * EnergyBins * NeutrinoSpecies;\n    // nu_e\n    table_data[p * number_of_vars + shift] = 1.0;\n    table_data[p * number_of_vars + 1 + shift] = 9.0;\n    // nu_a\n    table_data[p * number_of_vars + EnergyBins + shift] = 0.5;\n    table_data[p * number_of_vars + EnergyBins + 1 + shift] = 4.5;\n  }\n  const Particles::MonteCarlo::NeutrinoInteractionTable<2, 2> interaction_table(\n      table_data, table_neutrino_energies, table_log_density,\n      table_log_temperature, table_electron_fraction);\n  CHECK(table_neutrino_energies == std::array<double, EnergyBins>{{1.0, 3.0}});\n\n  const size_t dv_size = 1;\n  DataVector zero_dv(dv_size, 0.0);\n  std::array<std::array<DataVector, 2>, 2> emission_in_cells = {\n      std::array<DataVector, 2>{{zero_dv, zero_dv}},\n      std::array<DataVector, 2>{{zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 2>, 2> absorption_opacity = {\n      std::array<DataVector, 2>{{zero_dv, zero_dv}},\n      std::array<DataVector, 2>{{zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 2>, 2> scattering_opacity = {\n      std::array<DataVector, 2>{{zero_dv, zero_dv}},\n      std::array<DataVector, 2>{{zero_dv, zero_dv}}};\n  const double minimum_temperature = 0.0;\n  Scalar<DataVector> baryon_density(dv_size, exp(-5.0));\n  Scalar<DataVector> temperature(dv_size, exp(-1.0));\n  Scalar<DataVector> electron_fraction(dv_size, 0.055);\n  interaction_table.get_neutrino_matter_interactions(\n      &emission_in_cells, &absorption_opacity, &scattering_opacity,\n      electron_fraction, baryon_density, temperature,\n      minimum_temperature);\n\n  CHECK(gsl::at(gsl::at(emission_in_cells, 0), 0)[0] == 0.5);\n  CHECK(gsl::at(gsl::at(emission_in_cells, 0), 1)[0] == 4.5);\n  CHECK(gsl::at(gsl::at(emission_in_cells, 1), 0)[0] == 0.25);\n  CHECK(gsl::at(gsl::at(emission_in_cells, 1), 1)[0] == 2.25);\n  CHECK(gsl::at(gsl::at(absorption_opacity, 0), 0)[0] == 0.5);\n  CHECK(gsl::at(gsl::at(absorption_opacity, 0), 1)[0] == 4.5);\n  CHECK(gsl::at(gsl::at(absorption_opacity, 1), 0)[0] == 0.25);\n  CHECK(gsl::at(gsl::at(absorption_opacity, 1), 1)[0] == 2.25);\n  CHECK(gsl::at(gsl::at(scattering_opacity, 0), 0)[0] == 0.5);\n  CHECK(gsl::at(gsl::at(scattering_opacity, 0), 1)[0] == 4.5);\n  CHECK(gsl::at(gsl::at(scattering_opacity, 1), 0)[0] == 0.25);\n  CHECK(gsl::at(gsl::at(scattering_opacity, 1), 1)[0] == 2.25);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloInteractionTable\",\n                  \"[Unit][Evolution]\") {\n  const std::string h5_file_name{\n      unit_test_src_path() +\n      \"Evolution/Particles/MonteCarlo/NuLib_TestTable.h5\"};\n  const Particles::MonteCarlo::NeutrinoInteractionTable<4, 3> interaction_table(\n      h5_file_name);\n  const double minimum_temperature = 0.0;\n\n  const size_t dv_size = 4;\n  DataVector zero_dv(4, 0.0);\n  std::array<std::array<DataVector, 4>, 3> emission_in_cells = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 4>, 3> absorption_opacity = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n  std::array<std::array<DataVector, 4>, 3> scattering_opacity = {\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}},\n      std::array<DataVector, 4>{{zero_dv, zero_dv, zero_dv, zero_dv}}};\n\n  // Point 0, using default values, is below the lower bound of\n  // the table in each dimension\n  Scalar<DataVector> baryon_density(dv_size, 1.e-16);\n  Scalar<DataVector> temperature(dv_size, 1.e-5);\n  Scalar<DataVector> electron_fraction(dv_size, 0.0);\n\n  // Point 1, 2 do not require interapolation, and are at points\n  // (0,0,0) and (1,1,1) of the  table in rho x temp x ye\n  // for the table NuLib_TestTable.h5\n  get(baryon_density)[1] = 1.619109365278362e-05;\n  get(baryon_density)[2] = 1.619109365278362e-05;\n  get(temperature)[1] = 10.0;\n  get(temperature)[2] = 10.0;\n  get(electron_fraction)[1] = 0.05;\n  get(electron_fraction)[2] = 0.06;\n\n  // Point 3 constructed to get 0.3 * (Point 1) + 0.7 * (Point 2)\n  // Note that we use interpolation in log density and log temperature, but\n  // linearly in electron fraction\n  get(baryon_density)[3] = exp(0.3 * log(get(baryon_density)[1]) +\n                               0.7 * log(get(baryon_density)[2]));\n  get(temperature)[3] =\n      exp(0.3 * log(get(temperature)[1]) + 0.7 * log(get(temperature)[2]));\n  get(electron_fraction)[3] =\n      0.3 * get(electron_fraction)[1] + 0.7 * get(electron_fraction)[2];\n\n  interaction_table.get_neutrino_matter_interactions(\n      &emission_in_cells, &absorption_opacity, &scattering_opacity,\n      electron_fraction, baryon_density, temperature,\n      minimum_temperature);\n\n  const std::array<double, 4>& table_neutrino_energies =\n      interaction_table.get_neutrino_energies();\n  const std::array<double, 4> expected_neutrino_energies = {2, 6, 9, 10.5};\n\n  CHECK(table_neutrino_energies == expected_neutrino_energies);\n\n  for (size_t ng = 0; ng < 4; ng++) {\n    for (size_t ns = 0; ns < 3; ns++) {\n      CHECK(fabs(gsl::at(gsl::at(emission_in_cells, ns), ng)[0] -\n                 gsl::at(gsl::at(emission_in_cells, ns), ng)[1]) <=\n            1.e-5 * (gsl::at(gsl::at(emission_in_cells, ns), ng)[0] +\n                     gsl::at(gsl::at(emission_in_cells, ns), ng)[1]));\n      CHECK(fabs(gsl::at(gsl::at(absorption_opacity, ns), ng)[0] -\n                 gsl::at(gsl::at(absorption_opacity, ns), ng)[1]) <=\n            1.e-5 * (gsl::at(gsl::at(absorption_opacity, ns), ng)[0] +\n                     gsl::at(gsl::at(absorption_opacity, ns), ng)[1]));\n      CHECK(fabs(gsl::at(gsl::at(scattering_opacity, ns), ng)[0] -\n                 gsl::at(gsl::at(scattering_opacity, ns), ng)[1]) <=\n            1.e-5 * (gsl::at(gsl::at(scattering_opacity, ns), ng)[0] +\n                     gsl::at(gsl::at(scattering_opacity, ns), ng)[1]));\n\n      CHECK(fabs(gsl::at(gsl::at(emission_in_cells, ns), ng)[3] -\n                 0.3 * gsl::at(gsl::at(emission_in_cells, ns), ng)[1] -\n                 0.7 * gsl::at(gsl::at(emission_in_cells, ns), ng)[2]) <=\n            1.e-5 * (gsl::at(gsl::at(emission_in_cells, ns), ng)[1] +\n                     gsl::at(gsl::at(emission_in_cells, ns), ng)[2]));\n      CHECK(fabs(gsl::at(gsl::at(absorption_opacity, ns), ng)[3] -\n                 0.3 * gsl::at(gsl::at(absorption_opacity, ns), ng)[1] -\n                 0.7 * gsl::at(gsl::at(absorption_opacity, ns), ng)[2]) <=\n            1.e-5 * (gsl::at(gsl::at(absorption_opacity, ns), ng)[1] +\n                     gsl::at(gsl::at(absorption_opacity, ns), ng)[2]));\n      CHECK(fabs(gsl::at(gsl::at(scattering_opacity, ns), ng)[3] -\n                 0.3 * gsl::at(gsl::at(scattering_opacity, ns), ng)[1] -\n                 0.7 * gsl::at(gsl::at(scattering_opacity, ns), ng)[2]) <=\n            1.e-5 * (gsl::at(gsl::at(scattering_opacity, ns), ng)[1] +\n                     gsl::at(gsl::at(scattering_opacity, ns), ng)[2]));\n    }\n  }\n\n  test_explicit_interaction_table();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_Packet.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Particles/MonteCarlo/EvolvePackets.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n\nnamespace {\n\nvoid check_packet() {\n  const double epsilon_approx = 1.e-15;\n\n  const size_t dv_size = 5;\n  Scalar<DataVector> lapse(dv_size, 1.0);\n  Scalar<DataVector> lorentz_factor(dv_size, 1.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> lower_spatial_four_velocity =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n\n  const size_t pos = 3;\n  Particles::MonteCarlo::Packet packet(1, 2.0, pos, 0.0, -1.0, -0.5, -0.2, 0.9,\n                                       -0.3, 0.2, 0.1);\n  // The values below are wildly inconsistent, but we only want to check the\n  // math\n  get(lapse)[pos] = 0.8;\n  get(lorentz_factor)[pos] = 1.3;\n  lower_spatial_four_velocity.get(0)[pos] = 0.3;\n  lower_spatial_four_velocity.get(1)[pos] = 0.4;\n  lower_spatial_four_velocity.get(2)[pos] = 0.5;\n  inv_spatial_metric.get(0, 0) = 1.1;\n  inv_spatial_metric.get(1, 1) = 1.2;\n  inv_spatial_metric.get(2, 2) = 1.3;\n  inv_spatial_metric.get(0, 1) = 0.1;\n  inv_spatial_metric.get(0, 2) = 0.2;\n  inv_spatial_metric.get(1, 2) = 0.3;\n\n  // p^t = \\sqrt{\\gamma^{ij} p_i p_j}/\\alpha\n  const double expected_momentum_upper_t =\n      sqrt(1.1 * 0.3 * 0.3 + 1.2 * 0.2 * 0.2 + 1.3 * 0.1 * 0.1 +\n           2.0 * 0.1 * (-0.3) * 0.2 + 2.0 * 0.2 * (-0.3) * 0.1 +\n           2.0 * 0.3 * 0.2 * 0.1) /\n      0.8;\n  packet.renormalize_momentum(inv_spatial_metric, lapse);\n  CHECK(fabs(packet.momentum_upper_t - expected_momentum_upper_t) <\n        epsilon_approx);\n\n  // E = W \\alpha p^t - \\gamma^{ij} u_i p_j\n  const double expected_energy =\n      0.8 * 1.3 * expected_momentum_upper_t - 1.1 * (-0.3) * 0.3 -\n      1.2 * 0.2 * 0.4 - 1.3 * 0.1 * 0.5 - 0.1 * (-0.3) * 0.4 -\n      0.1 * (0.2) * (0.3) - 0.2 * (-0.3) * 0.5 - 0.2 * 0.1 * 0.3 -\n      0.3 * 0.2 * 0.5 - 0.3 * 0.1 * 0.4;\n  const double computed_energy =\n      Particles::MonteCarlo::compute_fluid_frame_energy(\n          packet, lorentz_factor, lower_spatial_four_velocity, lapse,\n          inv_spatial_metric);\n  CHECK(fabs(expected_energy - computed_energy) < epsilon_approx);\n\n  CHECK(packet.species == 1);\n  CHECK(packet.coordinates.get(0) == -1.0);\n  CHECK(packet.coordinates.get(1) == -0.5);\n  CHECK(packet.coordinates.get(2) == -0.2);\n  CHECK(packet.momentum.get(0) == -0.3);\n  CHECK(packet.momentum.get(1) == 0.2);\n  CHECK(packet.momentum.get(2) == 0.1);\n  CHECK(packet.time == 0.0);\n  CHECK(packet.index_of_closest_grid_point == pos);\n  CHECK(packet.number_of_neutrinos == 2.0);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloPacket\",\n                  \"[Unit][Evolution]\") {\n  check_packet();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_Scattering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <random>\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Particles/MonteCarlo/CouplingTermsForPropagation.hpp\"\n#include \"Evolution/Particles/MonteCarlo/EvolvePackets.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Scattering.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Frame {\nstruct ElementLogical;\nstruct Fluid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace {\n\nvoid test_single_scatter() {\n  MAKE_GENERATOR(generator);\n\n  DataVector zero_dv(8);\n  zero_dv = 0.0;\n  InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      inverse_jacobian = make_with_value<\n          InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(\n          zero_dv, 0.0);\n  Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid> jacobian =\n      make_with_value<Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(\n          zero_dv, 0.0);\n\n  // Set Jacobian to a Lorentz boost in the y-direction with W=2\n  const double v_boost = sqrt(3) / 2.0;\n  const double W_boost = 2.0;\n\n  jacobian.get(0, 0) = W_boost;\n  jacobian.get(0, 2) = -W_boost * v_boost;\n  jacobian.get(1, 1) = 1.0;\n  jacobian.get(2, 0) = -W_boost * v_boost;\n  jacobian.get(2, 2) = W_boost;\n  jacobian.get(3, 3) = 1.0;\n  inverse_jacobian.get(0, 0) = W_boost;\n  inverse_jacobian.get(0, 2) = W_boost * v_boost;\n  inverse_jacobian.get(1, 1) = 1.0;\n  inverse_jacobian.get(2, 0) = W_boost * v_boost;\n  inverse_jacobian.get(2, 2) = W_boost;\n  inverse_jacobian.get(3, 3) = 1.0;\n\n  Particles::MonteCarlo::Packet packet(0, 1.0, 0, 0.0, -1.0, -1.0, -1.0, 1.0,\n                                       1.0, 0.0, 0.0);\n\n  auto boosted_frame_energy = [&W_boost, &v_boost, &packet]() {\n    return W_boost * (packet.momentum_upper_t - packet.momentum[1] * v_boost);\n  };\n\n  const double initial_fluid_frame_energy = boosted_frame_energy();\n  for (int step = 0; step < 5; step++) {\n    scatter_packet(&packet, &generator, initial_fluid_frame_energy, jacobian,\n                   inverse_jacobian);\n    const double current_fluid_frame_energy = boosted_frame_energy();\n    CHECK(fabs(initial_fluid_frame_energy - current_fluid_frame_energy) <\n          1.e-14);\n  }\n}\n\nvoid test_diffusion_params() {\n  const Particles::MonteCarlo::DiffusionMonteCarloParameters diffusion_params;\n  const double epsilon_integral = 1.e-4;\n\n  // Numbers checked through independent numerical integration\n  // Check that we recover Eq 29 of 10.1093/mnras/sty108 at 5 points\n  CHECK(fabs(gsl::at(diffusion_params.ScatteringRofP, 0) - 0.0) <\n        epsilon_integral);\n  CHECK(fabs(gsl::at(diffusion_params.ScatteringRofP, 250) - 0.778631) <\n        epsilon_integral);\n  CHECK(fabs(gsl::at(diffusion_params.ScatteringRofP, 500) - 1.08765) <\n        epsilon_integral);\n  CHECK(fabs(gsl::at(diffusion_params.ScatteringRofP, 750) - 1.43324) <\n        epsilon_integral);\n  CHECK(fabs(gsl::at(diffusion_params.ScatteringRofP, 999) - 2.85186) <\n        epsilon_integral);\n  // Check that we recover Eq. 30 of 10.1093/mnras/sty108 at 4 points\n  CHECK(fabs(gsl::at(diffusion_params.OpacityDependentCorrectionToRofP, 0) -\n             0.828982) < epsilon_integral);\n  CHECK(fabs(gsl::at(diffusion_params.OpacityDependentCorrectionToRofP, 25) -\n             0.948835) < epsilon_integral);\n  CHECK(fabs(gsl::at(diffusion_params.OpacityDependentCorrectionToRofP, 50) -\n             0.986138) < epsilon_integral);\n  CHECK(fabs(gsl::at(diffusion_params.OpacityDependentCorrectionToRofP, 75) -\n             0.996452) < epsilon_integral);\n  CHECK(fabs(gsl::at(diffusion_params.OpacityDependentCorrectionToRofP, 100) -\n             0.999121) < epsilon_integral);\n}\n\nvoid test_diffusion() {\n  const Particles::MonteCarlo::DiffusionMonteCarloParameters diffusion_params;\n\n  MAKE_GENERATOR(generator);\n  const double time_step = 0.5;\n  const double scattering_opacity = 50.0;\n  const double absorption_opacity = 1.e-60;\n\n  const size_t dv_size = 1;\n  DataVector zero_dv(dv_size);\n  zero_dv = 0.0;\n  Scalar<DataVector> lapse(dv_size, 1.0);\n  Scalar<DataVector> lorentz_factor(dv_size, 1.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> lower_spatial_four_velocity =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::I<DataVector, 3, Frame::Inertial> shift =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  inv_spatial_metric.get(0, 0) = 1.0;\n  inv_spatial_metric.get(1, 1) = 1.0;\n  inv_spatial_metric.get(2, 2) = 1.0;\n  spatial_metric.get(0, 0) = 1.0;\n  spatial_metric.get(1, 1) = 1.0;\n  spatial_metric.get(2, 2) = 1.0;\n  tnsr::i<DataVector, 3, Frame::Inertial> d_lapse =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::iJ<DataVector, 3, Frame::Inertial> d_shift =\n      make_with_value<tnsr::iJ<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::iJJ<DataVector, 3, Frame::Inertial> d_inv_spatial_metric =\n      make_with_value<tnsr::iJJ<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  std::optional<tnsr::I<DataVector, 3, Frame::Inertial>> mesh_velocity =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  mesh_velocity.value().get(0) = 0.1;\n  mesh_velocity.value().get(1) = 0.2;\n  mesh_velocity.value().get(2) = 0.3;\n  // Jacobian set to identity for now\n  InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      inverse_jacobian_inertial_to_fluid = make_with_value<\n          InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse,\n                                                                         0.0);\n  inverse_jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n  Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      jacobian_inertial_to_fluid = make_with_value<\n          Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse, 0.0);\n  jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n\n  // Logical to inertial inverse jacobian, also identity for now\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian_logical_to_inertial =\n          make_with_value<InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                                          Frame::Inertial>>(lapse, 0.0);\n  inverse_jacobian_logical_to_inertial.get(0, 0) = 1.0;\n  inverse_jacobian_logical_to_inertial.get(1, 1) = 1.0;\n  inverse_jacobian_logical_to_inertial.get(2, 2) = 1.0;\n\n  Scalar<DataVector> coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(zero_dv, 0.0);\n  Scalar<DataVector> coupling_rho_ye =\n      make_with_value<Scalar<DataVector>>(zero_dv, 0.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(zero_dv, 0.0);\n\n  DataVector prefactor_diffusion_time_vector = zero_dv;\n  DataVector prefactor_diffusion_four_velocity = zero_dv;\n  DataVector prefactor_diffusion_time_step = zero_dv;\n  Particles::MonteCarlo::DiffusionPrecomputeForElement(\n      &prefactor_diffusion_time_vector, &prefactor_diffusion_four_velocity,\n      &prefactor_diffusion_time_step, lorentz_factor,\n      lower_spatial_four_velocity, lapse, shift, spatial_metric);\n\n  for (size_t i = 0; i < prefactor_diffusion_time_vector.size(); i++) {\n    CHECK(std::isnan(prefactor_diffusion_time_vector[i]));\n    CHECK(std::isnan(prefactor_diffusion_four_velocity[i]));\n  }\n  CHECK(prefactor_diffusion_time_step == -1.0);\n\n  Parallel::printf(\"Diffusion limit:\\n\");\n  for (size_t i = 0; i < 100000; i++) {\n    Particles::MonteCarlo::Packet packet(0, 1.0, 0, 0.0, 0.0, 0.0, 0.0, 1.0,\n                                         1.0, 0.0, 0.0);\n    double neutrino_energy = 1.0;\n    const size_t extended_idx = 0;\n    Particles::MonteCarlo::diffuse_packet(\n        &packet, &generator, &neutrino_energy, &coupling_tilde_tau,\n        &coupling_tilde_s, &coupling_rho_ye, extended_idx, time_step,\n        diffusion_params, absorption_opacity, scattering_opacity,\n        lorentz_factor, lower_spatial_four_velocity, lapse, shift, d_lapse,\n        d_shift, d_inv_spatial_metric, spatial_metric, inv_spatial_metric,\n        mesh_velocity, inverse_jacobian_logical_to_inertial,\n        jacobian_inertial_to_fluid, inverse_jacobian_inertial_to_fluid,\n        prefactor_diffusion_time_step, prefactor_diffusion_four_velocity,\n        prefactor_diffusion_time_vector);\n\n    if (mesh_velocity.has_value()) {\n      for (size_t d = 0; d < 3; d++) {\n        packet.coordinates[d] += mesh_velocity.value().get(d)[0] * time_step;\n      }\n    }\n\n    const double rad = sqrt(packet.coordinates[0] * packet.coordinates[0] +\n                            packet.coordinates[1] * packet.coordinates[1] +\n                            packet.coordinates[2] * packet.coordinates[2]);\n    const double cth = packet.coordinates[2] / rad;\n    const double phi = atan2(packet.coordinates[1], packet.coordinates[0]);\n    const double pitch = (packet.coordinates[0] * packet.momentum[0] +\n                          packet.coordinates[1] * packet.momentum[1] +\n                          packet.coordinates[2] * packet.momentum[2]) /\n                         neutrino_energy / rad;\n    Parallel::printf(\"%.5f %.5f %.5f %.5f 0\\n\", rad, cth, phi, pitch);\n  }\n\n  Parallel::printf(\"Coupling: %.5e %.5e %.5e %.5e %.5e\\n\",\n                   get(coupling_tilde_tau)[0], coupling_tilde_s.get(0)[0],\n                   coupling_tilde_s.get(1)[0], coupling_tilde_s.get(2)[0],\n                   get(coupling_rho_ye)[0]);\n  // Reset coupling terms\n  get(coupling_tilde_tau)[0] = 0.0;\n  for (size_t d = 0; d < 3; d++) {\n    coupling_tilde_s.get(d)[0] = 0.0;\n  }\n  get(coupling_rho_ye)[0] = 0.0;\n\n  Parallel::printf(\"Full scattering:\\n\");\n  std::uniform_real_distribution<double> rng_uniform_zero_to_one(0.0, 1.0);\n  double neutrino_energy = 1.0;\n  double dt_scattering = 0.0;\n  for (size_t i = 0; i < 100000; i++) {\n    Particles::MonteCarlo::Packet packet(0, 1.0, 0, 0.0, 0.0, 0.0, 0.0, 1.0,\n                                         1.0, 0.0, 0.0);\n    const size_t extended_idx = 0;\n    while (packet.time < time_step) {\n      neutrino_energy = Particles::MonteCarlo::compute_fluid_frame_energy(\n          packet, lorentz_factor, lower_spatial_four_velocity, lapse,\n          inv_spatial_metric);\n      Particles::MonteCarlo::scatter_packet(\n          &packet, &generator, neutrino_energy, jacobian_inertial_to_fluid,\n          inverse_jacobian_inertial_to_fluid);\n      dt_scattering = std::clamp(\n          -log(rng_uniform_zero_to_one(generator) + 1.e-70) /\n              (scattering_opacity)*packet.momentum_upper_t / neutrino_energy,\n          0.0, time_step - packet.time);\n\n      Particles::MonteCarlo::evolve_single_packet_on_geodesic(\n          &packet, dt_scattering, lapse, shift, d_lapse, d_shift,\n          d_inv_spatial_metric, inv_spatial_metric, mesh_velocity,\n          inverse_jacobian_logical_to_inertial);\n      const std::array<double, 3> lower_spatial_four_velocity_packet = {\n          lower_spatial_four_velocity.get(0)[0],\n          lower_spatial_four_velocity.get(1)[0],\n          lower_spatial_four_velocity.get(2)[0]};\n      Particles::MonteCarlo::AddCouplingTermsForPropagation(\n          &coupling_tilde_tau, &coupling_tilde_s, &coupling_rho_ye, packet,\n          extended_idx, dt_scattering, absorption_opacity, 0.0, neutrino_energy,\n          get(lapse)[0], get(lorentz_factor)[0],\n          lower_spatial_four_velocity_packet);\n    }\n\n    if (mesh_velocity.has_value()) {\n      for (size_t d = 0; d < 3; d++) {\n        packet.coordinates[d] += mesh_velocity.value().get(d)[0] * time_step;\n      }\n    }\n\n    const double rad = sqrt(packet.coordinates[0] * packet.coordinates[0] +\n                            packet.coordinates[1] * packet.coordinates[1] +\n                            packet.coordinates[2] * packet.coordinates[2]);\n    const double cth = packet.coordinates[2] / rad;\n    const double phi = atan2(packet.coordinates[1], packet.coordinates[0]);\n    const double pitch = (packet.coordinates[0] * packet.momentum[0] +\n                          packet.coordinates[1] * packet.momentum[1] +\n                          packet.coordinates[2] * packet.momentum[2]) /\n                         neutrino_energy / rad;\n    Parallel::printf(\"%.5f %.5f %.5f %.5f 1\\n\", rad, cth, phi, pitch);\n  }\n  Parallel::printf(\"Coupling: %.5e %.5e %.5e %.5e %.5e\\n\",\n                   get(coupling_tilde_tau)[0], coupling_tilde_s.get(0)[0],\n                   coupling_tilde_s.get(1)[0], coupling_tilde_s.get(2)[0],\n                   get(coupling_rho_ye)[0]);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloScattering\",\n                  \"[Unit][Evolution]\") {\n  test_single_scatter();\n  test_diffusion_params();\n  // Not turned on by defaults... too long for automated tests,\n  // but useful framework to test diffusion regime.\n  // NOLINTNEXTLINE(readability-simplify-boolean-expr)\n  if ((false)) {\n    test_diffusion();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_SwapGrTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Particles/MonteCarlo/SwapGrTags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarlo.SwapGrTags\",\n                  \"[Unit][Evolution]\") {\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  const Particles::MonteCarlo::System::spacetime_variables_tag::type\n      active_gr_vars(dg_mesh.number_of_grid_points());\n  const evolution::dg::subcell::Tags::Inactive<\n      typename Particles::MonteCarlo::System::spacetime_variables_tag>::type\n      inactive_gr_vars(subcell_mesh.number_of_grid_points());\n\n  auto box = db::create<db::AddSimpleTags<\n      Particles::MonteCarlo::System::spacetime_variables_tag,\n      evolution::dg::subcell::Tags::Inactive<\n          Particles::MonteCarlo::System::spacetime_variables_tag>,\n      domain::Tags::Mesh<3>, evolution::dg::subcell::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::ActiveGrid>>(\n      active_gr_vars, inactive_gr_vars, dg_mesh, subcell_mesh,\n      evolution::dg::subcell::ActiveGrid::Dg);\n\n  for (const auto active_grid : {evolution::dg::subcell::ActiveGrid::Dg,\n                                 evolution::dg::subcell::ActiveGrid::Subcell,\n                                 evolution::dg::subcell::ActiveGrid::Dg}) {\n    db::mutate<evolution::dg::subcell::Tags::ActiveGrid>(\n        [&active_grid](const auto active_grid_ptr) {\n          *active_grid_ptr = active_grid;\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<Particles::MonteCarlo::SwapGrTags>(make_not_null(&box));\n\n    if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n      CHECK(db::get<Particles::MonteCarlo::System::spacetime_variables_tag>(box)\n                .number_of_grid_points() == dg_mesh.number_of_grid_points());\n      CHECK(db::get<evolution::dg::subcell::Tags::Inactive<\n                Particles::MonteCarlo::System::spacetime_variables_tag>>(box)\n                .number_of_grid_points() ==\n            subcell_mesh.number_of_grid_points());\n    } else {\n      CHECK(db::get<Particles::MonteCarlo::System::spacetime_variables_tag>(box)\n                .number_of_grid_points() ==\n            subcell_mesh.number_of_grid_points());\n      CHECK(db::get<evolution::dg::subcell::Tags::Inactive<\n                Particles::MonteCarlo::System::spacetime_variables_tag>>(box)\n                .number_of_grid_points() == dg_mesh.number_of_grid_points());\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_TakeTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/Particles/MonteCarlo/EvolvePackets.hpp\"\n#include \"Evolution/Particles/MonteCarlo/NeutrinoInteractionTable.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/TemplatedLocalFunctions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n\nnamespace{\n\nvoid test_flat_space_time_step() {\n  const Mesh<3> mesh(2, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered);\n\n  MAKE_GENERATOR(generator);\n\n  const size_t NeutrinoSpecies = 2;\n  const size_t NeutrinoEnergies = 2;\n\n  const size_t size_1d = 2;\n  const size_t num_ghost_zones = 1;\n\n  const size_t dv_size = cube(size_1d);\n  const DataVector zero_dv(dv_size, 0.0);\n  const size_t dv_size_with_ghost = cube(size_1d + 2 * num_ghost_zones);\n  const DataVector zero_dv_with_ghost(dv_size_with_ghost, 0.0);\n  const size_t dv_size_in_ghost = square(size_1d) * num_ghost_zones;\n  const DataVector zero_dv_in_ghost(dv_size_in_ghost, 0.0);\n  const DataVector one_dv_in_ghost(dv_size_in_ghost, 1.0);\n\n  // Minkowski metric\n  const Scalar<DataVector> lapse{DataVector(dv_size, 1.0)};\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  inv_spatial_metric.get(0, 0) = 1.0;\n  inv_spatial_metric.get(1, 1) = 1.0;\n  inv_spatial_metric.get(2, 2) = 1.0;\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  spatial_metric.get(0, 0) = 1.0;\n  spatial_metric.get(1, 1) = 1.0;\n  spatial_metric.get(2, 2) = 1.0;\n  const Scalar<DataVector> sqrt_determinant_spatial_metric(dv_size, 1.0);\n  const tnsr::I<DataVector, 3, Frame::Inertial> shift =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  const tnsr::i<DataVector, 3, Frame::Inertial> d_lapse =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  const tnsr::iJ<DataVector, 3, Frame::Inertial> d_shift =\n      make_with_value<tnsr::iJ<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  const tnsr::iJJ<DataVector, 3, Frame::Inertial> d_inv_spatial_metric =\n      make_with_value<tnsr::iJJ<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n\n  // Mesh velocity set to std::null for now\n  const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>> mesh_velocity =\n      std::nullopt;\n\n  // Jacobian set to identity for now\n  InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      inverse_jacobian_inertial_to_fluid = make_with_value<\n          InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse,\n                                                                         0.0);\n  inverse_jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n  Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      jacobian_inertial_to_fluid = make_with_value<\n          Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse, 0.0);\n  jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n  // Logical to inertial inverse jacobian, also identity for now\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian_logical_to_inertial =\n          make_with_value<InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                                          Frame::Inertial>>(lapse, 0.0);\n  inverse_jacobian_logical_to_inertial.get(0, 0) = 1.0;\n  inverse_jacobian_logical_to_inertial.get(1, 1) = 1.0;\n  inverse_jacobian_logical_to_inertial.get(2, 2) = 1.0;\n  const Scalar<DataVector> det_jacobian_logical_to_inertial(dv_size, 1.0);\n\n  // Coordinates\n  tnsr::I<DataVector, 3, Frame::ElementLogical> mesh_coordinates =\n      make_with_value<tnsr::I<DataVector, 3, Frame::ElementLogical>>(lapse,\n                                                                     0.0);\n  mesh_coordinates.get(0) =\n      DataVector{-0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5};\n  mesh_coordinates.get(1) =\n      DataVector{-0.5, -0.5, 0.5, 0.5, -0.5, -0.5, 0.5, 0.5};\n  mesh_coordinates.get(2) =\n      DataVector{-0.5, -0.5, -0.5, -0.5, 0.5, 0.5, 0.5, 0.5};\n\n  const size_t species = 1;\n  const double number_of_neutrinos = 1.0;\n  const size_t index_of_closest_grid_point = 0;\n  const double t0 = 0.0;\n  const double x0 = -0.5;\n  const double y0 = -0.5;\n  const double z0 = -0.5;\n  const double p_upper_t0 = 1.0;\n  const double p_x0 = 1.0;\n  const double p_y0 = 0.0;\n  const double p_z0 = 0.0;\n  Particles::MonteCarlo::Packet packet(species, number_of_neutrinos,\n                                       index_of_closest_grid_point, t0, x0, y0,\n                                       z0, p_upper_t0, p_x0, p_y0, p_z0);\n  packet.renormalize_momentum(inv_spatial_metric, lapse);\n  CHECK(packet.momentum_upper_t == 1.0);\n\n  Scalar<DataVector> coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(zero_dv_with_ghost, 0.0);\n  Scalar<DataVector> coupling_tilde_rho_ye =\n      make_with_value<Scalar<DataVector>>(zero_dv_with_ghost, 0.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(\n          zero_dv_with_ghost, 0.0);\n\n  std::vector<Particles::MonteCarlo::Packet> packets{packet};\n  Particles::MonteCarlo::TemplatedLocalFunctions<NeutrinoEnergies,\n                                                 NeutrinoSpecies>\n      MonteCarloStruct;\n\n  const double start_time = 0.0;\n  const double final_time = 1.6;\n  const double time_step = 0.4;\n  std::array<DataVector, NeutrinoSpecies> single_packet_energy = {zero_dv,\n                                                                  zero_dv};\n  for (size_t s = 0; s < NeutrinoSpecies; s++) {\n    gsl::at(single_packet_energy,s) = 1.0;\n  }\n\n  // Neutrino-matter interactions\n  const std::vector<double> table_log_density{-10.0, 0.0};\n  const std::vector<double> table_log_temperature{-2.0, 0.0};\n  const std::vector<double> table_electron_fraction{0.05, 0.06};\n  const size_t n_points_table = 8;\n  const size_t number_of_vars = 3 * NeutrinoEnergies * NeutrinoSpecies;\n  const std::array<double, NeutrinoEnergies> table_neutrino_energies = {1.0,\n                                                                        3.0};\n  std::vector<double> table_data{};\n  table_data.resize(number_of_vars * n_points_table);\n  // Fill in fake data for interaction rates. Fastest moving index is density,\n  // then temperature, then electron fraction.\n  // Here, we set interaction rates that linearly grow in log(density) so\n  // we fill all 'high density' points of the table.\n  for (size_t p = 1; p < n_points_table; p += 2) {\n    // Emissivity\n    // nu_e\n    table_data[p * number_of_vars] = 1.0;\n    table_data[p * number_of_vars + 1] = 9.0;\n    // nu_a\n    table_data[p * number_of_vars + NeutrinoEnergies] = 0.5;\n    table_data[p * number_of_vars + NeutrinoEnergies + 1] = 4.5;\n    // Absorption\n    size_t idx_shift = NeutrinoEnergies * NeutrinoSpecies;\n    // nu_e\n    table_data[p * number_of_vars + idx_shift] = 1.0;\n    table_data[p * number_of_vars + 1 + idx_shift] = 9.0;\n    // nu_a\n    table_data[p * number_of_vars + NeutrinoEnergies + idx_shift] = 0.5;\n    table_data[p * number_of_vars + NeutrinoEnergies + 1 + idx_shift] = 4.5;\n    // Scattering\n    idx_shift = 2 * NeutrinoEnergies * NeutrinoSpecies;\n    // nu_e\n    table_data[p * number_of_vars + idx_shift] = 1.0;\n    table_data[p * number_of_vars + 1 + idx_shift] = 9.0;\n    // nu_a\n    table_data[p * number_of_vars + NeutrinoEnergies + idx_shift] = 0.5;\n    table_data[p * number_of_vars + NeutrinoEnergies + 1 + idx_shift] = 4.5;\n  }\n  const Particles::MonteCarlo::NeutrinoInteractionTable<2, 2> interaction_table(\n      table_data, table_neutrino_energies, table_log_density,\n      table_log_temperature, table_electron_fraction);\n\n  std::string h5_file_name_compose{\n    unit_test_src_path() +\n    \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n  EquationsOfState::Tabulated3D<true> equation_of_state(h5_file_name_compose,\n                                                        \"/dd2\");\n\n  // Currently we choose values leading to no interaction, for\n  // predictability...\n  Scalar<DataVector> baryon_density(dv_size, 1.e-10);\n  Scalar<DataVector> temperature(dv_size, 0.01);\n  Scalar<DataVector> electron_fraction(dv_size, 0.05);\n  Scalar<DataVector> lorentz_factor(dv_size, 1.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> lower_spatial_four_velocity =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  Scalar<DataVector> cell_light_crossing_time(dv_size, 0.6);\n\n  // Ghost zone data (currently zero for all fluid variables on lower end of\n  // element and nullopt on upper end of element)\n  DirectionalIdMap<3, std::optional<DataVector>> baryon_density_ghost_zones{};\n  DirectionalIdMap<3, std::optional<DataVector>> temperature_ghost_zones{};\n  DirectionalIdMap<3, std::optional<DataVector>>\n      electron_fraction_ghost_zones{};\n  DirectionalIdMap<3, std::optional<DataVector>>\n      cell_light_crossing_time_ghost_zones{};\n  for (size_t d = 0; d < 3; d++) {\n    Direction<3> up(d, Side::Upper);\n    Direction<3> down(d, Side::Lower);\n    const ElementId<3> dummy_neighbor_index(0, 0);\n    baryon_density_ghost_zones.insert(\n        std::pair{DirectionalId<3>{up, dummy_neighbor_index}, std::nullopt});\n    baryon_density_ghost_zones.insert(std::pair{\n        DirectionalId<3>{down, dummy_neighbor_index}, zero_dv_in_ghost});\n    temperature_ghost_zones.insert(\n        std::pair{DirectionalId<3>{up, dummy_neighbor_index}, std::nullopt});\n    temperature_ghost_zones.insert(std::pair{\n        DirectionalId<3>{down, dummy_neighbor_index}, zero_dv_in_ghost});\n    electron_fraction_ghost_zones.insert(\n        std::pair{DirectionalId<3>{up, dummy_neighbor_index}, std::nullopt});\n    electron_fraction_ghost_zones.insert(std::pair{\n        DirectionalId<3>{down, dummy_neighbor_index}, zero_dv_in_ghost});\n    cell_light_crossing_time_ghost_zones.insert(\n        std::pair{DirectionalId<3>{up, dummy_neighbor_index}, std::nullopt});\n    cell_light_crossing_time_ghost_zones.insert(std::pair{\n        DirectionalId<3>{down, dummy_neighbor_index}, one_dv_in_ghost});\n  }\n\n  double current_time = start_time;\n  while (current_time < final_time) {\n    MonteCarloStruct.take_time_step_on_element(\n        &packets, &coupling_tilde_tau, &coupling_tilde_rho_ye,\n        &coupling_tilde_s, &generator, &single_packet_energy, current_time,\n        current_time + time_step, equation_of_state, interaction_table,\n        electron_fraction, baryon_density, temperature, lorentz_factor,\n        lower_spatial_four_velocity, lapse, shift, d_lapse, d_shift,\n        d_inv_spatial_metric, spatial_metric, inv_spatial_metric,\n        sqrt_determinant_spatial_metric, cell_light_crossing_time, mesh,\n        mesh_coordinates, num_ghost_zones, mesh_velocity,\n        inverse_jacobian_logical_to_inertial, det_jacobian_logical_to_inertial,\n        jacobian_inertial_to_fluid, inverse_jacobian_inertial_to_fluid,\n        electron_fraction_ghost_zones, baryon_density_ghost_zones,\n        temperature_ghost_zones, cell_light_crossing_time_ghost_zones);\n    current_time += time_step;\n    const double expected_x0 = -0.5 + current_time;\n    // Note that the index dv_size is used to represent all GZs\n    const size_t expected_idx =\n        (expected_x0 < 0.0) ? 0 : (expected_x0 < 1.0 ? 1 : dv_size);\n\n    // In the current setup, we just propagate a single packet to the\n    // final time.\n    CHECK(packets[0].coordinates.get(0) == expected_x0);\n    CHECK(packets[0].index_of_closest_grid_point == expected_idx);\n  }\n  size_t n_packets = packets.size();\n  CHECK(n_packets==1);\n  CHECK(packets[0].time==final_time);\n}\n\n} // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloTakeTimeStep\",\n                  \"[Unit][Evolution]\") {\n  test_flat_space_time_step();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Particles/MonteCarlo/Test_TimeStepAction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Interpolators.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Actions/TimeStepActions.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunication.hpp\"\n#include \"Evolution/Particles/MonteCarlo/GhostZoneCommunicationTags.hpp\"\n#include \"Evolution/Particles/MonteCarlo/MortarData.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Packet.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1>>;\n  using flux_variables = tmpl::list<Var1>;\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n\n  using initial_tags = tmpl::list<\n      Particles::MonteCarlo::Tags::RandomNumberGenerator, ::Tags::TimeStepId,\n      ::Tags::Next<::Tags::TimeStepId>, domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::ActiveGrid, domain::Tags::Element<Dim>,\n      Particles::MonteCarlo::Tags::McGhostZoneDataTag<Dim>,\n      Particles::MonteCarlo::Tags::PacketsOnElement,\n      ::Tags::Variables<tmpl::list<Var1>>,\n      Particles::MonteCarlo::Tags::MortarDataTag<Dim>,\n      hydro::Tags::LorentzFactor<DataVector>,\n      hydro::Tags::RestMassDensity<DataVector>,\n      hydro::Tags::ElectronFraction<DataVector>,\n      hydro::Tags::Temperature<DataVector>,\n      Particles::MonteCarlo::Tags::CellLightCrossingTime<DataVector>,\n      Particles::MonteCarlo::Tags::CouplingTildeTau<DataVector>,\n      Particles::MonteCarlo::Tags::CouplingTildeRhoYe<DataVector>,\n      Particles::MonteCarlo::Tags::CouplingTildeS<DataVector, Dim>,\n      domain::Tags::NeighborMesh<Dim>,\n      Particles::MonteCarlo::Tags::DesiredPacketEnergyAtEmission<3>,\n      hydro::Tags::SpatialVelocity<DataVector, Dim, Frame::Inertial>,\n      gr::Tags::Lapse<DataVector>,\n      gr::Tags::Shift<DataVector, Dim, Frame::Inertial>,\n      gh::Tags::Phi<DataVector, Dim, Frame::Inertial>,\n      gr::Tags::SpatialMetric<DataVector, Dim, Frame::Inertial>,\n      gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame::Inertial>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      domain::Tags::MeshVelocity<Dim>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<Dim>,\n      evolution::dg::subcell::fd::Tags::DetInverseJacobianLogicalToInertial,\n      domain::Tags::InverseJacobian<Dim + 1, Frame::Inertial, Frame::Fluid>,\n      domain::Tags::DetInvJacobian<Frame::Inertial, Frame::Fluid>,\n      domain::Tags::Jacobian<Dim + 1, Frame::Inertial, Frame::Fluid>,\n      evolution::dg::subcell::Tags::InterpolatorsFromFdToNeighborFd<Dim>>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<initial_tags>,\n                 Particles::MonteCarlo::Actions::TakeTimeStep<4, 3>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using system = System<Dim>;\n\n  using const_global_cache_tags =\n      tmpl::list<hydro::Tags::GrmhdEquationOfState,\n                 Particles::MonteCarlo::Tags::InteractionRatesTable<4, 3>>;\n};\n\nvoid test_advance_packets() {\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> rng_uniform_zero_to_one(0.0, 1.0);\n  const size_t Dim = 3;\n\n  register_classes_with_charm<EquationsOfState::Tabulated3D<true>>();\n  register_classes_with_charm<\n      Particles::MonteCarlo::NeutrinoInteractionTable<4, 3>>();\n\n  // Fake EoS and NuLib tables\n  const std::string h5_file_name_compose{\n      unit_test_src_path() +\n      \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n  std::unique_ptr<EquationsOfState::EquationOfState<true, 3>>\n      equation_of_state_ptr =\n          std::make_unique<EquationsOfState::Tabulated3D<true>>(\n              h5_file_name_compose, \"/dd2\");\n  const std::string h5_file_name_nulib{\n      unit_test_src_path() +\n      \"Evolution/Particles/MonteCarlo/NuLib_TestTable.h5\"};\n  std::unique_ptr<Particles::MonteCarlo::NeutrinoInteractionTable<4, 3>>\n      interaction_table_ptr = std::make_unique<\n          Particles::MonteCarlo::NeutrinoInteractionTable<4, 3>>(\n          h5_file_name_nulib);\n\n  using Interps = DirectionalIdMap<Dim, std::optional<intrp::Irregular<Dim>>>;\n  using metavars = Metavariables<Dim>;\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n\n  const TimeStepId time_step_id{true, 1, Time{Slab{1.0, 2.0}, {0, 10}}};\n  const TimeStepId next_time_step_id{true, 1, Time{Slab{1.0, 2.0}, {1, 10}}};\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const evolution::dg::subcell::ActiveGrid active_grid =\n      evolution::dg::subcell::ActiveGrid::Subcell;\n  const size_t n_pts = subcell_mesh.number_of_grid_points();\n\n  const DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  const ElementId<Dim> self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {0, 0}}}};\n  const Element<Dim> element{self_id, neighbors};\n\n  using NeighborDataMap =\n      DirectionalIdMap<Dim, Particles::MonteCarlo::McGhostZoneData<Dim>>;\n  NeighborDataMap neighbor_data{};\n\n  // Coordinates\n  const auto mesh_coordinates = logical_coordinates(subcell_mesh);\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coordinates =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          mesh_coordinates.get(0), 0);\n  inertial_coordinates.get(0) = mesh_coordinates.get(0) + 6.0;\n  inertial_coordinates.get(1) = mesh_coordinates.get(1);\n  inertial_coordinates.get(2) = mesh_coordinates.get(2);\n\n  using evolved_vars_tags = tmpl::list<Var1>;\n  Variables<evolved_vars_tags> evolved_vars{n_pts};\n  // Set Var1 to the logical coords, just need some data\n  get(get<Var1>(evolved_vars)) = get<0>(logical_coordinates(subcell_mesh));\n\n  const DataVector zero_dv(n_pts, 0.0);\n\n  // Parameters for KerrSchild solution\n  const double mass = 1.01;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  const double t = 1.3;\n  // Evaluate solution\n  const gh::Solutions::WrappedGr<gr::Solutions::KerrSchild> solution(mass, spin,\n                                                                     center);\n\n  // Compute metric quantities\n  const auto vars =\n      solution.variables(inertial_coordinates, t,\n                         typename gh::Solutions::WrappedGr<\n                             gr::Solutions::KerrSchild>::tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& deriv_lapse = get<typename gr::Solutions::KerrSchild::DerivLapse<\n      DataVector, Frame::Inertial>>(vars);\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, 3, Frame::Inertial>>(vars);\n  const auto& deriv_shift = get<typename gr::Solutions::KerrSchild::DerivShift<\n      DataVector, Frame::Inertial>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>>(vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>>(vars);\n  const Scalar<DataVector> cell_light_crossing_time =\n      make_with_value<Scalar<DataVector>>(zero_dv, 1.0);\n  const auto& deriv_spatial_metric =\n      get<typename gr::Solutions::KerrSchild::DerivSpatialMetric<\n          DataVector, Frame::Inertial>>(vars);\n  const auto& phi = get<gh::Tags::Phi<DataVector, 3, Frame::Inertial>>(vars);\n\n  tnsr::iJJ<DataVector, 3, Frame::Inertial> deriv_inverse_spatial_metric =\n      make_with_value<tnsr::iJJ<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = i; j < 3; j++) {\n      for (size_t k = 0; k < 3; k++) {\n        for (size_t l = 0; l < 3; l++) {\n          for (size_t m = 0; m < 3; m++) {\n            deriv_inverse_spatial_metric.get(k, i, j) -=\n                inverse_spatial_metric.get(i, l) *\n                inverse_spatial_metric.get(j, m) *\n                deriv_spatial_metric.get(k, l, m);\n          }\n        }\n      }\n    }\n  }\n\n  // Not needed?\n  Scalar<DataVector> sqrt_determinant_spatial_metric(n_pts, 1.0);\n\n  // Fluid variables (not used)\n  Scalar<DataVector> rest_mass_density(zero_dv);\n  Scalar<DataVector> lorentz_factor =\n    make_with_value<Scalar<DataVector>>(lapse, 1.0);;\n  Scalar<DataVector> electron_fraction(zero_dv);\n  Scalar<DataVector> temperature(zero_dv);\n  tnsr::I<DataVector, 3, Frame::Inertial> spatial_velocity =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  tnsr::i<DataVector, 3, Frame::Inertial> lower_spatial_four_velocity =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(lapse, 0.0);\n  std::array<DataVector, 3> single_packet_energy = {zero_dv, zero_dv, zero_dv};\n  gsl::at(single_packet_energy, 0) = 1.0;\n  gsl::at(single_packet_energy, 1) = 1.0;\n  gsl::at(single_packet_energy, 2) = 1.0;\n\n  // Coupling data (not used)\n  const auto& subcell_extents = subcell_mesh.extents();\n  const size_t num_ghost_zones = 1;\n  size_t mesh_size_with_ghost = 1;\n  for (size_t d = 0; d < Dim; d++) {\n    mesh_size_with_ghost *= subcell_extents[d] + 2 * num_ghost_zones;\n  }\n  const DataVector zero_dv_with_ghost(mesh_size_with_ghost, 0.0);\n  const DataVector one_dv_with_ghost(mesh_size_with_ghost, 1.0);\n  Scalar<DataVector> coupling_tilde_tau =\n      make_with_value<Scalar<DataVector>>(zero_dv_with_ghost, 0.0);\n  Scalar<DataVector> coupling_tilde_rho_ye =\n      make_with_value<Scalar<DataVector>>(zero_dv_with_ghost, 0.0);\n  tnsr::i<DataVector, Dim> coupling_tilde_s =\n      make_with_value<tnsr::i<DataVector, Dim>>(zero_dv_with_ghost, 0.0);\n\n  // Grid coordinates on subcell mesh\n  const size_t mesh_size = subcell_extents[0];\n  CHECK(subcell_mesh.extents()[1] == mesh_size);\n  CHECK(subcell_mesh.extents()[2] == mesh_size);\n\n  const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>> mesh_velocity =\n      std::nullopt;\n\n  // Jacobian set to identity for now\n  Scalar<DataVector> det_inverse_jacobian_inertial_to_fluid(n_pts, 1.0);\n  InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      inverse_jacobian_inertial_to_fluid = make_with_value<\n          InverseJacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse,\n                                                                         0.0);\n  inverse_jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  inverse_jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n  Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>\n      jacobian_inertial_to_fluid = make_with_value<\n          Jacobian<DataVector, 4, Frame::Inertial, Frame::Fluid>>(lapse, 0.0);\n  jacobian_inertial_to_fluid.get(0, 0) = 1.0;\n  jacobian_inertial_to_fluid.get(1, 1) = 1.0;\n  jacobian_inertial_to_fluid.get(2, 2) = 1.0;\n  jacobian_inertial_to_fluid.get(3, 3) = 1.0;\n  // Logical to inertial inverse jacobian, also identity for now\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian_logical_to_inertial =\n          make_with_value<InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                                          Frame::Inertial>>(lapse, 0.0);\n  inverse_jacobian_logical_to_inertial.get(0, 0) = 1.0;\n  inverse_jacobian_logical_to_inertial.get(1, 1) = 1.0;\n  inverse_jacobian_logical_to_inertial.get(2, 2) = 1.0;\n  Scalar<DataVector> det_inverse_jacobian_logical_to_inertial(n_pts, 1.0);\n\n  // Initialize MortarData\n  Particles::MonteCarlo::MortarData<Dim> mortar_data{};\n\n  std::vector<Particles::MonteCarlo::Packet> packets_on_element{};\n\n  // Initialize packet on the x-axis\n  const size_t species = 1;\n  const double number_of_neutrinos = 2.0;\n  // Index will be recomputed below\n  const size_t index_of_closest_grid_point = 0;\n  const double t0 = time_step_id.step_time().value();\n  // Logical coordinates drawn from a unit cube far enough\n  // away from the boundary that the packet won't escape\n  const double x0 = rng_uniform_zero_to_one(generator) - 0.5;\n  const double y0 = rng_uniform_zero_to_one(generator) - 0.5;\n  const double z0 = rng_uniform_zero_to_one(generator) - 0.5;\n  // p^t will be self-consistently reset below\n  const double p_upper_t0 = 1.0;\n  // Random momentum chosen to be in the lowest energy bin\n  const double p_x0 = rng_uniform_zero_to_one(generator) - 0.5;\n  const double p_y0 = rng_uniform_zero_to_one(generator) - 0.5;\n  const double p_z0 = rng_uniform_zero_to_one(generator) - 0.5;\n  Particles::MonteCarlo::Packet packet(species, number_of_neutrinos,\n                                       index_of_closest_grid_point, t0, x0, y0,\n                                       z0, p_upper_t0, p_x0, p_y0, p_z0);\n  // Self-consistency: update index of closest point and p^t\n  std::array<size_t, 3> closest_point_index_3d{0, 0, 0};\n  for (size_t d = 0; d < 3; d++) {\n    gsl::at(closest_point_index_3d, d) =\n        std::floor((packet.coordinates[d] - mesh_coordinates.get(d)[0]) /\n                       (2.0 / static_cast<double>(mesh_size)) +\n                   0.5);\n  }\n  const Index<3>& extents = subcell_mesh.extents();\n  packet.index_of_closest_grid_point =\n      closest_point_index_3d[0] +\n      extents[0] *\n          (closest_point_index_3d[1] + extents[1] * closest_point_index_3d[2]);\n  // Reset p^t\n  packet.renormalize_momentum(inverse_spatial_metric, lapse);\n  packets_on_element.push_back(packet);\n  const Particles::MonteCarlo::Packet packet_t0(packet);\n\n  MockRuntimeSystem runner{\n      {std::move(equation_of_state_ptr), std::move(interaction_table_ptr)}};\n\n  Interps fd_to_neighbor_fd_interpolants{};\n  ActionTesting::emplace_array_component_and_initialize<comp>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, self_id,\n      {generator,\n       time_step_id,\n       next_time_step_id,\n       dg_mesh,\n       subcell_mesh,\n       active_grid,\n       element,\n       neighbor_data,\n       packets_on_element,\n       evolved_vars,\n       mortar_data,\n       lorentz_factor,\n       rest_mass_density,\n       electron_fraction,\n       temperature,\n       cell_light_crossing_time,\n       coupling_tilde_tau,\n       coupling_tilde_rho_ye,\n       coupling_tilde_s,\n       typename domain::Tags::NeighborMesh<Dim>::type{},\n       single_packet_energy,\n       spatial_velocity,\n       lapse,\n       shift,\n       phi,\n       spatial_metric,\n       inverse_spatial_metric,\n       sqrt_determinant_spatial_metric,\n       mesh_coordinates,\n       mesh_velocity,\n       inverse_jacobian_logical_to_inertial,\n       det_inverse_jacobian_logical_to_inertial,\n       inverse_jacobian_inertial_to_fluid,\n       det_inverse_jacobian_inertial_to_fluid,\n       jacobian_inertial_to_fluid,\n       fd_to_neighbor_fd_interpolants});\n\n  // Run singe time step\n  ActionTesting::next_action<comp>(make_not_null(&runner), self_id);\n\n  const auto& packets_from_box = ActionTesting::get_databox_tag<\n      comp, Particles::MonteCarlo::Tags::PacketsOnElement>(runner, self_id);\n  CHECK(packets_from_box[0].time == next_time_step_id.step_time().value());\n\n  // Test action vs standalone function. This is non-trivial because\n  // the standalone function directly uses d_lapse, d_shift, d_g\n  // instead of calculating those quantities from phi\n  std::vector<Particles::MonteCarlo::Packet> packets_on_element_2{};\n  Particles::MonteCarlo::TemplatedLocalFunctions<4, 3> MonteCarloStruct;\n  const double final_time = next_time_step_id.step_time().value();\n  packets_on_element_2.push_back(packet_t0);\n  // Interaction rates\n  const std::array<double, 4> energy_at_bin_center = {0.1, 1.0, 2.0, 3.0};\n  const std::array<std::array<DataVector, 4>, 3> absorption_opacity = {\n      std::array<DataVector, 4>{{zero_dv_with_ghost, zero_dv_with_ghost,\n                                 zero_dv_with_ghost, zero_dv_with_ghost}},\n      std::array<DataVector, 4>{{zero_dv_with_ghost, zero_dv_with_ghost,\n                                 zero_dv_with_ghost, zero_dv_with_ghost}},\n      std::array<DataVector, 4>{{zero_dv_with_ghost, zero_dv_with_ghost,\n                                 zero_dv_with_ghost, zero_dv_with_ghost}}};\n  const std::array<std::array<DataVector, 4>, 3> scattering_opacity = {\n      std::array<DataVector, 4>{{zero_dv_with_ghost, zero_dv_with_ghost,\n                                 zero_dv_with_ghost, zero_dv_with_ghost}},\n      std::array<DataVector, 4>{{zero_dv_with_ghost, zero_dv_with_ghost,\n                                 zero_dv_with_ghost, zero_dv_with_ghost}},\n      std::array<DataVector, 4>{{zero_dv_with_ghost, zero_dv_with_ghost,\n                                 zero_dv_with_ghost, zero_dv_with_ghost}}};\n  MonteCarloStruct.evolve_packets(\n      &packets_on_element_2, &generator, &coupling_tilde_tau, &coupling_tilde_s,\n      &coupling_tilde_rho_ye, final_time, subcell_mesh, mesh_coordinates,\n      num_ghost_zones, absorption_opacity, scattering_opacity,\n      energy_at_bin_center, lorentz_factor, lower_spatial_four_velocity, lapse,\n      shift, deriv_lapse, deriv_shift, deriv_inverse_spatial_metric,\n      spatial_metric, inverse_spatial_metric, cell_light_crossing_time,\n      mesh_velocity, inverse_jacobian_logical_to_inertial,\n      jacobian_inertial_to_fluid, inverse_jacobian_inertial_to_fluid);\n  const double eps_test = 1.e-14;\n  CHECK(packets_from_box[0].time == packets_on_element_2[0].time);\n  CHECK(fabs(packets_from_box[0].coordinates[0] -\n             packets_on_element_2[0].coordinates[0]) < eps_test);\n  CHECK(fabs(packets_from_box[0].coordinates[1] -\n             packets_on_element_2[0].coordinates[1]) < eps_test);\n  CHECK(fabs(packets_from_box[0].coordinates[2] -\n             packets_on_element_2[0].coordinates[2]) < eps_test);\n  CHECK(fabs(packets_from_box[0].momentum[0] -\n             packets_on_element_2[0].momentum[0]) < eps_test);\n  CHECK(fabs(packets_from_box[0].momentum[1] -\n             packets_on_element_2[0].momentum[1]) < eps_test);\n  CHECK(fabs(packets_from_box[0].momentum[2] -\n             packets_on_element_2[0].momentum[2]) < eps_test);\n  CHECK(fabs(packets_from_box[0].momentum_upper_t -\n             packets_on_element_2[0].momentum_upper_t) < eps_test);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Particles.MonteCarloTimeStepAction\",\n                  \"[Unit][Evolution]\") {\n  test_advance_packets();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Ringdown/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Ringdown\")\n\nset(LIBRARY_SOURCES\n  Test_StrahlkorperCoefsAndCenters.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  DomainCreators\n  Domain\n  GeneralRelativity\n  H5\n  Ringdown\n  Spectral\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  Utilities\n  )\n\nspectre_python_add_dependencies(\n  ${LIBRARY}\n  PyCoordinateMaps\n  PyDomain\n  PyDomainTimeDependentOptions\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/Evolution/Ringdown/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n    \"Evolution.Ringdown.ComputeShapeAndTranslationFoT\"\n    Test_ComputeRingdownShapeAndTranslationFoT.py\n    \"Python\"\n    None\n)\n"
  },
  {
    "path": "tests/Unit/Evolution/Ringdown/Python/Test_ComputeRingdownShapeAndTranslationFoT.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport math\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport numpy as np\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre import Spectral\nfrom spectre.DataStructures import DataVector, ModalVector\nfrom spectre.Domain import (\n    PiecewisePolynomial2,\n    PiecewisePolynomial3,\n    QuaternionFunctionOfTime,\n    serialize_domain,\n    serialize_functions_of_time,\n)\nfrom spectre.Domain.Creators import BinaryCompactObject, DomainCreator3D\nfrom spectre.Domain.Creators.TimeDependentOptions import (\n    BinaryCompactObjectTimeDependentOptions,\n    ExpansionMapOptions,\n    RotationMapOptions,\n    TranslationMapOptions,\n)\nfrom spectre.Evolution.Ringdown.ComputeRingdownShapeAndTranslationFoT import (\n    compute_ringdown_shape_and_translation_fot,\n)\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\nfrom spectre.SphericalHarmonics import Frame, Strahlkorper, ylm_legend_and_data\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestComputeAhCCoefs(unittest.TestCase):\n    def test_compute_ringdown_shape_and_translation_fot(self):\n        # Building a fake directory to hold fake reduction data\n        self.test_dir = Path(\n            unit_test_build_path(), \"Unit/Evolution/Ringdown/Python/Ringdown\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.inspiral_reduction_data = self.test_dir / \"BbhReductions.h5\"\n        shape_coefs = [5.0, 6.0, 7.0, 8.0, 9.0, 10.0]\n        times = [4990.0, 4992.0, 4994.0, 4996.0, 4998.0, 5000.0]\n        time_to_match = 5000.0\n        ahc_center = [0.0, 0.0, 0.0]\n        ahc_lmax = 2\n        with spectre_h5.H5File(\n            str(self.inspiral_reduction_data.resolve()), \"a\"\n        ) as reduction_file:\n            legend = [\n                \"Time\",\n                \"InertialExpansionCenter_x\",\n                \"InertialExpansionCenter_y\",\n                \"InertialExpansionCenter_z\",\n                \"Lmax\",\n                \"coef(0,0)\",\n                \"coef(1,-1)\",\n                \"coef(1,0)\",\n                \"coef(1,1)\",\n                \"coef(2,-2)\",\n                \"coef(2,-1)\",\n                \"coef(2,0)\",\n                \"coef(2,1)\",\n                \"coef(2,2)\",\n            ]\n            reduction_dat = reduction_file.try_insert_dat(\n                \"ObservationAhC_Ylm.dat\", legend, 0\n            )\n            for x in range(0, 5):\n                reduction_dat.append(\n                    [\n                        [\n                            times[x],\n                            ahc_center[0],\n                            ahc_center[1],\n                            ahc_center[2],\n                            ahc_lmax,\n                            shape_coefs[x],\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                        ]\n                    ]\n                )\n            reduction_file.close_current_object()\n\n        fot_dict = {}\n        fot_dict[\"Expansion\"] = [1.0, 0.0, 0.0]\n        fot_dict[\"ExpansionOuterBoundary\"] = [1.0, -1e-6, 0.0]\n        fot_dict[\"Rotation\"] = [\n            [0.0, 0.0, 0.0, 1.0],\n            [0.15, 0.0, 0.0, 0.02],\n            [0.06, 0.0, 0.0, 0.03],\n        ]\n        fot_dict[\"Translation\"] = [\n            [0.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0],\n        ]\n\n        # Making volume data for functions of time to be extracted\n        rotation_fot = QuaternionFunctionOfTime(\n            time=times[0],\n            initial_quat_func=[DataVector(size=4, fill=1.0)],\n            initial_angle_func=4 * [DataVector(size=3, fill=0.0)],\n            expiration_time=math.inf,\n        )\n        expansion_fot = PiecewisePolynomial3(\n            times[0], 4 * [DataVector(size=1, fill=1.0)], math.inf\n        )\n        expansion_outer_fot = PiecewisePolynomial3(\n            times[0], 4 * [DataVector(size=1, fill=1.0)], math.inf\n        )\n        translation_fot = PiecewisePolynomial2(\n            times[0],\n            [\n                DataVector([1.0, -1.0, 0.5]),\n                DataVector([0.2, 0.1, 0.0]),\n                DataVector([0.0, 0.0, 0.0]),\n            ],\n            math.inf,\n        )\n        serialized_fots = serialize_functions_of_time(\n            {\n                \"Expansion\": expansion_fot,\n                \"ExpansionOuterBoundary\": expansion_outer_fot,\n                \"Rotation\": rotation_fot,\n                \"Translation\": translation_fot,\n            }\n        )\n\n        expansion_map = ExpansionMapOptions([1.0, 1e-4, 0.0], 100.0, 1e-6)\n        rotation_map = RotationMapOptions([[0.0, 0.0, 0.0, 1.0]], 100.0)\n        translation_map = TranslationMapOptions(\n            [[1.0, -1.0, 0.5], [0.2, 0.1, 0.0], [0.0, 0.0, 0.0]]\n        )\n        bco_time_dependent_options = BinaryCompactObjectTimeDependentOptions(\n            times[0],\n            expansion_map,\n            rotation_map,\n            translation_map,\n            None,\n            None,\n            None,\n            None,\n        )\n\n        binary_domain = BinaryCompactObject(\n            inner_radius_a=0.5,\n            outer_radius_a=2.0,\n            x_coord_a=5.0,\n            excise_a=True,\n            use_logarithmic_map_a=True,\n            inner_radius_b=0.5,\n            outer_radius_b=2.0,\n            x_coord_b=-5.0,\n            excise_b=True,\n            use_logarithmic_map_b=True,\n            center_of_mass_offset=[0.1, 0.2],\n            envelope_radius=50.0,\n            outer_radius=600.0,\n            cube_scale=1.2,\n            initial_refinement=1,\n            initial_number_of_grid_points=5,\n            use_equiangular_map=True,\n            radial_partitioning_outer_shell=[],\n            opening_angle_in_degrees=120.0,\n            time_dependent_options=bco_time_dependent_options,\n        ).create_domain()\n\n        serialized_binary_domain = serialize_domain(binary_domain)\n        self.inspiral_volume_data = self.test_dir / \"BbhVolume0.h5\"\n        with spectre_h5.H5File(self.inspiral_volume_data, \"w\") as volume_file:\n            volfile = volume_file.insert_vol(\"ForContinuation\", version=0)\n            for x in range(0, 6):\n                volfile.write_volume_data(\n                    observation_id=x,\n                    observation_value=times[x],\n                    elements=[\n                        ElementVolumeData(\n                            element_name=\"WhatTheFreak\",\n                            components=[\n                                TensorComponent(\n                                    \"IsGoingOnHere\",\n                                    np.random.rand(3),\n                                ),\n                            ],\n                            extents=[3],\n                            basis=[Spectral.Basis.Legendre],\n                            quadrature=[Spectral.Quadrature.GaussLobatto],\n                        )\n                    ],\n                    serialized_domain=serialized_binary_domain,\n                    serialized_observation_functions_of_time=serialized_fots,\n                )\n        volume_file.close_current_object()\n\n        ringdown_ylm_coefs, ringdown_ylm_legend, ahc_translation_fot = (\n            compute_ringdown_shape_and_translation_fot(\n                path_to_volume_data=str(self.inspiral_volume_data),\n                volume_subfile_name=\"ForContinuation\",\n                ahc_reductions_path=str(self.inspiral_reduction_data),\n                ahc_subfile=\"ObservationAhC_Ylm.dat\",\n                evaluated_fot_dict=fot_dict,\n                number_of_ahc_finds_for_fit=5,\n                match_time=time_to_match,\n                settling_timescale=10.0,\n                zero_coefs_eps=None,\n            )\n        )\n        # Expected fit should be a line\n        expected_fit_ahc_coefs = [\n            10,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        ]\n        expected_fit_dt_ahc_coefs = [\n            0.5,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        ]\n        expected_fit_dt2_ahc_coefs = [\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n        ]\n        expected_fit_ahc_coefs_mv = ModalVector(expected_fit_ahc_coefs)\n        expected_fit_dt_ahc_coefs_mv = ModalVector(expected_fit_dt_ahc_coefs)\n        expected_fit_dt2_ahc_coefs_mv = ModalVector(expected_fit_dt2_ahc_coefs)\n        expected_ahc_strahlkorper = Strahlkorper[Frame.Inertial](\n            ahc_lmax, ahc_lmax, expected_fit_ahc_coefs_mv, ahc_center\n        )\n        expected_dt_ahc_strahlkorper = Strahlkorper[Frame.Inertial](\n            ahc_lmax, ahc_lmax, expected_fit_dt_ahc_coefs_mv, ahc_center\n        )\n        expected_dt2_ahc_strahlkorper = Strahlkorper[Frame.Inertial](\n            ahc_lmax, ahc_lmax, expected_fit_dt2_ahc_coefs_mv, ahc_center\n        )\n        # These are bad legends because they say InertialExpansionCenter instead\n        # of Distorted.\n        bad_legend_ahc, expected_ahc_ylm_coefs = ylm_legend_and_data(\n            expected_ahc_strahlkorper, time_to_match, ahc_lmax\n        )\n        bad_legend_dt_ahc, expected_dt_ahc_ylm_coefs = ylm_legend_and_data(\n            expected_dt_ahc_strahlkorper, time_to_match, ahc_lmax\n        )\n        bad_legend_dt2_ahc, expected_dt2_ahc_ylm_coefs = ylm_legend_and_data(\n            expected_dt2_ahc_strahlkorper, time_to_match, ahc_lmax\n        )\n        expected_legends_ahc = [\n            \"Time\",\n            \"DistortedExpansionCenter_x\",\n            \"DistortedExpansionCenter_y\",\n            \"DistortedExpansionCenter_z\",\n            \"Lmax\",\n            \"coef(0,0)\",\n            \"coef(1,-1)\",\n            \"coef(1,0)\",\n            \"coef(1,1)\",\n            \"coef(2,-2)\",\n            \"coef(2,-1)\",\n            \"coef(2,0)\",\n            \"coef(2,1)\",\n            \"coef(2,2)\",\n        ]\n        for x in range(0, len(expected_ahc_ylm_coefs)):\n            self.assertAlmostEqual(\n                first=expected_ahc_ylm_coefs[x],\n                second=ringdown_ylm_coefs[0][x],\n                places=11,\n            )\n            self.assertAlmostEqual(\n                first=expected_dt_ahc_ylm_coefs[x],\n                second=ringdown_ylm_coefs[1][x],\n                places=11,\n            )\n            self.assertAlmostEqual(\n                first=expected_dt2_ahc_ylm_coefs[x],\n                second=ringdown_ylm_coefs[2][x],\n                places=11,\n            )\n        self.assertNotEqual(bad_legend_ahc, ringdown_ylm_legend[0])\n        self.assertNotEqual(bad_legend_dt_ahc, ringdown_ylm_legend[1])\n        self.assertNotEqual(bad_legend_dt2_ahc, ringdown_ylm_legend[2])\n        self.assertEqual(expected_legends_ahc, ringdown_ylm_legend[0])\n        self.assertEqual(expected_legends_ahc, ringdown_ylm_legend[1])\n        self.assertEqual(expected_legends_ahc, ringdown_ylm_legend[2])\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Evolution/Ringdown/Test_StrahlkorperCoefsAndCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ExpansionMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/RotationMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/StrahlkorperTransformations.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Evolution/Ringdown/StrahlkorperCoefsAndCenters.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/ChangeCenterOfStrahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\n// [[TimeOut, 20]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Ringdown.StrahlkorperCoefsAndCenters\",\n                  \"[Unit][Evolution]\") {\n  // Write a temporary H5 file with Strahlkorpers at different times, then\n  // pass that file's path to strahlkorper_coefs_and_centers.\n  // First, if the temporary file exists, remove it\n  const std::string horizons_file_name{\"Unit.Evolution.Ringdown.SCoefsRDis.h5\"};\n  const std::string horizons_subfile_name{\"/ObservationAhC__Ylm.dat\"};\n  if (file_system::check_if_file_exists(horizons_file_name)) {\n    file_system::rm(horizons_file_name, true);\n  }\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  MAKE_GENERATOR(generator);\n\n  // Start out with a Strahlkorper at rest in a grid frame, then map it\n  // to the inertial frame. The shape map is an identity map (it's initialized\n  // with Schwarzschild coefficients), so the grid->distorted map\n  // here is the identity. But start in a grid frame to use the available\n  // grid->distorted instantiation of strahlkorper_in_different_frame.\n  constexpr const size_t l_max = 12;\n  constexpr const size_t m_max = 12;\n  const auto kerr_horizon_radius = get(gr::Solutions::kerr_horizon_radius(\n      ::ylm::Spherepack(l_max, m_max).theta_phi_points(), 1.0,\n      {{0.0, 0.0, 0.8}}));\n  auto expected_strahlkorper = ylm::Strahlkorper<Frame::Grid>(\n      l_max, m_max, kerr_horizon_radius, std::array<double, 3>{4.4, 5.5, 6.6});\n\n  // Make a set of times to evaluate the functions of time at\n  static constexpr size_t number_of_times{9};\n  const std::array<double, number_of_times> times{0.0, 0.2, 0.4, 0.6, 0.8,\n                                                  1.0, 1.2, 1.4, 1.6};\n\n  // Set match time to earliest time; must be earliest time, since\n  // the grid->inertial map used below will not be valid at times earlier\n  // than the match time\n  const double match_time{1.6};\n\n  // Next, set up a temporary domain (just to hold functions of time)\n  // and some functions of time defining the ringdown Distorted->Inertial map.\n  std::uniform_real_distribution<double> fot_dist{0.1, 0.5};\n  std::uniform_real_distribution<double> exp_dist{0.6, 0.8};\n  const auto exp_func_and_2_derivs =\n      make_with_random_values<std::array<double, 3>>(make_not_null(&generator),\n                                                     make_not_null(&exp_dist));\n  const auto exp_outer_bdry_func_and_2_derivs =\n      make_with_random_values<std::array<double, 3>>(make_not_null(&generator),\n                                                     make_not_null(&exp_dist));\n  auto initial_unit_quaternion = make_with_random_values<std::array<double, 4>>(\n      make_not_null(&generator), make_not_null(&fot_dist));\n  const double initial_unit_quaternion_magnitude = sqrt(\n      square(initial_unit_quaternion[0]) + square(initial_unit_quaternion[1]) +\n      square(initial_unit_quaternion[2]) + square(initial_unit_quaternion[3]));\n  for (size_t i = 0; i < 4; ++i) {\n    gsl::at(initial_unit_quaternion, i) /= initial_unit_quaternion_magnitude;\n  }\n  const std::vector<std::array<double, 4>> rot_func_and_2_derivs{\n      initial_unit_quaternion,\n      make_with_random_values<std::array<double, 4>>(make_not_null(&generator),\n                                                     make_not_null(&fot_dist)),\n      make_with_random_values<std::array<double, 4>>(make_not_null(&generator),\n                                                     make_not_null(&fot_dist))};\n\n  std::uniform_real_distribution<double> settling_dist{0.5, 1.5};\n  const double settling_timescale{settling_dist(generator)};\n\n  const domain::creators::time_dependent_options::ShapeMapOptions<\n      false, domain::ObjectLabel::None>\n      shape_map_options{l_max, std::nullopt};\n  const domain::creators::time_dependent_options::ExpansionMapOptions<true>\n      expansion_map_options{exp_func_and_2_derivs, settling_timescale,\n                            exp_outer_bdry_func_and_2_derivs,\n                            settling_timescale};\n  const domain::creators::time_dependent_options::RotationMapOptions<true>\n      rotation_map_options{rot_func_and_2_derivs, settling_timescale};\n  const domain::creators::sphere::TimeDependentMapOptions\n      time_dependent_map_options{times.at(0),          shape_map_options,\n                                 rotation_map_options, expansion_map_options,\n                                 std::nullopt,         true};\n\n  const domain::creators::Sphere domain_creator{\n      0.01,\n      100.0,\n      // nullptr because no boundary condition\n      domain::creators::Sphere::Excision{nullptr},\n      static_cast<size_t>(0),\n      static_cast<size_t>(5),\n      false,\n      std::nullopt,\n      {50.0},\n      domain::CoordinateMaps::Distribution::Linear,\n      ShellWedges::All,\n      time_dependent_map_options};\n  const auto temporary_domain = domain_creator.create_domain();\n  const auto functions_of_time = domain_creator.functions_of_time();\n\n  // For each Strahlkorper, transform from distorted -> inertial using\n  // strahlkorper_in_different_frame, then\n  // get its inertial coefficients, and write them out to the h5 file\n  std::vector<std::vector<double>> strahlkorper_ringdown_inertial_coefs{\n      number_of_times};\n  std::vector<std::string> legend{};\n  ylm::Strahlkorper<Frame::Inertial> current_inertial_strahlkorper;\n  for (size_t i = 0; i < number_of_times; ++i) {\n    legend.resize(0);  // clear and reuse for next row of data\n    strahlkorper_in_different_frame(\n        make_not_null(&current_inertial_strahlkorper), expected_strahlkorper,\n        temporary_domain, functions_of_time, gsl::at(times, i));\n    ylm::change_expansion_center_of_strahlkorper_to_physical(\n        make_not_null(&current_inertial_strahlkorper),\n        evolution::Ringdown::expansion_center_tolerance);\n    ylm::fill_ylm_legend_and_data(\n        make_not_null(&legend),\n        make_not_null(&strahlkorper_ringdown_inertial_coefs[i]),\n        current_inertial_strahlkorper, gsl::at(times, i), l_max);\n  }\n  {\n    h5::H5File<h5::AccessType::ReadWrite> strahlkorper_file{horizons_file_name,\n                                                            true};\n    auto& coefs_file =\n        strahlkorper_file.insert<h5::Dat>(horizons_subfile_name, legend, 4);\n    coefs_file.append(strahlkorper_ringdown_inertial_coefs);\n  }\n\n  const domain::creators::time_dependent_options::RotationMapOptions<false>\n      rotation_map_options_bco{std::array{0.0, 0.0, 0.0}};\n  const domain::creators::time_dependent_options::ExpansionMapOptions<false>\n      expansion_map_options_bco{{1.0, 1e-5, 0.0}, 50, -1.0e-6};\n  const domain::creators::bco::TimeDependentMapOptions<false>\n      time_dependent_map_options_bco{\n          times.at(0),\n          expansion_map_options_bco,\n          rotation_map_options_bco,\n          std::nullopt,\n          std::nullopt,\n          domain::creators::time_dependent_options::ShapeMapOptions<\n              true, domain::ObjectLabel::A>{\n              32_st, domain::creators::time_dependent_options::\n                         KerrSchildFromBoyerLindquist{0.2, {0.0, 0.0, 0.0}}},\n          domain::creators::time_dependent_options::ShapeMapOptions<\n              true, domain::ObjectLabel::B>{\n              32_st, domain::creators::time_dependent_options::\n                         KerrSchildFromBoyerLindquist{0.4, {0.0, 0.0, 0.0}}},\n          std::nullopt};\n\n  using Object = domain::creators::BinaryCompactObject<false>::Object;\n  const domain::creators::BinaryCompactObject<false> domain_creator_bco{\n      Object{0.1, 6., 8., true, true},\n      Object{0.2, 6, -6., true, true},\n      std::array<double, 2>{{0., 0.}},\n      60.,\n      300.,\n      1.0,\n      0_st,\n      6_st,\n      true,\n      domain::CoordinateMaps::Distribution::Projective,\n      std::vector<double>{},\n      domain::CoordinateMaps::Distribution::Inverse,\n      120.,\n      time_dependent_map_options_bco};\n  const auto domain_bco = domain_creator_bco.create_domain();\n  const auto functions_of_time_bco = domain_creator_bco.functions_of_time();\n  auto serialized_fots_bco = serialize(functions_of_time_bco);\n  auto serialized_domain_bco = serialize(domain_bco);\n\n  if (file_system::check_if_file_exists(\"BbhVolume0.h5\")) {\n    file_system::rm(\"BbhVolume0.h5\", true);\n  }\n  h5::H5File<h5::AccessType::ReadWrite> h5_file{\"BbhVolume0.h5\", true};\n  auto& vol_file = h5_file.insert<h5::VolumeData>(\"ForContinuation\");\n\n  for (size_t i = 0; i < times.size(); i++) {\n    vol_file.write_volume_data(\n        i, times.at(i),\n        {ElementVolumeData{\n            \"FakeElementName\",\n            {TensorComponent{\"RandomTensor\", DataVector{3, 0.0}}},\n            {3},\n            {Spectral::Basis::Legendre},\n            {Spectral::Quadrature::GaussLobatto}}},\n        serialized_domain_bco, serialized_fots_bco);\n  }\n  h5_file.close_current_object();\n\n  // Call strahlkorper_coefs_and_centers()\n  constexpr size_t times_to_retrieve{number_of_times - 2};\n  const std::pair<std::vector<DataVector>, std::vector<std::array<double, 3>>>\n      distorted_and_translation_coefs =\n          evolution::Ringdown::strahlkorper_coefs_and_centers(\n              \"BbhVolume0.h5\", \"ForContinuation\", horizons_file_name,\n              horizons_subfile_name, times_to_retrieve, match_time,\n              settling_timescale, exp_func_and_2_derivs,\n              exp_outer_bdry_func_and_2_derivs, rot_func_and_2_derivs);\n\n  // Checks\n  // std::vector is the expected size\n  const size_t times_retrieved = distorted_and_translation_coefs.first.size();\n  CHECK(times_retrieved == times_to_retrieve);\n\n  const auto distorted_coefs = distorted_and_translation_coefs.first;\n  const auto translation_coefs = distorted_and_translation_coefs.second;\n\n  // Check that retrieved coefs are the expected size\n  const auto& expected_coefs = expected_strahlkorper.coefficients();\n  const size_t coefs_size_expected = expected_coefs.size();\n  const size_t coefs_size_retrieved = distorted_coefs[0].size();\n  CHECK(coefs_size_expected == coefs_size_retrieved);\n  CHECK(translation_coefs.size() == times_to_retrieve);\n\n  if (file_system::check_if_file_exists(horizons_file_name)) {\n    file_system::rm(horizons_file_name, true);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryConditions/DemandOutgoingCharSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef error(face_mesh_velocity, outward_directed_normal_covector, u):\n    speed = outward_directed_normal_covector[0] * u\n    if not face_mesh_velocity is None:\n        speed -= face_mesh_velocity[0] * outward_directed_normal_covector[0]\n\n    if speed < 0.0:\n        return \"DemandOutgoingCharSpeeds boundary condition violated\"\n    return None\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryConditions/Dirichlet.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef error(face_mesh_velocity, outward_directed_normal_covector):\n    return None\n\n\ndef flux_1(face_mesh_velocity, outward_directed_normal_covector):\n    return np.asarray([0.5])\n\n\ndef u_1(face_mesh_velocity, outward_directed_normal_covector):\n    return 1.0\n\n\ndef flux_m1(face_mesh_velocity, outward_directed_normal_covector):\n    return np.asarray([0.5])\n\n\ndef u_m1(face_mesh_velocity, outward_directed_normal_covector):\n    return -1.0\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryConditions/DirichletAnalytic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef error_sinusoid(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    time,\n    ignored_argument_for_analytic_data,\n):\n    return None\n\n\ndef u_sinusoid(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    time,\n    ignored_argument_for_analytic_data,\n):\n    return np.sin(coords[0])\n\n\ndef flux_sinusoid(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    time,\n    ignored_argument_for_analytic_data,\n):\n    u = np.sin(coords[0])\n    return np.asarray([0.5 * u**2])\n\n\ndef error_step(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    time,\n    ignored_argument_for_analytic_data,\n):\n    return None\n\n\ndef u_step(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    time,\n    analytic_soln_params,\n):\n    current_shock_position = (\n        analytic_soln_params[2]\n        + 0.5 * (analytic_soln_params[0] + analytic_soln_params[1]) * time\n    )\n    if coords[0] - current_shock_position < 0.0:\n        return analytic_soln_params[0]\n    else:\n        return analytic_soln_params[1]\n\n\ndef flux_step(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    time,\n    analytic_soln_params,\n):\n    return np.asarray(\n        [\n            0.5\n            * u_step(\n                face_mesh_velocity,\n                outward_directed_normal_covector,\n                coords,\n                time,\n                analytic_soln_params,\n            )\n            ** 2\n        ]\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.BoundaryConditions.DemandOutgoingCharSpeeds\",\n                  \"[Unit][Burgers]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Burgers/BoundaryConditions/\"};\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      Burgers::BoundaryConditions::DemandOutgoingCharSpeeds,\n      Burgers::BoundaryConditions::BoundaryCondition, Burgers::System,\n      tmpl::list<Burgers::BoundaryCorrections::Rusanov>>(\n      make_not_null(&gen), \"DemandOutgoingCharSpeeds\",\n      tuples::TaggedTuple<helpers::Tags::PythonFunctionForErrorMessage<>>{\n          \"error\"},\n      \"DemandOutgoingCharSpeeds:\\n\", Index<0>{1}, db::DataBox<tmpl::list<>>{},\n      tuples::TaggedTuple<>{});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryConditions/Test_Dirichlet.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Dirichlet.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.BoundaryConditions.Dirichlet\",\n                  \"[Unit][Burgers]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Burgers/BoundaryConditions/\"};\n  MAKE_GENERATOR(gen);\n  helpers::test_boundary_condition_with_python<\n      Burgers::BoundaryConditions::Dirichlet,\n      Burgers::BoundaryConditions::BoundaryCondition, Burgers::System,\n      tmpl::list<Burgers::BoundaryCorrections::Rusanov>>(\n      make_not_null(&gen), \"Dirichlet\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<Burgers::Tags::U>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>>>{\n          \"error\", \"u_1\", \"flux_1\"},\n      \"Dirichlet:\\n\"\n      \"  U: 1.0\\n\",\n      Index<0>{1}, db::DataBox<tmpl::list<>>{}, tuples::TaggedTuple<>{});\n\n  helpers::test_boundary_condition_with_python<\n      Burgers::BoundaryConditions::Dirichlet,\n      Burgers::BoundaryConditions::BoundaryCondition, Burgers::System,\n      tmpl::list<Burgers::BoundaryCorrections::Rusanov>>(\n      make_not_null(&gen), \"Dirichlet\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<Burgers::Tags::U>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>>>{\n          \"error\", \"u_m1\", \"flux_m1\"},\n      \"Dirichlet:\\n\"\n      \"  U: -1.0\\n\",\n      Index<0>{1}, db::DataBox<tmpl::list<>>{}, tuples::TaggedTuple<>{});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Burgers/Sinusoid.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Step.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\nstruct ConvertSinusoid {\n  using unpacked_container = double;\n  using packed_container = Burgers::AnalyticData::Sinusoid;\n  using packed_type = double;\n\n  static inline unpacked_container unpack(const packed_container /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return 0.0;  // no parameter but we need some placeholder type\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> /*packed*/,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    // no parameters but we need a placeholder function\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct ConvertStep {\n  using unpacked_container = std::array<double, 3>;\n  using packed_container = Burgers::Solutions::Step;\n  using packed_type = double;\n\n  static packed_container create_container() { return {2.0, 1.0, 0.5}; }\n\n  static inline unpacked_container unpack(const packed_container /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return {{2.0, 1.0, 0.5}};\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container unpacked,\n                          const size_t /*grid_point_index*/) {\n    *packed = packed_container{unpacked[0], unpacked[1], unpacked[2]};\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Burgers::BoundaryConditions::BoundaryCondition,\n                   tmpl::list<Burgers::BoundaryConditions::DirichletAnalytic>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   tmpl::list<Burgers::Solutions::Step,\n                              Burgers::AnalyticData::Sinusoid>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.BoundaryConditions.DirichletAnalytic\",\n                  \"[Unit][Burgers]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Burgers/BoundaryConditions/\"};\n  MAKE_GENERATOR(gen);\n  const auto box_analytic_data = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticData<Burgers::AnalyticData::Sinusoid>>>(\n      0.4, Burgers::AnalyticData::Sinusoid{});\n  register_classes_with_charm<Burgers::Solutions::Step,\n                              Burgers::AnalyticData::Sinusoid>();\n\n  helpers::test_boundary_condition_with_python<\n      Burgers::BoundaryConditions::DirichletAnalytic,\n      Burgers::BoundaryConditions::BoundaryCondition, Burgers::System,\n      tmpl::list<Burgers::BoundaryCorrections::Rusanov>,\n      tmpl::list<ConvertSinusoid>,\n      tmpl::list<Tags::AnalyticData<Burgers::AnalyticData::Sinusoid>>,\n      Metavariables>(\n      make_not_null(&gen), \"DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<Burgers::Tags::U>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>>>{\n          \"error_sinusoid\", \"u_sinusoid\", \"flux_sinusoid\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    Sinusoid:\\n\",\n      Index<0>{1}, box_analytic_data, tuples::TaggedTuple<>{});\n\n  const auto box_analytic_soln = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticSolution<Burgers::Solutions::Step>>>(\n      0.5, ConvertStep::create_container());\n\n  helpers::test_boundary_condition_with_python<\n      Burgers::BoundaryConditions::DirichletAnalytic,\n      Burgers::BoundaryConditions::BoundaryCondition, Burgers::System,\n      tmpl::list<Burgers::BoundaryCorrections::Rusanov>,\n      tmpl::list<ConvertStep>,\n      tmpl::list<Tags::AnalyticSolution<Burgers::Solutions::Step>>,\n      Metavariables>(\n      make_not_null(&gen), \"DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<Burgers::Tags::U>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>>>{\n          \"error_step\", \"u_step\", \"flux_step\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    Step:\\n\"\n      \"      LeftValue: 2.0\\n\"\n      \"      RightValue: 1.0\\n\"\n      \"      InitialPosition: 0.5\\n\",\n      Index<0>{1}, box_analytic_soln, tuples::TaggedTuple<>{});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryConditions/Test_Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Factory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.BoundaryConditions.Periodic\",\n                  \"[Unit][Burgers]\") {\n  helpers::test_periodic_condition<\n      domain::BoundaryConditions::Periodic<\n          Burgers::BoundaryConditions::BoundaryCondition>,\n      Burgers::BoundaryConditions::BoundaryCondition>(\"Periodic:\\n\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryCorrections/Hll.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    u, flux_u, normal_covector, mesh_velocity, normal_dot_mesh_velocity\n):\n    result = u if normal_covector[0] > 0.0 else -u\n    return (\n        u,\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_u)),\n        np.asarray(\n            result\n            if normal_dot_mesh_velocity is None\n            else (result - normal_dot_mesh_velocity)\n        ),\n    )\n\n\ndef dg_boundary_terms(\n    interior_u,\n    interior_normal_dot_flux_u,\n    interior_char_speed,\n    exterior_u,\n    exterior_normal_dot_flux_u,\n    exterior_char_speed,\n    use_strong_form,\n):\n    lambda_min = np.minimum(\n        np.minimum(0.0, interior_char_speed), -exterior_char_speed\n    )\n    lambda_max = np.maximum(\n        np.maximum(0.0, interior_char_speed), -exterior_char_speed\n    )\n    result = (\n        (\n            lambda_max * interior_normal_dot_flux_u\n            + lambda_min * exterior_normal_dot_flux_u\n        )\n        + lambda_max * lambda_min * (exterior_u - interior_u)\n    ) / (lambda_max - lambda_min)\n\n    if use_strong_form:\n        result -= interior_normal_dot_flux_u\n    return (np.asarray(result),)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    u, flux_u, normal_covector, mesh_velocity, normal_dot_mesh_velocity\n):\n    return (\n        u,\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_u)),\n        np.asarray(\n            np.abs(u)\n            if normal_dot_mesh_velocity is None\n            else np.abs(u - normal_dot_mesh_velocity)\n        ),\n    )\n\n\ndef dg_boundary_terms(\n    interior_u,\n    interior_normal_dot_flux_u,\n    interior_abs_char_speed,\n    exterior_u,\n    exterior_normal_dot_flux_u,\n    exterior_abs_char_speed,\n    use_strong_form,\n):\n    if use_strong_form:\n        return (\n            np.asarray(\n                -0.5 * (interior_normal_dot_flux_u + exterior_normal_dot_flux_u)\n                - 0.5\n                * np.maximum(interior_abs_char_speed, exterior_abs_char_speed)\n                * (exterior_u - interior_u)\n            ),\n        )\n    else:\n        return (\n            np.asarray(\n                0.5 * (interior_normal_dot_flux_u - exterior_normal_dot_flux_u)\n                - 0.5\n                * np.maximum(interior_abs_char_speed, exterior_abs_char_speed)\n                * (exterior_u - interior_u)\n            ),\n        )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryCorrections/Test_Hll.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.BoundaryCorrections.Hll\", \"[Unit][Burgers]\") {\n  PUPable_reg(Burgers::BoundaryCorrections::Hll);\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Burgers/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<\n      Burgers::System>(\n      make_not_null(&gen), Burgers::BoundaryCorrections::Hll{},\n      Mesh<0>{1, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      Burgers::System>(\n      make_not_null(&gen), \"Hll\", \"dg_package_data\", \"dg_boundary_terms\",\n      Burgers::BoundaryCorrections::Hll{},\n      Mesh<0>{1, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n\n  const auto Hll = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection, Burgers::BoundaryCorrections::Hll>(\"Hll:\");\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      Burgers::System>(\n      make_not_null(&gen), \"Hll\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const Burgers::BoundaryCorrections::Hll&>(*Hll),\n      Mesh<0>{1, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/BoundaryCorrections/Test_Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.BoundaryCorrections.Rusanov\",\n                  \"[Unit][Burgers]\") {\n  PUPable_reg(Burgers::BoundaryCorrections::Rusanov);\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Burgers/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<\n      Burgers::System>(\n      make_not_null(&gen), Burgers::BoundaryCorrections::Rusanov{},\n      Mesh<0>{1, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      Burgers::System>(\n      make_not_null(&gen), \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      Burgers::BoundaryCorrections::Rusanov{},\n      Mesh<0>{1, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n\n  const auto rusanov =\n      TestHelpers::test_factory_creation<evolution::BoundaryCorrection,\n                                         Burgers::BoundaryCorrections::Rusanov>(\n          \"Rusanov:\");\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      Burgers::System>(\n      make_not_null(&gen), \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const Burgers::BoundaryCorrections::Rusanov&>(*rusanov),\n      Mesh<0>{1, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Test_Burgers)\n\nset(LIBRARY_SOURCES\n  BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp\n  BoundaryConditions/Test_Dirichlet.cpp\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryConditions/Test_Periodic.cpp\n  BoundaryCorrections/Test_Hll.cpp\n  BoundaryCorrections/Test_Rusanov.cpp\n  FiniteDifference/Test_BoundaryConditionGhostData.cpp\n  FiniteDifference/Test_MonotonisedCentral.cpp\n  FiniteDifference/Test_Tag.cpp\n  Subcell/Test_ComputeFluxes.cpp\n  Subcell/Test_GhostData.cpp\n  Subcell/Test_NeighborPackagedData.cpp\n  Subcell/Test_SetInitialRdmpData.cpp\n  Subcell/Test_TciOnDgGrid.cpp\n  Subcell/Test_TciOnFdGrid.cpp\n  Subcell/Test_TimeDerivative.cpp\n  Test_Characteristics.cpp\n  Test_Fluxes.cpp\n  Test_Tags.cpp\n  Test_TimeDerivativeTerms.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Burgers\n  BurgersAnalyticData\n  BurgersSolutions\n  DataStructures\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/FiniteDifference/Linear.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef u(x, t):\n    return x[0] / (t + 0.5)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/FiniteDifference/Test_BoundaryConditionGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/BoundaryConditionGhostData.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Linear.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\n// Metavariables to parse the list of derived classes of boundary conditions\nstruct EvolutionMetaVars {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Burgers::BoundaryConditions::BoundaryCondition,\n                   Burgers::BoundaryConditions::standard_boundary_conditions>>;\n  };\n};\n\ntemplate <typename BoundaryConditionType>\nvoid test(const BoundaryConditionType& boundary_condition) {\n  const size_t num_dg_pts = 3;\n\n  // Create an 1D interval [-1, 1] and use it for test\n  const std::array<double, 1> lower_x{-1.0};\n  const std::array<double, 1> upper_x{1.0};\n  const std::array<size_t, 1> refinement_level_x{0};\n  const std::array<size_t, 1> number_of_grid_points_in_x{num_dg_pts};\n  const auto interval = domain::creators::Interval(\n      lower_x, upper_x, refinement_level_x, number_of_grid_points_in_x,\n      {{{{boundary_condition.get_clone(), boundary_condition.get_clone()}}}});\n  auto domain = interval.create_domain();\n  auto boundary_conditions = interval.external_boundary_conditions();\n  const auto element = domain::create_initial_element(\n      ElementId<1>{0, {SegmentId{0, 0}}}, domain.blocks(),\n      std::vector<std::array<size_t, 1>>{{refinement_level_x}});\n\n  // Mesh and coordinates\n  const Mesh<1> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  // use MC reconstruction for test\n  using ReconstructorForTest = Burgers::fd::MonotonisedCentral;\n\n  // dummy neighbor data to put into DataBox\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<1>::type\n      ghost_data{};\n\n  // Below are tags required by DirichletAnalytic boundary condition to compute\n  // inertial coords of ghost FD cells:\n  //  - time\n  //  - functions of time\n  //  - element map\n  //  - coordinate map\n  //  - subcell logical coordinates\n  //  - analytic solution\n\n  const double time{0.0};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  const ElementMap<1, Frame::Grid> logical_to_grid_map(\n      ElementId<1>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<1>{}));\n\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<1>{});\n\n  const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n\n  using SolutionForTest = Burgers::Solutions::Linear;\n  const SolutionForTest solution{-0.5};\n\n  // Below are tags required by DemandOutgoingCharSpeeds boundary condition\n  //  - scalar field U on subcell mesh\n  //  - mesh velocity\n  //  - normal vectors\n\n  const double volume_u_val = 1.0;\n  Scalar<DataVector> u_subcell{subcell_mesh.number_of_grid_points(),\n                               volume_u_val};\n\n  std::optional<tnsr::I<DataVector, 1>> volume_mesh_velocity{};\n\n  typename evolution::dg::Tags::NormalCovectorAndMagnitude<1>::type\n      normal_vectors{};\n  for (const auto& direction : Direction<1>::all_directions()) {\n    const auto coordinate_map =\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<1>{});\n    const auto moving_mesh_map =\n        domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<1>{});\n\n    const Mesh<0> face_mesh = subcell_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<1>, tnsr::i<DataVector, 1, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, 1, Frame::Inertial> unnormalized_covector{};\n    unnormalized_covector.get(0) =\n        coordinate_map.inv_jacobian(face_logical_coords)\n            .get(direction.dimension(), 0);\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        evolution::dg::Actions::detail::NormalVector<1>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<Burgers::System>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, moving_mesh_map);\n  }\n\n  // create a box\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<EvolutionMetaVars>,\n      domain::Tags::Domain<1>, domain::Tags::ExternalBoundaryConditions<1>,\n      evolution::dg::subcell::Tags::Mesh<1>,\n      evolution::dg::subcell::Tags::Coordinates<1, Frame::ElementLogical>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<1>,\n      Burgers::fd::Tags::Reconstructor, domain::Tags::MeshVelocity<1>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<1>, Tags::Time,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::ElementMap<1, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<1, Frame::Grid,\n                                                  Frame::Inertial>,\n      Tags::AnalyticSolution<SolutionForTest>, Burgers::Tags::U>>(\n      EvolutionMetaVars{}, std::move(domain), std::move(boundary_conditions),\n      subcell_mesh, subcell_logical_coords, ghost_data,\n      std::unique_ptr<Burgers::fd::Reconstructor>{\n          std::make_unique<ReconstructorForTest>()},\n      volume_mesh_velocity, normal_vectors, time,\n      clone_unique_ptrs(functions_of_time),\n      ElementMap<1, Frame::Grid>{\n          ElementId<1>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<1>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<1>{}),\n      solution, u_subcell);\n\n  {\n    // compute FD ghost data and retrieve the result\n    Burgers::fd::BoundaryConditionGhostData::apply(make_not_null(&box), element,\n                                                   ReconstructorForTest{});\n    const auto direction = Direction<1>::upper_xi();\n    const DirectionalId<1> mortar_id = {direction,\n                                        ElementId<1>::external_boundary_id()};\n    const DataVector& fd_ghost_data =\n        get<evolution::dg::subcell::Tags::GhostDataForReconstruction<1>>(box)\n            .at(mortar_id)\n            .neighbor_ghost_data_for_reconstruction();\n\n    // now check values for each types of boundary conditions\n\n    if (typeid(BoundaryConditionType) ==\n        typeid(Burgers::BoundaryConditions::Dirichlet)) {\n      const DataVector expected_ghost_u{fd_ghost_data.size(), 0.3};\n      CHECK_ITERABLE_APPROX(expected_ghost_u, fd_ghost_data);\n    }\n\n    if (typeid(BoundaryConditionType) ==\n        typeid(Burgers::BoundaryConditions::DirichletAnalytic)) {\n      const size_t ghost_zone_size{ReconstructorForTest{}.ghost_zone_size()};\n\n      const auto ghost_logical_coords =\n          evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n              subcell_mesh, ghost_zone_size, direction);\n\n      const auto ghost_inertial_coords = (*grid_to_inertial_map)(\n          logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n      const auto solution_py = pypp::call<Scalar<DataVector>>(\n          \"Linear\", \"u\", ghost_inertial_coords, time);\n      DataVector expected_ghost_u{get(solution_py).size()};\n      std::copy(get(solution_py).begin(), get(solution_py).end(),\n                expected_ghost_u.begin());\n\n      CHECK_ITERABLE_APPROX(expected_ghost_u, fd_ghost_data);\n    }\n\n    if (typeid(BoundaryConditionType) ==\n        typeid(Burgers::BoundaryConditions::DemandOutgoingCharSpeeds)) {\n      // U = 1.0 on the subcell mesh, so the DemandOutgoingCharSpeeds condition\n      // will not throw any error. Here we just check if\n      // `Burgers::fd::BoundaryConditionGhostData::apply()` has correctly filled\n      // out `fd_ghost_data` with the outermost value.\n      const DataVector expected_ghost_u{fd_ghost_data.size(), volume_u_val};\n      CHECK_ITERABLE_APPROX(expected_ghost_u, fd_ghost_data);\n\n      // Test when U=-1.0, which will raise ERROR by violating the\n      // DemandOutgoingCharSpeeds condition.\n      db::mutate<Burgers::Tags::U>(\n          [](const gsl::not_null<Scalar<DataVector>*> volume_u) {\n            get(*volume_u) = -1.0;\n          },\n          make_not_null(&box));\n      // See if the code fails correctly.\n      CHECK_THROWS_WITH(([&box, &element]() {\n                          Burgers::fd::BoundaryConditionGhostData::apply(\n                              make_not_null(&box), element,\n                              ReconstructorForTest{});\n                        })(),\n                        Catch::Matchers::ContainsSubstring(\n                            \"DemandOutgoingCharSpeeds boundary \"\n                            \"condition (subcell) violated\"));\n\n      // Test when the volume mesh velocity has value, which will raise ERROR.\n      db::mutate<domain::Tags::MeshVelocity<1>>(\n          [&subcell_mesh](\n              const gsl::not_null<std::optional<tnsr::I<DataVector, 1>>*>\n                  mesh_velocity) {\n            tnsr::I<DataVector, 1> volume_mesh_velocity_tnsr(\n                subcell_mesh.number_of_grid_points(), 0.0);\n            *mesh_velocity = volume_mesh_velocity_tnsr;\n          },\n          make_not_null(&box));\n      // See if the code fails correctly.\n      CHECK_THROWS_WITH(\n          ([&box, &element]() {\n            Burgers::fd::BoundaryConditionGhostData::apply(\n                make_not_null(&box), element, ReconstructorForTest{});\n          })(),\n          Catch::Matchers::ContainsSubstring(\n              \"Subcell currently does not support moving mesh\"));\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Fd.BCondGhostData\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Burgers/FiniteDifference\"};\n\n  test(TestHelpers::test_creation<Burgers::BoundaryConditions::Dirichlet>(\n      \"U: 0.3\\n\"));\n  test(Burgers::BoundaryConditions::DirichletAnalytic{\n      std::make_unique<Burgers::Solutions::Linear>(-0.5)});\n  test(Burgers::BoundaryConditions::DemandOutgoingCharSpeeds{});\n\n  // check that the periodic BC fails\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(([]() {\n                      test(domain::BoundaryConditions::Periodic<\n                           Burgers::BoundaryConditions::BoundaryCondition>{});\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"not on external boundaries\"));\n#endif\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/FiniteDifference/Test_MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"Evolution/Systems/Burgers/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Burgers/FiniteDifference/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Fd.MonotonisedCentral\",\n                  \"[Unit][Evolution]\") {\n  auto mc = TestHelpers::test_creation<Burgers::fd::MonotonisedCentral>(\"\");\n  CHECK(mc == Burgers::fd::MonotonisedCentral());\n\n  test_serialization(mc);\n\n  const auto mc_from_options_base = TestHelpers::test_factory_creation<\n      Burgers::fd::Reconstructor, Burgers::fd::OptionTags::Reconstructor>(\n      \"MonotonisedCentral:\\n\");\n  auto* const mc_from_options =\n      dynamic_cast<const Burgers::fd::MonotonisedCentral*>(\n          mc_from_options_base.get());\n  REQUIRE(mc_from_options != nullptr);\n  CHECK(*mc_from_options == mc);\n\n  const size_t num_pts = 5;\n  TestHelpers::Burgers::fd::test_reconstructor(num_pts, mc);\n\n  Burgers::fd::MonotonisedCentral mc_copy;\n  test_move_semantics(std::move(mc), mc_copy);  //  NOLINT\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/FiniteDifference/Test_Tag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Fd.Tag\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<Burgers::fd::Tags::Reconstructor>(\n      \"Reconstructor\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Flux.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef flux(u):\n    return np.array([0.5 * u**2])\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Subcell/Test_ComputeFluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers {\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Subcell.ComputeFluxes\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n\n  using argument_tags = typename Fluxes::argument_tags;\n  using return_tags = typename Fluxes::return_tags;\n\n  // generate random vars\n  const size_t num_pts = 5;\n  auto random_vars = make_with_random_values<\n      Variables<tmpl::append<return_tags, argument_tags>>>(make_not_null(&gen),\n                                                           dist, num_pts);\n\n  // store computed fluxes into return_tags portion of `random_vars`\n  subcell::compute_fluxes(make_not_null(&random_vars));\n\n  // compute expected fluxes\n  Variables<return_tags> expected_flux{num_pts};\n  Fluxes::apply(\n      make_not_null(\n          &get<::Tags::Flux<Tags::U, tmpl::size_t<1>, Frame::Inertial>>(\n              expected_flux)),\n      get<Tags::U>(random_vars));\n\n  // check result\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::Flux<Tags::U, tmpl::size_t<1>, Frame::Inertial>>(\n          random_vars)),\n      (get<::Tags::Flux<Tags::U, tmpl::size_t<1>, Frame::Inertial>>(\n          expected_flux)));\n}\n}  // namespace Burgers\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Subcell/Test_GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/GhostData.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Subcell.GhostData\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n\n  // make random U on DG and subcell mesh\n  const size_t num_of_pts_dg_grid = 4;\n  const Mesh<1> dg_mesh{num_of_pts_dg_grid, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  const auto random_vars_dg =\n      make_with_random_values<Variables<tmpl::list<Burgers::Tags::U>>>(\n          make_not_null(&gen), dist, dg_mesh.number_of_grid_points());\n  const auto random_vars_subcell =\n      make_with_random_values<Variables<tmpl::list<Burgers::Tags::U>>>(\n          make_not_null(&gen), dist, subcell_mesh.number_of_grid_points());\n\n  // add the random U on the subcell mesh to a databox, apply\n  // GhostDataOnSubcells and compare with the returned vector\n  auto box_subcell = db::create<\n      db::AddSimpleTags<::Tags::Variables<tmpl::list<Burgers::Tags::U>>>>(\n      random_vars_subcell);\n  DataVector retrieved_vars_subcell =\n      db::mutate_apply<Burgers::subcell::GhostVariables>(\n          make_not_null(&box_subcell), 2_st);\n  REQUIRE(retrieved_vars_subcell.size() ==\n          subcell_mesh.number_of_grid_points() + 2);\n  CHECK_ITERABLE_APPROX(get(get<Burgers::Tags::U>(random_vars_subcell)),\n                        DataVector(retrieved_vars_subcell.data(),\n                                   retrieved_vars_subcell.size() - 2));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Subcell/Test_NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Burgers/FiniteDifference/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Burgers {\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Subcell.NeighborPackagedData\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n\n  // 1. create random U on an element and its neighbor elements\n  // 2. send through reconstruction and compute FD fluxes on mortars\n  // 3. feed argument variables of dg_package_data() function to\n  //    the NeighborPackagedData struct and retrieve the packaged data\n  // 4. check if it agrees with the expected value\n\n  using evolved_vars_tags = typename System::variables_tag::tags_list;\n  using fluxes_tags = typename Fluxes::return_tags;\n\n  // Perform test with MC reconstruction & Rusanov riemann solver\n  using ReconstructorUsedForTest = fd::MonotonisedCentral;\n  using BoundaryCorrectionUsedForTest = BoundaryCorrections::Rusanov;\n\n  // create an element and its neighbor elements\n  DirectionMap<1, Neighbors<1>> element_neighbors{};\n  for (size_t i = 0; i < 2; ++i) {\n    element_neighbors[gsl::at(Direction<1>::all_directions(), i)] =\n        Neighbors<1>{{ElementId<1>{i + 1, {}}},\n                     OrientationMap<1>::create_aligned()};\n  }\n  const Element<1> element{ElementId<1>{0, {}}, element_neighbors};\n\n  // generate random U on the dg mesh and project it to subcell mesh\n  const size_t num_dg_pts = 5;\n  const Mesh<1> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  Variables<evolved_vars_tags> volume_vars_dg{dg_mesh.number_of_grid_points()};\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  fill_with_random_values(make_not_null(&volume_vars_dg), make_not_null(&gen),\n                          make_not_null(&dist));\n  Variables<evolved_vars_tags> volume_vars_subcell =\n      evolution::dg::subcell::fd::project(volume_vars_dg, dg_mesh,\n                                          subcell_mesh.extents());\n\n  // generate (random) ghost data from neighbor\n  auto logical_coords_subcell = logical_coordinates(subcell_mesh);\n  const ReconstructorUsedForTest reconstructor{};\n  const auto compute_random_variable = [&gen, &dist](const auto& coords) {\n    Variables<evolved_vars_tags> vars{get<0>(coords).size(), 0.0};\n    fill_with_random_values(make_not_null(&vars), make_not_null(&gen),\n                            make_not_null(&dist));\n    return vars;\n  };\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<1>::type\n      ghost_data = TestHelpers::Burgers::fd::compute_ghost_data(\n          subcell_mesh, logical_coords_subcell, element.neighbors(),\n          reconstructor.ghost_zone_size(), compute_random_variable);\n\n  DirectionMap<1, std::optional<Variables<\n                      tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                 evolution::dg::Tags::NormalCovector<1>>>>>\n      normal_vectors{};\n  for (const auto& direction : Direction<1>::all_directions()) {\n    const auto coordinate_map =\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<1>{});\n    const auto moving_mesh_map =\n        domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<1>{});\n\n    const Mesh<0> face_mesh = dg_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<1>, tnsr::i<DataVector, 1, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, 1, Frame::Inertial> unnormalized_covector{};\n\n    unnormalized_covector.get(0) =\n        coordinate_map.inv_jacobian(face_logical_coords)\n            .get(direction.dimension(), 0);\n\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        evolution::dg::Actions::detail::NormalVector<1>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<System>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, moving_mesh_map);\n  }\n\n  auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Element<1>, domain::Tags::Mesh<1>,\n      evolution::dg::subcell::Tags::Mesh<1>, typename System::variables_tag,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<1>,\n      fd::Tags::Reconstructor, evolution::Tags::BoundaryCorrection,\n      domain::Tags::ElementMap<1, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<1, Frame::Grid,\n                                                  Frame::Inertial>,\n      evolution::dg::subcell::Tags::Coordinates<1, Frame::ElementLogical>,\n      domain::Tags::MeshVelocity<1>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<1>>>(\n      element, dg_mesh, subcell_mesh, volume_vars_dg, ghost_data,\n      std::unique_ptr<fd::Reconstructor>{\n          std::make_unique<ReconstructorUsedForTest>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<BoundaryCorrectionUsedForTest>()},\n      ElementMap<1, Frame::Grid>{\n          ElementId<1>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<1>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<1>{}),\n      logical_coords_subcell,\n      std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>{}, normal_vectors);\n\n  // Compute the packaged data\n  std::vector<DirectionalId<1>> mortars_to_reconstruct_to{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    mortars_to_reconstruct_to.emplace_back(\n        DirectionalId<1>{direction, *neighbors.begin()});\n  }\n  const auto packaged_data =\n      subcell::NeighborPackagedData::apply(box, mortars_to_reconstruct_to);\n\n  // Now for each directions, check that the packaged_data agrees with expected\n  // values\n  using dg_package_field_tags =\n      typename BoundaryCorrectionUsedForTest::dg_package_field_tags;\n  using dg_package_data_argument_tags =\n      tmpl::append<evolved_vars_tags, fluxes_tags>;\n\n  BoundaryCorrectionUsedForTest boundary_corr_for_test{};\n\n  Variables<dg_package_data_argument_tags> vars_on_mortar_face{0};\n  Variables<dg_package_field_tags> expected_fd_packaged_data_on_mortar{0};\n\n  for (const auto& mortar_id : mortars_to_reconstruct_to) {\n    const Direction<1>& direction = mortar_id.direction();\n\n    // Note : 1D mortar has only one face point\n    vars_on_mortar_face.initialize(1);\n    expected_fd_packaged_data_on_mortar.initialize(1);\n\n    // reconstruct U on the mortar\n    dynamic_cast<const ReconstructorUsedForTest&>(reconstructor)\n        .reconstruct_fd_neighbor(make_not_null(&vars_on_mortar_face),\n                                 volume_vars_subcell, element, ghost_data,\n                                 subcell_mesh, direction);\n\n    // compute fluxes\n    Burgers::subcell::compute_fluxes(make_not_null(&vars_on_mortar_face));\n\n    // revert normal vector\n    auto normal_covector = get<evolution::dg::Tags::NormalCovector<1>>(\n        *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<1>>(box).at(\n            mortar_id.direction()));\n    for (auto& t : normal_covector) {\n      t *= -1.0;\n    }\n\n    // Compute the expected packaged data\n    evolution::dg::Actions::detail::dg_package_data<System>(\n        make_not_null(&expected_fd_packaged_data_on_mortar),\n        boundary_corr_for_test, vars_on_mortar_face, normal_covector,\n        {std::nullopt}, box,\n        typename BoundaryCorrectionUsedForTest::dg_package_data_volume_tags{},\n        dg_package_data_argument_tags{});\n\n    const DataVector vector_to_check{\n        expected_fd_packaged_data_on_mortar.data(),\n        expected_fd_packaged_data_on_mortar.size()};\n\n    CHECK_ITERABLE_APPROX(vector_to_check, packaged_data.at(mortar_id));\n  }\n}\n}  // namespace Burgers\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Subcell/Test_SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Subcell.SetInitialRdmpData\",\n                  \"[Unit][Evolution]\") {\n  using Vars = Variables<tmpl::list<Burgers::Tags::U>>;\n\n  const Mesh<1> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const size_t number_of_dg_grid_points{dg_mesh.number_of_grid_points()};\n\n  Vars dg_vars{number_of_dg_grid_points, 1.0};\n\n  // While the code is supposed to be used on the subcells, that doesn't\n  // actually matter.\n  const auto& dg_u = get<Burgers::Tags::U>(dg_vars);\n  using std::max;\n  using std::min;\n  const auto subcell_u = evolution::dg::subcell::fd::project(\n      get(dg_u), dg_mesh, subcell_mesh.extents());\n  evolution::dg::subcell::RdmpTciData rdmp_data{};\n  Burgers::subcell::SetInitialRdmpData::apply(\n      make_not_null(&rdmp_data), get<Burgers::Tags::U>(dg_vars),\n      evolution::dg::subcell::ActiveGrid::Dg, dg_mesh, subcell_mesh);\n  const evolution::dg::subcell::RdmpTciData expected_dg_rdmp_data{\n      {max(max(get(dg_u), max(subcell_u)))},\n      {min(min(get(dg_u)), min(subcell_u))}};\n  CHECK(rdmp_data == expected_dg_rdmp_data);\n\n  Burgers::subcell::SetInitialRdmpData::apply(\n      make_not_null(&rdmp_data), get<Burgers::Tags::U>(dg_vars),\n      evolution::dg::subcell::ActiveGrid::Subcell, dg_mesh, subcell_mesh);\n  const evolution::dg::subcell::RdmpTciData expected_subcell_rdmp_data{\n      {max(get(dg_u))}, {min(get(dg_u))}};\n  CHECK(rdmp_data == expected_subcell_rdmp_data);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Subcell/Test_TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/TciOnDgGrid.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Subcell.TciOnDgGrid\",\n                  \"[Unit][Evolution]\") {\n  enum class TestThis { AllGood, PerssonU, RdmpU };\n\n  for (const auto test_this :\n       {TestThis::AllGood, TestThis::PerssonU, TestThis::RdmpU}) {\n    const Mesh<1> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n    const Mesh<1> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n    const size_t number_of_points{dg_mesh.number_of_grid_points()};\n\n    Scalar<DataVector> u{number_of_points, 1.0};\n\n    if (test_this == TestThis::PerssonU) {\n      // make a troubled cell\n      get(u)[number_of_points / 2] += 1.0;\n    }\n    // Set the RDMP TCI past data.\n    using std::max;\n    using std::min;\n    evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{\n        {max(max(get(u)), max(evolution::dg::subcell::fd::project(\n                              get(u), dg_mesh, subcell_mesh.extents())))},\n        {min(min(get(u)), min(evolution::dg::subcell::fd::project(\n                              get(u), dg_mesh, subcell_mesh.extents())))}};\n\n    const auto expected_rdmp_data = past_rdmp_tci_data;\n\n    if (test_this == TestThis::RdmpU) {\n      // Assumes min is positive, increase it so we fail the TCI\n      past_rdmp_tci_data.min_variables_values[0] *= 1.01;\n    }\n\n    // check the result\n    const double persson_exponent{4.0};\n    const evolution::dg::subcell::SubcellOptions subcell_options{\n        persson_exponent,\n        1_st,\n        1.0e-16,\n        1.0e-4,\n        false,\n        false,\n        evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n        false,\n        std::nullopt,\n        fd::DerivativeOrder::Two,\n        1,\n        1,\n        1};\n\n    const bool element_stays_on_dg = false;\n    const std::tuple<bool, evolution::dg::subcell::RdmpTciData> result =\n        Burgers::subcell::TciOnDgGrid::apply(\n            u, dg_mesh, subcell_mesh, past_rdmp_tci_data, subcell_options,\n            persson_exponent, element_stays_on_dg);\n\n    CHECK(std::get<1>(result) == expected_rdmp_data);\n\n    if (test_this == TestThis::AllGood) {\n      CHECK_FALSE(std::get<0>(result));\n    } else {\n      CHECK(std::get<0>(result));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Subcell/Test_TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/TciOnFdGrid.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Subcell.TciOnFdGrid\",\n                  \"[Unit][Evolution]\") {\n  enum class TestThis { AllGood, PerssonU, RdmpU };\n\n  for (const auto test_this :\n       {TestThis::AllGood, TestThis::PerssonU, TestThis::RdmpU}) {\n    const Mesh<1> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n    const Mesh<1> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n    const size_t number_of_points{subcell_mesh.number_of_grid_points()};\n\n    Scalar<DataVector> u{number_of_points, 1.0};\n\n    if (test_this == TestThis::PerssonU) {\n      // make a troubled cell\n      get(u)[number_of_points / 2] += 1.0;\n    }\n\n    // Set the RDMP TCI past data.\n    using std::max;\n    using std::min;\n    evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{\n        {max(max(get(u)),\n             max(evolution::dg::subcell::fd::reconstruct(\n                 get(u), dg_mesh, subcell_mesh.extents(),\n                 evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)))},\n        {min(\n            min(get(u)),\n            min(evolution::dg::subcell::fd::reconstruct(\n                get(u), dg_mesh, subcell_mesh.extents(),\n                evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)))}};\n\n    const evolution::dg::subcell::RdmpTciData expected_rdmp_tci_data{\n        {max(get(u))}, {min(get(u))}};\n\n    if (test_this == TestThis::RdmpU) {\n      // Assumes min is positive, increase it so we fail the TCI\n      past_rdmp_tci_data.min_variables_values[0] *= 1.01;\n    }\n\n    // check the result\n    const double persson_exponent{4.0};\n    const evolution::dg::subcell::SubcellOptions subcell_options{\n        persson_exponent,\n        1_st,\n        1.0e-16,\n        1.0e-4,\n        false,\n        false,\n        evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n        false,\n        std::nullopt,\n        fd::DerivativeOrder::Two,\n        1,\n        1,\n        1};\n    const std::tuple<bool, evolution::dg::subcell::RdmpTciData> result =\n        Burgers::subcell::TciOnFdGrid::apply(\n            u, dg_mesh, subcell_mesh, past_rdmp_tci_data, subcell_options,\n            persson_exponent, false);\n    CHECK(get<1>(result) == expected_rdmp_tci_data);\n    if (test_this == TestThis::AllGood) {\n      CHECK_FALSE(std::get<0>(result));\n    } else {\n      CHECK(std::get<0>(result));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Subcell/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Burgers/FiniteDifference/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Step.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// These solution tag and metavariables are not strictly required for testing\n// subcell time derivative, but needed for compilation since\n// BoundaryConditionGhostData requires this to be in box.\nstruct DummyAnalyticSolutionTag : db::SimpleTag {\n  using type = Burgers::Solutions::Step;\n};\n\nstruct DummyEvolutionMetaVars {\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled_at_external_boundary = false;\n  };\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Burgers::BoundaryConditions::BoundaryCondition,\n                   Burgers::BoundaryConditions::standard_boundary_conditions>,\n        tmpl::pair<\n            evolution::BoundaryCorrection,\n            Burgers::BoundaryCorrections::standard_boundary_corrections>>;\n  };\n};\n}  // namespace\n\nnamespace Burgers {\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Subcell.TimeDerivative\",\n                  \"[Unit][Evolution]\") {\n  using evolved_vars_tag = typename System::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, evolved_vars_tag>;\n\n  DirectionMap<1, Neighbors<1>> neighbors{};\n  for (size_t i = 0; i < 2; ++i) {\n    neighbors[gsl::at(Direction<1>::all_directions(), i)] = Neighbors<1>{\n        {ElementId<1>{i + 1, {}}}, OrientationMap<1>::create_aligned()};\n  }\n  const Element<1> element{ElementId<1>{0, {}}, neighbors};\n\n  const size_t num_dg_pts = 5;\n  const Mesh<1> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  // Perform test with MC reconstruction & Rusanov riemann solver\n  using ReconstructionForTest = fd::MonotonisedCentral;\n  using BoundaryCorrectionForTest = BoundaryCorrections::Rusanov;\n\n  // Set the testing profile for U.\n  // Here we use\n  //   * U(x)   = x + 1\n  const auto compute_test_solution = [](const auto& coords) {\n    using tag = Tags::U;\n    Variables<tmpl::list<tag>> vars{get<0>(coords).size(), 0.0};\n    get(get<tag>(vars)) += coords.get(0) + 1.0;\n    return vars;\n  };\n  auto logical_coords_subcell = logical_coordinates(subcell_mesh);\n  const auto volume_vars_subcell =\n      compute_test_solution(logical_coords_subcell);\n\n  // set the ghost data from neighbor\n  const ReconstructionForTest reconstructor{};\n  evolution::dg::subcell::Tags::GhostDataForReconstruction<1>::type ghost_data =\n      TestHelpers::Burgers::fd::compute_ghost_data(\n          subcell_mesh, logical_coords_subcell, element.neighbors(),\n          reconstructor.ghost_zone_size(), compute_test_solution);\n\n  // Below are also dummy variables required for compilation due to boundary\n  // condition FD ghost data. Since the element used here for testing has\n  // neighbors in all directions, BoundaryConditionGhostData::apply() is not\n  // actually called so it is okay to leave these variables somewhat poorly\n  // initialized.\n  Domain<1> dummy_domain{};\n  std::optional<tnsr::I<DataVector, 1>> dummy_volume_mesh_velocity{};\n  typename evolution::dg::Tags::NormalCovectorAndMagnitude<1>::type\n      dummy_normal_covector_and_magnitude{};\n  const double dummy_time{0.0};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      dummy_functions_of_time{};\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Element<1>, evolution::dg::subcell::Tags::Mesh<1>,\n          evolved_vars_tag, dt_variables_tag,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<1>,\n          fd::Tags::Reconstructor, evolution::Tags::BoundaryCorrection,\n          domain::Tags::ElementMap<1, Frame::Grid>,\n          domain::CoordinateMaps::Tags::CoordinateMap<1, Frame::Grid,\n                                                      Frame::Inertial>,\n          evolution::dg::subcell::Tags::Coordinates<1, Frame::ElementLogical>,\n          evolution::dg::Tags::MortarData<1>, domain::Tags::Domain<1>,\n          domain::Tags::MeshVelocity<1, Frame::Inertial>,\n          evolution::dg::Tags::NormalCovectorAndMagnitude<1>, ::Tags::Time,\n          domain::Tags::FunctionsOfTimeInitialize, DummyAnalyticSolutionTag,\n          Parallel::Tags::MetavariablesImpl<DummyEvolutionMetaVars>>,\n      db::AddComputeTags<\n          evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGridCompute<\n              domain::Tags::ElementMap<1, Frame::Grid>, 1>>>(\n      element, subcell_mesh, volume_vars_subcell,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      ghost_data,\n      std::unique_ptr<fd::Reconstructor>{\n          std::make_unique<ReconstructionForTest>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<BoundaryCorrectionForTest>()},\n      ElementMap<1, Frame::Grid>{\n          ElementId<1>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<1>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<1>{}),\n      logical_coords_subcell,\n      typename evolution::dg::Tags::MortarData<1>::type{},\n      std::move(dummy_domain), dummy_volume_mesh_velocity,\n      dummy_normal_covector_and_magnitude, dummy_time,\n      clone_unique_ptrs(dummy_functions_of_time),\n      Burgers::Solutions::Step{2.0, 1.0, 0.0}, DummyEvolutionMetaVars{});\n\n  subcell::TimeDerivative::apply(make_not_null(&box));\n\n  const auto& dt_vars = db::get<dt_variables_tag>(box);\n\n  // Analytic time derivative of U for the testing profile\n  //   * dt(U) = -(1+x)\n  const auto compute_test_derivative = [](const auto& coords) {\n    using tag = ::Tags::dt<Tags::U>;\n    Variables<tmpl::list<tag>> dt_expected{get<0>(coords).size(), 0.0};\n    get(get<tag>(dt_expected)) += -1.0 - coords.get(0);\n    return dt_expected;\n  };\n\n  CHECK_ITERABLE_APPROX(get<::Tags::dt<Tags::U>>(dt_vars),\n                        get<::Tags::dt<Tags::U>>(\n                            compute_test_derivative(logical_coords_subcell)));\n}\n}  // namespace Burgers\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Burgers/Characteristics.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.Characteristics\", \"[Unit][Burgers]\") {\n  TestHelpers::db::test_compute_tag<Burgers::Tags::CharacteristicSpeedsCompute>(\n      \"CharacteristicSpeeds\");\n  {\n    const auto box = db::create<\n        db::AddSimpleTags<Burgers::Tags::U,\n                          domain::Tags::UnnormalizedFaceNormal<1>>,\n        db::AddComputeTags<Burgers::Tags::CharacteristicSpeedsCompute>>(\n        Scalar<DataVector>{{{{4.0}}}}, tnsr::i<DataVector, 1>{{{{1.0}}}});\n    CHECK(db::get<Burgers::Tags::CharacteristicSpeeds>(box)[0] == 4.0);\n  }\n  {\n    const auto box = db::create<\n        db::AddSimpleTags<Burgers::Tags::U,\n                          domain::Tags::UnnormalizedFaceNormal<1>>,\n        db::AddComputeTags<Burgers::Tags::CharacteristicSpeedsCompute>>(\n        Scalar<DataVector>{{{{4.0}}}}, tnsr::i<DataVector, 1>{{{{-1.0}}}});\n    CHECK(db::get<Burgers::Tags::CharacteristicSpeeds>(box)[0] == -4.0);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.ComputeLargestCharacteristicSpeed\",\n                  \"[Unit][Burgers]\") {\n  double largest_characteristic_speed =\n      std::numeric_limits<double>::signaling_NaN();\n  Burgers::Tags::ComputeLargestCharacteristicSpeed::function(\n      make_not_null(&largest_characteristic_speed),\n      Scalar<DataVector>{{{{1., 2., 4., 3.}}}});\n  CHECK(largest_characteristic_speed == 4.0);\n  Burgers::Tags::ComputeLargestCharacteristicSpeed::function(\n      make_not_null(&largest_characteristic_speed),\n      Scalar<DataVector>{{{{1., 2., 4., -5.}}}});\n  CHECK(largest_characteristic_speed == 5.0);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Test_Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.Fluxes\", \"[Unit][Burgers]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Burgers\"};\n\n  pypp::check_with_random_values<1>(&Burgers::Fluxes::apply, \"Flux\", {\"flux\"},\n                                    {{{-1.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Burgers.Tags\", \"[Unit][Burgers]\") {\n  TestHelpers::db::test_simple_tag<Burgers::Tags::U>(\"U\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Burgers/Test_TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Burgers/System.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Evolution/Systems/Burgers/TimeDerivativeTerms.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Burgers.TimeDerivativeTerms\", \"[Unit][Burgers]\") {\n  constexpr size_t num_points = 10;\n  const Mesh<1> mesh(num_points, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const auto coords = get<0>(logical_coordinates(mesh));\n\n  // Arbitrary polynomial whose square is exactly representable.\n  const Scalar<DataVector> burgers_u{DataVector{\n      pow<num_points / 2 - 1>(coords) + pow<num_points / 4>(coords) + 5.0}};\n\n  tnsr::I<DataVector, 1, Frame::Inertial> flux(num_points);\n  Scalar<DataVector> dudt{num_points, 0.0};\n  Burgers::TimeDerivativeTerms::apply(&dudt, &flux, burgers_u);\n\n  // dudt should be zero since we have no source terms\n  const Scalar<DataVector> dudt_expected{num_points, 0.0};\n  const tnsr::I<DataVector, 1, Frame::Inertial> flux_expected{\n      DataVector{0.5 * square(get(burgers_u))}};\n\n  CHECK_ITERABLE_APPROX(dudt, dudt_expected);\n  CHECK_ITERABLE_APPROX(flux, flux_expected);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Burgers)\nadd_subdirectory(Cce)\nadd_subdirectory(Ccz4)\nadd_subdirectory(CurvedScalarWave)\nadd_subdirectory(ForceFree)\nadd_subdirectory(GeneralizedHarmonic)\nadd_subdirectory(GrMhd)\nadd_subdirectory(NewtonianEuler)\nadd_subdirectory(RadiationTransport)\nadd_subdirectory(ScalarAdvection)\nadd_subdirectory(ScalarTensor)\nadd_subdirectory(ScalarWave)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_CceActions\")\n\nset(LIBRARY_SOURCES\n  Test_AnalyticBoundaryCommunication.cpp\n  Test_CalculateScriInputs.cpp\n  Test_CharacteristicEvolutionBondiCalculations.cpp\n  Test_InitializeFirstHypersurface.cpp\n  Test_InsertInterpolationScriData.cpp\n  Test_FilterSwshVolumeQuantity.cpp\n  Test_GhBoundaryCommunication.cpp\n  Test_H5BoundaryCommunication.cpp\n  Test_InitializeCharacteristicEvolution.cpp\n  Test_InitializeKleinGordonCharacteristicEvolution.cpp\n  Test_InitializeKleinGordonFirstHypersurface.cpp\n  Test_InitializeKleinGordonWorldtubeBoundary.cpp\n  Test_InitializeWorldtubeBoundary.cpp\n  Test_KleinGordonCceCalculations.cpp\n  Test_KleinGordonH5BoundaryCommunication.cpp\n  Test_Psi0Matching.cpp\n  Test_RequestBoundaryData.cpp\n  Test_RequestKleinGordonBoundaryData.cpp\n  Test_SendGhVarsToCce.cpp\n  Test_ScriObserveInterpolated.cpp\n  Test_TimeManagement.cpp\n  Test_UpdateGauge.cpp\n  Test_WriteScriBondiQuantities.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Cce\n  CceHelpers\n  GeneralRelativitySolutions\n  H5\n  Interpolation\n  InterpolationHelpers\n  Observer\n  SpinWeightedSphericalHarmonics\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/ScriObserveInterpolated.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef compute_News(\n    linear_coefficient,\n    quadratic_coefficient,\n    time,\n    news_coefficient,\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    _6,\n    _7,\n):\n    return news_coefficient * (\n        1.0 + linear_coefficient * time + quadratic_coefficient * time**2\n    )\n\n\ndef compute_EthInertialRetardedTime(\n    linear_coefficient,\n    quadratic_coefficient,\n    time,\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    _6,\n    eth_u_coefficient,\n    _7,\n):\n    return eth_u_coefficient * (\n        1.0 + linear_coefficient * time + quadratic_coefficient * time**2\n    )\n\n\ndef compute_Du_TimeIntegral_ScriPlus_Psi4(\n    linear_coefficient,\n    quadratic_coefficient,\n    time,\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    psi4_coefficient,\n    _6,\n    _7,\n):\n    return psi4_coefficient * (\n        linear_coefficient + 2.0 * quadratic_coefficient * time\n    )\n\n\ndef compute_ScriPlus_Psi3(\n    linear_coefficient,\n    quadratic_coefficient,\n    time,\n    _1,\n    psi3_coefficient,\n    _2,\n    _3,\n    _4,\n    psi4_coefficient,\n    eth_u_coefficient,\n    _5,\n):\n    time_factor = (\n        1.0 + linear_coefficient * time + quadratic_coefficient * time**2\n    )\n    psi4 = psi4_coefficient * (\n        linear_coefficient + 2.0 * quadratic_coefficient * time\n    )\n    psi3 = psi3_coefficient * time_factor\n    eth_u = eth_u_coefficient * time_factor\n    return psi3 + 0.5 * eth_u * psi4\n\n\ndef compute_ScriPlus_Psi2(\n    linear_coefficient,\n    quadratic_coefficient,\n    time,\n    _1,\n    psi3_coefficient,\n    psi2_coefficient,\n    _2,\n    _3,\n    psi4_coefficient,\n    eth_u_coefficient,\n    _4,\n):\n    time_factor = (\n        1.0 + linear_coefficient * time + quadratic_coefficient * time**2\n    )\n    psi4 = psi4_coefficient * (\n        linear_coefficient + 2.0 * quadratic_coefficient * time\n    )\n    psi3 = psi3_coefficient * time_factor\n    psi2 = psi2_coefficient * time_factor\n    eth_u = eth_u_coefficient * time_factor\n    return psi2 + psi3 * eth_u + 0.25 * psi4 * eth_u**2\n\n\ndef compute_ScriPlus_Psi1(\n    linear_coefficient,\n    quadratic_coefficient,\n    time,\n    _1,\n    psi3_coefficient,\n    psi2_coefficient,\n    psi1_coefficient,\n    _2,\n    psi4_coefficient,\n    eth_u_coefficient,\n    _3,\n):\n    time_factor = (\n        1.0 + linear_coefficient * time + quadratic_coefficient * time**2\n    )\n    psi4 = psi4_coefficient * (\n        linear_coefficient + 2.0 * quadratic_coefficient * time\n    )\n    psi3 = psi3_coefficient * time_factor\n    psi2 = psi2_coefficient * time_factor\n    psi1 = psi1_coefficient * time_factor\n    eth_u = eth_u_coefficient * time_factor\n    return (\n        psi1\n        + 1.5 * psi2 * eth_u\n        + 0.75 * psi3 * eth_u**2\n        + 0.125 * psi4 * eth_u**3\n    )\n\n\ndef compute_ScriPlus_Psi0(\n    linear_coefficient,\n    quadratic_coefficient,\n    time,\n    _1,\n    psi3_coefficient,\n    psi2_coefficient,\n    psi1_coefficient,\n    psi0_coefficient,\n    psi4_coefficient,\n    eth_u_coefficient,\n    _2,\n):\n    time_factor = (\n        1.0 + linear_coefficient * time + quadratic_coefficient * time**2\n    )\n    psi4 = psi4_coefficient * (\n        linear_coefficient + 2.0 * quadratic_coefficient * time\n    )\n    psi3 = psi3_coefficient * time_factor\n    psi2 = psi2_coefficient * time_factor\n    psi1 = psi1_coefficient * time_factor\n    psi0 = psi0_coefficient * time_factor\n    eth_u = eth_u_coefficient * time_factor\n    return (\n        psi0\n        + 2.0 * psi1 * eth_u\n        + 0.75 * psi2 * eth_u**2\n        + 0.5 * psi3 * eth_u**3\n        + 0.0625 * psi4 * eth_u**4\n    )\n\n\ndef compute_ScriPlus_Strain(\n    linear_coefficient,\n    quadratic_coefficient,\n    time,\n    _1,\n    _2,\n    _3,\n    _4,\n    _5,\n    _6,\n    _7,\n    strain_coefficient,\n):\n    return strain_coefficient * (\n        1.0 + linear_coefficient * time + quadratic_coefficient * time**2\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_AnalyticBoundaryCommunication.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionScri.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/RequestBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_analytic_worldtube_boundary {\n  using component_being_mocked = AnalyticWorldtubeBoundary<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using initialize_action_list =\n      tmpl::list<Actions::InitializeWorldtubeBoundary<\n                     AnalyticWorldtubeBoundary<Metavariables>>,\n                 Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Actions::InitializeCharacteristicEvolutionScri<\n          typename Metavariables::scri_values_to_observe,\n          typename Metavariables::cce_boundary_component>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::RequestBoundaryData<\n                         AnalyticWorldtubeBoundary<Metavariables>,\n                         mock_characteristic_evolution<Metavariables>>,\n                     Actions::ReceiveWorldtubeData<\n                         Metavariables, typename Metavariables::\n                                            cce_boundary_communication_tags>,\n                     Actions::RequestNextBoundaryData<\n                         AnalyticWorldtubeBoundary<Metavariables>,\n                         mock_characteristic_evolution<Metavariables>>>>>;\n};\n\nstruct test_metavariables {\n  using evolved_swsh_tags = tmpl::list<Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Tags::BondiH>;\n  using cce_step_choosers = tmpl::list<>;\n  using evolved_coordinates_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::InertialRetardedTime>>;\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using cce_boundary_component = AnalyticWorldtubeBoundary<test_metavariables>;\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Tags::BondiR, Tags::DuRDividedByR, Tags::BondiJ,\n                     Tags::Dr<Tags::BondiJ>, Tags::BondiBeta, Tags::BondiQ,\n                     Tags::BondiU, Tags::BondiW, Tags::BondiH>,\n          tmpl::bind<Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Tags::BondiUAtScri, Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using scri_values_to_observe = tmpl::list<>;\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      bondi_hypersurface_step_tags,\n      tmpl::bind<integrand_terms_to_compute_for_bondi_variable, tmpl::_1>>>;\n  using cce_integration_independent_tags = pre_computation_tags;\n  using cce_temporary_equations_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          cce_integrand_tags, tmpl::bind<integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags = tmpl::list<Tags::CauchyAngularCoords>;\n  using cce_scri_tags =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>,\n                 Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>;\n  using ccm_psi0 = tmpl::list<\n      Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n      Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n\n  using component_list =\n      tmpl::list<mock_analytic_worldtube_boundary<test_metavariables>,\n                 mock_characteristic_evolution<test_metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.AnalyticBoundaryCommunication\",\n    \"[Unit][Cce]\") {\n  register_classes_with_charm<Cce::Solutions::RotatingSchwarzschild>();\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  using evolution_component = mock_characteristic_evolution<test_metavariables>;\n  using worldtube_component =\n      mock_analytic_worldtube_boundary<test_metavariables>;\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n  const double extraction_radius = 100.0;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n\n  const double frequency = 0.1 * value_dist(gen);\n  const double target_time = 50.0 * value_dist(gen);\n  const double start_time = target_time;\n  const double target_step_size = 0.01 * value_dist(gen);\n  const double end_time = target_time + 10.0 * target_step_size;\n  const size_t scri_plus_interpolation_order = 2_st;\n\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{\n      tuples::tagged_tuple_from_typelist<\n          Parallel::get_const_global_cache_tags<test_metavariables>>{\n          false, l_max, extraction_radius, end_time, start_time,\n          number_of_radial_points,\n          static_cast<std::unique_ptr<LtsTimeStepper>>(\n              std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  const AnalyticBoundaryDataManager analytic_manager{\n      l_max, extraction_radius,\n      std::make_unique<Cce::Solutions::RotatingSchwarzschild>(extraction_radius,\n                                                              1.0, frequency)};\n  runner.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<evolution_component>(\n      &runner, 0, target_step_size, target_step_size,\n      scri_plus_interpolation_order,\n      serialize_and_deserialize(analytic_manager));\n  // Serialize and deserialize to get around the lack of implicit copy\n  // constructor.\n  ActionTesting::emplace_component<worldtube_component>(\n      &runner, 0, serialize_and_deserialize(analytic_manager));\n\n  // Run the initializations\n  for (size_t i = 0; i < 6; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n  for (size_t i = 0; i < 3; ++i) {\n    ActionTesting::next_action<worldtube_component>(make_not_null(&runner), 0);\n  }\n  runner.set_phase(Parallel::Phase::Evolve);\n\n  // Execute the first request for boundary data\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // Check that the receive action is appropriately not ready\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_terminate<evolution_component>(runner, 0));\n\n  // the response (`BoundaryComputeAndSendToEvolution`)\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n  // then `ReceiveWorldtubeBoundaryData` in the evolution\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      expected_boundary_variables{number_of_angular_points};\n  analytic_manager.populate_hypersurface_boundary_data(\n      make_not_null(&expected_boundary_variables), target_time);\n\n  tmpl::for_each<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      [&expected_boundary_variables, &runner](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& test_lhs =\n            ActionTesting::get_databox_tag<evolution_component, tag>(runner, 0);\n        const auto& test_rhs = get<tag>(expected_boundary_variables);\n        CHECK_ITERABLE_APPROX(test_lhs, test_rhs);\n      });\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_CalculateScriInputs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/CalculateScriInputs.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\n\nnamespace {\n// these will be generated in the test\nusing all_pre_swsh_derivative_scri_input_tags = tmpl::list<\n    Tags::BondiH, Tags::DuRDividedByR, Tags::EthRDividedByR,\n    Tags::Dy<Tags::BondiJ>, Tags::Dy<Tags::BondiW>, Tags::Dy<Tags::BondiQ>,\n    Tags::Dy<Tags::BondiU>, Tags::Dy<Tags::Dy<Tags::BondiBeta>>,\n    Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                     Spectral::Swsh::Tags::EthEthbar>>;\n\n// these need to be computed via `PreSwshDerivatives` and\n// `PrecomputeCceDependencies`, but are not calculated in the action being\n// tested\nusing extra_pre_swsh_derivative_scri_tags =\n    tmpl::list<Tags::Dy<Tags::Dy<Tags::BondiJ>>>;\n\nusing extra_precomputation_scri_tags = tmpl::list<Tags::OneMinusY>;\n\nusing all_real_boundary_scri_input_tags =\n    tmpl::list<Tags::InertialRetardedTime>;\n\nusing transform_buffers_for_scri =\n    tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n        all_swsh_derivative_tags_for_scri,\n        tmpl::bind<Spectral::Swsh::coefficient_buffer_tags_for_derivative_tag,\n                   tmpl::_1>>>>;\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using simple_tags = db::AddSimpleTags<\n      ::Tags::Variables<tmpl::append<all_swsh_derivative_tags_for_scri,\n                                     all_pre_swsh_derivative_tags_for_scri,\n                                     all_pre_swsh_derivative_scri_input_tags,\n                                     extra_pre_swsh_derivative_scri_tags,\n                                     extra_precomputation_scri_tags>>,\n      ::Tags::Variables<transform_buffers_for_scri>,\n      ::Tags::Variables<all_real_boundary_scri_input_tags>,\n      ::Tags::Variables<\n          tmpl::append<all_boundary_pre_swsh_derivative_tags_for_scri,\n                       all_boundary_swsh_derivative_tags_for_scri>>>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  // so that the precomputation mutator can be used in the tmpl::transform\n  template <typename Tag>\n  using local_precompute = PrecomputeCceDependencies<Tags::BoundaryValue, Tag>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<tmpl::transform<\n                         extra_pre_swsh_derivative_scri_tags,\n                         tmpl::bind<::Actions::MutateApply,\n                                    tmpl::bind<PreSwshDerivatives, tmpl::_1>>>,\n                     tmpl::transform<\n                         extra_precomputation_scri_tags,\n                         tmpl::bind<::Actions::MutateApply,\n                                    tmpl::bind<local_precompute, tmpl::_1>>>,\n                     Actions::CalculateScriInputs>>>;\n};\n\nstruct metavariables {\n  using component_list =\n      tmpl::list<mock_characteristic_evolution<metavariables>>;\n};\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.CalculateScriInputs\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  // limited l_max distribution because test depends on an analytic\n  // basis function with factorials.\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(gen);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n  const size_t number_of_radial_points = sdist(gen);\n  CAPTURE(l_max);\n  CAPTURE(number_of_radial_points);\n  using component = mock_characteristic_evolution<metavariables>;\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      tuples::tagged_tuple_from_typelist<\n          Parallel::get_const_global_cache_tags<metavariables>>{\n          l_max, number_of_radial_points}};\n  Variables<tmpl::append<\n      all_swsh_derivative_tags_for_scri, all_pre_swsh_derivative_tags_for_scri,\n      all_pre_swsh_derivative_scri_input_tags,\n      extra_pre_swsh_derivative_scri_tags, extra_precomputation_scri_tags>>\n      component_volume_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n          number_of_radial_points};\n  Variables<all_real_boundary_scri_input_tags> component_real_variables{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  Variables<transform_buffers_for_scri> component_transform_variables{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max) *\n      number_of_radial_points};\n  Variables<tmpl::append<all_boundary_pre_swsh_derivative_tags_for_scri,\n                         all_boundary_swsh_derivative_tags_for_scri>>\n      component_boundary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n  tmpl::for_each<\n      all_pre_swsh_derivative_scri_input_tags>([&component_volume_variables,\n                                                &gen, &coefficient_distribution,\n                                                &number_of_radial_points,\n                                                &l_max](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    SpinWeighted<ComplexModalVector, tag::type::type::spin> generated_modes{\n        number_of_radial_points *\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n    Spectral::Swsh::TestHelpers::generate_swsh_modes<tag::type::type::spin>(\n        make_not_null(&generated_modes.data()), make_not_null(&gen),\n        make_not_null(&coefficient_distribution), number_of_radial_points,\n        l_max);\n    get(get<tag>(component_volume_variables)) =\n        Spectral::Swsh::inverse_swsh_transform(l_max, number_of_radial_points,\n                                               generated_modes);\n    // aggressive filter to make the uniformly generated random modes\n    // somewhat reasonable\n    Spectral::Swsh::filter_swsh_volume_quantity(\n        make_not_null(&get(get<tag>(component_volume_variables))), l_max,\n        l_max / 2, 32.0, 8);\n  });\n\n  tmpl::for_each<all_real_boundary_scri_input_tags>([&component_real_variables,\n                                                     &gen,\n                                                     &coefficient_distribution,\n                                                     &l_max](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    SpinWeighted<ComplexModalVector, 0> generated_modes{\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n    Spectral::Swsh::TestHelpers::generate_swsh_modes<0>(\n        make_not_null(&generated_modes.data()), make_not_null(&gen),\n        make_not_null(&coefficient_distribution), 1, l_max);\n    SpinWeighted<ComplexDataVector, 0> spin_weighted_buffer =\n        Spectral::Swsh::inverse_swsh_transform(l_max, 1, generated_modes);\n    // aggressive filter to make the uniformly generated random modes\n    // somewhat reasonable\n    Spectral::Swsh::filter_swsh_boundary_quantity(\n        make_not_null(&spin_weighted_buffer), l_max, l_max / 2);\n    get(get<tag>(component_real_variables)) = real(spin_weighted_buffer.data());\n  });\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {component_volume_variables, component_transform_variables,\n       component_real_variables, component_boundary_variables});\n  auto expected_box = db::create<\n      tmpl::append<component::simple_tags,\n                   db::AddSimpleTags<Tags::LMax, Tags::NumberOfRadialPoints>>>(\n      component_volume_variables, component_transform_variables,\n      component_real_variables, component_boundary_variables, l_max,\n      number_of_radial_points);\n  runner.set_phase(Parallel::Phase::Initialization);\n\n  // run the initialization to get the values into the databox\n  tmpl::for_each<extra_pre_swsh_derivative_scri_tags>(\n      [&expected_box](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        db::mutate_apply<PreSwshDerivatives<tag>>(make_not_null(&expected_box));\n      });\n\n  tmpl::for_each<extra_precomputation_scri_tags>([&expected_box](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    db::mutate_apply<PrecomputeCceDependencies<Tags::BoundaryValue, tag>>(\n        make_not_null(&expected_box));\n  });\n\n  runner.set_phase(Parallel::Phase::Evolve);\n  // this will execute all of the `MutateApply` actions for the `extra...`\n  // typelists\n  for (size_t i = 0;\n       i < tmpl::size<extra_pre_swsh_derivative_scri_tags>::value +\n               tmpl::size<extra_precomputation_scri_tags>::value + 1;\n       ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n\n  // execute the desired box manipulations directly without calling the actions.\n  // The mutations themselves are tested in other unit tests.\n  tmpl::for_each<tmpl::append<all_pre_swsh_derivative_tags_for_scri,\n                              all_boundary_pre_swsh_derivative_tags_for_scri>>(\n      [&expected_box](auto pre_swsh_derivative_tag_v) {\n        using pre_swsh_derivative_tag =\n            typename decltype(pre_swsh_derivative_tag_v)::type;\n        db::mutate_apply<PreSwshDerivatives<pre_swsh_derivative_tag>>(\n            make_not_null(&expected_box));\n      });\n\n  db::mutate_apply<\n      Spectral::Swsh::AngularDerivatives<all_swsh_derivative_tags_for_scri>>(\n      make_not_null(&expected_box));\n  tmpl::for_each<all_boundary_swsh_derivative_tags_for_scri>(\n      [&expected_box, &l_max](auto swsh_derivative_tag_v) {\n        using swsh_derivative_tag =\n            typename decltype(swsh_derivative_tag_v)::type;\n        db::mutate<swsh_derivative_tag>(\n            [&l_max](const gsl::not_null<typename swsh_derivative_tag::type*>\n                         derivative,\n                     const typename swsh_derivative_tag::derivative_of::type&\n                         argument) {\n              Spectral::Swsh::angular_derivatives<\n                  tmpl::list<typename swsh_derivative_tag::derivative_kind>>(\n                  l_max, 1, make_not_null(&get(*derivative)), get(argument));\n            },\n            make_not_null(&expected_box),\n            db::get<typename swsh_derivative_tag::derivative_of>(expected_box));\n      });\n\n  tmpl::for_each<all_swsh_derivative_tags_for_scri>([&expected_box](\n                                                        auto derivative_tag_v) {\n    using derivative_tag = typename decltype(derivative_tag_v)::type;\n    detail::apply_swsh_jacobian_helper<derivative_tag>(\n        make_not_null(&expected_box),\n        typename ApplySwshJacobianInplace<\n            derivative_tag>::on_demand_argument_tags{});\n  });\n\n  Approx multiple_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  tmpl::for_each<tmpl::append<all_swsh_derivative_tags_for_scri,\n                              all_pre_swsh_derivative_tags_for_scri,\n                              all_boundary_pre_swsh_derivative_tags_for_scri,\n                              all_boundary_swsh_derivative_tags_for_scri>>(\n      [&expected_box, &runner, &multiple_derivative_approx](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        const auto& test_lhs =\n            ActionTesting::get_databox_tag<component, tag>(runner, 0);\n        const auto& test_rhs = db::get<tag>(expected_box);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_lhs, test_rhs,\n                                     multiple_derivative_approx);\n      });\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_CharacteristicEvolutionBondiCalculations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/CharacteristicEvolutionBondiCalculations.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeFirstHypersurface.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ConformalFactor.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass TimeStepId;\nnamespace Tags {\nstruct TimeStepId;\n}  // namespace Tags\n\nnamespace Cce {\n\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_observer_writer {\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using simple_tags = tmpl::list<observers::Tags::H5FileLock>;\n\n  using const_global_cache_tags = tmpl::list<>;\n\n  using metavariables = Metavariables;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct DummyBoundary {};\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Actions::ReceiveWorldtubeData<\n          Metavariables,\n          typename Metavariables::cce_boundary_communication_tags>,\n      Actions::InitializeFirstHypersurface<false, DummyBoundary<Metavariables>>,\n      ::Actions::MutateApply<InitializeGauge>,\n      ::Actions::MutateApply<GaugeUpdateAngularFromCartesian<\n          Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>,\n      ::Actions::MutateApply<GaugeUpdateJacobianFromCoordinates<\n          Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n          Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>,\n      ::Actions::MutateApply<\n          GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>,\n      ::Actions::MutateApply<\n          GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                           Tags::PartiallyFlatGaugeOmega>>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::CalculateIntegrandInputsForTag<Tags::BondiBeta>,\n                     Actions::PrecomputeGlobalCceDependencies>>>;\n};\n\nstruct metavariables {\n  using evolved_swsh_tags = tmpl::list<Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Tags::BondiH>;\n  using cce_step_choosers = tmpl::list<>;\n  using evolved_coordinates_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::InertialRetardedTime>>;\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Tags::BondiR, Tags::DuRDividedByR, Tags::BondiJ,\n                     Tags::Dr<Tags::BondiJ>, Tags::BondiBeta, Tags::BondiQ,\n                     Tags::BondiU, Tags::BondiW, Tags::BondiH>,\n          tmpl::bind<Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Tags::BondiUAtScri, Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>>>;\n\n  using const_global_cache_tags =\n      tmpl::list<Tags::SpecifiedStartTime, Tags::InitializeJ<false>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using scri_values_to_observe = tmpl::list<>;\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      bondi_hypersurface_step_tags,\n      tmpl::bind<integrand_terms_to_compute_for_bondi_variable, tmpl::_1>>>;\n  using cce_integration_independent_tags =\n      tmpl::append<pre_computation_tags, tmpl::list<Tags::DuRDividedByR>>;\n  using cce_temporary_equations_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          cce_integrand_tags, tmpl::bind<integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags = tmpl::list<Tags::CauchyAngularCoords>;\n\n  using cce_scri_tags =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>,\n                 Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>;\n  using ccm_psi0 = tmpl::list<\n      Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n      Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n\n  using component_list =\n      tmpl::list<mock_characteristic_evolution<metavariables>,\n                 mock_observer_writer<metavariables>>;\n};\n\nstruct TestSendToEvolution {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      const db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const TimeStepId& time,\n      const Variables<typename Metavariables::cce_boundary_communication_tags>&\n          data_to_send) {\n    Parallel::receive_data<Cce::ReceiveTags::BoundaryData<\n        typename Metavariables::cce_boundary_communication_tags>>(\n        Parallel::get_parallel_component<\n            mock_characteristic_evolution<metavariables>>(cache),\n        time, data_to_send, true);\n  }\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.CharacteristicBondiCalculations\",\n    \"[Unit][Cce]\") {\n  register_derived_classes_with_charm<InitializeJ::InitializeJ<false>>();\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  using component = mock_characteristic_evolution<metavariables>;\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(gen);\n  const std::array<double, 3> spin{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  const std::array<double, 3> center{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  const gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n  const double frequency = 0.1 * value_dist(gen);\n  const double amplitude = 0.1 * value_dist(gen);\n  const double target_time = 50.0 * value_dist(gen);\n\n  // create the test file, because on initialization the manager will need\n  // to get basic data out of the file\n  const double start_time = target_time;\n  const double target_step_size = 0.01 * value_dist(gen);\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {start_time, std::make_unique<InitializeJ::InverseCubic<false>>(), false,\n       l_max, number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component_and_initialize<\n      mock_observer_writer<metavariables>>(&runner, 0, {Parallel::NodeLock{}});\n  ActionTesting::emplace_component<component>(&runner, 0, target_step_size,\n                                              target_step_size);\n\n  // this should run the initialization\n  for(size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  const size_t libsharp_size =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n  // manually create and place the boundary data in the box:\n  tnsr::ii<ComplexModalVector, 3> spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dt_spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dr_spatial_metric_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dt_shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dr_shift_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dt_lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dr_lapse_coefficients{libsharp_size};\n  TestHelpers::create_fake_time_varying_data(\n      make_not_null(&spatial_metric_coefficients),\n      make_not_null(&dt_spatial_metric_coefficients),\n      make_not_null(&dr_spatial_metric_coefficients),\n      make_not_null(&shift_coefficients), make_not_null(&dt_shift_coefficients),\n      make_not_null(&dr_shift_coefficients), make_not_null(&lapse_coefficients),\n      make_not_null(&dt_lapse_coefficients),\n      make_not_null(&dr_lapse_coefficients), solution, extraction_radius,\n      amplitude, frequency, target_time, l_max, false);\n\n  using boundary_variables_tag = ::Tags::Variables<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>;\n  using pre_swsh_derivative_tag_list =\n      tmpl::append<pre_swsh_derivative_tags_to_compute_for_t<Tags::BondiBeta>,\n                   tmpl::list<Tags::BondiJ, Tags::BondiBeta>>;\n\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  auto boundary_box = db::create<db::AddSimpleTags<\n      boundary_variables_tag, ::Tags::Variables<pre_swsh_derivative_tag_list>,\n      ::Tags::Variables<typename metavariables::cce_gauge_boundary_tags>,\n      typename metavariables::evolved_coordinates_variables_tag,\n      ::Tags::Variables<typename metavariables::cce_angular_coordinate_tags>,\n      ::Tags::Variables<\n          typename metavariables::cce_integration_independent_tags>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Tags::LMax, Tags::NumberOfRadialPoints>>(\n      typename boundary_variables_tag::type{number_of_angular_points},\n      Variables<pre_swsh_derivative_tag_list>{number_of_radial_points *\n                                              number_of_angular_points},\n      Variables<typename metavariables::cce_gauge_boundary_tags>{\n          number_of_angular_points},\n      typename metavariables::evolved_coordinates_variables_tag::type{\n          number_of_angular_points},\n      Variables<typename metavariables::cce_angular_coordinate_tags>{\n          number_of_angular_points},\n      Variables<typename metavariables::cce_integration_independent_tags>{\n          number_of_angular_points * number_of_radial_points},\n      Spectral::Swsh::SwshInterpolator{}, l_max, number_of_radial_points);\n  db::mutate<boundary_variables_tag>(\n      [&spatial_metric_coefficients, &dt_spatial_metric_coefficients,\n       &dr_spatial_metric_coefficients, &shift_coefficients,\n       &dt_shift_coefficients, &dr_shift_coefficients, &lapse_coefficients,\n       &dt_lapse_coefficients, &dr_lapse_coefficients, &extraction_radius](\n          const gsl::not_null<\n              Variables<Tags::characteristic_worldtube_boundary_tags<\n                  Tags::BoundaryValue>>*>\n              boundary_variables) {\n        create_bondi_boundary_data(\n            boundary_variables, spatial_metric_coefficients,\n            dt_spatial_metric_coefficients, dr_spatial_metric_coefficients,\n            shift_coefficients, dt_shift_coefficients, dr_shift_coefficients,\n            lapse_coefficients, dt_lapse_coefficients, dr_lapse_coefficients,\n            extraction_radius, l_max);\n      },\n      make_not_null(&boundary_box));\n\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::simple_action<component, TestSendToEvolution>(\n      make_not_null(&runner), 0,\n      ActionTesting::get_databox_tag<component, ::Tags::TimeStepId>(runner, 0),\n      db::get<::Tags::Variables<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>>(\n          boundary_box));\n  {\n    using tag = Cce::ReceiveTags::BoundaryData<\n        typename metavariables::cce_boundary_communication_tags>;\n    const auto& inbox = ActionTesting::get_inbox_tag<component, tag>(runner, 0);\n\n    const std::string inbox_output = tag::output_inbox(inbox, 1_st);\n    const std::string expected_inbox_output =\n        MakeString{}\n        << std::scientific << std::setprecision(16)\n        << \" CceBoundaryDataInbox:\\n\"\n        << \"  Time: \"\n        << ActionTesting::get_databox_tag<component, ::Tags::TimeStepId>(runner,\n                                                                         0)\n        << \"\\n\";\n    CHECK(inbox_output == expected_inbox_output);\n  }\n\n  // the rest of the initialization routine\n  for (size_t i = 0; i < 8; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n\n  // this should run the computation actions\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n\n  // perform the expected transformations on the `boundary_box`\n  auto node_lock = Parallel::NodeLock{};\n  db::mutate_apply<InitializeJ::InitializeJ<false>::mutate_tags,\n                   InitializeJ::InitializeJ<false>::argument_tags>(\n      InitializeJ::InverseCubic<false>{}, make_not_null(&boundary_box),\n      make_not_null(&node_lock));\n\n  db::mutate_apply<InitializeGauge>(make_not_null(&boundary_box));\n  db::mutate_apply<GaugeUpdateAngularFromCartesian<\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      make_not_null(&boundary_box));\n  db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      make_not_null(&boundary_box));\n  db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n      make_not_null(&boundary_box));\n  db::mutate_apply<\n      GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                       Tags::PartiallyFlatGaugeOmega>>(\n      make_not_null(&boundary_box));\n\n  tmpl::for_each<gauge_adjustments_setup_tags>([&boundary_box](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    db::mutate_apply<GaugeAdjustedBoundaryValue<tag>>(\n        make_not_null(&boundary_box));\n  });\n  mutate_all_precompute_cce_dependencies<Tags::EvolutionGaugeBoundaryValue>(\n      make_not_null(&boundary_box));\n  mutate_all_pre_swsh_derivatives_for_tag<Tags::BondiBeta>(\n      make_not_null(&boundary_box));\n  mutate_all_swsh_derivatives_for_tag<Tags::BondiBeta>(\n      make_not_null(&boundary_box));\n\n  using bondi_calculation_result_tags =\n      tmpl::append<pre_computation_tags,\n                   pre_swsh_derivative_tags_to_compute_for_t<Tags::BondiBeta>>;\n\n  // check the main result tags for correctness\n  tmpl::for_each<bondi_calculation_result_tags>(\n      [&runner, &boundary_box](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& test_lhs =\n            ActionTesting::get_databox_tag<component, tag>(runner, 0);\n        const auto& test_rhs = db::get<tag>(boundary_box);\n        CAPTURE(test_rhs);\n        CHECK_ITERABLE_APPROX(test_lhs, test_rhs);\n      });\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_FilterSwshVolumeQuantity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/FilterSwshVolumeQuantity.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\n\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using simple_tags =\n      db::AddSimpleTags<Tags::LMax,\n                        ::Tags::Variables<tmpl::list<Tags::BondiJ>>>;\n  using compute_tags = db::AddComputeTags<>;\n\n  using initialize_action_list =\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags, compute_tags>>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::FilterSwshVolumeQuantity<Tags::BondiJ>>>>;\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n          phase_dependent_action_list>;\n};\n\nstruct metavariables {\n  using component_list =\n      tmpl::list<mock_characteristic_evolution<metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.FilterSwshVolumeQuantity\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  // limited l_max distribution because test depends on an analytic\n  // basis function with factorials.\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(gen);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n  const size_t number_of_radial_points = 2;\n  CAPTURE(l_max);\n  // Generate data uniform in r with all angular modes\n  SpinWeighted<ComplexModalVector, 2> generated_modes;\n  generated_modes.data() = make_with_random_values<ComplexModalVector>(\n      make_not_null(&gen), make_not_null(&coefficient_distribution),\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max));\n  for (const auto mode : Spectral::Swsh::cached_coefficients_metadata(l_max)) {\n    if (mode.l < 2) {\n      generated_modes.data()[mode.transform_of_real_part_offset] = 0.0;\n      generated_modes.data()[mode.transform_of_imag_part_offset] = 0.0;\n    }\n    if (mode.m == 0) {\n      generated_modes.data()[mode.transform_of_real_part_offset] =\n          real(generated_modes.data()[mode.transform_of_real_part_offset]);\n      generated_modes.data()[mode.transform_of_imag_part_offset] =\n          real(generated_modes.data()[mode.transform_of_imag_part_offset]);\n    }\n  }\n  const auto pre_filter_angular_data =\n      Spectral::Swsh::inverse_swsh_transform(l_max, 1, generated_modes);\n  Scalar<SpinWeighted<ComplexDataVector, 2>> to_filter;\n  get(to_filter) = SpinWeighted<ComplexDataVector, 2>{create_vector_of_n_copies(\n      pre_filter_angular_data.data(), number_of_radial_points)};\n  Variables<tmpl::list<Tags::BondiJ>> component_variables{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n      number_of_radial_points};\n  get<Tags::BondiJ>(component_variables) = to_filter;\n\n  using component = mock_characteristic_evolution<metavariables>;\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      tuples::tagged_tuple_from_typelist<\n          Parallel::get_const_global_cache_tags<metavariables>>{l_max - 2, 36.0,\n                                                                32_st}};\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0, {l_max, std::move(component_variables)});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n\n  Spectral::Swsh::filter_swsh_volume_quantity(make_not_null(&get(to_filter)),\n                                              l_max, l_max - 2, 36.0, 32);\n\n  const auto& filtered_from_component =\n      ActionTesting::get_databox_tag<component, Tags::BondiJ>(runner, 0);\n\n  CHECK_ITERABLE_APPROX(get(filtered_from_component), get(to_filter));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_GhBoundaryCommunication.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionScri.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ReceiveGhWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/RequestBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_gh_worldtube_boundary : GhWorldtubeBoundary<Metavariables> {\n  using component_being_mocked = GhWorldtubeBoundary<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeWorldtubeBoundary<GhWorldtubeBoundary<Metavariables>>>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>>;\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n          phase_dependent_action_list>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Actions::InitializeCharacteristicEvolutionScri<\n          typename Metavariables::scri_values_to_observe,\n          typename Metavariables::cce_boundary_component>>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::RequestBoundaryData<\n                         GhWorldtubeBoundary<Metavariables>,\n                         mock_characteristic_evolution<Metavariables>>,\n                     Actions::ReceiveWorldtubeData<\n                         Metavariables, typename Metavariables::\n                                            cce_boundary_communication_tags>,\n                     Actions::RequestNextBoundaryData<\n                         GhWorldtubeBoundary<Metavariables>,\n                         mock_characteristic_evolution<Metavariables>>>>>;\n};\n\nstruct test_metavariables {\n  using evolved_swsh_tags = tmpl::list<Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Tags::BondiH>;\n  using cce_step_choosers = tmpl::list<>;\n  using evolved_coordinates_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::InertialRetardedTime>>;\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using cce_boundary_component = GhWorldtubeBoundary<test_metavariables>;\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Tags::BondiR, Tags::DuRDividedByR, Tags::BondiJ,\n                     Tags::Dr<Tags::BondiJ>, Tags::BondiBeta, Tags::BondiQ,\n                     Tags::BondiU, Tags::BondiW, Tags::BondiH>,\n          tmpl::bind<Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Tags::BondiUAtScri, Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using scri_values_to_observe = tmpl::list<>;\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      bondi_hypersurface_step_tags,\n      tmpl::bind<integrand_terms_to_compute_for_bondi_variable, tmpl::_1>>>;\n  using cce_integration_independent_tags = pre_computation_tags;\n  using cce_temporary_equations_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          cce_integrand_tags, tmpl::bind<integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags = tmpl::list<Tags::CauchyAngularCoords>;\n  using cce_scri_tags =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>,\n                 Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>;\n  using ccm_psi0 = tmpl::list<\n        Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n        Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n\n  using component_list =\n      tmpl::list<mock_gh_worldtube_boundary<test_metavariables>,\n                 mock_characteristic_evolution<test_metavariables>>;\n\n  static constexpr bool evolve_ccm = false;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.GhBoundaryCommunication\",\n                  \"[Unit][Cce]\") {\n  using evolution_component = mock_characteristic_evolution<test_metavariables>;\n  using worldtube_component = mock_gh_worldtube_boundary<test_metavariables>;\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n  const double extraction_radius = 100.0;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n\n  const double frequency = 0.1 * value_dist(gen);\n  const double amplitude = 0.1 * value_dist(gen);\n  const double target_time = 50.0 * value_dist(gen);\n\n  const double start_time = target_time;\n  const double target_step_size = 0.01 * value_dist(gen);\n  const double end_time = target_time + 10.0 * target_step_size;\n\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{\n      tuples::tagged_tuple_from_typelist<\n          Parallel::get_const_global_cache_tags<test_metavariables>>{\n          false, l_max, extraction_radius, end_time, start_time,\n          number_of_radial_points,\n          static_cast<std::unique_ptr<LtsTimeStepper>>(\n              std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  // first prepare the input for the modal version\n  const double mass = value_dist(gen);\n  const std::array<double, 3> spin{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  const std::array<double, 3> center{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  const gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  // create the test file, because on initialization the manager will need\n  // to get basic data out of the file\n  const size_t scri_plus_interpolation_order = 3;\n\n  runner.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<evolution_component>(\n      &runner, 0, target_step_size, target_step_size,\n      scri_plus_interpolation_order);\n  ActionTesting::emplace_component<worldtube_component>(\n      &runner, 0,\n      InterfaceManagers::GhLocalTimeStepping{\n          std::make_unique<intrp::LinearSpanInterpolator>()},\n      InterfaceManagers::GhLockstep{});\n\n  // this should run the initializations\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<worldtube_component>(make_not_null(&runner), 0);\n  }\n  runner.set_phase(Parallel::Phase::Evolve);\n\n  // the first request\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // check that the 'block' appropriately reports that it's not ready:\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_terminate<evolution_component>(runner, 0));\n\n  // send the current timestep data to the boundary component\n  tnsr::aa<DataVector, 3> spacetime_metric{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  tnsr::iaa<DataVector, 3> phi{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  tnsr::aa<DataVector, 3> pi{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  TestHelpers::create_fake_time_varying_gh_nodal_data(\n      make_not_null(&spacetime_metric), make_not_null(&phi), make_not_null(&pi),\n      solution, extraction_radius, amplitude, frequency, target_time, l_max);\n  ActionTesting::simple_action<\n      worldtube_component,\n      Actions::ReceiveGhWorldtubeData<evolution_component, true>>(\n      make_not_null(&runner), 0,\n      TimeStepId{true, -2,\n                 Time{{target_time, target_time + target_step_size}, {0, 1}}},\n      spacetime_metric, phi, pi);\n\n  // the first response (`BoundaryComputeAndSendToEvolution`)\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n  // then `ReceiveWorldtubeBoundaryData` in the evolution\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      expected_boundary_variables{number_of_angular_points};\n  create_bondi_boundary_data(make_not_null(&expected_boundary_variables), phi,\n                             pi, spacetime_metric, extraction_radius, l_max);\n\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  tmpl::for_each<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      [&expected_boundary_variables, &runner,\n       &angular_derivative_approx](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& test_lhs =\n            ActionTesting::get_databox_tag<evolution_component, tag>(runner, 0);\n        const auto& test_rhs = get<tag>(expected_boundary_variables);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_lhs, test_rhs,\n                                     angular_derivative_approx);\n      });\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_H5BoundaryCommunication.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/RequestBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_observer_writer {\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using simple_tags = tmpl::list<observers::Tags::H5FileLock>;\n\n  using const_global_cache_tags = tmpl::list<>;\n\n  using metavariables = Metavariables;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_h5_worldtube_boundary {\n  using component_being_mocked = H5WorldtubeBoundary<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeWorldtubeBoundary<H5WorldtubeBoundary<Metavariables>>>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n          phase_dependent_action_list>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::RequestBoundaryData<\n                         H5WorldtubeBoundary<Metavariables>,\n                         mock_characteristic_evolution<Metavariables>>,\n                     Actions::ReceiveWorldtubeData<\n                         Metavariables, typename Metavariables::\n                                            cce_boundary_communication_tags>,\n                     Actions::RequestNextBoundaryData<\n                         H5WorldtubeBoundary<Metavariables>,\n                         mock_characteristic_evolution<Metavariables>>>>>;\n};\n\nstruct test_metavariables {\n  using evolved_swsh_tags = tmpl::list<Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Tags::BondiH>;\n  using cce_step_choosers = tmpl::list<>;\n  using evolved_coordinates_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::InertialRetardedTime>>;\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using cce_boundary_component = H5WorldtubeBoundary<test_metavariables>;\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Tags::BondiR, Tags::DuRDividedByR, Tags::BondiJ,\n                     Tags::Dr<Tags::BondiJ>, Tags::BondiBeta, Tags::BondiQ,\n                     Tags::BondiU, Tags::BondiW, Tags::BondiH>,\n          tmpl::bind<Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Tags::BondiUAtScri, Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using scri_values_to_observe = tmpl::list<>;\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      bondi_hypersurface_step_tags,\n      tmpl::bind<integrand_terms_to_compute_for_bondi_variable, tmpl::_1>>>;\n  using cce_integration_independent_tags = pre_computation_tags;\n  using cce_temporary_equations_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          cce_integrand_tags, tmpl::bind<integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags = tmpl::list<Tags::CauchyAngularCoords>;\n  using cce_scri_tags =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>,\n                 Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>;\n  using ccm_psi0 = tmpl::list<\n      Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n      Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n\n  using component_list =\n      tmpl::list<mock_h5_worldtube_boundary<test_metavariables>,\n                 mock_characteristic_evolution<test_metavariables>,\n                 mock_observer_writer<test_metavariables>>;\n\n  static constexpr bool evolve_ccm = false;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.H5BoundaryCommunication\",\n                  \"[Unit][Cce]\") {\n  using evolution_component = mock_characteristic_evolution<test_metavariables>;\n  using worldtube_component = mock_h5_worldtube_boundary<test_metavariables>;\n  using writer_component = mock_observer_writer<test_metavariables>;\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n\n  const std::string filename = \"H5BoundaryCommunicationTestCceR0100.h5\";\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(gen);\n  const std::array<double, 3> spin{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  const std::array<double, 3> center{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n  const double frequency = 0.1 * value_dist(gen);\n  const double amplitude = 0.1 * value_dist(gen);\n  const double target_time = 50.0 * value_dist(gen);\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  TestHelpers::write_test_file(solution, filename, target_time,\n                               extraction_radius, frequency, amplitude, l_max);\n\n  const double start_time = target_time;\n  const double target_step_size = 0.01 * value_dist(gen);\n  const double end_time = std::numeric_limits<double>::quiet_NaN();\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{\n      {false, l_max, extraction_radius,\n       Tags::EndTimeFromFile::create_from_options(end_time, filename, false),\n       start_time, number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  // create the test file, because on initialization the manager will need\n  // to get basic data out of the file\n  const size_t buffer_size = 5;\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component_and_initialize<writer_component>(\n      &runner, 0, {Parallel::NodeLock{}});\n  ActionTesting::emplace_component<evolution_component>(\n      &runner, 0, target_step_size, target_step_size);\n  ActionTesting::emplace_component<worldtube_component>(\n      &runner, 0,\n      Tags::H5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          std::optional<double>{}));\n\n  // this should run the initializations\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<worldtube_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n\n  // the first request\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // check that the 'block' appropriately reports that it's not ready:\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_terminate<evolution_component>(runner, 0));\n\n  // the first response (`BoundaryComputeAndSendToEvolution`)\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n\n  // generate the 'expected' boundary variables and compare them to the\n  // variables obtained from the actions.\n  const size_t libsharp_size =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  tnsr::ii<ComplexModalVector, 3> spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dt_spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dr_spatial_metric_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dt_shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dr_shift_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dt_lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dr_lapse_coefficients{libsharp_size};\n  TestHelpers::create_fake_time_varying_data(\n      make_not_null(&spatial_metric_coefficients),\n      make_not_null(&dt_spatial_metric_coefficients),\n      make_not_null(&dr_spatial_metric_coefficients),\n      make_not_null(&shift_coefficients), make_not_null(&dt_shift_coefficients),\n      make_not_null(&dr_shift_coefficients), make_not_null(&lapse_coefficients),\n      make_not_null(&dt_lapse_coefficients),\n      make_not_null(&dr_lapse_coefficients), solution, extraction_radius,\n      amplitude, frequency, target_time, l_max, false);\n\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      expected_boundary_variables{number_of_angular_points};\n  create_bondi_boundary_data(\n      make_not_null(&expected_boundary_variables), spatial_metric_coefficients,\n      dt_spatial_metric_coefficients, dr_spatial_metric_coefficients,\n      shift_coefficients, dt_shift_coefficients, dr_shift_coefficients,\n      lapse_coefficients, dt_lapse_coefficients, dr_lapse_coefficients,\n      extraction_radius, l_max);\n\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  tmpl::for_each<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      [&expected_boundary_variables, &runner,\n       &angular_derivative_approx](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& test_lhs =\n            ActionTesting::get_databox_tag<evolution_component, tag>(runner, 0);\n        const auto& test_rhs = get<tag>(expected_boundary_variables);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_lhs, test_rhs,\n                                     angular_derivative_approx);\n      });\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_InitializeCharacteristicEvolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionScri.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/Actions/CharacteristicInitialization.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\nstruct TimeStep;\nstruct TimeStepId;\n}  // namespace Tags\n\nnamespace Cce {\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Actions::InitializeCharacteristicEvolutionScri<\n          typename Metavariables::scri_values_to_observe, NoSuchType>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n};\n\nstruct metavariables {\n  using evolved_swsh_tags = tmpl::list<Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Tags::BondiH>;\n  using cce_step_choosers = tmpl::list<>;\n  using evolved_coordinates_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::InertialRetardedTime>>;\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Tags::BondiR, Tags::DuRDividedByR, Tags::BondiJ,\n                     Tags::Dr<Tags::BondiJ>, Tags::BondiBeta, Tags::BondiQ,\n                     Tags::BondiU, Tags::BondiW, Tags::BondiH>,\n          tmpl::bind<Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Tags::BondiUAtScri, Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Cce::Tags::CauchyGaugeC, Cce::Tags::CauchyGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Cce::Tags::CauchyGaugeOmega,\n      Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::CauchyGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>>>;\n\n  using const_global_cache_tags = tmpl::list<Tags::SpecifiedStartTime>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using scri_values_to_observe = tmpl::list<>;\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      bondi_hypersurface_step_tags,\n      tmpl::bind<integrand_terms_to_compute_for_bondi_variable, tmpl::_1>>>;\n  using cce_integration_independent_tags =\n      tmpl::append<pre_computation_tags, tmpl::list<Tags::DuRDividedByR>>;\n  using cce_temporary_equations_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          cce_integrand_tags, tmpl::bind<integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags =\n      tmpl::list<Tags::CauchyAngularCoords, Tags::PartiallyFlatAngularCoords>;\n  using cce_scri_tags =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>,\n                 Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>;\n  using ccm_psi0 = tmpl::list<\n        Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n        Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n\n  using component_list =\n      tmpl::list<mock_characteristic_evolution<metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.InitializeCharacteristicEvolution\",\n    \"[Unit][Cce]\") {\n  using component = mock_characteristic_evolution<metavariables>;\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n\n  const std::string filename =\n      \"InitializeCharacteristicEvolutionTest_CceR0100.h5\";\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(gen);\n  const std::array<double, 3> spin{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  const std::array<double, 3> center{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n  const double frequency = 0.1 * value_dist(gen);\n  const double amplitude = 0.1 * value_dist(gen);\n  const double target_time = 50.0 * value_dist(gen);\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  TestHelpers::write_test_file(solution, filename, target_time,\n                               extraction_radius, frequency, amplitude, l_max);\n\n  // create the test file, because on initialization the manager will need\n  // to get basic data out of the file\n  const double start_time = value_dist(gen);\n  const double target_step_size = 0.01 * value_dist(gen);\n  const size_t scri_plus_interpolation_order = 3;\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {start_time, false, l_max, number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<component>(\n      &runner, 0, target_step_size * 0.75, target_step_size,\n      scri_plus_interpolation_order);\n\n  // this should run the initialization\n  for (size_t i = 0; i < 6; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n\n  TestHelpers::check_characteristic_initialization<component>(\n      runner, start_time, target_step_size, l_max, number_of_radial_points);\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_InitializeFirstHypersurface.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/UpdateGauge.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ConformalFactor.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nnamespace {\nusing swsh_boundary_tags_to_generate =\n    tmpl::list<Tags::BoundaryValue<Tags::BondiJ>,\n               Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>,\n               Tags::BoundaryValue<Tags::BondiBeta>,\n               Tags::BoundaryValue<Tags::BondiR>>;\n\nusing real_cauchy_boundary_tags_to_compute =\n    tmpl::list<Tags::CauchyCartesianCoords, Tags::CauchyAngularCoords,\n               Tags::InertialRetardedTime>;\n\nusing swsh_cauchy_boundary_tags_to_compute =\n    tmpl::list<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD>;\n\nusing real_inertial_boundary_tags_to_compute =\n    tmpl::list<Tags::PartiallyFlatCartesianCoords,\n               Tags::PartiallyFlatAngularCoords>;\n\nusing swsh_inertial_boundary_tags_to_compute =\n    tmpl::list<Tags::CauchyGaugeC, Tags::CauchyGaugeD>;\n\nusing swsh_volume_tags_to_compute = tmpl::list<Tags::BondiJ>;\n\ntemplate <typename Metavariables>\nstruct dummy_boundary {};\n\ntemplate <typename Metavariables>\nstruct mock_observer_writer {\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using simple_tags = tmpl::list<observers::Tags::H5FileLock>;\n\n  using const_global_cache_tags = tmpl::list<>;\n\n  using metavariables = Metavariables;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using simple_tags = db::AddSimpleTags<\n      ::Tags::Variables<tmpl::append<real_cauchy_boundary_tags_to_compute,\n                                     real_inertial_boundary_tags_to_compute>>,\n      ::Tags::Variables<tmpl::append<swsh_boundary_tags_to_generate,\n                                     swsh_cauchy_boundary_tags_to_compute,\n                                     swsh_inertial_boundary_tags_to_compute>>,\n      ::Tags::Variables<swsh_volume_tags_to_compute>, ::Tags::TimeStepId>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tags =\n      tmpl::list<Tags::InitializeJ<metavariables::evolve_ccm>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<\n              Actions::InitializeFirstHypersurface<\n                  metavariables::evolve_ccm, dummy_boundary<Metavariables>>,\n              ::Actions::MutateApply<GaugeUpdateAngularFromCartesian<\n                  Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>,\n              ::Actions::MutateApply<GaugeUpdateJacobianFromCoordinates<\n                  Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                  Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>,\n              std::conditional_t<\n                  Metavariables::evolve_ccm,\n                  tmpl::list<\n                      ::Actions::MutateApply<GaugeUpdateAngularFromCartesian<\n                          Tags::PartiallyFlatAngularCoords,\n                          Tags::PartiallyFlatCartesianCoords>>,\n                      ::Actions::MutateApply<GaugeUpdateJacobianFromCoordinates<\n                          Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n                          Tags::PartiallyFlatAngularCoords,\n                          Tags::PartiallyFlatCartesianCoords>>>,\n                  tmpl::list<>>>>>;\n};\n\ntemplate <bool EvolveCcm>\nstruct metavariables {\n  using component_list =\n      tmpl::list<mock_observer_writer<metavariables>,\n                 mock_characteristic_evolution<metavariables>>;\n\n  static constexpr bool evolve_ccm = EvolveCcm;\n};\n\ntemplate <bool EvolveCcm>\nvoid test_InitializeFirstHypersurface() {\n  register_derived_classes_with_charm<InitializeJ::InitializeJ<EvolveCcm>>();\n\n  MAKE_GENERATOR(gen);\n  // limited l_max distribution because test depends on an analytic\n  // basis function with factorials.\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(gen);\n  const size_t number_of_radial_points = sdist(gen);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n  CAPTURE(l_max);\n\n  using component = mock_characteristic_evolution<metavariables<EvolveCcm>>;\n  ActionTesting::MockRuntimeSystem<metavariables<EvolveCcm>> runner{\n      {std::make_unique<InitializeJ::InverseCubic<EvolveCcm>>(), l_max,\n       number_of_radial_points}};\n\n  Variables<tmpl::append<real_cauchy_boundary_tags_to_compute,\n                         real_inertial_boundary_tags_to_compute>>\n      real_variables{Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  Variables<tmpl::append<swsh_boundary_tags_to_generate,\n                         swsh_cauchy_boundary_tags_to_compute,\n                         swsh_inertial_boundary_tags_to_compute>>\n      swsh_variables{Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  Variables<swsh_volume_tags_to_compute> swsh_volume_variables{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n      number_of_radial_points};\n  TimeStepId time_step_id{true, 0, Time{Slab{1.0, 2.0}, {0, 1}}};\n\n  tmpl::for_each<swsh_boundary_tags_to_generate>([&swsh_variables, &gen,\n                                                  &coefficient_distribution,\n                                                  &l_max](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    SpinWeighted<ComplexModalVector, tag::type::type::spin> generated_modes{\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n    Spectral::Swsh::TestHelpers::generate_swsh_modes<tag::type::type::spin>(\n        make_not_null(&generated_modes.data()), make_not_null(&gen),\n        make_not_null(&coefficient_distribution), 1, l_max);\n    Spectral::Swsh::inverse_swsh_transform(\n        l_max, 1, make_not_null(&get(get<tag>(swsh_variables))),\n        generated_modes);\n    // aggressive filter to make the uniformly generated random modes\n    // somewhat reasonable\n    Spectral::Swsh::filter_swsh_volume_quantity(\n        make_not_null(&get(get<tag>(swsh_variables))), l_max, l_max / 2, 32.0,\n        8);\n  });\n\n  ActionTesting::emplace_component_and_initialize<\n      mock_observer_writer<metavariables<EvolveCcm>>>(&runner, 0,\n                                                      {Parallel::NodeLock{}});\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {real_variables, swsh_variables, swsh_volume_variables, time_step_id});\n\n  auto expected_box =\n      db::create<tmpl::push_back<typename component::simple_tags, Tags::LMax,\n                                 Tags::NumberOfRadialPoints>>(\n          std::move(real_variables), std::move(swsh_variables),\n          std::move(swsh_volume_variables), time_step_id, l_max,\n          number_of_radial_points);\n\n  runner.set_phase(Parallel::Phase::Testing);\n  // apply the `InitializeFirstHypersurface` action\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  if constexpr (EvolveCcm) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n\n  // apply the corresponding mutators to the `expected_box`\n  auto node_lock = Parallel::NodeLock{};\n  db::mutate_apply<\n      typename Cce::InitializeJ::InitializeJ<EvolveCcm>::mutate_tags,\n      typename Cce::InitializeJ::InitializeJ<EvolveCcm>::argument_tags>(\n      Cce::InitializeJ::InverseCubic<EvolveCcm>{}, make_not_null(&expected_box),\n      make_not_null(&node_lock));\n  db::mutate_apply<GaugeUpdateAngularFromCartesian<\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      make_not_null(&expected_box));\n  db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      make_not_null(&expected_box));\n  if constexpr (EvolveCcm) {\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&expected_box));\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&expected_box));\n  }\n  db::mutate_apply<InitializeScriPlusValue<Tags::InertialRetardedTime>>(\n      make_not_null(&expected_box), 1.0);\n\n  tmpl::for_each<tmpl::append<real_cauchy_boundary_tags_to_compute,\n                              swsh_cauchy_boundary_tags_to_compute,\n                              swsh_volume_tags_to_compute>>(\n      [&runner, &expected_box](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        CAPTURE(db::tag_name<tag>());\n        const auto& test_lhs =\n            ActionTesting::get_databox_tag<component, tag>(runner, 0);\n        const auto& test_rhs = db::get<tag>(expected_box);\n        CHECK_ITERABLE_APPROX(test_lhs, test_rhs);\n      });\n\n  if constexpr (EvolveCcm) {\n    tmpl::for_each<tmpl::append<real_inertial_boundary_tags_to_compute,\n                                swsh_inertial_boundary_tags_to_compute>>(\n        [&runner, &expected_box](auto tag_v) {\n          using tag = typename decltype(tag_v)::type;\n          CAPTURE(db::tag_name<tag>());\n          const auto& test_lhs =\n              ActionTesting::get_databox_tag<component, tag>(runner, 0);\n          const auto& test_rhs = db::get<tag>(expected_box);\n          CHECK_ITERABLE_APPROX(test_lhs, test_rhs);\n        });\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.InitializeFirstHypersurface\",\n    \"[Unit][Cce]\") {\n  // Evolve inertial coordinates\n  test_InitializeFirstHypersurface<true>();\n  // Do not evolve inertial coordinates\n  test_InitializeFirstHypersurface<false>();\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_InitializeKleinGordonCharacteristicEvolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Executables/Cce/CharacteristicExtractBase.hpp\"\n#include \"Evolution/Systems/Cce/Components/KleinGordonCharacteristicEvolution.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/Actions/CharacteristicInitialization.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/KleinGordonBoundaryTestHelpers.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace Cce {\nnamespace {\n\ntemplate <typename Metavariables>\nstruct mock_klein_gordon_characteristic_evolution {\n  using component_being_mocked =\n      KleinGordonCharacteristicEvolution<Metavariables>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeKleinGordonVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Actions::InitializeCharacteristicEvolutionScri<\n          typename Metavariables::scri_values_to_observe, NoSuchType>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n};\n\nstruct metavariables : CharacteristicExtractDefaults<false> {\n  using cce_base = CharacteristicExtractDefaults<false>;\n  using evolved_swsh_tags = tmpl::append<cce_base::evolved_swsh_tags,\n                                         tmpl::list<Cce::Tags::KleinGordonPsi>>;\n  using evolved_swsh_dt_tags =\n      tmpl::append<cce_base::evolved_swsh_dt_tags,\n                   tmpl::list<Cce::Tags::KleinGordonPi>>;\n  using cce_step_choosers = tmpl::list<>;\n  using scri_values_to_observe = tmpl::list<>;\n  using klein_gordon_boundary_communication_tags =\n      Cce::Tags::klein_gordon_worldtube_boundary_tags;\n  using klein_gordon_gauge_boundary_tags = tmpl::list<\n      Cce::Tags::EvolutionGaugeBoundaryValue<Cce::Tags::KleinGordonPsi>,\n      Cce::Tags::EvolutionGaugeBoundaryValue<Cce::Tags::KleinGordonPi>>;\n\n  using klein_gordon_scri_tags =\n      tmpl::list<Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPsi>,\n                 Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPi>>;\n\n  using klein_gordon_pre_swsh_derivative_tags =\n      tmpl::list<Cce::Tags::Dy<Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>>,\n                 Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>>;\n  using klein_gordon_swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::EthEth>,\n      Spectral::Swsh::Tags::Derivative<Cce::Tags::KleinGordonPsi,\n                                       Spectral::Swsh::Tags::EthEthbar>>;\n  using klein_gordon_transform_buffer_tags = tmpl::list<\n      Spectral::Swsh::Tags::SwshTransform<Cce::Tags::KleinGordonPsi>,\n      Spectral::Swsh::Tags::SwshTransform<\n          Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::KleinGordonPsi, Spectral::Swsh::Tags::Eth>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::KleinGordonPsi, Spectral::Swsh::Tags::Ethbar>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>, Spectral::Swsh::Tags::Eth>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::Dy<Cce::Tags::KleinGordonPsi>,\n          Spectral::Swsh::Tags::Ethbar>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEth>>,\n      Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n          Cce::Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEthbar>>>;\n\n  using klein_gordon_source_tags = tmpl::flatten<\n      tmpl::transform<Cce::bondi_hypersurface_step_tags,\n                      tmpl::bind<Cce::Tags::KleinGordonSource, tmpl::_1>>>;\n\n  using klein_gordon_cce_integrand_tags =\n      tmpl::list<Cce::Tags::PoleOfIntegrand<Cce::Tags::KleinGordonPi>,\n                 Cce::Tags::RegularIntegrand<Cce::Tags::KleinGordonPi>>;\n\n  using const_global_cache_tags = tmpl::list<Tags::SpecifiedStartTime>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using component_list =\n      tmpl::list<mock_klein_gordon_characteristic_evolution<metavariables>>;\n};\n\n// This function tests the `initialize_action_list` of the\n// `KleinGordonCharacteristicEvolution` component, which initializes the data\n// storage for all tags of the component.\n//\n// The function begins by generating and storing some scalar and tensor data\n// into an HDF5 file named `filename` using `write_scalar_tensor_test_file`.\n// Subsequently, it initializes a mocked evolution component\n// `mock_klein_gordon_characteristic_evolution<Metavariables>` defined above\n// that has the same `initialize_action_list` as\n// `KleinGordonCharacteristicEvolution`. The function then goes through the\n// action list and finally checks whether the tags are in the expected state.\ntemplate <typename Generator>\nvoid test_klein_gordon_cce_initialization(const gsl::not_null<Generator*> gen) {\n  using component = mock_klein_gordon_characteristic_evolution<metavariables>;\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n\n  const std::string filename =\n      \"InitializeKleinGordonCharacteristicEvolutionTest_CceR0100.h5\";\n\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  TestHelpers::write_scalar_tensor_test_file(solution, filename, target_time,\n                                             extraction_radius, frequency,\n                                             amplitude, l_max);\n\n  const double start_time = value_dist(*gen);\n  const double target_step_size = 0.01 * value_dist(*gen);\n  const size_t scri_plus_interpolation_order = 3;\n\n  // tests start here\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {start_time, false, l_max, number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<component>(\n      &runner, 0, target_step_size * 0.75, target_step_size,\n      scri_plus_interpolation_order);\n\n  // go through the action list\n  for (size_t i = 0; i < 7; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n\n  // The tensor part:\n  //\n  // check that the tags are in the expected state (here we just make\n  // sure it has the right size - it shouldn't have been written to yet).\n  // This part is the same as `Test_InitializeCharacteristicEvolution.cpp`\n  TestHelpers::check_characteristic_initialization<component>(\n      runner, start_time, target_step_size, l_max, number_of_radial_points);\n\n  // then we repeat the tests for the scalar (Klein-Gordon) part\n  const auto& kg_boundary_communication_tags = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<\n          typename metavariables::klein_gordon_boundary_communication_tags>>(\n      runner, 0);\n  CHECK(kg_boundary_communication_tags.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const auto& kg_gauge_boundary_tags = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<\n          typename metavariables::klein_gordon_gauge_boundary_tags>>(\n      runner, 0);\n  CHECK(kg_gauge_boundary_tags.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const auto& kg_scri_tags = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<typename metavariables::klein_gordon_scri_tags>>(runner,\n                                                                         0);\n  CHECK(kg_scri_tags.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const auto& kg_pre_swsh_derivative_tags = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<\n          typename metavariables::klein_gordon_pre_swsh_derivative_tags>>(\n      runner, 0);\n  CHECK(kg_pre_swsh_derivative_tags.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n            number_of_radial_points);\n\n  const auto& kg_swsh_derivative_tags = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<\n          typename metavariables::klein_gordon_swsh_derivative_tags>>(runner,\n                                                                      0);\n  CHECK(kg_swsh_derivative_tags.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n            number_of_radial_points);\n\n  const auto& kg_transform_buffer_tags = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<\n          typename metavariables::klein_gordon_transform_buffer_tags>>(runner,\n                                                                       0);\n  CHECK(kg_transform_buffer_tags.number_of_grid_points() ==\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max) *\n            number_of_radial_points);\n\n  const auto& kg_source_tags = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<typename metavariables::klein_gordon_source_tags>>(\n      runner, 0);\n  CHECK(kg_source_tags.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n            number_of_radial_points);\n\n  const auto& kg_integrand_tags = ActionTesting::get_databox_tag<\n      component, ::Tags::Variables<\n                     typename metavariables::klein_gordon_cce_integrand_tags>>(\n      runner, 0);\n  CHECK(kg_integrand_tags.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n            number_of_radial_points);\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.InitializeKleinGordonCce\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_klein_gordon_cce_initialization(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_InitializeKleinGordonFirstHypersurface.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeKleinGordonFirstHypersurface.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_kg_characteristic_evolution {\n  using simple_tags = db::AddSimpleTags<\n      ::Tags::Variables<tmpl::list<Tags::BoundaryValue<Tags::KleinGordonPsi>>>,\n      ::Tags::Variables<tmpl::list<Tags::KleinGordonPsi>>, ::Tags::TimeStepId>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::InitializeKleinGordonFirstHypersurface>>>;\n};\n\nstruct metavariables {\n  using component_list =\n      tmpl::list<mock_kg_characteristic_evolution<metavariables>>;\n};\n\n// This function tests that the action\n// `Actions::InitializeKleinGordonFirstHypersurface` can be correctly invoked.\n//\n// The action is invoked by a mock run time system, then the function checks\n// that the constructed hypersurface data (`computed_psi`) agree with what we\n// expect.\ntemplate <typename Generator>\nvoid test_klein_gordon_first_hypersurface(const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(*gen);\n  const size_t number_of_radial_points = sdist(*gen);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n\n  using component = mock_kg_characteristic_evolution<metavariables>;\n\n  // generate boundary data for the Klein-Gordon variable\n  // (`kg_boundary_variable`)\n  SpinWeighted<ComplexModalVector, 0> generated_kg_boundary_modes{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n  SpinWeighted<ComplexDataVector, 0> generated_kg_boundary_data{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n  Spectral::Swsh::TestHelpers::generate_swsh_modes<0>(\n      make_not_null(&generated_kg_boundary_modes.data()), gen,\n      make_not_null(&coefficient_distribution), 1, l_max);\n  Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, make_not_null(&generated_kg_boundary_data),\n      generated_kg_boundary_modes);\n  // aggressive filter to make the uniformly generated random modes\n  // somewhat reasonable\n  Spectral::Swsh::filter_swsh_boundary_quantity(\n      make_not_null(&generated_kg_boundary_data), l_max, l_max / 2);\n\n  Variables<tmpl::list<Tags::BoundaryValue<Tags::KleinGordonPsi>>>\n      kg_boundary_variable{real(generated_kg_boundary_data.data())};\n\n  const size_t boundary_size =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  Variables<tmpl::list<Tags::KleinGordonPsi>> kg_variable_to_compute{\n      boundary_size * number_of_radial_points};\n\n  // required by the action\n  TimeStepId time_step_id{true, 0, Time{Slab{1.0, 2.0}, {0, 1}}};\n\n  // tests start here\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {l_max, number_of_radial_points}};\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0, {kg_boundary_variable, kg_variable_to_compute, time_step_id});\n\n  runner.set_phase(Parallel::Phase::Evolve);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  auto computed_psi =\n      ActionTesting::get_databox_tag<component, Tags::KleinGordonPsi>(runner,\n                                                                      0);\n\n  const DataVector one_minus_y_collocation =\n      1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n                number_of_radial_points);\n\n  // compare with the expected values\n  for (size_t i = 0; i < number_of_radial_points; i++) {\n    ComplexDataVector angular_view_of_computed_psi{\n        get(computed_psi).data().data() + boundary_size * i, boundary_size};\n\n    auto expected_psi = real(generated_kg_boundary_data.data()) *\n                        one_minus_y_collocation[i] / 2.;\n    CHECK(angular_view_of_computed_psi == expected_psi);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.InitializeKleinGordonFirstHypersurface\",\n    \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_klein_gordon_first_hypersurface(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_InitializeKleinGordonWorldtubeBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/Actions/WorldtubeBoundaryMocking.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/KleinGordonBoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\n\nnamespace {\n\nstruct KleinGordonH5Metavariables {\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using klein_gordon_boundary_communication_tags =\n      Tags::klein_gordon_worldtube_boundary_tags;\n  using component_list = tmpl::list<\n      mock_klein_gordon_h5_worldtube_boundary<KleinGordonH5Metavariables>>;\n\n  static constexpr bool evolve_ccm = false;\n};\n\n// This function tests the action\n// `InitializeWorldtubeBoundary<KleinGordonH5WorldtubeBoundary<Metavariables>>`.\n// The action is responsible for initializing the tags\n// `Metavariables::cce_boundary_communication_tags` and\n// `Metavariables::klein_gordon_boundary_communication_tags` for the boundary\n// component. The initialization process involves reading tensor and scalar data\n// separately from an HDF5 file. This is handled by two managers:\n// `Tags::H5WorldtubeBoundaryDataManager` and\n// `Tags::KleinGordonH5WorldtubeBoundaryDataManager`.\n//\n// The function begins by generating and storing some scalar and tensor data\n// into an HDF5 file named `filename` using `write_scalar_tensor_test_file`.\n// Subsequently, it initializes a mocked worldtube boundary component that\n// contains two optional tags: `Tags::H5WorldtubeBoundaryDataManager` and\n// `Tags::KleinGordonH5WorldtubeBoundaryDataManager`.\n// The function then invokes the action under examination within the\n// initialize-action list to initialize the specified tags. Finally, it tests\n// whether the tags are in the expected state.\ntemplate <typename Generator>\nvoid test_klein_gordon_h5_initialization(const gsl::not_null<Generator*> gen) {\n  using component =\n      mock_klein_gordon_h5_worldtube_boundary<KleinGordonH5Metavariables>;\n  const size_t l_max = 8;\n  const size_t end_time = 100.0;\n  const size_t start_time = 0.0;\n\n  const size_t buffer_size = 8;\n  const std::string filename =\n      \"InitializeKleinGordonWorldtubeBoundaryTest_CceR0100.h5\";\n\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n  TestHelpers::write_scalar_tensor_test_file(solution, filename, target_time,\n                                             extraction_radius, frequency,\n                                             amplitude, l_max);\n\n  // tests start here\n  ActionTesting::MockRuntimeSystem<KleinGordonH5Metavariables> runner{\n      tuples::tagged_tuple_from_typelist<\n          Parallel::get_const_global_cache_tags<KleinGordonH5Metavariables>>{\n          l_max, extraction_radius, end_time, start_time}};\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<component>(\n      &runner, 0,\n      Tags::H5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          std::optional<double>{}),\n      Tags::KleinGordonH5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          std::optional<double>{}));\n\n  // go through the initialization list\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n\n  // The tensor part:\n  //\n  // check that the tensor data manager copied out of the databox has the\n  // correct properties\n  const auto& tensor_data_manager =\n      ActionTesting::get_databox_tag<component,\n                                     Tags::H5WorldtubeBoundaryDataManager>(\n          runner, 0);\n  CHECK(tensor_data_manager.get_l_max() == l_max);\n  const auto tensor_time_span = tensor_data_manager.get_time_span();\n  CHECK(tensor_time_span.first == 0);\n  CHECK(tensor_time_span.second == 0);\n\n  // check that the tensor Variables is in the expected state (here we just make\n  // sure it has the right size - it shouldn't have been written to yet)\n  const auto& tensor_variables = ActionTesting::get_databox_tag<\n      component, ::Tags::Variables<typename KleinGordonH5Metavariables::\n                                       cce_boundary_communication_tags>>(runner,\n                                                                         0);\n  CHECK(\n      get(get<Tags::BoundaryValue<Tags::BondiBeta>>(tensor_variables)).size() ==\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  // then we repeat the tests for the scalar (Klein-Gordon) part\n  const auto& scalar_data_manager = ActionTesting::get_databox_tag<\n      component, Tags::KleinGordonH5WorldtubeBoundaryDataManager>(runner, 0);\n  CHECK(scalar_data_manager.get_l_max() == l_max);\n  const auto scalar_time_span = scalar_data_manager.get_time_span();\n  CHECK(scalar_time_span.first == 0);\n  CHECK(scalar_time_span.second == 0);\n\n  // check that the scalar Variables is in the expected state (here we just make\n  // sure it has the right size - it shouldn't have been written to yet)\n  const auto& scalar_variables = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<typename KleinGordonH5Metavariables::\n                            klein_gordon_boundary_communication_tags>>(runner,\n                                                                       0);\n  CHECK(get(get<Tags::BoundaryValue<Tags::KleinGordonPsi>>(scalar_variables))\n            .size() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.InitializeKleinGordonWorldtubeBoundary\",\n    \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_klein_gordon_h5_initialization(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_InitializeWorldtubeBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/Actions/WorldtubeBoundaryMocking.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/TimeSteppers/AdamsMoultonPc.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct mock_analytic_worldtube_boundary {\n  using initialize_action_list =\n      tmpl::list<Actions::InitializeWorldtubeBoundary<\n                     AnalyticWorldtubeBoundary<Metavariables>>,\n                 Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n  using const_global_cache_tags = tmpl::list<\n      Tags::CceEvolutionPrefix<::Tags::ConcreteTimeStepper<LtsTimeStepper>>>;\n};\n\nstruct H5Metavariables {\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using component_list =\n      tmpl::list<mock_h5_worldtube_boundary<H5Metavariables>>;\n  static constexpr bool evolve_ccm = false;\n};\n\nstruct GhMetavariables {\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using component_list =\n      tmpl::list<mock_gh_worldtube_boundary<GhMetavariables>>;\n  static constexpr bool evolve_ccm = false;\n};\n\nstruct AnalyticMetavariables {\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using component_list =\n      tmpl::list<mock_analytic_worldtube_boundary<AnalyticMetavariables>>;\n};\n\ntemplate <typename Generator>\nvoid test_h5_initialization(const gsl::not_null<Generator*> gen) {\n  using component = mock_h5_worldtube_boundary<H5Metavariables>;\n  const size_t l_max = 8;\n  const size_t end_time = 100.0;\n  const size_t start_time = 0.0;\n  const double extraction_radius = 100.0;\n  ActionTesting::MockRuntimeSystem<H5Metavariables> runner{\n      tuples::tagged_tuple_from_typelist<\n          Parallel::get_const_global_cache_tags<H5Metavariables>>{\n          l_max, extraction_radius, end_time, start_time}};\n\n  const size_t buffer_size = 8;\n  const std::string filename = \"InitializeWorldtubeBoundaryTest_CceR0100.h5\";\n\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n  TestHelpers::write_test_file(solution, filename, target_time,\n                               extraction_radius, frequency, amplitude, l_max);\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<component>(\n      &runner, 0,\n      Tags::H5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          std::optional<double>{}));\n\n  // this should run the initialization\n  for (size_t i = 0; i < 3; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n  // check that the h5 data manager copied out of the databox has the correct\n  // properties that we can examine without running the other actions\n  const auto& data_manager =\n      ActionTesting::get_databox_tag<component,\n                                     Tags::H5WorldtubeBoundaryDataManager>(\n          runner, 0);\n  CHECK(data_manager.get_l_max() == l_max);\n  const auto time_span = data_manager.get_time_span();\n  CHECK(time_span.first == 0);\n  CHECK(time_span.second == 0);\n\n  // check that the Variables is in the expected state (here we just make sure\n  // it has the right size - it shouldn't have been written to yet)\n  const auto& variables = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<\n          typename H5Metavariables::cce_boundary_communication_tags>>(runner,\n                                                                      0);\n\n  CHECK(get(get<Tags::BoundaryValue<Tags::BondiBeta>>(variables)).size() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n\nvoid test_gh_initialization() {\n  using component = mock_gh_worldtube_boundary<GhMetavariables>;\n  const size_t l_max = 8;\n  const double extraction_radius = 100.0;\n  ActionTesting::MockRuntimeSystem<GhMetavariables> runner{\n      tuples::tagged_tuple_from_typelist<\n          Parallel::get_const_global_cache_tags<GhMetavariables>>{\n          l_max, extraction_radius, std::numeric_limits<double>::infinity(),\n          0.0}};\n\n  runner.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<component>(\n      &runner, 0,\n      InterfaceManagers::GhLocalTimeStepping{\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u)},\n      InterfaceManagers::GhLockstep{});\n\n  // this should run the initialization\n  for (size_t i = 0; i < 3; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  runner.set_phase(Parallel::Phase::Evolve);\n\n  // check that the Variables is in the expected state (here we just make sure\n  // it has the right size - it shouldn't have been written to yet)\n  const auto& variables = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<\n          typename GhMetavariables::cce_boundary_communication_tags>>(runner,\n                                                                      0);\n  CHECK(get(get<Tags::BoundaryValue<Tags::BondiBeta>>(variables)).size() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n}\n\ntemplate <typename SolutionType>\nvoid test_analytic_initialization() {\n  using component = mock_analytic_worldtube_boundary<AnalyticMetavariables>;\n  const size_t l_max = 8;\n  const double extraction_radius = 20.0;\n  register_classes_with_charm<TimeSteppers::AdamsMoultonPc<false>>();\n  ActionTesting::MockRuntimeSystem<AnalyticMetavariables> runner{\n      {static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsMoultonPc<false>>(3)),\n       l_max, extraction_radius, 100.0, 0.0}};\n\n  runner.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<component>(\n      &runner, 0,\n      AnalyticBoundaryDataManager{12_st, extraction_radius,\n                                  std::make_unique<SolutionType>()});\n  // this should run the initialization\n  for (size_t i = 0; i < 3; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  runner.set_phase(Parallel::Phase::Evolve);\n\n  // check that the Variables is in the expected state (here we just make sure\n  // it has the right size - it shouldn't have been written to yet)\n  const auto& variables = ActionTesting::get_databox_tag<\n      component,\n      ::Tags::Variables<\n          typename AnalyticMetavariables::cce_boundary_communication_tags>>(\n      runner, 0);\n  CHECK(get(get<Tags::BoundaryValue<Tags::BondiBeta>>(variables)).size() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.InitializeWorldtubeBoundary\",\n    \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_h5_initialization(make_not_null(&gen));\n  test_gh_initialization();\n  test_analytic_initialization<Solutions::RotatingSchwarzschild>();\n  CHECK_THROWS_WITH(\n      (test_analytic_initialization<Solutions::RobinsonTrautman>()),\n      Catch::Matchers::ContainsSubstring(\n          \"Do not use RobinsonTrautman analytic solution with\"));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_InsertInterpolationScriData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionScri.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InsertInterpolationScriData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/RequestBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce {\nnamespace {\n\nstd::unordered_map<std::string, std::vector<double>> written_modes;\nstruct MockWriteReductionDataRow {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(const db::DataBox<DbTagsList>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& /*file_legend*/,\n                    const std::tuple<std::vector<double>>& data_row) {\n    written_modes[subfile_name] = std::get<0>(data_row);\n  }\n};\n\nstruct RotatingSchwarzschildWithNoninertialNews\n    : public Cce::Solutions::RotatingSchwarzschild {\n  using Cce::Solutions::RotatingSchwarzschild::RotatingSchwarzschild;\n  // ignore warning for unused | operator\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(RotatingSchwarzschildWithNoninertialNews);\n#pragma GCC diagnostic pop\n  bool use_noninertial_news() const override { return true; }\n\n  std::unique_ptr<Cce::Solutions::WorldtubeData> get_clone() const override {\n    return std::make_unique<RotatingSchwarzschildWithNoninertialNews>(*this);\n  }\n  void pup(PUP::er& p) override {\n    Cce::Solutions::RotatingSchwarzschild::pup(p);\n  }\n};\n\nPUP::able::PUP_ID RotatingSchwarzschildWithNoninertialNews::my_PUP_ID = 0;\n\nstruct SetRandomBoundaryValues {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    MAKE_GENERATOR(gen);\n    UniformCustomDistribution<double> value_dist{0.1, 0.5};\n    tmpl::for_each<typename Metavariables::cce_scri_tags>(\n        [&gen, &value_dist, &box](auto tag_v) {\n          using tag = typename decltype(tag_v)::type;\n          db::mutate<tag>(\n              [&gen, &value_dist](\n                  const gsl::not_null<typename tag::type*> scri_value) {\n                fill_with_random_values(make_not_null(&get(*scri_value).data()),\n                                        make_not_null(&gen),\n                                        make_not_null(&value_dist));\n              },\n              make_not_null(&box));\n        });\n\n    db::mutate<Tags::InertialRetardedTime>(\n        [&gen,\n         &value_dist](const gsl::not_null<Scalar<DataVector>*> scri_value) {\n          fill_with_random_values(make_not_null(&get(*scri_value)),\n                                  make_not_null(&gen),\n                                  make_not_null(&value_dist));\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockObserver {\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_threaded_actions =\n      tmpl::list<observers::ThreadedActions::WriteReductionDataRow>;\n  using with_these_threaded_actions = tmpl::list<MockWriteReductionDataRow>;\n\n  using const_global_cache_tags = tmpl::list<Tags::ObservationLMax>;\n  using initialize_action_list =\n      tmpl::list<observers::Actions::InitializeWriter<Metavariables>>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockCharacteristicEvolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Actions::InitializeCharacteristicEvolutionScri<\n          typename Metavariables::scri_values_to_observe,\n          typename Metavariables::cce_boundary_component>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<\n              SetRandomBoundaryValues,\n              tmpl::transform<\n                  typename Metavariables::scri_values_to_observe,\n                  tmpl::bind<Actions::InsertInterpolationScriData, tmpl::_1,\n                             tmpl::pin<typename Metavariables::\n                                           cce_boundary_component>>>>>>;\n};\n\nstruct test_metavariables {\n  using evolved_swsh_tags = tmpl::list<Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Tags::BondiH>;\n  using cce_step_choosers = tmpl::list<>;\n  using evolved_coordinates_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::InertialRetardedTime>>;\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Tags::BondiR, Tags::DuRDividedByR, Tags::BondiJ,\n                     Tags::Dr<Tags::BondiJ>, Tags::BondiBeta, Tags::BondiQ,\n                     Tags::BondiU, Tags::BondiW, Tags::BondiH>,\n          tmpl::bind<Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Tags::BondiUAtScri, Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>>>;\n\n  using const_global_cache_tags = tmpl::list<Tags::SpecifiedStartTime>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      bondi_hypersurface_step_tags,\n      tmpl::bind<integrand_terms_to_compute_for_bondi_variable, tmpl::_1>>>;\n  using cce_integration_independent_tags =\n      tmpl::append<pre_computation_tags, tmpl::list<Tags::DuRDividedByR>>;\n  using cce_temporary_equations_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          cce_integrand_tags, tmpl::bind<integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags = tmpl::list<Tags::CauchyAngularCoords>;\n  using cce_scri_tags =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>,\n                 Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>;\n\n  using scri_values_to_observe = tmpl::list<\n      Cce::Tags::News,\n      ::Tags::Multiplies<Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                         Cce::Tags::ScriPlus<Cce::Tags::Psi2>>,\n      Cce::Tags::Du<\n          Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>>,\n      ::Tags::Multiplies<Cce::Tags::Du<Cce::Tags::TimeIntegral<\n                             Cce::Tags::ScriPlus<Cce::Tags::Psi4>>>,\n                         Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>>;\n\n  using observed_reduction_data_tags = tmpl::list<>;\n  using cce_boundary_component =\n      Cce::AnalyticWorldtubeBoundary<test_metavariables>;\n  using ccm_psi0 = tmpl::list<\n        Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n        Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n\n  using component_list =\n      tmpl::list<MockCharacteristicEvolution<test_metavariables>,\n                 MockObserver<test_metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.InsertInterpolationScriData\",\n    \"[Unit][Cce]\") {\n  register_classes_with_charm<Cce::Solutions::RotatingSchwarzschild>();\n  register_classes_with_charm<RotatingSchwarzschildWithNoninertialNews>();\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  using evolution_component = MockCharacteristicEvolution<test_metavariables>;\n  using observation_component = MockObserver<test_metavariables>;\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n  const size_t scri_output_density = 5;\n\n  const double start_time = value_dist(gen);\n  const double target_step_size = 0.01 * value_dist(gen);\n  const size_t buffer_size = 5;\n  const double extraction_radius = 100.0;\n\n  const RotatingSchwarzschildWithNoninertialNews analytic_solution{\n      extraction_radius, 1.0, 0.05};\n  const AnalyticBoundaryDataManager analytic_manager{\n      l_max, extraction_radius, analytic_solution.get_clone()};\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{\n      {start_time, false, l_max, l_max, number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3)),\n       scri_output_density, true}};\n  runner.set_phase(Parallel::Phase::Initialization);\n  // Serialize and deserialize to get around the lack of implicit copy\n  // constructor.\n  ActionTesting::emplace_component<evolution_component>(\n      &runner, 0, target_step_size, target_step_size, buffer_size,\n      serialize_and_deserialize(analytic_manager));\n  ActionTesting::emplace_component<MockObserver<test_metavariables>>(&runner,\n                                                                     0);\n  // the initialization actions\n  for (size_t i = 0; i < 6; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n  runner.set_phase(Parallel::Phase::Evolve);\n  // five steps to create then put the data in each of the interpolation\n  // queues\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n\n  // check each of the interpolation queues to make sure that they've received\n  // the data\n  const auto& standard_interpolator = ActionTesting::get_databox_tag<\n      evolution_component,\n      Tags::InterpolationManager<ComplexDataVector, Tags::News>>(runner, 0);\n  CHECK(standard_interpolator.number_of_data_points() == 1);\n  CHECK(standard_interpolator.number_of_target_times() == 5);\n\n  const auto& du_interpolator = ActionTesting::get_databox_tag<\n      evolution_component,\n      Tags::InterpolationManager<ComplexDataVector,\n                                 Cce::Tags::Du<Cce::Tags::TimeIntegral<\n                                     Cce::Tags::ScriPlus<Cce::Tags::Psi4>>>>>(\n      runner, 0);\n  CHECK(du_interpolator.number_of_data_points() == 1);\n  CHECK(du_interpolator.number_of_target_times() == 5);\n\n  const auto& multiplies_interpolator = ActionTesting::get_databox_tag<\n      evolution_component,\n      Tags::InterpolationManager<\n          ComplexDataVector,\n          ::Tags::Multiplies<Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                             Cce::Tags::ScriPlus<Cce::Tags::Psi2>>>>(runner, 0);\n  CHECK(multiplies_interpolator.number_of_data_points() == 1);\n  CHECK(multiplies_interpolator.number_of_target_times() == 5);\n\n  const auto& multiplies_du_interpolator = ActionTesting::get_databox_tag<\n      evolution_component,\n      Tags::InterpolationManager<\n          ComplexDataVector,\n          ::Tags::Multiplies<Cce::Tags::Du<Cce::Tags::TimeIntegral<\n                                 Cce::Tags::ScriPlus<Cce::Tags::Psi4>>>,\n                             Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>>>(\n      runner, 0);\n  CHECK(multiplies_du_interpolator.number_of_data_points() == 1);\n  CHECK(multiplies_du_interpolator.number_of_target_times() == 5);\n\n  // check the output news against the news provided by the analytic solution\n  ActionTesting::invoke_queued_threaded_action<observation_component>(\n      make_not_null(&runner), 0);\n  ActionTesting::invoke_queued_threaded_action<observation_component>(\n      make_not_null(&runner), 0);\n  const ComplexModalVector analytic_news_modes =\n      Spectral::Swsh::libsharp_to_goldberg_modes(\n          Spectral::Swsh::swsh_transform(\n              l_max, 1,\n              get(get<Tags::News>(analytic_solution.variables(\n                  l_max, start_time, tmpl::list<Tags::News>{})))),\n          l_max)\n          .data();\n  CHECK(written_modes[\"/Cce/News_Noninertial\"].size() ==\n        2 * square(l_max + 1) + 1);\n  CHECK(written_modes[\"/Cce/News_Noninertial\"][0] == start_time);\n  CHECK(written_modes[\"/Cce/News_Noninertial_expected\"][0] == start_time);\n  for (size_t i = 0; i < square(l_max + 1); ++i) {\n    CHECK(written_modes[\"/Cce/News_Noninertial_expected\"][2 * i + 1] ==\n          real(analytic_news_modes[i]));\n    CHECK(written_modes[\"/Cce/News_Noninertial_expected\"][2 * i + 2] ==\n          imag(analytic_news_modes[i]));\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_KleinGordonCceCalculations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/Actions/CharacteristicEvolutionBondiCalculations.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeKleinGordonFirstHypersurface.hpp\"\n#include \"Evolution/Systems/Cce/KleinGordonSource.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\nnamespace {\n\nusing real_volume_tags_to_generate = tmpl::list<Tags::OneMinusY>;\n\nusing swsh_volume_tags_to_generate =\n    tmpl::list<Tags::Exp2Beta, Tags::BondiR, Tags::BondiK, Tags::EthRDividedByR,\n               Tags::EthEthRDividedByR, Tags::EthEthbarRDividedByR,\n               Tags::BondiJ, Tags::Dy<Tags::KleinGordonPsi>,\n               Tags::KleinGordonPsi, Tags::Dy<Tags::Dy<Tags::KleinGordonPsi>>,\n               Tags::BondiU, Tags::Dy<Tags::BondiU>, Tags::Dy<Tags::BondiW>,\n               Tags::DuRDividedByR, Tags::BondiW,\n               Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                Spectral::Swsh::Tags::Ethbar>,\n               Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                Spectral::Swsh::Tags::Ethbar>,\n               Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                Spectral::Swsh::Tags::Eth>,\n               Spectral::Swsh::Tags::Derivative<\n                   ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n                   Spectral::Swsh::Tags::Eth>>;\n\nusing swsh_boundary_tags_to_generate =\n    tmpl::list<Tags::BoundaryValue<Tags::KleinGordonPi>, Tags::BondiUAtScri>;\n\nusing swsh_volume_tags_to_compute =\n    tmpl::list<Tags::KleinGordonSource<Tags::BondiBeta>,\n               Tags::KleinGordonSource<Tags::BondiQ>,\n               Tags::KleinGordonSource<Tags::BondiU>,\n               Tags::KleinGordonSource<Tags::BondiW>,\n               Tags::KleinGordonSource<Tags::BondiH>,\n               Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::KleinGordonPsi>,\n                                                Spectral::Swsh::Tags::Ethbar>,\n               Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::KleinGordonPsi>,\n                                                Spectral::Swsh::Tags::Eth>,\n               Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                Spectral::Swsh::Tags::Ethbar>,\n               Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                Spectral::Swsh::Tags::Eth>,\n               Spectral::Swsh::Tags::Derivative<Tags::KleinGordonPsi,\n                                                Spectral::Swsh::Tags::EthEth>,\n               Spectral::Swsh::Tags::Derivative<\n                   Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEthbar>,\n               Tags::PoleOfIntegrand<Tags::KleinGordonPi>,\n               Tags::RegularIntegrand<Tags::KleinGordonPi>>;\n\nusing swsh_boundary_tags_to_compute =\n    tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::KleinGordonPi>>;\n\nusing swsh_transform_tags_to_compute = tmpl::list<\n    Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n        Tags::Dy<Tags::KleinGordonPsi>, Spectral::Swsh::Tags::Eth>>,\n    Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n        Tags::Dy<Tags::KleinGordonPsi>, Spectral::Swsh::Tags::Ethbar>>,\n    Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n        Tags::KleinGordonPsi, Spectral::Swsh::Tags::Eth>>,\n    Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n        Tags::KleinGordonPsi, Spectral::Swsh::Tags::Ethbar>>,\n    Spectral::Swsh::Tags::SwshTransform<Tags::Dy<Tags::KleinGordonPsi>>,\n    Spectral::Swsh::Tags::SwshTransform<Tags::KleinGordonPsi>,\n    Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n        Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEth>>,\n    Spectral::Swsh::Tags::SwshTransform<Spectral::Swsh::Tags::Derivative<\n        Tags::KleinGordonPsi, Spectral::Swsh::Tags::EthEthbar>>>;\n\ntemplate <typename Metavariables>\nstruct mock_kg_characteristic_evolution {\n  using simple_tags = db::AddSimpleTags<\n      ::Tags::Variables<tmpl::append<real_volume_tags_to_generate,\n                                     swsh_volume_tags_to_generate,\n                                     swsh_volume_tags_to_compute>>,\n      ::Tags::Variables<tmpl::append<swsh_boundary_tags_to_generate,\n                                     swsh_boundary_tags_to_compute>>,\n      ::Tags::Variables<swsh_transform_tags_to_compute>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<\n              Actions::CalculateIntegrandInputsForTag<Tags::KleinGordonPi>,\n              tmpl::transform<\n                  bondi_hypersurface_step_tags,\n                  tmpl::bind<::Actions::MutateApply,\n                             tmpl::bind<ComputeKleinGordonSource, tmpl::_1>>>,\n              ::Actions::MutateApply<\n                  GaugeAdjustedBoundaryValue<Tags::KleinGordonPi>>,\n              tmpl::transform<\n                  integrand_terms_to_compute_for_bondi_variable<\n                      Tags::KleinGordonPi>,\n                  tmpl::bind<::Actions::MutateApply,\n                             tmpl::bind<ComputeBondiIntegrand, tmpl::_1>>>>>>;\n\n  using const_global_cache_tags =\n      tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>;\n};\n\nstruct metavariables {\n  using component_list =\n      tmpl::list<mock_kg_characteristic_evolution<metavariables>>;\n};\n\n// This unit test is to validate the automatic calling chain of Klein-Gordon Cce\n// calculations, including the action\n// `CalculateIntegrandInputsForTag<Tags::KleinGordonPi>`, the mutators\n// `ComputeKleinGordonSource`,\n// `GaugeAdjustedBoundaryValue<Tags::KleinGordonPi>`,\n// `ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::KleinGordonPi>>`, and\n// `ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::KleinGordonPi>>`. The test\n// involves\n// (a) Fills a bunch of variables with random numbers (filtered so that there is\n// no aliasing in highest modes).\n// (b) Puts those variables in two places: the MockRuntimeSystem runner and a\n// databox called expected_box.\n// (c) Calls next_action a few times (which fills the results in the databox of\n// MockRuntimeSystem runner).\n// (d) Calls all the individual calculations by hand, filling the results in\n// expected_box.\n// (e) Compares MockRuntimeSystem's databox vs expected_box.\ntemplate <typename Generator>\nvoid test_klein_gordon_cce_source(const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(*gen);\n  const size_t number_of_radial_points = sdist(*gen);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n\n  Variables<swsh_transform_tags_to_compute> component_swsh_transform_variables{\n      number_of_radial_points *\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n\n  Variables<\n      tmpl::append<real_volume_tags_to_generate, swsh_volume_tags_to_generate,\n                   swsh_volume_tags_to_compute>>\n      component_volume_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n          number_of_radial_points};\n\n  Variables<tmpl::append<swsh_boundary_tags_to_generate,\n                         swsh_boundary_tags_to_compute>>\n      component_boundary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n  tmpl::for_each<swsh_volume_tags_to_generate>(\n      [&component_volume_variables, &gen, &coefficient_distribution,\n       &number_of_radial_points, &l_max](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        SpinWeighted<ComplexModalVector, tag::type::type::spin> generated_modes{\n            number_of_radial_points *\n            Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n        Spectral::Swsh::TestHelpers::generate_swsh_modes<tag::type::type::spin>(\n            make_not_null(&generated_modes.data()), gen,\n            make_not_null(&coefficient_distribution), number_of_radial_points,\n            l_max);\n        get(get<tag>(component_volume_variables)) =\n            Spectral::Swsh::inverse_swsh_transform(\n                l_max, number_of_radial_points, generated_modes);\n        // aggressive filter to make the uniformly generated random modes\n        // somewhat reasonable\n        Spectral::Swsh::filter_swsh_volume_quantity(\n            make_not_null(&get(get<tag>(component_volume_variables))), l_max,\n            l_max / 2, 32.0, 8);\n      });\n\n  tmpl::for_each<swsh_boundary_tags_to_generate>(\n      [&component_boundary_variables, &gen, &coefficient_distribution,\n       &l_max](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        SpinWeighted<ComplexModalVector, tag::type::type::spin> generated_modes{\n            Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n        Spectral::Swsh::TestHelpers::generate_swsh_modes<tag::type::type::spin>(\n            make_not_null(&generated_modes.data()), gen,\n            make_not_null(&coefficient_distribution), 1, l_max);\n        get(get<tag>(component_boundary_variables)) =\n            Spectral::Swsh::inverse_swsh_transform(l_max, 1, generated_modes);\n        // aggressive filter to make the uniformly generated random modes\n        // somewhat reasonable\n        Spectral::Swsh::filter_swsh_boundary_quantity(\n            make_not_null(&get(get<tag>(component_boundary_variables))), l_max,\n            l_max / 2);\n      });\n\n  tnsr::i<DataVector, 3> cartesian_cauchy_coordinates;\n  tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>\n      angular_cauchy_coordinates;\n\n  Spectral::Swsh::create_angular_and_cartesian_coordinates(\n      make_not_null(&cartesian_cauchy_coordinates),\n      make_not_null(&angular_cauchy_coordinates), l_max);\n  Spectral::Swsh::SwshInterpolator interpolator(\n      get<0>(angular_cauchy_coordinates), get<1>(angular_cauchy_coordinates),\n      l_max);\n\n  PrecomputeCceDependencies<Tags::EvolutionGaugeBoundaryValue,\n                            Tags::OneMinusY>::\n      apply(make_not_null(&get<Tags::OneMinusY>(component_volume_variables)),\n            l_max, number_of_radial_points);\n\n  using component = mock_kg_characteristic_evolution<metavariables>;\n\n  // tests start here\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      tuples::tagged_tuple_from_typelist<\n          Parallel::get_const_global_cache_tags<metavariables>>{\n          l_max, number_of_radial_points}};\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {component_volume_variables, component_boundary_variables,\n       component_swsh_transform_variables, interpolator});\n  auto expected_box = db::create<\n      tmpl::append<component::simple_tags,\n                   db::AddSimpleTags<Tags::LMax, Tags::NumberOfRadialPoints>>>(\n      component_volume_variables, component_boundary_variables,\n      component_swsh_transform_variables, interpolator, l_max,\n      number_of_radial_points);\n\n  runner.set_phase(Parallel::Phase::Evolve);\n\n  for (int i = 0; i < 9; i++) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n\n  // tests for `Actions::CalculateIntegrandInputsForTag<Tags::KleinGordonPi>`\n  {\n    // tests for `mutate_all_pre_swsh_derivatives_for_tag<Tags::KleinGordonPi>`\n    tmpl::for_each<pre_swsh_derivative_tags_to_compute_for_t<\n        Tags::KleinGordonPi>>([&expected_box,\n                               &runner](auto pre_swsh_derivative_tag_v) {\n      using pre_swsh_derivative_tag =\n          typename decltype(pre_swsh_derivative_tag_v)::type;\n      using mutation = PreSwshDerivatives<pre_swsh_derivative_tag>;\n      db::mutate_apply<mutation>(make_not_null(&expected_box));\n\n      auto computed_result =\n          ActionTesting::get_databox_tag<component, pre_swsh_derivative_tag>(\n              runner, 0);\n\n      auto expected_result = db::get<pre_swsh_derivative_tag>(expected_box);\n      CHECK(computed_result == expected_result);\n    });\n\n    // tests for `mutate_all_swsh_derivatives_for_tag<Tags::KleinGordonPi>`\n    db::mutate_apply<Spectral::Swsh::AngularDerivatives<\n        single_swsh_derivative_tags_to_compute_for_t<Tags::KleinGordonPi>>>(\n        make_not_null(&expected_box));\n    tmpl::for_each<\n        single_swsh_derivative_tags_to_compute_for_t<Tags::KleinGordonPi>>(\n        [&expected_box, &runner](auto derivative_tag_v) {\n          using derivative_tag = typename decltype(derivative_tag_v)::type;\n          detail::apply_swsh_jacobian_helper<derivative_tag>(\n              make_not_null(&expected_box),\n              typename ApplySwshJacobianInplace<\n                  derivative_tag>::on_demand_argument_tags{});\n\n          auto computed_result =\n              ActionTesting::get_databox_tag<component, derivative_tag>(runner,\n                                                                        0);\n\n          auto expected_result = db::get<derivative_tag>(expected_box);\n          CHECK(computed_result == expected_result);\n        });\n\n    db::mutate_apply<Spectral::Swsh::AngularDerivatives<\n        second_swsh_derivative_tags_to_compute_for_t<Tags::KleinGordonPi>>>(\n        make_not_null(&expected_box));\n    tmpl::for_each<\n        second_swsh_derivative_tags_to_compute_for_t<Tags::KleinGordonPi>>(\n        [&expected_box, &runner](auto derivative_tag_v) {\n          using derivative_tag = typename decltype(derivative_tag_v)::type;\n          detail::apply_swsh_jacobian_helper<derivative_tag>(\n              make_not_null(&expected_box),\n              typename ApplySwshJacobianInplace<\n                  derivative_tag>::on_demand_argument_tags{});\n\n          auto computed_result =\n              ActionTesting::get_databox_tag<component, derivative_tag>(runner,\n                                                                        0);\n\n          auto expected_result = db::get<derivative_tag>(expected_box);\n          CHECK(computed_result == expected_result);\n        });\n  }\n\n  // tests for `ComputeKleinGordonSource`\n  tmpl::for_each<bondi_hypersurface_step_tags>([&expected_box,\n                                                &runner](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    db::mutate_apply<ComputeKleinGordonSource<tag>>(\n        make_not_null(&expected_box));\n\n    auto computed_result =\n        ActionTesting::get_databox_tag<component, Tags::KleinGordonSource<tag>>(\n            runner, 0);\n\n    auto expected_result = db::get<Tags::KleinGordonSource<tag>>(expected_box);\n    CHECK(computed_result == expected_result);\n  });\n\n  // tests for `GaugeAdjustedBoundaryValue<Tags::KleinGordonPi>`\n  {\n    db::mutate_apply<GaugeAdjustedBoundaryValue<Tags::KleinGordonPi>>(\n        make_not_null(&expected_box));\n    auto computed_result = ActionTesting::get_databox_tag<\n        component, Tags::EvolutionGaugeBoundaryValue<Tags::KleinGordonPi>>(\n        runner, 0);\n\n    auto expected_result =\n        db::get<Tags::EvolutionGaugeBoundaryValue<Tags::KleinGordonPi>>(\n            expected_box);\n    CHECK(computed_result == expected_result);\n  }\n\n  // tests for\n  // `ComputeBondiIntegrand<Tags::RegularIntegrand<Tags::KleinGordonPi>>` and\n  // `ComputeBondiIntegrand<Tags::PoleOfIntegrand<Tags::KleinGordonPi>>`\n  tmpl::for_each<\n      integrand_terms_to_compute_for_bondi_variable<Tags::KleinGordonPi>>(\n      [&expected_box, &runner](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        db::mutate_apply<ComputeBondiIntegrand<tag>>(\n            make_not_null(&expected_box));\n\n        auto computed_result =\n            ActionTesting::get_databox_tag<component, tag>(runner, 0);\n\n        auto expected_result = db::get<tag>(expected_box);\n        CHECK(computed_result == expected_result);\n      });\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.KGCceCalculations\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_klein_gordon_cce_source(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_KleinGordonH5BoundaryCommunication.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Executables/Cce/CharacteristicExtractBase.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Components/KleinGordonCharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/KleinGordonBoundaryTestHelpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace Cce {\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct mock_observer_writer {\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using simple_tags = tmpl::list<observers::Tags::H5FileLock>;\n\n  using const_global_cache_tags = tmpl::list<>;\n\n  using metavariables = Metavariables;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_klein_gordon_h5_worldtube_boundary {\n  using component_being_mocked = KleinGordonH5WorldtubeBoundary<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using initialize_action_list =\n      tmpl::list<Actions::InitializeWorldtubeBoundary<\n          KleinGordonH5WorldtubeBoundary<Metavariables>>>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n          phase_dependent_action_list>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_klein_gordon_characteristic_evolution {\n  using component_being_mocked =\n      KleinGordonCharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeKleinGordonVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<\n              Actions::RequestBoundaryData<\n                  KleinGordonH5WorldtubeBoundary<Metavariables>,\n                  mock_klein_gordon_characteristic_evolution<Metavariables>>,\n              Actions::ReceiveWorldtubeData<\n                  Metavariables,\n                  typename Metavariables::cce_boundary_communication_tags>,\n              Actions::ReceiveWorldtubeData<\n                  Metavariables, typename Metavariables::\n                                     klein_gordon_boundary_communication_tags>,\n              Actions::RequestNextBoundaryData<\n                  KleinGordonH5WorldtubeBoundary<Metavariables>,\n                  mock_klein_gordon_characteristic_evolution<Metavariables>>>>>;\n};\n\nstruct test_metavariables : CharacteristicExtractDefaults<false> {\n  using cce_base = CharacteristicExtractDefaults<false>;\n  using evolved_swsh_tags = tmpl::append<cce_base::evolved_swsh_tags,\n                                         tmpl::list<Cce::Tags::KleinGordonPsi>>;\n  using evolved_swsh_dt_tags =\n      tmpl::append<cce_base::evolved_swsh_dt_tags,\n                   tmpl::list<Cce::Tags::KleinGordonPi>>;\n  using cce_step_choosers = tmpl::list<>;\n  using scri_values_to_observe = tmpl::list<>;\n\n  using klein_gordon_boundary_communication_tags =\n      Cce::Tags::klein_gordon_worldtube_boundary_tags;\n  using klein_gordon_gauge_boundary_tags = tmpl::list<\n      Cce::Tags::EvolutionGaugeBoundaryValue<Cce::Tags::KleinGordonPsi>,\n      Cce::Tags::EvolutionGaugeBoundaryValue<Cce::Tags::KleinGordonPi>>;\n  using klein_gordon_scri_tags =\n      tmpl::list<Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPsi>,\n                 Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPi>>;\n\n  using klein_gordon_pre_swsh_derivative_tags = tmpl::list<>;\n  using klein_gordon_swsh_derivative_tags = tmpl::list<>;\n  using klein_gordon_transform_buffer_tags = tmpl::list<>;\n  using klein_gordon_source_tags = tmpl::list<>;\n  using klein_gordon_cce_integrand_tags = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using component_list =\n      tmpl::list<mock_klein_gordon_h5_worldtube_boundary<test_metavariables>,\n                 mock_klein_gordon_characteristic_evolution<test_metavariables>,\n                 mock_observer_writer<test_metavariables>>;\n};\n\n// This function tests the communication between the evolution and boundary\n// components of `KleinGordonCharacteristicExtract`, which passes around\n// worldtube data for Klein-Gordon CCE evolution.\n//\n// The core check flow is as follows:\n//  (a) The evolution component asks the boundary component to send worldtube\n//  data once the data are available (`RequestBoundaryData`)\n//  (b) When the data are not ready, the evolution is paused and waits for data.\n//  Here we check that the evolution component is indeed terminated.\n//  (c) Then the boundary component prepares and sends the data.\n//  (d) The evolution component receives the data.\n//  (e) We check that the received data are the same as the ones we generated at\n//  the beginning.\ntemplate <typename Generator>\nvoid test_klein_gordon_h5_boundary_communication(\n    const gsl::not_null<Generator*> gen) {\n  using evolution_component =\n      mock_klein_gordon_characteristic_evolution<test_metavariables>;\n  using worldtube_component =\n      mock_klein_gordon_h5_worldtube_boundary<test_metavariables>;\n  using writer_component = mock_observer_writer<test_metavariables>;\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n\n  const std::string filename =\n      \"KleinGordonH5BoundaryCommunicationTestCceR0100.h5\";\n\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n  TestHelpers::write_scalar_tensor_test_file(solution, filename, target_time,\n                                             extraction_radius, frequency,\n                                             amplitude, l_max);\n\n  const double start_time = target_time;\n  const double target_step_size = 0.01 * value_dist(*gen);\n\n  // tests start here\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{\n      {false, l_max, extraction_radius,\n       Tags::EndTimeFromFile::create_from_options(std::nullopt, filename,\n                                                  false),\n       start_time, number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  const size_t buffer_size = 5;\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component_and_initialize<writer_component>(\n      &runner, 0, {Parallel::NodeLock{}});\n  ActionTesting::emplace_component<evolution_component>(\n      &runner, 0, target_step_size, target_step_size);\n  ActionTesting::emplace_component<worldtube_component>(\n      &runner, 0,\n      Tags::H5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          std::optional<double>{}),\n      Tags::KleinGordonH5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          std::optional<double>{}));\n\n  // this should run the initializations\n  for (size_t i = 0; i < 6; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<worldtube_component>(make_not_null(&runner), 0);\n  }\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n\n  // ask the boundary component for data\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // the first request from the evolution component (for tensor data)\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // now the evolution component should be paused since the data are not ready\n  CHECK(ActionTesting::get_terminate<evolution_component>(runner, 0));\n\n  // the response of the boundary component\n  // (`BoundaryComputeAndSendToEvolution`): prepare boundary data for tensor and\n  // scalar variables\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n\n  // rerun the two `ReceiveWorldtubeData` to receive tensor and scalar data\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n\n  // finally, run `RequestNextBoundaryData`.\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n\n  // Then we check that the received data are consistent with what we generated\n  //\n  // first, the tensor variables\n  const size_t libsharp_size =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  tnsr::ii<ComplexModalVector, 3> spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dt_spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dr_spatial_metric_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dt_shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dr_shift_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dt_lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dr_lapse_coefficients{libsharp_size};\n  TestHelpers::create_fake_time_varying_data(\n      make_not_null(&spatial_metric_coefficients),\n      make_not_null(&dt_spatial_metric_coefficients),\n      make_not_null(&dr_spatial_metric_coefficients),\n      make_not_null(&shift_coefficients), make_not_null(&dt_shift_coefficients),\n      make_not_null(&dr_shift_coefficients), make_not_null(&lapse_coefficients),\n      make_not_null(&dt_lapse_coefficients),\n      make_not_null(&dr_lapse_coefficients), solution, extraction_radius,\n      amplitude, frequency, target_time, l_max, false);\n\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      expected_boundary_variables{number_of_angular_points};\n  create_bondi_boundary_data(\n      make_not_null(&expected_boundary_variables), spatial_metric_coefficients,\n      dt_spatial_metric_coefficients, dr_spatial_metric_coefficients,\n      shift_coefficients, dt_shift_coefficients, dr_shift_coefficients,\n      lapse_coefficients, dt_lapse_coefficients, dr_lapse_coefficients,\n      extraction_radius, l_max);\n\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  tmpl::for_each<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      [&expected_boundary_variables, &runner,\n       &angular_derivative_approx](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& test_lhs =\n            ActionTesting::get_databox_tag<evolution_component, tag>(runner, 0);\n        const auto& test_rhs = get<tag>(expected_boundary_variables);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_lhs, test_rhs,\n                                     angular_derivative_approx);\n      });\n\n  // then the scalar variables\n  Scalar<ComplexModalVector> expected_kg_psi_modal;\n  Scalar<ComplexModalVector> expected_kg_pi_modal;\n  Scalar<DataVector> expected_kg_psi_nodal;\n  Scalar<DataVector> expected_kg_pi_nodal;\n\n  TestHelpers::create_fake_time_varying_klein_gordon_data(\n      make_not_null(&expected_kg_psi_modal),\n      make_not_null(&expected_kg_pi_modal),\n      make_not_null(&expected_kg_psi_nodal),\n      make_not_null(&expected_kg_pi_nodal), extraction_radius, amplitude,\n      frequency, target_time, l_max);\n\n  const auto& kg_psi_from_actions =\n      ActionTesting::get_databox_tag<evolution_component,\n                                     Tags::BoundaryValue<Tags::KleinGordonPsi>>(\n          runner, 0);\n\n  const auto& kg_pi_from_actions =\n      ActionTesting::get_databox_tag<evolution_component,\n                                     Tags::BoundaryValue<Tags::KleinGordonPi>>(\n          runner, 0);\n\n  // convert `expected_kg_psi_nodal` and `expected_kg_pi_nodal` to\n  // `ComplexDataVector`.\n  ComplexDataVector expected_kg_psi_nodal_complex{get(expected_kg_psi_nodal)};\n  ComplexDataVector expected_kg_pi_nodal_complex{get(expected_kg_pi_nodal)};\n\n  CHECK_ITERABLE_CUSTOM_APPROX(get(kg_psi_from_actions).data(),\n                               expected_kg_psi_nodal_complex,\n                               angular_derivative_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(kg_pi_from_actions).data(),\n                               expected_kg_pi_nodal_complex,\n                               angular_derivative_approx);\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.KleinGordonH5BoundaryCommunication\",\n    \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_klein_gordon_h5_boundary_communication(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_Psi0Matching.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/Psi0Matching.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n\nnamespace Cce {\n\nnamespace {\n\nstruct UpdateCartesianFromAngular {\n  using const_global_cache_tags = tmpl::list<Tags::LMax>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&box));\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::PartiallyFlatAngularCoords>>(\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nusing real_tags_to_generate = tmpl::list<Tags::PartiallyFlatCartesianCoords>;\n\nusing real_tags_to_compute = tmpl::list<Tags::PartiallyFlatAngularCoords>;\n\nusing swsh_volume_tags_to_generate = tmpl::list<Tags::BondiJ, Tags::OneMinusY>;\n\nusing swsh_boundary_tags_to_generate =\n    tmpl::list<Tags::CauchyGaugeC, Tags::CauchyGaugeD, Tags::CauchyGaugeOmega,\n               Tags::BoundaryValue<Tags::BondiR>,\n               Tags::BoundaryValue<Tags::BondiBeta>>;\n\nusing swsh_volume_tags_to_compute =\n    tmpl::list<Tags::BondiJCauchyView, Tags::Dy<Tags::BondiJCauchyView>,\n               Tags::Dy<Tags::Dy<Tags::BondiJCauchyView>>, Tags::Psi0Match,\n               Tags::Dy<Tags::Psi0Match>>;\n\nusing swsh_boundary_tags_to_compute =\n    tmpl::list<Tags::BoundaryValue<Tags::Psi0Match>,\n               Tags::BoundaryValue<Tags::Dlambda<Tags::Psi0Match>>>;\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using simple_tags = tmpl::push_back<\n      db::AddSimpleTags<\n          ::Tags::Variables<\n              tmpl::append<real_tags_to_generate, real_tags_to_compute>>,\n          ::Tags::Variables<tmpl::append<swsh_volume_tags_to_generate,\n                                         swsh_volume_tags_to_compute>>,\n          ::Tags::Variables<tmpl::append<swsh_boundary_tags_to_generate,\n                                         swsh_boundary_tags_to_compute>>>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::PartiallyFlatAngularCoords>>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<UpdateCartesianFromAngular,\n                     Actions::CalculatePsi0AndDerivAtInnerBoundary>>>;\n};\n\nstruct metavariables {\n  using component_list =\n      tmpl::list<mock_characteristic_evolution<metavariables>>;\n};\n}  // namespace\n\n// This unit test is to validate the automatic calling chain of the action\n// CalculatePsi0AndDerivAtInnerBoundary. The test involves\n// (a) Fills a bunch of variables with random numbers (filtered so that there is\n// no aliasing in highest modes).\n// (b) Puts those variables in two places: the\n// MockRuntimeSystem runner and a databox called expected_box.\n// (c) Calls next_action twice (which fills the results in the databox of\n// MockRuntimeSystem runner).\n// (d) Calls all the individual mutators by hand, filling the results in\n// expected_box.\n// (e) Compares MockRuntimeSystem's databox vs expected_box.\n// This test cannot catch any error in any of the individual mutators. The\n// correctness of the mutators is tested in Test_NewmanPenrose.cpp\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.Psi0Matching\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  // limited l_max distribution because test depends on an analytic\n  // basis function with factorials.\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(gen);\n  UniformCustomDistribution<size_t> sdist_rad{2, 3};\n  const size_t number_of_radial_points = sdist_rad(gen);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n  CAPTURE(l_max);\n\n  using component = mock_characteristic_evolution<metavariables>;\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{l_max}};\n\n  // (a) Fills a bunch of variables with random numbers (filtered so that there\n  // is no aliasing in highest modes).\n  Variables<tmpl::append<real_tags_to_generate, real_tags_to_compute>>\n      real_variables{Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  Variables<\n      tmpl::append<swsh_volume_tags_to_generate, swsh_volume_tags_to_compute>>\n      swsh_volume_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n          number_of_radial_points};\n  Variables<tmpl::append<swsh_boundary_tags_to_generate,\n                         swsh_boundary_tags_to_compute>>\n      swsh_boundary_variables{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n  tmpl::for_each<real_tags_to_generate>([\n    &real_variables, &gen, &coefficient_distribution, &l_max\n  ](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    SpinWeighted<ComplexModalVector, 0> generated_modes{\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n    SpinWeighted<ComplexDataVector, 0> generated_data{\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    for (auto& tensor_component : get<tag>(real_variables)) {\n      Spectral::Swsh::TestHelpers::generate_swsh_modes<0>(\n          make_not_null(&generated_modes.data()), make_not_null(&gen),\n          make_not_null(&coefficient_distribution), 1, l_max);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&generated_data), generated_modes);\n      // aggressive filter to make the uniformly generated random modes\n      // somewhat reasonable\n      Spectral::Swsh::filter_swsh_boundary_quantity(\n          make_not_null(&generated_data), l_max, l_max / 2);\n      tensor_component = real(generated_data.data());\n    }\n  });\n\n  tmpl::for_each<swsh_volume_tags_to_generate>([&swsh_volume_variables, &gen,\n                                                &coefficient_distribution,\n                                                &l_max,\n                                                &number_of_radial_points](\n                                                   auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    const size_t number_of_angular_points =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n    SpinWeighted<ComplexModalVector, 0> generated_modes{\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n    SpinWeighted<ComplexDataVector, 0> generated_data{\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    for (auto& tensor_component : get<tag>(swsh_volume_variables)) {\n      SpinWeighted<ComplexDataVector, 2> angular_view_j;\n      for (size_t i = 0; i < number_of_radial_points; ++i) {\n        Spectral::Swsh::TestHelpers::generate_swsh_modes<0>(\n            make_not_null(&generated_modes.data()), make_not_null(&gen),\n            make_not_null(&coefficient_distribution), 1, l_max);\n        Spectral::Swsh::inverse_swsh_transform(\n            l_max, 1, make_not_null(&generated_data), generated_modes);\n        // aggressive filter to make the uniformly generated random modes\n        // somewhat reasonable\n        Spectral::Swsh::filter_swsh_boundary_quantity(\n            make_not_null(&generated_data), l_max, l_max / 2);\n        angular_view_j.set_data_ref(\n            tensor_component.data().data() + i * number_of_angular_points,\n            number_of_angular_points);\n        angular_view_j.data() = generated_data.data();\n      }\n    }\n  });\n\n  tmpl::for_each<swsh_boundary_tags_to_generate>([\n    &swsh_boundary_variables, &gen, &coefficient_distribution, &l_max\n  ](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    SpinWeighted<ComplexModalVector, 0> generated_modes{\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n    SpinWeighted<ComplexDataVector, 0> generated_data{\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    for (auto& tensor_component : get<tag>(swsh_boundary_variables)) {\n      Spectral::Swsh::TestHelpers::generate_swsh_modes<0>(\n          make_not_null(&generated_modes.data()), make_not_null(&gen),\n          make_not_null(&coefficient_distribution), 1, l_max);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&generated_data), generated_modes);\n      // aggressive filter to make the uniformly generated random modes\n      // somewhat reasonable\n      Spectral::Swsh::filter_swsh_boundary_quantity(\n          make_not_null(&generated_data), l_max, l_max / 2);\n      tensor_component = generated_data.data();\n    }\n  });\n\n  // (b) Puts those variables in two places: the\n  // MockRuntimeSystem runner and a databox called expected_box.\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {real_variables, swsh_volume_variables, swsh_boundary_variables,\n       Spectral::Swsh::SwshInterpolator{}});\n  auto expected_box = db::create<\n      tmpl::append<component::simple_tags, db::AddSimpleTags<Tags::LMax>>>(\n      std::move(real_variables), std::move(swsh_volume_variables),\n      std::move(swsh_boundary_variables), Spectral::Swsh::SwshInterpolator{},\n      l_max);\n\n  // (c) Calls next_action twice (which fills the results in the databox of\n  // MockRuntimeSystem runner).\n  runner.set_phase(Parallel::Phase::Evolve);\n  // Obtain inertial angular coordinates and SwshInterpolator that are needed\n  // by `CalculatePsi0AndDerivAtInnerBoundary`. This action is tested in\n  // Test_UpdateGauge.cpp\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  // Apply the `CalculatePsi0AndDerivAtInnerBoundary` action\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n\n  // (d) Calls all the individual mutators by hand, filling the results in\n  // expected_box.\n  db::mutate_apply<GaugeUpdateAngularFromCartesian<\n      Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n      make_not_null(&expected_box));\n  db::mutate_apply<GaugeUpdateInterpolator<Tags::PartiallyFlatAngularCoords>>(\n      make_not_null(&expected_box));\n\n  db::mutate_apply<TransformBondiJToCauchyCoords>(make_not_null(&expected_box));\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::BondiJCauchyView>>>(\n      make_not_null(&expected_box));\n  db::mutate_apply<\n      PreSwshDerivatives<Tags::Dy<Tags::Dy<Tags::BondiJCauchyView>>>>(\n      make_not_null(&expected_box));\n  db::mutate_apply<VolumeWeyl<Tags::Psi0Match>>(make_not_null(&expected_box));\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::Psi0Match>>>(\n      make_not_null(&expected_box));\n  db::mutate_apply<InnerBoundaryWeyl>(make_not_null(&expected_box));\n\n  // (e) Compares MockRuntimeSystem's databox vs expected_box.\n  tmpl::for_each<swsh_volume_tags_to_compute>(\n      [&runner, &expected_box](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        const auto& action_result =\n            ActionTesting::get_databox_tag<component, tag>(runner, 0);\n        const auto& expected_result = db::get<tag>(expected_box);\n        CHECK_ITERABLE_APPROX(action_result, expected_result);\n      });\n\n  tmpl::for_each<swsh_boundary_tags_to_compute>(\n      [&runner, &expected_box ](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        const auto& action_result =\n            ActionTesting::get_databox_tag<component, tag>(runner, 0);\n        const auto& expected_result = db::get<tag>(expected_box);\n        CHECK_ITERABLE_APPROX(action_result, expected_result);\n      });\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_RequestBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Actions/RequestBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/Actions/WorldtubeBoundaryMocking.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace Actions {\nnamespace {\nstd::vector<double> times_requested;\ntemplate <typename BoundaryComponent, typename EvolutionComponent>\nstruct MockBoundaryComputeAndSendToEvolution {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl2::flat_any_v<std::is_same_v<\n                ::Tags::Variables<\n                    typename Metavariables::cce_boundary_communication_tags>,\n                DbTags>...>> = nullptr>\n  static void apply(const db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const TimeStepId& time) {\n    times_requested.push_back(time.substep_time());\n  }\n};\n}  // namespace\n}  // namespace Actions\n\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::RequestBoundaryData<\n                         H5WorldtubeBoundary<Metavariables>,\n                         mock_characteristic_evolution<Metavariables>>,\n                     Actions::RequestNextBoundaryData<\n                         H5WorldtubeBoundary<Metavariables>,\n                         mock_characteristic_evolution<Metavariables>>>>>;\n};\n\nstruct test_metavariables {\n  using evolved_swsh_tags = tmpl::list<Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Tags::BondiH>;\n  using cce_step_choosers = tmpl::list<>;\n  using evolved_coordinates_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::InertialRetardedTime>>;\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Tags::BondiR, Tags::DuRDividedByR, Tags::BondiJ,\n                     Tags::Dr<Tags::BondiJ>, Tags::BondiBeta, Tags::BondiQ,\n                     Tags::BondiU, Tags::BondiW, Tags::BondiH>,\n          tmpl::bind<Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Tags::BondiUAtScri, Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using scri_values_to_observe = tmpl::list<>;\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      bondi_hypersurface_step_tags,\n      tmpl::bind<integrand_terms_to_compute_for_bondi_variable, tmpl::_1>>>;\n  using cce_integration_independent_tags =\n      tmpl::append<pre_computation_tags, tmpl::list<Tags::DuRDividedByR>>;\n  using cce_temporary_equations_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          cce_integrand_tags, tmpl::bind<integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags = tmpl::list<Tags::CauchyAngularCoords>;\n  using cce_scri_tags =\n      tmpl::list<Cce::Tags::News, Cce::Tags::ScriPlus<Cce::Tags::Strain>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi0>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi1>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi2>,\n                 Cce::Tags::ScriPlus<Cce::Tags::Psi3>,\n                 Cce::Tags::TimeIntegral<Cce::Tags::ScriPlus<Cce::Tags::Psi4>>,\n                 Cce::Tags::ScriPlusFactor<Cce::Tags::Psi4>>;\n\n  using ccm_psi0 = tmpl::list<\n          Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n          Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n\n  using component_list =\n      tmpl::list<mock_h5_worldtube_boundary<test_metavariables>,\n                 mock_characteristic_evolution<test_metavariables>>;\n\n  static constexpr bool evolve_ccm = false;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.RequestBoundaryData\",\n                  \"[Unit][Cce]\") {\n  using evolution_component = mock_characteristic_evolution<test_metavariables>;\n  using worldtube_component = mock_h5_worldtube_boundary<test_metavariables>;\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n\n  const std::string filename = \"BoundaryDataTest_CceR0100.h5\";\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(gen);\n  const std::array<double, 3> spin{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  const std::array<double, 3> center{\n      {value_dist(gen), value_dist(gen), value_dist(gen)}};\n  const gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n  const double frequency = 0.1 * value_dist(gen);\n  const double amplitude = 0.1 * value_dist(gen);\n  const double target_time = 50.0 * value_dist(gen);\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  // create the test file, because on initialization the manager will need\n  // to get basic data out of the file\n  TestHelpers::write_test_file(solution, filename, target_time,\n                               extraction_radius, frequency, amplitude, l_max);\n\n  const double start_time = target_time;\n  const double target_step_size = 0.01 * value_dist(gen);\n  const double end_time = start_time + 10 * target_step_size;\n  const size_t buffer_size = 5;\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{\n      {false, l_max, extraction_radius,\n       Tags::EndTimeFromFile::create_from_options(end_time, filename, false),\n       start_time, number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  // requested step size and slab size chosen to be sure that the\n  // chosen step is a predictable value (not subject to roundoff\n  // fluctuations in the generated value)\n  ActionTesting::emplace_component<evolution_component>(\n      &runner, 0, target_step_size * 0.75, target_step_size);\n  ActionTesting::emplace_component<worldtube_component>(\n      &runner, 0,\n      Tags::H5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3_st,\n                                                                       4_st),\n          std::optional<double>{}));\n\n  // this should run the initializations\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n  for(size_t i = 0; i < 3; ++i) {\n    ActionTesting::next_action<worldtube_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n\n  // the first request\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // the first response\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n  CHECK(Actions::times_requested.size() == 1);\n  CHECK(Actions::times_requested[0] == start_time);\n\n  // the second request (the next substep)\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // the second response\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n  CHECK(Actions::times_requested.size() == 2);\n  CHECK(Actions::times_requested[1] == start_time + target_step_size * 0.75);\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_RequestKleinGordonBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Executables/Cce/CharacteristicExtractBase.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeKleinGordonVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/RequestBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/KleinGordonCharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/Actions/WorldtubeBoundaryMocking.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/KleinGordonBoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace Cce {\nnamespace Actions {\nnamespace {\nstd::vector<double> times_requested;  // NOLINT\ntemplate <typename BoundaryComponent, typename EvolutionComponent>\nstruct MockBoundaryComputeAndSendToEvolution {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl2::flat_any_v<std::is_same_v<\n                ::Tags::Variables<\n                    typename Metavariables::cce_boundary_communication_tags>,\n                DbTags>...>> = nullptr>\n  static void apply(const db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const TimeStepId& time) {\n    times_requested.push_back(time.substep_time());\n  }\n};\n}  // namespace\n}  // namespace Actions\n\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_kg_characteristic_evolution {\n  using component_being_mocked =\n      KleinGordonCharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeKleinGordonVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      // advance the time so that the current `TimeStepId` is valid without\n      // having to perform self-start.\n      ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>,\n      Parallel::Actions::TerminatePhase>;\n\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::RequestBoundaryData<\n                         KleinGordonH5WorldtubeBoundary<Metavariables>,\n                         mock_kg_characteristic_evolution<Metavariables>>,\n                     Actions::RequestNextBoundaryData<\n                         KleinGordonH5WorldtubeBoundary<Metavariables>,\n                         mock_kg_characteristic_evolution<Metavariables>>>>>;\n};\n\nstruct test_metavariables : CharacteristicExtractDefaults<false> {\n  using cce_base = CharacteristicExtractDefaults<false>;\n  using evolved_swsh_tags = tmpl::append<cce_base::evolved_swsh_tags,\n                                         tmpl::list<Cce::Tags::KleinGordonPsi>>;\n  using evolved_swsh_dt_tags =\n      tmpl::append<cce_base::evolved_swsh_dt_tags,\n                   tmpl::list<Cce::Tags::KleinGordonPi>>;\n  using cce_step_choosers = tmpl::list<>;\n  using scri_values_to_observe = tmpl::list<>;\n\n  using klein_gordon_boundary_communication_tags =\n      Cce::Tags::klein_gordon_worldtube_boundary_tags;\n  using klein_gordon_gauge_boundary_tags = tmpl::list<\n      Cce::Tags::EvolutionGaugeBoundaryValue<Cce::Tags::KleinGordonPsi>,\n      Cce::Tags::EvolutionGaugeBoundaryValue<Cce::Tags::KleinGordonPi>>;\n  using klein_gordon_scri_tags =\n      tmpl::list<Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPsi>,\n                 Cce::Tags::ScriPlus<Cce::Tags::KleinGordonPi>>;\n\n  using klein_gordon_pre_swsh_derivative_tags = tmpl::list<>;\n  using klein_gordon_swsh_derivative_tags = tmpl::list<>;\n  using klein_gordon_transform_buffer_tags = tmpl::list<>;\n  using klein_gordon_source_tags = tmpl::list<>;\n  using klein_gordon_cce_integrand_tags = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using component_list =\n      tmpl::list<mock_klein_gordon_h5_worldtube_boundary<test_metavariables>,\n                 mock_kg_characteristic_evolution<test_metavariables>>;\n};\n\n// This function tests that `Actions::RequestBoundaryData` and\n// `Actions::RequestNextBoundaryData` can be successfully invoked by\n// `KleinGordonCharacteristicEvolution`.\n//\n// The function begins by generating and storing some scalar and tensor data\n// into an HDF5 file named `filename` using `write_scalar_tensor_test_file`.\n// Subsequently, it initializes a mocked evolution component\n// `mock_kg_characteristic_evolution<Metavariables>` and a mocked boundary\n// component `mock_klein_gordon_h5_worldtube_boundary<Metavariables>`. Next, the\n// function invokes `Actions::RequestBoundaryData` and\n// `Actions::RequestNextBoundaryData`, with `BoundaryComputeAndSendToEvolution`\n// replaced by `MockBoundaryComputeAndSendToEvolution`. Here\n// `MockBoundaryComputeAndSendToEvolution` simply stores the time stamps\n// (`times_requested`) that the evolution component needs. Finally, we check\n// that `times_requested` agrees with what we expect.\ntemplate <typename Generator>\nvoid test_klein_gordon_boundary_data(const gsl::not_null<Generator*> gen) {\n  using evolution_component =\n      mock_kg_characteristic_evolution<test_metavariables>;\n  using worldtube_component =\n      mock_klein_gordon_h5_worldtube_boundary<test_metavariables>;\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  const size_t number_of_radial_points = 10;\n  const size_t l_max = 8;\n\n  const std::string filename = \"KleinGordonBoundaryDataTest_CceR0100.h5\";\n\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n\n  // create the test file, because on initialization the manager will need to\n  // get basic data out of the file\n  TestHelpers::write_scalar_tensor_test_file(solution, filename, target_time,\n                                             extraction_radius, frequency,\n                                             amplitude, l_max);\n\n  const double start_time = target_time;\n  const double target_step_size = 0.01 * value_dist(*gen);\n  const double end_time = start_time + 10 * target_step_size;\n  const size_t buffer_size = 5;\n\n  // tests start here\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{\n      {false, l_max, extraction_radius,\n       Tags::EndTimeFromFile::create_from_options(end_time, filename, false),\n       start_time, number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3))}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  // requested step size and slab size chosen to be sure that the\n  // chosen step is a predictable value (not subject to roundoff\n  // fluctuations in the generated value)\n  ActionTesting::emplace_component<evolution_component>(\n      &runner, 0, target_step_size * 0.75, target_step_size);\n  ActionTesting::emplace_component<worldtube_component>(\n      &runner, 0,\n      Tags::H5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          std::optional<double>{}),\n      Tags::KleinGordonH5WorldtubeBoundaryDataManager::create_from_options(\n          l_max, filename, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          std::optional<double>{}));\n\n  // this should run the initializations\n  for (size_t i = 0; i < 6; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<worldtube_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n\n  // the first request\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // the first response\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n  CHECK(Actions::times_requested.size() == 1);\n  CHECK(Actions::times_requested[0] == start_time);\n\n  // the second request (the next substep)\n  ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  // the second response\n  ActionTesting::invoke_queued_simple_action<worldtube_component>(\n      make_not_null(&runner), 0);\n  CHECK(Actions::times_requested.size() == 2);\n  CHECK(Actions::times_requested[1] == start_time + target_step_size * 0.75);\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Cce.Actions.RequestKleinGoronBoundaryData\",\n    \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_klein_gordon_boundary_data(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_ScriObserveInterpolated.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <boost/algorithm/string/erase.hpp>\n#include <boost/algorithm/string/replace.hpp>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionScri.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeCharacteristicEvolutionVariables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InsertInterpolationScriData.hpp\"\n#include \"Evolution/Systems/Cce/Actions/ScriObserveInterpolated.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Cce.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce {\nnamespace {\n\ntemplate <typename Tag>\nstruct SetBoundaryValues {\n  template <\n      typename ParallelComponent, typename... DbTags, typename Metavariables,\n      typename ArrayIndex,\n      Requires<tmpl::list_contains_v<tmpl::list<DbTags...>, Tag>> = nullptr>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    ComplexDataVector set_values) {\n    db::mutate<Tag>(\n        [&set_values](const gsl::not_null<typename Tag::type*>\n                          spin_weighted_scalar_quantity) {\n          get(*spin_weighted_scalar_quantity).data() = std::move(set_values);\n        },\n        make_not_null(&box));\n  }\n\n  template <\n      typename ParallelComponent, typename... DbTags, typename Metavariables,\n      typename ArrayIndex,\n      Requires<tmpl::list_contains_v<tmpl::list<DbTags...>, Tag>> = nullptr>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, DataVector set_values) {\n    db::mutate<Tag>(\n        [&set_values](\n            const gsl::not_null<typename Tag::type*> scalar_quantity) {\n          get(*scalar_quantity) = std::move(set_values);\n        },\n        make_not_null(&box));\n  }\n};\n\ntemplate <typename Metavariables>\nstruct mock_observer {\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using const_global_cache_tags =\n      tmpl::list<observers::Tags::ReductionFileName, Tags::ObservationLMax>;\n  using initialize_action_list =\n      tmpl::list<observers::Actions::InitializeWriter<Metavariables>>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeCharacteristicEvolutionVariables<Metavariables>,\n      Actions::InitializeCharacteristicEvolutionTime<\n          typename Metavariables::evolved_coordinates_variables_tag,\n          typename Metavariables::evolved_swsh_tags, false>,\n      Actions::InitializeCharacteristicEvolutionScri<\n          typename Metavariables::scri_values_to_observe,\n          typename Metavariables::cce_boundary_component>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = size_t;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::StepperErrorEstimatesEnabled>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<\n              tmpl::transform<\n                  typename Metavariables::scri_values_to_observe,\n                  tmpl::bind<Actions::InsertInterpolationScriData, tmpl::_1,\n                             tmpl::pin<typename Metavariables::\n                                           cce_boundary_component>>>,\n              Actions::ScriObserveInterpolated<\n                  mock_observer<Metavariables>,\n                  typename Metavariables::cce_boundary_component, false>,\n              ::Actions::MutateApply<AdvanceTime<Tags::CceEvolutionPrefix>>>>>;\n};\n\nstruct test_metavariables {\n  using evolved_swsh_tags = tmpl::list<Tags::BondiJ>;\n  using evolved_swsh_dt_tags = tmpl::list<Tags::BondiH>;\n  using cce_step_choosers = tmpl::list<>;\n  using evolved_coordinates_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::CauchyCartesianCoords, Tags::InertialRetardedTime>>;\n  using cce_boundary_communication_tags =\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>;\n  using cce_gauge_boundary_tags = tmpl::flatten<tmpl::list<\n      tmpl::transform<\n          tmpl::list<Tags::BondiR, Tags::DuRDividedByR, Tags::BondiJ,\n                     Tags::Dr<Tags::BondiJ>, Tags::BondiBeta, Tags::BondiQ,\n                     Tags::BondiU, Tags::BondiW, Tags::BondiH>,\n          tmpl::bind<Tags::EvolutionGaugeBoundaryValue, tmpl::_1>>,\n      Tags::BondiUAtScri, Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::PartiallyFlatGaugeOmega, Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<>>>;\n  };\n\n  using const_global_cache_tags =\n      tmpl::list<Tags::SpecifiedStartTime, Tags::ExtractionRadiusSimple>;\n  using cce_integrand_tags = tmpl::flatten<tmpl::transform<\n      bondi_hypersurface_step_tags,\n      tmpl::bind<integrand_terms_to_compute_for_bondi_variable, tmpl::_1>>>;\n  using cce_integration_independent_tags =\n      tmpl::append<pre_computation_tags, tmpl::list<Tags::DuRDividedByR>>;\n  using cce_temporary_equations_tags =\n      tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          cce_integrand_tags, tmpl::bind<integrand_temporary_tags, tmpl::_1>>>>;\n  using cce_pre_swsh_derivatives_tags = all_pre_swsh_derivative_tags;\n  using cce_transform_buffer_tags = all_transform_buffer_tags;\n  using cce_swsh_derivative_tags = all_swsh_derivative_tags;\n  using cce_angular_coordinate_tags = tmpl::list<Tags::CauchyAngularCoords>;\n  using cce_scri_tags =\n      tmpl::list<Tags::News, Tags::ScriPlus<Tags::Psi3>,\n                 Tags::ScriPlus<Tags::Psi2>, Tags::ScriPlus<Tags::Psi1>,\n                 Tags::ScriPlus<Tags::Psi0>,\n                 Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>,\n                 Tags::EthInertialRetardedTime, Tags::ScriPlus<Tags::Strain>>;\n\n  using scri_values_to_observe =\n      tmpl::list<Tags::News, Tags::ScriPlus<Tags::Psi3>,\n                 Tags::ScriPlus<Tags::Psi2>, Tags::ScriPlus<Tags::Psi1>,\n                 Tags::ScriPlus<Tags::Psi0>,\n                 Tags::Du<Tags::TimeIntegral<Tags::ScriPlus<Tags::Psi4>>>,\n                 Tags::EthInertialRetardedTime, Tags::ScriPlus<Tags::Strain>>;\n\n  using observed_reduction_data_tags = tmpl::list<>;\n  using cce_boundary_component =\n      Cce::AnalyticWorldtubeBoundary<test_metavariables>;\n  using ccm_psi0 = tmpl::list<\n      Cce::Tags::BoundaryValue<Cce::Tags::Psi0Match>,\n      Cce::Tags::BoundaryValue<Cce::Tags::Dlambda<Cce::Tags::Psi0Match>>>;\n\n  using component_list =\n      tmpl::list<mock_characteristic_evolution<test_metavariables>,\n                 mock_observer<test_metavariables>>;\n};\n}  // namespace\n\ntemplate <typename TagToCalculate, typename... Tags>\nComplexDataVector compute_expected_field_from_pypp(\n    const Variables<tmpl::list<Tags...>>& random_values,\n    const double linear_coefficient, const double quadratic_coefficient,\n    const double time, TagToCalculate /*meta*/) {\n  const size_t size = random_values.number_of_grid_points();\n  Scalar<DataVector> linear_coefficient_vector;\n  get(linear_coefficient_vector) = DataVector{size, linear_coefficient};\n  Scalar<DataVector> quadratic_coefficient_vector;\n  get(quadratic_coefficient_vector) = DataVector{size, quadratic_coefficient};\n  Scalar<DataVector> time_vector;\n  get(time_vector) = DataVector{size, time};\n  std::string tag_name = db::tag_name<TagToCalculate>();\n  boost::algorithm::replace_all(tag_name, \"(\", \"_\");\n  boost::algorithm::erase_all(tag_name, \")\");\n  return get(pypp::call<typename TagToCalculate::type>(\n                 \"ScriObserveInterpolated\", \"compute_\" + tag_name,\n                 linear_coefficient_vector, quadratic_coefficient_vector,\n                 time_vector, get<Tags>(random_values)...))\n      .data();\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.ScriObserveInterpolated\",\n                  \"[Unit][Cce]\") {\n  register_classes_with_charm<Cce::Solutions::RotatingSchwarzschild>();\n  register_classes_with_charm<Cce::Solutions::TeukolskyWave>();\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  using evolution_component = mock_characteristic_evolution<test_metavariables>;\n  using observation_component = mock_observer<test_metavariables>;\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Cce/Actions/\"};\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n\n  const size_t number_of_radial_points = 4;\n  const size_t l_max = 4;\n  const size_t scri_output_density = 1;\n  const std::string filename = \"ScriObserveInterpolatedTest_CceOutput\";\n\n  const double start_time = 0.0;\n  const double target_step_size = 0.1;\n  const size_t scri_interpolation_size = 3;\n\n  const double amplitude = 0.01 * value_dist(gen);\n  const double duration = 50.0;\n  const double extraction_radius = 100.0;\n  Solutions::TeukolskyWave analytic_solution{extraction_radius, amplitude,\n                                             duration};\n  const AnalyticBoundaryDataManager analytic_manager{\n      l_max, extraction_radius, analytic_solution.get_clone()};\n\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{\n      {start_time, extraction_radius, false, filename, l_max, l_max,\n       number_of_radial_points,\n       static_cast<std::unique_ptr<LtsTimeStepper>>(\n           std::make_unique<::TimeSteppers::AdamsBashforth>(3)),\n       scri_output_density, false}};\n\n  runner.set_phase(Parallel::Phase::Initialization);\n  // Serialize and deserialize to get around the lack of implicit copy\n  // constructor.\n  ActionTesting::emplace_component<evolution_component>(\n      &runner, 0, target_step_size, target_step_size, scri_interpolation_size,\n      serialize_and_deserialize(analytic_manager));\n  if (file_system::check_if_file_exists(filename + \".h5\")) {\n    file_system::rm(filename + \".h5\", true);\n  }\n\n  ActionTesting::emplace_component<observation_component>(&runner, 0);\n\n  // the initialization actions\n  for (size_t i = 0; i < 4; ++i) {\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n  }\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<observation_component>(make_not_null(&runner),\n                                                      0);\n  }\n  runner.set_phase(Parallel::Phase::Evolve);\n\n  // generate data that will be well behaved for the interpolation and the\n  // decomposition to modes done in the observation routine\n  UniformCustomDistribution<double> time_dist{-0.01, 0.01};\n\n  const double linear_coefficient = value_dist(gen) * 0.1;\n  const double quadratic_coefficient = value_dist(gen) * 0.1;\n  const size_t vector_size =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t data_points = 10;\n\n  Variables<typename test_metavariables::cce_scri_tags> random_scri_values{\n      vector_size};\n  tmpl::for_each<typename test_metavariables::cce_scri_tags>(\n      [&l_max, &random_scri_values, &gen,\n       &coefficient_distribution](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        SpinWeighted<ComplexModalVector, tag::type::type::spin> generated_modes{\n            Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n        Spectral::Swsh::TestHelpers::generate_swsh_modes<tag::type::type::spin>(\n            make_not_null(&generated_modes.data()), make_not_null(&gen),\n            make_not_null(&coefficient_distribution), 1, l_max);\n        Spectral::Swsh::inverse_swsh_transform(\n            l_max, 1, make_not_null(&get(get<tag>(random_scri_values))),\n            generated_modes);\n      });\n\n  for (size_t i = 0; i < 3 * data_points; ++i) {\n    // this will give random times that are nonetheless guaranteed to be\n    // monotonically increasing\n    const DataVector time_vector =\n        i * 0.1 / 3.0 +\n        make_with_random_values<DataVector>(\n            make_not_null(&gen), make_not_null(&time_dist), vector_size);\n    ActionTesting::simple_action<evolution_component,\n                                 SetBoundaryValues<Tags::InertialRetardedTime>>(\n        make_not_null(&runner), 0, time_vector);\n    tmpl::for_each<typename test_metavariables::cce_scri_tags>(\n        [&runner, &random_scri_values, &linear_coefficient, &time_vector,\n         &quadratic_coefficient](auto tag_v) {\n          using tag = typename decltype(tag_v)::type;\n          ActionTesting::simple_action<evolution_component,\n                                       SetBoundaryValues<tag>>(\n              make_not_null(&runner), 0,\n              get(get<tag>(random_scri_values)).data() *\n                  (1.0 + linear_coefficient * (time_vector) +\n                   quadratic_coefficient * square(time_vector)));\n        });\n\n    // should put the data we just set into the interpolator\n    for (size_t j = 0; j < tmpl::size<test_metavariables::cce_scri_tags>::value;\n         ++j) {\n      ActionTesting::next_action<evolution_component>(make_not_null(&runner),\n                                                      0);\n    }\n    // should process the interpolation and write to file\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n    ActionTesting::next_action<evolution_component>(make_not_null(&runner), 0);\n    while (not ActionTesting::is_threaded_action_queue_empty<\n           observation_component>(runner, 0)) {\n      ActionTesting::invoke_queued_threaded_action<observation_component>(\n          make_not_null(&runner), 0);\n    }\n  }\n  const auto& interpolation_manager = ActionTesting::get_databox_tag<\n      evolution_component,\n      Tags::InterpolationManager<ComplexDataVector, Tags::News>>(runner, 0);\n  // we won't have dropped all of the volume data or gotten through all of the\n  // data, because the last few points will not be sufficiently centered on the\n  // interpolation stencil\n  CHECK(interpolation_manager.number_of_data_points() < 10);\n  CHECK(interpolation_manager.number_of_target_times() < 8);\n\n  // most of the interpolations should be written now, so we read in the file\n  // and check that they are as expected.\n\n  // scoped to close the file\n  {\n    h5::H5File<h5::AccessType::ReadOnly> read_file{filename + \".h5\"};\n    const auto& cce_file = read_file.get<h5::Cce>(\"SpectreR0100\", l_max, 0);\n    const std::unordered_map<std::string, Matrix> all_data =\n        cce_file.get_data();\n    Approx interpolation_approx =\n        Approx::custom()\n            .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n            .scale(1.0);\n    tmpl::for_each<typename test_metavariables::scri_values_to_observe>(\n        [&random_scri_values, &linear_coefficient, &quadratic_coefficient,\n         &l_max, &interpolation_approx, &all_data](auto tag_v) {\n          using tag = typename decltype(tag_v)::type;\n          const std::string& name = Actions::detail::ScriOutput<tag>::name();\n          REQUIRE(all_data.contains(name));\n          const Matrix& data_matrix = all_data.at(name);\n          CHECK(data_matrix.rows() > 20);\n          // skip the first time because the extrapolation will make that value\n          // unreliable\n          INFO(db::tag_name<tag>());\n          for (size_t i = 1; i < data_matrix.rows(); ++i) {\n            SpinWeighted<ComplexDataVector, tag::type::type::spin> expected;\n            expected.data() = compute_expected_field_from_pypp(\n                random_scri_values, linear_coefficient, quadratic_coefficient,\n                data_matrix(i, 0), tag{});\n            const auto expected_goldberg_modes =\n                Spectral::Swsh::libsharp_to_goldberg_modes(\n                    Spectral::Swsh::swsh_transform(l_max, 1, expected), l_max);\n            for (size_t j = 0; j < square(l_max + 1); ++j) {\n              CHECK(data_matrix(i, 2 * j + 1) ==\n                    interpolation_approx(\n                        real(expected_goldberg_modes.data()[j])));\n              CHECK(data_matrix(i, 2 * j + 2) ==\n                    interpolation_approx(\n                        imag(expected_goldberg_modes.data()[j])));\n            }\n          }\n        });\n    read_file.close_current_object();\n    const auto& dataset = read_file.get<h5::Dat>(\"/Cce/News_expected\");\n    const Matrix data_matrix = dataset.get_data();\n    for (size_t i = 1; i < data_matrix.rows(); ++i) {\n      const ComplexModalVector analytic_news_modes =\n          Spectral::Swsh::libsharp_to_goldberg_modes(\n              Spectral::Swsh::swsh_transform(\n                  l_max, 1,\n                  get(get<Tags::News>(analytic_solution.variables(\n                      l_max, data_matrix(i, 0), tmpl::list<Tags::News>{})))),\n              l_max)\n              .data();\n      CAPTURE(i);\n      for (size_t j = 0; j < square(l_max + 1); ++j) {\n        CHECK(data_matrix(i, 2 * j + 1) == real(analytic_news_modes.data()[j]));\n        CHECK(data_matrix(i, 2 * j + 2) == imag(analytic_news_modes.data()[j]));\n      }\n    }\n  }\n  if (file_system::check_if_file_exists(filename + \".h5\")) {\n    file_system::rm(filename + \".h5\", true);\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_SendGhVarsToCce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/Cce/Actions/SendGhVarsToCce.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/Interpolation/InterpolateOnElementTestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct mock_element {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Metavariables::volume_dim>;\n  using simple_tags = tmpl::list<\n      ::Tags::Time, domain::Tags::Mesh<Metavariables::volume_dim>,\n      ::Tags::Variables<\n          tmpl::list<InterpolateOnElementTestHelpers::Tags::TestSolution>>,\n      intrp::Tags::PointInfo<typename Metavariables::InterpolationTargetA,\n                             tmpl::size_t<Metavariables::volume_dim>>,\n      domain::Tags::Coordinates<Metavariables::volume_dim, Frame::Inertial>>;\n  using compute_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Cce::Actions::SendGhVarsToCce<\n              typename Metavariables::InterpolationTargetA>>>>;\n};\n\ntemplate <typename ElemComponent>\nstruct initialize_elements_and_queue_simple_actions {\n  template <typename InterpPointInfo, typename Runner>\n  void operator()(const DomainCreator<3>& domain_creator,\n                  const Domain<3>& domain,\n                  const std::vector<ElementId<3>>& element_ids,\n                  const InterpPointInfo& interp_point_info, Runner& runner,\n                  const double time) {\n    using elem_component = ElemComponent;\n\n    // Emplace elements.\n    for (const auto& element_id : element_ids) {\n      // 1. Get vars, mesh, and coords\n      auto [vars, mesh, inertial_coords] =\n          InterpolateOnElementTestHelpers::make_volume_data_and_mesh<\n              ElemComponent, false>(domain_creator, runner, domain, element_id,\n                                    time);\n\n      // 2. emplace element.\n      ActionTesting::emplace_component_and_initialize<elem_component>(\n          &runner, element_id,\n          {time, mesh, std::move(vars), interp_point_info,\n           std::move(inertial_coords)});\n    }\n\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n    // Call the action on all the elements.\n    for (const auto& element_id : element_ids) {\n      ActionTesting::next_action<elem_component>(make_not_null(&runner),\n                                                 element_id);\n    }\n  }\n};\n\nstruct MockComputeTargetPoints\n    : tt::ConformsTo<intrp::protocols::ComputeTargetPoints> {\n  using is_sequential = std::false_type;\n  using frame = ::Frame::Inertial;\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static tnsr::I<DataVector, 3, Frame::Inertial> points(\n      const db::DataBox<DbTags>& /*box*/,\n      const tmpl::type_<Metavariables>& /*meta*/,\n      const TemporalId& /*temporal_id*/) {\n    return tnsr::I<DataVector, 3, Frame::Inertial>{};\n  }\n};\n\nstruct MockPostInterpolationCallback\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const TemporalId& /*temporal_id*/) {}\n};\n\nstruct InterpolationTargetAImpl\n    : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  using temporal_id = ::Tags::Time;\n  using vars_to_interpolate_to_target =\n      tmpl::list<InterpolateOnElementTestHelpers::Tags::TestSolution>;\n  using compute_target_points = MockComputeTargetPoints;\n  using post_interpolation_callbacks =\n      tmpl::list<MockPostInterpolationCallback>;\n  using compute_items_on_target = tmpl::list<>;\n};\n\nstruct MockMetavariables {\n  using InterpolationTargetA = InterpolationTargetAImpl;\n  static constexpr bool use_time_dependent_maps = false;\n  static constexpr size_t volume_dim = 3;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<3>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Event, tmpl::flatten<tmpl::list<\n                              intrp::Events::InterpolateWithoutInterpComponent<\n                                  volume_dim, InterpolationTargetA,\n                                  typename InterpolationTargetAImpl::\n                                      vars_to_interpolate_to_target>>>>>;\n  };\n\n  using interpolation_target_tags = tmpl::list<InterpolationTargetA>;\n\n  using component_list =\n      tmpl::list<InterpolateOnElementTestHelpers::mock_interpolation_target<\n                     MockMetavariables, InterpolationTargetA>,\n                 mock_element<MockMetavariables>>;\n};\n\ntemplate <typename MockMetavariables>\nvoid run_test() {\n  using metavars = MockMetavariables;\n  using elem_component = mock_element<metavars>;\n  InterpolateOnElementTestHelpers::test_interpolate_on_element<\n      metavars, elem_component, false>(\n      initialize_elements_and_queue_simple_actions<elem_component>{});\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.SendGhVarsToCce\",\n                  \"[Unit][Cce]\") {\n  domain::creators::register_derived_with_charm();\n  run_test<MockMetavariables>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_TimeManagement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"Evolution/Systems/Cce/Actions/TimeManagement.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using component_being_mocked = CharacteristicEvolution<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using simple_tags =\n      db::AddSimpleTags<::Tags::TimeStepId, Tags::EndTimeFromFile>;\n  using compute_tags = db::AddComputeTags<>;\n\n  using initialize_action_list =\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags, compute_tags>>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             initialize_action_list>,\n      Parallel::PhaseActions<Parallel::Phase::Evolve,\n                             tmpl::list<Actions::ExitIfEndTimeReached>>>;\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n          phase_dependent_action_list>;\n};\n\nstruct metavariables {\n  using component_list =\n      tmpl::list<mock_characteristic_evolution<metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.TimeManagement\",\n                  \"[Unit][Cce]\") {\n  using component = mock_characteristic_evolution<metavariables>;\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n\n  const TimeStepId current_id{true, 0, Time{{0.0, 1.0}, {0, 1}}};\n  const double end_time = 2.0;\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0, {std::move(current_id), end_time});  // NOLINT\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Evolve);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK_FALSE(ActionTesting::get_terminate<component>(runner, 0));\n\n  auto& box = ActionTesting::get_databox<component>(make_not_null(&runner), 0);\n  db::mutate<::Tags::TimeStepId>(\n      [](const gsl::not_null<TimeStepId*> time_step_id) {\n        *time_step_id = TimeStepId{true, 0, Time{{1.5, 2.5}, {3, 4}}};\n      },\n      make_not_null(&box));\n\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_terminate<component>(runner, 0));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_UpdateGauge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/UpdateGauge.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nnamespace {\nusing real_tags_to_generate =\n    tmpl::list<Tags::CauchyCartesianCoords, Tags::PartiallyFlatCartesianCoords>;\n\nusing real_tags_to_compute =\n    tmpl::list<Tags::CauchyAngularCoords, Tags::PartiallyFlatAngularCoords>;\n\nusing swsh_tags_to_compute =\n    tmpl::list<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n               Tags::PartiallyFlatGaugeOmega, Tags::CauchyGaugeC,\n               Tags::CauchyGaugeD, Tags::CauchyGaugeOmega,\n               Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                                Spectral::Swsh::Tags::Eth>,\n               Spectral::Swsh::Tags::Derivative<Tags::CauchyGaugeOmega,\n                                                Spectral::Swsh::Tags::Eth>>;\n\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution {\n  using simple_tags = tmpl::push_back<\n      db::AddSimpleTags<::Tags::Variables<tmpl::append<real_tags_to_generate,\n                                                       real_tags_to_compute>>,\n                        ::Tags::Variables<swsh_tags_to_compute>>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::PartiallyFlatAngularCoords>>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::UpdateGauge<Metavariables::evolve_ccm>>>>;\n};\n\nstruct metavariables {\n  static constexpr bool evolve_ccm = true;\n  using component_list =\n      tmpl::list<mock_characteristic_evolution<metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.UpdateGauge\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  // limited l_max distribution because test depends on an analytic\n  // basis function with factorials.\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(gen);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n  CAPTURE(l_max);\n\n  using component = mock_characteristic_evolution<metavariables>;\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{l_max}};\n  Variables<tmpl::append<real_tags_to_generate, real_tags_to_compute>>\n      real_variables{Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  Variables<swsh_tags_to_compute> swsh_variables{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n  tmpl::for_each<real_tags_to_generate>([&real_variables, &gen,\n                                         &coefficient_distribution,\n                                         &l_max](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    SpinWeighted<ComplexModalVector, 0> generated_modes{\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n    SpinWeighted<ComplexDataVector, 0> generated_data{\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    for (auto& tensor_component : get<tag>(real_variables)) {\n      Spectral::Swsh::TestHelpers::generate_swsh_modes<0>(\n          make_not_null(&generated_modes.data()), make_not_null(&gen),\n          make_not_null(&coefficient_distribution), 1, l_max);\n      Spectral::Swsh::inverse_swsh_transform(\n          l_max, 1, make_not_null(&generated_data), generated_modes);\n      // aggressive filter to make the uniformly generated random modes\n      // somewhat reasonable\n      Spectral::Swsh::filter_swsh_boundary_quantity(\n          make_not_null(&generated_data), l_max, l_max / 2);\n      tensor_component = real(generated_data.data());\n    }\n  });\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {real_variables, swsh_variables, Spectral::Swsh::SwshInterpolator{},\n       Spectral::Swsh::SwshInterpolator{}});\n  auto expected_box = db::create<\n      tmpl::append<component::simple_tags, db::AddSimpleTags<Tags::LMax>>>(\n      std::move(real_variables), std::move(swsh_variables),\n      Spectral::Swsh::SwshInterpolator{}, Spectral::Swsh::SwshInterpolator{},\n      l_max);\n\n  runner.set_phase(Parallel::Phase::Evolve);\n  // apply the `UpdateGauge` action\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n\n  // apply the corresponding mutators to the `expected_box`\n  db::mutate_apply<GaugeUpdateAngularFromCartesian<\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      make_not_null(&expected_box));\n  db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      make_not_null(&expected_box));\n  db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n      make_not_null(&expected_box));\n  db::mutate_apply<\n      GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                       Tags::PartiallyFlatGaugeOmega>>(\n      make_not_null(&expected_box));\n\n  if (metavariables::evolve_ccm) {\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&expected_box));\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&expected_box));\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::PartiallyFlatAngularCoords>>(\n        make_not_null(&expected_box));\n    db::mutate_apply<GaugeUpdateOmega<Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n                                      Tags::CauchyGaugeOmega>>(\n        make_not_null(&expected_box));\n  }\n\n  tmpl::for_each<tmpl::append<real_tags_to_compute, swsh_tags_to_compute>>(\n      [&runner, &expected_box](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        const auto& test_lhs =\n            ActionTesting::get_databox_tag<component, tag>(runner, 0);\n        const auto& test_rhs = db::get<tag>(expected_box);\n        CHECK_ITERABLE_APPROX(test_lhs, test_rhs);\n      });\n\n  // verify the interpolators are the same by interpolating a random set of\n  // modes.\n  SpinWeighted<ComplexModalVector, 2> generated_modes{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n  Spectral::Swsh::TestHelpers::generate_swsh_modes<2>(\n      make_not_null(&generated_modes.data()), make_not_null(&gen),\n      make_not_null(&coefficient_distribution), 1, l_max);\n\n  const Spectral::Swsh::SwshInterpolator& computed_interpolator =\n      ActionTesting::get_databox_tag<\n          component,\n          Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>>(\n          runner, 0);\n  const Spectral::Swsh::SwshInterpolator& expected_interpolator = db::get<\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>>(\n      expected_box);\n\n  SpinWeighted<ComplexDataVector, 2> interpolated_points_from_computed{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  SpinWeighted<ComplexDataVector, 2> interpolated_points_from_expected{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  computed_interpolator.interpolate(\n      make_not_null(&interpolated_points_from_computed),\n      Spectral::Swsh::libsharp_to_goldberg_modes(generated_modes, l_max));\n  expected_interpolator.interpolate(\n      make_not_null(&interpolated_points_from_expected),\n      Spectral::Swsh::libsharp_to_goldberg_modes(generated_modes, l_max));\n  CHECK_ITERABLE_APPROX(interpolated_points_from_computed,\n                        interpolated_points_from_expected);\n\n  if (metavariables::evolve_ccm) {\n    const Spectral::Swsh::SwshInterpolator& computed_interpolator_inertial =\n        ActionTesting::get_databox_tag<component,\n                                       Spectral::Swsh::Tags::SwshInterpolator<\n                                           Tags::PartiallyFlatAngularCoords>>(\n            runner, 0);\n    const Spectral::Swsh::SwshInterpolator& expected_interpolator_inertial =\n        db::get<Spectral::Swsh::Tags::SwshInterpolator<\n            Tags::PartiallyFlatAngularCoords>>(expected_box);\n\n    SpinWeighted<ComplexDataVector, 2>\n        interpolated_points_from_computed_inertial{\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    SpinWeighted<ComplexDataVector, 2>\n        interpolated_points_from_expected_inertial{\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    computed_interpolator_inertial.interpolate(\n        make_not_null(&interpolated_points_from_computed_inertial),\n        Spectral::Swsh::libsharp_to_goldberg_modes(generated_modes, l_max));\n    expected_interpolator_inertial.interpolate(\n        make_not_null(&interpolated_points_from_expected_inertial),\n        Spectral::Swsh::libsharp_to_goldberg_modes(generated_modes, l_max));\n    CHECK_ITERABLE_APPROX(interpolated_points_from_computed_inertial,\n                          interpolated_points_from_expected_inertial);\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Actions/Test_WriteScriBondiQuantities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Evolution/Systems/Cce/Actions/WriteScriBondiQuantities.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/IO/Observers/ObserverHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Cce.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestObservers_detail;\n\nnamespace {\nusing registration_list = tmpl::list<>;\n\nusing metavariables = helpers::Metavariables<registration_list>;\nusing obs_component = helpers::observer_component<metavariables>;\nusing obs_writer = helpers::observer_writer_component<metavariables>;\nusing element_comp =\n    helpers::element_component<metavariables, registration_list>;\n\nvoid test() {\n  const std::string output_file_prefix = \"WriteScriBondiQuantities\";\n  const std::string output_file = output_file_prefix + \".h5\";\n  if (file_system::check_if_file_exists(output_file)) {\n    file_system::rm(output_file, true);\n  }\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {Domain<3>{},\n       std::unordered_map<\n           std::string,\n           std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{},\n       output_file_prefix, \"\"}};\n  ActionTesting::emplace_nodegroup_component<obs_writer>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_writer>(make_not_null(&runner), 0);\n  }\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& cache = ActionTesting::cache<obs_writer>(runner, 0);\n  auto& obs_writer_proxy = Parallel::get_parallel_component<obs_writer>(cache);\n\n  const size_t l_max = 12;\n  const size_t num_points = 2 * square(l_max + 1) + 1;\n\n  // We don't actually care here about what data was written, just that it *was*\n  // written\n  std::unordered_set<std::string> bondi_variables{\"EthInertialRetardedTime\",\n                                                  \"News\",\n                                                  \"Psi0\",\n                                                  \"Psi1\",\n                                                  \"Psi2\",\n                                                  \"Psi3\",\n                                                  \"Psi4\",\n                                                  \"Strain\"};\n  std::unordered_map<std::string, std::vector<double>> fake_data{};\n  for (const std::string& bondi_var : bondi_variables) {\n    fake_data[bondi_var] = std::vector<double>(num_points, 0.0);\n  }\n\n  // First test the threaded action\n  Parallel::threaded_action<Cce::Actions::WriteScriBondiQuantities>(\n      obs_writer_proxy[0], \"/TestBondiThreaded\", l_max, fake_data);\n\n  CHECK(ActionTesting::number_of_queued_threaded_actions<obs_writer>(runner,\n                                                                     0) == 1);\n  ActionTesting::invoke_queued_threaded_action<obs_writer>(\n      make_not_null(&runner), 0);\n\n  {\n    h5::H5File<h5::AccessType::ReadOnly> h5_file{output_file};\n    const auto groups = h5_file.groups();\n    REQUIRE(alg::found(groups, \"TestBondiThreaded.cce\"));\n    const auto& cce_file = h5_file.get<h5::Cce>(\"TestBondiThreaded\", l_max, 0);\n    const std::unordered_map<std::string, Matrix> data_matrix =\n        cce_file.get_data();\n    for (const std::string& bondi_var : bondi_variables) {\n      REQUIRE(data_matrix.contains(bondi_var));\n      CHECK(data_matrix.at(bondi_var).rows() == 1);\n      CHECK(data_matrix.at(bondi_var).columns() == num_points);\n    }\n  }\n\n  // Now test the local synchronous action. However, the action testing\n  // framework can't handle local synchronous actions so we just call the apply\n  // function itself.\n  auto& box = ActionTesting::get_databox<obs_writer>(make_not_null(&runner), 0);\n  Parallel::NodeLock lock{};\n\n  Cce::Actions::WriteScriBondiQuantities::apply<void>(\n      box, make_not_null(&lock), cache, \"/TestBondiSync\", l_max, fake_data);\n\n  {\n    h5::H5File<h5::AccessType::ReadOnly> h5_file{output_file};\n    const auto groups = h5_file.groups();\n    REQUIRE(alg::found(groups, \"TestBondiSync.cce\"));\n    const auto& cce_file = h5_file.get<h5::Cce>(\"TestBondiSync\", l_max, 0);\n    const std::unordered_map<std::string, Matrix> data_matrix =\n        cce_file.get_data();\n    for (const std::string& bondi_var : bondi_variables) {\n      REQUIRE(data_matrix.contains(bondi_var));\n      CHECK(data_matrix.at(bondi_var).rows() == 1);\n      CHECK(data_matrix.at(bondi_var).columns() == num_points);\n    }\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(Cce::Actions::WriteScriBondiQuantities::apply<void>(\n                        box, make_not_null(&lock), cache, \"/TestBondiFail\",\n                        l_max + 1, fake_data),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Some data sent to WriteScriBondiQuantities is \"\n                        \"not of the proper size 393\"));\n#endif\n\n  if (file_system::check_if_file_exists(output_file)) {\n    file_system::rm(output_file, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Actions.WriteScriBondiQuantities\",\n                  \"[Unit][Cce]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Cce_AnalyticSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_BouncingBlackHole.cpp\n  Test_GaugeWave.cpp\n  Test_LinearizedBondiSachs.cpp\n  Test_RobinsonTrautman.cpp\n  Test_RotatingSchwarzschild.cpp\n  Test_TeukolskyWave.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Cce\n  CceAnalyticSolutionsHelpers\n  DataStructures\n  GeneralizedHarmonic\n  GeneralRelativity\n  Utilities\n  SpinWeightedSphericalHarmonics\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/GaugeWave.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom numpy import array, cos, exp, sin\n\n\ndef coordinate_wave_function(t, r, f, a, tpeak, duration):\n    u = t - r\n    return a * sin(f * u) * exp(-((u - tpeak) ** 2) / duration**2)\n\n\ndef du_coordinate_wave_function(t, r, f, a, tpeak, duration):\n    u = t - r\n    return (\n        a\n        * (f * cos(f * u) - 2.0 * sin(f * u) * (u - tpeak) / duration**2)\n        * exp(-((u - tpeak) ** 2) / duration**2)\n    )\n\n\ndef du_du_coordinate_wave_function(t, r, f, a, tpeak, duration):\n    u = t - r\n    return (\n        -a * f**2 * sin(f * u)\n        - 2.0 * a * f * cos(f * u) * (u - tpeak) / duration**2\n        - 2.0 * a * sin(f * u) / duration**2\n        - 2.0\n        * (u - tpeak)\n        / duration**2\n        * (\n            a * f * cos(f * u)\n            - 2.0 * a * sin(f * u) * (u - tpeak) / duration**2\n        )\n    ) * exp(-((u - tpeak) ** 2) / duration**2)\n\n\ndef spherical_metric(sin_theta, cos_theta, t, r, m, f, a, tpeak, duration):\n    wave_func = coordinate_wave_function(t, r, f, a, tpeak, duration)\n    du_wave_func = du_coordinate_wave_function(t, r, f, a, tpeak, duration)\n    g_tt = -(r - 2.0 * m) * (r + du_wave_func) ** 2 / r**3\n    g_tr = (1 + du_wave_func / r) * (\n        2.0 * m / r\n        + (1.0 - 2.0 * m / r) * (du_wave_func / r + wave_func / r**2)\n    )\n    g_rr = (1 - du_wave_func / r - wave_func / r**2) * (\n        1.0\n        + 2.0 * m / r\n        + (1.0 - 2.0 * m / r) * (du_wave_func / r + wave_func / r**2)\n    )\n    return array(\n        [\n            [g_tt, g_tr, 0.0, 0.0],\n            [g_tr, g_rr, 0.0, 0.0],\n            [0.0, 0.0, r**2, 0.0],\n            [0.0, 0.0, 0.0, r**2],\n        ]\n    )\n\n\ndef dt_spherical_metric(sin_theta, cos_theta, t, r, m, f, a, tpeak, duration):\n    wave_func = coordinate_wave_function(t, r, f, a, tpeak, duration)\n    du_wave_func = du_coordinate_wave_function(t, r, f, a, tpeak, duration)\n    du_du_wave_func = du_du_coordinate_wave_function(\n        t, r, f, a, tpeak, duration\n    )\n    dt_g_tt = (\n        -2.0\n        * (1.0 - 2.0 * m / r)\n        * (1.0 + du_wave_func / r)\n        * (du_du_wave_func / r)\n    )\n    dt_g_tr = (du_du_wave_func / r) * (\n        2.0 * m / r\n        + (1.0 - 2.0 * m / r) * (du_wave_func / r + wave_func / r**2)\n    ) + (1.0 + du_wave_func / r) * (1.0 - 2.0 * m / r) * (\n        du_du_wave_func / r + du_wave_func / r**2\n    )\n    dt_g_rr = (-du_du_wave_func / r - du_wave_func / r**2) * (\n        1.0\n        + 2.0 * m / r\n        + (1.0 - 2.0 * m / r) * (du_wave_func / r + wave_func / r**2)\n    ) + (1.0 - du_wave_func / r - wave_func / r**2) * (1.0 - 2.0 * m / r) * (\n        du_du_wave_func / r + du_wave_func / r**2\n    )\n    return array(\n        [\n            [dt_g_tt, dt_g_tr, 0.0, 0.0],\n            [dt_g_tr, dt_g_rr, 0.0, 0.0],\n            [0.0, 0.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0, 0.0],\n        ]\n    )\n\n\ndef dr_spherical_metric(sin_theta, cos_theta, t, r, m, f, a, tpeak, duration):\n    wave_func = coordinate_wave_function(t, r, f, a, tpeak, duration)\n    du_wave_func = du_coordinate_wave_function(t, r, f, a, tpeak, duration)\n    dt_metric = dt_spherical_metric(\n        sin_theta, cos_theta, t, r, m, f, a, tpeak, duration\n    )\n    dr_g_tt = (\n        -(2.0 * m / r**2) * (1.0 + du_wave_func / r) ** 2\n        + 2.0\n        * (1.0 - 2.0 * m / r)\n        * (1.0 + du_wave_func / r)\n        * du_wave_func\n        / r**2\n    )\n    dr_g_tr = -du_wave_func / r**2 * (\n        2.0 * m / r\n        + (1.0 - 2.0 * m / r) * (du_wave_func / r + wave_func / r**2)\n    ) + (1.0 + du_wave_func / r) * (\n        -2.0 * m / r**2\n        + 2.0 * m / r**2 * (du_wave_func / r + wave_func / r**2)\n        + (1.0 - 2.0 * m / r)\n        * (-du_wave_func / r**2 - 2.0 * wave_func / r**3)\n    )\n    dr_g_rr = (du_wave_func / r**2 + 2.0 * wave_func / r**3) * (\n        1.0\n        + 2.0 * m / r\n        + (1.0 - 2.0 * m / r) * (du_wave_func / r + wave_func / r**2)\n    ) + (1.0 - du_wave_func / r - wave_func / r**2) * (\n        -2.0 * m / r**2\n        + 2.0 * m / r**2 * (du_wave_func / r + wave_func / r**2)\n        + (1.0 - 2.0 * m / r)\n        * (-du_wave_func / r**2 - 2.0 * wave_func / r**3)\n    )\n    return array(\n        [\n            [dr_g_tt - dt_metric[0, 0], dr_g_tr - dt_metric[0, 1], 0.0, 0.0],\n            [dr_g_tr - dt_metric[1, 0], dr_g_rr - dt_metric[1, 1], 0.0, 0.0],\n            [0.0, 0.0, 2.0 * r, 0.0],\n            [0.0, 0.0, 0.0, 2.0 * r],\n        ]\n    )\n\n\ndef news(sin_theta, t, r, m, f, a, tpeak, duration):\n    return complex(0.0, 0.0)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef spherical_metric(sin_theta, cos_theta, t, r, m, f):\n    return np.array(\n        [\n            [\n                (-(1.0 - 2.0 * m / r - f**2 * r**2 * sin_theta**2)),\n                0.0,\n                0.0,\n                r**2 * f * sin_theta,\n            ],\n            [0.0, 1.0 / (1.0 - 2.0 * m / r), 0.0, 0.0],\n            [0.0, 0.0, r**2, 0.0],\n            [r**2 * f * sin_theta, 0.0, 0.0, r**2],\n        ]\n    )\n\n\ndef dr_spherical_metric(sin_theta, cos_theta, t, r, m, f):\n    return np.array(\n        [\n            [\n                (-(2.0 * m / r**2 - 2.0 * f**2 * r * sin_theta**2)),\n                0.0,\n                0.0,\n                2.0 * r * f * sin_theta,\n            ],\n            [0.0, -2.0 * m / (r - 2.0 * m) ** 2, 0.0, 0.0],\n            [0.0, 0.0, 2.0 * r, 0.0],\n            [2.0 * r * f * sin_theta, 0.0, 0.0, 2.0 * r],\n        ]\n    )\n\n\ndef dt_spherical_metric(sin_theta, cos_theta, t, r, m, f):\n    return np.zeros((4, 4))\n\n\ndef news(sin_theta, t, r, m, f):\n    return complex(0.0, 0.0)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/Test_BouncingBlackHole.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/BouncingBlackHole.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce {\nnamespace {\ntnsr::A<DataVector, 3> bouncing_bh_mapped_coordinates(\n    const tnsr::A<DataVector, 3>& unmapped_coordinates, const double amplitude,\n    const double period) {\n  tnsr::A<DataVector, 3> result{get<0>(unmapped_coordinates).size()};\n  get<0>(result) = get<0>(unmapped_coordinates);\n  get<1>(result) =\n      get<1>(unmapped_coordinates) +\n      amplitude *\n          pow<4>(sin(2.0 * M_PI * get<0>(unmapped_coordinates) / period));\n  get<2>(result) = get<2>(unmapped_coordinates);\n  get<3>(result) = get<3>(unmapped_coordinates);\n  return result;\n}\n\ntnsr::aB<DataVector, 3> bouncing_bh_mapped_jacobian(\n    const tnsr::A<DataVector, 3>& unmapped_coordinates, const double amplitude,\n    const double period) {\n  tnsr::aB<DataVector, 3> result{get<0>(unmapped_coordinates).size(), 0.0};\n  get<0, 0>(result) = 1.0;\n  get<0, 1>(result) =\n      4.0 * amplitude *\n      pow<3>(sin(2.0 * M_PI * get<0>(unmapped_coordinates) / period)) *\n      cos(2.0 * M_PI * get<0>(unmapped_coordinates) / period) * 2.0 * M_PI /\n      period;\n  get<1, 1>(result) = 1.0;\n  get<2, 2>(result) = 1.0;\n  get<3, 3>(result) = 1.0;\n  return result;\n}\n\ntnsr::aaB<DataVector, 3> bouncing_bh_mapped_hessian(\n    const tnsr::A<DataVector, 3>& unmapped_coordinates, const double amplitude,\n    const double period) {\n  tnsr::aaB<DataVector, 3> result{get<0>(unmapped_coordinates).size(), 0.0};\n  get<0, 0, 1>(result) =\n      8.0 * amplitude * square(M_PI / period) *\n      (cos(4.0 * M_PI * get<0>(unmapped_coordinates) / period) -\n       cos(8.0 * M_PI * get<0>(unmapped_coordinates) / period));\n  return result;\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.BouncingBlackHole\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> large_parameter_dist{25.0, 100.0};\n  UniformCustomDistribution<double> small_parameter_dist{0.5, 2.0};\n\n  const double amplitude = small_parameter_dist(gen);\n  const double period = large_parameter_dist(gen);\n  const double mass = small_parameter_dist(gen);\n\n  const double extraction_radius = large_parameter_dist(gen);\n  const size_t l_max = 12;\n  const double time = large_parameter_dist(gen);\n\n  tnsr::A<DataVector, 3> unmapped_coordinates{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  const auto& collocation_metadata =\n      Spectral::Swsh::cached_collocation_metadata<\n          Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation_metadata) {\n    get<1>(unmapped_coordinates)[collocation_point.offset] =\n        extraction_radius * cos(collocation_point.phi) *\n        sin(collocation_point.theta);\n    get<2>(unmapped_coordinates)[collocation_point.offset] =\n        extraction_radius * sin(collocation_point.phi) *\n        sin(collocation_point.theta);\n    get<3>(unmapped_coordinates)[collocation_point.offset] =\n        extraction_radius * cos(collocation_point.theta);\n  }\n  get<0>(unmapped_coordinates) = time;\n\n  const auto mapped_coordinates =\n      bouncing_bh_mapped_coordinates(unmapped_coordinates, amplitude, period);\n\n  const auto mapped_radius = sqrt(square(get<1>(mapped_coordinates)) +\n                                  square(get<2>(mapped_coordinates)) +\n                                  square(get<3>(mapped_coordinates)));\n\n  tnsr::aa<DataVector, 3> schwarzschild_metric_at_mapped_coordinates{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  get<0, 0>(schwarzschild_metric_at_mapped_coordinates) =\n      2.0 * mass / mapped_radius - 1.0;\n  for (size_t i = 0; i < 3; ++i) {\n    schwarzschild_metric_at_mapped_coordinates.get(0, i + 1) =\n        2.0 * mass / mapped_radius *\n        (mapped_coordinates.get(i + 1) / mapped_radius);\n    schwarzschild_metric_at_mapped_coordinates.get(i + 1, i + 1) =\n        2.0 * mass * square(mapped_coordinates.get(i + 1)) /\n            pow<3>(mapped_radius) +\n        1.0;\n    for (size_t j = 0; j < i; ++j) {\n      schwarzschild_metric_at_mapped_coordinates.get(i + 1, j + 1) =\n          2.0 * mass * mapped_coordinates.get(i + 1) *\n          mapped_coordinates.get(j + 1) / pow<3>(mapped_radius);\n    }\n  }\n\n  // Apply the Jacobian to determine the down-index metric in the new coordinate\n  // system.\n  const auto mapped_jacobian =\n      bouncing_bh_mapped_jacobian(unmapped_coordinates, amplitude, period);\n  tnsr::aa<DataVector, 3> mapped_schwarzschild_metric{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max), 0.0};\n\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      for (size_t c = 0; c < 4; ++c) {\n        for (size_t d = 0; d < 4; ++d) {\n          mapped_schwarzschild_metric.get(a, b) +=\n              mapped_jacobian.get(a, c) * mapped_jacobian.get(b, d) *\n              schwarzschild_metric_at_mapped_coordinates.get(c, d);\n        }\n      }\n    }\n  }\n\n  tnsr::abb<DataVector, 3> d_schwarzschild_metric_at_mapped_coordinates{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max), 0.0};\n  // vanishing time derivative, so we just skip it in the loop\n  for (size_t k = 0; k < 3; ++k) {\n    d_schwarzschild_metric_at_mapped_coordinates.get(k + 1, 0, 0) =\n        -2.0 * mass * mapped_coordinates.get(k + 1) / pow<3>(mapped_radius);\n    for (size_t i = 0; i < 3; ++i) {\n      if (k == i) {\n        d_schwarzschild_metric_at_mapped_coordinates.get(k + 1, 0, i + 1) =\n            2.0 * mass / square(mapped_radius) -\n            4.0 * mass * square(mapped_coordinates.get(k + 1)) /\n                pow<4>(mapped_radius);\n      } else {\n        d_schwarzschild_metric_at_mapped_coordinates.get(k + 1, 0, i + 1) =\n            -4.0 * mass * mapped_coordinates.get(k + 1) *\n            mapped_coordinates.get(i + 1) / pow<4>(mapped_radius);\n      }\n      for (size_t j = i; j < 3; ++j) {\n        if (k == i) {\n          if (i == j) {\n            d_schwarzschild_metric_at_mapped_coordinates.get(k + 1, j + 1,\n                                                             i + 1) =\n                4.0 * mass * mapped_coordinates.get(j + 1) /\n                    pow<3>(mapped_radius) -\n                6.0 * mass * mapped_coordinates.get(k + 1) *\n                    mapped_coordinates.get(j + 1) *\n                    mapped_coordinates.get(i + 1) / pow<5>(mapped_radius);\n          } else {\n            d_schwarzschild_metric_at_mapped_coordinates.get(k + 1, j + 1,\n                                                             i + 1) =\n                2.0 * mass * mapped_coordinates.get(j + 1) /\n                    pow<3>(mapped_radius) -\n                6.0 * mass * mapped_coordinates.get(k + 1) *\n                    mapped_coordinates.get(j + 1) *\n                    mapped_coordinates.get(i + 1) / pow<5>(mapped_radius);\n          }\n        } else if (k == j) {\n          d_schwarzschild_metric_at_mapped_coordinates.get(k + 1, j + 1,\n                                                           i + 1) =\n              2.0 * mass * mapped_coordinates.get(i + 1) /\n                  pow<3>(mapped_radius) -\n              6.0 * mass * mapped_coordinates.get(k + 1) *\n                  mapped_coordinates.get(j + 1) *\n                  mapped_coordinates.get(i + 1) / pow<5>(mapped_radius);\n        } else {\n          d_schwarzschild_metric_at_mapped_coordinates.get(k + 1, j + 1,\n                                                           i + 1) =\n              -6.0 * mass * mapped_coordinates.get(k + 1) *\n              mapped_coordinates.get(j + 1) * mapped_coordinates.get(i + 1) /\n              pow<5>(mapped_radius);\n        }\n      }\n    }\n  }\n\n  const auto mapped_hessian =\n      bouncing_bh_mapped_hessian(unmapped_coordinates, amplitude, period);\n\n  tnsr::abb<DataVector, 3> mapped_d_schwarzschild_metric{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max), 0.0};\n\n  for (size_t c = 0; c < 4; ++c) {\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t b = a; b < 4; ++b) {\n        for (size_t d = 0; d < 4; ++d) {\n          for (size_t e = 0; e < 4; ++e) {\n            mapped_d_schwarzschild_metric.get(c, a, b) +=\n                mapped_hessian.get(a, c, d) *\n                    schwarzschild_metric_at_mapped_coordinates.get(d, e) *\n                    mapped_jacobian.get(b, e) +\n                mapped_jacobian.get(a, d) * mapped_hessian.get(b, c, e) *\n                    schwarzschild_metric_at_mapped_coordinates.get(d, e);\n            for (size_t f = 1; f < 4; ++f) {\n              mapped_d_schwarzschild_metric.get(c, a, b) +=\n                  mapped_jacobian.get(a, d) * mapped_jacobian.get(b, e) *\n                  mapped_jacobian.get(c, f) *\n                  d_schwarzschild_metric_at_mapped_coordinates.get(f, d, e);\n            }\n          }\n        }\n      }\n    }\n  }\n  // Now we test these tensors against the corresponding tensors from the\n  // generator\n  const auto analytic_solution =\n      Solutions::BouncingBlackHole{amplitude, extraction_radius, mass, period};\n  const auto boundary_tuple = analytic_solution.variables(\n      l_max, time, Solutions::BouncingBlackHole::tags{});\n  const auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_tuple);\n  const auto& dt_spacetime_metric =\n      get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(boundary_tuple);\n  const auto& d_spacetime_metric =\n      get<gh::Tags::Phi<DataVector, 3>>(boundary_tuple);\n\n  const auto serialized_and_deserialized_analytic_solution =\n      serialize_and_deserialize(analytic_solution);\n  const auto boundary_tuple_from_serialized =\n      serialized_and_deserialized_analytic_solution.variables(\n          l_max, time, tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>>{});\n  const auto& spacetime_metric_from_serialized =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(\n          boundary_tuple_from_serialized);\n  CHECK_ITERABLE_APPROX(spacetime_metric_from_serialized, spacetime_metric);\n\n  CHECK_ITERABLE_APPROX(spacetime_metric, mapped_schwarzschild_metric);\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b < 4; ++b) {\n      CHECK_ITERABLE_APPROX(dt_spacetime_metric.get(a, b),\n                            mapped_d_schwarzschild_metric.get(0, a, b));\n    }\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t b = 0; b < 4; ++b) {\n        CHECK_ITERABLE_APPROX(d_spacetime_metric.get(i, a, b),\n                              mapped_d_schwarzschild_metric.get(i + 1, a, b));\n      }\n    }\n  }\n\n  // check the coordinate values from the analytic solution (actually testing\n  // functionality from the abstract base class `WorldtubeData`).\n  const auto& cartesian_coordinates =\n      get<Tags::CauchyCartesianCoords>(boundary_tuple);\n  const auto& dr_cartesian_coordinates =\n      get<Tags::Dr<Tags::CauchyCartesianCoords>>(boundary_tuple);\n  const DataVector radius_squared_from_coordinates =\n      square(get<0>(cartesian_coordinates)) +\n      square(get<1>(cartesian_coordinates)) +\n      square(get<2>(cartesian_coordinates));\n  for (const auto& val : radius_squared_from_coordinates) {\n    CHECK(approx(val) == square(extraction_radius));\n  }\n  const DataVector radius_from_coordinates =\n      (get<0>(cartesian_coordinates) * get<0>(dr_cartesian_coordinates) +\n       get<1>(cartesian_coordinates) * get<1>(dr_cartesian_coordinates) +\n       get<2>(cartesian_coordinates) * get<2>(dr_cartesian_coordinates));\n  for (const auto& val : radius_from_coordinates) {\n    CHECK(approx(val) == extraction_radius);\n  }\n\n  Solutions::TestHelpers::check_adm_metric_quantities(\n      boundary_tuple, mapped_schwarzschild_metric, dt_spacetime_metric,\n      d_spacetime_metric);\n  Solutions::TestHelpers::test_initialize_j(\n      l_max, 5_st, extraction_radius, time,\n      std::make_unique<InitializeJ::InverseCubic<false>>(),\n      analytic_solution.get_clone());\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/Test_GaugeWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <memory>\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/GaugeWave.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce::Solutions {\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.GaugeWave\", \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> radius_dist{5.0, 10.0};\n  UniformCustomDistribution<double> parameter_dist{0.5, 2.0};\n  const double extraction_radius = radius_dist(gen);\n  const size_t l_max = 16;\n  // use a low frequency so that the dimensionless parameter in the metric is ~1\n  const double frequency = parameter_dist(gen) / 5.0;\n  const double mass = parameter_dist(gen);\n  const double amplitude = parameter_dist(gen) / 10.0;\n  const double peak_time = parameter_dist(gen);\n  const double duration = parameter_dist(gen);\n  TestHelpers::SphericalSolutionWrapper<GaugeWave> boundary_solution{\n      extraction_radius, mass, frequency, amplitude, peak_time, duration};\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Cce/AnalyticSolutions/\"};\n  boundary_solution.test_spherical_metric(\n      \"GaugeWave\", l_max, peak_time - 0.1 * duration,\n      Approx::custom().epsilon(1.e-11).scale(1.0), extraction_radius, mass,\n      frequency, amplitude, peak_time, duration);\n  boundary_solution.test_serialize_and_deserialize(l_max,\n                                                   peak_time - 0.1 * duration);\n  TestHelpers::test_initialize_j(\n      l_max, 5_st, extraction_radius, peak_time - 0.1 * duration,\n      std::make_unique<InitializeJ::InverseCubic<false>>(),\n      boundary_solution.get_clone());\n}\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/Test_LinearizedBondiSachs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticBoundaryDataManager.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryDataTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce::Solutions {\n\nnamespace {\ntemplate <int Spin>\nvoid check_22_and_33_modes(\n    const SpinWeighted<ComplexModalVector, Spin>& goldberg_modes,\n    const std::complex<double>& expected_22_mode,\n    const std::complex<double>& expected_33_mode, const size_t l_max,\n    Approx bondi_approx) {\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      goldberg_modes.data()[Spectral::Swsh::goldberg_mode_index(l_max, 2, 2)],\n      expected_22_mode, bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      goldberg_modes.data()[Spectral::Swsh::goldberg_mode_index(l_max, 2, -2)],\n      expected_22_mode, bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      goldberg_modes.data()[Spectral::Swsh::goldberg_mode_index(l_max, 3, 3)],\n      expected_33_mode, bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      goldberg_modes.data()[Spectral::Swsh::goldberg_mode_index(l_max, 3, -3)],\n      -expected_33_mode, bondi_approx);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.LinearizedBondiSachs\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> parameter_dist{0.1, 1.0};\n  const double extraction_radius = 5.0;\n  const size_t l_max = 24;\n  const double frequency = parameter_dist(gen);\n  const std::complex<double> c_2a =\n      0.01 * make_with_random_values<std::complex<double>>(\n                 make_not_null(&gen), make_not_null(&parameter_dist));\n  const std::complex<double> c_3a =\n      0.01 * make_with_random_values<std::complex<double>>(\n                 make_not_null(&gen), make_not_null(&parameter_dist));\n  const std::complex<double> c_2b = 3.0 * c_2a / square(frequency);\n  const std::complex<double> c_3b =\n      -3.0 * std::complex<double>(0.0, 1.0) * c_3a / pow<3>(frequency);\n\n  const LinearizedBondiSachs boundary_solution{\n      {c_2a, c_3a}, extraction_radius, frequency};\n  const double time = 10.0 * parameter_dist(gen);\n  const auto boundary_data = boundary_solution.variables(\n      l_max, time, Solutions::LinearizedBondiSachs::tags{});\n  const auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_data);\n  // check the serialization\n  const auto serialized_and_deserialized_analytic_solution =\n      serialize_and_deserialize(boundary_solution);\n  const auto boundary_tuple_from_serialized =\n      serialized_and_deserialized_analytic_solution.variables(\n          l_max, time, tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>>{});\n  const auto& spacetime_metric_from_serialized =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(\n          boundary_tuple_from_serialized);\n  CHECK_ITERABLE_APPROX(spacetime_metric_from_serialized, spacetime_metric);\n\n  CartesianiSphericalJ inverse_jacobian{get<0, 0>(spacetime_metric).size()};\n  boundary_solution.inverse_jacobian(make_not_null(&inverse_jacobian), l_max);\n  const auto bondi_quantities =\n      TestHelpers::extract_bondi_scalars_from_cartesian_metric(\n          spacetime_metric, inverse_jacobian, extraction_radius);\n\n  const auto& dt_spacetime_metric =\n      get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(boundary_data);\n  const auto dt_bondi_quantities =\n      TestHelpers::extract_dt_bondi_scalars_from_cartesian_metric(\n          dt_spacetime_metric, spacetime_metric, inverse_jacobian,\n          extraction_radius);\n\n  CartesianiSphericalJ dr_inverse_jacobian{get<0, 0>(spacetime_metric).size()};\n  boundary_solution.dr_inverse_jacobian(make_not_null(&dr_inverse_jacobian),\n                                        l_max);\n  const auto& d_spacetime_metric =\n      get<gh::Tags::Phi<DataVector, 3>>(boundary_data);\n  const auto& dr_cartesian_coordinates =\n      get<Tags::Dr<Tags::CauchyCartesianCoords>>(boundary_data);\n  tnsr::aa<DataVector, 3> dr_spacetime_metric{\n      get<0, 0>(spacetime_metric).size(), 0.0};\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      for (size_t i = 0; i < 3; ++i) {\n        dr_spacetime_metric.get(a, b) +=\n            dr_cartesian_coordinates.get(i) * d_spacetime_metric.get(i, a, b);\n      }\n    }\n  }\n  const auto dr_bondi_quantities =\n      TestHelpers::extract_dr_bondi_scalars_from_cartesian_metric(\n          dr_spacetime_metric, spacetime_metric, inverse_jacobian,\n          dr_inverse_jacobian, extraction_radius);\n\n  // Note for checking the bondi quantities, the best we can do is extract its\n  // mode content and compare it to the formula implemented in the analytic\n  // solution. The test that the formula in the analytic solution is correct, we\n  // have to actually run the analytic solution in the evolution system.\n\n  Approx bondi_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  // check that beta and its derivatives are as close to zero as we can\n  // expect given the reconstruction of the quantity from the metric.\n  const auto& beta = get<Tags::BondiBeta>(bondi_quantities);\n  const auto& dt_beta = get<::Tags::dt<Tags::BondiBeta>>(dt_bondi_quantities);\n  const auto& dr_beta = get<Tags::Dr<Tags::BondiBeta>>(dr_bondi_quantities);\n  for (size_t i = 0; i < get(beta).size(); ++i) {\n    CHECK(abs(get(beta).data()[i]) < 1e-10);\n    CHECK(abs(get(dt_beta).data()[i]) < 1e-10);\n    CHECK(abs(get(dr_beta).data()[i]) < 1e-10);\n  }\n  // check u and its derivatives\n  const auto u_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(l_max, 1,\n                                     get(get<Tags::BondiU>(bondi_quantities))),\n      l_max);\n  const auto dt_u_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(\n          l_max, 1, get(get<::Tags::dt<Tags::BondiU>>(dt_bondi_quantities))),\n      l_max);\n  const auto dr_u_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(\n          l_max, 1, get(get<Tags::Dr<Tags::BondiU>>(dr_bondi_quantities))),\n      l_max);\n  const std::complex<double> expected_time_factor =\n      cos(frequency * time) +\n      std::complex<double>(0.0, 1.0) * sin(frequency * time);\n  const std::complex<double> expected_dt_time_factor =\n      frequency * (std::complex<double>(0.0, 1.0) * cos(frequency * time) -\n                   sin(frequency * time));\n\n  const std::complex<double> expected_bondi_u_22_mode =\n      sqrt(3.0) * real(expected_time_factor *\n                       (0.5 * c_2a / square(extraction_radius) +\n                        0.25 * c_2b / pow<4>(extraction_radius) +\n                        std::complex<double>(0.0, 1.0) * frequency *\n                            (c_2b / (3.0 * pow<3>(extraction_radius)))));\n  const std::complex<double> expected_bondi_u_33_mode =\n      sqrt(6.0) *\n      real(expected_time_factor *\n           (0.5 * c_3a / square(extraction_radius) -\n            2.0 * square(frequency) * c_3b / (3.0 * pow<3>(extraction_radius)) +\n            c_3b / pow<5>(extraction_radius) +\n            std::complex<double>(0.0, 1.0) * frequency *\n                (1.25 * c_3b / pow<4>(extraction_radius))));\n  const std::complex<double> expected_dt_bondi_u_22_mode =\n      sqrt(3.0) * real(expected_dt_time_factor *\n                       (0.5 * c_2a / square(extraction_radius) +\n                        0.25 * c_2b / pow<4>(extraction_radius) +\n                        std::complex<double>(0.0, 1.0) * frequency *\n                        (c_2b / (3.0 * pow<3>(extraction_radius)))));\n  const std::complex<double> expected_dt_bondi_u_33_mode =\n      sqrt(6.0) *\n      real(expected_dt_time_factor *\n           (0.5 * c_3a / square(extraction_radius) -\n            2.0 * square(frequency) * c_3b / (3.0 * pow<3>(extraction_radius)) +\n            c_3b / pow<5>(extraction_radius) +\n            std::complex<double>(0.0, 1.0) * frequency *\n            (1.25 * c_3b / pow<4>(extraction_radius))));\n  const std::complex<double> expected_dr_bondi_u_22_mode =\n      -expected_dt_bondi_u_22_mode +\n      sqrt(3.0) * real(expected_time_factor *\n                       (-c_2a / pow<3>(extraction_radius) -\n                        c_2b / pow<5>(extraction_radius) +\n                        std::complex<double>(0.0, 1.0) * frequency *\n                            (-c_2b / (pow<4>(extraction_radius)))));\n  const std::complex<double> expected_dr_bondi_u_33_mode =\n      -expected_dt_bondi_u_33_mode +\n      sqrt(6.0) *\n      real(expected_time_factor *\n           (-c_3a / pow<3>(extraction_radius) +\n            2.0 * square(frequency) * c_3b / (pow<4>(extraction_radius)) -\n            5.0 * c_3b / pow<6>(extraction_radius) -\n            4.0 * std::complex<double>(0.0, 1.0) * frequency *\n                (1.25 * c_3b / pow<5>(extraction_radius))));\n  {\n    INFO(\"Bondi U modes\");\n    check_22_and_33_modes(u_goldberg_modes, expected_bondi_u_22_mode,\n                          expected_bondi_u_33_mode, l_max, bondi_approx);\n  }\n  {\n    INFO(\"dr Bondi U modes\");\n    check_22_and_33_modes(dr_u_goldberg_modes, expected_dr_bondi_u_22_mode,\n                          expected_dr_bondi_u_33_mode, l_max, bondi_approx);\n  }\n  {\n    INFO(\"dt Bondi U modes\");\n    check_22_and_33_modes(dt_u_goldberg_modes, expected_dt_bondi_u_22_mode,\n                          expected_dt_bondi_u_33_mode, l_max, bondi_approx);\n  }\n\n  // check w and its derivatives\n  const auto w_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(l_max, 1,\n                                     get(get<Tags::BondiW>(bondi_quantities))),\n      l_max);\n  const auto dt_w_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(\n          l_max, 1, get(get<::Tags::dt<Tags::BondiW>>(dt_bondi_quantities))),\n      l_max);\n  const auto dr_w_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(\n          l_max, 1, get(get<Tags::Dr<Tags::BondiW>>(dr_bondi_quantities))),\n      l_max);\n  const std::complex<double> expected_bondi_w_22_mode =\n      real(expected_time_factor *\n           (-square(frequency) * c_2b / square(extraction_radius) +\n            0.5 * c_2b / pow<4>(extraction_radius) +\n            std::complex<double>(0.0, 1.0) * frequency *\n                (c_2b / pow<3>(extraction_radius)))) /\n      sqrt(2.0);\n  const std::complex<double> expected_bondi_w_33_mode =\n      real(expected_time_factor *\n           (2.5 * frequency * c_3b / pow<4>(extraction_radius) +\n            3.0 * c_3b / pow<5>(extraction_radius) +\n            std::complex<double>(0.0, 1.0) * frequency *\n                (-2.0 * square(frequency) * c_3b / square(extraction_radius) -\n                 4.0 * frequency * c_3b / pow<3>(extraction_radius)))) /\n      sqrt(2.0);\n  const std::complex<double> expected_dt_bondi_w_22_mode =\n      real(expected_dt_time_factor *\n           (-square(frequency) * c_2b / square(extraction_radius) +\n            0.5 * c_2b / pow<4>(extraction_radius) +\n            std::complex<double>(0.0, 1.0) * frequency *\n            (c_2b / pow<3>(extraction_radius)))) /\n      sqrt(2.0);\n  const std::complex<double> expected_dt_bondi_w_33_mode =\n      real(expected_dt_time_factor *\n           (2.5 * frequency * c_3b / pow<4>(extraction_radius) +\n            3.0 * c_3b / pow<5>(extraction_radius) +\n            std::complex<double>(0.0, 1.0) * frequency *\n            (-2.0 * square(frequency) * c_3b / square(extraction_radius) -\n             4.0 * frequency * c_3b / pow<3>(extraction_radius)))) /\n      sqrt(2.0);\n  const std::complex<double> expected_dr_bondi_w_22_mode =\n      -expected_dt_bondi_w_22_mode +\n      real(expected_time_factor *\n           (2.0 * square(frequency) * c_2b / pow<3>(extraction_radius) -\n            2.0 * c_2b / pow<5>(extraction_radius) +\n            std::complex<double>(0.0, 1.0) * frequency *\n                (-3.0 * c_2b / pow<4>(extraction_radius)))) /\n          sqrt(2.0);\n  const std::complex<double> expected_dr_bondi_w_33_mode =\n      -expected_dt_bondi_w_33_mode +\n      real(expected_time_factor *\n           (-10.0 * frequency * c_3b / pow<5>(extraction_radius) -\n            15.0 * c_3b / pow<6>(extraction_radius) +\n            std::complex<double>(0.0, 1.0) * frequency *\n                (4.0 * square(frequency) * c_3b / pow<3>(extraction_radius) +\n                 12.0 * frequency * c_3b / pow<4>(extraction_radius)))) /\n          sqrt(2.0);\n  {\n    INFO(\"Bondi W modes\");\n    check_22_and_33_modes(w_goldberg_modes, expected_bondi_w_22_mode,\n                          expected_bondi_w_33_mode, l_max, bondi_approx);\n  }\n  {\n    INFO(\"dr Bondi W modes\");\n    check_22_and_33_modes(dr_w_goldberg_modes, expected_dr_bondi_w_22_mode,\n                          expected_dr_bondi_w_33_mode, l_max, bondi_approx);\n  }\n  {\n    INFO(\"dt Bondi W modes\");\n    check_22_and_33_modes(dt_w_goldberg_modes, expected_dt_bondi_w_22_mode,\n                          expected_dt_bondi_w_33_mode, l_max, bondi_approx);\n  }\n  // check j and its derivatives\n  const auto j_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(l_max, 1,\n                                     get(get<Tags::BondiJ>(bondi_quantities))),\n      l_max);\n  const auto dt_j_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(\n          l_max, 1, get(get<::Tags::dt<Tags::BondiJ>>(dt_bondi_quantities))),\n      l_max);\n  const auto dr_j_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(\n          l_max, 1, get(get<Tags::Dr<Tags::BondiJ>>(dr_bondi_quantities))),\n      l_max);\n  const std::complex<double> expected_bondi_j_22_mode =\n      sqrt(12.0) *\n      real(expected_time_factor * (0.25 * c_2a / extraction_radius -\n                                   c_2b / (12.0 * pow<3>(extraction_radius))));\n  const std::complex<double> expected_bondi_j_33_mode =\n      sqrt(60.0) * real(expected_time_factor *\n                        (0.1 * c_3a / extraction_radius -\n                         0.25 * c_3b / pow<4>(extraction_radius) +\n                         std::complex<double>(0.0, 1.0) * frequency *\n                             (-c_3b / (6.0 * pow<3>(extraction_radius)))));\n  const std::complex<double> expected_dt_bondi_j_22_mode =\n      sqrt(12.0) * real(expected_dt_time_factor *\n                        (0.25 * c_2a / extraction_radius -\n                         c_2b / (12.0 * pow<3>(extraction_radius))));\n  const std::complex<double> expected_dt_bondi_j_33_mode =\n      sqrt(60.0) * real(expected_dt_time_factor *\n                        (0.1 * c_3a / extraction_radius -\n                         0.25 * c_3b / pow<4>(extraction_radius) +\n                         std::complex<double>(0.0, 1.0) * frequency *\n                         (-c_3b / (6.0 * pow<3>(extraction_radius)))));\n  const std::complex<double> expected_dr_bondi_j_22_mode =\n      -expected_dt_bondi_j_22_mode +\n      sqrt(12.0) * real(expected_time_factor *\n                        (-0.25 * c_2a / square(extraction_radius) +\n                         c_2b / (4.0 * pow<4>(extraction_radius))));\n  const std::complex<double> expected_dr_bondi_j_33_mode =\n      -expected_dt_bondi_j_33_mode +\n      sqrt(60.0) * real(expected_time_factor *\n                        (-0.1 * c_3a / square(extraction_radius) +\n                         c_3b / pow<5>(extraction_radius) +\n                         std::complex<double>(0.0, 1.0) * frequency *\n                             (0.5 * c_3b / (pow<4>(extraction_radius)))));\n  {\n    INFO(\"Bondi J modes\");\n    check_22_and_33_modes(j_goldberg_modes, expected_bondi_j_22_mode,\n                          expected_bondi_j_33_mode, l_max, bondi_approx);\n  }\n  {\n    INFO(\"dr Bondi J modes\");\n\n    check_22_and_33_modes(dr_j_goldberg_modes, expected_dr_bondi_j_22_mode,\n                          expected_dr_bondi_j_33_mode, l_max, bondi_approx);\n  }\n  {\n    INFO(\"dt Bondi J modes\");\n    check_22_and_33_modes(dt_j_goldberg_modes, expected_dt_bondi_j_22_mode,\n                          expected_dt_bondi_j_33_mode, l_max, bondi_approx);\n  }\n  const std::complex<double> expected_news_22_mode =\n      real(std::complex<double>(0.0, 1.0) * pow<3>(frequency) * c_2b *\n           expected_time_factor / sqrt(12.0));\n  const std::complex<double> expected_news_33_mode =\n      real(-pow<4>(frequency) * c_3b * expected_time_factor / sqrt(15.0));\n  const auto news_goldberg_modes = Spectral::Swsh::libsharp_to_goldberg_modes(\n      Spectral::Swsh::swsh_transform(l_max, 1,\n                                     get(get<Tags::News>(boundary_data))),\n      l_max);\n  {\n    INFO(\"Bondi News modes\");\n    check_22_and_33_modes(news_goldberg_modes, expected_news_22_mode,\n                          expected_news_33_mode, l_max, bondi_approx);\n  }\n  Solutions::TestHelpers::check_adm_metric_quantities(\n      boundary_data, spacetime_metric, dt_spacetime_metric, d_spacetime_metric);\n  Solutions::TestHelpers::test_initialize_j(\n      l_max, 5_st, extraction_radius, time,\n      std::make_unique<\n          LinearizedBondiSachs_detail::InitializeJ::LinearizedBondiSachs>(\n          time, frequency, c_2a, c_2b, c_3a, c_3b),\n      boundary_solution.get_clone());\n}\n}  // namespace\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/Test_RobinsonTrautman.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"Options/Context.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::Solutions {\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.RobinsonTrautman\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> radius_dist{10.0, 20.0};\n  UniformCustomDistribution<double> parameter_dist{0.001, 0.01};\n  const double extraction_radius = radius_dist(gen);\n  const double start_time = 0.0;\n  // the system is somewhat stiff, so to save time in the test, we only evolve\n  // for a short time compared to the extraction radius.\n  const double time = 0.02;\n  const size_t l_max = 16;\n  // the l=2 modes are what we should care most about, so we test with only the\n  // (2, -2) entry populated.\n  std::vector<std::complex<double>> modes{0.0, 0.0, 0.0, 0.0,\n                                          parameter_dist(gen)};\n  Options::Context context{};\n  const RobinsonTrautman boundary_solution{\n      std::move(modes), extraction_radius, l_max, 1.0e-13, start_time, context};\n\n  const auto boundary_data = boundary_solution.variables(\n      l_max, time,\n      tmpl::list<Tags::Dr<Tags::CauchyCartesianCoords>,\n                 gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n                 gh::Tags::Phi<DataVector, 3>, Tags::News>{});\n\n  const auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(boundary_data);\n\n  CartesianiSphericalJ inverse_jacobian{get<0, 0>(spacetime_metric).size()};\n  boundary_solution.inverse_jacobian(make_not_null(&inverse_jacobian), l_max);\n\n  const auto bondi_quantities =\n      TestHelpers::extract_bondi_scalars_from_cartesian_metric(\n          spacetime_metric, inverse_jacobian, extraction_radius);\n\n  const auto& dt_spacetime_metric =\n      get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(boundary_data);\n  const auto dt_bondi_quantities =\n      TestHelpers::extract_dt_bondi_scalars_from_cartesian_metric(\n          dt_spacetime_metric, spacetime_metric, inverse_jacobian,\n          extraction_radius);\n  CartesianiSphericalJ dr_inverse_jacobian{get<0, 0>(spacetime_metric).size()};\n  boundary_solution.dr_inverse_jacobian(make_not_null(&dr_inverse_jacobian),\n                                        l_max);\n  const auto& d_spacetime_metric =\n      get<gh::Tags::Phi<DataVector, 3>>(boundary_data);\n  const auto& dr_cartesian_coordinates =\n      get<Tags::Dr<Tags::CauchyCartesianCoords>>(boundary_data);\n  tnsr::aa<DataVector, 3> dr_spacetime_metric{\n      get<0, 0>(spacetime_metric).size(), 0.0};\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      for (size_t i = 0; i < 3; ++i) {\n        dr_spacetime_metric.get(a, b) +=\n            dr_cartesian_coordinates.get(i) * d_spacetime_metric.get(i, a, b);\n      }\n    }\n  }\n  const auto dr_bondi_quantities =\n      TestHelpers::extract_dr_bondi_scalars_from_cartesian_metric(\n          dr_spacetime_metric, spacetime_metric, inverse_jacobian,\n          dr_inverse_jacobian, extraction_radius);\n\n  Approx bondi_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n\n  // we use the specific form of the Robinson-Trautman solution to determine\n  // the generating scalar, then use that to check that the remaining parts\n  // of the metric are appropriately related.\n  const SpinWeighted<ComplexDataVector, 0> inferred_rt_scalar =\n      exp(-2.0 * get(get<Tags::BondiBeta>(bondi_quantities)));\n  const SpinWeighted<ComplexDataVector, 1> expected_bondi_u =\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::Eth>(\n          l_max, 1, inferred_rt_scalar) /\n      extraction_radius;\n  const SpinWeighted<ComplexDataVector, 0> expected_bondi_w =\n      (inferred_rt_scalar +\n       Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::EthEthbar>(\n           l_max, 1, inferred_rt_scalar) -\n       1.0) /\n          extraction_radius -\n      2.0 / square(extraction_radius * inferred_rt_scalar);\n  const SpinWeighted<ComplexDataVector, 2> expected_bondi_j{\n      inferred_rt_scalar.size(), 0.0};\n  CHECK_ITERABLE_CUSTOM_APPROX(get(get<Tags::BondiU>(bondi_quantities)),\n                               expected_bondi_u, bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(get<Tags::BondiW>(bondi_quantities)),\n                               expected_bondi_w, bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(get<Tags::BondiJ>(bondi_quantities)),\n                               expected_bondi_j, bondi_approx);\n\n  const auto ethbar_ethbar_rt_scalar =\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::EthbarEthbar>(\n          l_max, 1, inferred_rt_scalar);\n  SpinWeighted<ComplexDataVector, 0> expected_dt_rt_scalar =\n      (-pow<4>(inferred_rt_scalar) *\n           Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::EthEth>(\n               l_max, 1, ethbar_ethbar_rt_scalar) +\n       pow<3>(inferred_rt_scalar) * ethbar_ethbar_rt_scalar *\n           conj(ethbar_ethbar_rt_scalar)) /\n      12.0;\n  Spectral::Swsh::filter_swsh_boundary_quantity(\n      make_not_null(&expected_dt_rt_scalar), l_max, l_max - 3);\n\n  // the evolution equation involves a fourth angular derivative, so requires a\n  // somewhat looser tolerance\n  Approx derivative_bondi_approx =\n      Approx::custom()\n      .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n      .scale(1.0);\n\n  const SpinWeighted<ComplexDataVector, 0> expected_dt_bondi_beta =\n      expected_dt_rt_scalar / (2.0 * inferred_rt_scalar);\n  const SpinWeighted<ComplexDataVector, 1> expected_dt_bondi_u =\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::Eth>(\n          l_max, 1, expected_dt_rt_scalar) /\n      extraction_radius;\n  const SpinWeighted<ComplexDataVector, 0> expected_dt_bondi_w =\n      (expected_dt_rt_scalar +\n       Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::EthEthbar>(\n           l_max, 1, expected_dt_rt_scalar)) /\n          extraction_radius +\n      4.0 * expected_dt_rt_scalar /\n          (square(extraction_radius) * pow<3>(inferred_rt_scalar));\n  const SpinWeighted<ComplexDataVector, 2> expected_dt_bondi_j{\n      inferred_rt_scalar.size(), 0.0};\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(get<::Tags::dt<Tags::BondiBeta>>(dt_bondi_quantities)),\n      expected_dt_bondi_beta, derivative_bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(get<::Tags::dt<Tags::BondiU>>(dt_bondi_quantities)),\n      expected_dt_bondi_u, derivative_bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(get<::Tags::dt<Tags::BondiW>>(dt_bondi_quantities)),\n      expected_dt_bondi_w, derivative_bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(get<::Tags::dt<Tags::BondiJ>>(dt_bondi_quantities)),\n      expected_dt_bondi_j, derivative_bondi_approx);\n\n  const SpinWeighted<ComplexDataVector, 0> expected_dr_bondi_beta =\n      -expected_dt_bondi_beta;\n  const SpinWeighted<ComplexDataVector, 1> expected_dr_bondi_u =\n      -expected_dt_bondi_u -\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::Eth>(\n          l_max, 1, inferred_rt_scalar) /\n          square(extraction_radius);\n  const SpinWeighted<ComplexDataVector, 0> expected_dr_bondi_w =\n      -expected_dt_bondi_w -\n      (inferred_rt_scalar +\n       Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::EthEthbar>(\n           l_max, 1, inferred_rt_scalar) -\n       1.0) /\n          square(extraction_radius) +\n      4.0 / (pow<3>(extraction_radius) * square(inferred_rt_scalar));\n  const SpinWeighted<ComplexDataVector, 2> expected_dr_bondi_j{\n      inferred_rt_scalar.size(), 0.0};\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(get<Tags::Dr<Tags::BondiBeta>>(dr_bondi_quantities)),\n      expected_dr_bondi_beta, bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(get<Tags::Dr<Tags::BondiU>>(dr_bondi_quantities)),\n      expected_dr_bondi_u, bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(get<Tags::Dr<Tags::BondiW>>(dr_bondi_quantities)),\n      expected_dr_bondi_w, bondi_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(get<Tags::Dr<Tags::BondiJ>>(dr_bondi_quantities)),\n      expected_dr_bondi_j, bondi_approx);\n\n  const auto& news = get(get<Tags::News>(boundary_data));\n  const SpinWeighted<ComplexDataVector, -2> expected_news =\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::EthbarEthbar>(\n          l_max, 1, inferred_rt_scalar) /\n      inferred_rt_scalar;\n  CHECK_ITERABLE_CUSTOM_APPROX(news, expected_news, bondi_approx);\n}\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/Test_RotatingSchwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce::Solutions {\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.RotatingSchwarzschild\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> radius_dist{5.0, 10.0};\n  UniformCustomDistribution<double> parameter_dist{0.5, 2.0};\n  const double extraction_radius = radius_dist(gen);\n  const size_t l_max = 16;\n  // use a low frequency so that the dimensionless parameter in the metric is ~1\n  const double frequency = parameter_dist(gen) / 30.0;\n  const double mass = parameter_dist(gen);\n  TestHelpers::SphericalSolutionWrapper<RotatingSchwarzschild>\n      boundary_solution{extraction_radius, mass, frequency};\n  const double time = 10.0 * parameter_dist(gen);\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Cce/AnalyticSolutions/\"};\n  boundary_solution.test_spherical_metric(\"RotatingSchwarzschild\", l_max, time,\n                                          approx, extraction_radius, mass,\n                                          frequency);\n  boundary_solution.test_serialize_and_deserialize(l_max, time);\n  TestHelpers::test_initialize_j(\n      l_max, 5_st, extraction_radius, time,\n      std::make_unique<InitializeJ::InverseCubic<false>>(),\n      boundary_solution.get_clone());\n}\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/Test_TeukolskyWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n\n#include \"Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce::Solutions {\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.TeukolskyWave\", \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> radius_dist{5.0, 10.0};\n  UniformCustomDistribution<double> parameter_dist{0.5, 2.0};\n  const double extraction_radius = radius_dist(gen);\n  const size_t l_max = 16;\n  // use a low frequency so that the dimensionless parameter in the metric is ~1\n  const double amplitude = parameter_dist(gen);\n  const double duration = 1.0 + parameter_dist(gen);\n  TestHelpers::SphericalSolutionWrapper<TeukolskyWave> boundary_solution{\n      extraction_radius, amplitude, duration};\n  const double time = duration + extraction_radius + parameter_dist(gen);\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/Cce/AnalyticSolutions/\"};\n  boundary_solution.test_spherical_metric(\n      \"TeukolskyWave\", l_max, time, Approx::custom().epsilon(1.e-12).scale(1.0),\n      extraction_radius, amplitude, duration);\n  boundary_solution.test_serialize_and_deserialize(l_max, time);\n  TestHelpers::test_initialize_j(\n      l_max, 5_st, extraction_radius, time,\n      std::make_unique<InitializeJ::InverseCubic<false>>(),\n      boundary_solution.get_clone());\n}\n}  // namespace Cce::Solutions\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\n# variable names chosen to visually correspond to the rendered mathematics.\n# t = time\n# r = extraction_radius\n# a = amplitude\n# k = duration\ndef pulse_profile_coefficient_a(t, r, a, k):\n    u = t - r\n    return (\n        3.0\n        * a\n        * np.exp(-(u**2) / k**2)\n        / (k**4 * r**5)\n        * (\n            3.0 * k**4\n            + 4.0 * r**2 * u**2\n            - 2.0 * k**2 * r * (r + 3.0 * u)\n        )\n    )\n\n\ndef pulse_profile_coefficient_b(t, r, a, k):\n    u = t - r\n    return (\n        2.0\n        * a\n        * np.exp(-(u**2) / k**2)\n        / (k**6 * r**5)\n        * (\n            -3.0 * k**6\n            + 4.0 * r**3 * u**3\n            - 6.0 * k**2 * r**2 * u * (r + u)\n            + 3.0 * k**4 * r * (r + 2.0 * u)\n        )\n    )\n\n\ndef pulse_profile_coefficient_c(t, r, a, k):\n    u = t - r\n    return (\n        0.25\n        * a\n        * np.exp(-(u**2) / k**2)\n        / (k**8 * r**5)\n        * (\n            21.0 * k**8\n            + 16.0 * r**4 * u**4\n            - 16.0 * k**2 * r**3 * u**2 * (3.0 * r + u)\n            - 6.0 * k**6 * r * (3.0 * r + 7.0 * u)\n            + 12.0 * k**4 * r**2 * (r**2 + 2.0 * r * u + 3.0 * u**2)\n        )\n    )\n\n\ndef dt_pulse_profile_coefficient_a(t, r, a, k):\n    u = t - r\n    return (\n        -6.0\n        * a\n        * np.exp(-(u**2) / k**2)\n        / (k**6 * r**5)\n        * (\n            4.0 * r**2 * u**3\n            + 3.0 * k**4 * (r + u)\n            - 6.0 * k**2 * r * u * (r + u)\n        )\n    )\n\n\ndef dt_pulse_profile_coefficient_b(t, r, a, k):\n    u = t - r\n    return (\n        4.0\n        * a\n        * np.exp(-(u**2) / k**2)\n        / (k**8 * r**5)\n        * (\n            -4.0 * r**3 * u**4\n            + 3.0 * k**6 * (r + u)\n            + 6.0 * k**2 * r**2 * u**2 * (2 * r + u)\n            - 3.0 * k**4 * r * (r**2 + 3 * r * u + 2 * u**2)\n        )\n    )\n\n\ndef dt_pulse_profile_coefficient_c(t, r, a, k):\n    u = t - r\n    return (\n        -0.5\n        * a\n        * np.exp(-(u**2) / k**2)\n        / (k**10 * r**5)\n        * (\n            16.0 * r**4 * u**5\n            + 21.0 * k**8 * (r + u)\n            - 16.0 * k**2 * r**3 * u**3 * (5.0 * r + u)\n            + 12.0\n            * k**4\n            * r**2\n            * u\n            * (5.0 * r**2 + 4 * r * u + 3 * u**2)\n            - 6.0 * k**6 * r * (2.0 * r**2 + 9.0 * r * u + 7.0 * u**2)\n        )\n    )\n\n\ndef dr_pulse_profile_coefficient_a(t, r, a, k):\n    u = t - r\n    return -dt_pulse_profile_coefficient_a(t, r, a, k) + 3.0 * a * np.exp(\n        -(u**2) / k**2\n    ) / (k**4 * r**6) * (\n        -15.0 * k**4\n        - 12.0 * r**2 * u**2\n        + 6.0 * k**2 * r * (r + 4.0 * u)\n    )\n\n\ndef dr_pulse_profile_coefficient_b(t, r, a, k):\n    u = t - r\n    return -dt_pulse_profile_coefficient_b(t, r, a, k) + 2.0 * a * np.exp(\n        -(u**2) / k**2\n    ) / (k**6 * r**6) * (\n        15.0 * k**6\n        - 8.0 * r**3 * u**3\n        + 6.0 * k**2 * r**2 * u * (2.0 * r + 3.0 * u)\n        - 3.0 * k**4 * r * (3.0 * r + 8.0 * u)\n    )\n\n\ndef dr_pulse_profile_coefficient_c(t, r, a, k):\n    u = t - r\n    return -dt_pulse_profile_coefficient_c(t, r, a, k) + 0.25 * a * np.exp(\n        -(u**2) / k**2\n    ) / (k**8 * r**6) * (\n        -105.0 * k**8\n        - 16.0 * r**4 * u**4\n        + 16.0 * k**2 * r**3 * u**2 * (3.0 * r + 2.0 * u)\n        + 6.0 * k**6 * r * (9.0 * r + 28.0 * u)\n        - 12.0 * k**4 * r**2 * (r**2 + 4.0 * r * u + 9.0 * u**2)\n    )\n\n\ndef spherical_metric(sin_theta, cos_theta, t, r, a, k):\n    f_r_r = 2.0 - 3.0 * sin_theta**2\n    f_r_th = -3.0 * sin_theta * cos_theta\n    fC_th_th = 3.0 * sin_theta**2\n    fA_th_th = -1.0\n    fC_ph_ph = -3.0 * sin_theta**2\n    fA_ph_ph = 3.0 * sin_theta**2 - 1.0\n    A = pulse_profile_coefficient_a(t, r, a, k)\n    B = pulse_profile_coefficient_b(t, r, a, k)\n    C = pulse_profile_coefficient_c(t, r, a, k)\n    return np.array(\n        [\n            [-1.0, 0.0, 0.0, 0.0],\n            [0.0, 1.0 + f_r_r * A, B * f_r_th * r, 0.0],\n            [\n                0.0,\n                B * f_r_th * r,\n                (1.0 + C * fC_th_th + A * fA_th_th) * r**2,\n                0.0,\n            ],\n            [0.0, 0.0, 0.0, (1.0 + C * fC_ph_ph + A * fA_ph_ph) * r**2],\n        ]\n    )\n\n\ndef dr_spherical_metric(sin_theta, cos_theta, t, r, a, k):\n    f_r_r = 2.0 - 3.0 * sin_theta**2\n    f_r_th = -3.0 * sin_theta * cos_theta\n    fC_th_th = 3.0 * sin_theta**2\n    fA_th_th = -1.0\n    fC_ph_ph = -3.0 * sin_theta**2\n    fA_ph_ph = 3.0 * sin_theta**2 - 1.0\n    A = pulse_profile_coefficient_a(t, r, a, k)\n    B = pulse_profile_coefficient_b(t, r, a, k)\n    C = pulse_profile_coefficient_c(t, r, a, k)\n    dr_A = dr_pulse_profile_coefficient_a(t, r, a, k)\n    dr_B = dr_pulse_profile_coefficient_b(t, r, a, k)\n    dr_C = dr_pulse_profile_coefficient_c(t, r, a, k)\n    return np.array(\n        [\n            [0.0, 0.0, 0.0, 0.0],\n            [0.0, f_r_r * dr_A, (B + r * dr_B) * f_r_th, 0.0],\n            [\n                0.0,\n                (B + r * dr_B) * f_r_th,\n                r\n                * (\n                    2.0\n                    + (2.0 * C + r * dr_C) * fC_th_th\n                    + (2.0 * A + r * dr_A) * fA_th_th\n                ),\n                0.0,\n            ],\n            [\n                0.0,\n                0.0,\n                0.0,\n                r\n                * (\n                    2.0\n                    + (2.0 * C + r * dr_C) * fC_ph_ph\n                    + (2.0 * A + r * dr_A) * fA_ph_ph\n                ),\n            ],\n        ]\n    )\n\n\ndef dt_spherical_metric(sin_theta, cos_theta, t, r, a, k):\n    f_r_r = 2.0 - 3.0 * sin_theta**2\n    f_r_th = -3.0 * sin_theta * cos_theta\n    fC_th_th = 3.0 * sin_theta**2\n    fA_th_th = -1.0\n    fC_ph_ph = -3.0 * sin_theta**2\n    fA_ph_ph = 3.0 * sin_theta**2 - 1.0\n    dt_A = dt_pulse_profile_coefficient_a(t, r, a, k)\n    dt_B = dt_pulse_profile_coefficient_b(t, r, a, k)\n    dt_C = dt_pulse_profile_coefficient_c(t, r, a, k)\n    return np.array(\n        [\n            [0.0, 0.0, 0.0, 0.0],\n            [0.0, f_r_r * dt_A, dt_B * f_r_th * r, 0.0],\n            [\n                0.0,\n                dt_B * f_r_th * r,\n                (dt_C * fC_th_th + dt_A * fA_th_th) * r**2,\n                0.0,\n            ],\n            [0.0, 0.0, 0.0, (dt_C * fC_ph_ph + dt_A * fA_ph_ph) * r**2],\n        ]\n    )\n\n\ndef news(sin_theta, t, r, a, k):\n    u = t - r\n    return (\n        -complex(6.0, 0.0)\n        * a\n        * sin_theta**2\n        * np.exp(-(u**2) / k**2)\n        * u\n        / k**10\n        * (15.0 * k**4 - 20 * k**2 * u**2 + 4.0 * u**4)\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/BoundaryData.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport math\n\nimport numpy as np\n\n\ndef cartesian_to_angular_coordinates(\n    cos_phi, cos_theta, sin_phi, sin_theta, extraction_radius\n):\n    return np.array([cos_phi * sin_theta, sin_phi * sin_theta, cos_theta])\n\n\ndef cartesian_to_angular_jacobian(\n    cos_phi, cos_theta, sin_phi, sin_theta, extraction_radius\n):\n    return np.array(\n        [\n            [cos_phi * sin_theta, sin_phi * sin_theta, cos_theta],\n            [\n                extraction_radius * cos_phi * cos_theta,\n                extraction_radius * sin_phi * cos_theta,\n                -extraction_radius * sin_theta,\n            ],\n            [-extraction_radius * sin_phi, extraction_radius * cos_phi, 0.0],\n        ]\n    )\n\n\ndef cartesian_to_angular_inverse_jacobian(\n    cos_phi, cos_theta, sin_phi, sin_theta, extraction_radius\n):\n    return np.array(\n        [\n            [\n                cos_phi * sin_theta,\n                cos_phi * cos_theta / extraction_radius,\n                -sin_phi / extraction_radius,\n            ],\n            [\n                sin_phi * sin_theta,\n                cos_theta * sin_phi / extraction_radius,\n                cos_phi / extraction_radius,\n            ],\n            [cos_theta, -sin_theta / extraction_radius, 0.0],\n        ]\n    )\n\n\ndef null_metric(cartesian_to_angular_jacobian, pi, psi):\n    null_metric = psi.copy()\n    null_metric[1, 1] = 0.0\n\n    null_metric[1, 2:4] = 0.0\n    null_metric[2:4, 1] = 0.0\n\n    null_metric[0, 1] = -1.0\n    null_metric[1, 0] = -1.0\n\n    null_metric[0, 0] = psi[0, 0]\n\n    null_metric[0, 2:4] = np.einsum(\n        \"Ai,i\", cartesian_to_angular_jacobian, psi[0, 1:4]\n    )[1:3]\n    null_metric[2:4, 0] = null_metric[0, 2:4]\n\n    null_metric[2:4, 2:4] = np.einsum(\n        \"Ai,Bj,ij\",\n        cartesian_to_angular_jacobian,\n        cartesian_to_angular_jacobian,\n        psi[1:4, 1:4],\n    )[1:4, 1:4]\n    return null_metric\n\n\ndef du_null_metric(cartesian_to_angular_jacobian, pi, psi):\n    du_null_metric = pi.copy()\n    du_null_metric[1, 0:4] = 0.0\n    du_null_metric[0:4, 1] = 0.0\n\n    du_null_metric[0, 0] = pi[0, 0]\n\n    du_null_metric[0, 2:4] = np.einsum(\n        \"Ai,i\", cartesian_to_angular_jacobian, pi[0][1:4]\n    )[1:3]\n    du_null_metric[2:4, 0] = du_null_metric[0, 2:4]\n\n    du_null_metric[2:4, 2:4] = np.einsum(\n        \"Ai,Bj,ij\",\n        cartesian_to_angular_jacobian,\n        cartesian_to_angular_jacobian,\n        pi[1:4, 1:4],\n    )[1:4, 1:4]\n\n    return du_null_metric\n\n\ndef inverse_null_metric(null_metric):\n    inverse_null_metric = null_metric.copy()\n    inverse_null_metric[1, 0] = -1.0\n    inverse_null_metric[0, 1] = -1.0\n\n    inverse_null_metric[0, 0] = 0.0\n    inverse_null_metric[0, 2:4] = 0.0\n    inverse_null_metric[2:4, 0] = 0.0\n    angular_determinant = (\n        null_metric[2, 2] * null_metric[3, 3]\n        - null_metric[2, 3] * null_metric[3, 2]\n    )\n    inverse_null_metric[2, 2] = null_metric[3, 3] / angular_determinant\n    inverse_null_metric[2, 3] = -null_metric[2, 3] / angular_determinant\n    inverse_null_metric[3, 2] = -null_metric[2, 3] / angular_determinant\n    inverse_null_metric[3, 3] = null_metric[2, 2] / angular_determinant\n\n    inverse_null_metric[1, 2:4] = np.einsum(\n        \"AB,B\", inverse_null_metric[2:4, 2:4], null_metric[2:4, 0]\n    )\n    inverse_null_metric[2:4, 1] = inverse_null_metric[1, 2:4]\n    inverse_null_metric[1, 1] = -null_metric[0, 0] + np.einsum(\n        \"A,A\", inverse_null_metric[1, 2:4], null_metric[2:4, 0]\n    )\n    return inverse_null_metric\n\n\ndef worldtube_normal(\n    cos_phi, cos_theta, psi, dt_psi, sin_phi, sin_theta, inverse_spatial_metric\n):\n    sigma = inverse_spatial_metric[0, :].copy()\n    sigma[0] = cos_phi * sin_theta**2\n    sigma[1] = sin_phi * sin_theta**2\n    sigma[2] = cos_theta * sin_theta\n    norm_of_sigma = math.sqrt(\n        np.einsum(\"i,j,ij\", sigma, sigma, inverse_spatial_metric)\n    )\n\n    worldtube_normal = np.einsum(\n        \"ij,j\", inverse_spatial_metric, sigma / norm_of_sigma\n    )\n    return worldtube_normal\n\n\ndef dt_worldtube_normal(\n    cos_phi, cos_theta, psi, dt_psi, sin_phi, sin_theta, inverse_spatial_metric\n):\n    sigma = inverse_spatial_metric[0, :].copy()\n    sigma[0] = cos_phi * sin_theta**2\n    sigma[1] = sin_phi * sin_theta**2\n    sigma[2] = cos_theta * sin_theta\n    norm_of_sigma = math.sqrt(\n        np.einsum(\"i,j,ij\", sigma, sigma, inverse_spatial_metric)\n    )\n\n    worldtube_normal = np.einsum(\n        \"ij,j\", inverse_spatial_metric, sigma / norm_of_sigma\n    )\n\n    dt_worldtube_normal = np.einsum(\n        \"ij,k,jk\",\n        0.5 * np.outer(worldtube_normal, worldtube_normal)\n        - inverse_spatial_metric,\n        worldtube_normal,\n        dt_psi[1:4, 1:4],\n    )\n    return dt_worldtube_normal\n\n\ndef null_vector_l(\n    dt_worldtube_normal,\n    dt_lapse,\n    dt_psi,\n    dt_shift,\n    lapse,\n    psi,\n    shift,\n    worldtube_normal,\n):\n    hypersurface_normal_vector = np.pad(\n        worldtube_normal, ((1, 0)), \"constant\"\n    ).copy()\n    hypersurface_normal_vector[0] = 1.0 / lapse\n    hypersurface_normal_vector[1:4] = -shift / lapse\n    null_l = np.pad(worldtube_normal, ((1, 0)), \"constant\").copy()\n    null_l[0] = hypersurface_normal_vector[0] / (\n        lapse - np.einsum(\"ij,i,j\", psi[1:4, 1:4], shift, worldtube_normal)\n    )\n    null_l[1:4] = (hypersurface_normal_vector[1:4] + worldtube_normal) / (\n        lapse - np.einsum(\"ij,i,j\", psi[1:4, 1:4], shift, worldtube_normal)\n    )\n    return null_l\n\n\ndef du_null_vector_l(\n    dt_worldtube_normal,\n    dt_lapse,\n    dt_psi,\n    dt_shift,\n    lapse,\n    psi,\n    shift,\n    worldtube_normal,\n):\n    hypersurface_normal_vector = np.pad(\n        worldtube_normal, ((1, 0)), \"constant\"\n    ).copy()\n    hypersurface_normal_vector[0] = 1.0 / lapse\n    hypersurface_normal_vector[1:4] = -shift / lapse\n\n    denominator = lapse - np.einsum(\n        \"ij,i,j\", psi[1:4, 1:4], shift, worldtube_normal\n    )\n\n    du_hypersurface_normal = np.pad(shift[:], ((1, 0)), \"constant\").copy()\n    du_hypersurface_normal[0] = -dt_lapse / lapse**2\n    du_hypersurface_normal[1:4] = -(dt_shift / lapse) + (\n        np.outer(dt_lapse, shift) / lapse**2\n    )\n\n    du_null_l = (du_hypersurface_normal) / denominator\n    du_null_l[1:4] += (dt_worldtube_normal) / denominator\n    du_denominator = (\n        -dt_lapse\n        + np.einsum(\"ij,i,j\", dt_psi[1:4, 1:4], shift, worldtube_normal)\n        + np.einsum(\"ij,i,j\", psi[1:4, 1:4], dt_shift, worldtube_normal)\n        + np.einsum(\"ij,i,j\", psi[1:4, 1:4], shift, dt_worldtube_normal)\n    )\n\n    du_null_l[0] += (\n        du_denominator * hypersurface_normal_vector[0] / denominator**2\n    )\n    du_null_l[1:4] += (\n        du_denominator\n        * (hypersurface_normal_vector[1:4] + worldtube_normal)\n        / denominator**2\n    )\n    return du_null_l\n\n\ndef dlambda_null_metric(\n    angular_d_null_l,\n    cartesian_to_angular_jacobian,\n    phi,\n    pi,\n    du_null_l,\n    inverse_null_metric,\n    null_l,\n    psi,\n):\n    dlambda_null_metric = psi.copy()\n    dlambda_null_metric[0, 0] = (\n        np.einsum(\"i,i\", null_l[1:4], phi[:, 0, 0])\n        + null_l[0] * pi[0, 0]\n        + 2.0 * np.einsum(\"a,a\", du_null_l, psi[:, 0])\n    )\n    dlambda_null_metric[1, :] = 0.0\n    dlambda_null_metric[:, 1] = 0.0\n\n    dlambda_null_metric[0, 2:4] = np.einsum(\n        \"Ak,a,ka\", cartesian_to_angular_jacobian[1:3, :], du_null_l, psi[1:4, :]\n    )\n    dlambda_null_metric[0, 2:4] += null_l[0] * np.einsum(\n        \"Ak,k\", cartesian_to_angular_jacobian[1:3, :], pi[1:4, 0]\n    )\n    dlambda_null_metric[0, 2:4] += np.einsum(\n        \"Ak,i,ik\",\n        cartesian_to_angular_jacobian[1:3, :],\n        null_l[1:4],\n        phi[:, 1:4, 0],\n    )\n    dlambda_null_metric[0, 2:4] += np.einsum(\n        \"Aa,a\", angular_d_null_l[0:2, :], psi[:, 0]\n    )\n    dlambda_null_metric[2:4, 0] = dlambda_null_metric[0, 2:4]\n\n    dlambda_null_metric[2:4, 2:4] = null_l[0] * np.einsum(\n        \"Ak,Bl,kl\",\n        cartesian_to_angular_jacobian[1:3, :],\n        cartesian_to_angular_jacobian[1:3, :],\n        pi[1:4, 1:4],\n    )\n    dlambda_null_metric[2:4, 2:4] += np.einsum(\n        \"Ak,Bl,i,ikl\",\n        cartesian_to_angular_jacobian[1:3, :],\n        cartesian_to_angular_jacobian[1:3, :],\n        null_l[1:4],\n        phi[:, 1:4, 1:4],\n    )\n    dlambda_null_metric[2:4, 2:4] += np.einsum(\n        \"Aa,Bl,al\",\n        angular_d_null_l[0:2, :],\n        cartesian_to_angular_jacobian[1:3, :],\n        psi[:, 1:4],\n    )\n    dlambda_null_metric[2:4, 2:4] += np.einsum(\n        \"Al,Ba,al\",\n        cartesian_to_angular_jacobian[1:3, :],\n        angular_d_null_l[0:2, :],\n        psi[:, 1:4],\n    )\n    return dlambda_null_metric\n\n\ndef inverse_dlambda_null_metric(\n    angular_d_null_l,\n    cartesian_to_angular_jacobian,\n    phi,\n    pi,\n    du_null_l,\n    inverse_null_metric,\n    null_l,\n    psi,\n):\n    dlambda_null_metric_value = dlambda_null_metric(\n        angular_d_null_l,\n        cartesian_to_angular_jacobian,\n        phi,\n        pi,\n        du_null_l,\n        inverse_null_metric,\n        null_l,\n        psi,\n    )\n\n    inverse_dlambda_null_metric_value = dlambda_null_metric_value.copy()\n\n    inverse_dlambda_null_metric_value[0, :] = 0.0\n    inverse_dlambda_null_metric_value[:, 0] = 0.0\n    inverse_dlambda_null_metric_value[1, 1] = (\n        -dlambda_null_metric_value[0, 0]\n        + 2.0\n        * np.einsum(\n            \"a,a\",\n            inverse_null_metric[1, 2:4],\n            dlambda_null_metric_value[0, 2:4],\n        )\n        - np.einsum(\n            \"a,b,ab\",\n            inverse_null_metric[1, 2:4],\n            inverse_null_metric[1, 2:4],\n            dlambda_null_metric_value[2:4, 2:4],\n        )\n    )\n    inverse_dlambda_null_metric_value[1, 2:4] = np.einsum(\n        \"ab, b\",\n        inverse_null_metric[2:4, 2:4],\n        dlambda_null_metric_value[0, 2:4],\n    ) - np.einsum(\n        \"ab, c, cb\",\n        inverse_null_metric[2:4, 2:4],\n        inverse_null_metric[1, 2:4],\n        dlambda_null_metric_value[2:4, 2:4],\n    )\n    inverse_dlambda_null_metric_value[2:4, 2:4] = -np.einsum(\n        \"ac,bd,cd\",\n        inverse_null_metric[2:4, 2:4],\n        inverse_null_metric[2:4, 2:4],\n        dlambda_null_metric_value[2:4, 2:4],\n    )\n\n    return inverse_dlambda_null_metric_value\n\n\ndef bondi_beta_worldtube_data(local_d_bondi_r):\n    return np.array(-0.5 * math.log(local_d_bondi_r[1]), dtype=complex)\n\n\ndef bondi_u_worldtube_data(down_dyad, local_d_bondi_r, inverse_null_metric):\n    u_vector = local_d_bondi_r[1] * inverse_null_metric[1, 2:4] + np.einsum(\n        \"b, ab\", local_d_bondi_r[2:4], inverse_null_metric[2:4, 2:4]\n    )\n    return -np.einsum(\"a,a\", u_vector, down_dyad) / local_d_bondi_r[1]\n\n\ndef bondi_w_worldtube_data(local_d_bondi_r, inverse_null_metric, local_bondi_r):\n    return (\n        -1.0\n        + (\n            inverse_null_metric[1, 1] * local_d_bondi_r[1] ** 2\n            + 2.0\n            * local_d_bondi_r[1]\n            * (\n                np.einsum(\n                    \"a,a\", local_d_bondi_r[2:4], inverse_null_metric[1, 2:4]\n                )\n                - local_d_bondi_r[0]\n            )\n            + np.einsum(\n                \"a,b,ab\",\n                local_d_bondi_r[2:4],\n                local_d_bondi_r[2:4],\n                inverse_null_metric[2:4, 2:4],\n            )\n        )\n        / local_d_bondi_r[1]\n    ) / local_bondi_r\n\n\ndef bondi_j_worldtube_data(null_metric, bondi_r, up_dyad):\n    return (\n        0.5\n        * np.einsum(\"a,b,ab\", up_dyad, up_dyad, null_metric[2:4, 2:4])\n        / bondi_r**2\n    )\n\n\ndef dr_bondi_j_worldtube_data(\n    dlambda_null_metric, local_d_bondi_r, bondi_j, local_bondi_r, up_dyad\n):\n    return (\n        0.5\n        * np.einsum(\"a,b,ab\", up_dyad, up_dyad, dlambda_null_metric[2:4, 2:4])\n        / local_bondi_r**2\n        - 2.0 * local_d_bondi_r[1] * bondi_j / local_bondi_r\n    ) / local_d_bondi_r[1]\n\n\ndef dr_bondi_j_denominator(\n    dlambda_null_metric, local_d_bondi_r, bondi_j, local_bondi_r, up_dyad\n):\n    return 1.0 / (local_bondi_r**2 * local_d_bondi_r[1])\n\n\ndef d2lambda_bondi_r(local_d_bondi_r, dr_bondi_j, bondi_j, bondi_r):\n    return np.array(\n        np.real(\n            -0.25\n            * bondi_r\n            * local_d_bondi_r[1] ** 2\n            * (\n                dr_bondi_j * np.conj(dr_bondi_j)\n                - 0.25\n                * (\n                    (\n                        dr_bondi_j * np.conj(bondi_j)\n                        + bondi_j * np.conj(dr_bondi_j)\n                    )\n                    / np.sqrt(1.0 + bondi_j * np.conj(bondi_j))\n                )\n                ** 2\n            )\n        )\n    )\n\n\ndef dlambda_bondi_u(\n    d2lambda_bondi_r,\n    dlambda_inverse_null_metric,\n    local_d_bondi_r,\n    down_dyad,\n    angular_d_dlambda_r,\n    inverse_null_metric,\n    bondi_u,\n):\n    dlambda_u_vector_part = -(\n        dlambda_inverse_null_metric[1, 2:4]\n        + np.einsum(\n            \"b,ab\",\n            angular_d_dlambda_r[0:2] / local_d_bondi_r[1],\n            inverse_null_metric[2:4, 2:4],\n        )\n        + np.einsum(\n            \"b, ab\",\n            local_d_bondi_r[2:4] / local_d_bondi_r[1],\n            dlambda_inverse_null_metric[2:4, 2:4],\n        )\n    )\n    dlambda_beta = -0.5 * d2lambda_bondi_r / local_d_bondi_r[1]\n\n    return np.einsum(\n        \"a,a\", dlambda_u_vector_part, down_dyad\n    ) + 2.0 * dlambda_beta * (\n        bondi_u + np.einsum(\"a,a\", inverse_null_metric[1, 2:4], down_dyad)\n    )\n\n\ndef bondi_q_worldtube_data(\n    local_d2lambda_bondi_r,\n    dlambda_inverse_null_metric,\n    local_d_bondi_r,\n    down_dyad,\n    angular_d_dlambda_r,\n    inverse_null_metric,\n    bondi_j,\n    local_bondi_r,\n    bondi_u,\n):\n    local_dlambda_bondi_u = dlambda_bondi_u(\n        local_d2lambda_bondi_r,\n        dlambda_inverse_null_metric,\n        local_d_bondi_r,\n        down_dyad,\n        angular_d_dlambda_r,\n        inverse_null_metric,\n        bondi_u,\n    )\n    return local_bondi_r**2 * (\n        bondi_j * np.conj(local_dlambda_bondi_u)\n        + np.sqrt(1.0 + bondi_j * np.conj(bondi_j)) * local_dlambda_bondi_u\n    )\n\n\ndef dr_bondi_u_worldtube_data(\n    local_d2lambda_bondi_r,\n    dlambda_inverse_null_metric,\n    local_d_bondi_r,\n    down_dyad,\n    angular_d_dlambda_r,\n    inverse_null_metric,\n    bondi_j,\n    local_bondi_r,\n    bondi_u,\n):\n    local_dlambda_bondi_u = dlambda_bondi_u(\n        local_d2lambda_bondi_r,\n        dlambda_inverse_null_metric,\n        local_d_bondi_r,\n        down_dyad,\n        angular_d_dlambda_r,\n        inverse_null_metric,\n        bondi_u,\n    )\n    return local_dlambda_bondi_u / local_d_bondi_r[1]\n\n\ndef bondi_h_worldtube_data(\n    local_d_bondi_r, bondi_j, du_null_metric, local_bondi_r, up_dyad\n):\n    return (\n        0.5\n        * np.einsum(\"a,b,ab\", up_dyad, up_dyad, du_null_metric[2:4, 2:4])\n        / local_bondi_r**2\n        - 2.0 * local_d_bondi_r[0] * bondi_j / local_bondi_r\n    )\n\n\ndef du_j_worldtube_data(\n    local_d_bondi_r,\n    bondi_j,\n    du_null_metric,\n    dlambda_null_metric,\n    local_bondi_r,\n    up_dyad,\n):\n    return (\n        0.5\n        * np.einsum(\n            \"a,b,ab\",\n            up_dyad,\n            up_dyad,\n            du_null_metric[2:4, 2:4]\n            - local_d_bondi_r[0]\n            * dlambda_null_metric[2:4, 2:4]\n            / local_d_bondi_r[1],\n        )\n        / local_bondi_r**2\n    )\n\n\ndef klein_gordon_psi_worldtube_data(csw_psi):\n    return csw_psi + 0j\n\n\ndef klein_gordon_pi_worldtube_data(csw_pi, csw_phi, lapse, shift):\n    return -lapse * csw_pi + np.dot(shift, csw_phi) + 0j\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Actions)\nadd_subdirectory(AnalyticSolutions)\n\nset(LIBRARY \"Test_Cce\")\n\nset(LIBRARY_SOURCES\n  InterfaceManagers/Test_GhLocalTimeStepping.cpp\n  InterfaceManagers/Test_GhLockstep.cpp\n  Test_AnalyticBoundaryDataManager.cpp\n  Test_BoundaryData.cpp\n  Test_BoundaryDataTags.cpp\n  Test_DumpBondiSachsOnWorldtube.cpp\n  Test_Equations.cpp\n  Test_GaugeTransformBoundaryData.cpp\n  Test_InitializeCce.cpp\n  Test_KleinGordonWorldtubeData.cpp\n  Test_LinearOperators.cpp\n  Test_LinearSolve.cpp\n  Test_NewmanPenrose.cpp\n  Test_OptionTags.cpp\n  Test_PreSwshDerivatives.cpp\n  Test_PrecomputeCceDependencies.cpp\n  Test_ScriPlusInterpolationManager.cpp\n  Test_ScriPlusValues.cpp\n  Test_SwshDerivatives.cpp\n  Test_Tags.cpp\n  Test_WorldtubeData.cpp\n  )\n\nadd_subdirectory(Events)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  Cce\n  CceHelpers\n  DataStructures\n  GeneralRelativitySolutions\n  Observer\n  Options\n  Spectral\n  SpinWeightedSphericalHarmonics\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Equations.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Test function for beta equation\n\n\ndef integrand_for_beta(dy_j, j, one_minus_y):\n    dy_jbar = np.conj(dy_j)\n    jbar = np.conj(j)\n    dy_j_jbar = j * dy_jbar + jbar * dy_j\n    k_squared = 1.0 + j * jbar\n    return (\n        one_minus_y\n        / 8.0\n        * (dy_j * dy_jbar - dy_j_jbar**2 / (4.0 * k_squared))\n    )\n\n\n# Test functions for Q equation\n\n\ndef integrand_for_q_pole_part(eth_beta):\n    return -4.0 * eth_beta\n\n\ndef integrand_for_q_regular_part(\n    _,\n    dy_beta,\n    dy_j,\n    j,\n    eth_dy_beta,\n    eth_j_jbar,\n    eth_jbar_dy_j,\n    ethbar_dy_j,\n    ethbar_j,\n    eth_r_divided_by_r,\n    k,\n):\n    # script_aq input is unused, needed only to match function signature from\n    # the C++\n    eth_dy_jbar = np.conj(ethbar_dy_j)\n    dy_jbar = np.conj(dy_j)\n    ethbar_r_divided_by_r = np.conj(eth_r_divided_by_r)\n    eth_jbar = np.conj(ethbar_j)\n    jbar = np.conj(j)\n    script_aq = (\n        -eth_jbar_dy_j / 4.0\n        + j * eth_dy_jbar / 4.0\n        - eth_jbar * dy_j / 4.0\n        + eth_j_jbar * (jbar * dy_j + j * dy_jbar) / (8.0 * (1.0 + j * jbar))\n        + (j * dy_jbar - jbar * dy_j) * eth_r_divided_by_r / 4.0\n    )\n    return -(\n        2.0 * script_aq\n        + 2.0 * j * np.conj(script_aq) / k\n        - 2.0 * eth_dy_beta\n        + ethbar_dy_j / k\n        - 2.0 * dy_beta * eth_r_divided_by_r\n        + dy_j * ethbar_r_divided_by_r / k\n    )\n\n\n# Test function for U equation\n\n\ndef integrand_for_u(exp_2_beta, j, q, k, r):\n    qbar = np.conj(q)\n    return exp_2_beta / (2.0 * r) * (k * q - j * qbar)\n\n\n# Test functions for W equation\n\n\ndef integrand_for_w_pole_part(ethbar_u):\n    eth_ubar = np.conj(ethbar_u)\n    return eth_ubar + ethbar_u\n\n\ndef integrand_for_w_regular_part(\n    _,\n    dy_u,\n    exp_2_beta,\n    j,\n    q,\n    eth_beta,\n    eth_eth_beta,\n    eth_ethbar_beta,\n    eth_ethbar_j,\n    eth_ethbar_j_jbar,\n    eth_j_jbar,\n    ethbar_dy_u,\n    ethbar_ethbar_j,\n    ethbar_j,\n    eth_r_divided_by_r,\n    k,\n    r,\n):\n    # script_av input is unused, needed only to match function signature from\n    # the C++\n    dy_ubar = np.conj(dy_u)\n    ethbar_beta = np.conj(eth_beta)\n    eth_dy_ubar = np.conj(ethbar_dy_u)\n    ethbar_ethbar_beta = np.conj(eth_eth_beta)\n    eth_eth_jbar = np.conj(ethbar_ethbar_j)\n    eth_jbar = np.conj(ethbar_j)\n    ethbar_j_jbar = np.conj(eth_j_jbar)\n    ethbar_r_divided_by_r = np.conj(eth_r_divided_by_r)\n    jbar = np.conj(j)\n    k_squared = 1.0 + j * jbar\n    qbar = np.conj(q)\n    script_av = (\n        eth_beta * eth_jbar\n        + ethbar_ethbar_j / 2.0\n        + j * ethbar_beta**2\n        + j * ethbar_ethbar_beta\n        + eth_j_jbar * ethbar_j_jbar / (8.0 * k_squared * k)\n        + 1 / (2.0 * k)\n        - eth_ethbar_j_jbar / (8.0 * k)\n        - eth_j_jbar * ethbar_beta / (2.0 * k)\n        - eth_jbar * ethbar_j / (4.0 * k)\n        - eth_ethbar_j * jbar / (4.0 * k)\n        + k / 2.0\n        - eth_ethbar_beta * k\n        - eth_beta * ethbar_beta * k\n        + 1.0 / 4.0 * (-k * q * qbar + j * qbar**2)\n    )\n    return (\n        1.0 / 4.0 * eth_dy_ubar\n        + 1.0 / 4.0 * ethbar_dy_u\n        + 1.0 / 4.0 * dy_u * ethbar_r_divided_by_r\n        + 1.0 / 4.0 * dy_ubar * eth_r_divided_by_r\n        - 1.0 / (2.0 * r)\n        + exp_2_beta * (script_av + np.conj(script_av)) / (4.0 * r)\n    )\n\n\n# Test functions for H equation\n\n\ndef integrand_for_h_pole_part(\n    j, u, w, eth_u, ethbar_j, ethbar_jbar_u, ethbar_u, k\n):\n    eth_ubar = np.conj(ethbar_u)\n    eth_j_ubar = np.conj(ethbar_jbar_u)\n    return (\n        -1.0 / 2.0 * eth_j_ubar\n        - j * eth_ubar\n        - 1.0 / 2.0 * j * ethbar_u\n        - k * eth_u\n        - 1.0 / 2.0 * u * ethbar_j\n        + 2.0 * j * w\n    )\n\n\ndef integrand_for_h_regular_part(\n    _0,\n    _1,\n    _2,\n    dy_dy_j,\n    dy_j,\n    dy_w,\n    exp_2_beta,\n    j,\n    q,\n    u,\n    w,\n    eth_beta,\n    eth_eth_beta,\n    eth_ethbar_beta,\n    eth_ethbar_j,\n    eth_ethbar_j_jbar,\n    eth_j_jbar,\n    eth_q,\n    eth_u,\n    eth_ubar_dy_j,\n    ethbar_dy_j,\n    ethbar_ethbar_j,\n    ethbar_j,\n    ethbar_jbar_dy_j,\n    ethbar_jbar_q_minus_2_eth_beta,\n    ethbar_q,\n    ethbar_u,\n    du_r_divided_by_r,\n    eth_r_divided_by_r,\n    k,\n    one_minus_y,\n    r,\n):\n    # script_aj, script_bj, and script_cj input is unused, needed only to match\n    # function signature from the C++\n    jbar = np.conj(j)\n    k_squared = 1.0 + j * jbar\n    eth_eth_jbar = np.conj(ethbar_ethbar_j)\n    ethbar_eth_jbar = np.conj(eth_ethbar_j)\n    eth_jbar = np.conj(ethbar_j)\n    ethbar_j_jbar = np.conj(eth_j_jbar)\n\n    script_aj = (\n        1.0 / 4.0 * eth_eth_jbar\n        - 1.0 / (4.0 * k * k_squared)\n        - eth_ethbar_j_jbar / (16.0 * k * k_squared)\n        + j * ethbar_eth_jbar / (16.0 * k * k_squared)\n        + 3.0 / (4.0 * k)\n        - eth_ethbar_beta / (4.0 * k)\n        - eth_ethbar_j * jbar * (1.0 - 1.0 / (4.0 * k_squared)) / (4.0 * k)\n        + 1.0\n        / 2.0\n        * eth_jbar\n        * (\n            eth_beta\n            + j * ethbar_j_jbar / (4.0 * k * k_squared)\n            - ethbar_j * (-1.0 + 2.0 * k_squared) / (4.0 * k * k_squared)\n            - 1.0 / 2.0 * q\n        )\n    )\n\n    dy_jbar = np.conj(dy_j)\n    dy_j_jbar = j * dy_jbar + jbar * dy_j\n    eth_dy_jbar = np.conj(ethbar_dy_j)\n    eth_j_dy_jbar = np.conj(ethbar_jbar_dy_j)\n    ubar = np.conj(u)\n\n    script_bj = (\n        (\n            -eth_u * jbar * dy_j_jbar / (4.0 * k)\n            + 1.0 / 2.0 * dy_w\n            + 1.0 / (4.0 * r)\n            + 1.0 / 4.0 * ethbar_j * dy_jbar * u\n            - ethbar_j_jbar * dy_j_jbar * u / (8.0 * k_squared)\n            - 1.0 / 4.0 * j * eth_dy_jbar * ubar\n            + 1.0 / 4.0 * eth_j_dy_jbar * ubar\n        )\n        + one_minus_y\n        * (\n            du_r_divided_by_r\n            * dy_j\n            * 1.0\n            / 4.0\n            * (-2.0 * dy_jbar + jbar * dy_j_jbar / k_squared)\n            - 1.0 / 4.0 * dy_j * dy_jbar * w\n            + w * dy_j_jbar**2 / (16.0 * k_squared)\n        )\n        + one_minus_y**2\n        * (\n            -dy_j * dy_jbar / (8.0 * r)\n            + dy_j_jbar**2 / (32.0 * k_squared * r)\n        )\n    )\n\n    script_cj = 1.0 / 2.0 * ethbar_j * k * (eth_beta - 1.0 / 2.0 * q)\n\n    eth_j_qbar_minus_2_ethbar_beta = np.conj(ethbar_jbar_q_minus_2_eth_beta)\n    eth_qbar = np.conj(ethbar_q)\n    ethbar_r_divided_by_r = np.conj(eth_r_divided_by_r)\n    ethbar_ubar = np.conj(eth_u)\n    eth_ubar = np.conj(ethbar_u)\n    ubar = np.conj(u)\n    dy_jbar = np.conj(dy_j)\n\n    return (\n        -1.0 / 2.0 * eth_ubar_dy_j\n        - 1.0 / 2.0 * u * ethbar_dy_j\n        - 1.0 / 2.0 * u * dy_j * ethbar_r_divided_by_r\n        + j * (script_bj + np.conj(script_bj))\n        + exp_2_beta\n        / (2.0 * r)\n        * (\n            script_cj\n            + j**2 / k_squared * np.conj(script_cj)\n            + eth_eth_beta\n            - 1.0 / 2.0 * eth_q\n            - j * (script_aj + np.conj(script_aj))\n            + eth_j_qbar_minus_2_ethbar_beta / (4.0 * k)\n            - j * eth_qbar / (4.0 * k)\n            + (eth_beta - 1.0 / 2.0 * q) ** 2\n        )\n        - dy_j\n        * (\n            jbar * eth_u / (2.0 * k)\n            - 1.0 / 2.0 * ethbar_ubar * j * k\n            + 1.0 / 4.0 * (eth_ubar - ethbar_u) * k_squared\n            + +1.0 / 2.0 * eth_r_divided_by_r * ubar\n            - 1.0 / 2.0 * w\n        )\n        + dy_jbar\n        * (\n            -1.0 / 4.0 * j**2 * (-eth_ubar + ethbar_u)\n            + eth_u * j * (j * jbar / (2.0 * k))\n        )\n        + one_minus_y\n        * (\n            1.0\n            / 2.0\n            * (-dy_j / r + 2.0 * du_r_divided_by_r * dy_dy_j + w * dy_dy_j)\n            + dy_j * (1.0 / 2.0 * dy_w + 1.0 / (2.0 * r))\n        )\n        + one_minus_y**2 * dy_dy_j / (4.0 * r)\n    )\n\n\ndef linear_factor_for_h(_, dy_j, j, one_minus_y):\n    # script_dj input is unused, needed only to match function signature from\n    # the C++\n    dy_jbar = np.conj(dy_j)\n    jbar = np.conj(j)\n    k_squared = 1.0 + j * np.conj(j)\n\n    return 1.0 + (\n        (1.0 / 4.0)\n        * np.conj(one_minus_y)\n        * j\n        * (-2.0 * dy_jbar + jbar * (jbar * dy_j + j * dy_jbar) / k_squared)\n    )\n\n\ndef linear_factor_for_conjugate_h(_, dy_j, j, one_minus_y):\n    # script_dj input is unused, needed only to match function signature from\n    # the C++\n    dy_jbar = np.conj(dy_j)\n    jbar = np.conj(j)\n    k_squared = 1.0 + j * np.conj(j)\n\n    return (\n        (1.0 / 4.0)\n        * one_minus_y\n        * j\n        * np.conj(\n            -2.0 * dy_jbar + jbar * (jbar * dy_j + j * dy_jbar) / k_squared\n        )\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Events/Test_ObserveFields.cpp\n  Events/Test_ObserveTimeStep.cpp\n  PARENT_SCOPE\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Events/Test_ObserveFields.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Evolution/Systems/Cce/Events/ObserveFields.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace {\nusing ObserveFields = Events::ObserveFields;\nconstexpr size_t l_max = 3;\nconstexpr size_t num_radial_grid_points = 2;\nconstexpr size_t num_angular_grid_points =\n    Spectral::Swsh::number_of_swsh_collocation_points(l_max);\nconstexpr size_t num_volume_grid_points =\n    num_radial_grid_points * num_angular_grid_points;\nconstexpr double time = 1.3;\n\nstruct MockWriteVolumeData {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename DataBox = db::DataBox<DbTagsList>>\n  static void apply(db::DataBox<DbTagsList>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const std::string& /*h5_file_name*/,\n                    const std::string& subfile_path,\n                    const observers::ObservationId& /*observation_id*/,\n                    std::vector<ElementVolumeData>&& volume_data) {\n    // If we were to have made the data non-zero values, the event does too many\n    // transformations for us to calculate what the result would be by hand, so\n    // we just check that the sizes are correct, and that the (code) time is\n    // correct\n    const size_t l_plus_one_squared = square(l_max + 1);\n    const DataVector& data_v =\n        std::get<DataVector>(volume_data[0].tensor_components[0].data);\n    if (subfile_path.find(\"InertialRetardedTime\") != std::string::npos) {\n      CHECK(data_v.size() == 2 * l_plus_one_squared);\n    } else if (subfile_path.find(\"OneMinusY\") != std::string::npos) {\n      CHECK(data_v.size() == num_radial_grid_points);\n    } else {\n      CHECK(data_v.size() ==\n            2 * l_plus_one_squared * num_radial_grid_points);\n    }\n  }\n};\n\nvoid check_h5_file(const std::string& filename_prefix) {\n  const h5::H5File<h5::AccessType::ReadOnly> h5_file{filename_prefix + \".h5\"};\n  const size_t l_plus_one_squared = square(l_max + 1);\n  {\n    const auto& u_volume_data =\n        h5_file.get<h5::VolumeData>(\"/CceVolumeData/InertialRetardedTime\");\n    const size_t observation_id = u_volume_data.find_observation_id(time);\n\n    const TensorComponent inertial_retarded_time_component =\n        u_volume_data.get_tensor_component(observation_id,\n                                           \"InertialRetardedTime\");\n\n    const DataVector inertial_retarded_time =\n        std::get<DataVector>(inertial_retarded_time_component.data);\n\n    CHECK(inertial_retarded_time.size() == 2 * l_plus_one_squared);\n    h5_file.close_current_object();\n  }\n  {\n    const auto& one_minus_y_volume_data =\n        h5_file.get<h5::VolumeData>(\"/CceVolumeData/OneMinusY\");\n    const size_t observation_id =\n        one_minus_y_volume_data.find_observation_id(time);\n\n    const TensorComponent one_minus_y_component =\n        one_minus_y_volume_data.get_tensor_component(observation_id,\n                                           \"OneMinusY\");\n\n    const DataVector one_minus_y =\n        std::get<DataVector>(one_minus_y_component.data);\n\n    CHECK(one_minus_y.size() == num_radial_grid_points);\n    h5_file.close_current_object();\n  }\n  {\n    const auto& volume_data =\n        h5_file.get<h5::VolumeData>(\"/CceVolumeData/VolumeData\");\n    const size_t observation_id = volume_data.find_observation_id(time);\n\n    const TensorComponent J_component =\n        volume_data.get_tensor_component(observation_id, \"J\");\n\n    const DataVector J_interleaved =\n        std::get<DataVector>(J_component.data);\n    CHECK(J_interleaved.size() ==\n          2 * l_plus_one_squared * num_radial_grid_points);\n    h5_file.close_current_object();\n  }\n}\n\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 tmpl::list<observers::Tags::H5FileLock>>>>>;\n  using const_global_cache_tags =\n      tmpl::list<observers::Tags::VolumeFileName>;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n\n  using replace_these_threaded_actions =\n      tmpl::list<observers::ThreadedActions::WriteVolumeData>;\n  using with_these_threaded_actions = tmpl::list<MockWriteVolumeData>;\n};\n\ntemplate <typename Metavariables>\nstruct MockElement {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<tmpl::push_back<\n          tmpl::list_difference<\n            ObserveFields::available_tags_to_observe,\n            tmpl::list<\n              Tags::Psi0, Tags::Psi1, Tags::Psi2,\n              Tags::NewmanPenroseAlpha, Tags::NewmanPenroseBeta,\n              Tags::NewmanPenroseGamma, Tags::NewmanPenroseEpsilon,\n              // Tags::NewmanPenroseKappa, // in our tetrad, \\kappa=0\n              Tags::NewmanPenroseTau, Tags::NewmanPenroseSigma,\n              Tags::NewmanPenroseRho,\n              Tags::NewmanPenrosePi, Tags::NewmanPenroseNu,\n              Tags::NewmanPenroseMu, Tags::NewmanPenroseLambda\n              >>,\n          Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                           Spectral::Swsh::Tags::Eth>,\n          Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                           Spectral::Swsh::Tags::Eth>,\n          Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                           Spectral::Swsh::Tags::Ethbar>,\n          Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                           Spectral::Swsh::Tags::Ethbar>,\n          Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                           Spectral::Swsh::Tags::Eth>,\n          Tags::Exp2Beta,\n          Tags::BondiK, Tags::LMax, Tags::NumberOfRadialPoints,\n          ::Tags::Time>>>>>;\n};\n\nstruct Metavars {\n  void pup(PUP::er& /*p*/) {}\n  using observed_reduction_data_tags = tmpl::list<>;\n  using component_list =\n      tmpl::list<MockObserverWriter<Metavars>, MockElement<Metavars>>;\n};\n\nvoid test(const bool write_synchronously) {\n  using metavars = Metavars;\n  using obs_writer = MockObserverWriter<metavars>;\n  using element = MockElement<metavars>;\n\n  const std::vector<std::string> observe_field_names{\n    \"InertialRetardedTime\", \"J\", \"Psi0\", \"Psi1\", \"Psi2\", \"Dy(H)\", \"OneMinusY\",\n    \"NewmanPenroseAlpha\"};\n  const std::string subgroup_name{\"CceVolumeData\"};\n  const Cce::Events::ObserveFields fields{subgroup_name, observe_field_names};\n  const Cce::Events::ObserveFields serialized_fields =\n      serialize_and_deserialize(fields);\n\n  const std::string volume_filename_prefix{\"PineTree\"};\n  if (file_system::check_if_file_exists(volume_filename_prefix + \".h5\")) {\n    file_system::rm(volume_filename_prefix + \".h5\", true);\n  }\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{\n      {volume_filename_prefix},\n      {},\n      std::vector<size_t>{write_synchronously ? 1_st : 2_st}};\n  ActionTesting::emplace_nodegroup_component_and_initialize<obs_writer>(\n      make_not_null(&runner), {Parallel::NodeLock{}});\n  ActionTesting::emplace_singleton_component_and_initialize<element>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, {});\n\n  auto& cache = ActionTesting::cache<obs_writer>(runner, 0);\n  auto& box = ActionTesting::get_databox<element>(make_not_null(&runner), 0);\n  auto obs_box = make_observation_box<\n      typename Cce::Events::ObserveFields::compute_tags_for_observation_box>(\n      make_not_null(&box));\n  const int array_index = 0;\n  const obs_writer* const component = nullptr;\n  const Event::ObservationValue observation_value{};\n\n  // We only set the sizes of the tensors that we are using\n  const auto set_number = [&box](const auto tag_v, const auto value_to_set_to) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    db::mutate<tag>(\n        [&value_to_set_to](\n            const gsl::not_null<typename tag::type*> value_to_set) {\n          *value_to_set = value_to_set_to;\n        },\n        make_not_null(&box));\n  };\n  const auto size_data = [&box](auto tag_v, const size_t size) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    db::mutate<tag>(\n        [&size](const auto tensor) {\n          // All are scalars\n          get(*tensor) = ComplexDataVector{size, 1.0};\n        },\n        make_not_null(&box));\n  };\n  set_number(Tags::LMax{}, l_max);\n  set_number(Tags::NumberOfRadialPoints{}, num_radial_grid_points);\n  set_number(::Tags::Time{}, time);\n  size_data(Tags::ComplexInertialRetardedTime{}, num_angular_grid_points);\n\n  tmpl::for_each<\n      tmpl::list<Tags::BondiJ, Tags::OneMinusY,\n                 Tags::Dy<Tags::BondiJ>, Tags::Dy<Tags::Dy<Tags::BondiJ>>,\n                 Tags::BondiK, Tags::BondiQ, Tags::Dy<Tags::BondiQ>,\n                 Tags::EthRDividedByR,\n                 Tags::BondiBeta, Tags::Dy<Tags::BondiBeta>,\n                 Tags::BondiH, Tags::Dy<Tags::BondiH>,\n                 Tags::BondiU, Tags::Dy<Tags::BondiU>,\n                 Tags::BondiW, Tags::Dy<Tags::BondiW>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                  Spectral::Swsh::Tags::Ethbar>,\n                 Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                                  Spectral::Swsh::Tags::Eth>,\n                 Tags::Exp2Beta,\n                 Tags::BondiR>>([&size_data](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    size_data(tag{}, num_volume_grid_points);\n  });\n\n  serialized_fields(obs_box, cache, array_index, component, observation_value);\n\n  if (write_synchronously) {\n    check_h5_file(volume_filename_prefix);\n  } else {\n    // 1 each for InertialRetardedTime, OneMinusY, and 1 for all the rest of the\n    // volume fields together.\n    const size_t expected_number_of_actions = 3;\n    CHECK(ActionTesting::number_of_queued_threaded_actions<obs_writer>(\n              runner, 0) == expected_number_of_actions);\n    for (size_t i = 0; i < expected_number_of_actions; i++) {\n      ActionTesting::invoke_queued_threaded_action<obs_writer>(\n          make_not_null(&runner), 0);\n    }\n  }\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH((Cce::Events::ObserveFields{\"CceVolumeData\",\n        std::vector<std::string>{\"Unique\", \"Duplicate\", \"Duplicate\"}}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"more than once in list of variables to observe\"));\n\n  CHECK_THROWS_WITH(\n      (Cce::Events::ObserveFields{\"CceVolumeData\",\n         std::vector<std::string>{\"MisspelledTag\"}}),\n      Catch::Matchers::ContainsSubstring(\n          \"MisspelledTag is not an available variable. Available variables:\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Events.ObserveFields\",\n                  \"[Unit][Evolution]\") {\n  test(true);\n  test(false);\n  test_errors();\n}\n}  // namespace\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Events/Test_ObserveTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/Cce/Events/ObserveTimeStep.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace {\nusing ObserveTimeStep = Events::ObserveTimeStep;\nconstexpr double time = 1.3;\nconstexpr double step = 0.27;\nstatic std::string filename = \"Strawberry\";\n\nstruct MockWriteReductionDataRow {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& file_legend,\n                    const std::tuple<std::vector<double>>& data_row) {\n    CHECK(subfile_name == \"/Cce/\" + filename);\n    CHECK(file_legend == std::vector<std::string>{\"Time\", \"Time Step\"});\n    CHECK(std::get<0>(data_row) == std::vector<double>{time, step});\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<>>>>>;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n\n  using replace_these_threaded_actions =\n      tmpl::list<observers::ThreadedActions::WriteReductionDataRow>;\n  using with_these_threaded_actions = tmpl::list<MockWriteReductionDataRow>;\n};\n\nstruct Metavars {\n  void pup(PUP::er& /*p*/) {}\n  using observed_reduction_data_tags = tmpl::list<>;\n  using component_list = tmpl::list<MockObserverWriter<Metavars>>;\n};\n\nvoid test() {\n  using metavars = Metavars;\n  using obs_writer = MockObserverWriter<metavars>;\n\n  // No need to print things to terminal here\n  ObserveTimeStep observe_time_step{filename, false};\n  ObserveTimeStep serialized_observe_time_step =\n      serialize_and_deserialize(observe_time_step);\n\n  Slab slab{time, time + step};\n  TimeDelta time_delta{slab, 1};\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{}};\n  ActionTesting::emplace_nodegroup_component_and_initialize<obs_writer>(\n      make_not_null(&runner), {});\n\n  auto& cache = ActionTesting::cache<obs_writer>(runner, 0);\n  const int array_index = 0;\n  const obs_writer* const component = nullptr;\n  const Event::ObservationValue observation_value{\"ObservationValue\", time};\n\n  serialized_observe_time_step(time_delta, cache, array_index, component,\n                               observation_value);\n\n  CHECK(ActionTesting::number_of_queued_threaded_actions<obs_writer>(runner,\n                                                                     0) == 1);\n  ActionTesting::invoke_queued_threaded_action<obs_writer>(\n      make_not_null(&runner), 0);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Events.ObserveTimeStep\",\n                  \"[Unit][Evolution]\") {\n  test();\n}\n}  // namespace\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/InterfaceManagers/Test_GhLocalTimeStepping.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLocalTimeStepping.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce::InterfaceManagers {\nnamespace {\n\ntemplate <typename Generator>\nvoid test_gh_local_time_stepping_interface_manager(\n    const gsl::not_null<Generator*> gen) {\n  register_derived_classes_with_charm<intrp::SpanInterpolator>();\n  // the frequency has to be small to be kind to the time stepper for the ~.1\n  // step size in this test\n  UniformCustomDistribution<double> value_dist{0.1, 1.0};\n  const double frequency = value_dist(*gen);\n\n  const Slab source_slab{0.0, 0.01};\n  // target slab twice as large to verify that the details of the TimeStepId's\n  // don't matter to the interface manager\n  const Slab target_slab{0.0, 0.02};\n\n  const std::vector<TimeStepId> source_time_steps{\n      {true, 0, {source_slab, {0, 1}}}, {true, 0, {source_slab, {1, 10}}},\n      {true, 0, {source_slab, {1, 5}}}, {true, 0, {source_slab, {3, 10}}},\n      {true, 0, {source_slab, {2, 5}}}, {true, 0, {source_slab, {1, 2}}},\n      {true, 0, {source_slab, {3, 5}}}, {true, 0, {source_slab, {7, 10}}},\n      {true, 0, {source_slab, {4, 5}}}, {true, 0, {source_slab, {9, 10}}}};\n  const std::vector<TimeStepId> target_time_steps{\n      {true, 0, {target_slab, {0, 1}}},   {true, 0, {target_slab, {1, 40}}},\n      {true, 0, {target_slab, {3, 40}}},  {true, 0, {target_slab, {7, 40}}},\n      {true, 0, {target_slab, {19, 80}}}, {true, 0, {target_slab, {7, 20}}},\n      {true, 0, {target_slab, {15, 40}}}, {true, 0, {target_slab, {1, 2}}}};\n  InterfaceManagers::GhLocalTimeStepping interface_manager{\n      std::make_unique<intrp::BarycentricRationalSpanInterpolator>(2u, 2u)};\n\n  // These represent data at time = 0, the time dependence for item i in the\n  // vector will be a * cos((frequency + i * 0.05) * t), so the first derivative\n  // is -a * (frequency + i * 0.05) * sin((frequency + i * 0.05) * t)\n  const double frequency_increment = 5.0e-2;\n  tnsr::aa<DataVector, 3> spacetime_metric{5_st};\n  tnsr::iaa<DataVector, 3> phi{5_st};\n  tnsr::aa<DataVector, 3> pi{5_st};\n  fill_with_random_values(make_not_null(&spacetime_metric), gen,\n                          make_not_null(&value_dist));\n  fill_with_random_values(make_not_null(&phi), gen, make_not_null(&value_dist));\n  fill_with_random_values(make_not_null(&pi), gen, make_not_null(&value_dist));\n\n  const auto check_no_retrieval =\n      [](const gsl::not_null<InterfaceManagers::GhLocalTimeStepping*>\n             local_interface_manager) {\n        CHECK_FALSE(\n            static_cast<bool>(local_interface_manager\n                                  ->retrieve_and_remove_first_ready_gh_data()));\n      };\n\n  const auto insert_source_data =\n      [&source_time_steps, &spacetime_metric, &phi, &pi, &frequency,\n       &frequency_increment](\n          const gsl::not_null<InterfaceManagers::GhLocalTimeStepping*>\n              local_interface_manager,\n          const size_t index) {\n        const auto current_time_step_id = source_time_steps[index];\n        std::optional<double> previous_time;\n        if (index != 0) {\n          previous_time = source_time_steps[index - 1].substep_time();\n        }\n        const double current_time = current_time_step_id.substep_time();\n\n        tnsr::aa<DataVector, 3> current_spacetime_metric{5_st};\n        tnsr::iaa<DataVector, 3> current_phi{5_st};\n        tnsr::aa<DataVector, 3> current_pi{5_st};\n\n        tnsr::aa<DataVector, 3> current_dt_spacetime_metric{5_st};\n        tnsr::iaa<DataVector, 3> current_dt_phi{5_st};\n        tnsr::aa<DataVector, 3> current_dt_pi{5_st};\n        for (size_t i = 0; i < tnsr::aa<DataVector, 3>::size(); ++i) {\n          for (size_t j = 0; j < current_spacetime_metric[i].size(); ++j) {\n            current_spacetime_metric[i][j] =\n                spacetime_metric[i][j] *\n                cos((frequency + j * frequency_increment) * current_time);\n\n            current_pi[i][j] =\n                pi[i][j] *\n                cos((frequency + j * frequency_increment) * current_time);\n          }\n        }\n        for (size_t i = 0; i < tnsr::iaa<DataVector, 3>::size(); ++i) {\n          for (size_t j = 0; j < current_phi[i].size(); ++j) {\n            current_phi[i][j] =\n                phi[i][j] *\n                cos((frequency + j * frequency_increment) * current_time);\n          }\n        }\n        local_interface_manager->insert_gh_data(\n            LinkedMessageId<double>{current_time_step_id.substep_time(),\n                                    previous_time},\n            current_spacetime_metric, current_phi, current_pi);\n      };\n\n  const auto request_target_time =\n      [&target_time_steps](\n          const gsl::not_null<InterfaceManagers::GhLocalTimeStepping*>\n              local_interface_manager,\n          const size_t index) {\n        local_interface_manager->request_gh_data(target_time_steps[index]);\n      };\n\n  const auto check_retrieval = [&target_time_steps, &frequency,\n                                &frequency_increment, &spacetime_metric, &pi,\n                                &phi](\n                                   const gsl::not_null<\n                                       InterfaceManagers::GhLocalTimeStepping*>\n                                       local_interface_manager,\n                                   const size_t index,\n                                   Approx local_approx = approx) {\n    const double current_time = target_time_steps[index].substep_time();\n    tnsr::aa<DataVector, 3> expected_spacetime_metric{5_st};\n    tnsr::iaa<DataVector, 3> expected_phi{5_st};\n    tnsr::aa<DataVector, 3> expected_pi{5_st};\n\n    for (size_t i = 0; i < tnsr::aa<DataVector, 3>::size(); ++i) {\n      for (size_t j = 0; j < expected_spacetime_metric[i].size(); ++j) {\n        expected_spacetime_metric[i][j] =\n            spacetime_metric[i][j] *\n            cos((frequency + j * frequency_increment) * current_time);\n\n        expected_pi[i][j] =\n            pi[i][j] *\n            cos((frequency + j * frequency_increment) * current_time);\n      }\n    }\n    for (size_t i = 0; i < tnsr::iaa<DataVector, 3>::size(); ++i) {\n      for (size_t j = 0; j < expected_phi[i].size(); ++j) {\n        expected_phi[i][j] =\n            phi[i][j] *\n            cos((frequency + j * frequency_increment) * current_time);\n      }\n    }\n\n    const auto retrieved_data =\n        local_interface_manager->retrieve_and_remove_first_ready_gh_data();\n    REQUIRE(static_cast<bool>(retrieved_data));\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        SINGLE_ARG(get<gr::Tags::SpacetimeMetric<DataVector, 3>>(\n            get<1>(*retrieved_data))),\n        expected_spacetime_metric, local_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        SINGLE_ARG(get<gh::Tags::Pi<DataVector, 3>>(get<1>(*retrieved_data))),\n        expected_pi, local_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        SINGLE_ARG(get<gh::Tags::Phi<DataVector, 3>>(get<1>(*retrieved_data))),\n        expected_phi, local_approx);\n  };\n\n  // Test plan (given in ratios of the source interval):\n  // insert 0.0\n  // request 0.0\n  // fail to retrieve 0.0\n  // request 0.05\n  // request 0.15\n  //\n  // insert 0.2\n  // request 0.35\n  // fail to retrieve 0.0\n  // insert 0.3\n  // insert 0.4\n  // fail to retrieve 0.0\n  // insert 0.1\n  // retrieve 0.0\n  // retrieve 0.05\n  // retrieve 0.15\n  // retrieve 0.35\n  //\n  // request 0.475 (19/40)\n  // fail to retrieve .475\n  // insert .5 data\n  // retrieve .475\n  // fail to retrieve no requests\n  // insert .6 data\n  // request .7\n  //\n  // clone and serialize; check remaining for original, serialized, and clone\n  //\n  // request .75\n  // insert .7 data\n  // insert .8 data\n  // retrieve .7\n  // retrieve .75\n\n  check_no_retrieval(make_not_null(&interface_manager));\n\n  insert_source_data(make_not_null(&interface_manager), 0_st);\n  request_target_time(make_not_null(&interface_manager), 0_st);\n  check_no_retrieval(make_not_null(&interface_manager));\n  request_target_time(make_not_null(&interface_manager), 1_st);\n  request_target_time(make_not_null(&interface_manager), 2_st);\n\n  insert_source_data(make_not_null(&interface_manager), 2_st);\n  request_target_time(make_not_null(&interface_manager), 3_st);\n  check_no_retrieval(make_not_null(&interface_manager));\n  insert_source_data(make_not_null(&interface_manager), 3_st);\n  insert_source_data(make_not_null(&interface_manager), 4_st);\n  check_no_retrieval(make_not_null(&interface_manager));\n  insert_source_data(make_not_null(&interface_manager), 1_st);\n  check_retrieval(make_not_null(&interface_manager), 0_st,\n                  Approx::custom().epsilon(1.e-5).scale(1.));\n  check_retrieval(make_not_null(&interface_manager), 1_st,\n                  Approx::custom().epsilon(1.e-7).scale(1.));\n  check_retrieval(make_not_null(&interface_manager), 2_st,\n                  Approx::custom().epsilon(1.e-9).scale(1.));\n  check_retrieval(make_not_null(&interface_manager), 3_st,\n                  Approx::custom().epsilon(1.e-11).scale(1.));\n\n  request_target_time(make_not_null(&interface_manager), 4_st);\n  check_no_retrieval(make_not_null(&interface_manager));\n  insert_source_data(make_not_null(&interface_manager), 5_st);\n  check_retrieval(make_not_null(&interface_manager), 4_st,\n                  Approx::custom().epsilon(1.e-11).scale(1.));\n  check_no_retrieval(make_not_null(&interface_manager));\n  insert_source_data(make_not_null(&interface_manager), 6_st);\n  request_target_time(make_not_null(&interface_manager), 5_st);\n\n  const auto second_half_checks =\n      [&request_target_time, &check_no_retrieval, &check_retrieval,\n       &insert_source_data](\n          const gsl::not_null<InterfaceManagers::GhLocalTimeStepping*>\n              local_interface_manager) {\n        request_target_time(local_interface_manager, 6_st);\n        insert_source_data(local_interface_manager, 7_st);\n        insert_source_data(local_interface_manager, 8_st);\n        check_retrieval(local_interface_manager, 5_st,\n                        Approx::custom().epsilon(1.e-11).scale(1.));\n        check_retrieval(local_interface_manager, 6_st,\n                        Approx::custom().epsilon(1.e-11).scale(1.));\n        check_no_retrieval(local_interface_manager);\n      };\n  auto clone = interface_manager.get_clone();\n  auto serialized_and_deserialized_interface_manager =\n      serialize_and_deserialize(interface_manager);\n  {\n    INFO(\"Checking original\");\n    second_half_checks(make_not_null(&interface_manager));\n  }\n  {\n    INFO(\"Checking clone\");\n    second_half_checks(make_not_null(\n        dynamic_cast<InterfaceManagers::GhLocalTimeStepping*>(&(*clone))));\n  }\n  {\n    INFO(\"Checking serialized and deserialized\");\n    second_half_checks(\n        make_not_null(&serialized_and_deserialized_interface_manager));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.GhLocalTimeStepping\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_gh_local_time_stepping_interface_manager(make_not_null(&gen));\n}\n}  // namespace Cce::InterfaceManagers\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/InterfaceManagers/Test_GhLockstep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce {\nnamespace {\n\ntemplate <typename Generator>\nvoid test_gh_lockstep_interface_manager(const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<double> value_dist{-5.0, 5.0};\n  UniformCustomDistribution<size_t> timestep_dist{1, 5};\n\n  std::vector<std::tuple<TimeStepId,\n                         InterfaceManagers::GhInterfaceManager::gh_variables>>\n      expected_gh_data(7);\n  size_t running_total = 0;\n  InterfaceManagers::GhLockstep interface_manager{};\n  tnsr::aa<DataVector, 3> spacetime_metric{5_st};\n  tnsr::iaa<DataVector, 3> phi{5_st};\n  tnsr::aa<DataVector, 3> pi{5_st};\n  // insert some time ids\n  for (size_t i = 0; i < 7; ++i) {\n    const size_t substep = running_total % 3;\n    const size_t step = running_total / 3;\n    // RK3-style substep\n    int substep_numerator = 0;\n    if (substep == 1) {\n      substep_numerator = 2;\n    } else if (substep == 2) {\n      substep_numerator = 1;\n    }\n    const Time step_time{{static_cast<double>(step), step + 1.0}, {0, 1}};\n    const double substep_time{\n        static_cast<double>(step) + 0.5 * substep_numerator};\n    const TimeStepId time_id{true, static_cast<int64_t>(step), step_time,\n                             substep, step_time.slab().duration(),\n                             substep_time};\n    fill_with_random_values(make_not_null(&spacetime_metric), gen,\n                            make_not_null(&value_dist));\n    fill_with_random_values(make_not_null(&phi), gen,\n                            make_not_null(&value_dist));\n    fill_with_random_values(make_not_null(&pi), gen,\n                            make_not_null(&value_dist));\n    interface_manager.insert_gh_data(time_id, spacetime_metric, phi, pi);\n    InterfaceManagers::GhInterfaceManager::gh_variables vars{\n        get<0, 0>(spacetime_metric).size()};\n    get<gr::Tags::SpacetimeMetric<DataVector, 3>>(vars) = spacetime_metric;\n    get<gh::Tags::Pi<DataVector, 3>>(vars) = pi;\n    get<gh::Tags::Phi<DataVector, 3>>(vars) = phi;\n    expected_gh_data[i] = std::make_tuple(time_id, std::move(vars));\n    running_total += timestep_dist(*gen);\n  }\n\n  const auto check_data_retrieval_against_vector =\n      [&expected_gh_data](\n          const gsl::not_null<InterfaceManagers::GhInterfaceManager*>\n              local_interface_manager,\n          const size_t expected_number_of_gh_times, const size_t vector_index) {\n        CHECK(local_interface_manager->number_of_pending_requests() == 0);\n        CHECK(local_interface_manager->number_of_gh_times() ==\n              expected_number_of_gh_times);\n        auto retrieved_data =\n            local_interface_manager->retrieve_and_remove_first_ready_gh_data();\n        REQUIRE(retrieved_data);\n        CHECK(get<0>(*retrieved_data) ==\n              get<0>(expected_gh_data[vector_index]));\n        CHECK(get<1>(*retrieved_data) ==\n              get<1>(expected_gh_data[vector_index]));\n\n        CHECK(local_interface_manager->number_of_pending_requests() == 0);\n        CHECK(local_interface_manager->number_of_gh_times() ==\n              expected_number_of_gh_times - 1);\n      };\n  {\n    INFO(\"Retrieve data from directly constructed manager\");\n    interface_manager.request_gh_data(get<0>(expected_gh_data[0]));\n    interface_manager.request_gh_data(get<0>(expected_gh_data[1]));\n    check_data_retrieval_against_vector(make_not_null(&interface_manager), 7_st,\n                                        0_st);\n    check_data_retrieval_against_vector(make_not_null(&interface_manager), 6_st,\n                                        1_st);\n  }\n  {\n    INFO(\"Retrieve data from serialized and deserialized manager\");\n    // check that the state is preserved during serialization\n    auto serialized_and_deserialized_interface_manager =\n        serialize_and_deserialize(interface_manager);\n    serialized_and_deserialized_interface_manager.request_gh_data(\n        get<0>(expected_gh_data[2]));\n    check_data_retrieval_against_vector(\n        make_not_null(&serialized_and_deserialized_interface_manager), 5_st,\n        2_st);\n  }\n\n  {\n    INFO(\"Retrieve data from cloned unique_ptr to manager\");\n    // check that the state is preserved through cloning\n    auto cloned_interface_manager = interface_manager.get_clone();\n    cloned_interface_manager->request_gh_data(get<0>(expected_gh_data[2]));\n    check_data_retrieval_against_vector(\n        make_not_null(cloned_interface_manager.get()), 5_st, 2_st);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.GhLockstep\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_gh_lockstep_interface_manager(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/NewmanPenrose.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef newman_penrose_alpha(\n    bondi_j, eth_j, ethbar_j, bondi_k, bondi_r, bondi_q, eth_beta, one_minus_y\n):\n    one_plus_k = 1.0 + bondi_k\n    sqrt_one_plus_k = np.sqrt(one_plus_k)\n    q_plus_two_eth_beta = bondi_q + 2.0 * eth_beta\n\n    return (\n        one_minus_y\n        / (32.0 * bondi_r)\n        * (\n            1.0\n            / sqrt_one_plus_k\n            * (\n                (np.conj(bondi_j) ** 2 * eth_j) / (bondi_k * one_plus_k)\n                + 1.0\n                / bondi_k\n                * (\n                    bondi_j * np.conj(eth_j)\n                    + np.conj(bondi_j) * ethbar_j\n                    - np.conj(ethbar_j)\n                )\n                + (\n                    2.0 * np.conj(bondi_j) * q_plus_two_eth_beta\n                    - 3.0 * np.conj(ethbar_j)\n                )\n            )\n            - 2.0 * sqrt_one_plus_k * np.conj(q_plus_two_eth_beta)\n        )\n    )\n\n\ndef newman_penrose_beta(\n    bondi_j, eth_j, ethbar_j, bondi_k, bondi_r, bondi_q, eth_beta, one_minus_y\n):\n    one_plus_k = 1.0 + bondi_k\n    sqrt_one_plus_k = np.sqrt(one_plus_k)\n    q_plus_two_eth_beta = bondi_q + 2.0 * eth_beta\n\n    return (\n        one_minus_y\n        / (32.0 * bondi_r)\n        * (\n            1.0\n            / sqrt_one_plus_k\n            * (\n                (\n                    -(bondi_j**2) * np.conj(eth_j) / (bondi_k * one_plus_k)\n                    + 1.0\n                    / bondi_k\n                    * (\n                        -bondi_j * np.conj(ethbar_j)\n                        - np.conj(bondi_j) * eth_j\n                        + ethbar_j\n                    )\n                    + (\n                        2.0 * bondi_j * np.conj(q_plus_two_eth_beta)\n                        - 3.0 * ethbar_j\n                    )\n                )\n            )\n            - 2.0 * sqrt_one_plus_k * q_plus_two_eth_beta\n        )\n    )\n\n\ndef newman_penrose_gamma(\n    bondi_j,\n    dy_j,\n    eth_j,\n    ethbar_j,\n    bondi_k,\n    bondi_h,\n    bondi_r,\n    bondi_u,\n    eth_u,\n    ethbar_u,\n    bondi_w,\n    dy_w,\n    exp_2_beta,\n    one_minus_y,\n):\n    one_plus_k = 1.0 + bondi_k\n\n    return (\n        1.0\n        / (np.sqrt(32.0) * exp_2_beta)\n        * (\n            1.0\n            / (2.0 * one_plus_k)\n            * (\n                one_minus_y\n                * (one_minus_y / (2.0 * bondi_r) + bondi_w)\n                * (np.conj(bondi_j) * dy_j - bondi_j * np.conj(dy_j))\n                + (\n                    2.0 * np.conj(bondi_h) * bondi_j\n                    - 2.0 * bondi_h * np.conj(bondi_j)\n                    + bondi_u\n                    * (bondi_j * np.conj(eth_j) - np.conj(bondi_j) * ethbar_j)\n                    + np.conj(bondi_u)\n                    * (bondi_j * np.conj(ethbar_j) - np.conj(bondi_j) * eth_j)\n                )\n            )\n            + 2.0 * one_minus_y * dy_w\n            + (\n                2.0 * bondi_w\n                + bondi_j * np.conj(eth_u)\n                - np.conj(bondi_j) * eth_u\n                + bondi_k * (ethbar_u - np.conj(ethbar_u))\n            )\n        )\n    )\n\n\ndef newman_penrose_epsilon(\n    bondi_j, dy_j, bondi_k, bondi_r, dy_beta, one_minus_y\n):\n    return (\n        one_minus_y**2\n        / (np.sqrt(8.0) * bondi_r)\n        * (\n            dy_beta\n            + (bondi_j * np.conj(dy_j) - np.conj(bondi_j) * dy_j)\n            / (8.0 * (1.0 + bondi_k))\n        )\n    )\n\n\ndef newman_penrose_tau(\n    bondi_j, bondi_k, bondi_r, bondi_q, eth_beta, one_minus_y\n):\n    one_plus_k = 1.0 + bondi_k\n    sqrt_one_plus_k = np.sqrt(one_plus_k)\n    two_eth_beta_minus_q = 2.0 * eth_beta - bondi_q\n\n    return (\n        one_minus_y\n        / (8.0 * bondi_r)\n        * (\n            sqrt_one_plus_k * two_eth_beta_minus_q\n            - bondi_j * np.conj(two_eth_beta_minus_q) / sqrt_one_plus_k\n        )\n    )\n\n\ndef newman_penrose_sigma(bondi_j, dy_j, bondi_k, bondi_r, one_minus_y):\n    one_plus_k = 1.0 + bondi_k\n\n    return (\n        one_minus_y**2\n        / (np.sqrt(128.0) * bondi_k * bondi_r)\n        * (bondi_j**2 * np.conj(dy_j) / one_plus_k - one_plus_k * dy_j)\n    )\n\n\ndef newman_penrose_rho(bondi_r, one_minus_y):\n    return -one_minus_y / (np.sqrt(8.0) * bondi_r)\n\n\ndef newman_penrose_pi(\n    bondi_j, bondi_k, bondi_r, bondi_q, eth_beta, one_minus_y\n):\n    one_plus_k = 1.0 + bondi_k\n    sqrt_one_plus_k = np.sqrt(one_plus_k)\n    q_plus_two_eth_beta = bondi_q + 2.0 * eth_beta\n\n    return (\n        one_minus_y\n        / (8.0 * bondi_r)\n        * (\n            np.conj(bondi_j) * q_plus_two_eth_beta / sqrt_one_plus_k\n            - sqrt_one_plus_k * np.conj(q_plus_two_eth_beta)\n        )\n    )\n\n\ndef newman_penrose_nu(bondi_j, bondi_k, eth_w, exp_2_beta):\n    one_plus_k = 1.0 + bondi_k\n    sqrt_one_plus_k = np.sqrt(one_plus_k)\n\n    return (\n        1.0\n        / (2.0 * exp_2_beta)\n        * (\n            np.conj(bondi_j) * eth_w / sqrt_one_plus_k\n            - sqrt_one_plus_k * np.conj(eth_w)\n        )\n    )\n\n\ndef newman_penrose_mu(bondi_r, bondi_w, ethbar_u, exp_2_beta, one_minus_y):\n    return (\n        1.0\n        / (np.sqrt(8.0) * exp_2_beta)\n        * (np.conj(ethbar_u) + ethbar_u - one_minus_y / bondi_r - 2.0 * bondi_w)\n    )\n\n\ndef newman_penrose_lambda(\n    bondi_j,\n    dy_j,\n    eth_j,\n    ethbar_j,\n    bondi_k,\n    bondi_h,\n    bondi_r,\n    bondi_u,\n    eth_u,\n    ethbar_u,\n    bondi_w,\n    exp_2_beta,\n    one_minus_y,\n):\n    one_plus_k = 1.0 + bondi_k\n\n    inner1 = (\n        one_minus_y\n        / (2.0 * one_plus_k)\n        * (\n            (np.conj(bondi_j) ** 2 * dy_j - np.conj(dy_j)) / bondi_k\n            - (2.0 + bondi_k) * np.conj(dy_j)\n        )\n    )\n\n    inner2 = 2.0 * bondi_h + bondi_u * ethbar_j + np.conj(bondi_u) * eth_j\n\n    return (\n        1.0\n        / (np.sqrt(32.0) * exp_2_beta)\n        * (\n            (one_minus_y / bondi_r + 2.0 * bondi_w) * inner1\n            + 2.0 * one_plus_k * np.conj(eth_u)\n            + (\n                np.conj(inner2)\n                + 2.0 * np.conj(bondi_j) * (ethbar_u - np.conj(ethbar_u))\n            )\n            + np.conj(inner2) / bondi_k\n            - np.conj(bondi_j) ** 2\n            * (inner2 + 2.0 * bondi_k * eth_u)\n            / (bondi_k * one_plus_k)\n        )\n    )\n\n\ndef psi0(bondi_j, dy_j, dy_dy_j, bondi_k, bondi_r, one_minus_y):\n    dy_beta = (\n        0.125\n        * one_minus_y\n        * (\n            dy_j * np.conj(dy_j)\n            - 0.25\n            * (bondi_j * np.conj(dy_j) + np.conj(bondi_j) * dy_j) ** 2\n            / bondi_k**2\n        )\n    )\n    return (\n        one_minus_y**4\n        * 1.0\n        / (16.0 * bondi_r**2)\n        * (\n            (1.0 + bondi_k) * dy_beta * dy_j / bondi_k\n            - bondi_j**2 * dy_beta * np.conj(dy_j) / (bondi_k + bondi_k**2)\n            - bondi_j * np.conj(bondi_j) ** 2 * dy_j**2 / (4.0 * bondi_k**3)\n            - bondi_j**3 * np.conj(dy_j) ** 2 / (4.0 * bondi_k**3)\n            + 0.5 * (-1.0 - 1.0 / bondi_k) * dy_dy_j\n            + 0.5 * bondi_j**2 * np.conj(dy_dy_j) / (bondi_k**2 + bondi_k)\n            + 0.5\n            * bondi_j\n            * (1.0 + bondi_k**2)\n            * dy_j\n            * np.conj(dy_j)\n            / bondi_k**3\n        )\n    )\n\n\ndef psi1(\n    bondi_j,\n    dy_j,\n    bondi_k,\n    bondi_q,\n    dy_q,\n    bondi_r,\n    eth_r_divided_by_r,\n    dy_beta,\n    eth_beta,\n    eth_dy_beta,\n    one_minus_y,\n):\n    prefac = 1.0 / np.sqrt(128.0)\n    one_plus_k = 1.0 + bondi_k\n    eth_beta_plus_half_q = eth_beta + 0.5 * bondi_q\n    conj_j_times_dy_j = np.conj(bondi_j) * dy_j\n\n    inner_expr = bondi_j * (\n        -2.0 * np.conj(dy_q)\n        + np.conj(dy_j)\n        * (2.0 * eth_beta_plus_half_q + bondi_j * np.conj(eth_beta_plus_half_q))\n    ) + one_plus_k * (\n        eth_beta_plus_half_q * (conj_j_times_dy_j - np.conj(conj_j_times_dy_j))\n        + 2.0 * (dy_q + bondi_j * np.conj(dy_q))\n        - one_plus_k * (2.0 * dy_q + dy_j * np.conj(eth_beta_plus_half_q))\n    )\n\n    return (\n        prefac\n        * one_minus_y**2\n        / (bondi_r**2 * np.sqrt(one_plus_k))\n        * (\n            bondi_j * np.conj(eth_beta_plus_half_q)\n            - one_plus_k * eth_beta_plus_half_q\n            + one_minus_y\n            * (\n                eth_dy_beta * one_plus_k\n                - bondi_j * np.conj(eth_dy_beta)\n                + dy_beta\n                * (\n                    one_plus_k * eth_r_divided_by_r\n                    - bondi_j * np.conj(eth_r_divided_by_r)\n                )\n                + 0.25 * inner_expr / bondi_k\n            )\n        )\n    )\n\n\ndef psi2(\n    bondi_j,\n    bondi_k,\n    bondi_r,\n    dy_mu,\n    eth_pi,\n    ethbar_pi,\n    np_alpha,\n    np_beta,\n    np_epsilon,\n    np_sigma,\n    np_rho,\n    np_pi,\n    np_mu,\n    np_lambda,\n    one_minus_y,\n):\n    sqrt_one_plus_k = np.sqrt(1.0 + bondi_k)\n\n    return (\n        0.25\n        * one_minus_y\n        / bondi_r\n        * (\n            np.sqrt(2.0) * one_minus_y * dy_mu\n            + sqrt_one_plus_k * eth_pi\n            - bondi_j * ethbar_pi / sqrt_one_plus_k\n        )\n        + (np_epsilon + np.conj(np_epsilon) - np.conj(np_rho)) * np_mu\n        + (np.conj(np_alpha) - np_beta - np.conj(np_pi)) * np_pi\n        - np_sigma * np_lambda\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/ScriPlusValues.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef news(dy_du_bondi_j, beta, eth_beta, eth_eth_beta, boundary_r):\n    return 2.0 * np.conj(\n        -boundary_r * np.exp(-2.0 * beta) * dy_du_bondi_j\n        + eth_eth_beta\n        + 2.0 * eth_beta**2\n    )\n\n\ndef time_integral_psi_4(\n    exp_2_beta,\n    dy_bondi_u,\n    eth_dy_bondi_u,\n    dy_du_bondi_j,\n    boundary_r,\n    eth_r_divided_by_r,\n):\n    return (\n        2.0\n        * (boundary_r / exp_2_beta)\n        * np.conj(\n            eth_dy_bondi_u + eth_r_divided_by_r * dy_bondi_u + dy_du_bondi_j\n        )\n    )\n\n\ndef psi_3(\n    exp_2_beta,\n    eth_beta,\n    eth_ethbar_beta,\n    ethbar_eth_ethbar_beta,\n    dy_du_bondi_j,\n    ethbar_dy_du_bondi_j,\n    boundary_r,\n    eth_r_divided_by_r,\n):\n    return (\n        2.0 * np.conj(eth_beta)\n        + 4.0 * np.conj(eth_beta) * eth_ethbar_beta\n        + ethbar_eth_ethbar_beta\n        + 2.0 * boundary_r * (np.conj(dy_du_bondi_j) * eth_beta / exp_2_beta)\n        - 2.0\n        * boundary_r\n        * (0.5 / exp_2_beta)\n        * (\n            np.conj(ethbar_dy_du_bondi_j)\n            + eth_r_divided_by_r * np.conj(dy_du_bondi_j)\n        )\n    )\n\n\ndef psi_2(\n    exp_2_beta,\n    dy_bondi_q,\n    ethbar_dy_bondi_q,\n    dy_bondi_u,\n    eth_dy_bondi_u,\n    dy_dy_bondi_u,\n    ethbar_dy_dy_bondi_u,\n    dy_dy_bondi_w,\n    dy_bondi_j,\n    dy_du_bondi_j,\n    boundary_r,\n    eth_r_divided_by_r,\n):\n    return (\n        -0.25\n        / exp_2_beta\n        * (\n            exp_2_beta\n            * (-2.0 * boundary_r)\n            * (\n                np.conj(ethbar_dy_bondi_q)\n                + eth_r_divided_by_r * np.conj(dy_bondi_q)\n            )\n            + 2.0\n            * boundary_r**2\n            * (\n                np.conj(ethbar_dy_dy_bondi_u)\n                + 2.0 * eth_r_divided_by_r * np.conj(dy_dy_bondi_u)\n                + ethbar_dy_dy_bondi_u\n                + 2.0 * np.conj(eth_r_divided_by_r) * dy_dy_bondi_u\n            )\n            + (-2.0 * boundary_r * dy_bondi_j)\n            * (\n                -2.0\n                * boundary_r\n                * (\n                    np.conj(eth_dy_bondi_u)\n                    + np.conj(eth_r_divided_by_r * dy_bondi_u)\n                    + np.conj(dy_du_bondi_j)\n                )\n            )\n            - 4.0 * boundary_r**2 * dy_dy_bondi_w\n        )\n    )\n\n\ndef psi_1(\n    dy_dy_bondi_beta,\n    eth_dy_dy_bondi_beta,\n    dy_bondi_j,\n    dy_bondi_q,\n    dy_dy_bondi_q,\n    boundary_r,\n    eth_r_divided_by_r,\n):\n    return 0.125 * (\n        -12.0\n        * (2.0 * boundary_r**2)\n        * (eth_dy_dy_bondi_beta + 2.0 * eth_r_divided_by_r * dy_dy_bondi_beta)\n        + (-2.0 * boundary_r * dy_bondi_j)\n        * (-2.0 * boundary_r * np.conj(dy_bondi_q))\n        + 2.0 * (2.0 * boundary_r**2 * dy_dy_bondi_q)\n    )\n\n\ndef psi_0(dy_bondi_j, dy_dy_dy_bondi_j, boundary_r):\n    return 1.5 * (\n        0.25\n        * (-2.0 * boundary_r * np.conj(dy_bondi_j))\n        * (-2.0 * boundary_r * dy_bondi_j) ** 2\n        + 4.0 / 3.0 * boundary_r**3 * dy_dy_dy_bondi_j\n    )\n\n\ndef strain(dy_bondi_j, eth_eth_retarded_time, boundary_r):\n    return -2.0 * np.conj(boundary_r * dy_bondi_j) + np.conj(\n        eth_eth_retarded_time\n    )\n\n\ndef klein_gordon_psi(dy_kg_psi, boundary_r):\n    return -2.0 * boundary_r * dy_kg_psi\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_AnalyticBoundaryDataManager.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticBoundaryDataManager.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace {\nstd::vector<double> output_news_data;\nstd::string data_set_name;\nstruct MockWriteReductionDataRow {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& /*file_legend*/,\n                    const std::tuple<std::vector<double>>& data_row) {\n    data_set_name = subfile_name;\n    output_news_data = std::get<0>(data_row);\n  }\n};\n\nstruct TestCallWriteNews {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl2::flat_any_v<std::is_same_v<\n                Tags::AnalyticBoundaryDataManager, DbTags>...>> = nullptr>\n  static void apply(const db::DataBox<tmpl::list<DbTags...>>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    db::get<Tags::AnalyticBoundaryDataManager>(box)\n        .template write_news<ParallelComponent>(\n            cache, db::get<::Tags::TimeStepId>(box).substep_time());\n  }\n};\n\ntemplate <typename Metavariables>\nstruct mock_observer_writer {\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_threaded_actions =\n      tmpl::list<observers::ThreadedActions::WriteReductionDataRow>;\n  using with_these_threaded_actions = tmpl::list<MockWriteReductionDataRow>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tags = tmpl::list<>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_boundary {\n  using simple_tags =\n      tmpl::list<Tags::AnalyticBoundaryDataManager, ::Tags::TimeStepId>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = size_t;\n  using const_global_cache_tags = tmpl::list<Tags::ObservationLMax>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, db::AddComputeTags<>>>>>;\n};\n\nstruct metavariables {\n  using component_list = tmpl::list<mock_observer_writer<metavariables>,\n                                    mock_boundary<metavariables>>;\n  using observed_reduction_data_tags = tmpl::list<>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.AnalyticBoundaryDataManager\",\n                  \"[Unit][Cce]\") {\n  register_classes_with_charm<Cce::Solutions::LinearizedBondiSachs>();\n  // set up the analytic data parameters\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(gen);\n  UniformCustomDistribution<double> parameter_dist{0.1, 1.0};\n  const double extraction_radius = 100.0 * parameter_dist(gen);\n  const double frequency = parameter_dist(gen);\n\n  const double time = 2.0;\n\n  Solutions::LinearizedBondiSachs analytic_solution{\n      {0.01 * parameter_dist(gen), 0.01 * parameter_dist(gen)},\n      extraction_radius,\n      frequency};\n  AnalyticBoundaryDataManager analytic_manager{l_max, extraction_radius,\n                                               analytic_solution.get_clone()};\n  // test the boundary computation\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      boundary_variables_from_manager{number_of_angular_points};\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      expected_boundary_variables{number_of_angular_points};\n\n  analytic_manager.populate_hypersurface_boundary_data(\n      make_not_null(&boundary_variables_from_manager), time);\n  const auto analytic_solution_gh_variables = analytic_solution.variables(\n      l_max, time,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>{});\n  create_bondi_boundary_data(\n      make_not_null(&expected_boundary_variables),\n      get<gh::Tags::Phi<DataVector, 3>>(analytic_solution_gh_variables),\n      get<gh::Tags::Pi<DataVector, 3>>(analytic_solution_gh_variables),\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(\n          analytic_solution_gh_variables),\n      extraction_radius, l_max);\n  tmpl::for_each<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      [&boundary_variables_from_manager,\n       &expected_boundary_variables](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        CHECK_ITERABLE_APPROX(get<tag>(boundary_variables_from_manager),\n                              get<tag>(expected_boundary_variables));\n      });\n\n  // test writing news\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{l_max}};\n  runner.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<mock_observer_writer<metavariables>>(&runner,\n                                                                        0_st);\n  ActionTesting::emplace_component_and_initialize<mock_boundary<metavariables>>(\n      &runner, 0_st,\n      tuples::TaggedTuple<Cce::Tags::AnalyticBoundaryDataManager,\n                          ::Tags::TimeStepId>{\n          std::move(analytic_manager),\n          TimeStepId{true, 0_st, {{2.0, 3.0}, {0_st, 1_st}}}});\n  ActionTesting::simple_action<mock_boundary<metavariables>, TestCallWriteNews>(\n      make_not_null(&runner), 0_st);\n  ActionTesting::invoke_queued_threaded_action<\n      mock_observer_writer<metavariables>>(make_not_null(&runner), 0_st);\n\n  const auto expected_news = get<Tags::News>(\n      analytic_solution.variables(l_max, time, tmpl::list<Tags::News>{}));\n  SpinWeighted<ComplexModalVector, -2> output_news_goldberg_modes{\n      square(l_max + 1)};\n  for (size_t i = 0; i < square(l_max + 1); ++i) {\n    output_news_goldberg_modes.data()[i] = std::complex<double>(\n        output_news_data[2 * i + 1], output_news_data[2 * i + 2]);\n  }\n  SpinWeighted<ComplexModalVector, -2> output_news_libsharp_modes{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n  Spectral::Swsh::goldberg_to_libsharp_modes(\n      make_not_null(&output_news_libsharp_modes), output_news_goldberg_modes,\n      l_max);\n  const auto output_news = Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, output_news_libsharp_modes);\n  CHECK_ITERABLE_APPROX(output_news.data(), get(expected_news).data());\n  CHECK(approx(output_news_data[0]) == time);\n  CHECK(data_set_name == \"/Cce/News_expected\");\n}\n}  // namespace\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_BoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\nnamespace {\n\nvoid pypp_test_worldtube_computation_steps() {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"Evolution/Systems/Cce/\"};\n\n  const size_t num_pts = 5;\n\n  pypp::check_with_random_values<1>(\n      &cartesian_to_spherical_coordinates_and_jacobians, \"BoundaryData\",\n      {\"cartesian_to_angular_coordinates\", \"cartesian_to_angular_jacobian\",\n       \"cartesian_to_angular_inverse_jacobian\"},\n      {{{1.0, 5.0}}}, DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(&null_metric_and_derivative, \"BoundaryData\",\n                                    {\"du_null_metric\", \"null_metric\"},\n                                    {{{1.0, 5.0}}}, DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(&worldtube_normal_and_derivatives,\n                                    \"BoundaryData\",\n                                    {\"worldtube_normal\", \"dt_worldtube_normal\"},\n                                    {{{1.0, 5.0}}}, DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(&null_vector_l_and_derivatives,\n                                    \"BoundaryData\",\n                                    {\"du_null_vector_l\", \"null_vector_l\"},\n                                    {{{1.0, 5.0}}}, DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(\n      &dlambda_null_metric_and_inverse, \"BoundaryData\",\n      {\"dlambda_null_metric\", \"inverse_dlambda_null_metric\"}, {{{1.0, 5.0}}},\n      DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(&beta_worldtube_data, \"BoundaryData\",\n                                    {\"bondi_beta_worldtube_data\"},\n                                    {{{1.0, 5.0}}}, DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(&bondi_u_worldtube_data, \"BoundaryData\",\n                                    {\"bondi_u_worldtube_data\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts}, 1.0e-11);\n\n  pypp::check_with_random_values<1>(&bondi_w_worldtube_data, \"BoundaryData\",\n                                    {\"bondi_w_worldtube_data\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts}, 1.0e-11);\n\n  pypp::check_with_random_values<1>(&bondi_j_worldtube_data, \"BoundaryData\",\n                                    {\"bondi_j_worldtube_data\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(\n      &dr_bondi_j, \"BoundaryData\",\n      {\"dr_bondi_j_worldtube_data\", \"dr_bondi_j_denominator\"}, {{{1.0, 5.0}}},\n      DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(&d2lambda_bondi_r, \"BoundaryData\",\n                                    {\"d2lambda_bondi_r\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(\n      &bondi_q_worldtube_data, \"BoundaryData\",\n      {\"bondi_q_worldtube_data\", \"dr_bondi_u_worldtube_data\"}, {{{1.0, 5.0}}},\n      DataVector{num_pts}, 1.0e-10);\n\n  pypp::check_with_random_values<1>(&bondi_h_worldtube_data, \"BoundaryData\",\n                                    {\"bondi_h_worldtube_data\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n\n  pypp::check_with_random_values<1>(&du_j_worldtube_data, \"BoundaryData\",\n                                    {\"du_j_worldtube_data\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(\n      &klein_gordon_psi_worldtube_data, \"BoundaryData\",\n      {\"klein_gordon_psi_worldtube_data\"}, {{{1.0, 5.0}}}, DataVector{num_pts});\n  pypp::check_with_random_values<1>(\n      &klein_gordon_pi_worldtube_data, \"BoundaryData\",\n      {\"klein_gordon_pi_worldtube_data\"}, {{{1.0, 5.0}}}, DataVector{num_pts});\n}\n\ntemplate <typename Generator>\nvoid test_trigonometric_function_identities(\n    const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<size_t> l_dist(3, 6);\n  const size_t l_max = l_dist(*gen);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  Scalar<DataVector> cos_phi{number_of_angular_points};\n  Scalar<DataVector> cos_theta{number_of_angular_points};\n  Scalar<DataVector> sin_phi{number_of_angular_points};\n  Scalar<DataVector> sin_theta{number_of_angular_points};\n  trigonometric_functions_on_swsh_collocation(\n      make_not_null(&cos_phi), make_not_null(&cos_theta),\n      make_not_null(&sin_phi), make_not_null(&sin_theta), l_max);\n  const DataVector unity{number_of_angular_points, 1.0};\n  {\n    INFO(\"Trigonometric identities\");\n    CHECK_ITERABLE_APPROX(square(get(cos_phi)) + square(get(sin_phi)), unity);\n    CHECK_ITERABLE_APPROX(square(get(cos_theta)) + square(get(sin_theta)),\n                          unity);\n  }\n\n  tnsr::I<DataVector, 3> cartesian_coords{number_of_angular_points};\n  SphericaliCartesianJ cartesian_to_angular_jacobian{number_of_angular_points};\n  CartesianiSphericalJ inverse_cartesian_to_angular_jacobian{\n      number_of_angular_points};\n  UniformCustomDistribution<double> radius_dist{10.0, 100.0};\n  const double extraction_radius = radius_dist(*gen);\n  cartesian_to_spherical_coordinates_and_jacobians(\n      make_not_null(&cartesian_coords),\n      make_not_null(&cartesian_to_angular_jacobian),\n      make_not_null(&inverse_cartesian_to_angular_jacobian), cos_phi, cos_theta,\n      sin_phi, sin_theta, extraction_radius);\n  DataVector jacobian_identity{number_of_angular_points};\n  const DataVector zero{number_of_angular_points, 0.0};\n  {\n    INFO(\"Jacobian identity\");\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        jacobian_identity = cartesian_to_angular_jacobian.get(i, 0) *\n                            inverse_cartesian_to_angular_jacobian.get(0, j);\n        for (size_t k = 1; k < 3; ++k) {\n          jacobian_identity += cartesian_to_angular_jacobian.get(i, k) *\n                               inverse_cartesian_to_angular_jacobian.get(k, j);\n        }\n        if (i == j) {\n          CHECK_ITERABLE_APPROX(jacobian_identity, unity);\n        } else {\n          CHECK_ITERABLE_APPROX(jacobian_identity, zero);\n        }\n      }\n    }\n  }\n}\n\ntemplate <typename Generator>\nvoid test_bondi_r(const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<size_t> l_dist(3, 6);\n  const size_t l_max = l_dist(*gen);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  tnsr::iaa<DataVector, 3> expected_phi{number_of_angular_points};\n  fill_with_random_values(make_not_null(&expected_phi), gen,\n                          make_not_null(&value_dist));\n  tnsr::aa<DataVector, 3> expected_psi{number_of_angular_points};\n  fill_with_random_values(make_not_null(&expected_psi), gen,\n                          make_not_null(&value_dist));\n  get<0, 0>(expected_psi) -= 1.0;\n  for (size_t a = 1; a < 4; ++a) {\n    expected_psi.get(a, a) += 1.0;\n  }\n  tnsr::aa<DataVector, 3> expected_dt_psi{number_of_angular_points};\n  fill_with_random_values(make_not_null(&expected_dt_psi), gen,\n                          make_not_null(&value_dist));\n\n  // test bondi_r now that we have an appropriate angular metric\n  tnsr::ii<DataVector, 2> angular_psi{number_of_angular_points};\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = A; B < 2; ++B) {\n      angular_psi.get(A, B) = expected_psi.get(A + 2, B + 2);\n    }\n  }\n  tnsr::aa<DataVector, 3, Frame::RadialNull> expected_psi_null_coords{\n      number_of_angular_points};\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      expected_psi_null_coords.get(a, b) = expected_psi.get(a, b);\n    }\n  }\n  Scalar<SpinWeighted<ComplexDataVector, 0>> local_bondi_r{\n      number_of_angular_points};\n  bondi_r(make_not_null(&local_bondi_r), expected_psi_null_coords);\n  Scalar<SpinWeighted<ComplexDataVector, 0>> expected_bondi_r{\n      number_of_angular_points};\n  get(expected_bondi_r).data() =\n      std::complex<double>(1.0, 0.0) *\n      pow(get(determinant_and_inverse(angular_psi).first), 0.25);\n  CHECK_ITERABLE_APPROX(get(local_bondi_r).data(),\n                        get(expected_bondi_r).data());\n}\n\ntemplate <typename Generator>\nvoid test_d_bondi_r_identities(const gsl::not_null<Generator*> gen) {\n  // more resolution needed because we want high precision on the angular\n  // derivative check.\n  UniformCustomDistribution<size_t> l_dist(8, 12);\n  // distribution chosen to be not too far from the scale of the diagnonal\n  // elements in the matrix\n  UniformCustomDistribution<double> value_dist{0.1, 0.2};\n  const size_t l_max = l_dist(*gen);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  Scalar<SpinWeighted<ComplexDataVector, 0>> bondi_r{number_of_angular_points};\n  // simple test function so that we can easily work out what the expected\n  // angular derivatives should be.\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get(bondi_r).data()[collocation_point.offset] =\n        5.0 + sin(collocation_point.theta) * cos(collocation_point.phi);\n  }\n  tnsr::aa<DataVector, 3, Frame::RadialNull> null_metric{\n      number_of_angular_points};\n  fill_with_random_values(make_not_null(&null_metric), gen,\n                          make_not_null(&value_dist));\n  // to make the inverse more well-behaved.\n  for (size_t a = 0; a < 4; ++a) {\n    null_metric.get(a, a) += 1.0;\n  }\n  const auto inverse_null_metric = determinant_and_inverse(null_metric).second;\n  tnsr::aa<DataVector, 3, Frame::RadialNull> dlambda_null_metric{\n      number_of_angular_points};\n  fill_with_random_values(make_not_null(&dlambda_null_metric), gen,\n                          make_not_null(&value_dist));\n  tnsr::aa<DataVector, 3, Frame::RadialNull> du_null_metric{\n      number_of_angular_points};\n  fill_with_random_values(make_not_null(&du_null_metric), gen,\n                          make_not_null(&value_dist));\n  // initialize the du_null_metric in a way that allows a trace identity to be\n  // used to check the calculation\n  tnsr::ii<DataVector, 2> angular_inverse_null_metric{number_of_angular_points};\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      angular_inverse_null_metric.get(A, B) =\n          inverse_null_metric.get(A + 2, B + 2);\n    }\n  }\n  const tnsr::II<DataVector, 2> trace_identity_angular_null_metric =\n      determinant_and_inverse(angular_inverse_null_metric).second;\n  double random_scaling = value_dist(*gen);\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      du_null_metric.get(A + 2, B + 2) =\n          random_scaling * trace_identity_angular_null_metric.get(A, B);\n    }\n  }\n  tnsr::a<DataVector, 3, Frame::RadialNull> d_bondi_r{number_of_angular_points};\n  Cce::d_bondi_r(make_not_null(&d_bondi_r), bondi_r, dlambda_null_metric,\n                 du_null_metric, inverse_null_metric, l_max);\n  DataVector expected_dtheta_r{number_of_angular_points};\n  DataVector expected_dphi_r{number_of_angular_points};\n  for (const auto collocation_point : collocation) {\n    expected_dtheta_r[collocation_point.offset] =\n        cos(collocation_point.theta) * cos(collocation_point.phi);\n    // note 'pfaffian' derivative with the 1/sin(theta)\n    expected_dphi_r[collocation_point.offset] = -sin(collocation_point.phi);\n  }\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(get<2>(d_bondi_r), expected_dtheta_r,\n                               angular_derivative_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get<3>(d_bondi_r), expected_dphi_r,\n                               angular_derivative_approx);\n  // a slightly different calculational path to improve test robustness\n  DataVector expected_dlambda_r{number_of_angular_points, 0.0};\n  for (size_t A = 0; A < 2; ++A) {\n    for (size_t B = 0; B < 2; ++B) {\n      expected_dlambda_r += inverse_null_metric.get(A + 2, B + 2) *\n                            dlambda_null_metric.get(A + 2, B + 2);\n    }\n  }\n  Approx trace_product_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n\n  expected_dlambda_r *= 0.25 * real(get(bondi_r).data());\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_dlambda_r, get<1>(d_bondi_r),\n                               trace_product_approx);\n\n  // use the trace identity to evaluate the du_r in the contrived case where the\n  // du_null metric is proportional to null_metric.\n  DataVector expected_du_r{number_of_angular_points, random_scaling * 2.0};\n  expected_du_r *= 0.25 * real(get(bondi_r).data());\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_du_r, get<0>(d_bondi_r),\n                               trace_product_approx);\n}\n\ntemplate <typename Generator>\nvoid test_dyad_identities(const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<size_t> l_dist(3, 6);\n  const size_t l_max = l_dist(*gen);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  tnsr::I<ComplexDataVector, 2, Frame::RadialNull> up_dyad{\n      number_of_angular_points};\n  tnsr::i<ComplexDataVector, 2, Frame::RadialNull> down_dyad{\n      number_of_angular_points};\n  Cce::dyads(make_not_null(&down_dyad), make_not_null(&up_dyad));\n  const ComplexDataVector two{number_of_angular_points,\n                              std::complex<double>(2.0, 0.0)};\n  const ComplexDataVector zero{number_of_angular_points,\n                               std::complex<double>(0.0, 0.0)};\n\n  ComplexDataVector dyad_product{number_of_angular_points, 0.0};\n  for (size_t A = 0; A < 2; ++A) {\n    dyad_product += up_dyad.get(A) * down_dyad.get(A);\n  }\n  CHECK_ITERABLE_APPROX(dyad_product, zero);\n  dyad_product = 0.0;\n  for (size_t A = 0; A < 2; ++A) {\n    dyad_product += up_dyad.get(A) * conj(down_dyad.get(A));\n  }\n  CHECK_ITERABLE_APPROX(dyad_product, two);\n}\n\ntemplate <typename TagList, typename AnalyticSolution>\nvoid dispatch_to_nodal_worldtube_computation_from_analytic(\n    const gsl::not_null<Variables<TagList>*> variables,\n    const AnalyticSolution& solution, const double extraction_radius,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  // create the vector of collocation points that we want to interpolate to\n  tnsr::I<DataVector, 3> collocation_points{number_of_angular_points};\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get<0>(collocation_points)[collocation_point.offset] =\n        extraction_radius * sin(collocation_point.theta) *\n        cos(collocation_point.phi);\n    get<1>(collocation_points)[collocation_point.offset] =\n        extraction_radius * sin(collocation_point.theta) *\n        sin(collocation_point.phi);\n    get<2>(collocation_points)[collocation_point.offset] =\n        extraction_radius * cos(collocation_point.theta);\n  }\n\n  const auto kerr_schild_variables = solution.variables(\n      collocation_points, 0.0, gr::Solutions::KerrSchild::tags<DataVector>{});\n\n  // direct collocation quantities for processing into the GH form of the\n  // worldtube function\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(kerr_schild_variables);\n  const auto& dt_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(kerr_schild_variables);\n  const auto& d_lapse = get<gr::Solutions::KerrSchild::DerivLapse<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(kerr_schild_variables);\n  const auto& d_shift = get<gr::Solutions::KerrSchild::DerivShift<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n          kerr_schild_variables);\n  const auto& d_spatial_metric =\n      get<gr::Solutions::KerrSchild::DerivSpatialMetric<DataVector>>(\n          kerr_schild_variables);\n\n  Scalar<DataVector> dr_lapse{number_of_angular_points};\n  get(dr_lapse) = (get<0>(collocation_points) * get<0>(d_lapse) +\n                   get<1>(collocation_points) * get<1>(d_lapse) +\n                   get<2>(collocation_points) * get<2>(d_lapse)) /\n                  extraction_radius;\n  tnsr::I<DataVector, 3> dr_shift{number_of_angular_points};\n  for (size_t i = 0; i < 3; ++i) {\n    dr_shift.get(i) = (get<0>(collocation_points) * d_shift.get(0, i) +\n                       get<1>(collocation_points) * d_shift.get(1, i) +\n                       get<2>(collocation_points) * d_shift.get(2, i)) /\n                      extraction_radius;\n  }\n  tnsr::ii<DataVector, 3> dr_spatial_metric{number_of_angular_points};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      dr_spatial_metric.get(i, j) =\n          (get<0>(collocation_points) * d_spatial_metric.get(0, i, j) +\n           get<1>(collocation_points) * d_spatial_metric.get(1, i, j) +\n           get<2>(collocation_points) * d_spatial_metric.get(2, i, j)) /\n          extraction_radius;\n    }\n  }\n\n  create_bondi_boundary_data(\n      variables, spatial_metric, dt_spatial_metric, dr_spatial_metric, shift,\n      dt_shift, dr_shift, lapse, dt_lapse, dr_lapse, extraction_radius, l_max);\n}\n\ntemplate <typename TagList, typename AnalyticSolution>\nvoid dispatch_to_gh_worldtube_computation_from_analytic(\n    const gsl::not_null<Variables<TagList>*> variables,\n    const AnalyticSolution& solution, const double extraction_radius,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  // create the vector of collocation points that we want to interpolate to\n  tnsr::I<DataVector, 3> collocation_points{number_of_angular_points};\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get<0>(collocation_points)[collocation_point.offset] =\n        extraction_radius * sin(collocation_point.theta) *\n        cos(collocation_point.phi);\n    get<1>(collocation_points)[collocation_point.offset] =\n        extraction_radius * sin(collocation_point.theta) *\n        sin(collocation_point.phi);\n    get<2>(collocation_points)[collocation_point.offset] =\n        extraction_radius * cos(collocation_point.theta);\n  }\n\n  const auto kerr_schild_variables = solution.variables(\n      collocation_points, 0.0, gr::Solutions::KerrSchild::tags<DataVector>{});\n\n  // direct collocation quantities for processing into the GH form of the\n  // worldtube function\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(kerr_schild_variables);\n  const auto& dt_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(kerr_schild_variables);\n  const auto& d_lapse = get<gr::Solutions::KerrSchild::DerivLapse<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(kerr_schild_variables);\n  const auto& d_shift = get<gr::Solutions::KerrSchild::DerivShift<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n          kerr_schild_variables);\n  const auto& d_spatial_metric =\n      get<gr::Solutions::KerrSchild::DerivSpatialMetric<DataVector>>(\n          kerr_schild_variables);\n\n  const auto phi =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  const auto psi = gr::spacetime_metric(lapse, shift, spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n                         dt_spatial_metric, phi);\n\n  create_bondi_boundary_data(variables, phi, pi, psi, extraction_radius, l_max);\n}\n\ntemplate <typename TagList, typename AnalyticSolution>\nvoid dispatch_to_modal_worldtube_computation_from_analytic(\n    const gsl::not_null<Variables<TagList>*> variables,\n    const AnalyticSolution& solution, const double extraction_radius,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  // create the vector of collocation points that we want to interpolate to\n  tnsr::I<DataVector, 3> collocation_coordinates{number_of_angular_points};\n  for (const auto collocation_point :\n       Spectral::Swsh::cached_collocation_metadata<\n           Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max)) {\n    get<0>(collocation_coordinates)[collocation_point.offset] =\n        extraction_radius * sin(collocation_point.theta) *\n        cos(collocation_point.phi);\n    get<1>(collocation_coordinates)[collocation_point.offset] =\n        extraction_radius * sin(collocation_point.theta) *\n        sin(collocation_point.phi);\n    get<2>(collocation_coordinates)[collocation_point.offset] =\n        extraction_radius * cos(collocation_point.theta);\n  }\n  const auto kerr_schild_variables =\n      solution.variables(collocation_coordinates, 0.0,\n                         gr::Solutions::KerrSchild::tags<DataVector>{});\n\n  // direct collocation quantities for processing into the GH form of the\n  // worldtube function\n  const Scalar<DataVector>& lapse =\n      get<gr::Tags::Lapse<DataVector>>(kerr_schild_variables);\n  const Scalar<DataVector>& dt_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(kerr_schild_variables);\n  const auto& d_lapse = get<gr::Solutions::KerrSchild::DerivLapse<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(kerr_schild_variables);\n  const auto& d_shift = get<gr::Solutions::KerrSchild::DerivShift<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n          kerr_schild_variables);\n  const auto& d_spatial_metric =\n      get<gr::Solutions::KerrSchild::DerivSpatialMetric<DataVector>>(\n          kerr_schild_variables);\n\n  Scalar<DataVector> dr_lapse{number_of_angular_points};\n  get(dr_lapse) = (get<0>(collocation_coordinates) * get<0>(d_lapse) +\n                   get<1>(collocation_coordinates) * get<1>(d_lapse) +\n                   get<2>(collocation_coordinates) * get<2>(d_lapse)) /\n                  extraction_radius;\n  tnsr::I<DataVector, 3> dr_shift{number_of_angular_points};\n  for (size_t i = 0; i < 3; ++i) {\n    dr_shift.get(i) = (get<0>(collocation_coordinates) * d_shift.get(0, i) +\n                       get<1>(collocation_coordinates) * d_shift.get(1, i) +\n                       get<2>(collocation_coordinates) * d_shift.get(2, i)) /\n                      extraction_radius;\n  }\n  tnsr::ii<DataVector, 3> dr_spatial_metric{number_of_angular_points};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      dr_spatial_metric.get(i, j) =\n          (get<0>(collocation_coordinates) * d_spatial_metric.get(0, i, j) +\n           get<1>(collocation_coordinates) * d_spatial_metric.get(1, i, j) +\n           get<2>(collocation_coordinates) * d_spatial_metric.get(2, i, j)) /\n          extraction_radius;\n    }\n  }\n\n  const auto lapse_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(lapse, l_max);\n  const auto dt_lapse_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(dt_lapse, l_max);\n  const auto dr_lapse_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(dr_lapse, l_max);\n\n  const auto shift_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(shift, l_max);\n  const auto dt_shift_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(dt_shift, l_max);\n  const auto dr_shift_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(dr_shift, l_max);\n\n  const auto spatial_metric_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(spatial_metric, l_max);\n  const auto dt_spatial_metric_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(dt_spatial_metric, l_max);\n  const auto dr_spatial_metric_coefficients =\n      TestHelpers::tensor_to_libsharp_coefficients(dr_spatial_metric, l_max);\n\n  create_bondi_boundary_data(\n      variables, spatial_metric_coefficients, dt_spatial_metric_coefficients,\n      dr_spatial_metric_coefficients, shift_coefficients, dt_shift_coefficients,\n      dr_shift_coefficients, lapse_coefficients, dt_lapse_coefficients,\n      dr_lapse_coefficients, extraction_radius, l_max);\n}\n\n// this tests that the method using modal construction of metric components\n// and derivatives gives the same answer as the version that just takes the GH\n// quantities.\ntemplate <typename Generator>\nvoid test_kerr_schild_boundary_consistency(\n    const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0 * value_dist(*gen);\n\n  UniformCustomDistribution<size_t> l_dist(12, 18);\n  const size_t l_max = l_dist(*gen);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      nodal_boundary_variables{number_of_angular_points};\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      gh_boundary_variables{number_of_angular_points};\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      modal_boundary_variables{number_of_angular_points};\n\n  dispatch_to_nodal_worldtube_computation_from_analytic(\n      make_not_null(&nodal_boundary_variables), solution, extraction_radius,\n      l_max);\n  dispatch_to_gh_worldtube_computation_from_analytic(\n      make_not_null(&gh_boundary_variables), solution, extraction_radius,\n      l_max);\n  dispatch_to_modal_worldtube_computation_from_analytic(\n      make_not_null(&modal_boundary_variables), solution, extraction_radius,\n      l_max);\n\n  // This can be tightened further with higher l_max above. Q, for instance, has\n  // aliasing trouble\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n\n  tmpl::for_each<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      [&](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& test_nodal = get<tag>(modal_boundary_variables);\n        const auto& test_gh = get<tag>(gh_boundary_variables);\n        const auto& test_modal = get<tag>(modal_boundary_variables);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_nodal, test_gh,\n                                     angular_derivative_approx);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_nodal, test_modal,\n                                     angular_derivative_approx);\n      });\n}\n\n// this tests that both execution pathways in Schwarzschild produce the expected\n// Bondi-like scalar quantities.\ntemplate <typename Generator>\nvoid test_schwarzschild_solution(const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0 * value_dist(*gen);\n\n  UniformCustomDistribution<size_t> l_dist(10, 14);\n  const size_t l_max = l_dist(*gen);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      nodal_boundary_variables{number_of_angular_points};\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      gh_boundary_variables{number_of_angular_points};\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      modal_boundary_variables{number_of_angular_points};\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      expected_variables{number_of_angular_points, 0.0};\n\n  dispatch_to_nodal_worldtube_computation_from_analytic(\n      make_not_null(&nodal_boundary_variables), solution, extraction_radius,\n      l_max);\n  dispatch_to_gh_worldtube_computation_from_analytic(\n      make_not_null(&gh_boundary_variables), solution, extraction_radius,\n      l_max);\n  dispatch_to_modal_worldtube_computation_from_analytic(\n      make_not_null(&modal_boundary_variables), solution, extraction_radius,\n      l_max);\n\n  get(get<Tags::BoundaryValue<Tags::BondiR>>(expected_variables)).data() =\n      extraction_radius;\n  get(get<Tags::BoundaryValue<Tags::BondiW>>(expected_variables)).data() =\n      -2.0 * mass / pow<2>(extraction_radius);\n\n  // This can be tightened further with higher l_max above.\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  tmpl::for_each<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      [&](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& expected = get<tag>(expected_variables);\n        const auto& test_nodal = get<tag>(nodal_boundary_variables);\n        const auto& test_gh = get<tag>(gh_boundary_variables);\n        const auto& test_modal = get<tag>(modal_boundary_variables);\n\n        CHECK_ITERABLE_CUSTOM_APPROX(expected, test_nodal,\n                                     angular_derivative_approx);\n        CHECK_ITERABLE_CUSTOM_APPROX(expected, test_gh,\n                                     angular_derivative_approx);\n        CHECK_ITERABLE_CUSTOM_APPROX(expected, test_modal,\n                                     angular_derivative_approx);\n      });\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.BoundaryData\", \"[Unit][Cce]\") {\n  pypp_test_worldtube_computation_steps();\n\n  MAKE_GENERATOR(gen);\n  test_trigonometric_function_identities(make_not_null(&gen));\n  test_bondi_r(make_not_null(&gen));\n  test_d_bondi_r_identities(make_not_null(&gen));\n  test_dyad_identities(make_not_null(&gen));\n  test_kerr_schild_boundary_consistency(make_not_null(&gen));\n  test_schwarzschild_solution(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_BoundaryDataTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/Cce/BoundaryDataTags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.BoundaryDataTags\",\n                  \"[Unit][Cce]\") {\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::CosPhi>(\"CosPhi\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::CosTheta>(\"CosTheta\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::SinPhi>(\"SinPhi\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::SinTheta>(\"SinTheta\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::CartesianCoordinates>(\n      \"CartesianCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      Cce::Tags::detail::CartesianToSphericalJacobian>(\n      \"CartesianToSphericalJacobian\");\n  TestHelpers::db::test_simple_tag<\n      Cce::Tags::detail::InverseCartesianToSphericalJacobian>(\n      \"InverseCartesianToSphericalJacobian\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::WorldtubeNormal>(\n      \"WorldtubeNormal\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::UpDyad>(\"UpDyad\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::DownDyad>(\"DownDyad\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::RealBondiR>(\"RealBondiR\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::AngularDNullL>(\n      \"AngularDNullL\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::detail::NullL>(\"NullL\");\n  TestHelpers::db::test_simple_tag<\n      Cce::Tags::detail::DLambda<Cce::Tags::detail::RealBondiR>>(\"DLambda\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_DumpBondiSachsOnWorldtube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <iomanip>\n#include <limits>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Callbacks/DumpBondiSachsOnWorldtube.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeModeRecorder.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<>>,\n                 observers::Actions::InitializeWriter<Metavariables>>>>;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n};\n\ntemplate <bool IncludeKleinGordon>\nstruct test_metavariables {\n  struct Target : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target = tmpl::append<\n        tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                   gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>,\n        tmpl::conditional_t<\n            IncludeKleinGordon,\n            tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                       CurvedScalarWave::Tags::Phi<3>,\n                       gr::Tags::Lapse<DataVector>,\n                       gr::Tags::Shift<DataVector, 3>>,\n            tmpl::list<>>>;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<Target, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::DumpBondiSachsOnWorldtube<\n            Target, IncludeKleinGordon>>;\n    using compute_items_on_target = tmpl::list<>;\n  };\n\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  void pup(PUP::er& /*p*/) {}\n\n  using const_global_cache_tags =\n      tmpl::list<intrp::Tags::Sphere<Target>, Cce::Tags::FilePrefix>;\n  using component_list =\n      tmpl::list<MockObserverWriter<test_metavariables<IncludeKleinGordon>>>;\n};\n\nstd::string get_filename(const std::string& filename_prefix,\n                         const double radius) {\n  return MakeString{} << filename_prefix << \"CceR\" << std::setfill('0')\n                      << std::setw(4) << std::lround(radius) << \".h5\";\n}\n\ntemplate <typename Tags, bool IncludeKleinGordon>\nauto make_spacetime_variables(const size_t size) {\n  static constexpr bool include_klein_gordon = IncludeKleinGordon;\n  Variables<Tags> spacetime_variables{size, 0.0};\n  auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(spacetime_variables);\n  get<0, 0>(spacetime_metric) = -1.0;\n  for (size_t i = 1; i < 4; i++) {\n    spacetime_metric.get(i, i) = 1.0;\n  }\n  if constexpr (include_klein_gordon) {\n    auto& csw_psi = get<CurvedScalarWave::Tags::Psi>(spacetime_variables);\n    auto& lapse = get<gr::Tags::Lapse<DataVector>>(spacetime_variables);\n    get(csw_psi) = 1.0;\n    get(lapse) = 1.0;\n  }\n  return spacetime_variables;\n}\n\n// True when Klein Gordon scalar variables are also dumped on the worldtube\ntemplate <bool IncludeKleinGordon>\nvoid test_impl(const std::string& filename_prefix,\n               const std::vector<double>& radii) {\n  static constexpr bool include_klein_gordon = IncludeKleinGordon;\n  CAPTURE(include_klein_gordon);\n  using metavars = test_metavariables<include_klein_gordon>;\n  using target = typename metavars::Target;\n  using writer = MockObserverWriter<metavars>;\n  using spacetime_tags = typename target::vars_to_interpolate_to_target;\n  using callback = tmpl::front<typename target::post_interpolation_callbacks>;\n  using cce_tags = typename callback::cce_boundary_tags;\n\n  // Choose only l_max = 2 for two reasons:\n  //   1. Speed\n  //   2. So we can easily check the legend by hand\n  const size_t l_max = 2;\n  const size_t num_points_single_sphere =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const std::vector<std::string> expected_all_legend{\n      \"Time\",     \"Re(0,0)\",  \"Im(0,0)\",  \"Re(1,-1)\", \"Im(1,-1)\",\n      \"Re(1,0)\",  \"Im(1,0)\",  \"Re(1,1)\",  \"Im(1,1)\",  \"Re(2,-2)\",\n      \"Im(2,-2)\", \"Re(2,-1)\", \"Im(2,-1)\", \"Re(2,0)\",  \"Im(2,0)\",\n      \"Re(2,1)\",  \"Im(2,1)\",  \"Re(2,2)\",  \"Im(2,2)\"};\n  const std::vector<std::string> expected_real_legend{\n      \"Time\",    \"Re(0,0)\", \"Re(1,0)\", \"Re(1,1)\", \"Im(1,1)\",\n      \"Re(2,0)\", \"Re(2,1)\", \"Im(2,1)\", \"Re(2,2)\", \"Im(2,2)\"};\n\n  // It doesn't really matter what the GH data is so long as we are able to\n  // calculate bondi data, because this is just testing that we can write the\n  // data. So choose Minkowski spacetime. This means pi, phi, and deriv phi are\n  // trivially 0. If the Klein-Gordon variables are included, choose constant\n  // scalar (csw_pi and csw_phi are zero in this case).\n  Variables<spacetime_tags> spacetime_variables =\n      make_spacetime_variables<spacetime_tags, include_klein_gordon>(\n          radii.size() * num_points_single_sphere);\n\n  // Options for Sphere\n  const ylm::AngularOrdering angular_ordering = ylm::AngularOrdering::Cce;\n  const std::array<double, 3> center = {{0.05, 0.06, 0.07}};\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n\n  // Only need variables in the box for this test\n  using db_tags = tmpl::list<::Tags::Variables<spacetime_tags>>;\n  const auto box = db::create<db_tags>(spacetime_variables);\n\n  // Check the error\n  CHECK_THROWS_WITH(\n      ([&box, &radii, &center, &filename_prefix]() {\n        const ylm::AngularOrdering local_angular_ordering =\n            ylm::AngularOrdering::Strahlkorper;\n        intrp::OptionHolders::Sphere sphere_opts(l_max, center, radii,\n                                                 local_angular_ordering);\n        MockRuntimeSystem runner{{std::move(sphere_opts), filename_prefix}};\n\n        ActionTesting::emplace_nodegroup_component_and_initialize<writer>(\n            make_not_null(&runner), {});\n\n        Parallel::GlobalCache<metavars>& cache =\n            ActionTesting::cache<writer>(runner, 0);\n\n        callback::apply(box, cache, 0.1);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"To use the DumpBondiSachsOnWorldtube post interpolation callback, \"\n          \"the angular ordering of the Spheres must be Cce\"));\n\n  intrp::OptionHolders::Sphere sphere_opts(l_max, center, radii,\n                                           angular_ordering);\n  MockRuntimeSystem runner{{std::move(sphere_opts), filename_prefix}};\n\n  ActionTesting::emplace_nodegroup_component_and_initialize<writer>(\n      make_not_null(&runner), {});\n\n  Parallel::GlobalCache<metavars>& cache =\n      ActionTesting::cache<writer>(runner, 0);\n\n  const std::vector<double> times{0.9, 1.3};\n\n  for (const double time : times) {\n    callback::apply(box, cache, time);\n  }\n\n  const Variables<spacetime_tags> single_spacetime_variables =\n      make_spacetime_variables<spacetime_tags, include_klein_gordon>(\n          num_points_single_sphere);\n  Variables<cce_tags> bondi_boundary_data{num_points_single_sphere};\n\n  const auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(single_spacetime_variables);\n  const auto& pi = get<gh::Tags::Pi<DataVector, 3>>(single_spacetime_variables);\n  const auto& phi =\n      get<gh::Tags::Phi<DataVector, 3>>(single_spacetime_variables);\n\n  for (const auto& radius : radii) {\n    CAPTURE(radius);\n    // Have to create the bondi data for every radius individually\n    {\n      auto non_klein_gordon_data =\n          bondi_boundary_data.template reference_subset<\n              Cce::Tags::characteristic_worldtube_boundary_tags<\n                  Cce::Tags::BoundaryValue>>();\n      Cce::create_bondi_boundary_data(make_not_null(&non_klein_gordon_data),\n                                      phi, pi, spacetime_metric, radius, l_max);\n    }\n    if constexpr (include_klein_gordon) {\n      const auto& csw_psi =\n          get<CurvedScalarWave::Tags::Psi>(single_spacetime_variables);\n      const auto& csw_pi =\n          get<CurvedScalarWave::Tags::Pi>(single_spacetime_variables);\n      const auto& csw_phi =\n          get<CurvedScalarWave::Tags::Phi<3>>(single_spacetime_variables);\n      const auto& lapse =\n          get<gr::Tags::Lapse<DataVector>>(single_spacetime_variables);\n      const auto& shift =\n          get<gr::Tags::Shift<DataVector, 3>>(single_spacetime_variables);\n\n      Cce::create_klein_gordon_boundary_data(\n          make_not_null(&bondi_boundary_data), csw_phi, csw_pi, csw_psi, lapse,\n          shift);\n    }\n    const auto file = h5::H5File<h5::AccessType::ReadOnly>(\n        get_filename(filename_prefix, radius));\n\n    tmpl::for_each<Cce::Tags::worldtube_boundary_tags_for_writing<\n        Cce::Tags::BoundaryValue, include_klein_gordon>>(\n        [&file, &bondi_boundary_data, &times, &l_max, &expected_all_legend,\n         &expected_real_legend](auto tag_v) {\n          using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n          const auto& bondi_data = get(get<tag>(bondi_boundary_data));\n          constexpr int spin = tag::tag::type::type::spin;\n          constexpr bool is_real = spin == 0;\n          const auto& expected_legend =\n              is_real ? expected_real_legend : expected_all_legend;\n\n          SpinWeighted<ComplexModalVector, spin> expected_data{\n              square(l_max + 1)};\n          Spectral::Swsh::libsharp_to_goldberg_modes(\n              make_not_null(&expected_data),\n              Spectral::Swsh::swsh_transform(l_max, 1, bondi_data), l_max);\n\n          const std::string tag_path =\n              Cce::dataset_label_for_tag<typename tag::tag>();\n          CAPTURE(tag_path);\n          const auto& dat_file = file.get<h5::Dat>(tag_path);\n          const Matrix written_data = dat_file.get_data();\n\n          CHECK(expected_legend == dat_file.get_legend());\n          CHECK(times.size() == written_data.rows());\n          const size_t expected_data_size =\n              square(l_max + 1) * (is_real ? 1 : 2);\n          CHECK(expected_data_size == written_data.columns() - 1);\n\n          // Since the metric isn't changing it should be the same data on each\n          // row just with a different time\n          for (size_t j = 0; j < times.size(); j++) {\n            const double time = times[j];\n            CAPTURE(time);\n            CHECK(time == written_data(j, 0));\n            size_t counter = 1;\n            for (size_t ell = 0; ell <= l_max; ell++) {\n              for (size_t m = is_real ? 0 : -ell; m <= ell; m++) {\n                const size_t goldberg_index =\n                    Spectral::Swsh::goldberg_mode_index(l_max, ell,\n                                                        static_cast<int>(m));\n                CHECK(written_data(j, counter) ==\n                      real(expected_data.data()[goldberg_index]));\n                counter++;\n                if (not is_real or m != 0) {\n                  CHECK(written_data(j, counter) ==\n                        imag(expected_data.data()[goldberg_index]));\n                  counter++;\n                }\n              }\n            }\n          }\n\n          file.close_current_object();\n        });\n  }\n}\n\nvoid delete_files(const std::string& filename_prefix,\n                  const std::vector<double>& radii_to_delete) {\n  for (const auto& radius : radii_to_delete) {\n    const std::string filename = get_filename(filename_prefix, radius);\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n  }\n}\n\ntemplate <bool IncludeKleinGordon>\nvoid test() {\n  const std::string& filename_prefix = \"Shrek-the-Third\";\n  std::vector<double> radii{100.0};\n\n  delete_files(filename_prefix, radii);\n  test_impl<IncludeKleinGordon>(filename_prefix, radii);\n\n  radii.push_back(150.0);\n  radii.push_back(200.0 - std::numeric_limits<double>::epsilon());\n\n  delete_files(filename_prefix, radii);\n  test_impl<IncludeKleinGordon>(filename_prefix, radii);\n  delete_files(filename_prefix, radii);\n}\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.DumpBondiSachsOnWorldtube\",\n                  \"[Unit][Cce]\") {\n  test<false>();\n  // Test with Klein-Gordon variables included\n  test<true>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Equations.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\nnamespace {\n\ntemplate <typename Type, size_t N>\nusing ForwardIgnoreIndex = Type;\n\n// This is a wrapper struct such that the evaluate function can be called with\n// the full set of arguments that would ordinarily be packed into the\n// DataBox. This is necessary for constructing a function that can be compared\n// to the pypp versions.\ntemplate <typename Mutator, typename OutputTag, typename ArgumentTagList,\n          size_t NumberOfGridPoints, typename... Args>\nstruct MutationFromArguments;\n\ntemplate <typename Mutator, typename OutputTag, typename... ArgumentTags,\n          size_t NumberOfGridPoints, typename... Args>\nstruct MutationFromArguments<Mutator, OutputTag, tmpl::list<ArgumentTags...>,\n                             NumberOfGridPoints, Args...> {\n  static ComplexDataVector evaluate(const Args&... args) {\n    auto box = db::create<db::AddSimpleTags<OutputTag, ArgumentTags...>>(\n        typename OutputTag::type{typename OutputTag::type::type{\n            ComplexDataVector{NumberOfGridPoints}}},\n        typename ArgumentTags::type{\n            typename ArgumentTags::type::type{args}}...);\n    db::mutate_apply<Mutator>(make_not_null(&box));\n    return ComplexDataVector{get(db::get<OutputTag>(box)).data()};\n  }\n};\n\n// Creates a proxy struct which acts like the function being tested, but with\n// arguments not packed into a DataBox, then compares the result of that\n// function to the result of a python function\ntemplate <typename Mutator, typename ArgumentTagList, typename OutputTag,\n          size_t NumberOfGridPoints, typename DataType, size_t... ScalarIndices>\nvoid forward_to_pypp_with(std::index_sequence<ScalarIndices...> /*meta*/,\n                          const std::string& python_function) {\n  using Evaluatable =\n      MutationFromArguments<Mutator, OutputTag, ArgumentTagList,\n                            NumberOfGridPoints,\n                            ForwardIgnoreIndex<DataType, ScalarIndices>...>;\n\n  pypp::check_with_random_values<1>(\n      &(Evaluatable::evaluate), \"Equations\", python_function, {{{-2.0, 2.0}}},\n      DataType{NumberOfGridPoints});\n}\n\ntemplate <typename Tag>\nusing all_arguments_for_integrand =\n    tmpl::append<typename ComputeBondiIntegrand<Tag>::temporary_tags,\n                 typename ComputeBondiIntegrand<Tag>::argument_tags>;\n\ntemplate <typename bondi_integrand_tag>\nstruct python_function_for_bondi_integrand;\n\ntemplate <>\nstruct python_function_for_bondi_integrand<Tags::Integrand<Tags::BondiBeta>> {\n  static std::string name() { return \"integrand_for_beta\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<\n    Tags::PoleOfIntegrand<Tags::BondiQ>> {\n  static std::string name() { return \"integrand_for_q_pole_part\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<\n    Tags::RegularIntegrand<Tags::BondiQ>> {\n  static std::string name() { return \"integrand_for_q_regular_part\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<Tags::Integrand<Tags::BondiU>> {\n  static std::string name() { return \"integrand_for_u\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<\n    Tags::PoleOfIntegrand<Tags::BondiW>> {\n  static std::string name() { return \"integrand_for_w_pole_part\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<\n    Tags::RegularIntegrand<Tags::BondiW>> {\n  static std::string name() { return \"integrand_for_w_regular_part\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<\n    Tags::PoleOfIntegrand<Tags::BondiH>> {\n  static std::string name() { return \"integrand_for_h_pole_part\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<\n    Tags::RegularIntegrand<Tags::BondiH>> {\n  static std::string name() { return \"integrand_for_h_regular_part\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<Tags::LinearFactor<Tags::BondiH>> {\n  static std::string name() { return \"linear_factor_for_h\"; }\n};\ntemplate <>\nstruct python_function_for_bondi_integrand<\n    Tags::LinearFactorForConjugate<Tags::BondiH>> {\n  static std::string name() { return \"linear_factor_for_conjugate_h\"; }\n};\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Equations\", \"[Unit][Cce]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"Evolution/Systems/Cce/\"};\n\n  using all_bondi_tags = tmpl::list<Tags::BondiBeta, Tags::BondiQ, Tags::BondiU,\n                                    Tags::BondiW, Tags::BondiH>;\n  tmpl::for_each<all_bondi_tags>([](auto x) {\n    using bondi_tag = typename decltype(x)::type;\n    tmpl::for_each<integrand_terms_to_compute_for_bondi_variable<bondi_tag>>(\n        [](auto y) {\n          using bondi_integrand_tag = typename decltype(y)::type;\n          using bondi_integrand_argument_list =\n              all_arguments_for_integrand<bondi_integrand_tag>;\n          // We check the equations with the DataBox interface, as it is easy\n          // to generate the box with the appropriate type lists,\n          // which are then used to create wrappers that can be compared to Pypp\n          // equations without explicitly enumerating the long argument lists in\n          // this test.\n          forward_to_pypp_with<ComputeBondiIntegrand<bondi_integrand_tag>,\n                               bondi_integrand_argument_list,\n                               bondi_integrand_tag, 5, ComplexDataVector>(\n              std::make_index_sequence<\n                  tmpl::size<bondi_integrand_argument_list>::value>{},\n              python_function_for_bondi_integrand<bondi_integrand_tag>::name());\n        });\n  });\n}\n}  // namespace\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_GaugeTransformBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/VolumeTestHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\nnamespace {\nusing coordinate_variables_tag = TestHelpers::coordinate_variables_tag;\nusing spin_weighted_variables_tag = TestHelpers::spin_weighted_variables_tag;\nusing volume_spin_weighted_variables_tag =\n    TestHelpers::volume_spin_weighted_variables_tag;\n\n// These gauge transforms are extremely hard to validate outside of a true\n// evolution system. Here we settle for verifying that for an\n// analytically-generated set of worldtube data, the transform for a\n// well-behaved set of coordinates is the inverse of the transform for the\n// inverse coordinate functions. This only verifies basic properties of the\n// transform; full validation can only come from tests of evolving systems.\ntemplate <typename Generator>\nvoid test_gauge_transforms_via_inverse_coordinate_map(\n    const gsl::not_null<Generator*> gen) {\n  const size_t l_max = 12;\n  const size_t number_of_radial_grid_points = 10;\n  const size_t number_of_angular_grid_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  auto forward_transform_box = Cce::TestHelpers::create_cce_volume_box(\n      gen, l_max, number_of_radial_grid_points, false);\n\n  auto inverse_transform_box = db::create<db::AddSimpleTags<\n      coordinate_variables_tag, spin_weighted_variables_tag,\n      ::Tags::Variables<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>,\n      volume_spin_weighted_variables_tag, Tags::LMax,\n      Tags::NumberOfRadialPoints,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Spectral::Swsh::Tags::SwshInterpolator<\n          Tags::PartiallyFlatAngularCoords>>>(\n      typename coordinate_variables_tag::type{number_of_angular_grid_points},\n      typename spin_weighted_variables_tag::type{number_of_angular_grid_points},\n      Variables<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>{\n          number_of_angular_grid_points},\n      typename volume_spin_weighted_variables_tag::type{\n          number_of_angular_grid_points * number_of_radial_grid_points},\n      l_max, number_of_radial_grid_points, Spectral::Swsh::SwshInterpolator{},\n      Spectral::Swsh::SwshInterpolator{});\n\n  const double variation_amplitude =\n      db::get<Cce::TestHelpers::VariationAmplitude>(forward_transform_box);\n  const double variation_amplitude_inertial =\n      db::get<Cce::TestHelpers::InertialVariationAmplitude>(\n          forward_transform_box);\n\n  db::mutate<Tags::CauchyCartesianCoords>(\n      [&l_max,\n       &variation_amplitude](const gsl::not_null<tnsr::i<DataVector, 3>*>\n                                 inverse_cauchy_cartesian_coordinates) {\n        tnsr::i<DataVector, 2> inverse_cauchy_angular_coordinates{\n            get<0>(*inverse_cauchy_cartesian_coordinates).size()};\n        // There is a bug in Clang 10.0.0 that gives a nonsensical\n        // error message for the following call to\n        // cached_collocation_metadata unless l_max is captured in\n        // this lambda. The capture should not be necessary because l_max is a\n        // const integer type that is initialized by a constant expression. Note\n        // that l_max need not be declared constexpr for its value to be\n        // retrieved inside a lambda without capturing it. This\n        // line silences the warning that says capturing l_max is not necessary.\n        (void)l_max;\n        const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n            Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n        for (const auto collocation_point : collocation) {\n          auto rootfind = RootFinder::toms748(\n              [&collocation_point, &variation_amplitude](const double x) {\n                return collocation_point.phi -\n                       (x + 1.0e-2 * variation_amplitude * cos(x) *\n                                sin(collocation_point.theta));\n              },\n              collocation_point.phi - 2.0e-2, collocation_point.phi + 2.0e-2,\n              1.0e-15, 1.0e-15);\n          get<1>(inverse_cauchy_angular_coordinates)[collocation_point.offset] =\n              rootfind;\n          get<0>(inverse_cauchy_angular_coordinates)[collocation_point.offset] =\n              collocation_point.theta;\n        }\n        get<0>(*inverse_cauchy_cartesian_coordinates) =\n            sin(get<0>(inverse_cauchy_angular_coordinates)) *\n            cos(get<1>(inverse_cauchy_angular_coordinates));\n        get<1>(*inverse_cauchy_cartesian_coordinates) =\n            sin(get<0>(inverse_cauchy_angular_coordinates)) *\n            sin(get<1>(inverse_cauchy_angular_coordinates));\n        get<2>(*inverse_cauchy_cartesian_coordinates) =\n            cos(get<0>(inverse_cauchy_angular_coordinates));\n      },\n      make_not_null(&inverse_transform_box));\n\n  db::mutate<Tags::PartiallyFlatCartesianCoords>(\n      [&l_max, &variation_amplitude_inertial](\n          const gsl::not_null<tnsr::i<DataVector, 3>*>\n              inverse_inertial_cartesian_coordinates) {\n        tnsr::i<DataVector, 2> inverse_inertial_angular_coordinates{\n            get<0>(*inverse_inertial_cartesian_coordinates).size()};\n        // There is a bug in Clang 10.0.0 that gives a nonsensical\n        // error message for the following call to\n        // cached_collocation_metadata unless l_max is captured in\n        // this lambda. The capture should not be necessary because l_max is a\n        // const integer type that is initialized by a constant expression. Note\n        // that l_max need not be declared constexpr for its value to be\n        // retrieved inside a lambda without capturing it. This\n        // line silences the warning that says capturing l_max is not necessary.\n        (void)l_max;\n        const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n            Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n        for (const auto& collocation_point : collocation) {\n          auto rootfind = RootFinder::toms748(\n              [&collocation_point,\n               &variation_amplitude_inertial](const double x) {\n                return collocation_point.phi -\n                       (x + 1.0e-2 * variation_amplitude_inertial * cos(x) *\n                                sin(collocation_point.theta));\n              },\n              collocation_point.phi - 2.0e-2, collocation_point.phi + 2.0e-2,\n              1.0e-15, 1.0e-15);\n          get<1>(\n              inverse_inertial_angular_coordinates)[collocation_point.offset] =\n              rootfind;\n          get<0>(\n              inverse_inertial_angular_coordinates)[collocation_point.offset] =\n              collocation_point.theta;\n        }\n        get<0>(*inverse_inertial_cartesian_coordinates) =\n            sin(get<0>(inverse_inertial_angular_coordinates)) *\n            cos(get<1>(inverse_inertial_angular_coordinates));\n        get<1>(*inverse_inertial_cartesian_coordinates) =\n            sin(get<0>(inverse_inertial_angular_coordinates)) *\n            sin(get<1>(inverse_inertial_angular_coordinates));\n        get<2>(*inverse_inertial_cartesian_coordinates) =\n            cos(get<0>(inverse_inertial_angular_coordinates));\n      },\n      make_not_null(&inverse_transform_box));\n\n  {\n    INFO(\"Checking GaugeUpdateAngularFromCartesian\");\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n        make_not_null(&forward_transform_box));\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&forward_transform_box));\n    double angular_phi = 0.0;\n    double angular_phi_inertial = 0.0;\n    const auto& computed_angular_cauchy_coordinates =\n        db::get<Tags::CauchyAngularCoords>(forward_transform_box);\n    const auto& computed_angular_inertial_coordinates =\n        db::get<Tags::PartiallyFlatAngularCoords>(forward_transform_box);\n    const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n        Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n    for (const auto collocation_point : collocation) {\n      angular_phi = collocation_point.phi + 1.0e-2 * variation_amplitude *\n                                                cos(collocation_point.phi) *\n                                                sin(collocation_point.theta);\n      angular_phi_inertial =\n          collocation_point.phi + 1.0e-2 * variation_amplitude_inertial *\n                                      cos(collocation_point.phi) *\n                                      sin(collocation_point.theta);\n      CHECK(\n          get<1>(\n              computed_angular_cauchy_coordinates)[collocation_point.offset] ==\n          approx((angular_phi > M_PI) ? angular_phi - 2.0 * M_PI\n                                      : angular_phi));\n      CHECK(\n          get<0>(\n              computed_angular_cauchy_coordinates)[collocation_point.offset] ==\n          approx(collocation_point.theta));\n      CHECK(get<1>(computed_angular_inertial_coordinates)[collocation_point\n                                                              .offset] ==\n            approx((angular_phi_inertial > M_PI)\n                       ? angular_phi_inertial - 2.0 * M_PI\n                       : angular_phi_inertial));\n      CHECK(get<0>(computed_angular_inertial_coordinates)[collocation_point\n                                                              .offset] ==\n            approx(collocation_point.theta));\n    }\n  }\n\n  {\n    INFO(\"Checking GaugeUpdateJacobianFromCoordinates and GaugeUpdateOmega\");\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n        make_not_null(&inverse_transform_box));\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&inverse_transform_box));\n\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n        make_not_null(&forward_transform_box));\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n        make_not_null(&inverse_transform_box));\n\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::PartiallyFlatAngularCoords>>(\n        make_not_null(&forward_transform_box));\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::PartiallyFlatAngularCoords>>(\n        make_not_null(&inverse_transform_box));\n\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n        Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n        make_not_null(&forward_transform_box));\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n        Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n        make_not_null(&inverse_transform_box));\n\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&forward_transform_box));\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&inverse_transform_box));\n\n    db::mutate_apply<\n        GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                         Tags::PartiallyFlatGaugeOmega>>(\n        make_not_null(&forward_transform_box));\n    db::mutate_apply<\n        GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                         Tags::PartiallyFlatGaugeOmega>>(\n        make_not_null(&inverse_transform_box));\n\n    db::mutate_apply<GaugeUpdateOmega<Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n                                      Tags::CauchyGaugeOmega>>(\n        make_not_null(&forward_transform_box));\n    db::mutate_apply<GaugeUpdateOmega<Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n                                      Tags::CauchyGaugeOmega>>(\n        make_not_null(&inverse_transform_box));\n\n    const auto& forward_cauchy_angular_coordinates =\n        db::get<Tags::CauchyAngularCoords>(forward_transform_box);\n    const auto& inverse_cauchy_angular_coordinates =\n        db::get<Tags::CauchyAngularCoords>(inverse_transform_box);\n\n    const auto& forward_inertial_angular_coordinates =\n        db::get<Tags::PartiallyFlatAngularCoords>(forward_transform_box);\n    const auto& inverse_inertial_angular_coordinates =\n        db::get<Tags::PartiallyFlatAngularCoords>(inverse_transform_box);\n\n    const Spectral::Swsh::SwshInterpolator interpolator{\n        get<0>(forward_cauchy_angular_coordinates),\n        get<1>(forward_cauchy_angular_coordinates), l_max};\n\n    const Spectral::Swsh::SwshInterpolator inverse_interpolator{\n        get<0>(inverse_cauchy_angular_coordinates),\n        get<1>(inverse_cauchy_angular_coordinates), l_max};\n\n    const Spectral::Swsh::SwshInterpolator interpolator_inertial{\n        get<0>(forward_inertial_angular_coordinates),\n        get<1>(forward_inertial_angular_coordinates), l_max};\n\n    const Spectral::Swsh::SwshInterpolator inverse_interpolator_inertial{\n        get<0>(inverse_inertial_angular_coordinates),\n        get<1>(inverse_inertial_angular_coordinates), l_max};\n\n    Scalar<SpinWeighted<ComplexDataVector, 0>> interpolated_forward_omega_cd{\n        number_of_angular_grid_points};\n    Scalar<SpinWeighted<ComplexDataVector, 0>>\n        interpolated_forward_omega_cauchy_cd{number_of_angular_grid_points};\n\n    // check that the coordinates are actually inverses of one another.\n    interpolator.interpolate(\n        make_not_null(&get(interpolated_forward_omega_cd)),\n        get(db::get<Tags::PartiallyFlatGaugeOmega>(inverse_transform_box)));\n    interpolator_inertial.interpolate(\n        make_not_null(&get(interpolated_forward_omega_cauchy_cd)),\n        get(db::get<Tags::CauchyGaugeOmega>(inverse_transform_box)));\n\n    SpinWeighted<ComplexDataVector, 0>\n        forward_and_inverse_interpolated_omega_cd{\n            number_of_angular_grid_points};\n    inverse_interpolator.interpolate(\n        make_not_null(&forward_and_inverse_interpolated_omega_cd),\n        get(interpolated_forward_omega_cd));\n\n    SpinWeighted<ComplexDataVector, 0>\n        forward_and_inverse_interpolated_omega_cauchy_cd{\n            number_of_angular_grid_points};\n    inverse_interpolator_inertial.interpolate(\n        make_not_null(&forward_and_inverse_interpolated_omega_cauchy_cd),\n        get(interpolated_forward_omega_cauchy_cd));\n\n    const auto& check_rhs =\n        get(db::get<Tags::PartiallyFlatGaugeOmega>(inverse_transform_box))\n            .data();\n    CHECK_ITERABLE_APPROX(forward_and_inverse_interpolated_omega_cd.data(),\n                          check_rhs);\n    const auto& check_rhs_cauchy =\n        get(db::get<Tags::CauchyGaugeOmega>(inverse_transform_box)).data();\n    CHECK_ITERABLE_APPROX(\n        forward_and_inverse_interpolated_omega_cauchy_cd.data(),\n        check_rhs_cauchy);\n\n    interpolator.interpolate(\n        make_not_null(&get(interpolated_forward_omega_cd)),\n        get(db::get<Tags::PartiallyFlatGaugeOmega>(inverse_transform_box)));\n    const auto& inverse_omega_cd =\n        db::get<Tags::PartiallyFlatGaugeOmega>(forward_transform_box);\n    const auto another_check_rhs = 1.0 / (get(inverse_omega_cd).data());\n    CHECK_ITERABLE_APPROX(get(interpolated_forward_omega_cd).data(),\n                          another_check_rhs);\n\n    interpolator_inertial.interpolate(\n        make_not_null(&get(interpolated_forward_omega_cauchy_cd)),\n        get(db::get<Tags::CauchyGaugeOmega>(inverse_transform_box)));\n    const auto& inverse_omega_cauchy_cd =\n        db::get<Tags::CauchyGaugeOmega>(forward_transform_box);\n    const auto another_check_rhs_cauchy =\n        1.0 / (get(inverse_omega_cauchy_cd).data());\n    CHECK_ITERABLE_APPROX(get(interpolated_forward_omega_cauchy_cd).data(),\n                          another_check_rhs_cauchy);\n  }\n\n  Approx interpolation_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  const auto check_gauge_adjustment_against_inverse = [&forward_transform_box,\n                                                       &inverse_transform_box,\n                                                       &interpolation_approx](\n                                                          auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    INFO(\"computing tag : \" << db::tag_name<tag>());\n    db::mutate_apply<GaugeAdjustedBoundaryValue<tag>>(\n        make_not_null(&forward_transform_box));\n    db::mutate<Tags::BoundaryValue<tag>>(\n        [](const gsl::not_null<typename Tags::BoundaryValue<tag>::type*>\n               inverse_transform_boundary_value,\n           const typename Tags::EvolutionGaugeBoundaryValue<tag>::type&\n               forward_transform_evolution_gauge_value) {\n          *inverse_transform_boundary_value =\n              forward_transform_evolution_gauge_value;\n        },\n        make_not_null(&inverse_transform_box),\n        db::get<Tags::EvolutionGaugeBoundaryValue<tag>>(forward_transform_box));\n    if (std::is_same_v<tag, Tags::BondiQ>) {\n      // populate dr_u in the inverse box using the equation of motion.\n      db::mutate<Tags::BoundaryValue<Tags::Dr<Tags::BondiU>>>(\n          [](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n                 dr_u,\n             const Scalar<SpinWeighted<ComplexDataVector, 0>>& beta,\n             const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n             const Scalar<SpinWeighted<ComplexDataVector, 1>>& q,\n             const Scalar<SpinWeighted<ComplexDataVector, 2>>& j) {\n            SpinWeighted<ComplexDataVector, 0> k;\n            k.data() = sqrt(1.0 + get(j).data() * conj(get(j).data()));\n            get(*dr_u).data() = exp(2.0 * get(beta).data()) /\n                                square(get(r).data()) *\n                                (k.data() * get(q).data() -\n                                 get(j).data() * conj(get(q).data()));\n          },\n          make_not_null(&inverse_transform_box),\n          db::get<Tags::BoundaryValue<Tags::BondiBeta>>(inverse_transform_box),\n          db::get<Tags::BoundaryValue<Tags::BondiR>>(inverse_transform_box),\n          db::get<Tags::BoundaryValue<Tags::BondiQ>>(inverse_transform_box),\n          db::get<Tags::BoundaryValue<Tags::BondiJ>>(inverse_transform_box));\n    }\n    if (std::is_same_v<tag, Tags::BondiH>) {\n      db::mutate<Tags::BoundaryValue<Tags::Du<Tags::BondiJ>>>(\n          [](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n                 du_j,\n             const Scalar<SpinWeighted<ComplexDataVector, 2>>& h,\n             const Scalar<SpinWeighted<ComplexDataVector, 2>>& dr_j,\n             const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n             const Scalar<SpinWeighted<ComplexDataVector, 0>>&\n                 du_r_divided_by_r) {\n            get(*du_j) = get(h) - get(r) * get(du_r_divided_by_r) * get(dr_j);\n          },\n          make_not_null(&inverse_transform_box),\n          db::get<Tags::BoundaryValue<Tags::BondiH>>(inverse_transform_box),\n          db::get<Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>>(\n              inverse_transform_box),\n          db::get<Tags::BoundaryValue<Tags::BondiR>>(inverse_transform_box),\n          db::get<Tags::BoundaryValue<Tags::DuRDividedByR>>(\n              inverse_transform_box));\n    }\n    db::mutate_apply<GaugeAdjustedBoundaryValue<tag>>(\n        make_not_null(&inverse_transform_box));\n\n    // Once we've done the same transform with the the forward and inverse\n    // coordinate maps, check that the final quantity is approximately the\n    // input.\n    const auto& check_lhs =\n        db::get<Tags::BoundaryValue<tag>>(forward_transform_box);\n    const auto& check_rhs =\n        db::get<Tags::EvolutionGaugeBoundaryValue<tag>>(inverse_transform_box);\n    CHECK_ITERABLE_CUSTOM_APPROX(check_lhs, check_rhs, interpolation_approx);\n  };\n\n  using first_phase_gauge_adjustments =\n      tmpl::list<Tags::BondiR, Tags::BondiJ, Tags::Dr<Tags::BondiJ>>;\n  {\n    INFO(\n        \"Checking first part of GaugeAdjustedBoundaryValue computations \"\n        \"(before the initial data computation)\");\n    tmpl::for_each<first_phase_gauge_adjustments>(\n        check_gauge_adjustment_against_inverse);\n  }\n\n  using InverseCubicEvolutionGauge =\n      Cce::TestHelpers::InverseCubicEvolutionGauge;\n  db::mutate_apply<InverseCubicEvolutionGauge::mutate_tags,\n                   InverseCubicEvolutionGauge::argument_tags>(\n      InverseCubicEvolutionGauge{}, make_not_null(&forward_transform_box));\n  db::mutate_apply<InverseCubicEvolutionGauge::mutate_tags,\n                   InverseCubicEvolutionGauge::argument_tags>(\n      InverseCubicEvolutionGauge{}, make_not_null(&inverse_transform_box));\n\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::BondiJ>>>(\n      make_not_null(&forward_transform_box));\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::BondiJ>>>(\n      make_not_null(&inverse_transform_box));\n\n  using second_phase_gauge_adjustments =\n      tmpl::list<Tags::BondiBeta, Tags::BondiU, Tags::BondiQ>;\n  {\n    INFO(\n        \"Checking second part of GaugeAdjustedBoundaryValue \"\n        \"computations (before the BondiUAtScri computation)\");\n    tmpl::for_each<second_phase_gauge_adjustments>(\n        check_gauge_adjustment_against_inverse);\n  }\n\n  // Set up a reasonable volume U and compute the corresponding value in the\n  // inverse gauge so that we can appropriately check the transform for the\n  // second phase. This is used only to determine the rate of change of the\n  // angular coordinates, and does not need to be consistent with the boundary\n  // conditions.\n  db::mutate<Tags::BondiU>(\n      [](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n             bondi_u,\n         const Scalar<SpinWeighted<ComplexDataVector, 1>>& boundary_u) {\n        const ComplexDataVector time_transform = 10.0 * get(boundary_u).data();\n        fill_with_n_copies(make_not_null(&(get(*bondi_u).data())),\n                           time_transform, number_of_radial_grid_points);\n      },\n      make_not_null(&forward_transform_box),\n      db::get<Tags::EvolutionGaugeBoundaryValue<Tags::BondiU>>(\n          forward_transform_box));\n\n  // choose a U value for the inverse transform that results in the appropriate\n  // inverse time dependence for the inverse coordinate transformation.\n  db::mutate<Tags::BondiU, Tags::BoundaryValue<Tags::BondiU>>(\n      [](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n             bondi_u,\n         const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n             boundary_u,\n         const Scalar<SpinWeighted<ComplexDataVector, 2>>& c,\n         const Scalar<SpinWeighted<ComplexDataVector, 0>>& d,\n         const Scalar<SpinWeighted<ComplexDataVector, 0>>& omega_cd,\n         const tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>&\n             x_of_x_tilde) {\n        Spectral::Swsh::SwshInterpolator interpolator{\n            get<0>(x_of_x_tilde), get<1>(x_of_x_tilde), l_max};\n        SpinWeighted<ComplexDataVector, 1> evolution_coords_u{\n            get(*boundary_u).size()};\n        interpolator.interpolate(make_not_null(&evolution_coords_u),\n                                 get(*boundary_u));\n\n        const ComplexDataVector minus_u =\n            10.0 * (-0.5 / square(get(omega_cd).data()) *\n                    (-get(c).data() * conj(evolution_coords_u.data()) +\n                     conj(get(d).data()) * evolution_coords_u.data()));\n        fill_with_n_copies(make_not_null(&(get(*bondi_u).data())), minus_u,\n                           number_of_radial_grid_points);\n      },\n      make_not_null(&inverse_transform_box),\n      db::get<Tags::PartiallyFlatGaugeC>(inverse_transform_box),\n      db::get<Tags::PartiallyFlatGaugeD>(inverse_transform_box),\n      db::get<Tags::PartiallyFlatGaugeOmega>(inverse_transform_box),\n      db::get<Tags::CauchyAngularCoords>(inverse_transform_box));\n\n  // subtract off the gauge adjustments from the gauge for the inverse\n  // transformation box so that the remaining quantities are all in a consistent\n  // gauge. This additional manipulation is not performed by the standard gauge\n  // transform as the extra operations are not required for typical evaluation.\n  db::mutate<Tags::EvolutionGaugeBoundaryValue<Tags::BondiU>>(\n      [](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n             evolution_gauge_boundary_u,\n         const Scalar<SpinWeighted<ComplexDataVector, 1>>&\n             evolution_gauge_full_u) {\n        const ComplexDataVector evolution_gauge_u_scri_slice;\n        make_const_view(\n            make_not_null(&evolution_gauge_u_scri_slice),\n            get(evolution_gauge_full_u).data(),\n            (number_of_radial_grid_points - 1) *\n                Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n        // we add instead of subtracting to save the inconvenience of extracting\n        // the value from the other box and again performing the jacobian\n        // manipulations used in determining the gauge alteration in the first\n        // place.\n        get(*evolution_gauge_boundary_u).data() += evolution_gauge_u_scri_slice;\n      },\n      make_not_null(&inverse_transform_box),\n      db::get<Tags::BondiU>(inverse_transform_box));\n\n  db::mutate_apply<GaugeUpdateTimeDerivatives>(\n      make_not_null(&forward_transform_box));\n  db::mutate_apply<GaugeUpdateTimeDerivatives>(\n      make_not_null(&inverse_transform_box));\n\n  // check that the du_omega_cd are as expected.\n  const auto& x_of_x_tilde =\n      db::get<Tags::CauchyAngularCoords>(inverse_transform_box);\n  const auto& forward_du_omega_cd =\n      db::get<Tags::Du<Tags::PartiallyFlatGaugeOmega>>(forward_transform_box);\n  const auto& forward_u_0 = db::get<Tags::BondiUAtScri>(forward_transform_box);\n  const auto& forward_eth_omega_cd =\n      db::get<Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                               Spectral::Swsh::Tags::Eth>>(\n          forward_transform_box);\n  const auto& forward_omega_cd =\n      get(db::get<Tags::PartiallyFlatGaugeOmega>(forward_transform_box));\n  const SpinWeighted<ComplexDataVector, 0>\n      forward_adjusted_du_omega_cd_over_omega =\n          (get(forward_du_omega_cd) -\n           0.5 * (get(forward_u_0) * conj(get(forward_eth_omega_cd)) +\n                  conj(get(forward_u_0)) * get(forward_eth_omega_cd))) /\n          forward_omega_cd;\n  const Spectral::Swsh::SwshInterpolator omega_interpolator{\n      get<0>(x_of_x_tilde), get<1>(x_of_x_tilde), l_max};\n\n  SpinWeighted<ComplexDataVector, 0> omega_comparison_lhs{\n      number_of_angular_grid_points};\n  omega_interpolator.interpolate(make_not_null(&omega_comparison_lhs),\n                                 forward_adjusted_du_omega_cd_over_omega);\n  const SpinWeighted<ComplexDataVector, 0> omega_comparison_rhs =\n      -get(db::get<Tags::Du<Tags::PartiallyFlatGaugeOmega>>(\n          inverse_transform_box)) /\n      get(db::get<Tags::PartiallyFlatGaugeOmega>(inverse_transform_box));\n\n  // check that the eth_omega_cd are as expected.\n  SpinWeighted<ComplexDataVector, 0> interpolated_forward_omega{\n      number_of_angular_grid_points};\n  omega_interpolator.interpolate(make_not_null(&interpolated_forward_omega),\n                                 forward_omega_cd);\n\n  SpinWeighted<ComplexDataVector, 1> eth_interpolated_forward_omega{\n      number_of_angular_grid_points};\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&eth_interpolated_forward_omega),\n      interpolated_forward_omega);\n\n  const auto& inverse_c =\n      db::get<Tags::PartiallyFlatGaugeC>(inverse_transform_box);\n  const auto& inverse_d =\n      db::get<Tags::PartiallyFlatGaugeD>(inverse_transform_box);\n\n  SpinWeighted<ComplexDataVector, 1> eth_omega{number_of_angular_grid_points};\n  Spectral::Swsh::angular_derivatives<tmpl::list<Spectral::Swsh::Tags::Eth>>(\n      l_max, 1, make_not_null(&eth_omega), forward_omega_cd);\n\n  SpinWeighted<ComplexDataVector, 1> interpolated_forward_eth_omega_cd{\n      number_of_angular_grid_points};\n  omega_interpolator.interpolate(\n      make_not_null(&interpolated_forward_eth_omega_cd),\n      get(forward_eth_omega_cd));\n\n  SpinWeighted<ComplexDataVector, 1> eth_omega_comparison_lhs;\n  eth_omega_comparison_lhs.data() =\n      (0.5 * ((get(inverse_c).data() *\n               conj(interpolated_forward_eth_omega_cd.data())) +\n              conj(get(inverse_d).data()) *\n                  interpolated_forward_eth_omega_cd.data()));\n\n  CHECK_ITERABLE_CUSTOM_APPROX(eth_omega_comparison_lhs,\n                               eth_interpolated_forward_omega,\n                               interpolation_approx);\n\n  using third_phase_gauge_adjustments =\n      tmpl::list<Tags::DuRDividedByR, Tags::BondiH, Tags::BondiW>;\n  {\n    INFO(\n        \"Checking third part of GaugeAdjustedBoundaryValue \"\n        \"computations (after the BondiUAtScri computation)\");\n    tmpl::for_each<third_phase_gauge_adjustments>(\n        check_gauge_adjustment_against_inverse);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.GaugeTransformBoundaryData\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  test_gauge_transforms_via_inverse_coordinate_map(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_InitializeCce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ConformalFactor.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/NoIncomingRadiation.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ZeroNonSmooth.hpp\"\n#include \"Evolution/Systems/Cce/LinearOperators.hpp\"\n#include \"Evolution/Systems/Cce/LinearSolve.hpp\"\n#include \"Evolution/Systems/Cce/NewmanPenrose.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce {\n\ntemplate <template <typename> typename BoundaryTag, typename DbTags>\nvoid check_boundary_and_asymptotic_j(\n    const gsl::not_null<db::DataBox<DbTags>*> box_to_initialize,\n    const size_t number_of_radial_points, const size_t l_max) {\n  // The goal for this initial data is that it should:\n  // - match the value of J and its first derivative on the boundary\n  // - have vanishing value and second derivative at scri+\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const SpinWeighted<ComplexDataVector, 2> boundary_slice_dy_j;\n  make_const_view(make_not_null(&boundary_slice_dy_j),\n                  get(db::get<Tags::Dy<Tags::BondiJ>>(*box_to_initialize)), 0,\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 2> boundary_slice_j;\n  const SpinWeighted<ComplexDataVector, 2> scri_slice_j;\n  make_const_view(make_not_null(&boundary_slice_j),\n                  get(db::get<Tags::BondiJ>(*box_to_initialize)), 0,\n                  number_of_angular_points);\n  make_const_view(make_not_null(&scri_slice_j),\n                  get(db::get<Tags::BondiJ>(*box_to_initialize)),\n                  number_of_angular_points * (number_of_radial_points - 1),\n                  number_of_angular_points);\n\n  const SpinWeighted<ComplexDataVector, 2> scri_slice_dy_dy_j;\n  make_const_view(\n      make_not_null(&scri_slice_dy_dy_j),\n      get(db::get<Tags::Dy<Tags::Dy<Tags::BondiJ>>>(*box_to_initialize)),\n      number_of_angular_points * (number_of_radial_points - 1),\n      number_of_angular_points);\n\n  Approx cce_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(db::get<BoundaryTag<Tags::BondiJ>>(*box_to_initialize)).data(),\n      boundary_slice_j, cce_approx);\n  const auto boundary_slice_dr_j =\n      (2.0 /\n       get(db::get<BoundaryTag<Tags::BondiR>>(*box_to_initialize)).data()) *\n      boundary_slice_dy_j.data();\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      boundary_slice_dr_j,\n      get(db::get<BoundaryTag<Tags::Dr<Tags::BondiJ>>>(*box_to_initialize))\n          .data(),\n      cce_approx);\n  const ComplexDataVector scri_plus_zeroes{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max), 0.0};\n  CHECK_ITERABLE_CUSTOM_APPROX(scri_slice_j, scri_plus_zeroes, cce_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(scri_slice_dy_dy_j.data(), scri_plus_zeroes,\n                               cce_approx);\n}\n\ntemplate <typename DbTags>\nvoid test_initialize_j_inverse_cubic(\n    const gsl::not_null<db::DataBox<DbTags>*> box_to_initialize,\n    const size_t l_max, const size_t number_of_radial_points) {\n  auto node_lock = Parallel::NodeLock{};\n  db::mutate_apply<InitializeJ::InitializeJ<true>::mutate_tags,\n                   InitializeJ::InitializeJ<true>::argument_tags>(\n      InitializeJ::InverseCubic<true>{}, box_to_initialize,\n      make_not_null(&node_lock));\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::BondiJ>>>(\n      box_to_initialize);\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::Dy<Tags::BondiJ>>>>(\n      box_to_initialize);\n  check_boundary_and_asymptotic_j<Tags::BoundaryValue>(\n      box_to_initialize, number_of_radial_points, l_max);\n}\n\ntemplate <typename DbTags>\nvoid test_initialize_j_zero_nonsmooth(\n    const gsl::not_null<db::DataBox<DbTags>*> box_to_initialize,\n    const size_t /*l_max*/, const size_t /*number_of_radial_points*/) {\n  // The iterative procedure can reach error levels better than 1.0e-8, but it\n  // is difficult to do so reliably and quickly for randomly generated data.\n  auto node_lock = Parallel::NodeLock{};\n  db::mutate_apply<InitializeJ::InitializeJ<false>::mutate_tags,\n                   InitializeJ::InitializeJ<false>::argument_tags>(\n      InitializeJ::ZeroNonSmooth{1.0e-8, 400}, box_to_initialize,\n      make_not_null(&node_lock));\n\n  // note we want to copy here to compare against the next version of the\n  // computation\n  // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n  const auto initialized_j = db::get<Tags::BondiJ>(*box_to_initialize);\n\n  const auto initializer = InitializeJ::ZeroNonSmooth{1.0e-8, 400};\n  const auto serialized_and_deserialized_initializer =\n      serialize_and_deserialize(initializer);\n\n  db::mutate_apply<InitializeJ::InitializeJ<false>::mutate_tags,\n                   InitializeJ::InitializeJ<false>::argument_tags>(\n      serialized_and_deserialized_initializer, box_to_initialize,\n      make_not_null(&node_lock));\n  const auto& initialized_j_from_serialized_and_deserialized =\n      db::get<Tags::BondiJ>(*box_to_initialize);\n\n  CHECK_ITERABLE_APPROX(\n      get(initialized_j).data(),\n      get(initialized_j_from_serialized_and_deserialized).data());\n\n  // generate the extra gauge quantities and verify that the boundary value for\n  // J is indeed within the tolerance.\n  db::mutate_apply<GaugeUpdateAngularFromCartesian<\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      box_to_initialize);\n  db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      box_to_initialize);\n  db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n      box_to_initialize);\n  db::mutate_apply<\n      GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                       Tags::PartiallyFlatGaugeOmega>>(box_to_initialize);\n\n  db::mutate_apply<GaugeAdjustedBoundaryValue<Tags::BondiJ>>(box_to_initialize);\n\n  const auto& gauge_adjusted_boundary_j =\n      db::get<Tags::EvolutionGaugeBoundaryValue<Tags::BondiJ>>(\n          *box_to_initialize);\n  for (auto val : get(gauge_adjusted_boundary_j).data()) {\n    CHECK(real(val) < 1.0e-8);\n    CHECK(imag(val) < 1.0e-8);\n  }\n\n  for (auto val : get(initialized_j).data()) {\n    CHECK(real(val) < 1.0e-8);\n    CHECK(imag(val) < 1.0e-8);\n  }\n}\n\ntemplate <typename DbTags>\nvoid test_zero_non_smooth_error(\n    const gsl::not_null<db::DataBox<DbTags>*> box_to_initialize,\n    const size_t /*l_max*/, const size_t /*number_of_radial_points*/) {\n  auto node_lock = Parallel::NodeLock{};\n  db::mutate_apply<InitializeJ::InitializeJ<false>::mutate_tags,\n                   InitializeJ::InitializeJ<false>::argument_tags>(\n      InitializeJ::ZeroNonSmooth{1.0e-12, 1, true}, box_to_initialize,\n      make_not_null(&node_lock));\n}\n\ntemplate <typename DbTags>\nvoid test_initialize_j_no_radiation(\n    const gsl::not_null<db::DataBox<DbTags>*> box_to_initialize,\n    const size_t l_max, const size_t /*number_of_radial_points*/) {\n  // The iterative procedure can reach error levels better than 1.0e-8, but it\n  // is difficult to do so reliably and quickly for randomly generated data.\n  auto node_lock = Parallel::NodeLock{};\n  db::mutate_apply<InitializeJ::InitializeJ<false>::mutate_tags,\n                   InitializeJ::InitializeJ<false>::argument_tags>(\n      InitializeJ::NoIncomingRadiation{1.0e-8, 400}, box_to_initialize,\n      make_not_null(&node_lock));\n\n  // note we want to copy here to compare against the next version of the\n  // computation\n  // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n  const auto initialized_j = db::get<Tags::BondiJ>(*box_to_initialize);\n\n  const auto initializer = InitializeJ::NoIncomingRadiation{1.0e-8, 400};\n  const auto serialized_and_deserialized_initializer =\n      serialize_and_deserialize(initializer);\n\n  db::mutate_apply<InitializeJ::InitializeJ<false>::mutate_tags,\n                   InitializeJ::InitializeJ<false>::argument_tags>(\n      serialized_and_deserialized_initializer, box_to_initialize,\n      make_not_null(&node_lock));\n  const auto& initialized_j_from_serialized_and_deserialized =\n      db::get<Tags::BondiJ>(*box_to_initialize);\n\n  CHECK_ITERABLE_APPROX(\n      get(initialized_j).data(),\n      get(initialized_j_from_serialized_and_deserialized).data());\n\n  db::mutate_apply<GaugeUpdateAngularFromCartesian<\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      box_to_initialize);\n  db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      box_to_initialize);\n  db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n      box_to_initialize);\n  db::mutate_apply<\n      GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                       Tags::PartiallyFlatGaugeOmega>>(box_to_initialize);\n\n  db::mutate_apply<PrecomputeCceDependencies<Tags::EvolutionGaugeBoundaryValue,\n                                             Tags::OneMinusY>>(\n      box_to_initialize);\n  db::mutate_apply<GaugeAdjustedBoundaryValue<Tags::BondiJ>>(box_to_initialize);\n\n  // check that the gauge-transformed boundary data matches up.\n  const auto& boundary_gauge_j =\n      db::get<Tags::EvolutionGaugeBoundaryValue<Tags::BondiJ>>(\n          *box_to_initialize);\n  for (size_t i = 0;\n       i < Spectral::Swsh::number_of_swsh_collocation_points(l_max); ++i) {\n    CHECK(approx(real(get(initialized_j).data()[i])) ==\n          real(get(boundary_gauge_j).data()[i]));\n    CHECK(approx(imag(get(initialized_j).data()[i])) ==\n          imag(get(boundary_gauge_j).data()[i]));\n  }\n\n  db::mutate_apply<GaugeAdjustedBoundaryValue<Tags::BondiR>>(box_to_initialize);\n\n  db::mutate_apply<PrecomputeCceDependencies<Tags::EvolutionGaugeBoundaryValue,\n                                             Tags::BondiR>>(box_to_initialize);\n  db::mutate_apply<PrecomputeCceDependencies<Tags::EvolutionGaugeBoundaryValue,\n                                             Tags::BondiK>>(box_to_initialize);\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::BondiJ>>>(\n      box_to_initialize);\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::Dy<Tags::BondiJ>>>>(\n      box_to_initialize);\n\n  db::mutate_apply<VolumeWeyl<Tags::Psi0>>(box_to_initialize);\n\n  Approx cce_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n  // check that the psi_0 condition holds to acceptable precision -- note the\n  // result of this involves multiple numerical derivatives, so needs to be\n  // slightly loose.\n  for (auto val : get(db::get<Tags::Psi0>(*box_to_initialize)).data()) {\n    CHECK(cce_approx(real(val)) == 0.0);\n    CHECK(cce_approx(imag(val)) == 0.0);\n  }\n}\n\ntemplate <typename DbTags>\nvoid test_initialize_j_conformal_factor(\n    const gsl::not_null<db::DataBox<DbTags>*> box_to_initialize,\n    const bool optimize_l_0_mode, const bool use_beta_integral_estimate,\n    const bool use_input_modes, const bool read_modes_from_file,\n    const ::Cce::InitializeJ::ConformalFactorIterationHeuristic\n        iteration_heuristic,\n    const size_t l_max, const size_t number_of_radial_points) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  CAPTURE(optimize_l_0_mode);\n  CAPTURE(use_beta_integral_estimate);\n  CAPTURE(use_input_modes);\n  CAPTURE(read_modes_from_file);\n  auto node_lock = Parallel::NodeLock{};\n  InitializeJ::ConformalFactor initialize_j_conformal_factor;\n  MAKE_GENERATOR(generator);\n  UniformCustomDistribution<double> dist(1.0e-4, 1.0e-3);\n  const std::string filename = \"ConformalFactorInputModes.h5\";\n\n  std::vector<double> input_mode_data(2 * square(l_max + 1));\n  for (size_t i = 0; i < 8; ++i) {\n    input_mode_data[i] = 0.0;\n  }\n  for (size_t i = 8; i < input_mode_data.size(); ++i) {\n    // exponentially decay higher l-modes\n    input_mode_data[i] = dist(generator) * exp(-1.0 * sqrt(i / 2));\n  }\n  std::vector<std::complex<double>> input_modes(square(l_max + 1));\n  for (size_t i = 0; i < input_modes.size(); ++i) {\n    input_modes[i] = std::complex<double>(input_mode_data[i * 2],\n                                          input_mode_data[i * 2 + 1]);\n  }\n  if (use_input_modes) {\n    if (read_modes_from_file) {\n      if (file_system::check_if_file_exists(filename)) {\n        file_system::rm(filename, true);\n      }\n      h5::H5File<h5::AccessType::ReadWrite> input_h5_modes{filename};\n\n      std::vector<std::string> file_legend{};\n      for (int l = 0; l <= static_cast<int>(l_max); ++l) {\n        for (int m = -l; m <= l; ++m) {\n          file_legend.push_back(\"Real Y_\" + std::to_string(l) + \",\" +\n                                std::to_string(m));\n          file_legend.push_back(\"Imag Y_\" + std::to_string(l) + \",\" +\n                                std::to_string(m));\n        }\n      }\n      auto& dataset =\n          input_h5_modes.try_insert<h5::Dat>(\"/InitialJ\", file_legend, 0);\n      dataset.append(input_mode_data);\n      input_h5_modes.close_current_object();\n    }\n  }\n  if (read_modes_from_file) {\n    InitializeJ::ConformalFactor initialize_j_constructed{\n        1.0e-8,\n        400,\n        true,\n        optimize_l_0_mode,\n        use_beta_integral_estimate,\n        iteration_heuristic,\n        use_input_modes,\n        filename};\n    initialize_j_conformal_factor =\n        serialize_and_deserialize(initialize_j_constructed);\n  } else {\n    InitializeJ::ConformalFactor initialize_j_constructed{\n        1.0e-8,\n        400,\n        true,\n        optimize_l_0_mode,\n        use_beta_integral_estimate,\n        iteration_heuristic,\n        use_input_modes,\n        input_modes};\n    const auto initialize_j_cloned = initialize_j_constructed.get_clone();\n    initialize_j_conformal_factor =\n        *dynamic_cast<::Cce::InitializeJ::ConformalFactor*>(\n            initialize_j_cloned.get());\n  }\n\n  db::mutate_apply<InitializeJ::InitializeJ<false>::mutate_tags,\n                   InitializeJ::InitializeJ<false>::argument_tags>(\n      initialize_j_conformal_factor, box_to_initialize,\n      make_not_null(&node_lock));\n  Approx iterative_solve_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n\n  // perform gauge transforms on the boundary\n  db::mutate_apply<GaugeUpdateAngularFromCartesian<\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      box_to_initialize);\n  db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n      Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n      Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n      box_to_initialize);\n  db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n      box_to_initialize);\n  db::mutate_apply<\n      GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                       Tags::PartiallyFlatGaugeOmega>>(box_to_initialize);\n  db::mutate_apply<GaugeAdjustedBoundaryValue<Tags::BondiBeta>>(\n      box_to_initialize);\n  db::mutate_apply<GaugeAdjustedBoundaryValue<Tags::BondiR>>(box_to_initialize);\n  db::mutate_apply<GaugeAdjustedBoundaryValue<Tags::BondiJ>>(box_to_initialize);\n  db::mutate_apply<GaugeAdjustedBoundaryValue<Tags::Dr<Tags::BondiJ>>>(\n      box_to_initialize);\n  const ComplexDataVector surface_zeroes{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max), 0.0};\n  db::mutate_apply<\n      PrecomputeCceDependencies<Tags::BoundaryValue, Tags::OneMinusY>>(\n      box_to_initialize);\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::BondiJ>>>(\n      box_to_initialize);\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::Dy<Tags::BondiJ>>>>(\n      box_to_initialize);\n\n  if (use_beta_integral_estimate) {\n    // check the conformal factor on scri\n    db::mutate_apply<ComputeBondiIntegrand<Tags::Integrand<Tags::BondiBeta>>>(\n        box_to_initialize);\n    db::mutate_apply<RadialIntegrateBondi<Tags::EvolutionGaugeBoundaryValue,\n                                          Tags::BondiBeta>>(box_to_initialize);\n    Approx beta_estimate_approx = Approx::custom().epsilon(5.0e-8).scale(1.0);\n    auto mutable_beta_copy = get(db::get<Tags::BondiBeta>(*box_to_initialize));\n    SpinWeighted<ComplexDataVector, 0> scri_slice_beta{ComplexDataVector{\n        mutable_beta_copy.data().data() +\n            (number_of_radial_points - 1) * number_of_angular_points,\n        number_of_angular_points}};\n    if (optimize_l_0_mode) {\n      CHECK_ITERABLE_CUSTOM_APPROX(scri_slice_beta, surface_zeroes,\n                                   beta_estimate_approx);\n    } else {\n      Spectral::Swsh::filter_swsh_boundary_quantity(\n          make_not_null(&scri_slice_beta), l_max, 1_st, l_max);\n      CHECK_ITERABLE_CUSTOM_APPROX(scri_slice_beta, surface_zeroes,\n                                   beta_estimate_approx);\n    }\n  } else {\n    // When not using the beta integral estimate, the conformal factor target on\n    // the boundary is chosen to minimize the value of beta\n    if (optimize_l_0_mode) {\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          get(db::get<Tags::EvolutionGaugeBoundaryValue<Tags::BondiBeta>>(\n                  *box_to_initialize))\n              .data(),\n          surface_zeroes, iterative_solve_approx);\n    } else {\n      auto filtered_beta =\n          get(db::get<Tags::EvolutionGaugeBoundaryValue<Tags::BondiBeta>>(\n              *box_to_initialize));\n      Spectral::Swsh::filter_swsh_boundary_quantity(\n          make_not_null(&filtered_beta), l_max, 1_st, l_max);\n      CHECK_ITERABLE_CUSTOM_APPROX(filtered_beta, surface_zeroes,\n                                   iterative_solve_approx);\n    }\n  }\n\n  check_boundary_and_asymptotic_j<Tags::EvolutionGaugeBoundaryValue>(\n      box_to_initialize, number_of_radial_points, l_max);\n  if (use_input_modes) {\n    // check the correctness of the initial data:\n    // - if using input modes, the 1/r part of j should match those modes.\n    const SpinWeighted<ComplexDataVector, 2> scri_slice_dy_j;\n    make_const_view(make_not_null(&scri_slice_dy_j),\n                    get(db::get<Tags::Dy<Tags::BondiJ>>(*box_to_initialize)),\n                    (number_of_radial_points - 1) * number_of_angular_points,\n                    number_of_angular_points);\n    // asymptotically, we have J = J^{(1)}/r = J^{(1)} * (1 - y) / 2 R,\n    SpinWeighted<ComplexDataVector, 2> inverse_r_part_of_asymptotic_j =\n        -2.0 * scri_slice_dy_j *\n        get(db::get<Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>>(\n            *box_to_initialize));\n    auto inverse_r_asymptotic_modes =\n        Spectral::Swsh::libsharp_to_goldberg_modes(\n            Spectral::Swsh::swsh_transform(l_max, 1_st,\n                                           inverse_r_part_of_asymptotic_j),\n            l_max);\n    for (size_t i = 0; i < square(l_max + 1); ++i) {\n      CAPTURE(i);\n      CHECK(approx(real(inverse_r_asymptotic_modes.data()[i])) ==\n            real(input_modes[i]));\n      CHECK(approx(imag(inverse_r_asymptotic_modes.data()[i])) ==\n            imag(input_modes[i]));\n    }\n  }\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n\n  const auto spin_weight_1_created = TestHelpers::test_creation<\n      ::Cce::InitializeJ::ConformalFactorIterationHeuristic>(\n      \"SpinWeight1CoordPerturbation\");\n  CHECK(spin_weight_1_created ==\n        ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n            SpinWeight1CoordPerturbation);\n  const auto only_vary_gauge_d_created = TestHelpers::test_creation<\n      ::Cce::InitializeJ::ConformalFactorIterationHeuristic>(\"OnlyVaryGaugeD\");\n  CHECK(only_vary_gauge_d_created ==\n        ::Cce::InitializeJ::ConformalFactorIterationHeuristic::OnlyVaryGaugeD);\n  const std::string spin_weight_1_streamed =\n      MakeString{} << ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n          SpinWeight1CoordPerturbation;\n  CHECK(spin_weight_1_streamed == \"SpinWeight1CoordPerturbation\");\n  const std::string only_vary_gauge_d_streamed =\n      MakeString{}\n      << ::Cce::InitializeJ::ConformalFactorIterationHeuristic::OnlyVaryGaugeD;\n  CHECK(only_vary_gauge_d_streamed == \"OnlyVaryGaugeD\");\n}\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.InitializeJ\", \"[Unit][Cce]\") {\n  MAKE_GENERATOR(generator);\n  UniformCustomDistribution<size_t> sdist{5, 6};\n  const size_t l_max = sdist(generator);\n  const size_t number_of_radial_points = sdist(generator);\n\n  using boundary_variables_tag = ::Tags::Variables<tmpl::push_back<\n      InitializeJ::InverseCubic<true>::boundary_tags, Tags::PartiallyFlatGaugeC,\n      Tags::PartiallyFlatGaugeD, Tags::PartiallyFlatGaugeOmega,\n      Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                       Spectral::Swsh::Tags::Eth>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiJ>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::Dr<Tags::BondiJ>>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>,\n      Tags::EvolutionGaugeBoundaryValue<Tags::BondiBeta>>>;\n  using pre_swsh_derivatives_variables_tag = ::Tags::Variables<tmpl::list<\n      Tags::BondiJ, Tags::Dy<Tags::BondiJ>, Tags::Dy<Tags::Dy<Tags::BondiJ>>,\n      Tags::BondiK, Tags::BondiR, Tags::Integrand<Tags::BondiBeta>,\n      Tags::BondiBeta, Tags::OneMinusY, Tags::Psi0>>;\n  using tensor_variables_tag = ::Tags::Variables<tmpl::list<\n      Tags::CauchyCartesianCoords, Tags::CauchyAngularCoords,\n      Tags::PartiallyFlatCartesianCoords, Tags::PartiallyFlatAngularCoords>>;\n\n  const size_t number_of_boundary_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_volume_points =\n      number_of_boundary_points * number_of_radial_points;\n  auto box_to_initialize = db::create<db::AddSimpleTags<\n      boundary_variables_tag, pre_swsh_derivatives_variables_tag,\n      tensor_variables_tag, Tags::LMax, Tags::NumberOfRadialPoints,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>>>(\n      typename boundary_variables_tag::type{number_of_boundary_points},\n      typename pre_swsh_derivatives_variables_tag::type{\n          number_of_volume_points},\n      typename tensor_variables_tag::type{number_of_boundary_points}, l_max,\n      number_of_radial_points, Spectral::Swsh::SwshInterpolator{});\n\n  // generate some random values for the boundary data. Mode magnitudes are\n  // roughly representative of typical strains seen in simulations, and are of a\n  // scale that can be fully solved by the iterative procedure used in the more\n  // elaborate initial data generators.\n  UniformCustomDistribution<double> dist(1.0e-5, 1.0e-4);\n  db::mutate<Tags::BoundaryValue<Tags::BondiR>,\n             Tags::BoundaryValue<Tags::BondiBeta>,\n             Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>,\n             Tags::BoundaryValue<Tags::BondiJ>>(\n      [&generator, &dist, &l_max](\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              boundary_r,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              boundary_beta,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n              boundary_dr_j,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n              boundary_j) {\n        SpinWeighted<ComplexModalVector, 2> generated_modes{\n            Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n        Spectral::Swsh::TestHelpers::generate_swsh_modes<2>(\n            make_not_null(&generated_modes.data()), make_not_null(&generator),\n            make_not_null(&dist), 1, l_max);\n\n        get(*boundary_j) =\n            Spectral::Swsh::inverse_swsh_transform(l_max, 1, generated_modes);\n        Spectral::Swsh::filter_swsh_boundary_quantity(\n            make_not_null(&get(*boundary_j)), l_max, l_max / 2);\n\n        SpinWeighted<ComplexModalVector, 0> generated_r_modes{\n            Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n        Spectral::Swsh::TestHelpers::generate_swsh_modes<0>(\n            make_not_null(&generated_modes.data()), make_not_null(&generator),\n            make_not_null(&dist), 1, l_max);\n\n        get(*boundary_r) = Spectral::Swsh::inverse_swsh_transform(\n                               l_max, 1, generated_r_modes) +\n                           100.0;\n        Spectral::Swsh::filter_swsh_boundary_quantity(\n            make_not_null(&get(*boundary_r)), l_max, l_max / 2);\n        get(*boundary_beta) =\n            Spectral::Swsh::inverse_swsh_transform(l_max, 1, generated_r_modes);\n        Spectral::Swsh::filter_swsh_boundary_quantity(\n            make_not_null(&get(*boundary_r)), l_max, l_max / 2);\n\n        get(*boundary_dr_j) = -get(*boundary_j) / get(*boundary_r);\n      },\n      make_not_null(&box_to_initialize));\n  {\n    INFO(\"Check inverse cubic initial data generator\");\n    test_initialize_j_inverse_cubic(make_not_null(&box_to_initialize), l_max,\n                                    number_of_radial_points);\n  }\n  {\n    INFO(\"Check zero nonsmooth initial data generator\");\n    test_initialize_j_zero_nonsmooth(make_not_null(&box_to_initialize), l_max,\n                                     number_of_radial_points);\n  }\n  CHECK_THROWS_WITH(\n      (test_zero_non_smooth_error(make_not_null(&box_to_initialize), l_max,\n                                  number_of_radial_points)),\n      Catch::Matchers::ContainsSubstring(\n          \"Initial data iterative angular solve\"));\n  {\n    INFO(\"Check no incoming radiation initial data generator\");\n    test_initialize_j_no_radiation(make_not_null(&box_to_initialize), l_max,\n                                   number_of_radial_points);\n  }\n  {\n    INFO(\"Check conformal factor initial data generator\");\n    test_initialize_j_conformal_factor(\n        make_not_null(&box_to_initialize), false, false, false, false,\n        ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n            SpinWeight1CoordPerturbation,\n        l_max, number_of_radial_points);\n    test_initialize_j_conformal_factor(\n        make_not_null(&box_to_initialize), false, true, false, false,\n        ::Cce::InitializeJ::ConformalFactorIterationHeuristic::OnlyVaryGaugeD,\n        l_max, number_of_radial_points);\n    test_initialize_j_conformal_factor(\n        make_not_null(&box_to_initialize), true, false, true, false,\n        ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n            SpinWeight1CoordPerturbation,\n        l_max, number_of_radial_points);\n    test_initialize_j_conformal_factor(\n        make_not_null(&box_to_initialize), true, true, true, true,\n        ::Cce::InitializeJ::ConformalFactorIterationHeuristic::\n            SpinWeight1CoordPerturbation,\n        l_max, number_of_radial_points);\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_KleinGordonWorldtubeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeModeRecorder.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/KleinGordonBoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce {\n\n// A dummy buffer updater that holds reference worldtube data to compare with\nclass KleinGordonDummyBufferUpdater\n    : public WorldtubeBufferUpdater<klein_gordon_input_tags> {\n public:\n  KleinGordonDummyBufferUpdater(DataVector time_buffer,\n                                const std::optional<double> extraction_radius,\n                                const double coordinate_amplitude,\n                                const double coordinate_frequency,\n                                const size_t l_max)\n      : time_buffer_{std::move(time_buffer)},\n        extraction_radius_{extraction_radius},\n        coordinate_amplitude_{coordinate_amplitude},\n        coordinate_frequency_{coordinate_frequency},\n        l_max_{l_max} {}\n\n  WRAPPED_PUPable_decl_template(KleinGordonDummyBufferUpdater);  // NOLINT\n\n  explicit KleinGordonDummyBufferUpdater(CkMigrateMessage* /*unused*/) {}\n\n  double update_buffers_for_time(\n      const gsl::not_null<Variables<klein_gordon_input_tags>*> buffers,\n      const gsl::not_null<size_t*> time_span_start,\n      const gsl::not_null<size_t*> time_span_end, const double time,\n      const size_t l_max, const size_t interpolator_length,\n      const size_t buffer_depth,\n      const bool /*time_varies_fastest*/ = true) const override {\n    if (*time_span_end > interpolator_length and\n        time_buffer_[*time_span_end - interpolator_length + 1] > time) {\n      // the next time an update will be required\n      return time_buffer_[*time_span_end - interpolator_length + 1];\n    }\n    // find the time spans that are needed\n    auto new_span_pair = detail::create_span_for_time_value(\n        time, buffer_depth, interpolator_length, 0, time_buffer_.size(),\n        time_buffer_);\n    *time_span_start = new_span_pair.first;\n    *time_span_end = new_span_pair.second;\n\n    Scalar<ComplexModalVector> kg_psi_modal;\n    Scalar<ComplexModalVector> kg_pi_modal;\n    Scalar<DataVector> kg_psi_nodal;\n    Scalar<DataVector> kg_pi_nodal;\n\n    for (size_t time_index = 0; time_index < *time_span_end - *time_span_start;\n         ++time_index) {\n      TestHelpers::create_fake_time_varying_klein_gordon_data(\n          make_not_null(&kg_psi_modal), make_not_null(&kg_pi_modal),\n          make_not_null(&kg_psi_nodal), make_not_null(&kg_pi_nodal),\n          extraction_radius_.value_or(default_extraction_radius_),\n          coordinate_amplitude_, coordinate_frequency_,\n          time_buffer_[time_index + *time_span_start], l_max);\n\n      this->update_buffer_with_scalar_at_time_index(\n          make_not_null(&get<Spectral::Swsh::Tags::SwshTransform<\n                            Cce::Tags::KleinGordonPsi>>(*buffers)),\n          kg_psi_modal, time_index, *time_span_end - *time_span_start);\n\n      this->update_buffer_with_scalar_at_time_index(\n          make_not_null(&get<Spectral::Swsh::Tags::SwshTransform<\n                            Cce::Tags::KleinGordonPi>>(*buffers)),\n          kg_pi_modal, time_index, *time_span_end - *time_span_start);\n    }\n    return time_buffer_[*time_span_end - interpolator_length + 1];\n  }\n\n  std::unique_ptr<WorldtubeBufferUpdater<klein_gordon_input_tags>> get_clone()\n      const override {\n    return std::make_unique<KleinGordonDummyBufferUpdater>(*this);\n  }\n\n  bool time_is_outside_range(const double time) const override {\n    return time < time_buffer_[0] or\n           time > time_buffer_[time_buffer_.size() - 1];\n  }\n\n  size_t get_l_max() const override { return l_max_; }\n\n  double get_extraction_radius() const override {\n    return extraction_radius_.value_or(default_extraction_radius_);\n  }\n\n  DataVector& get_time_buffer() override { return time_buffer_; }\n  bool has_version_history() const override { return true; }\n  void pup(PUP::er& p) override {\n    p | time_buffer_;\n    p | extraction_radius_;\n    p | coordinate_amplitude_;\n    p | coordinate_frequency_;\n    p | l_max_;\n  }\n\n private:\n  template <int Spin>\n  void update_buffer_with_scalar_at_time_index(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexModalVector, Spin>>*>\n          scalar_buffer,\n      const Scalar<ComplexModalVector>& spin_weighted_at_time,\n      const size_t time_index, const size_t time_span_extent) const {\n    for (size_t k = 0; k < get(spin_weighted_at_time).size(); ++k) {\n      get(*scalar_buffer).data()[time_index + k * time_span_extent] =\n          get(spin_weighted_at_time)[k];\n    }\n  }\n\n  DataVector time_buffer_;\n  std::optional<double> extraction_radius_;\n  double default_extraction_radius_ = 100.0;\n  double coordinate_amplitude_ = 0.0;\n  double coordinate_frequency_ = 0.0;\n  size_t l_max_ = 0;\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID Cce::KleinGordonDummyBufferUpdater::my_PUP_ID = 0;\n\nnamespace {\n\n// This function tests `KleinGordonWorldtubeDataManager`, with special focus on\n// its interpolator. Its private member `buffer_updater_` is created from the\n// dummy buffer updater `KleinGordonDummyBufferUpdater` defined above, instead\n// of `KleinGordonWorldtubeH5BufferUpdater`.  This allows us to stick\n// exclusively to the test of `KleinGordonWorldtubeDataManager`. The test of\n// `KleinGordonWorldtubeH5BufferUpdater` is performed below in\n// `test_klein_gordon_worldtube_buffer_updater`.\n//\n// The function first generates nodal and modal data for the scalar field `psi`\n// and its time derivative `pi` for a range of time stamps; and then uses the\n// interpolator of `KleinGordonWorldtubeDataManager` to interpolate the data to\n// a different time (`target_time`). Finally, it compares the interpolated\n// results with the expected ones.\ntemplate <typename Generator>\nvoid test_klein_gordon_data_manager_with_dummy_buffer_updater(\n    const gsl::not_null<Generator*> gen) {\n  const double extraction_radius = 100.0;\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n\n  const size_t buffer_size = 4;\n  const size_t l_max = 8;\n\n  DataVector time_buffer{30};\n  // `target_time` is not an element of `time_buffer`, so the interpolation is\n  // non-trivial.\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    time_buffer[i] = target_time - 1.55 + 0.1 * static_cast<double>(i);\n  }\n\n  // use `KleinGordonWorldtubeDataManager` to interpolate data\n  KleinGordonWorldtubeDataManager boundary_data_manager;\n\n  boundary_data_manager = KleinGordonWorldtubeDataManager{\n      std::make_unique<KleinGordonDummyBufferUpdater>(\n          time_buffer, extraction_radius, amplitude, frequency, l_max),\n      l_max, buffer_size,\n      std::make_unique<intrp::BarycentricRationalSpanInterpolator>(8u, 10u)};\n\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Variables<Tags::klein_gordon_worldtube_boundary_tags>\n      interpolated_boundary_variables{number_of_angular_points};\n\n  Parallel::NodeLock hdf5_lock{};\n  boundary_data_manager.populate_hypersurface_boundary_data(\n      make_not_null(&interpolated_boundary_variables), target_time,\n      make_not_null(&hdf5_lock));\n\n  // populate the expected variables with the result from the analytic modes\n  // passed to the boundary data computation.\n  Scalar<ComplexModalVector> kg_psi_modal;\n  Scalar<ComplexModalVector> kg_pi_modal;\n  Scalar<DataVector> kg_psi_nodal;\n  Scalar<DataVector> kg_pi_nodal;\n\n  TestHelpers::create_fake_time_varying_klein_gordon_data(\n      make_not_null(&kg_psi_modal), make_not_null(&kg_pi_modal),\n      make_not_null(&kg_psi_nodal), make_not_null(&kg_pi_nodal),\n      extraction_radius, amplitude, frequency, target_time, l_max);\n\n  // comparison\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e3)\n          .scale(1.0);\n\n  const auto& interpolated_psi =\n      get<Cce::Tags::BoundaryValue<Cce::Tags::KleinGordonPsi>>(\n          interpolated_boundary_variables);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(kg_psi_nodal), get(interpolated_psi).data(),\n                               angular_derivative_approx);\n\n  const auto& interpolated_pi =\n      get<Cce::Tags::BoundaryValue<Cce::Tags::KleinGordonPi>>(\n          interpolated_boundary_variables);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(kg_pi_nodal), get(interpolated_pi).data(),\n                               angular_derivative_approx);\n}\n\n// This function tests `KleinGordonWorldtubeH5BufferUpdater`, which handles\n// worldtube data of the Klein-Gordon system for CCE.\n// The testing procedure involves the creation of synthetic modal data for the\n// scalar field and its first time derivative, followed by the storage of this\n// data in an HDF5 file. Subsequently, the `KleinGordonWorldtubeH5BufferUpdater`\n// is used to read the worldtube data and verify that the loaded data matches\n// the originally generated data.\n//\n// The test involves three buffer updaters\n// (1) buffer_updater: a `KleinGordonWorldtubeH5BufferUpdater` object, built\n//     from a HDF5 file to be written.\n// (2) serialized_and_deserialized_updater: serialized-deserialized\n//     buffer_updater.\n// (3) dummy_buffer_updater: an object of `KleinGordonDummyBufferUpdater`\n//     defined above.\n//\n// In the test, we treat `dummy_buffer_updater` as a reference object, whose\n// stored modal data are to be compared with. For `buffer_updater` and\n// `serialized_and_deserialized_updater`, we check:\n//\n// (1) The extraction radius is correctly retrieved.\n// (2) The time stamps are the same as the ones we generate.\n// (3) The worldtube data are the same as `dummy_buffer_updater`.\ntemplate <typename Generator>\nvoid test_klein_gordon_worldtube_buffer_updater(\n    const gsl::not_null<Generator*> gen,\n    const bool extraction_radius_in_filename) {\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n\n  const double extraction_radius = 100.0;\n\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n\n  const size_t buffer_size = 4;\n  const size_t interpolator_length = 3;\n  const size_t file_l_max = 8;\n  const size_t computation_l_max = 10;\n\n  const std::string filename = extraction_radius_in_filename\n                                   ? \"KgBoundaryDataH5Test_CceR0100.h5\"\n                                   : \"KgBoundaryDataH5Test.h5\";\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n\n  // Generate fake modal data and write to `filename`\n  {\n    // scoped to close the file\n    Scalar<ComplexModalVector> kg_psi_modal;\n    Scalar<ComplexModalVector> kg_pi_modal;\n    Scalar<DataVector> kg_psi_nodal;\n    Scalar<DataVector> kg_pi_nodal;\n\n    Cce::WorldtubeModeRecorder recorder{file_l_max, filename};\n    for (size_t t = 0; t < 20; ++t) {\n      const double time = 0.01 * static_cast<double>(t) + target_time - 0.1;\n\n      TestHelpers::create_fake_time_varying_klein_gordon_data(\n          make_not_null(&kg_psi_modal), make_not_null(&kg_pi_modal),\n          make_not_null(&kg_psi_nodal), make_not_null(&kg_pi_nodal),\n          extraction_radius, amplitude, frequency, time, file_l_max);\n\n      recorder.append_modal_data<0>(\"/KGPsi\", time, get(kg_psi_modal),\n                                    file_l_max);\n      recorder.append_modal_data<0>(\"/dtKGPsi\", time, get(kg_pi_modal),\n                                    file_l_max);\n    }\n  }\n\n  // Create a `KleinGordonWorldtubeH5BufferUpdater` object `buffer_updater`\n  // from the HDF5 file `filename` written above.\n  // Then examine its extraction radius and time stamps.\n  auto buffer_updater =\n      extraction_radius_in_filename\n          ? KleinGordonWorldtubeH5BufferUpdater{filename}\n          : KleinGordonWorldtubeH5BufferUpdater{filename, extraction_radius};\n\n  size_t time_span_start = 0;\n  size_t time_span_end = 0;\n  Variables<klein_gordon_input_tags> coefficients_buffers_from_file{\n      (buffer_size + 2 * interpolator_length) * square(computation_l_max + 1)};\n  buffer_updater.update_buffers_for_time(\n      make_not_null(&coefficients_buffers_from_file),\n      make_not_null(&time_span_start), make_not_null(&time_span_end),\n      target_time, computation_l_max, interpolator_length, buffer_size);\n\n  const auto& time_buffer = buffer_updater.get_time_buffer();\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    CHECK(time_buffer[i] == approx(target_time - 0.1 + 0.01 * i));\n  }\n  CHECK(buffer_updater.get_extraction_radius() == 100.0);\n\n  // Test the `pup` function of `KleinGordonWorldtubeH5BufferUpdater`.\n  // Serialize and deserialize `buffer_updater` and repeat the checks above.\n  auto serialized_and_deserialized_updater =\n      serialize_and_deserialize(buffer_updater);\n\n  Variables<klein_gordon_input_tags> coefficients_buffers_from_serialized{\n      (buffer_size + 2 * interpolator_length) * square(computation_l_max + 1)};\n  size_t time_span_start_from_serialized = 0;\n  size_t time_span_end_from_serialized = 0;\n  serialized_and_deserialized_updater.update_buffers_for_time(\n      make_not_null(&coefficients_buffers_from_serialized),\n      make_not_null(&time_span_start_from_serialized),\n      make_not_null(&time_span_end_from_serialized), target_time,\n      computation_l_max, interpolator_length, buffer_size);\n\n  const auto& time_buffer_from_serialized =\n      serialized_and_deserialized_updater.get_time_buffer();\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    CHECK(time_buffer_from_serialized[i] ==\n          approx(target_time - 0.1 + 0.01 * i));\n  }\n  CHECK(serialized_and_deserialized_updater.get_extraction_radius() == 100.0);\n\n  // Compare the modal data of `buffer_updater` and\n  // `serialized_and_deserialized_updater`, which goes through the write-read\n  // process, with what we generated earilier. The fiducial modal data are\n  // stored in an object of `KleinGordonDummyBufferUpdater`.\n  time_span_start = 0;\n  time_span_end = 0;\n  const KleinGordonDummyBufferUpdater dummy_buffer_updater{\n      time_buffer, extraction_radius, amplitude, frequency, computation_l_max};\n  Variables<klein_gordon_input_tags> expected_coefficients_buffers{\n      (buffer_size + 2 * interpolator_length) * square(computation_l_max + 1)};\n  dummy_buffer_updater.update_buffers_for_time(\n      make_not_null(&expected_coefficients_buffers),\n      make_not_null(&time_span_start), make_not_null(&time_span_end),\n      target_time, computation_l_max, interpolator_length, buffer_size);\n\n  Approx modal_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e3)\n          .scale(1.0);\n\n  tmpl::for_each<klein_gordon_input_tags>(\n      [&expected_coefficients_buffers, &coefficients_buffers_from_file,\n       &coefficients_buffers_from_serialized, &modal_approx](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& expected_coefs = get<tag>(expected_coefficients_buffers);\n        const auto& kg_coefs = get<tag>(coefficients_buffers_from_file);\n        CHECK_ITERABLE_CUSTOM_APPROX(expected_coefs, kg_coefs, modal_approx);\n        const auto& serialized_kg_coefs =\n            get<tag>(coefficients_buffers_from_serialized);\n        CHECK_ITERABLE_CUSTOM_APPROX(expected_coefs, serialized_kg_coefs,\n                                     modal_approx);\n      });\n\n  // Finally remove the generated file\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.ReadKleinGordonBoundaryDataH5\",\n                  \"[Unit][Cce]\") {\n  register_derived_classes_with_charm<\n      Cce::WorldtubeBufferUpdater<klein_gordon_input_tags>>();\n  register_derived_classes_with_charm<Cce::WorldtubeDataManager<\n      Cce::Tags::klein_gordon_worldtube_boundary_tags>>();\n  register_derived_classes_with_charm<intrp::SpanInterpolator>();\n  MAKE_GENERATOR(gen);\n  {\n    INFO(\"Testing Klein-Gordon buffer updaters\");\n    test_klein_gordon_worldtube_buffer_updater(make_not_null(&gen), true);\n    test_klein_gordon_worldtube_buffer_updater(make_not_null(&gen), false);\n  }\n  {\n    INFO(\"Testing Klein-Gordon data manager\");\n    test_klein_gordon_data_manager_with_dummy_buffer_updater(\n        make_not_null(&gen));\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_LinearOperators.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/Cce/LinearOperators.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.LinearOperators\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(generator);\n  const size_t l_max = 6;\n  const size_t number_of_radial_points = 6;\n  UniformCustomDistribution<double> dist(0.1, 1.0);\n\n  const ComplexDataVector y = outer_product(\n      ComplexDataVector{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max), 1.0},\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(\n          number_of_radial_points));\n  const size_t polynomial_order = 3;\n  const auto y_polynomial_coefficients = make_with_random_values<DataVector>(\n      make_not_null(&generator), make_not_null(&dist), polynomial_order);\n\n  ComplexDataVector to_differentiate{y.size(), 0.0};\n  ComplexDataVector expected_derivative{y.size(), 0.0};\n  for (size_t i = 0; i < polynomial_order; ++i) {\n    to_differentiate += pow(y, i) * y_polynomial_coefficients[i];\n    if (i != 0) {\n      expected_derivative +=\n          static_cast<double>(i) * pow(y, i - 1) * y_polynomial_coefficients[i];\n    }\n  }\n  ComplexDataVector derivative{y.size()};\n  Cce::logical_partial_directional_derivative_of_complex(\n      make_not_null(&derivative), to_differentiate,\n      Mesh<3>{{{Spectral::Swsh::number_of_swsh_theta_collocation_points(l_max),\n                Spectral::Swsh::number_of_swsh_phi_collocation_points(l_max),\n                number_of_radial_points}},\n              Spectral::Basis::Legendre,\n              Spectral::Quadrature::GaussLobatto},\n      2);\n  CHECK_ITERABLE_APPROX(expected_derivative, derivative);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_LinearSolve.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/LinearSolve.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/CceComputationTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\n\nnamespace {\n\n// the first several helper functions in this file are used to create and\n// process the polynomial data that will be used to create the expected values\n// in each of the integration routines\n\nComplexDataVector radial_vector_from_power_series(\n    const ComplexModalVector& powers, const ComplexDataVector& one_minus_y) {\n  ComplexDataVector result{one_minus_y.size(), powers[0]};\n  for (size_t i = 1; i < powers.size(); ++i) {\n    // use of TestHelpers::power due to an internal bug in blaze powers of\n    // Complex vectors\n    result += powers[i] * TestHelpers::power(one_minus_y, i);\n  }\n  return result;\n}\n\ntemplate <typename BondiValueTag, typename DataBoxTagList>\nvoid make_boundary_data(const gsl::not_null<db::DataBox<DataBoxTagList>*> box,\n                        const gsl::not_null<ComplexDataVector*> expected,\n                        const size_t l_max) {\n  db::mutate<Tags::BoundaryValue<BondiValueTag>>(\n      [&expected, &l_max](const gsl::not_null<\n                          typename Tags::BoundaryValue<BondiValueTag>::type*>\n                              boundary) {\n        get(*boundary).data().set_data_ref(\n            expected->data(),\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n      },\n      box);\n}\n\ntemplate <typename DataBoxTagList, typename Generator, typename Distribution,\n          typename... Tags>\nvoid generate_powers_for_tags(\n    const gsl::not_null<db::DataBox<DataBoxTagList>*> box,\n    const gsl::not_null<Generator*> generator,\n    const gsl::not_null<Distribution*> distribution,\n    const size_t number_of_modes, tmpl::list<Tags...> /*meta*/) {\n  EXPAND_PACK_LEFT_TO_RIGHT(\n      db::mutate<TestHelpers::RadialPolyCoefficientsFor<Tags>>(\n          [](const gsl::not_null<Scalar<ComplexModalVector>*> modes,\n             const gsl::not_null<Generator*> gen,\n             const gsl::not_null<Distribution*> dist,\n             const size_t lambda_number_of_modes) {\n            get(*modes) = make_with_random_values<ComplexModalVector>(\n                gen, dist, lambda_number_of_modes);\n          },\n          box, generator, distribution, number_of_modes));\n}\n\ntemplate <typename Tag, typename DataBoxTagList>\nvoid zero_top_modes(const gsl::not_null<db::DataBox<DataBoxTagList>*> box,\n                    const size_t number_of_modes_to_zero,\n                    const size_t total_number_of_modes) {\n  db::mutate<TestHelpers::RadialPolyCoefficientsFor<Tag>>(\n      [&number_of_modes_to_zero, &total_number_of_modes](\n          const gsl::not_null<Scalar<ComplexModalVector>*> modes) {\n        for (size_t i = total_number_of_modes - number_of_modes_to_zero;\n             i < total_number_of_modes; ++i) {\n          get(*modes)[i] = 0.0;\n        }\n      },\n      box);\n}\n\ntemplate <typename DataBoxTagList, typename... Tags>\nvoid generate_volume_data_from_separable(\n    const gsl::not_null<db::DataBox<DataBoxTagList>*> box,\n    const ComplexDataVector& angular_data, const ComplexDataVector& one_minus_y,\n    tmpl::list<Tags...> /*meta*/) {\n  EXPAND_PACK_LEFT_TO_RIGHT(db::mutate<Tags>(\n      [](auto to_fill, const ComplexDataVector& lambda_one_minus_y,\n         const ComplexDataVector& lambda_angular_data,\n         const Scalar<ComplexModalVector>& modes) {\n        get(*to_fill).data() = outer_product(\n            lambda_angular_data,\n            radial_vector_from_power_series(get(modes), lambda_one_minus_y));\n      },\n      box, one_minus_y, angular_data,\n      db::get<TestHelpers::RadialPolyCoefficientsFor<Tags>>(*box)));\n}\n\ntemplate <typename BondiValueTag>\nauto create_box_for_bondi_integration(\n    const size_t l_max, const size_t number_of_radial_grid_points,\n    const size_t number_of_radial_polynomials) {\n  const size_t number_of_grid_points =\n      number_of_radial_grid_points *\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  using integration_tags = tmpl::flatten<tmpl::list<\n      BondiValueTag, typename RadialIntegrateBondi<\n                         Tags::BoundaryValue, BondiValueTag>::integrand_tags>>;\n  using integration_variables_tag = ::Tags::Variables<integration_tags>;\n  using integration_modes_variables_tag =\n      ::Tags::Variables<db::wrap_tags_in<TestHelpers::RadialPolyCoefficientsFor,\n                                         integration_tags>>;\n\n  return db::create<db::AddSimpleTags<\n      integration_variables_tag, Tags::BoundaryValue<BondiValueTag>,\n      integration_modes_variables_tag, Tags::LMax, Tags::NumberOfRadialPoints,\n      Tags::OneMinusY>>(\n      typename integration_variables_tag::type{number_of_grid_points},\n      typename Tags::BoundaryValue<BondiValueTag>::type{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)},\n      typename integration_modes_variables_tag::type{\n          number_of_radial_polynomials},\n      l_max, number_of_radial_grid_points,\n      Scalar<SpinWeighted<ComplexDataVector, 0>>{number_of_grid_points});\n}\n\n// This utility tests the standard integration (which really just applies the\n// standard indefinite integral to each radial slice)\ntemplate <typename BondiValueTag, typename Generator>\nvoid test_regular_integration(const gsl::not_null<Generator*> gen,\n                              const size_t number_of_radial_grid_points,\n                              const size_t l_max) {\n  UniformCustomDistribution<double> dist(0.1, 5.0);\n  const size_t number_of_radial_polynomials = 5;\n  auto box = create_box_for_bondi_integration<BondiValueTag>(\n      l_max, number_of_radial_grid_points, number_of_radial_polynomials);\n\n  generate_powers_for_tags(make_not_null(&box), gen, make_not_null(&dist),\n                           number_of_radial_polynomials,\n                           tmpl::list<BondiValueTag>{});\n\n  // use the above powers to infer the powers for the derivative\n  db::mutate<\n      TestHelpers::RadialPolyCoefficientsFor<Tags::Integrand<BondiValueTag>>>(\n      [](const gsl::not_null<Scalar<ComplexModalVector>*> integrand_modes,\n         const Scalar<ComplexModalVector>& bondi_value_modes) {\n        for (size_t i = 0; i < get(bondi_value_modes).size() - 1; ++i) {\n          // sign change because these are modes of 1 - y.\n          get(*integrand_modes)[i] =\n              -static_cast<double>(i + 1) * get(bondi_value_modes)[i + 1];\n        }\n        get(*integrand_modes)[get(bondi_value_modes).size() - 1] = 0.0;\n      },\n      make_not_null(&box),\n      db::get<TestHelpers::RadialPolyCoefficientsFor<BondiValueTag>>(box));\n\n  const ComplexDataVector one_minus_y =\n      std::complex<double>(1.0, 0.0) *\n      (1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                          Spectral::Quadrature::GaussLobatto>(\n                 number_of_radial_grid_points));\n\n  const auto random_angular_data = make_with_random_values<ComplexDataVector>(\n      gen, make_not_null(&dist),\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  ComplexDataVector expected = outer_product(\n      random_angular_data,\n      radial_vector_from_power_series(\n          get(db::get<TestHelpers::RadialPolyCoefficientsFor<BondiValueTag>>(\n              box)),\n          one_minus_y));\n\n  generate_volume_data_from_separable(\n      make_not_null(&box), random_angular_data, one_minus_y,\n      tmpl::list<Tags::Integrand<BondiValueTag>>{});\n\n  make_boundary_data<BondiValueTag>(make_not_null(&box),\n                                    make_not_null(&expected), l_max);\n\n  db::mutate_apply<RadialIntegrateBondi<Tags::BoundaryValue, BondiValueTag>>(\n      make_not_null(&box));\n\n  Approx numerical_differentiation_approximation =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n          .scale(1.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(expected,\n                               get(db::get<BondiValueTag>(box)).data(),\n                               numerical_differentiation_approximation);\n}\n\n// This algorithm tests the Q and W integration algorithms\ntemplate <typename BondiValueTag, typename Generator>\nvoid test_pole_integration(const gsl::not_null<Generator*> gen,\n                           const size_t number_of_radial_grid_points,\n                           const size_t l_max) {\n  UniformCustomDistribution<double> dist(0.1, 5.0);\n  const size_t number_of_radial_polynomials = 5;\n\n  auto box = create_box_for_bondi_integration<BondiValueTag>(\n      l_max, number_of_radial_grid_points, number_of_radial_polynomials);\n\n  generate_powers_for_tags(\n      make_not_null(&box), gen, make_not_null(&dist),\n      number_of_radial_polynomials,\n      tmpl::list<BondiValueTag, Tags::PoleOfIntegrand<BondiValueTag>>{});\n\n  // use the above powers to infer the powers for regular part of the integrand\n  // The coefficients of the polynomials used in the below mutations can be\n  // determined by expanding both sides of the differential equation\n  // (1 - y) \\partial_y f + 2 f = A + (1 - y) B,\n  // and matching order-by-order in (1 - y).\n  db::mutate<TestHelpers::RadialPolyCoefficientsFor<\n      Tags::PoleOfIntegrand<BondiValueTag>>>(\n      [](const gsl::not_null<Scalar<ComplexModalVector>*>\n             pole_of_integrand_modes,\n         const Scalar<ComplexModalVector>& bondi_value_modes) {\n        get(*pole_of_integrand_modes)[0] = 2.0 * get(bondi_value_modes)[0];\n      },\n      make_not_null(&box),\n      db::get<TestHelpers::RadialPolyCoefficientsFor<BondiValueTag>>(box));\n\n  db::mutate<TestHelpers::RadialPolyCoefficientsFor<\n      Tags::RegularIntegrand<BondiValueTag>>>(\n      [](const gsl::not_null<Scalar<ComplexModalVector>*>\n             regular_integrand_modes,\n         const Scalar<ComplexModalVector>& bondi_value_modes,\n         const Scalar<ComplexModalVector>& pole_integrand_modes) {\n        for (size_t i = 0; i < get(bondi_value_modes).size() - 1; ++i) {\n          // sign change because these are modes of 1 - y.\n          get(*regular_integrand_modes)[i] =\n              -get(pole_integrand_modes)[i + 1] +\n              (1.0 - static_cast<double>(i)) * get(bondi_value_modes)[i + 1];\n        }\n        get(*regular_integrand_modes)[get(bondi_value_modes).size() - 1] = 0.0;\n      },\n      make_not_null(&box),\n      db::get<TestHelpers::RadialPolyCoefficientsFor<BondiValueTag>>(box),\n      db::get<TestHelpers::RadialPolyCoefficientsFor<\n          Tags::PoleOfIntegrand<BondiValueTag>>>(box));\n\n  const ComplexDataVector one_minus_y =\n      std::complex<double>(1.0, 0.0) *\n      (1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                          Spectral::Quadrature::GaussLobatto>(\n                 number_of_radial_grid_points));\n\n  const auto random_angular_data = make_with_random_values<ComplexDataVector>(\n      gen, make_not_null(&dist),\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  ComplexDataVector expected = outer_product(\n      random_angular_data,\n      radial_vector_from_power_series(\n          get(db::get<TestHelpers::RadialPolyCoefficientsFor<BondiValueTag>>(\n              box)),\n          one_minus_y));\n\n  generate_volume_data_from_separable(\n      make_not_null(&box), random_angular_data, one_minus_y,\n      tmpl::list<Tags::PoleOfIntegrand<BondiValueTag>,\n                 Tags::RegularIntegrand<BondiValueTag>>{});\n\n  make_boundary_data<BondiValueTag>(make_not_null(&box),\n                                    make_not_null(&expected), l_max);\n\n  db::mutate<Tags::OneMinusY>(TestHelpers::volume_one_minus_y,\n                              make_not_null(&box), l_max);\n\n  db::mutate_apply<RadialIntegrateBondi<Tags::BoundaryValue, BondiValueTag>>(\n      make_not_null(&box));\n\n  Approx numerical_differentiation_approximation =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n          .scale(1.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(expected,\n                               get(db::get<BondiValueTag>(box)).data(),\n                               numerical_differentiation_approximation);\n}\n\n// this utility tests the H integration algorithm\ntemplate <typename BondiValueTag, typename Generator>\nvoid test_pole_integration_with_linear_operator(\n    const gsl::not_null<Generator*> gen, size_t number_of_radial_grid_points,\n    size_t l_max) {\n  // The typical linear solve performed during realistic CCE evolution involves\n  // fairly small wave amplitudes\n  UniformCustomDistribution<double> dist(0.01, 0.1);\n  const size_t number_of_radial_polynomials = 6;\n\n  auto box = create_box_for_bondi_integration<BondiValueTag>(\n      l_max, number_of_radial_grid_points, number_of_radial_polynomials);\n\n  generate_powers_for_tags(\n      make_not_null(&box), gen, make_not_null(&dist),\n      number_of_radial_polynomials,\n      tmpl::list<BondiValueTag, Tags::PoleOfIntegrand<BondiValueTag>,\n                 Tags::LinearFactor<BondiValueTag>,\n                 Tags::LinearFactorForConjugate<BondiValueTag>>{});\n\n  // In the full treatment of the CCE equations the linear factor is 1.0\n  // asymptotically (near y = 1.0) and the linear factor of the conjugate is 0.0\n  // asymptotically, with often small perturbation at linear and higher orders.\n  // Here we set that leading behavior explicitly.\n  db::mutate<\n      TestHelpers::RadialPolyCoefficientsFor<Tags::LinearFactor<BondiValueTag>>,\n      TestHelpers::RadialPolyCoefficientsFor<\n          Tags::LinearFactorForConjugate<BondiValueTag>>>(\n      [](const gsl::not_null<typename TestHelpers::RadialPolyCoefficientsFor<\n             Tags::LinearFactor<BondiValueTag>>::type*>\n             linear_factor_modes,\n         const gsl::not_null<typename TestHelpers::RadialPolyCoefficientsFor<\n             Tags::LinearFactorForConjugate<BondiValueTag>>::type*>\n             linear_factor_of_conjugate_modes) {\n        get(*linear_factor_modes)[0] = 1.0;\n        get(*linear_factor_of_conjugate_modes)[0] = 0.0;\n      },\n      make_not_null(&box));\n\n  zero_top_modes<BondiValueTag>(make_not_null(&box), 2,\n                                number_of_radial_polynomials);\n  zero_top_modes<Tags::LinearFactor<BondiValueTag>>(\n      make_not_null(&box), 3, number_of_radial_polynomials);\n  zero_top_modes<Tags::LinearFactorForConjugate<BondiValueTag>>(\n      make_not_null(&box), 3, number_of_radial_polynomials);\n\n  const ComplexDataVector one_minus_y =\n      std::complex<double>(1.0, 0.0) *\n      (1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                          Spectral::Quadrature::GaussLobatto>(\n                 number_of_radial_grid_points));\n\n  // generate random modes rather than random collocation values to ensure\n  // representability, and to filter the last couple of l modes so that aliasing\n  // doesn't hurt precision in the nonlinear formulas below.\n  auto random_angular_data = make_with_random_values<ComplexDataVector>(\n      gen, make_not_null(&dist),\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n  SpinWeighted<ComplexModalVector, 0> random_angular_modes{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n  fill_with_random_values(make_not_null(&random_angular_modes), gen,\n                          make_not_null(&dist));\n  const auto& coefficients_metadata =\n      Spectral::Swsh::cached_coefficients_metadata(l_max);\n  for (const auto mode : coefficients_metadata) {\n    if (mode.l > l_max - 2) {\n      random_angular_modes.data()[mode.transform_of_real_part_offset] = 0.0;\n      random_angular_modes.data()[mode.transform_of_imag_part_offset] = 0.0;\n    }\n  }\n  SpinWeighted<ComplexDataVector, 0> filter_buffer;\n  filter_buffer.set_data_ref(random_angular_data.data(),\n                             random_angular_data.size());\n  Spectral::Swsh::inverse_swsh_transform(\n      l_max, 1, make_not_null(&filter_buffer), random_angular_modes);\n\n  generate_volume_data_from_separable(\n      make_not_null(&box), random_angular_data, one_minus_y,\n      tmpl::list<Tags::PoleOfIntegrand<BondiValueTag>,\n                 Tags::LinearFactor<BondiValueTag>,\n                 Tags::LinearFactorForConjugate<BondiValueTag>>{});\n  // unlike the above tests, the nonlinear operators in the H equation ensures\n  // that we have to actually manually build up the last operator to ensure\n  // consistency (from the above data, the pole of integrand is not separable).\n  db::mutate<Tags::PoleOfIntegrand<BondiValueTag>,\n             Tags::RegularIntegrand<BondiValueTag>>(\n      [&random_angular_data, &l_max, &one_minus_y,\n       &number_of_radial_grid_points](\n          const gsl::not_null<\n              typename Tags::PoleOfIntegrand<BondiValueTag>::type*>\n              pole_integrand,\n          const gsl::not_null<\n              typename Tags::RegularIntegrand<BondiValueTag>::type*>\n              regular_integrand,\n          const Scalar<ComplexModalVector>& bondi_value_modes,\n          const Scalar<ComplexModalVector>& pole_integrand_modes,\n          const Scalar<ComplexModalVector>& linear_factor_modes,\n          const Scalar<ComplexModalVector>& linear_factor_of_conjugate_modes) {\n        for (size_t i = 0; i < number_of_radial_grid_points; ++i) {\n          ComplexDataVector angular_view_for_pole_integrand{\n              get(*pole_integrand).data().data() +\n                  i * Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n              Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n          ComplexDataVector angular_view_for_regular_integrand{\n              get(*regular_integrand).data().data() +\n                  i * Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n              Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n\n          angular_view_for_pole_integrand +=\n              square(random_angular_data) * get(bondi_value_modes)[0] *\n                  get(linear_factor_modes)[0] +\n              random_angular_data * conj(random_angular_data) *\n                  conj(get(bondi_value_modes)[0]) *\n                  get(linear_factor_of_conjugate_modes)[0] -\n              random_angular_data * get(pole_integrand_modes)[0];\n          angular_view_for_regular_integrand = 0.0;\n\n          for (size_t j = 0; j < 5; ++j) {\n            angular_view_for_regular_integrand +=\n                (-static_cast<double>(j + 1) * get(bondi_value_modes)[j + 1] -\n                 get(pole_integrand_modes)[j + 1]) *\n                random_angular_data *\n                (j == 0\n                     ? 1.0\n                     : (one_minus_y[i] == 0.0 ? 0.0 : pow(one_minus_y[i], j)));\n            for (size_t k =\n                     static_cast<size_t>(std::max(0, static_cast<int>(j) - 2));\n                 k < std::min(j + 2, size_t{3}); ++k) {\n              angular_view_for_regular_integrand +=\n                  (square(random_angular_data) *\n                       (get(linear_factor_modes)[k] *\n                        get(bondi_value_modes)[(j + 1) - k]) +\n                   random_angular_data * conj(random_angular_data) *\n                       (get(linear_factor_of_conjugate_modes)[k] *\n                        conj(get(bondi_value_modes)[(j + 1) - k]))) *\n                  (j == 0 ? 1.0\n                          : (real(one_minus_y[i]) == 0.0\n                                 ? 0.0\n                                 : pow(one_minus_y[i], j)));\n            }\n          }\n        }\n      },\n      make_not_null(&box),\n      db::get<TestHelpers::RadialPolyCoefficientsFor<BondiValueTag>>(box),\n      db::get<TestHelpers::RadialPolyCoefficientsFor<\n          Tags::PoleOfIntegrand<BondiValueTag>>>(box),\n      db::get<TestHelpers::RadialPolyCoefficientsFor<\n          Tags::LinearFactor<BondiValueTag>>>(box),\n      db::get<TestHelpers::RadialPolyCoefficientsFor<\n          Tags::LinearFactorForConjugate<BondiValueTag>>>(box));\n  ComplexDataVector expected = outer_product(\n      random_angular_data,\n      radial_vector_from_power_series(\n          get(db::get<TestHelpers::RadialPolyCoefficientsFor<BondiValueTag>>(\n              box)),\n          one_minus_y));\n\n  make_boundary_data<BondiValueTag>(make_not_null(&box),\n                                    make_not_null(&expected), l_max);\n\n  db::mutate<Tags::OneMinusY>(TestHelpers::volume_one_minus_y,\n                              make_not_null(&box), l_max);\n\n  db::mutate_apply<RadialIntegrateBondi<Tags::BoundaryValue, BondiValueTag>>(\n      make_not_null(&box));\n\n  Approx numerical_differentiation_approximation =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n          .scale(1.0);\n  INFO(\"number of radial grid points: \" << number_of_radial_grid_points);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected,\n                               get(db::get<BondiValueTag>(box)).data(),\n                               numerical_differentiation_approximation);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.LinearSolve\", \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{3, 6};\n  const size_t l_max = sdist(gen);\n  const size_t number_of_radial_grid_points = sdist(gen) + 4;\n\n  test_regular_integration<Tags::BondiBeta>(\n      make_not_null(&gen), number_of_radial_grid_points, l_max);\n  test_regular_integration<Tags::BondiU>(make_not_null(&gen),\n                                         number_of_radial_grid_points, l_max);\n  test_pole_integration<Tags::BondiQ>(make_not_null(&gen),\n                                      number_of_radial_grid_points, l_max);\n  test_pole_integration<Tags::BondiW>(make_not_null(&gen),\n                                      number_of_radial_grid_points, l_max);\n  test_pole_integration_with_linear_operator<Tags::BondiH>(\n      make_not_null(&gen), number_of_radial_grid_points, l_max);\n}\n}  // namespace\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_NewmanPenrose.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/Cce/NewmanPenrose.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/VolumeTestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\nnamespace {\nvoid pypp_test_volume_np_spin_coefficients() {\n  const pypp::SetupLocalPythonEnvironment\n      local_python_env{\"Evolution/Systems/Cce/\"};\n\n  const size_t num_pts = 5;\n\n  pypp::check_with_random_values<1>(&newman_penrose_alpha, \"NewmanPenrose\",\n                                    {\"newman_penrose_alpha\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_beta, \"NewmanPenrose\",\n                                    {\"newman_penrose_beta\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_gamma, \"NewmanPenrose\",\n                                    {\"newman_penrose_gamma\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_epsilon, \"NewmanPenrose\",\n                                    {\"newman_penrose_epsilon\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  // In our choice of tetrad, \\kappa=0\n  pypp::check_with_random_values<1>(&newman_penrose_tau, \"NewmanPenrose\",\n                                    {\"newman_penrose_tau\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_sigma, \"NewmanPenrose\",\n                                    {\"newman_penrose_sigma\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_rho, \"NewmanPenrose\",\n                                    {\"newman_penrose_rho\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_pi, \"NewmanPenrose\",\n                                    {\"newman_penrose_pi\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_nu, \"NewmanPenrose\",\n                                    {\"newman_penrose_nu\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_mu, \"NewmanPenrose\",\n                                    {\"newman_penrose_mu\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&newman_penrose_lambda, \"NewmanPenrose\",\n                                    {\"newman_penrose_lambda\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n}\n\nvoid pypp_test_volume_weyl() {\n  const pypp::SetupLocalPythonEnvironment\n      local_python_env{\"Evolution/Systems/Cce/\"};\n\n  const size_t num_pts = 5;\n\n  pypp::check_with_random_values<1>(&(VolumeWeyl<Tags::Psi0>::apply),\n                                    \"NewmanPenrose\", {\"psi0\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&(VolumeWeyl<Tags::Psi1>::apply),\n                                    \"NewmanPenrose\", {\"psi1\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n  pypp::check_with_random_values<1>(&(VolumeWeyl<Tags::Psi2>::apply),\n                                    \"NewmanPenrose\", {\"psi2\"}, {{{1.0, 5.0}}},\n                                    DataVector{num_pts});\n}\n}  // namespace\n\nnamespace {\n\nvoid test_np_spin_coefficient_compute_tags() {\n  using ::TestHelpers::db::test_compute_tag;\n\n  test_compute_tag<Cce::Tags::NewmanPenroseAlphaCompute>(\n      \"NewmanPenroseAlpha\");\n  test_compute_tag<Cce::Tags::NewmanPenroseBetaCompute>(\n      \"NewmanPenroseBeta\");\n  test_compute_tag<Cce::Tags::NewmanPenroseGammaCompute>(\n      \"NewmanPenroseGamma\");\n  test_compute_tag<Cce::Tags::NewmanPenroseEpsilonCompute>(\n      \"NewmanPenroseEpsilon\");\n  // In our choice of tetrad, \\kappa=0\n  test_compute_tag<Cce::Tags::NewmanPenroseTauCompute>(\n      \"NewmanPenroseTau\");\n  test_compute_tag<Cce::Tags::NewmanPenroseSigmaCompute>(\n      \"NewmanPenroseSigma\");\n  test_compute_tag<Cce::Tags::NewmanPenroseRhoCompute>(\n      \"NewmanPenroseRho\");\n  test_compute_tag<Cce::Tags::NewmanPenrosePiCompute>(\n      \"NewmanPenrosePi\");\n  test_compute_tag<Cce::Tags::NewmanPenroseNuCompute>(\n      \"NewmanPenroseNu\");\n  test_compute_tag<Cce::Tags::NewmanPenroseMuCompute>(\n      \"NewmanPenroseMu\");\n  test_compute_tag<Cce::Tags::NewmanPenroseLambdaCompute>(\n      \"NewmanPenroseLambda\");\n}\n\n// This unit test is to validate the calculation of the Weyl scalar psi0 on the\n// worldtube. The structure is in parallel with Test_GaugeTransformBoundaryData\n// (most codes are copied from there). The test constructs a stationary Kerr\n// spacetime in nontrivial time-dependent oscillating coordinates on both Cauchy\n// and CCE grids. Then we compute psi0 on the worldtube. In principle, it should\n// be consistent with 0.\ntemplate <typename Generator>\nvoid compute_psi0_of_bh_on_wt(const gsl::not_null<Generator*> gen) {\n  const size_t l_max = 12;\n  const size_t number_of_radial_grid_points = 10;\n  const size_t number_of_angular_grid_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  auto box = Cce::TestHelpers::create_cce_volume_box(\n      gen, l_max, number_of_radial_grid_points, true);\n\n  // Now we need to transform the boundary data of BondiJ and BondiR from the\n  // Cauchy grid to the partially flat grid.\n  const auto perform_gauge_adjustment = [&box](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    INFO(\"computing tag : \" << db::tag_name<tag>());\n    db::mutate_apply<GaugeAdjustedBoundaryValue<tag>>(make_not_null(&box));\n  };\n\n  using gauge_adjustments =\n      tmpl::list<Tags::BondiR, Tags::BondiJ, Tags::Dr<Tags::BondiJ>>;\n  tmpl::for_each<gauge_adjustments>(perform_gauge_adjustment);\n\n  // Now we construct the volume data of BondiJ (on a null slice) based on its\n  // boundary data.\n  db::mutate_apply<Cce::TestHelpers::InverseCubicEvolutionGauge::mutate_tags,\n                   Cce::TestHelpers::InverseCubicEvolutionGauge::argument_tags>(\n      Cce::TestHelpers::InverseCubicEvolutionGauge{}, make_not_null(&box));\n\n  // Then we compute psi0 on the worldtube\n  db::mutate_apply<TransformBondiJToCauchyCoords>(make_not_null(&box));\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::BondiJCauchyView>>>(\n      make_not_null(&box));\n  db::mutate_apply<\n      PreSwshDerivatives<Tags::Dy<Tags::Dy<Tags::BondiJCauchyView>>>>(\n      make_not_null(&box));\n  db::mutate_apply<\n      PrecomputeCceDependencies<Tags::BoundaryValue, Tags::OneMinusY>>(\n      make_not_null(&box));\n  db::mutate_apply<VolumeWeyl<Tags::Psi0Match>>(make_not_null(&box));\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::Psi0Match>>>(\n      make_not_null(&box));\n  db::mutate_apply<InnerBoundaryWeyl>(make_not_null(&box));\n\n  // Finally, we expect the results should be consistent with 0.\n  const auto& psi0_wt = db::get<Tags::BoundaryValue<Tags::Psi0Match>>(box);\n  SpinWeighted<ComplexDataVector, 2> psi0_desired{number_of_angular_grid_points,\n                                                  0.0};\n  Approx interpolation_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n          .scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(psi0_wt).data(), psi0_desired.data(),\n                               interpolation_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.NewmanPenrose\", \"[Unit][Cce]\") {\n  test_np_spin_coefficient_compute_tags();\n  pypp_test_volume_np_spin_coefficients();\n\n  pypp_test_volume_weyl();\n\n  MAKE_GENERATOR(gen);\n  compute_psi0_of_bh_on_wt(make_not_null(&gen));\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_OptionTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n\n#include \"Evolution/Systems/Cce/AnalyticBoundaryDataManager.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/BouncingBlackHole.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/GaugeWave.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/LinearizedBondiSachs.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RobinsonTrautman.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/RotatingSchwarzschild.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/TeukolskyWave.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/WorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ConformalFactor.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InverseCubic.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/NoIncomingRadiation.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/ZeroNonSmooth.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhInterfaceManager.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLocalTimeStepping.hpp\"\n#include \"Evolution/Systems/Cce/InterfaceManagers/GhLockstep.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/OptionTags/TimeStepper.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.OptionTags\", \"[Unit][Cce]\") {\n  TestHelpers::db::test_simple_tag<\n      Cce::InitializationTags::ScriInterpolationOrder>(\n      \"ScriInterpolationOrder\");\n  TestHelpers::db::test_simple_tag<Cce::InitializationTags::ScriOutputDensity>(\n      \"ScriOutputDensity\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::H5WorldtubeBoundaryDataManager>(\n      \"H5WorldtubeBoundaryDataManager\");\n  TestHelpers::db::test_simple_tag<\n      Cce::Tags::KleinGordonH5WorldtubeBoundaryDataManager>(\n      \"KleinGordonH5WorldtubeBoundaryDataManager\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::ExtractionRadius>(\n      \"ExtractionRadius\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::ExtractionRadiusSimple>(\n      \"ExtractionRadius\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::ExtractionRadiusFromH5>(\n      \"ExtractionRadius\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::FilePrefix>(\"FilePrefix\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::ObservationLMax>(\n      \"ObservationLMax\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::FilterLMax>(\"FilterLMax\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::RadialFilterAlpha>(\n      \"RadialFilterAlpha\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::RadialFilterHalfPower>(\n      \"RadialFilterHalfPower\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::StartTimeFromFile>(\"StartTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::EndTimeFromFile>(\"EndTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NoEndTime>(\"EndTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::SpecifiedStartTime>(\"StartTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::SpecifiedEndTime>(\"EndTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::GhInterfaceManager>(\n      \"GhInterfaceManager\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::AnalyticBoundaryDataManager>(\n      \"AnalyticBoundaryDataManager\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::InitializeJ<true>>(\"InitializeJ\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::InitializeJ<false>>(\n      \"InitializeJ\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::AnalyticInitializeJ>(\n      \"InitializeJ\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::OutputNoninertialNews>(\n      \"OutputNoninertialNews\");\n  TestHelpers::db::test_simple_tag<\n      Cce::Tags::CceEvolutionPrefix<::Tags::ConcreteTimeStepper<TimeStepper>>>(\n      \"ConcreteTimeStepper\");\n\n  CHECK(\n      TestHelpers::test_option_tag<Cce::OptionTags::BondiSachsOutputFilePrefix>(\n          \"Shrek\") == \"Shrek\");\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::FilterLMax>(\"7\") == 7_st);\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::RadialFilterAlpha>(\n            \"32.5\") == 32.5);\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::RadialFilterHalfPower>(\n            \"20\") == 20_st);\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::ObservationLMax>(\"6\") ==\n        6_st);\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::ExtractionRadius>(\n            \"100.0\") == 100.0);\n\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::EndTime>(\"4.0\") ==\n        std::optional<double>{4.0});\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::EndTime>(\"Auto\") ==\n        std::optional<double>{});\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::StartTime>(\"2.0\") ==\n        std::optional<double>{2.0});\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::StartTime>(\"Auto\") ==\n        std::optional<double>{});\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::BoundaryDataFilename>(\n            \"OptionTagsCceR0100.h5\") == \"OptionTagsCceR0100.h5\");\n  CHECK(TestHelpers::test_option_tag<\n            Cce::OptionTags::KleinGordonBoundaryDataFilename>(\n            \"OptionTagsKleinGordonCceR0100.h5\") ==\n        \"OptionTagsKleinGordonCceR0100.h5\");\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::H5LookaheadTimes>(\"5\") ==\n        5_st);\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::ScriInterpolationOrder>(\n            \"4\") == 4_st);\n\n  CHECK(TestHelpers::test_option_tag<Cce::OptionTags::ScriOutputDensity>(\"6\") ==\n        6_st);\n\n  TestHelpers::test_option_tag<Cce::OptionTags::InitializeJ<true>>(\n      \"InverseCubic\");\n  TestHelpers::test_option_tag<Cce::OptionTags::InitializeJ<false>>(\n      \"InverseCubic\");\n  TestHelpers::test_option_tag<Cce::OptionTags::AnalyticSolution>(\n      \"BouncingBlackHole:\\n\"\n      \"  Period: 40.0\\n\"\n      \"  ExtractionRadius: 30.0\\n\"\n      \"  Mass: 1.0\\n\"\n      \"  Amplitude: 2.0\");\n  TestHelpers::test_option_tag<Cce::OptionTags::AnalyticSolution>(\n      \"GaugeWave:\\n\"\n      \"  ExtractionRadius: 40.0\\n\"\n      \"  Mass: 1.0\\n\"\n      \"  Frequency: 0.5\\n\"\n      \"  Amplitude: 0.1\\n\"\n      \"  PeakTime: 50.0\\n\"\n      \"  Duration: 10.0\");\n  TestHelpers::test_option_tag<Cce::OptionTags::AnalyticSolution>(\n      \"LinearizedBondiSachs:\\n\"\n      \"  ExtractionRadius: 40.0\\n\"\n      \"  InitialModes: [[0.20, 0.10], [0.08, 0.04]]\\n\"\n      \"  Frequency: 0.2\");\n  TestHelpers::test_option_tag<Cce::OptionTags::AnalyticSolution>(\n      \"RobinsonTrautman:\\n\"\n      \"  InitialModes:\\n\"\n      \"    - [0.0, 0.0]\\n\"\n      \"    - [0.0, 0.0]\\n\"\n      \"    - [0.0, 0.0]\\n\"\n      \"    - [0.0, 0.0]\\n\"\n      \"    - [1.0, 0.5]\\n\"\n      \"  ExtractionRadius: 20.0\\n\"\n      \"  LMax: 16\\n\"\n      \"  Tolerance: 1e-10\\n\"\n      \"  StartTime: 0.0\");\n  TestHelpers::test_option_tag<Cce::OptionTags::AnalyticSolution>(\n      \"RotatingSchwarzschild:\\n\"\n      \"  ExtractionRadius: 20.0\\n\"\n      \"  Mass: 1.0\\n\"\n      \"  Frequency: 0.0\");\n  TestHelpers::test_option_tag<Cce::OptionTags::AnalyticSolution>(\n      \"TeukolskyWave:\\n\"\n      \"  ExtractionRadius: 40.0\\n\"\n      \"  Amplitude: 0.1\\n\"\n      \"  Duration: 3.0\");\n  TestHelpers::test_option_tag_factory_creation<\n      Cce::OptionTags::CceEvolutionPrefix<\n          ::OptionTags::TimeStepper<TimeStepper>>,\n      TimeSteppers::Rk3HesthavenSsp>(\"Rk3HesthavenSsp:\");\n\n  const std::string filename = \"OptionTagsTestCceR0100.h5\";\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  Cce::TestHelpers::write_test_file(\n      gr::Solutions::KerrSchild{1.0, {{0.2, 0.2, 0.2}}, {{0.0, 0.0, 0.0}}},\n      filename, 4.0, 100.0, 0.1, 0.1, 8);\n\n  CHECK(Cce::Tags::H5WorldtubeBoundaryDataManager::create_from_options(\n            8, filename, 3, std::make_unique<intrp::CubicSpanInterpolator>(),\n            std::nullopt)\n            ->get_l_max() == 8);\n\n  CHECK(Cce::Tags::FilePrefix::create_from_options(\"Shrek 2\") == \"Shrek 2\");\n  CHECK(Cce::Tags::LMax::create_from_options(8u) == 8u);\n  CHECK(Cce::Tags::NumberOfRadialPoints::create_from_options(6u) == 6u);\n\n  CHECK(Cce::Tags::StartTimeFromFile::create_from_options(\n            std::optional<double>{}, \"OptionTagsTestCceR0100.h5\", false) ==\n        2.5);\n  CHECK(Cce::Tags::StartTimeFromFile::create_from_options(\n            std::optional<double>{3.3}, \"OptionTagsTestCceR0100.h5\", false) ==\n        3.3);\n  CHECK(Cce::Tags::SpecifiedStartTime::create_from_options(\n            std::optional<double>(2.0)) == 2.0);\n\n  CHECK(Cce::Tags::EndTimeFromFile::create_from_options(\n            std::optional<double>{}, \"OptionTagsTestCceR0100.h5\", false) ==\n        5.4);\n  CHECK(Cce::Tags::EndTimeFromFile::create_from_options(\n            std::optional<double>{2.2}, \"OptionTagsTestCceR0100.h5\", false) ==\n        2.2);\n  CHECK(Cce::Tags::SpecifiedEndTime::create_from_options(\n            std::optional<double>(40.0)) == 40.0);\n\n  CHECK(Cce::Tags::ObservationLMax::create_from_options(5_st) == 5_st);\n\n  CHECK(Cce::InitializationTags::ScriInterpolationOrder::create_from_options(\n            6_st) == 6_st);\n\n  CHECK(Cce::InitializationTags::ScriOutputDensity::create_from_options(4_st) ==\n        4_st);\n\n  CHECK(Cce::Tags::AnalyticBoundaryDataManager::create_from_options(\n            10.0, 8,\n            std::make_unique<Cce::Solutions::RotatingSchwarzschild>(10.0, 1.0,\n                                                                    0.5))\n            .get_l_max() == 8);\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_PreSwshDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/LinearOperators.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/CceComputationTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\n\nnamespace {\ntemplate <int Spin>\nstruct TestSpinWeightedScalar : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Spin>>;\n};\n\nusing test_pre_swsh_derivative_dependencies =\n    tmpl::list<Tags::BondiBeta, Tags::BondiJ, Tags::BondiQ, Tags::BondiU>;\n}  // namespace\n\nnamespace detail {\n// these are provided as a slimmed-down representation of a computational\n// procedure that suffices to demonstrate that all of the template\n// specializations of `PreSwshDerivatives` work correctly, rather than executing\n// the full sequence of CCE steps, which would be too heavy for this test.\ntemplate <>\nstruct TagsToComputeForImpl<TestSpinWeightedScalar<0>> {\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiJ>, Tags::Dy<Tags::Dy<Tags::BondiJ>>,\n                 Tags::Dy<Tags::BondiBeta>, Tags::Dy<Tags::Dy<Tags::BondiBeta>>,\n                 Tags::Dy<Tags::BondiU>, Tags::Dy<Tags::Dy<Tags::BondiU>>,\n                 Tags::Dy<Tags::BondiQ>, Tags::Dy<Tags::Dy<Tags::BondiQ>>>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n};\n\ntemplate <>\nstruct TagsToComputeForImpl<TestSpinWeightedScalar<1>> {\n  using pre_swsh_derivative_tags = tmpl::list<\n      Tags::BondiJbar, Tags::BondiUbar, Tags::BondiQbar,\n      ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n      Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>,\n      Tags::Dy<Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>>,\n      ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>,\n      ::Tags::Multiplies<Tags::BondiUbar, Tags::Dy<Tags::BondiJ>>,\n      Tags::Dy<::Tags::Multiplies<Tags::BondiUbar, Tags::Dy<Tags::BondiJ>>>,\n      Tags::Dy<::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>>>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<>;\n};\n}  // namespace detail\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.PreSwshDerivatives\",\n                  \"[Unit][Cce]\") {\n  MAKE_GENERATOR(generator);\n  const size_t l_max = 8;\n  const size_t number_of_radial_grid_points = 8;\n\n  using pre_swsh_derivative_tag_list = tmpl::append<\n      pre_swsh_derivative_tags_to_compute_for_t<TestSpinWeightedScalar<0>>,\n      pre_swsh_derivative_tags_to_compute_for_t<TestSpinWeightedScalar<1>>,\n      test_pre_swsh_derivative_dependencies>;\n\n  using pre_swsh_derivatives_variables_tag =\n      ::Tags::Variables<pre_swsh_derivative_tag_list>;\n  using swsh_derivatives_variables_tag =\n      ::Tags::Variables<tmpl::list<Spectral::Swsh::Tags::Derivative<\n          Tags::BondiBeta, Spectral::Swsh::Tags::Eth>>>;\n  using separated_pre_swsh_derivatives_angular_data =\n      ::Tags::Variables<db::wrap_tags_in<TestHelpers::AngularCollocationsFor,\n                                         pre_swsh_derivative_tag_list>>;\n  using separated_pre_swsh_derivatives_radial_modes =\n      ::Tags::Variables<db::wrap_tags_in<TestHelpers::RadialPolyCoefficientsFor,\n                                         pre_swsh_derivative_tag_list>>;\n\n  const size_t number_of_grid_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n      number_of_radial_grid_points;\n\n  auto expected_box = db::create<db::AddSimpleTags<\n      pre_swsh_derivatives_variables_tag, swsh_derivatives_variables_tag,\n      separated_pre_swsh_derivatives_angular_data,\n      separated_pre_swsh_derivatives_radial_modes>>(\n      typename pre_swsh_derivatives_variables_tag::type{number_of_grid_points},\n      typename swsh_derivatives_variables_tag::type{number_of_grid_points, 0.0},\n      typename separated_pre_swsh_derivatives_angular_data::type{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)},\n      typename separated_pre_swsh_derivatives_radial_modes::type{\n          number_of_radial_grid_points});\n\n  db::mutate<pre_swsh_derivatives_variables_tag, swsh_derivatives_variables_tag,\n             separated_pre_swsh_derivatives_angular_data,\n             separated_pre_swsh_derivatives_radial_modes>(\n      [&generator](\n          const gsl::not_null<\n              typename pre_swsh_derivatives_variables_tag::type*>\n              pre_swsh_derivatives,\n          const gsl::not_null<typename swsh_derivatives_variables_tag::type*>\n              swsh_derivatives,\n          const gsl::not_null<\n              typename separated_pre_swsh_derivatives_angular_data::type*>\n              pre_swsh_separated_angular_data,\n          const gsl::not_null<\n              typename separated_pre_swsh_derivatives_radial_modes::type*>\n              pre_swsh_separated_radial_modes) {\n        UniformCustomDistribution<double> dist(0.1, 1.0);\n        SpinWeighted<ComplexDataVector, 0> boundary_r;\n        boundary_r.data() =\n            (10.0 +\n             std::complex<double>(1.0, 0.0) *\n                 make_with_random_values<DataVector>(\n                     make_not_null(&generator), make_not_null(&dist),\n                     Spectral::Swsh::number_of_swsh_collocation_points(l_max)));\n        Spectral::Swsh::filter_swsh_boundary_quantity(\n            make_not_null(&boundary_r), l_max, l_max - 3);\n        TestHelpers::generate_separable_expected<\n            test_pre_swsh_derivative_dependencies,\n            tmpl::list<TestSpinWeightedScalar<0>, TestSpinWeightedScalar<1>>>(\n            pre_swsh_derivatives, swsh_derivatives,\n            pre_swsh_separated_angular_data, pre_swsh_separated_radial_modes,\n            make_not_null(&generator), boundary_r, l_max,\n            number_of_radial_grid_points);\n      },\n      make_not_null(&expected_box));\n\n  auto computation_box =\n      db::create<db::AddSimpleTags<Tags::LMax, Tags::Integrand<Tags::BondiBeta>,\n                                   Tags::Integrand<Tags::BondiU>,\n                                   pre_swsh_derivatives_variables_tag,\n                                   swsh_derivatives_variables_tag>>(\n          l_max, db::get<Tags::Dy<Tags::BondiBeta>>(expected_box),\n          db::get<Tags::Dy<Tags::BondiU>>(expected_box),\n          typename pre_swsh_derivatives_variables_tag::type{\n              number_of_grid_points, 0.0},\n          typename swsh_derivatives_variables_tag::type{number_of_grid_points,\n                                                        0.0});\n\n  // duplicate the 'input' values to the computation box\n  TestHelpers::CopyDataBoxTags<\n      Tags::BondiBeta, Tags::BondiJ, Tags::BondiQ,\n      Tags::BondiU>::apply(make_not_null(&computation_box), expected_box);\n\n  mutate_all_pre_swsh_derivatives_for_tag<TestSpinWeightedScalar<0>>(\n      make_not_null(&computation_box));\n  mutate_all_pre_swsh_derivatives_for_tag<TestSpinWeightedScalar<1>>(\n      make_not_null(&computation_box));\n  // approximation needs a little bit of loosening to accommodate the comparison\n  // between the 'separable' math and the more standard numerical procedures.\n  Approx cce_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n\n  CHECK_VARIABLES_CUSTOM_APPROX(\n      db::get<pre_swsh_derivatives_variables_tag>(computation_box),\n      db::get<pre_swsh_derivatives_variables_tag>(expected_box), cce_approx);\n\n  // separately test the nonseparable Tags::JbarQMinus2EthBeta\n  using pre_swsh_spare_variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::BondiJ, Tags::BondiQ, Tags::JbarQMinus2EthBeta,\n                 Tags::DuRDividedByR, Tags::BondiH, Tags::OneMinusY,\n                 Tags::Dy<Tags::BondiJ>, Tags::Du<Tags::BondiJ>>>;\n  using swsh_derivatives_spare_variables_tag =\n      ::Tags::Variables<tmpl::list<Spectral::Swsh::Tags::Derivative<\n          Tags::BondiBeta, Spectral::Swsh::Tags::Eth>>>;\n  auto spare_computation_box =\n      db::create<db::AddSimpleTags<Tags::LMax, Tags::NumberOfRadialPoints,\n                                   pre_swsh_spare_variables_tag,\n                                   swsh_derivatives_spare_variables_tag>>(\n          l_max, number_of_radial_grid_points,\n          typename pre_swsh_spare_variables_tag::type{number_of_grid_points,\n                                                      0.0},\n          typename swsh_derivatives_spare_variables_tag::type{\n              number_of_grid_points, 0.0});\n  UniformCustomDistribution<double> dist(0.1, 1.0);\n  tmpl::for_each<tmpl::list<Tags::BondiJ, Tags::BondiQ,\n                            Spectral::Swsh::Tags::Derivative<\n                                Tags::BondiBeta, Spectral::Swsh::Tags::Eth>,\n                            Tags::DuRDividedByR, Tags::BondiH>>(\n      [&spare_computation_box, &generator, &dist](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        db::mutate<tag>(\n            [&generator,\n             &dist](const gsl::not_null<typename tag::type*> to_generate) {\n              fill_with_random_values(to_generate, make_not_null(&generator),\n                                      make_not_null(&dist));\n            },\n            make_not_null(&spare_computation_box));\n      });\n  db::mutate_apply<\n      PrecomputeCceDependencies<Tags::BoundaryValue, Tags::OneMinusY>>(\n      make_not_null(&spare_computation_box));\n\n  const auto& generated_j = get(db::get<Tags::BondiJ>(spare_computation_box));\n  const auto& generated_q = get(db::get<Tags::BondiQ>(spare_computation_box));\n  const auto& generated_eth_beta =\n      get(db::get<Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                                   Spectral::Swsh::Tags::Eth>>(\n          spare_computation_box));\n  db::mutate_apply<PreSwshDerivatives<Tags::JbarQMinus2EthBeta>>(\n      make_not_null(&spare_computation_box));\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::JbarQMinus2EthBeta>(spare_computation_box)).data(),\n      conj(generated_j.data()) *\n          (generated_q.data() - 2.0 * generated_eth_beta.data()));\n\n  db::mutate_apply<PreSwshDerivatives<Tags::Dy<Tags::BondiJ>>>(\n      make_not_null(&spare_computation_box));\n  db::mutate_apply<PreSwshDerivatives<Tags::Du<Tags::BondiJ>>>(\n      make_not_null(&spare_computation_box));\n\n  const auto& generated_h = get(db::get<Tags::BondiH>(spare_computation_box));\n  const auto& generated_dy_j =\n      get(db::get<Tags::Dy<Tags::BondiJ>>(spare_computation_box));\n  const auto& generated_du_r_divided_by_r =\n      get(db::get<Tags::DuRDividedByR>(spare_computation_box));\n  const auto& one_minus_y =\n      get(db::get<Tags::OneMinusY>(spare_computation_box));\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::Du<Tags::BondiJ>>(spare_computation_box)).data(),\n      generated_h.data() - one_minus_y.data() *\n                               generated_du_r_divided_by_r.data() *\n                               generated_dy_j.data());\n\n  // Check that DyCompute agrees with the automatic one\n  auto manual_dy_box = db::create<\n    db::AddSimpleTags<Tags::LMax, Tags::OneMinusY, Tags::BondiJ>,\n    db::AddComputeTags<Tags::DyCompute<Tags::BondiJ>>>(\n        db::get<Tags::LMax>(spare_computation_box),\n        db::get<Tags::OneMinusY>(spare_computation_box),\n        db::get<Tags::BondiJ>(spare_computation_box));\n\n  CHECK_ITERABLE_APPROX(\n      get(db::get<Tags::Dy<Tags::BondiJ>>(manual_dy_box)),\n      generated_dy_j);\n\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_PrecomputeCceDependencies.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/CceComputationTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\ntemplate <typename Generator, typename PrecomputationBox, typename ExpectedBox>\nvoid generate_boundary_values_and_expected(\n    const gsl::not_null<Generator*> generator,\n    const gsl::not_null<PrecomputationBox*> precomputation_box,\n    const gsl::not_null<ExpectedBox*> expected_box, const size_t l_max,\n    const size_t number_of_radial_grid_points) {\n  ComplexDataVector y = outer_product(\n      ComplexDataVector{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max), 1.0},\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(\n          number_of_radial_grid_points));\n\n  UniformCustomDistribution<double> dist(0.1, 1.0);\n  db::mutate<Tags::BoundaryValue<Tags::BondiR>,\n             Tags::BoundaryValue<Tags::DuRDividedByR>, Tags::BondiR,\n             Tags::DuRDividedByR, Tags::OneMinusY, Tags::BondiJ, Tags::BondiK>(\n      [&generator, &dist, &l_max, &number_of_radial_grid_points, &y](\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              boundary_r,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              boundary_du_r_divided_by_r,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> r,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              du_r_divided_by_r,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              one_minus_y,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> k) {\n        get(*boundary_r).data() =\n            (10.0 +\n             std::complex<double>(1.0, 0.0) *\n                 make_with_random_values<DataVector>(\n                     generator, make_not_null(&dist),\n                     Spectral::Swsh::number_of_swsh_collocation_points(l_max)));\n        get(*boundary_du_r_divided_by_r).data() =\n            std::complex<double>(1.0, 0.0) *\n            make_with_random_values<DataVector>(\n                generator, make_not_null(&dist),\n                Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n        // prevent aliasing; some terms are nonlinear in R\n        Spectral::Swsh::filter_swsh_boundary_quantity(\n            make_not_null(&get(*boundary_r)), l_max, l_max - 3);\n        fill_with_n_copies(make_not_null(&get(*r).data()),\n                           get(*boundary_r).data(),\n                           number_of_radial_grid_points);\n        fill_with_n_copies(make_not_null(&get(*du_r_divided_by_r).data()),\n                           get(*boundary_du_r_divided_by_r).data(),\n                           number_of_radial_grid_points);\n        get(*one_minus_y).data() = 1.0 - y;\n        get(*j).data() = make_with_random_values<ComplexDataVector>(\n            generator, make_not_null(&dist),\n            number_of_radial_grid_points *\n                Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n        get(*k).data() = sqrt(1.0 + get(*j).data() * conj(get(*j).data()));\n      },\n      expected_box);\n\n  TestHelpers::CopyDataBoxTags<Tags::BoundaryValue<Tags::BondiR>,\n                               Tags::BoundaryValue<Tags::DuRDividedByR>,\n                               Tags::BondiJ>::apply(precomputation_box,\n                                                    *expected_box);\n\n  db::mutate<Tags::EthRDividedByR, Tags::EthEthbarRDividedByR,\n             Tags::EthEthRDividedByR>(\n      [&l_max, &number_of_radial_grid_points](\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 1>>*>\n              eth_r_divided_by_r,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              eth_ethbar_r_divided_by_r,\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n              eth_eth_r_divided_by_r,\n          const Scalar<SpinWeighted<ComplexDataVector, 0>>& r) {\n        SpinWeighted<ComplexDataVector, 0> r_buffer;\n        r_buffer = get(r);\n        get(*eth_r_divided_by_r) =\n            Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::Eth>(\n                l_max, number_of_radial_grid_points, r_buffer) /\n            (get(r));\n        get(*eth_ethbar_r_divided_by_r) =\n            Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::EthEthbar>(\n                l_max, number_of_radial_grid_points, r_buffer) /\n            (get(r));\n        get(*eth_eth_r_divided_by_r) =\n            Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::EthEth>(\n                l_max, number_of_radial_grid_points, r_buffer) /\n            (get(r));\n      },\n      expected_box, db::get<Tags::BondiR>(*expected_box));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.PrecomputeCceDependencies\",\n                  \"[Unit][Cce]\") {\n  // Initialize the Variables that we need\n  MAKE_GENERATOR(generator);\n  UniformCustomDistribution<size_t> sdist{10, 14};\n  const size_t l_max = sdist(generator);\n  const size_t number_of_radial_grid_points = sdist(generator) / 2;\n\n  using boundary_variables_tag =\n      ::Tags::Variables<pre_computation_boundary_tags<Tags::BoundaryValue>>;\n  using independent_of_integration_variables_tag = ::Tags::Variables<\n      tmpl::push_back<pre_computation_tags, Tags::DuRDividedByR>>;\n  using pre_swsh_derivatives_variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::BondiJ>>;\n\n  const size_t number_of_boundary_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_volume_points =\n      number_of_boundary_points * number_of_radial_grid_points;\n  auto precomputation_box = db::create<db::AddSimpleTags<\n      boundary_variables_tag, independent_of_integration_variables_tag,\n      pre_swsh_derivatives_variables_tag, Spectral::Swsh::Tags::LMax,\n      Spectral::Swsh::Tags::NumberOfRadialPoints>>(\n      typename boundary_variables_tag::type{number_of_boundary_points},\n      typename independent_of_integration_variables_tag::type{\n          number_of_volume_points},\n      typename pre_swsh_derivatives_variables_tag::type{\n          number_of_volume_points},\n      l_max, number_of_radial_grid_points);\n\n  auto expected_box =\n      db::create<db::AddSimpleTags<boundary_variables_tag,\n                                   independent_of_integration_variables_tag,\n                                   pre_swsh_derivatives_variables_tag>>(\n          typename boundary_variables_tag::type{number_of_boundary_points},\n          typename independent_of_integration_variables_tag::type{\n              number_of_volume_points},\n          typename pre_swsh_derivatives_variables_tag::type{\n              number_of_volume_points});\n\n  generate_boundary_values_and_expected(\n      make_not_null(&generator), make_not_null(&precomputation_box),\n      make_not_null(&expected_box), l_max, number_of_radial_grid_points);\n\n  mutate_all_precompute_cce_dependencies<Tags::BoundaryValue>(\n      make_not_null(&precomputation_box));\n  db::mutate_apply<\n      PrecomputeCceDependencies<Tags::BoundaryValue, Tags::DuRDividedByR>>(\n      make_not_null(&precomputation_box));\n\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  CHECK_VARIABLES_CUSTOM_APPROX(\n      db::get<boundary_variables_tag>(precomputation_box),\n      db::get<boundary_variables_tag>(expected_box), angular_derivative_approx);\n\n  CHECK_VARIABLES_CUSTOM_APPROX(\n      db::get<pre_swsh_derivatives_variables_tag>(precomputation_box),\n      db::get<pre_swsh_derivatives_variables_tag>(expected_box),\n      angular_derivative_approx);\n\n  CHECK_VARIABLES_CUSTOM_APPROX(\n      db::get<independent_of_integration_variables_tag>(precomputation_box),\n      db::get<independent_of_integration_variables_tag>(expected_box),\n      angular_derivative_approx);\n}\n\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_ScriPlusInterpolationManager.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"Evolution/Systems/Cce/ScriPlusInterpolationManager.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\nnamespace {\n\ntemplate <typename VectorType, bool test_serialization>\nvoid test_interpolate_quadratic() {\n  MAKE_GENERATOR(generator);\n  UniformCustomDistribution<double> value_dist{0.1, 1.0};\n  // more slowly varying functions to give the interpolator an easier job\n  const double linear_coefficient = value_dist(generator) * 0.1;\n  const double quadratic_coefficient = value_dist(generator) * 0.1;\n  const size_t vector_size = 5;\n  const size_t data_points = 40;\n\n  const auto random_vector = make_with_random_values<VectorType>(\n      make_not_null(&generator), make_not_null(&value_dist), vector_size);\n\n  UniformCustomDistribution<double> time_dist{-0.03, 0.03};\n\n  ScriPlusInterpolationManager<VectorType, ::Tags::TempScalar<0, VectorType>>\n      interpolation_manager{\n          4, vector_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(7u, 9u)};\n\n  VectorType comparison_lhs;\n  VectorType comparison_rhs;\n\n  Approx interpolation_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  // Construct data at a peculiar time set\n  //  f(u_bondi) = f_0 *(1.0 + a * u_bondi + b * u_bondi^2);\n  //  where u_bondi is recorded with a small random deviation away from the\n  //  actual time\n  // We use intervals of 0.1 for the sampling to give the interpolator an easier\n  // time\n  for (size_t i = 0; i < data_points; ++i) {\n    // this will give random times that are nonetheless guaranteed to be\n    // monotonically increasing\n    const auto time_vector = make_with_random_values<DataVector>(\n        make_not_null(&generator), make_not_null(&time_dist), vector_size);\n    interpolation_manager.insert_data(\n        time_vector + i * 0.1,\n        random_vector *\n            (1.0 + linear_coefficient * (i * 0.1 + time_vector) +\n             quadratic_coefficient * square(i * 0.1 + time_vector)));\n    // serialize the manager halfway through the process to make sure everything\n    // still works after serialization; it should only be necessary to serialize\n    // once to confirm the correct behavior.\n    if (test_serialization and i == data_points / 2) {\n      interpolation_manager = serialize_and_deserialize(interpolation_manager);\n    }\n\n    // only demand accuracy when the interpolation is reasonable\n    if (i > 3 and i < data_points - 5) {\n      interpolation_manager.insert_target_time(i * 0.1);\n    }\n    while (interpolation_manager.first_time_is_ready_to_interpolate()) {\n      const auto interpolation_result =\n          interpolation_manager.interpolate_and_pop_first_time();\n      comparison_lhs = interpolation_result.second;\n      comparison_rhs =\n          random_vector *\n          (1.0 + linear_coefficient * interpolation_result.first +\n           quadratic_coefficient * square(interpolation_result.first));\n      CHECK_ITERABLE_CUSTOM_APPROX(comparison_lhs, comparison_rhs,\n                                   interpolation_approx);\n    }\n    // the culling of the data means that the interpolation manager should never\n    // have too many more points than it needs to get a good interpolation.\n    CHECK(interpolation_manager.number_of_data_points() < 12);\n  }\n  while (interpolation_manager.first_time_is_ready_to_interpolate()) {\n    auto interpolation_result =\n        interpolation_manager.interpolate_and_pop_first_time();\n    comparison_lhs = interpolation_result.second;\n    comparison_rhs =\n        random_vector *\n        (1.0 + linear_coefficient * interpolation_result.first +\n         quadratic_coefficient * square(interpolation_result.first));\n    CHECK_ITERABLE_CUSTOM_APPROX(comparison_lhs, comparison_rhs,\n                                 interpolation_approx);\n  }\n  CHECK(interpolation_manager.number_of_target_times() == 0);\n\n  // check the multiplication version\n  ScriPlusInterpolationManager<\n      VectorType, ::Tags::Multiplies<::Tags::TempScalar<0, VectorType>,\n                                     ::Tags::TempScalar<1, VectorType>>>\n      multiplication_interpolation_manager{\n          4, vector_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(7u, 9u)};\n\n  const auto multiplies_random_vector =\n      make_with_random_values<VectorType>(\n          make_not_null(&generator), make_not_null(&value_dist), vector_size);\n\n  for (size_t i = 0; i < data_points; ++i) {\n    // this will give random times that are nonetheless guaranteed to be\n    // monotonically increasing\n    const auto time_vector = make_with_random_values<DataVector>(\n        make_not_null(&generator), make_not_null(&time_dist), vector_size);\n    multiplication_interpolation_manager.insert_data(\n        time_vector + i * 0.1,\n        random_vector * (1.0 + linear_coefficient * (i * 0.1 + time_vector) +\n                         quadratic_coefficient * square(i * 0.1 + time_vector)),\n        multiplies_random_vector *\n            (1.0 + linear_coefficient * (i * 0.1 + time_vector) +\n             quadratic_coefficient * square(i * 0.1 + time_vector)));\n\n    // only demand accuracy when the interpolation is reasonable\n    if (i > 3 and i < data_points - 5) {\n      multiplication_interpolation_manager.insert_target_time(i * 0.1);\n    }\n    while (multiplication_interpolation_manager\n               .first_time_is_ready_to_interpolate()) {\n      const auto interpolation_result =\n          multiplication_interpolation_manager.interpolate_and_pop_first_time();\n      comparison_lhs = interpolation_result.second;\n      comparison_rhs =\n          random_vector * multiplies_random_vector *\n          square(1.0 + linear_coefficient * interpolation_result.first +\n                 quadratic_coefficient * square(interpolation_result.first));\n      CHECK_ITERABLE_CUSTOM_APPROX(comparison_lhs, comparison_rhs,\n                                   interpolation_approx);\n    }\n    // the culling of the data means that the interpolation manager should never\n    // have too many more points than it needs to get a good interpolation.\n    CHECK(multiplication_interpolation_manager.number_of_data_points() < 12);\n  }\n  while (multiplication_interpolation_manager\n             .first_time_is_ready_to_interpolate()) {\n    const auto interpolation_result =\n        multiplication_interpolation_manager.interpolate_and_pop_first_time();\n    comparison_lhs = interpolation_result.second;\n    comparison_rhs =\n        random_vector * multiplies_random_vector *\n        square(1.0 + linear_coefficient * interpolation_result.first +\n               quadratic_coefficient * square(interpolation_result.first));\n    CHECK_ITERABLE_CUSTOM_APPROX(comparison_lhs, comparison_rhs,\n                                 interpolation_approx);\n  }\n  CHECK(multiplication_interpolation_manager.number_of_target_times() == 0);\n\n  // test the time derivative version of the interpolation manager\n  ScriPlusInterpolationManager<VectorType,\n                               Tags::Du<::Tags::TempScalar<0, VectorType>>>\n      derivative_interpolation_manager{\n          4, vector_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(7u, 9u)};\n\n  // this test is a bit more demanding for the interpolator, so we multiply the\n  // time scale of the sampling by another 0.1\n  for (size_t i = 0; i < data_points; ++i) {\n    // this will give random times that are nonetheless guaranteed to be\n    // monotonically increasing\n    const DataVector time_vector =\n        make_with_random_values<DataVector>(\n            make_not_null(&generator), make_not_null(&time_dist), vector_size) *\n        0.1;\n    derivative_interpolation_manager.insert_data(\n        time_vector + i * 0.01,\n        random_vector *\n            (1.0 + linear_coefficient * (i * 0.01 + time_vector) +\n             quadratic_coefficient * square(i * 0.01 + time_vector)));\n\n    // only demand accuracy when the interpolation is reasonable\n    if (i > 3 and i < data_points - 5) {\n      derivative_interpolation_manager.insert_target_time(i * 0.01);\n    }\n    while (\n        derivative_interpolation_manager.first_time_is_ready_to_interpolate()) {\n      const auto interpolation_result =\n          derivative_interpolation_manager.interpolate_and_pop_first_time();\n      comparison_lhs = interpolation_result.second;\n      comparison_rhs =\n          random_vector * (linear_coefficient + 2.0 * quadratic_coefficient *\n                                                    interpolation_result.first);\n      CHECK_ITERABLE_CUSTOM_APPROX(comparison_lhs, comparison_rhs,\n                                   interpolation_approx);\n    }\n    // the culling of the data means that the interpolation manager should never\n    // have too many more points than it needs to get a good interpolation.\n    CHECK(derivative_interpolation_manager.number_of_data_points() < 12);\n  }\n  while (\n      derivative_interpolation_manager.first_time_is_ready_to_interpolate()) {\n    const auto interpolation_result =\n        derivative_interpolation_manager.interpolate_and_pop_first_time();\n    comparison_lhs = interpolation_result.second;\n    comparison_rhs =\n        random_vector * (linear_coefficient + 2.0 * quadratic_coefficient *\n                                                  interpolation_result.first);\n    CHECK_ITERABLE_CUSTOM_APPROX(comparison_lhs, comparison_rhs,\n                                 interpolation_approx);\n  }\n  CHECK(derivative_interpolation_manager.number_of_target_times() == 0);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.ScriPlusInterpolationManager\",\n                  \"[Unit][Evolution]\") {\n  register_derived_classes_with_charm<intrp::SpanInterpolator>();\n  test_interpolate_quadratic<DataVector, false>();\n  test_interpolate_quadratic<ComplexDataVector, false>();\n  test_interpolate_quadratic<DataVector, true>();\n  test_interpolate_quadratic<ComplexDataVector, true>();\n}\n}  // namespace\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_ScriPlusValues.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/ScriPlusValues.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nnamespace Cce {\n\nnamespace {\n\ntemplate <size_t FixedLMax, size_t FixedNumberOfRadialPoints, typename Mutator,\n          typename ReturnType, typename ArgumentTypeList>\nstruct WrapScriPlusComputationImpl;\n\ntemplate <size_t FixedLMax, size_t FixedNumberOfRadialPoints, typename Mutator,\n          typename ReturnType, typename... Arguments>\nstruct WrapScriPlusComputationImpl<FixedLMax, FixedNumberOfRadialPoints,\n                                   Mutator, ReturnType,\n                                   tmpl::list<Arguments...>> {\n  static void apply(const gsl::not_null<ReturnType*> pass_by_pointer,\n                    const Arguments&... arguments) {\n    Mutator::apply(pass_by_pointer, arguments..., FixedLMax,\n                   FixedNumberOfRadialPoints);\n  }\n};\n\ntemplate <size_t FixedLMax, size_t FixedNumberOfRadialPoints, typename Mutator>\nusing WrapScriPlusComputation = WrapScriPlusComputationImpl<\n    FixedLMax, FixedNumberOfRadialPoints, Mutator,\n    typename tmpl::front<typename Mutator::return_tags>::type,\n    tmpl::transform<typename Mutator::tensor_argument_tags,\n                    tmpl::bind<tmpl::type_from, tmpl::_1>>>;\n\nvoid pypp_test_scri_plus_computation_steps() {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"Evolution/Systems/Cce/\"};\n\n  constexpr size_t l_max = 3;\n  constexpr size_t number_of_radial_points = 1;\n\n  pypp::check_with_random_values<1>(\n      &WrapScriPlusComputation<l_max, number_of_radial_points,\n                               CalculateScriPlusValue<Tags::News>>::apply,\n      \"ScriPlusValues\", {\"news\"}, {{{0.1, 1.0}}},\n      DataVector{Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n\n  pypp::check_with_random_values<1>(\n      &WrapScriPlusComputation<l_max, number_of_radial_points,\n                               CalculateScriPlusValue<Tags::TimeIntegral<\n                                   Tags::ScriPlus<Tags::Psi4>>>>::apply,\n      \"ScriPlusValues\", {\"time_integral_psi_4\"}, {{{0.1, 1.0}}},\n      DataVector{Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n\n  pypp::check_with_random_values<1>(\n      &WrapScriPlusComputation<\n          l_max, number_of_radial_points,\n          CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi3>>>::apply,\n      \"ScriPlusValues\", {\"psi_3\"}, {{{0.1, 1.0}}},\n      DataVector{Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n\n  pypp::check_with_random_values<1>(\n      &WrapScriPlusComputation<\n          l_max, number_of_radial_points,\n          CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi2>>>::apply,\n      \"ScriPlusValues\", {\"psi_2\"}, {{{0.1, 1.0}}},\n      DataVector{Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n\n  pypp::check_with_random_values<1>(\n      &WrapScriPlusComputation<\n          l_max, number_of_radial_points,\n          CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi1>>>::apply,\n      \"ScriPlusValues\", {\"psi_1\"}, {{{0.1, 1.0}}},\n      DataVector{Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n\n  pypp::check_with_random_values<1>(\n      &WrapScriPlusComputation<\n          l_max, number_of_radial_points,\n          CalculateScriPlusValue<Tags::ScriPlus<Tags::Psi0>>>::apply,\n      \"ScriPlusValues\", {\"psi_0\"}, {{{0.1, 1.0}}},\n      DataVector{Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n\n  pypp::check_with_random_values<1>(\n      &WrapScriPlusComputation<\n          l_max, number_of_radial_points,\n          CalculateScriPlusValue<Tags::ScriPlus<Tags::Strain>>>::apply,\n      \"ScriPlusValues\", {\"strain\"}, {{{0.1, 1.0}}},\n      DataVector{Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n\n  pypp::check_with_random_values<1>(\n      &WrapScriPlusComputation<\n          l_max, number_of_radial_points,\n          CalculateScriPlusValue<Tags::ScriPlus<Tags::KleinGordonPsi>>>::apply,\n      \"ScriPlusValues\", {\"klein_gordon_psi\"}, {{{0.1, 1.0}}},\n      DataVector{Spectral::Swsh::number_of_swsh_collocation_points(l_max)});\n}\n\nvoid check_inertial_retarded_time_utilities() {\n  MAKE_GENERATOR(gen);\n\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  UniformCustomDistribution<size_t> l_dist(12, 18);\n  const size_t l_max = l_dist(gen);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_radial_points = 5;\n\n  auto time_box = db::create<db::AddSimpleTags<\n      Tags::LMax, Tags::InertialRetardedTime, Tags::ComplexInertialRetardedTime,\n      Tags::EthInertialRetardedTime, Tags::Exp2Beta,\n      ::Tags::dt<Tags::InertialRetardedTime>>>(\n           l_max,\n      Scalar<DataVector>{number_of_angular_points},\n      Scalar<SpinWeighted<ComplexDataVector, 0>>{number_of_angular_points},\n      Scalar<SpinWeighted<ComplexDataVector, 1>>{number_of_angular_points},\n      Scalar<SpinWeighted<ComplexDataVector, 0>>{number_of_angular_points *\n                                                 number_of_radial_points},\n      Scalar<DataVector>{number_of_angular_points});\n\n  db::mutate<Tags::Exp2Beta>(\n      [&gen, &value_dist](\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              exp_2_beta) {\n        fill_with_random_values(make_not_null(&get(*exp_2_beta).data()),\n                                make_not_null(&gen),\n                                make_not_null(&value_dist));\n      },\n      make_not_null(&time_box));\n  const double random_time = value_dist(gen);\n\n  db::mutate_apply<InitializeScriPlusValue<Tags::InertialRetardedTime>>(\n      make_not_null(&time_box), random_time);\n\n  for (auto val : get(db::get<Tags::InertialRetardedTime>(time_box))) {\n    CHECK(val == random_time);\n  }\n  db::mutate_apply<PreSwshDerivatives<Tags::ComplexInertialRetardedTime>>(\n      make_not_null(&time_box));\n\n  const std::complex<double> complex_random_time{random_time, 0.0};\n  for (auto val :\n       get(db::get<Tags::ComplexInertialRetardedTime>(time_box)).data()) {\n    CHECK(val == complex_random_time);\n  }\n\n  db::mutate_apply<\n      CalculateScriPlusValue<::Tags::dt<Tags::InertialRetardedTime>>>(\n      make_not_null(&time_box));\n\n  for (size_t i = 0; i < number_of_angular_points; ++i) {\n    CHECK(get(db::get<::Tags::dt<Tags::InertialRetardedTime>>(time_box))[i] ==\n          real(get(db::get<Tags::Exp2Beta>(time_box))\n                   .data()[i + number_of_angular_points *\n                                   (number_of_radial_points - 1)]));\n  }\n\n  const double random_time_delta = 0.1 * value_dist(gen);\n  const ComplexDataVector expected_retarded_time_intermediate_value =\n      std::complex<double>(1.0, 0.0) *\n      get(db::get<::Tags::dt<Tags::InertialRetardedTime>>(time_box));\n  auto expected_eth_retarded_time =\n      Spectral::Swsh::angular_derivative<Spectral::Swsh::Tags::Eth>(\n          l_max, 1,\n          SpinWeighted<ComplexDataVector, 0>(\n              expected_retarded_time_intermediate_value));\n  expected_eth_retarded_time.data() *= random_time_delta;\n  db::mutate<Tags::ComplexInertialRetardedTime>(\n      [&random_time_delta](\n          const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n              complex_retarded_time,\n          const Scalar<DataVector>& dt_inertial_time) {\n        get(*complex_retarded_time) = std::complex<double>(1.0, 0.0) *\n                                      random_time_delta * get(dt_inertial_time);\n      },\n      make_not_null(&time_box),\n      db::get<::Tags::dt<Tags::InertialRetardedTime>>(time_box));\n  db::mutate_apply<CalculateScriPlusValue<Tags::EthInertialRetardedTime>>(\n      make_not_null(&time_box));\n  CHECK_ITERABLE_APPROX(expected_eth_retarded_time,\n                        get(db::get<Tags::EthInertialRetardedTime>(time_box)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.ScriPlusValues\",\n                  \"[Unit][Evolution]\") {\n  pypp_test_scri_plus_computation_steps();\n\n  check_inertial_retarded_time_utilities();\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_SwshDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Evolution/Systems/Cce/SwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/CceComputationTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\n\nnamespace {\ntemplate <int Spin>\nstruct TestSpinWeightedScalar : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Spin>>;\n};\n\n// note: J must be omitted from this due to the need for the Precomputation step\n// in this test that was able to be omitted from the PreSwshDerivatives\n// test.\nusing test_swsh_derivative_dependencies =\n    tmpl::list<Tags::BondiBeta, Tags::BondiQ, Tags::BondiU>;\n}  // namespace\n\nnamespace detail {\ntemplate <>\nstruct TagsToComputeForImpl<TestSpinWeightedScalar<0>> {\n  using pre_swsh_derivative_tags =\n      tmpl::list<Tags::Dy<Tags::BondiJ>, Tags::Dy<Tags::Dy<Tags::BondiJ>>,\n                 Tags::Dy<Tags::BondiBeta>, Tags::Dy<Tags::Dy<Tags::BondiBeta>>,\n                 Tags::Dy<Tags::BondiU>, Tags::Dy<Tags::Dy<Tags::BondiU>>,\n                 Tags::Dy<Tags::BondiQ>, Tags::Dy<Tags::Dy<Tags::BondiQ>>>;\n  using second_swsh_derivative_tags = tmpl::list<>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiBeta>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::EthEth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::EthEthbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::Dy<Tags::BondiJ>,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::EthbarEthbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                       Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiQ, Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU,\n                                       Spectral::Swsh::Tags::Eth>>;\n};\n\ntemplate <>\nstruct TagsToComputeForImpl<TestSpinWeightedScalar<1>> {\n  using pre_swsh_derivative_tags = tmpl::list<\n      Tags::BondiJbar, Tags::BondiUbar, Tags::BondiQbar,\n      ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n      Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>,\n      Tags::Dy<Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>>,\n      ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>,\n      ::Tags::Multiplies<Tags::BondiUbar, Tags::Dy<Tags::BondiJ>>,\n      Tags::Dy<Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                                Spectral::Swsh::Tags::Ethbar>>,\n      Tags::Dy<::Tags::Multiplies<Tags::BondiUbar, Tags::Dy<Tags::BondiJ>>>,\n      Tags::Dy<::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>>>;\n  using second_swsh_derivative_tags =\n      tmpl::list<Spectral::Swsh::Tags::Derivative<\n          Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                           Spectral::Swsh::Tags::Ethbar>,\n          Spectral::Swsh::Tags::Eth>>;\n  using swsh_derivative_tags = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>,\n          Spectral::Swsh::Tags::Ethbar>,\n      Spectral::Swsh::Tags::Derivative<\n          Tags::Dy<::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJ, Tags::BondiJbar>,\n          Spectral::Swsh::Tags::EthEthbar>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiUbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<\n          ::Tags::Multiplies<Tags::BondiJbar, Tags::Dy<Tags::BondiJ>>,\n          Spectral::Swsh::Tags::Ethbar>>;\n};\n}  // namespace detail\n\nnamespace {\nstruct GenerateStartingData {\n  template <typename Generator, typename Distribution>\n  void operator()(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          boundary_r,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n          boundary_du_r_divided_by_r,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*>\n          angular_collocations_for_j,\n      const gsl::not_null<Scalar<ComplexModalVector>*> radial_polynomials_for_j,\n      const gsl::not_null<Generator*> generator,\n      const gsl::not_null<Distribution*> dist, const size_t l_max,\n      const size_t number_of_radial_grid_points, const ComplexDataVector& y) {\n    get(*boundary_r).data() =\n        (10.0 +\n         std::complex<double>(1.0, 0.0) *\n             make_with_random_values<DataVector>(\n                 generator, dist,\n                 Spectral::Swsh::number_of_swsh_collocation_points(l_max)));\n    get(*boundary_du_r_divided_by_r).data() =\n        std::complex<double>(1.0, 0.0) *\n        make_with_random_values<DataVector>(\n            generator, dist,\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n    // prevent aliasing; some terms are nonlinear in R\n    Spectral::Swsh::filter_swsh_boundary_quantity(\n        make_not_null(&get(*boundary_r)), l_max, 2);\n    Spectral::Swsh::filter_swsh_boundary_quantity(\n        make_not_null(&get(*boundary_du_r_divided_by_r)), l_max, 2);\n    // generate the separable j needed for precomputation step\n    get(*angular_collocations_for_j).data() =\n        make_with_random_values<ComplexDataVector>(\n            generator, dist,\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n    Spectral::Swsh::filter_swsh_boundary_quantity(\n        make_not_null(&get(*angular_collocations_for_j)), l_max, 2);\n    get(*radial_polynomials_for_j) =\n        make_with_random_values<ComplexModalVector>(\n            generator, dist, number_of_radial_grid_points);\n    for (size_t i = 0; i < number_of_radial_grid_points; ++i) {\n      get(*radial_polynomials_for_j)[i] *=\n          exp(-10.0 *\n              pow<2>(static_cast<double>(i) /\n                     static_cast<double>(number_of_radial_grid_points - 1)));\n      if (i > 3) {\n        get(*radial_polynomials_for_j)[i] = 0.0;\n      }\n    }\n    ComplexDataVector one_divided_by_r =\n        (1.0 - y) /\n        (2.0 * create_vector_of_n_copies(get(*boundary_r).data(),\n                                         number_of_radial_grid_points));\n    TestHelpers::generate_volume_data_from_separated_values(\n        make_not_null(&get(*j).data()), make_not_null(&one_divided_by_r),\n        get(*angular_collocations_for_j).data(), get(*radial_polynomials_for_j),\n        l_max, number_of_radial_grid_points);\n  }\n};\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.SwshDerivatives\", \"[Unit][Cce]\") {\n  MAKE_GENERATOR(generator);\n  const size_t l_max = 10;\n  const size_t number_of_radial_grid_points = 7;\n  const size_t number_of_grid_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n      number_of_radial_grid_points;\n\n  using pre_swsh_derivative_tag_list = tmpl::append<\n      pre_swsh_derivative_tags_to_compute_for_t<TestSpinWeightedScalar<0>>,\n      pre_swsh_derivative_tags_to_compute_for_t<TestSpinWeightedScalar<1>>,\n      test_swsh_derivative_dependencies, tmpl::list<Tags::BondiJ>>;\n\n  using swsh_derivative_tag_list = tmpl::remove_duplicates<tmpl::append<\n      single_swsh_derivative_tags_to_compute_for_t<TestSpinWeightedScalar<0>>,\n      second_swsh_derivative_tags_to_compute_for_t<TestSpinWeightedScalar<0>>,\n      single_swsh_derivative_tags_to_compute_for_t<TestSpinWeightedScalar<1>>,\n      second_swsh_derivative_tags_to_compute_for_t<TestSpinWeightedScalar<1>>>>;\n\n  using boundary_value_variables_tag =\n      ::Tags::Variables<pre_computation_boundary_tags<Tags::BoundaryValue>>;\n  using integration_independent_variables_tag = ::Tags::Variables<\n      tmpl::push_back<pre_computation_tags, Tags::DuRDividedByR>>;\n  using pre_swsh_derivatives_variables_tag =\n      ::Tags::Variables<pre_swsh_derivative_tag_list>;\n  using swsh_derivatives_variables_tag =\n      ::Tags::Variables<swsh_derivative_tag_list>;\n  using swsh_derivatives_coefficient_buffer_variables_tag =\n      ::Tags::Variables<tmpl::remove_duplicates<tmpl::flatten<tmpl::transform<\n          swsh_derivative_tag_list,\n          tmpl::bind<Spectral::Swsh::coefficient_buffer_tags_for_derivative_tag,\n                     tmpl::_1>>>>>;\n  using separated_angular_data_variables_tag = ::Tags::Variables<\n      db::wrap_tags_in<TestHelpers::AngularCollocationsFor,\n                       tmpl::append<pre_swsh_derivative_tag_list,\n                                    swsh_derivative_tag_list>>>;\n  using separated_radial_modes_variables_tag = ::Tags::Variables<\n      db::wrap_tags_in<TestHelpers::RadialPolyCoefficientsFor,\n                       tmpl::append<pre_swsh_derivative_tag_list,\n                                    swsh_derivative_tag_list>>>;\n\n  auto expected_box = db::create<db::AddSimpleTags<\n      Tags::LMax, Tags::NumberOfRadialPoints, boundary_value_variables_tag,\n      integration_independent_variables_tag, pre_swsh_derivatives_variables_tag,\n      swsh_derivatives_variables_tag, separated_angular_data_variables_tag,\n      separated_radial_modes_variables_tag>>(\n      l_max, number_of_radial_grid_points,\n      typename boundary_value_variables_tag::type{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max), 0.0},\n      typename integration_independent_variables_tag::type{\n          number_of_grid_points, 0.0},\n      typename pre_swsh_derivatives_variables_tag::type{number_of_grid_points},\n      typename swsh_derivatives_variables_tag::type{number_of_grid_points, 0.0},\n      typename separated_angular_data_variables_tag::type{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max)},\n      typename separated_radial_modes_variables_tag::type{\n          number_of_radial_grid_points});\n\n  // generate necessary boundary and integration-independent data for the rest\n  // of the computation\n  UniformCustomDistribution<double> dist(0.1, 1.0);\n  const ComplexDataVector y = outer_product(\n      ComplexDataVector{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max), 1.0},\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(\n          number_of_radial_grid_points));\n\n  db::mutate<Tags::BoundaryValue<Tags::BondiR>,\n             Tags::BoundaryValue<Tags::DuRDividedByR>, Tags::BondiJ,\n             TestHelpers::AngularCollocationsFor<Tags::BondiJ>,\n             TestHelpers::RadialPolyCoefficientsFor<Tags::BondiJ>>(\n      GenerateStartingData{}, make_not_null(&expected_box),\n      make_not_null(&generator), make_not_null(&dist), l_max,\n      number_of_radial_grid_points, y);\n\n  // apply the separable computation for all of the pre_swsh_derivatives and\n  // swsh_derivative quantities\n  db::mutate<pre_swsh_derivatives_variables_tag, swsh_derivatives_variables_tag,\n             separated_angular_data_variables_tag,\n             separated_radial_modes_variables_tag>(\n      [&generator](\n          const gsl::not_null<\n              typename pre_swsh_derivatives_variables_tag::type*>\n              pre_swsh_derivatives,\n          const gsl::not_null<typename swsh_derivatives_variables_tag::type*>\n              swsh_derivatives,\n          const gsl::not_null<\n              typename separated_angular_data_variables_tag::type*>\n              separated_angular_data,\n          const gsl::not_null<\n              typename separated_radial_modes_variables_tag::type*>\n              separated_radial_modes,\n          const SpinWeighted<ComplexDataVector, 0>& boundary_r) {\n        TestHelpers::generate_separable_expected<\n            test_swsh_derivative_dependencies,\n            tmpl::list<TestSpinWeightedScalar<0>, TestSpinWeightedScalar<1>>>(\n            pre_swsh_derivatives, swsh_derivatives, separated_angular_data,\n            separated_radial_modes, make_not_null(&generator), boundary_r,\n            l_max, number_of_radial_grid_points);\n      },\n      make_not_null(&expected_box),\n      get(db::get<Tags::BoundaryValue<Tags::BondiR>>(expected_box)));\n\n  auto computation_box = db::create<db::AddSimpleTags<\n      Tags::LMax, Tags::NumberOfRadialPoints, Tags::Integrand<Tags::BondiBeta>,\n      Tags::Integrand<Tags::BondiU>, boundary_value_variables_tag,\n      integration_independent_variables_tag, pre_swsh_derivatives_variables_tag,\n      swsh_derivatives_variables_tag,\n      swsh_derivatives_coefficient_buffer_variables_tag>>(\n      l_max, number_of_radial_grid_points,\n      db::get<Tags::Dy<Tags::BondiBeta>>(expected_box),\n      db::get<Tags::Dy<Tags::BondiU>>(expected_box),\n      typename boundary_value_variables_tag::type{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max), 0.0},\n      typename integration_independent_variables_tag::type{\n          number_of_grid_points, 0.0},\n      typename pre_swsh_derivatives_variables_tag::type{number_of_grid_points,\n                                                        0.0},\n      typename swsh_derivatives_variables_tag::type{number_of_grid_points, 0.0},\n      typename swsh_derivatives_coefficient_buffer_variables_tag::type{\n          Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max) *\n              number_of_radial_grid_points,\n          0.0});\n\n  TestHelpers::CopyDataBoxTags<\n      Tags::BoundaryValue<Tags::BondiR>,\n      Tags::BoundaryValue<Tags::DuRDividedByR>,\n      Tags::BondiJ>::apply(make_not_null(&computation_box), expected_box);\n\n  mutate_all_precompute_cce_dependencies<Tags::BoundaryValue>(\n      make_not_null(&computation_box));\n  mutate_all_precompute_cce_dependencies<Tags::BoundaryValue>(\n      make_not_null(&expected_box));\n\n  // duplicate the 'input' values to the computation box\n  TestHelpers::CopyDataBoxTags<\n      Tags::BondiBeta, Tags::BondiJ, Tags::BondiQ,\n      Tags::BondiU>::apply(make_not_null(&computation_box), expected_box);\n\n  mutate_all_pre_swsh_derivatives_for_tag<TestSpinWeightedScalar<0>>(\n      make_not_null(&computation_box));\n\n  mutate_all_swsh_derivatives_for_tag<TestSpinWeightedScalar<0>>(\n      make_not_null(&computation_box));\n\n  mutate_all_pre_swsh_derivatives_for_tag<TestSpinWeightedScalar<1>>(\n      make_not_null(&computation_box));\n\n  mutate_all_swsh_derivatives_for_tag<TestSpinWeightedScalar<1>>(\n      make_not_null(&computation_box));\n\n  // Test that manual SwshDerivs agree with the automatic ones\n  using manual_swsh_derivs_to_check = tmpl::list<\n      Tags::SwshDerivativeCompute<Tags::BondiBeta, Spectral::Swsh::Tags::Eth>,\n      Tags::SwshDerivativeCompute<Tags::BondiU, Spectral::Swsh::Tags::Eth>,\n      Tags::SwshDerivativeCompute<Tags::BondiJ, Spectral::Swsh::Tags::Ethbar>>;\n\n  using manual_swsh_derivs_base = tmpl::list<\n      Spectral::Swsh::Tags::Derivative<Tags::BondiBeta,\n                                       Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiU, Spectral::Swsh::Tags::Eth>,\n      Spectral::Swsh::Tags::Derivative<Tags::BondiJ,\n                                       Spectral::Swsh::Tags::Ethbar>>;\n\n  auto manual_swsh_deriv_box = db::create<\n      db::AddSimpleTags<Tags::LMax, Tags::NumberOfRadialPoints, Tags::OneMinusY,\n                        Tags::EthRDividedByR,\n                        /* Ideally we would get Tags::BondBeta... from\n                         * manual_swsh_derivs_to_check above, except then we\n                         * would need to be able to expand a parameter pack for\n                         * the arguments of db::create, and that would require\n                         * a helper; so instead we do it manually */\n                        Tags::BondiBeta, Tags::BondiU, Tags::BondiJ,\n                        Tags::Dy<Tags::BondiBeta>, Tags::Dy<Tags::BondiU>,\n                        Tags::Dy<Tags::BondiJ>>,\n      db::AddComputeTags<manual_swsh_derivs_to_check>>(\n      db::get<Tags::LMax>(computation_box),\n      db::get<Tags::NumberOfRadialPoints>(computation_box),\n      db::get<Tags::OneMinusY>(computation_box),\n      db::get<Tags::EthRDividedByR>(computation_box),\n      db::get<Tags::BondiBeta>(computation_box),\n      db::get<Tags::BondiU>(computation_box),\n      db::get<Tags::BondiJ>(computation_box),\n      db::get<Tags::Dy<Tags::BondiBeta>>(computation_box),\n      db::get<Tags::Dy<Tags::BondiU>>(computation_box),\n      db::get<Tags::Dy<Tags::BondiJ>>(computation_box));\n\n  // this can be tightened at the cost of needing a higher resolution due to the\n  // inherent aliasing in this system. A loose approx allows the test to be\n  // (relatively) fast\n  Approx loose_cce_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e8)\n          .scale(1.0);\n\n  CHECK_VARIABLES_CUSTOM_APPROX(\n      db::get<swsh_derivatives_variables_tag>(computation_box),\n      db::get<swsh_derivatives_variables_tag>(expected_box), loose_cce_approx);\n\n  tmpl::for_each<manual_swsh_derivs_base>(\n      [&manual_swsh_deriv_box, &computation_box,\n       &loose_cce_approx]<typename TagToTest>(tmpl::type_<TagToTest> /*meta*/) {\n        CHECK_ITERABLE_CUSTOM_APPROX(db::get<TagToTest>(manual_swsh_deriv_box),\n                                     db::get<TagToTest>(computation_box),\n                                     loose_cce_approx);\n      });\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nclass ComplexDataVector;\nnamespace {\nstruct SomeTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 0>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.Tags\", \"[Unit][Cce]\") {\n  TestHelpers::db::test_simple_tag<Cce::Tags::EndTime>(\"EndTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::StartTime>(\"StartTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiBeta>(\"BondiBeta\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiH>(\"H\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiJ>(\"J\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiJbar>(\"Jbar\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiK>(\"K\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiQ>(\"Q\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiQbar>(\"Qbar\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiU>(\"U\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiUAtScri>(\"BondiUAtScri\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiUbar>(\"Ubar\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiW>(\"W\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiJCauchyView>(\n      \"BondiJCauchyView\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::PartiallyFlatGaugeC>(\n      \"PartiallyFlatGaugeC\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::CauchyGaugeC>(\"CauchyGaugeC\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::PartiallyFlatGaugeD>(\n      \"PartiallyFlatGaugeD\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::CauchyGaugeD>(\"CauchyGaugeD\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::PartiallyFlatGaugeOmega>(\n      \"PartiallyFlatGaugeOmega\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::CauchyGaugeOmega>(\n      \"CauchyGaugeOmega\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::News>(\"News\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::CauchyAngularCoords>(\n      \"CauchyAngularCoords\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::PartiallyFlatAngularCoords>(\n      \"PartiallyFlatAngularCoords\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::CauchyCartesianCoords>(\n      \"CauchyCartesianCoords\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::PartiallyFlatCartesianCoords>(\n      \"PartiallyFlatCartesianCoords\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::InertialRetardedTime>(\n      \"InertialRetardedTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::ComplexInertialRetardedTime>(\n      \"ComplexInertialRetardedTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::EthInertialRetardedTime>(\n      \"EthInertialRetardedTime\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::OneMinusY>(\"OneMinusY\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::DuR>(\"DuR\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::DuRDividedByR>(\"DuRDividedByR\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::EthRDividedByR>(\"EthRDividedByR\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::EthEthRDividedByR>(\n      \"EthEthRDividedByR\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::EthEthbarRDividedByR>(\n      \"EthEthbarRDividedByR\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::Exp2Beta>(\"Exp2Beta\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::JbarQMinus2EthBeta>(\n      \"JbarQMinus2EthBeta\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::BondiR>(\"R\");\n\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseAlpha>(\n      \"NewmanPenroseAlpha\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseBeta>(\n      \"NewmanPenroseBeta\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseGamma>(\n      \"NewmanPenroseGamma\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseEpsilon>(\n      \"NewmanPenroseEpsilon\");\n  // In our choice of tetrad, \\kappa=0\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseTau>(\n      \"NewmanPenroseTau\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseSigma>(\n      \"NewmanPenroseSigma\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseRho>(\n      \"NewmanPenroseRho\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenrosePi>(\n      \"NewmanPenrosePi\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseNu>(\n      \"NewmanPenroseNu\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseMu>(\n      \"NewmanPenroseMu\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::NewmanPenroseLambda>(\n      \"NewmanPenroseLambda\");\n\n  TestHelpers::db::test_simple_tag<Cce::Tags::Psi0>(\"Psi0\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::Psi1>(\"Psi1\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::Psi2>(\"Psi2\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::Psi3>(\"Psi3\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::Psi4>(\"Psi4\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::Strain>(\"Strain\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::Psi0Match>(\"Psi0Match\");\n  TestHelpers::db::test_simple_tag<\n      Cce::Tags::InterpolationManager<ComplexDataVector, SomeTag>>(\n      \"InterpolationManager(SomeTag)\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::KleinGordonPsi>(\"KGPsi\");\n  TestHelpers::db::test_simple_tag<Cce::Tags::KleinGordonPi>(\"KGPi\");\n\n  TestHelpers::db::test_prefix_tag<::Tags::dt<Cce::Tags::BondiJ>>(\"H\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::Dy<SomeTag>>(\"Dy(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::Du<SomeTag>>(\"Du(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::Dr<SomeTag>>(\"Dr(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::Dlambda<SomeTag>>(\n      \"Dlambda(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::Integrand<SomeTag>>(\n      \"Integrand(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::BoundaryValue<SomeTag>>(\n      \"BoundaryValue(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<\n      Cce::Tags::EvolutionGaugeBoundaryValue<SomeTag>>(\n      \"EvolutionGaugeBoundaryValue(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::PoleOfIntegrand<SomeTag>>(\n      \"PoleOfIntegrand(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::RegularIntegrand<SomeTag>>(\n      \"RegularIntegrand(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::LinearFactor<SomeTag>>(\n      \"LinearFactor(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<\n      Cce::Tags::LinearFactorForConjugate<SomeTag>>(\n      \"LinearFactorForConjugate(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::TimeIntegral<SomeTag>>(\n      \"TimeIntegral(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::ScriPlus<SomeTag>>(\n      \"ScriPlus(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::ScriPlusFactor<SomeTag>>(\n      \"ScriPlusFactor(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<Cce::Tags::KleinGordonSource<SomeTag>>(\n      \"KleinGordonSource(SomeTag)\");\n  TestHelpers::db::test_prefix_tag<::Tags::dt<Cce::Tags::KleinGordonPsi>>(\n      \"KGPi\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Cce/Test_WorldtubeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeDataManager.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeModeRecorder.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/WriteToWorldtubeH5.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n\ntemplate <typename T>\nclass DummyBufferUpdater  // NOLINT\n    : public WorldtubeBufferUpdater<cce_metric_input_tags<T>> {\n public:\n  DummyBufferUpdater()\n      : extraction_radius_{1.0},\n        coordinate_amplitude_{0.0},\n        coordinate_frequency_{0.0},\n        l_max_{0} {}\n  DummyBufferUpdater(DataVector time_buffer,\n                     const gr::Solutions::KerrSchild& solution,\n                     const std::optional<double> extraction_radius,\n                     const double coordinate_amplitude,\n                     const double coordinate_frequency, const size_t l_max,\n                     const bool apply_normalization_bug = false,\n                     const bool has_version_history = true)\n      : time_buffer_{std::move(time_buffer)},\n        solution_{solution},\n        extraction_radius_{extraction_radius},\n        coordinate_amplitude_{coordinate_amplitude},\n        coordinate_frequency_{coordinate_frequency},\n        l_max_{l_max},\n        apply_normalization_bug_{apply_normalization_bug},\n        has_version_history_{has_version_history} {}\n\n  // NOLINTNEXTLINE\n  WRAPPED_PUPable_decl_base_template(\n      WorldtubeBufferUpdater<cce_metric_input_tags<T>>, DummyBufferUpdater);\n\n  explicit DummyBufferUpdater(CkMigrateMessage* /*unused*/)\n      : extraction_radius_{1.0},\n        coordinate_amplitude_{0.0},\n        coordinate_frequency_{0.0},\n        l_max_{0} {}\n\n  double update_buffers_for_time(\n      const gsl::not_null<Variables<cce_metric_input_tags<T>>*> buffers,\n      const gsl::not_null<size_t*> time_span_start,\n      const gsl::not_null<size_t*> time_span_end, const double time,\n      const size_t /*l_max*/, const size_t interpolator_length,\n      const size_t buffer_depth,\n      const bool time_varies_fastest = true) const override {\n    if (*time_span_end > interpolator_length and\n        time_buffer_[*time_span_end - interpolator_length + 1] > time) {\n      // the next time an update will be required\n      return time_buffer_[*time_span_end - interpolator_length + 1];\n    }\n    // find the time spans that are needed\n    auto new_span_pair = detail::create_span_for_time_value(\n        time, buffer_depth, interpolator_length, 0, time_buffer_.size(),\n        time_buffer_);\n    *time_span_start = new_span_pair.first;\n    *time_span_end = new_span_pair.second;\n\n    const size_t size =\n        std::is_same_v<T, ComplexModalVector>\n            ? square(l_max_ + 1)\n            : Spectral::Swsh::number_of_swsh_collocation_points(l_max_);\n    tnsr::ii<T, 3> spatial_metric_coefficients{size};\n    tnsr::ii<T, 3> dt_spatial_metric_coefficients{size};\n    tnsr::ii<T, 3> dr_spatial_metric_coefficients{size};\n    tnsr::I<T, 3> shift_coefficients{size};\n    tnsr::I<T, 3> dt_shift_coefficients{size};\n    tnsr::I<T, 3> dr_shift_coefficients{size};\n    Scalar<T> lapse_coefficients{size};\n    Scalar<T> dt_lapse_coefficients{size};\n    Scalar<T> dr_lapse_coefficients{size};\n    for (size_t time_index = 0; time_index < *time_span_end - *time_span_start;\n         ++time_index) {\n      TestHelpers::create_fake_time_varying_data(\n          make_not_null(&spatial_metric_coefficients),\n          make_not_null(&dt_spatial_metric_coefficients),\n          make_not_null(&dr_spatial_metric_coefficients),\n          make_not_null(&shift_coefficients),\n          make_not_null(&dt_shift_coefficients),\n          make_not_null(&dr_shift_coefficients),\n          make_not_null(&lapse_coefficients),\n          make_not_null(&dt_lapse_coefficients),\n          make_not_null(&dr_lapse_coefficients), solution_,\n          extraction_radius_.value_or(default_extraction_radius_),\n          coordinate_amplitude_, coordinate_frequency_,\n          time_buffer_[time_index + *time_span_start], l_max_, true,\n          apply_normalization_bug_);\n\n      ASSERT(get(lapse_coefficients).size() == size,\n             \"Oh no... Expected = \" << size << \", got \"\n                                    << get(lapse_coefficients).size());\n\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(&get<Tags::detail::SpatialMetric<T>>(*buffers)),\n          spatial_metric_coefficients, time_index,\n          *time_span_end - *time_span_start, time_varies_fastest);\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(\n              &get<Tags::detail::Dr<Tags::detail::SpatialMetric<T>>>(*buffers)),\n          dr_spatial_metric_coefficients, time_index,\n          *time_span_end - *time_span_start, time_varies_fastest);\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(\n              &get<::Tags::dt<Tags::detail::SpatialMetric<T>>>(*buffers)),\n          dt_spatial_metric_coefficients, time_index,\n          *time_span_end - *time_span_start, time_varies_fastest);\n\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(&get<Tags::detail::Shift<T>>(*buffers)),\n          shift_coefficients, time_index, *time_span_end - *time_span_start,\n          time_varies_fastest);\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(\n              &get<Tags::detail::Dr<Tags::detail::Shift<T>>>(*buffers)),\n          dr_shift_coefficients, time_index, *time_span_end - *time_span_start,\n          time_varies_fastest);\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(&get<::Tags::dt<Tags::detail::Shift<T>>>(*buffers)),\n          dt_shift_coefficients, time_index, *time_span_end - *time_span_start,\n          time_varies_fastest);\n\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(&get<Tags::detail::Lapse<T>>(*buffers)),\n          lapse_coefficients, time_index, *time_span_end - *time_span_start,\n          time_varies_fastest);\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(\n              &get<Tags::detail::Dr<Tags::detail::Lapse<T>>>(*buffers)),\n          dr_lapse_coefficients, time_index, *time_span_end - *time_span_start,\n          time_varies_fastest);\n      update_tensor_buffer_with_tensor_at_time_index(\n          make_not_null(&get<::Tags::dt<Tags::detail::Lapse<T>>>(*buffers)),\n          dt_lapse_coefficients, time_index, *time_span_end - *time_span_start,\n          time_varies_fastest);\n    }\n    return time_buffer_[*time_span_end - interpolator_length + 1];\n  }\n\n  std::unique_ptr<WorldtubeBufferUpdater<cce_metric_input_tags<T>>> get_clone()\n      const override {\n    return std::make_unique<DummyBufferUpdater>(*this);\n  }\n\n  bool time_is_outside_range(const double time) const override {\n    return time < time_buffer_[0] or\n           time > time_buffer_[time_buffer_.size() - 1];\n  }\n\n  size_t get_l_max() const override { return l_max_; }\n\n  double get_extraction_radius() const override {\n    return extraction_radius_.value_or(default_extraction_radius_);\n  }\n\n  bool has_version_history() const override { return has_version_history_; }\n\n  DataVector& get_time_buffer() override { return time_buffer_; }\n\n  void pup(PUP::er& p) override {\n    p | time_buffer_;\n    p | solution_;\n    p | extraction_radius_;\n    p | coordinate_amplitude_;\n    p | coordinate_frequency_;\n    p | default_extraction_radius_;\n    p | l_max_;\n    p | apply_normalization_bug_;\n    p | has_version_history_;\n  }\n\n private:\n  template <typename... Structure>\n  void update_tensor_buffer_with_tensor_at_time_index(\n      const gsl::not_null<Tensor<Structure...>*> tensor_buffer,\n      const Tensor<Structure...>& tensor_at_time, const size_t time_index,\n      const size_t time_span_extent, const bool time_varies_fastest) const {\n    for (size_t i = 0; i < tensor_at_time.size(); ++i) {\n      for (size_t k = 0; k < tensor_at_time[i].size(); ++k) {\n        const size_t buffer_index =\n            time_varies_fastest ? time_index + k * time_span_extent\n                                : k + time_index * tensor_at_time[i].size();\n        (*tensor_buffer)[i][buffer_index] = tensor_at_time[i][k];\n      }\n    }\n  }\n\n  DataVector time_buffer_;\n  gr::Solutions::KerrSchild solution_;\n  std::optional<double> extraction_radius_;\n  double default_extraction_radius_ = 100.0;\n  double coordinate_amplitude_;\n  double coordinate_frequency_;\n  size_t l_max_;\n  bool apply_normalization_bug_ = false;\n  bool has_version_history_ = true;\n};\n\ntemplate <typename T>\nclass BondiBufferUpdater\n    : public WorldtubeBufferUpdater<tmpl::conditional_t<\n          std::is_same_v<T, ComplexModalVector>,\n          Tags::worldtube_boundary_tags_for_writing<\n              Spectral::Swsh::Tags::SwshTransform>,\n          Tags::worldtube_boundary_tags_for_writing<Tags::BoundaryValue>>> {\n public:\n  using tags_for_writing = tmpl::conditional_t<\n      std::is_same_v<T, ComplexModalVector>,\n      Tags::worldtube_boundary_tags_for_writing<\n          Spectral::Swsh::Tags::SwshTransform>,\n      Tags::worldtube_boundary_tags_for_writing<Tags::BoundaryValue>>;\n\n  BondiBufferUpdater() = default;\n  BondiBufferUpdater(DataVector time_buffer,\n                     const gr::Solutions::KerrSchild& solution,\n                     const std::optional<double> extraction_radius,\n                     const double coordinate_amplitude,\n                     const double coordinate_frequency, const size_t l_max,\n                     const bool /*unused*/ = false)\n      : time_buffer_{std::move(time_buffer)},\n        solution_{solution},\n        extraction_radius_{extraction_radius},\n        coordinate_amplitude_{coordinate_amplitude},\n        coordinate_frequency_{coordinate_frequency},\n        l_max_{l_max} {}\n\n  // NOLINTNEXTLINE\n  WRAPPED_PUPable_decl_base_template(WorldtubeBufferUpdater<tags_for_writing>,\n                                     BondiBufferUpdater);\n\n  explicit BondiBufferUpdater(CkMigrateMessage* /*unused*/) {}\n\n  double update_buffers_for_time(\n      const gsl::not_null<Variables<tags_for_writing>*> buffers,\n      const gsl::not_null<size_t*> time_span_start,\n      const gsl::not_null<size_t*> time_span_end, const double time,\n      const size_t l_max, const size_t interpolator_length,\n      const size_t buffer_depth,\n      const bool time_varies_fastest = true) const override {\n    if (*time_span_end > interpolator_length and\n        time_buffer_[*time_span_end - interpolator_length + 1] > time) {\n      // the next time an update will be required\n      return time_buffer_[*time_span_end - interpolator_length + 1];\n    }\n    // find the time spans that are needed\n    auto new_span_pair = detail::create_span_for_time_value(\n        time, buffer_depth, interpolator_length, 0, time_buffer_.size(),\n        time_buffer_);\n    *time_span_start = new_span_pair.first;\n    *time_span_end = new_span_pair.second;\n\n    const size_t libsharp_size =\n        Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n    tnsr::ii<ComplexModalVector, 3> spatial_metric_coefficients{libsharp_size};\n    tnsr::ii<ComplexModalVector, 3> dt_spatial_metric_coefficients{\n        libsharp_size};\n    tnsr::ii<ComplexModalVector, 3> dr_spatial_metric_coefficients{\n        libsharp_size};\n    tnsr::I<ComplexModalVector, 3> shift_coefficients{libsharp_size};\n    tnsr::I<ComplexModalVector, 3> dt_shift_coefficients{libsharp_size};\n    tnsr::I<ComplexModalVector, 3> dr_shift_coefficients{libsharp_size};\n    Scalar<ComplexModalVector> lapse_coefficients{libsharp_size};\n    Scalar<ComplexModalVector> dt_lapse_coefficients{libsharp_size};\n    Scalar<ComplexModalVector> dr_lapse_coefficients{libsharp_size};\n\n    const size_t number_of_angular_points =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n    Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n        boundary_variables{number_of_angular_points};\n\n    for (size_t time_index = 0; time_index < *time_span_end - *time_span_start;\n         ++time_index) {\n      TestHelpers::create_fake_time_varying_data(\n          make_not_null(&spatial_metric_coefficients),\n          make_not_null(&dt_spatial_metric_coefficients),\n          make_not_null(&dr_spatial_metric_coefficients),\n          make_not_null(&shift_coefficients),\n          make_not_null(&dt_shift_coefficients),\n          make_not_null(&dr_shift_coefficients),\n          make_not_null(&lapse_coefficients),\n          make_not_null(&dt_lapse_coefficients),\n          make_not_null(&dr_lapse_coefficients), solution_,\n          extraction_radius_.value_or(default_extraction_radius_),\n          coordinate_amplitude_, coordinate_frequency_,\n          time_buffer_[time_index + *time_span_start], l_max_, false);\n\n      Cce::create_bondi_boundary_data(\n          make_not_null(&boundary_variables), spatial_metric_coefficients,\n          dt_spatial_metric_coefficients, dr_spatial_metric_coefficients,\n          shift_coefficients, dt_shift_coefficients, dr_shift_coefficients,\n          lapse_coefficients, dt_lapse_coefficients, dr_lapse_coefficients,\n          extraction_radius_.value_or(default_extraction_radius_), l_max);\n      tmpl::for_each<tmpl::transform<\n          tags_for_writing, tmpl::bind<db::remove_tag_prefix, tmpl::_1>>>(\n          [&, this](auto tag_v) {\n            using tag = typename decltype(tag_v)::type;\n            SpinWeighted<T, tag::type::type::spin> spin_weighted_at_time{};\n            if constexpr (std::is_same_v<T, ComplexModalVector>) {\n              spin_weighted_at_time =\n                  Spectral::Swsh::libsharp_to_goldberg_modes(\n                      Spectral::Swsh::swsh_transform(\n                          l_max, 1,\n                          get(get<Tags::BoundaryValue<tag>>(\n                              boundary_variables))),\n                      l_max);\n            } else {\n              (void)l_max;\n              spin_weighted_at_time =\n                  get(get<Tags::BoundaryValue<tag>>(boundary_variables));\n            }\n            this->update_buffer_with_scalar_at_time_index(\n                make_not_null(&get<tmpl::conditional_t<\n                                  std::is_same_v<T, ComplexModalVector>,\n                                  Spectral::Swsh::Tags::SwshTransform<tag>,\n                                  Tags::BoundaryValue<tag>>>(*buffers)),\n                spin_weighted_at_time, time_index,\n                *time_span_end - *time_span_start, time_varies_fastest);\n          });\n    }\n    return time_buffer_[*time_span_end - interpolator_length + 1];\n  }\n  std::unique_ptr<WorldtubeBufferUpdater<tags_for_writing>> get_clone()\n      const override {\n    return std::make_unique<BondiBufferUpdater>(*this);\n  }\n\n  bool time_is_outside_range(const double time) const override {\n    return time < time_buffer_[0] or\n           time > time_buffer_[time_buffer_.size() - 1];\n  }\n\n  size_t get_l_max() const override { return l_max_; }\n\n  double get_extraction_radius() const override {\n    return extraction_radius_.value_or(default_extraction_radius_);\n  }\n\n  DataVector& get_time_buffer() override { return time_buffer_; }\n\n  bool has_version_history() const override { return true; }\n\n  void pup(PUP::er& p) override {\n    p | time_buffer_;\n    p | solution_;\n    p | extraction_radius_;\n    p | coordinate_amplitude_;\n    p | coordinate_frequency_;\n    p | l_max_;\n  }\n\n private:\n  template <int Spin>\n  void update_buffer_with_scalar_at_time_index(\n      const gsl::not_null<Scalar<SpinWeighted<T, Spin>>*> scalar_buffer,\n      const SpinWeighted<T, Spin>& spin_weighted_at_time,\n      const size_t time_index, const size_t time_span_extent,\n      const bool time_varies_fastest) const {\n    for (size_t k = 0; k < spin_weighted_at_time.size(); ++k) {\n      const size_t buffer_index =\n          time_varies_fastest ? time_index + k * time_span_extent\n                              : k + time_index * spin_weighted_at_time.size();\n      get(*scalar_buffer).data()[buffer_index] =\n          spin_weighted_at_time.data()[k];\n    }\n  }\n\n  DataVector time_buffer_;\n  gr::Solutions::KerrSchild solution_;\n  std::optional<double> extraction_radius_;\n  double default_extraction_radius_ = 100.0;\n  double coordinate_amplitude_ = 0.0;\n  double coordinate_frequency_ = 0.0;\n  size_t l_max_ = 0;\n};\n\ntemplate <typename T>\nPUP::able::PUP_ID Cce::DummyBufferUpdater<T>::my_PUP_ID = 0;  // NOLINT\ntemplate <typename T>\nPUP::able::PUP_ID Cce::BondiBufferUpdater<T>::my_PUP_ID = 0;  // NOLINT\n\ntemplate class Cce::DummyBufferUpdater<ComplexModalVector>;\ntemplate class Cce::DummyBufferUpdater<DataVector>;\ntemplate class Cce::BondiBufferUpdater<ComplexModalVector>;\n\nnamespace {\n\ntemplate <typename DataManager, typename DummyUpdater, typename Generator>\nvoid test_data_manager_with_bondi_buffer_updater(\n    const gsl::not_null<Generator*> gen,\n    const bool apply_normalization_bug = false, const bool descending_m = true,\n    const std::optional<double> extraction_radius = std::nullopt) {\n  // note that the default_extraction_radius is what will be reported\n  // from the buffer updater when the extraction_radius is the default\n  // `std::nullopt`.\n  const double default_extraction_radius = 100.0;\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  // acceptable parameters for the fake sinusoid variation in the input\n  // parameters\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n\n  const size_t buffer_size = 4;\n  const size_t l_max = 8;\n\n  DataVector time_buffer{30};\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    time_buffer[i] = target_time - 1.55 + 0.1 * static_cast<double>(i);\n  }\n\n  DataManager boundary_data_manager;\n  if constexpr (std::is_same_v<DataManager, MetricWorldtubeDataManager>) {\n    if (not apply_normalization_bug) {\n      boundary_data_manager = DataManager{\n          std::make_unique<DummyUpdater>(time_buffer, solution,\n                                         extraction_radius, amplitude,\n                                         frequency, l_max, false, descending_m),\n          l_max, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          descending_m};\n    } else {\n      boundary_data_manager = DataManager{\n          std::make_unique<DummyUpdater>(time_buffer, solution,\n                                         extraction_radius, amplitude,\n                                         frequency, l_max, true, false),\n          l_max, buffer_size,\n          std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u),\n          descending_m};\n    }\n  } else {\n    // avoid compiler warnings in the case where the normalization bug booleans\n    // aren't used.\n    (void)apply_normalization_bug;\n    (void)descending_m;\n    boundary_data_manager = DataManager{\n        std::make_unique<DummyUpdater>(time_buffer, solution, extraction_radius,\n                                       amplitude, frequency, l_max, false),\n        l_max, buffer_size,\n        std::make_unique<intrp::BarycentricRationalSpanInterpolator>(3u, 4u)};\n  }\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      expected_boundary_variables{number_of_angular_points};\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      interpolated_boundary_variables{number_of_angular_points};\n\n  Parallel::NodeLock hdf5_lock{};\n  boundary_data_manager.populate_hypersurface_boundary_data(\n      make_not_null(&interpolated_boundary_variables), target_time,\n      make_not_null(&hdf5_lock));\n\n  // populate the expected variables with the result from the analytic modes\n  // passed to the boundary data computation.\n  const size_t libsharp_size =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n  tnsr::ii<ComplexModalVector, 3> spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dt_spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dr_spatial_metric_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dt_shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dr_shift_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dt_lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dr_lapse_coefficients{libsharp_size};\n  TestHelpers::create_fake_time_varying_data(\n      make_not_null(&spatial_metric_coefficients),\n      make_not_null(&dt_spatial_metric_coefficients),\n      make_not_null(&dr_spatial_metric_coefficients),\n      make_not_null(&shift_coefficients), make_not_null(&dt_shift_coefficients),\n      make_not_null(&dr_shift_coefficients), make_not_null(&lapse_coefficients),\n      make_not_null(&dt_lapse_coefficients),\n      make_not_null(&dr_lapse_coefficients), solution,\n      extraction_radius.value_or(default_extraction_radius), amplitude,\n      frequency, target_time, l_max, false);\n\n  create_bondi_boundary_data(\n      make_not_null(&expected_boundary_variables), spatial_metric_coefficients,\n      dt_spatial_metric_coefficients, dr_spatial_metric_coefficients,\n      shift_coefficients, dt_shift_coefficients, dr_shift_coefficients,\n      lapse_coefficients, dt_lapse_coefficients, dr_lapse_coefficients,\n      extraction_radius.value_or(default_extraction_radius), l_max);\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  tmpl::for_each<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>(\n      [&expected_boundary_variables, &interpolated_boundary_variables,\n       &angular_derivative_approx](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& test_lhs = get<tag>(expected_boundary_variables);\n        const auto& test_rhs = get<tag>(interpolated_boundary_variables);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_lhs, test_rhs,\n                                     angular_derivative_approx);\n      });\n}\n\ntemplate <typename T, typename Generator>\nvoid test_metric_worldtube_buffer_updater_impl(\n    const gsl::not_null<Generator*> gen,\n    const bool extraction_radius_in_filename, const bool time_varies_fastest,\n    const std::optional<bool>& extra_adm = std::nullopt) {\n  constexpr bool is_modal = std::is_same_v<T, ComplexModalVector>;\n  CAPTURE(is_modal);\n  CAPTURE(extraction_radius_in_filename);\n  CAPTURE(time_varies_fastest);\n  CAPTURE(extra_adm);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n  CAPTURE(mass);\n  CAPTURE(spin);\n  CAPTURE(center);\n\n  const double extraction_radius = 100.0;\n\n  // acceptable parameters for the fake sinusoid variation in the input\n  // parameters\n  const double frequency = 0.1 * value_dist(*gen);\n  // If using adm options, need amplitude to be zero since this will change the\n  // coordinates that the variables are at, and we assume a constant radius\n  const double amplitude = extra_adm.has_value() ? 0.0 : 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n  CAPTURE(frequency);\n  CAPTURE(amplitude);\n  CAPTURE(target_time);\n\n  const size_t buffer_size = 4;\n  const size_t interpolator_length = 2;\n  const size_t file_l_max = 8;\n  // Must be the same as file_l_max for nodal data\n  const size_t computation_l_max = is_modal ? file_l_max + 2 : file_l_max;\n  CAPTURE(buffer_size);\n  CAPTURE(interpolator_length);\n  CAPTURE(file_l_max);\n  CAPTURE(computation_l_max);\n\n  const size_t computation_buffer_size =\n      (buffer_size + 2 * interpolator_length) *\n      (is_modal ? square(computation_l_max + 1)\n                : Spectral::Swsh::number_of_swsh_collocation_points(\n                      computation_l_max));\n  Variables<cce_metric_input_tags<T>> coefficients_buffers_from_file{\n      computation_buffer_size};\n  Variables<cce_metric_input_tags<T>> expected_coefficients_buffers{\n      computation_buffer_size};\n  const std::string filename = extraction_radius_in_filename\n                                   ? \"BoundaryDataH5Test_CceR0100.h5\"\n                                   : \"BoundaryDataH5Test.h5\";\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  TestHelpers::write_test_file<T, false>(\n      solution, filename, target_time, extraction_radius, frequency, amplitude,\n      file_l_max, true, extra_adm.has_value());\n\n  using AdmOptions = typename MetricWorldtubeH5BufferUpdater<T>::AdmOptions;\n  std::optional<AdmOptions> adm_options{};\n\n  if (extra_adm.has_value()) {\n    using Lapse = typename AdmOptions::Lapse;\n    using Shift = typename AdmOptions::Shift;\n    const Lapse lapse{true};\n    if (extra_adm.value()) {\n      const Shift shift{true, 0.75};\n      adm_options = AdmOptions{lapse, shift};\n    } else {\n      const Shift shift{true, 2.0, 1.0};\n      adm_options = AdmOptions{lapse, shift};\n    }\n  }\n  // request an appropriate buffer\n  auto buffer_updater =\n      extraction_radius_in_filename\n          ? MetricWorldtubeH5BufferUpdater<T>{filename, std::nullopt, true,\n                                              adm_options}\n          : MetricWorldtubeH5BufferUpdater<T>{filename, extraction_radius, true,\n                                              adm_options};\n  auto serialized_and_deserialized_updater =\n      serialize_and_deserialize(buffer_updater);\n  size_t time_span_start = 0;\n  size_t time_span_end = 0;\n  if (not is_modal) {\n    CHECK_THROWS_WITH(\n        buffer_updater.update_buffers_for_time(\n            make_not_null(&coefficients_buffers_from_file),\n            make_not_null(&time_span_start), make_not_null(&time_span_end),\n            target_time, 2 * computation_l_max, interpolator_length,\n            buffer_size, time_varies_fastest),\n        Catch::Matchers::ContainsSubstring(\n            \"When reading in nodal data, the LMax that \"\n            \"MetricWorldtubeH5BufferUpdater was constructed with\"));\n    time_span_start = 0;\n    time_span_end = 0;\n    if (extra_adm.has_value()) {\n      CHECK_THROWS_WITH(\n          buffer_updater.update_buffers_for_time(\n              make_not_null(&coefficients_buffers_from_file),\n              make_not_null(&time_span_start), make_not_null(&time_span_end),\n              target_time, computation_l_max, interpolator_length, buffer_size,\n              true),\n          Catch::Matchers::ContainsSubstring(\"Time must not vary fastest\"));\n      time_span_start = 0;\n      time_span_end = 0;\n    }\n  }\n  buffer_updater.update_buffers_for_time(\n      make_not_null(&coefficients_buffers_from_file),\n      make_not_null(&time_span_start), make_not_null(&time_span_end),\n      target_time, computation_l_max, interpolator_length, buffer_size,\n      time_varies_fastest);\n\n  Variables<cce_metric_input_tags<T>> coefficients_buffers_from_serialized{\n      computation_buffer_size};\n  size_t time_span_start_from_serialized = 0;\n  size_t time_span_end_from_serialized = 0;\n  serialized_and_deserialized_updater.update_buffers_for_time(\n      make_not_null(&coefficients_buffers_from_serialized),\n      make_not_null(&time_span_start_from_serialized),\n      make_not_null(&time_span_end_from_serialized), target_time,\n      computation_l_max, interpolator_length, buffer_size, time_varies_fastest);\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  time_span_start = 0;\n  time_span_end = 0;\n  const auto& time_buffer = buffer_updater.get_time_buffer();\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    CHECK(time_buffer[i] == approx(target_time - 1.5 + 0.1 * i));\n  }\n  const auto& time_buffer_from_serialized =\n      serialized_and_deserialized_updater.get_time_buffer();\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    CHECK(time_buffer_from_serialized[i] ==\n          approx(target_time - 1.5 + 0.1 * i));\n  }\n\n  const DummyBufferUpdater<T> bondi_buffer_updater = serialize_and_deserialize(\n      DummyBufferUpdater<T>{time_buffer, solution, extraction_radius, amplitude,\n                            frequency, computation_l_max});\n  bondi_buffer_updater.update_buffers_for_time(\n      make_not_null(&expected_coefficients_buffers),\n      make_not_null(&time_span_start), make_not_null(&time_span_end),\n      target_time, computation_l_max, interpolator_length, buffer_size,\n      time_varies_fastest);\n\n  // check that the data in the buffer matches the expected analytic data.\n  tmpl::for_each<cce_metric_input_tags<T>>(\n      [&expected_coefficients_buffers, &coefficients_buffers_from_file,\n       &coefficients_buffers_from_serialized, &extra_adm](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        // Since we don't have an expected value for the time derivatives of the\n        // lapse or shift because of the 1+log slicing and gamma-driver\n        // conditions, we just don't check them here. All other values should be\n        // the same though\n        if (extra_adm.has_value() and\n            (std::is_same_v<tag, ::Tags::dt<Tags::detail::Shift<T>>> or\n             std::is_same_v<tag, ::Tags::dt<Tags::detail::Lapse<T>>>)) {\n          return;\n        }\n        INFO(db::tag_name<tag>());\n        const auto& test_lhs = get<tag>(expected_coefficients_buffers);\n        const auto& test_rhs = get<tag>(coefficients_buffers_from_file);\n        CHECK_ITERABLE_APPROX(test_lhs, test_rhs);\n        const auto& test_rhs_from_serialized =\n            get<tag>(coefficients_buffers_from_serialized);\n        CHECK_ITERABLE_APPROX(test_lhs, test_rhs_from_serialized);\n      });\n  CHECK(buffer_updater.get_extraction_radius() == 100.0);\n}\n\ntemplate <typename T, typename Generator>\nvoid test_bondi_worldtube_buffer_updater_impl(\n    const gsl::not_null<Generator*> gen,\n    const bool extraction_radius_in_filename, const bool time_varies_fastest) {\n  constexpr bool is_modal = std::is_same_v<T, ComplexModalVector>;\n  CAPTURE(is_modal);\n  CAPTURE(extraction_radius_in_filename);\n  CAPTURE(time_varies_fastest);\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n  // first prepare the input for the modal version\n  const double mass = value_dist(*gen);\n  const std::array<double, 3> spin{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  const std::array<double, 3> center{\n      {value_dist(*gen), value_dist(*gen), value_dist(*gen)}};\n  gr::Solutions::KerrSchild solution{mass, spin, center};\n  CAPTURE(mass);\n  CAPTURE(spin);\n  CAPTURE(center);\n\n  const double extraction_radius = 100.0;\n\n  // acceptable parameters for the fake sinusoid variation in the input\n  // parameters\n  const double frequency = 0.1 * value_dist(*gen);\n  const double amplitude = 0.1 * value_dist(*gen);\n  const double target_time = 50.0 * value_dist(*gen);\n  CAPTURE(frequency);\n  CAPTURE(amplitude);\n  CAPTURE(target_time);\n\n  const size_t buffer_size = 4;\n  const size_t interpolator_length = 3;\n  const size_t file_l_max = 8;\n  const size_t computation_l_max = is_modal ? file_l_max + 2 : file_l_max;\n  CAPTURE(buffer_size);\n  CAPTURE(interpolator_length);\n  CAPTURE(file_l_max);\n  CAPTURE(computation_l_max);\n\n  using tags_for_writing =\n      typename BondiWorldtubeH5BufferUpdater<T>::tags_for_writing;\n\n  const size_t computation_buffer_size =\n      (buffer_size + 2 * interpolator_length) *\n      (is_modal ? square(computation_l_max + 1)\n                : Spectral::Swsh::number_of_swsh_collocation_points(\n                      computation_l_max));\n  Variables<tags_for_writing> coefficients_buffers_from_file{\n      computation_buffer_size};\n  Variables<tags_for_writing> expected_coefficients_buffers{\n      computation_buffer_size};\n  size_t libsharp_size =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(file_l_max);\n  tnsr::ii<ComplexModalVector, 3> spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dt_spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dr_spatial_metric_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dt_shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dr_shift_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dt_lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dr_lapse_coefficients{libsharp_size};\n\n  const size_t file_number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(file_l_max);\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      boundary_data_variables{file_number_of_angular_points};\n\n  // write times to file for several steps before and after the target time\n  const std::string filename = extraction_radius_in_filename\n                                   ? \"BoundaryDataH5Test_CceR0100.h5\"\n                                   : \"BoundaryDataH5Test.h5\";\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n\n  // scoped to close the file\n  {\n    using RecorderType =\n        tmpl::conditional_t<is_modal, Cce::WorldtubeModeRecorder,\n                            Cce::TestHelpers::WorldtubeModeRecorder>;\n    RecorderType recorder{file_l_max, filename};\n    for (size_t t = 0; t < 20; ++t) {\n      const double time = 0.01 * static_cast<double>(t) + target_time - 0.1;\n      TestHelpers::create_fake_time_varying_data(\n          make_not_null(&spatial_metric_coefficients),\n          make_not_null(&dt_spatial_metric_coefficients),\n          make_not_null(&dr_spatial_metric_coefficients),\n          make_not_null(&shift_coefficients),\n          make_not_null(&dt_shift_coefficients),\n          make_not_null(&dr_shift_coefficients),\n          make_not_null(&lapse_coefficients),\n          make_not_null(&dt_lapse_coefficients),\n          make_not_null(&dr_lapse_coefficients), solution, extraction_radius,\n          amplitude, frequency, time, file_l_max, false);\n\n      create_bondi_boundary_data(\n          make_not_null(&boundary_data_variables), spatial_metric_coefficients,\n          dt_spatial_metric_coefficients, dr_spatial_metric_coefficients,\n          shift_coefficients, dt_shift_coefficients, dr_shift_coefficients,\n          lapse_coefficients, dt_lapse_coefficients, dr_lapse_coefficients,\n          extraction_radius, file_l_max);\n\n      // loop over the tags that we want to dump.\n      tmpl::for_each<Cce::Tags::worldtube_boundary_tags_for_writing<>>(\n          [&recorder, &boundary_data_variables, &time](auto tag_v) {\n            using tag = typename decltype(tag_v)::type;\n\n            const ComplexDataVector& nodal_data =\n                get(get<tag>(boundary_data_variables)).data();\n\n            if constexpr (is_modal) {\n              recorder.template append_modal_data<tag::type::type::spin>(\n                  dataset_label_for_tag<typename tag::tag>(), time, nodal_data,\n                  file_l_max);\n            } else {\n              //   // This will write nodal data\n              recorder.append_worldtube_mode_data(\n                  dataset_label_for_tag<typename tag::tag>(), time, nodal_data);\n            }\n          });\n    }\n  }\n  // request an appropriate buffer\n  auto buffer_updater =\n      extraction_radius_in_filename\n          ? BondiWorldtubeH5BufferUpdater<T>{filename}\n          : BondiWorldtubeH5BufferUpdater<T>{filename, extraction_radius};\n  CHECK(buffer_updater.get_l_max() == file_l_max);\n  auto serialized_and_deserialized_updater =\n      serialize_and_deserialize(buffer_updater);\n  size_t time_span_start = 0;\n  size_t time_span_end = 0;\n  if (not is_modal) {\n    CHECK_THROWS_WITH(\n        buffer_updater.update_buffers_for_time(\n            make_not_null(&coefficients_buffers_from_file),\n            make_not_null(&time_span_start), make_not_null(&time_span_end),\n            target_time, 2 * computation_l_max, interpolator_length,\n            buffer_size, time_varies_fastest),\n        Catch::Matchers::ContainsSubstring(\n            \"When reading in nodal data, the LMax that the BufferUpdater was \"\n            \"constructed with\"));\n    time_span_start = 0;\n    time_span_end = 0;\n  }\n  buffer_updater.update_buffers_for_time(\n      make_not_null(&coefficients_buffers_from_file),\n      make_not_null(&time_span_start), make_not_null(&time_span_end),\n      target_time, computation_l_max, interpolator_length, buffer_size,\n      time_varies_fastest);\n\n  Variables<tags_for_writing> coefficients_buffers_from_serialized{\n      computation_buffer_size};\n  size_t time_span_start_from_serialized = 0;\n  size_t time_span_end_from_serialized = 0;\n  serialized_and_deserialized_updater.update_buffers_for_time(\n      make_not_null(&coefficients_buffers_from_serialized),\n      make_not_null(&time_span_start_from_serialized),\n      make_not_null(&time_span_end_from_serialized), target_time,\n      computation_l_max, interpolator_length, buffer_size, time_varies_fastest);\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  time_span_start = 0;\n  time_span_end = 0;\n  const auto& time_buffer = buffer_updater.get_time_buffer();\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    CHECK(time_buffer[i] == approx(target_time - 0.1 + 0.01 * i));\n  }\n  const auto& time_buffer_from_serialized =\n      serialized_and_deserialized_updater.get_time_buffer();\n  for (size_t i = 0; i < time_buffer.size(); ++i) {\n    CHECK(time_buffer_from_serialized[i] ==\n          approx(target_time - 0.1 + 0.01 * i));\n  }\n\n  const BondiBufferUpdater<T> bondi_buffer_updater = serialize_and_deserialize(\n      BondiBufferUpdater<T>{time_buffer, solution, extraction_radius, amplitude,\n                            frequency, computation_l_max});\n  bondi_buffer_updater.update_buffers_for_time(\n      make_not_null(&expected_coefficients_buffers),\n      make_not_null(&time_span_start), make_not_null(&time_span_end),\n      target_time, computation_l_max, interpolator_length, buffer_size,\n      time_varies_fastest);\n\n  // this approximation needs to be comparatively loose because it is comparing\n  // modes, which tend to have the error set by the scale of the original\n  // collocation errors (so, the dominant modes), rather than the scale of the\n  // individual mode being examined.\n  Approx modal_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n\n  // check that the data in the buffer matches the expected analytic data.\n  tmpl::for_each<tags_for_writing>(\n      [&expected_coefficients_buffers, &coefficients_buffers_from_file,\n       &coefficients_buffers_from_serialized, &modal_approx](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        INFO(db::tag_name<tag>());\n        const auto& test_lhs = get<tag>(expected_coefficients_buffers);\n        const auto& test_rhs = get<tag>(coefficients_buffers_from_file);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_lhs, test_rhs, modal_approx);\n        const auto& test_rhs_from_serialized =\n            get<tag>(coefficients_buffers_from_serialized);\n        CHECK_ITERABLE_CUSTOM_APPROX(test_lhs, test_rhs_from_serialized,\n                                     modal_approx);\n      });\n  CHECK(buffer_updater.get_extraction_radius() == 100.0);\n}\n\ntemplate <typename Generator>\nvoid test_metric_worldtube_buffer_updater(const gsl::not_null<Generator*> gen) {\n  INFO(\"SpEC worldtube (aka metric)\");\n  for (const auto& [extraction_radius_in_filename, time_varies_fastest] :\n       cartesian_product(std::array{true, false}, std::array{true, false})) {\n    test_metric_worldtube_buffer_updater_impl<ComplexModalVector>(\n        gen, extraction_radius_in_filename, time_varies_fastest);\n    test_metric_worldtube_buffer_updater_impl<DataVector>(\n        gen, extraction_radius_in_filename, time_varies_fastest);\n  }\n\n  {\n    INFO(\"First order form\");\n    test_metric_worldtube_buffer_updater_impl<DataVector>(gen, true, false,\n                                                          {true});\n  }\n  {\n    INFO(\"Conformal Christoffel form\");\n    test_metric_worldtube_buffer_updater_impl<DataVector>(gen, true, false,\n                                                          {false});\n  }\n}\n\ntemplate <typename Generator>\nvoid test_bondi_worldtube_buffer_updater(const gsl::not_null<Generator*> gen) {\n  INFO(\"Reduced SpEC worldtube (aka Bondi)\");\n  for (const auto& [extraction_radius_in_filename, time_varies_fastest] :\n       cartesian_product(std::array{true, false}, std::array{true, false})) {\n    test_bondi_worldtube_buffer_updater_impl<ComplexModalVector>(\n        gen, extraction_radius_in_filename, time_varies_fastest);\n    test_bondi_worldtube_buffer_updater_impl<ComplexDataVector>(\n        gen, extraction_radius_in_filename, time_varies_fastest);\n  }\n}\n}  // namespace\n\n// An increased timeout because this test seems to have high variance in\n// duration. The high variance may be due to the comparatively high magnitude of\n// disk operations in this test.\n// [[TimeOut, 60]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Cce.ReadBoundaryDataH5\",\n                  \"[Unit][Cce]\") {\n  register_derived_classes_with_charm<\n      Cce::WorldtubeBufferUpdater<cce_metric_input_tags<ComplexModalVector>>>();\n  register_derived_classes_with_charm<\n      Cce::WorldtubeBufferUpdater<Tags::worldtube_boundary_tags_for_writing<\n          Spectral::Swsh::Tags::SwshTransform>>>();\n  register_derived_classes_with_charm<Cce::WorldtubeDataManager<\n      Cce::Tags::characteristic_worldtube_boundary_tags<\n          Cce::Tags::BoundaryValue>>>();\n  register_derived_classes_with_charm<intrp::SpanInterpolator>();\n  MAKE_GENERATOR(gen);\n  {\n    INFO(\"Testing buffer updaters\");\n    test_metric_worldtube_buffer_updater(make_not_null(&gen));\n    test_bondi_worldtube_buffer_updater(make_not_null(&gen));\n  }\n  {\n    INFO(\"Testing data managers\");\n    using DummyBufferUpdater = DummyBufferUpdater<ComplexModalVector>;\n    using BondiBufferUpdater = BondiBufferUpdater<ComplexModalVector>;\n    test_data_manager_with_bondi_buffer_updater<MetricWorldtubeDataManager,\n                                                DummyBufferUpdater>(\n        make_not_null(&gen));\n    // with normalization bug applied:\n    test_data_manager_with_bondi_buffer_updater<MetricWorldtubeDataManager,\n                                                DummyBufferUpdater>(\n        make_not_null(&gen), true, true);\n    test_data_manager_with_bondi_buffer_updater<MetricWorldtubeDataManager,\n                                                DummyBufferUpdater>(\n        make_not_null(&gen), false, true);\n    test_data_manager_with_bondi_buffer_updater<MetricWorldtubeDataManager,\n                                                DummyBufferUpdater>(\n        make_not_null(&gen), false, false);\n    // check the case for an explicitly provided extraction radius.\n    test_data_manager_with_bondi_buffer_updater<MetricWorldtubeDataManager,\n                                                DummyBufferUpdater>(\n        make_not_null(&gen), false, false, 200.0);\n    test_data_manager_with_bondi_buffer_updater<BondiWorldtubeDataManager,\n                                                BondiBufferUpdater>(\n        make_not_null(&gen));\n  }\n}\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/ATilde.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef a_tilde(\n    conformal_factor_squared,\n    spatial_metric,\n    extrinsic_curvature,\n    trace_extrinsic_curvature,\n):\n    return conformal_factor_squared * (\n        extrinsic_curvature - trace_extrinsic_curvature * spatial_metric / 3.0\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Ccz4/Ccz4WrappedGr.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/DummyReconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/Solutions/Factory.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Ccz4::BoundaryConditions::BoundaryCondition,\n                   tmpl::list<Ccz4::BoundaryConditions::DirichletAnalytic>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   Ccz4::Solutions::all_solutions>,\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   MathFunctions::all_math_functions<1, Frame::Inertial>>>;\n  };\n};\n\ntemplate <typename T, typename U>\nvoid test_fd(const U& boundary_condition, const T& analytic_solution_or_data) {\n  const double time = 1.3;\n  const Mesh<3> subcell_mesh{9, Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  const std::array<double, 3> lower_bound{{0.78, 1.18, 1.28}};\n  const std::array<double, 3> upper_bound{{0.82, 1.22, 1.32}};\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                   Affine{-1., 1., lower_bound[2], upper_bound[2]}});\n\n  const ElementId<3> element_id{0};\n  const ElementMap logical_to_grid_map{\n      element_id,\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          Affine3D{Affine{-1., 1., 2.0 * lower_bound[0], 2.0 * upper_bound[0]},\n                   Affine{-1., 1., 2.0 * lower_bound[1], 2.0 * upper_bound[1]},\n                   Affine{-1., 1., 2.0 * lower_bound[2], 2.0 * upper_bound[2]}})\n          .get_clone()};\n  const auto direction = Direction<3>::lower_xi();\n\n  const Ccz4::fd::DummyReconstructor reconstructor{};\n  const size_t ghost_zone_size = reconstructor.ghost_zone_size();\n\n  using Vars = Variables<Ccz4::fd::Tags::spacetime_reconstruction_tags>;\n  Vars vars{ghost_zone_size * subcell_mesh.extents().slice_away(0).product()};\n  const auto expected_vars = [&analytic_solution_or_data, &direction,\n                              &functions_of_time, &grid_to_inertial_map,\n                              &logical_to_grid_map, &subcell_mesh, time,\n                              ghost_zone_size]() {\n    const auto ghost_logical_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, ghost_zone_size, direction);\n\n    const auto ghost_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n    using tags =\n        tmpl::list<::Ccz4::Tags::ConformalMetric<DataVector, 3>,\n                   gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                   ::Ccz4::Tags::ConformalFactor<DataVector>,\n                   ::Ccz4::Tags::ATilde<DataVector, 3>,\n                   gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                   ::Ccz4::Tags::Theta<DataVector>,\n                   ::Ccz4::Tags::GammaHat<DataVector, 3>,\n                   ::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>;\n\n    tuples::tagged_tuple_from_typelist<tags> analytic_vars{};\n\n    if constexpr (::is_analytic_solution_v<T>) {\n      analytic_vars = analytic_solution_or_data.variables(ghost_inertial_coords,\n                                                          time, tags{});\n    } else {\n      (void)time;\n      analytic_vars =\n          analytic_solution_or_data.variables(ghost_inertial_coords, tags{});\n    }\n\n    Vars expected{get<0>(ghost_inertial_coords).size()};\n\n    get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(expected) =\n        get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(analytic_vars);\n    get<gr::Tags::Lapse<DataVector>>(expected) =\n        get<gr::Tags::Lapse<DataVector>>(analytic_vars);\n    get<gr::Tags::Shift<DataVector, 3>>(expected) =\n        get<gr::Tags::Shift<DataVector, 3>>(analytic_vars);\n    get<::Ccz4::Tags::ConformalFactor<DataVector>>(expected) =\n        get<::Ccz4::Tags::ConformalFactor<DataVector>>(analytic_vars);\n    get<::Ccz4::Tags::ATilde<DataVector, 3>>(expected) =\n        get<::Ccz4::Tags::ATilde<DataVector, 3>>(analytic_vars);\n    get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(expected) =\n        get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(analytic_vars);\n    get<::Ccz4::Tags::Theta<DataVector>>(expected) =\n        get<::Ccz4::Tags::Theta<DataVector>>(analytic_vars);\n    get<::Ccz4::Tags::GammaHat<DataVector, 3>>(expected) =\n        get<::Ccz4::Tags::GammaHat<DataVector, 3>>(analytic_vars);\n    get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(expected) =\n        get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(analytic_vars);\n    return expected;\n  }();\n  auto& [conformal_metric, conformal_factor, a_tilde, trace_extrinsic_curvature,\n         theta, gamma_hat, lapse, shift, auxiliary_shift_b] = vars;\n\n  boundary_condition.fd_ghost(\n      make_not_null(&conformal_metric), make_not_null(&lapse),\n      make_not_null(&shift), make_not_null(&conformal_factor),\n      make_not_null(&a_tilde), make_not_null(&trace_extrinsic_curvature),\n      make_not_null(&theta), make_not_null(&gamma_hat),\n      make_not_null(&auxiliary_shift_b), direction, subcell_mesh, time,\n      functions_of_time, logical_to_grid_map, grid_to_inertial_map,\n      reconstructor);\n  // failing line\n  CHECK(vars == expected_vars);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Ccz4.BoundaryConditions.DirichletAnalytic\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  register_factory_classes_with_charm<Metavariables>();\n  {\n    INFO(\"Test with analytic solution\");\n    const auto product_boundary_condition =\n        TestHelpers::test_creation<\n            std::unique_ptr<Ccz4::BoundaryConditions::BoundaryCondition>,\n            Metavariables>(\n            \"DirichletAnalytic:\\n\"\n            \"  AnalyticPrescription:\\n\"\n            \"      Ccz4(KerrSchild):\\n\"\n            \"        Mass: 1.0\\n\"\n            \"        Spin: [0.5, -0.2, 0.0]\\n\"\n            \"        Center: [-0.2, 0.0, 0.3]\\n\"\n            \"        Velocity: [0.0, 0.0, 0.0]\\n\")\n            ->get_clone();\n\n    const Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::KerrSchild>\n        analytic_solution_or_data{1.0,\n            {0.5, -0.2, 0.0}, {-0.2, 0.0, 0.3}, {0.0, 0.0, 0.0}};\n    const auto serialized_and_deserialized_condition =\n        serialize_and_deserialize(\n            *dynamic_cast<Ccz4::BoundaryConditions::DirichletAnalytic*>(\n                product_boundary_condition.get()));\n\n    test_fd<Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::KerrSchild>,\n            Ccz4::BoundaryConditions::DirichletAnalytic>(\n        serialized_and_deserialized_condition, analytic_solution_or_data);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/BoundaryConditions/Test_Sommerfeld.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/DummyReconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Vars = Variables<Ccz4::fd::Tags::spacetime_reconstruction_tags>;\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Ccz4::BoundaryConditions::BoundaryCondition,\n                             tmpl::list<Ccz4::BoundaryConditions::Sommerfeld>>>;\n  };\n};\n\nVars set_polynomial(const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n                    const size_t max_degree) {\n  DataVector result_vector{get<0>(coords).size(), 0.0};\n  for (size_t degree_x = 0; degree_x <= max_degree; ++degree_x) {\n    for (size_t degree_y = 0; degree_y <= max_degree - degree_x; ++degree_y) {\n      for (size_t degree_z = 0; degree_z <= max_degree - degree_x - degree_y;\n           ++degree_z) {\n        result_vector += pow(get<0>(coords), degree_x) *\n                         pow(get<1>(coords), degree_y) *\n                         pow(get<2>(coords), degree_z);\n      }\n    }\n  }\n  Vars vars{get<0>(coords).size()};\n\n  tmpl::for_each<Ccz4::fd::System::variables_tag_list>(\n      [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        for (auto& component : get<Tag>(vars)) {\n          component = result_vector;\n        }\n      });\n\n  return vars;\n}\n\ntemplate <typename U>\nvoid test_fd(const U& boundary_condition, const size_t max_degree) {\n  const Mesh<3> subcell_mesh{20, Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n\n  const std::array<double, 3> lower_bound{{0.78, 1.18, 1.28}};\n  const std::array<double, 3> upper_bound{{0.82, 1.22, 1.32}};\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                   Affine{-1., 1., lower_bound[2], upper_bound[2]}});\n\n  const ElementId<3> element_id{0};\n  const ElementMap logical_to_grid_map{\n      element_id,\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          Affine3D{Affine{-1., 1., 2.0 * lower_bound[0], 2.0 * upper_bound[0]},\n                   Affine{-1., 1., 2.0 * lower_bound[1], 2.0 * upper_bound[1]},\n                   Affine{-1., 1., 2.0 * lower_bound[2], 2.0 * upper_bound[2]}})\n          .get_clone()};\n\n  const auto direction = Direction<3>::lower_xi();\n\n  const Ccz4::fd::DummyReconstructor reconstructor{};\n  const size_t ghost_zone_size = reconstructor.ghost_zone_size();\n\n  const auto expected_vars = [&]() {\n    const auto ghost_logical_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, ghost_zone_size, direction);\n\n    const auto ghost_inertial_coords =\n        grid_to_inertial_map(logical_to_grid_map(ghost_logical_coords));\n\n    const auto analytic_vars =\n        set_polynomial(ghost_inertial_coords, max_degree);\n\n    Vars expected{get<0>(ghost_inertial_coords).size()};\n\n    get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(expected) =\n        get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(analytic_vars);\n    get<gr::Tags::Lapse<DataVector>>(expected) =\n        get<gr::Tags::Lapse<DataVector>>(analytic_vars);\n    get<gr::Tags::Shift<DataVector, 3>>(expected) =\n        get<gr::Tags::Shift<DataVector, 3>>(analytic_vars);\n    get<::Ccz4::Tags::ConformalFactor<DataVector>>(expected) =\n        get<::Ccz4::Tags::ConformalFactor<DataVector>>(analytic_vars);\n    get<::Ccz4::Tags::ATilde<DataVector, 3>>(expected) =\n        get<::Ccz4::Tags::ATilde<DataVector, 3>>(analytic_vars);\n    get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(expected) =\n        get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(analytic_vars);\n    get<::Ccz4::Tags::Theta<DataVector>>(expected) =\n        get<::Ccz4::Tags::Theta<DataVector>>(analytic_vars);\n    get<::Ccz4::Tags::GammaHat<DataVector, 3>>(expected) =\n        get<::Ccz4::Tags::GammaHat<DataVector, 3>>(analytic_vars);\n    get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(expected) =\n        get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(analytic_vars);\n    return expected;\n  }();\n\n  Vars vars{ghost_zone_size * subcell_mesh.extents().slice_away(0).product()};\n  auto& [conformal_metric, conformal_factor, a_tilde, trace_extrinsic_curvature,\n         theta, gamma_hat, lapse, shift, auxiliary_shift_b] = vars;\n\n  const auto interior_inertial_coords = grid_to_inertial_map(\n      logical_to_grid_map(logical_coordinates(subcell_mesh)));\n  const Vars interior_vars =\n      set_polynomial(interior_inertial_coords, max_degree);\n  auto& [int_conformal_metric, int_conformal_factor, int_a_tilde,\n         int_trace_extrinsic_curvature, int_theta, int_gamma_hat, int_lapse,\n         int_shift, int_auxiliary_shift_b] = interior_vars;\n\n  boundary_condition.fd_ghost(\n      make_not_null(&conformal_metric), make_not_null(&lapse),\n      make_not_null(&shift), make_not_null(&conformal_factor),\n      make_not_null(&a_tilde), make_not_null(&trace_extrinsic_curvature),\n      make_not_null(&theta), make_not_null(&gamma_hat),\n      make_not_null(&auxiliary_shift_b), direction,\n      // interior args in variables_tag_list order\n      int_conformal_metric, int_conformal_factor, int_a_tilde,\n      int_trace_extrinsic_curvature, int_theta, int_gamma_hat, int_lapse,\n      int_shift, int_auxiliary_shift_b, subcell_mesh, reconstructor);\n\n  tmpl::for_each<Ccz4::fd::System::variables_tag_list>(\n      [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        const std::string tag_name = db::tag_name<Tag>();\n        CAPTURE(tag_name);\n        for (auto expected_component = get<Tag>(expected_vars).cbegin(),\n                  component = get<Tag>(vars).cbegin();\n             expected_component != get<Tag>(expected_vars).cend() and\n             component != get<Tag>(vars).cend();\n             ++expected_component, ++component) {\n          CHECK_ITERABLE_APPROX(*component, *expected_component);\n        }\n      });\n}\n\nSPECTRE_TEST_CASE(\"Unit.Ccz4.BoundaryConditions.Sommerfeld\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  {\n    INFO(\"Test Sommerfeld BC\");\n    const auto product_boundary_condition =\n        TestHelpers::test_creation<\n            std::unique_ptr<Ccz4::BoundaryConditions::BoundaryCondition>,\n            Metavariables>(\"Sommerfeld:\\n\")\n            ->get_clone();\n\n    const auto serialized_and_deserialized_condition =\n        serialize_and_deserialize(\n            *dynamic_cast<Ccz4::BoundaryConditions::Sommerfeld*>(\n                product_boundary_condition.get()));\n\n    test_fd<Ccz4::BoundaryConditions::Sommerfeld>(\n        serialized_and_deserialized_condition, 1);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Ccz4\")\n\nset(LIBRARY_SOURCES\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryConditions/Test_Sommerfeld.cpp\n  FiniteDifference/Test_ApplyFilter.cpp\n  FiniteDifference/Test_BoundaryConditionGhostData.cpp\n  FiniteDifference/Test_Derivatives.cpp\n  FiniteDifference/Test_DummyReconstructor.cpp\n  FiniteDifference/Test_EnforceConstrainedEvolution.cpp\n  FiniteDifference/Test_Filter.cpp\n  FiniteDifference/Test_GhostData.cpp\n  FiniteDifference/Test_SoTimeDerivative.cpp\n  FiniteDifference/Test_Tags.cpp\n  Test_ATilde.cpp\n  Test_Ccz4WrappedGr.cpp\n  Test_Christoffel.cpp\n  Test_DerivChristoffel.cpp\n  Test_DerivLapse.cpp\n  Test_DerivZ4Constraint.cpp\n  Test_Ricci.cpp\n  Test_RicciScalarPlusDivergenceZ4Constraint.cpp\n  Test_Tags.cpp\n  Test_TempTags.cpp\n  Test_TimeDerivative.cpp\n  Test_Z4Constraint.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Ccz4\n  DataBoxTestHelpers\n  DataStructures\n  Domain\n  ErrorHandling\n  Framework\n  GeneralRelativity\n  GeneralRelativityHelpers\n  GeneralRelativitySolutions\n  LinearOperators\n  SoCcz4\n  Spectral\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Christoffel.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef conformal_christoffel_second_kind(\n    inverse_conformal_spatial_metric, field_d\n):\n    return np.einsum(\n        \"kl,ijl->kij\",\n        inverse_conformal_spatial_metric,\n        (\n            np.einsum(\"ijl\", field_d)\n            + np.einsum(\"jil\", field_d)\n            - np.einsum(\"lij\", field_d)\n        ),\n    )\n\n\ndef christoffel_second_kind(\n    conformal_spatial_metric,\n    inverse_conformal_spatial_metric,\n    field_p,\n    conformal_christoffel_second_kind,\n):\n    return np.einsum(\"kij->kij\", conformal_christoffel_second_kind) - (\n        np.einsum(\n            \"kl,ijl->kij\",\n            inverse_conformal_spatial_metric,\n            (\n                np.einsum(\"jl,i\", conformal_spatial_metric, field_p)\n                + np.einsum(\"il,j\", conformal_spatial_metric, field_p)\n                - np.einsum(\"ij,l\", conformal_spatial_metric, field_p)\n            ),\n        )\n    )\n\n\ndef contracted_conformal_christoffel_second_kind(\n    inverse_conformal_spatial_metric, conformal_christoffel_second_kind\n):\n    return np.einsum(\n        \"jl,ijl\",\n        inverse_conformal_spatial_metric,\n        conformal_christoffel_second_kind,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/DerivChristoffel.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef deriv_conformal_christoffel_second_kind(\n    inverse_conformal_spatial_metric, field_d, d_field_d, field_d_up\n):\n    return (\n        -2.0\n        * np.einsum(\n            \"kml,ijl->kmij\",\n            field_d_up,\n            (\n                np.einsum(\"ijl\", field_d)\n                + np.einsum(\"jil\", field_d)\n                - np.einsum(\"lij\", field_d)\n            ),\n        )\n        + np.einsum(\n            \"ml,ijkl->kmij\",\n            inverse_conformal_spatial_metric,\n            (\n                np.einsum(\"kijl\", d_field_d)\n                + np.einsum(\"ikjl\", d_field_d)\n                + np.einsum(\"kjil\", d_field_d)\n                + np.einsum(\"jkil\", d_field_d)\n                - np.einsum(\"klij\", d_field_d)\n                - np.einsum(\"lkij\", d_field_d)\n            ),\n        )\n        / 2.0\n    )\n\n\ndef deriv_contracted_conformal_christoffel_second_kind(\n    inverse_conformal_spatial_metric,\n    field_d_up,\n    conformal_christoffel_second_kind,\n    d_conformal_christoffel_second_kind,\n):\n    return -2.0 * np.einsum(\n        \"kjl,ijl->ki\", field_d_up, conformal_christoffel_second_kind\n    ) + np.einsum(\n        \"jl,kijl->ki\",\n        inverse_conformal_spatial_metric,\n        d_conformal_christoffel_second_kind,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/DerivLapse.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef grad_grad_lapse(lapse, christoffel_second_kind, field_a, d_field_a):\n    return (\n        lapse * np.einsum(\"i,j\", field_a, field_a)\n        - lapse * np.einsum(\"kij,k\", christoffel_second_kind, field_a)\n        + 0.5\n        * lapse\n        * (np.einsum(\"ij\", d_field_a) + np.einsum(\"ij->ji\", d_field_a))\n    )\n\n\ndef divergence_lapse(\n    conformal_factor_squared, inverse_conformal_metric, grad_grad_lapse\n):\n    return conformal_factor_squared * np.einsum(\n        \"ij,ij\", inverse_conformal_metric, grad_grad_lapse\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/DerivZ4Constraint.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef grad_spatial_z4_constraint(\n    spatial_z4_constraint,\n    conformal_spatial_metric,\n    christoffel_second_kind,\n    field_d,\n    gamma_hat_minus_contracted_conformal_christoffel,\n    d_gamma_hat_minus_contracted_conformal_christoffel,\n):\n    return (\n        np.einsum(\n            \"ijl,l\", field_d, gamma_hat_minus_contracted_conformal_christoffel\n        )\n        + 0.5\n        * np.einsum(\n            \"jl,il\",\n            conformal_spatial_metric,\n            d_gamma_hat_minus_contracted_conformal_christoffel,\n        )\n        - np.einsum(\"lij,l\", christoffel_second_kind, spatial_z4_constraint)\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_ApplyFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/ApplyFilter.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Filter.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Ccz4/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid set_polynomial(\n    const gsl::not_null<std::vector<DataVector>*> vars_ptr,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& local_logical_coords,\n    const size_t degree) {\n  for (auto& var : *vars_ptr) {\n    var = 0.0;\n    for (size_t i = 0; i < Dim; ++i) {\n      var += pow(local_logical_coords.get(i), degree);\n    }\n  }\n}\n\ntemplate <typename System>\nvoid set_solution(\n    const gsl::not_null<Variables<typename System::variables_tag::tags_list>*>\n        volume_vars,\n    const gsl::not_null<DirectionalIdMap<3, evolution::dg::subcell::GhostData>*>\n        neighbor_data,\n    const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& logical_coords,\n    const size_t deriv_order, const size_t degree) {\n  const auto set_data_vectors = [](const gsl::not_null<std::vector<DataVector>*>\n                                       local_dvs,\n                                   const auto local_vars) {\n    for (size_t i = 0; i < 6; ++i) {\n      (*local_dvs)[i].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 1; ++i) {\n      (*local_dvs)[i + 10].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::ConformalFactor<DataVector>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 6; ++i) {\n      (*local_dvs)[i + 11].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::ATilde<DataVector, 3>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 1; ++i) {\n      (*local_dvs)[i + 17].set_data_ref(make_not_null(\n          &get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 1; ++i) {\n      (*local_dvs)[i + 18].set_data_ref(\n          make_not_null(&get<::Ccz4::Tags::Theta<DataVector>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      (*local_dvs)[i + 19].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::GammaHat<DataVector, 3>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 1; ++i) {\n      (*local_dvs)[i + 6].set_data_ref(\n          make_not_null(&get<gr::Tags::Lapse<DataVector>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      (*local_dvs)[i + 7].set_data_ref(\n          make_not_null(&get<gr::Tags::Shift<DataVector, 3>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      (*local_dvs)[i + 22].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(*local_vars)[i]));\n    }\n  };\n\n  std::vector<DataVector> vars(25);\n  set_data_vectors(make_not_null(&vars), volume_vars);\n  set_polynomial(&vars, logical_coords, degree);\n\n  for (const auto& direction : Direction<3>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    std::vector<DataVector> neighbor_dvs(25);\n    Variables<::Ccz4::fd::System::variables_tag_list> neighbor_vars{\n        mesh.number_of_grid_points()};\n\n    set_data_vectors(make_not_null(&neighbor_dvs),\n                     make_not_null(&neighbor_vars));\n    set_polynomial(&neighbor_dvs, neighbor_logical_coords, degree);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars), mesh.extents(), deriv_order / 2 + 1,\n        std::unordered_set{direction.opposite()}, 0, {});\n    CAPTURE(deriv_order / 2 + 1);\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    const auto key = DirectionalId<3>{direction, ElementId<3>{0}};\n    (*neighbor_data)[key] = evolution::dg::subcell::GhostData{1};\n    (*neighbor_data)[key].neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n}\n\nvoid test(const bool evolve_lapse_and_shift) {\n  const size_t points_per_dimension = 6;\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n\n  using System = ::Ccz4::fd::System;\n\n  Variables<System::variables_tag_list> volume_evolved_variables{\n      subcell_mesh.number_of_grid_points()};\n\n  DirectionalIdMap<3, evolution::dg::subcell::GhostData>\n      neighbor_data_for_reconstruction{};\n\n  set_solution<System>(&volume_evolved_variables,\n                       &neighbor_data_for_reconstruction, subcell_mesh,\n                       logical_coords, 6, 5);\n\n  // Store the original variables for comparison\n  Variables<System::variables_tag_list> original_variables =\n      volume_evolved_variables;\n\n  // Set up the DataBox with all required tags\n  const double kreiss_oliger_epsilon = 0.5;  // Use valid epsilon value\n\n  auto box = db::create<db::AddSimpleTags<\n      System::variables_tag, evolution::dg::subcell::Tags::Mesh<3>,\n      Ccz4::fd::Tags::EvolveLapseAndShift, Ccz4::fd::Tags::KreissOligerEpsilon,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>>(\n      volume_evolved_variables, subcell_mesh,\n      evolve_lapse_and_shift, kreiss_oliger_epsilon,\n      neighbor_data_for_reconstruction);\n\n  // Apply the filter through the mutator\n  db::mutate_apply<Ccz4::fd::ApplyFilter>(make_not_null(&box));\n\n  // Get the result from the box\n  const auto& result = get<System::variables_tag>(box);\n\n  if (not evolve_lapse_and_shift) {\n    CHECK(get<gr::Tags::Lapse<DataVector>>(result) ==\n          get<gr::Tags::Lapse<DataVector>>(original_variables));\n    CHECK(get<gr::Tags::Shift<DataVector, 3>>(result) ==\n          get<gr::Tags::Shift<DataVector, 3>>(original_variables));\n    CHECK(\n        get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(result) ==\n        get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(original_variables));\n  }\n\n  tmpl::for_each<System::variables_tag_list>([&result,\n                                              &original_variables](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const auto& result_tensor = get<tag>(result);\n    const auto& volume_tensor = get<tag>(original_variables);\n    CAPTURE(pretty_type::name<tag>());\n    const Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n    for (size_t tensor_index = 0; tensor_index < result_tensor.size();\n         ++tensor_index) {\n      CHECK_ITERABLE_CUSTOM_APPROX(result_tensor[tensor_index],\n                                   volume_tensor[tensor_index], custom_approx);\n    }\n  });\n}\n\nvoid test_error_when_epsilon_out_of_range() {\n  const size_t points_per_dimension = 6;\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n\n  using System = ::Ccz4::fd::System;\n\n  Variables<System::variables_tag_list> volume_evolved_variables{\n      subcell_mesh.number_of_grid_points()};\n\n  DirectionalIdMap<3, evolution::dg::subcell::GhostData>\n      neighbor_data_for_reconstruction{};\n\n  set_solution<System>(&volume_evolved_variables,\n                       &neighbor_data_for_reconstruction, subcell_mesh,\n                       logical_coords, 6, 5);\n\n  // Test with epsilon = 0.0 (should throw error)\n  {\n    auto box = db::create<db::AddSimpleTags<\n        System::variables_tag, evolution::dg::subcell::Tags::Mesh<3>,\n        Ccz4::fd::Tags::EvolveLapseAndShift,\n        Ccz4::fd::Tags::KreissOligerEpsilon,\n        evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>>(\n        volume_evolved_variables, subcell_mesh, true, -0.1,\n        neighbor_data_for_reconstruction);\n\n    CHECK_THROWS_WITH(\n        db::mutate_apply<Ccz4::fd::ApplyFilter>(make_not_null(&box)),\n        Catch::Matchers::ContainsSubstring(\n            \"Kreiss-Oliger epsilon should be in the interval [0, 1]\"));\n  }\n\n  // Test with epsilon = 1.0 (should throw error)\n  {\n    auto box = db::create<db::AddSimpleTags<\n        System::variables_tag, evolution::dg::subcell::Tags::Mesh<3>,\n        Ccz4::fd::Tags::EvolveLapseAndShift,\n        Ccz4::fd::Tags::KreissOligerEpsilon,\n        evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>>(\n        volume_evolved_variables, subcell_mesh, true, 1.1,\n        neighbor_data_for_reconstruction);\n\n    CHECK_THROWS_WITH(\n        db::mutate_apply<Ccz4::fd::ApplyFilter>(make_not_null(&box)),\n        Catch::Matchers::ContainsSubstring(\n            \"Kreiss-Oliger epsilon should be in the interval [0, 1]\"));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Fd.ApplyFilter\",\n                  \"[Unit][Evolution]\") {\n  test(true);\n  test(false);\n  test_error_when_epsilon_out_of_range();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_BoundaryConditionGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/BoundaryConditionGhostData.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/DummyReconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Evolution/Systems/Ccz4/Solutions/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\nnamespace {\n\n// Metavariables to parse the list of derived classes of boundary conditions\nstruct EvolutionMetaVars {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<BoundaryConditions::BoundaryCondition,\n                             BoundaryConditions::standard_boundary_conditions>,\n                  tmpl::pair<evolution::initial_data::InitialData,\n                             Ccz4::Solutions::all_solutions>>;\n  };\n};\n\nusing SolutionForTest = Solutions::Ccz4WrappedGr<gr::Solutions::Minkowski<3>>;\n\nusing Vars = Variables<Ccz4::fd::Tags::spacetime_reconstruction_tags>;\nVars set_polynomial(const tnsr::I<DataVector, 3, Frame::Inertial>& coords,\n                    const size_t max_degree) {\n  DataVector result_vector{get<0>(coords).size(), 0.0};\n  for (size_t degree_x = 0; degree_x <= max_degree; ++degree_x) {\n    for (size_t degree_y = 0; degree_y <= max_degree - degree_x; ++degree_y) {\n      for (size_t degree_z = 0; degree_z <= max_degree - degree_x - degree_y;\n           ++degree_z) {\n        result_vector += pow(get<0>(coords), degree_x) *\n                         pow(get<1>(coords), degree_y) *\n                         pow(get<2>(coords), degree_z);\n      }\n    }\n  }\n  Vars vars{get<0>(coords).size()};\n\n  tmpl::for_each<Ccz4::fd::System::variables_tag_list>(\n      [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        for (auto& component : get<Tag>(vars)) {\n          component = result_vector;\n        }\n      });\n\n  return vars;\n}\n\ntemplate <typename BoundaryConditionType>\nvoid test(const BoundaryConditionType& boundary_condition,\n          const SolutionForTest& solution) {\n  const size_t num_dg_pts = 3;\n\n  // Create a 3D element [-1, 1]^3 and use it for test\n  const std::array<double, 3> lower_bounds{-1.0, -1.0, -1.0};\n  const std::array<double, 3> upper_bounds{1.0, 1.0, 1.0};\n  const std::array<size_t, 3> refinement_levels{0, 0, 0};\n  const std::array<size_t, 3> number_of_grid_points{num_dg_pts, num_dg_pts,\n                                                    num_dg_pts};\n  const domain::creators::Brick brick(\n      lower_bounds, upper_bounds, refinement_levels, number_of_grid_points,\n      {{{{boundary_condition.get_clone(), boundary_condition.get_clone()}},\n        {{boundary_condition.get_clone(), boundary_condition.get_clone()}},\n        {{boundary_condition.get_clone(), boundary_condition.get_clone()}}}});\n  auto domain = brick.create_domain();\n  auto boundary_conditions = brick.external_boundary_conditions();\n  const auto element = domain::create_initial_element(\n      ElementId<3>{0, {SegmentId{0, 0}, SegmentId{0, 0}, SegmentId{0, 0}}},\n      domain.blocks(), std::vector<std::array<size_t, 3>>{{refinement_levels}});\n\n  // Mesh and coordinates\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using ReconstructorForTest = DummyReconstructor;\n  const size_t ghost_zone_size{ReconstructorForTest{}.ghost_zone_size()};\n\n  // Below are tags required by DirichletAnalytic boundary condition to compute\n  // inertial coords of ghost FD cells:\n  //  - time\n  //  - functions of time\n  //  - element map\n  //  - coordinate map\n  //  - subcell logical coordinates\n  // -----------------------------------------\n\n  const double time{0.5};\n\n  const std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  const ElementMap<3, Frame::Grid> logical_to_grid_map(\n      ElementId<3>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<3>{}));\n\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n\n  // dummy neighbor data to put into DataBox\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<3>::type\n      ghost_data{};\n\n  const auto direction = Direction<3>::upper_xi();\n\n  const auto ghost_logical_coords =\n      evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n          subcell_mesh, ghost_zone_size, direction);\n\n  const auto ghost_inertial_coords = (*grid_to_inertial_map)(\n      logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n  const auto interior_inertial_coords = (*grid_to_inertial_map)(\n      logical_to_grid_map(subcell_logical_coords), time, functions_of_time);\n\n  // interior evolved variables for extrapolating into the ghost zone of\n  // external boundary to compute derivatives for the Sommerfeld BC\n  const Vars interior_evolved_vars =\n      set_polynomial(interior_inertial_coords, 1);\n\n  auto& [conformal_metric, conformal_factor, a_tilde, trace_extrinsic_curvature,\n         theta, gamma_hat, lapse, shift, auxiliary_shift_b] =\n      interior_evolved_vars;\n\n  auto box = db::create<db::AddSimpleTags<\n      Ccz4::Tags::ConformalMetric<DataVector, 3>, gr::Tags::Lapse<DataVector>,\n      gr::Tags::Shift<DataVector, 3>, Ccz4::Tags::ConformalFactor<DataVector>,\n      Ccz4::Tags::ATilde<DataVector, 3>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      Ccz4::Tags::Theta<DataVector>, Ccz4::Tags::GammaHat<DataVector, 3>,\n      Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>,\n      Parallel::Tags::MetavariablesImpl<EvolutionMetaVars>,\n      domain::Tags::Domain<3>, domain::Tags::ExternalBoundaryConditions<3>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::ElementLogical>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<3>,\n      Ccz4::fd::Tags::Reconstructor, ::Tags::Time,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::ElementMap<3, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                  Frame::Inertial>>>(\n      conformal_metric, lapse, shift, conformal_factor, a_tilde,\n      trace_extrinsic_curvature, theta, gamma_hat, auxiliary_shift_b,\n      EvolutionMetaVars{}, std::move(domain), std::move(boundary_conditions),\n      subcell_mesh, subcell_logical_coords, ghost_data,\n      std::unique_ptr<Ccz4::fd::Reconstructor>{\n          std::make_unique<ReconstructorForTest>()},\n      time, clone_unique_ptrs(functions_of_time),\n      ElementMap<3, Frame::Grid>{\n          ElementId<3>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<3>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{}));\n\n  // compute FD ghost data and retrieve the result\n  fd::BoundaryConditionGhostData::apply(make_not_null(&box), element,\n                                        ReconstructorForTest{});\n  const DirectionalId<3> mortar_id = {direction,\n                                      ElementId<3>::external_boundary_id()};\n  const DataVector& fd_ghost_data =\n      get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box)\n          .at(mortar_id)\n          .neighbor_ghost_data_for_reconstruction();\n\n  const size_t num_face_pts{\n      subcell_mesh.extents().slice_away(direction.dimension()).product()};\n  Variables<System::variables_tag_list> ghost_zone_vars{num_face_pts *\n                                                        ghost_zone_size};\n\n  std::copy(fd_ghost_data.begin(),\n            std::next(fd_ghost_data.begin(),\n                      static_cast<std::ptrdiff_t>(ghost_zone_vars.size())),\n            ghost_zone_vars.data());\n\n  // now check values for each types of boundary conditions\n  if (typeid(BoundaryConditionType) ==\n      typeid(Ccz4::BoundaryConditions::DirichletAnalytic)) {\n    const auto& expected_ghost_vars = solution.variables(\n        ghost_inertial_coords, time, typename System::variables_tag_list{});\n    tmpl::for_each<System::variables_tag_list>([&]<typename Tag>(\n                                                   tmpl::type_<Tag> /*meta*/) {\n      const std::string tag_name = db::tag_name<Tag>();\n      CAPTURE(tag_name);\n      CAPTURE(ghost_inertial_coords);\n      CHECK(tuples::get<Tag>(expected_ghost_vars) == get<Tag>(ghost_zone_vars));\n    });\n\n  } else if (typeid(BoundaryConditionType) ==\n             typeid(Ccz4::BoundaryConditions::Sommerfeld)) {\n    const auto expected_ghost_vars = set_polynomial(ghost_inertial_coords, 1);\n    tmpl::for_each<Ccz4::fd::System::variables_tag_list>(\n        [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n          const std::string tag_name = db::tag_name<Tag>();\n          CAPTURE(tag_name);\n          for (auto expected_component = get<Tag>(expected_ghost_vars).cbegin(),\n                    component = get<Tag>(ghost_zone_vars).cbegin();\n               expected_component != get<Tag>(expected_ghost_vars).cend() and\n               component != get<Tag>(ghost_zone_vars).cend();\n               ++expected_component, ++component) {\n            CHECK_ITERABLE_APPROX(*component, *expected_component);\n          }\n        });\n\n  } else {\n    FAIL(\"Boundary condition type not handled in test\");\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Fd.BCondGhostData\",\n                  \"[Unit][Evolution]\") {\n  const SolutionForTest solution{};\n\n  // test DirichletAnalytic BC\n  test(TestHelpers::test_creation<Ccz4::BoundaryConditions::DirichletAnalytic,\n                                  EvolutionMetaVars>(\"AnalyticPrescription:\\n\"\n                                                     \"  Ccz4(Minkowski)\\n\"),\n       solution);\n  test(Ccz4::BoundaryConditions::DirichletAnalytic{std::make_unique<\n           Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::Minkowski<3>>>()},\n       solution);\n\n  // test Sommerfeld BC; solution is unused\n  // test(Ccz4::BoundaryConditions::Sommerfeld{}, solution);\n\n  // check that the periodic BC fails\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([&solution]() {\n        test(domain::BoundaryConditions::Periodic<\n                 Ccz4::BoundaryConditions::BoundaryCondition>{},\n             solution);\n      })(),\n      Catch::Matchers::ContainsSubstring(\"not on external boundaries\"));\n#endif\n}\n}  // namespace\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_Derivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Derivatives.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n#include \"Helpers/Evolution/Systems/Ccz4/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.FiniteDifference.Derivatives\",\n                  \"[Unit][Evolution]\") {\n  const size_t points_per_dimension = 5;\n  const size_t ghost_zone_size = 3;\n  const size_t fd_deriv_order = 4;\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_jacobian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    cell_centered_logical_to_inertial_inv_jacobian.get(i, i) = 1.0;\n  }\n  const InverseHessian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_hessian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n\n  const Element<3> element = TestHelpers::Ccz4::fd::detail::set_element();\n\n  // Testing spacetime_derivatives()\n  DirectionalIdMap<3, evolution::dg::subcell::GhostData> all_ghost_data =\n      TestHelpers::Ccz4::fd::detail::compute_ghost_data(\n          subcell_mesh, logical_coords, element.neighbors(), ghost_zone_size,\n          TestHelpers::Ccz4::fd::detail::compute_prim_solution);\n\n  auto volume_prims_for_recons =\n      TestHelpers::Ccz4::fd::detail::compute_prim_solution(logical_coords);\n  Variables<typename Ccz4::fd::System::variables_tag::tags_list>\n      volume_evolved_vars{subcell_mesh.number_of_grid_points()};\n\n  get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(\n          volume_prims_for_recons);\n  get<::Ccz4::Tags::ATilde<DataVector, 3>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::ATilde<DataVector, 3>>(volume_prims_for_recons);\n  get<::Ccz4::Tags::ConformalFactor<DataVector>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::ConformalFactor<DataVector>>(volume_prims_for_recons);\n  get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(volume_evolved_vars) =\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(\n          volume_prims_for_recons);\n  get<::Ccz4::Tags::Theta<DataVector>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::Theta<DataVector>>(volume_prims_for_recons);\n  get<::Ccz4::Tags::GammaHat<DataVector, 3>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::GammaHat<DataVector, 3>>(volume_prims_for_recons);\n  get<gr::Tags::Shift<DataVector, 3>>(volume_evolved_vars) =\n      get<gr::Tags::Shift<DataVector, 3>>(volume_prims_for_recons);\n  get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(\n          volume_prims_for_recons);\n  get<gr::Tags::Lapse<DataVector>>(volume_evolved_vars) =\n      get<gr::Tags::Lapse<DataVector>>(volume_prims_for_recons);\n\n  Variables<\n      db::wrap_tags_in<Tags::deriv, typename Ccz4::fd::System::gradients_tags,\n                       tmpl::size_t<3>, Frame::Inertial>>\n      deriv_of_Ccz4_vars{subcell_mesh.number_of_grid_points()};\n\n  ::Ccz4::fd::spacetime_derivatives(\n      make_not_null(&deriv_of_Ccz4_vars), volume_evolved_vars, all_ghost_data,\n      fd_deriv_order, subcell_mesh,\n      cell_centered_logical_to_inertial_inv_jacobian);\n\n  Variables<\n      db::wrap_tags_in<Tags::deriv, typename Ccz4::fd::System::gradients_tags,\n                       tmpl::size_t<3>, Frame::Inertial>>\n      expected_deriv_of_Ccz4_vars{subcell_mesh.number_of_grid_points()};\n\n  auto& expected_d_metric =\n      get<::Tags::deriv<::Ccz4::Tags::ConformalMetric<DataVector, 3>,\n                        tmpl::size_t<3>, Frame::Inertial>>(\n          expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        expected_d_metric.get(i, j, k) =\n            (i == j) ? static_cast<double>(10 * j + 50 * k + 1) : 0.0;\n      }\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<::Ccz4::Tags::ConformalMetric<DataVector, 3>,\n                         tmpl::size_t<3>, Frame::Inertial>>(\n          deriv_of_Ccz4_vars)),\n      expected_d_metric);\n\n  auto& expected_d_atilde =\n      get<::Tags::deriv<::Ccz4::Tags::ATilde<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        expected_d_atilde.get(i, j, k) =\n            i == j ? static_cast<double>(1000 * j + 5000 * k + 1) : 0.0;\n      }\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<::Ccz4::Tags::ATilde<DataVector, 3>, tmpl::size_t<3>,\n                         Frame::Inertial>>(deriv_of_Ccz4_vars)),\n      expected_d_atilde);\n\n  auto& expected_d_conformal_factor =\n      get<::Tags::deriv<::Ccz4::Tags::ConformalFactor<DataVector>,\n                        tmpl::size_t<3>, Frame::Inertial>>(\n          expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_d_conformal_factor.get(i) = 1;\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<::Ccz4::Tags::ConformalFactor<DataVector>,\n                         tmpl::size_t<3>, Frame::Inertial>>(\n          deriv_of_Ccz4_vars)),\n      expected_d_conformal_factor);\n\n  auto& expected_d_trace_extrinsic_curvature =\n      get<::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                        tmpl::size_t<3>, Frame::Inertial>>(\n          expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_d_trace_extrinsic_curvature.get(i) = 1;\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                         tmpl::size_t<3>, Frame::Inertial>>(\n          deriv_of_Ccz4_vars)),\n      expected_d_trace_extrinsic_curvature);\n\n  auto& expected_d_theta =\n      get<::Tags::deriv<::Ccz4::Tags::Theta<DataVector>, tmpl::size_t<3>,\n                        Frame::Inertial>>(expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_d_theta.get(i) = 1;\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<::Ccz4::Tags::Theta<DataVector>, tmpl::size_t<3>,\n                         Frame::Inertial>>(deriv_of_Ccz4_vars)),\n      expected_d_theta);\n\n  auto& expected_d_gamma_hat =\n      get<::Tags::deriv<::Ccz4::Tags::GammaHat<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_d_gamma_hat.get(i, j) = 1;\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<::Ccz4::Tags::GammaHat<DataVector, 3>, tmpl::size_t<3>,\n                         Frame::Inertial>>(deriv_of_Ccz4_vars)),\n      expected_d_gamma_hat);\n\n  auto& expected_d_lapse =\n      get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        Frame::Inertial>>(expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_d_lapse.get(i) = 1;\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                         Frame::Inertial>>(deriv_of_Ccz4_vars)),\n      expected_d_lapse);\n\n  auto& expected_d_shift =\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_d_shift.get(i, j) = 1;\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                         Frame::Inertial>>(deriv_of_Ccz4_vars)),\n      expected_d_shift);\n\n  auto& expected_d_field_b =\n      get<::Tags::deriv<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>,\n                        tmpl::size_t<3>, Frame::Inertial>>(\n          expected_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_d_field_b.get(i, j) = 1;\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>,\n                         tmpl::size_t<3>, Frame::Inertial>>(\n          deriv_of_Ccz4_vars)),\n      expected_d_field_b);\n\n  // Testing second_spacetime_derivatives()\n  all_ghost_data = TestHelpers::Ccz4::fd::detail::compute_ghost_data(\n      subcell_mesh, logical_coords, element.neighbors(), ghost_zone_size,\n      TestHelpers::Ccz4::fd::detail::compute_prim_solution_for_second_deriv);\n\n  volume_prims_for_recons =\n      TestHelpers::Ccz4::fd::detail::compute_prim_solution_for_second_deriv(\n          logical_coords);\n\n  get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(\n          volume_prims_for_recons);\n  get<::Ccz4::Tags::ATilde<DataVector, 3>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::ATilde<DataVector, 3>>(volume_prims_for_recons);\n  get<::Ccz4::Tags::ConformalFactor<DataVector>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::ConformalFactor<DataVector>>(volume_prims_for_recons);\n  get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(volume_evolved_vars) =\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(\n          volume_prims_for_recons);\n  get<::Ccz4::Tags::Theta<DataVector>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::Theta<DataVector>>(volume_prims_for_recons);\n  get<::Ccz4::Tags::GammaHat<DataVector, 3>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::GammaHat<DataVector, 3>>(volume_prims_for_recons);\n  get<gr::Tags::Shift<DataVector, 3>>(volume_evolved_vars) =\n      get<gr::Tags::Shift<DataVector, 3>>(volume_prims_for_recons);\n  get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(volume_evolved_vars) =\n      get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(\n          volume_prims_for_recons);\n  get<gr::Tags::Lapse<DataVector>>(volume_evolved_vars) =\n      get<gr::Tags::Lapse<DataVector>>(volume_prims_for_recons);\n\n  Variables<db::wrap_tags_in<::Tags::second_deriv,\n                             typename Ccz4::fd::System::gradients_tags,\n                             tmpl::size_t<3>, Frame::Inertial>>\n      second_deriv_of_Ccz4_vars{subcell_mesh.number_of_grid_points()};\n\n  ::Ccz4::fd::second_spacetime_derivatives(\n      make_not_null(&second_deriv_of_Ccz4_vars), volume_evolved_vars,\n      all_ghost_data, fd_deriv_order, subcell_mesh,\n      cell_centered_logical_to_inertial_inv_jacobian,\n      cell_centered_logical_to_inertial_inv_hessian);\n\n  Variables<db::wrap_tags_in<::Tags::second_deriv,\n                             typename Ccz4::fd::System::gradients_tags,\n                             tmpl::size_t<3>, Frame::Inertial>>\n      expected_second_deriv_of_Ccz4_vars{subcell_mesh.number_of_grid_points()};\n\n  auto& expected_second_d_metric =\n      get<::Tags::second_deriv<::Ccz4::Tags::ConformalMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t l = 0; l < 3; ++l) {\n          expected_second_d_metric.get(i, j, k, l) =\n              (i == j and i == k)\n                  ? static_cast<double>(2 * (10 * k + 50 * l + 1))\n                  : 0.0;\n        }\n      }\n    }\n  }\n\n  const Approx custom_approx =\n      Approx::custom().epsilon(1.0e-12).scale(*std::max_element(\n          volume_evolved_vars.data(),\n          volume_evolved_vars.data() + volume_evolved_vars.size() - 1));\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<::Ccz4::Tags::ConformalMetric<DataVector, 3>,\n                                tmpl::size_t<3>, Frame::Inertial>>(\n          second_deriv_of_Ccz4_vars)),\n      expected_second_d_metric, custom_approx);\n\n  auto& expected_second_d_atilde =\n      get<::Tags::second_deriv<::Ccz4::Tags::ATilde<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t l = 0; l < 3; ++l) {\n          expected_second_d_atilde.get(i, j, k, l) =\n              (i == j and i == k)\n                  ? static_cast<double>(2 * (1000 * k + 5000 * l + 1))\n                  : 0.0;\n        }\n      }\n    }\n  }\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<::Ccz4::Tags::ATilde<DataVector, 3>,\n                                tmpl::size_t<3>, Frame::Inertial>>(\n          second_deriv_of_Ccz4_vars)),\n      expected_second_d_atilde, custom_approx);\n\n  auto& expected_second_d_conformal_factor =\n      get<::Tags::second_deriv<::Ccz4::Tags::ConformalFactor<DataVector>,\n                               tmpl::size_t<3>, Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_second_d_conformal_factor.get(i, j) = i == j ? 2 : 0.0;\n    }\n  }\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<::Ccz4::Tags::ConformalFactor<DataVector>,\n                                tmpl::size_t<3>, Frame::Inertial>>(\n          second_deriv_of_Ccz4_vars)),\n      expected_second_d_conformal_factor, custom_approx);\n\n  auto& expected_second_d_trace_extrinsic_curvature =\n      get<::Tags::second_deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                               tmpl::size_t<3>, Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_second_d_trace_extrinsic_curvature.get(i, j) = i == j ? 2 : 0.0;\n    }\n  }\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                                tmpl::size_t<3>, Frame::Inertial>>(\n          second_deriv_of_Ccz4_vars)),\n      expected_second_d_trace_extrinsic_curvature, custom_approx);\n\n  auto& expected_second_d_theta =\n      get<::Tags::second_deriv<::Ccz4::Tags::Theta<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_second_d_theta.get(i, j) = i == j ? 2 : 0.0;\n    }\n  }\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<::Ccz4::Tags::Theta<DataVector>,\n                                tmpl::size_t<3>, Frame::Inertial>>(\n          second_deriv_of_Ccz4_vars)),\n      expected_second_d_theta, custom_approx);\n\n  auto& expected_second_d_gamma_hat =\n      get<::Tags::second_deriv<::Ccz4::Tags::GammaHat<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        expected_second_d_gamma_hat.get(i, j, k) = i == j ? 2 : 0.0;\n      }\n    }\n  }\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<::Ccz4::Tags::GammaHat<DataVector, 3>,\n                                tmpl::size_t<3>, Frame::Inertial>>(\n          second_deriv_of_Ccz4_vars)),\n      expected_second_d_gamma_hat, custom_approx);\n\n  auto& expected_second_d_lapse =\n      get<::Tags::second_deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      expected_second_d_lapse.get(i, j) = i == j ? 2 : 0.0;\n      if ((i == 0 and j == 2) or (i == 2 and j == 0)) {\n        expected_second_d_lapse.get(i, j) += 1.0;\n      }\n    }\n  }\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                                Frame::Inertial>>(second_deriv_of_Ccz4_vars)),\n      expected_second_d_lapse, custom_approx);\n\n  auto& expected_second_d_shift =\n      get<::Tags::second_deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        expected_second_d_shift.get(i, j, k) = i == j ? 2 : 0.0;\n      }\n    }\n  }\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                                Frame::Inertial>>(second_deriv_of_Ccz4_vars)),\n      expected_second_d_shift, custom_approx);\n\n  auto& expected_second_d_field_b =\n      get<::Tags::second_deriv<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>>(\n          expected_second_deriv_of_Ccz4_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        expected_second_d_field_b.get(i, j, k) = i == j ? 2 : 0.0;\n      }\n    }\n  }\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<::Tags::second_deriv<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>,\n                                tmpl::size_t<3>, Frame::Inertial>>(\n          second_deriv_of_Ccz4_vars)),\n      expected_second_d_field_b, custom_approx);\n\n// Test ASSERT triggers for incorrect neighbor size.\n#ifdef SPECTRE_DEBUG\n  for (const auto& direction : Direction<3>::all_directions()) {\n    const DirectionalId<3> directional_element_id{\n        direction, *element.neighbors().at(direction).begin()};\n    DirectionalIdMap<3, evolution::dg::subcell::GhostData> bad_ghost_data =\n        all_ghost_data;\n    DataVector& neighbor_data = bad_ghost_data.at(directional_element_id)\n                                    .neighbor_ghost_data_for_reconstruction();\n    neighbor_data = DataVector{2};\n    const std::string match_string{\n        MakeString{}\n        << \"Amount of reconstruction data sent (\" << neighbor_data.size()\n        << \") from \" << directional_element_id\n        << \" is not a multiple of the number of reconstruction variables \"\n        << Variables<Ccz4::fd::Tags::spacetime_reconstruction_tags>::\n               number_of_independent_components};\n    CHECK_THROWS_WITH(\n        Ccz4::fd::spacetime_derivatives(\n            make_not_null(&deriv_of_Ccz4_vars), volume_evolved_vars,\n            bad_ghost_data, fd_deriv_order, subcell_mesh,\n            cell_centered_logical_to_inertial_inv_jacobian),\n        Catch::Matchers::ContainsSubstring(match_string));\n    CHECK_THROWS_WITH(\n        Ccz4::fd::second_spacetime_derivatives(\n            make_not_null(&second_deriv_of_Ccz4_vars), volume_evolved_vars,\n            bad_ghost_data, fd_deriv_order, subcell_mesh,\n            cell_centered_logical_to_inertial_inv_jacobian,\n            cell_centered_logical_to_inertial_inv_hessian),\n        Catch::Matchers::ContainsSubstring(match_string));\n  }\n#endif  // SPECTRE_DEBUG\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_DummyReconstructor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/DummyReconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n\nnamespace Ccz4::fd {\nnamespace {\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Fd.DummyReconstructor\",\n                  \"[Unit][Evolution]\") {\n  const auto dummy_from_options_base =\n      TestHelpers::test_factory_creation<Ccz4::fd::Reconstructor,\n                                         Ccz4::fd::OptionTags::Reconstructor>(\n          \"DummyReconstructor:\\n\");\n  auto* const dummy_from_options =\n      dynamic_cast<const Ccz4::fd::DummyReconstructor*>(\n          dummy_from_options_base.get());\n  REQUIRE(dummy_from_options != nullptr);\n  CHECK(dummy_from_options->ghost_zone_size() == 3);\n}\n\n}  // namespace\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_EnforceConstrainedEvolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/EnforceConstrainedEvolution.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Ccz4::fd {\nnamespace {\n\nvoid test(bool constrained_evolution) {\n  constexpr size_t dim = System::volume_dim;\n  const size_t num_pts = 5;\n  auto conformal_spatial_metric =\n      make_with_value<tnsr::ii<DataVector, dim>>(num_pts, 0.);\n  auto a_tilde = make_with_value<tnsr::ii<DataVector, dim>>(num_pts, 0.);\n\n  for (size_t i = 0; i < dim; ++i) {\n    conformal_spatial_metric.get(i, i) = DataVector{num_pts, 2.};\n    a_tilde.get(i, i) = DataVector{num_pts, 1.};\n  }\n\n  auto box = db::create<\n      db::AddSimpleTags<::Ccz4::fd::Tags::ConstrainedEvolution,\n                        ::Ccz4::Tags::ConformalMetric<DataVector, dim>,\n                        ::Ccz4::Tags::ATilde<DataVector, dim>>>(\n      constrained_evolution, conformal_spatial_metric, a_tilde);\n\n  for (size_t i = 0; i < dim; ++i) {\n    for (size_t j = i; j < dim; ++j) {\n      if (i == j) {\n        CHECK_ITERABLE_APPROX(\n            (get<::Ccz4::Tags::ConformalMetric<DataVector, dim>>(box))\n                .get(i, j),\n            (DataVector{num_pts, 2.}));\n        CHECK_ITERABLE_APPROX(\n            (get<::Ccz4::Tags::ATilde<DataVector, dim>>(box)).get(i, j),\n            (DataVector{num_pts, 1.}));\n      } else {\n        CHECK_ITERABLE_APPROX(\n            (get<::Ccz4::Tags::ConformalMetric<DataVector, dim>>(box))\n                .get(i, j),\n            (DataVector{num_pts, 0.}));\n        CHECK_ITERABLE_APPROX(\n            (get<::Ccz4::Tags::ATilde<DataVector, dim>>(box)).get(i, j),\n            (DataVector{num_pts, 0.}));\n      }\n    }\n  }\n\n  db::mutate_apply<EnforceConstrainedEvolution>(make_not_null(&box));\n\n  for (size_t i = 0; i < dim; ++i) {\n    if (get<::Ccz4::fd::Tags::ConstrainedEvolution>(box)) {\n      conformal_spatial_metric.get(i, i) = DataVector{num_pts, 1.};\n      a_tilde.get(i, i) = DataVector{num_pts, 0.};\n    } else {\n      conformal_spatial_metric.get(i, i) = DataVector{num_pts, 2.};\n      a_tilde.get(i, i) = DataVector{num_pts, 1.};\n    }\n  }\n  for (size_t i = 0; i < dim; ++i) {\n    for (size_t j = i; j < dim; ++j) {\n      CHECK_ITERABLE_APPROX(\n          (get<::Ccz4::Tags::ConformalMetric<DataVector, dim>>(box)).get(i, j),\n          conformal_spatial_metric.get(i, j));\n      CHECK_ITERABLE_APPROX(\n          (get<::Ccz4::Tags::ATilde<DataVector, dim>>(box)).get(i, j),\n          a_tilde.get(i, j));\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Fd.ECE\", \"[Unit][Evolution]\") {\n  test(true);\n  test(false);\n}\n}  // namespace\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_Filter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Filter.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Ccz4/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid set_polynomial(\n    const gsl::not_null<std::vector<DataVector>*> vars_ptr,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& local_logical_coords,\n    const size_t degree) {\n  for (auto& var : *vars_ptr) {\n    var = 0.0;\n    for (size_t i = 0; i < Dim; ++i) {\n      var += pow(local_logical_coords.get(i), degree);\n    }\n  }\n}\n\ntemplate <typename System>\nvoid set_solution(\n    const gsl::not_null<Variables<typename System::variables_tag::tags_list>*>\n        volume_vars,\n    const gsl::not_null<DirectionalIdMap<3, evolution::dg::subcell::GhostData>*>\n        neighbor_data,\n    const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& logical_coords,\n    const size_t deriv_order, const size_t degree) {\n  const auto set_data_vectors = [](const gsl::not_null<std::vector<DataVector>*>\n                                       local_dvs,\n                                   const auto local_vars) {\n    for (size_t i = 0; i < 6; ++i) {\n      (*local_dvs)[i].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 1; ++i) {\n      (*local_dvs)[i + 10].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::ConformalFactor<DataVector>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 6; ++i) {\n      (*local_dvs)[i + 11].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::ATilde<DataVector, 3>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 1; ++i) {\n      (*local_dvs)[i + 17].set_data_ref(make_not_null(\n          &get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 1; ++i) {\n      (*local_dvs)[i + 18].set_data_ref(\n          make_not_null(&get<::Ccz4::Tags::Theta<DataVector>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      (*local_dvs)[i + 19].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::GammaHat<DataVector, 3>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 1; ++i) {\n      (*local_dvs)[i + 6].set_data_ref(\n          make_not_null(&get<gr::Tags::Lapse<DataVector>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      (*local_dvs)[i + 7].set_data_ref(\n          make_not_null(&get<gr::Tags::Shift<DataVector, 3>>(*local_vars)[i]));\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      (*local_dvs)[i + 22].set_data_ref(make_not_null(\n          &get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(*local_vars)[i]));\n    }\n  };\n\n  std::vector<DataVector> vars(25);\n  set_data_vectors(make_not_null(&vars), volume_vars);\n  set_polynomial(&vars, logical_coords, degree);\n\n  for (const auto& direction : Direction<3>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    std::vector<DataVector> neighbor_dvs(25);\n    Variables<::Ccz4::fd::System::variables_tag_list> neighbor_vars{\n        mesh.number_of_grid_points()};\n\n    set_data_vectors(make_not_null(&neighbor_dvs),\n                     make_not_null(&neighbor_vars));\n    set_polynomial(&neighbor_dvs, neighbor_logical_coords, degree);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars), mesh.extents(), deriv_order / 2 + 1,\n        std::unordered_set{direction.opposite()}, 0, {});\n    CAPTURE(deriv_order / 2 + 1);\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    const auto key = DirectionalId<3>{direction, ElementId<3>{0}};\n    (*neighbor_data)[key] = evolution::dg::subcell::GhostData{1};\n    (*neighbor_data)[key].neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n}\n\nvoid test(const bool evolve_lapse_and_shift) {\n  const size_t points_per_dimension = 5;\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n\n  using System = ::Ccz4::fd::System;\n\n  Variables<System::variables_tag_list> volume_evolved_variables{\n      subcell_mesh.number_of_grid_points()};\n\n  DirectionalIdMap<3, evolution::dg::subcell::GhostData>\n      neighbor_data_for_reconstruction{};\n\n  set_solution<System>(&volume_evolved_variables,\n                       &neighbor_data_for_reconstruction, subcell_mesh,\n                       logical_coords, 4, 3);\n  Variables<System::variables_tag_list> result = volume_evolved_variables;\n\n  Ccz4::fd::ccz4_kreiss_oliger_filter(\n      make_not_null(&result), volume_evolved_variables,\n      neighbor_data_for_reconstruction, evolve_lapse_and_shift, subcell_mesh, 4,\n      1.0);\n\n  if (not evolve_lapse_and_shift) {\n    CHECK(get<gr::Tags::Lapse<DataVector>>(result) ==\n          get<gr::Tags::Lapse<DataVector>>(volume_evolved_variables));\n    CHECK(get<gr::Tags::Shift<DataVector, 3>>(result) ==\n          get<gr::Tags::Shift<DataVector, 3>>(volume_evolved_variables));\n    CHECK(get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(result) ==\n          get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(\n              volume_evolved_variables));\n  } else {\n    CHECK(get<gr::Tags::Lapse<DataVector>>(result) !=\n          get<gr::Tags::Lapse<DataVector>>(volume_evolved_variables));\n    CHECK(get<gr::Tags::Shift<DataVector, 3>>(result) !=\n          get<gr::Tags::Shift<DataVector, 3>>(volume_evolved_variables));\n    CHECK(get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(result) !=\n          get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(\n              volume_evolved_variables));\n  }\n\n  tmpl::for_each<System::variables_tag_list>(\n      [&result, &volume_evolved_variables](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        auto& result_tensor = get<tag>(result);\n        auto& volume_tensor = get<tag>(volume_evolved_variables);\n        CAPTURE(pretty_type::name<tag>());\n        const Approx custom_approx =\n          Approx::custom().epsilon(1.0e-12).scale(1.0);\n        for (size_t tensor_index = 0; tensor_index < result_tensor.size();\n             ++tensor_index) {\n          result_tensor[tensor_index] -= volume_tensor[tensor_index];\n          CHECK_ITERABLE_CUSTOM_APPROX(\n              result_tensor[tensor_index],\n              DataVector(result_tensor[tensor_index].size(), 0.0),\n              custom_approx);\n        }\n      });\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Fd.Filters\",\n                  \"[Unit][Evolution]\") {\n  test(true);\n  test(false);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/GhostData.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Fd.GhostData\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  const std::uniform_real_distribution<> dist(-1.0, 1.0);\n\n  const size_t points_per_dimension = 5;\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  const auto random_vars_subcell = make_with_random_values<\n      Variables<::Ccz4::fd::System::variables_tag_list>>(\n      make_not_null(&gen), dist, subcell_mesh.number_of_grid_points());\n  auto box_subcell =\n      db::create<db::AddSimpleTags<::Ccz4::fd::System::variables_tag>>(\n          random_vars_subcell);\n\n  DataVector retrieved_vars_subcell =\n      db::mutate_apply<::Ccz4::fd::GhostVariables>(make_not_null(&box_subcell),\n                                                   2_st);\n\n  const Variables<::Ccz4::fd::System::variables_tag_list> retrieved_vars{\n      retrieved_vars_subcell.data(), retrieved_vars_subcell.size() - 2};\n  tmpl::for_each<::Ccz4::fd::System::variables_tag_list>(\n      [&random_vars_subcell, &retrieved_vars](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<tag>(random_vars_subcell),\n                              get<tag>(retrieved_vars));\n      });\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_SoTimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/Ccz4/ATilde.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/Ccz4/BoundaryConditions/Sommerfeld.hpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivChristoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Derivatives.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/DummyReconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/SoTimeDerivative.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/Ccz4/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Evolution/Systems/Ccz4/Ccz4WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Ccz4::fd {\nnamespace {\n\ntemplate <bool EnableSubcell>\nstruct DummyEvolutionMetaVars {\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled_at_external_boundary = EnableSubcell;\n  };\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<BoundaryConditions::BoundaryCondition,\n                             BoundaryConditions::standard_boundary_conditions>>;\n  };\n};\n\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\n// Test second order CCZ4 in Minkowski spacetime\nvoid test_minkowski(const bool evolve_lapse_and_shift) {\n  // set up subcell grid\n  const size_t SpatialDim = 3;\n  using FrameType = Frame::Inertial;\n  const size_t points_per_dimension = 5;\n  const Ccz4::fd::DummyReconstructor recons{};\n  const size_t ghost_zone_size = recons.ghost_zone_size();\n  const Mesh<SpatialDim> subcell_mesh{points_per_dimension,\n                                      Spectral::Basis::FiniteDifference,\n                                      Spectral::Quadrature::CellCentered};\n\n  const std::array<double, SpatialDim> lower_bound{-2., 0., -0.5};\n  const std::array<double, SpatialDim> upper_bound{2., 2., -0.1};\n  const std::array<double, SpatialDim> coords_range = upper_bound - lower_bound;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, FrameType>(Affine3D{\n          Affine{-1., 1., lower_bound[0], upper_bound[0]},\n          Affine{-1., 1., lower_bound[1], upper_bound[1]},\n          Affine{-1., 1., lower_bound[2], upper_bound[2]},\n      });\n  // set up displaced logical coords\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n  const auto x = coord_map(logical_coords);\n  InverseJacobian<DataVector, SpatialDim, Frame::ElementLogical,\n                  Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_jacobian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    cell_centered_logical_to_inertial_inv_jacobian.get(i, i) =\n        2.0 / gsl::at(coords_range, i);\n  }\n\n  // We use a trivial inverse hessian here as this test is testing the time\n  // derivative. The only place where the inverse hessian is used in time\n  // derivative is in second_spacetime_derivatives, which is already tested in\n  // Test_Derivatives.cpp.\n  InverseHessian<DataVector, SpatialDim, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_hessian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n\n  const Element<SpatialDim> element =\n      TestHelpers::Ccz4::fd::detail::set_element();\n\n  const DirectionalIdMap<SpatialDim, evolution::dg::subcell::GhostData>\n      all_ghost_data =\n          TestHelpers::Ccz4::fd::detail::compute_ghost_data<Frame::Inertial>(\n              subcell_mesh, x, element.neighbors(), ghost_zone_size,\n              TestHelpers::Ccz4::fd::detail::Minkowski::\n                  compute_prim_solution_for_Minkowski<false>,\n              coords_range);\n\n  // Get system evolved variables\n  // Use the physical inertial coords\n  const auto evolved_vars = TestHelpers::Ccz4::fd::detail::Minkowski::\n      compute_prim_solution_for_Minkowski(x);\n\n  const DataVector used_for_size =\n      DataVector(subcell_mesh.number_of_grid_points(),\n                 std::numeric_limits<double>::signaling_NaN());\n  const auto k_0 = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto eta = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto upper_spatial_z4_constraint =\n      make_with_value<tnsr::I<DataVector, 3>>(\n          used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n  const double kappa_1 = 0.1;\n  const double kappa_2 = 0.2;\n  const double kappa_3 = 0.3;\n\n  // put needed quantities into databox\n  using dt_variables_tag =\n      db::add_tag_prefix<::Tags::dt, Ccz4::fd::System::variables_tag>;\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Ccz4::Tags::Kappa1, ::Ccz4::Tags::Kappa2, ::Ccz4::Tags::Kappa3,\n      ::Ccz4::fd::Tags::EvolveLapseAndShift, domain::Tags::Element<SpatialDim>,\n      fd::Tags::Reconstructor,\n      Parallel::Tags::MetavariablesImpl<DummyEvolutionMetaVars<false>>,\n      Ccz4::fd::System::variables_tag, ::Ccz4::Tags::Eta<DataVector>,\n      ::Ccz4::Tags::K0<DataVector>,\n      ::Ccz4::Tags::SpatialZ4ConstraintUp<DataVector, 3>, dt_variables_tag,\n      evolution::dg::subcell::Tags::Mesh<SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<SpatialDim>,\n      domain::Tags::ExternalBoundaryConditions<SpatialDim>,\n      evolution::dg::subcell::Tags::Coordinates<SpatialDim, Frame::Inertial>>>(\n      kappa_1, kappa_2, kappa_3, evolve_lapse_and_shift, element,\n      std::unique_ptr<Ccz4::fd::Reconstructor>{\n          std::make_unique<std::decay_t<decltype(recons)>>(recons)},\n      DummyEvolutionMetaVars<false>{}, evolved_vars, eta, k_0,\n      upper_spatial_z4_constraint,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian,\n      cell_centered_logical_to_inertial_inv_hessian, all_ghost_data,\n      std::vector<DirectionMap<\n          SpatialDim,\n          std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>{},\n      x);\n  // Check that all time derivatives are 0\n  ::Ccz4::fd::SoTimeDerivative::apply(make_not_null(&box));\n  const auto zero = DataVector(used_for_size.size(), 0.0);\n\n  tmpl::for_each<Ccz4::fd::System::variables_tag_list>(\n    [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        const std::string tag_name = db::tag_name<::Tags::dt<Tag>>();\n        CAPTURE(tag_name);\n        for (auto& component : get<::Tags::dt<Tag>>(box)) {\n            CHECK_ITERABLE_APPROX(component, zero);\n        }\n  });\n}\n\n// Test second-order CCZ4 in KerrSchild spacetime\n//\n// evolve_shift: whether or not to evolve the shift (always true for SO-CCZ4);\n// slicing_condition_type: which slicing condition to use (always 1+log for\n// SO-CCZ4)\nvoid test_kerrschild(const bool evolve_lapse_and_shift) {\n  const bool evolve_shift = evolve_lapse_and_shift;\n  const Ccz4::SlicingConditionType slicing_condition_type =\n      Ccz4::SlicingConditionType::Log;  // always use 1+log slicing\n\n  // set up subcell grid\n  const size_t SpatialDim = 3;\n  using FrameType = Frame::Inertial;\n  const size_t points_per_dimension = 20;\n  const Ccz4::fd::DummyReconstructor recons{};\n  const size_t ghost_zone_size = recons.ghost_zone_size();\n  const Mesh<SpatialDim> subcell_mesh{points_per_dimension,\n                                      Spectral::Basis::FiniteDifference,\n                                      Spectral::Quadrature::CellCentered};\n\n  const std::array<double, SpatialDim> lower_bound{0.8, 1., 1.3};\n  const std::array<double, SpatialDim> upper_bound{1.2, 1.2, 1.4};\n  const std::array<double, SpatialDim> coords_range = upper_bound - lower_bound;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, FrameType>(Affine3D{\n          Affine{-1., 1., lower_bound[0], upper_bound[0]},\n          Affine{-1., 1., lower_bound[1], upper_bound[1]},\n          Affine{-1., 1., lower_bound[2], upper_bound[2]},\n      });\n  // set up displaced logical coords\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n  const auto x = coord_map(logical_coords);\n  InverseJacobian<DataVector, SpatialDim, Frame::ElementLogical,\n                  Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_jacobian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    cell_centered_logical_to_inertial_inv_jacobian.get(i, i) =\n        2.0 / gsl::at(coords_range, i);\n  }\n\n  // We use a trivial inverse hessian here as this test is testing the time\n  // derivative. The only place where the inverse hessian is used in time\n  // derivative is in second_spacetime_derivatives, which is already tested in\n  // Test_Derivatives.cpp.\n  InverseHessian<DataVector, SpatialDim, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_hessian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n\n  const Element<SpatialDim> element =\n      TestHelpers::Ccz4::fd::detail::set_element();\n\n  // Setup solution\n  const double mass = 2.0;\n  const std::array<double, SpatialDim> spin{{0.2, 0.4, 0.8}};\n  const std::array<double, SpatialDim> center{{0.2, 0.5, 0.1}};\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  const double f = Ccz4::fd::System::f;\n\n  const DirectionalIdMap<SpatialDim, evolution::dg::subcell::GhostData>\n      all_ghost_data =\n          TestHelpers::Ccz4::fd::detail::compute_ghost_data<Frame::Inertial>(\n              subcell_mesh, x, element.neighbors(), ghost_zone_size,\n              TestHelpers::Ccz4::fd::detail::KerrSchild::\n                  compute_prim_solution_for_KerrSchild,\n              coords_range, t, f, evolve_shift, solution);\n\n  const auto evolved_vars = TestHelpers::Ccz4::fd::detail::KerrSchild::\n      compute_prim_solution_for_KerrSchild(x, t, f, evolve_shift, solution);\n\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(evolved_vars);\n  const auto d_lapse = partial_derivative(\n      lapse, subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian);\n  const DataVector used_for_size =\n      DataVector(subcell_mesh.number_of_grid_points(),\n                 std::numeric_limits<double>::signaling_NaN());\n  const auto eta = make_with_value<Scalar<DataVector>>(\n      used_for_size, 0.1);                      // change eta to non-zero later\n  const Scalar<DataVector> slicing_condition =  // g(\\alpha)\n      TestHelpers::Ccz4::fd::detail::KerrSchild::get_slicing_condition(\n          slicing_condition_type, lapse);\n  const auto k_0 = TestHelpers::Ccz4::fd::detail::KerrSchild::get_k_0_kerr(\n      get<gr::Tags::Shift<DataVector, SpatialDim>>(evolved_vars), lapse,\n      d_lapse, slicing_condition,\n      get<::Ccz4::Tags::Theta<DataVector>>(evolved_vars),\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars));\n  const auto upper_spatial_z4_constraint =\n      make_with_value<tnsr::I<DataVector, 3>>(\n          used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n  const double kappa_1 = 0.1;\n  const double kappa_2 = 0.2;\n  const double kappa_3 = 0.3;\n\n  // put needed quantities into databox\n  using dt_variables_tag =\n      db::add_tag_prefix<::Tags::dt, Ccz4::fd::System::variables_tag>;\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Ccz4::Tags::Kappa1, ::Ccz4::Tags::Kappa2, ::Ccz4::Tags::Kappa3,\n      ::Ccz4::fd::Tags::EvolveLapseAndShift, domain::Tags::Element<SpatialDim>,\n      fd::Tags::Reconstructor,\n      Parallel::Tags::MetavariablesImpl<DummyEvolutionMetaVars<false>>,\n      Ccz4::fd::System::variables_tag, ::Ccz4::Tags::Eta<DataVector>,\n      ::Ccz4::Tags::K0<DataVector>,\n      ::Ccz4::Tags::SpatialZ4ConstraintUp<DataVector, 3>, dt_variables_tag,\n      evolution::dg::subcell::Tags::Mesh<SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<SpatialDim>,\n      domain::Tags::ExternalBoundaryConditions<SpatialDim>,\n      evolution::dg::subcell::Tags::Coordinates<SpatialDim, Frame::Inertial>>>(\n      kappa_1, kappa_2, kappa_3, evolve_lapse_and_shift, element,\n      std::unique_ptr<Ccz4::fd::Reconstructor>{\n          std::make_unique<std::decay_t<decltype(recons)>>(recons)},\n      DummyEvolutionMetaVars<false>{}, evolved_vars, eta, k_0,\n      upper_spatial_z4_constraint,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian,\n      cell_centered_logical_to_inertial_inv_hessian, all_ghost_data,\n      std::vector<DirectionMap<\n          SpatialDim,\n          std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>{},\n      x);\n  // Check that all time derivatives are 0\n  ::Ccz4::fd::SoTimeDerivative::apply(make_not_null(&box));\n  const auto zero = DataVector(used_for_size.size(), 0.0);\n  const Approx custom_approx =\n      Approx::custom().epsilon(1.0e-9).scale(*std::max_element(\n          evolved_vars.data(), evolved_vars.data() + evolved_vars.size() - 1));\n\n  // Remove dt_b from the list, which will be checked separately below\n  tmpl::for_each<tmpl::remove<Ccz4::fd::System::variables_tag_list,\n    ::Ccz4::Tags::AuxiliaryShiftB<DataVector, SpatialDim>>>(\n      [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        const std::string tag_name = db::tag_name<::Tags::dt<Tag>>();\n        CAPTURE(tag_name);\n        for (auto& component : get<::Tags::dt<Tag>>(box)) {\n          CHECK_ITERABLE_CUSTOM_APPROX(component, zero, custom_approx);\n        }\n      });\n\n  // eq 12i\n  // \\partial_t b will not be 0 for KerrSchild if evolve_shift == true\n  // since we assume the shift is time-independent for testing\n  // but KerrSchild is not stationary under 1+log slicing\n  const auto d_gamma_hat = partial_derivative(\n      get<::Ccz4::Tags::GammaHat<DataVector, SpatialDim>>(box), subcell_mesh,\n      cell_centered_logical_to_inertial_inv_jacobian);\n  const auto d_b = partial_derivative(\n      get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, SpatialDim>>(box),\n      subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian);\n  const tnsr::I<DataVector, SpatialDim, FrameType> dt_b_expected =\n      TestHelpers::Ccz4::fd::detail::KerrSchild::get_dt_b_kerr_expected(\n          evolve_shift, get<::Ccz4::Tags::Eta<DataVector>>(box),\n          get<gr::Tags::Shift<DataVector, SpatialDim>>(box), d_gamma_hat,\n          get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, SpatialDim>>(box), d_b);\n  const auto& dt_b_actual =\n      get<::Tags::dt<::Ccz4::Tags::AuxiliaryShiftB<DataVector, SpatialDim>>>(\n          box);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_b_actual, dt_b_expected, custom_approx);\n}\n\n// Test second-order CCZ4 in a GaugePlaneWave spacetime\nvoid test_gauge_plane_wave(\n    const std::array<double, 3>& wave_vector,\n    std::unique_ptr<MathFunction<1, Frame::Inertial>> profile, const double t,\n    const bool evolve_lapse_and_shift) {\n  // set up subcell grid\n  const size_t SpatialDim = 3;\n  using FrameType = Frame::Inertial;\n  const size_t points_per_dimension = 20;\n  const Ccz4::fd::DummyReconstructor recons{};\n  const size_t ghost_zone_size = recons.ghost_zone_size();\n  const Mesh<SpatialDim> subcell_mesh{points_per_dimension,\n                                      Spectral::Basis::FiniteDifference,\n                                      Spectral::Quadrature::CellCentered};\n  const std::array<double, SpatialDim> lower_bound{-0.5, -2., 1.};\n  const std::array<double, SpatialDim> upper_bound{0.0, 2., 2.};\n  const std::array<double, SpatialDim> coords_range = upper_bound - lower_bound;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, FrameType>(Affine3D{\n          Affine{-1., 1., lower_bound[0], upper_bound[0]},\n          Affine{-1., 1., lower_bound[1], upper_bound[1]},\n          Affine{-1., 1., lower_bound[2], upper_bound[2]},\n      });\n  // set up displaced logical coords\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n  const auto x = coord_map(logical_coords);\n  InverseJacobian<DataVector, SpatialDim, Frame::ElementLogical,\n                  Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_jacobian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    cell_centered_logical_to_inertial_inv_jacobian.get(i, i) =\n        2.0 / gsl::at(coords_range, i);\n  }\n\n  // We use a trivial inverse hessian here as this test is testing the time\n  // derivative. The only place where the inverse hessian is used in time\n  // derivative is in second_spacetime_derivatives, which is already tested in\n  // Test_Derivatives.cpp.\n  InverseHessian<DataVector, SpatialDim, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_hessian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n\n  double omega = 0.0;\n  for (const auto& k : wave_vector) {\n    omega += square(k);\n  }\n  omega = pow(omega, 1.0 / 2.0);\n\n  const DataVector used_for_size =\n      DataVector(subcell_mesh.number_of_grid_points(),\n                 std::numeric_limits<double>::signaling_NaN());\n\n  tnsr::i<DataVector, SpatialDim, FrameType> k_tnsr{};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    k_tnsr.get(i) =\n        make_with_value<DataVector>(used_for_size, gsl::at(wave_vector, i));\n  }\n\n  const gr::Solutions::GaugePlaneWave<SpatialDim>::IntermediateVars<DataVector>\n      intermediate_sol(wave_vector, profile, omega, x, t);\n  const Scalar<DataVector> h{intermediate_sol.h};\n  const Scalar<DataVector> du_h{intermediate_sol.du_h};\n  const Scalar<DataVector> du_du_h{intermediate_sol.du_du_h};\n\n  // Setup solutions\n  const gr::Solutions::GaugePlaneWave<SpatialDim> solution(wave_vector,\n                                                           std::move(profile));\n  const auto gauge_plane_wave_vars = solution.variables(\n      x, t,\n      typename gr::Solutions::GaugePlaneWave<SpatialDim>::tags<DataVector>{});\n\n  const Element<SpatialDim> element =\n      TestHelpers::Ccz4::fd::detail::set_element();\n\n  const DirectionalIdMap<SpatialDim, evolution::dg::subcell::GhostData>\n      all_ghost_data = TestHelpers::Ccz4::fd::detail::compute_ghost_data(\n          subcell_mesh, x, element.neighbors(), ghost_zone_size,\n          TestHelpers::Ccz4::fd::detail::GaugePlaneWave::\n              compute_prim_solution_for_GaugePlaneWave,\n          coords_range, t, solution, intermediate_sol);\n\n  const auto evolved_vars = TestHelpers::Ccz4::fd::detail::GaugePlaneWave::\n      compute_prim_solution_for_GaugePlaneWave(x, t, solution,\n                                               intermediate_sol);\n\n  const auto& k_0 =\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars);\n\n  const auto eta = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto upper_spatial_z4_constraint =\n      make_with_value<tnsr::I<DataVector, 3>>(\n          used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n  const double kappa_1 = 0.1;\n  const double kappa_2 = 0.2;\n  const double kappa_3 = 0.3;\n\n  // put needed quantities into databox\n  using dt_variables_tag =\n      db::add_tag_prefix<::Tags::dt, Ccz4::fd::System::variables_tag>;\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Ccz4::Tags::Kappa1, ::Ccz4::Tags::Kappa2, ::Ccz4::Tags::Kappa3,\n      ::Ccz4::fd::Tags::EvolveLapseAndShift, domain::Tags::Element<SpatialDim>,\n      fd::Tags::Reconstructor,\n      Parallel::Tags::MetavariablesImpl<DummyEvolutionMetaVars<false>>,\n      Ccz4::fd::System::variables_tag, ::Ccz4::Tags::Eta<DataVector>,\n      ::Ccz4::Tags::K0<DataVector>,\n      ::Ccz4::Tags::SpatialZ4ConstraintUp<DataVector, 3>, dt_variables_tag,\n      evolution::dg::subcell::Tags::Mesh<SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<SpatialDim>,\n      domain::Tags::ExternalBoundaryConditions<SpatialDim>,\n      evolution::dg::subcell::Tags::Coordinates<SpatialDim, Frame::Inertial>>>(\n      kappa_1, kappa_2, kappa_3, evolve_lapse_and_shift, element,\n      std::unique_ptr<Ccz4::fd::Reconstructor>{\n          std::make_unique<std::decay_t<decltype(recons)>>(recons)},\n      DummyEvolutionMetaVars<false>{}, evolved_vars, eta, k_0,\n      upper_spatial_z4_constraint,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian,\n      cell_centered_logical_to_inertial_inv_hessian, all_ghost_data,\n      std::vector<DirectionMap<\n          SpatialDim,\n          std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>{},\n      x);\n  // Check all time derivatives\n  ::Ccz4::fd::SoTimeDerivative::apply(make_not_null(&box));\n\n  const Approx custom_approx =\n      Approx::custom().epsilon(1.0e-9).scale(*std::max_element(\n          evolved_vars.data(), evolved_vars.data() + evolved_vars.size() - 1));\n  // eq 12a\n  const auto& dt_conformal_spatial_metric_actual =\n      get<::Tags::dt<::Ccz4::Tags::ConformalMetric<DataVector, SpatialDim>>>(\n          box);\n\n  const auto conformal_factor =\n      get(get<::Ccz4::Tags::ConformalFactor<DataVector>>(evolved_vars));\n  Scalar<DataVector> conformal_factor_squared{};\n  get(conformal_factor_squared) = square(conformal_factor);\n  const auto& dt_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim>>>(\n          gauge_plane_wave_vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim, FrameType>>(\n          gauge_plane_wave_vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          gauge_plane_wave_vars);\n  const auto dt_conformal_spatial_metric_expected = TestHelpers::Ccz4::fd::\n      detail::GaugePlaneWave::get_dt_conformal_spatial_metric_gauge_plane_wave(\n          conformal_factor_squared, spatial_metric, inverse_spatial_metric,\n          dt_spatial_metric);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_conformal_spatial_metric_actual,\n                               dt_conformal_spatial_metric_expected,\n                               custom_approx);\n\n  // eq 12b\n  const auto& dt_lapse_actual =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(box);\n  const auto& d_lapse =\n      get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<SpatialDim>,\n                        FrameType>>(gauge_plane_wave_vars);\n  auto dt_lapse_expected = make_with_value<Scalar<DataVector>>(\n          used_for_size, 0.0);\n  if (evolve_lapse_and_shift) {\n      dt_lapse_expected = TestHelpers::Ccz4::fd::detail::GaugePlaneWave::\n      get_dt_lapse_gauge_plane_wave(\n          d_lapse, get<gr::Tags::Shift<DataVector, SpatialDim, FrameType>>(\n                       gauge_plane_wave_vars));\n  }\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_lapse_actual, dt_lapse_expected,\n                               custom_approx);\n\n  // eq 12c\n  const auto& dt_shift_actual =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, SpatialDim>>>(box);\n  const auto& d_shift =\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, FrameType>,\n                        tmpl::size_t<SpatialDim>, FrameType>>(\n          gauge_plane_wave_vars);\n  auto dt_shift_expected = make_with_value<tnsr::I<DataVector, 3>>(\n        used_for_size, 0.0);\n  if (evolve_lapse_and_shift) {\n    dt_shift_expected = TestHelpers::Ccz4::fd::detail::GaugePlaneWave::\n        get_dt_shift_gauge_plane_wave(\n            get<gr::Tags::Shift<DataVector, SpatialDim, FrameType>>(\n                gauge_plane_wave_vars),\n            d_shift);\n  }\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_shift_actual, dt_shift_expected,\n                               custom_approx);\n\n  // eq 12d\n  const auto& dt_conformal_factor_actual =\n      get<::Tags::dt<::Ccz4::Tags::ConformalFactor<DataVector>>>(box);\n  const auto dt_conformal_factor_expected = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_conformal_factor_gauge_plane_wave(\n          inverse_spatial_metric, dt_spatial_metric,\n          Scalar<DataVector>(conformal_factor));\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_conformal_factor_actual,\n                               dt_conformal_factor_expected, custom_approx);\n\n  // eq 12e\n  const auto& dt_a_tilde_actual =\n      get<::Tags::dt<::Ccz4::Tags::ATilde<DataVector, SpatialDim>>>(box);\n  const auto dt_conformal_factor = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_conformal_factor_gauge_plane_wave(\n          inverse_spatial_metric, dt_spatial_metric,\n          Scalar<DataVector>(conformal_factor));\n  const auto one_plus_h_times_omega_squared = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_one_plus_h_times_omega_squared(h, omega);\n  const auto dt_extrinsic_curvature = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_extrinsic_curvature_gauge_plane_wave(\n          k_tnsr, du_h, du_du_h, one_plus_h_times_omega_squared, omega);\n\n  const auto dt_inverse_spatial_metric = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_inverse_spatial_metric(inverse_spatial_metric,\n                                                    dt_spatial_metric);\n  const auto dt_trace_extrinsic_curvature_expected = TestHelpers::Ccz4::fd::\n      detail::GaugePlaneWave::get_dt_trace_extrinsic_curvature_gauge_plane_wave(\n          get<gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim>>(\n              gauge_plane_wave_vars),\n          dt_extrinsic_curvature, inverse_spatial_metric,\n          dt_inverse_spatial_metric);\n  const auto dt_a_tilde_expected = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_a_tilde_gauge_plane_wave(\n          Scalar<DataVector>(conformal_factor), conformal_factor_squared,\n          dt_conformal_factor, spatial_metric, dt_spatial_metric,\n          get<gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim>>(\n              gauge_plane_wave_vars),\n          dt_extrinsic_curvature,\n          get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars),\n          dt_trace_extrinsic_curvature_expected);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_a_tilde_actual, dt_a_tilde_expected,\n                               custom_approx);\n\n  // eq 12f\n  const auto& dt_trace_extrinsic_curvature_actual =\n      get<::Tags::dt<gr::Tags::TraceExtrinsicCurvature<DataVector>>>(box);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_trace_extrinsic_curvature_actual,\n                               dt_trace_extrinsic_curvature_expected,\n                               custom_approx);\n\n  // eq 12g\n  const auto& dt_theta_actual =\n      get<::Tags::dt<::Ccz4::Tags::Theta<DataVector>>>(box);\n  const auto& dt_theta_expected =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_theta_actual, dt_theta_expected,\n                               custom_approx);\n\n  // eq 12h\n  const auto& dt_gamma_hat_actual =\n      get<::Tags::dt<::Ccz4::Tags::GammaHat<DataVector, SpatialDim>>>(box);\n  const auto dt_conformal_spatial_metric = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_conformal_spatial_metric_gauge_plane_wave(\n          conformal_factor_squared, spatial_metric, inverse_spatial_metric,\n          dt_spatial_metric);\n  const auto inverse_conformal_spatial_metric =\n      determinant_and_inverse(\n          get<::Ccz4::Tags::ConformalMetric<DataVector, SpatialDim>>(\n              evolved_vars))\n          .second;\n  const auto dt_inverse_conformal_spatial_metric = TestHelpers::Ccz4::fd::\n      detail::GaugePlaneWave::get_dt_inverse_conformal_spatial_metric(\n          inverse_conformal_spatial_metric, dt_conformal_spatial_metric);\n  const auto dt_d_spatial_metric =\n      TestHelpers::Ccz4::fd::detail::GaugePlaneWave::\n          get_dt_d_spatial_metric_gauge_plane_wave(k_tnsr, du_du_h, omega);\n  const auto& d_spatial_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim>,\n                        tmpl::size_t<SpatialDim>, FrameType>>(\n          gauge_plane_wave_vars);\n  const auto d_conformal_factor = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_d_conformal_factor_gauge_plane_wave(\n          inverse_spatial_metric, d_spatial_metric,\n          Scalar<DataVector>(conformal_factor));\n  const auto dt_d_conformal_factor = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_d_conformal_factor_gauge_plane_wave(\n          Scalar<DataVector>(conformal_factor), dt_conformal_factor,\n          inverse_spatial_metric, dt_inverse_spatial_metric, d_spatial_metric,\n          dt_d_spatial_metric);\n  const auto dt_d_conformal_spatial_metric = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_d_conformal_spatial_metric_gauge_plane_wave(\n          spatial_metric, dt_spatial_metric, d_spatial_metric,\n          dt_d_spatial_metric, Scalar<DataVector>(conformal_factor),\n          dt_conformal_factor, d_conformal_factor, dt_d_conformal_factor);\n  const auto det_spatial_metric = determinant(spatial_metric);\n  const auto d_det_spatial_metric = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_d_det_spatial_metric_gauge_plane_wave(\n          det_spatial_metric,\n          get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim,\n                                             FrameType>>(gauge_plane_wave_vars),\n          get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim>,\n                            tmpl::size_t<SpatialDim>, FrameType>>(\n              gauge_plane_wave_vars));\n  const tnsr::ijj<DataVector, SpatialDim, FrameType>\n      d_conformal_spatial_metric = TestHelpers::Ccz4::fd::detail::KerrSchild::\n          get_d_conformal_spatial_metric(conformal_factor_squared,\n                                         spatial_metric, d_spatial_metric,\n                                         d_det_spatial_metric);\n  const auto dt_gamma_hat_expected = TestHelpers::Ccz4::fd::detail::\n      GaugePlaneWave::get_dt_gamma_hat_gauge_plane_wave(\n          inverse_conformal_spatial_metric, dt_inverse_conformal_spatial_metric,\n          d_conformal_spatial_metric, dt_d_conformal_spatial_metric);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_gamma_hat_actual, dt_gamma_hat_expected,\n                               custom_approx);\n\n  // eq 12i\n  auto dt_b_expected =\n        make_with_value<tnsr::I<DataVector, 3>>(used_for_size, 0.0);\n  if (evolve_lapse_and_shift) {\n    const tnsr::ijj<DataVector, SpatialDim, FrameType> field_d =\n        TestHelpers::Ccz4::fd::detail::KerrSchild::get_field_d(\n            d_conformal_spatial_metric);\n    const tnsr::iJJ<DataVector, SpatialDim, FrameType> field_d_up =\n        TestHelpers::Ccz4::fd::detail::GaugePlaneWave::get_field_d_up(\n            inverse_conformal_spatial_metric, field_d);\n    const auto d_field_d = partial_derivative(\n        field_d, subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian);\n    const auto d_conformal_christoffel_second_kind =\n        ::Ccz4::deriv_conformal_christoffel_second_kind(\n            inverse_conformal_spatial_metric, field_d, d_field_d, field_d_up);\n    const auto conformal_christoffel_second_kind =\n        ::Ccz4::conformal_christoffel_second_kind(\n            inverse_conformal_spatial_metric, field_d);\n    const auto d_gamma_hat =\n        ::Ccz4::deriv_contracted_conformal_christoffel_second_kind(\n            inverse_conformal_spatial_metric, field_d_up,\n            conformal_christoffel_second_kind,\n            d_conformal_christoffel_second_kind);\n    dt_b_expected = TestHelpers::\n        Ccz4::fd::detail::GaugePlaneWave::get_dt_b_gauge_plane_wave_expected(\n            dt_gamma_hat_expected, d_gamma_hat,\n            get<gr::Tags::Shift<DataVector, SpatialDim, FrameType>>(\n                gauge_plane_wave_vars));\n  }\n  const auto& dt_b_actual =\n      get<::Tags::dt<::Ccz4::Tags::AuxiliaryShiftB<DataVector, SpatialDim>>>(\n          box);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_b_actual, dt_b_expected, custom_approx);\n}\n\nvoid test_sommerfeld_bc(const bool evolve_lapse_and_shift) {\n  const size_t SpatialDim = 3;\n  using FrameType = Frame::Inertial;\n  const size_t points_per_dimension = 5;\n  const Ccz4::fd::DummyReconstructor recons{};\n  const size_t ghost_zone_size = recons.ghost_zone_size();\n  const Mesh<SpatialDim> subcell_mesh{points_per_dimension,\n                                      Spectral::Basis::FiniteDifference,\n                                      Spectral::Quadrature::CellCentered};\n\n  const std::array<double, SpatialDim> lower_bound{0.8, 0.7, -2.1};\n  const std::array<double, SpatialDim> upper_bound{1.0, 1.5, -0.9};\n  const std::array<double, SpatialDim> coords_range = upper_bound - lower_bound;\n\n  // Create an element with an external boundary by omitting the last neighbor\n  // in the upper_zeta direction.\n  const Element<SpatialDim> element =\n      TestHelpers::Ccz4::fd::detail::set_element(true);\n\n  // we cannot declare element_map const because it is not copyable into the box\n  // NOLINTNEXTLINE(misc-const-correctness)\n  ElementMap element_map{\n      element.id(),\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                   Affine{-1., 1., lower_bound[2], upper_bound[2]}})\n          .get_clone()};\n\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, FrameType>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n  const auto x = grid_to_inertial_map(element_map(logical_coords));\n\n  InverseJacobian<DataVector, SpatialDim, Frame::ElementLogical,\n                  Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_jacobian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    cell_centered_logical_to_inertial_inv_jacobian.get(i, i) =\n        2.0 / gsl::at(coords_range, i);\n  }\n\n  // We use a trivial inverse hessian here as this test is testing the time\n  // derivative. The only place where the inverse hessian is used in time\n  // derivative is in second_spacetime_derivatives, which is already tested in\n  // Test_Derivatives.cpp.\n  InverseHessian<DataVector, SpatialDim, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_hessian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n\n  // Ghost data from interior neighbors (none for the external face, which is\n  // set by BCs)\n  const DirectionalIdMap<SpatialDim, evolution::dg::subcell::GhostData>\n      all_ghost_data =\n          TestHelpers::Ccz4::fd::detail::compute_ghost_data<Frame::Inertial>(\n              subcell_mesh, x, element.neighbors(), ghost_zone_size,\n              TestHelpers::Ccz4::fd::detail::Minkowski::\n                  compute_prim_solution_for_Minkowski<false>,\n              coords_range);\n\n  // Minkowski evolved variables\n  auto evolved_vars = TestHelpers::Ccz4::fd::detail::Minkowski::\n      compute_prim_solution_for_Minkowski(x);\n\n  const DataVector radial_coords =\n      sqrt(square(get<0>(x)) + square(get<1>(x)) + square(get<2>(x)));\n\n  const DataVector used_for_size(subcell_mesh.number_of_grid_points(),\n                                 std::numeric_limits<double>::signaling_NaN());\n  // set dummy k_0 value to get non-trivial lapse evolution for testing\n  const auto k_0 = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto eta = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto upper_spatial_z4_constraint =\n      make_with_value<tnsr::I<DataVector, 3>>(\n          used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n  const double kappa_1 = 0.1;\n  const double kappa_2 = 0.2;\n  const double kappa_3 = 0.3;\n\n  using dt_variables_tag =\n      db::add_tag_prefix<::Tags::dt, Ccz4::fd::System::variables_tag>;\n\n  // Provide BC on the external face (upper_zeta)\n  std::vector<DirectionMap<\n      SpatialDim,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      external_bcs_per_block(1);\n  external_bcs_per_block[0][Direction<SpatialDim>::upper_zeta()] =\n      std::make_unique<Ccz4::BoundaryConditions::Sommerfeld>();\n\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Ccz4::Tags::Kappa1, ::Ccz4::Tags::Kappa2, ::Ccz4::Tags::Kappa3,\n      ::Ccz4::fd::Tags::EvolveLapseAndShift, domain::Tags::Element<SpatialDim>,\n      fd::Tags::Reconstructor,\n      Parallel::Tags::MetavariablesImpl<DummyEvolutionMetaVars<true>>,\n      Ccz4::fd::System::variables_tag, ::Ccz4::Tags::Eta<DataVector>,\n      ::Ccz4::Tags::K0<DataVector>,\n      ::Ccz4::Tags::SpatialZ4ConstraintUp<DataVector, 3>, dt_variables_tag,\n      evolution::dg::subcell::Tags::Mesh<SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<SpatialDim>,\n      domain::Tags::ExternalBoundaryConditions<SpatialDim>,\n      evolution::dg::subcell::Tags::Coordinates<SpatialDim, Frame::Inertial>,\n      ::Tags::Time, domain::Tags::FunctionsOfTime,\n      domain::Tags::ElementMap<SpatialDim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<SpatialDim, Frame::Grid,\n                                                  Frame::Inertial>>>(\n      kappa_1, kappa_2, kappa_3, evolve_lapse_and_shift, element,\n      std::unique_ptr<Ccz4::fd::Reconstructor>{\n          std::make_unique<std::decay_t<decltype(recons)>>(recons)},\n      DummyEvolutionMetaVars<true>{}, evolved_vars, eta, k_0,\n      upper_spatial_z4_constraint,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian,\n      cell_centered_logical_to_inertial_inv_hessian, all_ghost_data,\n      std::move(external_bcs_per_block), x, 0.0, std::move(functions_of_time),\n      std::move(element_map), grid_to_inertial_map.get_clone());\n\n  ::Ccz4::fd::SoTimeDerivative::apply(make_not_null(&box));\n\n  const size_t num_pts = subcell_mesh.number_of_grid_points();\n  const size_t num_face_pts = subcell_mesh.extents(0) * subcell_mesh.extents(1);\n\n  // Compute r on the upper zeta slice\n  const size_t num_unaffected_pts = num_pts - num_face_pts;\n  DataVector r_affected{};\n  make_const_view<DataVector>(make_not_null(&r_affected), radial_coords,\n                              num_unaffected_pts, num_face_pts);\n\n  {\n    // Points not in upper zeta slice should be unaffected (zero dt for\n    // Minkowski)\n    const auto unaffected_expected = DataVector(num_pts - num_face_pts, 0.0);\n    const auto& dt_lapse = get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(box);\n\n    DataVector unaffected_actual{};\n    make_const_view<DataVector>(make_not_null(&unaffected_actual),\n                                get(dt_lapse), 0, num_unaffected_pts);\n    CHECK_ITERABLE_APPROX(unaffected_actual, unaffected_expected);\n\n    DataVector affected_actual{};\n    make_const_view<DataVector>(make_not_null(&affected_actual), get(dt_lapse),\n                                num_unaffected_pts, num_face_pts);\n    // Points in upper zeta slice has expected dt = -1/r for lapse in Minkowski\n    const DataVector affected_expected = -1.0 / r_affected;\n    CHECK_ITERABLE_APPROX(affected_actual, affected_expected);\n  }\n\n  {\n    // Points not in upper zeta slice should be unaffected (zero dt for\n    // Minkowski)\n    const auto unaffected_expected = DataVector(num_pts - num_face_pts, 0.0);\n    const auto& dt_conformal_factor =\n        get<::Tags::dt<::Ccz4::Tags::ConformalFactor<DataVector>>>(box);\n\n    DataVector unaffected_actual{};\n    make_const_view<DataVector>(make_not_null(&unaffected_actual),\n                                get(dt_conformal_factor), 0,\n                                num_unaffected_pts);\n    CHECK_ITERABLE_APPROX(unaffected_actual, unaffected_expected);\n\n    DataVector affected_actual{};\n    make_const_view<DataVector>(make_not_null(&affected_actual),\n                                get(dt_conformal_factor), num_unaffected_pts,\n                                num_face_pts);\n    // Points in upper zeta slice has expected dt = -1/r for conformal factor in\n    // Minkowski\n    const DataVector affected_expected = -1.0 / r_affected;\n    CHECK_ITERABLE_APPROX(affected_actual, affected_expected);\n  }\n\n  {\n    // Points not in upper zeta slice should be unaffected (zero dt for\n    // Minkowski)\n    const auto unaffected_expected = DataVector(num_pts - num_face_pts, 0.0);\n    const auto& dt_conformal_metric =\n        get<::Tags::dt<::Ccz4::Tags::ConformalMetric<DataVector, SpatialDim>>>(\n            box);\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        DataVector unaffected_actual{};\n        make_const_view<DataVector>(make_not_null(&unaffected_actual),\n                                    dt_conformal_metric.get(i, j), 0,\n                                    num_unaffected_pts);\n        CHECK_ITERABLE_APPROX(unaffected_actual, unaffected_expected);\n\n        DataVector affected_actual{};\n        make_const_view<DataVector>(make_not_null(&affected_actual),\n                                    dt_conformal_metric.get(i, j),\n                                    num_unaffected_pts, num_face_pts);\n        // Points in upper zeta slice has expected dt = -1/r for diagonal in\n        // Minkowski\n        const DataVector affected_expected =\n            (i == j) ? (-1.0 / r_affected) : DataVector(num_face_pts, 0.0);\n        CHECK_ITERABLE_APPROX(affected_actual, affected_expected);\n      }\n    }\n  }\n\n  // all other evolved variables must have zero dt everywhere\n  {\n    const Approx custom_approx = Approx::custom().epsilon(1.0e-13).scale(1.0);\n    const DataVector all_expected{num_pts, 0.0};\n    tmpl::for_each<Ccz4::fd::System::variables_tag_list>(\n        [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n          // Skip variables already checked above: lapse, conformal factor,\n          // conformal metric\n          if constexpr (std::is_same_v<Tag, gr::Tags::Lapse<DataVector>> ||\n                        std::is_same_v<\n                            Tag, ::Ccz4::Tags::ConformalFactor<DataVector>> ||\n                        std::is_same_v<Tag, ::Ccz4::Tags::ConformalMetric<\n                                                DataVector, SpatialDim>>) {\n            return;\n          }\n          const std::string tag_name = db::tag_name<::Tags::dt<Tag>>();\n          CAPTURE(tag_name);\n          for (auto& component : get<::Tags::dt<Tag>>(box)) {\n            // dt_theta and dt_trace_extrinsic_curvature are slightly non-zero\n            // which is likely due to round-off error.\n            CHECK_ITERABLE_CUSTOM_APPROX(component, all_expected,\n                                         custom_approx);\n          }\n        });\n  }\n\n  // now change the lapse to some dummy values to test the radial deriv term in\n  // Sommerfeld BC\n  db::mutate<\n      Ccz4::fd::System::variables_tag, ::Ccz4::Tags::K0<DataVector>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<SpatialDim>>(\n      [&](const auto evolved_var_ptr, const auto k_0_ptr,\n          const auto all_ghost_data_ptr) {\n        get(get<gr::Tags::Lapse<DataVector>>(*evolved_var_ptr)) = get<0>(x);\n        get(*k_0_ptr) = DataVector(num_pts, 1.0);\n        *all_ghost_data_ptr =\n            TestHelpers::Ccz4::fd::detail::compute_ghost_data<Frame::Inertial>(\n                subcell_mesh, x, element.neighbors(), ghost_zone_size,\n                TestHelpers::Ccz4::fd::detail::Minkowski::\n                    compute_prim_solution_for_Minkowski<true>,\n                coords_range);\n      },\n      make_not_null(&box));\n\n  ::Ccz4::fd::SoTimeDerivative::apply(make_not_null(&box));\n\n  {\n    // Points not in upper zeta slice should be unaffected (value dependent on\n    // evolve_lapse_and_shift)\n    DataVector dt_lapse_expected = -2.0 * get<0>(x) / radial_coords;\n    for (size_t i = 0; i < num_unaffected_pts; ++i) {\n      dt_lapse_expected[i] = evolve_lapse_and_shift ? 2.0 * get<0>(x)[i] : 0.0;\n    }\n    DataVector dt_lapse_unaffected_expected{};\n    make_const_view<DataVector>(make_not_null(&dt_lapse_unaffected_expected),\n                                dt_lapse_expected, 0, num_unaffected_pts);\n\n    const auto& dt_lapse = get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(box);\n\n    DataVector unaffected_actual{};\n    make_const_view<DataVector>(make_not_null(&unaffected_actual),\n                                get(dt_lapse), 0, num_unaffected_pts);\n    CHECK_ITERABLE_APPROX(unaffected_actual, dt_lapse_unaffected_expected);\n\n    DataVector affected_actual{};\n    make_const_view<DataVector>(make_not_null(&affected_actual), get(dt_lapse),\n                                num_unaffected_pts, num_face_pts);\n    // Points in upper zeta slice has expected dt = -2*x/r for dummy lapse in\n    // Minkowski\n    DataVector dt_lapse_affected_expected{};\n    make_const_view<DataVector>(make_not_null(&dt_lapse_affected_expected),\n                                dt_lapse_expected, num_unaffected_pts,\n                                num_face_pts);\n    CHECK_ITERABLE_APPROX(affected_actual, dt_lapse_affected_expected);\n  }\n}\n\nvoid test_dirichlet_analytic_bc(const bool evolve_lapse_and_shift) {\n  const size_t SpatialDim = 3;\n  using FrameType = Frame::Inertial;\n  const size_t points_per_dimension = 5;\n  const Ccz4::fd::DummyReconstructor recons{};\n  const size_t ghost_zone_size = recons.ghost_zone_size();\n  const Mesh<SpatialDim> subcell_mesh{points_per_dimension,\n                                      Spectral::Basis::FiniteDifference,\n                                      Spectral::Quadrature::CellCentered};\n\n  const std::array<double, SpatialDim> lower_bound{0.8, 0.7, -2.1};\n  const std::array<double, SpatialDim> upper_bound{1.0, 1.5, -0.9};\n  const std::array<double, SpatialDim> coords_range = upper_bound - lower_bound;\n\n  // Create an element with an external boundary by omitting the last neighbor\n  // in the upper_zeta direction.\n  const Element<SpatialDim> element =\n      TestHelpers::Ccz4::fd::detail::set_element(true);\n\n  // we cannot declare element_map const because it is not copyable into the box\n  // NOLINTNEXTLINE(misc-const-correctness)\n  ElementMap element_map{\n      element.id(),\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                   Affine{-1., 1., lower_bound[2], upper_bound[2]}})\n          .get_clone()};\n\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, FrameType>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const auto logical_coords =\n      TestHelpers::Ccz4::fd::detail::set_logical_coordinates(subcell_mesh);\n  const auto x = grid_to_inertial_map(element_map(logical_coords));\n\n  InverseJacobian<DataVector, SpatialDim, Frame::ElementLogical,\n                  Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_jacobian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    cell_centered_logical_to_inertial_inv_jacobian.get(i, i) =\n        2.0 / gsl::at(coords_range, i);\n  }\n\n  // We use a trivial inverse hessian here as this test is testing the time\n  // derivative. The only place where the inverse hessian is used in time\n  // derivative is in second_spacetime_derivatives, which is already tested in\n  // Test_Derivatives.cpp.\n  InverseHessian<DataVector, SpatialDim, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_hessian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n\n  // Ghost data from interior neighbors (none for the external face, which is\n  // set by BCs)\n  const DirectionalIdMap<SpatialDim, evolution::dg::subcell::GhostData>\n      all_ghost_data =\n          TestHelpers::Ccz4::fd::detail::compute_ghost_data<Frame::Inertial>(\n              subcell_mesh, x, element.neighbors(), ghost_zone_size,\n              TestHelpers::Ccz4::fd::detail::Minkowski::\n                  compute_prim_solution_for_Minkowski<false>,\n              coords_range);\n\n  // Minkowski evolved variables\n  auto evolved_vars = TestHelpers::Ccz4::fd::detail::Minkowski::\n      compute_prim_solution_for_Minkowski(x);\n\n  const DataVector radial_coords =\n      sqrt(square(get<0>(x)) + square(get<1>(x)) + square(get<2>(x)));\n\n  const DataVector used_for_size(subcell_mesh.number_of_grid_points(),\n                                 std::numeric_limits<double>::signaling_NaN());\n  // set dummy k_0 value to get non-trivial lapse evolution for testing\n  const auto k_0 = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto eta = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto upper_spatial_z4_constraint =\n      make_with_value<tnsr::I<DataVector, 3>>(\n          used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n  const double kappa_1 = 0.1;\n  const double kappa_2 = 0.2;\n  const double kappa_3 = 0.3;\n\n  using dt_variables_tag =\n      db::add_tag_prefix<::Tags::dt, Ccz4::fd::System::variables_tag>;\n\n  // Provide BC on the external face (upper_zeta)\n  std::vector<DirectionMap<\n      SpatialDim,\n      std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      external_bcs_per_block(1);\n  external_bcs_per_block[0][Direction<SpatialDim>::upper_zeta()] =\n      std::make_unique<Ccz4::BoundaryConditions::DirichletAnalytic>(\n          std::make_unique<\n              Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::Minkowski<3>>>(\n              Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::Minkowski<3>>{}));\n\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Ccz4::Tags::Kappa1, ::Ccz4::Tags::Kappa2, ::Ccz4::Tags::Kappa3,\n      ::Ccz4::fd::Tags::EvolveLapseAndShift, domain::Tags::Element<SpatialDim>,\n      fd::Tags::Reconstructor,\n      Parallel::Tags::MetavariablesImpl<DummyEvolutionMetaVars<true>>,\n      Ccz4::fd::System::variables_tag, ::Ccz4::Tags::Eta<DataVector>,\n      ::Ccz4::Tags::K0<DataVector>,\n      ::Ccz4::Tags::SpatialZ4ConstraintUp<DataVector, 3>, dt_variables_tag,\n      evolution::dg::subcell::Tags::Mesh<SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::fd::Tags::InverseHessianLogicalToInertial<\n          SpatialDim>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<SpatialDim>,\n      domain::Tags::ExternalBoundaryConditions<SpatialDim>,\n      evolution::dg::subcell::Tags::Coordinates<SpatialDim, Frame::Inertial>,\n      ::Tags::Time, domain::Tags::FunctionsOfTime,\n      domain::Tags::ElementMap<SpatialDim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<SpatialDim, Frame::Grid,\n                                                  Frame::Inertial>>>(\n      kappa_1, kappa_2, kappa_3, evolve_lapse_and_shift, element,\n      std::unique_ptr<Ccz4::fd::Reconstructor>{\n          std::make_unique<std::decay_t<decltype(recons)>>(recons)},\n      DummyEvolutionMetaVars<true>{}, evolved_vars, eta, k_0,\n      upper_spatial_z4_constraint,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian,\n      cell_centered_logical_to_inertial_inv_hessian, all_ghost_data,\n      std::move(external_bcs_per_block), x, 0.0, std::move(functions_of_time),\n      std::move(element_map), grid_to_inertial_map.get_clone());\n\n  ::Ccz4::fd::SoTimeDerivative::apply(make_not_null(&box));\n\n  {\n    const size_t num_pts = subcell_mesh.number_of_grid_points();\n    const Approx custom_approx = Approx::custom().epsilon(1.0e-13).scale(1.0);\n    const DataVector all_expected{num_pts, 0.0};\n    tmpl::for_each<Ccz4::fd::System::variables_tag_list>(\n        [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n          const std::string tag_name = db::tag_name<::Tags::dt<Tag>>();\n          CAPTURE(tag_name);\n          for (auto& component : get<::Tags::dt<Tag>>(box)) {\n            // dt_theta and dt_trace_extrinsic_curvature are slightly non-zero\n            // which is likely due to round-off error.\n            CHECK_ITERABLE_CUSTOM_APPROX(component, all_expected,\n                                         custom_approx);\n          }\n        });\n  }\n}\n\nvoid test() {\n  test_minkowski(true);\n  test_kerrschild(true);\n  test_minkowski(false);\n  test_kerrschild(false);\n\n  const std::array<double, 3> k{{0.5, 0.1, -0.2}};\n  test_gauge_plane_wave(\n      k,\n      std::make_unique<MathFunctions::Sinusoid<1, Frame::Inertial>>(0.6, 0.8,\n                                                                    2.0),\n      0.4, true);\n  test_gauge_plane_wave(\n      k,\n      std::make_unique<MathFunctions::Sinusoid<1, Frame::Inertial>>(0.6, 0.8,\n                                                                    2.0),\n      0.4, false);\n\n  // Run Diridchlet BC test\n  test_dirichlet_analytic_bc(true);\n  test_dirichlet_analytic_bc(false);\n  // Run Sommerfeld BC test\n  test_sommerfeld_bc(true);\n  test_sommerfeld_bc(false);\n}\n}  // namespace\n\n// The tests run relatively long as we use much higher spatial\n// resolution (~8000 grid points per element) to reach a relative\n// error of 1e-9.\n// [[TimeOut, 40]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.FiniteDifference.TimeDerivative\",\n                  \"[Unit][Evolution]\") {\n  test();\n}\n}  // namespace Ccz4::fd\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/FiniteDifference/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct ArbitraryFrame;\n\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<Ccz4::fd::Tags::Reconstructor>(\n      \"Reconstructor\");\n  TestHelpers::db::test_simple_tag<Ccz4::fd::Tags::EvolveLapseAndShift>(\n      \"EvolveLapseAndShift\");\n  TestHelpers::db::test_simple_tag<Ccz4::fd::Tags::ConstrainedEvolution>(\n      \"ConstrainedEvolution\");\n  TestHelpers::db::test_simple_tag<Ccz4::fd::Tags::KreissOligerEpsilon>(\n      \"KreissOligerEpsilon\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.fd.Ccz4.Tags\", \"[Unit][Evolution]\") {\n  test_simple_tags();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Ricci.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef deriv_christoffel_second_kind(\n    d_conformal_christoffel_second_kind,\n    conformal_spatial_metric,\n    inverse_conformal_spatial_metric,\n    field_d,\n    field_d_up,\n    field_p,\n    d_field_p,\n):\n    return (\n        d_conformal_christoffel_second_kind\n        + 2.0\n        * (\n            np.einsum(\n                \"kml,ijl->kmij\",\n                field_d_up,\n                (\n                    np.einsum(\"jl,i\", conformal_spatial_metric, field_p)\n                    + np.einsum(\"il,j\", conformal_spatial_metric, field_p)\n                    - np.einsum(\"ij,l\", conformal_spatial_metric, field_p)\n                ),\n            )\n            - np.einsum(\n                \"ml,ijkl->kmij\",\n                inverse_conformal_spatial_metric,\n                (\n                    np.einsum(\"kjl,i\", field_d, field_p)\n                    + np.einsum(\"kil,j\", field_d, field_p)\n                    - np.einsum(\"kij,l\", field_d, field_p)\n                ),\n            )\n        )\n        - np.einsum(\n            \"ml,ijkl->kmij\",\n            inverse_conformal_spatial_metric,\n            (\n                np.einsum(\"jl,ki\", conformal_spatial_metric, d_field_p)\n                + np.einsum(\"jl,ik\", conformal_spatial_metric, d_field_p)\n                + np.einsum(\"il,kj\", conformal_spatial_metric, d_field_p)\n                + np.einsum(\"il,jk\", conformal_spatial_metric, d_field_p)\n                - np.einsum(\"ij,kl\", conformal_spatial_metric, d_field_p)\n                - np.einsum(\"ij,lk\", conformal_spatial_metric, d_field_p)\n            ),\n        )\n        / 2.0\n    )\n\n\ndef spatial_ricci_tensor(\n    christoffel_second_kind,\n    d_conformal_christoffel_second_kind,\n    conformal_spatial_metric,\n    inverse_conformal_spatial_metric,\n    field_d,\n    field_d_up,\n    field_p,\n    d_field_p,\n):\n    d_christoffel_second_kind = deriv_christoffel_second_kind(\n        d_conformal_christoffel_second_kind,\n        conformal_spatial_metric,\n        inverse_conformal_spatial_metric,\n        field_d,\n        field_d_up,\n        field_p,\n        d_field_p,\n    )\n\n    return (\n        np.einsum(\"mmij\", d_christoffel_second_kind)\n        - np.einsum(\"jmim\", d_christoffel_second_kind)\n        + np.einsum(\"lij,mlm\", christoffel_second_kind, christoffel_second_kind)\n        - np.einsum(\"lim,mlj\", christoffel_second_kind, christoffel_second_kind)\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/RicciScalarPlusDivergenceZ4Constraint.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef ricci_scalar_plus_divergence_z4_constraint(\n    conformal_factor_squared,\n    inverse_conformal_spatial_metric,\n    spatial_ricci_tensor,\n    grad_spatial_z4_constraint,\n):\n    return conformal_factor_squared * np.einsum(\n        \"ij,ij\",\n        inverse_conformal_spatial_metric,\n        spatial_ricci_tensor\n        + grad_spatial_z4_constraint\n        + np.einsum(\"ji\", grad_spatial_z4_constraint),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_ATilde.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/ATilde.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_a_tilde(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ii<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const Scalar<DataType>&)>(\n          &Ccz4::a_tilde<DataType, Dim, Frame::Inertial>),\n      \"ATilde\", \"a_tilde\", {{{-1., 1.}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.ATilde\", \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"Evolution/Systems/Ccz4/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_a_tilde, (1, 2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_Ccz4WrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/ATilde.hpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Evolution/Systems/Ccz4/Ccz4WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   tmpl::list<MathFunctions::PowX<1, Frame::Inertial>>>>;\n  };\n};\n\ntemplate <typename SolutionType>\nvoid test_copy_and_move(const SolutionType& solution) {\n  test_copy_semantics(solution);\n  auto solution_copy2 = solution;\n  // clang-tidy: std::move of trivially copyable type\n  test_move_semantics(std::move(solution_copy2), solution);  // NOLINT\n}\n\ntemplate <typename SolutionType>\nvoid test_ccz4_solution(\n    const SolutionType& solution,\n    const Ccz4::Solutions::Ccz4WrappedGr<SolutionType>& wrapped_solution) {\n  const DataVector data_vector{3.0, 4.0};\n  const tnsr::I<DataVector, SolutionType::volume_dim, Frame::Inertial> x{\n      data_vector};\n  // Don't set time to signaling NaN, since not all solutions tested here\n  // are static\n  const double t = 44.44;\n\n  // Check that the wrapped solution returns the same variables as\n  // the solution\n  const auto vars = solution.variables(\n      x, t, typename SolutionType::template tags<DataVector>{});\n  const auto wrapped_vars = wrapped_solution.variables(\n      x, t, typename SolutionType::template tags<DataVector>{});\n\n  tmpl::for_each<typename SolutionType::template tags<DataVector>>(\n      [&vars, &wrapped_vars](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        CHECK(get<tag>(vars) == get<tag>(wrapped_vars));\n      });\n\n  // Check that the wrapped solution returns the correct extra Ccz4 tags\n  const auto wrapped_Ccz4_vars = wrapped_solution.variables(\n      x, t,\n      tmpl::list<\n          Ccz4::Tags::ConformalMetric<DataVector, SolutionType::volume_dim>,\n          Ccz4::Tags::ConformalFactor<DataVector>,\n          Ccz4::Tags::ATilde<DataVector, SolutionType::volume_dim>,\n          gr::Tags::TraceExtrinsicCurvature<DataVector>,\n          Ccz4::Tags::Theta<DataVector>,\n          Ccz4::Tags::GammaHat<DataVector, SolutionType::volume_dim>,\n          Ccz4::Tags::AuxiliaryShiftB<DataVector, SolutionType::volume_dim>>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SolutionType::volume_dim>>(vars);\n  const auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(vars);\n  Scalar<DataVector> conformal_factor;\n  get(conformal_factor) = pow(get(sqrt_det_spatial_metric), -1. / 3.);\n  CHECK(conformal_factor ==\n        get<Ccz4::Tags::ConformalFactor<DataVector>>(wrapped_Ccz4_vars));\n\n  tnsr::ii<DataVector, SolutionType::volume_dim> conformal_spatial_metric;\n  ::tenex::evaluate<ti::i, ti::j>(\n      make_not_null(&conformal_spatial_metric),\n      conformal_factor() * conformal_factor() * spatial_metric(ti::i, ti::j));\n  CHECK(conformal_spatial_metric ==\n        get<Ccz4::Tags::ConformalMetric<DataVector, SolutionType::volume_dim>>(\n            wrapped_Ccz4_vars));\n\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataVector, SolutionType::volume_dim>>(\n          vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, SolutionType::volume_dim>>(\n          vars);\n  const auto trace_extrinsic_curvature =\n      trace(extrinsic_curvature, inverse_spatial_metric);\n  CHECK(trace_extrinsic_curvature ==\n        get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(wrapped_Ccz4_vars));\n\n  Scalar<DataVector> conformal_factor_squared;\n  get(conformal_factor_squared) = pow(get(sqrt_det_spatial_metric), -2. / 3.);\n  const auto a_tilde =\n      ::Ccz4::a_tilde(conformal_factor_squared, spatial_metric,\n                      extrinsic_curvature, trace_extrinsic_curvature);\n  CHECK(a_tilde ==\n        get<Ccz4::Tags::ATilde<DataVector, SolutionType::volume_dim>>(\n            wrapped_Ccz4_vars));\n\n  const auto theta =\n      make_with_value<Scalar<DataVector>>(conformal_factor_squared, 0.0);\n  CHECK(theta == get<Ccz4::Tags::Theta<DataVector>>(wrapped_Ccz4_vars));\n\n  tnsr::II<DataVector, SolutionType::volume_dim>\n      inverse_conformal_spatial_metric;\n  ::tenex::evaluate<ti::I, ti::J>(\n      make_not_null(&inverse_conformal_spatial_metric),\n      inverse_spatial_metric(ti::I, ti::J) / conformal_factor_squared());\n  const auto& d_spatial_metric = get<\n      Tags::deriv<gr::Tags::SpatialMetric<DataVector, SolutionType::volume_dim>,\n                  tmpl::size_t<SolutionType::volume_dim>, Frame::Inertial>>(\n      vars);\n  tnsr::i<DataVector, SolutionType::volume_dim> d_det_spatial_metric;\n  ::tenex::evaluate<ti::i>(make_not_null(&d_det_spatial_metric),\n                           sqrt_det_spatial_metric() *\n                               sqrt_det_spatial_metric() *\n                               inverse_spatial_metric(ti::J, ti::K) *\n                               d_spatial_metric(ti::i, ti::j, ti::k));\n  Scalar<DataVector> det_spatial_metric_to_minus_four_thirds;\n  get(det_spatial_metric_to_minus_four_thirds) =\n      pow(get(sqrt_det_spatial_metric), -8. / 3.);\n  tnsr::ijj<DataVector, SolutionType::volume_dim> field_d;\n  ::tenex::evaluate<ti::i, ti::j, ti::k>(\n      make_not_null(&field_d),\n      0.5 * conformal_factor_squared() * d_spatial_metric(ti::i, ti::j, ti::k) -\n          0.5 * spatial_metric(ti::j, ti::k) * d_det_spatial_metric(ti::i) *\n              det_spatial_metric_to_minus_four_thirds() / 3.);\n  const auto conformal_christoffel_second_kind =\n      ::Ccz4::conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, field_d);\n  const auto contracted_conformal_christoffel_second_kind =\n      ::Ccz4::contracted_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, conformal_christoffel_second_kind);\n  // we don't use CHECK here for we switch to tenex to evaluate field_d\n  // from the for loops in Ccz4WrappedGr.cpp\n  CHECK_ITERABLE_APPROX(\n      contracted_conformal_christoffel_second_kind,\n      (get<Ccz4::Tags::GammaHat<DataVector, SolutionType::volume_dim>>(\n          wrapped_Ccz4_vars)));\n\n  const double one_over_f = 1. / ::Ccz4::fd::System::f;\n  const bool shifting_shift = ::Ccz4::fd::System::shifting_shift;\n  tnsr::I<DataVector, Ccz4::Solutions::Ccz4WrappedGr<SolutionType>::volume_dim>\n      auxiliary_shift_b;\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, SolutionType::volume_dim>>(vars);\n  using DerivShift =\n      ::Tags::deriv<gr::Tags::Shift<DataVector, SolutionType::volume_dim>,\n                    tmpl::size_t<SolutionType::volume_dim>, Frame::Inertial>;\n  const auto& d_shift = get<DerivShift>(vars);\n  const auto& dt_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, SolutionType::volume_dim>>>(\n          vars);\n  if (shifting_shift) {\n    ::tenex::evaluate<ti::I>(\n        make_not_null(&auxiliary_shift_b),\n        one_over_f * dt_shift(ti::I) -\n            one_over_f * shift(ti::K) * d_shift(ti::k, ti::I));\n  } else {\n    ::tenex::evaluate<ti::I>(make_not_null(&auxiliary_shift_b),\n                             one_over_f * dt_shift(ti::I));\n  }\n  CHECK(auxiliary_shift_b ==\n        get<Ccz4::Tags::AuxiliaryShiftB<DataVector, SolutionType::volume_dim>>(\n            wrapped_Ccz4_vars));\n\n  // Weak test of operators == and !=\n  CHECK(wrapped_solution == wrapped_solution);\n  CHECK_FALSE(wrapped_solution != wrapped_solution);\n\n  if constexpr (std::is_same_v<SolutionType,\n                               gr::Solutions::GaugePlaneWave<3>>) {\n    register_factory_classes_with_charm<Metavariables>();\n  }\n\n  test_serialization(wrapped_solution);\n  test_copy_and_move(wrapped_solution);\n}\n\ntemplate <typename SolutionType>\nvoid test_construct_from_options(\n    const Ccz4::Solutions::Ccz4WrappedGr<SolutionType>& wrapped_solution) {\n  const auto created =\n      TestHelpers::test_creation<Ccz4::Solutions::Ccz4WrappedGr<SolutionType>>(\n          \"Mass: 0.5\\n\"\n          \"N: 2.5\");\n\n  CHECK(created == wrapped_solution);\n}\n\nvoid test_gauge_plane_wave() {\n  const auto wave_vector = std::array<double, 3>{{0.1, 0.2, 0.3}};\n  const gr::Solutions::GaugePlaneWave<3> gauge_plane_wave_solution{\n      wave_vector,\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2)};\n  const Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::GaugePlaneWave<3>>\n      wrapped_gauge_plane_wave_solution{\n          wave_vector,\n          std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2)};\n  test_ccz4_solution<gr::Solutions::GaugePlaneWave<3>>(\n      gauge_plane_wave_solution, wrapped_gauge_plane_wave_solution);\n}\n}  // namespace\n\n// [[TimeOut, 40]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Ccz4WrappedGr\",\n                  \"[Evolution][Unit]\") {\n  const double amplitude = 0.24;\n  const double wavelength = 4.4;\n  const gr::Solutions::GaugeWave<3> gauge_wave_solution{amplitude, wavelength};\n  const Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::GaugeWave<3>>\n      wrapped_gauge_wave_solution{amplitude, wavelength};\n  test_ccz4_solution<gr::Solutions::GaugeWave<3>>(gauge_wave_solution,\n                                                  wrapped_gauge_wave_solution);\n\n  test_gauge_plane_wave();\n\n  const gr::Solutions::Minkowski<3> minkowski_solution{};\n  const Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::Minkowski<3>>\n      wrapped_minkowski_solution{};\n  test_ccz4_solution<gr::Solutions::Minkowski<3>>(minkowski_solution,\n                                                  wrapped_minkowski_solution);\n\n  const double mass = 0.5;\n  const double n = 2.5;\n  const gr::Solutions::TrumpetSchwarzschild trumpet_schwarzschild_solution{mass,\n                                                                           n};\n  const Ccz4::Solutions::Ccz4WrappedGr<gr::Solutions::TrumpetSchwarzschild>\n      wrapped_trumpet_schwarzschild_solution{mass, n};\n  test_ccz4_solution<gr::Solutions::TrumpetSchwarzschild>(\n      trumpet_schwarzschild_solution, wrapped_trumpet_schwarzschild_solution);\n\n  test_construct_from_options<gr::Solutions::TrumpetSchwarzschild>(\n      wrapped_trumpet_schwarzschild_solution);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_Christoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_conformal_christoffel_second_kind(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::Ijj<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&)>(\n          &::Ccz4::conformal_christoffel_second_kind<DataType, Dim,\n                                                     Frame::Inertial>),\n      \"Christoffel\", \"conformal_christoffel_second_kind\", {{{-1., 1.}}},\n      used_for_size);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test_christoffel_second_kind(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::Ijj<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::i<DataType, Dim, Frame::Inertial>&,\n          const tnsr::Ijj<DataType, Dim, Frame::Inertial>&)>(\n          &::Ccz4::christoffel_second_kind<DataType, Dim, Frame::Inertial>),\n      \"Christoffel\", \"christoffel_second_kind\", {{{-1., 1.}}}, used_for_size);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test_contracted_conformal_christoffel_second_kind(\n    const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::Ijj<DataType, Dim, Frame::Inertial>&)>(\n          &::Ccz4::contracted_conformal_christoffel_second_kind<\n              DataType, Dim, Frame::Inertial>),\n      \"Christoffel\", \"contracted_conformal_christoffel_second_kind\",\n      {{{-1., 1.}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Christoffel\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"Evolution/Systems/Ccz4/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_conformal_christoffel_second_kind,\n                                    (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_christoffel_second_kind, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_contracted_conformal_christoffel_second_kind, (1, 2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_DerivChristoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivChristoffel.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_deriv_conformal_christoffel_second_kind(\n    const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iJkk<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijkk<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJJ<DataType, Dim, Frame::Inertial>&)>(\n          &Ccz4::deriv_conformal_christoffel_second_kind<DataType, Dim,\n                                                         Frame::Inertial>),\n      \"DerivChristoffel\", \"deriv_conformal_christoffel_second_kind\",\n      {{{-1., 1.}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iJkk<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJJ<DataType, Dim, Frame::Inertial>&)>(\n          &Ccz4::deriv_conformal_christoffel_second_kind<DataType, Dim,\n                                                         Frame::Inertial>),\n      \"DerivChristoffel\", \"deriv_conformal_christoffel_second_kind\",\n      {{{-1., 1.}}}, used_for_size);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_deriv_contracted_conformal_christoffel_second_kind(\n    const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iJ<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJJ<DataType, Dim, Frame::Inertial>&,\n          const tnsr::Ijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJkk<DataType, Dim, Frame::Inertial>&)>(\n          &Ccz4::deriv_contracted_conformal_christoffel_second_kind<\n              DataType, Dim, Frame::Inertial>),\n      \"DerivChristoffel\", \"deriv_contracted_conformal_christoffel_second_kind\",\n      {{{-1., 1.}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.DerivChristoffel\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"Evolution/Systems/Ccz4/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_compute_deriv_conformal_christoffel_second_kind, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_compute_deriv_contracted_conformal_christoffel_second_kind,\n      (1, 2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_DerivLapse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivLapse.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_grad_grad_lapse(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ij<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::Ijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::i<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ij<DataType, Dim, Frame::Inertial>&)>(\n          &::Ccz4::grad_grad_lapse<DataType, Dim, Frame::Inertial>),\n      \"DerivLapse\", \"grad_grad_lapse\", {{{-1., 1.}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ij<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::Ijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::i<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&)>(\n          &::Ccz4::grad_grad_lapse<DataType, Dim, Frame::Inertial>),\n      \"DerivLapse\", \"grad_grad_lapse\", {{{-1., 1.}}}, used_for_size);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test_divergence_lapse(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataType> (*)(\n          const Scalar<DataType>&,\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ij<DataType, Dim, Frame::Inertial>&)>(\n          &::Ccz4::divergence_lapse<DataType, Dim, Frame::Inertial>),\n      \"DerivLapse\", \"divergence_lapse\", {{{-1., 1.}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.DerivLapse\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"Evolution/Systems/Ccz4/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_grad_grad_lapse, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_divergence_lapse, (1, 2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_DerivZ4Constraint.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <climits>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivZ4Constraint.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_grad_spatial_z4_constraint(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ij<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::i<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::Ijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJ<DataType, Dim, Frame::Inertial>&)>(\n          &Ccz4::grad_spatial_z4_constraint<DataType, Dim, Frame::Inertial>),\n      \"DerivZ4Constraint\", \"grad_spatial_z4_constraint\", {{{-1., 1.}}},\n      used_for_size);\n}\n\n// Test that when \\f$\\hat{\\Gamma}^i == \\tilde{\\Gamma}^i\\f$ and \\f$Z_i == 0\\f$,\n// the gradient of \\f$Z_i\\f$ is 0\ntemplate <typename Generator, typename DataType>\nvoid test_grad_spatial_z4_vanishes(const gsl::not_null<Generator*> generator,\n                                   const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  // \\f$Z_i == 0\\f$\n  const auto spatial_z4_constraint =\n      make_with_value<tnsr::i<DataType, 3, Frame::Inertial>>(used_for_size,\n                                                             0.0);\n  const auto conformal_spatial_metric =\n      make_with_random_values<tnsr::ii<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n  const auto christoffel_second_kind =\n      make_with_random_values<tnsr::Ijj<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n  const auto field_d =\n      make_with_random_values<tnsr::ijj<DataType, 3, Frame::Inertial>>(\n          generator, distribution, used_for_size);\n  // \\f$\\hat{\\Gamma}^i == \\tilde{\\Gamma}^i\\f$\n  const auto gamma_hat_minus_contracted_conformal_christoffel =\n      make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(used_for_size,\n                                                             0.0);\n  // \\f$\\hat{\\Gamma}^i == \\tilde{\\Gamma}^i\\f$\n  const auto d_gamma_hat_minus_contracted_conformal_christoffel =\n      make_with_value<tnsr::iJ<DataType, 3, Frame::Inertial>>(used_for_size,\n                                                              0.0);\n\n  using result_tensor_type = tnsr::ij<DataType, 3, Frame::Inertial>;\n  const result_tensor_type expected_grad_z4_constraint =\n      make_with_value<result_tensor_type>(used_for_size, 0.0);\n\n  const result_tensor_type actual_grad_z4_constraint =\n      Ccz4::grad_spatial_z4_constraint(\n          spatial_z4_constraint, conformal_spatial_metric,\n          christoffel_second_kind, field_d,\n          gamma_hat_minus_contracted_conformal_christoffel,\n          d_gamma_hat_minus_contracted_conformal_christoffel);\n\n  CHECK_ITERABLE_APPROX(actual_grad_z4_constraint, expected_grad_z4_constraint);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.DerivZ4Constraint\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"Evolution/Systems/Ccz4/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_grad_spatial_z4_constraint,\n                                    (1, 2, 3));\n\n  MAKE_GENERATOR(generator);\n  test_grad_spatial_z4_vanishes(make_not_null(&generator),\n                                std::numeric_limits<double>::signaling_NaN());\n  test_grad_spatial_z4_vanishes(\n      make_not_null(&generator),\n      DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_Ricci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <climits>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivChristoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/Ricci.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <typename Solution>\nvoid test_compute_spatial_ricci_tensor(\n    const Solution& solution, size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Setup grid\n  const size_t SpatialDim = 3;\n  Mesh<SpatialDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim>>(vars);\n  const auto det_spatial_metric = determinant_and_inverse(spatial_metric).first;\n  const auto& d_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim>,\n                      tmpl::size_t<SpatialDim>, Frame::Inertial>>(vars);\n  const auto d_det_spatial_metric =\n      get<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim>>(\n          solution.variables(\n              x, t,\n              tmpl::list<\n                  gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim>>{}));\n\n  // Compute arguments for `spatial_ricci_tensor` function to test\n  const auto conformal_factor = pow(get(det_spatial_metric), -1. / 6.);\n\n  tnsr::ii<DataVector, SpatialDim, Frame::Inertial> conformal_spatial_metric{};\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = i; j < SpatialDim; j++) {\n      conformal_spatial_metric.get(i, j) =\n          square(conformal_factor) * spatial_metric.get(i, j);\n    }\n  }\n\n  tnsr::ijj<DataVector, SpatialDim, Frame::Inertial>\n      d_conformal_spatial_metric{};\n  for (size_t k = 0; k < SpatialDim; k++) {\n    for (size_t i = 0; i < SpatialDim; i++) {\n      for (size_t j = i; j < SpatialDim; j++) {\n        d_conformal_spatial_metric.get(k, i, j) =\n            pow<2>(conformal_factor) * d_spatial_metric.get(k, i, j) -\n            pow<8>(conformal_factor) * d_det_spatial_metric.get(k) *\n                spatial_metric.get(i, j) / 3.;\n      }\n    }\n  }\n\n  const auto inverse_conformal_spatial_metric =\n      determinant_and_inverse(conformal_spatial_metric).second;\n\n  tnsr::ijj<DataVector, SpatialDim, Frame::Inertial> field_d{};\n  for (size_t k = 0; k < SpatialDim; k++) {\n    for (size_t i = 0; i < SpatialDim; i++) {\n      for (size_t j = i; j < SpatialDim; j++) {\n        field_d.get(k, i, j) = 0.5 * d_conformal_spatial_metric.get(k, i, j);\n      }\n    }\n  }\n\n  auto field_d_up = gr::deriv_inverse_spatial_metric(\n      inverse_conformal_spatial_metric, field_d);\n  for (size_t k = 0; k < SpatialDim; k++) {\n    for (size_t i = 0; i < SpatialDim; i++) {\n      for (size_t j = i; j < SpatialDim; j++) {\n        field_d_up.get(k, i, j) *= -1.0;\n      }\n    }\n  }\n\n  const auto contracted_field_d_up =\n      ::tenex::evaluate<ti::L>(field_d_up(ti::m, ti::M, ti::L));\n\n  const auto d_field_d =\n      partial_derivative(field_d, mesh, coord_map.inv_jacobian(x_logical));\n  const auto d_conformal_christoffel_second_kind =\n      Ccz4::deriv_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, field_d, d_field_d, field_d_up);\n\n  tnsr::i<DataVector, SpatialDim, Frame::Inertial> field_p{};\n  for (size_t i = 0; i < SpatialDim; i++) {\n    field_p.get(i) =\n        -d_det_spatial_metric.get(i) / (6. * get(det_spatial_metric));\n  }\n\n  const auto d_field_p =\n      partial_derivative(field_p, mesh, coord_map.inv_jacobian(x_logical));\n  // testing spatial_ricci_tensor() with a symmetric d_field_p\n  tnsr::ii<DataVector, SpatialDim, Frame::Inertial> d_field_p_symm{};\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = i; j < SpatialDim; j++) {\n        d_field_p_symm.get(i, j) = d_field_p.get(i, j);\n    }\n  }\n\n  const auto conformal_christoffel_second_kind =\n      Ccz4::conformal_christoffel_second_kind(inverse_conformal_spatial_metric,\n                                              field_d);\n\n  const auto christoffel_second_kind = Ccz4::christoffel_second_kind(\n      conformal_spatial_metric, inverse_conformal_spatial_metric, field_p,\n      conformal_christoffel_second_kind);\n\n  const auto contracted_christoffel_second_kind =\n      tenex::evaluate<ti::l>(christoffel_second_kind(ti::M, ti::l, ti::m));\n  const auto d_christoffel_second_kind = partial_derivative(\n      christoffel_second_kind, mesh, coord_map.inv_jacobian(x_logical));\n  const auto contracted_d_conformal_christoffel_difference =\n      ::tenex::evaluate<ti::i, ti::j>(\n          d_conformal_christoffel_second_kind(ti::m, ti::M, ti::i, ti::j) -\n          d_conformal_christoffel_second_kind(ti::j, ti::M, ti::i, ti::m));\n\n  // Compute expected and actual ricci tensors using above computed arguments\n  const auto expected_py_ricci_tensor{\n      pypp::call<tnsr::ii<DataVector, SpatialDim, Frame::Inertial>>(\n          \"Ricci\", \"spatial_ricci_tensor\", christoffel_second_kind,\n          d_conformal_christoffel_second_kind, conformal_spatial_metric,\n          inverse_conformal_spatial_metric, field_d, field_d_up, field_p,\n          d_field_p)};\n  const auto expected_py_ricci_tensor_with_symmetric_d_field_p{\n      pypp::call<tnsr::ii<DataVector, SpatialDim, Frame::Inertial>>(\n          \"Ricci\", \"spatial_ricci_tensor\", christoffel_second_kind,\n          d_conformal_christoffel_second_kind, conformal_spatial_metric,\n          inverse_conformal_spatial_metric, field_d, field_d_up, field_p,\n          d_field_p_symm)};\n\n  const auto expected_cpp_ricci_tensor =\n      gr::ricci_tensor(christoffel_second_kind, d_christoffel_second_kind);\n\n  const auto actual_cpp_ricci_tensor = Ccz4::spatial_ricci_tensor(\n      christoffel_second_kind, contracted_christoffel_second_kind,\n      contracted_d_conformal_christoffel_difference, conformal_spatial_metric,\n      inverse_conformal_spatial_metric, field_d, field_d_up,\n      contracted_field_d_up, field_p, d_field_p);\n  const auto actual_cpp_ricci_tensor_with_symmetric_d_field_p\n    = Ccz4::spatial_ricci_tensor(\n        christoffel_second_kind, contracted_christoffel_second_kind,\n        contracted_d_conformal_christoffel_difference, conformal_spatial_metric,\n        inverse_conformal_spatial_metric, field_d, field_d_up,\n        contracted_field_d_up, field_p, d_field_p_symm);\n\n  CHECK_ITERABLE_APPROX(expected_py_ricci_tensor, actual_cpp_ricci_tensor);\n  CHECK_ITERABLE_APPROX(expected_py_ricci_tensor_with_symmetric_d_field_p,\n    actual_cpp_ricci_tensor_with_symmetric_d_field_p);\n  // A custom epsilon is used here because the Legendre polynomials don't fit\n  // the derivative of 1 / r well. This was looked at for various box sizes and\n  // number of 1D grid points.\n  Approx approx = Approx::custom().epsilon(1e-11).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_cpp_ricci_tensor,\n                               actual_cpp_ricci_tensor, approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_cpp_ricci_tensor,\n                               actual_cpp_ricci_tensor_with_symmetric_d_field_p,\n                               approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Ricci\", \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"Evolution/Systems/Ccz4/\");\n\n  const double mass = 2.;\n  const std::array<double, 3> spin{{0.3, 0.5, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  const size_t grid_size = 8;\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n\n  test_compute_spatial_ricci_tensor(solution, grid_size, lower_bound,\n                                    upper_bound);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_RicciScalarPlusDivergenceZ4Constraint.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Evolution/Systems/Ccz4/RicciScalarPlusDivergenceZ4Constraint.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_ricci_scalar_plus_divergence_z4_constraint(\n    const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataType> (*)(\n          const Scalar<DataType>&,\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ij<DataType, Dim, Frame::Inertial>&)>(\n          &Ccz4::ricci_scalar_plus_divergence_z4_constraint<DataType, Dim,\n                                                            Frame::Inertial>),\n      \"RicciScalarPlusDivergenceZ4Constraint\",\n      \"ricci_scalar_plus_divergence_z4_constraint\", {{{-1., 1.}}},\n      used_for_size);\n}\n\n// Test that when \\f$\\nabla_i Z_j == 0\\f$, \\f$R + 2 \\nabla_k Z^k == R\\f$. Uses\n// KerrSchild for reference Ricci tensor solution.\ntemplate <typename Solution>\nvoid test_divergence_spatial_z4_constraint_vanishes(\n    const Solution& solution, size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Setup grid\n  const size_t SpatialDim = 3;\n  Mesh<SpatialDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n  const size_t num_points_3d = grid_size_each_dimension *\n                               grid_size_each_dimension *\n                               grid_size_each_dimension;\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim>>(vars);\n  const auto det_and_inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric);\n  const auto det_spatial_metric = det_and_inverse_spatial_metric.first;\n  const auto inverse_spatial_metric = det_and_inverse_spatial_metric.second;\n  const auto& d_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim>,\n                      tmpl::size_t<SpatialDim>, Frame::Inertial>>(vars);\n\n  // Compute arguments for KerrSchild Ricci scalar and\n  // `Ccz4::ricci_scalar_plus_divergence_z4_constraint`\n  const DataVector used_for_size =\n      DataVector(num_points_3d, std::numeric_limits<double>::signaling_NaN());\n  Scalar<DataVector> conformal_factor_squared{};\n  get(conformal_factor_squared) = pow(get(det_spatial_metric), -1. / 3.);\n\n  tnsr::II<DataVector, SpatialDim, Frame::Inertial>\n      inverse_conformal_spatial_metric{};\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = i; j < SpatialDim; j++) {\n      inverse_conformal_spatial_metric.get(i, j) =\n          inverse_spatial_metric.get(i, j) / get(conformal_factor_squared);\n    }\n  }\n\n  const auto christoffel_second_kind =\n      gr::christoffel_second_kind(d_spatial_metric, inverse_spatial_metric);\n  const auto d_christoffel_second_kind = partial_derivative(\n      christoffel_second_kind, mesh, coord_map.inv_jacobian(x_logical));\n  const auto spatial_ricci_tensor =\n      gr::ricci_tensor(christoffel_second_kind, d_christoffel_second_kind);\n\n  auto expected_ricci_scalar =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = 0; j < SpatialDim; j++) {\n      get(expected_ricci_scalar) +=\n          inverse_spatial_metric.get(i, j) * spatial_ricci_tensor.get(i, j);\n    }\n  }\n\n  // Let \\f$\\nabla_i Z_j = 0\\f$\n  const auto grad_spatial_z4_constraint =\n      make_with_value<tnsr::ij<DataVector, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n\n  const auto actual_result = Ccz4::ricci_scalar_plus_divergence_z4_constraint(\n      conformal_factor_squared, inverse_conformal_spatial_metric,\n      spatial_ricci_tensor, grad_spatial_z4_constraint);\n\n  // Check that the result is the Ricci scalar\n  CHECK_ITERABLE_APPROX(actual_result, expected_ricci_scalar);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Ccz4.RicciScalarPlusDivergenceZ4Constraint\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"Evolution/Systems/Ccz4/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_compute_ricci_scalar_plus_divergence_z4_constraint, (1, 2, 3));\n\n  const double mass = 2.;\n  const std::array<double, 3> spin{{0.3, 0.5, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  const size_t grid_size = 8;\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n\n  test_divergence_spatial_z4_constraint_vanishes(solution, grid_size,\n                                                 lower_bound, upper_bound);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct ArbitraryFrame;\n}  // namespace\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::ConformalFactor<DataType>>(\n      \"ConformalFactor\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ConformalFactorSquared<DataType>>(\"ConformalFactorSquared\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ConformalMetric<DataType, Dim, Frame>>(\n      \"Conformal(SpatialMetric)\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::InverseConformalMetric<DataType, Dim, Frame>>(\n      \"Conformal(InverseSpatialMetric)\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::ATilde<DataType, Dim, Frame>>(\n      \"ATilde\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::TraceATilde<DataType>>(\n      \"TraceATilde\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::LogLapse<DataType>>(\"LogLapse\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::FieldA<DataType, Dim, Frame>>(\n      \"FieldA\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::FieldB<DataType, Dim, Frame>>(\n      \"FieldB\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::FieldD<DataType, Dim, Frame>>(\n      \"FieldD\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::LogConformalFactor<DataType>>(\n      \"LogConformalFactor\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::FieldP<DataType, Dim, Frame>>(\n      \"FieldP\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::FieldDUp<DataType, Dim, Frame>>(\n      \"FieldDUp\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ConformalChristoffelSecondKind<DataType, Dim, Frame>>(\n      \"ConformalChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::DerivConformalChristoffelSecondKind<DataType, Dim, Frame>>(\n      \"DerivConformalChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ChristoffelSecondKind<DataType, Dim, Frame>>(\n      \"ChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::Ricci<DataType, Dim, Frame>>(\n      \"Ricci\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::GradGradLapse<DataType, Dim, Frame>>(\"GradGradLapse\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::DivergenceLapse<DataType>>(\n      \"DivergenceLapse\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ContractedConformalChristoffelSecondKind<DataType, Dim,\n                                                           Frame>>(\n      \"ContractedConformalChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::DerivContractedConformalChristoffelSecondKind<DataType, Dim,\n                                                                Frame>>(\n      \"DerivContractedConformalChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::GammaHat<DataType, Dim, Frame>>(\n      \"GammaHat\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::SpatialZ4Constraint<DataType, Dim, Frame>>(\n      \"SpatialZ4Constraint\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::SpatialZ4ConstraintUp<DataType, Dim, Frame>>(\n      \"SpatialZ4ConstraintUp\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::GradSpatialZ4Constraint<DataType, Dim, Frame>>(\n      \"GradSpatialZ4Constraint\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::RicciScalarPlusDivergenceZ4Constraint<DataType>>(\n      \"RicciScalarPlusDivergenceZ4Constraint\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::Theta<DataType>>(\"Theta\");\n  TestHelpers::db::test_simple_tag<\n    Ccz4::Tags::AuxiliaryShiftB<DataType, Dim, Frame>>(\n        \"AuxiliaryShiftB\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::GammaDriverParam>(\n      \"GammaDriverParam\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::Kappa1>(\"Kappa1\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::Kappa2>(\"Kappa2\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::Kappa3>(\"Kappa3\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::K0<DataType>>(\"K0\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::Eta<DataType>>(\"Eta\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Tags\", \"[Unit][Evolution]\") {\n  test_simple_tags<double, 1,\n                   ArbitraryFrame>();\n  test_simple_tags<DataVector, 1, ArbitraryFrame>();\n  test_simple_tags<double, 2, ArbitraryFrame>();\n  test_simple_tags<DataVector, 2, ArbitraryFrame>();\n  test_simple_tags<double, 3, ArbitraryFrame>();\n  test_simple_tags<DataVector, 3, ArbitraryFrame>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_TempTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/Ccz4/TempTags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct ArbitraryFrame;\n}  // namespace\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::GammaHatMinusContractedConformalChristoffel<DataType, Dim,\n                                                              Frame>>(\n      \"GammaHatMinusContractedConformalChristoffel\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::KMinus2ThetaC<DataType>>(\n      \"KMinus2ThetaC\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::KMinusK0Minus2ThetaC<DataType>>(\n      \"KMinusK0Minus2ThetaC\");\n  TestHelpers::db::test_simple_tag<Ccz4::Tags::ContractedFieldB<DataType>>(\n      \"ContractedFieldB\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ConformalMetricTimesFieldB<DataType, Dim, Frame>>(\n      \"ConformalMetricTimesFieldB\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::LapseTimesRicciScalarPlus2DivergenceZ4Constraint<DataType>>(\n      \"LapseTimesRicciScalarPlus2DivergenceZ4Constraint\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ConformalMetricTimesTraceATilde<DataType, Dim, Frame>>(\n      \"ConformalMetricTimesTraceATilde\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::LapseTimesATilde<DataType, Dim, Frame>>(\"LapseTimesATilde\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::FieldDUpTimesATilde<DataType, Dim, Frame>>(\n      \"FieldDUpTimesATilde\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::LapseTimesDerivATilde<DataType, Dim, Frame>>(\n      \"LapseTimesDerivATilde\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::InverseConformalMetricTimesDerivATilde<DataType, Dim, Frame>>(\n      \"InverseConformalMetricTimesDerivATilde\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ATildeMinusOneThirdConformalMetricTimesTraceATilde<\n          DataType, Dim, Frame>>(\n      \"ATildeMinusOneThirdConformalMetricTimesTraceATilde\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::LapseTimesFieldA<DataType, Dim, Frame>>(\"LapseTimesFieldA\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ShiftTimesDerivGammaHat<DataType, Dim, Frame>>(\n      \"ShiftTimesDerivGammaHat\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::InverseTauTimesConformalMetric<DataType, Dim, Frame>>(\n      \"InverseTauTimesConformalMetric\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::LapseTimesSlicingCondition<DataType>>(\n      \"LapseTimesSlicingCondition\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::DetConformalSpatialMetric<DataType>>(\n      \"DetConformalSpatialMetric\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::InvATilde<DataType, Dim, Frame>>(\n      \"InvATilde\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ATildeTimesFieldB<DataType, Dim, Frame>>(\n      \"ATildeTimesFieldB\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::SymmetrizedDerivFieldB<DataType, Dim, Frame>>(\n      \"SymmetrizedDerivFieldB\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ContractedSymmetrizedDerivFieldB<DataType, Dim, Frame>>(\n      \"ContractedSymmetrizedDerivFieldB\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ContractedFieldDUp<DataType, Dim, Frame>>(\n      \"ContractedFieldDUp\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::HalfConformalFactorSquared<DataType>>(\n      \"HalfConformalFactorSquared\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::DerivGammaHatMinusContractedConformalChristoffel<\n          DataType, Dim, Frame>>(\n      \"DerivGammaHatMinusContractedConformalChristoffel\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ContractedChristoffelSecondKind<DataType, Dim, Frame>>(\n      \"ContractedChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::ContractedDerivConformalChristoffelDifference<\n          DataType, Dim, Frame>>(\n      \"ContractedDerivConformalChristoffelDifference\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::LapseTimesConformalMetric<DataType, Dim, Frame>>(\n      \"LapseTimesConformalMetric\");\n  TestHelpers::db::test_simple_tag<\n      Ccz4::Tags::SpatialRicciTensor<DataType, Dim, Frame>>(\n      \"SpatialRicciTensor\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.TempTags\", \"[Unit][Evolution]\") {\n  test_simple_tags<double, 1, ArbitraryFrame>();\n  test_simple_tags<DataVector, 1, ArbitraryFrame>();\n  test_simple_tags<double, 2, ArbitraryFrame>();\n  test_simple_tags<DataVector, 2, ArbitraryFrame>();\n  test_simple_tags<double, 3, ArbitraryFrame>();\n  test_simple_tags<DataVector, 3, ArbitraryFrame>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Evolution/Systems/Ccz4/ATilde.hpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivChristoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/TimeDerivative.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\n// Equations referenced below can be found in \\cite Dumbser2017okk\n\n// Compute g(\\alpha)\nScalar<DataVector> get_slicing_condition(\n    const Ccz4::SlicingConditionType slicing_condition_type,\n    const Scalar<DataVector>& lapse) {\n  Scalar<DataVector> slicing_condition(get(lapse));\n  if (slicing_condition_type == Ccz4::SlicingConditionType::Harmonic) {\n    // harmonic slicing condition: g(\\alpha) = 1.0\n    get(slicing_condition) = 1.0;\n  } else if (slicing_condition_type == Ccz4::SlicingConditionType::Log) {\n    // 1 + log slicing condition: g(\\alpha) = 2 / \\alpha\n    get(slicing_condition) = 2.0 / get(lapse);\n  } else {\n    ERROR(\"Unknown Ccz4::SlicingConditionType\");\n  }\n  return slicing_condition;\n}\n\n// Compute the trace of the extrinsic curvature K = K_{ij} * \\gamma^ij\ntemplate <size_t SpatialDim, typename FrameType>\nScalar<DataVector> get_trace_extrinsic_curvature(\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& extrinsic_curvature,\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric) {\n  Scalar<DataVector> trace_extrinsic_curvature(get<0, 0>(extrinsic_curvature));\n  get(trace_extrinsic_curvature) = 0.0;\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = 0; j < SpatialDim; j++) {\n      get(trace_extrinsic_curvature) +=\n          extrinsic_curvature.get(i, j) * inverse_spatial_metric.get(i, j);\n    }\n  }\n  return trace_extrinsic_curvature;\n}\n\n// Compute A_i from eq 6\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::i<DataVector, SpatialDim, FrameType> get_field_a(\n    const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_lapse) {\n  tnsr::i<DataVector, SpatialDim, FrameType> field_a(get(lapse));\n  for (size_t i = 0; i < SpatialDim; i++) {\n    field_a.get(i) = d_lapse.get(i) / get(lapse);\n  }\n  return field_a;\n}\n\n// Compute \\partial A_i from eq 6\n//\n// eq 6:\n//   A_i = \\partial_i \\alpha / \\alpha\n// then, the derivative is:\n//   \\partial_i A_j =\n//       ((\\partial_i (\\partial_j \\alpha)) \\alpha -\n//         \\partial_j \\alpha \\partial_i \\alpha) / \\alpha^2\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ij<DataVector, SpatialDim, FrameType> get_d_field_a(\n    const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_lapse,\n    const tnsr::ij<DataVector, SpatialDim, FrameType>& d_d_lapse) {\n  tnsr::ij<DataVector, SpatialDim, FrameType> d_field_a(get(lapse));\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = 0; j < SpatialDim; j++) {\n      d_field_a.get(i, j) =\n          (d_d_lapse.get(i, j) * get(lapse) - d_lapse.get(j) * d_lapse.get(i)) /\n          square(get(lapse));\n    }\n  }\n  return d_field_a;\n}\n\n// Compute the conformal spatial metric\n//\n// \\tilde{\\gamma}_{ij} = \\phi^2 \\gamma_{ij}\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ii<DataVector, SpatialDim, FrameType> get_conformal_spatial_metric(\n    const Scalar<DataVector>& conformal_factor_squared,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& spatial_metric) {\n  tnsr::ii<DataVector, SpatialDim, FrameType> conformal_spatial_metric(\n      get(conformal_factor_squared));\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = i; j < SpatialDim; j++) {\n      conformal_spatial_metric.get(i, j) =\n          get(conformal_factor_squared) * spatial_metric.get(i, j);\n    }\n  }\n  return conformal_spatial_metric;\n}\n\n// Compute the spatial derivative of the conformal spatial metric\n//\n// If \\tilde{\\gamma}_{ij} is the conformal metric and \\phi is the\n// conformal factor, \\tilde{\\gamma}_{ij} = \\phi^2 \\gamma_{ij}.\n// Therefore, the derivative of the conformal metric is:\n//   \\partial_k \\tilde{\\gamma}_{ij} =\n//       \\phi^2 \\partial_k \\gamma_{ij} +\n//       \\partial_k \\phi^2 \\gamma_{ij}\n//\n// Since \\phi = (det(\\gamma_{ij}))^{-1/6}:\n//   \\partial_k \\phi^2\n//        = \\partial_k ((det(\\gamma_{ij}))^{-1/6})^2\n//        = \\partial_k (det(\\gamma_{ij}))^{-1/3}\n//        = -(det(\\gamma_{ij}))^{-4/3}  \\partial_k (det(\\gamma_{ij}))/ 3\n//        = -\\phi^4 \\partial_k (det(\\gamma_{ij})) / 3\n//\n// Therefore:\n//   \\partial_k \\tilde{\\gamma}_{ij} =\n//       \\phi^2 \\partial_k \\gamma_{ij} -\n//       \\phi^4 \\partial_k (det(\\gamma_{ij})) \\gamma_{ij} / 3\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ijj<DataVector, SpatialDim, FrameType> get_d_conformal_spatial_metric(\n    const Scalar<DataVector>& conformal_factor_squared,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& d_spatial_metric,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_det_spatial_metric) {\n  tnsr::ijj<DataVector, SpatialDim, FrameType> d_conformal_spatial_metric(\n      get(conformal_factor_squared));\n  for (size_t k = 0; k < SpatialDim; k++) {\n    for (size_t i = 0; i < SpatialDim; i++) {\n      for (size_t j = i; j < SpatialDim; j++) {\n        d_conformal_spatial_metric.get(k, i, j) =\n            get(conformal_factor_squared) * d_spatial_metric.get(k, i, j) -\n            pow<4>(get(conformal_factor_squared)) *\n                d_det_spatial_metric.get(k) * spatial_metric.get(i, j) / 3.;\n      }\n    }\n  }\n  return d_conformal_spatial_metric;\n}\n\n// Compute D_{kij} from eq 6\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ijj<DataVector, SpatialDim, FrameType> get_field_d(\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>&\n        d_conformal_spatial_metric) {\n  tnsr::ijj<DataVector, SpatialDim, FrameType> field_d(\n      get<0, 0, 0>(d_conformal_spatial_metric));\n  for (size_t i = 0; i < field_d.size(); i++) {\n    field_d[i] = 0.5 * d_conformal_spatial_metric[i];\n  }\n  return field_d;\n}\n\n// Compute D_{k}^{ij} from eq 14\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::iJJ<DataVector, SpatialDim, FrameType> get_field_d_up(\n    const tnsr::II<DataVector, SpatialDim, FrameType>&\n        inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& field_d) {\n  tnsr::iJJ<DataVector, SpatialDim, FrameType> field_d_up =\n      gr::deriv_inverse_spatial_metric(inverse_conformal_spatial_metric,\n                                       field_d);\n  for (size_t i = 0; i < field_d.size(); i++) {\n    field_d_up[i] *= -1.0;\n  }\n  return field_d_up;\n}\n\n// Compute P_i from eq 6\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::i<DataVector, SpatialDim, FrameType> get_field_p(\n    const Scalar<DataVector>& det_spatial_metric,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_det_spatial_metric) {\n  tnsr::i<DataVector, SpatialDim, FrameType> field_p(get(det_spatial_metric));\n  for (size_t i = 0; i < SpatialDim; i++) {\n    field_p.get(i) =\n        -d_det_spatial_metric.get(i) / (6. * get(det_spatial_metric));\n  }\n  return field_p;\n}\n\n// \\brief Test first order CCZ4 with different binary settings against Minkowski\n//\n// \\details Tests that all time derivatives are 0. The evolution equations are\n// eq 12a - 12m in \\cite Dumbser2017okk.\n//\n// \\param evolve_shift whether or not to evolve the shift\n// \\param slicing_condition_type which slicing condition to use, e.g.\n// harmonic, 1 + log\nvoid test_minkowski(const Ccz4::EvolveShift evolve_shift,\n                    const Ccz4::SlicingConditionType slicing_condition_type) {\n  const size_t SpatialDim = 3;\n  using FrameType = Frame::Inertial;\n\n  // Setup solution\n  gr::Solutions::Minkowski<SpatialDim> solution{};\n\n  // Setup grid\n  const size_t num_points_1d = 4;\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n  Mesh<SpatialDim> mesh{num_points_1d, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, FrameType>(Affine3D{\n          Affine{-1., 1., lower_bound[0], upper_bound[0]},\n          Affine{-1., 1., lower_bound[1], upper_bound[1]},\n          Affine{-1., 1., lower_bound[2], upper_bound[2]},\n      });\n  const size_t num_points_3d = num_points_1d * num_points_1d * num_points_1d;\n  const DataVector used_for_size =\n      DataVector(num_points_3d, std::numeric_limits<double>::signaling_NaN());\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate solution\n  const auto minkowski_vars = solution.variables(\n      x, t, typename gr::Solutions::Minkowski<SpatialDim>::tags<DataVector>{});\n\n  // Get ingredients for computing arguments to Ccz4::TimeDerivative\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim, FrameType>>(\n          minkowski_vars);\n  const auto det_spatial_metric = determinant(spatial_metric);\n  const auto d_det_spatial_metric =\n      make_with_value<tnsr::i<DataVector, SpatialDim, FrameType>>(used_for_size,\n                                                                  0.0);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          minkowski_vars);\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(minkowski_vars);\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, SpatialDim, FrameType>>(minkowski_vars);\n  const auto& d_shift =\n      get<Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, FrameType>,\n                      tmpl::size_t<SpatialDim>, FrameType>>(minkowski_vars);\n\n  // Params\n  const double c = 1.0;\n  const double cleaning_speed = 1.6;  // e\n  const double f = 0.75;\n  const double kappa_1 = 0.1;\n  const double kappa_2 = 0.3;\n  const double kappa_3 = 0.4;\n  const double mu = 0.7;\n  const double one_over_relaxation_time = 10.0;         // \\tau^{-1}\n  const Scalar<DataVector> slicing_condition =          // g(\\alpha)\n      get_slicing_condition(slicing_condition_type, lapse);\n\n  // Choose free variables \\Theta, K_0, b^i, and \\eta\n  const auto theta = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto d_theta =\n      make_with_value<tnsr::i<DataVector, SpatialDim, FrameType>>(used_for_size,\n                                                                  0.0);\n\n  const auto extrinsic_curvature =\n      make_with_value<tnsr::ii<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  const auto trace_extrinsic_curvature =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto d_trace_extrinsic_curvature =\n      make_with_value<tnsr::i<DataVector, SpatialDim, FrameType>>(used_for_size,\n                                                                  0.0);\n\n  // K_0 == K\n  const auto& k_0 = trace_extrinsic_curvature;\n  const auto& d_k_0 = d_trace_extrinsic_curvature;\n\n  const auto b = make_with_value<tnsr::I<DataVector, SpatialDim, FrameType>>(\n      used_for_size, 0.0);\n  const auto d_b = make_with_value<tnsr::iJ<DataVector, SpatialDim, FrameType>>(\n      used_for_size, 0.0);\n\n  const auto eta = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n\n  // Compute arguments for Ccz4::TimeDerivative\n  Scalar<DataVector> ln_lapse{};\n  get(ln_lapse) = log(get(lapse));\n\n  // eq 6\n  const auto field_a =\n      make_with_value<tnsr::i<DataVector, SpatialDim, FrameType>>(used_for_size,\n                                                                  0.0);\n  const auto d_field_a =\n      make_with_value<tnsr::ij<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  const auto& field_b = d_shift;\n  const auto d_field_b =\n      make_with_value<tnsr::ijK<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  // since spatial_metric = conformal_spatial_metric,\n  // conformal factor == 1\n  const auto conformal_factor_squared =\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0);\n  const auto ln_conformal_factor =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n\n  // eq 3\n  const auto a_tilde =\n      Ccz4::a_tilde(conformal_factor_squared, spatial_metric,\n                    extrinsic_curvature, trace_extrinsic_curvature);\n  const auto d_a_tilde =\n      make_with_value<tnsr::ijj<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  const auto& conformal_spatial_metric = spatial_metric;\n  const auto d_conformal_spatial_metric =\n      make_with_value<tnsr::ijj<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  const auto& inverse_conformal_spatial_metric = inverse_spatial_metric;\n\n  // eq 6\n  const auto field_d =\n      make_with_value<tnsr::ijj<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n  const auto d_field_d =\n      make_with_value<tnsr::ijkk<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  // eq 14\n  const auto field_d_up =\n      make_with_value<tnsr::iJJ<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  // eq 6\n  const auto field_p =\n      make_with_value<tnsr::i<DataVector, SpatialDim, FrameType>>(used_for_size,\n                                                                  0.0);\n  const auto d_field_p =\n      make_with_value<tnsr::ij<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  // eq 15\n  const auto conformal_christoffel_second_kind =\n      Ccz4::conformal_christoffel_second_kind(inverse_conformal_spatial_metric,\n                                              field_d);\n  // eq 16\n  const auto d_conformal_christoffel_second_kind =\n      make_with_value<tnsr::iJkk<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  // eq 23\n  const auto contracted_conformal_christoffel_second_kind =\n      Ccz4::contracted_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, conformal_christoffel_second_kind);\n  // eq 24\n  const auto d_contracted_conformal_christoffel_second_kind =\n      make_with_value<tnsr::iJ<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n\n  // If Z4 constraint is 0, then \\hat{\\gamma} == \\tilde{\\gamma}\n  const auto& gamma_hat = contracted_conformal_christoffel_second_kind;\n  const auto& d_gamma_hat = d_contracted_conformal_christoffel_second_kind;\n\n  // LHS time derivatives of evolved variables: eq 12a - 12m\n  tnsr::ii<DataVector, SpatialDim> dt_conformal_spatial_metric_actual(\n      used_for_size);\n  Scalar<DataVector> dt_ln_lapse_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim> dt_shift_actual(used_for_size);\n  Scalar<DataVector> dt_ln_conformal_factor_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim> dt_a_tilde_actual(used_for_size);\n  Scalar<DataVector> dt_trace_extrinsic_curvature_actual(used_for_size);\n  Scalar<DataVector> dt_theta_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim> dt_gamma_hat_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim> dt_b_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> dt_field_a_actual(used_for_size);\n  tnsr::iJ<DataVector, SpatialDim> dt_field_b_actual(used_for_size);\n  tnsr::ijj<DataVector, SpatialDim> dt_field_d_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> dt_field_p_actual(used_for_size);\n  // quantities we need for computing eq 12 - 27\n  Scalar<DataVector> conformal_factor_squared_actual(used_for_size);\n  Scalar<DataVector> det_conformal_spatial_metric_actual(used_for_size);\n  tnsr::II<DataVector, SpatialDim> inv_conformal_spatial_metric_actual(\n      used_for_size);\n  tnsr::II<DataVector, SpatialDim> inv_spatial_metric_actual(used_for_size);\n  Scalar<DataVector> lapse_actual(used_for_size);\n  Scalar<DataVector> slicing_condition_actual(used_for_size);\n  Scalar<DataVector> d_slicing_condition_actual(used_for_size);\n  tnsr::II<DataVector, SpatialDim> inv_a_tilde_actual(used_for_size);\n  // quantities we need for computing eq 12 - 27\n  tnsr::ij<DataVector, SpatialDim> a_tilde_times_field_b_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim>\n      a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde_actual(\n          used_for_size);\n  Scalar<DataVector> contracted_field_b_actual(used_for_size);\n  tnsr::ijK<DataVector, SpatialDim> symmetrized_d_field_b_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> contracted_symmetrized_d_field_b_actual(\n      used_for_size);\n  tnsr::ijk<DataVector, SpatialDim> field_b_times_field_d_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> field_d_up_times_a_tilde_actual(\n      used_for_size);\n  tnsr::I<DataVector, SpatialDim> contracted_field_d_up_actual(used_for_size);\n  Scalar<DataVector> half_conformal_factor_squared_actual(used_for_size);\n  tnsr::ij<DataVector, SpatialDim> conformal_metric_times_field_b_actual(\n      used_for_size);\n  tnsr::ijk<DataVector, SpatialDim>\n      conformal_metric_times_symmetrized_d_field_b_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim> conformal_metric_times_trace_a_tilde_actual(\n      used_for_size);\n  tnsr::i<DataVector, SpatialDim> inv_conformal_metric_times_d_a_tilde_actual(\n      used_for_size);\n  tnsr::I<DataVector, SpatialDim>\n      gamma_hat_minus_contracted_conformal_christoffel_actual(used_for_size);\n  tnsr::iJ<DataVector, SpatialDim>\n      d_gamma_hat_minus_contracted_conformal_christoffel_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> contracted_christoffel_second_kind_actual(\n      used_for_size);\n  tnsr::ij<DataVector, SpatialDim>\n      contracted_d_conformal_christoffel_difference_actual(used_for_size);\n  Scalar<DataVector> k_minus_2_theta_c_actual(used_for_size);\n  Scalar<DataVector> k_minus_k0_minus_2_theta_c_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim> lapse_times_a_tilde_actual(used_for_size);\n  tnsr::ijj<DataVector, SpatialDim> lapse_times_d_a_tilde_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> lapse_times_field_a_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim> lapse_times_conformal_spatial_metric_actual(\n      used_for_size);\n  Scalar<DataVector> lapse_times_slicing_condition_actual(used_for_size);\n  Scalar<DataVector>\n      lapse_times_ricci_scalar_plus_divergence_z4_constraint_actual(\n          used_for_size);\n  tnsr::I<DataVector, SpatialDim> shift_times_deriv_gamma_hat_actual(\n      used_for_size);\n  tnsr::ii<DataVector, SpatialDim> inv_tau_times_conformal_metric_actual(\n      used_for_size);\n  // expressions and identities needed for evolution equations: eq 13 - 27\n  Scalar<DataVector> trace_a_tilde_actual(used_for_size);\n  tnsr::iJJ<DataVector, SpatialDim> field_d_up_actual(used_for_size);\n  tnsr::Ijj<DataVector, SpatialDim> conformal_christoffel_second_kind_actual(\n      used_for_size);\n  tnsr::iJkk<DataVector, SpatialDim> d_conformal_christoffel_second_kind_actual(\n      used_for_size);\n  tnsr::Ijj<DataVector, SpatialDim> christoffel_second_kind_actual(\n      used_for_size);\n  tnsr::ii<DataVector, SpatialDim> spatial_ricci_tensor_actual(used_for_size);\n  tnsr::ij<DataVector, SpatialDim> grad_grad_lapse_actual(used_for_size);\n  Scalar<DataVector> divergence_lapse_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim>\n      contracted_conformal_christoffel_second_kind_actual(used_for_size);\n  tnsr::iJ<DataVector, SpatialDim>\n      d_contracted_conformal_christoffel_second_kind_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> spatial_z4_constraint_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim> upper_spatial_z4_constraint_actual(\n      used_for_size);\n  tnsr::ij<DataVector, SpatialDim> grad_spatial_z4_constraint_actual(\n      used_for_size);\n  Scalar<DataVector> ricci_scalar_plus_divergence_z4_constraint_actual(\n      used_for_size);\n\n  ::Ccz4::TimeDerivative<SpatialDim>::apply(\n      make_not_null(&dt_conformal_spatial_metric_actual),\n      make_not_null(&dt_ln_lapse_actual), make_not_null(&dt_shift_actual),\n      make_not_null(&dt_ln_conformal_factor_actual),\n      make_not_null(&dt_a_tilde_actual),\n      make_not_null(&dt_trace_extrinsic_curvature_actual),\n      make_not_null(&dt_theta_actual), make_not_null(&dt_gamma_hat_actual),\n      make_not_null(&dt_b_actual), make_not_null(&dt_field_a_actual),\n      make_not_null(&dt_field_b_actual), make_not_null(&dt_field_d_actual),\n      make_not_null(&dt_field_p_actual),\n      make_not_null(&conformal_factor_squared_actual),\n      make_not_null(&det_conformal_spatial_metric_actual),\n      make_not_null(&inv_conformal_spatial_metric_actual),\n      make_not_null(&inv_spatial_metric_actual), make_not_null(&lapse_actual),\n      make_not_null(&slicing_condition_actual),\n      make_not_null(&d_slicing_condition_actual),\n      make_not_null(&inv_a_tilde_actual),\n      make_not_null(&a_tilde_times_field_b_actual),\n      make_not_null(\n          &a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde_actual),\n      make_not_null(&contracted_field_b_actual),\n      make_not_null(&symmetrized_d_field_b_actual),\n      make_not_null(&contracted_symmetrized_d_field_b_actual),\n      make_not_null(&field_b_times_field_d_actual),\n      make_not_null(&field_d_up_times_a_tilde_actual),\n      make_not_null(&contracted_field_d_up_actual),\n      make_not_null(&half_conformal_factor_squared_actual),\n      make_not_null(&conformal_metric_times_field_b_actual),\n      make_not_null(&conformal_metric_times_symmetrized_d_field_b_actual),\n      make_not_null(&conformal_metric_times_trace_a_tilde_actual),\n      make_not_null(&inv_conformal_metric_times_d_a_tilde_actual),\n      make_not_null(&gamma_hat_minus_contracted_conformal_christoffel_actual),\n      make_not_null(&d_gamma_hat_minus_contracted_conformal_christoffel_actual),\n      make_not_null(&contracted_christoffel_second_kind_actual),\n      make_not_null(&contracted_d_conformal_christoffel_difference_actual),\n      make_not_null(&k_minus_2_theta_c_actual),\n      make_not_null(&k_minus_k0_minus_2_theta_c_actual),\n      make_not_null(&lapse_times_a_tilde_actual),\n      make_not_null(&lapse_times_d_a_tilde_actual),\n      make_not_null(&lapse_times_field_a_actual),\n      make_not_null(&lapse_times_conformal_spatial_metric_actual),\n      make_not_null(&lapse_times_slicing_condition_actual),\n      make_not_null(\n          &lapse_times_ricci_scalar_plus_divergence_z4_constraint_actual),\n      make_not_null(&shift_times_deriv_gamma_hat_actual),\n      make_not_null(&inv_tau_times_conformal_metric_actual),\n      make_not_null(&trace_a_tilde_actual), make_not_null(&field_d_up_actual),\n      make_not_null(&conformal_christoffel_second_kind_actual),\n      make_not_null(&d_conformal_christoffel_second_kind_actual),\n      make_not_null(&christoffel_second_kind_actual),\n      make_not_null(&spatial_ricci_tensor_actual),\n      make_not_null(&grad_grad_lapse_actual),\n      make_not_null(&divergence_lapse_actual),\n      make_not_null(&contracted_conformal_christoffel_second_kind_actual),\n      make_not_null(&d_contracted_conformal_christoffel_second_kind_actual),\n      make_not_null(&spatial_z4_constraint_actual),\n      make_not_null(&upper_spatial_z4_constraint_actual),\n      make_not_null(&grad_spatial_z4_constraint_actual),\n      make_not_null(&ricci_scalar_plus_divergence_z4_constraint_actual), c,\n      cleaning_speed, eta, f, k_0, d_k_0, kappa_1, kappa_2, kappa_3, mu,\n      one_over_relaxation_time, evolve_shift, slicing_condition_type,\n      conformal_spatial_metric, ln_lapse, shift, ln_conformal_factor, a_tilde,\n      trace_extrinsic_curvature, theta, gamma_hat, b, field_a, field_b, field_d,\n      field_p, d_a_tilde, d_trace_extrinsic_curvature, d_theta, d_gamma_hat,\n      d_b, d_field_a, d_field_b, d_field_d, d_field_p);\n\n  const auto zero = DataVector(used_for_size.size(), 0.0);\n\n  // Check that all time derivatives are 0\n  for (auto& component : dt_conformal_spatial_metric_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_ln_lapse_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_shift_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_ln_conformal_factor_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_a_tilde_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_trace_extrinsic_curvature_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_theta_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_gamma_hat_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_b_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_field_a_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_field_b_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_field_d_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  for (auto& component : dt_field_p_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n}\n\n// Compute K_0 for KerrSchild\n//\n// Solve eq 4g for K_0, where \\partial_t \\alpha = 0:\n//   \\partial_t \\alpha =\n//       -\\alpha^2 g(\\alpha) (K - K_0 - 2 \\Theta) +\n//       \\beta^k \\partial_k \\alpha\n//   K_0 = -((\\beta^k \\partial_k \\alpha) / (\\alpha^2 * g(\\alpha)) -\n//           K + 2 \\Theta);\ntemplate <size_t SpatialDim, typename FrameType>\nScalar<DataVector> get_k_0_kerr(\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift,\n    const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_lapse,\n    const Scalar<DataVector>& slicing_condition,\n    const Scalar<DataVector>& theta,\n    const Scalar<DataVector>& trace_extrinsic_curvature) {\n  Scalar<DataVector> k_0(get(lapse));\n  get(k_0) = get<0>(shift) * get<0>(d_lapse);\n  for (size_t k = 1; k < SpatialDim; k++) {\n    get(k_0) += shift.get(k) * d_lapse.get(k);\n  }\n  get(k_0) = -((get(k_0) / (square(get(lapse)) * get(slicing_condition))) -\n               get(trace_extrinsic_curvature) + 2.0 * get(theta));\n  return k_0;\n}\n\n// Compute b^i for KerrSchild\n//\n// Solve eq 12c for b^i, where \\partial_t \\beta^i = 0:\n//   \\partial_t \\beta^i = s f b + s \\beta^k \\partial_k \\beta^i\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::I<DataVector, SpatialDim, FrameType> get_b_kerr(\n    const Ccz4::EvolveShift evolve_shift,\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift,\n    const tnsr::iJ<DataVector, SpatialDim, FrameType>& d_shift,\n    const double f) {\n  tnsr::I<DataVector, SpatialDim, FrameType> b(get<0>(shift));\n  if (not static_cast<bool>(evolve_shift)) {\n    // s == 0\n    // pick b = 0\n    for (auto& component : b) {\n      component = 0.0;\n    }\n  } else {\n    // s == 1\n    // b = -(\\beta^k \\partial_k \\beta^i) / f\n    for (size_t i = 0; i < SpatialDim; i++) {\n      b.get(i) = -shift.get(0) * d_shift.get(0, i);\n      for (size_t k = 1; k < SpatialDim; k++) {\n        b.get(i) -= shift.get(k) * d_shift.get(k, i);\n      }\n      b.get(i) /= f;\n    }\n  }\n  return b;\n}\n\n// Compute expected value for LHS of eq 12h\n//\n// \\partial_t \\hat{\\gamma}^i will not be 0 for KerrSchild if\n// evolve_shift == false\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::I<DataVector, SpatialDim, FrameType> get_dt_gamma_hat_kerr_expected(\n    const Ccz4::EvolveShift evolve_shift,\n    const tnsr::II<DataVector, SpatialDim, FrameType>&\n        inverse_conformal_spatial_metric,\n    const tnsr::ijK<DataVector, SpatialDim, FrameType>& d_field_b) {\n  tnsr::I<DataVector, SpatialDim, FrameType> dt_gamma_hat_kerr_expected(\n      get<0, 0>(inverse_conformal_spatial_metric));\n  if (static_cast<bool>(evolve_shift)) {\n    // s == 1\n    for (auto& component : dt_gamma_hat_kerr_expected) {\n      component = 0.0;\n    }\n  } else {\n    // s == 0\n    //\n    // When s == 1, \\partial_t \\hat{\\gamma}^i == 0, so when s == 0,\n    // dt_gamma_hat = terms in eq 12h with s as a factor\n    // (red terms that cancel out are ignored)\n    for (size_t i = 0; i < SpatialDim; i++) {\n      dt_gamma_hat_kerr_expected.get(i) = 0.0;\n      for (size_t k = 0; k < SpatialDim; k++) {\n        for (size_t l = 0; l < SpatialDim; l++) {\n          dt_gamma_hat_kerr_expected.get(i) -=\n              (0.5 * inverse_conformal_spatial_metric.get(k, l) *\n                   (d_field_b.get(k, l, i) + d_field_b.get(l, k, i)) +\n               0.5 * inverse_conformal_spatial_metric.get(i, k) *\n                   (d_field_b.get(k, l, l) + d_field_b.get(l, k, l)) / 3.0);\n        }\n      }\n    }\n  }\n  return dt_gamma_hat_kerr_expected;\n}\n\n// Compute expected value for LHS of eq 12i\n//\n// \\partial_t b will not be 0 for KerrSchild if evolve_shift == true\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::I<DataVector, SpatialDim, FrameType> get_dt_b_kerr_expected(\n    const Ccz4::EvolveShift evolve_shift, const Scalar<DataVector>& eta,\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift,\n    const tnsr::iJ<DataVector, SpatialDim, FrameType>& d_gamma_hat,\n    const tnsr::I<DataVector, SpatialDim, FrameType>& b,\n    const tnsr::iJ<DataVector, SpatialDim, FrameType>& d_b) {\n  tnsr::I<DataVector, SpatialDim, FrameType> dt_b_kerr_expected(get(eta));\n  if (static_cast<bool>(evolve_shift)) {\n    // s == 1\n    for (size_t i = 0; i < SpatialDim; i++) {\n      dt_b_kerr_expected.get(i) = -get(eta) * b.get(i) +\n                                  shift.get(0) * d_b.get(0, i) -\n                                  shift.get(0) * d_gamma_hat.get(0, i);\n      for (size_t k = 1; k < SpatialDim; k++) {\n        dt_b_kerr_expected.get(i) +=\n            shift.get(k) * d_b.get(k, i) - shift.get(k) * d_gamma_hat.get(k, i);\n      }\n    }\n  } else {\n    // s == 0\n    for (auto& component : dt_b_kerr_expected) {\n      component = 0.0;\n    }\n  }\n  return dt_b_kerr_expected;\n}\n\n// Compute expected value for LHS of eq 12l\n//\n// \\partial_t D_{kij} will not be 0 for KerrSchild if evolve_shift == false\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ijj<DataVector, SpatialDim, FrameType> get_dt_field_d_kerr_expected(\n    const Ccz4::EvolveShift evolve_shift,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& conformal_spatial_metric,\n    const tnsr::ijK<DataVector, SpatialDim, FrameType>& d_field_b) {\n  tnsr::ijj<DataVector, SpatialDim, FrameType> dt_field_d_kerr_expected(\n      get<0, 0>(conformal_spatial_metric));\n  if (static_cast<bool>(evolve_shift)) {\n    // s == 1\n    for (auto& component : dt_field_d_kerr_expected) {\n      component = 0.0;\n    }\n  } else {\n    // s == 0\n    //\n    // When s == 1, \\partial_t D_{kij} == 0, so when s == 0,\n    // \\partial_t D_{kij} = terms in eq 12l with s as a factor\n    for (size_t k = 0; k < SpatialDim; k++) {\n      for (size_t j = 0; j < SpatialDim; j++) {\n        for (size_t i = 0; i < SpatialDim; i++) {\n          dt_field_d_kerr_expected.get(k, i, j) = 0.0;\n          for (size_t m = 0; m < SpatialDim; m++) {\n            dt_field_d_kerr_expected.get(k, i, j) +=\n                0.5 * conformal_spatial_metric.get(i, j) *\n                    (d_field_b.get(k, m, m) + d_field_b.get(m, k, m)) / 3.0 -\n                0.25 * conformal_spatial_metric.get(m, i) *\n                    (d_field_b.get(k, j, m) + d_field_b.get(j, k, m)) -\n                0.25 * conformal_spatial_metric.get(m, j) *\n                    (d_field_b.get(k, i, m) + d_field_b.get(i, k, m));\n          }\n        }\n      }\n    }\n  }\n  return dt_field_d_kerr_expected;\n}\n\n// Compute expected value for LHS of eq 12m\n//\n// \\partial_t P_i will not be 0 for KerrSchild if evolve_shift == false\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::i<DataVector, SpatialDim, FrameType> get_dt_field_p_kerr_expected(\n    const Ccz4::EvolveShift evolve_shift,\n    const tnsr::ijK<DataVector, SpatialDim, FrameType>& d_field_b) {\n  tnsr::i<DataVector, SpatialDim, FrameType> dt_field_p_kerr_expected(\n      get<0, 0, 0>(d_field_b));\n  if (static_cast<bool>(evolve_shift)) {\n    // s == 1\n    for (auto& component : dt_field_p_kerr_expected) {\n      component = 0.0;\n    }\n  } else {\n    // s == 0\n    //\n    // When s == 1, \\partial_t P_i == 0, so when s == 0,\n    // \\partial_t P_i = terms in eq 12m with s as a factor\n    // (red terms that cancel out are ignored)\n    for (size_t k = 0; k < SpatialDim; k++) {\n      dt_field_p_kerr_expected.get(k) = 0.0;\n      for (size_t i = 0; i < SpatialDim; i++) {\n        dt_field_p_kerr_expected.get(k) +=\n            d_field_b.get(k, i, i) + d_field_b.get(i, k, i);\n      }\n      dt_field_p_kerr_expected.get(k) /= 6.0;  // *= 0.5 / 3\n    }\n  }\n  return dt_field_p_kerr_expected;\n}\n\n// \\brief Test first order CCZ4 with different binary settings against\n// KerrSchild\n//\n// \\details Tests that most time derivatives are 0. Depending on whether or not\n// the shift is evolved, some evolution variables will have non-zero time\n// derivatives. The evolution equations are eq 12a - 12m in\n// \\cite Dumbser2017okk. More concretely, depending on whether s == 1 or s == 0,\n// the time derivatives in eq 12c, 12h, 12i, 12k, 12l, and 12m may or may not be\n// expected to be 0. For cases when the time derivative of an evolution\n// variable is expected to be non-zero, the test checks for the expected\n// non-zero value by computing excess/missing terms due to the value of s.\n//\n// \\param evolve_shift whether or not to evolve the shift\n// \\param slicing_condition_type which slicing condition to use, e.g.\n// harmonic, 1 + log\nvoid test_kerrschild(const Ccz4::EvolveShift evolve_shift,\n                     const Ccz4::SlicingConditionType slicing_condition_type) {\n  const size_t SpatialDim = 3;\n  using FrameType = Frame::Inertial;\n\n  // Setup solution\n  const double mass = 2.;\n  const std::array<double, 3> spin{{0.3, 0.5, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  // Setup grid\n  const size_t num_points_1d = 6;\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n  Mesh<SpatialDim> mesh{num_points_1d, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, FrameType>(Affine3D{\n          Affine{-1., 1., lower_bound[0], upper_bound[0]},\n          Affine{-1., 1., lower_bound[1], upper_bound[1]},\n          Affine{-1., 1., lower_bound[2], upper_bound[2]},\n      });\n  const size_t num_points_3d = num_points_1d * num_points_1d * num_points_1d;\n  const DataVector used_for_size =\n      DataVector(num_points_3d, std::numeric_limits<double>::signaling_NaN());\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n  // Evaliuate analytic solution\n  const auto kerrschild_vars = solution.variables(\n      x, t, typename gr::Solutions::KerrSchild::tags<DataVector>{});\n\n  // Get ingredients for computing arguments to Ccz4::TimeDerivative\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim, FrameType>>(\n          kerrschild_vars);\n  const auto& d_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim>,\n                      tmpl::size_t<SpatialDim>, FrameType>>(kerrschild_vars);\n  const auto det_spatial_metric = determinant(spatial_metric);\n  const auto d_det_spatial_metric =\n      get<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          solution.variables(\n              x, t,\n              tmpl::list<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim,\n                                                         FrameType>>{}));\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim>>>(\n          kerrschild_vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          kerrschild_vars);\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(kerrschild_vars);\n  const auto& d_lapse =\n      get<Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<SpatialDim>,\n                      FrameType>>(kerrschild_vars);\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, SpatialDim, FrameType>>(kerrschild_vars);\n  const auto& d_shift =\n      get<Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, FrameType>,\n                      tmpl::size_t<SpatialDim>, FrameType>>(kerrschild_vars);\n\n  // Params\n  const double c = 1.0;\n  const double cleaning_speed = 1.6;  // e in the paper\n  const double f = 0.75;\n  const double kappa_1 = 0.1;\n  const double kappa_2 = 0.3;\n  const double kappa_3 = 0.4;\n  const double mu = 0.7;\n  const double one_over_relaxation_time = 10.0;         // \\tau^{-1}\n  const Scalar<DataVector> slicing_condition =          // g(\\alpha)\n      get_slicing_condition(slicing_condition_type, lapse);\n\n  // Choose free variables \\Theta, K_0, b^i, and \\eta\n  const auto theta = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  const auto d_theta =\n      make_with_value<tnsr::i<DataVector, SpatialDim, FrameType>>(used_for_size,\n                                                                  0.0);\n\n  const auto extrinsic_curvature =\n      gr::extrinsic_curvature(lapse, shift, d_shift, spatial_metric,\n                              dt_spatial_metric, d_spatial_metric);\n\n  // K = K_{ij} * \\gamma^ij\n  const Scalar<DataVector> trace_extrinsic_curvature =\n      get_trace_extrinsic_curvature(extrinsic_curvature,\n                                    inverse_spatial_metric);\n  const auto d_trace_extrinsic_curvature = partial_derivative(\n      trace_extrinsic_curvature, mesh, coord_map.inv_jacobian(x_logical));\n\n  // Solve eq 4g for K_0, where \\partial_t \\alpha = 0\n  const Scalar<DataVector> k_0 =\n      get_k_0_kerr(shift, lapse, d_lapse, slicing_condition, theta,\n                   trace_extrinsic_curvature);\n  const auto d_k_0 =\n      partial_derivative(k_0, mesh, coord_map.inv_jacobian(x_logical));\n\n  // Solve eq 12c for b^i, where \\partial_t \\beta^i = 0\n  const tnsr::I<DataVector, SpatialDim, FrameType> b =\n      get_b_kerr(evolve_shift, shift, d_shift, f);\n  const auto d_b =\n      partial_derivative(b, mesh, coord_map.inv_jacobian(x_logical));\n\n  const auto eta = make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n\n  // Compute arguments for Ccz4::TimeDerivative\n  Scalar<DataVector> ln_lapse{};\n  get(ln_lapse) = log(get(lapse));\n\n  // eq 6\n  const tnsr::i<DataVector, SpatialDim, FrameType> field_a =\n      get_field_a(lapse, d_lapse);\n\n  const auto d_d_lapse =\n      partial_derivative(d_lapse, mesh, coord_map.inv_jacobian(x_logical));\n  // \\partial_i A_j , where A_i is defined in eq 6\n  const tnsr::ij<DataVector, SpatialDim, FrameType> d_field_a =\n      get_d_field_a(lapse, d_lapse, d_d_lapse);\n\n  // eq 6\n  const auto& field_b = d_shift;\n  const auto d_field_b =\n      partial_derivative(field_b, mesh, coord_map.inv_jacobian(x_logical));\n\n  const auto conformal_factor = pow(get(det_spatial_metric), -1. / 6.);\n  Scalar<DataVector> conformal_factor_squared{};\n  get(conformal_factor_squared) = square(conformal_factor);\n  Scalar<DataVector> ln_conformal_factor{};\n  get(ln_conformal_factor) = log(conformal_factor);\n\n  // eq 3\n  const auto a_tilde =\n      Ccz4::a_tilde(conformal_factor_squared, spatial_metric,\n                    extrinsic_curvature, trace_extrinsic_curvature);\n  const auto d_a_tilde =\n      partial_derivative(a_tilde, mesh, coord_map.inv_jacobian(x_logical));\n\n  // \\tilde{\\gamma}_{ij} = \\phi^2 \\gamma_{ij}\n  const tnsr::ii<DataVector, SpatialDim, FrameType> conformal_spatial_metric =\n      get_conformal_spatial_metric(conformal_factor_squared, spatial_metric);\n  // \\partial_k \\tilde{\\gamma}_{ij}\n  const tnsr::ijj<DataVector, SpatialDim, FrameType>\n      d_conformal_spatial_metric = get_d_conformal_spatial_metric(\n          conformal_factor_squared, spatial_metric, d_spatial_metric,\n          d_det_spatial_metric);\n\n  const auto inverse_conformal_spatial_metric =\n      determinant_and_inverse(conformal_spatial_metric).second;\n\n  // eq 6\n  const tnsr::ijj<DataVector, SpatialDim, FrameType> field_d =\n      get_field_d(d_conformal_spatial_metric);\n  const auto d_field_d =\n      partial_derivative(field_d, mesh, coord_map.inv_jacobian(x_logical));\n\n  // eq 14\n  const tnsr::iJJ<DataVector, SpatialDim, FrameType> field_d_up =\n      get_field_d_up(inverse_conformal_spatial_metric, field_d);\n\n  // eq 6\n  const tnsr::i<DataVector, SpatialDim, FrameType> field_p =\n      get_field_p(det_spatial_metric, d_det_spatial_metric);\n  const auto d_field_p =\n      partial_derivative(field_p, mesh, coord_map.inv_jacobian(x_logical));\n\n  // eq 15\n  const auto conformal_christoffel_second_kind =\n      Ccz4::conformal_christoffel_second_kind(inverse_conformal_spatial_metric,\n                                              field_d);\n  // eq 16\n  const auto d_conformal_christoffel_second_kind =\n      Ccz4::deriv_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, field_d, d_field_d, field_d_up);\n\n  // eq 23\n  const auto contracted_conformal_christoffel_second_kind =\n      Ccz4::contracted_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, conformal_christoffel_second_kind);\n  // eq 24\n  const auto d_contracted_conformal_christoffel_second_kind =\n      Ccz4::deriv_contracted_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, field_d_up,\n          conformal_christoffel_second_kind,\n          d_conformal_christoffel_second_kind);\n\n  // If Z4 constraint is 0, then \\hat{\\gamma} == \\tilde{\\gamma}\n  const auto& gamma_hat = contracted_conformal_christoffel_second_kind;\n  const auto& d_gamma_hat = d_contracted_conformal_christoffel_second_kind;\n\n  // LHS time derivatives of evolved variables: eq 12a - 12m\n  tnsr::ii<DataVector, SpatialDim> dt_conformal_spatial_metric_actual(\n      used_for_size);\n  Scalar<DataVector> dt_ln_lapse_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim> dt_shift_actual(used_for_size);\n  Scalar<DataVector> dt_ln_conformal_factor_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim> dt_a_tilde_actual(used_for_size);\n  Scalar<DataVector> dt_trace_extrinsic_curvature_actual(used_for_size);\n  Scalar<DataVector> dt_theta_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim> dt_gamma_hat_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim> dt_b_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> dt_field_a_actual(used_for_size);\n  tnsr::iJ<DataVector, SpatialDim> dt_field_b_actual(used_for_size);\n  tnsr::ijj<DataVector, SpatialDim> dt_field_d_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> dt_field_p_actual(used_for_size);\n  // quantities we need for computing eq 12 - 27\n  Scalar<DataVector> conformal_factor_squared_actual(used_for_size);\n  Scalar<DataVector> det_conformal_spatial_metric_actual(used_for_size);\n  tnsr::II<DataVector, SpatialDim> inv_conformal_spatial_metric_actual(\n      used_for_size);\n  tnsr::II<DataVector, SpatialDim> inv_spatial_metric_actual(used_for_size);\n  Scalar<DataVector> lapse_actual(used_for_size);\n  Scalar<DataVector> slicing_condition_actual(used_for_size);\n  Scalar<DataVector> d_slicing_condition_actual(used_for_size);\n  tnsr::II<DataVector, SpatialDim> inv_a_tilde_actual(used_for_size);\n  // quantities we need for computing eq 12 - 27\n  tnsr::ij<DataVector, SpatialDim> a_tilde_times_field_b_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim>\n      a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde_actual(\n          used_for_size);\n  Scalar<DataVector> contracted_field_b_actual(used_for_size);\n  tnsr::ijK<DataVector, SpatialDim> symmetrized_d_field_b_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> contracted_symmetrized_d_field_b_actual(\n      used_for_size);\n  tnsr::ijk<DataVector, SpatialDim> field_b_times_field_d_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> field_d_up_times_a_tilde_actual(\n      used_for_size);\n  tnsr::I<DataVector, SpatialDim> contracted_field_d_up_actual(used_for_size);\n  Scalar<DataVector> half_conformal_factor_squared_actual(used_for_size);\n  tnsr::ij<DataVector, SpatialDim> conformal_metric_times_field_b_actual(\n      used_for_size);\n  tnsr::ijk<DataVector, SpatialDim>\n      conformal_metric_times_symmetrized_d_field_b_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim> conformal_metric_times_trace_a_tilde_actual(\n      used_for_size);\n  tnsr::i<DataVector, SpatialDim> inv_conformal_metric_times_d_a_tilde_actual(\n      used_for_size);\n  tnsr::I<DataVector, SpatialDim>\n      gamma_hat_minus_contracted_conformal_christoffel_actual(used_for_size);\n  tnsr::iJ<DataVector, SpatialDim>\n      d_gamma_hat_minus_contracted_conformal_christoffel_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> contracted_christoffel_second_kind_actual(\n      used_for_size);\n  tnsr::ij<DataVector, SpatialDim>\n      contracted_d_conformal_christoffel_difference_actual(used_for_size);\n  Scalar<DataVector> k_minus_2_theta_c_actual(used_for_size);\n  Scalar<DataVector> k_minus_k0_minus_2_theta_c_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim> lapse_times_a_tilde_actual(used_for_size);\n  tnsr::ijj<DataVector, SpatialDim> lapse_times_d_a_tilde_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> lapse_times_field_a_actual(used_for_size);\n  tnsr::ii<DataVector, SpatialDim> lapse_times_conformal_spatial_metric_actual(\n      used_for_size);\n  Scalar<DataVector> lapse_times_slicing_condition_actual(used_for_size);\n  Scalar<DataVector>\n      lapse_times_ricci_scalar_plus_divergence_z4_constraint_actual(\n          used_for_size);\n  tnsr::I<DataVector, SpatialDim> shift_times_deriv_gamma_hat_actual(\n      used_for_size);\n  tnsr::ii<DataVector, SpatialDim> inv_tau_times_conformal_metric_actual(\n      used_for_size);\n  // expressions and identities needed for evolution equations: eq 13 - 27\n  Scalar<DataVector> trace_a_tilde_actual(used_for_size);\n  tnsr::iJJ<DataVector, SpatialDim> field_d_up_actual(used_for_size);\n  tnsr::Ijj<DataVector, SpatialDim> conformal_christoffel_second_kind_actual(\n      used_for_size);\n  tnsr::iJkk<DataVector, SpatialDim> d_conformal_christoffel_second_kind_actual(\n      used_for_size);\n  tnsr::Ijj<DataVector, SpatialDim> christoffel_second_kind_actual(\n      used_for_size);\n  tnsr::ii<DataVector, SpatialDim> spatial_ricci_tensor_actual(used_for_size);\n  tnsr::ij<DataVector, SpatialDim> grad_grad_lapse_actual(used_for_size);\n  Scalar<DataVector> divergence_lapse_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim>\n      contracted_conformal_christoffel_second_kind_actual(used_for_size);\n  tnsr::iJ<DataVector, SpatialDim>\n      d_contracted_conformal_christoffel_second_kind_actual(used_for_size);\n  tnsr::i<DataVector, SpatialDim> spatial_z4_constraint_actual(used_for_size);\n  tnsr::I<DataVector, SpatialDim> upper_spatial_z4_constraint_actual(\n      used_for_size);\n  tnsr::ij<DataVector, SpatialDim> grad_spatial_z4_constraint_actual(\n      used_for_size);\n  Scalar<DataVector> ricci_scalar_plus_divergence_z4_constraint_actual(\n      used_for_size);\n\n  ::Ccz4::TimeDerivative<SpatialDim>::apply(\n      make_not_null(&dt_conformal_spatial_metric_actual),\n      make_not_null(&dt_ln_lapse_actual), make_not_null(&dt_shift_actual),\n      make_not_null(&dt_ln_conformal_factor_actual),\n      make_not_null(&dt_a_tilde_actual),\n      make_not_null(&dt_trace_extrinsic_curvature_actual),\n      make_not_null(&dt_theta_actual), make_not_null(&dt_gamma_hat_actual),\n      make_not_null(&dt_b_actual), make_not_null(&dt_field_a_actual),\n      make_not_null(&dt_field_b_actual), make_not_null(&dt_field_d_actual),\n      make_not_null(&dt_field_p_actual),\n      make_not_null(&conformal_factor_squared_actual),\n      make_not_null(&det_conformal_spatial_metric_actual),\n      make_not_null(&inv_conformal_spatial_metric_actual),\n      make_not_null(&inv_spatial_metric_actual), make_not_null(&lapse_actual),\n      make_not_null(&slicing_condition_actual),\n      make_not_null(&d_slicing_condition_actual),\n      make_not_null(&inv_a_tilde_actual),\n      make_not_null(&a_tilde_times_field_b_actual),\n      make_not_null(\n          &a_tilde_minus_one_third_conformal_metric_times_trace_a_tilde_actual),\n      make_not_null(&contracted_field_b_actual),\n      make_not_null(&symmetrized_d_field_b_actual),\n      make_not_null(&contracted_symmetrized_d_field_b_actual),\n      make_not_null(&field_b_times_field_d_actual),\n      make_not_null(&field_d_up_times_a_tilde_actual),\n      make_not_null(&contracted_field_d_up_actual),\n      make_not_null(&half_conformal_factor_squared_actual),\n      make_not_null(&conformal_metric_times_field_b_actual),\n      make_not_null(&conformal_metric_times_symmetrized_d_field_b_actual),\n      make_not_null(&conformal_metric_times_trace_a_tilde_actual),\n      make_not_null(&inv_conformal_metric_times_d_a_tilde_actual),\n      make_not_null(&gamma_hat_minus_contracted_conformal_christoffel_actual),\n      make_not_null(&d_gamma_hat_minus_contracted_conformal_christoffel_actual),\n      make_not_null(&contracted_christoffel_second_kind_actual),\n      make_not_null(&contracted_d_conformal_christoffel_difference_actual),\n      make_not_null(&k_minus_2_theta_c_actual),\n      make_not_null(&k_minus_k0_minus_2_theta_c_actual),\n      make_not_null(&lapse_times_a_tilde_actual),\n      make_not_null(&lapse_times_d_a_tilde_actual),\n      make_not_null(&lapse_times_field_a_actual),\n      make_not_null(&lapse_times_conformal_spatial_metric_actual),\n      make_not_null(&lapse_times_slicing_condition_actual),\n      make_not_null(\n          &lapse_times_ricci_scalar_plus_divergence_z4_constraint_actual),\n      make_not_null(&shift_times_deriv_gamma_hat_actual),\n      make_not_null(&inv_tau_times_conformal_metric_actual),\n      make_not_null(&trace_a_tilde_actual), make_not_null(&field_d_up_actual),\n      make_not_null(&conformal_christoffel_second_kind_actual),\n      make_not_null(&d_conformal_christoffel_second_kind_actual),\n      make_not_null(&christoffel_second_kind_actual),\n      make_not_null(&spatial_ricci_tensor_actual),\n      make_not_null(&grad_grad_lapse_actual),\n      make_not_null(&divergence_lapse_actual),\n      make_not_null(&contracted_conformal_christoffel_second_kind_actual),\n      make_not_null(&d_contracted_conformal_christoffel_second_kind_actual),\n      make_not_null(&spatial_z4_constraint_actual),\n      make_not_null(&upper_spatial_z4_constraint_actual),\n      make_not_null(&grad_spatial_z4_constraint_actual),\n      make_not_null(&ricci_scalar_plus_divergence_z4_constraint_actual), c,\n      cleaning_speed, eta, f, k_0, d_k_0, kappa_1, kappa_2, kappa_3, mu,\n      one_over_relaxation_time, evolve_shift, slicing_condition_type,\n      conformal_spatial_metric, ln_lapse, shift, ln_conformal_factor, a_tilde,\n      trace_extrinsic_curvature, theta, gamma_hat, b, field_a, field_b, field_d,\n      field_p, d_a_tilde, d_trace_extrinsic_curvature, d_theta, d_gamma_hat,\n      d_b, d_field_a, d_field_b, d_field_d, d_field_p);\n\n  const auto zero = DataVector(used_for_size.size(), 0.0);\n\n  // Check time derivatives eq (12a) - (12m)\n\n  // eq 12a\n  for (auto& component : dt_conformal_spatial_metric_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  // eq 12b\n  for (auto& component : dt_ln_lapse_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  // eq 12c\n  for (auto& component : dt_shift_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  // eq 12d\n  for (auto& component : dt_ln_conformal_factor_actual) {\n    CHECK_ITERABLE_APPROX(component, zero);\n  }\n  // eq 12e\n  Approx approx_12e = Approx::custom().epsilon(1e-11).scale(1.0);\n  for (auto& component : dt_a_tilde_actual) {\n    CHECK_ITERABLE_CUSTOM_APPROX(component, zero, approx_12e);\n  }\n  // eq 12f\n  Approx approx_12f = Approx::custom().epsilon(1e-11).scale(1.0);\n  for (auto& component : dt_trace_extrinsic_curvature_actual) {\n    CHECK_ITERABLE_CUSTOM_APPROX(component, zero, approx_12f);\n  }\n  // eq 12g\n  Approx approx_12g = Approx::custom().epsilon(1e-11).scale(1.0);\n  for (auto& component : dt_theta_actual) {\n    CHECK_ITERABLE_CUSTOM_APPROX(component, zero, approx_12g);\n  }\n  // eq 12h\n  // \\partial_t \\hat{\\gamma}^i will not be 0 for KerrSchild if\n  // evolve_shift == false\n  const tnsr::I<DataVector, SpatialDim, FrameType> dt_gamma_hat_expected =\n      get_dt_gamma_hat_kerr_expected(\n          evolve_shift, inverse_conformal_spatial_metric, d_field_b);\n  Approx approx_12h = Approx::custom().epsilon(1e-11).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_gamma_hat_actual, dt_gamma_hat_expected,\n                               approx_12h);\n  // eq 12i\n  // \\partial_t b will not be 0 for KerrSchild if evolve_shift == true\n  const tnsr::I<DataVector, SpatialDim, FrameType> dt_b_expected =\n      get_dt_b_kerr_expected(evolve_shift, eta, shift, d_gamma_hat, b, d_b);\n  Approx approx_12i = Approx::custom().epsilon(1e-11).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_b_actual, dt_b_expected, approx_12i);\n  // eq 12j\n  Approx approx_12j = Approx::custom().epsilon(1e-11).scale(1.0);\n  for (auto& component : dt_field_a_actual) {\n    CHECK_ITERABLE_CUSTOM_APPROX(component, zero, approx_12j);\n  }\n  // eq 12k\n  Approx approx_12k = Approx::custom().epsilon(1e-11).scale(1.0);\n  for (auto& component : dt_field_b_actual) {\n    CHECK_ITERABLE_CUSTOM_APPROX(component, zero, approx_12k);\n  }\n  // eq 12l\n  // \\partial_t D_{kij} will not be 0 for KerrSchild if evolve_shift == false\n  const tnsr::ijj<DataVector, SpatialDim, FrameType> dt_field_d_expected =\n      get_dt_field_d_kerr_expected(evolve_shift, conformal_spatial_metric,\n                                   d_field_b);\n  Approx approx_12l = Approx::custom().epsilon(1e-11).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_field_d_actual, dt_field_d_expected,\n                               approx_12l);\n  // eq 12m\n  // \\partial_t P_i will not be 0 for KerrSchild if evolve_shift == false\n  const tnsr::i<DataVector, SpatialDim, FrameType> dt_field_p_expected =\n      get_dt_field_p_kerr_expected(evolve_shift, d_field_b);\n  Approx approx_12m = Approx::custom().epsilon(1e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_field_p_actual, dt_field_p_expected,\n                               approx_12m);\n}\n\n// Test first order CCZ4 against Minkowski and KerrSchild\nvoid test(const Ccz4::EvolveShift evolve_shift,\n          const Ccz4::SlicingConditionType slicing_condition_type) {\n  test_minkowski(evolve_shift, slicing_condition_type);\n  test_kerrschild(evolve_shift, slicing_condition_type);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.TimeDerivative\",\n                  \"[Unit][Evolution]\") {\n  // Test first order CCZ4 with different settings\n  test(Ccz4::EvolveShift::True, Ccz4::SlicingConditionType::Harmonic);\n  test(Ccz4::EvolveShift::True, Ccz4::SlicingConditionType::Log);\n  test(Ccz4::EvolveShift::False, Ccz4::SlicingConditionType::Harmonic);\n  test(Ccz4::EvolveShift::False, Ccz4::SlicingConditionType::Log);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Test_Z4Constraint.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Ccz4/Z4Constraint.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_spatial_z4_constraint(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::i<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&)>(\n          &Ccz4::spatial_z4_constraint<DataType, Dim, Frame::Inertial>),\n      \"Z4Constraint\", \"spatial_z4_constraint\", {{{-1., 1.}}}, used_for_size);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_upper_spatial_z4_constraint(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&)>(\n          &Ccz4::upper_spatial_z4_constraint<DataType, Dim, Frame::Inertial>),\n      \"Z4Constraint\", \"upper_spatial_z4_constraint\", {{{-1., 1.}}},\n      used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Ccz4.Z4Constraint\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"Evolution/Systems/Ccz4/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_spatial_z4_constraint,\n                                    (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_upper_spatial_z4_constraint,\n                                    (1, 2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/Ccz4/Z4Constraint.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef spatial_z4_constraint(\n    conformal_spatial_metric, gamma_hat_minus_contracted_conformal_christoffel\n):\n    return 0.5 * np.einsum(\n        \"ij,j\",\n        conformal_spatial_metric,\n        gamma_hat_minus_contracted_conformal_christoffel,\n    )\n\n\ndef upper_spatial_z4_constraint(\n    half_conformal_factor_squared,\n    gamma_hat_minus_contracted_conformal_christoffel,\n):\n    return (\n        half_conformal_factor_squared\n        * gamma_hat_minus_contracted_conformal_christoffel\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Actions/Test_SetInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/InitialData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Factory.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Actions {\nnamespace {\n\nusing all_scalar_vars =\n    tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n               CurvedScalarWave::Tags::Phi<3>>;\n\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using component_being_mocked = void;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<\n              ::Tags::Variables<all_scalar_vars>,\n              domain::Tags::Coordinates<3, Frame::Inertial>, ::Tags::Time,\n              gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<CurvedScalarWave::Actions::SetInitialData,\n                     CurvedScalarWave::Actions::ReceiveNumericInitialData>>>;\n};\n\nstruct MockReadVolumeData {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      DataBox& /*box*/, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const importers::ImporterOptions& options, const size_t volume_data_id,\n      tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n          importers::Tags::Selected, NumericInitialData::all_vars>>\n          selected_fields) {\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        get<evolution::initial_data::Tags::InitialData>(cache));\n    CHECK(options == initial_data.importer_options());\n    CHECK(volume_data_id == initial_data.volume_data_id());\n    CHECK(get<importers::Tags::Selected<CurvedScalarWave::Tags::Psi>>(\n              selected_fields) == \"CustomPsi\");\n    CHECK(get<importers::Tags::Selected<CurvedScalarWave::Tags::Pi>>(\n              selected_fields) == \"CustomPi\");\n    CHECK(get<importers::Tags::Selected<CurvedScalarWave::Tags::Phi<3>>>(\n              selected_fields) == \"CustomPhi\");\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockVolumeDataReader {\n  using component_being_mocked = importers::ElementDataReader<Metavariables>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using replace_these_simple_actions =\n      tmpl::list<importers::Actions::ReadAllVolumeDataAndDistribute<\n          metavariables::volume_dim, NumericInitialData::all_vars,\n          MockElementArray<Metavariables>>>;\n  using with_these_simple_actions = tmpl::list<MockReadVolumeData>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  using component_list = tmpl::list<MockElementArray<Metavariables>,\n                                    MockVolumeDataReader<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            evolution::initial_data::InitialData,\n            tmpl::list<NumericInitialData,\n                       CurvedScalarWave::AnalyticData::PureSphericalHarmonic,\n                       ScalarWave::Solutions::PlaneWave<3>>>,\n        tmpl::pair<::MathFunction<1, Frame::Inertial>,\n                   MathFunctions::all_math_functions<1, Frame::Inertial>>>;\n  };\n};\n\nvoid test_set_initial_data(\n    const evolution::initial_data::InitialData& initial_data,\n    const std::string& option_string, const bool is_numeric) {\n  {\n    INFO(\"Factory creation\");\n    const auto created = TestHelpers::test_creation<\n        std::unique_ptr<evolution::initial_data::InitialData>, Metavariables>(\n        option_string);\n    if (is_numeric) {\n      CHECK(dynamic_cast<const NumericInitialData&>(*created) ==\n            dynamic_cast<const NumericInitialData&>(initial_data));\n    } else if (const auto* const pure_spherical_harmonic =\n                   dynamic_cast<const CurvedScalarWave::AnalyticData::\n                                    PureSphericalHarmonic*>(&initial_data)) {\n      CHECK(dynamic_cast<\n                const CurvedScalarWave::AnalyticData::PureSphericalHarmonic&>(\n                *created) == *pure_spherical_harmonic);\n    } else if (const auto* const plane_wave =\n                   dynamic_cast<const ScalarWave::Solutions::PlaneWave<3>*>(\n                       &initial_data)) {\n      CHECK(dynamic_cast<const ScalarWave::Solutions::PlaneWave<3>&>(\n                *created) == *plane_wave);\n    } else {\n      FAIL(\"Unexpected initial data type under test.\");\n    }\n  }\n\n  using reader_component = MockVolumeDataReader<Metavariables>;\n  using element_array = MockElementArray<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      initial_data.get_clone()};\n\n  ActionTesting::emplace_nodegroup_component<reader_component>(\n      make_not_null(&runner));\n\n  const ElementId<3> element_id{0};\n  const double initial_time = 0.;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.1, 10.0);\n  const size_t num_points = 100;\n  const auto shift = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&gen), make_not_null(&dist), DataVector{num_points});\n  const auto lapse = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&gen), make_not_null(&dist), DataVector{num_points});\n  const auto coords = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&gen), make_not_null(&dist), DataVector{num_points});\n\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), element_id,\n      {Variables<all_scalar_vars>{num_points}, coords, initial_time, lapse,\n       shift});\n\n  const auto get_element_tag = [&runner,\n                                &element_id](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n\n  if (is_numeric) {\n    REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n        make_not_null(&runner), element_id));\n\n    ActionTesting::invoke_queued_simple_action<reader_component>(\n        make_not_null(&runner), 0);\n\n    const auto& numeric_initial_data =\n        dynamic_cast<const NumericInitialData&>(initial_data);\n    auto& inbox = ActionTesting::get_inbox_tag<\n        element_array,\n        importers::Tags::VolumeData<NumericInitialData::all_vars>,\n        Metavariables>(make_not_null(&runner),\n                       element_id)[numeric_initial_data.volume_data_id()];\n\n    const CurvedScalarWave::AnalyticData::PureSphericalHarmonic source_data{\n        2.0, 1.0, {1, 0}};\n    const auto source_vars = source_data.variables(coords, all_scalar_vars{});\n    get<CurvedScalarWave::Tags::Psi>(inbox) =\n        get<CurvedScalarWave::Tags::Psi>(source_vars);\n    get<CurvedScalarWave::Tags::Pi>(inbox) =\n        get<CurvedScalarWave::Tags::Pi>(source_vars);\n    get<CurvedScalarWave::Tags::Phi<3>>(inbox) =\n        get<CurvedScalarWave::Tags::Phi<3>>(source_vars);\n\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              element_id);\n\n    tmpl::for_each<all_scalar_vars>(\n        [&get_element_tag, &source_vars](const auto tag_v) {\n          using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n          CHECK_ITERABLE_APPROX(get_element_tag(tag{}), get<tag>(source_vars));\n        });\n    return;\n  }\n\n  const auto& lapse_in_box = get_element_tag(gr::Tags::Lapse<DataVector>{});\n  const auto& shift_in_box = get_element_tag(gr::Tags::Shift<DataVector, 3>{});\n\n  const auto analytic_verifier = [&coords, &initial_time, &get_element_tag,\n                                  &lapse_in_box,\n                                  &shift_in_box](const auto& specific_data) {\n    using data_type = std::decay_t<decltype(specific_data)>;\n    if constexpr (tmpl::list_contains_v<typename data_type::tags,\n                                        CurvedScalarWave::Tags::Psi>) {\n      const auto curved_initial_data = evolution::Initialization::initial_data(\n          specific_data, coords, initial_time,\n          typename CurvedScalarWave::System<3>::variables_tag::tags_list{});\n      CHECK_ITERABLE_APPROX(\n          get_element_tag(CurvedScalarWave::Tags::Psi{}),\n          get<CurvedScalarWave::Tags::Psi>(curved_initial_data));\n      CHECK_ITERABLE_APPROX(\n          get_element_tag(CurvedScalarWave::Tags::Pi{}),\n          get<CurvedScalarWave::Tags::Pi>(curved_initial_data));\n      CHECK_ITERABLE_APPROX(\n          get_element_tag(CurvedScalarWave::Tags::Phi<3>{}),\n          get<CurvedScalarWave::Tags::Phi<3>>(curved_initial_data));\n    } else {\n      const auto flat_initial_data = evolution::Initialization::initial_data(\n          specific_data, coords, initial_time,\n          typename ScalarWave::System<3>::variables_tag::tags_list{});\n      const auto shift_dot_phi = dot_product(\n          shift_in_box, get<ScalarWave::Tags::Phi<3>>(flat_initial_data));\n      Scalar<DataVector> expected_pi{};\n      get(expected_pi) = (get(shift_dot_phi) +\n                          get(get<ScalarWave::Tags::Pi>(flat_initial_data))) /\n                         get(lapse_in_box);\n      CHECK_ITERABLE_APPROX(get_element_tag(CurvedScalarWave::Tags::Psi{}),\n                            get<ScalarWave::Tags::Psi>(flat_initial_data));\n      CHECK_ITERABLE_APPROX(get_element_tag(CurvedScalarWave::Tags::Phi<3>{}),\n                            get<ScalarWave::Tags::Phi<3>>(flat_initial_data));\n      CHECK_ITERABLE_APPROX(get_element_tag(CurvedScalarWave::Tags::Pi{}),\n                            expected_pi);\n    }\n  };\n\n  call_with_dynamic_type<\n      void, tmpl::list<CurvedScalarWave::AnalyticData::PureSphericalHarmonic,\n                       ScalarWave::Solutions::PlaneWave<3>>>(\n      &initial_data, [&analytic_verifier](const auto* const analytic_ptr) {\n        analytic_verifier(*analytic_ptr);\n      });\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.SetInitialData\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  test_set_initial_data(\n      NumericInitialData{\"TestInitialData.h5\",\n                         \"VolumeData\",\n                         0.,\n                         {1.0e-9},\n                         false,\n                         {\"CustomPsi\", \"CustomPi\", \"CustomPhi\"}},\n      \"NumericInitialData:\\n\"\n      \"  FileGlob: TestInitialData.h5\\n\"\n      \"  Subgroup: VolumeData\\n\"\n      \"  ObservationValue: 0.\\n\"\n      \"  ObservationValueEpsilon: 1e-9\\n\"\n      \"  ElementsAreIdentical: False\\n\"\n      \"  Variables:\\n\"\n      \"    Psi: CustomPsi\\n\"\n      \"    Pi: CustomPi\\n\"\n      \"    Phi: CustomPhi\",\n      true);\n  test_set_initial_data(\n      CurvedScalarWave::AnalyticData::PureSphericalHarmonic{2.0, 1.0, {2, -1}},\n      \"PureSphericalHarmonic:\\n\"\n      \"  Radius: 2.0 \\n\"\n      \"  Width: 1.0 \\n\"\n      \"  Mode: [2, -1]\",\n      false);\n  test_set_initial_data(\n      ScalarWave::Solutions::PlaneWave<3>{\n          {{1.5, -7.2, 2.7}},\n          {{2.4, -4.8, 8.4}},\n          std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(3)},\n      \"PlaneWave:\\n\"\n      \"  WaveVector: [1.5, -7.2, 2.7]\\n\"\n      \"  Center: [2.4, -4.8, 8.4]\\n\"\n      \"  Profile:\\n\"\n      \"    PowX:\\n\"\n      \"      Power: 3\",\n      false);\n}\n\n}  // namespace CurvedScalarWave::Actions\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/AnalyticConstant.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef error(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    inverse_spatial_metric_interior,\n    gamma1_interior,\n    gamma2_interior,\n    lapse_interior,\n    shift_interior,\n):\n    return None\n\n\ndef psi_analytic_constant(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    inverse_spatial_metric_interior,\n    gamma1_interior,\n    gamma2_interior,\n    lapse_interior,\n    shift_interior,\n):\n    return 2.71\n\n\ndef pi_analytic_constant(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    inverse_spatial_metric_interior,\n    gamma1_interior,\n    gamma2_interior,\n    lapse_interior,\n    shift_interior,\n):\n    return 0.0\n\n\ndef phi_analytic_constant(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    inverse_spatial_metric_interior,\n    gamma1_interior,\n    gamma2_interior,\n    lapse_interior,\n    shift_interior,\n):\n    return 0.0 * shift_interior\n\n\ndef lapse_analytic_constant(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    inverse_spatial_metric_interior,\n    gamma1_interior,\n    gamma2_interior,\n    lapse_interior,\n    shift_interior,\n):\n    return lapse_interior\n\n\ndef shift_analytic_constant(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    inverse_spatial_metric_interior,\n    gamma1_interior,\n    gamma2_interior,\n    lapse_interior,\n    shift_interior,\n):\n    return shift_interior\n\n\ndef constraint_gamma1_analytic_constant(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    inverse_spatial_metric_interior,\n    gamma1_interior,\n    gamma2_interior,\n    lapse_interior,\n    shift_interior,\n):\n    return gamma1_interior\n\n\ndef constraint_gamma2_analytic_constant(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    inverse_spatial_metric_interior,\n    gamma1_interior,\n    gamma2_interior,\n    lapse_interior,\n    shift_interior,\n):\n    return gamma2_interior\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom Evolution.Systems.CurvedScalarWave.Characteristics import (\n    char_speed_vminus,\n    char_speed_vplus,\n    char_speed_vpsi,\n    char_speed_vzero,\n)\n\n\ndef error(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    dt_psi,\n    dt_pi,\n    dt_phi,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    return None\n\n\ndef subtract_mesh_velocity(\n    face_mesh_velocity, dt_psi, dt_pi, dt_phi, d_psi, d_pi, d_phi\n):\n    if not (face_mesh_velocity is None):\n        dt_psi -= np.einsum(\"i,i->\", face_mesh_velocity, d_psi)\n        dt_pi -= np.einsum(\"i,i->\", face_mesh_velocity, d_pi)\n        dt_phi -= np.einsum(\"i,ij->j\", face_mesh_velocity, d_phi)\n\n\ndef dt_psi_constraint_preserving_spherical_radiation(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    dt_psi,\n    dt_pi,\n    dt_phi,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    subtract_mesh_velocity(\n        face_mesh_velocity, dt_psi, dt_pi, dt_phi, d_psi, d_pi, d_phi\n    )\n    char_speed_psi = char_speed_vpsi(gamma1, lapse, shift, normal_covector)\n\n    if face_mesh_velocity is not None:\n        char_speed_psi -= np.dot(normal_covector, face_mesh_velocity)\n\n    return np.dot(normal_vector, d_psi - phi) * min(0.0, char_speed_psi)\n\n\ndef dt_phi_constraint_preserving_spherical_radiation(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    dt_psi,\n    dt_pi,\n    dt_phi,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    subtract_mesh_velocity(\n        face_mesh_velocity, dt_psi, dt_pi, dt_phi, d_psi, d_pi, d_phi\n    )\n    char_speed_zero = char_speed_vzero(gamma1, lapse, shift, normal_covector)\n    if face_mesh_velocity is not None:\n        char_speed_zero -= np.dot(normal_covector, face_mesh_velocity)\n    return np.einsum(\"ij,j\", d_phi.T - d_phi, normal_vector) * min(\n        0, char_speed_zero\n    )\n\n\ndef dt_pi_constraint_preserving_spherical_radiation(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    dt_psi,\n    dt_pi,\n    dt_phi,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    dt_psi_correction = dt_psi_constraint_preserving_spherical_radiation(\n        face_mesh_velocity,\n        normal_covector,\n        normal_vector,\n        psi,\n        phi,\n        inertial_coords,\n        gamma1,\n        gamma2,\n        lapse,\n        shift,\n        dt_psi,\n        dt_pi,\n        dt_phi,\n        d_psi,\n        d_pi,\n        d_phi,\n    )\n    # dt's are already corrected in dt_psi\n    inv_radius = 1.0 / np.linalg.norm(inertial_coords)\n    bc_dt_pi = (\n        2.0 * inv_radius**2 * psi\n        + 4.0 * inv_radius * dt_psi\n        + 4.0 * inv_radius * np.dot(normal_vector, phi)\n        + 2.0 * np.dot(normal_vector, dt_phi)\n        + np.dot(shift, dt_phi)\n        + np.einsum(\"i,j,ij\", normal_vector, normal_vector, d_phi)\n    )\n    bc_dt_pi /= lapse\n    return bc_dt_pi - dt_pi + gamma2 * dt_psi_correction\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/DemandOutgoingCharSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom Evolution.Systems.CurvedScalarWave.Characteristics import (\n    char_speed_vminus,\n    char_speed_vplus,\n    char_speed_vpsi,\n    char_speed_vzero,\n)\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    gamma_1,\n    lapse,\n    shift,\n):\n    speeds = np.zeros(4)\n    speeds[0] = char_speed_vpsi(\n        gamma_1, lapse, shift, outward_directed_normal_covector\n    )\n    speeds[1] = char_speed_vzero(\n        gamma_1, lapse, shift, outward_directed_normal_covector\n    )\n    speeds[2] = char_speed_vplus(\n        gamma_1, lapse, shift, outward_directed_normal_covector\n    )\n    speeds[3] = char_speed_vminus(\n        gamma_1, lapse, shift, outward_directed_normal_covector\n    )\n    for speed in speeds:\n        if face_mesh_velocity is not None:\n            speed -= np.dot(\n                outward_directed_normal_covector, face_mesh_velocity\n            )\n        if speed < 0.0:\n            return \"Detected negative characteristic speed *\"\n    return None\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/Test_AnalyticConstant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/AnalyticConstant.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\nnamespace {\n\ntemplate <size_t Dim>\nvoid test() {\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      CurvedScalarWave::BoundaryConditions::AnalyticConstant<Dim>,\n      CurvedScalarWave::BoundaryConditions::BoundaryCondition<Dim>,\n      CurvedScalarWave::System<Dim>,\n      tmpl::list<CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.CurvedScalarWave.BoundaryConditions.\"\n      \"AnalyticConstant\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<CurvedScalarWave::Tags::Psi>,\n          helpers::Tags::PythonFunctionName<CurvedScalarWave::Tags::Pi>,\n          helpers::Tags::PythonFunctionName<CurvedScalarWave::Tags::Phi<Dim>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<\n              CurvedScalarWave::Tags::ConstraintGamma1>,\n          helpers::Tags::PythonFunctionName<\n              CurvedScalarWave::Tags::ConstraintGamma2>>{\n          \"error\", \"psi_analytic_constant\", \"pi_analytic_constant\",\n          \"phi_analytic_constant\", \"lapse_analytic_constant\",\n          \"shift_analytic_constant\", \"constraint_gamma1_analytic_constant\",\n          \"constraint_gamma2_analytic_constant\"},\n      \"AnalyticConstant:\\n\"\n      \"  Amplitude: 2.71\\n\",\n      Index<Dim - 1>{Dim == 1 ? 0 : 5}, db::DataBox<tmpl::list<>>{},\n      tuples::TaggedTuple<>{});\n}\n}  // namespace\nSPECTRE_TEST_CASE(\n    \"Unit.CurvedScalarWave.BoundaryConditions.AnalyticConstant\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/Test_ConstraintPreservingSphericalRadiation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\nnamespace {\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      CurvedScalarWave::BoundaryConditions::\n          ConstraintPreservingSphericalRadiation<Dim>,\n      CurvedScalarWave::BoundaryConditions::BoundaryCondition<Dim>,\n      CurvedScalarWave::System<Dim>,\n      tmpl::list<CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.CurvedScalarWave.BoundaryConditions.\"\n      \"ConstraintPreservingSphericalRadiation\",\n      tuples::TaggedTuple<helpers::Tags::PythonFunctionForErrorMessage<>,\n                          helpers::Tags::PythonFunctionName<\n                              ::Tags::dt<CurvedScalarWave::Tags::Psi>>,\n                          helpers::Tags::PythonFunctionName<\n                              ::Tags::dt<CurvedScalarWave::Tags::Pi>>,\n                          helpers::Tags::PythonFunctionName<\n                              ::Tags::dt<CurvedScalarWave::Tags::Phi<Dim>>>>{\n          \"error\", \"dt_psi_constraint_preserving_spherical_radiation\",\n          \"dt_pi_constraint_preserving_spherical_radiation\",\n          \"dt_phi_constraint_preserving_spherical_radiation\"},\n      \"ConstraintPreservingSphericalRadiation:\\n\",\n      Index<Dim - 1>{Dim == 1 ? 1 : 5}, db::DataBox<tmpl::list<>>{},\n      tuples::TaggedTuple<>{});\n}\n}  // namespace\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CurvedScalarWave.ConstraintPreservingRadiation\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\nnamespace {\n\ntemplate <size_t Dim>\nvoid test() {\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      CurvedScalarWave::BoundaryConditions::DemandOutgoingCharSpeeds<Dim>,\n      CurvedScalarWave::BoundaryConditions::BoundaryCondition<Dim>,\n      CurvedScalarWave::System<Dim>,\n      tmpl::list<CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.CurvedScalarWave.BoundaryConditions.\"\n      \"DemandOutgoingCharSpeeds\",\n      tuples::TaggedTuple<helpers::Tags::PythonFunctionForErrorMessage<>>{\n          \"error\"},\n      \"DemandOutgoingCharSpeeds:\\n\", Index<Dim - 1>{Dim == 1 ? 0 : 5},\n      db::DataBox<tmpl::list<>>{}, tuples::TaggedTuple<>{});\n}\n}  // namespace\nSPECTRE_TEST_CASE(\n    \"Unit.CurvedScalarWave.BoundaryConditions.DemandOutgoingCharSpeeds\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/Test_Worldtube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/Worldtube.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\n// here we check that the dg_ghost field returns evolved variables which\n// correspond to `VMinus` being calculated by the worldtube solution and all\n// other fields calculated by the interior solution.\nvoid test_dg_ghost(const gsl::not_null<std::mt19937*> gen) {\n  static constexpr size_t Dim = 3;\n  std::uniform_real_distribution<> dist(-10., 10.);\n  gr::Solutions::KerrSchild kerr_schild{2., {0.1, 0.2, 0.3}, {0.4, 0.6, 0.9}};\n  const size_t num_points = 1000.;\n  const auto coords =\n      make_with_random_values<tnsr::I<DataVector, 3>>(gen, dist, num_points);\n  const auto spacetime_variables = kerr_schild.variables(\n      coords, 0.,\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n                 gr::Tags::InverseSpatialMetric<DataVector, Dim>>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(spacetime_variables);\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, Dim>>(spacetime_variables);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, Dim>>(spacetime_variables);\n\n  auto normal_covector =\n      make_with_random_values<tnsr::i<DataVector, Dim>>(gen, dist, num_points);\n  const auto covector_mag = magnitude(normal_covector, inverse_spatial_metric);\n  for (size_t i = 0; i < Dim; ++i) {\n    normal_covector.get(i) /= covector_mag.get();\n  }\n  const auto normal_vector = tenex::evaluate<ti::I>(\n      inverse_spatial_metric(ti::I, ti::J) * normal_covector(ti::j));\n  CHECK_ITERABLE_APPROX(dot_product(normal_covector, normal_vector).get(),\n                        DataVector(num_points, 1.));\n\n  const auto psi_interior =\n      make_with_random_values<Scalar<DataVector>>(gen, dist, num_points);\n  const auto pi_interior =\n      make_with_random_values<Scalar<DataVector>>(gen, dist, num_points);\n  const auto phi_interior =\n      make_with_random_values<tnsr::i<DataVector, 3>>(gen, dist, num_points);\n  const auto gamma1 =\n      make_with_random_values<Scalar<DataVector>>(gen, dist, num_points);\n  const auto gamma2 =\n      make_with_random_values<Scalar<DataVector>>(gen, dist, num_points);\n  const auto dt_psi =\n      make_with_random_values<Scalar<DataVector>>(gen, dist, num_points);\n  const auto d_psi =\n      make_with_random_values<tnsr::i<DataVector, 3>>(gen, dist, num_points);\n  const auto d_phi =\n      make_with_random_values<tnsr::ij<DataVector, 3>>(gen, dist, num_points);\n  const auto worldtube_vars = make_with_random_values<Variables<\n      tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<Dim>>>>(gen, dist, num_points);\n\n  Scalar<DataVector> psi_res(num_points);\n  Scalar<DataVector> pi_res(num_points);\n  tnsr::i<DataVector, Dim, Frame::Inertial> phi_res(num_points);\n  Scalar<DataVector> lapse_res(num_points);\n  tnsr::I<DataVector, Dim, Frame::Inertial> shift_res(num_points);\n  Scalar<DataVector> gamma1_res(num_points);\n  Scalar<DataVector> gamma2_res(num_points);\n  tnsr::II<DataVector, Dim, Frame::Inertial> inverse_spatial_metric_res(\n      num_points);\n\n  CurvedScalarWave::BoundaryConditions::Worldtube<Dim> worldtube_bcs{};\n  worldtube_bcs.dg_ghost(\n      make_not_null(&psi_res), make_not_null(&pi_res), make_not_null(&phi_res),\n      make_not_null(&lapse_res), make_not_null(&shift_res),\n      make_not_null(&gamma1_res), make_not_null(&gamma2_res),\n      make_not_null(&inverse_spatial_metric_res), std::nullopt, normal_covector,\n      normal_vector, psi_interior, pi_interior, phi_interior, lapse, shift,\n      inverse_spatial_metric, gamma1, gamma2, dt_psi, d_psi, d_phi,\n      worldtube_vars);\n\n  CHECK(lapse_res == lapse);\n  CHECK(shift_res == shift);\n  CHECK(inverse_spatial_metric_res == inverse_spatial_metric);\n  CHECK(gamma1_res == gamma1);\n  CHECK(gamma2_res == gamma2);\n  const auto unit_normal_vector = tenex::evaluate<ti::I>(\n      inverse_spatial_metric(ti::I, ti::J) * normal_covector(ti::j));\n  const auto char_fields_interior = CurvedScalarWave::characteristic_fields(\n      gamma2, psi_interior, pi_interior, phi_interior, normal_covector,\n      normal_vector);\n\n  const auto char_fields_worldtube = CurvedScalarWave::characteristic_fields(\n      gamma2, get<CurvedScalarWave::Tags::Psi>(worldtube_vars),\n      get<CurvedScalarWave::Tags::Pi>(worldtube_vars),\n      get<CurvedScalarWave::Tags::Phi<Dim>>(worldtube_vars), normal_covector,\n      normal_vector);\n\n  const auto char_fields_res = CurvedScalarWave::characteristic_fields(\n      gamma2, psi_res, pi_res, phi_res, normal_covector, normal_vector);\n  Approx approx = Approx::custom().epsilon(1.e-12).scale(1.);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get<CurvedScalarWave::Tags::VPsi>(char_fields_interior),\n      get<CurvedScalarWave::Tags::VPsi>(char_fields_res), approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get<CurvedScalarWave::Tags::VZero<Dim>>(char_fields_interior),\n      get<CurvedScalarWave::Tags::VZero<Dim>>(char_fields_res), approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get<CurvedScalarWave::Tags::VPlus>(char_fields_interior),\n      get<CurvedScalarWave::Tags::VPlus>(char_fields_res), approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get<CurvedScalarWave::Tags::VMinus>(char_fields_worldtube),\n      get<CurvedScalarWave::Tags::VMinus>(char_fields_res), approx);\n}\n\n// this takes a variables on the C++ side filled with 1 and returns only\n// 1 on the python side, point by point. There is little point in trying to\n// transfer the variables to the python side because it is unclear how the\n// different components should be packaged and ordered.\ntemplate <size_t NumGridPoints>\nstruct ConvertWorldtubeSolution {\n  using unpacked_container = double;\n  using packed_container = Variables<\n      tmpl::list<CurvedScalarWave::Tags::Psi, ::CurvedScalarWave::Tags::Pi,\n                 CurvedScalarWave::Tags::Phi<3>>>;\n  using packed_type = packed_container;\n  static constexpr size_t face_size = NumGridPoints * NumGridPoints;\n\n  static packed_container create_container() {\n    return packed_container{face_size, 1.};\n  }\n\n  static inline unpacked_container unpack(const packed_container /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return 1.;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = packed_container{face_size, 1.};\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    return packed.number_of_grid_points();\n  }\n};\n\nvoid test_python(const gsl::not_null<std::mt19937*> gen) {\n  static constexpr size_t Dim = 3;\n  std::uniform_real_distribution<> dist(-10., 10.);\n  static constexpr size_t num_grid_points = 5;\n  auto box = db::create<db::AddSimpleTags<\n      CurvedScalarWave::Worldtube::Tags::WorldtubeSolution<Dim>>>(\n      ConvertWorldtubeSolution<num_grid_points>::create_container());\n\n  namespace helpers = TestHelpers::evolution::dg;\n  helpers::test_boundary_condition_with_python<\n      CurvedScalarWave::BoundaryConditions::Worldtube<Dim>,\n      CurvedScalarWave::BoundaryConditions::BoundaryCondition<Dim>,\n      CurvedScalarWave::System<Dim>,\n      tmpl::list<CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>,\n      tmpl::list<ConvertWorldtubeSolution<num_grid_points>>,\n      tmpl::list<CurvedScalarWave::Worldtube::Tags::WorldtubeSolution<Dim>>>(\n      gen,\n      \"Evolution.Systems.CurvedScalarWave.BoundaryConditions.\"\n      \"Worldtube\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::dt<CurvedScalarWave::Tags::Psi>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::dt<CurvedScalarWave::Tags::Pi>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::dt<CurvedScalarWave::Tags::Phi<Dim>>>,\n          helpers::Tags::PythonFunctionName<CurvedScalarWave::Tags::Psi>,\n          helpers::Tags::PythonFunctionName<CurvedScalarWave::Tags::Pi>,\n          helpers::Tags::PythonFunctionName<CurvedScalarWave::Tags::Phi<Dim>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<\n              gr::Tags::InverseSpatialMetric<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<\n              CurvedScalarWave::Tags::ConstraintGamma1>,\n          helpers::Tags::PythonFunctionName<\n              CurvedScalarWave::Tags::ConstraintGamma2>>{\n          \"error\", \"dt_psi_worldtube\", \"dt_pi_worldtube\", \"dt_phi_worldtube\",\n          \"psi\", \"pi\", \"phi\", \"lapse\", \"shift\", \"inverse_spatial_metric\",\n          \"gamma1\", \"gamma2\"},\n      \"Worldtube:\\n\", Index<Dim - 1>{num_grid_points}, box,\n      tuples::TaggedTuple<>{});\n}\n\n}  // namespace\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CSW.BoundaryConditions.Worldtube\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  MAKE_GENERATOR(gen);\n  test_dg_ghost(make_not_null(&gen));\n  test_python(make_not_null(&gen));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/Worldtube.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\nfrom Evolution.Systems.CurvedScalarWave import Characteristics\n\n\ndef error(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    return None\n\n\ndef dt_psi_worldtube(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    char_speed_psi = Characteristics.char_speed_vpsi(\n        gamma1, lapse, shift, normal_covector\n    )\n\n    if face_mesh_velocity is not None:\n        char_speed_psi -= np.dot(normal_covector, face_mesh_velocity)\n\n    return np.dot(normal_vector, d_psi - phi) * min(0.0, char_speed_psi)\n\n\ndef dt_phi_worldtube(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    char_speed_zero = Characteristics.char_speed_vzero(\n        gamma1, lapse, shift, normal_covector\n    )\n    if face_mesh_velocity is not None:\n        char_speed_zero -= np.dot(normal_covector, face_mesh_velocity)\n    return np.einsum(\"ij,j\", d_phi.T - d_phi, normal_vector) * min(\n        0, char_speed_zero\n    )\n\n\ndef dt_pi_worldtube(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    return gamma2 * dt_psi_worldtube(\n        face_mesh_velocity,\n        normal_covector,\n        normal_vector,\n        psi,\n        pi,\n        phi,\n        lapse,\n        shift,\n        inverse_spatial_metric,\n        gamma1,\n        gamma2,\n        dt_psi,\n        d_psi,\n        d_phi,\n        worldtube_vars,\n        dim,\n    )\n\n\ndef psi(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    return psi\n\n\ndef pi(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    # The worldtube data on the C++ side is a Variables filled with ones.\n    # Since we do not want a Variables on the python side, we hard-code it here\n    wt_psi = 1.0\n    wt_pi = 1.0\n    wt_phi = np.ones(3)\n\n    vpsi_interior = Characteristics.char_field_vpsi(\n        gamma2, psi, pi, phi, normal_covector, normal_vector\n    )\n    vzero_interior = Characteristics.char_field_vzero(\n        gamma2, psi, pi, phi, normal_covector, normal_vector\n    )\n    vplus_interior = Characteristics.char_field_vplus(\n        gamma2, psi, pi, phi, normal_covector, normal_vector\n    )\n    vminus_wt = Characteristics.char_field_vminus(\n        gamma2, wt_psi, wt_pi, wt_phi, normal_covector, normal_vector\n    )\n\n    return Characteristics.evol_field_pi(\n        gamma2,\n        vpsi_interior,\n        vzero_interior,\n        vplus_interior,\n        vminus_wt,\n        normal_covector,\n    )\n\n\ndef phi(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    # The worldtube data on the C++ side is a Variables filled with ones.\n    # Since we do not want a Variables on the python side, we hard-code it here\n    wt_psi = 1.0\n    wt_pi = 1.0\n    wt_phi = np.ones(3)\n\n    vpsi_interior = Characteristics.char_field_vpsi(\n        gamma2, psi, pi, phi, normal_covector, normal_vector\n    )\n    vzero_interior = Characteristics.char_field_vzero(\n        gamma2, psi, pi, phi, normal_covector, normal_vector\n    )\n    vplus_interior = Characteristics.char_field_vplus(\n        gamma2, psi, pi, phi, normal_covector, normal_vector\n    )\n    vminus_wt = Characteristics.char_field_vminus(\n        gamma2, wt_psi, wt_pi, wt_phi, normal_covector, normal_vector\n    )\n    return Characteristics.evol_field_phi(\n        gamma2,\n        vpsi_interior,\n        vzero_interior,\n        vplus_interior,\n        vminus_wt,\n        normal_covector,\n    )\n\n\ndef lapse(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    return lapse\n\n\ndef shift(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    return shift\n\n\ndef gamma1(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    return gamma1\n\n\ndef gamma2(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    return gamma2\n\n\ndef inverse_spatial_metric(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    gamma1,\n    gamma2,\n    dt_psi,\n    d_psi,\n    d_phi,\n    worldtube_vars,\n    dim,\n):\n    return inverse_spatial_metric\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryConditions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryCorrections/Test_UpwindPenalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n#include <random>\n#include <string>\n\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen, const size_t num_pts) {\n  PUPable_reg(CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim>);\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      CurvedScalarWave::System<Dim>>(\n      gen,\n      \"Evolution.Systems.CurvedScalarWave.BoundaryCorrections.UpwindPenalty\",\n      \"dg_package_data\", \"dg_boundary_terms\",\n      CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n\n  const auto upwind_penalty = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>(\n      \"UpwindPenalty:\");\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      CurvedScalarWave::System<Dim>>(\n      gen,\n      \"Evolution.Systems.CurvedScalarWave.BoundaryCorrections.UpwindPenalty\",\n      \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<\n          const CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim>&>(\n          *upwind_penalty),\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n}\n\ntemplate <size_t Dim>\nvoid test_flat_spacetime(const gsl::not_null<std::mt19937*> gen) {\n  INFO(\"test consistency of the curved-scalar-wave upwind flux\");\n  std::uniform_real_distribution<> uniform_m11_dist(0.1, 1.);\n\n  const size_t num_pts = 3;\n  const DataVector value{1., 2., 3.};\n  const tnsr::I<DataVector, Dim> x{value};\n  const double t = 1.3;\n\n  gr::Solutions::Minkowski<Dim> minkowski{};\n  ScalarWave::BoundaryCorrections::UpwindPenalty<Dim> sw_flux_computer{};\n  CurvedScalarWave::BoundaryCorrections::UpwindPenalty<Dim> csw_flux_computer{};\n\n  // variables for CSW\n  auto csw_v_psi_int = Scalar<DataVector>(num_pts);\n  auto csw_v_psi_ext = Scalar<DataVector>(num_pts);\n  auto csw_v_zero_int = tnsr::i<DataVector, Dim>(num_pts);\n  auto csw_v_zero_ext = tnsr::i<DataVector, Dim>(num_pts);\n  auto csw_v_plus_int = Scalar<DataVector>(num_pts);\n  auto csw_v_plus_ext = Scalar<DataVector>(num_pts);\n  auto csw_v_minus_int = Scalar<DataVector>(num_pts);\n  auto csw_v_minus_ext = Scalar<DataVector>(num_pts);\n  auto csw_gamma2_int = Scalar<DataVector>(num_pts);\n  auto csw_gamma2_ext = Scalar<DataVector>(num_pts);\n  auto csw_normal_int = tnsr::i<DataVector, Dim>(num_pts);\n  auto csw_normal_ext = tnsr::i<DataVector, Dim>(num_pts);\n  auto csw_char_speeds_int = tnsr::a<DataVector, 3>(num_pts);\n  auto csw_char_speeds_ext = tnsr::a<DataVector, 3>(num_pts);\n  auto csw_psi_bcorr = Scalar<DataVector>(num_pts);\n  auto csw_pi_bcorr = Scalar<DataVector>(num_pts);\n  auto csw_phi_bcorr = tnsr::i<DataVector, Dim>(num_pts);\n\n  // variables for SW\n  auto sw_char_speed_v_psi_int = Scalar<DataVector>(num_pts);\n  auto sw_char_speed_v_psi_ext = Scalar<DataVector>(num_pts);\n  auto sw_char_speed_v_zero_int = tnsr::i<DataVector, Dim>(num_pts);\n  auto sw_char_speed_v_zero_ext = tnsr::i<DataVector, Dim>(num_pts);\n  auto sw_char_speed_v_plus_int = Scalar<DataVector>(num_pts);\n  auto sw_char_speed_v_plus_ext = Scalar<DataVector>(num_pts);\n  auto sw_char_speed_v_minus_int = Scalar<DataVector>(num_pts);\n  auto sw_char_speed_v_minus_ext = Scalar<DataVector>(num_pts);\n  auto sw_char_speed_n_times_v_plus_int = tnsr::i<DataVector, Dim>(num_pts);\n  auto sw_char_speed_n_times_v_plus_ext = tnsr::i<DataVector, Dim>(num_pts);\n  auto sw_char_speed_n_times_v_minus_int = tnsr::i<DataVector, Dim>(num_pts);\n  auto sw_char_speed_n_times_v_minus_ext = tnsr::i<DataVector, Dim>(num_pts);\n  auto sw_char_speed_gamma2_v_psi_int = Scalar<DataVector>(num_pts);\n  auto sw_char_speed_gamma2_v_psi_ext = Scalar<DataVector>(num_pts);\n  auto sw_char_speeds_int = tnsr::i<DataVector, 3>(num_pts);\n  auto sw_char_speeds_ext = tnsr::i<DataVector, 3>(num_pts);\n  auto sw_psi_bcorr = Scalar<DataVector>(num_pts);\n  auto sw_pi_bcorr = Scalar<DataVector>(num_pts);\n  auto sw_phi_bcorr = tnsr::i<DataVector, Dim>(num_pts);\n\n  // For CSW & SW\n  // create psi\n  const auto psi = make_with_random_values<Scalar<DataVector>>(\n      gen, make_not_null(&uniform_m11_dist), x);\n  // create pi\n  const auto pi = make_with_random_values<Scalar<DataVector>>(\n      gen, make_not_null(&uniform_m11_dist), x);\n  // create phi\n  const auto phi = make_with_random_values<tnsr::i<DataVector, Dim>>(\n      gen, make_not_null(&uniform_m11_dist), x);\n  // create gamma2\n  const Scalar<DataVector> gamma2(num_pts, 0.);\n  // create normal covector\n  auto normal_int = make_with_random_values<tnsr::i<DataVector, Dim>>(\n      gen, make_not_null(&uniform_m11_dist), x);\n  tnsr::i<DataVector, Dim> normal_ext{num_pts};\n  {\n    const auto mag_normal = magnitude(normal_int);\n    for (size_t i = 0; i < Dim; ++i) {\n      normal_int.get(i) /= get(mag_normal);\n      normal_ext.get(i) = -normal_int.get(i);\n    }\n  }\n  // create normal vector\n  auto normal_vector_int = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      gen, make_not_null(&uniform_m11_dist), x);\n  tnsr::I<DataVector, Dim> normal_vector_ext{num_pts};\n  {\n    const auto mag_normal = magnitude(normal_vector_int);\n    for (size_t i = 0; i < Dim; ++i) {\n      normal_vector_int.get(i) /= get(mag_normal);\n      normal_vector_ext.get(i) = -normal_vector_int.get(i);\n    }\n  }\n  // create mesh velocity\n  auto mesh_velocity = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      gen, make_not_null(&uniform_m11_dist), x);\n\n  const auto mag_mesh_v = magnitude(mesh_velocity);\n  for (size_t i = 0; i < Dim; ++i) {\n    mesh_velocity.get(i) /= (2. * get(mag_mesh_v));\n  }\n  // create normal dot mesh velocity\n  const auto normal_dot_mesh_velociy_int =\n      dot_product(normal_int, mesh_velocity);\n  const auto normal_dot_mesh_velociy_ext =\n      dot_product(normal_ext, mesh_velocity);\n\n  // For CSW\n  // gamma1\n  const Scalar<DataVector> gamma1(num_pts, 0.);\n  // lapse\n  const auto lapse = get<gr::Tags::Lapse<DataVector>>(\n      minkowski.variables(x, t, tmpl::list<gr::Tags::Lapse<DataVector>>{}));\n  // shift\n  const auto shift = get<gr::Tags::Shift<DataVector, Dim>>(minkowski.variables(\n      x, t, tmpl::list<gr::Tags::Shift<DataVector, Dim>>{}));\n  // inverse spatial metric\n  const auto inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, Dim>>(minkowski.variables(\n          x, t, tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, Dim>>{}));\n\n  // ----- package data : CSW\n  csw_flux_computer.dg_package_data(\n      make_not_null(&csw_v_psi_int), make_not_null(&csw_v_zero_int),\n      make_not_null(&csw_v_plus_int), make_not_null(&csw_v_minus_int),\n      make_not_null(&csw_gamma2_int), make_not_null(&csw_normal_int),\n      make_not_null(&csw_char_speeds_int), psi, pi, phi, lapse, shift,\n      gamma1, gamma2, normal_int, normal_vector_int,\n      mesh_velocity, normal_dot_mesh_velociy_int);\n  csw_flux_computer.dg_package_data(\n      make_not_null(&csw_v_psi_ext), make_not_null(&csw_v_zero_ext),\n      make_not_null(&csw_v_plus_ext), make_not_null(&csw_v_minus_ext),\n      make_not_null(&csw_gamma2_ext), make_not_null(&csw_normal_ext),\n      make_not_null(&csw_char_speeds_ext), psi, pi, phi, lapse, shift,\n      gamma1, gamma2, normal_ext, normal_vector_ext,\n      mesh_velocity, normal_dot_mesh_velociy_ext);\n  csw_flux_computer.dg_boundary_terms(\n      make_not_null(&csw_psi_bcorr), make_not_null(&csw_pi_bcorr),\n      make_not_null(&csw_phi_bcorr), csw_v_psi_int, csw_v_zero_int,\n      csw_v_plus_int, csw_v_minus_int, csw_gamma2_int, csw_normal_int,\n      csw_char_speeds_int, csw_v_psi_ext, csw_v_zero_ext, csw_v_plus_ext,\n      csw_v_minus_ext, csw_gamma2_ext, csw_normal_ext, csw_char_speeds_ext,\n      dg::Formulation::StrongInertial);\n\n  // ----- package data : SW\n  sw_flux_computer.dg_package_data(\n      make_not_null(&sw_char_speed_v_psi_int),\n      make_not_null(&sw_char_speed_v_zero_int),\n      make_not_null(&sw_char_speed_v_plus_int),\n      make_not_null(&sw_char_speed_v_minus_int),\n      make_not_null(&sw_char_speed_n_times_v_plus_int),\n      make_not_null(&sw_char_speed_n_times_v_minus_int),\n      make_not_null(&sw_char_speed_gamma2_v_psi_int),\n      make_not_null(&sw_char_speeds_int), psi, pi, phi, gamma2, normal_int,\n      mesh_velocity, normal_dot_mesh_velociy_int);\n  sw_flux_computer.dg_package_data(\n      make_not_null(&sw_char_speed_v_psi_ext),\n      make_not_null(&sw_char_speed_v_zero_ext),\n      make_not_null(&sw_char_speed_v_plus_ext),\n      make_not_null(&sw_char_speed_v_minus_ext),\n      make_not_null(&sw_char_speed_n_times_v_plus_ext),\n      make_not_null(&sw_char_speed_n_times_v_minus_ext),\n      make_not_null(&sw_char_speed_gamma2_v_psi_ext),\n      make_not_null(&sw_char_speeds_ext), psi, pi, phi, gamma2, normal_ext,\n      mesh_velocity, normal_dot_mesh_velociy_ext);\n  sw_flux_computer.dg_boundary_terms(\n      make_not_null(&sw_psi_bcorr), make_not_null(&sw_pi_bcorr),\n      make_not_null(&sw_phi_bcorr), sw_char_speed_v_psi_int,\n      sw_char_speed_v_zero_int, sw_char_speed_v_plus_int,\n      sw_char_speed_v_minus_int, sw_char_speed_n_times_v_plus_int,\n      sw_char_speed_n_times_v_minus_int, sw_char_speed_gamma2_v_psi_int,\n      sw_char_speeds_int, sw_char_speed_v_psi_ext, sw_char_speed_v_zero_ext,\n      sw_char_speed_v_plus_ext, sw_char_speed_v_minus_ext,\n      sw_char_speed_n_times_v_plus_ext, sw_char_speed_n_times_v_minus_ext,\n      sw_char_speed_gamma2_v_psi_ext, sw_char_speeds_ext,\n      dg::Formulation::StrongInertial);\n\n  CHECK_ITERABLE_APPROX(sw_psi_bcorr, csw_psi_bcorr);\n  CHECK_ITERABLE_APPROX(sw_pi_bcorr, csw_pi_bcorr);\n  CHECK_ITERABLE_APPROX(sw_phi_bcorr, csw_phi_bcorr);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CurvedScalarWave.BoundaryCorrections.UpwindPenalty\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  MAKE_GENERATOR(gen);\n\n  test<1>(make_not_null(&gen), 1);\n  test<2>(make_not_null(&gen), 5);\n  test<3>(make_not_null(&gen), 5);\n\n  test_flat_spacetime<1>(make_not_null(&gen));\n  test_flat_spacetime<2>(make_not_null(&gen));\n  test_flat_spacetime<3>(make_not_null(&gen));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom Evolution.Systems.CurvedScalarWave.Characteristics import (\n    char_field_vminus,\n    char_field_vplus,\n    char_field_vpsi,\n    char_field_vzero,\n    char_speed_vminus,\n    char_speed_vplus,\n    char_speed_vpsi,\n    char_speed_vzero,\n    evol_field_phi,\n    evol_field_pi,\n    evol_field_psi,\n)\n\n\ndef dg_package_data(\n    psi,\n    pi,\n    phi,\n    lapse,\n    shift,\n    constraint_gamma1,\n    constraint_gamma2,\n    interface_unit_normal,\n    interface_unit_normal_vector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n):\n    char_speeds = np.zeros([4])\n    char_speeds[0] = char_speed_vpsi(\n        constraint_gamma1, lapse, shift, interface_unit_normal\n    )\n    char_speeds[1] = char_speed_vzero(\n        constraint_gamma1, lapse, shift, interface_unit_normal\n    )\n    char_speeds[2] = char_speed_vplus(\n        constraint_gamma1, lapse, shift, interface_unit_normal\n    )\n    char_speeds[3] = char_speed_vminus(\n        constraint_gamma1, lapse, shift, interface_unit_normal\n    )\n    if normal_dot_mesh_velocity is not None:\n        char_speeds = char_speeds - normal_dot_mesh_velocity\n\n    return (\n        char_field_vpsi(\n            constraint_gamma2,\n            psi,\n            pi,\n            phi,\n            interface_unit_normal,\n            interface_unit_normal_vector,\n        ),\n        char_field_vzero(\n            constraint_gamma2,\n            psi,\n            pi,\n            phi,\n            interface_unit_normal,\n            interface_unit_normal_vector,\n        ),\n        np.asarray(\n            char_field_vplus(\n                constraint_gamma2,\n                psi,\n                pi,\n                phi,\n                interface_unit_normal,\n                interface_unit_normal_vector,\n            )\n        ),\n        np.asarray(\n            char_field_vminus(\n                constraint_gamma2,\n                psi,\n                pi,\n                phi,\n                interface_unit_normal,\n                interface_unit_normal_vector,\n            )\n        ),\n        np.asarray(constraint_gamma2),\n        np.asarray(interface_unit_normal),\n        char_speeds,\n    )\n\n\ndef _weight_char_fields(\n    v_psi_int,\n    v_zero_int,\n    v_plus_int,\n    v_minus_int,\n    char_speeds_int,\n    v_psi_ext,\n    v_zero_ext,\n    v_plus_ext,\n    v_minus_ext,\n    char_speeds_ext,\n):\n    def weight(char_field, char_speed, sign):\n        weighted_field = char_field\n        weighted_field *= (\n            -sign * char_speed * np.heaviside(sign * char_speed, 0)\n        )\n        return weighted_field\n\n    return (\n        weight(v_psi_int, char_speeds_int[0], -1),\n        weight(v_zero_int, char_speeds_int[1], -1),\n        weight(v_plus_int, char_speeds_int[2], -1),\n        weight(v_minus_int, char_speeds_int[3], -1),\n        weight(v_psi_ext, char_speeds_ext[0], 1),\n        weight(v_zero_ext, char_speeds_ext[1], 1),\n        weight(v_plus_ext, char_speeds_ext[2], 1),\n        weight(v_minus_ext, char_speeds_ext[3], 1),\n    )\n\n\ndef dg_boundary_terms(\n    v_psi_int,\n    v_zero_int,\n    v_plus_int,\n    v_minus_int,\n    gamma2_int,\n    interface_normal_int,\n    char_speeds_int,\n    v_psi_ext,\n    v_zero_ext,\n    v_plus_ext,\n    v_minus_ext,\n    gamma2_ext,\n    interface_normal_ext,\n    char_speeds_ext,\n    use_strong_form,\n):\n    (\n        weighted_v_psi_int,\n        weighted_v_zero_int,\n        weighted_v_plus_int,\n        weighted_v_minus_int,\n        weighted_v_psi_ext,\n        weighted_v_zero_ext,\n        weighted_v_plus_ext,\n        weighted_v_minus_ext,\n    ) = _weight_char_fields(\n        v_psi_int,\n        v_zero_int,\n        v_plus_int,\n        v_minus_int,\n        char_speeds_int,\n        v_psi_ext,\n        v_zero_ext,\n        v_plus_ext,\n        v_minus_ext,\n        char_speeds_ext,\n    )\n    return (\n        np.asarray(\n            evol_field_psi(\n                gamma2_int,\n                weighted_v_psi_ext,\n                weighted_v_zero_ext,\n                weighted_v_plus_ext,\n                weighted_v_minus_ext,\n                -interface_normal_int,\n            )\n            - evol_field_psi(\n                gamma2_int,\n                weighted_v_psi_int,\n                weighted_v_zero_int,\n                weighted_v_plus_int,\n                weighted_v_minus_int,\n                interface_normal_int,\n            )\n        ),\n        np.asarray(\n            evol_field_pi(\n                gamma2_int,\n                weighted_v_psi_ext,\n                weighted_v_zero_ext,\n                weighted_v_plus_ext,\n                weighted_v_minus_ext,\n                -interface_normal_int,\n            )\n            - evol_field_pi(\n                gamma2_int,\n                weighted_v_psi_int,\n                weighted_v_zero_int,\n                weighted_v_plus_int,\n                weighted_v_minus_int,\n                interface_normal_int,\n            )\n        ),\n        np.asarray(\n            evol_field_phi(\n                gamma2_int,\n                weighted_v_psi_ext,\n                weighted_v_zero_ext,\n                weighted_v_plus_ext,\n                weighted_v_minus_ext,\n                -interface_normal_int,\n            )\n            - evol_field_phi(\n                gamma2_int,\n                weighted_v_psi_int,\n                weighted_v_zero_int,\n                weighted_v_plus_int,\n                weighted_v_minus_int,\n                interface_normal_int,\n            )\n        ),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/BoundaryCorrections/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Worldtube)\n\nset(LIBRARY \"Test_CurvedScalarWave\")\n\nset(LIBRARY_SOURCES\n  Actions/Test_SetInitialData.cpp\n  BoundaryCorrections/Test_UpwindPenalty.cpp\n  BoundaryConditions/Test_AnalyticConstant.cpp\n  BoundaryConditions/Test_ConstraintPreservingSphericalRadiation.cpp\n  BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp\n  BoundaryConditions/Test_Worldtube.cpp\n  Test_ApplyTensorYlmFilter.cpp\n  Test_BackgroundSpacetime.cpp\n  Test_CalculateGrVars.cpp\n  Test_Constraints.cpp\n  Test_Characteristics.cpp\n  Test_InitializeConstraintDampingGammas.cpp\n  Test_InitializeEvolvedVariables.cpp\n  Test_PsiSquared.cpp\n  Test_Tags.cpp\n  Test_TimeDerivative.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CurvedScalarWave\n  CurvedWaveEquationAnalyticData\n  CurvedScalarWaveHelpers\n  DataStructures\n  DomainBoundaryConditions\n  DomainBoundaryConditionsHelpers\n  GeneralRelativityHelpers\n  Importers\n  MathFunctions\n  Time\n  Utilities\n  WaveEquationSolutions\n)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Characteristics.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Test functions for characteristic speeds\n\n\ndef char_speed_vpsi(gamma1, lapse, shift, unit_normal):\n    return -(1.0 + gamma1) * np.dot(shift, unit_normal)\n\n\ndef char_speed_vzero(gamma1, lapse, shift, unit_normal):\n    return -np.dot(shift, unit_normal)\n\n\ndef char_speed_vplus(gamma1, lapse, shift, unit_normal):\n    return -np.dot(shift, unit_normal) + lapse\n\n\ndef char_speed_vminus(gamma1, lapse, shift, unit_normal):\n    return -np.dot(shift, unit_normal) - lapse\n\n\n# End test functions for characteristic speeds\n\n# Test functions for characteristic fields\n\n\ndef char_field_vpsi(gamma2, psi, pi, phi, normal_one_form, normal_vector):\n    return psi\n\n\ndef char_field_vzero(gamma2, psi, pi, phi, normal_one_form, normal_vector):\n    projection_tensor = np.identity(len(normal_vector)) - np.einsum(\n        \"i,j\", normal_one_form, normal_vector\n    )\n    return np.einsum(\"ij,j->i\", projection_tensor, phi)\n\n\ndef char_field_vplus(gamma2, psi, pi, phi, normal_one_form, normal_vector):\n    phi_dot_normal = np.dot(normal_vector, phi)\n    return pi + phi_dot_normal - (gamma2 * psi)\n\n\ndef char_field_vminus(gamma2, psi, pi, phi, normal_one_form, normal_vector):\n    phi_dot_normal = np.dot(normal_vector, phi)\n    return pi - phi_dot_normal - (gamma2 * psi)\n\n\ndef evol_field_psi(gamma2, vpsi, vzero, vplus, vminus, normal_one_form):\n    return vpsi\n\n\ndef evol_field_pi(gamma2, vpsi, vzero, vplus, vminus, normal_one_form):\n    return 0.5 * (vplus + vminus) + gamma2 * vpsi\n\n\ndef evol_field_phi(gamma2, vpsi, vzero, vplus, vminus, normal_one_form):\n    udiff = 0.5 * (vplus - vminus)\n    return (normal_one_form * udiff) + vzero\n\n\n# End test functions for characteristic fields\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Constraints.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Test functions for one-index constraint\n\n\ndef one_index_constraint(d_psi, phi):\n    return d_psi - phi\n\n\n# End test functions for one-index constraint\n\n# Test functions for two-index constraint\n\n\ndef two_index_constraint(d_phi):\n    return d_phi - np.transpose(d_phi)\n\n\n# End test functions for two-index constraint\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Fluxes.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Test functions for normal dot fluxes\n\n\ndef psi_normal_dot_flux(\n    psi,\n    pi,\n    phi,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    unit_normal,\n):\n    return -(1.0 + gamma1) * np.dot(shift, unit_normal) * psi\n\n\ndef pi_normal_dot_flux(\n    psi,\n    pi,\n    phi,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    unit_normal,\n):\n    return (\n        -np.dot(shift, unit_normal) * pi\n        + lapse\n        * np.einsum(\"ki,k,iab->ab\", inverse_spatial_metric, unit_normal, phi)\n        - gamma1 * gamma2 * np.dot(shift, unit_normal) * psi\n    )\n\n\ndef phi_dot_flux(\n    psi,\n    pi,\n    phi,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spatial_metric,\n    unit_normal,\n):\n    return (\n        -np.dot(shift, unit_normal) * phi\n        + lapse * np.einsum(\"i,ab->iab\", unit_normal, pi)\n        - gamma2 * lapse * np.einsum(\"i,ab->iab\", unit_normal, psi)\n    )\n\n\n# End test functions for normal dot fluxes\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_ApplyTensorYlmFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/ApplyTensorYlmFilter.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/Test_ApplyTensorYlmFilter.hpp\"\n\nnamespace ylm::TensorYlm {\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CurvedScalarWave.ApplyTensorYlmFilter\",\n    \"[NumericalAlgorithms][Unit]\") {\n  test_apply_filter<filter_detail::sw_vars_list<Frame::Inertial>>(0);\n  test_apply_filter<filter_detail::sw_vars_list<Frame::Inertial>>(5);\n}\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_BackgroundSpacetime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/Systems/CurvedScalarWave/BackgroundSpacetime.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n\nnamespace {\ntemplate <typename BackgroundType>\nvoid test_option_parsing(const BackgroundType& background,\n                         const std::string& option_string) {\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Tags::BackgroundSpacetime<BackgroundType>>(\n      \"BackgroundSpacetime\");\n  CHECK(TestHelpers::test_option_tag<\n            CurvedScalarWave::OptionTags::BackgroundSpacetime<BackgroundType>>(\n            option_string) == background);\n}\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.BackgroundSpacetime\",\n                  \"[Unit][Evolution][Options]\") {\n  const auto minkowski_background_1d = gr::Solutions::Minkowski<1>();\n  test_option_parsing(minkowski_background_1d, \"\");\n\n  const auto minkowski_background_2d = gr::Solutions::Minkowski<2>();\n  test_option_parsing(minkowski_background_2d, \"\");\n\n  const auto minkowski_background_3d = gr::Solutions::Minkowski<3>();\n  test_option_parsing(minkowski_background_3d, \"\");\n\n  const auto ks_background =\n      gr::Solutions::KerrSchild(0.5, {{0.1, 0.2, 0.3}}, {{1.0, 3.0, 2.0}});\n  const std::string& ks_option_string =\n      \"Mass: 0.5\\nSpin: [0.1,0.2,0.3]\\nCenter: [1.0,3.0,2.0]\\nVelocity: \"\n      \"[0.0,0.0,0.0]\";\n  test_option_parsing(ks_background, ks_option_string);\n\n  const auto spherical_ks_background = gr::Solutions::SphericalKerrSchild(\n      0.5, {{0.1, 0.2, 0.3}}, {{1.0, 3.0, 2.0}});\n  const std::string& spherical_ks_option_string =\n      \"Mass: 0.5\\nSpin: [0.1,0.2,0.3]\\nCenter: [1.0,3.0,2.0]\";\n  test_option_parsing(spherical_ks_background, spherical_ks_option_string);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_CalculateGrVars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/NonconservativeSystem.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BackgroundSpacetime.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/CalculateGrVars.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim, typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n\n  using initial_tags =\n      tmpl::list<CurvedScalarWave::Tags::BackgroundSpacetime<\n                     typename Metavariables::background_spacetime>,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>, ::Tags::Time>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<initial_tags>,\n                 CurvedScalarWave::Actions::CalculateGrVars<\n                     typename Metavariables::system, false>>>>;\n};\n\ntemplate <size_t Dim, typename System, typename BackgroundSpacetime>\nstruct Metavariables {\n  using background_spacetime = BackgroundSpacetime;\n  using component_list = tmpl::list<component<Dim, Metavariables>>;\n  using system = System;\n  using const_global_cache_tag_list = tmpl::list<>;\n};\n\ntemplate <typename BackgroundSpacetime>\nvoid test(const BackgroundSpacetime& background_spacetime,\n          const gsl::not_null<std::mt19937*> generator) {\n  static constexpr size_t Dim = BackgroundSpacetime::volume_dim;\n  using system = CurvedScalarWave::System<Dim>;\n  using metavars = Metavariables<Dim, system, BackgroundSpacetime>;\n  using comp = component<Dim, metavars>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{}};\n  const size_t num_points = 42;\n  std::uniform_real_distribution dist{-10., 10.};\n  const auto random_coords = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      generator, make_not_null(&dist), DataVector{num_points});\n  const double time = 0.;\n  const ElementId<Dim> element_id{0};\n  ActionTesting::emplace_component_and_initialize<comp>(\n      &runner, element_id, {background_spacetime, random_coords, time});\n  // invoke CalculateGrVars\n  ActionTesting::next_action<comp>(make_not_null(&runner), element_id);\n  const auto solution_at_coords = background_spacetime.variables(\n      random_coords, time, typename system::spacetime_tag_list{});\n  // check that each tag corresponds to analytic solution now\n  tmpl::for_each<typename system::spacetime_tag_list>(\n      [&runner, &element_id, &solution_at_coords](auto spacetime_tag_v) {\n        using spacetime_tag = tmpl::type_from<decltype(spacetime_tag_v)>;\n        CHECK(ActionTesting::get_databox_tag<comp, spacetime_tag>(runner,\n                                                                  element_id) ==\n              get<spacetime_tag>(solution_at_coords));\n      });\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.CalculateGrVars\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(generator);\n  test(gr::Solutions::Minkowski<1>(), make_not_null(&generator));\n  test(gr::Solutions::Minkowski<2>(), make_not_null(&generator));\n  test(gr::Solutions::Minkowski<3>(), make_not_null(&generator));\n  test(gr::Solutions::KerrSchild(1., {0.5, 0., 0.1}, {0.2, 0.5, -0.7}),\n       make_not_null(&generator));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace {\ntemplate <size_t Index, size_t SpatialDim>\nScalar<DataVector> speed_with_index(\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& normal) {\n  return Scalar<DataVector>{CurvedScalarWave::characteristic_speeds(\n      gamma_1, lapse, shift, normal)[Index]};\n}\n\ntemplate <size_t Index, size_t SpatialDim>\nScalar<DataVector> speed_with_index_from_tnsr(\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& shift,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& normal) {\n  auto char_speeds =\n      tnsr::a<DataVector, 3, Frame::Inertial>(get_size(get(lapse)));\n  CurvedScalarWave::characteristic_speeds(make_not_null(&char_speeds), gamma_1,\n                                          lapse, shift, normal);\n  return Scalar<DataVector>{char_speeds.get(Index)};\n}\n\ntemplate <size_t SpatialDim>\nvoid test_characteristic_speeds() {\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(speed_with_index<0, SpatialDim>,\n                                    \"Characteristics\", \"char_speed_vpsi\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<1, SpatialDim>,\n                                    \"Characteristics\", \"char_speed_vzero\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<3, SpatialDim>,\n                                    \"Characteristics\", \"char_speed_vminus\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<2, SpatialDim>,\n                                    \"Characteristics\", \"char_speed_vplus\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index_from_tnsr<0, SpatialDim>,\n                                    \"Characteristics\", \"char_speed_vpsi\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index_from_tnsr<1, SpatialDim>,\n                                    \"Characteristics\", \"char_speed_vzero\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index_from_tnsr<3, SpatialDim>,\n                                    \"Characteristics\", \"char_speed_vminus\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index_from_tnsr<2, SpatialDim>,\n                                    \"Characteristics\", \"char_speed_vplus\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n}\n}  // namespace\n\nnamespace {\ntemplate <typename Tag, size_t SpatialDim>\ntypename Tag::type field_with_tag_variables(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& normal_one_form,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& normal_vector) {\n  return get<Tag>(CurvedScalarWave::characteristic_fields(\n      gamma_2, psi, pi, phi, normal_one_form, normal_vector));\n}\n\n// uses the overload that returns tensors by reference\ntemplate <typename Tag, size_t SpatialDim>\ntypename Tag::type field_with_tag_tensor(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& normal_one_form,\n    const tnsr::I<DataVector, SpatialDim, Frame::Inertial>& normal_vector) {\n  Scalar<DataVector> v_psi{};\n  tnsr::i<DataVector, SpatialDim, Frame::Inertial> v_zero{};\n  Scalar<DataVector> v_plus{};\n  Scalar<DataVector> v_minus{};\n  CurvedScalarWave::characteristic_fields(\n      make_not_null(&v_psi), make_not_null(&v_zero), make_not_null(&v_plus),\n      make_not_null(&v_minus), gamma_2, psi, pi, phi, normal_one_form,\n      normal_vector);\n  Variables<tmpl::list<\n      CurvedScalarWave::Tags::VPsi, CurvedScalarWave::Tags::VZero<SpatialDim>,\n      CurvedScalarWave::Tags::VPlus, CurvedScalarWave::Tags::VMinus>>\n      vars(get(gamma_2).size());\n  get<CurvedScalarWave::Tags::VPsi>(vars) = v_psi;\n  get<CurvedScalarWave::Tags::VZero<SpatialDim>>(vars) = v_zero;\n  get<CurvedScalarWave::Tags::VPlus>(vars) = v_plus;\n  get<CurvedScalarWave::Tags::VMinus>(vars) = v_minus;\n  return get<Tag>(vars);\n}\n\ntemplate <size_t SpatialDim>\nvoid test_characteristic_fields() {\n  const DataVector used_for_size(5);\n  // VPsi\n  pypp::check_with_random_values<1>(\n      field_with_tag_variables<CurvedScalarWave::Tags::VPsi, SpatialDim>,\n      \"Characteristics\", \"char_field_vpsi\", {{{-2.0, 2.0}}}, used_for_size);\n  // VZero\n  pypp::check_with_random_values<1>(\n      field_with_tag_variables<CurvedScalarWave::Tags::VZero<SpatialDim>,\n                               SpatialDim>,\n      \"Characteristics\", \"char_field_vzero\", {{{-2.0, 2.0}}}, used_for_size);\n  // VPlus\n  pypp::check_with_random_values<1>(\n      field_with_tag_variables<CurvedScalarWave::Tags::VPlus, SpatialDim>,\n      \"Characteristics\", \"char_field_vplus\", {{{-2.0, 2.0}}}, used_for_size);\n  // VMinus\n  pypp::check_with_random_values<1>(\n      field_with_tag_variables<CurvedScalarWave::Tags::VMinus, SpatialDim>,\n      \"Characteristics\", \"char_field_vminus\", {{{-2.0, 2.0}}}, used_for_size);\n\n// VPsi\n  pypp::check_with_random_values<1>(\n      field_with_tag_tensor<CurvedScalarWave::Tags::VPsi, SpatialDim>,\n      \"Characteristics\", \"char_field_vpsi\", {{{-2.0, 2.0}}}, used_for_size);\n  // VZero\n  pypp::check_with_random_values<1>(\n      field_with_tag_tensor<CurvedScalarWave::Tags::VZero<SpatialDim>,\n                            SpatialDim>,\n      \"Characteristics\", \"char_field_vzero\", {{{-2.0, 2.0}}}, used_for_size);\n  // VPlus\n  pypp::check_with_random_values<1>(\n      field_with_tag_tensor<CurvedScalarWave::Tags::VPlus, SpatialDim>,\n      \"Characteristics\", \"char_field_vplus\", {{{-2.0, 2.0}}}, used_for_size);\n  // VMinus\n  pypp::check_with_random_values<1>(\n      field_with_tag_tensor<CurvedScalarWave::Tags::VMinus, SpatialDim>,\n      \"Characteristics\", \"char_field_vminus\", {{{-2.0, 2.0}}}, used_for_size);\n}\n}  // namespace\n\nnamespace {\n// uses the overload that returns variables\ntemplate <typename Tag, size_t SpatialDim>\ntypename Tag::type evolved_field_with_tag_variables(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& u_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& u_zero,\n    const Scalar<DataVector>& u_plus, const Scalar<DataVector>& u_minus,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& normal_one_form) {\n  return get<Tag>(CurvedScalarWave::evolved_fields_from_characteristic_fields(\n      gamma_2, u_psi, u_zero, u_plus, u_minus, normal_one_form));\n}\n\n// uses the overload that returns tensors by reference\ntemplate <typename Tag, size_t SpatialDim>\ntypename Tag::type evolved_field_with_tag_tensor(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& u_psi,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& u_zero,\n    const Scalar<DataVector>& u_plus, const Scalar<DataVector>& u_minus,\n    const tnsr::i<DataVector, SpatialDim, Frame::Inertial>& normal_one_form) {\n  Scalar<DataVector> psi{};\n  Scalar<DataVector> pi{};\n  tnsr::i<DataVector, SpatialDim, Frame::Inertial> phi{};\n  CurvedScalarWave::evolved_fields_from_characteristic_fields(\n      make_not_null(&psi), make_not_null(&pi), make_not_null(&phi), gamma_2,\n      u_psi, u_zero, u_plus, u_minus, normal_one_form);\n  Variables<tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                       CurvedScalarWave::Tags::Phi<SpatialDim>>>\n      vars(get(gamma_2).size());\n  get<CurvedScalarWave::Tags::Psi>(vars) = psi;\n  get<CurvedScalarWave::Tags::Pi>(vars) = pi;\n  get<CurvedScalarWave::Tags::Phi<SpatialDim>>(vars) = phi;\n  return get<Tag>(vars);\n}\n\ntemplate <size_t SpatialDim>\nvoid test_evolved_from_characteristic_fields() {\n  const DataVector used_for_size(5);\n  // Psi\n  pypp::check_with_random_values<1>(\n      evolved_field_with_tag_variables<CurvedScalarWave::Tags::Psi, SpatialDim>,\n      \"Characteristics\", \"evol_field_psi\", {{{-2.0, 2.0}}}, used_for_size);\n  // Pi\n  pypp::check_with_random_values<1>(\n      evolved_field_with_tag_variables<CurvedScalarWave::Tags::Pi, SpatialDim>,\n      \"Characteristics\", \"evol_field_pi\", {{{-2.0, 2.0}}}, used_for_size);\n  // Phi\n  pypp::check_with_random_values<1>(\n      evolved_field_with_tag_variables<CurvedScalarWave::Tags::Phi<SpatialDim>,\n                                       SpatialDim>,\n      \"Characteristics\", \"evol_field_phi\", {{{-2.0, 2.0}}}, used_for_size);\n\n  // Psi\n  pypp::check_with_random_values<1>(\n      evolved_field_with_tag_tensor<CurvedScalarWave::Tags::Psi, SpatialDim>,\n      \"Characteristics\", \"evol_field_psi\", {{{-2.0, 2.0}}}, used_for_size);\n  // Pi\n  pypp::check_with_random_values<1>(\n      evolved_field_with_tag_tensor<CurvedScalarWave::Tags::Pi, SpatialDim>,\n      \"Characteristics\", \"evol_field_pi\", {{{-2.0, 2.0}}}, used_for_size);\n  // Phi\n  pypp::check_with_random_values<1>(\n      evolved_field_with_tag_tensor<CurvedScalarWave::Tags::Phi<SpatialDim>,\n                                    SpatialDim>,\n      \"Characteristics\", \"evol_field_phi\", {{{-2.0, 2.0}}}, used_for_size);\n}\n\ntemplate <size_t SpatialDim>\nvoid test_characteristics_compute_tags() {\n  TestHelpers::db::test_compute_tag<\n      CurvedScalarWave::CharacteristicSpeedsCompute<SpatialDim>>(\n      \"CharacteristicSpeeds\");\n  TestHelpers::db::test_compute_tag<\n      CurvedScalarWave::CharacteristicFieldsCompute<SpatialDim>>(\n      \"CharacteristicFields\");\n  TestHelpers::db::test_compute_tag<\n      CurvedScalarWave::EvolvedFieldsFromCharacteristicFieldsCompute<\n          SpatialDim>>(\"EvolvedFieldsFromCharacteristicFields\");\n\n  const DataVector used_for_size(5);\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.0, 1.0);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  // Randomized tensors\n  const auto gamma_1 = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto gamma_2 = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto lapse = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto shift =\n      make_with_random_values<tnsr::I<DataVector, SpatialDim, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto inverse_spatial_metric = make_with_random_values<\n      tnsr::II<DataVector, SpatialDim, Frame::Inertial>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto psi = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto pi = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto phi =\n      make_with_random_values<tnsr::i<DataVector, SpatialDim, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto normal_one_form =\n      make_with_random_values<tnsr::i<DataVector, SpatialDim, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto normal_vector = tenex::evaluate<ti::I>(\n      inverse_spatial_metric(ti::I, ti::J) * normal_one_form(ti::j));\n\n  // Insert into databox\n  const auto box = db::create<\n      db::AddSimpleTags<\n          CurvedScalarWave::Tags::ConstraintGamma1,\n          CurvedScalarWave::Tags::ConstraintGamma2, gr::Tags::Lapse<DataVector>,\n          gr::Tags::Shift<DataVector, SpatialDim>,\n          gr::Tags::InverseSpatialMetric<DataVector, SpatialDim>,\n          CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n          CurvedScalarWave::Tags::Phi<SpatialDim>,\n          ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<SpatialDim>>>,\n      db::AddComputeTags<\n          CurvedScalarWave::CharacteristicSpeedsCompute<SpatialDim>,\n          CurvedScalarWave::CharacteristicFieldsCompute<SpatialDim>>>(\n      gamma_1, gamma_2, lapse, shift, inverse_spatial_metric, psi, pi, phi,\n      normal_one_form);\n  // Test compute tag for char speeds\n  CHECK(\n      db::get<CurvedScalarWave::Tags::CharacteristicSpeeds<SpatialDim>>(box) ==\n      CurvedScalarWave::characteristic_speeds(gamma_1, lapse, shift,\n                                              normal_one_form));\n  // Test compute tag for char fields\n  CHECK(\n      db::get<CurvedScalarWave::Tags::CharacteristicFields<SpatialDim>>(box) ==\n      CurvedScalarWave::characteristic_fields(gamma_2, psi, pi, phi,\n                                              normal_one_form, normal_vector));\n\n  // more randomized tensors\n  const auto u_psi = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto u_zero =\n      make_with_random_values<tnsr::i<DataVector, SpatialDim, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto u_plus = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto u_minus = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  // Insert into databox\n  const auto box2 = db::create<\n      db::AddSimpleTags<\n          CurvedScalarWave::Tags::ConstraintGamma2,\n          CurvedScalarWave::Tags::VPsi,\n          CurvedScalarWave::Tags::VZero<SpatialDim>,\n          CurvedScalarWave::Tags::VPlus, CurvedScalarWave::Tags::VMinus,\n          ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<SpatialDim>>>,\n      db::AddComputeTags<\n          CurvedScalarWave::EvolvedFieldsFromCharacteristicFieldsCompute<\n              SpatialDim>>>(gamma_2, u_psi, u_zero, u_plus, u_minus,\n                            normal_one_form);\n  // Test compute tag for evolved fields computed from char fields\n  CHECK(db::get<CurvedScalarWave::Tags::EvolvedFieldsFromCharacteristicFields<\n            SpatialDim>>(box2) ==\n        CurvedScalarWave::evolved_fields_from_characteristic_fields(\n            gamma_2, u_psi, u_zero, u_plus, u_minus, normal_one_form));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.Characteristics\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/CurvedScalarWave/\"};\n\n  test_characteristic_speeds<1>();\n  test_characteristic_speeds<2>();\n  test_characteristic_speeds<3>();\n\n  test_characteristic_fields<1>();\n  test_characteristic_fields<2>();\n  test_characteristic_fields<3>();\n\n  test_evolved_from_characteristic_fields<1>();\n  test_evolved_from_characteristic_fields<2>();\n  test_evolved_from_characteristic_fields<3>();\n\n  test_characteristics_compute_tags<1>();\n  test_characteristics_compute_tags<2>();\n  test_characteristics_compute_tags<3>();\n}\n\nnamespace {\ntemplate <size_t SpatialDim>\nvoid check_max_char_speed(const DataVector& used_for_size) {\n  CAPTURE(SpatialDim);\n  MAKE_GENERATOR(gen);\n\n  // For a thorough numerical test of the function that claims to be\n  // computing the maximum characteristic speed at a given point, we\n  // calculate the char speeds along a reasonably large number of random\n  // unit directions and check that the claimed maximum is higher\n  // than the maximum speed along each of those directions.\n  //\n  // A reasonable number of trials can be defined by asking that at\n  // least one of the randomly generated normal furnishes a max\n  // char speed (along that normal) that is within some fraction of the\n  // claimed globally max char speed. Below, that fraction is called\n  // `check_minimum`. Assuming a small but finite chance that this\n  // condition on the number of trials is not satisfied\n  // ('failure_tolerance`), this gives the number of trials to be\n  // log(failure_tolerance) / log(check_minimum) + 1.\n  //\n  // In each trial below we check that the max char speed along that trial's\n  // random unit normal remains smaller than the claimed maximum (within\n  // some numerical tolerance of course, given below by `check_maximum`).\n  // Having this tolerance is necessary in 1D as we only have two directions\n  // possible for the unit normal.\n  //\n  // At the end, we also check that indeed the largest of the char\n  // speeds coming from all trials is larger than the fraction\n  // `check_minimum` of the claimed absolute max char speed, as we intended\n  // in the first place.\n\n  // Fraction of times the test can randomly fail\n  const double failure_tolerance = 1e-10;\n  // Minimum fraction of the claimed result that must be found in some\n  // random trial\n  const double check_minimum = 0.9;\n  // Minimum fraction of the claimed result that can be found in any\n  // random trial (generally only important for 1D where the random\n  // vector will point along the maximum speed direction)\n  const double check_maximum = 1.0 + 1e-12;\n\n  const double trial_failure_rate =\n      SpatialDim == 1\n          ? 0.1  // correct value is 0.0, but cannot take log of that\n          : (SpatialDim == 2 ? 1.0 - 2.0 * acos(check_minimum) / M_PI\n                             : check_minimum);\n\n  const size_t num_trials =\n      static_cast<size_t>(log(failure_tolerance) / log(trial_failure_rate)) + 1;\n\n  const auto lapse = TestHelpers::gr::random_lapse(&gen, used_for_size);\n  const auto shift =\n      TestHelpers::gr::random_shift<SpatialDim>(&gen, used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<SpatialDim>(&gen, used_for_size);\n  std::uniform_real_distribution<> gamma_1_dist(0.0, 5.0);\n  const auto gamma_1 = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&gen), make_not_null(&gamma_1_dist), used_for_size);\n  double max_char_speed = std::numeric_limits<double>::signaling_NaN();\n  CurvedScalarWave::Tags::ComputeLargestCharacteristicSpeed<\n      SpatialDim>::function(make_not_null(&max_char_speed), gamma_1, lapse,\n                            shift, spatial_metric);\n\n  double maximum_observed = -std::numeric_limits<double>::infinity();\n  for (size_t i = 0; i < num_trials; ++i) {\n    const auto unit_one_form = raise_or_lower_index(\n        random_unit_normal(&gen, spatial_metric), spatial_metric);\n\n    const auto characteristic_speeds = CurvedScalarWave::characteristic_speeds(\n        gamma_1, lapse, shift, unit_one_form);\n\n    double max_speed_in_chosen_direction =\n        -std::numeric_limits<double>::infinity();\n    for (const auto& speed : characteristic_speeds) {\n      max_speed_in_chosen_direction =\n          std::max(max_speed_in_chosen_direction, max(abs(speed)));\n    }\n\n    CHECK(max_speed_in_chosen_direction <= max_char_speed * check_maximum);\n    maximum_observed =\n        std::max(maximum_observed, max_speed_in_chosen_direction);\n  }\n  CHECK(maximum_observed >= check_minimum * max_char_speed);\n}\n\ntemplate <size_t SpatialDim>\nvoid check_max_char_speed_compute_tag(const DataVector& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.0, 1.0);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  // Randomized tensors\n  const auto gamma_1 = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto lapse = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto shift =\n      make_with_random_values<tnsr::I<DataVector, SpatialDim, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto spatial_metric = make_with_random_values<\n      tnsr::ii<DataVector, SpatialDim, Frame::Inertial>>(\n      nn_generator, nn_distribution, used_for_size);\n\n  // Insert into databox\n  const auto box = db::create<\n      db::AddSimpleTags<CurvedScalarWave::Tags::ConstraintGamma1,\n                        gr::Tags::Lapse<DataVector>,\n                        gr::Tags::Shift<DataVector, SpatialDim>,\n                        gr::Tags::SpatialMetric<DataVector, SpatialDim>>,\n      db::AddComputeTags<CurvedScalarWave::Tags::\n                             ComputeLargestCharacteristicSpeed<SpatialDim>>>(\n      gamma_1, lapse, shift, spatial_metric);\n  // Test compute tag for computing max char speed\n  double max_char_speed = std::numeric_limits<double>::signaling_NaN();\n  CurvedScalarWave::Tags::ComputeLargestCharacteristicSpeed<\n      SpatialDim>::function(make_not_null(&max_char_speed), gamma_1, lapse,\n                            shift, spatial_metric);\n  CHECK(db::get<CurvedScalarWave::Tags::LargestCharacteristicSpeed>(box) ==\n        max_char_speed);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.MaxCharSpeed\",\n                  \"[Unit][Evolution]\") {\n  check_max_char_speed<1>(DataVector(5));\n  check_max_char_speed<2>(DataVector(5));\n  check_max_char_speed<3>(DataVector(5));\n\n  check_max_char_speed_compute_tag<1>(DataVector(5));\n  check_max_char_speed_compute_tag<2>(DataVector(5));\n  check_max_char_speed_compute_tag<3>(DataVector(5));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Constraints.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Test the return-by-value one-index constraint function using random values\ntemplate <size_t SpatialDim>\nvoid test_one_index_constraint_random(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::i<DataVector, SpatialDim, Frame::Inertial> (*)(\n          const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&,\n          const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&)>(\n          &CurvedScalarWave::one_index_constraint<SpatialDim>),\n      \"Constraints\", \"one_index_constraint\", {{{-10.0, 10.0}}}, used_for_size);\n}\n\n// Test the return-by-value two-index constraint function using random values\ntemplate <size_t SpatialDim>\nvoid test_two_index_constraint_random(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ij<DataVector, SpatialDim, Frame::Inertial> (*)(\n          const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>&)>(\n          &CurvedScalarWave::two_index_constraint<SpatialDim>),\n      \"Constraints\", \"two_index_constraint\", {{{-10.0, 10.0}}}, used_for_size,\n      1.0e-12);\n}\n\n// Test compute items for various constraints via insertion and retrieval\n// in a databox\ntemplate <size_t SpatialDim>\nvoid test_constraint_compute_items(const DataVector& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.0, 1.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  // Randomized tensors\n  const auto phi = make_with_random_values<tnsr::i<DataVector, SpatialDim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto d_phi = make_with_random_values<tnsr::ij<DataVector, SpatialDim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto d_psi = make_with_random_values<tnsr::i<DataVector, SpatialDim>>(\n      nn_generator, nn_distribution, used_for_size);\n  // Insert into databox\n  const auto box = db::create<\n      db::AddSimpleTags<\n          CurvedScalarWave::Tags::Phi<SpatialDim>,\n          ::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<SpatialDim>,\n                        Frame::Inertial>,\n          ::Tags::deriv<CurvedScalarWave::Tags::Phi<SpatialDim>,\n                        tmpl::size_t<SpatialDim>, Frame::Inertial>>,\n      db::AddComputeTags<\n          CurvedScalarWave::Tags::OneIndexConstraintCompute<SpatialDim>,\n          CurvedScalarWave::Tags::TwoIndexConstraintCompute<SpatialDim>>>(\n      phi, d_psi, d_phi);\n\n  // Check compute tag against locally computed quantities\n  CHECK(db::get<CurvedScalarWave::Tags::OneIndexConstraint<SpatialDim>>(box) ==\n        CurvedScalarWave::one_index_constraint(d_psi, phi));\n  CHECK(db::get<CurvedScalarWave::Tags::TwoIndexConstraint<SpatialDim>>(box) ==\n        CurvedScalarWave::two_index_constraint(d_phi));\n  TestHelpers::db::test_compute_tag<\n      CurvedScalarWave::Tags::OneIndexConstraintCompute<SpatialDim>>(\n      \"OneIndexConstraint\");\n  TestHelpers::db::test_compute_tag<\n      CurvedScalarWave::Tags::TwoIndexConstraintCompute<SpatialDim>>(\n      \"TwoIndexConstraint\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.Constraints\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/CurvedScalarWave/\"};\n  const auto used_for_size =\n      DataVector(5, std::numeric_limits<double>::signaling_NaN());\n  // Test the one-index constraint with random numbers\n  test_one_index_constraint_random<1>(used_for_size);\n  test_one_index_constraint_random<2>(used_for_size);\n  test_one_index_constraint_random<3>(used_for_size);\n\n  // Test the two-index constraint with random numbers\n  test_two_index_constraint_random<1>(used_for_size);\n  test_two_index_constraint_random<2>(used_for_size);\n  test_two_index_constraint_random<3>(used_for_size);\n\n  // Compute items\n  test_constraint_compute_items<1>(used_for_size);\n  test_constraint_compute_items<2>(used_for_size);\n  test_constraint_compute_items<3>(used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_InitializeConstraintDampingGammas.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <limits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Initialize.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_initialize_constraint_damping_gammas() {\n  auto box =\n      db::create<db::AddSimpleTags<CurvedScalarWave::Tags::ConstraintGamma1,\n                                   CurvedScalarWave::Tags::ConstraintGamma2,\n                                   domain::Tags::Mesh<Dim>>>(\n          Scalar<DataVector>{}, Scalar<DataVector>{},\n          Mesh<Dim>{4, Spectral::Basis::Chebyshev,\n                    Spectral::Quadrature::Gauss});\n  db::mutate_apply<\n      CurvedScalarWave::Initialization::InitializeConstraintDampingGammas<Dim>>(\n      make_not_null(&box));\n  CHECK(get<CurvedScalarWave::Tags::ConstraintGamma1>(box) ==\n        Scalar<DataVector>{pow(4, Dim), 0.});\n  CHECK(get<CurvedScalarWave::Tags::ConstraintGamma2>(box) ==\n        Scalar<DataVector>{pow(4, Dim), 1.});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CurvedScalarWave.InitializeConstraintGammas\",\n    \"[Unit][Evolution]\") {\n  test_initialize_constraint_damping_gammas<1>();\n  test_initialize_constraint_damping_gammas<2>();\n  test_initialize_constraint_damping_gammas<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_InitializeEvolvedVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <limits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Initialize.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Tags/Time.hpp\"\n\nnamespace {\ntemplate <typename BackgroundSpacetime, typename InitialData>\nvoid test_initialize_evolved_variables(\n    const gsl::not_null<std::mt19937*> generator,\n    const BackgroundSpacetime& background_spacetime,\n    const InitialData& initial_data) {\n  static_assert(BackgroundSpacetime::volume_dim == InitialData::volume_dim,\n                \"The dimensions of the background spacetime and the initial \"\n                \"data do not match.\");\n  static constexpr size_t Dim = BackgroundSpacetime::volume_dim;\n  using evolved_var_tag = typename CurvedScalarWave::System<Dim>::variables_tag;\n  const size_t grid_size = 100;\n  std::uniform_real_distribution dist{-10., 10.};\n  const auto random_coords = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      generator, make_not_null(&dist), DataVector{grid_size});\n  const double initial_time = 0.;\n\n  const auto lapse_and_shift = background_spacetime.variables(\n      random_coords, initial_time,\n      tmpl::list<gr::Tags::Lapse<DataVector>,\n                 gr::Tags::Shift<DataVector, Dim>>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(lapse_and_shift);\n  const auto& shift = get<gr::Tags::Shift<DataVector, Dim>>(lapse_and_shift);\n\n  using DerivedClasses = tmpl::list<InitialData>;\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Tags::Time, domain::Tags::Coordinates<Dim, Frame::Inertial>,\n      evolution::initial_data::Tags::InitialData, gr::Tags::Lapse<DataVector>,\n      gr::Tags::Shift<DataVector, Dim>, evolved_var_tag>>(\n      initial_time, random_coords, initial_data.get_clone(), lapse, shift,\n      typename evolved_var_tag::type{grid_size});\n\n  db::mutate_apply<CurvedScalarWave::Initialization::InitializeEvolvedVariables<\n      Dim, DerivedClasses>>(make_not_null(&box));\n\n  if constexpr (tmpl::list_contains_v<typename InitialData::tags,\n                                      CurvedScalarWave::Tags::Psi>) {\n    // it is a curved solution, no adjustment\n    const auto unadjusted_initial_data =\n        variables_from_tagged_tuple(evolution::Initialization::initial_data(\n            initial_data, random_coords, initial_time,\n            typename evolved_var_tag::tags_list{}));\n    CHECK_VARIABLES_APPROX(db::get<evolved_var_tag>(box),\n                           unadjusted_initial_data);\n  } else {\n    // it is a flat solution, need to adjust\n    const auto unadjusted_initial_data =\n        evolution::Initialization::initial_data(\n            initial_data, random_coords, initial_time,\n            typename ScalarWave::System<Dim>::variables_tag::tags_list{});\n    const auto& unadjusted_psi =\n        get<ScalarWave::Tags::Psi>(unadjusted_initial_data);\n    const auto& unadjusted_pi =\n        get<ScalarWave::Tags::Pi>(unadjusted_initial_data);\n    const auto& unadjusted_phi =\n        get<ScalarWave::Tags::Phi<Dim>>(unadjusted_initial_data);\n    Scalar<DataVector> adjusted_pi(grid_size);\n    const auto shift_dot_dpsi = dot_product(shift, unadjusted_phi);\n    get(adjusted_pi) = (get(shift_dot_dpsi) + get(unadjusted_pi)) / get(lapse);\n\n    CHECK_ITERABLE_APPROX(unadjusted_psi,\n                          db::get<CurvedScalarWave::Tags::Psi>(box));\n    CHECK_ITERABLE_APPROX(unadjusted_phi,\n                          db::get<CurvedScalarWave::Tags::Phi<Dim>>(box));\n    CHECK_ITERABLE_APPROX(adjusted_pi,\n                          db::get<CurvedScalarWave::Tags::Pi>(box));\n    if constexpr (std::is_same_v<BackgroundSpacetime,\n                                 gr::Solutions::Minkowski<Dim>>) {\n      CHECK_ITERABLE_APPROX(adjusted_pi, unadjusted_pi);\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CurvedScalarWave.InitializeEvolvedVariables\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(generator);\n\n  const ScalarWave::Solutions::PlaneWave<1> plane_wave_1d{\n      {{1.0}},\n      {{0.0}},\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(11., 1.4,\n                                                                    0.3)};\n  const ScalarWave::Solutions::PlaneWave<2> plane_wave_2d{\n      {{1.0, 1.0}},\n      {{0.0, 0.0}},\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(11., 1.4,\n                                                                    0.3)};\n  const ScalarWave::Solutions::PlaneWave<3> plane_wave_3d{\n      {{1.0, 1.0, 1.0}},\n      {{0.0, 0.0, 0.0}},\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(11., 1.4,\n                                                                    0.3)};\n  const ScalarWave::Solutions::RegularSphericalWave regular_spherical_wave{\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(11., 1.4,\n                                                                    0.3)};\n  const CurvedScalarWave::AnalyticData::PureSphericalHarmonic\n      pure_spherical_harmonic{10., 1., {4, -3}};\n\n  const gr::Solutions::Minkowski<1> minkowski_1d{};\n  const gr::Solutions::Minkowski<2> minkowski_2d{};\n  const gr::Solutions::Minkowski<3> minkowski_3d{};\n  const gr::Solutions::KerrSchild kerr_schild(0.7, {{0.1, -0.2, 0.3}},\n                                              {{0.7, 0.6, -2.}});\n  const gr::Solutions::SphericalKerrSchild spherical_kerr_schild(\n      0.7, {{0.1, -0.2, 0.3}}, {{0.7, 0.6, -2.}});\n\n  test_initialize_evolved_variables(make_not_null(&generator), minkowski_1d,\n                                    plane_wave_1d);\n  test_initialize_evolved_variables(make_not_null(&generator), minkowski_2d,\n                                    plane_wave_2d);\n  test_initialize_evolved_variables(make_not_null(&generator), minkowski_3d,\n                                    plane_wave_3d);\n  test_initialize_evolved_variables(make_not_null(&generator), minkowski_3d,\n                                    regular_spherical_wave);\n  test_initialize_evolved_variables(make_not_null(&generator), minkowski_3d,\n                                    pure_spherical_harmonic);\n\n  test_initialize_evolved_variables(make_not_null(&generator), kerr_schild,\n                                    plane_wave_3d);\n  test_initialize_evolved_variables(make_not_null(&generator), kerr_schild,\n                                    regular_spherical_wave);\n  test_initialize_evolved_variables(make_not_null(&generator), kerr_schild,\n                                    pure_spherical_harmonic);\n\n  test_initialize_evolved_variables(make_not_null(&generator),\n                                    spherical_kerr_schild, plane_wave_3d);\n  test_initialize_evolved_variables(\n      make_not_null(&generator), spherical_kerr_schild, regular_spherical_wave);\n  test_initialize_evolved_variables(make_not_null(&generator),\n                                    spherical_kerr_schild,\n                                    pure_spherical_harmonic);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_PsiSquared.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/CurvedScalarWave/PsiSquared.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.PsiSquared\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::PsiSquared>(\n      \"PsiSquared\");\n  TestHelpers::db::test_compute_tag<CurvedScalarWave::Tags::PsiSquaredCompute>(\n      \"PsiSquared\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.Tags\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::Psi>(\"Psi\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::Pi>(\"Pi\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::Phi<3>>(\"Phi\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::ConstraintGamma1>(\n      \"ConstraintGamma1\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::ConstraintGamma2>(\n      \"ConstraintGamma2\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Tags::OneIndexConstraint<3>>(\"OneIndexConstraint\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Tags::TwoIndexConstraint<3>>(\"TwoIndexConstraint\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::VPsi>(\"VPsi\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::VZero<3>>(\"VZero\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::VPlus>(\"VPlus\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Tags::VMinus>(\"VMinus\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Tags::CharacteristicSpeeds<3>>(\"CharacteristicSpeeds\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Tags::CharacteristicFields<3>>(\"CharacteristicFields\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Tags::EvolvedFieldsFromCharacteristicFields<3>>(\n      \"EvolvedFieldsFromCharacteristicFields\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TimeDerivative.hpp\"\n#include \"Helpers/Evolution/Systems/CurvedScalarWave/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nScalar<DataVector> make_lapse(const DataVector& used_for_size) {\n  return Scalar<DataVector>{make_with_value<DataVector>(used_for_size, 3.)};\n}\n\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim> make_shift(const DataVector& used_for_size) {\n  auto shift = make_with_value<tnsr::I<DataVector, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    shift.get(i) = make_with_value<DataVector>(used_for_size, i + 1.);\n  }\n  return shift;\n}\n\ntemplate <size_t Dim>\ntnsr::II<DataVector, Dim> make_inverse_spatial_metric(\n    const DataVector& used_for_size) {\n  auto metric = make_with_value<tnsr::II<DataVector, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = i; j < Dim; ++j) {\n      metric.get(i, j) =\n          make_with_value<DataVector>(used_for_size, (i + 1.) * (j + 1.));\n    }\n  }\n  return metric;\n}\n\ntemplate <size_t Dim>\ntnsr::i<DataVector, Dim> make_deriv_lapse(const DataVector& used_for_size) {\n  auto deriv_lapse =\n      make_with_value<tnsr::i<DataVector, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    deriv_lapse.get(i) =\n        make_with_value<DataVector>(used_for_size, 2.5 * (i + 1.));\n  }\n  return deriv_lapse;\n}\n\ntemplate <size_t Dim>\ntnsr::iJ<DataVector, Dim> make_deriv_shift(const DataVector& used_for_size) {\n  auto deriv_shift =\n      make_with_value<tnsr::iJ<DataVector, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      deriv_shift.get(i, j) =\n          make_with_value<DataVector>(used_for_size, 3. * (j + 1.) - i + 4.);\n    }\n  }\n  return deriv_shift;\n}\n\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim> make_trace_spatial_christoffel_second_kind(\n    const DataVector& used_for_size) {\n  auto trace_christoffel =\n      make_with_value<tnsr::I<DataVector, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    trace_christoffel.get(i) =\n        make_with_value<DataVector>(used_for_size, 3. * i - 2.5);\n  }\n  return trace_christoffel;\n}\n\nScalar<DataVector> make_trace_extrinsic_curvature(\n    const DataVector& used_for_size) {\n  return Scalar<DataVector>{make_with_value<DataVector>(used_for_size, 5.)};\n}\n\ntemplate <size_t Dim>\nVariables<tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                     CurvedScalarWave::Tags::Phi<Dim>>>\ncalculate_du_dt(const DataVector& used_for_size) {\n  auto dt_psi = make_with_value<Scalar<DataVector>>(used_for_size, 0.);\n  auto dt_pi = make_with_value<Scalar<DataVector>>(used_for_size, 0.);\n  auto dt_phi = make_with_value<tnsr::i<DataVector, Dim, Frame::Inertial>>(\n      used_for_size, 0.);\n  auto r_lapse = make_with_value<Scalar<DataVector>>(used_for_size, 0.);\n  auto r_shift = make_with_value<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n      used_for_size, 0.);\n  auto r_inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, Dim, Frame::Inertial>>(used_for_size,\n                                                                  0.);\n  auto r_gamma1 = make_with_value<Scalar<DataVector>>(used_for_size, 0.);\n  auto r_gamma2 = make_with_value<Scalar<DataVector>>(used_for_size, 0.);\n  CurvedScalarWave::TimeDerivative<Dim>::apply(\n      make_not_null(&dt_psi), make_not_null(&dt_pi), make_not_null(&dt_phi),\n      make_not_null(&r_lapse), make_not_null(&r_shift),\n      make_not_null(&r_inv_spatial_metric), make_not_null(&r_gamma1),\n      make_not_null(&r_gamma2), make_d_psi<Dim>(used_for_size),\n      make_d_pi<Dim>(used_for_size), make_d_phi<Dim>(used_for_size),\n      make_pi(used_for_size), make_phi<Dim>(used_for_size),\n      make_lapse(used_for_size), make_shift<Dim>(used_for_size),\n      make_deriv_lapse<Dim>(used_for_size),\n      make_deriv_shift<Dim>(used_for_size),\n      make_inverse_spatial_metric<Dim>(used_for_size),\n      make_trace_spatial_christoffel_second_kind<Dim>(used_for_size),\n      make_trace_extrinsic_curvature(used_for_size),\n      make_constraint_gamma1(used_for_size),\n      make_constraint_gamma2(used_for_size));\n  Variables<tmpl::list<CurvedScalarWave::Tags::Psi, CurvedScalarWave::Tags::Pi,\n                       CurvedScalarWave::Tags::Phi<Dim>>>\n      vars(used_for_size.size(), 0.);\n\n  get<CurvedScalarWave::Tags::Psi>(vars) = dt_psi;\n  get<CurvedScalarWave::Tags::Pi>(vars) = dt_pi;\n  get<CurvedScalarWave::Tags::Phi<Dim>>(vars) = dt_phi;\n  return vars;\n}\ntemplate <size_t Dim>\nvoid check_du_dt(const DataVector& used_for_size);\n\ntemplate <>\nvoid check_du_dt<1>(const DataVector& used_for_size) {\n  const auto vars = calculate_du_dt<1>(used_for_size);\n  const auto& dt_psi = get<CurvedScalarWave::Tags::Psi>(vars);\n  const auto& dt_pi = get<CurvedScalarWave::Tags::Pi>(vars);\n  const auto& dt_phi = get<CurvedScalarWave::Tags::Phi<1>>(vars);\n  CHECK_ITERABLE_APPROX(dt_psi.get(), (DataVector{used_for_size.size(), 5.3}));\n  CHECK_ITERABLE_APPROX(dt_pi.get(), (DataVector{used_for_size.size(), -22.5}));\n  CHECK_ITERABLE_APPROX(dt_phi.get(0), (DataVector{used_for_size.size(), 4.}));\n}\ntemplate <>\nvoid check_du_dt<2>(const DataVector& used_for_size) {\n  const auto vars = calculate_du_dt<2>(used_for_size);\n  const auto& dt_psi = get<CurvedScalarWave::Tags::Psi>(vars);\n  const auto& dt_pi = get<CurvedScalarWave::Tags::Pi>(vars);\n  const auto& dt_phi = get<CurvedScalarWave::Tags::Phi<2>>(vars);\n  CHECK_ITERABLE_APPROX(dt_psi.get(), (DataVector{used_for_size.size(), 68.3}));\n  CHECK_ITERABLE_APPROX(dt_pi.get(),\n                        (DataVector{used_for_size.size(), -272.15}));\n  CHECK_ITERABLE_APPROX(dt_phi.get(0),\n                        (DataVector{used_for_size.size(), -15.}));\n  CHECK_ITERABLE_APPROX(dt_phi.get(1),\n                        (DataVector{used_for_size.size(), -78.}));\n}\ntemplate <>\nvoid check_du_dt<3>(const DataVector& used_for_size) {\n  const auto vars = calculate_du_dt<3>(used_for_size);\n  const auto& dt_psi = get<CurvedScalarWave::Tags::Psi>(vars);\n  const auto& dt_pi = get<CurvedScalarWave::Tags::Pi>(vars);\n  const auto& dt_phi = get<CurvedScalarWave::Tags::Phi<3>>(vars);\n  CHECK_ITERABLE_APPROX(dt_psi.get(),\n                        (DataVector{used_for_size.size(), 255.8}));\n  CHECK_ITERABLE_APPROX(dt_pi.get(),\n                        (DataVector{used_for_size.size(), -976.85}));\n  CHECK_ITERABLE_APPROX(dt_phi.get(0),\n                        (DataVector{used_for_size.size(), -62.5}));\n  CHECK_ITERABLE_APPROX(dt_phi.get(1),\n                        (DataVector{used_for_size.size(), -117.}));\n  CHECK_ITERABLE_APPROX(dt_phi.get(2),\n                        (DataVector{used_for_size.size(), -171.5}));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.DuDt\",\n                  \"[Unit][Evolution]\") {\n  const DataVector used_for_size{2, 0.};\n  check_du_dt<1>(used_for_size);\n  check_du_dt<2>(used_for_size);\n  check_du_dt<3>(used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarWaveWorldtube\")\n\nset(LIBRARY_SOURCES\n  Test_AccelerationTerms.cpp\n  Test_KerrSchildDerivatives.cpp\n  Test_PunctureField.cpp\n  Test_RadiusFunctions.cpp\n  Test_PunctureInitialData.cpp\n  Test_SelfForce.cpp\n  Test_Tags.cpp\n  Test_Triggers.cpp\n  ElementActions/Test_SendToWorldtube.cpp\n  ElementActions/Test_ReceiveWorldtubeData.cpp\n  ElementActions/Test_InitializeConstraintGammas.cpp\n  ElementActions/Test_InitializeCurrentIteration.cpp\n  ElementActions/Test_Iterations.cpp\n  SingletonActions/Test_ChangeSlabSize.cpp\n  SingletonActions/Test_InitializeElementFacesGridCoordinates.cpp\n  SingletonActions/Test_InitializeEvolvedVariables.cpp\n  SingletonActions/Test_IterateAccelerationTerms.cpp\n  SingletonActions/Test_ObserveWorldtubeSolution.cpp\n  SingletonActions/Test_UpdateAcceleration.cpp\n  SingletonActions/Test_UpdateFunctionsOfTime.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ControlSystem\n  CurvedScalarWave\n  CurvedScalarWaveHelpers\n  DataStructures\n  DomainBoundaryConditions\n  DomainBoundaryConditionsHelpers\n  DomainCreators\n  GeneralRelativity\n  GeneralRelativityHelpers\n  GeneralRelativitySolutions\n  ObserverHelpers\n  ScalarWaveWorldtube\n  Time\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/Test_InitializeConstraintGammas.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/InitializeConstraintGammas.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_initialize_constraint_damping_gammas(\n    const gsl::not_null<std::mt19937*> gen,\n    const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  const size_t num_points = 100;\n  const auto random_coords =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          gen, dist, DataVector(num_points));\n  auto box = db::create<\n      db::AddSimpleTags<CurvedScalarWave::Tags::ConstraintGamma1,\n                        CurvedScalarWave::Tags::ConstraintGamma2,\n                        domain::Tags::Coordinates<Dim, Frame::Inertial>>>(\n      Scalar<DataVector>{}, Scalar<DataVector>{}, random_coords);\n  db::mutate_apply<CurvedScalarWave::Worldtube::Initialization::\n                       InitializeConstraintDampingGammas<Dim>>(\n      make_not_null(&box));\n  CHECK(get<CurvedScalarWave::Tags::ConstraintGamma1>(box) ==\n        Scalar<DataVector>{num_points, 0.});\n  CHECK_ITERABLE_APPROX(\n      get(get<CurvedScalarWave::Tags::ConstraintGamma2>(box)),\n      10. * exp(-square(get(magnitude(random_coords)) * 1.e-1)) + 1.e-4);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CSW.Worldtube.InitializeConstraintDampingGammas\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-10., 10.);\n  test_initialize_constraint_damping_gammas<1>(make_not_null(&gen),\n                                               make_not_null(&dist));\n  test_initialize_constraint_damping_gammas<2>(make_not_null(&gen),\n                                               make_not_null(&dist));\n  test_initialize_constraint_damping_gammas<3>(make_not_null(&gen),\n                                               make_not_null(&dist));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/Test_InitializeCurrentIteration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/InitializeCurrentIteration.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CSW.Worldtube.InitializeCurrentIteration\",\n    \"[Unit][Evolution]\") {\n  const size_t current_iteration = 77;\n  auto box =\n      db::create<db::AddSimpleTags<Tags::CurrentIteration>>(current_iteration);\n  db::mutate_apply<Initialization::InitializeCurrentIteration>(\n      make_not_null(&box));\n  CHECK(get<Tags::CurrentIteration>(box) == 0);\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/Test_Iterations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <random>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/IteratePunctureField.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/SendToWorldtube.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeElementFacesGridCoordinates.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/IterateAccelerationTerms.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ReceiveElementData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/SendAccelerationTerms.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateAcceleration.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using metavariables = Metavariables;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<\n                  domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                  domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                  Tags::PunctureField<Dim>, gr::Tags::Shift<DataVector, Dim>,\n                  gr::Tags::Lapse<DataVector>, ::Tags::TimeStepId,\n                  Tags::ParticlePositionVelocity<Dim>, Tags::FaceQuantities,\n                  Tags::CurrentIteration>,\n              db::AddComputeTags<\n                  Tags::FaceCoordinatesCompute<Dim, Frame::Inertial, true>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::SendToWorldtube, Actions::IteratePunctureField,\n                     CurvedScalarWave::Worldtube::Actions::\n                         ReceiveWorldtubeData>>>;\n};\ntemplate <typename Metavariables>\nstruct MockWorldtubeSingleton {\n  using metavariables = Metavariables;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::EvolvedPosition<Dim>, Tags::EvolvedVelocity<Dim>>>;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<\n                  Tags::ElementFacesGridCoordinates<Dim>, ::Tags::TimeStepId,\n                  Tags::CurrentIteration, Tags::GeodesicAcceleration<Dim>,\n                  CurvedScalarWave::Worldtube::Tags::ParticlePositionVelocity<\n                      Dim>,\n                  Tags::AccelerationTerms, dt_variables_tag>,\n              db::AddComputeTags<Tags::BackgroundQuantitiesCompute<Dim>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::ReceiveElementData,\n                     ::Actions::MutateApply<IterateAccelerationTerms>,\n                     Actions::SendAccelerationTerms<Metavariables>,\n                     ::Actions::MutateApply<UpdateAcceleration>>>>;\n  using component_being_mocked = WorldtubeSingleton<Metavariables>;\n};\n\ntemplate <size_t Dim>\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = Dim;\n\n  using component_list = tmpl::list<MockWorldtubeSingleton<MockMetavariables>,\n                                    MockElementArray<MockMetavariables>>;\n  using dg_element_array = MockElementArray<MockMetavariables>;\n  using const_global_cache_tags = tmpl::list<\n      domain::Tags::Domain<Dim>,\n      CurvedScalarWave::Tags::BackgroundSpacetime<gr::Solutions::KerrSchild>,\n      Tags::ExcisionSphere<Dim>, Tags::WorldtubeRadius, Tags::ExpansionOrder,\n      Tags::MaxIterations, Tags::Charge, Tags::Mass, ::Tags::Time,\n      Tags::SelfForceTurnOnTime, Tags::SelfForceTurnOnInterval>;\n};\n\nvoid test_iterations(const size_t max_iterations) {\n  CAPTURE(max_iterations);\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-10., 10.);\n  std::uniform_real_distribution<> pos_dist(2., 10.);\n  std::uniform_real_distribution<> vel_dist(-0.2, 0.2);\n  using metavars = MockMetavariables<Dim>;\n  domain::creators::register_derived_with_charm();\n  using element_chare = MockElementArray<metavars>;\n  using worldtube_chare = MockWorldtubeSingleton<metavars>;\n  const size_t initial_extent = 3;\n  const size_t face_size = initial_extent * initial_extent;\n  const DataVector used_for_size(face_size);\n  const auto basis = Spectral::Basis::Legendre;\n  const auto quadrature = Spectral::Quadrature::GaussLobatto;\n  const double charge = 0.1;\n  const double mass = 0.1;\n  gr::Solutions::KerrSchild kerr_schild(1., {{0., 0., 0.}}, {{0., 0., 0.}});\n  // we create several differently refined shells so a different number of\n  // elements sends data\n  for (const auto& [expansion_order, initial_refinement, worldtube_radius] :\n       cartesian_product(std::array<size_t, 2>{0, 1},\n                         std::array<size_t, 2>{0, 1},\n                         make_array(0.07, 1., 2.8))) {\n    CAPTURE(expansion_order);\n    CAPTURE(worldtube_radius);\n    CAPTURE(initial_refinement);\n    const domain::creators::Sphere shell{worldtube_radius,\n                                         3.,\n                                         domain::creators::Sphere::Excision{},\n                                         initial_refinement,\n                                         initial_extent,\n                                         true};\n    const auto shell_domain = shell.create_domain();\n    const auto excision_sphere =\n        shell_domain.excision_spheres().at(\"ExcisionSphere\");\n\n    const auto& initial_refinements = shell.initial_refinement_levels();\n    const auto& initial_extents = shell.initial_extents();\n    // before self force is turned on\n    const double time = 1.;\n    const double turn_on_time = 2.;\n    tuples::TaggedTuple<\n        domain::Tags::Domain<Dim>,\n        CurvedScalarWave::Tags::BackgroundSpacetime<gr::Solutions::KerrSchild>,\n        Tags::ExcisionSphere<Dim>, Tags::WorldtubeRadius, Tags::ExpansionOrder,\n        Tags::MaxIterations, Tags::Charge, Tags::Mass, ::Tags::Time,\n        Tags::SelfForceTurnOnTime, Tags::SelfForceTurnOnInterval>\n        tuple_of_opts{shell.create_domain(),\n                      kerr_schild,\n                      excision_sphere,\n                      excision_sphere.radius(),\n                      expansion_order,\n                      max_iterations,\n                      charge,\n                      std::make_optional(mass),\n                      time,\n                      std::make_optional(turn_on_time),\n                      std::nullopt};\n    ActionTesting::MockRuntimeSystem<metavars> runner{std::move(tuple_of_opts)};\n    const auto element_ids = initial_element_ids(initial_refinements);\n    const auto& blocks = shell_domain.blocks();\n    using puncture_field_type =\n        Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                             ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                             ::Tags::deriv<CurvedScalarWave::Tags::Psi,\n                                           tmpl::size_t<3>, Frame::Inertial>>>;\n    const puncture_field_type puncture_field{face_size, 0.};\n    const Time dummy_time{{1., 2.}, {1, 2}};\n    const TimeStepId dummy_time_step_id{true, 123, dummy_time};\n\n    const auto particle_position =\n        make_with_random_values<tnsr::I<double, Dim>>(make_not_null(&generator),\n                                                      pos_dist, 1);\n    const auto particle_velocity =\n        make_with_random_values<tnsr::I<double, Dim>>(make_not_null(&generator),\n                                                      vel_dist, 1);\n    const std::array<tnsr::I<double, Dim>, 2> particle_pos_vel{\n        particle_position, particle_velocity};\n    std::vector<ElementId<Dim>> abutting_element_ids{};\n    std::vector<ElementId<Dim>> non_abutting_element_ids{};\n\n    for (const auto& element_id : element_ids) {\n      auto element = domain::create_initial_element(element_id, blocks,\n                                                    initial_refinements);\n      auto mesh = domain::create_initial_mesh(initial_extents, element, basis,\n                                              quadrature);\n      const auto& my_block = blocks.at(element_id.block_id());\n      const ElementMap element_map(element_id,\n                                   my_block.stationary_map().get_clone());\n      const auto logical_coords = logical_coordinates(mesh);\n      const auto inertial_coords = element_map(logical_coords);\n      auto lapse_and_shift = kerr_schild.variables(\n          inertial_coords, 0.,\n          tmpl::list<gr::Tags::Lapse<DataVector>,\n                     gr::Tags::Shift<DataVector, Dim, Frame::Inertial>>{});\n      const bool is_abutting =\n          excision_sphere.abutting_direction(element_id).has_value();\n      using face_quantities_type =\n          Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                               ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                               gr::surfaces::Tags::AreaElement<DataVector>>>;\n      std::optional<face_quantities_type> optional_face_quantities =\n          is_abutting\n              ? std::make_optional<face_quantities_type>(\n                    make_with_random_values<face_quantities_type>(\n                        make_not_null(&generator), dist, DataVector(face_size)))\n              : std::nullopt;\n      std::optional<puncture_field_type> optional_puncture_field =\n          is_abutting ? std::make_optional<puncture_field_type>(puncture_field)\n                      : std::nullopt;\n      ActionTesting::emplace_array_component_and_initialize<element_chare>(\n          &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n          element_id,\n          {std::move(element), std::move(mesh), inertial_coords,\n           std::move(optional_puncture_field),\n           std::move(get<gr::Tags::Shift<DataVector, Dim, Frame::Inertial>>(\n               lapse_and_shift)),\n           std::move(get<gr::Tags::Lapse<DataVector>>(lapse_and_shift)),\n           dummy_time_step_id, particle_pos_vel, optional_face_quantities,\n           static_cast<size_t>(0)});\n      if (is_abutting) {\n        abutting_element_ids.push_back(element_id);\n\n      } else {\n        non_abutting_element_ids.push_back(element_id);\n      }\n    }\n\n    std::unordered_map<ElementId<Dim>, tnsr::I<DataVector, Dim, Frame::Grid>>\n        element_faces_grid_coords{};\n    Initialization::InitializeElementFacesGridCoordinates<Dim>::apply(\n        make_not_null(&element_faces_grid_coords), initial_extents,\n        initial_refinements, quadrature, shell_domain, excision_sphere);\n    const auto geodesic_acc = make_with_random_values<tnsr::I<double, Dim>>(\n        make_not_null(&generator), pos_dist, 1);\n    ActionTesting::emplace_singleton_component_and_initialize<worldtube_chare>(\n        &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n        {element_faces_grid_coords, dummy_time_step_id, static_cast<size_t>(0),\n         geodesic_acc, particle_pos_vel, Scalar<DataVector>{},\n         MockWorldtubeSingleton<\n             MockMetavariables<Dim>>::dt_variables_tag::type{}});\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n    // check that the non-abutting element_ids can just keep iterating\n    for (const auto& element_id : non_abutting_element_ids) {\n      CHECK(ActionTesting::get_next_action_index<element_chare>(\n                runner, element_id) == 0);\n      // SendToWorldtube\n      CHECK(ActionTesting::next_action_if_ready<element_chare>(\n          make_not_null(&runner), element_id));\n      CHECK(ActionTesting::get_next_action_index<element_chare>(\n                runner, element_id) == 1);\n      // IteratePunctureField\n      CHECK(ActionTesting::next_action_if_ready<element_chare>(\n          make_not_null(&runner), element_id));\n      CHECK(ActionTesting::get_next_action_index<element_chare>(\n                runner, element_id) == 2);\n      // ReceiveWorldtubeData\n      CHECK(ActionTesting::next_action_if_ready<element_chare>(\n          make_not_null(&runner), element_id));\n      CHECK(ActionTesting::get_next_action_index<element_chare>(\n                runner, element_id) == 0);\n    }\n\n    // ReceiveElementData should not be ready yet as the worldtube has not\n    // received any data\n    CHECK(not ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n\n    for (const auto& element_id : abutting_element_ids) {\n      const auto& element_iteration =\n          ActionTesting::get_databox_tag<element_chare, Tags::CurrentIteration>(\n              runner, element_id);\n      CHECK(element_iteration == 0);\n      // SendToWorldtube called on all elements\n      ActionTesting::next_action<element_chare>(make_not_null(&runner),\n                                                element_id);\n      // expecting data from the worldtube now\n      CHECK(not ActionTesting::next_action_if_ready<element_chare>(\n          make_not_null(&runner), element_id));\n    }\n\n    for (size_t current_iteration = 1; current_iteration + 1 <= max_iterations;\n         ++current_iteration) {\n      CAPTURE(current_iteration);\n      using inbox_tag = Tags::SphericalHarmonicsInbox<Dim>;\n      const auto& worldtube_inbox =\n          ActionTesting::get_inbox_tag<worldtube_chare, inbox_tag>(runner, 0);\n      CHECK(worldtube_inbox.count(dummy_time_step_id));\n      auto time_step_data = worldtube_inbox.at(dummy_time_step_id);\n      // these are all the element ids of elements abutting the worldtube, we\n      // check that these are the ones that were sent.\n      for (const auto& [element_id, _] : element_faces_grid_coords) {\n        CHECK(time_step_data.count(element_id));\n        time_step_data.erase(element_id);\n      }\n      // Check that have received only data from elements abutting the worldtube\n      CHECK(time_step_data.empty());\n      // ReceiveElementData\n      CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n          make_not_null(&runner), 0));\n      const auto& singleton_iteration =\n          ActionTesting::get_databox_tag<worldtube_chare,\n                                         Tags::CurrentIteration>(runner, 0);\n      CHECK(singleton_iteration == current_iteration);\n      CHECK(worldtube_inbox.empty());\n      // IterateAccelerationTerms\n      CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n          make_not_null(&runner), 0));\n      // SendAccelerationTerms\n      CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n          make_not_null(&runner), 0));\n      // expecting data from the elements now which is not sent yet\n      CHECK(not ActionTesting::next_action_if_ready<worldtube_chare>(\n          make_not_null(&runner), 0));\n      for (const auto& element_id : abutting_element_ids) {\n        const auto& self_force_inbox =\n            ActionTesting::get_inbox_tag<element_chare,\n                                         Tags::SelfForceInbox<Dim>>(runner,\n                                                                    element_id);\n        CHECK(self_force_inbox.count(dummy_time_step_id));\n        // the self force is not turned on, so the acceleration is just geodesic\n        for (size_t i = 0; i < Dim; ++i) {\n          CHECK(get(self_force_inbox.at(dummy_time_step_id))[i] ==\n                geodesic_acc.get(i));\n        }\n        // the other terms are just zero without the self force\n        for (size_t i = 3; i < 15; ++i) {\n          CHECK(get(self_force_inbox.at(dummy_time_step_id))[i] == 0.);\n        }\n        const std::string inbox_output =\n            Tags::SelfForceInbox<Dim>::output_inbox(self_force_inbox, 2);\n        const std::string expected_inbox_output =\n            MakeString{} << std::scientific << std::setprecision(16)\n                         << \"  SelfForceInbox:\\n\"\n                         << \"   Time: \" << dummy_time_step_id << \"\\n\";\n        CHECK(inbox_output == expected_inbox_output);\n        // IteratePunctureField\n        CHECK(ActionTesting::next_action_if_ready<element_chare>(\n            make_not_null(&runner), element_id));\n        CHECK(self_force_inbox.empty());\n        const auto& element_iteration =\n            ActionTesting::get_databox_tag<element_chare,\n                                           Tags::CurrentIteration>(runner,\n                                                                   element_id);\n        CHECK(element_iteration == current_iteration);\n        if (current_iteration > 0) {\n          CHECK(\n              ActionTesting::get_databox_tag<element_chare,\n                                             Tags::IteratedPunctureField<Dim>>(\n                  runner, element_id)\n                  .has_value());\n        }\n        // SendToWorldtube\n        CHECK(ActionTesting::next_action_if_ready<element_chare>(\n            make_not_null(&runner), element_id));\n        CHECK(not ActionTesting::next_action_if_ready<element_chare>(\n            make_not_null(&runner), element_id));\n      }\n    }\n    CHECK(ActionTesting::get_next_action_index<worldtube_chare>(runner, 0) ==\n          0);\n    // ReceiveElementData\n    CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n    // UpdateAcceleration should be queued now\n    CHECK(ActionTesting::get_next_action_index<worldtube_chare>(runner, 0) ==\n          3);\n    // iterations should have reset for singleton\n    const auto& singleton_iteration =\n        ActionTesting::get_databox_tag<worldtube_chare, Tags::CurrentIteration>(\n            runner, 0);\n    CHECK(singleton_iteration == 0);\n    for (const auto& element_id : abutting_element_ids) {\n      // Should be at ReceiveWorldtubeData now\n      CHECK(ActionTesting::get_next_action_index<element_chare>(\n                runner, element_id) == 2);\n      // iterations should have reset for elements\n      const auto& element_iteration =\n          ActionTesting::get_databox_tag<element_chare, Tags::CurrentIteration>(\n              runner, element_id);\n      CHECK(element_iteration == 0);\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.CurvedScalarWave.Worldtube.Iterations\", \"[Unit]\") {\n  test_iterations(0);\n  test_iterations(1);\n  test_iterations(2);\n  test_iterations(5);\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/Test_ReceiveWorldtubeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeElementFacesGridCoordinates.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/SendToElements.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using metavariables = Metavariables;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<\n                  domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                  Tags::PunctureField<Dim>, Tags::IteratedPunctureField<Dim>,\n                  gr::Tags::Shift<DataVector, Dim>, gr::Tags::Lapse<DataVector>,\n                  ::Tags::TimeStepId, Tags::WorldtubeSolution<Dim>,\n                  Tags::FaceCoordinates<Dim, Frame::Inertial, true>>,\n              db::AddComputeTags<>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<Actions::ReceiveWorldtubeData>>>;\n};\ntemplate <typename Metavariables>\nstruct MockWorldtubeSingleton {\n  using metavariables = Metavariables;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<\n                  Tags::ElementFacesGridCoordinates<Dim>, ::Tags::TimeStepId,\n                  Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim,\n                                       Frame::Inertial>,\n                  Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                                       Frame::Inertial>,\n                  Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim,\n                                       Frame::Inertial>,\n                  Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 1, Dim,\n                                       Frame::Inertial>,\n                  Tags::Psi0>,\n              db::AddComputeTags<>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::SendToElements<Metavariables>>>>;\n  using component_being_mocked = WorldtubeSingleton<Metavariables>;\n};\n\ntemplate <size_t Dim>\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = Dim;\n  using dg_element_array = MockElementArray<MockMetavariables>;\n  using component_list = tmpl::list<MockWorldtubeSingleton<MockMetavariables>,\n                                    MockElementArray<MockMetavariables>>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::ExcisionSphere<Dim>, Tags::ExpansionOrder,\n                 Tags::MaxIterations>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.CurvedScalarWave.Worldtube.ReceiveWorldtubeData\",\n                  \"[Unit]\") {\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-10., 10.);\n  using metavars = MockMetavariables<Dim>;\n  domain::creators::register_derived_with_charm();\n  using element_chare = MockElementArray<metavars>;\n  using worldtube_chare = MockWorldtubeSingleton<metavars>;\n  const size_t initial_extent = 5;\n  const size_t face_size = initial_extent * initial_extent;\n  const auto basis = Spectral::Basis::Legendre;\n  const auto quadrature = Spectral::Quadrature::GaussLobatto;\n  // we create several differently refined shells so a different number of\n  // elements sends data\n  for (const auto& [expansion_order, initial_refinement, worldtube_radius] :\n       cartesian_product(std::array<size_t, 2>{0, 1},\n                         std::array<size_t, 3>{0, 1, 2},\n                         make_array(0.07, 1., 2.8))) {\n    const domain::creators::Sphere shell{worldtube_radius,\n                                         3.,\n                                         domain::creators::Sphere::Excision{},\n                                         initial_refinement,\n                                         initial_extent,\n                                         true};\n    const auto shell_domain = shell.create_domain();\n    const auto excision_sphere =\n        shell_domain.excision_spheres().at(\"ExcisionSphere\");\n\n    const auto& initial_refinements = shell.initial_refinement_levels();\n    const auto& initial_extents = shell.initial_extents();\n    ActionTesting::MockRuntimeSystem<metavars> runner{\n        {excision_sphere, expansion_order, static_cast<size_t>(0)}};\n    const auto element_ids = initial_element_ids(initial_refinements);\n    const auto& blocks = shell_domain.blocks();\n\n    using puncture_field_type =\n        Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                             ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                             ::Tags::deriv<CurvedScalarWave::Tags::Psi,\n                                           tmpl::size_t<3>, Frame::Inertial>>>;\n\n    // The puncture field will get subtracted from the DG field. Here, we set\n    // the puncture field to 0, so psi and dt_psi are passed on directly\n    // and we can check the analytical result.\n    const puncture_field_type puncture_field{face_size, 0.};\n    const double psi_coefs_0 = dist(generator);\n    const double pi_coefs_0 = dist(generator);\n    const auto psi_coefs_1 =\n        make_with_random_values<tnsr::i<double, Dim, Frame::Inertial>>(\n            make_not_null(&generator), dist, 0.);\n    const auto pi_coefs_1 =\n        make_with_random_values<tnsr::i<double, Dim, Frame::Inertial>>(\n            make_not_null(&generator), dist, 0.);\n    const Time dummy_time{{1., 2.}, {1, 2}};\n    const TimeStepId dummy_time_step_id{true, 123, dummy_time};\n    std::unordered_map<ElementId<Dim>, tnsr::I<DataVector, Dim, Frame::Grid>>\n        element_faces_grid_coords{};\n    Initialization::InitializeElementFacesGridCoordinates<Dim>::apply(\n        make_not_null(&element_faces_grid_coords), initial_extents,\n        initial_refinements, quadrature, shell_domain, excision_sphere);\n    for (const auto& element_id : element_ids) {\n      auto element = domain::create_initial_element(element_id, blocks,\n                                                    initial_refinements);\n      auto mesh = domain::create_initial_mesh(initial_extents, element, basis,\n                                              quadrature);\n      const size_t grid_size = mesh.number_of_grid_points();\n\n      // we set lapse and shift to Minkowski so dt Psi = - Pi\n      Scalar<DataVector> lapse(grid_size, 1.);\n      tnsr::I<DataVector, Dim, Frame::Inertial> shift(grid_size, 0.);\n      // not initialized, the action does that for us\n      typename CurvedScalarWave::System<Dim>::variables_tag::type\n          worldtube_solution{};\n      std::optional<puncture_field_type> optional_puncture_field =\n          excision_sphere.abutting_direction(element_id).has_value()\n              ? std::make_optional<puncture_field_type>(puncture_field)\n              : std::nullopt;\n      std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>\n          inertial_face_coords{};\n\n      if (element_faces_grid_coords.count(element_id) > 0) {\n        const auto& grid_face_coords = element_faces_grid_coords.at(element_id);\n        inertial_face_coords.emplace(get<0>(grid_face_coords).size());\n        for (size_t i = 0; i < Dim; ++i) {\n          inertial_face_coords.value().get(i) = grid_face_coords.get(i);\n        }\n      }\n      ActionTesting::emplace_array_component_and_initialize<element_chare>(\n          &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n          element_id,\n          {std::move(element), std::move(mesh),\n           std::move(optional_puncture_field), std::nullopt, std::move(shift),\n           std::move(lapse), dummy_time_step_id, worldtube_solution,\n           inertial_face_coords});\n    }\n\n    ActionTesting::emplace_singleton_component_and_initialize<worldtube_chare>(\n        &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n        {element_faces_grid_coords, dummy_time_step_id,\n         Scalar<double>(psi_coefs_0), Scalar<double>(pi_coefs_0), psi_coefs_1,\n         pi_coefs_1, Scalar<DataVector>(static_cast<size_t>(1), psi_coefs_0)});\n\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n    // If it is an abutting element, we don't have data yet and should not be\n    // ready. Else, there is nothing to do so it's ready.\n    for (const auto& element_id : element_ids) {\n      if (element_faces_grid_coords.count(element_id)) {\n        CHECK(not ActionTesting::next_action_if_ready<element_chare>(\n            make_not_null(&runner), element_id));\n      } else {\n        CHECK(ActionTesting::next_action_if_ready<element_chare>(\n            make_not_null(&runner), element_id));\n      }\n    }\n    // SendToElements\n    ActionTesting::next_action<worldtube_chare>(make_not_null(&runner), 0);\n    const size_t num_coefs = expansion_order == 0 ? 1 : 4;\n    for (const auto& element_id : element_ids) {\n      using inbox_tag = Tags::RegularFieldInbox<Dim>;\n      const auto& element_inbox =\n          ActionTesting::get_inbox_tag<element_chare, inbox_tag>(runner,\n                                                                 element_id);\n      if (element_faces_grid_coords.count(element_id)) {\n        CHECK(element_inbox.count(dummy_time_step_id));\n        const auto& inbox_data = element_inbox.at(dummy_time_step_id);\n        const auto& inertial_coords =\n            ActionTesting::get_databox_tag<\n                element_chare,\n                Tags::FaceCoordinates<Dim, Frame::Inertial, true>>(runner,\n                                                                   element_id)\n                .value();\n        DataVector expected_solution_psi(face_size, psi_coefs_0);\n        DataVector expected_solution_dt_psi(face_size, pi_coefs_0);\n        DataVector expected_inbox_psi(num_coefs);\n        DataVector expected_inbox_dt_psi(num_coefs);\n        expected_inbox_psi.at(0) = psi_coefs_0;\n        expected_inbox_dt_psi.at(0) = pi_coefs_0;\n        if (expansion_order > 0) {\n          for (size_t i = 0; i < Dim; ++i) {\n            expected_inbox_psi.at(i + 1) = psi_coefs_1.get(i);\n            expected_inbox_dt_psi.at(i + 1) = pi_coefs_1.get(i);\n            expected_solution_psi +=\n                psi_coefs_1.get(i) * inertial_coords.get(i);\n            expected_solution_dt_psi +=\n                pi_coefs_1.get(i) * inertial_coords.get(i);\n          }\n        }\n\n        CHECK_ITERABLE_APPROX(get(get<CurvedScalarWave::Tags::Psi>(inbox_data)),\n                              expected_inbox_psi);\n        CHECK_ITERABLE_APPROX(\n            get(get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(inbox_data)),\n            expected_inbox_dt_psi);\n\n        // ReceiveWorldtubeData\n        CHECK(ActionTesting::next_action_if_ready<element_chare>(\n            make_not_null(&runner), element_id));\n        CHECK(element_inbox.empty());\n        const auto& worldtube_solution =\n            ActionTesting::get_databox_tag<element_chare,\n                                           Tags::WorldtubeSolution<Dim>>(\n                runner, element_id);\n        CHECK_ITERABLE_APPROX(\n            get(get<CurvedScalarWave::Tags::Psi>(worldtube_solution)),\n            expected_solution_psi);\n        CHECK_ITERABLE_APPROX(\n            get(get<CurvedScalarWave::Tags::Pi>(worldtube_solution)),\n            -expected_solution_dt_psi);\n        for (size_t i = 0; i < Dim; ++i) {\n          DataVector expected_di_psi(\n              face_size, expansion_order > 0 ? psi_coefs_1.get(i) : 0.);\n          CHECK_ITERABLE_APPROX(\n              get<CurvedScalarWave::Tags::Phi<Dim>>(worldtube_solution).get(i),\n              expected_di_psi);\n        }\n      } else {\n        CHECK(element_inbox.empty());\n      }\n    }\n  }\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/Test_SendToWorldtube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <random>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/IteratePunctureField.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/ReceiveWorldtubeData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/ElementActions/SendToWorldtube.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeElementFacesGridCoordinates.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/IterateAccelerationTerms.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ReceiveElementData.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateAcceleration.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using metavariables = Metavariables;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<\n                  domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                  domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                  Tags::PunctureField<Dim>, gr::Tags::Shift<DataVector, Dim>,\n                  gr::Tags::Lapse<DataVector>,\n                  domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                Frame::Inertial>,\n                  typename CurvedScalarWave::System<Dim>::variables_tag,\n                  ::Tags::TimeStepId, Tags::ParticlePositionVelocity<Dim>,\n                  Tags::CurrentIteration>,\n              db::AddComputeTags<\n                  Tags::FaceCoordinatesCompute<Dim, Frame::Inertial, true>,\n                  Tags::FaceQuantitiesCompute>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::SendToWorldtube, Actions::IteratePunctureField,\n                     CurvedScalarWave::Worldtube::Actions::\n                         ReceiveWorldtubeData>>>;\n};\ntemplate <typename Metavariables>\nstruct MockWorldtubeSingleton {\n  using metavariables = Metavariables;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::EvolvedPosition<Dim>, Tags::EvolvedVelocity<Dim>>>;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<\n                  Tags::ElementFacesGridCoordinates<Dim>, ::Tags::TimeStepId,\n                  Tags::CurrentIteration, Tags::GeodesicAcceleration<Dim>,\n                  CurvedScalarWave::Worldtube::Tags::ParticlePositionVelocity<\n                      Dim>,\n                  Tags::BackgroundQuantities<Dim>, dt_variables_tag>,\n              db::AddComputeTags<>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::ReceiveElementData,\n                     ::Actions::MutateApply<IterateAccelerationTerms>,\n                     Actions::SendAccelerationTerms<Metavariables>,\n                     ::Actions::MutateApply<UpdateAcceleration>>>>;\n  using component_being_mocked = WorldtubeSingleton<Metavariables>;\n};\n\ntemplate <size_t Dim>\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = Dim;\n\n  using component_list = tmpl::list<MockWorldtubeSingleton<MockMetavariables>,\n                                    MockElementArray<MockMetavariables>>;\n  using dg_element_array = MockElementArray<MockMetavariables>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<Dim>, Tags::ExcisionSphere<Dim>,\n                 Tags::WorldtubeRadius, Tags::ExpansionOrder,\n                 Tags::MaxIterations, Tags::Charge, Tags::Mass, ::Tags::Time,\n                 Tags::SelfForceTurnOnTime, Tags::SelfForceTurnOnInterval>;\n};\n\n// This test checks that `SendToWorldtube` integrates the regular field on the\n// element surfaces abutting the worldtube and sends this data to the worldtube\n// which reduces it in `ReceiveElementData`. There are several other actions in\n// in the action lists which are needed for the test to compile but are never\n// used. The iterative scheme is test in Test_Iterations.cpp.\nSPECTRE_TEST_CASE(\"Unit.CurvedScalarWave.Worldtube.SendToWorldtube\", \"[Unit]\") {\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-10., 10.);\n  using metavars = MockMetavariables<Dim>;\n  domain::creators::register_derived_with_charm();\n  using element_chare = MockElementArray<metavars>;\n  using worldtube_chare = MockWorldtubeSingleton<metavars>;\n  const size_t initial_extent = 10;\n  const size_t face_size = initial_extent * initial_extent;\n  const auto basis = Spectral::Basis::Legendre;\n  const auto quadrature = Spectral::Quadrature::GaussLobatto;\n  // unused but the tag is needed to compile\n  const double time = std::numeric_limits<double>::signaling_NaN();\n  // we create several differently refined shells so a different number of\n  // elements sends data\n  for (const auto& [expansion_order, initial_refinement, worldtube_radius] :\n       cartesian_product(std::array<size_t, 2>{0, 1},\n                         std::array<size_t, 2>{0, 1},\n                         make_array(0.07, 1., 2.8))) {\n    CAPTURE(expansion_order);\n    CAPTURE(worldtube_radius);\n    CAPTURE(initial_refinement);\n    const domain::creators::Sphere shell{worldtube_radius,\n                                         3.,\n                                         domain::creators::Sphere::Excision{},\n                                         initial_refinement,\n                                         initial_extent,\n                                         true};\n    const auto shell_domain = shell.create_domain();\n    const auto excision_sphere =\n        shell_domain.excision_spheres().at(\"ExcisionSphere\");\n\n    const auto& initial_refinements = shell.initial_refinement_levels();\n    const auto& initial_extents = shell.initial_extents();\n    // self force and therefore iterative scheme is turned off\n    tuples::TaggedTuple<domain::Tags::Domain<Dim>, Tags::ExcisionSphere<Dim>,\n                        Tags::WorldtubeRadius, Tags::ExpansionOrder,\n                        Tags::MaxIterations, Tags::Charge, Tags::Mass,\n                        ::Tags::Time, Tags::SelfForceTurnOnTime,\n                        Tags::SelfForceTurnOnInterval>\n        tuple_of_opts{shell.create_domain(),\n                      excision_sphere,\n                      excision_sphere.radius(),\n                      expansion_order,\n                      static_cast<size_t>(0),\n                      0.1,\n                      std::nullopt,\n                      time,\n                      std::nullopt,\n                      std::nullopt};\n    ActionTesting::MockRuntimeSystem<metavars> runner{std::move(tuple_of_opts)};\n    const auto element_ids = initial_element_ids(initial_refinements);\n    const auto& blocks = shell_domain.blocks();\n\n    using puncture_field_type =\n        Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                             ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                             ::Tags::deriv<CurvedScalarWave::Tags::Psi,\n                                           tmpl::size_t<3>, Frame::Inertial>>>;\n\n    // The puncture field will get subtracted from the DG field. Here, we set\n    // the puncture field to 0, so psi and dt_psi are integrated directly\n    // and we can check the analytical result.\n    const puncture_field_type puncture_field{face_size, 0.};\n    const double psi_coefs_0 = dist(generator);\n    const double pi_coefs_0 = dist(generator);\n    const auto psi_coefs_1 =\n        make_with_random_values<tnsr::i<double, Dim, Frame::Inertial>>(\n            make_not_null(&generator), dist, 0.);\n    const auto pi_coefs_1 =\n        make_with_random_values<tnsr::i<double, Dim, Frame::Inertial>>(\n            make_not_null(&generator), dist, 0.);\n    const Time dummy_time{{1., 2.}, {1, 2}};\n    const TimeStepId dummy_time_step_id{true, 123, dummy_time};\n    // the particle is at the center of the excision sphere which is the\n    // origin in the Shell domain\n    tnsr::I<double, Dim> particle_position(0.);\n    auto particle_velocity = particle_position;\n    const std::array<tnsr::I<double, Dim>, 2> particle_pos_vel{\n        {std::move(particle_position), std::move(particle_velocity)}};\n    for (const auto& element_id : element_ids) {\n      auto element = domain::create_initial_element(element_id, blocks,\n                                                    initial_refinements);\n      auto mesh = domain::create_initial_mesh(initial_extents, element, basis,\n                                              quadrature);\n      const auto& my_block = blocks.at(element_id.block_id());\n      const ElementMap element_map(element_id,\n                                   my_block.stationary_map().get_clone());\n      const auto logical_coords = logical_coordinates(mesh);\n      const auto inertial_coords = element_map(logical_coords);\n\n      auto inertial_inv_jacobian = element_map.inv_jacobian(logical_coords);\n      const size_t grid_size = mesh.number_of_grid_points();\n      // we set lapse and shift to Minkowski so dt Psi = - Pi, and the value we\n      // pass in for Pi will get integrated directly\n      Scalar<DataVector> lapse(grid_size, 1.);\n      tnsr::I<DataVector, Dim, Frame::Inertial> shift(grid_size, 0.);\n      typename CurvedScalarWave::System<Dim>::variables_tag::type evolved_vars(\n          grid_size, 0.);\n      const bool is_abutting =\n          excision_sphere.abutting_direction(element_id).has_value();\n      get(get<CurvedScalarWave::Tags::Psi>(evolved_vars)) = psi_coefs_0;\n      get(get<CurvedScalarWave::Tags::Pi>(evolved_vars)) = pi_coefs_0;\n      if (expansion_order > 0) {\n        for (size_t i = 0; i < Dim; ++i) {\n          get(get<CurvedScalarWave::Tags::Psi>(evolved_vars)) +=\n              psi_coefs_1.get(i) * inertial_coords.get(i);\n          get(get<CurvedScalarWave::Tags::Pi>(evolved_vars)) +=\n              pi_coefs_1.get(i) * inertial_coords.get(i);\n        }\n      }\n      std::optional<puncture_field_type> optional_puncture_field =\n          is_abutting ? std::make_optional<puncture_field_type>(puncture_field)\n                      : std::nullopt;\n\n      ActionTesting::emplace_array_component_and_initialize<element_chare>(\n          &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n          element_id,\n          {std::move(element), std::move(mesh), inertial_coords,\n           std::move(optional_puncture_field), std::move(shift),\n           std::move(lapse), std::move(inertial_inv_jacobian), evolved_vars,\n           dummy_time_step_id, particle_pos_vel, static_cast<size_t>(0)});\n    }\n\n    std::unordered_map<ElementId<Dim>, tnsr::I<DataVector, Dim, Frame::Grid>>\n        element_faces_grid_coords{};\n    Initialization::InitializeElementFacesGridCoordinates<Dim>::apply(\n        make_not_null(&element_faces_grid_coords), initial_extents,\n        initial_refinements, quadrature, shell_domain, excision_sphere);\n\n    // these are all unused\n    tuples::TaggedTuple<\n        gr::Tags::SpacetimeMetric<double, Dim>,\n        gr::Tags::InverseSpacetimeMetric<double, Dim>,\n        gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>,\n        gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>,\n        Tags::TimeDilationFactor>\n        background_quantities{};\n    MockWorldtubeSingleton<MockMetavariables<Dim>>::dt_variables_tag::type\n        dt_variables{};\n    tnsr::I<double, Dim> geodesic_acc{};\n    ActionTesting::emplace_singleton_component_and_initialize<worldtube_chare>(\n        &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n        {element_faces_grid_coords, dummy_time_step_id, static_cast<size_t>(0),\n         geodesic_acc, particle_pos_vel, background_quantities, dt_variables});\n\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n    // ReceiveElementData should not be ready yet as the worldtube has not\n    // received any data\n    CHECK(not ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n    // SendToWorldtube called on all elements\n    for (const auto& element_id : element_ids) {\n      ActionTesting::next_action<element_chare>(make_not_null(&runner),\n                                                element_id);\n    }\n\n    using inbox_tag = Tags::SphericalHarmonicsInbox<Dim>;\n    const auto& worldtube_inbox =\n        ActionTesting::get_inbox_tag<worldtube_chare, inbox_tag>(runner, 0);\n    CHECK(worldtube_inbox.count(dummy_time_step_id));\n    auto time_step_data = worldtube_inbox.at(dummy_time_step_id);\n    // these are all the element ids of elements abutting the worldtube, we\n    // check that these are the ones that were sent.\n    for (const auto& [element_id, _] : element_faces_grid_coords) {\n      CHECK(time_step_data.count(element_id));\n      time_step_data.erase(element_id);\n    }\n    // Check that have received only data from elements abutting the worldtube\n    CHECK(time_step_data.empty());\n    // ReceiveElementData called\n    CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n    CHECK(worldtube_inbox.empty());\n    const auto& psi_monopole_worldtube = ActionTesting::get_databox_tag<\n        worldtube_chare,\n        Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>>(\n        runner, 0);\n    const auto& dt_psi_monopole_worldtube = ActionTesting::get_databox_tag<\n        worldtube_chare, Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0,\n                                              Dim, Frame::Inertial>>(runner, 0);\n    // the integral is over a low resolution DG grid which introduces a large\n    // error\n    Approx apprx = Approx::custom().epsilon(1e-8).scale(1.0);\n    CHECK(get(psi_monopole_worldtube) == apprx(psi_coefs_0));\n    CHECK(get(dt_psi_monopole_worldtube) == -apprx(pi_coefs_0));\n    if (expansion_order > 0) {\n      const auto& psi_dipole_worldtube = ActionTesting::get_databox_tag<\n          worldtube_chare,\n          Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>>(\n          runner, 0);\n      const auto& dt_psi_dipole_worldtube = ActionTesting::get_databox_tag<\n          worldtube_chare, Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>,\n                                                1, Dim, Frame::Inertial>>(\n          runner, 0);\n      CHECK_ITERABLE_CUSTOM_APPROX(psi_dipole_worldtube, psi_coefs_1, apprx);\n      for (size_t i = 0; i < Dim; ++i) {\n        CHECK(dt_psi_dipole_worldtube.get(i) == apprx(-pi_coefs_1.get(i)));\n      }\n    }\n  }\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef self_force_acceleration(\n    dt_psi_monopole, psi_dipole, vel, charge, mass, inverse_metric, dilation\n):\n    # Prepend extra value so dimensions work out for einsum.\n    # The 0th component does not affect the final result\n    four_vel = np.concatenate((np.empty(1), vel), axis=0)\n    d_psi = np.concatenate(([dt_psi_monopole], psi_dipole), axis=0)\n    self_force_acc = np.einsum(\"ij,j\", inverse_metric, d_psi)\n    self_force_acc -= np.einsum(\"i,j,j\", four_vel, inverse_metric[0], d_psi)\n    self_force_acc *= charge / mass / dilation**2\n    return self_force_acc[1:]\n\n\ndef self_force_per_mass(d_psi, four_velocity, charge, mass, inverse_metric):\n    self_force_per_mass = np.einsum(\"ij,j\", inverse_metric, d_psi)\n    self_force_per_mass += np.einsum(\n        \"i,j,j\", four_velocity, four_velocity, d_psi\n    )\n    return charge / mass * self_force_per_mass\n\n\ndef dt_self_force_per_mass(\n    d_psi,\n    dt_d_psi,\n    four_velocity,\n    dt_four_velocity,\n    charge,\n    mass,\n    inverse_metric,\n    dt_inverse_metric,\n):\n    dt_self_force_per_mass = np.einsum(\"ij,j\", dt_inverse_metric, d_psi)\n    dt_self_force_per_mass += np.einsum(\"ij,j\", inverse_metric, dt_d_psi)\n\n    dt_self_force_per_mass += np.einsum(\n        \"i,j,j\", dt_four_velocity, four_velocity, d_psi\n    )\n    dt_self_force_per_mass += np.einsum(\n        \"i,j,j\", four_velocity, dt_four_velocity, d_psi\n    )\n    dt_self_force_per_mass += np.einsum(\n        \"i,j,j\", four_velocity, four_velocity, dt_d_psi\n    )\n    return charge / mass * dt_self_force_per_mass\n\n\ndef dt2_self_force_per_mass(\n    d_psi,\n    dt_d_psi,\n    dt2_d_psi,\n    four_velocity,\n    dt_four_velocity,\n    dt2_four_velocity,\n    charge,\n    mass,\n    inverse_metric,\n    dt_inverse_metric,\n    dt2_inverse_metric,\n):\n    dt2_self_force_per_mass = np.einsum(\"ij,j\", dt2_inverse_metric, d_psi)\n    dt2_self_force_per_mass += 2.0 * np.einsum(\n        \"ij,j\", dt_inverse_metric, dt_d_psi\n    )\n    dt2_self_force_per_mass += np.einsum(\"ij,j\", inverse_metric, dt2_d_psi)\n\n    dt2_self_force_per_mass += np.einsum(\n        \"i,j,j\", dt2_four_velocity, four_velocity, d_psi\n    )\n    dt2_self_force_per_mass += 2.0 * np.einsum(\n        \"i,j,j\", dt_four_velocity, dt_four_velocity, d_psi\n    )\n    dt2_self_force_per_mass += 2.0 * np.einsum(\n        \"i,j,j\", dt_four_velocity, four_velocity, dt_d_psi\n    )\n    dt2_self_force_per_mass += 2.0 * np.einsum(\n        \"i,j,j\", four_velocity, dt_four_velocity, dt_d_psi\n    )\n    dt2_self_force_per_mass += np.einsum(\n        \"i,j,j\", four_velocity, dt2_four_velocity, d_psi\n    )\n    dt2_self_force_per_mass += np.einsum(\n        \"i,j,j\", four_velocity, four_velocity, dt2_d_psi\n    )\n    return charge / mass * dt2_self_force_per_mass\n\n\ndef Du_self_force_per_mass(\n    self_force, dt_self_force, four_velocity, christoffel\n):\n    Du_self_force_per_mass = four_velocity[0] * dt_self_force\n    Du_self_force_per_mass += np.einsum(\n        \"ijk,j,k\", christoffel, four_velocity, self_force\n    )\n    return Du_self_force_per_mass\n\n\ndef dt_Du_self_force_per_mass(\n    self_force,\n    dt_self_force,\n    dt2_self_force,\n    four_velocity,\n    dt_four_velocity,\n    christoffel,\n    dt_christoffel,\n):\n    dt_Du_self_force_per_mass = (\n        dt_four_velocity[0] * dt_self_force + dt2_self_force * four_velocity[0]\n    )\n    dt_Du_self_force_per_mass += np.einsum(\n        \"ijk,j,k\", dt_christoffel, four_velocity, self_force\n    )\n    dt_Du_self_force_per_mass += np.einsum(\n        \"ijk,j,k\", christoffel, dt_four_velocity, self_force\n    )\n    dt_Du_self_force_per_mass += np.einsum(\n        \"ijk,j,k\", christoffel, four_velocity, dt_self_force\n    )\n    return dt_Du_self_force_per_mass\n\n\ndef turn_on_function(time, timescale):\n    return 1.0 - np.exp(-((time / timescale) ** 4))\n\n\ndef iterate_acceleration_terms(\n    vel,\n    psi_monopole,\n    dt_psi_monopole,\n    psi_dipole,\n    dt_psi_dipole,\n    roll_on,\n    dt_roll_on,\n    dt2_roll_on,\n    imetric,\n    christoffel,\n    trace_christoffel,\n    di_imetric,\n    dij_imetric,\n    di_trace_christoffel,\n    di_christoffel,\n    u0,\n    geodesic_acc,\n    charge,\n    mass,\n    current_iteration,\n):\n    res = np.zeros(15)\n\n    evolved_mass = (\n        mass if current_iteration == 1 else mass - charge * psi_monopole\n    )\n    dt_evolved_mass = dt_psi_monopole + np.einsum(\"i,i\", vel, psi_dipole)\n    dt_evolved_mass = 0 if current_iteration == 1 else -charge * dt_evolved_mass\n    self_force_acc = self_force_acceleration(\n        dt_psi_monopole, psi_dipole, vel, charge, evolved_mass, imetric, u0\n    )\n    acc = geodesic_acc + roll_on * self_force_acc\n    res[:3] = acc\n\n    d_psi = np.concatenate(([dt_psi_monopole], psi_dipole), axis=0)\n    u = np.concatenate(([u0], u0 * vel))\n    f = self_force_per_mass(d_psi, u, charge, evolved_mass, imetric)\n    f_rollon = f * roll_on\n    res[3:6] = f_rollon[:3]\n\n    dt_u = (\n        charge / evolved_mass * np.einsum(\"ab,b\", imetric, d_psi)\n        - np.einsum(\"abc,b,c\", christoffel, u, u)\n    ) / u0\n    dt_u_roll_on = dt_u * roll_on\n    dt_imetric = np.einsum(\"abc,a\", di_imetric, vel)\n    dt2_psi = (\n        -2.0 * np.einsum(\"i,i\", imetric[0, 1:], dt_psi_dipole)\n        + trace_christoffel[0] * dt_psi_monopole\n        + np.einsum(\"i,i\", trace_christoffel[1:], psi_dipole)\n    ) / imetric[0, 0]\n    dt_d_psi = np.concatenate(([dt2_psi], dt_psi_dipole))\n    dt_d_psi[0] += np.einsum(\"i,i\", vel, dt_psi_dipole)\n    dt_f = (\n        charge\n        / evolved_mass\n        * (\n            np.einsum(\"ab,b\", dt_imetric, d_psi)\n            + np.einsum(\"a,b,b\", dt_u_roll_on, u, d_psi)\n            + np.einsum(\"a,b,b\", u, dt_u_roll_on, d_psi)\n            + np.einsum(\"ab,b\", imetric, dt_d_psi)\n            + np.einsum(\"a,b,b\", u, u, dt_d_psi)\n        )\n    )\n    dt_f -= dt_evolved_mass / evolved_mass * f\n    dt_f_rollon = dt_f * roll_on + f * dt_roll_on\n    res[6:9] = dt_f_rollon[:3]\n\n    cov_f = Du_self_force_per_mass(f_rollon, dt_f_rollon, u, christoffel)\n    res[9:12] = cov_f[:3]\n    dt2_di_psi = (\n        -2.0 * np.einsum(\"ij,j\", di_imetric[:, 1:, 0], dt_psi_dipole)\n        + di_trace_christoffel[:, 0] * dt_psi_monopole\n        + trace_christoffel[0] * dt_psi_dipole\n        + np.einsum(\"ij,j\", di_trace_christoffel[:, 1:], psi_dipole)\n        - di_imetric[:, 0, 0] * dt2_psi\n    ) / imetric[0, 0]\n    dt3_psi = (\n        -2.0 * np.einsum(\"i,i\", imetric[0, 1:], dt2_di_psi)\n        + trace_christoffel[0] * dt2_psi\n        + np.einsum(\"i,i\", trace_christoffel[1:], dt_psi_dipole)\n    ) / imetric[0, 0]\n    dt2_d_psi = np.concatenate(([dt3_psi], dt2_di_psi))\n    dt2_d_psi[0] += 2.0 * np.einsum(\"i,i\", vel, dt2_di_psi) + np.einsum(\n        \"i,i\", acc, dt_psi_dipole\n    )\n    dt_christoffel = np.einsum(\"iabc,i\", di_christoffel, vel)\n    dt2_imetric = np.einsum(\"ijab,i,j\", dij_imetric, vel, vel) + np.einsum(\n        \"iab,i\", di_imetric, acc\n    )\n    dt2_u = (\n        charge\n        / evolved_mass\n        * (\n            np.einsum(\"ab,b\", dt_imetric, d_psi)\n            + np.einsum(\"ab,b\", imetric, dt_d_psi)\n            - dt_evolved_mass / evolved_mass * np.einsum(\"ab,b\", imetric, d_psi)\n        )\n        - np.einsum(\"abc,b,c\", dt_christoffel, u, u)\n        - 2.0 * np.einsum(\"abc,b,c\", christoffel, dt_u, u)\n        - dt_u * dt_u[0]\n    ) / u[0]\n    dt2_u_roll_on = dt_roll_on * dt_u + roll_on * dt2_u\n    dt2_f = dt2_self_force_per_mass(\n        d_psi,\n        dt_d_psi,\n        dt2_d_psi,\n        u,\n        dt_u_roll_on,\n        dt2_u_roll_on,\n        charge,\n        evolved_mass,\n        imetric,\n        dt_imetric,\n        dt2_imetric,\n    )\n    dt2_evolved_mass = (\n        dt2_psi\n        + 2.0 * np.einsum(\"i,i\", vel, dt_psi_dipole)\n        + np.einsum(\"i,i\", acc, psi_dipole)\n    )\n    dt2_evolved_mass = (\n        0.0 if current_iteration == 1 else -charge * dt2_evolved_mass\n    )\n\n    dt2_f += (\n        -2.0 * dt_evolved_mass / evolved_mass * dt_f\n        + f\n        * (2.0 * dt_evolved_mass**2 - dt2_evolved_mass * evolved_mass)\n        / evolved_mass**2\n    )\n\n    dt2_f_roll_on = dt2_roll_on * f + 2.0 * dt_roll_on * dt_f + roll_on * dt2_f\n    dt_cov_f = dt_Du_self_force_per_mass(\n        f_rollon,\n        dt_f_rollon,\n        dt2_f_roll_on,\n        u,\n        dt_u_roll_on,\n        christoffel,\n        dt_christoffel,\n    )\n    res[12:15] = dt_cov_f[:3]\n\n    return res\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/Test_ChangeSlabSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Inboxes.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ChangeSlabSize.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\ntemplate <typename Metavariables>\nstruct MockWorldtubeSingleton {\n  using metavariables = Metavariables;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<\n                  ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n                  ::Tags::TimeStep, ::Tags::ConcreteTimeStepper<TimeStepper>>,\n              time_stepper_ref_tags<TimeStepper>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<Actions::ChangeSlabSize>>>;\n  using component_being_mocked = WorldtubeSingleton<Metavariables>;\n};\n\ntemplate <size_t Dim>\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list =\n      tmpl::list<MockWorldtubeSingleton<MockMetavariables<Dim>>>;\n  using const_global_cache_tags = tmpl::list<>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.CurvedScalarWave.Worldtube.ChangeSlabSize\", \"[Unit]\") {\n  static constexpr size_t Dim = 3;\n  using worldtube_chare = MockWorldtubeSingleton<MockMetavariables<Dim>>;\n  register_classes_with_charm<TimeSteppers::Rk3HesthavenSsp>();\n\n  const auto time_stepper = TimeSteppers::Rk3HesthavenSsp{};\n  const double slab_1_start = 1.5;\n  const double slab_1_end = 2.0;\n\n  const Slab slab_1(slab_1_start, slab_1_end);\n  const auto start_time_1 = slab_1.start();\n  const TimeStepId time_step_id_1(true, 1, start_time_1);\n  const auto step_1 = time_step_id_1.step_time().slab().duration();\n\n  // we use two element ids for this test\n  const auto element_ids =\n      initial_element_ids(0, std::array<size_t, Dim>{{1, 0, 0}});\n\n  // we need to create a new runner every time an ASSERT is triggered because\n  // they can not be copied.\n  const auto create_new_runner =\n      [&time_step_id_1, &time_stepper,\n       &step_1](gsl::not_null<\n                ActionTesting::MockRuntimeSystem<MockMetavariables<Dim>>*>\n                    runner) {\n        ActionTesting::emplace_component_and_initialize<worldtube_chare>(\n            runner, 0,\n            {time_step_id_1, time_stepper.next_time_id(time_step_id_1, step_1),\n             step_1, std::make_unique<TimeSteppers::Rk3HesthavenSsp>()});\n        ActionTesting::set_phase(runner, Parallel::Phase::Testing);\n      };\n  const auto check_time_tags =\n      [](const ActionTesting::MockRuntimeSystem<MockMetavariables<Dim>>& runner,\n         const TimeStepId& time_step_id, const TimeStepId& next_time_step_id,\n         const TimeDelta& step) {\n        CHECK(\n            ActionTesting::get_databox_tag<worldtube_chare, ::Tags::TimeStepId>(\n                runner, 0) == time_step_id);\n        CHECK(ActionTesting::get_databox_tag<worldtube_chare,\n                                             ::Tags::Next<::Tags::TimeStepId>>(\n                  runner, 0) == next_time_step_id);\n        CHECK(ActionTesting::get_databox_tag<worldtube_chare, ::Tags::TimeStep>(\n                  runner, 0) == step);\n      };\n  using inbox_variables_type =\n      Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                           ::Tags::dt<CurvedScalarWave::Tags::Psi>>>;\n  {\n    ActionTesting::MockRuntimeSystem<MockMetavariables<Dim>> runner{{}};\n    create_new_runner(make_not_null(&runner));\n    // ChangeSlabSize but no data sent yet\n    CHECK_FALSE(ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n    check_time_tags(runner, time_step_id_1,\n                    time_stepper.next_time_id(time_step_id_1, step_1), step_1);\n    auto& worldtube_inbox =\n        ActionTesting::get_inbox_tag<worldtube_chare,\n                                     Tags::SphericalHarmonicsInbox<Dim>>(\n            make_not_null(&runner), 0);\n    worldtube_inbox[time_step_id_1][element_ids.at(0)] = inbox_variables_type{};\n    // ChangeSlabSize but only 1/2 elements have sent\n    CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n    check_time_tags(runner, time_step_id_1,\n                    time_stepper.next_time_id(time_step_id_1, step_1), step_1);\n    // send from other element\n    worldtube_inbox[time_step_id_1][element_ids.at(1)] = inbox_variables_type{};\n    CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n    // same slab size so nothing shoud have changed.\n    check_time_tags(runner, time_step_id_1,\n                    time_stepper.next_time_id(time_step_id_1, step_1), step_1);\n\n    const std::string inbox_output =\n        Tags::SphericalHarmonicsInbox<Dim>::output_inbox(worldtube_inbox, 2);\n    const std::string expected_inbox_output_v1 =\n        MakeString{} << std::scientific << std::setprecision(16)\n                     << \"  SphericalHarmonicsInbox:\\n\"\n                     << \"   Time: \" << time_step_id_1 << \"\\n\"\n                     << \"    ElementId: \" << element_ids.at(1) << \"\\n\"\n                     << \"    ElementId: \" << element_ids.at(0) << \"\\n\";\n    const std::string expected_inbox_output_v2 =\n        MakeString{} << std::scientific << std::setprecision(16)\n                     << \"  SphericalHarmonicsInbox:\\n\"\n                     << \"   Time: \" << time_step_id_1 << \"\\n\"\n                     << \"    ElementId: \" << element_ids.at(0) << \"\\n\"\n                     << \"    ElementId: \" << element_ids.at(1) << \"\\n\";\n    CHECK((inbox_output == expected_inbox_output_v1 or\n           inbox_output == expected_inbox_output_v2));\n\n#ifdef SPECTRE_DEBUG\n    const TimeStepId time_step_id_new_slab_number(true, 2, start_time_1);\n    // sending a second time step id, should fail\n    worldtube_inbox[time_step_id_new_slab_number][element_ids.at(0)] =\n        inbox_variables_type{};\n    CHECK_THROWS_WITH(\n        ActionTesting::next_action<worldtube_chare>(make_not_null(&runner), 0),\n        Catch::Matchers::ContainsSubstring(\n            \"Received data from two different time step ids.\"));\n#endif\n  }\n#ifdef SPECTRE_DEBUG\n  {\n    ActionTesting::MockRuntimeSystem<MockMetavariables<Dim>> runner{{}};\n    create_new_runner(make_not_null(&runner));\n    auto& worldtube_inbox =\n        ActionTesting::get_inbox_tag<worldtube_chare,\n                                     Tags::SphericalHarmonicsInbox<Dim>>(\n            make_not_null(&runner), 0);\n    // different slab start -> invalid\n    const double slab_2_start = 1.7;\n    const Slab slab_2(slab_2_start, slab_1_end);\n    const auto start_time_2 = slab_2.start();\n    const TimeStepId time_step_id_2(true, 1, start_time_2);\n    worldtube_inbox[time_step_id_2][element_ids.at(0)] = inbox_variables_type{};\n    CHECK_THROWS_WITH(\n        ActionTesting::next_action<worldtube_chare>(make_not_null(&runner), 0),\n        Catch::Matchers::ContainsSubstring(\n            \"The new slab should start at the same time as the old one.\"));\n  }\n#endif\n\n  {\n    ActionTesting::MockRuntimeSystem<MockMetavariables<Dim>> runner{{}};\n    create_new_runner(make_not_null(&runner));\n    auto& worldtube_inbox =\n        ActionTesting::get_inbox_tag<worldtube_chare,\n                                     Tags::SphericalHarmonicsInbox<Dim>>(\n            make_not_null(&runner), 0);\n    // different slab end\n    const double slab_2_end = 1.7;\n    const Slab slab_2(slab_1_start, slab_2_end);\n    const auto start_time_2 = slab_2.start();\n    const TimeStepId time_step_id_2(true, 1, start_time_2);\n    const auto step_2 = time_step_id_2.step_time().slab().duration();\n    worldtube_inbox[time_step_id_2][element_ids.at(0)] = inbox_variables_type{};\n    // data sent from only one element but with a different time step id, so the\n    // time tags should have changed.\n    CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n    check_time_tags(runner, time_step_id_2,\n                    time_stepper.next_time_id(time_step_id_2, step_2), step_2);\n    // data sent from both elements\n    worldtube_inbox[time_step_id_2][element_ids.at(1)] = inbox_variables_type{};\n    CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n        make_not_null(&runner), 0));\n    check_time_tags(runner, time_step_id_2,\n                    time_stepper.next_time_id(time_step_id_2, step_2), step_2);\n  }\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/Test_InitializeElementFacesGridCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Creators/TimeDependence/RotationAboutZAxis.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeElementFacesGridCoordinates.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\nvoid test_initialize_element_faces_coordinates_map(\n    const DomainCreator<3>& domain_creator,\n    const Spectral::Quadrature& quadrature) {\n  static constexpr size_t Dim = 3;\n  const auto domain = domain_creator.create_domain();\n  const auto& excision_spheres = domain.excision_spheres();\n  const auto& initial_refinements = domain_creator.initial_refinement_levels();\n  const auto& initial_extents = domain_creator.initial_extents();\n  for (const auto& [_, excision_sphere] : excision_spheres) {\n    auto box = db::create<db::AddSimpleTags<\n        ::domain::Tags::Domain<Dim>, ::domain::Tags::InitialExtents<Dim>,\n        ::domain::Tags::InitialRefinementLevels<Dim>,\n        evolution::dg::Tags::Quadrature, Tags::ExcisionSphere<Dim>,\n        Tags::ElementFacesGridCoordinates<Dim>>>(\n        domain_creator.create_domain(), initial_extents, initial_refinements,\n        quadrature, excision_sphere,\n        std::unordered_map<ElementId<3>,\n                           tnsr::I<DataVector, Dim, Frame::Grid>>{});\n\n    db::mutate_apply<\n        Initialization::InitializeElementFacesGridCoordinates<Dim>>(\n        make_not_null(&box));\n\n    const auto& element_faces_coords =\n        db::get<Tags::ElementFacesGridCoordinates<Dim>>(box);\n\n    // refinement is the same everywhere so we just get the 0th block in 0th\n    // direction\n    CHECK(element_faces_coords.size() ==\n          6 * pow(4, initial_refinements.at(0).at(0)));\n    for (const auto& [element_id, coords] : element_faces_coords) {\n      CHECK(excision_sphere.abutting_direction(element_id).has_value());\n      const auto& face_size = coords.get(0).size();\n      // extents are also the same everywhere\n      CHECK(face_size == square(initial_extents.at(0).at(0)));\n      CHECK_ITERABLE_APPROX(magnitude(coords).get(),\n                            DataVector(face_size, excision_sphere.radius()));\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.Worldtube.InitializeElementFacesGridCoordinates\",\n    \"[Unit][Evolution]\") {\n  for (const auto initial_refinement : std::array<size_t, 2>{{0, 1}}) {\n    const size_t initial_extents = 3;\n    const auto quadrature = Spectral::Quadrature::GaussLobatto;\n    CAPTURE(initial_refinement);\n    CAPTURE(quadrature);\n    INFO(\"Testing Shell time independent\");\n    const domain::creators::Sphere shell{1.5,\n                                         3.,\n                                         domain::creators::Sphere::Excision{},\n                                         initial_refinement,\n                                         initial_extents,\n                                         true};\n    test_initialize_element_faces_coordinates_map(shell, quadrature);\n\n    INFO(\"Testing Shell time dependent\");\n    const domain::creators::Sphere shell_time_dependent{\n        1.5,\n        3.,\n        domain::creators::Sphere::Excision{},\n        initial_refinement,\n        initial_extents,\n        true,\n        std::nullopt,\n        {},\n        {domain::CoordinateMaps::Distribution::Linear},\n        ShellWedges::All,\n        std::make_unique<\n            domain::creators::time_dependence::RotationAboutZAxis<3>>(0., 1.,\n                                                                      1., 1.)};\n    test_initialize_element_faces_coordinates_map(shell_time_dependent,\n                                                  quadrature);\n\n    INFO(\"Testing BinaryCompactObject\");\n    const domain::creators::BinaryCompactObject<true> binary_compact_object{\n        domain::creators::BinaryCompactObject<true>::Object{\n            0.5, 3., 8.,\n            std::make_optional(\n                domain::creators::BinaryCompactObject<true>::Excision{nullptr}),\n            false},\n        domain::creators::BinaryCompactObject<true>::Object{\n            1.5, 3., -5.,\n            std::make_optional(\n                domain::creators::BinaryCompactObject<true>::Excision{nullptr}),\n            false},\n        std::array<double, 2>{{0.1, 0.2}},\n        30.,\n        50.,\n        1.0,\n        initial_refinement,\n        initial_extents};\n    test_initialize_element_faces_coordinates_map(binary_compact_object,\n                                                  quadrature);\n  }\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/Test_InitializeEvolvedVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n#include <array>\n#include <memory>\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeEvolvedVariables.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CSW.Worldtube.InitializeEvolvedVariables\",\n    \"[Unit][Evolution]\") {\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::EvolvedPosition<3>, Tags::EvolvedVelocity<3>>>;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  const tnsr::I<double, 3> initial_pos{{1., 2., 3.}};\n  const tnsr::I<double, 3, Frame::Grid> initial_excision_pos{{1., 2., 3.}};\n  const tnsr::I<double, 3> initial_vel{{4., 5., 6.}};\n  const size_t current_iteration = 77;\n  const double expiration_time = 1234.;\n  const double initial_time = 0.;\n  const double initial_wt_radius = 12345.;\n  const double wt_radius = 2.;\n  const ExcisionSphere<3> excision_sphere(wt_radius, initial_excision_pos, {});\n  auto box =\n      db::create<db::AddSimpleTags<\n                     variables_tag, dt_variables_tag,\n                     ::Tags::HistoryEvolvedVariables<variables_tag>,\n                     ::Tags::ConcreteTimeStepper<TimeStepper>,\n                     Tags::InitialPositionAndVelocity, Tags::CurrentIteration,\n                     Tags::ExpirationTime, Tags::WorldtubeRadius, ::Tags::Time,\n                     Tags::ExcisionSphere<3>>,\n                 time_stepper_ref_tags<TimeStepper>>(\n          variables_tag::type{}, dt_variables_tag::type{},\n          TimeSteppers::History<variables_tag::type>{},\n          static_cast<std::unique_ptr<TimeStepper>>(\n              std::make_unique<TimeSteppers::AdamsBashforth>(4)),\n          std::array<tnsr::I<double, 3>, 2>{{initial_pos, initial_vel}},\n          current_iteration, expiration_time, initial_wt_radius, initial_time,\n          excision_sphere);\n\n  db::mutate_apply<Initialization::InitializeEvolvedVariables>(\n      make_not_null(&box));\n  const auto vars = db::get<variables_tag>(box);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(get<Tags::EvolvedPosition<3>>(vars).get(i)[0] == initial_pos.get(i));\n    CHECK(get<Tags::EvolvedVelocity<3>>(vars).get(i)[0] == initial_vel.get(i));\n  }\n  CHECK(db::get<dt_variables_tag>(box) ==\n        dt_variables_tag::type(size_t(1), 0.));\n  CHECK(db::get<::Tags::HistoryEvolvedVariables<variables_tag>>(box) ==\n        TimeSteppers::History<variables_tag::type>(1));\n  CHECK(get<Tags::CurrentIteration>(box) == 0);\n  CHECK(get<Tags::ExpirationTime>(box) == initial_time + 1e-10);\n  CHECK(get<Tags::WorldtubeRadius>(box) == wt_radius);\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/Test_IterateAccelerationTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/KerrSchildDerivatives.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/IterateAccelerationTerms.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CSW.Worldtube.IterateAccelerationTerms\",\n    \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/CurvedScalarWave/Worldtube\"};\n  MAKE_GENERATOR(gen);\n  static constexpr size_t Dim = 3;\n  const std::uniform_real_distribution<double> dist(-1., 1.);\n  const std::uniform_real_distribution<double> pos_dist(2., 10.);\n  const std::uniform_real_distribution<double> vel_dist(-0.1, 0.1);\n  const gr::Solutions::KerrSchild kerr_schild(1.4, {{0.1, 0.2, 0.3}},\n                                              {{0., 0., 0.}});\n  const auto pos = make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n      make_not_null(&gen), pos_dist, 1);\n  const auto vel = make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n      make_not_null(&gen), vel_dist, 1);\n\n  const auto psi_monopole =\n      make_with_random_values<Scalar<double>>(make_not_null(&gen), dist, 1);\n  const auto dt_psi_monopole =\n      make_with_random_values<Scalar<double>>(make_not_null(&gen), dist, 1);\n  const auto psi_dipole = make_with_random_values<tnsr::i<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto dt_psi_dipole = make_with_random_values<tnsr::i<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const double charge = 0.1;\n  const double mass = 0.1;\n  const double time = 10.;\n  const double turn_on_time = 20.;\n  const double turn_on_interval = 1.;\n  const size_t current_iteration = 1;\n  auto box = db::create<\n      db::AddSimpleTags<\n          Tags::AccelerationTerms, Tags::ParticlePositionVelocity<Dim>,\n          CurvedScalarWave::Tags::BackgroundSpacetime<\n              gr::Solutions::KerrSchild>,\n          Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>,\n          Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                               Frame::Inertial>,\n          Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>,\n          Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 1, Dim,\n                               Frame::Inertial>,\n          Tags::Charge, Tags::Mass, ::Tags::Time, Tags::SelfForceTurnOnTime,\n          Tags::SelfForceTurnOnInterval, Tags::CurrentIteration>,\n      db::AddComputeTags<Tags::BackgroundQuantitiesCompute<Dim>,\n                         Tags::GeodesicAccelerationCompute<Dim>>>(\n      Scalar<DataVector>{}, std::array<tnsr::I<double, 3>, 2>{pos, vel},\n      kerr_schild, psi_monopole, dt_psi_monopole, psi_dipole, dt_psi_dipole,\n      charge, std::make_optional(mass), time, std::make_optional(turn_on_time),\n      std::make_optional(turn_on_interval), current_iteration);\n  db::mutate_apply<IterateAccelerationTerms>(make_not_null(&box));\n\n  const auto& background = db::get<Tags::BackgroundQuantities<Dim>>(box);\n  const auto& geodesic_acc = db::get<Tags::GeodesicAcceleration<Dim>>(box);\n  const auto& imetric =\n      get<gr::Tags::InverseSpacetimeMetric<double, Dim>>(background);\n  const auto& metric = get<gr::Tags::SpacetimeMetric<double, Dim>>(background);\n  const auto& christoffel =\n      get<gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>(background);\n  const auto& trace_christoffel =\n      get<gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>>(\n          background);\n  const auto di_imetric = spatial_derivative_inverse_ks_metric(pos);\n  const auto dij_imetric = second_spatial_derivative_inverse_ks_metric(pos);\n  const auto di_trace_christoffel =\n      spatial_derivative_ks_contracted_christoffel(pos);\n  const auto di_metric = spatial_derivative_ks_metric(metric, di_imetric);\n  const auto dij_metric = second_spatial_derivative_metric(\n      metric, di_metric, di_imetric, dij_imetric);\n  const auto di_christoffel = spatial_derivative_christoffel(\n      di_metric, dij_metric, imetric, di_imetric);\n  const auto& u0 = get(get<Tags::TimeDilationFactor>(background));\n\n  double roll_on = 0.;\n  double dt_roll_on = 0.;\n  double dt2_roll_on = 0.;\n\n  const auto expected_terms = pypp::call<DataVector>(\n      \"SelfForce\", \"iterate_acceleration_terms\", vel, psi_monopole,\n      dt_psi_monopole, psi_dipole, dt_psi_dipole, roll_on, dt_roll_on,\n      dt2_roll_on, imetric, christoffel, trace_christoffel, di_imetric,\n      dij_imetric, di_trace_christoffel, di_christoffel, u0, geodesic_acc,\n      charge, mass, current_iteration);\n\n  CHECK_ITERABLE_APPROX(expected_terms,\n                        get(db::get<Tags::AccelerationTerms>(box)));\n\n  const double new_time = 21.123;\n  db::mutate<::Tags::Time>(\n      [&new_time](const gsl::not_null<double*> local_time) {\n        *local_time = new_time;\n      },\n      make_not_null(&box));\n  db::mutate_apply<IterateAccelerationTerms>(make_not_null(&box));\n\n  const double t_minus_turnup = new_time - turn_on_time;\n  roll_on = turn_on_function(t_minus_turnup, turn_on_interval);\n  dt_roll_on = dt_turn_on_function(t_minus_turnup, turn_on_interval);\n  dt2_roll_on = dt2_turn_on_function(t_minus_turnup, turn_on_interval);\n\n  const auto expected_terms_2 = pypp::call<DataVector>(\n      \"SelfForce\", \"iterate_acceleration_terms\", vel, psi_monopole,\n      dt_psi_monopole, psi_dipole, dt_psi_dipole, roll_on, dt_roll_on,\n      dt2_roll_on, imetric, christoffel, trace_christoffel, di_imetric,\n      dij_imetric, di_trace_christoffel, di_christoffel, u0, geodesic_acc,\n      charge, mass, current_iteration);\n\n  CHECK_ITERABLE_APPROX(expected_terms_2,\n                        get(db::get<Tags::AccelerationTerms>(box)));\n\n  static constexpr size_t new_iteration = 7;\n  db::mutate<Tags::CurrentIteration>(\n      [](const gsl::not_null<size_t*> local_current_iteration) {\n        *local_current_iteration = new_iteration;\n      },\n      make_not_null(&box));\n  db::mutate_apply<IterateAccelerationTerms>(make_not_null(&box));\n\n  const auto expected_terms_3 = pypp::call<DataVector>(\n      \"SelfForce\", \"iterate_acceleration_terms\", vel, psi_monopole,\n      dt_psi_monopole, psi_dipole, dt_psi_dipole, roll_on, dt_roll_on,\n      dt2_roll_on, imetric, christoffel, trace_christoffel, di_imetric,\n      dij_imetric, di_trace_christoffel, di_christoffel, u0, geodesic_acc,\n      charge, mass, new_iteration);\n  CHECK_ITERABLE_APPROX(expected_terms_3,\n                        get(db::get<Tags::AccelerationTerms>(box)));\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/Test_ObserveWorldtubeSolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/ObserveWorldtubeSolution.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/Triggers/TimeCompares.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\ntemplate <typename Metavariables>\nstruct MockWorldtubeSingleton {\n  using metavariables = Metavariables;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<\n                  ::Tags::Time, ::Tags::TimeStepId,\n                  Tags::ObserveCoefficientsTrigger, Tags::Psi0, Tags::dtPsi0,\n                  Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim,\n                                       Frame::Inertial>,\n                  Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                                       Frame::Inertial>,\n                  Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim,\n                                       Frame::Inertial>,\n                  Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 1, Dim,\n                                       Frame::Inertial>,\n                  Tags::ExcisionSphere<Dim>, Tags::EvolvedPosition<Dim>,\n                  Tags::EvolvedVelocity<Dim>,\n                  ::Tags::dt<Tags::EvolvedVelocity<Dim>>>,\n              db::AddComputeTags<>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<Actions::ObserveWorldtubeSolution>>>;\n  using component_being_mocked = WorldtubeSingleton<Metavariables>;\n};\n\ntemplate <size_t Dim>\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<\n      MockWorldtubeSingleton<MockMetavariables<Dim>>,\n      TestHelpers::observers::MockObserverWriter<MockMetavariables<Dim>>>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::ExpansionOrder, Tags::WorldtubeRadius, Tags::Verbosity>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Trigger, tmpl::list<Triggers::TimeCompares>>>;\n  };\n};\n\ntemplate <typename Generator>\nvoid check_observe_worldtube_solution(\n    const gsl::not_null<Generator*> generator,\n    const gsl::not_null<std::uniform_real_distribution<>*> dist,\n    const size_t expansion_order) {\n  static constexpr size_t Dim = 3;\n  using worldtube_chare = MockWorldtubeSingleton<MockMetavariables<Dim>>;\n  using mock_observer_writer =\n      TestHelpers::observers::MockObserverWriter<MockMetavariables<Dim>>;\n  std::unique_ptr<Trigger> trigger(new Triggers::TimeCompares(\n      Options::Comparator::Comparison::GreaterThan, 2.));\n  const auto psi0 = make_with_random_values<Scalar<DataVector>>(generator, dist,\n                                                                DataVector(1));\n  const auto dt_psi0 = make_with_random_values<Scalar<DataVector>>(\n      generator, dist, DataVector(1));\n  const auto psi_monopole =\n      make_with_random_values<Scalar<double>>(generator, dist, 1);\n  const auto dt_psi_monopole =\n      make_with_random_values<Scalar<double>>(generator, dist, 1);\n  const auto psi_dipole =\n      make_with_random_values<tnsr::i<double, Dim, Frame::Inertial>>(generator,\n                                                                     dist, 1);\n  const auto dt_psi_dipole =\n      make_with_random_values<tnsr::i<double, Dim, Frame::Inertial>>(generator,\n                                                                     dist, 1);\n\n  const auto position =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          generator, dist, DataVector(1));\n  const auto velocity =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          generator, dist, DataVector(1));\n  const auto acceleration =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          generator, dist, DataVector(1));\n  const double wt_radius = 1.5;\n  const ExcisionSphere<Dim> excision_sphere(\n      wt_radius, tnsr::I<double, Dim, Frame::Grid>{{0., 0., 0.}}, {});\n\n  ActionTesting::MockRuntimeSystem<MockMetavariables<Dim>> runner{\n      {expansion_order, wt_radius, ::Verbosity::Silent}};\n\n  // this should NOT trigger because the initial time is too low and the substep\n  // is not 0\n  const double initial_time_value = 0.5;\n  const Slab initial_slab(0.2, 1.2);\n  const TimeDelta test_delta(initial_slab, {2, 4});\n  const Time initial_time(initial_slab, {2, 4});\n  const TimeStepId id(true, 1234, initial_time, 98, test_delta, 1.1);\n  ActionTesting::emplace_component_and_initialize<worldtube_chare>(\n      make_not_null(&runner), 0,\n      {initial_time_value, id, std::move(trigger), psi0, dt_psi0, psi_monopole,\n       dt_psi_monopole, psi_dipole, dt_psi_dipole, excision_sphere, position,\n       velocity, acceleration});\n  ActionTesting::emplace_nodegroup_component_and_initialize<\n      mock_observer_writer>(make_not_null(&runner), {});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<worldtube_chare>(make_not_null(&runner), 0);\n  // should not trigger, so no action in queue\n  CHECK(ActionTesting::is_threaded_action_queue_empty<mock_observer_writer>(\n      runner, 0));\n\n  auto& box =\n      ActionTesting::get_databox<worldtube_chare>(make_not_null(&runner), 0);\n  // mutate the time. This still should NOT trigger because we are still not at\n  // the beginning of a slab\n  const double new_time_value = 3.9;\n  db::mutate<::Tags::Time>(\n      [&new_time_value](const gsl::not_null<double*> time) {\n        *time = new_time_value;\n      },\n      make_not_null(&box));\n  ActionTesting::next_action<worldtube_chare>(make_not_null(&runner), 0);\n  // should not trigger, so no action in queue\n  CHECK(ActionTesting::is_threaded_action_queue_empty<mock_observer_writer>(\n      runner, 0));\n\n  // create new time step id with substep 0\n  const Slab new_slab(new_time_value, new_time_value + 1.);\n  const Time new_time(new_slab, {0, 4});\n  const TimeStepId new_id(true, 12345, new_time);\n  db::mutate<::Tags::TimeStepId>(\n      [&new_id](const gsl::not_null<TimeStepId*> time_step_id) {\n        *time_step_id = new_id;\n      },\n      make_not_null(&box));\n\n  // now we should trigger the action\n  ActionTesting::next_action<worldtube_chare>(make_not_null(&runner), 0);\n  CHECK_FALSE(\n      ActionTesting::is_threaded_action_queue_empty<mock_observer_writer>(\n          runner, 0));\n  ActionTesting::invoke_queued_threaded_action<mock_observer_writer>(\n      make_not_null(&runner), 0);\n  const auto& mock_h5_file = ActionTesting::get_databox_tag<\n      mock_observer_writer, TestHelpers::observers::MockReductionFileTag>(\n      runner, 0);\n  const auto& dat_file = mock_h5_file.get_dat(\"/PsiTaylorCoefs\");\n  const auto& data = dat_file.get_data();\n\n  CHECK(data.at(0, 0) == new_time_value);\n  CHECK(data.at(0, 1) == get<0>(position)[0]);\n  CHECK(data.at(0, 2) == get<1>(position)[0]);\n  CHECK(data.at(0, 3) == get<2>(position)[0]);\n  CHECK(data.at(0, 4) == get<0>(velocity)[0]);\n  CHECK(data.at(0, 5) == get<1>(velocity)[0]);\n  CHECK(data.at(0, 6) == get<2>(velocity)[0]);\n  CHECK(data.at(0, 7) == get<0>(acceleration)[0]);\n  CHECK(data.at(0, 8) == get<1>(acceleration)[0]);\n  CHECK(data.at(0, 9) == get<2>(acceleration)[0]);\n\n  if (expansion_order == 0) {\n    const std::vector<std::string> legend_0{\n        {\"Time\", \"Position_x\", \"Position_y\", \"Position_z\", \"Velocity_x\",\n         \"Velocity_y\", \"Velocity_z\", \"Acceleration_x\", \"Acceleration_y\",\n         \"Acceleration_z\", \"Psi0\", \"dtPsi0\"}};\n    CHECK(legend_0 == dat_file.get_legend());\n    CHECK(data.rows() == 1);\n    CHECK(data.columns() == 12);\n    CHECK(data.at(0, 10) == get(psi_monopole));\n    CHECK(data.at(0, 11) == get(dt_psi_monopole));\n  } else if (expansion_order == 1) {\n    const std::vector<std::string> legend_1{\n        {\"Time\", \"Position_x\", \"Position_y\", \"Position_z\", \"Velocity_x\",\n         \"Velocity_y\", \"Velocity_z\", \"Acceleration_x\", \"Acceleration_y\",\n         \"Acceleration_z\", \"Psi0\", \"Psix\", \"Psiy\", \"Psiz\", \"dtPsi0\", \"dtPsix\",\n         \"dtPsiy\", \"dtPsiz\"}};\n    CHECK(legend_1 == dat_file.get_legend());\n    CHECK(data.rows() == 1);\n    CHECK(data.columns() == 18);\n    CHECK(data.at(0, 10) == get(psi_monopole));\n    CHECK(data.at(0, 11) == get<0>(psi_dipole));\n    CHECK(data.at(0, 12) == get<1>(psi_dipole));\n    CHECK(data.at(0, 13) == get<2>(psi_dipole));\n    CHECK(data.at(0, 14) == get(dt_psi_monopole));\n    CHECK(data.at(0, 15) == get<0>(dt_psi_dipole));\n    CHECK(data.at(0, 16) == get<1>(dt_psi_dipole));\n    CHECK(data.at(0, 17) == get<2>(dt_psi_dipole));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.CurvedScalarWave.Worldtube.ObserveWorldtubeSolution\",\n                  \"[Unit]\") {\n  static constexpr size_t Dim = 3;\n  register_factory_classes_with_charm<MockMetavariables<Dim>>();\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-10., 10.);\n  check_observe_worldtube_solution(make_not_null(&generator),\n                                   make_not_null(&dist), 0);\n  check_observe_worldtube_solution(make_not_null(&generator),\n                                   make_not_null(&dist), 1);\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/Test_UpdateAcceleration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateAcceleration.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CSW.Worldtube.UpdateAcceleration\",\n                  \"[Unit][Evolution]\") {\n  static constexpr size_t Dim = 3;\n  using variables_tag = ::Tags::Variables<\n      tmpl::list<Tags::EvolvedPosition<Dim>, Tags::EvolvedVelocity<Dim>>>;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const DataVector used_for_size(1);\n  auto dt_evolved_vars = make_with_random_values<dt_variables_tag::type>(\n      make_not_null(&gen), dist, used_for_size);\n  const auto geodesic_acc = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto vel = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto pos = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const std::array<tnsr::I<double, Dim>, 2> pos_vel{{pos, vel}};\n  const auto metric = make_with_random_values<tnsr::aa<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto inverse_metric = make_with_random_values<tnsr::AA<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto christoffel = make_with_random_values<tnsr::Abb<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto trace_christoffel = make_with_random_values<tnsr::A<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto dilation =\n      make_with_random_values<Scalar<double>>(make_not_null(&gen), dist, 1);\n  const Tags::BackgroundQuantities<Dim>::type background_quantities{\n      metric, inverse_metric, christoffel, trace_christoffel, dilation};\n  const auto psi_monopole =\n      make_with_random_values<Scalar<double>>(make_not_null(&gen), dist, 1);\n  const auto dt_psi_monopole =\n      make_with_random_values<Scalar<double>>(make_not_null(&gen), dist, 1);\n  const auto psi_dipole = make_with_random_values<tnsr::i<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n\n  const double mass = 0.1;\n  const double charge = 0.2;\n\n  const double time = 105.;\n  const double turn_on_time = 100.;\n  const double turn_on_interval = 10.;\n  const size_t max_iterations = 0;\n  auto box = db::create<db::AddSimpleTags<\n      dt_variables_tag, Tags::ParticlePositionVelocity<Dim>,\n      Tags::BackgroundQuantities<Dim>, Tags::GeodesicAcceleration<Dim>,\n      Stf::Tags::StfTensor<Tags::PsiWorldtube, 0, Dim, Frame::Inertial>,\n      Stf::Tags::StfTensor<::Tags::dt<Tags::PsiWorldtube>, 0, Dim,\n                           Frame::Inertial>,\n      Stf::Tags::StfTensor<Tags::PsiWorldtube, 1, Dim, Frame::Inertial>,\n      Tags::Charge, Tags::Mass, Tags::MaxIterations, ::Tags::Time,\n      Tags::SelfForceTurnOnTime, Tags::SelfForceTurnOnInterval>>(\n      std::move(dt_evolved_vars), pos_vel, background_quantities, geodesic_acc,\n      psi_monopole, dt_psi_monopole, psi_dipole, charge,\n      std::make_optional(mass), max_iterations, time,\n      std::make_optional(turn_on_time), std::make_optional(turn_on_interval));\n\n  db::mutate_apply<UpdateAcceleration>(make_not_null(&box));\n  const auto& dt_vars = db::get<dt_variables_tag>(box);\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(get<::Tags::dt<Tags::EvolvedPosition<Dim>>>(dt_vars).get(i)[0] ==\n          vel.get(i));\n    CHECK(get<::Tags::dt<Tags::EvolvedVelocity<Dim>>>(dt_vars).get(i)[0] ==\n          geodesic_acc.get(i));\n  }\n\n  db::mutate<Tags::MaxIterations>(\n      [](const gsl::not_null<size_t*> max_iterations_arg) {\n        *max_iterations_arg = 1;\n      },\n      make_not_null(&box));\n\n  db::mutate_apply<UpdateAcceleration>(make_not_null(&box));\n\n  const double roll_on =\n      turn_on_function(time - turn_on_time, turn_on_interval);\n  const double evolved_mass = mass - charge * get(psi_monopole);\n  const auto self_force_acc =\n      self_force_acceleration(dt_psi_monopole, psi_dipole, vel, charge,\n                              evolved_mass, inverse_metric, dilation);\n\n  const Approx approx = Approx::custom().epsilon(1e-12);\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(get<::Tags::dt<Tags::EvolvedPosition<Dim>>>(dt_vars).get(i)[0] ==\n          approx(vel.get(i)));\n    CHECK(get<::Tags::dt<Tags::EvolvedVelocity<Dim>>>(dt_vars).get(i)[0] ==\n          approx(geodesic_acc.get(i) + roll_on * self_force_acc.get(i)));\n  }\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/Test_UpdateFunctionsOfTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/UpdateFunctionsOfTime.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonChare.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/CurvedScalarWave/Worldtube/TestHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/FunctionsOfTimeAreReady.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\ntemplate <typename Metavariables>\nstruct MockWorldtubeSingleton {\n  using metavariables = Metavariables;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n  static constexpr size_t Dim = metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<::Tags::Time,\n                                Tags::ParticlePositionVelocity<Dim>,\n                                ::Tags::Next<::Tags::TimeStepId>,\n                                Tags::WorldtubeRadius, Tags::ExpirationTime>,\n              db::AddComputeTags<>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::UpdateFunctionsOfTime,\n                     domain::Actions::CheckFunctionsOfTimeAreReady<Dim>>>>;\n  using component_being_mocked = WorldtubeSingleton<Metavariables>;\n};\n\ntemplate <size_t Dim>\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<MockWorldtubeSingleton<MockMetavariables>>;\n  using const_global_cache_tags =\n      tmpl::list<Tags::ExcisionSphere<Dim>, domain::Tags::Domain<3>,\n                 Tags::WorldtubeRadiusParameters,\n                 Tags::BlackHoleRadiusParameters>;\n};\n\n// this test takes a random position and velocity of the scalar charge and then\n// calls `UpdateFunctionsOfTime`. After this action, the functions of time\n// should be set in such a way that the worldtube excision sphere has the same\n// position, velocity as the particle with the appropriate worldtube radius.\nvoid test_particle_motion() {\n  static constexpr size_t Dim = 3;\n  using metavars = MockMetavariables<Dim>;\n  domain::creators::register_derived_with_charm();\n  using worldtube_chare = MockWorldtubeSingleton<metavars>;\n  const double initial_worldtube_radius = 0.456;\n\n  const auto bco_domain_creator =\n      TestHelpers::CurvedScalarWave::Worldtube::worldtube_binary_compact_object<\n          true>(6., initial_worldtube_radius, 0.1);\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  const auto domain = bco_domain_creator->create_domain();\n  const auto wt_excision_sphere =\n      domain.excision_spheres().at(\"ExcisionSphereA\");\n  const auto bh_excision_sphere =\n      domain.excision_spheres().at(\"ExcisionSphereB\");\n\n  // these will be overwritten\n  const std::array<double, 4> wt_radius_options{{0.1, 5., 1e-2, 2.3}};\n  const std::array<double, 4> bh_radius_options{{0.1, 5., 1e-2, 0.123}};\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {\n          wt_excision_sphere,\n          bco_domain_creator->create_domain(),\n          wt_radius_options,\n          bh_radius_options,\n      },\n      bco_domain_creator->functions_of_time()};\n\n  const double current_time = 1.;\n  const Time next_time{{current_time, 2.}, {1, 2}};\n  const TimeStepId next_time_step_id{true, 123, next_time};\n  std::array<tnsr::I<double, Dim, Frame::Inertial>, 2> particle_pos_vel{};\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> pos_dist(-10., 10.);\n  std::uniform_real_distribution<double> vel_dist(-0.2, 0.2);\n  particle_pos_vel.at(0) =\n      tnsr::I<double, Dim, Frame::Inertial>{{pos_dist(gen), pos_dist(gen), 0.}};\n  particle_pos_vel.at(1) =\n      tnsr::I<double, Dim, Frame::Inertial>{{vel_dist(gen), vel_dist(gen), 0.}};\n  const double initial_expiration_time = 1e-10;\n  ActionTesting::emplace_singleton_component_and_initialize<worldtube_chare>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n      {1., particle_pos_vel, next_time_step_id, initial_worldtube_radius,\n       initial_expiration_time});\n  const auto& global_cache = ActionTesting::cache<worldtube_chare>(runner, 0);\n\n  const auto& fots = get<domain::Tags::FunctionsOfTime>(global_cache);\n  CHECK(fots.at(\"Rotation\")->time_bounds()[1] ==\n        approx(initial_expiration_time));\n  CHECK(fots.at(\"Expansion\")->time_bounds()[1] ==\n        approx(initial_expiration_time));\n  CHECK(fots.at(\"SizeA\")->time_bounds()[1] == approx(initial_expiration_time));\n  CHECK(fots.at(\"SizeB\")->time_bounds()[1] == approx(initial_expiration_time));\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  // UpdateFunctionsOfTime\n  CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n      make_not_null(&runner), 0));\n  // CheckFunctionsOfTimeAreReady\n  CHECK(ActionTesting::next_action_if_ready<worldtube_chare>(\n      make_not_null(&runner), 0));\n\n  const double new_expiration_time_expected = 1.005;\n\n  CHECK(fots.at(\"Rotation\")->time_bounds()[1] ==\n        approx(new_expiration_time_expected));\n  CHECK(fots.at(\"Expansion\")->time_bounds()[1] ==\n        approx(new_expiration_time_expected));\n  CHECK(fots.at(\"SizeA\")->time_bounds()[1] ==\n        approx(new_expiration_time_expected));\n  CHECK(fots.at(\"SizeB\")->time_bounds()[1] ==\n        approx(new_expiration_time_expected));\n\n  const auto& expiration_time_tag =\n      ActionTesting::get_databox_tag<worldtube_chare, Tags::ExpirationTime>(\n          runner, 0);\n  CHECK(expiration_time_tag == approx(new_expiration_time_expected));\n\n  const auto& wt_neighbor_block = domain.blocks()[1];\n  // does not include shape map\n  const auto& grid_to_inertial_map_particle =\n      wt_excision_sphere.moving_mesh_grid_to_inertial_map();\n  // includes worldtube shape map\n  const auto& grid_to_inertial_map_wt_boundary =\n      wt_neighbor_block.moving_mesh_grid_to_inertial_map();\n\n  // checks the position and velocity of the worldtube excision sphere are now\n  // mapped according to the position and velocity of the particle\n  const auto mapped_particle =\n      grid_to_inertial_map_particle.coords_frame_velocity_jacobians(\n          wt_excision_sphere.center(), 0.5, fots);\n  CHECK_ITERABLE_APPROX(std::get<0>(mapped_particle), particle_pos_vel.at(0));\n  CHECK_ITERABLE_APPROX(std::get<3>(mapped_particle), particle_pos_vel.at(1));\n  const double orbit_radius = get(magnitude(particle_pos_vel[0]));\n\n  // check that the grid points on the worldtube boundary are mapped according\n  // to the prescribed worldtube radius\n  auto wt_boundary_point = wt_excision_sphere.center();\n  wt_boundary_point[0] += wt_excision_sphere.radius();\n  const auto mapped_wt_boundary_data =\n      grid_to_inertial_map_wt_boundary.coords_frame_velocity_jacobians(\n          wt_boundary_point, 0.5, fots);\n  const auto& mapped_wt_boundary_point = std::get<0>(mapped_wt_boundary_data);\n  const double wt_radius = get(magnitude(tenex::evaluate<ti::I>(\n      mapped_wt_boundary_point(ti::I) - particle_pos_vel.at(0)(ti::I))));\n  const double wt_radius_expected = smooth_broken_power_law(\n      orbit_radius, wt_radius_options[0], wt_radius_options[1],\n      wt_radius_options[2], wt_radius_options[3]);\n  CHECK(wt_radius == approx(wt_radius_expected));\n\n  const auto& wt_radius_tag =\n      ActionTesting::get_databox_tag<worldtube_chare, Tags::WorldtubeRadius>(\n          runner, 0);\n  CHECK(wt_radius_tag == approx(wt_radius_expected));\n\n  const auto& bh_neighbor_block = domain.blocks()[12];\n  // includes bh shape map\n  const auto& grid_to_inertial_map_bh_boundary =\n      bh_neighbor_block.moving_mesh_grid_to_inertial_map();\n  auto bh_boundary_point = bh_excision_sphere.center();\n  bh_boundary_point[0] += bh_excision_sphere.radius();\n  const auto mapped_bh_boundary_point = std::get<0>(\n      grid_to_inertial_map_bh_boundary.coords_frame_velocity_jacobians(\n          bh_boundary_point, 0.5, fots));\n  const double bh_radius = get(magnitude(mapped_bh_boundary_point));\n  const double bh_radius_expected = smooth_broken_power_law(\n      orbit_radius, bh_radius_options[0], bh_radius_options[1],\n      bh_radius_options[2], bh_radius_options[3]);\n  CHECK(bh_radius == approx(bh_radius_expected));\n}\n\nSPECTRE_TEST_CASE(\"Unit.CurvedScalarWave.Worldtube.UpdateFunctionsOfTime\",\n                  \"[Unit]\") {\n  test_particle_motion();\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/Test_AccelerationTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave {\nnamespace {\n\nusing deriv_psi_tag =\n    ::Tags::deriv<Tags::Psi, tmpl::size_t<3>, Frame::Inertial>;\nusing puncture_vars =\n    Variables<tmpl::list<Tags::Psi, ::Tags::dt<Tags::Psi>, deriv_psi_tag>>;\n\n// tests the derivative of the puncture field against a finite difference\n// calculation\nvoid test_derivative() {\n  MAKE_GENERATOR(gen);\n  Approx local_approx = Approx::custom().epsilon(1e-9).scale(1.0);\n  std::uniform_real_distribution<double> theta_dist{0, M_PI};\n  std::uniform_real_distribution<double> phi_dist{0, 2 * M_PI};\n  std::uniform_real_distribution<double> pos_dist{2., 10.};\n  std::uniform_real_distribution<double> vel_acc_dist{-0.1, 0.1};\n  const size_t size = 1;\n  const double wt_radius = 0.1;\n  const auto random_position =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&pos_dist), 1);\n  const auto random_velocity =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&vel_acc_dist), 1);\n  const auto random_acceleration =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&vel_acc_dist), 1);\n  const auto random_self_force = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&gen), make_not_null(&vel_acc_dist), DataVector(12));\n  const auto helper_func_0 = [&random_position, &random_velocity,\n                              &random_acceleration, &random_self_force,\n                              size](const std::array<double, 3>& point) {\n    tnsr::I<DataVector, 3, Frame::Inertial> tensor_point(size);\n    tensor_point.get(0) = point.at(0);\n    tensor_point.get(1) = point.at(1);\n    tensor_point.get(2) = point.at(2);\n    puncture_vars acc_terms{1};\n    Worldtube::acceleration_terms_0(\n        make_not_null(&acc_terms), tensor_point, random_position,\n        random_velocity, random_acceleration, 1., get(random_self_force)[0],\n        get(random_self_force)[1], get(random_self_force)[2],\n        get(random_self_force)[3], get(random_self_force)[4],\n        get(random_self_force)[5]);\n    return get<Tags::Psi>(acc_terms).get()[0];\n  };\n  const auto helper_func_1 = [&random_position, &random_velocity,\n                              &random_acceleration, &random_self_force,\n                              size](const std::array<double, 3>& point) {\n    tnsr::I<DataVector, 3, Frame::Inertial> tensor_point(size);\n    tensor_point.get(0) = point.at(0);\n    tensor_point.get(1) = point.at(1);\n    tensor_point.get(2) = point.at(2);\n    puncture_vars acc_terms{1};\n    Worldtube::acceleration_terms_1(\n        make_not_null(&acc_terms), tensor_point, random_position,\n        random_velocity, random_acceleration, 1., get(random_self_force)[0],\n        get(random_self_force)[1], get(random_self_force)[2],\n        get(random_self_force)[3], get(random_self_force)[4],\n        get(random_self_force)[5], get(random_self_force)[6],\n        get(random_self_force)[7], get(random_self_force)[8],\n        get(random_self_force)[9], get(random_self_force)[10],\n        get(random_self_force)[11]);\n    return get<Tags::Psi>(acc_terms).get()[0];\n  };\n  for (size_t i = 0; i < 20; ++i) {\n    const auto theta = theta_dist(gen);\n    const auto phi = phi_dist(gen);\n    std::array<double, 3> test_point{wt_radius * sin(theta) * cos(phi),\n                                     wt_radius * sin(theta) * sin(phi),\n                                     wt_radius * cos(theta)};\n    tnsr::I<DataVector, 3, Frame::Inertial> tensor_point(size);\n    for (size_t j = 0; j < 3; ++j) {\n      gsl::at(test_point, j) -= random_position.get(j);\n      tensor_point.get(j)[0] = test_point.at(j);\n    }\n    const double dx = 1e-4;\n\n    // Check order 0\n    puncture_vars acc_terms_0{1};\n    Worldtube::acceleration_terms_0(\n        make_not_null(&acc_terms_0), tensor_point, random_position,\n        random_velocity, random_acceleration, 1., get(random_self_force)[0],\n        get(random_self_force)[1], get(random_self_force)[2],\n        get(random_self_force)[3], get(random_self_force)[4],\n        get(random_self_force)[5]);\n    const auto& di_psi_0 = get<deriv_psi_tag>(acc_terms_0);\n    for (size_t j = 0; j < 3; ++j) {\n      const auto numerical_deriv_j =\n          numerical_derivative(helper_func_0, test_point, j, dx);\n      CHECK(di_psi_0.get(j)[0] == local_approx(numerical_deriv_j));\n    }\n    // check order 1\n    puncture_vars acc_terms_1{1};\n    Worldtube::acceleration_terms_1(\n        make_not_null(&acc_terms_1), tensor_point, random_position,\n        random_velocity, random_acceleration, 1., get(random_self_force)[0],\n        get(random_self_force)[1], get(random_self_force)[2],\n        get(random_self_force)[3], get(random_self_force)[4],\n        get(random_self_force)[5], get(random_self_force)[6],\n        get(random_self_force)[7], get(random_self_force)[8],\n        get(random_self_force)[9], get(random_self_force)[10],\n        get(random_self_force)[11]);\n    const auto& di_psi_1 = get<deriv_psi_tag>(acc_terms_1);\n    for (size_t j = 0; j < 3; ++j) {\n      const auto numerical_deriv_j =\n          numerical_derivative(helper_func_1, test_point, j, dx);\n      CHECK(di_psi_1.get(j)[0] == local_approx(numerical_deriv_j));\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.AccelerationTerms\",\n                  \"[Unit][Evolution]\") {\n  test_derivative();\n}\n}  // namespace\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/Test_KerrSchildDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/KerrSchildDerivatives.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\nvoid test_derivative_inverse_metric() {\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  std::uniform_real_distribution<double> pos_dist{2., 10.};\n\n  const gr::Solutions::KerrSchild kerr_schild(1., {{0., 0., 0.}},\n                                              {{0., 0., 0.}});\n  const auto test_point =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&pos_dist), 1);\n  const auto di_imetric = spatial_derivative_inverse_ks_metric(test_point);\n\n  const std::array<double, 3> array_point{test_point.get(0), test_point.get(1),\n                                          test_point.get(2)};\n  const double dx = 1e-4;\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b <= a; ++b) {\n      for (size_t j = 0; j < 3; ++j) {\n        auto inverse_metric_helper = [&kerr_schild, a,\n                                      b](const std::array<double, 3>& point) {\n          const tnsr::I<double, 3> tensor_point(point);\n          const auto kerr_schild_quantities_local = kerr_schild.variables(\n              tensor_point, 0.,\n              tmpl::list<gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                         gr::Tags::InverseSpatialMetric<double, Dim>>{});\n          const auto inverse_metric_local = gr::inverse_spacetime_metric(\n              get<gr::Tags::Lapse<double>>(kerr_schild_quantities_local),\n              get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities_local),\n              get<gr::Tags::InverseSpatialMetric<double, Dim>>(\n                  kerr_schild_quantities_local));\n          return inverse_metric_local.get(a, b);\n        };\n        const auto numerical_deriv_imetric_j =\n            numerical_derivative(inverse_metric_helper, array_point, j, dx);\n        CHECK(di_imetric.get(j, a, b) ==\n              local_approx(numerical_deriv_imetric_j));\n      }\n    }\n  }\n}\n\nvoid test_derivative_metric() {\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  std::uniform_real_distribution<double> pos_dist{2., 10.};\n\n  const gr::Solutions::KerrSchild kerr_schild(1., {{0., 0., 0.}},\n                                              {{0., 0., 0.}});\n  const auto test_point =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&pos_dist), 1);\n  const auto kerr_schild_quantities = kerr_schild.variables(\n      test_point, 0.,\n      tmpl::list<gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                 gr::Tags::SpatialMetric<double, Dim>,\n                 gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>{});\n  const auto metric = gr::spacetime_metric(\n      get<gr::Tags::Lapse<double>>(kerr_schild_quantities),\n      get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities),\n      get<gr::Tags::SpatialMetric<double, Dim>>(kerr_schild_quantities));\n\n  const auto& di_imetric = spatial_derivative_inverse_ks_metric(test_point);\n  const auto di_metric = spatial_derivative_ks_metric(metric, di_imetric);\n\n  const std::array<double, 3> array_point{test_point.get(0), test_point.get(1),\n                                          test_point.get(2)};\n  const double dx = 1e-4;\n\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b <= a; ++b) {\n      for (size_t j = 0; j < 3; ++j) {\n        const auto metric_helper = [&kerr_schild, a,\n                                    b](const std::array<double, 3>& point) {\n          const tnsr::I<double, 3> tensor_point(point);\n          const auto kerr_schild_quantities_local = kerr_schild.variables(\n              tensor_point, 0.,\n              tmpl::list<gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                         gr::Tags::SpatialMetric<double, Dim>>{});\n          const auto metric_local = gr::spacetime_metric(\n              get<gr::Tags::Lapse<double>>(kerr_schild_quantities_local),\n              get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities_local),\n              get<gr::Tags::SpatialMetric<double, Dim>>(\n                  kerr_schild_quantities_local));\n          return metric_local.get(a, b);\n        };\n        const auto numerical_deriv_metric_j =\n            numerical_derivative(metric_helper, array_point, j, dx);\n        CHECK(di_metric.get(j, a, b) == local_approx(numerical_deriv_metric_j));\n      }\n    }\n  }\n}\n\nvoid test_second_derivative_inverse_metric() {\n  MAKE_GENERATOR(gen);\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  std::uniform_real_distribution<double> pos_dist{2., 10.};\n\n  const gr::Solutions::KerrSchild kerr_schild(1., {{0., 0., 0.}},\n                                              {{0., 0., 0.}});\n\n  const auto test_point =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&pos_dist), 1);\n\n  const auto& dij_imetric =\n      second_spatial_derivative_inverse_ks_metric(test_point);\n\n  const std::array<double, 3> array_point{test_point.get(0), test_point.get(1),\n                                          test_point.get(2)};\n  const double dx = 1e-4;\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b <= a; ++b) {\n      for (size_t j = 0; j < 3; ++j) {\n        for (size_t k = 0; k <= j; ++k) {\n          const auto di_imetric_helper =\n              [a, b, j](const std::array<double, 3>& point) {\n                const tnsr::I<double, 3> tensor_point(point);\n                const auto di_imetric_local =\n                    spatial_derivative_inverse_ks_metric(tensor_point);\n                return di_imetric_local.get(j, a, b);\n              };\n          const auto second_numerical_deriv_imetric_k =\n              numerical_derivative(di_imetric_helper, array_point, k, dx);\n          CHECK(dij_imetric.get(k, j, a, b) ==\n                local_approx(second_numerical_deriv_imetric_k));\n        }\n      }\n    }\n  }\n}\n\nvoid test_second_derivative_metric() {\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  std::uniform_real_distribution<double> pos_dist{2., 10.};\n  const gr::Solutions::KerrSchild kerr_schild(1., {{0., 0., 0.}},\n                                              {{0., 0., 0.}});\n\n  const auto test_point =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&pos_dist), 1);\n  const auto kerr_schild_quantities = kerr_schild.variables(\n      test_point, 0.,\n      tmpl::list<gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                 gr::Tags::SpatialMetric<double, Dim>,\n                 gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>{});\n  const auto metric = gr::spacetime_metric(\n      get<gr::Tags::Lapse<double>>(kerr_schild_quantities),\n      get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities),\n      get<gr::Tags::SpatialMetric<double, Dim>>(kerr_schild_quantities));\n\n  const auto& di_imetric = spatial_derivative_inverse_ks_metric(test_point);\n  const auto& dij_imetric =\n      second_spatial_derivative_inverse_ks_metric(test_point);\n  const auto di_metric = spatial_derivative_ks_metric(metric, di_imetric);\n  const auto& dij_metric = second_spatial_derivative_metric(\n      metric, di_metric, di_imetric, dij_imetric);\n\n  const std::array<double, 3> array_point{test_point.get(0), test_point.get(1),\n                                          test_point.get(2)};\n  const double dx = 1e-4;\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b <= a; ++b) {\n      for (size_t j = 0; j < 3; ++j) {\n        for (size_t k = 0; k <= j; ++k) {\n          const auto di_metric_helper = [&kerr_schild, j, a,\n                                         b](const std::array<double, 3>&\n                                                point) {\n            const tnsr::I<double, 3> tensor_point(point);\n            const auto kerr_schild_quantities_local = kerr_schild.variables(\n                tensor_point, 0.,\n                tmpl::list<\n                    gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                    gr::Tags::SpatialMetric<double, Dim>,\n                    gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>{});\n            const auto metric_local = gr::spacetime_metric(\n                get<gr::Tags::Lapse<double>>(kerr_schild_quantities_local),\n                get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities_local),\n                get<gr::Tags::SpatialMetric<double, Dim>>(\n                    kerr_schild_quantities_local));\n            const auto di_imetric_local =\n                spatial_derivative_inverse_ks_metric(tensor_point);\n            const auto di_metric_local =\n                spatial_derivative_ks_metric(metric_local, di_imetric_local);\n            return di_metric_local.get(j, a, b);\n          };\n\n          const auto second_numerical_deriv_metric_k =\n              numerical_derivative(di_metric_helper, array_point, k, dx);\n          CHECK(dij_metric.get(k, j, a, b) ==\n                local_approx(second_numerical_deriv_metric_k));\n        }\n      }\n    }\n  }\n}\n\nvoid test_derivative_christoffel() {\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  std::uniform_real_distribution<double> pos_dist{2., 10.};\n\n  const gr::Solutions::KerrSchild kerr_schild(1., {{0., 0., 0.}},\n                                              {{0., 0., 0.}});\n\n  const auto test_point =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&pos_dist), 1);\n  const auto kerr_schild_quantities = kerr_schild.variables(\n      test_point, 0.,\n      tmpl::list<gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                 gr::Tags::SpatialMetric<double, Dim>,\n                 gr::Tags::InverseSpatialMetric<double, Dim>,\n                 gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>{});\n  const auto metric = gr::spacetime_metric(\n      get<gr::Tags::Lapse<double>>(kerr_schild_quantities),\n      get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities),\n      get<gr::Tags::SpatialMetric<double, Dim>>(kerr_schild_quantities));\n  const auto inverse_metric = gr::inverse_spacetime_metric(\n      get<gr::Tags::Lapse<double>>(kerr_schild_quantities),\n      get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities),\n      get<gr::Tags::InverseSpatialMetric<double, Dim>>(kerr_schild_quantities));\n\n  const auto& di_imetric = spatial_derivative_inverse_ks_metric(test_point);\n  const auto& dij_imetric =\n      second_spatial_derivative_inverse_ks_metric(test_point);\n  const auto di_metric = spatial_derivative_ks_metric(metric, di_imetric);\n  const auto& dij_metric = second_spatial_derivative_metric(\n      metric, di_metric, di_imetric, dij_imetric);\n  const auto di_christoffel = spatial_derivative_christoffel(\n      di_metric, dij_metric, inverse_metric, di_imetric);\n\n  const std::array<double, 3> array_point{test_point.get(0), test_point.get(1),\n                                          test_point.get(2)};\n  const double dx = 1e-4;\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b < 4; ++b) {\n      for (size_t c = 0; c < 4; ++c) {\n        for (size_t j = 0; j < 3; ++j) {\n          const auto christoffel_helper =\n              [&kerr_schild, a, b, c](const std::array<double, 3>& point) {\n                const tnsr::I<double, 3> tensor_point(point);\n                const auto kerr_schild_quantities_local = kerr_schild.variables(\n                    tensor_point, 0.,\n                    tmpl::list<gr::Tags::SpacetimeChristoffelSecondKind<\n                        double, Dim>>{});\n                const auto& christoffel_local =\n                    get<gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>(\n                        kerr_schild_quantities_local);\n                return christoffel_local.get(a, b, c);\n              };\n\n          const auto numerical_deriv_christoffel_j =\n              numerical_derivative(christoffel_helper, array_point, j, dx);\n          CHECK(di_christoffel.get(j, a, b, c) ==\n                local_approx(numerical_deriv_christoffel_j));\n        }\n      }\n    }\n  }\n}\n\nvoid test_derivative_contracted_christoffel() {\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  std::uniform_real_distribution<double> pos_dist{2., 10.};\n\n  const gr::Solutions::KerrSchild kerr_schild(1., {{0., 0., 0.}},\n                                              {{0., 0., 0.}});\n\n  const auto test_point =\n      make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&pos_dist), 1);\n  const auto di_contracted_christoffel =\n      spatial_derivative_ks_contracted_christoffel(test_point);\n\n  const std::array<double, 3> array_point{test_point.get(0), test_point.get(1),\n                                          test_point.get(2)};\n  const double dx = 1e-4;\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t j = 0; j < 3; ++j) {\n      const auto contracted_christoffel_helper =\n          [&kerr_schild, a](const std::array<double, 3>& point) {\n            const tnsr::I<double, 3> tensor_point(point);\n            const auto kerr_schild_quantities_local = kerr_schild.variables(\n                tensor_point, 0.,\n                tmpl::list<\n                    gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                    gr::Tags::SpatialMetric<double, Dim>,\n                    gr::Tags::InverseSpatialMetric<double, Dim>,\n                    gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>{});\n            const auto imetric_local = gr::inverse_spacetime_metric(\n                get<gr::Tags::Lapse<double>>(kerr_schild_quantities_local),\n                get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities_local),\n                get<gr::Tags::InverseSpatialMetric<double, Dim>>(\n                    kerr_schild_quantities_local));\n            const auto& christoffel_local =\n                get<gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>(\n                    kerr_schild_quantities_local);\n            const auto contracted_christoffel_local =\n                trace_last_indices(christoffel_local, imetric_local);\n            return contracted_christoffel_local.get(a);\n          };\n      const auto numerical_deriv_contracted_christoffel_j =\n          numerical_derivative(contracted_christoffel_helper, array_point, j,\n                               dx);\n      CHECK(di_contracted_christoffel.get(j, a) ==\n            local_approx(numerical_deriv_contracted_christoffel_j));\n    }\n  }\n}\n\n// All the derivatives are checked against finite difference calculations\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.CurvedScalarWave.KerrSchildDerivatives\",\n    \"[Unit][Evolution]\") {\n  test_derivative_inverse_metric();\n  test_derivative_metric();\n  test_second_derivative_inverse_metric();\n  test_second_derivative_metric();\n  test_derivative_christoffel();\n  test_derivative_contracted_christoffel();\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/Test_PunctureField.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace CurvedScalarWave {\nnamespace {\n\nusing deriv_psi_tag =\n    ::Tags::deriv<Tags::Psi, tmpl::size_t<3>, Frame::Inertial>;\nusing puncture_vars =\n    Variables<tmpl::list<Tags::Psi, ::Tags::dt<Tags::Psi>, deriv_psi_tag>>;\n\nstd::array<tnsr::I<double, 3>, 3> get_circular_orbit_pos_vel_acc(\n    const double orbit_radius, const double time) {\n  const double angular_velocity = 1. / (sqrt(orbit_radius) * orbit_radius);\n  tnsr::I<double, 3> position{{orbit_radius * cos(angular_velocity * time),\n                               orbit_radius * sin(angular_velocity * time),\n                               0.}};\n  tnsr::I<double, 3> velocity{\n      {-angular_velocity * orbit_radius * sin(angular_velocity * time),\n       angular_velocity * orbit_radius * cos(angular_velocity * time), 0.}};\n  tnsr::I<double, 3> acceleration{\n      {-square(angular_velocity) * orbit_radius * cos(angular_velocity * time),\n       -square(angular_velocity) * orbit_radius * sin(angular_velocity * time),\n       0.}};\n  return std::array<tnsr::I<double, 3>, 3>{\n      {std::move(position), std::move(velocity), std::move(acceleration)}};\n}\n\n// tests that the puncture field corresponds to a circular orbit with angular\n// velocity = orbit_radius^(-3/2)\nvoid test_circular_orbit() {\n  MAKE_GENERATOR(gen);\n  // sample 100 random points around the worldtube\n  const double orbit_radius = 7.;\n  const double orbit_speed = pow(orbit_radius, -1.5);\n  const size_t num_points = 100;\n  std::uniform_real_distribution dist_around_wt(-3., 3.);\n  auto sample_points =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist_around_wt),\n          DataVector(num_points));\n  sample_points.get(0) += orbit_radius;\n  const double time_0 = 0.;\n  const double time_1 = 20.;\n\n  const auto [position_t0, velocity_t0, acceleration_t0] =\n      get_circular_orbit_pos_vel_acc(orbit_radius, time_0);\n  const auto [position_t1, velocity_t1, acceleration_t1] =\n      get_circular_orbit_pos_vel_acc(orbit_radius, time_1);\n  for (size_t order = 0; order <= 1; ++order) {\n    CAPTURE(order);\n    puncture_vars puncture_t0{num_points};\n    Worldtube::puncture_field(make_not_null(&puncture_t0), sample_points,\n                              position_t0, velocity_t0, acceleration_t0, 1.,\n                              order);\n    // rotate the sample points and check that the values don't change\n    tnsr::I<DataVector, 3, Frame::Inertial> sample_points_rotated(num_points,\n                                                                  0.);\n    sample_points_rotated.get(0) =\n        sample_points.get(0) * cos(orbit_speed * time_1) -\n        sample_points.get(1) * sin(orbit_speed * time_1);\n    sample_points_rotated.get(1) =\n        sample_points.get(0) * sin(orbit_speed * time_1) +\n        sample_points.get(1) * cos(orbit_speed * time_1);\n    sample_points_rotated.get(2) = sample_points.get(2);\n    puncture_vars puncture_t1{num_points};\n    Worldtube::puncture_field(make_not_null(&puncture_t1),\n                              sample_points_rotated, position_t1, velocity_t1,\n                              acceleration_t1, 1., order);\n    Approx local_approx = Approx::custom().epsilon(1.e-11).scale(1.);\n    CHECK_ITERABLE_CUSTOM_APPROX(get<Tags::Psi>(puncture_t0).get(),\n                                 get<Tags::Psi>(puncture_t1).get(),\n                                 local_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(get<::Tags::dt<Tags::Psi>>(puncture_t0).get(),\n                                 get<::Tags::dt<Tags::Psi>>(puncture_t1).get(),\n                                 local_approx);\n\n    // check that the spatial derivative also gets rotated\n    const auto& di_psi = get<deriv_psi_tag>(puncture_t0);\n    tnsr::i<DataVector, 3, Frame::Inertial> di_psi_rotated(num_points);\n    di_psi_rotated.get(0) = di_psi.get(0) * cos(orbit_speed * time_1) -\n                            di_psi.get(1) * sin(orbit_speed * time_1);\n    di_psi_rotated.get(1) = di_psi.get(0) * sin(orbit_speed * time_1) +\n                            di_psi.get(1) * cos(orbit_speed * time_1);\n    di_psi_rotated.get(2) = di_psi.get(2);\n    CHECK_ITERABLE_CUSTOM_APPROX(di_psi_rotated,\n                                 get<deriv_psi_tag>(puncture_t1), local_approx);\n  }\n}\n\n// tests the derivative of the puncture field against a finite difference\n// calculation\nvoid test_derivative() {\n  MAKE_GENERATOR(gen);\n  Approx local_approx = Approx::custom().epsilon(1e-9).scale(1.0);\n  std::uniform_real_distribution<double> theta_dist{0, M_PI};\n  std::uniform_real_distribution<double> phi_dist{0, 2 * M_PI};\n  std::uniform_real_distribution<double> pos_dist{2., 10.};\n  std::uniform_real_distribution<double> vel_acc_dist{-0.1, 0.1};\n\n  const double wt_radius = 0.1;\n  for (size_t order = 0; order <= 1; ++order) {\n    CAPTURE(order);\n    const auto random_position =\n        make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n            make_not_null(&gen), make_not_null(&pos_dist), 1);\n    const auto random_velocity =\n        make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n            make_not_null(&gen), make_not_null(&vel_acc_dist), 1);\n    const auto random_acceleration =\n        make_with_random_values<tnsr::I<double, 3, Frame::Inertial>>(\n            make_not_null(&gen), make_not_null(&vel_acc_dist), 1);\n    const auto helper_func = [&random_position, &random_velocity,\n                              &random_acceleration,\n                              &order](const std::array<double, 3>& point) {\n      tnsr::I<DataVector, 3, Frame::Inertial> tensor_point(size_t(1));\n      tensor_point.get(0) = point.at(0);\n      tensor_point.get(1) = point.at(1);\n      tensor_point.get(2) = point.at(2);\n      puncture_vars singular_field{1};\n      Worldtube::puncture_field(make_not_null(&singular_field), tensor_point,\n                                random_position, random_velocity,\n                                random_acceleration, 1., order);\n      return get<Tags::Psi>(singular_field).get()[0];\n    };\n    for (size_t i = 0; i < 20; ++i) {\n      const auto theta = theta_dist(gen);\n      const auto phi = phi_dist(gen);\n      std::array<double, 3> test_point{wt_radius * sin(theta) * cos(phi),\n                                       wt_radius * sin(theta) * sin(phi),\n                                       wt_radius * cos(theta)};\n      tnsr::I<DataVector, 3, Frame::Inertial> tensor_point(size_t(1));\n      for (size_t j = 0; j < 3; ++j) {\n        gsl::at(test_point, j) -= random_position.get(j);\n        tensor_point.get(j)[0] = test_point.at(j);\n      }\n      const double dx = 1e-4;\n      puncture_vars singular_field{1};\n      Worldtube::puncture_field(make_not_null(&singular_field), tensor_point,\n                                random_position, random_velocity,\n                                random_acceleration, 1., order);\n      const auto& di_psi = get<deriv_psi_tag>(singular_field);\n      for (size_t j = 0; j < 3; ++j) {\n        const auto numerical_deriv_j =\n            numerical_derivative(helper_func, test_point, j, dx);\n        CHECK(di_psi.get(j)[0] == local_approx(numerical_deriv_j));\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.PunctureField\",\n                  \"[Unit][Evolution]\") {\n  test_circular_orbit();\n  test_derivative();\n}\n}  // namespace\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/Test_PunctureInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/InitialData/ZerothOrderPuncture.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace CurvedScalarWave {\nnamespace {\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.PunctureInitialData\",\n                  \"[Unit][Evolution]\") {\n  const std::array<double, 3> pos_array{{12., 13., 0.}};\n  const std::array<double, 3> vel_array{{0.1, 0.2, 0.}};\n  const double charge = 0.23;\n\n  const AnalyticData::ZerothOrderPuncture zeroth_order_puncture(\n      pos_array, vel_array, charge);\n  const auto copy = zeroth_order_puncture;\n  CHECK(zeroth_order_puncture == copy);\n  test_serialization(zeroth_order_puncture);\n\n  const tnsr::I<double, 3> pos(pos_array);\n  const tnsr::I<double, 3> vel(vel_array);\n\n  const std::uniform_real_distribution sample_dist(-100., 100.);\n  const size_t num_points = 1000;\n  MAKE_GENERATOR(gen);\n\n  const auto sample_coords =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), sample_dist, DataVector(num_points));\n\n  const auto vars = zeroth_order_puncture.variables(sample_coords, {});\n  auto sample_coords_centered = sample_coords;\n  for (size_t i = 0; i < 3; ++i) {\n    sample_coords_centered.get(i) -= pos.get(i);\n  }\n\n  // central black hole of mass 1\n  const gr::Solutions::KerrSchild ks{\n      1., {0., 0., 0.}, {0., 0., 0.}, {0., 0., 0.}};\n  const auto background_vars_christoffel = ks.variables(\n      pos, 0.,\n      tmpl::list<gr::Tags::SpacetimeChristoffelSecondKind<double, 3,\n                                                          Frame::Inertial>>{});\n  const auto& christoffel =\n      get<gr::Tags::SpacetimeChristoffelSecondKind<double, 3, Frame::Inertial>>(\n          background_vars_christoffel);\n\n  const auto background_vars =\n      ks.variables(sample_coords, 0.,\n                   tmpl::list<gr::Tags::Shift<DataVector, 3>,\n                              gr::Tags::Lapse<DataVector>>{});\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(background_vars);\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(background_vars);\n  const auto geodesic_acceleration =\n      gr::geodesic_acceleration(vel, christoffel);\n  Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                       ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                       ::Tags::deriv<CurvedScalarWave::Tags::Psi,\n                                     tmpl::size_t<3>, Frame::Inertial>>>\n      expected_puncture_field(num_points);\n\n  Worldtube::puncture_field_0(make_not_null(&expected_puncture_field),\n                              sample_coords_centered, pos, vel,\n                              geodesic_acceleration, 1.);\n  expected_puncture_field *= charge;\n\n  const auto shift_dot_phi = dot_product(shift, get<Tags::Phi<3>>(vars));\n\n  const auto dt_psi =\n      get(shift_dot_phi) - get(lapse) * get(get<Tags::Pi>(vars));\n\n  CHECK_ITERABLE_APPROX(get<Tags::Psi>(vars),\n                        get<Tags::Psi>(expected_puncture_field));\n  CHECK_ITERABLE_APPROX(\n      dt_psi, get(get<::Tags::dt<Tags::Psi>>(expected_puncture_field)));\n  const auto& di_psi =\n      get<::Tags::deriv<Tags::Psi, tmpl::size_t<3>, Frame::Inertial>>(\n          expected_puncture_field);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK_ITERABLE_APPROX(di_psi.get(i), get<Tags::Phi<3>>(vars).get(i));\n  }\n}\n}  // namespace\n}  // namespace CurvedScalarWave\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/Test_RadiusFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/RadiusFunctions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\nvoid test_smooth_broken_power_law_limits() {\n  const double radius_at_inf = 1.234;\n  const double rb = 43.45;\n  for (const double exp : {0., 0.01, 0.5, 1., 1.5, 2.456, 3., 4.}) {\n    for (const double delta : {0.01, 0.0345, 0.1}) {\n      CAPTURE(exp, delta);\n      const double r_left_1 = 1.1;\n      const double r_left_2 = 1.2;\n      const double v1 =\n          smooth_broken_power_law(r_left_1, exp, radius_at_inf, rb, delta);\n      const double v2 =\n          smooth_broken_power_law(r_left_2, exp, radius_at_inf, rb, delta);\n      const double left_exponent = log(v2 / v1) / log(r_left_2 / r_left_1);\n      CHECK(left_exponent == approx(exp));\n\n      const double r_right_1 = 100. * rb;\n      const double r_right_2 = 100. * rb + 0.1;\n      const double v1_const =\n          smooth_broken_power_law(r_right_1, exp, radius_at_inf, rb, delta);\n      const double v2_const =\n          smooth_broken_power_law(r_right_2, exp, radius_at_inf, rb, delta);\n      CHECK(v1_const == approx(radius_at_inf));\n      CHECK(v2_const == approx(radius_at_inf));\n    }\n  }\n}\n\ndouble central_difference_order8(const std::function<double(double)>& func,\n                                 const double x, const double h) {\n  return (3. * func(x - 4. * h) - 32. * func(x - 3. * h) +\n          168. * func(x - 2. * h) - 672. * func(x - h) + 672. * func(x + h) +\n          -168. * func(x + 2. * h) + 32. * func(x + 3. * h) -\n          3. * func(x + 4. * h)) /\n         (840. * h);\n}\n\nvoid test_smooth_broken_power_law_derivative() {\n  const double radius_at_inf = 1.234;\n  const double rb = 10.;\n\n  // the finite difference method carries an error of about 1e-10\n  const Approx custom_approx = Approx::custom().epsilon(1.e-10).scale(1.0);\n\n  for (const double exp : {0., 0.01, 0.5, 1., 1.5, 2.456, 3., 4.}) {\n    for (const double delta : {0.01, 0.0345, 0.1}) {\n      auto fd_func = [exp, radius_at_inf, rb, delta](double r) -> double {\n        return smooth_broken_power_law(r, exp, radius_at_inf, rb, delta);\n      };\n      // test for a wide range of radii: critical are the points around rb in\n      // the transition region and around rb + 1000 * delta, where the\n      // derivative automatically returns 0.\n      for (const double test_radius : {0.1, 5., rb - delta, rb, rb + delta, 15.,\n                                       rb + 999. * delta, rb + 1001. * delta}) {\n        CAPTURE(exp, delta, test_radius);\n        const double fd_derivative =\n            central_difference_order8(fd_func, test_radius, 1e-3);\n        const double analytical_derivative = smooth_broken_power_law_derivative(\n            test_radius, exp, radius_at_inf, rb, delta);\n        CHECK(fd_derivative == custom_approx(analytical_derivative));\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.RadiusFunctions\",\n                  \"[Unit][Evolution]\") {\n  test_smooth_broken_power_law_limits();\n  test_smooth_broken_power_law_derivative();\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/Test_SelfForce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SelfForce.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\n\nvoid test_self_force_acceleration() {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<double, 3> (*)(\n          const Scalar<double>&, const tnsr::i<double, 3>&,\n          const tnsr::I<double, 3>&, const double, const double,\n          const tnsr::AA<double, 3>&, const Scalar<double>&)>(\n          self_force_acceleration<3>),\n      \"SelfForce\", \"self_force_acceleration\", {{{-2.0, 2.0}}}, 1);\n}\n\nvoid test_self_force_per_mass() {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::A<double, 3> (*)(\n          const tnsr::a<double, 3>&, const tnsr::A<double, 3>&, const double,\n          const double, const tnsr::AA<double, 3>&)>(self_force_per_mass<3>),\n      \"SelfForce\", \"self_force_per_mass\", {{{-2.0, 2.0}}}, 1);\n}\n\nvoid test_dt_self_force_per_mass() {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::A<double, 3> (*)(\n          const tnsr::a<double, 3>&, const tnsr::a<double, 3>&,\n          const tnsr::A<double, 3>&, const tnsr::A<double, 3>&, const double,\n          const double, const tnsr::AA<double, 3>&,\n          const tnsr::AA<double, 3>&)>(dt_self_force_per_mass<3>),\n      \"SelfForce\", \"dt_self_force_per_mass\", {{{-2.0, 2.0}}}, 1);\n}\n\nvoid test_dt2_self_force_per_mass() {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::A<double, 3> (*)(\n          const tnsr::a<double, 3>&, const tnsr::a<double, 3>&,\n          const tnsr::a<double, 3>&, const tnsr::A<double, 3>&,\n          const tnsr::A<double, 3>&, const tnsr::A<double, 3>&, const double,\n          const double, const tnsr::AA<double, 3>&, const tnsr::AA<double, 3>&,\n          const tnsr::AA<double, 3>&)>(dt2_self_force_per_mass<3>),\n      \"SelfForce\", \"dt2_self_force_per_mass\", {{{-2.0, 2.0}}}, 1);\n}\n\nvoid test_Du_self_force_per_mass() {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::A<double, 3> (*)(\n          const tnsr::A<double, 3>&, const tnsr::A<double, 3>&,\n          const tnsr::A<double, 3>&, const tnsr::Abb<double, 3>&)>(\n          Du_self_force_per_mass<3>),\n      \"SelfForce\", \"Du_self_force_per_mass\", {{{-2.0, 2.0}}}, 1);\n}\n\nvoid test_dt_Du_self_force_per_mass() {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::A<double, 3> (*)(\n          const tnsr::A<double, 3>&, const tnsr::A<double, 3>&,\n          const tnsr::A<double, 3>&, const tnsr::A<double, 3>&,\n          const tnsr::A<double, 3>&, const tnsr::Abb<double, 3>&,\n          const tnsr::Abb<double, 3>&)>(dt_Du_self_force_per_mass<3>),\n      \"SelfForce\", \"dt_Du_self_force_per_mass\", {{{-2.0, 2.0}}}, 1);\n}\n\nvoid test_turn_on_function() {\n  pypp::check_with_random_values<1>(\n      static_cast<double (*)(const double time,\n                             const double turn_on_timescale)>(turn_on_function),\n      \"SelfForce\", \"turn_on_function\", {{{0., 2.0}}}, 1);\n}\n\nvoid test_turn_on_function_derivatives() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution dist(0.001, 10000.);\n  const double timescale = dist(generator);\n  const double time = dist(generator);\n  const std::array<double, 1> time_array{{time}};\n\n  const auto func_helper = [&timescale](const std::array<double, 1>& time_in) {\n    return turn_on_function(time_in[0], timescale);\n  };\n  const auto dt_func_helper =\n      [&timescale](const std::array<double, 1>& time_in) {\n        return dt_turn_on_function(time_in[0], timescale);\n      };\n\n  const double dt = 1e-5;\n  const Approx custom_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  const auto numerical_deriv_func =\n      numerical_derivative(func_helper, time_array, 0, dt);\n  CHECK(dt_turn_on_function(time, timescale) ==\n        custom_approx(numerical_deriv_func));\n\n  const auto numerical_deriv_dt_func =\n      numerical_derivative(dt_func_helper, time_array, 0, dt);\n  CHECK(dt2_turn_on_function(time, timescale) ==\n        custom_approx(numerical_deriv_dt_func));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CSW.Worldtube.SelfForce\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/CurvedScalarWave/Worldtube\"};\n  test_self_force_acceleration();\n  test_self_force_per_mass();\n  test_dt_self_force_per_mass();\n  test_dt2_self_force_per_mass();\n  test_Du_self_force_per_mass();\n  test_dt_Du_self_force_per_mass();\n  test_turn_on_function();\n  test_turn_on_function_derivatives();\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/PunctureField.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/SingletonActions/InitializeElementFacesGridCoordinates.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/CurvedScalarWave/Worldtube/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\nvoid test_excision_sphere_tag() {\n  INFO(\"test_excision_sphere_tag\");\n  const std::unique_ptr<DomainCreator<3>> shell =\n      std::make_unique<domain::creators::Sphere>(\n          1., 2., domain::creators::Sphere::Excision{}, 2_st, 4_st, true);\n  const auto shell_excision_sphere =\n      shell->create_domain().excision_spheres().at(\"ExcisionSphere\");\n\n  CHECK(Tags::ExcisionSphere<3>::create_from_options(shell, \"ExcisionSphere\") ==\n        shell_excision_sphere);\n  CHECK_THROWS_WITH(\n      Tags::ExcisionSphere<3>::create_from_options(shell,\n                                                   \"OtherExcisionSphere\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Specified excision sphere 'OtherExcisionSphere' not available. \"\n          \"Available excision spheres are: (ExcisionSphere)\"));\n}\n\nvoid test_initial_position_velocity_tag() {\n  INFO(\"test_initial_position_velocity_tag\");\n  const double orbital_radius = 7.;\n  const double angular_vel = 0.1;\n  const auto domain_creator =\n      TestHelpers::CurvedScalarWave::Worldtube::worldtube_binary_compact_object<\n          false>(orbital_radius, 0.2, angular_vel);\n  const tnsr::I<double, 3> expected_pos{{orbital_radius, 0., 0.}};\n  const tnsr::I<double, 3> expected_vel{{0., angular_vel * orbital_radius, 0.}};\n  CHECK(Tags::InitialPositionAndVelocity::create_from_options(\n            domain_creator, \"ExcisionSphereA\", 0.) ==\n        std::array<tnsr::I<double, 3>, 2>{{expected_pos, expected_vel}});\n}\n\nvoid test_compute_face_coordinates_grid() {\n  INFO(\"test_compute_face_coordinates_grid\");\n  static constexpr size_t Dim = 3;\n  ::TestHelpers::db::test_compute_tag<\n      Tags::FaceCoordinatesCompute<Dim, Frame::Grid, true>>(\"FaceCoordinates\");\n  ::TestHelpers::db::test_compute_tag<\n      Tags::FaceCoordinatesCompute<Dim, Frame::Grid, false>>(\"FaceCoordinates\");\n  ::TestHelpers::db::test_compute_tag<\n      Tags::FaceCoordinatesCompute<Dim, Frame::Inertial, true>>(\n      \"FaceCoordinates\");\n  ::TestHelpers::db::test_compute_tag<\n      Tags::FaceCoordinatesCompute<Dim, Frame::Inertial, false>>(\n      \"FaceCoordinates\");\n\n  for (const auto& initial_refinement : std::array<size_t, 2>{{0, 1}}) {\n    CAPTURE(initial_refinement);\n    const auto basis = Spectral::Basis::Legendre;\n    const auto quadrature = Spectral::Quadrature::GaussLobatto;\n    // we create two shells with different resolutions\n    const size_t extents_1 = 5;\n    const size_t extents_2 = 7;\n    const domain::creators::Sphere shell_1{1.5,\n                                           3.,\n                                           domain::creators::Sphere::Excision{},\n                                           initial_refinement,\n                                           extents_1,\n                                           true};\n    const domain::creators::Sphere shell_2{1.5,\n                                           3.,\n                                           domain::creators::Sphere::Excision{},\n                                           initial_refinement,\n                                           extents_2,\n                                           true};\n    const auto& initial_extents_1 = shell_1.initial_extents();\n    const auto& initial_extents_2 = shell_2.initial_extents();\n\n    const auto domain = shell_1.create_domain();\n    const auto& blocks = domain.blocks();\n    const auto& excision_sphere =\n        domain.excision_spheres().at(\"ExcisionSphere\");\n    const auto& initial_refinements = shell_1.initial_refinement_levels();\n    const auto element_ids = initial_element_ids(initial_refinements);\n\n    // this worldtube tag should hold the same coordinates of all elements in a\n    // map so we use it to check\n    std::unordered_map<ElementId<Dim>, tnsr::I<DataVector, Dim, Frame::Grid>>\n        all_faces_grid_coords_1{};\n    std::unordered_map<ElementId<Dim>, tnsr::I<DataVector, Dim, Frame::Grid>>\n        all_faces_grid_coords_2{};\n    Initialization::InitializeElementFacesGridCoordinates<Dim>::apply(\n        make_not_null(&all_faces_grid_coords_1), initial_extents_1,\n        initial_refinements, quadrature, domain, excision_sphere);\n    Initialization::InitializeElementFacesGridCoordinates<Dim>::apply(\n        make_not_null(&all_faces_grid_coords_2), initial_extents_2,\n        initial_refinements, quadrature, domain, excision_sphere);\n\n    for (const auto& element_id : element_ids) {\n      const auto element = domain::create_initial_element(element_id, blocks,\n                                                          initial_refinements);\n      const auto& my_block = blocks.at(element_id.block_id());\n      const ElementMap element_map(\n          element_id, my_block.stationary_map().get_to_grid_frame());\n      const auto mesh_1 = domain::create_initial_mesh(\n          initial_extents_1, element, basis, quadrature);\n      const auto grid_coords_1 = element_map(logical_coordinates(mesh_1));\n      const auto mesh_2 = domain::create_initial_mesh(\n          initial_extents_2, element, basis, quadrature);\n      const auto grid_coords_2 = element_map(logical_coordinates(mesh_2));\n\n      auto box =\n          db::create<db::AddSimpleTags<\n                         Tags::ExcisionSphere<Dim>, domain::Tags::Element<Dim>,\n                         domain::Tags::Coordinates<Dim, Frame::Grid>,\n                         domain::Tags::Mesh<Dim>>,\n                     db::AddComputeTags<\n                         Tags::FaceCoordinatesCompute<Dim, Frame::Grid, true>>>(\n              excision_sphere, element, grid_coords_1, mesh_1);\n\n      const auto centered_face_coordinates_1 =\n          db::get<Tags::FaceCoordinates<Dim, Frame::Grid, true>>(box);\n      if (all_faces_grid_coords_1.count(element_id)) {\n        CHECK(centered_face_coordinates_1.has_value());\n        CHECK_ITERABLE_APPROX(centered_face_coordinates_1.value(),\n                              all_faces_grid_coords_1.at(element_id));\n      } else {\n        CHECK(not centered_face_coordinates_1.has_value());\n      }\n\n      // we mutate to the differently refined grid, simulating p-refinement\n      ::Initialization::mutate_assign<\n          tmpl::list<domain::Tags::Mesh<Dim>,\n                     domain::Tags::Coordinates<Dim, Frame::Grid>>>(\n          make_not_null(&box), mesh_2, grid_coords_2);\n\n      const auto centered_face_coordinates_2 =\n          db::get<Tags::FaceCoordinates<Dim, Frame::Grid, true>>(box);\n      if (all_faces_grid_coords_2.count(element_id)) {\n        // p-refinement should not change which elements are abutting i.e. have\n        // a value\n        CHECK(centered_face_coordinates_1.has_value());\n        CHECK(centered_face_coordinates_2.has_value());\n        CHECK_ITERABLE_APPROX(centered_face_coordinates_2.value(),\n                              all_faces_grid_coords_2.at(element_id));\n      } else {\n        CHECK(not centered_face_coordinates_1.has_value());\n        CHECK(not centered_face_coordinates_2.has_value());\n      }\n    }\n  }\n}\n\nvoid test_compute_face_coordinates() {\n  INFO(\"test_compute_face_coordinates\");\n  static constexpr size_t Dim = 3;\n  const auto domain_creator =\n      TestHelpers::CurvedScalarWave::Worldtube::worldtube_binary_compact_object<\n          false>(7., 0.2, pow(7, -1.5));\n  const double initial_time = 0.;\n  auto domain = domain_creator->create_domain();\n  const auto excision_sphere = domain.excision_spheres().at(\"ExcisionSphereA\");\n  const auto& blocks = domain.blocks();\n  const auto initial_extents = domain_creator->initial_extents();\n  const auto initial_refinements = domain_creator->initial_refinement_levels();\n  const auto element_ids = initial_element_ids(initial_refinements);\n  const auto basis = Spectral::Basis::Legendre;\n  const auto quadrature = Spectral::Quadrature::GaussLobatto;\n  std::unordered_map<ElementId<Dim>, tnsr::I<DataVector, Dim, Frame::Grid>>\n      all_faces_grid_coords{};\n  Initialization::InitializeElementFacesGridCoordinates<Dim>::apply(\n      make_not_null(&all_faces_grid_coords), initial_extents,\n      initial_refinements, quadrature, domain, excision_sphere);\n  for (const auto& element_id : element_ids) {\n    const auto element =\n        domain::create_initial_element(element_id, blocks, initial_refinements);\n    const auto mesh = domain::create_initial_mesh(initial_extents, element,\n                                                  basis, quadrature);\n    const auto& my_block = blocks.at(element_id.block_id());\n    const ElementMap element_map(\n        element_id,\n        my_block.moving_mesh_logical_to_grid_map().get_to_grid_frame());\n    const auto grid_coords = element_map(logical_coordinates(mesh));\n\n    auto box = db::create<\n        db::AddSimpleTags<::Tags::Time, Tags::ExcisionSphere<3>,\n                          domain::Tags::FunctionsOfTimeInitialize,\n                          domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                          domain::Tags::Coordinates<Dim, Frame::Grid>,\n                          ::domain::CoordinateMaps::Tags::CoordinateMap<\n                              Dim, Frame::Grid, Frame::Inertial>>,\n        db::AddComputeTags<\n            Tags::ParticlePositionVelocityCompute<3>,\n            ::domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<\n                ::domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                              Frame::Inertial>>,\n            ::domain::Tags::InertialFromGridCoordinatesCompute<Dim>,\n            Tags::FaceCoordinatesCompute<3, Frame::Grid, true>,\n            Tags::FaceCoordinatesCompute<3, Frame::Grid, false>,\n            Tags::FaceCoordinatesCompute<3, Frame::Inertial, true>,\n            Tags::FaceCoordinatesCompute<3, Frame::Inertial, false>>>(\n        initial_time, excision_sphere, domain_creator->functions_of_time(),\n        element, mesh, grid_coords,\n        my_block.moving_mesh_grid_to_inertial_map().get_clone());\n    const auto centered_grid_1 =\n        db::get<Tags::FaceCoordinates<3, Frame::Grid, true>>(box);\n    const auto uncentered_grid_1 =\n        db::get<Tags::FaceCoordinates<3, Frame::Grid, false>>(box);\n    const auto centered_inertial_1 =\n        db::get<Tags::FaceCoordinates<3, Frame::Inertial, true>>(box);\n    const auto uncentered_inertial_1 =\n        db::get<Tags::FaceCoordinates<3, Frame::Inertial, false>>(box);\n    if (all_faces_grid_coords.count(element_id)) {\n      CHECK(centered_grid_1.has_value());\n      CHECK(uncentered_grid_1.has_value());\n      CHECK(centered_inertial_1.has_value());\n      CHECK(uncentered_inertial_1.has_value());\n\n      CHECK_ITERABLE_APPROX(centered_grid_1.value(),\n                            all_faces_grid_coords.at(element_id));\n      for (size_t i = 0; i < 3; ++i) {\n        CHECK_ITERABLE_APPROX(centered_grid_1->get(i),\n                              centered_inertial_1->get(i));\n        CHECK_ITERABLE_APPROX(uncentered_grid_1->get(i),\n                              uncentered_inertial_1->get(i));\n        CHECK_ITERABLE_APPROX(\n            centered_grid_1->get(i),\n            uncentered_grid_1->get(i) - excision_sphere.center().get(i));\n      }\n    } else {\n      CHECK(not centered_grid_1.has_value());\n      CHECK(not uncentered_grid_1.has_value());\n      CHECK(not centered_inertial_1.has_value());\n      CHECK(not uncentered_inertial_1.has_value());\n    }\n\n    const double new_time = 5.;\n    ::Initialization::mutate_assign<tmpl::list<::Tags::Time>>(\n        make_not_null(&box), new_time);\n\n    const auto centered_grid_2 =\n        db::get<Tags::FaceCoordinates<3, Frame::Grid, true>>(box);\n    const auto uncentered_grid_2 =\n        db::get<Tags::FaceCoordinates<3, Frame::Grid, false>>(box);\n    const auto centered_inertial_2 =\n        db::get<Tags::FaceCoordinates<3, Frame::Inertial, true>>(box);\n    const auto uncentered_inertial_2 =\n        db::get<Tags::FaceCoordinates<3, Frame::Inertial, false>>(box);\n    if (all_faces_grid_coords.count(element_id)) {\n      CHECK(centered_grid_2.has_value());\n      CHECK(uncentered_grid_2.has_value());\n      CHECK(centered_inertial_2.has_value());\n      CHECK(uncentered_grid_2.has_value());\n\n      CHECK_ITERABLE_APPROX(centered_grid_2.value(), centered_grid_1.value());\n      CHECK_ITERABLE_APPROX(uncentered_grid_2.value(),\n                            uncentered_grid_1.value());\n      const auto& inertial_particle_position =\n          db::get<Tags::ParticlePositionVelocity<Dim>>(box).at(0);\n      const auto& grid_to_inertial_map =\n          db::get<::domain::CoordinateMaps::Tags::CoordinateMap<\n              Dim, Frame::Grid, Frame::Inertial>>(box);\n      const auto& functions_of_time =\n          db::get<domain::Tags::FunctionsOfTime>(box);\n      CHECK_ITERABLE_APPROX(uncentered_inertial_2.value(),\n                            grid_to_inertial_map(uncentered_grid_2.value(),\n                                                 new_time, functions_of_time));\n      for (size_t i = 0; i < Dim; ++i) {\n        CHECK_ITERABLE_APPROX(\n            centered_inertial_2->get(i),\n            uncentered_inertial_2->get(i) - inertial_particle_position.get(i));\n      }\n    } else {\n      CHECK(not centered_grid_2.has_value());\n      CHECK(not uncentered_grid_2.has_value());\n      CHECK(not centered_inertial_2.has_value());\n      CHECK(not uncentered_inertial_2.has_value());\n    }\n  }\n}\n\nvoid test_particle_position_velocity_compute() {\n  INFO(\"test_particle_position_velocity_compute\");\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  const double orbit_radius = 9.;\n  const double angular_velocity = 1. / (sqrt(orbit_radius) * orbit_radius);\n  const auto domain_creator =\n      TestHelpers::CurvedScalarWave::Worldtube::worldtube_binary_compact_object<\n          false>(orbit_radius, 0.2, angular_velocity);\n  const double initial_time = 0.;\n  auto domain = domain_creator->create_domain();\n  const auto excision_sphere = domain.excision_spheres().at(\"ExcisionSphereA\");\n  std::uniform_real_distribution<> time_dist(0., 10.);\n  auto box =\n      db::create<db::AddSimpleTags<::Tags::Time, Tags::ExcisionSphere<3>,\n                                   domain::Tags::FunctionsOfTimeInitialize>,\n                 db::AddComputeTags<Tags::ParticlePositionVelocityCompute<3>>>(\n          initial_time, excision_sphere, domain_creator->functions_of_time());\n  const auto get_position_velocity_circular = [orbit_radius, angular_velocity](\n                                                  const double time) {\n    tnsr::I<double, Dim> position{{orbit_radius * cos(angular_velocity * time),\n                                   orbit_radius * sin(angular_velocity * time),\n                                   0.}};\n    tnsr::I<double, Dim> velocity{\n        {-orbit_radius * angular_velocity * sin(angular_velocity * time),\n         orbit_radius * angular_velocity * cos(angular_velocity * time), 0.}};\n    return std::array<tnsr::I<double, Dim>, 2>{std::move(position),\n                                               std::move(velocity)};\n  };\n  // There is an error introduced here which comes from the\n  // quaternion function of time integrating the orbit\n  Approx apprx = Approx::custom().epsilon(1e-10).scale(1.0);\n  for (size_t i = 0; i < 100; ++i) {\n    const double random_time = time_dist(gen);\n    ::Initialization::mutate_assign<tmpl::list<::Tags::Time>>(\n        make_not_null(&box), random_time);\n    const auto& position_velocity =\n        db::get<Tags::ParticlePositionVelocity<Dim>>(box);\n    const auto position_velocity_circular =\n        get_position_velocity_circular(random_time);\n    CHECK_ITERABLE_CUSTOM_APPROX(position_velocity.at(0),\n                                 position_velocity_circular.at(0), apprx);\n    CHECK_ITERABLE_CUSTOM_APPROX(position_velocity.at(1),\n                                 position_velocity_circular.at(1), apprx);\n  }\n}\n\nvoid test_evolved_particle_position_velocity_compute() {\n  INFO(\"test_evolved_particle_position_velocity_compute\");\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const DataVector used_for_size(1);\n  const auto evolved_pos = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      make_not_null(&gen), dist, used_for_size);\n  const auto evolved_vel = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      make_not_null(&gen), dist, used_for_size);\n  auto box = db::create<\n      db::AddSimpleTags<Tags::EvolvedPosition<Dim>, Tags::EvolvedVelocity<Dim>>,\n      db::AddComputeTags<Tags::EvolvedParticlePositionVelocityCompute<3>>>(\n      evolved_pos, evolved_vel);\n  const auto& pos_vel = db::get<Tags::ParticlePositionVelocity<Dim>>(box);\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(pos_vel[0].get(i) == evolved_pos.get(i)[0]);\n    CHECK(pos_vel[1].get(i) == evolved_vel.get(i)[0]);\n  }\n}\n\nvoid test_constraint_gammas_compute() {\n  INFO(\"test_constraint_gammas_compute\");\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  const std::uniform_real_distribution<> dist(-1., 1.);\n  const auto pos = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto vel = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n\n  const size_t coord_size = 100;\n  const auto coords = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      make_not_null(&gen), dist, DataVector(coord_size));\n\n  auto box = db::create<\n      db::AddSimpleTags<Tags::ParticlePositionVelocity<Dim>,\n                        domain::Tags::Coordinates<Dim, Frame::Inertial>>,\n      db::AddComputeTags<Tags::ConstraintGamma1Compute,\n                         Tags::ConstraintGamma2Compute>>(\n      std::array<tnsr::I<double, Dim>, 2>{pos, vel}, coords);\n\n  const auto& gamma1 = db::get<CurvedScalarWave::Tags::ConstraintGamma1>(box);\n  CHECK(get(gamma1) == DataVector(coord_size, 0.));\n\n  auto centered_coords = coords;\n  for (size_t i = 0; i < 3; ++i) {\n    centered_coords.get(i) -= pos.get(i);\n  }\n  const auto centered_radii = magnitude(centered_coords);\n  const auto gamma2_expected =\n      10. * exp(-square(0.1 * get(centered_radii))) + 1e-3;\n  const auto& gamma2 = db::get<CurvedScalarWave::Tags::ConstraintGamma2>(box);\n  CHECK_ITERABLE_APPROX(gamma2_expected, get(gamma2));\n}\n\nvoid test_geodesic_acceleration_compute() {\n  INFO(\"test_geodesic_acceleration_compute\");\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(1., 100.);\n  const auto random_position = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const auto random_velocity = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), dist, 1);\n  const gr::Solutions::KerrSchild kerr_schild(0.1, make_array(0.5, 0.1, 0.2),\n                                              make_array(0.3, 0.1, 0.2));\n  auto box =\n      db::create<db::AddSimpleTags<Tags::ParticlePositionVelocity<Dim>,\n                                   CurvedScalarWave::Tags::BackgroundSpacetime<\n                                       gr::Solutions::KerrSchild>>,\n                 db::AddComputeTags<Tags::GeodesicAccelerationCompute<Dim>>>(\n          std::array<tnsr::I<double, Dim>, 2>{\n              {random_position, random_velocity}},\n          kerr_schild);\n  const auto christoffel = get<\n      gr::Tags::SpacetimeChristoffelSecondKind<double, Dim, Frame::Inertial>>(\n      kerr_schild.variables(random_position, 0.,\n                            tmpl::list<gr::Tags::SpacetimeChristoffelSecondKind<\n                                double, Dim, Frame::Inertial>>{}));\n  const auto expected_acceleration =\n      gr::geodesic_acceleration(random_velocity, christoffel);\n  CHECK_ITERABLE_APPROX(db::get<Tags::GeodesicAcceleration<Dim>>(box),\n                        expected_acceleration);\n}\n\nvoid test_background_quantities_compute() {\n  INFO(\"test_background_quantities_compute\");\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n\n  // the velocity has to be less than c or the dilation factor is undefined\n  std::uniform_real_distribution<> pos_dist(2., 10.);\n  std::uniform_real_distribution<> vel_dist(-0.2, 0.2);\n\n  const auto random_position = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), pos_dist, 1);\n  const auto random_velocity = make_with_random_values<tnsr::I<double, Dim>>(\n      make_not_null(&gen), vel_dist, 1);\n  const gr::Solutions::KerrSchild kerr_schild(1., make_array(0.3, 0.1, 0.2),\n                                              make_array(0.3, 0.1, 0.2));\n  auto box =\n      db::create<db::AddSimpleTags<Tags::ParticlePositionVelocity<Dim>,\n                                   CurvedScalarWave::Tags::BackgroundSpacetime<\n                                       gr::Solutions::KerrSchild>>,\n                 db::AddComputeTags<Tags::BackgroundQuantitiesCompute<Dim>>>(\n          std::array<tnsr::I<double, Dim>, 2>{\n              {random_position, random_velocity}},\n          kerr_schild);\n  const auto kerr_schild_quantities = kerr_schild.variables(\n      random_position, 0.,\n      tmpl::list<gr::Tags::Lapse<double>, gr::Tags::Shift<double, Dim>,\n                 gr::Tags::SpatialMetric<double, Dim>,\n                 gr::Tags::InverseSpatialMetric<double, Dim>,\n                 gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>{});\n  const auto expected_metric = gr::spacetime_metric(\n      get<gr::Tags::Lapse<double>>(kerr_schild_quantities),\n      get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities),\n      get<gr::Tags::SpatialMetric<double, Dim>>(kerr_schild_quantities));\n  const auto expected_inverse_metric = gr::inverse_spacetime_metric(\n      get<gr::Tags::Lapse<double>>(kerr_schild_quantities),\n      get<gr::Tags::Shift<double, Dim>>(kerr_schild_quantities),\n      get<gr::Tags::InverseSpatialMetric<double, Dim>>(kerr_schild_quantities));\n  const auto& expected_christoffel =\n      get<gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>(\n          kerr_schild_quantities);\n  const auto expected_trace_christoffel =\n      tenex::evaluate<ti::C>(expected_inverse_metric(ti::A, ti::B) *\n                             expected_christoffel(ti::C, ti::a, ti::b));\n\n  const auto& background_quantities =\n      db::get<Tags::BackgroundQuantities<Dim>>(box);\n\n  const auto& metric =\n      get<gr::Tags::SpacetimeMetric<double, Dim>>(background_quantities);\n  const auto& inverse_metric =\n      get<gr::Tags::InverseSpacetimeMetric<double, Dim>>(background_quantities);\n  const auto& christoffel =\n      get<gr::Tags::SpacetimeChristoffelSecondKind<double, Dim>>(\n          background_quantities);\n  const auto& trace_christoffel =\n      get<gr::Tags::TraceSpacetimeChristoffelSecondKind<double, Dim>>(\n          background_quantities);\n\n  CHECK_ITERABLE_APPROX(metric, expected_metric);\n  CHECK_ITERABLE_APPROX(inverse_metric, expected_inverse_metric);\n  CHECK_ITERABLE_APPROX(christoffel, expected_christoffel);\n  CHECK_ITERABLE_APPROX(trace_christoffel, expected_trace_christoffel);\n\n  const double dilation_factor =\n      get(get<Tags::TimeDilationFactor>(background_quantities));\n\n  tnsr::A<double, Dim> four_velocity{};\n  get<0>(four_velocity) = dilation_factor;\n  for (size_t i = 0; i < Dim; ++i) {\n    four_velocity.get(i + 1) = random_velocity.get(i) * dilation_factor;\n  }\n  const auto normalization = tenex::evaluate<>(\n      metric(ti::a, ti::b) * four_velocity(ti::A) * four_velocity(ti::B));\n  CHECK(get(normalization) == approx(-1.));\n}\n\nvoid test_face_quantities_compute() {\n  INFO(\"test_face_quantities_compute\");\n  static constexpr size_t Dim = 3;\n  MAKE_GENERATOR(gen);\n  const double worldtube_radius = 1.5;\n  const size_t initial_refinement = 1;\n  const size_t initial_extent = 4;\n  const domain::creators::Sphere shell{worldtube_radius,\n                                       3.,\n                                       domain::creators::Sphere::Excision{},\n                                       initial_refinement,\n                                       initial_extent,\n                                       true};\n  const auto shell_domain = shell.create_domain();\n  const auto excision_sphere =\n      shell_domain.excision_spheres().at(\"ExcisionSphere\");\n  const auto& initial_refinements = shell.initial_refinement_levels();\n  const auto& initial_extents = shell.initial_extents();\n  const auto element_ids = initial_element_ids(initial_refinements);\n  const auto& blocks = shell_domain.blocks();\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const auto basis = Spectral::Basis::Legendre;\n  const auto quadrature = Spectral::Quadrature::GaussLobatto;\n\n  for (const auto& element_id : element_ids) {\n    const auto element =\n        domain::create_initial_element(element_id, blocks, initial_refinements);\n    const auto mesh = domain::create_initial_mesh(initial_extents, element,\n                                                  basis, quadrature);\n    const auto& my_block = blocks.at(element_id.block_id());\n    const ElementMap element_map(element_id,\n                                 my_block.stationary_map().get_clone());\n    const auto logical_coords = logical_coordinates(mesh);\n    const auto inertial_coords = element_map(logical_coords);\n    const auto inertial_inv_jacobian = element_map.inv_jacobian(logical_coords);\n    const size_t grid_size = mesh.number_of_grid_points();\n    const DataVector used_for_size(grid_size);\n    const auto lapse = make_with_random_values<Scalar<DataVector>>(\n        make_not_null(&gen), dist, used_for_size);\n    const auto shift =\n        make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n            make_not_null(&gen), dist, used_for_size);\n    const auto evolved_vars = make_with_random_values<\n        CurvedScalarWave::System<Dim>::variables_tag::type>(\n        make_not_null(&gen), dist, used_for_size);\n\n    const auto box = db::create<\n        db::AddSimpleTags<CurvedScalarWave::System<Dim>::variables_tag,\n                          gr::Tags::Shift<DataVector, Dim>,\n                          gr::Tags::Lapse<DataVector>,\n                          domain::Tags::InverseJacobian<\n                              Dim, Frame::ElementLogical, Frame::Inertial>,\n                          Tags::ExcisionSphere<Dim>, domain::Tags::Element<Dim>,\n                          domain::Tags::Mesh<Dim>>,\n        db::AddComputeTags<Tags::FaceQuantitiesCompute>>(\n        evolved_vars, shift, lapse, inertial_inv_jacobian, excision_sphere,\n        element, mesh);\n    const auto& direction = excision_sphere.abutting_direction(element_id);\n    const auto& face_quantities = db::get<Tags::FaceQuantities>(box);\n    if (not direction.has_value()) {\n      CHECK(not face_quantities.has_value());\n    } else {\n      CHECK(face_quantities.has_value());\n      // here, we compute the `FaceQuantities` in the volume and slice them\n      // after\n      const auto& psi = get<CurvedScalarWave::Tags::Psi>(evolved_vars);\n      const auto& pi = get<CurvedScalarWave::Tags::Pi>(evolved_vars);\n      const auto& phi = get<CurvedScalarWave::Tags::Phi<Dim>>(evolved_vars);\n      const auto dt_psi =\n          tenex::evaluate(shift(ti::I) * phi(ti::i) - lapse() * pi());\n      const auto slice_index =\n          index_to_slice_at(mesh.extents(), direction.value());\n      const auto sliced_dim = direction.value().dimension();\n      const auto face_psi =\n          data_on_slice(psi, mesh.extents(), sliced_dim, slice_index);\n      const auto face_dt_psi =\n          data_on_slice(dt_psi, mesh.extents(), sliced_dim, slice_index);\n      const auto face_inv_jacobian = data_on_slice(\n          inertial_inv_jacobian, mesh.extents(), sliced_dim, slice_index);\n      const auto area_element =\n          euclidean_area_element(face_inv_jacobian, direction.value());\n      CHECK_ITERABLE_APPROX(\n          face_psi, get<CurvedScalarWave::Tags::Psi>(face_quantities.value()));\n      CHECK_ITERABLE_APPROX(face_dt_psi,\n                            get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(\n                                face_quantities.value()));\n      CHECK_ITERABLE_APPROX(area_element,\n                            get<gr::surfaces::Tags::AreaElement<DataVector>>(\n                                face_quantities.value()));\n    }\n  }\n}\n\nvoid test_puncture_field() {\n  INFO(\"test_puncture_field\");\n  static constexpr size_t Dim = 3;\n  ::TestHelpers::db::test_compute_tag<Tags::PunctureFieldCompute<Dim>>(\n      \"PunctureField\");\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const tnsr::I<double, Dim, Frame::Inertial> charge_pos{{7.1, 0., 0.}};\n  const tnsr::I<double, Dim, Frame::Inertial> charge_vel{{0.1, 0.2, 0.}};\n  const tnsr::I<double, Dim, Frame::Inertial> charge_acc{{0.1, 0.2, 0.}};\n  const double charge = 0.1;\n  const std::array<tnsr::I<double, Dim, Frame::Inertial>, 2> pos_vel{\n      {charge_pos, charge_vel}};\n\n  auto random_points =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          make_not_null(&gen), dist, DataVector(50));\n  random_points.get(0) += charge_pos.get(0);\n\n  const size_t order = 1;\n  const auto box_abutting = db::create<\n      db::AddSimpleTags<Tags::FaceCoordinates<Dim, Frame::Inertial, true>,\n                        Tags::ParticlePositionVelocity<Dim>,\n                        Tags::GeodesicAcceleration<Dim>, Tags::ExpansionOrder,\n                        Tags::Charge>,\n      db::AddComputeTags<Tags::PunctureFieldCompute<Dim>>>(\n      std::make_optional<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          random_points),\n      pos_vel, charge_acc, order, charge);\n\n  const auto singular_field = get<Tags::PunctureField<Dim>>(box_abutting);\n\n  CHECK(singular_field.has_value());\n  Variables<tmpl::list<CurvedScalarWave::Tags::Psi,\n                       ::Tags::dt<CurvedScalarWave::Tags::Psi>,\n                       ::Tags::deriv<CurvedScalarWave::Tags::Psi,\n                                     tmpl::size_t<3>, Frame::Inertial>>>\n      expected{};\n  puncture_field(make_not_null(&expected), random_points, charge_pos,\n                 charge_vel, charge_acc, 1., order);\n  expected *= charge;\n  CHECK_VARIABLES_APPROX(singular_field.value(), expected);\n  std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>> nullopt{};\n  const auto box_not_abutting = db::create<\n      db::AddSimpleTags<Tags::FaceCoordinates<Dim, Frame::Inertial, true>,\n                        Tags::ParticlePositionVelocity<Dim>,\n                        Tags::GeodesicAcceleration<Dim>, Tags::ExpansionOrder,\n                        Tags::Charge>,\n      db::AddComputeTags<Tags::PunctureFieldCompute<Dim>>>(\n      nullopt, pos_vel, charge_acc, order, charge);\n  const auto puncture_field_nullopt =\n      get<Tags::PunctureField<Dim>>(box_not_abutting);\n  CHECK(not puncture_field_nullopt.has_value());\n}\n\nvoid test_self_force_options() {\n  INFO(\"test_self_force_options\");\n  const auto options = TestHelpers::test_creation<OptionTags::SelfForceOptions>(\n      \"Mass: 0.1\\n\"\n      \"Iterations: 3\\n\"\n      \"TurnOnTime: 1234.\\n\"\n      \"TurnOnInterval: 987.\");\n  CHECK(options.mass == 0.1);\n  CHECK(options.iterations == 3);\n  CHECK(options.turn_on_time == 1234.);\n  CHECK(options.turn_on_interval == 987.);\n}\n\nvoid test_radius_options() {\n  INFO(\"test_radius_options\");\n  {\n    const auto wt_options =\n        TestHelpers::test_creation<OptionTags::RadiusOptions<true>>(\n            \"TransitionRadius: 5.\\n\"\n            \"TransitionWidth: 0.1\\n\"\n            \"Exponent: 3.2\\n\"\n            \"Amplitude: 0.3\\n\");\n    CHECK(wt_options.transition_radius == 5.);\n    CHECK(wt_options.transition_width == 0.1);\n    CHECK(wt_options.exponent == 3.2);\n    CHECK(wt_options.amplitude == 0.3);\n  }\n  {\n    const auto bh_options =\n        TestHelpers::test_creation<OptionTags::RadiusOptions<false>>(\n            \"TransitionRadius: 5.\\n\"\n            \"TransitionWidth: 0.1\\n\"\n            \"Exponent: 3.2\\n\"\n            \"Amplitude: 0.3\\n\");\n    CHECK(bh_options.transition_radius == 5.);\n    CHECK(bh_options.transition_width == 0.1);\n    CHECK(bh_options.exponent == 3.2);\n    CHECK(bh_options.amplitude == 0.3);\n  }\n}\n\n}  // namespace\n\n// [[TimeOut, 15]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.Worldtube.Tags\",\n                  \"[Unit][Evolution]\") {\n  CHECK(TestHelpers::test_option_tag<OptionTags::ExcisionSphere>(\"Worldtube\") ==\n        \"Worldtube\");\n  CHECK(TestHelpers::test_option_tag<\n            CurvedScalarWave::Worldtube::OptionTags::ExpansionOrder>(\"0\") == 0);\n  CHECK(TestHelpers::test_option_tag<\n            CurvedScalarWave::Worldtube::OptionTags::Charge>(\"1.\") == 1.);\n  CHECK(TestHelpers::test_option_tag<\n            CurvedScalarWave::Worldtube::OptionTags::Spin>(\"[0., 0., 0.5]\") ==\n        std::array<double, 3>{0., 0., 0.5});\n  TestHelpers::db::test_simple_tag<Tags::ExcisionSphere<3>>(\"ExcisionSphere\");\n\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::ExpansionOrder>(\"ExpansionOrder\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Worldtube::Tags::Charge>(\n      \"Charge\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Worldtube::Tags::Spin>(\n      \"Spin\");\n  TestHelpers::db::test_simple_tag<CurvedScalarWave::Worldtube::Tags::Mass>(\n      \"Mass\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::SelfForceTurnOnTime>(\n      \"SelfForceTurnOnTime\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::SelfForceTurnOnInterval>(\n      \"SelfForceTurnOnInterval\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::ExpirationTime>(\"ExpirationTime\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::WorldtubeRadius>(\"WorldtubeRadius\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::MaxIterations>(\"MaxIterations\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::BlackHoleRadiusParameters>(\n      \"BlackHoleRadiusParameters\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::WorldtubeRadiusParameters>(\n      \"WorldtubeRadiusParameters\");\n  TestHelpers::db::test_simple_tag<\n      CurvedScalarWave::Worldtube::Tags::CurrentIteration>(\"CurrentIteration\");\n  TestHelpers::db::test_simple_tag<Tags::ElementFacesGridCoordinates<3>>(\n      \"ElementFacesGridCoordinates\");\n  TestHelpers::db::test_simple_tag<Tags::FaceCoordinates<3, Frame::Grid, true>>(\n      \"FaceCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      Tags::FaceCoordinates<3, Frame::Grid, false>>(\"FaceCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      Tags::FaceCoordinates<3, Frame::Inertial, true>>(\"FaceCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      Tags::FaceCoordinates<3, Frame::Inertial, false>>(\"FaceCoordinates\");\n  TestHelpers::db::test_simple_tag<Tags::ParticlePositionVelocity<3>>(\n      \"ParticlePositionVelocity\");\n  TestHelpers::db::test_simple_tag<Tags::EvolvedPosition<3>>(\"EvolvedPosition\");\n  TestHelpers::db::test_simple_tag<Tags::EvolvedVelocity<3>>(\"EvolvedVelocity\");\n  TestHelpers::db::test_simple_tag<Tags::PunctureField<3>>(\"PunctureField\");\n  TestHelpers::db::test_simple_tag<Tags::IteratedPunctureField<3>>(\n      \"IteratedPunctureField\");\n  TestHelpers::db::test_simple_tag<Tags::ObserveCoefficientsTrigger>(\n      \"ObserveCoefficientsTrigger\");\n  TestHelpers::db::test_simple_tag<Tags::GeodesicAcceleration<3>>(\n      \"GeodesicAcceleration\");\n  TestHelpers::db::test_simple_tag<Tags::TimeDilationFactor>(\n      \"TimeDilationFactor\");\n  TestHelpers::db::test_simple_tag<Tags::BackgroundQuantities<3>>(\n      \"BackgroundQuantities\");\n  TestHelpers::db::test_simple_tag<Tags::AccelerationTerms>(\n      \"AccelerationTerms\");\n  TestHelpers::db::test_simple_tag<Tags::Verbosity>(\"Verbosity\");\n  test_excision_sphere_tag();\n  test_self_force_options();\n  test_radius_options();\n  test_initial_position_velocity_tag();\n  test_compute_face_coordinates_grid();\n  test_compute_face_coordinates();\n  test_particle_position_velocity_compute();\n  test_evolved_particle_position_velocity_compute();\n  test_geodesic_acceleration_compute();\n  test_background_quantities_compute();\n  test_face_quantities_compute();\n  test_puncture_field();\n  test_constraint_gammas_compute();\n}\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/Worldtube/Test_Triggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/RadiusFunctions.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/InsideHorizon.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Worldtube/Triggers/OrbitRadius.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n\nnamespace CurvedScalarWave::Worldtube {\nnamespace {\nvoid test_inside_horizon() {\n  const double exp = 1.5;\n  const double rinf_large = 0.1;\n  const double rb = 0.1;\n  const double delta = 0.01;\n\n  const std::array<double, 4> wt_radius_params_large{\n      {exp, rinf_large, rb, delta}};\n  // unused\n  const tnsr::I<double, 3> vel{{0., 0., 0.}};\n\n  // particle outside the horizon\n  const tnsr::I<double, 3> pos_outside{{0., 2., 0.}};\n  const std::array<tnsr::I<double, 3>, 2> pos_vel_outside{{pos_outside, vel}};\n  const Triggers::InsideHorizon inside_horizon_trigger{};\n  CHECK(inside_horizon_trigger(pos_vel_outside, wt_radius_params_large) ==\n        false);\n\n  // particle inside horizon but worldtube sticks out\n  const tnsr::I<double, 3> pos_inside{{1.95, 0., 0.}};\n  const std::array<tnsr::I<double, 3>, 2> pos_vel_inside{{pos_inside, vel}};\n  CHECK(inside_horizon_trigger(pos_vel_inside, wt_radius_params_large) ==\n        false);\n\n  // shrink worldtube radius to fit inside horizons\n  const double rinf_small = 0.01;\n  const std::array<double, 4> wt_radius_params_small{\n      {exp, rinf_small, rb, delta}};\n\n  CHECK(inside_horizon_trigger(pos_vel_inside, wt_radius_params_small) == true);\n}\n\nvoid test_orbit_radius() {\n  const std::vector<double> radii{3., 17., 23.};\n  const Triggers::OrbitRadius orbit_radius_trigger(radii);\n\n  const tnsr::I<double, 3> position_0{{2.99, 0., 0.}};\n  const tnsr::I<double, 3> velocity_0{{0.01, 0., 0.}};\n\n  const std::array<tnsr::I<double, 3>, 2> pos_vel_0{position_0,\n                                                  velocity_0};\n  const Slab large_slab(0., 1.);\n  const Slab small_slab(0., 0.1);\n\n  CHECK(orbit_radius_trigger(pos_vel_0, large_slab.duration()));\n  CHECK_FALSE(orbit_radius_trigger(pos_vel_0, small_slab.duration()));\n\n  const tnsr::I<double, 3> position_1{{0., 0., 17.01}};\n  const tnsr::I<double, 3> velocity_1{{0., 0., -0.01}};\n  const std::array<tnsr::I<double, 3>, 2> pos_vel_1{position_1,\n    velocity_1};\n  CHECK(orbit_radius_trigger(pos_vel_1, large_slab.duration()));\n  CHECK_FALSE(orbit_radius_trigger(pos_vel_1, small_slab.duration()));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.CurvedScalarWave.Worldtube.Triggers\",\n                  \"[Unit][Evolution]\") {\n  test_inside_horizon();\n  test_orbit_radius();\n}\n}  // namespace\n}  // namespace CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/CurvedScalarWave/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/BoundaryConditions/DemandOutgoingCharSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef characteristic_speeds(lapse, shift, outward_directed_normal_covector):\n    return [\n        lapse - np.dot(outward_directed_normal_covector, shift),\n        -lapse - np.dot(outward_directed_normal_covector, shift),\n    ]\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    shift,\n    lapse,\n):\n    speeds = characteristic_speeds(\n        lapse, shift, outward_directed_normal_covector\n    )\n    for i in range(2):\n        if face_mesh_velocity is not None:\n            speeds[i] -= np.dot(\n                outward_directed_normal_covector, face_mesh_velocity\n            )\n        if speeds[i] < 0.0:\n            return \"DemandOutgoingCharSpeeds boundary condition violated\"\n    return None\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/BoundaryConditions/DirichletAnalytic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Evolution.Systems.ForceFree.ElectricCurrentDensity as current\nimport Evolution.Systems.ForceFree.Fluxes as fluxes\nimport numpy as np\nimport PointwiseFunctions.AnalyticData.ForceFree.FfeBreakdown as breakdown\nimport PointwiseFunctions.AnalyticSolutions.ForceFree.FastWave as fastwave\n\n\ndef soln_error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return None\n\n\ndef soln_lapse(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return 1.0\n\n\ndef soln_shift(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return np.asarray([0.0, 0.0, 0.0])\n\n\ndef soln_sqrt_det_spatial_metric(coords, time):\n    return 1.0\n\n\ndef soln_spatial_metric(coords, time):\n    return np.identity(3)\n\n\ndef soln_inverse_spatial_metric(coords, time):\n    return np.identity(3)\n\n\ndef soln_tilde_e(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return fastwave.TildeE(coords, time)\n\n\ndef soln_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return fastwave.TildeB(coords, time)\n\n\ndef soln_tilde_psi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return fastwave.TildePsi(coords, time)\n\n\ndef soln_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return fastwave.TildePhi(coords, time)\n\n\ndef soln_tilde_q(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return fastwave.TildeQ(coords, time)\n\n\ndef soln_flux_tilde_e(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = fastwave.TildeE(coords, time)\n    tilde_b = fastwave.TildeB(coords, time)\n    tilde_psi = fastwave.TildePsi(coords, time)\n    tilde_phi = fastwave.TildePhi(coords, time)\n    tilde_q = fastwave.TildeQ(coords, time)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_e_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef soln_flux_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = fastwave.TildeE(coords, time)\n    tilde_b = fastwave.TildeB(coords, time)\n    tilde_psi = fastwave.TildePsi(coords, time)\n    tilde_phi = fastwave.TildePhi(coords, time)\n    tilde_q = fastwave.TildeQ(coords, time)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_b_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef soln_flux_tilde_psi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = fastwave.TildeE(coords, time)\n    tilde_b = fastwave.TildeB(coords, time)\n    tilde_psi = fastwave.TildePsi(coords, time)\n    tilde_phi = fastwave.TildePhi(coords, time)\n    tilde_q = fastwave.TildeQ(coords, time)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_psi_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef soln_flux_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = fastwave.TildeE(coords, time)\n    tilde_b = fastwave.TildeB(coords, time)\n    tilde_psi = fastwave.TildePsi(coords, time)\n    tilde_phi = fastwave.TildePhi(coords, time)\n    tilde_q = fastwave.TildeQ(coords, time)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_phi_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef soln_flux_tilde_q(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = fastwave.TildeE(coords, time)\n    tilde_b = fastwave.TildeB(coords, time)\n    tilde_psi = fastwave.TildePsi(coords, time)\n    tilde_phi = fastwave.TildePhi(coords, time)\n    tilde_q = fastwave.TildeQ(coords, time)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_q_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef data_tilde_e(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return breakdown.TildeE(coords)\n\n\ndef data_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return breakdown.TildeB(coords)\n\n\ndef data_tilde_psi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return breakdown.TildePsi(coords)\n\n\ndef data_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return breakdown.TildePhi(coords)\n\n\ndef data_tilde_q(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    return breakdown.TildeQ(coords)\n\n\ndef data_flux_tilde_e(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = breakdown.TildeE(coords)\n    tilde_b = breakdown.TildeB(coords)\n    tilde_psi = breakdown.TildePsi(coords)\n    tilde_phi = breakdown.TildePhi(coords)\n    tilde_q = breakdown.TildeQ(coords)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_e_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef data_flux_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = breakdown.TildeE(coords)\n    tilde_b = breakdown.TildeB(coords)\n    tilde_psi = breakdown.TildePsi(coords)\n    tilde_phi = breakdown.TildePhi(coords)\n    tilde_q = breakdown.TildeQ(coords)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_b_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef data_flux_tilde_psi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = breakdown.TildeE(coords)\n    tilde_b = breakdown.TildeB(coords)\n    tilde_psi = breakdown.TildePsi(coords)\n    tilde_phi = breakdown.TildePhi(coords)\n    tilde_q = breakdown.TildeQ(coords)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_psi_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef data_flux_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = breakdown.TildeE(coords)\n    tilde_b = breakdown.TildeB(coords)\n    tilde_psi = breakdown.TildePsi(coords)\n    tilde_phi = breakdown.TildePhi(coords)\n    tilde_q = breakdown.TildeQ(coords)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_phi_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef data_flux_tilde_q(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n    parallel_conductivity,\n):\n    tilde_e = breakdown.TildeE(coords)\n    tilde_b = breakdown.TildeB(coords)\n    tilde_psi = breakdown.TildePsi(coords)\n    tilde_phi = breakdown.TildePhi(coords)\n    tilde_q = breakdown.TildeQ(coords)\n    lapse = soln_lapse(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    shift = soln_shift(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n        parallel_conductivity,\n    )\n    sqrt_det_spatial_metric = soln_sqrt_det_spatial_metric(coords, time)\n    spatial_metric = soln_spatial_metric(coords, time)\n    inv_spatial_metric = soln_inverse_spatial_metric(coords, time)\n\n    return fluxes.tilde_q_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        current.tilde_j(\n            tilde_q,\n            tilde_e,\n            tilde_b,\n            parallel_conductivity,\n            lapse,\n            sqrt_det_spatial_metric,\n            spatial_metric,\n        ),\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.ForceFree.BoundaryConditions.DemandOutgoingCharSpeeds\",\n                  \"[Unit][GrMhd]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ForceFree/BoundaryConditions/\"};\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      ForceFree::BoundaryConditions::DemandOutgoingCharSpeeds,\n      ForceFree::BoundaryConditions::BoundaryCondition, ForceFree::System,\n      tmpl::list<ForceFree::BoundaryCorrections::Rusanov>>(\n      make_not_null(&gen), \"DemandOutgoingCharSpeeds\",\n      tuples::TaggedTuple<helpers::Tags::PythonFunctionForErrorMessage<>>{\n          \"error\"},\n      \"DemandOutgoingCharSpeeds:\\n\", Index<2>{5}, db::DataBox<tmpl::list<>>{},\n      tuples::TaggedTuple<>{});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\n\nstruct ConvertFfeBreakdown {\n  using unpacked_container = double;\n  using packed_container = ForceFree::AnalyticData::FfeBreakdown;\n  using packed_type = double;\n\n  static packed_container create_container() { return {}; }\n\n  static inline unpacked_container unpack(const packed_container /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return 0.0;  // no parameter but we need some placeholder type\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> /*packed*/,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    // no parameters but we need a placeholder function\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct ConvertFastwave {\n  using unpacked_container = int;\n  using packed_container = ForceFree::Solutions::FastWave;\n  using packed_type = double;\n\n  static packed_container create_container() { return {}; }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return 3;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = packed_container{};\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            ForceFree::BoundaryConditions::BoundaryCondition,\n            tmpl::list<ForceFree::BoundaryConditions::DirichletAnalytic>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   tmpl::list<ForceFree::Solutions::FastWave,\n                              ForceFree::AnalyticData::FfeBreakdown>>>;\n  };\n};\n\nvoid test_solution() {\n  register_classes_with_charm(ForceFree::Solutions::all_solutions{});\n\n  MAKE_GENERATOR(gen);\n\n  const auto box_analytic_soln = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticSolution<ForceFree::Solutions::FastWave>,\n      ForceFree::Tags::ParallelConductivity>>(\n      0.5, ConvertFastwave::create_container(), 100.0);\n\n  helpers::test_boundary_condition_with_python<\n      ForceFree::BoundaryConditions::DirichletAnalytic,\n      ForceFree::BoundaryConditions::BoundaryCondition, ForceFree::System,\n      tmpl::list<ForceFree::BoundaryCorrections::Rusanov>,\n      tmpl::list<ConvertFastwave>,\n      tmpl::list<Tags::AnalyticSolution<ForceFree::Solutions::FastWave>>,\n      Metavariables>(\n      make_not_null(&gen),\n      \"Evolution.Systems.ForceFree.BoundaryConditions.DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildeE>,\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildeB>,\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildePsi>,\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildePhi>,\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildeQ>,\n\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildeE, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildeB, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildePsi, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildePhi, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildeQ, tmpl::size_t<3>, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, 3>>>{\n          \"soln_error\", \"soln_tilde_e\", \"soln_tilde_b\", \"soln_tilde_psi\",\n          \"soln_tilde_phi\", \"soln_tilde_q\", \"soln_flux_tilde_e\",\n          \"soln_flux_tilde_b\", \"soln_flux_tilde_psi\", \"soln_flux_tilde_phi\",\n          \"soln_flux_tilde_q\", \"soln_lapse\", \"soln_shift\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    FastWave:\\n\",\n      Index<2>{5}, box_analytic_soln, tuples::TaggedTuple<>{});\n}\n\nvoid test_data() {\n  MAKE_GENERATOR(gen);\n\n  register_classes_with_charm(ForceFree::AnalyticData::all_data{});\n\n  const auto box_analytic_data = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticData<ForceFree::AnalyticData::FfeBreakdown>,\n      ForceFree::Tags::ParallelConductivity>>(\n      0.5, ConvertFfeBreakdown::create_container(), 100.0);\n\n  helpers::test_boundary_condition_with_python<\n      ForceFree::BoundaryConditions::DirichletAnalytic,\n      ForceFree::BoundaryConditions::BoundaryCondition, ForceFree::System,\n      tmpl::list<ForceFree::BoundaryCorrections::Rusanov>,\n      tmpl::list<ConvertFfeBreakdown>,\n      tmpl::list<Tags::AnalyticData<ForceFree::AnalyticData::FfeBreakdown>>,\n      Metavariables>(\n      make_not_null(&gen),\n      \"Evolution.Systems.ForceFree.BoundaryConditions.DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildeE>,\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildeB>,\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildePsi>,\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildePhi>,\n          helpers::Tags::PythonFunctionName<ForceFree::Tags::TildeQ>,\n\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildeE, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildeB, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildePsi, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildePhi, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              ForceFree::Tags::TildeQ, tmpl::size_t<3>, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, 3>>>{\n          \"soln_error\", \"data_tilde_e\", \"data_tilde_b\", \"data_tilde_psi\",\n          \"data_tilde_phi\", \"data_tilde_q\", \"data_flux_tilde_e\",\n          \"data_flux_tilde_b\", \"data_flux_tilde_psi\", \"data_flux_tilde_phi\",\n          \"data_flux_tilde_q\", \"soln_lapse\", \"soln_shift\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    FfeBreakdown:\\n\",\n      Index<2>{5}, box_analytic_data, tuples::TaggedTuple<>{});\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ForceFree.BoundaryConditions.DirichletAnalytic\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test_solution();\n  test_data();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/BoundaryConditions/Test_Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryConditions/Factory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.ForceFree.BoundaryConditions.Periodic\",\n                  \"[Unit][ForceFree]\") {\n  helpers::test_periodic_condition<\n      domain::BoundaryConditions::Periodic<\n          ForceFree::BoundaryConditions::BoundaryCondition>,\n      ForceFree::BoundaryConditions::BoundaryCondition>(\"Periodic:\\n\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    flux_tilde_e,\n    flux_tilde_b,\n    flux_tilde_psi,\n    flux_tilde_phi,\n    flux_tilde_q,\n    lapse,\n    shift,\n    normal_covector,\n    normal_vector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n):\n    def compute_char(lapse_sign):\n        return np.abs(\n            np.asarray(\n                (lapse_sign * lapse - np.dot(shift, normal_covector))\n                if normal_dot_mesh_velocity is None\n                else (\n                    lapse_sign * lapse\n                    - np.dot(shift, normal_covector)\n                    - normal_dot_mesh_velocity\n                )\n            )\n        )\n\n    return (\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        np.asarray(np.einsum(\"ij,i->j\", flux_tilde_e, normal_covector)),\n        np.asarray(np.einsum(\"ij,i->j\", flux_tilde_b, normal_covector)),\n        np.asarray(np.dot(flux_tilde_psi, normal_covector)),\n        np.asarray(np.dot(flux_tilde_phi, normal_covector)),\n        np.asarray(np.dot(flux_tilde_q, normal_covector)),\n        np.asarray(np.maximum(compute_char(1.0), compute_char(-1.0))),\n    )\n\n\ndef dg_boundary_terms(\n    interior_tilde_e,\n    interior_tilde_b,\n    interior_tilde_psi,\n    interior_tilde_phi,\n    interior_tilde_q,\n    interior_normal_dot_flux_tilde_e,\n    interior_normal_dot_flux_tilde_b,\n    interior_normal_dot_flux_tilde_psi,\n    interior_normal_dot_flux_tilde_phi,\n    interior_normal_dot_flux_tilde_q,\n    interior_abs_char_speed,\n    exterior_tilde_e,\n    exterior_tilde_b,\n    exterior_tilde_psi,\n    exterior_tilde_phi,\n    exterior_tilde_q,\n    exterior_normal_dot_flux_tilde_e,\n    exterior_normal_dot_flux_tilde_b,\n    exterior_normal_dot_flux_tilde_psi,\n    exterior_normal_dot_flux_tilde_phi,\n    exterior_normal_dot_flux_tilde_q,\n    exterior_abs_char_speed,\n    use_strong_form,\n):\n    sign_for_form = 1.0 if use_strong_form else -1.0\n\n    # Use scope and locals() to get arguments into the eval context below\n    scope = locals()\n\n    def impl(var_name):\n        return np.asarray(\n            (\n                -0.5\n                * (\n                    sign_for_form\n                    * eval(\"interior_normal_dot_flux_\" + var_name, scope)\n                    + eval(\"exterior_normal_dot_flux_\" + var_name, scope)\n                )\n                - 0.5\n                * np.maximum(interior_abs_char_speed, exterior_abs_char_speed)\n                * (\n                    eval(\"exterior_\" + var_name, scope)\n                    - eval(\"interior_\" + var_name, scope)\n                )\n            )\n        )\n\n    return (\n        impl(\"tilde_e\"),\n        impl(\"tilde_b\"),\n        impl(\"tilde_psi\"),\n        impl(\"tilde_phi\"),\n        impl(\"tilde_q\"),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/BoundaryCorrections/Test_Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace ForceFree {\n\nnamespace {\n\nSPECTRE_TEST_CASE(\"Unit.ForceFree.BoundaryCorrections.Rusanov\",\n                  \"[Unit][ForceFree]\") {\n  PUPable_reg(BoundaryCorrections::Rusanov);\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ForceFree/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  using system = System;\n\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<system>(\n      make_not_null(&gen), BoundaryCorrections::Rusanov{},\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<system>(\n      make_not_null(&gen), \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      BoundaryCorrections::Rusanov{},\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n\n  const auto rusanov = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection, BoundaryCorrections::Rusanov>(\"Rusanov:\");\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<system>(\n      make_not_null(&gen), \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const BoundaryCorrections::Rusanov&>(*rusanov),\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n\n  CHECK_FALSE(BoundaryCorrections::Rusanov{} != BoundaryCorrections::Rusanov{});\n}\n\n}  // namespace\n}  // namespace ForceFree\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Test_ForceFree)\n\nset(LIBRARY_SOURCES\n  BoundaryConditions/Test_Periodic.cpp\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryCorrections/Test_Rusanov.cpp\n  FiniteDifference/Test_AdaptiveOrder.cpp\n  FiniteDifference/Test_MonotonisedCentral.cpp\n  FiniteDifference/Test_Tags.cpp\n  FiniteDifference/Test_Wcns5z.cpp\n  Subcell/Test_ComputeFluxes.cpp\n  Subcell/Test_GhostData.cpp\n  Subcell/Test_NeighborPackagedData.cpp\n  Subcell/Test_SetInitialRdmpData.cpp\n  Subcell/Test_TciOnDgGrid.cpp\n  Subcell/Test_TciOnFdGrid.cpp\n  Subcell/Test_TciOptions.cpp\n  Test_Characteristics.cpp\n  Test_ElectricCurrentDensity.cpp\n  Test_ElectromagneticVariables.cpp\n  Test_Fluxes.cpp\n  Test_MaskNeutronStarInterior.cpp\n  Test_Sources.cpp\n  Test_Tags.cpp\n  Test_TimeDerivativeTerms.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  ForceFree\n  ForceFreeAnalyticData\n  ForceFreeSolutions\n  GeneralRelativityHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/ElectricCurrentDensity.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef levi_civita_symbol(i, j, k):\n    return np.sign(j - i) * np.sign(k - i) * np.sign(k - j)\n\n\ndef tilde_j_drift(\n    tilde_q,\n    tilde_e,\n    tilde_b,\n    parallel_conductivity,\n    lapse,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n):\n    tilde_e_one_form = np.einsum(\"a, ia\", tilde_e, spatial_metric)\n    tilde_b_one_form = np.einsum(\"a, ia\", tilde_b, spatial_metric)\n    tilde_b_squared = np.einsum(\"a, a\", tilde_b_one_form, tilde_b)\n\n    result = tilde_e * 0.0\n\n    for i in range(3):\n        for j in range(3):\n            for k in range(3):\n                e_ijk = levi_civita_symbol(i, j, k) / sqrt_det_spatial_metric\n                result[i] += e_ijk * tilde_e_one_form[j] * tilde_b_one_form[k]\n\n    # overall scaling\n    result = result * lapse * tilde_q / tilde_b_squared\n\n    return result\n\n\ndef tilde_j_parallel(\n    tilde_q,\n    tilde_e,\n    tilde_b,\n    parallel_conductivity,\n    lapse,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n):\n    tilde_e_one_form = np.einsum(\"a, ia\", tilde_e, spatial_metric)\n    tilde_b_one_form = np.einsum(\"a, ia\", tilde_b, spatial_metric)\n\n    tilde_e_squared = np.einsum(\"a, a\", tilde_e_one_form, tilde_e)\n    tilde_b_squared = np.einsum(\"a, a\", tilde_b_one_form, tilde_b)\n    tilde_e_dot_tilde_b = np.einsum(\"a, a\", tilde_e_one_form, tilde_b)\n\n    return (\n        parallel_conductivity\n        * lapse\n        * (\n            tilde_e_dot_tilde_b * tilde_b\n            + max(tilde_e_squared - tilde_b_squared, 0.0) * tilde_e\n        )\n        / tilde_b_squared\n    )\n\n\ndef tilde_j(\n    tilde_q,\n    tilde_e,\n    tilde_b,\n    parallel_conductivity,\n    lapse,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n):\n    return tilde_j_drift(\n        tilde_q,\n        tilde_e,\n        tilde_b,\n        parallel_conductivity,\n        lapse,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n    ) + tilde_j_parallel(\n        tilde_q,\n        tilde_e,\n        tilde_b,\n        parallel_conductivity,\n        lapse,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/ElectromagneticVariables.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef electric_field_compute(tilde_e, sqrt_det_spatial_metric):\n    return tilde_e / sqrt_det_spatial_metric\n\n\ndef magnetic_field_compute(tilde_b, sqrt_det_spatial_metric):\n    return tilde_b / sqrt_det_spatial_metric\n\n\ndef charge_density_compute(tilde_q, sqrt_det_spatial_metric):\n    return tilde_q / sqrt_det_spatial_metric\n\n\ndef electric_current_density_compute(tilde_j, lapse, sqrt_det_spatial_metric):\n    return tilde_j / (lapse * sqrt_det_spatial_metric)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/FiniteDifference/Test_AdaptiveOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/AdaptiveOrder.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/ForceFree/FiniteDifference/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Fd.AdaptiveOrder\",\n                  \"[Unit][Evolution]\") {\n  using AdaptiveOrder = ForceFree::fd::AdaptiveOrder;\n  using Reconstructor = ForceFree::fd::Reconstructor;\n\n  auto mc = fd::reconstruction::FallbackReconstructorType::MonotonisedCentral;\n\n  TestHelpers::ForceFree::fd::test_reconstructor(\n      8, AdaptiveOrder{4.0, 4.0, 4.0, mc});\n  TestHelpers::ForceFree::fd::test_reconstructor(\n      6, AdaptiveOrder{4.0, 4.0, std::nullopt, mc});\n  TestHelpers::ForceFree::fd::test_reconstructor(\n      8, AdaptiveOrder{4.0, std::nullopt, 4.0, mc});\n\n  const AdaptiveOrder ao_recons{4.0, std::nullopt, std::nullopt, mc};\n  TestHelpers::ForceFree::fd::test_reconstructor(4, ao_recons);\n\n  const auto ao_from_options_base = TestHelpers::test_factory_creation<\n      Reconstructor, ForceFree::fd::OptionTags::Reconstructor>(\n      \"AdaptiveOrder:\\n\"\n      \"  Alpha5: 4.0\\n\"\n      \"  Alpha7: None\\n\"\n      \"  Alpha9: None\\n\"\n      \"  LowOrderReconstructor: MonotonisedCentral\\n\");\n  auto* const ao_from_options =\n      dynamic_cast<const AdaptiveOrder*>(ao_from_options_base.get());\n  REQUIRE(ao_from_options != nullptr);\n  CHECK(*ao_from_options == ao_recons);\n\n  CHECK(ao_recons != AdaptiveOrder(4.5, std::nullopt, std::nullopt, mc));\n  CHECK(ao_recons != AdaptiveOrder(4.0, 4.0, std::nullopt, mc));\n  CHECK(AdaptiveOrder(4.0, 4.0, std::nullopt, mc) !=\n        AdaptiveOrder(4.0, 4.1, std::nullopt, mc));\n  CHECK(ao_recons != AdaptiveOrder(4.0, std::nullopt, 4.0, mc));\n  CHECK(AdaptiveOrder(4.0, std::nullopt, 4.0, mc) !=\n        AdaptiveOrder(4.0, std::nullopt, 4.1, mc));\n  CHECK(AdaptiveOrder(5.0, std::nullopt, 4.0, mc) ==\n        AdaptiveOrder(5.0, std::nullopt, 4.0, mc));\n  CHECK(AdaptiveOrder(5.0, 6.0, 4.0, mc) == AdaptiveOrder(5.0, 6.0, 4.0, mc));\n  CHECK(AdaptiveOrder(5.0, 6.0, 4.0, mc) != AdaptiveOrder(5.1, 6.0, 4.0, mc));\n  CHECK(AdaptiveOrder(5.0, 6.0, 4.0, mc) != AdaptiveOrder(5.0, 6.1, 4.0, mc));\n  CHECK(AdaptiveOrder(5.0, 6.0, 4.0, mc) != AdaptiveOrder(5.0, 6.0, 4.1, mc));\n  CHECK(AdaptiveOrder(5.0, 6.0, 4.0, mc) !=\n        AdaptiveOrder(5.0, 6.0, 4.0,\n                      fd::reconstruction::FallbackReconstructorType::Minmod));\n  CHECK(ao_recons !=\n        AdaptiveOrder(4.0, std::nullopt, std::nullopt,\n                      fd::reconstruction::FallbackReconstructorType::Minmod));\n\n  CHECK_THROWS_WITH(\n      AdaptiveOrder(4.5, std::nullopt, std::nullopt,\n                    fd::reconstruction::FallbackReconstructorType::None),\n      Catch::Matchers::ContainsSubstring(\n          \"None is not an allowed low-order reconstructor.\"));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/FiniteDifference/Test_MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/ForceFree/FiniteDifference/TestHelpers.hpp\"\n\nnamespace {\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Fd.MonotonisedCentral\",\n                  \"[Unit][Evolution]\") {\n  using MonotonisedCentral = ForceFree::fd::MonotonisedCentral;\n  using Reconstructor = ForceFree::fd::Reconstructor;\n\n  const MonotonisedCentral mc_recons{};\n  TestHelpers::ForceFree::fd::test_reconstructor(5, mc_recons);\n\n  const auto mc_from_options_base = TestHelpers::test_factory_creation<\n      Reconstructor, ForceFree::fd::OptionTags::Reconstructor>(\n      \"MonotonisedCentral:\\n\");\n  auto* const mc_from_options =\n      dynamic_cast<const MonotonisedCentral*>(mc_from_options_base.get());\n  REQUIRE(mc_from_options != nullptr);\n  CHECK(*mc_from_options == mc_recons);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/FiniteDifference/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Fd.Tag\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<ForceFree::fd::Tags::Reconstructor>(\n      \"Reconstructor\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/FiniteDifference/Test_Wcns5z.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Wcns5z.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/ForceFree/FiniteDifference/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Fd.Wcns5z\",\n                  \"[Unit][Evolution]\") {\n  using Wcns5z = ForceFree::fd::Wcns5z;\n  using Reconstructor = ForceFree::fd::Reconstructor;\n\n  auto mc = fd::reconstruction::FallbackReconstructorType::MonotonisedCentral;\n\n  const Wcns5z wcns5z_recons{2, 2.0e-16, mc, 1};\n  TestHelpers::ForceFree::fd::test_reconstructor(5, wcns5z_recons);\n\n  const auto wcns5z_from_options_base = TestHelpers::test_factory_creation<\n      Reconstructor, ForceFree::fd::OptionTags::Reconstructor>(\n      \"Wcns5z:\\n\"\n      \"  NonlinearWeightExponent: 2\\n\"\n      \"  Epsilon: 2.0e-16\\n\"\n      \"  FallbackReconstructor: MonotonisedCentral\\n\"\n      \"  MaxNumberOfExtrema: 1\\n\");\n  auto* const wcns5z_from_options =\n      dynamic_cast<const Wcns5z*>(wcns5z_from_options_base.get());\n  REQUIRE(wcns5z_from_options != nullptr);\n  CHECK(*wcns5z_from_options == wcns5z_recons);\n\n  CHECK(wcns5z_recons != Wcns5z(1, 2.0e-16, mc, 1));\n  CHECK(wcns5z_recons != Wcns5z(2, 1.0e-16, mc, 1));\n  CHECK(wcns5z_recons !=\n        Wcns5z(2, 2.0e-16, fd::reconstruction::FallbackReconstructorType::None,\n               1));\n  CHECK(wcns5z_recons != Wcns5z(2, 2.0e-16, mc, 2));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Fluxes.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef levi_civita_symbol(i, j, k):\n    return np.sign(j - i) * np.sign(k - i) * np.sign(k - j)\n\n\ndef tilde_e_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n):\n    magnetic_field_one_form = (\n        np.einsum(\"a, ia\", tilde_b, spatial_metric) / sqrt_det_spatial_metric\n    )\n\n    result = -np.outer(shift, tilde_e) + lapse * (\n        inv_spatial_metric * tilde_psi\n    )\n\n    for j in range(3):\n        for i in range(3):\n            for k in range(3):\n                result[j, i] -= (\n                    lapse\n                    * levi_civita_symbol(i, j, k)\n                    * magnetic_field_one_form[k]\n                )\n\n    return result\n\n\ndef tilde_b_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n):\n    electric_field_one_form = (\n        np.einsum(\"a, ia\", tilde_e, spatial_metric) / sqrt_det_spatial_metric\n    )\n\n    result = -np.outer(shift, tilde_b) + lapse * (\n        inv_spatial_metric * tilde_phi\n    )\n\n    for j in range(3):\n        for i in range(3):\n            for k in range(3):\n                result[j, i] += (\n                    lapse\n                    * levi_civita_symbol(i, j, k)\n                    * electric_field_one_form[k]\n                )\n\n    return result\n\n\ndef tilde_psi_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n):\n    return -shift * tilde_psi + lapse * tilde_e\n\n\ndef tilde_phi_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n):\n    return -shift * tilde_phi + lapse * tilde_b\n\n\ndef tilde_q_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n):\n    return tilde_j - shift * tilde_q\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/MaskNeutronStarInterior.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef compute_ns_interior_mask(coords):\n    r_squared = np.einsum(\"a, a\", coords, coords)\n\n    if r_squared < 1.0:\n        return -1.0\n    else:\n        return 1.0\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Sources.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom ElectricCurrentDensity import tilde_j_drift\n\n\ndef trace_spatial_Gamma_second_kind(inv_spatial_metric, d_spatial_metric):\n    # returns \\gamma^{jk} \\Gamma^i_{jk}\n    term_one = np.einsum(\"ab, iab\", inv_spatial_metric, d_spatial_metric)\n    term_two = np.einsum(\"ab, aib\", inv_spatial_metric, d_spatial_metric)\n    return -0.5 * np.einsum(\"ia, a\", inv_spatial_metric, term_one) + np.einsum(\n        \"ia, a\", inv_spatial_metric, term_two\n    )\n\n\ndef source_tilde_e(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n    extrinsic_curvature,\n):\n    tilde_j = tilde_j_drift(\n        tilde_q,\n        tilde_e,\n        tilde_b,\n        parallel_conductivity,\n        lapse,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n    )\n    return (\n        -tilde_j\n        - np.einsum(\"a, ai\", tilde_e, d_shift)\n        + tilde_psi\n        * (\n            np.einsum(\"a, ia\", d_lapse, inv_spatial_metric)\n            - lapse\n            * trace_spatial_Gamma_second_kind(\n                inv_spatial_metric, d_spatial_metric\n            )\n        )\n    )\n\n\ndef source_tilde_b(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n    extrinsic_curvature,\n):\n    return -np.einsum(\"a, ai\", tilde_b, d_shift) + tilde_phi * (\n        np.einsum(\"a, ia\", d_lapse, inv_spatial_metric)\n        - lapse\n        * trace_spatial_Gamma_second_kind(inv_spatial_metric, d_spatial_metric)\n    )\n\n\ndef source_tilde_phi(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n    extrinsic_curvature,\n):\n    return np.einsum(\"a, a\", tilde_b, d_lapse) - lapse * tilde_phi * (\n        np.einsum(\"ab, ab\", inv_spatial_metric, extrinsic_curvature) + kappa_phi\n    )\n\n\ndef source_tilde_psi(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n    extrinsic_curvature,\n):\n    return (\n        np.einsum(\"a, a\", tilde_e, d_lapse)\n        + lapse * tilde_q\n        - lapse\n        * tilde_psi\n        * (\n            np.einsum(\"ab, ab\", inv_spatial_metric, extrinsic_curvature)\n            + kappa_psi\n        )\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Subcell/Test_ComputeFluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ForceFree/Fluxes.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree {\nnamespace {\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  using argument_tags = typename Fluxes::argument_tags;\n  using return_tags = typename Fluxes::return_tags;\n\n  const size_t num_pts = 5;\n  auto random_vars = make_with_random_values<\n      Variables<tmpl::append<return_tags, argument_tags>>>(gen, dist, num_pts);\n\n  // store computed fluxes into return_tags portion of `random_vars`\n  subcell::compute_fluxes(make_not_null(&random_vars));\n\n  // compute expected fluxes\n  Variables<return_tags> expected_fluxes{num_pts};\n  Fluxes::apply(\n      make_not_null(\n          &get<::Tags::Flux<Tags::TildeE, tmpl::size_t<3>, Frame::Inertial>>(\n              expected_fluxes)),\n      make_not_null(\n          &get<::Tags::Flux<Tags::TildeB, tmpl::size_t<3>, Frame::Inertial>>(\n              expected_fluxes)),\n      make_not_null(\n          &get<::Tags::Flux<Tags::TildePsi, tmpl::size_t<3>, Frame::Inertial>>(\n              expected_fluxes)),\n      make_not_null(\n          &get<::Tags::Flux<Tags::TildePhi, tmpl::size_t<3>, Frame::Inertial>>(\n              expected_fluxes)),\n      make_not_null(\n          &get<::Tags::Flux<Tags::TildeQ, tmpl::size_t<3>, Frame::Inertial>>(\n              expected_fluxes)),\n      get<Tags::TildeE>(random_vars), get<Tags::TildeB>(random_vars),\n      get<Tags::TildePsi>(random_vars), get<Tags::TildePhi>(random_vars),\n      get<Tags::TildeQ>(random_vars), get<Tags::TildeJ>(random_vars),\n      get<gr::Tags::Lapse<DataVector>>(random_vars),\n      get<gr::Tags::Shift<DataVector, 3>>(random_vars),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(random_vars),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(random_vars),\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(random_vars));\n\n  tmpl::for_each<Fluxes::return_tags>(\n      [&expected_fluxes, &random_vars](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<tag>(random_vars), get<tag>(expected_fluxes));\n      });\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Subcell.ComputeFluxes\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  test(make_not_null(&gen), make_not_null(&dist));\n}\n\n}  // namespace\n}  // namespace ForceFree\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Subcell/Test_GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/GhostData.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree::fd {\n\nnamespace {\nvoid test_ghost_data_on_subcells(\n    const gsl::not_null<std::mt19937*> gen,\n    const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using evolved_vars_tags = System::variables_tag::tags_list;\n  const auto evolved_vars =\n      make_with_random_values<Variables<evolved_vars_tags>>(\n          gen, dist, subcell_mesh.number_of_grid_points());\n  const auto tilde_j = make_with_random_values<ForceFree::Tags::TildeJ::type>(\n      gen, dist, subcell_mesh.number_of_grid_points());\n\n  auto box = db::create<db::AddSimpleTags<::Tags::Variables<evolved_vars_tags>,\n                                          ForceFree::Tags::TildeJ>>(\n      evolved_vars, tilde_j);\n  DataVector recons_prims_rdmp =\n      db::mutate_apply<ForceFree::subcell::GhostVariables>(make_not_null(&box),\n                                                           2_st);\n  const Variables<ForceFree::fd::tags_list_for_reconstruction> recons_vars{\n      recons_prims_rdmp.data(), recons_prims_rdmp.size() - 2};\n\n  tmpl::for_each<evolved_vars_tags>([&evolved_vars, &recons_vars](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK_ITERABLE_APPROX(get<tag>(recons_vars), get<tag>(evolved_vars));\n  });\n  CHECK_ITERABLE_APPROX(get<ForceFree::Tags::TildeJ>(recons_vars), tilde_j);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Subcell.GhostData\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  test_ghost_data_on_subcells(make_not_null(&gen), make_not_null(&dist));\n}\n}  // namespace\n\n}  // namespace ForceFree::fd\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Subcell/Test_NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionOrder.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/ForceFree/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/ReconstructWork.tpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/Fluxes.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/ForceFree/FiniteDifference/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/FastWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree {\nnamespace {\n\nvoid test_neighbor_packaged_data(const gsl::not_null<std::mt19937*> gen) {\n  //\n  // Test is performed as follows\n  //\n  // 1. create random U on an element and its neighbor elements\n  // 2. send through subcell reconstruction and compute FD fluxes on mortars\n  // 3. feed argument variables of dg_package_data() function to\n  //    the NeighborPackagedData struct and retrieve the packaged data\n  // 4. check if it agrees with expected values\n  //\n\n  using evolved_vars_tags = typename System::variables_tag::tags_list;\n  using fluxes_tags = typename Fluxes::return_tags;\n\n  using ReconstructionForTest = fd::MonotonisedCentral;\n  using BoundaryCorrectionForTest = BoundaryCorrections::Rusanov;\n\n  using SolutionForTest = Solutions::FastWave;\n  const SolutionForTest solution{};\n\n  const double time{0.0};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  // create an element and its neighbor elements\n  DirectionMap<3, Neighbors<3>> element_neighbors{};\n  for (size_t i = 0; i < 2 * 3_st; ++i) {\n    element_neighbors[gsl::at(Direction<3>::all_directions(), i)] =\n        Neighbors<3>{{ElementId<3>{i + 1, {}}},\n                     OrientationMap<3>::create_aligned()};\n  }\n  const Element<3> element{ElementId<3>{0, {}}, element_neighbors};\n\n  // Mesh (DG, Subcell), coordinate maps, logical and inertial coords.\n  const size_t num_dg_pts_per_dimension = 5;\n  const Mesh<3> dg_mesh{num_dg_pts_per_dimension, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  const auto logical_to_grid_map = ElementMap<3, Frame::Grid>{\n      ElementId<3>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<3>{})};\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const auto dg_logical_coords = logical_coordinates(dg_mesh);\n  const auto dg_inertial_coords = grid_to_inertial_map(\n      logical_to_grid_map(dg_logical_coords), time, functions_of_time);\n  const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n  const auto subcell_inertial_coords = grid_to_inertial_map(\n      logical_to_grid_map(subcell_logical_coords), time, functions_of_time);\n\n  // Create background metric variables on DG mesh and subcell mesh\n  Variables<typename System::spacetime_variables_tag::tags_list> dg_gr_vars{\n      dg_mesh.number_of_grid_points()};\n  dg_gr_vars.assign_subset(solution.variables(\n      dg_inertial_coords, time,\n      typename System::spacetime_variables_tag::tags_list{}));\n  using inactive_metric_vars_tags =\n      evolution::dg::subcell::Tags::Inactive<System::spacetime_variables_tag>;\n  inactive_metric_vars_tags::type subcell_gr_vars{\n      subcell_mesh.number_of_grid_points()};\n  subcell_gr_vars.assign_subset(solution.variables(\n      subcell_inertial_coords, time,\n      typename System::spacetime_variables_tag::tags_list{}));\n\n  // Compute face-centered subcell GR vars\n  std::array<typename System::flux_spacetime_variables_tag::type, 3>\n      face_centered_gr_vars{};\n  {\n    const size_t num_face_centered_mesh_grid_pts =\n        (subcell_mesh.extents(0) + 1) * subcell_mesh.extents(1) *\n        subcell_mesh.extents(2);\n    for (size_t d = 0; d < 3; ++d) {\n      gsl::at(face_centered_gr_vars, d)\n          .initialize(num_face_centered_mesh_grid_pts);\n      const auto basis = make_array<3>(subcell_mesh.basis(0));\n      auto quadrature = make_array<3>(subcell_mesh.quadrature(0));\n      auto extents = make_array<3>(subcell_mesh.extents(0));\n      gsl::at(extents, d) = subcell_mesh.extents(0) + 1;\n      gsl::at(quadrature, d) = Spectral::Quadrature::FaceCentered;\n      const Mesh<3> face_centered_mesh{extents, basis, quadrature};\n      const auto face_centered_logical_coords =\n          logical_coordinates(face_centered_mesh);\n      const auto face_centered_inertial_coords = grid_to_inertial_map(\n          logical_to_grid_map(face_centered_logical_coords), time,\n          functions_of_time);\n      face_centered_gr_vars.at(d).assign_subset(solution.variables(\n          face_centered_inertial_coords, time,\n          typename System::flux_spacetime_variables_tag::tags_list{}));\n    }\n  }\n\n  // generate random volume vars on the dg mesh and project it to subcell mesh\n  Variables<evolved_vars_tags> dg_evolved_vars{dg_mesh.number_of_grid_points()};\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  fill_with_random_values(make_not_null(&dg_evolved_vars), gen,\n                          make_not_null(&dist));\n  Variables<evolved_vars_tags> volume_vars_subcell =\n      evolution::dg::subcell::fd::project(dg_evolved_vars, dg_mesh,\n                                          subcell_mesh.extents());\n\n  // We also need TildeJ in the DataBox when performing FD reconstruction.\n  // For testing purpose it is okay to have TildeJ on DG grid not necessarily\n  // consistent with DG volume variables, but we need correctly projected values\n  // on FD grid.\n  const auto dg_tilde_j = make_with_random_values<Tags::TildeJ::type>(\n      gen, make_not_null(&dist), dg_mesh.number_of_grid_points());\n  const auto subcell_tilde_j = [&dg_tilde_j, &dg_mesh, &subcell_mesh]() {\n    Variables<tmpl::list<Tags::TildeJ>> var{dg_mesh.number_of_grid_points()};\n    for (size_t d = 0; d < 3; ++d) {\n      get<Tags::TildeJ>(var).get(d) = dg_tilde_j.get(d);\n    }\n    return get<Tags::TildeJ>(evolution::dg::subcell::fd::project(\n        var, dg_mesh, subcell_mesh.extents()));\n  }();\n\n  // generate random ghost data in neighboring elements\n  const ReconstructionForTest reconstructor{};\n  const auto compute_random_variable = [&gen, &dist](const auto& coords) {\n    Variables<ForceFree::fd::tags_list_for_reconstruction> vars{\n        get<0>(coords).size(), 0.0};\n    fill_with_random_values(make_not_null(&vars), gen, make_not_null(&dist));\n    return vars;\n  };\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<3>::type\n      ghost_data = TestHelpers::ForceFree::fd::compute_ghost_data(\n          subcell_mesh, subcell_logical_coords, element.neighbors(),\n          reconstructor.ghost_zone_size(), compute_random_variable);\n\n  // Assign normal vector\n  DirectionMap<3, std::optional<Variables<\n                      tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                 evolution::dg::Tags::NormalCovector<3>>>>>\n      normal_vectors{};\n  for (const auto& direction : Direction<3>::all_directions()) {\n    using inverse_spatial_metric_tag =\n        typename System::inverse_spatial_metric_tag;\n    const Mesh<2> face_mesh = dg_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<3>, tnsr::i<DataVector, 3, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, 3, Frame::Inertial> unnormalized_covector{};\n    const auto element_logical_to_grid_inv_jac =\n        logical_to_grid_map.inv_jacobian(face_logical_coords);\n    const auto grid_to_inertial_inv_jac = grid_to_inertial_map.inv_jacobian(\n        logical_to_grid_map(face_logical_coords), time, functions_of_time);\n    InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n        element_logical_to_inertial_inv_jac{};\n    for (size_t logical_i = 0; logical_i < 3; ++logical_i) {\n      for (size_t inertial_i = 0; inertial_i < 3; ++inertial_i) {\n        element_logical_to_inertial_inv_jac.get(logical_i, inertial_i) =\n            element_logical_to_grid_inv_jac.get(logical_i, 0) *\n            grid_to_inertial_inv_jac.get(0, inertial_i);\n        for (size_t grid_i = 1; grid_i < 3; ++grid_i) {\n          element_logical_to_inertial_inv_jac.get(logical_i, inertial_i) +=\n              element_logical_to_grid_inv_jac.get(logical_i, grid_i) *\n              grid_to_inertial_inv_jac.get(grid_i, inertial_i);\n        }\n      }\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      unnormalized_covector.get(i) =\n          element_logical_to_inertial_inv_jac.get(direction.dimension(), i);\n    }\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        inverse_spatial_metric_tag,\n        evolution::dg::Actions::detail::NormalVector<3>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n    fields_on_face.assign_subset(solution.variables(\n        grid_to_inertial_map(logical_to_grid_map(face_logical_coords), time,\n                             functions_of_time),\n        time, tmpl::list<inverse_spatial_metric_tag>{}));\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<System>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, grid_to_inertial_map);\n  }\n\n  auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Element<3>, domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>, typename System::variables_tag,\n      Tags::TildeJ, typename System::spacetime_variables_tag,\n      evolution::dg::subcell::Tags::OnSubcellFaces<\n          typename System::flux_spacetime_variables_tag, 3>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<3>,\n      fd::Tags::Reconstructor, evolution::Tags::BoundaryCorrection,\n      ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::ElementMap<3, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                  Frame::Inertial>,\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::ElementLogical>,\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n      domain::Tags::MeshVelocity<3>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<3>,\n      ::Tags::AnalyticSolution<SolutionForTest>,\n      evolution::dg::subcell::Tags::SubcellOptions<3>>>(\n      element, dg_mesh, subcell_mesh, dg_evolved_vars, dg_tilde_j, dg_gr_vars,\n      face_centered_gr_vars, ghost_data,\n      std::unique_ptr<fd::Reconstructor>{\n          std::make_unique<ReconstructionForTest>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<BoundaryCorrectionForTest>()},\n      time, clone_unique_ptrs(functions_of_time),\n      ElementMap<3, Frame::Grid>{\n          ElementId<3>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<3>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{}),\n      subcell_logical_coords, subcell_inertial_coords,\n      std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>{}, normal_vectors,\n      solution,\n      evolution::dg::subcell::SubcellOptions{\n          4.0, 1_st, 1.0e-3, 1.0e-4, false, false,\n          evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n          std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1});\n\n  // Compute the packaged data\n  std::vector<DirectionalId<3>> mortars_to_reconstruct_to{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    mortars_to_reconstruct_to.emplace_back(\n        DirectionalId<3>{direction, *neighbors.begin()});\n  }\n  const auto packaged_data =\n      subcell::NeighborPackagedData::apply(box, mortars_to_reconstruct_to);\n\n  // Now for each directions, check that the packaged_data agrees with\n  // expected values\n  BoundaryCorrectionForTest boundary_corr_for_test{};\n  using dg_package_field_tags =\n      typename BoundaryCorrectionForTest::dg_package_field_tags;\n  using dg_package_data_temporary_tags =\n      typename BoundaryCorrectionForTest::dg_package_data_temporary_tags;\n\n  Variables<tmpl::append<\n      tmpl::list<Tags::TildeJ>, evolved_vars_tags, fluxes_tags,\n      tmpl::remove_duplicates<tmpl::push_back<\n          dg_package_data_temporary_tags,\n          gr::Tags::SpatialMetric<DataVector, 3>,\n          gr::Tags::SqrtDetSpatialMetric<DataVector>,\n          gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>,\n          evolution::dg::Actions::detail::NormalVector<3>>>>>\n      vars_on_mortar_face{0};\n  Variables<dg_package_field_tags> expected_fd_packaged_data_on_mortar{0};\n\n  for (const auto& mortar_id : mortars_to_reconstruct_to) {\n    const Direction<3>& direction = mortar_id.direction();\n    const size_t dim = direction.dimension();\n    Index<3> extents = subcell_mesh.extents();\n\n    ++extents[dim];\n    const size_t num_mortar_face_pts =\n        subcell_mesh.extents().slice_away(dim).product();\n\n    vars_on_mortar_face.initialize(num_mortar_face_pts);\n\n    // reconstruct TildeJ and evolved vars on the mortar\n    auto recons_vars_on_mortar_face =\n        vars_on_mortar_face.template reference_subset<\n            ForceFree::fd::tags_list_for_reconstruction>();\n    dynamic_cast<const ReconstructionForTest&>(reconstructor)\n        .reconstruct_fd_neighbor(make_not_null(&recons_vars_on_mortar_face),\n                                 volume_vars_subcell, subcell_tilde_j, element,\n                                 ghost_data, subcell_mesh, direction);\n\n    // retrieve face-centered GR vars and slice it on the mortar, then\n    // compute fluxes with it\n    tmpl::for_each<System::flux_spacetime_variables_tag::tags_list>(\n        [&dim, &direction, &extents, &vars_on_mortar_face,\n         &gr_vars_on_faces =\n             db::get<evolution::dg::subcell::Tags::OnSubcellFaces<\n                 typename System::flux_spacetime_variables_tag, 3>>(box)](\n            auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          data_on_slice(make_not_null(&get<tag>(vars_on_mortar_face)),\n                        get<tag>(gsl::at(gr_vars_on_faces, dim)), extents, dim,\n                        direction.side() == Side::Lower\n                            ? 0\n                            : extents[direction.dimension()] - 1);\n        });\n    subcell::compute_fluxes(make_not_null(&vars_on_mortar_face));\n\n    // revert normal vector and project to DG grid\n    auto normal_covector = get<evolution::dg::Tags::NormalCovector<3>>(\n        *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<3>>(box).at(\n            mortar_id.direction()));\n    for (auto& t : normal_covector) {\n      t *= -1.0;\n    }\n    const auto dg_normal_covector = normal_covector;\n    for (size_t i = 0; i < 3; ++i) {\n      normal_covector.get(i) = evolution::dg::subcell::fd::project(\n          dg_normal_covector.get(i),\n          dg_mesh.slice_away(mortar_id.direction().dimension()),\n          subcell_mesh.extents().slice_away(mortar_id.direction().dimension()));\n    }\n\n    // Compute the expected packaged data\n    expected_fd_packaged_data_on_mortar.initialize(num_mortar_face_pts);\n    using dg_package_data_projected_tags =\n        tmpl::append<evolved_vars_tags, fluxes_tags,\n                     dg_package_data_temporary_tags>;\n    evolution::dg::Actions::detail::dg_package_data<System>(\n        make_not_null(&expected_fd_packaged_data_on_mortar),\n        boundary_corr_for_test, vars_on_mortar_face, normal_covector,\n        {std::nullopt}, box,\n        typename BoundaryCorrectionForTest::dg_package_data_volume_tags{},\n        dg_package_data_projected_tags{});\n\n    // reconstruct from FD to DG grid for 2d\n    auto expected_dg_packaged_data = evolution::dg::subcell::fd::reconstruct(\n        expected_fd_packaged_data_on_mortar,\n        dg_mesh.slice_away(mortar_id.direction().dimension()),\n        subcell_mesh.extents().slice_away(mortar_id.direction().dimension()),\n        evolution::dg::subcell::fd::ReconstructionMethod::DimByDim);\n\n    const DataVector vector_to_check{expected_dg_packaged_data.data(),\n                                     expected_dg_packaged_data.size()};\n\n    CHECK_ITERABLE_APPROX(vector_to_check, packaged_data.at(mortar_id));\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ForceFree.Subcell.NeighborPackagedData\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  test_neighbor_packaged_data(make_not_null(&gen));\n}\n\n}  // namespace\n}  // namespace ForceFree\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Subcell/Test_SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/RdmpTciData.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ForceFree::subcell {\nnamespace {\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Subcell.SetInitialRdmpData\",\n                  \"[Unit][Evolution]\") {\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using EvolvedVars = typename System::variables_tag::type;\n  EvolvedVars dg_vars{dg_mesh.number_of_grid_points(), 1.0};\n\n  // While the code is supposed to be used on the subcells, that doesn't\n  // actually matter.\n\n  const auto subcell_vars = evolution::dg::subcell::fd::project(\n      dg_vars, dg_mesh, subcell_mesh.extents());\n\n  evolution::dg::subcell::RdmpTciData rdmp_data{};\n  ForceFree::subcell::SetInitialRdmpData::apply(\n      make_not_null(&rdmp_data), get<ForceFree::Tags::TildeE>(dg_vars),\n      get<ForceFree::Tags::TildeB>(dg_vars),\n      evolution::dg::subcell::ActiveGrid::Dg, dg_mesh, subcell_mesh);\n\n  const auto& dg_tilde_e_magnitude =\n      magnitude(get<ForceFree::Tags::TildeE>(dg_vars));\n  const auto dg_tilde_b_magnitude =\n      magnitude(get<ForceFree::Tags::TildeB>(dg_vars));\n\n  const auto projected_subcell_tilde_e_magnitude =\n      Scalar<DataVector>(evolution::dg::subcell::fd::project(\n          get(dg_tilde_e_magnitude), dg_mesh, subcell_mesh.extents()));\n  const auto projected_subcell_tilde_b_magnitude =\n      Scalar<DataVector>(evolution::dg::subcell::fd::project(\n          get(dg_tilde_b_magnitude), dg_mesh, subcell_mesh.extents()));\n\n  evolution::dg::subcell::RdmpTciData expected_dg_rdmp_data{};\n  using std::max;\n  using std::min;\n  expected_dg_rdmp_data.max_variables_values =\n      DataVector{max(max(get(dg_tilde_e_magnitude)),\n                     max(get(projected_subcell_tilde_e_magnitude))),\n                 max(max(get(dg_tilde_b_magnitude)),\n                     max(get(projected_subcell_tilde_b_magnitude)))};\n  expected_dg_rdmp_data.min_variables_values =\n      DataVector{min(min(get(dg_tilde_e_magnitude)),\n                     min(get(projected_subcell_tilde_e_magnitude))),\n                 min(min(get(dg_tilde_b_magnitude)),\n                     min(get(projected_subcell_tilde_b_magnitude)))};\n\n  CHECK(rdmp_data == expected_dg_rdmp_data);\n\n  ForceFree::subcell::SetInitialRdmpData::apply(\n      make_not_null(&rdmp_data), get<ForceFree::Tags::TildeE>(subcell_vars),\n      get<ForceFree::Tags::TildeB>(subcell_vars),\n      evolution::dg::subcell::ActiveGrid::Subcell, dg_mesh, subcell_mesh);\n\n  const auto subcell_tilde_e_magnitude =\n      magnitude(get<ForceFree::Tags::TildeE>(subcell_vars));\n  const auto subcell_tilde_b_magnitude =\n      magnitude(get<ForceFree::Tags::TildeB>(subcell_vars));\n\n  evolution::dg::subcell::RdmpTciData expected_subcell_rdmp_data{};\n  expected_subcell_rdmp_data.max_variables_values = DataVector{\n      max(get(subcell_tilde_e_magnitude)), max(get(subcell_tilde_b_magnitude))};\n  expected_subcell_rdmp_data.min_variables_values = DataVector{\n      min(get(subcell_tilde_e_magnitude)), min(get(subcell_tilde_b_magnitude))};\n\n  CHECK(rdmp_data == expected_subcell_rdmp_data);\n}\n\n}  // namespace\n}  // namespace ForceFree::subcell\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Subcell/Test_TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/TciOnDgGrid.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nenum class TestThis {\n  AllGood,\n  PerssonMagTildeE,\n  PerssonMagTildeB,\n  PerssonTildeQ,\n  PerssonTildeQBelowCutoff,\n  PerssonTildeQDoNotCheck,\n  RdmpMagTildeE,\n  RdmpMagTildeB\n};\n\nvoid test(const TestThis test_this, const int expected_tci_status) {\n  CAPTURE(test_this);\n  CAPTURE(expected_tci_status);\n\n  const Mesh<3> dg_mesh{6, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using TildeE = ForceFree::Tags::TildeE;\n  using TildeB = ForceFree::Tags::TildeB;\n  using TildeQ = ForceFree::Tags::TildeQ;\n\n  using VarsForTciTest = Variables<tmpl::list<TildeE, TildeB, TildeQ>>;\n  VarsForTciTest dg_vars{dg_mesh.number_of_grid_points(), 0.0};\n\n  // set variables on the dg mesh for the test\n  get(get<TildeQ>(dg_vars)) = 1.0;\n  for (size_t i = 0; i < 3; ++i) {\n    get<TildeE>(dg_vars).get(i) = 2.0;\n    get<TildeB>(dg_vars).get(i) = 3.0;\n  }\n\n  // Just use flat spacetime since none of the TCI checks depends on metric\n  // variables\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{\n      dg_mesh.number_of_grid_points(), 0.0};\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric{\n      dg_mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = inv_spatial_metric.get(i, i) = 1.0;\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      dg_mesh.number_of_grid_points(), 1.0};\n\n  const double persson_exponent = 4.0;\n\n  const evolution::dg::subcell::SubcellOptions subcell_options{\n      persson_exponent,\n      1_st,\n      1.0e-10,\n      1.0e-10,\n      false,\n      false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n      false,\n      std::nullopt,\n      fd::DerivativeOrder::Two,\n      1,\n      1,\n      1};\n\n  const ForceFree::subcell::TciOptions tci_options{1.0e-10};\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Tags::Variables<VarsForTciTest::tags_list>, ::domain::Tags::Mesh<3>,\n      ::evolution::dg::subcell::Tags::Mesh<3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      ForceFree::subcell::Tags::TciOptions,\n      evolution::dg::subcell::Tags::SubcellOptions<3>,\n      evolution::dg::subcell::Tags::DataForRdmpTci>>(\n      dg_vars, dg_mesh, subcell_mesh, sqrt_det_spatial_metric, spatial_metric,\n      inv_spatial_metric, tci_options, subcell_options,\n      evolution::dg::subcell::RdmpTciData{});\n\n  const size_t point_to_change = dg_mesh.number_of_grid_points() / 2;\n\n  if (test_this == TestThis::PerssonMagTildeE) {\n    db::mutate<TildeE>(\n        [point_to_change](const auto tilde_e_ptr) {\n          (*tilde_e_ptr).get(0)[point_to_change] *= 2.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonMagTildeB) {\n    db::mutate<TildeB>(\n        [point_to_change](const auto tilde_b_ptr) {\n          (*tilde_b_ptr).get(0)[point_to_change] *= 2.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeQ) {\n    db::mutate<TildeQ>(\n        [point_to_change](const auto tilde_q_ptr) {\n          get(*tilde_q_ptr)[point_to_change] *= 2.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeQBelowCutoff) {\n    db::mutate<TildeQ>(\n        [point_to_change](const auto tilde_q_ptr) {\n          get(*tilde_q_ptr)[point_to_change] *= 2.0;\n          get(*tilde_q_ptr) *= 1e-20;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeQDoNotCheck) {\n    db::mutate<TildeQ, ForceFree::subcell::Tags::TciOptions>(\n        [point_to_change](const auto tilde_q_ptr, const auto tci_options_ptr) {\n          get(*tilde_q_ptr)[point_to_change] *= 2.0;\n          tci_options_ptr->tilde_q_cutoff = std::nullopt;\n        },\n        make_not_null(&box));\n  }\n\n  // Set the RDMP TCI past data.\n  using std::max;\n  using std::min;\n\n  const auto dg_mag_tilde_e = get(magnitude(db::get<TildeE>(box)));\n  const auto dg_mag_tilde_b = get(magnitude(db::get<TildeB>(box)));\n  const auto dg_tilde_q = get(db::get<TildeQ>(box));\n  const auto subcell_mag_tilde_e = evolution::dg::subcell::fd::project(\n      dg_mag_tilde_e, dg_mesh, subcell_mesh.extents());\n  const auto subcell_mag_tilde_b = evolution::dg::subcell::fd::project(\n      dg_mag_tilde_b, dg_mesh, subcell_mesh.extents());\n\n  evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{};\n  past_rdmp_tci_data.max_variables_values =\n      DataVector{max(max(dg_mag_tilde_e), max(subcell_mag_tilde_e)),\n                 max(max(dg_mag_tilde_b), max(subcell_mag_tilde_b))};\n  past_rdmp_tci_data.min_variables_values =\n      DataVector{min(min(dg_mag_tilde_e), min(subcell_mag_tilde_e)),\n                 min(min(dg_mag_tilde_b), min(subcell_mag_tilde_b))};\n\n  const evolution::dg::subcell::RdmpTciData expected_rdmp_tci_data =\n      past_rdmp_tci_data;\n\n  // Modify past data if we are expecting an RDMP TCI failure.\n  db::mutate<evolution::dg::subcell::Tags::DataForRdmpTci>(\n      [&past_rdmp_tci_data, &test_this](const auto rdmp_tci_data_ptr) {\n        *rdmp_tci_data_ptr = past_rdmp_tci_data;\n        // Assumes min is positive, increase it so we fail the TCI\n        if (test_this == TestThis::RdmpMagTildeE) {\n          rdmp_tci_data_ptr->min_variables_values[0] *= 1.01;\n        } else if (test_this == TestThis::RdmpMagTildeB) {\n          rdmp_tci_data_ptr->min_variables_values[1] *= 1.01;\n        }\n      },\n      make_not_null(&box));\n\n  const bool element_stays_on_dg = false;\n  const std::tuple<int, evolution::dg::subcell::RdmpTciData> result =\n      db::mutate_apply<ForceFree::subcell::TciOnDgGrid>(\n          make_not_null(&box), persson_exponent, element_stays_on_dg);\n\n  CHECK(get<1>(result) == expected_rdmp_tci_data);\n\n  if (test_this == TestThis::AllGood) {\n    CHECK_FALSE(get<0>(result));\n  } else {\n    CHECK(get<0>(result) == expected_tci_status);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.ForceFree.Systems.Subcell.TciOnDgGrid\",\n                  \"[Unit][Evolution]\") {\n  test(TestThis::AllGood, 0);\n  test(TestThis::PerssonMagTildeE, -1);\n  test(TestThis::PerssonMagTildeB, -2);\n  test(TestThis::PerssonTildeQ, -3);\n  test(TestThis::PerssonTildeQBelowCutoff, 0);\n  test(TestThis::PerssonTildeQDoNotCheck, 0);\n  test(TestThis::RdmpMagTildeE, -4);\n  test(TestThis::RdmpMagTildeB, -5);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Subcell/Test_TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/TciOnFdGrid.hpp\"\n#include \"Evolution/Systems/ForceFree/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ForceFree/System.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nenum class TestThis {\n  AllGood,\n  PerssonMagTildeE,\n  PerssonMagTildeB,\n  PerssonTildeQ,\n  PerssonTildeQBelowCutoff,\n  PerssonTildeQDoNotCheck,\n  RdmpMagTildeE,\n  RdmpMagTildeB\n};\n\nvoid test(const TestThis test_this, const int expected_tci_status) {\n  CAPTURE(test_this);\n  CAPTURE(expected_tci_status);\n\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using TildeE = ForceFree::Tags::TildeE;\n  using TildeB = ForceFree::Tags::TildeB;\n  using TildeQ = ForceFree::Tags::TildeQ;\n\n  using VarsForTciTest = Variables<tmpl::list<TildeE, TildeB, TildeQ>>;\n  VarsForTciTest subcell_vars{subcell_mesh.number_of_grid_points(), 0.0};\n\n  // set variables on the dg mesh for the test\n  get(get<TildeQ>(subcell_vars)) = 1.0;\n  for (size_t i = 0; i < 3; ++i) {\n    get<TildeE>(subcell_vars).get(i) = 2.0;\n    get<TildeB>(subcell_vars).get(i) = 3.0;\n  }\n\n  const double persson_exponent = 5.0;\n\n  const evolution::dg::subcell::SubcellOptions subcell_options{\n      persson_exponent,\n      1_st,\n      1.0e-10,\n      1.0e-10,\n      false,\n      false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n      false,\n      std::nullopt,\n      fd::DerivativeOrder::Two,\n      1,\n      1,\n      1};\n\n  const ForceFree::subcell::TciOptions tci_options{1.0e-10};\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Tags::Variables<VarsForTciTest::tags_list>, ::domain::Tags::Mesh<3>,\n      ::evolution::dg::subcell::Tags::Mesh<3>,\n      ForceFree::subcell::Tags::TciOptions,\n      evolution::dg::subcell::Tags::SubcellOptions<3>,\n      evolution::dg::subcell::Tags::DataForRdmpTci>>(\n      subcell_vars, dg_mesh, subcell_mesh, tci_options, subcell_options,\n      evolution::dg::subcell::RdmpTciData{});\n\n  const size_t point_to_change = subcell_mesh.number_of_grid_points() / 2;\n\n  if (test_this == TestThis::PerssonMagTildeE) {\n    db::mutate<TildeE>(\n        [point_to_change](const auto tilde_e_ptr) {\n          for (size_t i = 0; i < 3; ++i) {\n            tilde_e_ptr->get(i)[point_to_change] *= 10.0;\n          }\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonMagTildeB) {\n    db::mutate<TildeB>(\n        [point_to_change](const auto tilde_b_ptr) {\n          for (size_t i = 0; i < 3; ++i) {\n            tilde_b_ptr->get(i)[point_to_change] *= 10.0;\n          }\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeQ) {\n    db::mutate<TildeQ>(\n        [point_to_change](const auto tilde_q_ptr) {\n          get(*tilde_q_ptr)[point_to_change] *= 10.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeQBelowCutoff) {\n    db::mutate<TildeQ>(\n        [point_to_change](const auto tilde_q_ptr) {\n          get(*tilde_q_ptr)[point_to_change] *= 10.0;\n          get(*tilde_q_ptr) *= 1e-20;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeQDoNotCheck) {\n    db::mutate<TildeQ, ForceFree::subcell::Tags::TciOptions>(\n        [point_to_change](const auto tilde_q_ptr, const auto tci_options_ptr) {\n          get(*tilde_q_ptr)[point_to_change] *= 10.0;\n          tci_options_ptr->tilde_q_cutoff = std::nullopt;\n        },\n        make_not_null(&box));\n  }\n\n  // Set the RDMP TCI past data.\n  using std::max;\n  using std::min;\n  const auto DimByDim =\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim;\n\n  const auto subcell_mag_tilde_e = get(magnitude(db::get<TildeE>(box)));\n  const auto subcell_mag_tilde_b = get(magnitude(db::get<TildeB>(box)));\n  const auto subcell_tilde_q = get(db::get<TildeQ>(box));\n  const auto dg_mag_tilde_e = evolution::dg::subcell::fd::reconstruct(\n      subcell_mag_tilde_e, dg_mesh, subcell_mesh.extents(), DimByDim);\n  const auto dg_mag_tilde_b = evolution::dg::subcell::fd::reconstruct(\n      subcell_mag_tilde_b, dg_mesh, subcell_mesh.extents(), DimByDim);\n\n  evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{};\n  past_rdmp_tci_data.max_variables_values =\n      DataVector{max(max(dg_mag_tilde_e), max(subcell_mag_tilde_e)),\n                 max(max(dg_mag_tilde_b), max(subcell_mag_tilde_b))};\n  past_rdmp_tci_data.min_variables_values =\n      DataVector{min(min(dg_mag_tilde_e), min(subcell_mag_tilde_e)),\n                 min(min(dg_mag_tilde_b), min(subcell_mag_tilde_b))};\n\n  // Note : RDMP TCI data consists of max and min of subcell variables only when\n  // using FD grid\n  evolution::dg::subcell::RdmpTciData expected_rdmp_tci_data{};\n  expected_rdmp_tci_data.max_variables_values =\n      DataVector{max(subcell_mag_tilde_e), max(subcell_mag_tilde_b)};\n  expected_rdmp_tci_data.min_variables_values =\n      DataVector{min(subcell_mag_tilde_e), min(subcell_mag_tilde_b)};\n\n  // Modify past data if we are expecting an RDMP TCI failure.\n  db::mutate<evolution::dg::subcell::Tags::DataForRdmpTci>(\n      [&past_rdmp_tci_data, &test_this](const auto rdmp_tci_data_ptr) {\n        *rdmp_tci_data_ptr = past_rdmp_tci_data;\n        // Assumes min is positive, increase it so we fail the TCI\n        if (test_this == TestThis::RdmpMagTildeE) {\n          rdmp_tci_data_ptr->min_variables_values[0] *= 1.01;\n        } else if (test_this == TestThis::RdmpMagTildeB) {\n          rdmp_tci_data_ptr->min_variables_values[1] *= 1.01;\n        }\n      },\n      make_not_null(&box));\n\n  const std::tuple<int, evolution::dg::subcell::RdmpTciData> result =\n      db::mutate_apply<ForceFree::subcell::TciOnFdGrid>(\n          make_not_null(&box), persson_exponent, false);\n\n  CHECK(get<1>(result) == expected_rdmp_tci_data);\n\n  if (test_this == TestThis::AllGood) {\n    CHECK_FALSE(get<0>(result));\n  } else {\n    CHECK(get<0>(result) == expected_tci_status);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Subcell.TciOnFdGrid\",\n                  \"[Unit][Evolution]\") {\n  test(TestThis::AllGood, 0);\n  test(TestThis::PerssonMagTildeE, 1);\n  test(TestThis::PerssonMagTildeB, 2);\n  test(TestThis::PerssonTildeQ, 3);\n  test(TestThis::PerssonTildeQBelowCutoff, 0);\n  test(TestThis::PerssonTildeQDoNotCheck, 0);\n  test(TestThis::RdmpMagTildeE, 4);\n  test(TestThis::RdmpMagTildeB, 5);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Subcell/Test_TciOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ForceFree/Subcell/TciOptions.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Subcell.TciOptions\",\n                  \"[Unit][Evolution]\") {\n  const auto tci_options_from_opts =\n      TestHelpers::test_option_tag<ForceFree::subcell::OptionTags::TciOptions>(\n          \"TildeQCutoff: 1.0e-10\\n\");\n  const auto tci_options = serialize_and_deserialize(tci_options_from_opts);\n  CHECK(tci_options.tilde_q_cutoff  == 1.0e-10);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ForceFree/Characteristics.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Characteristics\",\n                  \"[Unit][Evolution]\") {\n  // test for tags\n  TestHelpers::db::test_compute_tag<\n      ForceFree::Tags::LargestCharacteristicSpeedCompute>(\n      \"LargestCharacteristicSpeed\");\n\n  // test for randomly generated fields\n  MAKE_GENERATOR(gen);\n  const DataVector used_for_size(5);\n\n  const auto lapse =\n      TestHelpers::gr::random_lapse(make_not_null(&gen), used_for_size);\n  const auto shift =\n      TestHelpers::gr::random_shift<3>(make_not_null(&gen), used_for_size);\n  const auto spatial_metric = TestHelpers::gr::random_spatial_metric<3>(\n      make_not_null(&gen), used_for_size);\n\n  const auto shift_magnitude = magnitude(shift, spatial_metric);\n\n  // Check that locally computed value matches the returned one\n  double largest_characteristic_speed =\n      std::numeric_limits<double>::signaling_NaN();\n  ForceFree::Tags::LargestCharacteristicSpeedCompute::function(\n      make_not_null(&largest_characteristic_speed), lapse, shift,\n      spatial_metric);\n  CHECK(largest_characteristic_speed == max(get(shift_magnitude) + get(lapse)));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Test_ElectricCurrentDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/ForceFree/ElectricCurrentDensity.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.ElectricCurrentDensity\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ForceFree\"};\n\n  pypp::check_with_random_values<1>(&ForceFree::ComputeDriftTildeJ::apply,\n                                    \"ElectricCurrentDensity\", {\"tilde_j_drift\"},\n                                    {{{-1.0, 1.0}}}, DataVector{5}, 1.0e-10);\n\n  pypp::check_with_random_values<1>(\n      &ForceFree::ComputeParallelTildeJ::apply, \"ElectricCurrentDensity\",\n      {\"tilde_j_parallel\"}, {{{-1.0, 1.0}}}, DataVector{5}, 1.0e-10);\n\n  pypp::check_with_random_values<1>(&ForceFree::Tags::ComputeTildeJ::function,\n                                    \"ElectricCurrentDensity\", {\"tilde_j\"},\n                                    {{{-1.0, 1.0}}}, DataVector{5}, 1.0e-10);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Test_ElectromagneticVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/ForceFree/ElectromagneticVariables.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.ElectromagneticVariables\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ForceFree\"};\n\n  pypp::check_with_random_values<1>(\n      ForceFree::Tags::ElectricFieldCompute::function,\n      \"ElectromagneticVariables\", {\"electric_field_compute\"}, {{{-1.0, 1.0}}},\n      DataVector{5});\n\n  pypp::check_with_random_values<1>(\n      ForceFree::Tags::MagneticFieldCompute::function,\n      \"ElectromagneticVariables\", {\"magnetic_field_compute\"}, {{{-1.0, 1.0}}},\n      DataVector{5});\n\n  pypp::check_with_random_values<1>(\n      ForceFree::Tags::ChargeDensityCompute::function,\n      \"ElectromagneticVariables\", {\"charge_density_compute\"}, {{{-1.0, 1.0}}},\n      DataVector{5});\n\n  pypp::check_with_random_values<1>(\n      ForceFree::Tags::ElectricCurrentDensityCompute::function,\n      \"ElectromagneticVariables\", {\"electric_current_density_compute\"},\n      {{{-1.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Test_Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/ForceFree/Fluxes.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Fluxes\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ForceFree\"};\n\n  pypp::check_with_random_values<1>(\n      &ForceFree::Fluxes::apply, \"Fluxes\",\n      {\"tilde_e_flux\", \"tilde_b_flux\", \"tilde_psi_flux\", \"tilde_phi_flux\",\n       \"tilde_q_flux\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Test_MaskNeutronStarInterior.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/Systems/ForceFree/MaskNeutronStarInterior.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/RotatingDipole.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ForceFree {\nnamespace {\n\nstruct MetavariablesForTest {\n  using component_list = tmpl::list<>;\n  using initial_data_list = tmpl::list<ForceFree::AnalyticData::RotatingDipole>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>>;\n  };\n};\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.MaskNsInterior\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ForceFree\"};\n\n  const size_t num_dg_pts = 5;\n\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  const auto logical_to_grid_map = ElementMap<3, Frame::Grid>{\n      ElementId<3>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<3>{})};\n\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const auto dg_logical_coords = logical_coordinates(dg_mesh);\n  const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n\n  const auto dg_inertial_coords =\n      (*grid_to_inertial_map)(logical_to_grid_map(dg_logical_coords));\n  const auto subcell_inertial_coords =\n      (*grid_to_inertial_map)(logical_to_grid_map(subcell_logical_coords));\n\n  auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Coordinates<3, Frame::Inertial>,\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n      Tags::NsInteriorMask,\n      evolution::dg::subcell::Tags::Inactive<Tags::NsInteriorMask>,\n      evolution::initial_data::Tags::InitialData>>(\n      dg_inertial_coords, subcell_inertial_coords,\n      std::optional<Scalar<DataVector>>{}, std::optional<Scalar<DataVector>>{},\n      AnalyticData::RotatingDipole{1.0, 0.1, 0.1, 0.1, 0.5}.get_clone());\n\n  // Apply the mutator and check the values of masking variables on both grids.\n  db::mutate_apply<MaskNeutronStarInterior<MetavariablesForTest, false>>(\n      make_not_null(&box));\n  db::mutate_apply<MaskNeutronStarInterior<MetavariablesForTest, true>>(\n      make_not_null(&box));\n\n  {\n    const auto dg_mask = get<Tags::NsInteriorMask>(box);\n    const Scalar<DataVector> dg_mask_from_python{pypp::call<Scalar<DataVector>>(\n        \"MaskNeutronStarInterior\", \"compute_ns_interior_mask\",\n        dg_inertial_coords)};\n    CHECK(dg_mask == dg_mask_from_python);\n\n    const auto subcell_mask =\n        get<evolution::dg::subcell::Tags::Inactive<Tags::NsInteriorMask>>(box);\n    const Scalar<DataVector> subcell_mask_from_python{\n        pypp::call<Scalar<DataVector>>(\"MaskNeutronStarInterior\",\n                                       \"compute_ns_interior_mask\",\n                                       subcell_inertial_coords)};\n    CHECK(subcell_mask == subcell_mask_from_python);\n  }\n\n  // Initialize the mask variables back to `null`, push the coordinates far\n  // away from the NS surface, then run the mutator again.\n  db::mutate<domain::Tags::Coordinates<3, Frame::Inertial>,\n             evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n             Tags::NsInteriorMask,\n             evolution::dg::subcell::Tags::Inactive<Tags::NsInteriorMask>>(\n      [](const auto dg_coords, const auto subcell_coords, const auto dg_mask,\n         const auto subcell_mask) {\n        for (size_t d = 0; d < 3; ++d) {\n          (*dg_coords).get(d) += 5.0;\n          (*subcell_coords).get(d) += 5.0;\n        }\n        *dg_mask = std::optional<Scalar<DataVector>>{};\n        *subcell_mask = std::optional<Scalar<DataVector>>{};\n      },\n      make_not_null(&box));\n\n  db::mutate_apply<MaskNeutronStarInterior<MetavariablesForTest, false>>(\n      make_not_null(&box));\n  db::mutate_apply<MaskNeutronStarInterior<MetavariablesForTest, true>>(\n      make_not_null(&box));\n\n  CHECK(get<Tags::NsInteriorMask>(box).has_value() == false);\n  CHECK(get<evolution::dg::subcell::Tags::Inactive<Tags::NsInteriorMask>>(box)\n            .has_value() == false);\n}\n\n}  // namespace\n}  // namespace ForceFree\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Test_Sources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/ForceFree/Sources.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Sources\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ForceFree\"};\n\n  pypp::check_with_random_values<1>(&ForceFree::Sources::apply, \"Sources\",\n                                    {\"source_tilde_e\", \"source_tilde_b\",\n                                     \"source_tilde_psi\", \"source_tilde_phi\"},\n                                    {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.Tags\",\n                  \"[Unit][Evolution]\") {\n  // Primitive variable tags\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::ElectricField>(\n      \"ElectricField\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::MagneticField>(\n      \"MagneticField\");\n  TestHelpers::db::test_simple_tag<\n      ForceFree::Tags::ElectricDivergenceCleaningField>(\n      \"ElectricDivergenceCleaningField\");\n  TestHelpers::db::test_simple_tag<\n      ForceFree::Tags::MagneticDivergenceCleaningField>(\n      \"MagneticDivergenceCleaningField\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::ChargeDensity>(\n      \"ChargeDensity\");\n\n  // Evolved variable tags\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::TildeE>(\"TildeE\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::TildeB>(\"TildeB\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::TildePsi>(\"TildePsi\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::TildePhi>(\"TildePhi\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::TildeQ>(\"TildeQ\");\n\n  // Electric current density & constraint damping\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::TildeJ>(\"TildeJ\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::ElectricCurrentDensity>(\n      \"ElectricCurrentDensity\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::KappaPsi>(\"KappaPsi\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::KappaPhi>(\"KappaPhi\");\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::ParallelConductivity>(\n      \"ParallelConductivity\");\n\n  // etc.\n  TestHelpers::db::test_simple_tag<ForceFree::Tags::NsInteriorMask>(\n      \"NsInteriorMask\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/Test_TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ForceFree/TimeDerivativeTerms.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid forward_to_time_deriv(\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_e,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_psi,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_phi,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_q,\n\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_psi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_q_flux,\n\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_e,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_psi, const Scalar<DataVector>& tilde_phi,\n    const Scalar<DataVector>& tilde_q,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_j_drift,\n    const double kappa_psi, const double kappa_phi,\n    const double parallel_conductivity,\n\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric) {\n  get(*non_flux_terms_dt_tilde_q) = 0.0;\n\n  Variables<typename ForceFree::TimeDerivativeTerms::temporary_tags> temp{\n      get(lapse).size(), 0.0};\n\n  ForceFree::TimeDerivativeTerms::apply(\n      non_flux_terms_dt_tilde_e, non_flux_terms_dt_tilde_b,\n      non_flux_terms_dt_tilde_psi, non_flux_terms_dt_tilde_phi,\n      non_flux_terms_dt_tilde_q,\n\n      tilde_e_flux, tilde_b_flux, tilde_psi_flux, tilde_phi_flux, tilde_q_flux,\n\n      make_not_null(&get<typename ForceFree::TimeDerivativeTerms::\n                             LapseTimesElectricFieldOneForm>(temp)),\n      make_not_null(&get<typename ForceFree::TimeDerivativeTerms::\n                             LapseTimesMagneticFieldOneForm>(temp)),\n      make_not_null(\n          &get<typename ForceFree::TimeDerivativeTerms::TildeJDrift>(temp)),\n\n      make_not_null(\n          &get<gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>>(temp)),\n      make_not_null(\n          &get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>>(temp)),\n      make_not_null(\n          &get<gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>(\n              temp)),\n\n      make_not_null(&get<gr::Tags::Lapse<DataVector>>(temp)),\n      make_not_null(&get<gr::Tags::Shift<DataVector, 3>>(temp)),\n      make_not_null(&get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp)),\n\n      tilde_e, tilde_b, tilde_psi, tilde_phi, tilde_q, tilde_j_drift, kappa_psi,\n      kappa_phi, parallel_conductivity,\n\n      lapse, shift, sqrt_det_spatial_metric, spatial_metric, inv_spatial_metric,\n      extrinsic_curvature, d_lapse, d_shift, d_spatial_metric);\n\n  CHECK(get<gr::Tags::Lapse<DataVector>>(temp) == lapse);\n  CHECK(get<gr::Tags::Shift<DataVector, 3>>(temp) == shift);\n  CHECK(get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp) ==\n        inv_spatial_metric);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ForceFree.TimeDerivativeTerms\",\n                  \"[Unit][GrMhd]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ForceFree\"};\n\n  pypp::check_with_random_values<1>(\n      &forward_to_time_deriv, \"TimeDerivative\",\n      {\"source_tilde_e\", \"source_tilde_b\", \"source_tilde_psi\",\n       \"source_tilde_phi\", \"source_tilde_q\", \"tilde_e_flux\", \"tilde_b_flux\",\n       \"tilde_psi_flux\", \"tilde_phi_flux\", \"tilde_q_flux\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ForceFree/TimeDerivative.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Fluxes\nimport numpy as np\nimport Sources\n\n\ndef tilde_e_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Fluxes.tilde_e_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        tilde_j,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef tilde_b_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Fluxes.tilde_b_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        tilde_j,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef tilde_psi_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Fluxes.tilde_psi_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        tilde_j,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef tilde_phi_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Fluxes.tilde_phi_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        tilde_j,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef tilde_q_flux(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Fluxes.tilde_q_flux(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        tilde_j,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef source_tilde_e(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Sources.source_tilde_e(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        kappa_psi,\n        kappa_phi,\n        parallel_conductivity,\n        lapse,\n        d_lapse,\n        d_shift,\n        d_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n        extrinsic_curvature,\n    )\n\n\ndef source_tilde_b(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Sources.source_tilde_b(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        kappa_psi,\n        kappa_phi,\n        parallel_conductivity,\n        lapse,\n        d_lapse,\n        d_shift,\n        d_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n        extrinsic_curvature,\n    )\n\n\ndef source_tilde_psi(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Sources.source_tilde_psi(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        kappa_psi,\n        kappa_phi,\n        parallel_conductivity,\n        lapse,\n        d_lapse,\n        d_shift,\n        d_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n        extrinsic_curvature,\n    )\n\n\ndef source_tilde_phi(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return Sources.source_tilde_phi(\n        tilde_e,\n        tilde_b,\n        tilde_psi,\n        tilde_phi,\n        tilde_q,\n        kappa_psi,\n        kappa_phi,\n        parallel_conductivity,\n        lapse,\n        d_lapse,\n        d_shift,\n        d_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n        extrinsic_curvature,\n    )\n\n\ndef source_tilde_q(\n    tilde_e,\n    tilde_b,\n    tilde_psi,\n    tilde_phi,\n    tilde_q,\n    tilde_j,\n    kappa_psi,\n    kappa_phi,\n    parallel_conductivity,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n):\n    return 0.0 * lapse\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Actions/Test_SetInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.tpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace gh {\nnamespace {\n\nusing gh_system_vars = gh::System<3>::variables_tag::tags_list;\n\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using component_being_mocked = void;  // Not needed\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using mutable_global_cache_tags =\n      tmpl::list<gh::Tags::SetPiAndPhiFromConstraints>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::append<\n              gh_system_vars,\n              tmpl::list<domain::Tags::Mesh<3>,\n                         domain::Tags::Coordinates<3, Frame::Inertial>,\n                         domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                                       Frame::Inertial>,\n                         ::Tags::Time>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<gh::Actions::SetInitialData,\n                     gh::Actions::ReceiveNumericInitialData>>>;\n};\n\nstruct MockReadVolumeData {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      DataBox& /*box*/, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const importers::ImporterOptions& options, const size_t volume_data_id,\n      tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n          importers::Tags::Selected, NumericInitialData::all_vars>>\n          selected_fields) {\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        get<evolution::initial_data::Tags::InitialData>(cache));\n    CHECK(options == initial_data.importer_options());\n    CHECK(volume_data_id == initial_data.volume_data_id());\n    const auto selected_vars = initial_data.selected_variables();\n    if (std::holds_alternative<NumericInitialData::GhVars>(selected_vars)) {\n      CHECK(get<importers::Tags::Selected<\n                gr::Tags::SpacetimeMetric<DataVector, 3>>>(selected_fields) ==\n            \"CustomSpacetimeMetric\");\n      CHECK(get<importers::Tags::Selected<Tags::Pi<DataVector, 3>>>(\n                selected_fields) == \"CustomPi\");\n      CHECK(get<importers::Tags::Selected<Tags::Phi<DataVector, 3>>>(\n                selected_fields) == \"CustomPhi\");\n      CHECK_FALSE(\n          get<importers::Tags::Selected<\n              gr::Tags::SpatialMetric<DataVector, 3>>>(selected_fields));\n      CHECK_FALSE(get<importers::Tags::Selected<gr::Tags::Lapse<DataVector>>>(\n          selected_fields));\n      CHECK_FALSE(\n          get<importers::Tags::Selected<gr::Tags::Shift<DataVector, 3>>>(\n              selected_fields));\n    } else if (std::holds_alternative<NumericInitialData::AdmVars>(\n                   selected_vars)) {\n      CHECK(get<importers::Tags::Selected<\n                gr::Tags::SpatialMetric<DataVector, 3>>>(selected_fields) ==\n            \"CustomSpatialMetric\");\n      CHECK(get<importers::Tags::Selected<gr::Tags::Lapse<DataVector>>>(\n                selected_fields) == \"CustomLapse\");\n      CHECK(get<importers::Tags::Selected<gr::Tags::Shift<DataVector, 3>>>(\n                selected_fields) == \"CustomShift\");\n      CHECK(get<importers::Tags::Selected<\n                gr::Tags::ExtrinsicCurvature<DataVector, 3>>>(\n                selected_fields) == \"CustomExtrinsicCurvature\");\n      CHECK_FALSE(\n          get<importers::Tags::Selected<\n              gr::Tags::SpacetimeMetric<DataVector, 3>>>(selected_fields));\n      CHECK_FALSE(get<importers::Tags::Selected<Tags::Pi<DataVector, 3>>>(\n          selected_fields));\n      CHECK_FALSE(get<importers::Tags::Selected<Tags::Phi<DataVector, 3>>>(\n          selected_fields));\n    } else {\n      REQUIRE(false);\n    }\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockVolumeDataReader {\n  using component_being_mocked = importers::ElementDataReader<Metavariables>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using replace_these_simple_actions =\n      tmpl::list<importers::Actions::ReadAllVolumeDataAndDistribute<\n          metavariables::volume_dim, NumericInitialData::all_vars,\n          MockElementArray<Metavariables>>>;\n  using with_these_simple_actions = tmpl::list<MockReadVolumeData>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  using component_list = tmpl::list<MockElementArray<Metavariables>,\n                                    MockVolumeDataReader<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        evolution::initial_data::InitialData,\n        tmpl::list<NumericInitialData,\n                   gh::Solutions::WrappedGr<gr::Solutions::KerrSchild>>>>;\n  };\n};\n\nvoid test_set_initial_data(\n    const evolution::initial_data::InitialData& initial_data,\n    const std::string& option_string, const bool is_numeric) {\n  {\n    INFO(\"Factory creation\");\n    const auto created = TestHelpers::test_creation<\n        std::unique_ptr<evolution::initial_data::InitialData>, Metavariables>(\n        option_string);\n    if (is_numeric) {\n      CHECK(dynamic_cast<const NumericInitialData&>(*created) ==\n            dynamic_cast<const NumericInitialData&>(initial_data));\n    } else {\n      CHECK(dynamic_cast<\n                const gh::Solutions::WrappedGr<gr::Solutions::KerrSchild>&>(\n                *created) ==\n            dynamic_cast<\n                const gh::Solutions::WrappedGr<gr::Solutions::KerrSchild>&>(\n                initial_data));\n    }\n  }\n\n  using reader_component = MockVolumeDataReader<Metavariables>;\n  using element_array = MockElementArray<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {initial_data.get_clone()}, {true}};\n\n  // Setup mock data file reader\n  ActionTesting::emplace_nodegroup_component<reader_component>(\n      make_not_null(&runner));\n\n  // Setup element\n  const ElementId<3> element_id{0};\n  const Mesh<3> mesh{8, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Wedge<3>{\n              2., 4., 1., 1., OrientationMap<3>::create_aligned(), true});\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto coords = map(logical_coords);\n  const auto inv_jacobian = map.inv_jacobian(logical_coords);\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), element_id,\n      {tnsr::aa<DataVector, 3>{}, tnsr::aa<DataVector, 3>{},\n       tnsr::iaa<DataVector, 3>{}, mesh, coords, inv_jacobian, 0.});\n\n  const auto get_element_tag = [&runner,\n                                &element_id](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  // We use a Kerr solution to generate data\n  gh::Solutions::WrappedGr<gr::Solutions::KerrSchild> kerr{\n      1., {{0., 0., 0.}}, {{0., 0., 0.}}};\n  const auto kerr_gh_vars = kerr.variables(coords, 0., gh_system_vars{});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const auto& cache = ActionTesting::cache<element_array>(runner, element_id);\n\n  // SetInitialData\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n\n  if (is_numeric) {\n    INFO(\"Numeric initial data\");\n    const auto& numeric_id =\n        dynamic_cast<const NumericInitialData&>(initial_data);\n\n    CHECK(Parallel::get<gh::Tags::SetPiAndPhiFromConstraints>(cache) ==\n          std::holds_alternative<NumericInitialData::AdmVars>(\n              numeric_id.selected_variables()));\n\n    REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n        make_not_null(&runner), element_id));\n\n    // MockReadVolumeData\n    ActionTesting::invoke_queued_simple_action<reader_component>(\n        make_not_null(&runner), 0);\n\n    // Insert KerrSchild data into the inbox\n    using inbox_tag = importers::Tags::VolumeData<NumericInitialData::all_vars>;\n    auto& inboxes =\n        ActionTesting::get_inbox_tag<element_array, inbox_tag, Metavariables>(\n            make_not_null(&runner), element_id);\n    auto& inbox = inboxes[numeric_id.volume_data_id()];\n    const auto& selected_vars = numeric_id.selected_variables();\n    if (std::holds_alternative<NumericInitialData::GhVars>(selected_vars)) {\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(inbox) =\n          get<gr::Tags::SpacetimeMetric<DataVector, 3>>(kerr_gh_vars);\n      get<Tags::Pi<DataVector, 3>>(inbox) =\n          get<Tags::Pi<DataVector, 3>>(kerr_gh_vars);\n      get<Tags::Phi<DataVector, 3>>(inbox) =\n          get<Tags::Phi<DataVector, 3>>(kerr_gh_vars);\n    } else if (std::holds_alternative<NumericInitialData::AdmVars>(\n                   selected_vars)) {\n      const auto kerr_adm_vars = kerr.variables(\n          coords, 0.,\n          tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                     gr::Tags::Lapse<DataVector>,\n                     gr::Tags::Shift<DataVector, 3>,\n                     gr::Tags::ExtrinsicCurvature<DataVector, 3>>{});\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(inbox) =\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(kerr_adm_vars);\n      get<gr::Tags::Lapse<DataVector>>(inbox) =\n          get<gr::Tags::Lapse<DataVector>>(kerr_adm_vars);\n      get<gr::Tags::Shift<DataVector, 3>>(inbox) =\n          get<gr::Tags::Shift<DataVector, 3>>(kerr_adm_vars);\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(inbox) =\n          get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(kerr_adm_vars);\n    } else {\n      REQUIRE(false);\n    }\n\n    const std::string inbox_output = inbox_tag::output_inbox(inboxes, 1_st);\n    const std::string expected_inbox_output =\n        MakeString{} << \" VolumeDataInbox:\\n\"\n                     << \"  Index: \" << numeric_id.volume_data_id() << \"\\n\";\n    CHECK(inbox_output == expected_inbox_output);\n\n    // ReceiveNumericInitialData\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              element_id);\n  } else {\n    CHECK(Parallel::get<gh::Tags::SetPiAndPhiFromConstraints>(cache));\n  }\n\n  // Check result. These variables are not particularly precise because we are\n  // taking numerical derivatives on a fairly coarse wedge-shaped grid.\n  Approx custom_approx = Approx::custom().epsilon(1.e-3).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get_element_tag(gr::Tags::SpacetimeMetric<DataVector, 3>{}),\n      (get<gr::Tags::SpacetimeMetric<DataVector, 3>>(kerr_gh_vars)),\n      custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get_element_tag(Tags::Pi<DataVector, 3>{}),\n                               (get<Tags::Pi<DataVector, 3>>(kerr_gh_vars)),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get_element_tag(Tags::Phi<DataVector, 3>{}),\n                               (get<Tags::Phi<DataVector, 3>>(kerr_gh_vars)),\n                               custom_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Gh.NumericInitialData\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  test_set_initial_data(\n      NumericInitialData{\"TestInitialData.h5\",\n                         \"VolumeData\",\n                         0.,\n                         {1.0e-9},\n                         false,\n                         NumericInitialData::GhVars{\"CustomSpacetimeMetric\",\n                                                    \"CustomPi\", \"CustomPhi\"}},\n      \"NumericInitialData:\\n\"\n      \"  FileGlob: TestInitialData.h5\\n\"\n      \"  Subgroup: VolumeData\\n\"\n      \"  ObservationValue: 0.\\n\"\n      \"  ObservationValueEpsilon: 1e-9\\n\"\n      \"  ElementsAreIdentical: False\\n\"\n      \"  Variables:\\n\"\n      \"    SpacetimeMetric: CustomSpacetimeMetric\\n\"\n      \"    Pi: CustomPi\\n\"\n      \"    Phi: CustomPhi\\n\",\n      true);\n  test_set_initial_data(\n      NumericInitialData{\n          \"TestInitialData.h5\", \"VolumeData\", 0., std::nullopt, false,\n          NumericInitialData::AdmVars{\"CustomSpatialMetric\", \"CustomLapse\",\n                                      \"CustomShift\",\n                                      \"CustomExtrinsicCurvature\"}},\n      \"NumericInitialData:\\n\"\n      \"  FileGlob: TestInitialData.h5\\n\"\n      \"  Subgroup: VolumeData\\n\"\n      \"  ObservationValue: 0.\\n\"\n      \"  ObservationValueEpsilon: Auto\\n\"\n      \"  ElementsAreIdentical: False\\n\"\n      \"  Variables:\\n\"\n      \"    SpatialMetric: CustomSpatialMetric\\n\"\n      \"    Lapse: CustomLapse\\n\"\n      \"    Shift: CustomShift\\n\"\n      \"    ExtrinsicCurvature: CustomExtrinsicCurvature\",\n      true);\n  test_set_initial_data(\n      gh::Solutions::WrappedGr<gr::Solutions::KerrSchild>{\n          1., {{0., 0., 0.}}, {{0., 0., 0.}}},\n      \"GeneralizedHarmonic(KerrSchild):\\n\"\n      \"  Mass: 1.\\n\"\n      \"  Spin: [0, 0, 0]\\n\"\n      \"  Center: [0, 0, 0]\\n\"\n      \"  Velocity: [0, 0, 0]\",\n      false);\n}\n\n}  // namespace gh\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport itertools as it\n\nimport Evolution.Systems.GeneralizedHarmonic.Characteristics as gh_characteristics\nimport Evolution.Systems.GeneralizedHarmonic.Constraints as gh_constraints\nimport numpy as np\nimport PointwiseFunctions.GeneralRelativity.Christoffel as ch\nimport PointwiseFunctions.GeneralRelativity.ComputeGhQuantities as gh\nimport PointwiseFunctions.GeneralRelativity.ComputeSpacetimeQuantities as gr\nimport PointwiseFunctions.GeneralRelativity.InterfaceNullNormal as nn\nimport PointwiseFunctions.GeneralRelativity.ProjectionOperators as proj\nimport PointwiseFunctions.GeneralRelativity.WeylPropagating as wp\n\n\ndef constraint_preserving_corrections_dt_v_psi(\n    unit_interface_normal_vector, three_index_constraint, char_speeds\n):\n    return char_speeds[0] * np.einsum(\n        \"i,iab->ab\", unit_interface_normal_vector, three_index_constraint\n    )\n\n\ndef constraint_preserving_corrections_dt_v_zero(\n    unit_interface_normal_vector, four_index_constraint, char_speeds\n):\n    spatial_dim = len(unit_interface_normal_vector)\n    result = np.zeros([spatial_dim, 1 + spatial_dim, 1 + spatial_dim])\n\n    if spatial_dim == 2:\n        result[0, :, :] += (\n            char_speeds[1]\n            * unit_interface_normal_vector[1]\n            * four_index_constraint[1, :, :]\n        )\n        result[1, :, :] += (\n            char_speeds[1]\n            * unit_interface_normal_vector[0]\n            * four_index_constraint[0, :, :]\n        )\n    elif spatial_dim == 3:\n\n        def is_even(sequence):\n            count = 0\n            for i, n in enumerate(sequence, start=1):\n                count += sum(n > num for num in sequence[i:])\n            return not count % 2\n\n        for p in it.permutations(np.arange(len(unit_interface_normal_vector))):\n            sgn = 1 if is_even(p) else -1\n            result[p[0], :, :] += (\n                sgn\n                * char_speeds[1]\n                * unit_interface_normal_vector[p[2]]\n                * four_index_constraint[p[1], :, :]\n            )\n    return result\n\n\ndef add_gauge_sommerfeld_terms_to_dt_v_minus(\n    gamma2,\n    inertial_coords,\n    incoming_null_one_form,\n    outgoing_null_one_form,\n    incoming_null_vector,\n    outgoing_null_vector,\n    projection_Ab,\n    char_projected_rhs_dt_v_psi,\n):\n    gauge_bc_coeff = 1.0\n    inertial_radius = np.sum(inertial_coords**2) ** 0.5\n    prefac = gamma2 - gauge_bc_coeff / inertial_radius\n\n    t1_ = np.einsum(\n        \"a,cb,d,cd->ab\",\n        incoming_null_one_form,\n        projection_Ab,\n        outgoing_null_vector,\n        char_projected_rhs_dt_v_psi,\n    )\n    t2_ = np.einsum(\n        \"b,ca,d,cd->ab\",\n        incoming_null_one_form,\n        projection_Ab,\n        outgoing_null_vector,\n        char_projected_rhs_dt_v_psi,\n    )\n    t3_ = np.einsum(\n        \"a,b,c,d,cd->ab\",\n        incoming_null_one_form,\n        outgoing_null_one_form,\n        incoming_null_vector,\n        outgoing_null_vector,\n        char_projected_rhs_dt_v_psi,\n    )\n    t4_ = np.einsum(\n        \"b,a,c,d,cd->ab\",\n        incoming_null_one_form,\n        outgoing_null_one_form,\n        incoming_null_vector,\n        outgoing_null_vector,\n        char_projected_rhs_dt_v_psi,\n    )\n    t5_ = np.einsum(\n        \"a,b,c,d,cd->ab\",\n        incoming_null_one_form,\n        incoming_null_one_form,\n        outgoing_null_vector,\n        outgoing_null_vector,\n        char_projected_rhs_dt_v_psi,\n    )\n    return prefac * (t1_ + t2_ - t3_ - t4_ - t5_)\n\n\ndef add_constraint_dependent_terms_to_dt_v_minus(\n    outgoing_null_one_form,\n    incoming_null_vector,\n    outgoing_null_vector,\n    projection_ab,\n    projection_Ab,\n    projection_AB,\n    constraint_char_zero_plus,\n    constraint_char_zero_minus,\n    char_projected_rhs_dt_v_minus,\n    char_speeds,\n):\n    mu = 0.0  # hard-coded value from SpEC Bbh input file Mu = 0\n\n    t1_ = np.einsum(\n        \"c,d,a,b,cd->ab\",\n        incoming_null_vector,\n        incoming_null_vector,\n        outgoing_null_one_form,\n        outgoing_null_one_form,\n        char_projected_rhs_dt_v_minus,\n    )\n    t2_ = np.einsum(\n        \"c,da,b,cd->ab\",\n        incoming_null_vector,\n        projection_Ab,\n        outgoing_null_one_form,\n        char_projected_rhs_dt_v_minus,\n    )\n    t3_ = np.einsum(\n        \"c,db,a,cd->ab\",\n        incoming_null_vector,\n        projection_Ab,\n        outgoing_null_one_form,\n        char_projected_rhs_dt_v_minus,\n    )\n    t4_ = np.einsum(\n        \"d,ca,b,cd->ab\",\n        incoming_null_vector,\n        projection_Ab,\n        outgoing_null_one_form,\n        char_projected_rhs_dt_v_minus,\n    )\n    t5_ = np.einsum(\n        \"d,cb,a,cd->ab\",\n        incoming_null_vector,\n        projection_Ab,\n        outgoing_null_one_form,\n        char_projected_rhs_dt_v_minus,\n    )\n    t6_ = np.einsum(\n        \"cd,ab,cd->ab\",\n        projection_AB,\n        projection_ab,\n        char_projected_rhs_dt_v_minus,\n    )\n\n    common_term = (\n        np.sqrt(0.5)\n        * char_speeds[3]\n        * (constraint_char_zero_minus - mu * constraint_char_zero_plus)\n    )\n    t7_ = np.einsum(\n        \"a,b,c,c->ab\",\n        outgoing_null_one_form,\n        outgoing_null_one_form,\n        incoming_null_vector,\n        common_term,\n    )\n    t8_ = np.einsum(\n        \"ab,c,c->ab\", projection_ab, outgoing_null_vector, common_term\n    )\n    t9_ = np.einsum(\n        \"cb,a,c->ab\", projection_Ab, outgoing_null_one_form, common_term\n    )\n    t10_ = np.einsum(\n        \"ca,b,c->ab\", projection_Ab, outgoing_null_one_form, common_term\n    )\n    return 0.5 * (2.0 * t1_ - t2_ - t3_ - t4_ - t5_ + t6_) + (\n        t7_ + t8_ - t9_ - t10_\n    )\n\n\ndef add_physical_dof_terms_to_dt_v_minus(\n    gamma2,\n    unit_interface_normal_one_form,\n    unit_interface_normal_vector,\n    spacetime_unit_normal_vector,\n    projection_ab,\n    projection_Ab,\n    projection_AB,\n    inverse_spatial_metric,\n    extrinsic_curvature,\n    spacetime_metric,\n    inverse_spacetime_metric,\n    three_index_constraint,\n    char_projected_rhs_dt_v_minus,\n    phi,\n    d_phi,\n    d_pi,\n    char_speeds,\n):\n    mu_phys = 0\n    adjust_phys_using_c4 = True\n    gamma2_in_phys = True\n    # calculate weyl propagating modes\n    #       cov deriv of Kij\n    #       calculate ricci3\n    #       adjust_phys_using_c4\n    #       calculate projection operators\n    spatial_christoffel_1st_kind = ch.christoffel_first_kind(phi[:, 1:, 1:])\n    spatial_christoffel_second_kind = np.einsum(\n        \"ij,jkl->ikl\", inverse_spatial_metric, spatial_christoffel_1st_kind\n    )\n    cov_d_Kij = gh.covariant_deriv_extrinsic_curvture(\n        extrinsic_curvature,\n        spacetime_unit_normal_vector,\n        spatial_christoffel_second_kind,\n        inverse_spacetime_metric,\n        phi,\n        d_pi,\n        d_phi,\n    )\n    ricci3 = gh.gh_spatial_ricci_tensor(phi, d_phi, inverse_spatial_metric)\n    if adjust_phys_using_c4:\n        ricci3 = ricci3 + 0.25 * (\n            np.einsum(\n                \"kl,iklj->ij\", inverse_spatial_metric, d_phi[:, :, 1:, 1:]\n            )\n            - np.einsum(\n                \"kl,kilj->ij\", inverse_spatial_metric, d_phi[:, :, 1:, 1:]\n            )\n            + np.einsum(\n                \"kl,jkli->ij\", inverse_spatial_metric, d_phi[:, :, 1:, 1:]\n            )\n            - np.einsum(\n                \"kl,kjli->ij\", inverse_spatial_metric, d_phi[:, :, 1:, 1:]\n            )\n        )\n        ricci3 = ricci3 + 0.5 * (\n            np.einsum(\n                \"k,a,ikja->ij\",\n                unit_interface_normal_vector,\n                spacetime_unit_normal_vector,\n                d_phi[:, :, 1:, :],\n            )\n            - np.einsum(\n                \"k,a,kija->ij\",\n                unit_interface_normal_vector,\n                spacetime_unit_normal_vector,\n                d_phi[:, :, 1:, :],\n            )\n            + np.einsum(\n                \"k,a,jkia->ij\",\n                unit_interface_normal_vector,\n                spacetime_unit_normal_vector,\n                d_phi[:, :, 1:, :],\n            )\n            - np.einsum(\n                \"k,a,kjia->ij\",\n                unit_interface_normal_vector,\n                spacetime_unit_normal_vector,\n                d_phi[:, :, 1:, :],\n            )\n        )\n    spatial_proj_IJ = proj.transverse_projection_operator(\n        inverse_spatial_metric, unit_interface_normal_vector\n    )\n    spatial_proj_ij = proj.transverse_projection_operator(\n        spacetime_metric[1:, 1:], unit_interface_normal_one_form\n    )\n    spatial_proj_Ij = (\n        proj.transverse_projection_operator_mixed_from_spatial_input(\n            unit_interface_normal_vector, unit_interface_normal_one_form\n        )\n    )\n    weyl_prop_plus = wp.weyl_propagating_mode_plus(\n        ricci3,\n        extrinsic_curvature,\n        inverse_spatial_metric,\n        cov_d_Kij,\n        unit_interface_normal_vector,\n        spatial_proj_IJ,\n        spatial_proj_ij,\n        spatial_proj_Ij,\n    )\n    weyl_prop_minus = wp.weyl_propagating_mode_minus(\n        ricci3,\n        extrinsic_curvature,\n        inverse_spatial_metric,\n        cov_d_Kij,\n        unit_interface_normal_vector,\n        spatial_proj_IJ,\n        spatial_proj_ij,\n        spatial_proj_Ij,\n    )\n    # calculate U3+ U3-\n    U3_plus = 2 * np.einsum(\n        \"ia,jb,ij->ab\",\n        projection_Ab[1:, :],\n        projection_Ab[1:, :],\n        weyl_prop_plus,\n    )\n    U3_minus = 2 * np.einsum(\n        \"ia,jb,ij->ab\",\n        projection_Ab[1:, :],\n        projection_Ab[1:, :],\n        weyl_prop_minus,\n    )\n    # calculate corrections\n    tmp_ = char_speeds[3] * (U3_minus - mu_phys * U3_plus)\n    if gamma2_in_phys:\n        tmp_ = tmp_ - char_speeds[3] * gamma2 * np.einsum(\n            \"i,iab->ab\", unit_interface_normal_vector, three_index_constraint\n        )\n\n    t1_ = np.einsum(\n        \"ac,bd,ab->cd\",\n        projection_Ab,\n        projection_Ab,\n        char_projected_rhs_dt_v_minus + tmp_,\n    )\n    t2_ = -0.5 * np.einsum(\n        \"ab,cd,ab->cd\",\n        projection_AB,\n        projection_ab,\n        char_projected_rhs_dt_v_minus + tmp_,\n    )\n    return t1_ + t2_\n\n\ndef constraint_preserving_corrections_dt_v_minus(\n    outgoing_null_one_form,\n    incoming_null_vector,\n    outgoing_null_vector,\n    projection_ab,\n    projection_Ab,\n    projection_AB,\n    char_projected_rhs_dt_v_minus,\n    constraint_char_zero_plus,\n    constraint_char_zero_minus,\n    char_speeds,\n):\n    return (\n        add_constraint_dependent_terms_to_dt_v_minus(\n            outgoing_null_one_form,\n            incoming_null_vector,\n            outgoing_null_vector,\n            projection_ab,\n            projection_Ab,\n            projection_AB,\n            constraint_char_zero_plus,\n            constraint_char_zero_minus,\n            char_projected_rhs_dt_v_minus,\n            char_speeds,\n        )\n        - char_projected_rhs_dt_v_minus\n    )\n\n\ndef constraint_preserving_gauge_corrections_dt_v_minus(\n    gamma2,\n    inertial_coords,\n    incoming_null_one_form,\n    outgoing_null_one_form,\n    incoming_null_vector,\n    outgoing_null_vector,\n    projection_ab,\n    projection_Ab,\n    projection_AB,\n    char_projected_rhs_dt_v_psi,\n    char_projected_rhs_dt_v_minus,\n    constraint_char_zero_plus,\n    constraint_char_zero_minus,\n    char_speeds,\n):\n    return (\n        add_constraint_dependent_terms_to_dt_v_minus(\n            outgoing_null_one_form,\n            incoming_null_vector,\n            outgoing_null_vector,\n            projection_ab,\n            projection_Ab,\n            projection_AB,\n            constraint_char_zero_plus,\n            constraint_char_zero_minus,\n            char_projected_rhs_dt_v_minus,\n            char_speeds,\n        )\n        + add_gauge_sommerfeld_terms_to_dt_v_minus(\n            gamma2,\n            inertial_coords,\n            incoming_null_one_form,\n            outgoing_null_one_form,\n            incoming_null_vector,\n            outgoing_null_vector,\n            projection_Ab,\n            char_projected_rhs_dt_v_psi,\n        )\n        - char_projected_rhs_dt_v_minus\n    )\n\n\ndef constraint_preserving_gauge_physical_corrections_dt_v_minus(\n    gamma2,\n    inertial_coords,\n    unit_interface_normal_one_form,\n    unit_interface_normal_vector,\n    spacetime_unit_normal_vector,\n    incoming_null_one_form,\n    outgoing_null_one_form,\n    incoming_null_vector,\n    outgoing_null_vector,\n    projection_ab,\n    projection_Ab,\n    projection_AB,\n    inverse_spatial_metric,\n    extrinsic_curvature,\n    spacetime_metric,\n    inverse_spacetime_metric,\n    three_index_constraint,\n    char_projected_rhs_dt_v_psi,\n    char_projected_rhs_dt_v_minus,\n    constraint_char_zero_plus,\n    constraint_char_zero_minus,\n    phi,\n    d_phi,\n    d_pi,\n    char_speeds,\n):\n    return (\n        add_constraint_dependent_terms_to_dt_v_minus(\n            outgoing_null_one_form,\n            incoming_null_vector,\n            outgoing_null_vector,\n            projection_ab,\n            projection_Ab,\n            projection_AB,\n            constraint_char_zero_plus,\n            constraint_char_zero_minus,\n            char_projected_rhs_dt_v_minus,\n            char_speeds,\n        )\n        + add_physical_dof_terms_to_dt_v_minus(\n            gamma2,\n            unit_interface_normal_one_form,\n            unit_interface_normal_vector,\n            spacetime_unit_normal_vector,\n            projection_ab,\n            projection_Ab,\n            projection_AB,\n            inverse_spatial_metric,\n            extrinsic_curvature,\n            spacetime_metric,\n            inverse_spacetime_metric,\n            three_index_constraint,\n            char_projected_rhs_dt_v_minus,\n            phi,\n            d_phi,\n            d_pi,\n            char_speeds,\n        )\n        + add_gauge_sommerfeld_terms_to_dt_v_minus(\n            gamma2,\n            inertial_coords,\n            incoming_null_one_form,\n            outgoing_null_one_form,\n            incoming_null_vector,\n            outgoing_null_vector,\n            projection_Ab,\n            char_projected_rhs_dt_v_psi,\n        )\n        - char_projected_rhs_dt_v_minus\n    )\n\n\ndef compute_intermediate_vars(\n    face_mesh_velocity,\n    normal_covector,\n    pi,\n    phi,\n    spacetime_metric,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    spacetime_unit_normal_one_form,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_pi,\n    dt_phi,\n    dt_spacetime_metric,\n    d_pi,\n    d_phi,\n    d_spacetime_metric,\n):\n    inverse_spatial_metric = inverse_spacetime_metric[1:, 1:] + (\n        np.einsum(\"i,j->ij\", shift, shift) / (lapse * lapse)\n    )\n    unit_interface_normal_vector = np.einsum(\n        \"i,ij->j\", normal_covector, inverse_spatial_metric\n    )\n    extrinsic_curvature = gh.extrinsic_curvature(\n        spacetime_unit_normal_vector, pi, phi\n    )\n    four_index_constraint = 0 * three_index_constraint  # Dim == 1\n    if len(normal_covector) == 2:\n        four_index_constraint[0, :, :] = d_phi[0, 1, :, :] - d_phi[1, 0, :, :]\n        four_index_constraint[1, :, :] = -four_index_constraint[0, :, :]\n    elif len(normal_covector) == 3:\n        four_index_constraint = gh_constraints.four_index_constraint(d_phi)\n\n    incoming_null_one_form = nn.interface_incoming_null_normal(\n        spacetime_unit_normal_one_form, normal_covector, shift\n    )\n    outgoing_null_one_form = nn.interface_outgoing_null_normal(\n        spacetime_unit_normal_one_form, normal_covector, shift\n    )\n    incoming_null_vector = nn.interface_incoming_null_normal(\n        spacetime_unit_normal_vector, unit_interface_normal_vector\n    )\n    outgoing_null_vector = nn.interface_outgoing_null_normal(\n        spacetime_unit_normal_vector, unit_interface_normal_vector\n    )\n\n    projection_ab = proj.projection_operator_transverse_to_interface(\n        spacetime_metric,\n        spacetime_unit_normal_one_form,\n        normal_covector,\n        shift,\n    )\n    projection_Ab = proj.projection_operator_transverse_to_interface_mixed(\n        spacetime_unit_normal_vector,\n        spacetime_unit_normal_one_form,\n        unit_interface_normal_vector,\n        normal_covector,\n        shift,\n    )\n    projection_AB = proj.projection_operator_transverse_to_interface(\n        inverse_spacetime_metric,\n        spacetime_unit_normal_vector,\n        unit_interface_normal_vector,\n    )\n\n    char_projected_rhs_dt_v_psi = gh_characteristics.char_field_upsi(\n        gamma2,\n        inverse_spatial_metric,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        normal_covector,\n    )\n    char_projected_rhs_dt_v_zero = gh_characteristics.char_field_uzero(\n        gamma2,\n        inverse_spatial_metric,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        normal_covector,\n    )\n    char_projected_rhs_dt_v_plus = gh_characteristics.char_field_uplus(\n        gamma2,\n        inverse_spatial_metric,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        normal_covector,\n    )\n    char_projected_rhs_dt_v_minus = gh_characteristics.char_field_uminus(\n        gamma2,\n        inverse_spatial_metric,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        normal_covector,\n    )\n\n    two_index_constraint_ = gh_constraints.two_index_constraint(\n        spacetime_deriv_gauge_source,\n        spacetime_unit_normal_one_form,\n        spacetime_unit_normal_vector,\n        inverse_spatial_metric,\n        inverse_spacetime_metric,\n        pi,\n        phi,\n        d_pi,\n        d_phi,\n        gamma2,\n        three_index_constraint,\n    )\n    f_constraint_ = gh_constraints.f_constraint(\n        gauge_source,\n        spacetime_deriv_gauge_source,\n        spacetime_unit_normal_one_form,\n        spacetime_unit_normal_vector,\n        inverse_spatial_metric,\n        inverse_spacetime_metric,\n        pi,\n        phi,\n        d_pi,\n        d_phi,\n        gamma2,\n        three_index_constraint,\n    )\n    constraint_char_zero_plus = f_constraint_ - np.einsum(\n        \"i,ia->a\", unit_interface_normal_vector, two_index_constraint_\n    )\n    constraint_char_zero_minus = f_constraint_ + np.einsum(\n        \"i,ia->a\", unit_interface_normal_vector, two_index_constraint_\n    )\n\n    if face_mesh_velocity is not None:\n        char_speeds = [\n            gh_characteristics.char_speed_upsi_moving_mesh(\n                gamma1, lapse, shift, normal_covector, face_mesh_velocity\n            ),\n            gh_characteristics.char_speed_uzero_moving_mesh(\n                gamma1, lapse, shift, normal_covector, face_mesh_velocity\n            ),\n            gh_characteristics.char_speed_uplus_moving_mesh(\n                gamma1, lapse, shift, normal_covector, face_mesh_velocity\n            ),\n            gh_characteristics.char_speed_uminus_moving_mesh(\n                gamma1, lapse, shift, normal_covector, face_mesh_velocity\n            ),\n        ]\n    else:\n        char_speeds = [\n            gh_characteristics.char_speed_upsi(\n                gamma1, lapse, shift, normal_covector\n            ),\n            gh_characteristics.char_speed_uzero(\n                gamma1, lapse, shift, normal_covector\n            ),\n            gh_characteristics.char_speed_uplus(\n                gamma1, lapse, shift, normal_covector\n            ),\n            gh_characteristics.char_speed_uminus(\n                gamma1, lapse, shift, normal_covector\n            ),\n        ]\n\n    return (\n        unit_interface_normal_vector,\n        four_index_constraint,\n        inverse_spatial_metric,\n        extrinsic_curvature,\n        incoming_null_one_form,\n        outgoing_null_one_form,\n        incoming_null_vector,\n        outgoing_null_vector,\n        projection_ab,\n        projection_Ab,\n        projection_AB,\n        char_projected_rhs_dt_v_psi,\n        char_projected_rhs_dt_v_zero,\n        char_projected_rhs_dt_v_plus,\n        char_projected_rhs_dt_v_minus,\n        constraint_char_zero_plus,\n        constraint_char_zero_minus,\n        char_speeds,\n    )\n\n\ndef error(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    if face_mesh_velocity is not None:\n        (\n            unit_interface_normal_vector,\n            four_index_constraint,\n            inverse_spatial_metric,\n            extrinsic_curvature,\n            incoming_null_one_form,\n            outgoing_null_one_form,\n            incoming_null_vector,\n            outgoing_null_vector,\n            projection_ab,\n            projection_Ab,\n            projection_AB,\n            char_projected_rhs_dt_v_psi,\n            char_projected_rhs_dt_v_zero,\n            char_projected_rhs_dt_v_plus,\n            char_projected_rhs_dt_v_minus,\n            constraint_char_zero_plus,\n            constraint_char_zero_minus,\n            char_speeds,\n        ) = compute_intermediate_vars(\n            face_mesh_velocity,\n            normal_covector,\n            pi,\n            phi,\n            spacetime_metric,\n            coords,\n            gamma1,\n            gamma2,\n            lapse,\n            shift,\n            inverse_spacetime_metric,\n            spacetime_unit_normal_vector,\n            gr.spacetime_normal_one_form(lapse, shift),\n            three_index_constraint,\n            gauge_source,\n            spacetime_deriv_gauge_source,\n            dt_pi,\n            dt_phi,\n            dt_spacetime_metric,\n            d_pi,\n            d_phi,\n            d_spacetime_metric,\n        )\n        if (np.amin(char_speeds) < 0.0) and (\n            np.dot(face_mesh_velocity, normal_covector) > 0.0\n        ):\n            return (\n                \"We found the radial mesh velocity points in the direction \"\n                \"of the outward normal, i.e. we possibly have an expanding \"\n                \"domain. Its unclear if proper boundary conditions are \"\n                \"imposed in this case.\"\n            )\n    return None\n\n\ndef set_bc_corr_zero_when_char_speed_is_positive(dt_v_corr, char_speeds):\n    if char_speeds > 0.0:\n        return dt_v_corr * 0\n    return dt_v_corr\n\n\ndef dt_corrs_ConstraintPreservingGauge(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    spacetime_unit_normal_one_form,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n):\n    (\n        unit_interface_normal_vector,\n        four_index_constraint,\n        inverse_spatial_metric,\n        extrinsic_curvature,\n        incoming_null_one_form,\n        outgoing_null_one_form,\n        incoming_null_vector,\n        outgoing_null_vector,\n        projection_ab,\n        projection_Ab,\n        projection_AB,\n        char_projected_rhs_dt_v_psi,\n        char_projected_rhs_dt_v_zero,\n        char_projected_rhs_dt_v_plus,\n        char_projected_rhs_dt_v_minus,\n        constraint_char_zero_plus,\n        constraint_char_zero_minus,\n        char_speeds,\n    ) = compute_intermediate_vars(\n        face_mesh_velocity,\n        normal_covector,\n        pi,\n        phi,\n        spacetime_metric,\n        coords,\n        gamma1,\n        gamma2,\n        lapse,\n        shift,\n        inverse_spacetime_metric,\n        spacetime_unit_normal_vector,\n        spacetime_unit_normal_one_form,\n        three_index_constraint,\n        gauge_source,\n        spacetime_deriv_gauge_source,\n        dt_pi,\n        dt_phi,\n        dt_spacetime_metric,\n        d_pi,\n        d_phi,\n        d_spacetime_metric,\n    )\n    if np.amin(char_speeds) >= 0.0:\n        return (pi * 0, phi * 0, pi * 0, pi * 0)\n    dt_v_psi = constraint_preserving_corrections_dt_v_psi(\n        unit_interface_normal_vector, three_index_constraint, char_speeds\n    )\n    dt_v_zero = constraint_preserving_corrections_dt_v_zero(\n        unit_interface_normal_vector, four_index_constraint, char_speeds\n    )\n    dt_v_plus = -1.0 * char_projected_rhs_dt_v_plus\n    dt_v_minus = constraint_preserving_gauge_corrections_dt_v_minus(\n        gamma2,\n        coords,\n        incoming_null_one_form,\n        outgoing_null_one_form,\n        incoming_null_vector,\n        outgoing_null_vector,\n        projection_ab,\n        projection_Ab,\n        projection_AB,\n        char_projected_rhs_dt_v_psi,\n        char_projected_rhs_dt_v_minus,\n        constraint_char_zero_plus,\n        constraint_char_zero_minus,\n        char_speeds,\n    )\n    dt_v_psi = set_bc_corr_zero_when_char_speed_is_positive(\n        dt_v_psi, char_speeds[0]\n    )\n    dt_v_zero = set_bc_corr_zero_when_char_speed_is_positive(\n        dt_v_zero, char_speeds[1]\n    )\n    dt_v_plus = set_bc_corr_zero_when_char_speed_is_positive(\n        dt_v_plus, char_speeds[2]\n    )\n    dt_v_minus = set_bc_corr_zero_when_char_speed_is_positive(\n        dt_v_minus, char_speeds[3]\n    )\n    return dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus\n\n\ndef dt_corrs_ConstraintPreservingGaugePhysical(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    spacetime_unit_normal_one_form,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n):\n    (\n        unit_interface_normal_vector,\n        four_index_constraint,\n        inverse_spatial_metric,\n        extrinsic_curvature,\n        incoming_null_one_form,\n        outgoing_null_one_form,\n        incoming_null_vector,\n        outgoing_null_vector,\n        projection_ab,\n        projection_Ab,\n        projection_AB,\n        char_projected_rhs_dt_v_psi,\n        char_projected_rhs_dt_v_zero,\n        char_projected_rhs_dt_v_plus,\n        char_projected_rhs_dt_v_minus,\n        constraint_char_zero_plus,\n        constraint_char_zero_minus,\n        char_speeds,\n    ) = compute_intermediate_vars(\n        face_mesh_velocity,\n        normal_covector,\n        pi,\n        phi,\n        spacetime_metric,\n        coords,\n        gamma1,\n        gamma2,\n        lapse,\n        shift,\n        inverse_spacetime_metric,\n        spacetime_unit_normal_vector,\n        spacetime_unit_normal_one_form,\n        three_index_constraint,\n        gauge_source,\n        spacetime_deriv_gauge_source,\n        dt_pi,\n        dt_phi,\n        dt_spacetime_metric,\n        d_pi,\n        d_phi,\n        d_spacetime_metric,\n    )\n    if np.amin(char_speeds) >= 0.0:\n        return (pi * 0, phi * 0, pi * 0, pi * 0)\n    dt_v_psi = constraint_preserving_corrections_dt_v_psi(\n        unit_interface_normal_vector, three_index_constraint, char_speeds\n    )\n    dt_v_zero = constraint_preserving_corrections_dt_v_zero(\n        unit_interface_normal_vector, four_index_constraint, char_speeds\n    )\n    dt_v_plus = -1.0 * char_projected_rhs_dt_v_plus\n    dt_v_minus = constraint_preserving_gauge_physical_corrections_dt_v_minus(\n        gamma2,\n        coords,\n        normal_covector,\n        unit_interface_normal_vector,\n        spacetime_unit_normal_vector,\n        incoming_null_one_form,\n        outgoing_null_one_form,\n        incoming_null_vector,\n        outgoing_null_vector,\n        projection_ab,\n        projection_Ab,\n        projection_AB,\n        inverse_spatial_metric,\n        extrinsic_curvature,\n        spacetime_metric,\n        inverse_spacetime_metric,\n        three_index_constraint,\n        char_projected_rhs_dt_v_psi,\n        char_projected_rhs_dt_v_minus,\n        constraint_char_zero_plus,\n        constraint_char_zero_minus,\n        phi,\n        d_phi,\n        d_pi,\n        char_speeds,\n    )\n    dt_v_psi = set_bc_corr_zero_when_char_speed_is_positive(\n        dt_v_psi, char_speeds[0]\n    )\n    dt_v_zero = set_bc_corr_zero_when_char_speed_is_positive(\n        dt_v_zero, char_speeds[1]\n    )\n    dt_v_plus = set_bc_corr_zero_when_char_speed_is_positive(\n        dt_v_plus, char_speeds[2]\n    )\n    dt_v_minus = set_bc_corr_zero_when_char_speed_is_positive(\n        dt_v_minus, char_speeds[3]\n    )\n    return dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus\n\n\ndef subtract_mesh_velocity(\n    face_mesh_velocity,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n):\n    if not (face_mesh_velocity is None):\n        dt_spacetime_metric -= np.einsum(\n            \"i,iab->ab\", face_mesh_velocity, d_spacetime_metric\n        )\n        dt_pi -= np.einsum(\"i,iab->ab\", face_mesh_velocity, d_pi)\n        dt_phi -= np.einsum(\"i,ijab->jab\", face_mesh_velocity, d_phi)\n\n\ndef dt_spacetime_metric(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    subtract_mesh_velocity(\n        face_mesh_velocity,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n    (dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus) = (\n        dt_corrs_ConstraintPreservingGauge(\n            face_mesh_velocity,\n            normal_covector,\n            normal_vector,\n            spacetime_metric,\n            pi,\n            phi,\n            inertial_coords,\n            gamma1,\n            gamma2,\n            lapse,\n            shift,\n            inverse_spacetime_metric,\n            spacetime_unit_normal_vector,\n            gr.spacetime_normal_one_form(lapse, shift),\n            three_index_constraint,\n            gauge_source,\n            spacetime_deriv_gauge_source,\n            dt_spacetime_metric,\n            dt_pi,\n            dt_phi,\n            d_spacetime_metric,\n            d_pi,\n            d_phi,\n        )\n    )\n    return dt_v_psi\n\n\ndef dt_spacetime_metric_static_mesh(\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    in_dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    return dt_spacetime_metric(\n        0.0 * normal_vector,\n        normal_covector,\n        normal_vector,\n        spacetime_metric,\n        pi,\n        phi,\n        inertial_coords,\n        gamma1,\n        gamma2,\n        lapse,\n        shift,\n        inverse_spacetime_metric,\n        spacetime_unit_normal_vector,\n        three_index_constraint,\n        gauge_source,\n        spacetime_deriv_gauge_source,\n        in_dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n\n\ndef dt_pi_ConstraintPreservingGauge(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    subtract_mesh_velocity(\n        face_mesh_velocity,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n    (dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus) = (\n        dt_corrs_ConstraintPreservingGauge(\n            face_mesh_velocity,\n            normal_covector,\n            normal_vector,\n            spacetime_metric,\n            pi,\n            phi,\n            inertial_coords,\n            gamma1,\n            gamma2,\n            lapse,\n            shift,\n            inverse_spacetime_metric,\n            spacetime_unit_normal_vector,\n            gr.spacetime_normal_one_form(lapse, shift),\n            three_index_constraint,\n            gauge_source,\n            spacetime_deriv_gauge_source,\n            dt_spacetime_metric,\n            dt_pi,\n            dt_phi,\n            d_spacetime_metric,\n            d_pi,\n            d_phi,\n        )\n    )\n    return gh_characteristics.evol_field_pi(\n        gamma2, dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus, normal_covector\n    )\n\n\ndef dt_pi_ConstraintPreservingGauge_static_mesh(\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    return dt_pi_ConstraintPreservingGauge(\n        0.0 * normal_vector,\n        normal_covector,\n        normal_vector,\n        spacetime_metric,\n        pi,\n        phi,\n        inertial_coords,\n        gamma1,\n        gamma2,\n        lapse,\n        shift,\n        inverse_spacetime_metric,\n        spacetime_unit_normal_vector,\n        three_index_constraint,\n        gauge_source,\n        spacetime_deriv_gauge_source,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n\n\ndef dt_pi_ConstraintPreservingGaugePhysical(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    subtract_mesh_velocity(\n        face_mesh_velocity,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n    (dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus) = (\n        dt_corrs_ConstraintPreservingGaugePhysical(\n            face_mesh_velocity,\n            normal_covector,\n            normal_vector,\n            spacetime_metric,\n            pi,\n            phi,\n            inertial_coords,\n            gamma1,\n            gamma2,\n            lapse,\n            shift,\n            inverse_spacetime_metric,\n            spacetime_unit_normal_vector,\n            gr.spacetime_normal_one_form(lapse, shift),\n            three_index_constraint,\n            gauge_source,\n            spacetime_deriv_gauge_source,\n            dt_spacetime_metric,\n            dt_pi,\n            dt_phi,\n            d_spacetime_metric,\n            d_pi,\n            d_phi,\n        )\n    )\n    return gh_characteristics.evol_field_pi(\n        gamma2, dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus, normal_covector\n    )\n\n\ndef dt_pi_ConstraintPreservingGaugePhysical_static_mesh(\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    return dt_pi_ConstraintPreservingGaugePhysical(\n        0.0 * normal_vector,\n        normal_covector,\n        normal_vector,\n        spacetime_metric,\n        pi,\n        phi,\n        inertial_coords,\n        gamma1,\n        gamma2,\n        lapse,\n        shift,\n        inverse_spacetime_metric,\n        spacetime_unit_normal_vector,\n        three_index_constraint,\n        gauge_source,\n        spacetime_deriv_gauge_source,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n\n\ndef dt_phi_ConstraintPreservingGauge(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    subtract_mesh_velocity(\n        face_mesh_velocity,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n    (dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus) = (\n        dt_corrs_ConstraintPreservingGauge(\n            face_mesh_velocity,\n            normal_covector,\n            normal_vector,\n            spacetime_metric,\n            pi,\n            phi,\n            inertial_coords,\n            gamma1,\n            gamma2,\n            lapse,\n            shift,\n            inverse_spacetime_metric,\n            spacetime_unit_normal_vector,\n            gr.spacetime_normal_one_form(lapse, shift),\n            three_index_constraint,\n            gauge_source,\n            spacetime_deriv_gauge_source,\n            dt_spacetime_metric,\n            dt_pi,\n            dt_phi,\n            d_spacetime_metric,\n            d_pi,\n            d_phi,\n        )\n    )\n    return gh_characteristics.evol_field_phi(\n        gamma2, dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus, normal_covector\n    )\n\n\ndef dt_phi_ConstraintPreservingGauge_static_mesh(\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    return dt_phi_ConstraintPreservingGauge(\n        0.0 * normal_vector,\n        normal_covector,\n        normal_vector,\n        spacetime_metric,\n        pi,\n        phi,\n        inertial_coords,\n        gamma1,\n        gamma2,\n        lapse,\n        shift,\n        inverse_spacetime_metric,\n        spacetime_unit_normal_vector,\n        three_index_constraint,\n        gauge_source,\n        spacetime_deriv_gauge_source,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n\n\ndef dt_phi_ConstraintPreservingGaugePhysical(\n    face_mesh_velocity,\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    subtract_mesh_velocity(\n        face_mesh_velocity,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n    (dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus) = (\n        dt_corrs_ConstraintPreservingGaugePhysical(\n            face_mesh_velocity,\n            normal_covector,\n            normal_vector,\n            spacetime_metric,\n            pi,\n            phi,\n            inertial_coords,\n            gamma1,\n            gamma2,\n            lapse,\n            shift,\n            inverse_spacetime_metric,\n            spacetime_unit_normal_vector,\n            gr.spacetime_normal_one_form(lapse, shift),\n            three_index_constraint,\n            gauge_source,\n            spacetime_deriv_gauge_source,\n            dt_spacetime_metric,\n            dt_pi,\n            dt_phi,\n            d_spacetime_metric,\n            d_pi,\n            d_phi,\n        )\n    )\n    return gh_characteristics.evol_field_phi(\n        gamma2, dt_v_psi, dt_v_zero, dt_v_plus, dt_v_minus, normal_covector\n    )\n\n\ndef dt_phi_ConstraintPreservingGaugePhysical_static_mesh(\n    normal_covector,\n    normal_vector,\n    spacetime_metric,\n    pi,\n    phi,\n    inertial_coords,\n    gamma1,\n    gamma2,\n    lapse,\n    shift,\n    inverse_spacetime_metric,\n    spacetime_unit_normal_vector,\n    three_index_constraint,\n    gauge_source,\n    spacetime_deriv_gauge_source,\n    dt_spacetime_metric,\n    dt_pi,\n    dt_phi,\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    time=0.0,\n):\n    return dt_phi_ConstraintPreservingGaugePhysical(\n        0.0 * normal_vector,\n        normal_covector,\n        normal_vector,\n        spacetime_metric,\n        pi,\n        phi,\n        inertial_coords,\n        gamma1,\n        gamma2,\n        lapse,\n        shift,\n        inverse_spacetime_metric,\n        spacetime_unit_normal_vector,\n        three_index_constraint,\n        gauge_source,\n        spacetime_deriv_gauge_source,\n        dt_spacetime_metric,\n        dt_pi,\n        dt_phi,\n        d_spacetime_metric,\n        d_pi,\n        d_phi,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DemandOutgoingCharSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Evolution.Systems.GeneralizedHarmonic.Characteristics as ght\nimport numpy as np\n\n\ndef characteristic_speeds(gamma_1, lapse, shift, unit_normal_one_form):\n    return [\n        ght.char_speed_upsi(gamma_1, lapse, shift, unit_normal_one_form),\n        ght.char_speed_uzero(gamma_1, lapse, shift, unit_normal_one_form),\n        ght.char_speed_uplus(gamma_1, lapse, shift, unit_normal_one_form),\n        ght.char_speed_uminus(gamma_1, lapse, shift, unit_normal_one_form),\n    ]\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    gamma_1,\n    lapse,\n    shift,\n):\n    if face_mesh_velocity is not None:\n        char_speeds = [\n            ght.char_speed_upsi_moving_mesh(\n                gamma_1,\n                lapse,\n                shift,\n                outward_directed_normal_covector,\n                face_mesh_velocity,\n            ),\n            ght.char_speed_uzero_moving_mesh(\n                gamma_1,\n                lapse,\n                shift,\n                outward_directed_normal_covector,\n                face_mesh_velocity,\n            ),\n            ght.char_speed_uplus_moving_mesh(\n                gamma_1,\n                lapse,\n                shift,\n                outward_directed_normal_covector,\n                face_mesh_velocity,\n            ),\n            ght.char_speed_uminus_moving_mesh(\n                gamma_1,\n                lapse,\n                shift,\n                outward_directed_normal_covector,\n                face_mesh_velocity,\n            ),\n        ]\n    else:\n        char_speeds = [\n            ght.char_speed_upsi(\n                gamma_1, lapse, shift, outward_directed_normal_covector\n            ),\n            ght.char_speed_uzero(\n                gamma_1, lapse, shift, outward_directed_normal_covector\n            ),\n            ght.char_speed_uplus(\n                gamma_1, lapse, shift, outward_directed_normal_covector\n            ),\n            ght.char_speed_uminus(\n                gamma_1, lapse, shift, outward_directed_normal_covector\n            ),\n        ]\n    for i in range(4):\n        if char_speeds[i] < 0.0:\n            return \"DemandOutgoingCharSpeeds boundary condition violated\"\n    return None\n\n    pass\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletAnalytic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nimport PointwiseFunctions.AnalyticSolutions.GeneralRelativity.GaugeWave as gw\nimport PointwiseFunctions.GeneralRelativity.ComputeGhQuantities as gh\nimport PointwiseFunctions.GeneralRelativity.ComputeSpacetimeQuantities as gr\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    interior_gamma1,\n    interior_gamma2,\n    time,\n    dim,\n):\n    return None\n\n\n_amplitude = 0.2\n_wavelength = 10.0\n\n\ndef lapse(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    interior_gamma1,\n    interior_gamma2,\n    time,\n    dim,\n):\n    return gw.gauge_wave_lapse(coords, time, _amplitude, _wavelength)\n\n\ndef shift(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    interior_gamma1,\n    interior_gamma2,\n    time,\n    dim,\n):\n    return gw.gauge_wave_shift(coords, time, _amplitude, _wavelength)\n\n\ndef spacetime_metric(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    interior_gamma1,\n    interior_gamma2,\n    time,\n    dim,\n):\n    return gr.spacetime_metric(\n        gw.gauge_wave_lapse(coords, time, _amplitude, _wavelength),\n        gw.gauge_wave_shift(coords, time, _amplitude, _wavelength),\n        gw.gauge_wave_spatial_metric(coords, time, _amplitude, _wavelength),\n    )\n\n\ndef phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    interior_gamma1,\n    interior_gamma2,\n    time,\n    dim,\n):\n    lapse = gw.gauge_wave_lapse(coords, time, _amplitude, _wavelength)\n    shift = gw.gauge_wave_shift(coords, time, _amplitude, _wavelength)\n    spatial_metric = gw.gauge_wave_spatial_metric(\n        coords, time, _amplitude, _wavelength\n    )\n    deriv_lapse = gw.gauge_wave_d_lapse(coords, time, _amplitude, _wavelength)\n    deriv_shift = gw.gauge_wave_d_shift(coords, time, _amplitude, _wavelength)\n    deriv_spatial_metric = gw.gauge_wave_d_spatial_metric(\n        coords, time, _amplitude, _wavelength\n    )\n    return gh.phi(\n        lapse,\n        deriv_lapse,\n        shift,\n        deriv_shift,\n        spatial_metric,\n        deriv_spatial_metric,\n    )\n\n\ndef pi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    interior_gamma1,\n    interior_gamma2,\n    time,\n    dim,\n):\n    lapse = gw.gauge_wave_lapse(coords, time, _amplitude, _wavelength)\n    shift = gw.gauge_wave_shift(coords, time, _amplitude, _wavelength)\n    spatial_metric = gw.gauge_wave_spatial_metric(\n        coords, time, _amplitude, _wavelength\n    )\n    dt_lapse = gw.gauge_wave_dt_lapse(coords, time, _amplitude, _wavelength)\n    dt_shift = gw.gauge_wave_dt_shift(coords, time, _amplitude, _wavelength)\n    dt_spatial_metric = gw.gauge_wave_dt_spatial_metric(\n        coords, time, _amplitude, _wavelength\n    )\n    return gh.pi(\n        lapse,\n        dt_lapse,\n        shift,\n        dt_shift,\n        spatial_metric,\n        dt_spatial_metric,\n        phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            interior_gamma1,\n            interior_gamma2,\n            time,\n            dim,\n        ),\n    )\n\n\ndef constraint_gamma1(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    interior_gamma1,\n    interior_gamma2,\n    time,\n    dim,\n):\n    assert interior_gamma1 >= 0.0\n    return interior_gamma1\n\n\ndef constraint_gamma2(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    interior_gamma1,\n    interior_gamma2,\n    time,\n    dim,\n):\n    assert interior_gamma2 >= 0.0\n    return interior_gamma2\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletMinkowski.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nimport PointwiseFunctions.GeneralRelativity.ComputeSpacetimeQuantities as gr\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_gamma1,\n    interior_gamma2,\n):\n    return None\n\n\ndef lapse(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_gamma1,\n    interior_gamma2,\n):\n    return 1.0\n\n\ndef shift(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_gamma1,\n    interior_gamma2,\n):\n    return np.zeros_like(outward_directed_normal_covector)\n\n\ndef spacetime_metric(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_gamma1,\n    interior_gamma2,\n):\n    return gr.spacetime_metric(\n        -1.0,\n        np.zeros_like(outward_directed_normal_covector),\n        np.diag(np.ones(len(outward_directed_normal_covector))),\n    )\n\n\ndef phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_gamma1,\n    interior_gamma2,\n):\n    return np.zeros(\n        (\n            len(outward_directed_normal_covector),\n            len(outward_directed_normal_covector) + 1,\n            len(outward_directed_normal_covector) + 1,\n        )\n    )\n\n\ndef pi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_gamma1,\n    interior_gamma2,\n):\n    return np.zeros(\n        (\n            len(outward_directed_normal_covector) + 1,\n            len(outward_directed_normal_covector) + 1,\n        )\n    )\n\n\ndef constraint_gamma1(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_gamma1,\n    interior_gamma2,\n):\n    assert interior_gamma1 >= 0.0\n    return interior_gamma1\n\n\ndef constraint_gamma2(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_gamma1,\n    interior_gamma2,\n):\n    assert interior_gamma2 >= 0.0\n    return interior_gamma2\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Test_Bjorhus.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\nusing frame = Frame::Inertial;\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   tmpl::list<MathFunctions::Sinusoid<1, Frame::Inertial>>>,\n        tmpl::pair<gh::BoundaryConditions::BoundaryCondition<Dim>,\n                   tmpl::list<gh::BoundaryConditions::\n                                  ConstraintPreservingBjorhus<Dim>>>>;\n  };\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  MAKE_GENERATOR(gen);\n  for (const auto& [bc_string, bc_type] :\n       {std::pair{\"ConstraintPreserving\"s, \"ConstraintPreservingGauge\"s},\n        std::pair{\"ConstraintPreservingPhysical\"s,\n                  \"ConstraintPreservingGaugePhysical\"s}}) {\n    CAPTURE(bc_string);\n    const auto box_of_gridless_data =\n        db::create<db::AddSimpleTags<::Tags::Time>>(0.0);\n\n    helpers::test_boundary_condition_with_python<\n        gh::BoundaryConditions::ConstraintPreservingBjorhus<Dim>,\n        gh::BoundaryConditions::BoundaryCondition<Dim>, gh::System<Dim>,\n        tmpl::list<gh::BoundaryCorrections::UpwindPenalty<Dim>>, tmpl::list<>,\n        tmpl::list<>, Metavariables<Dim>>(\n        make_not_null(&gen),\n        \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n        tuples::TaggedTuple<\n            helpers::Tags::PythonFunctionForErrorMessage<>,\n            helpers::Tags::PythonFunctionName<\n                ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, Dim, frame>>>,\n            helpers::Tags::PythonFunctionName<\n                ::Tags::dt<gh::Tags::Pi<DataVector, Dim, frame>>>,\n            helpers::Tags::PythonFunctionName<\n                ::Tags::dt<gh::Tags::Phi<DataVector, Dim, frame>>>>{\n            \"error\", \"dt_spacetime_metric\", \"dt_pi_\" + bc_type,\n            \"dt_phi_\" + bc_type},\n        \"ConstraintPreservingBjorhus:\\n\"\n        \"  Type: \" +\n            bc_string +\n            (Dim == 3 ? \"\\n  IncomingWaveProfile: None\" : \"\"),\n        Index<Dim - 1>{Dim == 1 ? 1 : 5}, box_of_gridless_data,\n        tuples::TaggedTuple<\n            helpers::Tags::Range<gr::Tags::Lapse<DataVector>>,\n            helpers::Tags::Range<gr::Tags::Shift<DataVector, Dim, frame>>,\n            helpers::Tags::Range<\n                gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim, frame>>,\n            helpers::Tags::Range<\n                domain::Tags::Coordinates<Dim, Frame::Inertial>>>{\n            std::array<double, 2>{{0.8, 1.}}, std::array<double, 2>{{0.1, 0.2}},\n            std::array<double, 2>{{0.1, 1.}},\n            std::array<double, 2>{{-1000., 1000.}}},\n        1.e-6);\n  }\n}\n\nvoid test_incoming_wave_profile_option_parsing_and_dim_guard() {\n  {\n    const auto created = TestHelpers::test_creation<\n        std::unique_ptr<gh::BoundaryConditions::BoundaryCondition<3>>,\n        Metavariables<3>>(\n        \"ConstraintPreservingBjorhus:\\n\"\n        \"  Type: ConstraintPreservingPhysical\\n\"\n        \"  IncomingWaveProfile: None\");\n    CHECK(dynamic_cast<\n              const gh::BoundaryConditions::ConstraintPreservingBjorhus<3>*>(\n              created.get()) != nullptr);\n  }\n\n  {\n    const auto created = TestHelpers::test_creation<\n        std::unique_ptr<gh::BoundaryConditions::BoundaryCondition<3>>,\n        Metavariables<3>>(\n        \"ConstraintPreservingBjorhus:\\n\"\n        \"  Type: ConstraintPreservingPhysical\\n\"\n        \"  IncomingWaveProfile:\\n\"\n        \"    Sinusoid:\\n\"\n        \"      Amplitude: 1.2\\n\"\n        \"      Wavenumber: 0.7\\n\"\n        \"      Phase: 0.4\");\n    CHECK(dynamic_cast<\n              const gh::BoundaryConditions::ConstraintPreservingBjorhus<3>*>(\n              created.get()) != nullptr);\n  }\n\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<\n          std::unique_ptr<gh::BoundaryConditions::BoundaryCondition<1>>,\n          Metavariables<1>>(\"ConstraintPreservingBjorhus:\\n\"\n                            \"  Type: ConstraintPreservingPhysical\\n\"\n                            \"  IncomingWaveProfile:\\n\"\n                            \"    Sinusoid:\\n\"\n                            \"      Amplitude: 1.2\\n\"\n                            \"      Wavenumber: 0.7\\n\"\n                            \"      Phase: 0.4\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Option 'IncomingWaveProfile' is not a valid option.\"));\n\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<\n          std::unique_ptr<gh::BoundaryConditions::BoundaryCondition<2>>,\n          Metavariables<2>>(\"ConstraintPreservingBjorhus:\\n\"\n                            \"  Type: ConstraintPreservingPhysical\\n\"\n                            \"  IncomingWaveProfile:\\n\"\n                            \"    Sinusoid:\\n\"\n                            \"      Amplitude: 1.2\\n\"\n                            \"      Wavenumber: 0.7\\n\"\n                            \"      Phase: 0.4\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Option 'IncomingWaveProfile' is not a valid option.\"));\n}\n\ntemplate <size_t Dim>\nvoid wrap_dt_vars_corrections_ConstraintPreservingGauge(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, frame>*>\n        dt_spacetime_metric_correction,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, frame>*> dt_pi_correction,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, frame>*> dt_phi_correction,\n    const tnsr::I<DataVector, Dim, frame>& face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, frame>& normal_covector,\n    const tnsr::I<DataVector, Dim, frame>& normal_vector,\n    // c.f. dg_interior_evolved_variables_tags\n    const tnsr::aa<DataVector, Dim, frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, frame>& pi,\n    const tnsr::iaa<DataVector, Dim, frame>& phi,\n    // c.f. dg_interior_temporary_tags\n    const tnsr::I<DataVector, Dim, frame>& coords,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const tnsr::AA<DataVector, Dim, Frame::Inertial>& inverse_spacetime_metric,\n    const tnsr::A<DataVector, Dim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_constraint,\n    const tnsr::a<DataVector, Dim, frame>& gauge_source,\n    const tnsr::ab<DataVector, Dim, frame>& spacetime_deriv_gauge_source,\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, Dim, frame>& dt_spacetime_metric,\n    const tnsr::aa<DataVector, Dim, frame>& dt_pi,\n    const tnsr::iaa<DataVector, Dim, frame>& dt_phi,\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, Dim, frame>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, Dim, frame>& d_pi,\n    const tnsr::ijaa<DataVector, Dim, frame>& d_phi) {\n  gh::BoundaryConditions::ConstraintPreservingBjorhus<Dim> bjorhus_obj{\n      gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType::\n          ConstraintPreserving};\n  bjorhus_obj.dg_time_derivative(\n      dt_spacetime_metric_correction, dt_pi_correction, dt_phi_correction,\n      face_mesh_velocity, normal_covector, normal_vector, spacetime_metric, pi,\n      phi, coords, gamma1, gamma2, lapse, shift, inverse_spacetime_metric,\n      spacetime_unit_normal_vector, three_index_constraint, gauge_source,\n      spacetime_deriv_gauge_source, dt_spacetime_metric, dt_pi, dt_phi,\n      d_spacetime_metric, d_pi, d_phi);\n}\n\ntemplate <size_t Dim>\nvoid wrap_dt_vars_corrections_ConstraintPreservingGauge_static_mesh(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, frame>*>\n        dt_spacetime_metric_correction,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, frame>*> dt_pi_correction,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, frame>*> dt_phi_correction,\n    const tnsr::i<DataVector, Dim, frame>& normal_covector,\n    const tnsr::I<DataVector, Dim, frame>& normal_vector,\n    // c.f. dg_interior_evolved_variables_tags\n    const tnsr::aa<DataVector, Dim, frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, frame>& pi,\n    const tnsr::iaa<DataVector, Dim, frame>& phi,\n    // c.f. dg_interior_temporary_tags\n    const tnsr::I<DataVector, Dim, frame>& coords,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const tnsr::AA<DataVector, Dim, Frame::Inertial>& inverse_spacetime_metric,\n    const tnsr::A<DataVector, Dim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_constraint,\n    const tnsr::a<DataVector, Dim, frame>& gauge_source,\n    const tnsr::ab<DataVector, Dim, frame>& spacetime_deriv_gauge_source,\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, Dim, frame>& dt_spacetime_metric,\n    const tnsr::aa<DataVector, Dim, frame>& dt_pi,\n    const tnsr::iaa<DataVector, Dim, frame>& dt_phi,\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, Dim, frame>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, Dim, frame>& d_pi,\n    const tnsr::ijaa<DataVector, Dim, frame>& d_phi) {\n  gh::BoundaryConditions::ConstraintPreservingBjorhus<Dim> bjorhus_obj{\n      gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType::\n          ConstraintPreserving};\n  bjorhus_obj.dg_time_derivative(\n      dt_spacetime_metric_correction, dt_pi_correction, dt_phi_correction,\n      std::nullopt, normal_covector, normal_vector, spacetime_metric, pi, phi,\n      coords, gamma1, gamma2, lapse, shift, inverse_spacetime_metric,\n      spacetime_unit_normal_vector, three_index_constraint, gauge_source,\n      spacetime_deriv_gauge_source, dt_spacetime_metric, dt_pi, dt_phi,\n      d_spacetime_metric, d_pi, d_phi);\n}\n\ntemplate <size_t Dim>\nvoid wrap_dt_vars_corrections_ConstraintPreservingGaugePhysical(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, frame>*>\n        dt_spacetime_metric_correction,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, frame>*> dt_pi_correction,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, frame>*> dt_phi_correction,\n    const tnsr::I<DataVector, Dim, frame>& face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, frame>& normal_covector,\n    const tnsr::I<DataVector, Dim, frame>& normal_vector,\n    // c.f. dg_interior_evolved_variables_tags\n    const tnsr::aa<DataVector, Dim, frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, frame>& pi,\n    const tnsr::iaa<DataVector, Dim, frame>& phi,\n    // c.f. dg_interior_temporary_tags\n    const tnsr::I<DataVector, Dim, frame>& coords,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const tnsr::AA<DataVector, Dim, Frame::Inertial>& inverse_spacetime_metric,\n    const tnsr::A<DataVector, Dim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_constraint,\n    const tnsr::a<DataVector, Dim, frame>& gauge_source,\n    const tnsr::ab<DataVector, Dim, frame>& spacetime_deriv_gauge_source,\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, Dim, frame>& dt_spacetime_metric,\n    const tnsr::aa<DataVector, Dim, frame>& dt_pi,\n    const tnsr::iaa<DataVector, Dim, frame>& dt_phi,\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, Dim, frame>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, Dim, frame>& d_pi,\n    const tnsr::ijaa<DataVector, Dim, frame>& d_phi) {\n  gh::BoundaryConditions::ConstraintPreservingBjorhus<Dim> bjorhus_obj{\n      gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType::\n          ConstraintPreservingPhysical};\n  bjorhus_obj.dg_time_derivative(\n      dt_spacetime_metric_correction, dt_pi_correction, dt_phi_correction,\n      face_mesh_velocity, normal_covector, normal_vector, spacetime_metric, pi,\n      phi, coords, gamma1, gamma2, lapse, shift, inverse_spacetime_metric,\n      spacetime_unit_normal_vector, three_index_constraint, gauge_source,\n      spacetime_deriv_gauge_source, dt_spacetime_metric, dt_pi, dt_phi,\n      d_spacetime_metric, d_pi, d_phi);\n}\n\ntemplate <size_t Dim>\nvoid wrap_dt_vars_corrections_ConstraintPreservingGaugePhysical_static_mesh(\n    const gsl::not_null<tnsr::aa<DataVector, Dim, frame>*>\n        dt_spacetime_metric_correction,\n    const gsl::not_null<tnsr::aa<DataVector, Dim, frame>*> dt_pi_correction,\n    const gsl::not_null<tnsr::iaa<DataVector, Dim, frame>*> dt_phi_correction,\n    const tnsr::i<DataVector, Dim, frame>& normal_covector,\n    const tnsr::I<DataVector, Dim, frame>& normal_vector,\n    // c.f. dg_interior_evolved_variables_tags\n    const tnsr::aa<DataVector, Dim, frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, frame>& pi,\n    const tnsr::iaa<DataVector, Dim, frame>& phi,\n    // c.f. dg_interior_temporary_tags\n    const tnsr::I<DataVector, Dim, frame>& coords,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& shift,\n    const tnsr::AA<DataVector, Dim, Frame::Inertial>& inverse_spacetime_metric,\n    const tnsr::A<DataVector, Dim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_constraint,\n    const tnsr::a<DataVector, Dim, frame>& gauge_source,\n    const tnsr::ab<DataVector, Dim, frame>& spacetime_deriv_gauge_source,\n    // c.f. dg_interior_dt_vars_tags\n    const tnsr::aa<DataVector, Dim, frame>& dt_spacetime_metric,\n    const tnsr::aa<DataVector, Dim, frame>& dt_pi,\n    const tnsr::iaa<DataVector, Dim, frame>& dt_phi,\n    // c.f. dg_interior_deriv_vars_tags\n    const tnsr::iaa<DataVector, Dim, frame>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, Dim, frame>& d_pi,\n    const tnsr::ijaa<DataVector, Dim, frame>& d_phi) {\n  gh::BoundaryConditions::ConstraintPreservingBjorhus<Dim> bjorhus_obj{\n      gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType::\n          ConstraintPreservingPhysical};\n  bjorhus_obj.dg_time_derivative(\n      dt_spacetime_metric_correction, dt_pi_correction, dt_phi_correction,\n      std::nullopt, normal_covector, normal_vector, spacetime_metric, pi, phi,\n      coords, gamma1, gamma2, lapse, shift, inverse_spacetime_metric,\n      spacetime_unit_normal_vector, three_index_constraint, gauge_source,\n      spacetime_deriv_gauge_source, dt_spacetime_metric, dt_pi, dt_phi,\n      d_spacetime_metric, d_pi, d_phi);\n}\n\ntemplate <size_t Dim>\nvoid test_with_random_values(const DataVector& used_for_size) {\n  // Static mesh\n  pypp::check_with_random_values<1>(\n      wrap_dt_vars_corrections_ConstraintPreservingGauge_static_mesh<Dim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      {\"dt_spacetime_metric_static_mesh\",\n       \"dt_pi_ConstraintPreservingGauge_static_mesh\",\n       \"dt_phi_ConstraintPreservingGauge_static_mesh\"},\n      {{{0.1, 1.}}}, used_for_size, 1.e-6);\n  pypp::check_with_random_values<1>(\n      wrap_dt_vars_corrections_ConstraintPreservingGaugePhysical_static_mesh<\n          Dim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      {\"dt_spacetime_metric_static_mesh\",\n       \"dt_pi_ConstraintPreservingGaugePhysical_static_mesh\",\n       \"dt_phi_ConstraintPreservingGaugePhysical_static_mesh\"},\n      {{{0.1, 1.}}}, used_for_size, 1.e-6);\n\n  // Moving mesh\n  pypp::check_with_random_values<1>(\n      wrap_dt_vars_corrections_ConstraintPreservingGauge<Dim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      {\"dt_spacetime_metric\", \"dt_pi_ConstraintPreservingGauge\",\n       \"dt_phi_ConstraintPreservingGauge\"},\n      {{{0.1, 1.}}}, used_for_size, 1.e-6);\n  pypp::check_with_random_values<1>(\n      wrap_dt_vars_corrections_ConstraintPreservingGaugePhysical<Dim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      {\"dt_spacetime_metric\", \"dt_pi_ConstraintPreservingGaugePhysical\",\n       \"dt_phi_ConstraintPreservingGaugePhysical\"},\n      {{{0.1, 1.}}}, used_for_size, 1.e-6);\n}\n}  // namespace\n\n// [[TimeOut, 20]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.BCBjorhus.Cls\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  test<1>();\n  test<2>();\n  test<3>();\n\n  const DataVector used_for_size(3);\n\n  test_with_random_values<1>(used_for_size);\n  test_with_random_values<2>(used_for_size);\n  test_with_random_values<3>(used_for_size);\n  test_incoming_wave_profile_option_parsing_and_dim_guard();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Test_BjorhusImpl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BjorhusImpl.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing frame = Frame::Inertial;\nconstexpr size_t VolumeDim = 3;\n\n// Test boundary conditions on dt<VSpacetimeMetric> in 3D against SpEC\nvoid test_constraint_preserving_bjorhus_v_psi_vs_spec_3d(\n    const size_t grid_size_each_dimension) {\n  // Setup grid\n  Mesh<VolumeDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const Direction<VolumeDim> direction(1, Side::Upper);  // +y direction\n  const size_t slice_grid_points =\n      mesh.extents().slice_away(direction.dimension()).product();\n\n  // Populate various tensors needed to compute BcDtVSpacetimeMetric\n  tnsr::iaa<DataVector, VolumeDim, frame> local_three_index_constraint(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::I<DataVector, VolumeDim, frame> local_unit_interface_normal_vector(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  std::array<DataVector, 4> local_char_speeds{\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN())};\n  // Allocate memory for output\n  tnsr::aa<DataVector, VolumeDim, frame> local_bc_dt_v_psi(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  {\n    // Setting the 3-index constraint\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = 0; b <= VolumeDim; ++b) {\n          // In SpEC, this constraint is explicitly computed using\n          // d_i psi_ab and phi_iab as inputs. The explicit subtractions\n          // below are here to remind us what values those two input\n          // tensors were set to in SpEC to get the desired BC in this test.\n          local_three_index_constraint.get(0, a, b)[i] = 11. - 3.;\n          local_three_index_constraint.get(1, a, b)[i] = 13. - 5.;\n          local_three_index_constraint.get(2, a, b)[i] = 17. - 7.;\n        }\n      }\n    }\n    // Setting unit_interface_normal_Vector\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      get<0>(local_unit_interface_normal_vector)[i] = -1.;\n      get<1>(local_unit_interface_normal_vector)[i] = 0.;\n      get<2>(local_unit_interface_normal_vector)[i] = 0.;\n    }\n    // Setting char speeds\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      local_char_speeds.at(0)[i] = -0.3;\n      local_char_speeds.at(1)[i] = -0.1;\n    }\n\n    // Compute rhs value\n    gh::BoundaryConditions::Bjorhus::constraint_preserving_corrections_dt_v_psi(\n        make_not_null(&local_bc_dt_v_psi), local_unit_interface_normal_vector,\n        local_three_index_constraint, local_char_speeds);\n    // Setting local_RhsVSpacetimeMetric\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_bc_dt_v_psi.get(0, a)[i] += 23.;\n      }\n      for (size_t a = 1; a <= VolumeDim; ++a) {\n        local_bc_dt_v_psi.get(1, a)[i] += 29.;\n      }\n      for (size_t a = 2; a <= VolumeDim; ++a) {\n        local_bc_dt_v_psi.get(2, a)[i] += 31.;\n      }\n      local_bc_dt_v_psi.get(3, 3)[i] += 37.;\n    }\n  }\n  // Initialize with values from SpEC\n  auto spec_bc_dt_v_psi =\n      make_with_value<tnsr::aa<DataVector, VolumeDim, frame>>(\n          local_bc_dt_v_psi, std::numeric_limits<double>::signaling_NaN());\n\n  for (size_t i = 0; i < slice_grid_points; ++i) {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      spec_bc_dt_v_psi.get(0, a)[i] = 25.4;\n    }\n    spec_bc_dt_v_psi.get(1, 1)[i] = 31.4;\n    spec_bc_dt_v_psi.get(1, 2)[i] = 31.4;\n    spec_bc_dt_v_psi.get(1, 3)[i] = 31.4;\n    spec_bc_dt_v_psi.get(2, 2)[i] = 33.4;\n    spec_bc_dt_v_psi.get(2, 3)[i] = 33.4;\n    spec_bc_dt_v_psi.get(3, 3)[i] = 39.4;\n  }\n\n  // Compare values returned by BC action vs those from SpEC\n  CHECK_ITERABLE_APPROX(local_bc_dt_v_psi, spec_bc_dt_v_psi);\n\n  // Test for another set of values\n  {\n    // Setting unit_interface_normal_Vector\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      local_unit_interface_normal_vector.get(0)[i] = -1.;\n      local_unit_interface_normal_vector.get(1)[i] = 1.;\n      local_unit_interface_normal_vector.get(2)[i] = 1.;\n    }\n    // Compute rhs value\n    gh::BoundaryConditions::Bjorhus::constraint_preserving_corrections_dt_v_psi(\n        make_not_null(&local_bc_dt_v_psi), local_unit_interface_normal_vector,\n        local_three_index_constraint, local_char_speeds);\n    // Setting local_RhsVSpacetimeMetric\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_bc_dt_v_psi.get(0, a)[i] += 23.;\n      }\n      for (size_t a = 1; a <= VolumeDim; ++a) {\n        local_bc_dt_v_psi.get(1, a)[i] += 29.;\n      }\n      for (size_t a = 2; a <= VolumeDim; ++a) {\n        local_bc_dt_v_psi.get(2, a)[i] += 31.;\n      }\n      local_bc_dt_v_psi.get(3, 3)[i] += 37.;\n    }\n  }\n\n  // Initialize with values from SpEC\n  for (size_t i = 0; i < slice_grid_points; ++i) {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      spec_bc_dt_v_psi.get(0, a)[i] = 20.;\n    }\n    get<1, 1>(spec_bc_dt_v_psi)[i] = 26.;\n    get<1, 2>(spec_bc_dt_v_psi)[i] = 26.;\n    get<1, 3>(spec_bc_dt_v_psi)[i] = 26.;\n    get<2, 2>(spec_bc_dt_v_psi)[i] = 28.;\n    get<2, 3>(spec_bc_dt_v_psi)[i] = 28.;\n    get<3, 3>(spec_bc_dt_v_psi)[i] = 34.;\n  }\n\n  // Compare values returned by BC action vs those from SpEC\n  CHECK_ITERABLE_APPROX(local_bc_dt_v_psi, spec_bc_dt_v_psi);\n}\n\n// Test boundary conditions on dt<VZero>\nvoid test_constraint_preserving_bjorhus_v_zero_vs_spec_3d(\n    const size_t grid_size_each_dimension) {\n  // Setup grid\n  Mesh<VolumeDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const Direction<VolumeDim> direction(1, Side::Upper);  // +y direction\n  const size_t slice_grid_points =\n      mesh.extents().slice_away(direction.dimension()).product();\n\n  // Populate various tensors needed to compute BcDtVZero\n  tnsr::iaa<DataVector, VolumeDim, frame> local_four_index_constraint(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::I<DataVector, VolumeDim, frame> local_unit_interface_normal_vector(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  std::array<DataVector, 4> local_char_speeds{\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN())};\n  // Allocate memory for output\n  tnsr::iaa<DataVector, VolumeDim, frame> local_bc_dt_v_zero(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  {\n    // Setting the 4-index constraint:\n    // initialize dPhi (with same values as for SpEC) and compute C4 from it\n    auto local_dphi = make_with_value<tnsr::ijaa<DataVector, VolumeDim, frame>>(\n        local_unit_interface_normal_vector,\n        std::numeric_limits<double>::signaling_NaN());\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = 0; b <= VolumeDim; ++b) {\n        for (size_t i = 0; i < slice_grid_points; ++i) {\n          local_dphi.get(0, 0, a, b)[i] = 3.;\n          local_dphi.get(0, 1, a, b)[i] = 5.;\n          local_dphi.get(0, 2, a, b)[i] = 7.;\n          local_dphi.get(1, 0, a, b)[i] = 59.;\n          local_dphi.get(1, 1, a, b)[i] = 61.;\n          local_dphi.get(1, 2, a, b)[i] = 67.;\n          local_dphi.get(2, 0, a, b)[i] = 73.;\n          local_dphi.get(2, 1, a, b)[i] = 79.;\n          local_dphi.get(2, 2, a, b)[i] = 83.;\n        }\n      }\n    }\n    // C4_{iab} = LeviCivita^{ijk} dphi_{jkab}\n    local_four_index_constraint = gh::four_index_constraint(local_dphi);\n\n    // Setting unit_interface_normal_Vector\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      get<0>(local_unit_interface_normal_vector)[i] = -1.;\n      get<1>(local_unit_interface_normal_vector)[i] = 1.;\n      get<2>(local_unit_interface_normal_vector)[i] = 1.;\n    }\n    // Setting char speeds\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      local_char_speeds.at(0)[i] = -0.3;\n      local_char_speeds.at(1)[i] = -0.1;\n    }\n\n    // Compute rhs value\n    gh::BoundaryConditions::Bjorhus::\n        constraint_preserving_corrections_dt_v_zero(\n            make_not_null(&local_bc_dt_v_zero),\n            local_unit_interface_normal_vector, local_four_index_constraint,\n            local_char_speeds);\n    // Setting local_Rhs\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = a; b <= VolumeDim; ++b) {\n          local_bc_dt_v_zero.get(0, a, b)[i] += 91.;\n          local_bc_dt_v_zero.get(1, a, b)[i] += 97.;\n          local_bc_dt_v_zero.get(2, a, b)[i] += 101.;\n        }\n      }\n    }\n  }\n  // Initialize with values from SpEC\n  auto spec_bc_dt_v_zero =\n      make_with_value<tnsr::iaa<DataVector, VolumeDim, frame>>(\n          local_bc_dt_v_zero, std::numeric_limits<double>::signaling_NaN());\n\n  for (size_t i = 0; i < slice_grid_points; ++i) {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      spec_bc_dt_v_zero.get(0, 0, a)[i] = 79.;\n      spec_bc_dt_v_zero.get(1, 0, a)[i] = 90.4;\n      spec_bc_dt_v_zero.get(2, 0, a)[i] = 95.6;\n    }\n    for (size_t a = 1; a <= VolumeDim; ++a) {\n      spec_bc_dt_v_zero.get(0, 1, a)[i] = 79.;\n      spec_bc_dt_v_zero.get(1, 1, a)[i] = 90.4;\n      spec_bc_dt_v_zero.get(2, 1, a)[i] = 95.6;\n    }\n\n    get<0, 2, 2>(spec_bc_dt_v_zero)[i] = 79.;\n    get<0, 2, 3>(spec_bc_dt_v_zero)[i] = 79.;\n    get<1, 2, 2>(spec_bc_dt_v_zero)[i] = 90.4;\n    get<1, 2, 3>(spec_bc_dt_v_zero)[i] = 90.4;\n    get<2, 2, 2>(spec_bc_dt_v_zero)[i] = 95.6;\n    get<2, 2, 3>(spec_bc_dt_v_zero)[i] = 95.6;\n\n    get<0, 3, 3>(spec_bc_dt_v_zero)[i] = 79.;\n    get<1, 3, 3>(spec_bc_dt_v_zero)[i] = 90.4;\n    get<2, 3, 3>(spec_bc_dt_v_zero)[i] = 95.6;\n  }\n\n  // Compare values returned by BC action vs those from SpEC\n  CHECK_ITERABLE_APPROX(local_bc_dt_v_zero, spec_bc_dt_v_zero);\n\n  // Test for another set of values\n  {\n    // Setting unit_interface_normal_Vector\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      get<0>(local_unit_interface_normal_vector)[i] = -1.;\n      get<1>(local_unit_interface_normal_vector)[i] = 0.;\n      get<2>(local_unit_interface_normal_vector)[i] = 0.;\n    }\n    // Compute rhs value\n    gh::BoundaryConditions::Bjorhus::\n        constraint_preserving_corrections_dt_v_zero(\n            make_not_null(&local_bc_dt_v_zero),\n            local_unit_interface_normal_vector, local_four_index_constraint,\n            local_char_speeds);\n    // Setting local_Rhs\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = a; b <= VolumeDim; ++b) {\n          local_bc_dt_v_zero.get(0, a, b)[i] += 91.;\n          local_bc_dt_v_zero.get(1, a, b)[i] += 97.;\n          local_bc_dt_v_zero.get(2, a, b)[i] += 101.;\n        }\n      }\n    }\n  }\n\n  // Initialize with values from SpEC\n  for (size_t i = 0; i < slice_grid_points; ++i) {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      spec_bc_dt_v_zero.get(0, 0, a)[i] = 91.;\n      spec_bc_dt_v_zero.get(1, 0, a)[i] = 91.6;\n      spec_bc_dt_v_zero.get(2, 0, a)[i] = 94.4;\n    }\n    for (size_t a = 1; a <= VolumeDim; ++a) {\n      spec_bc_dt_v_zero.get(0, 1, a)[i] = 91.;\n      spec_bc_dt_v_zero.get(1, 1, a)[i] = 91.6;\n      spec_bc_dt_v_zero.get(2, 1, a)[i] = 94.4;\n    }\n\n    get<0, 2, 2>(spec_bc_dt_v_zero)[i] = 91.;\n    get<0, 2, 3>(spec_bc_dt_v_zero)[i] = 91.;\n    get<1, 2, 2>(spec_bc_dt_v_zero)[i] = 91.6;\n    get<1, 2, 3>(spec_bc_dt_v_zero)[i] = 91.6;\n    get<2, 2, 2>(spec_bc_dt_v_zero)[i] = 94.4;\n    get<2, 2, 3>(spec_bc_dt_v_zero)[i] = 94.4;\n\n    get<0, 3, 3>(spec_bc_dt_v_zero)[i] = 91.;\n    get<1, 3, 3>(spec_bc_dt_v_zero)[i] = 91.6;\n    get<2, 3, 3>(spec_bc_dt_v_zero)[i] = 94.4;\n  }\n\n  // Compare values returned by BC action vs those from SpEC\n  CHECK_ITERABLE_APPROX(local_bc_dt_v_zero, spec_bc_dt_v_zero);\n}\n\nvoid test_constraint_preserving_gauge_physical_bjorhus_v_minus_vs_spec_3d(\n    const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& /* upper_bound */) {\n  // Setup grid\n  Mesh<VolumeDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  // Setup coordinates\n  const Direction<VolumeDim> direction(1, Side::Upper);  // +y direction\n  const size_t slice_grid_points =\n      mesh.extents().slice_away(direction.dimension()).product();\n  const auto inertial_coords = [&slice_grid_points, &lower_bound]() {\n    tnsr::I<DataVector, VolumeDim, frame> tmp(slice_grid_points, 0.);\n    // +y direction\n    get<1>(tmp) = 0.5;\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      for (size_t j = 0; j < VolumeDim; ++j) {\n        get<0>(tmp)[i * VolumeDim + j] =\n            lower_bound[0] + 0.5 * static_cast<double>(i);\n        get<2>(tmp)[i * VolumeDim + j] =\n            lower_bound[2] + 0.5 * static_cast<double>(j);\n      }\n    }\n    return tmp;\n  }();\n\n  // Populate various tensors needed to compute BcDtVMinus as done in SpEC\n  tnsr::I<DataVector, VolumeDim, frame> local_unit_interface_normal_vector(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::I<DataVector, VolumeDim, frame> local_inertial_coords(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  const auto& local_inertial_coords_x = get<0>(local_inertial_coords);\n  const auto& local_inertial_coords_y = get<1>(local_inertial_coords);\n  const auto& local_inertial_coords_z = get<2>(local_inertial_coords);\n  Scalar<DataVector> local_constraint_gamma2(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // timelike and spacelike SPACETIME vectors, l^a and k^a\n  tnsr::a<DataVector, VolumeDim, frame> local_outgoing_null_one_form(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::a<DataVector, VolumeDim, frame> local_incoming_null_one_form(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // timelike and spacelike SPACETIME oneforms, l_a and k_a\n  tnsr::A<DataVector, VolumeDim, frame> local_outgoing_null_vector(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::A<DataVector, VolumeDim, frame> local_incoming_null_vector(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // spacetime projection operator P_ab, P^ab, and P^a_b\n  tnsr::AA<DataVector, VolumeDim, frame> local_projection_AB(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::aa<DataVector, VolumeDim, frame> local_projection_ab(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::Ab<DataVector, VolumeDim, frame> local_projection_Ab(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // constraint characteristics\n  tnsr::a<DataVector, VolumeDim, frame> local_constraint_char_zero_minus(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::a<DataVector, VolumeDim, frame> local_constraint_char_zero_plus(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // RhsVSpacetimeMetric and RhsVMinus\n  tnsr::aa<DataVector, VolumeDim, frame> local_char_projected_rhs_dt_v_psi(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::aa<DataVector, VolumeDim, frame> local_char_projected_rhs_dt_v_minus(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // Vars\n  tnsr::aa<DataVector, VolumeDim, frame> local_pi(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::iaa<DataVector, VolumeDim, frame> local_phi(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // Char speeds\n  std::array<DataVector, 4> local_char_speeds{\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN()),\n      DataVector(slice_grid_points,\n                 std::numeric_limits<double>::signaling_NaN())};\n\n  // interface normal one form\n  tnsr::i<DataVector, VolumeDim, frame> local_unit_interface_normal_one_form(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // spacetime unit normal vec\n  tnsr::A<DataVector, VolumeDim, frame> local_spacetime_unit_normal_vector(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // metrics\n  tnsr::II<DataVector, VolumeDim, frame> local_inverse_spatial_metric(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::aa<DataVector, VolumeDim, frame> local_spacetime_metric(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::AA<DataVector, VolumeDim, frame> local_inverse_spacetime_metric(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // extrinsic curvature\n  tnsr::ii<DataVector, VolumeDim, frame> local_extrinsic_curvature(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // deriv of pi and phi\n  tnsr::iaa<DataVector, VolumeDim, frame> local_d_pi(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n  tnsr::ijaa<DataVector, VolumeDim, frame> local_d_phi(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  // 3-index constraint\n  tnsr::iaa<DataVector, VolumeDim, frame> local_three_index_constraint(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  {\n    // Setting coords\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      local_inertial_coords.get(i) = inertial_coords.get(i);\n    }\n\n    // Setting local_unit_interface_normal_one_form\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      local_unit_interface_normal_one_form.get(0)[i] = -1.;\n      local_unit_interface_normal_one_form.get(1)[i] = 1.;\n      local_unit_interface_normal_one_form.get(2)[i] = 1.;\n    }\n    // Setting local_spacetime_unit_normal_vector\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      local_spacetime_unit_normal_vector.get(0)[i] = -1.;\n      local_spacetime_unit_normal_vector.get(1)[i] = -3.;\n      local_spacetime_unit_normal_vector.get(2)[i] = -5.;\n      local_spacetime_unit_normal_vector.get(3)[i] = -7.;\n    }\n    // Setting local_inverse_spatial_metric\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t j = 0; j < VolumeDim; ++j) {\n        local_inverse_spatial_metric.get(0, j)[i] = 41.;\n        local_inverse_spatial_metric.get(1, j)[i] = 43.;\n        local_inverse_spatial_metric.get(2, j)[i] = 47.;\n      }\n    }\n    // Setting local_spacetime_metric\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_spacetime_metric.get(0, a)[i] = 257.;\n        local_spacetime_metric.get(1, a)[i] = 263.;\n        local_spacetime_metric.get(2, a)[i] = 269.;\n        local_spacetime_metric.get(3, a)[i] = 271.;\n      }\n    }\n    // Setting local_inverse_spacetime_metric\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_inverse_spacetime_metric.get(0, a)[i] =\n            -277.;  // needs to be < 0 for lapse\n        local_inverse_spacetime_metric.get(1, a)[i] = 281.;\n        local_inverse_spacetime_metric.get(2, a)[i] = 283.;\n        local_inverse_spacetime_metric.get(3, a)[i] = 293.;\n      }\n    }\n    // Setting local_extrinsic_curvature\n    // ONLY ON THE +Y AXIS (Y = +0.5)\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      std::array<double, 9> spec_vals{};\n      bool not_initialized = true;\n\n      if ((local_inertial_coords_x[i] == 299. or\n           local_inertial_coords_x[i] == 299.5 or\n           local_inertial_coords_x[i] == 300.) and\n          local_inertial_coords_y[i] == 0.5 and\n          (local_inertial_coords_z[i] == -0.5 or\n           local_inertial_coords_z[i] == 0. or\n           local_inertial_coords_z[i] == 0.5)) {\n        spec_vals = {{200.2198037251189, 266.7930716334918, 333.3663395418648,\n                      266.7930716334918, 333.3663395418648, 399.9396074502377,\n                      333.3663395418648, 399.9396074502377, 466.5128753586106}};\n        not_initialized = false;\n      }\n\n      if (not_initialized) {\n        ERROR(\"Not checking the correct face, coordinates not recognized\");\n      }\n      for (size_t j = 0; j < VolumeDim; ++j) {\n        for (size_t k = 0; k < VolumeDim; ++k) {\n          local_extrinsic_curvature.get(j, k)[i] =\n              gsl::at(spec_vals, j * (0 + VolumeDim) + k);\n        }\n      }\n    }\n\n    // Setting local_d_phi\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = 0; b <= VolumeDim; ++b) {\n          local_d_pi.get(0, a, b)[i] = 1.;\n          local_d_phi.get(0, 0, a, b)[i] = 3.;\n          local_d_phi.get(0, 1, a, b)[i] = 5.;\n          local_d_phi.get(0, 2, a, b)[i] = 7.;\n          local_d_pi.get(1, a, b)[i] = 53.;\n          local_d_phi.get(1, 0, a, b)[i] = 59.;\n          local_d_phi.get(1, 1, a, b)[i] = 61.;\n          local_d_phi.get(1, 2, a, b)[i] = 67.;\n          local_d_pi.get(2, a, b)[i] = 71.;\n          local_d_phi.get(2, 0, a, b)[i] = 73.;\n          local_d_phi.get(2, 1, a, b)[i] = 79.;\n          local_d_phi.get(2, 2, a, b)[i] = 83.;\n        }\n      }\n    }\n    // Setting 3idxConstraint\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = 0; b <= VolumeDim; ++b) {\n          local_three_index_constraint.get(0, a, b)[i] = 11. - 3.;\n          local_three_index_constraint.get(1, a, b)[i] = 13. - 5.;\n          local_three_index_constraint.get(2, a, b)[i] = 17. - 7.;\n        }\n      }\n    }\n\n    // Setting constraint_gamma2\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      get(local_constraint_gamma2)[i] = 113.;\n    }\n    // Note: explicit division by sqrt(2) below for the computation of ui, uI,\n    // vi, vI are left as-is to remind us that SpEC uses these null vectors\n    // and one forms without normalization by sqrt(2).\n    // Setting incoming null one_form: ui\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      local_incoming_null_one_form.get(0)[i] = -2. / sqrt(2.);\n      local_incoming_null_one_form.get(1)[i] = 5. / sqrt(2.);\n      local_incoming_null_one_form.get(2)[i] = 3. / sqrt(2.);\n      local_incoming_null_one_form.get(3)[i] = 7. / sqrt(2.);\n    }\n    // Setting incoming null vector: uI\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      local_incoming_null_vector.get(0)[i] = -1. / sqrt(2.);\n      local_incoming_null_vector.get(1)[i] = 13. / sqrt(2.);\n      local_incoming_null_vector.get(2)[i] = 17. / sqrt(2.);\n      local_incoming_null_vector.get(3)[i] = 19. / sqrt(2.);\n    }\n    // Setting outgoing null one_form: vi\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      local_outgoing_null_one_form.get(0)[i] = -1. / sqrt(2.);\n      local_outgoing_null_one_form.get(1)[i] = 3. / sqrt(2.);\n      local_outgoing_null_one_form.get(2)[i] = 2. / sqrt(2.);\n      local_outgoing_null_one_form.get(3)[i] = 5. / sqrt(2.);\n    }\n    // Setting outgoing null vector: vI\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      local_outgoing_null_vector.get(0)[i] = -1. / sqrt(2.);\n      local_outgoing_null_vector.get(1)[i] = 2. / sqrt(2.);\n      local_outgoing_null_vector.get(2)[i] = 3. / sqrt(2.);\n      local_outgoing_null_vector.get(3)[i] = 5. / sqrt(2.);\n    }\n    // Setting projection Ab\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_projection_Ab.get(0, a)[i] = 233.;\n        local_projection_Ab.get(1, a)[i] = 239.;\n        local_projection_Ab.get(2, a)[i] = 241.;\n        local_projection_Ab.get(3, a)[i] = 251.;\n      }\n    }\n    // Setting projection ab\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_projection_ab.get(0, a)[i] = 379.;\n        local_projection_ab.get(1, a)[i] = 383.;\n        local_projection_ab.get(2, a)[i] = 389.;\n        local_projection_ab.get(3, a)[i] = 397.;\n      }\n    }\n    // Setting projection AB\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_projection_AB.get(0, a)[i] = 353.;\n        local_projection_AB.get(1, a)[i] = 359.;\n        local_projection_AB.get(2, a)[i] = 367.;\n        local_projection_AB.get(3, a)[i] = 373.;\n      }\n    }\n    // Setting local_RhsVSpacetimeMetric\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_char_projected_rhs_dt_v_psi.get(0, a)[i] = 23.;\n        local_char_projected_rhs_dt_v_psi.get(1, a)[i] = 29.;\n        local_char_projected_rhs_dt_v_psi.get(2, a)[i] = 31.;\n        local_char_projected_rhs_dt_v_psi.get(3, a)[i] = 37.;\n      }\n    }\n    // Setting RhsVMinus\n    for (size_t i = 0; i < get<0>(local_inertial_coords).size(); ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        local_char_projected_rhs_dt_v_minus.get(0, a)[i] = 331.;\n        local_char_projected_rhs_dt_v_minus.get(1, a)[i] = 337.;\n        local_char_projected_rhs_dt_v_minus.get(2, a)[i] = 347.;\n        local_char_projected_rhs_dt_v_minus.get(3, a)[i] = 349.;\n      }\n    }\n\n    // Setting unit_interface_normal_Vector\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      get<0>(local_unit_interface_normal_vector)[i] = -1.;\n      get<1>(local_unit_interface_normal_vector)[i] = 1.;\n      get<2>(local_unit_interface_normal_vector)[i] = 1.;\n    }\n    // Setting pi AND phi\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = 0; b <= VolumeDim; ++b) {\n          local_pi.get(a, b)[i] = 1.;\n          local_phi.get(0, a, b)[i] = 3.;\n          local_phi.get(1, a, b)[i] = 5.;\n          local_phi.get(2, a, b)[i] = 7.;\n        }\n      }\n    }\n    // Setting char speeds\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      local_char_speeds.at(0)[i] = -0.3;\n      local_char_speeds.at(1)[i] = -0.1;\n      local_char_speeds.at(3)[i] = -0.2;\n    }\n    // Setting constraint_char_zero_plus AND constraint_char_zero_minus\n    // ONLY ON THE +Y AXIS (Y = +0.5)\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      std::array<double, 4> spec_vals{};\n      std::array<double, 4> spec_vals2{};\n      bool not_initialized = true;\n\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{3722388974.799386, -16680127.68747905, -22991565.68745775,\n                      -29394198.68743645}};\n        spec_vals2 = {{3866802572.424386, -19802442.06247905,\n                       -19496408.06245775, -19257249.06243645}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{1718287695.133032, -35082292.16184025, -44420849.32723039,\n                      -53850596.45928719}};\n        spec_vals2 = {{1866652790.548975, -38170999.90741873,\n                       -40892085.07280888, -43680040.20486567}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{1718299060.415949, -35082089.27527188, -44420613.14790046,\n                      -53850326.98725116}};\n        spec_vals2 = {{1866664134.129611, -38170797.39904065,\n                       -40891849.27166924, -43679771.11101994}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{1718276282.501221, -35082495.89577083, -44421086.49296889,\n                      -53850867.05677793}};\n        spec_vals2 = {{1866641399.709844, -38171203.26157932,\n                       -40892321.85877737, -43680310.4225864}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{1718287685.660846, -35082292.33093269, -44420849.52407002,\n                      -53850596.68387395}};\n        spec_vals2 = {{1866652781.094877, -38171000.07619599,\n                       -40892085.26933331, -43680040.42913724}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{1718299050.990844, -35082089.44352208, -44420613.34375966,\n                      -53850327.21071925}};\n        spec_vals2 = {{1866664124.722503, -38170797.56697725,\n                       -40891849.46721483, -43679771.33417442}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{1718276292.082241, -35082495.72473276, -44421086.29386414,\n                      -53850866.82960649}};\n        spec_vals2 = {{1866641409.272568, -38171203.09086005,\n                       -40892321.65999144, -43680310.19573379}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{1718287695.194063, -35082292.16074976, -44420849.32596073,\n                      -53850596.45783833}};\n        spec_vals2 = {{1866652790.609889, -38170999.90633029,\n                       -40892085.07154126, -43680040.20341886}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{1718299060.476573, -35082089.27418864, -44420613.14663924,\n                      -53850326.98581193}};\n        spec_vals2 = {{1866664134.190119, -38170797.39795944,\n                       -40891849.27041004, -43679771.10958273}};\n        not_initialized = false;\n      }\n      if (not_initialized) {\n        ERROR(\"Not checking the correct face, coordinates not recognized\");\n      }\n      for (size_t j = 0; j <= VolumeDim; ++j) {\n        local_constraint_char_zero_plus.get(j)[i] = gsl::at(spec_vals, j);\n        local_constraint_char_zero_minus.get(j)[i] = gsl::at(spec_vals2, j);\n      }\n    }\n  }\n\n  // Memory for output\n  tnsr::aa<DataVector, VolumeDim, frame> local_bc_dt_v_minus(\n      slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n  {\n    // Compute the new boundary corrections for dt<vminus>\n    gh::BoundaryConditions::Bjorhus::\n        constraint_preserving_gauge_corrections_dt_v_minus(\n            make_not_null(&local_bc_dt_v_minus), local_constraint_gamma2,\n            local_inertial_coords, local_incoming_null_one_form,\n            local_outgoing_null_one_form, local_incoming_null_vector,\n            local_outgoing_null_vector, local_projection_ab,\n            local_projection_Ab, local_projection_AB,\n            local_char_projected_rhs_dt_v_psi,\n            local_char_projected_rhs_dt_v_minus,\n            local_constraint_char_zero_plus, local_constraint_char_zero_minus,\n            local_char_speeds);\n    // Add in the current boundary value to get corrected values for dt<vminus>\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = a; b <= VolumeDim; ++b) {\n        local_bc_dt_v_minus.get(a, b) +=\n            local_char_projected_rhs_dt_v_minus.get(a, b);\n      }\n    }\n\n    // Initialize with values from SpEC\n    tnsr::aa<DataVector, VolumeDim, frame> spec_bc_dt_v_minus(\n        slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      std::array<double, 16> spec_vals{};\n      bool not_initialized = true;\n\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{-22857869555.93848, 330934979077.5689, 242482973593.2169,\n                      507808643438.4713, 330934979077.5689, 690190606335.8783,\n                      600780523621.8837, 868984678067.689, 242482973593.2169,\n                      600780523621.8837, 514052337348.0055, 781537245156.9633,\n                      507808643438.4713, 868984678067.689, 781537245156.9633,\n                      1054439575483.625}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{6085210488.394159, 167380193629.8459, 127052654518.3203,\n                      248004925243.5957, 167380193629.8459, 332680231531.8318,\n                      291581334988.8124, 414851930920.4022, 127052654518.3203,\n                      291581334988.8124, 252051387928.7005, 374742777072.0203,\n                      248004925243.5957, 414851930920.4022, 374742777072.0203,\n                      501009779843.9042}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{6084984067.712499, 167381093626.4053, 127053272910.3827,\n                      248006388447.6548, 167381093626.4053, 332682261170.2852,\n                      291583083105.2007, 414854523601.7003, 127053272910.3827,\n                      291583083105.2007, 252052859833.8237, 374745093603.8436,\n                      248006388447.6548, 414854523601.7003, 374745093603.8436,\n                      501012947925.7576}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{6085437853.704128, 167379289884.4027, 127052033550.7529,\n                      248003455943.9015, 167379289884.4027, 332678193437.6569,\n                      291579579589.7132, 414849327437.366, 127052033550.7529,\n                      291579579589.7132, 252049909891.7733, 374740450889.0901,\n                      248003455943.9015, 414849327437.366, 374740450889.0901,\n                      501006598562.8735}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{6085210677.100476, 167380192879.7604, 127052654002.9328,\n                      248004924024.1152, 167380192879.7604, 332680229840.2671,\n                      291581333531.8772, 414851928759.5795, 127052654002.9328,\n                      291581333531.8772, 252051386701.9684, 374742775141.3494,\n                      248004924024.1152, 414851928759.5795, 374742775141.3494,\n                      501009777203.5247}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{6084984255.479748, 167381092880.0475, 127053272397.5563,\n                      248006387234.2355, 167381092880.0475, 332682259487.1282,\n                      291583081655.5068, 414854521451.6181, 127053272397.5563,\n                      291583081655.5068, 252052858613.1887, 374745091682.769,\n                      248006387234.2355, 414854521451.6181, 374745091682.769,\n                      501012945298.5021}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{6085437662.827703, 167379290643.1056, 127052034072.0609,\n                      248003457177.3931, 167379290643.1056, 332678195148.6575,\n                      291579581063.3884, 414849329623.0162, 127052034072.0609,\n                      291579581063.3884, 252049911132.6, 374740452841.9442,\n                      248003457177.3931, 414849329623.0162, 374740452841.9442,\n                      501006601233.5914}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{6085210487.177534, 167380193634.6784, 127052654521.6405,\n                      248004925251.4529, 167380193634.6784, 332680231542.7307,\n                      291581334998.1996, 414851930934.3248, 127052654521.6405,\n                      291581334998.1996, 252051387936.6043, 374742777084.4599,\n                      248004925251.4529, 414851930934.3248, 374742777084.4599,\n                      501009779860.9169}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{6084984066.503965, 167381093631.2057, 127053272913.6808,\n                      248006388455.4596, 167381093631.2057, 332682261181.1116,\n                      291583083114.5252, 414854523615.53, 127053272913.6808,\n                      291583083114.5252, 252052859841.6748, 374745093616.2003,\n                      248006388455.4596, 414854523615.53, 374745093616.2003,\n                      501012947942.6568}};\n        not_initialized = false;\n      }\n      if (not_initialized) {\n        ERROR(\"Not checking the correct face, coordinates not recognized\");\n      }\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = a; b <= VolumeDim; ++b) {\n          spec_bc_dt_v_minus.get(a, b)[i] =\n              gsl::at(spec_vals, a * (1 + VolumeDim) + b);\n        }\n      }\n    }\n\n    // Compare values returned by BC action vs those from SpEC\n    CHECK_ITERABLE_APPROX(local_bc_dt_v_minus, spec_bc_dt_v_minus);\n  }\n\n  {  // Compute the new boundary corrections for dt<vminus>\n    gh::BoundaryConditions::Bjorhus::\n        constraint_preserving_gauge_physical_corrections_dt_v_minus(\n            make_not_null(&local_bc_dt_v_minus), local_constraint_gamma2,\n            local_inertial_coords, 0., local_unit_interface_normal_one_form,\n            local_unit_interface_normal_vector,\n            local_spacetime_unit_normal_vector, local_incoming_null_one_form,\n            local_outgoing_null_one_form, local_incoming_null_vector,\n            local_outgoing_null_vector, local_projection_ab,\n            local_projection_Ab, local_projection_AB,\n            local_inverse_spatial_metric, local_extrinsic_curvature,\n            local_spacetime_metric, local_inverse_spacetime_metric,\n            local_three_index_constraint, local_char_projected_rhs_dt_v_psi,\n            local_char_projected_rhs_dt_v_minus,\n            local_constraint_char_zero_plus, local_constraint_char_zero_minus,\n            local_phi, local_d_phi, local_d_pi, local_char_speeds, nullptr);\n    // Add in the current boundary value to get corrected values for dt<vminus>\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = a; b <= VolumeDim; ++b) {\n        local_bc_dt_v_minus.get(a, b) +=\n            local_char_projected_rhs_dt_v_minus.get(a, b);\n      }\n    }\n\n    // Initialize with values from SpEC\n    tnsr::aa<DataVector, VolumeDim, frame> spec_bc_dt_v_minus(\n        slice_grid_points, std::numeric_limits<double>::signaling_NaN());\n\n    for (size_t i = 0; i < slice_grid_points; ++i) {\n      std::array<double, 16> spec_vals{};\n      bool not_initialized = true;\n\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{7.122948293721196e+19, 7.122948329100482e+19,\n                      7.122948320255282e+19, 7.122948346787851e+19,\n                      7.122948329100482e+19, 7.639071460057165e+19,\n                      7.639071451116156e+19, 7.639071477936572e+19,\n                      7.122948320255282e+19, 7.639071451116156e+19,\n                      8.413256084990011e+19, 8.413256111738503e+19,\n                      7.122948346787851e+19, 7.639071477936572e+19,\n                      8.413256111738503e+19, 9.445502329090969e+19}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{7.122948296615504e+19, 7.122948312745003e+19,\n                      7.122948308712251e+19, 7.122948320807479e+19,\n                      7.122948312745003e+19, 7.639071424306128e+19,\n                      7.639071420196236e+19, 7.639071432523298e+19,\n                      7.122948308712251e+19, 7.639071420196236e+19,\n                      8.413256058789916e+19, 8.413256071059056e+19,\n                      7.122948320807479e+19, 7.639071432523298e+19,\n                      8.413256071059056e+19, 9.445502273747989e+19}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == -0.5) {\n        spec_vals = {{7.122948296615481e+19, 7.122948312745094e+19,\n                      7.122948308712312e+19, 7.122948320807626e+19,\n                      7.122948312745094e+19, 7.639071424306332e+19,\n                      7.639071420196412e+19, 7.639071432523558e+19,\n                      7.122948308712312e+19, 7.639071420196412e+19,\n                      8.413256058790063e+19, 8.413256071059287e+19,\n                      7.122948320807626e+19, 7.639071432523558e+19,\n                      8.413256071059287e+19, 9.445502273748306e+19}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{7.122948296615527e+19, 7.122948312744913e+19,\n                      7.122948308712188e+19, 7.122948320807332e+19,\n                      7.122948312744913e+19, 7.639071424305924e+19,\n                      7.639071420196061e+19, 7.639071432523037e+19,\n                      7.122948308712188e+19, 7.639071420196061e+19,\n                      8.413256058789768e+19, 8.413256071058824e+19,\n                      7.122948320807332e+19, 7.639071432523037e+19,\n                      8.413256071058824e+19, 9.445502273747671e+19}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{7.122948296615504e+19, 7.122948312745003e+19,\n                      7.122948308712251e+19, 7.122948320807479e+19,\n                      7.122948312745003e+19, 7.639071424306128e+19,\n                      7.639071420196236e+19, 7.639071432523298e+19,\n                      7.122948308712251e+19, 7.639071420196236e+19,\n                      8.413256058789916e+19, 8.413256071059056e+19,\n                      7.122948320807479e+19, 7.639071432523298e+19,\n                      8.413256071059056e+19, 9.445502273747989e+19}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.) {\n        spec_vals = {{7.122948296615482e+19, 7.122948312745094e+19,\n                      7.122948308712312e+19, 7.122948320807626e+19,\n                      7.122948312745094e+19, 7.63907142430633e+19,\n                      7.63907142019641e+19, 7.639071432523556e+19,\n                      7.122948308712312e+19, 7.63907142019641e+19,\n                      8.413256058790063e+19, 8.413256071059287e+19,\n                      7.122948320807626e+19, 7.639071432523556e+19,\n                      8.413256071059287e+19, 9.445502273748306e+19}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{7.122948296615527e+19, 7.122948312744913e+19,\n                      7.122948308712188e+19, 7.122948320807332e+19,\n                      7.122948312744913e+19, 7.639071424305924e+19,\n                      7.639071420196061e+19, 7.639071432523039e+19,\n                      7.122948308712188e+19, 7.639071420196061e+19,\n                      8.413256058789768e+19, 8.413256071058824e+19,\n                      7.122948320807332e+19, 7.639071432523039e+19,\n                      8.413256071058824e+19, 9.445502273747671e+19}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 299.5 and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{7.122948296615504e+19, 7.122948312745003e+19,\n                      7.122948308712251e+19, 7.122948320807479e+19,\n                      7.122948312745003e+19, 7.639071424306128e+19,\n                      7.639071420196236e+19, 7.639071432523298e+19,\n                      7.122948308712251e+19, 7.639071420196236e+19,\n                      8.413256058789916e+19, 8.413256071059056e+19,\n                      7.122948320807479e+19, 7.639071432523298e+19,\n                      8.413256071059056e+19, 9.445502273747989e+19}};\n        not_initialized = false;\n      }\n      if (local_inertial_coords_x[i] == 300. and\n          local_inertial_coords_y[i] == 0.5 and\n          local_inertial_coords_z[i] == 0.5) {\n        spec_vals = {{7.122948296615481e+19, 7.122948312745094e+19,\n                      7.122948308712312e+19, 7.122948320807626e+19,\n                      7.122948312745094e+19, 7.639071424306332e+19,\n                      7.639071420196412e+19, 7.639071432523558e+19,\n                      7.122948308712312e+19, 7.639071420196412e+19,\n                      8.413256058790063e+19, 8.413256071059287e+19,\n                      7.122948320807626e+19, 7.639071432523558e+19,\n                      8.413256071059287e+19, 9.445502273748306e+19}};\n        not_initialized = false;\n      }\n      if (not_initialized) {\n        ERROR(\"Not checking the correct face, coordinates not recognized\");\n      }\n      for (size_t a = 0; a <= VolumeDim; ++a) {\n        for (size_t b = a; b <= VolumeDim; ++b) {\n          spec_bc_dt_v_minus.get(a, b)[i] =\n              gsl::at(spec_vals, a * (1 + VolumeDim) + b);\n        }\n      }\n    }\n\n    // Compare values returned by BC action vs those from SpEC\n    CHECK_ITERABLE_APPROX(local_bc_dt_v_minus, spec_bc_dt_v_minus);\n  }\n}\n}  // namespace\n\n// Python tests\nnamespace {\ntemplate <size_t VolumeDim>\ntnsr::aa<DataVector, VolumeDim, Frame::Inertial> wrapper_func_v_psi(\n    const tnsr::I<DataVector, VolumeDim, Frame::Inertial>&\n        interface_normal_vector,\n    const tnsr::iaa<DataVector, VolumeDim, Frame::Inertial>&\n        three_index_constraint,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds) {\n  std::array<DataVector, 4> char_speed_array{\n      {char_speeds.get(0), char_speeds.get(1), char_speeds.get(2),\n       char_speeds.get(3)}};\n  auto dt_v_psi =\n      make_with_value<tnsr::aa<DataVector, VolumeDim, Frame::Inertial>>(\n          interface_normal_vector, 0.);\n  gh::BoundaryConditions::Bjorhus::constraint_preserving_corrections_dt_v_psi<\n      VolumeDim, DataVector>(make_not_null(&dt_v_psi), interface_normal_vector,\n                             three_index_constraint, char_speed_array);\n  return dt_v_psi;\n}\n\ntemplate <size_t VolumeDim>\nvoid test_constraint_preserving_corrections_dt_v_psi(\n    const size_t grid_size_each_dimension) {\n  pypp::check_with_random_values<1>(\n      &wrapper_func_v_psi<VolumeDim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      \"constraint_preserving_corrections_dt_v_psi\", {{{-1., 1.}}},\n      DataVector(grid_size_each_dimension));\n}\n\ntemplate <size_t VolumeDim>\ntnsr::iaa<DataVector, VolumeDim, Frame::Inertial> wrapper_func_v_zero(\n    const tnsr::I<DataVector, VolumeDim, Frame::Inertial>&\n        interface_normal_vector,\n    const tnsr::iaa<DataVector, VolumeDim, Frame::Inertial>&\n        four_index_constraint,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds) {\n  std::array<DataVector, 4> char_speed_array{\n      char_speeds.get(0), char_speeds.get(1), char_speeds.get(2),\n      char_speeds.get(3)};\n  auto dt_v_zero =\n      make_with_value<tnsr::iaa<DataVector, VolumeDim, Frame::Inertial>>(\n          get<0>(interface_normal_vector), 0.);\n  gh::BoundaryConditions::Bjorhus::constraint_preserving_corrections_dt_v_zero<\n      VolumeDim, DataVector>(make_not_null(&dt_v_zero), interface_normal_vector,\n                             four_index_constraint, char_speed_array);\n  return dt_v_zero;\n}\n\ntemplate <size_t VolumeDim>\nvoid test_constraint_preserving_corrections_dt_v_zero(\n    const size_t grid_size_each_dimension) {\n  pypp::check_with_random_values<1>(\n      &wrapper_func_v_zero<VolumeDim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      \"constraint_preserving_corrections_dt_v_zero\", {{{-1., 1.}}},\n      DataVector(grid_size_each_dimension));\n}\n\ntemplate <size_t VolumeDim>\ntnsr::aa<DataVector, VolumeDim, Frame::Inertial> wrapper_func_cp_v_minus(\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        outgoing_null_one_form,\n    const tnsr::A<DataVector, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataVector, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataVector, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataVector, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds) {\n  const std::array<DataVector, 4> char_speed_array{\n      char_speeds.get(0), char_speeds.get(1), char_speeds.get(2),\n      char_speeds.get(3)};\n  auto dt_v_minus =\n      make_with_value<tnsr::aa<DataVector, VolumeDim, Frame::Inertial>>(\n          get<0>(outgoing_null_one_form), 0.);\n  gh::BoundaryConditions::Bjorhus::constraint_preserving_corrections_dt_v_minus<\n      VolumeDim, DataVector>(\n      make_not_null(&dt_v_minus), outgoing_null_one_form, incoming_null_vector,\n      outgoing_null_vector, projection_ab, projection_Ab, projection_AB,\n      char_projected_rhs_dt_v_minus, constraint_char_zero_plus,\n      constraint_char_zero_minus, char_speed_array);\n  return dt_v_minus;\n}\n\ntemplate <size_t VolumeDim>\ntnsr::aa<DataVector, VolumeDim, Frame::Inertial> wrapper_func_cpg_v_minus(\n    const Scalar<DataVector>& gamma2,\n    const tnsr::I<DataVector, VolumeDim, Frame::Inertial>& inertial_coords,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        incoming_null_one_form,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        outgoing_null_one_form,\n    const tnsr::A<DataVector, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataVector, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataVector, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataVector, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_psi,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds) {\n  std::array<DataVector, 4> char_speed_array{\n      char_speeds.get(0), char_speeds.get(1), char_speeds.get(2),\n      char_speeds.get(3)};\n  auto dt_v_minus =\n      make_with_value<tnsr::aa<DataVector, VolumeDim, Frame::Inertial>>(\n          get(gamma2), 0.);\n  gh::BoundaryConditions::Bjorhus::\n      constraint_preserving_gauge_corrections_dt_v_minus<VolumeDim, DataVector>(\n          make_not_null(&dt_v_minus), gamma2, inertial_coords,\n          incoming_null_one_form, outgoing_null_one_form, incoming_null_vector,\n          outgoing_null_vector, projection_ab, projection_Ab, projection_AB,\n          char_projected_rhs_dt_v_psi, char_projected_rhs_dt_v_minus,\n          constraint_char_zero_plus, constraint_char_zero_minus,\n          char_speed_array);\n  return dt_v_minus;\n}\n\ntemplate <size_t VolumeDim>\ntnsr::aa<DataVector, VolumeDim, Frame::Inertial> wrapper_func_cpgp_v_minus(\n    const Scalar<DataVector>& gamma2,\n    const tnsr::I<DataVector, VolumeDim, Frame::Inertial>& inertial_coords,\n    const tnsr::i<DataVector, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_one_form,\n    const tnsr::I<DataVector, VolumeDim, Frame::Inertial>&\n        unit_interface_normal_vector,\n    const tnsr::A<DataVector, VolumeDim, Frame::Inertial>&\n        spacetime_unit_normal_vector,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        incoming_null_one_form,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        outgoing_null_one_form,\n    const tnsr::A<DataVector, VolumeDim, Frame::Inertial>& incoming_null_vector,\n    const tnsr::A<DataVector, VolumeDim, Frame::Inertial>& outgoing_null_vector,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>& projection_ab,\n    const tnsr::Ab<DataVector, VolumeDim, Frame::Inertial>& projection_Ab,\n    const tnsr::AA<DataVector, VolumeDim, Frame::Inertial>& projection_AB,\n    const tnsr::II<DataVector, VolumeDim, Frame::Inertial>&\n        inverse_spatial_metric,\n    const tnsr::ii<DataVector, VolumeDim, Frame::Inertial>& extrinsic_curvature,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>& spacetime_metric,\n    const tnsr::AA<DataVector, VolumeDim, Frame::Inertial>&\n        inverse_spacetime_metric,\n    const tnsr::iaa<DataVector, VolumeDim, Frame::Inertial>&\n        three_index_constraint,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_psi,\n    const tnsr::aa<DataVector, VolumeDim, Frame::Inertial>&\n        char_projected_rhs_dt_v_minus,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_plus,\n    const tnsr::a<DataVector, VolumeDim, Frame::Inertial>&\n        constraint_char_zero_minus,\n    const tnsr::iaa<DataVector, VolumeDim, Frame::Inertial>& phi,\n    const tnsr::ijaa<DataVector, VolumeDim, Frame::Inertial>& d_phi,\n    const tnsr::iaa<DataVector, VolumeDim, Frame::Inertial>& d_pi,\n    const tnsr::a<DataVector, 3, Frame::Inertial>& char_speeds) {\n  std::array<DataVector, 4> char_speed_array{\n      char_speeds.get(0), char_speeds.get(1), char_speeds.get(2),\n      char_speeds.get(3)};\n  auto dt_v_minus =\n      make_with_value<tnsr::aa<DataVector, VolumeDim, Frame::Inertial>>(\n          get(gamma2), 0.);\n  gh::BoundaryConditions::Bjorhus::\n      constraint_preserving_gauge_physical_corrections_dt_v_minus<VolumeDim,\n                                                                  DataVector>(\n          make_not_null(&dt_v_minus), gamma2, inertial_coords, 0.,\n          unit_interface_normal_one_form, unit_interface_normal_vector,\n          spacetime_unit_normal_vector, incoming_null_one_form,\n          outgoing_null_one_form, incoming_null_vector, outgoing_null_vector,\n          projection_ab, projection_Ab, projection_AB, inverse_spatial_metric,\n          extrinsic_curvature, spacetime_metric, inverse_spacetime_metric,\n          three_index_constraint, char_projected_rhs_dt_v_psi,\n          char_projected_rhs_dt_v_minus, constraint_char_zero_plus,\n          constraint_char_zero_minus, phi, d_phi, d_pi, char_speed_array,\n          nullptr);\n  return dt_v_minus;\n}\n\nvoid test_incoming_wave_profile_uses_profile_value_in_3d() {\n  constexpr size_t local_volume_dim = 3;\n  const size_t num_points = 2;\n  const double time = 0.37;\n\n  auto gamma2 = make_with_value<Scalar<DataVector>>(num_points, 0.5);\n  auto inertial_coords =\n      make_with_value<tnsr::I<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    1.0);\n  inertial_coords.get(0) = DataVector{2.0, 2.5};\n  inertial_coords.get(1) = DataVector{1.0, 1.5};\n  inertial_coords.get(2) = DataVector{0.8, 1.2};\n\n  auto unit_interface_normal_one_form =\n      make_with_value<tnsr::i<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  auto unit_interface_normal_vector =\n      make_with_value<tnsr::I<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  unit_interface_normal_one_form.get(0) = 1.0;\n  unit_interface_normal_vector.get(0) = 1.0;\n\n  auto spacetime_unit_normal_vector =\n      make_with_value<tnsr::A<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  spacetime_unit_normal_vector.get(0) = 1.0;\n\n  auto incoming_null_one_form =\n      make_with_value<tnsr::a<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  auto outgoing_null_one_form =\n      make_with_value<tnsr::a<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  auto incoming_null_vector =\n      make_with_value<tnsr::A<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  auto outgoing_null_vector =\n      make_with_value<tnsr::A<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  incoming_null_one_form.get(0) = 1.0;\n  outgoing_null_one_form.get(0) = 1.0;\n  incoming_null_vector.get(0) = 1.0;\n  outgoing_null_vector.get(0) = 1.0;\n\n  auto projection_ab =\n      make_with_value<tnsr::aa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto projection_Ab =\n      make_with_value<tnsr::Ab<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto projection_AB =\n      make_with_value<tnsr::AA<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  for (size_t a = 0; a <= local_volume_dim; ++a) {\n    projection_ab.get(a, a) = 1.0;\n    projection_AB.get(a, a) = 1.0;\n    projection_Ab.get(a, a) = 1.0;\n  }\n\n  auto inverse_spatial_metric =\n      make_with_value<tnsr::II<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto extrinsic_curvature =\n      make_with_value<tnsr::ii<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  for (size_t i = 0; i < local_volume_dim; ++i) {\n    inverse_spatial_metric.get(i, i) = 1.0;\n  }\n\n  auto spacetime_metric =\n      make_with_value<tnsr::aa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto inverse_spacetime_metric =\n      make_with_value<tnsr::AA<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  for (size_t a = 0; a <= local_volume_dim; ++a) {\n    spacetime_metric.get(a, a) = (a == 0 ? -1.0 : 1.0);\n    inverse_spacetime_metric.get(a, a) = (a == 0 ? -1.0 : 1.0);\n  }\n\n  auto three_index_constraint =\n      make_with_value<tnsr::iaa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto char_projected_rhs_dt_v_psi =\n      make_with_value<tnsr::aa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto char_projected_rhs_dt_v_minus =\n      make_with_value<tnsr::aa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto constraint_char_zero_plus =\n      make_with_value<tnsr::a<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  auto constraint_char_zero_minus =\n      make_with_value<tnsr::a<DataVector, local_volume_dim, frame>>(get(gamma2),\n                                                                    0.0);\n  auto phi = make_with_value<tnsr::iaa<DataVector, local_volume_dim, frame>>(\n      get(gamma2), 0.0);\n  auto d_phi = make_with_value<tnsr::ijaa<DataVector, local_volume_dim, frame>>(\n      get(gamma2), 0.0);\n  auto d_pi = make_with_value<tnsr::iaa<DataVector, local_volume_dim, frame>>(\n      get(gamma2), 0.0);\n  const std::array<DataVector, 4> char_speeds{\n      DataVector(num_points, 0.0), DataVector(num_points, 0.0),\n      DataVector(num_points, 0.0), DataVector{-0.7, -1.1}};\n\n  auto dt_v_minus_no_profile =\n      make_with_value<tnsr::aa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto dt_v_minus_with_profile =\n      make_with_value<tnsr::aa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n\n  auto incoming_wave_profile =\n      std::make_unique<MathFunctions::Sinusoid<1, Frame::Inertial>>(1.4, 0.7,\n                                                                    0.2);\n  const double profile_value = incoming_wave_profile->operator()(time);\n  const double profile_derivative = incoming_wave_profile->first_deriv(time);\n  CHECK(std::abs(profile_value - profile_derivative) > 1.0e-3);\n\n  gh::BoundaryConditions::Bjorhus::\n      constraint_preserving_gauge_physical_corrections_dt_v_minus<\n          local_volume_dim, DataVector>(\n          make_not_null(&dt_v_minus_no_profile), gamma2, inertial_coords, time,\n          unit_interface_normal_one_form, unit_interface_normal_vector,\n          spacetime_unit_normal_vector, incoming_null_one_form,\n          outgoing_null_one_form, incoming_null_vector, outgoing_null_vector,\n          projection_ab, projection_Ab, projection_AB, inverse_spatial_metric,\n          extrinsic_curvature, spacetime_metric, inverse_spacetime_metric,\n          three_index_constraint, char_projected_rhs_dt_v_psi,\n          char_projected_rhs_dt_v_minus, constraint_char_zero_plus,\n          constraint_char_zero_minus, phi, d_phi, d_pi, char_speeds, nullptr);\n  gh::BoundaryConditions::Bjorhus::\n      constraint_preserving_gauge_physical_corrections_dt_v_minus<\n          local_volume_dim, DataVector>(\n          make_not_null(&dt_v_minus_with_profile), gamma2, inertial_coords,\n          time, unit_interface_normal_one_form, unit_interface_normal_vector,\n          spacetime_unit_normal_vector, incoming_null_one_form,\n          outgoing_null_one_form, incoming_null_vector, outgoing_null_vector,\n          projection_ab, projection_Ab, projection_AB, inverse_spatial_metric,\n          extrinsic_curvature, spacetime_metric, inverse_spacetime_metric,\n          three_index_constraint, char_projected_rhs_dt_v_psi,\n          char_projected_rhs_dt_v_minus, constraint_char_zero_plus,\n          constraint_char_zero_minus, phi, d_phi, d_pi, char_speeds,\n          incoming_wave_profile.get());\n\n  auto delta = dt_v_minus_with_profile;\n  for (size_t a = 0; a <= local_volume_dim; ++a) {\n    for (size_t b = a; b <= local_volume_dim; ++b) {\n      delta.get(a, b) -= dt_v_minus_no_profile.get(a, b);\n    }\n  }\n\n  auto expected_delta =\n      make_with_value<tnsr::aa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto injected_wave =\n      make_with_value<tnsr::aa<DataVector, local_volume_dim, frame>>(\n          get(gamma2), 0.0);\n  auto char_speed_3 =\n      make_with_value<Scalar<DataVector>>(get(gamma2), 0.0);\n  get(char_speed_3) = char_speeds[3];\n  injected_wave.get(1, 1) = profile_value;\n  injected_wave.get(2, 2) = profile_value;\n  injected_wave.get(3, 3) = -2.0 * profile_value;\n  tenex::evaluate<ti::a, ti::b>(\n      make_not_null(&expected_delta),\n      (projection_Ab(ti::C, ti::a) * projection_Ab(ti::D, ti::b) -\n       0.5 * projection_ab(ti::a, ti::b) * projection_AB(ti::C, ti::D)) *\n          char_speed_3() * (-injected_wave(ti::c, ti::d)));\n\n  CHECK_ITERABLE_APPROX(delta, expected_delta);\n}\n\ntemplate <size_t LocalVolumeDim>\nvoid test_incoming_wave_profile_throws_for_non_3d() {\n  static_assert(LocalVolumeDim < 3);\n  const size_t num_points = 1;\n\n  auto gamma2 =\n      make_with_value<Scalar<DataVector>>(DataVector(num_points), 0.5);\n  auto inertial_coords =\n      make_with_value<tnsr::I<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  1.0);\n  auto unit_interface_normal_one_form =\n      make_with_value<tnsr::i<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  auto unit_interface_normal_vector =\n      make_with_value<tnsr::I<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  unit_interface_normal_one_form.get(0) = 1.0;\n  unit_interface_normal_vector.get(0) = 1.0;\n\n  auto spacetime_unit_normal_vector =\n      make_with_value<tnsr::A<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  spacetime_unit_normal_vector.get(0) = 1.0;\n  auto incoming_null_one_form =\n      make_with_value<tnsr::a<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  auto outgoing_null_one_form =\n      make_with_value<tnsr::a<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  auto incoming_null_vector =\n      make_with_value<tnsr::A<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  auto outgoing_null_vector =\n      make_with_value<tnsr::A<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  auto projection_ab =\n      make_with_value<tnsr::aa<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  auto projection_Ab =\n      make_with_value<tnsr::Ab<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  auto projection_AB =\n      make_with_value<tnsr::AA<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  for (size_t a = 0; a <= LocalVolumeDim; ++a) {\n    projection_ab.get(a, a) = 1.0;\n    projection_AB.get(a, a) = 1.0;\n    projection_Ab.get(a, a) = 1.0;\n  }\n  auto inverse_spatial_metric =\n      make_with_value<tnsr::II<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  auto extrinsic_curvature =\n      make_with_value<tnsr::ii<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  auto spacetime_metric =\n      make_with_value<tnsr::aa<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  auto inverse_spacetime_metric =\n      make_with_value<tnsr::AA<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  for (size_t i = 0; i < LocalVolumeDim; ++i) {\n    inverse_spatial_metric.get(i, i) = 1.0;\n  }\n  for (size_t a = 0; a <= LocalVolumeDim; ++a) {\n    spacetime_metric.get(a, a) = (a == 0 ? -1.0 : 1.0);\n    inverse_spacetime_metric.get(a, a) = (a == 0 ? -1.0 : 1.0);\n  }\n  auto three_index_constraint =\n      make_with_value<tnsr::iaa<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                    0.0);\n  auto char_projected_rhs_dt_v_psi =\n      make_with_value<tnsr::aa<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  auto char_projected_rhs_dt_v_minus =\n      make_with_value<tnsr::aa<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n  auto constraint_char_zero_plus =\n      make_with_value<tnsr::a<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  auto constraint_char_zero_minus =\n      make_with_value<tnsr::a<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                  0.0);\n  auto phi = make_with_value<tnsr::iaa<DataVector, LocalVolumeDim, frame>>(\n      get(gamma2), 0.0);\n  auto d_phi = make_with_value<tnsr::ijaa<DataVector, LocalVolumeDim, frame>>(\n      get(gamma2), 0.0);\n  auto d_pi = make_with_value<tnsr::iaa<DataVector, LocalVolumeDim, frame>>(\n      get(gamma2), 0.0);\n  const std::array<DataVector, 4> char_speeds{\n      DataVector(num_points, 0.0), DataVector(num_points, 0.0),\n      DataVector(num_points, 0.0), DataVector(num_points, -1.0)};\n  auto bc_dt_v_minus =\n      make_with_value<tnsr::aa<DataVector, LocalVolumeDim, frame>>(get(gamma2),\n                                                                   0.0);\n\n  auto incoming_wave_profile =\n      std::make_unique<MathFunctions::Sinusoid<1, Frame::Inertial>>(1.0, 0.7,\n                                                                    0.2);\n  CHECK_THROWS_WITH(\n      (gh::BoundaryConditions::Bjorhus::\n           constraint_preserving_gauge_physical_corrections_dt_v_minus<\n               LocalVolumeDim, DataVector>(\n               make_not_null(&bc_dt_v_minus), gamma2, inertial_coords, 0.0,\n               unit_interface_normal_one_form, unit_interface_normal_vector,\n               spacetime_unit_normal_vector, incoming_null_one_form,\n               outgoing_null_one_form, incoming_null_vector,\n               outgoing_null_vector, projection_ab, projection_Ab,\n               projection_AB, inverse_spatial_metric, extrinsic_curvature,\n               spacetime_metric, inverse_spacetime_metric,\n               three_index_constraint, char_projected_rhs_dt_v_psi,\n               char_projected_rhs_dt_v_minus, constraint_char_zero_plus,\n               constraint_char_zero_minus, phi, d_phi, d_pi, char_speeds,\n               incoming_wave_profile.get())),\n      Catch::Matchers::ContainsSubstring(\n          \"IncomingWaveProfile can only be used in 3 spatial dimensions.\"));\n}\n\ntemplate <size_t VolumeDim>\nvoid test_constraint_preserving_corrections_dt_v_minus(\n    const size_t grid_size_each_dimension) {\n  pypp::check_with_random_values<1>(\n      &wrapper_func_cp_v_minus<VolumeDim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      \"constraint_preserving_corrections_dt_v_minus\", {{{-1., 1.}}},\n      DataVector(grid_size_each_dimension));\n}\n\ntemplate <size_t VolumeDim>\nvoid test_constraint_preserving_gauge_corrections_dt_v_minus(\n    const size_t grid_size_each_dimension) {\n  pypp::check_with_random_values<1>(\n      &wrapper_func_cpg_v_minus<VolumeDim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      \"constraint_preserving_gauge_corrections_dt_v_minus\", {{{-1., 1.}}},\n      DataVector(grid_size_each_dimension));\n}\n\ntemplate <size_t VolumeDim>\nvoid test_constraint_preserving_gauge_physical_corrections_dt_v_minus(\n    const size_t grid_size_each_dimension) {\n  pypp::check_with_random_values<1>(\n      &wrapper_func_cpgp_v_minus<VolumeDim>,\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.Bjorhus\",\n      \"constraint_preserving_gauge_physical_corrections_dt_v_minus\",\n      {{{-1., 1.}}}, DataVector(grid_size_each_dimension));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.BCBjorhus.VPsi\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  // Piece-wise tests with SpEC output\n  const size_t grid_size = 3;\n\n  // Python tests\n  test_constraint_preserving_corrections_dt_v_psi<1>(grid_size);\n  test_constraint_preserving_corrections_dt_v_psi<2>(grid_size);\n  test_constraint_preserving_corrections_dt_v_psi<3>(grid_size);\n\n  // Piece-wise tests with SpEC output in 3D\n  test_constraint_preserving_bjorhus_v_psi_vs_spec_3d(grid_size);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.BCBjorhus.VZero\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  // Piece-wise tests with SpEC output\n  const size_t grid_size = 3;\n\n  // Python tests\n  test_constraint_preserving_corrections_dt_v_zero<1>(grid_size);\n  test_constraint_preserving_corrections_dt_v_zero<2>(grid_size);\n  test_constraint_preserving_corrections_dt_v_zero<3>(grid_size);\n\n  // Piece-wise tests with SpEC output\n  test_constraint_preserving_bjorhus_v_zero_vs_spec_3d(grid_size);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.BCBjorhus.VMinus\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  const size_t grid_size = 3;\n\n  // Python tests\n  test_constraint_preserving_corrections_dt_v_minus<1>(grid_size);\n  test_constraint_preserving_corrections_dt_v_minus<2>(grid_size);\n  test_constraint_preserving_corrections_dt_v_minus<3>(grid_size);\n  test_constraint_preserving_gauge_corrections_dt_v_minus<1>(grid_size);\n  test_constraint_preserving_gauge_corrections_dt_v_minus<2>(grid_size);\n  test_constraint_preserving_gauge_corrections_dt_v_minus<3>(grid_size);\n  test_constraint_preserving_gauge_physical_corrections_dt_v_minus<1>(\n      grid_size);\n  test_constraint_preserving_gauge_physical_corrections_dt_v_minus<2>(\n      grid_size);\n  test_constraint_preserving_gauge_physical_corrections_dt_v_minus<3>(\n      grid_size);\n  test_incoming_wave_profile_uses_profile_value_in_3d();\n  test_incoming_wave_profile_throws_for_non_3d<1>();\n  test_incoming_wave_profile_throws_for_non_3d<2>();\n\n  // Piece-wise tests with SpEC output in 3D\n  const std::array<double, 3> lower_bound{{299., -0.5, -0.5}};\n  const std::array<double, 3> upper_bound{{300., 0.5, 0.5}};\n\n  test_constraint_preserving_gauge_physical_bjorhus_v_minus_vs_spec_3d(\n      grid_size, lower_bound, upper_bound);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\nnamespace {\n\ntemplate <size_t Dim>\nvoid test() {\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      gh::BoundaryConditions::DemandOutgoingCharSpeeds<Dim>,\n      gh::BoundaryConditions::BoundaryCondition<Dim>, gh::System<Dim>,\n      tmpl::list<gh::BoundaryCorrections::UpwindPenalty<Dim>>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.\"\n      \"DemandOutgoingCharSpeeds\",\n      tuples::TaggedTuple<helpers::Tags::PythonFunctionForErrorMessage<>>{\n          \"error\"},\n      \"DemandOutgoingCharSpeeds:\\n\", Index<Dim - 1>{Dim == 1 ? 0 : 5},\n      db::DataBox<tmpl::list<>>{},\n      tuples::TaggedTuple<helpers::Tags::Range<gh::Tags::ConstraintGamma1>>{\n          std::array{0.0, 1.0}});\n}\n}  // namespace\nSPECTRE_TEST_CASE(\n    \"Unit.GeneralizedHarmonic.BoundaryConditions.DemandOutgoingCharSpeeds\",\n    \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>, tmpl::list<>>,\n        tmpl::pair<gh::BoundaryConditions::BoundaryCondition<Dim>,\n                   tmpl::list<gh::BoundaryConditions::DirichletAnalytic<Dim>>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   gh::Solutions::all_solutions<Dim>>>;\n  };\n};\n\ntemplate <size_t Dim>\nstruct ConvertPlaneWave {\n  using unpacked_container = int;\n  using packed_container =\n      gh::Solutions::WrappedGr<gr::Solutions::GaugeWave<Dim>>;\n  using packed_type = double;\n\n  static packed_container create_container() {\n    const double amplitude = 0.2;\n    const double wavelength = 10.0;\n    return {amplitude, wavelength};\n  }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return Dim;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  register_classes_with_charm(gh::Solutions::all_solutions<Dim>{});\n  MAKE_GENERATOR(gen);\n  const auto box_analytic_soln = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticSolution<gh::Solutions::WrappedGr<\n                      gr::Solutions::GaugeWave<Dim>>>>>(\n      0.5, ConvertPlaneWave<Dim>::create_container());\n\n  helpers::test_boundary_condition_with_python<\n      gh::BoundaryConditions::DirichletAnalytic<Dim>,\n      gh::BoundaryConditions::BoundaryCondition<Dim>, gh::System<Dim>,\n      tmpl::list<gh::BoundaryCorrections::UpwindPenalty<Dim>>,\n      tmpl::list<ConvertPlaneWave<Dim>>,\n      tmpl::list<Tags::AnalyticSolution<\n          gh::Solutions::WrappedGr<gr::Solutions::GaugeWave<Dim>>>>,\n      Metavariables<Dim>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.\"\n      \"DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              gr::Tags::SpacetimeMetric<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<gh::Tags::Pi<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<gh::Tags::Phi<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<gh::Tags::ConstraintGamma1>,\n          helpers::Tags::PythonFunctionName<gh::Tags::ConstraintGamma2>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, Dim>>>{\n          \"error\", \"spacetime_metric\", \"pi\", \"phi\", \"constraint_gamma1\",\n          \"constraint_gamma2\", \"lapse\", \"shift\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    GeneralizedHarmonic(GaugeWave):\\n\"\n      \"      Amplitude: 0.2\\n\"\n      \"      Wavelength: 10.0\\n\",\n      Index<Dim - 1>{Dim == 1 ? 1 : 5}, box_analytic_soln,\n      tuples::TaggedTuple<helpers::Tags::Range<gh::Tags::ConstraintGamma1>,\n                          helpers::Tags::Range<gh::Tags::ConstraintGamma2>>{\n          std::array{0.0, 1.0}, std::array{0.0, 1.0}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.GeneralizedHarmonic.BoundaryConditions.DirichletAnalytic\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Test_DirichletMinkowski.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletMinkowski.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nstruct ConvertMinkowski {\n  using unpacked_container = int;\n  using packed_container =\n      gh::Solutions::WrappedGr<gr::Solutions::Minkowski<Dim>>;\n  using packed_type = double;\n\n  static packed_container create_container() { return {}; }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return Dim;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  MAKE_GENERATOR(gen);\n  const auto box_analytic_soln = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticSolution<gh::Solutions::WrappedGr<\n                      gr::Solutions::Minkowski<Dim>>>>>(\n      0.5, ConvertMinkowski<Dim>::create_container());\n\n  helpers::test_boundary_condition_with_python<\n      gh::BoundaryConditions::DirichletMinkowski<Dim>,\n      gh::BoundaryConditions::BoundaryCondition<Dim>, gh::System<Dim>,\n      tmpl::list<gh::BoundaryCorrections::UpwindPenalty<Dim>>,\n      tmpl::list<ConvertMinkowski<Dim>>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.GeneralizedHarmonic.BoundaryConditions.\"\n      \"DirichletMinkowski\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              gr::Tags::SpacetimeMetric<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<gh::Tags::Pi<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<gh::Tags::Phi<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<gh::Tags::ConstraintGamma1>,\n          helpers::Tags::PythonFunctionName<gh::Tags::ConstraintGamma2>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, Dim>>>{\n          \"error\", \"spacetime_metric\", \"pi\", \"phi\", \"constraint_gamma1\",\n          \"constraint_gamma2\", \"lapse\", \"shift\"},\n      \"DirichletMinkowski:\\n\", Index<Dim - 1>{Dim == 1 ? 1 : 5},\n      box_analytic_soln,\n      tuples::TaggedTuple<helpers::Tags::Range<gh::Tags::ConstraintGamma1>,\n                          helpers::Tags::Range<gh::Tags::ConstraintGamma2>>{\n          std::array{0.0, 1.0}, std::array{0.0, 1.0}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.GeneralizedHarmonic.BoundaryConditions.DirichletMinkowski\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Test_Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  helpers::test_periodic_condition<\n      domain::BoundaryConditions::Periodic<\n          gh::BoundaryConditions::BoundaryCondition<Dim>>,\n      gh::BoundaryConditions::BoundaryCondition<Dim>>(\"Periodic:\\n\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GeneralizedHarmonic.BoundaryConditions.Periodic\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Test_AveragedUpwindPenalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ExtractPoint.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/AveragedUpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nusing gh_tags =\n    tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n               gh::Tags::Pi<DataVector, Dim>, gh::Tags::Phi<DataVector, Dim>>;\ntemplate <size_t Dim>\nusing GhVars = Variables<gh_tags<Dim>>;\n\ntemplate <size_t Dim>\nstruct MeshVelocity : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nusing Args = Variables<tmpl::push_back<\n    gh_tags<Dim>, gh::Tags::ConstraintGamma1, gh::Tags::ConstraintGamma2,\n    domain::Tags::UnnormalizedFaceNormal<Dim>, MeshVelocity<Dim>>>;\n\ntemplate <size_t Dim, typename Correction>\nVariables<typename Correction::dg_package_field_tags> call_dg_package_data(\n    const Correction& correction, const Args<Dim>& args,\n    const bool include_mesh_velocity) {\n  const Scalar<DataVector> unused_lapse{};\n  const tnsr::I<DataVector, Dim> unused_shift{};\n  const tnsr::I<DataVector, Dim> unused_normal_vector{};\n  const Scalar<DataVector> unused_normal_dot_mesh_velocity{};\n\n  Variables<typename Correction::dg_package_field_tags> packaged_data{\n      args.number_of_grid_points()};\n  tmpl::as_pack<typename Correction::dg_package_field_tags>(\n      [&]<typename... DgPackageFieldTags>(\n          tmpl::type_<DgPackageFieldTags>... /*meta*/) {\n        correction.dg_package_data(\n            make_not_null(&get<DgPackageFieldTags>(packaged_data))...,\n            get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(args),\n            get<gh::Tags::Pi<DataVector, Dim>>(args),\n            get<gh::Tags::Phi<DataVector, Dim>>(args),\n            get<gh::Tags::ConstraintGamma1>(args),\n            get<gh::Tags::ConstraintGamma2>(args), unused_lapse, unused_shift,\n            get<domain::Tags::UnnormalizedFaceNormal<Dim>>(args),\n            unused_normal_vector,\n            include_mesh_velocity ? std::optional{get<MeshVelocity<Dim>>(args)}\n                                  : std::nullopt,\n            include_mesh_velocity\n                ? std::optional{unused_normal_dot_mesh_velocity}\n                : std::nullopt);\n      });\n  return packaged_data;\n}\n\ntemplate <size_t Dim, typename Correction>\nGhVars<Dim> call_dg_boundary_terms(\n    const Correction& correction,\n    const Variables<typename Correction::dg_package_field_tags>& packaged_int,\n    const Variables<typename Correction::dg_package_field_tags>& packaged_ext) {\n  GhVars<Dim> boundary_terms{packaged_int.number_of_grid_points()};\n  tmpl::as_pack<gh_tags<Dim>>(\n      [&]<typename... GhVarTags>(tmpl::type_<GhVarTags>... /*meta*/) {\n        tmpl::as_pack<typename Correction::dg_package_field_tags>(\n            [&]<typename... DgPackageFieldTags>(\n                tmpl::type_<DgPackageFieldTags>... /*meta*/) {\n              correction.dg_boundary_terms(\n                  make_not_null(&get<GhVarTags>(boundary_terms))...,\n                  get<DgPackageFieldTags>(packaged_int)...,\n                  get<DgPackageFieldTags>(packaged_ext)...,\n                  dg::Formulation::StrongInertial);\n            });\n      });\n  return boundary_terms;\n}\n\n// Note: The exterior normal is negated before calling the correction\ntemplate <size_t Dim>\nGhVars<Dim> boundary_correction(const Args<Dim>& interior, Args<Dim> exterior,\n                                const bool include_mesh_velocity = true) {\n  const gh::BoundaryCorrections::AveragedUpwindPenalty<Dim> correction{};\n\n  for (auto& component :\n       get<domain::Tags::UnnormalizedFaceNormal<Dim>>(exterior)) {\n    component *= -1.0;\n  }\n\n  const auto packaged_int =\n      call_dg_package_data<Dim>(correction, interior, include_mesh_velocity);\n  const auto packaged_ext =\n      call_dg_package_data<Dim>(correction, exterior, include_mesh_velocity);\n  return call_dg_boundary_terms<Dim>(correction, packaged_int, packaged_ext);\n}\n\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen, const size_t num_pts) {\n  register_classes_with_charm<\n      gh::BoundaryCorrections::AveragedUpwindPenalty<Dim>>();\n\n  CHECK(gh::BoundaryCorrections::AveragedUpwindPenalty<Dim>{} ==\n        gh::BoundaryCorrections::AveragedUpwindPenalty<Dim>{});\n  CHECK_FALSE(gh::BoundaryCorrections::AveragedUpwindPenalty<Dim>{} !=\n              gh::BoundaryCorrections::AveragedUpwindPenalty<Dim>{});\n\n  std::uniform_real_distribution dist(-0.1, 0.1);\n  std::uniform_real_distribution velocity_dist(-2.0, 2.0);\n  auto average_fields =\n      make_with_random_values<Args<Dim>>(gen, make_not_null(&dist), num_pts);\n  // Use a larger range for the mesh velocity so we get all sign\n  // combinations for the speeds.\n  fill_with_random_values(\n      make_not_null(&get<MeshVelocity<Dim>>(average_fields)), gen,\n      make_not_null(&velocity_dist));\n\n  // Make the metric reasonable\n  {\n    auto& metric =\n        get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(average_fields);\n    get<0, 0>(metric) -= 1.0;\n    for (size_t i = 1; i <= Dim; ++i) {\n      metric.get(i, i) += 1.0;\n    }\n  }\n\n  const auto delta_fields =\n      make_with_random_values<Args<Dim>>(gen, make_not_null(&dist), num_pts);\n  const auto correction = boundary_correction<Dim>(\n      average_fields - delta_fields, average_fields + delta_fields);\n\n  // Check that the function at one point matches a single point\n  // calculation\n  {\n    const auto single_point_average =\n        extract_point(average_fields, num_pts / 2);\n    const auto single_point_delta = extract_point(delta_fields, num_pts / 2);\n    const auto single_point_correction =\n        boundary_correction<Dim>(single_point_average - single_point_delta,\n                                 single_point_average + single_point_delta);\n    CHECK_VARIABLES_APPROX(single_point_correction,\n                           extract_point(correction, num_pts / 2));\n  }\n\n  // Check that the solution is linear in the jump if the average is\n  // kept constant\n  {\n    const auto correction_minus4 =\n        boundary_correction<Dim>(average_fields + 4.0 * delta_fields,\n                                 average_fields - 4.0 * delta_fields);\n    CHECK_VARIABLES_APPROX(correction_minus4, -4.0 * correction);\n  }\n\n  // Check that zero mesh velocity gives the same result as a fixed mesh\n  {\n    auto zero_mesh_average = average_fields;\n    auto zero_mesh_delta = delta_fields;\n    for (auto& component : get<MeshVelocity<Dim>>(zero_mesh_average)) {\n      component = 0.0;\n    }\n    for (auto& component : get<MeshVelocity<Dim>>(zero_mesh_delta)) {\n      component = 0.0;\n    }\n\n    const auto zero_mesh_correction =\n        boundary_correction<Dim>(zero_mesh_average - zero_mesh_delta,\n                                 zero_mesh_average + zero_mesh_delta);\n    const auto fixed_mesh_correction = boundary_correction<Dim>(\n        average_fields - delta_fields, average_fields + delta_fields, false);\n    CHECK(zero_mesh_correction == fixed_mesh_correction);\n  }\n\n  // Actually perform the characteristic decomposition and compare to\n  // the result.\n  {\n    const auto& spacetime_metric =\n        get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(average_fields);\n    const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n    const auto inverse_spatial_metric =\n        determinant_and_inverse(spatial_metric).second;\n    const auto shift = gr::shift(spacetime_metric, inverse_spatial_metric);\n    const auto lapse = gr::lapse(shift, spacetime_metric);\n    // The evolved<->characteristic functions require a unit normal,\n    // but the boundary correction should do the normalization itself\n    // (since averaging the two sides doesn't preserve normalization).\n    auto unit_normal_one_form =\n        get<domain::Tags::UnnormalizedFaceNormal<Dim>>(average_fields);\n    const auto normal_magnitude =\n        magnitude(unit_normal_one_form, inverse_spatial_metric);\n    for (auto& component : unit_normal_one_form) {\n      component /= get(normal_magnitude);\n    }\n\n    auto speeds = gh::characteristic_speeds(\n        get<gh::Tags::ConstraintGamma1>(average_fields), lapse, shift,\n        unit_normal_one_form,\n        std::optional{get<MeshVelocity<Dim>>(average_fields)});\n\n    using CharacteristicFields = Variables<\n        tmpl::list<gh::Tags::VSpacetimeMetric<DataVector, Dim, Frame::Inertial>,\n                   gh::Tags::VZero<DataVector, Dim, Frame::Inertial>,\n                   gh::Tags::VPlus<DataVector, Dim, Frame::Inertial>,\n                   gh::Tags::VMinus<DataVector, Dim, Frame::Inertial>>>;\n\n    // Factor of 2.0 comes from (avg + delta) - (avg - delta) = 2 delta\n    CharacteristicFields characteristic_correction =\n        2.0 * gh::characteristic_fields(\n                  get<gh::Tags::ConstraintGamma2>(average_fields),\n                  inverse_spatial_metric,\n                  get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(delta_fields),\n                  get<gh::Tags::Pi<DataVector, Dim>>(delta_fields),\n                  get<gh::Tags::Phi<DataVector, Dim>>(delta_fields),\n                  unit_normal_one_form);\n    for (size_t p = 0; p < num_pts; ++p) {\n      for (auto& component :\n           get<gh::Tags::VSpacetimeMetric<DataVector, Dim, Frame::Inertial>>(\n               characteristic_correction)) {\n        component[p] *= speeds[0][p] < 0.0 ? speeds[0][p] : 0.0;\n      }\n      for (auto& component :\n           get<gh::Tags::VZero<DataVector, Dim, Frame::Inertial>>(\n               characteristic_correction)) {\n        component[p] *= speeds[1][p] < 0.0 ? speeds[1][p] : 0.0;\n      }\n      for (auto& component :\n           get<gh::Tags::VPlus<DataVector, Dim, Frame::Inertial>>(\n               characteristic_correction)) {\n        component[p] *= speeds[2][p] < 0.0 ? speeds[2][p] : 0.0;\n      }\n      for (auto& component :\n           get<gh::Tags::VMinus<DataVector, Dim, Frame::Inertial>>(\n               characteristic_correction)) {\n        component[p] *= speeds[3][p] < 0.0 ? speeds[3][p] : 0.0;\n      }\n    }\n\n    const auto expected_correction =\n        gh::evolved_fields_from_characteristic_fields(\n            get<gh::Tags::ConstraintGamma2>(average_fields),\n            get<gh::Tags::VSpacetimeMetric<DataVector, Dim, Frame::Inertial>>(\n                characteristic_correction),\n            get<gh::Tags::VZero<DataVector, Dim, Frame::Inertial>>(\n                characteristic_correction),\n            get<gh::Tags::VPlus<DataVector, Dim, Frame::Inertial>>(\n                characteristic_correction),\n            get<gh::Tags::VMinus<DataVector, Dim, Frame::Inertial>>(\n                characteristic_correction),\n            unit_normal_one_form);\n\n    CHECK_VARIABLES_APPROX(correction, expected_correction);\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.GeneralizedHarmonic.BoundaryCorrections.AveragedUpwindPenalty\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n\n  test<1>(make_not_null(&gen), 1);\n  test<2>(make_not_null(&gen), 5);\n  test<3>(make_not_null(&gen), 5);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Test_UpwindPenalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen, const size_t num_pts) {\n  PUPable_reg(gh::BoundaryCorrections::UpwindPenalty<Dim>);\n\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<\n      gh::System<Dim>>(gen, gh::BoundaryCorrections::UpwindPenalty<Dim>{},\n                       Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                                     Spectral::Quadrature::Gauss},\n                       {}, {});\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      gh::System<Dim>>(gen, \"UpwindPenalty\", \"dg_package_data\",\n                       \"dg_boundary_terms\",\n                       gh::BoundaryCorrections::UpwindPenalty<Dim>{},\n                       Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                                     Spectral::Quadrature::Gauss},\n                       {}, {});\n\n  const auto upwind_penalty = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      gh::BoundaryCorrections::UpwindPenalty<Dim>>(\"UpwindPenalty:\");\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      gh::System<Dim>>(\n      gen, \"UpwindPenalty\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const gh::BoundaryCorrections::UpwindPenalty<Dim>&>(\n          *upwind_penalty),\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n\n  CHECK_FALSE(gh::BoundaryCorrections::UpwindPenalty<Dim>{} !=\n              gh::BoundaryCorrections::UpwindPenalty<Dim>{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GeneralizedHarmonic.BoundaryCorrections.UpwindPenalty\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  test<1>(make_not_null(&gen), 1);\n  test<2>(make_not_null(&gen), 5);\n  test<3>(make_not_null(&gen), 5);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    spacetime_metric,\n    pi,\n    phi,\n    constraint_gamma1,\n    constraint_gamma2,\n    lapse,\n    shift,\n    normal_covector,\n    normal_vector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n):\n    char_speeds = np.zeros([4])\n    char_speeds[0] = -(1.0 + constraint_gamma1) * np.dot(shift, normal_covector)\n    char_speeds[1] = -np.dot(shift, normal_covector)\n    char_speeds[2] = lapse - np.dot(shift, normal_covector)\n    char_speeds[3] = -lapse - np.dot(shift, normal_covector)\n    if not (normal_dot_mesh_velocity is None):\n        for i in range(4):\n            char_speeds[i] -= normal_dot_mesh_velocity\n        char_speeds[0] -= normal_dot_mesh_velocity * constraint_gamma1\n\n    char_speed_v_zero = (\n        phi - np.einsum(\"j,i,jab->iab\", normal_vector, normal_covector, phi)\n    ) * char_speeds[1]\n    char_speed_v_plus = (\n        pi\n        + np.einsum(\"iab,i->ab\", phi, normal_vector)\n        - constraint_gamma2 * spacetime_metric\n    ) * char_speeds[2]\n\n    char_speed_v_minus = (\n        pi\n        - np.einsum(\"iab,i->ab\", phi, normal_vector)\n        - constraint_gamma2 * spacetime_metric\n    ) * char_speeds[3]\n\n    return (\n        np.asarray(char_speeds[0] * spacetime_metric),\n        char_speed_v_zero,\n        char_speed_v_plus,\n        char_speed_v_minus,\n        np.einsum(\"i,ab->iab\", normal_covector, char_speed_v_plus),\n        np.einsum(\"i,ab->iab\", normal_covector, char_speed_v_minus),\n        np.asarray(char_speeds[0] * spacetime_metric * constraint_gamma2),\n        char_speeds,\n    )\n\n\ndef dg_boundary_terms(\n    int_char_speed_v_spacetime_metric,\n    int_char_speed_v_zero,\n    int_char_speed_v_plus,\n    int_char_speed_v_minus,\n    int_char_speed_v_plus_times_normal,\n    int_char_speed_v_minus_times_normal,\n    int_char_speed_gamma2_v_spacetime_metric,\n    int_char_speeds,\n    ext_char_speed_v_spacetime_metric,\n    ext_char_speed_v_zero,\n    ext_char_speed_v_plus,\n    ext_char_speed_v_minus,\n    ext_char_speed_v_plus_times_normal,\n    ext_char_speed_v_minus_times_normal,\n    ext_char_speed_gamma2_v_spacetime_metric,\n    ext_char_speeds,\n    use_strong_form,\n):\n    result_spacetime_metric = int_char_speed_v_spacetime_metric * 0.0\n    if ext_char_speeds[0] > 0.0:\n        result_spacetime_metric -= ext_char_speed_v_spacetime_metric\n    if int_char_speeds[0] < 0.0:\n        result_spacetime_metric -= int_char_speed_v_spacetime_metric\n\n    result_pi = int_char_speed_v_spacetime_metric * 0.0\n    # Add v^+ terms\n    if ext_char_speeds[2] > 0.0:\n        result_pi -= 0.5 * ext_char_speed_v_plus\n    if int_char_speeds[2] < 0.0:\n        result_pi -= 0.5 * int_char_speed_v_plus\n\n    # Add v^- terms\n    if ext_char_speeds[3] > 0.0:\n        result_pi -= 0.5 * ext_char_speed_v_minus\n    if int_char_speeds[3] < 0.0:\n        result_pi -= 0.5 * int_char_speed_v_minus\n\n    # Add v^\\Psi terms\n    if ext_char_speeds[0] > 0.0:\n        result_pi -= ext_char_speed_gamma2_v_spacetime_metric\n    if int_char_speeds[0] < 0.0:\n        result_pi -= int_char_speed_gamma2_v_spacetime_metric\n\n    result_phi = int_char_speed_v_zero * 0.0\n\n    # Add v^+ terms\n    if ext_char_speeds[2] > 0.0:\n        result_phi -= 0.5 * ext_char_speed_v_plus_times_normal\n    if int_char_speeds[2] < 0.0:\n        result_phi -= 0.5 * int_char_speed_v_plus_times_normal\n\n    # Add v^- terms\n    if ext_char_speeds[3] > 0.0:\n        result_phi += 0.5 * ext_char_speed_v_minus_times_normal\n    if int_char_speeds[3] < 0.0:\n        result_phi += 0.5 * int_char_speed_v_minus_times_normal\n\n    # Add v^0 terms\n    if ext_char_speeds[1] > 0.0:\n        result_phi -= ext_char_speed_v_zero\n    if int_char_speeds[1] < 0.0:\n        result_phi -= int_char_speed_v_zero\n\n    return (\n        result_spacetime_metric,\n        result_pi,\n        result_phi,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(GaugeSourceFunctions)\n\nset(LIBRARY \"Test_GeneralizedHarmonic\")\n\nset(LIBRARY_SOURCES\n  Actions/Test_SetInitialData.cpp\n  BoundaryConditions/Test_Bjorhus.cpp\n  BoundaryConditions/Test_BjorhusImpl.cpp\n  BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryConditions/Test_DirichletMinkowski.cpp\n  BoundaryConditions/Test_Periodic.cpp\n  BoundaryCorrections/Test_AveragedUpwindPenalty.cpp\n  BoundaryCorrections/Test_UpwindPenalty.cpp\n  Test_ApplyTensorYlmFilter.cpp\n  Test_Characteristics.cpp\n  Test_Constraints.cpp\n  Test_DuDt.cpp\n  Test_DuDtTempTags.cpp\n  Test_Fluxes.cpp\n  Test_BbhCompletionCriteria.cpp\n  Test_BbhCompletionSingleton.cpp\n  Test_BbhCheckpointAndExitIfCompletePhaseChange.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataBoxTestHelpers\n  DataStructures\n  DataStructuresHelpers\n  DiscontinuousGalerkin\n  Domain\n  DomainStructure\n  FunctionsOfTime\n  GeneralRelativity\n  GeneralRelativityHelpers\n  GeneralRelativitySolutions\n  GeneralizedHarmonic\n  Importers\n  LinearOperators\n  MathFunctions\n  Options\n  Spectral\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Characteristics.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\n# Test functions for characteristic speeds\ndef char_speed_upsi(gamma1, lapse, shift, unit_normal):\n    return -(1.0 + gamma1) * np.dot(shift, unit_normal)\n\n\ndef char_speed_upsi_moving_mesh(\n    gamma1, lapse, shift, unit_normal, mesh_velocity\n):\n    return -(1.0 + gamma1) * np.dot(shift, unit_normal) - (\n        1.0 + gamma1\n    ) * np.dot(mesh_velocity, unit_normal)\n\n\ndef char_speed_uzero(gamma1, lapse, shift, unit_normal):\n    return -np.dot(shift, unit_normal)\n\n\ndef char_speed_uzero_moving_mesh(\n    gamma1, lapse, shift, unit_normal, mesh_velocity\n):\n    return -np.dot(shift, unit_normal) - np.dot(mesh_velocity, unit_normal)\n\n\ndef char_speed_uplus(gamma1, lapse, shift, unit_normal):\n    return -np.dot(shift, unit_normal) + lapse\n\n\ndef char_speed_uplus_moving_mesh(\n    gamma1, lapse, shift, unit_normal, mesh_velocity\n):\n    return (\n        -np.dot(shift, unit_normal) + lapse - np.dot(mesh_velocity, unit_normal)\n    )\n\n\ndef char_speed_uminus(gamma1, lapse, shift, unit_normal):\n    return -np.dot(shift, unit_normal) - lapse\n\n\ndef char_speed_uminus_moving_mesh(\n    gamma1, lapse, shift, unit_normal, mesh_velocity\n):\n    return (\n        -np.dot(shift, unit_normal) - lapse - np.dot(mesh_velocity, unit_normal)\n    )\n\n\n# End test functions for characteristic speeds\n\n\n# Test functions for characteristic fields\ndef char_field_upsi(\n    gamma2, inverse_spatial_metric, spacetime_metric, pi, phi, normal_one_form\n):\n    return spacetime_metric\n\n\ndef char_field_uzero(\n    gamma2, inverse_spatial_metric, spacetime_metric, pi, phi, normal_one_form\n):\n    normal_vector = np.einsum(\"ij,j\", inverse_spatial_metric, normal_one_form)\n    projection_tensor = np.identity(len(normal_vector)) - np.einsum(\n        \"i,j\", normal_one_form, normal_vector\n    )\n    return np.einsum(\"ij,jab->iab\", projection_tensor, phi)\n\n\ndef char_field_uplus(\n    gamma2, inverse_spatial_metric, spacetime_metric, pi, phi, normal_one_form\n):\n    normal_vector = np.einsum(\"ij,j\", inverse_spatial_metric, normal_one_form)\n    phi_dot_normal = np.einsum(\"i,iab->ab\", normal_vector, phi)\n    return pi + 1 * phi_dot_normal - (gamma2 * spacetime_metric)\n\n\ndef char_field_uminus(\n    gamma2, inverse_spatial_metric, spacetime_metric, pi, phi, normal_one_form\n):\n    normal_vector = np.einsum(\"ij,j\", inverse_spatial_metric, normal_one_form)\n    phi_dot_normal = np.einsum(\"i,iab->ab\", normal_vector, phi)\n    return pi - phi_dot_normal - (gamma2 * spacetime_metric)\n\n\n# Test functions for evolved fields\n\n\ndef evol_field_psi(gamma2, upsi, uzero, uplus, uminus, normal_one_form):\n    return upsi\n\n\ndef evol_field_pi(gamma2, upsi, uzero, uplus, uminus, normal_one_form):\n    return 0.5 * (uplus + uminus) + gamma2 * upsi\n\n\ndef evol_field_phi(gamma2, upsi, uzero, uplus, uminus, normal_one_form):\n    udiff = 0.5 * (uplus - uminus)\n    return np.einsum(\"i,ab->iab\", normal_one_form, udiff) + uzero\n\n\n# End test functions for characteristic fields\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Constraints.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Test functions for gauge constraint\n\n\ndef gauge_constraint(\n    gauge_function,\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    inverse_spatial_metric,\n    inverse_spacetime_metric,\n    pi,\n    phi,\n):\n    # Sums only go over spatial indices of second index of phi\n    phi_ija = phi[:, 1:, :]\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    constraint = (\n        gauge_function\n        + np.einsum(\"ij,ija->a\", inverse_spatial_metric, phi_ija)\n        + np.einsum(\"b,ba->a\", spacetime_normal_vector, pi)\n        - 0.5\n        * np.insert(\n            np.einsum(\"bc,ibc->i\", inverse_spacetime_metric, phi),\n            0,\n            np.zeros(phi[0][0][0].shape),\n        )\n        - 0.5\n        * np.einsum(\n            \"a,i,bc,ibc->a\",\n            spacetime_normal_one_form,\n            spacetime_normal_vector_I,\n            inverse_spacetime_metric,\n            phi,\n        )\n        - 0.5\n        * np.einsum(\n            \"a,bc,bc->a\",\n            spacetime_normal_one_form,\n            inverse_spacetime_metric,\n            pi,\n        )\n    )\n    return constraint\n\n\n# End test functions for gauge constraint\n\n# Test functions for two-index constraint\n\n\ndef two_index_constraint_term_1_of_11(inverse_spatial_metric, d_phi):\n    d_phi_jika = d_phi[:, :, 1:, :]\n    return np.einsum(\"jk,jika->ia\", inverse_spatial_metric, d_phi_jika)\n\n\ndef two_index_constraint_term_2_of_11(\n    spacetime_normal_vector,\n    spacetime_normal_one_form,\n    inverse_spacetime_metric,\n    d_phi,\n):\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    d_phi_aicd = np.pad(d_phi, ((1, 0), (0, 0), (0, 0), (0, 0)), \"constant\")\n    term = np.einsum(\"cd,aicd->ia\", inverse_spacetime_metric, d_phi_aicd)\n    term += np.einsum(\n        \"j,a,cd,jicd->ia\",\n        spacetime_normal_vector_I,\n        spacetime_normal_one_form,\n        inverse_spacetime_metric,\n        d_phi,\n    )\n    return -0.5 * term\n\n\ndef two_index_constraint_term_3_of_11(spacetime_normal_vector, d_pi):\n    return np.einsum(\"b,iba->ia\", spacetime_normal_vector, d_pi)\n\n\ndef two_index_constraint_term_4_of_11(\n    spacetime_normal_one_form, inverse_spacetime_metric, d_pi\n):\n    return -0.5 * np.einsum(\n        \"cd,icd,a->ia\",\n        inverse_spacetime_metric,\n        d_pi,\n        spacetime_normal_one_form,\n    )\n\n\ndef two_index_constraint_term_5_of_11(d_gauge_function):\n    return d_gauge_function[1:, :]\n\n\ndef two_index_constraint_term_6_of_11(\n    spacetime_normal_vector,\n    spacetime_normal_one_form,\n    phi,\n    inverse_spacetime_metric,\n):\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    phi_acd = np.pad(phi, ((1, 0), (0, 0), (0, 0)), \"constant\")\n    term = np.einsum(\n        \"acd,ief,ce,df->ia\",\n        phi_acd,\n        phi,\n        inverse_spacetime_metric,\n        inverse_spacetime_metric,\n    )\n    term += np.einsum(\n        \"j,a,jcd,ief,ce,df->ia\",\n        spacetime_normal_vector_I,\n        spacetime_normal_one_form,\n        phi,\n        phi,\n        inverse_spacetime_metric,\n        inverse_spacetime_metric,\n    )\n    return 0.5 * term\n\n\ndef two_index_constraint_term_7_of_11(\n    inverse_spatial_metric,\n    phi,\n    inverse_spacetime_metric,\n    spacetime_normal_vector,\n    spacetime_normal_one_form,\n):\n    phi_ike = phi[:, 1:, :]\n    return 0.5 * np.einsum(\n        \"jk,jcd,ike,cd,e,a->ia\",\n        inverse_spatial_metric,\n        phi,\n        phi_ike,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n        spacetime_normal_one_form,\n    )\n\n\ndef two_index_constraint_term_8_of_11(inverse_spatial_metric, phi):\n    phi_jma = phi[:, 1:, :]\n    phi_ikn = phi[:, 1:, 1:]\n    return -1.0 * np.einsum(\n        \"jk,mn,jma,ikn->ia\",\n        inverse_spatial_metric,\n        inverse_spatial_metric,\n        phi_jma,\n        phi_ikn,\n    )\n\n\ndef two_index_constraint_term_9_of_11(\n    phi,\n    pi,\n    spacetime_normal_one_form,\n    inverse_spacetime_metric,\n    spacetime_normal_vector,\n):\n    term = 0.5 * np.einsum(\n        \"icd,be,a,cb,de->ia\",\n        phi,\n        pi,\n        spacetime_normal_one_form,\n        inverse_spacetime_metric,\n        inverse_spacetime_metric,\n    )\n    term += 0.25 * np.einsum(\n        \"icd,be,a,be,c,d->ia\",\n        phi,\n        pi,\n        spacetime_normal_one_form,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n    )\n    return term\n\n\ndef two_index_constraint_term_10_of_11(\n    phi, pi, spacetime_normal_vector, inverse_spacetime_metric\n):\n    term = -1.0 * np.einsum(\n        \"icd,ba,c,bd->ia\",\n        phi,\n        pi,\n        spacetime_normal_vector,\n        inverse_spacetime_metric,\n    )\n    term -= 0.5 * np.einsum(\n        \"icd,ba,c,b,d->ia\",\n        phi,\n        pi,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n    )\n    return term\n\n\ndef two_index_constraint_term_11_of_11(\n    gamma2,\n    spacetime_normal_one_form,\n    inverse_spacetime_metric,\n    spacetime_normal_vector,\n    three_index_constraint,\n):\n    term = (\n        0.5\n        * gamma2\n        * np.einsum(\n            \"a,cd,icd->ia\",\n            spacetime_normal_one_form,\n            inverse_spacetime_metric,\n            three_index_constraint,\n        )\n    )\n    term -= gamma2 * np.einsum(\n        \"d,iad->ia\", spacetime_normal_vector, three_index_constraint\n    )\n    return term\n\n\ndef two_index_constraint(\n    d_gauge_function,\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    inverse_spatial_metric,\n    inverse_spacetime_metric,\n    pi,\n    phi,\n    d_pi,\n    d_phi,\n    gamma2,\n    three_index_constraint,\n):\n    constraint = two_index_constraint_term_1_of_11(\n        inverse_spatial_metric, d_phi\n    )\n    constraint += two_index_constraint_term_2_of_11(\n        spacetime_normal_vector,\n        spacetime_normal_one_form,\n        inverse_spacetime_metric,\n        d_phi,\n    )\n    constraint += two_index_constraint_term_3_of_11(\n        spacetime_normal_vector, d_pi\n    )\n    constraint += two_index_constraint_term_4_of_11(\n        spacetime_normal_one_form, inverse_spacetime_metric, d_pi\n    )\n    constraint += two_index_constraint_term_5_of_11(d_gauge_function)\n    constraint += two_index_constraint_term_6_of_11(\n        spacetime_normal_vector,\n        spacetime_normal_one_form,\n        phi,\n        inverse_spacetime_metric,\n    )\n    constraint += two_index_constraint_term_7_of_11(\n        inverse_spatial_metric,\n        phi,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n        spacetime_normal_one_form,\n    )\n    constraint += two_index_constraint_term_8_of_11(inverse_spatial_metric, phi)\n    constraint += two_index_constraint_term_9_of_11(\n        phi,\n        pi,\n        spacetime_normal_one_form,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n    constraint += two_index_constraint_term_10_of_11(\n        phi, pi, spacetime_normal_vector, inverse_spacetime_metric\n    )\n    constraint += two_index_constraint_term_11_of_11(\n        gamma2,\n        spacetime_normal_one_form,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n        three_index_constraint,\n    )\n    return constraint\n\n\n# End test functions for two-index constraint\n\n# Test functions for four-index constraint\n\n\ndef four_index_constraint(d_phi):\n    e_ijk = np.zeros((3, 3, 3))\n    e_ijk[0, 1, 2] = e_ijk[1, 2, 0] = e_ijk[2, 0, 1] = 1.0\n    e_ijk[0, 2, 1] = e_ijk[2, 1, 0] = e_ijk[1, 0, 2] = -1.0\n    constraint = np.einsum(\"ijk,jkab->iab\", e_ijk, d_phi)\n    return constraint\n\n\n# End test functions for four-index constraint\n\n\n# Test functions for F constraint\n\n\ndef f_constraint_term_1_of_25(\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    inverse_spacetime_metric,\n    d_pi,\n):\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    d_pi_abc = np.pad(d_pi, ((1, 0), (0, 0), (0, 0)), \"constant\")\n    term = np.einsum(\"bc,abc\", inverse_spacetime_metric, d_pi_abc)\n    term += np.einsum(\n        \"a,i,bc,ibc\",\n        spacetime_normal_one_form,\n        spacetime_normal_vector_I,\n        inverse_spacetime_metric,\n        d_pi,\n    )\n    return 0.5 * term\n\n\ndef f_constraint_term_2_of_25(inverse_spatial_metric, d_pi):\n    d_pi_ija = d_pi[:, 1:, :]\n    return -1.0 * np.einsum(\"ij,ija\", inverse_spatial_metric, d_pi_ija)\n\n\ndef f_constraint_term_3_of_25(\n    inverse_spatial_metric, spacetime_normal_vector, d_phi\n):\n    return -1.0 * np.einsum(\n        \"ij,b,ijba\", inverse_spatial_metric, spacetime_normal_vector, d_phi\n    )\n\n\ndef f_constraint_term_4_of_25(\n    spacetime_normal_one_form,\n    inverse_spacetime_metric,\n    inverse_spatial_metric,\n    d_phi,\n):\n    return 0.5 * np.einsum(\n        \"a,bc,ij,ijbc\",\n        spacetime_normal_one_form,\n        inverse_spacetime_metric,\n        inverse_spatial_metric,\n        d_phi,\n    )\n\n\ndef f_constraint_term_5_of_25(\n    spacetime_normal_one_form, inverse_spatial_metric, d_gauge_function\n):\n    d_gauge_function_ij = d_gauge_function[1:, 1:]\n    return np.einsum(\n        \"a,ij,ij\",\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        d_gauge_function_ij,\n    )\n\n\ndef f_constraint_term_6_of_25(\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    phi,\n    inverse_spatial_metric,\n    inverse_spacetime_metric,\n):\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    phi_ijb = phi[:, 1:, :]\n    phi_ajb = np.pad(phi, ((1, 0), (0, 0), (0, 0)), \"constant\")[:, 1:, :]\n    term = np.einsum(\n        \"ajb,jk,kcd,bd,c\",\n        phi_ajb,\n        inverse_spatial_metric,\n        phi,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n    return term + np.einsum(\n        \"a,i,ijb,jk,kcd,bd,c\",\n        spacetime_normal_one_form,\n        spacetime_normal_vector_I,\n        phi_ijb,\n        inverse_spatial_metric,\n        phi,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n\n\ndef f_constraint_term_7_of_25(\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    phi,\n    inverse_spatial_metric,\n    inverse_spacetime_metric,\n):\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    phi_ijb = phi[:, 1:, :]\n    phi_ajb = np.pad(phi, ((1, 0), (0, 0), (0, 0)), \"constant\")[:, 1:, :]\n    term = np.einsum(\n        \"ajb,jk,kcd,cd,b\",\n        phi_ajb,\n        inverse_spatial_metric,\n        phi,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n    return -0.5 * (\n        term\n        + np.einsum(\n            \"a,i,ijb,jk,kcd,cd,b\",\n            spacetime_normal_one_form,\n            spacetime_normal_vector_I,\n            phi_ijb,\n            inverse_spatial_metric,\n            phi,\n            inverse_spacetime_metric,\n            spacetime_normal_vector,\n        )\n    )\n\n\ndef f_constraint_term_8_of_25(\n    spacetime_normal_one_form, spacetime_normal_vector, d_gauge_function\n):\n    d_gauge_function[0, :] = 0\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    term = -1.0 * np.einsum(\"b,ab\", spacetime_normal_vector, d_gauge_function)\n    return term - np.einsum(\n        \"a,i,b,ib\",\n        spacetime_normal_one_form,\n        spacetime_normal_vector_I,\n        spacetime_normal_vector,\n        d_gauge_function[1:, :],\n    )\n\n\ndef f_constraint_term_9_of_25(\n    inverse_spatial_metric,\n    phi,\n    inverse_spacetime_metric,\n    spacetime_normal_vector,\n):\n    return np.einsum(\n        \"ij,icd,jba,bc,d\",\n        inverse_spatial_metric,\n        phi,\n        phi,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n\n\ndef f_constraint_term_10_of_25(\n    spacetime_normal_one_form,\n    inverse_spatial_metric,\n    phi,\n    inverse_spacetime_metric,\n):\n    phi_imc = phi[:, 1:, :]\n    return -0.5 * np.einsum(\n        \"a,ij,mn,imc,njd,cd\",\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        inverse_spatial_metric,\n        phi_imc,\n        phi_imc,\n        inverse_spacetime_metric,\n    )\n\n\ndef f_constraint_term_11_of_25(\n    spacetime_normal_one_form,\n    inverse_spatial_metric,\n    phi,\n    inverse_spacetime_metric,\n):\n    return -0.25 * np.einsum(\n        \"a,ij,icd,jbe,cb,de\",\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        phi,\n        phi,\n        inverse_spacetime_metric,\n        inverse_spacetime_metric,\n    )\n\n\ndef f_constraint_term_12_of_25(\n    spacetime_normal_one_form, pi, inverse_spacetime_metric\n):\n    return 0.25 * np.einsum(\n        \"a,cd,be,cb,de\",\n        spacetime_normal_one_form,\n        pi,\n        pi,\n        inverse_spacetime_metric,\n        inverse_spacetime_metric,\n    )\n\n\ndef f_constraint_term_13_of_25(inverse_spatial_metric, gauge_function, pi):\n    gauge_function_i = gauge_function[1:]\n    pi_ja = pi[1:, :]\n    return -1.0 * np.einsum(\n        \"ij,i,ja\", inverse_spatial_metric, gauge_function_i, pi_ja\n    )\n\n\ndef f_constraint_term_14_of_25(\n    spacetime_normal_vector, inverse_spatial_metric, pi\n):\n    pi_bi = pi[:, 1:]\n    pi_ja = pi[1:, :]\n    return -1.0 * np.einsum(\n        \"b,ij,bi,ja\",\n        spacetime_normal_vector,\n        inverse_spatial_metric,\n        pi_bi,\n        pi_ja,\n    )\n\n\ndef f_constraint_term_15_of_25(\n    inverse_spacetime_metric,\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    phi,\n    pi,\n):\n    phi_acd = np.pad(phi, ((1, 0), (0, 0), (0, 0)), \"constant\")\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    term = np.einsum(\n        \"acd,c,d,be,be\",\n        phi_acd,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n        pi,\n        inverse_spacetime_metric,\n    )\n    term += np.einsum(\n        \"a,i,icd,c,d,be,be\",\n        spacetime_normal_one_form,\n        spacetime_normal_vector_I,\n        phi,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n        pi,\n        inverse_spacetime_metric,\n    )\n    return -0.25 * term\n\n\ndef f_constraint_term_16_of_25(\n    spacetime_normal_one_form,\n    pi,\n    inverse_spacetime_metric,\n    spacetime_normal_vector,\n):\n    return 0.5 * np.einsum(\n        \"a,cd,be,ce,d,b\",\n        spacetime_normal_one_form,\n        pi,\n        pi,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n    )\n\n\ndef f_constraint_term_17_of_25(\n    inverse_spacetime_metric,\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    phi,\n    pi,\n):\n    phi_acd = np.pad(phi, ((1, 0), (0, 0), (0, 0)), \"constant\")\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    term = np.einsum(\n        \"acd,be,c,b,de\",\n        phi_acd,\n        pi,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n        inverse_spacetime_metric,\n    )\n    return term + np.einsum(\n        \"a,i,icd,be,c,b,de\",\n        spacetime_normal_one_form,\n        spacetime_normal_vector_I,\n        phi,\n        pi,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n        inverse_spacetime_metric,\n    )\n\n\ndef f_constraint_term_18_of_25(\n    inverse_spatial_metric, phi, spacetime_normal_vector, pi\n):\n    pi_je = pi[1:, :]\n    return -1.0 * np.einsum(\n        \"ij,iba,b,je,e\",\n        inverse_spatial_metric,\n        phi,\n        spacetime_normal_vector,\n        pi_je,\n        spacetime_normal_vector,\n    )\n\n\ndef f_constraint_term_19_of_25(\n    inverse_spatial_metric, phi, spacetime_normal_vector, pi\n):\n    pi_ja = pi[1:, :]\n    return -0.5 * np.einsum(\n        \"ij,icd,c,d,ja\",\n        inverse_spatial_metric,\n        phi,\n        spacetime_normal_vector,\n        spacetime_normal_vector,\n        pi_ja,\n    )\n\n\ndef f_constraint_term_20_of_25(\n    inverse_spatial_metric, gauge_function, phi, spacetime_normal_vector\n):\n    gauge_function_i = gauge_function[1:]\n    return -1.0 * np.einsum(\n        \"ij,i,jab,b\",\n        inverse_spatial_metric,\n        gauge_function_i,\n        phi,\n        spacetime_normal_vector,\n    )\n\n\ndef f_constraint_term_21_of_25(\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    phi,\n    gauge_function,\n    inverse_spacetime_metric,\n):\n    phi_acd = np.pad(phi, ((1, 0), (0, 0), (0, 0)), \"constant\")\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    term = np.einsum(\n        \"acd,b,bc,d\",\n        phi_acd,\n        gauge_function,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n    return term + np.einsum(\n        \"a,i,icd,b,bc,d\",\n        spacetime_normal_one_form,\n        spacetime_normal_vector_I,\n        phi,\n        gauge_function,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n\n\ndef f_constraint_term_22_of_25(\n    gamma2,\n    inverse_spacetime_metric,\n    spacetime_normal_vector,\n    three_index_constraint,\n    spacetime_normal_one_form,\n):\n    inverse_spacetime_metric_ID = inverse_spacetime_metric[1:, :]\n    spacetime_normal_vector_I = spacetime_normal_vector[1:]\n    three_index_constraint_acd = np.pad(\n        three_index_constraint, ((1, 0), (0, 0), (0, 0)), \"constant\"\n    )\n    term = np.einsum(\n        \"id,ida\", inverse_spacetime_metric_ID, three_index_constraint\n    )\n    term += np.einsum(\n        \"i,d,ida\",\n        spacetime_normal_vector_I,\n        spacetime_normal_vector,\n        three_index_constraint,\n    )\n    term -= 0.5 * np.einsum(\n        \"cd,acd\", inverse_spacetime_metric, three_index_constraint_acd\n    )\n    term -= 0.5 * np.einsum(\n        \"a,i,cd,icd\",\n        spacetime_normal_one_form,\n        spacetime_normal_vector_I,\n        inverse_spacetime_metric,\n        three_index_constraint,\n    )\n    return gamma2 * term\n\n\ndef f_constraint_term_23_of_25(\n    spacetime_normal_one_form,\n    pi,\n    inverse_spacetime_metric,\n    gauge_function,\n    spacetime_normal_vector,\n):\n    return 0.5 * np.einsum(\n        \"a,cd,cd,b,b\",\n        spacetime_normal_one_form,\n        pi,\n        inverse_spacetime_metric,\n        gauge_function,\n        spacetime_normal_vector,\n    )\n\n\ndef f_constraint_term_24_of_25(\n    spacetime_normal_one_form,\n    inverse_spatial_metric,\n    phi,\n    gauge_function,\n    inverse_spacetime_metric,\n):\n    phi_ijc = phi[:, 1:, :]\n    return -1.0 * np.einsum(\n        \"a,ij,ijc,d,cd\",\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        phi_ijc,\n        gauge_function,\n        inverse_spacetime_metric,\n    )\n\n\ndef f_constraint_term_25_of_25(\n    spacetime_normal_one_form,\n    inverse_spatial_metric,\n    gauge_function,\n    phi,\n    inverse_spacetime_metric,\n):\n    gauge_function_i = gauge_function[1:]\n    return 0.5 * np.einsum(\n        \"a,ij,i,jcd,cd\",\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        gauge_function_i,\n        phi,\n        inverse_spacetime_metric,\n    )\n\n\ndef f_constraint_stress_energy_term(\n    inverse_spacetime_metric,\n    spacetime_normal_vector,\n    spacetime_normal_one_form,\n    trace_reversed_stress_energy,\n):\n    return (\n        -16.0\n        * np.pi\n        * (\n            np.einsum(\n                \"b, ab\", spacetime_normal_vector, trace_reversed_stress_energy\n            )\n            - 0.5\n            * spacetime_normal_one_form\n            * np.einsum(\n                \"ab, ab\", inverse_spacetime_metric, trace_reversed_stress_energy\n            )\n        )\n    )\n\n\ndef f_constraint_with_stress_energy(\n    gauge_function,\n    d_gauge_function,\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    inverse_spatial_metric,\n    inverse_spacetime_metric,\n    pi,\n    phi,\n    d_pi,\n    d_phi,\n    gamma2,\n    three_index_constraint,\n    trace_reversed_stress_energy,\n):\n    constraint = f_constraint(\n        gauge_function,\n        d_gauge_function,\n        spacetime_normal_one_form,\n        spacetime_normal_vector,\n        inverse_spatial_metric,\n        inverse_spacetime_metric,\n        pi,\n        phi,\n        d_pi,\n        d_phi,\n        gamma2,\n        three_index_constraint,\n    )\n    constraint += f_constraint_stress_energy_term(\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n        spacetime_normal_one_form,\n        trace_reversed_stress_energy,\n    )\n    return constraint\n\n\ndef f_constraint(\n    gauge_function,\n    d_gauge_function,\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    inverse_spatial_metric,\n    inverse_spacetime_metric,\n    pi,\n    phi,\n    d_pi,\n    d_phi,\n    gamma2,\n    three_index_constraint,\n):\n    constraint = f_constraint_term_1_of_25(\n        spacetime_normal_one_form,\n        spacetime_normal_vector,\n        inverse_spacetime_metric,\n        d_pi,\n    )\n    constraint += f_constraint_term_2_of_25(inverse_spatial_metric, d_pi)\n    constraint += f_constraint_term_3_of_25(\n        inverse_spatial_metric, spacetime_normal_vector, d_phi\n    )\n    constraint += f_constraint_term_4_of_25(\n        spacetime_normal_one_form,\n        inverse_spacetime_metric,\n        inverse_spatial_metric,\n        d_phi,\n    )\n    constraint += f_constraint_term_5_of_25(\n        spacetime_normal_one_form, inverse_spatial_metric, d_gauge_function\n    )\n    constraint += f_constraint_term_6_of_25(\n        spacetime_normal_one_form,\n        spacetime_normal_vector,\n        phi,\n        inverse_spatial_metric,\n        inverse_spacetime_metric,\n    )\n    constraint += f_constraint_term_7_of_25(\n        spacetime_normal_one_form,\n        spacetime_normal_vector,\n        phi,\n        inverse_spatial_metric,\n        inverse_spacetime_metric,\n    )\n    constraint += f_constraint_term_8_of_25(\n        spacetime_normal_one_form, spacetime_normal_vector, d_gauge_function\n    )\n    constraint += f_constraint_term_9_of_25(\n        inverse_spatial_metric,\n        phi,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n    constraint += f_constraint_term_10_of_25(\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        phi,\n        inverse_spacetime_metric,\n    )\n    constraint += f_constraint_term_11_of_25(\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        phi,\n        inverse_spacetime_metric,\n    )\n    constraint += f_constraint_term_12_of_25(\n        spacetime_normal_one_form, pi, inverse_spacetime_metric\n    )\n    constraint += f_constraint_term_13_of_25(\n        inverse_spatial_metric, gauge_function, pi\n    )\n    constraint += f_constraint_term_14_of_25(\n        spacetime_normal_vector, inverse_spatial_metric, pi\n    )\n    constraint += f_constraint_term_15_of_25(\n        inverse_spacetime_metric,\n        spacetime_normal_one_form,\n        spacetime_normal_vector,\n        phi,\n        pi,\n    )\n    constraint += f_constraint_term_16_of_25(\n        spacetime_normal_one_form,\n        pi,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n    )\n    constraint += f_constraint_term_17_of_25(\n        inverse_spacetime_metric,\n        spacetime_normal_one_form,\n        spacetime_normal_vector,\n        phi,\n        pi,\n    )\n    constraint += f_constraint_term_18_of_25(\n        inverse_spatial_metric, phi, spacetime_normal_vector, pi\n    )\n    constraint += f_constraint_term_19_of_25(\n        inverse_spatial_metric, phi, spacetime_normal_vector, pi\n    )\n    constraint += f_constraint_term_20_of_25(\n        inverse_spatial_metric, gauge_function, phi, spacetime_normal_vector\n    )\n    constraint += f_constraint_term_21_of_25(\n        spacetime_normal_one_form,\n        spacetime_normal_vector,\n        phi,\n        gauge_function,\n        inverse_spacetime_metric,\n    )\n    constraint += f_constraint_term_22_of_25(\n        gamma2,\n        inverse_spacetime_metric,\n        spacetime_normal_vector,\n        three_index_constraint,\n        spacetime_normal_one_form,\n    )\n    constraint += f_constraint_term_23_of_25(\n        spacetime_normal_one_form,\n        pi,\n        inverse_spacetime_metric,\n        gauge_function,\n        spacetime_normal_vector,\n    )\n    constraint += f_constraint_term_24_of_25(\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        phi,\n        gauge_function,\n        inverse_spacetime_metric,\n    )\n    constraint += f_constraint_term_25_of_25(\n        spacetime_normal_one_form,\n        inverse_spatial_metric,\n        gauge_function,\n        phi,\n        inverse_spacetime_metric,\n    )\n    return constraint\n\n\n# End test functions for F constraint\n\n# Begin test functions for constraint energy\n\n\ndef constraint_energy(\n    gauge_constraint,\n    f_constraint,\n    two_index_constraint,\n    three_index_constraint,\n    four_index_constraint,\n    inverse_spatial_metric,\n    spatial_metric_determinant,\n    gauge_constraint_multiplier,\n    two_index_constraint_multiplier,\n    three_index_constraint_multiplier,\n    four_index_constraint_multiplier,\n):\n    energy = 0.0\n    energy += gauge_constraint_multiplier * np.einsum(\n        \"a,a\", gauge_constraint, gauge_constraint\n    )\n    energy += two_index_constraint_multiplier * np.einsum(\n        \"a,a\", f_constraint, f_constraint\n    )\n    energy += two_index_constraint_multiplier * np.einsum(\n        \"ia,ja,ij\",\n        two_index_constraint,\n        two_index_constraint,\n        inverse_spatial_metric,\n    )\n    energy += three_index_constraint_multiplier * np.einsum(\n        \"iab,jab,ij\",\n        three_index_constraint,\n        three_index_constraint,\n        inverse_spatial_metric,\n    )\n    energy += (\n        2.0\n        * four_index_constraint_multiplier\n        * spatial_metric_determinant\n        * np.einsum(\n            \"iab,jab,ij\",\n            four_index_constraint,\n            four_index_constraint,\n            inverse_spatial_metric,\n        )\n    )\n    return energy\n\n\n# End test functions for constraint energy\n\n# Begin test function for normalized constraint energy\n\n\ndef constraint_energy_normalization(\n    d_spacetime_metric,\n    d_pi,\n    d_phi,\n    inverse_spatial_metric,\n    sqrt_spatial_metric_determinant,\n    dimensional_constant,\n):\n    energy_norm = (\n        dimensional_constant**2\n        * np.einsum(\"iac,jac\", d_spacetime_metric, d_spacetime_metric)\n        + np.einsum(\"iac,jac\", d_pi, d_pi)\n        + np.einsum(\"kl,ikac,jlac\", inverse_spatial_metric, d_phi, d_phi)\n    )\n    energy_norm = (\n        np.einsum(\"ij,ij\", inverse_spatial_metric, energy_norm)\n        * sqrt_spatial_metric_determinant\n    )\n    return energy_norm\n\n\n# End test functions for normalized constraint energy\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GeneralizedHarmonicGaugeSourceFunctions\")\n\nset(LIBRARY_SOURCES\n  Test_AnalyticChristoffel.cpp\n  Test_DampedHarmonic.cpp\n  Test_DampedWaveHelpers.cpp\n  Test_HalfPiPhiTwoNormals.cpp\n  Test_Harmonic.cpp\n  Test_SetPiAndPhiFromConstraints.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainCreators\n  FunctionsOfTime\n  GeneralRelativity\n  GeneralizedHarmonic\n  LinearOperators\n  Options\n  Parallel\n  Spectral\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nimport PointwiseFunctions.GeneralRelativity.ComputeSpacetimeQuantities as ComputeSpacetimeQuantities\nfrom PointwiseFunctions.GeneralRelativity.ComputeGhQuantities import (\n    deriv_lapse,\n    deriv_shift,\n    deriv_spatial_metric,\n    dt_lapse,\n    dt_shift,\n    dt_spatial_metric,\n    spacetime_deriv_detg,\n)\nfrom PointwiseFunctions.GeneralRelativity.ComputeSpacetimeQuantities import (\n    derivatives_of_spacetime_metric,\n    inverse_spacetime_metric,\n    spacetime_normal_vector,\n)\n\nfrom .DampedWaveHelpers import (\n    log_fac,\n    spacetime_deriv_spatial_weight_function,\n    spatial_weight_function,\n)\n\n\ndef roll_on_function(time, t_start, sigma_t):\n    if time < t_start:\n        return 0.0\n    return 1.0 - np.exp(-(((time - t_start) / sigma_t) ** 4))\n\n\ndef time_deriv_roll_on_function(time, t_start, sigma_t):\n    if time < t_start:\n        return 0.0\n    tnrm = (time - t_start) / sigma_t\n    return np.exp(-(tnrm**4)) * 4 * tnrm**3 / sigma_t\n\n\ndef spatial_metric_from_spacetime_metric(spacetime_metric):\n    return spacetime_metric[1:, 1:]\n\n\ndef damped_harmonic_gauge_source_function_rollon(\n    gauge_h_init,\n    dgauge_h_init,\n    spacetime_metric,\n    pi,\n    phi,\n    time,\n    coords,\n    amp_coef_L1,\n    amp_coef_L2,\n    amp_coef_S,\n    rollon_start_time,\n    rollon_width,\n    sigma_r,\n):\n    spatial_dim = spacetime_metric.shape[0] - 1\n    spacetime_metric[0, 0] -= 1.0\n    for i in range(1, spatial_dim + 1):\n        spacetime_metric[i, i] += 1.0\n    spatial_metric = spacetime_metric[1:, 1:]\n    det_spatial_metric = np.linalg.det(spatial_metric)\n    inverse_spatial_metric = np.linalg.inv(spatial_metric)\n    shift = ComputeSpacetimeQuantities.shift(\n        spacetime_metric, inverse_spatial_metric\n    )\n    lapse = ComputeSpacetimeQuantities.lapse(shift, spacetime_metric)\n    sqrt_det_spatial_metric = np.sqrt(det_spatial_metric)\n\n    unit_normal_one_form = np.empty([spatial_dim + 1])\n    unit_normal_one_form[0] = -lapse\n    unit_normal_one_form[1:] = 0.0\n\n    # We cannot pass int through pypp right now, so we hard-code exponents.\n    exp_L1 = 4\n    exp_L2 = 4\n    exp_S = 4\n\n    log_sqrtg_over_lapse = log_fac(lapse, sqrt_det_spatial_metric, 0.5)\n    log_one_over_lapse = log_fac(lapse, sqrt_det_spatial_metric, 0.0)\n\n    if gauge_h_init is None:\n        R = 1.0\n    else:\n        R = roll_on_function(time, rollon_start_time, rollon_width)\n    W = spatial_weight_function(coords, sigma_r)\n\n    muL1 = amp_coef_L1 * R * W * log_sqrtg_over_lapse**exp_L1\n    muL2 = amp_coef_L2 * R * W * log_one_over_lapse**exp_L2\n    muS = amp_coef_S * R * W * log_sqrtg_over_lapse**exp_S\n    h_pre1 = muL1 * log_sqrtg_over_lapse + muL2 * log_one_over_lapse\n    h_pre2 = muS / lapse\n    if gauge_h_init is None:\n        init_gauge_term = 0.0\n    else:\n        init_gauge_term = (1.0 - R) * gauge_h_init\n\n    return (\n        init_gauge_term\n        + h_pre1 * unit_normal_one_form\n        - h_pre2 * np.einsum(\"ai,i->a\", spacetime_metric[:, 1:], shift)\n    )\n\n\ndef spacetime_deriv_damped_harmonic_gauge_source_function_rollon(\n    gauge_h_init,\n    dgauge_h_init,\n    spacetime_metric,\n    pi,\n    phi,\n    time,\n    coords,\n    amp_coef_L1,\n    amp_coef_L2,\n    amp_coef_S,\n    rollon_start_time,\n    rollon_width,\n    sigma_r,\n):\n    spatial_dim = spacetime_metric.shape[0] - 1\n    spacetime_metric[0, 0] -= 1.0\n    for i in range(1, spatial_dim + 1):\n        spacetime_metric[i, i] += 1.0\n    spatial_metric = spacetime_metric[1:, 1:]\n    det_spatial_metric = np.linalg.det(spatial_metric)\n    inverse_spatial_metric = np.linalg.inv(spatial_metric)\n    shift = ComputeSpacetimeQuantities.shift(\n        spacetime_metric, inverse_spatial_metric\n    )\n    lapse = ComputeSpacetimeQuantities.lapse(shift, spacetime_metric)\n    sqrt_det_spatial_metric = np.sqrt(det_spatial_metric)\n\n    unit_normal_one_form = np.empty([spatial_dim + 1])\n    unit_normal_one_form[0] = -lapse\n    unit_normal_one_form[1:] = 0.0\n\n    spacetime_unit_normal = spacetime_normal_vector(lapse, shift)\n    spatial_metric = spatial_metric_from_spacetime_metric(spacetime_metric)\n    det_spatial_metric = sqrt_det_spatial_metric**2\n    d3_spatial_metric = deriv_spatial_metric(phi)\n    inv_spacetime_metric = inverse_spacetime_metric(\n        lapse, shift, inverse_spatial_metric\n    )\n    d0_spatial_metric = dt_spatial_metric(lapse, shift, phi, pi)\n\n    log_sqrtg_over_lapse = np.log(sqrt_det_spatial_metric / lapse)\n    log_sqrtg_over_lapse_pow3 = log_sqrtg_over_lapse**3\n    log_sqrtg_over_lapse_pow4 = log_sqrtg_over_lapse * log_sqrtg_over_lapse_pow3\n    log_sqrtg_over_lapse_pow5 = log_sqrtg_over_lapse * log_sqrtg_over_lapse_pow4\n    one_over_lapse = 1.0 / lapse\n    log_one_over_lapse = np.log(one_over_lapse)\n    log_one_over_lapse_pow4 = log_one_over_lapse**4\n    log_one_over_lapse_pow5 = log_one_over_lapse * log_one_over_lapse_pow4\n\n    if gauge_h_init is None:\n        R = 1.0\n    else:\n        R = roll_on_function(time, rollon_start_time, rollon_width)\n    W = spatial_weight_function(coords, sigma_r)\n\n    muL1 = R * W * log_sqrtg_over_lapse_pow4\n    muS_over_N = muL1 / lapse\n\n    mu1 = muL1 * log_sqrtg_over_lapse\n    mu2 = R * W * log_one_over_lapse_pow5\n\n    d4_W = spacetime_deriv_spatial_weight_function(coords, sigma_r)\n    if gauge_h_init is None:\n        d0_R = 0.0\n    else:\n        d0_R = time_deriv_roll_on_function(\n            time, rollon_start_time, rollon_width\n        )\n    d4_RW = R * d4_W\n    d4_RW[0] += W * d0_R\n\n    d4_g = spacetime_deriv_detg(\n        sqrt_det_spatial_metric, inverse_spatial_metric, d0_spatial_metric, phi\n    )\n\n    d0_N = dt_lapse(lapse, shift, spacetime_unit_normal, phi, pi)\n    d3_N = deriv_lapse(lapse, spacetime_unit_normal, phi)\n    d4_N = np.zeros(1 + spatial_dim)\n    d4_N[0] = d0_N\n    d4_N[1:] = d3_N\n\n    d0_shift = dt_shift(\n        lapse, shift, inverse_spatial_metric, spacetime_unit_normal, phi, pi\n    )\n    d3_shift = deriv_shift(\n        lapse, inv_spacetime_metric, spacetime_unit_normal, phi\n    )\n    d4_shift = np.einsum(\n        \"a,b->ab\", np.zeros(spatial_dim + 1), np.zeros(spatial_dim + 1)\n    )\n    d4_shift[0, 1:] = d0_shift\n    d4_shift[1:, 1:] = d3_shift\n\n    prefac0 = 2.0 * R * W * log_sqrtg_over_lapse_pow3\n    prefac1 = 2.5 * R * W * log_sqrtg_over_lapse_pow4\n    prefac2 = -5.0 * R * W * log_one_over_lapse_pow4 * one_over_lapse\n    d4_muL1 = log_sqrtg_over_lapse_pow4 * d4_RW + prefac0 * (\n        d4_g / det_spatial_metric - 2.0 * one_over_lapse * d4_N\n    )\n    d4_mu1 = log_sqrtg_over_lapse_pow5 * d4_RW + prefac1 * (\n        d4_g / det_spatial_metric - 2.0 * one_over_lapse * d4_N\n    )\n    d4_mu2 = log_one_over_lapse_pow5 * d4_RW + prefac2 * d4_N\n\n    d4_normal_one_form = np.einsum(\n        \"a,b->ab\", np.zeros(spatial_dim + 1), np.zeros(spatial_dim + 1)\n    )\n    d4_normal_one_form[:, 0] = -d4_N\n\n    d4_muS_over_N = one_over_lapse * d4_muL1 - muL1 * one_over_lapse**2 * d4_N\n\n    d4_psi = derivatives_of_spacetime_metric(\n        lapse,\n        d0_N,\n        d3_N,\n        shift,\n        d0_shift,\n        d3_shift,\n        spatial_metric,\n        d0_spatial_metric,\n        d3_spatial_metric,\n    )\n\n    if gauge_h_init is None:\n        dT1 = 0.0\n    else:\n        dT1 = (1.0 - R) * dgauge_h_init\n        dT1[0, :] -= d0_R * gauge_h_init\n\n    dT2 = (\n        amp_coef_L1 * mu1 + amp_coef_L2 * mu2\n    ) * d4_normal_one_form + np.einsum(\n        \"a,b->ab\",\n        amp_coef_L1 * d4_mu1 + amp_coef_L2 * d4_mu2,\n        unit_normal_one_form,\n    )\n\n    dT3 = np.einsum(\n        \"a,b->ab\", np.zeros(spatial_dim + 1), np.zeros(spatial_dim + 1)\n    )\n    dT3 -= np.einsum(\n        \"a,b->ab\",\n        d4_muS_over_N,\n        np.einsum(\"bi,i->b\", spacetime_metric[:, 1:], shift),\n    )\n    dT3 -= muS_over_N * np.einsum(\"abi,i->ab\", d4_psi[:, :, 1:], shift)\n    dT3 -= muS_over_N * np.einsum(\n        \"ai,bi->ab\", d4_shift[:, 1:], spacetime_metric[:, 1:]\n    )\n    return dT1 + dT2 + amp_coef_S * dT3\n\n\ndef damped_harmonic_gauge_source_function(\n    spacetime_metric,\n    pi,\n    phi,\n    coords,\n    amp_coef_L1,\n    amp_coef_L2,\n    amp_coef_S,\n    sigma_r,\n):\n    return damped_harmonic_gauge_source_function_rollon(\n        None,\n        None,\n        spacetime_metric,\n        pi,\n        phi,\n        None,\n        coords,\n        amp_coef_L1,\n        amp_coef_L2,\n        amp_coef_S,\n        None,\n        None,\n        sigma_r,\n    )\n\n\ndef spacetime_deriv_damped_harmonic_gauge_source_function(\n    spacetime_metric,\n    pi,\n    phi,\n    coords,\n    amp_coef_L1,\n    amp_coef_L2,\n    amp_coef_S,\n    sigma_r,\n):\n    return spacetime_deriv_damped_harmonic_gauge_source_function_rollon(\n        None,\n        None,\n        spacetime_metric,\n        pi,\n        phi,\n        None,\n        coords,\n        amp_coef_L1,\n        amp_coef_L2,\n        amp_coef_S,\n        None,\n        None,\n        sigma_r,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedWaveHelpers.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom PointwiseFunctions.GeneralRelativity.ComputeGhQuantities import (\n    deriv_lapse,\n    dt_lapse,\n    spacetime_deriv_detg,\n)\n\n\ndef spatial_weight_function(coords, r_max):\n    r2 = np.sum([coords[i] ** 2 for i in range(len(coords))])\n    return np.exp(-r2 / r_max / r_max)\n\n\ndef spacetime_deriv_spatial_weight_function(coords, r_max, W=None):\n    if W is None:\n        W = spatial_weight_function(coords, r_max)\n    DW = np.zeros(len(coords) + 1)\n    DW[1:] = -2.0 * W / r_max / r_max * coords\n    return DW\n\n\ndef log_fac(lapse, sqrt_det_spatial_metric, exponent):\n    # if exponent == 0, the first term automatically vanishes\n    return exponent * np.log(sqrt_det_spatial_metric**2) - np.log(lapse)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/HalfPiPhiTwoNormals.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef half_pi_two_normals(normal_vector, pi, phi):\n    return 0.5 * np.einsum(\"a,b,ab->\", normal_vector, normal_vector, pi)\n\n\ndef half_phi_two_normals(normal_vector, pi, phi):\n    return 0.5 * np.einsum(\"a,b,iab->i\", normal_vector, normal_vector, phi)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Test_AnalyticChristoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/RegisterDerived.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nauto make_coord_map() {\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  if constexpr (Dim == 1) {\n    Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n    return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n        affine_map);\n  } else if constexpr (Dim == 2) {\n    Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n    Affine2D product_map{affine_map, affine_map};\n    return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n        product_map);\n  } else {\n    Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n    Affine3D product_map{affine_map, affine_map, affine_map};\n    return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n        product_map);\n  }\n}\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<gh::gauges::GaugeCondition,\n                   tmpl::list<gh::gauges::AnalyticChristoffel>>,\n        tmpl::pair<\n            evolution::initial_data::InitialData,\n            tmpl::list<\n                gh::Solutions::WrappedGr<gr::Solutions::KerrSchild>,\n                gh::Solutions::WrappedGr<gr::Solutions::GaugeWave<Dim>>>>>;\n  };\n};\n\ntemplate <size_t Dim>\nvoid test_gauge_wave(const Mesh<Dim>& mesh) {\n  const auto gauge_condition = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<gh::gauges::GaugeCondition>,\n                                 Metavariables<Dim>>(\n          \"AnalyticChristoffel:\\n\"\n          \"  AnalyticPrescription:\\n\"\n          \"    GeneralizedHarmonic(GaugeWave):\\n\"\n          \"      Amplitude: 0.0012\\n\"\n          \"      Wavelength: 1.4\\n\")\n          ->get_clone());\n  CHECK_FALSE(gauge_condition->is_harmonic());\n\n  const size_t num_points = mesh.number_of_grid_points();\n\n  const double time = 1.2;\n  const auto coord_map = make_coord_map<Dim>();\n  const auto logical_coords = logical_coordinates(mesh);\n  const tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords =\n      coord_map(logical_coords);\n  const auto inverse_jacobian = coord_map.inv_jacobian(logical_coords);\n\n  tnsr::a<DataVector, Dim, Frame::Inertial> gauge_h(num_points);\n  tnsr::ab<DataVector, Dim, Frame::Inertial> d4_gauge_h(num_points);\n  // Used dispatch with defaulted arguments that we don't need for Analytic\n  // gauge.\n  gh::gauges::dispatch<gh::Solutions::all_solutions<Dim>>(\n      make_not_null(&gauge_h), make_not_null(&d4_gauge_h), {}, {}, {}, {}, {},\n      {}, {}, {}, {}, mesh, time, inertial_coords, inverse_jacobian,\n      *gauge_condition);\n\n  CHECK_ITERABLE_APPROX(\n      gauge_h, (tnsr::a<DataVector, Dim, Frame::Inertial>(num_points, 0.0)));\n  CHECK_ITERABLE_APPROX(d4_gauge_h, (tnsr::ab<DataVector, Dim, Frame::Inertial>(\n                                        num_points, 0.0)));\n}\n\nvoid test_ks(const Mesh<3>& mesh) {\n  const auto gauge_condition = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<gh::gauges::GaugeCondition>,\n                                 Metavariables<3>>(\n          \"AnalyticChristoffel:\\n\"\n          \"  AnalyticPrescription:\\n\"\n          \"    GeneralizedHarmonic(KerrSchild):\\n\"\n          \"      Mass: 1.2\\n\"\n          \"      Spin: [0.1, 0.2, 0.3]\\n\"\n          \"      Center: [-0.1, -0.2, -0.4]\\n\"\n          \"      Velocity: [0.0, 0.0, 0.0]\\n\")\n          ->get_clone());\n  CHECK_FALSE(gauge_condition->is_harmonic());\n\n  const size_t num_points = mesh.number_of_grid_points();\n\n  const double time = 1.2;\n  const auto coord_map = make_coord_map<3>();\n  const auto logical_coords = logical_coordinates(mesh);\n  const tnsr::I<DataVector, 3, Frame::Inertial> inertial_coords =\n      coord_map(logical_coords);\n  const auto inverse_jacobian = coord_map.inv_jacobian(logical_coords);\n\n  tnsr::a<DataVector, 3, Frame::Inertial> gauge_h(num_points);\n  tnsr::ab<DataVector, 3, Frame::Inertial> d4_gauge_h(num_points);\n  // Used dispatch with defaulted arguments that we don't need for Analytic\n  // gauge.\n  gh::gauges::dispatch<gh::Solutions::all_solutions<3>>(\n      make_not_null(&gauge_h), make_not_null(&d4_gauge_h), {}, {}, {}, {}, {},\n      {}, {}, {}, {}, mesh, time, inertial_coords, inverse_jacobian,\n      *gauge_condition);\n\n  const gh::Solutions::WrappedGr<gr::Solutions::KerrSchild> kerr_schild{\n      1.2, {0.1, 0.2, 0.3}, {-0.1, -0.2, -0.4}};\n  const auto [pi, phi, spacetime_metric] = kerr_schild.variables(\n      inertial_coords, time,\n      tmpl::list<gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                 gr::Tags::SpacetimeMetric<DataVector, 3>>{});\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto shift = gr::shift(spacetime_metric, inverse_spatial_metric);\n  const auto lapse = gr::lapse(shift, spacetime_metric);\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, 3, Frame::Inertial>(lapse);\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  const auto inverse_spacetime_metric =\n      determinant_and_inverse(spacetime_metric).second;\n  auto expected_gauge_h = gh::trace_christoffel(\n      spacetime_normal_one_form, spacetime_normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi);\n  for (auto& t : expected_gauge_h) {\n    t *= -1.0;\n  }\n\n  CHECK_ITERABLE_APPROX(gauge_h, expected_gauge_h);\n  // Compute numerical spatial derivative\n  tnsr::ab<DataVector, 3, Frame::Inertial> expected_d4_gauge_h{num_points};\n  tnsr::ia<DataVector, 3, Frame::Inertial> expected_di_gauge_h{};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t a = 0; a < 3 + 1; ++a) {\n      expected_di_gauge_h.get(i, a).set_data_ref(\n          make_not_null(&expected_d4_gauge_h.get(i + 1, a)));\n    }\n  }\n  partial_derivative(make_not_null(&expected_di_gauge_h), expected_gauge_h,\n                     mesh, inverse_jacobian);\n  // Set time derivative to zero. We are assuming a static solution.\n  for (size_t a = 0; a < 4; ++a) {\n    expected_d4_gauge_h.get(0, a) = 0.0;\n  }\n  CHECK_ITERABLE_APPROX(d4_gauge_h, expected_d4_gauge_h);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GeneralizedHarmonic.Gauge.AnalyticChristoffel\",\n    \"[Unit][Evolution]\") {\n  gh::gauges::register_derived_with_charm();\n  for (const auto& basis_and_quadrature :\n       {std::pair{Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto},\n        std::pair{Spectral::Basis::FiniteDifference,\n                  Spectral::Quadrature::CellCentered}}) {\n    test_gauge_wave<1>(\n        {5, basis_and_quadrature.first, basis_and_quadrature.second});\n    test_gauge_wave<2>(\n        {5, basis_and_quadrature.first, basis_and_quadrature.second});\n    test_gauge_wave<3>(\n        {5, basis_and_quadrature.first, basis_and_quadrature.second});\n    test_ks({5, basis_and_quadrature.first, basis_and_quadrature.second});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Test_DampedHarmonic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedWaveHelpers.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/HalfPiPhiTwoNormals.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/RegisterDerived.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// The return-by-value implementations of spatial_weight_function and\n// spacetime_deriv_of_spatial_weight_function are intentionally only available\n// in the test because while convenient the additional allocations are bad for\n// performance. By not having them available in the production code we avoid\n// possible accidental usage.\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nScalar<DataType> spatial_weight_function(\n    const tnsr::I<DataType, SpatialDim, Frame>& coords, const double sigma_r) {\n  Scalar<DataType> spatial_weight{};\n  spatial_weight_function(make_not_null(&spatial_weight), coords, sigma_r);\n  return spatial_weight;\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::a<DataType, SpatialDim, Frame> spacetime_deriv_of_spatial_weight_function(\n    const tnsr::I<DataType, SpatialDim, Frame>& coords, const double sigma_r) {\n  tnsr::a<DataType, SpatialDim, Frame> d4_weight{};\n  spacetime_deriv_of_spatial_weight_function(\n      make_not_null(&d4_weight), coords, sigma_r,\n      spatial_weight_function(coords, sigma_r));\n  return d4_weight;\n}\n\ntemplate <size_t SpatialDim, typename Frame, typename DataType>\nvoid test_rollon_function(const DataType& used_for_size) {\n  INFO(\"Test rollon function\");\n  // roll_on_function\n  pypp::check_with_random_values<1>(\n      &::gh::gauges::DampedHarmonicGauge_detail::roll_on_function,\n      \"Evolution.Systems.GeneralizedHarmonic.GaugeSourceFunctions.\"\n      \"DampedHarmonic\",\n      \"roll_on_function\", {{{std::numeric_limits<double>::denorm_min(), 1.}}},\n      used_for_size);\n  // time_deriv_of_roll_on_function\n  pypp::check_with_random_values<1>(\n      &::gh::gauges::DampedHarmonicGauge_detail::time_deriv_of_roll_on_function,\n      \"Evolution.Systems.GeneralizedHarmonic.GaugeSourceFunctions.\"\n      \"DampedHarmonic\",\n      \"time_deriv_roll_on_function\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size);\n}\n\n//  Tests of the damped harmonic gauge source function and its spacetime\n//  derivative. We need a wrapper because pypp does not currently support\n//  integer types.\ntemplate <size_t SpatialDim, typename Frame>\nvoid wrap_damped_harmonic_rollon(\n    const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*> gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame>*> d4_gauge_h,\n    const tnsr::a<DataVector, SpatialDim, Frame>& gauge_h_init,\n    const tnsr::ab<DataVector, SpatialDim, Frame>& dgauge_h_init,\n    tnsr::aa<DataVector, SpatialDim, Frame> spacetime_metric,\n    const tnsr::aa<DataVector, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataVector, SpatialDim, Frame>& phi, double time,\n    const tnsr::I<DataVector, SpatialDim, Frame>& coords,\n    const double amp_coef_L1, const double amp_coef_L2, const double amp_coef_S,\n    const double rollon_start_time, const double rollon_width,\n    const double sigma_r) {\n  get<0, 0>(spacetime_metric) -= 1.0;\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    spacetime_metric.get(i + 1, i + 1) += 1.0;\n  }\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  const auto [det_spatial_metric, inverse_spatial_metric] =\n      determinant_and_inverse(spatial_metric);\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      DataVector{sqrt(get(det_spatial_metric))}};\n  const auto shift = gr::shift(spacetime_metric, inverse_spatial_metric);\n  const auto lapse = gr::lapse(shift, spacetime_metric);\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  tnsr::abb<DataVector, SpatialDim, Frame> d4_spacetime_metric{};\n  gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&d4_spacetime_metric), lapse, shift, pi, phi);\n  Scalar<DataVector> half_pi_two_normals{get(lapse).size(), 0.0};\n  tnsr::i<DataVector, SpatialDim, Frame> half_phi_two_normals{get(lapse).size(),\n                                                              0.0};\n  gh::gauges::half_pi_and_phi_two_normals(make_not_null(&half_pi_two_normals),\n                                          make_not_null(&half_phi_two_normals),\n                                          spacetime_normal_vector, pi, phi);\n\n  gh::gauges::damped_harmonic_rollon(\n      gauge_h, d4_gauge_h, gauge_h_init, dgauge_h_init, lapse, shift,\n      sqrt_det_spatial_metric, inverse_spatial_metric, d4_spacetime_metric,\n      half_pi_two_normals, half_phi_two_normals, spacetime_metric, phi, time,\n      coords, amp_coef_L1, amp_coef_L2, amp_coef_S, 4, 4, 4, rollon_start_time,\n      rollon_width, sigma_r);\n}\n\ntemplate <size_t SpatialDim, typename Frame>\nvoid wrap_damped_harmonic(\n    const gsl::not_null<tnsr::a<DataVector, SpatialDim, Frame>*> gauge_h,\n    const gsl::not_null<tnsr::ab<DataVector, SpatialDim, Frame>*> d4_gauge_h,\n    tnsr::aa<DataVector, SpatialDim, Frame> spacetime_metric,\n    const tnsr::aa<DataVector, SpatialDim, Frame>& pi,\n    const tnsr::iaa<DataVector, SpatialDim, Frame>& phi,\n    const tnsr::I<DataVector, SpatialDim, Frame>& coords,\n    const double amp_coef_L1, const double amp_coef_L2, const double amp_coef_S,\n    const double sigma_r) {\n  get<0, 0>(spacetime_metric) -= 1.0;\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    spacetime_metric.get(i + 1, i + 1) += 1.0;\n  }\n\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  const auto [det_spatial_metric, inverse_spatial_metric] =\n      determinant_and_inverse(spatial_metric);\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      DataVector{sqrt(get(det_spatial_metric))}};\n  const auto shift = gr::shift(spacetime_metric, inverse_spatial_metric);\n  const auto lapse = gr::lapse(shift, spacetime_metric);\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  tnsr::abb<DataVector, SpatialDim, Frame> d4_spacetime_metric{};\n  gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&d4_spacetime_metric), lapse, shift, pi, phi);\n  Scalar<DataVector> half_pi_two_normals{get(lapse).size(), 0.0};\n  tnsr::i<DataVector, SpatialDim, Frame> half_phi_two_normals{get(lapse).size(),\n                                                              0.0};\n  gh::gauges::half_pi_and_phi_two_normals(make_not_null(&half_pi_two_normals),\n                                          make_not_null(&half_phi_two_normals),\n                                          spacetime_normal_vector, pi, phi);\n\n  gh::gauges::damped_harmonic(\n      gauge_h, d4_gauge_h, lapse, shift, sqrt_det_spatial_metric,\n      inverse_spatial_metric, d4_spacetime_metric, half_pi_two_normals,\n      half_phi_two_normals, spacetime_metric, phi, coords, amp_coef_L1,\n      amp_coef_L2, amp_coef_S, 4, 4, 4, sigma_r);\n}\n\n// Compare with Python implementation\ntemplate <size_t SpatialDim, typename Frame>\nvoid test_with_python(const DataVector& used_for_size) {\n  INFO(\"Test with python\");\n  CAPTURE(SpatialDim);\n  CAPTURE(Frame{});\n  pypp::check_with_random_values<1>(\n      &wrap_damped_harmonic_rollon<SpatialDim, Frame>,\n      \"Evolution.Systems.GeneralizedHarmonic.GaugeSourceFunctions.\"\n      \"DampedHarmonic\",\n      {\"damped_harmonic_gauge_source_function_rollon\",\n       \"spacetime_deriv_damped_harmonic_gauge_source_function_rollon\"},\n      {{{-0.1, 0.1}}}, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &wrap_damped_harmonic<SpatialDim, Frame>,\n      \"Evolution.Systems.GeneralizedHarmonic.GaugeSourceFunctions.\"\n      \"DampedHarmonic\",\n      {\"damped_harmonic_gauge_source_function\",\n       \"spacetime_deriv_damped_harmonic_gauge_source_function\"},\n      {{{-0.1, 0.1}}}, used_for_size);\n}\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<gh::gauges::GaugeCondition,\n                             tmpl::list<gh::gauges::DampedHarmonic>>>;\n  };\n};\n\ntemplate <size_t Dim>\nvoid test_derived_class(const Mesh<Dim>& mesh) {\n  CAPTURE(Dim);\n\n  const auto gauge_condition = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<gh::gauges::GaugeCondition>,\n                                 Metavariables<Dim>>(\n          \"DampedHarmonic:\\n\"\n          \"  SpatialDecayWidth: 100.0\\n\"\n          \"  Amplitudes: [0.5, 1.5, 2.5]\\n\"\n          \"  Exponents: [2, 4, 6]\\n\")\n          ->get_clone());\n  CHECK_FALSE(gauge_condition->is_harmonic());\n\n  const size_t num_points = mesh.number_of_grid_points();\n\n  const double time = 1.2;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> deriv_dist(-1.e-5, 1.e-5);\n  std::uniform_real_distribution<> metric_dist(0.1, 1.0);\n  const auto pi =\n      make_with_random_values<tnsr::aa<DataVector, Dim, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&deriv_dist), num_points);\n  const auto phi =\n      make_with_random_values<tnsr::iaa<DataVector, Dim, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&deriv_dist), num_points);\n\n  auto spacetime_metric =\n      make_with_random_values<tnsr::aa<DataVector, Dim, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&metric_dist), num_points);\n  get<0, 0>(spacetime_metric) += -2.0;\n  for (size_t i = 0; i < Dim; ++i) {\n    spacetime_metric.get(i + 1, i + 1) += 4.0;\n    spacetime_metric.get(i + 1, 0) *= 0.01;\n  }\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  const auto [det_spatial_metric, inverse_spatial_metric] =\n      determinant_and_inverse(spatial_metric);\n  const Scalar<DataVector> sqrt_det_spatial_metric(\n      sqrt(get(det_spatial_metric)));\n  const auto shift = gr::shift(spacetime_metric, inverse_spatial_metric);\n  const auto lapse = gr::lapse(shift, spacetime_metric);\n  const auto inverse_spacetime_metric =\n      determinant_and_inverse(spacetime_metric).second;\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n\n  std::uniform_real_distribution<> coords_dist(1.0, 100.0);\n  const auto inertial_coords =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&coords_dist), num_points);\n\n  tnsr::abb<DataVector, Dim, Frame::Inertial> d4_spacetime_metric{};\n  gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&d4_spacetime_metric), lapse, shift, pi, phi);\n\n  Scalar<DataVector> half_pi_two_normals{get(lapse).size(), 0.0};\n  tnsr::i<DataVector, Dim, Frame::Inertial> half_phi_two_normals{\n      get(lapse).size(), 0.0};\n  gh::gauges::half_pi_and_phi_two_normals(make_not_null(&half_pi_two_normals),\n                                          make_not_null(&half_phi_two_normals),\n                                          spacetime_normal_vector, pi, phi);\n\n  tnsr::a<DataVector, Dim, Frame::Inertial> gauge_h(num_points);\n  tnsr::ab<DataVector, Dim, Frame::Inertial> d4_gauge_h(num_points);\n  dynamic_cast<const gh::gauges::DampedHarmonic&>(*gauge_condition)\n      .gauge_and_spacetime_derivative(\n          make_not_null(&gauge_h), make_not_null(&d4_gauge_h), lapse, shift,\n          sqrt_det_spatial_metric, inverse_spatial_metric, d4_spacetime_metric,\n          half_pi_two_normals, half_phi_two_normals, spacetime_metric, phi,\n          time, inertial_coords);\n\n  // Used dispatch with defaulted arguments that we don't need for\n  // DampedHarmonic gauge.\n  gh::gauges::dispatch<gh::Solutions::all_solutions<Dim>>(\n      make_not_null(&gauge_h), make_not_null(&d4_gauge_h), lapse, shift,\n      sqrt_det_spatial_metric, inverse_spatial_metric, d4_spacetime_metric,\n      half_pi_two_normals, half_phi_two_normals, spacetime_metric, phi, mesh,\n      time, inertial_coords, {}, *gauge_condition);\n\n  tnsr::a<DataVector, Dim, Frame::Inertial> expected_gauge_h(num_points);\n  tnsr::ab<DataVector, Dim, Frame::Inertial> expected_d4_gauge_h(num_points);\n  gh::gauges::damped_harmonic(\n      make_not_null(&expected_gauge_h), make_not_null(&expected_d4_gauge_h),\n      lapse, shift, sqrt_det_spatial_metric, inverse_spatial_metric,\n      d4_spacetime_metric, half_pi_two_normals, half_phi_two_normals,\n      spacetime_metric, phi, inertial_coords, 0.5, 1.5, 2.5, 2, 4, 6, 100.0);\n\n  CHECK_ITERABLE_APPROX(gauge_h, expected_gauge_h);\n  CHECK_ITERABLE_APPROX(d4_gauge_h, expected_d4_gauge_h);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GeneralizedHarmonic.Gauge.DampedHarmonic\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  const DataVector used_for_size(5);\n\n  test_rollon_function<1, Frame::Inertial>(used_for_size);\n  test_rollon_function<2, Frame::Inertial>(used_for_size);\n  test_rollon_function<3, Frame::Inertial>(used_for_size);\n\n  test_rollon_function<1, Frame::Inertial>(1.);\n  test_rollon_function<2, Frame::Inertial>(1.);\n  test_rollon_function<3, Frame::Inertial>(1.);\n\n  // Compare with Python implementation\n  test_with_python<1, Frame::Inertial>(used_for_size);\n  test_with_python<2, Frame::Inertial>(used_for_size);\n  test_with_python<3, Frame::Inertial>(used_for_size);\n\n  // Check the derived class for input file creation works.\n  gh::gauges::register_derived_with_charm();\n  for (const auto& basis_and_quadrature :\n       {std::pair{Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto},\n        std::pair{Spectral::Basis::FiniteDifference,\n                  Spectral::Quadrature::CellCentered}}) {\n    test_derived_class<1>(\n        {5, basis_and_quadrature.first, basis_and_quadrature.second});\n    test_derived_class<2>(\n        {5, basis_and_quadrature.first, basis_and_quadrature.second});\n    test_derived_class<3>(\n        {5, basis_and_quadrature.first, basis_and_quadrature.second});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Test_DampedWaveHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedWaveHelpers.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n\nnamespace {\ntemplate <size_t SpatialDim, typename Frame, typename DataType>\nvoid test_detail_functions(const DataType& used_for_size) {\n  // weight_function\n  pypp::check_with_random_values<2>(\n      &::gh::gauges::DampedHarmonicGauge_detail::spatial_weight_function<\n          DataType, SpatialDim, Frame>,\n      \"Evolution.Systems.GeneralizedHarmonic.GaugeSourceFunctions.\"\n      \"DampedWaveHelpers\",\n      {\"spatial_weight_function\"},\n      {{{-10., 10.}, {std::numeric_limits<double>::denorm_min(), 10.}}},\n      used_for_size);\n  // spacetime_deriv_of_spatial_weight_function\n  pypp::check_with_random_values<3>(\n      &::gh::gauges::DampedHarmonicGauge_detail::\n          spacetime_deriv_of_spatial_weight_function<DataType, SpatialDim,\n                                                     Frame>,\n      \"Evolution.Systems.GeneralizedHarmonic.GaugeSourceFunctions.\"\n      \"DampedWaveHelpers\",\n      {\"spacetime_deriv_spatial_weight_function\"},\n      {{{-10., 10.},\n        {std::numeric_limits<double>::denorm_min(), 10.},\n        {std::numeric_limits<double>::denorm_min(), 10.}}},\n      used_for_size);\n  // log_factor_metric_lapse\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataType> (*)(const Scalar<DataType>&,\n                                       const Scalar<DataType>&, const double)>(\n          &::gh::gauges::DampedHarmonicGauge_detail::log_factor_metric_lapse<\n              DataType>),\n      \"Evolution.Systems.GeneralizedHarmonic.GaugeSourceFunctions.\"\n      \"DampedWaveHelpers\",\n      \"log_fac\", {{{std::numeric_limits<double>::denorm_min(), 10.}}},\n      used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.Gh.Gauge.DampedWaveHelpers\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  const DataVector used_for_size(4);\n\n  test_detail_functions<1, Frame::Inertial>(used_for_size);\n  test_detail_functions<2, Frame::Inertial>(used_for_size);\n  test_detail_functions<3, Frame::Inertial>(used_for_size);\n\n  test_detail_functions<1, Frame::Inertial>(1.);\n  test_detail_functions<2, Frame::Inertial>(1.);\n  test_detail_functions<3, Frame::Inertial>(1.);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Test_HalfPiPhiTwoNormals.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/HalfPiPhiTwoNormals.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nnamespace {\ntemplate <size_t SpatialDim, typename Frame>\nvoid test_with_python(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      &gh::gauges::half_pi_and_phi_two_normals<SpatialDim, Frame>,\n      \"Evolution.Systems.GeneralizedHarmonic.GaugeSourceFunctions.\"\n      \"HalfPiPhiTwoNormals\",\n      {\"half_pi_two_normals\", \"half_phi_two_normals\"}, {{{-0.01, 0.01}}},\n      used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GeneralizedHarmonic.Gauge.HalfPiPhiTwoNormals\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  const DataVector used_for_size(5);\n\n  test_with_python<1, Frame::Grid>(used_for_size);\n  test_with_python<2, Frame::Grid>(used_for_size);\n  test_with_python<3, Frame::Grid>(used_for_size);\n  test_with_python<1, Frame::Inertial>(used_for_size);\n  test_with_python<2, Frame::Inertial>(used_for_size);\n  test_with_python<3, Frame::Inertial>(used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Test_Harmonic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/RegisterDerived.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<gh::gauges::GaugeCondition,\n                             tmpl::list<gh::gauges::Harmonic>>>;\n  };\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  const auto gauge_condition = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<gh::gauges::GaugeCondition>,\n                                 Metavariables>(\"Harmonic:\")\n          ->get_clone());\n  CHECK(gauge_condition->is_harmonic());\n\n  const size_t num_points = 5;\n  tnsr::a<DataVector, Dim, Frame::Inertial> gauge_h(num_points);\n  tnsr::ab<DataVector, Dim, Frame::Inertial> d4_gauge_h(num_points);\n\n  const double time = std::numeric_limits<double>::signaling_NaN();\n  const tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords(num_points);\n\n  // Used dispatch with defaulted arguments that we don't need for Harmonic\n  // gauge.\n  gh::gauges::dispatch<gh::Solutions::all_solutions<Dim>>(\n      make_not_null(&gauge_h), make_not_null(&d4_gauge_h), {}, {}, {}, {}, {},\n      {}, {}, {}, {}, Mesh<Dim>{}, time, inertial_coords, {}, *gauge_condition);\n  CHECK(gauge_h == tnsr::a<DataVector, Dim, Frame::Inertial>(num_points, 0.0));\n  CHECK(d4_gauge_h ==\n        tnsr::ab<DataVector, Dim, Frame::Inertial>(num_points, 0.0));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.Gauge.Harmonic\",\n                  \"[Unit][Evolution]\") {\n  gh::gauges::register_derived_with_charm();\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Test_SetPiAndPhiFromConstraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nusing evolved_vars_tags =\n    tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n               gh::Tags::Pi<DataVector, Dim>, gh::Tags::Phi<DataVector, Dim>>;\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using simple_tags = db::AddSimpleTags<\n      ::Tags::Time,\n      ::Tags::Variables<evolved_vars_tags<Metavariables::volume_dim>>,\n      domain::Tags::Mesh<Metavariables::volume_dim>,\n      domain::Tags::ElementMap<Metavariables::volume_dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<Metavariables::volume_dim,\n                                                  Frame::Grid, Frame::Inertial>,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::Coordinates<Metavariables::volume_dim,\n                                Frame::ElementLogical>>;\n  using compute_tags = db::AddComputeTags<>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<gh::gauges::SetPiAndPhiFromConstraints<\n              gh::Solutions::all_solutions<Metavariables::volume_dim>,\n              Metavariables::volume_dim>>>>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> generator) {\n  CAPTURE(Dim);\n  using metavariables = Metavariables<Dim>;\n  using component = Component<metavariables>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavariables>;\n\n  register_classes_with_charm<\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                            domain::CoordinateMaps::Identity<Dim>>,\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<Dim>>,\n      gh::gauges::DampedHarmonic>();\n\n  std::uniform_real_distribution<> metric_dist(0.1, 1.);\n  std::uniform_real_distribution<> deriv_dist(-1.e-5, 1.e-5);\n\n  const Mesh<Dim> mesh{5, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const size_t num_points = mesh.number_of_grid_points();\n  Variables<evolved_vars_tags<Dim>> evolved_vars{mesh.number_of_grid_points()};\n  get<gh::Tags::Pi<DataVector, Dim>>(evolved_vars) =\n      make_with_random_values<tnsr::aa<DataVector, Dim, Frame::Inertial>>(\n          generator, make_not_null(&deriv_dist), num_points);\n  get<gh::Tags::Phi<DataVector, Dim>>(evolved_vars) =\n      make_with_random_values<tnsr::iaa<DataVector, Dim, Frame::Inertial>>(\n          generator, make_not_null(&deriv_dist), num_points);\n  get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(evolved_vars) =\n      make_with_random_values<tnsr::aa<DataVector, Dim, Frame::Inertial>>(\n          generator, make_not_null(&metric_dist), num_points);\n  get<0, 0>(get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(evolved_vars)) +=\n      -2.0;\n  for (size_t i = 0; i < Dim; ++i) {\n    get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(evolved_vars)\n        .get(i + 1, i + 1) += 4.0;\n    get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(evolved_vars)\n        .get(i + 1, 0) *= 0.01;\n  }\n\n  // Testing SetPiAndPhiFromConstraints = False.\n  {\n    MockRuntimeSystem runner{\n        {std::unique_ptr<gh::gauges::GaugeCondition>(\n            std::make_unique<gh::gauges::DampedHarmonic>(\n                100., std::array{1.2, 1.5, 1.7}, std::array{2, 4, 6}))},\n        {false}};\n    ActionTesting::emplace_component_and_initialize<component>(\n        &runner, 0,\n        {0., evolved_vars, mesh,\n         ElementMap<Dim, Frame::Grid>{\n             ElementId<Dim>{0},\n             domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                 domain::CoordinateMaps::Identity<Dim>{})},\n         domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n             domain::CoordinateMaps::Identity<Dim>{}),\n         std::unordered_map<\n             std::string,\n             std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{},\n         logical_coordinates(mesh)});\n\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n\n    // Should be exact since we didn't compute anything\n    const auto& box = ActionTesting::get_databox<component>(runner, 0);\n    CHECK(get<gh::Tags::Pi<DataVector, Dim>>(evolved_vars) ==\n          db::get<gh::Tags::Pi<DataVector, Dim>>(box));\n    CHECK(get<gh::Tags::Phi<DataVector, Dim>>(evolved_vars) ==\n          db::get<gh::Tags::Phi<DataVector, Dim>>(box));\n  }\n\n  MockRuntimeSystem runner{\n      {std::unique_ptr<gh::gauges::GaugeCondition>(\n          std::make_unique<gh::gauges::DampedHarmonic>(\n              100., std::array{1.2, 1.5, 1.7}, std::array{2, 4, 6}))},\n      {true}};\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {0., evolved_vars, mesh,\n       ElementMap<Dim, Frame::Grid>{\n           ElementId<Dim>{0},\n           domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n               domain::CoordinateMaps::Identity<Dim>{})},\n       domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n           domain::CoordinateMaps::Identity<Dim>{}),\n       std::unordered_map<\n           std::string,\n           std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{},\n       logical_coordinates(mesh)});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n\n  // Verify that the gauge constraint is satisfied\n  const auto& box = ActionTesting::get_databox<component>(runner, 0);\n  const auto& spacetime_metric =\n      db::get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(box);\n  const auto& pi = db::get<gh::Tags::Pi<DataVector, Dim>>(box);\n  const auto& phi = db::get<gh::Tags::Phi<DataVector, Dim>>(box);\n\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  auto [sqrt_det_spatial_metric, inverse_spatial_metric] =\n      determinant_and_inverse(spatial_metric);\n  get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n  const auto shift = gr::shift(spacetime_metric, inverse_spatial_metric);\n  const auto lapse = gr::lapse(shift, spacetime_metric);\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, Dim, Frame::Inertial>(lapse);\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  tnsr::abb<DataVector, Dim, Frame::Inertial> d4_spacetime_metric{};\n  gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&d4_spacetime_metric), lapse, shift, pi, phi);\n\n  Scalar<DataVector> half_pi_two_normals{get(lapse).size(), 0.0};\n  tnsr::i<DataVector, Dim, Frame::Inertial> half_phi_two_normals{\n      get(lapse).size(), 0.0};\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    get(half_pi_two_normals) += spacetime_normal_vector.get(a) *\n                                spacetime_normal_vector.get(a) * pi.get(a, a);\n    for (size_t i = 0; i < Dim; ++i) {\n      half_phi_two_normals.get(i) += 0.5 * spacetime_normal_vector.get(a) *\n                                     spacetime_normal_vector.get(a) *\n                                     phi.get(i, a, a);\n    }\n    for (size_t b = a + 1; b < Dim + 1; ++b) {\n      get(half_pi_two_normals) += 2.0 * spacetime_normal_vector.get(a) *\n                                  spacetime_normal_vector.get(b) * pi.get(a, b);\n      for (size_t i = 0; i < Dim; ++i) {\n        half_phi_two_normals.get(i) += spacetime_normal_vector.get(a) *\n                                       spacetime_normal_vector.get(b) *\n                                       phi.get(i, a, b);\n      }\n    }\n  }\n  get(half_pi_two_normals) *= 0.5;\n\n  tnsr::a<DataVector, Dim, Frame::Inertial> gauge_h(num_points);\n  tnsr::ab<DataVector, Dim, Frame::Inertial> d4_gauge_h(num_points);\n  gh::gauges::dispatch<gh::Solutions::all_solutions<Dim>>(\n      make_not_null(&gauge_h), make_not_null(&d4_gauge_h), lapse, shift,\n      sqrt_det_spatial_metric, inverse_spatial_metric, d4_spacetime_metric,\n      half_pi_two_normals, half_phi_two_normals, spacetime_metric, phi, mesh,\n      db::get<::Tags::Time>(box),\n      db::get<domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                          Frame::Inertial>>(\n          box)(db::get<domain::Tags::ElementMap<Dim, Frame::Grid>>(box)(\n          db::get<domain::Tags::Coordinates<Dim, Frame::ElementLogical>>(box))),\n      {}, db::get<gh::gauges::Tags::GaugeCondition>(box));\n\n  const auto gauge_constraint = gh::gauge_constraint(\n      gauge_h, spacetime_normal_one_form, spacetime_normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi);\n  const tnsr::a<DataVector, Dim, Frame::Inertial> expected_gauge_constraint{\n      get<0>(gauge_constraint).size(), 0.};\n\n  Approx local_approx = Approx::custom().epsilon(1.e-10).scale(1.);\n  CHECK_ITERABLE_CUSTOM_APPROX(gauge_constraint, expected_gauge_constraint,\n                               local_approx);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GH.Gauge.SetPiAndPhiFromConstraints\",\n                  \"[Unit][Evolution][Actions]\") {\n  MAKE_GENERATOR(generator);\n  test<1>(make_not_null(&generator));\n  test<2>(make_not_null(&generator));\n  test<3>(make_not_null(&generator));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Harmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  TestHelpers::db::test_compute_tag<gh::gauges::Tags::GaugeAndDerivativeCompute<\n      Dim, gh::Solutions::all_solutions<Dim>>>(\n      \"Variables(GaugeH,SpacetimeDerivGaugeH)\");\n\n  // Use Harmonic gauge since then we don't need to set any values and we can\n  // still check that the compute tag works correctly.\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n          gr::Tags::SpacetimeNormalOneForm<DataVector, Dim>,\n          gr::Tags::SpacetimeNormalVector<DataVector, Dim>,\n          gr::Tags::SqrtDetSpatialMetric<DataVector>,\n          gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n          gr::Tags::SpacetimeMetric<DataVector, Dim>,\n          gh::Tags::Pi<DataVector, Dim>, gh::Tags::Phi<DataVector, Dim>,\n          ::Events::Tags::ObserverMesh<Dim>, ::Tags::Time,\n          ::Events::Tags::ObserverCoordinates<Dim, Frame::Inertial>,\n          ::Events::Tags::ObserverInverseJacobian<Dim, Frame::ElementLogical,\n                                                  Frame::Inertial>,\n          gh::gauges::Tags::GaugeCondition>,\n      db::AddComputeTags<gh::gauges::Tags::GaugeAndDerivativeCompute<\n          Dim, gh::Solutions::all_solutions<Dim>>>>(\n      Scalar<DataVector>{}, tnsr::I<DataVector, Dim, Frame::Inertial>{},\n      tnsr::a<DataVector, Dim, Frame::Inertial>{},\n      tnsr::A<DataVector, Dim, Frame::Inertial>{}, Scalar<DataVector>{},\n      tnsr::II<DataVector, Dim, Frame::Inertial>{},\n      tnsr::aa<DataVector, Dim, Frame::Inertial>{},\n      tnsr::aa<DataVector, Dim, Frame::Inertial>{},\n      tnsr::iaa<DataVector, Dim, Frame::Inertial>{},\n      Mesh<Dim>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, 1.3,\n      tnsr::I<DataVector, Dim, Frame::Inertial>{},\n      InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                      Frame::Inertial>{},\n      std::unique_ptr<gh::gauges::GaugeCondition>{\n          std::make_unique<gh::gauges::Harmonic>()});\n\n  const size_t num_points =\n      db::get<::Events::Tags::ObserverMesh<Dim>>(box).number_of_grid_points();\n\n  CHECK(db::get<gh::Tags::GaugeH<DataVector, Dim>>(box) ==\n        tnsr::a<DataVector, Dim, Frame::Inertial>(num_points, 0.0));\n  CHECK(db::get<gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>>(box) ==\n        tnsr::ab<DataVector, Dim, Frame::Inertial>(num_points, 0.0));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.Gauge.Tags\",\n                  \"[Evolution][Unit]\") {\n  TestHelpers::db::test_simple_tag<gh::gauges::Tags::GaugeCondition>(\n      \"GaugeCondition\");\n\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_ApplyTensorYlmFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/ApplyTensorYlmFilter.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/Test_ApplyTensorYlmFilter.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/ApplyTensorYlmFilter.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmFilter.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ylm::TensorYlm {\nnamespace {\nvoid test_break_spacetime_vars_into_spatial_pieces() {\n  constexpr size_t mesh_size = 10;\n\n  Variables<filter_detail::gh_spacetime_vars_list> gh_spacetime_vars(mesh_size);\n\n  // Fill with random numbers\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> dist{-1.0, 1.0};\n  for (size_t i = 0; i < gh_spacetime_vars.size(); ++i) {\n    gh_spacetime_vars.data()[i] = dist(generator);\n  }\n\n  // Break into spatial pieces, then do the inverse, and make\n  // sure we get the original back.\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Inertial>>\n      gh_spatial_vars(mesh_size);\n  filter_detail::break_spacetime_vars_into_spatial_pieces(\n      make_not_null(&gh_spatial_vars), gh_spacetime_vars);\n  Variables<filter_detail::gh_spacetime_vars_list> test_gh_spacetime_vars(\n      mesh_size);\n  filter_detail::assemble_spacetime_vars_from_spatial_pieces(\n      make_not_null(&test_gh_spacetime_vars), gh_spatial_vars);\n\n  // This should be equal to the last bit, since we aren't doing\n  // any operations that should incur roundoff error.\n  for (size_t i = 0; i < gh_spacetime_vars.size(); ++i) {\n    CHECK(gh_spacetime_vars.data()[i] == test_gh_spacetime_vars.data()[i]);\n  }\n}\n\nvoid test_transform_spatial_tensors_to_different_frame() {\n  constexpr size_t mesh_size = 10;\n\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Inertial>> inertial_vars(\n      mesh_size);\n\n  // Fill inertial_vars with random numbers\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> dist{-1.0, 1.0};\n  for (size_t i = 0; i < inertial_vars.size(); ++i) {\n    inertial_vars.data()[i] = dist(generator);\n  }\n\n  // Create a Jacobian that is filled with random numbers, but make\n  // sure that the jacobian is invertible.  Do this by making the\n  // Jacobian diagonally dominant.\n  std::uniform_real_distribution<double> positive_dist{0.5, 1.0};\n  InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid> jac(mesh_size);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      jac.get(i, j) = 0.05 * dist(generator);\n    }\n    jac.get(i, i) += positive_dist(generator);\n  }\n\n  // Invert the Jacobian\n  InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial> invjac(\n      mesh_size);\n  Scalar<DataVector> det(mesh_size);\n  determinant_and_inverse(make_not_null(&det), make_not_null(&invjac), jac);\n\n  // Transform and inverse-transform, and make sure we get what\n  // we started with.\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Grid>> grid_vars(\n      mesh_size);\n  filter_detail::transform_spatial_tensors_to_different_frame_without_hessians(\n      make_not_null(&grid_vars), inertial_vars, jac);\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Inertial>>\n      test_inertial_vars(mesh_size);\n  filter_detail::transform_spatial_tensors_to_different_frame_without_hessians(\n      make_not_null(&test_inertial_vars), grid_vars, invjac);\n\n  // We may get roundoff differences because there is multiplication\n  // by Jacobians.\n  for (size_t i = 0; i < inertial_vars.size(); ++i) {\n    CHECK(inertial_vars.data()[i] == approx(test_inertial_vars.data()[i]));\n  }\n}\n\nvoid test_modal_nodal_invertibility() {\n  constexpr size_t radial_extents = 3;\n  constexpr size_t ell_max = 4;\n\n  const auto& ylm = ::ylm::get_spherepack_cache(ell_max);\n  const size_t spectral_mesh_size = ylm.spectral_size() * radial_extents;\n  const size_t physical_mesh_size = ylm.physical_size() * radial_extents;\n\n  // Fill modal variables with random numbers in each mode.\n  // Note that we fill only valid modes in the storage.\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Grid>> modal_vars(\n      spectral_mesh_size);\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> dist{-1.0, 1.0};\n  ylm::SpherepackIterator it(ell_max, ell_max, radial_extents, true);\n  tmpl::for_each<filter_detail::gh_spatial_vars_list<Frame::Grid>>(\n      [&modal_vars, &it, &dist,\n       &generator]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        auto& tensor = get<Tag>(modal_vars);\n        for (auto& component : tensor) {\n          for (size_t offset = 0; offset < radial_extents; ++offset) {\n            for (it.reset(); it; ++it) {\n              component[it() + offset] = dist(generator);\n            }\n          }\n        }\n      });\n\n  // Do modal to nodal, and inverse transformation.\n  // We do modal->nodal->modal rather than nodal->modal->nodal because\n  // for S2, nodal points have more degrees of freedom than nodal values,\n  // i.e. arbitrary nodal values are not necessarily representable as modes.\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Grid>> nodal_vars(\n      physical_mesh_size);\n  filter_detail::modal_to_nodal_ylm(make_not_null(&nodal_vars), modal_vars, ylm,\n                                    radial_extents);\n  Variables<filter_detail::gh_spatial_vars_list<Frame::Grid>> test_modal_vars(\n      spectral_mesh_size, 0.0);\n  filter_detail::nodal_to_modal_ylm(make_not_null(&test_modal_vars), nodal_vars,\n                                    ylm, radial_extents);\n\n  // We should get (modulo roundoff) what we started with.\n  tmpl::for_each<filter_detail::gh_spatial_vars_list<Frame::Grid>>(\n      [&modal_vars, &test_modal_vars,\n       &it]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        constexpr size_t num_independent_components =\n            Tag::type::structure::size();\n        const auto& tensor_a = get<Tag>(modal_vars);\n        const auto& tensor_b = get<Tag>(test_modal_vars);\n        for (size_t storage_index = 0;\n             storage_index < num_independent_components; ++storage_index) {\n          const auto& a = tensor_a[storage_index];\n          const auto& b = tensor_b[storage_index];\n          for (size_t offset = 0; offset < radial_extents; ++offset) {\n            for (it.reset(); it; ++it) {\n              CHECK(a[it() + offset] == approx(b[it() + offset]));\n            }\n          }\n        }\n      });\n}\n\n// Debug builds are timing out slightly, so increase the timeout.\n// [[TimeOut, 20]]\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GeneralizedHarmonic.ApplyTensorYlmFilter\",\n    \"[NumericalAlgorithms][Unit]\") {\n  test_break_spacetime_vars_into_spatial_pieces();\n  test_transform_spatial_tensors_to_different_frame();\n  test_modal_nodal_invertibility();\n  test_apply_filter<filter_detail::gh_spacetime_vars_list>(0);\n  test_apply_filter<filter_detail::gh_spacetime_vars_list>(5);\n}\n}  // namespace\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_BbhCheckpointAndExitIfCompletePhaseChange.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <utility>\n\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/PhaseControl/CheckpointAndExitIfComplete.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        PhaseChange,\n        tmpl::list<gh::bbh::phase_control::CheckpointAndExitIfComplete>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.GeneralizedHarmonic.BbhCheckpointAndExitIfCompletePhaseChange\",\n    \"[Unit][Evolution]\") {\n  // `contribute_phase_data_impl` is not tested directly because reduction\n  // contributions are currently not supported in this unit-test style.\n  const Parallel::GlobalCache<Metavariables> cache{};\n  const gh::bbh::phase_control::CheckpointAndExitIfComplete phase_change{};\n  using PhaseChangeDecisionData = tuples::TaggedTuple<\n      gh::bbh::phase_control::Tags::CheckpointRequested,\n      gh::bbh::phase_control::Tags::ExitAfterWriteCheckpoint>;\n\n  {\n    INFO(\"Initialize decision data\");\n    PhaseChangeDecisionData phase_change_decision_data{true, true};\n    phase_change.initialize_phase_data<Metavariables>(\n        make_not_null(&phase_change_decision_data));\n    CHECK(phase_change_decision_data == PhaseChangeDecisionData{false, false});\n  }\n  {\n    INFO(\"No request means no phase change\");\n    PhaseChangeDecisionData phase_change_decision_data{false, false};\n    const auto decision_result = phase_change.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Evolve,\n        cache);\n    CHECK(decision_result == std::nullopt);\n    CHECK(phase_change_decision_data == PhaseChangeDecisionData{false, false});\n  }\n  {\n    INFO(\"Completion request in Evolve jumps to WriteCheckpoint\");\n    PhaseChangeDecisionData phase_change_decision_data{true, false};\n    const auto decision_result = phase_change.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Evolve,\n        cache);\n    CHECK(\n        decision_result ==\n        std::make_pair(Parallel::Phase::WriteCheckpoint,\n                       PhaseControl::ArbitrationStrategy::RunPhaseImmediately));\n    CHECK(phase_change_decision_data == PhaseChangeDecisionData{false, true});\n  }\n  {\n    INFO(\"After WriteCheckpoint, jump to Exit\");\n    PhaseChangeDecisionData phase_change_decision_data{false, true};\n    const auto decision_result = phase_change.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data),\n        Parallel::Phase::WriteCheckpoint, cache);\n    CHECK(\n        decision_result ==\n        std::make_pair(Parallel::Phase::Exit,\n                       PhaseControl::ArbitrationStrategy::RunPhaseImmediately));\n    CHECK(phase_change_decision_data == PhaseChangeDecisionData{false, false});\n  }\n  {\n    INFO(\"If both decisions are true in Evolve, prioritize WriteCheckpoint\");\n    PhaseChangeDecisionData phase_change_decision_data{true, true};\n    const auto decision_result = phase_change.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Evolve,\n        cache);\n    CHECK(\n        decision_result ==\n        std::make_pair(Parallel::Phase::WriteCheckpoint,\n                       PhaseControl::ArbitrationStrategy::RunPhaseImmediately));\n    CHECK(phase_change_decision_data == PhaseChangeDecisionData{false, true});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_BbhCompletionCriteria.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionCriteria.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GeneralizedHarmonic.BbhCompletionCriteria\",\n    \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<\n      gh::bbh::Tags::MinCommonHorizonSuccessesBeforeChecks>(\n      \"MinCommonHorizonSuccessesBeforeChecks\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::MaxCommonHorizonSuccesses>(\n      \"MaxCommonHorizonSuccesses\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::GaugeConstraintLinfThreshold>(\n      \"GaugeConstraintLinfThreshold\");\n  TestHelpers::db::test_simple_tag<\n      gh::bbh::Tags::ThreeIndexConstraintLinfThreshold>(\n      \"ThreeIndexConstraintLinfThreshold\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::CommonHorizonLMaxThreshold>(\n      \"CommonHorizonLMaxThreshold\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::ConstraintCheckVerbose>(\n      \"ConstraintCheckVerbose\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::GaugeConstraintExceeded>(\n      \"GaugeConstraintExceeded\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::ThreeIndexConstraintExceeded>(\n      \"ThreeIndexConstraintExceeded\");\n  TestHelpers::db::test_simple_tag<\n      gh::bbh::Tags::CommonHorizonLMaxBelowOrEqualThreshold>(\n      \"CommonHorizonLMaxBelowOrEqualThreshold\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::CommonHorizonSuccessCount>(\n      \"CommonHorizonSuccessCount\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::CompletionRequested>(\n      \"CompletionRequested\");\n  TestHelpers::db::test_simple_tag<gh::bbh::Tags::ElementCompletionRequested>(\n      \"ElementCompletionRequested\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_BbhCompletionSingleton.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionCriteria.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Bbh/CompletionSingleton.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct MockSingletonComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using simple_tags =\n      tmpl::list<gh::bbh::Tags::GaugeConstraintExceeded,\n                 gh::bbh::Tags::ThreeIndexConstraintExceeded,\n                 gh::bbh::Tags::CommonHorizonLMaxBelowOrEqualThreshold,\n                 gh::bbh::Tags::CommonHorizonSuccessCount,\n                 gh::bbh::Tags::CompletionRequested,\n                 gh::bbh::Tags::CommonHorizonSuccessRecords,\n                 gh::bbh::Tags::ConstraintCheckRecords,\n                 gh::bbh::Tags::ReportedConstraintCheckRecords,\n                 gh::bbh::Tags::ElementCompletionRequested>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockElementComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using simple_tags = tmpl::list<gh::bbh::Tags::ElementCompletionRequested>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\nstruct MockMetavariables {\n  using component_list = tmpl::list<MockSingletonComponent<MockMetavariables>,\n                                    MockElementComponent<MockMetavariables>>;\n  using gh_dg_element_array = MockElementComponent<MockMetavariables>;\n  using const_global_cache_tags =\n      tmpl::list<gh::bbh::Tags::MinCommonHorizonSuccessesBeforeChecks,\n                 gh::bbh::Tags::MaxCommonHorizonSuccesses,\n                 gh::bbh::Tags::GaugeConstraintLinfThreshold,\n                 gh::bbh::Tags::ThreeIndexConstraintLinfThreshold,\n                 gh::bbh::Tags::CommonHorizonLMaxThreshold,\n                 gh::bbh::Tags::ConstraintCheckVerbose>;\n  using mutable_global_cache_tags = tmpl::list<>;\n};\n\nusing mock_component = MockSingletonComponent<MockMetavariables>;\nusing mock_element_component = MockElementComponent<MockMetavariables>;\n\nauto make_runner(const size_t min_common_horizon_successes_before_checks = 2,\n                 const size_t max_common_horizon_successes = 100,\n                 const double gauge_constraint_linf_threshold = 10.0,\n                 const double three_index_constraint_linf_threshold = 20.0,\n                 const size_t common_horizon_lmax_threshold = 6,\n                 const bool constraint_check_verbose = false) {\n  return ActionTesting::MockRuntimeSystem<MockMetavariables>{\n      {min_common_horizon_successes_before_checks, max_common_horizon_successes,\n       gauge_constraint_linf_threshold, three_index_constraint_linf_threshold,\n       common_horizon_lmax_threshold, constraint_check_verbose}};\n}\n\nvoid initialize_components(\n    const gsl::not_null<ActionTesting::MockRuntimeSystem<MockMetavariables>*>\n        runner) {\n  ActionTesting::emplace_component_and_initialize<mock_component>(\n      runner, 0,\n      {false, false, false, 0_st, false,\n       gh::bbh::Tags::CommonHorizonSuccessRecords::type{},\n       gh::bbh::Tags::ConstraintCheckRecords::type{},\n       gh::bbh::Tags::ReportedConstraintCheckRecords::type{}, false});\n  ActionTesting::emplace_component_and_initialize<mock_element_component>(\n      runner, 0, {false});\n}\n\nSPECTRE_TEST_CASE(\"Unit.GeneralizedHarmonic.BbhCompletionSingleton\",\n                  \"[Unit][Evolution]\") {\n  {\n    INFO(\"Constraint checks remain gated by min successes at their check time\");\n    auto runner = make_runner();\n    initialize_components(make_not_null(&runner));\n    auto& box = ActionTesting::get_databox<mock_component>(runner, 0);\n    auto& element_box =\n        ActionTesting::get_databox<mock_element_component>(runner, 0);\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::ProcessConstraintMaxima>(\n        make_not_null(&runner), 0, 1.5, 11.0, 1.0);\n    CHECK_FALSE(db::get<gh::bbh::Tags::GaugeConstraintExceeded>(box));\n    CHECK_FALSE(db::get<gh::bbh::Tags::CompletionRequested>(box));\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{2.0, std::nullopt},\n        8_st);\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{3.0, std::nullopt},\n        8_st);\n    CHECK(db::get<gh::bbh::Tags::CommonHorizonSuccessCount>(box) == 2_st);\n    CHECK_FALSE(db::get<gh::bbh::Tags::GaugeConstraintExceeded>(box));\n    CHECK_FALSE(db::get<gh::bbh::Tags::CompletionRequested>(box));\n\n    // Delayed older successes still don't arm the t=1.5 constraint check until\n    // enough successes exist at or before that check time.\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{1.0, std::nullopt},\n        8_st);\n    CHECK_FALSE(db::get<gh::bbh::Tags::GaugeConstraintExceeded>(box));\n    CHECK_FALSE(db::get<gh::bbh::Tags::CompletionRequested>(box));\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{1.2, std::nullopt},\n        8_st);\n    CHECK(db::get<gh::bbh::Tags::CommonHorizonSuccessCount>(box) == 4_st);\n    CHECK(db::get<gh::bbh::Tags::GaugeConstraintExceeded>(box));\n    CHECK(db::get<gh::bbh::Tags::CompletionRequested>(box));\n    CHECK_FALSE(\n        ActionTesting::is_simple_action_queue_empty<mock_element_component>(\n            runner, 0));\n    ActionTesting::invoke_queued_simple_action<mock_element_component>(\n        make_not_null(&runner), 0);\n    CHECK(db::get<gh::bbh::Tags::ElementCompletionRequested>(element_box));\n  }\n\n  {\n    INFO(\"Delayed older AhC success can retroactively satisfy the LMax path\");\n    auto runner = make_runner();\n    initialize_components(make_not_null(&runner));\n    auto& box = ActionTesting::get_databox<mock_component>(runner, 0);\n    auto& element_box =\n        ActionTesting::get_databox<mock_element_component>(runner, 0);\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{2.0, std::nullopt},\n        8_st);\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{3.0, std::nullopt},\n        8_st);\n    CHECK(db::get<gh::bbh::Tags::CommonHorizonSuccessCount>(box) == 2_st);\n    CHECK_FALSE(\n        db::get<gh::bbh::Tags::CommonHorizonLMaxBelowOrEqualThreshold>(box));\n    CHECK_FALSE(db::get<gh::bbh::Tags::CompletionRequested>(box));\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{1.0, std::nullopt},\n        6_st);\n    CHECK(db::get<gh::bbh::Tags::CommonHorizonSuccessCount>(box) == 3_st);\n    CHECK(db::get<gh::bbh::Tags::CommonHorizonLMaxBelowOrEqualThreshold>(box));\n    CHECK(db::get<gh::bbh::Tags::CompletionRequested>(box));\n    CHECK_FALSE(\n        ActionTesting::is_simple_action_queue_empty<mock_element_component>(\n            runner, 0));\n    ActionTesting::invoke_queued_simple_action<mock_element_component>(\n        make_not_null(&runner), 0);\n    CHECK(db::get<gh::bbh::Tags::ElementCompletionRequested>(element_box));\n  }\n\n  {\n    INFO(\"Max AhC successes can request completion\");\n    auto runner = make_runner(2, 3, 1.0e6, 1.0e6, 0, false);\n    initialize_components(make_not_null(&runner));\n    auto& box = ActionTesting::get_databox<mock_component>(runner, 0);\n    auto& element_box =\n        ActionTesting::get_databox<mock_element_component>(runner, 0);\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{1.0, std::nullopt},\n        8_st);\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{2.0, std::nullopt},\n        8_st);\n    CHECK(db::get<gh::bbh::Tags::CommonHorizonSuccessCount>(box) == 2_st);\n    CHECK_FALSE(db::get<gh::bbh::Tags::CompletionRequested>(box));\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{3.0, std::nullopt},\n        8_st);\n    CHECK(db::get<gh::bbh::Tags::CommonHorizonSuccessCount>(box) == 3_st);\n    CHECK_FALSE(\n        db::get<gh::bbh::Tags::CommonHorizonLMaxBelowOrEqualThreshold>(box));\n    CHECK_FALSE(db::get<gh::bbh::Tags::GaugeConstraintExceeded>(box));\n    CHECK_FALSE(db::get<gh::bbh::Tags::ThreeIndexConstraintExceeded>(box));\n    CHECK(db::get<gh::bbh::Tags::CompletionRequested>(box));\n    CHECK_FALSE(\n        ActionTesting::is_simple_action_queue_empty<mock_element_component>(\n            runner, 0));\n    ActionTesting::invoke_queued_simple_action<mock_element_component>(\n        make_not_null(&runner), 0);\n    CHECK(db::get<gh::bbh::Tags::ElementCompletionRequested>(element_box));\n  }\n\n  {\n    INFO(\"Three-index threshold can request completion\");\n    auto runner = make_runner(2, 100, 1.0e6, 20.0, 0, false);\n    initialize_components(make_not_null(&runner));\n    auto& box = ActionTesting::get_databox<mock_component>(runner, 0);\n    auto& element_box =\n        ActionTesting::get_databox<mock_element_component>(runner, 0);\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{1.0, std::nullopt},\n        8_st);\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{1.2, std::nullopt},\n        8_st);\n    CHECK_FALSE(db::get<gh::bbh::Tags::CompletionRequested>(box));\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::ProcessConstraintMaxima>(\n        make_not_null(&runner), 0, 1.5, 1.0, 21.0);\n    CHECK_FALSE(db::get<gh::bbh::Tags::GaugeConstraintExceeded>(box));\n    CHECK(db::get<gh::bbh::Tags::ThreeIndexConstraintExceeded>(box));\n    CHECK(db::get<gh::bbh::Tags::CompletionRequested>(box));\n    CHECK_FALSE(\n        ActionTesting::is_simple_action_queue_empty<mock_element_component>(\n            runner, 0));\n    ActionTesting::invoke_queued_simple_action<mock_element_component>(\n        make_not_null(&runner), 0);\n    CHECK(db::get<gh::bbh::Tags::ElementCompletionRequested>(element_box));\n  }\n\n  {\n    INFO(\"Invalid min/max input relation errors in release mode\");\n    auto runner = make_runner(3, 2, 1.0e6, 1.0e6, 0, false);\n    initialize_components(make_not_null(&runner));\n\n    CHECK_THROWS_WITH(\n        (ActionTesting::simple_action<\n            mock_component, gh::bbh::Actions::RecordCommonHorizonSuccess>(\n            make_not_null(&runner), 0,\n            LinkedMessageId<double>{1.0, std::nullopt}, 8_st)),\n        Catch::Matchers::ContainsSubstring(\"MaxCommonHorizonSuccesses\"));\n  }\n\n  {\n    INFO(\"Duplicate AhC records are rejected\");\n    auto runner = make_runner(10, 100, 1.0e6, 1.0e6, 0, false);\n    initialize_components(make_not_null(&runner));\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::RecordCommonHorizonSuccess>(\n        make_not_null(&runner), 0, LinkedMessageId<double>{2.0, std::nullopt},\n        8_st);\n    CHECK_THROWS_WITH(\n        (ActionTesting::simple_action<\n            mock_component, gh::bbh::Actions::RecordCommonHorizonSuccess>(\n            make_not_null(&runner), 0,\n            LinkedMessageId<double>{2.0, std::nullopt}, 7_st)),\n        Catch::Matchers::ContainsSubstring(\n            \"Duplicate common-horizon completion record\"));\n  }\n\n  {\n    INFO(\"Duplicate constraint records are rejected\");\n    auto runner = make_runner(10, 100, 1.0e6, 1.0e6, 0, false);\n    initialize_components(make_not_null(&runner));\n\n    ActionTesting::simple_action<mock_component,\n                                 gh::bbh::Actions::ProcessConstraintMaxima>(\n        make_not_null(&runner), 0, 2.0, 5.0, 6.0);\n    CHECK_THROWS_WITH(\n        (ActionTesting::simple_action<\n            mock_component, gh::bbh::Actions::ProcessConstraintMaxima>(\n            make_not_null(&runner), 0, 2.0, 8.0, 9.0)),\n        Catch::Matchers::ContainsSubstring(\n            \"Duplicate BBH completion constraint-max record\"));\n  }\n\n  {\n    INFO(\"Element completion-request simple action latches true\");\n    auto runner = make_runner();\n    initialize_components(make_not_null(&runner));\n    auto& box = ActionTesting::get_databox<mock_element_component>(runner, 0);\n    ActionTesting::simple_action<\n        mock_element_component,\n        gh::bbh::Actions::SetElementCompletionRequested>(make_not_null(&runner),\n                                                         0);\n    CHECK(db::get<gh::bbh::Tags::ElementCompletionRequested>(box));\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <size_t Index, size_t Dim, typename Frame>\nScalar<DataVector> speed_with_index(\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const tnsr::i<DataVector, Dim, Frame>& normal) {\n  return Scalar<DataVector>{\n      gh::characteristic_speeds(gamma_1, lapse, shift, normal, {})[Index]};\n}\n\ntemplate <size_t Index, size_t Dim, typename Frame>\nScalar<DataVector> char_speed_with_moving_mesh(\n    const Scalar<DataVector>& gamma_1, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, Dim, Frame>& shift,\n    const tnsr::i<DataVector, Dim, Frame>& normal,\n    const tnsr::I<DataVector, Dim, Frame>& mesh_velocity) {\n  return Scalar<DataVector>{gh::characteristic_speeds(\n      gamma_1, lapse, shift, normal, {mesh_velocity})[Index]};\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid test_characteristic_speeds() {\n  TestHelpers::db::test_compute_tag<\n      gh::CharacteristicSpeedsCompute<Dim, Frame>>(\"CharacteristicSpeeds\");\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(speed_with_index<0, Dim, Frame>,\n                                    \"Characteristics\", \"char_speed_upsi\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<1, Dim, Frame>,\n                                    \"Characteristics\", \"char_speed_uzero\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<3, Dim, Frame>,\n                                    \"Characteristics\", \"char_speed_uminus\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<2, Dim, Frame>,\n                                    \"Characteristics\", \"char_speed_uplus\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      char_speed_with_moving_mesh<0, Dim, Frame>, \"Characteristics\",\n      \"char_speed_upsi_moving_mesh\", {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      char_speed_with_moving_mesh<1, Dim, Frame>, \"Characteristics\",\n      \"char_speed_uzero_moving_mesh\", {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      char_speed_with_moving_mesh<2, Dim, Frame>, \"Characteristics\",\n      \"char_speed_uplus_moving_mesh\", {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      char_speed_with_moving_mesh<3, Dim, Frame>, \"Characteristics\",\n      \"char_speed_uminus_moving_mesh\", {{{-2.0, 2.0}}}, used_for_size);\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid test_characteristic_speeds_on_strahlkorper() {\n  TestHelpers::db::test_simple_tag<\n      gh::CharacteristicSpeedsOnStrahlkorper<Frame>>(\n      \"CharacteristicSpeedsOnStrahlkorper\");\n  TestHelpers::db::test_compute_tag<\n      gh::CharacteristicSpeedsOnStrahlkorperCompute<Dim, Frame>>(\n      \"CharacteristicSpeedsOnStrahlkorper\");\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(speed_with_index<0, Dim, Frame>,\n                                    \"Characteristics\", \"char_speed_upsi\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<1, Dim, Frame>,\n                                    \"Characteristics\", \"char_speed_uzero\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<3, Dim, Frame>,\n                                    \"Characteristics\", \"char_speed_uminus\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<2, Dim, Frame>,\n                                    \"Characteristics\", \"char_speed_uplus\",\n                                    {{{-2.0, 2.0}}}, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      char_speed_with_moving_mesh<0, Dim, Frame>, \"Characteristics\",\n      \"char_speed_upsi_moving_mesh\", {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      char_speed_with_moving_mesh<1, Dim, Frame>, \"Characteristics\",\n      \"char_speed_uzero_moving_mesh\", {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      char_speed_with_moving_mesh<3, Dim, Frame>, \"Characteristics\",\n      \"char_speed_uminus_moving_mesh\", {{{-2.0, 2.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      char_speed_with_moving_mesh<2, Dim, Frame>, \"Characteristics\",\n      \"char_speed_uplus_moving_mesh\", {{{-2.0, 2.0}}}, used_for_size);\n}\n\ntemplate <size_t Dim, typename TargetFrame>\nvoid test_characteristic_speed_vectors() {\n  using SourceFrame = Frame::ElementLogical;\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::VSpacetimeMetricSpeed<DataVector, Dim>>(\n      \"VSpacetimeMetricSpeed\");\n  TestHelpers::db::test_simple_tag<gh::Tags::VZeroSpeed<DataVector, Dim>>(\n      \"VZeroSpeed\");\n  TestHelpers::db::test_simple_tag<gh::Tags::VPlusSpeed<DataVector, Dim>>(\n      \"VPlusSpeed\");\n  TestHelpers::db::test_simple_tag<gh::Tags::VMinusSpeed<DataVector, Dim>>(\n      \"VMinusSpeed\");\n\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::VSpacetimeMetricSpeedCompute<Dim, TargetFrame, SourceFrame>>(\n      \"VSpacetimeMetricSpeed\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::VZeroSpeedCompute<Dim, TargetFrame, SourceFrame>>(\"VZeroSpeed\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::VPlusSpeedCompute<Dim, TargetFrame, SourceFrame>>(\"VPlusSpeed\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::VMinusSpeedCompute<Dim, TargetFrame, SourceFrame>>(\n      \"VMinusSpeed\");\n\n  MAKE_GENERATOR(gen);\n  const DataVector used_for_size(5);\n  std::uniform_real_distribution<> unit_dist(-1.0, 1.0);\n  std::uniform_real_distribution<> reals_dist(-2.0, 2.0);\n  const auto gamma_1 = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&gen), make_not_null(&reals_dist), used_for_size);\n  const auto lapse = TestHelpers::gr::random_lapse(&gen, used_for_size);\n  const auto shift =\n      make_with_random_values<tnsr::I<DataVector, Dim, TargetFrame>>(\n          make_not_null(&gen), make_not_null(&unit_dist), used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<Dim>(&gen, used_for_size);\n  tnsr::II<DataVector, Dim, TargetFrame> inverse_spatial_metric{};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      inverse_spatial_metric.get(i, j) = spatial_metric.get(i, j);\n    }\n  }\n  const auto mesh_velocity_non_optional =\n      make_with_random_values<tnsr::I<DataVector, Dim, TargetFrame>>(\n          make_not_null(&gen), make_not_null(&reals_dist), used_for_size);\n  const auto mesh_velocity =\n      std::make_optional(std::move(mesh_velocity_non_optional));\n  // Almost certain to be non-singular\n  const auto inverse_jacobian = make_with_random_values<\n      InverseJacobian<DataVector, Dim, Frame::ElementLogical, TargetFrame>>(\n      make_not_null(&gen), make_not_null(&reals_dist), used_for_size);\n\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, TargetFrame>\n      normalized_inv_jacobian{get_size(get<0, 0>(inverse_jacobian))};\n  DataVector magnitude{get_size(get<0, 0>(inverse_jacobian))};\n  for (size_t i_hat = 0; i_hat < Dim; ++i_hat) {\n    magnitude = 0.;\n    for (size_t j = 0; j < Dim; ++j) {\n      for (size_t k = 0; k < Dim; ++k) {\n        magnitude += inverse_jacobian.get(i_hat, j) *\n                     inverse_spatial_metric.get(j, k) *\n                     inverse_jacobian.get(i_hat, k);\n      }\n    }\n    ASSERT(min(magnitude) > 0,\n           \"Trying to normalize inverse jacobian with a negative magnitude: \"\n               << magnitude);\n    magnitude = sqrt(magnitude);\n    for (size_t j = 0; j < Dim; ++j) {\n      normalized_inv_jacobian.get(i_hat, j) =\n          inverse_jacobian.get(i_hat, j) / magnitude;\n    }\n  }\n  for (const bool use_mesh : {true, false}) {\n    auto box = db::create<\n        db::AddSimpleTags<\n            ::gh::Tags::ConstraintGamma1, gr::Tags::Lapse<DataVector>,\n            gr::Tags::Shift<DataVector, Dim, TargetFrame>,\n            domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                          TargetFrame>,\n            gr::Tags::InverseSpatialMetric<DataVector, Dim, TargetFrame>,\n            domain::Tags::MeshVelocity<Dim, TargetFrame>>,\n        db::AddComputeTags<::gh::Tags::VSpacetimeMetricSpeedCompute<\n                               Dim, TargetFrame, Frame::ElementLogical>,\n                           ::gh::Tags::VZeroSpeedCompute<Dim, TargetFrame,\n                                                         Frame::ElementLogical>,\n                           ::gh::Tags::VPlusSpeedCompute<Dim, TargetFrame,\n                                                         Frame::ElementLogical>,\n                           ::gh::Tags::VMinusSpeedCompute<\n                               Dim, TargetFrame, Frame::ElementLogical>>>(\n        gamma_1, lapse, shift, inverse_jacobian, inverse_spatial_metric,\n        use_mesh ? mesh_velocity : std::nullopt);\n\n    const auto& vspacetime_tensor =\n        db::get<::gh::Tags::VSpacetimeMetricSpeed<DataVector, Dim,\n                                                  Frame::ElementLogical>>(box);\n    const auto& vzero_tensor =\n        db::get<::gh::Tags::VZeroSpeed<DataVector, Dim, Frame::ElementLogical>>(\n            box);\n    const auto& vplus_tensor =\n        db::get<::gh::Tags::VPlusSpeed<DataVector, Dim, Frame::ElementLogical>>(\n            box);\n    const auto& vminus_tensor = db::get<\n        ::gh::Tags::VMinusSpeed<DataVector, Dim, Frame::ElementLogical>>(box);\n\n    tnsr::i<DataVector, Dim, TargetFrame> normal{};\n    for (size_t i = 0; i < Dim; ++i) {\n      CAPTURE(i);\n      for (size_t j = 0; j < Dim; ++j) {\n        normal.get(j) = normalized_inv_jacobian.get(i, j);\n      }\n      const auto [vspacetime_scalar, vzero_scalar, vplus_scalar,\n                  vminus_scalar] =\n          gh::characteristic_speeds(gamma_1, lapse, shift, normal,\n                                    use_mesh ? mesh_velocity : std::nullopt);\n\n      CHECK_ITERABLE_APPROX(vspacetime_scalar, vspacetime_tensor.get(i));\n      CHECK_ITERABLE_APPROX(vzero_scalar, vzero_tensor.get(i));\n      CHECK_ITERABLE_APPROX(vplus_scalar, vplus_tensor.get(i));\n      CHECK_ITERABLE_APPROX(vminus_scalar, vminus_tensor.get(i));\n    }\n  }\n}\n\n// Test return-by-reference GH char speeds by comparing to Kerr-Schild\ntemplate <typename Solution>\nvoid test_characteristic_speeds_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Set up grid\n  const size_t spatial_dim = 3;\n  const Mesh<spatial_dim> mesh{grid_size_each_dimension,\n                               Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, spatial_dim>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, spatial_dim>>(vars);\n\n  // Get ingredients\n  const auto gamma_1 = make_with_value<Scalar<DataVector>>(x, 0.1);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  // Outward 3-normal to the surface on which characteristic fields are needed\n  auto unit_normal_one_form =\n      make_with_value<tnsr::i<DataVector, spatial_dim, Frame::Inertial>>(x, 1.);\n  const auto norm_of_one_form =\n      magnitude(unit_normal_one_form, inverse_spatial_metric);\n  for (size_t i = 0; i < spatial_dim; ++i) {\n    unit_normal_one_form.get(i) /= get(norm_of_one_form);\n  }\n\n  // Get generalized harmonic characteristic speeds locally\n  const auto shift_dot_normal = dot_product(shift, unit_normal_one_form);\n  const auto upsi_speed = -(1. + get(gamma_1)) * get(shift_dot_normal);\n  const auto uzero_speed = -get(shift_dot_normal);\n  const auto uplus_speed = -get(shift_dot_normal) + get(lapse);\n  const auto uminus_speed = -get(shift_dot_normal) - get(lapse);\n\n  // Check that locally computed fields match returned ones\n  std::array<DataVector, 4> char_speeds_from_func{};\n  gh::CharacteristicSpeedsCompute<spatial_dim, Frame::Inertial>::function(\n      make_not_null(&char_speeds_from_func), gamma_1, lapse, shift,\n      unit_normal_one_form, {});\n  const auto& upsi_speed_from_func = char_speeds_from_func[0];\n  const auto& uzero_speed_from_func = char_speeds_from_func[1];\n  const auto& uplus_speed_from_func = char_speeds_from_func[2];\n  const auto& uminus_speed_from_func = char_speeds_from_func[3];\n\n  CHECK_ITERABLE_APPROX(upsi_speed, upsi_speed_from_func);\n  CHECK_ITERABLE_APPROX(uzero_speed, uzero_speed_from_func);\n  CHECK_ITERABLE_APPROX(uplus_speed, uplus_speed_from_func);\n  CHECK_ITERABLE_APPROX(uminus_speed, uminus_speed_from_func);\n\n  // Check that locally computed fields on Strahlkorper match returned ones\n  // Note that CharacteristicSpeedsOnStrahlkorperCompute has a different\n  // return type than CharacteristicSpeedsCompute.\n  tnsr::a<DataVector, 3, Frame::Inertial> char_speeds_from_strahlkorper{};\n  gh::CharacteristicSpeedsOnStrahlkorperCompute<spatial_dim, Frame::Inertial>::\n      function(make_not_null(&char_speeds_from_strahlkorper), gamma_1, lapse,\n               shift, unit_normal_one_form);\n  const auto& upsi_speed_from_strahlkorper = char_speeds_from_strahlkorper[0];\n  const auto& uzero_speed_from_strahlkorper = char_speeds_from_strahlkorper[1];\n  const auto& uplus_speed_from_strahlkorper = char_speeds_from_strahlkorper[2];\n  const auto& uminus_speed_from_strahlkorper = char_speeds_from_strahlkorper[3];\n\n  CHECK_ITERABLE_APPROX(upsi_speed, upsi_speed_from_strahlkorper);\n  CHECK_ITERABLE_APPROX(uzero_speed, uzero_speed_from_strahlkorper);\n  CHECK_ITERABLE_APPROX(uplus_speed, uplus_speed_from_strahlkorper);\n  CHECK_ITERABLE_APPROX(uminus_speed, uminus_speed_from_strahlkorper);\n}\n}  // namespace\n\nnamespace {\ntemplate <typename Tag, size_t Dim, typename Frame>\ntypename Tag::type field_with_tag(\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::II<DataVector, Dim, Frame>& inverse_spatial_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim, Frame>& pi,\n    const tnsr::iaa<DataVector, Dim, Frame>& phi,\n    const tnsr::i<DataVector, Dim, Frame>& normal_one_form) {\n  return get<Tag>(gh::characteristic_fields(gamma_2, inverse_spatial_metric,\n                                            spacetime_metric, pi, phi,\n                                            normal_one_form));\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid test_characteristic_fields() {\n  TestHelpers::db::test_compute_tag<\n      gh::CharacteristicFieldsCompute<Dim, Frame>>(\"CharacteristicFields\");\n  const DataVector used_for_size(5);\n  // VSpacetimeMetric\n  pypp::check_with_random_values<1>(\n      field_with_tag<gh::Tags::VSpacetimeMetric<DataVector, Dim, Frame>, Dim,\n                     Frame>,\n      \"Characteristics\", \"char_field_upsi\", {{{-2., 2.}}}, used_for_size);\n  // VZero\n  pypp::check_with_random_values<1>(\n      field_with_tag<gh::Tags::VZero<DataVector, Dim, Frame>, Dim, Frame>,\n      \"Characteristics\", \"char_field_uzero\", {{{-2., 2.}}}, used_for_size,\n      1.e-9);  // last argument loosens tolerance from\n               // default of 1.0e-12 to avoid occasional\n               // failures of this test, suspected from\n               // accumulated roundoff error\n  // VPlus\n  pypp::check_with_random_values<1>(\n      field_with_tag<gh::Tags::VPlus<DataVector, Dim, Frame>, Dim, Frame>,\n      \"Characteristics\", \"char_field_uplus\", {{{-2., 2.}}}, used_for_size,\n      1.e-10);  // last argument loosens tolerance from\n                // default of 1.0e-12 to avoid occasional\n                // failures of this test, suspected from\n                // accumulated roundoff error\n  // VMinus\n  pypp::check_with_random_values<1>(\n      field_with_tag<gh::Tags::VMinus<DataVector, Dim, Frame>, Dim, Frame>,\n      \"Characteristics\", \"char_field_uminus\", {{{-2., 2.}}}, used_for_size,\n      1.e-10);  // last argument loosens tolerance from\n                // default of 1.0e-12 to avoid occasional\n                // failures of this test, suspected from\n                // accumulated roundoff error\n}\n\n// Test return-by-reference GH char fields by comparing to Kerr-Schild\ntemplate <typename Solution>\nvoid test_characteristic_fields_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Set up grid\n  const size_t spatial_dim = 3;\n  const Mesh<spatial_dim> mesh{grid_size_each_dimension,\n                               Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, spatial_dim>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataVector, spatial_dim>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, spatial_dim>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, spatial_dim>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Get ingredients\n  const size_t n_pts = x.begin()->size();\n  const auto gamma_2 = make_with_value<Scalar<DataVector>>(x, 0.1);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n  const auto phi =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n                         dt_spatial_metric, phi);\n  const auto normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, spatial_dim, Frame::Inertial>(\n          lapse);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n  // Outward 3-normal to the surface on which characteristic fields are needed\n  auto unit_normal_one_form =\n      make_with_value<tnsr::i<DataVector, spatial_dim, Frame::Inertial>>(x, 1.);\n  const auto norm_of_one_form =\n      magnitude(unit_normal_one_form, inverse_spatial_metric);\n  for (size_t i = 0; i < spatial_dim; ++i) {\n    unit_normal_one_form.get(i) /= get(norm_of_one_form);\n  }\n  const auto normal =\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric);\n\n  // Compute characteristic fields locally\n  tnsr::aa<DataVector, spatial_dim, Frame::Inertial> phi_dot_normal{\n      DataVector(n_pts, 0.)};\n  // Compute phi_dot_normal_{ab} = n^i \\Phi_{iab}\n  for (size_t mu = 0; mu < spatial_dim + 1; ++mu) {\n    for (size_t nu = 0; nu < mu + 1; ++nu) {\n      for (size_t i = 0; i < spatial_dim; ++i) {\n        phi_dot_normal.get(mu, nu) += normal.get(i) * phi.get(i, mu, nu);\n      }\n    }\n  }\n  tnsr::iaa<DataVector, spatial_dim, Frame::Inertial> phi_dot_projection_tensor{\n      DataVector(n_pts, 0.)};\n  // Compute phi_dot_projection_tensor_{kab} = projection_tensor^i_k \\Phi_{kab}\n  for (size_t i = 0; i < spatial_dim; ++i) {\n    for (size_t mu = 0; mu < spatial_dim + 1; ++mu) {\n      for (size_t nu = 0; nu < mu + 1; ++nu) {\n        phi_dot_projection_tensor.get(i, mu, nu) =\n            phi.get(i, mu, nu) -\n            unit_normal_one_form.get(i) * phi_dot_normal.get(mu, nu);\n      }\n    }\n  }\n  // Eq.(32)-(34) of Lindblom+ (2005)\n  const auto& upsi = spacetime_metric;\n  const auto& uzero = phi_dot_projection_tensor;\n  tnsr::aa<DataVector, spatial_dim, Frame::Inertial> uplus{\n      DataVector(n_pts, 0.)};\n  tnsr::aa<DataVector, spatial_dim, Frame::Inertial> uminus{\n      DataVector(n_pts, 0.)};\n  for (size_t mu = 0; mu < spatial_dim + 1; ++mu) {\n    for (size_t nu = 0; nu < mu + 1; ++nu) {\n      uplus.get(mu, nu) = pi.get(mu, nu) + phi_dot_normal.get(mu, nu) -\n                          get(gamma_2) * spacetime_metric.get(mu, nu);\n      uminus.get(mu, nu) = pi.get(mu, nu) - phi_dot_normal.get(mu, nu) -\n                           get(gamma_2) * spacetime_metric.get(mu, nu);\n    }\n  }\n\n  // Check that locally computed fields match returned ones\n  const auto uvars = gh::characteristic_fields(gamma_2, inverse_spatial_metric,\n                                               spacetime_metric, pi, phi,\n                                               unit_normal_one_form);\n\n  const auto& upsi_from_func =\n      get<gh::Tags::VSpacetimeMetric<DataVector, spatial_dim>>(uvars);\n  const auto& uzero_from_func =\n      get<gh::Tags::VZero<DataVector, spatial_dim>>(uvars);\n  const auto& uplus_from_func =\n      get<gh::Tags::VPlus<DataVector, spatial_dim>>(uvars);\n  const auto& uminus_from_func =\n      get<gh::Tags::VMinus<DataVector, spatial_dim>>(uvars);\n\n  CHECK_ITERABLE_APPROX(upsi, upsi_from_func);\n  CHECK_ITERABLE_APPROX(uzero, uzero_from_func);\n  CHECK_ITERABLE_APPROX(uplus, uplus_from_func);\n  CHECK_ITERABLE_APPROX(uminus, uminus_from_func);\n}\n}  // namespace\n\nnamespace {\ntemplate <typename Tag, size_t Dim, typename Frame>\ntypename Tag::type evol_field_with_tag(\n    const Scalar<DataVector>& gamma_2,\n    const tnsr::aa<DataVector, Dim, Frame>& u_psi,\n    const tnsr::iaa<DataVector, Dim, Frame>& u_zero,\n    const tnsr::aa<DataVector, Dim, Frame>& u_plus,\n    const tnsr::aa<DataVector, Dim, Frame>& u_minus,\n    const tnsr::i<DataVector, Dim, Frame>& normal_one_form) {\n  return get<Tag>(gh::evolved_fields_from_characteristic_fields(\n      gamma_2, u_psi, u_zero, u_plus, u_minus, normal_one_form));\n}\n\ntemplate <size_t Dim, typename Frame>\nvoid test_evolved_from_characteristic_fields() {\n  TestHelpers::db::test_compute_tag<\n      gh::EvolvedFieldsFromCharacteristicFieldsCompute<Dim, Frame>>(\n      \"EvolvedFieldsFromCharacteristicFields\");\n  const DataVector used_for_size(5);\n  // Psi\n  pypp::check_with_random_values<1>(\n      evol_field_with_tag<gr::Tags::SpacetimeMetric<DataVector, Dim, Frame>,\n                          Dim, Frame>,\n      \"Characteristics\", \"evol_field_psi\", {{{-2., 2.}}}, used_for_size);\n  // Pi\n  pypp::check_with_random_values<1>(\n      evol_field_with_tag<gh::Tags::Pi<DataVector, Dim, Frame>, Dim, Frame>,\n      \"Characteristics\", \"evol_field_pi\", {{{-2., 2.}}}, used_for_size);\n  // Phi\n  pypp::check_with_random_values<1>(\n      evol_field_with_tag<gh::Tags::Phi<DataVector, Dim, Frame>, Dim, Frame>,\n      \"Characteristics\", \"evol_field_phi\", {{{-2., 2.}}}, used_for_size);\n}\n\n// Test return-by-reference GH fundamental fields by comparing to Kerr-Schild\ntemplate <typename Solution>\nvoid test_evolved_from_characteristic_fields_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Set up grid\n  const size_t spatial_dim = 3;\n  const Mesh<spatial_dim> mesh{grid_size_each_dimension,\n                               Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, spatial_dim>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataVector, spatial_dim>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, spatial_dim>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, spatial_dim>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Get ingredients\n  const size_t n_pts = x.begin()->size();\n  const auto gamma_2 = make_with_value<Scalar<DataVector>>(x, 0.1);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n  const auto phi =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n                         dt_spatial_metric, phi);\n  const auto normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, spatial_dim, Frame::Inertial>(\n          lapse);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n  // Outward 3-normal to the surface on which characteristic fields are needed\n  auto unit_normal_one_form =\n      make_with_value<tnsr::i<DataVector, spatial_dim, Frame::Inertial>>(x, 1.);\n  const auto norm_of_one_form =\n      magnitude(unit_normal_one_form, inverse_spatial_metric);\n  for (size_t i = 0; i < spatial_dim; ++i) {\n    unit_normal_one_form.get(i) /= get(norm_of_one_form);\n  }\n  const auto normal =\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric);\n\n  // Fundamental fields (psi, pi, phi) have already been computed locally.\n  // Now, check that these locally computed fields match returned ones\n  tnsr::aa<DataVector, spatial_dim, Frame::Inertial> phi_dot_normal{\n      DataVector(n_pts, 0.)};\n  // Compute phi_dot_normal_{ab} = n^i \\Phi_{iab}\n  for (size_t mu = 0; mu < spatial_dim + 1; ++mu) {\n    for (size_t nu = 0; nu < mu + 1; ++nu) {\n      for (size_t i = 0; i < spatial_dim; ++i) {\n        phi_dot_normal.get(mu, nu) += normal.get(i) * phi.get(i, mu, nu);\n      }\n    }\n  }\n  tnsr::iaa<DataVector, spatial_dim, Frame::Inertial> phi_dot_projection_tensor{\n      DataVector(n_pts, 0.)};\n  // Compute phi_dot_projection_tensor_{kab} = projection_tensor^i_k \\Phi_{kab}\n  for (size_t i = 0; i < spatial_dim; ++i) {\n    for (size_t mu = 0; mu < spatial_dim + 1; ++mu) {\n      for (size_t nu = 0; nu < mu + 1; ++nu) {\n        phi_dot_projection_tensor.get(i, mu, nu) =\n            phi.get(i, mu, nu) -\n            unit_normal_one_form.get(i) * phi_dot_normal.get(mu, nu);\n      }\n    }\n  }\n  // Eq.(32)-(34) of Lindblom+ (2005)\n  const auto& upsi = spacetime_metric;\n  const auto& uzero = phi_dot_projection_tensor;\n  tnsr::aa<DataVector, spatial_dim, Frame::Inertial> uplus{\n      DataVector(n_pts, 0.)};\n  tnsr::aa<DataVector, spatial_dim, Frame::Inertial> uminus{\n      DataVector(n_pts, 0.)};\n  for (size_t mu = 0; mu < spatial_dim + 1; ++mu) {\n    for (size_t nu = 0; nu < mu + 1; ++nu) {\n      uplus.get(mu, nu) = pi.get(mu, nu) + phi_dot_normal.get(mu, nu) -\n                          get(gamma_2) * spacetime_metric.get(mu, nu);\n      uminus.get(mu, nu) = pi.get(mu, nu) - phi_dot_normal.get(mu, nu) -\n                           get(gamma_2) * spacetime_metric.get(mu, nu);\n    }\n  }\n  const auto ffields = gh::evolved_fields_from_characteristic_fields(\n      gamma_2, upsi, uzero, uplus, uminus, unit_normal_one_form);\n  const auto& psi_from_func =\n      get<gr::Tags::SpacetimeMetric<DataVector, spatial_dim>>(ffields);\n  const auto& pi_from_func =\n      get<gh::Tags::Pi<DataVector, spatial_dim>>(ffields);\n  const auto& phi_from_func =\n      get<gh::Tags::Phi<DataVector, spatial_dim>>(ffields);\n\n  CHECK_ITERABLE_APPROX(spacetime_metric, psi_from_func);\n  CHECK_ITERABLE_APPROX(pi, pi_from_func);\n  CHECK_ITERABLE_APPROX(phi, phi_from_func);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.Characteristics\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GeneralizedHarmonic/\"};\n\n  test_characteristic_speeds<1, Frame::Grid>();\n  test_characteristic_speeds<2, Frame::Grid>();\n  test_characteristic_speeds<3, Frame::Grid>();\n  test_characteristic_speeds<1, Frame::Inertial>();\n  test_characteristic_speeds<2, Frame::Inertial>();\n  test_characteristic_speeds<3, Frame::Inertial>();\n\n  test_characteristic_speeds_on_strahlkorper<1, Frame::Grid>();\n  test_characteristic_speeds_on_strahlkorper<2, Frame::Grid>();\n  test_characteristic_speeds_on_strahlkorper<3, Frame::Grid>();\n  test_characteristic_speeds_on_strahlkorper<1, Frame::Inertial>();\n  test_characteristic_speeds_on_strahlkorper<2, Frame::Inertial>();\n  test_characteristic_speeds_on_strahlkorper<3, Frame::Inertial>();\n\n  test_characteristic_speed_vectors<1, Frame::Grid>();\n  test_characteristic_speed_vectors<2, Frame::Grid>();\n  test_characteristic_speed_vectors<3, Frame::Grid>();\n  test_characteristic_speed_vectors<1, Frame::Inertial>();\n  test_characteristic_speed_vectors<2, Frame::Inertial>();\n  test_characteristic_speed_vectors<3, Frame::Inertial>();\n\n  // Test GH characteristic speeds against Kerr Schild\n  const double mass = 2.;\n  const std::array<double, 3> spin{{0.3, 0.5, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  const size_t grid_size = 2;\n  const std::array<double, 3> lower_bound{{0.82, 1.22, 1.32}};\n  const std::array<double, 3> upper_bound{{0.78, 1.18, 1.28}};\n\n  test_characteristic_speeds_analytic(solution, grid_size, lower_bound,\n                                      upper_bound);\n\n  test_characteristic_fields<1, Frame::Grid>();\n  test_characteristic_fields<2, Frame::Grid>();\n  test_characteristic_fields<3, Frame::Grid>();\n  test_characteristic_fields<1, Frame::Inertial>();\n  test_characteristic_fields<2, Frame::Inertial>();\n  test_characteristic_fields<3, Frame::Inertial>();\n\n  test_evolved_from_characteristic_fields<1, Frame::Grid>();\n  test_evolved_from_characteristic_fields<2, Frame::Grid>();\n  test_evolved_from_characteristic_fields<3, Frame::Grid>();\n  test_evolved_from_characteristic_fields<1, Frame::Inertial>();\n  test_evolved_from_characteristic_fields<2, Frame::Inertial>();\n  test_evolved_from_characteristic_fields<3, Frame::Inertial>();\n\n  // Test GH characteristic fields against Kerr Schild\n  test_characteristic_fields_analytic(solution, grid_size, lower_bound,\n                                      upper_bound);\n  test_evolved_from_characteristic_fields_analytic(solution, grid_size,\n                                                   lower_bound, upper_bound);\n}\n\nnamespace {\ntemplate <size_t Dim>\nvoid check_max_char_speed(const DataVector& used_for_size) {\n  CAPTURE(Dim);\n  MAKE_GENERATOR(gen);\n\n  // Fraction of times the test can randomly fail\n  const double failure_tolerance = 1e-10;\n  // Minimum fraction of the claimed result that must be found in some\n  // random trial\n  const double check_minimum = 0.9;\n  // Minimum fraction of the claimed result that can be found in any\n  // random trial (generally only important for 1D where the random\n  // vector will point along the maximum speed direction)\n  const double check_maximum = 1.0 + 1e-12;\n\n  const double trial_failure_rate =\n      Dim == 1\n          ? 0.1  // correct value is 0.0, but cannot take log of that\n          : (Dim == 2 ? 1.0 - 2.0 * acos(check_minimum) / M_PI : check_minimum);\n\n  const size_t trials =\n      static_cast<size_t>(log(failure_tolerance) / log(trial_failure_rate)) + 1;\n\n  const auto lapse = TestHelpers::gr::random_lapse(&gen, used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<Dim>(&gen, used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<Dim>(&gen, used_for_size);\n  std::uniform_real_distribution<> gamma_1_dist(-5.0, 5.0);\n  const auto gamma_1 = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&gen), make_not_null(&gamma_1_dist), used_for_size);\n  double max_char_speed = std::numeric_limits<double>::signaling_NaN();\n  gh::Tags::ComputeLargestCharacteristicSpeed<Dim, Frame::Inertial>::function(\n      make_not_null(&max_char_speed), gamma_1, lapse, shift, spatial_metric);\n\n  double maximum_observed = -std::numeric_limits<double>::infinity();\n  for (size_t i = 0; i < trials; ++i) {\n    const auto unit_one_form = raise_or_lower_index(\n        random_unit_normal(&gen, spatial_metric), spatial_metric);\n\n    const auto characteristic_speeds =\n        gh::characteristic_speeds(gamma_1, lapse, shift, unit_one_form, {});\n    double max_speed_in_chosen_direction = 0.0;\n    for (const auto& speed : characteristic_speeds) {\n      max_speed_in_chosen_direction =\n          std::max(max_speed_in_chosen_direction, max(abs(speed)));\n    }\n\n    CHECK(max_speed_in_chosen_direction <= max_char_speed * check_maximum);\n    maximum_observed =\n        std::max(maximum_observed, max_speed_in_chosen_direction);\n  }\n  CHECK(maximum_observed >= check_minimum * max_char_speed);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.MaxCharSpeed\",\n                  \"[Unit][Evolution]\") {\n  check_max_char_speed<1>(DataVector(5));\n  check_max_char_speed<2>(DataVector(5));\n  check_max_char_speed<3>(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/GaussianPlusConstant.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_three_index_constraint(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iaa<DataType, SpatialDim, Frame> (*)(\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&)>(\n          &gh::three_index_constraint<DataType, SpatialDim, Frame>),\n      \"numpy\", \"subtract\", {{{-1.0, 1.0}}}, used_for_size);\n}\n\n// Test the return-by-value gauge constraint function using random values\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_gauge_constraint_random(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::a<DataType, SpatialDim, Frame> (*)(\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&,\n          const tnsr::AA<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&)>(\n          &gh::gauge_constraint<DataType, SpatialDim, Frame>),\n      \"Constraints\", \"gauge_constraint\", {{{-1.0, 1.0}}}, used_for_size);\n}\n\n// Test the return-by-reference gauge constraint by comparing to Kerr-Schild\ntemplate <typename Solution>\nvoid test_gauge_constraint_analytic(const Solution& solution,\n                                    const size_t grid_size_each_dimension,\n                                    const std::array<double, 3>& lower_bound,\n                                    const std::array<double, 3>& upper_bound) {\n  // Check vs. time-independent analytic solution\n  // Set up grid\n  const Mesh<3> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1.0, 1.0, lower_bound[0], upper_bound[0]},\n              Affine{-1.0, 1.0, lower_bound[1], upper_bound[1]},\n              Affine{-1.0, 1.0, lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Get ingredients for computing the gauge constraint\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto gauge_function = gh::gauge_source(\n      lapse, dt_lapse, d_lapse, shift, dt_shift, d_shift, spatial_metric,\n      trace(gr::extrinsic_curvature(lapse, shift, d_shift, spatial_metric,\n                                    dt_spatial_metric, d_spatial_metric),\n            inverse_spatial_metric),\n      trace_last_indices(gr::christoffel_first_kind(d_spatial_metric),\n                         inverse_spatial_metric));\n  const auto phi =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n                         dt_spatial_metric, phi);\n  const auto normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, 3, Frame::Inertial>(lapse);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n\n  // Get the constraint, and check that it vanishes\n  auto constraint =\n      make_with_value<tnsr::a<DataVector, 3, Frame::Inertial>>(x, 0.0);\n  const gsl::not_null<tnsr::a<DataVector, 3, Frame::Inertial>*>\n      constraint_pointer = &(constraint);\n  gh::gauge_constraint(constraint_pointer, gauge_function, normal_one_form,\n                       normal_vector, inverse_spatial_metric,\n                       inverse_spacetime_metric, pi, phi);\n  CHECK_ITERABLE_APPROX(constraint,\n                        make_with_value<decltype(constraint)>(x, 0.0));\n}\n\n// Test the return-by-value two-index constraint function using random values\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_two_index_constraint_random(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ia<DataType, SpatialDim, Frame> (*)(\n          const tnsr::ab<DataType, SpatialDim, Frame>&,\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&,\n          const tnsr::AA<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::ijaa<DataType, SpatialDim, Frame>&,\n          const Scalar<DataType>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&)>(\n          &gh::two_index_constraint<DataType, SpatialDim, Frame>),\n      \"Constraints\", \"two_index_constraint\", {{{-1.0, 1.0}}}, used_for_size);\n}\n\n// Test the return-by-reference two-index constraint\n// by comparing to a time-independent analytic solution (e.g. Kerr-Schild)\ntemplate <typename Solution>\nvoid test_two_index_constraint_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound, const double error_tolerance) {\n  // Shorter names for tags.\n  using SpacetimeMetric = gr::Tags::SpacetimeMetric<DataVector, 3>;\n  using Pi = ::gh::Tags::Pi<DataVector, 3>;\n  using Phi = ::gh::Tags::Phi<DataVector, 3>;\n  using GaugeH = ::gh::Tags::GaugeH<DataVector, 3>;\n  using VariablesTags = tmpl::list<SpacetimeMetric, Pi, Phi, GaugeH>;\n\n  // Check vs. time-independent analytic solution\n  // Set up grid\n  const size_t data_size = pow<3>(grid_size_each_dimension);\n  const Mesh<3> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1.0, 1.0, lower_bound[0], upper_bound[0]},\n              Affine{-1.0, 1.0, lower_bound[1], upper_bound[1]},\n              Affine{-1.0, 1.0, lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Compute quantities from variables for the two-index constraint\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, 3, Frame::Inertial>(lapse);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n\n  // Arbitrary choice for gamma2\n  const auto gamma2 = make_with_value<Scalar<DataVector>>(x, 4.0);\n\n  // Compute derivatives d_psi, d_phi, d_pi, and d_gauge_function numerically\n  Variables<VariablesTags> gh_vars(data_size);\n  auto& spacetime_metric = get<SpacetimeMetric>(gh_vars);\n  auto& pi = get<Pi>(gh_vars);\n  auto& phi = get<Phi>(gh_vars);\n  auto& gauge_function = get<GaugeH>(gh_vars);\n  spacetime_metric = gr::spacetime_metric(lapse, shift, spatial_metric);\n  phi =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n              dt_spatial_metric, phi);\n  gauge_function = gh::gauge_source(\n      lapse, dt_lapse, d_lapse, shift, dt_shift, d_shift, spatial_metric,\n      trace(gr::extrinsic_curvature(lapse, shift, d_shift, spatial_metric,\n                                    dt_spatial_metric, d_spatial_metric),\n            inverse_spatial_metric),\n      trace_last_indices(gr::christoffel_first_kind(d_spatial_metric),\n                         inverse_spatial_metric));\n\n  // Compute numerical derivatives of psi,pi,phi,H\n  const auto gh_derivs =\n      partial_derivatives<VariablesTags, VariablesTags, 3, Frame::Inertial>(\n          gh_vars, mesh, coord_map.inv_jacobian(x_logical));\n  const auto& d_spacetime_metric =\n      get<Tags::deriv<SpacetimeMetric, tmpl::size_t<3>, Frame::Inertial>>(\n          gh_derivs);\n  const auto& d_pi =\n      get<Tags::deriv<Pi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n  const auto& d_phi =\n      get<Tags::deriv<Phi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n  const auto& d_gauge_function =\n      get<Tags::deriv<GaugeH, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n\n  // Populate spacetime derivative of gauge function\n  const auto d4_gauge_function = [&x, &d_gauge_function]() {\n    auto tmp =\n        make_with_value<tnsr::ab<DataVector, 3, Frame::Inertial>>(x, 0.0);\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t i = 0; i < 3; ++i) {\n        tmp.get(i + 1, a) = d_gauge_function.get(i, a);\n      }\n      // Populate the time derivative with NaN as a check that those are not\n      // being used in calculating constraints\n      tmp.get(0, a) = std::numeric_limits<double>::signaling_NaN();\n    }\n    return tmp;\n  }();\n\n  // Compute the three-index constraint\n  const auto three_index_constraint =\n      gh::three_index_constraint(d_spacetime_metric, phi);\n\n  // Get the constraint, and check that it vanishes to error_tolerance\n  auto two_index_constraint =\n      make_with_value<tnsr::ia<DataVector, 3, Frame::Inertial>>(\n          x, std::numeric_limits<double>::signaling_NaN());\n  gh::two_index_constraint(make_not_null(&two_index_constraint),\n                           d4_gauge_function, normal_one_form, normal_vector,\n                           inverse_spatial_metric, inverse_spacetime_metric, pi,\n                           phi, d_pi, d_phi, gamma2, three_index_constraint);\n\n  const Approx numerical_approx =\n      Approx::custom().epsilon(error_tolerance).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      two_index_constraint,\n      make_with_value<decltype(two_index_constraint)>(x, 0.0),\n      numerical_approx);\n}\n\n// Test the return-by-value four-index constraint function using random values\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_four_index_constraint_random(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iaa<DataType, SpatialDim, Frame> (*)(\n          const tnsr::ijaa<DataType, SpatialDim, Frame>&)>(\n          &gh::four_index_constraint<DataType, SpatialDim, Frame>),\n      \"Constraints\", \"four_index_constraint\", {{{-1.0, 1.0}}}, used_for_size);\n}\n\n// Test the return-by-reference four-index constraint\n// by comparing to a time-independent analytic solution (e.g. Kerr-Schild)\ntemplate <typename Solution>\nvoid test_four_index_constraint_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound, const double error_tolerance) {\n  // Shorter names for tags.\n  using Phi = ::gh::Tags::Phi<DataVector, 3>;\n  using VariablesTags = tmpl::list<Phi>;\n\n  // Check vs. time-independent analytic solution\n  // Set up grid\n  const size_t data_size = pow<3>(grid_size_each_dimension);\n  const Mesh<3> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1.0, 1.0, lower_bound[0], upper_bound[0]},\n              Affine{-1.0, 1.0, lower_bound[1], upper_bound[1]},\n              Affine{-1.0, 1.0, lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Compute derivative d_phi numerically\n  Variables<VariablesTags> gh_vars(data_size);\n  get<Phi>(gh_vars) =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n\n  // Compute numerical derivatives of phi\n  const auto gh_derivs =\n      partial_derivatives<VariablesTags, VariablesTags, 3, Frame::Inertial>(\n          gh_vars, mesh, coord_map.inv_jacobian(x_logical));\n  const auto& d_phi =\n      get<Tags::deriv<Phi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n\n  // Get the constraint, and check that it vanishes to error_tolerance\n  auto four_index_constraint =\n      make_with_value<tnsr::iaa<DataVector, 3, Frame::Inertial>>(\n          x, std::numeric_limits<double>::signaling_NaN());\n  gh::four_index_constraint(make_not_null(&four_index_constraint), d_phi);\n\n  const Approx numerical_approx =\n      Approx::custom().epsilon(error_tolerance).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      four_index_constraint,\n      make_with_value<decltype(four_index_constraint)>(x, 0.0),\n      numerical_approx);\n}\n\n// Test the return-by-value F constraint function using random values\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_f_constraint_random(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::a<DataType, SpatialDim, Frame> (*)(\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::ab<DataType, SpatialDim, Frame>&,\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&,\n          const tnsr::AA<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::ijaa<DataType, SpatialDim, Frame>&,\n          const Scalar<DataType>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&)>(\n          &gh::f_constraint<DataType, SpatialDim, Frame>),\n      \"Constraints\", \"f_constraint\", {{{-1.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::a<DataType, SpatialDim, Frame> (*)(\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::ab<DataType, SpatialDim, Frame>&,\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&,\n          const tnsr::AA<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::ijaa<DataType, SpatialDim, Frame>&,\n          const Scalar<DataType>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&)>(\n          &gh::f_constraint<DataType, SpatialDim, Frame>),\n      \"Constraints\", \"f_constraint_with_stress_energy\", {{{-1.0, 1.0}}},\n      used_for_size);\n}\n\n// Test the return-by-reference F constraint\n// by comparing to a time-independent analytic solution (e.g. Kerr-Schild)\ntemplate <typename Solution>\nvoid test_f_constraint_analytic(const Solution& solution,\n                                const size_t grid_size_each_dimension,\n                                const std::array<double, 3>& lower_bound,\n                                const std::array<double, 3>& upper_bound,\n                                const double error_tolerance) {\n  // Shorter names for tags.\n  using SpacetimeMetric = gr::Tags::SpacetimeMetric<DataVector, 3>;\n  using Pi = ::gh::Tags::Pi<DataVector, 3>;\n  using Phi = ::gh::Tags::Phi<DataVector, 3>;\n  using GaugeH = ::gh::Tags::GaugeH<DataVector, 3>;\n  using VariablesTags = tmpl::list<SpacetimeMetric, Pi, Phi, GaugeH>;\n\n  // Check vs. time-independent analytic solution\n  // Set up grid\n  const size_t data_size = pow<3>(grid_size_each_dimension);\n  const Mesh<3> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1.0, 1.0, lower_bound[0], upper_bound[0]},\n              Affine{-1.0, 1.0, lower_bound[1], upper_bound[1]},\n              Affine{-1.0, 1.0, lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Compute quantities from variables for the two-index constraint\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, 3, Frame::Inertial>(lapse);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n\n  // Arbitrary choice for gamma2\n  const auto gamma2 = make_with_value<Scalar<DataVector>>(x, 4.0);\n\n  // Compute derivatives d_psi, d_phi, d_pi, and d_gauge_function numerically\n  Variables<VariablesTags> gh_vars(data_size);\n  auto& spacetime_metric = get<SpacetimeMetric>(gh_vars);\n  auto& pi = get<Pi>(gh_vars);\n  auto& phi = get<Phi>(gh_vars);\n  auto& gauge_function = get<GaugeH>(gh_vars);\n  spacetime_metric = gr::spacetime_metric(lapse, shift, spatial_metric);\n  phi =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n              dt_spatial_metric, phi);\n  gauge_function = gh::gauge_source(\n      lapse, dt_lapse, d_lapse, shift, dt_shift, d_shift, spatial_metric,\n      trace(gr::extrinsic_curvature(lapse, shift, d_shift, spatial_metric,\n                                    dt_spatial_metric, d_spatial_metric),\n            inverse_spatial_metric),\n      trace_last_indices(gr::christoffel_first_kind(d_spatial_metric),\n                         inverse_spatial_metric));\n\n  // Compute numerical derivatives of psi,pi,phi,H\n  const auto gh_derivs =\n      partial_derivatives<VariablesTags, VariablesTags, 3, Frame::Inertial>(\n          gh_vars, mesh, coord_map.inv_jacobian(x_logical));\n  const auto& d_spacetime_metric =\n      get<Tags::deriv<SpacetimeMetric, tmpl::size_t<3>, Frame::Inertial>>(\n          gh_derivs);\n  const auto& d_pi =\n      get<Tags::deriv<Pi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n  const auto& d_phi =\n      get<Tags::deriv<Phi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n  const auto& d_gauge_function =\n      get<Tags::deriv<GaugeH, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n\n  // Populate spacetime derivative of gauge function\n  const auto d4_gauge_function = [&x, &d_gauge_function]() {\n    auto tmp =\n        make_with_value<tnsr::ab<DataVector, 3, Frame::Inertial>>(x, 0.0);\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t i = 0; i < 3; ++i) {\n        tmp.get(i + 1, a) = d_gauge_function.get(i, a);\n      }\n      // Populate the time derivative with NaN as a check that those are not\n      // being used in calculating constraints\n      tmp.get(0, a) = std::numeric_limits<double>::signaling_NaN();\n    }\n    return tmp;\n  }();\n\n  // Compute the three-index constraint\n  const auto three_index_constraint =\n      gh::three_index_constraint(d_spacetime_metric, phi);\n\n  // Get the constraint, and check that it vanishes to error_tolerance\n  auto f_constraint = make_with_value<tnsr::a<DataVector, 3, Frame::Inertial>>(\n      x, std::numeric_limits<double>::signaling_NaN());\n  gh::f_constraint(make_not_null(&f_constraint), gauge_function,\n                   d4_gauge_function, normal_one_form, normal_vector,\n                   inverse_spatial_metric, inverse_spacetime_metric, pi, phi,\n                   d_pi, d_phi, gamma2, three_index_constraint);\n\n  const Approx numerical_approx =\n      Approx::custom().epsilon(error_tolerance).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(f_constraint,\n                               make_with_value<decltype(f_constraint)>(x, 0.0),\n                               numerical_approx);\n}\n\n// Test the return-by-value constraint energy function using random values\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_constraint_energy_random(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataType> (*)(\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::a<DataType, SpatialDim, Frame>&,\n          const tnsr::ia<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&, const Scalar<DataType>&,\n          double, double, double, double)>(\n          &gh::constraint_energy<DataType, SpatialDim, Frame>),\n      \"Constraints\", \"constraint_energy\", {{{-1.0, 1.0}}}, used_for_size);\n}\n\n// Test the return-by-reference constraint energy\n// by comparing to a time-independent analytic solution (e.g. Kerr-Schild)\ntemplate <typename Solution>\nvoid test_constraint_energy_analytic(const Solution& solution,\n                                     const size_t grid_size_each_dimension,\n                                     const std::array<double, 3>& lower_bound,\n                                     const std::array<double, 3>& upper_bound,\n                                     const double error_tolerance) {\n  // Shorter names for tags.\n  using SpacetimeMetric = gr::Tags::SpacetimeMetric<DataVector, 3>;\n  using Pi = ::gh::Tags::Pi<DataVector, 3>;\n  using Phi = ::gh::Tags::Phi<DataVector, 3>;\n  using GaugeH = ::gh::Tags::GaugeH<DataVector, 3>;\n  using VariablesTags = tmpl::list<SpacetimeMetric, Pi, Phi, GaugeH>;\n\n  // Check vs. time-independent analytic solution\n  // Set up grid\n  const size_t data_size = pow<3>(grid_size_each_dimension);\n  const Mesh<3> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1.0, 1.0, lower_bound[0], upper_bound[0]},\n              Affine{-1.0, 1.0, lower_bound[1], upper_bound[1]},\n              Affine{-1.0, 1.0, lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars);\n\n  const auto determinant_and_inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric);\n  const auto& determinant_spatial_metric =\n      determinant_and_inverse_spatial_metric.first;\n  const auto& inverse_spatial_metric =\n      determinant_and_inverse_spatial_metric.second;\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, 3, Frame::Inertial>(lapse);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n\n  // Compute derivative d_phi numerically\n  Variables<VariablesTags> gh_vars(data_size);\n  auto& spacetime_metric = get<SpacetimeMetric>(gh_vars);\n  auto& pi = get<Pi>(gh_vars);\n  auto& phi = get<Phi>(gh_vars);\n  auto& gauge_function = get<GaugeH>(gh_vars);\n  spacetime_metric = gr::spacetime_metric(lapse, shift, spatial_metric);\n  phi =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n              dt_spatial_metric, phi);\n  gauge_function = gh::gauge_source(\n      lapse, dt_lapse, d_lapse, shift, dt_shift, d_shift, spatial_metric,\n      trace(gr::extrinsic_curvature(lapse, shift, d_shift, spatial_metric,\n                                    dt_spatial_metric, d_spatial_metric),\n            inverse_spatial_metric),\n      trace_last_indices(gr::christoffel_first_kind(d_spatial_metric),\n                         inverse_spatial_metric));\n\n  // Compute numerical derivatives of psi,pi,phi,H\n  const auto gh_derivs =\n      partial_derivatives<VariablesTags, VariablesTags, 3, Frame::Inertial>(\n          gh_vars, mesh, coord_map.inv_jacobian(x_logical));\n  const auto& d_spacetime_metric =\n      get<Tags::deriv<SpacetimeMetric, tmpl::size_t<3>, Frame::Inertial>>(\n          gh_derivs);\n  const auto& d_pi =\n      get<Tags::deriv<Pi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n  const auto& d_phi =\n      get<Tags::deriv<Phi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n  const auto& d_gauge_function =\n      get<Tags::deriv<GaugeH, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n\n  // Populate spacetime derivative of gauge function\n  const auto d4_gauge_function = [&x, &d_gauge_function]() {\n    auto tmp =\n        make_with_value<tnsr::ab<DataVector, 3, Frame::Inertial>>(x, 0.0);\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t i = 0; i < 3; ++i) {\n        tmp.get(i + 1, a) = d_gauge_function.get(i, a);\n      }\n      // Populate the time derivative with NaN as a check that those are not\n      // being used in calculating constraints\n      tmp.get(0, a) = std::numeric_limits<double>::signaling_NaN();\n    }\n    return tmp;\n  }();\n\n  // Arbitrary choice for gamma2\n  const auto gamma2 = make_with_value<Scalar<DataVector>>(x, 4.0);\n\n  // Get the constraints\n  const auto gauge_constraint = gh::gauge_constraint(\n      gauge_function, normal_one_form, normal_vector, inverse_spatial_metric,\n      inverse_spacetime_metric, pi, phi);\n  const auto three_index_constraint =\n      gh::three_index_constraint(d_spacetime_metric, phi);\n  const auto four_index_constraint = gh::four_index_constraint(d_phi);\n  const auto two_index_constraint = gh::two_index_constraint(\n      d4_gauge_function, normal_one_form, normal_vector, inverse_spatial_metric,\n      inverse_spacetime_metric, pi, phi, d_pi, d_phi, gamma2,\n      three_index_constraint);\n  const auto f_constraint = gh::f_constraint(\n      gauge_function, d4_gauge_function, normal_one_form, normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi, d_pi, d_phi,\n      gamma2, three_index_constraint);\n\n  auto constraint_energy = make_with_value<Scalar<DataVector>>(\n      x, std::numeric_limits<double>::signaling_NaN());\n  gh::constraint_energy(\n      make_not_null(&constraint_energy), gauge_constraint, f_constraint,\n      two_index_constraint, three_index_constraint, four_index_constraint,\n      inverse_spatial_metric, determinant_spatial_metric, 2.0, -3.0, 4.0, -5.0);\n\n  const Approx numerical_approx =\n      Approx::custom().epsilon(error_tolerance).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      constraint_energy, make_with_value<decltype(constraint_energy)>(x, 0.0),\n      numerical_approx);\n}\n\n// Test the return-by-value normalized constraint energy function using random\n// values\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_constraint_energy_normalization_random(\n    const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataType> (*)(\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::ijaa<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&, const Scalar<DataType>&,\n          double)>(\n          &gh::constraint_energy_normalization<DataType, SpatialDim, Frame>),\n      \"Constraints\", \"constraint_energy_normalization\", {{{-1.0, 1.0}}},\n      used_for_size);\n}\n\n// Test the return-by-value normalized constraint energy function for Minkowski\nvoid test_constraint_energy_normalization_minkowski(\n    const DataVector& used_for_size) {\n  const auto d_spacetime_metric =\n      make_with_value<tnsr::iaa<DataVector, 3, Frame::Inertial>>(used_for_size,\n                                                                 0.0);\n  const auto d_pi = make_with_value<tnsr::iaa<DataVector, 3, Frame::Inertial>>(\n      used_for_size, 0.0);\n  const auto d_phi =\n      make_with_value<tnsr::ijaa<DataVector, 3, Frame::Inertial>>(used_for_size,\n                                                                  0.0);\n  auto inverse_spatial_metric =\n      make_with_value<tnsr::II<DataVector, 3, Frame::Inertial>>(used_for_size,\n                                                                0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    inverse_spatial_metric.get(i, i) = 1.0;\n  }\n  const auto sqrt_spatial_metric_determinant =\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0);\n  const double dimensional_constant = 3.0;\n\n  // type result = func(args...);\n  const Scalar<DataVector> result = gh::constraint_energy_normalization(\n      d_spacetime_metric, d_pi, d_phi, inverse_spatial_metric,\n      sqrt_spatial_metric_determinant, dimensional_constant);\n\n  CHECK(get(result) == 0.0);\n}\n\n// Test compute items for various constraints via insertion and retrieval\n// in a databox\ntemplate <typename Solution>\nvoid test_constraint_compute_items(const Solution& solution,\n                                   const size_t grid_size,\n                                   const std::array<double, 3>& lower_bound,\n                                   const std::array<double, 3>& upper_bound) {\n  // Check that compute items are named correctly\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ConstraintGamma0Compute<3, Frame::Inertial>>(\n      \"ConstraintGamma0\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ConstraintGamma1Compute<3, Frame::Inertial>>(\n      \"ConstraintGamma1\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ConstraintGamma2Compute<3, Frame::Inertial>>(\n      \"ConstraintGamma2\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::GaugeHImplicitFrom3p1QuantitiesCompute<3, Frame::Inertial>>(\n      \"GaugeH\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::SpacetimeDerivGaugeHCompute<3, Frame::Inertial>>(\n      \"SpacetimeDerivGaugeH\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::GaugeConstraintCompute<3, Frame::Inertial>>(\"GaugeConstraint\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::FConstraintCompute<3, Frame::Inertial>>(\"FConstraint\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::TwoIndexConstraintCompute<3, Frame::Inertial>>(\n      \"TwoIndexConstraint\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ThreeIndexConstraintCompute<3, Frame::Inertial>>(\n      \"ThreeIndexConstraint\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::FourIndexConstraintCompute<3, Frame::Inertial>>(\n      \"FourIndexConstraint\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ConstraintEnergyCompute<3, Frame::Inertial>>(\n      \"ConstraintEnergy\");\n\n  // Check vs. time-independent analytic solution\n  // Set up grid\n  const size_t data_size = pow<3>(grid_size);\n  const Mesh<3> mesh{grid_size, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{Affine{-1.0, 1.0, lower_bound[0], upper_bound[0]},\n                   Affine{-1.0, 1.0, lower_bound[1], upper_bound[1]},\n                   Affine{-1.0, 1.0, lower_bound[2], upper_bound[2]}});\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& time_deriv_lapse =\n      get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& deriv_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n  const auto& deriv_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& time_deriv_shift =\n      get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& time_deriv_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars);\n  const auto& deriv_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Compute quantities from variables for the two-index constraint\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, 3, Frame::Inertial>(lapse);\n  const auto det_and_inv = determinant_and_inverse(spatial_metric);\n  const auto& det_spatial_metric = det_and_inv.first;\n  const auto& inverse_spatial_metric = det_and_inv.second;\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n\n  const auto christoffel_first_kind =\n      gr::christoffel_first_kind(deriv_spatial_metric);\n  const auto trace_christoffel_first_kind =\n      trace_last_indices(christoffel_first_kind, inverse_spatial_metric);\n\n  // Compute derivatives d_spacetime_metric, d_phi, d_pi, and d_gauge_function\n  // numerically\n  // First, prepare\n  using VariablesTags =\n      tmpl::list<gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                 gh::Tags::GaugeH<DataVector, 3>>;\n\n  Variables<VariablesTags> gh_vars(data_size);\n  auto& pi = get<gh::Tags::Pi<DataVector, 3>>(gh_vars);\n  auto& phi = get<gh::Tags::Phi<DataVector, 3>>(gh_vars);\n  auto& gauge_source = get<gh::Tags::GaugeH<DataVector, 3>>(gh_vars);\n  phi = gh::phi(lapse, deriv_lapse, shift, deriv_shift, spatial_metric,\n                deriv_spatial_metric);\n  pi = gh::pi(lapse, time_deriv_lapse, shift, time_deriv_shift, spatial_metric,\n              time_deriv_spatial_metric, phi);\n  const auto extrinsic_curvature =\n      gh::extrinsic_curvature(spacetime_normal_vector, pi, phi);\n  const auto trace_extrinsic_curvature =\n      trace(extrinsic_curvature, inverse_spatial_metric);\n  gauge_source =\n      gh::gauge_source(lapse, time_deriv_lapse, deriv_lapse, shift,\n                       time_deriv_shift, deriv_shift, spatial_metric,\n                       trace_extrinsic_curvature, trace_christoffel_first_kind);\n  // Second, compute derivatives\n  const auto gh_derivs =\n      partial_derivatives<VariablesTags, VariablesTags, 3, Frame::Inertial>(\n          gh_vars, mesh, coord_map.inv_jacobian(x_logical));\n  const auto& deriv_pi =\n      get<Tags::deriv<gh::Tags::Pi<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(gh_derivs);\n  const auto& deriv_phi =\n      get<Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(gh_derivs);\n  const auto& deriv_gauge_source =\n      get<Tags::deriv<gh::Tags::GaugeH<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(gh_derivs);\n\n  // The spatial derivative of the spacetime metric is needed to evaluate\n  // the three-index constraint. Here, we choose random values for this tensor,\n  // so that the constraint is deliberately nonzero.\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_dist = make_not_null(&dist);\n  const auto& deriv_spacetime_metric =\n      make_with_random_values<tnsr::iaa<DataVector, 3, Frame::Inertial>>(\n          nn_generator, nn_dist, get<0, 0>(pi));\n\n  auto time_deriv_gauge_source =\n      make_with_value<tnsr::a<DataVector, 3, Frame::Inertial>>(x, 0.);\n  get<0>(time_deriv_gauge_source) = 0.05;\n  get<1>(time_deriv_gauge_source) = 0.06;\n  get<2>(time_deriv_gauge_source) = 0.07;\n  get<3>(time_deriv_gauge_source) = -0.05;\n\n  tnsr::ab<DataVector, 3, Frame::Inertial> spacetime_deriv_gauge_source{};\n  gh::Tags::SpacetimeDerivGaugeHCompute<3, Frame::Inertial>::function(\n      make_not_null(&spacetime_deriv_gauge_source), time_deriv_gauge_source,\n      deriv_gauge_source);\n\n  // Make DampingFunctions for the constraint damping parameters\n  // Note: these parameters are taken from SpEC single-black-hole simulations\n  constexpr double constant_02 = 0.001;\n  constexpr double amplitude_0 = 3.0;\n  constexpr double amplitude_2 = 1.0;\n  constexpr double constant_1 = -1.0;\n  constexpr double amplitude_1 = 0.0;\n  constexpr double width = 11.3137084989848;  // sqrt(128.0)\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n\n  std::unordered_map<std::string,  // NOLINT(misc-const-correctness)\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  // Insert into databox\n  const auto box = db::create<\n      db::AddSimpleTags<domain::Tags::FunctionsOfTimeInitialize, ::Tags::Time,\n                        domain::Tags::Coordinates<3, Frame::Inertial>,\n                        gr::Tags::SpatialMetric<DataVector, 3>,\n                        gr::Tags::Lapse<DataVector>,\n                        gr::Tags::Shift<DataVector, 3>,\n                        ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::deriv<gr::Tags::Shift<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>,\n                        ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n                        ::Tags::dt<gr::Tags::Shift<DataVector, 3>>,\n                        ::Tags::deriv<gh::Tags::GaugeH<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::dt<gh::Tags::GaugeH<DataVector, 3>>,\n                        ::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::deriv<gh::Tags::Phi<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::deriv<gh::Tags::Pi<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        gh::Tags::DampingFunctionGamma0<3, Frame::Inertial>,\n                        gh::Tags::DampingFunctionGamma1<3, Frame::Inertial>,\n                        gh::Tags::DampingFunctionGamma2<3, Frame::Inertial>>,\n      db::AddComputeTags<\n          gh::Tags::ConstraintGamma0Compute<3, Frame::Inertial>,\n          gh::Tags::ConstraintGamma1Compute<3, Frame::Inertial>,\n          gh::Tags::ConstraintGamma2Compute<3, Frame::Inertial>,\n          gr::Tags::SpacetimeNormalOneFormCompute<DataVector, 3,\n                                                  Frame::Inertial>,\n          gr::Tags::SpacetimeNormalVectorCompute<DataVector, 3,\n                                                 Frame::Inertial>,\n          gr::Tags::DetAndInverseSpatialMetricCompute<DataVector, 3,\n                                                      Frame::Inertial>,\n          gr::Tags::InverseSpacetimeMetricCompute<DataVector, 3,\n                                                  Frame::Inertial>,\n          gr::Tags::SpatialChristoffelFirstKindCompute<DataVector, 3,\n                                                       Frame::Inertial>,\n          gr::Tags::TraceSpatialChristoffelFirstKindCompute<DataVector, 3,\n                                                            Frame::Inertial>,\n          gh::Tags::PhiCompute<3, Frame::Inertial>,\n          gh::Tags::PiCompute<3, Frame::Inertial>,\n          gh::Tags::ExtrinsicCurvatureCompute<3, Frame::Inertial>,\n          gh::Tags::TraceExtrinsicCurvatureCompute<3, Frame::Inertial>,\n          gh::Tags::GaugeHImplicitFrom3p1QuantitiesCompute<3, Frame::Inertial>,\n          gh::Tags::SpacetimeDerivGaugeHCompute<3, Frame::Inertial>,\n          gh::Tags::FourIndexConstraintCompute<3, Frame::Inertial>,\n          gh::Tags::ThreeIndexConstraintCompute<3, Frame::Inertial>,\n          gh::Tags::TwoIndexConstraintCompute<3, Frame::Inertial>,\n          gh::Tags::GaugeConstraintCompute<3, Frame::Inertial>,\n          gh::Tags::FConstraintCompute<3, Frame::Inertial>,\n          gh::Tags::ConstraintEnergyCompute<3, Frame::Inertial>>>(\n      std::move(functions_of_time),\n      std::numeric_limits<double>::signaling_NaN(), x,\n\n      spatial_metric, lapse, shift, deriv_spatial_metric, deriv_lapse,\n      deriv_shift, time_deriv_spatial_metric, time_deriv_lapse,\n      time_deriv_shift, deriv_gauge_source, time_deriv_gauge_source,\n      deriv_spacetime_metric, deriv_phi, deriv_pi,\n      std::move(static_cast<std::unique_ptr<\n                    ConstraintDamping::DampingFunction<3, Frame::Inertial>>>(\n          std::make_unique<\n              ConstraintDamping::GaussianPlusConstant<3, Frame::Inertial>>(\n              constant_02, amplitude_0, width, center))),\n      std::move(static_cast<std::unique_ptr<\n                    ConstraintDamping::DampingFunction<3, Frame::Inertial>>>(\n          std::make_unique<\n              ConstraintDamping::GaussianPlusConstant<3, Frame::Inertial>>(\n              constant_1, amplitude_1, width, center))),\n      std::move(static_cast<std::unique_ptr<\n                    ConstraintDamping::DampingFunction<3, Frame::Inertial>>>(\n          std::make_unique<\n              ConstraintDamping::GaussianPlusConstant<3, Frame::Inertial>>(\n              constant_02, amplitude_2, width, center))));\n\n  // Compute tested quantities locally\n  std::unordered_map<std::string,  // NOLINT(misc-const-correctness)\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      empty_functions_of_time{};\n  Scalar<DataVector> gamma0{};\n  gh::Tags::ConstraintGamma0Compute<3, Frame::Inertial>::function(\n      make_not_null(&gamma0),\n      ConstraintDamping::GaussianPlusConstant<3, Frame::Inertial>{\n          constant_02, amplitude_0, width, center},\n      x, std::numeric_limits<double>::signaling_NaN(), empty_functions_of_time);\n  Scalar<DataVector> gamma1{};\n  gh::Tags::ConstraintGamma1Compute<3, Frame::Inertial>::function(\n      make_not_null(&gamma1),\n      ConstraintDamping::GaussianPlusConstant<3, Frame::Inertial>{\n          constant_1, amplitude_1, width, center},\n      x, std::numeric_limits<double>::signaling_NaN(), empty_functions_of_time);\n  Scalar<DataVector> gamma2{};\n  gh::Tags::ConstraintGamma2Compute<3, Frame::Inertial>::function(\n      make_not_null(&gamma2),\n      ConstraintDamping::GaussianPlusConstant<3, Frame::Inertial>{\n          constant_02, amplitude_2, width, center},\n      x, std::numeric_limits<double>::signaling_NaN(), empty_functions_of_time);\n\n  const auto four_index_constraint = gh::four_index_constraint(deriv_phi);\n  const auto three_index_constraint =\n      gh::three_index_constraint(deriv_spacetime_metric, phi);\n  const auto two_index_constraint = gh::two_index_constraint(\n      spacetime_deriv_gauge_source, spacetime_normal_one_form,\n      spacetime_normal_vector, inverse_spatial_metric, inverse_spacetime_metric,\n      pi, phi, deriv_pi, deriv_phi, gamma2, three_index_constraint);\n  const auto gauge_constraint = gh::gauge_constraint(\n      gauge_source, spacetime_normal_one_form, spacetime_normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi);\n  const auto f_constraint = gh::f_constraint(\n      gauge_source, spacetime_deriv_gauge_source, spacetime_normal_one_form,\n      spacetime_normal_vector, inverse_spatial_metric, inverse_spacetime_metric,\n      pi, phi, deriv_pi, deriv_phi, gamma2, three_index_constraint);\n  const auto constraint_energy = gh::constraint_energy(\n      gauge_constraint, f_constraint, two_index_constraint,\n      three_index_constraint, four_index_constraint, inverse_spatial_metric,\n      det_spatial_metric);\n\n  // Check that their compute items in databox furnish identical values\n  CHECK(db::get<gh::Tags::ConstraintGamma0>(box) == gamma0);\n  CHECK(db::get<gh::Tags::ConstraintGamma1>(box) == gamma1);\n  CHECK(db::get<gh::Tags::ConstraintGamma2>(box) == gamma2);\n  CHECK(db::get<gh::Tags::GaugeH<DataVector, 3>>(box) == gauge_source);\n  CHECK(db::get<gh::Tags::SpacetimeDerivGaugeH<DataVector, 3>>(box) ==\n        spacetime_deriv_gauge_source);\n  CHECK(db::get<gh::Tags::FourIndexConstraint<DataVector, 3>>(box) ==\n        four_index_constraint);\n  CHECK(db::get<gh::Tags::ThreeIndexConstraint<DataVector, 3>>(box) ==\n        three_index_constraint);\n  CHECK(db::get<gh::Tags::TwoIndexConstraint<DataVector, 3>>(box) ==\n        two_index_constraint);\n  CHECK(db::get<gh::Tags::GaugeConstraint<DataVector, 3>>(box) ==\n        gauge_constraint);\n  CHECK(db::get<gh::Tags::FConstraint<DataVector, 3>>(box) == f_constraint);\n  CHECK(db::get<gh::Tags::ConstraintEnergy<DataVector, 3>>(box) ==\n        constraint_energy);\n}\n\nvoid three_index_constraint() {\n  test_three_index_constraint<DataVector, 1, Frame::Grid>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_three_index_constraint<DataVector, 1, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_three_index_constraint<double, 1, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_three_index_constraint<double, 1, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_three_index_constraint<DataVector, 2, Frame::Grid>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_three_index_constraint<DataVector, 2, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_three_index_constraint<double, 2, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_three_index_constraint<double, 2, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_three_index_constraint<DataVector, 3, Frame::Grid>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_three_index_constraint<DataVector, 3, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_three_index_constraint<double, 3, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_three_index_constraint<double, 3, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n}\n\nvoid gauge_constraint() {\n  // Test the gauge constraint against Kerr Schild\n  const double mass = 1.4;\n  const std::array<double, 3> spin{{0.4, 0.3, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  const size_t grid_size = 3;\n  const std::array<double, 3> lower_bound{{0.82, 1.24, 1.32}};\n  const std::array<double, 3> upper_bound{{0.8, 1.22, 1.30}};\n\n  test_gauge_constraint_analytic(solution, grid_size, lower_bound, upper_bound);\n\n  // Test the gauge constraint with random numbers\n  test_gauge_constraint_random<DataVector, 1, Frame::Grid>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_gauge_constraint_random<DataVector, 1, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_gauge_constraint_random<double, 1, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_gauge_constraint_random<double, 1, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_gauge_constraint_random<DataVector, 2, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_gauge_constraint_random<double, 2, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_gauge_constraint_random<double, 2, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_gauge_constraint_random<DataVector, 3, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_gauge_constraint_random<double, 3, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_gauge_constraint_random<double, 3, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n}\n\nvoid two_index_constraint() {\n  // Test the two-index constraint against Kerr Schild\n  const double mass = 1.4;\n  const std::array<double, 3> dimensionless_spin{{0.4, 0.3, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, dimensionless_spin, center);\n\n  const size_t grid_size = 5;\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n\n  // Note: looser numerical tolerance because this check\n  // uses numerical derivatives\n  test_two_index_constraint_analytic(\n      solution, grid_size, lower_bound, upper_bound,\n      std::numeric_limits<double>::epsilon() * 1.e8);\n\n  // Test the two-index constraint with random numbers\n  test_two_index_constraint_random<DataVector, 1, Frame::Grid>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_two_index_constraint_random<DataVector, 1, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_two_index_constraint_random<double, 1, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_two_index_constraint_random<double, 1, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_two_index_constraint_random<DataVector, 2, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_two_index_constraint_random<double, 2, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_two_index_constraint_random<double, 2, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_two_index_constraint_random<DataVector, 3, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_two_index_constraint_random<double, 3, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_two_index_constraint_random<double, 3, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n}\n\nvoid four_index_constraint() {\n  // Test the four-index constraint against Kerr Schild\n  const double mass = 1.4;\n  const std::array<double, 3> dimensionless_spin{{0.4, 0.3, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, dimensionless_spin, center);\n\n  const size_t grid_size = 5;\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n\n  // Note: looser numerical tolerance because this check\n  // uses numerical derivatives\n  test_four_index_constraint_analytic(\n      solution, grid_size, lower_bound, upper_bound,\n      std::numeric_limits<double>::epsilon() * 1.e8);\n\n  // Test the four-index constraint with random numbers\n  test_four_index_constraint_random<DataVector, 3, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_four_index_constraint_random<double, 3, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_four_index_constraint_random<double, 3, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n}\n\nvoid f_constraint() {\n  // Test the F constraint against Kerr Schild\n  const double mass = 1.4;\n  const std::array<double, 3> dimensionless_spin{{0.4, 0.3, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, dimensionless_spin, center);\n\n  const size_t grid_size = 5;\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n\n  // Note: looser numerical tolerance because this check\n  // uses numerical derivatives\n  test_f_constraint_analytic(solution, grid_size, lower_bound, upper_bound,\n                             std::numeric_limits<double>::epsilon() * 1.e8);\n\n  // Test the F constraint with random numbers\n  test_f_constraint_random<DataVector, 1, Frame::Grid>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_f_constraint_random<DataVector, 1, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_f_constraint_random<double, 1, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_f_constraint_random<double, 1, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_f_constraint_random<DataVector, 2, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_f_constraint_random<double, 2, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_f_constraint_random<double, 2, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_f_constraint_random<DataVector, 3, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_f_constraint_random<double, 3, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_f_constraint_random<double, 3, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n}\n\nvoid constraint_energy() {\n  // Test the constraint energy against Kerr Schild\n  const double mass = 1.4;\n  const std::array<double, 3> spin{{0.4, 0.3, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  const size_t grid_size = 4;\n  const std::array<double, 3> lower_bound{{0.82, 1.24, 1.32}};\n  const std::array<double, 3> upper_bound{{0.8, 1.22, 1.30}};\n\n  test_constraint_energy_analytic(\n      solution, grid_size, lower_bound, upper_bound,\n      std::numeric_limits<double>::epsilon() * 1.e7);\n\n  // Test the constraint energy with random numbers\n  test_constraint_energy_random<DataVector, 1, Frame::Grid>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_constraint_energy_random<DataVector, 1, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_constraint_energy_random<double, 1, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_constraint_energy_random<double, 1, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_constraint_energy_random<DataVector, 2, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_constraint_energy_random<double, 2, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_constraint_energy_random<double, 2, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_constraint_energy_random<DataVector, 3, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_constraint_energy_random<double, 3, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_constraint_energy_random<double, 3, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n}\n\nvoid constraint_energy_normalization() {\n  // Test the normalized constraint energy in minkowski\n  test_constraint_energy_normalization_minkowski(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n\n  // Test the normalized constraint energy with random numbers\n  test_constraint_energy_normalization_random<DataVector, 1, Frame::Grid>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_constraint_energy_normalization_random<DataVector, 1, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_constraint_energy_normalization_random<double, 1, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_constraint_energy_normalization_random<double, 1, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_constraint_energy_normalization_random<DataVector, 2, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_constraint_energy_normalization_random<double, 2, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_constraint_energy_normalization_random<double, 2, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_constraint_energy_normalization_random<DataVector, 3, Frame::Inertial>(\n      DataVector(4, std::numeric_limits<double>::signaling_NaN()));\n  test_constraint_energy_normalization_random<double, 3, Frame::Grid>(\n      std::numeric_limits<double>::signaling_NaN());\n  test_constraint_energy_normalization_random<double, 3, Frame::Inertial>(\n      std::numeric_limits<double>::signaling_NaN());\n}\n\nvoid test_compute_tags() {\n  // Test the F constraint against Kerr Schild\n  const double mass = 1.4;\n  const std::array<double, 3> dimensionless_spin{{0.4, 0.3, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, dimensionless_spin, center);\n\n  const size_t grid_size = 3;\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n\n  test_constraint_compute_items(solution, grid_size, lower_bound, upper_bound);\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.Constraints\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GeneralizedHarmonic/\"};\n  three_index_constraint();\n  gauge_constraint();\n  two_index_constraint();\n  four_index_constraint();\n  f_constraint();\n  constraint_energy();\n  constraint_energy_normalization();\n  test_compute_tags();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_DuDt.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Identity.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/DuDtTempTags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n// Since repeatedly generating new numbers to compare to SpEC when the function\n// signature of the GH time derivative changes is difficult, we use a reference\n// implementation that we test against the Spectral Einstein Code (SpEC) to test\n// against our RHS changes.\ntemplate <size_t Dim>\nstd::tuple<tnsr::aa<DataVector, Dim, Frame::Inertial>,\n           tnsr::aa<DataVector, Dim, Frame::Inertial>,\n           tnsr::iaa<DataVector, Dim, Frame::Inertial>>\ngh_rhs_reference_impl(\n    const tnsr::aa<DataVector, Dim>& spacetime_metric,\n    const tnsr::aa<DataVector, Dim>& pi, const tnsr::iaa<DataVector, Dim>& phi,\n    const tnsr::iaa<DataVector, Dim>& d_spacetime_metric,\n    const tnsr::iaa<DataVector, Dim>& d_pi,\n    const tnsr::ijaa<DataVector, Dim>& d_phi, const Scalar<DataVector>& gamma0,\n    const Scalar<DataVector>& gamma1, const Scalar<DataVector>& gamma2,\n    const tnsr::a<DataVector, Dim>& gauge_function,\n    const tnsr::ab<DataVector, Dim>& spacetime_deriv_gauge_function,\n    const Scalar<DataVector>& lapse, const tnsr::I<DataVector, Dim>& shift,\n    const tnsr::II<DataVector, Dim>& inverse_spatial_metric,\n    const tnsr::AA<DataVector, Dim>& inverse_spacetime_metric,\n    const tnsr::a<DataVector, Dim>& trace_christoffel,\n    const tnsr::abb<DataVector, Dim>& christoffel_first_kind,\n    const tnsr::Abb<DataVector, Dim>& christoffel_second_kind,\n    const tnsr::A<DataVector, Dim>& normal_spacetime_vector,\n    const tnsr::a<DataVector, Dim>& normal_spacetime_one_form) {\n  const size_t n_pts = shift.begin()->size();\n\n  tnsr::aa<DataVector, Dim, Frame::Inertial> dt_spacetime_metric(n_pts);\n  tnsr::aa<DataVector, Dim, Frame::Inertial> dt_pi(n_pts);\n  tnsr::iaa<DataVector, Dim, Frame::Inertial> dt_phi(n_pts);\n\n  const DataVector gamma12 = gamma1.get() * gamma2.get();\n\n  tnsr::Iaa<DataVector, Dim> phi_1_up{DataVector(n_pts, 0.)};\n  for (size_t m = 0; m < Dim; ++m) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      for (size_t n = 0; n < Dim; ++n) {\n        for (size_t nu = mu; nu < Dim + 1; ++nu) {\n          phi_1_up.get(m, mu, nu) +=\n              inverse_spatial_metric.get(m, n) * phi.get(n, mu, nu);\n        }\n      }\n    }\n  }\n\n  tnsr::abC<DataVector, Dim> phi_3_up{DataVector(n_pts, 0.)};\n  for (size_t m = 0; m < Dim; ++m) {\n    for (size_t nu = 0; nu < Dim + 1; ++nu) {\n      for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n        for (size_t beta = 0; beta < Dim + 1; ++beta) {\n          phi_3_up.get(m, nu, alpha) +=\n              inverse_spacetime_metric.get(alpha, beta) * phi.get(m, nu, beta);\n        }\n      }\n    }\n  }\n\n  tnsr::aB<DataVector, Dim> pi_2_up{DataVector(n_pts, 0.)};\n  for (size_t nu = 0; nu < Dim + 1; ++nu) {\n    for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n      for (size_t beta = 0; beta < Dim + 1; ++beta) {\n        pi_2_up.get(nu, alpha) +=\n            inverse_spacetime_metric.get(alpha, beta) * pi.get(nu, beta);\n      }\n    }\n  }\n\n  tnsr::abC<DataVector, Dim> christoffel_first_kind_3_up{DataVector(n_pts, 0.)};\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    for (size_t nu = 0; nu < Dim + 1; ++nu) {\n      for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n        for (size_t beta = 0; beta < Dim + 1; ++beta) {\n          christoffel_first_kind_3_up.get(mu, nu, alpha) +=\n              inverse_spacetime_metric.get(alpha, beta) *\n              christoffel_first_kind.get(mu, nu, beta);\n        }\n      }\n    }\n  }\n\n  tnsr::a<DataVector, Dim> pi_dot_normal_spacetime_vector{\n      DataVector(n_pts, 0.)};\n  for (size_t nu = 0; nu < Dim + 1; ++nu) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      pi_dot_normal_spacetime_vector.get(mu) +=\n          normal_spacetime_vector.get(nu) * pi.get(nu, mu);\n    }\n  }\n\n  DataVector pi_contract_two_normal_spacetime_vectors{DataVector(n_pts, 0.)};\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    pi_contract_two_normal_spacetime_vectors +=\n        normal_spacetime_vector.get(mu) *\n        pi_dot_normal_spacetime_vector.get(mu);\n  }\n\n  tnsr::ia<DataVector, Dim> phi_dot_normal_spacetime_vector{\n      DataVector(n_pts, 0.)};\n  for (size_t n = 0; n < Dim; ++n) {\n    for (size_t nu = 0; nu < Dim + 1; ++nu) {\n      for (size_t mu = 0; mu < Dim + 1; ++mu) {\n        phi_dot_normal_spacetime_vector.get(n, nu) +=\n            normal_spacetime_vector.get(mu) * phi.get(n, mu, nu);\n      }\n    }\n  }\n\n  tnsr::a<DataVector, Dim> phi_contract_two_normal_spacetime_vectors{\n      DataVector(n_pts, 0.)};\n  for (size_t n = 0; n < Dim; ++n) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      phi_contract_two_normal_spacetime_vectors.get(n) +=\n          normal_spacetime_vector.get(mu) *\n          phi_dot_normal_spacetime_vector.get(n, mu);\n    }\n  }\n\n  tnsr::iaa<DataVector, Dim> three_index_constraint{DataVector(n_pts, 0.)};\n  for (size_t n = 0; n < Dim; ++n) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      for (size_t nu = mu; nu < Dim + 1; ++nu) {\n        three_index_constraint.get(n, mu, nu) =\n            d_spacetime_metric.get(n, mu, nu) - phi.get(n, mu, nu);\n      }\n    }\n  }\n\n  tnsr::a<DataVector, Dim> one_index_constraint{DataVector(n_pts, 0.)};\n  for (size_t nu = 0; nu < Dim + 1; ++nu) {\n    one_index_constraint.get(nu) =\n        gauge_function.get(nu) + trace_christoffel.get(nu);\n  }\n\n  DataVector normal_dot_one_index_constraint{DataVector(n_pts, 0.)};\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    normal_dot_one_index_constraint +=\n        normal_spacetime_vector.get(mu) * one_index_constraint.get(mu);\n  }\n\n  const DataVector gamma1p1 = 1.0 + gamma1.get();\n\n  tnsr::aa<DataVector, Dim> shift_dot_three_index_constraint{\n      DataVector(n_pts, 0.)};\n  for (size_t m = 0; m < Dim; ++m) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      for (size_t nu = mu; nu < Dim + 1; ++nu) {\n        shift_dot_three_index_constraint.get(mu, nu) +=\n            shift.get(m) * three_index_constraint.get(m, mu, nu);\n      }\n    }\n  }\n\n  // Here are the actual equations\n\n  // Equation for dt_spacetime_metric\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    for (size_t nu = mu; nu < Dim + 1; ++nu) {\n      dt_spacetime_metric.get(mu, nu) = -lapse.get() * pi.get(mu, nu);\n      dt_spacetime_metric.get(mu, nu) +=\n          gamma1p1 * shift_dot_three_index_constraint.get(mu, nu);\n      for (size_t m = 0; m < Dim; ++m) {\n        dt_spacetime_metric.get(mu, nu) += shift.get(m) * phi.get(m, mu, nu);\n      }\n    }\n  }\n\n  // Equation for dt_pi\n  for (size_t mu = 0; mu < Dim + 1; ++mu) {\n    for (size_t nu = mu; nu < Dim + 1; ++nu) {\n      dt_pi.get(mu, nu) =\n          -spacetime_deriv_gauge_function.get(mu, nu) -\n          spacetime_deriv_gauge_function.get(nu, mu) -\n          0.5 * pi_contract_two_normal_spacetime_vectors * pi.get(mu, nu) +\n          gamma0.get() * (normal_spacetime_one_form.get(mu) *\n                              one_index_constraint.get(nu) +\n                          normal_spacetime_one_form.get(nu) *\n                              one_index_constraint.get(mu)) -\n          gamma0.get() * spacetime_metric.get(mu, nu) *\n              normal_dot_one_index_constraint;\n\n      for (size_t delta = 0; delta < Dim + 1; ++delta) {\n        dt_pi.get(mu, nu) += 2 * christoffel_second_kind.get(delta, mu, nu) *\n                                 gauge_function.get(delta) -\n                             2 * pi.get(mu, delta) * pi_2_up.get(nu, delta);\n\n        for (size_t n = 0; n < Dim; ++n) {\n          dt_pi.get(mu, nu) +=\n              2 * phi_1_up.get(n, mu, delta) * phi_3_up.get(n, nu, delta);\n        }\n\n        for (size_t alpha = 0; alpha < Dim + 1; ++alpha) {\n          dt_pi.get(mu, nu) -=\n              2. * christoffel_first_kind_3_up.get(mu, alpha, delta) *\n              christoffel_first_kind_3_up.get(nu, delta, alpha);\n        }\n      }\n\n      for (size_t m = 0; m < Dim; ++m) {\n        dt_pi.get(mu, nu) -=\n            pi_dot_normal_spacetime_vector.get(m + 1) * phi_1_up.get(m, mu, nu);\n\n        for (size_t n = 0; n < Dim; ++n) {\n          dt_pi.get(mu, nu) -=\n              inverse_spatial_metric.get(m, n) * d_phi.get(m, n, mu, nu);\n        }\n      }\n\n      dt_pi.get(mu, nu) *= lapse.get();\n\n      dt_pi.get(mu, nu) +=\n          gamma12 * shift_dot_three_index_constraint.get(mu, nu);\n\n      for (size_t m = 0; m < Dim; ++m) {\n        // DualFrame term\n        dt_pi.get(mu, nu) += shift.get(m) * d_pi.get(m, mu, nu);\n      }\n    }\n  }\n\n  // Equation for dt_phi\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t mu = 0; mu < Dim + 1; ++mu) {\n      for (size_t nu = mu; nu < Dim + 1; ++nu) {\n        dt_phi.get(i, mu, nu) =\n            0.5 * pi.get(mu, nu) *\n                phi_contract_two_normal_spacetime_vectors.get(i) -\n            d_pi.get(i, mu, nu) +\n            gamma2.get() * three_index_constraint.get(i, mu, nu);\n        for (size_t n = 0; n < Dim; ++n) {\n          dt_phi.get(i, mu, nu) +=\n              phi_dot_normal_spacetime_vector.get(i, n + 1) *\n              phi_1_up.get(n, mu, nu);\n        }\n\n        dt_phi.get(i, mu, nu) *= lapse.get();\n        for (size_t m = 0; m < Dim; ++m) {\n          dt_phi.get(i, mu, nu) += shift.get(m) * d_phi.get(m, i, mu, nu);\n        }\n      }\n    }\n  }\n  return std::make_tuple(std::move(dt_spacetime_metric), std::move(dt_pi),\n                         std::move(dt_phi));\n}\n\n// Creates the random numbers that match what was used when comparing the GH RHS\n// to the Spectral Einstein Code (SpEC) implementation.\ntemplate <typename Tensor>\nTensor create_tensor_with_random_values(\n    const size_t n_pts, const gsl::not_null<std::mt19937*> gen) {\n  Tensor tensor(n_pts);\n  for (auto& data : tensor) {\n    for (size_t s = 0; s < data.size(); ++s) {\n      data[s] = std::uniform_real_distribution<>(-10, 10)(*gen);\n    }\n  }\n  return tensor;\n}\n\nvoid test_reference_impl_against_spec() {\n  const size_t dim = 3;\n  const size_t n_pts = 2;\n\n  // This test compares the output of the GeneralizedHarmonic RHS to the output\n  // computed using SpEC for the specific input tensors generated by this seed.\n  std::mt19937 gen(1.);\n\n  const auto psi = create_tensor_with_random_values<\n      tnsr::aa<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto pi = create_tensor_with_random_values<\n      tnsr::aa<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto phi = create_tensor_with_random_values<\n      tnsr::iaa<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto d_psi = create_tensor_with_random_values<\n      tnsr::iaa<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto d_pi = create_tensor_with_random_values<\n      tnsr::iaa<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto d_phi = create_tensor_with_random_values<\n      tnsr::ijaa<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto gauge_function = create_tensor_with_random_values<\n      tnsr::a<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto spacetime_deriv_gauge_function = create_tensor_with_random_values<\n      tnsr::ab<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto gamma0 = create_tensor_with_random_values<Scalar<DataVector>>(\n      n_pts, make_not_null(&gen));\n  const auto gamma1 = create_tensor_with_random_values<Scalar<DataVector>>(\n      n_pts, make_not_null(&gen));\n  const auto gamma2 = create_tensor_with_random_values<Scalar<DataVector>>(\n      n_pts, make_not_null(&gen));\n  const auto lapse = create_tensor_with_random_values<Scalar<DataVector>>(\n      n_pts, make_not_null(&gen));\n  const auto shift = create_tensor_with_random_values<\n      tnsr::I<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto inverse_spatial_metric = create_tensor_with_random_values<\n      tnsr::II<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto inverse_psi = create_tensor_with_random_values<\n      tnsr::AA<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto christoffel_first_kind = create_tensor_with_random_values<\n      tnsr::abb<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto christoffel_second_kind = create_tensor_with_random_values<\n      tnsr::Abb<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto trace_christoffel_first_kind = create_tensor_with_random_values<\n      tnsr::a<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto normal_one_form = create_tensor_with_random_values<\n      tnsr::a<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n  const auto normal_vector = create_tensor_with_random_values<\n      tnsr::A<DataVector, dim, Frame::Inertial>>(n_pts, make_not_null(&gen));\n\n  const auto [dt_psi, dt_pi, dt_phi] = gh_rhs_reference_impl(\n      psi, pi, phi, d_psi, d_pi, d_phi, gamma0, gamma1, gamma2, gauge_function,\n      spacetime_deriv_gauge_function, lapse, shift, inverse_spatial_metric,\n      inverse_psi, trace_christoffel_first_kind, christoffel_first_kind,\n      christoffel_second_kind, normal_vector, normal_one_form);\n\n  CHECK(dt_psi.get(0, 0)[0] == approx(-488.874963261792004));\n  CHECK(dt_psi.get(0, 0)[1] == approx(-81.510619345029468));\n  CHECK(dt_psi.get(0, 1)[0] == approx(-250.047822240104125));\n  CHECK(dt_psi.get(0, 1)[1] == approx(296.302836131049901));\n  CHECK(dt_psi.get(0, 2)[0] == approx(-500.997780531678018));\n  CHECK(dt_psi.get(0, 2)[1] == approx(466.785608622853886));\n  CHECK(dt_psi.get(0, 3)[0] == approx(467.671854125188190));\n  CHECK(dt_psi.get(0, 3)[1] == approx(495.703587199368314));\n  CHECK(dt_psi.get(1, 1)[0] == approx(-1455.466926116829427));\n  CHECK(dt_psi.get(1, 1)[1] == approx(537.232777225229825));\n  CHECK(dt_psi.get(1, 2)[0] == approx(605.305755751280685));\n  CHECK(dt_psi.get(1, 2)[1] == approx(-147.555027862059319));\n  CHECK(dt_psi.get(1, 3)[0] == approx(272.588366899036828));\n  CHECK(dt_psi.get(1, 3)[1] == approx(103.726287040730483));\n  CHECK(dt_psi.get(2, 2)[0] == approx(-698.404763643118372));\n  CHECK(dt_psi.get(2, 2)[1] == approx(-54.529887031214074));\n  CHECK(dt_psi.get(2, 3)[0] == approx(685.972755990463384));\n  CHECK(dt_psi.get(2, 3)[1] == approx(354.613491895308584));\n  CHECK(dt_psi.get(3, 3)[0] == approx(-245.215344706651052));\n  CHECK(dt_psi.get(3, 3)[1] == approx(-276.589507479549297));\n  CHECK(dt_pi.get(0, 0)[0] == approx(-1994.203229187604393));\n  CHECK(dt_pi.get(0, 0)[1] == approx(-525201.290706016356125));\n  CHECK(dt_pi.get(0, 1)[0] == approx(6311.905865276433360));\n  CHECK(dt_pi.get(0, 1)[1] == approx(-240190.350753725302638));\n  CHECK(dt_pi.get(0, 2)[0] == approx(-7434.327351519351396));\n  CHECK(dt_pi.get(0, 2)[1] == approx(-252008.364267619152088));\n  CHECK(dt_pi.get(0, 3)[0] == approx(2838.324638613014940));\n  CHECK(dt_pi.get(0, 3)[1] == approx(51236.973434082428867));\n  CHECK(dt_pi.get(1, 1)[0] == approx(-354.079220430901557));\n  CHECK(dt_pi.get(1, 1)[1] == approx(126094.565118705097120));\n  CHECK(dt_pi.get(1, 2)[0] == approx(-5619.191809738355005));\n  CHECK(dt_pi.get(1, 2)[1] == approx(-107407.264584976015612));\n  CHECK(dt_pi.get(1, 3)[0] == approx(897.475897012644054));\n  CHECK(dt_pi.get(1, 3)[1] == approx(89268.754206339028315));\n  CHECK(dt_pi.get(2, 2)[0] == approx(-21157.862553074402967));\n  CHECK(dt_pi.get(2, 2)[1] == approx(-94493.937608642052510));\n  CHECK(dt_pi.get(2, 3)[0] == approx(-6057.203556770715295));\n  CHECK(dt_pi.get(2, 3)[1] == approx(-153608.523542909941170));\n  CHECK(dt_pi.get(3, 3)[0] == approx(-17537.832989340076892));\n  CHECK(dt_pi.get(3, 3)[1] == approx(-60984.946935138112167));\n  CHECK(dt_phi.get(0, 0, 0)[0] == approx(334.313398747739541));\n  CHECK(dt_phi.get(0, 0, 0)[1] == approx(25514.306297616123629));\n  CHECK(dt_phi.get(0, 0, 1)[0] == approx(2077.905394153287034));\n  CHECK(dt_phi.get(0, 0, 1)[1] == approx(-23798.504669162444770));\n  CHECK(dt_phi.get(0, 0, 2)[0] == approx(-1284.875746578147073));\n  CHECK(dt_phi.get(0, 0, 2)[1] == approx(-19831.646993788559485));\n  CHECK(dt_phi.get(0, 0, 3)[0] == approx(233.700445801117098));\n  CHECK(dt_phi.get(0, 0, 3)[1] == approx(-34774.544349022733513));\n  CHECK(dt_phi.get(0, 1, 1)[0] == approx(885.722636195821224));\n  CHECK(dt_phi.get(0, 1, 1)[1] == approx(-33932.040869534444937));\n  CHECK(dt_phi.get(0, 1, 2)[0] == approx(-650.957577518966218));\n  CHECK(dt_phi.get(0, 1, 2)[1] == approx(2638.696086360518620));\n  CHECK(dt_phi.get(0, 1, 3)[0] == approx(31.485299569095631));\n  CHECK(dt_phi.get(0, 1, 3)[1] == approx(-3754.688091563506987));\n  // This value is only accurate to within 1e-13 when built on release with GCC\n  // 6.3\n  Approx coarser_approx = Approx::custom().epsilon(1e-13);\n  CHECK(dt_phi.get(0, 2, 2)[0] == coarser_approx(10.787542001317307));\n  CHECK(dt_phi.get(0, 2, 2)[1] == approx(6388.265784499862093));\n  CHECK(dt_phi.get(0, 2, 3)[0] == approx(-264.672203939199221));\n  CHECK(dt_phi.get(0, 2, 3)[1] == approx(-14106.605264503405124));\n  CHECK(dt_phi.get(0, 3, 3)[0] == approx(-186.705594181490596));\n  CHECK(dt_phi.get(0, 3, 3)[1] == approx(37619.063591638048820));\n  CHECK(dt_phi.get(1, 0, 0)[0] == approx(-562.600849624935677));\n  CHECK(dt_phi.get(1, 0, 0)[1] == approx(26636.175386835606332));\n  CHECK(dt_phi.get(1, 0, 1)[0] == approx(-7348.564617671276210));\n  CHECK(dt_phi.get(1, 0, 1)[1] == approx(-10239.582656574073553));\n  CHECK(dt_phi.get(1, 0, 2)[0] == approx(3735.515737741963676));\n  CHECK(dt_phi.get(1, 0, 2)[1] == approx(-9541.966102859829334));\n  CHECK(dt_phi.get(1, 0, 3)[0] == approx(-189.717363255539595));\n  CHECK(dt_phi.get(1, 0, 3)[1] == approx(-38768.276299298151571));\n  CHECK(dt_phi.get(1, 1, 1)[0] == approx(-3992.834118054164264));\n  CHECK(dt_phi.get(1, 1, 1)[1] == approx(-27052.090043482003239));\n  CHECK(dt_phi.get(1, 1, 2)[0] == approx(3861.313476781318514));\n  CHECK(dt_phi.get(1, 1, 2)[1] == approx(9909.233790204545585));\n  CHECK(dt_phi.get(1, 1, 3)[0] == approx(781.120763452604706));\n  CHECK(dt_phi.get(1, 1, 3)[1] == approx(32796.160467693429382));\n  CHECK(dt_phi.get(1, 2, 2)[0] == approx(-1182.382985415981693));\n  CHECK(dt_phi.get(1, 2, 2)[1] == approx(-19585.499004859902925));\n  CHECK(dt_phi.get(1, 2, 3)[0] == approx(2533.123508025803858));\n  CHECK(dt_phi.get(1, 2, 3)[1] == approx(39702.781747730077768));\n  CHECK(dt_phi.get(1, 3, 3)[0] == approx(1356.930486013502104));\n  CHECK(dt_phi.get(1, 3, 3)[1] == approx(18614.741669036127860));\n  CHECK(dt_phi.get(2, 0, 0)[0] == approx(-267.727784019130809));\n  CHECK(dt_phi.get(2, 0, 0)[1] == approx(-34312.754364113869087));\n  CHECK(dt_phi.get(2, 0, 1)[0] == approx(-3628.105306092249975));\n  CHECK(dt_phi.get(2, 0, 1)[1] == approx(26021.001208727309859));\n  CHECK(dt_phi.get(2, 0, 2)[0] == approx(1787.972861399919111));\n  CHECK(dt_phi.get(2, 0, 2)[1] == approx(20245.691906571166328));\n  CHECK(dt_phi.get(2, 0, 3)[0] == approx(-345.850954809957727));\n  CHECK(dt_phi.get(2, 0, 3)[1] == approx(45631.496531139637227));\n  CHECK(dt_phi.get(2, 1, 1)[0] == approx(-1936.213815715472037));\n  CHECK(dt_phi.get(2, 1, 1)[1] == approx(39876.740245914304978));\n  CHECK(dt_phi.get(2, 1, 2)[0] == approx(2047.144489678545369));\n  CHECK(dt_phi.get(2, 1, 2)[1] == approx(-6162.040420810454634));\n  CHECK(dt_phi.get(2, 1, 3)[0] == approx(490.866985829649764));\n  CHECK(dt_phi.get(2, 1, 3)[1] == approx(-5369.075417735416522));\n  CHECK(dt_phi.get(2, 2, 2)[0] == approx(-444.587399207923738));\n  CHECK(dt_phi.get(2, 2, 2)[1] == approx(1205.339390995000485));\n  CHECK(dt_phi.get(2, 2, 3)[0] == approx(1208.970864104782549));\n  CHECK(dt_phi.get(2, 2, 3)[1] == approx(1422.401000428901625));\n  CHECK(dt_phi.get(2, 3, 3)[0] == approx(1116.070338196526109));\n  CHECK(dt_phi.get(2, 3, 3)[1] == approx(-42638.998279054998420));\n}\n\ntemplate <size_t Dim, typename Generator>\nvoid test_compute_dudt(const gsl::not_null<Generator*> generator) {\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n  using gh_tags_list =\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                 gh::Tags::Pi<DataVector, Dim>, gh::Tags::Phi<DataVector, Dim>>;\n\n  const double time = 1.3;\n  const gh::gauges::DampedHarmonic gauge_condition{\n      100., std::array{1.2, 1.5, 1.7}, std::array{2, 4, 6}};\n  const size_t num_grid_points_1d = 3;\n  const Mesh<Dim> mesh(num_grid_points_1d, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const DataVector used_for_size(mesh.number_of_grid_points());\n\n  Variables<gh_tags_list> evolved_vars(mesh.number_of_grid_points());\n  fill_with_random_values(make_not_null(&evolved_vars), generator,\n                          make_not_null(&distribution));\n  // In order to satisfy the physical requirements on the spacetime metric we\n  // compute it from the helper functions that generate a physical lapse, shift,\n  // and spatial metric.\n  gr::spacetime_metric(\n      make_not_null(\n          &get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(evolved_vars)),\n      TestHelpers::gr::random_lapse(generator, used_for_size),\n      TestHelpers::gr::random_shift<Dim>(generator, used_for_size),\n      TestHelpers::gr::random_spatial_metric<Dim>(generator, used_for_size));\n\n  const auto logical_coords = logical_coordinates(mesh);\n  tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords{};\n  for (size_t i = 0; i < Dim; ++i) {\n    inertial_coords.get(i) = logical_coords.get(i);\n  }\n\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inv_jac{};\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      if (i == j) {\n        inv_jac.get(i, j) = DataVector(mesh.number_of_grid_points(), 1.0);\n      } else {\n        inv_jac.get(i, j) = DataVector(mesh.number_of_grid_points(), 0.0);\n      }\n    }\n  }\n\n  const auto partial_derivs =\n      partial_derivatives<gh_tags_list>(evolved_vars, mesh, inv_jac);\n\n  const auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(evolved_vars);\n  const auto& phi = get<gh::Tags::Phi<DataVector, Dim>>(evolved_vars);\n  const auto& pi = get<gh::Tags::Pi<DataVector, Dim>>(evolved_vars);\n  const auto& d_spacetime_metric =\n      get<Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, Dim>,\n                      tmpl::size_t<Dim>, Frame::Inertial>>(partial_derivs);\n  const auto& d_phi =\n      get<Tags::deriv<gh::Tags::Phi<DataVector, Dim>, tmpl::size_t<Dim>,\n                      Frame::Inertial>>(partial_derivs);\n  const auto& d_pi =\n      get<Tags::deriv<gh::Tags::Pi<DataVector, Dim>, tmpl::size_t<Dim>,\n                      Frame::Inertial>>(partial_derivs);\n\n  const auto gamma0 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&distribution), used_for_size);\n  const auto gamma1 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&distribution), used_for_size);\n  const auto gamma2 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&distribution), used_for_size);\n\n  // Quantities as input for reference RHS\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  const auto [det_spatial_metric, inverse_spatial_metric] =\n      determinant_and_inverse<gr::Tags::DetSpatialMetric<DataVector>,\n                              gr::Tags::InverseSpatialMetric<DataVector, Dim>>(\n          spatial_metric);\n  const auto shift = gr::shift(spacetime_metric, inverse_spatial_metric);\n  const auto lapse = gr::lapse(shift, spacetime_metric);\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  tnsr::abb<DataVector, Dim> da_spacetime_metric;\n  gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&da_spacetime_metric), lapse, shift, pi, phi);\n  const auto christoffel_first_kind =\n      gr::christoffel_first_kind(da_spacetime_metric);\n  const auto christoffel_second_kind = raise_or_lower_first_index(\n      christoffel_first_kind, inverse_spacetime_metric);\n  const auto trace_christoffel_first_kind =\n      trace_last_indices(christoffel_first_kind, inverse_spacetime_metric);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n  const auto normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, Dim, Frame::Inertial>(lapse);\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      sqrt(get(det_spatial_metric))};\n\n  Scalar<DataVector> half_pi_two_normals{get(lapse).size(), 0.0};\n  tnsr::i<DataVector, Dim, Frame::Inertial> half_phi_two_normals{\n      get(lapse).size(), 0.0};\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    get(half_pi_two_normals) +=\n        normal_vector.get(a) * normal_vector.get(a) * pi.get(a, a);\n    for (size_t i = 0; i < Dim; ++i) {\n      half_phi_two_normals.get(i) +=\n          0.5 * normal_vector.get(a) * normal_vector.get(a) * phi.get(i, a, a);\n    }\n    for (size_t b = a + 1; b < Dim + 1; ++b) {\n      get(half_pi_two_normals) +=\n          2.0 * normal_vector.get(a) * normal_vector.get(b) * pi.get(a, b);\n      for (size_t i = 0; i < Dim; ++i) {\n        half_phi_two_normals.get(i) +=\n            normal_vector.get(a) * normal_vector.get(b) * phi.get(i, a, b);\n      }\n    }\n  }\n  get(half_pi_two_normals) *= 0.5;\n\n  tnsr::a<DataVector, Dim> gauge_h{mesh.number_of_grid_points()};\n  tnsr::ab<DataVector, Dim> d4_gauge_h{mesh.number_of_grid_points()};\n\n  gh::gauges::dispatch<gh::Solutions::all_solutions<Dim>>(\n      make_not_null(&gauge_h), make_not_null(&d4_gauge_h), lapse, shift,\n      sqrt_det_spatial_metric, inverse_spatial_metric, da_spacetime_metric,\n      half_pi_two_normals, half_phi_two_normals, spacetime_metric, phi, mesh,\n      time, inertial_coords, inv_jac, gauge_condition);\n\n  const auto [expected_dt_spacetime_metric, expected_dt_pi, expected_dt_phi] =\n      gh_rhs_reference_impl(\n          spacetime_metric, pi, phi, d_spacetime_metric, d_pi, d_phi, gamma0,\n          gamma1, gamma2, gauge_h, d4_gauge_h, lapse, shift,\n          inverse_spatial_metric, inverse_spacetime_metric,\n          trace_christoffel_first_kind, christoffel_first_kind,\n          christoffel_second_kind, normal_vector, normal_one_form);\n\n  tnsr::aa<DataVector, Dim, Frame::Inertial> dt_spacetime_metric(\n      mesh.number_of_grid_points());\n  tnsr::aa<DataVector, Dim, Frame::Inertial> dt_pi(\n      mesh.number_of_grid_points());\n  tnsr::iaa<DataVector, Dim, Frame::Inertial> dt_phi(\n      mesh.number_of_grid_points());\n\n  Variables<tmpl::list<\n      gh::Tags::ConstraintGamma1, gh::Tags::ConstraintGamma2,\n      gh::Tags::GaugeH<DataVector, Dim>,\n      gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>, gh::Tags::Gamma1Gamma2,\n      gh::Tags::HalfPiTwoNormals, gh::Tags::NormalDotOneIndexConstraint,\n      gh::Tags::Gamma1Plus1, gh::Tags::PiOneNormal<Dim>,\n      gh::Tags::GaugeConstraint<DataVector, Dim>,\n      gh::Tags::HalfPhiTwoNormals<Dim>,\n      gh::Tags::ShiftDotThreeIndexConstraint<Dim>,\n      gh::Tags::MeshVelocityDotThreeIndexConstraint<Dim>,\n      gh::Tags::PhiOneNormal<Dim>, gh::Tags::PiSecondIndexUp<Dim>,\n      gh::Tags::ThreeIndexConstraint<DataVector, Dim>,\n      gh::Tags::PhiFirstIndexUp<Dim>, gh::Tags::PhiThirdIndexUp<Dim>,\n      gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<Dim>,\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, Dim>,\n      gr::Tags::InverseSpatialMetric<DataVector, Dim>,\n      gr::Tags::DetSpatialMetric<DataVector>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, Dim>,\n      gr::Tags::SpacetimeChristoffelFirstKind<DataVector, Dim>,\n      gr::Tags::SpacetimeChristoffelSecondKind<DataVector, Dim>,\n      gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, Dim>,\n      gr::Tags::SpacetimeNormalVector<DataVector, Dim>>>\n      buffer(mesh.number_of_grid_points());\n\n  gh::TimeDerivative<gh::Solutions::all_solutions<Dim>, Dim>::apply(\n      make_not_null(&dt_spacetime_metric), make_not_null(&dt_pi),\n      make_not_null(&dt_phi),\n      make_not_null(&get<gh::Tags::ConstraintGamma1>(buffer)),\n      make_not_null(&get<gh::Tags::ConstraintGamma2>(buffer)),\n      make_not_null(&get<gh::Tags::GaugeH<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::Gamma1Gamma2>(buffer)),\n      make_not_null(&get<gh::Tags::HalfPiTwoNormals>(buffer)),\n      make_not_null(&get<gh::Tags::NormalDotOneIndexConstraint>(buffer)),\n      make_not_null(&get<gh::Tags::Gamma1Plus1>(buffer)),\n      make_not_null(&get<gh::Tags::PiOneNormal<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::GaugeConstraint<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::HalfPhiTwoNormals<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::ShiftDotThreeIndexConstraint<Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::MeshVelocityDotThreeIndexConstraint<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiOneNormal<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PiSecondIndexUp<Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::ThreeIndexConstraint<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiFirstIndexUp<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiThirdIndexUp<Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<Dim>>(\n              buffer)),\n      make_not_null(&get<gr::Tags::Lapse<DataVector>>(buffer)),\n      make_not_null(&get<gr::Tags::Shift<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::InverseSpatialMetric<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gr::Tags::DetSpatialMetric<DataVector>>(buffer)),\n      make_not_null(&get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::InverseSpacetimeMetric<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelFirstKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelSecondKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeNormalVector<DataVector, Dim>>(buffer)),\n      d_spacetime_metric, d_pi, d_phi, spacetime_metric, pi, phi, gamma0,\n      gamma1, gamma2, gauge_condition, mesh, time, inertial_coords, inv_jac,\n      {});\n\n  CHECK_ITERABLE_APPROX(get<gh::Tags::ConstraintGamma1>(buffer), gamma1);\n  CHECK_ITERABLE_APPROX(get<gh::Tags::ConstraintGamma2>(buffer), gamma2);\n  CHECK_ITERABLE_APPROX((get<gh::Tags::GaugeH<DataVector, Dim>>(buffer)),\n                        gauge_h);\n  Approx custom_approx = Approx::custom().epsilon(1.e-10);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      (get<gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>>(buffer)),\n      d4_gauge_h, custom_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_dt_spacetime_metric,\n                               dt_spacetime_metric, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_dt_pi, dt_pi, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_dt_phi, dt_phi, custom_approx);\n\n  // Test the moving mesh damping terms:\n  // 1. Compute 3-index constraint from existing d_spacetime_metric, phi\n  // 2. Generate random mesh velocity\n  // 3. Compute dot product of shift with 3-index constraint and\n  //    mesh velocity with 3-index constraint\n  // 4. Recompute the time derivatives without moving mesh, using the value of\n  //    the dot product of the shift with the 3-index constraint computed above\n  // 5. Compute the same time derivatives as in step 4, except include the\n  //    moving mesh terms.\n  // 6. Compute the difference of the result from step 5\n  //    - the result from step 4.\n  // 7. Check that the differences are the expected mesh-velocity damping terms.\n  const tnsr::iaa<DataVector, Dim, Frame::Inertial>& three_index_con =\n      ::gh::three_index_constraint(d_spacetime_metric, phi);\n  tnsr::I<DataVector, Dim, Frame::Inertial> mesh_velocity =\n      TestHelpers::gr::random_shift<Dim>(generator, used_for_size);\n  auto shift_dot_three_index_constraint =\n      make_with_value<tnsr::aa<DataVector, Dim, Frame::Inertial>>(used_for_size,\n                                                                  0.0);\n  auto mesh_velocity_dot_three_index_constraint =\n      make_with_value<tnsr::aa<DataVector, Dim, Frame::Inertial>>(used_for_size,\n                                                                  0.0);\n\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = a; b < Dim + 1; ++b) {\n      for (size_t i = 0; i < Dim; ++i) {\n        shift_dot_three_index_constraint.get(a, b) +=\n            shift.get(i) * three_index_con.get(i, a, b);\n        mesh_velocity_dot_three_index_constraint.get(a, b) +=\n            mesh_velocity.get(i) * three_index_con.get(i, a, b);\n      }\n    }\n  }\n\n  gh::TimeDerivative<gh::Solutions::all_solutions<Dim>, Dim>::apply(\n      make_not_null(&dt_spacetime_metric), make_not_null(&dt_pi),\n      make_not_null(&dt_phi),\n      make_not_null(&get<gh::Tags::ConstraintGamma1>(buffer)),\n      make_not_null(&get<gh::Tags::ConstraintGamma2>(buffer)),\n      make_not_null(&get<gh::Tags::GaugeH<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::Gamma1Gamma2>(buffer)),\n      make_not_null(&get<gh::Tags::HalfPiTwoNormals>(buffer)),\n      make_not_null(&get<gh::Tags::NormalDotOneIndexConstraint>(buffer)),\n      make_not_null(&get<gh::Tags::Gamma1Plus1>(buffer)),\n      make_not_null(&get<gh::Tags::PiOneNormal<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::GaugeConstraint<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::HalfPhiTwoNormals<Dim>>(buffer)),\n      make_not_null(&shift_dot_three_index_constraint),\n      make_not_null(&mesh_velocity_dot_three_index_constraint),\n      make_not_null(&get<gh::Tags::PhiOneNormal<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PiSecondIndexUp<Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::ThreeIndexConstraint<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiFirstIndexUp<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiThirdIndexUp<Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<Dim>>(\n              buffer)),\n      make_not_null(&get<gr::Tags::Lapse<DataVector>>(buffer)),\n      make_not_null(&get<gr::Tags::Shift<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::InverseSpatialMetric<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gr::Tags::DetSpatialMetric<DataVector>>(buffer)),\n      make_not_null(&get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::InverseSpacetimeMetric<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelFirstKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelSecondKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeNormalVector<DataVector, Dim>>(buffer)),\n      d_spacetime_metric, d_pi, d_phi, spacetime_metric, pi, phi, gamma0,\n      gamma1, gamma2, gauge_condition, mesh, time, inertial_coords, inv_jac,\n      {});\n\n  tnsr::aa<DataVector, Dim, Frame::Inertial> dt_spacetime_metric_moving_mesh(\n      mesh.number_of_grid_points());\n  tnsr::aa<DataVector, Dim, Frame::Inertial> dt_pi_moving_mesh(\n      mesh.number_of_grid_points());\n  tnsr::iaa<DataVector, Dim, Frame::Inertial> dt_phi_moving_mesh(\n      mesh.number_of_grid_points());\n  gh::TimeDerivative<gh::Solutions::all_solutions<Dim>, Dim>::apply(\n      make_not_null(&dt_spacetime_metric_moving_mesh),\n      make_not_null(&dt_pi_moving_mesh), make_not_null(&dt_phi_moving_mesh),\n      make_not_null(&get<gh::Tags::ConstraintGamma1>(buffer)),\n      make_not_null(&get<gh::Tags::ConstraintGamma2>(buffer)),\n      make_not_null(&get<gh::Tags::GaugeH<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::Gamma1Gamma2>(buffer)),\n      make_not_null(&get<gh::Tags::HalfPiTwoNormals>(buffer)),\n      make_not_null(&get<gh::Tags::NormalDotOneIndexConstraint>(buffer)),\n      make_not_null(&get<gh::Tags::Gamma1Plus1>(buffer)),\n      make_not_null(&get<gh::Tags::PiOneNormal<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::GaugeConstraint<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::HalfPhiTwoNormals<Dim>>(buffer)),\n      make_not_null(&shift_dot_three_index_constraint),\n      make_not_null(&mesh_velocity_dot_three_index_constraint),\n      make_not_null(&get<gh::Tags::PhiOneNormal<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PiSecondIndexUp<Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::ThreeIndexConstraint<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiFirstIndexUp<Dim>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiThirdIndexUp<Dim>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<Dim>>(\n              buffer)),\n      make_not_null(&get<gr::Tags::Lapse<DataVector>>(buffer)),\n      make_not_null(&get<gr::Tags::Shift<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::InverseSpatialMetric<DataVector, Dim>>(buffer)),\n      make_not_null(&get<gr::Tags::DetSpatialMetric<DataVector>>(buffer)),\n      make_not_null(&get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::InverseSpacetimeMetric<DataVector, Dim>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelFirstKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelSecondKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, Dim>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeNormalVector<DataVector, Dim>>(buffer)),\n      d_spacetime_metric, d_pi, d_phi, spacetime_metric, pi, phi, gamma0,\n      gamma1, gamma2, gauge_condition, mesh, time, inertial_coords, inv_jac,\n      std::optional{mesh_velocity});\n\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = a; b < Dim + 1; ++b) {\n      dt_spacetime_metric_moving_mesh.get(a, b) -=\n          dt_spacetime_metric.get(a, b);\n      dt_pi_moving_mesh.get(a, b) -= dt_pi.get(a, b);\n      mesh_velocity_dot_three_index_constraint.get(a, b) *= get(gamma1);\n    }\n  }\n  Approx custom_approx_mesh_constraint =\n      Approx::custom().epsilon(1.e-9).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_spacetime_metric_moving_mesh,\n                               mesh_velocity_dot_three_index_constraint,\n                               custom_approx_mesh_constraint);\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = a; b < Dim + 1; ++b) {\n      mesh_velocity_dot_three_index_constraint.get(a, b) *= get(gamma2);\n    }\n  }\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_pi_moving_mesh,\n                               mesh_velocity_dot_three_index_constraint,\n                               custom_approx_mesh_constraint);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.DuDt\",\n                  \"[Unit][GeneralizedHarmonic]\") {\n  test_reference_impl_against_spec();\n\n  MAKE_GENERATOR(generator);\n  test_compute_dudt<1>(make_not_null(&generator));\n  test_compute_dudt<2>(make_not_null(&generator));\n  test_compute_dudt<3>(make_not_null(&generator));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_DuDtTempTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/Systems/GeneralizedHarmonic/DuDtTempTags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\ntemplate <size_t Dim>\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<gh::Tags::Gamma1Gamma2>(\"Gamma1Gamma2\");\n  TestHelpers::db::test_simple_tag<gh::Tags::HalfPiTwoNormals>(\n      \"HalfPiTwoNormals\");\n  TestHelpers::db::test_simple_tag<gh::Tags::NormalDotOneIndexConstraint>(\n      \"NormalDotOneIndexConstraint\");\n  TestHelpers::db::test_simple_tag<gh::Tags::Gamma1Plus1>(\"Gamma1Plus1\");\n  TestHelpers::db::test_simple_tag<gh::Tags::PiOneNormal<Dim>>(\"PiOneNormal\");\n  TestHelpers::db::test_simple_tag<gh::Tags::HalfPhiTwoNormals<Dim>>(\n      \"HalfPhiTwoNormals\");\n  TestHelpers::db::test_simple_tag<gh::Tags::ShiftDotThreeIndexConstraint<Dim>>(\n      \"ShiftDotThreeIndexConstraint\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::MeshVelocityDotThreeIndexConstraint<Dim>>(\n      \"MeshVelocityDotThreeIndexConstraint\");\n  TestHelpers::db::test_simple_tag<gh::Tags::PhiOneNormal<Dim>>(\"PhiOneNormal\");\n  TestHelpers::db::test_simple_tag<gh::Tags::PiSecondIndexUp<Dim>>(\n      \"PiSecondIndexUp\");\n  TestHelpers::db::test_simple_tag<gh::Tags::PhiFirstIndexUp<Dim>>(\n      \"PhiFirstIndexUp\");\n  TestHelpers::db::test_simple_tag<gh::Tags::PhiThirdIndexUp<Dim>>(\n      \"PhiThirdIndexUp\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<Dim>>(\n      \"SpacetimeChristoffelFirstKindThirdIndexUp\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.DuDtTempTags\",\n                  \"[Unit][Evolution]\") {\n  test_simple_tags<1>();\n  test_simple_tags<2>();\n  test_simple_tags<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Equations.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  constexpr size_t num_points = 5;\n  const tnsr::aa<DataVector, Dim> spacetime_metric{num_points};\n  tnsr::aa<DataVector, Dim> normal_dot_flux_spacetime_metric{num_points};\n  tnsr::aa<DataVector, Dim> normal_dot_flux_pi{num_points};\n  tnsr::iaa<DataVector, Dim> normal_dot_flux_phi{num_points};\n\n  gh::ComputeNormalDotFluxes<Dim>::apply(\n      make_not_null(&normal_dot_flux_spacetime_metric),\n      make_not_null(&normal_dot_flux_pi), make_not_null(&normal_dot_flux_phi),\n      spacetime_metric);\n\n  const DataVector zero{num_points, 0.};\n  for (size_t storage_index = 0;\n       storage_index < normal_dot_flux_spacetime_metric.size();\n       ++storage_index) {\n    CHECK(normal_dot_flux_spacetime_metric[storage_index] == zero);\n    CHECK(normal_dot_flux_pi[storage_index] == zero);\n  }\n\n  for (size_t storage_index = 0;\n       storage_index < normal_dot_flux_phi.size();\n       ++storage_index) {\n    CHECK(normal_dot_flux_phi[storage_index] == zero);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.NormalDotFluxes\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n\nnamespace {\nstruct ArbitraryFrame;\n}  // namespace\n\ntemplate <size_t Dim, typename Frame>\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<gh::Tags::Pi<DataVector, Dim, Frame>>(\"Pi\");\n  TestHelpers::db::test_simple_tag<gh::Tags::Phi<DataVector, Dim, Frame>>(\n      \"Phi\");\n  TestHelpers::db::test_simple_tag<gh::Tags::ConstraintGamma0>(\n      \"ConstraintGamma0\");\n  TestHelpers::db::test_simple_tag<gh::Tags::ConstraintGamma1>(\n      \"ConstraintGamma1\");\n  TestHelpers::db::test_simple_tag<gh::Tags::ConstraintGamma2>(\n      \"ConstraintGamma2\");\n  TestHelpers::db::test_simple_tag<gh::Tags::GaugeH<DataVector, Dim, Frame>>(\n      \"GaugeH\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::SpacetimeDerivGaugeH<DataVector, Dim, Frame>>(\n      \"SpacetimeDerivGaugeH\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::InitialGaugeH<DataVector, Dim, Frame>>(\"InitialGaugeH\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::SpacetimeDerivInitialGaugeH<DataVector, Dim, Frame>>(\n      \"SpacetimeDerivInitialGaugeH\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::VSpacetimeMetric<DataVector, Dim, Frame>>(\"VSpacetimeMetric\");\n  TestHelpers::db::test_simple_tag<gh::Tags::VZero<DataVector, Dim, Frame>>(\n      \"VZero\");\n  TestHelpers::db::test_simple_tag<gh::Tags::VPlus<DataVector, Dim, Frame>>(\n      \"VPlus\");\n  TestHelpers::db::test_simple_tag<gh::Tags::VMinus<DataVector, Dim, Frame>>(\n      \"VMinus\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::CharacteristicSpeeds<DataVector, Dim, Frame>>(\n      \"CharacteristicSpeeds\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::CharacteristicFields<DataVector, Dim, Frame>>(\n      \"CharacteristicFields\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::EvolvedFieldsFromCharacteristicFields<DataVector, Dim, Frame>>(\n      \"EvolvedFieldsFromCharacteristicFields\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::GaugeConstraint<DataVector, Dim, Frame>>(\"GaugeConstraint\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::FConstraint<DataVector, Dim, Frame>>(\"FConstraint\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::TwoIndexConstraint<DataVector, Dim, Frame>>(\n      \"TwoIndexConstraint\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::ThreeIndexConstraint<DataVector, Dim, Frame>>(\n      \"ThreeIndexConstraint\");\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::FourIndexConstraint<DataVector, Dim, Frame>>(\n      \"FourIndexConstraint\");\n\n  TestHelpers::db::test_simple_tag<\n      gh::Tags::ConstraintEnergy<DataVector, Dim, Frame>>(\"ConstraintEnergy\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GeneralizedHarmonic.Tags\",\n                  \"[Unit][Evolution]\") {\n  test_simple_tags<1, ArbitraryFrame>();\n  test_simple_tags<2, ArbitraryFrame>();\n  test_simple_tags<3, ArbitraryFrame>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GeneralizedHarmonic/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(GhValenciaDivClean)\nadd_subdirectory(ValenciaDivClean)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Actions/Test_SetInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Actions/SetInitialData.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace grmhd::GhValenciaDivClean::Actions {\nnamespace {\n\nusing TovStar = gh::Solutions::WrappedGr<RelativisticEuler::Solutions::TovStar>;\n\nusing gh_system_vars = gh::System<3>::variables_tag::tags_list;\nusing all_ghmhd_vars =\n    tmpl::append<hydro::grmhd_tags<DataVector>, gh_system_vars>;\n\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using component_being_mocked = void;  // Not needed\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using mutable_global_cache_tags =\n      tmpl::list<gh::Tags::SetPiAndPhiFromConstraints>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              tmpl::list<\n                  ::Tags::Variables<all_ghmhd_vars>,\n                  hydro::Tags::GrmhdEquationOfState, domain::Tags::Mesh<3>,\n                  domain::Tags::Coordinates<3, Frame::Inertial>,\n                  domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                                Frame::Inertial>,\n                  ::Tags::Time, evolution::dg::subcell::Tags::ActiveGrid,\n                  evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n                  evolution::dg::subcell::fd::Tags::\n                      InverseJacobianLogicalToInertial<3>>,\n              db::AddComputeTags<evolution::dg::subcell::Tags::MeshCompute<3>,\n                                 evolution::dg::subcell::Tags::\n                                     LogicalCoordinatesCompute<3>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<\n              grmhd::GhValenciaDivClean::Actions::SetInitialData,\n              grmhd::GhValenciaDivClean::Actions::ReceiveNumericInitialData>>>;\n};\n\nstruct MockReadVolumeData {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      DataBox& /*box*/, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const importers::ImporterOptions& options, const size_t volume_data_id,\n      tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n          importers::Tags::Selected, NumericInitialData::all_vars>>\n          selected_fields) {\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        get<evolution::initial_data::Tags::InitialData>(cache));\n    CHECK(options == initial_data.importer_options());\n    CHECK(volume_data_id == initial_data.volume_data_id());\n    CHECK(get<importers::Tags::Selected<\n              hydro::Tags::RestMassDensity<DataVector>>>(selected_fields) ==\n          \"CustomRho\");\n    CHECK(get<importers::Tags::Selected<\n              hydro::Tags::LowerSpatialFourVelocity<DataVector, 3>>>(\n              selected_fields) == \"CustomUi\");\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockVolumeDataReader {\n  using component_being_mocked = importers::ElementDataReader<Metavariables>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using replace_these_simple_actions =\n      tmpl::list<importers::Actions::ReadAllVolumeDataAndDistribute<\n          metavariables::volume_dim, NumericInitialData::all_vars,\n          MockElementArray<Metavariables>>>;\n  using with_these_simple_actions = tmpl::list<MockReadVolumeData>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  using component_list = tmpl::list<MockElementArray<Metavariables>,\n                                    MockVolumeDataReader<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<evolution::initial_data::InitialData,\n                             tmpl::list<NumericInitialData, TovStar>>>;\n  };\n};\n\nvoid test_set_initial_data(\n    const evolution::initial_data::InitialData& initial_data,\n    const std::string& option_string, const bool is_numeric,\n    const evolution::dg::subcell::ActiveGrid active_grid) {\n  {\n    INFO(\"Factory creation\");\n    const auto created = TestHelpers::test_creation<\n        std::unique_ptr<evolution::initial_data::InitialData>, Metavariables>(\n        option_string);\n    if (is_numeric) {\n      CHECK(dynamic_cast<const NumericInitialData&>(*created) ==\n            dynamic_cast<const NumericInitialData&>(initial_data));\n    } else {\n      CHECK(dynamic_cast<const TovStar&>(*created) ==\n            dynamic_cast<const TovStar&>(initial_data));\n    }\n  }\n\n  using reader_component = MockVolumeDataReader<Metavariables>;\n  using element_array = MockElementArray<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {initial_data.get_clone()}, {true}};\n\n  // We get test data from a TOV solution\n  TovStar tov_star{\n      1.e-3,\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100., 2.)};\n  const double star_radius = tov_star.radial_solution().outer_radius();\n  const auto eos = tov_star.equation_of_state().promote_to_3d_eos();\n\n  // Setup mock data file reader\n  ActionTesting::emplace_nodegroup_component<reader_component>(\n      make_not_null(&runner));\n\n  // Setup element\n  const ElementId<3> element_id{0, {{{1, 0}, {1, 0}, {1, 0}}}};\n  const Mesh<3> dg_mesh{8, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const size_t dg_num_points = dg_mesh.number_of_grid_points();\n  const size_t subcell_num_points = subcell_mesh.number_of_grid_points();\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Wedge<3>{\n              0.75 * star_radius, star_radius * 1.25, 1., 1.,\n              OrientationMap<3>::create_aligned(), true});\n  const auto dg_logical_coords = logical_coordinates(dg_mesh);\n  const auto dg_coords = map(dg_logical_coords);\n  const auto dg_inv_jacobian = map.inv_jacobian(dg_logical_coords);\n  const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n  const auto subcell_coords = map(subcell_logical_coords);\n  const auto subcell_inv_jacobian = map.inv_jacobian(subcell_logical_coords);\n  const bool subcell_active =\n      active_grid == evolution::dg::subcell::ActiveGrid::Subcell;\n  const auto& active_coords = subcell_active ? subcell_coords : dg_coords;\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), element_id,\n      {Variables<all_ghmhd_vars>{subcell_active ? subcell_num_points\n                                                : dg_num_points},\n       eos->get_clone(), dg_mesh, dg_coords, dg_inv_jacobian, 0., active_grid,\n       subcell_coords, subcell_inv_jacobian});\n  auto tov_vars = tov_star.variables(\n      active_coords, 0.,\n      tmpl::append<hydro::grmhd_tags<DataVector>, gh_system_vars,\n                   tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>>>{});\n\n  const auto get_element_tag = [&runner,\n                                &element_id](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const auto& cache = ActionTesting::cache<element_array>(runner, element_id);\n\n  // SetInitialData\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n\n  if (is_numeric) {\n    INFO(\"Numeric initial data\");\n    const auto& numeric_id =\n        dynamic_cast<const NumericInitialData&>(initial_data);\n\n    CHECK(Parallel::get<gh::Tags::SetPiAndPhiFromConstraints>(cache) ==\n          std::holds_alternative<gh::NumericInitialData::AdmVars>(\n              numeric_id.gh_numeric_id().selected_variables()));\n\n    REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n        make_not_null(&runner), element_id));\n\n    // MockReadVolumeData\n    ActionTesting::invoke_queued_simple_action<reader_component>(\n        make_not_null(&runner), 0);\n\n    // Insert TOV data into the inbox\n    auto& inbox = ActionTesting::get_inbox_tag<\n        element_array,\n        importers::Tags::VolumeData<NumericInitialData::all_vars>,\n        Metavariables>(make_not_null(&runner),\n                       element_id)[numeric_id.volume_data_id()];\n    const auto& gh_selected_vars =\n        numeric_id.gh_numeric_id().selected_variables();\n    if (std::holds_alternative<gh::NumericInitialData::GhVars>(\n            gh_selected_vars)) {\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(inbox) =\n          get<gr::Tags::SpacetimeMetric<DataVector, 3>>(tov_vars);\n      get<gh::Tags::Pi<DataVector, 3>>(inbox) =\n          get<gh::Tags::Pi<DataVector, 3>>(tov_vars);\n      get<gh::Tags::Phi<DataVector, 3>>(inbox) =\n          get<gh::Tags::Phi<DataVector, 3>>(tov_vars);\n    } else if (std::holds_alternative<gh::NumericInitialData::AdmVars>(\n                   gh_selected_vars)) {\n      const auto tov_adm_vars = tov_star.variables(\n          active_coords, 0.,\n          tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                     gr::Tags::Lapse<DataVector>,\n                     gr::Tags::Shift<DataVector, 3>,\n                     gr::Tags::ExtrinsicCurvature<DataVector, 3>>{});\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(inbox) =\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(tov_adm_vars);\n      get<gr::Tags::Lapse<DataVector>>(inbox) =\n          get<gr::Tags::Lapse<DataVector>>(tov_adm_vars);\n      get<gr::Tags::Shift<DataVector, 3>>(inbox) =\n          get<gr::Tags::Shift<DataVector, 3>>(tov_adm_vars);\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(inbox) =\n          get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(tov_adm_vars);\n    } else {\n      REQUIRE(false);\n    }\n    const auto& hydro_selected_vars =\n        numeric_id.hydro_numeric_id().selected_variables();\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(tov_vars);\n    get<hydro::Tags::RestMassDensity<DataVector>>(inbox) =\n        get<hydro::Tags::RestMassDensity<DataVector>>(tov_vars);\n    const auto& W = get<hydro::Tags::LorentzFactor<DataVector>>(tov_vars);\n    auto& u_i =\n        get<hydro::Tags::LowerSpatialFourVelocity<DataVector, 3>>(inbox);\n    u_i = raise_or_lower_index(\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(tov_vars),\n        spatial_metric);\n    for (size_t d = 0; d < 3; ++d) {\n      u_i.get(d) *= get(W);\n    }\n    get<hydro::Tags::ElectronFraction<DataVector>>(inbox) =\n        get<hydro::Tags::ElectronFraction<DataVector>>(tov_vars);\n    get<hydro::Tags::MagneticField<DataVector, 3>>(inbox) =\n        get<hydro::Tags::MagneticField<DataVector, 3>>(tov_vars);\n\n    // Override hydro variables if constant value is specified in options\n    const auto selected_electron_fraction =\n        get<ValenciaDivClean::NumericInitialData::VarName<\n            hydro::Tags::ElectronFraction<DataVector>,\n            std::bool_constant<false>>>(hydro_selected_vars);\n    if (std::holds_alternative<double>(selected_electron_fraction)) {\n      get<hydro::Tags::ElectronFraction<DataVector>>(tov_vars) =\n          make_with_value<Scalar<DataVector>>(\n              W, std::get<double>(selected_electron_fraction));\n    }\n    const auto selected_magnetic_field =\n        get<ValenciaDivClean::NumericInitialData::VarName<\n            hydro::Tags::MagneticField<DataVector, 3>,\n            std::bool_constant<false>>>(hydro_selected_vars);\n    if (std::holds_alternative<double>(selected_magnetic_field)) {\n      get<hydro::Tags::MagneticField<DataVector, 3>>(tov_vars) =\n          make_with_value<tnsr::I<DataVector, 3>>(\n              W, std::get<double>(selected_magnetic_field));\n    }\n\n    // ReceiveNumericInitialData\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              element_id);\n  } else {\n    CHECK(Parallel::get<gh::Tags::SetPiAndPhiFromConstraints>(cache));\n  }\n\n  // Check result. The GH variables are not particularly precise because we\n  // are taking numerical derivatives on a fairly coarse wedge-shaped grid.\n  Approx custom_approx = Approx::custom().epsilon(1.e-2).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get_element_tag(gr::Tags::SpacetimeMetric<DataVector, 3>{}),\n      (get<gr::Tags::SpacetimeMetric<DataVector, 3>>(tov_vars)), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get_element_tag(gh::Tags::Pi<DataVector, 3>{}),\n                               (get<gh::Tags::Pi<DataVector, 3>>(tov_vars)),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get_element_tag(gh::Tags::Phi<DataVector, 3>{}),\n                               (get<gh::Tags::Phi<DataVector, 3>>(tov_vars)),\n                               custom_approx);\n  tmpl::for_each<hydro::grmhd_tags<DataVector>>(\n      [&get_element_tag, &tov_vars](const auto tag_v) {\n        using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n        CAPTURE(pretty_type::name<tag>());\n        CHECK_ITERABLE_APPROX(get_element_tag(tag{}), (get<tag>(tov_vars)));\n      });\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GhValenciaDivClean.SetInitialData\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  EquationsOfState::register_derived_with_charm();\n  for (const auto active_grid : {evolution::dg::subcell::ActiveGrid::Dg,\n                                 evolution::dg::subcell::ActiveGrid::Subcell}) {\n    test_set_initial_data(\n        NumericInitialData{\n            \"TestInitialData.h5\",\n            \"VolumeData\",\n            0.,\n            {1.0e-9},\n            false,\n            gh::NumericInitialData::GhVars{\"CustomSpacetimeMetric\", \"CustomPi\",\n                                           \"CustomPhi\"},\n            {\"CustomRho\", \"CustomUi\", \"CustomYe\", \"CustomB\"},\n            1.e-14},\n        \"NumericInitialData:\\n\"\n        \"  FileGlob: TestInitialData.h5\\n\"\n        \"  Subgroup: VolumeData\\n\"\n        \"  ObservationValue: 0.\\n\"\n        \"  ObservationValueEpsilon: 1e-9\\n\"\n        \"  ElementsAreIdentical: False\\n\"\n        \"  GhVariables:\\n\"\n        \"    SpacetimeMetric: CustomSpacetimeMetric\\n\"\n        \"    Pi: CustomPi\\n\"\n        \"    Phi: CustomPhi\\n\"\n        \"  HydroVariables:\\n\"\n        \"    RestMassDensity: CustomRho\\n\"\n        \"    LowerSpatialFourVelocity: CustomUi\\n\"\n        \"    ElectronFraction: CustomYe\\n\"\n        \"    MagneticField: CustomB\\n\"\n        \"  DensityCutoff: 1.e-14\",\n        true, active_grid);\n    test_set_initial_data(\n        NumericInitialData{\"TestInitialData.h5\",\n                           \"VolumeData\",\n                           0.,\n                           std::nullopt,\n                           false,\n                           gh::NumericInitialData::AdmVars{\n                               \"CustomSpatialMetric\", \"CustomLapse\",\n                               \"CustomShift\", \"CustomExtrinsicCurvature\"},\n                           {\"CustomRho\", \"CustomUi\", 0.15, 0.},\n                           1.e-14},\n        \"NumericInitialData:\\n\"\n        \"  FileGlob: TestInitialData.h5\\n\"\n        \"  Subgroup: VolumeData\\n\"\n        \"  ObservationValue: 0.\\n\"\n        \"  ObservationValueEpsilon: Auto\\n\"\n        \"  ElementsAreIdentical: False\\n\"\n        \"  GhVariables:\\n\"\n        \"    SpatialMetric: CustomSpatialMetric\\n\"\n        \"    Lapse: CustomLapse\\n\"\n        \"    Shift: CustomShift\\n\"\n        \"    ExtrinsicCurvature: CustomExtrinsicCurvature\\n\"\n        \"  HydroVariables:\\n\"\n        \"    RestMassDensity: CustomRho\\n\"\n        \"    LowerSpatialFourVelocity: CustomUi\\n\"\n        \"    ElectronFraction: 0.15\\n\"\n        \"    MagneticField: 0.\\n\"\n        \"  DensityCutoff: 1.e-14\",\n        true, active_grid);\n    test_set_initial_data(\n        TovStar{1.e-3,\n                std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.,\n                                                                          2.)},\n        \"GeneralizedHarmonic(TovStar):\\n\"\n        \"  CentralDensity: 1.e-3\\n\"\n        \"  EquationOfState:\\n\"\n        \"    PolytropicFluid:\\n\"\n        \"      PolytropicConstant: 100.\\n\"\n        \"      PolytropicExponent: 2.\\n\"\n        \"  Coordinates: Schwarzschild\\n\",\n        false, active_grid);\n  }\n}\n\n}  // namespace\n}  // namespace grmhd::GhValenciaDivClean::Actions\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Test_ConstraintPreservingFreeOutflow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <random>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/ConstraintPreservingFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>, tmpl::list<>>,\n        tmpl::pair<\n            grmhd::GhValenciaDivClean::BoundaryConditions::BoundaryCondition,\n            tmpl::list<grmhd::GhValenciaDivClean::BoundaryConditions::\n                           ConstraintPreservingFreeOutflow>>>;\n  };\n};\n\ntemplate <typename System, typename U>\nvoid test_dg(const gsl::not_null<std::mt19937*> generator,\n             const U& boundary_condition) {\n  const double time = 1.3;\n  const size_t num_points = 5;\n\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  const gh::Solutions::WrappedGr<grmhd::Solutions::BondiMichel>\n      analytic_solution{1.0, 4.0, 0.1, 2.0, 0.01};\n\n  const auto interior_gamma1 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n  const auto interior_gamma2 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n\n  const auto coords =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          generator, make_not_null(&dist), num_points);\n\n  using Vars = Variables<tmpl::pop_back<\n      grmhd::GhValenciaDivClean::fd::tags_list_for_reconstruct_fd_neighbor>>;\n  using tags_not_set_by_boundary_condition =\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n  using PrimVars =\n      Variables<tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                           hydro::Tags::ElectronFraction<DataVector>,\n                           hydro::Tags::SpecificInternalEnergy<DataVector>,\n                           hydro::Tags::SpecificEnthalpy<DataVector>,\n                           hydro::Tags::Pressure<DataVector>,\n                           hydro::Tags::Temperature<DataVector>,\n                           hydro::Tags::SpatialVelocity<DataVector, 3>,\n                           hydro::Tags::LorentzFactor<DataVector>,\n                           hydro::Tags::MagneticField<DataVector, 3>>>;\n  Vars vars{num_points};\n  Vars expected_vars;\n  PrimVars prim_vars;\n\n  std::tie(expected_vars, prim_vars) = [&analytic_solution, &coords,\n                                        &interior_gamma1, &interior_gamma2,\n                                        time]() {\n    Vars expected{num_points};\n    auto& [spacetime_metric, pi, phi, tilde_d, tilde_ye, tilde_tau, tilde_s,\n           tilde_b, tilde_phi,\n\n           rest_mass_density, electron_fraction, specific_internal_energy,\n           spatial_velocity, magnetic_field, divergence_cleaning_field,\n           lorentz_factor, pressure, temperature,\n           lorentz_factor_times_spatial_velocity,\n\n           tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n           tilde_b_flux, tilde_phi_flux,\n\n           gamma1, gamma2, lapse, shift,\n\n           spatial_velocity_one_form, spatial_metric, sqrt_det_spatial_metric,\n           inverse_spatial_metric] = expected;\n\n    gamma1 = interior_gamma1;\n    gamma2 = interior_gamma2;\n\n    PrimVars local_prim_vars{num_points};\n\n    using tags = tmpl::list<\n        hydro::Tags::RestMassDensity<DataVector>,\n        hydro::Tags::ElectronFraction<DataVector>,\n        hydro::Tags::SpecificInternalEnergy<DataVector>,\n        hydro::Tags::SpecificEnthalpy<DataVector>,\n        hydro::Tags::Pressure<DataVector>, hydro::Tags::Temperature<DataVector>,\n        hydro::Tags::SpatialVelocity<DataVector, 3>,\n        hydro::Tags::LorentzFactor<DataVector>,\n        hydro::Tags::MagneticField<DataVector, 3>,\n        hydro::Tags::DivergenceCleaningField<DataVector>,\n        gr::Tags::SpatialMetric<DataVector, 3>,\n        gr::Tags::InverseSpatialMetric<DataVector, 3>,\n        gr::Tags::SqrtDetSpatialMetric<DataVector>, gr::Tags::Lapse<DataVector>,\n        gr::Tags::Shift<DataVector, 3>,\n        gr::Tags::SpacetimeMetric<DataVector, 3>, ::gh::Tags::Pi<DataVector, 3>,\n        ::gh::Tags::Phi<DataVector, 3>>;\n\n    tuples::tagged_tuple_from_typelist<tags> analytic_vars{};\n\n    analytic_vars = analytic_solution.variables(coords, time, tags{});\n    local_prim_vars.assign_subset(analytic_vars);\n    spacetime_metric =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(analytic_vars);\n    pi = get<::gh::Tags::Pi<DataVector, 3>>(analytic_vars);\n    phi = get<::gh::Tags::Phi<DataVector, 3>>(analytic_vars);\n    lapse = get<gr::Tags::Lapse<DataVector>>(analytic_vars);\n    shift = get<gr::Tags::Shift<DataVector, 3>>(analytic_vars);\n    inverse_spatial_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(analytic_vars);\n\n    grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n        make_not_null(&tilde_d), make_not_null(&tilde_ye),\n        make_not_null(&tilde_tau), make_not_null(&tilde_s),\n        make_not_null(&tilde_b), make_not_null(&tilde_phi),\n        get<hydro::Tags::RestMassDensity<DataVector>>(analytic_vars),\n        get<hydro::Tags::ElectronFraction<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(analytic_vars),\n        get<hydro::Tags::Pressure<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::LorentzFactor<DataVector>>(analytic_vars),\n        get<hydro::Tags::MagneticField<DataVector, 3>>(analytic_vars),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(analytic_vars),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(analytic_vars));\n\n    grmhd::ValenciaDivClean::ComputeFluxes::apply(\n        make_not_null(&tilde_d_flux), make_not_null(&tilde_ye_flux),\n        make_not_null(&tilde_tau_flux), make_not_null(&tilde_s_flux),\n        make_not_null(&tilde_b_flux), make_not_null(&tilde_phi_flux), tilde_d,\n        tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, lapse, shift,\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(analytic_vars),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(analytic_vars),\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::Pressure<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::LorentzFactor<DataVector>>(analytic_vars),\n        get<hydro::Tags::MagneticField<DataVector, 3>>(analytic_vars));\n\n    return std::tuple(expected, local_prim_vars);\n  }();\n\n  // Pick random direction normal covector, then normalize and compute normal\n  // vector.\n  tnsr::i<DataVector, 3> normal_covector{num_points};\n  get<0>(normal_covector) = 0.5;\n  get<1>(normal_covector) = 0.0;\n  get<2>(normal_covector) = 0.5;\n  const auto magnitude_normal = magnitude(\n      normal_covector,\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(expected_vars));\n  for (size_t i = 0; i < 3; ++i) {\n    normal_covector.get(i) /= get(magnitude_normal);\n  }\n  const auto normal_vector =\n      tenex::evaluate<ti::I>(normal_covector(ti::j) *\n                             get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                                 expected_vars)(ti::I, ti::J));\n\n  auto& [spacetime_metric, pi, phi, tilde_d, tilde_ye, tilde_tau, tilde_s,\n         tilde_b, tilde_phi,\n\n         rest_mass_density, electron_fraction, specific_internal_energy,\n         spatial_velocity, magnetic_field, divergence_cleaning_field,\n         lorentz_factor, pressure, temperature,\n         lorentz_factor_times_spatial_velocity,\n\n         tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n         tilde_b_flux, tilde_phi_flux, gamma1, gamma2, lapse, shift,\n\n         spatial_velocity_one_form, spatial_metric, sqrt_det_spatial_metric,\n\n         inverse_spatial_metric] = vars;\n\n  CHECK(\n      not boundary_condition\n              .dg_ghost(\n                  make_not_null(&spacetime_metric), make_not_null(&pi),\n                  make_not_null(&phi), make_not_null(&tilde_d),\n                  make_not_null(&tilde_ye), make_not_null(&tilde_tau),\n                  make_not_null(&tilde_s), make_not_null(&tilde_b),\n                  make_not_null(&tilde_phi), make_not_null(&tilde_d_flux),\n                  make_not_null(&tilde_ye_flux), make_not_null(&tilde_tau_flux),\n                  make_not_null(&tilde_s_flux), make_not_null(&tilde_b_flux),\n                  make_not_null(&tilde_phi_flux), make_not_null(&gamma1),\n                  make_not_null(&gamma2), make_not_null(&lapse),\n                  make_not_null(&shift),\n                  make_not_null(&spatial_velocity_one_form),\n                  make_not_null(&rest_mass_density),\n                  make_not_null(&electron_fraction),\n                  make_not_null(&temperature), make_not_null(&spatial_velocity),\n                  make_not_null(&inverse_spatial_metric), {}, normal_covector,\n                  normal_vector,\n\n                  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Pi<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Phi<DataVector, 3>>(expected_vars),\n\n                  get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n                  get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars),\n                  get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                      prim_vars),\n                  get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars),\n                  get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars),\n                  get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars),\n                  get<hydro::Tags::Pressure<DataVector>>(prim_vars),\n                  get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n\n                  coords, interior_gamma1, interior_gamma2,\n                  get<gr::Tags::Lapse<DataVector>>(expected_vars),\n                  get<gr::Tags::Shift<DataVector, 3>>(expected_vars),\n                  get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                      expected_vars),\n                  {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})\n              .has_value());\n\n  const auto inverse_spacetime_metric =\n      determinant_and_inverse(spacetime_metric).second;\n  const auto spacetime_normal_vector = gr::spacetime_normal_vector(\n      get<gr::Tags::Lapse<DataVector>>(expected_vars),\n      get<gr::Tags::Shift<DataVector, 3>>(expected_vars));\n  const auto gauge_source = make_with_random_values<tnsr::a<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto spacetime_deriv_gauge_source =\n      make_with_random_values<tnsr::ab<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n  const auto d_spacetime_metric =\n      make_with_random_values<tnsr::iaa<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n  const auto d_pi = make_with_random_values<tnsr::iaa<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto d_phi = make_with_random_values<tnsr::ijaa<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto three_index_constraint =\n      make_with_random_values<tnsr::iaa<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n  const auto logical_dt_spacetime_metric =\n      make_with_random_values<tnsr::aa<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n  const auto logical_dt_pi = make_with_random_values<tnsr::aa<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto logical_dt_phi = make_with_random_values<tnsr::iaa<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n\n  using DtVars = Variables<\n      db::wrap_tags_in<::Tags::dt, typename System::variables_tag::tags_list>>;\n  DtVars dt_vars{num_points};\n\n  CHECK(\n      not boundary_condition\n              .dg_time_derivative(\n                  make_not_null(\n                      &get<\n                          ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n                          dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<gh::Tags::Phi<DataVector, 3>>>(dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeD>>(\n                          dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeYe>>(\n                          dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeTau>>(\n                          dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeS<\n                          Frame::Inertial>>>(dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeB<\n                          Frame::Inertial>>>(dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildePhi>>(\n                          dt_vars)),\n\n                  {}, normal_covector, normal_vector,\n\n                  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Pi<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Phi<DataVector, 3>>(expected_vars),\n\n                  get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n                  get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars),\n                  get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                      prim_vars),\n                  get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars),\n                  get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars),\n                  get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars),\n                  get<hydro::Tags::Pressure<DataVector>>(prim_vars),\n                  get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n\n                  coords, interior_gamma1, interior_gamma2,\n                  get<gr::Tags::Lapse<DataVector>>(expected_vars),\n                  get<gr::Tags::Shift<DataVector, 3>>(expected_vars),\n                  get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                      expected_vars),\n                  inverse_spacetime_metric, spacetime_normal_vector,\n                  three_index_constraint, gauge_source,\n                  spacetime_deriv_gauge_source, logical_dt_spacetime_metric,\n                  logical_dt_pi, logical_dt_phi, d_spacetime_metric, d_pi,\n                  d_phi)\n              .has_value());\n\n  grmhd::ValenciaDivClean::BoundaryConditions::HydroFreeOutflow\n      grmhd_free_outflow{};\n\n  auto& [spacetime_metric_expected, pi_expected, phi_expected, tilde_d_expected,\n         tilde_ye_expected, tilde_tau_expected, tilde_s_expected,\n         tilde_b_expected, tilde_phi_expected,\n\n         rest_mass_density_expected, electron_fraction_expected,\n         specific_internal_energy_expected, spatial_velocity_expected,\n         magnetic_field_expected, divergence_cleaning_field_expected,\n         lorentz_factor_expected, pressure_expected, temperature_expected,\n         lorentz_factor_times_spatial_velocity_expected,\n\n         tilde_d_flux_expected, tilde_ye_flux_expected, tilde_tau_flux_expected,\n         tilde_s_flux_expected, tilde_b_flux_expected, tilde_phi_flux_expected,\n         gamma1_expected, gamma2_expected, lapse_expected, shift_expected,\n\n         spatial_velocity_one_form_expected, spatial_metric_expected,\n         sqrt_det_spatial_metric_expected, inverse_spatial_metric_expected] =\n      expected_vars;\n\n  CHECK(not grmhd_free_outflow\n                .dg_ghost(\n                    make_not_null(&tilde_d_expected),\n                    make_not_null(&tilde_ye_expected),\n                    make_not_null(&tilde_tau_expected),\n                    make_not_null(&tilde_s_expected),\n                    make_not_null(&tilde_b_expected),\n                    make_not_null(&tilde_phi_expected),\n                    make_not_null(&tilde_d_flux_expected),\n                    make_not_null(&tilde_ye_flux_expected),\n                    make_not_null(&tilde_tau_flux_expected),\n                    make_not_null(&tilde_s_flux_expected),\n                    make_not_null(&tilde_b_flux_expected),\n                    make_not_null(&tilde_phi_flux_expected),\n                    make_not_null(&lapse_expected),\n                    make_not_null(&shift_expected),\n                    make_not_null(&spatial_velocity_one_form_expected),\n                    make_not_null(&rest_mass_density_expected),\n                    make_not_null(&electron_fraction_expected),\n                    make_not_null(&temperature_expected),\n                    make_not_null(&spatial_velocity_expected),\n                    make_not_null(&inverse_spatial_metric_expected), {},\n                    normal_covector, normal_vector,\n\n                    get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n                    get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars),\n                    get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n                        prim_vars),\n                    get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars),\n                    get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars),\n                    get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars),\n                    get<hydro::Tags::Pressure<DataVector>>(prim_vars),\n                    get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n\n                    shift, lapse, inverse_spatial_metric)\n                .has_value());\n\n  tmpl::for_each<typename Vars::tags_list>([&expected_vars, &vars](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    if constexpr (not tmpl::list_contains_v<tags_not_set_by_boundary_condition,\n                                            tag>) {\n      CAPTURE(db::tag_name<tag>());\n      CHECK(get<tag>(vars) == get<tag>(expected_vars));\n    }\n  });\n\n  // Test constraint-preserving BC\n  DtVars expected_dt_vars{num_points, 0.0};\n  gh::BoundaryConditions::ConstraintPreservingBjorhus<3> gh_cp{\n      gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType::\n          ConstraintPreservingPhysical};\n  CHECK(\n      not gh_cp\n              .dg_time_derivative(\n                  make_not_null(\n                      &get<\n                          ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n                          expected_dt_vars)),\n                  make_not_null(&get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(\n                      expected_dt_vars)),\n                  make_not_null(&get<::Tags::dt<gh::Tags::Phi<DataVector, 3>>>(\n                      expected_dt_vars)),\n\n                  {}, normal_covector, normal_vector,\n\n                  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Pi<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Phi<DataVector, 3>>(expected_vars),\n\n                  coords, interior_gamma1, interior_gamma2,\n                  get<gr::Tags::Lapse<DataVector>>(expected_vars),\n                  get<gr::Tags::Shift<DataVector, 3>>(expected_vars),\n                  inverse_spacetime_metric, spacetime_normal_vector,\n                  three_index_constraint, gauge_source,\n                  spacetime_deriv_gauge_source, logical_dt_spacetime_metric,\n                  logical_dt_pi, logical_dt_phi, d_spacetime_metric, d_pi,\n                  d_phi)\n              .has_value());\n  tmpl::for_each<typename DtVars::tags_list>(\n      [&dt_vars, &expected_dt_vars](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CAPTURE(db::tag_name<tag>());\n        CHECK(get<tag>(dt_vars) == get<tag>(expected_dt_vars));\n      });\n}\n\ntemplate <typename System, typename U>\nvoid test_fd(const U& boundary_condition) {\n  const auto direction = Direction<3>::lower_xi();\n\n  const grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>\n      reconstructor{};\n\n  using Vars = Variables<grmhd::GhValenciaDivClean::Tags::\n                             primitive_grmhd_and_spacetime_reconstruction_tags>;\n  Vars vars{reconstructor.ghost_zone_size() * 5};\n  auto& [rest_mass_density, electron_fraction, pressure,\n         lorentz_factor_times_spatial_velocity, magnetic_field,\n         divergence_cleaning_field, spacetime_metric, pi, phi] = vars;\n\n  CHECK_THROWS_WITH(\n      boundary_condition.fd_ghost(\n          make_not_null(&spacetime_metric), make_not_null(&pi),\n          make_not_null(&phi), make_not_null(&rest_mass_density),\n          make_not_null(&electron_fraction), make_not_null(&pressure),\n          make_not_null(&lorentz_factor_times_spatial_velocity),\n          make_not_null(&magnetic_field),\n          make_not_null(&divergence_cleaning_field), direction),\n      Catch::Matchers::ContainsSubstring(\n          \"Not implemented because it's not trivial to figure out what the\"));\n}\n}  // namespace\n\n// clang-format off\nSPECTRE_TEST_CASE(\n  \"Unit.GhValenciaDivClean.BoundaryConditions.ConstraintPreservingFreeOutflow\",\n  \"[Unit][Evolution]\") {\n  // clang-format on\n  MAKE_GENERATOR(gen);\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto product_boundary_condition =\n      TestHelpers::test_creation<\n          std::unique_ptr<\n              grmhd::GhValenciaDivClean::BoundaryConditions::BoundaryCondition>,\n          Metavariables>(\n          \"ConstraintPreservingFreeOutflow:\\n\"\n          \"  Type: ConstraintPreservingPhysical\\n\"\n          \"  IncomingWaveProfile: None\\n\")\n          ->get_clone();\n\n  const auto serialized_and_deserialized_condition = serialize_and_deserialize(\n      *dynamic_cast<grmhd::GhValenciaDivClean::BoundaryConditions::\n                        ConstraintPreservingFreeOutflow*>(\n          product_boundary_condition.get()));\n\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n  test_dg<System, grmhd::GhValenciaDivClean::BoundaryConditions::\n                      ConstraintPreservingFreeOutflow>(\n      make_not_null(&gen), serialized_and_deserialized_condition);\n  test_fd<System, grmhd::GhValenciaDivClean::BoundaryConditions::\n                      ConstraintPreservingFreeOutflow>(\n      serialized_and_deserialized_condition);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename System>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            grmhd::GhValenciaDivClean::BoundaryConditions::BoundaryCondition,\n            tmpl::list<grmhd::GhValenciaDivClean::BoundaryConditions::\n                           DirichletAnalytic<System>>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   ghmhd::GhValenciaDivClean::InitialData::initial_data_list>,\n        tmpl::pair<\n            grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField,\n            tmpl::list<grmhd::AnalyticData::InitialMagneticFields::Poloidal>>>;\n  };\n};\n\ntemplate <typename T, typename U>\nvoid test_dg(const gsl::not_null<std::mt19937*> generator,\n             const U& boundary_condition, const T& analytic_solution_or_data) {\n  const double time = 1.3;\n  const size_t num_points = 5;\n\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  const auto interior_gamma1 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n  const auto interior_gamma2 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n\n  const auto coords =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          generator, make_not_null(&dist), num_points);\n\n  using Vars = Variables<tmpl::pop_back<\n      grmhd::GhValenciaDivClean::fd::tags_list_for_reconstruct_fd_neighbor>>;\n  using tags_not_set_by_boundary_condition =\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n  Vars vars{num_points};\n  const auto expected_vars = [&analytic_solution_or_data, &coords,\n                              &interior_gamma1, &interior_gamma2, time]() {\n    Vars expected{num_points};\n    auto& [spacetime_metric, pi, phi, tilde_d, tilde_ye, tilde_tau, tilde_s,\n           tilde_b, tilde_phi,\n\n           rest_mass_density, electron_fraction, specific_internal_energy,\n           spatial_velocity, magnetic_field, divergence_cleaning_field,\n           lorentz_factor, pressure, temperature,\n           lorentz_factor_times_spatial_velocity,\n\n           tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n           tilde_b_flux, tilde_phi_flux, gamma1, gamma2, lapse, shift,\n\n           spatial_velocity_one_form, spatial_metric, sqrt_det_spatial_metric,\n\n           inverse_spatial_metric] = expected;\n\n    gamma1 = interior_gamma1;\n    gamma2 = interior_gamma2;\n\n    using tags =\n        tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                   hydro::Tags::ElectronFraction<DataVector>,\n                   hydro::Tags::Temperature<DataVector>,\n                   hydro::Tags::SpecificInternalEnergy<DataVector>,\n                   hydro::Tags::SpecificEnthalpy<DataVector>,\n                   hydro::Tags::Pressure<DataVector>,\n                   hydro::Tags::SpatialVelocity<DataVector, 3>,\n                   hydro::Tags::LorentzFactor<DataVector>,\n                   hydro::Tags::MagneticField<DataVector, 3>,\n                   hydro::Tags::DivergenceCleaningField<DataVector>,\n                   gr::Tags::SpatialMetric<DataVector, 3>,\n                   gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                   gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                   gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                   gr::Tags::SpacetimeMetric<DataVector, 3>,\n                   ::gh::Tags::Pi<DataVector, 3>,\n                   ::gh::Tags::Phi<DataVector, 3>>;\n\n    tuples::tagged_tuple_from_typelist<tags> analytic_vars{};\n\n    if constexpr (::is_analytic_solution_v<T>) {\n      analytic_vars = analytic_solution_or_data.variables(coords, time, tags{});\n    } else {\n      (void)time;\n      analytic_vars = analytic_solution_or_data.variables(coords, tags{});\n    }\n    expected.assign_subset(analytic_vars);\n    tenex::evaluate<ti::i>(\n        make_not_null(&spatial_velocity_one_form),\n        spatial_velocity(ti::J) * spatial_metric(ti::i, ti::j));\n\n    grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n        make_not_null(&tilde_d), make_not_null(&tilde_ye),\n        make_not_null(&tilde_tau), make_not_null(&tilde_s),\n        make_not_null(&tilde_b), make_not_null(&tilde_phi),\n        get<hydro::Tags::RestMassDensity<DataVector>>(analytic_vars),\n        get<hydro::Tags::ElectronFraction<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(analytic_vars),\n        get<hydro::Tags::Pressure<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::LorentzFactor<DataVector>>(analytic_vars),\n        get<hydro::Tags::MagneticField<DataVector, 3>>(analytic_vars),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(analytic_vars),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(analytic_vars));\n\n    grmhd::ValenciaDivClean::ComputeFluxes::apply(\n        make_not_null(&tilde_d_flux), make_not_null(&tilde_ye_flux),\n        make_not_null(&tilde_tau_flux), make_not_null(&tilde_s_flux),\n        make_not_null(&tilde_b_flux), make_not_null(&tilde_phi_flux), tilde_d,\n        tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, lapse, shift,\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(analytic_vars),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(analytic_vars),\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::Pressure<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::LorentzFactor<DataVector>>(analytic_vars),\n        get<hydro::Tags::MagneticField<DataVector, 3>>(analytic_vars));\n\n    return expected;\n  }();\n  auto& [spacetime_metric, pi, phi, tilde_d, tilde_ye, tilde_tau, tilde_s,\n         tilde_b, tilde_phi,\n\n         rest_mass_density, electron_fraction, specific_internal_energy,\n         spatial_velocity, magnetic_field, divergence_cleaning_field,\n         lorentz_factor, pressure, temperature,\n         lorentz_factor_times_spatial_velocity,\n\n         tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n         tilde_b_flux, tilde_phi_flux, gamma1, gamma2, lapse, shift,\n\n         spatial_velocity_one_form, spatial_metric, sqrt_det_spatial_metric,\n\n         inverse_spatial_metric] = vars;\n\n  const auto result = boundary_condition.dg_ghost(\n      make_not_null(&spacetime_metric), make_not_null(&pi), make_not_null(&phi),\n      make_not_null(&tilde_d), make_not_null(&tilde_ye),\n      make_not_null(&tilde_tau), make_not_null(&tilde_s),\n      make_not_null(&tilde_b), make_not_null(&tilde_phi),\n      make_not_null(&tilde_d_flux), make_not_null(&tilde_ye_flux),\n      make_not_null(&tilde_tau_flux), make_not_null(&tilde_s_flux),\n      make_not_null(&tilde_b_flux), make_not_null(&tilde_phi_flux),\n      make_not_null(&gamma1), make_not_null(&gamma2), make_not_null(&lapse),\n      make_not_null(&shift), make_not_null(&spatial_velocity_one_form),\n      make_not_null(&rest_mass_density), make_not_null(&electron_fraction),\n      make_not_null(&temperature), make_not_null(&spatial_velocity),\n      make_not_null(&inverse_spatial_metric), {}, {}, {}, coords,\n      interior_gamma1, interior_gamma2, time);\n  CHECK(not result.has_value());\n\n  tmpl::for_each<typename Vars::tags_list>([&expected_vars, &vars](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    if constexpr (not tmpl::list_contains_v<tags_not_set_by_boundary_condition,\n                                            tag>) {\n      CAPTURE(db::tag_name<tag>());\n      CHECK(get<tag>(vars) == get<tag>(expected_vars));\n    }\n  });\n}\n\ntemplate <typename System, typename T, typename U>\nvoid test_fd(const U& boundary_condition, const T& analytic_solution_or_data) {\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  const double time = 1.3;\n  const Mesh<3> subcell_mesh{9, Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  const std::array<double, 3> lower_bound{{0.78, 1.18, 1.28}};\n  const std::array<double, 3> upper_bound{{0.82, 1.22, 1.32}};\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                   Affine{-1., 1., lower_bound[2], upper_bound[2]}});\n  const ElementId<3> element_id{0};\n  const ElementMap logical_to_grid_map{\n      element_id,\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          Affine3D{Affine{-1., 1., 2.0 * lower_bound[0], 2.0 * upper_bound[0]},\n                   Affine{-1., 1., 2.0 * lower_bound[1], 2.0 * upper_bound[1]},\n                   Affine{-1., 1., 2.0 * lower_bound[2], 2.0 * upper_bound[2]}})\n          .get_clone()};\n  const auto direction = Direction<3>::lower_xi();\n\n  grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>\n      reconstructor{};\n\n  using Vars = Variables<grmhd::GhValenciaDivClean::Tags::\n                             primitive_grmhd_and_spacetime_reconstruction_tags>;\n  Vars vars{reconstructor.ghost_zone_size() *\n            subcell_mesh.extents().slice_away(0).product()};\n  const auto expected_vars = [&analytic_solution_or_data, &direction,\n                              &functions_of_time, &grid_to_inertial_map,\n                              &logical_to_grid_map, &reconstructor,\n                              &subcell_mesh, time]() {\n    const auto ghost_logical_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, reconstructor.ghost_zone_size(), direction);\n\n    const auto ghost_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n    using tags = tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                            hydro::Tags::ElectronFraction<DataVector>,\n                            hydro::Tags::Temperature<DataVector>,\n                            hydro::Tags::SpatialVelocity<DataVector, 3>,\n                            hydro::Tags::LorentzFactor<DataVector>,\n                            hydro::Tags::MagneticField<DataVector, 3>,\n                            hydro::Tags::DivergenceCleaningField<DataVector>,\n                            gr::Tags::SpacetimeMetric<DataVector, 3>,\n                            ::gh::Tags::Pi<DataVector, 3>,\n                            ::gh::Tags::Phi<DataVector, 3>>;\n\n    tuples::tagged_tuple_from_typelist<tags> analytic_vars{};\n\n    if constexpr (::is_analytic_solution_v<T>) {\n      analytic_vars = analytic_solution_or_data.variables(ghost_inertial_coords,\n                                                          time, tags{});\n    } else {\n      (void)time;\n      analytic_vars =\n          analytic_solution_or_data.variables(ghost_inertial_coords, tags{});\n    }\n\n    Vars expected{get<0>(ghost_inertial_coords).size()};\n\n    get<gr::Tags::SpacetimeMetric<DataVector, 3>>(expected) =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(analytic_vars);\n    get<::gh::Tags::Pi<DataVector, 3>>(expected) =\n        get<::gh::Tags::Pi<DataVector, 3>>(analytic_vars);\n    get<::gh::Tags::Phi<DataVector, 3>>(expected) =\n        get<::gh::Tags::Phi<DataVector, 3>>(analytic_vars);\n    get<hydro::Tags::RestMassDensity<DataVector>>(expected) =\n        get<hydro::Tags::RestMassDensity<DataVector>>(analytic_vars);\n    get<hydro::Tags::ElectronFraction<DataVector>>(expected) =\n        get<hydro::Tags::ElectronFraction<DataVector>>(analytic_vars);\n    get<hydro::Tags::Temperature<DataVector>>(expected) =\n        get<hydro::Tags::Temperature<DataVector>>(analytic_vars);\n\n    for (size_t i = 0; i < 3; ++i) {\n      auto& lorentz_factor =\n          get<hydro::Tags::LorentzFactor<DataVector>>(analytic_vars);\n      auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(analytic_vars);\n      get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n          expected)\n          .get(i) = get(lorentz_factor) * spatial_velocity.get(i);\n    }\n\n    get<hydro::Tags::MagneticField<DataVector, 3>>(expected) =\n        get<hydro::Tags::MagneticField<DataVector, 3>>(analytic_vars);\n    get<hydro::Tags::DivergenceCleaningField<DataVector>>(expected) =\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(analytic_vars);\n    return expected;\n  }();\n  auto& [rest_mass_density, electron_fraction, temperature,\n         lorentz_factor_times_spatial_velocity, magnetic_field,\n         divergence_cleaning_field, spacetime_metric, pi, phi] = vars;\n\n  boundary_condition.fd_ghost(\n      make_not_null(&spacetime_metric), make_not_null(&pi), make_not_null(&phi),\n      make_not_null(&rest_mass_density), make_not_null(&electron_fraction),\n      make_not_null(&temperature),\n      make_not_null(&lorentz_factor_times_spatial_velocity),\n      make_not_null(&magnetic_field), make_not_null(&divergence_cleaning_field),\n      direction, subcell_mesh, time, functions_of_time, logical_to_grid_map,\n      grid_to_inertial_map, reconstructor);\n  // failing line\n  CHECK(vars == expected_vars);\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.GhValenciaDivClean.BoundaryConditions.DirichletAnalytic\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n  register_factory_classes_with_charm<Metavariables<System>>();\n  EquationsOfState::register_derived_with_charm();\n  {\n    INFO(\"Test with analytic solution\");\n    const auto product_boundary_condition =\n        TestHelpers::test_creation<\n            std::unique_ptr<grmhd::GhValenciaDivClean::BoundaryConditions::\n                                BoundaryCondition>,\n            Metavariables<System>>(\n            \"DirichletAnalytic:\\n\"\n            \"  AnalyticPrescription:\\n\"\n            \"      GeneralizedHarmonic(BondiMichel):\\n\"\n            \"        Mass: 1.0\\n\"\n            \"        SonicRadius: 4.0\\n\"\n            \"        SonicDensity: 0.1\\n\"\n            \"        PolytropicExponent: 2.0\\n\"\n            \"        MagFieldStrength: 0.01\\n\")\n            ->get_clone();\n\n    const gh::Solutions::WrappedGr<grmhd::Solutions::BondiMichel>\n        analytic_solution_or_data{1.0, 4.0, 0.1, 2.0, 0.01};\n    const auto serialized_and_deserialized_condition =\n        serialize_and_deserialize(\n            *dynamic_cast<grmhd::GhValenciaDivClean::BoundaryConditions::\n                              DirichletAnalytic<System>*>(\n                product_boundary_condition.get()));\n\n    test_dg(make_not_null(&gen), serialized_and_deserialized_condition,\n            analytic_solution_or_data);\n    test_fd<System,\n            gh::Solutions::WrappedGr<grmhd::Solutions::BondiMichel>,\n            grmhd::GhValenciaDivClean::BoundaryConditions::DirichletAnalytic<\n            System>>(serialized_and_deserialized_condition,\n                                          analytic_solution_or_data);\n  }\n  {\n    INFO(\"Test with analytic data\");\n    const auto product_boundary_condition =\n        TestHelpers::test_creation<\n            std::unique_ptr<grmhd::GhValenciaDivClean::BoundaryConditions::\n                                BoundaryCondition>,\n            Metavariables<System>>(\n            \"DirichletAnalytic:\\n\"\n            \"  AnalyticPrescription:\\n\"\n            \"      GeneralizedHarmonic(MagnetizedTovStar):\\n\"\n            \"        CentralDensity: 1.28e-3\\n\"\n            \"        EquationOfState:\\n\"\n            \"          PolytropicFluid:\\n\"\n            \"            PolytropicConstant: 100.0\\n\"\n            \"            PolytropicExponent: 2.0\\n\"\n            \"        Coordinates: Schwarzschild\\n\"\n            \"        MagneticFields: \\n\"\n            \"          - Poloidal:\\n\"\n            \"              PressureExponent: 2\\n\"\n            \"              CutoffPressure: 0.04\\n\"\n            \"              VectorPotentialAmplitude: 2500\\n\"\n            \"              Center: [0.0, 0.0, 0.0]\\n\"\n            \"              MaxDistanceFromCenter: 100.0\\n\")\n            ->get_clone();\n\n    const gh::Solutions::WrappedGr<grmhd::AnalyticData::MagnetizedTovStar>\n        analytic_solution_or_data{\n            1.28e-3,\n            std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0,\n                                                                      2.0),\n            RelativisticEuler::Solutions::TovCoordinates::Schwarzschild,\n            make_vector<\n                std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                    InitialMagneticField>>(\n                std::make_unique<\n                    grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n                    2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))};\n    const auto serialized_and_deserialized_condition =\n        serialize_and_deserialize(\n            *dynamic_cast<grmhd::GhValenciaDivClean::BoundaryConditions::\n                              DirichletAnalytic<System>*>(\n                product_boundary_condition.get()));\n\n    test_dg(make_not_null(&gen), serialized_and_deserialized_condition,\n            analytic_solution_or_data);\n    test_fd<System,\n            gh::Solutions::WrappedGr<grmhd::AnalyticData::MagnetizedTovStar>,\n            grmhd::GhValenciaDivClean::BoundaryConditions::DirichletAnalytic<\n            System>>(serialized_and_deserialized_condition,\n                                          analytic_solution_or_data);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Test_DirichletFreeOutflow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = ::TestHelpers::evolution::dg;\n\nnamespace {\n\ntemplate <typename System>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            grmhd::GhValenciaDivClean::BoundaryConditions::BoundaryCondition,\n            tmpl::list<grmhd::GhValenciaDivClean::BoundaryConditions::\n                           DirichletFreeOutflow<System>>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   ghmhd::GhValenciaDivClean::InitialData::initial_data_list>,\n        tmpl::pair<\n            grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField,\n            tmpl::list<grmhd::AnalyticData::InitialMagneticFields::Poloidal>>>;\n  };\n};\n\ntemplate <typename T, typename U>\nvoid test_dg(const gsl::not_null<std::mt19937*> generator,\n             const U& boundary_condition, const T& analytic_solution_or_data) {\n  const double time = 1.3;\n  const size_t num_points = 5;\n\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  const auto interior_gamma1 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n  const auto interior_gamma2 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n\n  const auto coords =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          generator, make_not_null(&dist), num_points);\n\n  using Vars = Variables<tmpl::pop_back<\n      grmhd::GhValenciaDivClean::fd::tags_list_for_reconstruct_fd_neighbor>>;\n  using tags_not_set_by_boundary_condition =\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n  using PrimVars =\n      Variables<tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                           hydro::Tags::ElectronFraction<DataVector>,\n                           hydro::Tags::Pressure<DataVector>,\n                           hydro::Tags::SpecificInternalEnergy<DataVector>,\n                           hydro::Tags::SpecificEnthalpy<DataVector>,\n                           hydro::Tags::Temperature<DataVector>,\n                           hydro::Tags::SpatialVelocity<DataVector, 3>,\n                           hydro::Tags::LorentzFactor<DataVector>,\n                           hydro::Tags::MagneticField<DataVector, 3>>>;\n  Vars vars{num_points};\n  Vars expected_vars;\n  PrimVars prim_vars;\n\n  std::tie(expected_vars, prim_vars) = [&analytic_solution_or_data, &coords,\n                                        &interior_gamma1, &interior_gamma2,\n                                        time]() {\n    Vars expected{num_points};\n    auto& [spacetime_metric, pi, phi, tilde_d, tilde_ye, tilde_tau, tilde_s,\n           tilde_b, tilde_phi,\n\n           rest_mass_density, electron_fraction, specific_internal_energy,\n           spatial_velocity, magnetic_field, divergence_cleaning_field,\n           lorentz_factor, pressure, temperature,\n           lorentz_factor_times_spatial_velocity,\n\n           tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n           tilde_b_flux, tilde_phi_flux,\n\n           gamma1, gamma2, lapse, shift,\n\n           spatial_velocity_one_form, spatial_metric, sqrt_det_spatial_metric,\n           inverse_spatial_metric] = expected;\n\n    gamma1 = interior_gamma1;\n    gamma2 = interior_gamma2;\n\n    PrimVars local_prim_vars{num_points};\n\n    using tags = tmpl::list<\n        hydro::Tags::RestMassDensity<DataVector>,\n        hydro::Tags::ElectronFraction<DataVector>,\n        hydro::Tags::SpecificInternalEnergy<DataVector>,\n        hydro::Tags::SpecificEnthalpy<DataVector>,\n        hydro::Tags::Temperature<DataVector>, hydro::Tags::Pressure<DataVector>,\n        hydro::Tags::SpatialVelocity<DataVector, 3>,\n        hydro::Tags::LorentzFactor<DataVector>,\n        hydro::Tags::MagneticField<DataVector, 3>,\n        hydro::Tags::DivergenceCleaningField<DataVector>,\n        gr::Tags::SpatialMetric<DataVector, 3>,\n        gr::Tags::InverseSpatialMetric<DataVector, 3>,\n        gr::Tags::SqrtDetSpatialMetric<DataVector>, gr::Tags::Lapse<DataVector>,\n        gr::Tags::Shift<DataVector, 3>,\n        gr::Tags::SpacetimeMetric<DataVector, 3>, ::gh::Tags::Pi<DataVector, 3>,\n        ::gh::Tags::Phi<DataVector, 3>>;\n\n    tuples::tagged_tuple_from_typelist<tags> analytic_vars{};\n\n    if constexpr (::is_analytic_solution_v<T>) {\n      analytic_vars = analytic_solution_or_data.variables(coords, time, tags{});\n    } else {\n      (void)time;\n      analytic_vars = analytic_solution_or_data.variables(coords, tags{});\n    }\n    local_prim_vars.assign_subset(analytic_vars);\n    spacetime_metric =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(analytic_vars);\n    pi = get<::gh::Tags::Pi<DataVector, 3>>(analytic_vars);\n    phi = get<::gh::Tags::Phi<DataVector, 3>>(analytic_vars);\n    lapse = get<gr::Tags::Lapse<DataVector>>(analytic_vars);\n    shift = get<gr::Tags::Shift<DataVector, 3>>(analytic_vars);\n    inverse_spatial_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(analytic_vars);\n\n    grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n        make_not_null(&tilde_d), make_not_null(&tilde_ye),\n        make_not_null(&tilde_tau), make_not_null(&tilde_s),\n        make_not_null(&tilde_b), make_not_null(&tilde_phi),\n        get<hydro::Tags::RestMassDensity<DataVector>>(analytic_vars),\n        get<hydro::Tags::ElectronFraction<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(analytic_vars),\n        get<hydro::Tags::Pressure<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::LorentzFactor<DataVector>>(analytic_vars),\n        get<hydro::Tags::MagneticField<DataVector, 3>>(analytic_vars),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(analytic_vars),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(analytic_vars));\n\n    grmhd::ValenciaDivClean::ComputeFluxes::apply(\n        make_not_null(&tilde_d_flux), make_not_null(&tilde_ye_flux),\n        make_not_null(&tilde_tau_flux), make_not_null(&tilde_s_flux),\n        make_not_null(&tilde_b_flux), make_not_null(&tilde_phi_flux), tilde_d,\n        tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, lapse, shift,\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(analytic_vars),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(analytic_vars),\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::Pressure<DataVector>>(analytic_vars),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(analytic_vars),\n        get<hydro::Tags::LorentzFactor<DataVector>>(analytic_vars),\n        get<hydro::Tags::MagneticField<DataVector, 3>>(analytic_vars));\n\n    return std::tuple(expected, local_prim_vars);\n  }();\n\n  // Pick random direction normal covector, then normalize and compute normal\n  // vector.\n  tnsr::i<DataVector, 3> normal_covector{num_points};\n  get<0>(normal_covector) = 0.5;\n  get<1>(normal_covector) = 0.0;\n  get<2>(normal_covector) = 0.5;\n  const auto magnitude_normal = magnitude(\n      normal_covector,\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(expected_vars));\n  for (size_t i = 0; i < 3; ++i) {\n    normal_covector.get(i) /= get(magnitude_normal);\n  }\n  const auto normal_vector =\n      tenex::evaluate<ti::I>(normal_covector(ti::j) *\n                             get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                                 expected_vars)(ti::I, ti::J));\n\n  auto& [spacetime_metric, pi, phi, tilde_d, tilde_ye, tilde_tau, tilde_s,\n         tilde_b, tilde_phi,\n\n         rest_mass_density, electron_fraction, specific_internal_energy,\n         spatial_velocity, magnetic_field, divergence_cleaning_field,\n         lorentz_factor, pressure, temperature,\n         lorentz_factor_times_spatial_velocity,\n\n         tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux,\n         tilde_b_flux, tilde_phi_flux, gamma1, gamma2, lapse, shift,\n\n         spatial_velocity_one_form, spatial_metric, sqrt_det_spatial_metric,\n\n         inverse_spatial_metric] = vars;\n\n  const auto result = boundary_condition.dg_ghost(\n      make_not_null(&spacetime_metric), make_not_null(&pi), make_not_null(&phi),\n      make_not_null(&tilde_d), make_not_null(&tilde_ye),\n      make_not_null(&tilde_tau), make_not_null(&tilde_s),\n      make_not_null(&tilde_b), make_not_null(&tilde_phi),\n      make_not_null(&tilde_d_flux), make_not_null(&tilde_ye_flux),\n      make_not_null(&tilde_tau_flux), make_not_null(&tilde_s_flux),\n      make_not_null(&tilde_b_flux), make_not_null(&tilde_phi_flux),\n      make_not_null(&gamma1), make_not_null(&gamma2), make_not_null(&lapse),\n      make_not_null(&shift), make_not_null(&spatial_velocity_one_form),\n      make_not_null(&rest_mass_density), make_not_null(&electron_fraction),\n      make_not_null(&temperature), make_not_null(&spatial_velocity),\n      make_not_null(&inverse_spatial_metric), {}, normal_covector,\n      normal_vector,\n\n      get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n      get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars),\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prim_vars),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars),\n      get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars),\n      get<hydro::Tags::Pressure<DataVector>>(prim_vars),\n      get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n\n      coords, interior_gamma1, interior_gamma2, time);\n  CHECK(not result.has_value());\n\n  grmhd::ValenciaDivClean::BoundaryConditions::HydroFreeOutflow\n      grmhd_free_outflow{};\n\n  auto& [spacetime_metric_expected, pi_expected, phi_expected, tilde_d_expected,\n         tilde_ye_expected, tilde_tau_expected, tilde_s_expected,\n         tilde_b_expected, tilde_phi_expected,\n\n         rest_mass_density_expected, electron_fraction_expected,\n         specific_internal_energy_expected, spatial_velocity_expected,\n         magnetic_field_expected, divergence_cleaning_field_expected,\n         lorentz_factor_expected, pressure_expected, temperature_expected,\n         lorentz_factor_times_spatial_velocity_expected,\n\n         tilde_d_flux_expected, tilde_ye_flux_expected, tilde_tau_flux_expected,\n         tilde_s_flux_expected, tilde_b_flux_expected, tilde_phi_flux_expected,\n         gamma1_expected, gamma2_expected, lapse_expected, shift_expected,\n\n         spatial_velocity_one_form_expected, spatial_metric_expected,\n         sqrt_det_spatial_metric_expected, inverse_spatial_metric_expected] =\n      expected_vars;\n\n  grmhd_free_outflow.dg_ghost(\n      make_not_null(&tilde_d_expected), make_not_null(&tilde_ye_expected),\n      make_not_null(&tilde_tau_expected), make_not_null(&tilde_s_expected),\n      make_not_null(&tilde_b_expected), make_not_null(&tilde_phi_expected),\n      make_not_null(&tilde_d_flux_expected),\n      make_not_null(&tilde_ye_flux_expected),\n      make_not_null(&tilde_tau_flux_expected),\n      make_not_null(&tilde_s_flux_expected),\n      make_not_null(&tilde_b_flux_expected),\n      make_not_null(&tilde_phi_flux_expected), make_not_null(&lapse_expected),\n      make_not_null(&shift_expected),\n      make_not_null(&spatial_velocity_one_form_expected),\n      make_not_null(&rest_mass_density_expected),\n      make_not_null(&electron_fraction_expected),\n      make_not_null(&temperature_expected),\n      make_not_null(&spatial_velocity_expected),\n      make_not_null(&inverse_spatial_metric_expected), {}, normal_covector,\n      normal_vector,\n\n      get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n      get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars),\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prim_vars),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars),\n      get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars),\n      get<hydro::Tags::Pressure<DataVector>>(prim_vars),\n      get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n\n      shift, lapse, inverse_spatial_metric);\n\n  tmpl::for_each<typename Vars::tags_list>([&expected_vars, &vars](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    if constexpr (not tmpl::list_contains_v<tags_not_set_by_boundary_condition,\n                                            tag>) {\n      CAPTURE(db::tag_name<tag>());\n      CHECK(get<tag>(vars) == get<tag>(expected_vars));\n    }\n  });\n}\n\ntemplate <typename System, typename T, typename U>\nvoid test_fd(const U& boundary_condition, const T& analytic_solution_or_data) {\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  const double time = 1.3;\n  const Mesh<3> subcell_mesh{9, Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  const std::array<double, 3> lower_bound{{0.78, 1.18, 1.28}};\n  const std::array<double, 3> upper_bound{{0.82, 1.22, 1.32}};\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          Affine3D{Affine{-1., 1., lower_bound[0], upper_bound[0]},\n                   Affine{-1., 1., lower_bound[1], upper_bound[1]},\n                   Affine{-1., 1., lower_bound[2], upper_bound[2]}});\n  const ElementId<3> element_id{0};\n  const ElementMap logical_to_grid_map{\n      element_id,\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n          Affine3D{Affine{-1., 1., 2.0 * lower_bound[0], 2.0 * upper_bound[0]},\n                   Affine{-1., 1., 2.0 * lower_bound[1], 2.0 * upper_bound[1]},\n                   Affine{-1., 1., 2.0 * lower_bound[2], 2.0 * upper_bound[2]}})\n          .get_clone()};\n  const auto direction = Direction<3>::lower_xi();\n\n  grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>\n      reconstructor{};\n\n  using Vars = Variables<grmhd::GhValenciaDivClean::Tags::\n                             primitive_grmhd_and_spacetime_reconstruction_tags>;\n  using PrimVars =\n      Variables<tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                           hydro::Tags::ElectronFraction<DataVector>,\n                           hydro::Tags::Pressure<DataVector>,\n                           hydro::Tags::SpecificInternalEnergy<DataVector>,\n                           hydro::Tags::SpecificEnthalpy<DataVector>,\n                           hydro::Tags::Temperature<DataVector>,\n                           hydro::Tags::SpatialVelocity<DataVector, 3>,\n                           hydro::Tags::LorentzFactor<DataVector>,\n                           hydro::Tags::MagneticField<DataVector, 3>>>;\n  Vars vars{reconstructor.ghost_zone_size() *\n            subcell_mesh.extents().slice_away(0).product()};\n  PrimVars prim_vars{subcell_mesh.number_of_grid_points()};\n  if constexpr (::is_analytic_solution_v<T>) {\n    prim_vars.assign_subset(analytic_solution_or_data.variables(\n        grid_to_inertial_map(\n            logical_to_grid_map(logical_coordinates(subcell_mesh)), time,\n            functions_of_time),\n        time, typename PrimVars::tags_list{}));\n  } else {\n    prim_vars.assign_subset(analytic_solution_or_data.variables(\n        grid_to_inertial_map(\n            logical_to_grid_map(logical_coordinates(subcell_mesh)), time,\n            functions_of_time),\n        typename PrimVars::tags_list{}));\n  }\n\n  Vars expected_vars = [&analytic_solution_or_data, &direction,\n                        &functions_of_time, &grid_to_inertial_map,\n                        &logical_to_grid_map, &reconstructor, &subcell_mesh,\n                        time]() {\n    const auto ghost_logical_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, reconstructor.ghost_zone_size(), direction);\n\n    const auto ghost_inertial_coords = grid_to_inertial_map(\n        logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n    using tags = tmpl::list<\n        hydro::Tags::RestMassDensity<DataVector>,\n        hydro::Tags::ElectronFraction<DataVector>,\n        hydro::Tags::Temperature<DataVector>, hydro::Tags::Pressure<DataVector>,\n        hydro::Tags::SpatialVelocity<DataVector, 3>,\n        hydro::Tags::LorentzFactor<DataVector>,\n        hydro::Tags::MagneticField<DataVector, 3>,\n        hydro::Tags::DivergenceCleaningField<DataVector>,\n        gr::Tags::SpacetimeMetric<DataVector, 3>, ::gh::Tags::Pi<DataVector, 3>,\n        ::gh::Tags::Phi<DataVector, 3>>;\n\n    tuples::tagged_tuple_from_typelist<tags> analytic_vars{};\n\n    if constexpr (::is_analytic_solution_v<T>) {\n      analytic_vars = analytic_solution_or_data.variables(ghost_inertial_coords,\n                                                          time, tags{});\n    } else {\n      (void)time;\n      analytic_vars =\n          analytic_solution_or_data.variables(ghost_inertial_coords, tags{});\n    }\n\n    Vars expected{get<0>(ghost_inertial_coords).size()};\n\n    get<gr::Tags::SpacetimeMetric<DataVector, 3>>(expected) =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(analytic_vars);\n    get<::gh::Tags::Pi<DataVector, 3>>(expected) =\n        get<::gh::Tags::Pi<DataVector, 3>>(analytic_vars);\n    get<::gh::Tags::Phi<DataVector, 3>>(expected) =\n        get<::gh::Tags::Phi<DataVector, 3>>(analytic_vars);\n    get<hydro::Tags::RestMassDensity<DataVector>>(expected) =\n        get<hydro::Tags::RestMassDensity<DataVector>>(analytic_vars);\n    get<hydro::Tags::ElectronFraction<DataVector>>(expected) =\n        get<hydro::Tags::ElectronFraction<DataVector>>(analytic_vars);\n    get<hydro::Tags::Temperature<DataVector>>(expected) =\n        get<hydro::Tags::Temperature<DataVector>>(analytic_vars);\n\n    for (size_t i = 0; i < 3; ++i) {\n      auto& lorentz_factor =\n          get<hydro::Tags::LorentzFactor<DataVector>>(analytic_vars);\n      auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(analytic_vars);\n      get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n          expected)\n          .get(i) = get(lorentz_factor) * spatial_velocity.get(i);\n    }\n\n    get<hydro::Tags::MagneticField<DataVector, 3>>(expected) =\n        get<hydro::Tags::MagneticField<DataVector, 3>>(analytic_vars);\n    get<hydro::Tags::DivergenceCleaningField<DataVector>>(expected) =\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(analytic_vars);\n    return expected;\n  }();\n  auto& [rest_mass_density, electron_fraction, temperature,\n         lorentz_factor_times_spatial_velocity, magnetic_field,\n         divergence_cleaning_field, spacetime_metric, pi, phi] = vars;\n\n  // Note: Once we support high-order fluxes with GHMHD we will need to\n  // handle this correctly.\n  std::optional<Variables<db::wrap_tags_in<\n      ::Tags::Flux, typename grmhd::ValenciaDivClean::System::flux_variables,\n      tmpl::size_t<3>, Frame::Inertial>>>\n      cell_centered_ghost_fluxes{std::nullopt};\n  // Set to zero since it shouldn't be used\n  Scalar<DataVector> interior_specific_internal_energy{};\n  Scalar<DataVector> specific_internal_energy{};\n  Scalar<DataVector> pressure{};\n  tnsr::I<DataVector, 3> spatial_velocity{};\n  Scalar<DataVector> lorentz_factor{};\n  const tnsr::I<DataVector, 3> interior_shift{};\n  const Scalar<DataVector> interior_lapse{};\n  const tnsr::ii<DataVector, 3> interior_spatial_metric{};\n  tnsr::ii<DataVector, 3> spatial_metric{};\n  tnsr::II<DataVector, 3> inv_spatial_metric{};\n  Scalar<DataVector> sqrt_det_spatial_metric{};\n  Scalar<DataVector> lapse{};\n  tnsr::I<DataVector, 3> shift{};\n\n  boundary_condition.fd_ghost(\n      make_not_null(&spacetime_metric), make_not_null(&pi), make_not_null(&phi),\n      make_not_null(&rest_mass_density), make_not_null(&electron_fraction),\n      make_not_null(&temperature),\n      make_not_null(&lorentz_factor_times_spatial_velocity),\n      make_not_null(&magnetic_field), make_not_null(&divergence_cleaning_field),\n\n      direction, subcell_mesh,\n\n      get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n      get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars),\n      get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n      get<hydro::Tags::Pressure<DataVector>>(prim_vars),\n      interior_specific_internal_energy,\n      get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars),\n\n      time, functions_of_time, logical_to_grid_map, grid_to_inertial_map,\n      reconstructor);\n\n  grmhd::ValenciaDivClean::BoundaryConditions::HydroFreeOutflow\n      grmhd_free_outflow{};\n\n  auto& [rest_mass_density_expected, electron_fraction_expected,\n         temperature_expected, lorentz_factor_times_spatial_velocity_expected,\n         magnetic_field_expected, divergence_cleaning_field_expected,\n         spacetime_metric_expected, pi_expected, phi_expected] = expected_vars;\n\n  grmhd_free_outflow.fd_ghost_impl(\n      make_not_null(&rest_mass_density_expected),\n      make_not_null(&electron_fraction_expected),\n      make_not_null(&temperature_expected), make_not_null(&pressure),\n      make_not_null(&specific_internal_energy),\n      make_not_null(&lorentz_factor_times_spatial_velocity_expected),\n      make_not_null(&spatial_velocity), make_not_null(&lorentz_factor),\n      make_not_null(&magnetic_field_expected),\n      make_not_null(&divergence_cleaning_field_expected),\n\n      make_not_null(&spatial_metric), make_not_null(&inv_spatial_metric),\n      make_not_null(&sqrt_det_spatial_metric), make_not_null(&lapse),\n      make_not_null(&shift),\n\n      direction, subcell_mesh,\n\n      get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n      get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars),\n      get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n      get<hydro::Tags::Pressure<DataVector>>(prim_vars),\n      interior_specific_internal_energy,\n      get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars),\n\n      // Note: metric vars are empty because they shouldn't be used\n      interior_spatial_metric, interior_lapse, interior_shift,\n\n      reconstructor.ghost_zone_size(),\n\n      cell_centered_ghost_fluxes.has_value());\n\n  tmpl::for_each<typename Vars::tags_list>([&expected_vars, &vars](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CAPTURE(pretty_type::short_name<tag>());\n    CHECK(get<tag>(vars) == get<tag>(expected_vars));\n  });\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.GhValenciaDivClean.BoundaryConditions.DirichletFreeOutflow\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n  register_factory_classes_with_charm<Metavariables<System>>();\n  EquationsOfState::register_derived_with_charm();\n\n  {\n    INFO(\"Test with analytic solution\");\n    const auto product_boundary_condition =\n        TestHelpers::test_creation<\n            std::unique_ptr<grmhd::GhValenciaDivClean::BoundaryConditions::\n                                BoundaryCondition>,\n            Metavariables<System>>(\n            \"DirichletFreeOutflow:\\n\"\n            \"  AnalyticPrescription:\\n\"\n            \"    GeneralizedHarmonic(BondiMichel):\\n\"\n            \"        Mass: 1.0\\n\"\n            \"        SonicRadius: 4.0\\n\"\n            \"        SonicDensity: 0.1\\n\"\n            \"        PolytropicExponent: 2.0\\n\"\n            \"        MagFieldStrength: 0.01\\n\")\n            ->get_clone();\n    const gh::Solutions::WrappedGr<grmhd::Solutions::BondiMichel>\n        analytic_solution_or_data{1.0, 4.0, 0.1, 2.0, 0.01};\n    const auto serialized_and_deserialized_condition =\n        serialize_and_deserialize(\n            *dynamic_cast<grmhd::GhValenciaDivClean::BoundaryConditions::\n                              DirichletFreeOutflow<System>*>(\n                product_boundary_condition.get()));\n\n    test_dg(make_not_null(&gen), serialized_and_deserialized_condition,\n            analytic_solution_or_data);\n    test_fd<System>(serialized_and_deserialized_condition,\n                                     analytic_solution_or_data);\n  }\n  {\n    INFO(\"Test with analytic data\");\n    const auto product_boundary_condition =\n        TestHelpers::test_creation<\n            std::unique_ptr<grmhd::GhValenciaDivClean::BoundaryConditions::\n                                BoundaryCondition>,\n            Metavariables<System>>(\n            \"DirichletFreeOutflow:\\n\"\n            \"  AnalyticPrescription:\\n\"\n            \"      GeneralizedHarmonic(MagnetizedTovStar):\\n\"\n            \"        CentralDensity: 1.28e-3\\n\"\n            \"        EquationOfState:\\n\"\n            \"          PolytropicFluid:\\n\"\n            \"            PolytropicConstant: 100.0\\n\"\n            \"            PolytropicExponent: 2.0\\n\"\n            \"        Coordinates: Schwarzschild\\n\"\n            \"        MagneticFields: \\n\"\n            \"          - Poloidal:\\n\"\n            \"              PressureExponent: 2\\n\"\n            \"              CutoffPressure: 0.04\\n\"\n            \"              VectorPotentialAmplitude: 2500\\n\"\n            \"              Center: [0.0, 0.0, 0.0]\\n\"\n            \"              MaxDistanceFromCenter: 100.0\\n\")\n            ->get_clone();\n    const gh::Solutions::WrappedGr<grmhd::AnalyticData::MagnetizedTovStar>\n        analytic_solution_or_data{\n            1.28e-3,\n            std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0,\n                                                                      2.0),\n            RelativisticEuler::Solutions::TovCoordinates::Schwarzschild,\n            make_vector<\n                std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                    InitialMagneticField>>(\n                std::make_unique<\n                    grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n                    2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))};\n    const auto serialized_and_deserialized_condition =\n        serialize_and_deserialize(\n            *dynamic_cast<grmhd::GhValenciaDivClean::BoundaryConditions::\n                              DirichletFreeOutflow<System>*>(\n                product_boundary_condition.get()));\n\n    test_dg(make_not_null(&gen), serialized_and_deserialized_condition,\n            analytic_solution_or_data);\n    test_fd<System>(serialized_and_deserialized_condition,\n                                     analytic_solution_or_data);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/Test_ProductOfCorrections.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers/RetrieveUniquePtr.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace helpers = TestHelpers::evolution::dg;\n\ntemplate <typename DerivedCorrection, typename PackagedFieldTagList,\n          typename EvolvedTagList, typename FluxTagList, typename TempTagList,\n          typename PrimTagList, typename VolumeTagList>\nstruct ComputeBoundaryCorrectionHelperImpl;\n\ntemplate <typename DerivedCorrection, typename... PackagedFieldTags,\n          typename... EvolvedTags, typename... FluxTags, typename... TempTags,\n          typename... PrimTags, typename... VolumeTags>\nstruct ComputeBoundaryCorrectionHelperImpl<\n    DerivedCorrection, tmpl::list<PackagedFieldTags...>,\n    tmpl::list<EvolvedTags...>, tmpl::list<FluxTags...>,\n    tmpl::list<TempTags...>, tmpl::list<PrimTags...>,\n    tmpl::list<VolumeTags...>> {\n  template <typename PackagedVariables, typename EvolvedVariables,\n            typename FluxVariables, typename TempVariables,\n            typename PrimVariables, typename VolumeVariables>\n  static double dg_package_data(\n      const gsl::not_null<PackagedVariables*> packaged_variables,\n      const EvolvedVariables& evolved_variables,\n      const FluxVariables& flux_variables, const TempVariables& temp_variables,\n      const PrimVariables& prim_variables,\n      const VolumeVariables& volume_variables,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const DerivedCorrection& derived_correction) {\n    return derived_correction.dg_package_data(\n        make_not_null(&get<PackagedFieldTags>(*packaged_variables))...,\n        get<EvolvedTags>(evolved_variables)...,\n        get<FluxTags>(flux_variables)..., get<TempTags>(temp_variables)...,\n        get<PrimTags>(prim_variables)..., normal_covector, normal_vector,\n        mesh_velocity, normal_dot_mesh_velocity,\n        StdHelpers::retrieve(get<VolumeTags>(volume_variables))...);\n  }\n\n  template <typename EvolvedVariables, typename PackagedVariables>\n  static void dg_boundary_terms(\n      const gsl::not_null<EvolvedVariables*> boundary_corrections,\n      const PackagedVariables& internal_packaged_fields,\n      const PackagedVariables& external_packaged_fields,\n      dg::Formulation dg_formulation,\n      const DerivedCorrection& derived_correction) {\n    derived_correction.dg_boundary_terms(\n        make_not_null(&get<EvolvedTags>(*boundary_corrections))...,\n        get<PackagedFieldTags>(internal_packaged_fields)...,\n        get<PackagedFieldTags>(external_packaged_fields)..., dg_formulation);\n  }\n};\n\ntemplate <typename DerivedCorrection, typename EvolvedTagList,\n          typename FluxTagList>\nusing ComputeBoundaryCorrectionHelper = ComputeBoundaryCorrectionHelperImpl<\n    DerivedCorrection, typename DerivedCorrection::dg_package_field_tags,\n    EvolvedTagList, FluxTagList,\n    typename DerivedCorrection::dg_package_data_temporary_tags,\n    typename DerivedCorrection::dg_package_data_primitive_tags,\n    typename DerivedCorrection::dg_package_data_volume_tags>;\n\ntemplate <typename DerivedGhCorrection, typename DerivedValenciaCorrection,\n          typename NeutrinoTransportSystem>\nvoid test_boundary_correction_combination(\n    const DerivedGhCorrection& derived_gh_correction,\n    const DerivedValenciaCorrection& derived_valencia_correction,\n    const grmhd::GhValenciaDivClean::BoundaryCorrections::ProductOfCorrections<\n        DerivedGhCorrection, DerivedValenciaCorrection>&\n        derived_product_correction,\n    const dg::Formulation formulation) {\n  CHECK(derived_product_correction.gh_correction() == derived_gh_correction);\n  CHECK(derived_product_correction.valencia_correction() ==\n        derived_valencia_correction);\n  using gh_variables_tags = typename gh::System<3_st>::variables_tag::tags_list;\n  using valencia_variables_tags =\n      typename grmhd::ValenciaDivClean::System::variables_tag::tags_list;\n  using evolved_variables_type =\n      Variables<tmpl::append<gh_variables_tags, valencia_variables_tags>>;\n\n  using derived_product_correction_type =\n      grmhd::GhValenciaDivClean::BoundaryCorrections::ProductOfCorrections<\n          DerivedGhCorrection, DerivedValenciaCorrection>;\n\n  using packaged_variables_type = Variables<\n      typename derived_product_correction_type::dg_package_field_tags>;\n\n  using gh_flux_tags =\n      db::wrap_tags_in<::Tags::Flux, typename gh::System<3_st>::flux_variables,\n                       tmpl::size_t<3_st>, Frame::Inertial>;\n  using valencia_flux_tags =\n      db::wrap_tags_in<::Tags::Flux,\n                       typename grmhd::ValenciaDivClean::System::flux_variables,\n                       tmpl::size_t<3_st>, Frame::Inertial>;\n  using flux_variables_type =\n      Variables<tmpl::append<gh_flux_tags, valencia_flux_tags>>;\n\n  using temporary_variables_type = Variables<\n      typename derived_product_correction_type::dg_package_data_temporary_tags>;\n  using primitive_variables_type = Variables<\n      typename derived_product_correction_type::dg_package_data_primitive_tags>;\n\n  const size_t element_size = 10_st;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  packaged_variables_type expected_packaged_variables{element_size};\n  packaged_variables_type packaged_variables{element_size};\n\n  const auto evolved_variables =\n      make_with_random_values<evolved_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto flux_variables = make_with_random_values<flux_variables_type>(\n      make_not_null(&gen), make_not_null(&dist), element_size);\n  primitive_variables_type prim_variables{element_size};\n  if constexpr (tmpl::size<typename derived_product_correction_type::\n                               dg_package_data_primitive_tags>::value > 0) {\n    fill_with_random_values(make_not_null(&prim_variables), make_not_null(&gen),\n                            make_not_null(&dist));\n  }\n  const auto temporary_variables =\n      make_with_random_values<temporary_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const tuples::TaggedTuple<hydro::Tags::GrmhdEquationOfState> volume_variables{\n      EquationsOfState::PolytropicFluid<true>{100.0, 2.0}.promote_to_3d_eos()};\n\n  const auto normal_covector =\n      make_with_random_values<tnsr::i<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto normal_vector =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto mesh_velocity =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto normal_dot_mesh_velocity =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n\n  double expected_package_gh_result =\n      ComputeBoundaryCorrectionHelper<DerivedGhCorrection, gh_variables_tags,\n                                      gh_flux_tags>::\n          dg_package_data(make_not_null(&expected_packaged_variables),\n                          evolved_variables, flux_variables,\n                          temporary_variables, prim_variables, volume_variables,\n                          normal_covector, normal_vector, mesh_velocity,\n                          normal_dot_mesh_velocity, derived_gh_correction);\n  double expected_package_valencia_result = ComputeBoundaryCorrectionHelper<\n      DerivedValenciaCorrection, valencia_variables_tags, valencia_flux_tags>::\n      dg_package_data(make_not_null(&expected_packaged_variables),\n                      evolved_variables, flux_variables, temporary_variables,\n                      prim_variables, volume_variables, normal_covector,\n                      normal_vector, mesh_velocity, normal_dot_mesh_velocity,\n                      derived_valencia_correction);\n\n  double package_combined_result = ComputeBoundaryCorrectionHelper<\n      derived_product_correction_type,\n      tmpl::append<gh_variables_tags, valencia_variables_tags>,\n      tmpl::append<gh_flux_tags, valencia_flux_tags>>::\n      dg_package_data(make_not_null(&packaged_variables), evolved_variables,\n                      flux_variables, temporary_variables, prim_variables,\n                      volume_variables, normal_covector, normal_vector,\n                      mesh_velocity, normal_dot_mesh_velocity,\n                      derived_product_correction);\n  CHECK(approx(SINGLE_ARG(std::max(expected_package_gh_result,\n                                   expected_package_valencia_result))) ==\n        package_combined_result);\n  CHECK_VARIABLES_APPROX(packaged_variables, expected_packaged_variables);\n\n  auto serialized_and_deserialized_correction =\n      serialize_and_deserialize(derived_product_correction);\n\n  package_combined_result = ComputeBoundaryCorrectionHelper<\n      derived_product_correction_type,\n      tmpl::append<gh_variables_tags, valencia_variables_tags>,\n      tmpl::append<gh_flux_tags, valencia_flux_tags>>::\n      dg_package_data(make_not_null(&packaged_variables), evolved_variables,\n                      flux_variables, temporary_variables, prim_variables,\n                      volume_variables, normal_covector, normal_vector,\n                      mesh_velocity, normal_dot_mesh_velocity,\n                      serialized_and_deserialized_correction);\n  CHECK(approx(SINGLE_ARG(std::max(expected_package_gh_result,\n                                   expected_package_valencia_result))) ==\n        package_combined_result);\n  CHECK_VARIABLES_APPROX(packaged_variables, expected_packaged_variables);\n\n  const auto external_packaged_fields =\n      make_with_random_values<packaged_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto internal_packaged_fields =\n      make_with_random_values<packaged_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n\n  evolved_variables_type expected_boundary_correction{element_size};\n  evolved_variables_type boundary_correction{element_size};\n  ComputeBoundaryCorrectionHelper<DerivedGhCorrection, gh_variables_tags,\n                                  gh_flux_tags>::\n      dg_boundary_terms(make_not_null(&expected_boundary_correction),\n                        internal_packaged_fields, external_packaged_fields,\n                        formulation, derived_gh_correction);\n  ComputeBoundaryCorrectionHelper<DerivedValenciaCorrection,\n                                  valencia_variables_tags, valencia_flux_tags>::\n      dg_boundary_terms(make_not_null(&expected_boundary_correction),\n                        internal_packaged_fields, external_packaged_fields,\n                        formulation, derived_valencia_correction);\n\n  ComputeBoundaryCorrectionHelper<\n      derived_product_correction_type,\n      tmpl::append<gh_variables_tags, valencia_variables_tags>,\n      tmpl::append<gh_flux_tags, valencia_flux_tags>>::\n      dg_boundary_terms(make_not_null(&boundary_correction),\n                        internal_packaged_fields, external_packaged_fields,\n                        formulation, derived_product_correction);\n  CHECK_VARIABLES_APPROX(boundary_correction, expected_boundary_correction);\n\n  ComputeBoundaryCorrectionHelper<\n      derived_product_correction_type,\n      tmpl::append<gh_variables_tags, valencia_variables_tags>,\n      tmpl::append<gh_flux_tags, valencia_flux_tags>>::\n      dg_boundary_terms(make_not_null(&boundary_correction),\n                        internal_packaged_fields, external_packaged_fields,\n                        formulation, serialized_and_deserialized_correction);\n  CHECK_VARIABLES_APPROX(boundary_correction, expected_boundary_correction);\n\n  PUPable_reg(SINGLE_ARG(\n      grmhd::GhValenciaDivClean::BoundaryCorrections::ProductOfCorrections<\n          DerivedGhCorrection, DerivedValenciaCorrection>));\n\n  const tuples::TaggedTuple<\n      helpers::Tags::Range<gr::Tags::Lapse<DataVector>>,\n      helpers::Tags::Range<gr::Tags::Shift<DataVector, 3>>>\n      ranges{std::array{0.3, 1.0}, std::array{0.01, 0.02}};\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<\n      grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>>(\n      make_not_null(&gen), derived_product_correction,\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_variables, ranges);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.GhValenciaDivClean.BoundaryCorrections.ProductOfCorrections\",\n    \"[Unit][Evolution]\") {\n  // scoped to separate out each product combination\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  {\n    INFO(\"Product correction UpwindPenalty and Rusanov\");\n    using ProductUpwindPenaltyAndRusanov =\n        grmhd::GhValenciaDivClean::BoundaryCorrections::ProductOfCorrections<\n            gh::BoundaryCorrections::UpwindPenalty<3_st>,\n            grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov>;\n    grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov valencia_correction{};\n    gh::BoundaryCorrections::UpwindPenalty<3_st> gh_correction{};\n    TestHelpers::test_factory_creation<evolution::BoundaryCorrection,\n                                       ProductUpwindPenaltyAndRusanov>(\n        \"ProductUpwindPenaltyAndRusanov:\\n\"\n        \"  UpwindPenalty:\\n\"\n        \"  Rusanov:\");\n    const ProductUpwindPenaltyAndRusanov product_boundary_correction{\n        gh_correction, valencia_correction};\n    for (const auto formulation :\n         {dg::Formulation::StrongInertial, dg::Formulation::WeakInertial}) {\n      test_boundary_correction_combination<\n          gh::BoundaryCorrections::UpwindPenalty<3_st>,\n          grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov,\n          NeutrinoTransportSystem>(gh_correction, valencia_correction,\n                                   product_boundary_correction, formulation);\n    }\n  }\n  {\n    INFO(\"Product correction UpwindPenalty and Hll\");\n    using ProductUpwindPenaltyAndHll =\n        grmhd::GhValenciaDivClean::BoundaryCorrections::ProductOfCorrections<\n            gh::BoundaryCorrections::UpwindPenalty<3_st>,\n            grmhd::ValenciaDivClean::BoundaryCorrections::Hll>;\n    const grmhd::ValenciaDivClean::BoundaryCorrections::Hll valencia_correction{\n        1.0e-30, 1.0e-8};\n    const gh::BoundaryCorrections::UpwindPenalty<3_st> gh_correction{};\n    TestHelpers::test_factory_creation<evolution::BoundaryCorrection,\n                                       ProductUpwindPenaltyAndHll>(\n        \"ProductUpwindPenaltyAndHll:\\n\"\n        \"  UpwindPenalty:\\n\"\n        \"  Hll:\\n\"\n        \"    MagneticFieldMagnitudeForHydro: 1.0e-30\\n\"\n        \"    LightSpeedDensityCutoff: 1.0e-8\\n\");\n    const ProductUpwindPenaltyAndHll product_boundary_correction{\n        gh_correction, valencia_correction};\n    for (const auto formulation :\n         {dg::Formulation::StrongInertial, dg::Formulation::WeakInertial}) {\n      test_boundary_correction_combination<\n          gh::BoundaryCorrections::UpwindPenalty<3_st>,\n          grmhd::ValenciaDivClean::BoundaryCorrections::Hll,\n          NeutrinoTransportSystem>(gh_correction, valencia_correction,\n                                   product_boundary_correction, formulation);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GhValenciaDivClean\")\n\nset(LIBRARY_SOURCES\n  Actions/Test_SetInitialData.cpp\n  BoundaryConditions/Test_ConstraintPreservingFreeOutflow.cpp\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryConditions/Test_DirichletFreeOutflow.cpp\n  BoundaryCorrections/Test_ProductOfCorrections.cpp\n  FiniteDifference/Test_BoundaryConditionGhostData.cpp\n  FiniteDifference/Test_Derivatives.cpp\n  FiniteDifference/Test_FillNeighborSpacetimeVariables.cpp\n  FiniteDifference/Test_FilterOptions.cpp\n  FiniteDifference/Test_Filters.cpp\n  FiniteDifference/Test_MonotonisedCentral.cpp\n  FiniteDifference/Test_PositivityPreservingAdaptiveOrder.cpp\n  FiniteDifference/Test_Wcns5z.cpp\n  Subcell/Test_FixConservativesAndComputePrims.cpp\n  Subcell/Test_NeighborPackagedData.cpp\n  Subcell/Test_PrimitiveGhostData.cpp\n  Subcell/Test_PrimsAfterRollback.cpp\n  Subcell/Test_ResizeAndComputePrimitives.cpp\n  Subcell/Test_ZeroTimeDerivatives.cpp\n  Test_Characteristics.cpp\n  Test_Constraints.cpp\n  Test_SetPiAndPhiFromConstraints.cpp\n  Test_StressEnergy.cpp\n  Test_Tags.cpp\n  Test_TimeDerivativeTerms.cpp\n  )\n\n# The following test is disabled for nvcc because nvcc can't handle it\n# (it runs out of memory).\nif(NOT ${KOKKOS_CXX_COMPILER_ID} STREQUAL \"NVIDIA\")\n  list(APPEND LIBRARY_SOURCES Subcell/Test_TimeDerivative.cpp)\nendif()\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DomainBoundaryConditionsHelpers\n  DomainHelpers\n  Framework\n  GeneralRelativityHelpers\n  GeneralRelativitySolutions\n  GeneralizedHarmonic\n  GhGrMhdSolutions\n  GhValenciaDivClean\n  HydroHelpers\n  ValenciaDivClean\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Test_BoundaryConditionGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/ConstraintPreservingFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/BoundaryConditionGhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\nnamespace {\n\n// Metavariables to parse the list of derived classes of boundary conditions\ntemplate <typename System>\nstruct EvolutionMetaVars {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        BoundaryConditions::BoundaryCondition,\n        tmpl::push_back<\n            BoundaryConditions::standard_boundary_conditions<\n                System>,\n            BoundaryConditions::DirichletAnalytic<System>>>>;\n  };\n};\n\nusing SolutionForTest =\n    gh::Solutions::WrappedGr<RelativisticEuler::Solutions::TovStar>;\n\ntemplate <typename System, typename BoundaryConditionType>\nvoid test(const BoundaryConditionType& boundary_condition,\n          const SolutionForTest& solution) {\n  CAPTURE(pretty_type::name<BoundaryConditionType>());\n  const size_t num_dg_pts = 4;\n\n  // Create a 3D element [-1, 1]^3 and use it for test\n  const std::array lower_bounds{-1.0, -1.0, -1.0};\n  const std::array upper_bounds{1.0, 1.0, 1.0};\n  const std::array<size_t, 3> refinement_levels{0, 0, 0};\n  const std::array<size_t, 3> number_of_grid_points{num_dg_pts, num_dg_pts,\n                                                    num_dg_pts};\n  const domain::creators::Brick brick(\n      lower_bounds, upper_bounds, refinement_levels, number_of_grid_points,\n      {{{{boundary_condition.get_clone(), boundary_condition.get_clone()}},\n        {{boundary_condition.get_clone(), boundary_condition.get_clone()}},\n        {{boundary_condition.get_clone(), boundary_condition.get_clone()}}}});\n  auto domain = brick.create_domain();\n  auto boundary_conditions = brick.external_boundary_conditions();\n  const auto element = domain::create_initial_element(\n      ElementId<3>{0, {SegmentId{0, 0}, SegmentId{0, 0}, SegmentId{0, 0}}},\n      domain.blocks(), std::vector<std::array<size_t, 3>>{{refinement_levels}});\n\n  // Mesh and coordinates\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  // use MC reconstruction for test\n  using ReconstructorForTest =\n      fd::MonotonisedCentralPrim<System>;\n  const size_t ghost_zone_size{ReconstructorForTest{}.ghost_zone_size()};\n\n  // dummy neighbor data to put into DataBox\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<3>::type\n      neighbor_data{};\n\n  // Below are tags required by DirichletAnalytic boundary condition to compute\n  // inertial coords of ghost FD cells:\n  //  - time\n  //  - functions of time\n  //  - element map\n  //  - coordinate map\n  //  - subcell logical coordinates\n  //  - analytic solution\n\n  const double time{0.5};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  const ElementMap<3, Frame::Grid> logical_to_grid_map(\n      ElementId<3>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<3>{}));\n\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n\n  // Below are tags used for testing Outflow boundary condition\n  //  - volume metric and primitive variables on subcell mesh\n  //  - mesh velocity\n  //  - normal vectors\n\n  const auto subcell_inertial_coords = (*grid_to_inertial_map)(\n      logical_to_grid_map(subcell_logical_coords), time, functions_of_time);\n\n  using SpacetimeMetric = gr::Tags::SpacetimeMetric<DataVector, 3>;\n  using Pi = gh::Tags::Pi<DataVector, 3>;\n  using Phi = gh::Tags::Phi<DataVector, 3>;\n  using RestMassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using LorentzFactor = hydro::Tags::LorentzFactor<DataVector>;\n  using SpatialVelocity = hydro::Tags::SpatialVelocity<DataVector, 3>;\n  using MagneticField = hydro::Tags::MagneticField<DataVector, 3>;\n  using DivergenceCleaningField =\n      hydro::Tags::DivergenceCleaningField<DataVector>;\n\n  std::optional<tnsr::I<DataVector, 3>> volume_mesh_velocity{};\n\n  typename evolution::dg::Tags::NormalCovectorAndMagnitude<3>::type\n      normal_vectors{};\n  for (const auto& direction : Direction<3>::all_directions()) {\n    const auto coordinate_map =\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<3>{});\n    const auto moving_mesh_map =\n        domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<3>{});\n\n    using inverse_spatial_metric_tag =\n        typename System::inverse_spatial_metric_tag;\n    const Mesh<2> face_mesh = subcell_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<3>, tnsr::i<DataVector, 3, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, 3, Frame::Inertial> unnormalized_covector{};\n    for (size_t i = 0; i < 3; ++i) {\n      unnormalized_covector.get(i) =\n          coordinate_map.inv_jacobian(face_logical_coords)\n              .get(direction.dimension(), i);\n    }\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        inverse_spatial_metric_tag,\n        evolution::dg::Actions::detail::NormalVector<3>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n    fields_on_face.assign_subset(\n        solution.variables(coordinate_map(face_logical_coords), time,\n                           tmpl::list<inverse_spatial_metric_tag>{}));\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<\n            System>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, moving_mesh_map);\n  }\n\n  Variables<typename System::primitive_variables_tag::tags_list>\n      volume_prim_vars{subcell_mesh.number_of_grid_points()};\n\n  get(get<RestMassDensity>(volume_prim_vars)) = 1.0;\n  get(get<ElectronFraction>(volume_prim_vars)) = 0.1;\n  get(get<Temperature>(volume_prim_vars)) = 0.5;\n  get(get<Pressure>(volume_prim_vars)) =\n      get(solution.equation_of_state().pressure_from_density(\n          get<RestMassDensity>(volume_prim_vars)));\n  get(get<LorentzFactor>(volume_prim_vars)) = 2.0;\n  for (size_t i = 0; i < 3; ++i) {\n    get<SpatialVelocity>(volume_prim_vars).get(i) = 0.1;\n    get<MagneticField>(volume_prim_vars).get(i) = 0.5;\n  }\n  get(get<DivergenceCleaningField>(volume_prim_vars)) = 1e-2;\n\n  using Vlo =\n      typename VariableFixing::FixToAtmosphere<3>::VelocityLimitingOptions;\n  const VariableFixing::FixToAtmosphere<3> variable_fixer{\n      1.e-12, 3.e-12, Vlo{0.0, 1.e-4, 3.e-12, 1.e-11}, std::nullopt};\n\n  // create a box for test\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<EvolutionMetaVars<System>>,\n      domain::Tags::Domain<3>, domain::Tags::ExternalBoundaryConditions<3>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::ElementLogical>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<3>,\n      fd::Tags::Reconstructor<System>, domain::Tags::MeshVelocity<3>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<3>, ::Tags::Time,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::ElementMap<3, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                  Frame::Inertial>,\n      typename System::primitive_variables_tag,\n      ::Tags::VariableFixer<::VariableFixing::FixToAtmosphere<3>>,\n      hydro::Tags::GrmhdEquationOfState>>(\n      EvolutionMetaVars<System>{}, std::move(domain),\n      std::move(boundary_conditions), subcell_mesh, subcell_logical_coords,\n      neighbor_data,\n      std::unique_ptr<fd::Reconstructor<System>>{\n          std::make_unique<ReconstructorForTest>()},\n      volume_mesh_velocity, normal_vectors, time,\n      clone_unique_ptrs(functions_of_time),\n      ElementMap<3, Frame::Grid>{\n          ElementId<3>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<3>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{}),\n      volume_prim_vars, variable_fixer,\n      solution.equation_of_state().promote_to_3d_eos());\n\n  // compute FD ghost data and retrieve the result\n  fd::BoundaryConditionGhostData<System>::apply(\n      make_not_null(&box), element, ReconstructorForTest{});\n  const auto direction = Direction<3>::upper_xi();\n  const DirectionalId<3> mortar_id{direction,\n                                   ElementId<3>::external_boundary_id()};\n  const DataVector& fd_ghost_data =\n      get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box)\n          .at(mortar_id)\n          .neighbor_ghost_data_for_reconstruction();\n\n  // Copy the computed FD ghost data into a Variables object in order to\n  // facilitate comparison. Note that the returned FD ghost data contains the\n  // product of the lorentz factor and spatial velocity, Wv^i, not each of\n  // those separately.\n  using LorentzFactorTimesSpatialVelocity =\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n  using prims_to_reconstruct = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n  const size_t num_face_pts{\n      subcell_mesh.extents().slice_away(direction.dimension()).product()};\n  Variables<prims_to_reconstruct> fd_ghost_vars{ghost_zone_size * num_face_pts};\n  std::copy(fd_ghost_data.begin(), fd_ghost_data.end(), fd_ghost_vars.data());\n\n  //\n  // now test each boundary conditions\n  //\n  if (typeid(BoundaryConditionType) ==\n      typeid(BoundaryConditions::DirichletAnalytic<System>)) {\n    const auto ghost_logical_coords =\n        evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n            subcell_mesh, ghost_zone_size, direction);\n    const auto ghost_inertial_coords = (*grid_to_inertial_map)(\n        logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n\n    const auto [expected_rest_mass_density, expected_electron_fraction,\n                expected_Temperature, expected_lorentz_factor,\n                expected_spatial_velocity, expected_magnetic_field,\n                expected_div_cleaning_field, expected_spacetime_metric,\n                expected_pi, expected_phi] =\n        solution.variables(\n            ghost_inertial_coords, time,\n            tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                       LorentzFactor, SpatialVelocity, MagneticField,\n                       DivergenceCleaningField, SpacetimeMetric, Pi, Phi>{});\n\n    // check values\n    CHECK_ITERABLE_APPROX(get<RestMassDensity>(fd_ghost_vars),\n                          expected_rest_mass_density);\n    CHECK_ITERABLE_APPROX(get<ElectronFraction>(fd_ghost_vars),\n                          expected_electron_fraction);\n    CHECK_ITERABLE_APPROX(get<Temperature>(fd_ghost_vars),\n                          expected_Temperature);\n    {\n      tnsr::I<DataVector, 3> expected_lorentz_factor_times_spatial_velocity{\n          ghost_zone_size * num_face_pts};\n      for (size_t i = 0; i < 3; ++i) {\n        expected_lorentz_factor_times_spatial_velocity.get(i) =\n            get(expected_lorentz_factor) * expected_spatial_velocity.get(i);\n      }\n      CHECK_ITERABLE_APPROX(\n          get<LorentzFactorTimesSpatialVelocity>(fd_ghost_vars),\n          expected_lorentz_factor_times_spatial_velocity);\n    }\n    CHECK_ITERABLE_APPROX(get<MagneticField>(fd_ghost_vars),\n                          expected_magnetic_field);\n    CHECK_ITERABLE_APPROX(get<DivergenceCleaningField>(fd_ghost_vars),\n                          expected_div_cleaning_field);\n    CHECK_ITERABLE_APPROX(get<SpacetimeMetric>(fd_ghost_vars),\n                          expected_spacetime_metric);\n    CHECK_ITERABLE_APPROX(get<Pi>(fd_ghost_vars), expected_pi);\n    CHECK_ITERABLE_APPROX(get<Phi>(fd_ghost_vars), expected_phi);\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Fd.BCondGhostData\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/\"};\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  const SolutionForTest solution{RelativisticEuler::Solutions::TovStar{\n      1.28e-3, EquationsOfState::PolytropicFluid<true>{100.0, 2.0}.get_clone(),\n      RelativisticEuler::Solutions::TovCoordinates::Schwarzschild}};\n  test<System>(\n      grmhd::GhValenciaDivClean::BoundaryConditions::DirichletAnalytic<\n          System>{std::make_unique<SolutionForTest>(solution)},\n      solution);\n  CHECK_THROWS_WITH(test<System>(\n                        grmhd::GhValenciaDivClean::BoundaryConditions::\n                            ConstraintPreservingFreeOutflow{},\n                        solution),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Not implemented because it's not trivial \"\n                        \"to figure out what the right way of\"));\n\n// check that the periodic BC fails\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      test<System>(\n          domain::BoundaryConditions::Periodic<\n              BoundaryConditions::BoundaryCondition>{},\n          solution),\n      Catch::Matchers::ContainsSubstring(\"not on external boundaries\"));\n#endif\n}\n}  // namespace\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Test_Derivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Derivatives.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Fd.Derivatives\",\n    \"[Unit][Evolution]\") {\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n  const size_t points_per_dimension = 5;\n  const size_t ghost_zone_size = 3;\n  const size_t fd_deriv_order = 4;\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  const auto logical_coords = TestHelpers::grmhd::GhValenciaDivClean::fd::\n      detail::set_logical_coordinates(subcell_mesh);\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_jacobian{\n          subcell_mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    cell_centered_logical_to_inertial_inv_jacobian.get(i, i) = 1.0;\n  }\n\n  const Element<3> element =\n      TestHelpers::grmhd::GhValenciaDivClean::fd::detail::set_element();\n\n  const DirectionalIdMap<3, evolution::dg::subcell::GhostData> all_ghost_data =\n      TestHelpers::grmhd::GhValenciaDivClean::fd::detail::compute_ghost_data(\n          subcell_mesh, logical_coords, element.neighbors(), ghost_zone_size,\n          TestHelpers::grmhd::GhValenciaDivClean::fd::detail::\n              compute_prim_solution);\n  const auto volume_prims_for_recons =\n      TestHelpers::grmhd::GhValenciaDivClean::fd::detail::compute_prim_solution(\n          logical_coords);\n  Variables<typename System::variables_tag::tags_list>\n      volume_evolved_vars{subcell_mesh.number_of_grid_points()};\n  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_evolved_vars) =\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_prims_for_recons);\n  get<gh::Tags::Phi<DataVector, 3>>(volume_evolved_vars) =\n      get<gh::Tags::Phi<DataVector, 3>>(volume_prims_for_recons);\n  get<gh::Tags::Pi<DataVector, 3>>(volume_evolved_vars) =\n      get<gh::Tags::Pi<DataVector, 3>>(volume_prims_for_recons);\n\n  Variables<db::wrap_tags_in<Tags::deriv,\n                             typename System::gradients_tags,\n                             tmpl::size_t<3>, Frame::Inertial>>\n      deriv_of_gh_vars{subcell_mesh.number_of_grid_points()};\n\n  grmhd::GhValenciaDivClean::fd::spacetime_derivatives<System>(\n      make_not_null(&deriv_of_gh_vars), volume_evolved_vars, all_ghost_data,\n      fd_deriv_order, subcell_mesh,\n      cell_centered_logical_to_inertial_inv_jacobian);\n\n  Variables<db::wrap_tags_in<Tags::deriv,\n                             typename System::gradients_tags,\n                             tmpl::size_t<3>, Frame::Inertial>>\n      expected_deriv_of_gh_vars{subcell_mesh.number_of_grid_points()};\n\n  auto& expected_d_metric =\n      get<::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                        tmpl::size_t<3>, Frame::Inertial>>(\n          expected_deriv_of_gh_vars);\n  get<0, 0, 0>(expected_d_metric) = get<1, 0, 0>(expected_d_metric) =\n      get<2, 0, 0>(expected_d_metric) = 0.0;\n  get<0, 1, 0>(expected_d_metric) = get<1, 1, 0>(expected_d_metric) =\n      get<2, 1, 0>(expected_d_metric) = 0.001;\n  get<0, 2, 0>(expected_d_metric) = get<1, 2, 0>(expected_d_metric) =\n      get<2, 2, 0>(expected_d_metric) = 0.002;\n  get<0, 3, 0>(expected_d_metric) = get<1, 3, 0>(expected_d_metric) =\n      get<2, 3, 0>(expected_d_metric) = 0.003;\n  get<0, 1, 1>(expected_d_metric) = get<1, 1, 1>(expected_d_metric) =\n      get<2, 1, 1>(expected_d_metric) = 0.002;\n  get<0, 2, 1>(expected_d_metric) = get<1, 2, 1>(expected_d_metric) =\n      get<2, 2, 1>(expected_d_metric) = 0.004;\n  get<0, 2, 2>(expected_d_metric) = get<1, 2, 2>(expected_d_metric) =\n      get<2, 2, 2>(expected_d_metric) = 0.006;\n  get<0, 3, 1>(expected_d_metric) = get<1, 3, 1>(expected_d_metric) =\n      get<2, 3, 1>(expected_d_metric) = 0.006;\n  get<0, 2, 2>(expected_d_metric) = get<1, 2, 2>(expected_d_metric) =\n      get<2, 2, 2>(expected_d_metric) = 0.006;\n  get<0, 3, 2>(expected_d_metric) = get<1, 3, 2>(expected_d_metric) =\n      get<2, 3, 2>(expected_d_metric) = 0.009;\n  get<0, 3, 3>(expected_d_metric) = get<1, 3, 3>(expected_d_metric) =\n      get<2, 3, 3>(expected_d_metric) = 0.012;\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                         tmpl::size_t<3>, Frame::Inertial>>(deriv_of_gh_vars)),\n      expected_d_metric);\n\n  auto& expected_d_pi =\n      get<::Tags::deriv<gh::Tags::Pi<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(expected_deriv_of_gh_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t b = a; b < 4; ++b) {\n        expected_d_pi.get(i, a, b) = 500.0 * a + 10000.0 * b + 1.0 + i;\n      }\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<gh::Tags::Pi<DataVector, 3>, tmpl::size_t<3>,\n                         Frame::Inertial>>(deriv_of_gh_vars)),\n      expected_d_pi);\n\n  auto& expected_d_phi =\n      get<::Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(expected_deriv_of_gh_vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t a = 0; a < 4; ++a) {\n        for (size_t b = a; b < 4; ++b) {\n          expected_d_phi.get(i, j, a, b) =\n              i == j ? (50.0 * a + 1000.0 * b + 10.0 * i + 1.0) : 0.0;\n        }\n      }\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                         Frame::Inertial>>(deriv_of_gh_vars)),\n      expected_d_phi);\n\n  // Test ASSERT triggers for incorrect neighbor size.\n#ifdef SPECTRE_DEBUG\n  for (const auto& direction : Direction<3>::all_directions()) {\n    const DirectionalId<3> directional_element_id{\n        direction, *element.neighbors().at(direction).begin()};\n    DirectionalIdMap<3, evolution::dg::subcell::GhostData> bad_ghost_data =\n        all_ghost_data;\n    DataVector& neighbor_data = bad_ghost_data.at(directional_element_id)\n                                    .neighbor_ghost_data_for_reconstruction();\n    neighbor_data = DataVector{2};\n    const std::string match_string{\n        MakeString{}\n        << \"Amount of reconstruction data sent (\" << neighbor_data.size()\n        << \") from \" << directional_element_id\n        << \" is not a multiple of the number of reconstruction variables \"\n        << Variables<grmhd::GhValenciaDivClean::Tags::\n                         primitive_grmhd_and_spacetime_reconstruction_tags>::\n               number_of_independent_components};\n    CHECK_THROWS_WITH(grmhd::GhValenciaDivClean::fd::spacetime_derivatives<\n                          System>(\n                          make_not_null(&deriv_of_gh_vars), volume_evolved_vars,\n                          bad_ghost_data, fd_deriv_order, subcell_mesh,\n                          cell_centered_logical_to_inertial_inv_jacobian),\n                      Catch::Matchers::ContainsSubstring(match_string));\n  }\n#endif  // SPECTRE_DEBUG\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Test_FillNeighborSpacetimeVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FillNeighborSpacetimeVariables.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Fd.\"\n    \"FillNeighborSpacetimeVariables\",\n    \"[Unit][Evolution]\") {\n  const size_t points_per_dimension = 5;\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n\n  using NeighborVariables =\n      Variables<grmhd::GhValenciaDivClean::Tags::\n                    primitive_grmhd_and_spacetime_reconstruction_tags>;\n  using gh_tags =\n      grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags;\n  constexpr size_t number_of_gh_components = Variables<\n      grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>::\n      number_of_independent_components;\n\n  DirectionalIdMap<3, evolution::dg::subcell::GhostData> neighbor_data{};\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist(-5.0, 5.0);\n  for (const auto& direction : Direction<3>::all_directions()) {\n    const auto neighbor_vars = make_with_random_values<NeighborVariables>(\n        make_not_null(&gen), make_not_null(&dist),\n        subcell_mesh.number_of_grid_points());\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars), subcell_mesh.extents(), 2,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    const auto key = DirectionalId<3>{direction, ElementId<3>{0}};\n    neighbor_data[key] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[key].neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n\n  DirectionMap<3, gsl::span<const double>> result{};\n  grmhd::GhValenciaDivClean::fd::fill_neighbor_spacetime_variables<\n      NeighborVariables, tmpl::front<gh_tags>>(\n      make_not_null(&result), neighbor_data, number_of_gh_components);\n\n  for (const auto& direction : Direction<3>::all_directions()) {\n    const auto key = DirectionalId<3>{direction, ElementId<3>{0}};\n    auto& result_in_dir = result[direction];\n    auto& ghost_data_in_dir =\n        (neighbor_data[key].neighbor_ghost_data_for_reconstruction());\n    const size_t neighbor_number_of_points =\n        ghost_data_in_dir.size() /\n        NeighborVariables::number_of_independent_components;\n\n    const NeighborVariables\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        view{const_cast<double*>(ghost_data_in_dir.data()),\n             neighbor_number_of_points *\n                 NeighborVariables::number_of_independent_components};\n    const auto subset_view = view.extract_subset<gh_tags>();\n    const auto compare = subset_view.data();\n    for (size_t element = 0;\n         element < number_of_gh_components * neighbor_number_of_points;\n         ++element) {\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      CHECK(result_in_dir[element] == *(compare + element));\n    };\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Test_FilterOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FilterOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Fd.FilterOptions\",\n    \"[Unit][Evolution]\") {\n  CHECK(not serialize_and_deserialize(\n                TestHelpers::test_creation<\n                    grmhd::GhValenciaDivClean::fd::FilterOptions>(\n                    \"SpacetimeDissipation: None\"))\n                .spacetime_dissipation.has_value());\n  CHECK(serialize_and_deserialize(\n            TestHelpers::test_creation<\n                grmhd::GhValenciaDivClean::fd::FilterOptions>(\n                \"SpacetimeDissipation: 0.1\"))\n            .spacetime_dissipation.value() == 0.1);\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::GhValenciaDivClean::fd::FilterOptions>(\n          \"SpacetimeDissipation: 0.0\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Spacetime dissipation must be between 0 and 1, but got\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::GhValenciaDivClean::fd::FilterOptions>(\n          \"SpacetimeDissipation: 1.0\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Spacetime dissipation must be between 0 and 1, but got\"));\n\n  CHECK(serialize_and_deserialize(\n            TestHelpers::test_option_tag<\n                grmhd::GhValenciaDivClean::fd::OptionTags::FilterOptions>(\n                \"SpacetimeDissipation: 0.1\"))\n            .spacetime_dissipation.value() == 0.1);\n  TestHelpers::db::test_simple_tag<\n      grmhd::GhValenciaDivClean::fd::Tags::FilterOptions>(\"FilterOptions\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Test_Filters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Filters.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid set_polynomial(\n    const gsl::not_null<std::vector<DataVector>*> vars_ptr,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& local_logical_coords,\n    const size_t degree) {\n  for (auto& var : *vars_ptr) {\n    var = 0.0;\n    for (size_t i = 0; i < Dim; ++i) {\n      var += pow(local_logical_coords.get(i), degree);\n    }\n  }\n}\n\ntemplate <typename System>\nvoid set_solution(\n    const gsl::not_null<Variables<typename System::variables_tag::tags_list>*>\n        volume_vars,\n    const gsl::not_null<DirectionalIdMap<3, evolution::dg::subcell::GhostData>*>\n        neighbor_data,\n    const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& logical_coords,\n    const size_t deriv_order, const size_t degree) {\n  const auto set_data_vectors =\n      [](const gsl::not_null<std::vector<DataVector>*> local_dvs,\n         const auto local_vars) {\n        for (size_t i = 0; i < 10; ++i) {\n          (*local_dvs)[i].set_data_ref(make_not_null(\n              &get<gr::Tags::SpacetimeMetric<DataVector, 3>>(*local_vars)[i]));\n        }\n        for (size_t i = 0; i < 10; ++i) {\n          (*local_dvs)[i + 10].set_data_ref(\n              make_not_null(&get<gh::Tags::Pi<DataVector, 3>>(*local_vars)[i]));\n        }\n        for (size_t i = 0; i < 30; ++i) {\n          (*local_dvs)[i + 20].set_data_ref(make_not_null(\n              &get<gh::Tags::Phi<DataVector, 3>>(*local_vars)[i]));\n        }\n      };\n  std::vector<DataVector> vars(50);\n  set_data_vectors(make_not_null(&vars), volume_vars);\n  set_polynomial(&vars, logical_coords, degree);\n\n  for (const auto& direction : Direction<3>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    std::vector<DataVector> neighbor_dvs(50);\n    Variables<grmhd::GhValenciaDivClean::Tags::\n                  primitive_grmhd_and_spacetime_reconstruction_tags>\n        neighbor_vars{mesh.number_of_grid_points()};\n    set_data_vectors(make_not_null(&neighbor_dvs),\n                     make_not_null(&neighbor_vars));\n    set_polynomial(&neighbor_dvs, neighbor_logical_coords, degree);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars), mesh.extents(), deriv_order / 2 + 1,\n        std::unordered_set{direction.opposite()}, 0, {});\n    CAPTURE(deriv_order / 2 + 1);\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    const auto key = DirectionalId<3>{direction, ElementId<3>{0}};\n    (*neighbor_data)[key] = evolution::dg::subcell::GhostData{1};\n    (*neighbor_data)[key].neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Fd.Filters\",\n                  \"[Unit][Evolution]\") {\n  const size_t points_per_dimension = 5;\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  const auto logical_coords = TestHelpers::grmhd::GhValenciaDivClean::fd::\n      detail::set_logical_coordinates(subcell_mesh);\n\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  Variables<typename grmhd::GhValenciaDivClean::System<\n      NeutrinoTransportSystem>::variables_tag::tags_list>\n      result{subcell_mesh.number_of_grid_points(), 0.0};\n  Variables<typename grmhd::GhValenciaDivClean::System<\n      NeutrinoTransportSystem>::variables_tag::tags_list>\n      volume_evolved_variables{subcell_mesh.number_of_grid_points()};\n\n  DirectionalIdMap<3, evolution::dg::subcell::GhostData>\n      neighbor_data_for_reconstruction{};\n\n  set_solution<System>(&volume_evolved_variables,\n                       &neighbor_data_for_reconstruction, subcell_mesh,\n                       logical_coords, 4, 3);\n\n  grmhd::GhValenciaDivClean::fd::spacetime_kreiss_oliger_filter(\n      make_not_null(&result), volume_evolved_variables,\n      neighbor_data_for_reconstruction, subcell_mesh, 4, 1.0);\n\n  tmpl::for_each<\n      grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags>(\n      [&result, &volume_evolved_variables](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        auto& result_tensor = get<tag>(result);\n        auto& volume_tensor = get<tag>(volume_evolved_variables);\n        Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n        for (size_t tensor_index = 0; tensor_index < result_tensor.size();\n             ++tensor_index) {\n          result_tensor[tensor_index] -= volume_tensor[tensor_index];\n          CHECK_ITERABLE_CUSTOM_APPROX(\n              result_tensor[tensor_index],\n              DataVector(result_tensor[tensor_index].size(), 0.0),\n              custom_approx);\n        }\n      });\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Test_MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Fd.MonotonisedCentralPrim\",\n    \"[Unit][Evolution]\") {\n  namespace helpers = TestHelpers::grmhd::GhValenciaDivClean::fd;\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  PUPable_reg(SINGLE_ARG(grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<\n                         System>));\n  const auto mc_from_options_base = TestHelpers::test_factory_creation<\n      grmhd::GhValenciaDivClean::fd::Reconstructor<System>,\n      grmhd::GhValenciaDivClean::fd::OptionTags::Reconstructor<System>>(\n      \"MonotonisedCentralPrim:\\n\"\n      \"  AtmosphereTreatment: Never\\n\"\n      \"  ReconstructRhoTimesTemperature: false\\n\");\n  const auto mc_deserialized = serialize_and_deserialize(mc_from_options_base);\n  auto* const mc_from_options =\n      dynamic_cast<const grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<\n          System>*>(mc_deserialized.get());\n  REQUIRE(mc_from_options != nullptr);\n  CHECK(\n      grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Always, false} !=\n      grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never, false});\n  CHECK(\n      grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Always, false} !=\n      grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Always, true});\n  CHECK(*mc_from_options ==\n        grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>{\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Never, false});\n  test_move_semantics(\n      grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never, false},\n      grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>{\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never, false});\n\n  helpers::test_prim_reconstructor(5, *mc_from_options);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Test_PositivityPreservingAdaptiveOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LagrangePolynomial.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Tag>\nstruct value_tag;\n\ntemplate <template <typename, size_t, typename...> typename Tag>\nstruct value_tag<Tag<DataVector, 3>> {\n  using type = Tag<double, 3>;\n};\n\ntemplate <typename Tag>\nusing value_tag_t = typename value_tag<Tag>::type;\n\ntemplate <typename Reconstructor>\nvoid test_neighbor_positivity(const Reconstructor& reconstructor) {\n  using positive_tags = tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                                   hydro::Tags::ElectronFraction<DataVector>,\n                                   hydro::Tags::Temperature<DataVector>>;\n  using non_positive_tags =\n      tmpl::list<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n  using spacetime_tags =\n      ::grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags;\n\n  DirectionMap<3, Neighbors<3>> element_neighbors{};\n  element_neighbors[Direction<3>::upper_xi()] =\n      Neighbors<3>{{ElementId<3>{1, {}}}, OrientationMap<3>::create_aligned()};\n  element_neighbors[Direction<3>::lower_xi()] =\n      Neighbors<3>{{ElementId<3>{2, {}}}, OrientationMap<3>::create_aligned()};\n  const Element<3> element{ElementId<3>{0, {}}, std::move(element_neighbors)};\n\n  const Mesh<3> subcell_mesh{{{11, 1, 1}},\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n\n  tmpl::wrap<tmpl::transform<spacetime_tags, value_tag<tmpl::_1>>,\n             tuples::TaggedTuple>\n      spacetime_values{};\n  {\n    auto& spacetime_metric =\n        get<gr::Tags::SpacetimeMetric<double, 3>>(spacetime_values);\n    std::fill(spacetime_metric.begin(), spacetime_metric.end(), 0.0);\n    get<0, 0>(spacetime_metric) = -1.0;\n    for (size_t i = 1; i < 4; ++i) {\n      spacetime_metric.get(i, i) = 1.0;\n    }\n    auto& pi = get<gh::Tags::Pi<double, 3>>(spacetime_values);\n    std::fill(pi.begin(), pi.end(), 0.0);\n    auto& phi = get<gh::Tags::Phi<double, 3>>(spacetime_values);\n    std::fill(phi.begin(), phi.end(), 0.0);\n  }\n\n  Variables<spacetime_tags> volume_spacetime_vars{\n      subcell_mesh.number_of_grid_points()};\n  tmpl::for_each<spacetime_tags>([&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n    const auto& value = get<value_tag_t<Tag>>(spacetime_values);\n    std::copy(value.begin(), value.end(),\n              get<Tag>(volume_spacetime_vars).begin());\n  });\n\n  const double atmosphere = 1.0e-15;\n  const Variables<hydro::grmhd_tags<DataVector>> volume_prims{\n      subcell_mesh.number_of_grid_points(), atmosphere};\n\n  const auto ghost_zone_size = reconstructor.ghost_zone_size();\n  std::vector<double> ghost_values{};\n  ghost_values.reserve(ghost_zone_size);\n  ASSERT(ghost_zone_size > 1, \"Not supported by test\");\n  {\n    // Construct ghost data so polynomial interpolation will give\n    // -3*atmosphere at the boundary.  We choose the\n    // 2*ghost_zone_size-1 points on a polynomial defined by\n    // 2*ghost_zone_size-2 control points, as this will trick the\n    // Persson TCI into thinking the solution is smooth.\n    std::vector<double> points{};\n    points.reserve(2 * ghost_zone_size - 2);\n    std::vector<double> values{};\n    values.reserve(2 * ghost_zone_size - 2);\n    points.push_back(-0.5);\n    values.push_back(-3.0 * atmosphere);\n\n    for (size_t i = 1; i < ghost_zone_size; ++i) {\n      points.push_back(-static_cast<double>(i));\n      values.push_back(atmosphere);\n    }\n\n    for (size_t i = 0; i < ghost_zone_size - 2; ++i) {\n      points.push_back(static_cast<double>(i));\n      values.push_back(atmosphere);\n      ghost_values.push_back(atmosphere);\n    }\n\n    for (const double ghost_x : {static_cast<double>(ghost_zone_size - 2),\n                                 static_cast<double>(ghost_zone_size - 1)}) {\n      double ghost_value = 0.0;\n      for (size_t i = 0; i < points.size(); ++i) {\n        ghost_value +=\n            values[i] *\n            lagrange_polynomial(i, ghost_x, points.begin(), points.end());\n      }\n      ghost_values.push_back(ghost_value);\n    }\n  }\n\n  using GhostData = evolution::dg::subcell::GhostData;\n  DirectionalIdMap<3, GhostData> ghost_data{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    const DirectionalId<3> mortar_id{direction, *neighbors.begin()};\n    Variables<::grmhd::GhValenciaDivClean::Tags::\n                  primitive_grmhd_and_spacetime_reconstruction_tags>\n        ghost_vars{};\n    auto& mortar_data = ghost_data[mortar_id];\n    mortar_data = GhostData{1};\n    mortar_data.neighbor_ghost_data_for_reconstruction() = DataVector{\n        ghost_vars.number_of_independent_components * ghost_zone_size};\n    ghost_vars.set_data_ref(\n        mortar_data.neighbor_ghost_data_for_reconstruction().data(),\n        mortar_data.neighbor_ghost_data_for_reconstruction().size());\n    DataVector ghost_component{ghost_zone_size};\n    if (direction.side() == Side::Upper) {\n      std::copy(ghost_values.begin(), ghost_values.end(),\n                ghost_component.begin());\n    } else {\n      std::copy(ghost_values.rbegin(), ghost_values.rend(),\n                ghost_component.begin());\n    }\n    tmpl::for_each<tmpl::append<positive_tags, non_positive_tags>>(\n        [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n          auto& tensor = get<Tag>(ghost_vars);\n          std::fill(tensor.begin(), tensor.end(), ghost_component);\n        });\n    tmpl::for_each<spacetime_tags>(\n        [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n          const auto& value = get<value_tag_t<Tag>>(spacetime_values);\n          std::copy(value.begin(), value.end(), get<Tag>(ghost_vars).begin());\n        });\n  }\n\n  const EquationsOfState::IdealFluid<true> eos{1.4};\n  const VariableFixing::FixToAtmosphere<3> fix_to_atmosphere{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    CAPTURE(direction);\n    Variables<\n        ::grmhd::GhValenciaDivClean::fd::tags_list_for_reconstruct_fd_neighbor>\n        vars_on_mortar{1};\n    reconstructor.reconstruct_fd_neighbor(\n        make_not_null(&vars_on_mortar), volume_prims, volume_spacetime_vars,\n        eos, element, ghost_data, subcell_mesh, fix_to_atmosphere, direction);\n\n    tmpl::for_each<positive_tags>([&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n      CAPTURE(pretty_type::name<Tag>());\n      CHECK(get(get<Tag>(vars_on_mortar))[0] > 0.0);\n    });\n\n    // Check that the test is actually testing something.\n    tmpl::for_each<non_positive_tags>(\n        [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n          CAPTURE(pretty_type::name<Tag>());\n          for (const auto& component : get<Tag>(vars_on_mortar)) {\n            CHECK(component[0] < 0.0);\n          }\n        });\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Fd.Ppao\",\n                  \"[Unit][Evolution]\") {\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  namespace helpers = TestHelpers::grmhd::GhValenciaDivClean::fd;\n  PUPable_reg(SINGLE_ARG(\n      grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n          System>));\n  const auto ppao_from_options_base = TestHelpers::test_factory_creation<\n      grmhd::GhValenciaDivClean::fd::Reconstructor<System>,\n      grmhd::GhValenciaDivClean::fd::OptionTags::Reconstructor<System>>(\n      \"PositivityPreservingAdaptiveOrderPrim:\\n\"\n      \"  Alpha5: 3.7\\n\"\n      \"  Alpha7: None\\n\"\n      \"  Alpha9: None\\n\"\n      \"  LowOrderReconstructor: MonotonisedCentral\\n\"\n      \"  AtmosphereTreatment: Never\\n\"\n      \"  ReconstructRhoTimesTemperature: true\\n\");\n  const auto ppao_deserialized =\n      serialize_and_deserialize(ppao_from_options_base);\n  auto* const ppao_from_options = dynamic_cast<\n      const grmhd::GhValenciaDivClean::fd::\n          PositivityPreservingAdaptiveOrderPrim<System>*>(\n      ppao_deserialized.get());\n  REQUIRE(ppao_from_options != nullptr);\n  CHECK(grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n            System>{\n            3.7, std::nullopt, std::nullopt,\n            fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n            false} !=\n        grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n            System>{\n            3.7, std::nullopt, std::nullopt,\n            fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Never, false});\n  CHECK(\n      grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n          System>{\n          3.7, std::nullopt, std::nullopt,\n          fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Always, false} !=\n      grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n          System>{\n          3.8, std::nullopt, std::nullopt,\n          fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Always, false});\n  // Can't use high-order reconstruction yet. We'll enable these tests later.\n  //\n  // CHECK(\n  //     grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n  //         System>{\n  //         3.7, std::nullopt, std::nullopt,\n  //         fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n  //         ::VariableFixing::FixReconstructedStateToAtmosphere::Always, false}\n  //         !=\n  //     grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n  //         System>{\n  //         3.7, 3.5, std::nullopt,\n  //         fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n  //         ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n  //         false});\n  // CHECK(\n  //     grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n  //         System>{\n  //         3.7, std::nullopt, std::nullopt,\n  //         fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n  //         ::VariableFixing::FixReconstructedStateToAtmosphere::Always, false}\n  //         !=\n  //     grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n  //         System>{\n  //         3.7, std::nullopt, 3.6,\n  //         fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n  //         ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n  //         false});\n  CHECK(grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n            System>{\n            3.7, std::nullopt, std::nullopt,\n            fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n            false} !=\n        grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n            System>{\n            3.7, std::nullopt, std::nullopt,\n            fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always, true});\n  CHECK(*ppao_from_options ==\n        grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n            System>{\n            3.7, std::nullopt, std::nullopt,\n            fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Never, true});\n  test_move_semantics(\n      grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n          System>{\n          3.7, std::nullopt, std::nullopt,\n          fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never, true},\n      grmhd::GhValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim<\n          System>{\n          3.7, std::nullopt, std::nullopt,\n          fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never, true});\n  helpers::test_prim_reconstructor(10, *ppao_from_options);\n\n  test_neighbor_positivity(*ppao_from_options);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Test_Wcns5z.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Wcns5z.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Fd.Wcns5z\",\n                  \"[Unit][Evolution]\") {\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  namespace helpers = TestHelpers::grmhd::GhValenciaDivClean::fd;\n  PUPable_reg(SINGLE_ARG(\n      grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>));\n  const auto wcns5z_from_options_base = TestHelpers::test_factory_creation<\n      grmhd::GhValenciaDivClean::fd::Reconstructor<System>,\n      grmhd::GhValenciaDivClean::fd::OptionTags::Reconstructor<System>>(\n      \"Wcns5zPrim:\\n\"\n      \"  NonlinearWeightExponent: 2\\n\"\n      \"  Epsilon: 1.e-42\\n\"\n      \"  FallbackReconstructor: None\\n\"\n      \"  MaxNumberOfExtrema: 0\\n\"\n      \"  AtmosphereTreatment: Never\\n\"\n      \"  ReconstructRhoTimesTemperature: true\\n\");\n  const auto wcns5z_deserialized =\n      serialize_and_deserialize(wcns5z_from_options_base);\n  auto* const wcns5z_from_options =\n      dynamic_cast<const grmhd::GhValenciaDivClean::fd::Wcns5zPrim<\n          System>*>(wcns5z_deserialized.get());\n  REQUIRE(wcns5z_from_options != nullptr);\n  CHECK(grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n            true} !=\n        grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Never, true});\n  CHECK(grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n            true} !=\n        grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            1, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always, true});\n  CHECK(grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n            true} !=\n        grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 2.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always, true});\n  CHECK(grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n            true} !=\n        grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::Minmod, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always, true});\n  CHECK(grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always,\n            true} !=\n        grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 1,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Always, true});\n  CHECK(*wcns5z_from_options ==\n        grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n            2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n            ::VariableFixing::FixReconstructedStateToAtmosphere::Never, true});\n  test_move_semantics(\n      grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n          2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never, true},\n      grmhd::GhValenciaDivClean::fd::Wcns5zPrim<System>{\n          2, 1.e-42, fd::reconstruction::FallbackReconstructorType::None, 0,\n          ::VariableFixing::FixReconstructedStateToAtmosphere::Never, true});\n  helpers::test_prim_reconstructor(7, *wcns5z_from_options);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef four_velocity_one_form(\n    rest_mass_density,\n    spatial_velocity_one_form,\n    magnetic_field_one_form,\n    magnetic_field_squared,\n    magnetic_field_dot_spatial_velocity,\n    lorentz_factor,\n    one_over_w_squared,\n    pressure,\n    specific_enthalpy,\n    spacetime_metric,\n    shift,\n    lapse,\n):\n    spatial_four_velocity = lorentz_factor * spatial_velocity_one_form\n    return np.insert(\n        spatial_four_velocity,\n        0,\n        np.einsum(\"a,a\", shift, spatial_four_velocity) - lapse * lorentz_factor,\n    )\n\n\ndef comoving_magnetic_field_one_form(\n    rest_mass_density,\n    spatial_velocity_one_form,\n    magnetic_field_one_form,\n    magnetic_field_squared,\n    magnetic_field_dot_spatial_velocity,\n    lorentz_factor,\n    one_over_w_squared,\n    pressure,\n    specific_internal_energy,\n    spacetime_metric,\n    shift,\n    lapse,\n):\n    spatial_comoving_magnetic_field_one_form = (\n        magnetic_field_one_form / lorentz_factor\n        + lorentz_factor\n        * magnetic_field_dot_spatial_velocity\n        * spatial_velocity_one_form\n    )\n\n    return np.insert(\n        spatial_comoving_magnetic_field_one_form,\n        0,\n        np.einsum(\"a,a\", shift, spatial_comoving_magnetic_field_one_form)\n        - lapse * lorentz_factor * magnetic_field_dot_spatial_velocity,\n    )\n\n\ndef trace_reversed_stress_energy(\n    rest_mass_density,\n    spatial_velocity_one_form,\n    magnetic_field_one_form,\n    magnetic_field_squared,\n    magnetic_field_dot_spatial_velocity,\n    lorentz_factor,\n    one_over_w_squared,\n    pressure,\n    specific_internal_energy,\n    spacetime_metric,\n    shift,\n    lapse,\n):\n    local_four_velocity_one_form = four_velocity_one_form(\n        rest_mass_density,\n        spatial_velocity_one_form,\n        magnetic_field_one_form,\n        magnetic_field_squared,\n        magnetic_field_dot_spatial_velocity,\n        lorentz_factor,\n        one_over_w_squared,\n        pressure,\n        specific_internal_energy,\n        spacetime_metric,\n        shift,\n        lapse,\n    )\n    local_comoving_magnetic_field_one_form = comoving_magnetic_field_one_form(\n        rest_mass_density,\n        spatial_velocity_one_form,\n        magnetic_field_one_form,\n        magnetic_field_squared,\n        magnetic_field_dot_spatial_velocity,\n        lorentz_factor,\n        one_over_w_squared,\n        pressure,\n        specific_internal_energy,\n        spacetime_metric,\n        shift,\n        lapse,\n    )\n    modified_enthalpy_times_rest_mass = (\n        rest_mass_density * (1 + specific_internal_energy)\n        + pressure\n        + magnetic_field_squared * one_over_w_squared\n        + magnetic_field_dot_spatial_velocity**2\n    )\n\n    return (\n        modified_enthalpy_times_rest_mass\n        * np.outer(local_four_velocity_one_form, local_four_velocity_one_form)\n        + (0.5 * modified_enthalpy_times_rest_mass - pressure)\n        * spacetime_metric\n        - np.outer(\n            local_comoving_magnetic_field_one_form,\n            local_comoving_magnetic_field_one_form,\n        )\n    )\n\n\ndef add_stress_energy_term_to_dt_pi(trace_reversed_stress_energy, lapse):\n    return 0.1234 - 16.0 * np.pi * lapse * trace_reversed_stress_energy\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/Test_FixConservativesAndComputePrims.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/FixConservativesAndComputePrims.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/MagneticFieldTreatment.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename NeutrinoTransportSystem, typename EquationOfStateType>\nvoid test(const EquationOfStateType& eos) {\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  // Only use 1 grid point to see that we correctly flagged the point as being\n  // fixed. We're really only testing that the mutator calls the correct\n  // functions.\n  const size_t num_pts = 1;\n  tnsr::I<DataVector, 3, Frame::Inertial> subcell_coords{num_pts, 0.0};\n  tnsr::aa<DataVector, 3, Frame::Inertial> spacetime_metric{num_pts, 0.0};\n  get<0, 0>(spacetime_metric) = -1.0;\n  for (size_t i = 1; i < 4; ++i) {\n    spacetime_metric.get(i, i) = 1.0;\n  }\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{num_pts, 0.0};\n  tnsr::II<DataVector, 3, Frame::Inertial> inverse_spatial_metric{num_pts, 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = spacetime_metric.get(i + 1, i + 1);\n    inverse_spatial_metric.get(i, i) = 1.0 / spatial_metric.get(i, i);\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      sqrt(get(determinant(spatial_metric)))};\n\n  const grmhd::ValenciaDivClean::FixConservatives variable_fixer{\n      1.e-7,   1.0e-7,\n      1.0e-10, 1.0e-10,\n      0.0,     0.0,\n      1.e-7,   0.0,\n      true,    hydro::MagneticFieldTreatment::AssumeNonZero};\n  typename System::variables_tag::type cons_vars{num_pts, 0.0};\n  get(get<grmhd::ValenciaDivClean::Tags::TildeD>(cons_vars))[0] = 2.e-12;\n  get(get<grmhd::ValenciaDivClean::Tags::TildeYe>(cons_vars))[0] = 2.e-13;\n  get(get<grmhd::ValenciaDivClean::Tags::TildeTau>(cons_vars))[0] = 1.e-7;\n  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(cons_vars) = spacetime_metric;\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  auto box = db::create<db::AddSimpleTags<\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::Inertial>,\n      grmhd::ValenciaDivClean::Tags::VariablesNeededFixing,\n      typename System::variables_tag, typename System::primitive_variables_tag,\n      ::Tags::VariableFixer<grmhd::ValenciaDivClean::FixConservatives>,\n      hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>>(\n      subcell_coords, false, cons_vars,\n      typename System::primitive_variables_tag::type{num_pts, 1.0e-4},\n      variable_fixer,\n      std::unique_ptr<\n          typename EquationsOfState::get_eos_base<EquationOfStateType>>{\n          std::make_unique<EquationOfStateType>(eos)},\n      primitive_from_conservative_options);\n\n  using recovery_schemes = tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>;\n  db::mutate_apply<\n      grmhd::GhValenciaDivClean::subcell::FixConservativesAndComputePrims<\n          recovery_schemes, System>>(make_not_null(&box));\n\n  // Verify that the conserved variables were fixed\n  CHECK(db::get<grmhd::ValenciaDivClean::Tags::VariablesNeededFixing>(box));\n\n  // Manually do a primitive recovery and see that the values match what's in\n  // the DataBox.\n  typename System::primitive_variables_tag::type expected_prims{num_pts,\n                                                                1.0e-4};\n  grmhd::ValenciaDivClean::PrimitiveFromConservative<recovery_schemes, true>::\n      apply(\n          make_not_null(\n              &get<hydro::Tags::RestMassDensity<DataVector>>(expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::ElectronFraction<DataVector>>(expected_prims)),\n          make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n              expected_prims)),\n          make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n              expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::MagneticField<DataVector, 3>>(expected_prims)),\n          make_not_null(&get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n              expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::LorentzFactor<DataVector>>(expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::Pressure<DataVector>>(expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::Temperature<DataVector>>(expected_prims)),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildePhi>(box), spatial_metric,\n          inverse_spatial_metric, sqrt_det_spatial_metric, eos,\n          primitive_from_conservative_options);\n  CHECK_VARIABLES_APPROX(db::get<typename System::primitive_variables_tag>(box),\n                         expected_prims);\n}\n}  // namespace\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GhValenciaDivClean.Subcell.FixConsAndComputePrims\",\n    \"[Unit][Evolution]\") {\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n\n  test<NeutrinoTransportSystem>(EquationsOfState::Barotropic3D(\n      EquationsOfState::PolytropicFluid<true>(100.0, 2.0)));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/Test_NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FilterOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/Factory.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\nnamespace {\ntemplate <typename System>\ndouble test(const size_t num_dg_pts) {\n  using BoundaryCorrection = BoundaryCorrections::ProductOfCorrections<\n      gh::BoundaryCorrections::UpwindPenalty<3>,\n      ValenciaDivClean::BoundaryCorrections::Hll>;\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const Affine affine_map{-1.0, 1.0, -4.0, 4.0};\n  const ElementId<3> element_id{\n      0, {SegmentId{3, 4}, SegmentId{3, 4}, SegmentId{3, 4}}};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  std::vector<Block<3>> blocks;\n  blocks.emplace_back(Block<3>{\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D{affine_map, affine_map, affine_map}),\n      0,\n      {}});\n  const auto& block = blocks[0];\n  ElementMap<3, Frame::Grid> element_map{\n      element_id, block.is_time_dependent()\n                      ? block.moving_mesh_logical_to_grid_map().get_clone()\n                      : block.stationary_map().get_to_grid_frame()};\n  const auto element = domain::create_initial_element(\n      element_id, blocks,\n      std::vector<std::array<size_t, 3>>{std::array<size_t, 3>{{3, 3, 3}}});\n\n  const auto moving_mesh_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const gh::Solutions::WrappedGr<::RelativisticEuler::Solutions::TovStar> soln{\n      1.28e-3,\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0)\n          ->get_clone(),\n      RelativisticEuler::Solutions::TovCoordinates::Schwarzschild};\n\n  const double time = 0.0;\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const auto dg_coords = moving_mesh_map(\n      element_map(logical_coordinates(dg_mesh)), time, functions_of_time);\n\n  // Neighbor data for reconstruction.\n  //\n  // 0. neighbors coords (our logical coords +2)\n  // 1. compute prims from solution\n  // 2. compute prims needed for reconstruction\n  // 3. set neighbor data\n  evolution::dg::subcell::Tags::GhostDataForReconstruction<3>::type\n      neighbor_data{};\n  using prims_to_reconstruct_tags = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n  for (const Direction<3>& direction : Direction<3>::all_directions()) {\n    auto neighbor_logical_coords = logical_coordinates(subcell_mesh);\n    neighbor_logical_coords.get(direction.dimension()) +=\n        2.0 * direction.sign();\n    auto neighbor_coords = moving_mesh_map(element_map(neighbor_logical_coords),\n                                           time, functions_of_time);\n    const auto neighbor_prims = soln.variables(\n        neighbor_coords, time,\n        tmpl::append<typename System::primitive_variables_tag::tags_list,\n                     typename System::gh_system::variables_tag::tags_list>{});\n    Variables<prims_to_reconstruct_tags> prims_to_reconstruct{\n        subcell_mesh.number_of_grid_points()};\n    prims_to_reconstruct.assign_subset(neighbor_prims);\n    get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n        prims_to_reconstruct) =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(neighbor_prims);\n    for (auto& component :\n         get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n             prims_to_reconstruct)) {\n      component *=\n          get(get<hydro::Tags::LorentzFactor<DataVector>>(neighbor_prims));\n    }\n\n    // Slice data so we can add it to the element's neighbor data\n    DataVector neighbor_data_in_direction =\n        evolution::dg::subcell::slice_data(\n            prims_to_reconstruct, subcell_mesh.extents(),\n            grmhd::ValenciaDivClean::fd::MonotonisedCentralPrim{}\n                .ghost_zone_size(),\n            std::unordered_set{direction.opposite()}, 0, {})\n            .at(direction.opposite());\n\n    const auto key =\n        DirectionalId<3>{direction, *element.neighbors().at(direction).begin()};\n    neighbor_data[key] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[key].neighbor_ghost_data_for_reconstruction() =\n        std::move(neighbor_data_in_direction);\n  }\n\n  DirectionMap<3, std::optional<Variables<\n                      tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                 evolution::dg::Tags::NormalCovector<3>>>>>\n      normal_vectors{};\n  for (const auto& direction : Direction<3>::all_directions()) {\n    using inverse_spatial_metric_tag =\n        typename System::inverse_spatial_metric_tag;\n    const Mesh<2> face_mesh = dg_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<3>, tnsr::i<DataVector, 3, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, 3, Frame::Inertial> unnormalized_covector{};\n    const auto element_logical_to_grid_inv_jac =\n        element_map.inv_jacobian(face_logical_coords);\n    const auto grid_to_inertial_inv_jac = moving_mesh_map.inv_jacobian(\n        element_map(face_logical_coords), time, functions_of_time);\n    InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n        element_logical_to_inertial_inv_jac{};\n    for (size_t logical_i = 0; logical_i < 3; ++logical_i) {\n      for (size_t inertial_i = 0; inertial_i < 3; ++inertial_i) {\n        element_logical_to_inertial_inv_jac.get(logical_i, inertial_i) =\n            element_logical_to_grid_inv_jac.get(logical_i, 0) *\n            grid_to_inertial_inv_jac.get(0, inertial_i);\n        for (size_t grid_i = 1; grid_i < 3; ++grid_i) {\n          element_logical_to_inertial_inv_jac.get(logical_i, inertial_i) +=\n              element_logical_to_grid_inv_jac.get(logical_i, grid_i) *\n              grid_to_inertial_inv_jac.get(grid_i, inertial_i);\n        }\n      }\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      unnormalized_covector.get(i) =\n          element_logical_to_inertial_inv_jac.get(direction.dimension(), i);\n    }\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        inverse_spatial_metric_tag,\n        evolution::dg::Actions::detail::NormalVector<3>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n    fields_on_face.assign_subset(\n        soln.variables(moving_mesh_map(element_map(face_logical_coords), time,\n                                       functions_of_time),\n                       time, tmpl::list<inverse_spatial_metric_tag>{}));\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<System>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, moving_mesh_map);\n  }\n\n  using variables_tag = typename System::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  Variables<typename System::primitive_variables_tag::tags_list> dg_prim_vars{\n      dg_mesh.number_of_grid_points()};\n  dg_prim_vars.assign_subset(soln.variables(\n      dg_coords, time, typename System::primitive_variables_tag::tags_list{}));\n  typename variables_tag::type initial_variables{\n      dg_mesh.number_of_grid_points()};\n  initial_variables.assign_subset(soln.variables(\n      dg_coords, time, typename System::gh_system::variables_tag::tags_list{}));\n\n  const auto gamma1 =  // Gamma1, taken from SpEC BNS\n      std::make_unique<ConstraintDamping::GaussianPlusConstant<3, Frame::Grid>>(\n          -0.999, 0.999 * 1.0,\n          10.0 * 10.0,  // second 10 is \"separation\" of NSes\n          std::array{0.0, 0.0, 0.0});\n  const auto gamma2 =  // Gamma1, taken from SpEC BNS\n      std::make_unique<ConstraintDamping::GaussianPlusConstant<3, Frame::Grid>>(\n          0.01, 1.35 * 1.0 / 1.4, 5.5 * 1.4, std::array{0.0, 0.0, 0.0});\n\n  // Below are also dummy variables required for compilation due to boundary\n  // condition FD ghost data. Since the element used here for testing has\n  // neighbors in all directions, BoundaryConditionGhostData::apply() is not\n  // actually called so it is okay to leave these variables somewhat poorly\n  // initialized.\n  std::optional<tnsr::I<DataVector, 3>> dummy_volume_mesh_velocity{};\n  using DampingFunction = ConstraintDamping::DampingFunction<3, Frame::Grid>;\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Element<3>, domain::Tags::Mesh<3>,\n          evolution::dg::subcell::Tags::Mesh<3>,\n          fd::Tags::Reconstructor<System>, evolution::Tags::BoundaryCorrection,\n          hydro::Tags::GrmhdEquationOfState,\n          typename System::primitive_variables_tag, dt_variables_tag,\n          variables_tag,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<3>,\n          ValenciaDivClean::Tags::ConstraintDampingParameter,\n          domain::Tags::ElementMap<3, Frame::Grid>,\n          domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                      Frame::Inertial>,\n          domain::Tags::MeshVelocity<3, Frame::Inertial>,\n          evolution::dg::Tags::NormalCovectorAndMagnitude<3>, ::Tags::Time,\n          domain::Tags::FunctionsOfTimeInitialize,\n          gh::Tags::DampingFunctionGamma0<3, Frame::Grid>,\n          gh::Tags::DampingFunctionGamma1<3, Frame::Grid>,\n          gh::Tags::DampingFunctionGamma2<3, Frame::Grid>,\n          ::gh::gauges::Tags::GaugeCondition,\n          grmhd::GhValenciaDivClean::fd::Tags::FilterOptions,\n          evolution::dg::subcell::Tags::SubcellOptions<3>,\n          ::Tags::VariableFixer<::VariableFixing::FixToAtmosphere<3>>>,\n      db::AddComputeTags<\n          ::domain::Tags::LogicalCoordinates<3>,\n          ::domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<3, Frame::Grid>,\n              domain::Tags::Coordinates<3, Frame::ElementLogical>>,\n          evolution::dg::subcell::Tags::LogicalCoordinatesCompute<3>,\n          ::domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<3, Frame::Grid>,\n              evolution::dg::subcell::Tags::Coordinates<3,\n                                                        Frame::ElementLogical>,\n              evolution::dg::subcell::Tags::Coordinates>,\n          gr::Tags::SpatialMetricCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::DetAndInverseSpatialMetricCompute<DataVector, 3,\n                                                      Frame::Inertial>,\n          gr::Tags::SqrtDetSpatialMetricCompute<DataVector, 3, Frame::Inertial>,\n          gh::Tags::ConstraintGamma0Compute<3, Frame::Grid>,\n          gh::Tags::ConstraintGamma1Compute<3, Frame::Grid>,\n          gh::Tags::ConstraintGamma2Compute<3, Frame::Grid>>>(\n      element, dg_mesh, subcell_mesh,\n      std::unique_ptr<grmhd::GhValenciaDivClean::fd::Reconstructor<System>>{\n          std::make_unique<\n              grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<BoundaryCorrection>(\n              gh::BoundaryCorrections::UpwindPenalty<3>{},\n              ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8})},\n      soln.equation_of_state().promote_to_3d_eos(), dg_prim_vars,\n      // Set incorrect size for dt variables because they should get resized.\n      Variables<typename dt_variables_tag::tags_list>{}, initial_variables,\n      neighbor_data, 1.0, std::move(element_map), moving_mesh_map.get_clone(),\n      dummy_volume_mesh_velocity, normal_vectors, time,\n      clone_unique_ptrs(functions_of_time),\n      // Note: These damping functions all assume Grid==Inertial. We need to\n      // rescale the widths in the Grid frame for binaries.\n      std::unique_ptr<DampingFunction>(  // Gamma0, taken from SpEC BNS\n          std::make_unique<\n              ConstraintDamping::GaussianPlusConstant<3, Frame::Grid>>(\n              0.01, 0.09 * 1.0 / 1.4, 5.5 * 1.4, std::array{0.0, 0.0, 0.0})),\n      gamma1->get_clone(), gamma2->get_clone(),\n      std::unique_ptr<gh::gauges::GaugeCondition>(\n          std::make_unique<gh::gauges::AnalyticChristoffel>(soln.get_clone())),\n      grmhd::GhValenciaDivClean::fd::FilterOptions{0.001},\n      evolution::dg::subcell::SubcellOptions{\n          4.0, 1_st, 1.0e-3, 1.0e-4, false, false,\n          evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n          std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1},\n      // Just use a default-constructed fixer and have the reconstructor not\n      // call it.\n      ::VariableFixing::FixToAtmosphere<3>{});\n\n  db::mutate_apply<ValenciaDivClean::ConservativeFromPrimitive>(\n      make_not_null(&box));\n\n  std::vector<DirectionalId<3>> mortars_to_reconstruct_to{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    mortars_to_reconstruct_to.emplace_back(\n        DirectionalId<3>{direction, *neighbors.begin()});\n  }\n\n  const auto all_packaged_data = subcell::NeighborPackagedData<System>::apply(\n      box, mortars_to_reconstruct_to);\n\n  // Parse out evolved vars, since those are easiest to check for correctness,\n  // then return absolute difference between analytic and reconstructed values.\n  DirectionalIdMap<3, typename variables_tag::type> evolved_vars_errors{};\n  double max_rel_error = 0.0;\n  for (const auto& [direction_and_id, data] : all_packaged_data) {\n    const auto& direction = direction_and_id.direction();\n    const Mesh<2> dg_interface_mesh = dg_mesh.slice_away(direction.dimension());\n\n    using dg_package_field_tags =\n        typename BoundaryCorrection::dg_package_field_tags;\n    const Variables<dg_package_field_tags> packaged_data{\n        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n        const_cast<double*>(data.data()), data.size()};\n\n    auto sliced_vars = data_on_slice(\n        db::get<variables_tag>(box), dg_mesh.extents(), direction.dimension(),\n        direction.side() == Side::Upper\n            ? dg_mesh.extents(direction.dimension()) - 1\n            : 0);\n\n    tmpl::for_each<\n        typename System::grmhd_system::variables_tag::type::tags_list>(\n        [&sliced_vars, &max_rel_error, &packaged_data](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          auto& sliced_tensor = get<tag>(sliced_vars);\n          const auto& packaged_data_tensor = get<tag>(packaged_data);\n          for (size_t tensor_index = 0; tensor_index < sliced_tensor.size();\n               ++tensor_index) {\n            max_rel_error = std::max(\n                max_rel_error,\n                max(abs(sliced_tensor[tensor_index] -\n                        packaged_data_tensor[tensor_index])) /\n                    std::max({max(abs(sliced_tensor[tensor_index])),\n                              max(abs(packaged_data_tensor[tensor_index])),\n                              1.0e-17}));\n          }\n        });\n    auto& sliced_spacetime_metric =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(sliced_vars);\n    const auto& packaged_data_spacetime_metric =\n        get<gh::Tags::VSpacetimeMetric<DataVector, 3>>(packaged_data);\n\n    const auto spatial_metric = gr::spatial_metric(sliced_spacetime_metric);\n    const auto [det_spatial_metric, inverse_spatial_metric] =\n        determinant_and_inverse(spatial_metric);\n    const auto shift =\n        gr::shift(sliced_spacetime_metric, inverse_spatial_metric);\n    const auto lapse = gr::lapse(shift, sliced_spacetime_metric);\n    // Need normal vector...\n    tnsr::i<DataVector, 3, Frame::Inertial> normal_covector(get(lapse).size(),\n                                                            0.0);\n    normal_covector.get(direction.dimension()) = direction.sign();\n    const auto normal_magnitude =\n        magnitude(normal_covector, inverse_spatial_metric);\n    normal_covector.get(direction.dimension()) /= get(normal_magnitude);\n    tnsr::I<DataVector, 3, Frame::Inertial> normal_vector(get(lapse).size(),\n                                                          0.0);\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        normal_vector.get(i) +=\n            inverse_spatial_metric.get(i, j) * normal_covector.get(j);\n      }\n    }\n\n    const auto dg_interface_grid_coords =\n        db::get<domain::Tags::ElementMap<3, Frame::Grid>>(box)(\n            interface_logical_coordinates(dg_interface_mesh, direction));\n    Scalar<DataVector> gamma2_sdv{dg_interface_mesh.number_of_grid_points()};\n    (*gamma2)(make_not_null(&gamma2_sdv), dg_interface_grid_coords, 0.0, {});\n\n    const auto& pi = get<gh::Tags::Pi<DataVector, 3>>(sliced_vars);\n    const auto& phi = get<gh::Tags::Phi<DataVector, 3>>(sliced_vars);\n    tnsr::aa<DataVector, 3> v_plus_times_lambda_plus = pi;\n    tnsr::aa<DataVector, 3> v_minus_times_lambda_minus = pi;\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t b = a; b < 4; ++b) {\n        for (size_t i = 0; i < 3; ++i) {\n          v_plus_times_lambda_plus.get(a, b) +=\n              normal_vector.get(i) * phi.get(i, a, b);\n          v_minus_times_lambda_minus.get(a, b) -=\n              normal_vector.get(i) * phi.get(i, a, b);\n        }\n        v_plus_times_lambda_plus.get(a, b) -=\n            get(gamma2_sdv) * sliced_spacetime_metric.get(a, b);\n        v_minus_times_lambda_minus.get(a, b) -=\n            get(gamma2_sdv) * sliced_spacetime_metric.get(a, b);\n\n        // Multiply by char speed. Note that shift is zero in TOV\n        v_plus_times_lambda_plus.get(a, b) *= get(lapse);\n        v_minus_times_lambda_minus.get(a, b) *= -get(lapse);\n      }\n    }\n\n    const auto& packaged_data_v_plus_times_lambda_plus =\n        get<gh::Tags::VPlus<DataVector, 3>>(packaged_data);\n    const auto& packaged_data_v_minus_times_lambda_minus =\n        get<gh::Tags::VMinus<DataVector, 3>>(packaged_data);\n    for (size_t tensor_index = 0; tensor_index < sliced_spacetime_metric.size();\n         ++tensor_index) {\n      max_rel_error = std::max(\n          max_rel_error,\n          max(abs(\n              // Note: this is first char speed * g_{ab}\n              get<tmpl::at_c<dg_package_field_tags, 7>>(packaged_data)[0] *\n                  sliced_spacetime_metric[tensor_index] -\n              packaged_data_spacetime_metric[tensor_index])) /\n              std::max({max(abs(sliced_spacetime_metric[tensor_index])),\n                        max(abs(packaged_data_spacetime_metric[tensor_index])),\n                        1.0e-17}));\n      max_rel_error = std::max(\n          max_rel_error,\n          max(abs((direction.side() == Side::Upper ? -1.0 : 1.0) *\n                      (direction.side() == Side::Upper\n                           ? v_minus_times_lambda_minus[tensor_index]\n                           : v_plus_times_lambda_plus[tensor_index]) -\n                  packaged_data_v_plus_times_lambda_plus[tensor_index])) /\n              std::max(\n                  {max(abs((direction.side() == Side::Upper\n                                ? v_minus_times_lambda_minus[tensor_index]\n                                : v_plus_times_lambda_plus[tensor_index]))),\n                   max(abs(\n                       packaged_data_v_plus_times_lambda_plus[tensor_index])),\n                   1.0e-17}));\n      max_rel_error = std::max(\n          max_rel_error,\n          max(abs((direction.side() == Side::Lower ? 1.0 : -1.0) *\n                      (direction.side() == Side::Lower\n                           ? v_minus_times_lambda_minus[tensor_index]\n                           : v_plus_times_lambda_plus[tensor_index]) -\n                  packaged_data_v_minus_times_lambda_minus[tensor_index])) /\n              std::max(\n                  {max(abs((direction.side() == Side::Lower\n                                ? v_minus_times_lambda_minus[tensor_index]\n                                : v_plus_times_lambda_plus[tensor_index]))),\n                   max(abs(\n                       packaged_data_v_minus_times_lambda_minus[tensor_index])),\n                   1.0e-17}));\n    }\n  }\n  return max_rel_error;\n}\n\n// [[Timeout, 10]]\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GhValenciaDivClean.Subcell.NeighborPackagedData\",\n    \"[Unit][Evolution]\") {\n  // This tests sets up a cube [-4,4]^3 in a TOV star spacetime and verifies\n  // that the time derivative vanishes. Or, more specifically, that the time\n  // derivative decreases with increasing resolution and is below 1.0e-4.\n\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  const double error_4 = test<System>(4);\n  const double error_8 = test<System>(8);\n  CHECK(error_4 > error_8);\n  // Check that the error is \"reasonably small\"\n  CHECK(error_8 < 1.0e-4);\n}\n}  // namespace\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/Test_PrimitiveGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimitiveGhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing copied_tags =\n    tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n               hydro::Tags::ElectronFraction<DataVector>,\n               hydro::Tags::Temperature<DataVector>,\n               hydro::Tags::MagneticField<DataVector, 3>,\n               hydro::Tags::DivergenceCleaningField<DataVector>>;\nusing gh_tags = grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags;\nusing tags_for_reconstruction = grmhd::GhValenciaDivClean::Tags::\n    primitive_grmhd_and_spacetime_reconstruction_tags;\n\nvoid test_primitive_ghost_data_on_subcells(\n    const gsl::not_null<std::mt19937*> gen,\n    const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const auto prims =\n      make_with_random_values<Variables<hydro::grmhd_tags<DataVector>>>(\n          gen, dist, subcell_mesh.number_of_grid_points());\n  const auto gh_vars = make_with_random_values<Variables<gh_tags>>(\n      gen, dist, subcell_mesh.number_of_grid_points());\n\n  auto box = db::create<\n      db::AddSimpleTags<::Tags::Variables<hydro::grmhd_tags<DataVector>>,\n                        ::Tags::Variables<gh_tags>>>(prims, gh_vars);\n  DataVector recons_prims_rdmp = db::mutate_apply<\n      grmhd::GhValenciaDivClean::subcell::PrimitiveGhostVariables>(\n      make_not_null(&box), 2_st);\n  const Variables<tags_for_reconstruction> recons_prims{\n      recons_prims_rdmp.data(), recons_prims_rdmp.size() - 2};\n  tmpl::for_each<copied_tags>([&prims, &recons_prims](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK_ITERABLE_APPROX(get<tag>(recons_prims), get<tag>(prims));\n  });\n  tmpl::for_each<gh_tags>([&gh_vars, &recons_prims](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK_ITERABLE_APPROX(get<tag>(recons_prims), get<tag>(gh_vars));\n  });\n  auto lorentz_factor_times_v_I =\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prims);\n  for (auto& component : lorentz_factor_times_v_I) {\n    component *= get(get<hydro::Tags::LorentzFactor<DataVector>>(prims));\n  }\n  CHECK_ITERABLE_APPROX(\n      (get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n          recons_prims)),\n      lorentz_factor_times_v_I);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GhValenciaDivClean.Subcell.PrimitiveGhostData\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.0, 1.0);\n  test_primitive_ghost_data_on_subcells(make_not_null(&gen),\n                                        make_not_null(&dist));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/Test_PrimsAfterRollback.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/PrimsAfterRollback.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace {\ntemplate <typename EquationOfStateType>\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist,\n          const bool did_rollback, const EquationOfStateType& eos_input) {\n  const Mesh<3> dg_mesh{4, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using cons_tag = typename grmhd::ValenciaDivClean::System::variables_tag;\n  using prim_tag =\n      typename grmhd::ValenciaDivClean::System::primitive_variables_tag;\n  using ConsVars = typename cons_tag::type;\n  using PrimVars = typename prim_tag::type;\n\n  const size_t subcell_num_pts = subcell_mesh.number_of_grid_points();\n  tnsr::aa<DataVector, 3, Frame::Inertial> spacetime_metric{subcell_num_pts,\n                                                            0.0};\n  get<0, 0>(spacetime_metric) = -1.08;\n  for (size_t i = 1; i < 4; ++i) {\n    spacetime_metric.get(i, i) = 1.0 + 0.01 * i;\n  }\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{subcell_num_pts, 0.0};\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric{subcell_num_pts,\n                                                              0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = spacetime_metric.get(i + 1, i + 1);\n    inv_spatial_metric.get(i, i) = 1.0 / spatial_metric.get(i, i);\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      sqrt(get(determinant(spatial_metric)))};\n\n  std::unique_ptr<EquationsOfState::get_eos_base<EquationOfStateType>> eos =\n      std::make_unique<EquationOfStateType>(eos_input);\n\n  // Compute the conservatives on the FD grid by first computing the primitives\n  // on the FD grid, then compute the conservatives from the primitives.\n  auto subcell_prims = make_with_random_values<PrimVars>(\n      gen, dist, subcell_mesh.number_of_grid_points());\n  PrimVars dg_prims{};\n  ConsVars subcell_cons{};\n  if (did_rollback) {\n    subcell_cons.initialize(subcell_mesh.number_of_grid_points());\n    if constexpr (EquationOfStateType::thermodynamic_dim == 1) {\n      get<hydro::Tags::Pressure<DataVector>>(subcell_prims) =\n          eos->pressure_from_density(\n              get<hydro::Tags::RestMassDensity<DataVector>>(subcell_prims));\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(subcell_prims) =\n          eos->specific_internal_energy_from_density(\n              get<hydro::Tags::RestMassDensity<DataVector>>(subcell_prims));\n    } else if constexpr (EquationOfStateType::thermodynamic_dim == 2) {\n      ERROR(\"2D EoS not implemented\");\n    } else {\n      get<hydro::Tags::Pressure<DataVector>>(subcell_prims) =\n          eos->pressure_from_density_and_temperature(\n              get<hydro::Tags::RestMassDensity<DataVector>>(subcell_prims),\n              get<hydro::Tags::Temperature<DataVector>>(subcell_prims),\n              get<hydro::Tags::ElectronFraction<DataVector>>(subcell_prims));\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(subcell_prims) =\n          eos->specific_internal_energy_from_density_and_temperature(\n              get<hydro::Tags::RestMassDensity<DataVector>>(subcell_prims),\n              get<hydro::Tags::Temperature<DataVector>>(subcell_prims),\n              get<hydro::Tags::ElectronFraction<DataVector>>(subcell_prims));\n    }\n    {\n      const auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>(\n              subcell_prims);\n      get(get<hydro::Tags::LorentzFactor<DataVector>>(subcell_prims)) =\n          1.0 / sqrt(1.0 - get(dot_product(spatial_velocity, spatial_velocity,\n                                           spatial_metric)));\n    }\n    ConservativeFromPrimitive::apply(\n        make_not_null(&get<Tags::TildeD>(subcell_cons)),\n        make_not_null(&get<Tags::TildeYe>(subcell_cons)),\n        make_not_null(&get<Tags::TildeTau>(subcell_cons)),\n        make_not_null(&get<Tags::TildeS<Frame::Inertial>>(subcell_cons)),\n        make_not_null(&get<Tags::TildeB<Frame::Inertial>>(subcell_cons)),\n        make_not_null(&get<Tags::TildePhi>(subcell_cons)),\n        get<hydro::Tags::RestMassDensity<DataVector>>(subcell_prims),\n        get<hydro::Tags::ElectronFraction<DataVector>>(subcell_prims),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(subcell_prims),\n        get<hydro::Tags::Pressure<DataVector>>(subcell_prims),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>(\n            subcell_prims),\n        get<hydro::Tags::LorentzFactor<DataVector>>(subcell_prims),\n        get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(\n            subcell_prims),\n        sqrt_det_spatial_metric, spatial_metric,\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(subcell_prims));\n    dg_prims = evolution::dg::subcell::fd::reconstruct(\n        subcell_prims, dg_mesh, subcell_mesh.extents(),\n        evolution::dg::subcell::fd::ReconstructionMethod::AllDimsAtOnce);\n  }\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  // The DG prims are used as an initial guess so we need to provide them\n  auto box = db::create<db::AddSimpleTags<\n      evolution::dg::subcell::Tags::DidRollback, cons_tag, prim_tag,\n      gr::Tags::SpacetimeMetric<DataVector, 3>, ::domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>, hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>>(\n      did_rollback, subcell_cons, dg_prims, spacetime_metric, dg_mesh,\n      subcell_mesh, eos->promote_to_3d_eos(),\n      primitive_from_conservative_options);\n\n  using recovery_schemes = tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n  db::mutate_apply<\n      GhValenciaDivClean::subcell::PrimsAfterRollback<recovery_schemes>>(\n      make_not_null(&box));\n\n  if (did_rollback) {\n    REQUIRE(db::get<prim_tag>(box).number_of_grid_points() == subcell_num_pts);\n    PrimVars expected_subcell_prims = subcell_prims;\n    // since primitive recovery doesn't yield the exact primitives that you\n    // started with and we want to make sure the primitive recovery algorithm\n    // was run, we need to solve for the primitive variables explicitly.\n    get(get<hydro::Tags::Pressure<DataVector>>(expected_subcell_prims)) =\n        evolution::dg::subcell::fd::project(\n            get(get<hydro::Tags::Pressure<DataVector>>(dg_prims)), dg_mesh,\n            subcell_mesh.extents());\n    PrimitiveFromConservative<recovery_schemes>::apply(\n        make_not_null(&get<hydro::Tags::RestMassDensity<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::ElectronFraction<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::MagneticField<DataVector, 3>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::LorentzFactor<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(\n            &get<hydro::Tags::Pressure<DataVector>>(expected_subcell_prims)),\n        make_not_null(\n            &get<hydro::Tags::Temperature<DataVector>>(expected_subcell_prims)),\n        get<grmhd::ValenciaDivClean::Tags::TildeD>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildeYe>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildeTau>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildePhi>(box), spatial_metric,\n        inv_spatial_metric, sqrt_det_spatial_metric,\n        db::get<hydro::Tags::GrmhdEquationOfState>(box),\n        primitive_from_conservative_options);\n    CHECK_VARIABLES_APPROX(db::get<prim_tag>(box), expected_subcell_prims);\n  } else {\n    CHECK(db::get<prim_tag>(box).number_of_grid_points() == 0);\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GhValenciaDivClean.Subcell.PrimsAfterRollback\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  // Use a small range of random values since we need recovery to succeed and we\n  // also reconstruct to the DG grid from the FD grid and need to maintain a\n  // somewhat reasonable state on both grids.\n  const EquationsOfState::PolytropicFluid<true> polytropic_eos(1.4, 5.0 / 3.0);\n  const EquationsOfState::Barotropic3D<EquationsOfState::PolytropicFluid<true>>\n      wrapped_3d_polytropic_eos(polytropic_eos);\n  std::uniform_real_distribution<> dist(0.5, 0.50005);\n  for (const bool did_rollback : {true, false}) {\n    test(make_not_null(&gen), make_not_null(&dist), did_rollback,\n         polytropic_eos);\n    test(make_not_null(&gen), make_not_null(&dist), did_rollback,\n         wrapped_3d_polytropic_eos);\n  }\n}\n}  // namespace\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/Test_ResizeAndComputePrimitives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/ResizeAndComputePrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\nnamespace {\ntemplate <typename EquationOfStateType>\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist,\n          const evolution::dg::subcell::ActiveGrid active_grid,\n          const bool start_on_dg, const EquationOfStateType& input_eos) {\n  CAPTURE(active_grid);\n  CAPTURE(start_on_dg);\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using cons_tag = typename grmhd::ValenciaDivClean::System::variables_tag;\n  using prim_tag =\n      typename grmhd::ValenciaDivClean::System::primitive_variables_tag;\n  using ConsVars = typename cons_tag::type;\n  using PrimVars = typename prim_tag::type;\n\n  const size_t active_num_pts =\n      active_grid == evolution::dg::subcell::ActiveGrid::Dg\n          ? dg_mesh.number_of_grid_points()\n          : subcell_mesh.number_of_grid_points();\n  tnsr::aa<DataVector, 3, Frame::Inertial> spacetime_metric{active_num_pts,\n                                                            0.0};\n  for (size_t i = 0; i < 4; ++i) {\n    spacetime_metric.get(i, i) = 1.0 + 0.01 * i;\n  }\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{active_num_pts, 0.0};\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric{active_num_pts,\n                                                              0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = spacetime_metric.get(i + 1, i + 1);\n    inv_spatial_metric.get(i, i) = 1.0 / spatial_metric.get(i, i);\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      sqrt(get(determinant(spatial_metric)))};\n\n  auto eos = input_eos.get_clone();\n  auto prim_vars = make_with_random_values<PrimVars>(\n      gen, dist,\n      start_on_dg ? dg_mesh.number_of_grid_points()\n                  : subcell_mesh.number_of_grid_points());\n  if constexpr (EquationOfStateType::thermodynamic_dim == 1) {\n    get<hydro::Tags::Pressure<DataVector>>(prim_vars) =\n        eos->pressure_from_density(\n            get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars));\n    get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prim_vars) =\n        eos->specific_internal_energy_from_density(\n            get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars));\n  }\n\n  else if constexpr (EquationOfStateType::thermodynamic_dim == 2) {\n    ERROR(\"2D EoS not implemented\");\n  } else {\n    get<hydro::Tags::Pressure<DataVector>>(prim_vars) =\n        eos->pressure_from_density_and_temperature(\n            get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n            get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n            get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars));\n    get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prim_vars) =\n        eos->specific_internal_energy_from_density_and_temperature(\n            get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n            get<hydro::Tags::Temperature<DataVector>>(prim_vars),\n            get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars));\n  }\n  {\n    const auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>(\n            prim_vars);\n    tnsr::ii<DataVector, 3, Frame::Inertial> subcell_spatial_metric{\n        start_on_dg ? dg_mesh.number_of_grid_points()\n                    : subcell_mesh.number_of_grid_points(),\n        0.0};\n    for (size_t i = 0; i < 3; ++i) {\n      subcell_spatial_metric.get(i, i) = 1.0 + 0.01 * i;\n    }\n    get(get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars)) =\n        1.0 / sqrt(1.0 - get(dot_product(spatial_velocity, spatial_velocity,\n                                         subcell_spatial_metric)));\n  }\n  ConsVars cons_vars{};\n  const auto compute_cons = [&cons_vars, &spatial_metric,\n                             &sqrt_det_spatial_metric](const auto& prims) {\n    cons_vars.initialize(prims.number_of_grid_points());\n    ValenciaDivClean::ConservativeFromPrimitive::apply(\n        make_not_null(&get<ValenciaDivClean::Tags::TildeD>(cons_vars)),\n        make_not_null(&get<ValenciaDivClean::Tags::TildeYe>(cons_vars)),\n        make_not_null(&get<ValenciaDivClean::Tags::TildeTau>(cons_vars)),\n        make_not_null(\n            &get<ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(cons_vars)),\n        make_not_null(\n            &get<ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(cons_vars)),\n        make_not_null(&get<ValenciaDivClean::Tags::TildePhi>(cons_vars)),\n        get<hydro::Tags::RestMassDensity<DataVector>>(prims),\n        get<hydro::Tags::ElectronFraction<DataVector>>(prims),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prims),\n        get<hydro::Tags::Pressure<DataVector>>(prims),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>(\n            prims),\n        get<hydro::Tags::LorentzFactor<DataVector>>(prims),\n        get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(prims),\n        sqrt_det_spatial_metric, spatial_metric,\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(prims));\n  };\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    auto dg_prims = prim_vars;\n    if (not start_on_dg) {\n      dg_prims = evolution::dg::subcell::fd::reconstruct(\n          prim_vars, dg_mesh, subcell_mesh.extents(),\n          evolution::dg::subcell::fd ::ReconstructionMethod::AllDimsAtOnce);\n    }\n    compute_cons(dg_prims);\n  } else {\n    compute_cons(prim_vars);\n  }\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  auto box = db::create<db::AddSimpleTags<\n      evolution::dg::subcell::Tags::ActiveGrid, cons_tag, prim_tag,\n      gr::Tags::SpacetimeMetric<DataVector, 3>, ::domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>, hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>>(\n      active_grid, cons_vars, prim_vars, spacetime_metric, dg_mesh,\n      subcell_mesh, std::move(eos), primitive_from_conservative_options);\n\n  using recovery_schemes = tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n  db::mutate_apply<grmhd::GhValenciaDivClean::subcell::ResizeAndComputePrims<\n      recovery_schemes>>(make_not_null(&box));\n\n  REQUIRE(db::get<prim_tag>(box).number_of_grid_points() == active_num_pts);\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    prim_vars.initialize(cons_vars.number_of_grid_points());\n    grmhd::ValenciaDivClean::PrimitiveFromConservative<recovery_schemes>::apply(\n        make_not_null(\n            &get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::DivergenceCleaningField<DataVector>>(prim_vars)),\n        make_not_null(&get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars)),\n        make_not_null(&get<hydro::Tags::Pressure<DataVector>>(prim_vars)),\n        make_not_null(&get<hydro::Tags::Temperature<DataVector>>(prim_vars)),\n        get<grmhd::ValenciaDivClean::Tags::TildeD>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildeYe>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildeTau>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildePhi>(cons_vars), spatial_metric,\n        inv_spatial_metric, sqrt_det_spatial_metric,\n        db::get<hydro::Tags::GrmhdEquationOfState>(box),\n        primitive_from_conservative_options);\n  }\n  CHECK_VARIABLES_APPROX(db::get<prim_tag>(box), prim_vars);\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GhValenciaDivClean.Subcell.ResizeAndComputePrims\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  // Use a small range of random values since we need recovery to succeed and we\n  // also reconstruct to the DG grid from the FD grid and need to maintain a\n  // somewhat reasonable state on both grids.\n  const EquationsOfState::Barotropic3D<EquationsOfState::PolytropicFluid<true>>\n      wrapped_3d_polytropic_eos(\n          EquationsOfState::PolytropicFluid<true>(1.4, 5.0 / 3.0));\n  std::uniform_real_distribution<> dist(0.5, 0.505);\n  for (const auto active_grid : {evolution::dg::subcell::ActiveGrid::Dg,\n                                 evolution::dg::subcell::ActiveGrid::Subcell}) {\n    test(make_not_null(&gen), make_not_null(&dist), active_grid, false,\n         wrapped_3d_polytropic_eos);\n    if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n      test(make_not_null(&gen), make_not_null(&dist), active_grid, true,\n           wrapped_3d_polytropic_eos);\n    }\n  }\n}\n}  // namespace\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Norms.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Rotation.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Jacobians.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/ReconstructionOrder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/FilterOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/Factory.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace grmhd::GhValenciaDivClean {\nnamespace {\n\n// These solution tag and metavariables are not strictly required for testing\n// subcell time derivative, but needed for compilation since\n// BoundaryConditionGhostData requires this to be in box.\nstruct DummyAnalyticSolutionTag : db::SimpleTag {\n  using type =\n      gh::Solutions::WrappedGr<::RelativisticEuler::Solutions::TovStar>;\n};\n\ntemplate <typename System>\nstruct DummyEvolutionMetaVars {\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled_at_external_boundary = true;\n  };\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<BoundaryConditions::BoundaryCondition,\n                   tmpl::push_back<\n                       BoundaryConditions::standard_boundary_conditions<System>,\n                       BoundaryConditions::DirichletAnalytic<System>>>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   BoundaryCorrections::standard_boundary_corrections>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   ghmhd::GhValenciaDivClean::InitialData::\n                       analytic_solutions_and_data_list>>;\n  };\n};\n\ntemplate <typename System>\ndouble test(const size_t num_dg_pts, std::optional<double> expansion_velocity,\n            const bool test_non_diagonal_jacobian) {\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\n  const gh::Solutions::WrappedGr<::RelativisticEuler::Solutions::TovStar> soln{\n      1.28e-3,\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0)\n          ->get_clone(),\n      RelativisticEuler::Solutions::TovCoordinates::Schwarzschild};\n  const Affine affine_map{-1.0, 1.0, -4.0, 4.0};\n  const ElementId<3> element_id{\n      0, {SegmentId{3, 4}, SegmentId{3, 4}, SegmentId{3, 7}}};\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n      external_boundary_conditions(1);\n  for (const auto& direction : Direction<3>::all_directions()) {\n    external_boundary_conditions.at(0)[direction] =\n        grmhd::GhValenciaDivClean::BoundaryConditions::DirichletAnalytic<\n            System>(std::make_unique<gh::Solutions::WrappedGr<\n                        ::RelativisticEuler::Solutions::TovStar>>(soln))\n            .get_clone();\n  }\n  std::vector<Block<3>> blocks;\n  blocks.emplace_back(\n      Block<3>{test_non_diagonal_jacobian\n                   ? domain::make_coordinate_map_base<Frame::BlockLogical,\n                                                      Frame::Inertial>(\n                         ::domain::CoordinateMaps::Rotation<3>(0.7, 0, 0.))\n                   : domain::make_coordinate_map_base<Frame::BlockLogical,\n                                                      Frame::Inertial>(\n                         Affine3D{affine_map, affine_map, affine_map}),\n               0,\n               {}});\n  const auto& block = blocks[0];\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  ElementMap<3, Frame::Grid> element_map{\n      element_id, block.is_time_dependent()\n                      ? block.moving_mesh_logical_to_grid_map().get_clone()\n                      : block.stationary_map().get_to_grid_frame()};\n  const auto grid_to_inertial_map =\n      ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          ::domain::CoordinateMaps::Identity<3>{});\n  const auto element = domain::create_initial_element(\n      element_id, blocks,\n      std::vector<std::array<size_t, 3>>{std::array<size_t, 3>{{3, 3, 3}}});\n\n  const double time = 0.0;\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const size_t num_dg_pts_3d = num_dg_pts * num_dg_pts * num_dg_pts;\n\n  const auto logical_coords = logical_coordinates(subcell_mesh);\n  const auto cell_centered_coords = (*grid_to_inertial_map)(\n      element_map(logical_coords), time, functions_of_time);\n  const auto dg_coords = (*grid_to_inertial_map)(\n      element_map(logical_coordinates(dg_mesh)), time, functions_of_time);\n\n  const InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      cell_centered_logical_to_grid_inv_jacobian =\n          element_map.inv_jacobian(logical_coords);\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      cell_centered_logical_to_inertial_inv_jacobian =\n          InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>(\n              subcell_mesh.number_of_grid_points());\n  const auto& cell_centered_grid_to_inertial_inv_jacobian =\n      grid_to_inertial_map->inv_jacobian(element_map(logical_coords));\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      auto& inv_jacobian_component =\n          cell_centered_logical_to_inertial_inv_jacobian.get(i, j);\n      inv_jacobian_component = 0.;\n      for (size_t k = 0; k < 3; k++) {\n        inv_jacobian_component +=\n            cell_centered_logical_to_grid_inv_jacobian.get(i, k) *\n            cell_centered_grid_to_inertial_inv_jacobian.get(k, j);\n      }\n    }\n  }\n\n  const InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      dg_logical_to_grid_inv_jacobian =\n          element_map.inv_jacobian(logical_coordinates(dg_mesh));\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      dg_logical_to_inertial_inv_jacobian =\n          InverseJacobian<DataVector, 3, Frame::ElementLogical,\n                          Frame::Inertial>(dg_mesh.number_of_grid_points());\n  const auto& dg_grid_to_inertial_inv_jacobian =\n      grid_to_inertial_map->inv_jacobian(\n          element_map(logical_coordinates(dg_mesh)));\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      auto& inv_jacobian_component =\n          dg_logical_to_inertial_inv_jacobian.get(i, j);\n      inv_jacobian_component = 0.;\n      for (size_t k = 0; k < 3; k++) {\n        inv_jacobian_component += dg_logical_to_grid_inv_jacobian.get(i, k) *\n                                  dg_grid_to_inertial_inv_jacobian.get(k, j);\n      }\n    }\n  }\n\n  using variables_tag = typename System::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n  using evolved_tags = typename System::variables_tag::tags_list;\n  using conserved_tags =\n      typename grmhd::ValenciaDivClean::ConservativeFromPrimitive::return_tags;\n  using gh_variables_tag = typename System::gh_system::variables_tag;\n  using gh_variables_tags = typename gh_variables_tag::tags_list;\n  Variables<typename System::primitive_variables_tag::tags_list>\n      cell_centered_prim_vars{subcell_mesh.number_of_grid_points()};\n  cell_centered_prim_vars.assign_subset(\n      soln.variables(cell_centered_coords, time,\n                     typename System::primitive_variables_tag::tags_list{}));\n  typename variables_tag::type initial_variables{\n      subcell_mesh.number_of_grid_points()};\n  initial_variables.assign_subset(\n      soln.variables(cell_centered_coords, time,\n                     typename System::gh_system::variables_tag::tags_list{}));\n  std::optional<tnsr::I<DataVector, 3, Frame::Inertial>> dg_mesh_velocity{};\n  std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>\n      subcell_mesh_velocity{};\n  if (expansion_velocity.has_value()) {\n    dg_mesh_velocity = std::optional<tnsr::I<DataVector, 3>>(\n        tnsr::I<DataVector, 3>(num_dg_pts_3d));\n    for (int i = 0; i < 3; i++) {\n      dg_mesh_velocity.value().get(i) =\n          dg_coords.get(i) * expansion_velocity.value();\n    }\n\n    subcell_mesh_velocity = std::optional<tnsr::I<DataVector, 3>>(\n        tnsr::I<DataVector, 3>(subcell_mesh.number_of_grid_points()));\n    for (int i = 0; i < 3; i++) {\n      subcell_mesh_velocity.value().get(i) =\n          cell_centered_coords.get(i) * expansion_velocity.value();\n    }\n  }\n  std::optional<Scalar<DataVector>> div_dg_mesh_velocity{};\n  if (expansion_velocity.has_value()) {\n    div_dg_mesh_velocity =\n        std::optional<Scalar<DataVector>>(Scalar<DataVector>(num_dg_pts_3d));\n    div_dg_mesh_velocity.value().get() = 3.0 * expansion_velocity.value();\n  }\n  // To be recomputed on each face.\n  std::optional<Scalar<DataVector>> normal_dot_mesh_velocity{};\n\n  // Neighbor data for reconstruction.\n  //\n  // 0. neighbors coords (our logical coords +2)\n  // 1. compute prims from solution\n  // 2. compute prims needed for reconstruction\n  // 3. set neighbor data\n  evolution::dg::subcell::Tags::GhostDataForReconstruction<3>::type\n      neighbor_data{};\n  using prims_to_reconstruct_tags = grmhd::GhValenciaDivClean::Tags::\n      primitive_grmhd_and_spacetime_reconstruction_tags;\n  for (const auto& [direction, neighbors_in_direction] : element.neighbors()) {\n    auto neighbor_logical_coords = logical_coordinates(subcell_mesh);\n    neighbor_logical_coords.get(direction.dimension()) +=\n        2.0 * direction.sign();\n    auto neighbor_coords = (*grid_to_inertial_map)(\n        element_map(neighbor_logical_coords), time, functions_of_time);\n    const auto neighbor_prims = soln.variables(\n        neighbor_coords, time,\n        tmpl::append<typename System::primitive_variables_tag::tags_list,\n                     typename System::gh_system::variables_tag::tags_list>{});\n    Variables<prims_to_reconstruct_tags> prims_to_reconstruct{\n        subcell_mesh.number_of_grid_points()};\n    prims_to_reconstruct.assign_subset(neighbor_prims);\n    get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n        prims_to_reconstruct) =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(neighbor_prims);\n    for (auto& component :\n         get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n             prims_to_reconstruct)) {\n      component *=\n          get(get<hydro::Tags::LorentzFactor<DataVector>>(neighbor_prims));\n    }\n\n    // Slice data so we can add it to the element's neighbor data\n    DataVector neighbor_data_in_direction =\n        evolution::dg::subcell::slice_data(\n            prims_to_reconstruct, subcell_mesh.extents(),\n            grmhd::ValenciaDivClean::fd::MonotonisedCentralPrim{}\n                .ghost_zone_size(),\n            std::unordered_set{direction.opposite()}, 0, {})\n            .at(direction.opposite());\n    const auto key =\n        DirectionalId<3>{direction, *element.neighbors().at(direction).begin()};\n    neighbor_data[key] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[key].neighbor_ghost_data_for_reconstruction() =\n        neighbor_data_in_direction;\n  }\n\n  Domain<3> domain{std::move(blocks)};\n\n  const auto gamma1 =  // Gamma1, taken from SpEC BNS\n      std::make_unique<ConstraintDamping::GaussianPlusConstant<3, Frame::Grid>>(\n          -0.999, 0.999 * 1.0,\n          10.0 * 10.0,  // second 10 is \"separation\" of NSes\n          std::array{0.0, 0.0, 0.0});\n  const auto gamma2 =  // Gamma2, taken from SpEC BNS\n      std::make_unique<ConstraintDamping::GaussianPlusConstant<3, Frame::Grid>>(\n          0.01, 1.35 * 1.0 / 1.4, 5.5 * 1.4, std::array{0.0, 0.0, 0.0});\n\n  // Set mortar data, both for ourselves on some interfaces and for our\n  // neighbors to emulate a rollback and DG-FD interface.\n  evolution::dg::Tags::MortarData<3>::type mortar_data{};\n  const Slab slab{0.0, 1.0};\n  using BoundaryCorrection = BoundaryCorrections::ProductOfCorrections<\n      gh::BoundaryCorrections::UpwindPenalty<3>,\n      ValenciaDivClean::BoundaryCorrections::Hll>;\n  const BoundaryCorrection boundary_correction{\n      gh::BoundaryCorrections::UpwindPenalty<3>{},\n      ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8}};\n  const auto insert_dg_data = [&](const Direction<3>& direction,\n                                  const bool local_data) {\n    const Mesh<2> interface_mesh = dg_mesh.slice_away(2);\n    const auto face_grid_coords =\n        element_map(interface_logical_coordinates(interface_mesh, direction));\n    const auto face_coords =\n        (*grid_to_inertial_map)(face_grid_coords, time, functions_of_time);\n    const auto face_prims = soln.variables(\n        face_coords, time,\n        tmpl::append<\n            typename System::primitive_variables_tag::tags_list,\n            typename System::gh_system::variables_tag::tags_list,\n            tmpl::list<gr::Tags::Lapse<DataVector>,\n                       gr::Tags::Shift<DataVector, 3>,\n                       gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                       gr::Tags::SpatialMetric<DataVector, 3>,\n                       gr::Tags::InverseSpatialMetric<DataVector, 3>>>{});\n    using flux_tags =\n        typename grmhd::ValenciaDivClean::ComputeFluxes::return_tags;\n    using flux_argument_tags =\n        typename grmhd::ValenciaDivClean::ComputeFluxes::argument_tags;\n    using dg_package_data_temporary_tags =\n        typename BoundaryCorrection::dg_package_data_temporary_tags;\n    Variables<tmpl::remove_duplicates<tmpl::append<\n        typename System::primitive_variables_tag::tags_list,\n        typename System::gh_system::variables_tag::tags_list, flux_tags,\n        flux_argument_tags,\n        tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                   gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                   gr::Tags::SpatialMetric<DataVector, 3>,\n                   gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                   ::gh::Tags::ConstraintGamma1, ::gh::Tags::ConstraintGamma2>,\n        dg_package_data_temporary_tags, prims_to_reconstruct_tags>>>\n        prims_to_reconstruct{interface_mesh.number_of_grid_points()};\n    prims_to_reconstruct.assign_subset(face_prims);\n    get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n        prims_to_reconstruct) =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(face_prims);\n    for (auto& component :\n         get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n             prims_to_reconstruct)) {\n      component *= get(get<hydro::Tags::LorentzFactor<DataVector>>(face_prims));\n    }\n\n    using p2c_argument_tags = typename grmhd::ValenciaDivClean::\n        ConservativeFromPrimitive::argument_tags;\n    grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n        make_not_null(\n            &get<tmpl::at_c<conserved_tags, 0>>(prims_to_reconstruct)),\n        make_not_null(\n            &get<tmpl::at_c<conserved_tags, 1>>(prims_to_reconstruct)),\n        make_not_null(\n            &get<tmpl::at_c<conserved_tags, 2>>(prims_to_reconstruct)),\n        make_not_null(\n            &get<tmpl::at_c<conserved_tags, 3>>(prims_to_reconstruct)),\n        make_not_null(\n            &get<tmpl::at_c<conserved_tags, 4>>(prims_to_reconstruct)),\n        make_not_null(\n            &get<tmpl::at_c<conserved_tags, 5>>(prims_to_reconstruct)),\n\n        get<tmpl::at_c<p2c_argument_tags, 0>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 1>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 2>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 3>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 4>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 5>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 6>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 7>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 8>>(prims_to_reconstruct),\n        get<tmpl::at_c<p2c_argument_tags, 9>>(prims_to_reconstruct));\n\n    grmhd::ValenciaDivClean::ComputeFluxes::apply(\n        make_not_null(&get<tmpl::at_c<flux_tags, 0>>(prims_to_reconstruct)),\n        make_not_null(&get<tmpl::at_c<flux_tags, 1>>(prims_to_reconstruct)),\n        make_not_null(&get<tmpl::at_c<flux_tags, 2>>(prims_to_reconstruct)),\n        make_not_null(&get<tmpl::at_c<flux_tags, 3>>(prims_to_reconstruct)),\n        make_not_null(&get<tmpl::at_c<flux_tags, 4>>(prims_to_reconstruct)),\n        make_not_null(&get<tmpl::at_c<flux_tags, 5>>(prims_to_reconstruct)),\n\n        get<tmpl::at_c<flux_argument_tags, 0>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 1>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 2>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 3>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 4>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 5>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 6>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 7>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 8>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 9>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 10>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 11>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 12>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 13>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_argument_tags, 14>>(prims_to_reconstruct));\n\n    // Add mesh velocity contribution to neighbor fluxes\n    std::optional<tnsr::I<DataVector, 3, Frame::Inertial>> face_mesh_velocity =\n        {};\n    if (expansion_velocity.has_value()) {\n      face_mesh_velocity = tnsr::I<DataVector, 3, Frame::Inertial>{\n          interface_mesh.number_of_grid_points()};\n      for (size_t i = 0; i < 3; i++) {\n        face_mesh_velocity.value().get(i) =\n            face_coords.get(i) * expansion_velocity.value();\n      }\n\n      tmpl::for_each<conserved_tags>([&prims_to_reconstruct,\n                                      &face_mesh_velocity](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        using flux_tag = ::Tags::Flux<tag, tmpl::size_t<3>, Frame::Inertial>;\n        using FluxTensor = typename flux_tag::type;\n        const auto& var = get<tag>(prims_to_reconstruct);\n        auto& flux = get<flux_tag>(prims_to_reconstruct);\n        for (size_t storage_index = 0; storage_index < var.size();\n             ++storage_index) {\n          const auto tensor_index = var.get_tensor_index(storage_index);\n          for (size_t j = 0; j < 3; j++) {\n            const auto flux_storage_index =\n                FluxTensor::get_storage_index(prepend(tensor_index, j));\n            flux[flux_storage_index] -=\n                face_mesh_velocity.value().get(j) * var[storage_index];\n          }\n        }\n      });\n    }\n\n    (*gamma1)(\n        make_not_null(&get<::gh::Tags::ConstraintGamma1>(prims_to_reconstruct)),\n        face_grid_coords, time, functions_of_time);\n    (*gamma2)(\n        make_not_null(&get<::gh::Tags::ConstraintGamma2>(prims_to_reconstruct)),\n        face_grid_coords, time, functions_of_time);\n\n    tnsr::i<DataVector, 3, Frame::Inertial> normal_covector =\n        unnormalized_face_normal(interface_mesh, element_map,\n                                 *grid_to_inertial_map, time, functions_of_time,\n                                 direction);\n    const auto normal_magnitude = magnitude(\n        normal_covector, get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                             prims_to_reconstruct));\n    for (auto& component : normal_covector) {\n      component /= get(normal_magnitude);\n    }\n    tnsr::I<DataVector, 3, Frame::Inertial> normal_vector{\n        interface_mesh.number_of_grid_points(), 0.0};\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        normal_vector.get(i) +=\n            get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                prims_to_reconstruct)\n                .get(i, j) *\n            normal_covector.get(j);\n      }\n    }\n    if (not local_data) {\n      for (size_t i = 0; i < 3; ++i) {\n        normal_covector.get(i) *= -1.0;\n        normal_vector.get(i) *= -1.0;\n      }\n    }\n\n    if (expansion_velocity.has_value()) {\n      normal_dot_mesh_velocity =\n          Scalar<DataVector>{interface_mesh.number_of_grid_points()};\n      normal_dot_mesh_velocity.value() =\n          dot_product(face_mesh_velocity.value(), normal_covector);\n    }\n\n    {\n      auto& spatial_velocity_one_form =\n          get<tmpl::at_c<dg_package_data_temporary_tags, 4>>(\n              prims_to_reconstruct);\n      const auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n              prims_to_reconstruct);\n      const auto& spatial_metric =\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(prims_to_reconstruct);\n\n      for (size_t i = 0; i < 3; ++i) {\n        spatial_velocity_one_form.get(i) = 0.0;\n        for (size_t j = 0; j < 3; ++j) {\n          spatial_velocity_one_form.get(i) +=\n              spatial_metric.get(i, j) * spatial_velocity.get(j);\n        }\n      }\n    }\n\n    using dg_package_fields =\n        typename BoundaryCorrection::dg_package_field_tags;\n    Variables<dg_package_fields> dg_packaged_data{\n        interface_mesh.number_of_grid_points()};\n    using dg_package_data_primitive_tags =\n        typename BoundaryCorrection::dg_package_data_primitive_tags;\n    boundary_correction.dg_package_data(\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 0>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 1>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 2>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 3>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 4>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 5>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 6>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 7>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 8>>(dg_packaged_data)),\n        make_not_null(&get<tmpl::at_c<dg_package_fields, 9>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 10>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 11>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 12>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 13>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 14>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 15>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 16>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 17>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 18>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 19>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 20>>(dg_packaged_data)),\n        make_not_null(\n            &get<tmpl::at_c<dg_package_fields, 21>>(dg_packaged_data)),\n\n        // vars,\n        get<tmpl::at_c<evolved_tags, 0>>(prims_to_reconstruct),\n        get<tmpl::at_c<evolved_tags, 1>>(prims_to_reconstruct),\n        get<tmpl::at_c<evolved_tags, 2>>(prims_to_reconstruct),\n        get<tmpl::at_c<evolved_tags, 3>>(prims_to_reconstruct),\n        get<tmpl::at_c<evolved_tags, 4>>(prims_to_reconstruct),\n        get<tmpl::at_c<evolved_tags, 5>>(prims_to_reconstruct),\n        get<tmpl::at_c<evolved_tags, 6>>(prims_to_reconstruct),\n        get<tmpl::at_c<evolved_tags, 7>>(prims_to_reconstruct),\n        get<tmpl::at_c<evolved_tags, 8>>(prims_to_reconstruct),\n\n        // fluxes,\n        get<tmpl::at_c<flux_tags, 0>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_tags, 1>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_tags, 2>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_tags, 3>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_tags, 4>>(prims_to_reconstruct),\n        get<tmpl::at_c<flux_tags, 5>>(prims_to_reconstruct),\n\n        // temporaries,\n        get<tmpl::at_c<dg_package_data_temporary_tags, 0>>(\n            prims_to_reconstruct),\n        get<tmpl::at_c<dg_package_data_temporary_tags, 1>>(\n            prims_to_reconstruct),\n        get<tmpl::at_c<dg_package_data_temporary_tags, 2>>(\n            prims_to_reconstruct),\n        get<tmpl::at_c<dg_package_data_temporary_tags, 3>>(\n            prims_to_reconstruct),\n        get<tmpl::at_c<dg_package_data_temporary_tags, 4>>(\n            prims_to_reconstruct),\n\n        // prims\n        get<tmpl::at_c<dg_package_data_primitive_tags, 0>>(\n            prims_to_reconstruct),\n        get<tmpl::at_c<dg_package_data_primitive_tags, 1>>(\n            prims_to_reconstruct),\n        get<tmpl::at_c<dg_package_data_primitive_tags, 2>>(\n            prims_to_reconstruct),\n        get<tmpl::at_c<dg_package_data_primitive_tags, 3>>(\n            prims_to_reconstruct),\n\n        normal_covector, normal_vector, face_mesh_velocity,\n        normal_dot_mesh_velocity,\n        *(soln.equation_of_state().promote_to_3d_eos()));\n\n    DataVector interface_data{dg_packaged_data.size(),\n                              std::numeric_limits<double>::signaling_NaN()};\n    std::copy(std::data(dg_packaged_data),\n              std::next(std::data(dg_packaged_data),\n                        static_cast<std::ptrdiff_t>(dg_packaged_data.size())),\n              interface_data.begin());\n\n    auto& the_mortar_data = mortar_data[DirectionalId<3>{\n        direction, *element.neighbors().at(direction).begin()}];\n    if (local_data) {\n      the_mortar_data.local().face_mesh = interface_mesh;\n      the_mortar_data.local().mortar_data = std::move(interface_data);\n    } else {\n      the_mortar_data.neighbor().face_mesh = interface_mesh;\n      the_mortar_data.neighbor().mortar_data = std::move(interface_data);\n    }\n  };\n  insert_dg_data(Direction<3>::lower_zeta(), true);\n  insert_dg_data(Direction<3>::lower_xi(), false);\n\n  // Below are also dummy variables required for compilation due to boundary\n  // condition FD ghost data. Since the element used here for testing has\n  // neighbors in all directions, BoundaryConditionGhostData::apply() is not\n  // actually called so it is okay to leave these variables somewhat poorly\n  // initialized.\n  typename evolution::dg::Tags::NormalCovectorAndMagnitude<3>::type\n      dummy_normal_covector_and_magnitude{};\n  using DampingFunction = ConstraintDamping::DampingFunction<3, Frame::Grid>;\n  typename evolution::dg::subcell::Tags::ReconstructionOrder<3>::type\n      dummy_reconstruction_order{};\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Element<3>, evolution::dg::subcell::Tags::Mesh<3>,\n          domain::Tags::Mesh<3>, fd::Tags::Reconstructor<System>,\n          evolution::Tags::BoundaryCorrection,\n          hydro::Tags::GrmhdEquationOfState,\n          typename System::primitive_variables_tag, dt_variables_tag,\n          variables_tag,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<3>,\n          evolution::dg::subcell::Tags::ReconstructionOrder<3>,\n          ValenciaDivClean::Tags::ConstraintDampingParameter,\n          evolution::dg::Tags::MortarData<3>,\n          domain::Tags::ElementMap<3, Frame::Grid>,\n          domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                      Frame::Inertial>,\n          domain::Tags::Domain<3>, domain::Tags::ExternalBoundaryConditions<3>,\n          domain::Tags::MeshVelocity<3, Frame::Inertial>,\n          domain::Tags::DivMeshVelocity,\n          domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                        Frame::Inertial>,\n          evolution::dg::Tags::NormalCovectorAndMagnitude<3>, ::Tags::Time,\n          domain::Tags::FunctionsOfTimeInitialize,\n          Parallel::Tags::MetavariablesImpl<DummyEvolutionMetaVars<System>>,\n          gh::Tags::DampingFunctionGamma0<3, Frame::Grid>,\n          gh::Tags::DampingFunctionGamma1<3, Frame::Grid>,\n          gh::Tags::DampingFunctionGamma2<3, Frame::Grid>,\n          ::gh::gauges::Tags::GaugeCondition,\n          grmhd::GhValenciaDivClean::fd::Tags::FilterOptions,\n          ::Tags::VariableFixer<::VariableFixing::FixToAtmosphere<3>>>,\n      db::AddComputeTags<\n          evolution::dg::subcell::Tags::LogicalCoordinatesCompute<3>,\n          ::domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<3, Frame::Grid>,\n              evolution::dg::subcell::Tags::Coordinates<3,\n                                                        Frame::ElementLogical>,\n              evolution::dg::subcell::Tags::Coordinates>,\n          evolution::dg::subcell::Tags::InertialCoordinatesCompute<\n              ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                            Frame::Inertial>>,\n          evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGridCompute<\n              ::domain::Tags::ElementMap<3, Frame::Grid>, 3>,\n          evolution::dg::subcell::fd::Tags::\n              DetInverseJacobianLogicalToGridCompute<3>,\n          evolution::dg::subcell::fd::Tags::\n              InverseJacobianLogicalToInertialCompute<\n                  ::domain::CoordinateMaps::Tags::CoordinateMap<\n                      3, Frame::Grid, Frame::Inertial>,\n                  3>,\n          evolution::dg::subcell::fd::Tags::\n              DetInverseJacobianLogicalToInertialCompute<\n                  ::domain::CoordinateMaps::Tags::CoordinateMap<\n                      3, Frame::Grid, Frame::Inertial>,\n                  3>,\n          domain::Tags::DetInvJacobianCompute<3, Frame::ElementLogical,\n                                              Frame::Inertial>,\n          gr::Tags::SpatialMetricCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::DetAndInverseSpatialMetricCompute<DataVector, 3,\n                                                      Frame::Inertial>,\n          gr::Tags::SqrtDetSpatialMetricCompute<DataVector, 3,\n                                                Frame::Inertial>>>(\n      element, subcell_mesh, dg_mesh,\n      std::unique_ptr<grmhd::GhValenciaDivClean::fd::Reconstructor<System>>{\n          std::make_unique<\n              grmhd::GhValenciaDivClean::fd::MonotonisedCentralPrim<System>>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<BoundaryCorrections::ProductOfCorrections<\n              gh::BoundaryCorrections::UpwindPenalty<3>,\n              ValenciaDivClean::BoundaryCorrections::Hll>>(\n              gh::BoundaryCorrections::UpwindPenalty<3>{},\n              ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8})},\n      soln.equation_of_state().promote_to_3d_eos(), cell_centered_prim_vars,\n      // Set incorrect size for dt variables because they should get resized.\n      Variables<typename dt_variables_tag::tags_list>{}, initial_variables,\n      neighbor_data, dummy_reconstruction_order, 1.0, mortar_data,\n      std::move(element_map),\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{}),\n      std::move(domain), std::move(external_boundary_conditions),\n      dg_mesh_velocity, div_dg_mesh_velocity,\n      dg_logical_to_inertial_inv_jacobian, dummy_normal_covector_and_magnitude,\n      time, clone_unique_ptrs(functions_of_time),\n      DummyEvolutionMetaVars<System>{},\n      // Note: These damping functions all assume Grid==Inertial. We need to\n      // rescale the widths in the Grid frame for binaries.\n      std::unique_ptr<DampingFunction>(  // Gamma0, taken from SpEC BNS\n          std::make_unique<\n              ConstraintDamping::GaussianPlusConstant<3, Frame::Grid>>(\n              0.01, 0.09 * 1.0 / 1.4, 5.5 * 1.4, std::array{0.0, 0.0, 0.0})),\n      gamma1->get_clone(), gamma2->get_clone(),\n      std::unique_ptr<gh::gauges::GaugeCondition>(\n          std::make_unique<gh::gauges::AnalyticChristoffel>(soln.get_clone())),\n      grmhd::GhValenciaDivClean::fd::FilterOptions{0.001},\n      // Just use a default-constructed fixer and have the reconstructor not\n      // call it.\n      ::VariableFixing::FixToAtmosphere<3>{});\n\n  db::mutate_apply<ValenciaDivClean::ConservativeFromPrimitive>(\n      make_not_null(&box));\n\n  subcell::TimeDerivative<System>::apply(make_not_null(&box));\n\n  // We test that the time derivative converges to zero,\n  // so we remove the expected value of the time derivative for moving meshes\n  Variables<evolved_tags> output_minus_expected_dt_vars{\n      subcell_mesh.number_of_grid_points()};\n  const auto& dt_vars = db::get<dt_variables_tag>(box);\n\n  tmpl::for_each<conserved_tags>(\n      [&box, &subcell_mesh, &expansion_velocity, &cell_centered_coords,\n       &cell_centered_logical_to_inertial_inv_jacobian,\n       &output_minus_expected_dt_vars, &dt_vars](auto var_tag_v) {\n        using var_tag = tmpl::type_from<decltype(var_tag_v)>;\n        const auto& var = get<var_tag>(box);\n        const auto deriv_var = partial_derivative(\n            var, subcell_mesh, cell_centered_logical_to_inertial_inv_jacobian);\n\n        auto& output_minus_expected_dt_var =\n            get<var_tag>(output_minus_expected_dt_vars);\n        const auto& output_dt_var = get<::Tags::dt<var_tag>>(dt_vars);\n        for (size_t i = 0; i < output_minus_expected_dt_var.size(); ++i) {\n          output_minus_expected_dt_var[i] = output_dt_var[i];\n          if (expansion_velocity.has_value()) {\n            for (size_t j = 0; j < 3; ++j) {\n              const auto deriv_index =\n                  j * output_minus_expected_dt_var.size() + i;\n              output_minus_expected_dt_var[i] -= cell_centered_coords.get(j) *\n                                                 deriv_var[deriv_index] *\n                                                 expansion_velocity.value();\n            }\n          }\n        }\n      });\n\n  // We need the gradient of the GR variable (and partial_derivative, as is,\n  // does not work)\n  using gh_gradient_tags = typename TimeDerivativeTerms::gh_gradient_tags;\n  const auto& gh_evolved_vars = db::get<variables_tag>(box);\n  Variables<db::wrap_tags_in<::Tags::deriv, gh_gradient_tags, tmpl::size_t<3>,\n                             Frame::Inertial>>\n      cell_centered_gh_derivs{subcell_mesh.number_of_grid_points()};\n  const size_t fd_deriv_order = 4;\n  grmhd::GhValenciaDivClean::fd::spacetime_derivatives<System>(\n      make_not_null(&cell_centered_gh_derivs), gh_evolved_vars,\n      db::get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box),\n      fd_deriv_order, subcell_mesh,\n      cell_centered_logical_to_inertial_inv_jacobian);\n\n  auto& temp = get<gr::Tags::SpacetimeMetric<DataVector, 3>>(\n      output_minus_expected_dt_vars);\n  temp = get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(dt_vars);\n\n  tmpl::for_each<gh_variables_tags>([&expansion_velocity, &cell_centered_coords,\n                                     &output_minus_expected_dt_vars, &dt_vars,\n                                     &cell_centered_gh_derivs](auto var_tag_v) {\n    using var_tag = tmpl::type_from<decltype(var_tag_v)>;\n    using grad_tag = ::Tags::deriv<var_tag, tmpl::size_t<3>, Frame::Inertial>;\n    using FluxTensor = typename grad_tag::type;\n    const auto& deriv_var = get<grad_tag>(cell_centered_gh_derivs);\n\n    auto& output_minus_expected_dt_var =\n        get<var_tag>(output_minus_expected_dt_vars);\n    const auto& output_dt_var = get<::Tags::dt<var_tag>>(dt_vars);\n    for (size_t i = 0; i < output_minus_expected_dt_var.size(); ++i) {\n      output_minus_expected_dt_var[i] = output_dt_var[i];\n      if (expansion_velocity.has_value()) {\n        const auto tensor_index = output_dt_var.get_tensor_index(i);\n        for (size_t j = 0; j < 3; ++j) {\n          const auto deriv_index =\n              FluxTensor::get_storage_index(prepend(tensor_index, j));\n          output_minus_expected_dt_var[i] -= cell_centered_coords.get(j) *\n                                             deriv_var[deriv_index] *\n                                             expansion_velocity.value();\n        }\n      }\n    }\n  });\n\n  return std::max(\n      {max(abs(get(get<grmhd::ValenciaDivClean::Tags::TildeD>(\n           output_minus_expected_dt_vars)))),\n       max(abs(get(get<grmhd::ValenciaDivClean::Tags::TildeYe>(\n           output_minus_expected_dt_vars)))),\n       max(abs(get(get<grmhd::ValenciaDivClean::Tags::TildeTau>(\n           output_minus_expected_dt_vars)))),\n       max(get(magnitude(get<grmhd::ValenciaDivClean::Tags::TildeS<>>(\n           output_minus_expected_dt_vars)))),\n       max(get(magnitude(get<grmhd::ValenciaDivClean::Tags::TildeB<>>(\n           output_minus_expected_dt_vars)))),\n       max(get(pointwise_l2_norm(get<gr::Tags::SpacetimeMetric<DataVector, 3>>(\n           output_minus_expected_dt_vars)))),\n       max(get(\n           pointwise_l2_norm(get<gh::Tags::Pi<DataVector, 3, Frame::Inertial>>(\n               output_minus_expected_dt_vars)))),\n       max(get(\n           pointwise_l2_norm(get<gh::Tags::Phi<DataVector, 3, Frame::Inertial>>(\n               output_minus_expected_dt_vars))))});\n}\n\n// [[Timeout, 20]]\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GhValenciaDivClean.Subcell.TimeDerivative\",\n    \"[Unit][Evolution]\") {\n  // This tests sets up a cube [-4,4]^3 in a TOV star spacetime and verifies\n  // that the time derivative vanishes. Or, more specifically, that the time\n  // derivative decreases with increasing resolution and is below 1.0e-6.\n\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  std::optional<double> expansion_velocity = {};\n\n  CHECK(test<System>(4, expansion_velocity, false) >\n        test<System>(8, expansion_velocity, false));\n  CHECK(test<System>(8, expansion_velocity, false) < 1.0e-6);\n\n  expansion_velocity = 0.5;\n\n  CHECK(test<System>(4, expansion_velocity, false) >\n        test<System>(8, expansion_velocity, false));\n  CHECK(test<System>(8, expansion_velocity, false) < 1.0e-6);\n\n  CHECK(test<System>(4, expansion_velocity, true) >\n        test<System>(8, expansion_velocity, true));\n  CHECK(test<System>(8, expansion_velocity, true) < 1.0e-6);\n}\n}  // namespace\n}  // namespace grmhd::GhValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/Test_ZeroTimeDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Subcell/ZeroTimeDerivatives.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/NoNeutrinos/System.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GhValenciaDivClean.Subcell.ZeroTimeDerivatives\",\n    \"[Unit][Evolution]\") {\n  using NeutrinoTransportSystem = RadiationTransport::NoNeutrinos::System;\n  using System = grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n  using DtVarsTag = ::Tags::Variables<\n      db::wrap_tags_in<::Tags::dt, typename System::variables_tag::tags_list>>;\n  using DtVars = typename DtVarsTag::type;\n  using MhdTags =\n      typename grmhd::ValenciaDivClean::System::variables_tag::tags_list;\n  using SubcellOptions = evolution::dg::subcell::SubcellOptions;\n  const size_t num_points = 10;\n\n  const domain::creators::Sphere sphere(\n      10.0, 80.0, domain::creators::Sphere::InnerCube{0.0}, 3_st, 8_st, false);\n  REQUIRE(sphere.create_domain().blocks().size() == 7);\n  REQUIRE(sphere.create_domain().block_names().at(6) == \"InnerCube\");\n\n  const SubcellOptions subcell_options{\n      SubcellOptions{4.0, 1, 1.0e-4, 1.0e-4, false, false,\n                     evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n                     true, std::vector<std::string>{\"InnerCube\"},\n                     fd::DerivativeOrder::Two, 10, 10, 2},\n      sphere};\n\n  // This shouldn't alter anything because we don't have a neighbor doing\n  // DG-only\n  const Element<3> element_in_dg_only{\n      ElementId<3>{0, {}},\n      {{Direction<3>::lower_xi(),\n        Neighbors<3>{ElementId<3>{5, {}},\n                     OrientationMap<3>::create_aligned()}}}};\n  auto box_in_dg_only = db::create<\n      tmpl::list<DtVarsTag, evolution::dg::subcell::Tags::SubcellOptions<3>,\n                 domain::Tags::Element<3>>>(\n      DtVars{num_points, 1.2345}, subcell_options, element_in_dg_only);\n  db::mutate_apply<\n      grmhd::GhValenciaDivClean::subcell::ZeroMhdTimeDerivatives<System>>(\n      make_not_null(&box_in_dg_only));\n  CHECK(db::get<DtVarsTag>(box_in_dg_only) == DtVars{num_points, 1.2345});\n\n  // Check that we are mutating if our neighbor is locked to DG.\n  const Element<3> element_neighboring_dg_only{\n      ElementId<3>{0, {}},\n      {{Direction<3>::lower_xi(),\n        Neighbors<3>{ElementId<3>{6, {}},\n                     OrientationMap<3>::create_aligned()}}}};\n  auto box_neighboring_dg_only = db::create<\n      tmpl::list<DtVarsTag, evolution::dg::subcell::Tags::SubcellOptions<3>,\n                 domain::Tags::Element<3>>>(\n      DtVars{num_points, 1.2345}, subcell_options, element_neighboring_dg_only);\n  db::mutate_apply<\n      grmhd::GhValenciaDivClean::subcell::ZeroMhdTimeDerivatives<System>>(\n      make_not_null(&box_neighboring_dg_only));\n  {\n    DtVars expected{num_points, 1.2345};\n    tmpl::for_each<MhdTags>([&expected]<class Tag>(tmpl::type_<Tag> /*meta*/) {\n      auto& var = get<::Tags::dt<Tag>>(expected);\n      for (size_t storage_index = 0; storage_index < var.size();\n           ++storage_index) {\n        var[storage_index] = 0.0;\n      }\n    });\n    CHECK(db::get<DtVarsTag>(box_neighboring_dg_only) == expected);\n  }\n\n  // Check that if we are doing DG-only and have a neighboring doing DG-only\n  // that we don't zero out data.\n  const Element<3> element_neighboring_and_in_dg_only{\n      ElementId<3>{6, {}},\n      {{Direction<3>::lower_xi(),\n        Neighbors<3>{ElementId<3>{6, {}},\n                     OrientationMap<3>::create_aligned()}}}};\n  auto box_neighboring_and_in_dg_only = db::create<\n      tmpl::list<DtVarsTag, evolution::dg::subcell::Tags::SubcellOptions<3>,\n                 domain::Tags::Element<3>>>(DtVars{num_points, 1.2345},\n                                            subcell_options,\n                                            element_neighboring_and_in_dg_only);\n  db::mutate_apply<\n      grmhd::GhValenciaDivClean::subcell::ZeroMhdTimeDerivatives<System>>(\n      make_not_null(&box_neighboring_and_in_dg_only));\n  CHECK(db::get<DtVarsTag>(box_neighboring_and_in_dg_only) ==\n        DtVars{num_points, 1.2345});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Characteristics.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n\nnamespace {\nvoid check_max_char_speed(const DataVector& used_for_size) {\n  MAKE_GENERATOR(gen);\n\n  const Scalar<DataVector> gamma_1{used_for_size.size(), 0.0};\n  const auto lapse = TestHelpers::gr::random_lapse(&gen, used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<3>(&gen, used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<3>(&gen, used_for_size);\n\n  double max_char_speed = std::numeric_limits<double>::signaling_NaN();\n  grmhd::GhValenciaDivClean::Tags::ComputeLargestCharacteristicSpeed<\n      Frame::Inertial>::function(make_not_null(&max_char_speed), lapse, shift,\n                                 spatial_metric);\n\n  double gh_max_char_speed = std::numeric_limits<double>::signaling_NaN();\n  gh::Tags::ComputeLargestCharacteristicSpeed<3, Frame::Inertial>::function(\n      make_not_null(&gh_max_char_speed), gamma_1, lapse, shift, spatial_metric);\n  CHECK(max_char_speed == gh_max_char_speed);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.MaxCharSpeed\",\n    \"[Unit][Evolution]\") {\n  check_max_char_speed(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Test_Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Constraints.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.Constraints\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_compute_tag<\n      grmhd::GhValenciaDivClean::Tags::FConstraintCompute<3, Frame::Inertial>>(\n      \"FConstraint\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Test_SetPiAndPhiFromConstraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Dispatch.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/SetPiAndPhiFromConstraints.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing evolved_vars_tags =\n    tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n               gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>;\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using simple_tags = db::AddSimpleTags<\n      ::Tags::Time, ::Tags::Variables<evolved_vars_tags>, domain::Tags::Mesh<3>,\n      domain::Tags::ElementMap<3, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                  Frame::Inertial>,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::Coordinates<3, Frame::ElementLogical>,\n      evolution::dg::subcell::Tags::ActiveGrid>;\n  using compute_tags = db::AddComputeTags<\n      evolution::dg::subcell::Tags::MeshCompute<3>,\n      evolution::dg::subcell::Tags::LogicalCoordinatesCompute<3>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<grmhd::GhValenciaDivClean::SetPiAndPhiFromConstraints>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.GhValenciaDivClean.\"\n    \"SetPiAndPhiFromConstraints\",\n    \"[Unit][Evolution][Actions]\") {\n  MAKE_GENERATOR(generator);\n  using component = Component<Metavariables>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\n\n  register_classes_with_charm<\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Grid,\n                            domain::CoordinateMaps::Identity<3>>,\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<3>>,\n      gh::gauges::DampedHarmonic>();\n\n  std::uniform_real_distribution<> metric_dist(0.1, 1.);\n  std::uniform_real_distribution<> deriv_dist(-1.e-5, 1.e-5);\n\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto make_vars = [&generator, &deriv_dist,\n                          &metric_dist](const Mesh<3>& mesh) {\n    const size_t num_points = mesh.number_of_grid_points();\n    Variables<evolved_vars_tags> evolved_vars{mesh.number_of_grid_points()};\n    get<gh::Tags::Pi<DataVector, 3>>(evolved_vars) =\n        make_with_random_values<tnsr::aa<DataVector, 3, Frame::Inertial>>(\n            make_not_null(&generator), make_not_null(&deriv_dist), num_points);\n    get<gh::Tags::Phi<DataVector, 3>>(evolved_vars) =\n        make_with_random_values<tnsr::iaa<DataVector, 3, Frame::Inertial>>(\n            make_not_null(&generator), make_not_null(&deriv_dist), num_points);\n    get<gr::Tags::SpacetimeMetric<DataVector, 3>>(evolved_vars) =\n        make_with_random_values<tnsr::aa<DataVector, 3, Frame::Inertial>>(\n            make_not_null(&generator), make_not_null(&metric_dist), num_points);\n    get<0, 0>(get<gr::Tags::SpacetimeMetric<DataVector, 3>>(evolved_vars)) +=\n        -2.0;\n    for (size_t i = 0; i < 3; ++i) {\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(evolved_vars)\n          .get(i + 1, i + 1) += 4.0;\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(evolved_vars)\n          .get(i + 1, 0) *= 0.01;\n    }\n    return evolved_vars;\n  };\n\n  const auto initial_dg_vars = make_vars(dg_mesh);\n\n  // Testing SetPiAndPhiFromConstraints = False.\n  {\n    MockRuntimeSystem runner{\n        {std::unique_ptr<gh::gauges::GaugeCondition>(\n            std::make_unique<gh::gauges::DampedHarmonic>(\n                100., std::array{1.2, 1.5, 1.7}, std::array{2, 4, 6}))},\n        {false}};\n    ActionTesting::emplace_component_and_initialize<component>(\n        &runner, 0,\n        {0., initial_dg_vars, dg_mesh,\n         ElementMap<3, Frame::Grid>{\n             ElementId<3>{0},\n             domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n                 domain::CoordinateMaps::Identity<3>{})},\n         domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n             domain::CoordinateMaps::Identity<3>{}),\n         std::unordered_map<\n             std::string,\n             std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{},\n         logical_coordinates(dg_mesh), evolution::dg::subcell::ActiveGrid::Dg});\n\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n\n    // Should be exact since we didn't compute anything\n    const auto& box = ActionTesting::get_databox<component>(runner, 0);\n    CHECK(get<gh::Tags::Pi<DataVector, 3>>(initial_dg_vars) ==\n          db::get<gh::Tags::Pi<DataVector, 3>>(box));\n    CHECK(get<gh::Tags::Phi<DataVector, 3>>(initial_dg_vars) ==\n          db::get<gh::Tags::Phi<DataVector, 3>>(box));\n  }\n\n  MockRuntimeSystem runner{\n      {std::unique_ptr<gh::gauges::GaugeCondition>(\n          std::make_unique<gh::gauges::DampedHarmonic>(\n              100., std::array{1.2, 1.5, 1.7}, std::array{2, 4, 6}))},\n      {true}};\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {0., initial_dg_vars, dg_mesh,\n       ElementMap<3, Frame::Grid>{\n           ElementId<3>{0},\n           domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n               domain::CoordinateMaps::Identity<3>{})},\n       domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n           domain::CoordinateMaps::Identity<3>{}),\n       std::unordered_map<\n           std::string,\n           std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{},\n       logical_coordinates(dg_mesh), evolution::dg::subcell::ActiveGrid::Dg});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  auto& box = ActionTesting::get_databox<component>(make_not_null(&runner), 0);\n\n  const auto initial_subcell_vars =\n      make_vars(db::get<evolution::dg::subcell::Tags::Mesh<3>>(box));\n\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n\n  const auto check = [&box](const Mesh<3>& mesh, const auto& initial_vars) {\n    CAPTURE(mesh);\n    tnsr::aa<DataVector, 3, Frame::Inertial> expected_pi =\n        get<gh::Tags::Pi<DataVector, 3>>(initial_vars);\n    tnsr::iaa<DataVector, 3, Frame::Inertial> expected_phi =\n        get<gh::Tags::Phi<DataVector, 3>>(initial_vars);\n    gh::gauges::SetPiAndPhiFromConstraints<\n        ghmhd::GhValenciaDivClean::InitialData::\n            analytic_solutions_and_data_list,\n        3>::impl(make_not_null(&expected_pi), make_not_null(&expected_phi), 0.,\n                 mesh, db::get<domain::Tags::ElementMap<3, Frame::Grid>>(box),\n                 db::get<domain::CoordinateMaps::Tags::CoordinateMap<\n                     3, Frame::Grid, Frame::Inertial>>(box),\n                 db::get<domain::Tags::FunctionsOfTime>(box),\n                 logical_coordinates(mesh),\n                 get<gr::Tags::SpacetimeMetric<DataVector, 3>>(initial_vars),\n                 db::get<gh::gauges::Tags::GaugeCondition>(box), true);\n\n    const auto& pi = db::get<gh::Tags::Pi<DataVector, 3>>(box);\n    CHECK(pi == expected_pi);\n    const auto& phi = db::get<gh::Tags::Phi<DataVector, 3>>(box);\n    CHECK(phi == expected_phi);\n  };\n\n  // Check that initial grid is fine.\n  check(db::get<domain::Tags::Mesh<3>>(box), initial_dg_vars);\n\n  // Switch to subcell grid and check we compute Pi correctly\n  db::mutate<evolution::dg::subcell::Tags::ActiveGrid,\n             ::Tags::Variables<evolved_vars_tags>>(\n      [&initial_subcell_vars](const auto active_grid_ptr,\n                              const auto variables_ptr) {\n        *active_grid_ptr = evolution::dg::subcell::ActiveGrid::Subcell;\n        *variables_ptr = initial_subcell_vars;\n      },\n      make_not_null(&box));\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  check(db::get<evolution::dg::subcell::Tags::Mesh<3>>(box),\n        initial_subcell_vars);\n\n  // // Switch back to DG and check we compute Pi correctly\n  db::mutate<evolution::dg::subcell::Tags::ActiveGrid,\n             ::Tags::Variables<evolved_vars_tags>>(\n      [&initial_dg_vars](const auto active_grid_ptr, const auto variables_ptr) {\n        *active_grid_ptr = evolution::dg::subcell::ActiveGrid::Dg;\n        *variables_ptr = initial_dg_vars;\n      },\n      make_not_null(&box));\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  check(db::get<domain::Tags::Mesh<3>>(box), initial_dg_vars);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Test_StressEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.GhValenciaDivClean.StressEnergy\",\n                  \"[Unit][GrMhd]\") {\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n    \"Evolution/Systems/GrMhd/GhValenciaDivClean\"};\n\n  pypp::check_with_random_values<1>(\n      &grmhd::GhValenciaDivClean::trace_reversed_stress_energy, \"StressEnergy\",\n      {\"trace_reversed_stress_energy\", \"four_velocity_one_form\",\n       \"comoving_magnetic_field_one_form\"},\n      {{{1.0e-2, 0.5}}}, DataVector{5});\n\n  pypp::check_with_random_values<1>(\n      &grmhd::GhValenciaDivClean::add_stress_energy_term_to_dt_pi,\n      \"StressEnergy\",\n      {\"add_stress_energy_term_to_dt_pi\"},\n      {{{1.0e-2, 0.5}}}, DataVector{5}, 1.0e-12, std::random_device{}(),\n      0.1234);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GhValenciaDivClean.Tags\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<\n      grmhd::GhValenciaDivClean::Tags::TraceReversedStressEnergy>(\n      \"TraceReversedStressEnergy\");\n  TestHelpers::db::test_simple_tag<\n      grmhd::GhValenciaDivClean::Tags::StressEnergy>(\"StressEnergy\");\n  TestHelpers::db::test_simple_tag<\n      grmhd::GhValenciaDivClean::Tags::ComovingMagneticField>(\n      \"ComovingMagneticField\");\n  TestHelpers::db::test_simple_tag<\n      grmhd::GhValenciaDivClean::Tags::ComovingMagneticFieldOneForm>(\n          \"ComovingMagneticFieldOneForm\");\n  TestHelpers::db::test_simple_tag<\n      grmhd::GhValenciaDivClean::Tags::FourVelocity>(\n          \"FourVelocity\");\n  TestHelpers::db::test_simple_tag<\n      grmhd::GhValenciaDivClean::Tags::FourVelocityOneForm>(\n          \"FourVelocityOneForm\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/GhValenciaDivClean/Test_TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace {\ntemplate <typename ComputeVolumeTimeDerivativeTerms, size_t Dim,\n          typename EvolvedTagList, typename FluxTagList, typename TempTagList,\n          typename GradientTagList, typename ArgTagList>\nstruct ComputeVolumeTimeDerivativeTermsHelper;\n\ntemplate <typename ComputeVolumeTimeDerivativeTerms, size_t Dim,\n          typename... EvolvedTags, typename... FluxTags, typename... TempTags,\n          typename... GradientTags, typename... ArgTags>\nstruct ComputeVolumeTimeDerivativeTermsHelper<\n    ComputeVolumeTimeDerivativeTerms, Dim, tmpl::list<EvolvedTags...>,\n    tmpl::list<FluxTags...>, tmpl::list<TempTags...>,\n    tmpl::list<GradientTags...>, tmpl::list<ArgTags...>> {\n  template <typename EvolvedVariables, typename FluxVariables,\n            typename TemporaryVariables, typename GradientVariables,\n            typename ArgumentVariables>\n  static void apply(\n      const gsl::not_null<EvolvedVariables*> dt_vars_ptr,\n      [[maybe_unused]] const gsl::not_null<FluxVariables*> volume_fluxes,\n      const gsl::not_null<TemporaryVariables*> temporaries,\n      const GradientVariables& partial_derivs,\n      const ArgumentVariables& time_derivative_args) {\n    ComputeVolumeTimeDerivativeTerms::apply(\n        make_not_null(&get<::Tags::dt<EvolvedTags>>(*dt_vars_ptr))...,\n        make_not_null(&get<FluxTags>(*volume_fluxes))...,\n        make_not_null(&get<TempTags>(*temporaries))...,\n        get<GradientTags>(partial_derivs)..., [](const auto& t) -> const auto& {\n          if constexpr (tt::is_a_v<std::unique_ptr,\n                                   std::decay_t<decltype(t)>>) {\n            return *t;\n          } else {\n            return t;\n          }\n        }(tuples::get<ArgTags>(time_derivative_args))...);\n  }\n\n  template <typename EvolvedVariables, typename FluxVariables,\n            typename TemporaryVariables, typename GradientVariables,\n            typename ArgumentVariables>\n  static void apply_packed(\n      const gsl::not_null<EvolvedVariables*> dt_vars_ptr,\n      [[maybe_unused]] const gsl::not_null<FluxVariables*> volume_fluxes,\n      const gsl::not_null<TemporaryVariables*> temporaries,\n      const GradientVariables& partial_derivs,\n      const ArgumentVariables& time_derivative_args) {\n    ComputeVolumeTimeDerivativeTerms::apply(\n        dt_vars_ptr, volume_fluxes, temporaries,\n        get<GradientTags>(partial_derivs)..., [](const auto& t) -> const auto& {\n          if constexpr (tt::is_a_v<std::unique_ptr,\n                                   std::decay_t<decltype(t)>>) {\n            return *t;\n          } else {\n            return t;\n          }\n        }(tuples::get<ArgTags>(time_derivative_args))...);\n  }\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GhValenciaDivClean.TimeDerivativeTerms\",\n    \"[Unit][Evolution]\") {\n  using gh_variables_tags = typename gh::System<3_st>::variables_tag::tags_list;\n  using valencia_variables_tags =\n      typename grmhd::ValenciaDivClean::System::variables_tag::tags_list;\n\n  using gh_dt_variables_tags =\n      grmhd::GhValenciaDivClean::TimeDerivativeTerms::gh_dt_tags;\n  using valencia_dt_variables_tags =\n      grmhd::GhValenciaDivClean::TimeDerivativeTerms::valencia_dt_tags;\n  using dt_variables_type =\n      Variables<tmpl::append<gh_dt_variables_tags, valencia_dt_variables_tags>>;\n\n  using gh_flux_tags = tmpl::list<>;\n  using valencia_flux_tags = grmhd::GhValenciaDivClean::detail::\n      TimeDerivativeTermsImpl::valencia_flux_tags;\n  using flux_variables_type =\n      Variables<tmpl::append<gh_flux_tags, valencia_flux_tags>>;\n\n  using gh_temp_tags =\n      grmhd::GhValenciaDivClean::TimeDerivativeTerms::gh_temp_tags;\n  using valencia_temp_tags =\n      grmhd::GhValenciaDivClean::TimeDerivativeTerms::valencia_temp_tags;\n  using temp_variables_type = Variables<\n      typename grmhd::GhValenciaDivClean::TimeDerivativeTerms::temporary_tags>;\n\n  using gh_gradient_tags = tmpl::transform<\n      grmhd::GhValenciaDivClean::TimeDerivativeTerms::gh_gradient_tags,\n      tmpl::bind<::Tags::deriv, tmpl::_1, tmpl::pin<tmpl::size_t<3_st>>,\n                 tmpl::pin<Frame::Inertial>>>;\n  using valencia_gradient_tags = tmpl::list<>;\n  using gradient_variables_type = Variables<gh_gradient_tags>;\n\n  using gh_arg_tags =\n      grmhd::GhValenciaDivClean::TimeDerivativeTerms::gh_arg_tags;\n  using valencia_arg_tags =\n      grmhd::GhValenciaDivClean::TimeDerivativeTerms::valencia_arg_tags;\n  using all_valencia_arg_tags =\n      typename grmhd::ValenciaDivClean::TimeDerivativeTerms::argument_tags;\n  using SpatialMetricTag = gr::Tags::SpatialMetric<DataVector, 3>;\n  using d_SpatialMetricTag =\n      ::Tags::deriv<SpatialMetricTag, tmpl::size_t<3>, Frame::Inertial>;\n  using arg_variables_type = tuples::tagged_tuple_from_typelist<\n      tmpl::remove<tmpl::append<gh_arg_tags, valencia_arg_tags,\n                                tmpl::list<::Tags::VariableFixer<\n                                    ::VariableFixing::FixToAtmosphere<3>>>>,\n                   gr::Tags::SpatialMetric<DataVector, 3>>>;\n\n  const size_t element_size = 10_st;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  dt_variables_type expected_dt_variables{element_size};\n  dt_variables_type dt_variables{element_size};\n  // Because the tilde_d evolution equation has no source terms, the dt_tilde_d\n  // isn't set by the valencia div clean system, so we just set it arbitrarily\n  fill_with_random_values(\n      make_not_null(&get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeD>>(\n          expected_dt_variables)),\n      make_not_null(&gen), make_not_null(&dist));\n  get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeD>>(dt_variables) =\n      get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeD>>(\n          expected_dt_variables);\n\n  fill_with_random_values(\n      make_not_null(&get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeYe>>(\n          expected_dt_variables)),\n      make_not_null(&gen), make_not_null(&dist));\n  get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeYe>>(dt_variables) =\n      get<::Tags::dt<grmhd::ValenciaDivClean::Tags::TildeYe>>(\n          expected_dt_variables);\n\n  flux_variables_type expected_flux_variables{element_size};\n  flux_variables_type flux_variables{element_size};\n\n  temp_variables_type temp_variables{element_size};\n  temp_variables_type expected_temp_variables{element_size};\n\n  const auto gradient_variables =\n      make_with_random_values<gradient_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  arg_variables_type arg_variables;\n  tmpl::for_each<\n      tmpl::remove<tmpl::remove<tmpl::append<gh_arg_tags, valencia_arg_tags>,\n                                SpatialMetricTag>,\n                   d_SpatialMetricTag>>([&gen, &dist,\n                                         &arg_variables](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    if constexpr (std::is_same_v<\n                      typename tag::type,\n                      std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>>) {\n      tuples::get<tag>(arg_variables) = make_with_random_values<\n          typename tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), DataVector{element_size});\n    } else if constexpr (tt::is_a_v<Tensor, typename tag::type>) {\n      tuples::get<tag>(arg_variables) =\n          make_with_random_values<typename tag::type>(make_not_null(&gen),\n                                                      make_not_null(&dist),\n                                                      DataVector{element_size});\n    }\n  });\n  get<gh::gauges::Tags::GaugeCondition>(arg_variables) =\n      std::make_unique<gh::gauges::DampedHarmonic>(\n          100., std::array{1.2, 1.5, 1.7}, std::array{2, 4, 6});\n\n  // ensure that the signature of the metric is correct\n  {\n    auto& metric =\n        tuples::get<gr::Tags::SpacetimeMetric<DataVector, 3_st>>(arg_variables);\n    get<0, 0>(metric) += -2.0;\n    for (size_t i = 0; i < 3; ++i) {\n      metric.get(i + 1, i + 1) += 4.0;\n      metric.get(i + 1, 0) *= 0.01;\n    }\n  }\n  using Vlo =\n      typename VariableFixing::FixToAtmosphere<3>::VelocityLimitingOptions;\n  using Klo = typename VariableFixing::FixToAtmosphere<3>::KappaLimitingOptions;\n  const VariableFixing::FixToAtmosphere<3> variable_fixer_klo{\n      1.e-12, 3.e-12, Vlo{0.0, 1.e-4, 3.e-12, 1.e-11},\n      Klo{3.e-12, 1.e-3, 3.e-11, 0.01, std::nullopt, false}};\n  tuples::get<::Tags::VariableFixer<::VariableFixing::FixToAtmosphere<3>>>(\n      arg_variables) = variable_fixer_klo;\n\n  ComputeVolumeTimeDerivativeTermsHelper<\n      gh::TimeDerivative<ghmhd::GhValenciaDivClean::InitialData::\n                             analytic_solutions_and_data_list,\n                         3_st>,\n      3_st, gh_variables_tags, gh_flux_tags, gh_temp_tags, gh_gradient_tags,\n      tmpl::remove<gh_arg_tags, gr::Tags::SpatialMetric<DataVector, 3>>>::\n      apply(make_not_null(&expected_dt_variables),\n            make_not_null(&expected_flux_variables),\n            make_not_null(&expected_temp_variables), gradient_variables,\n            arg_variables);\n  // appropriately mimic the behavior of the `TimeDerivativeTerms`\n  // implementation wherein it uses the temporary tags from the Generalized\n  // Harmonic system that are applicable to the spacetime parts of the Valencia\n  // system.\n  tuples::tagged_tuple_from_typelist<all_valencia_arg_tags>\n      all_valencia_argument_variables{};\n\n  tmpl::for_each<tmpl::remove<all_valencia_arg_tags, SpatialMetricTag>>(\n      [&arg_variables, &expected_temp_variables,\n       &all_valencia_argument_variables](const auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        if constexpr (tmpl::list_contains_v<gh_temp_tags, tag>) {\n          tuples::get<tag>(all_valencia_argument_variables) =\n              get<tag>(expected_temp_variables);\n        } else if constexpr (std::is_same_v<\n                                 tag, ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                                    tmpl::size_t<3>,\n                                                    Frame::Inertial>>) {\n          tuples::get<tag>(all_valencia_argument_variables) =\n              gh::spatial_deriv_of_lapse(\n                  get<gr::Tags::Lapse<DataVector>>(expected_temp_variables),\n                  get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(\n                      expected_temp_variables),\n                  get<gh::Tags::Phi<DataVector, 3>>(arg_variables));\n          get<tag>(expected_temp_variables) =\n              tuples::get<tag>(all_valencia_argument_variables);\n        } else if constexpr (std::is_same_v<\n                                 tag, ::Tags::deriv<\n                                          gr::Tags::Shift<DataVector, 3>,\n                                          tmpl::size_t<3>, Frame::Inertial>>) {\n          tuples::get<tag>(all_valencia_argument_variables) =\n              gh::spatial_deriv_of_shift(\n                  get<gr::Tags::Lapse<DataVector>>(expected_temp_variables),\n                  get<gr::Tags::InverseSpacetimeMetric<DataVector, 3>>(\n                      expected_temp_variables),\n                  get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(\n                      expected_temp_variables),\n                  get<gh::Tags::Phi<DataVector, 3>>(arg_variables));\n          get<tag>(expected_temp_variables) =\n              tuples::get<tag>(all_valencia_argument_variables);\n        } else if constexpr (std::is_same_v<tag, gr::Tags::ExtrinsicCurvature<\n                                                     DataVector, 3>>) {\n          tuples::get<tag>(all_valencia_argument_variables) =\n              gh::extrinsic_curvature(\n                  get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(\n                      expected_temp_variables),\n                  get<gh::Tags::Pi<DataVector, 3>>(arg_variables),\n                  get<gh::Tags::Phi<DataVector, 3>>(arg_variables));\n          get<tag>(expected_temp_variables) =\n              tuples::get<tag>(all_valencia_argument_variables);\n        } else {\n          tuples::get<tag>(all_valencia_argument_variables) =\n              tuples::get<tag>(arg_variables);\n        }\n      });\n  // Set spatial metric and its derivative\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      make_const_view(\n          make_not_null(\n              &std::as_const(get<gr::Tags::SpatialMetric<DataVector, 3>>(\n                                 all_valencia_argument_variables))\n                   .get(i, j)),\n          get<gr::Tags::SpacetimeMetric<DataVector, 3>>(arg_variables)\n              .get(i + 1, j + 1),\n          0, element_size);\n    }\n  }\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = j; k < 3; ++k) {\n        make_const_view(\n            make_not_null(&std::as_const(get<d_SpatialMetricTag>(\n                                             all_valencia_argument_variables))\n                               .get(i, j, k)),\n            get<gh::Tags::Phi<DataVector, 3>>(arg_variables)\n                .get(i, j + 1, k + 1),\n            0, element_size);\n      }\n    }\n  }\n\n  ComputeVolumeTimeDerivativeTermsHelper<\n      grmhd::ValenciaDivClean::TimeDerivativeTerms, 3_st,\n      valencia_variables_tags, valencia_flux_tags, valencia_temp_tags,\n      valencia_gradient_tags,\n      all_valencia_arg_tags>::apply(make_not_null(&expected_dt_variables),\n                                    make_not_null(&expected_flux_variables),\n                                    make_not_null(&expected_temp_variables),\n                                    gradient_variables,\n                                    all_valencia_argument_variables);\n\n  // compute the stress energy for the expected variables\n  grmhd::GhValenciaDivClean::trace_reversed_stress_energy(\n      make_not_null(\n          &get<grmhd::GhValenciaDivClean::Tags::TraceReversedStressEnergy>(\n              expected_temp_variables)),\n      make_not_null(&get<grmhd::GhValenciaDivClean::Tags::FourVelocityOneForm>(\n          expected_temp_variables)),\n      make_not_null(\n          &get<grmhd::GhValenciaDivClean::Tags::ComovingMagneticFieldOneForm>(\n              expected_temp_variables)),\n      tuples::get<hydro::Tags::RestMassDensity<DataVector>>(arg_variables),\n      get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3, Frame::Inertial>>(\n          expected_temp_variables),\n      get<hydro::Tags::MagneticFieldOneForm<DataVector, 3>>(\n          expected_temp_variables),\n      get<hydro::Tags::MagneticFieldSquared<DataVector>>(\n          expected_temp_variables),\n      get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n          expected_temp_variables),\n      tuples::get<hydro::Tags::LorentzFactor<DataVector>>(arg_variables),\n      get<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::\n              OneOverLorentzFactorSquared>(expected_temp_variables),\n      tuples::get<hydro::Tags::Pressure<DataVector>>(arg_variables),\n      tuples::get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n          arg_variables),\n      tuples::get<gr::Tags::SpacetimeMetric<DataVector, 3_st>>(arg_variables),\n      get<gr::Tags::Shift<DataVector, 3_st>>(expected_temp_variables),\n      get<gr::Tags::Lapse<DataVector>>(expected_temp_variables));\n  dt_variables_type expected_dt_vars_no_stress_tensor{\n      expected_dt_variables.number_of_grid_points(), 0.0};\n  tmpl::for_each<gh_dt_variables_tags>(\n      [&expected_dt_vars_no_stress_tensor,\n       &expected_dt_variables]<class Tag>(tmpl::type_<Tag> /*meta*/) {\n        get<Tag>(expected_dt_vars_no_stress_tensor) =\n            get<Tag>(expected_dt_variables);\n      });\n\n  // apply the correction to dt pi for the expected variables\n  grmhd::GhValenciaDivClean::add_stress_energy_term_to_dt_pi(\n      make_not_null(&get<::Tags::dt<gh::Tags::Pi<DataVector, 3_st>>>(\n          expected_dt_variables)),\n      get<grmhd::GhValenciaDivClean::Tags::TraceReversedStressEnergy>(\n          expected_temp_variables),\n      get<gr::Tags::Lapse<DataVector>>(expected_temp_variables));\n\n  ComputeVolumeTimeDerivativeTermsHelper<\n      grmhd::GhValenciaDivClean::TimeDerivativeTerms, 3_st,\n      tmpl::append<gh_variables_tags, valencia_variables_tags>,\n      typename flux_variables_type::tags_list,\n      typename temp_variables_type::tags_list,\n      typename gradient_variables_type::tags_list,\n      tmpl::remove<tmpl::remove<typename arg_variables_type::tags_list,\n                                SpatialMetricTag>,\n                   d_SpatialMetricTag>>::\n      apply_packed(make_not_null(&dt_variables), make_not_null(&flux_variables),\n                   make_not_null(&temp_variables), gradient_variables,\n                   arg_variables);\n\n  const Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(dt_variables, expected_dt_variables,\n                                custom_approx);\n  CHECK_VARIABLES_CUSTOM_APPROX(flux_variables, expected_flux_variables,\n                                custom_approx);\n  CHECK_VARIABLES_CUSTOM_APPROX(temp_variables, expected_temp_variables,\n                                custom_approx);\n\n  {\n    INFO(\n        \"Check that having tiny density means we skip computing the MHD \"\n        \"system.\");\n    get(tuples::get<hydro::Tags::RestMassDensity<DataVector>>(arg_variables)) =\n        0.0;\n    tmpl::for_each<valencia_flux_tags>([&flux_variables]<class Tag>(\n                                           tmpl::type_<Tag> /*meta*/) {\n      for (size_t storage_index = 0;\n           storage_index < get<Tag>(flux_variables).size(); ++storage_index) {\n        get<Tag>(flux_variables)[storage_index] = 1.0e300;\n      }\n    });\n\n    dt_variables.initialize(dt_variables.number_of_grid_points(), 0.0);\n\n    ComputeVolumeTimeDerivativeTermsHelper<\n        grmhd::GhValenciaDivClean::TimeDerivativeTerms, 3_st,\n        tmpl::append<gh_variables_tags, valencia_variables_tags>,\n        typename flux_variables_type::tags_list,\n        typename temp_variables_type::tags_list,\n        typename gradient_variables_type::tags_list,\n        tmpl::remove<tmpl::remove<typename arg_variables_type::tags_list,\n                                  SpatialMetricTag>,\n                     d_SpatialMetricTag>>::\n        apply_packed(\n            make_not_null(&dt_variables), make_not_null(&flux_variables),\n            make_not_null(&temp_variables), gradient_variables, arg_variables);\n\n    expected_flux_variables.initialize(\n        expected_flux_variables.number_of_grid_points(), 0.0);\n    CHECK_VARIABLES_APPROX(flux_variables, expected_flux_variables);\n\n    CHECK_VARIABLES_APPROX(dt_variables, expected_dt_vars_no_stress_tensor);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Actions/Test_NumericInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Actions/NumericInitialData.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace grmhd::ValenciaDivClean::Actions {\nnamespace {\n\nusing all_hydro_vars = hydro::grmhd_tags<DataVector>;\n\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using component_being_mocked = void;  // Not needed\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              tmpl::list<::Tags::Variables<all_hydro_vars>,\n                         hydro::Tags::GrmhdEquationOfState,\n                         gr::Tags::InverseSpatialMetric<DataVector, 3>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<grmhd::ValenciaDivClean::Actions::ReadNumericInitialData,\n                     grmhd::ValenciaDivClean::Actions::SetNumericInitialData>>>;\n};\n\nstruct MockReadVolumeData {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      DataBox& /*box*/, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const importers::ImporterOptions& options, const size_t volume_data_id,\n      tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n          importers::Tags::Selected, NumericInitialData::all_vars>>\n          selected_fields) {\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        get<evolution::initial_data::Tags::InitialData>(cache));\n    CHECK(options == initial_data.importer_options());\n    CHECK(volume_data_id == initial_data.volume_data_id());\n    CHECK(get<importers::Tags::Selected<\n              hydro::Tags::RestMassDensity<DataVector>>>(selected_fields) ==\n          \"CustomRho\");\n    CHECK(get<importers::Tags::Selected<\n              hydro::Tags::LowerSpatialFourVelocity<DataVector, 3>>>(\n              selected_fields) == \"CustomUi\");\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockVolumeDataReader {\n  using component_being_mocked = importers::ElementDataReader<Metavariables>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using replace_these_simple_actions =\n      tmpl::list<importers::Actions::ReadAllVolumeDataAndDistribute<\n          metavariables::volume_dim, NumericInitialData::all_vars,\n          MockElementArray<Metavariables>>>;\n  using with_these_simple_actions = tmpl::list<MockReadVolumeData>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  using component_list = tmpl::list<MockElementArray<Metavariables>,\n                                    MockVolumeDataReader<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<evolution::initial_data::InitialData,\n                             tmpl::list<NumericInitialData>>>;\n  };\n};\n\nvoid test_numeric_initial_data(const NumericInitialData& initial_data,\n                               const std::string& option_string) {\n  {\n    INFO(\"Factory creation\");\n    const auto created = TestHelpers::test_creation<\n        std::unique_ptr<evolution::initial_data::InitialData>, Metavariables>(\n        option_string);\n    CHECK(dynamic_cast<const NumericInitialData&>(*created) == initial_data);\n  }\n\n  using reader_component = MockVolumeDataReader<Metavariables>;\n  using element_array = MockElementArray<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      initial_data.get_clone()};\n\n  // We get test data from a TOV solution\n  RelativisticEuler::Solutions::TovStar tov_star{\n      1.e-3,\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100., 2.)};\n  const double star_radius = tov_star.radial_solution().outer_radius();\n  const auto& eos = tov_star.equation_of_state();\n\n  // Setup mock data file reader\n  ActionTesting::emplace_nodegroup_component<reader_component>(\n      make_not_null(&runner));\n\n  // Setup element\n  const ElementId<3> element_id{0, {{{1, 0}, {1, 0}, {1, 0}}}};\n  const Mesh<3> mesh{6, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const size_t num_points = mesh.number_of_grid_points();\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Wedge<3>{\n              star_radius / 2., star_radius * 2., 1., 1.,\n              OrientationMap<3>::create_aligned(), true});\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto coords = map(logical_coords);\n  using spatial_metric_tag = gr::Tags::SpatialMetric<DataVector, 3>;\n  const auto spatial_metric = get<spatial_metric_tag>(\n      tov_star.variables(coords, 0., tmpl::list<spatial_metric_tag>{}));\n  const auto inv_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), element_id,\n      {Variables<all_hydro_vars>{num_points}, eos.promote_to_3d_eos(),\n       inv_spatial_metric});\n\n  const auto get_element_tag = [&runner,\n                                &element_id](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // ReadNumericInitialData\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n      make_not_null(&runner), element_id));\n\n  // MockReadVolumeData\n  ActionTesting::invoke_queued_simple_action<reader_component>(\n      make_not_null(&runner), 0);\n\n  // Insert TOV data into the inbox\n  auto& inbox = ActionTesting::get_inbox_tag<\n      element_array, importers::Tags::VolumeData<NumericInitialData::all_vars>,\n      Metavariables>(make_not_null(&runner),\n                     element_id)[initial_data.volume_data_id()];\n  const auto& selected_vars = initial_data.selected_variables();\n  auto tov_vars = tov_star.variables(coords, 0., all_hydro_vars{});\n  get<hydro::Tags::RestMassDensity<DataVector>>(inbox) =\n      get<hydro::Tags::RestMassDensity<DataVector>>(tov_vars);\n  const auto& W = get<hydro::Tags::LorentzFactor<DataVector>>(tov_vars);\n  auto& u_i = get<hydro::Tags::LowerSpatialFourVelocity<DataVector, 3>>(inbox);\n  u_i = raise_or_lower_index(\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(tov_vars),\n      spatial_metric);\n  for (size_t d = 0; d < 3; ++d) {\n    u_i.get(d) *= get(W);\n  }\n  get<hydro::Tags::ElectronFraction<DataVector>>(inbox) =\n      get<hydro::Tags::ElectronFraction<DataVector>>(tov_vars);\n  get<hydro::Tags::MagneticField<DataVector, 3>>(inbox) =\n      get<hydro::Tags::MagneticField<DataVector, 3>>(tov_vars);\n\n  // Override variables if constant value is specified in options\n  const auto selected_electron_fraction =\n      get<NumericInitialData::VarName<hydro::Tags::ElectronFraction<DataVector>,\n                                      std::bool_constant<false>>>(\n          selected_vars);\n  if (std::holds_alternative<double>(selected_electron_fraction)) {\n    get<hydro::Tags::ElectronFraction<DataVector>>(tov_vars) =\n        make_with_value<Scalar<DataVector>>(\n            W, std::get<double>(selected_electron_fraction));\n  }\n  const auto selected_magnetic_field =\n      get<NumericInitialData::VarName<hydro::Tags::MagneticField<DataVector, 3>,\n                                      std::bool_constant<false>>>(\n          selected_vars);\n  if (std::holds_alternative<double>(selected_magnetic_field)) {\n    get<hydro::Tags::MagneticField<DataVector, 3>>(tov_vars) =\n        make_with_value<tnsr::I<DataVector, 3>>(\n            W, std::get<double>(selected_magnetic_field));\n  }\n\n  // SetNumericInitialData\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n\n  // Check result\n  tmpl::for_each<all_hydro_vars>(\n      [&get_element_tag, &tov_vars](const auto tag_v) {\n        using tag = tmpl::type_from<std::decay_t<decltype(tag_v)>>;\n        CAPTURE(pretty_type::name<tag>());\n        CHECK_ITERABLE_APPROX(get_element_tag(tag{}), (get<tag>(tov_vars)));\n      });\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ValenciaDivClean.NumericInitialData\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  EquationsOfState::register_derived_with_charm();\n  test_numeric_initial_data(\n      NumericInitialData{\"TestInitialData.h5\",\n                         \"VolumeData\",\n                         0.,\n                         {1.0e-9},\n                         false,\n                         {\"CustomRho\", \"CustomUi\", \"CustomYe\", \"CustomB\"},\n                         1.e-14},\n      \"NumericInitialData:\\n\"\n      \"  FileGlob: TestInitialData.h5\\n\"\n      \"  Subgroup: VolumeData\\n\"\n      \"  ObservationValue: 0.\\n\"\n      \"  ObservationValueEpsilon: 1e-9\\n\"\n      \"  ElementsAreIdentical: False\\n\"\n      \"  Variables:\\n\"\n      \"    RestMassDensity: CustomRho\\n\"\n      \"    LowerSpatialFourVelocity: CustomUi\\n\"\n      \"    ElectronFraction: CustomYe\\n\"\n      \"    MagneticField: CustomB\\n\"\n      \"  DensityCutoff: 1.e-14\");\n  test_numeric_initial_data(\n      NumericInitialData{\"TestInitialData.h5\",\n                         \"VolumeData\",\n                         0.,\n                         std::nullopt,\n                         false,\n                         {\"CustomRho\", \"CustomUi\", 0.15, 0.},\n                         1.e-14},\n      \"NumericInitialData:\\n\"\n      \"  FileGlob: TestInitialData.h5\\n\"\n      \"  Subgroup: VolumeData\\n\"\n      \"  ObservationValue: 0.\\n\"\n      \"  ObservationValueEpsilon: Auto\\n\"\n      \"  ElementsAreIdentical: False\\n\"\n      \"  Variables:\\n\"\n      \"    RestMassDensity: CustomRho\\n\"\n      \"    LowerSpatialFourVelocity: CustomUi\\n\"\n      \"    ElectronFraction: 0.15\\n\"\n      \"    MagneticField: 0.\\n\"\n      \"  DensityCutoff: 1.e-14\");\n}\n\n}  // namespace grmhd::ValenciaDivClean::Actions\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DemandOutgoingCharSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef characteristic_speeds(lapse, shift, outward_directed_normal_covector):\n    return [\n        lapse - np.dot(outward_directed_normal_covector, shift),\n        -lapse - np.dot(outward_directed_normal_covector, shift),\n    ]\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    shift,\n    lapse,\n):\n    speeds = characteristic_speeds(\n        lapse, shift, outward_directed_normal_covector\n    )\n    for i in range(2):\n        if face_mesh_velocity is not None:\n            speeds[i] -= np.dot(\n                outward_directed_normal_covector, face_mesh_velocity\n            )\n        if speeds[i] < 0.0:\n            return \"DemandOutgoingCharSpeeds boundary condition violated\"\n    return None\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DirichletAnalytic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Evolution.Systems.GrMhd.ValenciaDivClean.ConservativeFromPrimitive as cons\nimport Evolution.Systems.GrMhd.ValenciaDivClean.Fluxes as fluxes\nimport numpy as np\nimport PointwiseFunctions.AnalyticData.GrMhd.MagneticRotor as rotor\nimport PointwiseFunctions.AnalyticSolutions.Hydro.SmoothFlow as hydro\n\n\ndef soln_error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return None\n\n\n_soln_electron_fraction = 0.1\n_soln_pressure = 1.0\n_soln_adiabatic_index = 5.0 / 3.0\n_soln_perturbation_size = 0.2\n\n\ndef _soln_mean_velocity():\n    dim = 3\n    mean_v = []\n    for i in range(0, dim):\n        mean_v.append(0.9 - i * 0.5)\n    return np.asarray(mean_v)\n\n\ndef _soln_wave_vector():\n    dim = 3\n    wave_vector = []\n    for i in range(0, dim):\n        wave_vector.append(0.1 + i)\n    return np.asarray(wave_vector)\n\n\ndef soln_velocity(coords, time):\n    return hydro.spatial_velocity(\n        coords,\n        time,\n        _soln_mean_velocity(),\n        _soln_wave_vector(),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef soln_electron_fraction(coords, time):\n    return _soln_electron_fraction\n\n\ndef soln_lorentz_factor(coords, time):\n    return hydro.lorentz_factor(\n        coords,\n        time,\n        _soln_mean_velocity(),\n        _soln_wave_vector(),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef soln_specific_internal_energy(coords, time):\n    return hydro.specific_internal_energy(\n        coords,\n        time,\n        _soln_mean_velocity(),\n        _soln_wave_vector(),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef soln_specific_enthalpy(coords, time):\n    return hydro.specific_enthalpy_relativistic(\n        coords,\n        time,\n        _soln_mean_velocity(),\n        _soln_wave_vector(),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef soln_pressure(coords, time):\n    return _soln_pressure\n\n\ndef soln_mass_density(coords, time):\n    return hydro.rest_mass_density(\n        coords,\n        time,\n        _soln_mean_velocity(),\n        _soln_wave_vector(),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef soln_temperature(coords, time):\n    return (_soln_adiabatic_index - 1) * soln_specific_internal_energy(\n        coords, time\n    )\n\n\ndef soln_magnetic_field(coords, time):\n    return np.array([0.0, 0.0, 0.0])\n\n\ndef divergence_cleaning_field(coords, time):\n    return 0.0\n\n\ndef soln_sqrt_det_spatial_metric(coords, time):\n    return 1.0\n\n\ndef soln_spatial_metric(coords, time):\n    return np.identity(3)\n\n\ndef soln_inverse_spatial_metric(coords, time):\n    return np.identity(3)\n\n\ndef soln_spatial_velocity(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_velocity(coords, time)\n\n\ndef soln_rest_mass_density(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_mass_density(coords, time)\n\n\ndef soln_electron_fraction_long(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_electron_fraction(coords, time)\n\n\ndef soln_temperature_long(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_temperature(coords, time)\n\n\ndef soln_tilde_d(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_lorentz_factor(coords, time) * soln_mass_density(coords, time)\n\n\ndef soln_tilde_ye(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return (\n        soln_lorentz_factor(coords, time)\n        * soln_mass_density(coords, time)\n        * soln_electron_fraction(coords, time)\n    )\n\n\ndef soln_tilde_tau(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return cons.tilde_tau(\n        soln_mass_density(coords, time),\n        soln_electron_fraction(coords, time),\n        soln_specific_internal_energy(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        divergence_cleaning_field(coords, time),\n    )\n\n\ndef soln_tilde_s(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return cons.tilde_s(\n        soln_mass_density(coords, time),\n        soln_electron_fraction(coords, time),\n        soln_specific_internal_energy(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        divergence_cleaning_field(coords, time),\n    )\n\n\ndef soln_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return cons.tilde_b(\n        soln_mass_density(coords, time),\n        soln_electron_fraction(coords, time),\n        soln_specific_internal_energy(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        divergence_cleaning_field(coords, time),\n    )\n\n\ndef soln_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return cons.tilde_phi(\n        soln_mass_density(coords, time),\n        soln_electron_fraction(coords, time),\n        soln_specific_internal_energy(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        divergence_cleaning_field(coords, time),\n    )\n\n\ndef soln_lapse(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return 1.0\n\n\ndef soln_shift(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return np.asarray([0.0, 0.0, 0.0])\n\n\ndef soln_flux_tilde_d(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_d_flux(\n        soln_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n    )\n\n\ndef soln_flux_tilde_ye(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_ye_flux(\n        soln_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n    )\n\n\ndef soln_flux_tilde_tau(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_tau_flux(\n        soln_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n    )\n\n\ndef soln_flux_tilde_s(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_s_flux(\n        soln_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n    )\n\n\ndef soln_flux_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_b_flux(\n        soln_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n    )\n\n\ndef soln_flux_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_phi_flux(\n        soln_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        soln_pressure(coords, time),\n        soln_velocity(coords, time),\n        soln_lorentz_factor(coords, time),\n        soln_magnetic_field(coords, time),\n    )\n\n\n_data_rotor_radius = 0.1\n_data_rotor_density = 10.0\n_data_background_density = 1.0\n_data_pressure = 1.0\n_data_angular_velocity = 9.95\n_data_adiabatic_index = 5.0 / 3.0\n_data_magnetic_field = np.asarray([3.54490770181103205, 0.0, 0.0])\n\n_data_electron_fraction = 0.1\n\n\ndef data_velocity(coords):\n    return rotor.spatial_velocity(\n        coords,\n        _data_rotor_radius,\n        _data_rotor_density,\n        _data_background_density,\n        _data_pressure,\n        _data_angular_velocity,\n        _data_magnetic_field,\n        _data_adiabatic_index,\n    )\n\n\ndef data_lorentz_factor(coords):\n    return rotor.lorentz_factor(\n        coords,\n        _data_rotor_radius,\n        _data_rotor_density,\n        _data_background_density,\n        _data_pressure,\n        _data_angular_velocity,\n        _data_magnetic_field,\n        _data_adiabatic_index,\n    )\n\n\ndef data_specific_internal_energy(coords):\n    return rotor.specific_internal_energy(\n        coords,\n        _data_rotor_radius,\n        _data_rotor_density,\n        _data_background_density,\n        _data_pressure,\n        _data_angular_velocity,\n        _data_magnetic_field,\n        _data_adiabatic_index,\n    )\n\n\ndef data_specific_enthalpy(coords):\n    return rotor.specific_enthalpy(\n        coords,\n        _data_rotor_radius,\n        _data_rotor_density,\n        _data_background_density,\n        _data_pressure,\n        _data_angular_velocity,\n        _data_magnetic_field,\n        _data_adiabatic_index,\n    )\n\n\ndef data_pressure(coords):\n    return _data_pressure\n\n\ndef data_electron_fraction(coords):\n    return _data_electron_fraction\n\n\ndef data_mass_density(coords):\n    return rotor.rest_mass_density(\n        coords,\n        _data_rotor_radius,\n        _data_rotor_density,\n        _data_background_density,\n        _data_pressure,\n        _data_angular_velocity,\n        _data_magnetic_field,\n        _data_adiabatic_index,\n    )\n\n\ndef data_magnetic_field(coords):\n    return rotor.magnetic_field(\n        coords,\n        _data_rotor_radius,\n        _data_rotor_density,\n        _data_background_density,\n        _data_pressure,\n        _data_angular_velocity,\n        _data_magnetic_field,\n        _data_adiabatic_index,\n    )\n\n\ndef data_temperature(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return (_data_adiabatic_index - 1) * data_specific_internal_energy(coords)\n\n\ndef data_electron_fraction_long(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return _data_electron_fraction\n\n\ndef data_rest_mass_density(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return data_mass_density(coords)\n\n\ndef data_spatial_velocity(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return data_velocity(coords)\n\n\ndef data_tilde_d(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return data_lorentz_factor(coords) * data_mass_density(coords)\n\n\ndef data_tilde_ye(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return (\n        data_lorentz_factor(coords)\n        * data_mass_density(coords)\n        * data_electron_fraction(coords)\n    )\n\n\ndef data_tilde_tau(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return cons.tilde_tau(\n        data_mass_density(coords),\n        data_electron_fraction(coords),\n        data_specific_internal_energy(coords),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        divergence_cleaning_field(coords, time),\n    )\n\n\ndef data_tilde_s(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return cons.tilde_s(\n        data_mass_density(coords),\n        data_electron_fraction(coords),\n        data_specific_internal_energy(coords),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        divergence_cleaning_field(coords, time),\n    )\n\n\ndef data_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return cons.tilde_b(\n        data_mass_density(coords),\n        data_electron_fraction(coords),\n        data_specific_internal_energy(coords),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        divergence_cleaning_field(coords, time),\n    )\n\n\ndef data_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return cons.tilde_phi(\n        data_mass_density(coords),\n        data_electron_fraction(coords),\n        data_specific_internal_energy(coords),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        divergence_cleaning_field(coords, time),\n    )\n\n\ndef data_flux_tilde_d(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_d_flux(\n        data_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n    )\n\n\ndef data_flux_tilde_ye(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_ye_flux(\n        data_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n    )\n\n\ndef data_flux_tilde_tau(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_tau_flux(\n        data_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n    )\n\n\ndef data_flux_tilde_s(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_s_flux(\n        data_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n    )\n\n\ndef data_flux_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_b_flux(\n        data_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n    )\n\n\ndef data_flux_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return fluxes.tilde_phi_flux(\n        data_tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        data_tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_lapse(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_shift(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            coords,\n            time,\n            dim,\n        ),\n        soln_sqrt_det_spatial_metric(coords, time),\n        soln_spatial_metric(coords, time),\n        soln_inverse_spatial_metric(coords, time),\n        data_pressure(coords),\n        data_velocity(coords),\n        data_lorentz_factor(coords),\n        data_magnetic_field(coords),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Evolution.Systems.GrMhd.ValenciaDivClean.ConservativeFromPrimitive as cons\nimport Evolution.Systems.GrMhd.ValenciaDivClean.Fluxes as fluxes\nimport numpy as np\n\n\ndef _exterior_spatial_velocity(\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_spatial_velocity,\n):\n    dot_interior_spatial_velocity = np.dot(\n        outward_directed_normal_covector, interior_spatial_velocity\n    )\n    return np.where(\n        dot_interior_spatial_velocity > 0.0,\n        interior_spatial_velocity,\n        interior_spatial_velocity\n        - outward_directed_normal_vector * dot_interior_spatial_velocity,\n    )\n\n\ndef _spatial_metric(inv_spatial_metric):\n    return np.linalg.inv(inv_spatial_metric)\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return None\n\n\ndef tilde_d(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_d(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_ye(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_ye(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_tau(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_tau(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_s(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_s(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_b(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_phi(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef _return_cons_vars(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return {\n        \"tilde_d\": tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n        ),\n        \"tilde_ye\": tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n        ),\n        \"tilde_tau\": tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n        ),\n        \"tilde_s\": tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n        ),\n        \"tilde_b\": tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n        ),\n        \"tilde_phi\": tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n        ),\n    }\n\n\ndef flux_tilde_d(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n    )\n\n    return fluxes.tilde_d_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n    )\n\n\ndef flux_tilde_ye(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n    )\n\n    return fluxes.tilde_ye_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n    )\n\n\ndef flux_tilde_tau(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n    )\n\n    return fluxes.tilde_tau_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n    )\n\n\ndef flux_tilde_s(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n    )\n\n    return fluxes.tilde_s_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n    )\n\n\ndef flux_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n    )\n\n    return fluxes.tilde_b_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n    )\n\n\ndef flux_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n    )\n\n    return fluxes.tilde_phi_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        interior_magnetic_field,\n    )\n\n\ndef lapse(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return lapse\n\n\ndef shift(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return shift\n\n\ndef inv_spatial_metric(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return inv_spatial_metric\n\n\ndef spatial_velocity(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n\n\ndef spatial_velocity_one_form(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    return np.einsum(\"i, ij\", exterior_spatial_velocity, spatial_metric)\n\n\ndef rest_mass_density(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return interior_rest_mass_density\n\n\ndef electron_fraction(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return interior_electron_fraction\n\n\ndef temperature(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n):\n    return interior_temperature\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Reflective.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Evolution.Systems.GrMhd.ValenciaDivClean.ConservativeFromPrimitive as cons\nimport Evolution.Systems.GrMhd.ValenciaDivClean.Fluxes as fluxes\nimport numpy as np\n\n\ndef _exterior_spatial_velocity(\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_spatial_velocity,\n    reflect_both,\n):\n    dot_interior_spatial_velocity = np.dot(\n        outward_directed_normal_covector, interior_spatial_velocity\n    )\n\n    if reflect_both:\n        return np.where(\n            dot_interior_spatial_velocity > 0.0,\n            interior_spatial_velocity\n            - 2\n            * outward_directed_normal_vector\n            * dot_interior_spatial_velocity,\n            interior_spatial_velocity\n            + 2\n            * outward_directed_normal_vector\n            * dot_interior_spatial_velocity,\n        )\n\n    else:\n        return np.where(\n            dot_interior_spatial_velocity > 0.0,\n            interior_spatial_velocity\n            - 2\n            * outward_directed_normal_vector\n            * dot_interior_spatial_velocity,\n            interior_spatial_velocity,\n        )\n\n\ndef _exterior_magnetic_field(\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_magnetic_field,\n    reflect_both,\n):\n    dot_interior_magnetic_field = np.dot(\n        outward_directed_normal_covector, interior_magnetic_field\n    )\n    if reflect_both:\n        return np.where(\n            dot_interior_magnetic_field > 0.0,\n            interior_magnetic_field\n            - 2 * outward_directed_normal_vector * dot_interior_magnetic_field,\n            interior_magnetic_field\n            + 2 * outward_directed_normal_vector * dot_interior_magnetic_field,\n        )\n    else:\n        return np.where(\n            dot_interior_magnetic_field > 0.0,\n            interior_magnetic_field\n            - 2 * outward_directed_normal_vector * dot_interior_magnetic_field,\n            interior_magnetic_field,\n        )\n\n\ndef _spatial_metric(inv_spatial_metric):\n    return np.linalg.inv(inv_spatial_metric)\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return None\n\n\ndef tilde_d(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_d(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_ye(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_ye(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_tau(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_tau(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_s(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_s(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_b(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    return cons.tilde_phi(\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        0.0,\n    )\n\n\ndef _return_cons_vars(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return {\n        \"tilde_d\": tilde_d(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n            reflect_both,\n        ),\n        \"tilde_ye\": tilde_ye(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n            reflect_both,\n        ),\n        \"tilde_tau\": tilde_tau(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n            reflect_both,\n        ),\n        \"tilde_s\": tilde_s(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n            reflect_both,\n        ),\n        \"tilde_b\": tilde_b(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n            reflect_both,\n        ),\n        \"tilde_phi\": tilde_phi(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            outward_directed_normal_vector,\n            interior_rest_mass_density,\n            interior_electron_fraction,\n            interior_specific_internal_energy,\n            interior_spatial_velocity,\n            interior_magnetic_field,\n            interior_lorentz_factor,\n            interior_pressure,\n            interior_temperature,\n            shift,\n            lapse,\n            inv_spatial_metric,\n            reflect_both,\n        ),\n    }\n\n\ndef flux_tilde_d(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n        reflect_both,\n    )\n\n    return fluxes.tilde_d_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n    )\n\n\ndef flux_tilde_ye(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n        reflect_both,\n    )\n\n    return fluxes.tilde_ye_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n    )\n\n\ndef flux_tilde_tau(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n        reflect_both,\n    )\n\n    return fluxes.tilde_tau_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n    )\n\n\ndef flux_tilde_s(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n        reflect_both,\n    )\n\n    return fluxes.tilde_s_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n    )\n\n\ndef flux_tilde_b(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n        reflect_both,\n    )\n\n    return fluxes.tilde_b_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n    )\n\n\ndef flux_tilde_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    exterior_magnetic_field = _exterior_magnetic_field(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_magnetic_field,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    sqrt_det_spatial_metric = np.sqrt(np.linalg.det(spatial_metric))\n\n    cons_vars = _return_cons_vars(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_rest_mass_density,\n        interior_electron_fraction,\n        interior_specific_internal_energy,\n        interior_spatial_velocity,\n        interior_magnetic_field,\n        interior_lorentz_factor,\n        interior_pressure,\n        interior_temperature,\n        shift,\n        lapse,\n        inv_spatial_metric,\n        reflect_both,\n    )\n\n    return fluxes.tilde_phi_flux(\n        cons_vars[\"tilde_d\"],\n        cons_vars[\"tilde_ye\"],\n        cons_vars[\"tilde_tau\"],\n        cons_vars[\"tilde_s\"],\n        cons_vars[\"tilde_b\"],\n        cons_vars[\"tilde_phi\"],\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        interior_pressure,\n        exterior_spatial_velocity,\n        interior_lorentz_factor,\n        exterior_magnetic_field,\n    )\n\n\ndef lapse(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return lapse\n\n\ndef shift(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return shift\n\n\ndef inv_spatial_metric(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return inv_spatial_metric\n\n\ndef spatial_velocity(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n\n\ndef spatial_velocity_one_form(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    exterior_spatial_velocity = _exterior_spatial_velocity(\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        interior_spatial_velocity,\n        reflect_both,\n    )\n    spatial_metric = _spatial_metric(inv_spatial_metric)\n    return np.einsum(\"i, ij\", exterior_spatial_velocity, spatial_metric)\n\n\ndef rest_mass_density(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return interior_rest_mass_density\n\n\ndef electron_fraction(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return interior_electron_fraction\n\n\ndef temperature(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    interior_rest_mass_density,\n    interior_electron_fraction,\n    interior_specific_internal_energy,\n    interior_spatial_velocity,\n    interior_magnetic_field,\n    interior_lorentz_factor,\n    interior_pressure,\n    interior_temperature,\n    shift,\n    lapse,\n    inv_spatial_metric,\n    reflect_both,\n):\n    return interior_temperature\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.BoundaryConditions.DemandOutgoingCharSpeeds\",\n                  \"[Unit][GrMhd]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/\"};\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      grmhd::ValenciaDivClean::BoundaryConditions::DemandOutgoingCharSpeeds,\n      grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition,\n      grmhd::ValenciaDivClean::System,\n      tmpl::list<grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov>>(\n      make_not_null(&gen), \"DemandOutgoingCharSpeeds\",\n      tuples::TaggedTuple<helpers::Tags::PythonFunctionForErrorMessage<>>{\n          \"error\"},\n      \"DemandOutgoingCharSpeeds:\\n\", Index<2>{5}, db::DataBox<tmpl::list<>>{},\n      tuples::TaggedTuple<>{});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/AllSolutions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition,\n            tmpl::list<grmhd::ValenciaDivClean::BoundaryConditions::\n                           DirichletAnalytic>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   grmhd::ValenciaDivClean::InitialData::initial_data_list>,\n        tmpl::pair<\n            grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField,\n            tmpl::list<grmhd::AnalyticData::InitialMagneticFields::Poloidal>>>;\n  };\n};\n}  // namespace\n\nnamespace {\nstruct ConvertSmoothFlow {\n  using unpacked_container = int;\n  using packed_container = grmhd::Solutions::SmoothFlow;\n  using packed_type = double;\n\n  static packed_container create_container() {\n    constexpr size_t dim = 3;\n    std::array<double, dim> wave_vector{};\n    for (size_t i = 0; i < dim; ++i) {\n      gsl::at(wave_vector, i) = 0.1 + i;\n    }\n    std::array<double, dim> mean_velocity{};\n    for (size_t i = 0; i < dim; ++i) {\n      gsl::at(mean_velocity, i) = 0.9 - i * 0.5;\n    }\n    const double pressure = 1.0;\n    const double adiabatic_index = 5.0 / 3.0;\n    const double perturbation_size = 0.2;\n    return {mean_velocity, wave_vector, pressure, adiabatic_index,\n            perturbation_size};\n  }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return 3;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct ConvertMagneticRotor {\n  using unpacked_container = int;\n  using packed_container = grmhd::AnalyticData::MagneticRotor;\n  using packed_type = double;\n\n  static packed_container create_container() {\n    const double rotor_radius = 0.1;\n    const double rotor_density = 10.0;\n    const double background_density = 1.0;\n    const double pressure = 1.0;\n    const double angular_velocity = 9.95;\n    const std::array<double, 3> magnetic_field{{3.54490770181103205, 0.0, 0.0}};\n    const double adiabatic_index = 5.0 / 3.0;\n    return {rotor_radius,     rotor_density,  background_density, pressure,\n            angular_velocity, magnetic_field, adiabatic_index};\n  }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return 3;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nvoid test_soln() {\n  register_classes_with_charm(\n      grmhd::ValenciaDivClean::InitialData::initial_data_list{});\n  MAKE_GENERATOR(gen);\n  const auto box_analytic_soln = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticSolution<grmhd::Solutions::SmoothFlow>>>(\n      0.5, ConvertSmoothFlow::create_container());\n\n  helpers::test_boundary_condition_with_python<\n      grmhd::ValenciaDivClean::BoundaryConditions::DirichletAnalytic,\n      grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition,\n      grmhd::ValenciaDivClean::System,\n      tmpl::list<grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov>,\n      tmpl::list<ConvertSmoothFlow>,\n      tmpl::list<Tags::AnalyticSolution<grmhd::Solutions::SmoothFlow>>,\n      Metavariables>(\n      make_not_null(&gen),\n      \"Evolution.Systems.GrMhd.ValenciaDivClean.BoundaryConditions.\"\n      \"DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeD>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeYe>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeTau>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildePhi>,\n\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>,\n              tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>,\n              tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, 3>>,\n          helpers::Tags::PythonFunctionName<hydro::Tags::SpatialVelocityOneForm<\n              DataVector, 3, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::RestMassDensity<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::ElectronFraction<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::Temperature<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>>{\n          \"soln_error\", \"soln_tilde_d\", \"soln_tilde_ye\", \"soln_tilde_tau\",\n          \"soln_tilde_s\", \"soln_tilde_b\", \"soln_tilde_phi\", \"soln_flux_tilde_d\",\n          \"soln_flux_tilde_ye\", \"soln_flux_tilde_tau\", \"soln_flux_tilde_s\",\n          \"soln_flux_tilde_b\", \"soln_flux_tilde_phi\", \"soln_lapse\",\n          \"soln_shift\",\n          \"soln_spatial_velocity\",  // flat spacetime, so up and down indices\n                                    // are same\n          \"soln_rest_mass_density\", \"soln_electron_fraction_long\",\n          \"soln_temperature_long\", \"soln_spatial_velocity\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    SmoothFlow:\\n\"\n      \"      WaveVector: [0.1, 1.1, 2.1 ]\\n\"\n      \"      MeanVelocity: [0.9, 0.4, -0.1]\\n\"\n      \"      Pressure: 1.0\\n\"\n      \"      AdiabaticIndex: 1.6666666666666666\\n\"\n      \"      PerturbationSize: 0.2\",\n      Index<2>{5}, box_analytic_soln, tuples::TaggedTuple<>{});\n}\n\nvoid test_data() {\n  register_classes_with_charm(\n      grmhd::ValenciaDivClean::InitialData::initial_data_list{});\n  MAKE_GENERATOR(gen);\n  const auto box_analytic_data = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticData<grmhd::AnalyticData::MagneticRotor>>>(\n      0.5, ConvertMagneticRotor::create_container());\n\n  helpers::test_boundary_condition_with_python<\n      grmhd::ValenciaDivClean::BoundaryConditions::DirichletAnalytic,\n      grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition,\n      grmhd::ValenciaDivClean::System,\n      tmpl::list<grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov>,\n      tmpl::list<ConvertMagneticRotor>,\n      tmpl::list<Tags::AnalyticData<grmhd::AnalyticData::MagneticRotor>>,\n      Metavariables>(\n      make_not_null(&gen),\n      \"Evolution.Systems.GrMhd.ValenciaDivClean.BoundaryConditions.\"\n      \"DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeD>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeYe>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeTau>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildePhi>,\n\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>,\n              tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>,\n              tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, 3>>,\n          helpers::Tags::PythonFunctionName<hydro::Tags::SpatialVelocityOneForm<\n              DataVector, 3, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::RestMassDensity<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::ElectronFraction<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::Temperature<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>>{\n          \"soln_error\", \"data_tilde_d\", \"data_tilde_ye\", \"data_tilde_tau\",\n          \"data_tilde_s\", \"data_tilde_b\", \"data_tilde_phi\", \"data_flux_tilde_d\",\n          \"data_flux_tilde_ye\", \"data_flux_tilde_tau\", \"data_flux_tilde_s\",\n          \"data_flux_tilde_b\", \"data_flux_tilde_phi\", \"soln_lapse\",\n          \"soln_shift\",\n          \"data_spatial_velocity\",  // flat spacetime, so up and down indices\n                                    // are same\n          \"data_rest_mass_density\", \"data_electron_fraction_long\",\n          \"data_temperature\", \"data_spatial_velocity\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    MagneticRotor:\\n\"\n      \"      RotorRadius: 0.1\\n\"\n      \"      RotorDensity: 10.0\\n\"\n      \"      BackgroundDensity: 1.0\\n\"\n      \"      Pressure: 1.0\\n\"\n      \"      AngularVelocity: 9.95\\n\"\n      \"      MagneticField: [3.54490770181103205, 0.0, 0.0]\\n\"\n      \"      AdiabaticIndex: 1.666666666666666666\",\n      Index<2>{5}, box_analytic_data, tuples::TaggedTuple<>{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.GrMhd.ValenciaDivClean.BoundaryConditions.DirichletAnalytic\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test_soln();\n  test_data();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Test_HydroFreeOutflow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/HydroFreeOutflow.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = ::TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.BoundaryConditions.HydroFreeOutflow\",\n                  \"[Unit][GrMhd]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  MAKE_GENERATOR(gen);\n\n  const auto face_mesh_index = Index<2>{2};\n  DataVector used_for_size{face_mesh_index.product()};\n  std::uniform_real_distribution<> dist(0.5, 1.0);\n\n  const auto spatial_metric =\n      ::TestHelpers::gr::random_spatial_metric<3, DataVector, Frame::Inertial>(\n          make_not_null(&gen), used_for_size);\n  const auto sqrt_det_spatial_metric = determinant(spatial_metric);\n\n  const auto box_with_gridless_tags =\n      db::create<db::AddSimpleTags<gr::Tags::SpatialMetric<DataVector, 3>,\n                                   gr::Tags::SqrtDetSpatialMetric<DataVector>>>(\n          spatial_metric, sqrt_det_spatial_metric);\n\n  helpers::test_boundary_condition_with_python<\n      grmhd::ValenciaDivClean::BoundaryConditions::HydroFreeOutflow,\n      grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition,\n      grmhd::ValenciaDivClean::System,\n      tmpl::list<grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.GrMhd.ValenciaDivClean.BoundaryConditions.\"\n      \"HydroFreeOutflow\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeD>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeYe>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeTau>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildePhi>,\n\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>,\n              tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>,\n              tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, 3>>,\n          helpers::Tags::PythonFunctionName<hydro::Tags::SpatialVelocityOneForm<\n              DataVector, 3, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::RestMassDensity<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::ElectronFraction<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::Temperature<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<\n              gr::Tags::InverseSpatialMetric<DataVector, 3>>>{\n          \"error\",\n          \"tilde_d\",\n          \"tilde_ye\",\n          \"tilde_tau\",\n          \"tilde_s\",\n          \"tilde_b\",\n          \"tilde_phi\",\n          \"flux_tilde_d\",\n          \"flux_tilde_ye\",\n          \"flux_tilde_tau\",\n          \"flux_tilde_s\",\n          \"flux_tilde_b\",\n          \"flux_tilde_phi\",\n          \"lapse\",\n          \"shift\",\n          \"spatial_velocity_one_form\",\n          \"rest_mass_density\",\n          \"electron_fraction\",\n          \"temperature\",\n          \"spatial_velocity\",\n          \"inv_spatial_metric\"},\n      \"HydroFreeOutflow:\\n\", face_mesh_index, box_with_gridless_tags,\n      tuples::TaggedTuple<>{}, 1.0e-10);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Test_Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.BoundaryConditions.Periodic\",\n                  \"[Unit][Evolution]\") {\n  helpers::test_periodic_condition<\n      domain::BoundaryConditions::Periodic<\n          grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition>,\n      grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition>(\n      \"Periodic:\\n\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Test_Reflective.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Reflective.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = ::TestHelpers::evolution::dg;\nnamespace {\nvoid test_stuffs(const bool reflect_both) {\n  MAKE_GENERATOR(gen);\n\n  const auto face_mesh_index = Index<2>{2};\n  const DataVector used_for_size{face_mesh_index.product()};\n  std::uniform_real_distribution<> dist(0.5, 1.0);\n\n  const auto spatial_metric =\n      ::TestHelpers::gr::random_spatial_metric<3, DataVector, Frame::Inertial>(\n          make_not_null(&gen), used_for_size);\n  const auto sqrt_det_spatial_metric = determinant(spatial_metric);\n\n  struct ReflectBoth : db::SimpleTag {\n    using type = bool;\n  };\n\n  const auto box_with_gridless_tags =\n      db::create<db::AddSimpleTags<gr::Tags::SpatialMetric<DataVector, 3>,\n                                   gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                                   ReflectBoth>>(\n          spatial_metric, sqrt_det_spatial_metric, reflect_both);\n\n  // for factory string\n  const std::string reflect_both_str = (reflect_both) ? \"true\" : \"false\";\n\n  helpers::test_boundary_condition_with_python<\n      grmhd::ValenciaDivClean::BoundaryConditions::Reflective,\n      grmhd::ValenciaDivClean::BoundaryConditions::BoundaryCondition,\n      grmhd::ValenciaDivClean::System,\n      tmpl::list<grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov>,\n      tmpl::list<>, tmpl::list<ReflectBoth>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.GrMhd.ValenciaDivClean.BoundaryConditions.\"\n      \"Reflective\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeD>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeYe>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeTau>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              grmhd::ValenciaDivClean::Tags::TildePhi>,\n\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>,\n              tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>,\n              tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                           tmpl::size_t<3>, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<gr::Tags::Lapse<DataVector>>,\n          helpers::Tags::PythonFunctionName<gr::Tags::Shift<DataVector, 3>>,\n          helpers::Tags::PythonFunctionName<hydro::Tags::SpatialVelocityOneForm<\n              DataVector, 3, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::RestMassDensity<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::ElectronFraction<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::Temperature<DataVector>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<\n              gr::Tags::InverseSpatialMetric<DataVector, 3>>>{\n          \"error\",\n          \"tilde_d\",\n          \"tilde_ye\",\n          \"tilde_tau\",\n          \"tilde_s\",\n          \"tilde_b\",\n          \"tilde_phi\",\n          \"flux_tilde_d\",\n          \"flux_tilde_ye\",\n          \"flux_tilde_tau\",\n          \"flux_tilde_s\",\n          \"flux_tilde_b\",\n          \"flux_tilde_phi\",\n          \"lapse\",\n          \"shift\",\n          \"spatial_velocity_one_form\",\n          \"rest_mass_density\",\n          \"electron_fraction\",\n          \"temperature\",\n          \"spatial_velocity\",\n          \"inv_spatial_metric\"},\n      \"Reflective:\\n\"\n      \"  ReflectBoth: \" +\n          reflect_both_str + \"\\n\",\n      face_mesh_index, box_with_gridless_tags, tuples::TaggedTuple<>{},\n      1.0e-10);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.BoundaryConditions.Reflective\", \"[Unit][GrMhd]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  // Test for two cases of Reflective.reflect_both\n\n  test_stuffs(true);\n  test_stuffs(false);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Hll.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n_atmosphere_density_cutoff = 1.0e-8\n_magnetic_field_magnitude_for_hydro = 1.0e-30\n_polytropic_constant = 100.0\n_polytropic_gamma = 2.0\n\n\ndef dg_package_data(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    flux_tilde_d,\n    flux_tilde_ye,\n    flux_tilde_tau,\n    flux_tilde_s,\n    flux_tilde_b,\n    flux_tilde_phi,\n    lapse,\n    shift,\n    spatial_velocity_one_form,\n    rest_mass_density,\n    electron_fraction,\n    temperature,\n    spatial_velocity,\n    normal_covector,\n    normal_vector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n    equation_of_state,\n):\n    def compute_char(lapse_sign):\n        magnetic_field_magnitude = np.sqrt(np.einsum(\"i,i->\", tilde_b, tilde_b))\n        if (\n            magnetic_field_magnitude < _magnetic_field_magnitude_for_hydro\n            and rest_mass_density > _atmosphere_density_cutoff\n        ):\n            # Sound speeds\n            pressure = (\n                _polytropic_constant * rest_mass_density**_polytropic_gamma\n            )\n            sound_speed_squared = min(\n                max(\n                    (\n                        _polytropic_gamma\n                        * (_polytropic_gamma - 1.0)\n                        * pressure\n                        / (\n                            rest_mass_density * (_polytropic_gamma - 1.0)\n                            + _polytropic_gamma * pressure\n                        )\n                    ),\n                    0.0,\n                ),\n                1.0,\n            )\n            velocity_dot_normal = min(\n                max(np.einsum(\"i,i->\", spatial_velocity, normal_covector), 0.0),\n                1.0 - 1.0e-8,\n            )\n            velocity_squared = np.einsum(\n                \"i,i->\", spatial_velocity, spatial_velocity_one_form\n            )\n            one_over_lorentz_factor_squared = 1.0 - velocity_squared\n            d = np.sqrt(\n                max(\n                    0.0,\n                    sound_speed_squared\n                    * one_over_lorentz_factor_squared\n                    * (\n                        1.0\n                        - velocity_squared * sound_speed_squared\n                        - velocity_dot_normal**2 * (1.0 - sound_speed_squared)\n                    ),\n                )\n            )\n            Lambda = (\n                lapse\n                / (1.0 - velocity_squared * sound_speed_squared)\n                * (\n                    velocity_dot_normal * (1.0 - sound_speed_squared)\n                    + lapse_sign * d\n                )\n            )\n            return np.asarray(\n                (Lambda - np.dot(shift, normal_covector))\n                if normal_dot_mesh_velocity is None\n                else (\n                    Lambda\n                    - np.dot(shift, normal_covector)\n                    - normal_dot_mesh_velocity\n                )\n            )\n        else:\n            # Light speeds\n            return np.asarray(\n                (lapse_sign * lapse - np.dot(shift, normal_covector))\n                if normal_dot_mesh_velocity is None\n                else (\n                    lapse_sign * lapse\n                    - np.dot(shift, normal_covector)\n                    - normal_dot_mesh_velocity\n                )\n            )\n\n    return (\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        np.asarray(np.dot(flux_tilde_d, normal_covector)),\n        np.asarray(np.dot(flux_tilde_ye, normal_covector)),\n        np.asarray(np.dot(flux_tilde_tau, normal_covector)),\n        np.einsum(\"ij,i->j\", flux_tilde_s, normal_covector),\n        np.einsum(\"ij,i->j\", flux_tilde_b, normal_covector),\n        np.asarray(np.dot(flux_tilde_phi, normal_covector)),\n        compute_char(1.0),\n        compute_char(-1.0),\n    )\n\n\ndef dg_boundary_terms(\n    interior_tilde_d,\n    interior_tilde_ye,\n    interior_tilde_tau,\n    interior_tilde_s,\n    interior_tilde_b,\n    interior_tilde_phi,\n    interior_normal_dot_flux_tilde_d,\n    interior_normal_dot_flux_tilde_ye,\n    interior_normal_dot_flux_tilde_tau,\n    interior_normal_dot_flux_tilde_s,\n    interior_normal_dot_flux_tilde_b,\n    interior_normal_dot_flux_tilde_phi,\n    interior_largest_outgoing_char_speed,\n    interior_largest_ingoing_char_speed,\n    exterior_tilde_d,\n    exterior_tilde_ye,\n    exterior_tilde_tau,\n    exterior_tilde_s,\n    exterior_tilde_b,\n    exterior_tilde_phi,\n    exterior_normal_dot_flux_tilde_d,\n    exterior_normal_dot_flux_tilde_ye,\n    exterior_normal_dot_flux_tilde_tau,\n    exterior_normal_dot_flux_tilde_s,\n    exterior_normal_dot_flux_tilde_b,\n    exterior_normal_dot_flux_tilde_phi,\n    exterior_largest_outgoing_char_speed,\n    exterior_largest_ingoing_char_speed,\n    use_strong_form,\n):\n    lambda_max = np.maximum(\n        0.0,\n        np.maximum(\n            interior_largest_outgoing_char_speed,\n            -exterior_largest_ingoing_char_speed,\n        ),\n    )\n    lambda_min = np.minimum(\n        0.0,\n        np.minimum(\n            interior_largest_ingoing_char_speed,\n            -exterior_largest_outgoing_char_speed,\n        ),\n    )\n\n    lambda_interior = lambda_min if use_strong_form else lambda_max\n\n    # Use scope and locals() to get arguments into the eval context below\n    scope = locals()\n\n    def impl(var_name):\n        return np.asarray(\n            (\n                lambda_interior\n                * eval(\"interior_normal_dot_flux_\" + var_name, scope)\n                + lambda_min\n                * eval(\"exterior_normal_dot_flux_\" + var_name, scope)\n                + lambda_max\n                * lambda_min\n                * (\n                    eval(\"exterior_\" + var_name, scope)\n                    - eval(\"interior_\" + var_name, scope)\n                )\n            )\n            / (lambda_max - lambda_min)\n        )\n\n    return (\n        impl(\"tilde_d\"),\n        impl(\"tilde_ye\"),\n        impl(\"tilde_tau\"),\n        impl(\"tilde_s\"),\n        impl(\"tilde_b\"),\n        impl(\"tilde_phi\"),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    flux_tilde_d,\n    flux_tilde_ye,\n    flux_tilde_tau,\n    flux_tilde_s,\n    flux_tilde_b,\n    flux_tilde_phi,\n    lapse,\n    shift,\n    spatial_velocity_one_form,\n    rest_mass_density,\n    electron_fraction,\n    temperature,\n    spatial_velocity,\n    normal_covector,\n    normal_vector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n    equation_of_state,\n):\n    def compute_char(lapse_sign):\n        return np.abs(\n            np.asarray(\n                (lapse_sign * lapse - np.dot(shift, normal_covector))\n                if normal_dot_mesh_velocity is None\n                else (\n                    lapse_sign * lapse\n                    - np.dot(shift, normal_covector)\n                    - normal_dot_mesh_velocity\n                )\n            )\n        )\n\n    return (\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        np.asarray(np.dot(flux_tilde_d, normal_covector)),\n        np.asarray(np.dot(flux_tilde_ye, normal_covector)),\n        np.asarray(np.dot(flux_tilde_tau, normal_covector)),\n        np.einsum(\"ij,i->j\", flux_tilde_s, normal_covector),\n        np.einsum(\"ij,i->j\", flux_tilde_b, normal_covector),\n        np.asarray(np.dot(flux_tilde_phi, normal_covector)),\n        np.asarray(np.maximum(compute_char(1.0), compute_char(-1.0))),\n    )\n\n\ndef dg_boundary_terms(\n    interior_tilde_d,\n    interior_tilde_ye,\n    interior_tilde_tau,\n    interior_tilde_s,\n    interior_tilde_b,\n    interior_tilde_phi,\n    interior_normal_dot_flux_tilde_d,\n    interior_normal_dot_flux_tilde_ye,\n    interior_normal_dot_flux_tilde_tau,\n    interior_normal_dot_flux_tilde_s,\n    interior_normal_dot_flux_tilde_b,\n    interior_normal_dot_flux_tilde_phi,\n    interior_abs_char_speed,\n    exterior_tilde_d,\n    exterior_tilde_ye,\n    exterior_tilde_tau,\n    exterior_tilde_s,\n    exterior_tilde_b,\n    exterior_tilde_phi,\n    exterior_normal_dot_flux_tilde_d,\n    exterior_normal_dot_flux_tilde_ye,\n    exterior_normal_dot_flux_tilde_tau,\n    exterior_normal_dot_flux_tilde_s,\n    exterior_normal_dot_flux_tilde_b,\n    exterior_normal_dot_flux_tilde_phi,\n    exterior_abs_char_speed,\n    use_strong_form,\n):\n    sign_for_form = 1.0 if use_strong_form else -1.0\n\n    # Use scope and locals() to get arguments into the eval context below\n    scope = locals()\n\n    def impl(var_name):\n        return np.asarray(\n            (\n                -0.5\n                * (\n                    sign_for_form\n                    * eval(\"interior_normal_dot_flux_\" + var_name, scope)\n                    + eval(\"exterior_normal_dot_flux_\" + var_name, scope)\n                )\n                - 0.5\n                * np.maximum(interior_abs_char_speed, exterior_abs_char_speed)\n                * (\n                    eval(\"exterior_\" + var_name, scope)\n                    - eval(\"interior_\" + var_name, scope)\n                )\n            )\n        )\n\n    return (\n        impl(\"tilde_d\"),\n        impl(\"tilde_ye\"),\n        impl(\"tilde_tau\"),\n        impl(\"tilde_s\"),\n        impl(\"tilde_b\"),\n        impl(\"tilde_phi\"),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Test_Hll.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace {\nnamespace helpers = TestHelpers::evolution::dg;\n\nstruct ConvertPolytropic {\n  using unpacked_container = bool;\n  using packed_container = EquationsOfState::EquationOfState<true, 3>;\n  using packed_type = bool;\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return true;\n  }\n\n  [[noreturn]] static inline void pack(\n      const gsl::not_null<packed_container*> /*packed*/,\n      const unpacked_container& /*unpacked*/,\n      const size_t /*grid_point_index*/) {\n    ERROR(\"Should not be converting an EOS from an unpacked to a packed type\");\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.BoundaryCorrections.Hll\",\n                  \"[Unit][GrMhd]\") {\n  PUPable_reg(grmhd::ValenciaDivClean::BoundaryCorrections::Hll);\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  using system = grmhd::ValenciaDivClean::System;\n\n  const tuples::TaggedTuple<\n      helpers::Tags::Range<gr::Tags::Lapse<DataVector>>,\n      helpers::Tags::Range<gr::Tags::Shift<DataVector, 3>>>\n      ranges{std::array{0.3, 1.0}, std::array{0.01, 0.02}};\n  const tuples::TaggedTuple<hydro::Tags::GrmhdEquationOfState> volume_data{\n      EquationsOfState::PolytropicFluid<true>{100.0, 2.0}.promote_to_3d_eos()};\n\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<system>(\n      make_not_null(&gen),\n      grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8},\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      system, tmpl::list<ConvertPolytropic>>(\n      make_not_null(&gen), \"Hll\", \"dg_package_data\", \"dg_boundary_terms\",\n      grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8},\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  // Test hydro\n  const tuples::TaggedTuple<\n      helpers::Tags::Range<gr::Tags::Lapse<DataVector>>,\n      helpers::Tags::Range<gr::Tags::Shift<DataVector, 3>>,\n      helpers::Tags::Range<grmhd::ValenciaDivClean::Tags::TildeB<>>>\n      ranges_hydro{std::array{0.3, 1.0}, std::array{0.01, 0.02},\n                   std::array{1.0e-25, 1.0e-20}};\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      system, tmpl::list<ConvertPolytropic>>(\n      make_not_null(&gen), \"Hll\", \"dg_package_data\", \"dg_boundary_terms\",\n      grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8},\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_data, ranges_hydro);\n\n  // Test light speed density cutoff\n  const tuples::TaggedTuple<\n      helpers::Tags::Range<hydro::Tags::RestMassDensity<DataVector>>,\n      helpers::Tags::Range<gr::Tags::Lapse<DataVector>>,\n      helpers::Tags::Range<gr::Tags::Shift<DataVector, 3>>>\n      ranges_atmo{std::array{1.0e-10, 1.0e-9}, std::array{0.3, 1.0},\n                  std::array{0.01, 0.02}};\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      system, tmpl::list<ConvertPolytropic>>(\n      make_not_null(&gen), \"Hll\", \"dg_package_data\", \"dg_boundary_terms\",\n      grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8},\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_data, ranges_atmo);\n\n  const auto hll = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      grmhd::ValenciaDivClean::BoundaryCorrections::Hll>(\n      \"Hll:\\n\"\n      \"  MagneticFieldMagnitudeForHydro: 1.0e-30\\n\"\n      \"  LightSpeedDensityCutoff: 1.0e-8\\n\");\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      system, tmpl::list<ConvertPolytropic>>(\n      make_not_null(&gen), \"Hll\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const grmhd::ValenciaDivClean::BoundaryCorrections::Hll&>(\n          *hll),\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  CHECK_FALSE(\n      grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8} !=\n      grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8});\n  CHECK(grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8} !=\n        grmhd::ValenciaDivClean::BoundaryCorrections::Hll{2.0e-30, 1.0e-8});\n  CHECK(grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 1.0e-8} !=\n        grmhd::ValenciaDivClean::BoundaryCorrections::Hll{1.0e-30, 2.0e-8});\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Test_Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// NOLINTNEXTLINE(misc-unused-alias-decls)\nnamespace helpers = TestHelpers::evolution::dg;\n\nstruct ConvertPolytropic {\n  using unpacked_container = bool;\n  using packed_container = EquationsOfState::EquationOfState<true, 3>;\n  using packed_type = bool;\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return true;\n  }\n\n  [[noreturn]] static inline void pack(\n      const gsl::not_null<packed_container*> /*packed*/,\n      const unpacked_container& /*unpacked*/,\n      const size_t /*grid_point_index*/) {\n    ERROR(\"Should not be converting an EOS from an unpacked to a packed type\");\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.BoundaryCorrections.Rusanov\",\n                  \"[Unit][GrMhd]\") {\n  PUPable_reg(grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov);\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  using system = grmhd::ValenciaDivClean::System;\n\n  const tuples::TaggedTuple<hydro::Tags::GrmhdEquationOfState> volume_data{\n      EquationsOfState::PolytropicFluid<true>{100.0, 2.0}.promote_to_3d_eos()};\n\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<system>(\n      make_not_null(&gen),\n      grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov{},\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_data, {});\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      system, tmpl::list<ConvertPolytropic>>(\n      make_not_null(&gen), \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov{},\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_data, {});\n\n  const auto rusanov = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov>(\"Rusanov:\");\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      system, tmpl::list<ConvertPolytropic>>(\n      make_not_null(&gen), \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<\n          const grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov&>(\n          *rusanov),\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      volume_data, {});\n\n  CHECK_FALSE(grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov{} !=\n              grmhd::ValenciaDivClean::BoundaryCorrections::Rusanov{});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ValenciaDivClean\")\n\nset(LIBRARY_SOURCES\n  Actions/Test_NumericInitialData.cpp\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp\n  BoundaryConditions/Test_HydroFreeOutflow.cpp\n  BoundaryConditions/Test_Periodic.cpp\n  BoundaryConditions/Test_Reflective.cpp\n  BoundaryCorrections/Test_Hll.cpp\n  BoundaryCorrections/Test_Rusanov.cpp\n  FiniteDifference/Test_BoundaryConditionGhostData.cpp\n  FiniteDifference/Test_MonotonicityPreserving5.cpp\n  FiniteDifference/Test_MonotonisedCentral.cpp\n  FiniteDifference/Test_PositivityPreservingAdaptiveOrder.cpp\n  FiniteDifference/Test_Tag.cpp\n  FiniteDifference/Test_Wcns5z.cpp\n  Subcell/Test_ComputeFluxes.cpp\n  Subcell/Test_FixConservativesAndComputePrims.cpp\n  Subcell/Test_NeighborPackagedData.cpp\n  Subcell/Test_PrimitiveGhostData.cpp\n  Subcell/Test_PrimsAfterRollback.cpp\n  Subcell/Test_ResizeAndComputePrimitives.cpp\n  Subcell/Test_SetInitialRdmpData.cpp\n  Subcell/Test_SwapGrTags.cpp\n  Subcell/Test_TciOnDgGrid.cpp\n  Subcell/Test_TciOnFdGrid.cpp\n  Subcell/Test_TciOptions.cpp\n  Subcell/Test_TimeDerivative.cpp\n  Test_Characteristics.cpp\n  Test_ComovingMagneticFieldMagnitude.cpp\n  Test_ComputeFluxesFromPrimitives.cpp\n  Test_ConservativeFromPrimitive.cpp\n  Test_FixConservatives.cpp\n  Test_Flattener.cpp\n  Test_Fluxes.cpp\n  Test_PrimitiveFromConservative.cpp\n  Test_QuadrupoleFormula.cpp\n  Test_SetVariablesNeededFixingToFalse.cpp\n  Test_Sources.cpp\n  Test_Tags.cpp\n  Test_TimeDerivativeTerms.cpp\n  Test_ValenciaDivClean.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainBoundaryConditionsHelpers\n  DomainHelpers\n  DomainStructure\n  Framework\n  GeneralRelativityHelpers\n  GrMhdAnalyticData\n  GrMhdSolutions\n  Hydro\n  HydroHelpers\n  Importers\n  Utilities\n  ValenciaDivClean\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/CharacteristicSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef CharacteristicSpeeds(\n    lapse,\n    shift,\n    spatial_velocity,\n    spatial_velocity_sqrd,\n    sound_speed_sqrd,\n    alfven_speed_sqrd,\n    normal_oneform,\n):\n    normal_velocity = np.dot(spatial_velocity, normal_oneform)\n    normal_shift = np.dot(shift, normal_oneform)\n    sound_speed_sqrd += alfven_speed_sqrd * (1.0 - sound_speed_sqrd)\n    prefactor = lapse / (1.0 - spatial_velocity_sqrd * sound_speed_sqrd)\n    first_term = prefactor * normal_velocity * (1.0 - sound_speed_sqrd)\n    second_term = (\n        prefactor\n        * np.sqrt(sound_speed_sqrd)\n        * np.sqrt(\n            (1.0 - spatial_velocity_sqrd)\n            * (\n                1.0\n                - spatial_velocity_sqrd * sound_speed_sqrd\n                - normal_velocity * normal_velocity * (1.0 - sound_speed_sqrd)\n            )\n        )\n    )\n    result = [-lapse - normal_shift]\n    result.append(first_term - second_term - normal_shift)\n    for i in range(0, spatial_velocity.size + 2):\n        result.append(lapse * normal_velocity - normal_shift)\n    result.append(first_term + second_term - normal_shift)\n    result.append(lapse - normal_shift)\n    return result\n\n\ndef characteristic_speeds_hydro(\n    spatial_velocity,\n    spatial_velocity_squared,\n    sound_speed_squared,\n    lorentz_factor,\n    normal_oneform,\n):\n    normal_velocity = np.dot(spatial_velocity, normal_oneform)\n    denom = 1.0 - spatial_velocity_squared * sound_speed_squared\n    under = (\n        1.0\n        - spatial_velocity_squared * sound_speed_squared\n        - normal_velocity * normal_velocity * (1.0 - sound_speed_squared)\n    )\n\n    d = lorentz_factor * np.sqrt(under)\n    factor = (1.0 - sound_speed_squared) * normal_velocity\n    cs = np.sqrt(sound_speed_squared)\n\n    y_plus = (factor + cs * d / (lorentz_factor * lorentz_factor)) / denom\n    y_minus = (factor - cs * d / (lorentz_factor * lorentz_factor)) / denom\n\n    return [\n        normal_velocity,\n        float(y_plus),\n        float(y_minus),\n    ]\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/ComovingMagneticFieldMagnitude.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom ConservativeFromPrimitive import b_dot_v, b_squared\n\n\ndef comoving_b_magnitude(\n    magnetic_field, spatial_velocity, lorentz_factor, spatial_metric\n):\n    return np.sqrt(\n        b_squared(magnetic_field, spatial_metric) / lorentz_factor**2\n        + b_dot_v(magnetic_field, spatial_velocity, spatial_metric) ** 2\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef b_dot_v(magnetic_field, spatial_velocity, spatial_metric):\n    return np.einsum(\n        \"ab, ab\", spatial_metric, np.outer(magnetic_field, spatial_velocity)\n    )\n\n\ndef b_squared(magnetic_field, spatial_metric):\n    return np.einsum(\n        \"ab, ab\", spatial_metric, np.outer(magnetic_field, magnetic_field)\n    )\n\n\ndef magnetic_field_one_form(magnetic_field, spatial_metric):\n    return np.einsum(\"a, ia\", magnetic_field, spatial_metric)\n\n\ndef p_star(pressure, b_dot_v, b_squared, lorentz_factor):\n    return pressure + 0.5 * (b_dot_v**2 + b_squared / lorentz_factor**2)\n\n\ndef spatial_velocity_one_form(spatial_velocity, spatial_metric):\n    return np.einsum(\"a, ia\", spatial_velocity, spatial_metric)\n\n\ndef vsq(spatial_velocity, spatial_metric):\n    return np.einsum(\n        \"ab, ab\", spatial_metric, np.outer(spatial_velocity, spatial_velocity)\n    )\n\n\ndef tilde_d(\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    divergence_cleaning_field,\n):\n    return lorentz_factor * rest_mass_density * sqrt_det_spatial_metric\n\n\ndef tilde_ye(\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    divergence_cleaning_field,\n):\n    return (\n        lorentz_factor\n        * rest_mass_density\n        * sqrt_det_spatial_metric\n        * electron_fraction\n    )\n\n\ndef tilde_tau(\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    divergence_cleaning_field,\n):\n    spatial_velocity_squared = vsq(spatial_velocity, spatial_metric)\n    return (\n        (\n            (\n                pressure * spatial_velocity_squared\n                + (\n                    lorentz_factor\n                    / (1.0 + lorentz_factor)\n                    * spatial_velocity_squared\n                    + specific_internal_energy\n                )\n                * rest_mass_density\n            )\n            * lorentz_factor**2\n        )\n        + 0.5\n        * b_squared(magnetic_field, spatial_metric)\n        * (1.0 + spatial_velocity_squared)\n        - 0.5\n        * np.square(b_dot_v(magnetic_field, spatial_velocity, spatial_metric))\n    ) * sqrt_det_spatial_metric\n\n\ndef tilde_s(\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    divergence_cleaning_field,\n):\n    return (\n        spatial_velocity_one_form(spatial_velocity, spatial_metric)\n        * (\n            lorentz_factor**2\n            * (pressure + rest_mass_density * (1.0 + specific_internal_energy))\n            + b_squared(magnetic_field, spatial_metric)\n        )\n        - magnetic_field_one_form(magnetic_field, spatial_metric)\n        * b_dot_v(magnetic_field, spatial_velocity, spatial_metric)\n    ) * sqrt_det_spatial_metric\n\n\ndef tilde_b(\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    divergence_cleaning_field,\n):\n    return sqrt_det_spatial_metric * magnetic_field\n\n\ndef tilde_phi(\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    divergence_cleaning_field,\n):\n    return sqrt_det_spatial_metric * divergence_cleaning_field\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Test_BoundaryConditionGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/ExternalBoundaryConditions.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DgSubcell/GhostZoneLogicalCoordinates.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComputeFluxesFromPrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/BoundaryConditionGhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace {\n\n// Metavariables to parse the list of derived classes of boundary conditions\nstruct EvolutionMetaVars {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<BoundaryConditions::BoundaryCondition,\n                             BoundaryConditions::standard_boundary_conditions>>;\n  };\n};\n\nusing SolutionForTest = Solutions::SmoothFlow;\n\ntemplate <typename T>\nusing Flux = ::Tags::Flux<T, tmpl::size_t<3>, Frame::Inertial>;\n\nconst std::array<double, 3> mean_velocity = {{0.23, 0.01, 0.31}};\nconst std::array<double, 3> wave_vector = {{0.11, 0.23, 0.32}};\nconst double pressure = 1.0;\nconst double adiabatic_index = 1.4;\nconst double perturbation_size = 0.3;\n\ntemplate <typename BoundaryConditionType>\nvoid test(const BoundaryConditionType& boundary_condition,\n          const bool set_fluxes, SolutionForTest solution) {\n  CAPTURE(set_fluxes);\n  const size_t num_dg_pts = 3;\n\n  // Create a 3D element [-1, 1]^3 and use it for test\n  const std::array<double, 3> lower_bounds{-1.0, -1.0, -1.0};\n  const std::array<double, 3> upper_bounds{1.0, 1.0, 1.0};\n  const std::array<size_t, 3> refinement_levels{0, 0, 0};\n  const std::array<size_t, 3> number_of_grid_points{num_dg_pts, num_dg_pts,\n                                                    num_dg_pts};\n  const domain::creators::Brick brick(\n      lower_bounds, upper_bounds, refinement_levels, number_of_grid_points,\n      {{{{boundary_condition.get_clone(), boundary_condition.get_clone()}},\n        {{boundary_condition.get_clone(), boundary_condition.get_clone()}},\n        {{boundary_condition.get_clone(), boundary_condition.get_clone()}}}});\n  auto domain = brick.create_domain();\n  auto boundary_conditions = brick.external_boundary_conditions();\n  const auto element = domain::create_initial_element(\n      ElementId<3>{0, {SegmentId{0, 0}, SegmentId{0, 0}, SegmentId{0, 0}}},\n      domain.blocks(), std::vector<std::array<size_t, 3>>{{refinement_levels}});\n\n  // Mesh and coordinates\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  // use MC reconstruction for test\n  using ReconstructorForTest = fd::MonotonisedCentralPrim;\n  const size_t ghost_zone_size{ReconstructorForTest{}.ghost_zone_size()};\n\n  // dummy neighbor data to put into DataBox\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<3>::type\n      neighbor_data{};\n\n  // Below are tags required by DirichletAnalytic boundary condition to compute\n  // inertial coords of ghost FD cells:\n  //  - time\n  //  - functions of time\n  //  - element map\n  //  - coordinate map\n  //  - subcell logical coordinates\n  //  - analytic solution\n\n  const double time{0.5};\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  const ElementMap<3, Frame::Grid> logical_to_grid_map(\n      ElementId<3>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<3>{}));\n\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const auto subcell_logical_coords = logical_coordinates(subcell_mesh);\n\n  // Below are tags used for testing DemandOutgoingCharSpeeds boundary condition\n  //  - volume metric and primitive variables on subcell mesh\n  //  - mesh velocity\n  //  - normal vectors\n\n  const auto subcell_inertial_coords = (*grid_to_inertial_map)(\n      logical_to_grid_map(subcell_logical_coords), time, functions_of_time);\n\n  using RestMassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using LorentzFactor = hydro::Tags::LorentzFactor<DataVector>;\n  using SpatialVelocity = hydro::Tags::SpatialVelocity<DataVector, 3>;\n  using MagneticField = hydro::Tags::MagneticField<DataVector, 3>;\n  using DivergenceCleaningField =\n      hydro::Tags::DivergenceCleaningField<DataVector>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n\n  // Use the Minkowski spacetime for spacetime vars, but we manually tweak\n  // values of shift vector so that the DemandOutgoingCharSpeeds condition can\n  // be satisfied at the first place. Later we will change shift vector to its\n  // original value (0.0) and check that the DemandOutgoingCharSpeeds condition\n  // is violated as expected.\n  Variables<tmpl::append<typename System::spacetime_variables_tag::tags_list,\n                         typename System::primitive_variables_tag::tags_list>>\n      volume_vars{subcell_mesh.number_of_grid_points()};\n  volume_vars.assign_subset(solution.variables(\n      subcell_inertial_coords, time,\n      typename System::spacetime_variables_tag::tags_list{}));\n  for (size_t i = 0; i < 3; ++i) {\n    get<gr::Tags::Shift<DataVector, 3>>(volume_vars).get(i) = -2.0;\n  }\n\n  get(get<RestMassDensity>(volume_vars)) = 1.0;\n  get(get<ElectronFraction>(volume_vars)) = 0.1;\n  get(get<Pressure>(volume_vars)) = 1.0;\n  get(get<LorentzFactor>(volume_vars)) = 2.0;\n  // This quantity is fixed by the equation of state given P and rho\n  get(get<SpecificInternalEnergy>(volume_vars)) = 1.0 / (1.4 - 1.0);\n  get<Temperature>(volume_vars) =\n      solution.equation_of_state().temperature_from_density_and_energy(\n          get<RestMassDensity>(volume_vars),\n          get<SpecificInternalEnergy>(volume_vars));\n  for (size_t i = 0; i < 3; ++i) {\n    get<SpatialVelocity>(volume_vars).get(i) = 0.1;\n    get<MagneticField>(volume_vars).get(i) = 0.5;\n  }\n  get(get<DivergenceCleaningField>(volume_vars)) = 1e-2;\n\n  std::optional<tnsr::I<DataVector, 3>> volume_mesh_velocity{};\n\n  typename evolution::dg::Tags::NormalCovectorAndMagnitude<3>::type\n      normal_vectors{};\n  for (const auto& direction : Direction<3>::all_directions()) {\n    const auto coordinate_map =\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<3>{});\n    const auto moving_mesh_map =\n        domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<3>{});\n\n    using inverse_spatial_metric_tag =\n        typename System::inverse_spatial_metric_tag;\n    const Mesh<2> face_mesh = subcell_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<3>, tnsr::i<DataVector, 3, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, 3, Frame::Inertial> unnormalized_covector{};\n    for (size_t i = 0; i < 3; ++i) {\n      unnormalized_covector.get(i) =\n          coordinate_map.inv_jacobian(face_logical_coords)\n              .get(direction.dimension(), i);\n    }\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        inverse_spatial_metric_tag,\n        evolution::dg::Actions::detail::NormalVector<3>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n    fields_on_face.assign_subset(\n        solution.variables(coordinate_map(face_logical_coords), time,\n                           tmpl::list<inverse_spatial_metric_tag>{}));\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<System>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, moving_mesh_map);\n  }\n\n  // Compute the cell-centered fluxes.\n  using cons_tags = typename System::variables_tag::tags_list;\n  typename System::variables_tag::type volume_cons_vars{\n      subcell_mesh.number_of_grid_points()};\n  typename evolution::dg::subcell::Tags::CellCenteredFlux<\n      typename System::flux_variables, 3>::type cell_centered_fluxes{};\n  if (set_fluxes) {\n    cell_centered_fluxes = typename decltype(cell_centered_fluxes)::value_type{\n        subcell_mesh.number_of_grid_points()};\n    compute_fluxes_from_primitives(make_not_null(&cell_centered_fluxes.value()),\n                                   volume_vars);\n  }\n\n  using Vlo =\n      typename VariableFixing::FixToAtmosphere<3>::VelocityLimitingOptions;\n  const VariableFixing::FixToAtmosphere<3> variable_fixer{\n      1.e-12, 3.e-12, Vlo{0.0, 1.e-4, 3.e-12, 1.e-11}, std::nullopt};\n\n  // create a box for test\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<EvolutionMetaVars>,\n      domain::Tags::Domain<3>, domain::Tags::ExternalBoundaryConditions<3>,\n      evolution::dg::subcell::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Coordinates<3, Frame::ElementLogical>,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<3>,\n      fd::Tags::Reconstructor, domain::Tags::MeshVelocity<3>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<3>, ::Tags::Time,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::ElementMap<3, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                  Frame::Inertial>,\n      ::Tags::Variables<\n          tmpl::append<typename System::spacetime_variables_tag::tags_list,\n                       typename System::primitive_variables_tag::tags_list>>,\n      evolution::dg::subcell::Tags::CellCenteredFlux<\n          typename System::flux_variables, 3>,\n      ::Tags::VariableFixer<::VariableFixing::FixToAtmosphere<3>>,\n      hydro::Tags::GrmhdEquationOfState>>(\n      EvolutionMetaVars{}, std::move(domain), std::move(boundary_conditions),\n      subcell_mesh, subcell_logical_coords, neighbor_data,\n      std::unique_ptr<fd::Reconstructor>{\n          std::make_unique<ReconstructorForTest>()},\n      volume_mesh_velocity, normal_vectors, time,\n      clone_unique_ptrs(functions_of_time),\n      ElementMap<3, Frame::Grid>{\n          ElementId<3>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<3>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{}),\n      volume_vars, cell_centered_fluxes, variable_fixer,\n      solution.equation_of_state().promote_to_3d_eos());\n\n  {\n    // compute FD ghost data and retrieve the result\n    fd::BoundaryConditionGhostData::apply(make_not_null(&box), element,\n                                          ReconstructorForTest{});\n    const auto direction = Direction<3>::upper_xi();\n    const DirectionalId<3> mortar_id = {direction,\n                                        ElementId<3>::external_boundary_id()};\n    const DataVector& fd_ghost_data =\n        get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box)\n            .at(mortar_id)\n            .neighbor_ghost_data_for_reconstruction();\n\n    // Copy the computed FD ghost data into a Variables object in order to\n    // facilitate comparison. Note that the returned FD ghost data contains the\n    // product of the lorentz factor and spatial velocity, Wv^i, not each of\n    // those separately.\n    using LorentzFactorTimesSpatialVelocity =\n        hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n    using prims_to_reconstruct =\n        tmpl::list<RestMassDensity, ElectronFraction, Temperature,\n                   LorentzFactorTimesSpatialVelocity, MagneticField,\n                   DivergenceCleaningField>;\n    const size_t num_face_pts{\n        subcell_mesh.extents().slice_away(direction.dimension()).product()};\n    Variables<prims_to_reconstruct> fd_ghost_vars{ghost_zone_size *\n                                                  num_face_pts};\n    std::copy(fd_ghost_data.begin(),\n              std::next(fd_ghost_data.begin(),\n                        static_cast<std::ptrdiff_t>(fd_ghost_vars.size())),\n              fd_ghost_vars.data());\n    using VarsFluxes =\n        Variables<db::wrap_tags_in<Flux, typename System::flux_variables>>;\n    VarsFluxes fd_ghost_fluxes{ghost_zone_size * num_face_pts, 0.0};\n    if (set_fluxes) {\n      REQUIRE(fd_ghost_data.size() ==\n              (fd_ghost_vars.size() + fd_ghost_fluxes.size()));\n      std::copy(std::next(fd_ghost_data.begin(),\n                          static_cast<std::ptrdiff_t>(fd_ghost_vars.size())),\n                fd_ghost_data.end(), fd_ghost_fluxes.data());\n    } else {\n      REQUIRE(fd_ghost_data.size() == fd_ghost_vars.size());\n    }\n\n    const auto check_fluxes =\n        [&fd_ghost_fluxes, &solution](\n            const auto& rest_mass_density, const auto& electron_fraction,\n            const auto& specific_internal_energy, const auto& local_temperature,\n            const auto& spatial_velocity, const auto& lorentz_factor,\n            const auto& magnetic_field, const auto& div_cleaning_field,\n            const double shift_value) {\n          using fluxes_tags = tmpl::push_back<\n              typename System::primitive_variables_tag::tags_list,\n              gr::Tags::SpatialMetric<DataVector, 3>,\n              gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n              gr::Tags::SqrtDetSpatialMetric<DataVector>,\n              gr::Tags::InverseSpatialMetric<DataVector, 3>>;\n          Variables<fluxes_tags> boundary_data{get(rest_mass_density).size()};\n\n          get<RestMassDensity>(boundary_data) = rest_mass_density;\n          get<ElectronFraction>(boundary_data) = electron_fraction;\n          get<SpecificInternalEnergy>(boundary_data) = specific_internal_energy;\n          get<Temperature>(boundary_data) = local_temperature;\n          get<SpatialVelocity>(boundary_data) = spatial_velocity;\n          get<LorentzFactor>(boundary_data) = lorentz_factor;\n          get<MagneticField>(boundary_data) = magnetic_field;\n          get<DivergenceCleaningField>(boundary_data) = div_cleaning_field;\n\n          get<Pressure>(boundary_data) =\n              solution.equation_of_state().pressure_from_density_and_energy(\n                  rest_mass_density,\n                  solution.equation_of_state()\n                      .specific_internal_energy_from_density_and_temperature(\n                          rest_mass_density, local_temperature));\n\n          tnsr::ii<DataVector, 3> spatial_metric(get(rest_mass_density).size(),\n                                                 0.0);\n          tnsr::II<DataVector, 3> inverse_spatial_metric(\n              get(rest_mass_density).size(), 0.0);\n          for (size_t i = 0; i < 3; ++i) {\n            spatial_metric.get(i, i) = 1.0;\n            inverse_spatial_metric.get(i, i) = 1.0;\n          }\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_data) =\n              spatial_metric;\n          get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_data) =\n              inverse_spatial_metric;\n          get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(boundary_data) =\n              Scalar<DataVector>(get(rest_mass_density).size(), 1.0);\n          get<gr::Tags::Lapse<DataVector>>(boundary_data) =\n              Scalar<DataVector>(get(rest_mass_density).size(), 1.0);\n          get<gr::Tags::Shift<DataVector, 3>>(boundary_data) =\n              tnsr::I<DataVector, 3>(get(rest_mass_density).size(),\n                                     shift_value);\n          typename evolution::dg::subcell::Tags::CellCenteredFlux<\n              typename System::flux_variables, 3>::type::value_type\n              expected_neighbor_fluxes{get(rest_mass_density).size()};\n          compute_fluxes_from_primitives(\n              make_not_null(&expected_neighbor_fluxes), boundary_data);\n          tmpl::for_each<tmpl::list<tmpl::at_c<cons_tags, 0>>>(\n              [&fd_ghost_fluxes, &expected_neighbor_fluxes](auto cons_tag_v) {\n                using tag =\n                    Flux<tmpl::type_from<std::decay_t<decltype(cons_tag_v)>>>;\n                CAPTURE(pretty_type::get_name<tag>());\n                CHECK_ITERABLE_APPROX(get<tag>(fd_ghost_fluxes),\n                                      get<tag>(expected_neighbor_fluxes));\n              });\n        };\n\n    //\n    // now test each boundary conditions\n    //\n    if (typeid(BoundaryConditionType) ==\n        typeid(BoundaryConditions::DirichletAnalytic)) {\n      const auto ghost_logical_coords =\n          evolution::dg::subcell::fd::ghost_zone_logical_coordinates(\n              subcell_mesh, ghost_zone_size, direction);\n      const auto ghost_inertial_coords = (*grid_to_inertial_map)(\n          logical_to_grid_map(ghost_logical_coords), time, functions_of_time);\n      const auto rest_mass_density_py = pypp::call<Scalar<DataVector>>(\n          \"GrMhd.SmoothFlow\", \"rest_mass_density\", ghost_inertial_coords, time,\n          mean_velocity, wave_vector, pressure, adiabatic_index,\n          perturbation_size);\n      const auto electron_fraction_py = pypp::call<Scalar<DataVector>>(\n          \"GrMhd.SmoothFlow\", \"electron_fraction\", ghost_inertial_coords, time,\n          mean_velocity, wave_vector, pressure, adiabatic_index,\n          perturbation_size);\n      const auto pressure_py = pypp::call<Scalar<DataVector>>(\n          \"GrMhd.SmoothFlow\", \"pressure\", ghost_inertial_coords, time,\n          mean_velocity, wave_vector, pressure, adiabatic_index,\n          perturbation_size);\n      const auto temperature_py = pypp::call<Scalar<DataVector>>(\n          \"GrMhd.SmoothFlow\", \"temperature\", ghost_inertial_coords, time,\n          mean_velocity, wave_vector, pressure, adiabatic_index,\n          perturbation_size);\n      const auto specific_internal_energy_py = pypp::call<Scalar<DataVector>>(\n          \"GrMhd.SmoothFlow\", \"specific_internal_energy\", ghost_inertial_coords,\n          time, mean_velocity, wave_vector, pressure, adiabatic_index,\n          perturbation_size);\n      const auto lorentz_factor_py = pypp::call<Scalar<DataVector>>(\n          \"GrMhd.SmoothFlow\", \"lorentz_factor\", ghost_inertial_coords, time,\n          mean_velocity, wave_vector, pressure, adiabatic_index,\n          perturbation_size);\n      const auto spatial_velocity_py = pypp::call<tnsr::I<DataVector, 3>>(\n          \"GrMhd.SmoothFlow\", \"spatial_velocity\", ghost_inertial_coords, time,\n          mean_velocity, wave_vector, pressure, adiabatic_index,\n          perturbation_size);\n      const auto magnetic_field_py = pypp::call<tnsr::I<DataVector, 3>>(\n          \"GrMhd.SmoothFlow\", \"magnetic_field\", ghost_inertial_coords, time,\n          mean_velocity, wave_vector, pressure, adiabatic_index,\n          perturbation_size);\n      const auto div_cleaning_field_py = pypp::call<Scalar<DataVector>>(\n          \"GrMhd.SmoothFlow\", \"divergence_cleaning_field\",\n          ghost_inertial_coords, time, mean_velocity, wave_vector, pressure,\n          adiabatic_index, perturbation_size);\n\n      // check values\n      CHECK_ITERABLE_APPROX(get<RestMassDensity>(fd_ghost_vars),\n                            rest_mass_density_py);\n      CHECK_ITERABLE_APPROX(get<ElectronFraction>(fd_ghost_vars),\n                            electron_fraction_py);\n      CHECK_ITERABLE_APPROX(get<Temperature>(fd_ghost_vars), temperature_py);\n      {\n        tnsr::I<DataVector, 3> lorentz_factor_times_spatial_velocity_py{\n            ghost_zone_size * num_face_pts};\n        for (size_t i = 0; i < 3; ++i) {\n          lorentz_factor_times_spatial_velocity_py.get(i) =\n              get(lorentz_factor_py) * spatial_velocity_py.get(i);\n        }\n        CHECK_ITERABLE_APPROX(\n            get<LorentzFactorTimesSpatialVelocity>(fd_ghost_vars),\n            lorentz_factor_times_spatial_velocity_py);\n      }\n      CHECK_ITERABLE_APPROX(get<MagneticField>(fd_ghost_vars),\n                            magnetic_field_py);\n      CHECK_ITERABLE_APPROX(get<DivergenceCleaningField>(fd_ghost_vars),\n                            div_cleaning_field_py);\n      if (set_fluxes) {\n        check_fluxes(rest_mass_density_py, electron_fraction_py,\n                     specific_internal_energy_py, temperature_py,\n                     spatial_velocity_py, lorentz_factor_py, magnetic_field_py,\n                     div_cleaning_field_py, 0.0);\n      }\n    }\n\n    if (typeid(BoundaryConditionType) ==\n        typeid(BoundaryConditions::DemandOutgoingCharSpeeds)) {\n      // At this moment shift vector was set to be that the\n      // DemandOutgoingCharSpeedscondition does not throw any error. Here we\n      // just check if `fd::BoundaryConditionGhostData::apply()` has correctly\n      // filled out `fd_ghost_data` with the outermost value.\n      Variables<prims_to_reconstruct> expected_ghost_vars{ghost_zone_size *\n                                                          num_face_pts};\n      get(get<RestMassDensity>(expected_ghost_vars)) = 1.0;\n      get(get<Temperature>(expected_ghost_vars)) = 1.0;\n      get(get<ElectronFraction>(expected_ghost_vars)) = 0.1;\n      for (size_t i = 0; i < 3; ++i) {\n        get<LorentzFactorTimesSpatialVelocity>(expected_ghost_vars).get(i) =\n            0.2;\n        get<MagneticField>(expected_ghost_vars).get(i) = 0.5;\n      }\n      get(get<DivergenceCleaningField>(expected_ghost_vars)) = 1e-2;\n\n      tmpl::for_each<prims_to_reconstruct>(\n          [&expected_ghost_vars, &fd_ghost_vars](auto tag_v) {\n            using tag = typename tmpl::type_from<decltype(tag_v)>;\n            CHECK_ITERABLE_APPROX(get<tag>(expected_ghost_vars),\n                                  get<tag>(fd_ghost_vars));\n          });\n\n      if (set_fluxes) {\n        const Scalar<DataVector> expected_specific_internal_energy(\n            expected_ghost_vars.number_of_grid_points(), 0.3);\n        const Scalar<DataVector> expected_lorentz_factor(\n            expected_ghost_vars.number_of_grid_points(),\n            sqrt(1.0 +\n                 square(get<0>(get<LorentzFactorTimesSpatialVelocity>(\n                     expected_ghost_vars))[0]) +\n                 square(get<1>(get<LorentzFactorTimesSpatialVelocity>(\n                     expected_ghost_vars))[0]) +\n                 square(get<2>(get<LorentzFactorTimesSpatialVelocity>(\n                     expected_ghost_vars))[0])));\n        const tnsr::I<DataVector, 3> expected_spatial_velocity =\n            tenex::evaluate<ti::I>(get<LorentzFactorTimesSpatialVelocity>(\n                                       expected_ghost_vars)(ti::I) /\n                                   expected_lorentz_factor());\n        check_fluxes(get<RestMassDensity>(expected_ghost_vars),\n                     get<ElectronFraction>(expected_ghost_vars),\n                     expected_specific_internal_energy,\n                     get<Temperature>(expected_ghost_vars),\n                     expected_spatial_velocity, expected_lorentz_factor,\n                     get<MagneticField>(expected_ghost_vars),\n                     get<DivergenceCleaningField>(expected_ghost_vars), -2.0);\n      }\n\n      // Set shift to be zero so that the DemandOutgoingCharSpeeds condition is\n      // violated. See if the code fails correctly.\n      db::mutate<gr::Tags::Shift<DataVector, 3>>(\n          [](const gsl::not_null<tnsr::I<DataVector, 3>*> shift_vector) {\n            for (size_t i = 0; i < 3; ++i) {\n              (*shift_vector).get(i) = 0.0;\n            }\n          },\n          make_not_null(&box));\n      CHECK_THROWS_WITH(\n          ([&box, &element]() {\n            fd::BoundaryConditionGhostData::apply(make_not_null(&box), element,\n                                                  ReconstructorForTest{});\n          })(),\n          Catch::Matchers::ContainsSubstring(\n              \"Subcell DemandOutgoingCharSpeeds boundary condition violated\"));\n\n      // Test when the volume mesh velocity has value, which will raise ERROR.\n      // See if the code fails correctly.\n      db::mutate<domain::Tags::MeshVelocity<3>>(\n          [&subcell_mesh](\n              const gsl::not_null<std::optional<tnsr::I<DataVector, 3>>*>\n                  mesh_velocity) {\n            tnsr::I<DataVector, 3> volume_mesh_velocity_tnsr(\n                subcell_mesh.number_of_grid_points(), 0.0);\n            *mesh_velocity = volume_mesh_velocity_tnsr;\n          },\n          make_not_null(&box));\n      CHECK_THROWS_WITH(\n          ([&box, &element]() {\n            fd::BoundaryConditionGhostData::apply(make_not_null(&box), element,\n                                                  ReconstructorForTest{});\n          })(),\n          Catch::Matchers::ContainsSubstring(\n              \"Subcell currently does not support moving mesh\"));\n    }\n\n    if (typeid(BoundaryConditionType) ==\n        typeid(BoundaryConditions::HydroFreeOutflow)) {\n      Variables<prims_to_reconstruct> expected_ghost_vars{ghost_zone_size *\n                                                          num_face_pts};\n      // cf) See line 172-179 for values of prim variables in volume.\n      get(get<RestMassDensity>(expected_ghost_vars)) = 1.0;\n      get(get<Temperature>(expected_ghost_vars)) =\n          get(solution.equation_of_state().temperature_from_density_and_energy(\n              get<RestMassDensity>(expected_ghost_vars),\n              solution.equation_of_state()\n                  .specific_internal_energy_from_density_and_pressure(\n                      get<RestMassDensity>(expected_ghost_vars),\n                      Scalar<DataVector>(\n                          expected_ghost_vars.number_of_grid_points(), 1.0))));\n      get(get<ElectronFraction>(expected_ghost_vars)) = 0.1;\n      for (size_t i = 0; i < 3; ++i) {\n        get<LorentzFactorTimesSpatialVelocity>(expected_ghost_vars).get(i) =\n            0.2;\n        get<MagneticField>(expected_ghost_vars).get(i) = 0.5;\n      }\n      get(get<DivergenceCleaningField>(expected_ghost_vars)) =\n          0.0;  // note the difference from DemandOutgoingCharSpeeds boundary\n                // condition. While the volume value was set to 1e-2,\n                // HydroFreeOutflow boundary should assign zero to Phi in ghost\n                // zones.\n\n      tmpl::for_each<prims_to_reconstruct>(\n          [&expected_ghost_vars, &fd_ghost_vars](auto tag_v) {\n            using tag = typename tmpl::type_from<decltype(tag_v)>;\n            CHECK_ITERABLE_APPROX(get<tag>(expected_ghost_vars),\n                                  get<tag>(fd_ghost_vars));\n          });\n      if (set_fluxes) {\n        const Scalar<DataVector> expected_specific_internal_energy(\n            expected_ghost_vars.number_of_grid_points(), 0.3);\n        const Scalar<DataVector> expected_lorentz_factor(\n            expected_ghost_vars.number_of_grid_points(),\n            sqrt(1.0 +\n                 square(get<0>(get<LorentzFactorTimesSpatialVelocity>(\n                     expected_ghost_vars))[0]) +\n                 square(get<1>(get<LorentzFactorTimesSpatialVelocity>(\n                     expected_ghost_vars))[0]) +\n                 square(get<2>(get<LorentzFactorTimesSpatialVelocity>(\n                     expected_ghost_vars))[0])));\n        const tnsr::I<DataVector, 3> expected_spatial_velocity =\n            tenex::evaluate<ti::I>(get<LorentzFactorTimesSpatialVelocity>(\n                                       expected_ghost_vars)(ti::I) /\n                                   expected_lorentz_factor());\n        check_fluxes(get<RestMassDensity>(expected_ghost_vars),\n                     get<ElectronFraction>(expected_ghost_vars),\n                     expected_specific_internal_energy,\n                     get<Temperature>(expected_ghost_vars),\n                     expected_spatial_velocity, expected_lorentz_factor,\n                     get<MagneticField>(expected_ghost_vars),\n                     get<DivergenceCleaningField>(expected_ghost_vars), -2.0);\n      }\n\n      // Now set the spatial velocity pointing inward with respect to the\n      // upper_xi() direction which is the direction with which test is done.\n      // Then re-compute ghost zone data to see if inward-pointing components\n      // are set to zero.\n      db::mutate<SpatialVelocity>(\n          [](const gsl::not_null<tnsr::I<DataVector, 3>*>\n                 interior_spatial_velocity) {\n            (*interior_spatial_velocity).get(0) = -0.2;\n          },\n          make_not_null(&box));\n      fd::BoundaryConditionGhostData::apply(make_not_null(&box), element,\n                                            ReconstructorForTest{});\n\n      const DataVector& fd_ghost_data_velocity_inward =\n          get<evolution::dg::subcell::Tags::GhostDataForReconstruction<3>>(box)\n              .at(mortar_id)\n              .neighbor_ghost_data_for_reconstruction();\n      Variables<prims_to_reconstruct> fd_ghost_vars_velocity_inward{\n          ghost_zone_size * num_face_pts};\n      std::copy(fd_ghost_data_velocity_inward.begin(),\n                std::next(fd_ghost_data_velocity_inward.begin(),\n                          static_cast<std::ptrdiff_t>(\n                              fd_ghost_vars_velocity_inward.size())),\n                fd_ghost_vars_velocity_inward.data());\n\n      // we expect Wv^0 is zero\n      get<LorentzFactorTimesSpatialVelocity>(expected_ghost_vars).get(0) = 0.0;\n\n      tmpl::for_each<prims_to_reconstruct>(\n          [&expected_ghost_vars, &fd_ghost_vars_velocity_inward](auto tag_v) {\n            using tag = typename tmpl::type_from<decltype(tag_v)>;\n            CHECK_ITERABLE_APPROX(get<tag>(expected_ghost_vars),\n                                  get<tag>(fd_ghost_vars_velocity_inward));\n          });\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.ValenciaDivClean.Fd.BCondGhostData\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/\"};\n\n  const SolutionForTest solution{mean_velocity, wave_vector, pressure,\n                                 adiabatic_index, perturbation_size};\n\n  for (const bool set_fluxes : {true, false}) {\n    test(\n        BoundaryConditions::DirichletAnalytic{\n            std::make_unique<SolutionForTest>(solution)},\n        set_fluxes, solution);\n    test(BoundaryConditions::DemandOutgoingCharSpeeds{}, set_fluxes, solution);\n    test(BoundaryConditions::HydroFreeOutflow{}, set_fluxes, solution);\n  }\n\n// check that the periodic BC fails\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([&solution]() {\n        test(domain::BoundaryConditions::Periodic<\n                 BoundaryConditions::BoundaryCondition>{},\n             false, solution);\n      })(),\n      Catch::Matchers::ContainsSubstring(\"not on external boundaries\"));\n#endif\n}\n}  // namespace\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Test_MonotonicityPreserving5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonicityPreserving5.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.ValenciaDivClean.Fd.Mp5Prim\",\n                  \"[Unit][Evolution]\") {\n  namespace helpers = TestHelpers::grmhd::ValenciaDivClean::fd;\n  // Test reconstructing T and rho*T. For low-order reconstructors this can\n  // fail (like MC) because the nonlinear terms (quadratics) aren't captured\n  // accurately enough.\n  const grmhd::ValenciaDivClean::fd::MonotonicityPreserving5Prim mp5_recons{\n      4.0, 1e-10, false};\n  helpers::test_prim_reconstructor(4, mp5_recons);\n  helpers::test_prim_reconstructor(\n      4, grmhd::ValenciaDivClean::fd::MonotonicityPreserving5Prim{4.0, 1e-10,\n                                                                  true});\n\n  const auto wcns5z_from_options_base = TestHelpers::test_factory_creation<\n      grmhd::ValenciaDivClean::fd::Reconstructor,\n      grmhd::ValenciaDivClean::fd::OptionTags::Reconstructor>(\n      \"MonotonicityPreserving5Prim:\\n\"\n      \"  Alpha: 4.0\\n\"\n      \"  Epsilon: 1.0e-10\\n\"\n      \"  ReconstructRhoTimesTemperature: false\\n\");\n  auto* const mp5_from_options = dynamic_cast<\n      const grmhd::ValenciaDivClean::fd::MonotonicityPreserving5Prim*>(\n      wcns5z_from_options_base.get());\n  REQUIRE(mp5_from_options != nullptr);\n  CHECK(*mp5_from_options == mp5_recons);\n\n  CHECK(mp5_recons != grmhd::ValenciaDivClean::fd::MonotonicityPreserving5Prim(\n                          3.0, 1e-10, false));\n  CHECK(mp5_recons != grmhd::ValenciaDivClean::fd::MonotonicityPreserving5Prim(\n                          4.0, 2e-10, false));\n  CHECK(mp5_recons != grmhd::ValenciaDivClean::fd::MonotonicityPreserving5Prim(\n                          4.0, 1e-10, true));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Test_MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.GrMhd.ValenciaDivClean.Fd.MonotonisedCentralPrim\",\n    \"[Unit][Evolution]\") {\n  namespace helpers = TestHelpers::grmhd::ValenciaDivClean::fd;\n  // We only test reconstructing T because for low-order reconstructors this\n  // fails because the nonlinear terms (quadratics) aren't captured\n  // accurately enough.\n  const grmhd::ValenciaDivClean::fd::MonotonisedCentralPrim mc_recons{false};\n  helpers::test_prim_reconstructor(5, mc_recons);\n  const auto mc_from_options_base = TestHelpers::test_factory_creation<\n      grmhd::ValenciaDivClean::fd::Reconstructor,\n      grmhd::ValenciaDivClean::fd::OptionTags::Reconstructor>(\n      \"MonotonisedCentralPrim:\\n\"\n      \"  ReconstructRhoTimesTemperature: false\\n\");\n  auto* const mc_from_options =\n      dynamic_cast<const grmhd::ValenciaDivClean::fd::MonotonisedCentralPrim*>(\n          mc_from_options_base.get());\n  REQUIRE(mc_from_options != nullptr);\n  CHECK(*mc_from_options == mc_recons);\n  const grmhd::ValenciaDivClean::fd::MonotonisedCentralPrim mc_recons_recon_T{\n      true};\n  CHECK_FALSE(mc_recons_recon_T == mc_recons);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Test_PositivityPreservingAdaptiveOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.ValenciaDivClean.Fd.PpaoPrim\",\n                  \"[Unit][Evolution]\") {\n  namespace helpers = TestHelpers::grmhd::ValenciaDivClean::fd;\n  auto mc = fd::reconstruction::FallbackReconstructorType::MonotonisedCentral;\n\n  // Test reconstructing T and rho*T. For low-order reconstructors this can\n  // fail (like MC) because the nonlinear terms (quadratics) aren't captured\n  // accurately enough.\n  for (const bool reconstruct_rho_times_T : {false, true}) {\n    CAPTURE(reconstruct_rho_times_T);\n    helpers::test_prim_reconstructor(\n        6, grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim{\n               4.0, 4.0, std::nullopt, mc, reconstruct_rho_times_T});\n    helpers::test_prim_reconstructor(\n        8, grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim{\n               4.0, std::nullopt, 4.0, mc, reconstruct_rho_times_T});\n    helpers::test_prim_reconstructor(\n        8, grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim{\n               4.0, 4.0, 4.0, mc, reconstruct_rho_times_T});\n  }\n\n  const grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim\n      ppao_recons{4.0, std::nullopt, std::nullopt, mc, false};\n  helpers::test_prim_reconstructor(4, ppao_recons);\n\n  const auto ppao_from_options_base = TestHelpers::test_factory_creation<\n      grmhd::ValenciaDivClean::fd::Reconstructor,\n      grmhd::ValenciaDivClean::fd::OptionTags::Reconstructor>(\n      \"PositivityPreservingAdaptiveOrderPrim:\\n\"\n      \"  Alpha5: 4.0\\n\"\n      \"  Alpha7: None\\n\"\n      \"  Alpha9: None\\n\"\n      \"  LowOrderReconstructor: MonotonisedCentral\\n\"\n      \"  ReconstructRhoTimesTemperature: false\\n\");\n  auto* const ppao_from_options =\n      dynamic_cast<const grmhd::ValenciaDivClean::fd::\n                       PositivityPreservingAdaptiveOrderPrim*>(\n          ppao_from_options_base.get());\n  REQUIRE(ppao_from_options != nullptr);\n  CHECK(*ppao_from_options == ppao_recons);\n\n  CHECK(ppao_recons !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            4.5, std::nullopt, std::nullopt, mc, false));\n  CHECK(ppao_recons !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            4.0, 4.0, std::nullopt, mc, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            4.0, 4.0, std::nullopt, mc, false) !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            4.0, 4.1, std::nullopt, mc, false));\n  CHECK(ppao_recons !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            4.0, std::nullopt, 4.0, mc, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            4.0, std::nullopt, 4.0, mc, false) !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            4.0, std::nullopt, 4.1, mc, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, std::nullopt, 4.0, mc, false) ==\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, std::nullopt, 4.0, mc, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0, mc, false) ==\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0, mc, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0, mc, false) !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.1, 6.0, 4.0, mc, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0, mc, false) !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.1, 4.0, mc, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0, mc, false) !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.1, mc, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0, mc, false) !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0,\n            fd::reconstruction::FallbackReconstructorType::Minmod, false));\n  CHECK(ppao_recons !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            4.0, std::nullopt, std::nullopt,\n            fd::reconstruction::FallbackReconstructorType::Minmod, false));\n  CHECK(grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0, mc, false) !=\n        grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n            5.0, 6.0, 4.0, mc, true));\n\n  CHECK_THROWS_WITH(\n      grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim(\n          4.5, std::nullopt, std::nullopt,\n          fd::reconstruction::FallbackReconstructorType::None, false),\n      Catch::Matchers::ContainsSubstring(\n          \"None is not an allowed low-order reconstructor.\"));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Test_Tag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.ValenciaDivClean.Fd.Tag\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<\n      grmhd::ValenciaDivClean::fd::Tags::Reconstructor>(\"Reconstructor\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Test_Wcns5z.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Wcns5z.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PrimReconstructor.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.ValenciaDivClean.Fd.Wcns5zPrim\",\n                  \"[Unit][Evolution]\") {\n  namespace helpers = TestHelpers::grmhd::ValenciaDivClean::fd;\n  auto mc = fd::reconstruction::FallbackReconstructorType::MonotonisedCentral;\n\n  // Test reconstructing T and rho*T. For low-order reconstructors this can\n  // fail (like MC) because the nonlinear terms (quadratics) aren't captured\n  // accurately enough.\n  const grmhd::ValenciaDivClean::fd::Wcns5zPrim wcns5z_recons{2, 2.0e-16, mc, 1,\n                                                              false};\n  helpers::test_prim_reconstructor(4, wcns5z_recons);\n  helpers::test_prim_reconstructor(\n      4, grmhd::ValenciaDivClean::fd::Wcns5zPrim{2, 2.0e-16, mc, 1, true});\n\n  const auto wcns5z_from_options_base = TestHelpers::test_factory_creation<\n      grmhd::ValenciaDivClean::fd::Reconstructor,\n      grmhd::ValenciaDivClean::fd::OptionTags::Reconstructor>(\n      \"Wcns5zPrim:\\n\"\n      \"  NonlinearWeightExponent: 2\\n\"\n      \"  Epsilon: 2.0e-16\\n\"\n      \"  FallbackReconstructor: MonotonisedCentral\\n\"\n      \"  MaxNumberOfExtrema: 1\\n\"\n      \"  ReconstructRhoTimesTemperature: false\\n\");\n  auto* const wcns5z_from_options =\n      dynamic_cast<const grmhd::ValenciaDivClean::fd::Wcns5zPrim*>(\n          wcns5z_from_options_base.get());\n  REQUIRE(wcns5z_from_options != nullptr);\n  CHECK(*wcns5z_from_options == wcns5z_recons);\n\n  CHECK(wcns5z_recons !=\n        grmhd::ValenciaDivClean::fd::Wcns5zPrim(1, 2.0e-16, mc, 1, false));\n  CHECK(wcns5z_recons !=\n        grmhd::ValenciaDivClean::fd::Wcns5zPrim(2, 1.0e-16, mc, 1, false));\n  CHECK(wcns5z_recons !=\n        grmhd::ValenciaDivClean::fd::Wcns5zPrim(\n            2, 2.0e-16, fd::reconstruction::FallbackReconstructorType::None, 1,\n            false));\n  CHECK(wcns5z_recons !=\n        grmhd::ValenciaDivClean::fd::Wcns5zPrim(2, 2.0e-16, mc, 2, false));\n  CHECK(wcns5z_recons !=\n        grmhd::ValenciaDivClean::fd::Wcns5zPrim(2, 2.0e-16, mc, 1, true));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef b_dot_v(magnetic_field, spatial_velocity, spatial_metric):\n    return np.einsum(\n        \"ab, ab\", spatial_metric, np.outer(magnetic_field, spatial_velocity)\n    )\n\n\ndef b_squared(magnetic_field, spatial_metric):\n    return np.einsum(\n        \"ab, ab\", spatial_metric, np.outer(magnetic_field, magnetic_field)\n    )\n\n\ndef magnetic_field_one_form(magnetic_field, spatial_metric):\n    return np.einsum(\"a, ia\", magnetic_field, spatial_metric)\n\n\ndef p_star(pressure, b_dot_v, b_squared, lorentz_factor):\n    return pressure + 0.5 * (b_dot_v**2 + b_squared / lorentz_factor**2)\n\n\ndef spatial_velocity_one_form(spatial_velocity, spatial_metric):\n    return np.einsum(\"a, ia\", spatial_velocity, spatial_metric)\n\n\ndef tilde_d_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n):\n    return tilde_d * (lapse * spatial_velocity - shift)\n\n\ndef tilde_ye_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n):\n    return tilde_ye * (lapse * spatial_velocity - shift)\n\n\ndef tilde_tau_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n):\n    b_dot_v_ = b_dot_v(magnetic_field, spatial_velocity, spatial_metric)\n    return (\n        sqrt_det_spatial_metric\n        * lapse\n        * p_star(\n            pressure,\n            b_dot_v_,\n            b_squared(magnetic_field, spatial_metric),\n            lorentz_factor,\n        )\n        * spatial_velocity\n        + tilde_tau * (lapse * spatial_velocity - shift)\n        - lapse * b_dot_v_ * tilde_b\n    )\n\n\ndef tilde_s_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n):\n    b_dot_v_ = b_dot_v(magnetic_field, spatial_velocity, spatial_metric)\n    b_i = (\n        magnetic_field_one_form(magnetic_field, spatial_metric) / lorentz_factor\n        + spatial_velocity_one_form(spatial_velocity, spatial_metric)\n        * lorentz_factor\n        * b_dot_v_\n    )\n    result = np.outer(lapse * spatial_velocity - shift, tilde_s)\n    result -= lapse / lorentz_factor * np.outer(tilde_b, b_i)\n    result += (\n        sqrt_det_spatial_metric\n        * lapse\n        * p_star(\n            pressure,\n            b_dot_v_,\n            b_squared(magnetic_field, spatial_metric),\n            lorentz_factor,\n        )\n        * np.identity(shift.size)\n    )\n    return result\n\n\ndef tilde_b_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n):\n    result = np.outer(lapse * spatial_velocity - shift, tilde_b)\n    result += lapse * inv_spatial_metric * tilde_phi\n    result -= lapse * np.outer(tilde_b, spatial_velocity)\n    return result\n\n\ndef tilde_phi_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n):\n    return lapse * tilde_b - tilde_phi * shift\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Sources.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef b_dot_v(magnetic_field, spatial_velocity, spatial_metric):\n    return np.einsum(\n        \"ab, ab\", spatial_metric, np.outer(magnetic_field, spatial_velocity)\n    )\n\n\ndef b_squared(magnetic_field, spatial_metric):\n    return np.einsum(\n        \"ab, ab\", spatial_metric, np.outer(magnetic_field, magnetic_field)\n    )\n\n\ndef p_star(pressure, b_dot_v, b_squared, lorentz_factor):\n    return pressure + 0.5 * (b_dot_v**2 + b_squared / lorentz_factor**2)\n\n\ndef stress_tensor(\n    spatial_velocity,\n    magnetic_field,\n    rest_mass_density,\n    specific_internal_energy,\n    lorentz_factor,\n    pressure,\n    spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n):\n    b_squared_ = b_squared(magnetic_field, spatial_metric)\n    b_dot_v_ = b_dot_v(magnetic_field, spatial_velocity, spatial_metric)\n    return sqrt_det_spatial_metric * (\n        (\n            ((1 + specific_internal_energy) * rest_mass_density + pressure)\n            * lorentz_factor**2\n            + b_squared_\n        )\n        * np.outer(spatial_velocity, spatial_velocity)\n        + p_star(pressure, b_dot_v_, b_squared_, lorentz_factor)\n        * inv_spatial_metric\n        - b_dot_v_\n        * (\n            np.outer(magnetic_field, spatial_velocity)\n            + np.outer(spatial_velocity, magnetic_field)\n        )\n        - np.outer(magnetic_field, magnetic_field) / lorentz_factor**2\n    )\n\n\ndef source_tilde_tau(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    spatial_velocity,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    lorentz_factor,\n    pressure,\n    lapse,\n    d_lapse,\n    d_shift,\n    spatial_metric,\n    d_spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    stress_tensor_ = stress_tensor(\n        spatial_velocity,\n        magnetic_field,\n        rest_mass_density,\n        specific_internal_energy,\n        lorentz_factor,\n        pressure,\n        spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n    )\n    return lapse * np.einsum(\n        \"ab, ab\", stress_tensor_, extrinsic_curvature\n    ) - np.einsum(\"ab, ab\", inv_spatial_metric, np.outer(tilde_s, d_lapse))\n\n\ndef source_tilde_s(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    spatial_velocity,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    lorentz_factor,\n    pressure,\n    lapse,\n    d_lapse,\n    d_shift,\n    spatial_metric,\n    d_spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    stress_tensor_ = stress_tensor(\n        spatial_velocity,\n        magnetic_field,\n        rest_mass_density,\n        specific_internal_energy,\n        lorentz_factor,\n        pressure,\n        spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n    )\n    return (\n        0.5 * lapse * np.einsum(\"ab, iab\", stress_tensor_, d_spatial_metric)\n        + np.einsum(\"a, ia\", tilde_s, d_shift)\n        - (tilde_d + tilde_tau) * d_lapse\n    )\n\n\ndef source_tilde_b(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    spatial_velocity,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    lorentz_factor,\n    pressure,\n    lapse,\n    d_lapse,\n    d_shift,\n    spatial_metric,\n    d_spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    term_one = np.einsum(\"ab, iab\", inv_spatial_metric, d_spatial_metric)\n    term_two = np.einsum(\"ab, aib\", inv_spatial_metric, d_spatial_metric)\n    return (\n        tilde_phi * np.einsum(\"a, ia\", d_lapse, inv_spatial_metric)\n        - np.einsum(\"a, ai\", tilde_b, d_shift)\n        + lapse\n        * tilde_phi\n        * (\n            0.5 * np.einsum(\"ia, a\", inv_spatial_metric, term_one)\n            - np.einsum(\"ia, a\", inv_spatial_metric, term_two)\n        )\n    )\n\n\ndef source_tilde_phi(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    spatial_velocity,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_internal_energy,\n    lorentz_factor,\n    pressure,\n    lapse,\n    d_lapse,\n    d_shift,\n    spatial_metric,\n    d_spatial_metric,\n    inv_spatial_metric,\n    sqrt_det_spatial_metric,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return lapse * tilde_phi * (\n        -np.einsum(\"ab, ab\", inv_spatial_metric, extrinsic_curvature)\n        - constraint_damping_parameter\n    ) + np.einsum(\"a, a\", tilde_b, d_lapse)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_ComputeFluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace {\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.Subcell.ComputeFluxes\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist(0.0, 1.0);\n  const size_t num_pts = 5;\n\n  auto vars = make_with_random_values<Variables<\n      tmpl::append<ComputeFluxes::return_tags, ComputeFluxes::argument_tags>>>(\n      make_not_null(&gen), make_not_null(&dist), num_pts);\n\n  Variables<ComputeFluxes::return_tags> expected_fluxes{num_pts};\n  ComputeFluxes::apply(\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeS<>,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeB<>,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      get<grmhd::ValenciaDivClean::Tags::TildeD>(vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeYe>(vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeTau>(vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeS<>>(vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeB<>>(vars),\n      get<grmhd::ValenciaDivClean::Tags::TildePhi>(vars),\n      get<gr::Tags::Lapse<DataVector>>(vars),\n      get<gr::Tags::Shift<DataVector, 3>>(vars),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(vars),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars),\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(vars),\n      get<hydro::Tags::Pressure<DataVector>>(vars),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(vars),\n      get<hydro::Tags::LorentzFactor<DataVector>>(vars),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(vars));\n\n  subcell::compute_fluxes(make_not_null(&vars));\n\n  tmpl::for_each<ComputeFluxes::return_tags>(\n      [&expected_fluxes, &vars](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<tag>(vars), get<tag>(expected_fluxes));\n      });\n}\n}  // namespace\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_FixConservativesAndComputePrims.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/FixConservativesAndComputePrims.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/MagneticFieldTreatment.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.Subcell.FixConsAndComputePrims\",\n    \"[Unit][Evolution]\") {\n  using System = grmhd::ValenciaDivClean::System;\n\n  // Only use 1 grid point to see that we correctly flagged the point as being\n  // fixed. We're really only testing that the mutator calls the correct\n  // functions.\n  const size_t num_pts = 1;\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{num_pts, 0.0};\n  tnsr::II<DataVector, 3, Frame::Inertial> inverse_spatial_metric{num_pts, 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = 1.0;\n    inverse_spatial_metric.get(i, i) = 1.0;\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{num_pts, 1.0};\n  const grmhd::ValenciaDivClean::FixConservatives variable_fixer{\n      1.e-7,   1.0e-7,\n      1.0e-10, 1.0e-10,\n      0.0,     0.0,\n      1.e-7,   0.0,\n      true,    hydro::MagneticFieldTreatment::AssumeNonZero};\n  typename System::variables_tag::type cons_vars{num_pts, 0.0};\n  get(get<grmhd::ValenciaDivClean::Tags::TildeD>(cons_vars))[0] = 2.e-12;\n  get(get<grmhd::ValenciaDivClean::Tags::TildeYe>(cons_vars))[0] = 2.e-13;\n  get(get<grmhd::ValenciaDivClean::Tags::TildeTau>(cons_vars))[0] = 1.e-7;\n\n  const EquationsOfState::Barotropic3D eos{\n      EquationsOfState::PolytropicFluid<true>{100.0, 2.0}};\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  auto box = db::create<db::AddSimpleTags<\n      grmhd::ValenciaDivClean::Tags::VariablesNeededFixing,\n      typename System::variables_tag, typename System::primitive_variables_tag,\n      ::Tags::VariableFixer<grmhd::ValenciaDivClean::FixConservatives>,\n      hydro::Tags::GrmhdEquationOfState, gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>>(\n      false, cons_vars,\n      typename System::primitive_variables_tag::type{num_pts, 1.0e-4},\n      variable_fixer, eos.get_clone(), spatial_metric, inverse_spatial_metric,\n      sqrt_det_spatial_metric, primitive_from_conservative_options);\n\n  using recovery_schemes = tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>;\n  db::mutate_apply<grmhd::ValenciaDivClean::subcell::\n                       FixConservativesAndComputePrims<recovery_schemes>>(\n      make_not_null(&box));\n\n  // Verify that the conserved variables were fixed\n  CHECK(db::get<grmhd::ValenciaDivClean::Tags::VariablesNeededFixing>(box));\n\n  // Manually do a primitive recovery and see that the values match what's in\n  // the DataBox.\n  typename System::primitive_variables_tag::type expected_prims{num_pts,\n                                                                1.0e-4};\n  grmhd::ValenciaDivClean::PrimitiveFromConservative<recovery_schemes, true>::\n      apply(\n          make_not_null(\n              &get<hydro::Tags::RestMassDensity<DataVector>>(expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::ElectronFraction<DataVector>>(expected_prims)),\n          make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n              expected_prims)),\n          make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n              expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::MagneticField<DataVector, 3>>(expected_prims)),\n          make_not_null(&get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n              expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::LorentzFactor<DataVector>>(expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::Pressure<DataVector>>(expected_prims)),\n          make_not_null(\n              &get<hydro::Tags::Temperature<DataVector>>(expected_prims)),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(box),\n          db::get<grmhd::ValenciaDivClean::Tags::TildePhi>(box), spatial_metric,\n          inverse_spatial_metric, sqrt_det_spatial_metric, eos,\n          primitive_from_conservative_options);\n  CHECK_VARIABLES_APPROX(db::get<typename System::primitive_variables_tag>(box),\n                         expected_prims);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace {\ntemplate <size_t Dim, typename... Maps, typename Solution>\nauto face_centered_gr_tags(\n    const Mesh<Dim>& subcell_mesh, const double time,\n    const ElementMap<3, Frame::Grid>& element_map,\n    const domain::CoordinateMap<Frame::Grid, Frame::Inertial, Maps...>&\n        moving_mesh_map,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const Solution& soln) {\n  std::array<typename System::flux_spacetime_variables_tag::type, Dim>\n      face_centered_gr_vars{};\n\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto basis = make_array<Dim>(subcell_mesh.basis(0));\n    auto quadrature = make_array<Dim>(subcell_mesh.quadrature(0));\n    auto extents = make_array<Dim>(subcell_mesh.extents(0));\n    gsl::at(extents, d) = subcell_mesh.extents(0) + 1;\n    gsl::at(quadrature, d) = Spectral::Quadrature::FaceCentered;\n    const Mesh<Dim> face_centered_mesh{extents, basis, quadrature};\n    const auto face_centered_logical_coords =\n        logical_coordinates(face_centered_mesh);\n    const auto face_centered_inertial_coords = moving_mesh_map(\n        element_map(face_centered_logical_coords), time, functions_of_time);\n\n    gsl::at(face_centered_gr_vars, d)\n        .initialize(face_centered_mesh.number_of_grid_points());\n    gsl::at(face_centered_gr_vars, d)\n        .assign_subset(soln.variables(\n            face_centered_inertial_coords, time,\n            typename System::flux_spacetime_variables_tag::tags_list{}));\n  }\n  return face_centered_gr_vars;\n}\n\ndouble test(const size_t num_dg_pts) {\n  using variables_tag = typename System::variables_tag;\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  const ElementId<3> element_id{\n      0, {SegmentId{3, 4}, SegmentId{3, 4}, SegmentId{3, 4}}};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  std::vector<Block<3>> blocks;\n  blocks.emplace_back(Block<3>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D{affine_map, affine_map, affine_map}),\n      0, {}));\n  const auto& block = blocks[0];\n  ElementMap<3, Frame::Grid> element_map{\n      element_id, block.is_time_dependent()\n                      ? block.moving_mesh_logical_to_grid_map().get_clone()\n                      : block.stationary_map().get_to_grid_frame()};\n  const auto element = domain::create_initial_element(\n      element_id, blocks,\n      std::vector<std::array<size_t, 3>>{std::array<size_t, 3>{{3, 3, 3}}});\n\n  const auto moving_mesh_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const grmhd::Solutions::BondiMichel soln{1.0, 5.0, 0.05, 1.4, 2.0};\n\n  const double time = 0.0;\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const auto dg_coords = moving_mesh_map(\n      element_map(logical_coordinates(dg_mesh)), time, functions_of_time);\n\n  // Neighbor data for reconstruction.\n  //\n  // 0. neighbors coords (our logical coords +2)\n  // 1. compute prims from solution\n  // 2. compute prims needed for reconstruction\n  // 3. set neighbor data\n  evolution::dg::subcell::Tags::GhostDataForReconstruction<3>::type\n      neighbor_data{};\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n  for (const Direction<3>& direction : Direction<3>::all_directions()) {\n    auto neighbor_logical_coords = logical_coordinates(subcell_mesh);\n    neighbor_logical_coords.get(direction.dimension()) +=\n        2.0 * direction.sign();\n    auto neighbor_coords = moving_mesh_map(element_map(neighbor_logical_coords),\n                                           time, functions_of_time);\n    const auto neighbor_prims =\n        soln.variables(neighbor_coords, time,\n                       typename System::primitive_variables_tag::tags_list{});\n    Variables<prims_to_reconstruct_tags> prims_to_reconstruct{\n        subcell_mesh.number_of_grid_points()};\n    prims_to_reconstruct.assign_subset(neighbor_prims);\n    get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n        prims_to_reconstruct) =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(neighbor_prims);\n    for (auto& component :\n         get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n             prims_to_reconstruct)) {\n      component *=\n          get(get<hydro::Tags::LorentzFactor<DataVector>>(neighbor_prims));\n    }\n\n    // Slice data so we can add it to the element's neighbor data\n    DataVector neighbor_data_in_direction =\n        evolution::dg::subcell::slice_data(\n            prims_to_reconstruct, subcell_mesh.extents(),\n            grmhd::ValenciaDivClean::fd::MonotonisedCentralPrim{}\n                .ghost_zone_size(),\n            std::unordered_set{direction.opposite()}, 0, {})\n            .at(direction.opposite());\n    const auto key =\n        DirectionalId<3>{direction, *element.neighbors().at(direction).begin()};\n    neighbor_data[key] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[key].neighbor_ghost_data_for_reconstruction() =\n        std::move(neighbor_data_in_direction);\n  }\n\n  Variables<typename System::spacetime_variables_tag::tags_list>\n      dg_spacetime_vars{dg_mesh.number_of_grid_points()};\n  dg_spacetime_vars.assign_subset(soln.variables(\n      dg_coords, time, typename System::spacetime_variables_tag::tags_list{}));\n  Variables<typename System::primitive_variables_tag::tags_list> dg_prim_vars{\n      dg_mesh.number_of_grid_points()};\n  dg_prim_vars.assign_subset(soln.variables(\n      dg_coords, time, typename System::primitive_variables_tag::tags_list{}));\n\n  DirectionMap<3, std::optional<Variables<\n                      tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                 evolution::dg::Tags::NormalCovector<3>>>>>\n      normal_vectors{};\n  for (const auto& direction : Direction<3>::all_directions()) {\n    using inverse_spatial_metric_tag =\n        typename System::inverse_spatial_metric_tag;\n    const Mesh<2> face_mesh = dg_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<3>, tnsr::i<DataVector, 3, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, 3, Frame::Inertial> unnormalized_covector{};\n    const auto element_logical_to_grid_inv_jac =\n        element_map.inv_jacobian(face_logical_coords);\n    const auto grid_to_inertial_inv_jac = moving_mesh_map.inv_jacobian(\n        element_map(face_logical_coords), time, functions_of_time);\n    InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n        element_logical_to_inertial_inv_jac{};\n    for (size_t logical_i = 0; logical_i < 3; ++logical_i) {\n      for (size_t inertial_i = 0; inertial_i < 3; ++inertial_i) {\n        element_logical_to_inertial_inv_jac.get(logical_i, inertial_i) =\n            element_logical_to_grid_inv_jac.get(logical_i, 0) *\n            grid_to_inertial_inv_jac.get(0, inertial_i);\n        for (size_t grid_i = 1; grid_i < 3; ++grid_i) {\n          element_logical_to_inertial_inv_jac.get(logical_i, inertial_i) +=\n              element_logical_to_grid_inv_jac.get(logical_i, grid_i) *\n              grid_to_inertial_inv_jac.get(grid_i, inertial_i);\n        }\n      }\n    }\n    for (size_t i = 0; i < 3; ++i) {\n      unnormalized_covector.get(i) =\n          element_logical_to_inertial_inv_jac.get(direction.dimension(), i);\n    }\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        inverse_spatial_metric_tag,\n        evolution::dg::Actions::detail::NormalVector<3>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n    fields_on_face.assign_subset(\n        soln.variables(moving_mesh_map(element_map(face_logical_coords), time,\n                                       functions_of_time),\n                       time, tmpl::list<inverse_spatial_metric_tag>{}));\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<System>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, moving_mesh_map);\n  }\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Element<3>, domain::Tags::Mesh<3>,\n          evolution::dg::subcell::Tags::Mesh<3>, fd::Tags::Reconstructor,\n          evolution::Tags::BoundaryCorrection,\n          hydro::Tags::GrmhdEquationOfState,\n          typename System::spacetime_variables_tag,\n          typename System::primitive_variables_tag, variables_tag,\n          evolution::dg::subcell::Tags::OnSubcellFaces<\n              typename System::flux_spacetime_variables_tag, 3>,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<3>,\n          Tags::ConstraintDampingParameter, evolution::dg::Tags::MortarData<3>,\n          domain::Tags::MeshVelocity<3>,\n          evolution::dg::Tags::NormalCovectorAndMagnitude<3>,\n          evolution::dg::subcell::Tags::SubcellOptions<3>>,\n      db::AddComputeTags<\n          evolution::dg::subcell::Tags::LogicalCoordinatesCompute<3>>>(\n      element, dg_mesh, subcell_mesh,\n      std::unique_ptr<grmhd::ValenciaDivClean::fd::Reconstructor>{\n          std::make_unique<\n              grmhd::ValenciaDivClean::fd::MonotonisedCentralPrim>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<grmhd::ValenciaDivClean::BoundaryCorrections::Hll>(\n              1.0e-30, 1.0e-8)},\n      soln.equation_of_state().promote_to_3d_eos(), dg_spacetime_vars,\n      dg_prim_vars,\n      typename variables_tag::type{dg_mesh.number_of_grid_points()},\n      face_centered_gr_tags(subcell_mesh, time, element_map, moving_mesh_map,\n                            functions_of_time, soln),\n      neighbor_data, 1.0, evolution::dg::Tags::MortarData<3>::type{},\n      std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>{}, normal_vectors,\n      evolution::dg::subcell::SubcellOptions{\n          4.0, 1_st, 1.0e-3, 1.0e-4, false, false,\n          evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n          std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1});\n  db::mutate_apply<ConservativeFromPrimitive>(make_not_null(&box));\n\n  std::vector<DirectionalId<3>> mortars_to_reconstruct_to{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    mortars_to_reconstruct_to.emplace_back(\n        DirectionalId<3>{direction, *neighbors.begin()});\n  }\n\n  const auto all_packaged_data =\n      subcell::NeighborPackagedData::apply(box, mortars_to_reconstruct_to);\n\n  // Parse out evolved vars, since those are easiest to check for correctness,\n  // then return absolute difference between analytic and reconstructed values.\n  DirectionalIdMap<3, typename variables_tag::type> evolved_vars_errors{};\n  double max_rel_error = 0.0;\n  for (const auto& [direction_and_id, data] : all_packaged_data) {\n    const auto& direction = direction_and_id.direction();\n    using dg_package_field_tags = typename grmhd::ValenciaDivClean::\n        BoundaryCorrections::Hll::dg_package_field_tags;\n    const Mesh<2> face_mesh = dg_mesh.slice_away(direction.dimension());\n    Variables<dg_package_field_tags> packaged_data{\n        face_mesh.number_of_grid_points()};\n    std::copy(data.begin(), data.end(), packaged_data.data());\n\n    auto sliced_vars = data_on_slice(\n        db::get<variables_tag>(box), dg_mesh.extents(), direction.dimension(),\n        direction.side() == Side::Upper\n            ? dg_mesh.extents(direction.dimension()) - 1\n            : 0);\n\n    tmpl::for_each<typename variables_tag::type::tags_list>(\n        [&sliced_vars, &max_rel_error, &packaged_data](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          auto& sliced_tensor = get<tag>(sliced_vars);\n          const auto& packaged_data_tensor = get<tag>(packaged_data);\n          for (size_t tensor_index = 0; tensor_index < sliced_tensor.size();\n               ++tensor_index) {\n            max_rel_error = std::max(\n                max_rel_error,\n                max(abs(sliced_tensor[tensor_index] -\n                        packaged_data_tensor[tensor_index])) /\n                    std::max({max(abs(sliced_tensor[tensor_index])),\n                              max(abs(packaged_data_tensor[tensor_index])),\n                              1.0e-17}));\n          }\n        });\n  }\n  return max_rel_error;\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.Subcell.NeighborPackagedData\",\n    \"[Unit][Evolution]\") {\n  // This tests sets up a cube [2,3]^3 in a Bondi-Michel spacetime and verifies\n  // that the difference between the reconstructed evolved variables and the\n  // sliced (exact on LGL grid) evolved variables on the interfaces decreases.\n  const double error_4 = test(4);\n  const double error_8 = test(8);\n  CHECK(error_4 > error_8);\n  // Check that the error is \"reasonably small\"\n  CHECK(error_8 < 1.0e-5);\n}\n}  // namespace\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_PrimitiveGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimitiveGhostData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing prims_to_reconstruct_tags =\n    tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n               hydro::Tags::ElectronFraction<DataVector>,\n               hydro::Tags::Temperature<DataVector>,\n               hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n               hydro::Tags::MagneticField<DataVector, 3>,\n               hydro::Tags::DivergenceCleaningField<DataVector>>;\nusing copied_tags =\n    tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n               hydro::Tags::ElectronFraction<DataVector>,\n               hydro::Tags::Temperature<DataVector>,\n               hydro::Tags::MagneticField<DataVector, 3>,\n               hydro::Tags::DivergenceCleaningField<DataVector>>;\n\nvoid test_primitive_ghost_data_on_subcells(\n    const gsl::not_null<std::mt19937*> gen,\n    const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const auto prims =\n      make_with_random_values<Variables<hydro::grmhd_tags<DataVector>>>(\n          gen, dist, subcell_mesh.number_of_grid_points());\n\n  auto box = db::create<\n      db::AddSimpleTags<::Tags::Variables<hydro::grmhd_tags<DataVector>>>>(\n      prims);\n  DataVector recons_prims_rdmp = db::mutate_apply<\n      grmhd::ValenciaDivClean::subcell::PrimitiveGhostVariables>(\n      make_not_null(&box), 2_st);\n  const Variables<prims_to_reconstruct_tags> recons_prims{\n      recons_prims_rdmp.data(), recons_prims_rdmp.size() - 2};\n  tmpl::for_each<copied_tags>([&prims, &recons_prims](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK_ITERABLE_APPROX(get<tag>(recons_prims), get<tag>(prims));\n  });\n  auto lorentz_factor_times_v_I =\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prims);\n  for (auto& component : lorentz_factor_times_v_I) {\n    component *= get(get<hydro::Tags::LorentzFactor<DataVector>>(prims));\n  }\n  CHECK_ITERABLE_APPROX(\n      (get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n          recons_prims)),\n      lorentz_factor_times_v_I);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.Subcell.PrimitiveGhostData\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.0, 1.0);\n  test_primitive_ghost_data_on_subcells(make_not_null(&gen),\n                                        make_not_null(&dist));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_PrimsAfterRollback.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/PrimsAfterRollback.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace {\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist,\n          const bool did_rollback) {\n  const Mesh<3> dg_mesh{4, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using cons_tag = typename grmhd::ValenciaDivClean::System::variables_tag;\n  using prim_tag =\n      typename grmhd::ValenciaDivClean::System::primitive_variables_tag;\n  using ConsVars = typename cons_tag::type;\n  using PrimVars = typename prim_tag::type;\n\n  const size_t subcell_num_pts = subcell_mesh.number_of_grid_points();\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{subcell_num_pts, 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = 1.0 + 0.01 * i;\n  }\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric{subcell_num_pts,\n                                                              0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    inv_spatial_metric.get(i, i) = 1.0 / spatial_metric.get(i, i);\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      sqrt(get(determinant(spatial_metric)))};\n\n  std::unique_ptr<EquationsOfState::EquationOfState<true, 1>> eos =\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(1.4, 5.0 / 3.0);\n\n  // Compute the conservatives on the FD grid by first computing the primitives\n  // on the FD grid, then compute the conservatives from the primitives.\n  auto subcell_prims = make_with_random_values<PrimVars>(\n      gen, dist, subcell_mesh.number_of_grid_points());\n  PrimVars dg_prims{};\n  ConsVars subcell_cons{};\n  if (did_rollback) {\n    subcell_cons.initialize(subcell_mesh.number_of_grid_points());\n    get<hydro::Tags::Pressure<DataVector>>(subcell_prims) =\n        eos->pressure_from_density(\n            get<hydro::Tags::RestMassDensity<DataVector>>(subcell_prims));\n    get<hydro::Tags::SpecificInternalEnergy<DataVector>>(subcell_prims) =\n        eos->specific_internal_energy_from_density(\n            get<hydro::Tags::RestMassDensity<DataVector>>(subcell_prims));\n    {\n      const auto& spatial_velocity =\n          get<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>(\n              subcell_prims);\n      get(get<hydro::Tags::LorentzFactor<DataVector>>(subcell_prims)) =\n          1.0 / sqrt(1.0 - get(dot_product(spatial_velocity, spatial_velocity,\n                                           spatial_metric)));\n    }\n    ConservativeFromPrimitive::apply(\n        make_not_null(&get<Tags::TildeD>(subcell_cons)),\n        make_not_null(&get<Tags::TildeYe>(subcell_cons)),\n        make_not_null(&get<Tags::TildeTau>(subcell_cons)),\n        make_not_null(&get<Tags::TildeS<Frame::Inertial>>(subcell_cons)),\n        make_not_null(&get<Tags::TildeB<Frame::Inertial>>(subcell_cons)),\n        make_not_null(&get<Tags::TildePhi>(subcell_cons)),\n        get<hydro::Tags::RestMassDensity<DataVector>>(subcell_prims),\n        get<hydro::Tags::ElectronFraction<DataVector>>(subcell_prims),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(subcell_prims),\n        get<hydro::Tags::Pressure<DataVector>>(subcell_prims),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>(\n            subcell_prims),\n        get<hydro::Tags::LorentzFactor<DataVector>>(subcell_prims),\n        get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(\n            subcell_prims),\n        sqrt_det_spatial_metric, spatial_metric,\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(subcell_prims));\n    dg_prims = evolution::dg::subcell::fd::reconstruct(\n        subcell_prims, dg_mesh, subcell_mesh.extents(),\n        evolution::dg::subcell::fd::ReconstructionMethod::AllDimsAtOnce);\n  }\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  // The DG prims are used as an initial guess so we need to provide them\n  auto box = db::create<db::AddSimpleTags<\n      evolution::dg::subcell::Tags::DidRollback, cons_tag, prim_tag,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>, ::domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>, hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>>(\n      did_rollback, subcell_cons, dg_prims, spatial_metric, inv_spatial_metric,\n      sqrt_det_spatial_metric, dg_mesh, subcell_mesh, eos->promote_to_3d_eos(),\n      primitive_from_conservative_options);\n\n  using recovery_schemes = tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n  db::mutate_apply<subcell::PrimsAfterRollback<recovery_schemes>>(\n      make_not_null(&box));\n\n  if (did_rollback) {\n    REQUIRE(db::get<prim_tag>(box).number_of_grid_points() == subcell_num_pts);\n    PrimVars expected_subcell_prims = subcell_prims;\n    get(get<hydro::Tags::Pressure<DataVector>>(expected_subcell_prims)) =\n        evolution::dg::subcell::fd::project(\n            get(get<hydro::Tags::Pressure<DataVector>>(dg_prims)), dg_mesh,\n            subcell_mesh.extents());\n    PrimitiveFromConservative<recovery_schemes>::apply(\n        make_not_null(&get<hydro::Tags::RestMassDensity<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::ElectronFraction<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::SpecificInternalEnergy<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::SpatialVelocity<DataVector, 3>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::MagneticField<DataVector, 3>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::DivergenceCleaningField<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(&get<hydro::Tags::LorentzFactor<DataVector>>(\n            expected_subcell_prims)),\n        make_not_null(\n            &get<hydro::Tags::Pressure<DataVector>>(expected_subcell_prims)),\n        make_not_null(\n            &get<hydro::Tags::Temperature<DataVector>>(expected_subcell_prims)),\n        get<grmhd::ValenciaDivClean::Tags::TildeD>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildeYe>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildeTau>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(box),\n        get<grmhd::ValenciaDivClean::Tags::TildePhi>(box), spatial_metric,\n        inv_spatial_metric, sqrt_det_spatial_metric,\n        db::get<hydro::Tags::GrmhdEquationOfState>(box),\n        primitive_from_conservative_options);\n    CHECK_VARIABLES_APPROX(db::get<prim_tag>(box), expected_subcell_prims);\n  } else {\n    CHECK(db::get<prim_tag>(box).number_of_grid_points() == 0);\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.Subcell.PrimsAfterRollback\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  // Use a small range of random values since we need recovery to succeed and we\n  // also reconstruct to the DG grid from the FD grid and need to maintain a\n  // somewhat reasonable state on both grids.\n  std::uniform_real_distribution<> dist(0.5, 0.50005);\n  for (const bool did_rollback : {true, false}) {\n    test(make_not_null(&gen), make_not_null(&dist), did_rollback);\n  }\n}\n}  // namespace\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_ResizeAndComputePrimitives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/ResizeAndComputePrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace {\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist,\n          const evolution::dg::subcell::ActiveGrid active_grid,\n          const bool start_on_dg) {\n  CAPTURE(active_grid);\n  CAPTURE(start_on_dg);\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using cons_tag = typename grmhd::ValenciaDivClean::System::variables_tag;\n  using prim_tag =\n      typename grmhd::ValenciaDivClean::System::primitive_variables_tag;\n  using ConsVars = typename cons_tag::type;\n  using PrimVars = typename prim_tag::type;\n\n  const size_t active_num_pts =\n      active_grid == evolution::dg::subcell::ActiveGrid::Dg\n          ? dg_mesh.number_of_grid_points()\n          : subcell_mesh.number_of_grid_points();\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{active_num_pts, 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = 1.0 + 0.01 * i;\n  }\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric{active_num_pts,\n                                                              0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    inv_spatial_metric.get(i, i) = 1.0 / spatial_metric.get(i, i);\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      sqrt(get(determinant(spatial_metric)))};\n\n  const auto eos = EquationsOfState::Barotropic3D{\n      EquationsOfState::PolytropicFluid<true>{\n          1.4,\n          5.0 / 3.0}}.get_clone();\n  auto prim_vars = make_with_random_values<PrimVars>(\n      gen, dist,\n      start_on_dg ? dg_mesh.number_of_grid_points()\n                  : subcell_mesh.number_of_grid_points());\n  get<hydro::Tags::Pressure<DataVector>>(prim_vars) =\n      eos->pressure_from_density_and_temperature(\n          get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n          Scalar<DataVector>{}, Scalar<DataVector>{});\n  get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prim_vars) =\n      eos->specific_internal_energy_from_density_and_temperature(\n          get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n          Scalar<DataVector>{}, Scalar<DataVector>{});\n  {\n    const auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>(\n            prim_vars);\n    tnsr::ii<DataVector, 3, Frame::Inertial> subcell_spatial_metric{\n        start_on_dg ? dg_mesh.number_of_grid_points()\n                    : subcell_mesh.number_of_grid_points(),\n        0.0};\n    for (size_t i = 0; i < 3; ++i) {\n      subcell_spatial_metric.get(i, i) = 1.0 + 0.01 * i;\n    }\n    get(get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars)) =\n        1.0 / sqrt(1.0 - get(dot_product(spatial_velocity, spatial_velocity,\n                                         subcell_spatial_metric)));\n  }\n  ConsVars cons_vars{};\n  const auto compute_cons = [&cons_vars, &spatial_metric,\n                             &sqrt_det_spatial_metric](const auto& prims) {\n    cons_vars.initialize(prims.number_of_grid_points());\n    ConservativeFromPrimitive::apply(\n        make_not_null(&get<Tags::TildeD>(cons_vars)),\n        make_not_null(&get<Tags::TildeYe>(cons_vars)),\n        make_not_null(&get<Tags::TildeTau>(cons_vars)),\n        make_not_null(&get<Tags::TildeS<Frame::Inertial>>(cons_vars)),\n        make_not_null(&get<Tags::TildeB<Frame::Inertial>>(cons_vars)),\n        make_not_null(&get<Tags::TildePhi>(cons_vars)),\n        get<hydro::Tags::RestMassDensity<DataVector>>(prims),\n        get<hydro::Tags::ElectronFraction<DataVector>>(prims),\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prims),\n        get<hydro::Tags::Pressure<DataVector>>(prims),\n        get<hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>>(\n            prims),\n        get<hydro::Tags::LorentzFactor<DataVector>>(prims),\n        get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(prims),\n        sqrt_det_spatial_metric, spatial_metric,\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(prims));\n  };\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    auto dg_prims = prim_vars;\n    if (not start_on_dg) {\n      dg_prims = evolution::dg::subcell::fd::reconstruct(\n          prim_vars, dg_mesh, subcell_mesh.extents(),\n          evolution::dg::subcell::fd ::ReconstructionMethod::AllDimsAtOnce);\n    }\n    compute_cons(dg_prims);\n  } else {\n    compute_cons(prim_vars);\n  }\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  auto box = db::create<db::AddSimpleTags<\n      evolution::dg::subcell::Tags::ActiveGrid, cons_tag, prim_tag,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>, ::domain::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::Mesh<3>, hydro::Tags::GrmhdEquationOfState,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>>(\n      active_grid, cons_vars, prim_vars, spatial_metric, inv_spatial_metric,\n      sqrt_det_spatial_metric, dg_mesh, subcell_mesh, eos->get_clone(),\n      primitive_from_conservative_options);\n\n  using recovery_schemes = tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin,\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>;\n  db::mutate_apply<grmhd::ValenciaDivClean::subcell::ResizeAndComputePrims<\n      recovery_schemes>>(make_not_null(&box));\n\n  REQUIRE(db::get<prim_tag>(box).number_of_grid_points() == active_num_pts);\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    prim_vars.initialize(cons_vars.number_of_grid_points());\n    grmhd::ValenciaDivClean::PrimitiveFromConservative<recovery_schemes>::apply(\n        make_not_null(\n            &get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::SpatialVelocity<DataVector, 3>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::MagneticField<DataVector, 3>>(prim_vars)),\n        make_not_null(\n            &get<hydro::Tags::DivergenceCleaningField<DataVector>>(prim_vars)),\n        make_not_null(&get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars)),\n        make_not_null(&get<hydro::Tags::Pressure<DataVector>>(prim_vars)),\n        make_not_null(&get<hydro::Tags::Temperature<DataVector>>(prim_vars)),\n        get<grmhd::ValenciaDivClean::Tags::TildeD>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildeYe>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildeTau>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(cons_vars),\n        get<grmhd::ValenciaDivClean::Tags::TildePhi>(cons_vars), spatial_metric,\n        inv_spatial_metric, sqrt_det_spatial_metric,\n        db::get<hydro::Tags::GrmhdEquationOfState>(box),\n        primitive_from_conservative_options);\n  }\n  CHECK_VARIABLES_APPROX(db::get<prim_tag>(box), prim_vars);\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.Subcell.ResizeAndComputePrims\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  // Use a small range of random values since we need recovery to succeed and we\n  // also reconstruct to the DG grid from the FD grid and need to maintain a\n  // somewhat reasonable state on both grids.\n  std::uniform_real_distribution<> dist(0.5, 0.505);\n  for (const auto active_grid : {evolution::dg::subcell::ActiveGrid::Dg,\n                                 evolution::dg::subcell::ActiveGrid::Subcell}) {\n    test(make_not_null(&gen), make_not_null(&dist), active_grid, false);\n    if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n      test(make_not_null(&gen), make_not_null(&dist), active_grid, true);\n    }\n  }\n}\n}  // namespace\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.Subcell.SetInitialRdmpData\",\n    \"[Unit][Evolution]\") {\n  using ConsVars =\n      typename grmhd::ValenciaDivClean::System::variables_tag::type;\n\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  ConsVars dg_vars{dg_mesh.number_of_grid_points(), 1.0};\n\n  // While the code is supposed to be used on the subcells, that doesn't\n  // actually matter.\n  const auto subcell_vars = evolution::dg::subcell::fd::project(\n      dg_vars, dg_mesh, subcell_mesh.extents());\n  evolution::dg::subcell::RdmpTciData rdmp_data{};\n  grmhd::ValenciaDivClean::subcell::SetInitialRdmpData::apply(\n      make_not_null(&rdmp_data),\n      get<grmhd::ValenciaDivClean::Tags::TildeD>(dg_vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeYe>(dg_vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeTau>(dg_vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeB<>>(dg_vars),\n      evolution::dg::subcell::ActiveGrid::Dg, dg_mesh, subcell_mesh);\n  const auto& dg_tilde_d = get<grmhd::ValenciaDivClean::Tags::TildeD>(dg_vars);\n  const auto& dg_tilde_ye =\n      get<grmhd::ValenciaDivClean::Tags::TildeYe>(dg_vars);\n  const auto& dg_tilde_tau =\n      get<grmhd::ValenciaDivClean::Tags::TildeTau>(dg_vars);\n  const auto dg_tilde_b_magnitude =\n      magnitude(get<grmhd::ValenciaDivClean::Tags::TildeB<>>(dg_vars));\n  const auto& subcell_tilde_d =\n      get<grmhd::ValenciaDivClean::Tags::TildeD>(subcell_vars);\n  const auto& subcell_tilde_ye =\n      get<grmhd::ValenciaDivClean::Tags::TildeYe>(subcell_vars);\n  const auto& subcell_tilde_tau =\n      get<grmhd::ValenciaDivClean::Tags::TildeTau>(subcell_vars);\n  const auto projected_subcell_tilde_b_magnitude =\n      Scalar<DataVector>(evolution::dg::subcell::fd::project(\n          get(dg_tilde_b_magnitude), dg_mesh, subcell_mesh.extents()));\n\n  evolution::dg::subcell::RdmpTciData expected_dg_rdmp_data{};\n  using std::max;\n  using std::min;\n  expected_dg_rdmp_data.max_variables_values =\n      DataVector{max(max(get(dg_tilde_d)), max(get(subcell_tilde_d))),\n                 max(max(get(dg_tilde_ye)), max(get(subcell_tilde_ye))),\n                 max(max(get(dg_tilde_tau)), max(get(subcell_tilde_tau))),\n                 max(max(get(dg_tilde_b_magnitude)),\n                     max(get(projected_subcell_tilde_b_magnitude)))};\n  expected_dg_rdmp_data.min_variables_values =\n      DataVector{min(min(get(dg_tilde_d)), min(get(subcell_tilde_d))),\n                 min(min(get(dg_tilde_ye)), min(get(subcell_tilde_ye))),\n                 min(min(get(dg_tilde_tau)), min(get(subcell_tilde_tau))),\n                 min(min(get(dg_tilde_b_magnitude)),\n                     min(get(projected_subcell_tilde_b_magnitude)))};\n  CHECK(rdmp_data == expected_dg_rdmp_data);\n\n  grmhd::ValenciaDivClean::subcell::SetInitialRdmpData::apply(\n      make_not_null(&rdmp_data),\n      get<grmhd::ValenciaDivClean::Tags::TildeD>(subcell_vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeYe>(subcell_vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeTau>(subcell_vars),\n      get<grmhd::ValenciaDivClean::Tags::TildeB<>>(subcell_vars),\n      evolution::dg::subcell::ActiveGrid::Subcell, dg_mesh, subcell_mesh);\n\n  const auto subcell_tilde_b_magnitude =\n      magnitude(get<grmhd::ValenciaDivClean::Tags::TildeB<>>(subcell_vars));\n  evolution::dg::subcell::RdmpTciData expected_subcell_rdmp_data{};\n  expected_subcell_rdmp_data.max_variables_values = DataVector{\n      max(get(subcell_tilde_d)), max(get(subcell_tilde_ye)),\n      max(get(subcell_tilde_tau)), max(get(subcell_tilde_b_magnitude))};\n  expected_subcell_rdmp_data.min_variables_values = DataVector{\n      min(get(subcell_tilde_d)), min(get(subcell_tilde_ye)),\n      min(get(subcell_tilde_tau)), min(get(subcell_tilde_b_magnitude))};\n  CHECK(rdmp_data == expected_subcell_rdmp_data);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_SwapGrTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/SwapGrTags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ValenciaDivClean.Subcell.SwapGrTags\",\n                  \"[Unit][Evolution]\") {\n  const Mesh<3> dg_mesh{5, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  const grmhd::ValenciaDivClean::System::spacetime_variables_tag::type\n      active_gr_vars(dg_mesh.number_of_grid_points());\n  const evolution::dg::subcell::Tags::Inactive<\n      typename grmhd::ValenciaDivClean::System::spacetime_variables_tag>::type\n      inactive_gr_vars(subcell_mesh.number_of_grid_points());\n\n  auto box = db::create<db::AddSimpleTags<\n      grmhd::ValenciaDivClean::System::spacetime_variables_tag,\n      evolution::dg::subcell::Tags::Inactive<\n          grmhd::ValenciaDivClean::System::spacetime_variables_tag>,\n      domain::Tags::Mesh<3>, evolution::dg::subcell::Tags::Mesh<3>,\n      evolution::dg::subcell::Tags::ActiveGrid>>(\n      active_gr_vars, inactive_gr_vars, dg_mesh, subcell_mesh,\n      evolution::dg::subcell::ActiveGrid::Dg);\n\n  for (const auto active_grid : {evolution::dg::subcell::ActiveGrid::Dg,\n                                 evolution::dg::subcell::ActiveGrid::Subcell,\n                                 evolution::dg::subcell::ActiveGrid::Dg}) {\n    db::mutate<evolution::dg::subcell::Tags::ActiveGrid>(\n        [&active_grid](const auto active_grid_ptr) {\n          *active_grid_ptr = active_grid;\n        },\n        make_not_null(&box));\n\n    db::mutate_apply<grmhd::ValenciaDivClean::subcell::SwapGrTags>(\n        make_not_null(&box));\n\n    if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n      CHECK(\n          db::get<grmhd::ValenciaDivClean::System::spacetime_variables_tag>(box)\n              .number_of_grid_points() == dg_mesh.number_of_grid_points());\n      CHECK(db::get<evolution::dg::subcell::Tags::Inactive<\n                grmhd::ValenciaDivClean::System::spacetime_variables_tag>>(box)\n                .number_of_grid_points() ==\n            subcell_mesh.number_of_grid_points());\n    } else {\n      CHECK(\n          db::get<grmhd::ValenciaDivClean::System::spacetime_variables_tag>(box)\n              .number_of_grid_points() == subcell_mesh.number_of_grid_points());\n      CHECK(db::get<evolution::dg::subcell::Tags::Inactive<\n                grmhd::ValenciaDivClean::System::spacetime_variables_tag>>(box)\n                .number_of_grid_points() == dg_mesh.number_of_grid_points());\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnDgGrid.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\nnamespace {\nenum class TestThis {\n  AllGood,\n  SmallTildeD,\n  InAtmosphere,\n  TildeB2TooBig,\n  PrimRecoveryFailed,\n  PerssonTildeD,\n  PerssonPressure,\n  PerssonTildeB,\n  NegativeTildeDSubcell,\n  NegativeTildeTauSubcell,\n  NegativeTildeTau,\n  RdmpTildeD,\n  RdmpTildeTau,\n  RdmpMagnitudeTildeB\n};\n\nvoid test(const TestThis test_this, const int expected_tci_status,\n          const bool element_stays_on_dg) {\n  CAPTURE(test_this);\n  CAPTURE(expected_tci_status);\n  CAPTURE(element_stays_on_dg);\n  const EquationsOfState::Barotropic3D eos{\n      EquationsOfState::PolytropicFluid<true>{100.0, 2.0}};\n  const Mesh<3> mesh{6, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(mesh);\n  using ConsVars =\n      typename grmhd::ValenciaDivClean::System::variables_tag::type;\n  using PrimVars = Variables<hydro::grmhd_tags<DataVector>>;\n\n  const double persson_exponent = 4.0;\n  PrimVars prim_vars{mesh.number_of_grid_points(), 0.0};\n  get(get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars)) = 1.0;\n  get(get<hydro::Tags::ElectronFraction<DataVector>>(prim_vars)) = 0.1;\n  if (test_this == TestThis::InAtmosphere) {\n    get(get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars)) = 1.0e-12;\n  }\n  get<hydro::Tags::SpecificInternalEnergy<DataVector>>(prim_vars) =\n      eos.specific_internal_energy_from_density_and_temperature(\n          get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n          Scalar<DataVector>{}, Scalar<DataVector>{});\n  get(get<hydro::Tags::LorentzFactor<DataVector>>(prim_vars)) = 1.0;\n  get<hydro::Tags::Pressure<DataVector>>(prim_vars) =\n      eos.pressure_from_density_and_temperature(\n          get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars),\n          Scalar<DataVector>{}, Scalar<DataVector>{});\n  // set magnetic field to tiny but non-zero value\n  for (size_t i = 0; i < 3; ++i) {\n    get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(prim_vars)\n        .get(i) = 1.0e-50;\n  }\n  // Tracking max field for check later\n  double max_B_field = 1.0e-50;\n  // Just use flat space since none of the TCI checks really depend on the\n  // spacetime variables.\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric{\n      mesh.number_of_grid_points(), 0.0};\n  tnsr::II<DataVector, 3, Frame::Inertial> inv_spatial_metric{\n      mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = inv_spatial_metric.get(i, i) = 1.0;\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{mesh.number_of_grid_points(),\n                                                   1.0};\n\n  const grmhd::ValenciaDivClean::subcell::TciOptions tci_options{\n      1.0e-20,\n      1.e-3,\n      1.0e-40,\n      1.1e-12,\n      1.0e-12,\n      test_this == TestThis::PerssonTildeB ? std::optional<double>{1.0e-2}\n                                           : std::nullopt};\n\n  const evolution::dg::subcell::SubcellOptions subcell_options{\n      persson_exponent,\n      1_st,\n      1.0e-60,  // Tiny value because the magnetic field is so small\n      1.0e-4,\n      false,\n      false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n      false,\n      std::nullopt,\n      fd::DerivativeOrder::Two,\n      1,\n      1,\n      1};\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Tags::Variables<typename ConsVars::tags_list>,\n      ::Tags::Variables<typename PrimVars::tags_list>, ::domain::Tags::Mesh<3>,\n      ::evolution::dg::subcell::Tags::Mesh<3>,\n      hydro::Tags::GrmhdEquationOfState,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      grmhd::ValenciaDivClean::subcell::Tags::TciOptions,\n      evolution::dg::subcell::Tags::SubcellOptions<3>,\n      evolution::dg::subcell::Tags::DataForRdmpTci,\n      grmhd::ValenciaDivClean::Tags::PrimitiveFromConservativeOptions>>(\n      ConsVars{mesh.number_of_grid_points()}, prim_vars, mesh, subcell_mesh,\n      eos.get_clone(), sqrt_det_spatial_metric, spatial_metric,\n      inv_spatial_metric, tci_options, subcell_options,\n      evolution::dg::subcell::RdmpTciData{},\n      primitive_from_conservative_options);\n\n  db::mutate_apply<grmhd::ValenciaDivClean::ConservativeFromPrimitive>(\n      make_not_null(&box));\n\n  const auto original_rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(prim_vars);\n\n  // set B and Phi to 0 since they should be set by recovery\n  db::mutate<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>,\n             hydro::Tags::DivergenceCleaningField<DataVector>>(\n      [](const auto mag_field_ptr, const auto phi_ptr) {\n        for (size_t i = 0; i < 3; ++i) {\n          mag_field_ptr->get(i) = 0;\n        }\n        get(*phi_ptr) = 0;\n      },\n      make_not_null(&box));\n\n  const size_t point_to_change = mesh.number_of_grid_points() / 2;\n\n  if (test_this == TestThis::AllGood) {\n    // slightly modify D to ensure that after the con2prim, the original density\n    // does not match the new density\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeD>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] *= 1.001;\n        },\n        make_not_null(&box));\n  }\n  if (test_this == TestThis::SmallTildeD) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeD>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] = 1.0e-30;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::InAtmosphere) {\n    // Make sure the PerssonTCI would trigger in the atmosphere to verify that\n    // the reason we didn't mark the cell as troubled is because we're in\n    // atmosphere.\n    db::mutate<hydro::Tags::Pressure<DataVector>>(\n        [point_to_change](const auto pressure_ptr) {\n          get(*pressure_ptr)[point_to_change] *= 1.5;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::TildeB2TooBig) {\n    const double large_B_field_magnitude = 1.0e4;\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeB<Frame::Inertial>>(\n\n        [&large_B_field_magnitude, point_to_change](const auto tilde_b_ptr) {\n          get<0>(*tilde_b_ptr)[point_to_change] = large_B_field_magnitude;\n          get<1>(*tilde_b_ptr)[point_to_change] = large_B_field_magnitude;\n          get<2>(*tilde_b_ptr)[point_to_change] = large_B_field_magnitude;\n        },\n        make_not_null(&box));\n    max_B_field = std::max(max_B_field, large_B_field_magnitude);\n\n  } else if (test_this == TestThis::PrimRecoveryFailed) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeS<Frame::Inertial>>(\n        [point_to_change](const auto tilde_s_ptr) {\n          // Manipulate one of the conserved variable in an (very) inconsistent\n          // way so that primitive recovery fails.\n          get<0>(*tilde_s_ptr)[point_to_change] = 1e3;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonPressure) {\n    db::mutate<hydro::Tags::Pressure<DataVector>>(\n        [point_to_change](const auto pressure_ptr) {\n          get(*pressure_ptr)[point_to_change] *= 2.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeD) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeD>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] *= 2.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeB) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeB<>>(\n\n        [&max_B_field, point_to_change](const auto tilde_b_ptr) {\n          for (size_t i = 0; i < 3; ++i) {\n            tilde_b_ptr->get(i)[point_to_change] = 6.0;\n            max_B_field =\n                std::max(max_B_field, tilde_b_ptr->get(i)[point_to_change]);\n          }\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::NegativeTildeDSubcell) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeD>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] = 1e-200;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::NegativeTildeTauSubcell) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeTau>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] = 1.0e-200;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::NegativeTildeTau) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeTau>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] = -1.0e-20;\n        },\n        make_not_null(&box));\n  }\n\n  // Set the RDMP TCI past data.\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{};\n  const auto magnitude_tilde_b =\n      magnitude(db::get<grmhd::ValenciaDivClean::Tags::TildeB<>>(box));\n\n  past_rdmp_tci_data.max_variables_values = DataVector{\n      max(max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box))),\n          max(evolution::dg::subcell::fd::project(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box)), mesh,\n              subcell_mesh.extents()))),\n      max(max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box))),\n          max(evolution::dg::subcell::fd::project(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box)), mesh,\n              subcell_mesh.extents()))),\n      max(max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box))),\n          max(evolution::dg::subcell::fd::project(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box)), mesh,\n              subcell_mesh.extents()))),\n      max(max(get(magnitude_tilde_b)),\n          max(evolution::dg::subcell::fd::project(get(magnitude_tilde_b), mesh,\n                                                  subcell_mesh.extents())))};\n  past_rdmp_tci_data.min_variables_values = DataVector{\n      min(min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box))),\n          min(evolution::dg::subcell::fd::project(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box)), mesh,\n              subcell_mesh.extents()))),\n      min(min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box))),\n          min(evolution::dg::subcell::fd::project(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box)), mesh,\n              subcell_mesh.extents()))),\n      min(min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box))),\n          min(evolution::dg::subcell::fd::project(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box)), mesh,\n              subcell_mesh.extents()))),\n      min(min(get(magnitude_tilde_b)),\n          min(evolution::dg::subcell::fd::project(get(magnitude_tilde_b), mesh,\n                                                  subcell_mesh.extents())))};\n\n  const evolution::dg::subcell::RdmpTciData expected_rdmp_tci_data =\n      past_rdmp_tci_data;\n\n  // Modify past data if we are expecting an RDMP TCI failure.\n  db::mutate<evolution::dg::subcell::Tags::DataForRdmpTci>(\n      [&past_rdmp_tci_data, &test_this](const auto rdmp_tci_data_ptr) {\n        *rdmp_tci_data_ptr = past_rdmp_tci_data;\n        if (test_this == TestThis::RdmpTildeD) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[0] *= 1.01;\n        } else if (test_this == TestThis::RdmpTildeTau) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[1] *= 1.01;\n        } else if (test_this == TestThis::RdmpMagnitudeTildeB) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[2] *= 1.01;\n        }\n      },\n      make_not_null(&box));\n\n  const std::tuple<int, evolution::dg::subcell::RdmpTciData> result =\n      db::mutate_apply<grmhd::ValenciaDivClean::subcell::TciOnDgGrid<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>>(\n          make_not_null(&box), persson_exponent, element_stays_on_dg);\n\n  CHECK(get<1>(result) == expected_rdmp_tci_data);\n\n  if (test_this == TestThis::AllGood or test_this == TestThis::InAtmosphere) {\n    CHECK_FALSE(get<0>(result));\n    CHECK(db::get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(\n              box) ==\n          get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(\n              prim_vars));\n    CHECK(db::get<hydro::Tags::DivergenceCleaningField<DataVector>>(box) ==\n          get<hydro::Tags::DivergenceCleaningField<DataVector>>(prim_vars));\n    if (test_this == TestThis::AllGood) {\n      // Density should not be the same for an updated Dtilde\n      CHECK_FALSE(db::get<hydro::Tags::RestMassDensity<DataVector>>(box) ==\n                  original_rest_mass_density);\n    }\n  } else {\n    CHECK(get<0>(result) == expected_tci_status);\n    // Ensure data before does not equal data after, if TCI is in trouble\n    // box is output from tciondggrid, prim_vars are initial value\n    if (element_stays_on_dg) {\n      // The updated box primitive variables should have a maximum that match\n      // the original primvars magnetic field x component: 1e-50 for non\n      // B field TCI test cases, 6 for PerssonTildeB, or 1.0e4 for TildeB2TooBig\n      CHECK(max(get<0>(\n                db::get<\n                    hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(\n                    box))) == approx(max_B_field));\n    } else {\n      // If element_stays_on_dg is false, then\n      // the primitives will not update, causing a mismatch between\n      // the updated box values and the original primvars.\n      CHECK(db::get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(\n                box) !=\n            get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(\n                prim_vars));\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ValenciaDivClean.Subcell.TciOnDgGrid\",\n                  \"[Unit][Evolution]\") {\n  for (const auto& element_stays_on_dg : make_array(false, true)) {\n    test(TestThis::AllGood, 0, element_stays_on_dg);\n    test(TestThis::InAtmosphere, 0, element_stays_on_dg);\n    test(TestThis::SmallTildeD, -1, element_stays_on_dg);\n    test(TestThis::NegativeTildeDSubcell, -1, element_stays_on_dg);\n    test(TestThis::NegativeTildeTau, -2, element_stays_on_dg);\n    test(TestThis::NegativeTildeTauSubcell, -2, element_stays_on_dg);\n    test(TestThis::TildeB2TooBig, -3, element_stays_on_dg);\n    test(TestThis::PrimRecoveryFailed, -4, element_stays_on_dg);\n    test(TestThis::PerssonTildeD, -5, element_stays_on_dg);\n    test(TestThis::PerssonPressure, -7, element_stays_on_dg);\n    test(TestThis::PerssonTildeB, -8, element_stays_on_dg);\n    test(TestThis::RdmpTildeD, -9, element_stays_on_dg);\n    test(TestThis::RdmpTildeTau, -10, element_stays_on_dg);\n    test(TestThis::RdmpMagnitudeTildeB, -11, element_stays_on_dg);\n  }  // end for loop over element_stays_on_dg\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOnFdGrid.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nenum class TestThis {\n  AllGood,\n  Atmosphere,\n  NeededFixing,\n  PerssonTildeD,\n  PerssonTildeYe,\n  PerssonPressure,\n  PerssonTildeB,\n  NegativeTildeD,\n  NegativeTildeYe,\n  NegativeTildeTau,\n  RdmpTildeD,\n  RdmpTildeYe,\n  RdmpTildeTau,\n  RdmpMagnitudeTildeB\n};\n\nvoid test(const TestThis test_this, const int expected_tci_status) {\n  const Mesh<3> mesh{6, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(mesh);\n  const double persson_exponent = 5.0;\n  const grmhd::ValenciaDivClean::subcell::TciOptions tci_options{\n      1.0e-12,\n      1.e-3,\n      1.0e-40,\n      1.0e-11,\n      1.0e-12,\n      test_this == TestThis::PerssonTildeB ? std::optional<double>{1.0e-2}\n                                           : std::nullopt};\n\n  const evolution::dg::subcell::SubcellOptions subcell_options{\n      persson_exponent,\n      1_st,\n      1.0e-60,  // Tiny value because the magnetic field is so small\n      1.0e-4,\n      false,\n      false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n      false,\n      std::nullopt,\n      fd::DerivativeOrder::Two,\n      1,\n      1,\n      1};\n\n  auto box = db::create<db::AddSimpleTags<\n      grmhd::ValenciaDivClean::Tags::TildeD,\n      grmhd::ValenciaDivClean::Tags::TildeYe,\n      grmhd::ValenciaDivClean::Tags::TildeTau,\n      grmhd::ValenciaDivClean::Tags::TildeB<>,\n      hydro::Tags::RestMassDensity<DataVector>,\n      hydro::Tags::Pressure<DataVector>,\n      grmhd::ValenciaDivClean::Tags::VariablesNeededFixing,\n      domain::Tags::Mesh<3>, ::evolution::dg::subcell::Tags::Mesh<3>,\n      grmhd::ValenciaDivClean::subcell::Tags::TciOptions,\n      evolution::dg::subcell::Tags::SubcellOptions<3>,\n      evolution::dg::subcell::Tags::DataForRdmpTci>>(\n      Scalar<DataVector>(subcell_mesh.number_of_grid_points(), 1.0),\n      Scalar<DataVector>(subcell_mesh.number_of_grid_points(), 0.1),\n      Scalar<DataVector>(subcell_mesh.number_of_grid_points(), 1.0),\n      tnsr::I<DataVector, 3, Frame::Inertial>(\n          subcell_mesh.number_of_grid_points(), 1.0),\n      Scalar<DataVector>(subcell_mesh.number_of_grid_points(), 1.0),\n      Scalar<DataVector>(subcell_mesh.number_of_grid_points(), 1.0),\n      test_this == TestThis::NeededFixing, mesh, subcell_mesh, tci_options,\n      subcell_options, evolution::dg::subcell::RdmpTciData{});\n\n  const size_t point_to_change = mesh.number_of_grid_points() / 2;\n  if (test_this == TestThis::PerssonPressure) {\n    db::mutate<hydro::Tags::Pressure<DataVector>>(\n        [point_to_change](const auto pressure_ptr) {\n          get(*pressure_ptr)[point_to_change] *= 2.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeD) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeD>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] *= 2.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeYe) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeYe>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] *= 2.0;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::PerssonTildeB) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeB<>>(\n        [point_to_change](const auto tilde_b_ptr) {\n          for (size_t i = 0; i < 3; ++i) {\n            tilde_b_ptr->get(i)[point_to_change] *= 2.0;\n          }\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::NegativeTildeD) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeD>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] = -1.0e-20;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::NegativeTildeYe) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeYe>(\n        [point_to_change](const auto tilde_d_ptr) {\n          get(*tilde_d_ptr)[point_to_change] = -1.0e-20;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::NegativeTildeTau) {\n    db::mutate<grmhd::ValenciaDivClean::Tags::TildeTau>(\n        [point_to_change](const auto tilde_tau_ptr) {\n          get(*tilde_tau_ptr)[point_to_change] = -1.0e-20;\n        },\n        make_not_null(&box));\n  } else if (test_this == TestThis::Atmosphere) {\n    db::mutate<hydro::Tags::RestMassDensity<DataVector>,\n               grmhd::ValenciaDivClean::Tags::VariablesNeededFixing>(\n        [](const auto rest_mass_density_ptr,\n           const auto variables_needed_fixing_ptr) {\n          *variables_needed_fixing_ptr = true;\n          get(*rest_mass_density_ptr) =\n              5.0e-12;  // smaller than atmosphere density but\n                        // bigger than the Min(TildeD) TCI option\n        },\n        make_not_null(&box));\n  }\n\n  // Set the RDMP TCI past data.\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{};\n  const auto magnitude_tilde_b =\n      magnitude(db::get<grmhd::ValenciaDivClean::Tags::TildeB<>>(box));\n\n  past_rdmp_tci_data.max_variables_values = DataVector{\n      max(max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box))),\n          max(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box)), mesh,\n              subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim))),\n      max(max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box))),\n          max(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box)), mesh,\n              subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim))),\n      max(max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box))),\n          max(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box)), mesh,\n              subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim))),\n      max(max(get(magnitude_tilde_b)),\n          max(evolution::dg::subcell::fd::reconstruct(\n              get(magnitude_tilde_b), mesh, subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)))};\n  past_rdmp_tci_data.min_variables_values = DataVector{\n      min(min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box))),\n          min(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box)), mesh,\n              subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim))),\n      min(min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box))),\n          min(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box)), mesh,\n              subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim))),\n      min(min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box))),\n          min(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box)), mesh,\n              subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim))),\n      min(min(get(magnitude_tilde_b)),\n          min(evolution::dg::subcell::fd::reconstruct(\n              get(magnitude_tilde_b), mesh, subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)))};\n\n  evolution::dg::subcell::RdmpTciData expected_rdmp_tci_data{};\n  expected_rdmp_tci_data.max_variables_values = DataVector{\n      max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box))),\n      max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box))),\n      max(get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box))),\n      max(get(magnitude_tilde_b))};\n  expected_rdmp_tci_data.min_variables_values = DataVector{\n      min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeD>(box))),\n      min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeYe>(box))),\n      min(get(db::get<grmhd::ValenciaDivClean::Tags::TildeTau>(box))),\n      min(get(magnitude_tilde_b))};\n\n  // Modify past data if we are expected an RDMP TCI failure.\n  db::mutate<evolution::dg::subcell::Tags::DataForRdmpTci>(\n      [&past_rdmp_tci_data, &test_this](const auto rdmp_tci_data_ptr) {\n        *rdmp_tci_data_ptr = past_rdmp_tci_data;\n        if (test_this == TestThis::RdmpTildeD) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[0] *= 1.01;\n        } else if (test_this == TestThis::RdmpTildeYe) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[1] *= 1.01;\n        } else if (test_this == TestThis::RdmpTildeTau) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[2] *= 1.01;\n        } else if (test_this == TestThis::RdmpMagnitudeTildeB) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[3] *= 1.01;\n        }\n      },\n      make_not_null(&box));\n\n  const std::tuple<int, evolution::dg::subcell::RdmpTciData> result =\n      db::mutate_apply<grmhd::ValenciaDivClean::subcell::TciOnFdGrid>(\n          make_not_null(&box), persson_exponent, false);\n  CHECK(get<1>(result) == expected_rdmp_tci_data);\n\n  if (test_this == TestThis::AllGood) {\n    CHECK_FALSE(get<0>(result));\n  } else {\n    CHECK(get<0>(result) == expected_tci_status);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ValenciaDivClean.Subcell.TciOnFdGrid\",\n                  \"[Unit][Evolution]\") {\n  test(TestThis::AllGood, 0);\n  test(TestThis::Atmosphere, 0);\n  test(TestThis::NegativeTildeD, 1);\n  test(TestThis::NegativeTildeYe, 2);\n  test(TestThis::NegativeTildeTau, 3);\n  test(TestThis::NeededFixing, 4);\n  test(TestThis::PerssonTildeD, 5);\n  test(TestThis::PerssonTildeYe, 6);\n  test(TestThis::PerssonPressure, 7);\n  test(TestThis::RdmpTildeD, 8);\n  test(TestThis::RdmpTildeYe, 9);\n  test(TestThis::RdmpTildeTau, 10);\n  test(TestThis::RdmpMagnitudeTildeB, 11);\n  test(TestThis::PerssonTildeB, 12);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_TciOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TciOptions.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.Subcell.TciOptions\",\n                  \"[Unit][GrMhd]\") {\n  const auto tci_options_from_opts = TestHelpers::test_option_tag<\n      grmhd::ValenciaDivClean::subcell::OptionTags::TciOptions>(\n      \"MinimumValueOfD: 1.0e-18\\n\"\n      \"MinimumValueOfYe: 1.0e-3\\n\"\n      \"MinimumValueOfTildeTau: 1.0e-38\\n\"\n      \"AtmosphereDensity: 1.1e-12\\n\"\n      \"SafetyFactorForB: 1.0e-12\\n\"\n      \"MagneticFieldCutoff: 0.01\\n\");\n  const auto tci_options = serialize_and_deserialize(tci_options_from_opts);\n  CHECK(tci_options.minimum_rest_mass_density_times_lorentz_factor == 1.0e-18);\n  CHECK(tci_options.minimum_tilde_tau == 1.0e-38);\n  CHECK(tci_options.atmosphere_density == 1.1e-12);\n  CHECK(tci_options.minimum_ye == 1.e-3);\n  CHECK(tci_options.safety_factor_for_magnetic_field == 1.0e-12);\n  CHECK(tci_options.magnetic_field_cutoff.value() == 0.01);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <memory>\n#include <optional>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/Tags/CellCenteredFlux.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/ReconstructionOrder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/TagsDomain.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace {\n\n// These solution tag and metavariables are not strictly required for testing\n// subcell time derivative, but needed for compilation since\n// BoundaryConditionGhostData requires this to be in box.\nstruct DummyAnalyticSolutionTag : db::SimpleTag {\n  using type = Solutions::SmoothFlow;\n};\n\nstruct DummyEvolutionMetaVars {\n  struct SubcellOptions {\n    static constexpr bool subcell_enabled_at_external_boundary = false;\n  };\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<BoundaryConditions::BoundaryCondition,\n                   BoundaryConditions::standard_boundary_conditions>,\n        tmpl::pair<evolution::BoundaryCorrection,\n                   BoundaryCorrections::standard_boundary_corrections>>;\n  };\n};\n\ntemplate <size_t Dim, typename... Maps, typename Solution>\nauto face_centered_gr_tags(\n    const Mesh<Dim>& subcell_mesh, const double time,\n    const ElementMap<3, Frame::Grid>& element_map,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>&\n        grid_to_inertial_map,\n    const Solution& soln) {\n  std::array<typename System::flux_spacetime_variables_tag::type, Dim>\n      face_centered_gr_vars{};\n\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto basis = make_array<Dim>(subcell_mesh.basis(0));\n    auto quadrature = make_array<Dim>(subcell_mesh.quadrature(0));\n    auto extents = make_array<Dim>(subcell_mesh.extents(0));\n    gsl::at(extents, d) = subcell_mesh.extents(0) + 1;\n    gsl::at(quadrature, d) = Spectral::Quadrature::FaceCentered;\n    const Mesh<Dim> face_centered_mesh{extents, basis, quadrature};\n    const auto face_centered_logical_coords =\n        logical_coordinates(face_centered_mesh);\n    const auto face_centered_inertial_coords =\n        grid_to_inertial_map(element_map(face_centered_logical_coords));\n\n    gsl::at(face_centered_gr_vars, d)\n        .initialize(face_centered_mesh.number_of_grid_points());\n    gsl::at(face_centered_gr_vars, d)\n        .assign_subset(soln.variables(\n            face_centered_inertial_coords, time,\n            typename System::flux_spacetime_variables_tag::tags_list{}));\n  }\n  return face_centered_gr_vars;\n}\n\nstd::array<double, 5> test(const size_t num_dg_pts,\n                           const ::fd::DerivativeOrder fd_derivative_order,\n                           std::optional<double> expansion_velocity) {\n  using flux_tags =\n      db::wrap_tags_in<::Tags::Flux, typename System::flux_variables,\n                       tmpl::size_t<3>, Frame::Inertial>;\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const Affine affine_map{-1.0, 1.0, 1.0, 15.0};\n  std::vector<Block<3>> blocks;\n  blocks.emplace_back(Block<3>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D{affine_map, affine_map, affine_map}),\n      0, {}));\n  const auto element = domain::create_initial_element(\n      ElementId<3>{0, {SegmentId{2, 2}, SegmentId{2, 2}, SegmentId{2, 2}}},\n      blocks,\n      std::vector<std::array<size_t, 3>>{std::array<size_t, 3>{{3, 3, 3}}});\n  const ElementMap<3, Frame::Grid> element_map{\n      element.id(),\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          Affine3D{affine_map, affine_map, affine_map})};\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<3>{});\n\n  const grmhd::ValenciaDivClean::fd::PositivityPreservingAdaptiveOrderPrim\n      recons{\n          3.8, std::nullopt, 4.0,\n          ::fd::reconstruction::FallbackReconstructorType::MonotonisedCentral,\n          true};\n  REQUIRE((static_cast<int>(fd_derivative_order) < 0 or\n           (static_cast<size_t>(fd_derivative_order) / 2 <=\n            recons.ghost_zone_size())));\n\n  const grmhd::Solutions::BondiMichel soln{1.0, 5.0, 0.05, 1.4, 2.0};\n\n  const double time = 0.0;\n  const Mesh<3> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const size_t num_dg_pts_3d = num_dg_pts * num_dg_pts * num_dg_pts;\n  const auto cell_centered_coords =\n      (*grid_to_inertial_map)(element_map(logical_coordinates(subcell_mesh)));\n  const auto dg_coords =\n      (*grid_to_inertial_map)(element_map(logical_coordinates(dg_mesh)));\n\n  Variables<typename System::spacetime_variables_tag::tags_list>\n      cell_centered_spacetime_vars{subcell_mesh.number_of_grid_points()};\n  cell_centered_spacetime_vars.assign_subset(\n      soln.variables(cell_centered_coords, time,\n                     typename System::spacetime_variables_tag::tags_list{}));\n  Variables<typename System::primitive_variables_tag::tags_list>\n      cell_centered_prim_vars{subcell_mesh.number_of_grid_points()};\n  cell_centered_prim_vars.assign_subset(\n      soln.variables(cell_centered_coords, time,\n                     typename System::primitive_variables_tag::tags_list{}));\n  using variables_tag = typename System::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n\n  // Moving mesh info (if needed)\n  std::optional<tnsr::I<DataVector, 3>> dg_mesh_velocity{};\n  std::optional<tnsr::I<DataVector, 3>> subcell_mesh_velocity{};\n  if (expansion_velocity.has_value()) {\n    dg_mesh_velocity = std::optional<tnsr::I<DataVector, 3>>(\n        tnsr::I<DataVector, 3>(num_dg_pts_3d));\n    for (int i = 0; i < 3; i++) {\n      dg_mesh_velocity.value().get(i) =\n          dg_coords.get(i) * expansion_velocity.value();\n    }\n    subcell_mesh_velocity = std::optional<tnsr::I<DataVector, 3>>(\n        tnsr::I<DataVector, 3>(subcell_mesh.number_of_grid_points()));\n    for (int i = 0; i < 3; i++) {\n      subcell_mesh_velocity.value().get(i) =\n          cell_centered_coords.get(i) * expansion_velocity.value();\n    }\n  }\n  std::optional<Scalar<DataVector>> div_dg_mesh_velocity{};\n  if (expansion_velocity.has_value()) {\n    div_dg_mesh_velocity =\n        std::optional<Scalar<DataVector>>(Scalar<DataVector>(num_dg_pts_3d));\n    div_dg_mesh_velocity.value().get() = 3.0 * expansion_velocity.value();\n  }\n\n  // Neighbor data for reconstruction.\n  //\n  // 0. neighbors coords (our logical coords +2)\n  // 1. compute prims from solution\n  // 2. compute prims needed for reconstruction\n  // 3. set neighbor data\n  evolution::dg::subcell::Tags::GhostDataForReconstruction<3>::type\n      neighbor_data{};\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>>;\n  for (const Direction<3>& direction : Direction<3>::all_directions()) {\n    auto neighbor_logical_coords = logical_coordinates(subcell_mesh);\n    neighbor_logical_coords.get(direction.dimension()) +=\n        2.0 * direction.sign();\n    auto neighbor_coords =\n        (*grid_to_inertial_map)(element_map(neighbor_logical_coords));\n    const auto neighbor_prims =\n        soln.variables(neighbor_coords, time,\n                       typename System::primitive_variables_tag::tags_list{});\n    static constexpr size_t prim_components =\n        Variables<prims_to_reconstruct_tags>::number_of_independent_components;\n    DataVector volume_neighbor_data{\n        (prim_components +\n         ((fd_derivative_order != ::fd::DerivativeOrder::Two)\n              ? Variables<flux_tags>::number_of_independent_components\n              : 0)) *\n            subcell_mesh.number_of_grid_points(),\n        0.0};\n    if (fd_derivative_order != ::fd::DerivativeOrder::Two) {\n      Variables<typename System::spacetime_variables_tag::tags_list>\n          neighbor_cell_centered_spacetime_vars{\n              subcell_mesh.number_of_grid_points()};\n      neighbor_cell_centered_spacetime_vars.assign_subset(soln.variables(\n          neighbor_coords, time,\n          typename System::spacetime_variables_tag::tags_list{}));\n      Variables<typename System::variables_tag::tags_list> neighbor_cons{\n          subcell_mesh.number_of_grid_points()};\n      apply(make_not_null(&neighbor_cons),\n            grmhd::ValenciaDivClean::ConservativeFromPrimitive{},\n            neighbor_cell_centered_spacetime_vars, neighbor_prims);\n\n      Variables<flux_tags> neighbor_fluxes{\n          std::next(\n              volume_neighbor_data.data(),\n              static_cast<std::ptrdiff_t>(\n                  prim_components * subcell_mesh.number_of_grid_points())),\n          Variables<flux_tags>::number_of_independent_components *\n              subcell_mesh.number_of_grid_points()};\n      apply(make_not_null(&neighbor_fluxes),\n            grmhd::ValenciaDivClean::ComputeFluxes{},\n            neighbor_cell_centered_spacetime_vars, neighbor_prims,\n            neighbor_cons);\n      if (expansion_velocity.has_value()) {\n        using evolved_vars_tags = typename System::variables_tag::tags_list;\n        tnsr::I<DataVector, 3> neighbor_mesh_velocity{\n            subcell_mesh.number_of_grid_points()};\n        for (size_t i = 0; i < 3; i++) {\n          neighbor_mesh_velocity.get(i) =\n              neighbor_coords.get(i) * expansion_velocity.value();\n        }\n        tmpl::for_each<evolved_vars_tags>([&neighbor_cons, &neighbor_fluxes,\n                                           &neighbor_mesh_velocity](\n                                              auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          using flux_tag = ::Tags::Flux<tag, tmpl::size_t<3>, Frame::Inertial>;\n          using FluxTensor = typename flux_tag::type;\n          const auto& var = get<tag>(neighbor_cons);\n          auto& flux = get<flux_tag>(neighbor_fluxes);\n          for (size_t storage_index = 0; storage_index < var.size();\n               ++storage_index) {\n            const auto tensor_index = var.get_tensor_index(storage_index);\n            for (size_t i = 0; i < 3; i++) {\n              const auto flux_storage_index =\n                  FluxTensor::get_storage_index(prepend(tensor_index, i));\n              flux[flux_storage_index] -=\n                  var[storage_index] * neighbor_mesh_velocity.get(i);\n            }\n          }\n        });\n      }\n    }\n    Variables<prims_to_reconstruct_tags> prims_to_reconstruct{\n        volume_neighbor_data.data(),\n        prim_components * subcell_mesh.number_of_grid_points()};\n    prims_to_reconstruct.assign_subset(neighbor_prims);\n    get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n        prims_to_reconstruct) =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(neighbor_prims);\n    for (auto& component :\n         get<hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>>(\n             prims_to_reconstruct)) {\n      component *=\n          get(get<hydro::Tags::LorentzFactor<DataVector>>(neighbor_prims));\n    }\n    // Slice data so we can add it to the element's neighbor data\n    DataVector neighbor_data_in_direction =\n        evolution::dg::subcell::slice_data(\n            volume_neighbor_data, subcell_mesh.extents(),\n            recons.ghost_zone_size(), std::unordered_set{direction.opposite()},\n            0, {})\n            .at(direction.opposite());\n    const auto key =\n        DirectionalId<3>{direction, *element.neighbors().at(direction).begin()};\n    neighbor_data[key] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[key].neighbor_ghost_data_for_reconstruction() =\n        neighbor_data_in_direction;\n  }\n\n  // Below are also dummy variables required for compilation due to boundary\n  // condition FD ghost data. Since the element used here for testing has\n  // neighbors in all directions, BoundaryConditionGhostData::apply() is not\n  // actually called so it is okay to leave these variables somewhat poorly\n  // initialized.\n  Domain<3> dummy_domain{};\n  typename evolution::dg::Tags::NormalCovectorAndMagnitude<3>::type\n      dummy_normal_covector_and_magnitude{};\n  const double dummy_time{0.0};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      dummy_functions_of_time{};\n\n  using CellCenteredFluxesTag = evolution::dg::subcell::Tags::CellCenteredFlux<\n      typename System::flux_variables, 3>;\n  typename CellCenteredFluxesTag::type cell_centered_fluxes{};\n  Variables<typename System::variables_tag::tags_list> cell_centered_cons_vars{\n      subcell_mesh.number_of_grid_points()};\n  apply(make_not_null(&cell_centered_cons_vars),\n        grmhd::ValenciaDivClean::ConservativeFromPrimitive{},\n        cell_centered_spacetime_vars, cell_centered_prim_vars);\n  if (fd_derivative_order != ::fd::DerivativeOrder::Two) {\n    cell_centered_fluxes =\n        Variables<flux_tags>{subcell_mesh.number_of_grid_points()};\n    apply(make_not_null(&(cell_centered_fluxes.value())),\n          grmhd::ValenciaDivClean::ComputeFluxes{},\n          cell_centered_spacetime_vars, cell_centered_prim_vars,\n          cell_centered_cons_vars);\n    if (expansion_velocity.has_value()) {\n      using evolved_vars_tags = typename System::variables_tag::tags_list;\n      tmpl::for_each<evolved_vars_tags>([&cell_centered_cons_vars,\n                                         &cell_centered_fluxes,\n                                         &subcell_mesh_velocity](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        using flux_tag = ::Tags::Flux<tag, tmpl::size_t<3>, Frame::Inertial>;\n        using FluxTensor = typename flux_tag::type;\n        const auto& var = get<tag>(cell_centered_cons_vars);\n        auto& flux = get<flux_tag>(cell_centered_fluxes.value());\n        for (size_t storage_index = 0; storage_index < var.size();\n             ++storage_index) {\n          const auto tensor_index = var.get_tensor_index(storage_index);\n          for (size_t i = 0; i < 3; i++) {\n            const auto flux_storage_index =\n                FluxTensor::get_storage_index(prepend(tensor_index, i));\n            flux[flux_storage_index] -=\n                var[storage_index] * subcell_mesh_velocity.value().get(i);\n          }\n        }\n      });\n    }\n  }\n  auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Element<3>, evolution::dg::subcell::Tags::Mesh<3>,\n          domain::Tags::Mesh<3>, fd::Tags::Reconstructor,\n          evolution::Tags::BoundaryCorrection,\n          hydro::Tags::GrmhdEquationOfState,\n          typename System::spacetime_variables_tag,\n          typename System::primitive_variables_tag, dt_variables_tag,\n          variables_tag,\n          evolution::dg::subcell::Tags::OnSubcellFaces<\n              typename System::flux_spacetime_variables_tag, 3>,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<3>,\n          Tags::ConstraintDampingParameter, evolution::dg::Tags::MortarData<3>,\n          domain::Tags::ElementMap<3, Frame::Grid>,\n          domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                      Frame::Inertial>,\n          domain::Tags::Domain<3>,\n          domain::Tags::MeshVelocity<3, Frame::Inertial>,\n          domain::Tags::DivMeshVelocity,\n          evolution::dg::Tags::NormalCovectorAndMagnitude<3>, ::Tags::Time,\n          domain::Tags::FunctionsOfTimeInitialize, DummyAnalyticSolutionTag,\n          Parallel::Tags::MetavariablesImpl<DummyEvolutionMetaVars>,\n          CellCenteredFluxesTag,\n          evolution::dg::subcell::Tags::SubcellOptions<3>,\n          evolution::dg::subcell::Tags::ReconstructionOrder<3>>,\n      db::AddComputeTags<\n          ::domain::Tags::LogicalCoordinates<3>,\n          // Compute tags for Frame::Grid quantities\n          ::domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<3, Frame::Grid>,\n              ::domain::Tags::Coordinates<3, Frame::ElementLogical>>,\n          ::domain::Tags::InverseJacobianCompute<\n              ::domain::Tags::ElementMap<3, Frame::Grid>,\n              ::domain::Tags::Coordinates<3, Frame::ElementLogical>>,\n          // Compute tags for Frame::Inertial quantities\n          ::domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<\n              ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                            Frame::Inertial>>,\n          ::domain::Tags::InertialFromGridCoordinatesCompute<3>,\n          ::domain::Tags::ElementToInertialInverseJacobian<3>,\n          ::domain::Tags::DetInvJacobianCompute<3, Frame::ElementLogical,\n                                                Frame::Inertial>,\n\n          evolution::dg::subcell::Tags::LogicalCoordinatesCompute<3>,\n          ::domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<3, Frame::Grid>,\n              evolution::dg::subcell::Tags::Coordinates<3,\n                                                        Frame::ElementLogical>,\n              evolution::dg::subcell::Tags::Coordinates>,\n          evolution::dg::subcell::Tags::InertialCoordinatesCompute<\n              ::domain::CoordinateMaps::Tags::CoordinateMap<3, Frame::Grid,\n                                                            Frame::Inertial>>,\n          evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGridCompute<\n              ::domain::Tags::ElementMap<3, Frame::Grid>, 3>,\n          evolution::dg::subcell::fd::Tags::\n              DetInverseJacobianLogicalToGridCompute<3>,\n          evolution::dg::subcell::fd::Tags::\n              InverseJacobianLogicalToInertialCompute<\n                  ::domain::CoordinateMaps::Tags::CoordinateMap<\n                      3, Frame::Grid, Frame::Inertial>,\n                  3>,\n          evolution::dg::subcell::fd::Tags::\n              DetInverseJacobianLogicalToInertialCompute<\n                  ::domain::CoordinateMaps::Tags::CoordinateMap<\n                      3, Frame::Grid, Frame::Inertial>,\n                  3>>>(\n      element, subcell_mesh, dg_mesh,\n      std::unique_ptr<grmhd::ValenciaDivClean::fd::Reconstructor>{\n          std::make_unique<std::decay_t<decltype(recons)>>(recons)},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<grmhd::ValenciaDivClean::BoundaryCorrections::Hll>(\n              1.0e-30, 1.0e-8)},\n      soln.equation_of_state().promote_to_3d_eos(),\n      cell_centered_spacetime_vars, cell_centered_prim_vars,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      cell_centered_cons_vars,\n      face_centered_gr_tags(subcell_mesh, time, element_map,\n                            *grid_to_inertial_map, soln),\n      neighbor_data, 1.0, evolution::dg::Tags::MortarData<3>::type{},\n      ElementMap<3, Frame::Grid>{element.id(),\n                                 element_map.block_map().get_clone()},\n      grid_to_inertial_map->get_clone(), std::move(dummy_domain),\n      dg_mesh_velocity, div_dg_mesh_velocity,\n      dummy_normal_covector_and_magnitude, dummy_time,\n      clone_unique_ptrs(dummy_functions_of_time),\n      grmhd::Solutions::SmoothFlow{}, DummyEvolutionMetaVars{},\n      cell_centered_fluxes,\n      evolution::dg::subcell::SubcellOptions{\n          4.0, 1_st, 1.0e-3, 1.0e-4, false, false,\n          evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n          std::nullopt, fd_derivative_order, 1, 1, 1},\n      typename evolution::dg::subcell::Tags::ReconstructionOrder<3>::type{});\n\n  db::mutate_apply<ConservativeFromPrimitive>(make_not_null(&box));\n\n  subcell::TimeDerivative::apply(make_not_null(&box));\n\n  if (static_cast<int>(fd_derivative_order) < 0) {\n    CHECK(db::get<evolution::dg::subcell::Tags::ReconstructionOrder<3>>(box)\n              .has_value());\n  } else {\n    CHECK(not db::get<evolution::dg::subcell::Tags::ReconstructionOrder<3>>(box)\n                  .has_value());\n  }\n\n  // We test that the time derivative converges to zero,\n  // so we remove the expected value of the time derivative for moving meshes\n  Variables<typename System::variables_tag::tags_list>\n      output_minus_expected_dt_cons_vars{subcell_mesh.number_of_grid_points()};\n  const auto& dt_vars = db::get<dt_variables_tag>(box);\n\n  const auto& cell_centered_logical_to_inertial_inv_jacobian = db::get<\n      evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToInertial<3>>(\n      box);\n  tmpl::for_each<typename System::variables_tag::tags_list>(\n      [&box, &subcell_mesh, &expansion_velocity, &cell_centered_coords,\n       &cell_centered_logical_to_inertial_inv_jacobian,\n       &output_minus_expected_dt_cons_vars, &dt_vars](auto cons_var_tag_v) {\n        using cons_var_tag = tmpl::type_from<decltype(cons_var_tag_v)>;\n        const auto& cons_var = get<cons_var_tag>(box);\n        const auto deriv_cons_var =\n            partial_derivative(cons_var, subcell_mesh,\n                               cell_centered_logical_to_inertial_inv_jacobian);\n\n        auto& output_minus_expected_dt_var =\n            get<cons_var_tag>(output_minus_expected_dt_cons_vars);\n        const auto& output_dt_var = get<::Tags::dt<cons_var_tag>>(dt_vars);\n        for (size_t i = 0; i < output_minus_expected_dt_var.size(); ++i) {\n          output_minus_expected_dt_var[i] = output_dt_var[i];\n          if (expansion_velocity.has_value()) {\n            for (size_t j = 0; j < 3; ++j) {\n              const auto deriv_index =\n                  j * output_minus_expected_dt_var.size() + i;\n              output_minus_expected_dt_var[i] -= cell_centered_coords.get(j) *\n                                                 deriv_cons_var[deriv_index] *\n                                                 expansion_velocity.value();\n            }\n          }\n        }\n      });\n\n  return {\n      {max(abs(get(get<Tags::TildeD>(output_minus_expected_dt_cons_vars)))),\n       max(abs(get(get<Tags::TildeYe>(output_minus_expected_dt_cons_vars)))),\n       max(abs(get(get<Tags::TildeTau>(output_minus_expected_dt_cons_vars)))),\n       max(get(\n           magnitude(get<Tags::TildeS<>>(output_minus_expected_dt_cons_vars)))),\n       max(get(magnitude(\n           get<Tags::TildeB<>>(output_minus_expected_dt_cons_vars))))}};\n}\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.Subcell.TimeDerivative\",\n    \"[Unit][Evolution]\") {\n  std::optional<std::array<double, 5>> previous_error_5{};\n  std::optional<std::array<double, 5>> previous_error_6{};\n  std::array<double, 5> second_order_error_5{};\n  std::array<double, 5> second_order_error_6{};\n  std::optional<double> dummy_expansion_velocity{};\n  using DO = ::fd::DerivativeOrder;\n  // Note: All the high order cases are commented out because we don't yet\n  // have support for high-order FD on curved meshes.\n  for (const DO fd_do : {\n           DO::Two  // , DO::Four, DO::Six, DO::Eight, DO::Ten\n       }) {\n    CAPTURE(fd_do);\n    // This tests sets up a cube [2,3]^3 in a Bondi-Michel spacetime and\n    // verifies that the time derivative vanishes. Or, more specifically, that\n    // the time derivative decreases with increasing resolution.\n    const auto five_pts_data = test(5, fd_do, dummy_expansion_velocity);\n    const auto six_pts_data = test(6, fd_do, dummy_expansion_velocity);\n    if (fd_do == DO::Two) {\n      second_order_error_5 = five_pts_data;\n      second_order_error_6 = six_pts_data;\n    }\n\n    for (size_t i = 0; i < five_pts_data.size(); ++i) {\n      CAPTURE(i);\n      CHECK(gsl::at(six_pts_data, i) < gsl::at(five_pts_data, i));\n      // Check that as we increase the order of the FD derivative the error\n      // decreases.\n      if (previous_error_5.has_value()) {\n        CHECK(gsl::at(five_pts_data, i) < gsl::at(previous_error_5.value(), i));\n      }\n      if (previous_error_6.has_value()) {\n        CHECK(gsl::at(six_pts_data, i) < gsl::at(previous_error_6.value(), i));\n      }\n    }\n    previous_error_5 = five_pts_data;\n    previous_error_6 = six_pts_data;\n  }\n\n  // Check that a vanishing mesh velocity does not modify the answer\n  {\n    // This tests sets up a cube [2,3]^3 in a Bondi-Michel spacetime and\n    // verifies that the time derivative is the same when using no mesh\n    // velocity and when using a zero mesh velocity\n    std::optional<double> zero_expansion_velocity(0.0);\n    const auto data_no_mesh_velocity = test(5, DO::Two,  // DO::Four,\n                                            dummy_expansion_velocity);\n    const auto data_mesh_velocity = test(5, DO::Two,  // DO::Four,\n                                         zero_expansion_velocity);\n\n    for (size_t i = 0; i < data_no_mesh_velocity.size(); ++i) {\n      CAPTURE(i);\n      CHECK(gsl::at(data_no_mesh_velocity, i) ==\n            gsl::at(data_mesh_velocity, i));\n    }\n  }\n\n  // Now use an expansion map.\n  previous_error_5 = {};\n  previous_error_6 = {};\n  for (const DO fd_do : {\n           DO::Two  // , DO::Four\n       }) {\n    CAPTURE(fd_do);\n    std::optional<double> expansion_velocity(0.1);\n    // This tests sets up a cube [2,3]^3 in a Bondi-Michel spacetime and\n    // verifies that the difference between correct and expected time\n    // derivative vanishes. Or, more specifically, that\n    // the time derivative decreases with increasing resolution.\n    const auto five_pts_data = test(5, fd_do, expansion_velocity);\n    const auto six_pts_data = test(6, fd_do, expansion_velocity);\n    if (fd_do == DO::Two) {\n      second_order_error_5 = five_pts_data;\n      second_order_error_6 = six_pts_data;\n    }\n\n    for (size_t i = 0; i < five_pts_data.size(); ++i) {\n      CAPTURE(i);\n      CHECK(gsl::at(six_pts_data, i) < gsl::at(five_pts_data, i));\n      // Check that as we increase the order of the FD derivative the error\n      // decreases.\n      if (previous_error_5.has_value()) {\n        CHECK(gsl::at(five_pts_data, i) < gsl::at(previous_error_5.value(), i));\n      }\n      if (previous_error_6.has_value()) {\n        CHECK(gsl::at(six_pts_data, i) < gsl::at(previous_error_6.value(), i));\n      }\n    }\n    previous_error_5 = five_pts_data;\n    previous_error_6 = six_pts_data;\n  }\n\n  // Check the adaptive correction order works.\n  // for (const auto& recon_order : make_array(\n  //          DO::OneHigherThanRecons, DO::OneHigherThanReconsButFiveToFour)) {\n  //   CAPTURE(recon_order);\n  //   const auto five_pts_data = test(5, recon_order,\n  //   dummy_expansion_velocity); const auto six_pts_data = test(6, recon_order,\n  //   dummy_expansion_velocity); for (size_t i = 0; i < five_pts_data.size();\n  //   ++i) {\n  //     CAPTURE(i);\n  //     CHECK(gsl::at(six_pts_data, i) < gsl::at(five_pts_data, i));\n  //     CHECK(gsl::at(five_pts_data, i) < gsl::at(second_order_error_5, i));\n  //     CHECK(gsl::at(six_pts_data, i) < gsl::at(second_order_error_6, i));\n  //   }\n  // }\n}\n}  // namespace\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Characteristics.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\nvoid test_characteristic_speeds(const DataVector& /*used_for_size*/) {\n  //  Arbitrary random numbers can produce a negative radicand in Lambda^\\pm.\n  //  This bound helps to prevent that situation.\n  // const double max_value = 1.0 / sqrt(3);\n  // pypp::check_with_random_values<7>(\n  //     &grmhd::ValenciaDivClean::characteristic_speeds_approximate_mhd<3>,\n  //     \"CharacteristicSpeeds\", \"CharacteristicSpeeds\",\n  //     {{{0.0, 1.0},\n  //       {-1.0, 1.0},\n  //       {-max_value, max_value},\n  //       {0.0, 1.0},\n  //       {0.0, 1.0},\n  //       {0.0, 1.0},\n  //       {-max_value, max_value}}},\n  //     used_for_size);\n}\n\nvoid test_with_normal_along_coordinate_axes(const DataVector& used_for_size) {\n  MAKE_GENERATOR(generator);\n  namespace helper = TestHelpers::hydro;\n  namespace gr_helper = TestHelpers::gr;\n  const auto nn_gen = make_not_null(&generator);\n  const auto rest_mass_density = helper::random_density(nn_gen, used_for_size);\n  const EquationsOfState::PolytropicFluid<true> eos(0.001, 4.0 / 3.0);\n  const auto specific_internal_energy =\n      eos.specific_internal_energy_from_density(rest_mass_density);\n  const auto specific_enthalpy = hydro::relativistic_specific_enthalpy(\n      rest_mass_density, specific_internal_energy,\n      eos.pressure_from_density(rest_mass_density));\n\n  const auto electron_fraction =\n      helper::random_electron_fraction(nn_gen, used_for_size);\n\n  const auto lapse = gr_helper::random_lapse(nn_gen, used_for_size);\n  const auto shift = gr_helper::random_shift<3>(nn_gen, used_for_size);\n  const auto spatial_metric =\n      gr_helper::random_spatial_metric<3>(nn_gen, used_for_size);\n  const auto lorentz_factor =\n      helper::random_lorentz_factor(nn_gen, used_for_size);\n  const auto spatial_velocity =\n      helper::random_velocity(nn_gen, lorentz_factor, spatial_metric);\n  const auto spatial_velocity_squared =\n      dot_product(spatial_velocity, spatial_velocity, spatial_metric);\n\n  const auto magnetic_field = helper::random_magnetic_field(\n      nn_gen, eos.pressure_from_density(rest_mass_density), spatial_metric);\n  const auto magnetic_field_squared =\n      dot_product(magnetic_field, magnetic_field, spatial_metric);\n  const auto magnetic_field_dot_spatial_velocity =\n      dot_product(spatial_velocity, magnetic_field, spatial_metric);\n  const DataVector comoving_magnetic_field_squared =\n      get(magnetic_field_squared) / square(get(lorentz_factor)) +\n      square(get(magnetic_field_dot_spatial_velocity));\n  const Scalar<DataVector> alfven_speed_squared{\n      comoving_magnetic_field_squared /\n      (comoving_magnetic_field_squared +\n       get(rest_mass_density) * get(specific_enthalpy))};\n  const Scalar<DataVector> sound_speed_squared{\n      (get(eos.chi_from_density(rest_mass_density)) +\n       get(eos.kappa_times_p_over_rho_squared_from_density(\n           rest_mass_density))) /\n      get(specific_enthalpy)};\n\n  for (const auto& direction : Direction<3>::all_directions()) {\n    const auto normal = unit_basis_form(\n        direction, determinant_and_inverse(spatial_metric).second);\n\n    const auto& eos_base =\n        static_cast<const EquationsOfState::EquationOfState<true, 1>&>(eos);\n    const Approx custom_approx = Approx::custom().epsilon(1.0e-10);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        grmhd::ValenciaDivClean::characteristic_speeds_approximate_mhd(\n            rest_mass_density, electron_fraction, specific_internal_energy,\n            specific_enthalpy, spatial_velocity, lorentz_factor, magnetic_field,\n            lapse, shift, spatial_metric, normal, eos_base),\n        (pypp::call<std::array<DataVector, 9>>(\n            \"CharacteristicSpeeds\", \"CharacteristicSpeeds\", lapse, shift,\n            spatial_velocity, spatial_velocity_squared, sound_speed_squared,\n            alfven_speed_squared, normal)),\n        custom_approx);\n  }\n}\n\nvoid test_hydro_characteristic_speed(const DataVector& used_for_size) {\n  MAKE_GENERATOR(generator);\n  namespace helper = TestHelpers::hydro;\n  namespace gr_helper = TestHelpers::gr;\n  const auto nn_gen = make_not_null(&generator);\n\n  const auto rest_mass_density = helper::random_density(nn_gen, used_for_size);\n  const auto specific_internal_energy =\n      helper::random_specific_internal_energy(nn_gen, used_for_size);\n  const auto electron_fraction =\n      helper::random_electron_fraction(nn_gen, used_for_size);\n  const auto lorentz_factor =\n      helper::random_lorentz_factor(nn_gen, used_for_size);\n  const auto spatial_metric =\n      gr_helper::random_spatial_metric<3>(nn_gen, used_for_size);\n  const auto spatial_velocity =\n      helper::random_velocity(nn_gen, lorentz_factor, spatial_metric);\n  const auto spatial_velocity_squared =\n      dot_product(spatial_velocity, spatial_velocity, spatial_metric);\n\n  const EquationsOfState::IdealFluid<true> base_eos(4.0 / 3.0);\n  const auto eos_3d = base_eos.promote_to_3d_eos();\n\n  const auto temperature = eos_3d->temperature_from_density_and_energy(\n      rest_mass_density, specific_internal_energy, electron_fraction);\n\n  const auto sound_speed_squared =\n      eos_3d->sound_speed_squared_from_density_and_temperature(\n          rest_mass_density, temperature, electron_fraction);\n  const auto specific_enthalpy = hydro::relativistic_specific_enthalpy(\n      rest_mass_density, specific_internal_energy,\n      eos_3d->pressure_from_density_and_energy(\n          rest_mass_density, specific_internal_energy, electron_fraction));\n\n  for (const auto& direction : Direction<3>::all_directions()) {\n    const auto unit_normal = unit_basis_form(\n        direction, determinant_and_inverse(spatial_metric).second);\n\n    const Approx custom_approx = Approx::custom().epsilon(1.0e-10);\n\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        grmhd::ValenciaDivClean::characteristic_speeds_hydro<3>(\n            spatial_velocity, rest_mass_density, specific_internal_energy,\n            specific_enthalpy, electron_fraction, lorentz_factor, unit_normal,\n            spatial_metric, *eos_3d),\n        (pypp::call<std::array<DataVector, 3>>(\n            \"CharacteristicSpeeds\", \"characteristic_speeds_hydro\",\n            spatial_velocity, spatial_velocity_squared, sound_speed_squared,\n            lorentz_factor, unit_normal)),\n        custom_approx);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.Characteristics\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean\"};\n\n  const DataVector dv(5);\n  test_characteristic_speeds(dv);\n  // Test with aligned normals to check the code works\n  // with vector components being 0.\n  test_with_normal_along_coordinate_axes(dv);\n  test_hydro_characteristic_speed(dv);\n\n  TestHelpers::db::test_compute_tag<\n      grmhd::ValenciaDivClean::Tags::CharacteristicSpeedsCompute>(\n      \"CharacteristicSpeeds\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_ComovingMagneticFieldMagnitude.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComovingMagneticFieldMagnitude.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.ComovingMagneticFieldMagnitude\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean\"};\n\n  pypp::check_with_random_values<1>(\n      &grmhd::ValenciaDivClean::Tags::ComovingMagneticFieldMagnitudeCompute::\n          function,\n      \"ComovingMagneticFieldMagnitude\", {\"comoving_b_magnitude\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_ComputeFluxesFromPrimitives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ComputeFluxesFromPrimitives.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace grmhd::ValenciaDivClean {\nnamespace {\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.ComputeFluxesFromPrimitives\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist(0.0, 1.0);\n  const size_t num_pts = 5;\n\n  using ConservativeTags =\n      typename grmhd::ValenciaDivClean::ConservativeFromPrimitive::return_tags;\n  using ArgumentTags = typename grmhd::ValenciaDivClean::\n      ConservativeFromPrimitive::argument_tags;\n\n  auto flux_vars =\n      make_with_random_values<Variables<ComputeFluxes::return_tags>>(\n          make_not_null(&gen), make_not_null(&dist), num_pts);\n\n  auto conservative_vars = make_with_random_values<Variables<ConservativeTags>>(\n      make_not_null(&gen), make_not_null(&dist), num_pts);\n\n  auto boundary_vars = make_with_random_values<Variables<tmpl::push_back<\n      ArgumentTags, gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>>>>(\n      make_not_null(&gen), make_not_null(&dist), num_pts);\n\n  Variables<ConservativeFromPrimitive::return_tags> expected_conservative{\n      num_pts};\n  Variables<ComputeFluxes::return_tags> expected_fluxes{num_pts};\n  ConservativeFromPrimitive::apply(\n      make_not_null(\n          &get<grmhd::ValenciaDivClean::Tags::TildeD>(expected_conservative)),\n      make_not_null(\n          &get<grmhd::ValenciaDivClean::Tags::TildeYe>(expected_conservative)),\n      make_not_null(\n          &get<grmhd::ValenciaDivClean::Tags::TildeTau>(expected_conservative)),\n      make_not_null(\n          &get<grmhd::ValenciaDivClean::Tags::TildeS<>>(expected_conservative)),\n      make_not_null(\n          &get<grmhd::ValenciaDivClean::Tags::TildeB<>>(expected_conservative)),\n      make_not_null(\n          &get<grmhd::ValenciaDivClean::Tags::TildePhi>(expected_conservative)),\n      get<hydro::Tags::RestMassDensity<DataVector>>(boundary_vars),\n      get<hydro::Tags::ElectronFraction<DataVector>>(boundary_vars),\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(boundary_vars),\n      get<hydro::Tags::Pressure<DataVector>>(boundary_vars),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_vars),\n      get<hydro::Tags::LorentzFactor<DataVector>>(boundary_vars),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(boundary_vars),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(boundary_vars),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_vars),\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(boundary_vars));\n  ComputeFluxes::apply(\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeS<>,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeB<>,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                                      tmpl::size_t<3>, Frame::Inertial>>(\n          expected_fluxes)),\n      get<grmhd::ValenciaDivClean::Tags::TildeD>(expected_conservative),\n      get<grmhd::ValenciaDivClean::Tags::TildeYe>(expected_conservative),\n      get<grmhd::ValenciaDivClean::Tags::TildeTau>(expected_conservative),\n      get<grmhd::ValenciaDivClean::Tags::TildeS<>>(expected_conservative),\n      get<grmhd::ValenciaDivClean::Tags::TildeB<>>(expected_conservative),\n      get<grmhd::ValenciaDivClean::Tags::TildePhi>(expected_conservative),\n      get<gr::Tags::Lapse<DataVector>>(boundary_vars),\n      get<gr::Tags::Shift<DataVector, 3>>(boundary_vars),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(boundary_vars),\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_vars),\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(boundary_vars),\n      get<hydro::Tags::Pressure<DataVector>>(boundary_vars),\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(boundary_vars),\n      get<hydro::Tags::LorentzFactor<DataVector>>(boundary_vars),\n      get<hydro::Tags::MagneticField<DataVector, 3>>(boundary_vars));\n\n  compute_fluxes_from_primitives(make_not_null(&flux_vars), boundary_vars);\n\n  tmpl::for_each<ComputeFluxes::return_tags>(\n      [&expected_fluxes, &flux_vars](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<tag>(flux_vars), get<tag>(expected_fluxes));\n      });\n}\n}  // namespace\n}  // namespace grmhd::ValenciaDivClean\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_ConservativeFromPrimitive.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.ConservativeFromPrimitive\",\n                  \"[Unit][GrMhd]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean\"};\n\n  pypp::check_with_random_values<1>(\n      &grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply,\n      \"ConservativeFromPrimitive\",\n      {\"tilde_d\", \"tilde_ye\", \"tilde_tau\", \"tilde_s\", \"tilde_b\", \"tilde_phi\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_FixConservatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FixConservatives.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/MagneticFieldTreatment.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nvoid test_variable_fixer(\n    const grmhd::ValenciaDivClean::FixConservatives& variable_fixer,\n    const bool enable) {\n  // Call variable fixer at five points\n  // [0]:  tilde_d is too small, should be raised to limit\n  // [1]:  tilde_ye is too small, should be raised to limit\n  // [2]:  tilde_tau is too small, raise to level of needed, which also\n  //       causes tilde_s to be zeroed\n  // [3]:  tilde_S is too big, so it is lowered\n  // [4]:  all values are good, no changes\n\n  Scalar<DataVector> tilde_d{DataVector{2.e-12, 1.0, 1.0, 1.0, 1.0}};\n  // We assume that ye = 0.1\n  Scalar<DataVector> tilde_ye{DataVector{2.e-13, 2.0e-10, 1.0, 1.0, 1.0}};\n  Scalar<DataVector> tilde_tau{DataVector{4.5, 4.5, 1.5, 4.5, 4.5}};\n  auto tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto tilde_b =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  tilde_s.get(0) = DataVector{3.0, 3.0, 0.0, 6.0, 5.0};\n  tilde_b.get(1) = DataVector{2.0, 2.0, 2.0, 2.0, 2.0};\n\n  auto expected_tilde_d = tilde_d;\n  if (enable) {\n    get(expected_tilde_d)[0] = 1.e-12;\n  }\n\n  auto expected_tilde_ye = tilde_ye;\n  if (enable) {\n    get(expected_tilde_ye)[0] = 1.e-13;  // since Y_e = 0.1\n    get(expected_tilde_ye)[1] = 1.e-10;\n  }\n\n  auto expected_tilde_tau = tilde_tau;\n  if (enable) {\n    get(expected_tilde_tau)[2] = 2.0;\n  }\n\n  auto expected_tilde_s = tilde_s;\n  if (enable) {\n    expected_tilde_s.get(0)[3] = sqrt(27.0);\n  }\n\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto sqrt_det_spatial_metric =\n      make_with_value<Scalar<DataVector>>(tilde_d, 1.0);\n  for (size_t d = 0; d < 3; ++d) {\n    spatial_metric.get(d, d) = get(sqrt_det_spatial_metric);\n    inv_spatial_metric.get(d, d) = get(sqrt_det_spatial_metric);\n  }\n\n  CHECK(enable == variable_fixer(&tilde_d, &tilde_ye, &tilde_tau, &tilde_s,\n                                 tilde_b, spatial_metric, inv_spatial_metric,\n                                 sqrt_det_spatial_metric));\n\n  CHECK_ITERABLE_APPROX(tilde_d, expected_tilde_d);\n  CHECK_ITERABLE_APPROX(tilde_ye, expected_tilde_ye);\n  CHECK_ITERABLE_APPROX(tilde_tau, expected_tilde_tau);\n  CHECK_ITERABLE_APPROX(tilde_s, expected_tilde_s);\n}\n\nvoid test_variable_fixer_zero_b_field(\n    const grmhd::ValenciaDivClean::FixConservatives& variable_fixer,\n    const bool enable) {\n  // Call variable fixer with AssumeZero for MagneticField\n  // at two points.\n  // [0] : negative tilde tau, should be raised to zero.\n  // [1] : positive tilde tau, no changes.\n  Scalar<DataVector> tilde_d{DataVector{1.0, 1.0}};\n  // We assume that ye = 0.1\n  Scalar<DataVector> tilde_ye{DataVector{1.0, 1.0}};\n  Scalar<DataVector> tilde_tau{DataVector{-2.0, 4.5}};\n  auto expected_tilde_tau = tilde_tau;\n  if (enable) {\n    get(expected_tilde_tau)[0] = 0.0;\n  }\n  auto tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto tilde_b =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto sqrt_det_spatial_metric =\n      make_with_value<Scalar<DataVector>>(tilde_d, 1.0);\n  for (size_t d = 0; d < 3; ++d) {\n    spatial_metric.get(d, d) = get(sqrt_det_spatial_metric);\n    inv_spatial_metric.get(d, d) = get(sqrt_det_spatial_metric);\n  }\n\n  CHECK(enable == variable_fixer(&tilde_d, &tilde_ye, &tilde_tau, &tilde_s,\n                                 tilde_b, spatial_metric, inv_spatial_metric,\n                                 sqrt_det_spatial_metric));\n  CHECK_ITERABLE_APPROX(tilde_tau, expected_tilde_tau);\n}\n\nvoid run_benchmark(const bool enable) {\n  if (not enable) {\n    return;\n  }\n  const size_t num_points = 512;\n  Scalar<DataVector> tilde_d{DataVector(num_points)};\n  Scalar<DataVector> tilde_ye{DataVector(num_points)};\n  Scalar<DataVector> tilde_tau{DataVector(num_points)};\n  auto tilde_s =\n      make_with_value<tnsr::i<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto tilde_b =\n      make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  for (size_t i = 0; i < num_points; ++i) {\n    if (i % 2 == 0) {\n      // tilde_d is too small, should be raised to limit\n      get(tilde_d)[i] = 2.e-12;\n      get(tilde_ye)[i] = 2.e-13;\n      get(tilde_tau)[i] = 4.5;\n      tilde_s.get(0)[i] = 3.0;\n      tilde_b.get(0)[i] = 2.0e-6;\n    } else {\n      // tilde_tau is too small, raise to level of needed, which also\n      // causes tilde_s to be zeroed\n      get(tilde_d)[i] = 1.0;\n      get(tilde_ye)[i] = 1.0;\n      get(tilde_tau)[i] = 1.5;\n      tilde_s.get(0)[i] = 0.0;\n      tilde_b.get(0)[i] = 2.0;\n    }\n  }\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto inv_spatial_metric =\n      make_with_value<tnsr::II<DataVector, 3, Frame::Inertial>>(tilde_d, 0.0);\n  auto sqrt_det_spatial_metric =\n      make_with_value<Scalar<DataVector>>(tilde_d, 1.0);\n  for (size_t d = 0; d < 3; ++d) {\n    spatial_metric.get(d, d) = get(sqrt_det_spatial_metric);\n    inv_spatial_metric.get(d, d) = get(sqrt_det_spatial_metric);\n  }\n\n  const grmhd::ValenciaDivClean::FixConservatives variable_fixer{\n      1.e-12,  1.0e-11,\n      1.0e-10, 1.0e-9,\n      0.0,     0.0,\n      1.0e-8,  0.0001,\n      true,    hydro::MagneticFieldTreatment::AssumeNonZero};\n\n  BENCHMARK(\"FixConservatives\") {\n    // Note: Benchmarking and perf indicates that software prefetching is likely\n    // the next big gain for conservative variable fixing.\n    variable_fixer(&tilde_d, &tilde_ye, &tilde_tau, &tilde_s, tilde_b,\n                   spatial_metric, inv_spatial_metric, sqrt_det_spatial_metric);\n  };\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.GrMhd.ValenciaDivClean.FixConservatives\",\n                  \"[VariableFixing][Unit]\") {\n  for (const bool enable : {true, false}) {\n    const grmhd::ValenciaDivClean::FixConservatives variable_fixer{\n        1.e-12,  1.0e-11,\n        1.0e-10, 1.0e-9,\n        0.0,     0.0,\n        1.e-12,  0.0,\n        enable,  hydro::MagneticFieldTreatment::AssumeNonZero};\n\n    const grmhd::ValenciaDivClean::FixConservatives variable_fixer_zero_b_field{\n        1.e-12,  1.0e-11,\n        1.0e-10, 1.0e-9,\n        0.0,     0.0,\n        1.e-12,  0.0,\n        enable,  hydro::MagneticFieldTreatment::AssumeZero};\n\n    test_variable_fixer(serialize_and_deserialize(variable_fixer), enable);\n    test_variable_fixer_zero_b_field(\n        serialize_and_deserialize(variable_fixer_zero_b_field), enable);\n    test_serialization(variable_fixer);\n    test_serialization(variable_fixer_zero_b_field);\n  }\n\n  const auto fixer_from_options =\n      TestHelpers::test_creation<grmhd::ValenciaDivClean::FixConservatives>(\n          \"MinimumValueOfD: 1.0e-12\\n\"\n          \"CutoffD: 1.0e-11\\n\"\n          \"MinimumValueOfYe: 1.0e-10\\n\"\n          \"CutoffYe: 1.0e-9\\n\"\n          \"SafetyFactorForB: 0.0\\n\"\n          \"SafetyFactorForS: 0.0\\n\"\n          \"SafetyFactorForSCutoffD: 1.0e-12\\n\"\n          \"SafetyFactorForSSlope: 0.0\\n\"\n          \"Enable: true\\n\"\n          \"MagneticField: AssumeNonZero\\n\");\n\n  const auto fixer_from_options_zero_b_field =\n      TestHelpers::test_creation<grmhd::ValenciaDivClean::FixConservatives>(\n          \"MinimumValueOfD: 1.0e-12\\n\"\n          \"CutoffD: 1.0e-11\\n\"\n          \"MinimumValueOfYe: 1.0e-10\\n\"\n          \"CutoffYe: 1.0e-9\\n\"\n          \"SafetyFactorForB: 0.0\\n\"\n          \"SafetyFactorForS: 0.0\\n\"\n          \"SafetyFactorForSCutoffD: 1.0e-12\\n\"\n          \"SafetyFactorForSSlope: 0.0\\n\"\n          \"Enable: true\\n\"\n          \"MagneticField: AssumeZero\\n\");\n  test_variable_fixer(fixer_from_options, true);\n  test_variable_fixer_zero_b_field(fixer_from_options_zero_b_field, true);\n\n  run_benchmark(false);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_Flattener.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Flattener.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\nclass KastaunEtAl;\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.Flattener\", \"[Unit][GrMhd]\") {\n  const Mesh<3> mesh(2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const size_t num_points = mesh.number_of_grid_points();\n\n  const EquationsOfState::Equilibrium3D ideal_fluid{\n      EquationsOfState::IdealFluid<true>{4.0 / 3.0}};\n  const Scalar<DataVector> tilde_phi(num_points, 0.);\n  tnsr::I<DataVector, 3> tilde_b;\n  get<0>(tilde_b) = DataVector(num_points, 0.02);\n  get<1>(tilde_b) = DataVector(num_points, 0.02);\n  get<2>(tilde_b) = DataVector{{0.2, 0.3, 0.1, 0.1, 0.1, 0.01, 0.4, 0.5}};\n\n  const Scalar<DataVector> sqrt_det_spatial_metric(DataVector{\n      {sqrt(1.1), sqrt(1.1), sqrt(1.1), 1., 1., sqrt(2.184), 1., 1.}});\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric(num_points, 0.);\n  tnsr::II<DataVector, 3, Frame::Inertial> inverse_spatial_metric(num_points,\n                                                                  0.);\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) = cbrt(get(sqrt_det_spatial_metric));\n    inverse_spatial_metric.get(i, i) = 1. / spatial_metric.get(i, i);\n  }\n\n  const Scalar<DataVector> det_logical_to_inertial_jacobian(\n      DataVector{{0.2, 0.1, 0.3, 0.2, 0.5, 0.6, 0.4, 0.5}});\n\n  const double volume_of_cell =\n      definite_integral(get(det_logical_to_inertial_jacobian), mesh);\n\n  const auto flattener = serialize_and_deserialize(\n      grmhd::ValenciaDivClean::Flattener<tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>{\n          true, true, false, true});\n  CHECK(flattener ==\n        grmhd::ValenciaDivClean::Flattener<tmpl::list<\n            grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>{\n            true, true, false, true});\n  CHECK(flattener !=\n        grmhd::ValenciaDivClean::Flattener<tmpl::list<\n            grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>{\n            false, true, false, true});\n  CHECK(flattener !=\n        grmhd::ValenciaDivClean::Flattener<tmpl::list<\n            grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>{\n            true, true, true, true});\n  CHECK(flattener !=\n        grmhd::ValenciaDivClean::Flattener<tmpl::list<\n            grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>{\n            true, true, false, false});\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  {\n    INFO(\"Case: NoOp\");\n    Scalar<DataVector> tilde_d(\n        DataVector{{1.4, 1.3, 1.2, 1.6, 1.8, 1.7, 1.3, 1.4}});\n    Scalar<DataVector> tilde_ye(\n        DataVector{{0.14, 0.13, 0.12, 0.16, 0.18, 0.17, 0.13, 0.14}});\n    Scalar<DataVector> tilde_tau(\n        DataVector{{3.1, 3.3, 4.3, 3.5, 2.9, 2.8, 2.6, 2.7}});\n    tnsr::i<DataVector, 3> tilde_s;\n    get<0>(tilde_s) = DataVector(num_points, 0.);\n    get<1>(tilde_s) = DataVector(num_points, 0.);\n    get<2>(tilde_s) =\n        DataVector{{-0.3, -0.2, -0.4, -0.3, -0.8, -0.2, -0.3, -0.1}};\n\n    const auto expected_tilde_d = tilde_d;\n    const auto expected_tilde_ye = tilde_ye;\n    const auto expected_tilde_tau = tilde_tau;\n    const auto expected_tilde_s = tilde_s;\n\n    Variables<hydro::grmhd_tags<DataVector>> prims(num_points, 0.);\n\n    flattener(make_not_null(&tilde_d), make_not_null(&tilde_ye),\n              make_not_null(&tilde_tau), make_not_null(&tilde_s),\n              make_not_null(&prims), tilde_b, tilde_phi,\n              sqrt_det_spatial_metric, spatial_metric, inverse_spatial_metric,\n              mesh, det_logical_to_inertial_jacobian, ideal_fluid,\n              primitive_from_conservative_options);\n\n    CHECK_ITERABLE_APPROX(tilde_d, expected_tilde_d);\n    CHECK_ITERABLE_APPROX(tilde_ye, expected_tilde_ye);\n    CHECK_ITERABLE_APPROX(tilde_tau, expected_tilde_tau);\n    CHECK_ITERABLE_APPROX(tilde_s, expected_tilde_s);\n  }\n\n  {\n    INFO(\"Case: ScaledSolution because of negative TildeD\");\n    constexpr double safety = 0.95;\n    Scalar<DataVector> tilde_d(\n        DataVector{{1.4, 1.3, 1.2, 1.6, -1.6, 1.7, 1.3, 1.4}});\n    Scalar<DataVector> tilde_ye(\n        DataVector{{0.14, 0.13, 0.12, 0.16, -0.16, 0.17, 0.13, 0.14}});\n    Scalar<DataVector> tilde_tau(\n        DataVector{{2.1, 2.3, 4.3, 3.5, 2.9, 1.8, 2.6, 2.9}});\n    tnsr::i<DataVector, 3> tilde_s;\n    get<0>(tilde_s) = DataVector(num_points, 0.);\n    get<1>(tilde_s) = DataVector(num_points, 0.);\n    get<2>(tilde_s) =\n        DataVector{{-0.3, -0.2, -0.4, -0.3, -0.8, -0.2, -0.3, -0.1}};\n    const double expected_mean_tilde_d =\n        definite_integral(get(det_logical_to_inertial_jacobian) * get(tilde_d),\n                          mesh) /\n        volume_of_cell;\n    const double expected_mean_tilde_tau =\n        definite_integral(\n            get(det_logical_to_inertial_jacobian) * get(tilde_tau), mesh) /\n        volume_of_cell;\n    std::array<double, 3> expected_mean_tilde_s{};\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(expected_mean_tilde_s, i) =\n          definite_integral(\n              get(det_logical_to_inertial_jacobian) * tilde_s.get(i), mesh) /\n          volume_of_cell;\n    }\n\n    const double rescale_factor =\n        safety * expected_mean_tilde_d /\n        (expected_mean_tilde_d -\n         (get(tilde_d)[4] * get(sqrt_det_spatial_metric)[4]));\n    const Scalar<DataVector> expected_tilde_d(\n        DataVector(expected_mean_tilde_d +\n                   rescale_factor * (get(tilde_d) - expected_mean_tilde_d)));\n    const Scalar<DataVector> expected_tilde_tau(DataVector(\n        expected_mean_tilde_tau +\n        rescale_factor * (get(tilde_tau) - expected_mean_tilde_tau)));\n    auto expected_tilde_s = tilde_s;\n    for (size_t i = 0; i < 3; ++i) {\n      expected_tilde_s.get(i) =\n          gsl::at(expected_mean_tilde_s, i) +\n          rescale_factor * (tilde_s.get(i) - gsl::at(expected_mean_tilde_s, i));\n    }\n\n    Variables<hydro::grmhd_tags<DataVector>> prims(num_points, 0.);\n\n    flattener(make_not_null(&tilde_d), make_not_null(&tilde_ye),\n              make_not_null(&tilde_tau), make_not_null(&tilde_s),\n              make_not_null(&prims), tilde_b, tilde_phi,\n              sqrt_det_spatial_metric, spatial_metric, inverse_spatial_metric,\n              mesh, det_logical_to_inertial_jacobian, ideal_fluid,\n              primitive_from_conservative_options);\n\n    // check 1) action, 2) positive tilde_d, 3) all fields changed\n    CHECK(min(get(tilde_d)) > 0.);\n    CHECK(min(get(tilde_ye)) > 0.);\n    CHECK(definite_integral(\n              get(det_logical_to_inertial_jacobian) * get(tilde_d), mesh) /\n              volume_of_cell ==\n          expected_mean_tilde_d);\n    CHECK(definite_integral(\n              get(det_logical_to_inertial_jacobian) * get(tilde_tau), mesh) /\n              volume_of_cell ==\n          expected_mean_tilde_tau);\n    for (size_t i = 0; i < 3; ++i) {\n      CAPTURE(i);\n      CHECK(gsl::at(expected_mean_tilde_s, i) ==\n            definite_integral(\n                get(det_logical_to_inertial_jacobian) * tilde_s.get(i), mesh) /\n                volume_of_cell);\n    }\n\n    CHECK_ITERABLE_APPROX(tilde_d, expected_tilde_d);\n    CHECK_ITERABLE_APPROX(tilde_tau, expected_tilde_tau);\n    CHECK_ITERABLE_APPROX(tilde_s, expected_tilde_s);\n  }\n\n  {\n    INFO(\"Case: SetSolutionToMean because of too-small TildeTau\");\n    Scalar<DataVector> tilde_d(\n        DataVector{{1.4, 1.3, 1.2, 1.6, 1.8, 1.7, 1.3, 1.4}});\n    Scalar<DataVector> tilde_ye(\n        DataVector{{0.14, 0.13, 0.12, 0.16, 0.18, 0.17, 0.13, 0.14}});\n    Scalar<DataVector> tilde_tau(\n        DataVector{{3.1, 3.3, 4.3, 3.5, 2.9, 2.8, 2.6, 9.e-3}});\n    tnsr::i<DataVector, 3> tilde_s;\n    get<0>(tilde_s) = DataVector(num_points, 0.);\n    get<1>(tilde_s) = DataVector(num_points, 0.);\n    get<2>(tilde_s) =\n        DataVector{{-0.3, -0.2, -0.4, -0.3, -0.8, -0.2, -0.3, -0.1}};\n\n    Variables<hydro::grmhd_tags<DataVector>> prims(num_points, 0.);\n\n    const double expected_mean_tilde_d =\n        definite_integral(get(det_logical_to_inertial_jacobian) * get(tilde_d),\n                          mesh) /\n        volume_of_cell;\n    const double expected_mean_tilde_tau =\n        definite_integral(\n            get(det_logical_to_inertial_jacobian) * get(tilde_tau), mesh) /\n        volume_of_cell;\n    std::array<double, 3> expected_mean_tilde_s{};\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(expected_mean_tilde_s, i) =\n          definite_integral(\n              get(det_logical_to_inertial_jacobian) * tilde_s.get(i), mesh) /\n          volume_of_cell;\n    }\n\n    constexpr double safety = 0.99;\n    const double scale_factor =\n        safety *\n        (0.5 *\n             get(dot_product(tilde_b, tilde_b,\n                             spatial_metric))[num_points - 1] /\n             get(sqrt_det_spatial_metric)[num_points - 1] -\n         expected_mean_tilde_tau) /\n        (get(tilde_tau)[num_points - 1] - expected_mean_tilde_tau);\n\n    const Scalar<DataVector> expected_tilde_tau{\n        DataVector{expected_mean_tilde_tau +\n                   scale_factor * (get(tilde_tau) - expected_mean_tilde_tau)}};\n    const Scalar<DataVector> expected_tilde_d = tilde_d;\n    const tnsr::i<DataVector, 3, Frame::Inertial> expected_tilde_s = tilde_s;\n\n    flattener(make_not_null(&tilde_d), make_not_null(&tilde_ye),\n              make_not_null(&tilde_tau), make_not_null(&tilde_s),\n              make_not_null(&prims), tilde_b, tilde_phi,\n              sqrt_det_spatial_metric, spatial_metric, inverse_spatial_metric,\n              mesh, det_logical_to_inertial_jacobian, ideal_fluid,\n              primitive_from_conservative_options);\n\n    CHECK(definite_integral(\n              get(det_logical_to_inertial_jacobian) * get(tilde_d), mesh) /\n              volume_of_cell ==\n          expected_mean_tilde_d);\n    CHECK(definite_integral(\n              get(det_logical_to_inertial_jacobian) * get(tilde_tau), mesh) /\n              volume_of_cell ==\n          approx(expected_mean_tilde_tau));\n    for (size_t i = 0; i < 3; ++i) {\n      CAPTURE(i);\n      CHECK(gsl::at(expected_mean_tilde_s, i) ==\n            definite_integral(\n                get(det_logical_to_inertial_jacobian) * tilde_s.get(i), mesh) /\n                volume_of_cell);\n    }\n\n    CHECK_ITERABLE_APPROX(tilde_d, expected_tilde_d);\n    CHECK_ITERABLE_APPROX(tilde_tau, expected_tilde_tau);\n    CHECK_ITERABLE_APPROX(tilde_s, expected_tilde_s);\n\n    // Check that the scaling made the max be less than one:\n    CHECK(1. > max(0.5 * get(dot_product(tilde_b, tilde_b, spatial_metric)) /\n                   get(sqrt_det_spatial_metric) / get(tilde_tau)));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.Fluxes\", \"[Unit][GrMhd]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean\"};\n\n  pypp::check_with_random_values<1>(\n      &grmhd::ValenciaDivClean::ComputeFluxes::apply, \"Fluxes\",\n      {\"tilde_d_flux\", \"tilde_ye_flux\", \"tilde_tau_flux\", \"tilde_s_flux\",\n       \"tilde_b_flux\", \"tilde_phi_flux\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_PrimitiveFromConservative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/KastaunEtAlHydro.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/NewmanHamlin.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PalenzuelaEtAl.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/PrimitiveFromConservativeOptions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/HybridEos.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes {\nclass KastaunEtAl;\nclass NewmanHamlin;\nclass PalenzuelaEtAl;\n}  // namespace grmhd::ValenciaDivClean::PrimitiveRecoverySchemes\n\nnamespace {\n\n// In certain cases the evolved variables may go out of\n// bounds (for EoS evaluation) check that primitive\n// recovery correctly fixes these (currently only\n// used for electron fraction)\ntemplate <typename OrderedListOfPrimitiveRecoverySchemes,\n          bool UseMagneticField = false>\nvoid test_potentially_eos_dependent_primitive_corrections(\n    const EquationsOfState::EquationOfState<true, 3>& equation_of_state,\n    const Scalar<DataVector>& input_rest_mass_density,\n    const Scalar<DataVector>& input_temperature,\n    const Scalar<DataVector>& input_electron_fraction,\n    const gsl::not_null<std::mt19937*> generator) {\n  const DataVector& used_for_size = get(input_rest_mass_density);\n  // Taken as references for now, but make copies if in the future\n  // they are corrected as well.\n  const auto& expected_rest_mass_density = input_rest_mass_density;\n  const auto& expected_temperature = input_temperature;\n  // The electron fraction may evolve out of bounds\n  const auto expected_electron_fraction =\n      Scalar<DataVector>(min(0.5, max(get(input_electron_fraction), 0.)));\n  const auto expected_lorentz_factor =\n      TestHelpers::hydro::random_lorentz_factor(generator, used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<3>(generator, used_for_size);\n  const auto expected_spatial_velocity = TestHelpers::hydro::random_velocity(\n      generator, expected_lorentz_factor, spatial_metric);\n  Scalar<DataVector> expected_specific_internal_energy =\n      equation_of_state.specific_internal_energy_from_density_and_temperature(\n          expected_rest_mass_density, expected_temperature,\n          expected_electron_fraction);\n\n  Scalar<DataVector> expected_pressure =\n      equation_of_state.pressure_from_density_and_temperature(\n          expected_rest_mass_density, expected_temperature,\n          expected_electron_fraction);\n\n  auto expected_magnetic_field = TestHelpers::hydro::random_magnetic_field(\n      generator, expected_pressure, spatial_metric);\n  if constexpr (!UseMagneticField) {\n    get<0>(expected_magnetic_field) = 0.;\n    get<1>(expected_magnetic_field) = 0.;\n    get<2>(expected_magnetic_field) = 0.;\n  }\n\n  const auto expected_divergence_cleaning_field =\n      TestHelpers::hydro::random_divergence_cleaning_field(generator,\n                                                           used_for_size);\n\n  const auto det_and_inv = determinant_and_inverse(spatial_metric);\n  const auto& inv_spatial_metric = det_and_inv.second;\n  const Scalar<DataVector> sqrt_det_spatial_metric =\n      Scalar<DataVector>{sqrt(get(det_and_inv.first))};\n\n  const size_t number_of_points = used_for_size.size();\n  Scalar<DataVector> tilde_d(number_of_points);\n  Scalar<DataVector> tilde_ye(number_of_points);\n  Scalar<DataVector> tilde_tau(number_of_points);\n  tnsr::i<DataVector, 3> tilde_s(number_of_points);\n  tnsr::I<DataVector, 3> tilde_b(number_of_points);\n  Scalar<DataVector> tilde_phi(number_of_points);\n\n  grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n      make_not_null(&tilde_d), make_not_null(&tilde_ye),\n      make_not_null(&tilde_tau), make_not_null(&tilde_s),\n      make_not_null(&tilde_b), make_not_null(&tilde_phi),\n      expected_rest_mass_density, expected_electron_fraction,\n      expected_specific_internal_energy, expected_pressure,\n      expected_spatial_velocity, expected_lorentz_factor,\n      expected_magnetic_field, sqrt_det_spatial_metric, spatial_metric,\n      expected_divergence_cleaning_field);\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz =\n      0.5 * sqrt(std::numeric_limits<double>::max());\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  Scalar<DataVector> rest_mass_density(number_of_points);\n  Scalar<DataVector> electron_fraction(number_of_points);\n  Scalar<DataVector> specific_internal_energy(number_of_points);\n  tnsr::I<DataVector, 3> spatial_velocity(number_of_points);\n  tnsr::I<DataVector, 3> magnetic_field(number_of_points);\n  Scalar<DataVector> divergence_cleaning_field(number_of_points);\n  Scalar<DataVector> lorentz_factor(number_of_points);\n  // need to zero-initialize pressure because the recovery schemes assume it is\n  // not nan\n  Scalar<DataVector> pressure(number_of_points, 0.0);\n  Scalar<DataVector> temperature(number_of_points);\n  grmhd::ValenciaDivClean::\n      PrimitiveFromConservative<OrderedListOfPrimitiveRecoverySchemes>::apply(\n          make_not_null(&rest_mass_density), make_not_null(&electron_fraction),\n          make_not_null(&specific_internal_energy),\n          make_not_null(&spatial_velocity), make_not_null(&magnetic_field),\n          make_not_null(&divergence_cleaning_field),\n          make_not_null(&lorentz_factor), make_not_null(&pressure),\n          make_not_null(&temperature), tilde_d, tilde_ye, tilde_tau, tilde_s,\n          tilde_b, tilde_phi, spatial_metric, inv_spatial_metric,\n          sqrt_det_spatial_metric, equation_of_state,\n          primitive_from_conservative_options);\n\n  Approx larger_approx =\n      Approx::custom().epsilon(std::numeric_limits<double>::epsilon() * 1.e8);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_rest_mass_density, rest_mass_density,\n                               larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_electron_fraction, electron_fraction,\n                               larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_temperature, temperature,\n                               larger_approx);\n}\n\ntemplate <typename OrderedListOfPrimitiveRecoverySchemes,\n          bool UseMagneticField = true, typename EosType>\nvoid test_primitive_from_conservative_random(\n    const gsl::not_null<std::mt19937*> generator,\n    const EosType& equation_of_state, const DataVector& used_for_size) {\n  static_assert(EosType::thermodynamic_dim == 3);\n  static_assert(EosType::is_relativistic);\n  constexpr bool eos_is_barotropic =\n      tt::is_a_v<EquationsOfState::Barotropic3D, EosType>;\n  // generate random primitives with interesting astrophysical values\n  const auto expected_rest_mass_density =\n      TestHelpers::hydro::random_density(generator, used_for_size);\n  const auto expected_electron_fraction =\n      TestHelpers::hydro::random_electron_fraction(generator, used_for_size);\n  const auto expected_lorentz_factor =\n      TestHelpers::hydro::random_lorentz_factor(generator, used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<3>(generator, used_for_size);\n  const auto expected_spatial_velocity = TestHelpers::hydro::random_velocity(\n      generator, expected_lorentz_factor, spatial_metric);\n  Scalar<DataVector> expected_specific_internal_energy{};\n  Scalar<DataVector> expected_temperature{};\n  Scalar<DataVector> expected_pressure{};\n  if constexpr (eos_is_barotropic) {\n    expected_specific_internal_energy =\n        equation_of_state.specific_internal_energy_from_density_and_temperature(\n            expected_rest_mass_density, Scalar<DataVector>{},\n            Scalar<DataVector>{});\n  } else {\n    expected_specific_internal_energy =\n        TestHelpers::hydro::random_specific_internal_energy(generator,\n                                                            used_for_size);\n  }\n  expected_temperature = equation_of_state.temperature_from_density_and_energy(\n      expected_rest_mass_density, expected_specific_internal_energy,\n      expected_electron_fraction);\n  expected_pressure = equation_of_state.pressure_from_density_and_temperature(\n      expected_rest_mass_density, expected_temperature,\n      expected_electron_fraction);\n\n  auto expected_magnetic_field = TestHelpers::hydro::random_magnetic_field(\n      generator, expected_pressure, spatial_metric);\n  if constexpr (!UseMagneticField) {\n    get<0>(expected_magnetic_field) = 0.;\n    get<1>(expected_magnetic_field) = 0.;\n    get<2>(expected_magnetic_field) = 0.;\n  }\n\n  const auto expected_divergence_cleaning_field =\n      TestHelpers::hydro::random_divergence_cleaning_field(generator,\n                                                           used_for_size);\n\n  const auto det_and_inv = determinant_and_inverse(spatial_metric);\n  const auto& inv_spatial_metric = det_and_inv.second;\n  const Scalar<DataVector> sqrt_det_spatial_metric =\n      Scalar<DataVector>{sqrt(get(det_and_inv.first))};\n\n  const size_t number_of_points = used_for_size.size();\n  Scalar<DataVector> tilde_d(number_of_points);\n  Scalar<DataVector> tilde_ye(number_of_points);\n  Scalar<DataVector> tilde_tau(number_of_points);\n  tnsr::i<DataVector, 3> tilde_s(number_of_points);\n  tnsr::I<DataVector, 3> tilde_b(number_of_points);\n  Scalar<DataVector> tilde_phi(number_of_points);\n\n  grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n      make_not_null(&tilde_d), make_not_null(&tilde_ye),\n      make_not_null(&tilde_tau), make_not_null(&tilde_s),\n      make_not_null(&tilde_b), make_not_null(&tilde_phi),\n      expected_rest_mass_density, expected_electron_fraction,\n      expected_specific_internal_energy, expected_pressure,\n      expected_spatial_velocity, expected_lorentz_factor,\n      expected_magnetic_field, sqrt_det_spatial_metric, spatial_metric,\n      expected_divergence_cleaning_field);\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz =\n      0.5 * sqrt(std::numeric_limits<double>::max());\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n  Scalar<DataVector> rest_mass_density(number_of_points);\n  Scalar<DataVector> electron_fraction(number_of_points);\n  Scalar<DataVector> specific_internal_energy(number_of_points);\n  tnsr::I<DataVector, 3> spatial_velocity(number_of_points);\n  tnsr::I<DataVector, 3> magnetic_field(number_of_points);\n  Scalar<DataVector> divergence_cleaning_field(number_of_points);\n  Scalar<DataVector> lorentz_factor(number_of_points);\n  // need to zero-initialize pressure because the recovery schemes assume it is\n  // not nan\n  Scalar<DataVector> pressure(number_of_points, 0.0);\n  Scalar<DataVector> temperature(number_of_points);\n  grmhd::ValenciaDivClean::\n      PrimitiveFromConservative<OrderedListOfPrimitiveRecoverySchemes>::apply(\n          make_not_null(&rest_mass_density), make_not_null(&electron_fraction),\n          make_not_null(&specific_internal_energy),\n          make_not_null(&spatial_velocity), make_not_null(&magnetic_field),\n          make_not_null(&divergence_cleaning_field),\n          make_not_null(&lorentz_factor), make_not_null(&pressure),\n          make_not_null(&temperature), tilde_d, tilde_ye, tilde_tau, tilde_s,\n          tilde_b, tilde_phi, spatial_metric, inv_spatial_metric,\n          sqrt_det_spatial_metric, equation_of_state,\n          primitive_from_conservative_options);\n  INFO(\"Checking random-value primitive recovery.\");\n  Approx larger_approx =\n      Approx::custom().epsilon(std::numeric_limits<double>::epsilon() * 1.e8);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_rest_mass_density, rest_mass_density,\n                               larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_electron_fraction, electron_fraction,\n                               larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_specific_internal_energy,\n                               specific_internal_energy, larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_lorentz_factor, lorentz_factor,\n                               larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_pressure, pressure, larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_temperature, temperature,\n                               larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_spatial_velocity, spatial_velocity,\n                               larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_magnetic_field, magnetic_field,\n                               larger_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_divergence_cleaning_field,\n                               divergence_cleaning_field, larger_approx);\n}\n\ntemplate <typename OrderedListOfPrimitiveRecoverySchemes,\n          bool UseMagneticField = true>\nvoid test_primitive_from_conservative_known(const DataVector& used_for_size) {\n  const auto expected_rest_mass_density =\n      make_with_value<Scalar<DataVector>>(used_for_size, 2.0);\n  const auto expected_electron_fraction =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.1);\n  const auto expected_lorentz_factor =\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.25);\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3>>(used_for_size, 0.0);\n  get<0, 0>(spatial_metric) = 1.0;\n  get<1, 1>(spatial_metric) = 4.0;\n  get<2, 2>(spatial_metric) = 16.0;\n  auto expected_spatial_velocity =\n      make_with_value<tnsr::I<DataVector, 3>>(used_for_size, 0.0);\n  get<0>(expected_spatial_velocity) = 9.0 / 65.0;\n  get<1>(expected_spatial_velocity) = 6.0 / 65.0;\n  get<2>(expected_spatial_velocity) = 9.0 / 65.0;\n  const auto expected_specific_internal_energy =\n      make_with_value<Scalar<DataVector>>(used_for_size, 3.0);\n  const auto expected_temperature =\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0);\n  const auto expected_pressure =\n      make_with_value<Scalar<DataVector>>(used_for_size, 2.0);\n  auto expected_magnetic_field =\n      make_with_value<tnsr::I<DataVector, 3>>(used_for_size, 0.0);\n  get<0>(expected_magnetic_field) = 36.0 / 13.0;\n  get<1>(expected_magnetic_field) = 9.0 / 26.0;\n  get<2>(expected_magnetic_field) = 3.0 / 13.0;\n\n  if constexpr (!UseMagneticField) {\n    INFO(\"Not using Magnetic Field\");\n    get<0>(expected_magnetic_field) = 0.;\n    get<1>(expected_magnetic_field) = 0.;\n    get<2>(expected_magnetic_field) = 0.;\n  }\n  const auto expected_divergence_cleaning_field =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.5);\n\n  const auto det_and_inv = determinant_and_inverse(spatial_metric);\n  const auto& inv_spatial_metric = det_and_inv.second;\n  const Scalar<DataVector> sqrt_det_spatial_metric =\n      Scalar<DataVector>{sqrt(get(det_and_inv.first))};\n\n  const size_t number_of_points = used_for_size.size();\n  Scalar<DataVector> tilde_d(number_of_points);\n  Scalar<DataVector> tilde_ye(number_of_points);\n  Scalar<DataVector> tilde_tau(number_of_points);\n  tnsr::i<DataVector, 3> tilde_s(number_of_points);\n  tnsr::I<DataVector, 3> tilde_b(number_of_points);\n  Scalar<DataVector> tilde_phi(number_of_points);\n\n  const double cutoff_d_for_inversion = 0.0;\n  const double density_when_skipping_inversion = 0.0;\n  const double kastaun_max_lorentz = 1.0e4;\n  const grmhd::ValenciaDivClean::PrimitiveFromConservativeOptions\n      primitive_from_conservative_options(cutoff_d_for_inversion,\n                                          density_when_skipping_inversion,\n                                          kastaun_max_lorentz);\n\n  grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n      make_not_null(&tilde_d), make_not_null(&tilde_ye),\n      make_not_null(&tilde_tau), make_not_null(&tilde_s),\n      make_not_null(&tilde_b), make_not_null(&tilde_phi),\n      expected_rest_mass_density, expected_electron_fraction,\n      expected_specific_internal_energy, expected_pressure,\n      expected_spatial_velocity, expected_lorentz_factor,\n      expected_magnetic_field, sqrt_det_spatial_metric, spatial_metric,\n      expected_divergence_cleaning_field);\n\n  Scalar<DataVector> rest_mass_density(number_of_points);\n  Scalar<DataVector> electron_fraction(number_of_points);\n  Scalar<DataVector> specific_internal_energy(number_of_points);\n  Scalar<DataVector> temperature(number_of_points);\n  tnsr::I<DataVector, 3> spatial_velocity(number_of_points);\n  tnsr::I<DataVector, 3> magnetic_field(number_of_points);\n  Scalar<DataVector> divergence_cleaning_field(number_of_points);\n  Scalar<DataVector> lorentz_factor(number_of_points);\n  // need to zero-initialize pressure because the recovery schemes assume it is\n  // not nan\n  Scalar<DataVector> pressure(number_of_points, 0.0);\n  EquationsOfState::Equilibrium3D ideal_fluid{\n      EquationsOfState::IdealFluid<true>{4.0 / 3.0}};\n  grmhd::ValenciaDivClean::\n      PrimitiveFromConservative<OrderedListOfPrimitiveRecoverySchemes>::apply(\n          make_not_null(&rest_mass_density), make_not_null(&electron_fraction),\n          make_not_null(&specific_internal_energy),\n          make_not_null(&spatial_velocity), make_not_null(&magnetic_field),\n          make_not_null(&divergence_cleaning_field),\n          make_not_null(&lorentz_factor), make_not_null(&pressure),\n          make_not_null(&temperature), tilde_d, tilde_ye, tilde_tau, tilde_s,\n          tilde_b, tilde_phi, spatial_metric, inv_spatial_metric,\n          sqrt_det_spatial_metric, ideal_fluid,\n          primitive_from_conservative_options);\n  INFO(\"test primitive from conservative with known values\");\n  CHECK_ITERABLE_APPROX(expected_rest_mass_density, rest_mass_density);\n  CHECK_ITERABLE_APPROX(expected_electron_fraction, electron_fraction);\n  CHECK_ITERABLE_APPROX(expected_specific_internal_energy,\n                        specific_internal_energy);\n  CHECK_ITERABLE_APPROX(expected_lorentz_factor, lorentz_factor);\n  CHECK_ITERABLE_APPROX(expected_temperature, temperature);\n  CHECK_ITERABLE_APPROX(expected_pressure, pressure);\n  CHECK_ITERABLE_APPROX(expected_spatial_velocity, spatial_velocity);\n  CHECK_ITERABLE_APPROX(expected_magnetic_field, magnetic_field);\n  CHECK_ITERABLE_APPROX(expected_divergence_cleaning_field,\n                        divergence_cleaning_field);\n\n  if constexpr (not UseMagneticField) {\n    // Test KastaunHydro for FPE safety\n    tilde_tau = make_with_value<Scalar<DataVector>>(used_for_size, -10.);\n\n    grmhd::ValenciaDivClean::PrimitiveFromConservative<\n        OrderedListOfPrimitiveRecoverySchemes,\n        false>::apply(make_not_null(&rest_mass_density),\n                      make_not_null(&electron_fraction),\n                      make_not_null(&specific_internal_energy),\n                      make_not_null(&spatial_velocity),\n                      make_not_null(&magnetic_field),\n                      make_not_null(&divergence_cleaning_field),\n                      make_not_null(&lorentz_factor), make_not_null(&pressure),\n                      make_not_null(&temperature), tilde_d, tilde_ye, tilde_tau,\n                      tilde_s, tilde_b, tilde_phi, spatial_metric,\n                      inv_spatial_metric, sqrt_det_spatial_metric, ideal_fluid,\n                      primitive_from_conservative_options);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.PrimitiveFromConservative\",\n                  \"[Unit][GrMhd]\") {\n  MAKE_GENERATOR(generator);\n\n  EquationsOfState::Barotropic3D wrapped_3d_polytrope(\n      EquationsOfState::PolytropicFluid<true>(100.0, 2.0));\n  EquationsOfState::Equilibrium3D wrapped_3d_polytrope_hot(\n      EquationsOfState::HybridEos<EquationsOfState::PolytropicFluid<true>>(\n          EquationsOfState::PolytropicFluid<true>(100.0, 2.0), 5.0 / 3.0));\n  EquationsOfState::Equilibrium3D wrapped_ideal_fluid{\n      EquationsOfState::IdealFluid<true>(4.0 / 3.0)};\n  const DataVector dv(5);\n  test_primitive_from_conservative_known<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>>(dv);\n  test_primitive_from_conservative_known<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>(dv);\n  test_primitive_from_conservative_known<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAlHydro>,\n      false>(dv);\n  test_primitive_from_conservative_known<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>>(dv);\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>,\n      1>(&generator, wrapped_3d_polytrope, dv);\n  test_primitive_from_conservative_random<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>>(\n      &generator, wrapped_ideal_fluid, dv);\n  test_primitive_from_conservative_random<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>>(\n      &generator, wrapped_3d_polytrope, dv);\n  test_primitive_from_conservative_random<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>>(\n      &generator, wrapped_ideal_fluid, dv);\n  test_primitive_from_conservative_random<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>(\n      &generator, wrapped_3d_polytrope, dv);\n  test_primitive_from_conservative_random<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>(\n      &generator, wrapped_ideal_fluid, dv);\n\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAlHydro>,\n      false>(&generator, wrapped_3d_polytrope, dv);\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAlHydro>,\n      false>(&generator, wrapped_ideal_fluid, dv);\n  INFO(\"3D EoS Kastaun\");\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>,\n      true>(&generator, wrapped_3d_polytrope, dv);\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>,\n      false>(&generator, wrapped_3d_polytrope, dv);\n  INFO(\"3D EoS Kastaun Hydro\");\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAlHydro>,\n      false>(&generator, wrapped_3d_polytrope, dv);\n  INFO(\"3D EoS Newman-Hamlin\");\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>,\n      true>(&generator, wrapped_3d_polytrope, dv);\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>,\n      false>(&generator, wrapped_3d_polytrope, dv);\n  INFO(\"3D EoS Palenzuela\");\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n      true>(&generator, wrapped_3d_polytrope, dv);\n  test_primitive_from_conservative_random<\n      tmpl::list<\n          grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>,\n      false>(&generator, wrapped_3d_polytrope, dv);\n\n  test_potentially_eos_dependent_primitive_corrections<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>(\n      wrapped_3d_polytrope_hot, make_with_value<Scalar<DataVector>>(dv, 1e-4),\n      make_with_value<Scalar<DataVector>>(dv, 1e-1),\n      make_with_value<Scalar<DataVector>>(dv, -.1), &generator);\n  test_potentially_eos_dependent_primitive_corrections<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAl>>(\n      wrapped_3d_polytrope_hot, make_with_value<Scalar<DataVector>>(dv, 1e-4),\n      make_with_value<Scalar<DataVector>>(dv, 1e-1),\n      make_with_value<Scalar<DataVector>>(dv, 1.0), &generator);\n  test_potentially_eos_dependent_primitive_corrections<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAlHydro>>(\n      wrapped_3d_polytrope_hot, make_with_value<Scalar<DataVector>>(dv, 1e-4),\n      make_with_value<Scalar<DataVector>>(dv, 1e-1),\n      make_with_value<Scalar<DataVector>>(dv, -.1), &generator);\n  test_potentially_eos_dependent_primitive_corrections<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::KastaunEtAlHydro>>(\n      wrapped_3d_polytrope_hot, make_with_value<Scalar<DataVector>>(dv, 1e-4),\n      make_with_value<Scalar<DataVector>>(dv, 1e-1),\n      make_with_value<Scalar<DataVector>>(dv, 1.0), &generator);\n  test_potentially_eos_dependent_primitive_corrections<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>>(\n      wrapped_3d_polytrope_hot, make_with_value<Scalar<DataVector>>(dv, 1e-4),\n      make_with_value<Scalar<DataVector>>(dv, 1e-1),\n      make_with_value<Scalar<DataVector>>(dv, -.1), &generator);\n  test_potentially_eos_dependent_primitive_corrections<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::NewmanHamlin>>(\n      wrapped_3d_polytrope_hot, make_with_value<Scalar<DataVector>>(dv, 1e-4),\n      make_with_value<Scalar<DataVector>>(dv, 1e-1),\n      make_with_value<Scalar<DataVector>>(dv, 1.0), &generator);\n  test_potentially_eos_dependent_primitive_corrections<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>>(\n      wrapped_3d_polytrope_hot, make_with_value<Scalar<DataVector>>(dv, 1e-4),\n      make_with_value<Scalar<DataVector>>(dv, 1e-1),\n      make_with_value<Scalar<DataVector>>(dv, -.1), &generator);\n  test_potentially_eos_dependent_primitive_corrections<tmpl::list<\n      grmhd::ValenciaDivClean::PrimitiveRecoverySchemes::PalenzuelaEtAl>>(\n      wrapped_3d_polytrope_hot, make_with_value<Scalar<DataVector>>(dv, 1e-4),\n      make_with_value<Scalar<DataVector>>(dv, 1e-1),\n      make_with_value<Scalar<DataVector>>(dv, 1.0), &generator);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_QuadrupoleFormula.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/QuadrupoleFormula.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TransportVelocity.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.QuadrupoleFormula\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_compute_tag<\n      grmhd::ValenciaDivClean::Tags::QuadrupoleMomentCompute<\n               DataVector, 3, Frame::Inertial>>(\"QuadrupoleMoment\");\n  TestHelpers::db::test_compute_tag<\n      grmhd::ValenciaDivClean::Tags::QuadrupoleMomentDerivativeCompute<\n          DataVector, 3,\n          ::Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n          hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Inertial>,\n          Frame::Inertial>>(\"QuadrupoleMomentDerivative(SpatialVelocity)\");\n  TestHelpers::db::test_compute_tag<\n      grmhd::ValenciaDivClean::Tags::QuadrupoleMomentDerivativeCompute<\n          DataVector, 3,\n          ::Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n          hydro::Tags::TransportVelocity<DataVector, 3, Frame::Inertial>,\n          Frame::Inertial>>(\"QuadrupoleMomentDerivative(TransportVelocity)\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_SetVariablesNeededFixingToFalse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/SetVariablesNeededFixingToFalse.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ValenciaDivClean.SetVariablesNeededFixingToFalse\",\n    \"[Unit][Evolution]\") {\n  auto box = db::create<\n      db::AddSimpleTags<grmhd::ValenciaDivClean::Tags::VariablesNeededFixing>>(\n      true);\n  db::mutate_apply<grmhd::ValenciaDivClean::SetVariablesNeededFixingToFalse>(\n      make_not_null(&box));\n  CHECK_FALSE(\n      db::get<grmhd::ValenciaDivClean::Tags::VariablesNeededFixing>(box));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_Sources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Sources.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.Sources\", \"[Unit][GrMhd]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean\"};\n\n  pypp::check_with_random_values<1>(\n      &grmhd::ValenciaDivClean::ComputeSources::apply, \"Sources\",\n      {\"source_tilde_tau\", \"source_tilde_s\", \"source_tilde_b\",\n       \"source_tilde_phi\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ValenciaDivClean.Tags\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<\n      grmhd::ValenciaDivClean::Tags::VariablesNeededFixing>(\n          \"VariablesNeededFixing\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivativeTerms.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid forward_to_time_deriv(\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_d,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_ye,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_tau,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_s,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_b,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_phi,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_d_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_ye_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_tau_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, 3, Frame::Inertial>*> tilde_b_flux,\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*>\n        tilde_phi_flux,\n\n    const Scalar<DataVector>& tilde_d, const Scalar<DataVector>& tilde_ye,\n    const Scalar<DataVector>& tilde_tau,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& tilde_b,\n    const Scalar<DataVector>& tilde_phi, const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const Scalar<DataVector>& sqrt_det_spatial_metric,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& d_lapse,\n    const tnsr::iJ<DataVector, 3, Frame::Inertial>& d_shift,\n    const tnsr::ijj<DataVector, 3, Frame::Inertial>& d_spatial_metric,\n    const Scalar<DataVector>& pressure,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& spatial_velocity,\n    const Scalar<DataVector>& lorentz_factor,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& magnetic_field,\n\n    const Scalar<DataVector>& rest_mass_density,\n    const Scalar<DataVector>& electron_fraction,\n    const Scalar<DataVector>& specific_enthalpy,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& extrinsic_curvature,\n    const double constraint_damping_parameter) {\n  get(*non_flux_terms_dt_tilde_d) = 0.0;\n  get(*non_flux_terms_dt_tilde_ye) = 0.0;\n\n  Variables<\n      typename grmhd::ValenciaDivClean::TimeDerivativeTerms::temporary_tags>\n      temp{get(lapse).size(), 0.0};\n\n  grmhd::ValenciaDivClean::TimeDerivativeTerms::apply(\n      non_flux_terms_dt_tilde_d, non_flux_terms_dt_tilde_ye,\n      non_flux_terms_dt_tilde_tau, non_flux_terms_dt_tilde_s,\n      non_flux_terms_dt_tilde_b, non_flux_terms_dt_tilde_phi,\n\n      tilde_d_flux, tilde_ye_flux, tilde_tau_flux, tilde_s_flux, tilde_b_flux,\n      tilde_phi_flux,\n\n      make_not_null(\n          &get<hydro::Tags::SpatialVelocityOneForm<DataVector, 3,\n                                                   Frame::Inertial>>(temp)),\n      make_not_null(\n          &get<hydro::Tags::MagneticFieldOneForm<DataVector, 3,\n                                                 Frame::Inertial>>(temp)),\n      make_not_null(\n          &get<hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(temp)),\n      make_not_null(&get<hydro::Tags::MagneticFieldSquared<DataVector>>(temp)),\n      make_not_null(&get<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::\n                             OneOverLorentzFactorSquared>(temp)),\n      make_not_null(&get<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::\n                             PressureStar>(temp)),\n      make_not_null(&get<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::\n                             PressureStarLapseSqrtDetSpatialMetric>(temp)),\n      make_not_null(\n          &get<hydro::Tags::TransportVelocity<DataVector, 3, Frame::Inertial>>(\n              temp)),\n      make_not_null(&get<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::\n                             LapseTimesbOverW>(temp)),\n\n      // Source terms\n      make_not_null(\n          &get<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::TildeSUp>(\n              temp)),\n      make_not_null(&get<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::\n                             DensitizedStress>(temp)),\n      make_not_null(\n          &get<gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>>(temp)),\n      make_not_null(\n          &get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>>(temp)),\n      make_not_null(\n          &get<gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>(\n              temp)),\n      make_not_null(&get<typename grmhd::ValenciaDivClean::TimeDerivativeTerms::\n                             EnthalpyTimesDensityWSquaredPlusBSquared>(temp)),\n\n      make_not_null(&get<gr::Tags::Lapse<DataVector>>(temp)),\n      make_not_null(&get<gr::Tags::Shift<DataVector, 3>>(temp)),\n      make_not_null(&get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp)),\n\n      tilde_d, tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, lapse, shift,\n      sqrt_det_spatial_metric, spatial_metric, inv_spatial_metric, d_lapse,\n      d_shift, d_spatial_metric, pressure, spatial_velocity, lorentz_factor,\n      magnetic_field,\n\n      rest_mass_density, electron_fraction, specific_enthalpy,\n      extrinsic_curvature, constraint_damping_parameter);\n\n  CHECK(get<gr::Tags::Lapse<DataVector>>(temp) == lapse);\n  CHECK(get<gr::Tags::Shift<DataVector, 3>>(temp) == shift);\n  CHECK(get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp) ==\n        inv_spatial_metric);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GrMhd.ValenciaDivClean.TimeDerivativeTerms\",\n                  \"[Unit][GrMhd]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/GrMhd/ValenciaDivClean\"};\n\n  pypp::check_with_random_values<1>(\n      &forward_to_time_deriv, \"TimeDerivative\",\n      {\"source_tilde_d\", \"source_tilde_ye\", \"source_tilde_tau\",\n       \"source_tilde_s\", \"source_tilde_b\", \"source_tilde_phi\", \"tilde_d_flux\",\n       \"tilde_ye_flux\", \"tilde_tau_flux\", \"tilde_s_flux\", \"tilde_b_flux\",\n       \"tilde_phi_flux\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/Test_ValenciaDivClean.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.GrMhd.ValenciaDivClean.Tags\",\n                  \"[Unit][GrMhd]\") {\n  TestHelpers::db::test_simple_tag<\n      grmhd::ValenciaDivClean::Tags::CharacteristicSpeeds>(\n      \"CharacteristicSpeeds\");\n  TestHelpers::db::test_simple_tag<grmhd::ValenciaDivClean::Tags::TildeD>(\n      \"TildeD\");\n  TestHelpers::db::test_simple_tag<grmhd::ValenciaDivClean::Tags::TildeYe>(\n      \"TildeYe\");\n  TestHelpers::db::test_simple_tag<grmhd::ValenciaDivClean::Tags::TildeTau>(\n      \"TildeTau\");\n  TestHelpers::db::test_simple_tag<\n      grmhd::ValenciaDivClean::Tags::TildeS<Frame::Grid>>(\"Grid_TildeS\");\n  TestHelpers::db::test_simple_tag<\n      grmhd::ValenciaDivClean::Tags::TildeB<Frame::Grid>>(\"Grid_TildeB\");\n  TestHelpers::db::test_simple_tag<grmhd::ValenciaDivClean::Tags::TildePhi>(\n      \"TildePhi\");\n  TestHelpers::db::test_simple_tag<\n      grmhd::ValenciaDivClean::Tags::ConstraintDampingParameter>(\n      \"ConstraintDampingParameter\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/TimeDerivative.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Fluxes\nimport numpy as np\nimport Sources\n\n\ndef tilde_d_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Fluxes.tilde_d_flux(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        pressure,\n        spatial_velocity,\n        lorentz_factor,\n        magnetic_field,\n    )\n\n\ndef tilde_ye_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Fluxes.tilde_ye_flux(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        pressure,\n        spatial_velocity,\n        lorentz_factor,\n        magnetic_field,\n    )\n\n\ndef tilde_tau_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Fluxes.tilde_tau_flux(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        pressure,\n        spatial_velocity,\n        lorentz_factor,\n        magnetic_field,\n    )\n\n\ndef tilde_s_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Fluxes.tilde_s_flux(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        pressure,\n        spatial_velocity,\n        lorentz_factor,\n        magnetic_field,\n    )\n\n\ndef tilde_b_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Fluxes.tilde_b_flux(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        pressure,\n        spatial_velocity,\n        lorentz_factor,\n        magnetic_field,\n    )\n\n\ndef tilde_phi_flux(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Fluxes.tilde_phi_flux(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        lapse,\n        shift,\n        sqrt_det_spatial_metric,\n        spatial_metric,\n        inv_spatial_metric,\n        pressure,\n        spatial_velocity,\n        lorentz_factor,\n        magnetic_field,\n    )\n\n\ndef source_tilde_d(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return 0.0 * lapse\n\n\ndef source_tilde_ye(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return 0.0 * lapse\n\n\ndef source_tilde_tau(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Sources.source_tilde_tau(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        spatial_velocity,\n        magnetic_field,\n        rest_mass_density,\n        electron_fraction,\n        specific_enthalpy,\n        lorentz_factor,\n        pressure,\n        lapse,\n        d_lapse,\n        d_shift,\n        spatial_metric,\n        d_spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n        extrinsic_curvature,\n        constraint_damping_parameter,\n    )\n\n\ndef source_tilde_s(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Sources.source_tilde_s(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        spatial_velocity,\n        magnetic_field,\n        rest_mass_density,\n        electron_fraction,\n        specific_enthalpy,\n        lorentz_factor,\n        pressure,\n        lapse,\n        d_lapse,\n        d_shift,\n        spatial_metric,\n        d_spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n        extrinsic_curvature,\n        constraint_damping_parameter,\n    )\n\n\ndef source_tilde_b(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Sources.source_tilde_b(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        spatial_velocity,\n        magnetic_field,\n        rest_mass_density,\n        electron_fraction,\n        specific_enthalpy,\n        lorentz_factor,\n        pressure,\n        lapse,\n        d_lapse,\n        d_shift,\n        spatial_metric,\n        d_spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n        extrinsic_curvature,\n        constraint_damping_parameter,\n    )\n\n\ndef source_tilde_phi(\n    tilde_d,\n    tilde_ye,\n    tilde_tau,\n    tilde_s,\n    tilde_b,\n    tilde_phi,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    pressure,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    rest_mass_density,\n    electron_fraction,\n    specific_enthalpy,\n    extrinsic_curvature,\n    constraint_damping_parameter,\n):\n    return Sources.source_tilde_phi(\n        tilde_d,\n        tilde_ye,\n        tilde_tau,\n        tilde_s,\n        tilde_b,\n        tilde_phi,\n        spatial_velocity,\n        magnetic_field,\n        rest_mass_density,\n        electron_fraction,\n        specific_enthalpy,\n        lorentz_factor,\n        pressure,\n        lapse,\n        d_lapse,\n        d_shift,\n        spatial_metric,\n        d_spatial_metric,\n        inv_spatial_metric,\n        sqrt_det_spatial_metric,\n        extrinsic_curvature,\n        constraint_damping_parameter,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/ValenciaDivClean/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/GrMhd/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryConditions/DemandOutgoingCharSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef error(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    use_polytropic_eos,\n):\n    if use_polytropic_eos:\n        polytropic_constant = 1.4\n        polytropic_exponent = 5.0 / 3.0\n        sound_speed = np.sqrt(\n            polytropic_constant\n            * polytropic_exponent\n            * pow(int_mass_density, polytropic_exponent - 1.0)\n        )\n    else:\n        adiabatic_index = 1.3\n        chi = int_specific_internal_energy * (adiabatic_index - 1.0)\n        kappa_times_p_over_rho_squared = (\n            adiabatic_index - 1.0\n        ) ** 2 * int_specific_internal_energy\n        sound_speed = np.sqrt(chi + kappa_times_p_over_rho_squared)\n\n    normal_dot_velocity = np.einsum(\"i,i\", int_velocity, normal_covector)\n\n    if face_mesh_velocity is None:\n        min_char_speed = normal_dot_velocity - sound_speed\n    else:\n        normal_dot_mesh_velocity = np.einsum(\n            \"i,i\", face_mesh_velocity, normal_covector\n        )\n\n        min_char_speed = (\n            normal_dot_velocity - normal_dot_mesh_velocity - sound_speed\n        )\n\n    if min_char_speed < 0.0:\n        return \"DemandOutgoingCharSpeeds boundary condition violated\"\n\n    return None\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryConditions/DirichletAnalytic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Evolution.Systems.NewtonianEuler.TimeDerivative as flux\nimport numpy as np\nimport PointwiseFunctions.AnalyticSolutions.Hydro.SmoothFlow as hydro\n\n\ndef soln_error(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    return None\n\n\n_soln_pressure = 1.0\n_soln_adiabatic_index = 5.0 / 3.0\n_soln_perturbation_size = 0.2\n\n\ndef _soln_mean_velocity(dim):\n    mean_v = []\n    for i in range(0, dim):\n        mean_v.append(0.9 - i * 0.5)\n    return np.asarray(mean_v)\n\n\ndef _soln_wave_vector(dim):\n    wave_vector = []\n    for i in range(0, dim):\n        wave_vector.append(0.1 + i)\n    return np.asarray(wave_vector)\n\n\ndef soln_mass_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    return hydro.rest_mass_density(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef soln_momentum_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    return hydro.rest_mass_density(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    ) * hydro.spatial_velocity(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef soln_energy_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    velocity = hydro.spatial_velocity(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n    int_energy = hydro.specific_internal_energy(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n    return hydro.rest_mass_density(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    ) * (0.5 * np.dot(velocity, velocity) + int_energy)\n\n\ndef soln_flux_mass_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    return soln_momentum_density(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n\n\ndef soln_flux_momentum_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    velocity = hydro.spatial_velocity(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n    pressure = hydro.pressure(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n    return flux.momentum_density_flux_impl(\n        soln_momentum_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        ),\n        soln_energy_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        ),\n        velocity,\n        pressure,\n    )\n\n\ndef soln_flux_energy_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    velocity = hydro.spatial_velocity(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n    pressure = hydro.pressure(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n    return flux.energy_density_flux_impl(\n        soln_momentum_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        ),\n        soln_energy_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        ),\n        velocity,\n        pressure,\n    )\n\n\ndef soln_velocity(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    return hydro.spatial_velocity(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef soln_specific_internal_energy(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    return hydro.specific_internal_energy(\n        coords,\n        time,\n        _soln_mean_velocity(dim),\n        _soln_wave_vector(dim),\n        _soln_pressure,\n        _soln_adiabatic_index,\n        _soln_perturbation_size,\n    )\n\n\ndef data_error(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    return None\n\n\n_data_adiabatic_index = 1.4\n_data_strip_bimedian_height = 0.5\n_data_strip_thickness = 0.5\n_data_strip_density = 2.0\n_data_strip_velocity = 0.5\n_data_background_density = 1.0\n_data_background_velocity = -0.5\n_data_pressure = 2.5\n_data_perturb_amplitude = 0.1\n_data_perturb_width = 0.03\n\n\ndef data_mass_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    if (\n        np.abs(coords[-1] - _data_strip_bimedian_height)\n        < 0.5 * _data_strip_thickness\n    ):\n        return _data_strip_density\n    else:\n        return _data_background_density\n\n\ndef data_velocity(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    velocity = np.zeros([dim])\n    if (\n        np.abs(coords[-1] - _data_strip_bimedian_height)\n        < 0.5 * _data_strip_thickness\n    ):\n        velocity[0] = _data_strip_velocity\n    else:\n        velocity[0] = _data_background_velocity\n\n    one_over_two_sigma_squared = 0.5 / (_data_perturb_width) ** 2\n    strip_lower_bound = (\n        _data_strip_bimedian_height - 0.5 * _data_strip_thickness\n    )\n    strip_upper_bound = (\n        _data_strip_bimedian_height + 0.5 * _data_strip_thickness\n    )\n    velocity[-1] = np.exp(\n        -one_over_two_sigma_squared * (coords[-1] - strip_lower_bound) ** 2\n    ) + np.exp(\n        -one_over_two_sigma_squared * (coords[-1] - strip_upper_bound) ** 2\n    )\n    velocity[-1] *= _data_perturb_amplitude * np.sin(4.0 * np.pi * coords[0])\n    return np.asarray(velocity)\n\n\ndef data_momentum_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    return data_mass_density(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    ) * data_velocity(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n\n\ndef data_pressure(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    return _data_pressure\n\n\ndef data_specific_internal_energy(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    return (\n        1.0\n        / (_data_adiabatic_index - 1.0)\n        * data_pressure(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        )\n        / data_mass_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        )\n    )\n\n\ndef data_energy_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    velocity = data_velocity(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n    int_energy = data_specific_internal_energy(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n    return data_mass_density(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    ) * (0.5 * np.dot(velocity, velocity) + int_energy)\n\n\ndef data_flux_mass_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    return data_momentum_density(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n\n\ndef data_flux_momentum_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    velocity = data_velocity(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n    pressure = data_pressure(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n    return flux.momentum_density_flux_impl(\n        data_momentum_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        ),\n        data_energy_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        ),\n        velocity,\n        pressure,\n    )\n\n\ndef data_flux_energy_density(\n    face_mesh_velocity, outward_directed_normal_covector, coords, time\n):\n    dim = len(coords)\n    velocity = data_velocity(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n    pressure = data_pressure(\n        face_mesh_velocity, outward_directed_normal_covector, coords, time\n    )\n    return flux.energy_density_flux_impl(\n        data_momentum_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        ),\n        data_energy_density(\n            face_mesh_velocity,\n            outward_directed_normal_covector,\n            coords,\n            time,\n        ),\n        velocity,\n        pressure,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryConditions/Reflection.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Evolution.Systems.NewtonianEuler.ConservativeFromPrimitive as ConservativeFromPrimitive\nimport Evolution.Systems.NewtonianEuler.TimeDerivative as Flux\nimport numpy as np\n\n\ndef error(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    return None\n\n\ndef velocity(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    velocity_dot_normal = np.einsum(\"i,i\", normal_covector, int_velocity)\n    velocity = int_velocity - 2.0 * velocity_dot_normal * normal_covector\n\n    if face_mesh_velocity is None:\n        return velocity\n    else:\n        face_mesh_velocity_dot_normal = np.einsum(\n            \"i,i\", normal_covector, face_mesh_velocity\n        )\n        return velocity + 2.0 * face_mesh_velocity_dot_normal * normal_covector\n\n\ndef mass_density_cons(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    return int_mass_density\n\n\ndef momentum_density(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    return ConservativeFromPrimitive.momentum_density(\n        int_mass_density,\n        velocity(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        int_specific_internal_energy,\n    )\n\n\ndef energy_density(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    return ConservativeFromPrimitive.energy_density(\n        int_mass_density,\n        velocity(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        int_specific_internal_energy,\n    )\n\n\ndef flux_mass_density(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    return Flux.mass_density_cons_flux_impl(\n        momentum_density(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        energy_density(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        velocity(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        int_pressure,\n    )\n\n\ndef flux_momentum_density(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    return Flux.momentum_density_flux_impl(\n        momentum_density(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        energy_density(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        velocity(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        int_pressure,\n    )\n\n\ndef flux_energy_density(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    return Flux.energy_density_flux_impl(\n        momentum_density(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        energy_density(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        velocity(\n            face_mesh_velocity,\n            normal_covector,\n            int_mass_density,\n            int_velocity,\n            int_specific_internal_energy,\n            int_pressure,\n        ),\n        int_pressure,\n    )\n\n\ndef specific_internal_energy(\n    face_mesh_velocity,\n    normal_covector,\n    int_mass_density,\n    int_velocity,\n    int_specific_internal_energy,\n    int_pressure,\n):\n    return int_specific_internal_energy\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\n\nstruct ConvertPolytropic {\n  using unpacked_container = bool;\n  using packed_container = EquationsOfState::EquationOfState<false, 2>;\n  using packed_type = bool;\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return true;\n  }\n\n  [[noreturn]] static inline void pack(\n      const gsl::not_null<packed_container*> /*packed*/,\n      const unpacked_container& /*unpacked*/,\n      const size_t /*grid_point_index*/) {\n    ERROR(\"Should not be converting an EOS from an unpacked to a packed type\");\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct ConvertIdeal {\n  using unpacked_container = bool;\n  using packed_container = EquationsOfState::EquationOfState<false, 2>;\n  using packed_type = bool;\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return false;\n  }\n\n  [[noreturn]] static inline void pack(\n      const gsl::not_null<packed_container*> /*packed*/,\n      const unpacked_container& /*unpacked*/,\n      const size_t /*grid_point_index*/) {\n    ERROR(\"Should not be converting an EOS from an unpacked to a packed type\");\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct DummyInitialData {\n  using argument_tags = tmpl::list<>;\n  struct source_term_type {\n    using sourced_variables = tmpl::list<>;\n    using argument_tags = tmpl::list<>;\n  };\n};\n\ntemplate <size_t Dim, typename EosType>\nvoid test(EosType& eos) {\n  MAKE_GENERATOR(gen);\n\n  auto box = db::create<db::AddSimpleTags<\n      hydro::Tags::EquationOfState<false, EosType::thermodynamic_dim>>>(\n      eos.get_clone());\n\n  const tuples::TaggedTuple<\n      helpers::Tags::Range<hydro::Tags::RestMassDensity<DataVector>>,\n      helpers::Tags::Range<hydro::Tags::SpecificInternalEnergy<DataVector>>>\n      ranges{std::array{1.0e-30, 1.0}, std::array{1.0e-30, 1.0}};\n\n  helpers::test_boundary_condition_with_python<\n      NewtonianEuler::BoundaryConditions::DemandOutgoingCharSpeeds<Dim>,\n      NewtonianEuler::BoundaryConditions::BoundaryCondition<Dim>,\n      NewtonianEuler::System<Dim>,\n      tmpl::list<NewtonianEuler::BoundaryCorrections::Rusanov<Dim>>,\n      tmpl::list<tmpl::conditional_t<\n          std::is_same_v<EosType, EquationsOfState::IdealFluid<false>>,\n          ConvertIdeal, ConvertPolytropic>>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.NewtonianEuler.BoundaryConditions.\"\n      \"DemandOutgoingCharSpeeds\",\n      tuples::TaggedTuple<helpers::Tags::PythonFunctionForErrorMessage<>>{\n          \"error\"},\n      \"DemandOutgoingCharSpeeds:\\n\", Index<Dim - 1>{Dim == 1 ? 1 : 5}, box,\n      ranges);\n}  // namespace\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.NewtonianEuler.BoundaryConditions.DemandOutgoingCharSpeeds\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  EquationsOfState::Barotropic2D eos_polytrope{\n      EquationsOfState::PolytropicFluid<false>{1.4, 5.0 / 3.0}};\n  test<1>(eos_polytrope);\n  test<2>(eos_polytrope);\n  test<3>(eos_polytrope);\n\n  EquationsOfState::IdealFluid<false> eos_ideal{1.3};\n  test<1>(eos_ideal);\n  test<2>(eos_ideal);\n  test<3>(eos_ideal);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/AllSolutions.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/NoSource.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/KhInstability.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<NewtonianEuler::BoundaryConditions::BoundaryCondition<Dim>,\n                   tmpl::list<NewtonianEuler::BoundaryConditions::\n                                  DirichletAnalytic<Dim>>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   NewtonianEuler::InitialData::initial_data_list<Dim>>>;\n  };\n};\n\ntemplate <size_t Dim>\nstruct ConvertSmoothFlow {\n  using unpacked_container = int;\n  using packed_container = NewtonianEuler::Solutions::SmoothFlow<Dim>;\n  using packed_type = double;\n\n  static packed_container create_container() {\n    std::array<double, Dim> wave_vector{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(wave_vector, i) = 0.1 + i;\n    }\n    std::array<double, Dim> mean_velocity{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(mean_velocity, i) = 0.9 - i * 0.5;\n    }\n    const double pressure = 1.0;\n    const double adiabatic_index = 5.0 / 3.0;\n    const double perturbation_size = 0.2;\n    return {mean_velocity, wave_vector, pressure, adiabatic_index,\n            perturbation_size};\n  }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return Dim;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\ntemplate <size_t Dim>\nstruct ConvertKhInstability {\n  using unpacked_container = int;\n  using packed_container = NewtonianEuler::AnalyticData::KhInstability<Dim>;\n  using packed_type = double;\n\n  static packed_container create_container() {\n    const double adiabatic_index = 1.4;\n    const double strip_bimedian_height = 0.5;\n    const double strip_thickness = 0.5;\n    const double strip_density = 2.0;\n    const double strip_velocity = 0.5;\n    const double background_density = 1.0;\n    const double background_velocity = -0.5;\n    const double pressure = 2.5;\n    const double perturb_amplitude = 0.1;\n    const double perturb_width = 0.03;\n    return {adiabatic_index,     strip_bimedian_height,\n            strip_thickness,     strip_density,\n            strip_velocity,      background_density,\n            background_velocity, pressure,\n            perturb_amplitude,   perturb_width};\n  }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return Dim;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  register_classes_with_charm(\n      NewtonianEuler::InitialData::initial_data_list<Dim>{});\n  MAKE_GENERATOR(gen);\n  const auto box_analytic_soln = db::create<db::AddSimpleTags<\n      Tags::Time,\n      Tags::AnalyticSolution<NewtonianEuler::Solutions::SmoothFlow<Dim>>>>(\n      0.5, ConvertSmoothFlow<Dim>::create_container());\n\n  helpers::test_boundary_condition_with_python<\n      NewtonianEuler::BoundaryConditions::DirichletAnalytic<Dim>,\n      NewtonianEuler::BoundaryConditions::BoundaryCondition<Dim>,\n      NewtonianEuler::System<Dim>,\n      tmpl::list<NewtonianEuler::BoundaryCorrections::Rusanov<Dim>>,\n      tmpl::list<ConvertSmoothFlow<Dim>>, tmpl::list<>, Metavariables<Dim>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.NewtonianEuler.BoundaryConditions.DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              NewtonianEuler::Tags::MassDensityCons>,\n          helpers::Tags::PythonFunctionName<\n              NewtonianEuler::Tags::MomentumDensity<Dim>>,\n          helpers::Tags::PythonFunctionName<\n              NewtonianEuler::Tags::EnergyDensity>,\n\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<NewtonianEuler::Tags::MassDensityCons,\n                           tmpl::size_t<Dim>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<NewtonianEuler::Tags::MomentumDensity<Dim>,\n                           tmpl::size_t<Dim>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<NewtonianEuler::Tags::EnergyDensity,\n                           tmpl::size_t<Dim>, Frame::Inertial>>,\n\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::SpatialVelocity<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::SpecificInternalEnergy<DataVector>>>{\n          \"soln_error\", \"soln_mass_density\", \"soln_momentum_density\",\n          \"soln_energy_density\", \"soln_flux_mass_density\",\n          \"soln_flux_momentum_density\", \"soln_flux_energy_density\",\n          \"soln_velocity\", \"soln_specific_internal_energy\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    SmoothFlow:\\n\"\n      \"      MeanVelocity: [0.9\" +\n          (Dim == 1   ? std::string{}\n           : Dim == 2 ? std::string{\", 0.4\"}\n                      : std::string{\", 0.4, -0.1\"}) +\n          \"]\\n\"\n          \"      WaveVector: [0.1\" +\n          (Dim == 1   ? std::string{}\n           : Dim == 2 ? std::string{\", 1.1\"}\n                      : std::string{\", 1.1, 2.1\"}) +\n          \"]\\n\"\n          \"      Pressure: 1.0\\n\"\n          \"      AdiabaticIndex: 1.66666666666667\\n\"\n          \"      PerturbationSize: 0.2\\n\",\n      Index<Dim - 1>{Dim == 1 ? 1 : 5}, box_analytic_soln,\n      tuples::TaggedTuple<>{});\n\n  if constexpr (Dim > 1) {\n    // KH-instability is only implemented in 2d and 3d. If/when we have 1d\n    // analytic data that should also be tested.\n    const auto box_analytic_data = db::create<db::AddSimpleTags<\n        Tags::Time,\n        Tags::AnalyticData<NewtonianEuler::AnalyticData::KhInstability<Dim>>>>(\n        0.5, ConvertKhInstability<Dim>::create_container());\n\n    helpers::test_boundary_condition_with_python<\n        NewtonianEuler::BoundaryConditions::DirichletAnalytic<Dim>,\n        NewtonianEuler::BoundaryConditions::BoundaryCondition<Dim>,\n        NewtonianEuler::System<Dim>,\n        tmpl::list<NewtonianEuler::BoundaryCorrections::Rusanov<Dim>>,\n        tmpl::list<ConvertKhInstability<Dim>>, tmpl::list<>,\n        Metavariables<Dim>>(\n        make_not_null(&gen),\n        \"Evolution.Systems.NewtonianEuler.BoundaryConditions.DirichletAnalytic\",\n        tuples::TaggedTuple<\n            helpers::Tags::PythonFunctionForErrorMessage<>,\n            helpers::Tags::PythonFunctionName<\n                NewtonianEuler::Tags::MassDensityCons>,\n            helpers::Tags::PythonFunctionName<\n                NewtonianEuler::Tags::MomentumDensity<Dim>>,\n            helpers::Tags::PythonFunctionName<\n                NewtonianEuler::Tags::EnergyDensity>,\n\n            helpers::Tags::PythonFunctionName<\n                ::Tags::Flux<NewtonianEuler::Tags::MassDensityCons,\n                             tmpl::size_t<Dim>, Frame::Inertial>>,\n            helpers::Tags::PythonFunctionName<\n                ::Tags::Flux<NewtonianEuler::Tags::MomentumDensity<Dim>,\n                             tmpl::size_t<Dim>, Frame::Inertial>>,\n            helpers::Tags::PythonFunctionName<\n                ::Tags::Flux<NewtonianEuler::Tags::EnergyDensity,\n                             tmpl::size_t<Dim>, Frame::Inertial>>,\n\n            helpers::Tags::PythonFunctionName<\n                hydro::Tags::SpatialVelocity<DataVector, Dim>>,\n            helpers::Tags::PythonFunctionName<\n                hydro::Tags::SpecificInternalEnergy<DataVector>>>{\n            \"data_error\", \"data_mass_density\", \"data_momentum_density\",\n            \"data_energy_density\", \"data_flux_mass_density\",\n            \"data_flux_momentum_density\", \"data_flux_energy_density\",\n            \"data_velocity\", \"data_specific_internal_energy\"},\n        \"DirichletAnalytic:\\n\"\n        \"  AnalyticPrescription:\\n\"\n        \"    KhInstability:\\n\"\n        \"      AdiabaticIndex: 1.4\\n\"\n        \"      StripBimedianHeight: 0.5\\n\"\n        \"      StripThickness: 0.5\\n\"\n        \"      StripDensity: 2.0\\n\"\n        \"      StripVelocity: 0.5\\n\"\n        \"      BackgroundDensity: 1.0\\n\"\n        \"      BackgroundVelocity: -0.5\\n\"\n        \"      Pressure: 2.5\\n\"\n        \"      PerturbAmplitude: 0.1\\n\"\n        \"      PerturbWidth: 0.03\\n\",\n        Index<Dim - 1>{Dim == 1 ? 1 : 5}, box_analytic_data,\n        tuples::TaggedTuple<>{});\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NewtonianEuler.BoundaryConditions.DirichletAnalytic\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryConditions/Test_Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/Factory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  helpers::test_periodic_condition<\n      domain::BoundaryConditions::Periodic<\n          NewtonianEuler::BoundaryConditions::BoundaryCondition<Dim>>,\n      NewtonianEuler::BoundaryConditions::BoundaryCondition<Dim>>(\n      \"Periodic:\\n\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NewtonianEuler.BoundaryConditions.Periodic\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryConditions/Test_Reflection.cpp",
    "content": "// Distributed under the MIT License\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryConditions/Reflection.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace helpers = TestHelpers::evolution::dg;\n\ntemplate <size_t Dim>\nvoid test() {\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      NewtonianEuler::BoundaryConditions::Reflection<Dim>,\n      NewtonianEuler::BoundaryConditions::BoundaryCondition<Dim>,\n      NewtonianEuler::System<Dim>,\n      tmpl::list<NewtonianEuler::BoundaryCorrections::Rusanov<Dim>>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.NewtonianEuler.BoundaryConditions.Reflection\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<\n              NewtonianEuler::Tags::MassDensityCons>,\n          helpers::Tags::PythonFunctionName<\n              NewtonianEuler::Tags::MomentumDensity<Dim>>,\n          helpers::Tags::PythonFunctionName<\n              NewtonianEuler::Tags::EnergyDensity>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<NewtonianEuler::Tags::MassDensityCons,\n                           tmpl::size_t<Dim>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<NewtonianEuler::Tags::MomentumDensity<Dim>,\n                           tmpl::size_t<Dim>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<NewtonianEuler::Tags::EnergyDensity,\n                           tmpl::size_t<Dim>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::SpatialVelocity<DataVector, Dim>>,\n          helpers::Tags::PythonFunctionName<\n              hydro::Tags::SpecificInternalEnergy<DataVector>>>{\n          \"error\", \"mass_density_cons\", \"momentum_density\", \"energy_density\",\n          \"flux_mass_density\", \"flux_momentum_density\", \"flux_energy_density\",\n          \"velocity\", \"specific_internal_energy\"},\n      \"Reflection:\\n\", Index<Dim - 1>{Dim == 1 ? 1 : 5},\n      db::DataBox<tmpl::list<>>{}, tuples::TaggedTuple<>{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NewtonianEuler.BoundaryConditions.Reflection\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryConditions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hll.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    mass_density,\n    momentum_density,\n    energy_density,\n    flux_mass_density,\n    flux_momentum_density,\n    flux_energy_density,\n    velocity,\n    specific_internal_energy,\n    normal_covector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n    use_polytropic_fluid,\n):\n    velocity_dot_normal = np.einsum(\"i,i\", normal_covector, velocity)\n    if use_polytropic_fluid:\n        polytropic_constant = 1.0e-3\n        polytropic_exponent = 2.0\n        sound_speed = np.sqrt(\n            polytropic_constant\n            * polytropic_exponent\n            * pow(mass_density, polytropic_exponent - 1.0)\n        )\n    else:\n        adiabatic_index = 1.3\n        chi = specific_internal_energy * (adiabatic_index - 1.0)\n        kappa_times_p_over_rho_squared = (\n            adiabatic_index - 1.0\n        ) ** 2 * specific_internal_energy\n        sound_speed = np.sqrt(chi + kappa_times_p_over_rho_squared)\n\n    return (\n        mass_density,\n        momentum_density,\n        energy_density,\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_mass_density)),\n        np.asarray(\n            np.einsum(\"i,ij->j\", normal_covector, flux_momentum_density)\n        ),\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_energy_density)),\n        np.asarray(\n            velocity_dot_normal + sound_speed\n            if normal_dot_mesh_velocity is None\n            else velocity_dot_normal + sound_speed - normal_dot_mesh_velocity\n        ),\n        np.asarray(\n            velocity_dot_normal - sound_speed\n            if normal_dot_mesh_velocity is None\n            else velocity_dot_normal - sound_speed - normal_dot_mesh_velocity\n        ),\n    )\n\n\ndef dg_boundary_terms(\n    interior_mass_density,\n    interior_momentum_density,\n    interior_energy_density,\n    interior_normal_dot_flux_mass_density,\n    interior_normal_dot_flux_momentum_density,\n    interior_normal_dot_flux_energy_density,\n    interior_largest_outgoing_char_speed,\n    interior_largest_ingoing_char_speed,\n    exterior_mass_density,\n    exterior_momentum_density,\n    exterior_energy_density,\n    exterior_normal_dot_flux_mass_density,\n    exterior_normal_dot_flux_momentum_density,\n    exterior_normal_dot_flux_energy_density,\n    exterior_largest_outgoing_char_speed,\n    exterior_largest_ingoing_char_speed,\n    use_strong_form,\n):\n    lambda_max = np.maximum(\n        0.0,\n        np.maximum(\n            interior_largest_outgoing_char_speed,\n            -exterior_largest_ingoing_char_speed,\n        ),\n    )\n    lambda_min = np.minimum(\n        0.0,\n        np.minimum(\n            interior_largest_ingoing_char_speed,\n            -exterior_largest_outgoing_char_speed,\n        ),\n    )\n\n    lambda_interior = lambda_min if use_strong_form else lambda_max\n\n    # Use scope and locals() to get arguments into the eval context below\n    scope = locals()\n\n    def impl(var_name):\n        return np.asarray(\n            (\n                lambda_interior\n                * eval(\"interior_normal_dot_flux_\" + var_name, scope)\n                + lambda_min\n                * eval(\"exterior_normal_dot_flux_\" + var_name, scope)\n                + lambda_max\n                * lambda_min\n                * (\n                    eval(\"exterior_\" + var_name, scope)\n                    - eval(\"interior_\" + var_name, scope)\n                )\n            )\n            / (lambda_max - lambda_min)\n        )\n\n    return (\n        impl(\"mass_density\"),\n        impl(\"momentum_density\"),\n        impl(\"energy_density\"),\n    )\n\n\n# def dg_boundary_terms_momentum_density(\n#     interior_mass_density,\n#     interior_momentum_density,\n#     interior_energy_density,\n#     interior_normal_dot_flux_mass_density,\n#     interior_normal_dot_flux_momentum_density,\n#     interior_normal_dot_flux_energy_density,\n#     interior_largest_outgoing_char_speed,\n#     interior_largest_ingoing_char_speed,\n#     exterior_mass_density,\n#     exterior_momentum_density,\n#     exterior_energy_density,\n#     exterior_normal_dot_flux_mass_density,\n#     exterior_normal_dot_flux_momentum_density,\n#     exterior_normal_dot_flux_energy_density,\n#     exterior_largest_outgoing_char_speed,\n#     exterior_largest_ingoing_char_speed,\n#     use_strong_form,\n# ):\n#     lambda_max = np.maximum(\n#         0.0,\n#         np.maximum(\n#             interior_largest_outgoing_char_speed,\n#             -exterior_largest_ingoing_char_speed,\n#         ),\n#     )\n#     lambda_min = np.minimum(\n#         0.0,\n#         np.minimum(\n#             interior_largest_ingoing_char_speed,\n#             -exterior_largest_outgoing_char_speed,\n#         ),\n#     )\n\n#     if use_strong_form:\n#         return (\n#             lambda_min\n#             * (\n#                 interior_normal_dot_flux_momentum_density\n#                 + exterior_normal_dot_flux_momentum_density\n#             )\n#             + lambda_max\n#             * lambda_min\n#             * (exterior_momentum_density - interior_momentum_density)\n#         ) / (lambda_max - lambda_min)\n#     else:\n#         return (\n#             (\n#                 lambda_max * interior_normal_dot_flux_momentum_density\n#                 + lambda_min * exterior_normal_dot_flux_momentum_density\n#             )\n#             + lambda_max\n#             * lambda_min\n#             * (exterior_momentum_density - interior_momentum_density)\n#         ) / (lambda_max - lambda_min)\n\n\n# def dg_boundary_terms_energy_density(\n#     interior_mass_density,\n#     interior_momentum_density,\n#     interior_energy_density,\n#     interior_normal_dot_flux_mass_density,\n#     interior_normal_dot_flux_momentum_density,\n#     interior_normal_dot_flux_energy_density,\n#     interior_largest_outgoing_char_speed,\n#     interior_largest_ingoing_char_speed,\n#     exterior_mass_density,\n#     exterior_momentum_density,\n#     exterior_energy_density,\n#     exterior_normal_dot_flux_mass_density,\n#     exterior_normal_dot_flux_momentum_density,\n#     exterior_normal_dot_flux_energy_density,\n#     exterior_largest_outgoing_char_speed,\n#     exterior_largest_ingoing_char_speed,\n#     use_strong_form,\n# ):\n#     lambda_max = np.maximum(\n#         0.0,\n#         np.maximum(\n#             interior_largest_outgoing_char_speed,\n#             -exterior_largest_ingoing_char_speed,\n#         ),\n#     )\n#     lambda_min = np.minimum(\n#         0.0,\n#         np.minimum(\n#             interior_largest_ingoing_char_speed,\n#             -exterior_largest_outgoing_char_speed,\n#         ),\n#     )\n\n#     if use_strong_form:\n#         return (\n#             lambda_min\n#             * (\n#                 interior_normal_dot_flux_energy_density\n#                 + exterior_normal_dot_flux_energy_density\n#             )\n#             + lambda_max\n#             * lambda_min\n#             * (exterior_energy_density - interior_energy_density)\n#         ) / (lambda_max - lambda_min)\n#     else:\n#         return (\n#             (\n#                 lambda_max * interior_normal_dot_flux_energy_density\n#                 + lambda_min * exterior_normal_dot_flux_energy_density\n#             )\n#             + lambda_max\n#             * lambda_min\n#             * (exterior_energy_density - interior_energy_density)\n#         ) / (lambda_max - lambda_min)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hllc.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    mass_density,\n    momentum_density,\n    energy_density,\n    flux_mass_density,\n    flux_momentum_density,\n    flux_energy_density,\n    velocity,\n    specific_internal_energy,\n    normal_covector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n    use_polytropic_fluid,\n):\n    velocity_dot_normal = np.einsum(\"i,i\", normal_covector, velocity)\n    if use_polytropic_fluid:\n        polytropic_constant = 1.0e-3\n        polytropic_exponent = 2.0\n        sound_speed = np.sqrt(\n            polytropic_constant\n            * polytropic_exponent\n            * pow(mass_density, polytropic_exponent - 1.0)\n        )\n    else:\n        adiabatic_index = 1.3\n        chi = specific_internal_energy * (adiabatic_index - 1.0)\n        kappa_times_p_over_rho_squared = (\n            adiabatic_index - 1.0\n        ) ** 2 * specific_internal_energy\n        sound_speed = np.sqrt(chi + kappa_times_p_over_rho_squared)\n\n    if use_polytropic_fluid:\n        polytropic_constant = 1.0e-3\n        polytropic_exponent = 2.0\n        pressure = polytropic_constant * pow(mass_density, polytropic_exponent)\n    else:\n        adiabatic_index = 1.3\n        pressure = (\n            mass_density * specific_internal_energy * (adiabatic_index - 1.0)\n        )\n\n    return (\n        mass_density,\n        momentum_density,\n        energy_density,\n        np.asarray(pressure),\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_mass_density)),\n        np.asarray(\n            np.einsum(\"i,ij->j\", normal_covector, flux_momentum_density)\n        ),\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_energy_density)),\n        normal_covector,\n        np.asarray(\n            np.einsum(\"i,i\", normal_covector, velocity)\n            if normal_dot_mesh_velocity is None\n            else (\n                np.einsum(\"i,i\", normal_covector, velocity)\n                - normal_dot_mesh_velocity\n            )\n        ),\n        np.asarray(\n            velocity_dot_normal + sound_speed\n            if normal_dot_mesh_velocity is None\n            else velocity_dot_normal + sound_speed - normal_dot_mesh_velocity\n        ),\n        np.asarray(\n            velocity_dot_normal - sound_speed\n            if normal_dot_mesh_velocity is None\n            else velocity_dot_normal - sound_speed - normal_dot_mesh_velocity\n        ),\n    )\n\n\ndef dg_boundary_terms_mass_density(\n    interior_mass_density,\n    interior_momentum_density,\n    interior_energy_density,\n    interior_pressure,\n    interior_normal_dot_flux_mass_density,\n    interior_normal_dot_flux_momentum_density,\n    interior_normal_dot_flux_energy_density,\n    interior_interface_unit_normal,\n    interior_normal_dot_velocity,\n    interior_largest_outgoing_char_speed,\n    interior_largest_ingoing_char_speed,\n    exterior_mass_density,\n    exterior_momentum_density,\n    exterior_energy_density,\n    exterior_pressure,\n    exterior_normal_dot_flux_mass_density,\n    exterior_normal_dot_flux_momentum_density,\n    exterior_normal_dot_flux_energy_density,\n    exterior_interface_unit_normal,\n    exterior_normal_dot_velocity,\n    exterior_largest_outgoing_char_speed,\n    exterior_largest_ingoing_char_speed,\n    use_strong_form,\n):\n    lambda_max = np.maximum(\n        0.0,\n        np.maximum(\n            interior_largest_outgoing_char_speed,\n            -exterior_largest_ingoing_char_speed,\n        ),\n    )\n    lambda_min = np.minimum(\n        0.0,\n        np.minimum(\n            interior_largest_ingoing_char_speed,\n            -exterior_largest_outgoing_char_speed,\n        ),\n    )\n\n    lambda_star = (\n        exterior_pressure\n        - interior_pressure\n        + interior_mass_density\n        * interior_normal_dot_velocity\n        * (lambda_min - interior_normal_dot_velocity)\n        + exterior_mass_density\n        * exterior_normal_dot_velocity\n        * (lambda_max + exterior_normal_dot_velocity)\n    ) / (\n        interior_mass_density * (lambda_min - interior_normal_dot_velocity)\n        - exterior_mass_density * (lambda_max + exterior_normal_dot_velocity)\n    )\n\n    prefactor_int = (lambda_min - interior_normal_dot_velocity) / (\n        lambda_min - lambda_star\n    )\n    prefactor_ext = (lambda_max + exterior_normal_dot_velocity) / (\n        lambda_max - lambda_star\n    )\n\n    if use_strong_form:\n        return np.where(\n            lambda_star >= 0.0,\n            lambda_min * (prefactor_int - 1.0) * interior_mass_density,\n            -interior_normal_dot_flux_mass_density\n            - exterior_normal_dot_flux_mass_density\n            + lambda_max * (prefactor_ext - 1.0) * exterior_mass_density,\n        )\n    else:\n        return np.where(\n            lambda_star >= 0.0,\n            interior_normal_dot_flux_mass_density\n            + lambda_min * (prefactor_int - 1.0) * interior_mass_density,\n            -exterior_normal_dot_flux_mass_density\n            + lambda_max * (prefactor_ext - 1.0) * exterior_mass_density,\n        )\n\n\ndef dg_boundary_terms_momentum_density(\n    interior_mass_density,\n    interior_momentum_density,\n    interior_energy_density,\n    interior_pressure,\n    interior_normal_dot_flux_mass_density,\n    interior_normal_dot_flux_momentum_density,\n    interior_normal_dot_flux_energy_density,\n    interior_interface_unit_normal,\n    interior_normal_dot_velocity,\n    interior_largest_outgoing_char_speed,\n    interior_largest_ingoing_char_speed,\n    exterior_mass_density,\n    exterior_momentum_density,\n    exterior_energy_density,\n    exterior_pressure,\n    exterior_normal_dot_flux_mass_density,\n    exterior_normal_dot_flux_momentum_density,\n    exterior_normal_dot_flux_energy_density,\n    exterior_interface_unit_normal,\n    exterior_normal_dot_velocity,\n    exterior_largest_outgoing_char_speed,\n    exterior_largest_ingoing_char_speed,\n    use_strong_form,\n):\n    lambda_max = np.maximum(\n        0.0,\n        np.maximum(\n            interior_largest_outgoing_char_speed,\n            -exterior_largest_ingoing_char_speed,\n        ),\n    )\n    lambda_min = np.minimum(\n        0.0,\n        np.minimum(\n            interior_largest_ingoing_char_speed,\n            -exterior_largest_outgoing_char_speed,\n        ),\n    )\n\n    lambda_star = (\n        exterior_pressure\n        - interior_pressure\n        + interior_mass_density\n        * interior_normal_dot_velocity\n        * (lambda_min - interior_normal_dot_velocity)\n        + exterior_mass_density\n        * exterior_normal_dot_velocity\n        * (lambda_max + exterior_normal_dot_velocity)\n    ) / (\n        interior_mass_density * (lambda_min - interior_normal_dot_velocity)\n        - exterior_mass_density * (lambda_max + exterior_normal_dot_velocity)\n    )\n\n    prefactor_int = (lambda_min - interior_normal_dot_velocity) / (\n        lambda_min - lambda_star\n    )\n    prefactor_ext = (lambda_max + exterior_normal_dot_velocity) / (\n        lambda_max - lambda_star\n    )\n\n    if use_strong_form:\n        return np.where(\n            lambda_star >= 0.0,\n            lambda_min\n            * (\n                interior_mass_density\n                * prefactor_int\n                * (lambda_star - interior_normal_dot_velocity)\n                * interior_interface_unit_normal\n                + interior_momentum_density * (prefactor_int - 1.0)\n            ),\n            -interior_normal_dot_flux_momentum_density\n            - exterior_normal_dot_flux_momentum_density\n            + lambda_max\n            * (\n                exterior_mass_density\n                * prefactor_ext\n                * (lambda_star + exterior_normal_dot_velocity)\n                * (-exterior_interface_unit_normal)\n                + exterior_momentum_density * (prefactor_ext - 1.0)\n            ),\n        )\n    else:\n        return np.where(\n            lambda_star >= 0.0,\n            interior_normal_dot_flux_momentum_density\n            + lambda_min\n            * (\n                interior_mass_density\n                * prefactor_int\n                * (lambda_star - interior_normal_dot_velocity)\n                * interior_interface_unit_normal\n                + interior_momentum_density * (prefactor_int - 1.0)\n            ),\n            -exterior_normal_dot_flux_momentum_density\n            + lambda_max\n            * (\n                exterior_mass_density\n                * prefactor_ext\n                * (lambda_star + exterior_normal_dot_velocity)\n                * (-exterior_interface_unit_normal)\n                + exterior_momentum_density * (prefactor_ext - 1.0)\n            ),\n        )\n\n\ndef dg_boundary_terms_energy_density(\n    interior_mass_density,\n    interior_momentum_density,\n    interior_energy_density,\n    interior_pressure,\n    interior_normal_dot_flux_mass_density,\n    interior_normal_dot_flux_momentum_density,\n    interior_normal_dot_flux_energy_density,\n    interior_interface_unit_normal,\n    interior_normal_dot_velocity,\n    interior_largest_outgoing_char_speed,\n    interior_largest_ingoing_char_speed,\n    exterior_mass_density,\n    exterior_momentum_density,\n    exterior_energy_density,\n    exterior_pressure,\n    exterior_normal_dot_flux_mass_density,\n    exterior_normal_dot_flux_momentum_density,\n    exterior_normal_dot_flux_energy_density,\n    exterior_interface_unit_normal,\n    exterior_normal_dot_velocity,\n    exterior_largest_outgoing_char_speed,\n    exterior_largest_ingoing_char_speed,\n    use_strong_form,\n):\n    lambda_max = np.maximum(\n        0.0,\n        np.maximum(\n            interior_largest_outgoing_char_speed,\n            -exterior_largest_ingoing_char_speed,\n        ),\n    )\n    lambda_min = np.minimum(\n        0.0,\n        np.minimum(\n            interior_largest_ingoing_char_speed,\n            -exterior_largest_outgoing_char_speed,\n        ),\n    )\n\n    lambda_star = (\n        exterior_pressure\n        - interior_pressure\n        + interior_mass_density\n        * interior_normal_dot_velocity\n        * (lambda_min - interior_normal_dot_velocity)\n        + exterior_mass_density\n        * exterior_normal_dot_velocity\n        * (lambda_max + exterior_normal_dot_velocity)\n    ) / (\n        interior_mass_density * (lambda_min - interior_normal_dot_velocity)\n        - exterior_mass_density * (lambda_max + exterior_normal_dot_velocity)\n    )\n\n    prefactor_int = (lambda_min - interior_normal_dot_velocity) / (\n        lambda_min - lambda_star\n    )\n    prefactor_ext = (lambda_max + exterior_normal_dot_velocity) / (\n        lambda_max - lambda_star\n    )\n\n    if use_strong_form:\n        return np.where(\n            lambda_star >= 0.0,\n            lambda_min\n            * (\n                (prefactor_int - 1.0)\n                * (interior_energy_density + interior_pressure)\n                + prefactor_int\n                * interior_mass_density\n                * lambda_star\n                * (lambda_star - interior_normal_dot_velocity)\n            ),\n            -interior_normal_dot_flux_energy_density\n            - exterior_normal_dot_flux_energy_density\n            + lambda_max\n            * (\n                (prefactor_ext - 1.0)\n                * (exterior_energy_density + exterior_pressure)\n                + prefactor_ext\n                * exterior_mass_density\n                * lambda_star\n                * (lambda_star + exterior_normal_dot_velocity)\n            ),\n        )\n    else:\n        return np.where(\n            lambda_star >= 0.0,\n            interior_normal_dot_flux_energy_density\n            + lambda_min\n            * (\n                prefactor_int\n                * (\n                    interior_energy_density\n                    + interior_pressure\n                    * (lambda_star - interior_normal_dot_velocity)\n                    / (lambda_min - interior_normal_dot_velocity)\n                    + interior_mass_density\n                    * lambda_star\n                    * (lambda_star - interior_normal_dot_velocity)\n                )\n                - interior_energy_density\n            ),\n            -exterior_normal_dot_flux_energy_density\n            + lambda_max\n            * (\n                prefactor_ext\n                * (\n                    exterior_energy_density\n                    + exterior_pressure\n                    * (lambda_star + exterior_normal_dot_velocity)\n                    / (lambda_max + exterior_normal_dot_velocity)\n                    + exterior_mass_density\n                    * lambda_star\n                    * (lambda_star + exterior_normal_dot_velocity)\n                )\n                - exterior_energy_density\n            ),\n        )\n\n\ndef dg_boundary_terms(\n    interior_mass_density,\n    interior_momentum_density,\n    interior_energy_density,\n    interior_pressure,\n    interior_normal_dot_flux_mass_density,\n    interior_normal_dot_flux_momentum_density,\n    interior_normal_dot_flux_energy_density,\n    interior_interface_unit_normal,\n    interior_normal_dot_velocity,\n    interior_largest_outgoing_char_speed,\n    interior_largest_ingoing_char_speed,\n    exterior_mass_density,\n    exterior_momentum_density,\n    exterior_energy_density,\n    exterior_pressure,\n    exterior_normal_dot_flux_mass_density,\n    exterior_normal_dot_flux_momentum_density,\n    exterior_normal_dot_flux_energy_density,\n    exterior_interface_unit_normal,\n    exterior_normal_dot_velocity,\n    exterior_largest_outgoing_char_speed,\n    exterior_largest_ingoing_char_speed,\n    use_strong_form,\n):\n    lambda_max = np.maximum(\n        0.0,\n        np.maximum(\n            interior_largest_outgoing_char_speed,\n            -exterior_largest_ingoing_char_speed,\n        ),\n    )\n    lambda_min = np.minimum(\n        0.0,\n        np.minimum(\n            interior_largest_ingoing_char_speed,\n            -exterior_largest_outgoing_char_speed,\n        ),\n    )\n\n    lambda_star = (\n        exterior_pressure\n        - interior_pressure\n        + interior_mass_density\n        * interior_normal_dot_velocity\n        * (lambda_min - interior_normal_dot_velocity)\n        + exterior_mass_density\n        * exterior_normal_dot_velocity\n        * (lambda_max + exterior_normal_dot_velocity)\n    ) / (\n        interior_mass_density * (lambda_min - interior_normal_dot_velocity)\n        - exterior_mass_density * (lambda_max + exterior_normal_dot_velocity)\n    )\n\n    prefactor_int = (lambda_min - interior_normal_dot_velocity) / (\n        lambda_min - lambda_star\n    )\n    prefactor_ext = (lambda_max + exterior_normal_dot_velocity) / (\n        lambda_max - lambda_star\n    )\n\n    # Use scope and locals() to get arguments into the eval context below\n    scope = locals()\n\n    def mass_density_impl():\n        var_name = \"mass_density\"\n        if use_strong_form:\n            return np.where(\n                lambda_star >= 0.0,\n                lambda_min\n                * (prefactor_int - 1.0)\n                * eval(\"interior_\" + var_name, scope),\n                -eval(\"interior_normal_dot_flux_\" + var_name, scope)\n                - eval(\"exterior_normal_dot_flux_\" + var_name, scope)\n                + lambda_max\n                * (prefactor_ext - 1.0)\n                * eval(\"exterior_\" + var_name, scope),\n            )\n        else:\n            return np.where(\n                lambda_star >= 0.0,\n                eval(\"interior_normal_dot_flux_\" + var_name, scope)\n                + lambda_min\n                * (prefactor_int - 1.0)\n                * eval(\"interior_\" + var_name, scope),\n                -eval(\"exterior_normal_dot_flux_\" + var_name, scope)\n                + lambda_max\n                * (prefactor_ext - 1.0)\n                * eval(\"exterior_\" + var_name, scope),\n            )\n\n    def momentum_density_impl():\n        if use_strong_form:\n            return np.where(\n                lambda_star >= 0.0,\n                lambda_min\n                * (\n                    interior_mass_density\n                    * prefactor_int\n                    * (lambda_star - interior_normal_dot_velocity)\n                    * interior_interface_unit_normal\n                    + interior_momentum_density * (prefactor_int - 1.0)\n                ),\n                -interior_normal_dot_flux_momentum_density\n                - exterior_normal_dot_flux_momentum_density\n                + lambda_max\n                * (\n                    exterior_mass_density\n                    * prefactor_ext\n                    * (lambda_star + exterior_normal_dot_velocity)\n                    * (-exterior_interface_unit_normal)\n                    + exterior_momentum_density * (prefactor_ext - 1.0)\n                ),\n            )\n        else:\n            return np.where(\n                lambda_star >= 0.0,\n                interior_normal_dot_flux_momentum_density\n                + lambda_min\n                * (\n                    interior_mass_density\n                    * prefactor_int\n                    * (lambda_star - interior_normal_dot_velocity)\n                    * interior_interface_unit_normal\n                    + interior_momentum_density * (prefactor_int - 1.0)\n                ),\n                -exterior_normal_dot_flux_momentum_density\n                + lambda_max\n                * (\n                    exterior_mass_density\n                    * prefactor_ext\n                    * (lambda_star + exterior_normal_dot_velocity)\n                    * (-exterior_interface_unit_normal)\n                    + exterior_momentum_density * (prefactor_ext - 1.0)\n                ),\n            )\n\n    def energy_density_impl():\n        if use_strong_form:\n            return np.where(\n                lambda_star >= 0.0,\n                lambda_min\n                * (\n                    (prefactor_int - 1.0)\n                    * (interior_energy_density + interior_pressure)\n                    + prefactor_int\n                    * interior_mass_density\n                    * lambda_star\n                    * (lambda_star - interior_normal_dot_velocity)\n                ),\n                -interior_normal_dot_flux_energy_density\n                - exterior_normal_dot_flux_energy_density\n                + lambda_max\n                * (\n                    (prefactor_ext - 1.0)\n                    * (exterior_energy_density + exterior_pressure)\n                    + prefactor_ext\n                    * exterior_mass_density\n                    * lambda_star\n                    * (lambda_star + exterior_normal_dot_velocity)\n                ),\n            )\n        else:\n            return np.where(\n                lambda_star >= 0.0,\n                interior_normal_dot_flux_energy_density\n                + lambda_min\n                * (\n                    prefactor_int\n                    * (\n                        interior_energy_density\n                        + interior_pressure\n                        * (lambda_star - interior_normal_dot_velocity)\n                        / (lambda_min - interior_normal_dot_velocity)\n                        + interior_mass_density\n                        * lambda_star\n                        * (lambda_star - interior_normal_dot_velocity)\n                    )\n                    - interior_energy_density\n                ),\n                -exterior_normal_dot_flux_energy_density\n                + lambda_max\n                * (\n                    prefactor_ext\n                    * (\n                        exterior_energy_density\n                        + exterior_pressure\n                        * (lambda_star + exterior_normal_dot_velocity)\n                        / (lambda_max + exterior_normal_dot_velocity)\n                        + exterior_mass_density\n                        * lambda_star\n                        * (lambda_star + exterior_normal_dot_velocity)\n                    )\n                    - exterior_energy_density\n                ),\n            )\n\n    return (\n        mass_density_impl(),\n        momentum_density_impl(),\n        energy_density_impl(),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    mass_density,\n    momentum_density,\n    energy_density,\n    flux_mass_density,\n    flux_momentum_density,\n    flux_energy_density,\n    velocity,\n    specific_internal_energy,\n    normal_covector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n    use_polytropic_fluid,\n):\n    velocity_dot_normal = np.einsum(\"i,i\", normal_covector, velocity)\n    if use_polytropic_fluid:\n        polytropic_constant = 1.0e-3\n        polytropic_exponent = 2.0\n        sound_speed = np.sqrt(\n            polytropic_constant\n            * polytropic_exponent\n            * pow(mass_density, polytropic_exponent - 1.0)\n        )\n    else:\n        adiabatic_index = 1.3\n        chi = specific_internal_energy * (adiabatic_index - 1.0)\n        kappa_times_p_over_rho_squared = (\n            adiabatic_index - 1.0\n        ) ** 2 * specific_internal_energy\n        sound_speed = np.sqrt(chi + kappa_times_p_over_rho_squared)\n\n    return (\n        mass_density,\n        momentum_density,\n        energy_density,\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_mass_density)),\n        np.asarray(\n            np.einsum(\"i,ij->j\", normal_covector, flux_momentum_density)\n        ),\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_energy_density)),\n        np.asarray(\n            np.maximum(\n                np.abs(velocity_dot_normal - sound_speed),\n                np.abs(velocity_dot_normal + sound_speed),\n            )\n            if normal_dot_mesh_velocity is None\n            else np.maximum(\n                np.abs(\n                    velocity_dot_normal - sound_speed - normal_dot_mesh_velocity\n                ),\n                np.abs(\n                    velocity_dot_normal + sound_speed - normal_dot_mesh_velocity\n                ),\n            )\n        ),\n    )\n\n\ndef dg_boundary_terms(\n    interior_mass_density,\n    interior_momentum_density,\n    interior_energy_density,\n    interior_normal_dot_flux_mass_density,\n    interior_normal_dot_flux_momentum_density,\n    interior_normal_dot_flux_energy_density,\n    interior_abs_char_speed,\n    exterior_mass_density,\n    exterior_momentum_density,\n    exterior_energy_density,\n    exterior_normal_dot_flux_mass_density,\n    exterior_normal_dot_flux_momentum_density,\n    exterior_normal_dot_flux_energy_density,\n    exterior_abs_char_speed,\n    use_strong_form,\n):\n    sign_for_form = 1.0 if use_strong_form else -1.0\n\n    # Use scope and locals() to get arguments into the eval context below\n    scope = locals()\n\n    def impl(var_name):\n        return np.asarray(\n            (\n                -0.5\n                * (\n                    sign_for_form\n                    * eval(\"interior_normal_dot_flux_\" + var_name, scope)\n                    + eval(\"exterior_normal_dot_flux_\" + var_name, scope)\n                )\n                - 0.5\n                * np.maximum(interior_abs_char_speed, exterior_abs_char_speed)\n                * (\n                    eval(\"exterior_\" + var_name, scope)\n                    - eval(\"interior_\" + var_name, scope)\n                )\n            )\n        )\n\n    return (\n        impl(\"mass_density\"),\n        impl(\"momentum_density\"),\n        impl(\"energy_density\"),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Test_Hll.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct ConvertIdeal {\n  using unpacked_container = bool;\n  using packed_container = EquationsOfState::EquationOfState<false, 2>;\n  using packed_type = bool;\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return false;\n  }\n\n  [[noreturn]] static inline void pack(\n      const gsl::not_null<packed_container*> /*packed*/,\n      const unpacked_container& /*unpacked*/,\n      const size_t /*grid_point_index*/) {\n    ERROR(\"Should not be converting an EOS from an unpacked to a packed type\");\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct DummyInitialData {\n  using argument_tags = tmpl::list<>;\n  struct source_term_type {\n    using sourced_variables = tmpl::list<>;\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nnamespace helpers = TestHelpers::evolution::dg;\n\ntemplate <size_t Dim, typename EosType>\nvoid test(const gsl::not_null<std::mt19937*> gen, const size_t num_pts,\n          const EosType& equation_of_state) {\n  const tuples::TaggedTuple<\n      hydro::Tags::EquationOfState<false, EosType::thermodynamic_dim>>\n      volume_data{equation_of_state.get_clone()};\n  const tuples::TaggedTuple<\n      helpers::Tags::Range<NewtonianEuler::Tags::MassDensityCons>,\n      helpers::Tags::Range<hydro::Tags::SpecificInternalEnergy<DataVector>>>\n      ranges{std::array{1.0e-30, 1.0}, std::array{1.0e-30, 1.0}};\n\n  helpers::test_boundary_correction_conservation<NewtonianEuler::System<Dim>>(\n      gen, NewtonianEuler::BoundaryCorrections::Hll<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  helpers::test_boundary_correction_with_python<NewtonianEuler::System<Dim>,\n                                                tmpl::list<ConvertIdeal>>(\n      gen, \"Hll\", \"dg_package_data\", \"dg_boundary_terms\",\n      NewtonianEuler::BoundaryCorrections::Hll<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  const auto hll = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      NewtonianEuler::BoundaryCorrections::Hll<Dim>>(\"Hll:\");\n\n  helpers::test_boundary_correction_with_python<NewtonianEuler::System<Dim>,\n                                                tmpl::list<ConvertIdeal>>(\n      gen, \"Hll\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const NewtonianEuler::BoundaryCorrections::Hll<Dim>&>(*hll),\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NewtonianEuler.BoundaryCorrections.Hll\",\n                  \"[Unit][Evolution]\") {\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Hll<1>);\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Hll<2>);\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Hll<3>);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  test<1>(make_not_null(&gen), 1, EquationsOfState::IdealFluid<false>{1.3});\n  test<2>(make_not_null(&gen), 5, EquationsOfState::IdealFluid<false>{1.3});\n  test<3>(make_not_null(&gen), 5, EquationsOfState::IdealFluid<false>{1.3});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Test_Hllc.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hllc.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct ConvertIdeal {\n  using unpacked_container = bool;\n  using packed_container = EquationsOfState::EquationOfState<false, 2>;\n  using packed_type = bool;\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return false;\n  }\n\n  [[noreturn]] static inline void pack(\n      const gsl::not_null<packed_container*> /*packed*/,\n      const unpacked_container& /*unpacked*/,\n      const size_t /*grid_point_index*/) {\n    ERROR(\"Should not be converting an EOS from an unpacked to a packed type\");\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct DummyInitialData {\n  using argument_tags = tmpl::list<>;\n  struct source_term_type {\n    using sourced_variables = tmpl::list<>;\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nnamespace helpers = TestHelpers::evolution::dg;\n\ntemplate <size_t Dim, typename EosType>\nvoid test(const gsl::not_null<std::mt19937*> gen, const size_t num_pts,\n          const EosType& equation_of_state) {\n  const tuples::TaggedTuple<\n      hydro::Tags::EquationOfState<false, EosType::thermodynamic_dim>>\n      volume_data{equation_of_state.get_clone()};\n  const tuples::TaggedTuple<\n      helpers::Tags::Range<NewtonianEuler::Tags::MassDensityCons>,\n      helpers::Tags::Range<hydro::Tags::SpecificInternalEnergy<DataVector>>>\n      ranges{std::array{1.0e-30, 1.0}, std::array{1.0e-30, 1.0}};\n\n  helpers::test_boundary_correction_conservation<NewtonianEuler::System<Dim>>(\n      gen, NewtonianEuler::BoundaryCorrections::Hllc<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  helpers::test_boundary_correction_with_python<NewtonianEuler::System<Dim>,\n                                                tmpl::list<ConvertIdeal>>(\n      gen, \"Hllc\", \"dg_package_data\", \"dg_boundary_terms\",\n      NewtonianEuler::BoundaryCorrections::Hllc<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  const auto hllc = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      NewtonianEuler::BoundaryCorrections::Hllc<Dim>>(\"Hllc:\");\n\n  helpers::test_boundary_correction_with_python<NewtonianEuler::System<Dim>,\n                                                tmpl::list<ConvertIdeal>>(\n      gen, \"Hllc\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const NewtonianEuler::BoundaryCorrections::Hllc<Dim>&>(\n          *hllc),\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NewtonianEuler.BoundaryCorrections.Hllc\",\n                  \"[Unit][Evolution]\") {\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Hllc<1>);\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Hllc<2>);\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Hllc<3>);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  test<1>(make_not_null(&gen), 1, EquationsOfState::IdealFluid<false>{1.3});\n  test<2>(make_not_null(&gen), 5, EquationsOfState::IdealFluid<false>{1.3});\n  test<3>(make_not_null(&gen), 5, EquationsOfState::IdealFluid<false>{1.3});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/BoundaryCorrections/Test_Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct ConvertIdeal {\n  using unpacked_container = bool;\n  using packed_container = EquationsOfState::EquationOfState<false, 2>;\n  using packed_type = bool;\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    return false;\n  }\n\n  [[noreturn]] static inline void pack(\n      const gsl::not_null<packed_container*> /*packed*/,\n      const unpacked_container& /*unpacked*/,\n      const size_t /*grid_point_index*/) {\n    ERROR(\"Should not be converting an EOS from an unpacked to a packed type\");\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct DummyInitialData {\n  using argument_tags = tmpl::list<>;\n  struct source_term_type {\n    using sourced_variables = tmpl::list<>;\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nnamespace helpers = TestHelpers::evolution::dg;\n\ntemplate <size_t Dim, typename EosType>\nvoid test(const gsl::not_null<std::mt19937*> gen, const size_t num_pts,\n          const EosType& equation_of_state) {\n  tuples::TaggedTuple<\n      hydro::Tags::EquationOfState<false, EosType::thermodynamic_dim>>\n      volume_data{equation_of_state.get_clone()};\n  tuples::TaggedTuple<\n      helpers::Tags::Range<NewtonianEuler::Tags::MassDensityCons>,\n      helpers::Tags::Range<hydro::Tags::SpecificInternalEnergy<DataVector>>>\n      ranges{std::array<double, 2>{{1.0e-30, 1.0}},\n             std::array<double, 2>{{1.0e-30, 1.0}}};\n\n  helpers::test_boundary_correction_conservation<NewtonianEuler::System<Dim>>(\n      gen, NewtonianEuler::BoundaryCorrections::Rusanov<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  helpers::test_boundary_correction_with_python<NewtonianEuler::System<Dim>,\n                                                tmpl::list<ConvertIdeal>>(\n      gen, \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      NewtonianEuler::BoundaryCorrections::Rusanov<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n\n  const auto rusanov = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      NewtonianEuler::BoundaryCorrections::Rusanov<Dim>>(\"Rusanov:\");\n\n  helpers::test_boundary_correction_with_python<NewtonianEuler::System<Dim>,\n                                                tmpl::list<ConvertIdeal>>(\n      gen, \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const NewtonianEuler::BoundaryCorrections::Rusanov<Dim>&>(\n          *rusanov),\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      volume_data, ranges);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NewtonianEuler.BoundaryCorrections.Rusanov\",\n                  \"[Unit][Evolution]\") {\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Rusanov<1>);\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Rusanov<2>);\n  PUPable_reg(NewtonianEuler::BoundaryCorrections::Rusanov<3>);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  test<1>(make_not_null(&gen), 1, EquationsOfState::IdealFluid<false>{1.3});\n  test<2>(make_not_null(&gen), 5, EquationsOfState::IdealFluid<false>{1.3});\n  test<3>(make_not_null(&gen), 5, EquationsOfState::IdealFluid<false>{1.3});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_NewtonianEuler\")\n\nset(LIBRARY_SOURCES\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp\n  BoundaryConditions/Test_Periodic.cpp\n  BoundaryConditions/Test_Reflection.cpp\n  BoundaryCorrections/Test_Hll.cpp\n  BoundaryCorrections/Test_Hllc.cpp\n  BoundaryCorrections/Test_Rusanov.cpp\n  FiniteDifference/Test_AoWeno.cpp\n  FiniteDifference/Test_MonotonisedCentral.cpp\n  FiniteDifference/Test_Tag.cpp\n  Subcell/Test_ComputeFluxes.cpp\n  Subcell/Test_NeighborPackagedData.cpp\n  Subcell/Test_PrimitiveGhostData.cpp\n  Subcell/Test_PrimsAfterRollback.cpp\n  Subcell/Test_ResizeAndComputePrimitives.cpp\n  Subcell/Test_SetInitialRdmpData.cpp\n  Subcell/Test_TciOnDgGrid.cpp\n  Subcell/Test_TciOnFdGrid.cpp\n  Subcell/Test_TimeDerivative.cpp\n  Test_Characteristics.cpp\n  Test_ConservativeFromPrimitive.cpp\n  Test_Fluxes.cpp\n  Test_InternalEnergyDensity.cpp\n  Test_KineticEnergyDensity.cpp\n  Test_MachNumber.cpp\n  Test_PrimitiveFromConservative.cpp\n  Test_RamPressure.cpp\n  Test_SoundSpeedSquared.cpp\n  Test_SpecificKineticEnergy.cpp\n  Test_Tags.cpp\n  Test_TimeDerivativeTerms.cpp\n  )\n\nadd_subdirectory(Limiters)\nadd_subdirectory(Sources)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainBoundaryConditionsHelpers\n  DomainHelpers\n  Framework\n  Hydro\n  NewtonianEuler\n  NewtonianEulerAnalyticData\n  NewtonianEulerSolutions\n  NewtonianEulerSources\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/CharacteristicSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef CharacteristicSpeeds(velocity, sound_speed, normal):\n    normal_velocity = np.dot(velocity, normal)\n    result = [normal_velocity - sound_speed]\n    for i in range(0, velocity.size):\n        result.append(normal_velocity)\n    result.append(normal_velocity + sound_speed)\n    return result\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef mass_density_cons(mass_density, velocity, specific_internal_energy):\n    return mass_density\n\n\ndef momentum_density(mass_density, velocity, specific_internal_energy):\n    return mass_density * velocity\n\n\ndef energy_density(mass_density, velocity, specific_internal_energy):\n    return (\n        0.5 * mass_density * np.dot(velocity, velocity)\n        + mass_density * specific_internal_energy\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/FiniteDifference/Test_AoWeno.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/AoWeno.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/Systems/NewtonianEuler/FiniteDifference/PrimReconstructor.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  namespace helpers = TestHelpers::NewtonianEuler::fd;\n  const NewtonianEuler::fd::AoWeno53Prim<Dim> aoweno_recons{0.85, 0.8, 1.0e-12,\n                                                            8};\n  helpers::test_prim_reconstructor<Dim>(5, aoweno_recons);\n\n  const auto aoweno_from_options_base = TestHelpers::test_factory_creation<\n      NewtonianEuler::fd::Reconstructor<Dim>,\n      NewtonianEuler::fd::OptionTags::Reconstructor<Dim>>(\n      \"AoWeno53Prim:\\n\"\n      \"  GammaHi: 0.85\\n\"\n      \"  GammaLo: 0.8\\n\"\n      \"  Epsilon: 1.0e-12\\n\"\n      \"  NonlinearWeightExponent: 8\\n\");\n  auto* const aoweno_from_options =\n      dynamic_cast<const NewtonianEuler::fd::AoWeno53Prim<Dim>*>(\n          aoweno_from_options_base.get());\n  REQUIRE(aoweno_from_options != nullptr);\n  CHECK(*aoweno_from_options == aoweno_recons);\n\n  CHECK(aoweno_recons !=\n        NewtonianEuler::fd::AoWeno53Prim<Dim>(0.8, 0.8, 1.0e-12, 8));\n  CHECK(aoweno_recons !=\n        NewtonianEuler::fd::AoWeno53Prim<Dim>(0.85, 0.85, 1.0e-12, 8));\n  CHECK(aoweno_recons !=\n        NewtonianEuler::fd::AoWeno53Prim<Dim>(0.85, 0.8, 2.0e-12, 8));\n  CHECK(aoweno_recons !=\n        NewtonianEuler::fd::AoWeno53Prim<Dim>(0.85, 0.8, 1.0e-12, 6));\n  CHECK(aoweno_recons ==\n        NewtonianEuler::fd::AoWeno53Prim<Dim>(0.85, 0.8, 1.0e-12, 8));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Fd.AoWeno53Prim\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/FiniteDifference/Test_MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/Systems/NewtonianEuler/FiniteDifference/PrimReconstructor.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  namespace helpers = TestHelpers::NewtonianEuler::fd;\n  const NewtonianEuler::fd::MonotonisedCentralPrim<Dim> mc_recons{};\n  helpers::test_prim_reconstructor<Dim>(5, mc_recons);\n  const auto mc_from_options_base = TestHelpers::test_factory_creation<\n      NewtonianEuler::fd::Reconstructor<Dim>,\n      NewtonianEuler::fd::OptionTags::Reconstructor<Dim>>(\n      \"MonotonisedCentralPrim:\\n\");\n  auto* const mc_from_options =\n      dynamic_cast<const NewtonianEuler::fd::MonotonisedCentralPrim<Dim>*>(\n          mc_from_options_base.get());\n  REQUIRE(mc_from_options != nullptr);\n  CHECK(*mc_from_options == mc_recons);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Fd.MonotonisedCentralPrim\",\n    \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/FiniteDifference/Test_Tag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::fd::Tags::Reconstructor<Dim>>(\"Reconstructor\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Fd.Tag\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Limiters/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_NewtonianEulerLimiters\")\n\nset(LIBRARY_SOURCES\n  Test_CharacteristicHelpers.cpp\n  Test_Flattener.cpp\n  Test_KxrcfTci.cpp\n  Test_Minmod.cpp\n  Test_VariablesToLimit.cpp\n  Test_Weno.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  NewtonianEulerLimiters\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Limiters/Test_CharacteristicHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/CharacteristicHelpers.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n\nnamespace {\n\n// This computes a unit vector. It is NOT uniformly distributed in angle,\n// but for this test the angular distribution is not important.\ntemplate <size_t Dim>\ntnsr::i<double, Dim> random_unit_vector(\n    const gsl::not_null<std::mt19937*>& gen,\n    const gsl::not_null<std::uniform_real_distribution<>*>& dist) {\n  const double used_for_size = 0.;\n  auto result =\n      make_with_random_values<tnsr::i<double, Dim>>(gen, dist, used_for_size);\n  double vector_magnitude = get(magnitude(result));\n  // Though highly unlikely, the vector could have length nearly 0. If this\n  // happens, we edit the vector to make it non-zero.\n  if (vector_magnitude < 1e-3) {\n    get<0>(result) += 0.9;\n    vector_magnitude = get(magnitude(result));\n  }\n  // Make unit magnitude\n  for (auto& n_i : result) {\n    n_i /= vector_magnitude;\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid prepare_hydro_data_for_test(\n    const gsl::not_null<Scalar<DataVector>*>& mass_density,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>& momentum_density,\n    const gsl::not_null<Scalar<DataVector>*>& energy_density,\n    const gsl::not_null<Scalar<double>*>& mean_mass_density,\n    const gsl::not_null<tnsr::I<double, Dim>*>& mean_momentum_density,\n    const gsl::not_null<Scalar<double>*>& mean_energy_density,\n    const gsl::not_null<Matrix*>& right, const gsl::not_null<Matrix*>& left,\n    const Mesh<Dim>& mesh,\n    const EquationsOfState::IdealFluid<false>& equation_of_state,\n    const bool generate_random_vector = true) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1., 1.);\n  std::uniform_real_distribution<> distribution_positive(1e-3, 1.);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n  const auto nn_distribution_positive = make_not_null(&distribution_positive);\n\n  const auto unit_vector = [&generate_random_vector, &nn_generator,\n                            &nn_distribution]() {\n    if (generate_random_vector) {\n      return random_unit_vector<Dim>(nn_generator, nn_distribution);\n    } else {\n      // Return unit vector pointing in the \"last\" dimension\n      tnsr::i<double, Dim> result{{0.}};\n      get<Dim - 1>(result) = 1.;\n      return result;\n    }\n  }();\n\n  // Derive all fluid quantities from the primitive variables\n  const DataVector used_for_size(mesh.number_of_grid_points(), 0.);\n  const auto density = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution_positive, used_for_size);\n  const auto velocity = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto specific_internal_energy =\n      make_with_random_values<Scalar<DataVector>>(\n          nn_generator, nn_distribution_positive, used_for_size);\n\n  const auto pressure = equation_of_state.pressure_from_density_and_energy(\n      density, specific_internal_energy);\n\n  *mass_density = density;\n  *momentum_density = velocity;\n  for (size_t i = 0; i < Dim; ++i) {\n    momentum_density->get(i) *= get(density);\n  }\n  *energy_density = Scalar<DataVector>{\n      get(density) * (get(specific_internal_energy) +\n                      0.5 * get(dot_product(velocity, velocity)))};\n\n  get(*mean_mass_density) = mean_value(get(*mass_density), mesh);\n  for (size_t i = 0; i < Dim; ++i) {\n    mean_momentum_density->get(i) = mean_value(momentum_density->get(i), mesh);\n  }\n  get(*mean_energy_density) = mean_value(get(*energy_density), mesh);\n\n  const auto right_and_left =\n      NewtonianEuler::Limiters::right_and_left_eigenvectors<Dim>(\n          *mean_mass_density, *mean_momentum_density, *mean_energy_density,\n          equation_of_state, unit_vector);\n  *right = right_and_left.first;\n  *left = right_and_left.second;\n}\n\ntemplate <size_t Dim>\nvoid test_characteristic_helpers() {\n  INFO(\"Testing characteristic helpers\");\n  CAPTURE(Dim);\n  const Mesh<Dim> mesh(3, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n\n  Scalar<DataVector> mass_density;\n  tnsr::I<DataVector, Dim> momentum_density;\n  Scalar<DataVector> energy_density;\n  Scalar<double> mean_mass_density;\n  tnsr::I<double, Dim> mean_momentum_density;\n  Scalar<double> mean_energy_density;\n  Matrix right;\n  Matrix left;\n\n  prepare_hydro_data_for_test(\n      make_not_null(&mass_density), make_not_null(&momentum_density),\n      make_not_null(&energy_density), make_not_null(&mean_mass_density),\n      make_not_null(&mean_momentum_density),\n      make_not_null(&mean_energy_density), make_not_null(&right),\n      make_not_null(&left), mesh, equation_of_state);\n\n  // First test that the tensor-transform helpers are inverses of each\n  // other. This is a sanity check of the two tensor-transform helpers and\n  // of right_and_left_eigenvectors.\n  Scalar<DataVector> v_minus{};\n  tnsr::I<DataVector, Dim> v_momentum{};\n  Scalar<DataVector> v_plus{};\n  NewtonianEuler::Limiters::characteristic_fields(\n      make_not_null(&v_minus), make_not_null(&v_momentum),\n      make_not_null(&v_plus), mass_density, momentum_density, energy_density,\n      left);\n  Scalar<DataVector> recovered_mass_density{};\n  tnsr::I<DataVector, Dim> recovered_momentum_density{};\n  Scalar<DataVector> recovered_energy_density{};\n  NewtonianEuler::Limiters::conserved_fields_from_characteristic_fields(\n      make_not_null(&recovered_mass_density),\n      make_not_null(&recovered_momentum_density),\n      make_not_null(&recovered_energy_density), v_minus, v_momentum, v_plus,\n      right);\n  CHECK_ITERABLE_APPROX(mass_density, recovered_mass_density);\n  CHECK_ITERABLE_APPROX(momentum_density, recovered_momentum_density);\n  CHECK_ITERABLE_APPROX(energy_density, recovered_energy_density);\n\n  // Test that the cell-average helper matches the tensor helper\n  tuples::TaggedTuple<Tags::Mean<NewtonianEuler::Tags::VMinus>,\n                      Tags::Mean<NewtonianEuler::Tags::VMomentum<Dim>>,\n                      Tags::Mean<NewtonianEuler::Tags::VPlus>>\n      mean_char_vars;\n  NewtonianEuler::Limiters::characteristic_fields(\n      make_not_null(&mean_char_vars),\n      {mean_mass_density, mean_momentum_density, mean_energy_density}, left);\n  CHECK(get(get<Tags::Mean<NewtonianEuler::Tags::VMinus>>(mean_char_vars)) ==\n        approx(mean_value(get(v_minus), mesh)));\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(get<Tags::Mean<NewtonianEuler::Tags::VMomentum<Dim>>>(mean_char_vars)\n              .get(i) == approx(mean_value(v_momentum.get(i), mesh)));\n  }\n  CHECK(get(get<Tags::Mean<NewtonianEuler::Tags::VPlus>>(mean_char_vars)) ==\n        approx(mean_value(get(v_plus), mesh)));\n\n  // Test that the Variables helper matches the tensor helper\n  Variables<tmpl::list<NewtonianEuler::Tags::VMinus,\n                       NewtonianEuler::Tags::VMomentum<Dim>,\n                       NewtonianEuler::Tags::VPlus>>\n      char_vars(mesh.number_of_grid_points());\n  Variables<tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                       NewtonianEuler::Tags::MomentumDensity<Dim>,\n                       NewtonianEuler::Tags::EnergyDensity>>\n      cons_vars(mesh.number_of_grid_points());\n  get<NewtonianEuler::Tags::MassDensityCons>(cons_vars) = mass_density;\n  get<NewtonianEuler::Tags::MomentumDensity<Dim>>(cons_vars) = momentum_density;\n  get<NewtonianEuler::Tags::EnergyDensity>(cons_vars) = energy_density;\n  NewtonianEuler::Limiters::characteristic_fields(make_not_null(&char_vars),\n                                                  cons_vars, left);\n  CHECK_ITERABLE_APPROX(get<NewtonianEuler::Tags::VMinus>(char_vars), v_minus);\n  CHECK_ITERABLE_APPROX(get<NewtonianEuler::Tags::VMomentum<Dim>>(char_vars),\n                        v_momentum);\n  CHECK_ITERABLE_APPROX(get<NewtonianEuler::Tags::VPlus>(char_vars), v_plus);\n}\n\ntemplate <size_t Dim>\nvoid test_apply_limiter_to_char_fields() {\n  INFO(\"Testing apply_limiter_to_characteristic_fields_in_all_directions\");\n  CAPTURE(Dim);\n  const Mesh<Dim> mesh(3, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n\n  Scalar<DataVector> mass_density;\n  tnsr::I<DataVector, Dim> momentum_density;\n  Scalar<DataVector> energy_density;\n  Scalar<double> mean_mass_density;\n  tnsr::I<double, Dim> mean_momentum_density;\n  Scalar<double> mean_energy_density;\n  Matrix right;\n  Matrix left;\n\n  const bool generate_random_vector = false;\n  prepare_hydro_data_for_test(\n      make_not_null(&mass_density), make_not_null(&momentum_density),\n      make_not_null(&energy_density), make_not_null(&mean_mass_density),\n      make_not_null(&mean_momentum_density),\n      make_not_null(&mean_energy_density), make_not_null(&right),\n      make_not_null(&left), mesh, equation_of_state, generate_random_vector);\n\n  // Check case where limiter does nothing\n  const auto noop_expected_mass_density = mass_density;\n  const auto noop_expected_momentum_density = momentum_density;\n  const auto noop_expected_energy_density = energy_density;\n  const auto noop_limiter_wrapper =\n      [](const gsl::not_null<Scalar<DataVector>*> /*char_v_minus*/,\n         const gsl::not_null<tnsr::I<DataVector, Dim>*> /*char_v_momentum*/,\n         const gsl::not_null<Scalar<DataVector>*> /*char_v_plus*/,\n         const Matrix& /*left*/) -> bool { return false; };\n\n  const bool noop_result = NewtonianEuler::Limiters::\n      apply_limiter_to_characteristic_fields_in_all_directions(\n          make_not_null(&mass_density), make_not_null(&momentum_density),\n          make_not_null(&energy_density), mesh, equation_of_state,\n          noop_limiter_wrapper);\n  CHECK_FALSE(noop_result);\n  CHECK_ITERABLE_APPROX(mass_density, noop_expected_mass_density);\n  CHECK_ITERABLE_APPROX(momentum_density, noop_expected_momentum_density);\n  CHECK_ITERABLE_APPROX(energy_density, noop_expected_energy_density);\n\n  // Check with a silly (and completely nonphysical) limiter that scales char\n  // fields, which is relatively easy to check. The solution is set to 0 on the\n  // first dims (d < Dim-1), and scaled by some integers on dim d == Dim-1\n  size_t current_dim = 0;\n  const auto silly_limiter_wrapper =\n      [&current_dim](\n          const gsl::not_null<Scalar<DataVector>*> char_v_minus,\n          const gsl::not_null<tnsr::I<DataVector, Dim>*> char_v_momentum,\n          const gsl::not_null<Scalar<DataVector>*> char_v_plus,\n          const Matrix& /*left*/) -> bool {\n    if (current_dim == Dim - 1) {\n      get(*char_v_minus) *= 2.;\n      // no change to char_v_momentum\n      get(*char_v_plus) = 0.;\n    } else {\n      get(*char_v_minus) = 0.;\n      for (size_t i = 0; i < Dim; ++i) {\n        char_v_momentum->get(i) = 0.;\n      }\n      get(*char_v_plus) = 0.;\n      current_dim++;\n    }\n    return true;\n  };\n\n  Matrix silly_limiter_action(Dim + 2, Dim + 2, 0.);\n  silly_limiter_action(0, 0) = 2. / static_cast<double>(Dim);\n  for (size_t i = 0; i < Dim; ++i) {\n    silly_limiter_action(i + 1, i + 1) = 1. / static_cast<double>(Dim);\n  }\n  silly_limiter_action(Dim + 1, Dim + 1) = 0.;\n  const Matrix silly = right * silly_limiter_action * left;\n\n  auto expected_mass_density = mass_density;\n  auto expected_momentum_density = momentum_density;\n  auto expected_energy_density = energy_density;\n\n  get(expected_mass_density) = silly(0, 0) * get(mass_density);\n  for (size_t j = 0; j < Dim; ++j) {\n    expected_momentum_density.get(j) = silly(j + 1, 0) * get(mass_density);\n  }\n  get(expected_energy_density) = silly(Dim + 1, 0) * get(mass_density);\n  for (size_t i = 0; i < Dim; ++i) {\n    get(expected_mass_density) += silly(0, i + 1) * momentum_density.get(i);\n    for (size_t j = 0; j < Dim; ++j) {\n      expected_momentum_density.get(j) +=\n          silly(j + 1, i + 1) * momentum_density.get(i);\n    }\n    get(expected_energy_density) +=\n        silly(Dim + 1, i + 1) * momentum_density.get(i);\n  }\n  get(expected_mass_density) += silly(0, Dim + 1) * get(energy_density);\n  for (size_t j = 0; j < Dim; ++j) {\n    expected_momentum_density.get(j) +=\n        silly(j + 1, Dim + 1) * get(energy_density);\n  }\n  get(expected_energy_density) += silly(Dim + 1, Dim + 1) * get(energy_density);\n\n  const bool silly_result = NewtonianEuler::Limiters::\n      apply_limiter_to_characteristic_fields_in_all_directions(\n          make_not_null(&mass_density), make_not_null(&momentum_density),\n          make_not_null(&energy_density), mesh, equation_of_state,\n          silly_limiter_wrapper);\n  CHECK(silly_result);\n  CHECK_ITERABLE_APPROX(mass_density, expected_mass_density);\n  CHECK_ITERABLE_APPROX(momentum_density, expected_momentum_density);\n  CHECK_ITERABLE_APPROX(energy_density, expected_energy_density);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Limiters.CharacteristicHelpers\",\n    \"[Unit][Evolution]\") {\n  test_characteristic_helpers<1>();\n  test_characteristic_helpers<2>();\n  test_characteristic_helpers<3>();\n\n  test_apply_limiter_to_char_fields<1>();\n  test_apply_limiter_to_char_fields<2>();\n  test_apply_limiter_to_char_fields<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Limiters/Test_Flattener.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Flattener.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\nvoid test_flattener_1d() {\n  INFO(\"Testing flatten_solution in 1D\");\n  const Mesh<1> mesh(4, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n  const Scalar<DataVector> det_logical_to_inertial_jacobian(\n      DataVector{{2., 2., 2., 2.}});\n\n  {\n    INFO(\"Case: NoOp\");\n    Scalar<DataVector> mass_density_cons(DataVector{{0.8, 1.6, 2.2, 1.2}});\n    tnsr::I<DataVector, 1> momentum_density(DataVector{{-1., 0., 0., -2.}});\n    Scalar<DataVector> energy_density(DataVector{{1., 1.6, 1.9, 1.9}});\n\n    const auto expected_mass_density_cons = mass_density_cons;\n    const auto expected_momentum_density = momentum_density;\n    const auto expected_energy_density = energy_density;\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    CHECK(flattener_action == NewtonianEuler::Limiters::FlattenerAction::NoOp);\n    CHECK_ITERABLE_APPROX(mass_density_cons, expected_mass_density_cons);\n    CHECK_ITERABLE_APPROX(momentum_density, expected_momentum_density);\n    CHECK_ITERABLE_APPROX(energy_density, expected_energy_density);\n  }\n\n  {\n    INFO(\"Case: ScaledSolution because of negative density\");\n    Scalar<DataVector> mass_density_cons(DataVector{{0.8, -0.25, 2.25, 1.2}});\n    tnsr::I<DataVector, 1> momentum_density(DataVector{{-1., 0., 0., -2.}});\n    Scalar<DataVector> energy_density(DataVector{{1., 1.6, 1.9, 1.9}});\n\n    // Expect flattening factor (about mean) of 0.8 times the 0.95 safety = 0.76\n    // density mean = 1.\n    // momentum mean = -0.25\n    // energy mean = 1.7\n    const Scalar<DataVector> expected_mass_density_cons(\n        DataVector{{0.848, 0.05, 1.95, 1.152}});\n    const tnsr::I<DataVector, 1> expected_momentum_density(\n        DataVector{{-0.82, -0.06, -0.06, -1.58}});\n    const Scalar<DataVector> expected_energy_density(\n        DataVector{{1.168, 1.624, 1.852, 1.852}});\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    CHECK(flattener_action ==\n          NewtonianEuler::Limiters::FlattenerAction::ScaledSolution);\n    CHECK_ITERABLE_APPROX(mass_density_cons, expected_mass_density_cons);\n    CHECK_ITERABLE_APPROX(momentum_density, expected_momentum_density);\n    CHECK_ITERABLE_APPROX(energy_density, expected_energy_density);\n  }\n\n  {\n    INFO(\"Case: SetSolutionToMean because of negative pressure\");\n    Scalar<DataVector> mass_density_cons(DataVector{{0.8, -0.25, 2.25, 1.2}});\n    tnsr::I<DataVector, 1> momentum_density(DataVector{{-1., 0., 0., -2.}});\n    Scalar<DataVector> energy_density(DataVector{{1., 1.6, 1.9, 0.7}});\n\n    // density mean = 1.\n    // momentum mean = -0.25\n    // energy mean = 1.6\n    const auto expected_mass_density_cons =\n        make_with_value<Scalar<DataVector>>(mass_density_cons, 1.);\n    const auto expected_momentum_density =\n        make_with_value<tnsr::I<DataVector, 1>>(momentum_density, -0.25);\n    const auto expected_energy_density =\n        make_with_value<Scalar<DataVector>>(energy_density, 1.6);\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    CHECK(flattener_action ==\n          NewtonianEuler::Limiters::FlattenerAction::SetSolutionToMean);\n    CHECK_ITERABLE_APPROX(mass_density_cons, expected_mass_density_cons);\n    CHECK_ITERABLE_APPROX(momentum_density, expected_momentum_density);\n    CHECK_ITERABLE_APPROX(energy_density, expected_energy_density);\n  }\n\n  {\n    INFO(\"Case: ScaledSolution again, now with non-constant jacobian\");\n    const Scalar<DataVector> det_curved_jacobian(\n        DataVector{{1., 1.1, 1.2, 1.3}});\n    Scalar<DataVector> mass_density_cons(DataVector{{0.8, -0.25, 2.25, 1.2}});\n    tnsr::I<DataVector, 1> momentum_density(DataVector{{-1., 0., 0., -2.}});\n    Scalar<DataVector> energy_density(DataVector{{1., 1.6, 1.9, 1.9}});\n\n    // With this jacobian, the means are (from Mathematica)\n    // density mean = 2897 / 2769\n    // momentum mean = -3 / 23\n    // energy mean = 789 / 460\n    // And the scaling factor (before 0.95 safety) is 2897 / 3587\n    // Again from Mathematica, we get the expected results post-flattening...\n    const Scalar<DataVector> expected_mass_density_cons(\n        DataVector{{0.8581014826082916, 0.05248188405797101, 1.970623785368258,\n                    1.165004186817938}});\n    const tnsr::I<DataVector, 1> expected_momentum_density(\n        DataVector{{-0.8279723882134762, -0.06071562768936134,\n                    -0.06071562768936134, -1.595229148737591}});\n    const Scalar<DataVector> expected_energy_density(\n        DataVector{{1.166462012581666, 1.626816068896135, 1.856993097053369,\n                    1.856993097053369}});\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_curved_jacobian,\n        equation_of_state);\n\n    CHECK(flattener_action ==\n          NewtonianEuler::Limiters::FlattenerAction::ScaledSolution);\n    CHECK_ITERABLE_APPROX(mass_density_cons, expected_mass_density_cons);\n    CHECK_ITERABLE_APPROX(momentum_density, expected_momentum_density);\n    CHECK_ITERABLE_APPROX(energy_density, expected_energy_density);\n  }\n}\n\nvoid test_flattener_2d() {\n  INFO(\"Testing flatten_solution in 2D\");\n  const Mesh<2> mesh({{2, 3}}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n  const Scalar<DataVector> det_logical_to_inertial_jacobian(\n      DataVector{{2., 2.1, 2.1, 2.2, 2.2, 2.3}});\n\n  {\n    INFO(\"Case: NoOp\");\n    Scalar<DataVector> mass_density_cons(\n        DataVector{{0.8, 1., 0.6, 1.3, 1.1, 1.6}});\n    tnsr::I<DataVector, 2> momentum_density;\n    get<0>(momentum_density) = DataVector{{-0.2, 0.1, 0.2, -0.1, 0.1, -0.2}};\n    get<1>(momentum_density) = DataVector{{0.4, 0.3, 0.2, 0.3, 0.4, 0.7}};\n    Scalar<DataVector> energy_density(DataVector{{1., 1.6, 1.4, 2., 1.8, 1.9}});\n\n    const auto expected_mass_density_cons = mass_density_cons;\n    const auto expected_momentum_density = momentum_density;\n    const auto expected_energy_density = energy_density;\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    CHECK(flattener_action == NewtonianEuler::Limiters::FlattenerAction::NoOp);\n    CHECK_ITERABLE_APPROX(mass_density_cons, expected_mass_density_cons);\n    CHECK_ITERABLE_APPROX(momentum_density, expected_momentum_density);\n    CHECK_ITERABLE_APPROX(energy_density, expected_energy_density);\n  }\n\n  {\n    INFO(\"Case: ScaledSolution because of negative density\");\n    Scalar<DataVector> mass_density_cons(\n        DataVector{{0.8, 1., -0.2, 1.3, 1.1, 1.6}});\n    tnsr::I<DataVector, 2> momentum_density;\n    get<0>(momentum_density) = DataVector{{-0.2, 0.1, 0.2, -0.1, 0.1, -0.2}};\n    get<1>(momentum_density) = DataVector{{0.4, 0.3, 0.2, 0.3, 0.4, 0.7}};\n    Scalar<DataVector> energy_density(DataVector{{1., 1.6, 1.4, 2., 1.8, 1.9}});\n\n    const auto original_mass_density_cons = mass_density_cons;\n    const auto original_momentum_density = momentum_density;\n    const auto original_energy_density = energy_density;\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    // check 1) action, 2) positive density, 3) all fields changed\n    CHECK(flattener_action ==\n          NewtonianEuler::Limiters::FlattenerAction::ScaledSolution);\n    CHECK(min(get(mass_density_cons)) > 0.);\n    CHECK_FALSE(mass_density_cons == original_mass_density_cons);\n    CHECK_FALSE(momentum_density == original_momentum_density);\n    CHECK_FALSE(energy_density == original_energy_density);\n  }\n\n  {\n    INFO(\"Case: SetSolutionToMean because of negative pressure\");\n    Scalar<DataVector> mass_density_cons(\n        DataVector{{0.8, 1., 0.6, 1.3, 1.1, 1.6}});\n    tnsr::I<DataVector, 2> momentum_density;\n    get<0>(momentum_density) = DataVector{{-2.3, 1.9, 2.1, -3.2, 2.7, -3.2}};\n    get<1>(momentum_density) = DataVector{{0.4, 0.3, 0.2, 0.3, 0.4, 0.7}};\n    Scalar<DataVector> energy_density(DataVector{{1., 1.6, 1.4, 2., 1.8, 1.9}});\n\n    // make sure initial pressure is in fact negative, because if not this isn't\n    // testing the right thing\n    const auto specific_internal_energy_before = Scalar<DataVector>{\n        get(energy_density) / get(mass_density_cons) -\n        0.5 * get(dot_product(momentum_density, momentum_density)) /\n            square(get(mass_density_cons))};\n    const auto pressure_before =\n        equation_of_state.pressure_from_density_and_energy(\n            mass_density_cons, specific_internal_energy_before);\n    CHECK(min(get(pressure_before)) < 0.);\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    // check 1) action, 2) positive pressure, 3) all fields set to constant\n    CHECK(flattener_action ==\n          NewtonianEuler::Limiters::FlattenerAction::SetSolutionToMean);\n    const auto specific_internal_energy = Scalar<DataVector>{\n        get(energy_density) / get(mass_density_cons) -\n        0.5 * get(dot_product(momentum_density, momentum_density)) /\n            square(get(mass_density_cons))};\n    const auto pressure = equation_of_state.pressure_from_density_and_energy(\n        mass_density_cons, specific_internal_energy);\n    CHECK(min(get(pressure)) > 0.);\n    CHECK_ITERABLE_APPROX(mass_density_cons,\n                          make_with_value<Scalar<DataVector>>(\n                              get(pressure), get(mass_density_cons)[0]));\n    CHECK_ITERABLE_APPROX(\n        get<0>(momentum_density),\n        DataVector(mesh.number_of_grid_points(), get<0>(momentum_density)[0]));\n    CHECK_ITERABLE_APPROX(\n        get<1>(momentum_density),\n        DataVector(mesh.number_of_grid_points(), get<1>(momentum_density)[0]));\n    CHECK_ITERABLE_APPROX(energy_density,\n                          make_with_value<Scalar<DataVector>>(\n                              get(pressure), get(energy_density)[0]));\n  }\n}\n\nvoid test_flattener_3d() {\n  INFO(\"Testing flatten_solution in 3D\");\n  const Mesh<3> mesh(2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n  const Scalar<DataVector> det_logical_to_inertial_jacobian(\n      DataVector{{0.2, 0.1, 0.3, 0.2, 0.5, 0.6, 0.4, 0.5}});\n\n  {\n    INFO(\"Case: NoOp\");\n    Scalar<DataVector> mass_density_cons(\n        DataVector{{1.4, 1.3, 1.2, 1.6, 1.8, 1.7, 1.3, 1.4}});\n    tnsr::I<DataVector, 3> momentum_density;\n    get<0>(momentum_density) = DataVector(8, 0.);\n    get<1>(momentum_density) = DataVector(8, 0.);\n    get<2>(momentum_density) =\n        DataVector{{-0.3, -0.2, -0.4, -0.3, -0.8, -0.2, -0.3, -0.1}};\n    Scalar<DataVector> energy_density(\n        DataVector{{2.1, 2.3, 4.3, 3.5, 2.9, 1.8, 2.6, 2.9}});\n\n    const auto expected_mass_density_cons = mass_density_cons;\n    const auto expected_momentum_density = momentum_density;\n    const auto expected_energy_density = energy_density;\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    CHECK(flattener_action == NewtonianEuler::Limiters::FlattenerAction::NoOp);\n    CHECK_ITERABLE_APPROX(mass_density_cons, expected_mass_density_cons);\n    CHECK_ITERABLE_APPROX(momentum_density, expected_momentum_density);\n    CHECK_ITERABLE_APPROX(energy_density, expected_energy_density);\n  }\n\n  {\n    INFO(\"Case: ScaledSolution because of negative density\");\n    Scalar<DataVector> mass_density_cons(\n        DataVector{{1.4, 1.3, 1.2, 1.6, -1.6, 1.7, 1.3, 1.4}});\n    tnsr::I<DataVector, 3> momentum_density;\n    get<0>(momentum_density) = DataVector(8, 0.);\n    get<1>(momentum_density) = DataVector(8, 0.);\n    get<2>(momentum_density) =\n        DataVector{{-0.3, -0.2, -0.4, -0.3, -0.8, -0.2, -0.3, -0.1}};\n    Scalar<DataVector> energy_density(\n        DataVector{{2.1, 2.3, 4.3, 3.5, 2.9, 1.8, 2.6, 2.9}});\n\n    const auto original_mass_density_cons = mass_density_cons;\n    const auto original_momentum_density = momentum_density;\n    const auto original_energy_density = energy_density;\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    // check 1) action, 2) positive density, 3) all fields changed\n    CHECK(flattener_action ==\n          NewtonianEuler::Limiters::FlattenerAction::ScaledSolution);\n    CHECK(min(get(mass_density_cons)) > 0.);\n    CHECK_FALSE(mass_density_cons == original_mass_density_cons);\n    CHECK_FALSE(momentum_density == original_momentum_density);\n    CHECK_FALSE(energy_density == original_energy_density);\n  }\n\n  {\n    INFO(\"Case: SetSolutionToMean because of negative pressure\");\n    Scalar<DataVector> mass_density_cons(\n        DataVector{{1.4, 1.3, 1.2, 1.6, 1.8, 1.7, 1.3, 1.4}});\n    tnsr::I<DataVector, 3> momentum_density;\n    get<0>(momentum_density) = DataVector(8, -2.3);\n    get<1>(momentum_density) = DataVector(8, 0.8);\n    get<2>(momentum_density) =\n        DataVector{{-0.3, -0.2, -0.4, -0.3, -0.8, -0.2, -0.3, -0.1}};\n    Scalar<DataVector> energy_density(\n        DataVector{{2.1, 2.3, 4.3, 3.5, 2.9, 1.8, 2.6, 2.9}});\n\n    // make sure initial pressure is in fact negative, because if not this isn't\n    // testing the right thing\n    const auto specific_internal_energy_before = Scalar<DataVector>{\n        get(energy_density) / get(mass_density_cons) -\n        0.5 * get(dot_product(momentum_density, momentum_density)) /\n            square(get(mass_density_cons))};\n    const auto pressure_before =\n        equation_of_state.pressure_from_density_and_energy(\n            mass_density_cons, specific_internal_energy_before);\n    CHECK(min(get(pressure_before)) < 0.);\n\n    const auto flattener_action = NewtonianEuler::Limiters::flatten_solution(\n        make_not_null(&mass_density_cons), make_not_null(&momentum_density),\n        make_not_null(&energy_density), mesh, det_logical_to_inertial_jacobian,\n        equation_of_state);\n\n    // check 1) action, 2) positive pressure, 3) all fields set to constant\n    CHECK(flattener_action ==\n          NewtonianEuler::Limiters::FlattenerAction::SetSolutionToMean);\n    const auto specific_internal_energy = Scalar<DataVector>{\n        get(energy_density) / get(mass_density_cons) -\n        0.5 * get(dot_product(momentum_density, momentum_density)) /\n            square(get(mass_density_cons))};\n    const auto pressure = equation_of_state.pressure_from_density_and_energy(\n        mass_density_cons, specific_internal_energy);\n    CHECK(min(get(pressure)) > 0.);\n    CHECK_ITERABLE_APPROX(mass_density_cons,\n                          make_with_value<Scalar<DataVector>>(\n                              get(pressure), get(mass_density_cons)[0]));\n    CHECK_ITERABLE_APPROX(\n        get<0>(momentum_density),\n        DataVector(mesh.number_of_grid_points(), get<0>(momentum_density)[0]));\n    CHECK_ITERABLE_APPROX(\n        get<1>(momentum_density),\n        DataVector(mesh.number_of_grid_points(), get<1>(momentum_density)[0]));\n    CHECK_ITERABLE_APPROX(energy_density,\n                          make_with_value<Scalar<DataVector>>(\n                              get(pressure), get(energy_density)[0]));\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Limiters.Flattener\",\n                  \"[Unit][Evolution]\") {\n  test_flattener_1d();\n  test_flattener_2d();\n  test_flattener_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Limiters/Test_KxrcfTci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/KxrcfTci.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t VolumeDim>\nstruct TestPackagedData {\n  Variables<tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                       NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                       NewtonianEuler::Tags::EnergyDensity>>\n      volume_data;\n  Mesh<VolumeDim> mesh;\n};\n\ntemplate <size_t VolumeDim>\nDirectionMap<VolumeDim, DataVector> make_dirmap_of_datavectors_from_value(\n    const size_t size, const double value) {\n  DirectionMap<VolumeDim, DataVector> result{};\n  for (const auto& dir : Direction<VolumeDim>::all_directions()) {\n    result[dir] = DataVector(size, value);\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nvoid test_kxrcf_work(\n    const bool expected_detection, const double kxrcf_constant,\n    const Scalar<DataVector>& cons_density,\n    const tnsr::I<DataVector, VolumeDim>& cons_momentum,\n    const Scalar<DataVector>& cons_energy, const Mesh<VolumeDim>& mesh,\n    const Element<VolumeDim>& element,\n    const ElementMap<VolumeDim, Frame::Inertial>& element_map,\n    const DirectionMap<VolumeDim, DataVector>& neighbor_densities,\n    const DirectionMap<VolumeDim, DataVector>& neighbor_energies) {\n  // Check that this help function is called correctly\n  ASSERT(element.neighbors().size() == neighbor_densities.size(),\n         \"The test helper was passed inconsistent data.\");\n  ASSERT(element.neighbors().size() == neighbor_energies.size(),\n         \"The test helper was passed inconsistent data.\");\n  for (const auto& dir : Direction<VolumeDim>::all_directions()) {\n    const auto& ids = element.neighbors().at(dir).ids();\n    ASSERT(ids.size() == 1,\n           \"The test helper test_kxrcf_work isn't set up for h-refinement.\");\n  }\n\n  // Create and fill neighbor data\n  std::unordered_map<DirectionalId<VolumeDim>, TestPackagedData<VolumeDim>,\n                     boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_data{};\n  for (const auto& dir : Direction<VolumeDim>::all_directions()) {\n    const auto& id = *(element.neighbors().at(dir).ids().begin());\n    TestPackagedData<VolumeDim>& neighbor =\n        neighbor_data[DirectionalId<VolumeDim>{dir, id}];\n    neighbor.mesh = mesh;\n    neighbor.volume_data.initialize(mesh.number_of_grid_points(), 0.);\n    get(get<NewtonianEuler::Tags::MassDensityCons>(neighbor.volume_data)) =\n        neighbor_densities.at(dir);\n    get(get<NewtonianEuler::Tags::EnergyDensity>(neighbor.volume_data)) =\n        neighbor_energies.at(dir);\n  }\n\n  const auto element_size = size_of_element(element_map);\n  const Scalar<DataVector> det_jacobian{\n      {1. /\n       get(determinant(element_map.inv_jacobian(logical_coordinates(mesh))))}};\n\n  // Create and fill unit normals\n  DirectionMap<VolumeDim, std::optional<Variables<tmpl::list<\n                              evolution::dg::Tags::MagnitudeOfNormal,\n                              evolution::dg::Tags::NormalCovector<VolumeDim>>>>>\n      normals_and_magnitudes{};\n  for (const auto& dir : Direction<VolumeDim>::all_directions()) {\n    const auto boundary_mesh = mesh.slice_away(dir.dimension());\n    normals_and_magnitudes[dir] =\n        Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                             evolution::dg::Tags::NormalCovector<VolumeDim>>>(\n            boundary_mesh.number_of_grid_points());\n    auto& covector = get<evolution::dg::Tags::NormalCovector<VolumeDim>>(\n        normals_and_magnitudes[dir].value());\n    auto& normal_magnitude = get<evolution::dg::Tags::MagnitudeOfNormal>(\n        normals_and_magnitudes[dir].value());\n    unnormalized_face_normal(make_not_null(&(covector)),\n                             mesh.slice_away(dir.dimension()), element_map,\n                             dir);\n    normal_magnitude = magnitude(covector);\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      covector.get(d) /= get(normal_magnitude);\n    }\n  }\n\n  const bool tci_detection = NewtonianEuler::Limiters::Tci::kxrcf_indicator(\n      kxrcf_constant, cons_density, cons_momentum, cons_energy, mesh, element,\n      element_size, det_jacobian, normals_and_magnitudes, neighbor_data);\n  CHECK(tci_detection == expected_detection);\n}\n\nvoid test_kxrcf_1d() {\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const Mesh<1> mesh(3, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  using Affine = domain::CoordinateMaps::Affine;\n  const Affine xi_map{-1., 1., 3., 4.2};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          xi_map);\n  const ElementMap<1, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n\n  Scalar<DataVector> cons_density{DataVector{{0.4, 0.2, 0.}}};\n  tnsr::I<DataVector, 1> cons_momentum{DataVector{{0.2, 0.1, 0.05}}};\n  Scalar<DataVector> cons_energy{DataVector{{0., 1.3, 0.}}};\n\n  const auto neighbor_densities = make_dirmap_of_datavectors_from_value<1>(\n      mesh.number_of_grid_points(), 0.);\n  const auto neighbor_energies = make_dirmap_of_datavectors_from_value<1>(\n      mesh.number_of_grid_points(), 0.);\n\n  // In realistic uses of the TCI, the kxrcf_constant would be set to a finite\n  // value, perhaps 1, and different solutions may or may not trigger limiting.\n  // For ease of testing, we do the opposite: we fix the solution and vary the\n  // threshold.\n\n  // trigger: density inflow at lower xi boundary\n  double kxrcf_constant = 0.;\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: threshold is just met\n  kxrcf_constant = 4.81;\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // no trigger: threshold is not met\n  kxrcf_constant = 4.82;\n  test_kxrcf_work(false, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // no trigger: density has no jump at lower xi boundary\n  kxrcf_constant = 0.;\n  get(cons_density)[0] = 0.;\n  test_kxrcf_work(false, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: energy inflow at upper xi boundary\n  cons_density = Scalar<DataVector>{DataVector{{0., 0.3, 0.}}};\n  cons_momentum = tnsr::I<DataVector, 1>{DataVector{{-0.2, 0.1, -0.05}}};\n  cons_energy = Scalar<DataVector>{DataVector{{0., 1.3, 1.7}}};\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // no trigger: energy has no jump at upper xi boundary\n  get(cons_energy)[mesh.number_of_grid_points() - 1] = 0.;\n  test_kxrcf_work(false, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // no trigger: all boundaries have outgoing characteristics\n  cons_density = Scalar<DataVector>{DataVector{{0.4, 0.3, 0.2}}};\n  cons_momentum = tnsr::I<DataVector, 1>{DataVector{{-0.2, 0.1, 0.05}}};\n  cons_energy = Scalar<DataVector>{DataVector{{1.4, 1.3, 1.7}}};\n  test_kxrcf_work(false, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n}\n\nvoid test_kxrcf_2d() {\n  const auto element = TestHelpers::Limiters::make_element<2>();\n  const Mesh<2> mesh(3, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  const Affine xi_map{-1., 1., 3., 4.2};\n  const Affine eta_map{-1., 1., 3., 7.};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine2D(xi_map, eta_map));\n  const ElementMap<2, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n\n  const DataVector zero(mesh.number_of_grid_points(), 0.);\n\n  Scalar<DataVector> cons_density{\n      DataVector{{0.4, 0.3, 0.2, 0., 0., 0., 0., 0., 0.}}};\n  tnsr::I<DataVector, 2> cons_momentum{};\n  get<0>(cons_momentum) = zero;\n  get<1>(cons_momentum) = DataVector{{0.2, 0.1, 0.05, 0., 0., 0., 0., 0., 0.}};\n  Scalar<DataVector> cons_energy{\n      DataVector{{0., 0., 0., 0., 0., 0., 0.3, 0.2, 0.1}}};\n\n  const auto neighbor_densities = make_dirmap_of_datavectors_from_value<2>(\n      mesh.number_of_grid_points(), 0.);\n  const auto neighbor_energies = make_dirmap_of_datavectors_from_value<2>(\n      mesh.number_of_grid_points(), 0.);\n\n  // In realistic uses of the TCI, the kxrcf_constant would be set to a finite\n  // value, perhaps 1, and different solutions may or may not trigger limiting.\n  // For ease of testing, we do the opposite: we fix the solution and vary the\n  // threshold.\n\n  // trigger: density inflow at lower eta boundary\n  double kxrcf_constant = 0.;\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: density inflow on subset of lower eta boundary\n  get<1>(cons_momentum) = DataVector{{0.2, 0.1, -0.05, 0., 0., 0., 0., 0., 0.}};\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: threshold is just met\n  kxrcf_constant = 0.776;\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // no trigger: threshold is not met\n  kxrcf_constant = 0.777;\n  test_kxrcf_work(false, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: same threshold, but now upper xi boundary also contributes\n  get(cons_density) = DataVector{{0.4, 0.3, 0.2, 0., 0., 0., 0., 0., 0.8}};\n  get<0>(cons_momentum) = DataVector{{0., 0., 0., 0., 0., 0., 0., 0., -1.4}};\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: energy inflow at upper eta boundary\n  kxrcf_constant = 0.;\n  get(cons_density) = DataVector{{0.4, 0.3, 0.2, 0., 0., 0., 0., 0., 0.}};\n  get<0>(cons_momentum) = zero;\n  get<1>(cons_momentum) =\n      DataVector{{0., 0., 0., 0., 0., 0., -0.8, -1.3, -1.2}};\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // no trigger: all boundaries have outgoing characteristics\n  get<0>(cons_momentum) =\n      DataVector{{-0.2, 0., 0.05, -1.2, 0., 0.3, -0.9, 0., 0.4}};\n  get<1>(cons_momentum) =\n      DataVector{{-0.2, -0.1, -0.05, 0., 0., 0., 0.3, 1.2, 2.3}};\n  test_kxrcf_work(false, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n}\n\nvoid test_kxrcf_3d() {\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const Mesh<3> mesh(3, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const Affine xi_map{-1., 1., 3., 4.2};\n  const Affine eta_map{-1., 1., -3., -2.4};\n  const Affine zeta_map{-1., 1., 3., 7.};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D(xi_map, eta_map, zeta_map));\n  const ElementMap<3, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n\n  const DataVector zero(mesh.number_of_grid_points(), 0.);\n\n  Scalar<DataVector> cons_density{\n      // clang-format off\n      DataVector{{0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0.4, 0.3, 0.2, 0.3, 0.2, 0.2, 0.2, 0.2, 0.1}}};\n  // clang-format on\n  tnsr::I<DataVector, 3> cons_momentum{};\n  get<0>(cons_momentum) = zero;\n  get<1>(cons_momentum) = zero;\n  get<2>(cons_momentum) =\n      // clang-format off\n      DataVector{{0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  -0.1, -0.4, -0.2, -0.8, -0.3, -0.6, -1.2, -1.8, -0.9}};\n  // clang-format on\n  Scalar<DataVector> cons_energy{\n      // clang-format off\n      DataVector{{1.1, 1.3, 0.8, 1.3, 0.7, 0.6, 0.9, 1.2, 1.1,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.}}};\n  // clang-format on\n\n  const auto neighbor_densities = make_dirmap_of_datavectors_from_value<3>(\n      mesh.number_of_grid_points(), 0.);\n  const auto neighbor_energies = make_dirmap_of_datavectors_from_value<3>(\n      mesh.number_of_grid_points(), 0.);\n\n  // In realistic uses of the TCI, the kxrcf_constant would be set to a finite\n  // value, perhaps 1, and different solutions may or may not trigger limiting.\n  // For ease of testing, we do the opposite: we fix the solution and vary the\n  // threshold.\n\n  // trigger: density inflow at upper zeta boundary\n  double kxrcf_constant = 0.;\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: density inflow on subset of upper zeta boundary\n  get<2>(cons_momentum) =\n      // clang-format off\n      DataVector{{0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0.8, -0.4, -0.2, 0.1, 0.3, -0.6, -1.2, -1.8, -0.9}};\n  // clang-format on\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: threshold is just met\n  kxrcf_constant = 1.26;\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // no trigger: threshold is not met\n  kxrcf_constant = 1.27;\n  test_kxrcf_work(false, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: same threshold, but now lower eta boundary also contributes\n  get(cons_density) =\n      // clang-format off\n      DataVector{{0., 0.2, 0.2, 0., 0., 0., 0., 0., 0.,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0.4, 0.3, 0.2, 0.3, 0.2, 0.2, 0.2, 0.2, 0.1}};\n  // clang-format on\n  get<1>(cons_momentum) =\n      // clang-format off\n      DataVector{{0., 0.2, 0.2, 0., 0., 0., 0., 0., 0.,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0.1, 0.4, 0.2, 0., 0., 0., 0., 0., 0.}};\n  // clang-format on\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // trigger: energy inflow at lower zeta boundary\n  kxrcf_constant = 0.;\n  get(cons_density) =\n      // clang-format off\n      DataVector{{0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0.4, 0.3, 0.2, 0.3, 0.2, 0.2, 0.2, 0.2, 0.1}};\n  // clang-format on\n  get<1>(cons_momentum) = zero;\n  get<2>(cons_momentum) =\n      // clang-format off\n      DataVector{{0.3, 0.1, 0.2, 0.4, 0.2, 0.3, 0.6, 0.4, 0.7,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.}};\n  // clang-format on\n  test_kxrcf_work(true, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n\n  // no trigger: all boundaries have outgoing characteristics\n  get<0>(cons_momentum) =\n      // clang-format off\n      DataVector{{-0.2, 0., 0.05, -0.2, 0., 0.3, -0.8, 0., 0.2,\n                  -0.4, 0., 0.07, -0.9, 0., 0.7, -0.3, 0., 0.8,\n                  -0.7, 0., 0.1, -1.3, 0., 0.8, -0.2, 0., 0.7}};\n  // clang-format on\n  get<1>(cons_momentum) =\n      // clang-format off\n      DataVector{{-0.2, -0.1, -0.05, 0., 0., 0., 0.3, 1.2, 2.3,\n                  -0.4, -0.1, -0.1, 0., 0., 0., 0.3, 0.8, 1.6,\n                  -0.7, -0.2, -0.1, 0., 0., 0., 0.2, 0.2, 0.7}};\n  // clang-format on\n  get<2>(cons_momentum) =\n      // clang-format off\n      DataVector{{-0.2, -0.1, -0.05, -0.1, -0.1, -0.4, -0.3, -1.2, -2.3,\n                  0., 0., 0., 0., 0., 0., 0., 0., 0.,\n                  0.7, 1.3, 1.1, 0.5, 1., 0.2, 0.2, 0.3, 0.3}};\n  // clang-format on\n  test_kxrcf_work(false, kxrcf_constant, cons_density, cons_momentum,\n                  cons_energy, mesh, element, element_map, neighbor_densities,\n                  neighbor_energies);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Limiters.KxrcfTci\",\n                  \"[Unit][Evolution]\") {\n  test_kxrcf_1d();\n  test_kxrcf_2d();\n  test_kxrcf_3d();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Limiters/Test_Minmod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Minmod.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/MinmodType.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/CharacteristicHelpers.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Minmod.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nvoid test_neuler_minmod_option_parsing() {\n  INFO(\"Testing option parsing\");\n  const auto lambda_pi1 =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Minmod<1>>(\n          \"Type: LambdaPi1\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto lambda_pi1_cons =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Minmod<1>>(\n          \"Type: LambdaPi1\\n\"\n          \"VariablesToLimit: Conserved\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto lambda_pi1_tvb =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Minmod<1>>(\n          \"Type: LambdaPi1\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"TvbConstant: 1.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto lambda_pi1_noflat =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Minmod<1>>(\n          \"Type: LambdaPi1\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"ApplyFlattener: False\\n\"\n          \"DisableForDebugging: False\");\n  const auto lambda_pi1_disabled =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Minmod<1>>(\n          \"Type: LambdaPi1\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: True\");\n  const auto muscl =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Minmod<1>>(\n          \"Type: Muscl\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n\n  // Test operators == and !=\n  CHECK(lambda_pi1 == lambda_pi1);\n  CHECK(lambda_pi1 != lambda_pi1_cons);\n  CHECK(lambda_pi1 != lambda_pi1_tvb);\n  CHECK(lambda_pi1 != lambda_pi1_noflat);\n  CHECK(lambda_pi1 != lambda_pi1_disabled);\n  CHECK(lambda_pi1 != muscl);\n\n  const auto lambda_pin_2d =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Minmod<2>>(\n          \"Type: LambdaPiN\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto lambda_pin_3d =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Minmod<3>>(\n          \"Type: LambdaPiN\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"TvbConstant: 10.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n\n  // Test that creation from options gives correct object\n  const NewtonianEuler::Limiters::Minmod<1> expected_lambda_pi1(\n      Limiters::MinmodType::LambdaPi1,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0., true);\n  const NewtonianEuler::Limiters::Minmod<1> expected_lambda_pi1_cons(\n      Limiters::MinmodType::LambdaPi1,\n      NewtonianEuler::Limiters::VariablesToLimit::Conserved, 0., true);\n  const NewtonianEuler::Limiters::Minmod<1> expected_lambda_pi1_tvb(\n      Limiters::MinmodType::LambdaPi1,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 1., true);\n  const NewtonianEuler::Limiters::Minmod<1> expected_lambda_pi1_noflat(\n      Limiters::MinmodType::LambdaPi1,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0., false);\n  const NewtonianEuler::Limiters::Minmod<1> expected_lambda_pi1_disabled(\n      Limiters::MinmodType::LambdaPi1,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0., true,\n      true);\n  const NewtonianEuler::Limiters::Minmod<1> expected_muscl(\n      Limiters::MinmodType::Muscl,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0., true);\n  const NewtonianEuler::Limiters::Minmod<2> expected_lambda_pin_2d(\n      Limiters::MinmodType::LambdaPiN,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0., true);\n  const NewtonianEuler::Limiters::Minmod<3> expected_lambda_pin_3d(\n      Limiters::MinmodType::LambdaPiN,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 10., true);\n\n  CHECK(lambda_pi1 == expected_lambda_pi1);\n  CHECK(lambda_pi1_cons == expected_lambda_pi1_cons);\n  CHECK(lambda_pi1_tvb == expected_lambda_pi1_tvb);\n  CHECK(lambda_pi1_noflat == expected_lambda_pi1_noflat);\n  CHECK(lambda_pi1_disabled == expected_lambda_pi1_disabled);\n  CHECK(muscl == expected_muscl);\n  CHECK(lambda_pin_2d == expected_lambda_pin_2d);\n  CHECK(lambda_pin_3d == expected_lambda_pin_3d);\n}\n\nvoid test_neuler_minmod_serialization() {\n  INFO(\"Testing serialization\");\n  const NewtonianEuler::Limiters::Minmod<1> minmod(\n      Limiters::MinmodType::LambdaPi1,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 1., true);\n  test_serialization(minmod);\n}\n\n// Compare the specialized limiter to the generic limiter. The limiters are\n// compared for limiting conserved/evolved and limiting characteristic\n// variables.\n//\n// Note: comparing the specialized and generic limiters when limiting the\n// characteristic variables is tedious to do thoroughly, because we need to\n// call the generic limiter multiple times using characteristic fields w.r.t.\n// multiple different unit vectors. In essence, the test would need to fully\n// reimplement the specialized limiter. To avoid the test becoming so tedious,\n// we test a simpler restricted problem, where in 2D and 3D we require\n// input_momentum to be 0. With this simplification, the characteristic\n// decomposition becomes independent of the choice of unit vector, and therefore\n// the generic limiter can match the specialized limiter even when only one set\n// of characteristic (e.g., w.r.t. \\hat{x}) is used.\ntemplate <size_t VolumeDim>\nvoid test_neuler_vs_generic_minmod_work(\n    const Scalar<DataVector>& input_density,\n    const tnsr::I<DataVector, VolumeDim>& input_momentum,\n    const Scalar<DataVector>& input_energy, const Mesh<VolumeDim>& mesh,\n    const tnsr::I<DataVector, VolumeDim, Frame::ElementLogical>& logical_coords,\n    const std::array<double, VolumeDim>& element_size,\n    const Scalar<DataVector>& det_inv_logical_to_inertial_jacobian,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename NewtonianEuler::Limiters::Minmod<VolumeDim>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data) {\n  // Sanity check that input momentum satisfies simplifying assumptions\n  if constexpr (VolumeDim > 1) {\n    const auto zero_momentum = make_with_value<tnsr::I<DataVector, VolumeDim>>(\n        get<0>(input_momentum), 0.);\n    REQUIRE(input_momentum == zero_momentum);\n  }\n\n  const double tvb_constant = 0.;\n  const auto element = TestHelpers::Limiters::make_element<VolumeDim>();\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n\n  {\n    INFO(\"Compare specialized vs generic Minmod on conserved variables\");\n    auto density_generic = input_density;\n    auto momentum_generic = input_momentum;\n    auto energy_generic = input_energy;\n    const Limiters::Minmod<\n        VolumeDim, tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                              NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                              NewtonianEuler::Tags::EnergyDensity>>\n        minmod_generic(Limiters::MinmodType::LambdaPi1, tvb_constant);\n    const bool activated_generic = minmod_generic(\n        make_not_null(&density_generic), make_not_null(&momentum_generic),\n        make_not_null(&energy_generic), mesh, element, logical_coords,\n        element_size, neighbor_data);\n\n    auto density_specialized = input_density;\n    auto momentum_specialized = input_momentum;\n    auto energy_specialized = input_energy;\n    const auto vars_to_limit =\n        NewtonianEuler::Limiters::VariablesToLimit::Conserved;\n    const bool apply_flattener = false;\n    const NewtonianEuler::Limiters::Minmod<VolumeDim> minmod_specialized(\n        Limiters::MinmodType::LambdaPi1, vars_to_limit, tvb_constant,\n        apply_flattener);\n    const bool activated_specialized = minmod_specialized(\n        make_not_null(&density_specialized),\n        make_not_null(&momentum_specialized),\n        make_not_null(&energy_specialized), mesh, element, logical_coords,\n        element_size, det_inv_logical_to_inertial_jacobian, equation_of_state,\n        neighbor_data);\n\n    CHECK(activated_generic);\n    CHECK(activated_generic == activated_specialized);\n    CHECK_ITERABLE_APPROX(density_generic, density_specialized);\n    CHECK_ITERABLE_APPROX(momentum_generic, momentum_specialized);\n    CHECK_ITERABLE_APPROX(energy_generic, energy_specialized);\n  }\n\n  {\n    INFO(\"Compare specialized vs generic Minmod on characteristic variables\");\n    // Cellwise means, for cons/char transforms\n    const auto mean_density =\n        Scalar<double>{mean_value(get(input_density), mesh)};\n    const auto mean_momentum = [&input_momentum, &mesh]() {\n      tnsr::I<double, VolumeDim> result{};\n      for (size_t i = 0; i < VolumeDim; ++i) {\n        result.get(i) = mean_value(input_momentum.get(i), mesh);\n      }\n      return result;\n    }();\n    const auto mean_energy =\n        Scalar<double>{mean_value(get(input_energy), mesh)};\n\n    // Compute characteristic transformation using x-direction...\n    // Note that in 2D and 3D we know the fluid velocity is 0, so this choice\n    // is arbitrary in these higher dimensions.\n    const auto unit_vector = []() {\n      auto components = make_array<VolumeDim>(0.);\n      components[0] = 1.;\n      return tnsr::i<double, VolumeDim>(components);\n    }();\n    const auto right_and_left =\n        NewtonianEuler::Limiters::right_and_left_eigenvectors(\n            mean_density, mean_momentum, mean_energy, equation_of_state,\n            unit_vector);\n    const auto& right = right_and_left.first;\n    const auto& left = right_and_left.second;\n\n    // Transform tensors to characteristics\n    auto char_v_minus = input_density;\n    auto char_v_momentum = input_momentum;\n    auto char_v_plus = input_energy;\n    NewtonianEuler::Limiters::characteristic_fields(\n        make_not_null(&char_v_minus), make_not_null(&char_v_momentum),\n        make_not_null(&char_v_plus), input_density, input_momentum,\n        input_energy, left);\n\n    using GenericMinmod =\n        Limiters::Minmod<VolumeDim,\n                         tmpl::list<NewtonianEuler::Tags::VMinus,\n                                    NewtonianEuler::Tags::VMomentum<VolumeDim>,\n                                    NewtonianEuler::Tags::VPlus>>;\n    std::unordered_map<DirectionalId<VolumeDim>,\n                       typename GenericMinmod::PackagedData,\n                       boost::hash<DirectionalId<VolumeDim>>>\n        neighbor_char_data{};\n    for (const auto& [key, data] : neighbor_data) {\n      neighbor_char_data[key].element_size = data.element_size;\n      NewtonianEuler::Limiters::characteristic_fields(\n          make_not_null(&(neighbor_char_data[key].means)), data.means, left);\n    }\n\n    const GenericMinmod minmod_generic(Limiters::MinmodType::LambdaPi1,\n                                       tvb_constant);\n    const bool activated_generic = minmod_generic(\n        make_not_null(&char_v_minus), make_not_null(&char_v_momentum),\n        make_not_null(&char_v_plus), mesh, element, logical_coords,\n        element_size, neighbor_char_data);\n\n    // Transform back to evolved variables\n    // Note that because the fluid velocity is 0 and all sets of characteristics\n    // are identical, we can skip the step of combining different limiter\n    // results.\n    auto density_generic = input_density;\n    auto momentum_generic = input_momentum;\n    auto energy_generic = input_energy;\n    NewtonianEuler::Limiters::conserved_fields_from_characteristic_fields(\n        make_not_null(&density_generic), make_not_null(&momentum_generic),\n        make_not_null(&energy_generic), char_v_minus, char_v_momentum,\n        char_v_plus, right);\n\n    auto density_specialized = input_density;\n    auto momentum_specialized = input_momentum;\n    auto energy_specialized = input_energy;\n    const auto vars_to_limit =\n        NewtonianEuler::Limiters::VariablesToLimit::Characteristic;\n    const bool apply_flattener = false;\n    const NewtonianEuler::Limiters::Minmod<VolumeDim> minmod_specialized(\n        Limiters::MinmodType::LambdaPi1, vars_to_limit, tvb_constant,\n        apply_flattener);\n    const bool activated_specialized = minmod_specialized(\n        make_not_null(&density_specialized),\n        make_not_null(&momentum_specialized),\n        make_not_null(&energy_specialized), mesh, element, logical_coords,\n        element_size, det_inv_logical_to_inertial_jacobian, equation_of_state,\n        neighbor_data);\n\n    CHECK(activated_generic);\n    CHECK(activated_generic == activated_specialized);\n    CHECK_ITERABLE_APPROX(density_generic, density_specialized);\n    CHECK_ITERABLE_APPROX(momentum_generic, momentum_specialized);\n    CHECK_ITERABLE_APPROX(energy_generic, energy_specialized);\n  }\n}\n\nvoid test_neuler_vs_generic_minmod_1d() {\n  INFO(\"Testing NewtonianEuler::Limiters::Minmod limiter in 1D\");\n  const auto mesh =\n      Mesh<1>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const auto logical_coords = logical_coordinates(mesh);\n  using Affine = domain::CoordinateMaps::Affine;\n  const Affine xi_map{-1., 1., 3.7, 4.2};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          xi_map);\n  const ElementMap<1, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n  const auto element_size = size_of_element(element_map);\n  const auto det_inv_logical_to_inertial_jacobian =\n      determinant(element_map.inv_jacobian(logical_coords));\n\n  const auto& x = get<0>(logical_coords);\n  const auto mass_density_cons = [&x]() {\n    return Scalar<DataVector>{{{1. + 0.2 * x + 0.05 * square(x)}}};\n  }();\n  const auto momentum_density = [&x]() {\n    return tnsr::I<DataVector, 1>{{{0.2 - 0.3 * x}}};\n  }();\n  const auto energy_density = [&mesh]() {\n    return Scalar<DataVector>{DataVector(mesh.number_of_grid_points(), 1.)};\n  }();\n\n  std::unordered_map<DirectionalId<1>,\n                     NewtonianEuler::Limiters::Minmod<1>::PackagedData,\n                     boost::hash<DirectionalId<1>>>\n      neighbor_data{};\n  const std::array<DirectionalId<1>, 2> dir_keys = {\n      {{Direction<1>::lower_xi(), ElementId<1>(1)},\n       {Direction<1>::upper_xi(), ElementId<1>(2)}}};\n  for (const auto& dir_key : dir_keys) {\n    neighbor_data[dir_key].element_size = element_size;\n  }\n\n  using mean_rho = Tags::Mean<NewtonianEuler::Tags::MassDensityCons>;\n  get(get<mean_rho>(neighbor_data[dir_keys[0]].means)) = 0.4;\n  get(get<mean_rho>(neighbor_data[dir_keys[1]].means)) = 1.1;\n\n  using mean_rhou = Tags::Mean<NewtonianEuler::Tags::MomentumDensity<1>>;\n  get<0>(get<mean_rhou>(neighbor_data[dir_keys[0]].means)) = 0.2;\n  get<0>(get<mean_rhou>(neighbor_data[dir_keys[1]].means)) = -0.1;\n\n  using mean_eps = Tags::Mean<NewtonianEuler::Tags::EnergyDensity>;\n  get(get<mean_eps>(neighbor_data[dir_keys[0]].means)) = 1.;\n  get(get<mean_eps>(neighbor_data[dir_keys[1]].means)) = 1.;\n\n  test_neuler_vs_generic_minmod_work(\n      mass_density_cons, momentum_density, energy_density, mesh, logical_coords,\n      element_size, det_inv_logical_to_inertial_jacobian, neighbor_data);\n}\n\nvoid test_neuler_vs_generic_minmod_2d() {\n  INFO(\"Testing NewtonianEuler::Limiters::Minmod limiter in 2D\");\n  const auto mesh =\n      Mesh<2>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto element = TestHelpers::Limiters::make_element<2>();\n  const auto logical_coords = logical_coordinates(mesh);\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  const Affine xi_map{-1., 1., 3.7, 4.2};\n  const Affine eta_map{-1., 1., 3.2, 4.2};\n  const Affine2D map2d{xi_map, eta_map};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          map2d);\n  const ElementMap<2, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n  const auto element_size = size_of_element(element_map);\n  const auto det_inv_logical_to_inertial_jacobian =\n      determinant(element_map.inv_jacobian(logical_coords));\n\n  const auto& x = get<0>(logical_coords);\n  const auto& y = get<1>(logical_coords);\n  const auto mass_density_cons = [&x, &y]() {\n    return Scalar<DataVector>{\n        {{1. + 0.2 * x - 0.1 * y + 0.05 * x * square(y)}}};\n  }();\n  const auto momentum_density = [&mesh]() {\n    return tnsr::I<DataVector, 2>{DataVector(mesh.number_of_grid_points(), 0.)};\n  }();\n  const auto energy_density = [&mesh]() {\n    return Scalar<DataVector>{DataVector(mesh.number_of_grid_points(), 1.)};\n  }();\n\n  std::unordered_map<DirectionalId<2>,\n                     NewtonianEuler::Limiters::Minmod<2>::PackagedData,\n                     boost::hash<DirectionalId<2>>>\n      neighbor_data{};\n  const std::array<DirectionalId<2>, 4> dir_keys = {\n      {{Direction<2>::lower_xi(), ElementId<2>(1)},\n       {Direction<2>::upper_xi(), ElementId<2>(2)},\n       {Direction<2>::lower_eta(), ElementId<2>(3)},\n       {Direction<2>::upper_eta(), ElementId<2>(4)}}};\n  for (const auto& dir_key : dir_keys) {\n    neighbor_data[dir_key].element_size = element_size;\n  }\n\n  using mean_rho = Tags::Mean<NewtonianEuler::Tags::MassDensityCons>;\n  get(get<mean_rho>(neighbor_data[dir_keys[0]].means)) = 0.4;\n  get(get<mean_rho>(neighbor_data[dir_keys[1]].means)) = 1.1;\n  get(get<mean_rho>(neighbor_data[dir_keys[2]].means)) = 2.1;\n  get(get<mean_rho>(neighbor_data[dir_keys[3]].means)) = 0.9;\n\n  for (const auto& dir_key : dir_keys) {\n    using mean_rhou = Tags::Mean<NewtonianEuler::Tags::MomentumDensity<2>>;\n    get<0>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    get<1>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    using mean_eps = Tags::Mean<NewtonianEuler::Tags::EnergyDensity>;\n    get(get<mean_eps>(neighbor_data[dir_key].means)) = 1.;\n  }\n\n  test_neuler_vs_generic_minmod_work(\n      mass_density_cons, momentum_density, energy_density, mesh, logical_coords,\n      element_size, det_inv_logical_to_inertial_jacobian, neighbor_data);\n}\n\nvoid test_neuler_vs_generic_minmod_3d() {\n  INFO(\"Testing NewtonianEuler::Limiters::Minmod limiter in 3D\");\n  const auto mesh =\n      Mesh<3>(std::array<size_t, 3>{{3, 3, 4}}, Spectral::Basis::Legendre,\n              Spectral::Quadrature::GaussLobatto);\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const auto logical_coords = logical_coordinates(mesh);\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const Affine xi_map{-1., 1., 3.7, 4.2};\n  const Affine eta_map{-1., 1., 3.2, 4.2};\n  const Affine zeta_map{-1., 1., 1.3, 2.1};\n  const Affine3D map3d{xi_map, eta_map, zeta_map};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          map3d);\n  const ElementMap<3, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n  const auto element_size = size_of_element(element_map);\n  const auto det_inv_logical_to_inertial_jacobian =\n      determinant(element_map.inv_jacobian(logical_coords));\n\n  const auto& x = get<0>(logical_coords);\n  const auto& y = get<1>(logical_coords);\n  const auto& z = get<2>(logical_coords);\n  const auto mass_density_cons = [&x, &y, &z]() {\n    return Scalar<DataVector>{{{1. + 0.2 * x - 0.1 * y + 0.4 * z}}};\n  }();\n  const auto momentum_density = [&mesh]() {\n    return tnsr::I<DataVector, 3>{DataVector(mesh.number_of_grid_points(), 0.)};\n  }();\n  const auto energy_density = [&mesh]() {\n    return Scalar<DataVector>{DataVector(mesh.number_of_grid_points(), 1.)};\n  }();\n\n  std::unordered_map<DirectionalId<3>,\n                     NewtonianEuler::Limiters::Minmod<3>::PackagedData,\n                     boost::hash<DirectionalId<3>>>\n      neighbor_data{};\n  const std::array<DirectionalId<3>, 6> dir_keys = {\n      {{Direction<3>::lower_xi(), ElementId<3>(1)},\n       {Direction<3>::upper_xi(), ElementId<3>(2)},\n       {Direction<3>::lower_eta(), ElementId<3>(3)},\n       {Direction<3>::upper_eta(), ElementId<3>(4)},\n       {Direction<3>::lower_zeta(), ElementId<3>(5)},\n       {Direction<3>::upper_zeta(), ElementId<3>(6)}}};\n  for (const auto& dir_key : dir_keys) {\n    neighbor_data[dir_key].element_size = element_size;\n  }\n\n  using mean_rho = Tags::Mean<NewtonianEuler::Tags::MassDensityCons>;\n  get(get<mean_rho>(neighbor_data[dir_keys[0]].means)) = 0.;\n  get(get<mean_rho>(neighbor_data[dir_keys[1]].means)) = 0.;\n  get(get<mean_rho>(neighbor_data[dir_keys[2]].means)) = 0.1;\n  get(get<mean_rho>(neighbor_data[dir_keys[3]].means)) = 1.2;\n  get(get<mean_rho>(neighbor_data[dir_keys[4]].means)) = 1.1;\n  get(get<mean_rho>(neighbor_data[dir_keys[5]].means)) = 0.9;\n\n  for (const auto& dir_key : dir_keys) {\n    using mean_rhou = Tags::Mean<NewtonianEuler::Tags::MomentumDensity<3>>;\n    get<0>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    get<1>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    get<2>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    using mean_eps = Tags::Mean<NewtonianEuler::Tags::EnergyDensity>;\n    get(get<mean_eps>(neighbor_data[dir_key].means)) = 1.;\n  }\n\n  test_neuler_vs_generic_minmod_work(\n      mass_density_cons, momentum_density, energy_density, mesh, logical_coords,\n      element_size, det_inv_logical_to_inertial_jacobian, neighbor_data);\n}\n\ntemplate <size_t VolumeDim>\nvoid test_neuler_minmod_flattener() {\n  INFO(\"Testing flattener use in NewtonianEuler::Limiters::Minmod limiter\");\n  CAPTURE(VolumeDim);\n\n  const auto mesh = Mesh<VolumeDim>(2, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto);\n  // We use an element with no neighbors so the limiter does nothing\n  const auto element = Element<VolumeDim>{ElementId<VolumeDim>(0), {}};\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto element_size = make_array<VolumeDim>(0.6);\n  // inv_jac = logical volume / inertial volume\n  const Scalar<DataVector> det_inv_logical_to_inertial_jacobian{DataVector(\n      mesh.number_of_grid_points(), pow<VolumeDim>(2.) / pow<VolumeDim>(0.6))};\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n\n  const size_t num_points = mesh.number_of_grid_points();\n  const auto input_density = [&num_points]() {\n    // One negative value to trigger flattener\n    Scalar<DataVector> density{DataVector(num_points, 0.8)};\n    get(density)[0] = -0.2;\n    return density;\n  }();\n  const auto input_momentum = [&num_points]() {\n    return tnsr::I<DataVector, VolumeDim>{DataVector(num_points, 0.1)};\n  }();\n  const auto input_energy = [&num_points]() {\n    return Scalar<DataVector>{DataVector(num_points, 1.4)};\n  }();\n\n  // Empty map because no neighbors\n  const std::unordered_map<\n      DirectionalId<VolumeDim>,\n      typename NewtonianEuler::Limiters::Minmod<VolumeDim>::PackagedData,\n      boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_data{};\n\n  // First a sanity check: there should be a negative density for this test to\n  // make sense... otherwise we aren't testing anything useful\n  const bool has_negative_density = min(get(input_density)) < 0.;\n  REQUIRE(has_negative_density);\n  CHECK(true);\n\n  // With flattener off, check the limiter does nothing (because no neighbors).\n  //\n  // Note that the post-limiter result is unphysical: the density is still\n  // negative at one point. Because of this, we have to use the generic limiter\n  // when testing the limiter does nothing: the specialized limiter has an\n  // ASSERT that the post-limiting solution is valid which would fail here.\n  auto density = input_density;\n  auto momentum = input_momentum;\n  auto energy = input_energy;\n  const double tvb_constant = 0.;\n  const Limiters::Minmod<\n      VolumeDim, tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                            NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                            NewtonianEuler::Tags::EnergyDensity>>\n      minmod_noflat(Limiters::MinmodType::LambdaPi1, tvb_constant);\n  const bool activated_noflat = minmod_noflat(\n      make_not_null(&density), make_not_null(&momentum), make_not_null(&energy),\n      mesh, element, logical_coords, element_size, neighbor_data);\n  CHECK_FALSE(activated_noflat);\n\n  // With flattener on, check limiter activates\n  density = input_density;\n  momentum = input_momentum;\n  energy = input_energy;\n  const auto vars_to_limit =\n      NewtonianEuler::Limiters::VariablesToLimit::Conserved;\n  const NewtonianEuler::Limiters::Minmod<VolumeDim> minmod(\n      Limiters::MinmodType::LambdaPi1, vars_to_limit, tvb_constant, true);\n  const bool activated = minmod(\n      make_not_null(&density), make_not_null(&momentum), make_not_null(&energy),\n      mesh, element, logical_coords, element_size,\n      det_inv_logical_to_inertial_jacobian, equation_of_state, neighbor_data);\n  CHECK(activated);\n\n  // Finally, check the negative density is brought back to positive\n  const bool density_is_positive_everywhere = min(get(density)) > 0.;\n  CHECK(density_is_positive_everywhere);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Limiters.Minmod\",\n                  \"[Limiters][Unit]\") {\n  test_neuler_minmod_option_parsing();\n  test_neuler_minmod_serialization();\n\n  // The package_data function for NewtonianEuler::Limiters::Minmod is just a\n  // direct call to the generic Minmod package_data. We do not test it.\n\n  // We test the action of NewtonianEuler::Limiters::Minmod by comparing it to\n  // the action of the generic Minmod, for certain inputs where the comparison\n  // is not too difficult. To fully test the specialized limiter, would need to\n  // completely reimplement the logic within the test.\n  test_neuler_vs_generic_minmod_1d();\n  test_neuler_vs_generic_minmod_2d();\n  test_neuler_vs_generic_minmod_3d();\n\n  // Test the use of the post-processing flattener\n  test_neuler_minmod_flattener<1>();\n  test_neuler_minmod_flattener<2>();\n  test_neuler_minmod_flattener<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Limiters/Test_VariablesToLimit.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/Systems/NewtonianEuler/Limiters/VariablesToLimit.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Limiters.VariablesToLimit\",\n    \"[Limiters][Unit]\") {\n  CHECK(NewtonianEuler::Limiters::VariablesToLimit::Conserved ==\n        TestHelpers::test_creation<NewtonianEuler::Limiters::VariablesToLimit>(\n            \"Conserved\"));\n  CHECK(NewtonianEuler::Limiters::VariablesToLimit::Characteristic ==\n        TestHelpers::test_creation<NewtonianEuler::Limiters::VariablesToLimit>(\n            \"Characteristic\"));\n  CHECK(NewtonianEuler::Limiters::VariablesToLimit::NumericalCharacteristic ==\n        TestHelpers::test_creation<NewtonianEuler::Limiters::VariablesToLimit>(\n            \"NumericalCharacteristic\"));\n\n  CHECK(get_output(NewtonianEuler::Limiters::VariablesToLimit::Conserved) ==\n        \"Conserved\");\n  CHECK(\n      get_output(NewtonianEuler::Limiters::VariablesToLimit::Characteristic) ==\n      \"Characteristic\");\n  CHECK(get_output(NewtonianEuler::Limiters::VariablesToLimit::\n                       NumericalCharacteristic) == \"NumericalCharacteristic\");\n\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<NewtonianEuler::Limiters::VariablesToLimit>(\n          \"BadVars\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"BadVars\\\" to VariablesToLimit\"));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Limiters/Test_Weno.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/Weno.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Limiters/WenoType.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/CharacteristicHelpers.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Limiters/Weno.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nvoid test_neuler_weno_option_parsing() {\n  INFO(\"Testing option parsing\");\n  const auto sweno =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<1>>(\n          \"Type: SimpleWeno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"KxrcfConstant: None\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto sweno_nw =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<1>>(\n          \"Type: SimpleWeno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.02\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"KxrcfConstant: None\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto sweno_cons =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<1>>(\n          \"Type: SimpleWeno\\n\"\n          \"VariablesToLimit: Conserved\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"KxrcfConstant: None\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto sweno_tvb =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<1>>(\n          \"Type: SimpleWeno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 1.0\\n\"\n          \"KxrcfConstant: None\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto sweno_noflat =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<1>>(\n          \"Type: SimpleWeno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"KxrcfConstant: None\\n\"\n          \"ApplyFlattener: False\\n\"\n          \"DisableForDebugging: False\");\n  const auto sweno_disabled =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<1>>(\n          \"Type: SimpleWeno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"KxrcfConstant: None\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: True\");\n  const auto hweno =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<1>>(\n          \"Type: Hweno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: None\\n\"\n          \"KxrcfConstant: 0.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto hweno_kxrcf =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<1>>(\n          \"Type: Hweno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: None\\n\"\n          \"KxrcfConstant: 1.0\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n\n  // Test operators == and !=\n  CHECK(sweno == sweno);\n  CHECK(sweno != sweno_nw);\n  CHECK(sweno != sweno_cons);\n  CHECK(sweno != sweno_tvb);\n  CHECK(sweno != sweno_noflat);\n  CHECK(sweno != sweno_disabled);\n  CHECK(sweno != hweno);\n  CHECK(hweno != hweno_kxrcf);\n\n  const auto sweno_2d =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<2>>(\n          \"Type: SimpleWeno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"KxrcfConstant: None\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n  const auto sweno_3d =\n      TestHelpers::test_creation<NewtonianEuler::Limiters::Weno<3>>(\n          \"Type: SimpleWeno\\n\"\n          \"VariablesToLimit: Characteristic\\n\"\n          \"NeighborWeight: 0.001\\n\"\n          \"TvbConstant: 0.0\\n\"\n          \"KxrcfConstant: None\\n\"\n          \"ApplyFlattener: True\\n\"\n          \"DisableForDebugging: False\");\n\n  // Test that creation from options gives correct object\n  const NewtonianEuler::Limiters::Weno<1> expected_sweno(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, 0.0,\n      {}, true);\n  const NewtonianEuler::Limiters::Weno<1> expected_sweno_nw(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.02, 0.0, {},\n      true);\n  const NewtonianEuler::Limiters::Weno<1> expected_sweno_cons(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Conserved, 0.001, 0.0, {},\n      true);\n  const NewtonianEuler::Limiters::Weno<1> expected_sweno_tvb(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, 1.0,\n      {}, true);\n  const NewtonianEuler::Limiters::Weno<1> expected_sweno_noflat(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, 0.0,\n      {}, false);\n  const NewtonianEuler::Limiters::Weno<1> expected_sweno_disabled(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, 0.0,\n      {}, true, true);\n  const NewtonianEuler::Limiters::Weno<1> expected_hweno(\n      Limiters::WenoType::Hweno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, {},\n      0.0, true);\n  const NewtonianEuler::Limiters::Weno<1> expected_hweno_kxrcf(\n      Limiters::WenoType::Hweno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, {},\n      1.0, true);\n  const NewtonianEuler::Limiters::Weno<2> expected_sweno_2d(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, 0.0,\n      {}, true);\n  const NewtonianEuler::Limiters::Weno<3> expected_sweno_3d(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, 0.0,\n      {}, true);\n\n  CHECK(sweno == expected_sweno);\n  CHECK(sweno_nw == expected_sweno_nw);\n  CHECK(sweno_cons == expected_sweno_cons);\n  CHECK(sweno_tvb == expected_sweno_tvb);\n  CHECK(sweno_noflat == expected_sweno_noflat);\n  CHECK(sweno_disabled == expected_sweno_disabled);\n  CHECK(hweno == expected_hweno);\n  CHECK(hweno_kxrcf == expected_hweno_kxrcf);\n  CHECK(sweno_2d == expected_sweno_2d);\n  CHECK(sweno_3d == expected_sweno_3d);\n}\n\nvoid test_neuler_weno_serialization() {\n  INFO(\"Testing serialization\");\n  const NewtonianEuler::Limiters::Weno<1> weno(\n      Limiters::WenoType::SimpleWeno,\n      NewtonianEuler::Limiters::VariablesToLimit::Characteristic, 0.001, 1., {},\n      true);\n  test_serialization(weno);\n}\n\ntemplate <size_t Dim>\nDirectionMap<Dim, std::optional<Variables<\n                      tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                 evolution::dg::Tags::NormalCovector<Dim>>>>>\ncompute_normals_and_magnitudes(\n    const Mesh<Dim>& mesh,\n    const ElementMap<Dim, Frame::Inertial>& element_map) {\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      normals_and_magnitudes{};\n  for (const auto& dir : Direction<Dim>::all_directions()) {\n    const auto boundary_mesh = mesh.slice_away(dir.dimension());\n    normals_and_magnitudes[dir] =\n        Variables<tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                             evolution::dg::Tags::NormalCovector<Dim>>>(\n            boundary_mesh.number_of_grid_points());\n    auto& covector = get<evolution::dg::Tags::NormalCovector<Dim>>(\n        normals_and_magnitudes[dir].value());\n    auto& normal_magnitude = get<evolution::dg::Tags::MagnitudeOfNormal>(\n        normals_and_magnitudes[dir].value());\n    unnormalized_face_normal(make_not_null(&(covector)),\n                             mesh.slice_away(dir.dimension()), element_map,\n                             dir);\n    normal_magnitude = magnitude(covector);\n    for (size_t d = 0; d < Dim; ++d) {\n      covector.get(d) /= get(normal_magnitude);\n    }\n  }\n  return normals_and_magnitudes;\n}\n\n// Compare the specialized limiter to the generic limiter. The limiters are\n// compared for limiting conserved/evolved and limiting characteristic\n// variables.\n//\n// Note: comparing the specialized and generic limiters when limiting the\n// characteristic variables is tedious to do thoroughly, because we need to\n// call the generic limiter multiple times using characteristic fields w.r.t.\n// multiple different unit vectors. In essence, the test would need to fully\n// reimplement the specialized limiter. To avoid the test becoming so tedious,\n// we test a simpler restricted problem, where in 2D and 3D we require\n// input_momentum to be 0. With this simplification, the characteristic\n// decomposition becomes independent of the choice of unit vector, and therefore\n// the generic limiter can match the specialized limiter even when only one set\n// of characteristic (e.g., w.r.t. \\hat{x}) is used.\n//\n// However, this simplifying assumption doesn't play nicely with the HWENO\n// limiter, because it uses the KXRCF TCI that relies on non-zero velocities to\n// trigger the limiter... so we can use this function with HWENO only in 1D\n// where we allow non-zero velocities.\ntemplate <size_t VolumeDim>\nvoid test_neuler_vs_generic_weno_work(\n    const Limiters::WenoType weno_type, const Scalar<DataVector>& input_density,\n    const tnsr::I<DataVector, VolumeDim>& input_momentum,\n    const Scalar<DataVector>& input_energy, const Mesh<VolumeDim>& mesh,\n    const std::array<double, VolumeDim>& element_size,\n    const Scalar<DataVector>& det_inv_logical_to_inertial_jacobian,\n    const typename evolution::dg::Tags::NormalCovectorAndMagnitude<\n        VolumeDim>::type& normals_and_magnitudes,\n    const std::unordered_map<\n        DirectionalId<VolumeDim>,\n        typename NewtonianEuler::Limiters::Weno<VolumeDim>::PackagedData,\n        boost::hash<DirectionalId<VolumeDim>>>& neighbor_data) {\n  // Sanity check that\n  // - input momentum satisfies simplifying assumptions\n  // - limiter type is consistent with these simplifying assumptions\n  if constexpr (VolumeDim > 1) {\n    const auto zero_momentum = make_with_value<tnsr::I<DataVector, VolumeDim>>(\n        get<0>(input_momentum), 0.);\n    REQUIRE(input_momentum == zero_momentum);\n    REQUIRE(weno_type == Limiters::WenoType::SimpleWeno);\n  }\n\n  CAPTURE(weno_type);\n\n  const double neighbor_linear_weight = 0.001;\n  const double tvb_constant = 0.;\n  const double kxrcf_constant = 0.;\n  const auto element = TestHelpers::Limiters::make_element<VolumeDim>();\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n\n  {\n    INFO(\"Compare specialized vs generic Weno on conserved variables\");\n    auto density_generic = input_density;\n    auto momentum_generic = input_momentum;\n    auto energy_generic = input_energy;\n    const Limiters::Weno<\n        VolumeDim, tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                              NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                              NewtonianEuler::Tags::EnergyDensity>>\n        weno_generic(weno_type, neighbor_linear_weight, tvb_constant);\n    const bool activated_generic = weno_generic(\n        make_not_null(&density_generic), make_not_null(&momentum_generic),\n        make_not_null(&energy_generic), mesh, element, element_size,\n        neighbor_data);\n\n    std::optional<double> opt_tvb_constant{};\n    std::optional<double> opt_kxrcf_constant{};\n    if (weno_type == Limiters::WenoType::SimpleWeno) {\n      opt_tvb_constant = tvb_constant;\n    } else if (weno_type == Limiters::WenoType::Hweno) {\n      opt_kxrcf_constant = kxrcf_constant;\n    }\n    const bool apply_flattener = false;\n\n    auto density_specialized = input_density;\n    auto momentum_specialized = input_momentum;\n    auto energy_specialized = input_energy;\n    const auto vars_to_limit =\n        NewtonianEuler::Limiters::VariablesToLimit::Conserved;\n    const NewtonianEuler::Limiters::Weno<VolumeDim> weno_specialized(\n        weno_type, vars_to_limit, neighbor_linear_weight, opt_tvb_constant,\n        opt_kxrcf_constant, apply_flattener);\n    const bool activated_specialized = weno_specialized(\n        make_not_null(&density_specialized),\n        make_not_null(&momentum_specialized),\n        make_not_null(&energy_specialized), mesh, element, element_size,\n        det_inv_logical_to_inertial_jacobian, normals_and_magnitudes,\n        equation_of_state, neighbor_data);\n\n    CHECK(activated_generic);\n    CHECK(activated_generic == activated_specialized);\n    CHECK_ITERABLE_APPROX(density_generic, density_specialized);\n    CHECK_ITERABLE_APPROX(momentum_generic, momentum_specialized);\n    CHECK_ITERABLE_APPROX(energy_generic, energy_specialized);\n  }\n\n  {\n    INFO(\"Compare specialized vs generic Weno on characteristic variables\");\n    // Cellwise means, for cons/char transforms\n    const auto mean_density =\n        Scalar<double>{mean_value(get(input_density), mesh)};\n    const auto mean_momentum = [&input_momentum, &mesh]() {\n      tnsr::I<double, VolumeDim> result{};\n      for (size_t i = 0; i < VolumeDim; ++i) {\n        result.get(i) = mean_value(input_momentum.get(i), mesh);\n      }\n      return result;\n    }();\n    const auto mean_energy =\n        Scalar<double>{mean_value(get(input_energy), mesh)};\n\n    // Compute characteristic transformation using x-direction...\n    // Note that in 2D and 3D we know the fluid velocity is 0, so this\n    // choice is arbitrary in these higher dimensions.\n    const auto unit_vector = []() {\n      auto components = make_array<VolumeDim>(0.);\n      components[0] = 1.;\n      return tnsr::i<double, VolumeDim>(components);\n    }();\n    const auto right_and_left =\n        NewtonianEuler::Limiters::right_and_left_eigenvectors(\n            mean_density, mean_momentum, mean_energy, equation_of_state,\n            unit_vector);\n    const auto& right = right_and_left.first;\n    const auto& left = right_and_left.second;\n\n    // Transform tensors to characteristics\n    auto char_v_minus = input_density;\n    auto char_v_momentum = input_momentum;\n    auto char_v_plus = input_energy;\n    NewtonianEuler::Limiters::characteristic_fields(\n        make_not_null(&char_v_minus), make_not_null(&char_v_momentum),\n        make_not_null(&char_v_plus), input_density, input_momentum,\n        input_energy, left);\n\n    using GenericWeno =\n        Limiters::Weno<VolumeDim,\n                       tmpl::list<NewtonianEuler::Tags::VMinus,\n                                  NewtonianEuler::Tags::VMomentum<VolumeDim>,\n                                  NewtonianEuler::Tags::VPlus>>;\n    std::unordered_map<DirectionalId<VolumeDim>,\n                       typename GenericWeno::PackagedData,\n                       boost::hash<DirectionalId<VolumeDim>>>\n        neighbor_char_data{};\n    for (const auto& [key, data] : neighbor_data) {\n      neighbor_char_data[key].element_size = data.element_size;\n      neighbor_char_data[key].mesh = data.mesh;\n      NewtonianEuler::Limiters::characteristic_fields(\n          make_not_null(&(neighbor_char_data[key].means)), data.means, left);\n      neighbor_char_data[key].volume_data.initialize(\n          mesh.number_of_grid_points());\n      NewtonianEuler::Limiters::characteristic_fields(\n          make_not_null(&(neighbor_char_data[key].volume_data)),\n          data.volume_data, left);\n    }\n\n    const GenericWeno weno_generic(weno_type, neighbor_linear_weight,\n                                   tvb_constant);\n    const bool activated_generic = weno_generic(\n        make_not_null(&char_v_minus), make_not_null(&char_v_momentum),\n        make_not_null(&char_v_plus), mesh, element, element_size,\n        neighbor_char_data);\n\n    // Transform back to evolved variables\n    // Note that because the fluid velocity is 0 and all sets of\n    // characteristics are identical, we can skip the step of combining\n    // different limiter results.\n    auto density_generic = input_density;\n    auto momentum_generic = input_momentum;\n    auto energy_generic = input_energy;\n    NewtonianEuler::Limiters::conserved_fields_from_characteristic_fields(\n        make_not_null(&density_generic), make_not_null(&momentum_generic),\n        make_not_null(&energy_generic), char_v_minus, char_v_momentum,\n        char_v_plus, right);\n\n    std::optional<double> opt_tvb_constant{};\n    std::optional<double> opt_kxrcf_constant{};\n    if (weno_type == Limiters::WenoType::SimpleWeno) {\n      opt_tvb_constant = tvb_constant;\n    } else if (weno_type == Limiters::WenoType::Hweno) {\n      opt_kxrcf_constant = kxrcf_constant;\n    }\n    const bool apply_flattener = false;\n\n    auto density_specialized = input_density;\n    auto momentum_specialized = input_momentum;\n    auto energy_specialized = input_energy;\n    const auto vars_to_limit =\n        NewtonianEuler::Limiters::VariablesToLimit::Characteristic;\n    const NewtonianEuler::Limiters::Weno<VolumeDim> weno_specialized(\n        weno_type, vars_to_limit, neighbor_linear_weight, opt_tvb_constant,\n        opt_kxrcf_constant, apply_flattener);\n    const bool activated_specialized = weno_specialized(\n        make_not_null(&density_specialized),\n        make_not_null(&momentum_specialized),\n        make_not_null(&energy_specialized), mesh, element, element_size,\n        det_inv_logical_to_inertial_jacobian, normals_and_magnitudes,\n        equation_of_state, neighbor_data);\n\n    CHECK(activated_generic);\n    CHECK(activated_generic == activated_specialized);\n    CHECK_ITERABLE_APPROX(density_generic, density_specialized);\n    CHECK_ITERABLE_APPROX(momentum_generic, momentum_specialized);\n    CHECK_ITERABLE_APPROX(energy_generic, energy_specialized);\n  }\n}\n\nvoid test_neuler_vs_generic_weno_1d() {\n  INFO(\"Testing NewtonianEuler::Limiters::Weno limiter in 1D\");\n  const auto mesh =\n      Mesh<1>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto element = TestHelpers::Limiters::make_element<1>();\n  const auto logical_coords = logical_coordinates(mesh);\n  using Affine = domain::CoordinateMaps::Affine;\n  const Affine xi_map{-1., 1., 3.7, 4.2};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          xi_map);\n  const ElementMap<1, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n  const auto element_size = size_of_element(element_map);\n  const auto det_inv_logical_to_inertial_jacobian =\n      determinant(element_map.inv_jacobian(logical_coords));\n  const auto normals_and_magnitudes =\n      compute_normals_and_magnitudes(mesh, element_map);\n\n  const auto& x = get<0>(logical_coords);\n  const auto mass_density_cons = [&x]() {\n    return Scalar<DataVector>{{{1. + 0.2 * x + 0.05 * square(x)}}};\n  }();\n  const auto momentum_density = [&x]() {\n    return tnsr::I<DataVector, 1>{{{0.2 - 0.3 * x}}};\n  }();\n  const auto energy_density = [&mesh]() {\n    return Scalar<DataVector>{DataVector(mesh.number_of_grid_points(), 1.)};\n  }();\n\n  std::unordered_map<DirectionalId<1>,\n                     NewtonianEuler::Limiters::Weno<1>::PackagedData,\n                     boost::hash<DirectionalId<1>>>\n      neighbor_data{};\n  const std::array<DirectionalId<1>, 2> dir_keys = {\n      {{Direction<1>::lower_xi(), ElementId<1>(1)},\n       {Direction<1>::upper_xi(), ElementId<1>(2)}}};\n  const size_t num_pts = mesh.number_of_grid_points();\n  for (const auto& dir_key : dir_keys) {\n    neighbor_data[dir_key].element_size = element_size;\n    neighbor_data[dir_key].mesh = mesh;\n    neighbor_data[dir_key].volume_data.initialize(num_pts, 0.0);\n  }\n\n  // Note: the WENO algorithms are tested in detail elsewhere, so here we don't\n  // go through the trouble of setting up realistic neighbor data that varies\n  // over the neighbor's grid. Instead we pass in constant volume data because\n  // it's a lot easier to set up.\n  using rho = NewtonianEuler::Tags::MassDensityCons;\n  get(get<rho>(neighbor_data[dir_keys[0]].volume_data)) = 0.4;\n  get(get<rho>(neighbor_data[dir_keys[1]].volume_data)) = 1.1;\n  using mean_rho = Tags::Mean<NewtonianEuler::Tags::MassDensityCons>;\n  get(get<mean_rho>(neighbor_data[dir_keys[0]].means)) = 0.4;\n  get(get<mean_rho>(neighbor_data[dir_keys[1]].means)) = 1.1;\n\n  using rhou = NewtonianEuler::Tags::MomentumDensity<1>;\n  get<0>(get<rhou>(neighbor_data[dir_keys[0]].volume_data)) = 0.2;\n  get<0>(get<rhou>(neighbor_data[dir_keys[1]].volume_data)) = -0.1;\n  using mean_rhou = Tags::Mean<NewtonianEuler::Tags::MomentumDensity<1>>;\n  get<0>(get<mean_rhou>(neighbor_data[dir_keys[0]].means)) = 0.2;\n  get<0>(get<mean_rhou>(neighbor_data[dir_keys[1]].means)) = -0.1;\n\n  using eps = NewtonianEuler::Tags::EnergyDensity;\n  get(get<eps>(neighbor_data[dir_keys[0]].volume_data)) = 1.;\n  get(get<eps>(neighbor_data[dir_keys[1]].volume_data)) = 1.;\n  using mean_eps = Tags::Mean<NewtonianEuler::Tags::EnergyDensity>;\n  get(get<mean_eps>(neighbor_data[dir_keys[0]].means)) = 1.;\n  get(get<mean_eps>(neighbor_data[dir_keys[1]].means)) = 1.;\n\n  test_neuler_vs_generic_weno_work(\n      Limiters::WenoType::SimpleWeno, mass_density_cons, momentum_density,\n      energy_density, mesh, element_size, det_inv_logical_to_inertial_jacobian,\n      normals_and_magnitudes, neighbor_data);\n\n  test_neuler_vs_generic_weno_work(\n      Limiters::WenoType::Hweno, mass_density_cons, momentum_density,\n      energy_density, mesh, element_size, det_inv_logical_to_inertial_jacobian,\n      normals_and_magnitudes, neighbor_data);\n}\n\nvoid test_neuler_vs_generic_weno_2d() {\n  INFO(\"Testing NewtonianEuler::Limiters::Weno limiter in 2D\");\n  const auto mesh =\n      Mesh<2>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto element = TestHelpers::Limiters::make_element<2>();\n  const auto logical_coords = logical_coordinates(mesh);\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  const Affine xi_map{-1., 1., 3.7, 4.2};\n  const Affine eta_map{-1., 1., 3.2, 4.2};\n  const Affine2D map2d{xi_map, eta_map};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          map2d);\n  const ElementMap<2, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n  const auto element_size = size_of_element(element_map);\n  const auto det_inv_logical_to_inertial_jacobian =\n      determinant(element_map.inv_jacobian(logical_coords));\n  const auto normals_and_magnitudes =\n      compute_normals_and_magnitudes(mesh, element_map);\n\n  const auto& x = get<0>(logical_coords);\n  const auto& y = get<1>(logical_coords);\n  const auto mass_density_cons = [&x, &y]() {\n    return Scalar<DataVector>{\n        {{1. + 0.2 * x - 0.1 * y + 0.05 * x * square(y)}}};\n  }();\n  const auto momentum_density = [&mesh]() {\n    return tnsr::I<DataVector, 2>{DataVector(mesh.number_of_grid_points(), 0.)};\n  }();\n  const auto energy_density = [&x, &y]() {\n    return Scalar<DataVector>{{{1.3 + 0.1 * y - 0.06 * square(x) * y}}};\n  }();\n\n  std::unordered_map<DirectionalId<2>,\n                     NewtonianEuler::Limiters::Weno<2>::PackagedData,\n                     boost::hash<DirectionalId<2>>>\n      neighbor_data{};\n  const std::array<DirectionalId<2>, 4> dir_keys = {\n      {{Direction<2>::lower_xi(), ElementId<2>(1)},\n       {Direction<2>::upper_xi(), ElementId<2>(2)},\n       {Direction<2>::lower_eta(), ElementId<2>(3)},\n       {Direction<2>::upper_eta(), ElementId<2>(4)}}};\n  const size_t num_pts = mesh.number_of_grid_points();\n  for (const auto& dir_key : dir_keys) {\n    neighbor_data[dir_key].element_size = element_size;\n    neighbor_data[dir_key].mesh = mesh;\n    neighbor_data[dir_key].volume_data.initialize(num_pts, 0.0);\n  }\n\n  using rho = NewtonianEuler::Tags::MassDensityCons;\n  get(get<rho>(neighbor_data[dir_keys[0]].volume_data)) = 0.4;\n  get(get<rho>(neighbor_data[dir_keys[1]].volume_data)) = 1.1;\n  get(get<rho>(neighbor_data[dir_keys[2]].volume_data)) = 2.1;\n  get(get<rho>(neighbor_data[dir_keys[3]].volume_data)) = 0.9;\n  using mean_rho = Tags::Mean<NewtonianEuler::Tags::MassDensityCons>;\n  get(get<mean_rho>(neighbor_data[dir_keys[0]].means)) = 0.4;\n  get(get<mean_rho>(neighbor_data[dir_keys[1]].means)) = 1.1;\n  get(get<mean_rho>(neighbor_data[dir_keys[2]].means)) = 2.1;\n  get(get<mean_rho>(neighbor_data[dir_keys[3]].means)) = 0.9;\n\n  for (const auto& dir_key : dir_keys) {\n    using mean_rhou = Tags::Mean<NewtonianEuler::Tags::MomentumDensity<2>>;\n    get<0>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    get<1>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    using mean_eps = Tags::Mean<NewtonianEuler::Tags::EnergyDensity>;\n    get(get<mean_eps>(neighbor_data[dir_key].means)) = 1.;\n  }\n\n  test_neuler_vs_generic_weno_work(\n      Limiters::WenoType::SimpleWeno, mass_density_cons, momentum_density,\n      energy_density, mesh, element_size, det_inv_logical_to_inertial_jacobian,\n      normals_and_magnitudes, neighbor_data);\n}\n\nvoid test_neuler_vs_generic_weno_3d() {\n  INFO(\"Testing NewtonianEuler::Limiters::Weno limiter in 3D\");\n  const auto mesh =\n      Mesh<3>(3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto);\n  const auto element = TestHelpers::Limiters::make_element<3>();\n  const auto logical_coords = logical_coordinates(mesh);\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const Affine xi_map{-1., 1., 3.7, 4.2};\n  const Affine eta_map{-1., 1., 3.2, 4.2};\n  const Affine zeta_map{-1., 1., 1.3, 2.1};\n  const Affine3D map3d{xi_map, eta_map, zeta_map};\n  const auto coordmap =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          map3d);\n  const ElementMap<3, Frame::Inertial> element_map(element.id(),\n                                                   coordmap->get_clone());\n  const auto element_size = size_of_element(element_map);\n  const auto det_inv_logical_to_inertial_jacobian =\n      determinant(element_map.inv_jacobian(logical_coords));\n  const auto normals_and_magnitudes =\n      compute_normals_and_magnitudes(mesh, element_map);\n\n  const auto& x = get<0>(logical_coords);\n  const auto& y = get<1>(logical_coords);\n  const auto& z = get<2>(logical_coords);\n  const auto mass_density_cons = [&x, &y, &z]() {\n    return Scalar<DataVector>{{{1. + 0.2 * x - 0.1 * y + 0.4 * z}}};\n  }();\n  const auto momentum_density = [&mesh]() {\n    return tnsr::I<DataVector, 3>{DataVector(mesh.number_of_grid_points(), 0.)};\n  }();\n  const auto energy_density = [&x, &y, &z]() {\n    return Scalar<DataVector>{{{1.8 - 0.1 * square(x) * y * square(z)}}};\n  }();\n\n  std::unordered_map<DirectionalId<3>,\n                     NewtonianEuler::Limiters::Weno<3>::PackagedData,\n                     boost::hash<DirectionalId<3>>>\n      neighbor_data{};\n  const std::array<DirectionalId<3>, 6> dir_keys = {\n      {{Direction<3>::lower_xi(), ElementId<3>(1)},\n       {Direction<3>::upper_xi(), ElementId<3>(2)},\n       {Direction<3>::lower_eta(), ElementId<3>(3)},\n       {Direction<3>::upper_eta(), ElementId<3>(4)},\n       {Direction<3>::lower_zeta(), ElementId<3>(5)},\n       {Direction<3>::upper_zeta(), ElementId<3>(6)}}};\n  const size_t num_pts = mesh.number_of_grid_points();\n  for (const auto& dir_key : dir_keys) {\n    neighbor_data[dir_key].element_size = element_size;\n    neighbor_data[dir_key].mesh = mesh;\n    neighbor_data[dir_key].volume_data.initialize(num_pts, 0.0);\n  }\n\n  using rho = NewtonianEuler::Tags::MassDensityCons;\n  get(get<rho>(neighbor_data[dir_keys[0]].volume_data)) = 0.4;\n  get(get<rho>(neighbor_data[dir_keys[1]].volume_data)) = 1.1;\n  get(get<rho>(neighbor_data[dir_keys[2]].volume_data)) = 2.1;\n  get(get<rho>(neighbor_data[dir_keys[3]].volume_data)) = 0.9;\n  get(get<rho>(neighbor_data[dir_keys[4]].volume_data)) = 0.3;\n  get(get<rho>(neighbor_data[dir_keys[5]].volume_data)) = 1.3;\n  using mean_rho = Tags::Mean<NewtonianEuler::Tags::MassDensityCons>;\n  get(get<mean_rho>(neighbor_data[dir_keys[0]].means)) = 0.4;\n  get(get<mean_rho>(neighbor_data[dir_keys[1]].means)) = 1.1;\n  get(get<mean_rho>(neighbor_data[dir_keys[2]].means)) = 2.1;\n  get(get<mean_rho>(neighbor_data[dir_keys[3]].means)) = 0.9;\n  get(get<mean_rho>(neighbor_data[dir_keys[4]].means)) = 0.3;\n  get(get<mean_rho>(neighbor_data[dir_keys[5]].means)) = 1.3;\n\n  for (const auto& dir_key : dir_keys) {\n    using rhou = NewtonianEuler::Tags::MomentumDensity<3>;\n    get<0>(get<rhou>(neighbor_data[dir_key].volume_data)) = 0.;\n    get<1>(get<rhou>(neighbor_data[dir_key].volume_data)) = 0.;\n    using mean_rhou = Tags::Mean<NewtonianEuler::Tags::MomentumDensity<3>>;\n    get<0>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    get<1>(get<mean_rhou>(neighbor_data[dir_key].means)) = 0.;\n    using eps = NewtonianEuler::Tags::EnergyDensity;\n    get(get<eps>(neighbor_data[dir_key].volume_data)) = 1.;\n    using mean_eps = Tags::Mean<NewtonianEuler::Tags::EnergyDensity>;\n    get(get<mean_eps>(neighbor_data[dir_key].means)) = 1.;\n  }\n\n  test_neuler_vs_generic_weno_work(\n      Limiters::WenoType::SimpleWeno, mass_density_cons, momentum_density,\n      energy_density, mesh, element_size, det_inv_logical_to_inertial_jacobian,\n      normals_and_magnitudes, neighbor_data);\n}\n\ntemplate <size_t VolumeDim>\nvoid test_neuler_weno_flattener() {\n  INFO(\"Testing flattener use in NewtonianEuler::Limiters::Weno limiter\");\n  CAPTURE(VolumeDim);\n\n  const auto mesh = Mesh<VolumeDim>(2, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto);\n  // We use an element with no neighbors so the limiter does nothing\n  const auto element = Element<VolumeDim>{ElementId<VolumeDim>(0), {}};\n  const auto element_size = make_array<VolumeDim>(0.6);\n  // inv_jac = logical volume / inertial volume\n  const Scalar<DataVector> det_inv_logical_to_inertial_jacobian{DataVector(\n      mesh.number_of_grid_points(), pow<VolumeDim>(2.) / pow<VolumeDim>(0.6))};\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n\n  const size_t num_points = mesh.number_of_grid_points();\n  const auto input_density = [&num_points]() {\n    // One negative value to trigger flattener\n    Scalar<DataVector> density{DataVector(num_points, 0.8)};\n    get(density)[0] = -0.2;\n    return density;\n  }();\n  const auto input_momentum = [&num_points]() {\n    return tnsr::I<DataVector, VolumeDim>{DataVector(num_points, 0.1)};\n  }();\n  const auto input_energy = [&num_points]() {\n    return Scalar<DataVector>{DataVector(num_points, 1.4)};\n  }();\n\n  // Empty map because no neighbors\n  const std::unordered_map<\n      DirectionalId<VolumeDim>,\n      typename NewtonianEuler::Limiters::Weno<VolumeDim>::PackagedData,\n      boost::hash<DirectionalId<VolumeDim>>>\n      neighbor_data{};\n\n  // First a sanity check: there should be a negative density for this test to\n  // make sense... otherwise we aren't testing anything useful\n  const bool has_negative_density = min(get(input_density)) < 0.;\n  REQUIRE(has_negative_density);\n\n  // With flattener off, check the limiter does nothing (because no neighbors).\n  //\n  // Note that the post-limiter result is unphysical: the density is still\n  // negative at one point. Because of this, we have to use the generic limiter\n  // when testing the limiter does nothing: the specialized limiter has an\n  // ASSERT that the post-limiting solution is valid which would fail here.\n  auto density = input_density;\n  auto momentum = input_momentum;\n  auto energy = input_energy;\n  const double tvb_constant = 0.;\n  const Limiters::Weno<\n      VolumeDim, tmpl::list<NewtonianEuler::Tags::MassDensityCons,\n                            NewtonianEuler::Tags::MomentumDensity<VolumeDim>,\n                            NewtonianEuler::Tags::EnergyDensity>>\n      weno_noflat(Limiters::WenoType::SimpleWeno, 0.001, tvb_constant, {});\n  const bool activated_noflat = weno_noflat(\n      make_not_null(&density), make_not_null(&momentum), make_not_null(&energy),\n      mesh, element, element_size, neighbor_data);\n  CHECK_FALSE(activated_noflat);\n\n  // Not read from, so empty\n  const DirectionMap<VolumeDim,\n                     std::optional<Variables<tmpl::list<\n                         evolution::dg::Tags::MagnitudeOfNormal,\n                         evolution::dg::Tags::NormalCovector<VolumeDim>>>>>\n      normals_and_magnitudes{};\n\n  // With flattener on, check limiter activates\n  density = input_density;\n  momentum = input_momentum;\n  energy = input_energy;\n  const auto vars_to_limit =\n      NewtonianEuler::Limiters::VariablesToLimit::Conserved;\n  const NewtonianEuler::Limiters::Weno<VolumeDim> weno(\n      Limiters::WenoType::SimpleWeno, vars_to_limit, 0.001, tvb_constant, {},\n      true);\n  const bool activated = weno(\n      make_not_null(&density), make_not_null(&momentum), make_not_null(&energy),\n      mesh, element, element_size, det_inv_logical_to_inertial_jacobian,\n      normals_and_magnitudes, equation_of_state, neighbor_data);\n  CHECK(activated);\n\n  // Finally, check the negative density is brought back to positive\n  const bool density_is_positive_everywhere = min(get(density)) > 0.;\n  CHECK(density_is_positive_everywhere);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Limiters.Weno\",\n                  \"[Limiters][Unit]\") {\n  test_neuler_weno_option_parsing();\n  test_neuler_weno_serialization();\n\n  // The package_data function for NewtonianEuler::Limiters::Weno is just a\n  // direct call to the generic Weno package_data. We do not test it.\n\n  // Compare specialized vs generic Weno limiters on simplified input data.\n  // The SimpleWeno implementation is tested in all dims, but Hweno is tested\n  // in 1D only because its TCI isn't compatible with the simplified input data.\n  test_neuler_vs_generic_weno_1d();\n  test_neuler_vs_generic_weno_2d();\n  test_neuler_vs_generic_weno_3d();\n\n  // Test the use of the post-processing flattener\n  test_neuler_weno_flattener<1>();\n  test_neuler_weno_flattener<2>();\n  test_neuler_weno_flattener<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef mass_density(mass_density_cons, momentum_density, energy_density):\n    return mass_density_cons\n\n\ndef velocity(mass_density_cons, momentum_density, energy_density):\n    return momentum_density / mass_density_cons\n\n\ndef specific_internal_energy(\n    mass_density_cons, momentum_density, energy_density\n):\n    veloc = velocity(mass_density_cons, momentum_density, energy_density)\n    return energy_density / mass_density_cons - 0.5 * np.dot(veloc, veloc)\n\n\ndef pressure_1d(mass_density_cons, momentum_density, energy_density):\n    return 1.4 * np.power(mass_density_cons, 5.0 / 3.0)\n\n\ndef pressure_2d(mass_density_cons, momentum_density, energy_density):\n    return (\n        (2.0 / 3.0)\n        * mass_density_cons\n        * specific_internal_energy(\n            mass_density_cons, momentum_density, energy_density\n        )\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Sources/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_NewtonianEulerSources\")\n\nset(LIBRARY_SOURCES\n  Test_LaneEmdenGravitationalField.cpp\n  Test_UniformAcceleration.cpp\n  Test_VortexPerturbation.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  NewtonianEulerSources\n  NewtonianEulerSolutions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Sources/LaneEmdenGravitationalField.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom PointwiseFunctions.AnalyticSolutions.NewtonianEuler.LaneEmdenStar import (\n    gravitational_field,\n    mass_density,\n)\n\n\ndef source_momentum_density(\n    mass_density, momentum_density, x, central_mass_density, polytropic_constant\n):\n    g = gravitational_field(x, central_mass_density, polytropic_constant)\n    return mass_density * g\n\n\ndef source_energy_density(\n    mass_density, momentum_density, x, central_mass_density, polytropic_constant\n):\n    g = gravitational_field(x, central_mass_density, polytropic_constant)\n    return np.dot(momentum_density, g)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Sources/Test_LaneEmdenGravitationalField.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/LaneEmdenGravitationalField.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n// Need this proxy in order for pypp to evaluate function whose arguments\n// include a variable of type `NewtonianEuler::Solutions::LaneEmdenStar`\nstruct LaneEmdenGravitationalFieldProxy\n    : NewtonianEuler::Sources::LaneEmdenGravitationalField {\n  using NewtonianEuler::Sources::LaneEmdenGravitationalField::\n      LaneEmdenGravitationalField;\n  LaneEmdenGravitationalFieldProxy(const double central_mass_density,\n                                   const double polytropic_index)\n      : LaneEmdenGravitationalField(central_mass_density, polytropic_index) {}\n\n  void apply(\n      const gsl::not_null<tnsr::I<DataVector, 3>*> source_momentum_density,\n      const gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, 3>& momentum_density,\n      const tnsr::I<DataVector, 3>& coords) const {\n    get(*source_energy_density) = 0.0;\n    for (size_t i = 0; i < 3; ++i) {\n      source_momentum_density->get(i) = 0.0;\n    }\n    // EOS is unused\n    const EquationsOfState::IdealFluid<false> eos{5.0 / 3.0};\n    Scalar<DataVector> source_mass_density_cons{};\n    static_cast<const NewtonianEuler::Sources::LaneEmdenGravitationalField&>(\n        *this)(make_not_null(&source_mass_density_cons),\n               source_momentum_density, source_energy_density,\n               mass_density_cons, momentum_density, {}, {}, {}, {}, eos, coords,\n               0.0);\n  }\n};\n\nvoid test_sources() {\n  // Nothing special about these values, we just want them to be non-unity and\n  // to be different from each other:\n  const double central_mass_density = 0.7;\n  const double polytropic_constant = 2.0;\n  LaneEmdenGravitationalFieldProxy source_proxy(central_mass_density,\n                                                polytropic_constant);\n  pypp::check_with_random_values<3>(\n      &LaneEmdenGravitationalFieldProxy::apply, source_proxy,\n      \"Evolution.Systems.NewtonianEuler.Sources.LaneEmdenGravitationalField\",\n      {\"source_momentum_density\", \"source_energy_density\"},\n      {{{0.0, 3.0},\n        {-1.0, 1.0},\n        // with polytropic_constant == 2, star has outer radius ~ 1.77\n        {-2.0, 2.0}}},\n      std::make_tuple(central_mass_density, polytropic_constant),\n      DataVector(5));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Sources.LaneEmdenGravitationalField\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  test_sources();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Sources/Test_UniformAcceleration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/UniformAcceleration.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstruct UniformAccelerationProxy\n    : public NewtonianEuler::Sources::UniformAcceleration<Dim> {\n  void apply(\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> source_momentum_density,\n      const gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density) const {\n    get(*source_energy_density) = 0.0;\n    for (size_t i = 0; i < Dim; ++i) {\n      source_momentum_density->get(i) = 0.0;\n    }\n    const EquationsOfState::IdealFluid<false> eos{5.0 / 3.0};\n    Scalar<DataVector> source_mass_density_cons{};\n    static_cast<const NewtonianEuler::Sources::UniformAcceleration<Dim>&>(\n        *this)(make_not_null(&source_mass_density_cons),\n               source_momentum_density, source_energy_density,\n               mass_density_cons, momentum_density, {}, {}, {}, {}, eos, {},\n               0.0);\n  }\n\n  explicit UniformAccelerationProxy(\n      const std::array<double, Dim> acceleration_field)\n      : NewtonianEuler::Sources::UniformAcceleration<Dim>(acceleration_field) {}\n};\n\ntemplate <size_t Dim>\nvoid test_sources(const std::array<double, Dim>& acceleration_field,\n                  const DataVector& used_for_size) {\n  UniformAccelerationProxy<Dim> source_proxy(acceleration_field);\n  pypp::check_with_random_values<1>(\n      &UniformAccelerationProxy<Dim>::apply, source_proxy,\n      \"UniformAcceleration\",\n      {\"source_momentum_density\", \"source_energy_density\"}, {{{0.0, 3.0}}},\n      std::make_tuple(acceleration_field), used_for_size);\n\n  NewtonianEuler::Sources::UniformAcceleration<Dim> source(acceleration_field);\n  NewtonianEuler::Sources::UniformAcceleration<Dim> source_to_move(\n      acceleration_field);\n  test_move_semantics(std::move(source_to_move), source);  // NOLINT\n\n  test_serialization(source);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Sources.UniformAcceleration\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler/Sources\"};\n\n  const DataVector used_for_size(5);\n  test_sources<1>({{-2.0}}, used_for_size);\n  test_sources<2>({{-1.2, 8.7}}, used_for_size);\n  test_sources<3>({{1.8, -0.05, 5.7}}, used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Sources/Test_VortexPerturbation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/VortexPerturbation.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n\nnamespace {\n// Need this proxy in order for pypp to evaluate function whose arguments\n// include a variable of type `NewtonianEuler::Solutions::IsentropicVortex`\nstruct VortexPerturbation3dProxy\n    : NewtonianEuler::Sources::VortexPerturbation {\n  using NewtonianEuler::Sources::VortexPerturbation::VortexPerturbation;\n  explicit VortexPerturbation3dProxy(const double perturbation_amplitude)\n      : NewtonianEuler::Sources::VortexPerturbation(perturbation_amplitude){};\n\n  void apply(\n      const gsl::not_null<Scalar<DataVector>*> source_mass_density_cons,\n      const gsl::not_null<tnsr::I<DataVector, 3>*> source_momentum_density,\n      const gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, 3>& momentum_density,\n      const Scalar<DataVector>& energy_density_cons,\n      const Scalar<DataVector>& pressure,\n      const tnsr::I<DataVector, 3>& coords) const {\n    tnsr::I<DataVector, 3> velocity = momentum_density;\n    for (size_t i = 0; i < 3; ++i) {\n      velocity.get(i) = momentum_density.get(i) / get(mass_density_cons);\n    }\n\n    get(*source_mass_density_cons) = 0.0;\n    get(*source_energy_density) = 0.0;\n    for (size_t i = 0; i < 3; ++i) {\n      source_momentum_density->get(i) = 0.0;\n    }\n    // EOS is unused\n    const EquationsOfState::IdealFluid<false> eos{5.0 / 3.0};\n    static_cast<const NewtonianEuler::Sources::VortexPerturbation&>(*this)(\n        source_mass_density_cons, source_momentum_density,\n        source_energy_density, mass_density_cons, momentum_density,\n        energy_density_cons, velocity, pressure, {}, eos, coords, 0.0);\n  }\n};\n\nvoid test_sources() {\n  const double perturbation_amplitude = 0.1987;\n  VortexPerturbation3dProxy source_proxy(perturbation_amplitude);\n  pypp::check_with_random_values<1>(\n      &VortexPerturbation3dProxy::apply, source_proxy,\n      \"Evolution.Systems.NewtonianEuler.Sources.VortexPerturbation\",\n      {\"source_mass_density_cons\", \"source_momentum_density\",\n       \"source_energy_density\"},\n      {{{0.0, 1.0}}}, std::make_tuple(perturbation_amplitude), DataVector(5));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Sources.VortexPerturb\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  test_sources();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Sources/UniformAcceleration.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef source_mass_density(mass_density, momentum_density, acceleration_field):\n    return mass_density * 0.0\n\n\ndef source_momentum_density(mass_density, momentum_density, acceleration_field):\n    return mass_density * np.array(acceleration_field)\n\n\ndef source_energy_density(mass_density, momentum_density, acceleration_field):\n    return np.dot(momentum_density, np.array(acceleration_field))\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Sources/VortexPerturbation.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef source_mass_density_cons(\n    mass_density,\n    momentum_density,\n    energy_density,\n    pressure,\n    x,\n    perturbation_amplitude,\n):\n    return perturbation_amplitude * mass_density * np.cos(x[2])\n\n\ndef source_momentum_density(\n    mass_density,\n    momentum_density,\n    energy_density,\n    pressure,\n    x,\n    perturbation_amplitude,\n):\n    return momentum_density * perturbation_amplitude * np.cos(x[2])\n\n\ndef source_energy_density(\n    mass_density,\n    momentum_density,\n    energy_density,\n    pressure,\n    x,\n    perturbation_amplitude,\n):\n    return (\n        (energy_density + pressure + momentum_density[2] ** 2 / mass_density)\n        * perturbation_amplitude\n        * np.cos(x[2])\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Sources/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_ComputeFluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Fluxes.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  const size_t num_pts = 5;\n\n  auto vars = make_with_random_values<Variables<tmpl::append<\n      typename NewtonianEuler::ComputeFluxes<Dim>::return_tags,\n      typename NewtonianEuler::ComputeFluxes<Dim>::argument_tags>>>(gen, dist,\n                                                                    num_pts);\n\n  Variables<typename NewtonianEuler::ComputeFluxes<Dim>::return_tags>\n      expected_fluxes{num_pts};\n  NewtonianEuler::ComputeFluxes<Dim>::apply(\n      make_not_null(&get<::Tags::Flux<NewtonianEuler::Tags::MassDensityCons,\n                                      tmpl::size_t<Dim>, Frame::Inertial>>(\n          expected_fluxes)),\n      make_not_null(\n          &get<::Tags::Flux<NewtonianEuler::Tags::MomentumDensity<Dim>,\n                            tmpl::size_t<Dim>, Frame::Inertial>>(\n              expected_fluxes)),\n      make_not_null(&get<::Tags::Flux<NewtonianEuler::Tags::EnergyDensity,\n                                      tmpl::size_t<Dim>, Frame::Inertial>>(\n          expected_fluxes)),\n      get<NewtonianEuler::Tags::MomentumDensity<Dim>>(vars),\n      get<NewtonianEuler::Tags::EnergyDensity>(vars),\n      get<hydro::Tags::SpatialVelocity<DataVector, Dim>>(vars),\n      get<hydro::Tags::Pressure<DataVector>>(vars));\n\n  NewtonianEuler::subcell::compute_fluxes<Dim>(make_not_null(&vars));\n\n  tmpl::for_each<typename NewtonianEuler::ComputeFluxes<Dim>::return_tags>(\n      [&expected_fluxes, &vars](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        const auto& computed_flux = get<tag>(vars);\n        const auto& expected_flux = get<tag>(expected_fluxes);\n        CHECK_ITERABLE_APPROX(computed_flux, expected_flux);\n      });\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Subcell.ComputeFluxes\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist(0.0, 1.0);\n  test<1>(make_not_null(&gen), make_not_null(&dist));\n  test<2>(make_not_null(&gen), make_not_null(&dist));\n  test<3>(make_not_null(&gen), make_not_null(&dist));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Hll.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/AoWeno.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t Dim>\nauto make_coord_map();\n\ntemplate <>\nauto make_coord_map<1>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      affine_map);\n}\n\ntemplate <>\nauto make_coord_map<2>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  Affine2D product_map{affine_map, affine_map};\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      product_map);\n}\n\ntemplate <>\nauto make_coord_map<3>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  Affine3D product_map{affine_map, affine_map, affine_map};\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      product_map);\n}\n\ntemplate <size_t Dim>\nauto make_element();\n\ntemplate <>\nauto make_element<1>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  std::vector<Block<1>> blocks;\n  blocks.emplace_back(Block<1>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          affine_map),\n      0, {}));\n  return domain::create_initial_element(\n      ElementId<1>{0, {SegmentId{3, 4}}}, blocks,\n      std::vector<std::array<size_t, 1>>{std::array<size_t, 1>{{3}}});\n}\n\ntemplate <>\nauto make_element<2>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  std::vector<Block<2>> blocks;\n  blocks.emplace_back(Block<2>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine2D{affine_map, affine_map}),\n      0, {}));\n  return domain::create_initial_element(\n      ElementId<2>{0, {SegmentId{3, 4}, SegmentId{3, 4}}}, blocks,\n      std::vector<std::array<size_t, 2>>{std::array<size_t, 2>{{3, 3}}});\n}\n\ntemplate <>\nauto make_element<3>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  std::vector<Block<3>> blocks;\n  blocks.emplace_back(Block<3>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D{affine_map, affine_map, affine_map}),\n      0, {}));\n  return domain::create_initial_element(\n      ElementId<3>{0, {SegmentId{3, 4}, SegmentId{3, 4}, SegmentId{3, 4}}},\n      blocks,\n      std::vector<std::array<size_t, 3>>{std::array<size_t, 3>{{3, 3, 3}}});\n}\n\ntemplate <size_t Dim>\nstruct MetaVars {\n  using solution = NewtonianEuler::Solutions::SmoothFlow<Dim>;\n  using system = NewtonianEuler::System<Dim>;\n};\n\ntemplate <size_t Dim>\ndouble test(const size_t num_dg_pts) {\n  using solution = NewtonianEuler::Solutions::SmoothFlow<Dim>;\n  using eos = typename solution::equation_of_state_type;\n  using system = NewtonianEuler::System<Dim>;\n  using variables_tag = typename system::variables_tag;\n  const auto coordinate_map = make_coord_map<Dim>();\n  const auto moving_mesh_map =\n      domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{});\n  const auto element = make_element<Dim>();\n\n  const solution soln{make_array<Dim>(0.3), make_array<Dim>(-0.2), 0.5, 1.5,\n                      0.01};\n\n  const double time = 0.0;\n  const Mesh<Dim> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const auto dg_coords = coordinate_map(logical_coordinates(dg_mesh));\n\n  // Neighbor data for reconstruction.\n  //\n  // 0. neighbors coords (our logical coords +2)\n  // 1. compute prims from solution\n  // 2. compute prims needed for reconstruction\n  // 3. set neighbor data\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>::type\n      neighbor_data{};\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, Dim>,\n                 hydro::Tags::Pressure<DataVector>>;\n  using prim_tags = typename system::primitive_variables_tag::tags_list;\n  for (const Direction<Dim>& direction : Direction<Dim>::all_directions()) {\n    auto neighbor_logical_coords = logical_coordinates(subcell_mesh);\n    neighbor_logical_coords.get(direction.dimension()) +=\n        2.0 * direction.sign();\n    auto neighbor_coords = coordinate_map(neighbor_logical_coords);\n    const auto neighbor_prims =\n        soln.variables(neighbor_coords, time, prim_tags{});\n    Variables<prims_to_reconstruct_tags> prims_to_reconstruct{\n        subcell_mesh.number_of_grid_points()};\n    tmpl::for_each<prims_to_reconstruct_tags>(\n        [&prims_to_reconstruct, &neighbor_prims](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          get<tag>(prims_to_reconstruct) = get<tag>(neighbor_prims);\n        });\n    // Slice data so we can add it to the element's neighbor data\n    DataVector neighbor_data_in_direction =\n        evolution::dg::subcell::slice_data(\n            prims_to_reconstruct, subcell_mesh.extents(),\n            NewtonianEuler::fd::MonotonisedCentralPrim<Dim>{}.ghost_zone_size(),\n            std::unordered_set{direction.opposite()}, 0, {})\n            .at(direction.opposite());\n    const auto key = DirectionalId<Dim>{\n        direction, *element.neighbors().at(direction).begin()};\n    neighbor_data[key] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[key].neighbor_ghost_data_for_reconstruction() =\n        neighbor_data_in_direction;\n  }\n  Variables<prim_tags> dg_prim_vars{dg_mesh.number_of_grid_points()};\n  dg_prim_vars.assign_subset(soln.variables(dg_coords, time, prim_tags{}));\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      normal_vectors{};\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    const Mesh<Dim - 1> face_mesh = dg_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<Dim>,\n                       tnsr::i<DataVector, Dim, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, Dim, Frame::Inertial> unnormalized_covector{};\n    for (size_t i = 0; i < Dim; ++i) {\n      unnormalized_covector.get(i) =\n          coordinate_map.inv_jacobian(face_logical_coords)\n              .get(direction.dimension(), i);\n    }\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        evolution::dg::Actions::detail::NormalVector<Dim>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<system>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, moving_mesh_map);\n  }\n  auto box = db::create<\n      db::AddSimpleTags<\n          Parallel::Tags::MetavariablesImpl<MetaVars<Dim>>,\n          domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n          evolution::dg::subcell::Tags::Mesh<Dim>,\n          NewtonianEuler::fd::Tags::Reconstructor<Dim>,\n          evolution::Tags::BoundaryCorrection,\n          hydro::Tags::EquationOfState<false, eos::thermodynamic_dim>,\n          typename system::primitive_variables_tag, variables_tag,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n          evolution::dg::Tags::MortarData<Dim>, domain::Tags::MeshVelocity<Dim>,\n          evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>,\n          evolution::dg::subcell::Tags::SubcellOptions<Dim>>,\n      db::AddComputeTags<\n          evolution::dg::subcell::Tags::LogicalCoordinatesCompute<Dim>>>(\n      MetaVars<Dim>{}, element, dg_mesh, subcell_mesh,\n      std::unique_ptr<NewtonianEuler::fd::Reconstructor<Dim>>{\n          std::make_unique<NewtonianEuler::fd::MonotonisedCentralPrim<Dim>>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<NewtonianEuler::BoundaryCorrections::Hll<Dim>>()},\n      soln.equation_of_state().get_clone(), dg_prim_vars,\n      typename variables_tag::type{dg_mesh.number_of_grid_points()},\n      neighbor_data, typename evolution::dg::Tags::MortarData<Dim>::type{},\n      std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>{},\n      normal_vectors,\n      evolution::dg::subcell::SubcellOptions{\n          4.0, 1_st, 1.0e-3, 1.0e-4, false, false,\n          evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n          std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1});\n\n  db::mutate_apply<NewtonianEuler::ConservativeFromPrimitive<Dim>>(\n      make_not_null(&box));\n\n  std::vector<DirectionalId<Dim>> mortars_to_reconstruct_to{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    mortars_to_reconstruct_to.emplace_back(\n        DirectionalId<Dim>{direction, *neighbors.begin()});\n  }\n\n  const auto all_packaged_data =\n      NewtonianEuler::subcell::NeighborPackagedData::apply(\n          box, mortars_to_reconstruct_to);\n\n  // Parse out evolved vars, since those are easiest to check for correctness,\n  // then return absolute difference between analytic and reconstructed values.\n  DirectionalIdMap<Dim, typename variables_tag::type> evolved_vars_errors{};\n  double max_abs_error = 0.0;\n  for (const auto& [direction_and_id, data] : all_packaged_data) {\n    const auto& direction = direction_and_id.direction();\n    using dg_package_field_tags =\n        typename NewtonianEuler::BoundaryCorrections::Hll<\n            Dim>::dg_package_field_tags;\n    const Mesh<Dim - 1> face_mesh = dg_mesh.slice_away(direction.dimension());\n    Variables<dg_package_field_tags> packaged_data{\n        face_mesh.number_of_grid_points()};\n    std::copy(data.begin(), data.end(), packaged_data.data());\n    auto sliced_vars = data_on_slice(\n        db::get<variables_tag>(box), dg_mesh.extents(), direction.dimension(),\n        direction.side() == Side::Upper\n            ? dg_mesh.extents(direction.dimension()) - 1\n            : 0);\n\n    tmpl::for_each<typename variables_tag::type::tags_list>(\n        [&sliced_vars, &max_abs_error, &packaged_data](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          auto& sliced_tensor = get<tag>(sliced_vars);\n          const auto& packaged_data_tensor = get<tag>(packaged_data);\n          for (size_t tensor_index = 0; tensor_index < sliced_tensor.size();\n               ++tensor_index) {\n            max_abs_error = std::max(\n                max_abs_error, max(abs(sliced_tensor[tensor_index] -\n                                       packaged_data_tensor[tensor_index])));\n          }\n        });\n  }\n  return max_abs_error;\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtoniainEuler.Subcell.NeighborPackagedData\",\n    \"[Unit][Evolution]\") {\n  // This tests sets up a cube [2,3]^3 for a SmoothFlow problem and verifies\n  // that the difference between the reconstructed evolved variables and the\n  // sliced (exact on LGL grid) evolved variables on the interfaces decreases.\n  CHECK(test<1>(3) > test<1>(6));\n  CHECK(test<2>(3) > test<2>(6));\n  CHECK(test<3>(3) > test<3>(6));\n  // Check that the error is \"reasonably small\"\n  CHECK(test<1>(4) < 1.0e-4);\n  CHECK(test<2>(4) < 1.0e-4);\n  CHECK(test<3>(4) < 1.0e-4);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_PrimitiveGhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/PrimitiveGhostData.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_subcells(\n    const gsl::not_null<std::mt19937*> gen,\n    const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n  using prim_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using PrimVars = Variables<prim_tags>;\n  using prims_to_reconstruct_tags = tmpl::list<MassDensity, Velocity, Pressure>;\n  using ReconsPrimVars = Variables<prims_to_reconstruct_tags>;\n\n  const Mesh<Dim> subcell_mesh{9, Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::CellCentered};\n  // NOLINTNEXTLINE(modernize-use-auto)\n  const PrimVars prims = make_with_random_values<PrimVars>(\n      gen, dist, subcell_mesh.number_of_grid_points());\n\n  auto box = db::create<db::AddSimpleTags<::Tags::Variables<prim_tags>>>(prims);\n  DataVector prims_to_reconstruct_rdmp =\n      db::mutate_apply<NewtonianEuler::subcell::PrimitiveGhostVariables<Dim>>(\n          make_not_null(&box), 2_st);\n  const ReconsPrimVars prims_to_reconstruct{\n      prims_to_reconstruct_rdmp.data(), prims_to_reconstruct_rdmp.size() - 2};\n\n  tmpl::for_each<prims_to_reconstruct_tags>(\n      [&expected_prims = prims, &prims_to_reconstruct](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<tag>(prims_to_reconstruct),\n                              get<tag>(expected_prims));\n      });\n}\n\ntemplate <size_t Dim>\nvoid test_dg(const gsl::not_null<std::mt19937*> gen,\n             const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n  using prim_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using PrimVars = Variables<prim_tags>;\n  using prims_to_reconstruct_tags = tmpl::list<MassDensity, Velocity, Pressure>;\n  using ReconsPrimVars = Variables<prims_to_reconstruct_tags>;\n\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  // NOLINTNEXTLINE(modernize-use-auto)\n  const PrimVars prims = make_with_random_values<PrimVars>(\n      gen, dist, dg_mesh.number_of_grid_points());\n\n  auto box = db::create<\n      db::AddSimpleTags<::Tags::Variables<prim_tags>, domain::Tags::Mesh<Dim>,\n                        evolution::dg::subcell::Tags::Mesh<Dim>>>(\n      prims, dg_mesh, subcell_mesh);\n  DataVector prims_to_reconstruct_rdmp =\n      db::mutate_apply<NewtonianEuler::subcell::PrimitiveGhostVariables<Dim>>(\n          make_not_null(&box), 2_st);\n  const ReconsPrimVars prims_to_reconstruct{\n      prims_to_reconstruct_rdmp.data(), prims_to_reconstruct_rdmp.size() - 2};\n\n  const PrimVars expected_prims = prims;\n  tmpl::for_each<prims_to_reconstruct_tags>(\n      [&prims_to_reconstruct, &expected_prims](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<tag>(prims_to_reconstruct),\n                              get<tag>(expected_prims));\n      });\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Subcell.PrimitiveGhostData\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.0, 1.0);\n  test_subcells<1>(make_not_null(&gen), make_not_null(&dist));\n  test_subcells<2>(make_not_null(&gen), make_not_null(&dist));\n  test_subcells<3>(make_not_null(&gen), make_not_null(&dist));\n\n  test_dg<1>(make_not_null(&gen), make_not_null(&dist));\n  test_dg<2>(make_not_null(&gen), make_not_null(&dist));\n  test_dg<3>(make_not_null(&gen), make_not_null(&dist));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_PrimsAfterRollback.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Tags/DidRollback.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/PrimsAfterRollback.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist,\n          const bool did_rollback) {\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  using cons_tags = tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>;\n  using ConsVars = Variables<cons_tags>;\n  using prim_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using PrimVars = Variables<prim_tags>;\n\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  auto cons_vars = make_with_random_values<ConsVars>(\n      gen, dist, subcell_mesh.number_of_grid_points());\n  PrimVars expected_prim_vars{};\n\n  std::unique_ptr<EquationsOfState::EquationOfState<false, 2>> eos =\n      EquationsOfState::PolytropicFluid<false>{1.4, 5.0 / 3.0}\n          .promote_to_2d_eos();\n\n  auto box = db::create<db::AddSimpleTags<\n      evolution::dg::subcell::Tags::DidRollback, ::Tags::Variables<cons_tags>,\n      ::Tags::Variables<prim_tags>, ::domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      hydro::Tags::EquationOfState<false, 2>>>(did_rollback, cons_vars,\n                                               expected_prim_vars, dg_mesh,\n                                               subcell_mesh, std::move(eos));\n\n  db::mutate_apply<NewtonianEuler::subcell::PrimsAfterRollback<Dim>>(\n      make_not_null(&box));\n\n  if (did_rollback) {\n    REQUIRE(\n        db::get<::Tags::Variables<prim_tags>>(box).number_of_grid_points() ==\n        cons_vars.number_of_grid_points());\n    expected_prim_vars.initialize(cons_vars.number_of_grid_points());\n    NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n        make_not_null(&get<MassDensity>(expected_prim_vars)),\n        make_not_null(&get<Velocity>(expected_prim_vars)),\n        make_not_null(&get<SpecificInternalEnergy>(expected_prim_vars)),\n        make_not_null(&get<Pressure>(expected_prim_vars)),\n        get<MassDensityCons>(cons_vars), get<MomentumDensity>(cons_vars),\n        get<EnergyDensity>(cons_vars),\n        db::get<hydro::Tags::EquationOfState<false, 2>>(box));\n    CHECK_VARIABLES_APPROX(db::get<::Tags::Variables<prim_tags>>(box),\n                           expected_prim_vars);\n  } else {\n    CHECK(db::get<::Tags::Variables<prim_tags>>(box).number_of_grid_points() ==\n          0);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Subcell.PrimsAfterRollback\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n  for (const bool did_rollback : {true, false}) {\n    test<1>(make_not_null(&gen), make_not_null(&dist), did_rollback);\n    test<2>(make_not_null(&gen), make_not_null(&dist), did_rollback);\n    test<3>(make_not_null(&gen), make_not_null(&dist), did_rollback);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_ResizeAndComputePrimitives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/ResizeAndComputePrimitives.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist,\n          const evolution::dg::subcell::ActiveGrid active_grid) {\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  using cons_tags = tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>;\n  using ConsVars = Variables<cons_tags>;\n  using prim_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using PrimVars = Variables<prim_tags>;\n\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  auto cons_vars = make_with_random_values<ConsVars>(\n      gen, dist,\n      active_grid == evolution::dg::subcell::ActiveGrid::Dg\n          ? dg_mesh.number_of_grid_points()\n          : subcell_mesh.number_of_grid_points());\n  PrimVars prim_vars{};\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Subcell) {\n    prim_vars.initialize(subcell_mesh.number_of_grid_points(), 1.0);\n  }\n\n  std::unique_ptr<EquationsOfState::EquationOfState<false, 2>> eos =\n      EquationsOfState::PolytropicFluid<false>{1.4, 5.0 / 3.0}\n          .promote_to_2d_eos();\n\n  auto box = db::create<db::AddSimpleTags<\n      evolution::dg::subcell::Tags::ActiveGrid, ::Tags::Variables<cons_tags>,\n      ::Tags::Variables<prim_tags>, ::domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      hydro::Tags::EquationOfState<false, 2>>>(\n      active_grid, cons_vars, prim_vars, dg_mesh, subcell_mesh, std::move(eos));\n\n  db::mutate_apply<NewtonianEuler::subcell::ResizeAndComputePrims<Dim>>(\n      make_not_null(&box));\n\n  REQUIRE(db::get<::Tags::Variables<prim_tags>>(box).number_of_grid_points() ==\n          cons_vars.number_of_grid_points());\n  if (active_grid == evolution::dg::subcell::ActiveGrid::Dg) {\n    prim_vars.initialize(cons_vars.number_of_grid_points());\n    NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n        make_not_null(&get<MassDensity>(prim_vars)),\n        make_not_null(&get<Velocity>(prim_vars)),\n        make_not_null(&get<SpecificInternalEnergy>(prim_vars)),\n        make_not_null(&get<Pressure>(prim_vars)),\n        get<MassDensityCons>(cons_vars), get<MomentumDensity>(cons_vars),\n        get<EnergyDensity>(cons_vars),\n        db::get<hydro::Tags::EquationOfState<false, 2>>(box));\n  }\n  CHECK_VARIABLES_APPROX(db::get<::Tags::Variables<prim_tags>>(box), prim_vars);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Subcell.ResizeAndComputePrims\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n  for (const auto active_grid : {evolution::dg::subcell::ActiveGrid::Dg,\n                                 evolution::dg::subcell::ActiveGrid::Subcell}) {\n    test<1>(make_not_null(&gen), make_not_null(&dist), active_grid);\n    test<2>(make_not_null(&gen), make_not_null(&dist), active_grid);\n    test<3>(make_not_null(&gen), make_not_null(&dist), active_grid);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/SetInitialRdmpData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n  using ConsVars =\n      Variables<tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>>;\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  ConsVars dg_vars{dg_mesh.number_of_grid_points(), 1.0};\n\n  // While the code is supposed to be used on the subcells, that doesn't\n  // actually matter.\n  using std::max;\n  using std::min;\n  const auto& dg_mass_density =\n      get<NewtonianEuler::Tags::MassDensityCons>(dg_vars);\n  const auto& dg_energy_density =\n      get<NewtonianEuler::Tags::EnergyDensity>(dg_vars);\n  const auto subcell_mass_density = evolution::dg::subcell::fd::project(\n      get(dg_mass_density), dg_mesh, subcell_mesh.extents());\n  const auto subcell_energy_density = evolution::dg::subcell::fd::project(\n      get(dg_energy_density), dg_mesh, subcell_mesh.extents());\n  evolution::dg::subcell::RdmpTciData rdmp_data{};\n  NewtonianEuler::subcell::SetInitialRdmpData<Dim>::apply(\n      make_not_null(&rdmp_data), dg_vars,\n      evolution::dg::subcell::ActiveGrid::Dg, dg_mesh, subcell_mesh);\n  const evolution::dg::subcell::RdmpTciData expected_dg_rdmp_data{\n      {max(max(get(dg_mass_density)), max(subcell_mass_density)),\n       max(max(get(dg_energy_density)), max(subcell_energy_density))},\n      {min(min(get(dg_mass_density)), min(subcell_mass_density)),\n       min(min(get(dg_energy_density)), min(subcell_energy_density))}};\n  CHECK(rdmp_data == expected_dg_rdmp_data);\n\n  NewtonianEuler::subcell::SetInitialRdmpData<Dim>::apply(\n      make_not_null(&rdmp_data), dg_vars,\n      evolution::dg::subcell::ActiveGrid::Subcell, dg_mesh, subcell_mesh);\n  const evolution::dg::subcell::RdmpTciData expected_subcell_rdmp_data{\n      {max(get(dg_mass_density)), max(get(dg_energy_density))},\n      {min(get(dg_mass_density)), min(get(dg_energy_density))}};\n  CHECK(rdmp_data == expected_subcell_rdmp_data);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Subcell.SetInitialRdmpData\",\n    \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/TciOnDgGrid.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace {\nenum class TestThis {\n  AllGood,\n  SmallDensity,\n  SmallPressure,\n  PerssonDensity,\n  PerssonEnergyDensity,\n  RdmpMassDensity,\n  RdmpEnergyDensity\n};\n\ntemplate <size_t Dim>\nvoid test(const TestThis test_this) {\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using cons_tags = tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>;\n  using ConsVars = Variables<cons_tags>;\n  using prim_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using PrimVars = Variables<prim_tags>;\n\n  const double persson_exponent = 4.0;\n  PrimVars dg_prims{dg_mesh.number_of_grid_points(), 1.0e-7};\n  std::unique_ptr<EquationsOfState::EquationOfState<false, 2>> eos =\n      std::make_unique<EquationsOfState::IdealFluid<false>>(5.0 / 3.0);\n  if (test_this == TestThis::SmallDensity) {\n    get(get<MassDensity>(dg_prims))[dg_mesh.number_of_grid_points() / 2] =\n        0.1 * 1.0e-18;\n  } else if (test_this == TestThis::SmallPressure) {\n    get(get<Pressure>(dg_prims))[dg_mesh.number_of_grid_points() / 2] =\n        0.1 * 1.0e-18;\n  } else if (test_this == TestThis::PerssonDensity) {\n    get(get<MassDensity>(dg_prims))[dg_mesh.number_of_grid_points() / 2] =\n        1.0e18;\n  } else if (test_this == TestThis::PerssonEnergyDensity) {\n    get(get<Pressure>(dg_prims))[dg_mesh.number_of_grid_points() / 2] = 1.0e18;\n  }\n\n  get<SpecificInternalEnergy>(dg_prims) =\n      eos->specific_internal_energy_from_density_and_pressure(\n          get<MassDensity>(dg_prims), get<Pressure>(dg_prims));\n\n  const evolution::dg::subcell::SubcellOptions subcell_options{\n      persson_exponent,\n      1_st,\n      1.0e-18,\n      1.0e-4,\n      false,\n      false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n      false,\n      std::nullopt,\n      fd::DerivativeOrder::Two,\n      1,\n      1,\n      1};\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Tags::Variables<cons_tags>, ::Tags::Variables<prim_tags>,\n      ::domain::Tags::Mesh<Dim>, ::evolution::dg::subcell::Tags::Mesh<Dim>,\n      hydro::Tags::EquationOfState<false, 2>,\n      evolution::dg::subcell::Tags::SubcellOptions<Dim>,\n      evolution::dg::subcell::Tags::DataForRdmpTci>>(\n      ConsVars{dg_mesh.number_of_grid_points()}, dg_prims, dg_mesh,\n      subcell_mesh, std::move(eos), subcell_options,\n      evolution::dg::subcell::RdmpTciData{});\n  db::mutate_apply<NewtonianEuler::ConservativeFromPrimitive<Dim>>(\n      make_not_null(&box));\n\n  // Set the RDMP TCI past data.\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{\n      {max(max(get(get<MassDensityCons>(box))),\n           max(evolution::dg::subcell::fd::project(\n               get(get<MassDensityCons>(box)), dg_mesh,\n               subcell_mesh.extents()))),\n       max(max(get(get<EnergyDensity>(box))),\n           max(evolution::dg::subcell::fd::project(get(get<EnergyDensity>(box)),\n                                                   dg_mesh,\n                                                   subcell_mesh.extents())))},\n      {min(min(get(get<MassDensityCons>(box))),\n           min(evolution::dg::subcell::fd::project(\n               get(get<MassDensityCons>(box)), dg_mesh,\n               subcell_mesh.extents()))),\n       min(min(get(get<EnergyDensity>(box))),\n           min(evolution::dg::subcell::fd::project(get(get<EnergyDensity>(box)),\n                                                   dg_mesh,\n                                                   subcell_mesh.extents())))}};\n\n  const evolution::dg::subcell::RdmpTciData expected_rdmp_tci_data =\n      past_rdmp_tci_data;\n\n  // Modify past data if we are expected an RDMP TCI failure.\n  db::mutate<evolution::dg::subcell::Tags::DataForRdmpTci>(\n      [&past_rdmp_tci_data, &test_this](const auto rdmp_tci_data_ptr) {\n        *rdmp_tci_data_ptr = past_rdmp_tci_data;\n        if (test_this == TestThis::RdmpMassDensity) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[0] *= 1.01;\n        } else if (test_this == TestThis::RdmpEnergyDensity) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[1] *= 1.01;\n        }\n      },\n      make_not_null(&box));\n\n  const bool element_stays_on_dg = false;\n  const std::tuple<bool, evolution::dg::subcell::RdmpTciData> result =\n      db::mutate_apply<NewtonianEuler::subcell::TciOnDgGrid<Dim>>(\n          make_not_null(&box), persson_exponent, element_stays_on_dg);\n\n  CHECK_ITERABLE_APPROX(get<1>(result).max_variables_values,\n                        expected_rdmp_tci_data.max_variables_values);\n  CHECK_ITERABLE_APPROX(get<1>(result).min_variables_values,\n                        expected_rdmp_tci_data.min_variables_values);\n  if (test_this == TestThis::AllGood) {\n    CHECK_FALSE(std::get<0>(result));\n  } else {\n    CHECK(std::get<0>(result));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Subcell.TciOnDgGrid\",\n                  \"[Unit][Evolution]\") {\n  for (const auto test_this :\n       {TestThis::AllGood, TestThis::SmallDensity, TestThis::SmallPressure,\n        TestThis::PerssonDensity, TestThis::PerssonEnergyDensity,\n        TestThis::RdmpMassDensity, TestThis::RdmpEnergyDensity}) {\n    test<1>(test_this);\n    test<2>(test_this);\n    test<3>(test_this);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/TciOnFdGrid.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nnamespace {\nenum class TestThis {\n  AllGood,\n  SmallDensitySubcell,\n  SmallPressureSubcell,\n  PerssonDensity,\n  PerssonEnergyDensity,\n  RdmpMassDensity,\n  RdmpEnergyDensity\n};\n\ntemplate <size_t Dim>\nvoid test(const TestThis test_this) {\n  CAPTURE(Dim);\n  CAPTURE(test_this);\n  using MassDensityCons = NewtonianEuler::Tags::MassDensityCons;\n  using EnergyDensity = NewtonianEuler::Tags::EnergyDensity;\n  using MomentumDensity = NewtonianEuler::Tags::MomentumDensity<Dim>;\n\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  using cons_tags = tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>;\n  using ConsVars = Variables<cons_tags>;\n  using prim_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using PrimVars = Variables<prim_tags>;\n\n  const double persson_exponent = 4.0;\n  std::unique_ptr<EquationsOfState::EquationOfState<false, 2>> eos =\n      std::make_unique<EquationsOfState::IdealFluid<false>>(5.0 / 3.0);\n  ConsVars subcell_cons{subcell_mesh.number_of_grid_points()};\n  PrimVars subcell_prim{subcell_mesh.number_of_grid_points(), 1.0e-7};\n\n  const evolution::dg::subcell::SubcellOptions subcell_options{\n      persson_exponent,\n      1_st,\n      1.0e-18,\n      1.0e-4,\n      false,\n      false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n      false,\n      std::nullopt,\n      fd::DerivativeOrder::Two,\n      1,\n      1,\n      1};\n\n  if (test_this == TestThis::PerssonEnergyDensity) {\n    get(get<Pressure>(subcell_prim))[subcell_mesh.number_of_grid_points() / 2] =\n        1.0;\n  } else if (test_this == TestThis::SmallDensitySubcell) {\n    get(get<MassDensity>(\n        subcell_prim))[subcell_mesh.number_of_grid_points() / 2] =\n        0.1 * 1.0e-18;\n  } else if (test_this == TestThis::SmallPressureSubcell) {\n    get(get<Pressure>(subcell_prim))[subcell_mesh.number_of_grid_points() / 2] =\n        0.1 * 1.0e-18;\n  } else if (test_this == TestThis::PerssonDensity) {\n    get(get<MassDensity>(subcell_prim))[dg_mesh.number_of_grid_points() / 2] =\n        1.0e-6;\n  }\n\n  get<SpecificInternalEnergy>(subcell_prim) =\n      eos->specific_internal_energy_from_density_and_pressure(\n          get<MassDensity>(subcell_prim), get<Pressure>(subcell_prim));\n\n  auto box = db::create<db::AddSimpleTags<\n      ::Tags::Variables<cons_tags>, ::Tags::Variables<prim_tags>,\n      ::domain::Tags::Mesh<Dim>, ::evolution::dg::subcell::Tags::Mesh<Dim>,\n      hydro::Tags::EquationOfState<false, 2>,\n      evolution::dg::subcell::Tags::SubcellOptions<Dim>,\n      evolution::dg::subcell::Tags::DataForRdmpTci>>(\n      subcell_cons, subcell_prim, dg_mesh, subcell_mesh, std::move(eos),\n      subcell_options, evolution::dg::subcell::RdmpTciData{});\n  db::mutate_apply<NewtonianEuler::ConservativeFromPrimitive<Dim>>(\n      make_not_null(&box));\n\n  // Set the RDMP TCI past data.\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{};\n\n  past_rdmp_tci_data.max_variables_values = DataVector{\n      max(max(get(db::get<MassDensityCons>(box))),\n          max(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<MassDensityCons>(box)), dg_mesh,\n              subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim))),\n      max(max(get(db::get<EnergyDensity>(box))),\n          max(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<EnergyDensity>(box)), dg_mesh, subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)))};\n  past_rdmp_tci_data.min_variables_values = DataVector{\n      min(min(get(db::get<MassDensityCons>(box))),\n          min(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<MassDensityCons>(box)), dg_mesh,\n              subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim))),\n      min(min(get(db::get<EnergyDensity>(box))),\n          min(evolution::dg::subcell::fd::reconstruct(\n              get(db::get<EnergyDensity>(box)), dg_mesh, subcell_mesh.extents(),\n              evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)))};\n\n  evolution::dg::subcell::RdmpTciData expected_rdmp_tci_data{};\n  expected_rdmp_tci_data.max_variables_values =\n      DataVector{max(get(db::get<MassDensityCons>(box))),\n                 max(get(db::get<EnergyDensity>(box)))};\n  expected_rdmp_tci_data.min_variables_values =\n      DataVector{min(get(db::get<MassDensityCons>(box))),\n                 min(get(db::get<EnergyDensity>(box)))};\n\n  // Modify past data if we are expected an RDMP TCI failure.\n  db::mutate<evolution::dg::subcell::Tags::DataForRdmpTci>(\n      [&past_rdmp_tci_data, &test_this](const auto rdmp_tci_data_ptr) {\n        *rdmp_tci_data_ptr = past_rdmp_tci_data;\n        if (test_this == TestThis::RdmpMassDensity) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[0] *= 1.01;\n        } else if (test_this == TestThis::RdmpEnergyDensity) {\n          // Assumes min is positive, increase it so we fail the TCI\n          rdmp_tci_data_ptr->min_variables_values[1] *= 1.01;\n        }\n      },\n      make_not_null(&box));\n\n  const auto result =\n      db::mutate_apply<NewtonianEuler::subcell::TciOnFdGrid<Dim>>(\n          make_not_null(&box), persson_exponent, false);\n  CHECK(get<1>(result) == expected_rdmp_tci_data);\n\n  if (test_this == TestThis::AllGood) {\n    CHECK_FALSE(std::get<0>(result));\n  } else {\n    CHECK(std::get<0>(result));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Subcell.TciOnFdGrid\",\n                  \"[Unit][Evolution]\") {\n  for (const auto test_this :\n       {TestThis::AllGood, TestThis::SmallDensitySubcell,\n        TestThis::SmallPressureSubcell, TestThis::PerssonDensity,\n        TestThis::PerssonEnergyDensity, TestThis::RdmpMassDensity,\n        TestThis::RdmpEnergyDensity}) {\n    test<1>(test_this);\n    test<2>(test_this);\n    test<3>(test_this);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Subcell/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Sources/LaneEmdenGravitationalField.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/AoWeno.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Tag.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Factory.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/System.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace NewtonianEuler {\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t Dim>\nauto make_coord_map();\n\ntemplate <>\nauto make_coord_map<1>() {\n  Affine affine_map{-1.0, 1.0, 1.0, 3.0};\n  return domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n      affine_map);\n}\n\ntemplate <>\nauto make_coord_map<2>() {\n  Affine affine_map{-1.0, 1.0, 1.0, 3.0};\n  Affine2D product_map{affine_map, affine_map};\n  return domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n      product_map);\n}\n\ntemplate <>\nauto make_coord_map<3>() {\n  Affine affine_map{-1.0, 1.0, 1.0, 3.0};\n  Affine3D product_map{affine_map, affine_map, affine_map};\n  return domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(\n      product_map);\n}\n\ntemplate <size_t Dim>\nauto make_element();\n\ntemplate <>\nauto make_element<1>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  std::vector<Block<1>> blocks;\n  blocks.emplace_back(Block<1>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          affine_map),\n      0, {}));\n  return domain::create_initial_element(\n      ElementId<1>{0, {SegmentId{2, 2}}}, blocks,\n      std::vector<std::array<size_t, 1>>{std::array<size_t, 1>{{3}}});\n}\n\ntemplate <>\nauto make_element<2>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  std::vector<Block<2>> blocks;\n  blocks.emplace_back(Block<2>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine2D{affine_map, affine_map}),\n      0, {}));\n  return domain::create_initial_element(\n      ElementId<2>{0, {SegmentId{2, 2}, SegmentId{2, 2}}}, blocks,\n      std::vector<std::array<size_t, 2>>{std::array<size_t, 2>{{3, 3}}});\n}\n\ntemplate <>\nauto make_element<3>() {\n  Affine affine_map{-1.0, 1.0, 2.0, 3.0};\n  std::vector<Block<3>> blocks;\n  blocks.emplace_back(Block<3>(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          Affine3D{affine_map, affine_map, affine_map}),\n      0, {}));\n  return domain::create_initial_element(\n      ElementId<3>{0, {SegmentId{2, 2}, SegmentId{2, 2}, SegmentId{2, 2}}},\n      blocks,\n      std::vector<std::array<size_t, 3>>{std::array<size_t, 3>{{3, 3, 3}}});\n}\n\ntemplate <size_t Dim>\nstruct SmoothFlowMetaVars {\n  static constexpr size_t volume_dim = Dim;\n  using initial_data = NewtonianEuler::Solutions::SmoothFlow<Dim>;\n  using system = NewtonianEuler::System<Dim>;\n  using source_term_tag = NewtonianEuler::Tags::SourceTerm<Dim>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<evolution::BoundaryCorrection,\n                   BoundaryCorrections::standard_boundary_corrections<Dim>>>;\n  };\n  static auto solution() {\n    return initial_data{make_array<Dim>(0.0), make_array<Dim>(-0.2), 0.5, 1.5,\n                        0.01};\n  }\n};\n\nstruct LaneEmdenStarMetaVars {\n  static constexpr size_t volume_dim = 3;\n  using initial_data = NewtonianEuler::Solutions::LaneEmdenStar;\n  using system = NewtonianEuler::System<3>;\n  using source_term_tag = NewtonianEuler::Tags::SourceTerm<3>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<evolution::BoundaryCorrection,\n                   BoundaryCorrections::standard_boundary_corrections<3>>>;\n  };\n  static auto solution() { return initial_data{0.7, 250.0}; }\n};\n\ntemplate <typename Metavariables>\nstd::array<double, 3> test(const size_t num_dg_pts) {\n  using metavariables = Metavariables;\n  static constexpr size_t dim = metavariables::volume_dim;\n  using solution = typename metavariables::initial_data;\n  using system = typename metavariables::system;\n  const auto element = make_element<dim>();\n  const ElementMap<dim, Frame::Grid> element_map{\n      element.id(), make_coord_map<dim>().get_clone()};\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<dim>{});\n\n  const solution soln = metavariables::solution();\n\n  const double time = 0.0;\n  const Mesh<dim> dg_mesh{num_dg_pts, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const auto cell_centered_coords =\n      (*grid_to_inertial_map)(element_map(logical_coordinates(subcell_mesh)));\n\n  using prim_tags = typename system::primitive_variables_tag::tags_list;\n  Variables<prim_tags> cell_centered_prim_vars{\n      subcell_mesh.number_of_grid_points()};\n  cell_centered_prim_vars.assign_subset(\n      soln.variables(cell_centered_coords, time, prim_tags{}));\n  using variables_tag = typename system::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, variables_tag>;\n\n  // Neighbor data for reconstruction.\n  //\n  // 0. neighbors coords (our logical coords +2)\n  // 1. compute prims from solution\n  // 2. compute prims needed for reconstruction\n  // 3. set neighbor data\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>::type\n      neighbor_data{};\n  using prims_to_reconstruct_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, dim>,\n                 hydro::Tags::Pressure<DataVector>>;\n  for (const Direction<dim>& direction : Direction<dim>::all_directions()) {\n    auto neighbor_logical_coords = logical_coordinates(subcell_mesh);\n    neighbor_logical_coords.get(direction.dimension()) +=\n        2.0 * direction.sign();\n    auto neighbor_coords =\n        (*grid_to_inertial_map)(element_map(neighbor_logical_coords));\n    const auto neighbor_prims =\n        soln.variables(neighbor_coords, time, prim_tags{});\n    Variables<prims_to_reconstruct_tags> prims_to_reconstruct{\n        subcell_mesh.number_of_grid_points()};\n    tmpl::for_each<prims_to_reconstruct_tags>(\n        [&prims_to_reconstruct, &neighbor_prims](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          get<tag>(prims_to_reconstruct) = get<tag>(neighbor_prims);\n        });\n\n    // Slice data so we can add it to the element's neighbor data\n    DataVector neighbor_data_in_direction =\n        evolution::dg::subcell::slice_data(\n            prims_to_reconstruct, subcell_mesh.extents(),\n            NewtonianEuler::fd::MonotonisedCentralPrim<dim>{}.ghost_zone_size(),\n            std::unordered_set{direction.opposite()}, 0, {})\n            .at(direction.opposite());\n    const auto key = DirectionalId<dim>{\n        direction, *element.neighbors().at(direction).begin()};\n    neighbor_data[key] = evolution::dg::subcell::GhostData{1};\n    neighbor_data[key].neighbor_ghost_data_for_reconstruction() =\n        neighbor_data_in_direction;\n  }\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      dummy_functions_of_time{};\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Parallel::Tags::MetavariablesImpl<metavariables>,\n          typename metavariables::source_term_tag,\n          ::Tags::AnalyticSolution<solution>, domain::Tags::Element<dim>,\n          domain::Tags::ElementMap<dim, Frame::Grid>,\n          evolution::dg::subcell::Tags::Mesh<dim>, fd::Tags::Reconstructor<dim>,\n          evolution::Tags::BoundaryCorrection,\n          hydro::Tags::EquationOfState<false, 2>,\n          typename system::primitive_variables_tag, dt_variables_tag,\n          variables_tag,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<dim>,\n          evolution::dg::Tags::MortarData<dim>,\n          domain::CoordinateMaps::Tags::CoordinateMap<dim, Frame::Grid,\n                                                      Frame::Inertial>,\n          ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize>,\n      db::AddComputeTags<\n          evolution::dg::subcell::Tags::LogicalCoordinatesCompute<dim>,\n          ::domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<dim, Frame::Grid>,\n              evolution::dg::subcell::Tags::Coordinates<dim,\n                                                        Frame::ElementLogical>,\n              evolution::dg::subcell::Tags::Coordinates>,\n          evolution::dg::subcell::Tags::InertialCoordinatesCompute<\n              ::domain::CoordinateMaps::Tags::CoordinateMap<dim, Frame::Grid,\n                                                            Frame::Inertial>>,\n          evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGridCompute<\n              ::domain::Tags::ElementMap<dim, Frame::Grid>, dim>,\n          evolution::dg::subcell::fd::Tags::\n              DetInverseJacobianLogicalToGridCompute<dim>,\n          evolution::dg::subcell::fd::Tags::\n              InverseJacobianLogicalToInertialCompute<\n                  ::domain::CoordinateMaps::Tags::CoordinateMap<\n                      dim, Frame::Grid, Frame::Inertial>,\n                  dim>,\n          evolution::dg::subcell::fd::Tags::\n              DetInverseJacobianLogicalToInertialCompute<\n                  ::domain::CoordinateMaps::Tags::CoordinateMap<\n                      dim, Frame::Grid, Frame::Inertial>,\n                  dim>>>(\n      metavariables{},\n\n      []() -> std::unique_ptr<::NewtonianEuler::Sources::Source<dim>> {\n        if constexpr (std::is_same_v<\n                          solution,\n                          NewtonianEuler::Solutions::SmoothFlow<dim>>) {\n          return std::make_unique<::NewtonianEuler::Sources::NoSource<dim>>();\n        } else {\n          return std::make_unique<\n              ::NewtonianEuler::Sources::LaneEmdenGravitationalField>(0.7,\n                                                                      250.0);\n        }\n      }(),\n\n      soln, element,\n      ElementMap<dim, Frame::Grid>{element.id(),\n                                   element_map.block_map().get_clone()},\n      subcell_mesh,\n      std::unique_ptr<NewtonianEuler::fd::Reconstructor<dim>>{\n          std::make_unique<NewtonianEuler::fd::MonotonisedCentralPrim<dim>>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<NewtonianEuler::BoundaryCorrections::Hll<dim>>()},\n      soln.equation_of_state().promote_to_2d_eos(), cell_centered_prim_vars,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      typename variables_tag::type{}, neighbor_data,\n      typename evolution::dg::Tags::MortarData<dim>::type{},\n      grid_to_inertial_map->get_clone(), time,\n      clone_unique_ptrs(dummy_functions_of_time));\n  db::mutate_apply<ConservativeFromPrimitive<dim>>(make_not_null(&box));\n\n  NewtonianEuler::subcell::TimeDerivative<dim>::apply(make_not_null(&box));\n\n  const auto& dt_vars = db::get<dt_variables_tag>(box);\n  return {{max(abs(get(get<::Tags::dt<Tags::MassDensityCons>>(dt_vars)))),\n           max(abs(get(get<::Tags::dt<Tags::EnergyDensity>>(dt_vars)))),\n           max(get(magnitude(\n               get<::Tags::dt<Tags::MomentumDensity<dim>>>(dt_vars))))}};\n}\n\ntemplate <typename Metavariables>\nvoid test_convergence() {\n  CAPTURE(pretty_type::get_name<Metavariables>());\n  // This tests sets up a stationary SmoothFlow problem or LaneEmdenStar and\n  // verifies that the time derivative vanishes. Or, more specifically, that the\n  // time derivative decreases with increasing resolution.\n  // For the SmoothFlow problem, the time derivatives of the momentum and energy\n  // densities are roundoff.\n  const auto four_pts_data = test<Metavariables>(4);\n  const auto eight_pts_data = test<Metavariables>(8);\n\n  for (size_t i = 0; i < four_pts_data.size(); ++i) {\n    CAPTURE(i);\n    CAPTURE(gsl::at(four_pts_data, i));\n    CAPTURE(gsl::at(eight_pts_data, i));\n    const bool converging =\n        gsl::at(eight_pts_data, i) < gsl::at(four_pts_data, i);\n    const bool roundoff = gsl::at(eight_pts_data, i) < 1.e-14 and\n                          gsl::at(four_pts_data, i) < 1.e-14;\n    CHECK((converging or roundoff));\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.Subcell.TimeDerivative\",\n    \"[Unit][Evolution]\") {\n  test_convergence<SmoothFlowMetaVars<1>>();\n  test_convergence<SmoothFlowMetaVars<2>>();\n  test_convergence<SmoothFlowMetaVars<3>>();\n  test_convergence<LaneEmdenStarMetaVars>();\n}\n}  // namespace\n}  // namespace NewtonianEuler\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Characteristics.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test_compute_item_in_databox(const tnsr::I<DataVector, Dim>& velocity,\n                                  const Scalar<DataVector>& sound_speed,\n                                  const tnsr::i<DataVector, Dim>& normal) {\n  TestHelpers::db::test_compute_tag<\n      NewtonianEuler::Tags::CharacteristicSpeedsCompute<Dim>>(\n      \"CharacteristicSpeeds\");\n  const auto box = db::create<\n      db::AddSimpleTags<\n          hydro::Tags::SpatialVelocity<DataVector, Dim>,\n          NewtonianEuler::Tags::SoundSpeed<DataVector>,\n          ::Tags::Normalized<domain::Tags::UnnormalizedFaceNormal<Dim>>>,\n      db::AddComputeTags<\n          NewtonianEuler::Tags::CharacteristicSpeedsCompute<Dim>>>(\n      velocity, sound_speed, normal);\n  CHECK(NewtonianEuler::characteristic_speeds(velocity, sound_speed, normal) ==\n        db::get<NewtonianEuler::Tags::CharacteristicSpeeds<Dim>>(box));\n}\n\ntemplate <size_t Dim>\nvoid test_characteristic_speeds(const DataVector& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.0, 1.0);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  const auto velocity = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto sound_speed = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n\n  // test for normal along coordinate axes\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    const auto normal = euclidean_basis_vector(direction, used_for_size);\n\n    CHECK_ITERABLE_APPROX(\n        NewtonianEuler::characteristic_speeds(velocity, sound_speed, normal),\n        (pypp::call<std::array<DataVector, Dim + 2>>(\n            \"CharacteristicSpeeds\", \"CharacteristicSpeeds\", velocity,\n            sound_speed, normal)));\n  }\n\n  // test for normal of random orientation\n  pypp::check_with_random_values<3>(\n      static_cast<std::array<DataVector, Dim + 2> (*)(\n          const tnsr::I<DataVector, Dim>&, const Scalar<DataVector>&,\n          const tnsr::i<DataVector, Dim>&)>(\n          &NewtonianEuler::characteristic_speeds<Dim>),\n      \"CharacteristicSpeeds\", \"CharacteristicSpeeds\",\n      {{{-1.0, 1.0}, {0.0, 1.0}, {-1.0, 1.0}}}, used_for_size);\n  test_compute_item_in_databox(\n      velocity, sound_speed,\n      make_with_random_values<tnsr::i<DataVector, Dim>>(\n          nn_generator, nn_distribution, used_for_size));\n}\n\ntemplate <size_t Dim>\nvoid test_largest_characteristic_speed(const DataVector& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  const auto velocity = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto sound_speed = make_with_random_values<Scalar<DataVector>>(\n      nn_generator, nn_distribution, used_for_size);\n  double largest_characteristic_speed =\n      std::numeric_limits<double>::signaling_NaN();\n  NewtonianEuler::Tags::ComputeLargestCharacteristicSpeed<Dim>::function(\n      make_not_null(&largest_characteristic_speed), velocity, sound_speed);\n  CHECK(largest_characteristic_speed ==\n        max(get(sound_speed) + get(magnitude(velocity))));\n}\n\ntemplate <size_t Dim, size_t ThermodynamicDim>\nvoid test_left_and_right_eigenvectors_impl(\n    const Scalar<double>& density, const tnsr::I<double, Dim>& velocity,\n    const Scalar<double>& specific_internal_energy,\n    const tnsr::i<double, Dim>& unit_normal,\n    const EquationsOfState::EquationOfState<false, ThermodynamicDim>&\n        equation_of_state) {\n  Scalar<double> pressure{};\n  Scalar<double> kappa_over_density{};\n  if constexpr (ThermodynamicDim == 1) {\n    pressure = equation_of_state.pressure_from_density(density);\n    kappa_over_density = Scalar<double>{\n        {{get(equation_of_state.kappa_times_p_over_rho_squared_from_density(\n              density)) *\n          get(density) / get(pressure)}}};\n  } else if constexpr (ThermodynamicDim == 2) {\n    pressure = equation_of_state.pressure_from_density_and_energy(\n        density, specific_internal_energy);\n    kappa_over_density = Scalar<double>{\n        {{get(equation_of_state\n                  .kappa_times_p_over_rho_squared_from_density_and_energy(\n                      density, specific_internal_energy)) *\n          get(density) / get(pressure)}}};\n  }\n\n  const Scalar<double> v_squared{{{get(dot_product(velocity, velocity))}}};\n\n  const auto sound_speed_squared = NewtonianEuler::sound_speed_squared(\n      density, specific_internal_energy, equation_of_state);\n  const Scalar<double> energy_density{\n      {{(get(density) * get(specific_internal_energy)) +\n        (0.5 * get(density) * get(v_squared))}}};\n  const Scalar<double> specific_enthalpy{\n      {{(get(energy_density) + get(pressure)) / get(density)}}};\n\n  const Matrix right = NewtonianEuler::right_eigenvectors(\n      velocity, sound_speed_squared, specific_enthalpy, kappa_over_density,\n      unit_normal);\n  const Matrix left = NewtonianEuler::left_eigenvectors(\n      velocity, sound_speed_squared, specific_enthalpy, kappa_over_density,\n      unit_normal);\n\n  // Check that eigenvectors are inverses of each other\n  const auto id1 = left * right;\n  const auto id2 = right * left;\n\n  // For small values of specific_internal_energy, the relative error can\n  // increase from the default level\n  const Approx local_approx = Approx::custom().epsilon(1e-12).scale(1.0);\n  for (size_t i = 0; i < Dim + 2; ++i) {\n    for (size_t j = 0; j < Dim + 2; ++j) {\n      const double delta_ij = (i == j) ? 1. : 0.;\n      CHECK(id1(i, j) == local_approx(delta_ij));\n      CHECK(id2(i, j) == local_approx(delta_ij));\n    }\n  }\n\n  // Check that eigenvectors give correct fluxes\n  const double v_n = get(dot_product(unit_normal, velocity));\n  Matrix eigenvalues(Dim + 2, Dim + 2, 0.);\n  for (size_t i = 0; i < Dim + 2; ++i) {\n    eigenvalues(i, i) = v_n;\n  }\n  eigenvalues(0, 0) -= sqrt(get(sound_speed_squared));\n  eigenvalues(Dim + 1, Dim + 1) += sqrt(get(sound_speed_squared));\n  const auto flux_jacobian = right * eigenvalues * left;\n\n  const double b_times_theta =\n      get(kappa_over_density) * (get(v_squared) - get(specific_enthalpy)) +\n      get(sound_speed_squared);\n  const auto expected_flux_jacobian = NewtonianEuler::detail::flux_jacobian(\n      velocity, get(kappa_over_density), b_times_theta, get(specific_enthalpy),\n      unit_normal);\n\n  for (size_t i = 0; i < Dim + 2; ++i) {\n    for (size_t j = 0; j < Dim + 2; ++j) {\n      CHECK(flux_jacobian(i, j) == local_approx(expected_flux_jacobian(i, j)));\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_right_and_left_eigenvectors() {\n  // This test verifies that the eigenvectors satisfy the conditions by which\n  // they are defined:\n  // - the right and left eigenvectors are matrix inverses of each other, i.e.,\n  //   left * right == right * left == identity\n  // - the right and left eigenvectors diagonalize the flux Jacobian, i.e.,\n  //   right * eigenvalues * left == flux\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1., 1.);\n  std::uniform_real_distribution<> distribution_positive(1e-3, 1.);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n  const auto nn_distribution_positive = make_not_null(&distribution_positive);\n\n  const double used_for_size = 0.;\n  // This computes a unit normal. It is NOT uniformly distributed in angle,\n  // but for this test the angular distribution is not important.\n  const auto unit_normal = [&]() {\n    auto result = make_with_random_values<tnsr::i<double, Dim>>(\n        nn_generator, nn_distribution, used_for_size);\n    double normal_magnitude = get(magnitude(result));\n    // Though highly unlikely, the normal could have length nearly 0. If this\n    // happens, we edit the normal to make it non-zero.\n    if (normal_magnitude < 1e-3) {\n      get<0>(result) += 0.9;\n      normal_magnitude = get(magnitude(result));\n    }\n    for (auto& n_i : result) {\n      n_i /= normal_magnitude;\n    }\n    return result;\n  }();\n\n  // To check the diagonalization of the Jacobian, we need a self consistent set\n  // of primitive and derived-from-primitive variables -- so generate everything\n  // from the primitives\n  const auto density = make_with_random_values<Scalar<double>>(\n      nn_generator, nn_distribution_positive, used_for_size);\n  const auto velocity = make_with_random_values<tnsr::I<double, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto specific_internal_energy = make_with_random_values<Scalar<double>>(\n      nn_generator, nn_distribution_positive, used_for_size);\n\n  const EquationsOfState::IdealFluid<false> ideal_gas_eos{5. / 3.};\n  test_left_and_right_eigenvectors_impl(\n      density, velocity, specific_internal_energy, unit_normal, ideal_gas_eos);\n\n  const EquationsOfState::PolytropicFluid<false> polytropic_eos{100., 2.};\n  test_left_and_right_eigenvectors_impl(\n      density, velocity, specific_internal_energy, unit_normal, polytropic_eos);\n\n  // Test also in cases where unit normal is aligned to coordinate axes\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    const auto aligned = euclidean_basis_vector(direction, used_for_size);\n\n    test_left_and_right_eigenvectors_impl(\n        density, velocity, specific_internal_energy, aligned, ideal_gas_eos);\n\n    test_left_and_right_eigenvectors_impl(\n        density, velocity, specific_internal_energy, aligned, polytropic_eos);\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_numerical_eigenvectors() {\n  // This test verifies that the eigenvectors satisfy the conditions by which\n  // they are defined:\n  // - the right and left eigenvectors are matrix inverses of each other, i.e.,\n  //   left * right == right * left == identity\n  // - the right and left eigenvectors diagonalize the flux Jacobian, i.e.\n  //   right * eigenvalues * left == flux\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1., 1.);\n  std::uniform_real_distribution<> distribution_positive(1e-3, 1.);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n  const auto nn_distribution_positive = make_not_null(&distribution_positive);\n\n  const double used_for_size = 0.;\n  // This computes a unit normal. It is NOT uniformly distributed in angle,\n  // but for this test the distribution is not important.\n  const auto unit_normal = [&]() {\n    auto result = make_with_random_values<tnsr::i<double, Dim>>(\n        nn_generator, nn_distribution, used_for_size);\n    const double normal_magnitude = get(magnitude(result));\n    for (auto& n_i : result) {\n      n_i /= normal_magnitude;\n    }\n    return result;\n  }();\n\n  // To check the diagonalization of the Jacobian, we need a self consistent set\n  // of primitive and derived-from-primitive variables -- so generate everything\n  // from the primitives\n  const auto density = make_with_random_values<Scalar<double>>(\n      nn_generator, nn_distribution_positive, used_for_size);\n  const auto velocity = make_with_random_values<tnsr::I<double, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto specific_internal_energy = make_with_random_values<Scalar<double>>(\n      nn_generator, nn_distribution_positive, used_for_size);\n\n  const Scalar<double> v_squared{{{get(dot_product(velocity, velocity))}}};\n  const Scalar<double> energy_density{\n      {{(get(density) * get(specific_internal_energy)) +\n        (0.5 * get(density) * get(v_squared))}}};\n\n  const EquationsOfState::IdealFluid<false> equation_of_state{5. / 3.};\n  const auto pressure = equation_of_state.pressure_from_density_and_energy(\n      density, specific_internal_energy);\n  const Scalar<double> specific_enthalpy{\n      {{(get(energy_density) + get(pressure)) / get(density)}}};\n  const auto sound_speed_squared = NewtonianEuler::sound_speed_squared(\n      density, specific_internal_energy, equation_of_state);\n  const auto kappa_over_density = Scalar<double>{\n      {{get(equation_of_state\n                .kappa_times_p_over_rho_squared_from_density_and_energy(\n                    density, specific_internal_energy)) *\n        get(density) / get(pressure)}}};\n  const double b_times_theta =\n      (get(kappa_over_density) * (get(v_squared) - get(specific_enthalpy))) +\n      get(sound_speed_squared);\n\n  const auto expected_flux_jacobian = NewtonianEuler::detail::flux_jacobian(\n      velocity, get(kappa_over_density), b_times_theta, get(specific_enthalpy),\n      unit_normal);\n\n  const auto vals_and_vecs = NewtonianEuler::numerical_eigensystem(\n      velocity, sound_speed_squared, specific_enthalpy, kappa_over_density,\n      unit_normal);\n  const Matrix num_eigenvalues = [&vals_and_vecs]() {\n    Matrix result(Dim + 2, Dim + 2, 0.);\n    for (size_t i = 0; i < Dim + 2; ++i) {\n      result(i, i) = vals_and_vecs.first[i];\n    }\n    return result;\n  }();\n  const Matrix& num_right = vals_and_vecs.second.first;\n  const Matrix& num_left = vals_and_vecs.second.second;\n  const Matrix id1 = num_right * num_left;\n  const Matrix id2 = num_left * num_right;\n  const Matrix num_flux_jacobian = num_right * num_eigenvalues * num_left;\n\n  // Numerically-obtained eigenvectors may lead to slightly larger errors\n  const Approx local_approx = Approx::custom().epsilon(1e-11).scale(1.0);\n  for (size_t i = 0; i < Dim + 2; ++i) {\n    for (size_t j = 0; j < Dim + 2; ++j) {\n      const double delta_ij = (i == j) ? 1. : 0.;\n      CHECK(id1(i, j) == local_approx(delta_ij));\n      CHECK(id2(i, j) == local_approx(delta_ij));\n      CHECK(num_flux_jacobian(i, j) ==\n            local_approx(expected_flux_jacobian(i, j)));\n    }\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Characteristics\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_characteristic_speeds, (1, 2, 3))\n  CHECK_FOR_DATAVECTORS(test_largest_characteristic_speed, (1, 2, 3))\n\n  test_right_and_left_eigenvectors<1>();\n  test_right_and_left_eigenvectors<2>();\n  test_right_and_left_eigenvectors<3>();\n\n  test_numerical_eigenvectors<1>();\n  test_numerical_eigenvectors<2>();\n  test_numerical_eigenvectors<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_ConservativeFromPrimitive.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test_conservative_from_primitive(const DataVector& used_for_size) {\n  pypp::check_with_random_values<3>(\n      &NewtonianEuler::ConservativeFromPrimitive<Dim>::apply,\n      \"ConservativeFromPrimitive\",\n      {\"mass_density_cons\", \"momentum_density\", \"energy_density\"},\n      {{{-1.0, 1.0}, {-2.0, 2.0}, {-3.0, 3.0}}}, used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.ConservativeFromPrimitive\",\n    \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_conservative_from_primitive, (1, 2, 3))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Fluxes.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test_fluxes(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      &NewtonianEuler::ComputeFluxes<Dim>::apply, \"TimeDerivative\",\n      {\"mass_density_cons_flux_impl\", \"momentum_density_flux_impl\",\n       \"energy_density_flux_impl\"},\n      {{{-1.0, 1.0}}}, used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Fluxes\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_fluxes, (1, 2, 3))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_InternalEnergyDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/InternalEnergyDensity.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nvoid test_in_databox(const Scalar<DataType>& mass_density,\n                     const Scalar<DataType>& specific_internal_energy) {\n  TestHelpers::db::test_compute_tag<\n      NewtonianEuler::Tags::InternalEnergyDensityCompute<DataType>>(\n      \"InternalEnergyDensity\");\n  const auto box = db::create<\n      db::AddSimpleTags<hydro::Tags::RestMassDensity<DataType>,\n                        hydro::Tags::SpecificInternalEnergy<DataType>>,\n      db::AddComputeTags<\n          NewtonianEuler::Tags::InternalEnergyDensityCompute<DataType>>>(\n      mass_density, specific_internal_energy);\n\n  const auto expected_internal_energy_density =\n      NewtonianEuler::internal_energy_density(mass_density,\n                                              specific_internal_energy);\n  CHECK(db::get<NewtonianEuler::Tags::InternalEnergyDensity<DataType>>(box) ==\n        expected_internal_energy_density);\n}\n\ntemplate <typename DataType>\nvoid test(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> positive_distribution(0.0, 1.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_positive_distribution = make_not_null(&positive_distribution);\n\n  const auto mass_density = make_with_random_values<Scalar<DataType>>(\n      nn_generator, nn_positive_distribution, used_for_size);\n  Scalar<DataType> specific_internal_energy{};\n\n  // check with representative equation of state of one independent variable\n  EquationsOfState::PolytropicFluid<false> eos_1d(0.003, 4.0 / 3.0);\n  specific_internal_energy =\n      eos_1d.specific_internal_energy_from_density(mass_density);\n  CHECK(Scalar<DataType>{get(mass_density) * get(specific_internal_energy)} ==\n        NewtonianEuler::internal_energy_density(mass_density,\n                                                specific_internal_energy));\n\n  test_in_databox(mass_density, specific_internal_energy);\n\n  // check with representative equation of state of two independent variables\n  EquationsOfState::IdealFluid<false> eos_2d(5.0 / 3.0);\n  specific_internal_energy = make_with_random_values<Scalar<DataType>>(\n      nn_generator, nn_positive_distribution, used_for_size);\n  CHECK(Scalar<DataType>{get(mass_density) * get(specific_internal_energy)} ==\n        NewtonianEuler::internal_energy_density(mass_density,\n                                                specific_internal_energy));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.InternalEnergyDensity\",\n                  \"[Unit][Evolution]\") {\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  test(d);\n  test(dv);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_KineticEnergyDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/KineticEnergyDensity.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_in_databox(const Scalar<DataType>& mass_density,\n                     const tnsr::I<DataType, Dim>& velocity) {\n  TestHelpers::db::test_compute_tag<\n      NewtonianEuler::Tags::KineticEnergyDensityCompute<DataType, Dim>>(\n      \"KineticEnergyDensity\");\n\n  const auto box = db::create<\n      db::AddSimpleTags<hydro::Tags::RestMassDensity<DataType>,\n                        hydro::Tags::SpatialVelocity<DataType, Dim>>,\n      db::AddComputeTags<\n          NewtonianEuler::Tags::KineticEnergyDensityCompute<DataType, Dim>>>(\n      mass_density, velocity);\n\n  const auto expected_kinetic_energy_density =\n      NewtonianEuler::kinetic_energy_density(mass_density, velocity);\n  CHECK(db::get<NewtonianEuler::Tags::KineticEnergyDensity<DataType>>(box) ==\n        expected_kinetic_energy_density);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> positive_distribution(0.0, 1.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n  const auto nn_positive_distribution = make_not_null(&positive_distribution);\n\n  const auto mass_density = make_with_random_values<Scalar<DataType>>(\n      nn_generator, nn_positive_distribution, used_for_size);\n  const auto velocity = make_with_random_values<tnsr::I<DataType, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const DataType velocity_squared = get(dot_product(velocity, velocity));\n\n  CHECK(Scalar<DataType>{0.5 * get(mass_density) * velocity_squared} ==\n        NewtonianEuler::kinetic_energy_density(mass_density, velocity));\n\n  test_in_databox(mass_density, velocity);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.KineticEnergyDensity\",\n                  \"[Unit][Evolution]\") {\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test, (1, 2, 3))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_MachNumber.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/MachNumber.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <typename DataType, size_t Dim, typename EquationOfStateType>\nvoid test_in_databox(const Scalar<DataType>& mass_density,\n                     const tnsr::I<DataType, Dim>& velocity,\n                     const Scalar<DataType>& specific_internal_energy,\n                     const EquationOfStateType& equation_of_state) {\n  TestHelpers::db::test_compute_tag<\n      NewtonianEuler::Tags::MachNumberCompute<DataType, Dim>>(\"MachNumber\");\n\n  const auto box = db::create<\n      db::AddSimpleTags<hydro::Tags::RestMassDensity<DataType>,\n                        hydro::Tags::SpatialVelocity<DataType, Dim>,\n                        hydro::Tags::SpecificInternalEnergy<DataType>,\n                        hydro::Tags::EquationOfState<false, 2>>,\n      db::AddComputeTags<\n          NewtonianEuler::Tags::SoundSpeedSquaredCompute<DataType>,\n          NewtonianEuler::Tags::SoundSpeedCompute<DataType>,\n          NewtonianEuler::Tags::MachNumberCompute<DataType, Dim>>>(\n      mass_density, velocity, specific_internal_energy,\n      equation_of_state.promote_to_2d_eos());\n\n  const auto expected_sound_speed_squared = NewtonianEuler::sound_speed_squared(\n      mass_density, specific_internal_energy, equation_of_state);\n  const auto expected_sound_speed =\n      Scalar<DataType>{sqrt(get(expected_sound_speed_squared))};\n  const auto expected_mach_number =\n      NewtonianEuler::mach_number(velocity, expected_sound_speed);\n  CHECK(db::get<NewtonianEuler::Tags::MachNumber<DataType>>(box) ==\n        expected_mach_number);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> positive_distribution(0.0, 1.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n  const auto nn_positive_distribution = make_not_null(&positive_distribution);\n\n  const auto mass_density = make_with_random_values<Scalar<DataType>>(\n      nn_generator, nn_positive_distribution, used_for_size);\n  const auto velocity = make_with_random_values<tnsr::I<DataType, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const DataType velocity_squared = get(dot_product(velocity, velocity));\n  Scalar<DataType> specific_internal_energy{};\n  Scalar<DataType> sound_speed_squared{};\n\n  // check with representative equation of state of one independent variable\n  EquationsOfState::PolytropicFluid<false> eos_1d(0.003, 4.0 / 3.0);\n  get(sound_speed_squared) =\n      get(eos_1d.chi_from_density(mass_density)) +\n      get(eos_1d.kappa_times_p_over_rho_squared_from_density(mass_density));\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      Scalar<DataType>{sqrt(velocity_squared / get(sound_speed_squared))},\n      NewtonianEuler::mach_number(\n          velocity, Scalar<DataType>{sqrt(get(sound_speed_squared))}),\n      Approx::custom().epsilon(1.e-8));\n\n  test_in_databox(mass_density, velocity, specific_internal_energy, eos_1d);\n\n  // check with representative equation of state of two independent variables\n  EquationsOfState::IdealFluid<false> eos_2d(5.0 / 3.0);\n  specific_internal_energy = make_with_random_values<Scalar<DataType>>(\n      nn_generator, nn_positive_distribution, used_for_size);\n  get(sound_speed_squared) =\n      get(eos_2d.chi_from_density_and_energy(mass_density,\n                                             specific_internal_energy)) +\n      get(eos_2d.kappa_times_p_over_rho_squared_from_density_and_energy(\n          mass_density, specific_internal_energy));\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      Scalar<DataType>{sqrt(velocity_squared / get(sound_speed_squared))},\n      NewtonianEuler::mach_number(\n          velocity, Scalar<DataType>{sqrt(get(sound_speed_squared))}),\n      Approx::custom().epsilon(1.e-8));\n  test_in_databox(mass_density, velocity, specific_internal_energy, eos_2d);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.MachNumber\",\n                  \"[Unit][Evolution]\") {\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test, (1, 2, 3))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_PrimitiveFromConservative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/PrimitiveFromConservative.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct PrimitiveFromConservativeProxyThermoDim1 {\n  static void apply_helper(\n      const gsl::not_null<Scalar<DataVector>*> mass_density,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> velocity,\n      const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      const gsl::not_null<Scalar<DataVector>*> pressure,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density) {\n    NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n        mass_density, velocity, specific_internal_energy, pressure,\n        mass_density_cons, momentum_density, energy_density,\n        EquationsOfState::PolytropicFluid<false>(1.4, 5.0 / 3.0));\n  }\n};\n\ntemplate <size_t Dim>\nstruct PrimitiveFromConservativeProxyThermoDim2 {\n  static void apply_helper(\n      const gsl::not_null<Scalar<DataVector>*> mass_density,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> velocity,\n      const gsl::not_null<Scalar<DataVector>*> specific_internal_energy,\n      const gsl::not_null<Scalar<DataVector>*> pressure,\n      const Scalar<DataVector>& mass_density_cons,\n      const tnsr::I<DataVector, Dim>& momentum_density,\n      const Scalar<DataVector>& energy_density) {\n    NewtonianEuler::PrimitiveFromConservative<Dim>::apply(\n        mass_density, velocity, specific_internal_energy, pressure,\n        mass_density_cons, momentum_density, energy_density,\n        EquationsOfState::IdealFluid<false>(5.0 / 3.0));\n  }\n};\n\ntemplate <size_t Dim>\nvoid test_primitive_from_conservative(const DataVector& used_for_size) {\n  pypp::check_with_random_values<3>(\n      &PrimitiveFromConservativeProxyThermoDim1<Dim>::apply_helper,\n      \"PrimitiveFromConservative\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure_1d\"},\n      {{{0.0, 1.0}, {-2.0, 2.0}, {0.0, 3.0}}}, used_for_size);\n\n  pypp::check_with_random_values<3>(\n      &PrimitiveFromConservativeProxyThermoDim2<Dim>::apply_helper,\n      \"PrimitiveFromConservative\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure_2d\"},\n      {{{0.0, 1.0}, {-2.0, 2.0}, {0.0, 3.0}}}, used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.NewtonianEuler.PrimitiveFromConservative\",\n    \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_primitive_from_conservative, (1, 2, 3))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_RamPressure.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/RamPressure.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <typename DataType, size_t Dim>\nvoid test_in_databox(const Scalar<DataType>& mass_density,\n                     const tnsr::I<DataType, Dim>& velocity) {\n  TestHelpers::db::test_compute_tag<\n      NewtonianEuler::Tags::RamPressureCompute<DataType, Dim>>(\"RamPressure\");\n\n  const auto box =\n      db::create<db::AddSimpleTags<hydro::Tags::RestMassDensity<DataType>,\n                                   hydro::Tags::SpatialVelocity<DataType, Dim>>,\n                 db::AddComputeTags<\n                     NewtonianEuler::Tags::RamPressureCompute<DataType, Dim>>>(\n          mass_density, velocity);\n\n  const auto expected_ram_pressure =\n      NewtonianEuler::ram_pressure(mass_density, velocity);\n  CHECK(db::get<NewtonianEuler::Tags::RamPressure<DataType, Dim>>(box) ==\n        expected_ram_pressure);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> positive_distribution(0.0, 1.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n  const auto nn_positive_distribution = make_not_null(&positive_distribution);\n\n  const auto mass_density = make_with_random_values<Scalar<DataType>>(\n      nn_generator, nn_positive_distribution, used_for_size);\n  const auto velocity = make_with_random_values<tnsr::I<DataType, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n\n  auto expected_ram_pressure = make_with_random_values<tnsr::II<DataType, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      expected_ram_pressure.get(i, j) =\n          get(mass_density) * velocity.get(i) * velocity.get(j);\n    }\n  }\n  // Re-computation of dependent components in expected_ram_pressure will\n  // introduce some small error, so we loosen tolerance a bit.\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      expected_ram_pressure,\n      NewtonianEuler::ram_pressure(mass_density, velocity),\n      Approx::custom().epsilon(1.e-10));\n\n  test_in_databox(mass_density, velocity);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.RamPressure\",\n                  \"[Unit][Evolution]\") {\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test, (1, 2, 3))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_SoundSpeedSquared.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SoundSpeedSquared.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <typename DataType, typename EquationOfStateType>\nvoid test_in_databox(const Scalar<DataType>& mass_density,\n                     const Scalar<DataType>& specific_internal_energy,\n                     const EquationOfStateType& equation_of_state) {\n  TestHelpers::db::test_compute_tag<\n      NewtonianEuler::Tags::SoundSpeedSquaredCompute<DataType>>(\n      \"SoundSpeedSquared\");\n  TestHelpers::db::test_compute_tag<\n      NewtonianEuler::Tags::SoundSpeedCompute<DataType>>(\"SoundSpeed\");\n\n  const auto box = db::create<\n      db::AddSimpleTags<hydro::Tags::RestMassDensity<DataType>,\n                        hydro::Tags::SpecificInternalEnergy<DataType>,\n                        hydro::Tags::EquationOfState<false, 2>>,\n      db::AddComputeTags<\n          NewtonianEuler::Tags::SoundSpeedSquaredCompute<DataType>,\n          NewtonianEuler::Tags::SoundSpeedCompute<DataType>>>(\n      mass_density, specific_internal_energy,\n      equation_of_state.promote_to_2d_eos());\n\n  const auto expected_sound_speed_squared = NewtonianEuler::sound_speed_squared(\n      mass_density, specific_internal_energy, equation_of_state);\n  const auto expected_sound_speed =\n      Scalar<DataType>{sqrt(get(expected_sound_speed_squared))};\n  CHECK(db::get<hydro::Tags::SoundSpeedSquared<DataType>>(box) ==\n        expected_sound_speed_squared);\n  CHECK(db::get<NewtonianEuler::Tags::SoundSpeed<DataType>>(box) ==\n        expected_sound_speed);\n}\n\ntemplate <typename DataType>\nvoid test(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> positive_distribution(0.0, 1.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_positive_distribution = make_not_null(&positive_distribution);\n\n  const auto mass_density = make_with_random_values<Scalar<DataType>>(\n      nn_generator, nn_positive_distribution, used_for_size);\n  const auto specific_internal_energy =\n      make_with_random_values<Scalar<DataType>>(\n          nn_generator, nn_positive_distribution, used_for_size);\n  Scalar<DataType> sound_speed_squared{};\n\n  // check with representative equation of state of one independent variable\n  EquationsOfState::PolytropicFluid<false> eos_1d(0.003, 4.0 / 3.0);\n  get(sound_speed_squared) =\n      get(eos_1d.chi_from_density(mass_density)) +\n      get(eos_1d.kappa_times_p_over_rho_squared_from_density(mass_density));\n  CHECK(sound_speed_squared ==\n        NewtonianEuler::sound_speed_squared(mass_density,\n                                            specific_internal_energy, eos_1d));\n\n  test_in_databox(mass_density, specific_internal_energy, eos_1d);\n\n  // check with representative equation of state of two independent variables\n  EquationsOfState::IdealFluid<false> eos_2d(5.0 / 3.0);\n  get(sound_speed_squared) =\n      get(eos_2d.chi_from_density_and_energy(mass_density,\n                                             specific_internal_energy)) +\n      get(eos_2d.kappa_times_p_over_rho_squared_from_density_and_energy(\n          mass_density, specific_internal_energy));\n  CHECK(sound_speed_squared ==\n        NewtonianEuler::sound_speed_squared(mass_density,\n                                            specific_internal_energy, eos_2d));\n  test_in_databox(mass_density, specific_internal_energy, eos_2d);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.SoundSpeedSquared\",\n                  \"[Unit][Evolution]\") {\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  test(d);\n  test(dv);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_SpecificKineticEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/SpecificKineticEnergy.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <typename DataType, size_t Dim>\nvoid test_in_databox(const tnsr::I<DataType, Dim>& velocity) {\n  TestHelpers::db::test_compute_tag<\n      NewtonianEuler::Tags::SpecificKineticEnergyCompute<DataType, Dim>>(\n      \"SpecificKineticEnergy\");\n\n  const auto box = db::create<\n      db::AddSimpleTags<hydro::Tags::SpatialVelocity<DataType, Dim>>,\n      db::AddComputeTags<\n          NewtonianEuler::Tags::SpecificKineticEnergyCompute<DataType, Dim>>>(\n      velocity);\n\n  const auto expected_specific_kinetic_energy =\n      NewtonianEuler::specific_kinetic_energy(velocity);\n  CHECK(db::get<NewtonianEuler::Tags::SpecificKineticEnergy<DataType>>(box) ==\n        expected_specific_kinetic_energy);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> positive_distribution(0.0, 1.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  const auto velocity = make_with_random_values<tnsr::I<DataType, Dim>>(\n      nn_generator, nn_distribution, used_for_size);\n  const DataType velocity_squared = get(dot_product(velocity, velocity));\n\n  CHECK(Scalar<DataType>{0.5 * velocity_squared} ==\n        NewtonianEuler::specific_kinetic_energy(velocity));\n\n  test_in_databox(velocity);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.SpecificKineticEnergy\",\n                  \"[Unit][Evolution]\") {\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test, (1, 2, 3))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nstruct SomeSourceType {};\nstruct SomeInitialDataType {\n  using source_term_type = SomeSourceType;\n};\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_tags() {\n  TestHelpers::db::test_simple_tag<NewtonianEuler::Tags::MassDensityCons>(\n      \"MassDensityCons\");\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::Tags::MomentumDensity<Dim, Frame::Grid>>(\n      \"Grid_MomentumDensity\");\n  TestHelpers::db::test_simple_tag<NewtonianEuler::Tags::EnergyDensity>(\n      \"EnergyDensity\");\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::Tags::SoundSpeed<DataVector>>(\"SoundSpeed\");\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::Tags::CharacteristicSpeeds<Dim>>(\"CharacteristicSpeeds\");\n  TestHelpers::db::test_simple_tag<NewtonianEuler::Tags::VMinus>(\"VMinus\");\n  TestHelpers::db::test_simple_tag<NewtonianEuler::Tags::VMomentum<Dim>>(\n      \"VMomentum\");\n  TestHelpers::db::test_simple_tag<NewtonianEuler::Tags::VPlus>(\"VPlus\");\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::Tags::InternalEnergyDensity<DataVector>>(\n      \"InternalEnergyDensity\");\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::Tags::KineticEnergyDensity<DataVector>>(\n      \"KineticEnergyDensity\");\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::Tags::MachNumber<DataVector>>(\"MachNumber\");\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::Tags::RamPressure<DataVector, Dim, Frame::Grid>>(\n      \"Grid_RamPressure\");\n  TestHelpers::db::test_simple_tag<\n      NewtonianEuler::Tags::SpecificKineticEnergy<DataVector>>(\n      \"SpecificKineticEnergy\");\n  TestHelpers::db::test_simple_tag<NewtonianEuler::Tags::SourceTerm<Dim>>(\n      \"SourceTerm\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.Tags\",\n                  \"[Unit][Evolution]\") {\n  test_tags<1>();\n  test_tags<2>();\n  test_tags<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/Test_TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Systems/NewtonianEuler/Sources/UniformAcceleration.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/VortexPerturbation.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/TimeDerivativeTerms.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/Evolution/Systems/NewtonianEuler/TimeDerivativeTerms.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// We need a wrapper around the time derivative call because pypp cannot forward\n// the source terms, only Tensor<DataVector>s and doubles.\ntemplate <typename SourceTerm, size_t Dim>\nvoid wrap_time_derivative(\n    const gsl::not_null<Scalar<DataVector>*>\n        non_flux_terms_dt_mass_density_cons,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*>\n        non_flux_terms_dt_momentum_density,\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_energy_density,\n\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> mass_density_cons_flux,\n    const gsl::not_null<tnsr::IJ<DataVector, Dim>*> momentum_density_flux,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> energy_density_flux,\n\n    const Scalar<DataVector>& mass_density_cons,\n    const tnsr::I<DataVector, Dim>& momentum_density,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::I<DataVector, Dim>& velocity,\n    const Scalar<DataVector>& pressure,\n    const tnsr::I<DataVector, Dim>& coords) {\n  const double time = 1.38;\n  const EquationsOfState::IdealFluid<false> eos{5.0 / 3.0};\n  const Scalar<DataVector> specific_internal_energy =\n      eos.specific_internal_energy_from_density_and_pressure(mass_density_cons,\n                                                             pressure);\n  const SourceTerm source = []() {\n    if constexpr (std::is_same_v<\n                      SourceTerm,\n                      ::NewtonianEuler::Sources::UniformAcceleration<Dim>>) {\n      std::array<double, Dim> accel{};\n      for (size_t i = 0; i < Dim; ++i) {\n        gsl::at(accel, i) = 0.3 + static_cast<double>(i);\n      }\n      return ::NewtonianEuler::Sources::UniformAcceleration<Dim>{accel};\n    } else if constexpr (std::is_same_v<\n                             SourceTerm,\n                             ::NewtonianEuler::Sources::VortexPerturbation>) {\n      return ::NewtonianEuler::Sources::VortexPerturbation{0.1};\n    } else {\n      return SourceTerm{};\n    }\n  }();\n  Scalar<DataVector> enthalpy_density{get(energy_density).size()};\n  if constexpr (std::is_same_v<\n                    TestHelpers::NewtonianEuler::SomeOtherSourceType<Dim>,\n                    SourceTerm>) {\n    get(*non_flux_terms_dt_mass_density_cons) = -1.0;\n  }\n  NewtonianEuler::TimeDerivativeTerms<Dim>::apply(\n      non_flux_terms_dt_mass_density_cons, non_flux_terms_dt_momentum_density,\n      non_flux_terms_dt_energy_density, mass_density_cons_flux,\n      momentum_density_flux, energy_density_flux,\n\n      make_not_null(&enthalpy_density),\n\n      mass_density_cons, momentum_density, energy_density,\n\n      velocity, pressure, specific_internal_energy,\n\n      eos, coords, time, source);\n}\n\ntemplate <size_t Dim>\nvoid test_time_derivative(const DataVector& used_for_size) {\n  if constexpr (Dim == 3) {\n    pypp::check_with_random_values<1>(\n        &wrap_time_derivative<::NewtonianEuler::Sources::VortexPerturbation,\n                              Dim>,\n        \"TimeDerivative\",\n        {\"source_mass_density_cons_vortex_perturbation\",\n         \"source_momentum_density_cons_vortex_perturbation\",\n         \"source_energy_density_cons_vortex_perturbation\",\n         \"mass_density_cons_flux\", \"momentum_density_flux\",\n         \"energy_density_flux\"},\n        {{{-1.0, 1.0}}}, used_for_size);\n  }\n  pypp::check_with_random_values<1>(\n      &wrap_time_derivative<::NewtonianEuler::Sources::UniformAcceleration<Dim>,\n                            Dim>,\n      \"TimeDerivative\",\n      {\"source_mass_density_cons_uniform_acceleration\",\n       \"source_momentum_density_cons_uniform_acceleration\",\n       \"source_energy_density_cons_uniform_acceleration\",\n       \"mass_density_cons_flux\", \"momentum_density_flux\",\n       \"energy_density_flux\"},\n      {{{-1.0, 1.0}}}, used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.NewtonianEuler.TimeDerivative\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/NewtonianEuler\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_time_derivative, (1, 2, 3))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/TimeDerivative.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef mass_density_cons_flux_impl(\n    momentum_density, energy_density, velocity, pressure\n):\n    return momentum_density\n\n\ndef momentum_density_flux_impl(\n    momentum_density, energy_density, velocity, pressure\n):\n    result = np.outer(momentum_density, velocity)\n    result += pressure * np.identity(velocity.size)\n    return result\n\n\ndef energy_density_flux_impl(\n    momentum_density, energy_density, velocity, pressure\n):\n    return (energy_density + pressure) * velocity\n\n\ndef mass_density_cons_flux(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    return mass_density_cons_flux_impl(\n        momentum_density, energy_density, velocity, pressure\n    )\n\n\ndef momentum_density_flux(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    return momentum_density_flux_impl(\n        momentum_density, energy_density, velocity, pressure\n    )\n\n\ndef energy_density_flux(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    return energy_density_flux_impl(\n        momentum_density, energy_density, velocity, pressure\n    )\n\n\ndef source_mass_density_cons_uniform_acceleration(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    return mass_density * 0.0\n\n\ndef source_momentum_density_cons_uniform_acceleration(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    if len(momentum_density) == 1:\n        return mass_density * np.asarray([0.3])\n    elif len(momentum_density) == 2:\n        return mass_density * np.asarray([0.3, 1.3])\n    elif len(momentum_density) == 3:\n        return mass_density * np.asarray([0.3, 1.3, 2.3])\n\n\ndef source_energy_density_cons_uniform_acceleration(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    return np.einsum(\n        \"i,i->\",\n        (\n            np.asarray([0.3])\n            if len(momentum_density) == 1\n            else (\n                np.asarray([0.3, 1.3])\n                if len(momentum_density) == 2\n                else np.asarray([0.3, 1.3, 2.3])\n            )\n        ),\n        momentum_density,\n    )\n\n\n_perturbation_amplitude = 0.1\n\n\ndef source_mass_density_cons_vortex_perturbation(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    return mass_density * _perturbation_amplitude * np.cos(coords[2])\n\n\ndef source_momentum_density_cons_vortex_perturbation(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    return momentum_density * _perturbation_amplitude * np.cos(coords[2])\n\n\ndef source_energy_density_cons_vortex_perturbation(\n    mass_density, momentum_density, energy_density, velocity, pressure, coords\n):\n    return (\n        (energy_density + pressure + velocity[2] * momentum_density[2])\n        * _perturbation_amplitude\n        * np.cos(coords[2])\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/NewtonianEuler/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(M1Grey)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/DirichletAnalytic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Evolution.Systems.RadiationTransport.M1Grey.Fluxes as fluxes\nimport numpy as np\nimport PointwiseFunctions.AnalyticData.RadiationTransport.M1Grey.HomogeneousSphere as data\nimport PointwiseFunctions.AnalyticSolutions.RadiationTransport.M1Grey.ConstantM1 as soln\n\n\ndef soln_error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return None\n\n\n# constantM1 data\n_soln_mean_velocity = np.array([0.1, 0.2, 0.3])\n_soln_comoving_energy_density = 0.4\n\n# There is no Python implementation of the M1Closure, so for now we hardcode\n# the pressure tensor with values obtained by running the C++ code run with\n# the same input parameters. This is hacky, because it couples the C++ and\n# Python implementations and makes the test less robust.\n_tilde_p_values_from_cxx = np.array(\n    [\n        [0.13953488372093026, 0.012403100775193798, 0.018604651162790694],\n        [0.012403100775193798, 0.15813953488372096, 0.037209302325581388],\n        [0.018604651162790694, 0.037209302325581388, 0.18914728682170545],\n    ]\n)\n\n_radius = 1.0\n_emissivity_and_opacity = 1.0\n_outer_radius = 1.5\n_outer_opacity = 0.5\n_boundary_roundness = 0.03\n\n\ndef minerbo_closure(zeta):\n    return 1.0 / 3.0 + zeta * zeta * (\n        0.4 - 2.0 / 15.0 * zeta + 0.4 * zeta * zeta\n    )\n\n\n# compute pressure tensor for zero/low velocity case\n# non-zero velocities still have to be implemented\ndef calc_tilde_p(energy_density, momentum_density, inverse_spatial_metric):\n    contravariant_momentum_density = np.einsum(\n        \"i, ij\", momentum_density, inverse_spatial_metric\n    )\n\n    small_number = 1.0e-150\n\n    s2 = max(\n        (np.dot(momentum_density, contravariant_momentum_density), small_number)\n    )\n\n    zeta = np.sqrt(s2) / energy_density\n\n    chi = minerbo_closure(zeta)\n\n    d_thin_energy_over_momentum_squared = (\n        (1.5 * chi - 0.5) * energy_density / s2\n    )\n\n    d_thick_energy_over_3 = (0.5 - 0.5 * chi) * energy_density\n\n    dimensions = [0, 1, 2]\n\n    pressure_tensor = np.zeros((3, 3))\n\n    for i in dimensions:\n        for j in dimensions:\n            pressure_tensor[i, j] = (\n                d_thick_energy_over_3 * inverse_spatial_metric[i, j]\n                + d_thin_energy_over_momentum_squared\n                * contravariant_momentum_density[i]\n                * contravariant_momentum_density[j]\n            )\n\n    return pressure_tensor\n\n\ndef soln_tilde_e_nue_const(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln.tildeE(\n        coords, time, _soln_mean_velocity, _soln_comoving_energy_density\n    )\n\n\ndef soln_tilde_e_nue(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return data.m1_tildeE(\n        coords,\n        _radius,\n        _emissivity_and_opacity,\n        _outer_opacity,\n        _boundary_roundness,\n    )\n\n\ndef soln_tilde_e_bar_nue_const(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    # same as nue\n    return soln_tilde_e_nue_const(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n\n\ndef soln_tilde_e_bar_nue(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    # same as nue\n    return soln_tilde_e_nue(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n\n\ndef soln_tilde_s_nue_const(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln.tildeS(\n        coords, time, _soln_mean_velocity, _soln_comoving_energy_density\n    )\n\n\ndef soln_tilde_s_nue(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return data.m1_tildeS(\n        coords,\n        _radius,\n        _emissivity_and_opacity,\n        _outer_opacity,\n        _boundary_roundness,\n    )\n\n\n# same as nue\ndef soln_tilde_s_bar_nue_const(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_tilde_s_nue_const(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n\n\ndef soln_tilde_s_bar_nue(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_tilde_s_nue(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n\n\ndef soln_flux_tilde_e_nue_const(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    tilde_e = soln_tilde_e_nue_const(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n    tilde_s = soln_tilde_s_nue_const(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n    lapse = 1.0\n    shift = np.array([0.0, 0.0, 0.0])\n    spatial_metric = np.identity(3)\n    inv_spatial_metric = np.identity(3)\n    tilde_p = _tilde_p_values_from_cxx\n\n    return fluxes.tilde_e_flux(\n        tilde_e,\n        tilde_s,\n        tilde_p,\n        lapse,\n        shift,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef soln_flux_tilde_e_nue(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    tilde_e = soln_tilde_e_nue(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n    tilde_s = soln_tilde_s_nue(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n    lapse = 1.0\n    shift = np.array([0.0, 0.0, 0.0])\n    spatial_metric = np.identity(3)\n    inv_spatial_metric = np.identity(3)\n    tilde_p = calc_tilde_p(tilde_e, tilde_s, inv_spatial_metric)\n\n    return fluxes.tilde_e_flux(\n        tilde_e,\n        tilde_s,\n        tilde_p,\n        lapse,\n        shift,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\n# same as nue\ndef soln_flux_tilde_e_bar_nue_const(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_flux_tilde_e_nue_const(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n\n\ndef soln_flux_tilde_e_bar_nue(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_flux_tilde_e_nue(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n\n\ndef soln_flux_tilde_s_nue_const(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    tilde_e = soln_tilde_e_nue_const(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n    tilde_s = soln_tilde_s_nue_const(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n    lapse = 1.0\n    shift = np.array([0.0, 0.0, 0.0])\n    spatial_metric = np.identity(3)\n    inv_spatial_metric = np.identity(3)\n    tilde_p = _tilde_p_values_from_cxx\n\n    return fluxes.tilde_s_flux(\n        tilde_e,\n        tilde_s,\n        tilde_p,\n        lapse,\n        shift,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef soln_flux_tilde_s_nue(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    tilde_e = soln_tilde_e_nue(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n    tilde_s = soln_tilde_s_nue(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n    lapse = 1.0\n    shift = np.array([0.0, 0.0, 0.0])\n    spatial_metric = np.identity(3)\n    inv_spatial_metric = np.identity(3)\n    tilde_p = calc_tilde_p(tilde_e, tilde_s, inv_spatial_metric)\n\n    return fluxes.tilde_s_flux(\n        tilde_e,\n        tilde_s,\n        tilde_p,\n        lapse,\n        shift,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\n# same as nue\ndef soln_flux_tilde_s_bar_nue_const(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_flux_tilde_s_nue_const(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n\n\ndef soln_flux_tilde_s_bar_nue(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    coords,\n    time,\n    dim,\n):\n    return soln_flux_tilde_s_nue(\n        face_mesh_velocity,\n        outward_directed_normal_covector,\n        outward_directed_normal_vector,\n        coords,\n        time,\n        dim,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/HomogeneousSphere.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/ConstantM1.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\n\n// compare to other test_dirichlet analytic data\ntemplate <typename NeutrinoSpeciesList>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using initial_data_list =\n        tmpl::append<RadiationTransport::M1Grey::AnalyticData::all_data,\n                     RadiationTransport::M1Grey::Solutions::all_solutions>;\n    using factory_classes = tmpl::map<\n        tmpl::pair<RadiationTransport::M1Grey::BoundaryConditions::\n                       BoundaryCondition<NeutrinoSpeciesList>,\n                   tmpl::list<RadiationTransport::M1Grey::BoundaryConditions::\n                                  DirichletAnalytic<NeutrinoSpeciesList>>>,\n        tmpl::pair<evolution::initial_data::InitialData, initial_data_list>>;\n  };\n};\n\nstruct ConvertConstantM1 {\n  using unpacked_container = int;\n  using packed_container = RadiationTransport::M1Grey::Solutions::ConstantM1;\n  using packed_type = double;\n\n  static packed_container create_container() {\n    const std::array<double, 3> mean_velocity_{{0.1, 0.2, 0.3}};\n    const double comoving_energy_density = 0.4;\n    return {mean_velocity_, comoving_energy_density};\n  }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return 3;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nstruct ConvertHomogeneousSphere {\n  using unpacked_container = int;\n  using packed_container =\n      RadiationTransport::M1Grey::AnalyticData::HomogeneousSphere;\n  using packed_type = double;\n\n  static packed_container create_container() {\n    const double radius = 1.0;\n    const double emissivity_and_opacity = 1.0;\n    const double outer_opacity = 0.5;\n    const double boundary_roundness = 0.03;\n    return {radius, emissivity_and_opacity, outer_opacity, boundary_roundness};\n  }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return 3;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\nvoid test() {\n  using initial_data_list =\n      tmpl::append<RadiationTransport::M1Grey::AnalyticData::all_data,\n                   RadiationTransport::M1Grey::Solutions::all_solutions>;\n  register_classes_with_charm(initial_data_list{});\n\n  MAKE_GENERATOR(gen);\n\n  // Databox for constant M1\n  const auto box_analytic_soln = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticSolution<\n                      RadiationTransport::M1Grey::Solutions::ConstantM1>>>(\n      0.5, ConvertConstantM1::create_container());\n\n  // Databox for homogeneous sphere\n  const auto box_analytic_soln_homogen_sphere = db::create<db::AddSimpleTags<\n      Tags::Time, Tags::AnalyticData<RadiationTransport::M1Grey::AnalyticData::\n                                         HomogeneousSphere>>>(\n      0.5, ConvertHomogeneousSphere::create_container());\n\n  using neutrino_species = tmpl::list<neutrinos::ElectronNeutrinos<1>,\n                                      neutrinos::ElectronAntiNeutrinos<1>>;\n  using system = RadiationTransport::M1Grey::System<neutrino_species>;\n  using boundary_condition =\n      RadiationTransport::M1Grey::BoundaryConditions::BoundaryCondition<\n          neutrino_species>;\n  using dirichlet_analytic =\n      RadiationTransport::M1Grey::BoundaryConditions::DirichletAnalytic<\n          neutrino_species>;\n  using rusanov = RadiationTransport::M1Grey::BoundaryCorrections::Rusanov<\n      neutrino_species>;\n\n  using tilde_e_nue_tag =\n      RadiationTransport::M1Grey::Tags::TildeE<Frame::Inertial,\n                                               neutrinos::ElectronNeutrinos<1>>;\n  using tilde_e_bar_nue_tag = RadiationTransport::M1Grey::Tags::TildeE<\n      Frame::Inertial, neutrinos::ElectronAntiNeutrinos<1>>;\n  using tilde_s_nue_tag =\n      RadiationTransport::M1Grey::Tags::TildeS<Frame::Inertial,\n                                               neutrinos::ElectronNeutrinos<1>>;\n  using tilde_s_bar_nue_tag = RadiationTransport::M1Grey::Tags::TildeS<\n      Frame::Inertial, neutrinos::ElectronAntiNeutrinos<1>>;\n\n  // Constant M1\n  helpers::test_boundary_condition_with_python<\n      dirichlet_analytic, boundary_condition, system, tmpl::list<rusanov>,\n      tmpl::list<ConvertConstantM1>,\n      tmpl::list<Tags::AnalyticSolution<\n          RadiationTransport::M1Grey::Solutions::ConstantM1>>,\n      Metavariables<neutrino_species>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.RadiationTransport.M1Grey.BoundaryConditions.\"\n      \"DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<tilde_e_nue_tag>,\n          helpers::Tags::PythonFunctionName<tilde_e_bar_nue_tag>,\n          helpers::Tags::PythonFunctionName<tilde_s_nue_tag>,\n          helpers::Tags::PythonFunctionName<tilde_s_bar_nue_tag>,\n\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<tilde_e_nue_tag, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              tilde_e_bar_nue_tag, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<tilde_s_nue_tag, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              tilde_s_bar_nue_tag, tmpl::size_t<3>, Frame::Inertial>>>{\n          // python functions labeled below\n          \"soln_error\", \"soln_tilde_e_nue_const\", \"soln_tilde_e_bar_nue_const\",\n          \"soln_tilde_s_nue_const\", \"soln_tilde_s_bar_nue_const\",\n          \"soln_flux_tilde_e_nue_const\", \"soln_flux_tilde_e_bar_nue_const\",\n          \"soln_flux_tilde_s_nue_const\", \"soln_flux_tilde_s_bar_nue_const\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    ConstantM1:\\n\"\n      \"      MeanVelocity: [0.1, 0.2, 0.3]\\n\"\n      \"      ComovingEnergyDensity: 0.4\\n\",\n      Index<2>{5}, box_analytic_soln, tuples::TaggedTuple<>{});\n\n  // Homogeneous sphere\n  helpers::test_boundary_condition_with_python<\n      dirichlet_analytic, boundary_condition, system, tmpl::list<rusanov>,\n      tmpl::list<ConvertHomogeneousSphere>,\n      tmpl::list<Tags::AnalyticData<\n          RadiationTransport::M1Grey::AnalyticData::HomogeneousSphere>>,\n      Metavariables<neutrino_species>>(\n      make_not_null(&gen),\n      \"Evolution.Systems.RadiationTransport.M1Grey.BoundaryConditions.\"\n      \"DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<tilde_e_nue_tag>,\n          helpers::Tags::PythonFunctionName<tilde_e_bar_nue_tag>,\n          helpers::Tags::PythonFunctionName<tilde_s_nue_tag>,\n          helpers::Tags::PythonFunctionName<tilde_s_bar_nue_tag>,\n\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<tilde_e_nue_tag, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              tilde_e_bar_nue_tag, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<\n              ::Tags::Flux<tilde_s_nue_tag, tmpl::size_t<3>, Frame::Inertial>>,\n          helpers::Tags::PythonFunctionName<::Tags::Flux<\n              tilde_s_bar_nue_tag, tmpl::size_t<3>, Frame::Inertial>>>{\n          // python functions labeled below\n          \"soln_error\", \"soln_tilde_e_nue\", \"soln_tilde_e_bar_nue\",\n          \"soln_tilde_s_nue\", \"soln_tilde_s_bar_nue\", \"soln_flux_tilde_e_nue\",\n          \"soln_flux_tilde_e_bar_nue\", \"soln_flux_tilde_s_nue\",\n          \"soln_flux_tilde_s_bar_nue\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    HomogeneousSphere:\\n\"\n      \"      Radius: 1.0\\n\"\n      \"      EmissivityAndOpacity: 1.0\\n\"\n      \"      OuterOpacity: 0.5\\n\"\n      \"      BoundaryRoundness: 0.03\\n\", Index<2>{5},\n      box_analytic_soln_homogen_sphere, tuples::TaggedTuple<>{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.RadiationTransport.M1Grey.BoundaryConditions.DirichletAnalytic\",\n    \"[Unit][Evolution]\") {\n  using neutrino_species = tmpl::list<neutrinos::ElectronNeutrinos<1>,\n                                      neutrinos::ElectronAntiNeutrinos<1>>;\n\n  using dirichlet_analytic =\n      RadiationTransport::M1Grey::BoundaryConditions::DirichletAnalytic<\n          neutrino_species>;\n  PUPable_reg(dirichlet_analytic);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/Test_Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nSPECTRE_TEST_CASE(\"Unit.RadiationTransport.M1Grey.BoundaryConditions.Periodic\",\n                  \"[Unit][Evolution]\") {\n  using neutrino_species = tmpl::list<neutrinos::ElectronNeutrinos<1>,\n                                      neutrinos::ElectronAntiNeutrinos<1>>;\n  using boundary_condition =\n      RadiationTransport::M1Grey::BoundaryConditions::BoundaryCondition<\n          neutrino_species>;\n  helpers::test_periodic_condition<\n      domain::BoundaryConditions::Periodic<boundary_condition>,\n      boundary_condition>(\"Periodic:\\n\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/BoundaryConditions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Rusanov.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n_max_abs_char_speed = 1.0\n\n\ndef dg_package_data(\n    tilde_e_nue,\n    tilde_e_bar_nue,\n    tilde_s_nue,\n    tilde_s_bar_nue,\n    flux_tilde_e_nue,\n    flux_tilde_e_bar_nue,\n    flux_tilde_s_nue,\n    flux_tilde_s_bar_nue,\n    normal_covector,\n    normal_vector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n):\n    return (\n        tilde_e_nue,\n        tilde_e_bar_nue,\n        tilde_s_nue,\n        tilde_s_bar_nue,\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_tilde_e_nue)),\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_tilde_e_bar_nue)),\n        np.asarray(np.einsum(\"i,ij->j\", normal_covector, flux_tilde_s_nue)),\n        np.asarray(np.einsum(\"i,ij->j\", normal_covector, flux_tilde_s_bar_nue)),\n    )\n\n\ndef dg_boundary_terms(\n    interior_tilde_e_nue,\n    interior_tilde_e_bar_nue,\n    interior_tilde_s_nue,\n    interior_tilde_s_bar_nue,\n    interior_normal_dot_flux_tilde_e_nue,\n    interior_normal_dot_flux_tilde_e_bar_nue,\n    interior_normal_dot_flux_tilde_s_nue,\n    interior_normal_dot_flux_tilde_s_bar_nue,\n    exterior_tilde_e_nue,\n    exterior_tilde_e_bar_nue,\n    exterior_tilde_s_nue,\n    exterior_tilde_s_bar_nue,\n    exterior_normal_dot_flux_tilde_e_nue,\n    exterior_normal_dot_flux_tilde_e_bar_nue,\n    exterior_normal_dot_flux_tilde_s_nue,\n    exterior_normal_dot_flux_tilde_s_bar_nue,\n    use_strong_form,\n):\n    sign_for_form = 1.0 if use_strong_form else -1.0\n\n    # Use scope and locals() to get arguments into the eval context below\n    scope = locals()\n\n    def impl(var_name):\n        return np.asarray(\n            (\n                -0.5\n                * (\n                    sign_for_form\n                    * eval(\"interior_normal_dot_flux_\" + var_name, scope)\n                    + eval(\"exterior_normal_dot_flux_\" + var_name, scope)\n                )\n                - 0.5\n                * _max_abs_char_speed\n                * (\n                    eval(\"exterior_\" + var_name, scope)\n                    - eval(\"interior_\" + var_name, scope)\n                )\n            )\n        )\n\n    return (\n        impl(\"tilde_e_nue\"),\n        impl(\"tilde_e_bar_nue\"),\n        impl(\"tilde_s_nue\"),\n        impl(\"tilde_s_bar_nue\"),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Test_Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace helpers = TestHelpers::evolution::dg;\n\nvoid test(const gsl::not_null<std::mt19937*> gen, const size_t num_pts) {\n  using neutrino_species = tmpl::list<neutrinos::ElectronNeutrinos<1>,\n                                      neutrinos::ElectronAntiNeutrinos<1>>;\n  using system = RadiationTransport::M1Grey::System<neutrino_species>;\n  using rusanov = RadiationTransport::M1Grey::BoundaryCorrections::Rusanov<\n      neutrino_species>;\n\n  helpers::test_boundary_correction_conservation<system>(\n      gen, rusanov{},\n      Mesh<2>{num_pts, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      {}, {});\n\n  helpers::test_boundary_correction_with_python<system>(\n      gen, \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\", rusanov{},\n      Mesh<2>{num_pts, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      {}, {});\n\n  const auto rusanov_from_factory =\n      TestHelpers::test_factory_creation<evolution::BoundaryCorrection,\n                                         rusanov>(\"Rusanov:\");\n\n  helpers::test_boundary_correction_with_python<system>(\n      gen, \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const rusanov&>(*rusanov_from_factory),\n      Mesh<2>{num_pts, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n      {}, {});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.RadiationTransport.M1Grey.BoundaryCorrections.Rusanov\",\n                  \"[Unit][Evolution]\") {\n  using neutrino_species = tmpl::list<neutrinos::ElectronNeutrinos<1>,\n                                      neutrinos::ElectronAntiNeutrinos<1>>;\n  using rusanov = RadiationTransport::M1Grey::BoundaryCorrections::Rusanov<\n      neutrino_species>;\n  register_classes_with_charm<rusanov>();\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/RadiationTransport/M1Grey/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  test(make_not_null(&gen), 5);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_M1Grey\")\n\nset(LIBRARY_SOURCES\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryConditions/Test_Periodic.cpp\n  BoundaryCorrections/Test_Rusanov.cpp\n  Test_Actions.cpp\n  Test_Fluxes.cpp\n  Test_M1Closure.cpp\n  Test_M1HydroCoupling.cpp\n  Test_Sources.cpp\n  Test_System.cpp\n  Test_Tags.cpp\n  Test_TimeDerivativeTerms.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  DiscontinuousGalerkin\n  Domain\n  M1Grey\n  M1GreyAnalyticData\n  M1GreySolutions\n  Parallel\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/ComputeM1HydroCouplingJacobian.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef neutrino_source_jacobian(\n    tilde_s,\n    tilde_e,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    fluid_velocity,\n    fluid_lorentz_factor,\n    closure_factor,\n    comoving_energy_density,\n    comoving_momentum_density_spatial,\n    comoving_momentum_density_normal,\n    lapse,\n    spatial_metric,\n    inverse_spatial_metric,\n):\n    s_squared_floor = 1.0e-150\n\n    d_thin = (\n        0.2\n        * closure_factor**2\n        * (3.0 + closure_factor * (-1.0 + 3.0 * closure_factor))\n    )\n\n    d_thick = 1.0 - d_thin\n\n    fluid_velocity_lower = np.einsum(\"a, ia\", fluid_velocity, spatial_metric)\n\n    s_upper = np.einsum(\"a, ia\", tilde_s, inverse_spatial_metric)\n\n    # np.einsum(\"a, ia\", , )\n\n    comoving_four_momentum_density_upper = (\n        np.einsum(\n            \"a, ia\", comoving_momentum_density_spatial, inverse_spatial_metric\n        )\n        + comoving_momentum_density_normal * fluid_velocity\n    )\n\n    inverse_s_norm = 1.0 / (\n        np.einsum(\"a, a\", s_upper, tilde_s) + s_squared_floor\n    )\n\n    fluid_velocity_norm = np.einsum(\n        \"a, a\", fluid_velocity, fluid_velocity_lower\n    )\n\n    s_dot_fluid_velocity = np.einsum(\"a, a\", tilde_s, fluid_velocity)\n\n    s_dot_fluid_velocity_normalized = s_dot_fluid_velocity * inverse_s_norm\n\n    s_dot_fluid_velocity_squared_normalized = (\n        s_dot_fluid_velocity * s_dot_fluid_velocity_normalized\n    )\n\n    denom = 1.0 / (1.0 + 2.0 * fluid_lorentz_factor**2)\n\n    scaled_comoving_energy_density = (\n        closure_factor**2 * comoving_energy_density\n    )\n\n    h_difference_s_coef = fluid_lorentz_factor * (\n        fluid_velocity_norm - tilde_e * s_dot_fluid_velocity_normalized\n    )\n\n    common_difference_term = denom * (\n        (2.0 * fluid_lorentz_factor**2 - 3.0) * tilde_e\n        - 4.0 * fluid_lorentz_factor**2 * s_dot_fluid_velocity\n    )\n\n    j_difference = fluid_lorentz_factor**2 * (\n        fluid_velocity_norm * common_difference_term\n        + tilde_e * s_dot_fluid_velocity_squared_normalized\n    )\n\n    h_difference_v_coef = fluid_lorentz_factor * (\n        common_difference_term + j_difference + s_dot_fluid_velocity\n    )\n\n    h_difference = (\n        h_difference_s_coef * tilde_s\n        - h_difference_v_coef * fluid_velocity_lower\n    )\n\n    deriv_e_h_v_coef = fluid_lorentz_factor**2 * (\n        -4.0 * d_thick * denom\n        - d_thin * (1.0 + s_dot_fluid_velocity_squared_normalized)\n    )\n\n    deriv_e_h_s_coef = d_thin * s_dot_fluid_velocity_normalized\n\n    deriv_s_h_trace_coef = (\n        1.0 / fluid_lorentz_factor + d_thin * h_difference_s_coef\n    ) / fluid_lorentz_factor\n\n    deriv_s_j_v_coef = (\n        -2.0\n        * fluid_lorentz_factor\n        * (deriv_s_h_trace_coef + d_thick * fluid_velocity_norm * denom)\n    )\n\n    deriv_s_h_vv_coef = (\n        2.0\n        - denom * d_thick\n        + 2.0 * d_thin * fluid_lorentz_factor * h_difference_s_coef\n    )\n\n    deriv_s_h_vs_coef = d_thin * tilde_e * inverse_s_norm\n\n    deriv_s_h_ss_coef = (\n        2.0 * s_dot_fluid_velocity_normalized * deriv_s_h_vs_coef\n    )\n\n    deriv_s_j_s_coef = (\n        -2.0\n        * fluid_lorentz_factor\n        * s_dot_fluid_velocity_squared_normalized\n        * deriv_s_h_vs_coef\n    )\n\n    deriv_s_h_sv_coef = fluid_lorentz_factor * deriv_s_j_s_coef\n\n    constant_d_deriv_e_j_over_lorentz_factor = fluid_lorentz_factor * (\n        d_thin * (1.0 + s_dot_fluid_velocity_squared_normalized)\n        + 3.0 * d_thick * denom * (1.0 + fluid_velocity_norm)\n    )\n\n    constant_d_deriv_s_j_over_lorentz_factor = (\n        deriv_s_j_v_coef * fluid_velocity + deriv_s_j_s_coef * s_upper\n    )\n\n    constant_d_deriv_e_h_over_lorentz_factor = (\n        deriv_e_h_v_coef * fluid_velocity_lower - deriv_e_h_s_coef * tilde_s\n    )\n\n    # needs outer product\n    constant_d_deriv_s_h_over_lorentz_factor = (\n        deriv_s_h_vv_coef * np.outer(fluid_velocity, fluid_velocity_lower)\n        + deriv_s_h_ss_coef * np.outer(s_upper, tilde_s)\n        - deriv_s_h_sv_coef * np.outer(s_upper, fluid_velocity_lower)\n        - deriv_s_h_vs_coef * np.outer(fluid_velocity, tilde_s)\n    )\n\n    loop_bound = 3\n    for i in range(0, loop_bound):\n        constant_d_deriv_s_h_over_lorentz_factor[i, i] += deriv_s_h_trace_coef\n\n    deriv_dthin_prefactor = 1.0 / (\n        scaled_comoving_energy_density * j_difference\n        - np.einsum(\"a, a\", comoving_four_momentum_density_upper, h_difference)\n        + 5.0\n        / 3.0\n        * comoving_energy_density**2\n        / (2.0 + closure_factor * (-1.0 + closure_factor * 4.0))\n    )\n\n    deriv_e_dthin_over_lorentz_factor = deriv_dthin_prefactor * (\n        np.einsum(\n            \"a, a\",\n            comoving_four_momentum_density_upper,\n            constant_d_deriv_e_h_over_lorentz_factor,\n        )\n        - scaled_comoving_energy_density\n        * constant_d_deriv_e_j_over_lorentz_factor\n    )\n\n    deriv_s_dthin_over_lorentz_factor = deriv_dthin_prefactor * (\n        np.einsum(\n            \"a, ia\",\n            comoving_four_momentum_density_upper,\n            constant_d_deriv_s_h_over_lorentz_factor,\n        )\n        - scaled_comoving_energy_density\n        * constant_d_deriv_s_j_over_lorentz_factor\n    )\n\n    deriv_e_j = fluid_lorentz_factor * (\n        constant_d_deriv_e_j_over_lorentz_factor\n        + j_difference * deriv_e_dthin_over_lorentz_factor\n    )\n\n    deriv_s_j = fluid_lorentz_factor * (\n        constant_d_deriv_s_j_over_lorentz_factor\n        + j_difference * deriv_s_dthin_over_lorentz_factor\n    )\n\n    deriv_e_h = fluid_lorentz_factor * (\n        constant_d_deriv_e_h_over_lorentz_factor\n        + deriv_e_dthin_over_lorentz_factor * h_difference\n    )\n\n    deriv_s_h = fluid_lorentz_factor * (\n        constant_d_deriv_s_h_over_lorentz_factor\n        + np.outer(deriv_s_dthin_over_lorentz_factor, h_difference)\n    )\n\n    deriv_source_e_j_coef = lapse * fluid_lorentz_factor * scattering_opacity\n\n    deriv_source_e_v_coef = (\n        lapse * fluid_lorentz_factor * (absorption_opacity + scattering_opacity)\n    )\n\n    deriv_source_s_h_coef = -lapse * (absorption_opacity + scattering_opacity)\n\n    deriv_source_s_jv_coef = -lapse * fluid_lorentz_factor * absorption_opacity\n\n    # returned source terms\n    deriv_e_source_e = deriv_source_e_j_coef * deriv_e_j - deriv_source_e_v_coef\n\n    deriv_s_source_e = (\n        deriv_source_e_j_coef * deriv_s_j\n        + deriv_source_e_v_coef * fluid_velocity\n    )\n\n    deriv_e_source_s = (\n        deriv_source_s_h_coef * deriv_e_h\n        + deriv_source_s_jv_coef * deriv_e_j * fluid_velocity_lower\n    )\n\n    deriv_s_source_s = (\n        deriv_source_s_h_coef * deriv_s_h\n        + deriv_source_s_jv_coef * np.outer(deriv_s_j, fluid_velocity_lower)\n    )\n\n    return (\n        deriv_e_source_e,\n        deriv_s_source_e,\n        deriv_e_source_s,\n        deriv_s_source_s,\n    )\n\n\ndef hydro_coupling_jacobian_deriv_e_source_e(\n    tilde_s,\n    tilde_e,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    fluid_velocity,\n    fluid_lorentz_factor,\n    closure_factor,\n    comoving_energy_density,\n    comoving_momentum_density_spatial,\n    comoving_momentum_density_normal,\n    lapse,\n    spatial_metric,\n    inverse_spatial_metric,\n):\n    deriv_e_source_e, _, _, _ = neutrino_source_jacobian(\n        tilde_s,\n        tilde_e,\n        emissivity,\n        absorption_opacity,\n        scattering_opacity,\n        fluid_velocity,\n        fluid_lorentz_factor,\n        closure_factor,\n        comoving_energy_density,\n        comoving_momentum_density_spatial,\n        comoving_momentum_density_normal,\n        lapse,\n        spatial_metric,\n        inverse_spatial_metric,\n    )\n\n    result = deriv_e_source_e\n\n    return result\n\n\ndef hydro_coupling_jacobian_deriv_e_source_s(\n    tilde_s,\n    tilde_e,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    fluid_velocity,\n    fluid_lorentz_factor,\n    closure_factor,\n    comoving_energy_density,\n    comoving_momentum_density_spatial,\n    comoving_momentum_density_normal,\n    lapse,\n    spatial_metric,\n    inverse_spatial_metric,\n):\n    _, _, deriv_e_source_s, _ = neutrino_source_jacobian(\n        tilde_s,\n        tilde_e,\n        emissivity,\n        absorption_opacity,\n        scattering_opacity,\n        fluid_velocity,\n        fluid_lorentz_factor,\n        closure_factor,\n        comoving_energy_density,\n        comoving_momentum_density_spatial,\n        comoving_momentum_density_normal,\n        lapse,\n        spatial_metric,\n        inverse_spatial_metric,\n    )\n\n    result = deriv_e_source_s\n\n    return result\n\n\ndef hydro_coupling_jacobian_deriv_s_source_e(\n    tilde_s,\n    tilde_e,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    fluid_velocity,\n    fluid_lorentz_factor,\n    closure_factor,\n    comoving_energy_density,\n    comoving_momentum_density_spatial,\n    comoving_momentum_density_normal,\n    lapse,\n    spatial_metric,\n    inverse_spatial_metric,\n):\n    _, deriv_s_source_e, _, _ = neutrino_source_jacobian(\n        tilde_s,\n        tilde_e,\n        emissivity,\n        absorption_opacity,\n        scattering_opacity,\n        fluid_velocity,\n        fluid_lorentz_factor,\n        closure_factor,\n        comoving_energy_density,\n        comoving_momentum_density_spatial,\n        comoving_momentum_density_normal,\n        lapse,\n        spatial_metric,\n        inverse_spatial_metric,\n    )\n\n    result = deriv_s_source_e\n\n    return result\n\n\ndef hydro_coupling_jacobian_deriv_s_source_s(\n    tilde_s,\n    tilde_e,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    fluid_velocity,\n    fluid_lorentz_factor,\n    closure_factor,\n    comoving_energy_density,\n    comoving_momentum_density_spatial,\n    comoving_momentum_density_normal,\n    lapse,\n    spatial_metric,\n    inverse_spatial_metric,\n):\n    _, _, _, deriv_s_source_s = neutrino_source_jacobian(\n        tilde_s,\n        tilde_e,\n        emissivity,\n        absorption_opacity,\n        scattering_opacity,\n        fluid_velocity,\n        fluid_lorentz_factor,\n        closure_factor,\n        comoving_energy_density,\n        comoving_momentum_density_spatial,\n        comoving_momentum_density_normal,\n        lapse,\n        spatial_metric,\n        inverse_spatial_metric,\n    )\n\n    result = deriv_s_source_s\n\n    return result\n\n\n# End of functions for testing M1HydroCoupling.cpp (Jacobian terms)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Fluxes.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\n# Functions for testing Fluxes.cpp\ndef tilde_e_flux(\n    tilde_e, tilde_s, tilde_p, lapse, shift, spatial_metric, inv_spatial_metric\n):\n    result = (\n        lapse * (np.einsum(\"a, ia\", tilde_s, inv_spatial_metric))\n        - shift * tilde_e\n    )\n    return result\n\n\ndef tilde_s_flux(\n    tilde_e, tilde_s, tilde_p, lapse, shift, spatial_metric, inv_spatial_metric\n):\n    result = lapse * (np.einsum(\"ia, aj\", tilde_p, spatial_metric)) - np.outer(\n        shift, tilde_s\n    )\n    return result\n\n\n# End of functions for testing Fluxes.cpp\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/M1HydroCoupling.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\n# Functions for testing M1HydroCoupling.cpp\ndef hydro_coupling_tilde_e(\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    tilde_j,\n    tilde_hn,\n    tilde_hi,\n    fluid_velocity,\n    lorentz_factor,\n    lapse,\n    spatial_metric,\n    sqrt_det_spatial_metric,\n):\n    result = (\n        lapse\n        * lorentz_factor\n        * (sqrt_det_spatial_metric * emissivity - absorption_opacity * tilde_j)\n        + lapse * (absorption_opacity + scattering_opacity) * tilde_hn\n    )\n    return result\n\n\ndef hydro_coupling_tilde_s(\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    tilde_j,\n    tilde_hn,\n    tilde_hi,\n    fluid_velocity,\n    lorentz_factor,\n    lapse,\n    spatial_metric,\n    sqrt_det_spatial_metric,\n):\n    result = (\n        lapse\n        * np.einsum(\"a, ia\", fluid_velocity, spatial_metric)\n        * lorentz_factor\n        * (sqrt_det_spatial_metric * emissivity - absorption_opacity * tilde_j)\n        - lapse * (absorption_opacity + scattering_opacity) * tilde_hi\n    )\n\n    return result\n\n\n# End of functions for testing M1HydroCoupling.cpp\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Sources.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom M1HydroCoupling import *\n\n\n# Functions for testing Sources.cpp\ndef source_tilde_e(\n    tilde_e,\n    tilde_s,\n    tilde_p,\n    lapse,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    spatial_metric,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    tilde_j,\n    tilde_h_normal,\n    tilde_h_spatial,\n    spatial_velocity,\n    lorentz,\n    sqrt_det_spatial_metric,\n):\n    # coupling term with fluid\n    source_n = hydro_coupling_tilde_e(\n        emissivity,\n        absorption_opacity,\n        scattering_opacity,\n        tilde_j,\n        tilde_h_normal,\n        tilde_h_spatial,\n        spatial_velocity,\n        lorentz,\n        lapse,\n        spatial_metric,\n        sqrt_det_spatial_metric,\n    )\n\n    result = (\n        lapse * np.einsum(\"ab, ab\", tilde_p, extrinsic_curvature)\n        - np.einsum(\"ab, ab\", inv_spatial_metric, np.outer(tilde_s, d_lapse))\n        + source_n\n    )\n\n    return result\n\n\ndef source_tilde_s(\n    tilde_e,\n    tilde_s,\n    tilde_p,\n    lapse,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    inv_spatial_metric,\n    extrinsic_curvature,\n    spatial_metric,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    tilde_j,\n    tilde_h_normal,\n    tilde_h_spatial,\n    spatial_velocity,\n    lorentz,\n    sqrt_det_spatial_metric,\n):\n    # coupling with fluid\n    source_i = hydro_coupling_tilde_s(\n        emissivity,\n        absorption_opacity,\n        scattering_opacity,\n        tilde_j,\n        tilde_h_normal,\n        tilde_h_spatial,\n        spatial_velocity,\n        lorentz,\n        lapse,\n        spatial_metric,\n        sqrt_det_spatial_metric,\n    )\n\n    result = (\n        0.5 * lapse * np.einsum(\"ab, iab\", tilde_p, d_spatial_metric)\n        + np.einsum(\"a, ia\", tilde_s, d_shift)\n        - tilde_e * d_lapse\n        + source_i\n    )\n    return result\n\n\n# End of functions for testing Sources.cpp\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Test_Actions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/variant/get.hpp>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1Closure.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct mock_component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using Closure = typename RadiationTransport::M1Grey::ComputeM1Closure<\n      typename Metavariables::neutrino_species>;\n  using simple_tags = db::AddSimpleTags<tmpl::flatten<\n      tmpl::list<typename Closure::return_tags, typename Closure::argument_tags,\n                 domain::Tags::Coordinates<3, Frame::Inertial>>>>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<\n                     Parallel::Phase::Initialization,\n                     tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Testing,\n                     tmpl::list<Actions::MutateApply<\n                         typename RadiationTransport::M1Grey::ComputeM1Closure<\n                             typename metavariables::neutrino_species>>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<mock_component<Metavariables>>;\n  using neutrino_species = tmpl::list<neutrinos::ElectronNeutrinos<1>,\n                                      neutrinos::HeavyLeptonNeutrinos<0>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.RadiationTransport.M1Grey.Actions\", \"[Unit][M1Grey]\") {\n  using component = mock_component<Metavariables>;\n\n  const DataVector x{-2.0, -1.0, 0.0, 1.0, 2.0};\n  const DataVector y{-2.0, -1.0, 0.0, 1.0, 2.0};\n  const DataVector z{-2.0, -1.0, 0.0, 1.0, 2.0};\n\n  // Closure output variables (initialization only,\n  // use the same value for all species)\n  const DataVector xi{0.5, 0.5, 0.5, 0.5, 0.5};\n  const auto tildeP = make_with_value<tnsr::II<DataVector, 3>>(x, 0.5);\n  const DataVector J{0.5, 0.5, 0.5, 0.5, 0.5};\n  const DataVector Hn{0.5, 0.5, 0.5, 0.5, 0.5};\n  const DataVector Hx{0.5, 0.5, 0.5, 0.5, 0.5};\n  const DataVector Hy{0.5, 0.5, 0.5, 0.5, 0.5};\n  const DataVector Hz{0.5, 0.5, 0.5, 0.5, 0.5};\n  // Closure input variables\n  // First neutrino species (set to optically thick)\n  const DataVector E0{1.0, 1.0, 1.0, 1.0, 1.0};\n  const DataVector Sx0{0.0, 0.0, 0.0, 0.0, 0.0};\n  const DataVector Sy0{0.0, 0.0, 0.0, 0.0, 0.0};\n  const DataVector Sz0{0.0, 0.0, 0.0, 0.0, 0.0};\n  // Second neutrino species (set to optically thin)\n  const DataVector E1{1.0, 1.0, 1.0, 1.0, 1.0};\n  const DataVector Sx1{0.0, 0.0, 0.0, 1.0, 1.0};\n  const DataVector Sy1{0.0, 0.0, 1.0, 0.0, 0.0};\n  const DataVector Sz1{1.0, 1.0, 0.0, 0.0, 0.0};\n\n  // Fluid and metric variables\n  const DataVector vx{0.0, 0.0, 0.0, 0.0, 0.0};\n  const DataVector vy{0.0, 0.0, 0.0, 0.0, 0.0};\n  const DataVector vz{0.0, 0.0, 0.0, 0.0, 0.0};\n  const DataVector W{1.0, 1.0, 1.0, 1.0, 1.0};\n  auto metric = make_with_value<tnsr::ii<DataVector, 3>>(x, 0.0);\n  auto inverse_metric = make_with_value<tnsr::II<DataVector, 3>>(x, 0.0);\n  get<0, 0>(metric) = 1.;\n  get<1, 1>(metric) = 1.;\n  get<2, 2>(metric) = 1.;\n  get<0, 0>(inverse_metric) = 1.;\n  get<1, 1>(inverse_metric) = 1.;\n  get<2, 2>(inverse_metric) = 1.;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {Scalar<DataVector>{xi}, Scalar<DataVector>{xi}, tildeP, tildeP,\n       Scalar<DataVector>{J}, Scalar<DataVector>{J}, Scalar<DataVector>{Hn},\n       Scalar<DataVector>{Hn},\n       tnsr::i<DataVector, 3, Frame::Inertial>{{{Hx, Hy, Hz}}},\n       tnsr::i<DataVector, 3, Frame::Inertial>{{{Hx, Hy, Hz}}},\n       Scalar<DataVector>{E0}, Scalar<DataVector>{E1},\n       tnsr::i<DataVector, 3, Frame::Inertial>{{{Sx0, Sy0, Sz0}}},\n       tnsr::i<DataVector, 3, Frame::Inertial>{{{Sx1, Sy1, Sz1}}},\n       tnsr::I<DataVector, 3, Frame::Inertial>{{{vx, vy, vz}}},\n       Scalar<DataVector>{W}, metric, inverse_metric,\n       tnsr::I<DataVector, 3, Frame::Inertial>{{{x, y, z}}}});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  runner.next_action<component>(0);\n\n  // Check that first species return xi=0 (optically thick)\n  const DataVector expected_xi0{0.0, 0.0, 0.0, 0.0, 0.0};\n  CHECK_ITERABLE_APPROX(\n      (ActionTesting::get_databox_tag<\n           component, RadiationTransport::M1Grey::Tags::ClosureFactor<\n                          neutrinos::ElectronNeutrinos<1>>>(runner, 0)\n           .get()),\n      expected_xi0);\n  // Check that second species return xi=1 (optically thin)\n  const DataVector expected_xi1{1.0, 1.0, 1.0, 1.0, 1.0};\n  CHECK_ITERABLE_APPROX(\n      (ActionTesting::get_databox_tag<\n           component, RadiationTransport::M1Grey::Tags::ClosureFactor<\n                          neutrinos::HeavyLeptonNeutrinos<0>>>(runner, 0)\n           .get()),\n      expected_xi1);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Test_Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Fluxes.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.RadiationTransport.M1Grey.Fluxes\", \"[Unit][M1Grey]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/RadiationTransport/M1Grey\"};\n\n  pypp::check_with_random_values<1>(&RadiationTransport::M1Grey::ComputeFluxes<\n                                        neutrinos::ElectronNeutrinos<0>>::apply,\n                                    \"Fluxes\", {\"tilde_e_flux\", \"tilde_s_flux\"},\n                                    {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Test_M1Closure.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1Closure.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntnsr::a<DataVector, 3> add_time_component_from_normal(\n    const tnsr::i<DataVector, 3>& three_covector,\n    const tnsr::A<DataVector, 3>& normal_vector,\n    const Scalar<DataVector>& vector_dot_normal) {\n  auto four_covector =\n      make_with_value<tnsr::a<DataVector, 3>>(three_covector, 0.0);\n  const auto temporal_part = tenex::evaluate(\n      vector_dot_normal() - three_covector(ti::i) * normal_vector(ti::I));\n  tenex::evaluate<ti::t>(make_not_null(&four_covector),\n                         temporal_part() / normal_vector(ti::T));\n  tenex::evaluate<ti::i>(make_not_null(&four_covector), three_covector(ti::i));\n\n  INFO(\"Test-internal 4-vector creation\");\n  CHECK_ITERABLE_APPROX(\n      tenex::evaluate(four_covector(ti::a) * normal_vector(ti::A)),\n      vector_dot_normal);\n\n  return four_covector;\n}\n\nvoid check_closure_consistency(\n    const tnsr::ii<DataVector, 3>& spatial_metric,\n    const tnsr::I<DataVector, 3>& fluid_velocity,\n    const Scalar<DataVector>& fluid_lorentz_factor,\n    const Scalar<DataVector>& energy_density,\n    const tnsr::i<DataVector, 3>& momentum_density,\n    const Scalar<DataVector>& closure_factor,\n    const Scalar<DataVector>& comoving_energy_density,\n    const tnsr::i<DataVector, 3>& comoving_momentum_density_spatial,\n    const Scalar<DataVector>& comoving_momentum_density_normal,\n    const tnsr::II<DataVector, 3>& pressure_tensor) {\n  // The closure calculation does a root find to 6 digits.\n  auto closure_approx = Approx::custom().epsilon(1.0e-5).scale(1.0);\n\n  const auto zero = make_with_value<Scalar<DataVector>>(spatial_metric, 0.0);\n  // Lapse and shift don't come into the closure equations, so set\n  // them to one and zero for simplicity.\n  auto spacetime_metric = make_with_value<tnsr::aa<DataVector, 3>>(zero, 0.0);\n  tenex::evaluate<ti::t, ti::t>(make_not_null(&spacetime_metric), -1.0);\n  tenex::evaluate<ti::i, ti::j>(make_not_null(&spacetime_metric),\n                                spatial_metric(ti::i, ti::j));\n  const auto inverse_spacetime_metric =\n      determinant_and_inverse(spacetime_metric).second;\n\n  auto normal_covector = make_with_value<tnsr::a<DataVector, 3>>(zero, 0.0);\n  tenex::evaluate<ti::t>(make_not_null(&normal_covector), -1.0);\n  const auto normal_vector = tenex::evaluate<ti::A>(\n      inverse_spacetime_metric(ti::A, ti::B) * normal_covector(ti::b));\n\n  auto fluid_four_velocity = make_with_value<tnsr::A<DataVector, 3>>(zero, 0.0);\n  {\n    tenex::evaluate<ti::I>(make_not_null(&fluid_four_velocity),\n                           fluid_lorentz_factor() * fluid_velocity(ti::I));\n    const auto three_norm = tenex::evaluate(spacetime_metric(ti::a, ti::b) *\n                                            fluid_four_velocity(ti::A) *\n                                            fluid_four_velocity(ti::B));\n    // Shift = 0\n    tenex::evaluate<ti::T>(\n        make_not_null(&fluid_four_velocity),\n        sqrt(-(three_norm() + 1.0) / spacetime_metric(ti::t, ti::t)));\n  }\n\n  const auto momentum_density4 =\n      add_time_component_from_normal(momentum_density, normal_vector, zero);\n  const auto comoving_momentum_density4 = add_time_component_from_normal(\n      comoving_momentum_density_spatial, normal_vector,\n      comoving_momentum_density_normal);\n\n  // Check comoving momentum density is spatial in the fluid frame\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate(comoving_momentum_density4(ti::a) *\n                      fluid_four_velocity(ti::A)),\n      zero, closure_approx);\n\n  // Consistency of stress-energy tensor in two frames\n  const auto stress_energy_from_eulerian = tenex::evaluate<ti::a, ti::b>(\n      energy_density() * normal_covector(ti::a) * normal_covector(ti::b) +\n      momentum_density4(ti::a) * normal_covector(ti::b) +\n      normal_covector(ti::a) * momentum_density4(ti::b) +\n      spacetime_metric(ti::a, ti::i) * spacetime_metric(ti::b, ti::j) *\n          pressure_tensor(ti::I, ti::J));\n  // We don't have the fluid-frame pressure, so this is missing that\n  // contribution.  We will only check a projection that this pressure\n  // does not contribute to.\n  const auto stress_energy_from_comoving = tenex::evaluate<ti::a, ti::b>(\n      comoving_energy_density() * spacetime_metric(ti::a, ti::c) *\n          fluid_four_velocity(ti::C) * spacetime_metric(ti::b, ti::d) *\n          fluid_four_velocity(ti::D) +\n      comoving_momentum_density4(ti::a) * spacetime_metric(ti::b, ti::d) *\n          fluid_four_velocity(ti::D) +\n      spacetime_metric(ti::a, ti::c) * fluid_four_velocity(ti::C) *\n          comoving_momentum_density4(ti::b));\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate<ti::a>(stress_energy_from_eulerian(ti::a, ti::b) *\n                             fluid_four_velocity(ti::B)),\n      tenex::evaluate<ti::a>(stress_energy_from_comoving(ti::a, ti::b) *\n                             fluid_four_velocity(ti::B)),\n      closure_approx);\n\n  // Check the choice of the closure factor\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate(square(closure_factor()) *\n                      square(comoving_energy_density())),\n      tenex::evaluate(comoving_momentum_density4(ti::a) *\n                      inverse_spacetime_metric(ti::A, ti::B) *\n                      comoving_momentum_density4(ti::b)),\n      closure_approx);\n}\n\nvoid check_limits() {\n  const size_t used_for_size = 5;\n  RadiationTransport::M1Grey::ComputeM1Closure<\n      tmpl::list<neutrinos::ElectronNeutrinos<0>>>\n      closure;\n  // Create variables\n  // Input\n  Scalar<DataVector> energy_density(used_for_size);\n  tnsr::i<DataVector, 3, Frame::Inertial> momentum_density(used_for_size);\n  tnsr::I<DataVector, 3, Frame::Inertial> fluid_velocity(used_for_size);\n  Scalar<DataVector> fluid_lorentz_factor(used_for_size);\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric(used_for_size);\n  // Output\n  Scalar<DataVector> closure_factor(used_for_size);\n  tnsr::II<DataVector, 3, Frame::Inertial> pressure_tensor(used_for_size);\n  Scalar<DataVector> comoving_energy_density(used_for_size);\n  Scalar<DataVector> comoving_momentum_density_normal(used_for_size);\n  tnsr::i<DataVector, 3, Frame::Inertial> comoving_momentum_density_spatial(\n      used_for_size);\n\n  // Accuracy required for closure factor\n  static Approx custom_approx = Approx::custom().epsilon(1.e-5).scale(1.0);\n\n  // Set fluid/metric variables\n  for (size_t m = 0; m < 3; m++) {\n    fluid_velocity.get(m) = 0.1 * m;\n    spatial_metric.get(m, m) = 1. + 0.1 * m * m;\n    for (size_t n = m + 1; n < 3; n++) {\n      spatial_metric.get(m, n) = 0.1 * (m + n);\n    }\n  }\n  const auto det_and_inv = determinant_and_inverse(spatial_metric);\n  const auto& inv_spatial_metric = det_and_inv.second;\n  get(fluid_lorentz_factor) =\n      1. / sqrt(1. - get(dot_product(fluid_velocity, fluid_velocity,\n                                     spatial_metric)));\n\n  // Initialize closure factor (as the input value is used as initial\n  // guess for the root finding algorithm).\n  get(closure_factor) = -1.;\n  // (1) Optically thick limit:\n  Scalar<DataVector> four_thirds_square_fluid_lorentz_factor(\n      4. / 3. * square(get(fluid_lorentz_factor)));\n  get(energy_density) = get(four_thirds_square_fluid_lorentz_factor) - 1. / 3.;\n  for (int m = 0; m < 3; m++) {\n    momentum_density.get(m) = 0.;\n    for (int n = 0; n < 3; n++) {\n      momentum_density.get(m) += get(four_thirds_square_fluid_lorentz_factor) *\n                                 fluid_velocity.get(n) *\n                                 spatial_metric.get(m, n);\n    }\n  }\n  closure.apply(make_not_null(&closure_factor), make_not_null(&pressure_tensor),\n                make_not_null(&comoving_energy_density),\n                make_not_null(&comoving_momentum_density_normal),\n                make_not_null(&comoving_momentum_density_spatial),\n                energy_density, momentum_density, fluid_velocity,\n                fluid_lorentz_factor, spatial_metric, inv_spatial_metric);\n  const DataVector expected_xi0{0.0, 0.0, 0.0, 0.0, 0.0};\n  CHECK_ITERABLE_CUSTOM_APPROX(get(closure_factor), expected_xi0,\n                               custom_approx);\n  check_closure_consistency(\n      spatial_metric, fluid_velocity, fluid_lorentz_factor, energy_density,\n      momentum_density, closure_factor, comoving_energy_density,\n      comoving_momentum_density_spatial, comoving_momentum_density_normal,\n      pressure_tensor);\n\n  // (2) Optically thin limit\n  momentum_density.get(0) = -1.;\n  momentum_density.get(1) = 5.;\n  momentum_density.get(2) = 3.;\n  energy_density = magnitude(momentum_density, inv_spatial_metric);\n  closure.apply(make_not_null(&closure_factor), make_not_null(&pressure_tensor),\n                make_not_null(&comoving_energy_density),\n                make_not_null(&comoving_momentum_density_normal),\n                make_not_null(&comoving_momentum_density_spatial),\n                energy_density, momentum_density, fluid_velocity,\n                fluid_lorentz_factor, spatial_metric, inv_spatial_metric);\n  const DataVector expected_xi1{1.0, 1.0, 1.0, 1.0, 1.0};\n  CHECK_ITERABLE_CUSTOM_APPROX(get(closure_factor), expected_xi1,\n                               custom_approx);\n  check_closure_consistency(\n      spatial_metric, fluid_velocity, fluid_lorentz_factor, energy_density,\n      momentum_density, closure_factor, comoving_energy_density,\n      comoving_momentum_density_spatial, comoving_momentum_density_normal,\n      pressure_tensor);\n}\n\nvoid check_random() {\n  const size_t used_for_size = 5;\n  using closure = RadiationTransport::M1Grey::ComputeM1Closure<\n      tmpl::list<neutrinos::ElectronNeutrinos<0>>>;\n\n  MAKE_GENERATOR(gen);\n\n  std::uniform_real_distribution metric_distribution(-0.1, 0.1);\n  std::uniform_real_distribution momentum_distribution(-10.0, 10.0);\n  std::uniform_real_distribution velocity_magnitude_distribution(0.0, 0.9);\n\n  auto spatial_metric =\n      make_with_random_values<tnsr::ii<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&metric_distribution),\n          used_for_size);\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric.get(i, i) += 1.0;\n  }\n  const auto& inv_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const auto momentum_density =\n      make_with_random_values<tnsr::i<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&momentum_distribution),\n          used_for_size);\n  // We can take shift = 0, and then as momentum_magnitude^0 = 0 the\n  // spacetime norm and the spatial norm are the same.\n  const auto momentum_magnitude =\n      magnitude(momentum_density, inv_spatial_metric);\n  Scalar<DataVector> energy_density(used_for_size);\n  for (size_t i = 0; i < get(energy_density).size(); ++i) {\n    std::uniform_real_distribution energy_distribution(\n        get(momentum_magnitude)[i], 2.0 * get(momentum_magnitude)[i]);\n    get(energy_density)[i] = energy_distribution(gen);\n  }\n\n  // Test with zero fluid velocity (special case in calculation)\n  auto fluid_velocity =\n      make_with_value<tnsr::I<DataVector, 3>>(used_for_size, 0.0);\n  auto fluid_lorentz_factor =\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0);\n\n  // Output\n  Scalar<DataVector> closure_factor(used_for_size);\n  tnsr::II<DataVector, 3, Frame::Inertial> pressure_tensor(used_for_size);\n  Scalar<DataVector> comoving_energy_density(used_for_size);\n  Scalar<DataVector> comoving_momentum_density_normal(used_for_size);\n  tnsr::i<DataVector, 3, Frame::Inertial> comoving_momentum_density_spatial(\n      used_for_size);\n\n  // Initialize closure factor (as the input value is used as initial\n  // guess for the root finding algorithm).\n  get(closure_factor) = -1.;\n\n  closure::apply(make_not_null(&closure_factor),\n                 make_not_null(&pressure_tensor),\n                 make_not_null(&comoving_energy_density),\n                 make_not_null(&comoving_momentum_density_normal),\n                 make_not_null(&comoving_momentum_density_spatial),\n                 energy_density, momentum_density, fluid_velocity,\n                 fluid_lorentz_factor, spatial_metric, inv_spatial_metric);\n  check_closure_consistency(\n      spatial_metric, fluid_velocity, fluid_lorentz_factor, energy_density,\n      momentum_density, closure_factor, comoving_energy_density,\n      comoving_momentum_density_spatial, comoving_momentum_density_normal,\n      pressure_tensor);\n  // The two frames are the same\n  CHECK(energy_density == comoving_energy_density);\n  CHECK(momentum_density == comoving_momentum_density_spatial);\n  // Test continuity near the zero\n  {\n    fluid_velocity.get(0) = 1.0e-6;\n    tenex::evaluate(\n        make_not_null(&fluid_lorentz_factor),\n        1.0 / sqrt(1.0 - fluid_velocity(ti::I) * spatial_metric(ti::i, ti::j) *\n                             fluid_velocity(ti::J)));\n    Approx continuity_approx = Approx::custom().epsilon(1.0e-4).scale(1.0);\n    Scalar<DataVector> closure_factor2(used_for_size);\n    tnsr::II<DataVector, 3, Frame::Inertial> pressure_tensor2(used_for_size);\n    Scalar<DataVector> comoving_energy_density2(used_for_size);\n    Scalar<DataVector> comoving_momentum_density_normal2(used_for_size);\n    tnsr::i<DataVector, 3, Frame::Inertial> comoving_momentum_density_spatial2(\n        used_for_size);\n    get(closure_factor2) = -1.;\n    closure::apply(make_not_null(&closure_factor2),\n                   make_not_null(&pressure_tensor2),\n                   make_not_null(&comoving_energy_density2),\n                   make_not_null(&comoving_momentum_density_normal2),\n                   make_not_null(&comoving_momentum_density_spatial2),\n                   energy_density, momentum_density, fluid_velocity,\n                   fluid_lorentz_factor, spatial_metric, inv_spatial_metric);\n    CHECK_ITERABLE_CUSTOM_APPROX(closure_factor, closure_factor2,\n                                 continuity_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(pressure_tensor, pressure_tensor2,\n                                 continuity_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(comoving_energy_density,\n                                 comoving_energy_density2, continuity_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(comoving_momentum_density_normal,\n                                 comoving_momentum_density_normal2,\n                                 continuity_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(comoving_momentum_density_spatial,\n                                 comoving_momentum_density_spatial2,\n                                 continuity_approx);\n  }\n\n  // Test with a random fluid velocity.  With the random metric, it is\n  // hard to generate a valid velocity in one step, so we choose the\n  // direction and magnitude separately.\n  fill_with_random_values(make_not_null(&fluid_velocity), make_not_null(&gen),\n                          make_not_null(&momentum_distribution));\n  const auto velocity_magnitude = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&gen), make_not_null(&velocity_magnitude_distribution),\n      used_for_size);\n  {\n    const DataVector velocity_scale =\n        get(velocity_magnitude) /\n        get(magnitude(fluid_velocity, spatial_metric));\n    for (size_t i = 0; i < 3; ++i) {\n      fluid_velocity.get(i) *= velocity_scale;\n    }\n  }\n\n  get(fluid_lorentz_factor) = 1.0 / sqrt(1.0 - square(get(velocity_magnitude)));\n\n  closure::apply(make_not_null(&closure_factor),\n                 make_not_null(&pressure_tensor),\n                 make_not_null(&comoving_energy_density),\n                 make_not_null(&comoving_momentum_density_normal),\n                 make_not_null(&comoving_momentum_density_spatial),\n                 energy_density, momentum_density, fluid_velocity,\n                 fluid_lorentz_factor, spatial_metric, inv_spatial_metric);\n  check_closure_consistency(\n      spatial_metric, fluid_velocity, fluid_lorentz_factor, energy_density,\n      momentum_density, closure_factor, comoving_energy_density,\n      comoving_momentum_density_spatial, comoving_momentum_density_normal,\n      pressure_tensor);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Evolution.Systems.RadiationTransport.M1Grey.M1Closure\",\n                  \"[Unit][M1Grey]\") {\n  check_limits();\n  check_random();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Test_M1HydroCoupling.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/M1HydroCoupling.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.RadiationTransport.M1Grey.M1HydroCoupling\",\n                  \"[Unit][M1Grey]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/RadiationTransport/M1Grey\"};\n\n  using neutrino_species = tmpl::list<neutrinos::ElectronNeutrinos<1>>;\n  using CouplingClass =\n      typename RadiationTransport::M1Grey::ComputeM1HydroCoupling<\n          neutrino_species>;\n\n  using CouplingJacobianClass =\n      typename RadiationTransport::M1Grey::ComputeM1HydroCouplingJacobian<\n          neutrino_species>;\n\n  pypp::check_with_random_values<1>(\n      &CouplingClass::apply, \"M1HydroCoupling\",\n      {\"hydro_coupling_tilde_e\", \"hydro_coupling_tilde_s\"}, {{{0.0, 1.0}}},\n      DataVector{5});\n\n  pypp::check_with_random_values<1>(\n      &CouplingJacobianClass::apply, \"ComputeM1HydroCouplingJacobian\",\n      {\"hydro_coupling_jacobian_deriv_e_source_e\",\n       \"hydro_coupling_jacobian_deriv_e_source_s\",\n       \"hydro_coupling_jacobian_deriv_s_source_e\",\n       \"hydro_coupling_jacobian_deriv_s_source_s\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Test_Sources.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Sources.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.RadiationTransport.M1Grey.Sources\", \"[Unit][M1Grey]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/RadiationTransport/M1Grey\"};\n\n  pypp::check_with_random_values<1>(&RadiationTransport::M1Grey::ComputeSources<\n                                        neutrinos::ElectronNeutrinos<0>>::apply,\n                                    \"Sources\",\n                                    {\"source_tilde_e\", \"source_tilde_s\"},\n                                    {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Test_System.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <utility>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/System.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Helpers/Evolution/Imex/TestSector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.RadiationTransport.M1Grey.System.Imex\",\n                  \"[Unit][M1Grey]\") {\n  using DummySpecies = neutrinos::ElectronNeutrinos<1>;\n\n  using system = RadiationTransport::M1Grey::System<tmpl::list<DummySpecies>>;\n  using sector = tmpl::front<system::implicit_sectors>;\n\n  // Metric\n  Scalar<DataVector> lapse{};\n  get(lapse) = DataVector{1.0};\n  tnsr::ii<DataVector, 3> spatial_metric{};\n  get<0, 0>(spatial_metric) = DataVector{3.0};\n  get<0, 1>(spatial_metric) = DataVector{0.0};\n  get<0, 2>(spatial_metric) = DataVector{0.0};\n  get<1, 1>(spatial_metric) = DataVector{4.0};\n  get<1, 2>(spatial_metric) = DataVector{0.0};\n  get<2, 2>(spatial_metric) = DataVector{5.0};\n  tnsr::II<DataVector, 3> inverse_spatial_metric{};\n  Scalar<DataVector> sqrt_det_spatial_metric{};\n  determinant_and_inverse(make_not_null(&sqrt_det_spatial_metric),\n                          make_not_null(&inverse_spatial_metric),\n                          spatial_metric);\n  get(sqrt_det_spatial_metric) = sqrt(get(sqrt_det_spatial_metric));\n\n  // neutrino properties\n  Scalar<DataVector> emissivity{};\n  get(emissivity) = DataVector{2.0};\n  Scalar<DataVector> absorption_opacity{};\n  get(absorption_opacity) = DataVector{4.0};\n  Scalar<DataVector> scattering_opacity{};\n  get(scattering_opacity) = DataVector{3.0};\n  // fluid velocity (nonzero)\n  tnsr::I<DataVector, 3> fluid_velocity{};\n  get<0>(fluid_velocity) = DataVector{0.01};\n  get<1>(fluid_velocity) = DataVector{0.02};\n  get<2>(fluid_velocity) = DataVector{0.03};\n\n  Scalar<DataVector> lorentz_factor{1_st};\n  tenex::evaluate(\n      make_not_null(&lorentz_factor),\n      1.0 / sqrt(1.0 - fluid_velocity(ti::I) * fluid_velocity(ti::J) *\n                           spatial_metric(ti::i, ti::j)));\n\n  tnsr::I<DataVector, 3> fluid_velocity_zero_vel{};\n  get<0>(fluid_velocity_zero_vel) = DataVector{0.0};\n  get<1>(fluid_velocity_zero_vel) = DataVector{0.0};\n  get<2>(fluid_velocity_zero_vel) = DataVector{0.0};\n\n  Scalar<DataVector> lorentz_factor_zero_vel{1_st};\n  tenex::evaluate(make_not_null(&lorentz_factor_zero_vel),\n                  1.0 / sqrt(1.0 - fluid_velocity_zero_vel(ti::I) *\n                                       fluid_velocity_zero_vel(ti::J) *\n                                       spatial_metric(ti::i, ti::j)));\n\n  const double stencil_size = 1.0e-4;\n  const double tolerance = 1.0e-7;\n\n  // has to match tags_from_evolution in System.hpp :: M1Solve\n  const tuples::TaggedTuple<\n      gr::Tags::Lapse<DataVector>,\n      gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>,\n      RadiationTransport::M1Grey::Tags::GreyEmissivity<DummySpecies>,\n      RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<DummySpecies>,\n      RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<DummySpecies>,\n      hydro::Tags::LorentzFactor<DataVector>,\n      hydro::Tags::SpatialVelocity<DataVector, 3>>\n      list_of_values = {lapse,\n                        spatial_metric,\n                        sqrt_det_spatial_metric,\n                        inverse_spatial_metric,\n                        emissivity,\n                        absorption_opacity,\n                        scattering_opacity,\n                        lorentz_factor,\n                        fluid_velocity};\n\n  const tuples::TaggedTuple<\n      gr::Tags::Lapse<DataVector>,\n      gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>,\n      RadiationTransport::M1Grey::Tags::GreyEmissivity<DummySpecies>,\n      RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<DummySpecies>,\n      RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<DummySpecies>,\n      hydro::Tags::LorentzFactor<DataVector>,\n      hydro::Tags::SpatialVelocity<DataVector, 3>>\n      list_of_values_zero_vel = {lapse,\n                                 spatial_metric,\n                                 sqrt_det_spatial_metric,\n                                 inverse_spatial_metric,\n                                 emissivity,\n                                 absorption_opacity,\n                                 scattering_opacity,\n                                 lorentz_factor_zero_vel,\n                                 fluid_velocity_zero_vel};\n\n  using sector_variables_tag = Tags::Variables<sector::tensors>;\n  using SectorVariables = sector_variables_tag::type;\n\n  // note, here, explicit means not implicit, in the time stepper sense\n  SectorVariables explicit_values(1);\n  Scalar<DataVector>& tilde_e_vals = get<\n      RadiationTransport::M1Grey::Tags::TildeE<Frame::Inertial, DummySpecies>>(\n      explicit_values);\n  get(tilde_e_vals) = DataVector{1.001};\n\n  const double momentum_density_optically_thin = 2.0;\n  const double momentum_density_optically_intermediate = 1.0;\n  const double momentum_density_optically_thick = 5.0e-3;\n\n  const std::vector<double> momentum_densities = {\n      momentum_density_optically_thin, momentum_density_optically_intermediate,\n      momentum_density_optically_thick};\n\n  // loop over different momentum densities for each optical regime\n  for (const double momentum_density : momentum_densities) {\n    tnsr::i<DataVector, 3>& tilde_s_vals =\n        get<RadiationTransport::M1Grey::Tags::TildeS<Frame::Inertial,\n                                                     DummySpecies>>(\n            explicit_values);\n    get<0>(tilde_s_vals) = DataVector{0.0};\n    get<1>(tilde_s_vals) = DataVector{momentum_density};\n    get<2>(tilde_s_vals) = DataVector{0.0};\n\n    // zero velocity tests\n    TestHelpers::imex::test_sector<sector>(\n        stencil_size, tolerance, explicit_values, list_of_values_zero_vel);\n\n    // nonzero velocity tests\n    TestHelpers::imex::test_sector<sector>(stencil_size, tolerance,\n                                           explicit_values, list_of_values);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Characteristics.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\n// Test tags used in M1Grey code\nSPECTRE_TEST_CASE(\"Evolution.Systems.RadiationTransport.M1Grey.Tags\",\n                  \"[Unit][M1Grey]\") {\n  // (1) Tags for neutrino species\n  const neutrinos::ElectronNeutrinos<3> nue;\n  const neutrinos::ElectronAntiNeutrinos<3> nua;\n  const neutrinos::HeavyLeptonNeutrinos<3> nux;\n\n  CHECK(neutrinos::get_name(nue) == \"ElectronNeutrinos3\");\n  CHECK(neutrinos::get_name(nua) == \"ElectronAntiNeutrinos3\");\n  CHECK(neutrinos::get_name(nux) == \"HeavyLeptonNeutrinos3\");\n\n  // (2) Tags for evolved variables\n  TestHelpers::db::test_simple_tag<RadiationTransport::M1Grey::Tags::TildeE<\n      Frame::Grid, neutrinos::ElectronNeutrinos<1>>>(\n      \"Grid_TildeE_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<RadiationTransport::M1Grey::Tags::TildeS<\n      Frame::Grid, neutrinos::ElectronAntiNeutrinos<2>>>(\n      \"Grid_TildeS_ElectronAntiNeutrinos2\");\n  TestHelpers::db::test_simple_tag<RadiationTransport::M1Grey::Tags::TildeP<\n      Frame::Grid, neutrinos::ElectronAntiNeutrinos<2>>>(\n      \"Grid_TildeP_ElectronAntiNeutrinos2\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::TildeSVector<Frame::Grid>>(\n      \"Grid_TildeSVector\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::ClosureFactor<\n          neutrinos::ElectronNeutrinos<1>>>(\"ClosureFactor_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::TildeJ<\n          neutrinos::ElectronNeutrinos<1>>>(\"TildeJ_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::TildeHNormal<\n          neutrinos::ElectronNeutrinos<1>>>(\"TildeHNormal_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::TildeHSpatial<\n          Frame::Grid, neutrinos::ElectronNeutrinos<1>>>(\n      \"Grid_TildeHSpatial_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::GreyEmissivity<\n          neutrinos::ElectronNeutrinos<1>>>(\n      \"GreyEmissivity_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<\n          neutrinos::ElectronNeutrinos<1>>>(\n      \"GreyAbsorptionOpacity_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<\n          neutrinos::ElectronNeutrinos<1>>>(\n      \"GreyScatteringOpacity_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::M1HydroCouplingNormal<\n          neutrinos::ElectronNeutrinos<1>>>(\n      \"M1HydroCouplingNormal_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::M1HydroCouplingSpatial<\n          Frame::Grid, neutrinos::ElectronNeutrinos<1>>>(\n      \"Grid_M1HydroCouplingSpatial_ElectronNeutrinos1\");\n  TestHelpers::db::test_simple_tag<\n      RadiationTransport::M1Grey::Tags::CharacteristicSpeeds>(\n      \"CharacteristicSpeeds\");\n  TestHelpers::db::test_compute_tag<\n      RadiationTransport::M1Grey::Tags::CharacteristicSpeedsCompute>(\n      \"CharacteristicSpeeds\");\n\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/Test_TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/TimeDerivativeTerms.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nnamespace {\nvoid forward_to_time_deriv(\n    const gsl::not_null<Scalar<DataVector>*> non_flux_terms_dt_tilde_e,\n    const gsl::not_null<tnsr::i<DataVector, 3, Frame::Inertial>*>\n        non_flux_terms_dt_tilde_s,\n\n    const gsl::not_null<tnsr::I<DataVector, 3, Frame::Inertial>*> tilde_e_flux,\n    const gsl::not_null<tnsr::Ij<DataVector, 3, Frame::Inertial>*> tilde_s_flux,\n\n    const Scalar<DataVector>& tilde_e,\n    const tnsr::i<DataVector, 3, Frame::Inertial>& tilde_s,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& tilde_p,\n    const Scalar<DataVector>& lapse,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& shift,\n    const tnsr::ii<DataVector, 3, Frame::Inertial>& spatial_metric,\n    const tnsr::II<DataVector, 3, Frame::Inertial>& inv_spatial_metric,\n    const tnsr::i<DataVector, 3>& d_lapse,\n    const tnsr::iJ<DataVector, 3>& d_shift,\n    const tnsr::ijj<DataVector, 3>& d_spatial_metric,\n    const tnsr::ii<DataVector, 3>& extrinsic_curvature,\n    const Scalar<DataVector>& emissivity,\n    const Scalar<DataVector>& absorption_opacity,\n    const Scalar<DataVector>& scattering_opacity,\n    const Scalar<DataVector>& tilde_j, const Scalar<DataVector>& tilde_h_normal,\n    const tnsr::i<DataVector, 3>& tilde_h_spatial,\n    const tnsr::I<DataVector, 3>& spatial_velocity,\n    const Scalar<DataVector>& lorentz,\n    const Scalar<DataVector>& sqrt_det_spatial_metric) {\n  Variables<tmpl::list<typename RadiationTransport::M1Grey::TimeDerivativeTerms<\n                           neutrinos::ElectronNeutrinos<1>>::TildeSUp,\n                       gr::Tags::InverseSpatialMetric<DataVector, 3>>>\n      temp(get(lapse).size());\n\n  RadiationTransport::M1Grey::\n      TimeDerivativeTerms<neutrinos::ElectronNeutrinos<1>>::apply(\n          non_flux_terms_dt_tilde_e, non_flux_terms_dt_tilde_s, tilde_e_flux,\n          tilde_s_flux,\n\n          make_not_null(\n              &get<typename RadiationTransport::M1Grey::TimeDerivativeTerms<\n                  neutrinos::ElectronNeutrinos<1>>::TildeSUp>(temp)),\n          make_not_null(\n              &get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp)),\n\n          tilde_e, tilde_s, tilde_p, lapse, shift, spatial_metric,\n          inv_spatial_metric, d_lapse, d_shift, d_spatial_metric,\n          extrinsic_curvature, emissivity, absorption_opacity,\n          scattering_opacity, tilde_j, tilde_h_normal, tilde_h_spatial,\n          spatial_velocity, lorentz, sqrt_det_spatial_metric);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.RadiationTransport.M1Grey.TimeDerivativeTerms\",\n                  \"[Unit][M1Grey]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/RadiationTransport/M1Grey\"};\n\n  pypp::check_with_random_values<1>(\n      &forward_to_time_deriv, \"TimeDerivative\",\n      {\"non_flux_terms_dt_tilde_e\", \"non_flux_terms_dt_tilde_s\", \"tilde_e_flux\",\n       \"tilde_s_flux\"},\n      {{{0.0, 1.0}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/TimeDerivative.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Fluxes\nimport Sources\n\n\ndef non_flux_terms_dt_tilde_e(\n    tilde_e,\n    tilde_s,\n    tilde_p,\n    lapse,\n    shift,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    extrinsic_curvature,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    tilde_j,\n    tilde_h_normal,\n    tilde_h_spatial,\n    spatial_velocity,\n    lorentz,\n    sqrt_det_spatial_metric,\n):\n    return Sources.source_tilde_e(\n        tilde_e,\n        tilde_s,\n        tilde_p,\n        lapse,\n        d_lapse,\n        d_shift,\n        d_spatial_metric,\n        inv_spatial_metric,\n        extrinsic_curvature,\n        spatial_metric,\n        emissivity,\n        absorption_opacity,\n        scattering_opacity,\n        tilde_j,\n        tilde_h_normal,\n        tilde_h_spatial,\n        spatial_velocity,\n        lorentz,\n        sqrt_det_spatial_metric,\n    )\n\n\ndef non_flux_terms_dt_tilde_s(\n    tilde_e,\n    tilde_s,\n    tilde_p,\n    lapse,\n    shift,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    extrinsic_curvature,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    tilde_j,\n    tilde_h_normal,\n    tilde_h_spatial,\n    spatial_velocity,\n    lorentz,\n    sqrt_det_spatial_metric,\n):\n    return Sources.source_tilde_s(\n        tilde_e,\n        tilde_s,\n        tilde_p,\n        lapse,\n        d_lapse,\n        d_shift,\n        d_spatial_metric,\n        inv_spatial_metric,\n        extrinsic_curvature,\n        spatial_metric,\n        emissivity,\n        absorption_opacity,\n        scattering_opacity,\n        tilde_j,\n        tilde_h_normal,\n        tilde_h_spatial,\n        spatial_velocity,\n        lorentz,\n        sqrt_det_spatial_metric,\n    )\n\n\ndef tilde_e_flux(\n    tilde_e,\n    tilde_s,\n    tilde_p,\n    lapse,\n    shift,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    extrinsic_curvature,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    tilde_j,\n    tilde_h_normal,\n    tilde_h_spatial,\n    spatial_velocity,\n    lorentz,\n    sqrt_det_spatial_metric,\n):\n    return Fluxes.tilde_e_flux(\n        tilde_e,\n        tilde_s,\n        tilde_p,\n        lapse,\n        shift,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n\n\ndef tilde_s_flux(\n    tilde_e,\n    tilde_s,\n    tilde_p,\n    lapse,\n    shift,\n    spatial_metric,\n    inv_spatial_metric,\n    d_lapse,\n    d_shift,\n    d_spatial_metric,\n    extrinsic_curvature,\n    emissivity,\n    absorption_opacity,\n    scattering_opacity,\n    tilde_j,\n    tilde_h_normal,\n    tilde_h_spatial,\n    spatial_velocity,\n    lorentz,\n    sqrt_det_spatial_metric,\n):\n    return Fluxes.tilde_s_flux(\n        tilde_e,\n        tilde_s,\n        tilde_p,\n        lapse,\n        shift,\n        spatial_metric,\n        inv_spatial_metric,\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/M1Grey/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/RadiationTransport/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/BoundaryConditions/Test_Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryConditions/Factory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  helpers::test_periodic_condition<\n      domain::BoundaryConditions::Periodic<\n          ScalarAdvection::BoundaryConditions::BoundaryCondition<Dim>>,\n      ScalarAdvection::BoundaryConditions::BoundaryCondition<Dim>>(\n      \"Periodic:\\n\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarAdvection.BoundaryConditions.Periodic\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/BoundaryCorrections/Rusanov.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    u,\n    flux_u,\n    velocity_field,\n    normal_covector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n):\n    normal_dot_velocity = np.einsum(\"i,i\", velocity_field, normal_covector)\n    return (\n        u,\n        np.asarray(np.einsum(\"i,i\", normal_covector, flux_u)),\n        np.asarray(\n            np.abs(normal_dot_velocity)\n            if normal_dot_mesh_velocity is None\n            else np.abs(normal_dot_velocity - normal_dot_mesh_velocity)\n        ),\n    )\n\n\ndef dg_boundary_terms(\n    interior_u,\n    interior_normal_dot_flux_u,\n    interior_abs_char_speed,\n    exterior_u,\n    exterior_normal_dot_flux_u,\n    exterior_abs_char_speed,\n    use_strong_form,\n):\n    if use_strong_form:\n        return (\n            np.asarray(\n                -0.5 * (interior_normal_dot_flux_u + exterior_normal_dot_flux_u)\n                - 0.5\n                * np.maximum(interior_abs_char_speed, exterior_abs_char_speed)\n                * (exterior_u - interior_u)\n            ),\n        )\n    else:\n        return (\n            np.asarray(\n                0.5 * (interior_normal_dot_flux_u - exterior_normal_dot_flux_u)\n                - 0.5\n                * np.maximum(interior_abs_char_speed, exterior_abs_char_speed)\n                * (exterior_u - interior_u)\n            ),\n        )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/BoundaryCorrections/Test_Rusanov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryCorrections/Rusanov.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test_rusanov(const gsl::not_null<std::mt19937*> gen,\n                  const size_t num_pts) {\n  helpers::test_boundary_correction_conservation<ScalarAdvection::System<Dim>>(\n      gen, ScalarAdvection::BoundaryCorrections::Rusanov<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n\n  helpers::test_boundary_correction_with_python<ScalarAdvection::System<Dim>>(\n      gen, \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      ScalarAdvection::BoundaryCorrections::Rusanov<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n\n  const auto rusanov = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      ScalarAdvection::BoundaryCorrections::Rusanov<Dim>>(\"Rusanov:\");\n\n  helpers::test_boundary_correction_with_python<ScalarAdvection::System<Dim>>(\n      gen, \"Rusanov\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const ScalarAdvection::BoundaryCorrections::Rusanov<Dim>&>(\n          *rusanov),\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarAdvection.BoundaryCorrections.Rusanov\",\n                  \"[Unit][Evolution]\") {\n  PUPable_reg(ScalarAdvection::BoundaryCorrections::Rusanov<1>);\n  PUPable_reg(ScalarAdvection::BoundaryCorrections::Rusanov<2>);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarAdvection/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  test_rusanov<1>(make_not_null(&gen), 5);\n  test_rusanov<2>(make_not_null(&gen), 5);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Test_ScalarAdvection)\n\nset(LIBRARY_SOURCES\n  BoundaryConditions/Test_Periodic.cpp\n  BoundaryCorrections/Test_Rusanov.cpp\n  FiniteDifference/Test_AoWeno.cpp\n  FiniteDifference/Test_MonotonisedCentral.cpp\n  FiniteDifference/Test_Tag.cpp\n  Subcell/Test_ComputeFluxes.cpp\n  Subcell/Test_GhostData.cpp\n  Subcell/Test_NeighborPackagedData.cpp\n  Subcell/Test_SetInitialRdmpData.cpp\n  Subcell/Test_TciOnDgGrid.cpp\n  Subcell/Test_TciOnFdGrid.cpp\n  Subcell/Test_TciOptions.cpp\n  Subcell/Test_TimeDerivative.cpp\n  Subcell/Test_VelocityAtFace.cpp\n  Test_Characteristics.cpp\n  Test_Fluxes.cpp\n  Test_Tags.cpp\n  Test_TimeDerivativeTerms.cpp\n  Test_VelocityField.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ScalarAdvection\n  DataStructures\n  Domain\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/FiniteDifference/Test_AoWeno.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/ScalarAdvection/FiniteDifference/TestHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  // test creation\n  const auto aoweno = ScalarAdvection::fd::AoWeno53<Dim>(0.85, 0.8, 1e-12, 8);\n  CHECK(aoweno ==\n        TestHelpers::test_creation<ScalarAdvection::fd::AoWeno53<Dim>>(\n            \"GammaHi: 0.85\\n\"\n            \"GammaLo: 0.8\\n\"\n            \"Epsilon: 1.0e-12\\n\"\n            \"NonlinearWeightExponent: 8\\n\"));\n\n  // test operators\n  CHECK(aoweno == ScalarAdvection::fd::AoWeno53<Dim>(0.85, 0.8, 1.0e-12, 8));\n  CHECK(aoweno != ScalarAdvection::fd::AoWeno53<Dim>(0.8, 0.8, 1.0e-12, 8));\n  CHECK(aoweno != ScalarAdvection::fd::AoWeno53<Dim>(0.85, 0.85, 1.0e-12, 8));\n  CHECK(aoweno != ScalarAdvection::fd::AoWeno53<Dim>(0.85, 0.8, 2.0e-12, 8));\n  CHECK(aoweno != ScalarAdvection::fd::AoWeno53<Dim>(0.85, 0.8, 1.0e-12, 6));\n\n  // test serialization\n  test_serialization(aoweno);\n\n  // test derived\n  const auto aoweno_from_options_base = TestHelpers::test_factory_creation<\n      ScalarAdvection::fd::Reconstructor<Dim>,\n      ScalarAdvection::fd::OptionTags::Reconstructor<Dim>>(\n      \"AoWeno53:\\n\"\n      \"  GammaHi: 0.85\\n\"\n      \"  GammaLo: 0.8\\n\"\n      \"  Epsilon: 1.0e-12\\n\"\n      \"  NonlinearWeightExponent: 8\\n\");\n  auto* const aoweno_from_options =\n      dynamic_cast<const ScalarAdvection::fd::AoWeno53<Dim>*>(\n          aoweno_from_options_base.get());\n  REQUIRE(aoweno_from_options != nullptr);\n  CHECK(*aoweno_from_options == aoweno);\n\n  // test reconstruction\n  const size_t num_pts_per_dimension = 5;\n  TestHelpers::ScalarAdvection::fd::test_reconstructor<Dim>(\n      num_pts_per_dimension, aoweno);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.Fd.AoWeno53\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/FiniteDifference/Test_MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/Systems/ScalarAdvection/FiniteDifference/TestHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_create() {\n  const auto mc =\n      TestHelpers::test_creation<ScalarAdvection::fd::MonotonisedCentral<Dim>>(\n          \"\");\n  CHECK(mc == ScalarAdvection::fd::MonotonisedCentral<Dim>());\n}\n\ntemplate <size_t Dim>\nvoid test_serialize() {\n  ScalarAdvection::fd::MonotonisedCentral<Dim> mc;\n  test_serialization(mc);\n}\n\ntemplate <size_t Dim>\nvoid test_move() {\n  ScalarAdvection::fd::MonotonisedCentral<Dim> mc;\n  ScalarAdvection::fd::MonotonisedCentral<Dim> mc_copy;\n  test_move_semantics(std::move(mc), mc_copy);  //  NOLINT\n}\n\ntemplate <size_t Dim>\nvoid test_derived() {\n  const ScalarAdvection::fd::MonotonisedCentral<Dim> mc_recons{};\n  const auto mc_from_options_base = TestHelpers::test_factory_creation<\n      ScalarAdvection::fd::Reconstructor<Dim>,\n      ScalarAdvection::fd::OptionTags::Reconstructor<Dim>>(\n      \"MonotonisedCentral:\\n\");\n  auto* const mc_from_options =\n      dynamic_cast<const ScalarAdvection::fd::MonotonisedCentral<Dim>*>(\n          mc_from_options_base.get());\n  REQUIRE(mc_from_options != nullptr);\n  CHECK(*mc_from_options == mc_recons);\n}\n\ntemplate <size_t Dim>\nvoid test_mc() {\n  const ScalarAdvection::fd::MonotonisedCentral<Dim> mc_recons{};\n  const size_t num_pts_per_dimension = 5;\n  TestHelpers::ScalarAdvection::fd::test_reconstructor<Dim>(\n      num_pts_per_dimension, mc_recons);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ScalarAdvection.Fd.MonotonisedCentral\",\n    \"[Unit][Evolution]\") {\n  test_create<1>();\n  test_create<2>();\n  test_serialize<1>();\n  test_serialize<2>();\n  test_move<1>();\n  test_move<2>();\n  test_derived<1>();\n  test_derived<2>();\n\n  test_mc<1>();\n  test_mc<2>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/FiniteDifference/Test_Tag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  TestHelpers::db::test_simple_tag<\n      ScalarAdvection::fd::Tags::Reconstructor<Dim>>(\"Reconstructor\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.Fd.Tag\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Fluxes.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef compute_flux(u, velocity_field):\n    return u * velocity_field\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_ComputeFluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Fluxes.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection {\nnamespace {\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  using argument_tags = typename Fluxes<Dim>::argument_tags;\n  using return_tags = typename Fluxes<Dim>::return_tags;\n\n  // generate random vars\n  const size_t num_pts = 5;\n  auto random_vars = make_with_random_values<\n      Variables<tmpl::append<return_tags, argument_tags>>>(gen, dist, num_pts);\n\n  // store computed fluxes into return_tags portion of `random_vars`\n  subcell::compute_fluxes<Dim>(make_not_null(&random_vars));\n\n  // compute expected fluxes\n  Variables<return_tags> expected_flux{num_pts};\n  Fluxes<Dim>::apply(\n      make_not_null(\n          &get<::Tags::Flux<Tags::U, tmpl::size_t<Dim>, Frame::Inertial>>(\n              expected_flux)),\n      get<Tags::U>(random_vars), get<Tags::VelocityField<Dim>>(random_vars));\n\n  // check result\n  CHECK_ITERABLE_APPROX(\n      (get<::Tags::Flux<Tags::U, tmpl::size_t<Dim>, Frame::Inertial>>(\n          random_vars)),\n      (get<::Tags::Flux<Tags::U, tmpl::size_t<Dim>, Frame::Inertial>>(\n          expected_flux)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ScalarAdvection.Subcell.ComputeFluxes\",\n    \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n\n  test<1>(make_not_null(&gen), make_not_null(&dist));\n  test<2>(make_not_null(&gen), make_not_null(&dist));\n}\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_GhostData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/GhostData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nconst size_t num_of_pts_dg_grid = 4;\n\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist) {\n  // make random U on DG and subcell mesh\n  const Mesh<Dim> dg_mesh{num_of_pts_dg_grid, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const auto random_vars_dg =\n      make_with_random_values<Variables<tmpl::list<ScalarAdvection::Tags::U>>>(\n          gen, dist, dg_mesh.number_of_grid_points());\n  const auto random_vars_subcell =\n      make_with_random_values<Variables<tmpl::list<ScalarAdvection::Tags::U>>>(\n          gen, dist, subcell_mesh.number_of_grid_points());\n\n  // add the random U on the subcell mesh to a databox, apply\n  // GhostDataOnSubcells and compare with the returned vector\n  auto box_subcell = db::create<db::AddSimpleTags<\n      ::Tags::Variables<tmpl::list<ScalarAdvection::Tags::U>>>>(\n      random_vars_subcell);\n  DataVector retrieved_vars_subcell =\n      db::mutate_apply<ScalarAdvection::subcell::GhostVariables>(\n          make_not_null(&box_subcell), 2_st);\n  REQUIRE(retrieved_vars_subcell.size() ==\n          subcell_mesh.number_of_grid_points() + 2);\n  CHECK_ITERABLE_APPROX(get(get<ScalarAdvection::Tags::U>(random_vars_subcell)),\n                        DataVector(retrieved_vars_subcell.data(),\n                                   retrieved_vars_subcell.size() - 2));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.Subcell.GhostData\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n\n  test<1>(make_not_null(&gen), make_not_null(&dist));\n  test<2>(make_not_null(&gen), make_not_null(&dist));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_NeighborPackagedData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/DgSubcell/ReconstructionMethod.hpp\"\n#include \"Evolution/DgSubcell/SubcellOptions.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DgSubcell/Tags/SubcellOptions.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/PackageDataImpl.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/NormalVectorTags.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Fluxes.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/ComputeFluxes.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/NeighborPackagedData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/VelocityAtFace.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/ScalarAdvection/FiniteDifference/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection {\nnamespace {\ntemplate <size_t Dim>\nauto compute_face_tensor() {}\n\ntemplate <size_t Dim>\nvoid test_neighbor_packaged_data(const size_t num_dg_pts_per_dimension,\n                                 const gsl::not_null<std::mt19937*> gen) {\n  // 1. create random U on an element and its neighbor elements\n  // 2. send through reconstruction and compute FD fluxes on mortars\n  // 3. feed argument variables of dg_package_data() function to\n  //    the NeighborPackagedData struct and retrieve the packaged data\n  // 4. check if it agrees with the expected value\n\n  using evolved_vars_tags = typename System<Dim>::variables_tag::tags_list;\n  using fluxes_tags = typename Fluxes<Dim>::return_tags;\n\n  using velocity_field = Tags::VelocityField<Dim>;\n  using subcell_velocity_field =\n      evolution::dg::subcell::Tags::Inactive<velocity_field>;\n  using subcell_faces_velocity_field =\n      evolution::dg::subcell::Tags::OnSubcellFaces<velocity_field, Dim>;\n\n  // Perform test with MC reconstruction & Rusanov riemann solver\n  using ReconstructionForTest = typename fd::MonotonisedCentral<Dim>;\n  using BoundaryCorrectionForTest = typename BoundaryCorrections::Rusanov<Dim>;\n\n  // create an element and its neighbor elements\n  DirectionMap<Dim, Neighbors<Dim>> element_neighbors{};\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    element_neighbors[gsl::at(Direction<Dim>::all_directions(), i)] =\n        Neighbors<Dim>{{ElementId<Dim>{i + 1, {}}},\n                       OrientationMap<Dim>::create_aligned()};\n  }\n  const Element<Dim> element{ElementId<Dim>{0, {}}, element_neighbors};\n\n  // generate random U on the dg mesh and project it to subcell mesh\n  const Mesh<Dim> dg_mesh{num_dg_pts_per_dimension, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  Variables<evolved_vars_tags> volume_vars_dg{dg_mesh.number_of_grid_points()};\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  fill_with_random_values(make_not_null(&volume_vars_dg), gen,\n                          make_not_null(&dist));\n  Variables<evolved_vars_tags> volume_vars_subcell =\n      evolution::dg::subcell::fd::project(volume_vars_dg, dg_mesh,\n                                          subcell_mesh.extents());\n\n  // below are required for calling ScalarAdvection::VelocityAtFace::apply()\n  // function to compute velocity field on interfaces\n  const double time{0.0};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  // generate (random) ghost data from neighbor\n  auto logical_coords_subcell = logical_coordinates(subcell_mesh);\n  const ReconstructionForTest reconstructor{};\n  const auto compute_random_variable = [&gen, &dist](const auto& coords) {\n    Variables<evolved_vars_tags> vars{get<0>(coords).size(), 0.0};\n    fill_with_random_values(make_not_null(&vars), gen, make_not_null(&dist));\n    return vars;\n  };\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>::type\n      ghost_data = TestHelpers::ScalarAdvection::fd::compute_ghost_data(\n          subcell_mesh, logical_coords_subcell, element.neighbors(),\n          reconstructor.ghost_zone_size(), compute_random_variable);\n\n  DirectionMap<Dim, std::optional<Variables<\n                        tmpl::list<evolution::dg::Tags::MagnitudeOfNormal,\n                                   evolution::dg::Tags::NormalCovector<Dim>>>>>\n      normal_vectors{};\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    const auto coordinate_map =\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<Dim>{});\n    const auto moving_mesh_map =\n        domain::make_coordinate_map<Frame::Grid, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<Dim>{});\n\n    const Mesh<Dim - 1> face_mesh = dg_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    std::unordered_map<Direction<Dim>,\n                       tnsr::i<DataVector, Dim, Frame::Inertial>>\n        unnormalized_normal_covectors{};\n    tnsr::i<DataVector, Dim, Frame::Inertial> unnormalized_covector{};\n    for (size_t i = 0; i < Dim; ++i) {\n      unnormalized_covector.get(i) =\n          coordinate_map.inv_jacobian(face_logical_coords)\n              .get(direction.dimension(), i);\n    }\n    unnormalized_normal_covectors[direction] = unnormalized_covector;\n    Variables<tmpl::list<\n        evolution::dg::Actions::detail::NormalVector<Dim>,\n        evolution::dg::Actions::detail::OneOverNormalVectorMagnitude>>\n        fields_on_face{face_mesh.number_of_grid_points()};\n\n    normal_vectors[direction] = std::nullopt;\n    evolution::dg::Actions::detail::\n        unit_normal_vector_and_covector_and_magnitude<System<Dim>>(\n            make_not_null(&normal_vectors), make_not_null(&fields_on_face),\n            direction, unnormalized_normal_covectors, moving_mesh_map);\n  }\n\n  auto box = db::create<db::AddSimpleTags<\n      domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      typename System<Dim>::variables_tag,\n      evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n      fd::Tags::Reconstructor<Dim>, evolution::Tags::BoundaryCorrection,\n      ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::ElementMap<Dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                  Frame::Inertial>,\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      subcell_velocity_field, subcell_faces_velocity_field,\n      domain::Tags::MeshVelocity<Dim>,\n      evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>,\n      evolution::dg::subcell::Tags::SubcellOptions<Dim>>>(\n      element, dg_mesh, subcell_mesh, volume_vars_dg, ghost_data,\n      std::unique_ptr<fd::Reconstructor<Dim>>{\n          std::make_unique<ReconstructionForTest>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<BoundaryCorrectionForTest>()},\n      time, clone_unique_ptrs(functions_of_time),\n      ElementMap<Dim, Frame::Grid>{\n          ElementId<Dim>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<Dim>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{}),\n      logical_coords_subcell, typename subcell_velocity_field::type{},\n      std::array<typename subcell_faces_velocity_field::type::value_type,\n                 Dim>{},\n      std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>{},\n      normal_vectors,\n      evolution::dg::subcell::SubcellOptions{\n          4.0, 1_st, 1.0e-3, 1.0e-4, false, false,\n          evolution::dg::subcell::fd::ReconstructionMethod::DimByDim, false,\n          std::nullopt, ::fd::DerivativeOrder::Two, 1, 1, 1});\n\n  // Compute face-centered velocity field and add it to the box. This action\n  // needs to be called in prior since NeighborPackagedData::apply() internally\n  // retrieves face-centered values of velocity field when computing fluxes.\n  db::mutate_apply<ScalarAdvection::subcell::VelocityAtFace<Dim>>(\n      make_not_null(&box));\n\n  // Compute the packaged data\n  std::vector<DirectionalId<Dim>> mortars_to_reconstruct_to{};\n  for (const auto& [direction, neighbors] : element.neighbors()) {\n    mortars_to_reconstruct_to.emplace_back(\n        DirectionalId<Dim>{direction, *neighbors.begin()});\n  }\n  const auto packaged_data =\n      subcell::NeighborPackagedData::apply(box, mortars_to_reconstruct_to);\n\n  // Now for each directions, check that the packaged_data agrees with expected\n  // values\n  using dg_package_field_tags =\n      typename BoundaryCorrectionForTest ::dg_package_field_tags;\n  using dg_package_data_argument_tags = tmpl::append<\n      evolved_vars_tags, fluxes_tags,\n      typename BoundaryCorrectionForTest ::dg_package_data_temporary_tags>;\n\n  BoundaryCorrectionForTest boundary_corr_for_test{};\n\n  Variables<dg_package_data_argument_tags> vars_on_mortar_face{0};\n  Variables<dg_package_field_tags> expected_fd_packaged_data_on_mortar{0};\n\n  for (const auto& mortar_id : mortars_to_reconstruct_to) {\n    const Direction<Dim>& direction = mortar_id.direction();\n    Index<Dim> extents = subcell_mesh.extents();\n\n    if constexpr (Dim == 1) {\n      // Note : 1D mortar has only one face point\n      vars_on_mortar_face.initialize(1);\n      expected_fd_packaged_data_on_mortar.initialize(1);\n    } else {\n      ++extents[direction.dimension()];\n      const size_t num_mortar_face_pts =\n          subcell_mesh.extents().slice_away(direction.dimension()).product();\n      vars_on_mortar_face.initialize(num_mortar_face_pts);\n      expected_fd_packaged_data_on_mortar.initialize(num_mortar_face_pts);\n    }\n\n    // reconstruct U on the mortar\n    dynamic_cast<const ReconstructionForTest&>(reconstructor)\n        .reconstruct_fd_neighbor(make_not_null(&vars_on_mortar_face),\n                                 volume_vars_subcell, element, ghost_data,\n                                 subcell_mesh, direction);\n\n    // retrieve face-centered velocity field and slice it on the mortar, then\n    // compute fluxes with it\n    const auto& velocity_field_on_mortar = db::get<\n        evolution::dg::subcell::Tags::OnSubcellFaces<velocity_field, Dim>>(box);\n    data_on_slice(make_not_null(&get<velocity_field>(vars_on_mortar_face)),\n                  gsl::at(velocity_field_on_mortar, direction.dimension()),\n                  extents, direction.dimension(),\n                  direction.side() == Side::Lower\n                      ? 0\n                      : extents[direction.dimension()] - 1);\n    ScalarAdvection::subcell::compute_fluxes<Dim>(\n        make_not_null(&vars_on_mortar_face));\n\n    // reverse normal vector\n    auto normal_covector = get<evolution::dg::Tags::NormalCovector<Dim>>(\n        *db::get<evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>>(box).at(\n            mortar_id.direction()));\n    for (auto& t : normal_covector) {\n      t *= -1.0;\n    }\n\n    // Note : need to do the projection in 2d\n    if constexpr (Dim > 1) {\n      const auto dg_normal_covector = normal_covector;\n      for (size_t i = 0; i < Dim; ++i) {\n        normal_covector.get(i) = evolution::dg::subcell::fd::project(\n            dg_normal_covector.get(i),\n            dg_mesh.slice_away(mortar_id.direction().dimension()),\n            subcell_mesh.extents().slice_away(\n                mortar_id.direction().dimension()));\n      }\n    }\n\n    // Compute the expected packaged data\n    evolution::dg::Actions::detail::dg_package_data<System<Dim>>(\n        make_not_null(&expected_fd_packaged_data_on_mortar),\n        boundary_corr_for_test, vars_on_mortar_face, normal_covector,\n        {std::nullopt}, box,\n        typename BoundaryCorrectionForTest ::dg_package_data_volume_tags{},\n        dg_package_data_argument_tags{});\n\n    if constexpr (Dim == 1) {\n      // no need to reconstruct back to DG grid for 1D\n      const DataVector vector_to_check{\n          expected_fd_packaged_data_on_mortar.data(),\n          expected_fd_packaged_data_on_mortar.size()};\n\n      CHECK_ITERABLE_APPROX(vector_to_check, packaged_data.at(mortar_id));\n    } else {\n      // reconstruct from FD to DG grid for 2d\n      const auto expected_dg_packaged_data =\n          evolution::dg::subcell::fd::reconstruct(\n              expected_fd_packaged_data_on_mortar,\n              dg_mesh.slice_away(mortar_id.direction().dimension()),\n              subcell_mesh.extents().slice_away(\n                  mortar_id.direction().dimension()),\n              evolution::dg::subcell::fd::ReconstructionMethod::AllDimsAtOnce);\n\n      const DataVector vector_to_check{\n          // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n          const_cast<double*>(expected_dg_packaged_data.data()),\n          expected_dg_packaged_data.size()};\n\n      CHECK_ITERABLE_APPROX(vector_to_check, packaged_data.at(mortar_id));\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ScalarAdvection.Subcell.NeighborPackagedData\",\n    \"[Unit][Evolution]\") {\n  const size_t num_dg_pts_per_dimension = 5;\n  MAKE_GENERATOR(gen);\n\n  test_neighbor_packaged_data<1>(num_dg_pts_per_dimension, make_not_null(&gen));\n  test_neighbor_packaged_data<2>(num_dg_pts_per_dimension, make_not_null(&gen));\n}\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_SetInitialRdmpData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/SetInitialRdmpData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  // type aliases\n  using Vars = Variables<tmpl::list<ScalarAdvection::Tags::U>>;\n\n  // create DG mesh and subcell mesh for test\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  const size_t number_of_dg_grid_points{dg_mesh.number_of_grid_points()};\n\n  // create scalar field U for DG mesh\n  Vars dg_vars{number_of_dg_grid_points, 1.0};\n\n  // While the code is supposed to be used on the subcells, that doesn't\n  // actually matter.\n  const auto& dg_u = get<ScalarAdvection::Tags::U>(dg_vars);\n  using std::max;\n  using std::min;\n  const auto subcell_u = evolution::dg::subcell::fd::project(\n      get(dg_u), dg_mesh, subcell_mesh.extents());\n  evolution::dg::subcell::RdmpTciData rdmp_data{};\n  ScalarAdvection::subcell::SetInitialRdmpData<Dim>::apply(\n      make_not_null(&rdmp_data), dg_u, evolution::dg::subcell::ActiveGrid::Dg,\n      dg_mesh, subcell_mesh);\n  const evolution::dg::subcell::RdmpTciData expected_dg_rdmp_data{\n      {max(max(get(dg_u), max(subcell_u)))},\n      {min(min(get(dg_u)), min(subcell_u))}};\n  CHECK(rdmp_data == expected_dg_rdmp_data);\n\n  ScalarAdvection::subcell::SetInitialRdmpData<Dim>::apply(\n      make_not_null(&rdmp_data), dg_u,\n      evolution::dg::subcell::ActiveGrid::Subcell, dg_mesh, subcell_mesh);\n  const evolution::dg::subcell::RdmpTciData expected_rdmp_data{\n      {max(get(dg_u))}, {min(get(dg_u))}};\n  CHECK(rdmp_data == expected_rdmp_data);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ScalarAdvection.Subcell.SetInitialRdmpData\",\n    \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_TciOnDgGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Projection.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOnDgGrid.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\n// test cases to be covered\nenum class TestThis { AllGood, PerssonU, BelowCutoff, RdmpU };\n\ntemplate <size_t Dim>\nvoid test(const TestThis& test_this) {\n  // create DG mesh\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  // create scalar field U on the DG mesh\n  const size_t number_of_points{dg_mesh.number_of_grid_points()};\n  Scalar<DataVector> u{number_of_points, 1.0};\n\n  const ScalarAdvection::subcell::TciOptions tci_options{1.0e-8};\n\n  if (test_this == TestThis::PerssonU) {\n    // make a troubled cell\n    get(u)[number_of_points / 2] += 1.0;\n  }\n  if (test_this == TestThis::BelowCutoff) {\n    // make a troubled cell, but scale it to be smaller than the absolute cutoff\n    get(u)[number_of_points / 2] += 1.0;\n    get(u) *= 1.0e-10;\n  }\n\n  // Set the RDMP TCI past data.\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{\n      {max(max(get(u)), max(evolution::dg::subcell::fd::project(\n                            get(u), dg_mesh, subcell_mesh.extents())))},\n      {min(min(get(u)), min(evolution::dg::subcell::fd::project(\n                            get(u), dg_mesh, subcell_mesh.extents())))}};\n\n  const auto expected_rdmp_data = past_rdmp_tci_data;\n\n  if (test_this == TestThis::RdmpU) {\n    // Assumes min is positive, increase it so we fail the TCI\n    past_rdmp_tci_data.min_variables_values[0] *= 1.01;\n  }\n\n  // check the result\n  const double persson_exponent{4.0};\n  const evolution::dg::subcell::SubcellOptions subcell_options{\n      persson_exponent,\n      1_st,\n      1.0e-16,\n      1.0e-4,\n      false,\n      false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n      false,\n      std::nullopt,\n      fd::DerivativeOrder::Two,\n      1,\n      1,\n      1};\n\n  const bool element_stays_on_dg = false;\n  const std::tuple<bool, evolution::dg::subcell::RdmpTciData> result =\n      ScalarAdvection::subcell::TciOnDgGrid<Dim>::apply(\n          u, dg_mesh, subcell_mesh, past_rdmp_tci_data, subcell_options,\n          tci_options, persson_exponent, element_stays_on_dg);\n\n  CHECK(std::get<1>(result) == expected_rdmp_data);\n\n  if (test_this == TestThis::AllGood or test_this == TestThis::BelowCutoff) {\n    CHECK_FALSE(std::get<0>(result));\n  } else {\n    CHECK(std::get<0>(result));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.Subcell.TciOnDgGrid\",\n                  \"[Unit][Evolution]\") {\n  for (const auto test_this : {TestThis::AllGood, TestThis::PerssonU,\n                               TestThis::RdmpU, TestThis::BelowCutoff}) {\n    test<1>(test_this);\n    test<2>(test_this);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_TciOnFdGrid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Reconstruction.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOnFdGrid.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\n// test cases to be covered\nenum class TestThis { AllGood, PerssonU, BelowCutoff, RdmpU };\n\ntemplate <size_t Dim>\nvoid test(const TestThis& test_this) {\n  // create DG mesh\n  const Mesh<Dim> dg_mesh{5, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n  // create scalar field U on the subcell mesh\n  const size_t number_of_points{subcell_mesh.number_of_grid_points()};\n  Scalar<DataVector> u{number_of_points, 1.0};\n\n  const ScalarAdvection::subcell::TciOptions tci_options{1.0e-8};\n\n  if (test_this == TestThis::PerssonU) {\n    // make a troubled cell\n    get(u)[number_of_points / 2] += 1.0;\n  }\n  if (test_this == TestThis::BelowCutoff) {\n    // make a troubled cell, but scale it to be smaller than the cutoff\n    get(u)[number_of_points / 2] += 1.0;\n    get(u) *= 1.0e-10;\n  }\n\n  // Set the RDMP TCI past data.\n  using std::max;\n  using std::min;\n  evolution::dg::subcell::RdmpTciData past_rdmp_tci_data{\n      {max(max(get(u)),\n           max(evolution::dg::subcell::fd::reconstruct(\n               get(u), dg_mesh, subcell_mesh.extents(),\n               evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)))},\n      {min(min(get(u)),\n           min(evolution::dg::subcell::fd::reconstruct(\n               get(u), dg_mesh, subcell_mesh.extents(),\n               evolution::dg::subcell::fd::ReconstructionMethod::DimByDim)))}};\n\n  const evolution::dg::subcell::RdmpTciData expected_rdmp_tci_data{\n      {max(get(u))}, {min(get(u))}};\n\n  if (test_this == TestThis::RdmpU) {\n    // Assumes min is positive, increase it so we fail the TCI\n    past_rdmp_tci_data.min_variables_values[0] *= 1.01;\n  }\n\n  // check the result\n  const double persson_exponent{4.0};\n  const evolution::dg::subcell::SubcellOptions subcell_options{\n      persson_exponent,\n      1_st,\n      1.0e-16,\n      1.0e-4,\n      false,\n      false,\n      evolution::dg::subcell::fd::ReconstructionMethod::DimByDim,\n      false,\n      std::nullopt,\n      fd::DerivativeOrder::Two,\n      1,\n      1,\n      1};\n\n  const std::tuple<bool, evolution::dg::subcell::RdmpTciData> result =\n      ScalarAdvection::subcell::TciOnFdGrid<Dim>::apply(\n          u, dg_mesh, subcell_mesh, past_rdmp_tci_data, subcell_options,\n          tci_options, persson_exponent, false);\n\n  if (test_this == TestThis::AllGood or test_this == TestThis::BelowCutoff) {\n    CHECK_FALSE(std::get<0>(result));\n  } else {\n    CHECK(std::get<0>(result));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.Subcell.TciOnFdGrid\",\n                  \"[Unit][Evolution]\") {\n  for (const auto test_this : {TestThis::AllGood, TestThis::PerssonU,\n                               TestThis::BelowCutoff, TestThis::RdmpU}) {\n    test<1>(test_this);\n    test<2>(test_this);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_TciOptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TciOptions.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.ScalarAdvection.Subcell.TciOptions\",\n                  \"[Unit][Evolution]\") {\n  const auto tci_options_from_opts = TestHelpers::test_option_tag<\n      ScalarAdvection::subcell::OptionTags::TciOptions>(\"UCutoff: 1.0e-10\\n\");\n  const auto tci_options = serialize_and_deserialize(tci_options_from_opts);\n  CHECK(tci_options.u_cutoff == 1.0e-10);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DgSubcell/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/GhostDataForReconstruction.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/Mesh.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/BoundaryCorrections/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Factory.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/TimeDerivative.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/VelocityAtFace.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/System.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/ScalarAdvection/FiniteDifference/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ScalarAdvection {\nnamespace {\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<evolution::BoundaryCorrection,\n                   BoundaryCorrections::standard_boundary_corrections<Dim>>>;\n  };\n};\n\ntemplate <size_t Dim>\nvoid test_subcell_timederivative() {\n  using evolved_vars_tag = typename System<Dim>::variables_tag;\n  using dt_variables_tag = db::add_tag_prefix<::Tags::dt, evolved_vars_tag>;\n\n  using velocity_field = Tags::VelocityField<Dim>;\n  using subcell_velocity_field =\n      evolution::dg::subcell::Tags::Inactive<velocity_field>;\n  using subcell_faces_velocity_field =\n      evolution::dg::subcell::Tags::OnSubcellFaces<velocity_field, Dim>;\n\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    neighbors[gsl::at(Direction<Dim>::all_directions(), i)] = Neighbors<Dim>{\n        {ElementId<Dim>{i + 1, {}}}, OrientationMap<Dim>::create_aligned()};\n  }\n  const Element<Dim> element{ElementId<Dim>{0, {}}, neighbors};\n\n  const size_t num_dg_pts_per_dimension = 5;\n  const Mesh<Dim> dg_mesh{num_dg_pts_per_dimension, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<Dim> subcell_mesh = evolution::dg::subcell::fd::mesh(dg_mesh);\n\n  // Perform test with MC reconstruction & Rusanov riemann solver\n  using ReconstructionForTest = typename fd::MonotonisedCentral<Dim>;\n  using BoundaryCorrectionForTest = typename BoundaryCorrections::Rusanov<Dim>;\n\n  // required for calling ScalarAdvection::VelocityAtFace::apply()\n  const double time{0.0};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  // Set the testing profile for the scalar field U.\n  // Here we use\n  //   * U(x)   = 2x + 1        (for 1D)\n  //   * U(x,y) = 2x + y^2 + 1  (for 2D)\n  const auto compute_test_solution = [](const auto& coords) {\n    using tag = Tags::U;\n    Variables<tmpl::list<tag>> vars{get<0>(coords).size(), 0.0};\n    get(get<tag>(vars)) += 2.0 * coords.get(0) + 1.0;\n    if constexpr (Dim == 2) {\n      get(get<tag>(vars)) += square(coords.get(1));\n    }\n    return vars;\n  };\n  auto logical_coords_subcell = logical_coordinates(subcell_mesh);\n  const auto volume_vars_subcell =\n      compute_test_solution(logical_coords_subcell);\n\n  // set the ghost data from neighbor\n  const ReconstructionForTest reconstructor{};\n  typename evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>::type\n      ghost_data = TestHelpers::ScalarAdvection::fd::compute_ghost_data(\n          subcell_mesh, logical_coords_subcell, element.neighbors(),\n          reconstructor.ghost_zone_size(), compute_test_solution);\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Parallel::Tags::MetavariablesImpl<Metavariables<Dim>>,\n          domain::Tags::Element<Dim>, evolution::dg::subcell::Tags::Mesh<Dim>,\n          evolved_vars_tag, dt_variables_tag,\n          evolution::dg::subcell::Tags::GhostDataForReconstruction<Dim>,\n          fd::Tags::Reconstructor<Dim>, evolution::Tags::BoundaryCorrection,\n          ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize,\n          domain::Tags::ElementMap<Dim, Frame::Grid>,\n          domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                      Frame::Inertial>,\n          subcell_velocity_field, subcell_faces_velocity_field,\n          evolution::dg::Tags::MortarData<Dim>>,\n      db::AddComputeTags<\n          evolution::dg::subcell::Tags::LogicalCoordinatesCompute<Dim>,\n          ::domain::Tags::MappedCoordinates<\n              ::domain::Tags::ElementMap<Dim, Frame::Grid>,\n              evolution::dg::subcell::Tags::Coordinates<Dim,\n                                                        Frame::ElementLogical>,\n              evolution::dg::subcell::Tags::Coordinates>,\n          evolution::dg::subcell::Tags::InertialCoordinatesCompute<\n              ::domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                            Frame::Inertial>>,\n          evolution::dg::subcell::fd::Tags::InverseJacobianLogicalToGridCompute<\n              ::domain::Tags::ElementMap<Dim, Frame::Grid>, Dim>,\n          evolution::dg::subcell::fd::Tags::\n              DetInverseJacobianLogicalToGridCompute<Dim>,\n          evolution::dg::subcell::fd::Tags::\n              InverseJacobianLogicalToInertialCompute<\n                  ::domain::CoordinateMaps::Tags::CoordinateMap<\n                      Dim, Frame::Grid, Frame::Inertial>,\n                  Dim>,\n          evolution::dg::subcell::fd::Tags::\n              DetInverseJacobianLogicalToInertialCompute<\n                  ::domain::CoordinateMaps::Tags::CoordinateMap<\n                      Dim, Frame::Grid, Frame::Inertial>,\n                  Dim>>>(\n      Metavariables<Dim>{}, element, subcell_mesh, volume_vars_subcell,\n      Variables<typename dt_variables_tag::tags_list>{\n          subcell_mesh.number_of_grid_points()},\n      ghost_data,\n      std::unique_ptr<fd::Reconstructor<Dim>>{\n          std::make_unique<ReconstructionForTest>()},\n      std::unique_ptr<evolution::BoundaryCorrection>{\n          std::make_unique<BoundaryCorrectionForTest>()},\n      time, clone_unique_ptrs(functions_of_time),\n      ElementMap<Dim, Frame::Grid>{\n          ElementId<Dim>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<Dim>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{}),\n      typename subcell_velocity_field::type{},\n      std::array<typename subcell_faces_velocity_field::type::value_type,\n                 Dim>{},\n      typename evolution::dg::Tags::MortarData<Dim>::type{});\n\n  // Compute face-centered velocity field and add it to the box. This action\n  // needs to be called in prior since TimeDerivative::apply() internally\n  // retrieves face-centered values of velocity field when packaging data for\n  // riemann solve.\n  db::mutate_apply<ScalarAdvection::subcell::VelocityAtFace<Dim>>(\n      make_not_null(&box));\n\n  subcell::TimeDerivative<Dim>::apply(make_not_null(&box));\n\n  const auto& dt_vars = db::get<dt_variables_tag>(box);\n\n  // Analytic time derivative of U for the testing profile\n  //   * dt(U) = -2          (for 1D)\n  //   * dt(U) = -1 +3y -2xy (for 2D)\n  const auto compute_test_derivative = [](const auto& coords) {\n    using tag = ::Tags::dt<Tags::U>;\n    Variables<tmpl::list<tag>> dt_expected{get<0>(coords).size(), 0.0};\n    if constexpr (Dim == 1) {\n      get(get<tag>(dt_expected)) += -2.0;\n    } else if constexpr (Dim == 2) {\n      get(get<tag>(dt_expected)) +=\n          -1.0 + 3.0 * coords.get(1) - 2.0 * coords.get(0) * coords.get(1);\n    }\n    return dt_expected;\n  };\n\n  CHECK_ITERABLE_APPROX(get<::Tags::dt<Tags::U>>(dt_vars),\n                        get<::Tags::dt<Tags::U>>(\n                            compute_test_derivative(logical_coords_subcell)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ScalarAdvection.Subcell.TimeDerivative\",\n    \"[Unit][Evolution]\") {\n  test_subcell_timederivative<1>();\n  test_subcell_timederivative<2>();\n}\n}  // namespace ScalarAdvection\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Subcell/Test_VelocityAtFace.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/Tags/Inactive.hpp\"\n#include \"Evolution/DgSubcell/Tags/OnSubcellFaces.hpp\"\n#include \"Evolution/Initialization/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Subcell/VelocityAtFace.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/VelocityField.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test() {\n  // type aliases for the face tensor\n  using velocity_field = ::ScalarAdvection::Tags::VelocityField<Dim>;\n  using subcell_velocity_field =\n      ::evolution::dg::subcell::Tags::Inactive<velocity_field>;\n  using subcell_faces_velocity_field =\n      ::evolution::dg::subcell::Tags::OnSubcellFaces<velocity_field, Dim>;\n\n  using vars = typename velocity_field::type;\n  using subcell_vars = typename subcell_velocity_field::type;\n  using face_vars = typename subcell_faces_velocity_field::type::value_type;\n\n  const size_t num_pts = 5;\n  const Mesh<Dim> subcell_mesh{num_pts, Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::CellCentered};\n\n  const double time = 0.0;\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  // create a DataBox\n  auto box = db::create<db::AddSimpleTags<\n      ::Tags::Time,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::Tags::ElementMap<Dim, Frame::Grid>,\n      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                  Frame::Inertial>,\n      evolution::dg::subcell::Tags::Mesh<Dim>,\n      evolution::dg::subcell::Tags::Coordinates<Dim, Frame::ElementLogical>,\n      subcell_velocity_field, subcell_faces_velocity_field>>(\n      time, clone_unique_ptrs(functions_of_time),\n      ElementMap<Dim, Frame::Grid>{\n          ElementId<Dim>{0},\n          domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n              domain::CoordinateMaps::Identity<Dim>{})},\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{}),\n      subcell_mesh, logical_coordinates(subcell_mesh), subcell_vars{},\n      std::array<face_vars, Dim>{});\n\n  // apply the mutator\n  db::mutate_apply<ScalarAdvection::subcell::VelocityAtFace<Dim>>(\n      make_not_null(&box));\n\n  // construct cell-centered inertial coordinates for testing\n  const ElementMap<Dim, Frame::Grid> logical_to_grid_map{\n      ElementId<Dim>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n          domain::CoordinateMaps::Identity<Dim>{})};\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{});\n  const auto cell_centered_inertial_coords = (*grid_to_inertial_map)(\n      logical_to_grid_map(logical_coordinates(subcell_mesh)), time,\n      functions_of_time);\n\n  // compute the velocity field at the cell-centered grid points\n  vars expected_no_prefix_cell_centered_vars{\n      subcell_mesh.number_of_grid_points()};\n  ::ScalarAdvection::Tags::VelocityFieldCompute<Dim>::function(\n      make_not_null(&expected_no_prefix_cell_centered_vars),\n      cell_centered_inertial_coords);\n  const subcell_vars expected_cell_centered_vars{\n      std::move(expected_no_prefix_cell_centered_vars)};\n\n  // check cell-centered values\n  CHECK_ITERABLE_APPROX(db::get<subcell_velocity_field>(box),\n                        expected_cell_centered_vars);\n\n  // check face-centered values\n  for (size_t i = 0; i < Dim; ++i) {\n    // construct face-centered inertial coordinates\n    const auto basis = subcell_mesh.basis();\n    auto quadrature = subcell_mesh.quadrature();\n    // The following method of constructing the extents std::array avoids a\n    // suspected compiler bug when building on Apple Silicon. The apparent bug\n    // causes the Mesh<Dim> constructor to ignore modifications to the\n    // extents array's components, so here the array is constructed in a way\n    // that never modifies a component once set.\n    std::array<size_t, Dim> extents{};\n    for (size_t j = 0; j < Dim; ++j) {\n      if (j == i) {\n        gsl::at(extents, j) = subcell_mesh.extents(0) + 1;\n      } else {\n        gsl::at(extents, j) = subcell_mesh.extents(0);\n      }\n    }\n    gsl::at(quadrature, i) = Spectral::Quadrature::FaceCentered;\n    const Mesh<Dim> face_centered_mesh{extents, basis, quadrature};\n    const auto face_logical_coords = logical_coordinates(face_centered_mesh);\n    const auto face_inertial_coords = (*grid_to_inertial_map)(\n        logical_to_grid_map(face_logical_coords), time, functions_of_time);\n\n    // compute the velocity field at the face-centered grid points\n    face_vars expected_face_vars{face_centered_mesh.number_of_grid_points()};\n    ::ScalarAdvection::Tags::VelocityFieldCompute<Dim>::function(\n        make_not_null(&expected_face_vars), face_inertial_coords);\n\n    // check\n    CHECK_ITERABLE_APPROX(\n        gsl::at(db::get<subcell_faces_velocity_field>(box), i),\n        expected_face_vars);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ScalarAdvection.Subcell.VelocityAtFace\",\n    \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Characteristics.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_characteristics(const gsl::not_null<std::mt19937*> gen) {\n  // test for tags\n  TestHelpers::db::test_compute_tag<\n      ScalarAdvection::Tags::LargestCharacteristicSpeedCompute<Dim>>(\n      \"LargestCharacteristicSpeed\");\n\n  // test for randomly generated velocity field\n  const DataVector used_for_size(10);\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  const auto velocity_field = make_with_random_values<tnsr::I<DataVector, Dim>>(\n      gen, make_not_null(&distribution), used_for_size);\n  double largest_characteristic_speed =\n      std::numeric_limits<double>::signaling_NaN();\n  ScalarAdvection::Tags::LargestCharacteristicSpeedCompute<Dim>::function(\n      make_not_null(&largest_characteristic_speed), velocity_field);\n  CHECK(largest_characteristic_speed ==\n        max(get(magnitude<DataVector>(velocity_field))));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.Characteristics\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n\n  test_characteristics<1>(make_not_null(&gen));\n  test_characteristics<2>(make_not_null(&gen));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Test_Fluxes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Fluxes.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test_fluxes(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(&ScalarAdvection::Fluxes<Dim>::apply,\n                                    \"Fluxes\", {\"compute_flux\"}, {{{-1.0, 1.0}}},\n                                    used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.Fluxes\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarAdvection\"};\n\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_fluxes, (1, 2))\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_tags() {\n  TestHelpers::db::test_simple_tag<ScalarAdvection::Tags::U>(\"U\");\n  TestHelpers::db::test_simple_tag<ScalarAdvection::Tags::VelocityField<Dim>>(\n      \"VelocityField\");\n  TestHelpers::db::test_simple_tag<\n      ScalarAdvection::Tags::LargestCharacteristicSpeed>(\n      \"LargestCharacteristicSpeed\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.Tags\",\n                  \"[Unit][Evolution]\") {\n  test_tags<1>();\n  test_tags<2>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Test_TimeDerivativeTerms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/TimeDerivativeTerms.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test_time_derivative(const gsl::not_null<std::mt19937*> generator,\n                          const size_t number_of_pts) {\n  // create datavectors to store computed values of dudt and flux\n  Scalar<DataVector> dudt{number_of_pts, 0.0};\n  tnsr::I<DataVector, Dim, Frame::Inertial> flux(number_of_pts);\n  tnsr::I<DataVector, Dim, Frame::Inertial> temp_velocity_field(number_of_pts);\n\n  // generate random u and velocity field\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  const auto u = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&distribution), dudt);\n  const auto velocity_field =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          generator, make_not_null(&distribution), dudt);\n\n  // evaluate dudt and flux\n  ScalarAdvection::TimeDerivativeTerms<Dim>::apply(\n      &dudt, &flux, &temp_velocity_field, u, velocity_field);\n\n  // expected values of dudt and flux. note that dudt_expected is set to be\n  // equal to the original value of dudt (which is 0.0 here), since we have no\n  // source terms and the TimeDerivativeTerms::apply function should not make\n  // any change on dudt argument.\n  const Scalar<DataVector> dudt_expected{number_of_pts, 0.0};\n  const tnsr::I<DataVector, Dim, Frame::Inertial> flux_expected{\n      pypp::call<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          \"Fluxes\", \"compute_flux\", u, velocity_field)};\n\n  // check values\n  CHECK_ITERABLE_APPROX(dudt, dudt_expected);\n  CHECK_ITERABLE_APPROX(flux, flux_expected);\n  CHECK_ITERABLE_APPROX(temp_velocity_field, velocity_field);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.TimeDerivative\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarAdvection\"};\n  MAKE_GENERATOR(gen);\n\n  test_time_derivative<1>(make_not_null(&gen), 2);\n  test_time_derivative<2>(make_not_null(&gen), 5);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/Test_VelocityField.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/VelocityField.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_velocity_field(const gsl::not_null<std::mt19937*> gen) {\n  // test for tags\n  TestHelpers::db::test_compute_tag<\n      ScalarAdvection::Tags::VelocityFieldCompute<Dim>>(\"VelocityField\");\n\n  // generate random coordinates\n  const DataVector used_for_size(10);\n  std::uniform_real_distribution<> distribution(0.0);\n  if constexpr (Dim == 1) {\n    // computation domain is [-1, 1] for 1D advection\n    distribution = std::uniform_real_distribution<>(-1.0, 1.0);\n  } else if constexpr (Dim == 2) {\n    // computation domain is [0, 1] x [0, 1] for 2D advection\n    distribution = std::uniform_real_distribution<>(0.0, 1.0);\n  }\n  const auto inertial_coords =\n      make_with_random_values<tnsr::I<DataVector, Dim>>(\n          gen, make_not_null(&distribution), used_for_size);\n\n  // compute velocity field from VelocityFieldCompute struct\n  auto velocity_field = make_with_value<tnsr::I<DataVector, Dim>>(\n      inertial_coords, std::numeric_limits<double>::signaling_NaN());\n  ScalarAdvection::Tags::VelocityFieldCompute<Dim>::function(&velocity_field,\n                                                             inertial_coords);\n\n  // compute velocity field from python implementation\n  const tnsr::I<DataVector, Dim, Frame::Inertial> velocity_field_test{\n      pypp::call<tnsr::I<DataVector, Dim, Frame::Inertial>>(\n          \"VelocityField\", \"velocity_field\", inertial_coords)};\n\n  // check values\n  CHECK_ITERABLE_APPROX(velocity_field, velocity_field_test);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarAdvection.VelocityField\",\n                  \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarAdvection\"};\n  MAKE_GENERATOR(gen);\n\n  test_velocity_field<1>(make_not_null(&gen));\n  test_velocity_field<2>(make_not_null(&gen));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarAdvection/VelocityField.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef velocity_field(coords):\n    if len(coords) == 1:\n        return np.ones(1)\n    elif len(coords) == 2:\n        return np.asarray([0.5 - coords[1], -0.5 + coords[0]])\n    else:\n        raise TypeError(\"Coordinate dimension does not match\")\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/Actions/Test_SetInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <variant>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/SetPiAndPhiFromConstraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Actions/SetInitialData.hpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.tpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace ScalarTensor {\nnamespace {\n\nusing st_system_vars = ScalarTensor::System::variables_tag::tags_list;\n\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using component_being_mocked = void;  // Not needed\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using mutable_global_cache_tags =\n      tmpl::list<gh::Tags::SetPiAndPhiFromConstraints>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::append<\n              st_system_vars,\n              tmpl::list<domain::Tags::Mesh<3>,\n                         domain::Tags::Coordinates<3, Frame::Inertial>,\n                         domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                                       Frame::Inertial>,\n                         ::Tags::Time>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<ScalarTensor::Actions::SetInitialData,\n                     ScalarTensor::Actions::ReceiveNumericInitialData>>>;\n};\n\nstruct MockReadVolumeData {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      DataBox& /*box*/, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/,\n      const importers::ImporterOptions& options, const size_t volume_data_id,\n      tuples::tagged_tuple_from_typelist<db::wrap_tags_in<\n          importers::Tags::Selected, NumericInitialData::all_vars>>\n          selected_fields) {\n    const auto& initial_data = dynamic_cast<const NumericInitialData&>(\n        get<evolution::initial_data::Tags::InitialData>(cache));\n    CHECK(options == initial_data.importer_options());\n    CHECK(volume_data_id == initial_data.volume_data_id());\n    const auto gh_selected_vars =\n        initial_data.gh_numeric_id().selected_variables();\n    if (std::holds_alternative<gh::NumericInitialData::GhVars>(\n            gh_selected_vars)) {\n      CHECK(get<importers::Tags::Selected<\n                gr::Tags::SpacetimeMetric<DataVector, 3>>>(selected_fields) ==\n            \"CustomSpacetimeMetric\");\n      CHECK(get<importers::Tags::Selected<gh::Tags::Pi<DataVector, 3>>>(\n                selected_fields) == \"CustomPi\");\n      CHECK(get<importers::Tags::Selected<gh::Tags::Phi<DataVector, 3>>>(\n                selected_fields) == \"CustomPhi\");\n      CHECK_FALSE(\n          get<importers::Tags::Selected<\n              gr::Tags::SpatialMetric<DataVector, 3>>>(selected_fields));\n      CHECK_FALSE(get<importers::Tags::Selected<gr::Tags::Lapse<DataVector>>>(\n          selected_fields));\n      CHECK_FALSE(\n          get<importers::Tags::Selected<gr::Tags::Shift<DataVector, 3>>>(\n              selected_fields));\n    } else if (std::holds_alternative<gh::NumericInitialData::AdmVars>(\n                   gh_selected_vars)) {\n      CHECK(get<importers::Tags::Selected<\n                gr::Tags::SpatialMetric<DataVector, 3>>>(selected_fields) ==\n            \"CustomSpatialMetric\");\n      CHECK(get<importers::Tags::Selected<gr::Tags::Lapse<DataVector>>>(\n                selected_fields) == \"CustomLapse\");\n      CHECK(get<importers::Tags::Selected<gr::Tags::Shift<DataVector, 3>>>(\n                selected_fields) == \"CustomShift\");\n      CHECK(get<importers::Tags::Selected<\n                gr::Tags::ExtrinsicCurvature<DataVector, 3>>>(\n                selected_fields) == \"CustomExtrinsicCurvature\");\n      CHECK_FALSE(\n          get<importers::Tags::Selected<\n              gr::Tags::SpacetimeMetric<DataVector, 3>>>(selected_fields));\n      CHECK_FALSE(get<importers::Tags::Selected<gh::Tags::Pi<DataVector, 3>>>(\n          selected_fields));\n      CHECK_FALSE(get<importers::Tags::Selected<gh::Tags::Phi<DataVector, 3>>>(\n          selected_fields));\n    } else {\n      REQUIRE(false);\n    }\n    CHECK(get<importers::Tags::Selected<CurvedScalarWave::Tags::Psi>>(\n              selected_fields) == \"CustomPsi\");\n    CHECK(get<importers::Tags::Selected<CurvedScalarWave::Tags::Pi>>(\n              selected_fields) == \"CustomScalarPi\");\n    CHECK(get<importers::Tags::Selected<CurvedScalarWave::Tags::Phi<3>>>(\n              selected_fields) == \"CustomScalarPhi\");\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockVolumeDataReader {\n  using component_being_mocked = importers::ElementDataReader<Metavariables>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using replace_these_simple_actions =\n      tmpl::list<importers::Actions::ReadAllVolumeDataAndDistribute<\n          metavariables::volume_dim, NumericInitialData::all_vars,\n          MockElementArray<Metavariables>>>;\n  using with_these_simple_actions = tmpl::list<MockReadVolumeData>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  using component_list = tmpl::list<MockElementArray<Metavariables>,\n                                    MockVolumeDataReader<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        evolution::initial_data::InitialData,\n        tmpl::list<NumericInitialData,\n                   gh::Solutions::WrappedGr<\n                       ::ScalarTensor::AnalyticData::KerrSphericalHarmonic>>>>;\n  };\n};\n\nvoid test_set_initial_data(\n    const evolution::initial_data::InitialData& initial_data,\n    const std::string& option_string, const bool is_numeric) {\n  {\n    INFO(\"Factory creation\");\n    const auto created = TestHelpers::test_creation<\n        std::unique_ptr<evolution::initial_data::InitialData>, Metavariables>(\n        option_string);\n    if (is_numeric) {\n      CHECK(dynamic_cast<const NumericInitialData&>(*created) ==\n            dynamic_cast<const NumericInitialData&>(initial_data));\n    } else {\n      CHECK(dynamic_cast<const gh::Solutions::WrappedGr<\n                ::ScalarTensor::AnalyticData::KerrSphericalHarmonic>&>(\n                *created) ==\n            dynamic_cast<const gh::Solutions::WrappedGr<\n                ::ScalarTensor::AnalyticData::KerrSphericalHarmonic>&>(\n                initial_data));\n    }\n  }\n\n  using reader_component = MockVolumeDataReader<Metavariables>;\n  using element_array = MockElementArray<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {initial_data.get_clone()}, {true}};\n\n  // Setup mock data file reader\n  ActionTesting::emplace_nodegroup_component<reader_component>(\n      make_not_null(&runner));\n\n  // Setup element\n  const ElementId<3> element_id{0};\n  const Mesh<3> mesh{8, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Wedge<3>{\n              2., 4., 1., 1., OrientationMap<3>::create_aligned(), true});\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto coords = map(logical_coords);\n  const auto inv_jacobian = map.inv_jacobian(logical_coords);\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), element_id,\n      {tnsr::aa<DataVector, 3>{}, tnsr::aa<DataVector, 3>{},\n       tnsr::iaa<DataVector, 3>{}, Scalar<DataVector>{}, Scalar<DataVector>{},\n       tnsr::i<DataVector, 3>{}, mesh, coords, inv_jacobian, 0.});\n\n  const auto get_element_tag = [&runner,\n                                &element_id](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  // We use a Kerr solution to generate data\n  const gh::Solutions::WrappedGr<\n      ::ScalarTensor::AnalyticData::KerrSphericalHarmonic>\n      kerr{1., {{0., 0., 0.}}, 2.0, 1.0, 1.0, std::pair<size_t, int>{1, 0}};\n  const auto kerr_gh_vars = kerr.variables(coords, st_system_vars{});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const auto& cache = ActionTesting::cache<element_array>(runner, element_id);\n\n  // SetInitialData\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n\n  if (is_numeric) {\n    INFO(\"Numeric initial data\");\n    const auto& numeric_id =\n        dynamic_cast<const NumericInitialData&>(initial_data);\n\n    CHECK(Parallel::get<gh::Tags::SetPiAndPhiFromConstraints>(cache) ==\n          std::holds_alternative<gh::NumericInitialData::AdmVars>(\n              numeric_id.gh_numeric_id().selected_variables()));\n\n    REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n        make_not_null(&runner), element_id));\n\n    // MockReadVolumeData\n    ActionTesting::invoke_queued_simple_action<reader_component>(\n        make_not_null(&runner), 0);\n\n    // Insert KerrSchild data into the inbox\n    using inbox_tag = importers::Tags::VolumeData<NumericInitialData::all_vars>;\n    auto& inboxes =\n        ActionTesting::get_inbox_tag<element_array, inbox_tag, Metavariables>(\n            make_not_null(&runner), element_id);\n    auto& inbox = inboxes[numeric_id.volume_data_id()];\n    const auto& gh_selected_vars =\n        numeric_id.gh_numeric_id().selected_variables();\n    if (std::holds_alternative<gh::NumericInitialData::GhVars>(\n            gh_selected_vars)) {\n      get<gr::Tags::SpacetimeMetric<DataVector, 3>>(inbox) =\n          get<gr::Tags::SpacetimeMetric<DataVector, 3>>(kerr_gh_vars);\n      get<gh::Tags::Pi<DataVector, 3>>(inbox) =\n          get<gh::Tags::Pi<DataVector, 3>>(kerr_gh_vars);\n      get<gh::Tags::Phi<DataVector, 3>>(inbox) =\n          get<gh::Tags::Phi<DataVector, 3>>(kerr_gh_vars);\n    } else if (std::holds_alternative<gh::NumericInitialData::AdmVars>(\n                   gh_selected_vars)) {\n      const auto kerr_adm_vars = kerr.variables(\n          coords, tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                             gr::Tags::Lapse<DataVector>,\n                             gr::Tags::Shift<DataVector, 3>,\n                             gr::Tags::ExtrinsicCurvature<DataVector, 3>>{});\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(inbox) =\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(kerr_adm_vars);\n      get<gr::Tags::Lapse<DataVector>>(inbox) =\n          get<gr::Tags::Lapse<DataVector>>(kerr_adm_vars);\n      get<gr::Tags::Shift<DataVector, 3>>(inbox) =\n          get<gr::Tags::Shift<DataVector, 3>>(kerr_adm_vars);\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(inbox) =\n          get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(kerr_adm_vars);\n    } else {\n      REQUIRE(false);\n    }\n    get<CurvedScalarWave::Tags::Psi>(inbox) =\n        get<CurvedScalarWave::Tags::Psi>(kerr_gh_vars);\n    get<CurvedScalarWave::Tags::Pi>(inbox) =\n        get<CurvedScalarWave::Tags::Pi>(kerr_gh_vars);\n    get<CurvedScalarWave::Tags::Phi<3>>(inbox) =\n        get<CurvedScalarWave::Tags::Phi<3>>(kerr_gh_vars);\n\n    const std::string inbox_output = inbox_tag::output_inbox(inboxes, 1_st);\n    const std::string expected_inbox_output =\n        MakeString{} << \" VolumeDataInbox:\\n\"\n                     << \"  Index: \" << numeric_id.volume_data_id() << \"\\n\";\n    CHECK(inbox_output == expected_inbox_output);\n\n    // ReceiveNumericInitialData\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              element_id);\n  } else {\n    CHECK(Parallel::get<gh::Tags::SetPiAndPhiFromConstraints>(cache));\n  }\n\n  // Check result. These variables are not particularly precise because we are\n  // taking numerical derivatives on a fairly coarse wedge-shaped grid.\n  const Approx custom_approx = Approx::custom().epsilon(1.e-3).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get_element_tag(gr::Tags::SpacetimeMetric<DataVector, 3>{}),\n      (get<gr::Tags::SpacetimeMetric<DataVector, 3>>(kerr_gh_vars)),\n      custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get_element_tag(gh::Tags::Pi<DataVector, 3>{}),\n                               (get<gh::Tags::Pi<DataVector, 3>>(kerr_gh_vars)),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get_element_tag(gh::Tags::Phi<DataVector, 3>{}),\n      (get<gh::Tags::Phi<DataVector, 3>>(kerr_gh_vars)), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get_element_tag(CurvedScalarWave::Tags::Psi{}),\n                               (get<CurvedScalarWave::Tags::Psi>(kerr_gh_vars)),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get_element_tag(CurvedScalarWave::Tags::Pi{}),\n                               (get<CurvedScalarWave::Tags::Pi>(kerr_gh_vars)),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get_element_tag(CurvedScalarWave::Tags::Phi<3>{}),\n      (get<CurvedScalarWave::Tags::Phi<3>>(kerr_gh_vars)), custom_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarTensor.NumericInitialData\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  test_set_initial_data(\n      NumericInitialData{\"TestInitialData.h5\",\n                         \"VolumeData\",\n                         0.,\n                         {1.0e-9},\n                         false,\n                         gh::NumericInitialData::GhVars{\n                             \"CustomSpacetimeMetric\", \"CustomPi\", \"CustomPhi\"},\n                         CurvedScalarWave::NumericInitialData::ScalarVars{\n                             \"CustomPsi\", \"CustomScalarPi\", \"CustomScalarPhi\"}},\n      \"NumericInitialData:\\n\"\n      \"  FileGlob: TestInitialData.h5\\n\"\n      \"  Subgroup: VolumeData\\n\"\n      \"  ObservationValue: 0.\\n\"\n      \"  ObservationValueEpsilon: 1e-9\\n\"\n      \"  ElementsAreIdentical: False\\n\"\n      \"  GhVariables:\\n\"\n      \"    SpacetimeMetric: CustomSpacetimeMetric\\n\"\n      \"    Pi: CustomPi\\n\"\n      \"    Phi: CustomPhi\\n\"\n      \"  ScalarVariables:\\n\"\n      \"    Psi: CustomPsi\\n\"\n      \"    Pi: CustomScalarPi\\n\"\n      \"    Phi: CustomScalarPhi\\n\",\n      true);\n  test_set_initial_data(\n      NumericInitialData{\n          \"TestInitialData.h5\", \"VolumeData\", 0., std::nullopt, false,\n          gh::NumericInitialData::AdmVars{\"CustomSpatialMetric\", \"CustomLapse\",\n                                          \"CustomShift\",\n                                          \"CustomExtrinsicCurvature\"},\n          CurvedScalarWave::NumericInitialData::ScalarVars{\n              \"CustomPsi\", \"CustomScalarPi\", \"CustomScalarPhi\"}},\n      \"NumericInitialData:\\n\"\n      \"  FileGlob: TestInitialData.h5\\n\"\n      \"  Subgroup: VolumeData\\n\"\n      \"  ObservationValue: 0.\\n\"\n      \"  ObservationValueEpsilon: Auto\\n\"\n      \"  ElementsAreIdentical: False\\n\"\n      \"  GhVariables:\\n\"\n      \"    SpatialMetric: CustomSpatialMetric\\n\"\n      \"    Lapse: CustomLapse\\n\"\n      \"    Shift: CustomShift\\n\"\n      \"    ExtrinsicCurvature: CustomExtrinsicCurvature\\n\"\n      \"  ScalarVariables:\\n\"\n      \"    Psi: CustomPsi\\n\"\n      \"    Pi: CustomScalarPi\\n\"\n      \"    Phi: CustomScalarPhi\\n\",\n      true);\n  test_set_initial_data(\n      gh::Solutions::WrappedGr<\n          ::ScalarTensor::AnalyticData::KerrSphericalHarmonic>{\n          1., {{0., 0., 0.}}, 2.0, 1.0, 1.0, std::pair<size_t, int>{1, 0}},\n      \"GeneralizedHarmonic(KerrSphericalHarmonic):\\n\"\n      \" Mass: 1.0 \\n\"\n      \" Spin: [0.0, 0.0, 0.0] \\n\"\n      \" Amplitude: 2.0 \\n\"\n      \" Radius: 1.0 \\n\"\n      \" Width: 1.0 \\n\"\n      \" Mode: [1, 0]\",\n      false);\n}\n\n}  // namespace ScalarTensor\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/BoundaryConditions/DemandOutgoingCharSpeeds.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef characteristic_speeds(gamma_1, lapse, shift, unit_normal_one_form):\n    shift_dot_normal = np.dot(shift, unit_normal_one_form)\n    return [\n        -(1.0 + gamma_1) * shift_dot_normal,\n        -shift_dot_normal,\n        -shift_dot_normal + lapse,\n        -shift_dot_normal - lapse,\n    ]\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    outward_directed_normal_vector,\n    gh_gamma_1,\n    lapse,\n    shift,\n    csw_gamma_1,\n):\n    gh_speeds = characteristic_speeds(\n        gh_gamma_1, lapse, shift, outward_directed_normal_covector\n    )\n    for i in range(4):\n        if face_mesh_velocity is not None:\n            gh_speeds[i] -= np.dot(\n                outward_directed_normal_covector, face_mesh_velocity\n            )\n            gh_speeds[0] -= (\n                np.dot(outward_directed_normal_covector, face_mesh_velocity)\n                * gh_gamma_1\n            )\n        if gh_speeds[i] < 0.0:\n            return (\n                \"Detected negative characteristic speed at boundary with \"\n                \"outgoing char speeds boundary conditions specified. The \"\n                \"speed is \"\n            )\n\n    # Char speeds are the same for GH and CSW.\n    csw_speeds = characteristic_speeds(\n        csw_gamma_1, lapse, shift, outward_directed_normal_covector\n    )\n    for i in range(4):\n        if face_mesh_velocity is not None:\n            csw_speeds[i] -= np.dot(\n                outward_directed_normal_covector, face_mesh_velocity\n            )\n            csw_speeds[0] -= (\n                np.dot(outward_directed_normal_covector, face_mesh_velocity)\n                * gh_gamma_1\n            )\n        if csw_speeds[i] < 0.0:\n            return (\n                \"Detected negative characteristic speed at boundary with \"\n                \"outgoing char speeds boundary conditions specified. The \"\n                \"speed is \"\n            )\n    return None\n\n    pass\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/BoundaryConditions/Test_ConstraintPreserving.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <random>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Bjorhus.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/ConstraintPreserving.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>, tmpl::list<>>,\n        tmpl::pair<ScalarTensor::BoundaryConditions::BoundaryCondition,\n                   tmpl::list<ScalarTensor::BoundaryConditions::\n                                  ConstraintPreserving>>>;\n  };\n};\n\ntemplate <typename U>\nvoid test_dg(const gsl::not_null<std::mt19937*> generator,\n             const U& boundary_condition) {\n  const size_t num_points = 5;\n\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  const gh::Solutions::WrappedGr<\n      ScalarTensor::AnalyticData::KerrSphericalHarmonic>\n      analytic_data{1.0,    std::array<double, 3>{{0.0, 0.0, 0.0}},\n                    1.0e-5, 30.0,\n                    5.0,    std::pair<size_t, int>{0, 0}};\n\n  const auto interior_gamma1 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n  const auto interior_gamma2 = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n\n  const auto interior_gamma1_scalar =\n      make_with_random_values<Scalar<DataVector>>(\n          generator, make_not_null(&dist), num_points);\n  const auto interior_gamma2_scalar =\n      make_with_random_values<Scalar<DataVector>>(\n          generator, make_not_null(&dist), num_points);\n\n  const auto coords =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          generator, make_not_null(&dist), num_points);\n\n  using Vars = Variables<tmpl::append<\n      ScalarTensor::System::variables_tag::tags_list,\n      db::wrap_tags_in<::Tags::Flux, ScalarTensor::System::flux_variables,\n                       tmpl::size_t<3_st>, Frame::Inertial>,\n      tmpl::list<gh::Tags::ConstraintGamma1, gh::Tags::ConstraintGamma2,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 CurvedScalarWave::Tags::ConstraintGamma1,\n                 CurvedScalarWave::Tags::ConstraintGamma2>>>;\n  using PrimVars = Variables<tmpl::list<>>;\n\n  Vars vars{num_points};\n  Vars expected_vars;\n  PrimVars prim_vars;\n\n  std::tie(expected_vars, prim_vars) = [&analytic_data, &coords,\n                                        &interior_gamma1, &interior_gamma2,\n                                        &interior_gamma1_scalar,\n                                        &interior_gamma2_scalar]() {\n    Vars expected{num_points};\n    auto& [spacetime_metric, pi, phi, psi_scalar, pi_scalar, phi_scalar, gamma1,\n           gamma2, lapse, shift, inverse_spatial_metric, gamma1_scalar,\n           gamma2_scalar] = expected;\n\n    gamma1 = interior_gamma1;\n    gamma2 = interior_gamma2;\n\n    gamma1_scalar = interior_gamma1_scalar;\n    gamma2_scalar = interior_gamma2_scalar;\n\n    PrimVars local_prim_vars{num_points};\n\n    using tags =\n        tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                   gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                   gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                   gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                   gr::Tags::SpacetimeMetric<DataVector, 3>,\n                   ::gh::Tags::Pi<DataVector, 3>,\n                   ::gh::Tags::Phi<DataVector, 3>, CurvedScalarWave::Tags::Psi,\n                   CurvedScalarWave::Tags::Pi, CurvedScalarWave::Tags::Phi<3>>;\n\n    tuples::tagged_tuple_from_typelist<tags> analytic_vars{};\n\n    analytic_vars = analytic_data.variables(coords, tags{});\n\n    spacetime_metric =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(analytic_vars);\n    pi = get<::gh::Tags::Pi<DataVector, 3>>(analytic_vars);\n    phi = get<::gh::Tags::Phi<DataVector, 3>>(analytic_vars);\n\n    psi_scalar = get<CurvedScalarWave::Tags::Psi>(analytic_vars);\n    pi_scalar = get<CurvedScalarWave::Tags::Pi>(analytic_vars);\n    phi_scalar = get<CurvedScalarWave::Tags::Phi<3>>(analytic_vars);\n\n    lapse = get<gr::Tags::Lapse<DataVector>>(analytic_vars);\n    shift = get<gr::Tags::Shift<DataVector, 3>>(analytic_vars);\n    inverse_spatial_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(analytic_vars);\n\n    return std::tuple(expected, local_prim_vars);\n  }();\n\n  // Pick random direction normal covector, then normalize and compute normal\n  // vector.\n  tnsr::i<DataVector, 3> normal_covector{num_points};\n  get<0>(normal_covector) = 0.5;\n  get<1>(normal_covector) = 0.0;\n  get<2>(normal_covector) = 0.5;\n  const auto magnitude_normal = magnitude(\n      normal_covector,\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(expected_vars));\n  for (size_t i = 0; i < 3; ++i) {\n    normal_covector.get(i) /= get(magnitude_normal);\n  }\n  const auto normal_vector =\n      tenex::evaluate<ti::I>(normal_covector(ti::j) *\n                             get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                                 expected_vars)(ti::I, ti::J));\n\n  auto& [spacetime_metric, pi, phi, psi_scalar, pi_scalar, phi_scalar, gamma1,\n         gamma2, lapse, shift, inverse_spatial_metric, gamma1_scalar,\n         gamma2_scalar] = vars;\n\n  CHECK(\n      not boundary_condition\n              .dg_ghost(\n                  make_not_null(&spacetime_metric), make_not_null(&pi),\n                  make_not_null(&phi),\n\n                  make_not_null(&psi_scalar), make_not_null(&pi_scalar),\n                  make_not_null(&phi_scalar),\n\n                  make_not_null(&gamma1), make_not_null(&gamma2),\n                  make_not_null(&lapse), make_not_null(&shift),\n\n                  make_not_null(&gamma1_scalar), make_not_null(&gamma2_scalar),\n\n                  make_not_null(&inverse_spatial_metric), {}, normal_covector,\n                  normal_vector,\n\n                  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Pi<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Phi<DataVector, 3>>(expected_vars),\n\n                  get<CurvedScalarWave::Tags::Psi>(expected_vars),\n                  get<CurvedScalarWave::Tags::Pi>(expected_vars),\n                  get<CurvedScalarWave::Tags::Phi<3>>(expected_vars),\n\n                  coords, interior_gamma1, interior_gamma2,\n                  get<gr::Tags::Lapse<DataVector>>(expected_vars),\n                  get<gr::Tags::Shift<DataVector, 3>>(expected_vars),\n                  get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                      expected_vars),\n\n                  {}, {}, {}, {}, {},\n\n                  interior_gamma1_scalar, interior_gamma2_scalar,\n\n                  {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})\n              .has_value());\n\n  const auto inverse_spacetime_metric =\n      determinant_and_inverse(spacetime_metric).second;\n  const auto spacetime_normal_vector = gr::spacetime_normal_vector(\n      get<gr::Tags::Lapse<DataVector>>(expected_vars),\n      get<gr::Tags::Shift<DataVector, 3>>(expected_vars));\n  const auto gauge_source = make_with_random_values<tnsr::a<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto spacetime_deriv_gauge_source =\n      make_with_random_values<tnsr::ab<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n  const auto d_spacetime_metric =\n      make_with_random_values<tnsr::iaa<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n  const auto d_pi = make_with_random_values<tnsr::iaa<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto d_phi = make_with_random_values<tnsr::ijaa<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n\n  const auto d_psi_scalar = make_with_random_values<tnsr::i<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto d_pi_scalar = make_with_random_values<tnsr::i<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto d_phi_scalar = make_with_random_values<tnsr::ij<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n\n  const auto three_index_constraint =\n      make_with_random_values<tnsr::iaa<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n  const auto logical_dt_spacetime_metric =\n      make_with_random_values<tnsr::aa<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n  const auto logical_dt_pi = make_with_random_values<tnsr::aa<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n  const auto logical_dt_phi = make_with_random_values<tnsr::iaa<DataVector, 3>>(\n      generator, make_not_null(&dist), num_points);\n\n  const auto logical_dt_psi_scalar =\n      make_with_random_values<Scalar<DataVector>>(\n          generator, make_not_null(&dist), num_points);\n  const auto logical_dt_pi_scalar = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&dist), num_points);\n  const auto logical_dt_phi_scalar =\n      make_with_random_values<tnsr::i<DataVector, 3>>(\n          generator, make_not_null(&dist), num_points);\n\n  using DtVars = Variables<db::wrap_tags_in<\n      ::Tags::dt, ScalarTensor::System::variables_tag::tags_list>>;\n  DtVars dt_vars{num_points};\n\n  // Test CurvedScalarWave constraint-preserving BC\n  CHECK(\n      not boundary_condition\n              .dg_time_derivative(\n                  make_not_null(\n                      &get<\n                          ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n                          dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<gh::Tags::Phi<DataVector, 3>>>(dt_vars)),\n\n                  make_not_null(\n                      &get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<CurvedScalarWave::Tags::Pi>>(dt_vars)),\n                  make_not_null(\n                      &get<::Tags::dt<CurvedScalarWave::Tags::Phi<3>>>(\n                          dt_vars)),\n\n                  {}, normal_covector, normal_vector,\n\n                  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Pi<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Phi<DataVector, 3>>(expected_vars),\n\n                  get<CurvedScalarWave::Tags::Psi>(expected_vars),\n                  get<CurvedScalarWave::Tags::Pi>(expected_vars),\n                  get<CurvedScalarWave::Tags::Phi<3>>(expected_vars),\n\n                  coords, interior_gamma1, interior_gamma2,\n                  get<gr::Tags::Lapse<DataVector>>(expected_vars),\n                  get<gr::Tags::Shift<DataVector, 3>>(expected_vars),\n                  get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                      expected_vars),\n                  inverse_spacetime_metric, spacetime_normal_vector,\n                  three_index_constraint, gauge_source,\n                  spacetime_deriv_gauge_source,\n\n                  interior_gamma1_scalar, interior_gamma2_scalar,\n\n                  logical_dt_spacetime_metric, logical_dt_pi, logical_dt_phi,\n\n                  logical_dt_psi_scalar, logical_dt_pi_scalar,\n                  logical_dt_phi_scalar,\n\n                  d_spacetime_metric, d_pi, d_phi,\n\n                  d_psi_scalar, d_pi_scalar, d_phi_scalar)\n              .has_value());\n\n  CurvedScalarWave::BoundaryConditions::ConstraintPreservingSphericalRadiation<\n      3>\n      csw_constraint_preserving{};\n\n  DtVars expected_dt_vars{num_points, 0.0};\n\n  CHECK(not csw_constraint_preserving\n                .dg_time_derivative(\n\n                    make_not_null(&get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(\n                        expected_dt_vars)),\n                    make_not_null(&get<::Tags::dt<CurvedScalarWave::Tags::Pi>>(\n                        expected_dt_vars)),\n                    make_not_null(\n                        &get<::Tags::dt<CurvedScalarWave::Tags::Phi<3>>>(\n                            expected_dt_vars)),\n\n                    {}, normal_covector, normal_vector,\n\n                    get<CurvedScalarWave::Tags::Psi>(expected_vars),\n                    get<CurvedScalarWave::Tags::Phi<3>>(expected_vars),\n\n                    coords, interior_gamma1_scalar, interior_gamma2_scalar,\n\n                    get<gr::Tags::Lapse<DataVector>>(expected_vars),\n                    get<gr::Tags::Shift<DataVector, 3>>(expected_vars),\n\n                    logical_dt_psi_scalar, logical_dt_pi_scalar,\n                    logical_dt_phi_scalar, d_psi_scalar, d_pi_scalar,\n                    d_phi_scalar)\n                .has_value());\n\n  tmpl::for_each<typename Vars::tags_list>([&expected_vars, &vars](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CAPTURE(db::tag_name<tag>());\n    CHECK(get<tag>(vars) == get<tag>(expected_vars));\n  });\n\n  // Test Generalized Harmonic constraint-preserving BC\n  gh::BoundaryConditions::ConstraintPreservingBjorhus<3> gh_cp{\n      gh::BoundaryConditions::detail::ConstraintPreservingBjorhusType::\n          ConstraintPreservingPhysical};\n  CHECK(\n      not gh_cp\n              .dg_time_derivative(\n                  make_not_null(\n                      &get<\n                          ::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n                          expected_dt_vars)),\n                  make_not_null(&get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(\n                      expected_dt_vars)),\n                  make_not_null(&get<::Tags::dt<gh::Tags::Phi<DataVector, 3>>>(\n                      expected_dt_vars)),\n\n                  {}, normal_covector, normal_vector,\n\n                  get<gr::Tags::SpacetimeMetric<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Pi<DataVector, 3>>(expected_vars),\n                  get<gh::Tags::Phi<DataVector, 3>>(expected_vars),\n\n                  coords, interior_gamma1, interior_gamma2,\n                  get<gr::Tags::Lapse<DataVector>>(expected_vars),\n                  get<gr::Tags::Shift<DataVector, 3>>(expected_vars),\n                  inverse_spacetime_metric, spacetime_normal_vector,\n                  three_index_constraint, gauge_source,\n                  spacetime_deriv_gauge_source, logical_dt_spacetime_metric,\n                  logical_dt_pi, logical_dt_phi, d_spacetime_metric, d_pi,\n                  d_phi)\n              .has_value());\n  tmpl::for_each<typename DtVars::tags_list>(\n      [&dt_vars, &expected_dt_vars](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CAPTURE(db::tag_name<tag>());\n        CHECK(get<tag>(dt_vars) == get<tag>(expected_dt_vars));\n      });\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarTensor.BoundaryConditions.ConstraintPreserving\",\n                  \"[Unit][Evolution]\") {\n  MAKE_GENERATOR(gen);\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto product_boundary_condition =\n      TestHelpers::test_creation<\n          std::unique_ptr<ScalarTensor::BoundaryConditions::BoundaryCondition>,\n          Metavariables>(\n          \"ConstraintPreserving:\\n\"\n          \"  Type: ConstraintPreservingPhysical\\n\"\n          \"  IncomingWaveProfile: None\\n\")\n          ->get_clone();\n\n  const auto serialized_and_deserialized_condition = serialize_and_deserialize(\n      *dynamic_cast<ScalarTensor::BoundaryConditions::ConstraintPreserving*>(\n          product_boundary_condition.get()));\n\n  test_dg(make_not_null(&gen), serialized_and_deserialized_condition);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/DemandOutgoingCharSpeeds.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\n\nvoid test() {\n  MAKE_GENERATOR(gen);\n\n  helpers::test_boundary_condition_with_python<\n      ScalarTensor::BoundaryConditions::DemandOutgoingCharSpeeds,\n      ScalarTensor::BoundaryConditions::BoundaryCondition, ScalarTensor::System,\n      tmpl::list<ScalarTensor::BoundaryCorrections::ProductOfCorrections<\n          gh::BoundaryCorrections::UpwindPenalty<3>,\n          CurvedScalarWave::BoundaryCorrections::UpwindPenalty<3>>>>(\n      make_not_null(&gen), \"DemandOutgoingCharSpeeds\",\n      tuples::TaggedTuple<helpers::Tags::PythonFunctionForErrorMessage<>>{\n          \"error\"},\n      \"DemandOutgoingCharSpeeds:\\n\", Index<2>{5}, db::DataBox<tmpl::list<>>{},\n      tuples::TaggedTuple<helpers::Tags::Range<gh::Tags::ConstraintGamma1>>{\n          std::array{0.0, 1.0}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Evolution.Systems.ScalarTensor.BConds.DemandOutgoingCharSpeeds\",\n    \"[Unit][Evolution]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarTensor/BoundaryConditions/\"};\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/BoundaryCorrections/Test_ProductOfCorrections.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/ScalarTensor/BoundaryCorrections/ProductOfCorrections.hpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename DerivedCorrection, typename PackagedFieldTagList,\n          typename EvolvedTagList, typename TempTagList, typename VolumeTagList>\nstruct ComputeBoundaryCorrectionHelperImpl;\n\ntemplate <typename DerivedCorrection, typename... PackagedFieldTags,\n          typename... EvolvedTags, typename... TempTags, typename... VolumeTags>\nstruct ComputeBoundaryCorrectionHelperImpl<\n    DerivedCorrection, tmpl::list<PackagedFieldTags...>,\n    tmpl::list<EvolvedTags...>, tmpl::list<TempTags...>,\n    tmpl::list<VolumeTags...>> {\n  template <typename PackagedVariables, typename EvolvedVariables,\n            typename TempVariables,\n            typename VolumeVariables>\n  static double dg_package_data(\n      const gsl::not_null<PackagedVariables*> packaged_variables,\n      const EvolvedVariables& evolved_variables,\n      const TempVariables& temp_variables,\n      const VolumeVariables& volume_variables,\n      const tnsr::i<DataVector, 3, Frame::Inertial>& normal_covector,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& normal_vector,\n      const std::optional<tnsr::I<DataVector, 3, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n      const DerivedCorrection& derived_correction) {\n    return derived_correction.dg_package_data(\n        make_not_null(&get<PackagedFieldTags>(*packaged_variables))...,\n        get<EvolvedTags>(evolved_variables)...,\n        get<TempTags>(temp_variables)..., normal_covector, normal_vector,\n        mesh_velocity, normal_dot_mesh_velocity,\n        get<VolumeTags>(volume_variables)...);\n  }\n\n  template <typename EvolvedVariables, typename PackagedVariables>\n  static void dg_boundary_terms(\n      const gsl::not_null<EvolvedVariables*> boundary_corrections,\n      const PackagedVariables& internal_packaged_fields,\n      const PackagedVariables& external_packaged_fields,\n      dg::Formulation dg_formulation,\n      const DerivedCorrection& derived_correction) {\n    derived_correction.dg_boundary_terms(\n        make_not_null(&get<EvolvedTags>(*boundary_corrections))...,\n        get<PackagedFieldTags>(internal_packaged_fields)...,\n        get<PackagedFieldTags>(external_packaged_fields)..., dg_formulation);\n  }\n};\n\ntemplate <typename DerivedCorrection, typename EvolvedTagList\n          >\nusing ComputeBoundaryCorrectionHelper = ComputeBoundaryCorrectionHelperImpl<\n    DerivedCorrection, typename DerivedCorrection::dg_package_field_tags,\n    EvolvedTagList,\n    typename DerivedCorrection::dg_package_data_temporary_tags,\n    typename DerivedCorrection::dg_package_data_volume_tags>;\n\ntemplate <typename DerivedGhCorrection, typename DerivedScalarCorrection>\nvoid test_boundary_correction_combination(\n    const DerivedGhCorrection& derived_gh_correction,\n    const DerivedScalarCorrection& derived_scalar_correction,\n    const ScalarTensor::BoundaryCorrections::ProductOfCorrections<\n        DerivedGhCorrection, DerivedScalarCorrection>&\n        derived_product_correction,\n    const dg::Formulation formulation) {\n  CHECK(derived_product_correction.gh_correction() == derived_gh_correction);\n  CHECK(derived_product_correction.scalar_correction() ==\n        derived_scalar_correction);\n  using gh_variables_tags = typename gh::System<3>::variables_tag::tags_list;\n  using scalar_variables_tags =\n      typename CurvedScalarWave::System<3>::variables_tag::tags_list;\n  using evolved_variables_type =\n      Variables<tmpl::append<gh_variables_tags, scalar_variables_tags>>;\n\n  using derived_product_correction_type =\n      ScalarTensor::BoundaryCorrections::ProductOfCorrections<\n          DerivedGhCorrection, DerivedScalarCorrection>;\n\n  using packaged_variables_type = Variables<\n      typename derived_product_correction_type::dg_package_field_tags>;\n\n  using temporary_variables_type = Variables<\n      tmpl::remove_duplicates<typename derived_product_correction_type::\n                                  dg_package_data_temporary_tags>>;\n\n  using volume_variables_type = Variables<\n      typename derived_product_correction_type::dg_package_data_volume_tags>;\n\n  const size_t element_size = 10_st;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  packaged_variables_type expected_packaged_variables{element_size};\n  packaged_variables_type packaged_variables{element_size};\n\n  const auto evolved_variables =\n      make_with_random_values<evolved_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n\n  const auto temporary_variables =\n      make_with_random_values<temporary_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  volume_variables_type volume_variables{element_size};\n  if constexpr (tmpl::size<typename derived_product_correction_type::\n                               dg_package_data_volume_tags>::value > 0) {\n    fill_with_random_values(make_not_null(&volume_variables),\n                            make_not_null(&gen), make_not_null(&dist));\n  }\n\n  const auto normal_covector =\n      make_with_random_values<tnsr::i<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto normal_vector =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto mesh_velocity =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto normal_dot_mesh_velocity =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n\n  double expected_package_gh_result =\n      ComputeBoundaryCorrectionHelper<DerivedGhCorrection, gh_variables_tags>::\n          dg_package_data(make_not_null(&expected_packaged_variables),\n                          evolved_variables, temporary_variables,\n                          volume_variables, normal_covector, normal_vector,\n                          mesh_velocity, normal_dot_mesh_velocity,\n                          derived_gh_correction);\n  double expected_package_scalar_result =\n      ComputeBoundaryCorrectionHelper<DerivedScalarCorrection,\n                                      scalar_variables_tags>::\n          dg_package_data(make_not_null(&expected_packaged_variables),\n                          evolved_variables, temporary_variables,\n                          volume_variables, normal_covector, normal_vector,\n                          mesh_velocity, normal_dot_mesh_velocity,\n                          derived_scalar_correction);\n\n  double package_combined_result = ComputeBoundaryCorrectionHelper<\n      derived_product_correction_type,\n      tmpl::append<gh_variables_tags, scalar_variables_tags>>::\n      dg_package_data(make_not_null(&packaged_variables), evolved_variables,\n                      temporary_variables, volume_variables, normal_covector,\n                      normal_vector, mesh_velocity, normal_dot_mesh_velocity,\n                      derived_product_correction);\n  CHECK(approx(SINGLE_ARG(std::max(expected_package_gh_result,\n                                   expected_package_scalar_result))) ==\n        package_combined_result);\n  CHECK_VARIABLES_APPROX(packaged_variables, expected_packaged_variables);\n\n  auto serialized_and_deserialized_correction =\n      serialize_and_deserialize(derived_product_correction);\n\n  package_combined_result = ComputeBoundaryCorrectionHelper<\n      derived_product_correction_type,\n      tmpl::append<gh_variables_tags, scalar_variables_tags>>::\n      dg_package_data(make_not_null(&packaged_variables), evolved_variables,\n                      temporary_variables, volume_variables, normal_covector,\n                      normal_vector, mesh_velocity, normal_dot_mesh_velocity,\n                      serialized_and_deserialized_correction);\n  CHECK(approx(SINGLE_ARG(std::max(expected_package_gh_result,\n                                   expected_package_scalar_result))) ==\n        package_combined_result);\n  CHECK_VARIABLES_APPROX(packaged_variables, expected_packaged_variables);\n\n  const auto external_packaged_fields =\n      make_with_random_values<packaged_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n  const auto internal_packaged_fields =\n      make_with_random_values<packaged_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), element_size);\n\n  evolved_variables_type expected_boundary_correction{element_size};\n  evolved_variables_type boundary_correction{element_size};\n  ComputeBoundaryCorrectionHelper<DerivedGhCorrection, gh_variables_tags>::\n      dg_boundary_terms(make_not_null(&expected_boundary_correction),\n                        internal_packaged_fields, external_packaged_fields,\n                        formulation, derived_gh_correction);\n  ComputeBoundaryCorrectionHelper<DerivedScalarCorrection,\n                                  scalar_variables_tags>::\n      dg_boundary_terms(make_not_null(&expected_boundary_correction),\n                        internal_packaged_fields, external_packaged_fields,\n                        formulation, derived_scalar_correction);\n\n  ComputeBoundaryCorrectionHelper<\n      derived_product_correction_type,\n      tmpl::append<gh_variables_tags, scalar_variables_tags>>::\n      dg_boundary_terms(make_not_null(&boundary_correction),\n                        internal_packaged_fields, external_packaged_fields,\n                        formulation, derived_product_correction);\n  CHECK_VARIABLES_APPROX(boundary_correction, expected_boundary_correction);\n\n  ComputeBoundaryCorrectionHelper<\n      derived_product_correction_type,\n      tmpl::append<gh_variables_tags, scalar_variables_tags>>::\n      dg_boundary_terms(make_not_null(&boundary_correction),\n                        internal_packaged_fields, external_packaged_fields,\n                        formulation, serialized_and_deserialized_correction);\n  CHECK_VARIABLES_APPROX(boundary_correction, expected_boundary_correction);\n\n  PUPable_reg(\n      SINGLE_ARG(ScalarTensor::BoundaryCorrections::ProductOfCorrections<\n                 DerivedGhCorrection, DerivedScalarCorrection>));\n\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<\n      ScalarTensor::System>(\n      make_not_null(&gen), derived_product_correction,\n      Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss}, {},\n      {});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n \"Unit.Evolution.Systems.ScalarTensor.BoundaryCorrections.ProductOfCorrections\",\n    \"[Unit][Evolution]\") {\n  {\n    INFO(\"Product correction UpwindPenalty and UpwindPenalty\");\n    using ProductUpwindPenaltyGhAndUpwindPenaltyScalar =\n        ScalarTensor::BoundaryCorrections::ProductOfCorrections<\n            gh::BoundaryCorrections::UpwindPenalty<3>,\n            CurvedScalarWave::BoundaryCorrections::UpwindPenalty<3>>;\n    CurvedScalarWave::BoundaryCorrections::UpwindPenalty<3> scalar_correction{};\n    gh::BoundaryCorrections::UpwindPenalty<3> gh_correction{};\n    TestHelpers::test_factory_creation<\n        evolution::BoundaryCorrection,\n        ProductUpwindPenaltyGhAndUpwindPenaltyScalar>(\n        \"ProductUpwindPenaltyGHAndUpwindPenaltyScalar:\\n\"\n        \"  UpwindPenaltyGH:\\n\"\n        \"  UpwindPenaltyScalar:\");\n    const ProductUpwindPenaltyGhAndUpwindPenaltyScalar\n        product_boundary_correction{gh_correction, scalar_correction};\n    for (const auto formulation :\n         {dg::Formulation::StrongInertial, dg::Formulation::WeakInertial}) {\n      test_boundary_correction_combination<\n          gh::BoundaryCorrections::UpwindPenalty<3>,\n          CurvedScalarWave::BoundaryCorrections::UpwindPenalty<3>>(\n          gh_correction, scalar_correction, product_boundary_correction,\n          formulation);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarTensor\")\n\nset(LIBRARY_SOURCES\n  Actions/Test_SetInitialData.cpp\n  BoundaryConditions/Test_ConstraintPreserving.cpp\n  BoundaryConditions/Test_DemandOutgoingCharSpeeds.cpp\n  BoundaryCorrections/Test_ProductOfCorrections.cpp\n  Test_Characteristics.cpp\n  Test_Constraints.cpp\n  Test_Tags.cpp\n  Test_TimeDerivative.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Framework\n  GeneralRelativityHelpers\n  GhScalarTensorAnalyticData\n  ScalarTensor\n  ScalarTensorAnalyticData\n  ScalarTensorPointwise\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Characteristics.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Characteristics.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n\nnamespace {\nvoid check_max_char_speed(const DataVector& used_for_size) {\n  MAKE_GENERATOR(gen);\n\n  const Scalar<DataVector> gamma_1{used_for_size.size(), 0.0};\n  const auto lapse = TestHelpers::gr::random_lapse(&gen, used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<3>(&gen, used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<3>(&gen, used_for_size);\n  const Scalar<DataVector> gamma_1_scalar{used_for_size.size(), 0.0};\n\n  double max_char_speed = std::numeric_limits<double>::signaling_NaN();\n  ScalarTensor::Tags::ComputeLargestCharacteristicSpeed<\n      Frame::Inertial>::function(make_not_null(&max_char_speed), gamma_1, lapse,\n                                 shift, spatial_metric, gamma_1_scalar);\n\n  double gh_max_char_speed = std::numeric_limits<double>::signaling_NaN();\n  gh::Tags::ComputeLargestCharacteristicSpeed<3, Frame::Inertial>::function(\n      make_not_null(&gh_max_char_speed), gamma_1, lapse, shift, spatial_metric);\n\n  double scalar_max_char_speed = std::numeric_limits<double>::signaling_NaN();\n  CurvedScalarWave::Tags::ComputeLargestCharacteristicSpeed<3>::function(\n      make_not_null(&scalar_max_char_speed), gamma_1_scalar, lapse, shift,\n      spatial_metric);\n\n  CHECK(max_char_speed == std::max(gh_max_char_speed, scalar_max_char_speed));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarTensor.MaxCharSpeed\",\n                  \"[Unit][Evolution]\") {\n  check_max_char_speed(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/Test_Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ScalarTensor/Constraints.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarTensor.Constraints\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::FConstraintCompute<3, Frame::Inertial>>(\n      \"FConstraint\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct ArbitraryFrame;\n}  // namespace\n\ntemplate <size_t Dim, typename Frame>\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<\n      ScalarTensor::Tags::TraceReversedStressEnergy<DataVector, Dim, Frame>>(\n      \"TraceReversedStressEnergy\");\n  TestHelpers::db::test_simple_tag<ScalarTensor::Tags::ScalarMass>(\n      \"ScalarMass\");\n  TestHelpers::db::test_simple_tag<ScalarTensor::Tags::ScalarSource>(\n      \"ScalarSource\");\n}\n\ntemplate <size_t Dim>\nvoid test_prefix_tags() {\n  TestHelpers::db::test_prefix_tag<\n      ScalarTensor::Tags::Csw<CurvedScalarWave::Tags::Psi>>(\"Csw(Psi)\");\n  TestHelpers::db::test_prefix_tag<\n      ScalarTensor::Tags::Csw<CurvedScalarWave::Tags::Pi>>(\"Csw(Pi)\");\n  TestHelpers::db::test_prefix_tag<\n      ScalarTensor::Tags::Csw<CurvedScalarWave::Tags::Phi<Dim>>>(\"Csw(Phi)\");\n}\n\ntemplate <size_t Dim>\nvoid test_compute_tags() {\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::CswCompute<CurvedScalarWave::Tags::Psi>>(\"Csw(Psi)\");\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::CswCompute<CurvedScalarWave::Tags::Pi>>(\"Csw(Pi)\");\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::CswCompute<CurvedScalarWave::Tags::Phi<Dim>>>(\n      \"Csw(Phi)\");\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::CswOneIndexConstraintCompute<Dim>>(\n      \"Csw(OneIndexConstraint)\");\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::CswTwoIndexConstraintCompute<Dim>>(\n      \"Csw(TwoIndexConstraint)\");\n}\n\ntemplate <size_t Dim>\nvoid test_tags() {\n  test_simple_tags<Dim, ArbitraryFrame>();\n  test_prefix_tags<Dim>();\n  test_compute_tags<Dim>();\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarTensor.Tags\",\n                  \"[Unit][Evolution]\") {\n  test_tags<1_st>();\n  test_tags<2_st>();\n  test_tags<3_st>();\n  TestHelpers::test_option_tag<ScalarTensor::OptionTags::ScalarMass>(\"1.0\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarTensor/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/System.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/TimeDerivative.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/DampedHarmonic.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Tags/GaugeCondition.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/System.hpp\"\n#include \"Evolution/Systems/ScalarTensor/TimeDerivative.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GhScalarTensor/Factory.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarTensor.TimeDerivative\",\n                  \"[Unit][Evolution]\") {\n  // Backreaction of the scalar on the metric is currently disabled\n  static constexpr bool backreaction_is_enabled = false;\n\n  using gh_dt_variables_tags = ScalarTensor::TimeDerivative::gh_dt_tags;\n  using scalar_dt_variables_tags = ScalarTensor::TimeDerivative::scalar_dt_tags;\n  using dt_variables_type =\n      Variables<tmpl::append<gh_dt_variables_tags, scalar_dt_variables_tags>>;\n\n  using temp_variables_type =\n      Variables<typename ScalarTensor::TimeDerivative::temporary_tags>;\n\n  using gh_gradient_tags = tmpl::transform<\n      ScalarTensor::TimeDerivative::gh_gradient_tags,\n      tmpl::bind<::Tags::deriv, tmpl::_1, tmpl::pin<tmpl::size_t<3>>,\n                 tmpl::pin<Frame::Inertial>>>;\n  using scalar_gradient_tags = tmpl::transform<\n      ScalarTensor::TimeDerivative::scalar_gradient_tags,\n      tmpl::bind<::Tags::deriv, tmpl::_1, tmpl::pin<tmpl::size_t<3>>,\n                 tmpl::pin<Frame::Inertial>>>;\n  using gradient_variables_type =\n      Variables<tmpl::append<gh_gradient_tags, scalar_gradient_tags>>;\n\n  using gh_arg_tags = ScalarTensor::TimeDerivative::gh_arg_tags;\n  using scalar_arg_tags = ScalarTensor::TimeDerivative::scalar_arg_tags;\n  using arg_variables_type = tuples::tagged_tuple_from_typelist<\n      tmpl::append<gh_arg_tags, scalar_arg_tags,\n                   tmpl::list<ScalarTensor::Tags::ScalarSource>>>;\n\n  const size_t element_size = 10;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.1, 1.0);\n\n  dt_variables_type expected_dt_variables{element_size};\n  dt_variables_type dt_variables{element_size};\n\n  temp_variables_type expected_temp_variables{element_size};\n  temp_variables_type temp_variables{element_size};\n\n  const auto gradient_variables =\n      make_with_random_values<gradient_variables_type>(\n          make_not_null(&gen), make_not_null(&dist), DataVector{element_size});\n\n  arg_variables_type arg_variables;\n  tmpl::for_each<tmpl::append<gh_arg_tags, scalar_arg_tags,\n                              tmpl::list<ScalarTensor::Tags::ScalarSource>>>(\n      [&gen, &dist, &arg_variables](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        if constexpr (std::is_same_v<typename tag::type,\n                                     std::optional<tnsr::I<DataVector, 3,\n                                                           Frame::Inertial>>>) {\n          tuples::get<tag>(arg_variables) = make_with_random_values<\n              typename tnsr::I<DataVector, 3, Frame::Inertial>>(\n              make_not_null(&gen), make_not_null(&dist),\n              DataVector{element_size});\n        } else if constexpr (tt::is_a_v<Tensor, typename tag::type>) {\n          tuples::get<tag>(arg_variables) =\n              make_with_random_values<typename tag::type>(\n                  make_not_null(&gen), make_not_null(&dist),\n                  DataVector{element_size});\n        }\n      });\n  get<gh::gauges::Tags::GaugeCondition>(arg_variables) =\n      std::make_unique<gh::gauges::DampedHarmonic>(\n          100., std::array{1.2, 1.5, 1.7}, std::array{2, 4, 6});\n\n  // ensure that the signature of the metric is correct\n  {\n    auto& metric =\n        tuples::get<gr::Tags::SpacetimeMetric<DataVector, 3>>(arg_variables);\n    get<0, 0>(metric) += -2.0;\n    for (size_t i = 0; i < 3; ++i) {\n      metric.get(i + 1, i + 1) += 4.0;\n      metric.get(i + 1, 0) *= 0.01;\n    }\n  }\n\n  // The logic of the test is the following:\n  // We compute the individual time derivative functions for each system\n  // and then compare the results with the time derivative function for the\n  // combined system\n\n  // The time derivative function for GeneralizedHarmonic is\n  gh::TimeDerivative<gh::ScalarTensor::AnalyticData::all_analytic_data, 3>::\n      apply(\n          // GH evolved variables\n          make_not_null(\n              &get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n                  expected_dt_variables)),\n          make_not_null(&get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(\n              expected_dt_variables)),\n          make_not_null(&get<::Tags::dt<gh::Tags::Phi<DataVector, 3>>>(\n              expected_dt_variables)),\n          // GH temporaries\n          make_not_null(\n              &get<gh::Tags::ConstraintGamma1>(expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::ConstraintGamma2>(expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::GaugeH<DataVector, 3>>(expected_temp_variables)),\n          make_not_null(&get<gh::Tags::SpacetimeDerivGaugeH<DataVector, 3>>(\n              expected_temp_variables)),\n          make_not_null(&get<gh::Tags::Gamma1Gamma2>(expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::HalfPiTwoNormals>(expected_temp_variables)),\n          make_not_null(&get<gh::Tags::NormalDotOneIndexConstraint>(\n              expected_temp_variables)),\n          make_not_null(&get<gh::Tags::Gamma1Plus1>(expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::PiOneNormal<3>>(expected_temp_variables)),\n          make_not_null(&get<gh::Tags::GaugeConstraint<DataVector, 3>>(\n              expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::HalfPhiTwoNormals<3>>(expected_temp_variables)),\n          make_not_null(&get<gh::Tags::ShiftDotThreeIndexConstraint<3>>(\n              expected_temp_variables)),\n          make_not_null(&get<gh::Tags::MeshVelocityDotThreeIndexConstraint<3>>(\n              expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::PhiOneNormal<3>>(expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::PiSecondIndexUp<3>>(expected_temp_variables)),\n          make_not_null(&get<gh::Tags::ThreeIndexConstraint<DataVector, 3>>(\n              expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::PhiFirstIndexUp<3>>(expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::PhiThirdIndexUp<3>>(expected_temp_variables)),\n          make_not_null(\n              &get<gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<3>>(\n                  expected_temp_variables)),\n          make_not_null(\n              &get<gr::Tags::Lapse<DataVector>>(expected_temp_variables)),\n          make_not_null(\n              &get<gr::Tags::Shift<DataVector, 3>>(expected_temp_variables)),\n          make_not_null(&get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n              expected_temp_variables)),\n          make_not_null(&get<gr::Tags::DetSpatialMetric<DataVector>>(\n              expected_temp_variables)),\n          make_not_null(&get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n              expected_temp_variables)),\n          make_not_null(&get<gr::Tags::InverseSpacetimeMetric<DataVector, 3>>(\n              expected_temp_variables)),\n          make_not_null(\n              &get<gr::Tags::SpacetimeChristoffelFirstKind<DataVector, 3>>(\n                  expected_temp_variables)),\n          make_not_null(\n              &get<gr::Tags::SpacetimeChristoffelSecondKind<DataVector, 3>>(\n                  expected_temp_variables)),\n          make_not_null(\n              &get<gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, 3>>(\n                  expected_temp_variables)),\n          make_not_null(&get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(\n              expected_temp_variables)),\n          // GH gradient tags\n          get<::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                            tmpl::size_t<3>, Frame::Inertial>>(\n              gradient_variables),\n          get<::Tags::deriv<gh::Tags::Pi<DataVector, 3>, tmpl::size_t<3>,\n                            Frame::Inertial>>(gradient_variables),\n          get<::Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                            Frame::Inertial>>(gradient_variables),\n          // GH argument tags\n          tuples::get<gr::Tags::SpacetimeMetric<DataVector, 3>>(arg_variables),\n          tuples::get<gh::Tags::Pi<DataVector, 3>>(arg_variables),\n          tuples::get<gh::Tags::Phi<DataVector, 3>>(arg_variables),\n          tuples::get<gh::Tags::ConstraintGamma0>(arg_variables),\n          tuples::get<gh::Tags::ConstraintGamma1>(arg_variables),\n          tuples::get<gh::Tags::ConstraintGamma2>(arg_variables),\n\n          *tuples::get<gh::gauges::Tags::GaugeCondition>(arg_variables),\n\n          tuples::get<domain::Tags::Mesh<3>>(arg_variables),\n          tuples::get<::Tags::Time>(arg_variables),\n          tuples::get<domain::Tags::Coordinates<3, Frame::Inertial>>(\n              arg_variables),\n          tuples::get<domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                                    Frame::Inertial>>(\n              arg_variables),\n          tuples::get<domain::Tags::MeshVelocity<3, Frame::Inertial>>(\n              arg_variables));\n\n  // The time derivative function for CurvedScalarWave is\n  CurvedScalarWave::TimeDerivative<3>::apply(\n      // Scalar evolved variables\n      make_not_null(\n          &get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(expected_dt_variables)),\n      make_not_null(\n          &get<::Tags::dt<CurvedScalarWave::Tags::Pi>>(expected_dt_variables)),\n      make_not_null(&get<::Tags::dt<CurvedScalarWave::Tags::Phi<3>>>(\n          expected_dt_variables)),\n      // Scalar temporaries\n      make_not_null(&get<gr::Tags::Lapse<DataVector>>(expected_temp_variables)),\n      make_not_null(\n          &get<gr::Tags::Shift<DataVector, 3>>(expected_temp_variables)),\n      make_not_null(&get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n          expected_temp_variables)),\n      make_not_null(&get<CurvedScalarWave::Tags::ConstraintGamma1>(\n          expected_temp_variables)),\n      make_not_null(&get<CurvedScalarWave::Tags::ConstraintGamma2>(\n          expected_temp_variables)),\n      // Scalar gradient tags\n      get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                        Frame::Inertial>>(gradient_variables),\n      get<::Tags::deriv<CurvedScalarWave::Tags::Pi, tmpl::size_t<3>,\n                        Frame::Inertial>>(gradient_variables),\n      get<::Tags::deriv<CurvedScalarWave::Tags::Phi<3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(gradient_variables),\n      // Scalar argument tags\n      tuples::get<CurvedScalarWave::Tags::Pi>(arg_variables),\n      tuples::get<CurvedScalarWave::Tags::Phi<3>>(arg_variables),\n\n      tuples::get<gr::Tags::Lapse<DataVector>>(arg_variables),\n      tuples::get<gr::Tags::Shift<DataVector, 3>>(arg_variables),\n      tuples::get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                                Frame::Inertial>>(arg_variables),\n      tuples::get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                                Frame::Inertial>>(arg_variables),\n      tuples::get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(arg_variables),\n      tuples::get<gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>(\n          arg_variables),\n      tuples::get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(arg_variables),\n      tuples::get<CurvedScalarWave::Tags::ConstraintGamma1>(arg_variables),\n      tuples::get<CurvedScalarWave::Tags::ConstraintGamma2>(arg_variables));\n\n  // We compute the trace-reversed stress energy tensor for the expected\n  // variables\n  ScalarTensor::trace_reversed_stress_energy(\n      make_not_null(\n          &get<ScalarTensor::Tags::TraceReversedStressEnergy<\n              DataVector, 3, ::Frame::Inertial>>(expected_temp_variables)),\n      tuples::get<CurvedScalarWave::Tags::Pi>(arg_variables),\n      tuples::get<CurvedScalarWave::Tags::Phi<3>>(arg_variables),\n      tuples::get<gr::Tags::Lapse<DataVector>>(arg_variables),\n      tuples::get<gr::Tags::Shift<DataVector, 3, ::Frame::Inertial>>(\n          arg_variables));\n\n  // When we have backreaction we also need to compute and apply the correction\n  // to dt pi for the expected variables\n  if constexpr (backreaction_is_enabled) {\n    ScalarTensor::add_stress_energy_term_to_dt_pi(\n        make_not_null(&get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(\n            expected_dt_variables)),\n        get<ScalarTensor::Tags::TraceReversedStressEnergy<DataVector, 3,\n                                                          ::Frame::Inertial>>(\n            expected_temp_variables),\n        tuples::get<gr::Tags::Lapse<DataVector>>(arg_variables));\n  }\n\n  ScalarTensor::add_scalar_source_to_dt_pi_scalar(\n      make_not_null(\n          &get<::Tags::dt<CurvedScalarWave::Tags::Pi>>(expected_dt_variables)),\n      tuples::get<ScalarTensor::Tags::ScalarSource>(arg_variables),\n      tuples::get<gr::Tags::Lapse<DataVector>>(arg_variables));\n\n  // The time derivative function for the combined system is\n  ScalarTensor::TimeDerivative::apply(\n      // GH evolved variables\n      make_not_null(&get<::Tags::dt<gr::Tags::SpacetimeMetric<DataVector, 3>>>(\n          dt_variables)),\n      make_not_null(\n          &get<::Tags::dt<gh::Tags::Pi<DataVector, 3>>>(dt_variables)),\n      make_not_null(\n          &get<::Tags::dt<gh::Tags::Phi<DataVector, 3>>>(dt_variables)),\n      // Scalar evolved variables\n      make_not_null(\n          &get<::Tags::dt<CurvedScalarWave::Tags::Psi>>(dt_variables)),\n      make_not_null(&get<::Tags::dt<CurvedScalarWave::Tags::Pi>>(dt_variables)),\n      make_not_null(\n          &get<::Tags::dt<CurvedScalarWave::Tags::Phi<3>>>(dt_variables)),\n      // GH temporaries\n      make_not_null(&get<gh::Tags::ConstraintGamma1>(temp_variables)),\n      make_not_null(&get<gh::Tags::ConstraintGamma2>(temp_variables)),\n      make_not_null(&get<gh::Tags::GaugeH<DataVector, 3>>(temp_variables)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeDerivGaugeH<DataVector, 3>>(temp_variables)),\n      make_not_null(&get<gh::Tags::Gamma1Gamma2>(temp_variables)),\n      make_not_null(&get<gh::Tags::HalfPiTwoNormals>(temp_variables)),\n      make_not_null(\n          &get<gh::Tags::NormalDotOneIndexConstraint>(temp_variables)),\n      make_not_null(&get<gh::Tags::Gamma1Plus1>(temp_variables)),\n      make_not_null(&get<gh::Tags::PiOneNormal<3>>(temp_variables)),\n      make_not_null(\n          &get<gh::Tags::GaugeConstraint<DataVector, 3>>(temp_variables)),\n      make_not_null(&get<gh::Tags::HalfPhiTwoNormals<3>>(temp_variables)),\n      make_not_null(\n          &get<gh::Tags::ShiftDotThreeIndexConstraint<3>>(temp_variables)),\n      make_not_null(&get<gh::Tags::MeshVelocityDotThreeIndexConstraint<3>>(\n          temp_variables)),\n      make_not_null(&get<gh::Tags::PhiOneNormal<3>>(temp_variables)),\n      make_not_null(&get<gh::Tags::PiSecondIndexUp<3>>(temp_variables)),\n      make_not_null(\n          &get<gh::Tags::ThreeIndexConstraint<DataVector, 3>>(temp_variables)),\n      make_not_null(&get<gh::Tags::PhiFirstIndexUp<3>>(temp_variables)),\n      make_not_null(&get<gh::Tags::PhiThirdIndexUp<3>>(temp_variables)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<3>>(\n              temp_variables)),\n      make_not_null(&get<gr::Tags::Lapse<DataVector>>(temp_variables)),\n      make_not_null(&get<gr::Tags::Shift<DataVector, 3>>(temp_variables)),\n      make_not_null(\n          &get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(temp_variables)),\n      make_not_null(\n          &get<gr::Tags::DetSpatialMetric<DataVector>>(temp_variables)),\n      make_not_null(\n          &get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(temp_variables)),\n      make_not_null(&get<gr::Tags::InverseSpacetimeMetric<DataVector, 3>>(\n          temp_variables)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelFirstKind<DataVector, 3>>(\n              temp_variables)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelSecondKind<DataVector, 3>>(\n              temp_variables)),\n      make_not_null(\n          &get<gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, 3>>(\n              temp_variables)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(temp_variables)),\n      // Scalar temporaries\n      make_not_null(\n          &get<CurvedScalarWave::Tags::ConstraintGamma1>(temp_variables)),\n      make_not_null(\n          &get<CurvedScalarWave::Tags::ConstraintGamma2>(temp_variables)),\n      // Extra scalar temporaries\n      make_not_null(&get<ScalarTensor::Tags::TraceReversedStressEnergy<\n                        DataVector, 3, ::Frame::Inertial>>(temp_variables)),\n      // GH gradient tags\n      get<::Tags::deriv<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                        tmpl::size_t<3>, Frame::Inertial>>(gradient_variables),\n      get<::Tags::deriv<gh::Tags::Pi<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(gradient_variables),\n      get<::Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(gradient_variables),\n      // Scalar gradient tags\n      get<::Tags::deriv<CurvedScalarWave::Tags::Psi, tmpl::size_t<3>,\n                        Frame::Inertial>>(gradient_variables),\n      get<::Tags::deriv<CurvedScalarWave::Tags::Pi, tmpl::size_t<3>,\n                        Frame::Inertial>>(gradient_variables),\n      get<::Tags::deriv<CurvedScalarWave::Tags::Phi<3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(gradient_variables),\n      // GH argument tags\n      tuples::get<gr::Tags::SpacetimeMetric<DataVector, 3>>(arg_variables),\n\n      tuples::get<gh::Tags::Pi<DataVector, 3>>(arg_variables),\n      tuples::get<gh::Tags::Phi<DataVector, 3>>(arg_variables),\n      tuples::get<gh::Tags::ConstraintGamma0>(arg_variables),\n      tuples::get<gh::Tags::ConstraintGamma1>(arg_variables),\n      tuples::get<gh::Tags::ConstraintGamma2>(arg_variables),\n\n      *tuples::get<gh::gauges::Tags::GaugeCondition>(arg_variables),\n\n      tuples::get<domain::Tags::Mesh<3>>(arg_variables),\n      tuples::get<::Tags::Time>(arg_variables),\n      tuples::get<domain::Tags::Coordinates<3, Frame::Inertial>>(arg_variables),\n      tuples::get<domain::Tags::InverseJacobian<3, Frame::ElementLogical,\n                                                Frame::Inertial>>(\n          arg_variables),\n      tuples::get<domain::Tags::MeshVelocity<3, Frame::Inertial>>(\n          arg_variables),\n      // Scalar argument tags\n      tuples::get<CurvedScalarWave::Tags::Pi>(arg_variables),\n      tuples::get<CurvedScalarWave::Tags::Phi<3>>(arg_variables),\n\n      tuples::get<gr::Tags::Lapse<DataVector>>(arg_variables),\n      tuples::get<gr::Tags::Shift<DataVector, 3>>(arg_variables),\n      tuples::get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                                Frame::Inertial>>(arg_variables),\n      tuples::get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                                Frame::Inertial>>(arg_variables),\n      tuples::get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(arg_variables),\n      tuples::get<gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>(\n          arg_variables),\n      tuples::get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(arg_variables),\n      tuples::get<CurvedScalarWave::Tags::ConstraintGamma1>(arg_variables),\n      tuples::get<CurvedScalarWave::Tags::ConstraintGamma2>(arg_variables),\n\n      // Scalar extra argument tags\n      tuples::get<ScalarTensor::Tags::ScalarSource>(arg_variables));\n\n  // Finally we compare\n  CHECK_VARIABLES_APPROX(dt_variables, expected_dt_variables);\n  CHECK_VARIABLES_APPROX(temp_variables, expected_temp_variables);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    pi,\n    phi,\n    psi,\n    coords,\n    interior_gamma2,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    if (\n        not face_mesh_velocity is None\n        and -np.dot(face_mesh_velocity, outward_directed_normal_covector) < 0.0\n    ):\n        return (\n            \"Incoming characteristic speeds for constraint preserving \"\n            \"spherical radiation boundary.*\"\n        )\n    return None\n\n\ndef _dt_psi(face_mesh_velocity, outward_directed_normal_covector, phi, d_psi):\n    if face_mesh_velocity is None:\n        return 0.0\n\n    return -np.dot(\n        outward_directed_normal_covector, face_mesh_velocity\n    ) * np.dot(outward_directed_normal_covector, d_psi - phi)\n\n\ndef dt_pi_Sommerfeld(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    pi,\n    phi,\n    coords,\n    interior_gamma2,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    return (\n        np.einsum(\"ii\", d_phi)\n        + np.einsum(\n            \"i,i->\",\n            outward_directed_normal_covector,\n            interior_gamma2 * (d_psi - phi) - d_pi,\n        )\n        + interior_gamma2\n        * _dt_psi(\n            face_mesh_velocity, outward_directed_normal_covector, phi, d_psi\n        )\n    )\n\n\ndef dt_pi_FirstOrderBaylissTurkel(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    pi,\n    phi,\n    coords,\n    interior_gamma2,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    radius = np.sqrt(np.dot(coords, coords))\n    return (\n        np.einsum(\"ii\", d_phi)\n        + np.einsum(\n            \"i,i->\",\n            outward_directed_normal_covector,\n            interior_gamma2 * (d_psi - phi) - d_pi,\n        )\n        - pi / radius\n        + interior_gamma2\n        * _dt_psi(\n            face_mesh_velocity, outward_directed_normal_covector, phi, d_psi\n        )\n    )\n\n\ndef dt_pi_SecondOrderBaylissTurkel(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    pi,\n    phi,\n    coords,\n    interior_gamma2,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    radius = np.sqrt(np.dot(coords, coords))\n    return (\n        np.einsum(\"ii\", d_phi)\n        + (\n            np.dot(\n                outward_directed_normal_covector,\n                -2.0 * d_pi + interior_gamma2 * (d_psi - phi),\n            )\n            + np.einsum(\n                \"i,j,ij->\",\n                outward_directed_normal_covector,\n                outward_directed_normal_covector,\n                d_phi,\n            )\n            - 4.0 / radius * pi\n            + 4.0 / radius * np.dot(outward_directed_normal_covector, phi)\n            + 2.0 * psi / radius**2\n        )\n        + interior_gamma2\n        * _dt_psi(\n            face_mesh_velocity, outward_directed_normal_covector, phi, d_psi\n        )\n    )\n\n\ndef dt_phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    pi,\n    phi,\n    coords,\n    interior_gamma2,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    if face_mesh_velocity is None:\n        return 0.0 * d_psi\n\n    return -np.dot(\n        outward_directed_normal_covector, face_mesh_velocity\n    ) * np.einsum(\n        \"i, ij->j\",\n        outward_directed_normal_covector,\n        d_phi - np.transpose(d_phi),\n    )\n\n\ndef dt_psi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    pi,\n    phi,\n    coords,\n    interior_gamma2,\n    d_psi,\n    d_pi,\n    d_phi,\n):\n    assert interior_gamma2 >= 0.0  # make sure random gamma_2 is positive\n    return _dt_psi(\n        face_mesh_velocity, outward_directed_normal_covector, phi, d_psi\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryConditions/DirichletAnalytic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    interior_gamma2,\n    time,\n    dim,\n):\n    return None\n\n\ndef _gauss_amplitude():\n    return 0.9\n\n\ndef _gauss_width():\n    return 0.6\n\n\ndef _gauss_center():\n    return 0.0\n\n\ndef _center(dim):\n    center = [1.1]\n    if dim > 1:\n        center.append(0.1)\n        if dim > 2:\n            center.append(-0.9)\n    return np.asarray(center)\n\n\ndef _wave_vector(dim):\n    wave_vector = [0.1]\n    if dim > 1:\n        wave_vector.append(1.1)\n        if dim > 2:\n            wave_vector.append(2.1)\n    return np.asarray(wave_vector)\n\n\ndef _omega(dim):\n    wave_vector = _wave_vector(dim)\n    return np.sqrt(wave_vector.dot(wave_vector))\n\n\ndef _1d_u(coords, time, dim):\n    result = -_omega(dim) * time\n    for i in range(dim):\n        result += _wave_vector(dim)[i] * (coords[i] - _center(dim)[i])\n    return result\n\n\ndef _profile(u):\n    return _gauss_amplitude() * np.exp(\n        -((u - _gauss_center()) ** 2) / _gauss_width() ** 2\n    )\n\n\ndef _first_deriv(u):\n    return (\n        (-2.0 * _gauss_amplitude() / _gauss_width() ** 2)\n        * (u - _gauss_center())\n        * np.exp(-((u - _gauss_center()) ** 2) / _gauss_width() ** 2)\n    )\n\n\ndef pi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    interior_gamma2,\n    time,\n    dim,\n):\n    return _omega(dim) * _first_deriv(_1d_u(coords, time, dim))\n\n\ndef phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    interior_gamma2,\n    time,\n    dim,\n):\n    result = np.empty([dim])\n    du = _first_deriv(_1d_u(coords, time, dim))\n    for i in range(dim):\n        result[i] = _wave_vector(dim)[i] * du\n    return result\n\n\ndef psi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    interior_gamma2,\n    time,\n    dim,\n):\n    return _profile(_1d_u(coords, time, dim))\n\n\ndef constraint_gamma2(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    coords,\n    interior_gamma2,\n    time,\n    dim,\n):\n    assert interior_gamma2 >= 0.0  # make sure random gamma_2 is positive\n    return interior_gamma2\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryConditions/SphericalRadiation.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef error(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    phi,\n    coords,\n    interior_gamma2,\n):\n    if (\n        not face_mesh_velocity is None\n        and -np.dot(face_mesh_velocity, outward_directed_normal_covector) < 0.0\n    ):\n        return (\n            \"Incoming characteristic speeds for spherical radiation boundary.*\"\n        )\n    return None\n\n\ndef pi_Sommerfeld(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    phi,\n    coords,\n    interior_gamma2,\n):\n    return np.dot(outward_directed_normal_covector, phi)\n\n\ndef pi_BaylissTurkel(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    phi,\n    coords,\n    interior_gamma2,\n):\n    radius = np.sqrt(np.dot(coords, coords))\n    return np.dot(outward_directed_normal_covector, phi) + psi / radius\n\n\ndef phi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    phi,\n    coords,\n    interior_gamma2,\n):\n    return phi\n\n\ndef psi(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    phi,\n    coords,\n    interior_gamma2,\n):\n    return psi\n\n\ndef constraint_gamma2(\n    face_mesh_velocity,\n    outward_directed_normal_covector,\n    psi,\n    phi,\n    coords,\n    interior_gamma2,\n):\n    assert interior_gamma2 >= 0.0  # make sure random gamma_2 is positive\n    return interior_gamma2\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryConditions/Test_ConstraintPreservingSphericalRadiation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/ConstraintPreservingSphericalRadiation.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  MAKE_GENERATOR(gen);\n  for (const std::string& bc_string :\n       {\"Sommerfeld\"s, \"FirstOrderBaylissTurkel\"s,\n        \"SecondOrderBaylissTurkel\"s}) {\n    CAPTURE(bc_string);\n    helpers::test_boundary_condition_with_python<\n        ScalarWave::BoundaryConditions::ConstraintPreservingSphericalRadiation<\n            Dim>,\n        ScalarWave::BoundaryConditions::BoundaryCondition<Dim>,\n        ScalarWave::System<Dim>,\n        tmpl::list<ScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>>(\n        make_not_null(&gen), \"ConstraintPreservingSphericalRadiation\",\n        tuples::TaggedTuple<\n            helpers::Tags::PythonFunctionForErrorMessage<>,\n            helpers::Tags::PythonFunctionName<\n                ::Tags::dt<ScalarWave::Tags::Psi>>,\n            helpers::Tags::PythonFunctionName<::Tags::dt<ScalarWave::Tags::Pi>>,\n            helpers::Tags::PythonFunctionName<\n                ::Tags::dt<ScalarWave::Tags::Phi<Dim>>>>{\n            \"error\", \"dt_psi\", \"dt_pi_\" + bc_string, \"dt_phi\"},\n        \"ConstraintPreservingSphericalRadiation:\\n\"\n        \"  Type: \" +\n            bc_string,\n        Index<Dim - 1>{Dim == 1 ? 1 : 5}, db::DataBox<tmpl::list<>>{},\n        tuples::TaggedTuple<\n            helpers::Tags::Range<ScalarWave::Tags::ConstraintGamma2>>{\n            std::array{0.0, 1.0}});\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.ScalarWave.BoundaryConditions.ConstraintPreservingSphericalRadiation\",\n    \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarWave/BoundaryConditions/\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryConditions/Test_DirichletAnalytic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/DirichletAnalytic.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            ScalarWave::BoundaryConditions::BoundaryCondition<Dim>,\n            tmpl::list<ScalarWave::BoundaryConditions::DirichletAnalytic<Dim>>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   ScalarWave::Solutions::all_solutions<Dim>>,\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   MathFunctions::all_math_functions<1, Frame::Inertial>>>;\n  };\n};\n\ntemplate <size_t Dim>\nstruct ConvertPlaneWave {\n  using unpacked_container = int;\n  using packed_container = ScalarWave::Solutions::PlaneWave<Dim>;\n  using packed_type = double;\n\n  static packed_container create_container() {\n    std::array<double, Dim> wave_vector{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(wave_vector, i) = 0.1 + i;\n    }\n    std::array<double, Dim> center{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(center, i) = 1.1 - i;\n    }\n    return {wave_vector, center,\n            std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(\n                0.9, 0.6, 0.0)};\n  }\n\n  static inline unpacked_container unpack(const packed_container& /*packed*/,\n                                          const size_t /*grid_point_index*/) {\n    // No way of getting the args from the boundary condition.\n    return Dim;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container /*unpacked*/,\n                          const size_t /*grid_point_index*/) {\n    *packed = create_container();\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  register_classes_with_charm(ScalarWave::Solutions::all_solutions<Dim>{});\n  register_classes_with_charm(\n      MathFunctions::all_math_functions<1, Frame::Inertial>{});\n  CAPTURE(Dim);\n  MAKE_GENERATOR(gen);\n  const auto box_analytic_soln = db::create<db::AddSimpleTags<\n      Tags::Time,\n      Tags::AnalyticSolution<ScalarWave::Solutions::PlaneWave<Dim>>>>(\n      0.5, ConvertPlaneWave<Dim>::create_container());\n\n  helpers::test_boundary_condition_with_python<\n      ScalarWave::BoundaryConditions::DirichletAnalytic<Dim>,\n      ScalarWave::BoundaryConditions::BoundaryCondition<Dim>,\n      ScalarWave::System<Dim>,\n      tmpl::list<ScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>,\n      tmpl::list<ConvertPlaneWave<Dim>>,\n      tmpl::list<Tags::AnalyticSolution<ScalarWave::Solutions::PlaneWave<Dim>>>,\n      Metavariables<Dim>>(\n      make_not_null(&gen), \"DirichletAnalytic\",\n      tuples::TaggedTuple<\n          helpers::Tags::PythonFunctionForErrorMessage<>,\n          helpers::Tags::PythonFunctionName<ScalarWave::Tags::Psi>,\n          helpers::Tags::PythonFunctionName<ScalarWave::Tags::Pi>,\n          helpers::Tags::PythonFunctionName<ScalarWave::Tags::Phi<Dim>>,\n          helpers::Tags::PythonFunctionName<\n              ScalarWave::Tags::ConstraintGamma2>>{\"error\", \"psi\", \"pi\", \"phi\",\n                                                   \"constraint_gamma2\"},\n      \"DirichletAnalytic:\\n\"\n      \"  AnalyticPrescription:\\n\"\n      \"    PlaneWave:\\n\"\n      \"      WaveVector: [0.1\" +\n          (Dim > 1 ? std::string{\", 1.1\"} : std::string{}) +\n          (Dim > 2 ? std::string{\", 2.1\"} : std::string{}) +\n          \"]\\n\"\n          \"      Center: [1.1\" +\n          (Dim > 1 ? std::string{\", 0.1\"} : std::string{}) +\n          (Dim > 2 ? std::string{\", -0.9\"} : std::string{}) +\n          \"]\\n\"\n          \"      Profile:\\n\"\n          \"        Gaussian:\\n\"\n          \"          Amplitude: 0.9\\n\"\n          \"          Width: 0.6\\n\"\n          \"          Center: 0.0\\n\",\n      Index<Dim - 1>{Dim == 1 ? 1 : 5}, box_analytic_soln,\n      tuples::TaggedTuple<\n          helpers::Tags::Range<ScalarWave::Tags::ConstraintGamma2>>{\n          std::array{0.0, 1.0}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarWave.BoundaryConditions.DirichletAnalytic\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarWave/BoundaryConditions/\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryConditions/Test_Periodic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  helpers::test_periodic_condition<\n      domain::BoundaryConditions::Periodic<\n          ScalarWave::BoundaryConditions::BoundaryCondition<Dim>>,\n      ScalarWave::BoundaryConditions::BoundaryCondition<Dim>>(\"Periodic:\\n\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarWave.BoundaryConditions.Periodic\",\n                  \"[Unit][Evolution]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryConditions/Test_SphericalRadiation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/Factory.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryConditions/SphericalRadiation.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::evolution::dg;\n\nnamespace {\ntemplate <size_t Dim>\nvoid test() {\n  CAPTURE(Dim);\n  MAKE_GENERATOR(gen);\n  for (const std::string& bc_string : {\"Sommerfeld\"s, \"BaylissTurkel\"s}) {\n    CAPTURE(bc_string);\n    helpers::test_boundary_condition_with_python<\n        ScalarWave::BoundaryConditions::SphericalRadiation<Dim>,\n        ScalarWave::BoundaryConditions::BoundaryCondition<Dim>,\n        ScalarWave::System<Dim>,\n        tmpl::list<ScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>>(\n        make_not_null(&gen), \"SphericalRadiation\",\n        tuples::TaggedTuple<\n            helpers::Tags::PythonFunctionForErrorMessage<>,\n            helpers::Tags::PythonFunctionName<ScalarWave::Tags::Psi>,\n            helpers::Tags::PythonFunctionName<ScalarWave::Tags::Pi>,\n            helpers::Tags::PythonFunctionName<ScalarWave::Tags::Phi<Dim>>,\n            helpers::Tags::PythonFunctionName<\n                ScalarWave::Tags::ConstraintGamma2>>{\n            \"error\", \"psi\", \"pi_\" + bc_string, \"phi\", \"constraint_gamma2\"},\n        \"SphericalRadiation:\\n\"\n        \"  Type: \" +\n            bc_string,\n        Index<Dim - 1>{Dim == 1 ? 1 : 5}, db::DataBox<tmpl::list<>>{},\n        tuples::TaggedTuple<\n            helpers::Tags::Range<ScalarWave::Tags::ConstraintGamma2>>{\n            std::array{0.0, 1.0}});\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarWave.BoundaryConditions.SphericalRadiation\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarWave/BoundaryConditions/\"};\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryCorrections/Test_UpwindPenalty.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <pup.h>\n#include <string>\n\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> gen, const size_t num_pts) {\n  PUPable_reg(ScalarWave::BoundaryCorrections::UpwindPenalty<Dim>);\n  TestHelpers::evolution::dg::test_boundary_correction_conservation<\n      ScalarWave::System<Dim>>(\n      gen, ScalarWave::BoundaryCorrections::UpwindPenalty<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      ScalarWave::System<Dim>>(\n      gen, \"UpwindPenalty\", \"dg_package_data\", \"dg_boundary_terms\",\n      ScalarWave::BoundaryCorrections::UpwindPenalty<Dim>{},\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n\n  const auto upwind_penalty = TestHelpers::test_factory_creation<\n      evolution::BoundaryCorrection,\n      ScalarWave::BoundaryCorrections::UpwindPenalty<Dim>>(\"UpwindPenalty:\");\n\n  TestHelpers::evolution::dg::test_boundary_correction_with_python<\n      ScalarWave::System<Dim>>(\n      gen, \"UpwindPenalty\", \"dg_package_data\", \"dg_boundary_terms\",\n      dynamic_cast<const ScalarWave::BoundaryCorrections::UpwindPenalty<Dim>&>(\n          *upwind_penalty),\n      Mesh<Dim - 1>{num_pts, Spectral::Basis::Legendre,\n                    Spectral::Quadrature::Gauss},\n      {}, {});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ScalarWave.BoundaryCorrections.UpwindPenalty\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarWave/BoundaryCorrections\"};\n  MAKE_GENERATOR(gen);\n\n  test<1>(make_not_null(&gen), 1);\n  test<2>(make_not_null(&gen), 5);\n  test<3>(make_not_null(&gen), 5);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/BoundaryCorrections/UpwindPenalty.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef dg_package_data(\n    psi,\n    pi,\n    phi,\n    constraint_gamma2,\n    normal_covector,\n    mesh_velocity,\n    normal_dot_mesh_velocity,\n):\n    char_speeds = np.zeros([3])\n    char_speeds[0] = (\n        0.0 if normal_dot_mesh_velocity is None else -normal_dot_mesh_velocity\n    )\n    char_speeds[1] = (\n        1.0\n        if normal_dot_mesh_velocity is None\n        else 1.0 - normal_dot_mesh_velocity\n    )\n    char_speeds[2] = (\n        -1.0\n        if normal_dot_mesh_velocity is None\n        else -1.0 - normal_dot_mesh_velocity\n    )\n\n    char_speed_v_plus = np.asarray(\n        (pi + np.dot(phi, normal_covector) - constraint_gamma2 * psi)\n        * char_speeds[1]\n    )\n    char_speed_v_minus = np.asarray(\n        (pi - np.dot(phi, normal_covector) - constraint_gamma2 * psi)\n        * char_speeds[2]\n    )\n\n    return (\n        np.asarray(char_speeds[0] * psi),\n        np.asarray(\n            0.0 * phi\n            if normal_dot_mesh_velocity is None\n            else -normal_dot_mesh_velocity\n            * (phi - np.einsum(\"ijj->i\", normal_covector, normal_covector, phi))\n        ),\n        char_speed_v_plus,\n        char_speed_v_minus,\n        normal_covector * char_speed_v_plus,\n        normal_covector * char_speed_v_minus,\n        np.asarray(char_speeds[0] * psi * constraint_gamma2),\n        char_speeds,\n    )\n\n\ndef dg_boundary_terms(\n    int_char_speed_v_psi,\n    int_char_speed_v_zero,\n    int_char_speed_v_plus,\n    int_char_speed_v_minus,\n    int_char_speed_v_plus_times_normal,\n    int_char_speed_v_minus_times_normal,\n    int_char_speed_gamma2_v_psi,\n    int_char_speeds,\n    ext_char_speed_v_psi,\n    ext_char_speed_v_zero,\n    ext_char_speed_v_plus,\n    ext_char_speed_v_minus,\n    ext_char_speed_v_plus_times_normal,\n    ext_char_speed_v_minus_times_normal,\n    ext_char_speed_gamma2_v_psi,\n    ext_char_speeds,\n    use_strong_form,\n):\n    result_psi = int_char_speed_v_psi * 0.0\n    if ext_char_speeds[0] >= 0.0:\n        result_psi -= ext_char_speed_v_psi\n    if int_char_speeds[0] < 0.0:\n        result_psi -= int_char_speed_v_psi\n\n    result_pi = int_char_speed_v_psi * 0.0\n    # Add v^+ terms\n    if ext_char_speeds[1] > 0.0:\n        result_pi -= 0.5 * ext_char_speed_v_plus\n    if int_char_speeds[1] < 0.0:\n        result_pi -= 0.5 * int_char_speed_v_plus\n\n    # Add v^- terms\n    if ext_char_speeds[2] > 0.0:\n        result_pi -= 0.5 * ext_char_speed_v_minus\n    if int_char_speeds[2] < 0.0:\n        result_pi -= 0.5 * int_char_speed_v_minus\n\n    # Add v^\\Psi terms\n    if ext_char_speeds[0] > 0.0:\n        result_pi -= ext_char_speed_gamma2_v_psi\n    if int_char_speeds[0] < 0.0:\n        result_pi -= int_char_speed_gamma2_v_psi\n\n    result_phi = int_char_speed_v_zero * 0.0\n\n    # Add v^+ terms\n    if ext_char_speeds[1] >= 0.0:\n        result_phi -= 0.5 * ext_char_speed_v_plus_times_normal\n    if int_char_speeds[1] < 0.0:\n        result_phi -= 0.5 * int_char_speed_v_plus_times_normal\n\n    # Add v^- terms\n    if ext_char_speeds[2] >= 0.0:\n        result_phi += 0.5 * ext_char_speed_v_minus_times_normal\n    if int_char_speeds[2] < 0.0:\n        result_phi += 0.5 * int_char_speed_v_minus_times_normal\n\n    # Add v^0 terms\n    if ext_char_speeds[0] >= 0.0:\n        result_phi -= ext_char_speed_v_zero\n    if int_char_speeds[0] < 0.0:\n        result_phi -= int_char_speed_v_zero\n\n    return (\n        np.asarray(result_psi),\n        np.asarray(result_pi),\n        np.asarray(result_phi),\n    )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Test_ScalarWave)\n\nset(LIBRARY_SOURCES\n  BoundaryConditions/Test_ConstraintPreservingSphericalRadiation.cpp\n  BoundaryConditions/Test_DirichletAnalytic.cpp\n  BoundaryConditions/Test_Periodic.cpp\n  BoundaryConditions/Test_SphericalRadiation.cpp\n  BoundaryCorrections/Test_UpwindPenalty.cpp\n  Test_Characteristics.cpp\n  Test_Constraints.cpp\n  Test_EnergyDensity.cpp\n  Test_Equations.cpp\n  Test_MomentumDensity.cpp\n  Test_Tags.cpp\n  Test_TimeDerivative.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  MathFunctions\n  ScalarWave\n  Time\n  Utilities\n  WaveEquationSolutions\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Characteristics.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\n# Test functions for characteristic speeds\ndef char_speed_vpsi(unit_normal):\n    return 0.0\n\n\ndef char_speed_vzero(unit_normal):\n    return 0.0\n\n\ndef char_speed_vplus(unit_normal):\n    return 1.0\n\n\ndef char_speed_vminus(unit_normal):\n    return -1.0\n\n\n# End test functions for characteristic speeds\n\n\n# Test functions for characteristic fields\ndef char_field_vpsi(gamma2, psi, pi, phi, normal_one_form):\n    return psi\n\n\ndef char_field_vzero(gamma2, psi, pi, phi, normal_one_form):\n    normal_vector = normal_one_form\n    projection_tensor = np.identity(len(normal_vector)) - np.einsum(\n        \"i,j\", normal_one_form, normal_vector\n    )\n    return np.einsum(\"ij,j->i\", projection_tensor, phi)\n\n\ndef char_field_vplus(gamma2, psi, pi, phi, normal_one_form):\n    normal_vector = normal_one_form\n    phi_dot_normal = np.einsum(\"i,i->\", normal_vector, phi)\n    return pi + phi_dot_normal - (gamma2 * psi)\n\n\ndef char_field_vminus(gamma2, psi, pi, phi, normal_one_form):\n    normal_vector = normal_one_form\n    phi_dot_normal = np.einsum(\"i,i->\", normal_vector, phi)\n    return pi - phi_dot_normal - (gamma2 * psi)\n\n\n# End test functions for characteristic fields\n\n\n# Test functions for evolved fields\ndef evol_field_psi(gamma2, vpsi, vzero, vplus, vminus, normal_one_form):\n    return vpsi\n\n\ndef evol_field_pi(gamma2, vpsi, vzero, vplus, vminus, normal_one_form):\n    return 0.5 * (vplus + vminus) + gamma2 * vpsi\n\n\ndef evol_field_phi(gamma2, vpsi, vzero, vplus, vminus, normal_one_form):\n    return 0.5 * (vplus - vminus) * normal_one_form + vzero\n\n\n# End test functions for evolved fields\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Constraints.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef one_index_constraint(dpsi, phi):\n    return dpsi - phi\n\n\ndef two_index_constraint(dphi):\n    return dphi - np.transpose(dphi)\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/EnergyDensity.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Functions for testing EnergyDensity.cpp\n\n\ndef energy_density(pi, phi):\n    return 0.5 * (pi * pi + np.linalg.norm(phi) * np.linalg.norm(phi))\n\n\n# End functions for testing EnergyDensity.cpp\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/MomentumDensity.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Functions for testing MomentumDensity.cpp\n\n\ndef momentum_density(pi, phi):\n    return pi * phi\n\n\n# End functions for testing MomentumDensity.cpp\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Test_Characteristics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Evolution/Systems/ScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <size_t Index, size_t Dim>\nScalar<DataVector> speed_with_index(\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal) {\n  return Scalar<DataVector>{\n      ScalarWave::characteristic_speeds<Dim>(normal)[Index]};\n}\n\ntemplate <size_t Dim>\nvoid test_characteristic_speeds() {\n  TestHelpers::db::test_compute_tag<\n      ScalarWave::Tags::CharacteristicSpeedsCompute<Dim>>(\n      \"CharacteristicSpeeds\");\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(speed_with_index<0, Dim>, \"Characteristics\",\n                                    \"char_speed_vpsi\", {{{-10.0, 10.0}}},\n                                    used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<1, Dim>, \"Characteristics\",\n                                    \"char_speed_vzero\", {{{-10.0, 10.0}}},\n                                    used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<3, Dim>, \"Characteristics\",\n                                    \"char_speed_vminus\", {{{-10.0, 10.0}}},\n                                    used_for_size);\n  pypp::check_with_random_values<1>(speed_with_index<2, Dim>, \"Characteristics\",\n                                    \"char_speed_vplus\", {{{-10.0, 10.0}}},\n                                    used_for_size);\n}\n\n// Test return-by-reference char speeds by comparing to analytic solution\ntemplate <size_t Dim>\nvoid test_characteristic_speeds_analytic(\n    const size_t grid_size_each_dimension) {\n  // Setup mesh\n  Mesh<Dim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n  // Get ingredients\n  const size_t n_pts = mesh.number_of_grid_points();\n  // Outward 3-normal to the surface on which characteristic fields are needed\n  const tnsr::i<DataVector, Dim, Frame::Inertial> unit_normal_one_form{\n      DataVector(n_pts, 1. / sqrt(Dim))};\n\n  // Get characteristic speeds locally\n  const auto vpsi_speed_expected =\n      make_with_value<Scalar<DataVector>>(unit_normal_one_form, 0.);\n  const auto vzero_speed_expected =\n      make_with_value<Scalar<DataVector>>(unit_normal_one_form, 0.);\n  const auto vplus_speed_expected =\n      make_with_value<Scalar<DataVector>>(unit_normal_one_form, 1.);\n  const auto vminus_speed_expected =\n      make_with_value<Scalar<DataVector>>(unit_normal_one_form, -1.);\n\n  // Check that locally computed fields match returned ones\n  std::array<DataVector, 4> char_speeds{};\n  ScalarWave::Tags::CharacteristicSpeedsCompute<Dim>::function(\n      &char_speeds, unit_normal_one_form);\n  const auto& vpsi_speed = char_speeds[0];\n  const auto& vzero_speed = char_speeds[1];\n  const auto& vplus_speed = char_speeds[2];\n  const auto& vminus_speed = char_speeds[3];\n\n  CHECK_ITERABLE_APPROX(vpsi_speed_expected.get(), vpsi_speed);\n  CHECK_ITERABLE_APPROX(vzero_speed_expected.get(), vzero_speed);\n  CHECK_ITERABLE_APPROX(vplus_speed_expected.get(), vplus_speed);\n  CHECK_ITERABLE_APPROX(vminus_speed_expected.get(), vminus_speed);\n}\n}  // namespace\n\nnamespace {\ntemplate <typename Tag, size_t Dim>\ntypename Tag::type field_with_tag(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& psi,\n    const Scalar<DataVector>& pi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& phi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_one_form) {\n  Variables<tmpl::list<ScalarWave::Tags::VPsi, ScalarWave::Tags::VZero<Dim>,\n                       ScalarWave::Tags::VPlus, ScalarWave::Tags::VMinus>>\n      char_fields{};\n  ScalarWave::Tags::CharacteristicFieldsCompute<Dim>::function(\n      make_not_null(&char_fields), gamma_2, psi, pi, phi, normal_one_form);\n  return get<Tag>(char_fields);\n}\n\ntemplate <size_t Dim>\nvoid test_characteristic_fields() {\n  TestHelpers::db::test_compute_tag<\n      ScalarWave::Tags::CharacteristicFieldsCompute<Dim>>(\n      \"CharacteristicFields\");\n  const DataVector used_for_size(5);\n  // VPsi\n  pypp::check_with_random_values<1>(field_with_tag<ScalarWave::Tags::VPsi, Dim>,\n                                    \"Characteristics\", \"char_field_vpsi\",\n                                    {{{-10., 10.}}}, used_for_size);\n  // VZero\n  pypp::check_with_random_values<1>(\n      field_with_tag<ScalarWave::Tags::VZero<Dim>, Dim>, \"Characteristics\",\n      \"char_field_vzero\", {{{-10., 10.}}}, used_for_size, 1.e-11);\n  // VPlus\n  pypp::check_with_random_values<1>(\n      field_with_tag<ScalarWave::Tags::VPlus, Dim>, \"Characteristics\",\n      \"char_field_vplus\", {{{-10., 10.}}}, used_for_size);\n  // VMinus\n  pypp::check_with_random_values<1>(\n      field_with_tag<ScalarWave::Tags::VMinus, Dim>, \"Characteristics\",\n      \"char_field_vminus\", {{{-10., 10.}}}, used_for_size);\n}\n\n// Test return-by-reference char fields by comparing to analytic solution\ntemplate <size_t Dim, typename Solution>\nvoid test_characteristic_fields_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Set up grid\n  Mesh<Dim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-dependent solution.\n  const double t = 0.;\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t,\n                         tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                    ScalarWave::Tags::Phi<Dim>>{});\n  // Get ingredients\n  const size_t n_pts = mesh.number_of_grid_points();\n  const auto gamma_2 = make_with_value<Scalar<DataVector>>(x, 0.1);\n  const auto& psi = get<ScalarWave::Tags::Psi>(vars);\n  const auto& pi = get<ScalarWave::Tags::Pi>(vars);\n  const auto& phi = get<ScalarWave::Tags::Phi<Dim>>(vars);\n  // Outward 3-normal to the surface on which characteristic fields are needed\n  const auto unit_normal_one_form =\n      make_with_value<tnsr::i<DataVector, Dim, Frame::Inertial>>(\n          x, 1. / sqrt(Dim));\n\n  // Compute characteristic fields locally\n  const auto phi_dot_normal = dot_product(unit_normal_one_form, phi);\n\n  tnsr::i<DataVector, Dim, Frame::Inertial> phi_dot_projection_tensor{\n      DataVector(n_pts)};\n  for (size_t i = 0; i < Dim; ++i) {\n    phi_dot_projection_tensor.get(i) =\n        phi.get(i) - unit_normal_one_form.get(i) * get(phi_dot_normal);\n  }\n\n  const auto& vpsi_expected = psi;\n  const auto& vzero_expected = phi_dot_projection_tensor;\n  const Scalar<DataVector> vplus_expected{get(pi) + get(phi_dot_normal) -\n                                          get(gamma_2) * get(psi)};\n  const Scalar<DataVector> vminus_expected{get(pi) - get(phi_dot_normal) -\n                                           get(gamma_2) * get(psi)};\n\n  // Check that locally computed fields match returned ones\n  Variables<tmpl::list<ScalarWave::Tags::VPsi, ScalarWave::Tags::VZero<Dim>,\n                       ScalarWave::Tags::VPlus, ScalarWave::Tags::VMinus>>\n      uvars{};\n  ScalarWave::Tags::CharacteristicFieldsCompute<Dim>::function(\n      make_not_null(&uvars), gamma_2, psi, pi, phi, unit_normal_one_form);\n\n  const auto& vpsi = get<ScalarWave::Tags::VPsi>(uvars);\n  const auto& vzero = get<ScalarWave::Tags::VZero<Dim>>(uvars);\n  const auto& vplus = get<ScalarWave::Tags::VPlus>(uvars);\n  const auto& vminus = get<ScalarWave::Tags::VMinus>(uvars);\n\n  CHECK_ITERABLE_APPROX(vpsi_expected, vpsi);\n  CHECK_ITERABLE_APPROX(vzero_expected, vzero);\n  CHECK_ITERABLE_APPROX(vplus_expected, vplus);\n  CHECK_ITERABLE_APPROX(vminus_expected, vminus);\n}\n}  // namespace\n\nnamespace {\ntemplate <typename Tag, size_t Dim>\ntypename Tag::type evol_field_with_tag(\n    const Scalar<DataVector>& gamma_2, const Scalar<DataVector>& v_psi,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& v_zero,\n    const Scalar<DataVector>& v_plus, const Scalar<DataVector>& v_minus,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_one_form) {\n  Variables<tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                       ScalarWave::Tags::Phi<Dim>>>\n      evolved_vars{};\n  ScalarWave::Tags::EvolvedFieldsFromCharacteristicFieldsCompute<Dim>::function(\n      make_not_null(&evolved_vars), gamma_2, v_psi, v_zero, v_plus, v_minus,\n      unit_normal_one_form);\n  return get<Tag>(evolved_vars);\n}\n\ntemplate <size_t Dim>\nvoid test_evolved_from_characteristic_fields() {\n  TestHelpers::db::test_compute_tag<\n      ScalarWave::Tags::EvolvedFieldsFromCharacteristicFieldsCompute<Dim>>(\n      \"EvolvedFieldsFromCharacteristicFields\");\n  const DataVector used_for_size(5);\n  // Psi\n  pypp::check_with_random_values<1>(\n      evol_field_with_tag<ScalarWave::Tags::Psi, Dim>, \"Characteristics\",\n      \"evol_field_psi\", {{{-10., 10.}}}, used_for_size);\n  // Pi\n  pypp::check_with_random_values<1>(\n      evol_field_with_tag<ScalarWave::Tags::Pi, Dim>, \"Characteristics\",\n      \"evol_field_pi\", {{{-10., 10.}}}, used_for_size);\n  // Phi\n  pypp::check_with_random_values<1>(\n      evol_field_with_tag<ScalarWave::Tags::Phi<Dim>, Dim>, \"Characteristics\",\n      \"evol_field_phi\", {{{-10., 10.}}}, used_for_size);\n}\n\n// Test return-by-reference evolved fields by comparing to analytic solution\ntemplate <size_t Dim, typename Solution>\nvoid test_evolved_from_characteristic_fields_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Set up grid\n  Mesh<Dim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-dependent solution.\n  const double t = 0.;\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t,\n                         tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                    ScalarWave::Tags::Phi<Dim>>{});\n  // Get ingredients\n  const size_t n_pts = mesh.number_of_grid_points();\n  const auto gamma_2 = make_with_value<Scalar<DataVector>>(x, 0.1);\n  const auto& psi_expected = get<ScalarWave::Tags::Psi>(vars);\n  const auto& pi_expected = get<ScalarWave::Tags::Pi>(vars);\n  const auto& phi_expected = get<ScalarWave::Tags::Phi<Dim>>(vars);\n  // Outward 3-normal to the surface on which characteristic fields are needed\n  const auto unit_normal_one_form =\n      make_with_value<tnsr::i<DataVector, Dim, Frame::Inertial>>(\n          x, 1. / sqrt(Dim));\n\n  // Fundamental fields (psi, pi, phi) have already been computed locally.\n  // Now, check that these locally computed fields match returned ones\n  // First, compute characteristic fields locally\n  const auto phi_dot_normal = dot_product(unit_normal_one_form, phi_expected);\n\n  tnsr::i<DataVector, Dim, Frame::Inertial> phi_dot_projection_tensor{\n      DataVector(n_pts)};\n  for (size_t i = 0; i < Dim; ++i) {\n    phi_dot_projection_tensor.get(i) =\n        phi_expected.get(i) - unit_normal_one_form.get(i) * get(phi_dot_normal);\n  }\n\n  const auto& vpsi = psi_expected;\n  const auto& vzero = phi_dot_projection_tensor;\n  const Scalar<DataVector> vplus{get(pi_expected) + get(phi_dot_normal) -\n                                 get(gamma_2) * get(psi_expected)};\n  const Scalar<DataVector> vminus{get(pi_expected) - get(phi_dot_normal) -\n                                  get(gamma_2) * get(psi_expected)};\n  // Second, obtain evolved fields using compute tag\n  {\n    Variables<tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                         ScalarWave::Tags::Phi<Dim>>>\n        fields{};\n    ScalarWave::Tags::EvolvedFieldsFromCharacteristicFieldsCompute<\n        Dim>::function(make_not_null(&fields), gamma_2, vpsi, vzero, vplus,\n                       vminus, unit_normal_one_form);\n    const auto& psi = get<ScalarWave::Tags::Psi>(fields);\n    const auto& pi = get<ScalarWave::Tags::Pi>(fields);\n    const auto& phi = get<ScalarWave::Tags::Phi<Dim>>(fields);\n\n    CHECK_ITERABLE_APPROX(psi_expected, psi);\n    CHECK_ITERABLE_APPROX(pi_expected, pi);\n    CHECK_ITERABLE_APPROX(phi_expected, phi);\n  }\n  // Third, obtain evolved fields using function\n  {\n    const auto fields = ScalarWave::evolved_fields_from_characteristic_fields(\n        gamma_2, vpsi, vzero, vplus, vminus, unit_normal_one_form);\n    const auto& psi = get<ScalarWave::Tags::Psi>(fields);\n    const auto& pi = get<ScalarWave::Tags::Pi>(fields);\n    const auto& phi = get<ScalarWave::Tags::Phi<Dim>>(fields);\n\n    CHECK_ITERABLE_APPROX(psi_expected, psi);\n    CHECK_ITERABLE_APPROX(pi_expected, pi);\n    CHECK_ITERABLE_APPROX(phi_expected, phi);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarWave.Characteristics\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarWave/\"};\n\n  test_characteristic_speeds<1>();\n  test_characteristic_speeds<2>();\n  test_characteristic_speeds<3>();\n\n  test_characteristic_fields<1>();\n  test_characteristic_fields<2>();\n  test_characteristic_fields<3>();\n\n  test_evolved_from_characteristic_fields<1>();\n  test_evolved_from_characteristic_fields<2>();\n  test_evolved_from_characteristic_fields<3>();\n\n  // Test characteristics against 3D spherical wave\n  const size_t grid_size = 8;\n  const std::array<double, 3> lower_bound{{0.82, 1.22, 1.32}};\n  const std::array<double, 3> upper_bound{{0.78, 1.18, 1.28}};\n\n  const ScalarWave::Solutions::RegularSphericalWave spherical_wave_solution(\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(1., 1.,\n                                                                    0.));\n\n  test_characteristic_speeds_analytic<3>(grid_size);\n  test_characteristic_fields_analytic<3>(spherical_wave_solution, grid_size,\n                                         lower_bound, upper_bound);\n  test_evolved_from_characteristic_fields_analytic<3>(\n      spherical_wave_solution, grid_size, lower_bound, upper_bound);\n\n  // Test characteristics against 3D plane wave\n  const double kx = 1.5;\n  const double ky = -7.2;\n  const double kz = 2.7;\n  const double center_x = 2.4;\n  const double center_y = -4.8;\n  const double center_z = 8.4;\n  const ScalarWave::Solutions::PlaneWave<3> plane_wave_solution(\n      {{kx, ky, kz}}, {{center_x, center_y, center_z}},\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(3));\n\n  test_characteristic_speeds_analytic<3>(grid_size);\n  test_characteristic_fields_analytic<3>(plane_wave_solution, grid_size,\n                                         lower_bound, upper_bound);\n  test_evolved_from_characteristic_fields_analytic<3>(\n      plane_wave_solution, grid_size, lower_bound, upper_bound);\n\n  double largest_characteristic_speed =\n      std::numeric_limits<double>::signaling_NaN();\n  ScalarWave::Tags::ComputeLargestCharacteristicSpeed::function(\n      make_not_null(&largest_characteristic_speed));\n  CHECK(largest_characteristic_speed == 1.0);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Test_Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/ScalarWave/Constraints.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Test the return-by-value one-index constraint function using random values\ntemplate <size_t SpatialDim>\nvoid test_one_index_constraint_random(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::i<DataVector, SpatialDim, Frame::Inertial> (*)(\n          const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&,\n          const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&)>(\n          &ScalarWave::one_index_constraint<SpatialDim>),\n      \"Constraints\", \"one_index_constraint\", {{{-10.0, 10.0}}}, used_for_size);\n  TestHelpers::db::test_compute_tag<\n      ScalarWave::Tags::OneIndexConstraintCompute<SpatialDim>>(\n      \"OneIndexConstraint\");\n}\n\n// Test the return-by-value two-index constraint function using random values\ntemplate <size_t SpatialDim>\nvoid test_two_index_constraint_random(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ij<DataVector, SpatialDim, Frame::Inertial> (*)(\n          const tnsr::ij<DataVector, SpatialDim, Frame::Inertial>&)>(\n          &ScalarWave::two_index_constraint<SpatialDim>),\n      \"Constraints\", \"two_index_constraint\", {{{-10.0, 10.0}}}, used_for_size,\n      1.0e-12);\n  TestHelpers::db::test_compute_tag<\n      ScalarWave::Tags::TwoIndexConstraintCompute<SpatialDim>>(\n      \"TwoIndexConstraint\");\n}\n\ntemplate <typename Solution>\nvoid test_constraints_and_compute_tags_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound, const double error_tolerance) {\n  constexpr size_t spatial_dim = 3;\n\n  // Check vs. time-independent analytic solution\n  // Set up grid\n  Mesh<3> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto};\n  const size_t data_size = mesh.number_of_grid_points();\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1.0, 1.0, lower_bound[0], upper_bound[0]},\n              Affine{-1.0, 1.0, lower_bound[1], upper_bound[1]},\n              Affine{-1.0, 1.0, lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = 0.;\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t,\n                         tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                    ScalarWave::Tags::Phi<spatial_dim>>{});\n  const auto& psi = get<ScalarWave::Tags::Psi>(vars);\n  const auto& pi = get<ScalarWave::Tags::Pi>(vars);\n  const auto& phi = get<ScalarWave::Tags::Phi<spatial_dim>>(vars);\n  // Compute derivatives d_phi and d_psi numerically\n  // First, prepare\n  using variables_tags_to_differentiate =\n      tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Phi<3>>;\n  Variables<variables_tags_to_differentiate> local_vars(data_size);\n  get<ScalarWave::Tags::Psi>(local_vars) = psi;\n  get<ScalarWave::Tags::Phi<spatial_dim>>(local_vars) = phi;\n  // Second, compute derivatives\n  const auto local_derivs =\n      partial_derivatives<variables_tags_to_differentiate>(\n          local_vars, mesh, coord_map.inv_jacobian(x_logical));\n  const auto& deriv_psi =\n      get<Tags::deriv<ScalarWave::Tags::Psi, tmpl::size_t<spatial_dim>,\n                      Frame::Inertial>>(local_derivs);\n  const auto& deriv_phi =\n      get<Tags::deriv<ScalarWave::Tags::Phi<spatial_dim>,\n                      tmpl::size_t<spatial_dim>, Frame::Inertial>>(\n          local_derivs);\n\n  // (1.) Get the constraints, and check that they vanish to error_tolerance\n  auto one_index_constraint =\n      make_with_value<tnsr::i<DataVector, spatial_dim, Frame::Inertial>>(\n          x, std::numeric_limits<double>::signaling_NaN());\n  ScalarWave::one_index_constraint(make_not_null(&one_index_constraint),\n                                   deriv_psi, phi);\n  auto two_index_constraint =\n      make_with_value<tnsr::ij<DataVector, spatial_dim, Frame::Inertial>>(\n          x, std::numeric_limits<double>::signaling_NaN());\n  ScalarWave::two_index_constraint(make_not_null(&two_index_constraint),\n                                   deriv_phi);\n\n  Approx numerical_approx = Approx::custom().epsilon(error_tolerance).scale(1.);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      one_index_constraint,\n      (make_with_value<tnsr::i<DataVector, spatial_dim, Frame::Inertial>>(x,\n                                                                          0.)),\n      numerical_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      two_index_constraint,\n      (make_with_value<tnsr::ij<DataVector, spatial_dim, Frame::Inertial>>(x,\n                                                                           0.)),\n      numerical_approx);\n\n  // (2.) Test that compute tags return expected values\n  const auto box =\n      db::create<db::AddSimpleTags<\n                     domain::Tags::Coordinates<spatial_dim, Frame::Inertial>,\n                     ScalarWave::Tags::Psi, ScalarWave::Tags::Phi<spatial_dim>,\n                     ScalarWave::Tags::Pi,\n                     ::Tags::deriv<ScalarWave::Tags::Psi,\n                                   tmpl::size_t<spatial_dim>, Frame::Inertial>,\n                     ::Tags::deriv<ScalarWave::Tags::Phi<spatial_dim>,\n                                   tmpl::size_t<spatial_dim>, Frame::Inertial>>,\n                 db::AddComputeTags<\n                     ScalarWave::Tags::OneIndexConstraintCompute<spatial_dim>,\n                     ScalarWave::Tags::TwoIndexConstraintCompute<spatial_dim>>>(\n          x, psi, phi, pi, deriv_psi, deriv_phi);\n\n  // Check that their compute items in databox furnish identical values\n  CHECK(db::get<ScalarWave::Tags::OneIndexConstraint<spatial_dim>>(box) ==\n        one_index_constraint);\n  CHECK(db::get<ScalarWave::Tags::TwoIndexConstraint<spatial_dim>>(box) ==\n        two_index_constraint);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarWave.Constraints\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"Evolution/Systems/ScalarWave/\"};\n\n  {\n    INFO(\"Testing constraints with randomized input\");\n    // Test the one-index constraint with random numbers\n    GENERATE_UNINITIALIZED_DATAVECTOR;\n    CHECK_FOR_DATAVECTORS(test_one_index_constraint_random, (1, 2, 3));\n    CHECK_FOR_DATAVECTORS(test_two_index_constraint_random, (1, 2, 3));\n  }\n  {\n    INFO(\"Testing constraints for a spherical-wave analytic solution\");\n    const size_t grid_size = 8;\n    const std::array<double, 3> upper_bound{{6., 6., 6.}};\n    const std::array<double, 3> lower_bound{{0., 0., 0.}};\n\n    const ScalarWave::Solutions::RegularSphericalWave solution(\n        std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(1., 1.,\n                                                                      0.));\n\n    // Note: looser numerical tolerance because this check\n    // uses numerical derivatives\n    test_constraints_and_compute_tags_analytic(\n        solution, grid_size, lower_bound, upper_bound,\n        std::numeric_limits<double>::epsilon() * 1.e6);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Test_EnergyDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarWave/EnergyDensity.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n\nnamespace {\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_compute_item_in_databox(const DataType& used_for_size) {\n  TestHelpers::db::test_compute_tag<\n      ScalarWave::Tags::EnergyDensityCompute<SpatialDim>>(\"EnergyDensity\");\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n\n  const Scalar<DataVector> pi = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), dist, used_for_size);\n  const tnsr::i<DataVector, SpatialDim, Frame::Inertial> phi =\n      make_with_random_values<tnsr::i<DataVector, SpatialDim, Frame::Inertial>>(\n          make_not_null(&generator), dist, used_for_size);\n\n  const auto box = db::create<\n      db::AddSimpleTags<ScalarWave::Tags::Pi,\n                        ScalarWave::Tags::Phi<SpatialDim>>,\n      db::AddComputeTags<ScalarWave::Tags::EnergyDensityCompute<SpatialDim>>>(\n      pi, phi);\n\n  const auto expected = ScalarWave::energy_density(pi, phi);\n\n  CHECK_ITERABLE_APPROX(\n      (db::get<ScalarWave::Tags::EnergyDensity<SpatialDim>>(box)), expected);\n}\n\ntemplate <size_t SpatialDim>\nvoid test_energy_density(const DataVector& used_for_size) {\n  void (*f)(const gsl::not_null<Scalar<DataVector>*>, const Scalar<DataVector>&,\n            const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&) =\n      &ScalarWave::energy_density<SpatialDim>;\n  pypp::check_with_random_values<1>(f, \"EnergyDensity\", {\"energy_density\"},\n                                    {{{-1., 1.}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarWave.EnergyDensity\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"Evolution/Systems/ScalarWave/\");\n\n  const DataVector used_for_size(5);\n  test_energy_density<1>(used_for_size);\n  test_energy_density<2>(used_for_size);\n  test_energy_density<3>(used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Test_Equations.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarWave/Equations.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid check_normal_dot_fluxes(const size_t npts, const double t) {\n  const ScalarWave::Solutions::PlaneWave<Dim> solution(\n      make_array<Dim>(0.1), make_array<Dim>(0.0),\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(1.0, 1.0,\n                                                                    0.0));\n\n  const tnsr::I<DataVector, Dim> x = [npts]() {\n    auto logical_coords = logical_coordinates(Mesh<Dim>{\n        3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto});\n    tnsr::I<DataVector, Dim> coords{pow<Dim>(npts)};\n    for (size_t i = 0; i < Dim; ++i) {\n      coords.get(i) = std::move(logical_coords.get(i));\n    }\n    return coords;\n  }();\n\n  const auto psi = solution.psi(x, t);\n  const auto pi = Scalar<DataVector>{-get(solution.dpsi_dt(x, t))};\n  const auto phi = solution.dpsi_dx(x, t);\n\n  // Any numbers are fine, doesn't have anything to do with unit normal\n  auto unit_normal =\n      make_with_value<tnsr::i<DataVector, Dim, Frame::Inertial>>(pi, 0.0);\n  for (size_t d = 0; d < Dim; ++d) {\n    unit_normal.get(d) = x.get(d);\n  }\n\n  auto normal_dot_flux_psi = make_with_value<Scalar<DataVector>>(pi, 0.0);\n  auto normal_dot_flux_pi = make_with_value<Scalar<DataVector>>(pi, 0.0);\n  auto normal_dot_flux_phi =\n      make_with_value<tnsr::i<DataVector, Dim, Frame::Inertial>>(pi, 0.0);\n\n  ScalarWave::ComputeNormalDotFluxes<Dim>::apply(\n      make_not_null(&normal_dot_flux_psi), make_not_null(&normal_dot_flux_pi),\n      make_not_null(&normal_dot_flux_phi), pi);\n\n  const DataVector expected_normal_dot_fluxes{pow<Dim>(npts), 0.0};\n  CHECK(get(normal_dot_flux_psi) == expected_normal_dot_fluxes);\n  CHECK(get(normal_dot_flux_pi) == expected_normal_dot_fluxes);\n\n  for (size_t d = 0; d < Dim; ++d) {\n    CHECK(normal_dot_flux_phi.get(d) == expected_normal_dot_fluxes);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarWave.NormalDotFluxes\",\n                  \"[Unit][Evolution]\") {\n  constexpr double time = 0.7;\n  INFO(\"Check NormalDotFluxes\");\n  check_normal_dot_fluxes<1>(3, time);\n  check_normal_dot_fluxes<2>(3, time);\n  check_normal_dot_fluxes<3>(3, time);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Test_MomentumDensity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarWave/MomentumDensity.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n\nnamespace {\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_compute_item_in_databox(const DataType& used_for_size) {\n  TestHelpers::db::test_compute_tag<\n      ScalarWave::Tags::MomentumDensityCompute<SpatialDim>>(\"MomentumDensity\");\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n\n  const Scalar<DataVector> pi = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), dist, used_for_size);\n\n  const tnsr::i<DataVector, SpatialDim, Frame::Inertial> phi =\n      make_with_random_values<tnsr::i<DataVector, SpatialDim, Frame::Inertial>>(\n          make_not_null(&generator), dist, used_for_size);\n\n  const auto box = db::create<\n      db::AddSimpleTags<ScalarWave::Tags::Pi,\n                        ScalarWave::Tags::Phi<SpatialDim>>,\n      db::AddComputeTags<ScalarWave::Tags::MomentumDensityCompute<SpatialDim>>>(\n      pi, phi);\n\n  const auto expected = ScalarWave::momentum_density(pi, phi);\n\n  CHECK_ITERABLE_APPROX(\n      (db::get<ScalarWave::Tags::MomentumDensity<SpatialDim>>(box)), expected);\n}\n\ntemplate <size_t SpatialDim>\nvoid test_momentum_density(const DataVector& used_for_size) {\n  void (*f)(\n      const gsl::not_null<tnsr::i<DataVector, SpatialDim, Frame::Inertial>*>,\n      const Scalar<DataVector>&,\n      const tnsr::i<DataVector, SpatialDim, Frame::Inertial>&) =\n      &ScalarWave::momentum_density<SpatialDim>;\n  pypp::check_with_random_values<1>(f, \"MomentumDensity\", {\"momentum_density\"},\n                                    {{{-1., 1.}}}, used_for_size);\n  test_compute_item_in_databox<SpatialDim>(used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarWave.MomentumDensity\",\n                  \"[Unit][Evolution]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"Evolution/Systems/ScalarWave/\");\n\n  const DataVector used_for_size(5);\n  test_momentum_density<1>(used_for_size);\n  test_momentum_density<2>(used_for_size);\n  test_momentum_density<3>(used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarWave.Tags\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::Psi>(\"Psi\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::Pi>(\"Pi\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::Phi<3>>(\"Phi\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::ConstraintGamma2>(\n      \"ConstraintGamma2\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::OneIndexConstraint<3>>(\n      \"OneIndexConstraint\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::TwoIndexConstraint<3>>(\n      \"TwoIndexConstraint\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::VPsi>(\"VPsi\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::VZero<3>>(\"VZero\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::VPlus>(\"VPlus\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::VMinus>(\"VMinus\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::CharacteristicSpeeds<3>>(\n      \"CharacteristicSpeeds\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::CharacteristicFields<3>>(\n      \"CharacteristicFields\");\n  TestHelpers::db::test_simple_tag<\n      ScalarWave::Tags::EvolvedFieldsFromCharacteristicFields<3>>(\n      \"EvolvedFieldsFromCharacteristicFields\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::EnergyDensity<3>>(\n      \"EnergyDensity\");\n  TestHelpers::db::test_simple_tag<ScalarWave::Tags::MomentumDensity<3>>(\n      \"MomentumDensity\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/ScalarWave/Test_TimeDerivative.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Evolution/Systems/ScalarWave/TimeDerivative.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct ConstraintGamma2Copy : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nauto add_scalar_to_tensor_components(\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& input, double constant) {\n  tnsr::i<DataVector, Dim, Frame::Inertial> copy_of_input = [&input,\n                                                             &constant]() {\n    auto local_copy_of_input = input;\n    for (size_t i = 0; i < Dim; ++i) {\n      local_copy_of_input.get(i) += constant;\n    }\n    return local_copy_of_input;\n  }();\n  return copy_of_input;\n}\ntemplate <size_t Dim>\nvoid check_du_dt(const size_t npts, const double time) {\n  ScalarWave::Solutions::PlaneWave<Dim> solution(\n      make_array<Dim>(0.1), make_array<Dim>(0.0),\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(1.0, 1.0,\n                                                                    0.0));\n\n  tnsr::I<DataVector, Dim> x = [npts]() {\n    auto logical_coords = logical_coordinates(Mesh<Dim>{\n        3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto});\n    tnsr::I<DataVector, Dim> coords{pow<Dim>(npts)};\n    for (size_t i = 0; i < Dim; ++i) {\n      coords.get(i) = std::move(logical_coords.get(i));\n    }\n    return coords;\n  }();\n\n  auto local_check_du_dt = [&npts, &time, &solution, &x](\n                               const double gamma2, const double constraint) {\n    auto box = db::create<db::AddSimpleTags<\n        ScalarWave::Tags::ConstraintGamma2, ConstraintGamma2Copy,\n        Tags::dt<ScalarWave::Tags::Psi>, Tags::dt<ScalarWave::Tags::Pi>,\n        Tags::dt<ScalarWave::Tags::Phi<Dim>>, ScalarWave::Tags::Pi,\n        ScalarWave::Tags::Phi<Dim>,\n        Tags::deriv<ScalarWave::Tags::Psi, tmpl::size_t<Dim>, Frame::Inertial>,\n        Tags::deriv<ScalarWave::Tags::Pi, tmpl::size_t<Dim>, Frame::Inertial>,\n        Tags::deriv<ScalarWave::Tags::Phi<Dim>, tmpl::size_t<Dim>,\n                    Frame::Inertial>>>(\n        Scalar<DataVector>(pow<Dim>(npts), gamma2),\n        Scalar<DataVector>(pow<Dim>(npts), 0.0),\n        Scalar<DataVector>(pow<Dim>(npts), 0.0),\n        Scalar<DataVector>(pow<Dim>(npts), 0.0),\n        tnsr::i<DataVector, Dim, Frame::Inertial>(pow<Dim>(npts), 0.0),\n        Scalar<DataVector>(-1.0 * solution.dpsi_dt(x, time).get()),\n        add_scalar_to_tensor_components(solution.dpsi_dx(x, time), constraint),\n        solution.dpsi_dx(x, time),\n        [&x, &time, &solution]() {\n          auto dpi_dx = solution.d2psi_dtdx(x, time);\n          for (size_t i = 0; i < Dim; ++i) {\n            dpi_dx.get(i) *= -1.0;\n          }\n          return dpi_dx;\n        }(),\n        [&npts, &x, &time, &solution]() {\n          tnsr::ij<DataVector, Dim, Frame::Inertial> d2psi_dxdx{pow<Dim>(npts),\n                                                                0.0};\n          const tnsr::ii<DataVector, Dim, Frame::Inertial> ddpsi_soln =\n              solution.d2psi_dxdx(x, time);\n          for (size_t i = 0; i < Dim; ++i) {\n            for (size_t j = 0; j < Dim; ++j) {\n              d2psi_dxdx.get(i, j) = ddpsi_soln.get(i, j);\n            }\n          }\n          return d2psi_dxdx;\n        }());\n\n    db::mutate_apply<\n        tmpl::list<Tags::dt<ScalarWave::Tags::Psi>,\n                   Tags::dt<ScalarWave::Tags::Pi>,\n                   Tags::dt<ScalarWave::Tags::Phi<Dim>>, ConstraintGamma2Copy>,\n        tmpl::push_front<\n            typename ScalarWave::TimeDerivative<Dim>::argument_tags,\n            Tags::deriv<ScalarWave::Tags::Psi, tmpl::size_t<Dim>,\n                        Frame::Inertial>,\n            Tags::deriv<ScalarWave::Tags::Pi, tmpl::size_t<Dim>,\n                        Frame::Inertial>,\n            Tags::deriv<ScalarWave::Tags::Phi<Dim>, tmpl::size_t<Dim>,\n                        Frame::Inertial>>>(ScalarWave::TimeDerivative<Dim>{},\n                                           make_not_null(&box));\n\n    CHECK_ITERABLE_APPROX(db::get<Tags::dt<ScalarWave::Tags::Psi>>(box),\n                          solution.dpsi_dt(x, time));\n    CHECK_ITERABLE_APPROX(\n        db::get<Tags::dt<ScalarWave::Tags::Pi>>(box),\n        Scalar<DataVector>(-1.0 * solution.d2psi_dt2(x, time).get()));\n    CHECK_ITERABLE_APPROX(\n        db::get<Tags::dt<ScalarWave::Tags::Phi<Dim>>>(box),\n        add_scalar_to_tensor_components(solution.d2psi_dtdx(x, time),\n                                        -gamma2 * constraint));\n  };\n\n  // Test with constraint damping parameter set to zero\n  local_check_du_dt(0.0, 0.0);\n  local_check_du_dt(0.0, 100.0);\n  local_check_du_dt(0.0, -998.0);\n\n  // Test with constraint satisfied but nonzero constraint damping parameter\n  local_check_du_dt(10.0, 0.0);\n  local_check_du_dt(-4.3, 0.0);\n\n  // Test with one-index constraint NOT satisfied and nonzero constraint\n  // damping parameter\n  local_check_du_dt(10.0, 10.9);\n  local_check_du_dt(1.2, -77.0);\n  local_check_du_dt(-10.9, 43.0);\n  local_check_du_dt(-90.0, -56.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Systems.ScalarWave.TimeDerivative\",\n                  \"[Unit][Evolution]\") {\n  constexpr double time = 0.7;\n  check_du_dt<1>(3, time);\n  check_du_dt<2>(3, time);\n  check_du_dt<3>(3, time);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Systems/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Evolution/Test_BoundaryCorrectionTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <string>\n\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace {\nstruct BoundaryCorrection : public evolution::BoundaryCorrection {\n  BoundaryCorrection() = default;\n  BoundaryCorrection(const BoundaryCorrection&) = default;\n  BoundaryCorrection& operator=(const BoundaryCorrection&) = default;\n  BoundaryCorrection(BoundaryCorrection&&) = default;\n  BoundaryCorrection& operator=(BoundaryCorrection&&) = default;\n  ~BoundaryCorrection() override = default;\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"Halp\"};\n\n  explicit BoundaryCorrection(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(BoundaryCorrection);  // NOLINT\n#pragma GCC diagnostic pop\n\n  std::unique_ptr<evolution::BoundaryCorrection> get_clone() const override {\n    return std::make_unique<BoundaryCorrection>(*this);\n  }\n};\n\nPUP::able::PUP_ID BoundaryCorrection::my_PUP_ID = 0;  // NOLINT\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.BoundaryCorrectionTags\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<evolution::Tags::BoundaryCorrection>(\n      \"BoundaryCorrection\");\n  const auto boundary_correction =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::OptionTags::BoundaryCorrection, BoundaryCorrection>(\n          \"BoundaryCorrection\");\n  CHECK(dynamic_cast<const BoundaryCorrection*>(boundary_correction.get()) !=\n        nullptr);\n  CHECK(evolution::Tags::BoundaryCorrection::create_from_options(\n            boundary_correction) != nullptr);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Test_ComputeTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/ComputeTags.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Evolution/DgSubcell/Tags/ObserverCoordinates.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct FieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct TestAnalyticSolution : public MarkAsAnalyticSolution,\n                              public evolution::initial_data::InitialData {\n  TestAnalyticSolution() = default;\n  ~TestAnalyticSolution() override = default;\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override {\n    return std::make_unique<TestAnalyticSolution>(*this);\n  }\n\n  explicit TestAnalyticSolution(CkMigrateMessage* msg) : InitialData(msg) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestAnalyticSolution);\n#pragma GCC diagnostic pop\n\n  static tuples::TaggedTuple<FieldTag> variables(\n      const tnsr::I<DataVector, 1>& x, const double t,\n      const tmpl::list<FieldTag> /*meta*/) {\n    return {Scalar<DataVector>{t * get<0>(x)}};\n  }\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override { InitialData::pup(p); }\n};\n\nPUP::able::PUP_ID TestAnalyticSolution::my_PUP_ID = 0;\n\nstruct TestAnalyticData : public MarkAsAnalyticData,\n                          public evolution::initial_data::InitialData {\n  TestAnalyticData() = default;\n  ~TestAnalyticData() override = default;\n\n\n  auto get_clone() const\n      -> std::unique_ptr<evolution::initial_data::InitialData> override {\n    return std::make_unique<TestAnalyticData>(*this);\n  }\n\n  explicit TestAnalyticData(CkMigrateMessage* msg) : InitialData(msg) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestAnalyticData);\n#pragma GCC diagnostic pop\n\n  static tuples::TaggedTuple<FieldTag> variables(\n      const tnsr::I<DataVector, 1>& x, const tmpl::list<FieldTag> /*meta*/) {\n    return {Scalar<DataVector>{get<0>(x)}};\n  }\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override { InitialData::pup(p); }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nPUP::able::PUP_ID TestAnalyticData::my_PUP_ID = 0;\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.ComputeTags\", \"[Unit][Evolution]\") {\n  tnsr::I<DataVector, 1, Frame::Inertial> inertial_coords{{{{1., 2., 3., 4.}}}};\n  const double current_time = 2.;\n  const Variables<tmpl::list<FieldTag>> vars{4, 3.};\n  using solutions_and_data = tmpl::list<TestAnalyticSolution, TestAnalyticData>;\n  {\n    INFO(\"Test analytic solution with analytic solution tag for dg-subcell\");\n    tnsr::I<DataVector, 1, Frame::Inertial> subcell_inertial_coords{\n        {{{1., 1.5, 2., 2.5, 3., 3.5, 4.}}}};\n    auto box = db::create<\n        db::AddSimpleTags<\n            domain::Tags::Coordinates<1, Frame::Inertial>,\n            evolution::dg::subcell::Tags::Coordinates<1, Frame::Inertial>,\n            evolution::dg::subcell::Tags::ActiveGrid,\n            evolution::initial_data::Tags::InitialData, Tags::Time,\n            ::Tags::Variables<tmpl::list<FieldTag>>>,\n        db::AddComputeTags<\n            evolution::dg::subcell::Tags::ObserverCoordinatesCompute<\n                1, Frame::Inertial>,\n            evolution::Tags::AnalyticSolutionsCompute<1, tmpl::list<FieldTag>,\n                                                      true, solutions_and_data>,\n            Tags::ErrorsCompute<tmpl::list<FieldTag>>>>(\n        inertial_coords, subcell_inertial_coords,\n        evolution::dg::subcell::ActiveGrid::Dg,\n        std::unique_ptr<evolution::initial_data::InitialData>{\n            std::make_unique<TestAnalyticSolution>()},\n        current_time, vars);\n    const DataVector expected{2., 4., 6., 8.};\n    const DataVector expected_error{1., -1., -3., -5.};\n    CHECK_ITERABLE_APPROX(get(get<Tags::Analytic<FieldTag>>(box).value()),\n                          expected);\n    CHECK_ITERABLE_APPROX(get(get<Tags::Error<FieldTag>>(box).value()),\n                          expected_error);\n    db::mutate<evolution::dg::subcell::Tags::ActiveGrid,\n               ::Tags::Variables<tmpl::list<FieldTag>>>(\n        [](const auto active_grid_ptr, const auto vars_ptr) {\n          *active_grid_ptr = evolution::dg::subcell::ActiveGrid::Subcell;\n          vars_ptr->initialize(7);\n          *vars_ptr = Variables<tmpl::list<FieldTag>>{7, 3.};\n        },\n        make_not_null(&box));\n    const DataVector subcell_expected{2., 3., 4., 5., 6., 7., 8.};\n    const DataVector subcell_expected_error{1., 0., -1., -2., -3., -4., -5.};\n    CHECK_ITERABLE_APPROX(get(get<Tags::Analytic<FieldTag>>(box).value()),\n                          subcell_expected);\n    CHECK_ITERABLE_APPROX(get(get<Tags::Error<FieldTag>>(box).value()),\n                          subcell_expected_error);\n  }\n  {\n    INFO(\"Test analytic solution with base class\");\n    const auto box = db::create<\n        db::AddSimpleTags<domain::Tags::Coordinates<1, Frame::Inertial>,\n                          evolution::initial_data::Tags::InitialData,\n                          Tags::Time, ::Tags::Variables<tmpl::list<FieldTag>>>,\n        db::AddComputeTags<\n            evolution::Tags::AnalyticSolutionsCompute<\n                1, tmpl::list<FieldTag>, false, solutions_and_data>,\n            Tags::ErrorsCompute<tmpl::list<FieldTag>>>>(\n        inertial_coords,\n        std::unique_ptr<evolution::initial_data::InitialData>{\n            std::make_unique<TestAnalyticSolution>()},\n        current_time, vars);\n    const DataVector expected{2., 4., 6., 8.};\n    const DataVector expected_error{1., -1., -3., -5.};\n    CHECK_ITERABLE_APPROX(get(get<Tags::Analytic<FieldTag>>(box).value()),\n                          expected);\n    CHECK_ITERABLE_APPROX(get(get<Tags::Error<FieldTag>>(box).value()),\n                          expected_error);\n  }\n  {\n    INFO(\"Test analytic data with base class\");\n    const auto box = db::create<\n        db::AddSimpleTags<domain::Tags::Coordinates<1, Frame::Inertial>,\n                          evolution::initial_data::Tags::InitialData,\n                          Tags::Time, ::Tags::Variables<tmpl::list<FieldTag>>>,\n        db::AddComputeTags<\n            evolution::Tags::AnalyticSolutionsCompute<\n                1, tmpl::list<FieldTag>, false, solutions_and_data>,\n            Tags::ErrorsCompute<tmpl::list<FieldTag>>>>(\n        inertial_coords,\n        std::unique_ptr<evolution::initial_data::InitialData>{\n            std::make_unique<TestAnalyticData>()},\n        current_time, vars);\n    CHECK_FALSE(get<Tags::Analytic<FieldTag>>(box).has_value());\n    CHECK_FALSE(get<Tags::Error<FieldTag>>(box).has_value());\n  }\n\n  TestHelpers::db::test_compute_tag<evolution::Tags::AnalyticSolutionsCompute<\n      1, tmpl::list<FieldTag>, false, solutions_and_data>>(\"AnalyticSolutions\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Test_NumericInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/NumericInitialData.hpp\"\n#include \"Evolution/Protocols.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\n\n// Test that the numeric initial data class conforms to the protocol\nstatic_assert(\n    tt::assert_conforms_to_v<evolution::NumericInitialData,\n                             evolution::protocols::NumericInitialData>);\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Test_Protocols.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Protocols.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\n\n// [conforming_type_example]\nstruct ValidNumericInitialData\n    : tt::ConformsTo<evolution::protocols::NumericInitialData> {};\n// [conforming_type_example]\n\nstatic_assert(\n    tt::assert_conforms_to_v<ValidNumericInitialData,\n                             evolution::protocols::NumericInitialData>);\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n\nnamespace {\nstruct SomeType {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Tags\", \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<Filters::Tags::Filter<SomeType>>(\"Filter\");\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Test_TagsDomain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Tags.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/TagsDomain.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_tags() {\n  TestHelpers::db::test_compute_tag<\n      evolution::domain::Tags::DivMeshVelocityCompute<Dim>>(\n      \"div(MeshVelocity)\");\n}\n\ntemplate <size_t MeshDim>\nusing TranslationMap =\n    domain::CoordinateMaps::TimeDependent::Translation<MeshDim>;\n\ntemplate <size_t MeshDim>\nusing ConcreteMap = domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                                          TranslationMap<MeshDim>>;\n\ntemplate <size_t MeshDim>\nConcreteMap<MeshDim> create_coord_map(const std::string& f_of_t_name) {\n  return ConcreteMap<MeshDim>{TranslationMap<MeshDim>{f_of_t_name}};\n}\n\ntemplate <size_t Dim, bool IsTimeDependent>\nvoid test() {\n  using simple_tags = db::AddSimpleTags<\n      Tags::Time, domain::Tags::Mesh<Dim>,\n      domain::Tags::Coordinates<Dim, Frame::Grid>,\n      domain::Tags::InverseJacobian<Dim, Frame::ElementLogical, Frame::Grid>,\n      domain::Tags::FunctionsOfTimeInitialize,\n      domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                  Frame::Inertial>>;\n  using compute_tags = db::AddComputeTags<\n      domain::Tags::CoordinatesMeshVelocityAndJacobiansCompute<\n          domain::CoordinateMaps::Tags::CoordinateMap<Dim, Frame::Grid,\n                                                      Frame::Inertial>>,\n      domain::Tags::InertialFromGridCoordinatesCompute<Dim>,\n      domain::Tags::ElementToInertialInverseJacobian<Dim>,\n      domain::Tags::InertialMeshVelocityCompute<Dim>,\n      evolution::domain::Tags::DivMeshVelocityCompute<Dim>>;\n\n  const DataVector velocity{Dim, -4.3};\n  const double initial_time = 0.0;\n  const double expiration_time = 4.5;\n  // In 1d, the helper function create_coord_map will only use the first name,\n  // i.e., TranslationX. In 2d, it uses the first two names, TranslationX and\n  // TranslationY.\n  const std::string function_of_time_name = \"Translation\";\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> dist(-10.0, 10.0);\n\n  const Mesh<Dim> mesh{5, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const size_t num_pts = mesh.number_of_grid_points();\n\n  tnsr::I<DataVector, Dim, Frame::Grid> grid_coords{num_pts};\n  fill_with_random_values(make_not_null(&grid_coords), make_not_null(&gen),\n                          make_not_null(&dist));\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Grid>\n      element_to_grid_inverse_jacobian{num_pts};\n  fill_with_random_values(make_not_null(&element_to_grid_inverse_jacobian),\n                          make_not_null(&gen), make_not_null(&dist));\n\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[function_of_time_name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time,\n          std::array<DataVector, 3>{{{Dim, 0.0}, velocity, {Dim, 0.0}}},\n          expiration_time);\n\n  using MapPtr = std::unique_ptr<\n      domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>>;\n  const MapPtr grid_to_inertial_map =\n      IsTimeDependent ? MapPtr(std::make_unique<ConcreteMap<Dim>>(\n                            create_coord_map<Dim>(function_of_time_name)))\n                      : MapPtr(std::make_unique<domain::CoordinateMap<\n                                   Frame::Grid, Frame::Inertial,\n                                   domain::CoordinateMaps::Identity<Dim>>>());\n\n  const double time = 3.0;\n  auto box = db::create<simple_tags, compute_tags>(\n      time, mesh, grid_coords, element_to_grid_inverse_jacobian,\n      std::move(functions_of_time), grid_to_inertial_map->get_clone());\n\n  const auto check_helper = [&box, &mesh]() {\n    if (IsTimeDependent) {\n      const std::optional<Scalar<DataVector>>& div_frame_velocity =\n          db::get<domain::Tags::DivMeshVelocity>(box);\n      REQUIRE(div_frame_velocity.has_value());\n      CHECK(\n          *div_frame_velocity ==\n          divergence(\n              db::get<domain::Tags::MeshVelocity<Dim>>(box).value(), mesh,\n              db::get<domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                                    Frame::Inertial>>(box)));\n    } else {\n      // In the time-independent case, check that the divergence of the mesh\n      // velocity is not set.\n      CHECK_FALSE(db::get<domain::Tags::DivMeshVelocity>(box).has_value());\n    }\n  };\n  check_helper();\n\n  db::mutate<Tags::Time>(\n      [](const gsl::not_null<double*> local_time) { *local_time = 4.5; },\n      make_not_null(&box));\n  check_helper();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.TagsDomain\", \"[Unit][Evolution]\") {\n  test_tags<1>();\n  test_tags<2>();\n  test_tags<3>();\n\n  test<1, true>();\n  test<2, true>();\n  test<3, true>();\n\n  test<1, false>();\n  test<2, false>();\n  test<3, false>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/Triggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EvolutionTriggers\")\n\nset(LIBRARY_SOURCES\n  Test_SeparationLessThan.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  EventsAndTriggers\n  EvolutionTriggers\n  FunctionsOfTime\n  Options\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/Evolution/Triggers/Test_SeparationLessThan.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/SettleToConstantQuaternion.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/Triggers/SeparationLessThan.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <bool UseGridCentersFunctionOfTime>\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Trigger, tmpl::list<Triggers::SeparationLessThan<\n                                          UseGridCentersFunctionOfTime>>>>;\n  };\n};\n\nusing TranslationMap = domain::CoordinateMaps::TimeDependent::Translation<3>;\n\nvoid test_horizons() {\n  INFO(\"Testing with horizons\");\n  register_factory_classes_with_charm<Metavariables<false>>();\n  const std::string f_of_t_name = \"LlamasWithHats\";\n  std::unique_ptr<domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, 3>>\n      map = std::make_unique<\n          domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap>>(\n          TranslationMap{f_of_t_name});\n\n  const auto check = [&map, &f_of_t_name](\n                         const double separation, const double time,\n                         const double x_center_a, const double x_center_b,\n                         const bool expected_is_triggered) {\n    CAPTURE(separation, time, x_center_a, x_center_b, expected_is_triggered);\n    const tnsr::I<double, 3, Frame::Grid> center_a{\n        std::array{x_center_a, 0.0, 0.0}};\n    const tnsr::I<double, 3, Frame::Grid> center_b{\n        std::array{x_center_b, 0.0, 0.0}};\n\n    ExcisionSphere<3> excision_sphere_a{2.0, center_a, {}};\n    ExcisionSphere<3> excision_sphere_b{2.0, center_b, {}};\n\n    excision_sphere_a.inject_time_dependent_maps(map->get_clone());\n    excision_sphere_b.inject_time_dependent_maps(map->get_clone());\n\n    std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};\n    excision_spheres[\"ExcisionSphere\" + get_output(domain::ObjectLabel::A)] =\n        std::move(excision_sphere_a);\n    excision_spheres[\"ExcisionSphere\" + get_output(domain::ObjectLabel::B)] =\n        std::move(excision_sphere_b);\n\n    const Domain<3> domain{std::vector<Block<3>>{},\n                           std::move(excision_spheres)};\n\n    const Triggers::SeparationLessThan<false> trigger{separation};\n\n    // The coefs are zero because we want the inertial point to be the same\n    // as the grid point for easy checking\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        functions_of_time{};\n    functions_of_time[f_of_t_name] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n            time, std::array{DataVector{3, 0.0}}, time + 1.0);\n\n    const bool is_triggered =\n        trigger(time, domain, functions_of_time, center_a, center_b);\n\n    CHECK(is_triggered == expected_is_triggered);\n  };\n\n  for (const auto& [separation, x_center] : cartesian_product(\n           make_array(10.0, 5.0, 2.0), make_array(6.0, 5.0, 2.0, 0.75))) {\n    check(separation, 0.0, x_center, -x_center, 2.0 * x_center < separation);\n  }\n\n  TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables<false>>(\n      \"SeparationLessThan:\\n\"\n      \"  Value: 2.3\");\n}\n\nvoid test_grid_centers() {\n  INFO(\"Testing with grid centers\");\n  register_factory_classes_with_charm<Metavariables<true>>();\n\n  const Triggers::SeparationLessThan<false> trigger{6.0};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"GridCenters\"] =\n      std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>{\n          std::make_unique<::domain::FunctionsOfTime::PiecewisePolynomial<3>>(\n              0.0,\n              std::array<DataVector, 4>{{{16.0, 0.0, 0.0, -16.0, 0.0, 0.0},\n                                         {-0.001, 0.0, 0.0, 0.001, 0.0, 0.0},\n                                         {0.0, 0.0, 0.0, 0.0, 0.0, 0.0},\n                                         {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}}},\n              std::numeric_limits<double>::max())};\n  const double initial_omega_z = 0.01;\n  auto init_func_rotation = make_array<4, DataVector>(DataVector{3, 0.0});\n  init_func_rotation[1][2] = initial_omega_z;\n  auto init_quaternion = make_array<1, DataVector>(DataVector{4, 0.0});\n  init_quaternion[0][0] = 1.0;\n  functions_of_time[\"Rotation\"] =\n      std::make_unique<domain::FunctionsOfTime::QuaternionFunctionOfTime<3>>(\n          0.0, init_quaternion, init_func_rotation, 1.0e5);\n\n  CHECK_FALSE(trigger(0.0, functions_of_time));\n  CHECK_FALSE(trigger(9.0e3, functions_of_time));\n  CHECK_FALSE(trigger(12.9999999e3, functions_of_time));\n  CHECK(trigger(13.001e3, functions_of_time));\n  CHECK(trigger(14.0e3, functions_of_time));\n  CHECK(trigger(15.0e3, functions_of_time));\n\n  const double match_time = 14.5e3;\n  const std::array<DataVector, 3> settle_initial_func_and_derivs =\n      functions_of_time.at(\"Rotation\")->func_and_2_derivs(match_time);\n  functions_of_time[\"Rotation\"] =\n      std::make_unique<domain::FunctionsOfTime::SettleToConstantQuaternion>(\n          settle_initial_func_and_derivs, match_time, 60.0);\n  CHECK_FALSE(trigger(15.0e3, functions_of_time));\n\n  TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables<true>>(\n      \"SeparationLessThan:\\n\"\n      \"  Value: 2.3\");\n}\n\nvoid test_errors_horizons() {\n  const Triggers::SeparationLessThan<false> trigger{1.0};\n\n  std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};\n\n  CHECK_THROWS_WITH(\n      trigger(0.0, Domain<3>{std::vector<Block<3>>{}, excision_spheres}, {}, {},\n              {}),\n      Catch::Matchers::ContainsSubstring(\n          \"SeparationLessThan trigger expects an \"\n          \"excision sphere named 'ExcisionSphere\"));\n\n  ExcisionSphere<3> excision_sphere_a{};\n  excision_spheres[\"ExcisionSphere\" + get_output(domain::ObjectLabel::A)] =\n      std::move(excision_sphere_a);\n\n  CHECK_THROWS_WITH(\n      trigger(0.0, Domain<3>{std::vector<Block<3>>{}, excision_spheres}, {}, {},\n              {}),\n      Catch::Matchers::ContainsSubstring(\n          \"to be time dependent, but it is not.\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.Triggers.SeparationLessThan\",\n                  \"[Unit][Evolution]\") {\n  domain::FunctionsOfTime::register_derived_with_charm();\n  PUPable_reg(SINGLE_ARG(\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial, TranslationMap>));\n  test_horizons();\n  test_grid_centers();\n  test_errors_horizons();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Evolution/VariableFixing/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_VariableFixing\")\n\nset(LIBRARY_SOURCES\n  Test_Actions.cpp\n  Test_FixToAtmosphere.cpp\n  Test_LimitLorentzFactor.cpp\n  Test_ParameterizedDeleptonization.cpp\n  Test_RadiallyFallingFloor.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  Hydro\n  Parallel\n  VariableFixing\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Evolution/VariableFixing/Test_Actions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/VariableFixing/Actions.hpp\"\n#include \"Evolution/VariableFixing/RadiallyFallingFloor.hpp\"\n#include \"Evolution/VariableFixing/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/TagsDeclarations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct mock_component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using simple_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpecificEnthalpy<DataVector>,\n                 hydro::Tags::Temperature<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 domain::Tags::Coordinates<3, Frame::Inertial>,\n                 hydro::Tags::GrmhdEquationOfState>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<VariableFixing::Actions::FixVariables<\n                                 VariableFixing::RadiallyFallingFloor<3>>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<mock_component<Metavariables>>;\n};\n\nstruct SomeType {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.VariableFixing.Actions\",\n                  \"[Unit][Evolution][VariableFixing]\") {\n  register_derived_classes_with_charm<\n      EquationsOfState::EquationOfState<true, 3>>();\n  TestHelpers::db::test_simple_tag<Tags::VariableFixer<SomeType>>(\n      \"VariableFixer\");\n\n  using component = mock_component<Metavariables>;\n  const DataVector x{-2.0, -1.0, 0.0, 1.0, 2.0};\n  const DataVector y{-2.0, -1.0, 0.0, 1.0, 2.0};\n  const DataVector z{-2.0, -1.0, 0.0, 1.0, 2.0};\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      VariableFixing::RadiallyFallingFloor<3>(1.e-4, 1.e-5, -1.5, 1.e-7 / 3.0,\n                                              -2.5)};\n\n  EquationsOfState::PolytropicFluid<true> polytrope{1.0, 2.0};\n\n  const double root_three = sqrt(3.0);\n\n  const DataVector fixed_density{\n      2.3, 1.e-5 * pow(3, -0.75),\n      1.e-10,  // quantities at a radius below\n               // `radius_at_which_to_begin_applying_floor`\n               // do not get fixed.\n      1.e-5 * pow(3, -0.75), 1.e-5 * pow(2.0 * root_three, -1.5)};\n\n  auto fixed_pressure =\n      get(polytrope.pressure_from_density(Scalar<DataVector>{fixed_density}));\n\n  // This pressure term will remain the same b/c the radial coordinate is at the\n  // origin\n  fixed_pressure[2] = 2.0;\n\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0,\n      {Scalar<DataVector>{DataVector{2.3, -4.2, 1.e-10, 0.0, -0.1}},\n       Scalar<DataVector>{DataVector{0.0, 1.e-8, 2.0, -5.5, 3.2}},\n       Scalar<DataVector>{DataVector{1.0, 2.0, 3.0, 4.0, 5.0}},\n       Scalar<DataVector>{DataVector{1.0, 2.0, 3.0, 4.0, 5.0}},\n       Scalar<DataVector>{DataVector{1.0, 2.0, 3.0, 4.0, 5.0}},\n       Scalar<DataVector>{DataVector{1.0, 1.0, 1.0, 1.0, 1.0}},\n       tnsr::I<DataVector, 3, Frame::Inertial>{{{x, y, z}}},\n       polytrope.promote_to_3d_eos()});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& box = ActionTesting::get_databox<component>(runner, 0);\n  runner.next_action<component>(0);\n\n  CHECK_ITERABLE_APPROX(db::get<hydro::Tags::Pressure<DataVector>>(box).get(),\n                        fixed_pressure);\n  CHECK_ITERABLE_APPROX(\n      db::get<hydro::Tags::RestMassDensity<DataVector>>(box).get(),\n      fixed_density);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/VariableFixing/Test_FixToAtmosphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\nvoid test_fix_reconstructed_state_to_atmosphere() {\n  using Frs = VariableFixing::FixReconstructedStateToAtmosphere;\n  for (const auto t :\n       {Frs::Always, Frs::AtDgFdInterfaceOnly, Frs::OnFdOnly, Frs::Never}) {\n    CHECK(t == TestHelpers::test_creation<Frs>(get_output(t)));\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_variable_fixer(\n    const VariableFixing::FixToAtmosphere<Dim>& variable_fixer,\n    const EquationsOfState::EquationOfState<true, 1>& equation_of_state) {\n  // 2.e-12 -> below cutoff, velocity goes to zero\n  // 2.e-11 -> above cutoff, no changes\n  // 4.e-12 -> density unchanged, velocity restricted\n  Scalar<DataVector> density{DataVector{2.e-12, 2.e-11, 4.e-12}};\n  auto pressure = equation_of_state.pressure_from_density(density);\n  auto specific_internal_energy =\n      equation_of_state.specific_internal_energy_from_density(density);\n  auto temperature = equation_of_state.temperature_from_density(density);\n  const Scalar<DataVector> electron_fraction{\n      DataVector{get(density).size(), 0.5}};\n\n  Scalar<DataVector> lorentz_factor{\n      DataVector{5.0 / 3.0, 7.0710678118654752, 1.8898223650461359}};\n  auto spatial_velocity =\n      make_with_value<tnsr::I<DataVector, Dim, Frame::Inertial>>(density, 0.0);\n  spatial_velocity.get(0) = DataVector{0.8, 0.7, 0.6};\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataVector, Dim, Frame::Inertial>>(density, 0.0);\n  for (size_t i = 0; i < Dim; ++i) {\n    spatial_metric.get(i, i) = 2.0;\n  }\n  variable_fixer(&density, &specific_internal_energy, &spatial_velocity,\n                 &lorentz_factor, &pressure, &temperature, electron_fraction,\n                 spatial_metric, equation_of_state);\n\n  Scalar<DataVector> expected_density{DataVector{1.e-12, 2.e-11, 4.e-12}};\n  auto expected_pressure =\n      equation_of_state.pressure_from_density(expected_density);\n  auto expected_specific_internal_energy =\n      equation_of_state.specific_internal_energy_from_density(expected_density);\n  Scalar<DataVector> expected_lorentz_factor{\n      DataVector{1.0, 7.0710678118654752, 1.0000000001020408}};\n  auto expected_spatial_velocity =\n      make_with_value<tnsr::I<DataVector, Dim, Frame::Inertial>>(density, 0.0);\n  expected_spatial_velocity.get(0)[1] = 0.7;\n  // The [2] component of the expected velocity is:\n  //     velocity *= max_velocity_magnitude_ * (rho - rho_cut) /\n  //                 (trans_rho - rho_cut) / |v^i|\n  expected_spatial_velocity.get(0)[2] = 0.6 * (4.e-12 - 3.e-12) /\n                                        (1.e-11 - 3.e-12) * 1.e-4 /\n                                        sqrt(0.6 * 0.6 * 2.0);\n\n  CHECK_ITERABLE_APPROX(density, expected_density);\n  CHECK_ITERABLE_APPROX(pressure, expected_pressure);\n  CHECK_ITERABLE_APPROX(specific_internal_energy,\n                        expected_specific_internal_energy);\n  CHECK_ITERABLE_APPROX(lorentz_factor, expected_lorentz_factor);\n  CHECK_ITERABLE_APPROX(spatial_velocity, expected_spatial_velocity);\n}\n\ntemplate <size_t Dim>\nvoid test_variable_fixer(\n    const VariableFixing::FixToAtmosphere<Dim>& variable_fixer,\n    const EquationsOfState::EquationOfState<true, 2>& equation_of_state,\n    const bool use_kappa_limiting, const double min_temperature) {\n  CAPTURE(use_kappa_limiting);\n  CAPTURE(min_temperature);\n  Scalar<DataVector> density{DataVector{2.e-12, 2.e-11, 4.e-12, 2.e-11}};\n  Scalar<DataVector> specific_internal_energy{DataVector{\n      2., 3., 3.,\n      get(equation_of_state\n              .specific_internal_energy_from_density_and_temperature(\n                  Scalar<double>(get(density)[3]), Scalar<double>(-1e-5)))}};\n  auto pressure = equation_of_state.pressure_from_density_and_energy(\n      density, specific_internal_energy);\n  auto temperature = equation_of_state.temperature_from_density_and_energy(\n      density, specific_internal_energy);\n  const Scalar<DataVector> electron_fraction{\n      DataVector{get(density).size(), 0.5}};\n\n  Scalar<DataVector> lorentz_factor{DataVector{\n      5. / 3., 7.0710678118654752, 1.8898223650461359, 7.0710678118654752}};\n  CHECK(get(lorentz_factor).size() == get(density).size());\n  auto spatial_velocity =\n      make_with_value<tnsr::I<DataVector, Dim, Frame::Inertial>>(density, 0.);\n  spatial_velocity.get(0) = DataVector{0.8, 0.7, 0.6, 0.7};\n  CHECK(spatial_velocity.get(0).size() == get(density).size());\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataVector, Dim, Frame::Inertial>>(density, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    spatial_metric.get(i, i) = 2.;\n  }\n  variable_fixer(&density, &specific_internal_energy, &spatial_velocity,\n                 &lorentz_factor, &pressure, &temperature, electron_fraction,\n                 spatial_metric, equation_of_state);\n\n  Scalar<DataVector> expected_density{\n      DataVector{1.e-12, 2.e-11, 4.e-12, 2.e-11}};\n  const auto compute_temperature = [&equation_of_state, &expected_density,\n                                    &min_temperature](const size_t i) {\n    const double density_lower_bound = 3.e-12;\n    const double density_upper_bound = 3.e-11;\n    const double epsilon_kappa_pm = 1.1;\n    const bool below =\n        get(expected_density)[i] < epsilon_kappa_pm * density_lower_bound;\n    const double p_min = get(equation_of_state.pressure_from_density_and_energy(\n        Scalar<double>{get(expected_density)[i]},\n        equation_of_state.specific_internal_energy_from_density_and_temperature(\n            Scalar<double>{get(expected_density)[i]},\n            Scalar<double>{min_temperature})));\n    return get(equation_of_state.temperature_from_density_and_energy(\n        Scalar<double>{get(expected_density)[i]},\n        equation_of_state.specific_internal_energy_from_density_and_pressure(\n            Scalar<double>{get(expected_density)[i]},\n            Scalar<double>{\n                p_min * (1.0 + 0.01 * (below ? 1.0\n                                             : (get(expected_density)[i] -\n                                                density_lower_bound) /\n                                                   (density_upper_bound -\n                                                    density_lower_bound)))})));\n  };\n\n  Scalar<DataVector> expected_specific_internal_energy{\n      use_kappa_limiting\n          ? (min_temperature == 0.0 ? DataVector{4, 0.}\n                                    :  // do more complicated kappa limiting\n                 get(equation_of_state\n                         .specific_internal_energy_from_density_and_temperature(\n                             expected_density,\n                             Scalar<DataVector>{DataVector{\n                                 min_temperature, compute_temperature(1),\n                                 compute_temperature(2), 0.0}})))\n          : DataVector{\n                0.0, 3.0, 3.0,\n                get(equation_of_state\n                        .specific_internal_energy_from_density_and_temperature(\n                            Scalar<double>(get(expected_density)[3]),\n                            Scalar<double>(0.)))}};\n  auto expected_pressure = equation_of_state.pressure_from_density_and_energy(\n      expected_density, expected_specific_internal_energy);\n  auto expected_temperature =\n      equation_of_state.temperature_from_density_and_energy(\n          expected_density, expected_specific_internal_energy);\n  Scalar<DataVector> expected_lorentz_factor{DataVector{\n      1., 7.0710678118654752, 1.0000000001020408, 7.0710678118654752}};\n  auto expected_spatial_velocity =\n      make_with_value<tnsr::I<DataVector, Dim, Frame::Inertial>>(density, 0.);\n  expected_spatial_velocity.get(0)[1] = 0.7;\n  // The [2] component of the expected velocity is:\n  //     velocity *= max_velocity_magnitude_ * (rho - rho_cut) /\n  //                 (trans_rho - rho_cut) / |v^i|\n  expected_spatial_velocity.get(0)[2] = 0.6 * (4.e-12 - 3.e-12) /\n                                        (1.e-11 - 3.e-12) * 1.e-4 /\n                                        sqrt(0.6 * 0.6 * 2.);\n  // The [3] component is the same as the [1] component\n  expected_spatial_velocity.get(0)[3] = expected_spatial_velocity.get(0)[1];\n\n  CHECK(get(expected_lorentz_factor).size() == get(expected_density).size());\n  CHECK_ITERABLE_APPROX(density, expected_density);\n  CHECK_ITERABLE_APPROX(pressure, expected_pressure);\n  CHECK_ITERABLE_APPROX(temperature, expected_temperature);\n  CHECK_ITERABLE_APPROX(specific_internal_energy,\n                        expected_specific_internal_energy);\n  CHECK_ITERABLE_APPROX(lorentz_factor, expected_lorentz_factor);\n  CHECK_ITERABLE_APPROX(spatial_velocity, expected_spatial_velocity);\n}\n\ntemplate <size_t Dim>\nvoid test_variable_fixer() {\n  using Vlo =\n      typename VariableFixing::FixToAtmosphere<Dim>::VelocityLimitingOptions;\n  using Klo =\n      typename VariableFixing::FixToAtmosphere<Dim>::KappaLimitingOptions;\n  // Test for representative 1-d equation of state\n  const VariableFixing::FixToAtmosphere<Dim> variable_fixer_klo{\n      1.e-12, 3.e-12, Vlo{0.0, 1.e-4, 3.e-12, 1.e-11},\n      Klo{3.e-12, 1.e-3, 3.e-11, 0.01, std::nullopt, false}};\n  const VariableFixing::FixToAtmosphere<Dim> variable_fixer{\n      1.e-12, 3.e-12, Vlo{0.0, 1.e-4, 3.e-12, 1.e-11}, std::nullopt};\n  EquationsOfState::PolytropicFluid<true> polytrope{1.0, 2.0};\n  test_variable_fixer<Dim>(variable_fixer, polytrope);\n  test_variable_fixer<Dim>(variable_fixer_klo, polytrope);\n  test_serialization(variable_fixer);\n  test_serialization(variable_fixer_klo);\n\n  const auto check_velocity_limiting_options = [](const auto& var_fixer) {\n    CHECK(var_fixer.density_of_atmosphere() == 1.e-12);\n    CHECK(var_fixer.density_cutoff() == 3.e-12);\n    REQUIRE(var_fixer.velocity_limiting().has_value());\n    CHECK(var_fixer.velocity_limiting().value().atmosphere_max_velocity == 0.);\n    CHECK(var_fixer.velocity_limiting().value().near_atmosphere_max_velocity ==\n          1.e-4);\n    CHECK(var_fixer.velocity_limiting().value().atmosphere_density_cutoff ==\n          3.e-12);\n    CHECK(var_fixer.velocity_limiting().value().transition_density_bound ==\n          1.e-11);\n  };\n  check_velocity_limiting_options(variable_fixer);\n  check_velocity_limiting_options(variable_fixer_klo);\n\n  CHECK(not variable_fixer.kappa_limiting().has_value());\n  REQUIRE(variable_fixer_klo.kappa_limiting().has_value());\n  CHECK(variable_fixer_klo.kappa_limiting()->density_lower_bound == 3.e-12);\n  CHECK(variable_fixer_klo.kappa_limiting()->eplison_kappa_minus == 1.e-3);\n  CHECK(variable_fixer_klo.kappa_limiting()->density_upper_bound == 3.e-11);\n  CHECK(variable_fixer_klo.kappa_limiting()->epsilon_kappa_max == 0.01);\n  CHECK(variable_fixer_klo.kappa_limiting()->min_temperature == std::nullopt);\n  CHECK(variable_fixer_klo.kappa_limiting()->limit_above_density_upper_bound ==\n        false);\n\n  const auto fixer_from_options =\n      TestHelpers::test_creation<VariableFixing::FixToAtmosphere<Dim>>(\n          \"DensityOfAtmosphere: 1.0e-12\\n\"\n          \"DensityCutoff: 3.0e-12\\n\"\n          \"VelocityLimiting:\\n\"\n          \"  AtmosphereMaxVelocity: 0\\n\"\n          \"  NearAtmosphereMaxVelocity: 1.0e-4\\n\"\n          \"  AtmosphereDensityCutoff: 3.0e-12\\n\"\n          \"  TransitionDensityBound: 1.0e-11\\n\"\n          \"KappaLimiting: Disabled\\n\");\n  const auto fixer_from_options_klo =\n      TestHelpers::test_creation<VariableFixing::FixToAtmosphere<Dim>>(\n          \"DensityOfAtmosphere: 1.0e-12\\n\"\n          \"DensityCutoff: 3.0e-12\\n\"\n          \"VelocityLimiting:\\n\"\n          \"  AtmosphereMaxVelocity: 0\\n\"\n          \"  NearAtmosphereMaxVelocity: 1.0e-4\\n\"\n          \"  AtmosphereDensityCutoff: 3.0e-12\\n\"\n          \"  TransitionDensityBound: 1.0e-11\\n\"\n          \"KappaLimiting:\\n\"\n          \"  DensityLowerBound: 3.0e-12\\n\"\n          \"  EplisonKappaMinus: 1.0e-3\\n\"\n          \"  DensityUpperBound: 3.0e-11\\n\"\n          \"  EpsilonKappaMax: 0.01\\n\"\n          \"  MinTemperature: 1.0e-3\\n\"\n          \"  LimitAboveDensityUpperBound: False\\n\");\n  test_variable_fixer(fixer_from_options, polytrope);\n  test_variable_fixer(fixer_from_options_klo, polytrope);\n\n  // Test for representative 2-d equation of state\n  EquationsOfState::IdealFluid<true> ideal_fluid{5.0 / 3.0};\n  test_variable_fixer<Dim>(variable_fixer, ideal_fluid, false, 0.0);\n  test_variable_fixer<Dim>(variable_fixer_klo, ideal_fluid, true, 0.0);\n\n  test_variable_fixer<Dim>(fixer_from_options, ideal_fluid, false, 1.0e-3);\n  test_variable_fixer<Dim>(fixer_from_options_klo, ideal_fluid, true, 1.0e-3);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.VariableFixing.FixToAtmosphere\",\n                  \"[VariableFixing][Unit]\") {\n  test_fix_reconstructed_state_to_atmosphere();\n  test_variable_fixer<1>();\n  test_variable_fixer<2>();\n  test_variable_fixer<3>();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/VariableFixing/Test_LimitLorentzFactor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/VariableFixing/LimitLorentzFactor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace VariableFixing {\nnamespace {\nvoid test_variable_fixer(const LimitLorentzFactor& variable_fixer,\n                         const bool enable) {\n  const Scalar<DataVector> density{DataVector{1.0, 2.0, 1.0e-5, 1.0e-6}};\n  auto spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(density, 1.0);\n  get<0, 0>(spatial_metric) = 0.2;\n  get<0, 1>(spatial_metric) = 0.3;\n  get<0, 2>(spatial_metric) = 0.4;\n  get<1, 1>(spatial_metric) = 0.5;\n  get<1, 2>(spatial_metric) = 0.6;\n  get<2, 2>(spatial_metric) = 0.7;\n\n  tnsr::I<DataVector, 3, Frame::Inertial> spatial_velocity{\n      {{DataVector{0.4999, 0.4999, 0.4999, 0.4999},\n        DataVector{1.3567, 1.3566, 1.3567, 1.3566},\n        DataVector{-0.200, -0.200, -0.200, -0.200}}}};\n\n  auto lorentz_factor = hydro::lorentz_factor(\n      dot_product(spatial_velocity, spatial_velocity, spatial_metric));\n  const Scalar<DataVector> expected_lorentz_factor{DataVector{\n      get(lorentz_factor)[0], get(lorentz_factor)[1],\n      enable ? 50.0 : get(lorentz_factor)[2], get(lorentz_factor)[3]}};\n  const double rescale_velocity_factor =\n      enable ? sqrt((1.0 - 1.0 / square(get(expected_lorentz_factor)[2])) /\n                    (1.0 - 1.0 / square(get(lorentz_factor)[2])))\n             : 1.0;\n  const tnsr::I<DataVector, 3, Frame::Inertial> expected_spatial_velocity{\n      {{DataVector{0.4999, 0.4999, 0.4999 * rescale_velocity_factor, 0.4999},\n        DataVector{1.3567, 1.3566, 1.3567 * rescale_velocity_factor, 1.3566},\n        DataVector{-0.200, -0.200, -0.200 * rescale_velocity_factor, -0.200}}}};\n\n  variable_fixer(make_not_null(&lorentz_factor),\n                 make_not_null(&spatial_velocity), density);\n\n  CHECK(lorentz_factor == expected_lorentz_factor);\n  CHECK(spatial_velocity == expected_spatial_velocity);\n  Approx custom_approx = Approx::custom().epsilon(6.0e-14).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      expected_lorentz_factor,\n      hydro::lorentz_factor(\n          dot_product(spatial_velocity, spatial_velocity, spatial_metric)),\n      custom_approx);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.VariableFixing.LimitLorentzFactor\",\n                  \"[VariableFixing][Unit]\") {\n  SECTION(\"operator== and operator!=\") {\n    CHECK(LimitLorentzFactor{1.0, 8.0, true} ==\n          LimitLorentzFactor{1.0, 8.0, true});\n    CHECK_FALSE(LimitLorentzFactor{2., 8.0, true} ==\n                LimitLorentzFactor{1.0, 8.0, true});\n    CHECK_FALSE(LimitLorentzFactor{1.0, 9.0, true} ==\n                LimitLorentzFactor{1.0, 8.0, true});\n    CHECK_FALSE(LimitLorentzFactor{1.0, 8.0, true} !=\n                LimitLorentzFactor{1.0, 8.0, true});\n    CHECK_FALSE(LimitLorentzFactor{1.0, 8.0, true} ==\n                LimitLorentzFactor{1.0, 8.0, false});\n    CHECK(LimitLorentzFactor{2.0, 8.0, true} !=\n          LimitLorentzFactor{1.0, 8.0, true});\n    CHECK(LimitLorentzFactor{1.0, 9.0, true} !=\n          LimitLorentzFactor{1.0, 8.0, true});\n  }\n\n  SECTION(\"variable fixing\") {\n    for (const bool enable : {true, false}) {\n      CAPTURE(enable);\n      LimitLorentzFactor variable_fixer{1.0e-4, 50.0, enable};\n      test_serialization(variable_fixer);\n      test_variable_fixer(serialize_and_deserialize(variable_fixer), enable);\n    }\n    const auto fixer_from_options =\n        TestHelpers::test_creation<LimitLorentzFactor>(\n            \"MaxDensityCutoff: 1.0e-4\\n\"\n            \"LorentzFactorCap: 50.0\\n\"\n            \"Enable: true\\n\");\n    test_variable_fixer(fixer_from_options, true);\n  }\n}\n}  // namespace\n}  // namespace VariableFixing\n"
  },
  {
    "path": "tests/Unit/Evolution/VariableFixing/Test_ParameterizedDeleptonization.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/VariableFixing/ParameterizedDeleptonization.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\n// 1D EoS version\nvoid test_variable_fixer(\n    const VariableFixing::ParameterizedDeleptonization& variable_fixer,\n    const EquationsOfState::EquationOfState<true, 1>& equation_of_state) {\n  // 8.0e-11 -> below cutoff (less dense/further out in the star), picks high Ye\n  // 1.5e-10 -> above cutoff (high dense/closer to star center), pick low Ye\n  // 3.0e-10 -> density transition regions, should be in between\n\n  const Scalar<DataVector> density{DataVector{0.8e-10, 1.5e-10, 3e-10}};\n  Scalar<DataVector> electron_fraction{DataVector{0.5, 0.5, 0.5}};\n  // Temperature should remain unchanged with 1D EoS\n  Scalar<DataVector> dummy_temperature{DataVector{1.0, 1.0, 1.0}};\n  Scalar<DataVector> expected_temperature = dummy_temperature;\n\n  // These should remain unchanged, sinced param delep can only make Ye drop\n  Scalar<DataVector> electron_fraction_low{DataVector{0.1, 0.15, 0.2}};\n\n  auto pressure = equation_of_state.pressure_from_density(density);\n  auto specific_internal_energy =\n      equation_of_state.specific_internal_energy_from_density(density);\n\n  // check Ye values that will naturally decrease\n  variable_fixer(&specific_internal_energy, &electron_fraction, &pressure,\n                 &dummy_temperature, density, equation_of_state);\n\n  const Scalar<DataVector> expected_electron_fraction{\n      DataVector{0.5, 0.40980370118030457, 0.285}};\n\n  CHECK_ITERABLE_APPROX(electron_fraction, expected_electron_fraction);\n  CHECK_ITERABLE_APPROX(dummy_temperature, expected_temperature);\n\n  // check Ye values that will stay constant\n  const Scalar<DataVector> expected_electron_fraction_low{\n      electron_fraction_low};\n\n  variable_fixer(&specific_internal_energy, &electron_fraction_low, &pressure,\n                 &dummy_temperature, density, equation_of_state);\n\n  CHECK_ITERABLE_APPROX(electron_fraction_low, expected_electron_fraction_low);\n}\n\n// 2D EoS version\nvoid test_variable_fixer(\n    const VariableFixing::ParameterizedDeleptonization& variable_fixer,\n    const EquationsOfState::EquationOfState<true, 2>& equation_of_state) {\n  const Scalar<DataVector> density{DataVector{3.0e-9, 3.7e-9, 4.0e-9}};\n  Scalar<DataVector> electron_fraction{DataVector{0.5, 0.5, 0.5}};\n  // Temperature should remain unchanged with 2D EoS\n  Scalar<DataVector> dummy_temperature{DataVector{1.0, 1.0, 1.0}};\n  Scalar<DataVector> expected_temperature = dummy_temperature;\n\n  // These should remain unchanged, sinced param delep can only make Ye drop\n  Scalar<DataVector> electron_fraction_low{DataVector{0.1, 0.15, 0.2}};\n  Scalar<DataVector> specific_internal_energy{DataVector{1.0, 2.0, 3.0}};\n  auto pressure = equation_of_state.pressure_from_density_and_energy(\n      density, specific_internal_energy);\n\n  // check Ye values that will naturally decrease\n  variable_fixer(&specific_internal_energy, &electron_fraction, &pressure,\n                 &dummy_temperature, density, equation_of_state);\n\n  const Scalar<DataVector> expected_electron_fraction{\n      DataVector{0.49, 0.3077746733472282, 0.2}};\n\n  CHECK_ITERABLE_APPROX(electron_fraction, expected_electron_fraction);\n  CHECK_ITERABLE_APPROX(dummy_temperature, expected_temperature);\n\n  // check Ye values that will stay constant\n  const Scalar<DataVector> expected_electron_fraction_low{\n      electron_fraction_low};\n  variable_fixer(&specific_internal_energy, &electron_fraction_low, &pressure,\n                 &dummy_temperature, density, equation_of_state);\n\n  CHECK_ITERABLE_APPROX(electron_fraction_low, expected_electron_fraction_low);\n}\n\n// 3D EoS version\nvoid test_variable_fixer(\n    const VariableFixing::ParameterizedDeleptonization& variable_fixer,\n    const EquationsOfState::EquationOfState<true, 3>& equation_of_state) {\n  const Scalar<DataVector> density{DataVector{3.0e-9, 3.7e-9, 4.0e-9}};\n  const Scalar<DataVector> high_density{DataVector{5.0e-4, 2.5e-9, 3.0e-9}};\n  const Scalar<DataVector> initial_density = density;\n  Scalar<DataVector> electron_fraction{DataVector{0.5, 0.5, 0.5}};\n  Scalar<DataVector> temperature{DataVector{1.0, 1.0, 1.0}};\n  Scalar<DataVector> initial_temperature = temperature;\n\n  // These should remain unchanged, sinced param delep can only make Ye drop\n  Scalar<DataVector> electron_fraction_low{DataVector{0.1, 0.15, 0.2}};\n  Scalar<DataVector> specific_internal_energy{DataVector{1.0, 2.0, 3.0}};\n  Scalar<DataVector> initial_specific_internal_energy =\n      specific_internal_energy;\n  auto pressure = equation_of_state.pressure_from_density_and_energy(\n      density, specific_internal_energy, electron_fraction);\n  auto initial_pressure = pressure;\n\n  // check Ye values that will naturally decrease\n  variable_fixer(&specific_internal_energy, &electron_fraction, &pressure,\n                 &temperature, density, equation_of_state);\n\n  const Scalar<DataVector> expected_electron_fraction{\n      DataVector{0.49, 0.3077746733472282, 0.2}};\n\n  CHECK_ITERABLE_APPROX(electron_fraction, expected_electron_fraction);\n\n  // check Ye values that will stay constant\n  const Scalar<DataVector> expected_electron_fraction_low{\n      electron_fraction_low};\n  variable_fixer(&specific_internal_energy, &electron_fraction_low, &pressure,\n                 &temperature, density, equation_of_state);\n\n  CHECK_ITERABLE_APPROX(electron_fraction_low, expected_electron_fraction_low);\n\n  // For a 3D EoS call...\n  // density should not change\n  CHECK_ITERABLE_APPROX(density, initial_density);\n\n  // specific internal energy should not change\n  // Note in the entropy conserving version of param delep, this will change,\n  // according to the neutrino chemical potential\n  CHECK_ITERABLE_APPROX(specific_internal_energy,\n                        initial_specific_internal_energy);\n\n  // pressure should not change\n  CHECK_FALSE(pressure == initial_pressure);\n\n  // temperature should not change\n  CHECK_FALSE(temperature == initial_temperature);\n\n  // Check parameterized deleptonization does not activate if density is too\n  // high, simulating post bounce phase of supernova.\n  const auto electron_fraction_compare = electron_fraction;\n\n  variable_fixer(&specific_internal_energy, &electron_fraction, &pressure,\n                 &temperature, high_density, equation_of_state);\n\n  // Ye, eint, pressure, & temperature should not change b/c the density is too\n  // high\n  CHECK(electron_fraction == electron_fraction_compare);\n\n  CHECK_ITERABLE_APPROX(specific_internal_energy,\n                        initial_specific_internal_energy);\n\n  CHECK_FALSE(pressure == initial_pressure);\n\n  CHECK_FALSE(temperature == initial_temperature);\n\n}  // end 3D\n\nvoid test_variable_fixer() {\n  // Test for representative 1-d equation of state\n\n  const bool enable_param_delep = true;\n\n  const double hi_dens_cutoff = 2.0e-10;\n  const double lo_dens_cutoff = 1.0e-10;\n\n  const double ye_at_hi_dens = 0.285;\n  const double ye_at_lo_dens = 0.5;\n\n  const double ye_magnitude_scale = 0.035;\n\n  // high density scale, low density scale, Ye(hi dens), Ye(low dens), Ye\n  // correction scale\n  const VariableFixing::ParameterizedDeleptonization variable_fixer{\n      enable_param_delep, hi_dens_cutoff, lo_dens_cutoff,\n      ye_at_hi_dens,      ye_at_lo_dens,  ye_magnitude_scale};\n\n  const EquationsOfState::PolytropicFluid<true> polytrope{1.0, 2.0};\n\n  // catch error messages\n  CHECK_THROWS_WITH(\n      ([&hi_dens_cutoff, &lo_dens_cutoff, &ye_at_hi_dens, &ye_at_lo_dens,\n        &ye_magnitude_scale]() {\n        const VariableFixing::ParameterizedDeleptonization\n            variable_fixer_dens_error{\n                enable_param_delep, hi_dens_cutoff, 10.0 * lo_dens_cutoff,\n                ye_at_hi_dens,      ye_at_lo_dens,  ye_magnitude_scale};\n      })(),\n      Catch::Matchers::ContainsSubstring(\"The high density scale\"));\n\n  CHECK_THROWS_WITH(\n      ([&hi_dens_cutoff, &lo_dens_cutoff, &ye_at_lo_dens,\n        &ye_magnitude_scale]() {\n        const VariableFixing::ParameterizedDeleptonization\n            variable_fixer_dens_error{enable_param_delep, hi_dens_cutoff,\n                                      lo_dens_cutoff,     10.0 * ye_at_lo_dens,\n                                      ye_at_lo_dens,      ye_magnitude_scale};\n      })(),\n      Catch::Matchers::ContainsSubstring(\"The Ye at high density(\"));\n\n  // check expected values\n  test_variable_fixer(variable_fixer, polytrope);\n  test_serialization(variable_fixer);\n\n  {\n    INFO(\"Test 1D/barotropic EOS\");\n    const auto fixer_from_options = TestHelpers::test_creation<\n        VariableFixing::ParameterizedDeleptonization>(\n        \"Enable: true\\n\"\n        \"HighDensityScale: 2.0e-10\\n\"\n        \"LowDensityScale: 1.0e-10\\n\"\n        \"ElectronFractionAtHighDensity: 0.285\\n\"\n        \"ElectronFractionAtLowDensity: 0.5\\n\"\n        \"ElectronFractionCorrectionScale: 0.035\");\n\n    test_serialization(fixer_from_options);\n    test_variable_fixer(fixer_from_options, polytrope);\n  }\n\n  {\n    INFO(\"Test 2D EOS\");\n    const auto fixer_from_options = TestHelpers::test_creation<\n        VariableFixing::ParameterizedDeleptonization>(\n        \"Enable: true\\n\"\n        \"HighDensityScale: 4.0e-9\\n\"\n        \"LowDensityScale: 3.0e-9\\n\"\n        \"ElectronFractionAtHighDensity: 0.2\\n\"\n        \"ElectronFractionAtLowDensity: 0.49\\n\"\n        \"ElectronFractionCorrectionScale: 0.05\");\n\n    EquationsOfState::IdealFluid<true> ideal_fluid{5.0 / 3.0};\n    test_serialization(fixer_from_options);\n    test_variable_fixer(fixer_from_options, ideal_fluid);\n  }\n\n  {\n    INFO(\"Test 3D EOS\");\n    const auto fixer_from_options = TestHelpers::test_creation<\n        VariableFixing::ParameterizedDeleptonization>(\n        \"Enable: true\\n\"\n        \"HighDensityScale: 4.0e-9\\n\"\n        \"LowDensityScale: 3.0e-9\\n\"\n        \"ElectronFractionAtHighDensity: 0.2\\n\"\n        \"ElectronFractionAtLowDensity: 0.49\\n\"\n        \"ElectronFractionCorrectionScale: 0.05\");\n\n    EquationsOfState::Tabulated3D<true> tabulated_eos;\n\n    std::string h5_file_name{\n        unit_test_src_path() +\n        \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n\n    h5::H5File<h5::AccessType::ReadOnly> eos_file{h5_file_name};\n    const auto& compose_eos = eos_file.get<h5::EosTable>(\"/dd2\");\n\n    tabulated_eos.initialize(compose_eos);\n\n    test_serialization(fixer_from_options);\n    test_variable_fixer(fixer_from_options, tabulated_eos);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.VariableFixing.ParameterizedDeleptonization\",\n                  \"[VariableFixing][Unit]\") {\n  test_variable_fixer();\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/VariableFixing/Test_RadiallyFallingFloor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/VariableFixing/RadiallyFallingFloor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\n// ThermoDim = 1\nvoid test_eos_call(\n    const VariableFixing::RadiallyFallingFloor<3>& variable_fixer,\n    const EquationsOfState::EquationOfState<true, 1>& equation_of_state,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords) {\n  Scalar<DataVector> initial_pressure{DataVector{0.0, 1.e-8, 2.0, -5.5, 3.2}};\n  Scalar<DataVector> pressure = initial_pressure;\n  Scalar<DataVector> initial_density{DataVector{2.3, -4.2, 1.e-10, 0.0, -0.1}};\n  Scalar<DataVector> density = initial_density;\n  Scalar<DataVector> initial_specific_internal_energy{\n      DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n  Scalar<DataVector> specific_internal_energy =\n      initial_specific_internal_energy;\n  Scalar<DataVector> initial_temperature{DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n  Scalar<DataVector> temperature = initial_temperature;\n  Scalar<DataVector> initial_electron_fraction{\n      DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n  Scalar<DataVector> electron_fraction = initial_electron_fraction;\n\n  variable_fixer(&density, &pressure, &specific_internal_energy, &temperature,\n                 &electron_fraction, coords, equation_of_state);\n\n  // Density should change\n  CHECK_FALSE(initial_density == density);\n\n  // Pressure should change\n  CHECK_FALSE(initial_pressure == pressure);\n\n  // Specific internal energy should change\n  CHECK_FALSE(initial_specific_internal_energy == specific_internal_energy);\n\n  // temperature should change (for 1D EoS)\n  CHECK_FALSE(initial_temperature == temperature);\n\n  // Ye should remain unchanged (for 1D EoS)\n  CHECK_ITERABLE_APPROX(initial_electron_fraction, electron_fraction);\n}\n\n// ThermoDim = 2\ntemplate <size_t ThermoDim>\nvoid test_eos_call(\n    const VariableFixing::RadiallyFallingFloor<3>& variable_fixer,\n    const EquationsOfState::EquationOfState<true, ThermoDim>& equation_of_state,\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords) {\n  CAPTURE(ThermoDim);\n  Scalar<DataVector> initial_pressure{DataVector{0.0, 1.e-8, 2.0, -5.5, 3.2}};\n  Scalar<DataVector> pressure = initial_pressure;\n  Scalar<DataVector> initial_density{DataVector{2.3, -4.2, 1.e-10, 0.0, -0.1}};\n  Scalar<DataVector> density = initial_density;\n  Scalar<DataVector> initial_specific_internal_energy{\n      DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n  Scalar<DataVector> specific_internal_energy =\n      initial_specific_internal_energy;\n  Scalar<DataVector> initial_temperature{DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n  Scalar<DataVector> temperature = initial_temperature;\n  if (ThermoDim == 3) {\n    initial_temperature = initial_pressure;\n    get(initial_pressure) = DataVector{1.0, 2.0, 3.0, 4.0, 5.0};\n    pressure = initial_pressure;\n  }\n  Scalar<DataVector> initial_electron_fraction{\n      DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n  Scalar<DataVector> electron_fraction = initial_electron_fraction;\n\n  variable_fixer(&density, &pressure, &specific_internal_energy, &temperature,\n                 &electron_fraction, coords, equation_of_state);\n\n  // Density should change\n  CHECK_FALSE(initial_density == density);\n\n  // Pressure should change\n  CHECK_FALSE(initial_pressure == pressure);\n\n  // Specific internal energy should change\n  CHECK_FALSE(initial_specific_internal_energy == specific_internal_energy);\n\n  // temperature should change (for 2D EoS)\n  CHECK_FALSE(initial_temperature == temperature);\n\n  // Ye should remain unchanged (for 2D EoS)\n  CHECK_ITERABLE_APPROX(initial_electron_fraction, electron_fraction);\n}\n\nvoid test_variable_fixer(\n    const VariableFixing::RadiallyFallingFloor<3>& variable_fixer) {\n  Scalar<DataVector> pressure{DataVector{0.0, 1.e-8, 2.0, -5.5, 3.2}};\n  Scalar<DataVector> density{DataVector{2.3, -4.2, 1.e-10, 0.0, -0.1}};\n  Scalar<DataVector> specific_internal_energy{\n      DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n  Scalar<DataVector> temperature{DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n  Scalar<DataVector> electron_fraction{DataVector{1.0, 2.0, 3.0, 4.0, 5.0}};\n\n  const double root_three = sqrt(3.0);\n  const DataVector expected_density{\n      2.3, 1.e-5 * pow(3, -0.75),\n      1.e-10,  // quantities at a radius below\n               // `radius_at_which_to_begin_applying_floor` do not get fixed.\n      1.e-5 * pow(3, -0.75), 1.e-5 * pow(2.0 * root_three, -1.5)};\n  const DataVector x{-2.0, -1.0, 0.0, 1.0, 2.0};\n  const DataVector y{-2.0, -1.0, 0.0, 1.0, 2.0};\n  const DataVector z{-2.0, -1.0, 0.0, 1.0, 2.0};\n  tnsr::I<DataVector, 3, Frame::Inertial> coords{{{x, y, z}}};\n\n  const EquationsOfState::PolytropicFluid<true> polytrope{1.0, 2.0};\n  const EquationsOfState::IdealFluid<true> ideal_fluid{5.0 / 3.0};\n\n  variable_fixer(&density, &pressure, &specific_internal_energy, &temperature,\n                 &electron_fraction, coords, polytrope);\n\n  auto expected_pressure = get(polytrope.pressure_from_density(density));\n  // The i = 2 entry should not change, b/c the radial coordinate <\n  // minimum_radius_at_which_to_apply_floor_\n  expected_pressure[2] = pressure.get()[2];\n\n  CHECK_ITERABLE_APPROX(pressure.get(), expected_pressure);\n\n  CHECK_ITERABLE_APPROX(density.get(), expected_density);\n\n  // Ensure eos calls change proper values\n\n  // 1D\n  test_eos_call(variable_fixer, polytrope, coords);\n\n  // 2D\n  test_eos_call(variable_fixer, ideal_fluid, coords);\n\n  // 3D\n  const auto ideal_fluid_3d = ideal_fluid.promote_to_3d_eos();\n  test_eos_call(variable_fixer, *ideal_fluid_3d, coords);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.VariableFixing.RadiallyFallingFloor\",\n                  \"[VariableFixing][Unit]\") {\n  VariableFixing::RadiallyFallingFloor<3> variable_fixer{1.e-4, 1.e-5, -1.5,\n                                                         1.e-7 / 3.0, -2.5};\n  test_variable_fixer(variable_fixer);\n  test_serialization(variable_fixer);\n\n  const auto fixer_from_options =\n      TestHelpers::test_creation<VariableFixing::RadiallyFallingFloor<3>>(\n          \"MinimumRadius: 1.e-4\\n\"\n          \"ScaleDensityFloor: 1.e-5\\n\"\n          \"PowerDensityFloor: -1.5\\n\"\n          \"ScalePressureFloor: 0.33333333333333333e-7\\n\"\n          \"PowerPressureFloor: -2.5\\n\");\n  test_variable_fixer(fixer_from_options);\n}\n"
  },
  {
    "path": "tests/Unit/Evolution/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/Executables/CMakeLists.txt",
    "content": "## Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# These tests should get moved out of Unit eventually.\n\nset(_COMPOSE_OUTPUT ${CMAKE_BINARY_DIR}/Test_ConvertComposeTableOutput.h5)\n\nadd_test(\n  NAME \"Unit.Executables.ConvertComposeTable\"\n  COMMAND\n  ${SHELL_EXECUTABLE}\n  -c\n  \"rm -f ${_COMPOSE_OUTPUT} && \\\n   ${CMAKE_BINARY_DIR}/bin/ConvertComposeTable \\\n   --compose-directory ${CMAKE_SOURCE_DIR}/tests/Unit/IO/ \\\n   --output ${_COMPOSE_OUTPUT} \\\n   --eos-subfile \\\"/EosTable\\\" \\\n   && rm ${_COMPOSE_OUTPUT}\"\n  )\nset_standalone_test_properties(\"Unit.Executables.ConvertComposeTable\")\nset_tests_properties(\n    \"Unit.Executables.ConvertComposeTable\"\n    PROPERTIES\n    LABELS \"standalone;unit\")\nadd_dependencies(\n  unit-tests\n  ConvertComposeTable\n  )\nadd_dependencies(\n  test-executables\n  ConvertComposeTable\n  MinimalExample\n  )\n\nadd_standalone_test(\n  \"Unit.Executables.TimeStepperSummary\"\n  EXECUTABLE \"TimeStepperSummary\"\n  REGEX_TO_MATCH \"AdamsBashforth\\\\[2\\\\][^\\\\n]*N *Y\"\n  )\n\nset(\n  TEST_PREPROCESSCCEWORLDTUBE_LIBRARIES\n  Boost::boost\n  Cce\n  CceHelpers\n  DataStructures\n  GeneralRelativitySolutions\n  Spectral\n  SpinWeightedSphericalHarmonics\n  )\n\nset(EXECUTABLE \"Test_PreprocessCceWorldtube\")\n\nadd_standalone_test_executable(${EXECUTABLE})\n\ntarget_compile_definitions(\n  ${EXECUTABLE}\n  PUBLIC\n  BINDIR=\"${CMAKE_BINARY_DIR}\"\n  )\n\ntarget_link_libraries(\n  \"${EXECUTABLE}\"\n  PRIVATE\n  \"${TEST_PREPROCESSCCEWORLDTUBE_LIBRARIES}\")\n\nadd_test(\n  NAME \"Unit.Executables.PreprocessCceWorldtube\"\n  COMMAND\n  ${SHELL_EXECUTABLE}\n  -c\n  \"rm -rf PreprocessCceWorldtube &&\n   mkdir -p PreprocessCceWorldtube && \\\n   cd PreprocessCceWorldtube && \\\n   ${CMAKE_BINARY_DIR}/bin/${EXECUTABLE}\"\n  )\n\nadd_standalone_test(\n  \"Unit.Executables.MinimalExample\"\n  EXECUTABLE \"MinimalExample\"\n)\n"
  },
  {
    "path": "tests/Unit/Executables/Test_PreprocessCceWorldtube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <brigand/brigand.hpp>\n#include <cstdlib>\n#include <fstream>\n#include <optional>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeModeRecorder.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Math.hpp\"\n\n#if defined(__GNUC__) and not defined(__clang__)\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmissing-declarations\"\n#endif  // defined(__GNUC__) and not defined(__clang__)\n// Need this for linking, but it doesn't do anything\nextern \"C\" void CkRegisterMainModule() {}\n#if defined(__GNUC__) and not defined(__clang__)\n#pragma GCC diagnostic pop\n#endif  // defined(__GNUC__) and not defined(__clang__)\n\nnamespace {\nconstexpr size_t number_of_times = 30;\n\ndouble compute_time(const double target_time, const size_t time_index) {\n  // This formula matches the one in BoundaryTestHelpers.hpp which we use to\n  // write some of the worldtube data to disk\n  return 0.1 * static_cast<double>(time_index) + target_time - 1.5;\n}\n\nusing modal_tags = Cce::Tags::worldtube_boundary_tags_for_writing<\n    Spectral::Swsh::Tags::SwshTransform>;\nusing ExpectedDataType = std::vector<Variables<modal_tags>>;\n\ntemplate <typename Solution>\nExpectedDataType create_expected_data(const size_t l_max,\n                                      const double target_time,\n                                      const double extraction_radius,\n                                      const Solution& solution,\n                                      const double amplitude,\n                                      const double frequency) {\n  const size_t computation_l_max = 3 * l_max;\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(computation_l_max);\n  Variables<Cce::Tags::characteristic_worldtube_boundary_tags<\n      Cce::Tags::BoundaryValue>>\n      boundary_data_variables{number_of_angular_points};\n\n  const size_t libsharp_size =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(computation_l_max);\n  tnsr::ii<ComplexModalVector, 3> spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dt_spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dr_spatial_metric_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dt_shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dr_shift_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dt_lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dr_lapse_coefficients{libsharp_size};\n\n  const size_t computation_modal_size = square(computation_l_max + 1);\n  ComplexModalVector computation_goldberg_mode_buffer{computation_modal_size};\n\n  const size_t modal_size = square(l_max + 1);\n  std::vector<Variables<modal_tags>> result{number_of_times};\n  for (size_t i = 0; i < number_of_times; i++) {\n    result[i] = Variables<modal_tags>{modal_size};\n  }\n\n  for (size_t t = 0; t < number_of_times; ++t) {\n    const double time = compute_time(target_time, t);\n    // Create fake metric nodal data\n    Cce::TestHelpers::create_fake_time_varying_data(\n        make_not_null(&spatial_metric_coefficients),\n        make_not_null(&dt_spatial_metric_coefficients),\n        make_not_null(&dr_spatial_metric_coefficients),\n        make_not_null(&shift_coefficients),\n        make_not_null(&dt_shift_coefficients),\n        make_not_null(&dr_shift_coefficients),\n        make_not_null(&lapse_coefficients),\n        make_not_null(&dt_lapse_coefficients),\n        make_not_null(&dr_lapse_coefficients), solution, extraction_radius,\n        amplitude, frequency, time, computation_l_max, false);\n\n    // Convert to Bondi nodal\n    Cce::create_bondi_boundary_data(\n        make_not_null(&boundary_data_variables), spatial_metric_coefficients,\n        dt_spatial_metric_coefficients, dr_spatial_metric_coefficients,\n        shift_coefficients, dt_shift_coefficients, dr_shift_coefficients,\n        lapse_coefficients, dt_lapse_coefficients, dr_lapse_coefficients,\n        extraction_radius, computation_l_max);\n\n    // Convert to Bondi modal\n    tmpl::for_each<modal_tags>([&](auto tag_v) {\n      using wrapped_tag = tmpl::type_from<decltype(tag_v)>;\n      using tag = typename wrapped_tag::tag;\n      constexpr int Spin = tag::type::type::spin;\n\n      SpinWeighted<ComplexDataVector, Spin> nodal_data_view;\n      nodal_data_view.set_data_ref(\n          make_not_null(&const_cast<ComplexDataVector&>(  // NOLINT\n              get(get<Cce::Tags::BoundaryValue<tag>>(boundary_data_variables))\n                  .data())));\n      SpinWeighted<ComplexModalVector, Spin> goldberg_modes;\n      goldberg_modes.set_data_ref(computation_goldberg_mode_buffer.data(),\n                                  computation_modal_size);\n\n      // First transform to coefficients using swsh_transform, and then convert\n      // libsharp coefficients into modes\n      Spectral::Swsh::libsharp_to_goldberg_modes(\n          make_not_null(&goldberg_modes),\n          Spectral::Swsh::swsh_transform(computation_l_max, 1, nodal_data_view),\n          computation_l_max);\n\n      // Restrict the values back to the correct modal size from the computation\n      // modal size\n      ComplexModalVector goldberg_mode_view;\n      goldberg_mode_view.set_data_ref(goldberg_modes.data().data(), modal_size);\n\n      get(get<wrapped_tag>(result[t])).data() = goldberg_mode_view;\n    });\n  }\n\n  return result;\n}\n\n// Accuracy to which we check the data\nconstexpr double epsilon = 1.e-12;\n\nvoid check_expected_data(const std::string& output_filename, const size_t l_max,\n                         const ExpectedDataType& expected_data,\n                         const ExpectedDataType& second_expected_data,\n                         const double target_time,\n                         const double second_target_time,\n                         const bool check_second_expected_data) {\n  const size_t expected_number_of_times =\n      check_second_expected_data ? 2 * number_of_times : number_of_times;\n  const h5::H5File<h5::AccessType::ReadOnly> output_file{output_filename};\n\n  CAPTURE_FOR_ERROR(output_filename);\n  CAPTURE_FOR_ERROR(epsilon);\n\n  tmpl::for_each<modal_tags>([&](auto tag_v) {\n    using wrapped_tag = tmpl::type_from<decltype(tag_v)>;\n    using tag = typename wrapped_tag::tag;\n    constexpr int Spin = tag::type::type::spin;\n    constexpr bool is_real = Spin == 0;\n\n    const std::string dataset_name = Cce::dataset_label_for_tag<tag>();\n    const auto& bondi_dat_subfile = output_file.get<h5::Dat>(dataset_name);\n    CAPTURE_FOR_ERROR(dataset_name);\n    const auto bondi_data =\n        bondi_dat_subfile.get_data<std::vector<std::vector<double>>>();\n\n    SPECTRE_PARALLEL_REQUIRE(bondi_data.size() == expected_number_of_times);\n\n    const auto check_data_for_target_time = [&](const double local_target_time,\n                                                const auto& local_expected_data,\n                                                const size_t offset) {\n      for (size_t t = 0; t < number_of_times; t++) {\n        const double exptected_time = compute_time(local_target_time, t);\n        const auto& expected_bondi_var =\n            get(get<wrapped_tag>(local_expected_data[t])).data();\n        const std::vector<double>& bondi_var = bondi_data[offset + t];\n\n        CAPTURE_FOR_ERROR(bondi_var[0]);\n        CAPTURE_FOR_ERROR(t);\n        CAPTURE_FOR_ERROR(exptected_time);\n        SPECTRE_PARALLEL_REQUIRE(bondi_var[0] == exptected_time);\n\n        (void)expected_bondi_var;\n\n        CAPTURE_FOR_ERROR(expected_bondi_var);\n        CAPTURE_FOR_ERROR(bondi_var);\n\n        for (int l = 0; l <= static_cast<int>(l_max); l++) {\n          for (int m = (is_real ? 0 : -l); m <= l; m++) {\n            const size_t goldberg_index = Spectral::Swsh::goldberg_mode_index(\n                l_max, static_cast<size_t>(l), m);\n\n            CAPTURE_FOR_ERROR(l);\n            CAPTURE_FOR_ERROR(m);\n            CAPTURE_FOR_ERROR(goldberg_index);\n            CAPTURE_FOR_ERROR(expected_bondi_var[goldberg_index]);\n            std::complex<double> written_mode{};\n            size_t matrix_index = 0;\n            if (is_real) {\n              if (m == 0) {\n                matrix_index = static_cast<size_t>(square(l));  // NOLINT\n                written_mode =\n                    std::complex<double>{bondi_var[1 + matrix_index], 0.0};\n              } else {\n                matrix_index =\n                    static_cast<size_t>(square(l) + 2 * abs(m));  // NOLINT\n                written_mode =\n                    ((m > 0 or abs(m) % 2 == 0) ? 1.0 : -1.0) *\n                    std::complex<double>{bondi_var[1 + matrix_index - 1],\n                                         sgn(m) * bondi_var[1 + matrix_index]};\n              }\n            } else {\n              matrix_index = goldberg_index;\n              written_mode = std::complex<double>{\n                  bondi_var[1 + 2 * matrix_index],\n                  bondi_var[1 + 2 * matrix_index + 1],\n              };\n            }\n            CAPTURE_FOR_ERROR(matrix_index);\n            CAPTURE_FOR_ERROR(written_mode);\n            SPECTRE_PARALLEL_REQUIRE(equal_within_roundoff(\n                expected_bondi_var[goldberg_index], written_mode, epsilon));\n          }\n        }\n      }\n    };\n\n    check_data_for_target_time(target_time, expected_data, 0);\n\n    if (check_second_expected_data) {\n      check_data_for_target_time(second_target_time, second_expected_data,\n                                 number_of_times);\n    }\n\n    output_file.close_current_object();\n  });\n}\n\nvoid write_input_file(const std::string& input_data_format,\n                      const std::string& input_file_name,\n                      const std::vector<std::string>& input_worldtube_filenames,\n                      const std::string& output_filename,\n                      const std::optional<double>& worldtube_radius,\n                      const bool descending_m) {\n  std::string input_file =\n      \"# Distributed under the MIT License.\\n\"\n      \"# See LICENSE.txt for details.\\n\"\n      \"\\n\"\n      \"InputH5File: \";\n\n  if (input_worldtube_filenames.size() > 1) {\n    input_file += \"[\";\n  }\n\n  for (size_t i = 0; i < input_worldtube_filenames.size(); i++) {\n    input_file += input_worldtube_filenames[i];\n    if (i != input_worldtube_filenames.size() - 1) {\n      input_file += \", \";\n    }\n  }\n\n  if (input_worldtube_filenames.size() > 1) {\n    input_file += \"]\";\n  }\n\n  input_file += \"\\nOutputH5File: \" + output_filename + \"\\n\";\n  input_file += \"InputDataFormat:\";\n  if (input_data_format == \"FirstOrderMetricNodal\") {\n    input_file +=\n        \"\\n\"\n        \"  AdmMetricNodal:\\n\"\n        \"    Lapse:\\n\"\n        \"      Advective: True\\n\"\n        \"    Shift:\\n\"\n        \"      Advective: True\\n\"\n        \"      FirstOrderDriverFactor: 0.75\\n\";\n  } else if (input_data_format == \"ChristoffelMetricNodal\") {\n    input_file +=\n        \"\\n\"\n        \"  AdmMetricNodal:\\n\"\n        \"    Lapse:\\n\"\n        \"      Advective: True\\n\"\n        \"    Shift:\\n\"\n        \"      Advective: True\\n\"\n        \"      ConformalChristoffelFactor: 1.0\\n\"\n        \"      SecondOrderDriverEta: 2.0\\n\";\n  } else {\n    input_file += \" \" + input_data_format + \"\\n\";\n  }\n  input_file +=\n      \"ExtractionRadius: \" +\n      (worldtube_radius.has_value() ? std::to_string(worldtube_radius.value())\n                                    : \"Auto\") +\n      \"\\n\";\n  input_file += \"DescendingM: \"s + (descending_m ? \"True\\n\" : \"False\\n\");\n  input_file +=\n      \"FixSpecNormalization: False\\n\"\n      \"BufferDepth: Auto\\n\"\n      \"LMaxFactor: 3\\n\";\n\n  std::ofstream yaml_file(input_file_name);\n  yaml_file << input_file;\n  yaml_file.close();\n}\n}  // namespace\n\nint main() {\n  const size_t l_max = 16;\n  const double target_time = 20.0;\n  const double second_target_time = target_time + 20.0;\n  const double worldtube_radius = 123.0;\n  // These are just to create fake data\n  const double frequency = 0.01;\n  const double amplitude = 0.01;\n\n  const double mass = 3.5;\n  const std::array<double, 3> spin{-0.3, -0.2, 0.1};\n  const std::array<double, 3> center{0.0, 0.0, 0.0};\n  const gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  // Input worldtube H5 filenames\n  // Some have the worldtube radius and some don't to test the ExtractionRadius\n  // option\n  const std::string metric_modal_input_worldtube_filename{\n      \"Test_InputMetricModal_R0123.h5\"};\n  const std::string metric_modal_spec_input_worldtube_filename{\n      \"Test_InputMetricModalSpec_R0123.h5\"};\n  const std::string first_order_metric_nodal_input_worldtube_filename{\n      \"Test_InputFirstOrderMetricNodal.h5\"};\n  const std::string christoffel_metric_nodal_input_worldtube_filename{\n      \"Test_InputChristoffelMetricNodal.h5\"};\n  const std::string metric_nodal_1_input_worldtube_filename{\n      \"Test_InputMetricNodal_1.h5\"};\n  const std::string metric_nodal_2_input_worldtube_filename{\n      \"Test_InputMetricNodal_2.h5\"};\n  const std::string bondi_modal_1_input_worldtube_filename{\n      \"Test_InputBondiModal_1_R0123.h5\"};\n  const std::string bondi_modal_2_input_worldtube_filename{\n      \"Test_InputBondiModal_2_R0123.h5\"};\n  const std::string bondi_nodal_input_worldtube_filename{\n      \"Test_InputBondiNodal_R0123.h5\"};\n\n  // Output worldtube H5 filenames\n  const std::string metric_modal_output_worldtube_filename{\n      \"Test_OutputMetricModal_R0123.h5\"};\n  const std::string metric_modal_spec_output_worldtube_filename{\n      \"Test_OutputMetricModalSpec_R0123.h5\"};\n  const std::string first_order_metric_nodal_output_worldtube_filename{\n      \"Test_OutputFirstOrderMetricNodal.h5\"};\n  const std::string christoffel_metric_nodal_output_worldtube_filename{\n      \"Test_OutputChristoffelMetricNodal.h5\"};\n  const std::string metric_nodal_output_worldtube_filename{\n      \"Test_OutputMetricNodal.h5\"};\n  const std::string bondi_modal_output_worldtube_filename{\n      \"Test_OutputBondiModal_R0123.h5\"};\n  const std::string bondi_nodal_output_worldtube_filename{\n      \"Test_OutputBondiNodal_R0123.h5\"};\n\n  // Write metric data\n  Cce::TestHelpers::write_test_file<ComplexModalVector, false>(\n      solution, metric_modal_input_worldtube_filename, target_time,\n      worldtube_radius, frequency, amplitude, l_max, false);\n  Cce::TestHelpers::write_test_file<ComplexModalVector, false>(\n      solution, metric_modal_spec_input_worldtube_filename, target_time,\n      worldtube_radius, frequency, amplitude, l_max, true);\n  Cce::TestHelpers::write_test_file<DataVector, false>(\n      solution, first_order_metric_nodal_input_worldtube_filename, target_time,\n      worldtube_radius, frequency, amplitude, l_max, false, true);\n  Cce::TestHelpers::write_test_file<DataVector, false>(\n      solution, christoffel_metric_nodal_input_worldtube_filename, target_time,\n      worldtube_radius, frequency, amplitude, l_max, false, true);\n  Cce::TestHelpers::write_test_file<DataVector, false>(\n      solution, metric_nodal_1_input_worldtube_filename, target_time,\n      worldtube_radius, frequency, amplitude, l_max, false);\n  Cce::TestHelpers::write_test_file<DataVector, false>(\n      solution, metric_nodal_2_input_worldtube_filename, second_target_time,\n      worldtube_radius, frequency, amplitude, l_max, false);\n\n  // Write bondi data\n  Cce::TestHelpers::write_test_file<ComplexModalVector, true>(\n      solution, bondi_modal_1_input_worldtube_filename, target_time,\n      worldtube_radius, frequency, amplitude, l_max, false);\n  Cce::TestHelpers::write_test_file<ComplexModalVector, true>(\n      solution, bondi_modal_2_input_worldtube_filename, second_target_time,\n      worldtube_radius, frequency, amplitude, l_max, false);\n  Cce::TestHelpers::write_test_file<DataVector, true>(\n      solution, bondi_nodal_input_worldtube_filename, target_time,\n      worldtube_radius, frequency, amplitude, l_max, false);\n\n  // Write input file\n  write_input_file(\"MetricModal\", \"MetricModal.yaml\",\n                   {metric_modal_input_worldtube_filename},\n                   metric_modal_output_worldtube_filename, std::nullopt, false);\n  write_input_file(\"MetricModal\", \"MetricModalSpec.yaml\",\n                   {metric_modal_spec_input_worldtube_filename},\n                   metric_modal_spec_output_worldtube_filename, std::nullopt,\n                   true);\n  write_input_file(\"FirstOrderMetricNodal\", \"FirstOrderMetricNodal.yaml\",\n                   {first_order_metric_nodal_input_worldtube_filename},\n                   first_order_metric_nodal_output_worldtube_filename,\n                   {worldtube_radius}, false);\n  write_input_file(\"ChristoffelMetricNodal\", \"ChristoffelMetricNodal.yaml\",\n                   {christoffel_metric_nodal_input_worldtube_filename},\n                   christoffel_metric_nodal_output_worldtube_filename,\n                   {worldtube_radius}, false);\n  write_input_file(\"MetricNodal\", \"MetricNodal.yaml\",\n                   {metric_nodal_1_input_worldtube_filename,\n                    metric_nodal_2_input_worldtube_filename},\n                   metric_nodal_output_worldtube_filename, {worldtube_radius},\n                   false);\n  write_input_file(\"BondiModal\", \"BondiModal.yaml\",\n                   {bondi_modal_1_input_worldtube_filename,\n                    bondi_modal_2_input_worldtube_filename},\n                   bondi_modal_output_worldtube_filename, std::nullopt, false);\n  write_input_file(\n      \"BondiNodal\", \"BondiNodal.yaml\", {bondi_nodal_input_worldtube_filename},\n      bondi_nodal_output_worldtube_filename, {worldtube_radius}, false);\n\n// Get path to executable with a macro set in CMakeLists.txt\n#ifdef BINDIR\n  std::string executable = BINDIR;\n#else\n  std::string executable = \"nothing\";\n  ERROR(\n      \"BINDIR preprocessor macro not set from CMake. Something is wrong with \"\n      \"the build system.\");\n#endif\n  if (not executable.ends_with(\"/\")) {\n    executable += \"/\";\n  }\n  executable += \"bin/PreprocessCceWorldtube\";\n\n  const auto call_preprocess_cce_worldtube =\n      [&](const std::string& input_file_name) {\n        const std::string to_execute = executable + \" --input-file \" +\n                                       input_file_name + \".yaml > \" +\n                                       input_file_name + \".out 2>&1\";\n\n        CAPTURE_FOR_ERROR(input_file_name);\n        CAPTURE_FOR_ERROR(to_execute);\n        const int exit_code = std::system(to_execute.c_str());  // NOLINT\n\n        SPECTRE_PARALLEL_REQUIRE(exit_code == 0);\n        (void)exit_code;\n      };\n\n  // Call PreprocessCceWorldtube in a shell\n  call_preprocess_cce_worldtube(\"MetricModal\");\n  call_preprocess_cce_worldtube(\"MetricModalSpec\");\n  call_preprocess_cce_worldtube(\"FirstOrderMetricNodal\");\n  call_preprocess_cce_worldtube(\"ChristoffelMetricNodal\");\n  call_preprocess_cce_worldtube(\"MetricNodal\");\n  call_preprocess_cce_worldtube(\"BondiModal\");\n  call_preprocess_cce_worldtube(\"BondiNodal\");\n\n  // Create the expected bondi modal data\n  const auto expected_data = create_expected_data(\n      l_max, target_time, worldtube_radius, solution, amplitude, frequency);\n  const auto second_expected_data =\n      create_expected_data(l_max, second_target_time, worldtube_radius,\n                           solution, amplitude, frequency);\n\n  // Check that the expected bondi modal data is what was written in the output\n  // files for the different InputDataFormats. We don't check\n  // FirstOrderMetricNodal or ChristoffelMetricNodal because our expected data\n  // uses a different gauge than is assumed (1+log and gamma driver), and so\n  // some time derivatives will be incorrect.\n  check_expected_data(metric_modal_output_worldtube_filename, l_max,\n                      expected_data, second_expected_data, target_time,\n                      second_target_time, false);\n  check_expected_data(metric_nodal_output_worldtube_filename, l_max,\n                      expected_data, second_expected_data, target_time,\n                      second_target_time, true);\n  check_expected_data(bondi_modal_output_worldtube_filename, l_max,\n                      expected_data, second_expected_data, target_time,\n                      second_target_time, true);\n  check_expected_data(bondi_nodal_output_worldtube_filename, l_max,\n                      expected_data, second_expected_data, target_time,\n                      second_target_time, false);\n\n  return 0;\n}\n"
  },
  {
    "path": "tests/Unit/Framework/ActionTesting.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <charm++.h>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/MockDistributedObject.hpp\"\n#include \"Framework/MockRuntimeSystem.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <class ChareType>\nstruct get_array_index;\n}  // namespace Parallel\n/// \\endcond\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Structures used for mocking the parallel components framework in order\n * to test actions.\n *\n * The ActionTesting framework is designed to mock the parallel components so\n * that actions and sequences of actions can be tested in a controlled\n * environment that behaves effectively identical to the actual parallel\n * environment.\n *\n * ### The basics\n *\n * The action testing framework (ATF) works essentially identically to the\n * parallel infrastructure. A metavariables must be supplied which must at least\n * list the components used (`using component_list = tmpl::list<>`). As a simple\n * example, let's look at the test for the `Parallel::Actions::TerminatePhase`\n * action.\n *\n * The `Metavariables` is given by:\n *\n * \\snippet Test_TerminatePhase.cpp metavariables\n *\n * The component list in this case just contains a single component that is\n * described below.\n *\n * The component is templated on the metavariables, which, while not always\n * necessary, eliminates some compilation issues that may arise otherwise. The\n * component for the `TerminatePhase` test is given by:\n *\n * \\snippet Test_TerminatePhase.cpp component\n *\n * Just like with the standard parallel code, a `metavariables` type alias must\n * be present. The chare type should be `ActionTesting::MockArrayChare`,\n * `ActionTesting::MockGroupChare`, `ActionTesting::MockNodeGroupChare`, or\n * `ActionTesting::MockSingletonChare`. Currently many groups, nodegroups, and\n * singletons are treated during action testing as a one-element array\n * component, but that usage is deprecated and will eventually be removed.\n * The `index_type` must be whatever the actions will use to index\n * the array. In the case of a singleton `int` is recommended. Finally, the\n * `phase_dependent_action_list` must be specified just as in the cases for\n * parallel executables. In this case only the `Testing` phase is used and only\n * has the `TerminatePhase` action listed.\n *\n * The `SPECTRE_TEST_CASE` is\n *\n * \\snippet Test_TerminatePhase.cpp test case\n *\n * The type alias `component = Component<Metavariables>` is just used to reduce\n * the amount of typing needed. The ATF provides a\n * `ActionTesting::MockRuntimeSystem` class, which is the code that takes the\n * place of the parallel runtime system (RTS) (e.g. Charm++) that manages the\n * actions and components. The constructor of\n * `ActionTesting::MockRuntimeSystem` takes a `std::vector<size_t>` whose\n * length is the number of (mocked) nodes and whose values are the number of\n * (mocked) cores on each node; the RTS runs only on a single core, but it\n * keeps track of which components are on which (mocked) nodes and cores\n * so that one can test some of the functionality of multiple cores/nodes.\n * Components are added to the RTS using the functions\n * `ActionTesting::emplace_array_component()`,\n * `ActionTesting::emplace_singleton_component()`,\n * `ActionTesting::emplace_group_component()`, and\n * `ActionTesting::emplace_nodegroup_component()`.  Currently there is a\n * deprecated `ActionTesting::emplace_component()` function that is the same as\n * `ActionTesting::emplace_array_component()` and can be used for the\n * deprecated usage of treating singletons, groups, and nodegroups as\n * single-element arrays.  The emplace functions take the runner by\n * `not_null`.  The array emplace function take the array index of the\n * component being inserted (`0` in the above example, but this is arbitrary),\n * and the array and singleton emplace functions take the (mocked) node and\n * core on which the component lives.  The emplace function for groups places\n * its object on all (mocked) cores and the array index is the same as the\n * global core index; the emplace function for\n * nodegroups places its object on a single (mocked) core on each\n * (mocked) node, and the array index is the same as the node index.\n * All emplace functions optionally take a parameter pack of\n * additional arguments that are forwarded to the constructor of the component.\n * These additional arguments are how input options (set in the\n * `simple_tags_from_options` type alias of the parallel component) get passed\n * to parallel components.\n *\n * With the ATF, everything is controlled explicitly, providing the ability to\n * perform full introspection into the state of the RTS at virtually any point\n * during the execution. This means that the phase is not automatically advanced\n * like it is in a parallel executable, but instead must be advanced by calling\n * the `ActionTesting::set_phase` function. While testing the `TerminatePhase`\n * action we are only interesting in checking whether the algorithm has set the\n * `terminate` flag. This is done using the `ActionTesting::get_terminate()`\n * function and is done for each distributed object (i.e. per component). The\n * next action in the action list for the current phase is invoked by using the\n * `ActionTesting::next_action()` function, which takes as arguments the runner\n * by `not_null` and the array index of the distributed object to invoke the\n * next action on.\n *\n * ### InitializeDataBox and action introspection\n *\n * Having covered the very basics of ATF let us look at the test for the\n * `Actions::Goto` action. We will introduce the functionality\n * `InitializeDataBox`,`force_next_action_to_be`, `get_next_action_index`, and\n * `get_databox_tag`. The `Goto<the_label_class>` action changes the next action\n * in the algorithm to be the `Actions::Label<the_label_class>`. For this test\n * the Metavariables is:\n *\n * \\snippet Test_Goto.cpp metavariables\n *\n * You can see that this time there are two test phases, `Testing` and\n * `Execute`.\n *\n * The component for this case is quite a bit more complicated so we will go\n * through it in detail. It is given by\n *\n * \\snippet Test_Goto.cpp component\n *\n * In addition to the `Initialization` and `Exit` phases, there are two\n * additional phases, `Testing` and `Execute`.\n * Just as before there are `metavariables`, `chare_type`, and `array_index`\n * type aliases. The `repeat_until_phase_action_list` is the list of iterable\n * actions that are called during the `Execute` phase. The\n * `Initialization` phase action list now has the action\n * `ActionTesting::InitializeDataBox`, which takes simple tags and compute tags\n * that will be added to the DataBox in the Initialization phase. How the values\n * for the simple tags are set will be made clear below when we discuss the\n * `ActionTesting::emplace_component_and_initialize()` functions. The action\n * lists for the `Testing` and `Execute` phases are fairly simple and\n * not the focus of this discussion.\n *\n * Having discussed the metavariables and component, let us now look at the test\n * case.\n *\n * \\snippet Test_Goto.cpp test case\n *\n * Just as for the `TerminatePhase` test we have some type aliases to reduce the\n * amount of typing needed and to make the test easier to read. The runner is\n * again created with the default constructor. However, the component is now\n * inserted using the functions\n * `ActionTesting::emplace_array_component_and_initialize()`,\n * `ActionTesting::emplace_singleton_component_and_initialize()`,\n * `ActionTesting::emplace_group_component_and_initialize()`,\n * and `ActionTesting::emplace_nodegroup_component_and_initialize()`.\n * There is a deprecated function\n * `ActionTesting::emplace_component_and_initialize()` that is used when one\n * treats groups, nodegroups, and singletons as arrays. The third argument to\n * the `ActionTesting::emplace_component_and_initialize()` functions is a\n * `tuples::TaggedTuple` of the simple tags, which is to be populated with the\n * initial values for the component. Note that there is no call to\n * `ActionTesting::set_phase (&runner, Parallel::Phase::Initialization)`:\n * this and the required action invocation is handled internally by the\n * `ActionTesting::emplace_component_and_initialize()` functions.\n *\n * Once the phase is set the next action to be executed is set to be\n * `Actions::Label<Label1>` by calling the\n * `MockRuntimeSystem::force_next_action_to_be()`  member function of the\n * runner. The argument to the function is the array index of the component for\n * which to set the next action. After the `Label<Label1>` action is invoked we\n * check that the next action is the fourth (index `3` because we use zero-based\n * indexing) by calling the `MockRuntimeSystem::get_next_action_index()` member\n * function. For clarity, the indices of the actions in the `Phase::Testing`\n * phase are:\n * - 0: `Actions::Goto<Label1>`\n * - 1: `Actions::Label<Label2>`\n * - 2: `Actions::Label<Label1>`\n * - 3: `Actions::Goto<Label2>`\n *\n * ### DataBox introspection\n *\n * Since the exact DataBox type of any component at some point in the action\n * list may not be known, the `ActionTesting::get_databox_tag()` function is\n * provided. An example usage of this function can be seen in the last line of\n * the test snippet above. The component and tag are passed as template\n * parameters while the runner and array index are passed as arguments. It is\n * also possible to retrieve DataBox directly using the\n * `ActionTesting::get_databox()` function if the DataBox type is known:\n *\n * \\snippet Test_ActionTesting.cpp get databox\n *\n * There is also a `gsl::not_null` overload of `ActionTesting::get_databox()`\n * that can be used to mutate tags in the DataBox. It is also possible to check\n * if an item can be retrieved from the DataBox using the\n * `ActionTesting::tag_is_retrievable()` function as follows:\n *\n * \\snippet Test_ActionTesting.cpp tag is retrievable\n *\n * ### Stub actions and invoking simple and threaded actions\n *\n * A simple action can be invoked on a distributed object or component using\n * the `ActionTesting::simple_action()` function:\n *\n * \\snippet Test_ActionTesting.cpp invoke simple action\n *\n * A threaded action can be invoked on a distributed object or component using\n * the `ActionTesting::threaded_action()` function:\n *\n * \\snippet Test_ActionTesting.cpp invoke threaded action\n *\n * Sometimes an individual action calls other actions but we still want to be\n * able to test the action and its effects in isolation. To this end the ATF\n * supports replacing calls to actions with calls to other actions. For example,\n * instead of calling `simple_action_a` we want to call `simple_action_a_mock`\n * which just verifies the data received is correct and sets a flag in the\n * DataBox that it was invoked. This can be done by setting the type aliases\n * `replace_these_simple_actions` and `with_these_simple_actions` in the\n * parallel component definition as follows:\n *\n * \\snippet Test_ActionTesting.cpp simple action replace\n *\n * The length of the two type lists must be the same and the Nth action in\n * `replace_these_simple_actions` gets replaced by the Nth action in\n * `with_these_simple_actions`. Note that `simple_action_a_mock` will get\n * invoked in any context that `simple_action_a` is called to be invoked. The\n * same feature also exists for threaded actions:\n *\n * \\snippet Test_ActionTesting.cpp threaded action replace\n *\n * Furthermore, simple actions invoked from an action are not run immediately.\n * Instead, they are queued so that order randomization and introspection may\n * occur. The simplest form of introspection is checking whether the simple\n * action queue is empty:\n *\n * \\snippet Test_ActionTesting.cpp simple action queue empty\n *\n * The `ActionTesting::invoke_queued_simple_action()` invokes the next queued\n * simple action:\n *\n * \\snippet Test_ActionTesting.cpp invoke queued simple action\n *\n * Note that the same functionality exists for threaded actions. The functions\n * are `ActionTesting::is_threaded_action_queue_empty()`, and\n * `ActionTesting::invoke_queued_threaded_action()`.\n *\n * ### Reduction Actions\n *\n * The ATF, in general, does not support reduction actions at the moment.\n * Meaning that calls to `Parallel::contribute_to_reduction` and\n * `Parallel::threaded_action<observers::ThreadedActions::WriteReductionData>`\n * are not supported. However, the ATF does support calls using\n * `WriteReductionDataRow` without having to actually write any data to disk\n * (this avoids unnecessary IO when all we really care about is if the values\n * are correct).\n *\n * If you want to test code that has a call to\n * `Parallel::threaded_action<\n * observers::ThreadedActions::WriteReductionDataRow>`,\n * you'll need to add the `TestHelpers::observers::MockObserverWriter`\n * component, found in Helpers/IO/Observers/MockWriteReductionDataRow.hpp, to\n * your test metavars (see next section for what mocking a component means).\n * Initialize the component as follows:\n *\n * \\snippet Test_MockWriteReductionDataRow.cpp initialize_component\n *\n * Then, when you want to check the data that was \"written\", you can do\n * something along the lines of\n *\n * \\snippet Test_MockWriteReductionDataRow.cpp check_mock_writer_data\n *\n * The data is stored in `MockH5File` and `MockDat` objects, which have similar\n * interfaces to `h5::H5File` and `h5::Dat`, respectively.\n *\n * ### Mocking or replacing components with stubs\n *\n * An action can invoke an action on another parallel component. In this case we\n * need to be able to tell the mocking framework to replace the component the\n * action is trying to invoke the other action on and instead use a different\n * component that we have set up to mock the original component. For example,\n * the action below invokes an action on `ComponentB`\n *\n * \\snippet Test_ActionTesting.cpp action call component b\n *\n * Let us assume we cannot use the component `ComponentB` in our test and that\n * we need to mock it. We do so using the type alias `component_being_mocked` in\n * the mock component:\n *\n * \\snippet Test_ActionTesting.cpp mock component b\n *\n * When creating the runner `ComponentBMock` is emplaced:\n *\n * \\snippet Test_ActionTesting.cpp initialize component b\n *\n * Any checks and function calls into the ATF also use `ComponentBMock`. For\n * example:\n *\n * \\snippet Test_ActionTesting.cpp component b mock checks\n *\n * ### Const global cache tags\n *\n * Actions sometimes need tags/items to be placed into the\n * `Parallel::GlobalCache`. Once the list of tags for the global cache has\n * been assembled, the associated objects need to be inserted. This is done\n * using the constructor of the `ActionTesting::MockRuntimeSystem`. For example,\n * consider the tags:\n *\n * \\snippet Test_ActionTesting.cpp tags for const global cache\n *\n * These are added into the global cache by, for example, the metavariables:\n *\n * \\snippet Test_ActionTesting.cpp const global cache metavars\n *\n * A constructor of `ActionTesting::MockRuntimeSystem` takes a\n * `tuples::TaggedTuple` of the const global cache tags. If you know the order\n * of the tags in the `tuples::TaggedTuple` you can use the constructor without\n * explicitly specifying the type of the `tuples::TaggedTuple` as follows:\n *\n * \\snippet Test_ActionTesting.cpp constructor const global cache tags known\n *\n * If you do not know the order of the tags but know all the tags that are\n * present you can use another constructor where you explicitly specify the\n * `tuples::TaggedTuple` type:\n *\n * \\snippet Test_ActionTesting.cpp constructor const global cache tags unknown\n *\n * ### Mutable global cache tags\n *\n * Similarly to const global cache tags, sometimes Actions will want to change\n * the data of a tag in the `Parallel::GlobalCache`. Consider this tag:\n *\n * \\snippet Test_ActionTesting.cpp mutable cache tag\n *\n * To indicate that this tag in the global cache may be changed, it is added\n * by, for example, the metavariables:\n *\n * \\snippet Test_ActionTesting.cpp mutable global cache metavars\n *\n * Then, exactly like the const global cache tags, the constructor of\n * `ActionTesting::MockRuntimeSystem` takes a `tuples::TaggedTuple` of the\n * mutable global cache tags as its *second* argument. The mutable global cache\n * tags are empty by default so you need not specify a `tuples::TaggedTuple` if\n * you don't have any mutable global cache tags. Here is how you would specify\n * zero const global cache tags and one mutable global cache tag:\n *\n * \\snippet Test_ActionTesting.cpp mutable global cache runner\n *\n * To mutate a tag in the mutable global cache, use the `Parallel::mutate`\n * function just like you would in a charm-aware situation.\n *\n * ### Inbox tags introspection\n *\n * The inbox tags can also be retrieved from a component by using the\n * `ActionTesting::get_inbox_tag` function. Both a const version:\n *\n * \\snippet Test_ActionTesting.cpp const get inbox tags\n *\n * and a non-const version exist:\n *\n * \\snippet Test_ActionTesting.cpp get inbox tags\n *\n * The non-const version can be used like in the above example to clear or\n * otherwise manipulate the inbox tags.\n */\nnamespace ActionTesting {}\n\nnamespace ActionTesting {\n\n/// \\cond\nstruct MockArrayChare;\nstruct MockGroupChare;\nstruct MockNodeGroupChare;\nstruct MockSingletonChare;\n/// \\endcond\n\n// Initializes the DataBox values not set via the GlobalCache. This is\n// done as part of an `Initialization` phase and is triggered using the\n// `emplace_component_and_initialize` function.\ntemplate <typename... SimpleTags, typename ComputeTagsList>\nstruct InitializeDataBox<tmpl::list<SimpleTags...>, ComputeTagsList> {\n  using simple_tags = tmpl::list<SimpleTags...>;\n  using compute_tags = ComputeTagsList;\n  using InitialValues = tuples::TaggedTuple<SimpleTags...>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent,\n            typename ArrayIndex>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (not initial_values_valid_) {\n      ERROR(\n          \"The values being used to construct the initial DataBox have not \"\n          \"been set.\");\n    }\n    initial_values_valid_ = false;\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box),\n        std::move(tuples::get<SimpleTags>(initial_values_))...);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  /// Sets the initial values of simple tags in the DataBox.\n  static void set_initial_values(const InitialValues& t) {\n    initial_values_ = serialize_and_deserialize<InitialValues>(t);\n    initial_values_valid_ = true;\n  }\n\n private:\n  static bool initial_values_valid_;\n  static InitialValues initial_values_;\n};\n\n/// \\cond\ntemplate <typename... SimpleTags, typename ComputeTagsList>\ntuples::TaggedTuple<SimpleTags...> InitializeDataBox<\n    tmpl::list<SimpleTags...>, ComputeTagsList>::initial_values_ = {};\ntemplate <typename... SimpleTags, typename ComputeTagsList>\nbool InitializeDataBox<tmpl::list<SimpleTags...>,\n                       ComputeTagsList>::initial_values_valid_ = false;\n/// \\endcond\n\nnamespace ActionTesting_detail {\ntemplate <typename ChareType>\nstruct charm_element_proxy;\ntemplate <>\nstruct charm_element_proxy<MockArrayChare> : public CProxyElement_ArrayElement {\n};\ntemplate <>\nstruct charm_element_proxy<MockGroupChare> : public CProxyElement_IrrGroup {};\ntemplate <>\nstruct charm_element_proxy<MockNodeGroupChare>\n    : public CProxyElement_NodeGroup {};\ntemplate <>\nstruct charm_element_proxy<MockSingletonChare>\n    : public CProxyElement_ArrayElement {};\n\n// A mock class for the Charm++ generated CProxyElement_AlgorithmArray. This\n// is each element obtained by indexing a CProxy_AlgorithmArray.\ntemplate <typename Component, typename InboxTagList>\nclass MockDistributedObjectProxy\n    : public charm_element_proxy<typename Component::chare_type> {\n public:\n  using Inbox = tuples::tagged_tuple_from_typelist<InboxTagList>;\n\n  MockDistributedObjectProxy() = default;\n\n  MockDistributedObjectProxy(\n      size_t mock_node, size_t mock_local_core,\n      MockDistributedObject<Component>& mock_distributed_object, Inbox& inbox)\n      : mock_node_(mock_node),\n        mock_local_core_(mock_local_core),\n        mock_distributed_object_(&mock_distributed_object),\n        inbox_(&inbox) {}\n\n  template <typename InboxTag, typename Data>\n  void receive_data(const typename InboxTag::temporal_id& id, Data&& data,\n                    const bool enable_if_disabled = false) {\n    // The variable `enable_if_disabled` might be useful in the future but is\n    // not needed now. However, it is required by the interface to be compliant\n    // with the Algorithm invocations.\n    (void)enable_if_disabled;\n    InboxTag::insert_into_inbox(make_not_null(&tuples::get<InboxTag>(*inbox_)),\n                                id, std::forward<Data>(data));\n  }\n\n  template <typename InboxTag, typename MessageType>\n  void receive_data(MessageType* message) {\n    InboxTag::insert_into_inbox(make_not_null(&tuples::get<InboxTag>(*inbox_)),\n                                message);\n  }\n\n  template <typename Action, typename... Args>\n  void simple_action(std::tuple<Args...> args) {\n    mock_distributed_object_->template simple_action<Action>(std::move(args));\n  }\n\n  template <typename Action>\n  void simple_action() {\n    mock_distributed_object_->template simple_action<Action>();\n  }\n\n  template <typename Action, typename... Args>\n  void threaded_action(std::tuple<Args...> args) {\n    mock_distributed_object_->template threaded_action<Action>(std::move(args));\n  }\n\n  template <typename Action>\n  void threaded_action() {\n    mock_distributed_object_->template threaded_action<Action>();\n  }\n\n  void set_terminate(bool t) { mock_distributed_object_->set_terminate(t); }\n\n  // Actions may call this, but since tests step through actions manually it has\n  // no effect.\n  void perform_algorithm() {}\n  void perform_algorithm(const bool /*restart_if_terminated*/) {}\n\n  MockDistributedObject<Component>* ckLocal() {\n    return (mock_distributed_object_->my_node() ==\n                static_cast<int>(mock_node_) and\n            mock_distributed_object_->my_local_rank() ==\n                static_cast<int>(mock_local_core_))\n               ? mock_distributed_object_\n               : nullptr;\n  }\n\n  MockDistributedObject<Component>* ckLocalBranch() {\n    return (mock_distributed_object_->my_node() ==\n                static_cast<int>(mock_node_) and\n            (std::is_same_v<MockNodeGroupChare,\n                            typename Component::chare_type> or\n             mock_distributed_object_->my_local_rank() ==\n                 static_cast<int>(mock_local_core_)))\n               ? mock_distributed_object_\n               : nullptr;\n  }\n\n  // This does not create a new MockDistributedObject as dynamically\n  // creating/destroying MockDistributedObjects is not supported; it must be\n  // called on an existing MockDistrubtedObject.\n  template <typename CacheProxy>\n  void insert(\n      const CacheProxy& /*global_cache_proxy*/,\n      Parallel::Phase /*current_phase*/,\n      const std::unordered_map<Parallel::Phase, size_t>& /*phase_bookmarks*/,\n      const std::unique_ptr<Parallel::Callback>& callback) {\n    callback->invoke();\n  }\n\n  // This does nothing as dynamically creating/destroying MockDistributedObjects\n  // is not supported; the mock object will still exist...\n  void ckDestroy() {}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {\n    ERROR(\n        \"Should not try to serialize the MockDistributedObjectProxy. \"\n        \"If you encountered this error you are using the mocking framework \"\n        \"in a way that it was not intended to be used. It may be possible \"\n        \"to extend it to more use cases but it is recommended you file an \"\n        \"issue to discuss before modifying the mocking framework.\");\n  }\n\n private:\n  // mock_node_ and mock_local_core_ are the (mocked) node and core\n  // that this MockDistributedObjectProxy lives on.  This is different\n  // than the (mock) node and core that the referred-to MockDistributedObject\n  // lives on.\n  size_t mock_node_{0};\n  size_t mock_local_core_{0};\n  MockDistributedObject<Component>* mock_distributed_object_{nullptr};\n  Inbox* inbox_{nullptr};\n};\n\ntemplate <typename ChareType>\nstruct charm_base_proxy;\ntemplate <>\nstruct charm_base_proxy<MockArrayChare> : public CProxy_ArrayElement {};\ntemplate <>\nstruct charm_base_proxy<MockGroupChare> : public CProxy_IrrGroup {};\ntemplate <>\nstruct charm_base_proxy<MockNodeGroupChare> : public CProxy_NodeGroup {};\ntemplate <>\nstruct charm_base_proxy<MockSingletonChare> : public CProxy_ArrayElement {};\n\n// A mock class for the Charm++ generated CProxy_AlgorithmArray or\n// CProxy_AlgorithmGroup or CProxy_AlgorithmNodeGroup or\n// CProxy_AlgorithmSingleton.\ntemplate <typename Component, typename Index, typename InboxTagList,\n          typename ChareType>\nclass MockCollectionOfDistributedObjectsProxy\n    : public charm_base_proxy<ChareType> {\n public:\n  using Inboxes =\n      std::unordered_map<Index,\n                         tuples::tagged_tuple_from_typelist<InboxTagList>>;\n  using CollectionOfMockDistributedObjects =\n      std::unordered_map<Index, MockDistributedObject<Component>>;\n\n  MockCollectionOfDistributedObjectsProxy() : inboxes_(nullptr) {}\n\n  template <typename InboxTag, typename Data>\n  void receive_data(const typename InboxTag::temporal_id& id, const Data& data,\n                    const bool enable_if_disabled = false) {\n    // Call (and possibly create/store) a proxy on the local node and core\n    // that references each of the mock_distributed_objects.\n    for (const auto& key_value_pair : *mock_distributed_objects_) {\n      if (proxies_to_mock_distributed_objects_.count(key_value_pair.first) ==\n          0) {\n        proxies_to_mock_distributed_objects_.emplace(std::make_pair(\n            key_value_pair.first,\n            MockDistributedObjectProxy<Component, InboxTagList>(\n                mock_node_, mock_local_core_,\n                mock_distributed_objects_->at(key_value_pair.first),\n                inboxes_->operator[](key_value_pair.first))));\n      }\n      proxies_to_mock_distributed_objects_.at(key_value_pair.first)\n          .template receive_data<InboxTag>(id, data, enable_if_disabled);\n    }\n  }\n\n  void set_data(CollectionOfMockDistributedObjects* mock_distributed_objects,\n                Inboxes* inboxes, const size_t mock_node,\n                const size_t mock_local_core, const size_t mock_global_core) {\n    mock_distributed_objects_ = mock_distributed_objects;\n    inboxes_ = inboxes;\n    mock_node_ = mock_node;\n    mock_local_core_ = mock_local_core;\n    mock_global_core_ = mock_global_core;\n  }\n\n  MockDistributedObjectProxy<Component, InboxTagList>& operator[](\n      const Index& index) {\n    ASSERT(mock_distributed_objects_->count(index) == 1,\n           \"Should have exactly one mock distributed object with key '\"\n               << index << \"' but found \"\n               << mock_distributed_objects_->count(index)\n               << \". The known keys are \" << keys_of(*mock_distributed_objects_)\n               << \". Did you forget to add a mock distributed object when \"\n                  \"constructing \"\n                  \"the MockRuntimeSystem?\");\n    if (proxies_to_mock_distributed_objects_.count(index) == 0) {\n      proxies_to_mock_distributed_objects_.emplace(std::make_pair(\n          index, MockDistributedObjectProxy<Component, InboxTagList>(\n                     mock_node_, mock_local_core_,\n                     mock_distributed_objects_->at(index),\n                     inboxes_->operator[](index))));\n    }\n    return proxies_to_mock_distributed_objects_.at(index);\n  }\n\n  // ckLocalBranch should never be called on an array or singleton\n  // chare, because there is probably not a local branch on this processor.\n  // We include it here to mock groups and nodegroups.  For a mocked group,\n  // there is always one element per global core, so the index of the array\n  // is the same as the global core index.\n  // For a mocked nodegroup, there is one element per node, so the index\n  // of the array is the node index.\n  MockDistributedObject<Component>* ckLocalBranch() {\n    if constexpr (std::is_same_v<ChareType, MockGroupChare>) {\n      return std::addressof(mock_distributed_objects_->at(mock_global_core_));\n    } else if constexpr (std::is_same_v<ChareType, MockNodeGroupChare>) {\n      return std::addressof(mock_distributed_objects_->at(mock_node_));\n    } else {\n      static_assert(std::is_same_v<Component, NoSuchType>,\n                    \"Do not call ckLocalBranch for arrays or singletons\");\n    }\n  }\n\n  // ckLocal should be called only on a singleton.\n  template <typename U = ChareType,\n            typename = Requires<std::is_same_v<U, ChareType> and\n                                std::is_same_v<U, MockSingletonChare>>>\n  MockDistributedObject<Component>* ckLocal() {\n    static_assert(std::is_same_v<ChareType, MockSingletonChare>,\n                  \"Do not call ckLocal for other than a Singleton\");\n    auto& object = mock_distributed_objects_->at(0);\n    return (object.my_node() == static_cast<int>(mock_node_) and\n            object.my_local_rank() == static_cast<int>(mock_local_core_))\n               ? std::addressof(object)\n               : nullptr;\n  }\n\n  template <typename Action, typename... Args>\n  void simple_action(std::tuple<Args...> args) {\n    alg::for_each(*mock_distributed_objects_,\n                  [&args](auto& index_and_mock_distributed_object) {\n                    index_and_mock_distributed_object.second\n                        .template simple_action<Action>(args);\n                  });\n  }\n\n  template <typename Action>\n  void simple_action() {\n    alg::for_each(*mock_distributed_objects_,\n                  [](auto& index_and_mock_distributed_object) {\n                    index_and_mock_distributed_object.second\n                        .template simple_action<Action>();\n                  });\n  }\n\n  template <typename Action, typename... Args>\n  void threaded_action(std::tuple<Args...> args) {\n    static_assert(std::is_same_v<ChareType, MockNodeGroupChare>,\n                  \"Do not call threaded_action for other than a Nodegroup\");\n    alg::for_each(*mock_distributed_objects_,\n                  [&args](auto& index_and_mock_distributed_object) {\n                    index_and_mock_distributed_object.second\n                        .template threaded_action<Action>(args);\n                  });\n  }\n\n  template <typename Action>\n  void threaded_action() {\n    static_assert(std::is_same_v<ChareType, MockNodeGroupChare>,\n                  \"Do not call threaded_action for other than a Nodegroup\");\n    alg::for_each(*mock_distributed_objects_,\n                  [](auto& index_and_mock_distributed_object) {\n                    index_and_mock_distributed_object.second\n                        .template threaded_action<Action>();\n                  });\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {\n    ERROR(\n        \"Should not try to serialize the CollectionOfMockDistributedObjects. \"\n        \"If you encountered this error you are using the mocking framework \"\n        \"in a way that it was not intended to be used. It may be possible \"\n        \"to extend it to more use cases but it is recommended you file an \"\n        \"issue to discuss before modifying the mocking framework.\");\n  }\n\n private:\n  CollectionOfMockDistributedObjects* mock_distributed_objects_;\n  std::unordered_map<Index, MockDistributedObjectProxy<Component, InboxTagList>>\n      proxies_to_mock_distributed_objects_;\n  Inboxes* inboxes_;\n  // mock_node_, mock_local_core_, and mock_global_core_ are the\n  // (mock) node and core that the\n  // MockCollectionOfDistributedObjectsProxy lives on.  This is\n  // different than the (mock) nodes and cores that each element of the\n  // referred-to CollectionOfMockDistributedObjects lives on.\n  size_t mock_node_{0};\n  size_t mock_local_core_{0};\n  size_t mock_global_core_{0};\n};\n}  // namespace ActionTesting_detail\n\n/// A mock class for the CMake-generated `Parallel::Algorithms::Array`\nstruct MockArrayChare {\n  template <typename Component, typename Index>\n  using cproxy = ActionTesting_detail::MockCollectionOfDistributedObjectsProxy<\n      Component, Index,\n      typename MockDistributedObject<Component>::inbox_tags_list,\n      MockArrayChare>;\n  static std::string name() { return \"Array\"; }\n  using component_type = Parallel::Algorithms::Array;\n};\n/// A mock class for the CMake-generated `Parallel::Algorithms::Group`\nstruct MockGroupChare {\n  template <typename Component, typename Index>\n  using cproxy = ActionTesting_detail::MockCollectionOfDistributedObjectsProxy<\n      Component, Index,\n      typename MockDistributedObject<Component>::inbox_tags_list,\n      MockGroupChare>;\n  static std::string name() { return \"Group\"; }\n  using component_type = Parallel::Algorithms::Group;\n};\n/// A mock class for the CMake-generated `Parallel::Algorithms::NodeGroup`\nstruct MockNodeGroupChare {\n  template <typename Component, typename Index>\n  using cproxy = ActionTesting_detail::MockCollectionOfDistributedObjectsProxy<\n      Component, Index,\n      typename MockDistributedObject<Component>::inbox_tags_list,\n      MockNodeGroupChare>;\n  static std::string name() { return \"Nodegroup\"; }\n  using component_type = Parallel::Algorithms::Nodegroup;\n};\n/// A mock class for the CMake-generated `Parallel::Algorithms::Singleton`\nstruct MockSingletonChare {\n  template <typename Component, typename Index>\n  using cproxy = ActionTesting_detail::MockCollectionOfDistributedObjectsProxy<\n      Component, Index,\n      typename MockDistributedObject<Component>::inbox_tags_list,\n      MockSingletonChare>;\n  static std::string name() { return \"Singleton\"; }\n  using component_type = Parallel::Algorithms::Singleton;\n};\n}  // namespace ActionTesting\n\n/// \\cond HIDDEN_SYMBOLS\nnamespace Parallel {\ntemplate <>\nstruct get_array_index<ActionTesting::MockArrayChare> {\n  template <typename Component>\n  using f = typename Component::array_index;\n};\ntemplate <>\nstruct get_array_index<ActionTesting::MockGroupChare> {\n  template <typename Component>\n  using f = typename Component::array_index;\n};\ntemplate <>\nstruct get_array_index<ActionTesting::MockNodeGroupChare> {\n  template <typename Component>\n  using f = typename Component::array_index;\n};\ntemplate <>\nstruct get_array_index<ActionTesting::MockSingletonChare> {\n  template <typename Component>\n  using f = typename Component::array_index;\n};\n}  // namespace Parallel\n/// \\endcond\n"
  },
  {
    "path": "tests/Unit/Framework/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Framework)\n\nadd_subdirectory(Tests)\n\nset(LIBRARY_SOURCES\n  SetupLocalPythonEnvironment.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Boost::boost\n  DataStructuresHelpers\n  Options\n  Parallel\n  SystemUtilities\n\n  PRIVATE\n  Informer\n\n  PUBLIC\n  Catch2::Catch2\n  Charmxx::charmxx\n  DataStructures\n  ErrorHandling\n  Python::NumPy\n  Python::Python\n  Serialization\n  Utilities\n  )\n\n# Give SetupLocalPythonEnvironment access to installed Python packages.\n# The Python interpreter usually takes care of adding this directory to the path\n# at launch, but in SetupLocalPythonEnvironment we just link in Python. The\n# linked Python libs may reside in a different place than the Python interpreter\n# (in particular when the Python interpreter was symlinked to a venv). Thefore,\n# SetupLocalPythonEnvironment needs to add this directory to the path as well.\ntarget_compile_definitions(\n  ${LIBRARY}\n  PRIVATE\n  PYTHON_SITELIB=\"${Python_SITELIB}\"\n)\n"
  },
  {
    "path": "tests/Unit/Framework/CheckWithRandomValues.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <boost/preprocessor/arithmetic/inc.hpp>\n#include <boost/preprocessor/comparison/equal.hpp>\n#include <boost/preprocessor/comparison/not_equal.hpp>\n#include <boost/preprocessor/control/expr_iif.hpp>\n#include <boost/preprocessor/control/if.hpp>\n#include <boost/preprocessor/debug/assert.hpp>\n#include <boost/preprocessor/list/adt.hpp>\n#include <boost/preprocessor/list/fold_right.hpp>\n#include <boost/preprocessor/list/for_each.hpp>\n#include <boost/preprocessor/list/for_each_product.hpp>\n#include <boost/preprocessor/list/to_tuple.hpp>\n#include <boost/preprocessor/list/transform.hpp>\n#include <boost/preprocessor/logical/compl.hpp>\n#include <boost/preprocessor/logical/not.hpp>\n#include <boost/preprocessor/punctuation/is_begin_parens.hpp>\n#include <boost/preprocessor/repetition/for.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <boost/preprocessor/tuple/elem.hpp>\n#include <boost/preprocessor/tuple/enum.hpp>\n#include <boost/preprocessor/tuple/pop_front.hpp>\n#include <boost/preprocessor/tuple/push_back.hpp>\n#include <boost/preprocessor/tuple/push_front.hpp>\n#include <boost/preprocessor/tuple/rem.hpp>\n#include <boost/preprocessor/tuple/size.hpp>\n#include <boost/preprocessor/tuple/to_array.hpp>\n#include <boost/preprocessor/tuple/to_list.hpp>\n#include <boost/preprocessor/variadic/elem.hpp>\n#include <boost/preprocessor/variadic/to_list.hpp>\n#include <boost/preprocessor/variadic/to_tuple.hpp>\n#include <boost/vmd/is_empty.hpp>\n\n#include <limits>\n#include <random>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/FunctionInfo.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsStdArray.hpp\"\n\nnamespace pypp {\nnamespace TestHelpers_detail {\ntemplate <typename T>\nusing is_not_null = tt::is_a<gsl::not_null, T>;\n\ntemplate <typename T>\nstruct RemoveNotNull {\n  using type = T;\n};\n\ntemplate <typename T>\nstruct RemoveNotNull<gsl::not_null<T>> {\n  using type = std::remove_pointer_t<T>;\n};\n\ntemplate <typename MemberArg, typename UsedForSize, typename = std::nullptr_t>\nstruct ConvertToTensorImpl;\n\ntemplate <typename UsedForSize>\nstruct ConvertToTensorImpl<double, UsedForSize> {\n  static auto apply(const double value, const UsedForSize& used_for_size) {\n    return make_with_value<Scalar<DataVector>>(used_for_size, value);\n  }\n};\n\ntemplate <size_t Dim, typename UsedForSize>\nstruct ConvertToTensorImpl<std::array<double, Dim>, UsedForSize> {\n  static auto apply(const std::array<double, Dim>& arr,\n                    const UsedForSize& used_for_size) {\n    auto array_as_tensor =\n        make_with_value<tnsr::i<DataVector, Dim>>(used_for_size, 0.);\n    for (size_t i = 0; i < Dim; ++i) {\n      array_as_tensor.get(i) = gsl::at(arr, i);\n    }\n    return array_as_tensor;\n  }\n};\n\ntemplate <typename ReturnType, typename = std::nullptr_t>\nstruct ForwardToPyppImpl {\n  template <typename MemberArg, typename UsedForSize>\n  static decltype(auto) apply(const MemberArg& member_arg,\n                              const UsedForSize& /*used_for_size*/) {\n    return member_arg;\n  }\n};\n\ntemplate <typename ReturnType>\nstruct ForwardToPyppImpl<\n    ReturnType,\n    Requires<(tt::is_a_v<Tensor, ReturnType> or\n              tt::is_std_array_v<ReturnType>)and std::\n                 is_same_v<typename ReturnType::value_type, DataVector>>> {\n  template <typename MemberArg, typename UsedForSize>\n  static decltype(auto) apply(const MemberArg& member_arg,\n                              const UsedForSize& used_for_size) {\n    return ConvertToTensorImpl<MemberArg, UsedForSize>::apply(member_arg,\n                                                              used_for_size);\n  }\n};\n\n// Given the member variable of type MemberArg (either a double or array of\n// doubles), performs a conversion so that it can be correctly forwarded to\n// Pypp. If ReturnType is not a Tensor or array of DataVectors,\n// member_arg is simply forwarded, otherwise it is converted to a Tensor of\n// DataVectors.\ntemplate <typename PyppReturn, typename MemberArg, typename UsedForSize>\ndecltype(auto) forward_to_pypp(const MemberArg& member_arg,\n                               const UsedForSize& used_for_size) {\n  return ForwardToPyppImpl<PyppReturn>::apply(member_arg, used_for_size);\n}\n\ntemplate <class F, class T, class TagsList, class Klass, class... ReturnTypes,\n          class... ArgumentTypes, class... MemberArgs, size_t... ResultIs,\n          size_t... ArgumentIs, size_t... MemberArgsIs>\nvoid check_with_random_values_impl(\n    F&& f, const Klass& klass, const std::string& module_name,\n    const std::vector<std::string>& function_names, std::mt19937 generator,\n    std::array<std::uniform_real_distribution<>, sizeof...(ArgumentTypes)>\n        distributions,\n    const std::tuple<MemberArgs...>& member_args, const T& used_for_size,\n    tmpl::list<ReturnTypes...> /*return_types*/,\n    tmpl::list<ArgumentTypes...> /*argument_types*/,\n    std::index_sequence<ResultIs...> /*index_return_types*/,\n    std::index_sequence<ArgumentIs...> /*index_argument_types*/,\n    std::index_sequence<MemberArgsIs...> /*index_member_args*/,\n    // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n    TagsList /*meta*/, const double epsilon = 1.0e-12) {\n  // Note: generator and distributions cannot be const.\n  std::tuple<ArgumentTypes...> args{\n      make_with_value<ArgumentTypes>(used_for_size, 0.0)...};\n  EXPAND_PACK_LEFT_TO_RIGHT(fill_with_random_values(\n      make_not_null(&std::get<ArgumentIs>(args)), make_not_null(&generator),\n      make_not_null(&(distributions[ArgumentIs]))));\n\n  size_t count = 0;\n  const auto result = (klass.*f)(std::get<ArgumentIs>(args)...);\n  tmpl::for_each<TagsList>([&args, &used_for_size, &member_args, &epsilon,\n                            &module_name, &function_names, &count,\n                            &result](auto tag) {\n    (void)member_args;    // Avoid compiler warning\n    (void)used_for_size;  // Avoid compiler warning\n    using Tag = tmpl::type_from<decltype(tag)>;\n    const auto& function_name = function_names[count];\n    CAPTURE(module_name);\n    CAPTURE(function_name);\n    CAPTURE(args);\n    CAPTURE(member_args);\n    const auto python_result =\n        pypp::call<std::decay_t<decltype(tuples::get<Tag>(result))>>(\n            module_name, function_name, std::get<ArgumentIs>(args)...,\n            forward_to_pypp<std::decay_t<decltype(tuples::get<Tag>(result))>>(\n                std::get<MemberArgsIs>(member_args), used_for_size)...);\n    CHECK_ITERABLE_CUSTOM_APPROX(tuples::get<Tag>(result), python_result,\n                                 Approx::custom().epsilon(epsilon).scale(1.0));\n    ++count;\n  });\n}\n\ntemplate <class F, class T, class Klass, class... ArgumentTypes,\n          class... MemberArgs, size_t... ArgumentIs, size_t... MemberArgsIs>\nvoid check_with_random_values_impl(\n    F&& f, const Klass& klass, const std::string& module_name,\n    const std::string& function_name, std::mt19937 generator,\n    std::array<std::uniform_real_distribution<>, sizeof...(ArgumentTypes)>\n        distributions,\n    const std::tuple<MemberArgs...>& member_args, const T& used_for_size,\n    tmpl::list<ArgumentTypes...> /*argument_types*/,\n    std::index_sequence<ArgumentIs...> /*index_argument_types*/,\n    std::index_sequence<MemberArgsIs...> /*index_member_args*/,\n    NoSuchType /*meta*/, const double epsilon = 1.0e-12) {\n  // Note: generator and distributions cannot be const.\n  using f_info = tt::function_info<cpp20::remove_cvref_t<F>>;\n  using ResultType = typename f_info::return_type;\n  std::tuple<ArgumentTypes...> args{\n      make_with_value<ArgumentTypes>(used_for_size, 0.0)...};\n  EXPAND_PACK_LEFT_TO_RIGHT(fill_with_random_values(\n      make_not_null(&std::get<ArgumentIs>(args)), make_not_null(&generator),\n      make_not_null(&(distributions[ArgumentIs]))));\n  const auto result = [&args, &f, &klass]() {\n    (void)args;\n    if constexpr (std::is_same_v<NoSuchType, std::decay_t<Klass>>) {\n      (void)klass;\n      return f(std::get<ArgumentIs>(args)...);\n    } else {\n      return (klass.*f)(std::get<ArgumentIs>(args)...);\n    }\n  }();\n  CAPTURE(module_name);\n  CAPTURE(function_name);\n  CAPTURE(args);\n  CAPTURE(member_args);\n  const auto python_result = pypp::call<ResultType>(\n      module_name, function_name, std::get<ArgumentIs>(args)...,\n      forward_to_pypp<ResultType>(std::get<MemberArgsIs>(member_args),\n                                  used_for_size)...);\n  CHECK_ITERABLE_CUSTOM_APPROX(result, python_result,\n                               Approx::custom().epsilon(epsilon).scale(1.0));\n}\n\ntemplate <class F, class T, class Klass, class... ReturnTypes,\n          class... ArgumentTypes, class... MemberArgs, size_t... ResultIs,\n          size_t... ArgumentIs, size_t... MemberArgsIs>\nvoid check_with_random_values_impl(\n    F&& f, const Klass& klass, const std::string& module_name,\n    const std::vector<std::string>& function_names, std::mt19937 generator,\n    std::array<std::uniform_real_distribution<>, sizeof...(ArgumentTypes)>\n        distributions,\n    const std::tuple<MemberArgs...>& member_args, const T& used_for_size,\n    tmpl::list<ReturnTypes...> /*return_types*/,\n    tmpl::list<ArgumentTypes...> /*argument_types*/,\n    std::index_sequence<ResultIs...> /*index_return_types*/,\n    std::index_sequence<ArgumentIs...> /*index_argument_types*/,\n    std::index_sequence<MemberArgsIs...> /*index_member_args*/,\n    NoSuchType /* meta */, const double epsilon = 1.0e-12,\n    const std::optional<double>& initial_result_values = std::nullopt) {\n  // Note: generator and distributions cannot be const.\n  std::tuple<ReturnTypes...> results{make_with_value<ReturnTypes>(\n      used_for_size, initial_result_values.value_or(0.))...};\n  std::tuple<ArgumentTypes...> args{\n      make_with_value<ArgumentTypes>(used_for_size, 0.0)...};\n  EXPAND_PACK_LEFT_TO_RIGHT(fill_with_random_values(\n      make_not_null(&std::get<ArgumentIs>(args)), make_not_null(&generator),\n      make_not_null(&(distributions[ArgumentIs]))));\n  if (not initial_result_values.has_value()) {\n    EXPAND_PACK_LEFT_TO_RIGHT(fill_with_random_values(\n        make_not_null(&std::get<ResultIs>(results)), make_not_null(&generator),\n        make_not_null(&(distributions[0]))));\n  }\n  if constexpr (std::is_same_v<NoSuchType, std::decay_t<Klass>>) {\n    f(make_not_null(&std::get<ResultIs>(results))...,\n      std::get<ArgumentIs>(args)...);\n  } else {\n    (klass.*f)(make_not_null(&std::get<ResultIs>(results))...,\n               std::get<ArgumentIs>(args)...);\n  }\n  const auto helper = [&module_name, &function_names, &args, &results, &epsilon,\n                       &member_args, &used_for_size](auto result_i) {\n    (void)member_args;    // avoid compiler warning\n    (void)used_for_size;  // avoid compiler warning\n    constexpr size_t iter = decltype(result_i)::value;\n    const auto& function_name = function_names[iter];\n    CAPTURE(module_name);\n    CAPTURE(function_name);\n    CAPTURE(args);\n    CAPTURE(member_args);\n    const auto python_result = pypp::call<\n        std::tuple_element_t<iter, std::tuple<ReturnTypes...>>>(\n        module_name, function_name, std::get<ArgumentIs>(args)...,\n        forward_to_pypp<std::tuple_element_t<iter, std::tuple<ReturnTypes...>>>(\n            std::get<MemberArgsIs>(member_args), used_for_size)...);\n    CHECK_ITERABLE_CUSTOM_APPROX(std::get<iter>(results), python_result,\n                                 Approx::custom().epsilon(epsilon).scale(1.0));\n  };\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(std::integral_constant<size_t, ResultIs>{}));\n}\n}  // namespace TestHelpers_detail\n\n/*!\n * \\brief Tests a C++ function returning by value by comparing the result to a\n * python function\n *\n * Tests the function `f` by comparing the result to that of the python function\n * `function_name` in the file `module_name`. The function is tested by\n * generated random values in the half-open range [`lower_bound`,\n * `upper_bound`). The argument `used_for_size` is used for constructing the\n * arguments of `f` by calling `make_with_value<ArgumentType>(used_for_size,\n * 0.0)`.\n *\n * \\note You must explicitly pass the number of bounds you will be passing as\n * the first template parameter, the rest will be inferred.\n *\n * \\note If you have a test fail you can replay the scenario by feeding in the\n * seed that was printed out in the failed test as the last argument.\n *\n * \\param f The C++ function to test\n * \\param module_name The python file relative to the directory used in\n * `SetupLocalPythonEnvironment`\n * \\param function_name The name of the python function inside `module_name`\n * \\param lower_and_upper_bounds The lower and upper bounds for the randomly\n * generated numbers. Must be either an array of a single pair, or of as many\n * pairs as there are arguments to `f` that are not a `gsl::not_null`\n * \\param used_for_size The type `X` for the arguments of `f` of type\n *`Tensor<X>`\n * \\param epsilon A double specifying the comparison tolerance\n * (default 1.0e-12)\n * \\param seed The seed for the random number generator. This should only be\n * specified when debugging a failure with a particular set of random numbers,\n * in general it should be left to the default value.\n */\ntemplate <size_t NumberOfBounds, class F, class T,\n          Requires<not std::is_same_v<\n              typename tt::function_info<cpp20::remove_cvref_t<F>>::return_type,\n              void>> = nullptr>\n// The Requires is used so that we can call the std::vector<std::string> with\n// braces and not have it be ambiguous.\nvoid check_with_random_values(\n    F&& f, const std::string& module_name, const std::string& function_name,\n    const std::array<std::pair<double, double>, NumberOfBounds>&\n        lower_and_upper_bounds,\n    const T& used_for_size, const double epsilon = 1.0e-12,\n    const typename std::random_device::result_type seed =\n        std::random_device{}()) {\n  MAKE_GENERATOR(generator, seed);\n  using f_info = tt::function_info<cpp20::remove_cvref_t<F>>;\n  using number_of_not_null =\n      tmpl::count_if<typename f_info::argument_types,\n                     tmpl::bind<TestHelpers_detail::is_not_null, tmpl::_1>>;\n  using argument_types = tmpl::transform<\n      tmpl::pop_front<typename f_info::argument_types, number_of_not_null>,\n      std::decay<tmpl::_1>>;\n\n  static_assert(number_of_not_null::value == 0,\n                \"Cannot return arguments by gsl::not_null if the python \"\n                \"function name is passed as a string. If the function only \"\n                \"returns one gsl::not_null then you must pass in a one element \"\n                \"vector<string>.\");\n  static_assert(tmpl::size<argument_types>::value != 0,\n                \"The function 'f' must take at least one argument.\");\n  static_assert(NumberOfBounds == 1 or\n                    NumberOfBounds == tmpl::size<argument_types>::value,\n                \"The number of lower and upper bound pairs must be either 1 or \"\n                \"equal to the number of arguments taken by f that are not \"\n                \"gsl::not_null.\");\n  std::array<std::uniform_real_distribution<>,\n             tmpl::size<argument_types>::value>\n      distributions;\n  for (size_t i = 0; i < tmpl::size<argument_types>::value; ++i) {\n    gsl::at(distributions, i) = std::uniform_real_distribution<>{\n        gsl::at(lower_and_upper_bounds, NumberOfBounds == 1 ? 0 : i).first,\n        gsl::at(lower_and_upper_bounds, NumberOfBounds == 1 ? 0 : i).second};\n  }\n  TestHelpers_detail::check_with_random_values_impl(\n      std::forward<F>(f), NoSuchType{}, module_name, function_name, generator,\n      std::move(distributions), std::tuple<>{}, used_for_size, argument_types{},\n      std::make_index_sequence<tmpl::size<argument_types>::value>{},\n      std::make_index_sequence<0>{}, NoSuchType{}, epsilon);\n}\n\n/*!\n * \\brief Tests a C++ function returning by `gsl::not_null`  by comparing the\n * result to a python function\n *\n * Tests the function `f` by comparing the result to that of the python function\n * `function_name` in the file `module_name`. The function is tested by\n * generated random values in the half-open range [`lower_bound`, `upper_bound`)\n * for each argument. The argument `used_for_size` is used for constructing the\n * arguments of `f` by calling `make_with_value<ArgumentType>(used_for_size,\n * 0.0)`. For functions that return by `gsl::not_null`, the result will be\n * initialized with random values rather than to signaling `NaN`s. This means\n * functions do not need to support receiving a signaling `NaN` in their return\n * argument to be tested using this function. The optional argument\n * `initial_result_values` allows initializing the result buffers with a given\n * value instead of random data to test functions that mutate the result buffers\n * instead of assigning to it.\n *\n * \\note You must explicitly pass the number of bounds you will be passing as\n * the first template parameter, the rest will be inferred.\n *\n * \\note If you have a test fail you can replay the scenario by feeding in the\n * seed that was printed out in the failed test as the last argument.\n *\n * \\param f The C++ function to test\n * \\param module_name The python file relative to the directory used in\n * `SetupLocalPythonEnvironment`\n * \\param function_names The names of the python functions inside `module_name`\n * in the order that they return the `gsl::not_null` results\n * \\param lower_and_upper_bounds The lower and upper bounds for the randomly\n * generated numbers. Must be either an array of a single pair, or of as many\n * pairs as there are arguments to `f` that are not a `gsl::not_null`\n * \\param used_for_size The type `X` for the arguments of `f` of type\n * `Tensor<X>`\n * \\param epsilon A double specifying the comparison tolerance\n * (default 1.0e-12)\n * \\param seed The seed for the random number generator. This should only be\n * specified when debugging a failure with a particular set of random numbers,\n * in general it should be left to the default value.\n * \\param initial_result_values Fill the result buffers with this value instead\n * of random data before calling the function `f`.\n */\ntemplate <size_t NumberOfBounds, class F, class T>\nvoid check_with_random_values(\n    F&& f, const std::string& module_name,\n    const std::vector<std::string>& function_names,\n    const std::array<std::pair<double, double>, NumberOfBounds>&\n        lower_and_upper_bounds,\n    const T& used_for_size, const double epsilon = 1.0e-12,\n    const typename std::random_device::result_type seed =\n        std::random_device{}(),\n    const std::optional<double>& initial_result_values = std::nullopt) {\n  MAKE_GENERATOR(generator, seed);\n  using f_info = tt::function_info<cpp20::remove_cvref_t<F>>;\n  using number_of_not_null =\n      tmpl::count_if<typename f_info::argument_types,\n                     tmpl::bind<TestHelpers_detail::is_not_null, tmpl::_1>>;\n  using argument_types = tmpl::transform<\n      tmpl::pop_front<typename f_info::argument_types, number_of_not_null>,\n      std::decay<tmpl::_1>>;\n  using return_types =\n      tmpl::transform<tmpl::pop_back<typename f_info::argument_types,\n                                     tmpl::size<argument_types>>,\n                      TestHelpers_detail::RemoveNotNull<tmpl::_1>>;\n\n  static_assert(number_of_not_null::value != 0,\n                \"You must return at least one argument by gsl::not_null when \"\n                \"passing the python function names as a vector<string>. If \"\n                \"your function returns by value do not pass the function name \"\n                \"as a vector<string> but just a string.\");\n  static_assert(\n      std::is_same_v<typename f_info::return_type, void>,\n      \"A function returning by gsl::not_null must have a void return type.\");\n  static_assert(tmpl::size<argument_types>::value != 0,\n                \"The function 'f' must take at least one argument.\");\n  static_assert(NumberOfBounds == 1 or\n                    NumberOfBounds == tmpl::size<argument_types>::value,\n                \"The number of lower and upper bound pairs must be either 1 or \"\n                \"equal to the number of arguments taken by f that are not \"\n                \"gsl::not_null.\");\n  if (function_names.size() != number_of_not_null::value) {\n    ERROR(\n        \"The number of python functions passed must be the same as the number \"\n        \"of gsl::not_null arguments in the C++ function. The order of the \"\n        \"python functions must also be the same as the order of the \"\n        \"gsl::not_null arguments.\");\n  }\n  std::array<std::uniform_real_distribution<>,\n             tmpl::size<argument_types>::value>\n      distributions;\n  for (size_t i = 0; i < tmpl::size<argument_types>::value; ++i) {\n    gsl::at(distributions, i) = std::uniform_real_distribution<>{\n        gsl::at(lower_and_upper_bounds, NumberOfBounds == 1 ? 0 : i).first,\n        gsl::at(lower_and_upper_bounds, NumberOfBounds == 1 ? 0 : i).second};\n  }\n  TestHelpers_detail::check_with_random_values_impl(\n      std::forward<F>(f), NoSuchType{}, module_name, function_names, generator,\n      std::move(distributions), std::tuple<>{}, used_for_size, return_types{},\n      argument_types{},\n      std::make_index_sequence<tmpl::size<return_types>::value>{},\n      std::make_index_sequence<tmpl::size<argument_types>::value>{},\n      std::make_index_sequence<0>{}, NoSuchType{}, epsilon,\n      initial_result_values);\n}\n\n/*!\n * \\brief Tests a member function of a class returning by value by comparing the\n * result to a python function\n *\n * Tests the function `f` by comparing the result to that of the python function\n * `function_name` in the file `module_name`. An instance of the class is passed\n * in as the second argument and is the object on which the member function `f`\n * will be invoked. The member function is invoked as `klass.function`, so\n * passing in pointers is not supported. The function is tested by generated\n * random values in the half-open range [`lower_bound`, `upper_bound`). The\n * argument `used_for_size` is used for constructing the arguments of `f` by\n * calling `make_with_value<ArgumentType>(used_for_size, 0.0)`.\n *\n * \\note You must explicitly pass the number of bounds you will be passing\n * as the first template parameter, the rest will be inferred.\n *\n * \\note If you have a test fail you can replay the scenario by feeding in\n * the seed that was printed out in the failed test as the last argument.\n *\n * \\param f The member function to test\n * \\param klass the object on which to invoke `f`\n * \\param module_name The python file relative to the directory used in\n * `SetupLocalPythonEnvironment`\n * \\param function_name The name of the python function inside `module_name`\n * \\param lower_and_upper_bounds The lower and upper bounds for the randomly\n * generated numbers. Must be either an array of a single pair, or of as many\n * pairs as there are arguments to `f` that are not a `gsl::not_null`\n * \\param member_args a tuple of the member variables of the object `klass` that\n * the python function will need in order to perform the computation. These\n * should have the same types as the normal arguments passed to the member\n * function, e.g. `Tensor<X>`.\n * \\param used_for_size The type `X` for the arguments of `f` of type\n * `Tensor<X>`\n * \\param epsilon A double specifying the comparison tolerance\n * (default 1.0e-12)\n * \\param seed The seed for the random number generator. This should only be\n * specified when debugging a failure with a particular set of random numbers,\n * in general it should be left to the default value.\n */\ntemplate <\n    size_t NumberOfBounds, class F, class T, class... MemberArgs,\n    Requires<not std::is_same_v<typename tt::function_info<\n                                    cpp20::remove_cvref_t<F>>::return_type,\n                                void> and\n             not tt::is_a_v<tuples::TaggedTuple,\n                            typename tt::function_info<cpp20::remove_cvref_t<\n                                F>>::return_type>> = nullptr>\n// The Requires is used so that we can call the std::vector<std::string> with\n// braces and not have it be ambiguous.\nvoid check_with_random_values(\n    F&& f,\n    const typename tt::function_info<cpp20::remove_cvref_t<F>>::class_type&\n        klass,\n    const std::string& module_name, const std::string& function_name,\n    const std::array<std::pair<double, double>, NumberOfBounds>&\n        lower_and_upper_bounds,\n    const std::tuple<MemberArgs...>& member_args, const T& used_for_size,\n    const double epsilon = 1.0e-12,\n    const typename std::random_device::result_type seed =\n        std::random_device{}()) {\n  MAKE_GENERATOR(generator, seed);\n  using f_info = tt::function_info<cpp20::remove_cvref_t<F>>;\n  using number_of_not_null =\n      tmpl::count_if<typename f_info::argument_types,\n                     tmpl::bind<TestHelpers_detail::is_not_null, tmpl::_1>>;\n  using argument_types = tmpl::transform<\n      tmpl::pop_front<typename f_info::argument_types, number_of_not_null>,\n      std::decay<tmpl::_1>>;\n\n  static_assert(number_of_not_null::value == 0,\n                \"Cannot return arguments by gsl::not_null if the python \"\n                \"function name is passed as a string. If the function only \"\n                \"returns one gsl::not_null then you must pass in a one element \"\n                \"vector<string>.\");\n  static_assert(tmpl::size<argument_types>::value != 0,\n                \"The function 'f' must take at least one argument.\");\n  static_assert(NumberOfBounds == 1 or\n                    NumberOfBounds == tmpl::size<argument_types>::value,\n                \"The number of lower and upper bound pairs must be either 1 or \"\n                \"equal to the number of arguments taken by f that are not \"\n                \"gsl::not_null.\");\n  std::array<std::uniform_real_distribution<>,\n             tmpl::size<argument_types>::value>\n      distributions;\n  for (size_t i = 0; i < tmpl::size<argument_types>::value; ++i) {\n    gsl::at(distributions, i) = std::uniform_real_distribution<>{\n        gsl::at(lower_and_upper_bounds, NumberOfBounds == 1 ? 0 : i).first,\n        gsl::at(lower_and_upper_bounds, NumberOfBounds == 1 ? 0 : i).second};\n  }\n  TestHelpers_detail::check_with_random_values_impl(\n      std::forward<F>(f), klass, module_name, function_name, generator,\n      std::move(distributions), member_args, used_for_size, argument_types{},\n      std::make_index_sequence<tmpl::size<argument_types>::value>{},\n      std::make_index_sequence<sizeof...(MemberArgs)>{}, NoSuchType{}, epsilon);\n}\n\n/*!\n * \\brief Tests a member function of a class returning by either `gsl::not_null`\n * or `TaggedTuple` by comparing the result to a python function\n *\n * Tests the function `f` by comparing the result to that of the python\n * functions `function_names` in the file `module_name`. An instance of the\n * class is passed in as the second argument and is the object on which the\n * member function `f` will be invoked. The member function is invoked as\n * `klass.function`, so passing in pointers is not supported. The function is\n * tested by generated random values in the half-open range [`lower_bound`,\n * `upper_bound`). The argument `used_for_size` is used for constructing the\n * arguments of `f` by calling `make_with_value<ArgumentType>(used_for_size,\n * 0.0)`. For functions that return by `gsl::not_null`, the result will be\n * initialized with random values rather than to signaling `NaN`s. This means\n * functions do not need to support receiving a signaling `NaN` in their return\n * argument to be tested using this function.\n *\n * \\note You must explicitly pass the number of bounds you will be passing as\n * the first template parameter, the rest will be inferred.\n *\n * \\note If you have a test fail you can replay the scenario by feeding in the\n * seed that was printed out in the failed test as the last argument.\n *\n * \\param f The member function to test\n * \\param klass the object on which to invoke `f`\n * \\param module_name The python file relative to the directory used in\n * `SetupLocalPythonEnvironment`\n * \\param function_names The names of the python functions inside `module_name`\n * in the order that they return the `gsl::not_null` results\n * \\param lower_and_upper_bounds The lower and upper bounds for the randomly\n * generated numbers. Must be either an array of a single pair, or of as many\n * pairs as there are arguments to `f` that are not a `gsl::not_null`\n * \\param member_args a tuple of the member variables of the object `klass` that\n * the python function will need in order to perform the computation. These\n * should have the same types as the normal arguments passed to the member\n * function, e.g. `Tensor<X>`.\n * \\param used_for_size The type `X` for the arguments of `f` of type\n * `Tensor<X>`\n * \\param epsilon A double specifying the comparison tolerance\n * (default 1.0e-12)\n * \\param seed The seed for the random number generator. This should only be\n * specified when debugging a failure with a particular set of random numbers,\n * in general it should be left to the default value.\n */\ntemplate <size_t NumberOfBounds, class F, class T, class... MemberArgs,\n          Requires<std::is_same_v<typename tt::function_info<\n                                      cpp20::remove_cvref_t<F>>::return_type,\n                                  void> or\n                   tt::is_a_v<tuples::TaggedTuple,\n                              typename tt::function_info<cpp20::remove_cvref_t<\n                                  F>>::return_type>> = nullptr>\nvoid check_with_random_values(\n    F&& f,\n    const typename tt::function_info<cpp20::remove_cvref_t<F>>::class_type&\n        klass,\n    const std::string& module_name,\n    const std::vector<std::string>& function_names,\n    const std::array<std::pair<double, double>, NumberOfBounds>&\n        lower_and_upper_bounds,\n    const std::tuple<MemberArgs...>& member_args, const T& used_for_size,\n    const double epsilon = 1.0e-12,\n    const typename std::random_device::result_type seed =\n        std::random_device{}()) {\n  MAKE_GENERATOR(generator, seed);\n  using f_info = tt::function_info<cpp20::remove_cvref_t<F>>;\n  using number_of_not_null =\n      tmpl::count_if<typename f_info::argument_types,\n                     tmpl::bind<TestHelpers_detail::is_not_null, tmpl::_1>>;\n  using argument_types = tmpl::transform<\n      tmpl::pop_front<typename f_info::argument_types, number_of_not_null>,\n      std::decay<tmpl::_1>>;\n  using return_types =\n      tmpl::transform<tmpl::pop_back<typename f_info::argument_types,\n                                     tmpl::size<argument_types>>,\n                      TestHelpers_detail::RemoveNotNull<tmpl::_1>>;\n  constexpr bool return_type_is_tagged_tuple =\n      tt::is_a_v<tuples::TaggedTuple, typename f_info::return_type>;\n\n  static_assert(\n      number_of_not_null::value != 0 or return_type_is_tagged_tuple,\n      \"You must either return at least one argument by gsl::not_null when\"\n      \"passing the python function names as a vector<string>, or return by \"\n      \"value using a TaggedTuple. If your function returns by value with a \"\n      \"type that is not a TaggedTuple do not pass the function name as a \"\n      \"vector<string> but just a string.\");\n  static_assert(std::is_same_v<typename f_info::return_type, void> or\n                    return_type_is_tagged_tuple,\n                \"The function must either return by gsl::not_null and have a \"\n                \"void return type, or return by TaggedTuple\");\n  static_assert(tmpl::size<argument_types>::value != 0,\n                \"The function 'f' must take at least one argument.\");\n  static_assert(NumberOfBounds == 1 or\n                    NumberOfBounds == tmpl::size<argument_types>::value,\n                \"The number of lower and upper bound pairs must be either 1 or \"\n                \"equal to the number of arguments taken by f that are not \"\n                \"gsl::not_null.\");\n  if (function_names.size() != number_of_not_null::value and\n      not return_type_is_tagged_tuple) {\n    ERROR(\n        \"If testing a function that returns by gsl::not_null, the number of \"\n        \"python functions passed must be the same as the number of \"\n        \"gsl::not_null arguments in the C++ function. The order of the \"\n        \"python functions must also be the same as the order of the \"\n        \"gsl::not_null arguments.\");\n  }\n  std::array<std::uniform_real_distribution<>,\n             tmpl::size<argument_types>::value>\n      distributions;\n  for (size_t i = 0; i < tmpl::size<argument_types>::value; ++i) {\n    gsl::at(distributions, i) = std::uniform_real_distribution<>{\n        gsl::at(lower_and_upper_bounds, NumberOfBounds == 1 ? 0 : i).first,\n        gsl::at(lower_and_upper_bounds, NumberOfBounds == 1 ? 0 : i).second};\n  }\n  if constexpr (return_type_is_tagged_tuple) {\n    TestHelpers_detail::check_with_random_values_impl(\n        std::forward<F>(f), klass, module_name, function_names, generator,\n        std::move(distributions), member_args, used_for_size, return_types{},\n        argument_types{},\n        std::make_index_sequence<tmpl::size<return_types>::value>{},\n        std::make_index_sequence<tmpl::size<argument_types>::value>{},\n        std::make_index_sequence<sizeof...(MemberArgs)>{},\n        typename f_info::return_type::tags_list{}, epsilon);\n  } else {\n    TestHelpers_detail::check_with_random_values_impl(\n        std::forward<F>(f), klass, module_name, function_names, generator,\n        std::move(distributions), member_args, used_for_size, return_types{},\n        argument_types{},\n        std::make_index_sequence<tmpl::size<return_types>::value>{},\n        std::make_index_sequence<tmpl::size<argument_types>::value>{},\n        std::make_index_sequence<sizeof...(MemberArgs)>{}, NoSuchType{},\n        epsilon);\n  }\n}\n\n/// \\cond\n#define INVOKE_FUNCTION_TUPLE_PUSH_BACK(r, DATA, ELEM) \\\n  BOOST_PP_TUPLE_PUSH_BACK(DATA, ELEM)\n\n#define INVOKE_FUNCTION_WITH_MANY_TEMPLATE_PARAMS_IMPL(r, DATA)              \\\n  BOOST_PP_TUPLE_ELEM(                                                       \\\n      0, BOOST_PP_TUPLE_ELEM(                                                \\\n             0, DATA))<BOOST_PP_TUPLE_ELEM(2, BOOST_PP_TUPLE_ELEM(0, DATA)), \\\n                       BOOST_PP_TUPLE_ENUM(BOOST_PP_TUPLE_POP_FRONT(DATA))>  \\\n      BOOST_PP_TUPLE_ELEM(1, BOOST_PP_TUPLE_ELEM(0, DATA));\n\n// The case where there is more than one tuple of template parameters to tensor\n// product together. The first tuple of template parameters is transformed to a\n// tuple of (FUNCTION_NAME, TUPLE_ARGS, TEMPLATE_PARAM). Then the macro will\n// extract the values. The reason for needing to do this is that\n// BOOST_PP_LIST_FOR_EACH_PRODUCT does not allow for passing extra data along\n// to the macro.\n#define INVOKE_FUNCTION_WITH_MANY_TEMPLATE_PARAMS(TUPLE_OF_TEMPLATE_PARAMS) \\\n  BOOST_PP_LIST_FOR_EACH_PRODUCT(                                           \\\n      INVOKE_FUNCTION_WITH_MANY_TEMPLATE_PARAMS_IMPL,                       \\\n      BOOST_PP_TUPLE_SIZE(TUPLE_OF_TEMPLATE_PARAMS), TUPLE_OF_TEMPLATE_PARAMS)\n\n// The case where there is one tuple of template parameters to iterate over.\n#define INVOKE_FUNCTION_WITH_SINGLE_TEMPLATE_PARAM(_, DATA, ELEM) \\\n  BOOST_PP_TUPLE_ELEM(0, DATA)<ELEM> BOOST_PP_TUPLE_ELEM(1, DATA);\n\n#define INVOKE_FUNCTION_WITH_MANY_TEMPLATE_PARAMS_TUPLE_TO_LIST(r, _, ELEM) \\\n  BOOST_PP_TUPLE_TO_LIST(ELEM)\n/// \\endcond\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Macro used to invoke a test function of multiple template arguments.\n *\n * This macro allows to generate calls to multiple instances of\n * a test function template, all of which will receive the same parameters.\n * The first argument to this macro is the name of the function. The second\n * argument is a macro-tuple containing the parameters passed to each instance,\n * e.g. `(x, y)`. The remaining arguments are macro-tuples of the values for\n * each template parameter one wants to loop over, e.g.\n * `(1, 2, 3), (Frame::Inertial, Frame::Grid)`. For example, a function template\n *\n * \\code\n * template <class Arg1, size_t Arg2, class Arg3>\n * my_function(const double var_1, const int& var_2) { ... }\n * \\endcode\n *\n * can be invoked by writing\n *\n * \\code\n * INVOKE_TEST_FUNCTION(my_function, (d, i), (a, b, c), (1, 2, 3), (A, B, C))\n * \\endcode\n *\n * which will generate\n *\n * \\code\n * my_function<a, 1, A>(d, i);\n * my_function<a, 1, B>(d, i);\n * my_function<a, 1, C>(d, i);\n * my_function<a, 2, A>(d, i);\n * my_function<a, 2, B>(d, i);\n * my_function<a, 2, C>(d, i);\n * my_function<a, 3, A>(d, i);\n * my_function<a, 3, B>(d, i);\n * my_function<a, 3, C>(d, i);\n * my_function<b, 1, A>(d, i);\n * my_function<b, 1, B>(d, i);\n * my_function<b, 1, C>(d, i);\n * my_function<b, 2, A>(d, i);\n * my_function<b, 2, B>(d, i);\n * my_function<b, 2, C>(d, i);\n * my_function<b, 3, A>(d, i);\n * my_function<b, 3, B>(d, i);\n * my_function<b, 3, C>(d, i);\n * my_function<c, 1, A>(d, i);\n * my_function<c, 1, B>(d, i);\n * my_function<c, 1, C>(d, i);\n * my_function<c, 2, A>(d, i);\n * my_function<c, 2, B>(d, i);\n * my_function<c, 2, C>(d, i);\n * my_function<c, 3, A>(d, i);\n * my_function<c, 3, B>(d, i);\n * my_function<c, 3, C>(d, i);\n * \\endcode\n *\n * \\note The order of the macro-tuples of values must match the order of the\n * template parameters of the function.\n *\n * \\note The function to be called must at least have one template argument,\n * so passing an empty set of template parameters will not work.\n */\n#define INVOKE_TEST_FUNCTION(FUNCTION_NAME, TUPLE_ARGS, ...)                   \\\n  BOOST_PP_ASSERT_MSG(BOOST_PP_NOT(BOOST_VMD_IS_EMPTY(__VA_ARGS__)),           \\\n                      \"You cannot pass an empty set of template parameters \"   \\\n                      \"to INVOKE_TEST_FUNCTION\")                               \\\n  BOOST_PP_TUPLE_ENUM(                                                         \\\n      0,                                                                       \\\n      BOOST_PP_IF(                                                             \\\n          BOOST_PP_EQUAL(                                                      \\\n              BOOST_PP_TUPLE_SIZE(BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__)),    \\\n              1),                                                              \\\n          (BOOST_PP_LIST_FOR_EACH(                                             \\\n              INVOKE_FUNCTION_WITH_SINGLE_TEMPLATE_PARAM,                      \\\n              (FUNCTION_NAME, TUPLE_ARGS),                                     \\\n              BOOST_PP_TUPLE_TO_LIST(                                          \\\n                  BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__)))),                   \\\n          (INVOKE_FUNCTION_WITH_MANY_TEMPLATE_PARAMS(                          \\\n              BOOST_PP_TUPLE_PUSH_FRONT(                                       \\\n                  BOOST_PP_LIST_TO_TUPLE(BOOST_PP_LIST_TRANSFORM(              \\\n                      INVOKE_FUNCTION_WITH_MANY_TEMPLATE_PARAMS_TUPLE_TO_LIST, \\\n                      _,                                                       \\\n                      BOOST_PP_LIST_REST(                                      \\\n                          BOOST_PP_VARIADIC_TO_LIST(__VA_ARGS__)))),           \\\n                  BOOST_PP_LIST_TRANSFORM(                                     \\\n                      INVOKE_FUNCTION_TUPLE_PUSH_BACK,                         \\\n                      (FUNCTION_NAME, TUPLE_ARGS),                             \\\n                      BOOST_PP_TUPLE_TO_LIST(                                  \\\n                          BOOST_PP_VARIADIC_ELEM(0, __VA_ARGS__))))))))\n\n/// \\cond\n#define GENERATE_UNINITIALIZED_DOUBLE \\\n  const double d(std::numeric_limits<double>::signaling_NaN())\n\n#define GENERATE_UNINITIALIZED_DATAVECTOR const DataVector dv(5)\n\n#define GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR \\\n  GENERATE_UNINITIALIZED_DOUBLE;                     \\\n  GENERATE_UNINITIALIZED_DATAVECTOR\n\n#define CHECK_FOR_DOUBLES(FUNCTION_NAME, ...) \\\n  INVOKE_TEST_FUNCTION(FUNCTION_NAME, (d), __VA_ARGS__)\n\n#define CHECK_FOR_DATAVECTORS(FUNCTION_NAME, ...) \\\n  INVOKE_TEST_FUNCTION(FUNCTION_NAME, (dv), __VA_ARGS__)\n/// \\endcond\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Macro used to test functions whose parameter can be a `double` or a\n * `DataVector`.\n *\n * In testing multiple instances of a function template using random values, it\n * often proves useful to write a wrapper around\n * `pypp::check_with_random_values`. This way, one can easily loop over several\n * values of one or multiple template parameters (e.g. when testing a\n * function templated in the number of spacetime dimensions.) The template\n * parameters of the wrapper will then correspond to the template parameters of\n * the function, which will be used by `pypp::check_with_random_values`\n * to invoke and test each instance. Each of these wrappers will generally\n * need only one parameter, namely a variable `used_for_size` passed to\n * `pypp::check_with_random_values` that can be a `double`, a `DataVector`, or\n * both (provided that the function being tested is templated in the type of\n * `used_for_size`.) Since this is applied in multiple test files, all of these\n * files will share the same way to generate the required calls to the wrapper.\n *\n * This macro, along with\n *\n * \\code\n * CHECK_FOR_DOUBLES(FUNCTION_NAME, ...)\n * \\endcode\n * \\code\n * CHECK_FOR_DATAVECTORS(FUNCTION_NAME, ...)\n * \\endcode\n *\n * allow to generate calls to multiple instances of a test function template in\n * the same way as done by `INVOKE_TEST_FUNCTION(FUNCTION_NAME, ARGS_TUPLE,\n * ...)`\n * (to which these macros call), except that the tuple of arguments is not\n * passed, as these macros will assume that a `double` `d`\n * and/or a `DataVector` `dv` will be previously defined. Although any `d`s and\n * `dv`s will work, one can (and it is recommended to) generate signaling `NaN`\n * values for `d` and `dv`. This can be done by invoking one of the three\n * provided macros: `GENERATE_UNINIATILIZED_DOUBLE`,\n * `GENERATE_UNINITIALIZED_DATAVECTOR`, or\n * `GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR`. For example,\n *\n * \\code\n * GENERATE_UNINITIALIZED_DATAVECTOR;\n * CHECK_FOR_DATAVECTORS(test_fluxes, (1, 2, 3))\n * \\endcode\n *\n * will generate a test case for 1, 2 and 3 dimensions:\n *\n * \\code\n * const DataVector dv(5);\n * test_fluxes<1>(dv);\n * test_fluxes<2>(dv);\n * test_fluxes<3>(dv);\n * \\endcode\n *\n * Analogously, the wrapper\n *\n * \\code\n * template <size_t Dim, IndexType TypeOfIndex, typename DataType>\n * test_ricci(const DataType& used_for_size) { ... }\n * \\endcode\n *\n * can be invoked by writing\n *\n * \\code\n * GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n *\n * CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_ricci, (1, 2, 3),\n *                                   (IndexType::Spatial, IndexType::Spacetime))\n * \\endcode\n *\n * which will generate\n *\n * \\code\n * const double d(std::numeric_limits<double>::signaling_NaN());\n * const DataVector dv(5);\n *\n * test_ricci<1, IndexType::Spatial>(d);\n * test_ricci<1, IndexType::Spacetime>(d);\n * test_ricci<2, IndexType::Spatial>(d);\n * test_ricci<2, IndexType::Spacetime>(d);\n * test_ricci<3, IndexType::Spatial>(d);\n * test_ricci<3, IndexType::Spacetime>(d);\n * test_ricci<1, IndexType::Spatial>(dv);\n * test_ricci<1, IndexType::Spacetime>(dv);\n * test_ricci<2, IndexType::Spatial>(dv);\n * test_ricci<2, IndexType::Spacetime>(dv);\n * test_ricci<3, IndexType::Spatial>(dv);\n * test_ricci<3, IndexType::Spacetime>(dv);\n * \\endcode\n *\n * Note that it is not necessary to pass values for `DataType`, as they are\n * deduced from `used_for_size`.\n *\n * \\note The order of the macro-tuples of values must match the order of the\n * template parameters of the function.\n *\n * \\note The function to be called must at least have one template argument,\n * so passing an empty set of template parameters will not work.\n */\n#define CHECK_FOR_DOUBLES_AND_DATAVECTORS(FUNCTION_NAME, ...) \\\n  CHECK_FOR_DOUBLES(FUNCTION_NAME, __VA_ARGS__)               \\\n  CHECK_FOR_DATAVECTORS(FUNCTION_NAME, __VA_ARGS__)\n}  // namespace pypp\n"
  },
  {
    "path": "tests/Unit/Framework/MockDistributedObject.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <boost/preprocessor/control/if.hpp>\n#include <boost/preprocessor/logical/compl.hpp>\n#include <boost/preprocessor/logical/not.hpp>\n#include <boost/preprocessor/punctuation/comma_if.hpp>\n#include <boost/preprocessor/repetition/repeat.hpp>\n#include <converse.h>\n#include <cstddef>\n#include <deque>\n#include <exception>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/AlgorithmMetafunctions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Tags/ArrayIndex.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace ActionTesting {\n/// \\cond\nstruct MockNodeGroupChare;\n/// \\endcond\n\nnamespace detail {\n\n#define ACTION_TESTING_CHECK_MOCK_ACTION_LIST(NAME)                        \\\n  template <typename Component, typename = std::void_t<>>                  \\\n  struct get_##NAME##_mocking_list {                                       \\\n    using replace_these_##NAME = tmpl::list<>;                             \\\n    using with_these_##NAME = tmpl::list<>;                                \\\n  };                                                                       \\\n  template <typename Component>                                            \\\n  struct get_##NAME##_mocking_list<                                        \\\n      Component, std::void_t<typename Component::replace_these_##NAME,     \\\n                             typename Component::with_these_##NAME>> {     \\\n    using replace_these_##NAME = typename Component::replace_these_##NAME; \\\n    using with_these_##NAME = typename Component::with_these_##NAME;       \\\n  };                                                                       \\\n  template <typename Component>                                            \\\n  using replace_these_##NAME##_t =                                         \\\n      typename get_##NAME##_mocking_list<Component>::replace_these_##NAME; \\\n  template <typename Component>                                            \\\n  using with_these_##NAME##_t =                                            \\\n      typename get_##NAME##_mocking_list<Component>::with_these_##NAME\n\nACTION_TESTING_CHECK_MOCK_ACTION_LIST(simple_actions);\nACTION_TESTING_CHECK_MOCK_ACTION_LIST(threaded_actions);\n#undef ACTION_TESTING_CHECK_MOCK_ACTION_LIST\n\ntemplate <typename Component, typename = std::void_t<>>\nstruct get_simple_tags_from_options_from_component {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Component>\nstruct get_simple_tags_from_options_from_component<\n    Component, std::void_t<typename Component::simple_tags_from_options>> {\n  using type = typename Component::simple_tags_from_options;\n};\n\n// Returns the type of `Tag` (including const and reference-ness as would be\n// returned by `db::get<Tag>`) if the tag is in the `DataBox` of type\n// `DataBoxType`, otherwise returns `NoSuchType`.\ntemplate <typename Tag, typename DataBoxType,\n          bool = db::tag_is_retrievable_v<Tag, DataBoxType>>\nstruct item_type_if_contained;\n\ntemplate <typename Tag, typename DataBoxType>\nstruct item_type_if_contained<Tag, DataBoxType, true> {\n  using type = decltype(db::get<Tag>(DataBoxType{}));\n};\n\ntemplate <typename Tag, typename DataBoxType>\nstruct item_type_if_contained<Tag, DataBoxType, false> {\n  using type = NoSuchType;\n};\n\ntemplate <typename Tag, typename DataBoxType>\nusing item_type_if_contained_t =\n    typename item_type_if_contained<Tag, DataBoxType>::type;\n}  // namespace detail\n\n/// Wraps a size_t representing the node number.  This is so the user\n/// can write things like `emplace_array_component(NodeId{3},...)`  instead of\n/// `emplace_array_component(3,...)`.\nstruct NodeId {\n  size_t value;\n};\n\ninline bool operator==(const NodeId& lhs, const NodeId& rhs) {\n  return lhs.value == rhs.value;\n}\n\ninline bool operator!=(const NodeId& lhs, const NodeId& rhs) {\n  return not(lhs==rhs);\n}\n\n/// Wraps a size_t representing the local core number. This is so the\n/// user can write things like\n/// `emplace_array_component(NodeId{3},LocalCoreId{2},...)`  instead of\n/// `emplace_array_component(3,2,...)`.\n///\n/// The local core number is unique for each core on the same node,\n/// but cores on different nodes can have the same local core number.\n/// For example, if there are 3 nodes with 2\n/// cores each, then the cores on the first node have local core numbers\n/// 0 and 1, the cores on the second node also have local core numbers\n/// 0 and 1, and so on.\nstruct LocalCoreId {\n  size_t value;\n};\n\ninline bool operator==(const LocalCoreId& lhs, const LocalCoreId& rhs) {\n  return lhs.value == rhs.value;\n}\n\ninline bool operator!=(const LocalCoreId& lhs, const LocalCoreId& rhs) {\n  return not(lhs==rhs);\n}\n\n/// Wraps a size_t representing the global core number.\n///\n/// The global core number is unique for each core, even if the cores\n/// are on different nodes.  For example, if there are 3 nodes with 2\n/// cores each, the global core number goes from 0 through 5 to label\n/// each of the 6 cores, and no two cores have the same global core\n/// number.\nstruct GlobalCoreId {\n  size_t value;\n};\n\ninline bool operator==(const GlobalCoreId& lhs, const GlobalCoreId& rhs) {\n  return lhs.value == rhs.value;\n}\n\ninline bool operator!=(const GlobalCoreId& lhs, const GlobalCoreId& rhs) {\n  return not(lhs==rhs);\n}\n}  // namespace ActionTesting\n\nnamespace std {\ntemplate <>\nstruct hash<ActionTesting::NodeId> {\n  size_t operator()(const ActionTesting::NodeId& t) const { return t.value; }\n};\n\ntemplate <>\nstruct hash<ActionTesting::LocalCoreId> {\n  size_t operator()(const ActionTesting::LocalCoreId& t) const {\n    return t.value;\n  }\n};\n\ntemplate <>\nstruct hash<ActionTesting::GlobalCoreId> {\n  size_t operator()(const ActionTesting::GlobalCoreId& t) const {\n    return t.value;\n  }\n};\n}  // namespace std\n\nnamespace ActionTesting {\n/// MockDistributedObject mocks the DistributedObject class. It should not be\n/// considered as part of the user interface.\n///\n/// `MockDistributedObject` represents an object on a supercomputer\n/// that can have methods invoked on it (possibly) remotely; this is\n/// standard nomenclature in the HPC community, based on the idea that\n/// such objects get distributed among the cores/nodes on an HPC (even\n/// though each object typically lives on only one core).  For\n/// example, an element of an array chare in charm++ is a mock\n/// distributed object, whereas the entire array chare is a collection\n/// of mock distributed objects, each with its own array\n/// index.\n/// `MockDistributedObject` is a modified implementation of\n/// `DistributedObject` and so some of the code is shared between the\n/// two. The main difference is that `MockDistributedObject` has\n/// support for introspection. For example, it is possible to check\n/// how many simple actions are queued, to look at the inboxes,\n/// etc. Another key difference is that `MockDistributedObject` runs\n/// only one action in the action list at a time. This is done in\n/// order to provide opportunity for introspection and checking\n/// statements before and after actions are invoked.\ntemplate <typename Component>\nclass MockDistributedObject {\n private:\n  class InvokeActionBase {\n   public:\n    InvokeActionBase() = default;\n    InvokeActionBase(const InvokeActionBase&) = default;\n    InvokeActionBase& operator=(const InvokeActionBase&) = default;\n    InvokeActionBase(InvokeActionBase&&) = default;\n    InvokeActionBase& operator=(InvokeActionBase&&) = default;\n    virtual ~InvokeActionBase() = default;\n    virtual void invoke_action() = 0;\n  };\n\n  // Holds the arguments to be passed to the simple action once it is invoked.\n  // We delay simple action calls that are made from within an action for\n  // several reasons:\n  // - This is consistent with what actually happens in the parallel code\n  // - This prevents possible stack overflows\n  // - Allows better introspection and control over the Actions' behavior\n  template <typename Action, typename... Args>\n  class InvokeSimpleAction : public InvokeActionBase {\n   public:\n    InvokeSimpleAction(MockDistributedObject* mock_distributed_object,\n                       std::tuple<Args...> args)\n        : mock_distributed_object_(mock_distributed_object),\n          args_(std::move(args)) {}\n\n    explicit InvokeSimpleAction(MockDistributedObject* mock_distributed_object)\n        : mock_distributed_object_(mock_distributed_object) {}\n\n    void invoke_action() override {\n      if (not valid_) {\n        ERROR(\n            \"Cannot invoke the exact same simple action twice. This is an \"\n            \"internal bug in the action testing framework. Please file an \"\n            \"issue.\");\n      }\n      valid_ = false;\n      invoke_action_impl(std::move(args_));\n    }\n\n   private:\n    template <typename Arg0, typename... Rest>\n    void invoke_action_impl(std::tuple<Arg0, Rest...> args) {\n      mock_distributed_object_->simple_action<Action>(std::move(args), true);\n    }\n\n    template <typename... LocalArgs,\n              Requires<sizeof...(LocalArgs) == 0> = nullptr>\n    void invoke_action_impl(std::tuple<LocalArgs...> /*args*/) {\n      mock_distributed_object_->simple_action<Action>(true);\n    }\n\n    MockDistributedObject* mock_distributed_object_;\n    std::tuple<Args...> args_{};\n    bool valid_{true};\n  };\n\n  // Holds the arguments passed to threaded actions. `InvokeThreadedAction` is\n  // analogous to `InvokeSimpleAction`.\n  template <typename Action, typename... Args>\n  class InvokeThreadedAction : public InvokeActionBase {\n   public:\n    InvokeThreadedAction(MockDistributedObject* mock_distributed_object,\n                         std::tuple<Args...> args)\n        : mock_distributed_object_(mock_distributed_object),\n          args_(std::move(args)) {}\n\n    explicit InvokeThreadedAction(\n        MockDistributedObject* mock_distributed_object)\n        : mock_distributed_object_(mock_distributed_object) {}\n\n    void invoke_action() override {\n      if (not valid_) {\n        ERROR(\n            \"Cannot invoke the exact same threaded action twice. This is an \"\n            \"internal bug in the action testing framework. Please file an \"\n            \"issue.\");\n      }\n      valid_ = false;\n      invoke_action_impl(std::move(args_));\n    }\n\n   private:\n    template <typename Arg0, typename... Rest>\n    void invoke_action_impl(std::tuple<Arg0, Rest...> args) {\n      mock_distributed_object_->threaded_action<Action>(std::move(args), true);\n    }\n\n    template <typename... LocalArgs,\n              Requires<sizeof...(LocalArgs) == 0> = nullptr>\n    void invoke_action_impl(std::tuple<LocalArgs...> /*args*/) {\n      mock_distributed_object_->threaded_action<Action>(true);\n    }\n\n    MockDistributedObject* mock_distributed_object_;\n    std::tuple<Args...> args_{};\n    bool valid_{true};\n  };\n\n public:\n  using phase_dependent_action_lists =\n      typename Component::phase_dependent_action_list;\n  static_assert(tmpl::size<phase_dependent_action_lists>::value > 0,\n                \"Must have at least one phase dependent action list \"\n                \"(PhaseActions) in a parallel component.\");\n\n  using all_actions_list = tmpl::flatten<tmpl::transform<\n      phase_dependent_action_lists,\n      Parallel::get_action_list_from_phase_dep_action_list<tmpl::_1>>>;\n\n  using metavariables = typename Component::metavariables;\n\n  using inbox_tags_list =\n      Parallel::get_inbox_tags<tmpl::push_front<all_actions_list, Component>>;\n\n  using array_index = typename Parallel::get_array_index<\n      typename Component::chare_type>::template f<Component>;\n\n  using parallel_component = Component;\n\n  using all_cache_tags = Parallel::get_const_global_cache_tags<metavariables>;\n  using simple_tags_from_options =\n      typename detail::get_simple_tags_from_options_from_component<\n          Component>::type;\n  using initial_tags = tmpl::flatten<tmpl::list<\n      Parallel::Tags::MetavariablesImpl<metavariables>,\n      Parallel::Tags::ArrayIndex<array_index>,\n      Parallel::Tags::GlobalCache<metavariables>, simple_tags_from_options,\n      db::wrap_tags_in<Parallel::Tags::FromGlobalCache, all_cache_tags,\n                       metavariables>,\n      Parallel::Algorithm_detail::action_list_simple_tags<Component>,\n      Parallel::Algorithm_detail::action_list_compute_tags<Component>>>;\n  using databox_type = db::compute_databox_type<initial_tags>;\n\n  MockDistributedObject() = default;\n\n  template <typename... Options>\n  MockDistributedObject(\n      const NodeId node_id, const LocalCoreId local_core_id,\n      std::unordered_map<NodeId, std::unordered_map<LocalCoreId, GlobalCoreId>>\n          mock_global_cores,\n      std::unordered_map<GlobalCoreId, std::pair<NodeId, LocalCoreId>>\n          mock_nodes_and_local_cores,\n      const array_index& index,\n      Parallel::GlobalCache<typename Component::metavariables>* cache,\n      tuples::tagged_tuple_from_typelist<inbox_tags_list>* inboxes,\n      Options&&... opts)\n      : mock_node_(node_id.value),\n        mock_local_core_(local_core_id.value),\n        mock_global_cores_(std::move(mock_global_cores)),\n        mock_nodes_and_local_cores_(std::move(mock_nodes_and_local_cores)),\n        array_index_(index),\n        global_cache_(cache),\n        inboxes_(inboxes) {\n    ::Initialization::mutate_assign<tmpl::push_front<\n        simple_tags_from_options, Parallel::Tags::ArrayIndex<array_index>,\n        Parallel::Tags::GlobalCache<metavariables>>>(\n        make_not_null(&box_), array_index_, global_cache_,\n        std::forward<Options>(opts)...);\n  }\n\n  void set_phase(Parallel::Phase phase) {\n    phase_ = phase;\n    algorithm_step_ = 0;\n    terminate_ = number_of_actions_in_phase(phase) == 0;\n    halt_algorithm_until_next_phase_ = false;\n  }\n  Parallel::Phase get_phase() const { return phase_; }\n\n  void set_terminate(bool t) { terminate_ = t; }\n  bool get_terminate() const { return terminate_; }\n\n  // There are no phase bookmarks in mock distributed objects, so we just\n  // return an empty map\n  std::unordered_map<Parallel::Phase, size_t> phase_bookmarks() { return {}; }\n\n  // Actions may call this, but since tests step through actions manually it has\n  // no effect.\n  void perform_algorithm() {}\n\n  size_t number_of_actions_in_phase(const Parallel::Phase phase) const {\n    size_t number_of_actions = 0;\n    tmpl::for_each<phase_dependent_action_lists>(\n        [&number_of_actions, phase](auto pdal_v) {\n          const auto pdal = tmpl::type_from<decltype(pdal_v)>{};\n          if (pdal.phase == phase) {\n            number_of_actions = pdal.number_of_actions;\n          }\n        });\n    return number_of_actions;\n  }\n\n  /// @{\n  /// Returns the DataBox.\n  databox_type& get_databox() { return box_; }\n\n  const databox_type& get_databox() const { return box_; }\n  /// @}\n\n  template <typename Tag>\n  const auto& get_databox_tag() const {\n    return db::get<Tag>(box_);\n  }\n\n  /// Returns true if Tag was used to insert an item into the DataBox\n  template <typename Tag>\n  bool box_contains() const {\n    return tmpl::list_contains_v<typename databox_type::tags_list, Tag>;\n  }\n\n  /// \\brief Returns true if Tag can be retrieved (via db::get) from the DataBox\n  ///\n  /// \\note compute and reference tags can be retrieved via the simple tag they\n  /// are derived from.\n  template <typename Tag>\n  bool tag_is_retrievable() const {\n    return db::tag_is_retrievable_v<Tag, databox_type>;\n  }\n\n  /// Force the next action invoked to be the `next_action_id`th action in the\n  /// current phase.\n  void force_next_action_to_be(const size_t next_action_id) {\n    algorithm_step_ = next_action_id;\n  }\n\n  /// Returns which action (by integer) will be invoked next in the current\n  /// phase.\n  size_t get_next_action_index() const { return algorithm_step_; }\n\n  /// Invoke the next action in the action list for the current phase,\n  /// failing if it was not ready.\n  void next_action();\n\n  /// Invoke the next action in the action list for the current phase,\n  /// returning whether the action was ready.\n  template <typename CatchMatcher = int>\n  bool next_action_if_ready(const CatchMatcher* matcher = nullptr);\n\n  /// Defines the methods used for invoking threaded and simple actions. Since\n  /// the two cases are so similar we use a macro to reduce the amount of\n  /// redundant code.\n#define SIMPLE_AND_THREADED_ACTIONS(USE_SIMPLE_ACTION, NAME)                  \\\n  template <typename Action, typename... Args,                                \\\n            Requires<not tmpl::list_contains_v<                               \\\n                detail::replace_these_##NAME##s_t<Component>, Action>> =      \\\n                nullptr>                                                      \\\n  void NAME(std::tuple<Args...> args,                                         \\\n            const bool direct_from_action_runner = false) {                   \\\n    if (direct_from_action_runner) {                                          \\\n      performing_action_ = true;                                              \\\n      forward_tuple_to_##NAME<Action>(                                        \\\n          std::move(args), std::make_index_sequence<sizeof...(Args)>{});      \\\n      performing_action_ = false;                                             \\\n    } else {                                                                  \\\n      NAME##_queue_.push_back(                                                \\\n          std::make_unique<BOOST_PP_IF(USE_SIMPLE_ACTION, InvokeSimpleAction, \\\n                                       InvokeThreadedAction) < Action,        \\\n                           Args...> > (this, std::move(args)));               \\\n    }                                                                         \\\n  }                                                                           \\\n  template <typename Action, typename... Args,                                \\\n            Requires<tmpl::list_contains_v<                                   \\\n                detail::replace_these_##NAME##s_t<Component>, Action>> =      \\\n                nullptr>                                                      \\\n  void NAME(std::tuple<Args...> args,                                         \\\n            const bool direct_from_action_runner = false) {                   \\\n    using index_of_action =                                                   \\\n        tmpl::index_of<detail::replace_these_##NAME##s_t<Component>, Action>; \\\n    using new_action = tmpl::at_c<detail::with_these_##NAME##s_t<Component>,  \\\n                                  index_of_action::value>;                    \\\n    if (direct_from_action_runner) {                                          \\\n      performing_action_ = true;                                              \\\n      forward_tuple_to_##NAME<new_action>(                                    \\\n          std::move(args), std::make_index_sequence<sizeof...(Args)>{});      \\\n      performing_action_ = false;                                             \\\n    } else {                                                                  \\\n      NAME##_queue_.push_back(                                                \\\n          std::make_unique<BOOST_PP_IF(USE_SIMPLE_ACTION, InvokeSimpleAction, \\\n                                       InvokeThreadedAction) < new_action,    \\\n                           Args...> > (this, std::move(args)));               \\\n    }                                                                         \\\n  }                                                                           \\\n  template <typename Action,                                                  \\\n            Requires<not tmpl::list_contains_v<                               \\\n                detail::replace_these_##NAME##s_t<Component>, Action>> =      \\\n                nullptr>                                                      \\\n  void NAME(const bool direct_from_action_runner = false) {                   \\\n    if (direct_from_action_runner) {                                          \\\n      performing_action_ = true;                                              \\\n      Action::template apply<Component>(                                      \\\n          box_, *global_cache_,                                               \\\n          std::as_const(array_index_)                                         \\\n              BOOST_PP_COMMA_IF(BOOST_PP_NOT(USE_SIMPLE_ACTION)) BOOST_PP_IF( \\\n                  USE_SIMPLE_ACTION, , make_not_null(&node_lock_)));          \\\n      performing_action_ = false;                                             \\\n    } else {                                                                  \\\n      NAME##_queue_.push_back(                                                \\\n          std::make_unique<BOOST_PP_IF(USE_SIMPLE_ACTION, InvokeSimpleAction, \\\n                                       InvokeThreadedAction) < Action> >      \\\n          (this));                                                            \\\n    }                                                                         \\\n  }                                                                           \\\n  template <typename Action,                                                  \\\n            Requires<tmpl::list_contains_v<                                   \\\n                detail::replace_these_##NAME##s_t<Component>, Action>> =      \\\n                nullptr>                                                      \\\n  void NAME(const bool direct_from_action_runner = false) {                   \\\n    using index_of_action =                                                   \\\n        tmpl::index_of<detail::replace_these_##NAME##s_t<Component>, Action>; \\\n    using new_action = tmpl::at_c<detail::with_these_##NAME##s_t<Component>,  \\\n                                  index_of_action::value>;                    \\\n    if (direct_from_action_runner) {                                          \\\n      performing_action_ = true;                                              \\\n      new_action::template apply<Component>(                                  \\\n          box_, *global_cache_,                                               \\\n          std::as_const(array_index_)                                         \\\n              BOOST_PP_COMMA_IF(BOOST_PP_NOT(USE_SIMPLE_ACTION)) BOOST_PP_IF( \\\n                  USE_SIMPLE_ACTION, , make_not_null(&node_lock_)));          \\\n      performing_action_ = false;                                             \\\n    } else {                                                                  \\\n      simple_action_queue_.push_back(                                         \\\n          std::make_unique<BOOST_PP_IF(USE_SIMPLE_ACTION, InvokeSimpleAction, \\\n                                       InvokeThreadedAction) < new_action> >  \\\n          (this));                                                            \\\n    }                                                                         \\\n  }\n\n  SIMPLE_AND_THREADED_ACTIONS(1, simple_action)\n  SIMPLE_AND_THREADED_ACTIONS(0, threaded_action)\n#undef SIMPLE_AND_THREADED_ACTIONS\n\n  // local synchronous actions are performed as direct function calls\n  // regardless, so their 'mocking' implementation is no different from their\n  // original implementation\n  template <typename Action, typename... Args>\n  typename Action::return_type local_synchronous_action(Args&&... args) {\n    static_assert(std::is_same_v<typename Component::chare_type,\n                                 ActionTesting::MockNodeGroupChare>,\n                  \"Cannot call a local synchronous action on a chare that is \"\n                  \"not a NodeGroup\");\n    return Action::template apply<Component>(box_, make_not_null(&node_lock_),\n                                             std::forward<Args>(args)...);\n  }\n\n  bool is_simple_action_queue_empty() const {\n    return simple_action_queue_.empty();\n  }\n\n  size_t simple_action_queue_size() const {\n    return simple_action_queue_.size();\n  }\n\n  void invoke_queued_simple_action() {\n    if (simple_action_queue_.empty()) {\n      ERROR(\n          \"There are no queued simple actions to invoke. Are you sure a \"\n          \"previous action invoked a simple action on this component?\");\n    }\n    simple_action_queue_.front()->invoke_action();\n    simple_action_queue_.pop_front();\n  }\n\n  bool is_threaded_action_queue_empty() const {\n    return threaded_action_queue_.empty();\n  }\n\n  size_t threaded_action_queue_size() const {\n    return threaded_action_queue_.size();\n  }\n\n  void invoke_queued_threaded_action() {\n    if (threaded_action_queue_.empty()) {\n      ERROR(\n          \"There are no queued threaded actions to invoke. Are you sure a \"\n          \"previous action invoked a threaded action on this component?\");\n    }\n    threaded_action_queue_.front()->invoke_action();\n    threaded_action_queue_.pop_front();\n  }\n\n  template <typename InboxTag, typename Data>\n  void receive_data(const typename InboxTag::temporal_id& id, Data&& data,\n                    const bool enable_if_disabled = false) {\n    // The variable `enable_if_disabled` might be useful in the future but is\n    // not needed now. However, it is required by the interface to be compliant\n    // with the Algorithm invocations.\n    (void)enable_if_disabled;\n    InboxTag::insert_into_inbox(\n        make_not_null(&tuples::get<InboxTag>(*inboxes_)), id,\n        std::forward<Data>(data));\n  }\n\n  template <typename InboxTag, typename MessageType>\n  void receive_data(MessageType* message) {\n    InboxTag::insert_into_inbox(\n        make_not_null(&tuples::get<InboxTag>(*inboxes_)), message);\n  }\n\n  Parallel::GlobalCache<typename Component::metavariables>& cache() {\n    return *global_cache_;\n  }\n\n  /// @{\n  /// Wrappers for charm++ informational functions.\n\n  /// Number of processing elements\n  int number_of_procs() const;\n\n  /// Global %Index of my processing element.\n  int my_proc() const;\n\n  /// Number of nodes.\n  int number_of_nodes() const;\n\n  /// %Index of my node.\n  int my_node() const;\n\n  /// Number of processing elements on the given node.\n  int procs_on_node(int node_index) const;\n\n  /// The local index of my processing element on my node.\n  /// This is in the interval 0, ..., procs_on_node(my_node()) - 1.\n  int my_local_rank() const;\n\n  /// %Index of first processing element on the given node.\n  int first_proc_on_node(int node_index) const;\n\n  /// %Index of the node for the given processing element.\n  int node_of(int proc_index) const;\n\n  /// The local index for the given processing element on its node.\n  int local_rank_of(int proc_index) const;\n  /// @}\n\n public:\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p);\n\n private:\n  template <typename Action, typename... Args, size_t... Is>\n  void forward_tuple_to_simple_action(std::tuple<Args...>&& args,\n                                      std::index_sequence<Is...> /*meta*/) {\n    Action::template apply<Component>(\n        box_, *global_cache_, std::as_const(array_index_),\n        std::forward<Args>(std::get<Is>(args))...);\n  }\n\n  template <typename Action, typename... Args, size_t... Is>\n  void forward_tuple_to_threaded_action(std::tuple<Args...>&& args,\n                                        std::index_sequence<Is...> /*meta*/) {\n    Action::template apply<Component>(\n        box_, *global_cache_, std::as_const(array_index_),\n        make_not_null(&node_lock_), std::forward<Args>(std::get<Is>(args))...);\n  }\n\n  template <typename ThisAction, typename ActionList, typename DbTags>\n  bool invoke_iterable_action(db::DataBox<DbTags>& my_box) {\n    Parallel::AlgorithmExecution requested_execution{};\n    std::optional<std::size_t> next_action_step{};\n    std::tie(requested_execution, next_action_step) = ThisAction::apply(\n        my_box, *inboxes_, *global_cache_, std::as_const(array_index_),\n        ActionList{}, std::add_pointer_t<Component>{});\n\n    if (next_action_step.has_value()) {\n      ASSERT(\n          Parallel::AlgorithmExecution::Retry != requested_execution,\n          \"Switching actions on Retry doesn't make sense. Specify std::nullopt \"\n          \"as the second argument of the iterable action return type\");\n      algorithm_step_ = next_action_step.value();\n    }\n\n    switch (requested_execution) {\n      case Parallel::AlgorithmExecution::Continue:\n        return true;\n      case Parallel::AlgorithmExecution::Retry:\n        return false;\n      case Parallel::AlgorithmExecution::Pause:\n        terminate_ = true;\n        return true;\n      case Parallel::AlgorithmExecution::Halt:\n        halt_algorithm_until_next_phase_ = true;\n        terminate_ = true;\n        return true;\n      default:  // LCOV_EXCL_LINE\n        // LCOV_EXCL_START\n        ERROR(\"No case for a Parallel::AlgorithmExecution with integral value \"\n              << static_cast<\n                     std::underlying_type_t<Parallel::AlgorithmExecution>>(\n                     requested_execution)\n              << \"\\n\");\n        // LCOV_EXCL_STOP\n    }\n  }\n\n  template <typename PhaseDepActions, typename CatchMatcher, size_t... Is>\n  bool next_action_impl(const CatchMatcher* matcher,\n                        std::index_sequence<Is...> /*meta*/);\n\n  bool terminate_{false};\n  bool halt_algorithm_until_next_phase_{false};\n  databox_type box_;\n  // The next action we should execute.\n  size_t algorithm_step_ = 0;\n  bool performing_action_ = false;\n  Parallel::Phase phase_{Parallel::Phase::Initialization};\n\n  size_t mock_node_{0};\n  size_t mock_local_core_{0};\n  // mock_global_cores[node][local_core] is the global_core.\n  std::unordered_map<NodeId, std::unordered_map<LocalCoreId, GlobalCoreId>>\n      mock_global_cores_{};\n  // mock_nodes_and_local_cores_[global_core] is the pair node,local_core.\n  std::unordered_map<GlobalCoreId, std::pair<NodeId, LocalCoreId>>\n      mock_nodes_and_local_cores_{};\n\n  typename Component::array_index array_index_{};\n  Parallel::GlobalCache<typename Component::metavariables>* global_cache_{\n      nullptr};\n  tuples::tagged_tuple_from_typelist<inbox_tags_list>* inboxes_{nullptr};\n  std::deque<std::unique_ptr<InvokeActionBase>> simple_action_queue_;\n  std::deque<std::unique_ptr<InvokeActionBase>> threaded_action_queue_;\n  Parallel::NodeLock node_lock_;\n};\n\ntemplate <typename Component>\nvoid MockDistributedObject<Component>::pup(PUP::er& p) {\n  if (not p.isSizing()) {\n    ERROR(\n        \"MockDistributedObject is not serializable. This pup member can only \"\n        \"be used for sizing.\\n\");\n  }\n  p | box_;\n}\n\ntemplate <typename Component>\nvoid MockDistributedObject<Component>::next_action() {\n  if (not next_action_if_ready()) {\n    ERROR(\"Attempted to run an action, but it returned \"\n          \"AlgorithmExecution::Retry.  Actions that are expected to retry \"\n          \"can be tested with next_action_if_ready().\");\n  }\n}\n\ntemplate <typename Component>\ntemplate <typename CatchMatcher>\nbool MockDistributedObject<Component>::next_action_if_ready(\n    const CatchMatcher* const matcher) {\n  bool found_matching_phase = false;\n  bool was_ready = false;\n  const auto invoke_for_phase = [this, &found_matching_phase, &matcher,\n                                 &was_ready](auto phase_dep_v) {\n    using PhaseDep = typename decltype(phase_dep_v)::type;\n    constexpr Parallel::Phase phase = PhaseDep::phase;\n    using actions_list = typename PhaseDep::action_list;\n    if (phase_ == phase) {\n      found_matching_phase = true;\n      was_ready = this->template next_action_impl<PhaseDep>(\n          matcher, std::make_index_sequence<tmpl::size<actions_list>::value>{});\n    }\n  };\n  tmpl::for_each<phase_dependent_action_lists>(invoke_for_phase);\n  if (not found_matching_phase) {\n    ERROR(\"Could not find any actions in the current phase (\"\n          << phase_ << \") for the component '\" << pretty_type::name<Component>()\n          << \"'.\");\n  }\n  return was_ready;\n}\n\ntemplate <typename Component>\ntemplate <typename PhaseDepActions, typename CatchMatcher, size_t... Is>\nbool MockDistributedObject<Component>::next_action_impl(\n    const CatchMatcher* const matcher, std::index_sequence<Is...> /*meta*/) {\n  if (UNLIKELY(performing_action_)) {\n    ERROR(\n        \"Cannot call an Action while already calling an Action on the same \"\n        \"MockDistributedObject (an element of a parallel component array, or a \"\n        \"parallel component singleton).\");\n  }\n  if (UNLIKELY(halt_algorithm_until_next_phase_)) {\n    ERROR(\n        \"The algorithm has been halted pending a phase change. No iterable \"\n        \"action can be executed until after the next change of phase.\");\n  }\n  // Keep track of if we already evaluated an action since we want `next_action`\n  // to only evaluate one per call.\n  bool already_did_an_action = false;\n  bool was_ready = true;\n  const auto helper = [this, &already_did_an_action, &was_ready,\n                       &matcher](auto iteration) {\n    constexpr size_t iter = decltype(iteration)::value;\n    if (already_did_an_action or algorithm_step_ != iter) {\n      return;\n    }\n\n    using actions_list = typename PhaseDepActions::action_list;\n    using this_action = tmpl::at_c<actions_list, iter>;\n\n    performing_action_ = true;\n    ++algorithm_step_;\n    if (matcher != nullptr) {\n      if constexpr (not std::is_same_v<CatchMatcher, int>) {\n        CHECK_THROWS_WITH(\n            (invoke_iterable_action<this_action, actions_list>(box_)),\n            *matcher);\n      } else {\n        ERROR(\n            \"The matcher type 'int' is a placeholder and the matcher must be a \"\n            \"nullptr when the placeholder is used, but in this case the \"\n            \"matcher pointer was not null. This is likely an internal bug, so \"\n            \"please file an issue.\");\n      }\n      was_ready = false;\n      --algorithm_step_;\n    } else if (not invoke_iterable_action<this_action, actions_list>(box_)) {\n      was_ready = false;\n      --algorithm_step_;\n    }\n    performing_action_ = false;\n    already_did_an_action = true;\n    // Wrap counter if necessary\n    if (algorithm_step_ >= tmpl::size<actions_list>::value) {\n      algorithm_step_ = 0;\n    }\n  };\n  // Silence compiler warning when there are no Actions.\n  (void)helper;\n  EXPAND_PACK_LEFT_TO_RIGHT(helper(std::integral_constant<size_t, Is>{}));\n  return was_ready;\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::number_of_procs() const {\n  return static_cast<int>(mock_nodes_and_local_cores_.size());\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::my_proc() const {\n  return static_cast<int>(mock_global_cores_.at(NodeId{mock_node_})\n                              .at(LocalCoreId{mock_local_core_})\n                              .value);\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::number_of_nodes() const {\n  return static_cast<int>(mock_global_cores_.size());\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::my_node() const {\n  return static_cast<int>(mock_node_);\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::procs_on_node(\n    const int node_index) const {\n  return static_cast<int>(\n      mock_global_cores_.at(NodeId{static_cast<size_t>(node_index)}).size());\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::my_local_rank() const {\n  return static_cast<int>(mock_local_core_);\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::first_proc_on_node(\n    const int node_index) const {\n  return static_cast<int>(\n      mock_global_cores_.at(NodeId{static_cast<size_t>(node_index)})\n          .at(LocalCoreId{0})\n          .value);\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::node_of(const int proc_index) const {\n  return static_cast<int>(mock_nodes_and_local_cores_\n                              .at(GlobalCoreId{static_cast<size_t>(proc_index)})\n                              .first.value);\n}\n\ntemplate <typename Component>\nint MockDistributedObject<Component>::local_rank_of(\n    const int proc_index) const {\n  return static_cast<int>(mock_nodes_and_local_cores_\n                              .at(GlobalCoreId{static_cast<size_t>(proc_index)})\n                              .second.value);\n}\n\n}  // namespace ActionTesting\n"
  },
  {
    "path": "tests/Unit/Framework/MockRuntimeSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <memory>\n#include <numeric>\n#include <stdexcept>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/MockDistributedObject.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\n/// \\cond\nnamespace ActionTesting {\ntemplate <typename Component>\nclass MockDistributedObject;\ntemplate <typename SimpleTagsList, typename ComputeTagsList = tmpl::list<>>\nstruct InitializeDataBox;\nstruct MockArrayChare;\nstruct MockGroupChare;\nstruct MockNodeGroupChare;\nstruct MockSingletonChare;\n}  // namespace ActionTesting\n/// \\endcond\n\nnamespace ActionTesting {\n\nnamespace detail {\n// `get_initialization` computes the type of the TaggedTuple that holds the\n// initial simple tags for the DataBox, as well as the\n// `initialize_databox_action` so that the user interface to the Action Testing\n// does not require the user to redundantly specify this information.\n// `get_initialization` also performs various sanity checks for the user.\ntemplate <typename Component>\nstruct get_initialization {\n  template <typename T>\n  struct is_initialization_phase {\n    using type =\n        std::integral_constant<bool,\n                               T::phase == Parallel::Phase::Initialization>;\n  };\n\n  using initialization_pdal_list =\n      tmpl::filter<typename Component::phase_dependent_action_list,\n                   is_initialization_phase<tmpl::_1>>;\n  static_assert(\n      tmpl::size<initialization_pdal_list>::value == 1,\n      \"Must have exactly one Initialization PhaseDependentActionList\");\n\n  using initialization_pdal = typename tmpl::front<initialization_pdal_list>;\n\n  static_assert(\n      tt::is_a_v<ActionTesting::InitializeDataBox,\n                 tmpl::front<typename initialization_pdal::action_list>>,\n      \"The first action in the initialization phase must be \"\n      \"ActionTesting::InitializeDataBox, even if the simple and \"\n      \"compute tags are empty.\");\n\n  using initialize_databox_action =\n      tmpl::front<typename initialization_pdal::action_list>;\n\n  using InitialValues = typename initialize_databox_action::InitialValues;\n};\n}  // namespace detail\n\n/// \\ingroup TestingFrameworkGroup\n/// A class that mocks the infrastructure needed to run actions.  It simulates\n/// message passing using the inbox infrastructure and handles most of the\n/// arguments to the apply method. This mocks the Charm++ runtime system as\n/// well as the layer built on top of it as part of SpECTRE.\ntemplate <typename Metavariables>\nclass MockRuntimeSystem {\n public:\n  // No moving, since MockCollectionOfDistributedObjectsProxy holds a pointer to\n  // us.\n  MockRuntimeSystem(const MockRuntimeSystem&) = delete;\n  MockRuntimeSystem(MockRuntimeSystem&&) = delete;\n  MockRuntimeSystem& operator=(const MockRuntimeSystem&) = delete;\n  MockRuntimeSystem& operator=(MockRuntimeSystem&&) = delete;\n  ~MockRuntimeSystem() = default;\n\n  template <typename Component>\n  struct InboxesTag {\n    using type = std::unordered_map<\n        typename Component::array_index,\n        tuples::tagged_tuple_from_typelist<\n            typename MockDistributedObject<Component>::inbox_tags_list>>;\n  };\n\n  template <typename Component>\n  struct MockDistributedObjectsTag {\n    using type = std::unordered_map<typename Component::array_index,\n                                    MockDistributedObject<Component>>;\n  };\n\n  using GlobalCache = Parallel::GlobalCache<Metavariables>;\n  using CacheTuple = tuples::tagged_tuple_from_typelist<\n      Parallel::get_const_global_cache_tags<Metavariables>>;\n  using MutableCacheTuple = tuples::tagged_tuple_from_typelist<\n      Parallel::get_mutable_global_cache_tags<Metavariables>>;\n\n  using mock_objects_tags =\n      tmpl::transform<typename Metavariables::component_list,\n                      tmpl::bind<MockDistributedObjectsTag, tmpl::_1>>;\n  using CollectionOfMockDistributedObjects =\n      tuples::tagged_tuple_from_typelist<mock_objects_tags>;\n  using Inboxes = tuples::tagged_tuple_from_typelist<\n      tmpl::transform<typename Metavariables::component_list,\n                      tmpl::bind<InboxesTag, tmpl::_1>>>;\n\n  /// Construct from the tuple of GlobalCache objects.\n  explicit MockRuntimeSystem(\n      CacheTuple cache_contents, MutableCacheTuple mutable_cache_contents = {},\n      const std::vector<size_t>& number_of_mock_cores_on_each_mock_node = {1})\n      : mock_global_cores_([&number_of_mock_cores_on_each_mock_node]() {\n          std::unordered_map<NodeId,\n                             std::unordered_map<LocalCoreId, GlobalCoreId>>\n              mock_global_cores{};\n          size_t global_core = 0;\n          for (size_t node = 0;\n               node < number_of_mock_cores_on_each_mock_node.size(); ++node) {\n            std::unordered_map<LocalCoreId, GlobalCoreId> global_cores;\n            for (size_t local_core = 0;\n                 local_core < number_of_mock_cores_on_each_mock_node[node];\n                 ++local_core, ++global_core) {\n              global_cores.insert(\n                  {LocalCoreId{local_core}, GlobalCoreId{global_core}});\n            }\n            mock_global_cores.insert({NodeId{node}, global_cores});\n          }\n          return mock_global_cores;\n        }()),\n        mock_nodes_and_local_cores_(\n            [&number_of_mock_cores_on_each_mock_node]() {\n              std::unordered_map<GlobalCoreId, std::pair<NodeId, LocalCoreId>>\n                  mock_nodes_and_local_cores{};\n              size_t global_core = 0;\n              for (size_t node = 0;\n                   node < number_of_mock_cores_on_each_mock_node.size();\n                   ++node) {\n                for (size_t local_core = 0;\n                     local_core < number_of_mock_cores_on_each_mock_node[node];\n                     ++local_core, ++global_core) {\n                  mock_nodes_and_local_cores.insert(\n                      {GlobalCoreId{global_core},\n                       std::make_pair(NodeId{node}, LocalCoreId{local_core})});\n                }\n              }\n              return mock_nodes_and_local_cores;\n            }()),\n        caches_(mock_nodes_and_local_cores_.size()) {\n    // Fill the parallel components in each cache.  There is one cache\n    // per mock core.  The parallel components held in each cache (i.e. the\n    // MockProxies) are filled with the same GlobalCache data, and are\n    // passed the mock node and mock core associated with that cache.\n    // The actual chares held in each cache are emplaced by the user explicitly\n    // on a chosen node and core, via `emplace_component`.\n    for (const auto& [global_core_id, node_and_local_core] :\n         mock_nodes_and_local_cores_) {\n      const size_t global_core = global_core_id.value;\n      // Note that lambdas cannot capture structured bindings,\n      // so we define node and local_core here.\n      const auto& node = node_and_local_core.first.value;\n      const auto& local_core = node_and_local_core.second.value;\n      caches_.at(global_core) = std::make_unique<GlobalCache>(\n          serialize_and_deserialize(cache_contents),\n          serialize_and_deserialize(mutable_cache_contents),\n          number_of_mock_cores_on_each_mock_node, static_cast<int>(global_core),\n          static_cast<int>(node), static_cast<int>(local_core));\n      tmpl::for_each<typename Metavariables::component_list>(\n          [this, &global_core, &node, &local_core](auto component) {\n            using Component = tmpl::type_from<decltype(component)>;\n            Parallel::get_parallel_component<Component>(\n                *caches_.at(global_core))\n                .set_data(&tuples::get<MockDistributedObjectsTag<Component>>(\n                              mock_distributed_objects_),\n                          &tuples::get<InboxesTag<Component>>(inboxes_), node,\n                          local_core, global_core);\n          });\n    }\n  }\n\n  /// Construct from the tuple of const and mutable tags that might be in a\n  /// different order.\n  template <typename... CacheTags, typename... MutableCacheTags>\n  MockRuntimeSystem(\n      tuples::TaggedTuple<CacheTags...> cache_contents,\n      tuples::TaggedTuple<MutableCacheTags...> mutable_cache_contents = {},\n      const std::vector<size_t>& number_of_mock_cores_on_each_mock_node = {1})\n      : MockRuntimeSystem(\n            tuples::reorder<CacheTuple>(std::move(cache_contents)),\n            tuples::reorder<MutableCacheTuple>(\n                std::move(mutable_cache_contents)),\n            number_of_mock_cores_on_each_mock_node) {}\n\n  /// Emplace an array component that does not need to be initialized.\n  template <typename Component, typename... Options>\n  void emplace_array_component(\n      const NodeId node_id, const LocalCoreId local_core_id,\n      const typename Component::array_index& array_index, Options&&... opts) {\n    mock_distributed_objects<Component>().emplace(\n        array_index,\n        MockDistributedObject<Component>(\n            node_id, local_core_id, mock_global_cores_,\n            mock_nodes_and_local_cores_, array_index,\n            caches_.at(mock_global_cores_.at(node_id).at(local_core_id).value)\n                .get(),\n            // Next line inserts element into inboxes_ and returns ptr to it.\n            &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),\n            std::forward<Options>(opts)...));\n  }\n\n  /// Emplace a singleton component that does not need to be initialized.\n  template <typename Component, typename... Options>\n  void emplace_singleton_component(const NodeId node_id,\n                                   const LocalCoreId local_core_id,\n                                   Options&&... opts) {\n    static_assert(\n        std::is_same_v<typename Component::chare_type, MockSingletonChare>,\n        \"emplace_singleton_component expects a MockSingletonChare\");\n    // For a singleton, use an array index of zero.\n    const typename Component::array_index array_index{0};\n    mock_distributed_objects<Component>().emplace(\n        array_index,\n        MockDistributedObject<Component>(\n            node_id, local_core_id, mock_global_cores_,\n            mock_nodes_and_local_cores_, array_index,\n            caches_.at(mock_global_cores_.at(node_id).at(local_core_id).value)\n                .get(),\n            // Next line inserts element into inboxes_ and returns ptr to it.\n            &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),\n            std::forward<Options>(opts)...));\n  }\n\n  /// Emplace a group component that does not need to be initialized.\n  template <typename Component, typename... Options>\n  void emplace_group_component(Options&&... opts) {\n    static_assert(\n        std::is_same_v<typename Component::chare_type, MockGroupChare>,\n        \"emplace_group_component expects a MockGroupChare\");\n    // Emplace once for each core, index by global_core.\n    for (const auto& [global_core_id, node_and_local_core] :\n         mock_nodes_and_local_cores_) {\n      const size_t global_core = global_core_id.value;\n      const typename Component::array_index array_index = global_core;\n      mock_distributed_objects<Component>().emplace(\n          array_index,\n          MockDistributedObject<Component>(\n              node_and_local_core.first, node_and_local_core.second,\n              mock_global_cores_, mock_nodes_and_local_cores_, array_index,\n              caches_.at(global_core).get(),\n              // Next line inserts element into inboxes_ and returns ptr to it.\n              &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),\n              std::forward<Options>(opts)...));\n    }\n  }\n\n  /// Emplace a nodegroup component that does not need to be initialized.\n  template <typename Component, typename... Options>\n  void emplace_nodegroup_component(Options&&... opts) {\n    static_assert(\n        std::is_same_v<typename Component::chare_type, MockNodeGroupChare>,\n        \"emplace_nodegroup_component expects a MockNodeGroupChare\");\n    // Emplace once for each node, index by node number.\n    for (const auto& [node, local_and_global_cores] : mock_global_cores_) {\n      const typename Component::array_index array_index = node.value;\n      // Use first proc on each node as global_core\n      const size_t global_core =\n          local_and_global_cores.at(LocalCoreId{0}).value;\n      mock_distributed_objects<Component>().emplace(\n          array_index,\n          MockDistributedObject<Component>(\n              node, LocalCoreId{0}, mock_global_cores_,\n              mock_nodes_and_local_cores_, array_index,\n              caches_.at(global_core).get(),\n              // Next line inserts element into inboxes_ and returns ptr to it.\n              &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),\n              std::forward<Options>(opts)...));\n    }\n  }\n\n  /// Emplace a component that does not need to be initialized.\n  /// emplace_component is deprecated in favor of\n  /// emplace_array_component, emplace_singleton_component,\n  /// emplace_group_component, and emplace_nodegroup_component.\n  template <typename Component, typename... Options>\n  void emplace_component(const typename Component::array_index& array_index,\n                         Options&&... opts) {\n    emplace_array_component<Component>(NodeId{0}, LocalCoreId{0}, array_index,\n                                       std::forward<Options>(opts)...);\n  }\n\n  /// Emplace an array component that needs to be initialized.\n  template <typename Component, typename... Options>\n  void emplace_array_component_and_initialize(\n      const NodeId node_id, const LocalCoreId local_core_id,\n      const typename Component::array_index& array_index,\n      const typename detail::get_initialization<Component>::InitialValues&\n          initial_values,\n      Options&&... opts) {\n    detail::get_initialization<Component>::initialize_databox_action::\n        set_initial_values(initial_values);\n    auto [iterator, emplace_was_successful] =\n        mock_distributed_objects<Component>().emplace(\n            array_index,\n            MockDistributedObject<Component>(\n                node_id, local_core_id, mock_global_cores_,\n                mock_nodes_and_local_cores_, array_index,\n                caches_\n                    .at(mock_global_cores_.at(node_id).at(local_core_id).value)\n                    .get(),\n                // Next line inserts element into inboxes_ and returns ptr to\n                // it.\n                &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),\n                std::forward<Options>(opts)...));\n    if (not emplace_was_successful) {\n      ERROR(\"Failed to insert parallel component '\"\n            << pretty_type::get_name<Component>() << \"' with index \"\n            << array_index);\n    }\n    iterator->second.set_phase(Parallel::Phase::Initialization);\n    iterator->second.next_action();\n  }\n\n  /// Emplace a singleton component that needs to be initialized.\n  template <typename Component, typename... Options>\n  void emplace_singleton_component_and_initialize(\n      const NodeId node_id, const LocalCoreId local_core_id,\n      const typename detail::get_initialization<Component>::InitialValues&\n          initial_values,\n      Options&&... opts) {\n    static_assert(\n        std::is_same_v<typename Component::chare_type, MockSingletonChare>,\n        \"emplace_singleton_component_and_initialize expects a \"\n        \"MockSingletonChare\");\n    // Use 0 for the array_index\n    const typename Component::array_index& array_index{0};\n    detail::get_initialization<Component>::initialize_databox_action::\n        set_initial_values(initial_values);\n    auto [iterator, emplace_was_successful] =\n        mock_distributed_objects<Component>().emplace(\n            array_index,\n            MockDistributedObject<Component>(\n                node_id, local_core_id, mock_global_cores_,\n                mock_nodes_and_local_cores_, array_index,\n                caches_\n                    .at(mock_global_cores_.at(node_id).at(local_core_id).value)\n                    .get(),\n                // Next line inserts element into inboxes_ and returns ptr to\n                // it.\n                &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),\n                std::forward<Options>(opts)...));\n    if (not emplace_was_successful) {\n      ERROR(\"Failed to insert parallel component '\"\n            << pretty_type::get_name<Component>() << \"' with index \"\n            << array_index);\n    }\n    iterator->second.set_phase(Parallel::Phase::Initialization);\n    iterator->second.next_action();\n  }\n\n  /// Emplace a group component that needs to be initialized.\n  template <typename Component, typename... Options>\n  void emplace_group_component_and_initialize(\n      const typename detail::get_initialization<Component>::InitialValues&\n          initial_values,\n      Options&&... opts) {\n    static_assert(\n        std::is_same_v<typename Component::chare_type, MockGroupChare>,\n        \"emplace_group_component_and_initialize expects a MockGroupChare\");\n    // Emplace once for each core, index by global_core.\n    for (const auto& [global_core_id, node_and_local_core] :\n         mock_nodes_and_local_cores_) {\n      // Need to set initial values for each component, since the\n      // InitialDataBox action does a std::move.\n      detail::get_initialization<Component>::initialize_databox_action::\n          set_initial_values(initial_values);\n      const size_t global_core = global_core_id.value;\n      const typename Component::array_index array_index = global_core;\n      auto [iterator, emplace_was_successful] =\n          mock_distributed_objects<Component>().emplace(\n              array_index,\n              MockDistributedObject<Component>(\n                  node_and_local_core.first, node_and_local_core.second,\n                  mock_global_cores_, mock_nodes_and_local_cores_, array_index,\n                  caches_.at(global_core).get(),\n                  // Next line inserts element into inboxes_ and returns ptr to\n                  // it.\n                  &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),\n                  std::forward<Options>(opts)...));\n      if (not emplace_was_successful) {\n        ERROR(\"Failed to insert parallel component '\"\n              << pretty_type::get_name<Component>() << \"' with index \"\n              << array_index);\n      }\n      iterator->second.set_phase(Parallel::Phase::Initialization);\n      iterator->second.next_action();\n    }\n  }\n\n  /// Emplace a nodegroup component that needs to be initialized.\n  template <typename Component, typename... Options>\n  void emplace_nodegroup_component_and_initialize(\n      const typename detail::get_initialization<Component>::InitialValues&\n          initial_values,\n      Options&&... opts) {\n    static_assert(\n        std::is_same_v<typename Component::chare_type, MockNodeGroupChare>,\n        \"emplace_nodegroup_component_and_initialize expects a \"\n        \"MockNodeGroupChare\");\n    // Emplace once for each node, index by node number.\n    for (const auto& [node, local_and_global_cores] : mock_global_cores_) {\n      // Need to set initial values for each component, since the\n      // InitialDataBox action does a std::move.\n      detail::get_initialization<Component>::initialize_databox_action::\n          set_initial_values(initial_values);\n      const typename Component::array_index array_index = node.value;\n      // Use first proc on each node as global_core\n      const size_t global_core =\n          local_and_global_cores.at(LocalCoreId{0}).value;\n      auto [iterator, emplace_was_successful] =\n          mock_distributed_objects<Component>().emplace(\n              array_index,\n              MockDistributedObject<Component>(\n                  node, LocalCoreId{0}, mock_global_cores_,\n                  mock_nodes_and_local_cores_, array_index,\n                  caches_.at(global_core).get(),\n                  // Next line inserts element into inboxes_ and returns ptr to\n                  // it.\n                  &(tuples::get<InboxesTag<Component>>(inboxes_)[array_index]),\n                  std::forward<Options>(opts)...));\n      if (not emplace_was_successful) {\n        ERROR(\"Failed to insert parallel component '\"\n              << pretty_type::get_name<Component>() << \"' with index \"\n              << array_index);\n      }\n      iterator->second.set_phase(Parallel::Phase::Initialization);\n      iterator->second.next_action();\n    }\n  }\n\n  /// Emplace a component that needs to be initialized.\n  /// emplace_component_and_initialize is deprecated in favor of\n  /// emplace_array_component_and_initialize,\n  /// emplace_singleton_component_and_initialize,\n  /// emplace_group_component_and_initialize, and\n  /// emplace_nodegroup_component_and_initialize.\n  template <typename Component, typename... Options,\n            typename Metavars = Metavariables>\n  void emplace_component_and_initialize(\n      const typename Component::array_index& array_index,\n      const typename detail::get_initialization<Component>::InitialValues&\n          initial_values,\n      Options&&... opts) {\n    emplace_array_component_and_initialize<Component>(\n        NodeId{0}, LocalCoreId{0}, array_index, initial_values,\n        std::forward<Options>(opts)...);\n  }\n\n  /// @{\n  /// Invoke the simple action `Action` on the `Component` labeled by\n  /// `array_index` immediately.\n  template <typename Component, typename Action, typename Arg0,\n            typename... Args>\n  void simple_action(const typename Component::array_index& array_index,\n                     Arg0&& arg0, Args&&... args) {\n    try {\n      mock_distributed_objects<Component>()\n          .at(array_index)\n          .template simple_action<Action>(\n              std::make_tuple(std::forward<Arg0>(arg0),\n                              std::forward<Args>(args)...),\n              true);\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  template <typename Component, typename Action>\n  void simple_action(const typename Component::array_index& array_index) {\n    try {\n      mock_distributed_objects<Component>()\n          .at(array_index)\n          .template simple_action<Action>(true);\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n  /// @}\n\n  /// @{\n  /// Queue the simple action `Action` on the `Component` labeled by\n  /// `array_index`.\n  template <typename Component, typename Action, typename Arg0,\n            typename... Args>\n  void queue_simple_action(const typename Component::array_index& array_index,\n                           Arg0&& arg0, Args&&... args) {\n    try {\n      mock_distributed_objects<Component>()\n          .at(array_index)\n          .template simple_action<Action>(\n              std::make_tuple(std::forward<Arg0>(arg0),\n                              std::forward<Args>(args)...),\n              false);\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  template <typename Component, typename Action>\n  void queue_simple_action(const typename Component::array_index& array_index) {\n    try {\n      mock_distributed_objects<Component>()\n          .at(array_index)\n          .template simple_action<Action>(false);\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n  /// @}\n\n  /// @{\n  /// Invoke the threaded action `Action` on the `Component` labeled by\n  /// `array_index` immediately.\n  template <typename Component, typename Action, typename Arg0,\n            typename... Args>\n  void threaded_action(const typename Component::array_index& array_index,\n                       Arg0&& arg0, Args&&... args) {\n    try {\n      mock_distributed_objects<Component>()\n          .at(array_index)\n          .template threaded_action<Action>(\n              std::make_tuple(std::forward<Arg0>(arg0),\n                              std::forward<Args>(args)...),\n              true);\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  template <typename Component, typename Action>\n  void threaded_action(const typename Component::array_index& array_index) {\n    try {\n      mock_distributed_objects<Component>()\n          .at(array_index)\n          .template threaded_action<Action>(true);\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n  /// @}\n\n  /// Return true if there are no queued simple actions on the\n  /// `Component` labeled by `array_index`.\n  template <typename Component>\n  bool is_simple_action_queue_empty(\n      const typename Component::array_index& array_index) const {\n    try {\n      return mock_distributed_objects<Component>()\n          .at(array_index)\n          .is_simple_action_queue_empty();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  /// Return the number of queued simple actions on the\n  /// `Component` labeled by `array_index`.\n  template <typename Component>\n  size_t number_of_queued_simple_actions(\n      const typename Component::array_index& array_index) const {\n    try {\n      return mock_distributed_objects<Component>()\n          .at(array_index)\n          .simple_action_queue_size();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  /// Invoke the next queued simple action on the `Component` labeled by\n  /// `array_index`.\n  template <typename Component>\n  void invoke_queued_simple_action(\n      const typename Component::array_index& array_index) {\n    try {\n      mock_distributed_objects<Component>()\n          .at(array_index)\n          .invoke_queued_simple_action();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  /// Return true if there are no queued threaded actions on the\n  /// `Component` labeled by `array_index`.\n  template <typename Component>\n  bool is_threaded_action_queue_empty(\n      const typename Component::array_index& array_index) const {\n    try {\n      return mock_distributed_objects<Component>()\n          .at(array_index)\n          .is_threaded_action_queue_empty();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  /// Return the number of queued threaded actions on the\n  /// `Component` labeled by `array_index`.\n  template <typename Component>\n  size_t number_of_queued_threaded_actions(\n      const typename Component::array_index& array_index) const {\n    try {\n      return mock_distributed_objects<Component>()\n          .at(array_index)\n          .threaded_action_queue_size();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  /// Invoke the next queued threaded action on the `Component` labeled by\n  /// `array_index`.\n  template <typename Component>\n  void invoke_queued_threaded_action(\n      const typename Component::array_index& array_index) {\n    try {\n      mock_distributed_objects<Component>()\n          .at(array_index)\n          .invoke_queued_threaded_action();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  /// Instead of the next call to `next_action` applying the next action in\n  /// the action list, force the next action to be `Action`\n  template <typename Component, typename Action>\n  void force_next_action_to_be(\n      const typename Component::array_index& array_index) {\n    static_assert(\n        tmpl::list_contains_v<\n            typename MockDistributedObject<Component>::all_actions_list,\n            Action>,\n        \"Cannot force a next action that is not in the action list of the \"\n        \"parallel component. See the first template parameter of \"\n        \"'force_next_action_to_be' for the component and the second template \"\n        \"parameter for the action.\");\n    bool found_matching_phase = false;\n    const auto invoke_for_phase = [this, &array_index,\n                                   &found_matching_phase](auto phase_dep_v) {\n      using PhaseDep = decltype(phase_dep_v);\n      constexpr Parallel::Phase phase = PhaseDep::type::phase;\n      using actions_list = typename PhaseDep::type::action_list;\n      auto& distributed_object =\n          this->mock_distributed_objects<Component>().at(array_index);\n      if (distributed_object.get_phase() == phase) {\n        found_matching_phase = true;\n        distributed_object.force_next_action_to_be(\n            tmpl::conditional_t<\n                std::is_same<tmpl::no_such_type_,\n                             tmpl::index_of<actions_list, Action>>::value,\n                std::integral_constant<size_t, 0>,\n                tmpl::index_of<actions_list, Action>>::value);\n      }\n    };\n    tmpl::for_each<typename MockDistributedObject<\n        Component>::phase_dependent_action_lists>(invoke_for_phase);\n    if (not found_matching_phase) {\n      ERROR(\n          \"Could not find any actions in the current phase for the component '\"\n          << pretty_type::name<Component>()\n          << \"'. Maybe you are not in the phase you expected to be in? The \"\n             \"integer value corresponding to the current phase is \"\n          << static_cast<int>(this->mock_distributed_objects<Component>()\n                                  .at(array_index)\n                                  .get_phase()));\n    }\n  }\n\n  /// Obtain the index into the action list of the next action.\n  template <typename Component>\n  size_t get_next_action_index(\n      const typename Component::array_index& array_index) const {\n    try {\n      return mock_distributed_objects<Component>()\n          .at(array_index)\n          .get_next_action_index();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  /// Invoke the next action in the ActionList on the parallel component\n  /// `Component` on the component labeled by `array_index`, failing if it was\n  /// not ready.\n  template <typename Component>\n  void next_action(const typename Component::array_index& array_index) {\n    try {\n      mock_distributed_objects<Component>().at(array_index).next_action();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  /// @{\n  /// Invoke the next action in the ActionList on the parallel component\n  /// `Component` on the component labeled by `array_index`, returning whether\n  /// it was ready.\n  template <typename Component>\n  bool next_action_if_ready(\n      const typename Component::array_index& array_index) {\n    try {\n      return mock_distributed_objects<Component>()\n          .at(array_index)\n          .next_action_if_ready();\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n\n  template <typename Component, typename CatchMatcher>\n  bool next_action_if_ready(const typename Component::array_index& array_index,\n                            const CatchMatcher& matcher) {\n    static_assert(std::is_base_of_v<Catch::Matchers::MatcherBase<std::string>,\n                                    CatchMatcher>);\n    try {\n      return mock_distributed_objects<Component>()\n          .at(array_index)\n          .next_action_if_ready(std::addressof(matcher));\n    } catch (const std::exception& e) {\n      ERROR(\"Caught exception \" << e.what() << \" on array index \"\n                                << array_index);\n    }\n  }\n  /// @}\n\n  /// @{\n  /// Access the inboxes for a given component.\n  template <typename Component>\n  auto inboxes() -> std::unordered_map<\n      typename Component::array_index,\n      tuples::tagged_tuple_from_typelist<\n          typename MockDistributedObject<Component>::inbox_tags_list>>& {\n    return tuples::get<InboxesTag<Component>>(inboxes_);\n  }\n\n  template <typename Component>\n  auto inboxes() const -> const std::unordered_map<\n      typename Component::array_index,\n      tuples::tagged_tuple_from_typelist<\n          typename MockDistributedObject<Component>::inbox_tags_list>>& {\n    return tuples::get<InboxesTag<Component>>(inboxes_);\n  }\n  /// @}\n\n  /// Find the set of array indices on Component where the specified\n  /// inbox is not empty.\n  template <typename Component, typename InboxTag>\n  auto nonempty_inboxes()\n      -> std::unordered_set<typename Component::array_index> {\n    std::unordered_set<typename Component::array_index> result;\n    for (const auto& element_box : inboxes<Component>()) {\n      if (not tuples::get<InboxTag>(element_box.second).empty()) {\n        result.insert(element_box.first);\n      }\n    }\n    return result;\n  }\n\n  /// Access the mocked distributed objects for a component, indexed by array\n  /// index.\n  template <typename Component>\n  auto& mock_distributed_objects() {\n    return tuples::get<MockDistributedObjectsTag<Component>>(\n        mock_distributed_objects_);\n  }\n\n  template <typename Component>\n  const auto& mock_distributed_objects() const {\n    return tuples::get<MockDistributedObjectsTag<Component>>(\n        mock_distributed_objects_);\n  }\n\n  /// Set the phase of all parallel components to `next_phase`\n  void set_phase(const Parallel::Phase next_phase) {\n    tmpl::for_each<mock_objects_tags>([this, &next_phase](auto component_v) {\n      for (auto& object : tuples::get<typename decltype(component_v)::type>(\n               mock_distributed_objects_)) {\n        object.second.set_phase(next_phase);\n      }\n    });\n  }\n\n  /// Return number of (mock) global cores.\n  size_t num_global_cores() const { return mock_nodes_and_local_cores_.size(); }\n\n  /// Return number of (mock) nodes.\n  size_t num_nodes() const { return mock_global_cores_.size(); }\n\n private:\n  std::unordered_map<NodeId, std::unordered_map<LocalCoreId, GlobalCoreId>>\n      mock_global_cores_{};\n  std::unordered_map<GlobalCoreId, std::pair<NodeId, LocalCoreId>>\n      mock_nodes_and_local_cores_{};\n  // There is one GlobalCache per global_core.  We need to keep a vector of\n  // unique_ptrs rather than a vector of objects because GlobalCache is not\n  // move-assignable or copyable (because of its charm++ base classes, in\n  // particular CkReductionMgr).\n  std::vector<std::unique_ptr<GlobalCache>> caches_{};\n  Inboxes inboxes_{};\n  CollectionOfMockDistributedObjects mock_distributed_objects_{};\n};\n}  // namespace ActionTesting\n"
  },
  {
    "path": "tests/Unit/Framework/MockRuntimeSystemFreeFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines free functions that wrap MockRuntimeSystem member functions.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <random>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"Framework/MockRuntimeSystem.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace ActionTesting {\n/// Set the phase of all parallel components to `phase`\ntemplate <typename Metavariables>\nvoid set_phase(const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n               const Parallel::Phase& phase) {\n  runner->set_phase(phase);\n}\n\n/// Emplaces a distributed object with index `array_index` into the parallel\n/// component `Component`. The options `opts` are forwarded to be used in a call\n/// to `detail::ForwardAllOptionsToDataBox::apply`.\ntemplate <typename Component, typename... Options>\nvoid emplace_component(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    const typename Component::array_index& array_index, Options&&... opts) {\n  runner->template emplace_component<Component>(array_index,\n                                                std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed array object with index `array_index` into\n/// the parallel component `Component`. The options `opts` are\n/// forwarded to be used in a call to\n/// `detail::ForwardAllOptionsToDataBox::apply`.\ntemplate <typename Component, typename... Options>\nvoid emplace_array_component(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    const NodeId node_id, const LocalCoreId local_core_id,\n    const typename Component::array_index& array_index, Options&&... opts) {\n  runner->template emplace_array_component<Component>(\n      node_id, local_core_id, array_index, std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed singleton object into\n/// the parallel component `Component`. The options `opts` are\n/// forwarded to be used in a call to\n/// `detail::ForwardAllOptionsToDataBox::apply`.\ntemplate <typename Component, typename... Options>\nvoid emplace_singleton_component(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    const NodeId node_id, const LocalCoreId local_core_id, Options&&... opts) {\n  runner->template emplace_singleton_component<Component>(\n      node_id, local_core_id, std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed group object into\n/// the parallel component `Component`. The options `opts` are\n/// forwarded to be used in a call to\n/// `detail::ForwardAllOptionsToDataBox::apply`.\ntemplate <typename Component, typename... Options>\nvoid emplace_group_component(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    Options&&... opts) {\n  runner->template emplace_group_component<Component>(\n      std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed nodegroup object into\n/// the parallel component `Component`. The options `opts` are\n/// forwarded to be used in a call to\n/// `detail::ForwardAllOptionsToDataBox::apply`.\ntemplate <typename Component, typename... Options>\nvoid emplace_nodegroup_component(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    Options&&... opts) {\n  runner->template emplace_nodegroup_component<Component>(\n      std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed object with index `array_index` into the parallel\n/// component `Component`. The options `opts` are forwarded to be used in a call\n/// to `detail::ForwardAllOptionsToDataBox::apply` Additionally, the simple tags\n/// in the DataBox are initialized from the values set in `initial_values`.\ntemplate <typename Component, typename... Options,\n          typename Metavars = typename Component::metavariables>\nvoid emplace_component_and_initialize(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    const typename Component::array_index& array_index,\n    const typename detail::get_initialization<Component>::InitialValues&\n        initial_values,\n    Options&&... opts) {\n  runner->template emplace_component_and_initialize<Component>(\n      array_index, initial_values, std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed array object with index `array_index` into the\n/// parallel component `Component`. The options `opts` are forwarded to be used\n/// in a call to `detail::ForwardAllOptionsToDataBox::apply` Additionally, the\n/// simple tags in the DataBox are initialized from the values set in\n/// `initial_values`.\ntemplate <typename Component, typename... Options,\n          typename Metavars = typename Component::metavariables>\nvoid emplace_array_component_and_initialize(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    const NodeId node_id, const LocalCoreId local_core_id,\n    const typename Component::array_index& array_index,\n    const typename detail::get_initialization<Component>::InitialValues&\n        initial_values,\n    Options&&... opts) {\n  runner->template emplace_array_component_and_initialize<Component>(\n      node_id, local_core_id, array_index, initial_values,\n      std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed singleton object into the parallel\n/// component `Component`. The options `opts` are forwarded to be used\n/// in a call to `detail::ForwardAllOptionsToDataBox::apply`\n/// Additionally, the simple tags in the DataBox are initialized from\n/// the values set in `initial_values`.\ntemplate <typename Component, typename... Options,\n          typename Metavars = typename Component::metavariables>\nvoid emplace_singleton_component_and_initialize(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    const NodeId node_id, const LocalCoreId local_core_id,\n    const typename detail::get_initialization<Component>::InitialValues&\n        initial_values,\n    Options&&... opts) {\n  runner->template emplace_singleton_component_and_initialize<Component>(\n      node_id, local_core_id, initial_values, std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed group object into the\n/// parallel component `Component`. The options `opts` are forwarded to be used\n/// in a call to `detail::ForwardAllOptionsToDataBox::apply` Additionally, the\n/// simple tags in the DataBox are initialized from the values set in\n/// `initial_values`.\ntemplate <typename Component, typename... Options,\n          typename Metavars = typename Component::metavariables>\nvoid emplace_group_component_and_initialize(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    const typename detail::get_initialization<Component>::InitialValues&\n        initial_values,\n    Options&&... opts) {\n  runner->template emplace_group_component_and_initialize<Component>(\n      initial_values, std::forward<Options>(opts)...);\n}\n\n/// Emplaces a distributed nodegroup object into the\n/// parallel component `Component`. The options `opts` are forwarded to be used\n/// in a call to `detail::ForwardAllOptionsToDataBox::apply` Additionally, the\n/// simple tags in the DataBox are initialized from the values set in\n/// `initial_values`.\ntemplate <typename Component, typename... Options,\n          typename Metavars = typename Component::metavariables>\nvoid emplace_nodegroup_component_and_initialize(\n    const gsl::not_null<MockRuntimeSystem<typename Component::metavariables>*>\n        runner,\n    const typename detail::get_initialization<Component>::InitialValues&\n        initial_values,\n    Options&&... opts) {\n  runner->template emplace_nodegroup_component_and_initialize<Component>(\n      initial_values, std::forward<Options>(opts)...);\n}\n\n/// @{\n/// Retrieves the DataBox from the parallel component `Component` with\n/// index `array_index`.\ntemplate <typename Component, typename Metavariables>\nconst auto& get_databox(const MockRuntimeSystem<Metavariables>& runner,\n                        const typename Component::array_index& array_index) {\n  return runner.template mock_distributed_objects<Component>()\n      .at(array_index)\n      .get_databox();\n}\n\ntemplate <typename Component, typename Metavariables>\nauto& get_databox(const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n                  const typename Component::array_index& array_index) {\n  return runner->template mock_distributed_objects<Component>()\n      .at(array_index)\n      .get_databox();\n}\n/// @}\n\n/// Get the index in the action list of the next action.\ntemplate <typename Component, typename Metavariables>\nsize_t get_next_action_index(\n    const MockRuntimeSystem<Metavariables>& runner,\n    const typename Component::array_index& array_index) {\n  return runner.template get_next_action_index<Component>(array_index);\n}\n\n/// Returns the `Tag` from the `DataBox` of the parallel component `Component`\n/// with array index `array_index`. If the component's current `DataBox` type\n/// does not contain `Tag` then an error is emitted.\ntemplate <typename Component, typename Tag, typename Metavariables>\nconst auto& get_databox_tag(\n    const MockRuntimeSystem<Metavariables>& runner,\n    const typename Component::array_index& array_index) {\n  return runner.template mock_distributed_objects<Component>()\n      .at(array_index)\n      .template get_databox_tag<Tag>();\n}\n\n/// @{\n/// Returns the `InboxTag` from the parallel component `Component` with array\n/// index `array_index`.\ntemplate <typename Component, typename InboxTag, typename Metavariables>\nconst auto& get_inbox_tag(const MockRuntimeSystem<Metavariables>& runner,\n                          const typename Component::array_index& array_index) {\n  return tuples::get<InboxTag>(\n      runner.template inboxes<Component>().at(array_index));\n}\n\ntemplate <typename Component, typename InboxTag, typename Metavariables>\nauto& get_inbox_tag(\n    const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n    const typename Component::array_index& array_index) {\n  return tuples::get<InboxTag>(\n      runner->template inboxes<Component>().at(array_index));\n}\n/// @}\n\n/// Returns `true` if the current DataBox of `Component` with index\n/// `array_index` contains the tag `Tag`. If the tag is not contained, returns\n/// `false`.\ntemplate <typename Component, typename Tag, typename Metavariables>\nbool box_contains(const MockRuntimeSystem<Metavariables>& runner,\n                  const typename Component::array_index& array_index) {\n  return runner.template mock_distributed_objects<Component>()\n      .at(array_index)\n      .template box_contains<Tag>();\n}\n\n/// Returns `true` if the tag `Tag` can be retrieved from the current DataBox\n/// of `Component` with index `array_index`.\ntemplate <typename Component, typename Tag, typename Metavariables>\nbool tag_is_retrievable(const MockRuntimeSystem<Metavariables>& runner,\n                        const typename Component::array_index& array_index) {\n  return runner.template mock_distributed_objects<Component>()\n      .at(array_index)\n      .template tag_is_retrievable<Tag>();\n}\n\n/// Invoke the next action in the ActionList on the parallel component\n/// `Component` on the component labeled by `array_index`, failing if it was\n/// not ready.\ntemplate <typename Component, typename Metavariables>\nvoid next_action(const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n                 const typename Component::array_index& array_index) {\n  runner->template next_action<Component>(array_index);\n}\n\n/// @{\n/// Invoke the next action in the ActionList on the parallel component\n/// `Component` on the component labeled by `array_index`, returning whether\n/// it was ready.\n///\n/// If a Catch matcher is specified then it is assumed that the first action\n/// call should throw an exception. Essentially, the action invocation is\n/// wrapped in `CHECK_THROWS_WITH(invoke, matcher)`\ntemplate <typename Component, typename Metavariables>\nbool next_action_if_ready(\n    const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n    const typename Component::array_index& array_index) {\n  return runner->template next_action_if_ready<Component>(array_index);\n}\n\ntemplate <typename Component, typename Metavariables, typename CatchMatcher>\nbool next_action_if_ready(\n    const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n    const typename Component::array_index& array_index,\n    const CatchMatcher& matcher) {\n  return runner->template next_action_if_ready<Component>(array_index, matcher);\n}\n/// @}\n\n/// Runs the simple action `Action` on the `array_index`th element of the\n/// parallel component `Component`.\ntemplate <typename Component, typename Action, typename Metavariables,\n          typename... Args>\nvoid simple_action(\n    const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n    const typename Component::array_index& array_index, Args&&... args) {\n  runner->template simple_action<Component, Action>(\n      array_index, std::forward<Args>(args)...);\n}\n\n/// Queues the simple action `Action` on the `array_index`th element of the\n/// parallel component `Component`.\ntemplate <typename Component, typename Action, typename Metavariables,\n          typename... Args>\nvoid queue_simple_action(\n    const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n    const typename Component::array_index& array_index, Args&&... args) {\n  runner->template queue_simple_action<Component, Action>(\n      array_index, std::forward<Args>(args)...);\n}\n\n/// Runs the simple action `Action` on the `array_index`th element of the\n/// parallel component `Component`.\ntemplate <typename Component, typename Action, typename Metavariables,\n          typename... Args>\nvoid threaded_action(\n    const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n    const typename Component::array_index& array_index, Args&&... args) {\n  runner->template threaded_action<Component, Action>(\n      array_index, std::forward<Args>(args)...);\n}\n\n/// Runs the next queued simple action on the `array_index`th element of\n/// the parallel component `Component`.\ntemplate <typename Component, typename Metavariables>\nvoid invoke_queued_simple_action(\n    const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n    const typename Component::array_index& array_index) {\n  runner->template invoke_queued_simple_action<Component>(array_index);\n}\n\n/// Returns `true` if there are no simple actions in the queue.\ntemplate <typename Component, typename Metavariables>\nbool is_simple_action_queue_empty(\n    const MockRuntimeSystem<Metavariables>& runner,\n    const typename Component::array_index& array_index) {\n  return runner.template is_simple_action_queue_empty<Component>(array_index);\n}\n\n/// Returns the number of simple actions in the queue.\ntemplate <typename Component, typename Metavariables>\nsize_t number_of_queued_simple_actions(\n    const MockRuntimeSystem<Metavariables>& runner,\n    const typename Component::array_index& array_index) {\n  return runner.template number_of_queued_simple_actions<Component>(\n      array_index);\n}\n\n/// Runs the next queued threaded action on the `array_index`th element of\n/// the parallel component `Component`.\ntemplate <typename Component, typename Metavariables>\nvoid invoke_queued_threaded_action(\n    const gsl::not_null<MockRuntimeSystem<Metavariables>*> runner,\n    const typename Component::array_index& array_index) {\n  runner->template invoke_queued_threaded_action<Component>(array_index);\n}\n\n/// Returns `true` if there are no threaded actions in the queue.\ntemplate <typename Component, typename Metavariables>\nbool is_threaded_action_queue_empty(\n    const MockRuntimeSystem<Metavariables>& runner,\n    const typename Component::array_index& array_index) {\n  return runner.template is_threaded_action_queue_empty<Component>(array_index);\n}\n\n/// Returns the number of threaded actions in the queue.\ntemplate <typename Component, typename Metavariables>\nsize_t number_of_queued_threaded_actions(\n    const MockRuntimeSystem<Metavariables>& runner,\n    const typename Component::array_index& array_index) {\n  return runner.template number_of_queued_threaded_actions<Component>(\n      array_index);\n}\n\n/// Returns whether or not the `Component` with index `array_index` has been\n/// terminated.\ntemplate <typename Component, typename Metavariables>\nbool get_terminate(const MockRuntimeSystem<Metavariables>& runner,\n                   const typename Component::array_index& array_index) {\n  return runner.template mock_distributed_objects<Component>()\n      .at(array_index)\n      .get_terminate();\n}\n\n/// Returns the GlobalCache of `Component` with index `array_index`.\ntemplate <typename Component, typename Metavariables, typename ArrayIndex>\nParallel::GlobalCache<Metavariables>& cache(\n    MockRuntimeSystem<Metavariables>& runner, const ArrayIndex& array_index) {\n  return runner.template mock_distributed_objects<Component>()\n      .at(array_index)\n      .cache();\n}\n\n/// Returns a vector of all the indices of the Components in the\n/// ComponentList that have queued simple actions, for a particular\n/// array_index.\ntemplate <typename ComponentList, typename MockRuntimeSystem,\n          typename ArrayIndex>\nstd::vector<size_t> indices_of_components_with_queued_simple_actions(\n    const gsl::not_null<MockRuntimeSystem*> runner,\n    const ArrayIndex& array_index) {\n  std::vector<size_t> result{};\n  size_t i = 0;\n  tmpl::for_each<ComponentList>([&](auto tag) {\n    using Tag = typename decltype(tag)::type;\n    if (not runner->template is_simple_action_queue_empty<Tag>(array_index)) {\n      result.push_back(i);\n    }\n    ++i;\n  });\n  return result;\n}\n\n/// \\cond\nnamespace detail {\ntemplate<typename Component>\nstruct ArrayIndicesTag {\n  using type = std::vector<typename Component::array_index>;\n};\n\ntemplate <typename ComponentList>\nusing array_indices_tags =\n    tmpl::transform<ComponentList, tmpl::bind<ArrayIndicesTag, tmpl::_1>>;\n\ntemplate <typename ComponentList>\nusing array_indices_for_each_component =\n    tuples::tagged_tuple_from_typelist<array_indices_tags<ComponentList>>;\n}  // namespace detail\n/// \\endcond\n\n/// Returns a vector of array_indices for each Component.  The vector\n/// is filled with only those array_indices for which there are queued\n/// simple actions.\ntemplate <typename ComponentList, typename MockRuntimeSystem>\nauto array_indices_with_queued_simple_actions(\n    const gsl::not_null<MockRuntimeSystem*> runner)\n    -> detail::array_indices_for_each_component<ComponentList> {\n  detail::array_indices_for_each_component<ComponentList> result;\n  tmpl::for_each<ComponentList>([&](auto component) {\n    using Component = typename decltype(component)::type;\n    using array_indices_tag = detail::ArrayIndicesTag<Component>;\n    auto& result_this_component = tuples::get<array_indices_tag>(result);\n    for (auto& [index, mock_distributed_object] :\n         runner->template mock_distributed_objects<Component>()) {\n      if (not mock_distributed_object.is_simple_action_queue_empty()) {\n        result_this_component.push_back(index);\n      }\n    }\n  });\n  return result;\n}\n\n/// Given the output of `array_indices_with_queued_simple_actions`, returns\n/// the total number of array indices that have queued simple actions.\ntemplate <typename ComponentList>\nsize_t number_of_elements_with_queued_simple_actions(\n    const detail::array_indices_for_each_component<ComponentList>&\n        array_indices) {\n  size_t num_queued_actions = 0;\n  tmpl::for_each<ComponentList>([&](auto component) {\n    using Component = typename decltype(component)::type;\n    using array_indices_tag = detail::ArrayIndicesTag<Component>;\n    num_queued_actions += tuples::get<array_indices_tag>(array_indices).size();\n  });\n  return num_queued_actions;\n}\n\n/// Invokes the next queued action on a random Component on a random\n/// array_index of that component.  `array_indices` is the thing returned\n/// by `array_indices_with_queued_simple_actions`\ntemplate <typename ComponentList, typename MockRuntimeSystem,\n          typename Generator>\nvoid invoke_random_queued_simple_action(\n    const gsl::not_null<MockRuntimeSystem*> runner,\n    const gsl::not_null<Generator*> generator,\n    const detail::array_indices_for_each_component<ComponentList>&\n        array_indices) {\n  // Choose one queued action at random.\n  const size_t num_queued_actions =\n      number_of_elements_with_queued_simple_actions<ComponentList>(\n          array_indices);\n  std::uniform_int_distribution<size_t> ran(0, num_queued_actions - 1);\n  const size_t index_of_action_to_invoke = ran(*generator);\n\n  // Invoke the chosen queued action.\n  size_t queued_action_count = 0;\n  tmpl::for_each<ComponentList>([&runner, &array_indices,\n                                 &index_of_action_to_invoke,\n                                 &queued_action_count](auto component) {\n    using Component = typename decltype(component)::type;\n    using array_indices_tag = detail::ArrayIndicesTag<Component>;\n    const size_t num_queued_actions_this_comp =\n        tuples::get<array_indices_tag>(array_indices).size();\n    if (index_of_action_to_invoke >= queued_action_count and\n        index_of_action_to_invoke <\n            queued_action_count + num_queued_actions_this_comp) {\n      runner->template invoke_queued_simple_action<Component>(\n          tuples::get<array_indices_tag>(array_indices)\n              .at(index_of_action_to_invoke - queued_action_count));\n    }\n    queued_action_count += num_queued_actions_this_comp;\n  });\n}\n}  // namespace ActionTesting\n"
  },
  {
    "path": "tests/Unit/Framework/Pypp.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines function pypp::call<R,Args...>\n\n#pragma once\n\n#include <Python.h>\n#include <array>\n#include <boost/range/combine.hpp>\n#include <cstddef>\n#include <optional>\n#include <stdexcept>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/PyppFundamentals.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsStdArray.hpp\"\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Contains all functions for calling python from C++\n */\nnamespace pypp {\nnamespace detail {\ntemplate <typename T, typename ConversionClass>\nstruct PackedTypeMatches\n    : std::is_same<typename ConversionClass::packed_container, T> {};\n\ntemplate <typename T, typename ConversionClassList, typename = std::nullptr_t>\nstruct ContainerPackAndUnpack;\n\ntemplate <typename T, typename ConversionClassList>\nstruct ContainerPackAndUnpack<\n    T, ConversionClassList,\n    Requires<tmpl::found<ConversionClassList,\n                         PackedTypeMatches<tmpl::pin<T>, tmpl::_1>>::value>>\n    : tmpl::front<tmpl::find<ConversionClassList,\n                             PackedTypeMatches<tmpl::pin<T>, tmpl::_1>>> {\n  static_assert(\n      tmpl::count_if<ConversionClassList,\n                     PackedTypeMatches<tmpl::pin<T>, tmpl::_1>>::value == 1,\n      \"Found more than one conversion class for the type. See the first \"\n      \"template parameter of ContainerPackAndUnpack for the type being \"\n      \"converted, and the second template parameter for a list of all the \"\n      \"additional conversion classes.\");\n};\n\ntemplate <typename ConversionClassList>\nstruct ContainerPackAndUnpack<DataVector, ConversionClassList, std::nullptr_t> {\n  using unpacked_container = double;\n  using packed_container = DataVector;\n  using packed_type = packed_container;\n\n  static inline unpacked_container unpack(const packed_container& packed,\n                                          const size_t grid_point_index) {\n    ASSERT(grid_point_index < packed.size(),\n           \"Trying to slice DataVector of size \" << packed.size()\n                                                 << \" with grid_point_index \"\n                                                 << grid_point_index);\n    return packed[grid_point_index];\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container& unpacked,\n                          const size_t grid_point_index) {\n    (*packed)[grid_point_index] = unpacked;\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    return packed.size();\n  }\n};\n\ntemplate <typename ConversionClassList>\nstruct ContainerPackAndUnpack<ComplexDataVector, ConversionClassList,\n                              std::nullptr_t> {\n  using unpacked_container = std::complex<double>;\n  using packed_container = ComplexDataVector;\n  using packed_type = packed_container;\n\n  static inline unpacked_container unpack(const packed_type& packed,\n                                          const size_t grid_point_index) {\n    ASSERT(grid_point_index < packed.size(),\n           \"Trying to slice ComplexDataVector of size \"\n               << packed.size() << \" with grid_point_index \"\n               << grid_point_index);\n    return packed[grid_point_index];\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container& unpacked,\n                          const size_t grid_point_index) {\n    (*packed)[grid_point_index] = unpacked;\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    return packed.size();\n  }\n};\n\ntemplate <typename T, typename... Ts, typename ConversionClassList>\nstruct ContainerPackAndUnpack<Tensor<T, Ts...>, ConversionClassList,\n                              std::nullptr_t> {\n  using unpacked_container =\n      Tensor<typename ContainerPackAndUnpack<\n                 T, ConversionClassList>::unpacked_container,\n             Ts...>;\n  using packed_container = Tensor<\n      typename ContainerPackAndUnpack<T, ConversionClassList>::packed_container,\n      Ts...>;\n  using packed_type =\n      typename ContainerPackAndUnpack<T, ConversionClassList>::packed_type;\n\n  static inline unpacked_container unpack(const packed_container& packed,\n                                          const size_t grid_point_index) {\n    unpacked_container unpacked{};\n    for (size_t storage_index = 0; storage_index < unpacked.size();\n         ++storage_index) {\n      unpacked[storage_index] =\n          ContainerPackAndUnpack<T, ConversionClassList>::unpack(\n              packed[storage_index], grid_point_index);\n    }\n    return unpacked;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container& unpacked,\n                          const size_t grid_point_index) {\n    for (size_t storage_index = 0; storage_index < unpacked.size();\n         ++storage_index) {\n      ContainerPackAndUnpack<T, ConversionClassList>::pack(\n          make_not_null(&(*packed)[storage_index]), unpacked[storage_index],\n          grid_point_index);\n    }\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    return ContainerPackAndUnpack<T, ConversionClassList>::get_size(packed[0]);\n  }\n};\n\ntemplate <typename T, typename... Symm, typename... Indices,\n          typename ConversionClassList>\nstruct ContainerPackAndUnpack<std::tuple<Tensor<T, Symm, Indices>...>,\n                              ConversionClassList, std::nullptr_t> {\n  using unpacked_container =\n      std::tuple<Tensor<typename ContainerPackAndUnpack<\n                            T, ConversionClassList>::unpacked_container,\n                        Symm, Indices>...>;\n  using packed_container = std::tuple<Tensor<\n      typename ContainerPackAndUnpack<T, ConversionClassList>::packed_container,\n      Symm, Indices>...>;\n  using packed_type =\n      typename ContainerPackAndUnpack<T, ConversionClassList>::packed_type;\n\n  static inline unpacked_container unpack(const packed_container& packed,\n                                          const size_t grid_point_index) {\n    return unpack_impl(packed, grid_point_index,\n                       std::make_index_sequence<sizeof...(Indices)>{});\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container& unpacked,\n                          const size_t grid_point_index) {\n    pack_impl(packed, unpacked, grid_point_index,\n              std::make_index_sequence<sizeof...(Indices)>{});\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    return ContainerPackAndUnpack<T, ConversionClassList>::get_size(\n        std::get<0>(packed)[0]);\n  }\n\n private:\n  template <size_t... Is>\n  static unpacked_container unpack_impl(const packed_container& packed,\n                                        const size_t grid_point_index,\n                                        std::index_sequence<Is...> /*meta*/) {\n    unpacked_container unpacked{};\n    EXPAND_PACK_LEFT_TO_RIGHT([&unpacked, &packed, &grid_point_index]() {\n      std::get<Is>(unpacked) = ContainerPackAndUnpack<\n          Tensor<T, Symm, Indices>,\n          ConversionClassList>::unpack(std::get<Is>(packed), grid_point_index);\n    }());\n    return unpacked;\n  }\n\n  template <size_t... Is>\n  static void pack_impl(const gsl::not_null<packed_container*> packed,\n                        const unpacked_container& unpacked,\n                        const size_t grid_point_index,\n                        std::index_sequence<Is...> /*meta*/) {\n    EXPAND_PACK_LEFT_TO_RIGHT([&unpacked, &packed, &grid_point_index]() {\n      ContainerPackAndUnpack<Tensor<T, Symm, Indices>, ConversionClassList>::\n          pack(make_not_null(&std::get<Is>(*packed)), std::get<Is>(unpacked),\n               grid_point_index);\n    }());\n  }\n};\n\ntemplate <typename... Tags, typename ConversionClassList>\nstruct ContainerPackAndUnpack<tuples::TaggedTuple<Tags...>, ConversionClassList,\n                              std::nullptr_t> {\n private:\n  using first_tag = tmpl::front<tmpl::list<Tags...>>;\n\n public:\n  template <typename Tag>\n  struct UnpackedTag {\n    static std::string name() { return db::tag_name<Tag>(); }\n    using type = typename ContainerPackAndUnpack<\n        typename Tag::type, ConversionClassList>::unpacked_container;\n  };\n  using unpacked_container = tuples::TaggedTuple<UnpackedTag<Tags>...>;\n  using packed_container = tuples::TaggedTuple<Tags...>;\n  // This assumes the first member of the TaggedTuple sets the packed\n  // type. I'm (Nils Deppe) not sure how else to handle this right now.\n  using packed_type =\n      typename ContainerPackAndUnpack<typename first_tag::type,\n                                      ConversionClassList>::packed_type;\n\n  static inline unpacked_container unpack(const packed_container& packed,\n                                          const size_t grid_point_index) {\n    return {ContainerPackAndUnpack<typename Tags::type, ConversionClassList>::\n                unpack(tuples::get<Tags>(packed), grid_point_index)...};\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container& unpacked,\n                          const size_t grid_point_index) {\n    EXPAND_PACK_LEFT_TO_RIGHT([grid_point_index, &packed, &unpacked]() {\n      ContainerPackAndUnpack<typename Tags::type, ConversionClassList>::pack(\n          make_not_null(&tuples::get<Tags>(*packed)),\n          tuples::get<UnpackedTag<Tags>>(unpacked), grid_point_index);\n    }());\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    return ContainerPackAndUnpack<\n        typename first_tag::type,\n        ConversionClassList>::get_size(tuples::get<first_tag>(packed));\n  }\n};\n\ntemplate <typename T, size_t Size, typename ConversionClassList>\nstruct ContainerPackAndUnpack<std::array<T, Size>, ConversionClassList,\n                              std::nullptr_t> {\n  using unpacked_container =\n      std::array<typename ContainerPackAndUnpack<\n                     T, ConversionClassList>::unpacked_container,\n                 Size>;\n  using packed_container = std::array<\n      typename ContainerPackAndUnpack<T, ConversionClassList>::packed_container,\n      Size>;\n  using packed_type =\n      typename ContainerPackAndUnpack<T, ConversionClassList>::packed_type;\n\n  static inline unpacked_container unpack(const packed_container& packed,\n                                          const size_t grid_point_index) {\n    unpacked_container unpacked{};\n    for (size_t i = 0; i < Size; ++i) {\n      gsl::at(unpacked, i) =\n          ContainerPackAndUnpack<T, ConversionClassList>::unpack(\n              gsl::at(packed, i), grid_point_index);\n    }\n    return unpacked;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container& unpacked,\n                          const size_t grid_point_index) {\n    for (size_t i = 0; i < unpacked.size(); ++i) {\n      ContainerPackAndUnpack<T, ConversionClassList>::pack(\n          make_not_null(&gsl::at(*packed, i)), gsl::at(unpacked, i),\n          grid_point_index);\n    }\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    return ContainerPackAndUnpack<T, ConversionClassList>::get_size(packed[0]);\n  }\n};\n\n// scalars are the only sort of spin-weighted type we support.\ntemplate <typename ValueType, int Spin, typename ConversionClassList>\nstruct ContainerPackAndUnpack<Scalar<SpinWeighted<ValueType, Spin>>,\n                              ConversionClassList, std::nullptr_t> {\n  using unpacked_container = Scalar<typename ContainerPackAndUnpack<\n      ValueType, ConversionClassList>::unpacked_container>;\n  using packed_container =\n      Scalar<SpinWeighted<typename ContainerPackAndUnpack<\n                              ValueType, ConversionClassList>::packed_container,\n                          Spin>>;\n  using packed_type =\n      typename ContainerPackAndUnpack<ValueType,\n                                      ConversionClassList>::packed_type;\n\n  static inline unpacked_container unpack(const packed_container& packed,\n                                          const size_t grid_point_index) {\n    unpacked_container unpacked{};\n    get(unpacked) =\n        ContainerPackAndUnpack<ValueType, ConversionClassList>::unpack(\n            get(packed).data(), grid_point_index);\n    return unpacked;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container& unpacked,\n                          const size_t grid_point_index) {\n    ContainerPackAndUnpack<ValueType, ConversionClassList>::pack(\n        make_not_null(&(get(*packed).data())), get(unpacked), grid_point_index);\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    return ContainerPackAndUnpack<ValueType, ConversionClassList>::get_size(\n        packed[0].data());\n  }\n};\n\ntemplate <typename T, typename ConversionClassList>\nstruct ContainerPackAndUnpack<std::optional<T>, ConversionClassList,\n                              std::nullptr_t> {\n  using unpacked_container = std::optional<typename ContainerPackAndUnpack<\n      T, ConversionClassList>::unpacked_container>;\n  using packed_container = std::optional<typename ContainerPackAndUnpack<\n      T, ConversionClassList>::packed_container>;\n  using packed_type =\n      typename ContainerPackAndUnpack<T, ConversionClassList>::packed_type;\n\n  static inline unpacked_container unpack(const packed_container& packed,\n                                          const size_t grid_point_index) {\n    if (static_cast<bool>(packed)) {\n      return unpacked_container{\n          ContainerPackAndUnpack<T, ConversionClassList>::unpack(\n              *packed, grid_point_index)};\n    }\n    return unpacked_container{std::nullopt};\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container& unpacked,\n                          const size_t grid_point_index) {\n    if ((std::is_same_v<packed_type, DataVector> or\n         std::is_same_v<\n             packed_type,\n             ComplexDataVector>)and UNLIKELY(not static_cast<bool>(unpacked))) {\n      throw std::runtime_error(\n          \"Returned type is None in one element of the std::optional's \"\n          \"packed type (DataVector or ComplexDataVector). We can't support \"\n          \"this because we can't just make one element of the packed type be \"\n          \"an invalid optional.\");\n    }\n    ContainerPackAndUnpack<T, ConversionClassList>::pack(\n        make_not_null(&*packed), unpacked, grid_point_index);\n  }\n\n  static inline size_t get_size(const packed_container& packed) {\n    if (static_cast<bool>(packed)) {\n      return ContainerPackAndUnpack<T, ConversionClassList>::get_size(*packed);\n    }\n    return 1;\n  }\n};\n\ntemplate <typename T, typename ConversionClassList>\nstruct ContainerPackAndUnpack<\n    T, ConversionClassList,\n    Requires<std::is_floating_point_v<T> or std::is_integral_v<T> or\n             std::is_same_v<T, std::string>>> {\n  using unpacked_container = T;\n  using packed_container = T;\n  using packed_type = packed_container;\n\n  static inline unpacked_container unpack(const packed_container t,\n                                          const size_t /*grid_point_index*/) {\n    return t;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed,\n                          const unpacked_container unpacked,\n                          const size_t /*grid_point_index*/) {\n    *packed = unpacked;\n  }\n\n  static inline size_t get_size(const packed_container& /*packed*/) {\n    return 1;\n  }\n};\n\ntemplate <typename ReturnType>\nReturnType call_work(PyObject* python_module, PyObject* func, PyObject* args) {\n  PyObject* value = PyObject_CallObject(func, args);\n  Py_DECREF(args);  // NOLINT\n  if (value == nullptr) {\n    Py_DECREF(func);           // NOLINT\n    Py_DECREF(python_module);  // NOLINT\n    PyErr_Print();\n    throw std::runtime_error{\"Function returned null\"};\n  }\n\n  auto ret = from_py_object<ReturnType>(value);\n  Py_DECREF(value);  // NOLINT\n  return ret;\n}\n\ntemplate <typename ReturnType, typename ConversionClassList,\n          typename = std::nullptr_t>\nstruct CallImpl {\n  template <typename... Args>\n  static ReturnType call(const std::string& module_name,\n                         const std::string& function_name, const Args&... t) {\n    PyObject* python_module = PyImport_ImportModule(module_name.c_str());\n    if (python_module == nullptr) {\n      PyErr_Print();\n      throw std::runtime_error{std::string(\"Could not find python module.\\n\") +\n                               module_name};\n    }\n    PyObject* func =\n        PyObject_GetAttrString(python_module, function_name.c_str());\n    if (func == nullptr or not PyCallable_Check(func)) {\n      if (PyErr_Occurred()) {\n        PyErr_Print();\n      }\n      throw std::runtime_error{\"Could not find python function in module.\\n\"};\n    }\n    PyObject* args = pypp::make_py_tuple(t...);\n    auto ret = call_work<ReturnType>(python_module, func, args);\n    Py_DECREF(func);           // NOLINT\n    Py_DECREF(python_module);  // NOLINT\n    return ret;\n  }\n};\n\ntemplate <typename T, typename ConversionClassList>\nstruct is_tuple_of_tensors_of_vectors : std::false_type {\n  static T make_zero(const size_t npts) {\n    return make_with_value<typename ContainerPackAndUnpack<\n        T, ConversionClassList>::packed_container>(npts, 0.0);\n  }\n};\n\ntemplate <typename T, typename... Symms, typename... Indices,\n          typename ConversionClassList>\nstruct is_tuple_of_tensors_of_vectors<std::tuple<Tensor<T, Symms, Indices>...>,\n                                      ConversionClassList>\n    : std::bool_constant<std::is_same_v<DataVector, T> or\n                         std::is_same_v<ComplexDataVector, T>> {\n  static std::tuple<Tensor<T, Symms, Indices>...> make_zero(const size_t npts) {\n    return {make_with_value<typename ContainerPackAndUnpack<\n        Tensor<T, Symms, Indices>, ConversionClassList>::packed_container>(\n        npts, 0.0)...};\n  }\n};\n\ntemplate <typename... Tags, typename ConversionClassList>\nstruct is_tuple_of_tensors_of_vectors<tuples::TaggedTuple<Tags...>,\n                                      ConversionClassList>\n    : std::bool_constant<\n          (std::is_same_v<typename ContainerPackAndUnpack<\n                              tuples::TaggedTuple<Tags...>,\n                              ConversionClassList>::packed_type,\n                          DataVector> or\n           std::is_same_v<typename ContainerPackAndUnpack<\n                              tuples::TaggedTuple<Tags...>,\n                              ConversionClassList>::packed_type,\n                          ComplexDataVector>)and tt::\n              is_a_v<Tensor, typename tmpl::front<tmpl::list<Tags...>>::type>> {\n  static tuples::TaggedTuple<Tags...> make_zero(const size_t npts) {\n    return {[npts]() {\n      using tag_type = typename Tags::type;\n      if constexpr (std::is_same_v<\n                        typename ContainerPackAndUnpack<\n                            tag_type, ConversionClassList>::packed_type,\n                        DataVector> or\n                    std::is_same_v<\n                        typename ContainerPackAndUnpack<\n                            tag_type, ConversionClassList>::packed_type,\n                        ComplexDataVector>) {\n        return make_with_value<typename ContainerPackAndUnpack<\n            tag_type, ConversionClassList>::packed_container>(npts, 0.0);\n      } else {\n        (void)npts;\n        return tag_type{};\n      }\n    }()...};\n  }\n};\n\ntemplate <typename ReturnType, typename ConversionClassList>\nstruct CallImpl<\n    ReturnType, ConversionClassList,\n    Requires<\n        ((tt::is_a_v<Tensor, ReturnType> or tt::is_std_array_v<ReturnType>)and(\n            std::is_same_v<typename ContainerPackAndUnpack<\n                               ReturnType, ConversionClassList>::packed_type,\n                           DataVector> or\n            std::is_same_v<typename ContainerPackAndUnpack<\n                               ReturnType, ConversionClassList>::packed_type,\n                           ComplexDataVector>)) or\n        is_tuple_of_tensors_of_vectors<ReturnType,\n                                       ConversionClassList>::value>> {\n  template <typename... Args>\n  static ReturnType call(const std::string& module_name,\n                         const std::string& function_name, const Args&... t) {\n    static_assert(sizeof...(Args) > 0,\n                  \"Call to python which returns a Tensor of DataVectors must \"\n                  \"pass at least one argument\");\n\n    PyObject* python_module = PyImport_ImportModule(module_name.c_str());\n    if (python_module == nullptr) {\n      PyErr_Print();\n      throw std::runtime_error{std::string(\"Could not find python module.\\n\") +\n                               module_name};\n    }\n    PyObject* func =\n        PyObject_GetAttrString(python_module, function_name.c_str());\n    if (func == nullptr or not PyCallable_Check(func)) {\n      if (PyErr_Occurred()) {\n        PyErr_Print();\n      }\n      throw std::runtime_error{\"Could not find python function in module.\\n\"};\n    }\n\n    const std::array<size_t, sizeof...(Args)> arg_sizes{\n        {ContainerPackAndUnpack<Args, ConversionClassList>::get_size(t)...}};\n    const size_t npts = *std::max_element(arg_sizes.begin(), arg_sizes.end());\n    for (size_t i = 0; i < arg_sizes.size(); ++i) {\n      if (gsl::at(arg_sizes, i) != 1 and gsl::at(arg_sizes, i) != npts) {\n        ERROR(\n            \"Each argument must return size 1 or \"\n            << npts\n            << \" (the number of points in the DataVector), but argument number \"\n            << i << \" has size \" << gsl::at(arg_sizes, i));\n      }\n    }\n\n    auto return_container =\n        is_tuple_of_tensors_of_vectors<ReturnType,\n                                       ConversionClassList>::make_zero(npts);\n\n    for (size_t s = 0; s < npts; ++s) {\n      PyObject* args = pypp::make_py_tuple(\n          ContainerPackAndUnpack<Args, ConversionClassList>::unpack(t, s)...);\n      auto ret = call_work<typename ContainerPackAndUnpack<\n          ReturnType, ConversionClassList>::unpacked_container>(python_module,\n                                                                func, args);\n      ContainerPackAndUnpack<ReturnType, ConversionClassList>::pack(\n          make_not_null(&return_container), ret, s);\n    }\n    Py_DECREF(func);           // NOLINT\n    Py_DECREF(python_module);  // NOLINT\n    return return_container;\n  }\n};\n}  // namespace detail\n\n/// Calls a Python function from a module/file with given parameters\n///\n/// \\param module_name name of module the function is in\n/// \\param function_name name of Python function in module\n/// \\param t the arguments to be passed to the Python function\n/// \\return the object returned by the Python function converted to a C++ type\n///\n/// Custom conversion from containers to basic types that can be converted to\n/// python objects via the `pypp::ToPyObject` class can be added by passing a\n/// typelist of conversion class as the second template parameter to\n/// `pypp::call`. This is generally used for converting classes like\n/// `Tensor<DataVector, ...>` to a `Tensor<double, ...>` to support using\n/// `numpy.einsum` in the python code. However, this can also be used to convert\n/// a complicated class such as an equation of state to a bunch of numbers that\n/// the python code for the particular test can use to compute the required data\n/// without having to fully implement python bindings. The conversion classes\n/// must have the following:\n/// - a type alias `unpacked_container` that is the type at a single grid point.\n/// - a type alias `packed_container` the type used when converting the result\n///   from the python call back into the C++ code.\n/// - a type alias `packed_type` which corresponds to the packed type that the\n///   high-level container holds. For example, a `Scalar<DataVector>` would have\n///   `packed_type` being `DataVector`, a `std::vector<Scalar<DataVector>>`\n///   would have `packed_type` being `DataVector`, and a\n///   `std::vector<Scalar<double>>` would have `packed_type` being `double`.\n/// - an `unpack` function that takes as its arguments the `packed_container`\n///   and the `grid_point_index`, and returns an `unpacked_container` object\n/// - a `pack` function that takes as its arguments a\n///   `gsl::not_null<packed_container*>`, an `const unpacked_container&`, and a\n///   `size_t` corresponding to which grid point to pack\n/// - a `get_size` function that takes as its only argument an object of\n///   `packed_container` and returns the number of elements in the\n///   `packed_type`\n///\n/// Examples of conversion classes can be found in the specializations of\n/// `ContainerPackAndUnpack` in the `Pypp.hpp` file. Below is an example of a\n/// conversion class that takes a class (`ClassForConversionTest`) and returns\n/// the `a_` member variable.\n///\n/// \\snippet Test_Pypp.cpp convert_arbitrary_a\n///\n/// Here is the call to `pypp::call`:\n///\n/// \\snippet Test_Pypp.cpp convert_arbitrary_a_call\n///\n/// A conversion class for retrieving the member variables `b_` can also be\n/// written as follows:\n///\n/// \\snippet Test_Pypp.cpp convert_arbitrary_b\n///\n/// \\note In order to setup the python interpreter and add the local directories\n/// to the path, a SetupLocalPythonEnvironment object needs to be constructed\n/// in the local scope.\n///\n/// \\example\n/// The following example calls the function `test_numeric` from the module\n/// `pypp_py_tests` which multiplies two integers.\n/// \\snippet Test_Pypp.cpp pypp_int_test\n/// Alternatively, this examples calls `test_vector` from `pypp_py_tests` which\n/// converts two vectors to python lists and multiplies them pointwise.\n/// \\snippet Test_Pypp.cpp pypp_vector_test\n///\n/// Pypp can also be used to take a function that performs manipulations of\n/// NumPy arrays and apply it to either a Tensor of doubles or a Tensor of\n/// DataVectors. This is useful for testing functions which act on Tensors\n/// pointwise. For example, let's say we wanted to call the NumPy function which\n/// performs \\f$ v_i = A B^a C_{ia} + D^{ab} E_{iab} \\f$, which is implemented\n/// in python as\n///\n/// \\code{.py} def test_einsum(scalar, t_A, t_ia, t_AA, t_iaa):\n///    return scalar * np.einsum(\"a,ia->i\", t_A, t_ia) +\n///           np.einsum(\"ab, iab->i\", t_AA, t_iaa)\n/// \\endcode\n///\n/// where \\f$ v_i \\f$ is the return tensor and\n/// \\f$ A, B^a, C_{ia},D^{ab}, E_{iab} \\f$ are the input tensors respectively.\n/// We call this function through C++ as:\n/// \\snippet Test_Pypp.cpp einsum_example\n/// for type `T` either a `double` or `DataVector`.\n///\n/// Pypp will also support testing of functions which return and operate on\n/// `std::array`s of `DataVectors`s. To return a `std::array` of DataVectors,\n/// the python function should return a python list of doubles.\n///\n/// \\note In order to return a\n/// `Tensor<DataVector...>` from `pypp::call`, at least one\n/// `Tensor<DataVector...>` must be taken as an argument, as the size of the\n/// returned tensor needs to be deduced.\ntemplate <typename ReturnType, typename ConversionClassList = tmpl::list<>,\n          typename... Args>\nReturnType call(const std::string& module_name,\n                const std::string& function_name, const Args&... t) {\n  const ScopedFpeState disable_fpes(false);\n  return detail::CallImpl<ReturnType, ConversionClassList>::call(\n      module_name, function_name, t...);\n}\n}  // namespace pypp\n"
  },
  {
    "path": "tests/Unit/Framework/PyppFundamentals.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines classes for converting to and from Python objects\n\n#pragma once\n\n#include <Python.h>\n#include <array>\n#include <cstddef>\n#include <initializer_list>\n#include <optional>\n#include <stdexcept>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n// These macros are required so that the NumPy API will work when used in\n// multiple cpp files.\n#ifndef PY_ARRAY_UNIQUE_SYMBOL\n#define PY_ARRAY_UNIQUE_SYMBOL SPECTRE_PY_API\n#endif\n#define NO_IMPORT_ARRAY\n#define NPY_API_SYMBOL_ATTRIBUTE\n// Disable compiler warnings. NumPy ensures API compatibility among different\n// 1.x versions, as features become deprecated in Numpy 1.x will still function\n// but cause a compiler warning\n#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n#include <numpy/arrayobject.h>\n\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsStdArray.hpp\"\n\nnamespace pypp {\n/// \\cond\nusing None = void*;\n\ntemplate <typename T, typename = std::nullptr_t>\nstruct ToPyObject;\n\ntemplate <typename T>\nPyObject* to_py_object(const T& t) {\n  PyObject* value = ToPyObject<T>::convert(t);\n  if (value == nullptr) {\n    throw std::runtime_error{\"Failed to convert argument.\"};\n  }\n  return value;\n}\n\ntemplate <typename T, typename = std::nullptr_t>\nstruct FromPyObject;\n\ntemplate <typename T>\nT from_py_object(PyObject* t) {\n  return FromPyObject<T>::convert(t);\n}\n/// \\endcond\n\n/// Create a python tuple from Args\n///\n/// \\tparam Args the types of the arguments to be put in the tuple (deducible)\n/// \\param t the arguments to put into the tuple\n/// \\return PyObject* containing a Python tuple\ntemplate <typename... Args>\nPyObject* make_py_tuple(const Args&... t) {\n  PyObject* py_tuple = PyTuple_New(sizeof...(Args));\n  int entry = 0;\n  const auto add_entry = [&entry, &py_tuple](const auto& arg) {\n    PyObject* value = to_py_object(arg);\n    PyTuple_SetItem(py_tuple, entry, value);\n    entry++;\n    return '0';\n  };\n  (void)add_entry;  // GCC warns that add_entry is unused\n  (void)std::initializer_list<char>{add_entry(t)...};\n  return py_tuple;\n}\n\n///\\cond\ntemplate <>\nstruct ToPyObject<void, std::nullptr_t> {\n  static PyObject* convert() {\n    Py_RETURN_NONE;\n  }\n};\n\ntemplate <typename T>\nstruct ToPyObject<\n    T, Requires<std::is_same_v<typename std::decay<T>::type, std::string>>> {\n  static PyObject* convert(const T& t) {\n#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7\n    return PyString_FromString(t.c_str());\n#elif PY_MAJOR_VERSION == 3\n    return PyUnicode_FromString(t.c_str());\n#else\n    static_assert(false, \"Only works on Python 2.7 and 3.x\")\n#endif\n  }\n};\n\ntemplate <typename T>\nstruct ToPyObject<\n    T, Requires<std::is_same_v<typename std::decay<T>::type, bool>>> {\n  static PyObject* convert(const T& t) {\n    return PyBool_FromLong(static_cast<long>(t));\n  }\n};\n\ntemplate <typename T>\nstruct ToPyObject<\n    T, Requires<std::is_same_v<typename std::decay<T>::type, int> or\n                std::is_same_v<typename std::decay<T>::type, short>>> {\n  static PyObject* convert(const T& t) {\n#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7\n    return PyInt_FromLong(t);\n#elif PY_MAJOR_VERSION == 3\n    return PyLong_FromLong(t);\n#else\n    static_assert(false, \"Only works on Python 2.7 and 3.x\")\n#endif\n  }\n};\n\ntemplate <typename T>\nstruct ToPyObject<\n    T, Requires<std::is_same_v<typename std::decay<T>::type, long>>> {\n  static PyObject* convert(const T& t) { return PyLong_FromLong(t); }\n};\n\ntemplate <typename T>\nstruct ToPyObject<\n    T, Requires<std::is_same_v<typename std::decay<T>::type, unsigned long> or\n                std::is_same_v<typename std::decay<T>::type, unsigned int>>> {\n  static PyObject* convert(const T& t) { return PyLong_FromUnsignedLong(t); }\n};\n\ntemplate <typename T>\nstruct ToPyObject<\n    T,\n    Requires<std::is_same_v<typename std::decay<T>::type, size_t> and\n             not std::is_same_v<typename std::decay<T>::type, unsigned long> and\n             not std::is_same_v<typename std::decay<T>::type, unsigned int>>> {\n  static PyObject* convert(const T& t) { return PyLong_FromSize_t(t); }\n};\n\ntemplate <typename T>\nstruct ToPyObject<\n    T, Requires<std::is_floating_point<typename std::decay<T>::type>::value>> {\n  static PyObject* convert(const T& t) { return PyFloat_FromDouble(t); }\n};\n\ntemplate <typename T, typename A>\nstruct ToPyObject<std::vector<T, A>, std::nullptr_t> {\n  static PyObject* convert(const std::vector<T, A>& t) {\n    PyObject* list = PyList_New(static_cast<long>(t.size()));\n    if (list == nullptr) {\n      throw std::runtime_error{\"Failed to convert argument.\"};\n    }\n    for (size_t i = 0; i < t.size(); ++i) {\n      if (-1 ==\n          PyList_SetItem(list, static_cast<long>(i), to_py_object<T>(t[i]))) {\n        throw std::runtime_error{\"Failed to add to PyList.\"};\n      }\n    }\n    return list;\n  }\n};\n\ntemplate <typename T, size_t Size>\nstruct ToPyObject<std::array<T, Size>, std::nullptr_t> {\n  static PyObject* convert(const std::array<T, Size>& t) {\n    PyObject* list = PyList_New(static_cast<long>(t.size()));\n    if (list == nullptr) {\n      throw std::runtime_error{\"Failed to convert argument.\"};\n    }\n    for (size_t i = 0; i < Size; ++i) {\n      PyObject* value = ToPyObject<T>::convert(gsl::at(t, i));\n      if (value == nullptr) {\n        throw std::runtime_error{\"Failed to convert argument.\"};\n      }\n      if (-1 == PyList_SetItem(list, static_cast<long>(i), value)) {\n        throw std::runtime_error{\"Failed to add to PyList.\"};\n      }\n    }\n    return list;\n  }\n};\n\ntemplate <>\nstruct ToPyObject<DataVector, std::nullptr_t> {\n  static PyObject* convert(const DataVector& t) {\n    PyObject* npy_array = PyArray_SimpleNew(  // NOLINT\n        1, (std::array<long, 1>{{static_cast<long>(t.size())}}.data()),\n        NPY_DOUBLE);\n\n    if (npy_array == nullptr) {\n      throw std::runtime_error{\"Failed to convert argument.\"};\n    }\n    for (size_t i = 0; i < t.size(); ++i) {\n      // clang-tidy: Do not use pointer arithmetic\n      // clang-tidy: Do not use reinterpret cast\n      const auto data = static_cast<double*>(PyArray_GETPTR1(  // NOLINT\n          reinterpret_cast<PyArrayObject*>(npy_array),         // NOLINT\n          static_cast<long>(i)));\n      if (data == nullptr) {\n        throw std::runtime_error{\"Failed to access argument of PyArray.\"};\n      }\n      *data = t[i];\n    }\n    return npy_array;\n  }\n};\n\ntemplate <>\nstruct ToPyObject<ComplexDataVector, std::nullptr_t> {\n  static PyObject* convert(const ComplexDataVector& t) {\n    PyObject* npy_array = PyArray_SimpleNew(  // NOLINT\n        1, (std::array<long, 1>{{static_cast<long>(t.size())}}.data()),\n        NPY_COMPLEX128);\n\n    if (npy_array == nullptr) {\n      throw std::runtime_error{\"Failed to convert argument.\"};\n    }\n    for (size_t i = 0; i < t.size(); ++i) {\n      // clang-tidy: Do not use pointer arithmetic\n      // clang-tidy: Do not use reinterpret cast\n      auto* const data =\n          static_cast<std::complex<double>*>(PyArray_GETPTR1(  // NOLINT\n              reinterpret_cast<PyArrayObject*>(npy_array),     // NOLINT\n              static_cast<long>(i)));\n      if (data == nullptr) {\n        throw std::runtime_error{\"Failed to access argument of PyArray.\"};\n      }\n      *data = t[i];\n    }\n    return npy_array;\n  }\n};\n\ntemplate <>\nstruct FromPyObject<long, std::nullptr_t> {\n  static long convert(PyObject* t) {\n    if (t == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7\n    } else if (not PyInt_Check(t) and not PyLong_Check(t)) {\n#elif PY_MAJOR_VERSION == 3\n      // clang-tidy: hicpp-signed-bitwise\n    } else if (not PyLong_Check(t)) {  // NOLINT\n#else\n    } else {\n      static_assert(false, \"Only works on Python 2.7 and 3.x\")\n#endif\n      const std::string python_type{Py_TYPE(t)->tp_name};\n      throw std::runtime_error{\n          \"Cannot convert non-long/int type to long. Got \" + python_type};\n    }\n    return PyLong_AsLong(t);\n  }\n};\n\ntemplate <>\nstruct FromPyObject<unsigned long, std::nullptr_t> {\n  static unsigned long convert(PyObject* t) {\n    if (t == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7\n    } else if (not PyInt_Check(t) and not PyLong_Check(t)) {\n#elif PY_MAJOR_VERSION == 3\n      // clang-tidy: hicpp-signed-bitwise\n    } else if (not PyLong_Check(t)) {  // NOLINT\n#else\n    } else {\n      static_assert(false, \"Only works on Python 2.7 and 3.x\");\n#endif\n      const std::string python_type{Py_TYPE(t)->tp_name};\n      throw std::runtime_error{\n          \"Cannot convert non-long/int type to long. Got \" + python_type};\n    }\n    return PyLong_AsUnsignedLong(t);\n  }\n};\n\ntemplate <>\nstruct FromPyObject<double, std::nullptr_t> {\n  static double convert(PyObject* t) {\n    if (t == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n    } else if (not PyFloat_Check(t)) {\n      const std::string python_type{Py_TYPE(t)->tp_name};\n      throw std::runtime_error{\n          \"Cannot convert non-double type to double. Got \" + python_type};\n    }\n    return PyFloat_AsDouble(t);\n  }\n};\n\ntemplate <>\nstruct FromPyObject<bool, std::nullptr_t> {\n  static bool convert(PyObject* t) {\n    if (t == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n    } else if (not PyBool_Check(t)) {\n      const std::string python_type{Py_TYPE(t)->tp_name};\n      throw std::runtime_error{\"Cannot convert non-bool type to bool. Got \" +\n                               python_type};\n    }\n    return static_cast<bool>(PyLong_AsLong(t));\n  }\n};\n\ntemplate <>\nstruct FromPyObject<std::string, std::nullptr_t> {\n  static std::string convert(PyObject* t) {\n    if (t == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7\n    } else if (not PyString_CheckExact(t)) {\n#elif PY_MAJOR_VERSION == 3\n    } else if (not PyUnicode_CheckExact(t)) {\n#else\n    } else {\n      static_assert(false, \"Only works on Python 2.7 and 3.x\")\n#endif\n      const std::string python_type{Py_TYPE(t)->tp_name};\n      throw std::runtime_error{\n          \"Cannot convert non-string type to string. Got \" + python_type};\n    }\n#if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION == 7\n    return std::string(PyString_AsString(t));\n#elif PY_MAJOR_VERSION == 3\n    PyObject* tascii = PyUnicode_AsASCIIString(t);\n    if (nullptr == tascii) {\n      throw std::runtime_error{\"Cannot convert to ASCII string.\"};\n    }\n    std::string str = PyBytes_AsString(tascii);\n    Py_DECREF(tascii);  // NOLINT\n    return str;\n#else\n    static_assert(false, \"Only works on Python 2.7 and 3.x\")\n#endif\n  }\n};\n\n// This overload handles the case of converting from a python type of None to a\n// void*\ntemplate <>\nstruct FromPyObject<void*, std::nullptr_t> {\n  static void* convert(PyObject* t) {\n    if (t == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n    } else if (t != Py_None) {\n      const std::string python_type{Py_TYPE(t)->tp_name};\n      throw std::runtime_error{\"Cannot convert non-None type to void. Got \" +\n                               python_type};\n    }\n    return nullptr;\n  }\n};\n\ntemplate <typename T>\nstruct FromPyObject<T, Requires<tt::is_a_v<std::vector, T>>> {\n  static T convert(PyObject* p) {\n    if (p == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n    } else if (not PyList_CheckExact(p)) {\n      const std::string python_type{Py_TYPE(p)->tp_name};\n      throw std::runtime_error{\"Cannot convert non-list type to vector. Got \" +\n                               python_type};\n    }\n    T t(static_cast<size_t>(PyList_Size(p)));\n    for (size_t i = 0; i < t.size(); ++i) {\n      PyObject* value = PyList_GetItem(p, static_cast<long>(i));\n      if (value == nullptr) {\n        throw std::runtime_error{\"Failed to get argument from list.\"};\n      }\n      t[i] = from_py_object<typename T::value_type>(value);\n    }\n    return t;\n  }\n};\n\ntemplate <typename T>\nstruct FromPyObject<T, Requires<tt::is_std_array_v<T>>> {\n  static T convert(PyObject* p) {\n    if (p == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n    } else if (not PyList_CheckExact(p)) {\n      const std::string python_type{Py_TYPE(p)->tp_name};\n      throw std::runtime_error{\"Cannot convert non-list type to array. Got \" +\n                               python_type};\n    }\n    T t{};\n    // clang-tidy: Do no implicitly decay an array into a pointer\n    assert(PyList_Size(p) == static_cast<long>(t.size()));  // NOLINT\n    for (size_t i = 0; i < t.size(); ++i) {\n      PyObject* value = PyList_GetItem(p, static_cast<long>(i));\n      if (value == nullptr) {\n        throw std::runtime_error{\"Failed to get argument from list.\"};\n      }\n      gsl::at(t, i) = from_py_object<typename T::value_type>(value);\n    }\n    return t;\n  }\n};\ntemplate <>\nstruct FromPyObject<DataVector, std::nullptr_t> {\n  static DataVector convert(PyObject* p) {\n    if (p == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n    }\n    // clang-tidy: c-style casts. (Expanded from macro)\n    if (not PyArray_CheckExact(p)) {  // NOLINT\n      const std::string python_type{Py_TYPE(p)->tp_name};\n      throw std::runtime_error{\n          \"Cannot convert non-array type to DataVector. Got \" + python_type};\n    }\n    // clang-tidy: reinterpret_cast\n    const auto npy_array = reinterpret_cast<PyArrayObject*>(p);  // NOLINT\n    if (PyArray_TYPE(npy_array) != NPY_DOUBLE) {\n      throw std::runtime_error{\n          \"Cannot convert array of non-double type to DataVector.\"};\n    }\n    if (PyArray_NDIM(npy_array) != 1) {\n      throw std::runtime_error{\n          \"Cannot convert array of ndim != 1 to DataVector.\"};\n    }\n    // clang-tidy: c-style casts, pointer arithmetic. (Expanded from macro)\n    DataVector t(static_cast<size_t>(PyArray_Size(p)));  // NOLINT\n    for (size_t i = 0; i < t.size(); ++i) {\n      // clang-tidy: pointer arithmetic. (Expanded from macro)\n      const auto* const value = static_cast<const double*>(\n          PyArray_GETPTR1(npy_array, static_cast<long>(i)));  // NOLINT\n      if (value == nullptr) {\n        throw std::runtime_error{\"Failed to get argument from PyArray.\"};\n      }\n      t[i] = *value;\n    }\n    return t;\n  }\n};\n\ntemplate <>\nstruct FromPyObject<ComplexDataVector, std::nullptr_t> {\n  static ComplexDataVector convert(PyObject* p) {\n    if (p == nullptr) {\n      throw std::runtime_error{\"Received null PyObject.\"};\n    }\n    // clang-tidy: c-style casts. (Expanded from macro)\n    if (not PyArray_CheckExact(p)) {  // NOLINT\n      const std::string python_type{Py_TYPE(p)->tp_name};\n      throw std::runtime_error{\n          \"Cannot convert non-array type to ComplexDataVector. Got \" +\n          python_type};\n    }\n    // clang-tidy: reinterpret_cast\n    const auto npy_array = reinterpret_cast<PyArrayObject*>(p);  // NOLINT\n    if (PyArray_TYPE(npy_array) != NPY_COMPLEX128) {\n      throw std::runtime_error{\n          \"Cannot convert array of non-complex type to ComplexDataVector.\"};\n    }\n    if (PyArray_NDIM(npy_array) != 1) {\n      throw std::runtime_error{\n          \"Cannot convert array of ndim != 1 to ComplexDataVector.\"};\n    }\n    // clang-tidy: c-style casts, pointer arithmetic. (Expanded from macro)\n    ComplexDataVector t(static_cast<size_t>(PyArray_Size(p)));  // NOLINT\n    for (size_t i = 0; i < t.size(); ++i) {\n      // clang-tidy: pointer arithmetic. (Expanded from macro)\n      const auto* value = static_cast<const std::complex<double>*>(\n          PyArray_GETPTR1(npy_array, static_cast<long>(i)));  // NOLINT\n      if (value == nullptr) {\n        throw std::runtime_error{\"Failed to get argument from PyArray.\"};\n      }\n      t[i] = *value;\n    }\n    return t;\n  }\n};\n\n// This function is needed because one cannot cast an array of size_t's (used by\n// SpECTRE) to an array of longs (used by NumPy).\ntemplate <size_t Size>\nstd::array<long, Size> convert_array_of_size_t_to_array_of_long(\n    const std::array<size_t, Size>& arr_of_size_t) {\n  std::array<long, Size> arr_of_long{};\n  for (size_t i = 0; i < Size; ++i) {\n    gsl::at(arr_of_long, i) = static_cast<long>(gsl::at(arr_of_size_t, i));\n  }\n  return arr_of_long;\n}\n\ntemplate <typename T, size_t Size, typename PyObj>\nT* get_ptr_to_elem(PyObj* npy_array, const std::array<size_t, Size>& idx) {\n  auto t = static_cast<T*>(PyArray_GetPtr(          // NOLINT\n      reinterpret_cast<PyArrayObject*>(npy_array),  // NOLINT\n      convert_array_of_size_t_to_array_of_long(idx).data()));\n  if (t == nullptr) {\n    throw std::runtime_error{\"Failed to access element of PyArray.\"};\n  }\n  return t;\n}\n\ntemplate <typename T>\nT tensor_conversion_impl(PyObject* p) {\n  if (p == nullptr) {\n    throw std::runtime_error{\"Received null PyObject.\"};\n  }\n  // clang-tidy: c-style casts. (Expanded from macro)\n  if (not PyArray_CheckExact(p)) {  // NOLINT\n    const std::string python_type{Py_TYPE(p)->tp_name};\n    throw std::runtime_error{\n        \"Cannot convert non-array type (\" + python_type +\n        \") to Tensor. Must be a NumPy array to convert to a Tensor.\"};\n  }\n  // clang-tidy: reinterpret_cast\n  const auto npy_array = reinterpret_cast<PyArrayObject*>(p);  // NOLINT\n  using ValueType = tt::get_complex_or_fundamental_type_t<typename T::type>;\n  static_assert(std::is_same_v<ValueType, double> or\n                    std::is_same_v<ValueType, std::complex<double>>,\n                \"Only Tensors of type 'double' and 'std::complex<double>' are \"\n                \"supported.\");\n  if constexpr (std::is_same_v<ValueType, double>) {\n    if (PyArray_TYPE(npy_array) != NPY_DOUBLE) {\n      throw std::runtime_error{\n          \"Cannot convert Numpy array to Tensor of type 'double'.\"\n          \" Check the 'dtype' of the Numpy array.\"};\n    }\n  } else if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n    if (PyArray_TYPE(npy_array) != NPY_CDOUBLE) {\n      throw std::runtime_error{\n          \"Cannot convert Numpy array to Tensor of type 'std::complex<double>'.\"\n          \" Check the 'dtype' of the Numpy array.\"};\n    }\n  }\n  if (PyArray_NDIM(npy_array) != static_cast<long>(T::rank())) {\n    throw std::runtime_error{\"Mismatch between ndim of numpy ndarray (\" +\n                             std::to_string(PyArray_NDIM(npy_array)) +\n                             \") and rank of Tensor (\" +\n                             std::to_string(T::rank()) + \")\"};\n  }\n\n  const auto* npy_array_dims = PyArray_DIMS(npy_array);\n  constexpr auto t_array_dims = T::index_dims();\n  for (size_t i = 0; i < T::rank(); ++i) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    if (npy_array_dims[i] != static_cast<long>(gsl::at(t_array_dims, i))) {\n      throw std::runtime_error{\n          \"Mismatch between number of components of ndarray (\" +\n          std::to_string(npy_array_dims[i])  // NOLINT\n          + \") and Tensor of rank \" + std::to_string(T::rank()) + \" in \" +\n          std::to_string(i) + \"\\'th index with dimension \" +\n          std::to_string(static_cast<long>(gsl::at(t_array_dims, i)))};\n    }\n  }\n  auto t = make_with_value<T>(\n      *get_ptr_to_elem<typename T::type>(npy_array, make_array<T::rank()>(0ul)),\n      static_cast<typename T::value_type>(0.0));\n  for (IndexIterator<T::rank()> index_it((Index<T::rank()>(T::index_dims())));\n       index_it; ++index_it) {\n    const auto tensor_idx = (*index_it).indices();\n    t.get(tensor_idx) =\n        *get_ptr_to_elem<typename T::type>(npy_array, tensor_idx);\n  }\n  return t;\n}\n\ntemplate <>\nstruct FromPyObject<Scalar<double>> {\n  static Scalar<double> convert(PyObject* p) {\n    if (PyFloat_Check(p)) {\n      return Scalar<double>{PyFloat_AsDouble(p)};\n    } else {\n      return tensor_conversion_impl<Scalar<double>>(p);\n    }\n  }\n};\n\ntemplate <typename T>\nstruct FromPyObject<T, Requires<tt::is_a_v<Tensor, T> and T::rank() != 0 and\n                                std::is_same_v<typename T::type, double>>> {\n  static T convert(PyObject* p) { return tensor_conversion_impl<T>(p); }\n};\n\ntemplate <typename... Symms, typename... Indices>\nstruct FromPyObject<std::tuple<Tensor<double, Symms, Indices>...>,\n                    std::nullptr_t> {\n  static std::tuple<Tensor<double, Symms, Indices>...> convert(PyObject* p) {\n    if (PyTuple_CheckExact(p) == 0) {\n      const std::string python_type{Py_TYPE(p)->tp_name};\n      throw std::runtime_error{\"Expected a Python tuple but got \" +\n                               python_type};\n    }\n    if (PyTuple_Size(p) != sizeof...(Symms)) {\n      throw std::runtime_error{\n          \"Python tuple has size \" + std::to_string(PyTuple_Size(p)) +\n          \" but we expected size \" + std::to_string(sizeof...(Symms))};\n    }\n    return convert(p, std::make_index_sequence<sizeof...(Symms)>{});\n  }\n\n private:\n  template <size_t... Is>\n  static std::tuple<Tensor<double, Symms, Indices>...> convert(\n      PyObject* p, std::index_sequence<Is...> /*meta*/) {\n    std::tuple<Tensor<double, Symms, Indices>...> result{};\n    EXPAND_PACK_LEFT_TO_RIGHT([&result, &p]() {\n      try {\n        std::get<Is>(result) =\n            tensor_conversion_impl<Tensor<double, Symms, Indices>>(\n                PyTuple_GetItem(p, static_cast<ssize_t>(Is)));\n      } catch (const std::exception& e) {\n        throw std::runtime_error{\n            std::string{\n                \"std::tuple conversion failed for zero-based entry number \"} +\n            std::to_string(Is) +\n            \" of the tuple. Conversion error: \" + std::string{e.what()}};\n      }\n    }());\n    return result;\n  }\n};\n\ntemplate <typename... Tags>\nstruct FromPyObject<tuples::TaggedTuple<Tags...>, std::nullptr_t> {\n  static tuples::TaggedTuple<Tags...> convert(PyObject* p) {\n    if (PyDict_CheckExact(p) == 0) {\n      const std::string python_type{Py_TYPE(p)->tp_name};\n      throw std::runtime_error{\"Expected a Python dictionary but got \" +\n                               python_type};\n    }\n    tuples::TaggedTuple<Tags...> result{};\n    EXPAND_PACK_LEFT_TO_RIGHT([&result, &p]() {\n      const std::string tag_name = db::tag_name<Tags>();\n      PyObject* python_tag_name = PyUnicode_FromString(tag_name.c_str());\n      PyObject* tag_value = PyDict_GetItemWithError(p, python_tag_name);\n      if (tag_value == nullptr) {\n        PyObject* python_keys = PyDict_Keys(p);\n        const auto keys = from_py_object<std::vector<std::string>>(python_keys);\n        Py_DECREF(python_keys);\n        throw std::runtime_error(\"Could not find tag \" + tag_name +\n                                 \" in dictionary. Known keys are \" +\n                                 std::string{MakeString{} << keys});\n      }\n      try {\n        get<Tags>(result) = from_py_object<typename Tags::type>(tag_value);\n      } catch (const std::exception& e) {\n        throw std::runtime_error{\n            std::string{\"TaggedTuple conversion failed for tag name \"} +\n            tag_name + \". Conversion error: \" + std::string{e.what()}};\n      }\n      Py_DECREF(python_tag_name);\n      // tag_value is a borrowed reference, so no need to DECREF.\n    }());\n    return result;\n  }\n};\n\ntemplate <>\nstruct FromPyObject<Scalar<std::complex<double>>> {\n  static Scalar<std::complex<double>> convert(PyObject* p) {\n    if (PyComplex_Check(p)) {\n      return Scalar<std::complex<double>>{std::complex<double>(\n          PyComplex_RealAsDouble(p), PyComplex_ImagAsDouble(p))};\n    } else {\n      return tensor_conversion_impl<Scalar<std::complex<double>>>(p);\n    }\n  }\n};\n\ntemplate <typename T>\nstruct FromPyObject<\n    T, Requires<tt::is_a_v<Tensor, T> and T::rank() != 0 and\n                std::is_same_v<typename T::type, std::complex<double>>>> {\n  static T convert(PyObject* p) { return tensor_conversion_impl<T>(p); }\n};\n\ntemplate <typename T>\nstruct ToPyObject<T, Requires<tt::is_a_v<Tensor, T> and\n                              std::is_same_v<typename T::type, double>>> {\n  static PyObject* convert(const T& t) {\n    std::array<long, T::rank()> dims =\n        convert_array_of_size_t_to_array_of_long(T::index_dims());\n    // clang-tidy: cstyle casts, pointer arithmetic, implicit decay array to\n    // pointer. (Expanded from macro.)\n    PyObject* npy_array =\n        PyArray_SimpleNew(T::rank(), dims.data(), NPY_DOUBLE);  // NOLINT\n    if (npy_array == nullptr) {\n      throw std::runtime_error{\"Failed to create PyArray.\"};\n    }\n\n    for (IndexIterator<T::rank()> index_it((Index<T::rank()>(T::index_dims())));\n         index_it; ++index_it) {\n      const auto tensor_idx = (*index_it).indices();\n      *get_ptr_to_elem<typename T::type>(npy_array, tensor_idx) =\n          t.get(tensor_idx);\n    }\n    return npy_array;\n  }\n};\n\ntemplate <typename T>\nstruct ToPyObject<\n    T, Requires<tt::is_a_v<Tensor, T> and\n                std::is_same_v<typename T::type, std::complex<double>>>> {\n  static PyObject* convert(const T& t) {\n    std::array<long, T::rank()> dims =\n        convert_array_of_size_t_to_array_of_long(T::index_dims());\n    // clang-tidy: cstyle casts, pointer arithmetic, implicit decay array to\n    // pointer. (Expanded from macro.)\n    PyObject* npy_array =\n        PyArray_SimpleNew(T::rank(), dims.data(), NPY_COMPLEX128);  // NOLINT\n    if (npy_array == nullptr) {\n      throw std::runtime_error{\"Failed to create PyArray.\"};\n    }\n\n    for (IndexIterator<T::rank()> index_it((Index<T::rank()>(T::index_dims())));\n         index_it; ++index_it) {\n      const auto tensor_idx = (*index_it).indices();\n      *get_ptr_to_elem<std::complex<double>>(npy_array, tensor_idx) =\n          t.get(tensor_idx);\n    }\n    return npy_array;\n  }\n};\n\ntemplate <typename T>\nstruct FromPyObject<Scalar<T>, Requires<is_any_spin_weighted_v<T>>> {\n  static Scalar<T> convert(PyObject* p) {\n    return Scalar<T>{T{FromPyObject<typename T::value_type>::convert(p)}};\n  }\n};\n\ntemplate <typename T>\nstruct ToPyObject<Scalar<T>, Requires<is_any_spin_weighted_v<T>>> {\n  static PyObject* convert(const Scalar<T>& t) {\n    return ToPyObject<typename T::value_type>::convert(get(t).data());\n  }\n};\n\ntemplate <typename T>\nstruct FromPyObject<std::optional<T>> {\n  static std::optional<T> convert(PyObject* p) {\n    if (p == Py_None) {\n      return std::optional<T>{};\n    }\n    return FromPyObject<T>::convert(p);\n  }\n};\n\ntemplate <typename T>\nstruct ToPyObject<std::optional<T>> {\n  static PyObject* convert(const std::optional<T>& t) {\n    if (static_cast<bool>(t)) {\n      return to_py_object(*t);\n    }\n    Py_RETURN_NONE;\n  }\n};\n\n///\\endcond\n\n}  // namespace pypp\n"
  },
  {
    "path": "tests/Unit/Framework/SetupLocalPythonEnvironment.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <boost/preprocessor.hpp>\n#include <string>\n#include <vector>\n\n#ifndef PY_ARRAY_UNIQUE_SYMBOL\n#define PY_ARRAY_UNIQUE_SYMBOL SPECTRE_PY_API\n#endif\n#define NPY_API_SYMBOL_ATTRIBUTE\n\n#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n#include <Python.h>\n#include <numpy/arrayobject.h>\n\n#include \"Framework/PyppFundamentals.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace {\nvoid prepend_python_path(const std::string& new_path) {\n  if (not file_system::check_if_dir_exists(new_path)) {\n    ERROR_NO_TRACE(\"Trying to add path '\"\n                   << new_path\n                   << \"' to the python environment during setup \"\n                      \"but this directory does not exist. Either \"\n                      \"you have a typo in your path, or you need \"\n                      \"to create the appropriate site-packages \"\n                      \"directory after upgrading python.\");\n  }\n  PyObject* sys_path = PySys_GetObject(\"path\");\n  PyList_Insert(sys_path, 0, PyUnicode_FromString(new_path.c_str()));\n}\n}  // namespace\n\nnamespace pypp {\nSetupLocalPythonEnvironment::SetupLocalPythonEnvironment(\n    const std::string& cur_dir_relative_to_unit_test_path) {\n  // We have to clean up the Python environment only after all tests have\n  // finished running, since there could be multiple tests run in a single\n  // executable launch. This is done in TestMain(Charm).cpp.\n  if (not initialized) {\n    PyStatus status;\n    PyConfig config;\n    PyConfig_InitPythonConfig(&config);\n    // Populate the Python config with the standard values\n    status = PyConfig_Read(&config);\n    if (PyStatus_Exception(status) != 0) {\n      Py_ExitStatusException(status);\n    }\n    // Don't produce the __pycache__ dir (python 3.2 and newer) or the .pyc\n    // files (python 2.7) in the tests directory to avoid cluttering the source\n    // tree. The overhead of not having the compile files is <= 0.01s\n    config.write_bytecode = 0;\n    status = Py_InitializeFromConfig(&config);\n    if (PyStatus_Exception(status) != 0) {\n      Py_ExitStatusException(status);\n    }\n    PyConfig_Clear(&config);\n\n    // Add directory for installed packages (see CMakeLists.txt for details)\n    prepend_python_path(PYTHON_SITELIB);\n\n    // On some python versions init_numpy() can throw an FPE, this occurred at\n    // least with python 3.6, numpy 1.14.2.\n    ScopedFpeState disable_fpes(false);\n    init_numpy();\n    disable_fpes.restore_exceptions();\n  }\n  initialized = true;\n\n  // Add test directory to the Python path\n  prepend_python_path(unit_test_src_path() +\n                      cur_dir_relative_to_unit_test_path);\n}\n\n#if PY_MAJOR_VERSION == 3\nstd::nullptr_t SetupLocalPythonEnvironment::init_numpy() {\n  import_array();\n  return nullptr;\n}\n#else\nvoid SetupLocalPythonEnvironment::init_numpy() { import_array(); }\n#endif\n\nvoid SetupLocalPythonEnvironment::finalize_env() {\n  if (not finalized and initialized) {\n    Py_Finalize();\n  }\n  finalized = true;\n}\n\nbool SetupLocalPythonEnvironment::initialized = false;\nbool SetupLocalPythonEnvironment::finalized = false;\n}  // namespace pypp\n"
  },
  {
    "path": "tests/Unit/Framework/SetupLocalPythonEnvironment.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <patchlevel.h>\n#include <string>\n\n/// Contains all functions for pypp\nnamespace pypp {\n/// Enable calling of python in the local scope, and add directory(ies) to the\n/// front of the search path for modules. The directory which is appended to the\n/// path is relative to the `tests/Unit` directory.\nstruct SetupLocalPythonEnvironment {\n  explicit SetupLocalPythonEnvironment(\n      const std::string& cur_dir_relative_to_unit_test_path);\n\n  ~SetupLocalPythonEnvironment() = default;\n\n  SetupLocalPythonEnvironment(const SetupLocalPythonEnvironment&) = delete;\n  SetupLocalPythonEnvironment& operator=(const SetupLocalPythonEnvironment&) =\n      delete;\n  SetupLocalPythonEnvironment(const SetupLocalPythonEnvironment&&) = delete;\n  SetupLocalPythonEnvironment& operator=(const SetupLocalPythonEnvironment&&) =\n      delete;\n\n  /// \\cond\n  // We have to clean up the Python environment only after all tests have\n  // finished running, since there could be multiple tests run in a single\n  // executable launch. This is done in TestMain(Charm).cpp.\n  static void finalize_env();\n  /// \\endcond\n\n private:\n// In order to use NumPy's API, import_array() must be called. However it is a\n// macro which contains a return statement, returning NULL in python 3 and void\n// in python 2. As such it needs to be factored into its own function which\n// returns either nullptr or void depending on the version.\n#if PY_MAJOR_VERSION == 3\n  static std::nullptr_t init_numpy();\n#else\n  static void init_numpy();\n#endif\n  static bool initialized;\n  static bool finalized;\n};\n}  // namespace pypp\n"
  },
  {
    "path": "tests/Unit/Framework/TestCreation.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/algorithm/string.hpp>\n#include <memory>\n#include <string>\n#include <type_traits>\n\n#include \"Options/Auto.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace TestHelpers {\nnamespace TestCreation_detail {\ntemplate <typename T, typename = std::void_t<>>\nstruct looks_like_tag : std::false_type {};\n\ntemplate <typename T>\nstruct looks_like_tag<T, std::void_t<typename T::type>> : std::true_type {};\n\ntemplate <typename T>\nstruct TestCreationOpt {\n  using type = T;\n  static constexpr Options::String help = {\"halp\"};\n};\n\ntemplate <typename Tag, typename = void>\nstruct AddGroups {\n  template <typename OptionTag = Tag>\n  static std::string apply(std::string construction_string) {\n    construction_string.insert(0, 2, ' ');\n    construction_string =\n        boost::algorithm::replace_all_copy(construction_string, \"\\n\", \"\\n  \");\n    construction_string.insert(0, pretty_type::name<OptionTag>() + \":\\n\");\n    return construction_string;\n  }\n};\n\ntemplate <typename Tag>\nstruct AddGroups<Tag, std::void_t<typename Tag::group>> {\n  static std::string apply(const std::string& construction_string) {\n    return AddGroups<typename Tag::group>::apply(\n        AddGroups<void>::template apply<Tag>(construction_string));\n  }\n};\n\ntemplate <typename OptionTag, bool IsAuto>\nstruct option_return;\n\ntemplate <typename OptionTag>\nstruct option_return<OptionTag, true> {\n  using type = typename OptionTag::type::value_type;\n};\n\ntemplate <typename OptionTag>\nstruct option_return<OptionTag, false> {\n  using type = typename OptionTag::type;\n};\n\ntemplate <typename OptionTag>\nusing option_return_t = typename option_return<\n    OptionTag, tt::is_a_v<Options::Auto, typename OptionTag::type>>::type;\n\ntemplate <typename BaseClass, typename DerivedClass>\nstruct SingleFactoryMetavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<BaseClass, tmpl::list<DerivedClass>>>;\n  };\n};\n}  // namespace TestCreation_detail\n\n/// \\ingroup TestingFrameworkGroup\n/// Creates an instance of a given option-creatable type.\n///\n/// This is a wrapper around Options::Parser constructing a single,\n/// specified type \\p T from the supplied string.  If necessary,\n/// metavariables can be supplied as the second template argument.\n///\n/// A class can be explicitly created through a factory by passing\n/// `std::unique_ptr<BaseClass>` as the type.  This will require\n/// metavariables to be passed.  For testing basic factory creation,\n/// the simpler TestHelpers::test_factory_creation() can be used\n/// instead.\n///\n/// \\snippet Test_TestCreation.cpp class_without_metavariables\n/// \\snippet Test_TestCreation.cpp class_without_metavariables_create\n///\n/// \\see TestHelpers::test_option_tag()\ntemplate <typename T, typename Metavariables = NoSuchType>\nT test_creation(const std::string& construction_string) {\n  static_assert(not TestCreation_detail::looks_like_tag<Metavariables>::value,\n                \"test_creation no longer allows overriding the default \"\n                \"option tag.  To test a particular option tag use \"\n                \"test_option_tag.\");\n  using tag = TestCreation_detail::TestCreationOpt<T>;\n  Options::Parser<tmpl::list<tag>> options(\"\");\n  options.parse(\n      TestCreation_detail::AddGroups<tag>::apply(construction_string));\n  return options.template get<tag, Metavariables>();\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// Runs the option parser on a given tag\n///\n/// Runs the option parser with the supplied input on a given tag.\n/// The tag name and any groups are handled by this function and\n/// should not be supplied in the argument string.  If necessary,\n/// metavariables can be supplied as the second template argument.\n///\n/// \\snippet Test_TestCreation.cpp class_without_metavariables\n/// \\snippet Test_TestCreation.cpp class_without_metavariables_tag\n/// \\snippet Test_TestCreation.cpp class_without_metavariables_create_tag\n///\n/// \\see TestHelpers::test_creation()\ntemplate <typename OptionTag, typename Metavariables = NoSuchType>\nTestCreation_detail::option_return_t<OptionTag> test_option_tag(\n    const std::string& construction_string) {\n  Options::Parser<tmpl::list<OptionTag>> options(\"\");\n  options.parse(\n      TestCreation_detail::AddGroups<OptionTag>::apply(construction_string));\n  return options.template get<OptionTag, Metavariables>();\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// Creates a class of a known derived type using a factory.\n///\n/// This is a shorthand for creating a \\p DerivedClass through a \\p\n/// BaseClass factory, saving the caller from having to explicitly\n/// write metavariables with the appropriate `factory_classes` alias.\n/// The name of the type should be supplied as the first line of the\n/// passed string, just as for normal use of a factory.\n///\n/// If multiple factory creatable types must be handled or if\n/// metavariables must be passed for some other reason, then the more\n/// general TestHelpers::test_creation() must be used instead.\n///\n/// \\snippet Test_TestCreation.cpp test_factory_creation\ntemplate <typename BaseClass, typename DerivedClass>\nstd::unique_ptr<BaseClass> test_factory_creation(\n    const std::string& construction_string) {\n  return TestHelpers::test_creation<\n      std::unique_ptr<BaseClass>,\n      TestCreation_detail::SingleFactoryMetavariables<BaseClass, DerivedClass>>(\n      construction_string);\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief A wrapper that allows testing an option tag without having to write a\n * Metavariables class. Combines `TestHelpers::test_factory_creation()` and\n * `TestHelpers::test_option_tag()`.\n *\n * \\note Assumes that the option tag holds a `std::unique_ptr<BaseClass>`.\n *\n * \\snippet Test_TestCreation.cpp test_option_tag_factory_creation_tag\n * \\snippet Test_TestCreation.cpp test_option_tag_factory_creation\n */\ntemplate <typename OptionTag, typename DerivedClass>\ntypename OptionTag::type test_option_tag_factory_creation(\n    const std::string& construction_string) {\n  static_assert(\n      tt::is_a_v<std::unique_ptr, typename OptionTag::type>,\n      \"test_option_tag_factory_creation assumes the OptionTag holds a \"\n      \"std::unique_ptr<BaseClass>, but this does not appear to be the case.\");\n  using metavars = TestCreation_detail::SingleFactoryMetavariables<\n      typename OptionTag::type::element_type, DerivedClass>;\n  auto created = test_option_tag<OptionTag, metavars>(construction_string);\n  REQUIRE(dynamic_cast<const DerivedClass*>(created.get()) != nullptr);\n  return created;\n}\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Framework/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Commonly used routines, functions and definitions shared amongst unit tests\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <memory>\n#include <ostream>\n#include <random>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/HasEquivalence.hpp\"\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Tests the serialization of comparable types\n/// \\example\n/// \\snippet Test_PupStlCpp11.cpp example_serialize_comparable\ntemplate <typename T>\nvoid test_serialization(const T& t) {\n  INFO(\"Serialization\");\n  static_assert(tt::has_equivalence_v<T>, \"No operator== for T\");\n  CHECK(t == serialize_and_deserialize(t));\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test the serialization of a derived class via a base class pointer\n/// \\example\n/// \\snippet Test_PupStlCpp11.cpp example_serialize_derived\n/// \\tparam B the base class\n/// \\tparam D the derived class\n/// \\tparam Args deduced from `args`\n/// \\param args arguments passed to a constructor of the derived class\ntemplate <typename B, typename D, typename... Args>\nvoid test_serialization_via_base(Args&&... args) {\n  static_assert(std::is_base_of_v<B, D>,\n                \"passed input type is not derived from specified base\");\n  static_assert(tt::has_equivalence_v<D>, \"No operator== for derived class\");\n  register_classes_with_charm<D>();\n  std::unique_ptr<B> base = std::make_unique<D>(args...);\n  std::unique_ptr<B> pupped_base = serialize_and_deserialize(base);\n  CHECK_FALSE(nullptr == dynamic_cast<const D*>(pupped_base.get()));\n  const D derived(args...);\n  CHECK(derived == dynamic_cast<const D&>(*pupped_base));\n}\n\n/// Test for move semantics assuming operator== is implemented correctly.\n/// \\requires `std::is_rvalue_reference<decltype(a)>::%value` is true.\n/// If T is not default constructible, you pass additional\n/// arguments that are used to construct a T.\ntemplate <typename T, Requires<tt::has_equivalence<T>::value> = nullptr,\n          typename... Args>\nvoid test_move_semantics(T&& a, const T& comparison, Args&&... args) {\n  INFO(\"Move semantics\");\n  static_assert(std::is_rvalue_reference<decltype(a)>::value,\n                \"Must move into test_move_semantics\");\n  static_assert(std::is_move_assignable<T>::value);\n  static_assert(std::is_move_constructible<T>::value);\n  if (&a == &comparison or a != comparison) {\n    // We use ERROR instead of ASSERT (which we normally should be using) to\n    // guard against someone writing tests in Release mode where ASSERTs don't\n    // show up.\n    ERROR(\"'a' and 'comparison' must be distinct (but equal in value) objects\");\n  }\n  T b{std::forward<Args>(args)...};\n  // clang-tidy: use std::forward instead of std::move\n  b = std::move(a);  // NOLINT\n  {\n    INFO(\"Test move assignment\");\n    CHECK(b == comparison);\n    CHECK_FALSE(b != comparison);\n  }\n  T c(std::move(b));\n  {\n    INFO(\"Test move construction\");\n    CHECK(c == comparison);\n    CHECK_FALSE(c != comparison);\n  }\n}\n\n/// Test for copy and move semantics assuming operator== is\n/// implemented correctly.\ntemplate <typename T>\nvoid test_copy_semantics(const T& a) {\n  INFO(\"Copy semantics\");\n  static_assert(tt::has_equivalence_v<T>,\n                \"Class has no operator== implemented\");\n  static_assert(std::is_copy_assignable<T>::value,\n                \"Class is not copy assignable.\");\n  static_assert(std::is_copy_constructible<T>::value,\n                \"Class is not copy constructible.\");\n  static_assert(std::is_default_constructible_v<T>,\n                \"Class needs to be default constructible to check copy \"\n                \"assignment operator\");\n  T b{};\n  b = a;\n  CHECK(b == a);\n  CHECK_FALSE(b != a);\n  // clang-tidy: intentionally not a reference to force invocation of copy\n  // constructor\n  const T c(a);  // NOLINT\n  CHECK(c == a);\n  CHECK_FALSE(c != a);\n#if defined(__clang__) && __clang_major__ > 6\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wself-assign-overloaded\"\n#endif  // defined(__clang__) && __clang_major__ > 6\n  // clang-tidy: self-assignment\n  b = b;  // NOLINT\n#if defined(__clang__) && __clang_major__ > 6\n#pragma GCC diagnostic pop\n#endif  // defined(__clang__) && __clang_major__ > 6\n  CHECK(b == a);\n  CHECK_FALSE(b != a);\n\n  test_move_semantics(std::move(b), a);\n}\n\n// Test for iterators\ntemplate <typename Container>\nvoid test_iterators(Container& c) {\n  CHECK(std::distance(c.begin(), c.end()) ==\n        static_cast<decltype(std::distance(c.begin(), c.end()))>(c.size()));\n  CHECK(c.begin() == c.cbegin());\n  CHECK(c.end() == c.cend());\n\n  const auto& const_c = c;\n  CHECK(std::distance(const_c.begin(), const_c.end()) ==\n        static_cast<decltype(std::distance(const_c.begin(), const_c.end()))>(\n            const_c.size()));\n  CHECK(const_c.begin() == const_c.cbegin());\n  CHECK(const_c.end() == const_c.cend());\n}\n\n// Test for reverse iterators\ntemplate <typename Container>\nvoid test_reverse_iterators(Container& c) {\n  CHECK(std::distance(c.rbegin(), c.rend()) ==\n        static_cast<decltype(std::distance(c.rbegin(), c.rend()))>(c.size()));\n\n  CHECK(c.rbegin() == c.crbegin());\n  CHECK(c.rend() == c.crend());\n\n  auto it = c.begin();\n  auto rit = c.rbegin();\n  auto end = c.end();\n  auto rend = c.rend();\n  auto cit = c.cbegin();\n  auto cend = c.cend();\n  auto crit = c.crbegin();\n  auto crend = c.crend();\n\n  for (size_t i = 0; i < c.size(); i++) {\n    CHECK(*it == *(std::prev(rend, 1)));\n    CHECK(*rit == *(std::prev(end, 1)));\n    CHECK(*cit == *(std::prev(crend, 1)));\n    CHECK(*crit == *(std::prev(cend, 1)));\n    it++;\n    rit++;\n    rend--;\n    end--;\n    crit++;\n    cit++;\n    crend--;\n    cend--;\n  }\n\n  const auto& const_c = c;\n  CHECK(std::distance(const_c.begin(), const_c.end()) ==\n        static_cast<decltype(std::distance(const_c.begin(), const_c.end()))>(\n            const_c.size()));\n  auto c_it = const_c.begin();\n  auto c_rit = const_c.rbegin();\n  auto c_end = const_c.end();\n  auto c_rend = const_c.rend();\n  for (size_t i = 0; i < c.size(); i++) {\n    CHECK(*c_it == *(std::prev(c_rend, 1)));\n    CHECK(*c_rit == *(std::prev(c_end, 1)));\n    c_it++;\n    c_rit++;\n    c_rend--;\n    c_end--;\n  }\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Function to test comparison operators.  Pass values with\n * less < greater.\n */\ntemplate <typename T, typename U>\nvoid check_cmp(const T& less, const U& greater) {\n  CHECK(less == less);\n  CHECK_FALSE(less == greater);\n  CHECK(less != greater);\n  CHECK_FALSE(less != less);\n  CHECK(less < greater);\n  CHECK_FALSE(greater < less);\n  CHECK(greater > less);\n  CHECK_FALSE(less > greater);\n  CHECK(less <= greater);\n  CHECK_FALSE(greater <= less);\n  CHECK(greater >= less);\n  CHECK_FALSE(less >= greater);\n  CHECK(less <= less);\n  CHECK_FALSE(less < less);\n  CHECK(less >= less);\n  CHECK_FALSE(less > less);\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Check a op b == c and also the op= version.\n */\n#define CHECK_OP(a, op, b, c)   \\\n  do {                          \\\n    const auto& a_ = a;         \\\n    const auto& b_ = b;         \\\n    const auto& c_ = c;         \\\n    CHECK(a_ op b_ == c_);      \\\n    auto f = a_;                \\\n    CHECK((f op## = b_) == c_); \\\n    CHECK(f == c_);             \\\n  } while (false)\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Calculates the derivative of an Invocable at a point x - represented\n * by an array of doubles - in the domain of `map` with a sixth-order finite\n * difference method.\n *\n * \\details Intended for use with CoordinateMaps taking the domain {xi,eta,zeta}\n * to the range {x,y,z}. This function calculates the derivative along the\n * direction given by `direction` with a step size of `h`.\n *\n * \\requires direction be between 0 and VolumeDim\n */\ntemplate <typename Invocable, size_t VolumeDim>\nstd::invoke_result_t<const Invocable&, const std::array<double, VolumeDim>&>\nnumerical_derivative(const Invocable& function,\n                     const std::array<double, VolumeDim>& x,\n                     const size_t direction, const double delta) {\n  ASSERT(0 <= direction and direction < VolumeDim,\n         \"Trying to take derivative along axis \" << direction);\n\n  const auto dx = [direction, delta]() {\n    auto d = make_array<VolumeDim>(0.);\n    gsl::at(d, direction) = delta;\n    return d;\n  }();\n\n  const std::array<double, VolumeDim> x_1ahead = x + dx;\n  const std::array<double, VolumeDim> x_2ahead = x_1ahead + dx;\n  const std::array<double, VolumeDim> x_3ahead = x_2ahead + dx;\n  const std::array<double, VolumeDim> x_1behind = x - dx;\n  const std::array<double, VolumeDim> x_2behind = x_1behind - dx;\n  const std::array<double, VolumeDim> x_3behind = x_2behind - dx;\n  return (1.0 / (60.0 * delta)) * (function(x_3ahead) - function(x_3behind)) -\n         (3.0 / (20.0 * delta)) * (function(x_2ahead) - function(x_2behind)) +\n         (0.75 / delta) * (function(x_1ahead) - function(x_1behind));\n}\n\nstruct NonStreamable {\n  int value{0};\n};\n\ntemplate <>\nstruct std::hash<NonStreamable> {\n  std::size_t operator()(const NonStreamable& ns) const {\n    return std::hash<int>{}(ns.value);\n  }\n};\n\ninline bool operator==(const NonStreamable& a, const NonStreamable& b) {\n  return a.value == b.value;\n}\ninline bool operator!=(const NonStreamable& a, const NonStreamable& b) {\n  return not(a == b);\n}\ninline bool operator<(const NonStreamable& a, const NonStreamable& b) {\n  return a.value < b.value;\n}\n\nstruct NonCopyable {\n  constexpr NonCopyable() = default;\n  constexpr NonCopyable(const NonCopyable&) = delete;\n  constexpr NonCopyable& operator=(const NonCopyable&) = delete;\n  constexpr NonCopyable(NonCopyable&&) = default;\n  NonCopyable& operator=(NonCopyable&&) = default;\n  ~NonCopyable() = default;\n};\ninline bool operator==(const NonCopyable& /*a*/, const NonCopyable& /*b*/) {\n  return true;\n}\ninline bool operator!=(const NonCopyable& a, const NonCopyable& b) {\n  return not(a == b);\n}\ninline std::ostream& operator<<(std::ostream& os, const NonCopyable& /*v*/) {\n  return os << \"NC\";\n}\n\n/// \\cond\n#define MAKE_GENERATOR_IMPL_FIRST_ARG(NAME, ...) NAME\n#define MAKE_GENERATOR_IMPL_SECOND_ARG(NAME, SEED, ...) SEED\n#define MAKE_GENERATOR_IMPL_CONCAT2(a, b) a##b\n#define MAKE_GENERATOR_IMPL_CONCAT(a, b) MAKE_GENERATOR_IMPL_CONCAT2(a, b)\n// Because of preprocessor weirdness the name and arguments of this\n// macro get printed in the error message, so make it something\n// understandable and avoid requiring dummy arguments and such.\n#define MAKE_GENERATOR_MESSAGE(...)                            \\\n  MAKE_GENERATOR_IMPL_CONCAT(                                  \\\n      MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN), \\\n      _MAKE_GENERATOR_message)\n/// \\endcond\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief `MAKE_GENERATOR(NAME [, SEED])` declares a variable of name `NAME`\n/// containing a generator of type `std::mt19937`.\n///\n/// \\details As the generator is made, `INFO` and `CAPTURE_FOR_ERROR`\n/// are called to make sure failed tests provide seed information.\n/// `SEED` is chosen randomly if not supplied, otherwise it must be a\n/// constant expression.\n// What is going on here:\n//\n// If this is called as MAKE_GENERATOR(NAME):\n//   MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN)\n//     -> MAKE_GENERATOR_IMPL_FIRST_ARG(NAME, DUMMY_TOKEN)\n//     -> NAME\n//   MAKE_GENERATOR_IMPL_SECOND_ARG(\n//       __VA_ARGS__, std::random_device{}(), DUMMY_TOKEN)\n//     -> MAKE_GENERATOR_IMPL_SECOND_ARG(\n//            NAME, std::random_device{}(), DUMMY_TOKEN)\n//     -> std::random_device{}()\n//   So we create NAME with a random seed.\n//\n//   In this case DUMMY_TOKEN is needed because the \"...\" in the IMPL\n//   macros has to match at least one thing.\n//\n// If this is called as MAKE_GENERATOR(NAME, SEED):\n//   MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN)\n//     -> MAKE_GENERATOR_IMPL_FIRST_ARG(NAME, SEED, DUMMY_TOKEN)\n//     -> NAME\n//   MAKE_GENERATOR_IMPL_SECOND_ARG(\n//       __VA_ARGS__, std::random_device{}(), DUMMY_TOKEN)\n//     -> MAKE_GENERATOR_IMPL_SECOND_ARG(\n//            NAME, SEED, std::random_device{}(), DUMMY_TOKEN)\n//     -> SEED\n//   So we create NAME with seed SEED.\n//\n//   In this case the DUMMY_TOKEN is not necessary.\n#define MAKE_GENERATOR(...)                                             \\\n  std::mt19937 MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN); \\\n  /* Capture everything because we don't know what passed seed uses */  \\\n  const std::string MAKE_GENERATOR_MESSAGE(__VA_ARGS__) = [&]() {       \\\n    const auto MAKE_GENERATOR_seed = (MAKE_GENERATOR_IMPL_SECOND_ARG(   \\\n        __VA_ARGS__, std::random_device{}(), DUMMY_TOKEN));             \\\n    MAKE_GENERATOR_IMPL_FIRST_ARG(__VA_ARGS__, DUMMY_TOKEN)             \\\n        .seed(MAKE_GENERATOR_seed);                                     \\\n    return MakeString{} << \"Seed is: \" << MAKE_GENERATOR_seed           \\\n                        << \" from \" __FILE__ \":\" << __LINE__;           \\\n  }();                                                                  \\\n  INFO(MAKE_GENERATOR_MESSAGE(__VA_ARGS__));                            \\\n  CAPTURE_FOR_ERROR(MAKE_GENERATOR_MESSAGE(__VA_ARGS__))\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief `NumSamples` unique random elements of the `container`\n *\n * This function is useful to iterate over a random subset of a container, like\n * this:\n *\n * \\snippet Test_Wedge3D.cpp cartesian_product_loop\n *\n * \\note This implementation copies the random elements into a `std::array` that\n * can be iterated over. This can be changed if we need to support noncopyable\n * types.\n */\ntemplate <size_t NumSamples, typename Container,\n          typename ValueType = typename Container::value_type>\nstd::array<ValueType, NumSamples> random_sample(\n    const Container& container, const gsl::not_null<std::mt19937*> generator) {\n  ASSERT(NumSamples <= container.size(),\n         \"Cannot take \" << NumSamples << \" samples from container of size \"\n                        << container.size());\n  std::array<ValueType, NumSamples> result;\n  alg::sample(container, std::begin(result), NumSamples, *generator);\n  return result;\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Creates a new std::vector holding `number_of_samples` unique random\n * elements of the `container`\n *\n * \\note If `container` has fewer elements than `number_of_samples`, this\n * function returns a std::vector of all elements in `container`\n */\ntemplate <typename Container,\n          typename ValueType = typename Container::value_type>\nstd::vector<ValueType> random_sample(\n    const size_t number_of_samples, const Container& container,\n    const gsl::not_null<std::mt19937*> generator) {\n  std::vector<ValueType> result{};\n  result.reserve(std::min(number_of_samples, container.size()));\n  alg::sample(container, std::back_inserter(result), number_of_samples,\n              *generator);\n  return result;\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief A wrapper around Catch's CHECK macro that checks approximate equality\n * of each entry in each tag within a variables.\n */\n#define CHECK_VARIABLES_APPROX(a, b)                                         \\\n  do {                                                                       \\\n    INFO(__FILE__ \":\" + std::to_string(__LINE__) + \": \" #a \" == \" #b);       \\\n    check_variables_approx<std::common_type_t<                               \\\n        std::decay_t<decltype(a)>, std::decay_t<decltype(b)>>>::apply(a, b); \\\n  } while (false)\n\n/*!\n * \\ingroup TestingFrameworkGroup\n *  \\brief Same as `CHECK_VARIABLES_APPROX`, but with a user-defined Approx.\n *  The third argument should be of type `Approx`.\n */\n#define CHECK_VARIABLES_CUSTOM_APPROX(a, b, appx)                            \\\n  do {                                                                       \\\n    INFO(__FILE__ \":\" + std::to_string(__LINE__) + \": \" #a \" == \" #b);       \\\n    check_variables_approx<std::common_type_t<                               \\\n        std::decay_t<decltype(a)>, std::decay_t<decltype(b)>>>::apply(a, b,  \\\n                                                                      appx); \\\n  } while (false)\n\ntemplate <typename Tag, typename TagList>\nconst auto& extract_value_for_variables_comparison(\n    const Variables<TagList>& input_vars, std::true_type /*meta*/) {\n  // only Scalars of spin-weighted quantities are currently allowed. If that\n  // changes, then this solution will no longer be workable.\n  return get(get<Tag>(input_vars)).data();\n}\n\ntemplate <typename Tag, typename TagList>\nconst auto& extract_value_for_variables_comparison(\n    const Variables<TagList>& input_vars, std::false_type /*meta*/) {\n  return get<Tag>(input_vars);\n}\n\ntemplate <typename T>\nstruct check_variables_approx;\n\ntemplate <typename TagList>\nstruct check_variables_approx<Variables<TagList>> {\n  static void apply(const Variables<TagList>& a, const Variables<TagList>& b,\n                    const Approx& appx = approx) {\n    tmpl::for_each<TagList>([&a, &b, &appx](auto x) {\n      using Tag = typename decltype(x)::type;\n      INFO(db::tag_name<Tag>());\n      const auto& a_val = extract_value_for_variables_comparison<Tag>(\n          a, is_any_spin_weighted<typename Tag::type::type>{});\n      const auto& b_val = extract_value_for_variables_comparison<Tag>(\n          b, is_any_spin_weighted<typename Tag::type::type>{});\n      CHECK_ITERABLE_CUSTOM_APPROX(a_val, b_val, appx);\n    });\n  }\n};\n\n// an additional overload for SpinWeighted quantities that just forwards to\n// checking on the wrapped `data()` for the SpinWeighted\ntemplate <typename T>\nstruct check_iterable_approx<T, Requires<is_any_spin_weighted_v<T>>> {\n  static void apply(const T& a, const T& b, const Approx& appx = approx) {\n    check_iterable_approx<typename T::value_type>::apply(a.data(), b.data(),\n                                                         appx);\n  }\n};\n"
  },
  {
    "path": "tests/Unit/Framework/TestingFramework.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Code to wrap or improve the Catch testing framework used for unit tests.\n\n#pragma once\n\n#define CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS\n\n#include <blaze/math/typetraits/IsColumnMajorMatrix.h>\n#include <blaze/math/typetraits/IsDenseMatrix.h>\n#include <catch2/catch_all.hpp>\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <stdexcept>\n#include <string>\n#include <type_traits>\n#include <unordered_set>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/StlStreamDeclarations.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n#include \"Utilities/TypeTraits/IsIterable.hpp\"\n#include \"Utilities/TypeTraits/IsMaplike.hpp\"\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief A replacement for Catch's TEST_CASE that silences clang-tidy warnings\n */\n#define SPECTRE_TEST_CASE(m, n) TEST_CASE(m, n)  // NOLINT\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief A similar to Catch's REQUIRE statement, but can be used in tests that\n * spawn several chares with possibly complex interaction between the chares.\n */\n#define SPECTRE_PARALLEL_REQUIRE(expr)                                  \\\n  do {                                                                  \\\n    if (not(expr)) {                                                    \\\n      ERROR(\"\\nFailed comparison: \" << #expr << \"\\nLine: \" << __LINE__  \\\n                                    << \"\\nFile: \" << __FILE__ << \"\\n\"); \\\n    }                                                                   \\\n  } while (false)\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief A similar to Catch's REQUIRE_FALSE statement, but can be used in tests\n * that spawn several chares with possibly complex interaction between the\n * chares.\n */\n#define SPECTRE_PARALLEL_REQUIRE_FALSE(expr)                            \\\n  do {                                                                  \\\n    if ((expr)) {                                                       \\\n      ERROR(\"\\nFailed comparison: \" << #expr << \"\\nLine: \" << __LINE__  \\\n                                    << \"\\nFile: \" << __FILE__ << \"\\n\"); \\\n    }                                                                   \\\n  } while (false)\n\nusing Approx = Catch::Approx;\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Set a default tolerance for floating-point number comparison\n *\n * \\details\n * Catch's default (relative) tolerance for comparing floating-point numbers is\n * `std::numeric_limits<float>::%epsilon() * 100`, or roughly \\f$10^{-5}\\f$.\n * This tolerance is too loose for checking many scientific algorithms that\n * rely on double precision floating-point accuracy, so we provide a tighter\n * tighter tolerance through the `approx` static object.\n *\n * \\example\n * \\snippet Test_TestingFramework.cpp approx_test\n */\n// clang-tidy: static object creation may throw exception\nstatic const Approx approx =                                    // NOLINT\n    Approx::custom()                                            // NOLINT\n        .epsilon(std::numeric_limits<double>::epsilon() * 100)  // NOLINT\n        .scale(1.0);                                            // NOLINT\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief A wrapper around Catch's CHECK macro that checks approximate\n * equality of entries in iterable containers.  For maplike\n * containers, keys are checked for strict equality and values are\n * checked for approximate equality.\n */\n#define CHECK_ITERABLE_APPROX(a, b)                                          \\\n  do {                                                                       \\\n    INFO(__FILE__ \":\" + std::to_string(__LINE__) + \": \" #a \" == \" #b);       \\\n    check_iterable_approx<std::common_type_t<                                \\\n        std::decay_t<decltype(a)>, std::decay_t<decltype(b)>>>::apply(a, b); \\\n  } while (false)\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Same as `CHECK_ITERABLE_APPROX` with user-defined Approx.\n *  The third argument should be of type `Approx`.\n */\n#define CHECK_ITERABLE_CUSTOM_APPROX(a, b, appx)                             \\\n  do {                                                                       \\\n    INFO(__FILE__ \":\" + std::to_string(__LINE__) + \": \" #a \" == \" #b);       \\\n    check_iterable_approx<std::common_type_t<                                \\\n        std::decay_t<decltype(a)>, std::decay_t<decltype(b)>>>::apply(a, b,  \\\n                                                                      appx); \\\n  } while (false)\n\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T, typename = std::nullptr_t>\nstruct check_iterable_approx {\n  static void apply(const T& a, const T& b, const Approx& appx = approx) {\n    CHECK(a == appx(b));\n  }\n};\n\ntemplate <typename T>\nstruct check_iterable_approx<std::complex<T>, std::nullptr_t> {\n  static void apply(const std::complex<T>& a, const std::complex<T>& b,\n                    const Approx& appx = approx) {\n    check_iterable_approx<T>::apply(real(a), real(b), appx);\n    check_iterable_approx<T>::apply(imag(a), imag(b), appx);\n  }\n};\n\ntemplate <typename T>\nstruct check_iterable_approx<\n    T, Requires<not tt::is_maplike_v<T> and tt::is_iterable_v<T> and\n                not tt::is_a_v<std::unordered_set, T>>> {\n  static void apply(const T& a, const T& b, const Approx& appx = approx) {\n    CAPTURE(a);\n    CAPTURE(b);\n    auto a_it = a.begin();\n    auto b_it = b.begin();\n    CHECK(a_it != a.end());\n    CHECK(b_it != b.end());\n    while (a_it != a.end() and b_it != b.end()) {\n      check_iterable_approx<std::decay_t<decltype(*a_it)>>::apply(*a_it, *b_it,\n                                                                  appx);\n      ++a_it;\n      ++b_it;\n    }\n    {\n      INFO(\"Iterable is longer in first argument than in second argument\");\n      CHECK(a_it == a.end());\n    }\n    {\n      INFO(\"Iterable is shorter in first argument than in second argument\");\n      CHECK(b_it == b.end());\n    }\n  }\n};\n\ntemplate <typename T>\nstruct check_iterable_approx<T, Requires<tt::is_a_v<std::unordered_set, T>>> {\n  static void apply(const T& a, const T& b, const Approx& /*appx*/ = approx) {\n    // Approximate comparison of unordered sets is difficult\n    CHECK(a == b);\n  }\n};\n\ntemplate <typename T>\nstruct check_iterable_approx<\n    T, Requires<tt::is_maplike_v<T> and tt::is_iterable_v<T>>> {\n  static void apply(const T& a, const T& b, const Approx& appx = approx) {\n    CAPTURE(a);\n    CAPTURE(b);\n    for (const auto& kv : a) {\n      const auto& key = kv.first;\n      try {\n        const auto& a_value = kv.second;\n        const auto& b_value = b.at(key);\n        CAPTURE(key);\n        check_iterable_approx<std::decay_t<decltype(a_value)>>::apply(\n            a_value, b_value, appx);\n      } catch (const std::out_of_range&) {\n        INFO(\"Missing key in second container: \" << key);\n        CHECK(false);\n      }\n    }\n\n    for (const auto& kv : b) {\n      const auto& key = kv.first;\n      try {\n        a.at(key);\n        // We've checked that the values match above.\n      } catch (const std::out_of_range&) {\n        INFO(\"Missing key in first container: \" << key);\n        CHECK(false);\n      }\n    }\n  }\n};\n\n// This implementation is for a column-major matrix. It does not trivially\n// generalize to a row-major matrix because the iterator\n// `blaze::DynamicMatrix<T, SO>::cbegin(i)` traverses either a row or a\n// column, and takes its index as argument.\ntemplate <typename M>\nstruct check_iterable_approx<M, Requires<blaze::IsColumnMajorMatrix_v<M> and\n                                         blaze::IsDenseMatrix_v<M>>> {\n  static void apply(const M& a, const M& b, const Approx& appx = approx) {\n    CHECK(a.columns() == b.columns());\n    for (size_t j = 0; j < a.columns(); j++) {\n      CAPTURE(a);\n      CAPTURE(b);\n      auto a_it = a.cbegin(j);\n      auto b_it = b.cbegin(j);\n      CHECK(a_it != a.cend(j));\n      CHECK(b_it != b.cend(j));\n      while (a_it != a.cend(j) and b_it != b.cend(j)) {\n        check_iterable_approx<std::decay_t<decltype(*a_it)>>::apply(\n            *a_it, *b_it, appx);\n        ++a_it;\n        ++b_it;\n      }\n      {\n        INFO(\"Column \" << j\n                       << \" of the first matrix is longer than that of the \"\n                          \"second matrix.\");\n        CHECK(a_it == a.end(j));\n      }\n      {\n        INFO(\"Column \" << j\n                       << \" of the first matrix is shorter than that of the \"\n                          \"second matrix.\");\n        CHECK(b_it == b.end(j));\n      }\n    }\n  }\n};\n/// \\endcond\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Mark a test as checking the output with a regular expression\n *\n * \\details\n * The OUTPUT_TEST() macro should be the first line in the SPECTRE_TEST_CASE.\n * Catch requires at least one CHECK in each test to pass, so we add one in\n * case nothing but the output is checked.\n *\n * \\example\n * \\snippet Test_Printf.cpp output_test_example\n */\n#define OUTPUT_TEST() \\\n  do {                \\\n    CHECK(true);      \\\n  } while (false)\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Framework\")\n\nset(LIBRARY_SOURCES\n  Test_ActionTesting.cpp\n  Test_Pypp.cpp\n  Test_PyppAnalyticSolution.cpp\n  Test_PyppRandomValues.cpp\n  Test_TestCreation.cpp\n  Test_TestHelpers.cpp\n  Test_TestingFramework.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  SystemUtilities\n  )\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/PyppPyTests.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef test_none():\n    return None\n\n\ndef test_numeric(a, b):\n    return a * b\n\n\ndef test_vector(a, b):\n    test = list()\n    test.append(a[0] * b[0])\n    test.append(a[1] * b[1])\n    return test\n\n\ndef test_unordered_map(a, b):\n    test = {}\n    test[\"0\"] = a[\"0\"] * b[\"0\"]\n    test[\"1\"] = a[\"1\"] * b[\"1\"]\n    return test\n\n\ndef test_tuple(a, b):\n    if a[0] != 0.35 or a[1] != \"test 1\" or b[0] != \"test 2\" or b[1] != 100:\n        raise RuntimeError(\"Failed test_tuple\")\n    return 0.48, \"test 3\"\n\n\ndef test_tuple_list(a, b):\n    if a[0] != 0.35 or a[1] != \"test 1\" or b[0] != \"test 2\" or b[1] != 100:\n        raise RuntimeError(\"Failed test_tuple\")\n    return 0.48, [0.98, \"test 4\"]\n\n\ndef test_string(a):\n    if a != \"test string\":\n        raise RuntimeError(\"Failed string test\")\n    return \"back test string\"\n\n\n# These are used to check that converting to a DataVector throws\n\n\ndef two_dim_ndarray():\n    import numpy as np\n\n    return np.array([[1.0, 2.0], [3.0, 4]])\n\n\ndef ndarray_of_floats():\n    import numpy as np\n\n    return np.array([1.0, 2.0], dtype=\"float32\")\n\n\n# These are used to check converting to a Tensor works\n\n\ndef scalar_from_double():\n    return 0.8\n\n\ndef scalar_from_ndarray():\n    return np.array(0.8)\n\n\ndef vector():\n    return np.array([3.0, 4.0, 5.0, 6.0])\n\n\ndef tnsr_ia():\n    return np.array([[i + 2 * j + 1.0 for j in range(4)] for i in range(3)])\n\n\ndef tnsr_AA():\n    return np.array([[i + j + 1.0 for j in range(4)] for i in range(4)])\n\n\ndef tnsr_iaa():\n    return np.array(\n        [\n            [\n                [2.0 * (k + 1) * (j + 1) + i + 1.0 for k in range(4)]\n                for j in range(4)\n            ]\n            for i in range(3)\n        ]\n    )\n\n\ndef tnsr_aia():\n    a = np.array(\n        [\n            [\n                [2.0 * (k + 1) * (i + 1) + j + 1.5 for k in range(4)]\n                for j in range(3)\n            ]\n            for i in range(4)\n        ]\n    )\n    return a\n\n\ndef tnsr_aBcc():\n    return np.array(\n        [\n            [\n                [\n                    [3.0 * i + j + (k + 1) * (l + 1) + 1.0 for l in range(4)]\n                    for k in range(4)\n                ]\n                for j in range(4)\n            ]\n            for i in range(4)\n        ]\n    )\n\n\n# Test conversion from Tensor to numpy array works\n\n\ndef convert_scalar_to_ndarray_successful(a):\n    return bool(np.all(a == scalar_from_ndarray()))\n\n\ndef convert_scalar_to_double_unsuccessful(a):\n    return not isinstance(scalar_from_double(), type(a))\n\n\ndef convert_vector_successful(a):\n    return bool(np.all(a == vector()))\n\n\ndef convert_tnsr_ia_successful(a):\n    return bool(np.all(a == tnsr_ia()))\n\n\ndef convert_tnsr_AA_successful(a):\n    return bool(np.all(a == tnsr_AA()))\n\n\ndef convert_tnsr_iaa_successful(a):\n    return bool(np.all(a == tnsr_iaa()))\n\n\ndef convert_tnsr_aia_successful(a):\n    return bool(np.all(a == tnsr_aia()))\n\n\ndef convert_tnsr_aBcc_successful(a):\n    return bool(np.all(a == tnsr_aBcc()))\n\n\ndef test_function_of_time(x, t):\n    return 2 * x[0] + x[1] - x[2] - t\n\n\n# Used to test tensor of datavectors\ndef identity(a):\n    return a\n\n\ndef test_einsum(scalar, t_A, t_ia, t_AA, t_iaa):\n    return scalar * np.einsum(\"a,ia->i\", t_A, t_ia) + np.einsum(\n        \"ab, iab->i\", t_AA, t_iaa\n    )\n\n\ndef check_single_not_null0(t0):\n    return t0 + 5.0\n\n\ndef check_single_not_null1(t0, t1):\n    return t0 + t1\n\n\ndef check_single_not_null2(t0, t1):\n    return np.sqrt(t0) + 1.0 / np.sqrt(-t1)\n\n\ndef check_double_not_null0_result0(t0):\n    return t0 + 5.0\n\n\ndef check_double_not_null0_result1(t0):\n    return 2.0 * t0 + 5.0\n\n\ndef check_double_not_null1_result0(t0, t1):\n    return t0 + t1\n\n\ndef check_double_not_null1_result1(t0, t1):\n    return 2.0 * t0 + t1\n\n\n# [python_two_not_null]\ndef check_double_not_null2_result0(t0, t1):\n    return np.sqrt(t0) + 1.0 / np.sqrt(-t1)\n\n\ndef check_double_not_null2_result1(t0, t1):\n    return 2.0 * t0 + t1\n    # [python_two_not_null]\n\n\ndef check_by_value0(t0):\n    return t0 + 5.0\n\n\ndef check_by_value1(t0, t1):\n    return t0 + t1\n\n\ndef check_by_value2(t0, t1):\n    return np.sqrt(t0) + 1.0 / np.sqrt(-t1)\n\n\n# the below are used both by value and by not_null checks\ndef check_by_value1_class(t0, a):\n    return t0 + 5.0 * a\n\n\ndef check_by_value2_class(t0, a, b):\n    return t0 + 5.0 * a + b\n\n\ndef check_by_value3_class(t0, a, b, c):\n    return t0 + 5.0 * a + b + c[0] - 2.0 * c[1] - c[2]\n\n\ndef check2_by_value1_class(t0, t1, a):\n    return t0 + t1 + 5.0 * a\n\n\ndef check2_by_value2_class(t0, t1, a, b):\n    return t0 + 5.0 * a + t1 * b\n\n\ndef check2_by_value3_class(t0, t1, a, b, c):\n    return t0 * c[0] + 5.0 * a + t1 * b + c[1] - c[2]\n\n\ndef check2_by_value1_class1(t0, t1, a):\n    return 2.0 * t0 + t1 + 5.0 * a\n\n\ndef check2_by_value2_class1(t0, t1, a, b):\n    return 2.0 * t0 + 5.0 * a + t1 * b\n\n\ndef permute_array(a):\n    return [a[2], a[0], a[1]]\n\n\ndef check_solution_scalar(x, t, a, b):\n    return np.dot(x, b) + a - t\n\n\ndef check_solution_vector(x, t, a, b):\n    # b is passed in as a list so must be explicitly converted to an array\n    return a * x - t * np.array(b)\n\n\n# returns a complex scalar\ndef mixed_complex_real_function_1(spin_weighted, complex_tensor, real_tensor):\n    return spin_weighted * complex_tensor[0] / real_tensor[1]\n\n\n# returns a real rank one tensor of dimension 2\ndef mixed_complex_real_function_2(spin_weighted, complex_tensor, real_tensor):\n    return np.array(\n        [\n            np.real(spin_weighted * complex_tensor[0] * real_tensor[1]),\n            np.imag(complex_tensor[1] * real_tensor[0] / spin_weighted),\n        ]\n    )\n\n\ndef add_scalars(a, b):\n    if a is None:\n        return b\n    if b is None:\n        return a\n    return a + b\n\n\ndef tuple_of_tensor_not_tuple(a):\n    return np.asarray([a])\n\n\ndef tuple_of_tensor_wrong_size(a):\n    return (np.asarray([a]), np.asarray([a]), np.asarray([a]))\n\n\ndef tuple_of_tensor_failed_to_convert0(a):\n    return (np.asarray([a]), np.asarray([a]))\n\n\ndef tuple_of_tensor_failed_to_convert1(a):\n    return (np.asarray(a), np.asarray(a))\n\n\ndef tuple_of_tensor_works(a):\n    return (np.asarray(2.0 * a), np.asarray([5.0 * a]))\n\n\ndef tagged_tuple_of_tensor_wrong_type(a):\n    return 0\n\n\ndef tagged_tuple_of_tensor_missing_tag_too_small(a):\n    return {\"Var1\": np.asarray(2.0 * a), \"Var3\": 10}\n\n\ndef tagged_tuple_of_tensor_missing(a):\n    return {\n        \"Var1\": np.asarray(2.0 * a),\n        \"Var2b\": np.asarray([5.0 * a]),\n        \"Var3\": 10,\n    }\n\n\ndef tagged_tuple_of_tensor_convert_var1(a):\n    return {\"Var1\": np.asarray([a]), \"Var2\": np.asarray([5.0 * a]), \"Var3\": 10}\n\n\ndef tagged_tuple_of_tensor_convert_var2(a):\n    return {\"Var1\": np.asarray(2.0 * a), \"Var2\": np.asarray(a), \"Var3\": 10}\n\n\ndef tagged_tuple_of_tensor_convert_var3(a):\n    return {\n        \"Var1\": np.asarray(2.0 * a),\n        \"Var2\": np.asarray([5.0 * a]),\n        \"Var3\": np.asarray([5.0 * a]),\n    }\n\n\ndef tagged_tuple_of_tensor_works(a):\n    return {\n        \"Var1\": np.asarray(2.0 * a),\n        \"Var2\": np.asarray([5.0 * a]),\n        \"Var3\": 10,\n    }\n\n\ndef tagged_tuple_of_tensor_works_extra_dict(a):\n    return {\n        \"Var1\": np.asarray(2.0 * a),\n        \"Var2\": np.asarray([5.0 * a]),\n        \"Var3\": 10,\n        \"Var4\": 7.3,\n        \"Var5\": np.asarray(9.0 * a),\n    }\n\n\ndef custom_conversion(t, a):\n    return t * a\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/Test_ActionTesting.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace TestSimpleAndThreadedActions {\nstruct MockSingleton {\n  using chare_type = ActionTesting::MockSingletonChare;\n};\nstruct MockArray {\n  using chare_type = ActionTesting::MockArrayChare;\n};\nstruct MockGroup {\n  using chare_type = ActionTesting::MockGroupChare;\n};\nstruct MockNodegroup {\n  using chare_type = ActionTesting::MockNodeGroupChare;\n};\n\nstatic_assert(Parallel::is_singleton_v<MockSingleton>);\nstatic_assert(Parallel::is_array_v<MockArray>);\nstatic_assert(Parallel::is_group_v<MockGroup>);\nstatic_assert(Parallel::is_nodegroup_v<MockNodegroup>);\n\nstruct simple_action_a;\nstruct simple_action_a_mock;\nstruct simple_action_c;\nstruct simple_action_c_mock;\n\nstruct threaded_action_b;\nstruct threaded_action_b_mock;\n\n// [tags for const global cache]\nstruct ValueTag : db::SimpleTag {\n  using type = int;\n  static std::string name() { return \"ValueTag\"; }\n};\n\nstruct PassedToB : db::SimpleTag {\n  using type = double;\n  static std::string name() { return \"PassedToB\"; }\n};\n// [tags for const global cache]\n\ntemplate <typename Metavariables>\nstruct component_for_simple_action_mock {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 db::AddSimpleTags<ValueTag, PassedToB>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n\n  // [simple action replace]\n  using replace_these_simple_actions =\n      tmpl::list<simple_action_a, simple_action_c, threaded_action_b>;\n  using with_these_simple_actions =\n      tmpl::list<simple_action_a_mock, simple_action_c_mock,\n                 threaded_action_b_mock>;\n  // [simple action replace]\n  // [threaded action replace]\n  using replace_these_threaded_actions = tmpl::list<threaded_action_b>;\n  using with_these_threaded_actions = tmpl::list<threaded_action_b_mock>;\n  // [threaded action replace]\n};\n\nstruct simple_action_a_mock {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl::list_contains_v<DbTagsList, ValueTag>> = nullptr>\n  static void apply(db::DataBox<DbTagsList>& box,  // NOLINT\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const int value) {\n    db::mutate<ValueTag>(\n        [&value](const gsl::not_null<int*> value_box) { *value_box = value; },\n        make_not_null(&box));\n  }\n};\n\nstruct simple_action_b {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl::list_contains_v<DbTagsList, PassedToB>> = nullptr>\n  static void apply(db::DataBox<DbTagsList>& box,                 // NOLINT\n                    Parallel::GlobalCache<Metavariables>& cache,  // NOLINT\n                    const ArrayIndex& /*array_index*/, const double to_call) {\n    // simple_action_b is the action that we are testing, but it calls some\n    // other actions that we don't want to test. Those are mocked where the body\n    // of the mock actions records something in the DataBox that we check to\n    // verify the mock actions were actually called.\n    //\n    // We do some \"work\" here by updating the `PassedToB` tag in the DataBox.\n    db::mutate<PassedToB>(\n        [&to_call](const gsl::not_null<double*> passed_to_b) {\n          *passed_to_b = to_call;\n        },\n        make_not_null(&box));\n    if (to_call == 0) {\n      Parallel::simple_action<simple_action_a>(\n          Parallel::get_parallel_component<\n              component_for_simple_action_mock<Metavariables>>(cache),\n          11);\n    } else if (to_call == 1) {\n      Parallel::simple_action<simple_action_a>(\n          Parallel::get_parallel_component<\n              component_for_simple_action_mock<Metavariables>>(cache),\n          14);\n    } else if (to_call == 2) {\n      Parallel::simple_action<simple_action_c>(\n          Parallel::get_parallel_component<\n              component_for_simple_action_mock<Metavariables>>(cache));\n    } else if (to_call == 3) {\n      Parallel::simple_action<simple_action_c>(\n          Parallel::get_parallel_component<\n              component_for_simple_action_mock<Metavariables>>(cache)[0]);\n    } else {\n      ERROR(\"to_call should be 0, 1, 2, or 3\");\n    }\n  }\n};\n\nstruct simple_action_c_mock {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl::list_contains_v<DbTagsList, ValueTag>> = nullptr>\n  static void apply(db::DataBox<DbTagsList>& box,  // NOLINT\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    db::mutate<ValueTag>(\n        [](const gsl::not_null<int*> value_box) { *value_box = 25; },\n        make_not_null(&box));\n  }\n};\n\nstruct threaded_action_a {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl::list_contains_v<DbTagsList, ValueTag>> = nullptr>\n  static void apply(db::DataBox<DbTagsList>& box,  // NOLINT\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/) {\n    db::mutate<ValueTag>(\n        [](const gsl::not_null<int*> value_box) { *value_box = 35; },\n        make_not_null(&box));\n  }\n};\n\nstruct threaded_action_b_mock {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl::list_contains_v<DbTagsList, ValueTag>> = nullptr>\n  static void apply(db::DataBox<DbTagsList>& box,  // NOLINT\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> node_lock,\n                    const int tag) {\n    node_lock->lock();\n    db::mutate<ValueTag>(\n        [tag](const gsl::not_null<int*> value_box) { *value_box = tag; },\n        make_not_null(&box));\n    node_lock->unlock();\n  }\n};\n\nstruct SimpleActionMockMetavariables {\n  using component_list = tmpl::list<\n      component_for_simple_action_mock<SimpleActionMockMetavariables>>;\n};\n\ntemplate <typename Metavariables>\nstruct component_for_global_cache_tags {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\nstruct MockMetavariablesWithGlobalCacheTags {\n  using component_list = tmpl::list<\n      component_for_global_cache_tags<MockMetavariablesWithGlobalCacheTags>>;\n  // [const global cache metavars]\n  using const_global_cache_tags = tmpl::list<ValueTag, PassedToB>;\n  // [const global cache metavars]\n};\n\nvoid test_mock_runtime_system_constructors() {\n  using metavars = MockMetavariablesWithGlobalCacheTags;\n  using component = component_for_global_cache_tags<metavars>;\n  // Test whether we can construct with tagged tuples in different orders.\n  // [constructor const global cache tags known]\n  ActionTesting::MockRuntimeSystem<metavars> runner1{{3, 7.0}};\n  // [constructor const global cache tags known]\n  // [constructor const global cache tags unknown]\n  ActionTesting::MockRuntimeSystem<metavars> runner2{\n      tuples::TaggedTuple<PassedToB, ValueTag>{7.0, 3}};\n  // [constructor const global cache tags unknown]\n  ActionTesting::emplace_component<component>(&runner1, 0);\n  ActionTesting::emplace_component<component>(&runner2, 0);\n  const auto& cache1 = ActionTesting::cache<component>(runner1, 0_st);\n  const auto& cache2 = ActionTesting::cache<component>(runner2, 0_st);\n  CHECK(Parallel::get<ValueTag>(cache1) == Parallel::get<ValueTag>(cache2));\n  CHECK(Parallel::get<PassedToB>(cache1) == Parallel::get<PassedToB>(cache2));\n  CHECK(Parallel::get<ValueTag>(cache1) == 3);\n  CHECK(Parallel::get<PassedToB>(cache2) == 7);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ActionTesting.MockSimpleAction\", \"[Unit]\") {\n  using metavars = SimpleActionMockMetavariables;\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}};\n  ActionTesting::emplace_component_and_initialize<\n      component_for_simple_action_mock<metavars>>(&runner, 0, {0, -1});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // [get databox]\n  const auto& box =\n      ActionTesting::get_databox<component_for_simple_action_mock<metavars>>(\n          runner, 0);\n  // [get databox]\n  CHECK(db::get<PassedToB>(box) == -1);\n  runner.simple_action<component_for_simple_action_mock<metavars>,\n                       simple_action_b>(0, 0);\n  REQUIRE(not runner.is_simple_action_queue_empty<\n              component_for_simple_action_mock<metavars>>(0));\n  runner\n      .invoke_queued_simple_action<component_for_simple_action_mock<metavars>>(\n          0);\n  CHECK(db::get<PassedToB>(box) == 0);\n  CHECK(db::get<ValueTag>(box) == 11);\n  // [invoke simple action]\n  ActionTesting::simple_action<component_for_simple_action_mock<metavars>,\n                               simple_action_b>(make_not_null(&runner), 0, 2);\n  // [invoke simple action]\n  REQUIRE(not\n          // [simple action queue empty]\n          ActionTesting::is_simple_action_queue_empty<\n              component_for_simple_action_mock<metavars>>(runner, 0)\n          // [simple action queue empty]\n  );\n  // [invoke queued simple action]\n  ActionTesting::invoke_queued_simple_action<\n      component_for_simple_action_mock<metavars>>(make_not_null(&runner), 0);\n  // [invoke queued simple action]\n  CHECK(db::get<PassedToB>(box) == 2);\n  CHECK(db::get<ValueTag>(box) == 25);\n  REQUIRE(runner.is_simple_action_queue_empty<\n          component_for_simple_action_mock<metavars>>(0));\n  runner.queue_simple_action<component_for_simple_action_mock<metavars>,\n                             simple_action_b>(0, 1);\n  runner\n      .invoke_queued_simple_action<component_for_simple_action_mock<metavars>>(\n          0);\n  REQUIRE(not runner.is_simple_action_queue_empty<\n              component_for_simple_action_mock<metavars>>(0));\n  runner\n      .invoke_queued_simple_action<component_for_simple_action_mock<metavars>>(\n          0);\n  CHECK(db::get<PassedToB>(box) == 1);\n  CHECK(db::get<ValueTag>(box) == 14);\n  runner.simple_action<component_for_simple_action_mock<metavars>,\n                       simple_action_b>(0, 3);\n\n  CHECK_FALSE(runner.is_simple_action_queue_empty<\n              component_for_simple_action_mock<metavars>>(0));\n  runner\n      .invoke_queued_simple_action<component_for_simple_action_mock<metavars>>(\n          0);\n  CHECK(runner.is_simple_action_queue_empty<\n        component_for_simple_action_mock<metavars>>(0));\n  CHECK(db::get<PassedToB>(box) == 3);\n  CHECK(db::get<ValueTag>(box) == 25);\n\n  // [invoke threaded action]\n  ActionTesting::threaded_action<component_for_simple_action_mock<metavars>,\n                                 threaded_action_a>(make_not_null(&runner), 0);\n  // [invoke threaded action]\n  CHECK(db::get<ValueTag>(box) == 35);\n\n  ActionTesting::threaded_action<component_for_simple_action_mock<metavars>,\n                                 threaded_action_b>(make_not_null(&runner), 0,\n                                                    -50);\n  CHECK(db::get<ValueTag>(box) == -50);\n\n  test_mock_runtime_system_constructors();\n}\n}  // namespace TestSimpleAndThreadedActions\n\nnamespace TestIsRetrievable {\nstruct DummyTimeTag : db::SimpleTag {\n  static std::string name() { return \"DummyTime\"; }\n  using type = int;\n};\n\nstruct Action0 {\n  using simple_tags = tmpl::list<DummyTimeTag>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 6);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<Action0>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.ActionTesting.IsRetrievable\", \"[Unit]\") {\n  using metavars = Metavariables;\n  using component = Component<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}};\n  ActionTesting::emplace_component<component>(&runner, 0);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  CHECK(ActionTesting::tag_is_retrievable<component, DummyTimeTag>(runner, 0));\n  // Runs Action0\n  runner.next_action<component>(0);\n  CHECK(\n      // [tag is retrievable]\n      ActionTesting::tag_is_retrievable<component, DummyTimeTag>(runner, 0)\n      // [tag is retrievable]\n  );\n  CHECK(ActionTesting::get_databox_tag<component, DummyTimeTag>(runner, 0) ==\n        6);\n}\n}  // namespace TestIsRetrievable\n\nnamespace InboxTags {\nnamespace Tags {\nstruct ValueTag : public Parallel::InboxInserters::Value<ValueTag> {\n  using temporal_id = size_t;\n  using type = std::unordered_map<temporal_id, int>;\n};\n}  // namespace Tags\n\nnamespace Actions {\nstruct SendValue {\n  // Put inbox_tags in the send action instead of the receive action since all\n  // we want to do is test the `ActionTesting::get_inbox_tag` function.\n  using inbox_tags = tmpl::list<Tags::ValueTag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Parallel::receive_data<Tags::ValueTag>(\n        Parallel::get_parallel_component<ParallelComponent>(cache)[0], 1_st,\n        23);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Testing,\n                                        tmpl::list<Actions::SendValue>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.ActionTesting.GetInboxTags\", \"[Unit]\") {\n  using metavars = Metavariables;\n  using component = Component<metavars>;\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}};\n  ActionTesting::emplace_component<component>(&runner, 0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  CHECK(ActionTesting::get_inbox_tag<component, Tags::ValueTag>(\n            make_not_null(&runner), 0)\n            .empty());\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(\n      // [const get inbox tags]\n      ActionTesting::get_inbox_tag<component, Tags::ValueTag>(runner, 0).at(1)\n      // [const get inbox tags]\n      == 23);\n  // [get inbox tags]\n  ActionTesting::get_inbox_tag<component, Tags::ValueTag>(\n      make_not_null(&runner), 0)\n      .clear();\n  // [get inbox tags]\n  CHECK(ActionTesting::get_inbox_tag<component, Tags::ValueTag>(\n            make_not_null(&runner), 0)\n            .empty());\n}\n}  // namespace InboxTags\n\nnamespace TestComponentMocking {\nstruct ValueTag : db::SimpleTag {\n  using type = int;\n  static std::string name() { return \"ValueTag\"; }\n};\n\ntemplate <typename Metavariables>\nstruct ComponentA {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct ComponentB;\n\n// [mock component b]\ntemplate <typename Metavariables>\nstruct ComponentBMock {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using component_being_mocked = ComponentB<Metavariables>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 db::AddSimpleTags<ValueTag>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n// [mock component b]\n\nstruct ActionCalledOnComponentB {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl::list_contains_v<DbTagsList, ValueTag>> = nullptr>\n  static void apply(db::DataBox<DbTagsList>& box,                     // NOLINT\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,  // NOLINT\n                    const ArrayIndex& /*array_index*/) {\n    db::mutate<ValueTag>([](const gsl::not_null<int*> value) { *value = 5; },\n                         make_not_null(&box));\n  }\n};\n\n// [action call component b]\nstruct CallActionOnComponentB {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& /*box*/,             // NOLINT\n                    Parallel::GlobalCache<Metavariables>& cache,  // NOLINT\n                    const ArrayIndex& /*array_index*/) {\n    Parallel::simple_action<ActionCalledOnComponentB>(\n        Parallel::get_parallel_component<ComponentB<Metavariables>>(cache));\n  }\n};\n// [action call component b]\n\nstruct Metavariables {\n  using component_list =\n      tmpl::list<ComponentA<Metavariables>, ComponentBMock<Metavariables>>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.ActionTesting.MockComponent\", \"[Unit]\") {\n  using metavars = Metavariables;\n  using component_a = ComponentA<metavars>;\n  using component_b_mock = ComponentBMock<metavars>;\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}};\n  ActionTesting::emplace_component<component_a>(&runner, 0);\n  // [initialize component b]\n  ActionTesting::emplace_component_and_initialize<\n      ComponentBMock<Metavariables>>(&runner, 0, {0});\n  // [initialize component b]\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  CHECK(ActionTesting::get_databox_tag<component_b_mock, ValueTag>(runner, 0) ==\n        0);\n  ActionTesting::queue_simple_action<component_a, CallActionOnComponentB>(\n      make_not_null(&runner), 0);\n  ActionTesting::invoke_queued_simple_action<component_a>(\n      make_not_null(&runner), 0);\n  // [component b mock checks]\n  CHECK(not ActionTesting::is_simple_action_queue_empty<component_b_mock>(\n      runner, 0));\n  ActionTesting::invoke_queued_simple_action<component_b_mock>(\n      make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_databox_tag<component_b_mock, ValueTag>(runner, 0) ==\n        5);\n  // [component b mock checks]\n}\n}  // namespace TestComponentMocking\n\nnamespace TestNodesAndCores {\n\nstruct ValueTag : db::SimpleTag {\n  using type = int;\n};\n\nstruct ValueTagSizeT : db::SimpleTag {\n  using type = size_t;\n};\n\ntemplate <typename Metavariables>\nstruct ComponentA {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 db::AddSimpleTags<ValueTag, ValueTagSizeT>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\ntemplate <typename MyProxy, typename ArrayIndex>\nauto get_local(MyProxy& my_proxy, const ArrayIndex& array_index) {\n  if constexpr (Parallel::is_group_proxy<std::decay_t<MyProxy>>::value or\n                Parallel::is_node_group_proxy<std::decay_t<MyProxy>>::value) {\n    return Parallel::local_branch(my_proxy[array_index]);\n  } else {\n    return Parallel::local(my_proxy[array_index]);\n  }\n}\n\nstruct MyProc {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    return Parallel::my_proc<RetType>(*get_local(my_proxy, array_index));\n  }\n};\n\nstruct MyNode {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    return Parallel::my_node<RetType>(*get_local(my_proxy, array_index));\n  }\n};\n\nstruct LocalRank {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    return Parallel::my_local_rank<RetType>(*get_local(my_proxy, array_index));\n  }\n};\n\nstruct NumProcs {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    return Parallel::number_of_procs<RetType>(\n        *get_local(my_proxy, array_index));\n  }\n};\n\nstruct NumNodes {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    return Parallel::number_of_nodes<RetType>(\n        *get_local(my_proxy, array_index));\n  }\n};\n\ntemplate <int NodeIndex>\nstruct ProcsOnNode {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    const RetType value_from_int = Parallel::procs_on_node<RetType>(\n        static_cast<int>(NodeIndex), *Parallel::local(my_proxy[array_index]));\n    const RetType value_from_size_t = Parallel::procs_on_node<RetType>(\n        static_cast<size_t>(NodeIndex),\n        *Parallel::local(my_proxy[array_index]));\n    CHECK(value_from_int == value_from_size_t);\n    return value_from_int;\n  }\n};\n\ntemplate <int NodeIndex>\nstruct FirstProcOnNode {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    const RetType value_from_int = Parallel::first_proc_on_node<RetType>(\n        static_cast<int>(NodeIndex), *Parallel::local(my_proxy[array_index]));\n    const RetType value_from_size_t = Parallel::first_proc_on_node<RetType>(\n        static_cast<size_t>(NodeIndex),\n        *Parallel::local(my_proxy[array_index]));\n    CHECK(value_from_int == value_from_size_t);\n    return value_from_int;\n  }\n};\n\ntemplate <int ProcIndex>\nstruct NodeOf {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    const RetType value_from_int = Parallel::node_of<RetType>(\n        static_cast<int>(ProcIndex), *Parallel::local(my_proxy[array_index]));\n    const RetType value_from_size_t =\n        Parallel::node_of<RetType>(static_cast<size_t>(ProcIndex),\n                                   *Parallel::local(my_proxy[array_index]));\n    CHECK(value_from_int == value_from_size_t);\n    return value_from_int;\n  }\n};\n\ntemplate <int ProcIndex>\nstruct LocalRankOf {\n  template <typename MyProxy, typename ArrayIndex, typename RetType>\n  static auto f(MyProxy& my_proxy, const ArrayIndex& array_index) -> RetType {\n    const RetType value_from_int = Parallel::local_rank_of<RetType>(\n        static_cast<int>(ProcIndex), *Parallel::local(my_proxy[array_index]));\n    const RetType value_from_size_t = Parallel::local_rank_of<RetType>(\n        static_cast<size_t>(ProcIndex),\n        *Parallel::local(my_proxy[array_index]));\n    CHECK(value_from_int == value_from_size_t);\n    return value_from_int;\n  }\n};\n\ntemplate <typename Tag, typename Functor>\nstruct ActionSetValueTo {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl::list_contains_v<DbTagsList, Tag>> = nullptr>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    auto& my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    using ProxyType = std::decay_t<decltype(my_proxy)>;\n    using T = typename Tag::type;\n    // We must specify all templates here explicitly otherwise it won't build\n    T function_value =\n        Functor::template f<ProxyType, ArrayIndex, T>(my_proxy, array_index);\n    db::mutate<Tag>(\n        [&function_value](const gsl::not_null<T*> value) {\n          *value = function_value;\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct MetavariablesOneComponent {\n  using component_list = tmpl::list<ComponentA<MetavariablesOneComponent>>;\n};\n\nvoid test_parallel_info_functions() {\n  using metavars = MetavariablesOneComponent;\n  using component_a = ComponentA<metavars>;\n\n  // Choose 2 nodes with 3 cores on first node and 2 cores on second node.\n  const int num_nodes = 2;\n  const int procs_node_0 = 3;\n  const int procs_node_1 = 2;\n  const int num_procs = procs_node_0 + procs_node_1;\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {},\n      {},\n      {static_cast<size_t>(procs_node_0), static_cast<size_t>(procs_node_1)}};\n\n  // Choose array indices by hand, not in simple order, and choose\n  // arbitrary initial values.\n  ActionTesting::emplace_array_component_and_initialize<component_a>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0,\n      {-1, 101_st});\n  ActionTesting::emplace_array_component_and_initialize<component_a>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{1}, 2,\n      {-2, 102_st});\n  ActionTesting::emplace_array_component_and_initialize<component_a>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{2}, 4,\n      {-3, 103_st});\n  ActionTesting::emplace_array_component_and_initialize<component_a>(\n      &runner, ActionTesting::NodeId{1}, ActionTesting::LocalCoreId{0}, 1,\n      {-4, 104_st});\n  ActionTesting::emplace_array_component_and_initialize<component_a>(\n      &runner, ActionTesting::NodeId{1}, ActionTesting::LocalCoreId{1}, 3,\n      {-5, 105_st});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Check that initial values are correct.\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTag>(runner, 0) == -1);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTag>(runner, 1) == -4);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTag>(runner, 2) == -2);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTag>(runner, 3) == -5);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTag>(runner, 4) == -3);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTagSizeT>(runner, 0) ==\n        101);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTagSizeT>(runner, 1) ==\n        104);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTagSizeT>(runner, 2) ==\n        102);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTagSizeT>(runner, 3) ==\n        105);\n  CHECK(ActionTesting::get_databox_tag<component_a, ValueTagSizeT>(runner, 4) ==\n        103);\n\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<ValueTag, MyProc>>(\n        make_not_null(&runner), i);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<ValueTagSizeT, MyProc>>(\n        make_not_null(&runner), i);\n  }\n  using tag_list = tmpl::list<ValueTag, ValueTagSizeT>;\n  // Should all be set to proc (which Mark computed by hand)\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 0) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 1) == 3);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 2) == 1);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 3) == 4);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 4) == 2);\n  });\n\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<ValueTag, MyNode>>(\n        make_not_null(&runner), i);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<ValueTagSizeT, MyNode>>(\n        make_not_null(&runner), i);\n  }\n  // Should all be set to node (which Mark computed by hand)\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 0) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 1) == 1);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 2) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 3) == 1);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 4) == 0);\n  });\n\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<ValueTag, LocalRank>>(\n        make_not_null(&runner), i);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<ValueTagSizeT, LocalRank>>(\n        make_not_null(&runner), i);\n  }\n  // Should all be set to local rank (which Mark computed by hand)\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 0) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 1) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 2) == 1);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 3) == 1);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 4) == 2);\n  });\n\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    for (size_t i = 0; i < 5; ++i) {\n      ActionTesting::simple_action<component_a,\n                                   ActionSetValueTo<tag, NumProcs>>(\n          make_not_null(&runner), i);\n      CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, i) == 5);\n    }\n    for (size_t i = 0; i < 5; ++i) {\n      ActionTesting::simple_action<component_a,\n                                   ActionSetValueTo<tag, NumNodes>>(\n          make_not_null(&runner), i);\n      CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, i) == 2);\n    }\n  });\n\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    // procs_on_node for the 2 nodes.\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, ProcsOnNode<0>>>(\n        make_not_null(&runner), 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 0) == 3);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, ProcsOnNode<1>>>(\n        make_not_null(&runner), 2);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 2) == 2);\n\n    // first_proc_on_node for the 2 nodes.\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, FirstProcOnNode<0>>>(\n        make_not_null(&runner), 1);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 1) == 0);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, FirstProcOnNode<1>>>(\n        make_not_null(&runner), 3);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 3) == 3);\n  });\n\n  // node_of\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    ActionTesting::simple_action<component_a, ActionSetValueTo<tag, NodeOf<0>>>(\n        make_not_null(&runner), 0);\n    ActionTesting::simple_action<component_a, ActionSetValueTo<tag, NodeOf<1>>>(\n        make_not_null(&runner), 1);\n    ActionTesting::simple_action<component_a, ActionSetValueTo<tag, NodeOf<2>>>(\n        make_not_null(&runner), 2);\n    ActionTesting::simple_action<component_a, ActionSetValueTo<tag, NodeOf<3>>>(\n        make_not_null(&runner), 3);\n    ActionTesting::simple_action<component_a, ActionSetValueTo<tag, NodeOf<4>>>(\n        make_not_null(&runner), 4);\n  });\n  // Check if set to values that Mark computed by hand.\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 0) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 1) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 2) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 3) == 1);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 4) == 1);\n  });\n\n  // local_rank_of\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, LocalRankOf<0>>>(\n        make_not_null(&runner), 0);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, LocalRankOf<1>>>(\n        make_not_null(&runner), 1);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, LocalRankOf<2>>>(\n        make_not_null(&runner), 2);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, LocalRankOf<3>>>(\n        make_not_null(&runner), 3);\n    ActionTesting::simple_action<component_a,\n                                 ActionSetValueTo<tag, LocalRankOf<4>>>(\n        make_not_null(&runner), 4);\n  });\n  // Check if set to values that Mark computed by hand.\n  tmpl::for_each<tag_list>([&runner](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 0) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 1) == 1);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 2) == 2);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 3) == 0);\n    CHECK(ActionTesting::get_databox_tag<component_a, tag>(runner, 4) == 1);\n  });\n\n  // Check the parallel info functions of the GlobalCache in the testing\n  // framework\n  auto& cache = ActionTesting::cache<component_a>(runner, 0_st);\n  CHECK(cache.number_of_procs() == num_procs);\n  CHECK(cache.number_of_nodes() == num_nodes);\n  CHECK(cache.procs_on_node(0) == procs_node_0);\n  CHECK(cache.procs_on_node(1) == procs_node_1);\n  CHECK(cache.first_proc_on_node(0) == 0);\n  CHECK(cache.first_proc_on_node(1) == procs_node_0);\n  CHECK(Parallel::number_of_procs<int>(cache) == num_procs);\n  CHECK(Parallel::number_of_nodes<int>(cache) == num_nodes);\n  CHECK(Parallel::procs_on_node<int>(0, cache) == procs_node_0);\n  CHECK(Parallel::procs_on_node<int>(1, cache) == procs_node_1);\n  CHECK(Parallel::first_proc_on_node<int>(0, cache) == 0);\n  CHECK(Parallel::first_proc_on_node<int>(1, cache) == procs_node_0);\n  for (int i = 0; i < num_procs; i++) {\n    CHECK(cache.node_of(i) == (i < procs_node_0 ? 0 : 1));\n    CHECK(cache.local_rank_of(i) == (i < procs_node_0 ? i : i - procs_node_0));\n    CHECK(Parallel::node_of<int>(i, cache) == (i < procs_node_0 ? 0 : 1));\n    CHECK(Parallel::local_rank_of<int>(i, cache) ==\n          (i < procs_node_0 ? i : i - procs_node_0));\n  }\n  for (int i = 0; i < num_procs; i++) {\n    auto& local_cache =\n        ActionTesting::cache<component_a>(runner, static_cast<size_t>(i));\n    auto& proxy = Parallel::get_parallel_component<component_a>(local_cache);\n    auto& local_obj = *Parallel::local(proxy[static_cast<size_t>(i)]);\n    const int my_proc = Parallel::my_proc<int>(local_obj);\n    CHECK(local_cache.my_proc() == my_proc);\n    CHECK(local_cache.my_node() == (my_proc < procs_node_0 ? 0 : 1));\n    CHECK(local_cache.my_local_rank() ==\n          (my_proc < procs_node_0 ? my_proc : my_proc - procs_node_0));\n    CHECK(Parallel::my_proc<int>(local_cache) == my_proc);\n    CHECK(Parallel::my_node<int>(local_cache) ==\n          (my_proc < procs_node_0 ? 0 : 1));\n    CHECK(Parallel::my_local_rank<int>(local_cache) ==\n          (my_proc < procs_node_0 ? my_proc : my_proc - procs_node_0));\n  }\n}\n\ntemplate <typename Metavariables>\nstruct GroupComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 db::AddSimpleTags<ValueTag>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\nstruct MetavariablesGroupComponent {\n  using component_list =\n      tmpl::list<GroupComponent<MetavariablesGroupComponent>>;\n};\n\nvoid test_group_emplace() {\n  using metavars = MetavariablesGroupComponent;\n  using component = GroupComponent<metavars>;\n\n  // Choose 2 nodes with 3 cores on first node and 2 cores on second node.\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}, {}, {3, 2}};\n\n  ActionTesting::emplace_group_component_and_initialize<component>(&runner,\n                                                                   {-3});\n\n  // Check initial values for all components of the group.\n  for (size_t i = 0; i < 5; ++i) {\n    CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, i) == -3);\n  }\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Number of procs should be 5 for all indices.\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::simple_action<component,\n                                 ActionSetValueTo<ValueTag, NumProcs>>(\n        make_not_null(&runner), i);\n    CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, i) == 5);\n  }\n\n  // Number of nodes should be 2 for all indices.\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::simple_action<component,\n                                 ActionSetValueTo<ValueTag, NumNodes>>(\n        make_not_null(&runner), i);\n    CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, i) == 2);\n  }\n\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::simple_action<component, ActionSetValueTo<ValueTag, MyProc>>(\n        make_not_null(&runner), i);\n  }\n\n  // Should all be set to proc (which Mark computed by hand)\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 0) == 0);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 1) == 1);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 2) == 2);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 3) == 3);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 4) == 4);\n\n  for (size_t i = 0; i < 5; ++i) {\n    ActionTesting::simple_action<component, ActionSetValueTo<ValueTag, MyNode>>(\n        make_not_null(&runner), i);\n  }\n  // Should all be set to node (which Mark computed by hand)\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 0) == 0);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 1) == 0);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 2) == 0);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 3) == 1);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 4) == 1);\n}\n\ntemplate <typename Metavariables>\nstruct NodeGroupComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 db::AddSimpleTags<ValueTag>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\nstruct MetavariablesNodeGroupComponent {\n  using component_list =\n      tmpl::list<NodeGroupComponent<MetavariablesNodeGroupComponent>>;\n};\n\nvoid test_nodegroup_emplace() {\n  using metavars = MetavariablesNodeGroupComponent;\n  using component = NodeGroupComponent<metavars>;\n\n  // Choose 2 nodes with 3 cores on first node and 2 cores on second node.\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}, {}, {3, 2}};\n\n  ActionTesting::emplace_nodegroup_component_and_initialize<component>(&runner,\n                                                                       {-3});\n\n  // Check initial values for all components of the nodegroup.\n  for (size_t i = 0; i < 2; ++i) {\n    CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, i) == -3);\n  }\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Number of procs should be 5 for all indices.\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::simple_action<component,\n                                 ActionSetValueTo<ValueTag, NumProcs>>(\n        make_not_null(&runner), i);\n    CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, i) == 5);\n  }\n\n  // Number of nodes should be 2 for all indices.\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::simple_action<component,\n                                 ActionSetValueTo<ValueTag, NumNodes>>(\n        make_not_null(&runner), i);\n    CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, i) == 2);\n  }\n\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::simple_action<component, ActionSetValueTo<ValueTag, MyProc>>(\n        make_not_null(&runner), i);\n  }\n\n  // Should all be set to proc (which Mark computed by hand)\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 0) == 0);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 1) == 3);\n\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::simple_action<component, ActionSetValueTo<ValueTag, MyNode>>(\n        make_not_null(&runner), i);\n  }\n  // Should all be set to node (which Mark computed by hand)\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 0) == 0);\n  CHECK(ActionTesting::get_databox_tag<component, ValueTag>(runner, 1) == 1);\n}\n\nstruct MetavariablesWithPup {\n  using component_list = tmpl::list<NodeGroupComponent<MetavariablesWithPup>>;\n\n  void pup(PUP::er& /*p*/) {}\n};\n\nvoid test_sizing() {\n  using metavars = MetavariablesWithPup;\n  using component = NodeGroupComponent<metavars>;\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}};\n  ActionTesting::emplace_nodegroup_component_and_initialize<component>(&runner,\n                                                                       {-3});\n  auto& cache = ActionTesting::cache<component>(runner, 0_st);\n  auto& proxy = Parallel::get_parallel_component<component>(cache);\n  auto& local_branch = *Parallel::local_branch(proxy);\n\n  // The fact that this doesn't cause an error means it is successful. We aren't\n  // concerned with the actual value\n  const size_t size = size_of_object_in_bytes(local_branch);\n  (void)size;\n\n  CHECK_THROWS_WITH(([&local_branch]() { serialize(local_branch); }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"MockDistributedObject is not serializable. This pup \"\n                        \"member can only be used for sizing.\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.ActionTesting.NodesAndCores\", \"[Unit]\") {\n  test_parallel_info_functions();\n  test_group_emplace();\n  test_nodegroup_emplace();\n  test_sizing();\n}\n\n}  // namespace TestNodesAndCores\n\nnamespace TestMutableGlobalCache {\n// [mutable cache tag]\nstruct CacheTag : db::SimpleTag {\n  using type = int;\n};\n// [mutable cache tag]\n\ntemplate <int Value>\nstruct CacheTagUpdater {\n  static void apply(gsl::not_null<typename CacheTag::type*> tag_value) {\n    *tag_value = Value;\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\n// This Action does nothing other than set a bool so that we can\n// check if it was called.\nstatic bool simple_action_to_test_was_called = false;\nstruct SimpleActionToTest {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(const db::DataBox<DbTagsList>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    simple_action_to_test_was_called = true;\n  }\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n\n  // [mutable global cache metavars]\n  using mutable_global_cache_tags = tmpl::list<CacheTag>;\n  // [mutable global cache metavars]\n};\n\nSPECTRE_TEST_CASE(\"Unit.ActionTesting.MutableGlobalCache\", \"[Unit]\") {\n  using metavars = Metavariables;\n  using component = Component<metavars>;\n\n  // [mutable global cache runner]\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}, {0}};\n  // [mutable global cache runner]\n  ActionTesting::emplace_array_component<component>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0);\n\n  auto& cache = ActionTesting::cache<component>(runner, 0);\n  auto& element_proxy = ::Parallel::get_parallel_component<component>(cache)[0];\n  const auto array_component_id =\n      Parallel::make_array_component_id<component>(static_cast<int>(0));\n\n  CHECK(Parallel::mutable_cache_item_is_ready<CacheTag>(\n      cache, array_component_id, [](const int /*value*/) {\n        return std::unique_ptr<Parallel::Callback>{};\n      }));\n\n  // We cannot have two different types of callbacks at once in the mutable\n  // cache, so we test the simple action callbacks first while mutating the\n  // cache, then test the algorithm callbacks\n  CHECK(not Parallel::mutable_cache_item_is_ready<CacheTag>(\n      cache, array_component_id, [&element_proxy](const int /*value*/) {\n        return std::unique_ptr<Parallel::Callback>(\n            new Parallel::SimpleActionCallback<SimpleActionToTest,\n                                               decltype(element_proxy)>(\n                element_proxy));\n      }));\n\n  // Should be no queued simple actions.\n  CHECK(ActionTesting::is_simple_action_queue_empty<component>(runner, 0));\n\n  // After we mutate the item, then SimpleActionToTest should be queued...\n  Parallel::mutate<CacheTag, CacheTagUpdater<1>>(cache);\n  CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner, 0) ==\n        1);\n  // ... so invoke it\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n  // ... and test that the action was called.\n  CHECK(simple_action_to_test_was_called);\n\n  // Now reset for another call.\n  simple_action_to_test_was_called = false;\n\n  // Simple actions can be called on entire components\n  // (i.e. all elements at once as opposed to one element at a time).\n  auto& all_elements_proxy =\n      ::Parallel::get_parallel_component<component>(cache);\n  CHECK(not Parallel::mutable_cache_item_is_ready<CacheTag>(\n      cache, array_component_id, [&all_elements_proxy](const int /*value*/) {\n        return std::unique_ptr<Parallel::Callback>(\n            new Parallel::SimpleActionCallback<SimpleActionToTest,\n                                               decltype(all_elements_proxy)>(\n                all_elements_proxy));\n      }));\n\n  // Should be no queued simple actions.\n  CHECK(ActionTesting::is_simple_action_queue_empty<component>(runner, 0));\n\n  // After we mutate the item, then SimpleActionToTest should be queued...\n  Parallel::mutate<CacheTag, CacheTagUpdater<3>>(cache);\n  CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner, 0) ==\n        1);\n  // ... so invoke it\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n  // ... and test that the action was called.\n  CHECK(simple_action_to_test_was_called);\n\n  // Currently perform_algorithm cannot be called on all elements at\n  // once (in the ActionTesting framework), but this is not difficult\n  // to add in the future.\n  CHECK(not Parallel::mutable_cache_item_is_ready<CacheTag>(\n      cache, array_component_id, [&element_proxy](const int /*value*/) {\n        return std::unique_ptr<Parallel::Callback>(\n            new Parallel::PerformAlgorithmCallback<decltype(element_proxy)>(\n                element_proxy));\n      }));\n}\n}  // namespace TestMutableGlobalCache\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/Test_Pypp.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for detai\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <optional>\n#include <random>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/PyppFundamentals.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing namespace std::complex_literals;\n\n// [convert_arbitrary_a]\nstruct ClassForConversionTest {\n  double a_;\n  double b_;\n};\n\nstruct ConvertClassForConservionTestA {\n  using unpacked_container = double;\n  using packed_container = ClassForConversionTest;\n  using packed_type = double;\n\n  static inline unpacked_container unpack(const packed_container t,\n                                          const size_t /*grid_point_index*/) {\n    return t.a_;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed_t,\n                          const unpacked_container t,\n                          const size_t /*grid_point_index*/) {\n    packed_t->a_ = t;\n  }\n\n  static inline size_t get_size(const packed_container& /*t*/) { return 1; }\n};\n// [convert_arbitrary_a]\n\n// [convert_arbitrary_b]\nstruct ConvertClassForConservionTestB {\n  using unpacked_container = double;\n  using packed_container = ClassForConversionTest;\n  using packed_type = double;\n\n  static inline unpacked_container unpack(const packed_container t,\n                                          const size_t /*grid_point_index*/) {\n    return t.b_;\n  }\n\n  static inline void pack(const gsl::not_null<packed_container*> packed_t,\n                          const unpacked_container t,\n                          const size_t /*grid_point_index*/) {\n    packed_t->b_ = t;\n  }\n\n  static inline size_t get_size(const packed_container& /*t*/) { return 1; }\n};\n// [convert_arbitrary_b]\n\nvoid test_none() {\n  pypp::call<pypp::None>(\"PyppPyTests\", \"test_none\");\n  CHECK_THROWS_WITH(pypp::call<pypp::None>(\"PyppPyTests\", \"test_numeric\", 1, 2),\n                    \"Cannot convert non-None type to void. Got int\");\n  CHECK_THROWS_WITH(pypp::call<std::string>(\"PyppPyTests\", \"test_none\"),\n                    \"Cannot convert non-string type to string. Got NoneType\");\n}\n\nvoid test_std_string() {\n  const auto ret = pypp::call<std::string>(\"PyppPyTests\", \"test_string\",\n                                           std::string(\"test string\"));\n  CHECK(ret == std::string(\"back test string\"));\n  // Returns \"Function returned null\"\n  // CHECK_THROWS_WITH(pypp::call<std::string>(\"PyppPyTests\", \"test_string\",\n  //                                           std::string(\"test string_\")),\n  //                   \"Failed string test\");\n  CHECK_THROWS_WITH(pypp::call<double>(\"PyppPyTests\", \"test_string\",\n                                       std::string(\"test string\")),\n                    \"Cannot convert non-double type to double. Got str\");\n}\n\nvoid test_int() {\n  // [pypp_int_test]\n  const auto ret = pypp::call<long>(\"PyppPyTests\", \"test_numeric\", 3, 4);\n  CHECK(ret == 3 * 4);\n  // [pypp_int_test]\n  CHECK_THROWS_WITH(pypp::call<double>(\"PyppPyTests\", \"test_numeric\", 3, 4),\n                    \"Cannot convert non-double type to double. Got int\");\n  CHECK_THROWS_WITH(pypp::call<void*>(\"PyppPyTests\", \"test_numeric\", 3, 4),\n                    \"Cannot convert non-None type to void. Got int\");\n  CHECK_THROWS_WITH(pypp::call<long>(\"PyppPyTests\", \"test_none\"),\n                    \"Cannot convert non-long/int type to long. Got NoneType\");\n}\n\nvoid test_long() {\n  const auto ret = pypp::call<long>(\"PyppPyTests\", \"test_numeric\", 3L, 4L);\n  CHECK(ret == 3L * 4L);\n  CHECK_THROWS_WITH(pypp::call<double>(\"PyppPyTests\", \"test_numeric\", 3L, 4L),\n                    \"Cannot convert non-double type to double. Got int\");\n  CHECK_THROWS_WITH(pypp::call<long>(\"PyppPyTests\", \"test_numeric\", 3.0, 3.74),\n                    \"Cannot convert non-long/int type to long. Got float\");\n}\n\nvoid test_unsigned_long() {\n  const auto ret =\n      pypp::call<unsigned long>(\"PyppPyTests\", \"test_numeric\", 3ul, 4ul);\n  CHECK(ret == 3ul * 4ul);\n  CHECK_THROWS_WITH(pypp::call<double>(\"PyppPyTests\", \"test_numeric\", 3ul, 4ul),\n                    \"Cannot convert non-double type to double. Got int\");\n  CHECK_THROWS_WITH(\n      pypp::call<unsigned long>(\"PyppPyTests\", \"test_numeric\", 3.0, 3.74),\n      \"Cannot convert non-long/int type to long. Got float\");\n}\n\nvoid test_double() {\n  const auto ret =\n      pypp::call<double>(\"PyppPyTests\", \"test_numeric\", 3.49582, 3);\n  CHECK(ret == 3.0 * 3.49582);\n  CHECK_THROWS_WITH(pypp::call<long>(\"PyppPyTests\", \"test_numeric\", 3.8, 3.9),\n                    \"Cannot convert non-long/int type to long. Got float\");\n  CHECK_THROWS_WITH(pypp::call<double>(\"PyppPyTests\", \"test_numeric\", 3ul, 3ul),\n                    \"Cannot convert non-double type to double. Got int\");\n}\n\nvoid test_std_vector() {\n  // [pypp_vector_test]\n  const auto ret = pypp::call<std::vector<double>>(\n      \"PyppPyTests\", \"test_vector\", std::vector<double>{1.3, 4.9},\n      std::vector<double>{4.2, 6.8});\n  CHECK(approx(ret[0]) == 1.3 * 4.2);\n  CHECK(approx(ret[1]) == 4.9 * 6.8);\n  // [pypp_vector_test]\n  CHECK_THROWS_WITH(pypp::call<std::string>(\"PyppPyTests\", \"test_vector\",\n                                            std::vector<double>{1.3, 4.9},\n                                            std::vector<double>{4.2, 6.8}),\n                    \"Cannot convert non-string type to string. Got list\");\n}\n\nvoid test_std_array() {\n  // std::arrays and std::vectors should both convert to lists in python so this\n  // test calls the same python function as the vector test\n  const auto ret = pypp::call<std::array<double, 2>>(\n      \"PyppPyTests\", \"test_vector\", std::array<double, 2>{{1.3, 4.9}},\n      std::array<double, 2>{{4.2, 6.8}});\n  CHECK(approx(ret[0]) == 1.3 * 4.2);\n  CHECK(approx(ret[1]) == 4.9 * 6.8);\n  CHECK_THROWS_WITH(pypp::call<double>(\"PyppPyTests\", \"test_vector\",\n                                       std::array<double, 2>{{1.3, 4.9}},\n                                       std::array<double, 2>{{4.2, 6.8}}),\n                    \"Cannot convert non-double type to double. Got list\");\n\n  std::array<DataVector, 3> expected_array{\n      {DataVector{2, 3.}, DataVector{2, 1.}, DataVector{2, 2.}}};\n\n  CHECK(expected_array ==\n        (pypp::call<std::array<DataVector, 3>>(\n            \"PyppPyTests\", \"permute_array\",\n            tnsr::i<DataVector, 3>{\n                {{DataVector{2, 1.}, DataVector{2, 2.}, DataVector{2, 3.}}}})));\n\n  CHECK(expected_array ==\n        (pypp::call<std::array<DataVector, 3>>(\n            \"PyppPyTests\", \"permute_array\",\n            std::array<DataVector, 3>{\n                {DataVector{2, 1.}, DataVector{2, 2.}, DataVector{2, 3.}}})));\n}\n\nvoid test_datavector() {\n  const auto ret = pypp::call<DataVector>(\n      \"numpy\", \"multiply\", DataVector{1.3, 4.9}, DataVector{4.2, 6.8});\n  CHECK(approx(ret[0]) == 1.3 * 4.2);\n  CHECK(approx(ret[1]) == 4.9 * 6.8);\n  CHECK_THROWS_WITH(\n      pypp::call<std::string>(\"numpy\", \"multiply\", DataVector{1.3, 4.9},\n                              DataVector{4.2, 6.8}),\n      \"Cannot convert non-string type to string. Got numpy.ndarray\");\n  CHECK_THROWS_WITH(pypp::call<DataVector>(\"PyppPyTests\", \"two_dim_ndarray\"),\n                    \"Cannot convert array of ndim != 1 to DataVector.\");\n  CHECK_THROWS_WITH(pypp::call<DataVector>(\"PyppPyTests\", \"ndarray_of_floats\"),\n                    \"Cannot convert array of non-double type to DataVector.\");\n}\n\nvoid test_complex_datavector() {\n  const std::complex<double> test_value_0{1.3, 2.2};\n  const std::complex<double> test_value_1{4.0, 3.1};\n  const std::complex<double> test_value_2{4.2, 5.7};\n  const std::complex<double> test_value_3{6.8, 7.3};\n  const auto ret = pypp::call<ComplexDataVector>(\n      \"numpy\", \"multiply\", ComplexDataVector{test_value_0, test_value_1},\n      ComplexDataVector{test_value_2, test_value_3});\n  CHECK_ITERABLE_APPROX(ret[0], test_value_0 * test_value_2);\n  CHECK_ITERABLE_APPROX(ret[1], test_value_1 * test_value_3);\n  CHECK_THROWS_WITH(\n      pypp::call<std::string>(\"numpy\", \"multiply\",\n                              ComplexDataVector{test_value_0, test_value_1},\n                              ComplexDataVector{test_value_2, test_value_3}),\n      \"Cannot convert non-string type to string. Got numpy.ndarray\");\n  CHECK_THROWS_WITH(\n      pypp::call<ComplexDataVector>(\"PyppPyTests\", \"two_dim_ndarray\"),\n      \"Cannot convert array of non-complex type to ComplexDataVector.\");\n  CHECK_THROWS_WITH(\n      pypp::call<ComplexDataVector>(\"PyppPyTests\", \"ndarray_of_floats\"),\n      \"Cannot convert array of non-complex type to ComplexDataVector.\");\n\n  // test functionality of mixed complex and real values in tensors\n  const size_t vector_size = 1;\n  Scalar<SpinWeighted<ComplexDataVector, 1>> spin_weighted_argument{\n      vector_size};\n  get(spin_weighted_argument).data()[0] = test_value_1;\n  tnsr::i<ComplexDataVector, 2> complex_tensor_argument{vector_size};\n  get<0>(complex_tensor_argument)[0] = test_value_2;\n  get<1>(complex_tensor_argument)[0] = test_value_3;\n  tnsr::i<DataVector, 2> real_tensor_argument{vector_size};\n  get<0>(real_tensor_argument)[0] = 1.2;\n  get<1>(real_tensor_argument)[0] = 3.4;\n\n  const auto mixed_return_1 =\n      pypp::call<Scalar<SpinWeighted<ComplexDataVector, 1>>>(\n          \"PyppPyTests\", \"mixed_complex_real_function_1\",\n          spin_weighted_argument, complex_tensor_argument,\n          real_tensor_argument);\n  const auto mixed_return_2 = pypp::call<tnsr::i<DataVector, 2>>(\n      \"PyppPyTests\", \"mixed_complex_real_function_2\", spin_weighted_argument,\n      complex_tensor_argument, real_tensor_argument);\n  CHECK(real(get(mixed_return_1).data()[0]) ==\n        approx(real(test_value_1 * test_value_2 / 3.4)));\n  CHECK(imag(get(mixed_return_1).data()[0]) ==\n        approx(imag(test_value_1 * test_value_2 / 3.4)));\n\n  CHECK(get<0>(mixed_return_2)[0] ==\n        approx(real(test_value_1 * test_value_2 * 3.4)));\n  CHECK(get<1>(mixed_return_2)[0] ==\n        approx(imag(test_value_3 * 1.2 / test_value_1)));\n}\n\nvoid test_tensor_double() {\n  const Scalar<double> scalar{0.8};\n  const tnsr::A<double, 3> vector{{{3., 4., 5., 6.}}};\n  const auto tnsr_ia = []() {\n    tnsr::ia<double, 3> tnsr{};\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 4; ++j) {\n        tnsr.get(i, j) = i + 2. * j + 1.;\n      }\n    }\n    return tnsr;\n  }();\n  const auto tnsr_AA = []() {\n    tnsr::AA<double, 3> tnsr{};\n    for (size_t i = 0; i < 4; ++i) {\n      for (size_t j = 0; j < 4; ++j) {\n        tnsr.get(i, j) = static_cast<double>(i) + j + 1.;\n      }\n    }\n    return tnsr;\n  }();\n  const auto tnsr_iaa = []() {\n    tnsr::iaa<double, 3> tnsr{};\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 4; ++j) {\n        for (size_t k = 0; k < 4; ++k) {\n          tnsr.get(i, j, k) = 2. * (k + 1.) * (j + 1.) + i + 1.;\n        }\n      }\n    }\n    return tnsr;\n  }();\n  const auto tnsr_aia = []() {\n    tnsr::aia<double, 3> tnsr{};\n    for (size_t i = 0; i < 4; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        for (size_t k = 0; k < 4; ++k) {\n          tnsr.get(i, j, k) = 2. * (k + 1.) * (i + 1.) + j + 1.5;\n        }\n      }\n    }\n    return tnsr;\n  }();\n  const auto tnsr_aBcc = []() {\n    tnsr::aBcc<double, 3> tnsr{};\n    for (size_t i = 0; i < 4; ++i) {\n      for (size_t j = 0; j < 4; ++j) {\n        for (size_t k = 0; k < 4; ++k) {\n          for (size_t l = 0; l < 4; ++l) {\n            tnsr.get(i, j, k, l) = 3. * i + j + (k + 1.) * (l + 1.) + 1.;\n          }\n        }\n      }\n    }\n    return tnsr;\n  }();\n\n  // Check converting from Tensor to ndarray\n  CHECK(pypp::call<bool>(\"PyppPyTests\", \"convert_scalar_to_ndarray_successful\",\n                         scalar));\n  CHECK(pypp::call<bool>(\"PyppPyTests\", \"convert_scalar_to_double_unsuccessful\",\n                         scalar));\n  CHECK(pypp::call<bool>(\"PyppPyTests\", \"convert_vector_successful\", vector));\n  CHECK(pypp::call<bool>(\"PyppPyTests\", \"convert_tnsr_ia_successful\", tnsr_ia));\n  CHECK(pypp::call<bool>(\"PyppPyTests\", \"convert_tnsr_AA_successful\", tnsr_AA));\n  CHECK(\n      pypp::call<bool>(\"PyppPyTests\", \"convert_tnsr_iaa_successful\", tnsr_iaa));\n  CHECK(\n      pypp::call<bool>(\"PyppPyTests\", \"convert_tnsr_aia_successful\", tnsr_aia));\n  CHECK(pypp::call<bool>(\"PyppPyTests\", \"convert_tnsr_aBcc_successful\",\n                         tnsr_aBcc));\n\n  // Check converting from ndarray to Tensor\n  CHECK(scalar ==\n        (pypp::call<Scalar<double>>(\"PyppPyTests\", \"scalar_from_double\")));\n  CHECK(scalar ==\n        (pypp::call<Scalar<double>>(\"PyppPyTests\", \"scalar_from_ndarray\")));\n  CHECK(vector == (pypp::call<tnsr::A<double, 3>>(\"PyppPyTests\", \"vector\")));\n  CHECK(tnsr_ia == (pypp::call<tnsr::ia<double, 3>>(\"PyppPyTests\", \"tnsr_ia\")));\n  CHECK(tnsr_AA == (pypp::call<tnsr::AA<double, 3>>(\"PyppPyTests\", \"tnsr_AA\")));\n  CHECK(tnsr_iaa ==\n        (pypp::call<tnsr::iaa<double, 3>>(\"PyppPyTests\", \"tnsr_iaa\")));\n  CHECK(tnsr_aia ==\n        (pypp::call<tnsr::aia<double, 3>>(\"PyppPyTests\", \"tnsr_aia\")));\n  CHECK(tnsr_aBcc ==\n        (pypp::call<tnsr::aBcc<double, 3>>(\"PyppPyTests\", \"tnsr_aBcc\")));\n\n  // Check conversion throws with incorrect rank\n  CHECK_THROWS_WITH(\n      (pypp::call<tnsr::i<double, 3>>(\"PyppPyTests\", \"scalar_from_ndarray\")),\n      \"Mismatch between ndim of numpy ndarray (0) and rank of Tensor (1)\");\n  CHECK_THROWS_WITH(\n      (pypp::call<tnsr::ij<double, 3>>(\"PyppPyTests\", \"vector\")),\n      \"Mismatch between ndim of numpy ndarray (1) and rank of Tensor (2)\");\n  CHECK_THROWS_WITH(\n      (pypp::call<Scalar<double>>(\"PyppPyTests\", \"tnsr_AA\")),\n      \"Mismatch between ndim of numpy ndarray (2) and rank of Tensor (0)\");\n  // Check conversion throws with correct rank but incorrect dimension\n  CHECK_THROWS_WITH((pypp::call<tnsr::i<double, 3>>(\"PyppPyTests\", \"vector\")),\n                    \"Mismatch between number of components of ndarray (4) and \"\n                    \"Tensor of rank 1 in 0'th index with dimension 3\");\n  CHECK_THROWS_WITH(\n      (pypp::call<tnsr::iaa<double, 3>>(\"PyppPyTests\", \"tnsr_aia\")),\n      \"Mismatch between number of components of ndarray (4) and Tensor of rank \"\n      \"3 in 0'th index with dimension 3\");\n}\n\nvoid test_tensor_datavector() {\n  const size_t npts = 5;\n  const Scalar<DataVector> scalar{DataVector(npts, 0.8)};\n\n  const tnsr::A<DataVector, 3> vector{\n      {{DataVector(npts, 3.), DataVector(npts, 4.), DataVector(npts, 5.),\n        DataVector(npts, 6.)}}};\n\n  const auto tnsr_ia = []() {\n    tnsr::ia<DataVector, 3> tnsr{};\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 4; ++j) {\n        tnsr.get(i, j) = DataVector(npts, i + 2. * j + 1.);\n      }\n    }\n    return tnsr;\n  }();\n  const auto tnsr_AA = []() {\n    tnsr::AA<DataVector, 3> tnsr{};\n    for (size_t i = 0; i < 4; ++i) {\n      for (size_t j = 0; j < 4; ++j) {\n        tnsr.get(i, j) = DataVector(npts, static_cast<double>(i) + j + 1.);\n      }\n    }\n    return tnsr;\n  }();\n  const auto tnsr_iaa = []() {\n    tnsr::iaa<DataVector, 3> tnsr{};\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 4; ++j) {\n        for (size_t k = 0; k < 4; ++k) {\n          tnsr.get(i, j, k) =\n              DataVector(npts, 2. * (k + 1.) * (j + 1.) + i + 1.);\n        }\n      }\n    }\n    return tnsr;\n  }();\n  const auto tnsr_aia = []() {\n    tnsr::aia<DataVector, 3> tnsr{};\n    for (size_t i = 0; i < 4; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        for (size_t k = 0; k < 4; ++k) {\n          tnsr.get(i, j, k) =\n              DataVector(npts, 2. * (k + 1.) * (i + 1.) + j + 1.5);\n        }\n      }\n    }\n    return tnsr;\n  }();\n  const auto tnsr_aBcc = []() {\n    tnsr::aBcc<DataVector, 3> tnsr{};\n    for (size_t i = 0; i < 4; ++i) {\n      for (size_t j = 0; j < 4; ++j) {\n        for (size_t k = 0; k < 4; ++k) {\n          for (size_t l = 0; l < 4; ++l) {\n            tnsr.get(i, j, k, l) =\n                DataVector(npts, 3. * i + j + (k + 1.) * (l + 1.) + 1.);\n          }\n        }\n      }\n    }\n    return tnsr;\n  }();\n\n  CHECK(scalar ==\n        (pypp::call<Scalar<DataVector>>(\"PyppPyTests\", \"identity\", scalar)));\n  CHECK(vector == (pypp::call<tnsr::A<DataVector, 3>>(\"PyppPyTests\", \"identity\",\n                                                      vector)));\n  CHECK(tnsr_ia == (pypp::call<tnsr::ia<DataVector, 3>>(\"PyppPyTests\",\n                                                        \"identity\", tnsr_ia)));\n  CHECK(tnsr_AA == (pypp::call<tnsr::AA<DataVector, 3>>(\"PyppPyTests\",\n                                                        \"identity\", tnsr_AA)));\n  CHECK(tnsr_iaa == (pypp::call<tnsr::iaa<DataVector, 3>>(\n                        \"PyppPyTests\", \"identity\", tnsr_iaa)));\n  CHECK(tnsr_aia == (pypp::call<tnsr::aia<DataVector, 3>>(\n                        \"PyppPyTests\", \"identity\", tnsr_aia)));\n  CHECK(tnsr_aBcc == (pypp::call<tnsr::aBcc<DataVector, 3>>(\n                         \"PyppPyTests\", \"identity\", tnsr_aBcc)));\n}\n\nvoid test_tensor_complex_datavector() {\n  const size_t npts = 5;\n  const Scalar<ComplexDataVector> scalar{ComplexDataVector(npts, 0.8 + 0.2i)};\n  const tnsr::A<ComplexDataVector, 3> vector{\n      {{ComplexDataVector(npts, 3. + 2.i), ComplexDataVector(npts, 4. + 5.i),\n        ComplexDataVector(npts, 5. + 6.i), ComplexDataVector(npts, 6. + 7.i)}}};\n  CHECK(scalar == (pypp::call<Scalar<ComplexDataVector>>(\"PyppPyTests\",\n                                                         \"identity\", scalar)));\n  CHECK(vector == (pypp::call<tnsr::A<ComplexDataVector, 3>>(\n                      \"PyppPyTests\", \"identity\", vector)));\n}\n\ntemplate <typename T>\nvoid test_einsum(const T& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-10., 10.);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_dist = make_not_null(&dist);\n\n  const auto scalar =\n      make_with_random_values<Scalar<T>>(nn_generator, nn_dist, used_for_size);\n  const auto vector = make_with_random_values<tnsr::A<T, 3>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto tnsr_ia = make_with_random_values<tnsr::ia<T, 3>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto tnsr_AA = make_with_random_values<tnsr::AA<T, 3>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto tnsr_iaa = make_with_random_values<tnsr::iaa<T, 3>>(\n      nn_generator, nn_dist, used_for_size);\n\n  const auto expected = [&scalar, &vector, &tnsr_ia, &tnsr_AA, &tnsr_iaa,\n                         &used_for_size]() {\n    auto tnsr = make_with_value<tnsr::i<T, 3>>(used_for_size, 0.);\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t a = 0; a < 4; ++a) {\n        tnsr.get(i) += scalar.get() * vector.get(a) * tnsr_ia.get(i, a);\n        for (size_t b = 0; b < 4; ++b) {\n          tnsr.get(i) += tnsr_AA.get(a, b) * tnsr_iaa.get(i, a, b);\n        }\n      }\n    }\n    return tnsr;\n  }();\n  // [einsum_example]\n  const auto tensor_from_python = pypp::call<tnsr::i<T, 3>>(\n      \"PyppPyTests\", \"test_einsum\", scalar, vector, tnsr_ia, tnsr_AA, tnsr_iaa);\n  // [einsum_example]\n  CHECK_ITERABLE_CUSTOM_APPROX(expected, tensor_from_python,\n                               Approx::custom().epsilon(1.0e-12));\n}\n\nvoid test_function_of_time() {\n  const tnsr::i<double, 3> x_d{{{3.4, 4.2, 5.8}}};\n  const tnsr::i<DataVector, 3> x_dv{\n      {{DataVector(8, 3.4), DataVector(8, 4.2), DataVector(8, 5.8)}}};\n  const double t = -9.2;\n  const auto check = [](const auto& x, const double time) {\n    CHECK((2 * x.get(0) + x.get(1) - x.get(2) - time) ==\n          (pypp::call<Scalar<typename std::decay_t<decltype(x)>::value_type>>(\n               \"PyppPyTests\", \"test_function_of_time\", x, time)\n               .get()));\n  };\n  check(x_d, t);\n  check(x_dv, t);\n}\n\ntemplate <template <class> class Optional>\nvoid test_optional() {\n  const Optional<Scalar<double>> scalar_double_a{Scalar<double>{0.8}};\n  const Optional<Scalar<double>> scalar_double_b{Scalar<double>{1.8}};\n\n  const Optional<Scalar<DataVector>> scalar_datavector_a{\n      Scalar<DataVector>{{{{0.8, 0.7, 0.5}}}}};\n  const Optional<Scalar<DataVector>> scalar_datavector_b{\n      Scalar<DataVector>{{{{1.8, 2.3, 4.2}}}}};\n\n  const auto impl = [](const auto& a, const auto& b) {\n    using T = typename std::decay_t<decltype(a)>::value_type;\n    const auto result_double_double =\n        pypp::call<T>(\"PyppPyTests\", \"add_scalars\", a, b);\n    CHECK_ITERABLE_APPROX(get(result_double_double), get(T{get(*a) + get(*b)}));\n\n    const auto result_double_none =\n        pypp::call<T>(\"PyppPyTests\", \"add_scalars\", a, Optional<T>{});\n    CHECK_ITERABLE_APPROX(get(result_double_none), get(*a));\n\n    const auto result_none_double =\n        pypp::call<T>(\"PyppPyTests\", \"add_scalars\", Optional<T>{}, b);\n    CHECK_ITERABLE_APPROX(get(result_none_double), get(*b));\n  };\n\n  impl(scalar_double_a, scalar_double_b);\n  impl(scalar_datavector_a, scalar_datavector_b);\n}\n\nvoid test_tuple_of_tensors_of_data_vector() {\n  using ResultType =\n      std::tuple<Scalar<DataVector>, tnsr::I<DataVector, 1, Frame::Inertial>>;\n  CHECK_THROWS_WITH(\n      pypp::call<ResultType>(\"PyppPyTests\", \"tuple_of_tensor_not_tuple\",\n                             Scalar<DataVector>{3_st, 1.0}),\n      Catch::Matchers::ContainsSubstring(\"Expected a Python tuple but got\"));\n  CHECK_THROWS_WITH(\n      pypp::call<ResultType>(\"PyppPyTests\", \"tuple_of_tensor_wrong_size\",\n                             Scalar<DataVector>{3_st, 1.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"Python tuple has size 3 but we expected size 2\"));\n  CHECK_THROWS_WITH(pypp::call<ResultType>(\"PyppPyTests\",\n                                           \"tuple_of_tensor_failed_to_convert0\",\n                                           Scalar<DataVector>{3_st, 1.0}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"std::tuple conversion failed for zero-based entry \"\n                        \"number 0 of the tuple.\"));\n  CHECK_THROWS_WITH(pypp::call<ResultType>(\"PyppPyTests\",\n                                           \"tuple_of_tensor_failed_to_convert1\",\n                                           Scalar<DataVector>{3_st, 1.0}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"std::tuple conversion failed for zero-based entry \"\n                        \"number 1 of the tuple.\"));\n\n  const auto result =\n      pypp::call<ResultType>(\"PyppPyTests\", \"tuple_of_tensor_works\",\n                             Scalar<DataVector>{DataVector{1.0, 1.2, 1.5}});\n  CHECK_ITERABLE_APPROX(get(std::get<0>(result)), (DataVector{2.0, 2.4, 3.0}));\n  CHECK_ITERABLE_APPROX(get<0>(std::get<1>(result)),\n                        (DataVector{5.0, 6.0, 7.5}));\n}\n\nnamespace Tags {\nstruct Var1 {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 {\n  using type = tnsr::I<DataVector, 1, Frame::Inertial>;\n};\n\nstruct Var3 {\n  using type = long;\n};\n}  // namespace Tags\n\nvoid test_tagged_tuple() {\n  using ResultType = tuples::TaggedTuple<Tags::Var1, Tags::Var2, Tags::Var3>;\n  CHECK_THROWS_WITH(\n      pypp::call<ResultType>(\"PyppPyTests\", \"tagged_tuple_of_tensor_wrong_type\",\n                             Scalar<DataVector>{3_st, 1.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"Expected a Python dictionary but got\"));\n\n  CHECK_THROWS_WITH(\n      pypp::call<ResultType>(\"PyppPyTests\",\n                             \"tagged_tuple_of_tensor_missing_tag_too_small\",\n                             Scalar<DataVector>{3_st, 1.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"Could not find tag Var2 in dictionary. Known keys are \"\n          \"(Var1,Var3)\"));\n  CHECK_THROWS_WITH(\n      pypp::call<ResultType>(\"PyppPyTests\", \"tagged_tuple_of_tensor_missing\",\n                             Scalar<DataVector>{3_st, 1.0}),\n      Catch::Matchers::ContainsSubstring(\n          \"Could not find tag Var2 in dictionary. Known keys are \"\n          \"(Var1,Var2b,Var3)\"));\n  CHECK_THROWS_WITH(pypp::call<ResultType>(\n                        \"PyppPyTests\", \"tagged_tuple_of_tensor_convert_var1\",\n                        Scalar<DataVector>{3_st, 1.0}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"TaggedTuple conversion failed for tag name Var1.\"));\n  CHECK_THROWS_WITH(pypp::call<ResultType>(\n                        \"PyppPyTests\", \"tagged_tuple_of_tensor_convert_var2\",\n                        Scalar<DataVector>{3_st, 1.0}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"TaggedTuple conversion failed for tag name Var2.\"));\n  CHECK_THROWS_WITH(pypp::call<ResultType>(\n                        \"PyppPyTests\", \"tagged_tuple_of_tensor_convert_var3\",\n                        Scalar<DataVector>{3_st, 1.0}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"TaggedTuple conversion failed for tag name Var3.\"));\n\n  const auto result =\n      pypp::call<ResultType>(\"PyppPyTests\", \"tagged_tuple_of_tensor_works\",\n                             Scalar<DataVector>{DataVector{1.0, 1.2, 1.5}});\n  CHECK_ITERABLE_APPROX(get(tuples::get<Tags::Var1>(result)),\n                        (DataVector{2.0, 2.4, 3.0}));\n  CHECK_ITERABLE_APPROX(get<0>(tuples::get<Tags::Var2>(result)),\n                        (DataVector{5.0, 6.0, 7.5}));\n  CHECK(tuples::get<Tags::Var3>(result) == 10);\n\n  const auto result2 = pypp::call<ResultType>(\n      \"PyppPyTests\", \"tagged_tuple_of_tensor_works_extra_dict\",\n      Scalar<DataVector>{DataVector{1.0, 1.2, 1.5}});\n  CHECK_ITERABLE_APPROX(get(tuples::get<Tags::Var1>(result2)),\n                        (DataVector{2.0, 2.4, 3.0}));\n  CHECK_ITERABLE_APPROX(get<0>(tuples::get<Tags::Var2>(result2)),\n                        (DataVector{5.0, 6.0, 7.5}));\n  CHECK(tuples::get<Tags::Var3>(result2) == 10);\n}\n\nvoid test_custom_conversion() {\n  const Scalar<DataVector> t{DataVector{5, 2.5}};\n  {\n    // [convert_arbitrary_a_call]\n    const auto result = pypp::call<Scalar<DataVector>,\n                                   tmpl::list<ConvertClassForConservionTestA>>(\n        \"PyppPyTests\", \"custom_conversion\", t,\n        ClassForConversionTest{2.0, 3.0});\n    // [convert_arbitrary_a_call]\n    CHECK(DataVector{5, 5.0} == get(result));\n  }\n  {\n    const auto result = pypp::call<Scalar<DataVector>,\n                                   tmpl::list<ConvertClassForConservionTestB>>(\n        \"PyppPyTests\", \"custom_conversion\", t,\n        ClassForConversionTest{2.0, 3.0});\n    CHECK(DataVector{5, 7.5} == get(result));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Pypp\", \"[Pypp][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"Framework/Tests/\"};\n  {\n    INFO(\"Testing numpy and scipy support\");\n    CHECK((pypp::call<size_t>(\"numpy\", \"ndim\", tnsr::abcc<double, 3>{})) == 4);\n    CHECK((pypp::call<double>(\"scipy.linalg\", \"det\", tnsr::ii<double, 3>{})) ==\n          0.);\n  }\n\n  test_none();\n  test_std_string();\n  test_int();\n  test_long();\n  test_unsigned_long();\n  test_double();\n  test_std_vector();\n  test_std_array();\n  test_datavector();\n  test_complex_datavector();\n  test_tensor_double();\n  test_tensor_datavector();\n  test_tensor_complex_datavector();\n  test_einsum<double>(0.);\n  test_einsum<DataVector>(DataVector(5));\n  test_function_of_time();\n  test_optional<std::optional>();\n  test_tuple_of_tensors_of_data_vector();\n  test_tagged_tuple();\n  test_custom_conversion();\n}\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/Test_PyppAnalyticSolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for detai\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <tuple>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/PyppFundamentals.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct AnalyticSolutionTest {\n  template <typename T>\n  struct Var1 {\n    using type = Scalar<T>;\n  };\n  template <typename T>\n  struct Var2 {\n    using type = tnsr::I<T, 3>;\n  };\n\n  AnalyticSolutionTest(const double a, const std::array<double, 3>& b)\n      : a_(a), b_(b) {}\n\n  template <typename T>\n  tuples::TaggedTuple<Var1<T>, Var2<T>> solution(const tnsr::i<T, 3>& x,\n                                                 const double t) const {\n    auto sol =\n        make_with_value<tuples::TaggedTuple<Var1<T>, Var2<T>>>(x.get(0), 0.);\n    auto& scalar = tuples::get<Var1<T>>(sol);\n    auto& vector = tuples::get<Var2<T>>(sol);\n\n    for (size_t i = 0; i < 3; ++i) {\n      scalar.get() += x.get(i) * gsl::at(b_, i);\n      vector.get(i) = a_ * x.get(i) - gsl::at(b_, i) * t;\n    }\n    scalar.get() += a_ - t;\n    return sol;\n  }\n\n private:\n  double a_;\n  std::array<double, 3> b_;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Pypp.AnalyticSolution\", \"[Pypp][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"Framework/Tests/\"};\n  const double a = 4.3;\n  const std::array<double, 3> b{{-1.3, 5.6, -0.2}};\n  AnalyticSolutionTest solution{a, b};\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(\n      &AnalyticSolutionTest::solution<double>, solution, \"PyppPyTests\",\n      {\"check_solution_scalar\", \"check_solution_vector\"}, {{{-10.0, 10.0}}},\n      std::make_tuple(a, b), a);\n  pypp::check_with_random_values<1>(\n      &AnalyticSolutionTest::solution<DataVector>, solution, \"PyppPyTests\",\n      {\"check_solution_scalar\", \"check_solution_vector\"}, {{{-10.0, 10.0}}},\n      std::make_tuple(a, b), used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/Test_PyppRandomValues.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for detai\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <string>\n#include <tuple>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/PyppFundamentals.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace {\ntemplate <typename T>\nvoid check_single_not_null0(const gsl::not_null<T*> result, const T& t0) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    get(*result) = get(t0) + 5.0;\n  } else {\n    *result = t0 + 5.0;\n  }\n}\n\ntemplate <typename T>\nvoid check_single_not_null1(const gsl::not_null<T*> result, const T& t0,\n                            const T& t1) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    get(*result) = get(t0) + get(t1);\n  } else {\n    *result = t0 + t1;\n  }\n}\n\ntemplate <typename T>\nvoid check_single_not_null2(const gsl::not_null<T*> result, const T& t0,\n                            const T& t1) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    get(*result) = sqrt(get(t0)) + 1.0 / sqrt(-get(t1));\n  } else {\n    *result = sqrt(t0) + 1.0 / sqrt(-t1);\n  }\n}\n\ntemplate <typename T>\nvoid check_single_not_null3(const gsl::not_null<T*> result, const T& t0) {\n  // This function _adds_ to the result tensor, instead of assigning to it\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    get(*result) += get(t0) + 3.0;\n  } else {\n    *result += t0 + 3.0;\n  }\n}\n\ntemplate <typename T>\nvoid check_double_not_null0(const gsl::not_null<T*> result0,\n                            const gsl::not_null<T*> result1, const T& t0) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    get(*result0) = get(t0) + 5.0;\n    get(*result1) = 2.0 * get(t0) + 5.0;\n  } else {\n    *result0 = t0 + 5.0;\n    *result1 = 2.0 * t0 + 5.0;\n  }\n}\n\ntemplate <typename T>\nvoid check_double_not_null1(const gsl::not_null<T*> result0,\n                            const gsl::not_null<T*> result1, const T& t0,\n                            const T& t1) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    get(*result0) = get(t0) + get(t1);\n    get(*result1) = 2.0 * get(t0) + get(t1);\n  } else {\n    *result0 = t0 + t1;\n    *result1 = 2.0 * t0 + t1;\n  }\n}\n\ntemplate <typename T>\nvoid check_double_not_null2(const gsl::not_null<T*> result0,\n                            const gsl::not_null<T*> result1, const T& t0,\n                            const T& t1) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    get(*result0) = sqrt(get(t0)) + 1.0 / sqrt(-get(t1));\n    get(*result1) = 2.0 * get(t0) + get(t1);\n  } else {\n    *result0 = sqrt(t0) + 1.0 / sqrt(-t1);\n    *result1 = 2.0 * t0 + t1;\n  }\n}\n\ntemplate <typename T>\nT check_by_value0(const T& t0) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    return T{get(t0) + 5.0};\n  } else {\n    return t0 + 5.0;\n  }\n}\n\ntemplate <typename T>\nT check_by_value1(const T& t0, const T& t1) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    return T{get(t0) + get(t1)};\n  } else {\n    return t0 + t1;\n  }\n}\n\ntemplate <typename T>\nT check_by_value2(const T& t0, const T& t1) {\n  if constexpr (tt::is_a_v<Tensor, T>) {\n    return T{sqrt(get(t0)) + 1.0 / sqrt(-get(t1))};\n  } else {\n    return sqrt(t0) + 1.0 / sqrt(-t1);\n  }\n}\n\nclass RandomValuesTests {\n public:\n  RandomValuesTests(const double a, const double b,\n                    const std::array<double, 3>& c)\n      : a_(a), b_(b), c_(c) {}\n\n  // by value, single argument\n  template <typename T>\n  T check_by_value0(const T& t0) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      return T{get(t0) + 5.0};\n    } else {\n      return t0 + 5.0;\n    }\n  }\n\n  template <typename T>\n  T check_by_value1(const T& t0) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      return T{get(t0) + 5.0 * a_};\n    } else {\n      return t0 + 5.0 * a_;\n    }\n  }\n\n  template <typename T>\n  T check_by_value2(const T& t0) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      return T{get(t0) + 5.0 * a_ + b_};\n    } else {\n      return t0 + 5.0 * a_ + b_;\n    }\n  }\n\n  template <typename T>\n  T check_by_value3(const T& t0) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      return T{get(t0) + 5.0 * a_ + b_ + c_[0] - 2.0 * c_[1] - c_[2]};\n    } else {\n      return t0 + 5.0 * a_ + b_ + c_[0] - 2.0 * c_[1] - c_[2];\n    }\n  }\n\n  // by value, two arguments\n  template <typename T>\n  T check2_by_value0(const T& t0, const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      return T{get(t0) + get(t1)};\n    } else {\n      return t0 + t1;\n    }\n  }\n\n  template <typename T>\n  T check2_by_value1(const T& t0, const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      return T{get(t0) + get(t1) + 5.0 * a_};\n    } else {\n      return t0 + t1 + 5.0 * a_;\n    }\n  }\n\n  template <typename T>\n  T check2_by_value2(const T& t0, const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      return T{get(t0) + 5.0 * a_ + b_ * get(t1)};\n    } else {\n      return t0 + 5.0 * a_ + t1 * b_;\n    }\n  }\n\n  template <typename T>\n  T check2_by_value3(const T& t0, const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      return T{get(t0) * c_[0] + 5.0 * a_ + b_ * get(t1) + c_[1] - c_[2]};\n    } else {\n      return t0 * c_[0] + 5.0 * a_ + t1 * b_ + c_[1] - c_[2];\n    }\n  }\n\n  // single not_null, single argument\n  template <typename T>\n  void check_by_not_null0(const gsl::not_null<T*> result0, const T& t0) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      get(*result0) = get(t0) + 5.0;\n    } else {\n      *result0 = t0 + 5.0;\n    }\n  }\n\n  template <typename T>\n  void check_by_not_null1(const gsl::not_null<T*> result0, const T& t0) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      get(*result0) = get(t0) + 5.0 * a_;\n    } else {\n      *result0 = t0 + 5.0 * a_;\n    }\n  }\n\n  template <typename T>\n  void check_by_not_null2(const gsl::not_null<T*> result0, const T& t0) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      get(*result0) = get(t0) + 5.0 * a_ + b_;\n    } else {\n      *result0 = t0 + 5.0 * a_ + b_;\n    }\n  }\n\n  template <typename T>\n  void check_by_not_null3(const gsl::not_null<T*> result0, const T& t0) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      get(*result0) = get(t0) + 5.0 * a_ + b_ + c_[0] - 2.0 * c_[1] - c_[2];\n    } else {\n      *result0 = t0 + 5.0 * a_ + b_ + c_[0] - 2.0 * c_[1] - c_[2];\n    }\n  }\n\n  // by value, two arguments\n  template <typename T>\n  void check2_by_not_null0(const gsl::not_null<T*> result0, const T& t0,\n                           const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      *result0 = T{get(t0) + get(t1)};\n    } else {\n      *result0 = t0 + t1;\n    }\n  }\n\n  template <typename T>\n  void check2_by_not_null1(const gsl::not_null<T*> result0, const T& t0,\n                           const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      *result0 = T{get(t0) + get(t1) + 5.0 * a_};\n    } else {\n      *result0 = t0 + t1 + 5.0 * a_;\n    }\n  }\n\n  template <typename T>\n  void check2_by_not_null2(const gsl::not_null<T*> result0, const T& t0,\n                           const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      *result0 = T{get(t0) + 5.0 * a_ + b_ * get(t1)};\n    } else {\n      *result0 = t0 + 5.0 * a_ + t1 * b_;\n    }\n  }\n\n  template <typename T>\n  void check2_by_not_null3(const gsl::not_null<T*> result0, const T& t0,\n                           const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      *result0 = T{get(t0) * c_[0] + 5.0 * a_ + b_ * get(t1) + c_[1] - c_[2]};\n    } else {\n      *result0 = t0 * c_[0] + 5.0 * a_ + t1 * b_ + c_[1] - c_[2];\n    }\n  }\n\n  // by value, two arguments\n  template <typename T>\n  void check3_by_not_null0(const gsl::not_null<T*> result0,\n                           const gsl::not_null<T*> result1, const T& t0,\n                           const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      *result0 = T{get(t0) + get(t1)};\n      *result1 = T{2.0 * get(t0) + get(t1)};\n    } else {\n      *result0 = t0 + t1;\n      *result1 = 2.0 * t0 + t1;\n    }\n  }\n\n  template <typename T>\n  void check3_by_not_null1(const gsl::not_null<T*> result0,\n                           const gsl::not_null<T*> result1, const T& t0,\n                           const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      *result0 = T{get(t0) + get(t1) + 5.0 * a_};\n      *result1 = T{2.0 * get(t0) + get(t1) + 5.0 * a_};\n    } else {\n      *result0 = t0 + t1 + 5.0 * a_;\n      *result1 = 2.0 * t0 + t1 + 5.0 * a_;\n    }\n  }\n\n  template <typename T>\n  void check3_by_not_null2(const gsl::not_null<T*> result0,\n                           const gsl::not_null<T*> result1, const T& t0,\n                           const T& t1) const {\n    if constexpr (tt::is_a_v<Tensor, T>) {\n      *result0 = T{get(t0) + 5.0 * a_ + b_ * get(t1)};\n      *result1 = T{2.0 * get(t0) + 5.0 * a_ + b_ * get(t1)};\n    } else {\n      *result0 = t0 + 5.0 * a_ + t1 * b_;\n      *result1 = 2.0 * t0 + 5.0 * a_ + t1 * b_;\n    }\n  }\n\n private:\n  double a_ = 0;\n  double b_ = 0;\n  std::array<double, 3> c_{};\n};\n\ntemplate <typename T>\nvoid test_free(const T& value) {\n  pypp::check_with_random_values<1>(&check_single_not_null0<T>, \"PyppPyTests\",\n                                    {\"check_single_not_null0\"},\n                                    {{{-10.0, 10.0}}}, value);\n  pypp::check_with_random_values<1>(&check_single_not_null1<T>, \"PyppPyTests\",\n                                    {\"check_single_not_null1\"},\n                                    {{{-10.0, 10.0}}}, value);\n  pypp::check_with_random_values<2>(&check_single_not_null2<T>, \"PyppPyTests\",\n                                    {\"check_single_not_null2\"},\n                                    {{{0.0, 10.0}, {-10.0, 0.0}}}, value);\n  pypp::check_with_random_values<1>(&check_single_not_null3<T>, \"PyppPyTests\",\n                                    {\"check_single_not_null0\"},\n                                    {{{-10.0, 10.0}}}, value, 1.e-12, {}, 2.);\n\n  pypp::check_with_random_values<1>(\n      &check_double_not_null0<T>, \"PyppPyTests\",\n      {\"check_double_not_null0_result0\", \"check_double_not_null0_result1\"},\n      {{{-10.0, 10.0}}}, value);\n  pypp::check_with_random_values<1>(\n      &check_double_not_null1<T>, \"PyppPyTests\",\n      {\"check_double_not_null1_result0\", \"check_double_not_null1_result1\"},\n      {{{-10.0, 10.0}}}, value);\n  // [cxx_two_not_null]\n  pypp::check_with_random_values<2>(\n      &check_double_not_null2<T>, \"PyppPyTests\",\n      {\"check_double_not_null2_result0\", \"check_double_not_null2_result1\"},\n      {{{0.0, 10.0}, {-10.0, 0.0}}}, value);\n  // [cxx_two_not_null]\n\n  pypp::check_with_random_values<1>(&check_by_value0<T>, \"PyppPyTests\",\n                                    \"check_by_value0\", {{{-10.0, 10.0}}},\n                                    value);\n  pypp::check_with_random_values<1>(&check_by_value1<T>, \"PyppPyTests\",\n                                    \"check_by_value1\", {{{-10.0, 10.0}}},\n                                    value);\n  pypp::check_with_random_values<2>(&check_by_value2<T>, \"PyppPyTests\",\n                                    \"check_by_value2\",\n                                    {{{0.0, 10.0}, {-10.0, 0.0}}}, value);\n}\n\ntemplate <typename T>\nvoid test_member_functions(const T& value) {\n  const double a = 3.1;\n  const double b = 7.24;\n  const std::array<double, 3> c{{4.23, -8.3, 5.4}};\n  const RandomValuesTests test_class{a, b, c};\n\n  // by value, single argument\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check_by_value0<T>, test_class, \"PyppPyTests\",\n      \"check_by_value0\", {{{-10.0, 10.0}}}, std::make_tuple(), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check_by_value1<T>, test_class, \"PyppPyTests\",\n      \"check_by_value1_class\", {{{-10.0, 10.0}}}, std::make_tuple(a), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check_by_value2<T>, test_class, \"PyppPyTests\",\n      \"check_by_value2_class\", {{{-10.0, 10.0}}}, std::make_tuple(a, b), value);\n  pypp::check_with_random_values<1>(&RandomValuesTests::check_by_value3<T>,\n                                    test_class, \"PyppPyTests\",\n                                    \"check_by_value3_class\", {{{-10.0, 10.0}}},\n                                    std::make_tuple(a, b, c), value);\n\n  // by value, two arguments\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check2_by_value0<T>, test_class, \"PyppPyTests\",\n      \"check_by_value1\", {{{-10.0, 10.0}}}, std::make_tuple(), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check2_by_value1<T>, test_class, \"PyppPyTests\",\n      \"check2_by_value1_class\", {{{-10.0, 10.0}}}, std::make_tuple(a), value);\n  pypp::check_with_random_values<1>(&RandomValuesTests::check2_by_value2<T>,\n                                    test_class, \"PyppPyTests\",\n                                    \"check2_by_value2_class\", {{{-10.0, 10.0}}},\n                                    std::make_tuple(a, b), value);\n  pypp::check_with_random_values<1>(&RandomValuesTests::check2_by_value3<T>,\n                                    test_class, \"PyppPyTests\",\n                                    \"check2_by_value3_class\", {{{-10.0, 10.0}}},\n                                    std::make_tuple(a, b, c), value);\n\n  // Single not_null, single argument\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check_by_not_null0<T>, test_class, \"PyppPyTests\"s,\n      {\"check_by_value0\"s}, {{{-10.0, 10.0}}}, std::make_tuple(), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check_by_not_null1<T>, test_class, \"PyppPyTests\",\n      {\"check_by_value1_class\"}, {{{-10.0, 10.0}}}, std::make_tuple(a), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check_by_not_null2<T>, test_class, \"PyppPyTests\",\n      {\"check_by_value2_class\"}, {{{-10.0, 10.0}}}, std::make_tuple(a, b),\n      value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check_by_not_null3<T>, test_class, \"PyppPyTests\",\n      {\"check_by_value3_class\"}, {{{-10.0, 10.0}}}, std::make_tuple(a, b, c),\n      value);\n\n  // Single not_null, two arguments\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check2_by_not_null0<T>, test_class, \"PyppPyTests\",\n      {\"check_by_value1\"}, {{{-10.0, 10.0}}}, std::make_tuple(), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check2_by_not_null1<T>, test_class, {\"PyppPyTests\"},\n      {\"check2_by_value1_class\"}, {{{-10.0, 10.0}}}, std::make_tuple(a), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check2_by_not_null2<T>, test_class, {\"PyppPyTests\"},\n      {\"check2_by_value2_class\"}, {{{-10.0, 10.0}}}, std::make_tuple(a, b),\n      value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check2_by_not_null3<T>, test_class, {\"PyppPyTests\"},\n      {\"check2_by_value3_class\"}, {{{-10.0, 10.0}}}, std::make_tuple(a, b, c),\n      value);\n\n  // Double not_null, two arguments\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check3_by_not_null0<T>, test_class, \"PyppPyTests\",\n      {\"check_double_not_null1_result0\", \"check_double_not_null1_result1\"},\n      {{{-10.0, 10.0}}}, std::make_tuple(), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check3_by_not_null1<T>, test_class, {\"PyppPyTests\"},\n      {\"check2_by_value1_class\", \"check2_by_value1_class1\"}, {{{-10.0, 10.0}}},\n      std::make_tuple(a), value);\n  pypp::check_with_random_values<1>(\n      &RandomValuesTests::check3_by_not_null2<T>, test_class, {\"PyppPyTests\"},\n      {\"check2_by_value2_class\", \"check2_by_value2_class1\"}, {{{-10.0, 10.0}}},\n      std::make_tuple(a, b), value);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Pypp.CheckRandomValues\", \"[Pypp][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"Framework/Tests/\"};\n  constexpr size_t size = 5;\n  const DataVector dv(size);\n  const double doub(0.);\n  const Scalar<double> scalar_double{5.0};\n  const Scalar<DataVector> scalar_dv{size};\n  test_free(doub);\n  test_free(dv);\n  test_free(scalar_double);\n  test_free(scalar_dv);\n\n  // Test member functions\n  test_member_functions(doub);\n  test_member_functions(dv);\n  test_member_functions(scalar_double);\n  test_member_functions(scalar_dv);\n}\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/Test_TestCreation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <ostream>\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nenum class Color { Red, Green, Purple };\n}  // namespace\n\ntemplate <>\nstruct Options::create_from_yaml<Color> {\n  template <typename Metavariables>\n  static Color create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nColor Options::create_from_yaml<Color>::create<void>(\n    const Options::Option& options) {\n  const auto color_read = options.parse_as<std::string>();\n  if (color_read == \"Red\") {\n    return Color::Red;\n  } else if (color_read == \"Green\") {\n    return Color::Green;\n  } else if (color_read == \"Purple\") {\n    return Color::Purple;\n  }\n  PARSE_ERROR(options.context(), \"Failed to convert \\\"\"\n                                     << color_read\n                                     << \"\\\" to Color. Expected one of: \"\n                                        \"{Red, Green, Purple}.\");\n}\n\nnamespace {\n// [class_without_metavariables]\nstruct ClassWithoutMetavariables {\n  struct SizeT {\n    using type = size_t;\n    static constexpr Options::String help = {\"SizeT help\"};\n  };\n\n  using options = tmpl::list<SizeT>;\n  static constexpr Options::String help = {\"Help\"};\n\n  explicit ClassWithoutMetavariables(const size_t in_value) : value(in_value) {}\n\n  ClassWithoutMetavariables() = default;\n\n  size_t value{0};\n};\n// [class_without_metavariables]\n\nstruct ClassWithMetavariables {\n  struct SizeT {\n    using type = size_t;\n    static constexpr Options::String help = {\"SizeT help\"};\n  };\n\n  using options = tmpl::list<SizeT>;\n  static constexpr Options::String help = {\"Help\"};\n\n  template <typename Metavariables>\n  // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n  ClassWithMetavariables(const size_t in_value,\n                         const Options::Context& /*context*/,\n                         Metavariables /*meta*/) {\n    value = in_value * Metavariables::value_multiplier;\n  }\n\n  ClassWithMetavariables(const size_t /*in_value*/,\n                         const Options::Context& /*context*/,\n                         NoSuchType /*meta*/)\n      : value{std::numeric_limits<size_t>::max()} {}\n\n  ClassWithMetavariables() = default;\n\n  size_t value{0};\n};\n\nstruct OptionGroup1 {\n  static constexpr Options::String help = {\"OptionGroup1 help\"};\n};\n\nstruct OptionGroup2 {\n  static constexpr Options::String help = {\"OptionGroup2 help\"};\n  using group = OptionGroup1;\n};\n\ntemplate <typename T>\nstruct NoGroup {\n  using type = T;\n  static constexpr Options::String help = {\"halp\"};\n};\n\ntemplate <typename T>\nstruct OneGroup {\n  using type = T;\n  static constexpr Options::String help = {\"halp\"};\n  using group = OptionGroup1;\n};\n\ntemplate <typename T>\nstruct TwoGroup {\n  using type = T;\n  static constexpr Options::String help = {\"halp\"};\n  using group = OptionGroup2;\n};\n\ntemplate <size_t ValueMultiplier>\nstruct Metavars {\n  static constexpr size_t value_multiplier = ValueMultiplier;\n};\n\n// [class_without_metavariables_tag]\nstruct ExampleTag {\n  using type = ClassWithoutMetavariables;\n  static constexpr Options::String help = {\"help\"};\n  using group = OptionGroup1;\n};\n// [class_without_metavariables_tag]\n\nstruct BaseClass {\n  BaseClass() = default;\n  BaseClass(const BaseClass&) = delete;\n  BaseClass& operator=(const BaseClass&) = delete;\n  BaseClass(BaseClass&&) = default;\n  BaseClass& operator=(BaseClass&&) = default;\n  virtual ~BaseClass() = default;\n\n  virtual size_t get_value() const = 0;\n};\n\nstruct DerivedClass : BaseClass {\n  struct SizeT {\n    using type = size_t;\n    static constexpr Options::String help = {\"SizeT help\"};\n  };\n\n  using options = tmpl::list<SizeT>;\n  static constexpr Options::String help = {\"Help\"};\n\n  explicit DerivedClass(const size_t in_value) : value(in_value) {}\n\n  DerivedClass() = default;\n  DerivedClass(const DerivedClass&) = delete;\n  DerivedClass& operator=(const DerivedClass&) = delete;\n  DerivedClass(DerivedClass&&) = default;\n  DerivedClass& operator=(DerivedClass&&) = default;\n  ~DerivedClass() override = default;\n\n  size_t get_value() const override { return value; }\n\n  size_t value{0};\n};\n\nvoid test_test_creation() {\n  // Test creation of fundamentals\n  CHECK(TestHelpers::test_creation<double>(\"1.846\") == 1.846);\n  CHECK(TestHelpers::test_option_tag<NoGroup<double>>(\"1.846\") == 1.846);\n  CHECK(TestHelpers::test_option_tag<OneGroup<double>>(\"1.846\") == 1.846);\n  CHECK(TestHelpers::test_option_tag<TwoGroup<double>>(\"1.846\") == 1.846);\n\n  // Test class that doesn't need metavariables when not passing metavariables\n  // [class_without_metavariables_create]\n  CHECK(\n      TestHelpers::test_creation<ClassWithoutMetavariables>(\"SizeT: 7\").value ==\n      7);\n  // [class_without_metavariables_create]\n  // [class_without_metavariables_create_tag]\n  CHECK(TestHelpers::test_option_tag<ExampleTag>(\"SizeT: 7\").value == 7);\n  // [class_without_metavariables_create_tag]\n\n  CHECK(TestHelpers::test_option_tag<NoGroup<ClassWithoutMetavariables>>(\n            \"SizeT: 4\")\n            .value == 4);\n  CHECK(TestHelpers::test_option_tag<OneGroup<ClassWithoutMetavariables>>(\n            \"SizeT: 5\")\n            .value == 5);\n  CHECK(TestHelpers::test_option_tag<TwoGroup<ClassWithoutMetavariables>>(\n            \"SizeT: 6\")\n            .value == 6);\n\n  // Test class that doesn't need metavariables but passing metavariables\n  CHECK(TestHelpers::test_creation<ClassWithoutMetavariables, Metavars<3>>(\n            \"SizeT: 8\")\n            .value == 8);\n  CHECK(TestHelpers::test_option_tag<NoGroup<ClassWithoutMetavariables>,\n                                     Metavars<4>>(\"SizeT: 9\")\n            .value == 9);\n  CHECK(TestHelpers::test_option_tag<OneGroup<ClassWithoutMetavariables>,\n                                     Metavars<5>>(\"SizeT: 10\")\n            .value == 10);\n  CHECK(TestHelpers::test_option_tag<TwoGroup<ClassWithoutMetavariables>,\n                                     Metavars<6>>(\"SizeT: 11\")\n            .value == 11);\n\n  // Test class that uses metavariables but not passing metavariables\n  CHECK(\n      TestHelpers::test_option_tag<NoGroup<ClassWithMetavariables>>(\"SizeT: 4\")\n          .value == std::numeric_limits<size_t>::max());\n  CHECK(\n      TestHelpers::test_option_tag<OneGroup<ClassWithMetavariables>>(\"SizeT: 4\")\n          .value == std::numeric_limits<size_t>::max());\n  CHECK(\n      TestHelpers::test_option_tag<TwoGroup<ClassWithMetavariables>>(\"SizeT: 4\")\n          .value == std::numeric_limits<size_t>::max());\n\n  // Test class that uses metavariables but passing metavariables\n  CHECK(TestHelpers::test_creation<ClassWithMetavariables, Metavars<3>>(\n            \"SizeT: 4\")\n            .value == 12);\n  CHECK(TestHelpers::test_option_tag<NoGroup<ClassWithMetavariables>,\n                                     Metavars<4>>(\"SizeT: 4\")\n            .value == 16);\n  CHECK(TestHelpers::test_option_tag<OneGroup<ClassWithMetavariables>,\n                                     Metavars<5>>(\"SizeT: 4\")\n            .value == 20);\n  CHECK(TestHelpers::test_option_tag<TwoGroup<ClassWithMetavariables>,\n                                     Metavars<6>>(\"SizeT: 4\")\n            .value == 24);\n}\n\n// [test_option_tag_factory_creation_tag]\nnamespace OptionTags {\nstruct BaseClass {\n  static constexpr Options::String help = \"Halp\";\n  using type = std::unique_ptr<::BaseClass>;\n};\n}  // namespace OptionTags\n// [test_option_tag_factory_creation_tag]\n\nvoid test_test_factory_creation() {\n  // [test_factory_creation]\n  CHECK(TestHelpers::test_factory_creation<BaseClass, DerivedClass>(\n            \"DerivedClass:\\n\"\n            \"  SizeT: 5\")\n            ->get_value() == 5);\n  // [test_factory_creation]\n\n  // [test_option_tag_factory_creation]\n  CHECK(TestHelpers::test_option_tag_factory_creation<OptionTags::BaseClass,\n                                                      DerivedClass>(\n            \"DerivedClass:\\n\"\n            \"  SizeT: 5\")\n            ->get_value() == 5);\n  // [test_option_tag_factory_creation]\n}\n\nvoid test_test_enum_creation() {\n  CHECK(TestHelpers::test_creation<Color>(\"Purple\") == Color::Purple);\n  CHECK(TestHelpers::test_option_tag<NoGroup<Color>>(\"Purple\") ==\n        Color::Purple);\n  CHECK(TestHelpers::test_option_tag<OneGroup<Color>>(\"Purple\") ==\n        Color::Purple);\n  CHECK(TestHelpers::test_option_tag<TwoGroup<Color>>(\"Purple\") ==\n        Color::Purple);\n  CHECK(TestHelpers::test_creation<Color, Metavars<3>>(\"Purple\") ==\n        Color::Purple);\n  CHECK(TestHelpers::test_option_tag<NoGroup<Color>, Metavars<3>>(\"Purple\") ==\n        Color::Purple);\n  CHECK(TestHelpers::test_option_tag<OneGroup<Color>, Metavars<3>>(\"Purple\") ==\n        Color::Purple);\n  CHECK(TestHelpers::test_option_tag<TwoGroup<Color>, Metavars<3>>(\"Purple\") ==\n        Color::Purple);\n}\n\nSPECTRE_TEST_CASE(\"Unit.TestCreation\", \"[Unit]\") {\n  test_test_creation();\n  test_test_factory_creation();\n  test_test_enum_creation();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/Test_TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <map>\n#include <random>\n#include <set>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\nvoid test_test_helpers() {\n  std::vector<double> vector{0, 1, 2, 3};\n  test_iterators(vector);\n  test_reverse_iterators(vector);\n\n  std::set<double> set;\n  set.insert(0);\n  set.insert(1);\n  set.insert(2);\n  set.insert(3);\n  test_iterators(set);\n  test_reverse_iterators(set);\n\n  std::unordered_set<int> u_set;\n  u_set.insert(3);\n  u_set.insert(2);\n  u_set.insert(1);\n  u_set.insert(0);\n  test_iterators(u_set);\n\n  Approx larger_approx =\n      Approx::custom().epsilon(std::numeric_limits<double>::epsilon() * 1.e4);\n\n  const std::vector<double> vec_a{1., 2., 3.5};\n  CHECK_ITERABLE_APPROX(vec_a, vec_a);\n  auto vec_b = vec_a;\n  vec_b[1] += 1e-15;\n  CHECK(vec_a != vec_b);\n  CHECK_ITERABLE_APPROX(vec_a, vec_b);\n  vec_b[1] += 1e-12;\n  CHECK_ITERABLE_CUSTOM_APPROX(vec_a, vec_b, larger_approx);\n\n  const std::vector<std::complex<double>> complex_vector{\n      std::complex<double>(1.0, 1.5), std::complex<double>(2.0, 2.5),\n      std::complex<double>(3.0, 3.5)};\n  CHECK_ITERABLE_APPROX(complex_vector, complex_vector);\n  auto perturbed_complex_vector = complex_vector;\n  perturbed_complex_vector[1] += 1e-15;\n  CHECK(complex_vector != perturbed_complex_vector);\n  CHECK_ITERABLE_APPROX(complex_vector, perturbed_complex_vector);\n  perturbed_complex_vector[1] += 1e-12;\n  CHECK_ITERABLE_CUSTOM_APPROX(complex_vector, perturbed_complex_vector,\n                               larger_approx);\n\n  const std::vector<std::map<int, double>> vecmap_a{\n      {{1, 1.}, {2, 2.}}, {{1, 1.23}, {3, 4.56}, {5, 7.89}}};\n  CHECK_ITERABLE_APPROX(vecmap_a, vecmap_a);\n  auto vecmap_b = vecmap_a;\n  vecmap_b[1][1] += 1e-15;\n  CHECK(vecmap_a != vecmap_b);\n  CHECK_ITERABLE_APPROX(vecmap_a, vecmap_b);\n  vecmap_b[1][1] += 1e-12;\n  CHECK_ITERABLE_CUSTOM_APPROX(vecmap_a, vecmap_b, larger_approx);\n\n  // Check that CHECK_ITERABLE_APPROX works on various containers\n  {\n    const std::set<int> a{1, 2, 3};\n    CHECK_ITERABLE_APPROX(a, a);\n  }\n  {\n    // Iteration order is unspecified, but we create the containers\n    // differently so the order might differ between them.\n    const std::unordered_set<int> a{1, 2, 3};\n    const std::unordered_set<int> b{3, 2, 1};\n    CHECK_ITERABLE_APPROX(a, b);\n  }\n  {\n    // Iteration order is unspecified, but we create the containers\n    // differently so the order might differ between them.\n    const std::unordered_map<int, int> a{{1, 2}, {2, 3}, {3, 4}};\n    const std::unordered_map<int, int> b{{3, 4}, {2, 3}, {1, 2}};\n    CHECK_ITERABLE_APPROX(a, b);\n  }\n}\n\nvoid test_derivative() {\n  {  // 3D Test\n    const std::array<double, 3> x{{1.2, -3.4, 1.3}};\n    const double delta = 1.e-2;\n\n    const auto func = [](const std::array<double, 3>& y) {\n      return std::array<double, 3>{{sin(y[0]), cos(y[1]), exp(y[2])}};\n    };\n    const auto dfunc = [](const std::array<double, 3>& y) {\n      return std::array<double, 3>{{cos(y[0]), -sin(y[1]), exp(y[2])}};\n    };\n\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(gsl::at(numerical_derivative(func, x, i, delta), i) ==\n            approx(gsl::at(dfunc(x), i)));\n    }\n  }\n  {  // 2D Test\n    const std::array<double, 2> x{{1.2, -2.4}};\n    const double delta = 1.e-2;\n\n    const auto func = [](const std::array<double, 2>& y) {\n      return std::array<double, 2>{{sin(y[0]), cos(y[1])}};\n    };\n    const auto dfunc = [](const std::array<double, 2>& y) {\n      return std::array<double, 2>{{cos(y[0]), -sin(y[1])}};\n    };\n\n    for (size_t i = 0; i < 2; ++i) {\n      CHECK(gsl::at(numerical_derivative(func, x, i, delta), i) ==\n            approx(gsl::at(dfunc(x), i)));\n    }\n  }\n  {  // Non-array return type\n    const std::array<double, 1> x{{1.2}};\n    const double delta = 1.e-2;\n\n    const auto func = [](const std::array<double, 1>& y) { return sin(y[0]); };\n    const auto dfunc = [](const std::array<double, 1>& y) { return cos(y[0]); };\n\n    CHECK(numerical_derivative(func, x, 0, delta) == approx(dfunc(x)));\n  }\n  {  // Floating point precision test\n    const auto func = [](const std::array<double, 1>& /*x*/) { return 1.0; };\n    CHECK(numerical_derivative(func, std::array{1.0}, 0, 1.e-2) == 0.0);\n  }\n}\n\nvoid test_make_generator(const gsl::not_null<std::mt19937*> generator) {\n  MAKE_GENERATOR(gen2);\n  // This will fail randomly every 2**32 runs.  That is probably OK.\n  CHECK((*generator)() != gen2());\n\n  MAKE_GENERATOR(seeded_gen, 12345);\n  CHECK(seeded_gen() == 3992670690);\n\n  CHECK_THROWS_WITH([]() { ERROR(\"\"); }(),\n                    Catch::Matchers::ContainsSubstring(\"Seed is: 12345 from \"));\n}\n\nvoid test_random_sample(const gsl::not_null<std::mt19937*> generator) {\n  const std::vector<double> vec{1., 2., 3.5};\n  for (const double rnd : random_sample<2>(vec, generator)) {\n    CHECK((rnd == 1. or rnd == 2. or rnd == 3.5));\n  }\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(random_sample<5>(vec, generator),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot take 5 samples from container of size 3\"));\n#endif\n  const std::unordered_set ints{1, 4, 2, 3};\n  const std::vector<int> two_samples = random_sample(2, ints, generator);\n  CHECK(two_samples.size() == 2);\n  const std::vector<int> over_sampled = random_sample(10, ints, generator);\n  CHECK(over_sampled.size() == ints.size());\n  const auto check_sample = [&ints](const std::vector<int>& samples) {\n    for (const auto& sample : samples) {\n      CHECK(alg::count(ints, sample) == 1);\n      CHECK(alg::count(samples, sample) == 1);\n    }\n  };\n  check_sample(two_samples);\n  check_sample(over_sampled);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.TestHelpers\", \"[Unit]\") {\n  test_test_helpers();\n  test_derivative();\n  MAKE_GENERATOR(gen1);\n  test_make_generator(make_not_null(&gen1));\n  test_random_sample(make_not_null(&gen1));\n}\n"
  },
  {
    "path": "tests/Unit/Framework/Tests/Test_TestingFramework.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/System/Abort.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.TestingFramework.Approx\", \"[Unit]\") {\n  // [approx_test]\n  CHECK(1.0 == approx(1.0 + 1e-15));\n  CHECK(1.0 != approx(1.0 + 1e-13));\n  // [approx_test]\n  // also check numbers that are not order unity, but do not include in example\n  CHECK(1e-10 == approx(1e-10 + 1e-25).scale(1e-10));\n  CHECK(1e-10 == approx(1e-10 + 1e-23));\n  CHECK(1e-10 != approx(1e-10 + 1e-23).scale(1e-10));\n  CHECK(1e+10 == approx(1e+10 + 1e-5));\n  CHECK(1e+10 != approx(1e+10 + 1e-3));\n\n  // [approx_default]\n  CHECK(sin(M_PI / 4.0) == approx(cos(M_PI / 4.0)));\n  // [approx_default]\n  // [approx_single_custom]\n  // This check needs tolerance 1e-12 for X reason.\n  CHECK(1.0 == approx(1.0 + 5e-13).epsilon(1e-12));\n  // [approx_single_custom]\n  // [approx_new_custom]\n  // The checks in this test need tolerance 1e-12 for X reason.\n  Approx my_approx = Approx::custom().epsilon(1e-12);\n  CHECK(1.0 == my_approx(1.0 + 5e-13));\n  CHECK(1.0 != my_approx(1.0 + 5e-12));\n  // [approx_new_custom]\n\n  Approx custom_approx = Approx::custom().epsilon(5.e-12).scale(1.0);\n\n  CHECK_ITERABLE_APPROX(1.0, 1.0 + 1.e-15);\n  CHECK_ITERABLE_CUSTOM_APPROX(1.0, 1.0 + 1.e-13, custom_approx);\n\n  const std::complex c0{1.0, -1.0};\n  const std::complex c1{1.0 + 1.e-15, -1.0 + 1.e-15};\n  const std::complex c2{1.0 + 1.e-13, -1.0 + 1.e-13};\n  CHECK_ITERABLE_APPROX(c0, c1);\n  CHECK_ITERABLE_CUSTOM_APPROX(c0, c2, custom_approx);\n\n  DataVector dv0{1.0, 1.0, 1.0};\n  DataVector dv1{1.0 + 1.e-15, 1.0 - 1.e-15, 1.0};\n  DataVector dv2{1.0 + 1.e-15, 1.0 - 1.e-15, 1.0 - 1.e-13};\n  CHECK_ITERABLE_APPROX(dv0, dv1);\n  CHECK_ITERABLE_CUSTOM_APPROX(dv0, dv2, custom_approx);\n}\n\n// [[OutputRegex, I failed]]\n[[noreturn]] SPECTRE_TEST_CASE(\"Unit.TestingFramework.Abort\", \"[Unit]\") {\n  OUTPUT_TEST();\n  sys::abort(\"I failed\");\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(ControlSystem)\nadd_subdirectory(DataStructures)\nadd_subdirectory(Domain)\nadd_subdirectory(Elliptic)\nadd_subdirectory(Evolution)\nadd_subdirectory(IO)\nadd_subdirectory(NumericalAlgorithms)\nadd_subdirectory(Parallel)\nadd_subdirectory(ParallelAlgorithms)\nadd_subdirectory(PointwiseFunctions)\nadd_subdirectory(Time)\nadd_subdirectory(Tests)\nadd_subdirectory(Utilities)\n"
  },
  {
    "path": "tests/Unit/Helpers/ControlSystem/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"ControlSystemHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  Examples.hpp\n  SystemHelpers.hpp\n  TestStructs.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  ControlSystem\n  Domain\n  DataStructures\n  Observer\n  Parallel\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/ControlSystem/Examples.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/Protocols/Submeasurement.hpp\"\n#include \"ControlSystem/RunCallbacks.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace control_system::TestHelpers {\nstruct SomeTagOnElement : db::SimpleTag {\n  using type = double;\n};\n\nstruct SomeOtherTagOnElement : db::SimpleTag {\n  using type = double;\n};\n\nstruct SomeOtherTagOnElementCompute : SomeOtherTagOnElement, db::ComputeTag {\n  using base = SomeOtherTagOnElement;\n  using return_type = double;\n  using argument_tags = tmpl::list<>;\n  static void function(const gsl::not_null<double*> result) { *result = 0.0; }\n};\n\nstruct MeasurementResultTag : db::SimpleTag {\n  using type = double;\n};\n\nstruct MeasurementResultTime : db::SimpleTag {\n  using type = double;\n};\n\nstruct SomeControlSystemUpdater {\n  // The measurement tags will probably not be template parameters in\n  // most cases, but it makes it easier to make a clean example where\n  // this is called.\n  template <typename DbTags, typename Metavariables, typename ArrayIndex,\n            typename SubmeasurementTag>\n  static void apply(const gsl::not_null<db::DataBox<DbTags>*> box,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const double time,\n                    tuples::TaggedTuple<SubmeasurementTag> data) {\n    db::mutate<MeasurementResultTime, MeasurementResultTag>(\n        [&time, &data](const gsl::not_null<double*> stored_time,\n                       const gsl::not_null<double*> stored_data) {\n          *stored_time = time;\n          *stored_data = tuples::get<SubmeasurementTag>(data);\n        },\n        box);\n  }\n};\n\ntemplate <typename ControlSystems>\nstruct SomeEvent {};\n\n/// [Submeasurement]\nstruct ExampleSubmeasurement\n    : tt::ConformsTo<control_system::protocols::Submeasurement> {\n  // This submeasurement does not use an interpolation component.\n  template <typename ControlSystems>\n  using interpolation_target_tag = void;\n  // This submeasurement does not find a horizon.\n  template <typename ControlSystems>\n  using horizon_metavars = void;\n\n  template <typename ControlSystems>\n  using event = SomeEvent<ControlSystems>;\n};\n/// [Submeasurement]\n\n/// [Measurement]\nstruct ExampleMeasurement\n    : tt::ConformsTo<control_system::protocols::Measurement> {\n  using submeasurements = tmpl::list<ExampleSubmeasurement>;\n};\n/// [Measurement]\n\n/// [ControlError]\nstruct ExampleControlError\n    : tt::ConformsTo<control_system::protocols::ControlError> {\n  using object_centers = domain::object_list<domain::ObjectLabel::A>;\n\n  std::optional<double> get_suggested_timescale() const {\n    return suggested_timescale_;\n  }\n\n  void reset() { suggested_timescale_ = std::nullopt; }\n\n  void pup(PUP::er& /*p*/) {}\n\n  template <typename Metavariables, typename... QueueTags>\n  DataVector operator()(const ::TimescaleTuner<true>& tuner,\n                        const Parallel::GlobalCache<Metavariables>& cache,\n                        const double time,\n                        const std::string& function_of_time_name,\n                        const tuples::TaggedTuple<QueueTags...>& measurements) {\n    const auto& functions_of_time =\n        Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n    const double current_map_value =\n        functions_of_time.at(function_of_time_name)->func(time)[0][0];\n    const double measured_value = 0.0;\n    suggested_timescale_ = 1.0;\n    // Would do something like get<QueueTag>(measurements) here\n    (void)measurements;\n    // Size control needs the timescale tuner\n    (void)tuner;\n\n    return {current_map_value - measured_value};\n  }\n\n private:\n  std::optional<double> suggested_timescale_;\n};\n/// [ControlError]\n\n/// [ControlSystem]\nstruct ExampleControlSystem\n    : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return \"ExampleControlSystem\"; }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t num_components) {\n    ASSERT(num_components == 3,\n           \"This control system expected 3 components but there are \"\n               << num_components << \" instead.\");\n    return i == 0 ? \"X\" : (i == 1 ? \"Y\" : \"Z\");\n  }\n  using measurement = ExampleMeasurement;\n\n  using simple_tags = tmpl::list<>;\n\n  static constexpr size_t deriv_order = 2;\n\n  using control_error = ExampleControlError;\n\n  // This is not part of the required interface, but is used by this\n  // control system to store the measurement data.  Most control\n  // systems will do something like this.\n  struct ExampleSubmeasurementQueueTag {\n    using type = double;\n  };\n\n  // As with the previous struct, this is not part of the required\n  // interface.\n  struct MeasurementQueue : db::SimpleTag {\n    using type =\n        LinkedMessageQueue<double, tmpl::list<ExampleSubmeasurementQueueTag>>;\n  };\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<MeasurementResultTag>;\n\n    template <typename Metavariables>\n    static void apply(ExampleSubmeasurement /*meta*/,\n                      const double measurement_result,\n                      Parallel::GlobalCache<Metavariables>& cache,\n                      const LinkedMessageId<double>& measurement_id) {\n      // Process the submeasurement results and send whatever is\n      // necessary to the control system component.  Usually calls\n      // some simple action.\n      auto& control_system_proxy = Parallel::get_parallel_component<\n          ControlComponent<Metavariables, ExampleControlSystem>>(cache);\n      Parallel::simple_action<::Actions::UpdateMessageQueue<\n          MeasurementQueue, SomeControlSystemUpdater,\n          ExampleSubmeasurementQueueTag>>(control_system_proxy, measurement_id,\n                                          measurement_result);\n    }\n  };\n};\n/// [ControlSystem]\n}  // namespace control_system::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/ControlSystem/SystemHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"ControlSystem/Averager.hpp\"\n#include \"ControlSystem/Component.hpp\"\n#include \"ControlSystem/ControlErrors/Expansion.hpp\"\n#include \"ControlSystem/ControlErrors/Rotation.hpp\"\n#include \"ControlSystem/ControlErrors/Shape.hpp\"\n#include \"ControlSystem/ControlErrors/Translation.hpp\"\n#include \"ControlSystem/Controller.hpp\"\n#include \"ControlSystem/ExpirationTimes.hpp\"\n#include \"ControlSystem/Measurements/BothHorizons.hpp\"\n#include \"ControlSystem/Measurements/SingleHorizon.hpp\"\n#include \"ControlSystem/Systems/Expansion.hpp\"\n#include \"ControlSystem/Systems/Rotation.hpp\"\n#include \"ControlSystem/Systems/Shape.hpp\"\n#include \"ControlSystem/Systems/Translation.hpp\"\n#include \"ControlSystem/Tags/IsActiveMap.hpp\"\n#include \"ControlSystem/Tags/MeasurementTimescales.hpp\"\n#include \"ControlSystem/Tags/QueueTags.hpp\"\n#include \"ControlSystem/Tags/SystemTags.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"ControlSystem/UpdateControlSystem.hpp\"\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/ObjectCenter.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ExcisionSphere.hpp\"\n#include \"Domain/FunctionsOfTime/FixedSpeedCubic.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/QuaternionFunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/CreateFromOptions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Time/OptionTags/InitialTime.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace OptionTags {\nstruct InitialTime;\n}  // namespace OptionTags\n/// \\endcond\n\nnamespace TestHelpers::control_system {\ntemplate <typename ControlSystem>\nusing init_simple_tags =\n    tmpl::list<::control_system::Tags::Averager<ControlSystem>,\n               ::control_system::Tags::TimescaleTuner<ControlSystem>,\n               ::control_system::Tags::Controller<ControlSystem>,\n               ::control_system::Tags::ControlError<ControlSystem>,\n               ::control_system::Tags::CurrentNumberOfMeasurements,\n               ::control_system::Tags::UpdateAggregators,\n               typename ControlSystem::MeasurementQueue>;\n\nclass FakeCreator : public DomainCreator<3> {\n private:\n  struct NumberOfComponents {\n    using type = std::unordered_map<std::string, size_t>;\n    static constexpr Options::String help = {\n        \"Num of components for function of time\"};\n  };\n\n public:\n  using options = tmpl::list<NumberOfComponents>;\n  static constexpr Options::String help = {\n      \"Num of components for all functions of time being tested.\"};\n\n  FakeCreator() = default;\n\n  FakeCreator(const std::unordered_map<std::string, size_t>& num_components_map)\n      : num_components_map_(num_components_map) {}\n\n  Domain<3> create_domain() const override {\n    std::unordered_map<std::string, ExcisionSphere<3>> excision_spheres{};\n    excision_spheres.insert(\n        {\"ExcisionSphereA\",\n         ExcisionSphere<3>{1.3,\n                           tnsr::I<double, 3, Frame::Grid>{{+0.9, 0.0, 0.0}},\n                           {{0, Direction<3>::lower_zeta()},\n                            {1, Direction<3>::lower_zeta()},\n                            {2, Direction<3>::lower_zeta()},\n                            {3, Direction<3>::lower_zeta()},\n                            {4, Direction<3>::lower_zeta()},\n                            {5, Direction<3>::lower_zeta()}}}});\n    excision_spheres.insert(\n        {\"ExcisionSphereB\",\n         ExcisionSphere<3>{0.8,\n                           tnsr::I<double, 3, Frame::Grid>{{-1.1, 0.0, 0.0}},\n                           {{0, Direction<3>::lower_zeta()},\n                            {1, Direction<3>::lower_zeta()},\n                            {2, Direction<3>::lower_zeta()},\n                            {3, Direction<3>::lower_zeta()},\n                            {4, Direction<3>::lower_zeta()},\n                            {5, Direction<3>::lower_zeta()}}}});\n\n    return Domain<3>{std::vector<Block<3>>{}, std::move(excision_spheres)};\n  }\n\n  std::vector<DirectionMap<\n      3, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override {\n    ERROR(\"\");\n  }\n  std::vector<std::array<size_t, 3>> initial_extents() const override {\n    ERROR(\"\");\n  }\n  std::vector<std::array<size_t, 3>> initial_refinement_levels()\n      const override {\n    ERROR(\"\");\n  }\n\n  // This won't give the proper functions of time, it's just used for the number\n  // of components so we can make the values and expiration times whatever we\n  // want.\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                         /*initial_expiration_times*/\n                         = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override {\n    std::unordered_map<std::string,\n                       std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n        all_functions_of_time{};\n\n    for (const auto& [name, num_components] : num_components_map_) {\n      if (name == \"Rotation\") {\n        // Rotation always has 3\n        all_functions_of_time[name] = std::make_unique<\n            domain::FunctionsOfTime::QuaternionFunctionOfTime<2>>(\n            0.0, std::array<DataVector, 1>{{{1.0, 0.0, 0.0, 0.0}}},\n            std::array<DataVector, 3>{{{3, 0.0}, {3, 0.0}, {3, 0.0}}}, 1.0);\n      } else {\n        all_functions_of_time[name] =\n            std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n                0.0, std::array<DataVector, 1>{{{num_components, 0.0}}}, 1.0);\n      }\n    }\n\n    return all_functions_of_time;\n  }\n\n private:\n  std::unordered_map<std::string, size_t> num_components_map_{};\n};\n\ntemplate <typename Metavariables, typename ControlSystem>\nstruct MockControlComponent {\n  using array_index = int;\n  using component_being_mocked = ControlComponent<Metavariables, ControlSystem>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n\n  using system = ControlSystem;\n\n  using simple_tags = init_simple_tags<ControlSystem>;\n\n  using const_global_cache_tags =\n      tmpl::list<::control_system::Tags::MeasurementsPerUpdate,\n                 ::control_system::Tags::DelayUpdate,\n                 ::control_system::Tags::WriteDataToDisk,\n                 ::control_system::Tags::Verbosity,\n                 ::control_system::Tags::IsActiveMap,\n                 domain::Tags::ObjectCenter<domain::ObjectLabel::A>,\n                 domain::Tags::ObjectCenter<domain::ObjectLabel::B>,\n                 ::control_system::Tags::SystemToCombinedNames>;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavars>\nstruct MockElementComponent {\n  using array_index = int;\n  using chare_type = ActionTesting::MockArrayChare;\n\n  using metavariables = Metavars;\n\n  using simple_tags = tmpl::list<>;\n\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<Metavars::volume_dim>>;\n\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 ::control_system::Tags::MeasurementTimescales>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavars>\nstruct MockObserverWriter {\n  using component_being_mocked = observers::ObserverWriter<Metavars>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using const_global_cache_tags =\n      tmpl::list<observers::Tags::ReductionFileName>;\n\n  using simple_tags_from_options = tmpl::list<>;\n  using simple_tags = tmpl::list<observers::Tags::H5FileLock>;\n\n  using metavariables = Metavars;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <size_t TranslationDerivOrder, size_t RotationDerivOrder,\n          size_t ExpansionDerivOrder, size_t ShapeDerivOrder>\nstruct MockMetavars {\n  static constexpr size_t volume_dim = 3;\n\n  using metavars = MockMetavars<TranslationDerivOrder, RotationDerivOrder,\n                                ExpansionDerivOrder, ShapeDerivOrder>;\n\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<volume_dim>, tmpl::list<FakeCreator>>>;\n  };\n\n  static constexpr bool using_expansion = ExpansionDerivOrder != 0;\n  static constexpr bool using_rotation = RotationDerivOrder != 0;\n  static constexpr bool using_translation = TranslationDerivOrder != 0;\n  static constexpr bool using_shape = ShapeDerivOrder != 0;\n\n  // Even if we aren't using certain control systems, we still need valid deriv\n  // orders because everything is constructed by default in the SystemHelper.\n  // The bool above just determines if the functions of time are actually\n  // created or not because that's what matters\n  static constexpr size_t exp_deriv_order =\n      using_expansion ? ExpansionDerivOrder : 2;\n  static constexpr size_t rot_deriv_order =\n      using_rotation ? RotationDerivOrder : 2;\n  static constexpr size_t trans_deriv_order =\n      using_translation ? TranslationDerivOrder : 2;\n  static constexpr size_t shape_deriv_order = using_shape ? ShapeDerivOrder : 2;\n\n  using element_component = MockElementComponent<metavars>;\n\n  using BothHorizons = ::control_system::measurements::BothHorizons;\n\n  using expansion_system =\n      ::control_system::Systems::Expansion<exp_deriv_order, BothHorizons>;\n  using rotation_system =\n      ::control_system::Systems::Rotation<rot_deriv_order, BothHorizons>;\n  using translation_system =\n      ::control_system::Systems::Translation<trans_deriv_order, BothHorizons,\n                                             2>;\n  using shape_system =\n      ::control_system::Systems::Shape<::domain::ObjectLabel::A,\n                                       shape_deriv_order, BothHorizons>;\n\n  using control_systems = tmpl::flatten<tmpl::list<\n      tmpl::conditional_t<using_expansion, expansion_system, tmpl::list<>>,\n      tmpl::conditional_t<using_rotation, rotation_system, tmpl::list<>>,\n      tmpl::conditional_t<using_translation, translation_system, tmpl::list<>>,\n      tmpl::conditional_t<using_shape, shape_system, tmpl::list<>>>>;\n\n  using expansion_component = MockControlComponent<metavars, expansion_system>;\n  using rotation_component = MockControlComponent<metavars, rotation_system>;\n  using translation_component =\n      MockControlComponent<metavars, translation_system>;\n  using shape_component = MockControlComponent<metavars, shape_system>;\n\n  using observer_component = MockObserverWriter<metavars>;\n\n  using control_components = tmpl::flatten<tmpl::list<\n      tmpl::conditional_t<using_expansion, expansion_component, tmpl::list<>>,\n      tmpl::conditional_t<using_rotation, rotation_component, tmpl::list<>>,\n      tmpl::conditional_t<using_translation, translation_component,\n                          tmpl::list<>>,\n      tmpl::conditional_t<using_shape, shape_component, tmpl::list<>>>>;\n\n  using component_list = tmpl::flatten<\n      tmpl::list<observer_component, element_component, control_components>>;\n};\n\ntemplate <typename ExpansionSystem>\ndouble initialize_expansion_functions_of_time(\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        functions_of_time,\n    const double initial_time,\n    const std::unordered_map<std::string, double>& initial_expiration_times) {\n  constexpr size_t deriv_order = ExpansionSystem::deriv_order;\n  const double initial_expansion = 1.0;\n  const double expansion_velocity_outer_boundary = 0.0;\n  const double decay_timescale_outer_boundary = 0.05;\n  auto init_func_expansion =\n      make_array<deriv_order + 1, DataVector>(DataVector{1, 0.0});\n  init_func_expansion[0][0] = initial_expansion;\n\n  const std::string expansion_name = ExpansionSystem::name();\n  (*functions_of_time)[expansion_name] = std::make_unique<\n      domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>>(\n      initial_time, init_func_expansion,\n      initial_expiration_times.at(expansion_name));\n  (*functions_of_time)[expansion_name + \"OuterBoundary\"s] =\n      std::make_unique<domain::FunctionsOfTime::FixedSpeedCubic>(\n          initial_expansion, initial_time, expansion_velocity_outer_boundary,\n          decay_timescale_outer_boundary);\n\n  return 1.0;\n}\n\ntemplate <typename RotationSystem>\ndouble initialize_rotation_functions_of_time(\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        functions_of_time,\n    const double initial_time,\n    const std::unordered_map<std::string, double>& initial_expiration_times) {\n  constexpr size_t deriv_order = RotationSystem::deriv_order;\n\n  const double initial_omega_z = 0.01;\n  auto init_func_rotation =\n      make_array<deriv_order + 1, DataVector>(DataVector{3, 0.0});\n  init_func_rotation[1][2] = initial_omega_z;\n  auto init_quaternion = make_array<1, DataVector>(DataVector{4, 0.0});\n  init_quaternion[0][0] = 1.0;\n\n  const std::string rotation_name = RotationSystem::name();\n  (*functions_of_time)[rotation_name] = std::make_unique<\n      domain::FunctionsOfTime::QuaternionFunctionOfTime<deriv_order>>(\n      initial_time, init_quaternion, init_func_rotation,\n      initial_expiration_times.at(rotation_name));\n\n  return 1.0;\n}\n\ntemplate <typename TranslationSystem>\ndouble initialize_translation_functions_of_time(\n    const gsl::not_null<std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n        functions_of_time,\n    const double initial_time,\n    const std::unordered_map<std::string, double>& initial_expiration_times) {\n  constexpr size_t deriv_order = TranslationSystem::deriv_order;\n\n  auto init_func_translation =\n      make_array<deriv_order + 1, DataVector>(DataVector{3, 0.0});\n\n  const std::string translation_name = TranslationSystem::name();\n  (*functions_of_time)[translation_name] = std::make_unique<\n      domain::FunctionsOfTime::PiecewisePolynomial<deriv_order>>(\n      initial_time, init_func_translation,\n      initial_expiration_times.at(translation_name));\n\n  if (initial_expiration_times.count(\"Rotation\") == 0) {\n    auto local_init_func_rotation =\n        make_array<1, DataVector>(DataVector{4, 0.0});\n    local_init_func_rotation[0][0] = 1.0;\n    (*functions_of_time)[\"Rotation\"] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n            initial_time, local_init_func_rotation,\n            std::numeric_limits<double>::infinity());\n  }\n  if (initial_expiration_times.count(\"Expansion\") == 0) {\n    auto local_init_func_expansion =\n        make_array<1, DataVector>(DataVector{1, 0.0});\n    local_init_func_expansion[0][0] = 1.0;\n    (*functions_of_time)[\"Expansion\"] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n            initial_time, local_init_func_expansion,\n            std::numeric_limits<double>::infinity());\n  }\n\n  return 1.0;\n}\n\ntemplate <typename ElementComponent, typename Metavars, typename F,\n          typename CoordMap>\nstd::array<tnsr::I<double, 3, Frame::Grid>, 2>\ngrid_frame_horizon_centers_for_basic_control_systems(\n    const double time, ActionTesting::MockRuntimeSystem<Metavars>& runner,\n    const F position_function, const CoordMap& coord_map) {\n  auto& cache = ActionTesting::cache<ElementComponent>(runner, 0);\n  const auto& functions_of_time =\n      Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n  const auto positions = position_function(time);\n  return {{*coord_map.inverse(positions[0], time, functions_of_time),\n           *coord_map.inverse(positions[1], time, functions_of_time)}};\n}\n\ntemplate <typename ElementComponent, typename Metavars, typename F,\n          typename CoordMap>\nstd::pair<ylm::Strahlkorper<Frame::Distorted>,\n          ylm::Strahlkorper<Frame::Distorted>>\nbuild_horizons_for_basic_control_systems(\n    const double time, ActionTesting::MockRuntimeSystem<Metavars>& runner,\n    const F position_function, const CoordMap& coord_map) {\n  const auto positions =\n      grid_frame_horizon_centers_for_basic_control_systems<ElementComponent>(\n          time, runner, position_function, coord_map);\n\n  // Construct strahlkorpers to pass to control systems. Only the centers\n  // matter.\n  ylm::Strahlkorper<Frame::Distorted> horizon_a{\n      2,\n      2,\n      1.0,\n      {{get<0>(positions[0]), get<1>(positions[0]), get<2>(positions[0])}}};\n  ylm::Strahlkorper<Frame::Distorted> horizon_b{\n      2,\n      2,\n      1.0,\n      {{get<0>(positions[1]), get<1>(positions[1]), get<2>(positions[1])}}};\n\n  return std::make_pair<ylm::Strahlkorper<Frame::Distorted>,\n                        ylm::Strahlkorper<Frame::Distorted>>(\n      std::move(horizon_a), std::move(horizon_b));\n}\n\n/*!\n * \\brief Helper struct for testing basic control systems\n *\n * To signify which control systems you want, set the corresponding\n * DerivOrder. To turn control systems off, put 0 for their DerivOrder in the\n * templates of the metavariables.\n *\n * Ideally we'd construct the runner here and just pass that to the test to\n * simplify as must of the work as possible, but MockRuntimeSystems aren't\n * copy- or move-able so we have to make the necessary info available. The\n * simplest way to do this was to have functions that return references to the\n * member variables.\n *\n * \\note Translation control isn't supported yet. It will be added in the\n * future.\n */\ntemplate <typename Metavars>\nstruct SystemHelper {\n private:\n  template <typename System>\n  struct LocalTag {\n    using type = tuples::tagged_tuple_from_typelist<init_simple_tags<System>>;\n  };\n  using AllTags = tuples::tagged_tuple_from_typelist<tmpl::transform<\n      typename Metavars::control_systems, tmpl::bind<LocalTag, tmpl::_1>>>;\n\n public:\n  using control_systems = typename Metavars::control_systems;\n  using element_component = typename Metavars::element_component;\n  using control_components = typename Metavars::control_components;\n\n  // Members that may be moved out of this struct once they are\n  // constructed\n  auto& domain() { return domain_; }\n  auto& is_active_map() { return is_active_map_; }\n  auto& initial_functions_of_time() { return initial_functions_of_time_; }\n  auto& initial_measurement_timescales() {\n    return initial_measurement_timescales_;\n  }\n\n  // Members that won't be moved out of this struct\n  template <typename System>\n  const auto& init_tuple() {\n    return get<LocalTag<System>>(all_init_tags_);\n  }\n  const auto& horizon_a() { return horizon_a_; }\n  const auto& horizon_b() { return horizon_b_; }\n  template <typename System>\n  std::string name() {\n    return System::name();\n  }\n  const auto& system_to_combined_names() { return system_to_combined_names_; }\n\n  void reset() {\n    domain_ = Domain<3>{std::vector<Block<3>>{}, stored_excision_spheres_};\n    is_active_map_ = create_is_active_map();\n    initial_functions_of_time_ =\n        clone_unique_ptrs(stored_initial_functions_of_time_);\n    initial_measurement_timescales_ =\n        clone_unique_ptrs(stored_initial_measurement_timescales_);\n    all_init_tags_ = stored_all_init_tags_;\n    horizon_a_ = {};\n    horizon_b_ = {};\n  }\n\n  /*!\n   * \\brief Setup the test.\n   *\n   * The function `initialize_functions_of_time` must take a\n   * `const gsl::not_null<std::unordered_map<std::string,\n   * std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>` for the\n   * function of time map, a `const double` for the initial time, and a `const\n   * std::unordered_map<std::string, double>&` for the expiration times. This\n   * function will initialize the functions of time and must return a double\n   * that represents the radius of the excision spheres. Some existing functions\n   * are given in the `control_system::TestHelpers` namespace for Expansion,\n   * Rotation, and Translation.\n   */\n  template <typename F>\n  void setup_control_system_test(const double initial_time,\n                                 const double initial_separation,\n                                 const std::string& option_string,\n                                 const F initialize_functions_of_time) {\n    initial_time_ = initial_time;\n\n    parse_options(option_string);\n    // Initial parameters needed. Expiration times would normally be set during\n    // option parsing, and measurement timescales during initialization so we\n    // have to do them manually here instead.\n    std::unordered_map<std::string, std::pair<double, double>>\n        individual_minimums{};\n    std::unordered_map<std::string, double> initial_expiration_times{};\n    tmpl::for_each<control_systems>([this,\n                                     &individual_minimums](auto system_v) {\n      using system = tmpl::type_from<decltype(system_v)>;\n\n      auto& init_tuple = get<LocalTag<system>>(all_init_tags_);\n      auto& averager =\n          get<::control_system::Tags::Averager<system>>(init_tuple);\n      const auto& controller =\n          get<::control_system::Tags::Controller<system>>(init_tuple);\n      const auto& tuner =\n          get<::control_system::Tags::TimescaleTuner<system>>(init_tuple);\n      const DataVector measurement_timescale =\n          ::control_system::calculate_measurement_timescales(\n              controller, tuner, measurements_per_update_);\n      const double min_measurement_timescale = min(measurement_timescale);\n      averager.assign_time_between_measurements(min_measurement_timescale);\n\n      const double measurement_expr_time =\n          ::control_system::measurement_initial_expiration_time(\n              initial_time_, DataVector{min_measurement_timescale},\n              measurements_per_update_, delay_update_);\n\n      individual_minimums[name<system>()] =\n          std::make_pair(min_measurement_timescale, measurement_expr_time);\n    });\n\n    double overall_min_measurement_timescale =\n        std::numeric_limits<double>::infinity();\n    double overall_min_expiration_time =\n        std::numeric_limits<double>::infinity();\n    for (const auto& [system_name, min_measure_expr_time] :\n         individual_minimums) {\n      (void)system_name;\n      overall_min_measurement_timescale = std::min(\n          overall_min_measurement_timescale, min_measure_expr_time.first);\n      overall_min_expiration_time =\n          std::min(overall_min_expiration_time, min_measure_expr_time.second);\n    }\n\n    initial_measurement_timescales_[combined_name_] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n            initial_time_,\n            std::array{DataVector{overall_min_measurement_timescale}},\n            overall_min_expiration_time);\n\n    for (const auto& [system_name, min_measure_expr_time] :\n         individual_minimums) {\n      (void)min_measure_expr_time;\n      initial_expiration_times[system_name] =\n          ::control_system::function_of_time_initial_expiration_time(\n              initial_time_, DataVector{overall_min_measurement_timescale},\n              measurements_per_update_, delay_update_);\n    }\n\n    const double excision_radius =\n        initialize_functions_of_time(make_not_null(&initial_functions_of_time_),\n                                     initial_time_, initial_expiration_times);\n\n    // We don't need a real domain, just one that has the correct excision\n    // sphere centers because the control errors use the `excision_spheres()`\n    // member of a domain to get the centers. The names are chosen to match the\n    // BinaryCompactObject domain, which the control errors were based on and\n    // have these specific names hard-coded into them.\n    stored_excision_spheres_ =\n        std::unordered_map<std::string, ExcisionSphere<3>>{\n            {\"ExcisionSphereA\",\n             ExcisionSphere<3>{excision_radius,\n                               tnsr::I<double, 3, Frame::Grid>{\n                                   {+0.5 * initial_separation, 0.0, 0.0}},\n                               {{0, Direction<3>::lower_zeta()},\n                                {1, Direction<3>::lower_zeta()},\n                                {2, Direction<3>::lower_zeta()},\n                                {3, Direction<3>::lower_zeta()},\n                                {4, Direction<3>::lower_zeta()},\n                                {5, Direction<3>::lower_zeta()}}}},\n            {\"ExcisionSphereB\",\n             ExcisionSphere<3>{excision_radius,\n                               tnsr::I<double, 3, Frame::Grid>{\n                                   {-0.5 * initial_separation, 0.0, 0.0}},\n                               {{0, Direction<3>::lower_zeta()},\n                                {1, Direction<3>::lower_zeta()},\n                                {2, Direction<3>::lower_zeta()},\n                                {3, Direction<3>::lower_zeta()},\n                                {4, Direction<3>::lower_zeta()},\n                                {5, Direction<3>::lower_zeta()}}}}};\n    domain_ = Domain<3>{std::vector<Block<3>>{}, stored_excision_spheres_};\n\n    stored_initial_functions_of_time_ =\n        clone_unique_ptrs(initial_functions_of_time_);\n    stored_initial_measurement_timescales_ =\n        clone_unique_ptrs(initial_measurement_timescales_);\n    stored_all_init_tags_ = all_init_tags_;\n  }\n\n  /*!\n   * \\brief Actually run the control system test\n   *\n   * The `horizon_function` should return a\n   * `std::pair<ylm::Strahlkorper<Frame::Distorted>,\n   * ylm::Strahlkorper<Frame::Distorted>>` representing the two horizons in the\n   * grid frame. This means the user is responsible for doing any coordinate\n   * transformations inside `horizon_function` as this function won't do any.\n   * The `number_of_horizons` is used to determine if we actually use both\n   * horizon \"measurements\" as some control systems may only need one. If only\n   * one horizon is used, the default is to use AhA.\n   *\n   * For the basic control systems, a common function is defined for you:\n   * `control_system::TestHelpers::build_horizons_for_basic_control_systems()`.\n   */\n  template <typename Generator, typename F>\n  void run_control_system_test(\n      ActionTesting::MockRuntimeSystem<Metavars>& runner,\n      const double final_time, gsl::not_null<Generator*> generator,\n      const F horizon_function) {\n    auto& cache = ActionTesting::cache<element_component>(runner, 0);\n    const auto& measurement_timescales =\n        Parallel::get<::control_system::Tags::MeasurementTimescales>(cache);\n    const auto& functions_of_time =\n        Parallel::get<::domain::Tags::FunctionsOfTime>(cache);\n\n    double time = initial_time_;\n    std::optional<double> prev_time{};\n    int current_measurement = 0;\n    const auto calculate_dt =\n        [this, &measurement_timescales](const double local_time) -> double {\n      return measurement_timescales.at(combined_name_)->func(local_time)[0][0];\n    };\n    double dt = calculate_dt(time);\n\n    // Start loop\n    while (time < final_time) {\n      ++current_measurement;\n      // Setup the measurement Id. This would normally be created in the control\n      // system event.\n      const LinkedMessageId<double> measurement_id{time, prev_time};\n\n      // Get horizons in grid frame\n      std::tie(horizon_a_, horizon_b_) = horizon_function(time);\n\n      // Apply measurements\n      tmpl::for_each<control_components>([this, &runner, &generator,\n                                          &measurement_id, &current_measurement,\n                                          &cache](auto control_component) {\n        using component = tmpl::type_from<decltype(control_component)>;\n        using first_component = tmpl::front<control_components>;\n        using system = typename component::system;\n        INFO(\"Applying measurements for \" + system::name());\n        constexpr bool is_shape =\n            std::is_same_v<system, typename Metavars::shape_system>;\n        // Depending on the measurement, apply the submeasurements\n        if constexpr (is_shape) {\n          system::process_measurement::apply(\n              ::control_system::measurements::SingleHorizon<\n                  ::domain::ObjectLabel::A>::Submeasurement{},\n              horizon_a_, cache, measurement_id);\n        } else {\n          system::process_measurement::apply(\n              ::control_system::measurements::BothHorizons::FindHorizon<\n                  ::domain::ObjectLabel::A>{},\n              horizon_a_, cache, measurement_id);\n          system::process_measurement::apply(\n              ::control_system::measurements::BothHorizons::FindHorizon<\n                  ::domain::ObjectLabel::B>{},\n              horizon_b_, cache, measurement_id);\n        }\n        // First, there are the UpdateMessageQueue actions on the actual\n        // component.\n        CHECK(ActionTesting::number_of_queued_simple_actions<component>(\n                  runner, 0) == (is_shape ? 1 : 2));\n\n        if constexpr (not is_shape) {\n          // We invoke a random measurement because during a normal simulation\n          // we don't know which measurement will reach the control system\n          // first because of charm++ communication\n          ActionTesting::invoke_random_queued_simple_action<control_components>(\n              make_not_null(&runner), generator,\n              ActionTesting::array_indices_with_queued_simple_actions<\n                  control_components>(make_not_null(&runner)));\n        }\n        ActionTesting::invoke_queued_simple_action<component>(\n            make_not_null(&runner), 0);\n\n        // If this was an update, there should be a single simple action on the\n        // first component. Otherwise, no action\n        CHECK(ActionTesting::number_of_queued_simple_actions<first_component>(\n                  runner, 0) ==\n              (current_measurement == measurements_per_update_ ? 1 : 0));\n\n        if (current_measurement == measurements_per_update_) {\n          ActionTesting::invoke_queued_simple_action<first_component>(\n              make_not_null(&runner), 0);\n        }\n      });\n\n      // At this point, the control systems for each transformation should have\n      // done their thing and updated the functions of time (if they had enough\n      // data).\n\n      // Our dt is set by the smallest measurement timescale. The control system\n      // updates these timescales when it updates the functions of time\n      prev_time = time;\n      dt = calculate_dt(time);\n      if (current_measurement == measurements_per_update_) {\n        current_measurement = 0;\n      }\n      double min_expr_time = std::numeric_limits<double>::infinity();\n      for (const auto& [name, fot] : functions_of_time) {\n        (void)name;\n        min_expr_time = std::min(min_expr_time, min(fot->time_bounds()[1]));\n      }\n      if (equal_within_roundoff(min_expr_time, time + dt)) {\n        time = min_expr_time;\n      } else {\n        time += dt;\n      }\n    }\n\n    // Get horizons at final time in grid frame\n    std::tie(horizon_a_, horizon_b_) = horizon_function(final_time);\n  }\n\n private:\n  template <typename Component>\n  using option_tag = ::control_system::OptionTags::ControlSystemInputs<\n      typename Component::system>;\n  using option_list = tmpl::push_back<\n      tmpl::remove_duplicates<tmpl::transform<\n          control_components, tmpl::bind<option_tag, tmpl::_1>>>,\n      ::control_system::OptionTags::WriteDataToDisk, ::OptionTags::InitialTime,\n      domain::OptionTags::DomainCreator<3>,\n      ::control_system::OptionTags::MeasurementsPerUpdate,\n      ::control_system::OptionTags::DelayUpdate>;\n  template <typename System>\n  using creatable_tags = tmpl::list_difference<\n      init_simple_tags<System>,\n      tmpl::list<typename System::MeasurementQueue,\n                 ::control_system::Tags::CurrentNumberOfMeasurements,\n                 ::control_system::Tags::UpdateAggregators,\n                 ::control_system::Tags::MeasurementsPerUpdate>>;\n\n  void parse_options(const std::string& option_string) {\n    Options::Parser<option_list> parser{\"Peter Parker the option parser.\"};\n    parser.parse(option_string);\n    const tuples::tagged_tuple_from_typelist<option_list> options =\n        parser.template apply<option_list, Metavars>([](auto... args) {\n          return tuples::tagged_tuple_from_typelist<option_list>(\n              std::move(args)...);\n        });\n\n    const auto created_measurements_per_update =\n        Parallel::create_from_options<Metavars>(\n            options,\n            tmpl::list<::control_system::Tags::MeasurementsPerUpdate>{});\n\n    measurements_per_update_ =\n        get<::control_system::Tags::MeasurementsPerUpdate>(\n            created_measurements_per_update);\n\n    const auto created_delay_update = Parallel::create_from_options<Metavars>(\n        options, tmpl::list<::control_system::Tags::DelayUpdate>{});\n\n    delay_update_ =\n        get<::control_system::Tags::DelayUpdate>(created_delay_update);\n\n    std::unordered_map<std::string, ::control_system::UpdateAggregator>\n        update_aggregators{};\n    std::unordered_set<std::string> control_system_names{};\n\n    tmpl::for_each<control_systems>(\n        [&control_system_names](auto control_system_v) {\n          using system = tmpl::type_from<decltype(control_system_v)>;\n          control_system_names.insert(system::name());\n        });\n\n    update_aggregators[combined_name_] = ::control_system::UpdateAggregator{\n        combined_name_, std::move(control_system_names)};\n\n    tmpl::for_each<control_systems>([this, &options,\n                                     &update_aggregators](auto system_v) {\n      using system = tmpl::type_from<decltype(system_v)>;\n\n      tuples::tagged_tuple_from_typelist<creatable_tags<system>> created_tags =\n          Parallel::create_from_options<Metavars>(options,\n                                                  creatable_tags<system>{});\n\n      get<LocalTag<system>>(all_init_tags_) =\n          tuples::tagged_tuple_from_typelist<init_simple_tags<system>>{\n              get<::control_system::Tags::Averager<system>>(created_tags),\n              get<::control_system::Tags::TimescaleTuner<system>>(created_tags),\n              get<::control_system::Tags::Controller<system>>(created_tags),\n              get<::control_system::Tags::ControlError<system>>(created_tags),\n              0, update_aggregators,\n              // Just need an empty queue. It will get filled in as the control\n              // system is updated\n              LinkedMessageQueue<\n                  double,\n                  tmpl::conditional_t<\n                      std::is_same_v<system, typename Metavars::shape_system>,\n                      tmpl::list<::control_system::QueueTags::Horizon<\n                          ::Frame::Distorted, ::domain::ObjectLabel::A>>,\n                      tmpl::list<\n                          ::control_system::QueueTags::Center<\n                              ::domain::ObjectLabel::A, Frame::Grid>,\n                          ::control_system::QueueTags::Center<\n                              ::domain::ObjectLabel::B, Frame::Grid>>>>{}};\n    });\n  }\n\n  static std::unordered_map<std::string, bool> create_is_active_map() {\n    std::unordered_map<std::string, bool> result{};\n    tmpl::for_each<control_systems>([&result](auto control_system_v) {\n      using system = tmpl::type_from<decltype(control_system_v)>;\n      // For this test, control systems are always active\n      result[system::name()] = true;\n    });\n    return result;\n  }\n\n  // Members that may be moved out of this struct once they are\n  // constructed\n  Domain<3> domain_;\n  std::unordered_map<std::string, bool> is_active_map_{create_is_active_map()};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      initial_functions_of_time_{};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      initial_measurement_timescales_{};\n\n  // Members that won't be moved out of this struct\n  AllTags all_init_tags_{};\n  ylm::Strahlkorper<Frame::Distorted> horizon_a_{};\n  ylm::Strahlkorper<Frame::Distorted> horizon_b_{};\n  int measurements_per_update_{};\n  bool delay_update_{};\n  double initial_time_{std::numeric_limits<double>::signaling_NaN()};\n  std::unordered_map<std::string, std::string> system_to_combined_names_{\n      ::control_system::system_to_combined_names<control_systems>()};\n  // Only one because all systems use the same measurement\n  std::string combined_name_{\n      ::control_system::combined_name<control_systems>()};\n\n  // Initialization members. These are the values that will be used when the\n  // reset() function is called. We don't need the horizons because those are\n  // only used when the test is run, and we don't need initial time because\n  // that's never changed.\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      stored_initial_functions_of_time_{};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      stored_initial_measurement_timescales_{};\n  AllTags stored_all_init_tags_{};\n  // Can't copy Domain or Block so just create a duplicate using the excision\n  // spheres which are the only important part\n  std::unordered_map<std::string, ExcisionSphere<3>> stored_excision_spheres_{};\n};\n}  // namespace TestHelpers::control_system\n"
  },
  {
    "path": "tests/Unit/Helpers/ControlSystem/TestStructs.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"ControlSystem/Protocols/ControlError.hpp\"\n#include \"ControlSystem/Protocols/ControlSystem.hpp\"\n#include \"ControlSystem/Protocols/Measurement.hpp\"\n#include \"ControlSystem/TimescaleTuner.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Helpers/ControlSystem/Examples.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\nstruct PreviousTriggerTime;\n}  // namespace Tags\n\nnamespace control_system::TestHelpers {\nnamespace TestStructs_detail {\nstruct LabelA {};\n}  // namespace TestStructs_detail\n\ntemplate <typename Label, typename ControlSystems, bool CallRunCallbacks>\nclass TestEvent;\n\ntemplate <typename Label, bool CallRunCallbacks>\nstruct Submeasurement\n    : tt::ConformsTo<control_system::protocols::Submeasurement> {\n  template <typename ControlSystems>\n  using interpolation_target_tag = void;\n  template <typename ControlSystems>\n  using horizon_metavars = void;\n  template <typename ControlSystems>\n  using event = TestEvent<Label, ControlSystems, CallRunCallbacks>;\n};\n\ntemplate <typename Label, typename ControlSystems, bool CallRunCallbacks>\nclass TestEvent : public ::Event {\n public:\n  /// \\cond\n  // LCOV_EXCL_START\n  explicit TestEvent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TestEvent);  // NOLINT\n  // LCOV_EXCL_STOP\n  /// \\endcond\n\n  static constexpr bool factory_creatable = false;\n  TestEvent() = default;\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::conditional_t<\n      CallRunCallbacks,\n      tmpl::list<::Tags::Time, ::Tags::PreviousTriggerTime,\n                 control_system::TestHelpers::SomeTagOnElement>,\n      tmpl::list<>>;\n\n  template <typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(const double time, const std::optional<double> previous_time,\n                  const double data_from_element,\n                  Parallel::GlobalCache<Metavariables>& cache,\n                  const ArrayIndex& /*array_index*/,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    const LinkedMessageId<double> measurement_id{time, previous_time};\n    // Just a hack so we don't have to add a whole other component\n    const auto box = db::create<\n        db::AddSimpleTags<control_system::TestHelpers::MeasurementResultTag,\n                          ah::Tags::CurrentTime>>(\n        data_from_element, std::optional{measurement_id});\n    control_system::RunCallbacks<Submeasurement<Label, CallRunCallbacks>,\n                                 ControlSystems>::apply(box, cache,\n                                                        measurement_id);\n    ++call_count;\n    control_system::RunCallbacks<\n        Submeasurement<Label, CallRunCallbacks>,\n        ControlSystems>::apply(box, cache, FastFlow::Status::AbsTol);\n    ++call_count;\n  }\n\n  template <typename Metavariables, typename ArrayIndex,\n            typename ParallelComponent>\n  void operator()(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ArrayIndex& /*array_index*/,\n                  const ParallelComponent* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {\n    ++call_count;\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*component*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return true; }\n\n  static size_t call_count;  // NOLINT\n};\n\n// NOLINTBEGIN\ntemplate <typename Label, typename ControlSystems, bool CallRunCallbacks>\nsize_t TestEvent<Label, ControlSystems, CallRunCallbacks>::call_count = 0;\n\n/// \\cond\ntemplate <typename Label, typename ControlSystems, bool CallRunCallbacks>\nPUP::able::PUP_ID\n    TestEvent<Label, ControlSystems, CallRunCallbacks>::my_PUP_ID = 0;\n/// \\endcond\n// NOLINTEND\n\ntemplate <typename Label, bool CallRunCallbacks = false>\nstruct Measurement : tt::ConformsTo<control_system::protocols::Measurement> {\n  using submeasurements = tmpl::list<Submeasurement<Label, CallRunCallbacks>>;\n};\n\nstruct ControlError : tt::ConformsTo<control_system::protocols::ControlError> {\n  using object_centers = domain::object_list<>;\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  std::optional<double> get_suggested_timescale() const { return std::nullopt; }\n\n  void reset() {}\n\n  void pup(PUP::er& /*p*/) {}\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\"Example control error.\"};\n\n  template <typename Metavariables, typename... QueueTags>\n  DataVector operator()(\n      const ::TimescaleTuner<true>& /*tuner*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const double /*time*/, const std::string& /*function_of_time_name*/,\n      const tuples::TaggedTuple<QueueTags...>& /*measurements*/) {\n    return DataVector{};\n  }\n};\n\nstatic_assert(tt::assert_conforms_to_v<Measurement<TestStructs_detail::LabelA>,\n                                       control_system::protocols::Measurement>);\n\ntemplate <size_t DerivOrder, typename Label, typename Measurement>\nstruct System : tt::ConformsTo<control_system::protocols::ControlSystem> {\n  static std::string name() { return pretty_type::short_name<Label>(); }\n  static std::optional<std::string> component_name(\n      const size_t i, const size_t /*num_components*/) {\n    return get_output(i);\n  }\n  using measurement = Measurement;\n  using simple_tags = tmpl::list<>;\n  using control_error = ControlError;\n  static constexpr size_t deriv_order = DerivOrder;\n\n  struct process_measurement {\n    template <typename Submeasurement>\n    using argument_tags = tmpl::list<>;\n  };\n};\n\nstatic_assert(\n    tt::assert_conforms_to_v<System<2, TestStructs_detail::LabelA,\n                                    Measurement<TestStructs_detail::LabelA>>,\n                             control_system::protocols::ControlSystem>);\n}  // namespace control_system::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/ApplyMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n\nDataVector apply_matrix(const Matrix& m, const DataVector& v) {\n  ASSERT(m.columns() == v.size(), \"Bad apply_matrix\");\n  DataVector result(m.rows(), 0.);\n  for (size_t i = 0; i < m.rows(); ++i) {\n    for (size_t j = 0; j < m.columns(); ++j) {\n      result[i] += m(i, j) * v[j];\n    }\n  }\n  return result;\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/ApplyMatrix.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\cond\nclass DataVector;\nclass Matrix;\n/// \\endcond\n\n/// Multiply a matrix and a vector\nDataVector apply_matrix(const Matrix& m, const DataVector& v);\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"DataStructuresHelpers\")\n\nset(LIBRARY_SOURCES\n  ApplyMatrix.cpp\n  MathWrapperDetail.cpp\n  RandomUnitNormal.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Utilities\n  )\n\nadd_subdirectory(DataBox)\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/CustomStaticSizeVector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n\n#include \"DataStructures/VectorImpl.hpp\"\n\n// A VectorImpl whose static size limit is different than the default value\nclass CustomStaticSizeVector;\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(CustomStaticSizeVector);\n}  // namespace blaze\nclass CustomStaticSizeVector\n    : public VectorImpl<double, CustomStaticSizeVector, 3> {\n public:\n  CustomStaticSizeVector() = default;\n  CustomStaticSizeVector(const CustomStaticSizeVector&) = default;\n  CustomStaticSizeVector(CustomStaticSizeVector&&) = default;\n  CustomStaticSizeVector& operator=(const CustomStaticSizeVector&) = default;\n  CustomStaticSizeVector& operator=(CustomStaticSizeVector&&) = default;\n  ~CustomStaticSizeVector() = default;\n\n  using BaseType = VectorImpl<double, CustomStaticSizeVector, static_size>;\n\n  using BaseType::operator=;\n  using BaseType::VectorImpl;\n};\n\nnamespace blaze {\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(CustomStaticSizeVector);\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ALL_MAP_TRAITS(CustomStaticSizeVector);\n}  // namespace blaze\n\nSPECTRE_ALWAYS_INLINE auto fabs(const CustomStaticSizeVector& t) {\n  return abs(*t);\n}\n\nMAKE_STD_ARRAY_VECTOR_BINOPS(CustomStaticSizeVector)\n\nMAKE_WITH_VALUE_IMPL_DEFINITION_FOR(CustomStaticSizeVector)\n\nclass CustomComplexStaticSizeVector;\nnamespace blaze {\nDECLARE_GENERAL_VECTOR_BLAZE_TRAITS(CustomComplexStaticSizeVector);\n}  // namespace blaze\nclass CustomComplexStaticSizeVector\n    : public VectorImpl<std::complex<double>, CustomComplexStaticSizeVector,\n                        3> {\n public:\n  CustomComplexStaticSizeVector() = default;\n  CustomComplexStaticSizeVector(const CustomComplexStaticSizeVector&) = default;\n  CustomComplexStaticSizeVector(CustomComplexStaticSizeVector&&) = default;\n  CustomComplexStaticSizeVector& operator=(\n      const CustomComplexStaticSizeVector&) = default;\n  CustomComplexStaticSizeVector& operator=(CustomComplexStaticSizeVector&&) =\n      default;\n  ~CustomComplexStaticSizeVector() = default;\n\n  using BaseType = VectorImpl<std::complex<double>,\n                              CustomComplexStaticSizeVector, static_size>;\n\n  using BaseType::operator=;\n  using BaseType::VectorImpl;\n};\n\nnamespace blaze {\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ARITHMETIC_TRAITS(CustomComplexStaticSizeVector);\nVECTOR_BLAZE_TRAIT_SPECIALIZE_ALL_MAP_TRAITS(CustomComplexStaticSizeVector);\n}  // namespace blaze\n\nSPECTRE_ALWAYS_INLINE auto fabs(const CustomComplexStaticSizeVector& t) {\n  return abs(*t);\n}\n\nMAKE_STD_ARRAY_VECTOR_BINOPS(CustomComplexStaticSizeVector)\n\nMAKE_WITH_VALUE_IMPL_DEFINITION_FOR(CustomComplexStaticSizeVector)\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/DataBox/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"DataBoxTestHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  Examples.hpp\n  TestHelpers.hpp\n  TestTags.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  Framework\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/DataBox/Examples.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace db::TestHelpers {\n\nstruct OptionTag {};\n\nstruct ExampleSimpleTag0 : db::SimpleTag {\n  using type = double;\n  using option_tags = tmpl::list<OptionTag>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n\nstruct ExampleSimpleTag1 : db::SimpleTag {\n  using type = int;\n  using option_tags = tmpl::list<OptionTag>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option) { return option; }\n};\n\nstruct ExampleSimpleTag2 : db::SimpleTag {\n  using type = double;\n};\n\n// [mutator_protocol]\nstruct ExampleMutator : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<ExampleSimpleTag0, ExampleSimpleTag1>;\n  using argument_tags = tmpl::list<ExampleSimpleTag2>;\n\n  static void apply(const gsl::not_null<double*> item_0,\n                    const gsl::not_null<int*> item_1, const double item_2,\n                    const int additional_arg) {\n    *item_0 = 2.0 * item_2;\n    *item_1 = 2 * additional_arg;\n  }\n};\n// [mutator_protocol]\n}  // namespace db::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/DataBox/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/CreateHasStaticMemberVariable.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace TestHelpers {\n\nnamespace db {\n\nnamespace detail {\nCREATE_HAS_TYPE_ALIAS(argument_tags)\nCREATE_HAS_TYPE_ALIAS_V(argument_tags)\nCREATE_HAS_TYPE_ALIAS(base)\nCREATE_HAS_TYPE_ALIAS_V(base)\nCREATE_HAS_TYPE_ALIAS(return_type)\nCREATE_HAS_TYPE_ALIAS_V(return_type)\nCREATE_HAS_TYPE_ALIAS(tag)\nCREATE_HAS_TYPE_ALIAS_V(tag)\nCREATE_HAS_TYPE_ALIAS(type)\nCREATE_HAS_TYPE_ALIAS_V(type)\n\nCREATE_IS_CALLABLE(name)\nCREATE_IS_CALLABLE_V(name)\n\ntemplate <typename Tag>\nvoid check_tag_name(const std::string& expected_name) {\n  CHECK(::db::tag_name<Tag>() == expected_name);\n  if (is_name_callable_v<Tag> and not ::db::is_compute_tag_v<Tag>) {\n    INFO(\"Do not define name for Tag '\" << ::db::tag_name<Tag>() << \"',\");\n    INFO(\"as it will automatically be generated with that name.\");\n    CHECK(::db::tag_name<Tag>() != pretty_type::short_name<Tag>());\n  }\n}\n}  // namespace detail\n\ntemplate <typename Tag>\nvoid test_compute_tag(const std::string& expected_name) {\n  static_assert(::db::is_compute_tag_v<Tag>,\n                \"A compute tag must be derived from db::ComputeTag\");\n  static_assert(detail::has_return_type_v<Tag>);\n  static_assert(detail::has_argument_tags_v<Tag>);\n  static_assert(detail::has_base_v<Tag>);\n  static_assert(::db::is_simple_tag_v<typename Tag::base>,\n                \"The base type alias of a compute tag must be a simple tag.\");\n  static_assert(std::is_same_v<typename Tag::return_type, typename Tag::type>);\n  detail::check_tag_name<Tag>(expected_name);\n}\n\ntemplate <typename Tag>\nvoid test_prefix_tag(const std::string& expected_name) {\n  static_assert(std::is_base_of_v<::db::PrefixTag, Tag>,\n                \"A prefix tag must be derived from db::PrefixTag\");\n  static_assert(::db::is_simple_tag_v<Tag>,\n                \"A prefix tag should also be a simple tag\");\n  static_assert(detail::has_type_v<Tag>);\n  static_assert(detail::has_tag_v<Tag>);\n  detail::check_tag_name<Tag>(expected_name);\n}\n\ntemplate <typename Tag>\nvoid test_reference_tag(const std::string& expected_name) {\n  static_assert(::db::is_reference_tag_v<Tag>,\n                \"A reference tag must be derived from db::ReferenceTag\");\n  static_assert(detail::has_argument_tags_v<Tag>);\n  static_assert(detail::has_base_v<Tag>);\n  static_assert(::db::is_simple_tag_v<typename Tag::base>,\n                \"The base type alias of a reference tag must be a simple tag.\");\n  detail::check_tag_name<Tag>(expected_name);\n}\n\ntemplate <typename Tag>\nvoid test_simple_tag(const std::string& expected_name) {\n  static_assert(::db::is_simple_tag_v<Tag>,\n                \"A simple tag must be derived from db::SimpleTag, but not \"\n                \"db::ComputeTag\");\n  static_assert(detail::has_type_v<Tag>);\n  if constexpr (detail::has_base_v<Tag>) {\n    static_assert(::db::is_simple_tag_v<typename Tag::base>,\n                  \"The base type alias of a simple tag must be a simple tag.\");\n  }\n  static_assert(not std::is_same_v<Tag, typename Tag::type>,\n                \"A type cannot be its own tag.\");\n  detail::check_tag_name<Tag>(expected_name);\n}\n\n}  // namespace db\n\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/DataBox/TestTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers {\n\nstruct SomeType;\n\nvoid do_something(gsl::not_null<SomeType*> /* return_value */);\n\nnamespace db {\nnamespace Tags {\n\nstruct Bad {};\n\nstruct Simple : ::db::SimpleTag {\n  using type = SomeType;\n};\n\nstruct SimpleCompute : Simple, ::db::ComputeTag {\n  using base = Simple;\n  using return_type = SomeType;\n  using argument_tags = tmpl::list<>;\n  static constexpr auto function = do_something;\n};\n\nstruct ParentTag : ::db::SimpleTag {\n  using type = SomeType;\n};\n\nstruct SimpleReference : Simple, ::db::ReferenceTag {\n  using base = Simple;\n  using argument_tags = tmpl::list<ParentTag>;\n  static const auto& get(const typename ParentTag::type& /* parent_value */);\n};\n\ntemplate <typename Tag>\nstruct Label : ::db::PrefixTag, ::db::SimpleTag {\n  using tag = Tag;\n  using type = SomeType;\n};\n\n\n}  // namespace Tags\n}  // namespace db\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/MakeRandomVectorInMagnitudeRange.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Construct a spatial vector in a given magnitude range\n///\n/// The magnitude is computed with respect to the given metric, where the metric\n/// is assumed to have positive signature.\ntemplate <typename DataType, size_t Dim, UpLo Ul, typename Fr = Frame::Inertial,\n          Requires<(Ul == UpLo::Up)> = nullptr>\ntnsr::I<DataType, Dim, Fr> make_random_vector_in_magnitude_range(\n    const gsl::not_null<std::mt19937*> nn_generator,\n    const tnsr::ii<DataType, Dim, Fr>& metric, const double min_magnitude,\n    const double max_magnitude) {\n  if (min_magnitude < 0) {\n    ERROR(\"min_magnitude < 0. Magnitude must be non-negative\");\n  }\n  if (max_magnitude < 0) {\n    ERROR(\"max_magnitude < 0. Magnitude must be non-negative\");\n  }\n  ASSERT(min_magnitude <= max_magnitude,\n         \"min = \" << min_magnitude << \", max = \" << max_magnitude);\n\n  // generate distribution\n  std::uniform_real_distribution<> dist_magnitude(min_magnitude, max_magnitude);\n  const auto magnitude = make_with_random_values<Scalar<DataType>>(\n      nn_generator, make_not_null(&dist_magnitude), metric);\n\n  // construct vector\n  tnsr::I<DataType, Dim, Fr> x_up = random_unit_normal(nn_generator, metric);\n\n  for (size_t i = 0; i < Dim; i++) {\n    x_up.get(i) *= magnitude.get();\n  }\n\n  return x_up;\n}\n\ntemplate <typename DataType, size_t Dim, UpLo Ul, typename Fr = Frame::Inertial,\n          Requires<(Ul == UpLo::Lo)> = nullptr>\ntnsr::i<DataType, Dim, Fr> make_random_vector_in_magnitude_range(\n    const gsl::not_null<std::mt19937*> nn_generator,\n    const tnsr::ii<DataType, Dim, Fr>& metric, const double min_magnitude,\n    const double max_magnitude) {\n  const tnsr::I<DataType, Dim, Fr> x_up =\n      make_random_vector_in_magnitude_range<DataType, Dim, UpLo::Up, Fr>(\n          nn_generator, metric, min_magnitude, max_magnitude);\n\n  return raise_or_lower_index(x_up, metric);\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Construct a spatial vector in a given magnitude range\n///\n/// The magnitude is computed with respect to the flat space Euclidian metric.\ntemplate <typename DataType, size_t Dim, UpLo Ul, typename Fr = Frame::Inertial,\n          typename T>\nTensor<DataType, Symmetry<1>, index_list<SpatialIndex<Dim, Ul, Fr>>>\nmake_random_vector_in_magnitude_range_flat(\n    const gsl::not_null<std::mt19937*> nn_generator, const T& used_for_size,\n    const double min_magnitude, const double max_magnitude) {\n  // construct flat spatial metric\n  tnsr::ii<DataType, Dim, Fr> metric =\n      make_with_value<tnsr::ii<DataType, Dim, Fr>>(used_for_size, 0.0);\n  for (size_t i = 0; i < Dim; i++) {\n    metric.get(i, i) = 1.0;\n  }\n\n  return make_random_vector_in_magnitude_range<DataType, Dim, Ul, Fr>(\n      nn_generator, metric, min_magnitude, max_magnitude);\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/MakeWithRandomValues.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Helper functions for data structures used in unit tests\n\n#pragma once\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n#include \"Utilities/TypeTraits/IsIterable.hpp\"\n#include \"Utilities/TypeTraits/IsMaplike.hpp\"\n\nnamespace TestHelpers_detail {\n/// \\cond HIDDEN_SYMBOLS\ntemplate <typename T, typename = std::nullptr_t>\nstruct FillWithRandomValuesImpl;\n\ntemplate <typename T>\nstruct FillWithRandomValuesImpl<T, Requires<std::is_fundamental_v<T>>> {\n  template <typename UniformRandomBitGenerator,\n            typename RandomNumberDistribution>\n  static void apply(\n      const gsl::not_null<T*> data,\n      const gsl::not_null<UniformRandomBitGenerator*> generator,\n      const gsl::not_null<RandomNumberDistribution*> distribution) {\n    static_assert(\n        std::is_same_v<T, typename RandomNumberDistribution::result_type>,\n        \"Mismatch between data type and random number type.\");\n    *data = (*distribution)(*generator);\n  }\n};\n\ntemplate <typename T>\nstruct FillWithRandomValuesImpl<std::complex<T>> {\n  template <typename UniformRandomBitGenerator,\n            typename RandomNumberDistribution>\n  static void apply(\n      const gsl::not_null<std::complex<T>*> data,\n      const gsl::not_null<UniformRandomBitGenerator*> generator,\n      const gsl::not_null<RandomNumberDistribution*> distribution) {\n    static_assert(\n        std::is_same_v<T, typename RandomNumberDistribution::result_type>,\n        \"Mismatch between data type and random number type.\");\n    data->real((*distribution)(*generator));\n    data->imag((*distribution)(*generator));\n  }\n};\n\ntemplate <typename T, int Spin>\nstruct FillWithRandomValuesImpl<SpinWeighted<T, Spin>> {\n  template <typename UniformRandomBitGenerator,\n            typename RandomNumberDistribution>\n  static void apply(\n      const gsl::not_null<SpinWeighted<T, Spin>*> data,\n      const gsl::not_null<UniformRandomBitGenerator*> generator,\n      const gsl::not_null<RandomNumberDistribution*> distribution) {\n    FillWithRandomValuesImpl<T>::apply(&(data->data()), generator,\n                                       distribution);\n  }\n};\n\ntemplate <typename T>\nstruct FillWithRandomValuesImpl<\n    T, Requires<not tt::is_maplike_v<T> and tt::is_iterable_v<T>>> {\n  template <typename UniformRandomBitGenerator,\n            typename RandomNumberDistribution>\n  static void apply(\n      const gsl::not_null<T*> data,\n      const gsl::not_null<UniformRandomBitGenerator*> generator,\n      const gsl::not_null<RandomNumberDistribution*> distribution) {\n    for (auto& d : *data) {\n      FillWithRandomValuesImpl<std::decay_t<decltype(d)>>::apply(&d, generator,\n                                                                 distribution);\n    }\n  }\n};\n\ntemplate <typename... Tags>\nstruct FillWithRandomValuesImpl<Variables<tmpl::list<Tags...>>,\n                                std::nullptr_t> {\n  template <typename UniformRandomBitGenerator,\n            typename RandomNumberDistribution>\n  static void apply(\n      const gsl::not_null<Variables<tmpl::list<Tags...>>*> data,\n      [[maybe_unused]] const gsl::not_null<UniformRandomBitGenerator*>\n          generator,\n      [[maybe_unused]] const gsl::not_null<RandomNumberDistribution*>\n          distribution) {\n    EXPAND_PACK_LEFT_TO_RIGHT(\n        FillWithRandomValuesImpl<\n            std::decay_t<decltype(get<Tags>(*data))>>::apply(&get<Tags>(*data),\n                                                             generator,\n                                                             distribution));\n  }\n};\n/// \\endcond\n}  // namespace TestHelpers_detail\n\n/// @{\n/// \\ingroup TestingFrameworkGroup\n///\n/// \\brief A uniform distribution function object which redirects appropriately\n/// to either the `std::uniform_int_distribution` or the\n/// `std::uniform_real_distribution`. This also provides a convenience\n/// constructor which takes a 2-element array for the bounds for either\n/// floating point or int distributions.\ntemplate <typename T>\nclass UniformCustomDistribution\n    : public tmpl::conditional_t<\n          std::is_integral_v<T>,\n          std::uniform_int_distribution<std::remove_const_t<T>>,\n          std::uniform_real_distribution<std::remove_const_t<T>>> {\n  using base = tmpl::conditional_t<\n      std::is_integral_v<T>,\n      std::uniform_int_distribution<std::remove_const_t<T>>,\n      std::uniform_real_distribution<std::remove_const_t<T>>>;\n  static_assert(std::is_integral_v<T> or std::is_floating_point_v<T>,\n                \"UniformCustomDistribution currently supports only floating\"\n                \"point, integral, or boolean values\");\n\n public:\n  using base::base;\n  template <typename Bound>\n  explicit UniformCustomDistribution(std::array<Bound, 2> arr)\n      : base(arr[0], arr[1]) {}\n  using base::operator=;\n  using base::operator();\n};\n\ntemplate <>\nclass UniformCustomDistribution<bool> {\n  using base = NoSuchType;\n public:\n  using result_type = bool;\n  /// \\cond\n  UniformCustomDistribution() = default;\n  UniformCustomDistribution(const UniformCustomDistribution&) = default;\n  UniformCustomDistribution(UniformCustomDistribution&&) = default;\n  UniformCustomDistribution& operator=(const UniformCustomDistribution&) =\n      default;\n  UniformCustomDistribution& operator=(UniformCustomDistribution&&) = default;\n  /// \\endcond\n  template <typename Generator>\n  bool operator()(Generator& gen) {\n    return static_cast<bool>(gen() % 2);\n  }\n};\n/// @}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Fill an existing data structure with random values\ntemplate <typename T, typename UniformRandomBitGenerator,\n          typename RandomNumberDistribution>\nvoid fill_with_random_values(\n    const gsl::not_null<T*> data,\n    const gsl::not_null<UniformRandomBitGenerator*> generator,\n    const gsl::not_null<RandomNumberDistribution*> distribution) {\n  TestHelpers_detail::FillWithRandomValuesImpl<T>::apply(data, generator,\n                                                         distribution);\n}\n\n/// @{\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Make a data structure and fill it with random values\n///\n/// \\details Given an object of type `T`, create an object of type `ReturnType`\n/// whose elements are initialized to random values using the given random\n/// number generator and random number distribution.\n///\n/// \\requires the type `ReturnType` to be creatable using\n/// `make_with_value<ReturnType>(T)`\ntemplate <typename ReturnType, typename T, typename UniformRandomBitGenerator,\n          typename RandomNumberDistribution>\nReturnType make_with_random_values(\n    const gsl::not_null<UniformRandomBitGenerator*> generator,\n    const gsl::not_null<RandomNumberDistribution*> distribution,\n    const T& used_for_size) {\n  auto result = make_with_value<ReturnType>(\n      used_for_size,\n      std::numeric_limits<\n          tt::get_fundamental_type_t<ReturnType>>::signaling_NaN());\n  fill_with_random_values(make_not_null(&result), generator, distribution);\n  return result;\n}\n// distributions are sufficiently small to justify providing a convenience\n// function that receives them by value, which is useful when obtaining pointers\n// is inconvenient (e.g. for distributions that are obtained as\n// rvalues). Generators should never be copied, as doing so will cause\n// duplication of the pseudorandom numbers and performance hits due to the\n// nontrivial size.\n// clang-tidy: seems to erroneously believe this is a function declaration\n// rather than a definition.\ntemplate <typename ReturnType, typename T, typename UniformRandomBitGenerator,\n          typename RandomNumberDistribution>\nReturnType make_with_random_values(\n    const gsl::not_null<UniformRandomBitGenerator*> generator,  // NOLINT\n    RandomNumberDistribution distribution, const T& used_for_size) {\n  return make_with_random_values<ReturnType>(\n      generator, make_not_null(&distribution), used_for_size);\n}\n/// @}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Make a fixed-size data structure and fill with random values\n///\n/// \\details Given a template argument type `T`, create an object of the same\n/// type, fills it with random values, and returns the result. Acts as a\n/// convenience function to avoid users needing to put in constructors with\n/// `signaling_NaN()`s or `max()`s themselves when making with random values.\n/// Used as\n/// `make_with_random_values<Type>(make_not_null(&gen),make_not_null(&dist))`\ntemplate <typename T, typename UniformRandomBitGenerator,\n          typename RandomNumberDistribution>\nT make_with_random_values(\n    const gsl::not_null<UniformRandomBitGenerator*> generator,\n    const gsl::not_null<RandomNumberDistribution*> distribution) {\n  T result{};\n  fill_with_random_values(make_not_null(&result), generator, distribution);\n  return result;\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/MathWrapper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/MathWrapper.hpp\"\n#include \"Helpers/DataStructures/MathWrapperDetail.hpp\"\n\nnamespace TestHelpers::MathWrapper {\n\ntemplate <typename T, typename Scalar>\nvoid test_type(const T& first_value, const T& second_value,\n               const Scalar& scalar) {\n  T destination = first_value;\n  const auto destination_wrapper =\n      make_math_wrapper(make_not_null(&destination));\n\n  T source = second_value;\n  const auto mutable_source_wrapper = make_math_wrapper(make_not_null(&source));\n  const auto const_source_wrapper = make_math_wrapper(std::as_const(source));\n  using MutableWrapper = std::decay_t<decltype(mutable_source_wrapper)>;\n  using ConstWrapper = std::decay_t<decltype(const_source_wrapper)>;\n\n  // Test value_type\n  static_assert(not std::is_const_v<typename MutableWrapper::value_type>);\n  static_assert(std::is_const_v<typename ConstWrapper::value_type>);\n  static_assert(\n      std::is_same_v<::MathWrapper<typename MutableWrapper::value_type>,\n                     MutableWrapper>);\n  static_assert(std::is_same_v<::MathWrapper<typename ConstWrapper::value_type>,\n                               ConstWrapper>);\n  static_assert(std::is_same_v<math_wrapper_type<T>,\n                               typename MutableWrapper::value_type>);\n  static_assert(std::is_same_v<math_wrapper_type<const T>,\n                               typename ConstWrapper::value_type>);\n\n  detail::do_assignment(destination_wrapper, mutable_source_wrapper.to_const());\n  CHECK(destination == second_value);\n  CHECK(source == second_value);\n  source = first_value;\n  detail::do_assignment(destination_wrapper, mutable_source_wrapper.to_const());\n  CHECK(destination == first_value);\n  CHECK(source == first_value);\n  source = second_value;\n  detail::do_assignment(destination_wrapper, const_source_wrapper);\n  CHECK(destination == second_value);\n  CHECK(source == second_value);\n  source = first_value;\n  detail::do_assignment(destination_wrapper, const_source_wrapper);\n  CHECK(destination == first_value);\n  CHECK(source == first_value);\n\n  static_assert(std::is_same_v<typename MutableWrapper::scalar_type, Scalar>);\n  static_assert(std::is_same_v<typename ConstWrapper::scalar_type, Scalar>);\n  static_assert(std::is_same_v<Scalar, double> or\n                std::is_same_v<Scalar, std::complex<double>>);\n  source = first_value;\n  detail::do_multiply(destination_wrapper, scalar,\n                      mutable_source_wrapper.to_const());\n  CHECK(destination == T(scalar * first_value));\n  source = second_value;\n  detail::do_multiply(destination_wrapper, scalar, const_source_wrapper);\n  CHECK(destination == T(scalar * second_value));\n}\n}  // namespace TestHelpers::MathWrapper\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/MathWrapperDetail.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/DataStructures/MathWrapperDetail.hpp\"\n\n#include <type_traits>\n\n#include \"Utilities/GenerateInstantiations.hpp\"\n\nnamespace TestHelpers::MathWrapper::detail {\ntemplate <typename T>\nvoid do_assignment(const ::MathWrapper<T>& dest,\n                   const ::MathWrapper<const T>& source) {\n  *dest = *source;\n}\n\ntemplate <typename T>\nvoid do_multiply(const ::MathWrapper<T>& dest,\n                 const typename ::MathWrapper<T>::scalar_type& scalar,\n                 const ::MathWrapper<const T>& source) {\n  *dest = scalar * *source;\n}\n\n#define MATH_WRAPPER_TYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template void do_assignment(                                            \\\n      const ::MathWrapper<MATH_WRAPPER_TYPE(data)>& dest,                 \\\n      const ::MathWrapper<const MATH_WRAPPER_TYPE(data)>& source);        \\\n  template void do_multiply(                                              \\\n      const ::MathWrapper<MATH_WRAPPER_TYPE(data)>& dest,                 \\\n      const typename ::MathWrapper<MATH_WRAPPER_TYPE(data)>::scalar_type& \\\n          scalar,                                                         \\\n      const ::MathWrapper<const MATH_WRAPPER_TYPE(data)>& source);\n\n// [MATH_WRAPPER_TYPES_instantiate]\nGENERATE_INSTANTIATIONS(INSTANTIATE, (MATH_WRAPPER_TYPES))\n// [MATH_WRAPPER_TYPES_instantiate]\n\n#undef INSTANTIATE\n#undef MATH_WRAPPER_TYPE\n}  // namespace TestHelpers::MathWrapper::detail\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/MathWrapperDetail.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n// This code is in a separate file from `MathWrapper.hpp` to avoid including\n// `Framework/TestingFramework.hpp` in the source file, which breaks some builds\n// on macOS with Catch2 \"undefined symbols\" errors.\n\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/MathWrapper.hpp\"\n\nnamespace TestHelpers::MathWrapper {\n// Defined and instantiated in a separate compilation unit to test the\n// explicit instantiation code.\nnamespace detail {\ntemplate <typename T>\nvoid do_assignment(const ::MathWrapper<T>& dest,\n                   const ::MathWrapper<const T>& source);\n\ntemplate <typename T>\nvoid do_multiply(const ::MathWrapper<T>& dest,\n                 const typename ::MathWrapper<T>::scalar_type& scalar,\n                 const ::MathWrapper<const T>& source);\n}  // namespace detail\n}  // namespace TestHelpers::MathWrapper\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/RandomUnitNormal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\ntemplate <typename DataType>\ntnsr::I<DataType, 1> random_unit_normal(\n    const gsl::not_null<std::mt19937*> generator,\n    const tnsr::ii<DataType, 1>& spatial_metric) {\n  std::uniform_int_distribution<> distribution(0, 1);\n  auto unit_normal = make_with_value<tnsr::I<DataType, 1>>(\n      spatial_metric, 1.0 - 2.0 * distribution(*generator));\n  get<0>(unit_normal) /= get(magnitude(unit_normal, spatial_metric));\n  return unit_normal;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 2> random_unit_normal(\n    const gsl::not_null<std::mt19937*> generator,\n    const tnsr::ii<DataType, 2>& spatial_metric) {\n  std::uniform_real_distribution<> distribution(-M_PI, M_PI);\n  // non-const to reuse allocation\n  auto phi = make_with_random_values<DataType>(\n      generator, make_not_null(&distribution), spatial_metric);\n  auto unit_normal = make_with_value<tnsr::I<DataType, 2>>(spatial_metric, 1.0);\n  get<0>(unit_normal) *= cos(phi);\n  get<1>(unit_normal) *= sin(phi);\n  DataType one_over_magnitude = std::move(phi);\n  one_over_magnitude = 1.0 / get(magnitude(unit_normal, spatial_metric));\n  get<0>(unit_normal) *= one_over_magnitude;\n  get<1>(unit_normal) *= one_over_magnitude;\n  return unit_normal;\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> random_unit_normal(\n    const gsl::not_null<std::mt19937*> generator,\n    const tnsr::ii<DataType, 3>& spatial_metric) {\n  std::uniform_real_distribution<> nz_distribution(-1.0, 1.0);\n  // non-const to reuse allocation\n  auto nz = make_with_random_values<DataType>(\n      generator, make_not_null(&nz_distribution), spatial_metric);\n  const DataType rho = sqrt(1.0 - square(nz));\n  std::uniform_real_distribution<> phi_distribution(-M_PI, M_PI);\n  const auto phi = make_with_random_values<DataType>(\n      generator, make_not_null(&phi_distribution), spatial_metric);\n  auto unit_normal = make_with_value<tnsr::I<DataType, 3>>(spatial_metric, 0.0);\n  get<0>(unit_normal) = rho * cos(phi);\n  get<1>(unit_normal) = rho * sin(phi);\n  get<2>(unit_normal) = nz;\n  DataType one_over_magnitude = std::move(nz);\n  one_over_magnitude = 1.0 / get(magnitude(unit_normal, spatial_metric));\n  get<0>(unit_normal) *= one_over_magnitude;\n  get<1>(unit_normal) *= one_over_magnitude;\n  get<2>(unit_normal) *= one_over_magnitude;\n  return unit_normal;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATION(_, data)                                 \\\n  template tnsr::I<DTYPE(data), DIM(data)> random_unit_normal( \\\n      const gsl::not_null<std::mt19937*> generator,            \\\n      const tnsr::ii<DTYPE(data), DIM(data)>& spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (double, DataVector), (1, 2, 3))\n\n#undef INSTANTIATION\n#undef DIM\n#undef DTYPE\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/RandomUnitNormal.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <random>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Make a random unit normal vector at each element of `DataType`.\ntemplate <typename DataType>\ntnsr::I<DataType, 1> random_unit_normal(\n    gsl::not_null<std::mt19937*> generator,\n    const tnsr::ii<DataType, 1>& spatial_metric);\n\ntemplate <typename DataType>\ntnsr::I<DataType, 2> random_unit_normal(\n    gsl::not_null<std::mt19937*> generator,\n    const tnsr::ii<DataType, 2>& spatial_metric);\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> random_unit_normal(\n    gsl::not_null<std::mt19937*> generator,\n    const tnsr::ii<DataType, 3>& spatial_metric);\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <complex>\n#include <limits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n\nnamespace TestHelpers::tenex {\n// Get a placeholder value for a Tensor component based on the data type of its\n// components\ntemplate <typename DataType>\nstruct component_placeholder_value;\n\ntemplate <>\nstruct component_placeholder_value<double> {\n  static constexpr double value = std::numeric_limits<double>::max();\n};\n\ntemplate <>\nstruct component_placeholder_value<std::complex<double>> {\n  static constexpr std::complex<double> value = std::complex<double>(\n      std::numeric_limits<double>::max(), std::numeric_limits<double>::max());\n};\n\ntemplate <>\nstruct component_placeholder_value<DataVector> {\n  static constexpr double value = component_placeholder_value<double>::value;\n};\n\ntemplate <>\nstruct component_placeholder_value<ComplexDataVector> {\n  static constexpr std::complex<double> value =\n      component_placeholder_value<std::complex<double>>::value;\n};\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/EvaluateRank0.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <limits>\n#include <random>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::tenex {\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that evaluating a right hand side tensor expression containing a\n/// single rank 0 tensor correctly assigns the data to the evaluated left hand\n/// side tensor\n///\n/// \\tparam DataType the type of data being stored in the Tensors\ntemplate <typename DataType>\nvoid test_evaluate_rank_0() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-5.0, 5.0);\n  const size_t used_for_size = 3;\n  const auto R = make_with_random_values<Tensor<DataType>>(\n      make_not_null(&generator), distribution, used_for_size);\n\n  Scalar<DataType> L(used_for_size);\n  // component placeholder is used to detect if LHS scalar was not modified\n  std::fill(L.begin(), L.end(), component_placeholder_value<DataType>::value);\n  call_evaluate<true>(make_not_null(&L), R());\n\n  CHECK(L == R);  // check LHS evaluated correctly\n\n  // Test with Variables\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<::Tags::TempTensor<0, Scalar<DataType>>,\n                         ::Tags::TempTensor<1, Scalar<DataType>>>>\n        vars(used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n    Scalar<DataType>& R_temp =\n        get<::Tags::TempTensor<0, Scalar<DataType>>>(vars);\n    get(R_temp) = get(R);\n\n    Scalar<DataType>& L_temp =\n        get<::Tags::TempTensor<1, Scalar<DataType>>>(vars);\n    call_evaluate<true>(make_not_null(&L_temp), R());\n\n    CHECK(R_temp == R);  // check RHS wasn't modified\n    CHECK(L_temp == R);  // check LHS evaluated correctly\n  }\n}\n\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/EvaluateRank1.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <cstddef>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TestHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::tenex {\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that evaluating a right hand side tensor expression containing a\n/// single rank 1 tensor correctly assigns the data to the evaluated left hand\n/// side tensor\n///\n/// \\details See `test_evaluate_rank_3_impl` for general details.\n///\n/// \\tparam ReturnLhsTensor whether to test tensor expression evaluation by\n/// returning the result tensor or not, which instead tests evaluation by\n/// assigning to the result tensor passed in as an argument\n/// \\tparam DataType the type of data being stored in the Tensors\n/// \\tparam RhsTensorIndexTypeList the RHS Tensor's typelist of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\n/// \\tparam TensorIndex the TensorIndex used in the the TensorExpression,\n/// e.g. `ti::a`\n/// \\tparam LhsTensorIndexTypeList the LHS Tensor's typelist of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\ntemplate <bool ReturnLhsTensor, typename DataType,\n          typename RhsTensorIndexTypeList, auto& TensorIndex,\n          typename LhsTensorIndexTypeList = RhsTensorIndexTypeList>\nvoid test_evaluate_rank_1_impl() {\n  using symmetry = Symmetry<1>;\n  using L_a_type = Tensor<DataType, symmetry, LhsTensorIndexTypeList>;\n  using R_a_type = Tensor<DataType, symmetry, RhsTensorIndexTypeList>;\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-5.0, 5.0);\n  const size_t used_for_size = 3;\n  const auto R_a = make_with_random_values<R_a_type>(\n      make_not_null(&generator), distribution, used_for_size);\n  auto expected_L_a =\n      ReturnLhsTensor\n          ? L_a_type{}\n          : make_with_value<L_a_type>(\n                used_for_size, component_placeholder_value<DataType>::value);\n\n  using lhs_tensorindextype = tmpl::at_c<LhsTensorIndexTypeList, 0>;\n  using rhs_tensorindextype = tmpl::at_c<RhsTensorIndexTypeList, 0>;\n\n  const std::pair<size_t, size_t> lhs_index_value_range =\n      get_index_value_range<lhs_tensorindextype, TensorIndex>();\n  const std::pair<size_t, size_t> rhs_index_value_range =\n      get_index_value_range<rhs_tensorindextype, TensorIndex>();\n\n  for (size_t lhs_a = lhs_index_value_range.first,\n              rhs_a = rhs_index_value_range.first;\n       lhs_a <= lhs_index_value_range.second; lhs_a++, rhs_a++) {\n    expected_L_a.get(lhs_a) = R_a.get(rhs_a);\n  }\n\n  // L_a = R_a\n  L_a_type L_a(used_for_size);\n  // component placeholder is used to detect which components have incorrectly\n  // or correctly (in the case of using spatial or time indices for spacetime\n  // indices) not been modified by evaluation of the RHS expression\n  std::fill(L_a.begin(), L_a.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndex>(make_not_null(&L_a),\n                                              R_a(TensorIndex));\n\n  CHECK(L_a == expected_L_a);  // check LHS evaluated correctly\n\n  // Test with Variables\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<::Tags::TempTensor<0, R_a_type>,\n                         ::Tags::TempTensor<1, L_a_type>>>\n        vars(used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n    R_a_type& R_a_temp = get<::Tags::TempTensor<0, R_a_type>>(vars);\n    R_a_temp = R_a;\n\n    // L_a = R_a\n    L_a_type& L_a_temp = get<::Tags::TempTensor<1, L_a_type>>(vars);\n    std::fill(L_a_temp.begin(), L_a_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndex>(make_not_null(&L_a_temp),\n                                                R_a(TensorIndex));\n\n    CHECK(R_a_temp == R_a);           // check RHS wasn't modified\n    CHECK(L_a_temp == expected_L_a);  // check LHS evaluated correctly\n  }\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Iterate testing of evaluating single rank 1 Tensors on multiple\n/// dimensions\n///\n/// \\details See `test_evaluate_rank_3_impl` for general details.\n///\n/// \\tparam ReturnLhsTensor whether to test tensor expression evaluation by\n/// returning the result tensor or not, which instead tests evaluation by\n/// assigning to the result tensor passed in as an argument\n/// \\tparam DataType the type of data being stored in the Tensors\n/// \\tparam RhsIndexTypeList the RHS Tensor's integral list of `IndexType`s\n/// \\tparam TensorIndex the TensorIndex used in the the TensorExpression,\n/// e.g. `ti::a`\n/// \\tparam Frame the frame of the tensor index\n/// \\tparam LhsIndexTypeList the LHS Tensor's integral list of `IndexType`s\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsIndexTypeList,\n          auto& TensorIndex, typename Frame,\n          typename LhsIndexTypeList = RhsIndexTypeList>\nvoid test_evaluate_rank_1() {\n  constexpr IndexType rhs_indextype = tmpl::at_c<RhsIndexTypeList, 0>::value;\n  constexpr IndexType lhs_indextype = tmpl::at_c<LhsIndexTypeList, 0>::value;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define CALL_TEST_EVALUATE_RANK_1_IMPL(_, data)                   \\\n  test_evaluate_rank_1_impl<                                      \\\n      ReturnLhsTensor, DataType,                                  \\\n      index_list<::Tensor_detail::TensorIndexType<                \\\n          DIM(data), TensorIndex.valence, Frame, rhs_indextype>>, \\\n      TensorIndex,                                                \\\n      index_list<::Tensor_detail::TensorIndexType<                \\\n          DIM(data), TensorIndex.valence, Frame, lhs_indextype>>>();\n\n  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_1_IMPL, (1, 2, 3))\n\n#undef CALL_TEST_EVALUATE_RANK_1_IMPL\n\n#undef DIM\n}\n\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/EvaluateRank2.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <iterator>\n#include <limits>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TestHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::tenex {\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that evaluating a right hand side tensor expression containing a\n/// single rank 2 tensor correctly assigns the data to the evaluated left hand\n/// side tensor\n///\n/// \\details See `test_evaluate_rank_3_impl` for general details.\n///\n/// \\tparam ReturnLhsTensor whether to test tensor expression evaluation by\n/// returning the result tensor or not, which instead tests evaluation by\n/// assigning to the result tensor passed in as an argument\n/// \\tparam DataType the type of data being stored in the Tensors\n/// \\tparam RhsSymmetry the ::Symmetry of the RHS Tensor\n/// \\tparam RhsTensorIndexTypeList the RHS Tensor's typelist of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\n/// \\tparam TensorIndexA the first TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::a`\n/// \\tparam TensorIndexB the second TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::B`\n/// \\tparam LhsSymmetry the ::Symmetry of the LHS Tensor\n/// \\tparam LhsTensorIndexTypeList the LHS Tensor's typelist of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsTensorIndexTypeList, auto& TensorIndexA,\n          auto& TensorIndexB, typename LhsSymmetry = RhsSymmetry,\n          typename LhsTensorIndexTypeList = RhsTensorIndexTypeList>\nvoid test_evaluate_rank_2_impl() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-5.0, 5.0);\n  const size_t used_for_size = 3;\n  using R_ab_type = Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList>;\n  const auto R_ab = make_with_random_values<R_ab_type>(\n      make_not_null(&generator), distribution, used_for_size);\n  auto expected_L_ab =\n      ReturnLhsTensor\n          ? Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>{}\n          : make_with_value<\n                Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>>(\n                used_for_size, component_placeholder_value<DataType>::value);\n\n  const std::int32_t lhs_symmetry_element_a = tmpl::at_c<LhsSymmetry, 0>::value;\n  const std::int32_t lhs_symmetry_element_b = tmpl::at_c<LhsSymmetry, 1>::value;\n  using lhs_tensorindextype_a = tmpl::at_c<LhsTensorIndexTypeList, 0>;\n  using lhs_tensorindextype_b = tmpl::at_c<LhsTensorIndexTypeList, 1>;\n  using rhs_tensorindextype_a = tmpl::at_c<RhsTensorIndexTypeList, 0>;\n  using rhs_tensorindextype_b = tmpl::at_c<RhsTensorIndexTypeList, 1>;\n\n  std::array<std::pair<size_t, size_t>, 2> lhs_index_value_ranges{};\n  lhs_index_value_ranges[0] =\n      get_index_value_range<lhs_tensorindextype_a, TensorIndexA>();\n  lhs_index_value_ranges[1] =\n      get_index_value_range<lhs_tensorindextype_b, TensorIndexB>();\n  std::array<std::pair<size_t, size_t>, 2> rhs_index_value_ranges{};\n  rhs_index_value_ranges[0] =\n      get_index_value_range<rhs_tensorindextype_a, TensorIndexA>();\n  rhs_index_value_ranges[1] =\n      get_index_value_range<rhs_tensorindextype_b, TensorIndexB>();\n\n  for (size_t lhs_a = lhs_index_value_ranges[0].first,\n              rhs_a = rhs_index_value_ranges[0].first;\n       lhs_a <= lhs_index_value_ranges[0].second; lhs_a++, rhs_a++) {\n    for (size_t lhs_b = lhs_index_value_ranges[1].first,\n                rhs_b = rhs_index_value_ranges[1].first;\n         lhs_b <= lhs_index_value_ranges[1].second; lhs_b++, rhs_b++) {\n      expected_L_ab.get(lhs_a, lhs_b) = R_ab.get(rhs_a, rhs_b);\n    }\n  }\n\n  const auto rhs_expression = R_ab(TensorIndexA, TensorIndexB);\n\n  // L_{ab} = R_{ab}\n  using L_ab_type = Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>;\n  L_ab_type L_ab(used_for_size);\n  // component placeholder is used to detect which components have incorrectly\n  // or correctly (in the case of using spatial or time indices for spacetime\n  // indices) not been modified by evaluation of the RHS expression\n  std::fill(L_ab.begin(), L_ab.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexB>(\n      make_not_null(&L_ab), rhs_expression);\n\n  // L_{ba} = R_{ab}\n  using L_ba_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_a>;\n  using L_ba_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_a>;\n  using L_ba_type = Tensor<DataType, L_ba_symmetry, L_ba_tensorindextype_list>;\n  L_ba_type L_ba(used_for_size);\n  std::fill(L_ba.begin(), L_ba.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexA>(\n      make_not_null(&L_ba), rhs_expression);\n\n  const size_t dim_a = tmpl::at_c<LhsTensorIndexTypeList, 0>::dim;\n  const size_t dim_b = tmpl::at_c<LhsTensorIndexTypeList, 1>::dim;\n\n  // check LHS evaluated correctly\n  for (size_t lhs_a = 0; lhs_a < dim_a; ++lhs_a) {\n    for (size_t lhs_b = 0; lhs_b < dim_b; ++lhs_b) {\n      const auto& expected_result = expected_L_ab.get(lhs_a, lhs_b);\n\n      CHECK(L_ab.get(lhs_a, lhs_b) == expected_result);\n      CHECK(L_ba.get(lhs_b, lhs_a) == expected_result);\n    }\n  }\n\n  // Test with Variables\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<::Tags::TempTensor<0, R_ab_type>,\n                         ::Tags::TempTensor<1, L_ab_type>,\n                         ::Tags::TempTensor<2, L_ba_type>>>\n        vars(used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n    R_ab_type& R_ab_temp = get<::Tags::TempTensor<0, R_ab_type>>(vars);\n    R_ab_temp = R_ab;\n\n    // L_{ab} = R_{ab}\n    L_ab_type& L_ab_temp = get<::Tags::TempTensor<1, L_ab_type>>(vars);\n    std::fill(L_ab_temp.begin(), L_ab_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexB>(\n        make_not_null(&L_ab_temp), rhs_expression);\n\n    // L_{ba} = R_{ab}\n    L_ba_type& L_ba_temp = get<::Tags::TempTensor<2, L_ba_type>>(vars);\n    std::fill(L_ba_temp.begin(), L_ba_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexA>(\n        make_not_null(&L_ba_temp), rhs_expression);\n\n    // check RHS wasn't modified\n    CHECK(R_ab_temp == R_ab);\n\n    // check LHS evaluated correctly\n    for (size_t lhs_a = 0; lhs_a < dim_a; ++lhs_a) {\n      for (size_t lhs_b = 0; lhs_b < dim_b; ++lhs_b) {\n        const auto& expected_result = expected_L_ab.get(lhs_a, lhs_b);\n\n        CHECK(L_ab_temp.get(lhs_a, lhs_b) == expected_result);\n        CHECK(L_ba_temp.get(lhs_b, lhs_a) == expected_result);\n      }\n    }\n  }\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Iterate testing of evaluating single rank 2 Tensors on multiple\n/// dimension combinations\n///\n/// We test nonsymmetric indices and symmetric indices across two functions to\n/// ensure that the code works correctly with symmetries. This function tests\n/// one of the following symmetries:\n/// - <2, 1>\n/// - <1, 1>\n///\n/// \\details See `test_evaluate_rank_3_impl` for general details.\n///\n/// \\tparam ReturnLhsTensor whether to test tensor expression evaluation by\n/// returning the result tensor or not, which instead tests evaluation by\n/// assigning to the result tensor passed in as an argument\n/// \\tparam DataType the type of data being stored in the Tensors\n/// \\tparam RhsSymmetry the ::Symmetry of the RHS Tensor\n/// \\tparam RhsIndexTypeList the RHS Tensor's integral list of `IndexType`s\n/// \\tparam TensorIndexA the first TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::a`\n/// \\tparam TensorIndexB the second TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::B`\n/// \\tparam Frame the frame of the tensor index\n/// \\tparam LhsSymmetry the ::Symmetry of the LHS Tensor\n/// \\tparam LhsIndexTypeList the LHS Tensor's integral list of `IndexType`s\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsIndexTypeList, auto& TensorIndexA, auto& TensorIndexB,\n          typename Frame, typename LhsSymmetry = RhsSymmetry,\n          typename LhsIndexTypeList = RhsIndexTypeList,\n          Requires<std::is_same_v<RhsSymmetry, Symmetry<2, 1>>> = nullptr>\nvoid test_evaluate_rank_2() {\n  constexpr IndexType rhs_indextype_a = tmpl::at_c<RhsIndexTypeList, 0>::value;\n  constexpr IndexType rhs_indextype_b = tmpl::at_c<RhsIndexTypeList, 1>::value;\n  constexpr IndexType lhs_indextype_a = tmpl::at_c<LhsIndexTypeList, 0>::value;\n  constexpr IndexType lhs_indextype_b = tmpl::at_c<LhsIndexTypeList, 1>::value;\n\n#define DIM_A(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM_B(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define CALL_TEST_EVALUATE_RANK_2_IMPL(_, data)                               \\\n  test_evaluate_rank_2_impl<                                                  \\\n      ReturnLhsTensor, DataType, RhsSymmetry,                                 \\\n      index_list<                                                             \\\n          ::Tensor_detail::TensorIndexType<DIM_A(data), TensorIndexA.valence, \\\n                                           Frame, rhs_indextype_a>,           \\\n          ::Tensor_detail::TensorIndexType<DIM_B(data), TensorIndexB.valence, \\\n                                           Frame, rhs_indextype_b>>,          \\\n      TensorIndexA, TensorIndexB, LhsSymmetry,                                \\\n      index_list<                                                             \\\n          ::Tensor_detail::TensorIndexType<DIM_A(data), TensorIndexA.valence, \\\n                                           Frame, lhs_indextype_a>,           \\\n          ::Tensor_detail::TensorIndexType<DIM_B(data), TensorIndexB.valence, \\\n                                           Frame, lhs_indextype_b>>>();\n\n  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_2_IMPL, (1, 2, 3), (1, 2, 3))\n\n#undef CALL_TEST_EVALUATE_RANK_2_IMPL\n\n#undef DIM_B\n#undef DIM_A\n}\n\n/// \\ingroup TestingFrameworkGroup\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsIndexTypeList, auto& TensorIndexA, auto& TensorIndexB,\n          typename Frame, typename LhsSymmetry = RhsSymmetry,\n          typename LhsIndexTypeList = RhsIndexTypeList,\n          Requires<std::is_same_v<RhsSymmetry, Symmetry<1, 1>>> = nullptr>\nvoid test_evaluate_rank_2() {\n  constexpr IndexType rhs_indextype_a = tmpl::at_c<RhsIndexTypeList, 0>::value;\n  constexpr IndexType rhs_indextype_b = tmpl::at_c<RhsIndexTypeList, 1>::value;\n  constexpr IndexType lhs_indextype_a = tmpl::at_c<LhsIndexTypeList, 0>::value;\n  constexpr IndexType lhs_indextype_b = tmpl::at_c<LhsIndexTypeList, 1>::value;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define CALL_TEST_EVALUATE_RANK_2_IMPL(_, data)                             \\\n  test_evaluate_rank_2_impl<                                                \\\n      ReturnLhsTensor, DataType, RhsSymmetry,                               \\\n      index_list<                                                           \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexA.valence, \\\n                                           Frame, rhs_indextype_a>,         \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexB.valence, \\\n                                           Frame, rhs_indextype_b>>,        \\\n      TensorIndexA, TensorIndexB, LhsSymmetry,                              \\\n      index_list<                                                           \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexA.valence, \\\n                                           Frame, lhs_indextype_a>,         \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexB.valence, \\\n                                           Frame, lhs_indextype_b>>>();\n\n  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_2_IMPL, (1, 2, 3))\n\n#undef CALL_TEST_EVALUATE_RANK_2_IMPL\n\n#undef DIM\n}\n\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/EvaluateRank3.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <iterator>\n#include <limits>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TestHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::tenex {\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that evaluating a right hand side tensor expression containing a\n/// single rank 3 tensor correctly assigns the data to the evaluated left hand\n/// side tensor\n///\n/// \\details `TensorIndexA`, `TensorIndexB`, and `TensorIndexC` can be any type\n/// of TensorIndex and are not necessarily `ti::a`, `ti::b`, and `ti::c`. The\n/// \"A\", \"B\", and \"C\" suffixes just denote the ordering of the generic indices\n/// of the RHS tensor expression. In the RHS tensor expression, it means\n/// `TensorIndexA` is the first index used, `TensorIndexB` is the second index\n/// used, and `TensorIndexC` is the third index used.\n///\n/// If we consider the RHS tensor's generic indices to be (a, b, c), then this\n/// test checks that the data in the evaluated LHS tensor is correct according\n/// to the index orders of the LHS and RHS. The possible cases that are checked\n/// are when the LHS tensor is evaluated with index orders: (a, b, c),\n/// (a, c, b), (b, a, c), (b, c, a), (c, a, b), and (c, b, a).\n///\n/// If `ReturnLhsTensor == true`, the `tenex::evaluate` overload that returns\n/// the LHS tensor will be tested. This, in turn, includes testing whether\n/// `tenex::evaluate` is deducing the correct LHS tensor return type, where\n/// `LhsSymmetry` is its expected symmetry and `LhsTensorIndexType` is its\n/// expected list of indices.\n///\n/// If `ReturnLhsTensor == false`, the `tenex::evaluate` overload that takes a\n/// LHS tensor as an argument will be tested. In this case, `LhsSymmetry` and\n/// `LhsTensorIndexList` can be different from and will override what would be\n/// automatically deduced from the RHS tensor expression. This is useful for\n/// testing evaluations where the desired LHS tensor type would not\n/// automatically be deduced from the RHS expression. For example, given some\n/// tensor \\f$L_{abc}\\f$ with three spacetime indices, one can test whether\n/// \\f$L_{ijk} = ...\\f$ correctly only assigns to the spatial-spatial-spatial\n/// components of the tensor. Likewise, `ReturnLhsTensor == false` is\n/// necessary to test cases where the LHS symmetry is different from what\n/// would be deduced from the RHS expression.\n///\n/// \\tparam ReturnLhsTensor whether to test tensor expression evaluation by\n/// returning the result tensor or not, which instead tests evaluation by\n/// assigning to the result tensor passed in as an argument\n/// \\tparam DataType the type of data being stored in the Tensors\n/// \\tparam RhsSymmetry the ::Symmetry of the RHS Tensor\n/// \\tparam RhsTensorIndexTypeList the RHS Tensor's typelist of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\n/// \\tparam TensorIndexA the first TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::a`\n/// \\tparam TensorIndexB the second TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::B`\n/// \\tparam TensorIndexC the third TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::c`\n/// \\tparam LhsSymmetry the ::Symmetry of the LHS Tensor\n/// \\tparam LhsTensorIndexTypeList the LHS Tensor's typelist of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsTensorIndexTypeList, auto& TensorIndexA,\n          auto& TensorIndexB, auto& TensorIndexC,\n          typename LhsSymmetry = RhsSymmetry,\n          typename LhsTensorIndexTypeList = RhsTensorIndexTypeList>\nvoid test_evaluate_rank_3_impl() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-5.0, 5.0);\n  const size_t used_for_size = 3;\n  using R_abc_type = Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList>;\n  const auto R_abc = make_with_random_values<R_abc_type>(\n      make_not_null(&generator), distribution, used_for_size);\n  auto expected_L_abc =\n      ReturnLhsTensor\n          ? Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>{}\n          : make_with_value<\n                Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>>(\n                used_for_size, component_placeholder_value<DataType>::value);\n\n  const std::int32_t lhs_symmetry_element_a = tmpl::at_c<LhsSymmetry, 0>::value;\n  const std::int32_t lhs_symmetry_element_b = tmpl::at_c<LhsSymmetry, 1>::value;\n  const std::int32_t lhs_symmetry_element_c = tmpl::at_c<LhsSymmetry, 2>::value;\n  using lhs_tensorindextype_a = tmpl::at_c<LhsTensorIndexTypeList, 0>;\n  using lhs_tensorindextype_b = tmpl::at_c<LhsTensorIndexTypeList, 1>;\n  using lhs_tensorindextype_c = tmpl::at_c<LhsTensorIndexTypeList, 2>;\n  using rhs_tensorindextype_a = tmpl::at_c<RhsTensorIndexTypeList, 0>;\n  using rhs_tensorindextype_b = tmpl::at_c<RhsTensorIndexTypeList, 1>;\n  using rhs_tensorindextype_c = tmpl::at_c<RhsTensorIndexTypeList, 2>;\n\n  std::array<std::pair<size_t, size_t>, 3> lhs_index_value_ranges{};\n  lhs_index_value_ranges[0] =\n      get_index_value_range<lhs_tensorindextype_a, TensorIndexA>();\n  lhs_index_value_ranges[1] =\n      get_index_value_range<lhs_tensorindextype_b, TensorIndexB>();\n  lhs_index_value_ranges[2] =\n      get_index_value_range<lhs_tensorindextype_c, TensorIndexC>();\n  std::array<std::pair<size_t, size_t>, 3> rhs_index_value_ranges{};\n  rhs_index_value_ranges[0] =\n      get_index_value_range<rhs_tensorindextype_a, TensorIndexA>();\n  rhs_index_value_ranges[1] =\n      get_index_value_range<rhs_tensorindextype_b, TensorIndexB>();\n  rhs_index_value_ranges[2] =\n      get_index_value_range<rhs_tensorindextype_c, TensorIndexC>();\n\n  for (size_t lhs_a = lhs_index_value_ranges[0].first,\n              rhs_a = rhs_index_value_ranges[0].first;\n       lhs_a <= lhs_index_value_ranges[0].second; lhs_a++, rhs_a++) {\n    for (size_t lhs_b = lhs_index_value_ranges[1].first,\n                rhs_b = rhs_index_value_ranges[1].first;\n         lhs_b <= lhs_index_value_ranges[1].second; lhs_b++, rhs_b++) {\n      for (size_t lhs_c = lhs_index_value_ranges[2].first,\n                  rhs_c = rhs_index_value_ranges[2].first;\n           lhs_c <= lhs_index_value_ranges[2].second; lhs_c++, rhs_c++) {\n        expected_L_abc.get(lhs_a, lhs_b, lhs_c) =\n            R_abc.get(rhs_a, rhs_b, rhs_c);\n      }\n    }\n  }\n\n  const auto rhs_expression = R_abc(TensorIndexA, TensorIndexB, TensorIndexC);\n\n  // L_{abc} = R_{abc}\n  using L_abc_type = Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>;\n  L_abc_type L_abc(used_for_size);\n  // component placeholder is used to detect which components have incorrectly\n  // or correctly (in the case of using spatial or time indices for spacetime\n  // indices) not been modified by evaluation of the RHS expression\n  std::fill(L_abc.begin(), L_abc.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexB, TensorIndexC>(\n      make_not_null(&L_abc), rhs_expression);\n\n  // L_{acb} = R_{abc}\n  using L_acb_symmetry =\n      Symmetry<lhs_symmetry_element_a, lhs_symmetry_element_c,\n               lhs_symmetry_element_b>;\n  using L_acb_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_a, lhs_tensorindextype_c,\n                 lhs_tensorindextype_b>;\n  using L_acb_type =\n      Tensor<DataType, L_acb_symmetry, L_acb_tensorindextype_list>;\n  L_acb_type L_acb(used_for_size);\n  std::fill(L_acb.begin(), L_acb.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexC, TensorIndexB>(\n      make_not_null(&L_acb), rhs_expression);\n\n  // L_{bac} = R_{abc}\n  using L_bac_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_a,\n               lhs_symmetry_element_c>;\n  using L_bac_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_a,\n                 lhs_tensorindextype_c>;\n  using L_bac_type =\n      Tensor<DataType, L_bac_symmetry, L_bac_tensorindextype_list>;\n  L_bac_type L_bac(used_for_size);\n  std::fill(L_bac.begin(), L_bac.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexA, TensorIndexC>(\n      make_not_null(&L_bac), rhs_expression);\n\n  // L_{bca} = R_{abc}\n  using L_bca_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_c,\n               lhs_symmetry_element_a>;\n  using L_bca_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_c,\n                 lhs_tensorindextype_a>;\n  using L_bca_type =\n      Tensor<DataType, L_bca_symmetry, L_bca_tensorindextype_list>;\n  L_bca_type L_bca(used_for_size);\n  std::fill(L_bca.begin(), L_bca.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexC, TensorIndexA>(\n      make_not_null(&L_bca), rhs_expression);\n\n  // L_{cab} = R_{abc}\n  using L_cab_symmetry =\n      Symmetry<lhs_symmetry_element_c, lhs_symmetry_element_a,\n               lhs_symmetry_element_b>;\n  using L_cab_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_c, lhs_tensorindextype_a,\n                 lhs_tensorindextype_b>;\n  using L_cab_type =\n      Tensor<DataType, L_cab_symmetry, L_cab_tensorindextype_list>;\n  L_cab_type L_cab(used_for_size);\n  std::fill(L_cab.begin(), L_cab.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexA, TensorIndexB>(\n      make_not_null(&L_cab), rhs_expression);\n\n  // L_{cba} = R_{abc}\n  using L_cba_symmetry =\n      Symmetry<lhs_symmetry_element_c, lhs_symmetry_element_b,\n               lhs_symmetry_element_a>;\n  using L_cba_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_c, lhs_tensorindextype_b,\n                 lhs_tensorindextype_a>;\n  using L_cba_type =\n      Tensor<DataType, L_cba_symmetry, L_cba_tensorindextype_list>;\n  L_cba_type L_cba(used_for_size);\n  std::fill(L_cba.begin(), L_cba.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexB, TensorIndexA>(\n      make_not_null(&L_cba), rhs_expression);\n\n  const size_t dim_a = tmpl::at_c<LhsTensorIndexTypeList, 0>::dim;\n  const size_t dim_b = tmpl::at_c<LhsTensorIndexTypeList, 1>::dim;\n  const size_t dim_c = tmpl::at_c<LhsTensorIndexTypeList, 2>::dim;\n\n  // check LHS evaluated correctly\n  for (size_t lhs_a = 0; lhs_a < dim_a; ++lhs_a) {\n    for (size_t lhs_b = 0; lhs_b < dim_b; ++lhs_b) {\n      for (size_t lhs_c = 0; lhs_c < dim_c; ++lhs_c) {\n        const auto& expected_result = expected_L_abc.get(lhs_a, lhs_b, lhs_c);\n\n        CHECK(L_abc.get(lhs_a, lhs_b, lhs_c) == expected_result);\n        CHECK(L_acb.get(lhs_a, lhs_c, lhs_b) == expected_result);\n        CHECK(L_bac.get(lhs_b, lhs_a, lhs_c) == expected_result);\n        CHECK(L_bca.get(lhs_b, lhs_c, lhs_a) == expected_result);\n        CHECK(L_cab.get(lhs_c, lhs_a, lhs_b) == expected_result);\n        CHECK(L_cba.get(lhs_c, lhs_b, lhs_a) == expected_result);\n      }\n    }\n  }\n\n  // Test with Variables\n  if constexpr (is_derived_of_vector_impl_v<DataType>) {\n    Variables<tmpl::list<\n        ::Tags::TempTensor<0, R_abc_type>, ::Tags::TempTensor<1, L_abc_type>,\n        ::Tags::TempTensor<2, L_acb_type>, ::Tags::TempTensor<3, L_bac_type>,\n        ::Tags::TempTensor<4, L_bca_type>, ::Tags::TempTensor<5, L_cab_type>,\n        ::Tags::TempTensor<6, L_cba_type>>>\n        vars(used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n    R_abc_type& R_abc_temp = get<::Tags::TempTensor<0, R_abc_type>>(vars);\n    R_abc_temp = R_abc;\n\n    // L_{abc} = R_{abc}\n    L_abc_type& L_abc_temp = get<::Tags::TempTensor<1, L_abc_type>>(vars);\n    std::fill(L_abc_temp.begin(), L_abc_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexB, TensorIndexC>(\n        make_not_null(&L_abc_temp), rhs_expression);\n\n    // L_{acb} = R_{abc}\n    L_acb_type& L_acb_temp = get<::Tags::TempTensor<2, L_acb_type>>(vars);\n    std::fill(L_acb_temp.begin(), L_acb_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexC, TensorIndexB>(\n        make_not_null(&L_acb_temp), rhs_expression);\n\n    // L_{bac} = R_{abc}\n    L_bac_type& L_bac_temp = get<::Tags::TempTensor<3, L_bac_type>>(vars);\n    std::fill(L_bac_temp.begin(), L_bac_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexA, TensorIndexC>(\n        make_not_null(&L_bac_temp), rhs_expression);\n\n    // L_{bca} = R_{abc}\n    L_bca_type& L_bca_temp = get<::Tags::TempTensor<4, L_bca_type>>(vars);\n    std::fill(L_bca_temp.begin(), L_bca_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexC, TensorIndexA>(\n        make_not_null(&L_bca_temp), rhs_expression);\n\n    // L_{cab} = R_{abc}\n    L_cab_type& L_cab_temp = get<::Tags::TempTensor<5, L_cab_type>>(vars);\n    std::fill(L_cab_temp.begin(), L_cab_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexA, TensorIndexB>(\n        make_not_null(&L_cab_temp), rhs_expression);\n\n    // L_{cba} = R_{abc}\n    L_cba_type& L_cba_temp = get<::Tags::TempTensor<6, L_cba_type>>(vars);\n    std::fill(L_cba_temp.begin(), L_cba_temp.end(),\n              component_placeholder_value<DataType>::value);\n    call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexB, TensorIndexA>(\n        make_not_null(&L_cba_temp), rhs_expression);\n\n    // check RHS wasn't modified\n    CHECK(R_abc_temp == R_abc);\n\n    // check LHS evaluated correctly\n    for (size_t lhs_a = 0; lhs_a < dim_a; ++lhs_a) {\n      for (size_t lhs_b = 0; lhs_b < dim_b; ++lhs_b) {\n        for (size_t lhs_c = 0; lhs_c < dim_c; ++lhs_c) {\n          const auto& expected_result = expected_L_abc.get(lhs_a, lhs_b, lhs_c);\n\n          CHECK(L_abc_temp.get(lhs_a, lhs_b, lhs_c) == expected_result);\n          CHECK(L_acb_temp.get(lhs_a, lhs_c, lhs_b) == expected_result);\n          CHECK(L_bac_temp.get(lhs_b, lhs_a, lhs_c) == expected_result);\n          CHECK(L_bca_temp.get(lhs_b, lhs_c, lhs_a) == expected_result);\n          CHECK(L_cab_temp.get(lhs_c, lhs_a, lhs_b) == expected_result);\n          CHECK(L_cba_temp.get(lhs_c, lhs_b, lhs_a) == expected_result);\n        }\n      }\n    }\n  }\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Iterate testing of evaluating single rank 3 Tensors on multiple\n/// dimension combinations\n///\n/// We test various different symmetries across several functions to ensure that\n/// the code works correctly with symmetries. This function tests one of the\n/// following symmetries:\n/// - <3, 2, 1>\n/// - <2, 2, 1>\n/// - <2, 1, 2>\n/// - <2, 1, 1>\n/// - <1, 1, 1>\n///\n/// \\details See `test_evaluate_rank_3_impl` for general details.\n///\n/// \\tparam ReturnLhsTensor whether to test tensor expression evaluation by\n/// returning the result tensor or not, which instead tests evaluation by\n/// assigning to the result tensor passed in as an argument\n/// \\tparam DataType the type of data being stored in the Tensors\n/// \\tparam RhsSymmetry the ::Symmetry of the RHS Tensor\n/// \\tparam RhsIndexTypeList the RHS Tensor's integral list of `IndexType`s\n/// \\tparam TensorIndexA the first TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::a`\n/// \\tparam TensorIndexB the second TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::B`\n/// \\tparam TensorIndexC the third TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::c`\n/// \\tparam Frame the frame of the tensor index\n/// \\tparam LhsSymmetry the ::Symmetry of the LHS Tensor\n/// \\tparam LhsIndexTypeList the LHS Tensor's integral list of `IndexType`s\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsIndexTypeList, auto& TensorIndexA, auto& TensorIndexB,\n          auto& TensorIndexC, typename Frame,\n          typename LhsSymmetry = RhsSymmetry,\n          typename LhsIndexTypeList = RhsIndexTypeList,\n          Requires<std::is_same_v<RhsSymmetry, Symmetry<3, 2, 1>>> = nullptr>\nvoid test_evaluate_rank_3() {\n  constexpr IndexType rhs_indextype_a = tmpl::at_c<RhsIndexTypeList, 0>::value;\n  constexpr IndexType rhs_indextype_b = tmpl::at_c<RhsIndexTypeList, 1>::value;\n  constexpr IndexType rhs_indextype_c = tmpl::at_c<RhsIndexTypeList, 2>::value;\n  constexpr IndexType lhs_indextype_a = tmpl::at_c<LhsIndexTypeList, 0>::value;\n  constexpr IndexType lhs_indextype_b = tmpl::at_c<LhsIndexTypeList, 1>::value;\n  constexpr IndexType lhs_indextype_c = tmpl::at_c<LhsIndexTypeList, 2>::value;\n\n#define DIM_A(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM_B(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define DIM_C(data) BOOST_PP_TUPLE_ELEM(2, data)\n\n#define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data)                               \\\n  test_evaluate_rank_3_impl<                                                  \\\n      ReturnLhsTensor, DataType, RhsSymmetry,                                 \\\n      index_list<                                                             \\\n                                                                              \\\n          ::Tensor_detail::TensorIndexType<DIM_A(data), TensorIndexA.valence, \\\n                                           Frame, rhs_indextype_a>,           \\\n          ::Tensor_detail::TensorIndexType<DIM_B(data), TensorIndexB.valence, \\\n                                           Frame, rhs_indextype_b>,           \\\n          ::Tensor_detail::TensorIndexType<DIM_C(data), TensorIndexC.valence, \\\n                                           Frame, rhs_indextype_c>>,          \\\n      TensorIndexA, TensorIndexB, TensorIndexC, LhsSymmetry,                  \\\n      index_list<                                                             \\\n          ::Tensor_detail::TensorIndexType<DIM_A(data), TensorIndexA.valence, \\\n                                           Frame, lhs_indextype_a>,           \\\n          ::Tensor_detail::TensorIndexType<DIM_B(data), TensorIndexB.valence, \\\n                                           Frame, lhs_indextype_b>,           \\\n          ::Tensor_detail::TensorIndexType<DIM_C(data), TensorIndexC.valence, \\\n                                           Frame, lhs_indextype_c>>>();\n\n  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3), (1, 2, 3),\n                          (1, 2, 3))\n\n#undef CALL_TEST_EVALUATE_RANK_3_IMPL\n\n#undef DIM_C\n#undef DIM_B\n#undef DIM_A\n}\n\n/// \\ingroup TestingFrameworkGroup\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsIndexTypeList, auto& TensorIndexA, auto& TensorIndexB,\n          auto& TensorIndexC, typename Frame,\n          typename LhsSymmetry = RhsSymmetry,\n          typename LhsIndexTypeList = RhsIndexTypeList,\n          Requires<std::is_same_v<RhsSymmetry, Symmetry<2, 2, 1>>> = nullptr>\nvoid test_evaluate_rank_3() {\n  constexpr IndexType rhs_indextype_a = tmpl::at_c<RhsIndexTypeList, 0>::value;\n  constexpr IndexType rhs_indextype_b = tmpl::at_c<RhsIndexTypeList, 1>::value;\n  constexpr IndexType rhs_indextype_c = tmpl::at_c<RhsIndexTypeList, 2>::value;\n  constexpr IndexType lhs_indextype_a = tmpl::at_c<LhsIndexTypeList, 0>::value;\n  constexpr IndexType lhs_indextype_b = tmpl::at_c<LhsIndexTypeList, 1>::value;\n  constexpr IndexType lhs_indextype_c = tmpl::at_c<LhsIndexTypeList, 2>::value;\n\n#define DIM_AB(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM_C(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data)                                \\\n  test_evaluate_rank_3_impl<                                                   \\\n      ReturnLhsTensor, DataType, RhsSymmetry,                                  \\\n      index_list<                                                              \\\n          ::Tensor_detail::TensorIndexType<DIM_AB(data), TensorIndexA.valence, \\\n                                           Frame, rhs_indextype_a>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_AB(data), TensorIndexB.valence, \\\n                                           Frame, rhs_indextype_b>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_C(data), TensorIndexC.valence,  \\\n                                           Frame, rhs_indextype_c>>,           \\\n      TensorIndexA, TensorIndexB, TensorIndexC, LhsSymmetry,                   \\\n      index_list<                                                              \\\n          ::Tensor_detail::TensorIndexType<DIM_AB(data), TensorIndexA.valence, \\\n                                           Frame, lhs_indextype_a>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_AB(data), TensorIndexB.valence, \\\n                                           Frame, lhs_indextype_b>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_C(data), TensorIndexC.valence,  \\\n                                           Frame, lhs_indextype_c>>>();\n\n  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3), (1, 2, 3))\n\n#undef CALL_TEST_EVALUATE_RANK_3_IMPL\n\n#undef DIM_C\n#undef DIM_AB\n}\n\n/// \\ingroup TestingFrameworkGroup\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsIndexTypeList, auto& TensorIndexA, auto& TensorIndexB,\n          auto& TensorIndexC, typename Frame,\n          typename LhsSymmetry = RhsSymmetry,\n          typename LhsIndexTypeList = RhsIndexTypeList,\n          Requires<std::is_same_v<RhsSymmetry, Symmetry<1, 2, 1>>> = nullptr>\nvoid test_evaluate_rank_3() {\n  constexpr IndexType rhs_indextype_a = tmpl::at_c<RhsIndexTypeList, 0>::value;\n  constexpr IndexType rhs_indextype_b = tmpl::at_c<RhsIndexTypeList, 1>::value;\n  constexpr IndexType rhs_indextype_c = tmpl::at_c<RhsIndexTypeList, 2>::value;\n  constexpr IndexType lhs_indextype_a = tmpl::at_c<LhsIndexTypeList, 0>::value;\n  constexpr IndexType lhs_indextype_b = tmpl::at_c<LhsIndexTypeList, 1>::value;\n  constexpr IndexType lhs_indextype_c = tmpl::at_c<LhsIndexTypeList, 2>::value;\n\n#define DIM_AC(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM_B(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data)                                \\\n  test_evaluate_rank_3_impl<                                                   \\\n      ReturnLhsTensor, DataType, RhsSymmetry,                                  \\\n      index_list<                                                              \\\n          ::Tensor_detail::TensorIndexType<DIM_AC(data), TensorIndexA.valence, \\\n                                           Frame, rhs_indextype_a>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_B(data), TensorIndexB.valence,  \\\n                                           Frame, rhs_indextype_b>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_AC(data), TensorIndexC.valence, \\\n                                           Frame, rhs_indextype_c>>,           \\\n      TensorIndexA, TensorIndexB, TensorIndexC, LhsSymmetry,                   \\\n      index_list<                                                              \\\n          ::Tensor_detail::TensorIndexType<DIM_AC(data), TensorIndexA.valence, \\\n                                           Frame, lhs_indextype_a>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_B(data), TensorIndexB.valence,  \\\n                                           Frame, lhs_indextype_b>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_AC(data), TensorIndexC.valence, \\\n                                           Frame, lhs_indextype_c>>>();\n\n  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3), (1, 2, 3))\n\n#undef CALL_TEST_EVALUATE_RANK_3_IMPL\n\n#undef DIM_B\n#undef DIM_AC\n}\n\n/// \\ingroup TestingFrameworkGroup\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsIndexTypeList, auto& TensorIndexA, auto& TensorIndexB,\n          auto& TensorIndexC, typename Frame,\n          typename LhsSymmetry = RhsSymmetry,\n          typename LhsIndexTypeList = RhsIndexTypeList,\n          Requires<std::is_same_v<RhsSymmetry, Symmetry<2, 1, 1>>> = nullptr>\nvoid test_evaluate_rank_3() {\n  constexpr IndexType rhs_indextype_a = tmpl::at_c<RhsIndexTypeList, 0>::value;\n  constexpr IndexType rhs_indextype_b = tmpl::at_c<RhsIndexTypeList, 1>::value;\n  constexpr IndexType rhs_indextype_c = tmpl::at_c<RhsIndexTypeList, 2>::value;\n  constexpr IndexType lhs_indextype_a = tmpl::at_c<LhsIndexTypeList, 0>::value;\n  constexpr IndexType lhs_indextype_b = tmpl::at_c<LhsIndexTypeList, 1>::value;\n  constexpr IndexType lhs_indextype_c = tmpl::at_c<LhsIndexTypeList, 2>::value;\n\n#define DIM_A(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM_BC(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data)                                \\\n  test_evaluate_rank_3_impl<                                                   \\\n      ReturnLhsTensor, DataType, RhsSymmetry,                                  \\\n      index_list<                                                              \\\n          ::Tensor_detail::TensorIndexType<DIM_A(data), TensorIndexA.valence,  \\\n                                           Frame, rhs_indextype_a>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_BC(data), TensorIndexB.valence, \\\n                                           Frame, rhs_indextype_b>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_BC(data), TensorIndexC.valence, \\\n                                           Frame, rhs_indextype_c>>,           \\\n      TensorIndexA, TensorIndexB, TensorIndexC, LhsSymmetry,                   \\\n      index_list<                                                              \\\n          ::Tensor_detail::TensorIndexType<DIM_A(data), TensorIndexA.valence,  \\\n                                           Frame, lhs_indextype_a>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_BC(data), TensorIndexB.valence, \\\n                                           Frame, lhs_indextype_b>,            \\\n          ::Tensor_detail::TensorIndexType<DIM_BC(data), TensorIndexC.valence, \\\n                                           Frame, lhs_indextype_c>>>();\n\n  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3), (1, 2, 3))\n\n#undef CALL_TEST_EVALUATE_RANK_3_IMPL\n\n#undef DIM_BC\n#undef DIM_A\n}\n\n/// \\ingroup TestingFrameworkGroup\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsIndexTypeList, auto& TensorIndexA, auto& TensorIndexB,\n          auto& TensorIndexC, typename Frame,\n          typename LhsSymmetry = RhsSymmetry,\n          typename LhsIndexTypeList = RhsIndexTypeList,\n          Requires<std::is_same_v<RhsSymmetry, Symmetry<1, 1, 1>>> = nullptr>\nvoid test_evaluate_rank_3() {\n  constexpr IndexType rhs_indextype_a = tmpl::at_c<RhsIndexTypeList, 0>::value;\n  constexpr IndexType rhs_indextype_b = tmpl::at_c<RhsIndexTypeList, 1>::value;\n  constexpr IndexType rhs_indextype_c = tmpl::at_c<RhsIndexTypeList, 2>::value;\n  constexpr IndexType lhs_indextype_a = tmpl::at_c<LhsIndexTypeList, 0>::value;\n  constexpr IndexType lhs_indextype_b = tmpl::at_c<LhsIndexTypeList, 1>::value;\n  constexpr IndexType lhs_indextype_c = tmpl::at_c<LhsIndexTypeList, 2>::value;\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define CALL_TEST_EVALUATE_RANK_3_IMPL(_, data)                             \\\n  test_evaluate_rank_3_impl<                                                \\\n      ReturnLhsTensor, DataType, RhsSymmetry,                               \\\n      index_list<                                                           \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexA.valence, \\\n                                           Frame, rhs_indextype_a>,         \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexB.valence, \\\n                                           Frame, rhs_indextype_b>,         \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexC.valence, \\\n                                           Frame, rhs_indextype_c>>,        \\\n      TensorIndexA, TensorIndexB, TensorIndexC, LhsSymmetry,                \\\n      index_list<                                                           \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexA.valence, \\\n                                           Frame, lhs_indextype_a>,         \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexB.valence, \\\n                                           Frame, lhs_indextype_b>,         \\\n          ::Tensor_detail::TensorIndexType<DIM(data), TensorIndexC.valence, \\\n                                           Frame, lhs_indextype_c>>>();\n\n  GENERATE_INSTANTIATIONS(CALL_TEST_EVALUATE_RANK_3_IMPL, (1, 2, 3))\n\n#undef CALL_TEST_EVALUATE_RANK_3_IMPL\n\n#undef DIM\n}\n\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/EvaluateRank4.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <iterator>\n#include <limits>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/ComponentPlaceholder.hpp\"\n#include \"Helpers/DataStructures/Tensor/Expressions/TestHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::tenex {\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that evaluating a right hand side tensor expression containing a\n/// single rank 4 tensor correctly assigns the data to the evaluated left hand\n/// side tensor\n///\n/// \\details See `test_evaluate_rank_3_impl` for general details.\n///\n/// \\tparam ReturnLhsTensor whether to test tensor expression evaluation by\n/// returning the result tensor or not, which instead tests evaluation by\n/// assigning to the result tensor passed in as an argument\n/// \\tparam DataType the type of data being stored in the Tensors\n/// \\tparam RhsSymmetry the ::Symmetry of the RHS Tensor\n/// \\tparam RhsTensorIndexTypeList the RHS Tensor's typelist of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\n/// \\tparam TensorIndexA the first TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::a`\n/// \\tparam TensorIndexB the second TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::B`\n/// \\tparam TensorIndexC the third TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::c`\n/// \\tparam TensorIndexD the fourth TensorIndex used on the RHS of the\n/// TensorExpression, e.g. `ti::D`\n/// \\tparam LhsSymmetry the ::Symmetry of the LHS Tensor\n/// \\tparam LhsTensorIndexTypeList the LHS Tensor's typelist of\n/// \\ref SpacetimeIndex \"TensorIndexType\"s\ntemplate <bool ReturnLhsTensor, typename DataType, typename RhsSymmetry,\n          typename RhsTensorIndexTypeList, auto& TensorIndexA,\n          auto& TensorIndexB, auto& TensorIndexC, auto& TensorIndexD,\n          typename LhsSymmetry = RhsSymmetry,\n          typename LhsTensorIndexTypeList = RhsTensorIndexTypeList>\nvoid test_evaluate_rank_4() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-5.0, 5.0);\n  const size_t used_for_size = 3;\n  const auto R_abcd = make_with_random_values<\n      Tensor<DataType, RhsSymmetry, RhsTensorIndexTypeList>>(\n      make_not_null(&generator), distribution, used_for_size);\n  auto expected_L_abcd =\n      ReturnLhsTensor\n          ? Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>{}\n          : make_with_value<\n                Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>>(\n                used_for_size, component_placeholder_value<DataType>::value);\n\n  const std::int32_t lhs_symmetry_element_a = tmpl::at_c<LhsSymmetry, 0>::value;\n  const std::int32_t lhs_symmetry_element_b = tmpl::at_c<LhsSymmetry, 1>::value;\n  const std::int32_t lhs_symmetry_element_c = tmpl::at_c<LhsSymmetry, 2>::value;\n  const std::int32_t lhs_symmetry_element_d = tmpl::at_c<LhsSymmetry, 3>::value;\n  using lhs_tensorindextype_a = tmpl::at_c<LhsTensorIndexTypeList, 0>;\n  using lhs_tensorindextype_b = tmpl::at_c<LhsTensorIndexTypeList, 1>;\n  using lhs_tensorindextype_c = tmpl::at_c<LhsTensorIndexTypeList, 2>;\n  using lhs_tensorindextype_d = tmpl::at_c<LhsTensorIndexTypeList, 3>;\n  using rhs_tensorindextype_a = tmpl::at_c<RhsTensorIndexTypeList, 0>;\n  using rhs_tensorindextype_b = tmpl::at_c<RhsTensorIndexTypeList, 1>;\n  using rhs_tensorindextype_c = tmpl::at_c<RhsTensorIndexTypeList, 2>;\n  using rhs_tensorindextype_d = tmpl::at_c<RhsTensorIndexTypeList, 3>;\n\n  std::array<std::pair<size_t, size_t>, 4> lhs_index_value_ranges{};\n  lhs_index_value_ranges[0] =\n      get_index_value_range<lhs_tensorindextype_a, TensorIndexA>();\n  lhs_index_value_ranges[1] =\n      get_index_value_range<lhs_tensorindextype_b, TensorIndexB>();\n  lhs_index_value_ranges[2] =\n      get_index_value_range<lhs_tensorindextype_c, TensorIndexC>();\n  lhs_index_value_ranges[3] =\n      get_index_value_range<lhs_tensorindextype_d, TensorIndexD>();\n  std::array<std::pair<size_t, size_t>, 4> rhs_index_value_ranges{};\n  rhs_index_value_ranges[0] =\n      get_index_value_range<rhs_tensorindextype_a, TensorIndexA>();\n  rhs_index_value_ranges[1] =\n      get_index_value_range<rhs_tensorindextype_b, TensorIndexB>();\n  rhs_index_value_ranges[2] =\n      get_index_value_range<rhs_tensorindextype_c, TensorIndexC>();\n  rhs_index_value_ranges[3] =\n      get_index_value_range<rhs_tensorindextype_d, TensorIndexD>();\n\n  for (size_t lhs_a = lhs_index_value_ranges[0].first,\n              rhs_a = rhs_index_value_ranges[0].first;\n       lhs_a <= lhs_index_value_ranges[0].second; lhs_a++, rhs_a++) {\n    for (size_t lhs_b = lhs_index_value_ranges[1].first,\n                rhs_b = rhs_index_value_ranges[1].first;\n         lhs_b <= lhs_index_value_ranges[1].second; lhs_b++, rhs_b++) {\n      for (size_t lhs_c = lhs_index_value_ranges[2].first,\n                  rhs_c = rhs_index_value_ranges[2].first;\n           lhs_c <= lhs_index_value_ranges[2].second; lhs_c++, rhs_c++) {\n        for (size_t lhs_d = lhs_index_value_ranges[3].first,\n                    rhs_d = rhs_index_value_ranges[3].first;\n             lhs_d <= lhs_index_value_ranges[3].second; lhs_d++, rhs_d++) {\n          expected_L_abcd.get(lhs_a, lhs_b, lhs_c, lhs_d) =\n              R_abcd.get(rhs_a, rhs_b, rhs_c, rhs_d);\n        }\n      }\n    }\n  }\n\n  const auto rhs_expression =\n      R_abcd(TensorIndexA, TensorIndexB, TensorIndexC, TensorIndexD);\n\n  // L_{abcd} = R_{abcd}\n  using L_abcd_type = Tensor<DataType, LhsSymmetry, LhsTensorIndexTypeList>;\n  L_abcd_type L_abcd(used_for_size);\n  // component placeholder is used to detect which components have incorrectly\n  // or correctly (in the case of using spatial or time indices for spacetime\n  // indices) not been modified by evaluation of the RHS expression\n  std::fill(L_abcd.begin(), L_abcd.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexB, TensorIndexC,\n                TensorIndexD>(make_not_null(&L_abcd), rhs_expression);\n\n  // L_{abdc} = R_{abcd}\n  using L_abdc_symmetry =\n      Symmetry<lhs_symmetry_element_a, lhs_symmetry_element_b,\n               lhs_symmetry_element_d, lhs_symmetry_element_c>;\n  using L_abdc_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_a, lhs_tensorindextype_b,\n                 lhs_tensorindextype_d, lhs_tensorindextype_c>;\n  using L_abdc_type =\n      Tensor<DataType, L_abdc_symmetry, L_abdc_tensorindextype_list>;\n  L_abdc_type L_abdc(used_for_size);\n  std::fill(L_abdc.begin(), L_abdc.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexB, TensorIndexD,\n                TensorIndexC>(make_not_null(&L_abdc), rhs_expression);\n\n  // L_{acbd} = R_{abcd}\n  using L_acbd_symmetry =\n      Symmetry<lhs_symmetry_element_a, lhs_symmetry_element_c,\n               lhs_symmetry_element_b, lhs_symmetry_element_d>;\n  using L_acbd_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_a, lhs_tensorindextype_c,\n                 lhs_tensorindextype_b, lhs_tensorindextype_d>;\n  using L_acbd_type =\n      Tensor<DataType, L_acbd_symmetry, L_acbd_tensorindextype_list>;\n  L_acbd_type L_acbd(used_for_size);\n  std::fill(L_acbd.begin(), L_acbd.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexC, TensorIndexB,\n                TensorIndexD>(make_not_null(&L_acbd), rhs_expression);\n\n  // L_{acdb} = R_{abcd}\n  using L_acdb_symmetry =\n      Symmetry<lhs_symmetry_element_a, lhs_symmetry_element_c,\n               lhs_symmetry_element_d, lhs_symmetry_element_b>;\n  using L_acdb_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_a, lhs_tensorindextype_c,\n                 lhs_tensorindextype_d, lhs_tensorindextype_b>;\n  using L_acdb_type =\n      Tensor<DataType, L_acdb_symmetry, L_acdb_tensorindextype_list>;\n  L_acdb_type L_acdb(used_for_size);\n  std::fill(L_acdb.begin(), L_acdb.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexC, TensorIndexD,\n                TensorIndexB>(make_not_null(&L_acdb), rhs_expression);\n\n  // L_{adbc} = R_{abcd}\n  using L_adbc_symmetry =\n      Symmetry<lhs_symmetry_element_a, lhs_symmetry_element_d,\n               lhs_symmetry_element_b, lhs_symmetry_element_c>;\n  using L_adbc_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_a, lhs_tensorindextype_d,\n                 lhs_tensorindextype_b, lhs_tensorindextype_c>;\n  using L_adbc_type =\n      Tensor<DataType, L_adbc_symmetry, L_adbc_tensorindextype_list>;\n  L_adbc_type L_adbc(used_for_size);\n  std::fill(L_adbc.begin(), L_adbc.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexD, TensorIndexB,\n                TensorIndexC>(make_not_null(&L_adbc), rhs_expression);\n\n  // L_{adcb} = R_{abcd}\n  using L_adcb_symmetry =\n      Symmetry<lhs_symmetry_element_a, lhs_symmetry_element_d,\n               lhs_symmetry_element_c, lhs_symmetry_element_b>;\n  using L_adcb_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_a, lhs_tensorindextype_d,\n                 lhs_tensorindextype_c, lhs_tensorindextype_b>;\n  using L_adcb_type =\n      Tensor<DataType, L_adcb_symmetry, L_adcb_tensorindextype_list>;\n  L_adcb_type L_adcb(used_for_size);\n  std::fill(L_adcb.begin(), L_adcb.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexA, TensorIndexD, TensorIndexC,\n                TensorIndexB>(make_not_null(&L_adcb), rhs_expression);\n\n  // L_{bacd} = R_{abcd}\n  using L_bacd_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_a,\n               lhs_symmetry_element_c, lhs_symmetry_element_d>;\n  using L_bacd_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_a,\n                 lhs_tensorindextype_c, lhs_tensorindextype_d>;\n  using L_bacd_type =\n      Tensor<DataType, L_bacd_symmetry, L_bacd_tensorindextype_list>;\n  L_bacd_type L_bacd(used_for_size);\n  std::fill(L_bacd.begin(), L_bacd.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexA, TensorIndexC,\n                TensorIndexD>(make_not_null(&L_bacd), rhs_expression);\n\n  // L_{badc} = R_{abcd}\n  using L_badc_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_a,\n               lhs_symmetry_element_d, lhs_symmetry_element_c>;\n  using L_badc_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_a,\n                 lhs_tensorindextype_d, lhs_tensorindextype_c>;\n  using L_badc_type =\n      Tensor<DataType, L_badc_symmetry, L_badc_tensorindextype_list>;\n  L_badc_type L_badc(used_for_size);\n  std::fill(L_badc.begin(), L_badc.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexA, TensorIndexD,\n                TensorIndexC>(make_not_null(&L_badc), rhs_expression);\n\n  // L_{bcad} = R_{abcd}\n  using L_bcad_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_c,\n               lhs_symmetry_element_a, lhs_symmetry_element_d>;\n  using L_bcad_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_c,\n                 lhs_tensorindextype_a, lhs_tensorindextype_d>;\n  using L_bcad_type =\n      Tensor<DataType, L_bcad_symmetry, L_bcad_tensorindextype_list>;\n  L_bcad_type L_bcad(used_for_size);\n  std::fill(L_bcad.begin(), L_bcad.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexC, TensorIndexA,\n                TensorIndexD>(make_not_null(&L_bcad), rhs_expression);\n\n  // L_{bcda} = R_{abcd}\n  using L_bcda_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_c,\n               lhs_symmetry_element_d, lhs_symmetry_element_a>;\n  using L_bcda_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_c,\n                 lhs_tensorindextype_d, lhs_tensorindextype_a>;\n  using L_bcda_type =\n      Tensor<DataType, L_bcda_symmetry, L_bcda_tensorindextype_list>;\n  L_bcda_type L_bcda(used_for_size);\n  std::fill(L_bcda.begin(), L_bcda.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexC, TensorIndexD,\n                TensorIndexA>(make_not_null(&L_bcda), rhs_expression);\n\n  // L_{bdac} = R_{abcd}\n  using L_bdac_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_d,\n               lhs_symmetry_element_a, lhs_symmetry_element_c>;\n  using L_bdac_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_d,\n                 lhs_tensorindextype_a, lhs_tensorindextype_c>;\n  using L_bdac_type =\n      Tensor<DataType, L_bdac_symmetry, L_bdac_tensorindextype_list>;\n  L_bdac_type L_bdac(used_for_size);\n  std::fill(L_bdac.begin(), L_bdac.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexD, TensorIndexA,\n                TensorIndexC>(make_not_null(&L_bdac), rhs_expression);\n\n  // L_{bdca} = R_{abcd}\n  using L_bdca_symmetry =\n      Symmetry<lhs_symmetry_element_b, lhs_symmetry_element_d,\n               lhs_symmetry_element_c, lhs_symmetry_element_a>;\n  using L_bdca_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_b, lhs_tensorindextype_d,\n                 lhs_tensorindextype_c, lhs_tensorindextype_a>;\n  using L_bdca_type =\n      Tensor<DataType, L_bdca_symmetry, L_bdca_tensorindextype_list>;\n  L_bdca_type L_bdca(used_for_size);\n  std::fill(L_bdca.begin(), L_bdca.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexB, TensorIndexD, TensorIndexC,\n                TensorIndexA>(make_not_null(&L_bdca), rhs_expression);\n\n  // L_{cabd} = R_{abcd}\n  using L_cabd_symmetry =\n      Symmetry<lhs_symmetry_element_c, lhs_symmetry_element_a,\n               lhs_symmetry_element_b, lhs_symmetry_element_d>;\n  using L_cabd_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_c, lhs_tensorindextype_a,\n                 lhs_tensorindextype_b, lhs_tensorindextype_d>;\n  using L_cabd_type =\n      Tensor<DataType, L_cabd_symmetry, L_cabd_tensorindextype_list>;\n  L_cabd_type L_cabd(used_for_size);\n  std::fill(L_cabd.begin(), L_cabd.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexA, TensorIndexB,\n                TensorIndexD>(make_not_null(&L_cabd), rhs_expression);\n\n  // L_{cadb} = R_{abcd}\n  using L_cadb_symmetry =\n      Symmetry<lhs_symmetry_element_c, lhs_symmetry_element_a,\n               lhs_symmetry_element_d, lhs_symmetry_element_b>;\n  using L_cadb_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_c, lhs_tensorindextype_a,\n                 lhs_tensorindextype_d, lhs_tensorindextype_b>;\n  using L_cadb_type =\n      Tensor<DataType, L_cadb_symmetry, L_cadb_tensorindextype_list>;\n  L_cadb_type L_cadb(used_for_size);\n  std::fill(L_cadb.begin(), L_cadb.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexA, TensorIndexD,\n                TensorIndexB>(make_not_null(&L_cadb), rhs_expression);\n\n  // L_{cbad} = R_{abcd}\n  using L_cbad_symmetry =\n      Symmetry<lhs_symmetry_element_c, lhs_symmetry_element_b,\n               lhs_symmetry_element_a, lhs_symmetry_element_d>;\n  using L_cbad_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_c, lhs_tensorindextype_b,\n                 lhs_tensorindextype_a, lhs_tensorindextype_d>;\n  using L_cbad_type =\n      Tensor<DataType, L_cbad_symmetry, L_cbad_tensorindextype_list>;\n  L_cbad_type L_cbad(used_for_size);\n  std::fill(L_cbad.begin(), L_cbad.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexB, TensorIndexA,\n                TensorIndexD>(make_not_null(&L_cbad), rhs_expression);\n\n  // L_{cbda} = R_{abcd}\n  using L_cbda_symmetry =\n      Symmetry<lhs_symmetry_element_c, lhs_symmetry_element_b,\n               lhs_symmetry_element_d, lhs_symmetry_element_a>;\n  using L_cbda_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_c, lhs_tensorindextype_b,\n                 lhs_tensorindextype_d, lhs_tensorindextype_a>;\n  using L_cbda_type =\n      Tensor<DataType, L_cbda_symmetry, L_cbda_tensorindextype_list>;\n  L_cbda_type L_cbda(used_for_size);\n  std::fill(L_cbda.begin(), L_cbda.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexB, TensorIndexD,\n                TensorIndexA>(make_not_null(&L_cbda), rhs_expression);\n\n  // L_{cdab} = R_{abcd}\n  using L_cdab_symmetry =\n      Symmetry<lhs_symmetry_element_c, lhs_symmetry_element_d,\n               lhs_symmetry_element_a, lhs_symmetry_element_b>;\n  using L_cdab_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_c, lhs_tensorindextype_d,\n                 lhs_tensorindextype_a, lhs_tensorindextype_b>;\n  using L_cdab_type =\n      Tensor<DataType, L_cdab_symmetry, L_cdab_tensorindextype_list>;\n  L_cdab_type L_cdab(used_for_size);\n  std::fill(L_cdab.begin(), L_cdab.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexD, TensorIndexA,\n                TensorIndexB>(make_not_null(&L_cdab), rhs_expression);\n\n  // L_{cdba} = R_{abcd}\n  using L_cdba_symmetry =\n      Symmetry<lhs_symmetry_element_c, lhs_symmetry_element_d,\n               lhs_symmetry_element_b, lhs_symmetry_element_a>;\n  using L_cdba_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_c, lhs_tensorindextype_d,\n                 lhs_tensorindextype_b, lhs_tensorindextype_a>;\n  using L_cdba_type =\n      Tensor<DataType, L_cdba_symmetry, L_cdba_tensorindextype_list>;\n  L_cdba_type L_cdba(used_for_size);\n  std::fill(L_cdba.begin(), L_cdba.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexC, TensorIndexD, TensorIndexB,\n                TensorIndexA>(make_not_null(&L_cdba), rhs_expression);\n\n  // L_{dabc} = R_{abcd}\n  using L_dabc_symmetry =\n      Symmetry<lhs_symmetry_element_d, lhs_symmetry_element_a,\n               lhs_symmetry_element_b, lhs_symmetry_element_c>;\n  using L_dabc_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_d, lhs_tensorindextype_a,\n                 lhs_tensorindextype_b, lhs_tensorindextype_c>;\n  using L_dabc_type =\n      Tensor<DataType, L_dabc_symmetry, L_dabc_tensorindextype_list>;\n  L_dabc_type L_dabc(used_for_size);\n  std::fill(L_dabc.begin(), L_dabc.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexD, TensorIndexA, TensorIndexB,\n                TensorIndexC>(make_not_null(&L_dabc), rhs_expression);\n\n  // L_{dacb} = R_{abcd}\n  using L_dacb_symmetry =\n      Symmetry<lhs_symmetry_element_d, lhs_symmetry_element_a,\n               lhs_symmetry_element_c, lhs_symmetry_element_b>;\n  using L_dacb_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_d, lhs_tensorindextype_a,\n                 lhs_tensorindextype_c, lhs_tensorindextype_b>;\n  using L_dacb_type =\n      Tensor<DataType, L_dacb_symmetry, L_dacb_tensorindextype_list>;\n  L_dacb_type L_dacb(used_for_size);\n  std::fill(L_dacb.begin(), L_dacb.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexD, TensorIndexA, TensorIndexC,\n                TensorIndexB>(make_not_null(&L_dacb), rhs_expression);\n\n  // L_{dbac} = R_{abcd}\n  using L_dbac_symmetry =\n      Symmetry<lhs_symmetry_element_d, lhs_symmetry_element_b,\n               lhs_symmetry_element_a, lhs_symmetry_element_c>;\n  using L_dbac_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_d, lhs_tensorindextype_b,\n                 lhs_tensorindextype_a, lhs_tensorindextype_c>;\n  using L_dbac_type =\n      Tensor<DataType, L_dbac_symmetry, L_dbac_tensorindextype_list>;\n  L_dbac_type L_dbac(used_for_size);\n  std::fill(L_dbac.begin(), L_dbac.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexD, TensorIndexB, TensorIndexA,\n                TensorIndexC>(make_not_null(&L_dbac), rhs_expression);\n\n  // L_{dbca} = R_{abcd}\n  using L_dbca_symmetry =\n      Symmetry<lhs_symmetry_element_d, lhs_symmetry_element_b,\n               lhs_symmetry_element_c, lhs_symmetry_element_a>;\n  using L_dbca_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_d, lhs_tensorindextype_b,\n                 lhs_tensorindextype_c, lhs_tensorindextype_a>;\n  using L_dbca_type =\n      Tensor<DataType, L_dbca_symmetry, L_dbca_tensorindextype_list>;\n  L_dbca_type L_dbca(used_for_size);\n  std::fill(L_dbca.begin(), L_dbca.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexD, TensorIndexB, TensorIndexC,\n                TensorIndexA>(make_not_null(&L_dbca), rhs_expression);\n\n  // L_{dcab} = R_{abcd}\n  using L_dcab_symmetry =\n      Symmetry<lhs_symmetry_element_d, lhs_symmetry_element_c,\n               lhs_symmetry_element_a, lhs_symmetry_element_b>;\n  using L_dcab_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_d, lhs_tensorindextype_c,\n                 lhs_tensorindextype_a, lhs_tensorindextype_b>;\n  using L_dcab_type =\n      Tensor<DataType, L_dcab_symmetry, L_dcab_tensorindextype_list>;\n  L_dcab_type L_dcab(used_for_size);\n  std::fill(L_dcab.begin(), L_dcab.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexD, TensorIndexC, TensorIndexA,\n                TensorIndexB>(make_not_null(&L_dcab), rhs_expression);\n\n  // L_{dcba} = R_{abcd}\n  using L_dcba_symmetry =\n      Symmetry<lhs_symmetry_element_d, lhs_symmetry_element_c,\n               lhs_symmetry_element_b, lhs_symmetry_element_a>;\n  using L_dcba_tensorindextype_list =\n      tmpl::list<lhs_tensorindextype_d, lhs_tensorindextype_c,\n                 lhs_tensorindextype_b, lhs_tensorindextype_a>;\n  using L_dcba_type =\n      Tensor<DataType, L_dcba_symmetry, L_dcba_tensorindextype_list>;\n  L_dcba_type L_dcba(used_for_size);\n  std::fill(L_dcba.begin(), L_dcba.end(),\n            component_placeholder_value<DataType>::value);\n  call_evaluate<ReturnLhsTensor, TensorIndexD, TensorIndexC, TensorIndexB,\n                TensorIndexA>(make_not_null(&L_dcba), rhs_expression);\n\n  const size_t dim_a = tmpl::at_c<LhsTensorIndexTypeList, 0>::dim;\n  const size_t dim_b = tmpl::at_c<LhsTensorIndexTypeList, 1>::dim;\n  const size_t dim_c = tmpl::at_c<LhsTensorIndexTypeList, 2>::dim;\n  const size_t dim_d = tmpl::at_c<LhsTensorIndexTypeList, 3>::dim;\n\n  // check LHS evaluated correctly\n  for (size_t lhs_a = 0; lhs_a < dim_a; ++lhs_a) {\n    for (size_t lhs_b = 0; lhs_b < dim_b; ++lhs_b) {\n      for (size_t lhs_c = 0; lhs_c < dim_c; ++lhs_c) {\n        for (size_t lhs_d = 0; lhs_d < dim_d; ++lhs_d) {\n          const auto& expected_result =\n              expected_L_abcd.get(lhs_a, lhs_b, lhs_c, lhs_d);\n\n          CHECK(L_abcd.get(lhs_a, lhs_b, lhs_c, lhs_d) == expected_result);\n          CHECK(L_abdc.get(lhs_a, lhs_b, lhs_d, lhs_c) == expected_result);\n          CHECK(L_acbd.get(lhs_a, lhs_c, lhs_b, lhs_d) == expected_result);\n          CHECK(L_acdb.get(lhs_a, lhs_c, lhs_d, lhs_b) == expected_result);\n          CHECK(L_adbc.get(lhs_a, lhs_d, lhs_b, lhs_c) == expected_result);\n          CHECK(L_adcb.get(lhs_a, lhs_d, lhs_c, lhs_b) == expected_result);\n          CHECK(L_bacd.get(lhs_b, lhs_a, lhs_c, lhs_d) == expected_result);\n          CHECK(L_badc.get(lhs_b, lhs_a, lhs_d, lhs_c) == expected_result);\n          CHECK(L_bcad.get(lhs_b, lhs_c, lhs_a, lhs_d) == expected_result);\n          CHECK(L_bcda.get(lhs_b, lhs_c, lhs_d, lhs_a) == expected_result);\n          CHECK(L_bdac.get(lhs_b, lhs_d, lhs_a, lhs_c) == expected_result);\n          CHECK(L_bdca.get(lhs_b, lhs_d, lhs_c, lhs_a) == expected_result);\n          CHECK(L_cabd.get(lhs_c, lhs_a, lhs_b, lhs_d) == expected_result);\n          CHECK(L_cadb.get(lhs_c, lhs_a, lhs_d, lhs_b) == expected_result);\n          CHECK(L_cbad.get(lhs_c, lhs_b, lhs_a, lhs_d) == expected_result);\n          CHECK(L_cbda.get(lhs_c, lhs_b, lhs_d, lhs_a) == expected_result);\n          CHECK(L_cdab.get(lhs_c, lhs_d, lhs_a, lhs_b) == expected_result);\n          CHECK(L_cdba.get(lhs_c, lhs_d, lhs_b, lhs_a) == expected_result);\n          CHECK(L_dabc.get(lhs_d, lhs_a, lhs_b, lhs_c) == expected_result);\n          CHECK(L_dacb.get(lhs_d, lhs_a, lhs_c, lhs_b) == expected_result);\n          CHECK(L_dbac.get(lhs_d, lhs_b, lhs_a, lhs_c) == expected_result);\n          CHECK(L_dbca.get(lhs_d, lhs_b, lhs_c, lhs_a) == expected_result);\n          CHECK(L_dcab.get(lhs_d, lhs_c, lhs_a, lhs_b) == expected_result);\n          CHECK(L_dcba.get(lhs_d, lhs_c, lhs_b, lhs_a) == expected_result);\n        }\n      }\n    }\n  }\n}\n\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank0.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n\nnamespace TestHelpers::tenex {\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that the transformation between two rank 0 tensors' generic\n/// indices and the subsequent transformed multi-index is correctly computed\n///\n/// \\details The functions tested are:\n/// - `tenex::compute_tensorindex_transformation`\n/// - `tenex::transform_multi_index`\ninline void test_tensor_index_transformation_rank_0() {\n  const std::array<size_t, 0> index_order = {};\n\n  const std::array<size_t, 0> actual_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order, index_order);\n  const std::array<size_t, 0> expected_transformation = {};\n  CHECK(actual_transformation == expected_transformation);\n\n  const std::array<size_t, 0> tensor_multi_index = {};\n  CHECK(::tenex::transform_multi_index(\n            tensor_multi_index, expected_transformation) == tensor_multi_index);\n}\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank1.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <numeric>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n\nnamespace TestHelpers::tenex {\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that the transformation between two rank 1 tensors' generic\n/// indices and the subsequent transformed multi-indices are correctly computed\n///\n/// \\details The functions tested are:\n/// - `tenex::compute_tensorindex_transformation`\n/// - `tenex::transform_multi_index`\n///\n/// \\tparam TensorIndex the first generic tensor index, e.g. type of `ti::a`\ntemplate <typename TensorIndex>\nvoid test_tensor_index_transformation_rank_1(\n    const TensorIndex& /*tensorindex*/) {\n  const size_t dim = 3;\n\n  const std::array<size_t, 1> index_order = {TensorIndex::value};\n\n  const std::array<size_t, 1> actual_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order, index_order);\n  const std::array<size_t, 1> expected_transformation = {0};\n  CHECK(actual_transformation == expected_transformation);\n\n  // For L_a = R_a, check that L_i == R_i\n  for (size_t i = 0; i < dim; i++) {\n    const std::array<size_t, 1> tensor_multi_index = {i};\n    CHECK(::tenex::transform_multi_index(tensor_multi_index,\n                                         expected_transformation) ==\n          tensor_multi_index);\n  }\n}\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank2.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <numeric>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n\nnamespace TestHelpers::tenex {\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that the transformation between two rank 2 tensors' generic\n/// indices and the subsequent transformed multi-indices are correctly computed\n///\n/// \\details The functions tested are:\n/// - `tenex::compute_tensorindex_transformation`\n/// - `tenex::transform_multi_index`\n///\n/// If we consider the first tensor's generic indices to be (a, b), the possible\n/// orderings of the second tensor's generic indices are: (a, b) and (b, a). For\n/// each of these cases, this test checks that for each multi-index with the\n/// first generic index ordering, the equivalent multi-index with the second\n/// ordering is correctly computed.\n///\n/// \\tparam TensorIndexA the first generic tensor index, e.g. type of `ti::a`\n/// \\tparam TensorIndexB the second generic tensor index, e.g. type of `ti::B`\ntemplate <typename TensorIndexA, typename TensorIndexB>\nvoid test_tensor_index_transformation_rank_2(\n    const TensorIndexA& /*tensorindex_a*/,\n    const TensorIndexB& /*tensorindex_b*/) {\n  const size_t dim_a = 3;\n  const size_t dim_b = 4;\n\n  const std::array<size_t, 2> index_order_ab = {TensorIndexA::value,\n                                                TensorIndexB::value};\n  const std::array<size_t, 2> index_order_ba = {TensorIndexB::value,\n                                                TensorIndexA::value};\n\n  const std::array<size_t, 2> actual_ab_to_ab_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_ab,\n                                                  index_order_ab);\n  const std::array<size_t, 2> expected_ab_to_ab_transformation = {0, 1};\n  const std::array<size_t, 2> actual_ba_to_ab_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_ba,\n                                                  index_order_ab);\n  const std::array<size_t, 2> expected_ba_to_ab_transformation = {1, 0};\n\n  CHECK(actual_ab_to_ab_transformation == expected_ab_to_ab_transformation);\n  CHECK(actual_ba_to_ab_transformation == expected_ba_to_ab_transformation);\n\n  for (size_t i = 0; i < dim_a; i++) {\n    for (size_t j = 0; j < dim_b; j++) {\n      const std::array<size_t, 2> ij = {i, j};\n      const std::array<size_t, 2> ji = {j, i};\n\n      // For L_{ab} = R_{ab}, check that L_{ij} == R_{ij}\n      CHECK(::tenex::transform_multi_index(\n                ij, expected_ab_to_ab_transformation) == ij);\n      // For L_{ba} = R_{ab}, check that L_{ij} == R_{ji}\n      CHECK(::tenex::transform_multi_index(\n                ij, expected_ba_to_ab_transformation) == ji);\n    }\n  }\n}\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank3.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <numeric>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n\nnamespace TestHelpers::tenex {\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that the transformation between two rank 3 tensors' generic\n/// indices and the subsequent transformed multi-indices are correctly computed\n///\n/// \\details The functions tested are:\n/// - `tenex::compute_tensorindex_transformation`\n/// - `tenex::transform_multi_index`\n///\n/// If we consider the first tensor's generic indices to be (a, b, c), the\n/// possible orderings of the second tensor's generic indices are: (a, b, c),\n/// (a, c, b), (b, a, c), (b, c, a), (c, a, b), and (c, b, a). For each of\n/// these cases, this test checks that for each multi-index with the first\n/// generic index ordering, the equivalent multi-index with the second ordering\n/// is correctly computed.\n///\n/// \\tparam TensorIndexA the first generic tensor index, e.g. type of `ti::a`\n/// \\tparam TensorIndexB the second generic tensor index, e.g. type of `ti::B`\n/// \\tparam TensorIndexC the third generic tensor index, e.g. type of `ti::c`\ntemplate <typename TensorIndexA, typename TensorIndexB, typename TensorIndexC>\nvoid test_tensor_index_transformation_rank_3(\n    const TensorIndexA& /*tensorindex_a*/,\n    const TensorIndexB& /*tensorindex_b*/,\n    const TensorIndexC& /*tensorindex_c*/) {\n  const size_t dim_a = 4;\n  const size_t dim_b = 2;\n  const size_t dim_c = 3;\n\n  const std::array<size_t, 3> index_order_abc = {\n      TensorIndexA::value, TensorIndexB::value, TensorIndexC::value};\n  const std::array<size_t, 3> index_order_acb = {\n      TensorIndexA::value, TensorIndexC::value, TensorIndexB::value};\n  const std::array<size_t, 3> index_order_bac = {\n      TensorIndexB::value, TensorIndexA::value, TensorIndexC::value};\n  const std::array<size_t, 3> index_order_bca = {\n      TensorIndexB::value, TensorIndexC::value, TensorIndexA::value};\n  const std::array<size_t, 3> index_order_cab = {\n      TensorIndexC::value, TensorIndexA::value, TensorIndexB::value};\n  const std::array<size_t, 3> index_order_cba = {\n      TensorIndexC::value, TensorIndexB::value, TensorIndexA::value};\n\n  const std::array<size_t, 3> actual_abc_to_abc_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_abc,\n                                                  index_order_abc);\n  const std::array<size_t, 3> expected_abc_to_abc_transformation = {0, 1, 2};\n  const std::array<size_t, 3> actual_acb_to_abc_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_acb,\n                                                  index_order_abc);\n  const std::array<size_t, 3> expected_acb_to_abc_transformation = {0, 2, 1};\n  const std::array<size_t, 3> actual_bac_to_abc_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_bac,\n                                                  index_order_abc);\n  const std::array<size_t, 3> expected_bac_to_abc_transformation = {1, 0, 2};\n  const std::array<size_t, 3> actual_bca_to_abc_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_bca,\n                                                  index_order_abc);\n  const std::array<size_t, 3> expected_bca_to_abc_transformation = {2, 0, 1};\n  const std::array<size_t, 3> actual_cab_to_abc_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_cab,\n                                                  index_order_abc);\n  const std::array<size_t, 3> expected_cab_to_abc_transformation = {1, 2, 0};\n  const std::array<size_t, 3> actual_cba_to_abc_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_cba,\n                                                  index_order_abc);\n  const std::array<size_t, 3> expected_cba_to_abc_transformation = {2, 1, 0};\n\n  CHECK(actual_abc_to_abc_transformation == expected_abc_to_abc_transformation);\n  CHECK(actual_acb_to_abc_transformation == expected_acb_to_abc_transformation);\n  CHECK(actual_bac_to_abc_transformation == expected_bac_to_abc_transformation);\n  CHECK(actual_bca_to_abc_transformation == expected_bca_to_abc_transformation);\n  CHECK(actual_cab_to_abc_transformation == expected_cab_to_abc_transformation);\n  CHECK(actual_cba_to_abc_transformation == expected_cba_to_abc_transformation);\n\n  for (size_t i = 0; i < dim_a; i++) {\n    for (size_t j = 0; j < dim_b; j++) {\n      for (size_t k = 0; k < dim_c; k++) {\n        const std::array<size_t, 3> ijk = {i, j, k};\n        const std::array<size_t, 3> ikj = {i, k, j};\n        const std::array<size_t, 3> jik = {j, i, k};\n        const std::array<size_t, 3> jki = {j, k, i};\n        const std::array<size_t, 3> kij = {k, i, j};\n        const std::array<size_t, 3> kji = {k, j, i};\n\n        // For L_{abc} = R_{abc}, check that L_{ijk} == R_{ijk}\n        CHECK(::tenex::transform_multi_index(\n                  ijk, expected_abc_to_abc_transformation) == ijk);\n        // For L_{acb} = R_{abc}, check that L_{ijk} == R_{ikj}\n        CHECK(::tenex::transform_multi_index(\n                  ijk, expected_acb_to_abc_transformation) == ikj);\n        // For L_{bac} = R_{abc}, check that L_{ijk} == R_{jik}\n        CHECK(::tenex::transform_multi_index(\n                  ijk, expected_bac_to_abc_transformation) == jik);\n        // For L_{bca} = R_{abc}, check that L_{ijk} == R_{kij}\n        CHECK(::tenex::transform_multi_index(\n                  ijk, expected_bca_to_abc_transformation) == kij);\n        // For L_{cab} = R_{abc}, check that L_{ijk} == R_{jki}\n        CHECK(::tenex::transform_multi_index(\n                  ijk, expected_cab_to_abc_transformation) == jki);\n        // For L_{cba} = R_{abc}, check that L_{ijk} == R_{kji}\n        CHECK(::tenex::transform_multi_index(\n                  ijk, expected_cba_to_abc_transformation) == kji);\n      }\n    }\n  }\n}\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationRank4.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <numeric>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n\nnamespace TestHelpers::tenex {\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that the transformation between two rank 4 tensors' generic\n/// indices and the subsequent transformed multi-indices are correctly computed\n///\n/// \\details The functions tested are:\n/// - `tenex::compute_tensorindex_transformation`\n/// - `tenex::transform_multi_index`\n///\n/// If we consider the first tensor's generic indices to be (a, b, c, d), there\n/// are 24 permutations that are possible orderings of the second tensor's\n/// generic indices, such as: (a, b, c, d), (a, b, d, c), (a, c, b, d), etc. For\n/// each of these cases, this test checks that for each multi-index with the\n/// first generic index ordering, the equivalent multi-index with the second\n/// ordering is correctly computed.\n///\n/// \\tparam TensorIndexA the first generic tensor index, e.g. type of `ti::a`\n/// \\tparam TensorIndexB the second generic tensor index, e.g. type of `ti::B`\n/// \\tparam TensorIndexC the third generic tensor index, e.g. type of `ti::c`\n/// \\tparam TensorIndexD the fourth generic tensor index, e.g. type of `ti::D`\ntemplate <typename TensorIndexA, typename TensorIndexB, typename TensorIndexC,\n          typename TensorIndexD>\nvoid test_tensor_index_transformation_rank_4(\n    const TensorIndexA& /*tensorindex_a*/,\n    const TensorIndexB& /*tensorindex_b*/,\n    const TensorIndexC& /*tensorindex_c*/,\n    const TensorIndexD& /*tensorindex_d*/) {\n  const size_t dim_a = 4;\n  const size_t dim_b = 2;\n  const size_t dim_c = 3;\n  const size_t dim_d = 1;\n\n  const std::array<size_t, 4> index_order_abcd = {\n      TensorIndexA::value, TensorIndexB::value, TensorIndexC::value,\n      TensorIndexD::value};\n  const std::array<size_t, 4> index_order_abdc = {\n      TensorIndexA::value, TensorIndexB::value, TensorIndexD::value,\n      TensorIndexC::value};\n  const std::array<size_t, 4> index_order_acbd = {\n      TensorIndexA::value, TensorIndexC::value, TensorIndexB::value,\n      TensorIndexD::value};\n  const std::array<size_t, 4> index_order_acdb = {\n      TensorIndexA::value, TensorIndexC::value, TensorIndexD::value,\n      TensorIndexB::value};\n  const std::array<size_t, 4> index_order_adbc = {\n      TensorIndexA::value, TensorIndexD::value, TensorIndexB::value,\n      TensorIndexC::value};\n  const std::array<size_t, 4> index_order_adcb = {\n      TensorIndexA::value, TensorIndexD::value, TensorIndexC::value,\n      TensorIndexB::value};\n\n  const std::array<size_t, 4> index_order_bacd = {\n      TensorIndexB::value, TensorIndexA::value, TensorIndexC::value,\n      TensorIndexD::value};\n  const std::array<size_t, 4> index_order_badc = {\n      TensorIndexB::value, TensorIndexA::value, TensorIndexD::value,\n      TensorIndexC::value};\n  const std::array<size_t, 4> index_order_bcad = {\n      TensorIndexB::value, TensorIndexC::value, TensorIndexA::value,\n      TensorIndexD::value};\n  const std::array<size_t, 4> index_order_bcda = {\n      TensorIndexB::value, TensorIndexC::value, TensorIndexD::value,\n      TensorIndexA::value};\n  const std::array<size_t, 4> index_order_bdac = {\n      TensorIndexB::value, TensorIndexD::value, TensorIndexA::value,\n      TensorIndexC::value};\n  const std::array<size_t, 4> index_order_bdca = {\n      TensorIndexB::value, TensorIndexD::value, TensorIndexC::value,\n      TensorIndexA::value};\n\n  const std::array<size_t, 4> index_order_cabd = {\n      TensorIndexC::value, TensorIndexA::value, TensorIndexB::value,\n      TensorIndexD::value};\n  const std::array<size_t, 4> index_order_cadb = {\n      TensorIndexC::value, TensorIndexA::value, TensorIndexD::value,\n      TensorIndexB::value};\n  const std::array<size_t, 4> index_order_cbad = {\n      TensorIndexC::value, TensorIndexB::value, TensorIndexA::value,\n      TensorIndexD::value};\n  const std::array<size_t, 4> index_order_cbda = {\n      TensorIndexC::value, TensorIndexB::value, TensorIndexD::value,\n      TensorIndexA::value};\n  const std::array<size_t, 4> index_order_cdab = {\n      TensorIndexC::value, TensorIndexD::value, TensorIndexA::value,\n      TensorIndexB::value};\n  const std::array<size_t, 4> index_order_cdba = {\n      TensorIndexC::value, TensorIndexD::value, TensorIndexB::value,\n      TensorIndexA::value};\n\n  const std::array<size_t, 4> index_order_dabc = {\n      TensorIndexD::value, TensorIndexA::value, TensorIndexB::value,\n      TensorIndexC::value};\n  const std::array<size_t, 4> index_order_dacb = {\n      TensorIndexD::value, TensorIndexA::value, TensorIndexC::value,\n      TensorIndexB::value};\n  const std::array<size_t, 4> index_order_dbac = {\n      TensorIndexD::value, TensorIndexB::value, TensorIndexA::value,\n      TensorIndexC::value};\n  const std::array<size_t, 4> index_order_dbca = {\n      TensorIndexD::value, TensorIndexB::value, TensorIndexC::value,\n      TensorIndexA::value};\n  const std::array<size_t, 4> index_order_dcab = {\n      TensorIndexD::value, TensorIndexC::value, TensorIndexA::value,\n      TensorIndexB::value};\n  const std::array<size_t, 4> index_order_dcba = {\n      TensorIndexD::value, TensorIndexC::value, TensorIndexB::value,\n      TensorIndexA::value};\n\n  const std::array<size_t, 4> actual_abcd_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_abcd,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_abcd_to_abcd_transformation = {0, 1, 2,\n                                                                      3};\n  const std::array<size_t, 4> actual_abdc_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_abdc,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_abdc_to_abcd_transformation = {0, 1, 3,\n                                                                      2};\n  const std::array<size_t, 4> actual_acbd_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_acbd,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_acbd_to_abcd_transformation = {0, 2, 1,\n                                                                      3};\n  const std::array<size_t, 4> actual_acdb_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_acdb,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_acdb_to_abcd_transformation = {0, 3, 1,\n                                                                      2};\n  const std::array<size_t, 4> actual_adbc_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_adbc,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_adbc_to_abcd_transformation = {0, 2, 3,\n                                                                      1};\n  const std::array<size_t, 4> actual_adcb_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_adcb,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_adcb_to_abcd_transformation = {0, 3, 2,\n                                                                      1};\n\n  const std::array<size_t, 4> actual_bacd_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_bacd,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_bacd_to_abcd_transformation = {1, 0, 2,\n                                                                      3};\n  const std::array<size_t, 4> actual_badc_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_badc,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_badc_to_abcd_transformation = {1, 0, 3,\n                                                                      2};\n  const std::array<size_t, 4> actual_bcad_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_bcad,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_bcad_to_abcd_transformation = {2, 0, 1,\n                                                                      3};\n  const std::array<size_t, 4> actual_bcda_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_bcda,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_bcda_to_abcd_transformation = {3, 0, 1,\n                                                                      2};\n  const std::array<size_t, 4> actual_bdac_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_bdac,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_bdac_to_abcd_transformation = {2, 0, 3,\n                                                                      1};\n  const std::array<size_t, 4> actual_bdca_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_bdca,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_bdca_to_abcd_transformation = {3, 0, 2,\n                                                                      1};\n\n  const std::array<size_t, 4> actual_cabd_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_cabd,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_cabd_to_abcd_transformation = {1, 2, 0,\n                                                                      3};\n  const std::array<size_t, 4> actual_cadb_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_cadb,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_cadb_to_abcd_transformation = {1, 3, 0,\n                                                                      2};\n  const std::array<size_t, 4> actual_cbad_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_cbad,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_cbad_to_abcd_transformation = {2, 1, 0,\n                                                                      3};\n  const std::array<size_t, 4> actual_cbda_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_cbda,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_cbda_to_abcd_transformation = {3, 1, 0,\n                                                                      2};\n  const std::array<size_t, 4> actual_cdab_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_cdab,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_cdab_to_abcd_transformation = {2, 3, 0,\n                                                                      1};\n  const std::array<size_t, 4> actual_cdba_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_cdba,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_cdba_to_abcd_transformation = {3, 2, 0,\n                                                                      1};\n\n  const std::array<size_t, 4> actual_dabc_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_dabc,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_dabc_to_abcd_transformation = {1, 2, 3,\n                                                                      0};\n  const std::array<size_t, 4> actual_dacb_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_dacb,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_dacb_to_abcd_transformation = {1, 3, 2,\n                                                                      0};\n  const std::array<size_t, 4> actual_dbac_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_dbac,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_dbac_to_abcd_transformation = {2, 1, 3,\n                                                                      0};\n  const std::array<size_t, 4> actual_dbca_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_dbca,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_dbca_to_abcd_transformation = {3, 1, 2,\n                                                                      0};\n  const std::array<size_t, 4> actual_dcab_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_dcab,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_dcab_to_abcd_transformation = {2, 3, 1,\n                                                                      0};\n  const std::array<size_t, 4> actual_dcba_to_abcd_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_dcba,\n                                                  index_order_abcd);\n  const std::array<size_t, 4> expected_dcba_to_abcd_transformation = {3, 2, 1,\n                                                                      0};\n\n  CHECK(actual_abcd_to_abcd_transformation ==\n        expected_abcd_to_abcd_transformation);\n  CHECK(actual_abdc_to_abcd_transformation ==\n        expected_abdc_to_abcd_transformation);\n  CHECK(actual_acbd_to_abcd_transformation ==\n        expected_acbd_to_abcd_transformation);\n  CHECK(actual_acdb_to_abcd_transformation ==\n        expected_acdb_to_abcd_transformation);\n  CHECK(actual_adbc_to_abcd_transformation ==\n        expected_adbc_to_abcd_transformation);\n  CHECK(actual_adcb_to_abcd_transformation ==\n        expected_adcb_to_abcd_transformation);\n\n  CHECK(actual_bacd_to_abcd_transformation ==\n        expected_bacd_to_abcd_transformation);\n  CHECK(actual_badc_to_abcd_transformation ==\n        expected_badc_to_abcd_transformation);\n  CHECK(actual_bcad_to_abcd_transformation ==\n        expected_bcad_to_abcd_transformation);\n  CHECK(actual_bcda_to_abcd_transformation ==\n        expected_bcda_to_abcd_transformation);\n  CHECK(actual_bdac_to_abcd_transformation ==\n        expected_bdac_to_abcd_transformation);\n  CHECK(actual_bdca_to_abcd_transformation ==\n        expected_bdca_to_abcd_transformation);\n\n  CHECK(actual_cabd_to_abcd_transformation ==\n        expected_cabd_to_abcd_transformation);\n  CHECK(actual_cadb_to_abcd_transformation ==\n        expected_cadb_to_abcd_transformation);\n  CHECK(actual_cbad_to_abcd_transformation ==\n        expected_cbad_to_abcd_transformation);\n  CHECK(actual_cbda_to_abcd_transformation ==\n        expected_cbda_to_abcd_transformation);\n  CHECK(actual_cdab_to_abcd_transformation ==\n        expected_cdab_to_abcd_transformation);\n  CHECK(actual_cdba_to_abcd_transformation ==\n        expected_cdba_to_abcd_transformation);\n\n  CHECK(actual_dabc_to_abcd_transformation ==\n        expected_dabc_to_abcd_transformation);\n  CHECK(actual_dacb_to_abcd_transformation ==\n        expected_dacb_to_abcd_transformation);\n  CHECK(actual_dbac_to_abcd_transformation ==\n        expected_dbac_to_abcd_transformation);\n  CHECK(actual_dbca_to_abcd_transformation ==\n        expected_dbca_to_abcd_transformation);\n  CHECK(actual_dcab_to_abcd_transformation ==\n        expected_dcab_to_abcd_transformation);\n  CHECK(actual_dcba_to_abcd_transformation ==\n        expected_dcba_to_abcd_transformation);\n\n  for (size_t i = 0; i < dim_a; i++) {\n    for (size_t j = 0; j < dim_b; j++) {\n      for (size_t k = 0; k < dim_c; k++) {\n        for (size_t l = 0; l < dim_d; l++) {\n          const std::array<size_t, 4> ijkl = {i, j, k, l};\n          const std::array<size_t, 4> ijlk = {i, j, l, k};\n          const std::array<size_t, 4> ikjl = {i, k, j, l};\n          const std::array<size_t, 4> iklj = {i, k, l, j};\n          const std::array<size_t, 4> iljk = {i, l, j, k};\n          const std::array<size_t, 4> ilkj = {i, l, k, j};\n\n          const std::array<size_t, 4> jikl = {j, i, k, l};\n          const std::array<size_t, 4> jilk = {j, i, l, k};\n          const std::array<size_t, 4> jkil = {j, k, i, l};\n          const std::array<size_t, 4> jkli = {j, k, l, i};\n          const std::array<size_t, 4> jlik = {j, l, i, k};\n          const std::array<size_t, 4> jlki = {j, l, k, i};\n\n          const std::array<size_t, 4> kijl = {k, i, j, l};\n          const std::array<size_t, 4> kilj = {k, i, l, j};\n          const std::array<size_t, 4> kjil = {k, j, i, l};\n          const std::array<size_t, 4> kjli = {k, j, l, i};\n          const std::array<size_t, 4> klij = {k, l, i, j};\n          const std::array<size_t, 4> klji = {k, l, j, i};\n\n          const std::array<size_t, 4> lijk = {l, i, j, k};\n          const std::array<size_t, 4> likj = {l, i, k, j};\n          const std::array<size_t, 4> ljik = {l, j, i, k};\n          const std::array<size_t, 4> ljki = {l, j, k, i};\n          const std::array<size_t, 4> lkij = {l, k, i, j};\n          const std::array<size_t, 4> lkji = {l, k, j, i};\n\n          // For L_{abcd} = R_{abcd}, check that L_{ijkl} == R_{ijkl}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_abcd_to_abcd_transformation) == ijkl);\n          // For L_{abdc} = R_{abcd}, check that L_{ijkl} == R_{ijlk}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_abdc_to_abcd_transformation) == ijlk);\n          // For L_{acbd} = R_{abcd}, check that L_{ijkl} == R_{ikjl}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_acbd_to_abcd_transformation) == ikjl);\n          // For L_{acdb} = R_{abcd}, check that L_{ijkl} == R_{iljk}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_acdb_to_abcd_transformation) == iljk);\n          // For L_{adbc} = R_{abcd}, check that L_{ijkl} == R_{iklj}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_adbc_to_abcd_transformation) == iklj);\n          // For L_{adcb} = R_{abcd}, check that L_{ijkl} == R_{ilkj}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_adcb_to_abcd_transformation) == ilkj);\n\n          // For L_{bacd} = R_{abcd}, check that L_{ijkl} == R_{jikl}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_bacd_to_abcd_transformation) == jikl);\n          // For L_{badc} = R_{abcd}, check that L_{ijkl} == R_{jilk}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_badc_to_abcd_transformation) == jilk);\n          // For L_{bcad} = R_{abcd}, check that L_{ijkl} == R_{kijl}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_bcad_to_abcd_transformation) == kijl);\n          // For L_{bcda} = R_{abcd}, check that L_{ijkl} == R_{lijk}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_bcda_to_abcd_transformation) == lijk);\n          // For L_{bdac} = R_{abcd}, check that L_{ijkl} == R_{kilj}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_bdac_to_abcd_transformation) == kilj);\n          // For L_{bdca} = R_{abcd}, check that L_{ijkl} == R_{likj}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_bdca_to_abcd_transformation) == likj);\n\n          // For L_{cabd} = R_{abcd}, check that L_{ijkl} == R_{jkil}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_cabd_to_abcd_transformation) == jkil);\n          // For L_{cadb} = R_{abcd}, check that L_{ijkl} == R_{jlik}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_cadb_to_abcd_transformation) == jlik);\n          // For L_{cbad} = R_{abcd}, check that L_{ijkl} == R_{kjil}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_cbad_to_abcd_transformation) == kjil);\n          // For L_{cbda} = R_{abcd}, check that L_{ijkl} == R_{ljik}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_cbda_to_abcd_transformation) == ljik);\n          // For L_{cdab} = R_{abcd}, check that L_{ijkl} == R_{klij}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_cdab_to_abcd_transformation) == klij);\n          // For L_{cdba} = R_{abcd}, check that L_{ijkl} == R_{lkij}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_cdba_to_abcd_transformation) == lkij);\n\n          // For L_{dabc} = R_{abcd}, check that L_{ijkl} == R_{jkli}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_dabc_to_abcd_transformation) == jkli);\n          // For L_{dacb} = R_{abcd}, check that L_{ijkl} == R_{jlki}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_dacb_to_abcd_transformation) == jlki);\n          // For L_{dbac} = R_{abcd}, check that L_{ijkl} == R_{kjli}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_dbac_to_abcd_transformation) == kjli);\n          // For L_{dbca} = R_{abcd}, check that L_{ijkl} == R_{ljki}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_dbca_to_abcd_transformation) == ljki);\n          // For L_{dcab} = R_{abcd}, check that L_{ijkl} == R_{klji}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_dcab_to_abcd_transformation) == klji);\n          // For L_{dcba} = R_{abcd}, check that L_{ijkl} == R_{lkji}\n          CHECK(::tenex::transform_multi_index(\n                    ijkl, expected_dcba_to_abcd_transformation) == lkji);\n        }\n      }\n    }\n  }\n}\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/TensorIndexTransformationTimeIndex.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Expressions/TensorIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/TensorIndexTransformation.hpp\"\n\nnamespace TestHelpers::tenex {\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that the transformation between two tensors generic\n/// indices and the subsequent transformed multi-indices are correctly computed\n/// when time indices are used with at least one of the tensors\n///\n/// \\details The functions tested are:\n/// - `tenex::compute_tensorindex_transformation`\n/// - `tenex::transform_multi_index`\ninline void test_tensor_index_transformation_with_time_indices() {\n  const std::array<size_t, 0> index_order_empty = {};\n  const std::array<size_t, 1> index_order_t = {ti::t.value};\n  const std::array<size_t, 3> index_order_atb = {ti::a.value, ti::t.value,\n                                                 ti::b.value};\n  const std::array<size_t, 3> index_order_Ttt = {ti::T.value, ti::t.value,\n                                                 ti::t.value};\n  const std::array<size_t, 5> index_order_tbatT = {\n      ti::t.value, ti::b.value, ti::a.value, ti::t.value, ti::T.value};\n\n  const size_t time_index_placeholder = ::tenex::\n      TensorIndexTransformation_detail::time_index_position_placeholder;\n\n  // Transform multi-index for rank 0 tensor to multi-index for rank 1 tensor\n  // with time index\n  // e.g. transform multi-index for L to multi-index for R_{t}\n  const std::array<size_t, 1> actual_empty_to_t_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_empty,\n                                                  index_order_t);\n  const std::array<size_t, 1> expected_empty_to_t_transformation = {\n      time_index_placeholder};\n  CHECK(actual_empty_to_t_transformation == expected_empty_to_t_transformation);\n  const std::array<size_t, 0> multi_index_empty = {};\n  const std::array<size_t, 1> multi_index_t = {0};\n  CHECK(::tenex::transform_multi_index(multi_index_empty,\n                                       expected_empty_to_t_transformation) ==\n        multi_index_t);\n\n  // Transform multi-index for rank 1 tensor with time index to multi-index for\n  // rank 0 tensor\n  // e.g. transform multi-index for L_{t} to multi-index for R\n  const std::array<size_t, 0> actual_t_to_empty_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_t,\n                                                  index_order_empty);\n  const std::array<size_t, 0> expected_t_to_empty_transformation = {};\n  CHECK(actual_t_to_empty_transformation == expected_t_to_empty_transformation);\n  CHECK(::tenex::transform_multi_index(multi_index_t,\n                                       expected_t_to_empty_transformation) ==\n        multi_index_empty);\n\n  // Transform multi-index for rank 0 tensor to multi-index for rank 3 tensor\n  // with three time indices\n  // e.g. transform multi-index for L to multi-index for R^{t}{}_{tt}\n  const std::array<size_t, 3> actual_empty_to_Ttt_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_empty,\n                                                  index_order_Ttt);\n  const std::array<size_t, 3> expected_empty_to_Ttt_transformation = {\n      time_index_placeholder, time_index_placeholder, time_index_placeholder};\n  CHECK(actual_empty_to_Ttt_transformation ==\n        expected_empty_to_Ttt_transformation);\n  const std::array<size_t, 3> multi_index_Ttt = {0, 0, 0};\n  CHECK(::tenex::transform_multi_index(multi_index_empty,\n                                       expected_empty_to_Ttt_transformation) ==\n        multi_index_Ttt);\n\n  // Transform multi-index for rank 3 tensor with three time indices to\n  // multi-index for rank 0 tensor\n  // e.g. transform multi-index for L^{t}{}_{tt} to multi-index for R\n  const std::array<size_t, 0> actual_Ttt_to_empty_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_Ttt,\n                                                  index_order_empty);\n  const std::array<size_t, 0> expected_Ttt_to_empty_transformation = {};\n  CHECK(actual_Ttt_to_empty_transformation ==\n        expected_Ttt_to_empty_transformation);\n  CHECK(::tenex::transform_multi_index(multi_index_Ttt,\n                                       expected_Ttt_to_empty_transformation) ==\n        multi_index_empty);\n\n  // Transform multi-indices for rank 3 tensor with one time index to\n  // multi-indices for rank 5 tensor with three time indices\n  // e.g. transform multi-indices for L_{atb} to multi-indices for\n  // R_{tbat}{}^{t}\n  //\n  // Note: While SpECTRE Tensors only support up to rank 4, TensorExpressions\n  // may represent higher rank intermediate expressions in an equation. In\n  // addition, this case means to get at testing having a different number of\n  // time indices on either side in addition to a different relative index order\n  // for generic indices (i.e. a and b)\n  const std::array<size_t, 5> actual_atb_to_tbatT_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_atb,\n                                                  index_order_tbatT);\n  const std::array<size_t, 5> expected_atb_to_tbatT_transformation = {\n      time_index_placeholder, 2, 0, time_index_placeholder,\n      time_index_placeholder};\n  CHECK(actual_atb_to_tbatT_transformation ==\n        expected_atb_to_tbatT_transformation);\n\n  for (size_t a = 0; a < 4; a++) {\n    for (size_t b = 0; b < 4; b++) {\n      const std::array<size_t, 3> multi_index_atb = {a, 0, b};\n      const std::array<size_t, 5> multi_index_tbatT = {0, b, a, 0, 0};\n      CHECK(::tenex::transform_multi_index(\n                multi_index_atb, expected_atb_to_tbatT_transformation) ==\n            multi_index_tbatT);\n    }\n  }\n\n  // Transform multi-indices for rank 5 tensor with three time indices to\n  // multi-indices for rank 3 tensor with one time index time index\n  // e.g. transform multi-indices for L_{tbat}{}^{t} to multi-indices for\n  // R_{atb}\n  const std::array<size_t, 3> actual_tbatT_to_atb_transformation =\n      ::tenex::compute_tensorindex_transformation(index_order_tbatT,\n                                                  index_order_atb);\n  const std::array<size_t, 3> expected_tbatT_to_atb_transformation = {\n      2, time_index_placeholder, 1};\n  CHECK(actual_tbatT_to_atb_transformation ==\n        expected_tbatT_to_atb_transformation);\n\n  for (size_t b = 0; b < 4; b++) {\n    for (size_t a = 0; a < 4; a++) {\n      const std::array<size_t, 5> multi_index_tbatT = {0, b, a, 0, 0};\n      const std::array<size_t, 3> multi_index_atb = {a, 0, b};\n      CHECK(::tenex::transform_multi_index(\n                multi_index_tbatT, expected_tbatT_to_atb_transformation) ==\n            multi_index_atb);\n    }\n  }\n}\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/Tensor/Expressions/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\ingroup TestingFrameworkGroup\n/// Functions for testing `TensorExpression`s\nnamespace TestHelpers::tenex {\n// Helper that simply calls `tenex::evaluate`\ntemplate <bool ReturnLhsTensor, auto&... LhsTensorIndices, typename LhsTensor,\n          typename RhsExpression>\nvoid call_evaluate(const gsl::not_null<LhsTensor*> lhs_tensor,\n                   const RhsExpression& rhs_expression) {\n  if constexpr (ReturnLhsTensor) {\n    *lhs_tensor = ::tenex::evaluate<LhsTensorIndices...>(rhs_expression);\n  } else {\n    ::tenex::evaluate<LhsTensorIndices...>(lhs_tensor, rhs_expression);\n  }\n}\n\n// Returns the subset of index positions of an `Index` that a `TensorIndex`\n// refers to given the kind of index (e.g. spatial, spacetime, time) that\n// `Index` and `TensorIndex` each are\n//\n// - spatial `Index` and spatial `TensorIndex`: [0, Index::Dim - 1]\n// - spacetime `Index` and spacetime `TensorIndex`: [0, Index::Dim - 1]\n// - spacetime `Index` and spatial `TensorIndex`: [1, Index::Dim - 1]\n// - spacetime `Index` and concrete time `TensorIndex`: [0, 0]\ntemplate <typename Index, auto& TensorIndex>\nconstexpr std::pair<size_t, size_t> get_index_value_range() {\n  constexpr bool tensorindex_is_time =\n      ::tenex::detail::is_time_index_value(TensorIndex.value);\n  static_assert(\n      not(Index::index_type == IndexType::Spatial and tensorindex_is_time),\n      \"Cannot use a concrete time TensorIndex with a SpatialIndex.\");\n  std::pair<size_t, size_t> range{};\n  range.first =\n      Index::index_type == IndexType::Spacetime and not TensorIndex.is_spacetime\n          ? 1\n          : 0;\n  range.second = tensorindex_is_time ? 0 : Index::dim - 1;\n  return range;\n}\n}  // namespace TestHelpers::tenex\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/TestTags.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace TestHelpers::Tags {\n/// [simple_variables_tag]\ntemplate <typename VectorType = DataVector>\nstruct Vector : ::db::SimpleTag {\n  using type = tnsr::I<VectorType, 3>;\n};\n/// [simple_variables_tag]\n\ntemplate <typename VectorType = DataVector>\nstruct OneForm : ::db::SimpleTag {\n  using type = tnsr::i<VectorType, 3>;\n};\n\ntemplate <typename VectorType = DataVector>\nstruct Scalar : ::db::SimpleTag {\n  using type = ::Scalar<VectorType>;\n};\n\ntemplate <typename VectorType = DataVector>\nstruct Scalar2 : ::db::SimpleTag {\n  using type = ::Scalar<VectorType>;\n};\n\ntemplate <typename VectorType = DataVector>\nstruct DerivOfVector : ::db::SimpleTag {\n  using type = tnsr::iJ<VectorType, 3>;\n};\n\n/// [prefix_variables_tag]\ntemplate <class Tag>\nstruct Prefix0 : ::db::PrefixTag, ::db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n/// [prefix_variables_tag]\n\ntemplate <class Tag>\nstruct Prefix1 : ::db::PrefixTag, ::db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\ntemplate <class Tag>\nstruct Prefix2 : ::db::PrefixTag, ::db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\ntemplate <class Tag>\nstruct Prefix3 : ::db::PrefixTag, ::db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n}  // namespace TestHelpers::Tags\n"
  },
  {
    "path": "tests/Unit/Helpers/DataStructures/VectorImplTestHelper.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <map>\n#include <memory>\n#include <random>\n#include <sstream>\n#include <tuple>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/DereferenceWrapper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/Tuple.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n#include \"Utilities/TypeTraits/IsComplexOfFundamental.hpp\"\n\nnamespace TestHelpers {\nnamespace VectorImpl {\nnamespace detail {\ntemplate <typename T, typename... Ts>\nbool check_ownership_ok_impl(const size_t checked, const T& t,\n                             const Ts&... ts) {\n  if (checked < sizeof...(ts) + 1) {\n    bool result = check_ownership_ok_impl(checked + 1, ts..., t);\n    if (t.is_owning()) {\n      result =\n          result and (... and (not ts.is_owning() or t.data() != ts.data()));\n    } else {\n      result = result and (... or (ts.is_owning() and t.data() == ts.data()));\n    }\n    return result;\n  }\n  return true;\n}\n\ntemplate <typename T, typename... Ts>\nbool check_ownership_ok(const T& t, const Ts&... ts) {\n  std::stringstream vector_info{};\n  vector_info << t.is_owning() << \" \" << t.size() << \" \" << t.data() << \"\\n\";\n  (vector_info << ...\n               << (MakeString{} << ts.is_owning() << \" \" << ts.size() << \" \"\n                                << ts.data() << \"\\n\"));\n  INFO(vector_info.str());\n  const bool result = check_ownership_ok_impl(0, t, ts...);\n  CHECK(result);\n  return result;\n}\n\ntemplate <typename VectorType>\nvoid test_unowning_construct_and_assign() {\n  auto make_vector = [counter = 0]() mutable {  // NOLINT(spectre-mutable)\n    return VectorType(VectorType::static_size + 1, ++counter);\n  };\n\n  struct VectorState {\n    VectorState(const VectorType& v)\n        : owning_(v.is_owning()),\n          data_(v.data()),\n          size_(v.size()),\n          value_(v[0]) {}\n\n    bool check_is_same(const VectorType& v) const {\n      CHECK(owning_ == v.is_owning());\n      CHECK(data_ == v.data());\n      CHECK(size_ == v.size());\n      CHECK(value_ == v[0]);\n      return owning_ == v.is_owning() and data_ == v.data() and\n             size_ == v.size() and value_ == v[0];\n    }\n\n    bool check_is_same_except_value(const VectorType& v) const {\n      CHECK(owning_ == v.is_owning());\n      CHECK(data_ == v.data());\n      CHECK(size_ == v.size());\n      CHECK(value_ != v[0]);\n      return owning_ == v.is_owning() and data_ == v.data() and\n             size_ == v.size() and value_ != v[0];\n    }\n\n   private:\n    bool owning_;\n    const typename VectorType::value_type* data_;\n    size_t size_;\n    typename VectorType::value_type value_;\n  };\n\n  {\n    INFO(\"copy construct from owning\");\n    VectorType v1 = make_vector();\n    const VectorState v1_state(v1);\n    VectorType v2 = v1;\n    CHECK(v1_state.check_is_same(v1));\n    CHECK(v2.is_owning());\n    CHECK(v2.data() != v1.data());\n    CHECK(v2 == v1);\n    CHECK(check_ownership_ok(v1, v2));\n  }\n\n  {\n    INFO(\"copy assign owning -> owning\");\n    VectorType v1 = make_vector();\n    VectorType v2 = make_vector();\n    const VectorState v1_state(v1);\n    v2 = v1;\n    CHECK(v1_state.check_is_same(v1));\n    CHECK(v2.is_owning());\n    CHECK(v2.data() != v1.data());\n    CHECK(v2 == v1);\n    CHECK(check_ownership_ok(v1, v2));\n  }\n\n  {\n    INFO(\"move construct from owning\");\n    VectorType v1 = make_vector();\n    const VectorState v1_state(v1);\n    VectorType v2 = std::move(v1);\n    CHECK(v1_state.check_is_same(v2));\n    CHECK(v1.is_owning());\n    CHECK(v1.size() == 0);\n    CHECK(check_ownership_ok(v1, v2));\n  }\n\n  {\n    INFO(\"move assign owning -> owning\");\n    VectorType v1 = make_vector();\n    VectorType v2 = make_vector();\n    const VectorState v1_state(v1);\n    v2 = std::move(v1);\n    CHECK(v1_state.check_is_same(v2));\n    CHECK(v1.is_owning());\n    CHECK(v1.size() == 0);\n    CHECK(check_ownership_ok(v1, v2));\n  }\n\n  {\n    INFO(\"copy construct from non-owning\");\n    VectorType v1 = make_vector();\n    VectorType v1_ref(v1.data(), v1.size());\n    const VectorState v1_state(v1);\n    const VectorState v1_ref_state(v1_ref);\n    VectorType v2 = v1_ref;\n    CHECK(v1_state.check_is_same(v1));\n    CHECK(v1_ref_state.check_is_same(v1_ref));\n    CHECK(v2.is_owning());\n    CHECK(v2.data() != v1.data());\n    CHECK(v2 == v1);\n    CHECK(check_ownership_ok(v1, v2, v1_ref));\n  }\n\n  {\n    INFO(\"copy assign non-owning -> owning\");\n    VectorType v1 = make_vector();\n    VectorType v2 = make_vector();\n    VectorType v1_ref(v1.data(), v1.size());\n    const VectorState v1_state(v1);\n    const VectorState v1_ref_state(v1_ref);\n    v2 = v1;\n    CHECK(v1_state.check_is_same(v1));\n    CHECK(v1_ref_state.check_is_same(v1_ref));\n    CHECK(v2.is_owning());\n    CHECK(v2.data() != v1.data());\n    CHECK(v2 == v1);\n    CHECK(check_ownership_ok(v1, v2, v1_ref));\n  }\n\n  {\n    INFO(\"move construct from non-owning\");\n    VectorType v1 = make_vector();\n    VectorType v1_ref(v1.data(), v1.size());\n    const VectorState v1_state(v1);\n    const VectorState v1_ref_state(v1_ref);\n    VectorType v2 = std::move(v1_ref);\n    CHECK(v1_state.check_is_same(v1));\n    CHECK(v1_ref_state.check_is_same(v2));\n    CHECK(v1_ref.is_owning());\n    CHECK(v1_ref.size() == 0);\n    CHECK(check_ownership_ok(v1, v2, v1_ref));\n  }\n\n  {\n    INFO(\"move assign non-owning -> owning\");\n#ifdef SPECTRE_DEBUG\n    VectorType v1 = make_vector();\n    VectorType v1_ref(v1.data(), v1.size());\n    VectorType v2 = make_vector();\n    CHECK_THROWS_WITH(v2 = std::move(v1_ref),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Cannot move assign from a non-owning vector\"));\n#endif  // SPECTRE_DEBUG\n  }\n\n  {\n    INFO(\"copy assign owning -> non-owning\");\n    VectorType v1 = make_vector();\n    VectorType v2 = make_vector();\n    VectorType v2_ref(v2.data(), v2.size());\n    const VectorState v1_state(v1);\n    const VectorState v2_state(v2);\n    const VectorState v2_ref_state(v2_ref);\n    v2_ref = v1;\n    CHECK(v1_state.check_is_same(v1));\n    CHECK(v2_state.check_is_same_except_value(v2));\n    CHECK(v2_ref_state.check_is_same_except_value(v2_ref));\n    CHECK(v2 == v1);\n    CHECK(check_ownership_ok(v1, v2, v2_ref));\n  }\n\n  {\n    INFO(\"copy assign non-owning -> non-owning\");\n    VectorType v1 = make_vector();\n    VectorType v2 = make_vector();\n    VectorType v1_ref(v1.data(), v1.size());\n    VectorType v2_ref(v2.data(), v2.size());\n    const VectorState v1_state(v1);\n    const VectorState v2_state(v2);\n    const VectorState v1_ref_state(v1_ref);\n    const VectorState v2_ref_state(v2_ref);\n    v2_ref = v1_ref;\n    CHECK(v1_state.check_is_same(v1));\n    CHECK(v1_ref_state.check_is_same(v1_ref));\n    CHECK(v2_state.check_is_same_except_value(v2));\n    CHECK(v2_ref_state.check_is_same_except_value(v2_ref));\n    CHECK(v2 == v1);\n    CHECK(check_ownership_ok(v1, v2, v1_ref, v2_ref));\n  }\n\n  {\n    INFO(\"move assign owning -> non-owning\");\n    VectorType v1 = make_vector();\n    VectorType v2 = make_vector();\n    VectorType v2_ref(v2.data(), v2.size());\n    const VectorState v2_state(v2);\n    const VectorState v2_ref_state(v2_ref);\n    v2_ref = std::move(v1);\n    CHECK(v2_ref_state.check_is_same_except_value(v2_ref));\n    CHECK(v2_state.check_is_same_except_value(v2));\n    CHECK(v1.is_owning());\n    CHECK(v1.size() == 0);\n    CHECK(check_ownership_ok(v1, v2, v2_ref));\n  }\n\n  {\n    INFO(\"move assign non-owning -> non-owning\");\n#ifdef SPECTRE_DEBUG\n    VectorType v1 = make_vector();\n    VectorType v2 = make_vector();\n    VectorType v1_ref(v1.data(), v1.size());\n    VectorType v2_ref(v2.data(), v2.size());\n    CHECK_THROWS_WITH(v2_ref = std::move(v1_ref),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Cannot move assign from a non-owning vector\"));\n#endif  // SPECTRE_DEBUG\n  }\n\n  {\n    INFO(\"self copy assign owning -> non-owning\");\n    VectorType v = make_vector();\n    VectorType v_ref(v.data(), v.size());\n    const VectorState v_state(v);\n    const VectorState v_ref_state(v_ref);\n    v_ref = v;\n    CHECK(v_state.check_is_same(v));\n    CHECK(v_ref_state.check_is_same(v_ref));\n    CHECK(check_ownership_ok(v, v_ref));\n  }\n\n  {\n    INFO(\"self copy assign non-owning -> owning\");\n    VectorType v = make_vector();\n    VectorType v_ref(v.data(), v.size());\n    const VectorState v_state(v);\n    const VectorState v_ref_state(v_ref);\n    v = v_ref;\n    CHECK(v_state.check_is_same(v));\n    CHECK(v_ref_state.check_is_same(v_ref));\n    CHECK(check_ownership_ok(v, v_ref));\n  }\n\n  {\n    INFO(\"self copy assign non-owning -> non-owning\");\n    VectorType v = make_vector();\n    VectorType v_ref(v.data(), v.size());\n    VectorType v_ref2(v.data(), v.size());\n    const VectorState v_state(v);\n    const VectorState v_ref_state(v_ref);\n    const VectorState v_ref2_state(v_ref);\n    v_ref = v_ref2;\n    CHECK(v_state.check_is_same(v));\n    CHECK(v_ref_state.check_is_same(v_ref));\n    CHECK(v_ref2_state.check_is_same(v_ref2));\n    CHECK(check_ownership_ok(v, v_ref, v_ref2));\n  }\n\n  {\n    INFO(\"self move assign owning -> non-owning\");\n    // It's not entirely clear what the result of this should be, but\n    // v = std::move(v) is a no-op, so it seems reasonable for this to\n    // be too.\n    VectorType v = make_vector();\n    VectorType v_ref(v.data(), v.size());\n    const VectorState v_state(v);\n    const VectorState v_ref_state(v_ref);\n    v_ref = std::move(v);\n    CHECK(v_state.check_is_same(v));\n    CHECK(v_ref_state.check_is_same(v_ref));\n    CHECK(check_ownership_ok(v, v_ref));\n  }\n}\n}  // namespace detail\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief test construction and assignment of a `VectorType` with a `ValueType`\ntemplate <typename VectorType, typename ValueType>\nvoid vector_test_construct_and_assign(\n    tt::get_fundamental_type_t<ValueType> low =\n        tt::get_fundamental_type_t<ValueType>{-100.0},\n    tt::get_fundamental_type_t<ValueType> high =\n        tt::get_fundamental_type_t<ValueType>{100.0}) {\n  detail::test_unowning_construct_and_assign<VectorType>();\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<ValueType>> dist{low,\n                                                                        high};\n  std::vector<size_t> sizes;\n  if constexpr (VectorType::static_size >= 2) {\n    static_assert(VectorType::static_size < 19);\n    UniformCustomDistribution<size_t> static_sdist{2, VectorType::static_size};\n    UniformCustomDistribution<size_t> sdist{VectorType::static_size + 1, 20};\n    sizes = std::vector<size_t>{static_sdist(gen), VectorType::static_size,\n                                sdist(gen)};\n  } else {\n    UniformCustomDistribution<size_t> sdist{2, 20};\n    // Two for good measure\n    sizes = std::vector<size_t>{sdist(gen), sdist(gen)};\n  }\n\n  for (const size_t& size : sizes) {\n    CAPTURE(size);\n    const VectorType size_constructed{size};\n    CHECK(size_constructed.size() == size);\n    const auto generated_value1 = make_with_random_values<ValueType>(\n        make_not_null(&gen), make_not_null(&dist));\n\n    const VectorType value_size_constructed{size, generated_value1};\n    CHECK(value_size_constructed.size() == size);\n    alg::for_each(value_size_constructed,\n                  [generated_value1, &value_size_constructed](\n                      typename VectorType::value_type element) {\n                    CAPTURE(value_size_constructed);\n                    CHECK(element == generated_value1);\n                  });\n\n    // random generation must use `make_with_random_values`, because stored\n    // value in vector type might be a non-fundamental type.\n    const auto generated_value2 = make_with_random_values<ValueType>(\n        make_not_null(&gen), make_not_null(&dist));\n    const auto generated_value3 = make_with_random_values<ValueType>(\n        make_not_null(&gen), make_not_null(&dist));\n\n    VectorType initializer_list_constructed{\n        {static_cast<typename VectorType::value_type>(generated_value2),\n         static_cast<typename VectorType::value_type>(generated_value3)}};\n    CHECK(initializer_list_constructed.size() == 2);\n    CHECK(initializer_list_constructed.is_owning());\n    CHECK(gsl::at(initializer_list_constructed, 0) == generated_value2);\n    CHECK(gsl::at(initializer_list_constructed, 1) == generated_value3);\n\n    // construct from STL containers\n    const VectorType vector_constructed{\n        std::vector<typename VectorType::value_type>{\n            static_cast<typename VectorType::value_type>(generated_value2),\n            static_cast<typename VectorType::value_type>(generated_value3)}};\n    CHECK(vector_constructed.size() == 2);\n    CHECK(vector_constructed.is_owning());\n    CHECK(vector_constructed == initializer_list_constructed);\n    const VectorType array_constructed{\n        std::array<typename VectorType::value_type, 2>{\n            {static_cast<typename VectorType::value_type>(generated_value2),\n             static_cast<typename VectorType::value_type>(generated_value3)}}};\n    CHECK(array_constructed.size() == 2);\n    CHECK(array_constructed.is_owning());\n    CHECK(array_constructed == initializer_list_constructed);\n\n    // check equality operators do not perform approximate comparison\n    CHECK(SINGLE_ARG(\n        initializer_list_constructed ==\n        VectorType{\n            {static_cast<typename VectorType::value_type>(generated_value2),\n             static_cast<typename VectorType::value_type>(generated_value3)}}));\n    CHECK_FALSE(SINGLE_ARG(\n        initializer_list_constructed !=\n        VectorType{\n            {static_cast<typename VectorType::value_type>(generated_value2),\n             static_cast<typename VectorType::value_type>(generated_value3)}}));\n    const VectorType close = (1.0 + 1.0e-14) * initializer_list_constructed;\n    CHECK(initializer_list_constructed != close);\n    CHECK_FALSE(initializer_list_constructed == close);\n    CHECK_ITERABLE_APPROX(initializer_list_constructed, close);\n\n    CHECK(initializer_list_constructed !=\n          (initializer_list_constructed +\n           1.0e-11 * initializer_list_constructed));\n    CHECK_FALSE(initializer_list_constructed ==\n                (initializer_list_constructed +\n                 1.0e-11 * initializer_list_constructed));\n    CHECK(initializer_list_constructed !=\n          static_cast<typename VectorType::value_type>(generated_value2));\n    CHECK(static_cast<typename VectorType::value_type>(generated_value2) !=\n          initializer_list_constructed);\n    CHECK_FALSE(initializer_list_constructed ==\n                static_cast<typename VectorType::value_type>(generated_value2));\n    CHECK_FALSE(static_cast<typename VectorType::value_type>(\n                    generated_value2) == initializer_list_constructed);\n\n    CHECK((initializer_list_constructed -\n           1.0e-11 * initializer_list_constructed) !=\n          (initializer_list_constructed +\n           1.0e-11 * initializer_list_constructed));\n    CHECK_FALSE((initializer_list_constructed -\n                 1.0e-11 * initializer_list_constructed) ==\n                (initializer_list_constructed +\n                 1.0e-11 * initializer_list_constructed));\n\n    // NOLINTNEXTLINE(modernize-avoid-c-arrays)\n    typename VectorType::value_type raw_ptr[2] = {generated_value2,\n                                                  generated_value3};\n    const VectorType pointer_size_constructed{\n        static_cast<typename VectorType::value_type*>(raw_ptr), 2};\n    CHECK(initializer_list_constructed == pointer_size_constructed);\n    CHECK_FALSE(initializer_list_constructed != pointer_size_constructed);\n\n    test_copy_semantics(initializer_list_constructed);\n    auto initializer_list_constructed_copy = initializer_list_constructed;\n    CHECK(initializer_list_constructed_copy.is_owning());\n    CHECK(initializer_list_constructed_copy == pointer_size_constructed);\n    test_move_semantics(std::move(initializer_list_constructed),\n                        initializer_list_constructed_copy);\n\n    VectorType move_assignment_initialized;\n    move_assignment_initialized = std::move(initializer_list_constructed_copy);\n    CHECK(move_assignment_initialized.is_owning());\n\n    VectorType move_constructed{std::move(move_assignment_initialized)};\n    CHECK(move_constructed.is_owning());\n    CHECK(move_constructed == pointer_size_constructed);\n\n    // clang-tidy has performance complaints, and we're checking functionality\n    const VectorType copy_constructed{move_constructed};  // NOLINT\n    CHECK(copy_constructed.is_owning());\n    CHECK(copy_constructed == pointer_size_constructed);\n\n    // check the destructive resize utility\n    const VectorType destructive_resize_check_copy = move_constructed;\n    move_constructed.destructive_resize(move_constructed.size());\n    CHECK(move_constructed == destructive_resize_check_copy);\n    move_constructed.destructive_resize(move_constructed.size() + 1);\n    CHECK(move_constructed != destructive_resize_check_copy);\n    CHECK(move_constructed.size() == destructive_resize_check_copy.size() + 1);\n\n    move_constructed.clear();\n    CHECK(move_constructed == VectorType{});\n\n    {\n      VectorType source{size};\n      const auto* const source_data = source.data();\n      auto dest = std::move(source);\n      CHECK(contains_allocations(dest) == (source_data == dest.data()));\n      const VectorType reference(dest.data(), dest.size());\n      CHECK(not contains_allocations(reference));\n    }\n\n    CHECK(make_with_value<VectorType>(size, generated_value1) ==\n          VectorType(size, generated_value1));\n    CHECK(make_with_value<VectorType>(VectorType(size, ValueType{}),\n                                      generated_value1) ==\n          VectorType(size, generated_value1));\n\n    {\n      VectorType resize_from_size_t{};\n      set_number_of_grid_points(make_not_null(&resize_from_size_t), size);\n      CHECK(resize_from_size_t.size() == size);\n      VectorType resize_from_vector{};\n      set_number_of_grid_points(make_not_null(&resize_from_vector),\n                                VectorType(size, ValueType{}));\n      CHECK(resize_from_vector.size() == size);\n    }\n\n    // Check that clearing by copy-assignment works\n    {\n      VectorType vec(size, generated_value1);\n      const VectorType empty{};\n      vec = empty;\n      CHECK(vec.size() == 0);\n    }\n  }\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief test the serialization of a `VectorType` constructed with a\n/// `ValueType`\ntemplate <typename VectorType, typename ValueType>\nvoid vector_test_serialize(tt::get_fundamental_type_t<ValueType> low =\n                               tt::get_fundamental_type_t<ValueType>{-100.0},\n                           tt::get_fundamental_type_t<ValueType> high =\n                               tt::get_fundamental_type_t<ValueType>{100.0}) {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<ValueType>> dist{low,\n                                                                        high};\n  std::vector<size_t> sizes;\n  if constexpr (VectorType::static_size >= 2) {\n    static_assert(VectorType::static_size < 19);\n    UniformCustomDistribution<size_t> static_sdist{2, VectorType::static_size};\n    UniformCustomDistribution<size_t> sdist{VectorType::static_size + 1, 20};\n    sizes = std::vector<size_t>{static_sdist(gen), VectorType::static_size,\n                                sdist(gen)};\n  } else {\n    UniformCustomDistribution<size_t> sdist{2, 20};\n    // Two for good measure\n    sizes = std::vector<size_t>{sdist(gen), sdist(gen)};\n  }\n\n  for (const size_t& size : sizes) {\n    CAPTURE(size);\n    VectorType vector_test{size};\n    VectorType vector_control{size};\n    VectorType vector_ref;\n    const auto start_value = make_with_random_values<ValueType>(\n        make_not_null(&gen), make_not_null(&dist));\n    const auto value_difference = make_with_random_values<ValueType>(\n        make_not_null(&gen), make_not_null(&dist));\n    // generate_series is used to generate a pair of equivalent, but\n    // independently constructed, data sets to fill the vectors with.\n    ValueType current_value = start_value;\n    const auto generate_series = [&current_value, value_difference]() {\n      return current_value += value_difference;\n    };\n    std::generate(vector_test.begin(), vector_test.end(), generate_series);\n    current_value = start_value;\n    std::generate(vector_control.begin(), vector_control.end(),\n                  generate_series);\n    // checks the vectors have been constructed as expected\n    CHECK(vector_control == vector_test);\n    CHECK(vector_test.is_owning());\n    CHECK(vector_control.is_owning());\n    const VectorType serialized_vector_test =\n        serialize_and_deserialize(vector_test);\n    // check that the vector is unaltered by serialize_and_deserialize\n    CHECK(vector_control == vector_test);\n    CHECK(serialized_vector_test == vector_control);\n    CHECK(serialized_vector_test.is_owning());\n    CHECK(serialized_vector_test.data() != vector_test.data());\n    CHECK(vector_test.is_owning());\n  }\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief test the construction and move of a reference `VectorType`\n/// constructed with a `ValueType`\ntemplate <typename VectorType, typename ValueType>\nvoid vector_test_ref(tt::get_fundamental_type_t<ValueType> low =\n                         tt::get_fundamental_type_t<ValueType>{-100.0},\n                     tt::get_fundamental_type_t<ValueType> high =\n                         tt::get_fundamental_type_t<ValueType>{100.0}) {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<ValueType>> dist{low,\n                                                                        high};\n  std::vector<size_t> sizes;\n  if constexpr (VectorType::static_size >= 2) {\n    static_assert(VectorType::static_size < 19);\n    UniformCustomDistribution<size_t> static_sdist{2, VectorType::static_size};\n    UniformCustomDistribution<size_t> sdist{VectorType::static_size + 1, 20};\n    sizes = std::vector<size_t>{static_sdist(gen), VectorType::static_size,\n                                sdist(gen)};\n  } else {\n    UniformCustomDistribution<size_t> sdist{2, 20};\n    // Two for good measure\n    sizes = std::vector<size_t>{sdist(gen), sdist(gen)};\n  }\n\n  for (const size_t& size : sizes) {\n    CAPTURE(size);\n    auto original_vector = make_with_random_values<VectorType>(\n        make_not_null(&gen), make_not_null(&dist), VectorType{size});\n\n    {\n      INFO(\n          \"Check construction, copy, move, and ownership of reference vectors\");\n      VectorType ref_vector;\n      ref_vector.set_data_ref(&original_vector);\n      CHECK_FALSE(ref_vector.is_owning());\n      CHECK(original_vector.is_owning());\n      CHECK(ref_vector.data() == original_vector.data());\n\n      const VectorType data_check{original_vector};\n      CHECK(ref_vector.size() == size);\n      CHECK(ref_vector == data_check);\n      test_copy_semantics(ref_vector);\n\n      VectorType empty_ref{original_vector};\n      empty_ref.set_data_ref(nullptr, 0);\n      CHECK(not empty_ref.is_owning());\n      CHECK(empty_ref.size() == 0);\n\n      const VectorType move_constructed{std::move(ref_vector)};\n      CHECK(not move_constructed.is_owning());\n      // check the ability to make a const view\n      const VectorType const_view;\n      make_const_view(make_not_null(&const_view), move_constructed, 1,\n                      size - 1);\n      CHECK(const_view.size() == size - 1);\n      CHECK(const_view.data() == move_constructed.data() + 1);\n    }\n    {\n      INFO(\"Check move acts appropriately on both source and target refs\");\n      VectorType ref_original_vector;\n      ref_original_vector.set_data_ref(&original_vector);\n      auto generated_vector = make_with_random_values<VectorType>(\n          make_not_null(&gen), make_not_null(&dist), VectorType{size});\n      const VectorType generated_vector_copy = generated_vector;\n      ref_original_vector = std::move(generated_vector);\n      // clang-tidy : Intentionally testing use after move\n      CHECK(original_vector != generated_vector);  // NOLINT\n      CHECK(original_vector == generated_vector_copy);\n      const VectorType data_check_vector = ref_original_vector;\n      CHECK(data_check_vector == generated_vector_copy);\n    }\n    {\n      INFO(\"Check math affects both data vectors which share a ref\");\n      const auto generated_value1 = make_with_random_values<ValueType>(\n          make_not_null(&gen), make_not_null(&dist));\n      const auto generated_value2 = make_with_random_values<ValueType>(\n          make_not_null(&gen), make_not_null(&dist));\n      const auto sum_generated_values = generated_value1 + generated_value2;\n      VectorType sharing_vector{size, generated_value1};\n      VectorType owning_vector{size, generated_value2};\n      sharing_vector.set_data_ref(&owning_vector);\n      sharing_vector = sharing_vector + generated_value1;\n      CHECK_ITERABLE_APPROX(owning_vector,\n                            (VectorType{size, sum_generated_values}));\n      CHECK_ITERABLE_APPROX(sharing_vector,\n                            (VectorType{size, sum_generated_values}));\n    }\n  }\n}\n\nenum RefSizeErrorTestKind { Copy, ExpressionAssign, Move };\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Test that assigning to a non-owning `VectorType` of the wrong size\n/// appropriately generates an error.\n///\n/// \\details a calling function should be in a `CHECK_THROWS_WITH()` inside a\n/// `#ifdef SPECTRE_DEBUG` block, and check for the string \"Must\n/// copy/move/assign into same size\". Three types of tests are provided and one\n/// must be provided as the first function argument:\n/// - `RefSizeErrorTestKind::Copy`: Checks that copy-assigning to a non-owning\n/// `VectorType` from a `VectorType` with the wrong size generates an error.\n/// - `RefSizeErrorTestKind::ExpressionAssign`: Checks that assigning to a\n/// non-owning `VectorType` from an expression with alias `ResultType` of\n/// `VectorType` with the wrong size generates an error\n/// - `RefSizeErrorTestKind::Move`: Checks that move-assigning to a non-owning\n/// `VectorType` from a `VectorType` with the wrong size generates an error.\ntemplate <typename VectorType,\n          typename ValueType = typename VectorType::ElementType>\nvoid vector_ref_test_size_error(\n    RefSizeErrorTestKind test_kind,\n    tt::get_fundamental_type_t<ValueType> low =\n        tt::get_fundamental_type_t<ValueType>{-100.0},\n    tt::get_fundamental_type_t<ValueType> high =\n        tt::get_fundamental_type_t<ValueType>{100.0}) {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<ValueType>> dist{low,\n                                                                        high};\n  std::vector<size_t> sizes;\n  if constexpr (VectorType::static_size >= 2) {\n    static_assert(VectorType::static_size < 19);\n    UniformCustomDistribution<size_t> static_sdist{2, VectorType::static_size};\n    UniformCustomDistribution<size_t> sdist{VectorType::static_size + 1, 20};\n    sizes = std::vector<size_t>{static_sdist(gen), VectorType::static_size,\n                                sdist(gen)};\n  } else {\n    UniformCustomDistribution<size_t> sdist{2, 20};\n    // Two for good measure\n    sizes = std::vector<size_t>{sdist(gen), sdist(gen)};\n  }\n\n  for (const size_t& size : sizes) {\n    CAPTURE(size);\n    auto generated_vector = make_with_random_values<VectorType>(\n        make_not_null(&gen), make_not_null(&dist), VectorType{size});\n    VectorType ref_generated_vector;\n    ref_generated_vector.set_data_ref(&generated_vector);\n    const auto larger_generated_vector = make_with_random_values<VectorType>(\n        make_not_null(&gen), make_not_null(&dist), VectorType{size + 1});\n    // each of the following options should error, the reference should have\n    // received the wrong size\n    if (test_kind == RefSizeErrorTestKind::Copy) {\n      ref_generated_vector = larger_generated_vector;\n    }\n    if (test_kind == RefSizeErrorTestKind::ExpressionAssign) {\n      ref_generated_vector =\n          (larger_generated_vector + larger_generated_vector);\n    }\n    if (test_kind == RefSizeErrorTestKind::Move) {\n      ref_generated_vector = std::move(larger_generated_vector);\n    }\n  }\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief tests a small sample of math functions after a move of a\n/// `VectorType` initialized with `ValueType`\ntemplate <typename VectorType, typename ValueType>\nvoid vector_test_math_after_move(\n    tt::get_fundamental_type_t<ValueType> low =\n        tt::get_fundamental_type_t<ValueType>{-100.0},\n    tt::get_fundamental_type_t<ValueType> high =\n        tt::get_fundamental_type_t<ValueType>{100.0}) {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<tt::get_fundamental_type_t<ValueType>> dist{low,\n                                                                        high};\n  std::vector<size_t> sizes;\n  if constexpr (VectorType::static_size >= 2) {\n    static_assert(VectorType::static_size < 19);\n    UniformCustomDistribution<size_t> static_sdist{2, VectorType::static_size};\n    UniformCustomDistribution<size_t> sdist{VectorType::static_size + 1, 20};\n    sizes = std::vector<size_t>{static_sdist(gen), VectorType::static_size,\n                                sdist(gen)};\n  } else {\n    UniformCustomDistribution<size_t> sdist{2, 20};\n    // Two for good measure\n    sizes = std::vector<size_t>{sdist(gen), sdist(gen)};\n  }\n\n  for (const size_t& size : sizes) {\n    CAPTURE(size);\n    const auto generated_value1 = make_with_random_values<ValueType>(\n        make_not_null(&gen), make_not_null(&dist));\n    const auto generated_value2 = make_with_random_values<ValueType>(\n        make_not_null(&gen), make_not_null(&dist));\n    const auto sum_generated_values = generated_value1 + generated_value2;\n    const auto difference_generated_values =\n        generated_value1 - generated_value2;\n\n    const VectorType vector_math_lhs{size, generated_value1};\n    const VectorType vector_math_rhs{size, generated_value2};\n    {\n      INFO(\"Check move assignment and use after move\");\n      auto from_vector = make_with_random_values<VectorType>(\n          make_not_null(&gen), make_not_null(&dist), VectorType{size});\n      VectorType to_vector{};\n      to_vector = std::move(from_vector);\n      to_vector = vector_math_lhs + vector_math_rhs;\n      CHECK_ITERABLE_APPROX(to_vector,\n                            (VectorType{size, sum_generated_values}));\n      // clang-tidy: use after move (intentional here)\n      CHECK(from_vector.size() == 0);  // NOLINT\n      CHECK(from_vector.is_owning());\n      from_vector = vector_math_lhs - vector_math_rhs;\n      CHECK_ITERABLE_APPROX(from_vector,\n                            (VectorType{size, difference_generated_values}));\n      CHECK_ITERABLE_APPROX(to_vector,\n                            (VectorType{size, sum_generated_values}));\n    }\n    {\n      INFO(\"Check move assignment and value of target\");\n      auto from_value = make_with_random_values<ValueType>(\n          make_not_null(&gen), make_not_null(&dist));\n      VectorType from_vector{size, from_value};\n      VectorType to_vector{};\n      to_vector = std::move(from_vector);\n      from_vector = vector_math_lhs + vector_math_rhs;\n      CHECK_ITERABLE_APPROX(to_vector, (VectorType{size, from_value}));\n      CHECK_ITERABLE_APPROX(from_vector,\n                            (VectorType{size, sum_generated_values}));\n    }\n    {\n      INFO(\"Check move constructor and use after move\");\n      auto from_vector = make_with_random_values<VectorType>(\n          make_not_null(&gen), make_not_null(&dist), VectorType{size});\n      VectorType to_vector{std::move(from_vector)};\n      to_vector = vector_math_lhs + vector_math_rhs;\n      CHECK(to_vector.size() == size);\n      CHECK_ITERABLE_APPROX(to_vector,\n                            (VectorType{size, sum_generated_values}));\n      // clang-tidy: use after move (intentional here)\n      CHECK(from_vector.size() == 0);  // NOLINT\n      CHECK(from_vector.is_owning());\n      from_vector = vector_math_lhs - vector_math_rhs;\n      CHECK_ITERABLE_APPROX(from_vector,\n                            (VectorType{size, difference_generated_values}));\n      CHECK_ITERABLE_APPROX(to_vector,\n                            (VectorType{size, sum_generated_values}));\n    }\n\n    {\n      INFO(\"Check move constructor and value of target\");\n      auto from_value = make_with_random_values<ValueType>(\n          make_not_null(&gen), make_not_null(&dist));\n      VectorType from_vector{size, from_value};\n      const VectorType to_vector{std::move(from_vector)};\n      from_vector = vector_math_lhs + vector_math_rhs;\n      CHECK_ITERABLE_APPROX(to_vector, (VectorType{size, from_value}));\n      CHECK_ITERABLE_APPROX(from_vector,\n                            (VectorType{size, sum_generated_values}));\n    }\n  }\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Type alias to be more expressive with distribution bounds in vector\n/// tests which call the generic math test below\nusing Bound = std::array<double, 2>;\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief the set of test types that may be used for the math operations\n///\n/// \\details Three types of test are provided:\n/// - `Normal` is used to indicate those tests which should be performed over\n///   all combinations of the supplied vector type(s) and their value\n///   types. This is useful for e.g. `+`.\n///\n/// - `Strict` is used to indicate those tests which should be performed over\n///   only sets of the vector type and compared to the same operation of the set\n///   of its value type. This is useful for e.g. `atan2`, which cannot take a\n///   `DataVector` and a double as arguments.\n///\n/// - `Inplace` is used to indicate those tests which should be performed\n///   maintaining the type of the left-hand side of the operator and not\n///   including it in the combinations. Inplace operators such as `+=` have a\n///   more restrictive condition on the type of the left hand side than do\n///   simply `+`. (e.g. `double + complex<double>` compiles, but\n///   `double += complex<double>` does not)\n///\n/// - `GivenOrderOfArgumentsOnly` is used to indicate that the arguments given\n///   should not be taken in any combination apart from the given combination.\n///   This should be used for highly restrictive operations which are only\n///   supported for certain type combinations.\nenum TestKind { Normal, Strict, Inplace, GivenOrderOfArgumentsOnly };\n\nnamespace detail {\n\n// to choose between possible reference wrappers for the math tests.\nenum class UseRefWrap { Cref, None, Ref };\n\n// used so that the `std::variant` items below can have a consistent interface\ntemplate <typename T>\nstruct ValueWrapper {\n  explicit ValueWrapper(T value) : value_{value} {}\n\n  constexpr T get() const { return value_; }\n\n private:\n  T value_;\n};\n\n// Wrap is used to wrap values in a std::reference_wrapper using std::cref and\n// std::ref, or to not wrap at all. This is done to verify that all math\n// operations work transparently with a `std::reference_wrapper` too.\ntemplate <class T>\nstd::variant<ValueWrapper<T>, std::reference_wrapper<T>,\n             std::reference_wrapper<const T>>\nwrap(UseRefWrap wrapper, T& t) {\n  if (wrapper == UseRefWrap::Cref) {\n    return std::cref(t);\n  } else if (wrapper == UseRefWrap::Ref) {\n    return std::ref(t);\n  } else {\n    return ValueWrapper<T>(t);\n  }\n}\n\nconstexpr std::array<UseRefWrap, 2> NonConstWrapperList{UseRefWrap::None,\n                                                        UseRefWrap::Ref};\n\nconstexpr std::array<UseRefWrap, 3> WrapperList{\n    UseRefWrap::None, UseRefWrap::Ref, UseRefWrap::Cref};\n\n// struct used for determining the full number of elements in a vector, array of\n// vectors. Designed for use with `test_element_wise_function`\nstruct VectorOrArraySize {\n  // array of vectors version, assumes all vectors in the array are the same\n  // size, as will be the case for the relevant test helpers in this detail\n  // namespace\n  template <typename T, size_t S>\n  size_t operator()(const std::array<T, S>& container) const {\n    return S * VectorOrArraySize{}(container[0]);\n  }\n  // vector version\n  template <typename T,\n            Requires<std::is_arithmetic_v<typename T::ElementType> or\n                     tt::is_complex_of_fundamental_v<typename T::ElementType>> =\n                nullptr>\n  size_t operator()(const T& container) const {\n    return container.size();\n  }\n};\n\n// struct used for obtaining an indexed value in a vector, array of vectors.\n// Designed for use with `test_element_wise_function`\nstruct VectorOrArrayAt {\n  // array of vectors version\n  template <typename T, size_t S>\n  decltype(auto) operator()(std::array<T, S>& container, size_t index) {\n    return VectorOrArrayAt{}(gsl::at(container, index % S), index / S);\n  }\n  template <typename T, size_t S>\n  decltype(auto) operator()(const std::array<T, S>& container, size_t index) {\n    return VectorOrArrayAt{}(gsl::at(container, index % S), index / S);\n  }\n  // vector version\n  template <typename T,\n            Requires<std::is_arithmetic_v<typename T::ElementType> or\n                     tt::is_complex_of_fundamental_v<typename T::ElementType>> =\n                nullptr>\n  decltype(auto) operator()(T& container, size_t index) {\n    return container.at(index);\n  }\n};\n\n// given an explicit template parameter pack `Wraps`, wrap the elements of\n// `operand`, passed by pointer, element-by-element, and return the\n// resulting tuple of wrapped elements.\ntemplate <typename... Operands, size_t... Is>\nauto wrap_tuple(std::tuple<Operands...>& operand_values,\n                const std::array<UseRefWrap, sizeof...(Is)>& wraps,\n                std::index_sequence<Is...> /*meta*/) {\n  return std::make_tuple(wrap(wraps[Is], get<Is>(operand_values))...);\n}\n\n// CHECK forwarding for parameter pack expansion. Return value also required for\n// easy parameter pack use.\ninline int call_check_approx(const double a, const double b,\n                             Approx custom_approx) {\n  CHECK(custom_approx(a) == b);\n  return 0;\n}\n\ninline int call_check_approx(const std::complex<double> a,\n                             const std::complex<double> b,\n                             Approx custom_approx) {\n  CHECK(custom_approx(real(a)) == real(b));\n  CHECK(custom_approx(imag(a)) == imag(b));\n  return 0;\n}\n\ntemplate <typename Function, size_t... Is>\nclass CheckWrappedOperandsVisitor {\n public:\n  explicit CheckWrappedOperandsVisitor(const Function function)\n      : function_{function} {}\n\n  template <typename... WrappedOperands>\n  void operator()(WrappedOperands... operands) {\n    call_impl(operands.get()...);\n  }\n\n private:\n  template <typename... WrappedOperands>\n  void call_impl(WrappedOperands... operands) {\n    const size_t size_value =\n        std::max({get_size(operands, VectorOrArraySize{})...});\n    tuple_fold(std::make_tuple(operands...), [&size_value](const auto x) {\n      if (not(get_size(x, VectorOrArraySize{}) == size_value or\n              get_size(x, VectorOrArraySize{}) == 1)) {\n        ERROR(\n            \"inconsistent sized arguments passed \"\n            \"to CheckWrappedOperandsVisitor\");\n      }\n    });\n\n    auto original_arguments = std::make_tuple(operands...);\n    const auto result = function_(operands...);\n    Approx custom_approx = Approx::custom().epsilon(1.e-13).scale(1.0);\n    for (size_t i = 0; i < size_value; ++i) {\n      // the arguments which modify their arguments must be passed lvalues\n      auto element_of_arguments = std::make_tuple(get_element(\n          std::get<Is>(original_arguments), i, VectorOrArrayAt{})...);\n      call_check_approx(get_element(result, i, VectorOrArrayAt{}),\n                        function_(std::get<Is>(element_of_arguments)...),\n                        custom_approx);\n      // ensure that the final state of the arguments matches\n      expand_pack(call_check_approx(get_element(operands, i, VectorOrArrayAt{}),\n                                    std::get<Is>(element_of_arguments),\n                                    custom_approx)...);\n    }\n  }\n\n  Function function_;\n};\n\n// given the set of types of operands to test (`Operands`), and a set of\n// reference wrappers (`Wraps`), make each operand with random values according\n// to the bound from `Bound`. Then, call\n// `CheckWrappedOperandsVisitor` to test the element wise `function`.\ntemplate <typename Function, typename... Bounds, typename... Operands,\n          size_t... Is>\nvoid test_function_on_vector_operands(\n    const Function& function, const std::tuple<Bounds...>& bounds,\n    const std::array<UseRefWrap, sizeof...(Is)>& wraps,\n    const std::tuple<Operands...>& /*operands*/,\n    std::index_sequence<Is...> /*meta*/) {\n  MAKE_GENERATOR(generator);\n  UniformCustomDistribution<size_t> sdist{2, 5};\n  // Two for good measure\n  std::vector<size_t> sizes{sdist(generator), sdist(generator)};\n\n  for (const size_t& size : sizes) {\n    CAPTURE(size);\n    // using each distribution, generate a value for the appropriate operand\n    // type and put it in a tuple.\n    std::tuple<std::decay_t<Operands>...> operand_values{\n        make_with_random_values<Operands>(\n            make_not_null(&generator),\n            UniformCustomDistribution<tt::get_fundamental_type_t<\n                get_vector_element_type_t<Operands>>>{std::get<Is>(bounds)},\n            size)...};\n    // wrap the tuple of random values according to the passed in `wraps` --\n    // these will end up being a tuple of std::variant, that we need to choose\n    // between using compile-time logic\n    auto wrapped_operands = wrap_tuple(\n        operand_values, wraps, std::make_index_sequence<sizeof...(Bounds)>{});\n    const auto visitor_helper = [&function](auto... operands) {\n      CheckWrappedOperandsVisitor<Function, Is...> visitor{function};\n      std::visit(visitor, operands...);\n    };\n    std::apply(visitor_helper, wrapped_operands);\n  }\n}\n\n// Set of structs for choosing between execution paths when assembling the\n// argument list for the math testing utility\nnamespace TestFunctionsWithVectorArgumentsChoices {\nstruct FirstInplace {};\nstruct Continuing {};\nstruct Done {};\nstruct GivenOrderOfArgumentsOnly {};\n}  // namespace TestFunctionsWithVectorArgumentsChoices\n\n// helper struct implementation for choosing the next branch for assembling the\n// argument list for the math testing utility. This is done to use pattern\n// matching instead of SFINAE.\ntemplate <bool IsDone, bool IsFirstAssignment, bool GivenOrderOfArgumentsOnly>\nstruct next_choice_for_test_functions_recursion_impl;\n\n// if the size of the vector list is the same as the size of the distribution\n// bound list, then assembly is done\ntemplate <bool IsFirstAssignment, bool GivenOrderOfArgumentsOnly>\nstruct next_choice_for_test_functions_recursion_impl<\n    true, IsFirstAssignment, GivenOrderOfArgumentsOnly> {\n  using type = TestFunctionsWithVectorArgumentsChoices::Done;\n};\n\n// if we are assembling the first argument and the test type is Inplace, then\n// treat it separately\ntemplate <>\nstruct next_choice_for_test_functions_recursion_impl<false, true, false> {\n  using type = TestFunctionsWithVectorArgumentsChoices::FirstInplace;\n};\n\n// otherwise, continue assembling as ordinary\ntemplate <>\nstruct next_choice_for_test_functions_recursion_impl<false, false, false> {\n  using type = TestFunctionsWithVectorArgumentsChoices::Continuing;\n};\n\n// if using mode GivenOrderOfArgumentsOnly, follow a distinct exection path with\n// only the wraps assembled in combinations\ntemplate <bool IsFirstAssignment>\nstruct next_choice_for_test_functions_recursion_impl<false, IsFirstAssignment,\n                                                     true> {\n  using type =\n      TestFunctionsWithVectorArgumentsChoices::GivenOrderOfArgumentsOnly;\n};\n\n// helper function for easily determining the next branch of the call operator\n// for TestFunctionsWithVectorArgumentsImpl\ntemplate <size_t NumberOfOperands, typename BoundList, TestKind Test>\nusing next_choice_for_test_functions_recursion =\n    typename next_choice_for_test_functions_recursion_impl<\n        NumberOfOperands == tmpl::size<BoundList>::value,\n        NumberOfOperands == 0 and Test == TestKind::Inplace,\n        Test == TestKind::GivenOrderOfArgumentsOnly>::type;\n\n// functions to recursively assemble the arguments and wrappers for\n// calling the operator tester `test_function_on_vector_operands`.\n//\n// `UniqueTypeList` is a tmpl::list which stores the set of unique types to\n// test the functions with.\n//\n// `FirstOperand` is the first operand type, used when the Test is\n// `TestKind::Inplace`, as inplace operators have specific demands on the\n// first operand.\n\n// base case: the correct number of operand types has been obtained in\n// `Operands`, so this calls the test function with the function, bounds,\n// reference wrap, and operand type information.\ntemplate <TestKind Test, typename UniqueTypeList, typename FirstOperand,\n          typename... Operands, typename Function, typename... DistBounds>\nvoid assemble_test_function_arguments_and_execute_tests(\n    const Function& function, const std::tuple<DistBounds...>& bounds,\n    const std::array<UseRefWrap, sizeof...(Operands)>& wraps,\n    const std::tuple<Operands...> operands,\n    TestFunctionsWithVectorArgumentsChoices::Done /*meta*/) {\n  test_function_on_vector_operands(\n      function, bounds, wraps, operands,\n      std::make_index_sequence<sizeof...(DistBounds)>{});\n}\n\n// general case: add an additional reference wrapper identification type from\n// `WrapperList` to `wraps`, and an additional type from `UniqueTypeList`\n// to `operands`, and recurse on each option.\ntemplate <TestKind Test, typename UniqueTypeList, typename FirstOperand,\n          typename... Operands, typename Function, typename... DistBounds>\nvoid assemble_test_function_arguments_and_execute_tests(\n    const Function& function, const std::tuple<DistBounds...>& bounds,\n    const std::array<UseRefWrap, sizeof...(Operands)>& wraps,\n    const std::tuple<Operands...>&& operands,\n    TestFunctionsWithVectorArgumentsChoices::Continuing\n    /*meta*/) {\n  for (size_t i = 0; i < WrapperList.size(); ++i) {\n    tmpl::for_each<UniqueTypeList>([&function, &bounds, &wraps, &operands,\n                                    &i](const auto y) {\n      using next_vector = typename decltype(y)::type;\n      std::array<UseRefWrap, sizeof...(Operands) + 1_st> new_wraps;\n      for (size_t j = 0; j < sizeof...(Operands); ++j) {\n        new_wraps[j] = wraps[j];\n      }\n      new_wraps[new_wraps.size() - 1] = WrapperList[i];\n      assemble_test_function_arguments_and_execute_tests<Test, UniqueTypeList,\n                                                         FirstOperand>(\n          function, bounds, new_wraps,\n          std::tuple_cat(operands, std::tuple<next_vector>{}),\n          detail::next_choice_for_test_functions_recursion<\n              sizeof...(Operands) + 1_st, tmpl::list<DistBounds...>, Test>{});\n    });\n  }\n}\n\n// case of first operand and inplace test: the left hand operand for inplace\n// tests cannot be const, so the reference wrapper must be chosen\n// accordingly. Also, the left hand size type is fixed to be FirstOperand.\ntemplate <TestKind Test, typename UniqueTypeList, typename FirstOperand,\n          typename... Operands, typename Function, typename... DistBounds>\nvoid assemble_test_function_arguments_and_execute_tests(\n    const Function& function, const std::tuple<DistBounds...>& bounds,\n    const std::array<UseRefWrap, 0>& /*wraps*/,\n    const std::tuple<Operands...>&& /*operands*/,\n    TestFunctionsWithVectorArgumentsChoices::FirstInplace /*meta*/) {\n  for (size_t i = 0; i < NonConstWrapperList.size(); ++i) {\n    assemble_test_function_arguments_and_execute_tests<Test, UniqueTypeList,\n                                                       FirstOperand>(\n        function, bounds, std::array<UseRefWrap, 1>{{NonConstWrapperList[i]}},\n        std::tuple<FirstOperand>{},\n        detail::next_choice_for_test_functions_recursion<\n            1_st, tmpl::list<DistBounds...>, Test>{});\n  }\n}\n\n// case of TestKind of NoArgumentsCombinations: The Operands are already\n// assembled, we just need to loop through the wrap possibilities.\ntemplate <TestKind Test, typename UniqueTypeList, typename FirstOperand,\n          typename... Operands, typename Function, typename... DistBounds,\n          size_t NumberOfWraps>\nvoid assemble_test_function_arguments_and_execute_tests(\n    const Function& function, const std::tuple<DistBounds...>& bounds,\n    const std::array<UseRefWrap, NumberOfWraps>& wraps,\n    const std::tuple<Operands...>& operands,\n    TestFunctionsWithVectorArgumentsChoices::\n        GivenOrderOfArgumentsOnly /*meta*/) {\n  for (size_t i = 0; i < NonConstWrapperList.size(); ++i) {\n    std::array<UseRefWrap, NumberOfWraps + 1> new_wraps;\n    for (size_t j = 0; j < NumberOfWraps; ++j) {\n      new_wraps[j] = wraps[j];\n    }\n    new_wraps[new_wraps.size() - 1] = NonConstWrapperList[i];\n\n    assemble_test_function_arguments_and_execute_tests<Test, UniqueTypeList,\n                                                       FirstOperand>(\n        function, bounds, new_wraps, operands,\n        detail::next_choice_for_test_functions_recursion<\n            NumberOfWraps + 1_st, tmpl::list<DistBounds...>, Test>{});\n  }\n}\n\n// dispatch function for the individual tuples of functions and bounds for their\n// respective distributions. This processes the requested set of vector types to\n// test and passes the resulting request to the recursive argument assembly\n// function assemble_test_function_arguments_and_execute_tests above\ntemplate <TestKind Test, typename VectorType0, typename... VectorTypes,\n          typename Function, typename... DistBounds>\nvoid test_function_with_vector_arguments_impl(\n    const std::tuple<Function, std::tuple<DistBounds...>>&\n        function_and_argument_bounds) {\n  // The unique set of possible operands\n  using operand_type_list = tmpl::conditional_t<\n      Test == TestKind::Strict,\n      // if strict, the operand choices are just the vector types passed in\n      tmpl::remove_duplicates<tmpl::list<VectorType0, VectorTypes...>>,\n      // else, the operand choices include both the vectors and their element\n      // types\n      tmpl::remove_duplicates<tmpl::list<\n          VectorType0, VectorTypes..., get_vector_element_type_t<VectorType0>,\n          get_vector_element_type_t<VectorTypes>...>>>;\n\n  using starting_operands =\n      tmpl::conditional_t<Test == TestKind::GivenOrderOfArgumentsOnly,\n                          std::tuple<VectorType0, VectorTypes...>,\n                          std::tuple<>>;\n  assemble_test_function_arguments_and_execute_tests<Test, operand_type_list,\n                                                     VectorType0>(\n      get<0>(function_and_argument_bounds) /*function*/,\n      get<1>(function_and_argument_bounds) /*tuple of bounds*/,\n      std::array<UseRefWrap, 0>{}, starting_operands{},\n      next_choice_for_test_functions_recursion<0_st, tmpl::list<DistBounds...>,\n                                               Test>{});\n}\n}  // namespace detail\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief General entry function for testing arbitrary math functions\n * on vector types\n *\n * \\details This utility tests all combinations of the operator on the type\n * arguments, and all combinations of reference or constant reference wrappers\n * on all arguments. In certain test cases (see below), it also tests using the\n * vector type's `value_type`s in the operators as well (e.g. `DataVector +\n * double`). This is very useful for quickly generating a lot of tests, but the\n * number of tests scales exponentially in the number of arguments. Therefore,\n * functions with many arguments can be time-consuming to\n * run. 4-or-more-argument functions should be used only if completely necessary\n * and with caution. Any number of vector types may be specified, and tests are\n * run on all unique combinations of the provided. For instance, if only one\n * type is provided, the tests will be run only on combinations of that single\n * type and its `value_type`.\n *\n * \\param tuple_of_functions_and_argument_bounds A tuple of tuples, in which\n *  the inner tuple contains first a function object followed by a tuple of\n *  2-element arrays equal to the number of arguments, which represent the\n *  bounds for the random generation of the respective arguments. This system\n *  is provided for robust testing of operators like `/`, where the left-hand\n *  side has a different valid set of values than the right-hand-side.\n *\n * \\tparam Test from the `TestKind` enum, determines whether the tests will\n *  be:\n *  - `TestKind::Normal`: executed on all combinations of arguments and value\n *    types\n *  - `TestKind::Strict`: executed on all combinations of arguments, for only\n *    the vector types\n *  - `TestKind::Inplace`: executed on all combinations of arguments after the\n *    first, so first is always the 'left hand side' of the operator. In this\n *    case, at least two `VectorTypes` must be specified, where the first is\n *    used only for the left-hand side.\n *  - `TestKind::GivenOrderOfArgumentsOnly`: executed on only the combination of\n *    arguments provided, in the order provided. In this case, the number of\n *    provided types in `typename VectorType0, typename... VectorTypes` must\n *    precisely match the number of arguments taken by the function.\n *\n * \\tparam VectorType0 The first vector type for which combinations are\n *  tested. The first is accepted as a separate template argument for\n *  appropriately handling `Inplace` tests.\n * \\tparam VectorTypes The remaining types for which combinations are tested.\n *  Any number of types may be passed in, and the test will check the\n *  appropriate combinations of the vector types and (depending on the `Test`)\n *  the respective `value_type`s.\n */\ntemplate <TestKind Test, typename VectorType0, typename... VectorTypes,\n          typename... FunctionsAndArgumentBounds>\nvoid test_functions_with_vector_arguments(\n    const std::tuple<FunctionsAndArgumentBounds...>&\n        tuple_of_functions_and_argument_bounds) {\n  tuple_fold(\n      tuple_of_functions_and_argument_bounds,\n      [](const auto& function_and_argument_bounds) {\n        detail::test_function_with_vector_arguments_impl<Test, VectorType0,\n                                                         VectorTypes...>(\n            function_and_argument_bounds);\n      });\n}\n}  // namespace VectorImpl\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Amr/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"DomainAmrHelpers\")\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  NeighborFlagHelpers.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  NeighborFlagHelpers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Amr\n  DomainStructure\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Amr/NeighborFlagHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Domain/Amr/NeighborFlagHelpers.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstd::vector<std::array<::amr::Flag, Dim>> amr_flags();\n\ntemplate <>\nstd::vector<std::array<::amr::Flag, 1>> amr_flags<1>() {\n  // use IncreaseResolution instead of DoNothing to catch any instances of\n  // hard-coding DoNothing when the function should be valid for any of\n  // DoNothing, DecreaseResolution, or IncreaseResolution\n  return std::vector{std::array{::amr::Flag::Join},\n                     std::array{::amr::Flag::IncreaseResolution},\n                     std::array{::amr::Flag::Split}};\n}\n\ntemplate <>\nstd::vector<std::array<::amr::Flag, 2>> amr_flags<2>() {\n  // use IncreaseResolution instead of DoNothing to catch any instances of\n  // hard-coding DoNothing when the function should be valid for any of\n  // DoNothing, DecreaseResolution, or IncreaseResolution\n  return std::vector{\n      std::array{::amr::Flag::Join, ::amr::Flag::Join},\n      std::array{::amr::Flag::Join, ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::IncreaseResolution, ::amr::Flag::Join},\n      std::array{::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::IncreaseResolution, ::amr::Flag::Split},\n      std::array{::amr::Flag::Split, ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::Split, ::amr::Flag::Split}};\n}\n\ntemplate <>\nstd::vector<std::array<::amr::Flag, 3>> amr_flags<3>() {\n  // use IncreaseResolution instead of DoNothing to catch any instances of\n  // hard-coding DoNothing when the function should be valid for any of\n  // DoNothing, DecreaseResolution, or IncreaseResolution\n  return std::vector{\n      std::array{::amr::Flag::Join, ::amr::Flag::Join, ::amr::Flag::Join},\n      std::array{::amr::Flag::Join, ::amr::Flag::Join,\n                 ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::Join, ::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::Join},\n      std::array{::amr::Flag::Join, ::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::IncreaseResolution, ::amr::Flag::Join,\n                 ::amr::Flag::Join},\n      std::array{::amr::Flag::IncreaseResolution, ::amr::Flag::Join,\n                 ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::IncreaseResolution, ::amr::Flag::Join},\n      std::array{::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::IncreaseResolution, ::amr::Flag::Split},\n      std::array{::amr::Flag::IncreaseResolution, ::amr::Flag::Split,\n                 ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::IncreaseResolution, ::amr::Flag::Split,\n                 ::amr::Flag::Split},\n      std::array{::amr::Flag::Split, ::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::Split, ::amr::Flag::IncreaseResolution,\n                 ::amr::Flag::Split},\n      std::array{::amr::Flag::Split, ::amr::Flag::Split,\n                 ::amr::Flag::IncreaseResolution},\n      std::array{::amr::Flag::Split, ::amr::Flag::Split, ::amr::Flag::Split}};\n}\n\ntemplate <size_t Dim>\nbool are_valid_neighbor_flags(\n    const ElementId<Dim>& element_id,\n    const std::array<::amr::Flag, Dim>& element_flags,\n    const ElementId<Dim>& neighbor_id,\n    const std::array<::amr::Flag, Dim>& neighbor_flags,\n    const OrientationMap<Dim>& orientation_of_neighbor =\n        OrientationMap<Dim>::create_aligned()) {\n  if (element_id == neighbor_id) {\n    return element_flags == neighbor_flags;\n  }\n  for (size_t d = 0; d < Dim; ++d) {\n    if (neighbor_id.segment_id(d) == SegmentId{0, 0} and\n        gsl::at(neighbor_flags, d) == amr::Flag::Join) {\n      return false;\n    }\n  }\n  const auto element_desired_levels =\n      desired_refinement_levels(element_id, element_flags);\n  const auto neighbor_desired_levels = desired_refinement_levels_of_neighbor(\n      neighbor_id, neighbor_flags, orientation_of_neighbor);\n  for (size_t d = 0; d < Dim; ++d) {\n    if ((gsl::at(element_desired_levels, d) >\n         gsl::at(neighbor_desired_levels, d) + 1) or\n        (gsl::at(neighbor_desired_levels, d) >\n         gsl::at(element_desired_levels, d) + 1)) {\n      return false;\n    }\n    if (element_id.block_id() == neighbor_id.block_id()) {\n      const auto& element_segment = element_id.segment_id(d);\n      const auto& neighbor_segment = neighbor_id.segment_id(d);\n      if (element_segment.refinement_level() == 0 or\n          neighbor_segment.refinement_level() == 0) {\n        continue;\n      }\n      if (element_segment.endpoint(element_segment.side_of_sibling()) ==\n          neighbor_segment.endpoint(neighbor_segment.side_of_sibling())) {\n        if (gsl::at(element_flags, d) == ::amr::Flag::Join) {\n          if (element_desired_levels != neighbor_desired_levels) {\n            return false;\n          }\n        } else if (gsl::at(neighbor_flags, d) == ::amr::Flag::Join and\n                   element_segment.id_of_sibling() == neighbor_segment) {\n          return false;\n        }\n      }\n    }\n  }\n  return true;\n}\n}  // namespace\n\nnamespace TestHelpers::amr {\n\ntemplate <size_t Dim>\nvalid_info_t<Dim> valid_neighbor_info(\n    const ElementId<Dim>& element_id,\n    const std::array<::amr::Flag, Dim>& element_flags,\n    const Neighbors<Dim>& neighbors) {\n  valid_info_t<Dim> result{};\n  std::array<size_t, Dim> extents{};\n  alg::iota(extents, 2_st);\n  const Mesh<Dim> mesh{extents, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto& first_neighbor_id = *(neighbors.begin());\n  const auto& orientation_of_first_neighbor =\n      neighbors.orientation(first_neighbor_id);\n  for (const auto& neighbor_flags : amr_flags<Dim>()) {\n    if (are_valid_neighbor_flags(element_id, element_flags, first_neighbor_id,\n                                 neighbor_flags,\n                                 orientation_of_first_neighbor)) {\n      result.emplace_back(neighbor_info_t<Dim>{\n          {first_neighbor_id, ::amr::Info<Dim>{neighbor_flags, mesh}}});\n    }\n  }\n\n  for (auto it = std::next(neighbors.begin()); it != neighbors.end(); ++it) {\n    valid_info_t<Dim> prev_result{};\n    std::swap(result, prev_result);\n    const auto& neighbor_id = *it;\n    const auto& orientation_of_neighbor = neighbors.orientation(neighbor_id);\n    for (const auto& neighbor_flags : amr_flags<Dim>()) {\n      if (not are_valid_neighbor_flags(element_id, element_flags, neighbor_id,\n                                       neighbor_flags,\n                                       orientation_of_neighbor)) {\n        // neighbor_flags conflicts with element\n        continue;\n      }\n      for (const auto& valid_flags : prev_result) {\n        bool can_add_flag = true;\n        for (const auto& [prev_neighbor_id, prev_neighbor_info] : valid_flags) {\n          if (not are_valid_neighbor_flags(prev_neighbor_id,\n                                           prev_neighbor_info.flags,\n                                           neighbor_id, neighbor_flags)) {\n            // neighbor_flags conflicts with previous neighbor\n            can_add_flag = false;\n            break;\n          }\n        }\n        if (can_add_flag) {\n          auto new_flags = valid_flags;\n          new_flags.emplace(neighbor_id,\n                            ::amr::Info<Dim>{neighbor_flags, mesh});\n          result.emplace_back(std::move(new_flags));\n        }\n      }\n    }\n  }\n  return result;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                   \\\n  template valid_info_t<DIM(data)> valid_neighbor_info(        \\\n      const ElementId<DIM(data)>& element_id,                  \\\n      const std::array<::amr::Flag, DIM(data)>& element_flags, \\\n      const Neighbors<DIM(data)>& neighbors);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace TestHelpers::amr\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Amr/NeighborFlagHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n#include <vector>\n\n/// \\cond\nnamespace amr {\nenum class Flag;\ntemplate <size_t VolumeDim>\nstruct Info;\n}  // namespace amr\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim, typename IdType>\nclass Neighbors;\n/// \\endcond\n\nnamespace TestHelpers::amr {\ntemplate <size_t Dim>\nusing neighbor_info_t = std::unordered_map<ElementId<Dim>, ::amr::Info<Dim>>;\n\ntemplate <size_t Dim>\nusing valid_info_t = std::vector<neighbor_info_t<Dim>>;\n\n/// Returns all permutations of valid flags for the `neighbors` (in a single\n/// direction) of an Element with `element_id` and `element_flags`\n///\n/// The neighbor meshes are (2), (2, 3), or (2, 3, 4) point LGL meshes,\n/// depending on dimension.\ntemplate <size_t Dim>\nvalid_info_t<Dim> valid_neighbor_info(\n    const ElementId<Dim>& element_id,\n    const std::array<::amr::Flag, Dim>& element_flags,\n    const Neighbors<Dim, ElementId<Dim>>& neighbors);\n}  // namespace TestHelpers::amr\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Amr/RegistrationHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/ElementRegistrar.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::amr {\ntemplate <size_t Dim>\nstruct RegisteredElements : db::SimpleTag {\n  using type = std::unordered_set<ElementId<Dim>>;\n};\n\ntemplate <typename Metavariables>\nstruct Registrar {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using simple_tags = tmpl::list<RegisteredElements<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\nstruct DeregisterWithRegistrant {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const ElementId<Metavariables::volume_dim>& element_id) {\n    static constexpr size_t volume_dim = Metavariables::volume_dim;\n    db::mutate<RegisteredElements<volume_dim>>(\n        [&element_id](\n            const gsl::not_null<std::unordered_set<ElementId<volume_dim>>*>\n                registered_elements) {\n          registered_elements->erase(element_id);\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct RegisterWithRegistrant {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const ElementId<Metavariables::volume_dim>& element_id) {\n    static constexpr size_t volume_dim = Metavariables::volume_dim;\n    db::mutate<RegisteredElements<volume_dim>>(\n        [&element_id](\n            const gsl::not_null<std::unordered_set<ElementId<volume_dim>>*>\n                registered_elements) {\n          registered_elements->insert(element_id);\n        },\n        make_not_null(&box));\n  }\n};\n\n// [element_registrar_example]\nstruct RegisterElement : tt::ConformsTo<Parallel::protocols::ElementRegistrar> {\n private:\n  template <typename ParallelComponent, typename RegisterOrDeregisterAction,\n            typename DbTagList, typename Metavariables, typename ArrayIndex>\n  static void register_or_deregister_impl(\n      db::DataBox<DbTagList>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index) {\n    auto& registrar = *Parallel::local_branch(\n        Parallel::get_parallel_component<Registrar<Metavariables>>(cache));\n    Parallel::simple_action<RegisterOrDeregisterAction>(registrar, array_index);\n  }\n\n public:\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void perform_registration(db::DataBox<DbTagList>& box,\n                                   Parallel::GlobalCache<Metavariables>& cache,\n                                   const ArrayIndex& array_index) {\n    register_or_deregister_impl<ParallelComponent, RegisterWithRegistrant>(\n        box, cache, array_index);\n  }\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void perform_deregistration(\n      db::DataBox<DbTagList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index) {\n    register_or_deregister_impl<ParallelComponent, DeregisterWithRegistrant>(\n        box, cache, array_index);\n  }\n};\n// [element_registrar_example]\n\n}  // namespace TestHelpers::amr\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/BoundaryConditions/BoundaryCondition.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <vector>\n\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace TestHelpers::domain::BoundaryConditions {\ntemplate <size_t Dim>\nBoundaryConditionBase<Dim>::BoundaryConditionBase(CkMigrateMessage* const msg)\n    : ::domain::BoundaryConditions::BoundaryCondition(msg) {}\n\ntemplate <size_t Dim>\nvoid BoundaryConditionBase<Dim>::pup(PUP::er& p) {\n  ::domain::BoundaryConditions::BoundaryCondition::pup(p);\n}\n\ntemplate <size_t Dim>\nTestBoundaryCondition<Dim>::TestBoundaryCondition(Direction<Dim> direction,\n                                                  size_t block_id)\n    : direction_(std::move(direction)), block_id_(block_id) {}\n\ntemplate <size_t Dim>\nTestBoundaryCondition<Dim>::TestBoundaryCondition(const std::string& direction,\n                                                  size_t block_id)\n    : block_id_(block_id) {\n  std::array<std::pair<std::string, Direction<Dim>>, 6> known_directions{};\n  known_directions[0] = std::pair{\"lower-xi\", Direction<Dim>::lower_xi()};\n  known_directions[1] = std::pair{\"upper-xi\", Direction<Dim>::upper_xi()};\n  if constexpr (Dim > 1) {\n    known_directions[2] = std::pair{\"lower-eta\", Direction<Dim>::lower_eta()};\n    known_directions[3] = std::pair{\"upper-eta\", Direction<Dim>::upper_eta()};\n  }\n  if constexpr (Dim > 2) {\n    known_directions[4] = std::pair{\"lower-zeta\", Direction<Dim>::lower_zeta()};\n    known_directions[5] = std::pair{\"upper-zeta\", Direction<Dim>::upper_zeta()};\n  }\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    if (direction == gsl::at(known_directions, i).first) {\n      direction_ = gsl::at(known_directions, i).second;\n      break;\n    }\n    if (i == 2 * Dim - 1) {\n      std::string known_dirs{};\n      for (const auto& dir : known_directions) {\n        known_dirs += \" \" + dir.first;\n      }\n      ERROR(\"Unknown direction: \" << direction\n                                  << \"\\nKnown directions are: \" << known_dirs);\n    }\n  }\n}\n\ntemplate <size_t Dim>\nTestBoundaryCondition<Dim>::TestBoundaryCondition(CkMigrateMessage* const msg)\n    : BoundaryConditionBase<Dim>(msg) {}\n\ntemplate <size_t Dim>\nauto TestBoundaryCondition<Dim>::get_clone() const\n    -> std::unique_ptr<::domain::BoundaryConditions::BoundaryCondition> {\n  return std::make_unique<TestBoundaryCondition<Dim>>(*this);\n}\n\ntemplate <size_t Dim>\nvoid TestBoundaryCondition<Dim>::pup(PUP::er& p) {\n  BoundaryConditionBase<Dim>::pup(p);\n  p | direction_;\n  p | block_id_;\n}\n\ntemplate <size_t Dim>\nbool operator==(const TestBoundaryCondition<Dim>& lhs,\n                const TestBoundaryCondition<Dim>& rhs) {\n  return lhs.direction() == rhs.direction() and\n         lhs.block_id() == rhs.block_id();\n}\n\ntemplate <size_t Dim>\nbool operator!=(const TestBoundaryCondition<Dim>& lhs,\n                const TestBoundaryCondition<Dim>& rhs) {\n  return not(lhs == rhs);\n}\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID TestBoundaryCondition<Dim>::my_PUP_ID = 0;  // NOLINT\n\ntemplate <size_t Dim>\nvoid test_boundary_conditions(\n    const std::vector<DirectionMap<\n        Dim, std::unique_ptr<::domain::BoundaryConditions::BoundaryCondition>>>&\n        all_boundary_conditions) {\n  for (size_t block_id = 0; block_id < all_boundary_conditions.size();\n       ++block_id) {\n    const auto& boundary_conditions = all_boundary_conditions[block_id];\n    for (const auto& [direction, boundary_condition] : boundary_conditions) {\n      CAPTURE(direction);\n      const auto test_bc =\n          dynamic_cast<const TestHelpers::domain::BoundaryConditions::\n                           TestBoundaryCondition<Dim>*>(\n              boundary_condition.get());\n      REQUIRE(test_bc != nullptr);\n      CHECK(test_bc->direction() == direction);\n      CHECK(test_bc->block_id() == block_id);\n    }\n  }\n}\n\nvoid register_derived_with_charm() {\n  register_derived_classes_with_charm<BoundaryConditionBase<1>>();\n  register_derived_classes_with_charm<BoundaryConditionBase<2>>();\n  register_derived_classes_with_charm<BoundaryConditionBase<3>>();\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                           \\\n  template class BoundaryConditionBase<DIM(data)>;                       \\\n  template class TestBoundaryCondition<DIM(data)>;                       \\\n  template bool operator==(const TestBoundaryCondition<DIM(data)>& lhs,  \\\n                           const TestBoundaryCondition<DIM(data)>& rhs); \\\n  template bool operator!=(const TestBoundaryCondition<DIM(data)>& lhs,  \\\n                           const TestBoundaryCondition<DIM(data)>& rhs); \\\n  template void test_boundary_conditions(                                \\\n      const std::vector<DirectionMap<                                    \\\n          DIM(data), std::unique_ptr<                                    \\\n                         ::domain::BoundaryConditions::BoundaryCondition>>>&);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATION\n}  // namespace TestHelpers::domain::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <vector>\n\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/None.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Helpers for boundary conditions\nnamespace TestHelpers::domain::BoundaryConditions {\n/// \\cond\ntemplate <size_t Dim>\nclass TestBoundaryCondition;\n/// \\endcond\n\n/// \\brief A system-specific boundary condition base class.\n///\n/// To be used in conjunction with `SystemWithBoundaryConditions`\ntemplate <size_t Dim>\nclass BoundaryConditionBase\n    : public ::domain::BoundaryConditions::BoundaryCondition {\n public:\n  using creatable_classes = tmpl::list<\n      TestBoundaryCondition<Dim>,\n      ::domain::BoundaryConditions::None<BoundaryConditionBase<Dim>>,\n      ::domain::BoundaryConditions::Periodic<BoundaryConditionBase<Dim>>>;\n\n  BoundaryConditionBase() = default;\n  BoundaryConditionBase(BoundaryConditionBase&&) = default;\n  BoundaryConditionBase& operator=(BoundaryConditionBase&&) = default;\n  BoundaryConditionBase(const BoundaryConditionBase&) = default;\n  BoundaryConditionBase& operator=(const BoundaryConditionBase&) = default;\n  ~BoundaryConditionBase() override = default;\n\n  explicit BoundaryConditionBase(CkMigrateMessage* msg);\n\n  void pup(PUP::er& p) override;\n\n  static constexpr Options::String help = {\"Boundary conditions for tests.\"};\n};\n\n/// \\brief Concrete boundary condition\ntemplate <size_t Dim>\nclass TestBoundaryCondition final : public BoundaryConditionBase<Dim> {\n public:\n  TestBoundaryCondition() = default;\n  explicit TestBoundaryCondition(Direction<Dim> direction, size_t block_id = 0);\n  TestBoundaryCondition(const std::string& direction, size_t block_id);\n  TestBoundaryCondition(TestBoundaryCondition&&) = default;\n  TestBoundaryCondition& operator=(TestBoundaryCondition&&) = default;\n  TestBoundaryCondition(const TestBoundaryCondition&) = default;\n  TestBoundaryCondition& operator=(const TestBoundaryCondition&) = default;\n  ~TestBoundaryCondition() override = default;\n  explicit TestBoundaryCondition(CkMigrateMessage* const msg);\n\n  struct DirectionOptionTag {\n    using type = std::string;\n    static std::string name() { return \"Direction\"; }\n    static constexpr Options::String help =\n        \"The direction the boundary condition operates in.\";\n  };\n  struct BlockIdOptionTag {\n    using type = size_t;\n    static std::string name() { return \"BlockId\"; }\n    static constexpr Options::String help =\n        \"The id of the block the boundary condition operates in.\";\n  };\n\n  using options = tmpl::list<DirectionOptionTag, BlockIdOptionTag>;\n\n  static constexpr Options::String help = {\"Boundary condition for testing.\"};\n\n  WRAPPED_PUPable_decl_base_template(\n      ::domain::BoundaryConditions::BoundaryCondition,\n      TestBoundaryCondition<Dim>);\n\n  const Direction<Dim>& direction() const { return direction_; }\n  size_t block_id() const { return block_id_; }\n\n  auto get_clone() const -> std::unique_ptr<\n      ::domain::BoundaryConditions::BoundaryCondition> override;\n\n  void pup(PUP::er& p) override;\n\n private:\n  Direction<Dim> direction_{};\n  size_t block_id_{0};\n};\n\ntemplate <size_t Dim>\nbool operator==(const TestBoundaryCondition<Dim>& lhs,\n                const TestBoundaryCondition<Dim>& rhs);\n\ntemplate <size_t Dim>\nbool operator!=(const TestBoundaryCondition<Dim>& lhs,\n                const TestBoundaryCondition<Dim>& rhs);\n\ntemplate <size_t Dim>\nusing TestPeriodicBoundaryCondition =\n    ::domain::BoundaryConditions::Periodic<BoundaryConditionBase<Dim>>;\n\ntemplate <size_t Dim>\nusing TestNoneBoundaryCondition =\n    ::domain::BoundaryConditions::None<BoundaryConditionBase<Dim>>;\n\n/// Empty system that has boundary conditions\ntemplate <size_t Dim>\nstruct SystemWithBoundaryConditions {\n  using boundary_conditions_base = BoundaryConditionBase<Dim>;\n};\n\n/// Empty system that doesn't have boundary conditions\ntemplate <size_t Dim>\nstruct SystemWithoutBoundaryConditions {};\n\n/// Metavariables with a system that has boundary conditions\ntemplate <size_t Dim, typename Creator>\nstruct MetavariablesWithBoundaryConditions {\n  using system = SystemWithBoundaryConditions<Dim>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DomainCreator<Dim>, tmpl::list<Creator>>>;\n  };\n};\n\n/// Metavariables with a system that doesn't have boundary conditions\ntemplate <size_t Dim, typename Creator>\nstruct MetavariablesWithoutBoundaryConditions {\n  using system = SystemWithoutBoundaryConditions<Dim>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DomainCreator<Dim>, tmpl::list<Creator>>>;\n  };\n};\n\n/// Assuming `all_boundary_conditions` are of type `TestBoundaryCondition`,\n/// check their direction and block ID\ntemplate <size_t Dim>\nvoid test_boundary_conditions(\n    const std::vector<DirectionMap<\n        Dim, std::unique_ptr<::domain::BoundaryConditions::BoundaryCondition>>>&\n        all_boundary_conditions);\n\nvoid register_derived_with_charm();\n}  // namespace TestHelpers::domain::BoundaryConditions\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/BoundaryConditions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"DomainBoundaryConditionsHelpers\")\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BoundaryCondition.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  BoundaryCondition.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Domain\n  DomainBoundaryConditions\n  DomainCreators\n  DomainStructure\n  Options\n  Parallel\n  Utilities\n  PRIVATE\n  Framework\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"DomainHelpers\")\n\nset(LIBRARY_SOURCES\n  DomainTestHelpers.cpp\n  Creators/TimeDependent/TestHelpers.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Boost::boost\n  CoordinateMaps\n  FunctionsOfTime\n\n  PRIVATE\n  DomainBoundaryConditionsHelpers\n  DomainCreators\n  ErrorHandling\n  Framework\n  Spectral\n\n  PUBLIC\n  DataStructures\n  Domain\n  DomainStructure\n  Utilities\n  )\n\nadd_subdirectory(Amr)\nadd_subdirectory(BoundaryConditions)\nadd_subdirectory(Structure)\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n///\\file\n/// Helper functions for testing coordinate maps\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <functional>\n#include <limits>\n#include <memory>\n#include <numeric>\n#include <random>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependentHelpers.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Given a Map and a CoordinateMapBase, checks that the maps are equal by\n * downcasting `map_base` and then comparing to `map`. Returns false if the\n * downcast fails.\n */\ntemplate <typename Map, typename SourceFrame, typename TargetFrame>\nbool are_maps_equal(const Map& map,\n                    const domain::CoordinateMapBase<SourceFrame, TargetFrame,\n                                                    Map::dim>& map_base) {\n  const auto* map_derived = dynamic_cast<const Map*>(&map_base);\n  return map_derived == nullptr ? false : (*map_derived == map);\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Given two coordinate maps (but not their types), check that the maps\n/// are equal by evaluating them at a random set of points.\ntemplate <typename SourceFrame, typename TargetFrame, size_t VolumeDim>\nvoid check_if_maps_are_equal(\n    const domain::CoordinateMapBase<SourceFrame, TargetFrame, VolumeDim>&\n        map_one,\n    const domain::CoordinateMapBase<SourceFrame, TargetFrame, VolumeDim>&\n        map_two,\n    const double time = std::numeric_limits<double>::quiet_NaN(),\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time = {},\n    Approx custom_approx = approx) {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1, 1);\n\n  for (size_t n = 0; n < 10; ++n) {\n    tnsr::I<double, VolumeDim, SourceFrame> source_point{};\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      source_point.get(d) = real_dis(gen);\n    }\n    CAPTURE(source_point);\n    CHECK_ITERABLE_CUSTOM_APPROX(map_one(source_point, time, functions_of_time),\n                                 map_two(source_point, time, functions_of_time),\n                                 custom_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        map_one.jacobian(source_point, time, functions_of_time),\n        map_two.jacobian(source_point, time, functions_of_time), custom_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        map_one.inv_jacobian(source_point, time, functions_of_time),\n        map_two.inv_jacobian(source_point, time, functions_of_time),\n        custom_approx);\n  }\n}\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Given a coordinate map, check that this map is equal to the identity\n/// by evaluating the map at a random set of points.\ntemplate <typename Map>\nvoid check_if_map_is_identity(const Map& map) {\n  using IdentityMap = domain::CoordinateMaps::Identity<Map::dim>;\n  check_if_maps_are_equal(\n      domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(IdentityMap{}),\n      domain::make_coordinate_map<Frame::Inertial, Frame::Grid>(map));\n  CHECK(map.is_identity());\n}\n\n/// @{\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Given a Map `map`, checks that the jacobian gives expected results\n * when compared to the numerical derivative in each direction.\n */\ntemplate <typename Map>\nvoid test_jacobian(const Map& map,\n                   const std::array<double, Map::dim>& test_point) {\n  INFO(\"Test Jacobian\");\n  CAPTURE(test_point);\n  // Our default approx value is too stringent for this test\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  const double dx = 1e-4;\n  const auto jacobian = map.jacobian(test_point);\n  for (size_t i = 0; i < Map::dim; ++i) {\n    const auto numerical_deriv_i = numerical_derivative(map, test_point, i, dx);\n    for (size_t j = 0; j < Map::dim; ++j) {\n      INFO(\"i: \" << i << \" j: \" << j);\n      CHECK(jacobian.get(j, i) == local_approx(gsl::at(numerical_deriv_i, j)));\n    }\n  }\n}\n\ntemplate <typename Map>\nvoid test_jacobian(\n    const Map& map, const std::array<double, Map::dim>& test_point,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0)) {\n  INFO(\"Test time-dependent Jacobian\");\n  CAPTURE(test_point);\n  CAPTURE(time);\n  const auto compute_map_point =\n      [&map, time,\n       &functions_of_time](const std::array<double, Map::dim>& point) {\n        return map(point, time, functions_of_time);\n      };\n  const double dx = 1e-4;\n  const auto jacobian = map.jacobian(test_point, time, functions_of_time);\n  for (size_t i = 0; i < Map::dim; ++i) {\n    const auto numerical_deriv_i =\n        numerical_derivative(compute_map_point, test_point, i, dx);\n    for (size_t j = 0; j < Map::dim; ++j) {\n      INFO(\"i: \" << i << \" j: \" << j);\n      CHECK(jacobian.get(j, i) == local_approx(gsl::at(numerical_deriv_i, j)));\n    }\n  }\n}\n\ntemplate <typename Map>\nvoid test_jacobian(const Map& map,\n                   const std::array<DataVector, Map::dim>& test_point) {\n  INFO(\"Test Jacobian\");\n  CAPTURE(test_point);\n  // Our default approx value is too stringent for this test\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  const double dx = 1e-4;\n  const auto jacobian = map.jacobian(test_point);\n  for (size_t i = 0; i < Map::dim; ++i) {\n    for (size_t k = 0; k < gsl::at(test_point, 0).size(); k++) {\n      const auto numerical_deriv_i =\n          numerical_derivative(map, gsl::at(test_point, k), i, dx);\n      for (size_t j = 0; j < Map::dim; ++j) {\n        INFO(\"i: \" << i << \" j: \" << j);\n        CHECK(jacobian.get(j, i)[k] ==\n              local_approx(gsl::at(numerical_deriv_i, j)));\n      }\n    }\n  }\n}\n\ntemplate <typename Map>\nvoid test_jacobian(\n    const Map& map, const std::array<DataVector, Map::dim>& test_point,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0)) {\n  INFO(\"Test time-dependent Jacobian\");\n  CAPTURE(test_point);\n  CAPTURE(time);\n  const auto compute_map_point =\n      [&map, time,\n       &functions_of_time](const std::array<double, Map::dim>& point) {\n        return map(point, time, functions_of_time);\n      };\n  const double dx = 1e-4;\n  const auto jacobian = map.jacobian(test_point, time, functions_of_time);\n  std::vector<std::array<double, Map::dim>> dv_to_double_vector(\n      test_point[0].size());\n  std::array<double, Map::dim> dv_to_double{};\n  for (size_t i = 0; i < test_point[0].size(); ++i) {\n    for (size_t k = 0; k < Map::dim; k++) {\n      gsl::at(dv_to_double, k) = gsl::at(gsl::at(test_point, k), i);\n    }\n    dv_to_double_vector[i] = dv_to_double;\n  }\n  for (size_t i = 0; i < Map::dim; ++i) {\n    for (size_t k = 0; k < test_point[0].size(); k++) {\n      CAPTURE(dv_to_double_vector[k]);\n      CAPTURE(magnitude(dv_to_double_vector[k]));\n      const auto numerical_deriv_i = numerical_derivative(\n          compute_map_point, dv_to_double_vector[k], i, dx);\n      for (size_t j = 0; j < Map::dim; ++j) {\n        INFO(\"i: \" << i << \" j: \" << j << \" k: \" << k);\n        REQUIRE(jacobian.get(j, i)[k] ==\n                local_approx(gsl::at(numerical_deriv_i, j)));\n      }\n    }\n  }\n}\n/// @}\n\n/// @{\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Given a Map `map`, checks that the inverse jacobian and jacobian\n * multiply together to produce the identity matrix\n */\ntemplate <typename Map>\nvoid test_inv_jacobian(const Map& map,\n                       const std::array<double, Map::dim>& test_point) {\n  INFO(\"Test inverse Jacobian\");\n  CAPTURE(test_point);\n  const auto jacobian = map.jacobian(test_point);\n  const auto inv_jacobian = map.inv_jacobian(test_point);\n\n  const auto expected_identity = [&jacobian, &inv_jacobian]() {\n    std::array<std::array<double, Map::dim>, Map::dim> identity{};\n    for (size_t i = 0; i < Map::dim; ++i) {\n      for (size_t j = 0; j < Map::dim; ++j) {\n        gsl::at(gsl::at(identity, i), j) = 0.;\n        for (size_t k = 0; k < Map::dim; ++k) {\n          gsl::at(gsl::at(identity, i), j) +=\n              jacobian.get(i, k) * inv_jacobian.get(k, j);\n        }\n      }\n    }\n    return identity;\n  }();\n\n  for (size_t i = 0; i < Map::dim; ++i) {\n    for (size_t j = 0; j < Map::dim; ++j) {\n      CHECK(gsl::at(gsl::at(expected_identity, i), j) ==\n            approx(i == j ? 1. : 0.));\n    }\n  }\n}\n\ntemplate <typename Map>\nvoid test_inv_jacobian(\n    const Map& map, const std::array<double, Map::dim>& test_point,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  INFO(\"Test inverse time-dependent Jacobian\");\n  CAPTURE(test_point);\n  CAPTURE(time);\n  const auto jacobian = map.jacobian(test_point, time, functions_of_time);\n  const auto inv_jacobian =\n      map.inv_jacobian(test_point, time, functions_of_time);\n\n  const auto expected_identity = [&jacobian, &inv_jacobian]() {\n    std::array<std::array<double, Map::dim>, Map::dim> identity{};\n    for (size_t i = 0; i < Map::dim; ++i) {\n      for (size_t j = 0; j < Map::dim; ++j) {\n        gsl::at(gsl::at(identity, i), j) = 0.;\n        for (size_t k = 0; k < Map::dim; ++k) {\n          gsl::at(gsl::at(identity, i), j) +=\n              jacobian.get(i, k) * inv_jacobian.get(k, j);\n        }\n      }\n    }\n    return identity;\n  }();\n\n  for (size_t i = 0; i < Map::dim; ++i) {\n    for (size_t j = 0; j < Map::dim; ++j) {\n      CHECK(gsl::at(gsl::at(expected_identity, i), j) ==\n            approx(i == j ? 1. : 0.));\n    }\n  }\n}\n\ntemplate <typename Map>\nvoid test_inv_jacobian(const Map& map,\n                       const std::array<DataVector, Map::dim>& test_point) {\n  INFO(\"Test inverse Jacobian\");\n  CAPTURE(test_point);\n  const auto jacobian = map.jacobian(test_point);\n  const auto inv_jacobian = map.inv_jacobian(test_point);\n\n  const auto expected_identity = [&jacobian, &inv_jacobian]() {\n    auto identity =\n        make_with_value<tnsr::Ij<DataVector, Map::dim, Frame::NoFrame>>(\n            jacobian.get(0, 0).size(), 0.0);\n    for (size_t i = 0; i < Map::dim; ++i) {\n      for (size_t j = 0; j < Map::dim; ++j) {\n        for (size_t l = 0; l < jacobian.get(0, 0).size(); l++) {\n          for (size_t k = 0; k < Map::dim; ++k) {\n            identity.get(i, j)[l] += gsl::at(jacobian.get(i, k), l) *\n                                     gsl::at(inv_jacobian.get(k, j), l);\n          }\n        }\n      }\n    }\n    return identity;\n  }();\n\n  for (size_t i = 0; i < Map::dim; ++i) {\n    for (size_t j = 0; j < Map::dim; ++j) {\n      for (size_t k = 0; k < gsl::at(test_point, 0).size(); k++) {\n        CHECK(gsl::at(expected_identity.get(i, j), k) ==\n              approx(i == j ? 1. : 0.));\n      }\n    }\n  }\n}\n\ntemplate <typename Map>\nvoid test_inv_jacobian(\n    const Map& map, const std::array<DataVector, Map::dim>& test_point,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  INFO(\"Test inverse Jacobian\");\n  CAPTURE(test_point);\n  CAPTURE(time);\n  const auto jacobian = map.jacobian(test_point, time, functions_of_time);\n  const auto inv_jacobian =\n      map.inv_jacobian(test_point, time, functions_of_time);\n\n  const auto expected_identity = [&jacobian, &inv_jacobian]() {\n    auto identity =\n        make_with_value<tnsr::Ij<DataVector, Map::dim, Frame::NoFrame>>(\n            jacobian.get(0, 0).size(), 0.0);\n    for (size_t i = 0; i < Map::dim; ++i) {\n      for (size_t j = 0; j < Map::dim; ++j) {\n        for (size_t l = 0; l < jacobian.get(0, 0).size(); l++) {\n          for (size_t k = 0; k < Map::dim; ++k) {\n            identity.get(i, j)[l] += gsl::at(jacobian.get(i, k), l) *\n                                     gsl::at(inv_jacobian.get(k, j), l);\n          }\n        }\n      }\n    }\n    return identity;\n  }();\n  for (size_t i = 0; i < Map::dim; ++i) {\n    for (size_t j = 0; j < Map::dim; ++j) {\n      for (size_t k = 0; k < gsl::at(test_point, 0).size(); k++) {\n        CHECK(gsl::at(expected_identity.get(i, j), k) ==\n              approx(i == j ? 1. : 0.));\n      }\n    }\n  }\n}\n/// @}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Given a Map `map`, checks that the frame velocity matches a\n * sixth-order finite difference approximation.\n */\ntemplate <typename Map, typename T>\nvoid test_frame_velocity(\n    const Map& map, const std::array<T, Map::dim>& test_point,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0)) {\n  const auto compute_map_point = [&map, &test_point, &functions_of_time](\n                                     const std::array<double, 1>& time_point) {\n    return map(test_point, time_point[0], functions_of_time);\n  };\n  const double dt = 1e-4;\n\n  const auto frame_velocity =\n      map.frame_velocity(test_point, time, functions_of_time);\n  const auto numerical_frame_velocity = numerical_derivative(\n      compute_map_point, std::array<double, 1>{{time}}, 0, dt);\n  CHECK_ITERABLE_CUSTOM_APPROX(frame_velocity, numerical_frame_velocity,\n                               local_approx);\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Checks that the CoordinateMap `map` functions as expected when used as\n * the template parameter to the `CoordinateMap` type.\n */\ntemplate <typename Map, typename... Args>\nvoid test_coordinate_map_implementation(const Map& map) {\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map);\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1, 1);\n\n  const auto test_point = [&gen, &real_dis] {\n    std::array<double, Map::dim> p{};\n    for (size_t i = 0; i < Map::dim; ++i) {\n      gsl::at(p, i) = real_dis(gen);\n    }\n    return p;\n  }();\n\n  const auto test_point_tensor = [&test_point]() {\n    tnsr::I<double, Map::dim, Frame::BlockLogical> point_as_tensor{};\n    for (size_t i = 0; i < Map::dim; ++i) {\n      point_as_tensor.get(i) = gsl::at(test_point, i);\n    }\n    return point_as_tensor;\n  }();\n\n  for (size_t i = 0; i < Map::dim; ++i) {\n    CHECK(coord_map(test_point_tensor).get(i) ==\n          approx(gsl::at(map(test_point), i)));\n    for (size_t j = 0; j < Map::dim; ++j) {\n      CHECK(coord_map.jacobian(test_point_tensor).get(i, j) ==\n            map.jacobian(test_point).get(i, j));\n      CHECK(coord_map.inv_jacobian(test_point_tensor).get(i, j) ==\n            map.inv_jacobian(test_point).get(i, j));\n    }\n  }\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Checks that the CoordinateMap `map` functions as expected when used\n * with different argument types.\n */\ntemplate <typename Map, typename... Args>\nvoid test_coordinate_map_argument_types(\n    const Map& map, const std::array<double, Map::dim>& test_point,\n    const Args&... args) {\n  INFO(\"Test coordinate map argument types\");\n  CAPTURE(test_point);\n  const auto make_array_data_vector = [](const auto& double_array) {\n    std::array<DataVector, Map::dim> result;\n    std::transform(double_array.begin(), double_array.end(), result.begin(),\n                   [](const double x) {\n                     return DataVector{x, x};\n                   });\n    return result;\n  };\n  const auto add_reference_wrapper = [](const auto& unwrapped_array) {\n    using Arg = std::decay_t<decltype(unwrapped_array)>;\n    return make_array<std::reference_wrapper<const typename Arg::value_type>,\n                      Map::dim>(unwrapped_array);\n  };\n\n  {\n    INFO(\"Test call-operator\");\n    const auto mapped_point = map(test_point, args...);\n    CHECK_ITERABLE_APPROX(map(add_reference_wrapper(test_point), args...),\n                          mapped_point);\n    CHECK_ITERABLE_APPROX(map(make_array_data_vector(test_point), args...),\n                          make_array_data_vector(mapped_point));\n    CHECK_ITERABLE_APPROX(\n        map(add_reference_wrapper(make_array_data_vector(test_point)), args...),\n        make_array_data_vector(mapped_point));\n  }\n\n  // Here, time_args is a const auto& not const Args& because time_args\n  // is allowed to be different than Args (which was the reason for the\n  // overloader below that calls this function).\n  const auto check_jac = [](const auto& make_arr_data_vec,\n                            const auto& add_ref_wrap, const Map& the_map,\n                            const std::array<double, Map::dim>& point,\n                            const auto&... time_args) {\n    const auto make_tensor_data_vector = [](const auto& double_tensor) {\n      using Arg = std::decay_t<decltype(double_tensor)>;\n      Tensor<DataVector, typename Arg::symmetry, typename Arg::index_list>\n          result;\n      std::transform(double_tensor.begin(), double_tensor.end(), result.begin(),\n                     [](const double x) {\n                       return DataVector{x, x};\n                     });\n      return result;\n    };\n\n    {\n      INFO(\"Test Jacobian\");\n      const auto expected = the_map.jacobian(point, time_args...);\n      CHECK_ITERABLE_APPROX(the_map.jacobian(add_ref_wrap(point), time_args...),\n                            expected);\n      CHECK_ITERABLE_APPROX(\n          the_map.jacobian(make_arr_data_vec(point), time_args...),\n          make_tensor_data_vector(expected));\n      CHECK_ITERABLE_APPROX(\n          the_map.jacobian(add_ref_wrap(make_arr_data_vec(point)),\n                           time_args...),\n          make_tensor_data_vector(expected));\n    }\n    {\n      INFO(\"Test inverse Jacobian\");\n      const auto expected = the_map.inv_jacobian(point, time_args...);\n      // Set the scale according to the largest element of the inverse\n      // Jacobian. We can't resolve anything smaller than that in general\n      // since if we were to invert the Jacobian to get the inverse Jacobian,\n      // it's roughly the largest elements that set the limit.\n      Approx custom_approx = Approx::custom().epsilon(1.e-11).scale(\n          alg::accumulate(expected, std::numeric_limits<double>::lowest(),\n                          [](auto state, const double& element) {\n                            using std::max;\n                            return max(state, abs(element));\n                          }));\n      CHECK_ITERABLE_APPROX(\n          the_map.inv_jacobian(add_ref_wrap(point), time_args...), expected);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          the_map.inv_jacobian(make_arr_data_vec(point), time_args...),\n          make_tensor_data_vector(expected), custom_approx);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          the_map.inv_jacobian(add_ref_wrap(make_arr_data_vec(point)),\n                               time_args...),\n          make_tensor_data_vector(expected), custom_approx);\n    }\n\n    return nullptr;\n  };\n\n  if constexpr (domain::is_jacobian_time_dependent_t<decltype(map), double>{}) {\n    check_jac(make_array_data_vector, add_reference_wrapper, map, test_point,\n              args...);\n  } else {\n    check_jac(make_array_data_vector, add_reference_wrapper, map, test_point);\n  }\n}\n\n/// @{\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Given a Map `map`, checks that the inverse map gives expected results\n */\ntemplate <typename Map, typename T>\nvoid test_inverse_map(const Map& map,\n                      const std::array<T, Map::dim>& test_point) {\n  INFO(\"Test inverse map\");\n  CAPTURE(test_point);\n  const auto expected_test_point = map.inverse(map(test_point));\n  REQUIRE(expected_test_point.has_value());\n  CHECK_ITERABLE_APPROX(test_point, expected_test_point.value());\n}\n\ntemplate <typename Map, typename T>\nvoid test_inverse_map(\n    const Map& map, const std::array<T, Map::dim>& test_point,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    Approx local_approx = approx) {\n  INFO(\"Test inverse map time dependent\");\n  CAPTURE(test_point);\n  const auto expected_test_point = map.inverse(\n      map(test_point, time, functions_of_time), time, functions_of_time);\n  REQUIRE(expected_test_point.has_value());\n  CHECK_ITERABLE_CUSTOM_APPROX(test_point, expected_test_point.value(),\n                               local_approx);\n}\n/// @}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Given a Map `map`, tests the map functions, including map inverse,\n * jacobian, and inverse jacobian, for a series of points.\n * These points are chosen in a dim-dimensonal cube of side 2 centered at\n * the origin.  The map is expected to be valid on the boundaries of the cube.\n */\ntemplate <typename Map>\nvoid test_suite_for_map_on_unit_cube(const Map& map) {\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1.0, 1.0);\n\n  std::array<double, Map::dim> origin{};\n  std::array<double, Map::dim> random_point{};\n  for (size_t i = 0; i < Map::dim; i++) {\n    gsl::at(origin, i) = 0.0;\n    gsl::at(random_point, i) = real_dis(gen);\n  }\n\n  const auto test_helper = [&origin, &random_point](const auto& map_to_test) {\n    test_serialization(map_to_test);\n    CHECK_FALSE(map_to_test != map_to_test);\n    test_coordinate_map_argument_types(map_to_test, origin);\n\n    test_jacobian(map_to_test, origin);\n    test_inv_jacobian(map_to_test, origin);\n    test_inverse_map(map_to_test, origin);\n\n    for (VolumeCornerIterator<Map::dim> vci{}; vci; ++vci) {\n      test_jacobian(map_to_test, vci.coords_of_corner());\n      test_inv_jacobian(map_to_test, vci.coords_of_corner());\n      test_inverse_map(map_to_test, vci.coords_of_corner());\n    }\n\n    test_jacobian(map_to_test, random_point);\n    test_inv_jacobian(map_to_test, random_point);\n    test_inverse_map(map_to_test, random_point);\n  };\n  test_helper(map);\n  const auto map2 = serialize_and_deserialize(map);\n  check_if_maps_are_equal(\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map),\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map2));\n  test_helper(map2);\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Given a Map `map`, tests the map functions, including map inverse,\n * jacobian, and inverse jacobian, for a series of points.\n * These points are chosen in a sphere of radius `radius_of_sphere`, and the\n * map is expected to be valid on the boundary of that sphere as well as\n * in its interior.  The flag `include_origin` indicates whether to test the\n * map at the origin.\n * This test works only in 3 dimensions.\n */\ntemplate <typename Map>\nvoid test_suite_for_map_on_sphere(const Map& map,\n                                  const bool include_origin = true,\n                                  const double radius_of_sphere = 1.0) {\n  static_assert(Map::dim == 3,\n                \"test_suite_for_map_on_sphere works only for a 3d map\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n\n  // If we don't include the origin, we want to use some finite inner\n  // boundary so that random points stay away from the origin.\n  // test_jacobian has a dx of 1.e-4 for finite-differencing, so here\n  // we pick a value larger than that.\n  const double inner_bdry = include_origin ? 0.0 : 5.e-3;\n\n  std::uniform_real_distribution<> radius_dis(inner_bdry, radius_of_sphere);\n  std::uniform_real_distribution<> theta_dis(0, M_PI);\n  std::uniform_real_distribution<> phi_dis(0, 2.0 * M_PI);\n\n  const double theta = theta_dis(gen);\n  CAPTURE(theta);\n  const double phi = phi_dis(gen);\n  CAPTURE(phi);\n  const double radius = radius_dis(gen);\n  CAPTURE(radius);\n\n  const std::array<std::array<double, 3>, 3> points_to_test{\n      {// A random point in the interior\n       {radius * sin(theta) * cos(phi), radius * sin(theta) * sin(phi),\n        radius * cos(theta)},\n       // A random point on the outer boundary\n       {radius_of_sphere * sin(theta) * cos(phi),\n        radius_of_sphere * sin(theta) * sin(phi),\n        radius_of_sphere * cos(theta)},\n       // Either a point at the origin or (if include_origin is false)\n       // a random point on the inner boundary.\n       {inner_bdry * sin(theta) * cos(phi), inner_bdry * sin(theta) * sin(phi),\n        inner_bdry * cos(theta)}}};\n\n  const auto test_helper = [&points_to_test](const auto& map_to_test) {\n    test_serialization(map_to_test);\n    CHECK_FALSE(map_to_test != map_to_test);\n    for (const auto& point : points_to_test) {\n      test_coordinate_map_argument_types(map_to_test, point);\n      test_jacobian(map_to_test, point);\n      test_inv_jacobian(map_to_test, point);\n      test_inverse_map(map_to_test, point);\n    };\n  };\n\n  test_helper(map);\n  const auto map2 = serialize_and_deserialize(map);\n  check_if_maps_are_equal(\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map),\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map2));\n  test_helper(map2);\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Given a Map `map`, tests the map functions, including map inverse,\n * jacobian, and inverse jacobian, for a series of points.\n * These points are chosen in a right cylinder with cylindrical radius 1\n * and z-axis extending from -1 to +1. The\n * map is expected to be valid on the boundary of that cylinder as well as\n * in its interior.\n * This test works only in 3 dimensions.\n */\ntemplate <typename Map>\nvoid test_suite_for_map_on_cylinder(\n    const Map& map, const double inner_radius, const double outer_radius,\n    const bool test_random_z_bdry_roundoff = false,\n    const bool test_random_rho_bdry_roundoff = false) {\n  static_assert(Map::dim == 3,\n                \"test_suite_for_map_on_cylinder works only for a 3d map\");\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n\n  std::uniform_real_distribution<> radius_dis(inner_radius, outer_radius);\n  std::uniform_real_distribution<> phi_dis(0.0, 2.0 * M_PI);\n  std::uniform_real_distribution<> height_dis(-1.0, 1.0);\n\n  const double height = height_dis(gen);\n  CAPTURE(height);\n  const double phi = phi_dis(gen);\n  CAPTURE(phi);\n  const double radius = radius_dis(gen);\n  CAPTURE(radius);\n\n  const std::array<double, 3> random_point{\n      {radius * cos(phi), radius * sin(phi), height}};\n\n  const std::array<double, 3> random_bdry_point_rho{\n      {outer_radius * cos(phi), outer_radius * sin(phi), height}};\n\n  const std::array<double, 3> random_bdry_point_z{\n      {radius * cos(phi), radius * sin(phi), height > 0.5 ? 1.0 : -1.0}};\n\n  const std::array<double, 3> random_bdry_point_corner{\n      {outer_radius * cos(phi), outer_radius * sin(phi),\n       height > 0.5 ? 1.0 : -1.0}};\n\n  // If inner_radius is zero, this point is on the axis.\n  const std::array<double, 3> random_inner_bdry_point_or_origin{\n      {inner_radius * cos(phi), inner_radius * sin(phi), height}};\n\n  // If inner_radius is zero, this point is on the axis.\n  const std::array<double, 3> random_inner_bdry_point_corner{\n      {inner_radius * cos(phi), inner_radius * sin(phi),\n       height > 0.5 ? 1.0 : -1.0}};\n\n  const auto test_helper =\n      [](const auto& map_to_test,\n         const std::vector<std::array<double, 3>>& points_to_test) {\n        test_serialization(map_to_test);\n        CHECK_FALSE(map_to_test != map_to_test);\n\n        for (const auto& point : points_to_test) {\n          test_coordinate_map_argument_types(map_to_test, point);\n          test_jacobian(map_to_test, point);\n          test_inv_jacobian(map_to_test, point);\n          test_inverse_map(map_to_test, point);\n        }\n      };\n\n  const auto test_helper_all_points = [&test_helper, &random_bdry_point_rho,\n                                       &random_bdry_point_z,\n                                       &random_bdry_point_corner,\n                                       &random_inner_bdry_point_or_origin,\n                                       &random_inner_bdry_point_corner,\n                                       &random_point](const auto& map_to_test) {\n    test_helper(map_to_test,\n                {random_bdry_point_rho, random_bdry_point_z,\n                 random_bdry_point_corner, random_inner_bdry_point_or_origin,\n                 random_inner_bdry_point_corner, random_point});\n  };\n\n  // Test points that are within roundoff of the +/- z faces of the\n  // cylinder.  We test multiple points to increase the probability\n  // that we hit all of the relevant cases, since it is hard to\n  // predict the details of whether roundoff makes certain\n  // expressions in the map slightly smaller or slightly larger than\n  // they should be.\n  const auto test_helper_z_roundoff_points =\n      [&gen, &height_dis, &radius, &phi,\n       &test_helper](const auto& map_to_test) {\n        for (size_t i = 0; i < 50; ++i) {\n          const double z_roundoff = 1.e-15 * height_dis(gen);\n          CAPTURE(z_roundoff);\n          const std::array<double, 3> random_bdry_point_roundoff{\n              {radius * cos(phi), radius * sin(phi), 1.0 + z_roundoff}};\n          const std::array<double, 3> random_bdry_point_minus_roundoff{\n              {radius * cos(phi), radius * sin(phi), -1.0 + z_roundoff}};\n          test_helper(map_to_test, {random_bdry_point_roundoff,\n                                    random_bdry_point_minus_roundoff});\n        }\n      };\n  const auto test_helper_rho_roundoff_points =\n      [&gen, &radius_dis, &inner_radius, &outer_radius, &phi, &height,\n       &test_helper](const auto& map_to_test) {\n        for (size_t i = 0; i < 25; ++i) {\n          const double rho_roundoff = 1.e-15 * radius_dis(gen) / outer_radius;\n          CAPTURE(rho_roundoff);\n          const std::array<double, 3> random_bdry_point_inner_roundoff{\n              {(inner_radius + rho_roundoff) * cos(phi),\n               (inner_radius + rho_roundoff) * sin(phi), height}};\n          const std::array<double, 3> random_bdry_point_outer_roundoff{\n              {(outer_radius + rho_roundoff) * cos(phi),\n               (outer_radius + rho_roundoff) * sin(phi), height}};\n          const std::array<double, 3> random_bdry_point_outer_minus_roundoff{\n              {(outer_radius - rho_roundoff) * cos(phi),\n               (outer_radius - rho_roundoff) * sin(phi), height}};\n          if (inner_radius != 0.0) {\n            const std::array<double, 3> random_bdry_point_inner_minus_roundoff{\n                {(inner_radius - rho_roundoff) * cos(phi),\n                 (inner_radius - rho_roundoff) * sin(phi), height}};\n            test_helper(map_to_test, {random_bdry_point_inner_roundoff,\n                                      random_bdry_point_outer_roundoff,\n                                      random_bdry_point_outer_minus_roundoff,\n                                      random_bdry_point_inner_minus_roundoff});\n          } else {\n            // If inner_radius is zero, ignore\n            // random_bdry_point_inner_minus_roundoff because that\n            // would make the radius negative.\n            test_helper(map_to_test, {random_bdry_point_inner_roundoff,\n                                      random_bdry_point_outer_roundoff,\n                                      random_bdry_point_outer_minus_roundoff});\n          }\n        }\n      };\n\n  test_helper_all_points(map);\n  if (test_random_z_bdry_roundoff) {\n    test_helper_z_roundoff_points(map);\n  }\n  if (test_random_rho_bdry_roundoff) {\n    test_helper_rho_roundoff_points(map);\n  }\n  const auto map2 = serialize_and_deserialize(map);\n  check_if_maps_are_equal(\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map),\n      domain::make_coordinate_map<Frame::BlockLogical, Frame::Grid>(map2));\n  test_helper_all_points(map2);\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief An iterator for looping through all possible orientations\n * of the n-dim cube.\n */\ntemplate <size_t VolumeDim>\nclass OrientationMapIterator {\n public:\n  OrientationMapIterator() {\n    std::iota(dims_.begin(), dims_.end(), 0);\n    set_map();\n  }\n  void operator++() {\n    ++vci_;\n    if (not vci_) {\n      not_at_end_ = std::next_permutation(dims_.begin(), dims_.end());\n      vci_ = VolumeCornerIterator<VolumeDim>{};\n    }\n    set_map();\n  }\n  explicit operator bool() const { return not_at_end_; }\n  const OrientationMap<VolumeDim>& operator()() const { return map_; }\n  const OrientationMap<VolumeDim>& operator*() const { return map_; }\n  void set_map() {\n    for (size_t i = 0; i < VolumeDim; i++) {\n      gsl::at(directions_, i) =\n          Direction<VolumeDim>{gsl::at(dims_, i), gsl::at(vci_(), i)};\n    }\n    map_ = OrientationMap<VolumeDim>{directions_};\n  }\n\n private:\n  bool not_at_end_ = true;\n  std::array<size_t, VolumeDim> dims_{};\n  std::array<Direction<VolumeDim>, VolumeDim> directions_{};\n  VolumeCornerIterator<VolumeDim> vci_{};\n  OrientationMap<VolumeDim> map_ = OrientationMap<VolumeDim>::create_aligned();\n};\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Wedge OrientationMap in each of the six directions used in the\n * Sphere domain creator.\n */\ninline std::array<OrientationMap<3>, 6> all_wedge_directions() {\n  const OrientationMap<3> upper_zeta_rotation =\n      OrientationMap<3>::create_aligned();\n  const OrientationMap<3> lower_zeta_rotation(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_xi(), Direction<3>::lower_eta(),\n       Direction<3>::lower_zeta()}});\n  const OrientationMap<3> upper_eta_rotation(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::upper_zeta(),\n       Direction<3>::upper_xi()}});\n  const OrientationMap<3> lower_eta_rotation(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_eta(), Direction<3>::lower_zeta(),\n       Direction<3>::lower_xi()}});\n  const OrientationMap<3> upper_xi_rotation(std::array<Direction<3>, 3>{\n      {Direction<3>::upper_zeta(), Direction<3>::upper_xi(),\n       Direction<3>::upper_eta()}});\n  const OrientationMap<3> lower_xi_rotation(std::array<Direction<3>, 3>{\n      {Direction<3>::lower_zeta(), Direction<3>::lower_xi(),\n       Direction<3>::upper_eta()}});\n  return {{upper_zeta_rotation, lower_zeta_rotation, upper_eta_rotation,\n           lower_eta_rotation, upper_xi_rotation, lower_xi_rotation}};\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Creators/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Tuple.hpp\"\n\nnamespace TestHelpers::domain::creators {\n\ntemplate <size_t Dim>\nDomain<Dim> test_domain_creator(const DomainCreator<Dim>& domain_creator,\n                                const bool expect_boundary_conditions,\n                                const bool is_periodic = false,\n                                const std::vector<double>& times = {\n                                    // quiet NaN so CAPTURE(time) works\n                                    std::numeric_limits<double>::quiet_NaN()}) {\n  INFO(\"Test domain creator consistency\");\n  CAPTURE(Dim);\n  auto domain = domain_creator.create_domain();\n  const auto block_names = domain_creator.block_names();\n  const auto block_groups = domain_creator.block_groups();\n  const auto all_boundary_conditions =\n      domain_creator.external_boundary_conditions();\n  const auto initial_refinement_levels =\n      domain_creator.initial_refinement_levels();\n  const auto initial_extents = domain_creator.initial_extents();\n\n  const auto& blocks = domain.blocks();\n  REQUIRE(initial_refinement_levels.size() == blocks.size());\n  REQUIRE(initial_extents.size() == blocks.size());\n  {\n    CAPTURE(block_names);\n    CHECK((block_names.empty() or (block_names.size() == blocks.size())));\n    for (size_t block_id = 0; block_id < blocks.size(); ++block_id) {\n      CHECK(blocks[block_id].name() ==\n            (block_names.empty() ? \"\" : block_names[block_id]));\n    }\n    CHECK(domain.block_groups() == block_groups);\n    {\n      INFO(\"Test block names are unique\");\n      auto sorted_block_names = block_names;\n      alg::sort(sorted_block_names);\n      CHECK(std::adjacent_find(sorted_block_names.begin(),\n                               sorted_block_names.end()) ==\n            sorted_block_names.end());\n    }\n    {\n      INFO(\"Test block groups contain valid block names\");\n      for (const auto& [block_group, block_names_in_group] : block_groups) {\n        CAPTURE(block_group);\n        for (const auto& block_name : block_names_in_group) {\n          CAPTURE(block_name);\n          CHECK(alg::find(block_names, block_name) != block_names.end());\n        }\n      }\n    }\n    {\n      INFO(\n          \"Test block neighbors are never in the same direction as external \"\n          \"boundaries\");\n      for (size_t block_id = 0; block_id < block_names.size(); ++block_id) {\n        for (const auto& neighbor : blocks[block_id].neighbors()) {\n          // external and neighbor directions should never match\n          const auto& external_boundaries =\n              blocks[block_id].external_boundaries();\n          CHECK(external_boundaries.find(neighbor.first) ==\n                external_boundaries.end());\n        }\n      }\n    }\n  }\n\n  ::domain::creators::register_derived_with_charm();\n  ::domain::creators::time_dependence::register_derived_with_charm();\n  test_serialization(domain);\n\n  test_initial_domain(domain, initial_refinement_levels);\n  const auto functions_of_time = domain_creator.functions_of_time();\n  for (const double time : times) {\n    CAPTURE(time);\n    if (not is_periodic) {\n      test_physical_separation(domain.blocks(), time, functions_of_time);\n    }\n    // The 1D RotatedIntervals domain creator violates this condition\n    if constexpr (Dim != 1) {\n      test_det_jac_positive(domain.blocks(), time, functions_of_time);\n    }\n  }\n\n  if (expect_boundary_conditions) {\n    INFO(\"Boundary conditions\");\n    REQUIRE(all_boundary_conditions.size() == blocks.size());\n    for (size_t block_id = 0; block_id < blocks.size(); ++block_id) {\n      CAPTURE(block_id);\n      const auto& block = blocks[block_id];\n      const auto& boundary_conditions = all_boundary_conditions[block_id];\n      const auto& external_boundaries = block.external_boundaries();\n      REQUIRE(boundary_conditions.size() == external_boundaries.size());\n      for (const auto& direction : Direction<Dim>::all_directions()) {\n        CAPTURE(direction);\n        if (external_boundaries.find(direction) == external_boundaries.end()) {\n          INFO(\"Internal boundary should not specify a boundary condition\");\n          CHECK(boundary_conditions.find(direction) ==\n                boundary_conditions.end());\n        } else {\n          INFO(\"External boundary is missing a boundary condition\");\n          REQUIRE(boundary_conditions.find(direction) !=\n                  boundary_conditions.end());\n          REQUIRE(boundary_conditions.at(direction) != nullptr);\n        }\n      }\n    }\n  } else {\n    CHECK(all_boundary_conditions.empty());\n  }\n\n  // Check that every direction in every excision_sphere is also an\n  // external boundary of the correct Block.\n  for (const auto& excision_sphere_map_element : domain.excision_spheres()) {\n    for (const auto& [block_index, direction] :\n         excision_sphere_map_element.second.abutting_directions()) {\n      const auto& external_boundaries =\n          domain.blocks()[block_index].external_boundaries();\n      CHECK(external_boundaries.find(direction) != external_boundaries.end());\n    }\n  }\n\n  return domain;\n}\n\n/// Helper function to factory-create a domain creator in tests with or without\n/// boundary conditions\ntemplate <typename Creator, size_t Dim = Creator::volume_dim>\nvoid test_creation(const std::string& option_string, const Creator& rhs,\n                   const bool with_boundary_conditions) {\n  INFO(\"Option-creation\");\n  CAPTURE(option_string);\n  auto created = [&option_string, &with_boundary_conditions]() {\n    if (with_boundary_conditions) {\n      using metavars = TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithBoundaryConditions<Dim, Creator>;\n      return TestHelpers::test_option_tag<\n          ::domain::OptionTags::DomainCreator<Dim>, metavars>(option_string);\n    } else {\n      using metavars = TestHelpers::domain::BoundaryConditions::\n          MetavariablesWithoutBoundaryConditions<Dim, Creator>;\n      return TestHelpers::test_option_tag<\n          ::domain::OptionTags::DomainCreator<Dim>, metavars>(option_string);\n    }\n  }();\n  REQUIRE(dynamic_cast<const Creator*>(created.get()) != nullptr);\n  // Check equality of domain creators by comparing the subset of properties\n  // that support comparison\n  const auto& lhs = *created;\n  CHECK(lhs.create_domain() == rhs.create_domain());\n  CHECK(lhs.initial_extents() == rhs.initial_extents());\n  CHECK(lhs.initial_refinement_levels() == rhs.initial_refinement_levels());\n}\n\ntemplate <size_t Dim, typename... ExpectedFunctionsOfTime>\nvoid test_functions_of_time(\n    const DomainCreator<Dim>& creator,\n    const std::tuple<std::pair<std::string, ExpectedFunctionsOfTime>...>&\n        expected_functions_of_time,\n    const std::unordered_map<std::string, double>& initial_expiration_times =\n        {}) {\n  const std::unordered_map<\n      std::string, std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n      functions_of_time = creator.functions_of_time(initial_expiration_times);\n  REQUIRE(functions_of_time.size() == sizeof...(ExpectedFunctionsOfTime));\n\n  tuple_fold(expected_functions_of_time,\n             [&functions_of_time](const auto& name_and_function_of_time) {\n               const std::string& name = name_and_function_of_time.first;\n               const auto& function_of_time = name_and_function_of_time.second;\n               using FunctionOfTimeType =\n                   std::decay_t<decltype(function_of_time)>;\n               const bool in_functions_of_time =\n                   functions_of_time.find(name) != functions_of_time.end();\n               // NOLINTNEXTLINE(bugprone-infinite-loop) false positive\n               CHECK(in_functions_of_time);\n               if (in_functions_of_time) {\n                 const auto* function_from_creator =\n                     dynamic_cast<const FunctionOfTimeType*>(\n                         functions_of_time.at(name).get());\n                 REQUIRE(function_from_creator != nullptr);\n                 CHECK(*function_from_creator == function_of_time);\n               }\n             });\n}\n}  // namespace TestHelpers::domain::creators\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Creators/TimeDependence/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <random>\n\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n\n/*!\n * \\brief Generates random coordinates of double and DataVector types.\n *\n * Creates:\n * - `std::uniform_real_distribution<double>` named `dist` on interval\n *   `LOWER_BOUND` and `UPPER_BOUND`\n * - `grid_coords_dv` of type `tnsr::I<DataVector, DIM, Frame::Grid>`\n * - `grid_coords_double` of type `tnsr::I<double, DIMN, Frame::Grid>`\n * - `inertial_coords_dv` of type `tnsr::I<DataVector, DIM, Frame::Inertial>`\n * - `inertial_coords_double` of type `tnsr::I<double, DIMN, Frame::Inertial>`\n *\n * The argument `GENERATOR` must be a `gsl::not_null` to a random number\n * generator. Typically the generator would be created using the\n * `MAKE_GENERATOR` macro.\n */\n#define TIME_DEPENDENCE_GENERATE_COORDS(GENERATOR, DIM, LOWER_BOUND,      \\\n                                        UPPER_BOUND)                      \\\n  std::uniform_real_distribution<double> dist{LOWER_BOUND, UPPER_BOUND};  \\\n  const auto grid_coords_dv =                                             \\\n      make_with_random_values<tnsr::I<DataVector, DIM, Frame::Grid>>(     \\\n          GENERATOR, make_not_null(&dist), DataVector{5});                \\\n  const auto grid_coords_double =                                         \\\n      make_with_random_values<tnsr::I<double, DIM, Frame::Grid>>(         \\\n          GENERATOR, make_not_null(&dist));                               \\\n  const auto inertial_coords_dv =                                         \\\n      make_with_random_values<tnsr::I<DataVector, DIM, Frame::Inertial>>( \\\n          GENERATOR, make_not_null(&dist), DataVector{5});                \\\n  const auto inertial_coords_double =                                     \\\n      make_with_random_values<tnsr::I<double, DIM, Frame::Inertial>>(     \\\n          GENERATOR, make_not_null(&dist))\n\n/*!\n * \\brief Generates random coordinates of double and DataVector types.\n *\n * Same as TIME_DEPENDENCE_GENERATE_COORDS except:\n * - It creates only distorted-frame coords.\n * - It takes a DIST as an argument, since in typical usage one would\n *   call TIME_DEPENDENCE_GENERATE_COORDS and then call\n *   TIME_DEPENDENCE_GENERATE_DISTORTED_COORDS.\n *\n */\n#define TIME_DEPENDENCE_GENERATE_DISTORTED_COORDS(GENERATOR, DIST, DIM)    \\\n  const auto distorted_coords_dv =                                         \\\n      make_with_random_values<tnsr::I<DataVector, DIM, Frame::Distorted>>( \\\n          GENERATOR, make_not_null(&dist), DataVector{5});                 \\\n  const auto distorted_coords_double =                                     \\\n      make_with_random_values<tnsr::I<double, DIM, Frame::Distorted>>(     \\\n          GENERATOR, make_not_null(&dist));\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Creators/TimeDependent/TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Domain/Creators/TimeDependent/TestHelpers.hpp\"\n\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace TestHelpers::domain::creators {\nvoid write_volume_data(\n    const std::string& filename, const std::string& subfile_name,\n    const double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  h5::H5File<h5::AccessType::ReadWrite> h5_file{filename, true};\n  auto& vol_file = h5_file.insert<h5::VolumeData>(subfile_name);\n\n  const auto serialized_fots =\n      functions_of_time.empty() ? std::nullopt\n                                : std::optional{serialize(functions_of_time)};\n  // We don't care about the volume data here, just the functions of time\n  vol_file.write_volume_data(\n      0, time,\n      {ElementVolumeData{\"blah\",\n                         {TensorComponent{\"RandomTensor\", DataVector{3, 0.0}}},\n                         {3},\n                         {Spectral::Basis::Legendre},\n                         {Spectral::Quadrature::GaussLobatto}}},\n      std::nullopt, serialized_fots, serialized_fots);\n}\n}  // namespace TestHelpers::domain::creators\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Creators/TimeDependent/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n\nnamespace TestHelpers::domain::creators {\nvoid write_volume_data(\n    const std::string& filename, const std::string& subfile_name, double time,\n    const std::unordered_map<\n        std::string,\n        std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time = {});\n}  // namespace TestHelpers::domain::creators\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/DomainTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <ios>\n#include <limits>\n#include <memory>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ForceInline.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\ntemplate <size_t VolumeDim>\nclass Element;\n\nnamespace domain {\nnamespace {\ntemplate <size_t VolumeDim>\nboost::rational<size_t> fraction_of_block_face_area(\n    const ElementId<VolumeDim>& element_id,\n    const Direction<VolumeDim>& direction) {\n  const auto segment_ids = element_id.segment_ids();\n  size_t sum_of_refinement_levels = 0;\n  const size_t dim_normal_to_face = direction.dimension();\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    if (dim_normal_to_face != d) {\n      sum_of_refinement_levels += gsl::at(segment_ids, d).refinement_level();\n    }\n  }\n  return {1, two_to_the(sum_of_refinement_levels)};\n}\n\ntemplate <size_t VolumeDim>\nbool is_a_neighbor_of_my_neighbor_me(\n    const ElementId<VolumeDim>& my_id, const Element<VolumeDim>& my_neighbor,\n    const Direction<VolumeDim>& direction_to_me_in_neighbor) {\n  size_t number_of_matches = 0;\n  for (const auto& id_of_neighbor_of_neighbor :\n       my_neighbor.neighbors().at(direction_to_me_in_neighbor).ids()) {\n    if (my_id == id_of_neighbor_of_neighbor) {\n      ++number_of_matches;\n    }\n  }\n  return 1 == number_of_matches;\n}\n\ntemplate <size_t VolumeDim>\nvoid test_domain_connectivity(\n    const Domain<VolumeDim>& domain,\n    const std::unordered_map<ElementId<VolumeDim>, Element<VolumeDim>>&\n        elements_in_domain) {\n  boost::rational<size_t> volume_of_elements{0};\n  boost::rational<size_t> surface_area_of_external_boundaries{0};\n  // For internal boundaries within a Block, lower/upper is defined with\n  // respect to the logical coordinates of the block\n  // for internal boundaries between two Blocks, lower/upper is defined\n  // using the id of the Block (the Block with the smaller/larger id being\n  // associated with the lower/upper internal boundary).\n  boost::rational<size_t> surface_area_of_lower_internal_boundaries{0};\n  boost::rational<size_t> surface_area_of_upper_internal_boundaries{0};\n\n  for (const auto& key_value : elements_in_domain) {\n    const auto& element_id = key_value.first;\n    const auto& element = key_value.second;\n    CHECK(element_id == element.id());\n    volume_of_elements += fraction_of_block_volume(element_id);\n    for (const auto& direction : element.external_boundaries()) {\n      surface_area_of_external_boundaries +=\n          fraction_of_block_face_area(element_id, direction);\n    }\n    for (const auto& direction_neighbors : element.neighbors()) {\n      const auto& direction = direction_neighbors.first;\n      const auto& neighbors = direction_neighbors.second;\n      for (const auto& neighbor_id : neighbors.ids()) {\n        const auto& direction_to_me_in_neighbor =\n            neighbors.orientation(neighbor_id)(direction.opposite());\n        CHECK(is_a_neighbor_of_my_neighbor_me(\n            element_id, elements_in_domain.at(neighbor_id),\n            direction_to_me_in_neighbor));\n      }\n      const size_t element_block_id = element_id.block_id();\n      const size_t neighbor_block_id = neighbors.ids().begin()->block_id();\n      if (element_block_id == neighbor_block_id) {\n        if (Side::Lower == direction.side()) {\n          surface_area_of_lower_internal_boundaries +=\n              fraction_of_block_face_area(element_id, direction);\n        } else {\n          surface_area_of_upper_internal_boundaries +=\n              fraction_of_block_face_area(element_id, direction);\n        }\n      } else {\n        if (neighbors.are_conforming()) {\n          if (neighbor_block_id > element_block_id) {\n            surface_area_of_lower_internal_boundaries +=\n                fraction_of_block_face_area(element_id, direction);\n          } else {\n            surface_area_of_upper_internal_boundaries +=\n                fraction_of_block_face_area(element_id, direction);\n          }\n        }\n      }\n    }\n  }\n\n  CHECK(boost::rational<size_t>{domain.blocks().size()} == volume_of_elements);\n  CHECK(surface_area_of_lower_internal_boundaries ==\n        surface_area_of_upper_internal_boundaries);\n  size_t number_of_external_block_faces{0};\n  for (const auto& block : domain.blocks()) {\n    number_of_external_block_faces += block.external_boundaries().size();\n  }\n  CHECK(boost::rational<size_t>{number_of_external_block_faces} ==\n        surface_area_of_external_boundaries);\n}\n\ntemplate <size_t AllowedDifference>\nSPECTRE_ALWAYS_INLINE void check_if_levels_are_within(const size_t level_1,\n                                                      const size_t level_2) {\n  // clang-tidy complains about a catch-internal do {} while (false) loop.\n  // NOLINTNEXTLINE(bugprone-infinite-loop)\n  CHECK(level_1 <= level_2 + AllowedDifference);\n  // NOLINTNEXTLINE(bugprone-infinite-loop)\n  CHECK(level_2 <= level_1 + AllowedDifference);\n}\n\ntemplate <size_t AllowedTangentialDifference, size_t VolumeDim>\nvoid test_refinement_levels_of_neighbors(\n    const std::unordered_map<ElementId<VolumeDim>, Element<VolumeDim>>&\n        elements) {\n  for (const auto& key_value : elements) {\n    const auto& element_id = key_value.first;\n    const auto& element = key_value.second;\n    for (const auto& direction_neighbors : element.neighbors()) {\n      const auto& neighbors = direction_neighbors.second;\n      if (neighbors.are_conforming()) {\n        for (size_t d = 0; d < VolumeDim; ++d) {\n          // No restriction on perpendicular refinement.\n          if (d == direction_neighbors.first.dimension()) {\n            continue;\n          }\n\n          const size_t my_level = element_id.segment_id(d).refinement_level();\n          for (const auto neighbor_id : neighbors.ids()) {\n            const auto& orientation = neighbors.orientation(neighbor_id);\n            const size_t my_dim_in_neighbor = orientation(d);\n            const size_t neighbor_level =\n                neighbor_id.segment_id(my_dim_in_neighbor).refinement_level();\n            check_if_levels_are_within<AllowedTangentialDifference>(\n                my_level, neighbor_level);\n          }\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\nbool blocks_are_neighbors(const Block<VolumeDim>& host_block,\n                          const Block<VolumeDim>& neighbor_block) {\n  return alg::any_of(\n      host_block.neighbors(), [&neighbor_block](const auto& neighbor) {\n        return neighbor.second.ids().contains(neighbor_block.id());\n      });\n}\n\n// Finds the OrientationMap of a neighboring Block relative to a host Block.\ntemplate <size_t VolumeDim>\nOrientationMap<VolumeDim> find_neighbor_orientation(\n    const Block<VolumeDim>& host_block,\n    const Block<VolumeDim>& neighbor_block) {\n  for (const auto& neighbor : host_block.neighbors()) {\n    if (neighbor.second.ids().contains(neighbor_block.id())) {\n      return neighbor.second.orientation(neighbor_block.id());\n    }\n  }\n  ERROR(\"The Block `neighbor_block` is not a neighbor of `host_block`.\");\n}\n\n// Finds the Direction to the neighboring Block relative to a host Block.\ntemplate <size_t VolumeDim>\nDirection<VolumeDim> find_direction_to_neighbor(\n    const Block<VolumeDim>& host_block,\n    const Block<VolumeDim>& neighbor_block) {\n  for (const auto& neighbor : host_block.neighbors()) {\n    if (neighbor.second.ids().contains(neighbor_block.id())) {\n      return neighbor.first;\n    }\n  }\n  ERROR(\"The Block `neighbor_block` is not a neighbor of `host_block`.\");\n}\n\n// Convert Point to Directions, for use with OrientationMap\ntemplate <size_t VolumeDim>\nstd::array<Direction<VolumeDim>, VolumeDim> get_orthant(\n    const tnsr::I<double, VolumeDim, Frame::BlockLogical>& point) {\n  std::array<Direction<VolumeDim>, VolumeDim> result;\n  for (size_t i = 0; i < VolumeDim; i++) {\n    gsl::at(result, i) =\n        Direction<VolumeDim>(i, point[i] >= 0 ? Side::Upper : Side::Lower);\n  }\n  return result;\n}\n\n// Convert Directions to Point, for use with CoordinateMap\ntemplate <size_t VolumeDim>\ntnsr::I<double, VolumeDim, Frame::BlockLogical> get_corner_of_orthant(\n    const std::array<Direction<VolumeDim>, VolumeDim>& directions) {\n  tnsr::I<double, VolumeDim, Frame::BlockLogical> result{};\n  for (size_t i = 0; i < VolumeDim; i++) {\n    result[gsl::at(directions, i).dimension()] =\n        gsl::at(directions, i).side() == Side::Upper ? 1.0 : -1.0;\n  }\n  return result;\n}\n\n// The relative OrientationMap between Blocks induces a map that takes\n// Points in the host Block to Points in the neighbor Block.\ntemplate <size_t VolumeDim>\ntnsr::I<double, VolumeDim, Frame::BlockLogical> point_in_neighbor_frame(\n    const OrientationMap<VolumeDim>& orientation,\n    const tnsr::I<double, VolumeDim, Frame::BlockLogical>& point) {\n  auto point_get_orthant = get_orthant(point);\n  std::for_each(\n      point_get_orthant.begin(), point_get_orthant.end(),\n      [&orientation](auto& direction) { direction = orientation(direction); });\n  return get_corner_of_orthant(point_get_orthant);\n}\n\n// This tests whether a logical grid point on the face of the host Block\n// corresponds to the same logical grid point on the abutting face of the\n// neighbor Block (taking into account the discrete rotation of the\n// OrientationMap from the host Block to the neighbor Block).\ntemplate <size_t VolumeDim>\nvoid check_block_face_grid_points_align(\n    const Block<VolumeDim>& host_block, const Block<VolumeDim>& neighbor_block,\n    double time = std::numeric_limits<double>::signaling_NaN(),\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time = {}) {\n  const auto direction = find_direction_to_neighbor(host_block, neighbor_block);\n  const auto orientation =\n      find_neighbor_orientation(host_block, neighbor_block);\n  // Set up a Mesh on the shared face. Corner points are already checked by\n  // physical_separation, so check on Gauss points\n  Mesh<VolumeDim - 1> face_mesh;\n  if (host_block.topologies()[VolumeDim - 1] ==\n      domain::Topology::CartoonCylinder) {\n    if constexpr (VolumeDim == 3) {\n      face_mesh = Mesh<VolumeDim - 1>{\n          {3, 1},\n          {Spectral::Basis::Legendre, Spectral::Basis::Cartoon},\n          {Spectral::Quadrature::Gauss, Spectral::Quadrature::AxialSymmetry}};\n    } else {\n      ERROR(\"Cartoon basis used with non 3D mesh, got dim = \" << VolumeDim);\n    }\n  } else {\n    face_mesh = Mesh<VolumeDim - 1>{3_st, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::Gauss};\n  }\n  // We want block logical coordinates on face_mesh on the host side and the\n  // neighbor. The following returns element logical coordinates in the host\n  // frame, which we then copy into tensors holding block logical coordinates,\n  // taking into account the OrientationMap between the host and neighbor\n  // blocks.\n  tnsr::I<DataVector, VolumeDim, Frame::ElementLogical> xi =\n      interface_logical_coordinates(face_mesh, direction);\n  tnsr::I<DataVector, VolumeDim, Frame::BlockLogical> xi_host{};\n  tnsr::I<DataVector, VolumeDim, Frame::BlockLogical> xi_neighbor{};\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    // assume an Element covering the Block which maps element logical\n    // coordinates to block logical coordinates with the identity map\n    xi_host[d] = xi[d];\n    const auto dth_upper_direction_in_neighbor_frame =\n        orientation(Direction<VolumeDim>(d, Side::Upper));\n    // This takes into account the permutation of dimensions induced by the\n    // OrientationMap\n    xi_neighbor[dth_upper_direction_in_neighbor_frame.dimension()] = xi[d];\n    // There is a sign flip if this is the dimension of the direction to the\n    // neighbor as the logical coord is -1/+1 on the lower/upper side of the\n    // block.\n    // There is also a sign flip if the mapped upper direction becomes a lower\n    // direction\n    if ((dth_upper_direction_in_neighbor_frame.side() == Side::Lower) xor\n        (d == direction.dimension())) {\n      xi_neighbor[dth_upper_direction_in_neighbor_frame.dimension()] *= -1.0;\n    }\n  }\n  if (host_block.is_time_dependent() != neighbor_block.is_time_dependent()) {\n    ERROR(\n        \"Both host_block and neighbor_block must have the same time \"\n        \"dependence, but host_block has time-dependence status: \"\n        << std::boolalpha << host_block.is_time_dependent()\n        << \" and neighbor_block has: \" << neighbor_block.is_time_dependent());\n  }\n  CAPTURE(xi_host);\n  CAPTURE(xi_neighbor);\n  // Check that each grid point on the face Mesh has the same grid and\n  // inertial coordinates when mapped from the logical coordinates of each\n  // Block.\n  if (host_block.is_time_dependent()) {\n    ASSERT(not std::isnan(time),\n           \"Blocks have time dependent maps but a time to evaluate at was \"\n           \"not passed\");\n    const auto& host_map_logical_to_grid =\n        host_block.moving_mesh_logical_to_grid_map();\n    const auto& host_map_grid_to_inertial =\n        host_block.moving_mesh_grid_to_inertial_map();\n    const auto& neighbor_map_logical_to_grid =\n        neighbor_block.moving_mesh_logical_to_grid_map();\n    const auto& neighbor_map_grid_to_inertial =\n        neighbor_block.moving_mesh_grid_to_inertial_map();\n    const auto x_grid_self = host_map_logical_to_grid(xi_host);\n    const auto x_grid_neighbor = neighbor_map_logical_to_grid(xi_neighbor);\n    CHECK_ITERABLE_APPROX(x_grid_self, x_grid_neighbor);\n    const auto x_inertial_self =\n        host_map_grid_to_inertial(x_grid_self, time, functions_of_time);\n    const auto x_inertial_neighbor =\n        neighbor_map_grid_to_inertial(x_grid_neighbor, time, functions_of_time);\n    CHECK_ITERABLE_APPROX(x_inertial_self, x_inertial_neighbor);\n  } else {\n    const auto& host_map = host_block.stationary_map();\n    const auto& neighbor_map = neighbor_block.stationary_map();\n    const auto x_self = host_map(xi_host);\n    const auto x_neighbor = neighbor_map(xi_neighbor);\n    CHECK_ITERABLE_APPROX(x_self, x_neighbor);\n  }\n}\n\n// Given two Blocks which are neighbors, computes the max separation between\n// the abutting faces of the Blocks in the Frame::Inertial frame using the\n// CoordinateMaps of each block.\ntemplate <size_t VolumeDim>\ndouble physical_separation(\n    const Block<VolumeDim>& block1, const Block<VolumeDim>& block2,\n    double time = std::numeric_limits<double>::signaling_NaN(),\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time = {}) {\n  double max_separation = 0;\n  const auto direction = find_direction_to_neighbor(block1, block2);\n  const auto orientation = find_neighbor_orientation(block1, block2);\n  std::array<tnsr::I<double, VolumeDim, Frame::BlockLogical>,\n             two_to_the(VolumeDim - 1)>\n      shared_points1{};\n  std::array<tnsr::I<double, VolumeDim, Frame::BlockLogical>,\n             two_to_the(VolumeDim - 1)>\n      shared_points2{};\n  for (FaceCornerIterator<VolumeDim> fci(direction); fci; ++fci) {\n    gsl::at(shared_points1, fci.face_index()) = fci();\n  }\n  for (FaceCornerIterator<VolumeDim> fci(direction.opposite()); fci; ++fci) {\n    gsl::at(shared_points2, fci.face_index()) =\n        point_in_neighbor_frame(orientation, fci());\n  }\n  if (block1.is_time_dependent() != block2.is_time_dependent()) {\n    ERROR(\n        \"Both block1 and block2 must have the same time dependence, but block1 \"\n        \"has time-dependence status: \"\n        << std::boolalpha << block1.is_time_dependent()\n        << \" and block2 has: \" << block2.is_time_dependent());\n  }\n  if (block1.is_time_dependent()) {\n    ASSERT(not std::isnan(time),\n           \"Blocks have time dependent maps but a time to evaluate at was \"\n           \"not passed\");\n    const auto& map1_logical_to_grid = block1.moving_mesh_logical_to_grid_map();\n    const auto& map1_grid_to_inertial =\n        block1.moving_mesh_grid_to_inertial_map();\n    const auto& map2_logical_to_grid = block2.moving_mesh_logical_to_grid_map();\n    const auto& map2_grid_to_inertial =\n        block2.moving_mesh_grid_to_inertial_map();\n    for (size_t i = 0; i < two_to_the(VolumeDim - 1); i++) {\n      for (size_t j = 0; j < VolumeDim; j++) {\n        max_separation = std::max(\n            max_separation,\n            std::abs(map1_grid_to_inertial(\n                         map1_logical_to_grid(gsl::at(shared_points1, i)), time,\n                         functions_of_time)\n                         .get(j) -\n                     map2_grid_to_inertial(\n                         map2_logical_to_grid(gsl::at(shared_points2, i)), time,\n                         functions_of_time)\n                         .get(j)));\n      }\n    }\n  } else {\n    const auto& map1 = block1.stationary_map();\n    const auto& map2 = block2.stationary_map();\n    for (size_t i = 0; i < two_to_the(VolumeDim - 1); i++) {\n      for (size_t j = 0; j < VolumeDim; j++) {\n        max_separation = std::max(\n            max_separation, std::abs(map1(gsl::at(shared_points1, i)).get(j) -\n                                     map2(gsl::at(shared_points2, i)).get(j)));\n      }\n    }\n  }\n  return max_separation;\n}\n}  // namespace\n}  // namespace domain\n\nnamespace {\ntemplate <size_t Dim>\nvoid dispatch_check_if_maps_are_equal(\n    const Block<Dim>& block,\n    const domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, Dim>&\n        expected_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  check_if_maps_are_equal(expected_map, block.stationary_map(), time,\n                          functions_of_time);\n}\n\ntemplate <size_t Dim>\nvoid dispatch_check_if_maps_are_equal(\n    const Block<Dim>& block,\n    const domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, Dim>&\n        expected_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  check_if_maps_are_equal(expected_map,\n                          block.moving_mesh_grid_to_inertial_map(), time,\n                          functions_of_time);\n}\n\ntemplate <size_t Dim>\nvoid dispatch_check_if_maps_are_equal(\n    const Block<Dim>& block,\n    const domain::CoordinateMapBase<Frame::BlockLogical, Frame::Grid, Dim>&\n        expected_map,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  check_if_maps_are_equal(expected_map, block.moving_mesh_logical_to_grid_map(),\n                          time, functions_of_time);\n}\n}  // namespace\n\ntemplate <size_t VolumeDim, typename TargetFrameGridOrInertial>\nvoid test_domain_construction(\n    const Domain<VolumeDim>& domain,\n    const std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<VolumeDim>>>&\n        expected_external_boundaries,\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, TargetFrameGridOrInertial, VolumeDim>>>&\n        expected_maps,\n    const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>>&\n        expected_grid_to_inertial_maps) {\n  const auto& blocks = domain.blocks();\n  CHECK(blocks.size() == expected_external_boundaries.size());\n  CHECK(blocks.size() == expected_block_neighbors.size());\n  CHECK(blocks.size() == expected_maps.size());\n  for (size_t i = 0; i < blocks.size(); ++i) {\n    const auto& block = blocks[i];\n    CHECK(block.id() == i);\n    CHECK(block.neighbors() == expected_block_neighbors[i]);\n    CHECK(block.external_boundaries() == expected_external_boundaries[i]);\n    dispatch_check_if_maps_are_equal(block, *expected_maps[i], time,\n                                     functions_of_time);\n    if (std::is_same<TargetFrameGridOrInertial, Frame::Grid>::value) {\n      if (expected_grid_to_inertial_maps.size() != blocks.size()) {\n        ERROR(\"Need at least one grid to inertial map for each block (\"\n              << blocks.size() << \" blocks in total) but received \"\n              << expected_grid_to_inertial_maps.size() << \" instead.\");\n      }\n      dispatch_check_if_maps_are_equal(\n          block, *expected_grid_to_inertial_maps[i], time, functions_of_time);\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid test_physical_separation(\n    const std::vector<Block<VolumeDim>>& blocks, double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  INFO(\"Test physical separation\");\n  double tolerance = 1e-10;\n  for (size_t i = 0; i < blocks.size() - 1; i++) {\n    for (size_t j = i + 1; j < blocks.size(); j++) {\n      if (domain::blocks_are_neighbors(blocks[i], blocks[j])) {\n        CAPTURE(i);\n        CAPTURE(j);\n        if ((blocks[i].topologies() ==\n                domain::topologies::hypercube<VolumeDim> and\n            blocks[j].topologies() ==\n             domain::topologies::hypercube<VolumeDim>) or\n            blocks[i].topologies()[VolumeDim - 1] ==\n            domain::Topology::CartoonCylinder) {\n          CHECK(domain::physical_separation(blocks[i], blocks[j], time,\n                                            functions_of_time) < tolerance);\n          if constexpr (VolumeDim > 1) {\n            domain::check_block_face_grid_points_align(blocks[i], blocks[j],\n                                                       time, functions_of_time);\n          }\n        }\n      }\n    }\n  }\n}\n\n// Given a vector of Blocks, tests that the determinant of the Jacobian is\n// positive at all corners of those Blocks.\ntemplate <size_t VolumeDim>\nvoid test_det_jac_positive(\n    const std::vector<Block<VolumeDim>>& blocks, double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  INFO(\"Test determinant of Jacobian is positive\");\n  std::array<tnsr::I<double, VolumeDim, Frame::BlockLogical>,\n             two_to_the(VolumeDim)>\n      corner_points{};\n  size_t index = 0;\n  for (VolumeCornerIterator<VolumeDim> vci; vci; ++vci, ++index) {\n    gsl::at(corner_points, index) =\n        tnsr::I<double, VolumeDim, Frame::BlockLogical>(vci.coords_of_corner());\n  }\n  for(const auto& block: blocks) {\n    if (block.topologies() != domain::topologies::hypercube<VolumeDim>) {\n      continue;\n    }\n    CAPTURE(block.id());\n    if (block.is_time_dependent()) {\n      const auto& map_logical_to_grid = block.moving_mesh_logical_to_grid_map();\n      const auto& map_grid_to_inertial =\n          block.moving_mesh_grid_to_inertial_map();\n      for (size_t i = 0; i < two_to_the(VolumeDim); i++) {\n        CAPTURE(i);\n        CHECK(get(determinant(map_logical_to_grid.jacobian(\n                  gsl::at(corner_points, i)))) > 0.0);\n        CHECK(get(determinant(map_grid_to_inertial.jacobian(\n                  map_logical_to_grid(gsl::at(corner_points, i)), time,\n                  functions_of_time))) > 0.0);\n      }\n    } else {\n      const auto& map = block.stationary_map();\n      for (size_t i = 0; i < two_to_the(VolumeDim); i++) {\n        CAPTURE(i);\n        CHECK(get(determinant(map.jacobian(gsl::at(corner_points, i)))) >\n              0.0);\n      }\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\nboost::rational<size_t> fraction_of_block_volume(\n    const ElementId<VolumeDim>& element_id) {\n  const auto segment_ids = element_id.segment_ids();\n  size_t sum_of_refinement_levels = 0;\n  for (const auto& segment_id : segment_ids) {\n    sum_of_refinement_levels += segment_id.refinement_level();\n  }\n  return {1, two_to_the(sum_of_refinement_levels)};\n}\n\ntemplate <size_t VolumeDim>\nvoid test_initial_domain(const Domain<VolumeDim>& domain,\n                         const std::vector<std::array<size_t, VolumeDim>>&\n                             initial_refinement_levels) {\n  const auto element_ids = initial_element_ids(initial_refinement_levels);\n  const auto& blocks = domain.blocks();\n  CHECK(blocks.size() == initial_refinement_levels.size());\n  std::unordered_map<ElementId<VolumeDim>, Element<VolumeDim>> elements;\n  for (const auto& element_id : element_ids) {\n    elements.emplace(element_id,\n                     domain::create_initial_element(element_id, blocks,\n                                                    initial_refinement_levels));\n  }\n  domain::test_domain_connectivity(domain, elements);\n  domain::test_refinement_levels_of_neighbors<1>(elements);\n}\n\ntemplate <typename DataType, size_t SpatialDim>\ntnsr::i<DataType, SpatialDim> euclidean_basis_vector(\n    const Direction<SpatialDim>& direction, const DataType& used_for_size) {\n  auto basis_vector =\n      make_with_value<tnsr::i<DataType, SpatialDim>>(used_for_size, 0.0);\n\n  basis_vector.get(direction.axis()) =\n      make_with_value<DataType>(used_for_size, direction.sign());\n\n  return basis_vector;\n}\n\ntemplate <typename DataType, size_t SpatialDim>\ntnsr::i<DataType, SpatialDim> unit_basis_form(\n    const Direction<SpatialDim>& direction,\n    const tnsr::II<DataType, SpatialDim>& inv_spatial_metric) {\n  auto basis_form =\n      euclidean_basis_vector(direction, get<0, 0>(inv_spatial_metric));\n  const DataType inv_norm =\n      1.0 / get(magnitude(basis_form, inv_spatial_metric));\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    basis_form.get(i) *= inv_norm;\n  }\n  return basis_form;\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                          \\\n  template boost::rational<size_t> fraction_of_block_volume(          \\\n      const ElementId<DIM(data)>& element_id);                        \\\n  template void test_initial_domain(                                  \\\n      const Domain<DIM(data)>& domain,                                \\\n      const std::vector<std::array<size_t, DIM(data)>>&               \\\n          initial_refinement_levels);                                 \\\n  template void test_physical_separation(                             \\\n      const std::vector<Block<DIM(data)>>& blocks, const double time, \\\n      const std::unordered_map<                                       \\\n          std::string,                                                \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&  \\\n          functions_of_time);                                         \\\n  template void test_det_jac_positive(                                \\\n      const std::vector<Block<DIM(data)>>& blocks, const double time, \\\n      const std::unordered_map<                                       \\\n          std::string,                                                \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&  \\\n          functions_of_time);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef INSTANTIATE\n\n#define GET_FRAME(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE(_, data)                                                 \\\n  template void test_domain_construction<DIM(data), GET_FRAME(data)>(        \\\n      const Domain<DIM(data)>& domain,                                       \\\n      const std::vector<DirectionMap<DIM(data), BlockNeighbors<DIM(data)>>>& \\\n          expected_block_neighbors,                                          \\\n      const std::vector<std::unordered_set<Direction<DIM(data)>>>&           \\\n          expected_external_boundaries,                                      \\\n      const std::vector<std::unique_ptr<domain::CoordinateMapBase<           \\\n          Frame::BlockLogical, GET_FRAME(data), DIM(data)>>>& expected_maps, \\\n      const double time,                                                     \\\n      const std::unordered_map<                                              \\\n          std::string,                                                       \\\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&         \\\n          functions_of_time,                                                 \\\n      const std::vector<std::unique_ptr<domain::CoordinateMapBase<           \\\n          Frame::Grid, Frame::Inertial, DIM(data)>>>&                        \\\n          expected_logical_to_grid_maps);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3), (Frame::Grid, Frame::Inertial))\n\n#undef INSTANTIATE\n#undef GET_FRAME\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_BASIS_VECTOR(_, data)                          \\\n  template tnsr::i<DTYPE(data), DIM(data)> euclidean_basis_vector( \\\n      const Direction<DIM(data)>& direction,                       \\\n      const DTYPE(data) & used_for_size);                          \\\n  template tnsr::i<DTYPE(data), DIM(data)> unit_basis_form(        \\\n      const Direction<DIM(data)>& direction,                       \\\n      const tnsr::II<DTYPE(data), DIM(data)>& inv_spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_BASIS_VECTOR, (1, 2, 3),\n                        (double, DataVector))\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE_BASIS_VECTOR\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/DomainTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <boost/rational.hpp>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n\n/// \\cond\ntemplate <size_t VolumeDim>\nclass Block;\ntemplate <size_t VolumeDim>\nclass BlockNeighbors;\nnamespace domain {\ntemplate <typename SourceFrame, typename TargetFrame, size_t Dim>\nclass CoordinateMapBase;\n}  // namespace domain\nclass DataVector;\ntemplate <size_t VolumeDim>\nclass Direction;\ntemplate <size_t VolumeDim, typename T>\nclass DirectionMap;\ntemplate <size_t VolumeDim>\nclass Domain;\ntemplate <size_t VolumeDim>\nclass ElementId;\nnamespace domain::BoundaryConditions {\nclass BoundaryCondition;\n}  // namespace domain::BoundaryConditions\n/// \\endcond\n\n// Test that the Blocks in the Domain are constructed correctly.\n//\n// The boundary conditions test assumes that the\n// `TestHelpers::domain::BoundaryConditions::TestBoundaryCondition` class is\n// used as the concrete boundary condition.\ntemplate <size_t VolumeDim, typename TargetFrameGridOrInertial>\nvoid test_domain_construction(\n    const Domain<VolumeDim>& domain,\n    const std::vector<DirectionMap<VolumeDim, BlockNeighbors<VolumeDim>>>&\n        expected_block_neighbors,\n    const std::vector<std::unordered_set<Direction<VolumeDim>>>&\n        expected_external_boundaries,\n    const std::vector<std::unique_ptr<domain::CoordinateMapBase<\n        Frame::BlockLogical, TargetFrameGridOrInertial, VolumeDim>>>&\n        expected_maps,\n    double time = std::numeric_limits<double>::quiet_NaN(),\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time = {},\n    const std::vector<std::unique_ptr<\n        domain::CoordinateMapBase<Frame::Grid, Frame::Inertial, VolumeDim>>>&\n        expected_grid_to_inertial_maps = {});\n\n// Test that two neighboring Blocks abut each other.\ntemplate <size_t VolumeDim>\nvoid test_physical_separation(\n    const std::vector<Block<VolumeDim>>& blocks,\n    double time = std::numeric_limits<double>::signaling_NaN(),\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time = {});\n\n// Given a vector of Blocks, tests that the determinant of the Jacobian is\n// positive at all corners of those Blocks.\ntemplate <size_t VolumeDim>\nvoid test_det_jac_positive(\n    const std::vector<Block<VolumeDim>>& blocks,\n    double time = std::numeric_limits<double>::signaling_NaN(),\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time = {});\n\n// Fraction of the logical volume of a block covered by an element\n// The sum of this over all the elements of a block should be one\ntemplate <size_t VolumeDim>\nboost::rational<size_t> fraction_of_block_volume(\n    const ElementId<VolumeDim>& element_id);\n\n// Test that the Elements of the initial domain are connected and cover the\n// computational domain, as well as that neighboring Elements' refinement\n// levels do not differ too much.\ntemplate <size_t VolumeDim>\nvoid test_initial_domain(const Domain<VolumeDim>& domain,\n                         const std::vector<std::array<size_t, VolumeDim>>&\n                             initial_refinement_levels);\n\n// Euclidean basis vector along the given `Direction` and in the given\n// `Frame::Inertial` frame.\ntemplate <typename DataType, size_t SpatialDim>\ntnsr::i<DataType, SpatialDim> euclidean_basis_vector(\n    const Direction<SpatialDim>& direction, const DataType& used_for_size);\n\ntemplate <typename DataType, size_t SpatialDim>\ntnsr::i<DataType, SpatialDim> unit_basis_form(\n    const Direction<SpatialDim>& direction,\n    const tnsr::II<DataType, SpatialDim>& inv_spatial_metric);\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Structure/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"DomainStructureHelpers\")\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  NeighborHelpers.cpp\n  OrientationMapHelpers.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  NeighborHelpers.hpp\n  OrientationMapHelpers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DomainStructure\n  Utilities\n  PRIVATE\n  Framework\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Structure/NeighborHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Domain/Structure/NeighborHelpers.hpp\"\n\n#include <array>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Helpers/Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstd::vector<std::unordered_set<ElementId<Dim>>> valid_aligned_neighbor_ids(\n    const ElementId<Dim>& element_id, size_t neighbor_block_id,\n    size_t normal_dim,\n    const std::vector<SegmentId>& valid_neighbor_normal_segments);\n\ntemplate <>\nstd::vector<std::unordered_set<ElementId<1>>> valid_aligned_neighbor_ids<1>(\n    const ElementId<1>& /*element_id*/, size_t neighbor_block_id,\n    size_t /*normal_dim*/,\n    const std::vector<SegmentId>& valid_neighbor_normal_segments) {\n  std::vector<std::unordered_set<ElementId<1>>> result{};\n  for (const auto& normal_segment : valid_neighbor_normal_segments) {\n    result.emplace_back(std::unordered_set{\n        ElementId<1>{neighbor_block_id, std::array{normal_segment}}});\n  }\n  return result;\n}\n\nstd::array<SegmentId, 2> make_segment_ids(const SegmentId& normal_segment,\n                                          const SegmentId& transverse_segment,\n                                          const size_t normal_dim) {\n  return normal_dim == 0 ? std::array{normal_segment, transverse_segment}\n                         : std::array{transverse_segment, normal_segment};\n}\n\ntemplate <>\nstd::vector<std::unordered_set<ElementId<2>>> valid_aligned_neighbor_ids<2>(\n    const ElementId<2>& element_id, size_t neighbor_block_id,\n    const size_t normal_dim,\n    const std::vector<SegmentId>& valid_neighbor_normal_segments) {\n  const SegmentId transverse_segment =\n      element_id.segment_id(normal_dim == 0 ? 1 : 0);\n  std::vector<std::unordered_set<ElementId<2>>> result{};\n  for (const auto& normal_segment : valid_neighbor_normal_segments) {\n    result.emplace_back(std::unordered_set{ElementId<2>{\n        neighbor_block_id,\n        make_segment_ids(normal_segment, transverse_segment, normal_dim)}});\n    if (normal_segment == SegmentId{0, 0} and\n        neighbor_block_id == element_id.block_id()) {\n      // periodic so transverse extents must match\n      continue;\n    }\n    if (transverse_segment.refinement_level() > 0) {\n      result.emplace_back(std::unordered_set{ElementId<2>{\n          neighbor_block_id,\n          make_segment_ids(normal_segment, transverse_segment.id_of_parent(),\n                           normal_dim)}});\n    }\n    result.emplace_back(std::unordered_set{\n        ElementId<2>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<2>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)}});\n    if (normal_segment != valid_neighbor_normal_segments.front()) {\n      result.emplace_back(std::unordered_set{\n          ElementId<2>{\n              neighbor_block_id,\n              make_segment_ids(normal_segment.id_of_parent(),\n                               transverse_segment.id_of_child(Side::Lower),\n                               normal_dim)},\n          ElementId<2>{\n              neighbor_block_id,\n              make_segment_ids(normal_segment,\n                               transverse_segment.id_of_child(Side::Upper),\n                               normal_dim)}});\n      result.emplace_back(std::unordered_set{\n          ElementId<2>{\n              neighbor_block_id,\n              make_segment_ids(normal_segment,\n                               transverse_segment.id_of_child(Side::Lower),\n                               normal_dim)},\n          ElementId<2>{\n              neighbor_block_id,\n              make_segment_ids(normal_segment.id_of_parent(),\n                               transverse_segment.id_of_child(Side::Upper),\n                               normal_dim)}});\n    }\n  }\n  return result;\n}\n\nstd::array<SegmentId, 3> make_segment_ids(\n    const SegmentId& normal_segment, const SegmentId& first_transverse_segment,\n    const SegmentId& second_transverse_segment, const size_t normal_dim) {\n  return normal_dim == 0\n             ? std::array{normal_segment, first_transverse_segment,\n                          second_transverse_segment}\n             : (normal_dim == 1\n                    ? std::array{first_transverse_segment, normal_segment,\n                                 second_transverse_segment}\n                    : std::array{first_transverse_segment,\n                                 second_transverse_segment, normal_segment});\n}\n\ntemplate <>\nstd::vector<std::unordered_set<ElementId<3>>> valid_aligned_neighbor_ids<3>(\n    const ElementId<3>& element_id, size_t neighbor_block_id, size_t normal_dim,\n    const std::vector<SegmentId>& valid_neighbor_normal_segments) {\n  const SegmentId first_transverse_segment =\n      element_id.segment_id(normal_dim == 0 ? 1 : 0);\n  const SegmentId second_transverse_segment =\n      element_id.segment_id(normal_dim == 2 ? 1 : 2);\n  std::vector<std::unordered_set<ElementId<3>>> result{};\n  for (const auto& normal_segment : valid_neighbor_normal_segments) {\n    result.emplace_back(std::unordered_set{\n        ElementId<3>{neighbor_block_id,\n                     make_segment_ids(normal_segment, first_transverse_segment,\n                                      second_transverse_segment, normal_dim)}});\n    if (normal_segment == SegmentId{0, 0} and\n        neighbor_block_id == element_id.block_id()) {\n      // periodic so transverse extents must match\n      continue;\n    }\n    if (first_transverse_segment.refinement_level() > 0) {\n      result.emplace_back(std::unordered_set{ElementId<3>{\n          neighbor_block_id,\n          make_segment_ids(normal_segment,\n                           first_transverse_segment.id_of_parent(),\n                           second_transverse_segment, normal_dim)}});\n      if (second_transverse_segment.refinement_level() > 0) {\n        result.emplace_back(std::unordered_set{ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(\n                normal_segment, first_transverse_segment.id_of_parent(),\n                second_transverse_segment.id_of_parent(), normal_dim)}});\n      }\n    }\n    if (second_transverse_segment.refinement_level() > 0) {\n      result.emplace_back(std::unordered_set{ElementId<3>{\n          neighbor_block_id,\n          make_segment_ids(normal_segment, first_transverse_segment,\n                           second_transverse_segment.id_of_parent(),\n                           normal_dim)}});\n    }\n    result.emplace_back(std::unordered_set{\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Lower),\n                             second_transverse_segment, normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Upper),\n                             second_transverse_segment, normal_dim)}});\n    if (second_transverse_segment.refinement_level() > 0) {\n      result.emplace_back(std::unordered_set{\n          ElementId<3>{\n              neighbor_block_id,\n              make_segment_ids(\n                  normal_segment,\n                  first_transverse_segment.id_of_child(Side::Lower),\n                  second_transverse_segment.id_of_parent(), normal_dim)},\n          ElementId<3>{neighbor_block_id,\n                       make_segment_ids(\n                           normal_segment,\n                           first_transverse_segment.id_of_child(Side::Upper),\n                           second_transverse_segment, normal_dim)}});\n      result.emplace_back(std::unordered_set{\n          ElementId<3>{neighbor_block_id,\n                       make_segment_ids(\n                           normal_segment,\n                           first_transverse_segment.id_of_child(Side::Lower),\n                           second_transverse_segment, normal_dim)},\n          ElementId<3>{\n              neighbor_block_id,\n              make_segment_ids(\n                  normal_segment,\n                  first_transverse_segment.id_of_child(Side::Upper),\n                  second_transverse_segment.id_of_parent(), normal_dim)}});\n      result.emplace_back(std::unordered_set{\n          ElementId<3>{\n              neighbor_block_id,\n              make_segment_ids(\n                  normal_segment,\n                  first_transverse_segment.id_of_child(Side::Lower),\n                  second_transverse_segment.id_of_parent(), normal_dim)},\n          ElementId<3>{\n              neighbor_block_id,\n              make_segment_ids(\n                  normal_segment,\n                  first_transverse_segment.id_of_child(Side::Upper),\n                  second_transverse_segment.id_of_parent(), normal_dim)}});\n    }\n    result.emplace_back(std::unordered_set{\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment, first_transverse_segment,\n                             second_transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment, first_transverse_segment,\n                             second_transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)}});\n    if (first_transverse_segment.refinement_level() > 0) {\n      result.emplace_back(std::unordered_set{\n          ElementId<3>{\n              neighbor_block_id,\n              make_segment_ids(\n                  normal_segment, first_transverse_segment.id_of_parent(),\n                  second_transverse_segment.id_of_child(Side::Lower),\n                  normal_dim)},\n          ElementId<3>{neighbor_block_id,\n                       make_segment_ids(\n                           normal_segment, first_transverse_segment,\n                           second_transverse_segment.id_of_child(Side::Upper),\n                           normal_dim)}});\n      result.emplace_back(std::unordered_set{\n          ElementId<3>{neighbor_block_id,\n                       make_segment_ids(\n                           normal_segment, first_transverse_segment,\n                           second_transverse_segment.id_of_child(Side::Lower),\n                           normal_dim)},\n          ElementId<3>{\n              neighbor_block_id,\n              make_segment_ids(\n                  normal_segment, first_transverse_segment.id_of_parent(),\n                  second_transverse_segment.id_of_child(Side::Upper),\n                  normal_dim)}});\n      result.emplace_back(std::unordered_set{\n          ElementId<3>{\n              neighbor_block_id,\n              make_segment_ids(\n                  normal_segment, first_transverse_segment.id_of_parent(),\n                  second_transverse_segment.id_of_child(Side::Lower),\n                  normal_dim)},\n          ElementId<3>{\n              neighbor_block_id,\n              make_segment_ids(\n                  normal_segment, first_transverse_segment.id_of_parent(),\n                  second_transverse_segment.id_of_child(Side::Upper),\n                  normal_dim)}});\n    }\n    result.emplace_back(std::unordered_set{\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Lower),\n                             second_transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Upper),\n                             second_transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment, first_transverse_segment,\n                             second_transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)}});\n    result.emplace_back(std::unordered_set{\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment, first_transverse_segment,\n                             second_transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Lower),\n                             second_transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Upper),\n                             second_transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)}});\n    result.emplace_back(std::unordered_set{\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Lower),\n                             second_transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Upper),\n                             second_transverse_segment, normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Lower),\n                             second_transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)}});\n    result.emplace_back(std::unordered_set{\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Lower),\n                             second_transverse_segment, normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Upper),\n                             second_transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Upper),\n                             second_transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)}});\n    result.emplace_back(std::unordered_set{\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Lower),\n                             second_transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Upper),\n                             second_transverse_segment.id_of_child(Side::Lower),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Lower),\n                             second_transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)},\n        ElementId<3>{\n            neighbor_block_id,\n            make_segment_ids(normal_segment,\n                             first_transverse_segment.id_of_child(Side::Upper),\n                             second_transverse_segment.id_of_child(Side::Upper),\n                             normal_dim)}});\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::unordered_set<ElementId<Dim>> oriented_neighbor_ids(\n    const std::unordered_set<ElementId<Dim>>& aligned_neighbor_ids,\n    const OrientationMap<Dim>& orientation) {\n  std::unordered_set<ElementId<Dim>> result{};\n  for (const auto& neighbor_id : aligned_neighbor_ids) {\n    result.emplace(neighbor_id.block_id(),\n                   orientation(neighbor_id.segment_ids()));\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nbool are_neighbors_overlapping(const Neighbors<Dim>& neighbors) {\n  const auto& neighbor_ids = neighbors.ids();\n  for (auto it = std::next(neighbor_ids.begin()); it != neighbor_ids.end();\n       ++it) {\n    for (auto prev_it = neighbor_ids.begin(); prev_it != it; ++prev_it) {\n      if (overlapping(*prev_it, *it)) {\n        return true;\n      }\n    }\n  }\n  return false;\n}\n}  // namespace\n\nnamespace TestHelpers::domain {\nstd::vector<SegmentId> valid_neighbor_segments(const FaceType face_type,\n                                               const Side side) {\n  if (face_type == FaceType::External) {\n    return std::vector<SegmentId>{};\n  }\n  if (face_type == FaceType::Periodic) {\n    return std::vector{SegmentId{0, 0}};\n  }\n  ASSERT(face_type != FaceType::Internal,\n         \"Face type cannot be Internal for the root segment.\");\n  if (side == Side::Lower) {\n    return std::vector{SegmentId{0, 0}, SegmentId{1, 1}};\n  }\n  return std::vector{SegmentId{0, 0}, SegmentId{1, 0}};\n}\n\nstd::vector<SegmentId> valid_neighbor_segments(const SegmentId& segment_id) {\n  return std::vector{segment_id.id_of_sibling(),\n                     segment_id.id_of_abutting_nibling()};\n}\n\nstd::vector<SegmentId> valid_neighbor_segments(const SegmentId& segment_id,\n                                               const FaceType face_type) {\n  if (face_type == FaceType::External) {\n    return std::vector<SegmentId>{};\n  }\n  const size_t level = segment_id.refinement_level();\n  ASSERT(level != 0,\n         \"Cannot call this function on the root segment.  Use \"\n         \"valid_neighbor_segments(face_type, side) instead.\");\n\n  const Side sibling_side = segment_id.side_of_sibling();\n  const SegmentId base_segment =\n      (face_type == FaceType::Block or face_type == FaceType::Periodic)\n          ? segment_id.id_if_flipped()\n          : SegmentId{level, sibling_side == Side::Upper\n                                 ? segment_id.index() - 1\n                                 : segment_id.index() + 1};\n\n  const bool neighbor_can_be_parent_of_base =\n      face_type == FaceType::Block or\n      not(segment_id.id_of_parent() == base_segment.id_of_parent());\n\n  if (not neighbor_can_be_parent_of_base) {\n    return std::vector{base_segment, base_segment.id_of_child(sibling_side)};\n  }\n\n  return std::vector{base_segment.id_of_parent(), base_segment,\n                     base_segment.id_of_child(sibling_side)};\n}\n\ntemplate <size_t Dim>\nstd::vector<Neighbors<Dim>> valid_neighbors(\n    const gsl::not_null<std::mt19937*> generator,\n    const ElementId<Dim>& element_id, const Direction<Dim>& direction,\n    const FaceType face_type) {\n  const size_t normal_dim = direction.dimension();\n  const Side side = direction.side();\n  const SegmentId& normal_segment = element_id.segment_id(normal_dim);\n  const double endpoint = normal_segment.endpoint(side);\n  const OrientationMap<Dim> aligned = OrientationMap<Dim>::create_aligned();\n  const size_t block_id = element_id.block_id();\n  const size_t neighbor_block_id =\n      (face_type == FaceType::Block\n           ? block_id + 2 * normal_dim + (side == Side::Lower ? 1 : 2)\n           : block_id);\n  std::vector<Neighbors<Dim>> result;\n  if (endpoint == 1.0 or endpoint == -1.0) {\n    ASSERT(face_type != FaceType::Internal,\n           \"Element abuts the Block Boundary in the given direction, please \"\n           \"pass a valid FaceType\");\n    if (face_type == FaceType::External) {\n      return std::vector<Neighbors<Dim>>{};\n    }\n    const auto valid_neighbor_normal_segments =\n        (normal_segment.refinement_level() == 0\n             ? valid_neighbor_segments(face_type, side)\n             : valid_neighbor_segments(normal_segment, face_type));\n    const auto valid_orientations =\n        (face_type == FaceType::Periodic\n             ? std::vector{aligned}\n             : random_orientation_maps<Dim>(2, generator));\n    for (const auto& aligned_neighbor_ids :\n         valid_aligned_neighbor_ids(element_id, neighbor_block_id, normal_dim,\n                                    valid_neighbor_normal_segments)) {\n      for (const auto& orientation : valid_orientations) {\n        if (orientation.is_aligned()) {\n          result.emplace_back(aligned_neighbor_ids, orientation);\n        } else {\n          result.emplace_back(\n              oriented_neighbor_ids(aligned_neighbor_ids, orientation),\n              orientation);\n        }\n      }\n    }\n    return result;\n  }\n  ASSERT(face_type == FaceType::Internal,\n         \"Element is in the interior of a Block in the provided direction\");\n  const auto valid_neighbor_normal_segments =\n      (side == normal_segment.side_of_sibling()\n           ? valid_neighbor_segments(normal_segment)\n           : valid_neighbor_segments(normal_segment, face_type));\n  for (const auto& neighbor_ids :\n       valid_aligned_neighbor_ids(element_id, neighbor_block_id, normal_dim,\n                                  valid_neighbor_normal_segments)) {\n    result.emplace_back(neighbor_ids, aligned);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid check_neighbors(const Neighbors<Dim>& neighbors,\n                     const ElementId<Dim>& element_id,\n                     const Direction<Dim>& direction) {\n  CAPTURE(element_id);\n  CAPTURE(neighbors);\n  CAPTURE(direction);\n  if (neighbors.size() > 1) {\n    CHECK_FALSE(are_neighbors_overlapping(neighbors));\n  }\n  const size_t dim_normal_to_face = direction.dimension();\n  const Side neighbor_side = direction.side();\n  const Side element_side = opposite(neighbor_side);\n  double surface_area_covered = 0.0;\n  for (const auto& neighbor : neighbors) {\n    CHECK(neighbor.grid_index() == element_id.grid_index());\n    const OrientationMap<Dim>& orientation_map =\n        neighbors.orientation(neighbor);\n    double neighbor_overlap_area = 1.0;\n    const std::array<SegmentId, Dim> neighbor_segment_ids =\n        orientation_map.is_aligned()\n            ? neighbor.segment_ids()\n            : orientation_map.inverse_map()(neighbor.segment_ids());\n    for (size_t d = 0; d < Dim; ++d) {\n      const SegmentId& element_segment = element_id.segment_id(d);\n      const SegmentId& neighbor_segment = gsl::at(neighbor_segment_ids, d);\n      if (d == dim_normal_to_face) {\n        const double endpoint = element_segment.endpoint(neighbor_side);\n        if (endpoint == 1.0 or endpoint == -1.0) {\n          // point lies on a Block boundary, neighbor point should lie\n          // on opposite side of the Block\n          CHECK(-endpoint == neighbor_segment.endpoint(element_side));\n        } else {\n          // interior point, should be shared\n          CHECK(endpoint == neighbor_segment.endpoint(element_side));\n        }\n        if (element_id.block_id() == neighbor.block_id()) {\n          CHECK((element_id == neighbor or\n                 not overlapping(element_segment, neighbor_segment)));\n        }\n      } else {  // transverse to face\n        if (neighbors.size() == 1) {\n          CHECK((element_segment == neighbor_segment or\n                 element_segment.id_of_parent() == neighbor_segment));\n        } else {\n          if (neighbor_segment.refinement_level() > 0 and\n              element_segment == neighbor_segment.id_of_parent()) {\n            neighbor_overlap_area *= 0.5;\n          } else {\n            CHECK((element_segment == neighbor_segment or\n                   element_segment.id_of_parent() == neighbor_segment));\n          }\n        }\n      }\n    }\n    surface_area_covered += neighbor_overlap_area;\n  }\n  CHECK(surface_area_covered == 1.0);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                            \\\n  template std::vector<Neighbors<DIM(data)>> valid_neighbors(           \\\n      const gsl::not_null<std::mt19937*> generator,                     \\\n      const ElementId<DIM(data)>& element_id,                           \\\n      const Direction<DIM(data)>& direction, const FaceType face_type); \\\n  template void check_neighbors(const Neighbors<DIM(data)>& neighbors,  \\\n                                const ElementId<DIM(data)>& element_id, \\\n                                const Direction<DIM(data)>& direction);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace TestHelpers::domain\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Structure/NeighborHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <random>\n#include <vector>\n\n#include \"Domain/Structure/Side.hpp\"\n\n/// \\cond\ntemplate <size_t Dim>\nclass Direction;\ntemplate <size_t Dim>\nclass ElementId;\ntemplate <size_t Dim, typename IdType>\nclass Neighbors;\nclass SegmentId;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}\n/// \\endcond\n\nnamespace TestHelpers::domain {\n\n/// The type of face of an Element\nenum class FaceType {\n  /// Used to denote an Element face on an external boundary\n  External,\n  /// Used to denote an Element face on a periodic boundary\n  Periodic,\n  /// Used to denote an Element face shared with a neighboring Block\n  Block,\n  /// Used to denote an Element face not on the boundary of a Block\n  Internal\n};\n\n/// Valid SegmentIds for a neighbor of the root segment on the given side\n///\n/// \\details Returns an empty vector if `side` abuts an external boundary\n/// (indicated by passing External as the `face_type`). Returns the root segment\n/// if `face_type` is Periodic.\n///\n/// \\note If the neighbor segment is in another Block (indicated by passing\n/// Block as `face_type`), the returned SegmentId%s are in the logical frame of\n/// `segment_id` (i.e. they will need to be mapped to the neighbor logical frame\n/// by using the appropriate OrientationMap of the Block owning the ElementId\n/// with `segment_id`).\nstd::vector<SegmentId> valid_neighbor_segments(const FaceType face_type,\n                                               const Side side);\n\n/// Valid SegmentIds for a neighbor on the sibling side of a segment\nstd::vector<SegmentId> valid_neighbor_segments(const SegmentId& segment_id);\n\n/// Valid SegmentIds for a neighbor on the non-sibling side of a segment\n///\n/// \\details Returns an empty vector if the non-sibling side abuts an external\n/// boundary (indicated by passing External as `face_type`).\n///\n/// \\note If the non-sibling side abuts another Block (indicated by passing\n/// Block as `face_type`), the returned SegmentId%s are in the logical frame of\n/// `segment_id` (i.e. they will need to be mapped to the neighbor logical frame\n/// by using the appropriate OrientationMap of the Block owning the ElementId\n/// with `segment_id`).\nstd::vector<SegmentId> valid_neighbor_segments(const SegmentId& segment_id,\n                                               const FaceType face_type);\n\n/// \\brief All valid neighbor configurations for an Element with `element_id` in\n/// the given `direction` with the given `face_type`\n///\n/// \\details FaceType needs to be passed in only if the Element abuts a Block\n/// boundary in the given `direction`3\n///\n/// The `generator` is used to assign a random orientation if the neighbor is\n/// in a different block, i.e. `face_type == FaceType::Block`.\ntemplate <size_t Dim>\nstd::vector<Neighbors<Dim, ElementId<Dim>>> valid_neighbors(\n    gsl::not_null<std::mt19937*> generator, const ElementId<Dim>& element_id,\n    const Direction<Dim>& direction,\n    const FaceType face_type = FaceType::Internal);\n\n/// Checks that the `neighbors` for the Element with `element_id` in the given\n/// `direction` are a complete set of valid face neighbors\n///\n/// \\details A valid neighbor is within one refinement level in the dimensions\n/// parallel to the face between the Element  and the neighbor.  The set is\n/// complete if there are neighboring Element%s they completely cover the face.\ntemplate <size_t Dim>\nvoid check_neighbors(const Neighbors<Dim, ElementId<Dim>>& neighbors,\n                     const ElementId<Dim>& element_id,\n                     const Direction<Dim>& direction);\n}  // namespace TestHelpers::domain\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Structure/OrientationMapHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Domain/Structure/OrientationMapHelpers.hpp\"\n\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TestHelpers::domain {\ntemplate <>\nstd::vector<OrientationMap<1>> valid_orientation_maps<1>() {\n  return std::vector{OrientationMap<1>::create_aligned(),\n                     OrientationMap<1>{std::array{Direction<1>::lower_xi()}}};\n}\n\ntemplate <>\nstd::vector<OrientationMap<2>> valid_orientation_maps<2>() {\n  return std::vector{OrientationMap<2>::create_aligned(),\n                     OrientationMap<2>{std::array{Direction<2>::upper_eta(),\n                                                  Direction<2>::lower_xi()}},\n                     OrientationMap<2>{std::array{Direction<2>::lower_xi(),\n                                                  Direction<2>::lower_eta()}},\n                     OrientationMap<2>{std::array{Direction<2>::lower_eta(),\n                                                  Direction<2>::upper_xi()}}};\n}\n\ntemplate <>\nstd::vector<OrientationMap<3>> valid_orientation_maps<3>() {\n  return std::vector{OrientationMap<3>::create_aligned(),\n                     OrientationMap<3>{std::array{Direction<3>::lower_xi(),\n                                                  Direction<3>::lower_eta(),\n                                                  Direction<3>::upper_zeta()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_xi(),\n                                                  Direction<3>::upper_eta(),\n                                                  Direction<3>::lower_zeta()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_xi(),\n                                                  Direction<3>::lower_eta(),\n                                                  Direction<3>::lower_zeta()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_eta(),\n                                                  Direction<3>::upper_zeta(),\n                                                  Direction<3>::upper_xi()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_eta(),\n                                                  Direction<3>::lower_zeta(),\n                                                  Direction<3>::upper_xi()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_eta(),\n                                                  Direction<3>::upper_zeta(),\n                                                  Direction<3>::lower_xi()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_eta(),\n                                                  Direction<3>::lower_zeta(),\n                                                  Direction<3>::lower_xi()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_zeta(),\n                                                  Direction<3>::upper_xi(),\n                                                  Direction<3>::upper_eta()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_zeta(),\n                                                  Direction<3>::lower_xi(),\n                                                  Direction<3>::upper_eta()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_zeta(),\n                                                  Direction<3>::upper_xi(),\n                                                  Direction<3>::lower_eta()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_zeta(),\n                                                  Direction<3>::lower_xi(),\n                                                  Direction<3>::lower_eta()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_eta(),\n                                                  Direction<3>::lower_xi(),\n                                                  Direction<3>::upper_zeta()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_eta(),\n                                                  Direction<3>::upper_xi(),\n                                                  Direction<3>::upper_zeta()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_eta(),\n                                                  Direction<3>::lower_xi(),\n                                                  Direction<3>::lower_zeta()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_eta(),\n                                                  Direction<3>::upper_xi(),\n                                                  Direction<3>::lower_zeta()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_xi(),\n                                                  Direction<3>::upper_zeta(),\n                                                  Direction<3>::upper_eta()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_xi(),\n                                                  Direction<3>::lower_zeta(),\n                                                  Direction<3>::upper_eta()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_xi(),\n                                                  Direction<3>::upper_zeta(),\n                                                  Direction<3>::lower_eta()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_xi(),\n                                                  Direction<3>::lower_zeta(),\n                                                  Direction<3>::lower_eta()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_zeta(),\n                                                  Direction<3>::upper_eta(),\n                                                  Direction<3>::lower_xi()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_zeta(),\n                                                  Direction<3>::lower_eta(),\n                                                  Direction<3>::lower_xi()}},\n                     OrientationMap<3>{std::array{Direction<3>::lower_zeta(),\n                                                  Direction<3>::upper_eta(),\n                                                  Direction<3>::upper_xi()}},\n                     OrientationMap<3>{std::array{Direction<3>::upper_zeta(),\n                                                  Direction<3>::lower_eta(),\n                                                  Direction<3>::upper_xi()}}};\n}\n\ntemplate <size_t Dim>\nstd::vector<OrientationMap<Dim>> random_orientation_maps(\n    const size_t number_of_samples, gsl::not_null<std::mt19937*> generator) {\n  return random_sample(number_of_samples, valid_orientation_maps<Dim>(),\n                       generator);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                               \\\n  template std::vector<OrientationMap<DIM(data)>> random_orientation_maps( \\\n      const size_t number_of_samples, gsl::not_null<std::mt19937*> generator);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\n#undef DIM\n#undef INSTANTIATE\n}  // namespace TestHelpers::domain\n"
  },
  {
    "path": "tests/Unit/Helpers/Domain/Structure/OrientationMapHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <random>\n#include <vector>\n\n/// \\cond\ntemplate <size_t Dim>\nclass OrientationMap;\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}\n/// \\endcond\n\nnamespace TestHelpers::domain {\n/// \\brief List of all valid OrientationMap%s in a given dimension\n///\n/// \\details In more than one dimension, an OrientationMap is considered to be\n/// valid if it has a positive determinant for its discrete_rotation_jacobian.\n/// This restriction is relaxed in one dimension so that a non-aligned\n/// OrientationMap can be tested in 1D.\ntemplate <size_t Dim>\nstd::vector<OrientationMap<Dim>> valid_orientation_maps();\n\n/// Return a random sample of unique OrientationMap%s\n///\n/// \\note If the requested number_of_samples is larger than the number of\n/// possible OrientationMap%s, this will return all possible OrientationMap%s\ntemplate <size_t Dim>\nstd::vector<OrientationMap<Dim>> random_orientation_maps(\n    size_t number_of_samples, gsl::not_null<std::mt19937*> generator);\n}  // namespace TestHelpers::domain\n"
  },
  {
    "path": "tests/Unit/Helpers/Elliptic/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"EllipticTestHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  FirstOrderSystem.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  Elliptic\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Elliptic/DiscontinuousGalerkin/NumericalFluxes/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NumericalFluxes/NumericalFluxHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/SimpleBoundaryData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace EllipticNumericalFluxesTestHelpers {\n\n/// Test that the flux is single-valued on the interface, i.e. that the elements\n/// on either side of the interface are working with the same numerical flux\n/// data\ntemplate <size_t Dim, typename NumericalFluxComputer, typename MakeRandomArgs>\nvoid test_conservation(const NumericalFluxComputer& numerical_flux_computer,\n                       MakeRandomArgs&& make_random_args,\n                       const DataVector& used_for_size) {\n  const size_t num_points = used_for_size.size();\n  using PackagedData = dg::SimpleBoundaryData<\n      typename NumericalFluxComputer::package_field_tags,\n      typename NumericalFluxComputer::package_extra_tags>;\n  const auto make_random_packaged_data = [&numerical_flux_computer,\n                                          &make_random_args, &num_points]() {\n    return std::apply(\n        [&numerical_flux_computer, &num_points](const auto... args) {\n          PackagedData packaged_data{num_points};\n          dg::NumericalFluxes::package_data(make_not_null(&packaged_data),\n                                            numerical_flux_computer, args...);\n          return packaged_data;\n        },\n        make_random_args());\n  };\n  const auto packaged_data_interior = make_random_packaged_data();\n  const auto packaged_data_exterior = make_random_packaged_data();\n\n  using Vars = Variables<typename NumericalFluxComputer::variables_tags>;\n  Vars n_dot_num_flux_interior(num_points,\n                               std::numeric_limits<double>::signaling_NaN());\n  dg::NumericalFluxes::normal_dot_numerical_fluxes(\n      make_not_null(&n_dot_num_flux_interior), numerical_flux_computer,\n      packaged_data_interior, packaged_data_exterior);\n  Vars n_dot_num_flux_exterior(num_points,\n                               std::numeric_limits<double>::signaling_NaN());\n  dg::NumericalFluxes::normal_dot_numerical_fluxes(\n      make_not_null(&n_dot_num_flux_exterior), numerical_flux_computer,\n      packaged_data_exterior, packaged_data_interior);\n\n  CHECK_VARIABLES_APPROX(n_dot_num_flux_interior, -n_dot_num_flux_exterior);\n}\n\n}  // namespace EllipticNumericalFluxesTestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Elliptic/FirstOrderSystem.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Helper functions to test elliptic first-order systems\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Elliptic/Systems/GetSourcesComputer.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers {\n/// \\ingroup TestingFrameworkGroup\n/// Helper functions to test elliptic first-order systems\nnamespace elliptic {\nnamespace detail {\n\ntemplate <typename System, typename... PrimalFields, typename... PrimalFluxes,\n          typename... FluxesArgsTags>\nvoid test_first_order_fluxes_computer_impl(\n    const DataVector& used_for_size, tmpl::list<PrimalFields...> /*meta*/,\n    tmpl::list<PrimalFluxes...> /*meta*/,\n    tmpl::list<FluxesArgsTags...> /*meta*/) {\n  static constexpr size_t Dim = System::volume_dim;\n  using FluxesComputer = typename System::fluxes_computer;\n  using inv_metric_tag = typename System::inv_metric_tag;\n  using vars_tag = ::Tags::Variables<tmpl::list<PrimalFields...>>;\n  using VarsType = typename vars_tag::type;\n  using deriv_vars_tag = db::add_tag_prefix<::Tags::deriv, vars_tag,\n                                            tmpl::size_t<Dim>, Frame::Inertial>;\n  using DerivVarsType = typename deriv_vars_tag::type;\n  using fluxes_tag = ::Tags::Variables<tmpl::list<PrimalFluxes...>>;\n  using FluxesType = typename fluxes_tag::type;\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(0.5, 2.);\n\n  // Generate random variables\n  const auto vars = make_with_random_values<VarsType>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n  const auto deriv_vars = make_with_random_values<DerivVarsType>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n\n  // Generate fluxes from the variables with random arguments\n  tuples::TaggedTuple<FluxesArgsTags...> fluxes_args{\n      make_with_random_values<typename FluxesArgsTags::type>(\n          make_not_null(&generator), make_not_null(&dist), used_for_size)...};\n  // Silence unused variable warning when fluxes args is empty\n  (void)fluxes_args;\n  FluxesType expected_fluxes{used_for_size.size()};\n  FluxesComputer::apply(\n      make_not_null(&get<PrimalFluxes>(expected_fluxes))...,\n      get<FluxesArgsTags>(fluxes_args)..., get<PrimalFields>(vars)...,\n      get<::Tags::deriv<PrimalFields, tmpl::size_t<Dim>, Frame::Inertial>>(\n          deriv_vars)...);\n\n  // Create a DataBox\n  auto box = db::create<db::AddSimpleTags<vars_tag, deriv_vars_tag, fluxes_tag,\n                                          FluxesArgsTags...>>(\n      vars, deriv_vars,\n      make_with_value<typename fluxes_tag::type>(\n          used_for_size, std::numeric_limits<double>::signaling_NaN()),\n      get<FluxesArgsTags>(fluxes_args)...);\n\n  // Apply the fluxes computer to the DataBox\n  db::mutate_apply<tmpl::list<PrimalFluxes...>,\n                   tmpl::list<FluxesArgsTags..., PrimalFields...,\n                              ::Tags::deriv<PrimalFields, tmpl::size_t<Dim>,\n                                            Frame::Inertial>...>>(\n      FluxesComputer{}, make_not_null(&box));\n  CHECK(expected_fluxes == get<fluxes_tag>(box));\n\n  {\n    INFO(\"Fluxes computer on face\");\n    // Test that the two overloads are consistent: the one applied on the face\n    // just has the face normal \"baked in\".\n    const auto face_normal = make_with_random_values<tnsr::i<DataVector, Dim>>(\n        make_not_null(&generator), make_not_null(&dist), used_for_size);\n    tnsr::I<DataVector, Dim> face_normal_vector{used_for_size.size()};\n    if constexpr (std::is_same_v<inv_metric_tag, void>) {\n      for (size_t i = 0; i < Dim; ++i) {\n        face_normal_vector.get(i) = face_normal.get(i);\n      }\n    } else {\n      const auto& inv_metric = get<inv_metric_tag>(fluxes_args);\n      raise_or_lower_index(make_not_null(&face_normal_vector), face_normal,\n                           inv_metric);\n    }\n    DerivVarsType n_times_vars{used_for_size.size()};\n    normal_times_flux(make_not_null(&n_times_vars), face_normal, vars);\n    VarsType zero_vars{used_for_size.size(), 0.};\n    FluxesComputer::apply(\n        make_not_null(&get<PrimalFluxes>(expected_fluxes))...,\n        get<FluxesArgsTags>(fluxes_args)..., get<PrimalFields>(zero_vars)...,\n        get<::Tags::deriv<PrimalFields, tmpl::size_t<Dim>, Frame::Inertial>>(\n            n_times_vars)...);\n    FluxesType fluxes_on_face{used_for_size.size()};\n    FluxesComputer::apply(make_not_null(&get<PrimalFluxes>(fluxes_on_face))...,\n                          get<FluxesArgsTags>(fluxes_args)..., face_normal,\n                          face_normal_vector, get<PrimalFields>(vars)...);\n    CHECK_VARIABLES_APPROX(fluxes_on_face, expected_fluxes);\n  }\n}\n\n}  // namespace detail\n\n/*!\n * \\brief Test the `System::fluxes_computer` is functional\n *\n * This function tests the following properties of the\n * `System::fluxes_computer`:\n *\n * - It works with the fields and fluxes specified in the `System`.\n * - It can be applied to a DataBox, i.e. its argument tags are consistent with\n *   its apply function.\n */\ntemplate <typename System>\nvoid test_first_order_fluxes_computer(const DataVector& used_for_size) {\n  detail::test_first_order_fluxes_computer_impl<System>(\n      used_for_size, typename System::primal_fields{},\n      typename System::primal_fluxes{},\n      typename System::fluxes_computer::argument_tags{});\n}\n\nnamespace detail {\n\ntemplate <typename SourcesComputer, typename... PrimalFields,\n          typename... PrimalFluxes, typename... SourcesArgsTags>\nvoid test_first_order_sources_computer_impl(\n    const DataVector& used_for_size, tmpl::list<PrimalFields...> /*meta*/,\n    tmpl::list<PrimalFluxes...> /*meta*/,\n    tmpl::list<SourcesArgsTags...> /*meta*/) {\n  static constexpr size_t Dim = tmpl::front<\n      typename tmpl::front<tmpl::list<PrimalFluxes...>>::type::index_list>::dim;\n  using vars_tag = ::Tags::Variables<tmpl::list<PrimalFields...>>;\n  using VarsType = typename vars_tag::type;\n  using fluxes_tag = ::Tags::Variables<tmpl::list<PrimalFluxes...>>;\n  using FluxesType = typename fluxes_tag::type;\n  using sources_tag = db::add_tag_prefix<::Tags::Source, vars_tag>;\n  using SourcesType = typename sources_tag::type;\n  using deriv_vars_tag = db::add_tag_prefix<::Tags::deriv, vars_tag,\n                                            tmpl::size_t<Dim>, Frame::Inertial>;\n  using DerivVarsType = typename deriv_vars_tag::type;\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(0.5, 2.);\n\n  // Generate random variables and fluxes. Note that the sources make use of the\n  // pre-computed fluxes as an optimization (see elliptic::first_order_sources),\n  // but for the purpose of this random-value test the fluxes don't have to be\n  // computed from the variables.\n  const auto vars = make_with_random_values<VarsType>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n  const auto fluxes = make_with_random_values<FluxesType>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n  const auto deriv_vars = make_with_random_values<DerivVarsType>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n\n  // Generate sources from the variables with random arguments\n  tuples::TaggedTuple<SourcesArgsTags...> sources_args{\n      make_with_random_values<typename SourcesArgsTags::type>(\n          make_not_null(&generator), make_not_null(&dist), used_for_size)...};\n  // Silence unused variable warning when sources args is empty\n  (void)sources_args;\n  SourcesType expected_sources{used_for_size.size(), 0.};\n\n  SourcesComputer::apply(\n      make_not_null(&get<::Tags::Source<PrimalFields>>(expected_sources))...,\n      get<SourcesArgsTags>(sources_args)..., get<PrimalFields>(vars)...,\n      get<::Tags::deriv<PrimalFields, tmpl::size_t<Dim>, Frame::Inertial>>(\n          deriv_vars)...,\n      get<PrimalFluxes>(fluxes)...);\n  // Create a DataBox\n  auto box = db::create<db::AddSimpleTags<vars_tag, deriv_vars_tag, fluxes_tag,\n                                          sources_tag, SourcesArgsTags...>>(\n      vars, deriv_vars, fluxes,\n      make_with_value<typename sources_tag::type>(used_for_size, 0.),\n      get<SourcesArgsTags>(sources_args)...);\n\n  // Apply the sources computer to the DataBox\n  db::mutate_apply<tmpl::list<::Tags::Source<PrimalFields>...>,\n                   tmpl::list<SourcesArgsTags..., PrimalFields...,\n                              ::Tags::deriv<PrimalFields, tmpl::size_t<Dim>,\n                                            Frame::Inertial>...,\n                              PrimalFluxes...>>(SourcesComputer{},\n                                                make_not_null(&box));\n  CHECK(expected_sources == get<sources_tag>(box));\n}\n\n}  // namespace detail\n\n/*!\n * \\brief Test the `System::sources_computer` is functional\n *\n * This function tests the following properties of the\n * `System::sources_computer`:\n *\n * - It works with the fields and fluxes specified in the `System`.\n * - It can be applied to a DataBox, i.e. its argument tags are consistent with\n *   its apply function.\n */\ntemplate <typename System, bool Linearized = false>\nvoid test_first_order_sources_computer(const DataVector& used_for_size) {\n  using sources_computer = ::elliptic::get_sources_computer<System, Linearized>;\n  if constexpr (not std::is_same_v<sources_computer, void>) {\n    using primal_fields =\n        tmpl::conditional_t<Linearized,\n                            db::wrap_tags_in<NonlinearSolver::Tags::Correction,\n                                             typename System::primal_fields>,\n                            typename System::primal_fields>;\n    using primal_fluxes =\n        tmpl::conditional_t<Linearized,\n                            db::wrap_tags_in<NonlinearSolver::Tags::Correction,\n                                             typename System::primal_fluxes>,\n                            typename System::primal_fluxes>;\n    detail::test_first_order_sources_computer_impl<sources_computer>(\n        used_for_size, primal_fields{}, primal_fluxes{},\n        typename sources_computer::argument_tags{});\n  }\n}\n\n}  // namespace elliptic\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(DgSubcell)\nadd_subdirectory(DiscontinuousGalerkin)\nadd_subdirectory(Imex)\nadd_subdirectory(Systems)\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DgSubcell/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY DgSubcellHelpers)\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit/\n  HEADERS\n  ProjectionTestHelpers.hpp\n  )\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  ProjectionTestHelpers.cpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Spectral\n\n  PRIVATE\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DgSubcell/ProjectionTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Evolution/DgSubcell/ProjectionTestHelpers.hpp\"\n\n#include <array>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TestHelpers::evolution::dg::subcell {\n// computes a simple polynomial over the grid that we then project and\n// reconstruct in the tests.\ntemplate <size_t Dim, typename Fr>\nDataVector cell_values(const size_t max_polynomial_degree_plus_one,\n                       const tnsr::I<DataVector, Dim, Fr>& coords) {\n  DataVector result(get<0>(coords).size(), 0.0);\n  for (size_t d = 0; d < Dim; ++d) {\n    for (size_t i = 0; i < max_polynomial_degree_plus_one; ++i) {\n      result += pow(coords.get(d), i);\n    }\n  }\n  return result;\n}\n\n// Computes the average in each finite volume cell multiplied by the cell's\n// volume.\ntemplate <size_t Dim>\nDataVector cell_averages_times_volume(\n    const size_t max_polynomial_degree_plus_one,\n    const Index<Dim>& subcell_extents) {\n  Index<Dim> subcell_boundary_extents{};\n  for (size_t d = 0; d < Dim; ++d) {\n    subcell_boundary_extents[d] = subcell_extents[d] + 1;\n  }\n  std::array<DataVector, Dim> subcell_boundary_coords{};\n\n  for (size_t d = 0; d < Dim; ++d) {\n    gsl::at(subcell_boundary_coords, d) =\n        DataVector(subcell_boundary_extents.product());\n    const auto& collocation_points_in_this_dim =\n        Spectral::collocation_points<Spectral::Basis::FiniteDifference,\n                                     Spectral::Quadrature::FaceCentered>(\n            subcell_boundary_extents[d]);\n    for (IndexIterator<Dim> index(subcell_boundary_extents); index; ++index) {\n      gsl::at(subcell_boundary_coords, d)[index.collapsed_index()] =\n          collocation_points_in_this_dim[index()[d]];\n    }\n  }\n\n  DataVector integral_values_at_bounadry_points(\n      subcell_boundary_extents.product(), 0.0);\n  // Compute the integral pointwise over the subcell boundary points.\n  for (size_t d = 0; d < Dim; ++d) {\n    for (size_t i = 0; i < max_polynomial_degree_plus_one; ++i) {\n      DataVector temp =\n          pow(gsl::at(subcell_boundary_coords, d), i + 1) / (i + 1.0);\n      for (size_t j = 0; j < Dim; ++j) {\n        if (j != d) {\n          temp *= gsl::at(subcell_boundary_coords, j);\n        }\n      }\n      integral_values_at_bounadry_points += temp;\n    }\n  }\n  // Compute the average\n  DataVector result(subcell_extents.product());\n  for (IndexIterator<Dim> subcell_it(subcell_extents); subcell_it;\n       ++subcell_it) {\n    if (Dim == 1) {\n      result[subcell_it.collapsed_index()] =\n          integral_values_at_bounadry_points[subcell_it()[0] + 1] -\n          integral_values_at_bounadry_points[subcell_it()[0]];\n    } else if (Dim == 2) {\n      Index<Dim> uu_bound{};\n      Index<Dim> ul_bound{};\n      Index<Dim> lu_bound{};\n      Index<Dim> ll_bound{};\n      for (size_t d = 0; d < Dim; ++d) {\n        uu_bound[d] = subcell_it()[d] + 1;\n        ll_bound[d] = subcell_it()[d];\n        ul_bound[d] = d == 0 ? subcell_it()[d] + 1 : subcell_it()[d];\n        lu_bound[d] = d == 0 ? subcell_it()[d] : subcell_it()[d] + 1;\n      }\n      result[subcell_it.collapsed_index()] =\n          integral_values_at_bounadry_points[collapsed_index(\n              uu_bound, subcell_boundary_extents)] -\n          integral_values_at_bounadry_points[collapsed_index(\n              ul_bound, subcell_boundary_extents)] +\n          integral_values_at_bounadry_points[collapsed_index(\n              ll_bound, subcell_boundary_extents)] -\n          integral_values_at_bounadry_points[collapsed_index(\n              lu_bound, subcell_boundary_extents)];\n    } else if (Dim == 3) {\n      Index<Dim> uuu_bound{};\n      Index<Dim> uul_bound{};\n      Index<Dim> ulu_bound{};\n      Index<Dim> luu_bound{};\n      Index<Dim> ull_bound{};\n      Index<Dim> lul_bound{};\n      Index<Dim> llu_bound{};\n      Index<Dim> lll_bound{};\n      for (size_t d = 0; d < Dim; ++d) {\n        uuu_bound[d] = subcell_it()[d] + 1;\n        uul_bound[d] = d == 2 ? subcell_it()[d] : subcell_it()[d] + 1;\n        ulu_bound[d] = d == 1 ? subcell_it()[d] : subcell_it()[d] + 1;\n        luu_bound[d] = d == 0 ? subcell_it()[d] : subcell_it()[d] + 1;\n        ull_bound[d] = d == 0 ? subcell_it()[d] + 1 : subcell_it()[d];\n        lul_bound[d] = d == 1 ? subcell_it()[d] + 1 : subcell_it()[d];\n        llu_bound[d] = d == 2 ? subcell_it()[d] + 1 : subcell_it()[d];\n        lll_bound[d] = subcell_it()[d];\n      }\n      result[subcell_it.collapsed_index()] =\n          integral_values_at_bounadry_points[collapsed_index(\n              uuu_bound, subcell_boundary_extents)] -\n          integral_values_at_bounadry_points[collapsed_index(\n              luu_bound, subcell_boundary_extents)] -\n          integral_values_at_bounadry_points[collapsed_index(\n              ulu_bound, subcell_boundary_extents)] +\n          integral_values_at_bounadry_points[collapsed_index(\n              llu_bound, subcell_boundary_extents)] -\n          integral_values_at_bounadry_points[collapsed_index(\n              uul_bound, subcell_boundary_extents)] +\n          integral_values_at_bounadry_points[collapsed_index(\n              lul_bound, subcell_boundary_extents)] +\n          integral_values_at_bounadry_points[collapsed_index(\n              ull_bound, subcell_boundary_extents)] -\n          integral_values_at_bounadry_points[collapsed_index(\n              lll_bound, subcell_boundary_extents)];\n    }\n  }\n  return result;\n}\n\n#define GET_DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                            \\\n  template DataVector cell_values(                                        \\\n      size_t max_polynomial_degree_plus_one,                              \\\n      const tnsr::I<DataVector, GET_DIM(data), Frame::ElementLogical>&    \\\n          coords);                                                        \\\n  template DataVector cell_values(                                        \\\n      size_t max_polynomial_degree_plus_one,                              \\\n      const tnsr::I<DataVector, GET_DIM(data), Frame::Inertial>& coords); \\\n  template DataVector cell_averages_times_volume(                         \\\n      size_t max_polynomial_degree_plus_one,                              \\\n      const Index<GET_DIM(data)>& subcell_extents);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (1, 2, 3))\n\n#undef GET_DIM\n#undef INSTANTIATION\n\n}  // namespace TestHelpers::evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DgSubcell/ProjectionTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nclass DataVector;\ntemplate <size_t Dim>\nclass Index;\ntemplate <size_t Dim>\nclass Mesh;\n/// \\endcond\n\nnamespace TestHelpers::evolution::dg::subcell {\n// computes a simple polynomial over the grid that we then project and\n// reconstruct in the tests.\ntemplate <size_t Dim, typename Fr>\nDataVector cell_values(size_t max_polynomial_degree_plus_one,\n                       const tnsr::I<DataVector, Dim, Fr>& coords);\n\n// Computes the average in each finite volume cell multiplied by the cell's\n// volume.\ntemplate <size_t Dim>\nDataVector cell_averages_times_volume(size_t max_polynomial_degree_plus_one,\n                                      const Index<Dim>& subcell_extents);\n}  // namespace TestHelpers::evolution::dg::subcell\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/CMakeLists.txt",
    "content": "## Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"EvolutionDgActionsHelpers\")\n\n# We split the instantiations across multiple files to allow better\n# parallelization of the build.\nset(LIBRARY_SOURCES\n  InstantiateCons1d.cpp\n  InstantiateCons2d.cpp\n  InstantiateCons3d.cpp\n  InstantiateConsPrim1d.cpp\n  InstantiateConsPrim2d.cpp\n  InstantiateConsPrim3d.cpp\n  InstantiateMixed1d.cpp\n  InstantiateMixed2d.cpp\n  InstantiateMixed3d.cpp\n  InstantiateMixedPrim1d.cpp\n  InstantiateMixedPrim2d.cpp\n  InstantiateMixedPrim3d.cpp\n  InstantiateNoncons1d.cpp\n  InstantiateNoncons2d.cpp\n  InstantiateNoncons3d.cpp\n  SystemType.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ErrorHandling\n  Evolution\n  Framework\n  LinearOperators\n  DiscontinuousGalerkin\n  Domain\n  DomainStructure\n  Interpolation\n  Spectral\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/SystemType.hpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate <SystemType system_type, bool UsePrims, size_t Dim>\nvoid test();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/BoundaryCorrection.hpp\"\n#include \"Evolution/BoundaryCorrectionTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/VolumeTermsImpl.tpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/Mortars.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Initialization/QuadratureTag.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarDataHolder.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarInfo.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/TimeDerivativeDecisions.hpp\"\n#include \"Evolution/PassVariables.hpp\"\n#include \"Evolution/Systems/Burgers/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/SystemType.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/WeakDivergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/Constant.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CloneUniquePtrs.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n\n// Var3 is used as an extra quantity in the DataBox that the time derivative\n// computation depends on. It could be loosely interpreted as a \"primitive\"\n// variable, or a compute tag retrieved for the time derivative computation.\nstruct Var3 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct PrimVar1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct PrimVar2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nstruct PrimVarsCompute : db::ComputeTag,\n                         Tags::Variables<tmpl::list<PrimVar1, PrimVar2<Dim>>> {\n  using base = Tags::Variables<tmpl::list<PrimVar1, PrimVar2<Dim>>>;\n  using return_type = typename base::type;\n  static void function(const gsl::not_null<return_type*> result,\n                       const Mesh<Dim>& mesh) {\n    result->initialize(mesh.number_of_grid_points());\n    get(get<PrimVar1>(*result)) = 5.0;\n    for (size_t i = 0; i < Dim; ++i) {\n      get<PrimVar2<Dim>>(*result).get(i) = 7.0 + i;\n    }\n  }\n  using argument_tags = tmpl::list<domain::Tags::Mesh<Dim>>;\n};\n\nstruct Var3Squared : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct PackagedVar1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct PackagedVar2 : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nstruct SimpleUnnormalizedFaceNormal\n    : db::ComputeTag,\n      domain::Tags::UnnormalizedFaceNormal<Dim> {\n  using base = domain::Tags::UnnormalizedFaceNormal<Dim>;\n  using return_type = typename base::type;\n  static void function(const gsl::not_null<return_type*> result,\n                       const Mesh<Dim - 1>& face_mesh,\n                       const Direction<Dim>& direction) {\n    for (size_t i = 0; i < Dim; ++i) {\n      result->get(i) =\n          DataVector{face_mesh.number_of_grid_points(),\n                     i == direction.dimension() ? 1.0 * i + 0.25 : 0.0};\n    }\n    const auto mag = magnitude(*result);\n    for (size_t i = 0; i < Dim; ++i) {\n      result->get(i) /= get(mag);\n    }\n  }\n  using argument_tags =\n      tmpl::list<domain::Tags::Mesh<Dim - 1>, domain::Tags::Direction<Dim>>;\n};\n\ntemplate <size_t Dim, bool UseMovingMesh>\nstruct FaceMeshVelocity : db::ComputeTag, domain::Tags::MeshVelocity<Dim> {\n  using base = domain::Tags::MeshVelocity<Dim>;\n  using return_type = typename base::type;\n  static void function(const gsl::not_null<return_type*> result,\n                       const Mesh<Dim - 1>& face_mesh,\n                       const Direction<Dim>& direction) {\n    if constexpr (UseMovingMesh) {\n      tnsr::I<DataVector, Dim> mesh_velocity{face_mesh.number_of_grid_points()};\n      for (size_t i = 0; i < Dim; ++i) {\n        mesh_velocity.get(i) =\n            (i == direction.dimension() ? 2.0 * i + 0.5 : 0.0);\n      }\n      *result = std::move(mesh_velocity);\n    } else {\n      *result = return_type{};\n    }\n  }\n  using argument_tags =\n      tmpl::list<domain::Tags::Mesh<Dim - 1>, domain::Tags::Direction<Dim>>;\n};\n\ntemplate <size_t Dim, SystemType system_type, bool HasPrimitiveVars>\nstruct TimeDerivativeTerms {\n  /// [dt_ta]\n  using temporary_tags = tmpl::list<Var3Squared>;\n  using common_argument_tags = tmpl::list<Var1, Var2<Dim>, Var3>;\n  using argument_tags =\n      tmpl::conditional_t<HasPrimitiveVars,\n                          tmpl::push_back<common_argument_tags, PrimVar1>,\n                          common_argument_tags>;\n  /// [dt_ta]\n\n  // Conservative system\n  /// [dt_con]\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      // Time derivatives returned by reference. All the tags in the\n      // variables_tag in the system struct.\n      const gsl::not_null<Scalar<DataVector>*> dt_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> dt_var2,\n\n      // Fluxes returned by reference. Listed in the system struct as\n      // flux_variables.\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> flux_var1,\n      const gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>\n          flux_var2,\n\n      // Temporaries returned by reference. Listed in temporary_tags above.\n      const gsl::not_null<Scalar<DataVector>*> square_var3,\n\n      // Arguments listed in argument_tags above\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3) {\n    get(*square_var3) = square(get(var3));\n\n    // Set source terms\n    get(*dt_var1) = get(*square_var3);\n    for (size_t d = 0; d < Dim; ++d) {\n      dt_var2->get(d) = get(var3) * d;\n    }\n\n    // Set fluxes\n    for (size_t i = 0; i < Dim; ++i) {\n      flux_var1->get(i) = square(get(var1)) * var2.get(i);\n      for (size_t j = 0; j < Dim; ++j) {\n        flux_var2->get(i, j) = var2.get(i) * var2.get(j) * get(var1);\n        if (i == j) {\n          flux_var2->get(i, j) += cube(get(var1));\n        }\n      }\n    }\n    return {true};\n  }\n  /// [dt_con]\n\n  // with prims\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      const gsl::not_null<Scalar<DataVector>*> dt_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> dt_var2,\n\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> flux_var1,\n      const gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>\n          flux_var2,\n\n      const gsl::not_null<Scalar<DataVector>*> square_var3,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3, const Scalar<DataVector>& prim_var1) {\n    apply(dt_var1, dt_var2, flux_var1, flux_var2, square_var3, var1, var2,\n          var3);\n    get(*dt_var1) += get(prim_var1);\n    return {true};\n  }\n\n  // Nonconservative system\n  /// [dt_nc]\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      // Time derivatives returned by reference. All the tags in the\n      // variables_tag in the system struct.\n      const gsl::not_null<Scalar<DataVector>*> dt_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> dt_var2,\n\n      // Temporaries returned by reference. Listed in temporary_tags above.\n      const gsl::not_null<Scalar<DataVector>*> square_var3,\n\n      // Partial derivative arguments. Listed in the system struct as\n      // gradient_variables.\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,\n      const tnsr::iJ<DataVector, Dim, Frame::Inertial>& d_var2,\n\n      // Arguments listed in argument_tags above\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3) {\n    get(*square_var3) = square(get(var3));\n\n    // Set source terms and nonconservative products\n    get(*dt_var1) = get(*square_var3);\n    for (size_t d = 0; d < Dim; ++d) {\n      get(*dt_var1) -= var2.get(d) * d_var1.get(d);\n      dt_var2->get(d) = get(var3) * d;\n      for (size_t i = 0; i < Dim; ++i) {\n        dt_var2->get(d) -= get(var1) * var2.get(i) * d_var2.get(i, d);\n      }\n    }\n    return {true};\n  }\n  /// [dt_nc]\n\n  // Mixed system\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      const gsl::not_null<Scalar<DataVector>*> dt_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> dt_var2,\n\n      const gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>\n          flux_var2,\n\n      const gsl::not_null<Scalar<DataVector>*> square_var3,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3) {\n    get(*square_var3) = square(get(var3));\n\n    // Set source terms and nonconservative products\n    get(*dt_var1) = get(*square_var3);\n    for (size_t d = 0; d < Dim; ++d) {\n      get(*dt_var1) -= var2.get(d) * d_var1.get(d);\n      dt_var2->get(d) = get(var3) * d;\n    }\n\n    // Set fluxes\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        flux_var2->get(i, j) = var2.get(i) * var2.get(j) * get(var1);\n        if (i == j) {\n          flux_var2->get(i, j) += cube(get(var1));\n        }\n      }\n    }\n    return {true};\n  }\n  /// [dt_mp]\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      const gsl::not_null<Scalar<DataVector>*> dt_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> dt_var2,\n\n      const gsl::not_null<tnsr::IJ<DataVector, Dim, Frame::Inertial>*>\n          flux_var2,\n\n      const gsl::not_null<Scalar<DataVector>*> square_var3,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3, const Scalar<DataVector>& prim_var1) {\n    apply(dt_var1, dt_var2, flux_var2, square_var3, d_var1, var1, var2, var3);\n    get(*dt_var1) += get(prim_var1);\n    return {true};\n  }\n  /// [dt_mp]\n};\n\ntemplate <size_t Dim, SystemType system_type, bool HasPrimitiveVars>\nstruct TimeDerivativeTermsWithVariables\n    : public ::evolution::PassVariables,\n      private TimeDerivativeTerms<Dim, system_type, HasPrimitiveVars> {\n  using base = TimeDerivativeTerms<Dim, system_type, HasPrimitiveVars>;\n\n  using temporary_tags = typename base::temporary_tags;\n  using argument_tags = typename base::argument_tags;\n\n  using dt_var1 = ::Tags::dt<Var1>;\n  using dt_var2 = ::Tags::dt<Var2<Dim>>;\n  using flux_var1 = ::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>;\n  using flux_var2 = ::Tags::Flux<Var2<Dim>, tmpl::size_t<Dim>, Frame::Inertial>;\n\n  /// [dt_con_variables]\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      // Time derivatives returned by reference. All the tags in the\n      // variables_tag in the system struct.\n      const gsl::not_null<Variables<tmpl::list<dt_var1, dt_var2>>*> dt_vars,\n\n      // Fluxes returned by reference. Listed in the system struct as\n      // flux_variables.\n      const gsl::not_null<Variables<tmpl::list<flux_var1, flux_var2>>*>\n          flux_vars,\n\n      // Temporaries returned by reference. Listed in temporary_tags above.\n      const gsl::not_null<Variables<tmpl::list<Var3Squared>>*> temporaries,\n\n      // Arguments listed in argument_tags above\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3) {\n    // just forward to other implementation to reduce code duplication\n    base::apply(get<dt_var1>(dt_vars), get<dt_var2>(dt_vars),\n                get<flux_var1>(flux_vars), get<flux_var2>(flux_vars),\n                get<Var3Squared>(temporaries), var1, var2, var3);\n    return {true};\n  }\n  /// [dt_con_variables]\n\n  // with prims\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      const gsl::not_null<Variables<tmpl::list<dt_var1, dt_var2>>*> dt_vars,\n\n      const gsl::not_null<Variables<tmpl::list<flux_var1, flux_var2>>*>\n          flux_vars,\n\n      const gsl::not_null<Variables<tmpl::list<Var3Squared>>*> temporaries,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3, const Scalar<DataVector>& prim_var1) {\n    base::apply(get<dt_var1>(dt_vars), get<dt_var2>(dt_vars),\n                get<flux_var1>(flux_vars), get<flux_var2>(flux_vars),\n                get<Var3Squared>(temporaries), var1, var2, var3, prim_var1);\n    return {true};\n  }\n\n  // Nonconservative system\n  /// [dt_nc_variables]\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      // Time derivatives returned by reference. All the tags in the\n      // variables_tag in the system struct.\n      const gsl::not_null<Variables<tmpl::list<dt_var1, dt_var2>>*> dt_vars,\n\n      // Temporaries returned by reference. Listed in temporary_tags above.\n      const gsl::not_null<Variables<tmpl::list<Var3Squared>>*> temporaries,\n\n      // Partial derivative arguments. Listed in the system struct as\n      // gradient_variables.\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,\n      const tnsr::iJ<DataVector, Dim, Frame::Inertial>& d_var2,\n\n      // Arguments listed in argument_tags above\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3) {\n    // just forward to other implementation to reduce code duplication\n    base::apply(get<dt_var1>(dt_vars), get<dt_var2>(dt_vars),\n                get<Var3Squared>(temporaries), d_var1, d_var2, var1, var2,\n                var3);\n    return {true};\n  }\n  /// [dt_nc_variables]\n\n  // Mixed system\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      const gsl::not_null<Variables<tmpl::list<dt_var1, dt_var2>>*> dt_vars,\n\n      const gsl::not_null<Variables<tmpl::list<flux_var2>>*> flux_vars,\n\n      const gsl::not_null<Variables<tmpl::list<Var3Squared>>*> temporaries,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3) {\n    // just forward to other implementation to reduce code duplication\n    base::apply(get<dt_var1>(dt_vars), get<dt_var2>(dt_vars),\n                get<flux_var2>(flux_vars), get<Var3Squared>(temporaries),\n                d_var1, var1, var2, var3);\n    return {true};\n  }\n\n  /// [dt_mp_variables]\n  static ::evolution::dg::TimeDerivativeDecisions<Dim> apply(\n      const gsl::not_null<Variables<tmpl::list<dt_var1, dt_var2>>*> dt_vars,\n\n      const gsl::not_null<Variables<tmpl::list<flux_var2>>*> flux_vars,\n\n      const gsl::not_null<Variables<tmpl::list<Var3Squared>>*> temporaries,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& d_var1,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n      const Scalar<DataVector>& var3, const Scalar<DataVector>& prim_var1) {\n    // just forward to other implementation to reduce code duplication\n    base::apply(get<dt_var1>(dt_vars), get<dt_var2>(dt_vars),\n                get<flux_var2>(flux_vars), get<Var3Squared>(temporaries),\n                d_var1, var1, var2, var3, prim_var1);\n    return {true};\n  }\n  /// [dt_mp_variables]\n};\n\ntemplate <size_t Dim>\nstruct NonconservativeNormalDotFlux {\n  using argument_tags = tmpl::list<>;\n  static void apply(\n      const gsl::not_null<Scalar<DataVector>*> var1_normal_dot_flux,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          var2_normal_dot_flux) {\n    get(*var1_normal_dot_flux) = 1.1;\n    for (size_t i = 0; i < Dim; ++i) {\n      var2_normal_dot_flux->get(i) = 1.3 + i;\n    }\n  }\n};\n\ntemplate <size_t Dim, bool HasPrims>\nstruct BoundaryTerms final : public ::evolution::BoundaryCorrection {\n  struct MaxAbsCharSpeed : db::SimpleTag {\n    using type = Scalar<DataVector>;\n  };\n\n  /// \\cond\n  explicit BoundaryTerms(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BoundaryTerms);  // NOLINT\n  /// \\endcond\n  BoundaryTerms() = default;\n  BoundaryTerms(const BoundaryTerms&) = default;\n  BoundaryTerms& operator=(const BoundaryTerms&) = default;\n  BoundaryTerms(BoundaryTerms&&) = default;\n  BoundaryTerms& operator=(BoundaryTerms&&) = default;\n  ~BoundaryTerms() override = default;\n\n  using variables_tags = tmpl::list<Var1, Var2<Dim>>;\n  using variables_tag = Tags::Variables<variables_tags>;\n\n  std::unique_ptr<BoundaryCorrection> get_clone() const override {\n    return std::make_unique<BoundaryTerms>(*this);\n  }\n\n  void pup(PUP::er& p) override {  // NOLINT\n    BoundaryCorrection::pup(p);\n  }\n\n  /// [bt_nnv]\n  static constexpr bool need_normal_vector = false;\n  /// [bt_nnv]\n\n  /// [bt_ta]\n  using dg_package_field_tags = tmpl::push_back<\n      tmpl::append<db::wrap_tags_in<::Tags::NormalDotFlux, variables_tags>,\n                   variables_tags>,\n      MaxAbsCharSpeed>;\n  using dg_package_data_temporary_tags = tmpl::list<Var3Squared>;\n  using dg_package_data_primitive_tags =\n      tmpl::conditional_t<HasPrims, tmpl::list<PrimVar1>, tmpl::list<>>;\n  using dg_package_data_volume_tags =\n      tmpl::conditional_t<HasPrims, tmpl::list<Tags::TimeStepId>, tmpl::list<>>;\n  using dg_boundary_terms_volume_tags =\n      tmpl::conditional_t<HasPrims, tmpl::list<Tags::TimeStepId>, tmpl::list<>>;\n  /// [bt_ta]\n\n  // Conservative system\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    *out_normal_dot_flux_var1 = dot_product(flux_var1, normal_covector);\n    if (mesh_velocity.has_value()) {\n      get(*out_normal_dot_flux_var1) -=\n          get(var1) * get(dot_product(*mesh_velocity, normal_covector));\n    }\n    for (size_t i = 0; i < Dim; ++i) {\n      out_normal_dot_flux_var2->get(i) =\n          flux_var2.get(i, 0) * normal_covector.get(0);\n      if (mesh_velocity.has_value()) {\n        out_normal_dot_flux_var2->get(i) -=\n            var2.get(i) * get<0>(*mesh_velocity) * normal_covector.get(0);\n      }\n      for (size_t j = 1; j < Dim; ++j) {\n        out_normal_dot_flux_var2->get(i) +=\n            flux_var2.get(i, j) * normal_covector.get(j);\n        if (mesh_velocity.has_value()) {\n          out_normal_dot_flux_var2->get(i) -=\n              var2.get(i) * mesh_velocity->get(j) * normal_covector.get(j);\n        }\n      }\n    }\n    *out_var1 = var1;\n    *out_var2 = var2;\n\n    get(*max_abs_char_speed) = 2.0 * max(get(var3_squared));\n\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*max_abs_char_speed) += get(*normal_dot_mesh_velocity);\n    }\n    return max(get(*max_abs_char_speed));\n  }\n\n  // Conservative system with prim vars\n  /// [bt_cp]\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& flux_var1,\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n      const Scalar<DataVector>& prim_var1,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n\n      const TimeStepId& time_step_id) const {\n    dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                    out_var1, out_var2, max_abs_char_speed, var1, var2,\n                    flux_var1, flux_var2, var3_squared, normal_covector,\n                    mesh_velocity, normal_dot_mesh_velocity);\n    get(*out_var1) += get(prim_var1) + time_step_id.step_time().value();\n    return max(get(*max_abs_char_speed));\n  }\n  /// [bt_cp]\n\n  // Nonconservative system\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    get(*out_normal_dot_flux_var1) =\n        get(var1) + get(dot_product(var2, normal_covector));\n    if (mesh_velocity.has_value()) {\n      get(*out_normal_dot_flux_var1) -=\n          get(dot_product(*mesh_velocity, normal_covector));\n    }\n    for (size_t i = 0; i < Dim; ++i) {\n      out_normal_dot_flux_var2->get(i) =\n          normal_covector.get(i) * normal_covector.get(0) * var2.get(0);\n      if (mesh_velocity.has_value()) {\n        out_normal_dot_flux_var2->get(i) -=\n            var2.get(i) * get<0>(*mesh_velocity) * normal_covector.get(0);\n      }\n      for (size_t j = 1; j < Dim; ++j) {\n        out_normal_dot_flux_var2->get(i) +=\n            normal_covector.get(i) * normal_covector.get(j) * var2.get(j);\n        if (mesh_velocity.has_value()) {\n          out_normal_dot_flux_var2->get(i) -=\n              var2.get(i) * mesh_velocity->get(j) * normal_covector.get(j);\n        }\n      }\n    }\n    *out_var1 = var1;\n    *out_var2 = var2;\n\n    get(*max_abs_char_speed) = 2.0 * max(get(var3_squared));\n\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*max_abs_char_speed) += get(*normal_dot_mesh_velocity);\n    }\n    return max(get(*max_abs_char_speed));\n  }\n\n  // Mixed system\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity) const {\n    get(*out_normal_dot_flux_var1) =\n        get(var1) + get(dot_product(var2, normal_covector));\n    if (mesh_velocity.has_value()) {\n      get(*out_normal_dot_flux_var1) -=\n          get(dot_product(*mesh_velocity, normal_covector));\n    }\n    for (size_t i = 0; i < Dim; ++i) {\n      out_normal_dot_flux_var2->get(i) =\n          flux_var2.get(i, 0) * normal_covector.get(0);\n      if (mesh_velocity.has_value()) {\n        out_normal_dot_flux_var2->get(i) -=\n            var2.get(i) * get<0>(*mesh_velocity) * normal_covector.get(0);\n      }\n      for (size_t j = 1; j < Dim; ++j) {\n        out_normal_dot_flux_var2->get(i) +=\n            flux_var2.get(i, j) * normal_covector.get(j);\n        if (mesh_velocity.has_value()) {\n          out_normal_dot_flux_var2->get(i) -=\n              var2.get(i) * mesh_velocity->get(j) * normal_covector.get(j);\n        }\n      }\n    }\n    *out_var1 = var1;\n    *out_var2 = var2;\n\n    get(*max_abs_char_speed) = 2.0 * max(get(var3_squared));\n\n    if (normal_dot_mesh_velocity.has_value()) {\n      get(*max_abs_char_speed) += get(*normal_dot_mesh_velocity);\n    }\n    return max(get(*max_abs_char_speed));\n  }\n\n  // Mixed system with prims\n  /// [bt_mp]\n  double dg_package_data(\n      const gsl::not_null<Scalar<DataVector>*> out_normal_dot_flux_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*>\n          out_normal_dot_flux_var2,\n      const gsl::not_null<Scalar<DataVector>*> out_var1,\n      const gsl::not_null<tnsr::I<DataVector, Dim, Frame::Inertial>*> out_var2,\n      const gsl::not_null<Scalar<DataVector>*> max_abs_char_speed,\n\n      const Scalar<DataVector>& var1,\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& var2,\n\n      const tnsr::IJ<DataVector, Dim, Frame::Inertial>& flux_var2,\n\n      const Scalar<DataVector>& var3_squared,\n\n      const Scalar<DataVector>& prim_var1,\n\n      const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n          mesh_velocity,\n      const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n\n      const TimeStepId& time_step_id) const {\n    dg_package_data(out_normal_dot_flux_var1, out_normal_dot_flux_var2,\n                    out_var1, out_var2, max_abs_char_speed, var1, var2,\n                    flux_var2, var3_squared, normal_covector, mesh_velocity,\n                    normal_dot_mesh_velocity);\n    get(*out_var1) += get(prim_var1) + time_step_id.step_time().value();\n    return max(get(*max_abs_char_speed));\n  }\n  /// [bt_mp]\n};\n\ntemplate <size_t Dim, bool HasPrims>\nPUP::able::PUP_ID BoundaryTerms<Dim, HasPrims>::my_PUP_ID = 0;\n\n// We only use an DemandOutgoingCharSpeeds boundary condition with a static\n// member variable that we set to verify the call went through. The actual\n// boundary condition implementation is tested elsewhere.\ntemplate <size_t Dim>\nclass DemandOutgoingCharSpeeds;\n\ntemplate <size_t Dim>\nclass BoundaryCondition : public domain::BoundaryConditions::BoundaryCondition {\n public:\n  BoundaryCondition() = default;\n  BoundaryCondition(BoundaryCondition&&) = default;\n  BoundaryCondition& operator=(BoundaryCondition&&) = default;\n  BoundaryCondition(const BoundaryCondition&) = default;\n  BoundaryCondition& operator=(const BoundaryCondition&) = default;\n  ~BoundaryCondition() override = default;\n  explicit BoundaryCondition(CkMigrateMessage* msg)\n      : domain::BoundaryConditions::BoundaryCondition(msg) {}\n\n  void pup(PUP::er& p) override {\n    domain::BoundaryConditions::BoundaryCondition::pup(p);\n  }\n};\n\ntemplate <size_t Dim>\nclass DemandOutgoingCharSpeeds : public BoundaryCondition<Dim> {\n public:\n  DemandOutgoingCharSpeeds() = default;\n  DemandOutgoingCharSpeeds(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds& operator=(DemandOutgoingCharSpeeds&&) = default;\n  DemandOutgoingCharSpeeds(const DemandOutgoingCharSpeeds&) = default;\n  DemandOutgoingCharSpeeds& operator=(const DemandOutgoingCharSpeeds&) =\n      default;\n  ~DemandOutgoingCharSpeeds() override = default;\n\n  explicit DemandOutgoingCharSpeeds(CkMigrateMessage* msg)\n      : BoundaryCondition<Dim>(msg) {}\n\n  WRAPPED_PUPable_decl_base_template(\n      domain::BoundaryConditions::BoundaryCondition, DemandOutgoingCharSpeeds);\n\n  auto get_clone() const -> std::unique_ptr<\n      domain::BoundaryConditions::BoundaryCondition> override {\n    return std::make_unique<DemandOutgoingCharSpeeds<Dim>>(*this);\n  }\n\n  static constexpr ::evolution::BoundaryConditions::Type bc_type =\n      ::evolution::BoundaryConditions::Type::DemandOutgoingCharSpeeds;\n\n  void pup(PUP::er& p) override { BoundaryCondition<Dim>::pup(p); }\n\n  using dg_interior_evolved_variables_tags = tmpl::list<>;\n  using dg_interior_primitive_variables_tags = tmpl::list<>;\n  using dg_interior_temporary_tags = tmpl::list<>;\n  using dg_gridless_tags = tmpl::list<>;\n\n  static std::optional<std::string> dg_demand_outgoing_char_speeds(\n      const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n      /*face_mesh_velocity*/,\n      const tnsr::i<DataVector, Dim, Frame::Inertial>&\n      /*outward_directed_normal_covector*/) {\n    DemandOutgoingCharSpeeds<Dim>::number_of_times_called += 1;\n    return std::nullopt;\n  }\n\n  static size_t number_of_times_called;\n};\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID DemandOutgoingCharSpeeds<Dim>::my_PUP_ID = 0;\n\ntemplate <size_t Dim>\nsize_t DemandOutgoingCharSpeeds<Dim>::number_of_times_called = 0;\n\ntemplate <size_t Dim, SystemType system_type, bool HasPrimitiveVariables,\n          bool PassVariables>\nstruct System {\n  static constexpr bool has_primitive_and_conservative_vars =\n      HasPrimitiveVariables;\n  static constexpr size_t volume_dim = Dim;\n\n  using boundary_conditions_base = BoundaryCondition<Dim>;\n\n  using variables_tag = Tags::Variables<tmpl::list<Var1, Var2<Dim>>>;\n  using flux_variables = tmpl::conditional_t<\n      system_type == SystemType::Conservative, tmpl::list<Var1, Var2<Dim>>,\n      tmpl::conditional_t<system_type == SystemType::Nonconservative,\n                          tmpl::list<>, tmpl::list<Var2<Dim>>>>;\n  using gradient_variables = tmpl::conditional_t<\n      system_type == SystemType::Conservative, tmpl::list<>,\n      tmpl::conditional_t<system_type == SystemType::Nonconservative,\n                          tmpl::list<Var1, Var2<Dim>>, tmpl::list<Var1>>>;\n  using primitive_variables_tag =\n      Tags::Variables<tmpl::list<PrimVar1, PrimVar2<Dim>>>;\n\n  using compute_volume_time_derivative_terms = tmpl::conditional_t<\n      PassVariables,\n      TimeDerivativeTermsWithVariables<Dim, system_type,\n                                       has_primitive_and_conservative_vars>,\n      TimeDerivativeTerms<Dim, system_type,\n                          has_primitive_and_conservative_vars>>;\n\n  using normal_dot_fluxes = NonconservativeNormalDotFlux<Dim>;\n};\n\ntemplate <typename Metavariables>\nstruct component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Metavariables::volume_dim>;\n\n  using variables_tag = typename Metavariables::system::variables_tag;\n  using internal_directions =\n      domain::Tags::InternalDirections<Metavariables::volume_dim>;\n  using boundary_directions_interior =\n      domain::Tags::BoundaryDirectionsInterior<Metavariables::volume_dim>;\n\n  using simple_tags = tmpl::flatten<tmpl::list<\n      ::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep,\n      ::Tags::Time, ::Tags::AdaptiveSteppingDiagnostics,\n      ::evolution::dg::Tags::Quadrature, variables_tag,\n      db::add_tag_prefix<::Tags::dt, variables_tag>,\n      ::Tags::HistoryEvolvedVariables<variables_tag>, Var3,\n      domain::Tags::Mesh<Metavariables::volume_dim>,\n      ::domain::Tags::FunctionsOfTimeInitialize,\n      domain::CoordinateMaps::Tags::CoordinateMap<Metavariables::volume_dim,\n                                                  Frame::Grid, Frame::Inertial>,\n      domain::Tags::Element<Metavariables::volume_dim>,\n      domain::Tags::NeighborMesh<Metavariables::volume_dim>,\n      domain::Tags::Coordinates<Metavariables::volume_dim, Frame::Inertial>,\n      domain::Tags::InverseJacobian<Metavariables::volume_dim,\n                                    Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::MeshVelocity<Metavariables::volume_dim>,\n      domain::Tags::DivMeshVelocity,\n      domain::Tags::ElementMap<Metavariables::volume_dim, Frame::Grid>,\n      tmpl::conditional_t<\n          Metavariables::local_time_stepping,\n          tmpl::list<::Tags::ConcreteTimeStepper<LtsTimeStepper>>,\n          tmpl::list<::Tags::ConcreteTimeStepper<TimeStepper>>>>>;\n  using common_compute_tags = tmpl::list<\n      domain::Tags::JacobianCompute<Metavariables::volume_dim,\n                                    Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::DetInvJacobianCompute<\n          Metavariables::volume_dim, Frame::ElementLogical, Frame::Inertial>,\n      domain::Tags::InternalDirectionsCompute<Metavariables::volume_dim>,\n      domain::Tags::InterfaceCompute<\n          internal_directions,\n          domain::Tags::Direction<Metavariables::volume_dim>>,\n      domain::Tags::InterfaceCompute<\n          internal_directions,\n          domain::Tags::InterfaceMesh<Metavariables::volume_dim>>,\n      domain::Tags::InterfaceCompute<\n          internal_directions,\n          SimpleUnnormalizedFaceNormal<Metavariables::volume_dim>>,\n      domain::Tags::InterfaceCompute<\n          internal_directions,\n          FaceMeshVelocity<Metavariables::volume_dim,\n                           Metavariables::use_moving_mesh>>,\n\n      domain::Tags::BoundaryDirectionsInteriorCompute<\n          Metavariables::volume_dim>,\n      domain::Tags::InterfaceCompute<\n          boundary_directions_interior,\n          domain::Tags::Direction<Metavariables::volume_dim>>,\n      domain::Tags::InterfaceCompute<\n          boundary_directions_interior,\n          domain::Tags::InterfaceMesh<Metavariables::volume_dim>>,\n      domain::Tags::Slice<internal_directions, Var1>,\n      domain::Tags::Slice<internal_directions, Var2<Metavariables::volume_dim>>,\n      time_stepper_ref_tags<tmpl::conditional_t<\n          Metavariables::local_time_stepping, LtsTimeStepper, TimeStepper>>>;\n  using compute_tags = tmpl::conditional_t<\n      Metavariables::system::has_primitive_and_conservative_vars,\n      tmpl::push_front<common_compute_tags,\n                       PrimVarsCompute<Metavariables::volume_dim>>,\n      common_compute_tags>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::flatten<tmpl::list<\n              ActionTesting::InitializeDataBox<simple_tags, compute_tags>,\n              ::evolution::dg::Initialization::Mortars<\n                  Metavariables::volume_dim>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<::evolution::dg::Actions::ComputeTimeDerivative<\n              Metavariables::volume_dim, typename Metavariables::system,\n              AllStepChoosers, Metavariables::local_time_stepping,\n              Metavariables::use_nodegroup_dg_elements>>>>;\n};\n\ntemplate <size_t Dim, SystemType SystemTypeIn, bool LocalTimeStepping,\n          bool UseMovingMesh, bool HasPrimitiveVariables, bool PassVariables,\n          bool UseNodegroupDgElements>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  static constexpr SystemType system_type = SystemTypeIn;\n  static constexpr bool use_moving_mesh = UseMovingMesh;\n  static constexpr bool local_time_stepping = LocalTimeStepping;\n  static constexpr bool pass_variables = PassVariables;\n  static constexpr bool use_nodegroup_dg_elements = UseNodegroupDgElements;\n  using system =\n      System<Dim, system_type, HasPrimitiveVariables, pass_variables>;\n  using normal_dot_numerical_flux =\n      Tags::NumericalFlux<BoundaryTerms<Dim, HasPrimitiveVariables>>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::InitialExtents<Dim>, normal_dot_numerical_flux,\n                 domain::Tags::Domain<Dim>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<BoundaryCondition<Dim>,\n                   tmpl::list<DemandOutgoingCharSpeeds<Dim>>>,\n        tmpl::pair<::evolution::BoundaryCorrection,\n                   tmpl::list<BoundaryTerms<Dim, HasPrimitiveVariables>>>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   tmpl::list<StepChoosers::Constant>>>;\n  };\n\n  using component_list = tmpl::list<component<Metavariables>>;\n};\n\ntemplate <typename BoundaryCorrection, typename... PackagedFieldTags,\n          typename... ProjectedFieldTags, size_t Dim, typename TagRetriever,\n          typename... VolumeTags>\ndouble dg_package_data(\n    const gsl::not_null<Variables<tmpl::list<PackagedFieldTags...>>*>\n        packaged_data,\n    const BoundaryCorrection& boundary_correction,\n    const Variables<tmpl::list<ProjectedFieldTags...>>& projected_fields,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    const std::optional<Scalar<DataVector>>& normal_dot_mesh_velocity,\n    const TagRetriever& get_tag, tmpl::list<VolumeTags...> /*meta*/) {\n  return boundary_correction.dg_package_data(\n      make_not_null(&get<PackagedFieldTags>(*packaged_data))...,\n      get<ProjectedFieldTags>(projected_fields)..., normal_covector,\n      mesh_velocity, normal_dot_mesh_velocity, get_tag(VolumeTags{})...);\n}\n\ntemplate <bool LocalTimeStepping, bool UseMovingMesh, size_t Dim,\n          SystemType system_type, bool HasPrims, bool PassVariables,\n          bool UseNodegroupDgElements>\nvoid test_impl(const Spectral::Quadrature quadrature,\n               const ::dg::Formulation dg_formulation) {\n  CAPTURE(LocalTimeStepping);\n  CAPTURE(UseMovingMesh);\n  CAPTURE(Dim);\n  CAPTURE(system_type);\n  CAPTURE(HasPrims);\n  CAPTURE(PassVariables);\n  CAPTURE(quadrature);\n  CAPTURE(dg_formulation);\n  using metavars =\n      Metavariables<Dim, system_type, LocalTimeStepping, UseMovingMesh,\n                    HasPrims, PassVariables, UseNodegroupDgElements>;\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  register_factory_classes_with_charm<metavars>();\n\n  using system = typename metavars::system;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  using variables_tag = typename system::variables_tag;\n  using flux_variables = typename system::flux_variables;\n  using flux_variables_tag = ::Tags::Variables<flux_variables>;\n  using fluxes_tag = db::add_tag_prefix<::Tags::Flux, flux_variables_tag,\n                                        tmpl::size_t<Dim>, Frame::Inertial>;\n  // The reference element in 2d denoted by X below:\n  // ^ eta\n  // +-+-+> xi\n  // |X| |\n  // +-+-+\n  // | | |\n  // +-+-+\n  //\n  // The \"self_id\" for the element that we are considering is marked by an X in\n  // the diagram. We consider a configuration with one neighbor in the +xi\n  // direction (east_id), and (in 2d and 3d) one in the -eta (south_id)\n  // direction.\n  //\n  // In 1d there aren't any projections to test, and in 3d we only have 1\n  // element in the z-direction.\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  ElementId<Dim> self_id{};\n  ElementId<Dim> east_id{};\n  ElementId<Dim> south_id{};  // not used in 1d\n  OrientationMap<Dim> south_orientation{};\n\n  if constexpr (Dim == 1) {\n    self_id = ElementId<Dim>{0, {{{1, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}}}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n  } else if constexpr (Dim == 2) {\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{0, 0}, {0, 0}}}};\n    south_orientation = OrientationMap<Dim>{\n        std::array{Direction<Dim>::lower_xi(), Direction<Dim>::lower_eta()}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, south_orientation};\n  } else {\n    static_assert(Dim == 3, \"Only implemented tests in 1, 2, and 3d\");\n    self_id = ElementId<Dim>{0, {{{1, 0}, {0, 0}, {0, 0}}}};\n    east_id = ElementId<Dim>{0, {{{1, 1}, {0, 0}, {0, 0}}}};\n    south_id = ElementId<Dim>{1, {{{0, 0}, {0, 0}, {0, 0}}}};\n    south_orientation = OrientationMap<Dim>{\n        std::array{Direction<Dim>::upper_eta(), Direction<Dim>::upper_zeta(),\n                   Direction<Dim>::upper_xi()}};\n    neighbors[Direction<Dim>::upper_xi()] =\n        Neighbors<Dim>{{east_id}, OrientationMap<Dim>::create_aligned()};\n    neighbors[Direction<Dim>::lower_eta()] =\n        Neighbors<Dim>{{south_id}, south_orientation};\n  }\n\n  const auto grid_to_inertial_map =\n      domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{});\n\n  std::array<size_t, Dim> extents{};\n  alg::iota(extents, 2_st);\n  const Mesh<Dim> mesh{extents, Spectral::Basis::Legendre, quadrature};\n  const Element<Dim> element{self_id, neighbors};\n\n  DirectionalIdMap<Dim, Mesh<Dim>> neighbor_mesh{};\n  for (const auto& [direction, direction_neighbors] : neighbors) {\n    for (const auto& neighbor : direction_neighbors) {\n      const auto& neighbor_orientation =\n          direction_neighbors.orientation(neighbor);\n      neighbor_mesh.emplace(DirectionalId{direction, neighbor},\n                            neighbor_orientation.inverse_map()(mesh));\n    }\n  }\n\n  const Slab time_slab{0.2, 3.4};\n  const TimeDelta time_step{time_slab, {1, 128}};\n  const TimeStepId time_step_id{true, 3, Time{time_slab, {3, 128}}};\n  const TimeStepId next_time_step_id = time_step_id.next_step(time_step);\n\n  MockRuntimeSystem runner = [&dg_formulation, &extents, &south_orientation,\n                              &grid_to_inertial_map, &time_step]() {\n    std::vector<DirectionMap<\n        Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n        boundary_conditions{2};\n    std::vector<Block<Dim>> blocks{2};\n    DirectionMap<Dim, BlockNeighbors<Dim>> neighbors_block0{};\n    DirectionMap<Dim, BlockNeighbors<Dim>> neighbors_block1{};\n    if constexpr (Dim > 1) {\n      neighbors_block0[Direction<Dim>::lower_eta()] =\n          BlockNeighbors<Dim>{1, south_orientation};\n      neighbors_block1[south_orientation(Direction<Dim>::lower_eta())] =\n          BlockNeighbors<Dim>{0, south_orientation.inverse_map()};\n      for (const auto& direction : Direction<Dim>::all_directions()) {\n        if (direction != Direction<Dim>::lower_eta()) {\n          boundary_conditions[0][direction] =\n              std::make_unique<DemandOutgoingCharSpeeds<Dim>>();\n        }\n        if (direction != south_orientation(Direction<Dim>::lower_eta())) {\n          boundary_conditions[1][direction] =\n              std::make_unique<DemandOutgoingCharSpeeds<Dim>>();\n        }\n      }\n    } else {\n      (void)south_orientation;\n      for (const auto& direction : Direction<Dim>::all_directions()) {\n        boundary_conditions[0][direction] =\n            std::make_unique<DemandOutgoingCharSpeeds<Dim>>();\n        boundary_conditions[1][direction] =\n            std::make_unique<DemandOutgoingCharSpeeds<Dim>>();\n      }\n    }\n    blocks[0] = Block<Dim>{\n        domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<Dim>{}),\n        0, neighbors_block0};\n    blocks[1] = Block<Dim>{\n        domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n            domain::CoordinateMaps::Identity<Dim>{}),\n        1, neighbors_block1};\n    Domain<Dim> domain{std::move(blocks)};\n    domain.inject_time_dependent_map_for_block(\n        0, grid_to_inertial_map->get_clone());\n    domain.inject_time_dependent_map_for_block(\n        1, grid_to_inertial_map->get_clone());\n\n    if constexpr (LocalTimeStepping) {\n      std::vector<std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>>\n          step_choosers;\n      step_choosers.emplace_back(\n          std::make_unique<StepChoosers::Constant>(time_step.value()));\n      return MockRuntimeSystem{\n          {std::vector<std::array<size_t, Dim>>{extents, extents},\n           typename metavars::normal_dot_numerical_flux::type{},\n           std::move(domain), dg_formulation,\n           std::make_unique<BoundaryTerms<Dim, HasPrims>>(),\n           std::move(boundary_conditions), 1e-8, std::move(step_choosers)}};\n    } else {\n      (void)time_step;\n      return MockRuntimeSystem{\n          {std::vector<std::array<size_t, Dim>>{extents, extents},\n           typename metavars::normal_dot_numerical_flux::type{},\n           std::move(domain), dg_formulation,\n           std::make_unique<BoundaryTerms<Dim, HasPrims>>(),\n           std::move(boundary_conditions)}};\n    }\n  }();\n  const auto get_tag = [&runner, &self_id](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<component<metavars>, tag>(runner,\n                                                                    self_id);\n  };\n\n  // Set the Jacobian to not be the identity because otherwise bugs creep in\n  // easily.\n  ::InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inv_jac{mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < Dim; ++i) {\n    inv_jac.get(i, i) = 2.0;\n  }\n  const auto det_inv_jacobian = determinant(inv_jac);\n  const auto jacobian = determinant_and_inverse(inv_jac).second;\n\n  // We don't need the Jacobian and map to be consistent since we are just\n  // checking that given a Jacobian, coordinates, etc., the correct terms are\n  // added to the evolution equations.\n  const auto logical_coords = logical_coordinates(mesh);\n  tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords{};\n  for (size_t i = 0; i < logical_coords.size(); ++i) {\n    inertial_coords[i] = logical_coords[i];\n  }\n\n  std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>> mesh_velocity{};\n  std::optional<Scalar<DataVector>> div_mesh_velocity{};\n  if (UseMovingMesh) {\n    const std::array<double, 3> velocities = {{1.2, -1.4, 0.3}};\n    mesh_velocity =\n        tnsr::I<DataVector, Dim, Frame::Inertial>{mesh.number_of_grid_points()};\n    for (size_t i = 0; i < Dim; ++i) {\n      mesh_velocity->get(i) = gsl::at(velocities, i);\n    }\n    div_mesh_velocity = Scalar<DataVector>{mesh.number_of_grid_points(), 1.5};\n  }\n\n  Variables<tmpl::list<Var1, Var2<Dim>>> evolved_vars{\n      mesh.number_of_grid_points()};\n  Scalar<DataVector> var3{mesh.number_of_grid_points()};\n  // Set the variables so they are constant in y & z\n  for (size_t i = 0; i < mesh.number_of_grid_points(); i += 2) {\n    get(get<Var1>(evolved_vars))[i] = 3.0;\n    get(get<Var1>(evolved_vars))[i + 1] = 2.0;\n    for (size_t j = 0; j < Dim; ++j) {\n      get<Var2<Dim>>(evolved_vars).get(j)[i] = j + 1.0;\n      get<Var2<Dim>>(evolved_vars).get(j)[i + 1] = j + 3.0;\n    }\n    get(var3)[i] = 5.0;\n    get(var3)[i + 1] = 6.0;\n  }\n  using dt_variables_tags = tmpl::list<::Tags::dt<Var1>, ::Tags::dt<Var2<Dim>>>;\n  Variables<dt_variables_tags> dt_evolved_vars{mesh.number_of_grid_points()};\n  const ::TimeSteppers::History<decltype(evolved_vars)> history{1};\n\n  // Compute expected volume fluxes\n  [[maybe_unused]] const auto expected_fluxes = [&evolved_vars, &inv_jac, &mesh,\n                                                 &mesh_velocity, &var3]() {\n    [[maybe_unused]] const auto& var1 = get<Var1>(evolved_vars);\n    [[maybe_unused]] const auto& var2 = get<Var2<Dim>>(evolved_vars);\n    Variables<db::wrap_tags_in<::Tags::Flux, flux_variables, tmpl::size_t<Dim>,\n                               Frame::Inertial>>\n        fluxes{mesh.number_of_grid_points()};\n    [[maybe_unused]] Scalar<DataVector> dt_var1{mesh.number_of_grid_points()};\n    [[maybe_unused]] tnsr::I<DataVector, Dim, Frame::Inertial> dt_var2{\n        mesh.number_of_grid_points()};\n    Scalar<DataVector> square_var3{mesh.number_of_grid_points()};\n    if constexpr (system_type == SystemType::Conservative) {\n      (void)inv_jac;\n      const auto flux_var1 = make_not_null(\n          &get<::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>(fluxes));\n      const auto flux_var2 = make_not_null(\n          &get<::Tags::Flux<Var2<Dim>, tmpl::size_t<Dim>, Frame::Inertial>>(\n              fluxes));\n      if constexpr (HasPrims) {\n        typename PrimVarsCompute<Dim>::type prims{mesh.number_of_grid_points()};\n        PrimVarsCompute<Dim>::function(make_not_null(&prims), mesh);\n        TimeDerivativeTerms<Dim, system_type, HasPrims>::apply(\n            make_not_null(&dt_var1), make_not_null(&dt_var2), flux_var1,\n            flux_var2, &square_var3, var1, var2, var3, get<PrimVar1>(prims));\n      } else {\n        TimeDerivativeTerms<Dim, system_type, HasPrims>::apply(\n            make_not_null(&dt_var1), make_not_null(&dt_var2), flux_var1,\n            flux_var2, &square_var3, var1, var2, var3);\n      }\n      if (mesh_velocity.has_value()) {\n        for (size_t i = 0; i < Dim; ++i) {\n          flux_var1->get(i) -= get(var1) * mesh_velocity->get(i);\n          for (size_t j = 0; j < Dim; ++j) {\n            flux_var2->get(i, j) -= var2.get(j) * mesh_velocity->get(i);\n          }\n        }\n      }\n    } else if constexpr (system_type == SystemType::Mixed) {\n      const auto flux_var2 = make_not_null(\n          &get<::Tags::Flux<Var2<Dim>, tmpl::size_t<Dim>, Frame::Inertial>>(\n              fluxes));\n      const auto partial_derivs =\n          partial_derivatives<tmpl::list<Var1>>(evolved_vars, mesh, inv_jac);\n      const auto& d_var1 =\n          get<::Tags::deriv<Var1, tmpl::size_t<Dim>, Frame::Inertial>>(\n              partial_derivs);\n      if constexpr (HasPrims) {\n        typename PrimVarsCompute<Dim>::type prims{mesh.number_of_grid_points()};\n        PrimVarsCompute<Dim>::function(make_not_null(&prims), mesh);\n        TimeDerivativeTerms<Dim, system_type, HasPrims>::apply(\n            make_not_null(&dt_var1), make_not_null(&dt_var2), flux_var2,\n            &square_var3, d_var1, var1, var2, var3, get<PrimVar1>(prims));\n      } else {\n        TimeDerivativeTerms<Dim, system_type, HasPrims>::apply(\n            make_not_null(&dt_var1), make_not_null(&dt_var2), flux_var2,\n            &square_var3, d_var1, var1, var2, var3);\n      }\n      if (mesh_velocity.has_value()) {\n        for (size_t i = 0; i < Dim; ++i) {\n          for (size_t j = 0; j < Dim; ++j) {\n            flux_var2->get(i, j) -= var2.get(j) * mesh_velocity->get(i);\n          }\n        }\n      }\n    } else {\n      (void)inv_jac;\n      (void)mesh_velocity;\n      (void)var3;\n    }\n    return fluxes;\n  }();\n\n  // Our moving mesh map doesn't actually move (we set a mesh velocity, etc.\n  // separately), but we need the FunctionsOfTime tag for boundary conditions.\n  // When checking boundary conditions we just test that the boundary condition\n  // function is called, not that the resulting code is correct, and so we just\n  // use an DemandOutgoingCharSpeeds condition, which doesn't actually need the\n  // coordinate maps.\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n\n  ActionTesting::emplace_component_and_initialize<component<metavars>>(\n      &runner, self_id,\n      {time_step_id,\n       next_time_step_id,\n       time_step,\n       time_step_id.step_time().value(),\n       AdaptiveSteppingDiagnostics{},\n       quadrature,\n       evolved_vars,\n       dt_evolved_vars,\n       history,\n       var3,\n       mesh,\n       clone_unique_ptrs(functions_of_time),\n       grid_to_inertial_map->get_clone(),\n       element,\n       neighbor_mesh,\n       inertial_coords,\n       inv_jac,\n       mesh_velocity,\n       div_mesh_velocity,\n       ElementMap<Dim, Frame::Grid>{\n           self_id,\n           domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n               domain::CoordinateMaps::Identity<Dim>{})},\n       std::make_unique<TimeSteppers::AdamsBashforth>(5)});\n  for (const auto& [direction, neighbor_ids] : neighbors) {\n    (void)direction;\n    for (const auto& neighbor_id : neighbor_ids) {\n      ActionTesting::emplace_component_and_initialize<component<metavars>>(\n          &runner, neighbor_id,\n          {time_step_id,\n           next_time_step_id,\n           time_step,\n           time_step_id.step_time().value(),\n           AdaptiveSteppingDiagnostics{},\n           quadrature,\n           evolved_vars,\n           dt_evolved_vars,\n           history,\n           var3,\n           mesh,\n           clone_unique_ptrs(functions_of_time),\n           grid_to_inertial_map->get_clone(),\n           element,\n           neighbor_mesh,\n           inertial_coords,\n           inv_jac,\n           mesh_velocity,\n           div_mesh_velocity,\n           ElementMap<Dim, Frame::Grid>{\n               neighbor_id,\n               domain::make_coordinate_map_base<Frame::BlockLogical,\n                                                Frame::Grid>(\n                   domain::CoordinateMaps::Identity<Dim>{})},\n           std::make_unique<TimeSteppers::AdamsBashforth>(5)});\n    }\n  }\n\n  // Initialize both the \"old\" and \"new\" mortars\n  ActionTesting::next_action<component<metavars>>(make_not_null(&runner),\n                                                  self_id);\n  const auto variables_before_compute_time_derivatives =\n      get_tag(variables_tag{});\n  // Start testing the actual dg::ComputeTimeDerivative action\n  DemandOutgoingCharSpeeds<Dim>::number_of_times_called = 0;\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<component<metavars>>(make_not_null(&runner),\n                                                  self_id);\n  CHECK(DemandOutgoingCharSpeeds<Dim>::number_of_times_called ==\n        element.external_boundaries().size());\n\n  Variables<tmpl::list<::Tags::dt<Var1>, ::Tags::dt<Var2<Dim>>>>\n      expected_dt_evolved_vars{mesh.number_of_grid_points()};\n\n  // Set dt<Var1>\n  if constexpr (system_type == SystemType::Nonconservative or\n                system_type == SystemType::Mixed) {\n    for (size_t i = 0; i < mesh.number_of_grid_points(); i += 2) {\n      if (quadrature == Spectral::Quadrature::GaussLobatto) {\n        get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars))[i] = 26.0;\n        get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars))[i + 1] = 39.0;\n      } else if (quadrature == Spectral::Quadrature::Gauss) {\n        get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars))[i] =\n            26.7320508075688785;\n        get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars))[i + 1] =\n            41.196152422706632;\n      } else {\n        ERROR(\"Only support Gauss and Gauss-Lobatto quadrature in test, not \"\n              << quadrature);\n      }\n    }\n    if (UseMovingMesh) {\n      const tnsr::i<DataVector, Dim> d_var1 =\n          get<::Tags::deriv<Var1, tmpl::size_t<Dim>, Frame::Inertial>>(\n              partial_derivatives<tmpl::list<Var1>>(\n                  variables_before_compute_time_derivatives, mesh, inv_jac));\n      get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars)) +=\n          get<0>(d_var1) * get<0>(*mesh_velocity);\n    }\n  } else {\n    // Deal with source terms:\n    get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars)) = square(get(var3));\n    if constexpr (UseMovingMesh) {\n      get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars)) -=\n          1.5 * get(get<Var1>(evolved_vars));\n    }\n    // Deal with volume flux divergence\n    if (quadrature == Spectral::Quadrature::GaussLobatto and\n        dg_formulation == ::dg::Formulation::StrongInertial) {\n      get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars)) -= 3.0;\n      if constexpr (UseMovingMesh) {\n        get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars)) +=\n            mesh_velocity->get(0) * -1.0;\n      }\n    } else {\n      if (dg_formulation == ::dg::Formulation::StrongInertial) {\n        const auto div = divergence(expected_fluxes, mesh, inv_jac);\n        const Scalar<DataVector>& div_var1_flux = get<::Tags::div<\n            ::Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>>(div);\n        get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars)) -=\n            get(div_var1_flux);\n      } else {\n        Variables<db::wrap_tags_in<Tags::div, typename fluxes_tag::tags_list>>\n            weak_div_fluxes{mesh.number_of_grid_points()};\n\n        InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n            det_jac_times_inverse_jacobian{};\n        ::dg::metric_identity_det_jac_times_inv_jac(\n            make_not_null(&det_jac_times_inverse_jacobian), mesh,\n            inertial_coords, jacobian);\n\n        weak_divergence(make_not_null(&weak_div_fluxes), expected_fluxes, mesh,\n                        det_jac_times_inverse_jacobian);\n        weak_div_fluxes *= get(det_inv_jacobian);\n\n        get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars)) +=\n            get(get<Tags::div<\n                    Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>>(\n                weak_div_fluxes));\n      }\n    }\n  }\n  // Set dt<Var2<Dim>>\n  if constexpr (system_type == SystemType::Conservative or\n                system_type == SystemType::Mixed) {\n    // Deal with source terms:\n    for (size_t j = 0; j < Dim; ++j) {\n      get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(j) =\n          j * get(var3);\n    }\n    // Deal with volume flux divergence\n    if (quadrature == Spectral::Quadrature::GaussLobatto and\n        dg_formulation == ::dg::Formulation::StrongInertial) {\n      get<0>(get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars)) += 4.0;\n      if constexpr (Dim > 1) {\n        get<1>(get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars)) -= 18.0;\n      }\n      if constexpr (Dim > 2) {\n        get<2>(get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars)) -= 21.0;\n      }\n      if constexpr (UseMovingMesh) {\n        for (size_t j = 0; j < Dim; ++j) {\n          get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(j) +=\n              mesh_velocity->get(0) * 2.0;\n        }\n      }\n    } else {\n      if (dg_formulation == ::dg::Formulation::StrongInertial) {\n        const auto div = divergence(expected_fluxes, mesh, inv_jac);\n        const tnsr::I<DataVector, Dim>& div_var2_flux = get<::Tags::div<\n            ::Tags::Flux<Var2<Dim>, tmpl::size_t<Dim>, Frame::Inertial>>>(div);\n        for (size_t i = 0; i < Dim; ++i) {\n          get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(i) -=\n              div_var2_flux.get(i);\n        }\n      } else {\n        Variables<db::wrap_tags_in<Tags::div, typename fluxes_tag::tags_list>>\n            weak_div_fluxes{mesh.number_of_grid_points()};\n\n        InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n            det_jac_times_inverse_jacobian{};\n        ::dg::metric_identity_det_jac_times_inv_jac(\n            make_not_null(&det_jac_times_inverse_jacobian), mesh,\n            inertial_coords, determinant_and_inverse(inv_jac).second);\n\n        weak_divergence(make_not_null(&weak_div_fluxes), expected_fluxes, mesh,\n                        det_jac_times_inverse_jacobian);\n\n        weak_div_fluxes *= get(det_inv_jacobian);\n\n        for (size_t i = 0; i < Dim; ++i) {\n          get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(i) +=\n              get<Tags::div<\n                  Tags::Flux<Var2<Dim>, tmpl::size_t<Dim>, Frame::Inertial>>>(\n                  weak_div_fluxes)\n                  .get(i);\n        }\n      }\n    }\n    if constexpr (UseMovingMesh) {\n      for (size_t j = 0; j < Dim; ++j) {\n        get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(j) -=\n            1.5 * get<Var2<Dim>>(evolved_vars).get(j);\n      }\n    }\n  } else {\n    const tnsr::iJ<DataVector, Dim> d_var2 =\n        quadrature == Spectral::Quadrature::GaussLobatto\n            ? tnsr::iJ<DataVector, Dim>{}\n            : get<::Tags::deriv<Var2<Dim>, tmpl::size_t<Dim>, Frame::Inertial>>(\n                  partial_derivatives<tmpl::list<Var1, Var2<Dim>>>(\n                      variables_before_compute_time_derivatives, mesh,\n                      inv_jac));\n    if (quadrature == Spectral::Quadrature::GaussLobatto) {\n      for (size_t i = 0; i < mesh.number_of_grid_points(); i += 2) {\n        for (size_t j = 0; j < Dim; ++j) {\n          get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(j)[i] = -6.;\n          get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(j)[i + 1] =\n              -12.;\n        }\n      }\n    } else {\n      for (size_t d = 0; d < Dim; ++d) {\n        get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(d) =\n            -get(get<Var1>(evolved_vars)) *\n            get<0>(get<Var2<Dim>>(evolved_vars)) * d_var2.get(0, d);\n        for (size_t j = 1; j < Dim; ++j) {\n          get<0>(get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars)) -=\n              -get(get<Var1>(evolved_vars)) *\n              get<Var2<Dim>>(evolved_vars).get(j) * d_var2.get(j, d);\n        }\n      }\n    }\n\n    // source term\n    for (size_t j = 0; j < Dim; ++j) {\n      get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(j) +=\n          j * get(var3);\n    }\n    if (UseMovingMesh) {\n      for (size_t j = 0; j < Dim; ++j) {\n        if (quadrature == Spectral::Quadrature::GaussLobatto) {\n          get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(j) +=\n              2.0 * get<0>(*mesh_velocity);\n        } else {\n          get<::Tags::dt<Var2<Dim>>>(expected_dt_evolved_vars).get(j) +=\n              d_var2.get(0, j) * get<0>(*mesh_velocity);\n        }\n      }\n    }\n  }\n  if constexpr (HasPrims) {\n    get(get<::Tags::dt<Var1>>(expected_dt_evolved_vars)) += 5.0;\n  }\n\n  const auto local_approx = Approx::custom().scale(1.0).epsilon(1.e-13);\n  CHECK_VARIABLES_CUSTOM_APPROX(\n      SINGLE_ARG(ActionTesting::get_databox_tag<\n                 component<metavars>,\n                 db::add_tag_prefix<::Tags::dt,\n                                    typename metavars::system::variables_tag>>(\n          runner, self_id)),\n      expected_dt_evolved_vars, local_approx);\n\n  const DirectionalId<Dim> mortar_id_east{Direction<Dim>::upper_xi(), east_id};\n\n  // At this point we know the volume terms have been computed correctly and\n  // we want to verify that the functions we expect to be called on the\n  // interfaces are called. Working out all the numbers explicitly would be\n  // tedious.\n  using variables_tags = typename variables_tag::tags_list;\n  using temporary_tags_for_face = tmpl::list<Var3Squared>;\n  using primitive_tags_for_face = tmpl::conditional_t<\n      system::has_primitive_and_conservative_vars,\n      typename BoundaryTerms<Dim, HasPrims>::dg_package_data_primitive_tags,\n      tmpl::list<>>;\n  using volume_tags = tmpl::conditional_t<\n      system::has_primitive_and_conservative_vars,\n      typename BoundaryTerms<Dim, HasPrims>::dg_package_data_volume_tags,\n      tmpl::list<>>;\n  using fluxes_tags = db::wrap_tags_in<::Tags::Flux, flux_variables,\n                                       tmpl::size_t<Dim>, Frame::Inertial>;\n  using mortar_tags_list =\n      typename BoundaryTerms<Dim, HasPrims>::dg_package_field_tags;\n  std::unordered_map<Direction<Dim>, tnsr::i<DataVector, Dim, Frame::Inertial>>\n      face_normals{};\n  for (const auto& direction : element.internal_boundaries()) {\n    tnsr::i<DataVector, Dim, Frame::Inertial> volume_inv_jac_in_direction{\n        inv_jac[0].size()};\n    for (size_t inertial_index = 0; inertial_index < Dim; ++inertial_index) {\n      volume_inv_jac_in_direction.get(inertial_index) =\n          inv_jac.get(direction.dimension(), inertial_index);\n    }\n\n    const Mesh<Dim - 1> face_mesh = mesh.slice_away(direction.dimension());\n    face_normals[direction] = tnsr::i<DataVector, Dim, Frame::Inertial>{\n        face_mesh.number_of_grid_points()};\n    ::dg::project_tensor_to_boundary(\n        make_not_null(&face_normals[direction]), volume_inv_jac_in_direction,\n        mesh, direction);\n    for (auto& component : face_normals[direction]) {\n      component *= direction.sign();\n    }\n\n    // GCC-7 gives unused direction warnings if we use structured\n    // bindings.\n    const auto normal_magnitude = magnitude(face_normals.at(direction));\n    for (size_t i = 0; i < Dim; ++i) {\n      face_normals[direction].get(i) /= get(normal_magnitude);\n    }\n  }\n\n  const auto& mortar_meshes = get_tag(::evolution::dg::Tags::MortarMesh<Dim>{});\n  const auto& mortar_infos = get_tag(::evolution::dg::Tags::MortarInfo<Dim>{});\n\n  Variables<tmpl::list<Var3Squared>> volume_temporaries{\n      mesh.number_of_grid_points()};\n  get(get<Var3Squared>(volume_temporaries)) = square(get(var3));\n  const auto compute_expected_mortar_data =\n      [&element, &expected_fluxes, &face_normals, &get_tag, &mesh,\n       &mesh_velocity, &mortar_meshes, &mortar_infos, &volume_temporaries,\n       &variables_before_compute_time_derivatives](\n          const Direction<Dim>& local_direction,\n          const ElementId<Dim>& local_neighbor_id, const bool local_data) {\n        const auto& face_mesh = mesh.slice_away(local_direction.dimension());\n        // First project data to the face in the direction of the mortar\n        Variables<\n            tmpl::append<variables_tags, fluxes_tags, temporary_tags_for_face,\n                         primitive_tags_for_face>>\n            fields_on_face{face_mesh.number_of_grid_points()};\n        ::dg::project_contiguous_data_to_boundary(\n            make_not_null(&fields_on_face),\n            variables_before_compute_time_derivatives, mesh, local_direction);\n        if constexpr (tmpl::size<fluxes_tags>::value != 0) {\n          ::dg::project_contiguous_data_to_boundary(\n              make_not_null(&fields_on_face), expected_fluxes, mesh,\n              local_direction);\n        } else {\n          (void)expected_fluxes;\n        }\n        ::dg::project_tensors_to_boundary<temporary_tags_for_face>(\n            make_not_null(&fields_on_face), volume_temporaries, mesh,\n            local_direction);\n        if constexpr (system::has_primitive_and_conservative_vars) {\n          ::dg::project_tensors_to_boundary<primitive_tags_for_face>(\n              make_not_null(&fields_on_face),\n              get_tag(typename system::primitive_variables_tag{}), mesh,\n              local_direction);\n        }\n        std::optional<tnsr::I<DataVector, Dim>> face_mesh_velocity{};\n        if (UseMovingMesh) {\n          face_mesh_velocity =\n              tnsr::I<DataVector, Dim>{face_mesh.number_of_grid_points()};\n          ::dg::project_tensor_to_boundary(\n              make_not_null(&*face_mesh_velocity), *mesh_velocity, mesh,\n              local_direction);\n        }\n\n        // Compute the normal dot mesh velocity and then the packaged data\n        DataVector packaged_data_buffer{\n            face_mesh.number_of_grid_points() *\n            Variables<mortar_tags_list>::number_of_independent_components};\n        Variables<mortar_tags_list> packaged_data{packaged_data_buffer.data(),\n                                                  packaged_data_buffer.size()};\n        std::optional<Scalar<DataVector>> normal_dot_mesh_velocity{};\n\n        if (face_mesh_velocity.has_value()) {\n          normal_dot_mesh_velocity = dot_product(\n              *face_mesh_velocity, face_normals.at(local_direction));\n        }\n        const double max_char_speed_on_face = dg_package_data(\n            make_not_null(&packaged_data), BoundaryTerms<Dim, HasPrims>{},\n            fields_on_face, face_normals.at(local_direction),\n            face_mesh_velocity, normal_dot_mesh_velocity, get_tag,\n            volume_tags{});\n\n        CHECK(\n            max_char_speed_on_face ==\n            max(get(get<typename BoundaryTerms<Dim, HasPrims>::MaxAbsCharSpeed>(\n                packaged_data))));\n        // Project the face data (stored in packaged_data) to the mortar\n        const auto mortar_id =\n            DirectionalId<Dim>{local_direction, local_neighbor_id};\n        const auto& mortar_mesh = mortar_meshes.at(mortar_id);\n        const auto& mortar_size = mortar_infos.at(mortar_id).mortar_size();\n\n        DataVector expected_data{};\n        if (Spectral::needs_projection(face_mesh, mortar_mesh, mortar_size)) {\n          expected_data = DataVector{\n              mortar_mesh.number_of_grid_points() *\n              Variables<mortar_tags_list>::number_of_independent_components};\n          Variables<mortar_tags_list> projected_packaged_data{\n              expected_data.data(), expected_data.size()};\n          // Since projected_packaged_data is non-owning, if we tried to\n          // move-assign it with the result of ::dg::project_to_mortar, it would\n          // then become owning and the buffer it previously pointed to wouldn't\n          // be filled (and we want it to be filled). So instead force a\n          // copy-assign by having an intermediary, data_to_copy.\n          const auto data_to_copy = ::dg::project_to_mortar(\n              packaged_data, face_mesh, mortar_mesh, mortar_size);\n          projected_packaged_data = data_to_copy;\n        } else {\n          expected_data = packaged_data_buffer;\n        }\n\n        const auto& orientation = element.neighbors()\n                                      .at(local_direction)\n                                      .orientation(local_neighbor_id);\n        if (local_data or orientation.is_aligned()) {\n          return expected_data;\n        } else {\n          return orient_variables_on_slice(expected_data, mortar_mesh.extents(),\n                                           local_direction.dimension(),\n                                           orientation);\n        }\n      };\n\n  const auto check_mortar_data = [&compute_expected_mortar_data,\n                                  &det_inv_jacobian, &get_tag,\n                                  &mesh](const auto& mortar_data,\n                                         const auto& mortar_id) {\n    CHECK_ITERABLE_APPROX(mortar_data.mortar_data.value(),\n                          compute_expected_mortar_data(mortar_id.direction(),\n                                                       mortar_id.id(), true));\n\n    // Check face normal and/or Jacobians\n    const bool using_gauss_points =\n        mesh.quadrature(mortar_id.direction().dimension()) ==\n        Spectral::Quadrature::Gauss;\n    REQUIRE(mortar_data.face_normal_magnitude.has_value());\n    const Scalar<DataVector>& local_face_normal_magnitude =\n        mortar_data.face_normal_magnitude.value();\n    CHECK(local_face_normal_magnitude ==\n          get<::evolution::dg::Tags::MagnitudeOfNormal>(\n              *get_tag(::evolution::dg::Tags::NormalCovectorAndMagnitude<Dim>{})\n                   .at(mortar_id.direction())));\n\n    if (using_gauss_points) {\n      REQUIRE(mortar_data.volume_det_inv_jacobian.has_value());\n      const Scalar<DataVector>& local_volume_det_inv_jacobian =\n          mortar_data.volume_det_inv_jacobian.value();\n      CHECK(local_volume_det_inv_jacobian == det_inv_jacobian);\n\n      // We use IrregularGridInterpolant to avoid reusing/copying the\n      // apply_matrices-based interpolation in the source tree.\n      const Mesh<Dim - 1> face_mesh =\n          mesh.slice_away(mortar_id.direction().dimension());\n      const intrp::Irregular<Dim> interpolator{\n          mesh,\n          interface_logical_coordinates(face_mesh, mortar_id.direction())};\n      Variables<tmpl::list<::Tags::TempScalar<0>>> volume_det_jacobian{\n          mesh.number_of_grid_points()};\n      get(get<::Tags::TempScalar<0>>(volume_det_jacobian)) =\n          1.0 / get(det_inv_jacobian);\n      const Scalar<DataVector> expected_local_face_det_jacobian =\n          get<::Tags::TempScalar<0>>(\n              interpolator.interpolate(volume_det_jacobian));\n\n      REQUIRE(mortar_data.face_det_jacobian.has_value());\n      const Scalar<DataVector>& local_face_det_jacobian =\n          mortar_data.face_det_jacobian.value();\n      CHECK_ITERABLE_APPROX(local_face_det_jacobian,\n                            expected_local_face_det_jacobian);\n    }\n  };\n  if (LocalTimeStepping) {\n    const auto& east_mortar_data =\n        get_tag(::evolution::dg::Tags::MortarDataHistory<Dim>{})\n            .at(mortar_id_east)\n            .local()\n            .data(time_step_id);\n    CHECK(get_tag(::evolution::dg::Tags::MortarDataHistory<Dim>{})\n              .at(mortar_id_east)\n              .local()\n              .integration_order(time_step_id) == 1);\n    check_mortar_data(east_mortar_data, mortar_id_east);\n  } else {\n    CHECK_ITERABLE_APPROX(\n        get_tag(::evolution::dg::Tags::MortarData<Dim>{})\n            .at(mortar_id_east)\n            .local()\n            .mortar_data.value(),\n        compute_expected_mortar_data(mortar_id_east.direction(),\n                                     mortar_id_east.id(), true));\n  }\n  const DirectionalId<Dim> east_neighbor_mortar_id{\n      element.neighbors()\n          .at(mortar_id_east.direction())\n          .orientation(mortar_id_east.id())(\n              mortar_id_east.direction().opposite()),\n      element.id()};\n  const auto& east_received_messages =\n      ActionTesting::get_inbox_tag<\n          component<metavars>,\n          ::evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n              Dim, UseNodegroupDgElements>>(runner, mortar_id_east.id())\n          .messages.at(time_step_id);\n  const auto east_received_data =\n      alg::find_if(east_received_messages, [&](const auto& entry) {\n        return entry.first == east_neighbor_mortar_id;\n      });\n  REQUIRE(east_received_data != east_received_messages.end());\n  CHECK_ITERABLE_APPROX(\n      east_received_data->second.boundary_correction_data.value(),\n      compute_expected_mortar_data(mortar_id_east.direction(),\n                                   mortar_id_east.id(), false));\n  CHECK(east_received_data->second.validity_range == next_time_step_id);\n\n  if constexpr (Dim > 1) {\n    const DirectionalId<Dim> mortar_id_south{Direction<Dim>::lower_eta(),\n                                             south_id};\n\n    if (LocalTimeStepping) {\n      const auto& south_mortar_data =\n          get_tag(::evolution::dg::Tags::MortarDataHistory<Dim>{})\n              .at(mortar_id_south)\n              .local()\n              .data(time_step_id);\n      CHECK(get_tag(::evolution::dg::Tags::MortarDataHistory<Dim>{})\n                .at(mortar_id_south)\n                .local()\n                .integration_order(time_step_id) == 1);\n      check_mortar_data(south_mortar_data, mortar_id_south);\n    } else {\n      CHECK_ITERABLE_APPROX(get_tag(::evolution::dg::Tags::MortarData<Dim>{})\n                                .at(mortar_id_south)\n                                .local()\n                                .mortar_data.value(),\n                            compute_expected_mortar_data(\n                                Direction<Dim>::lower_eta(), south_id, true));\n    }\n    const DirectionalId<Dim> south_neighbor_mortar_id{\n        element.neighbors()\n            .at(mortar_id_south.direction())\n            .orientation(mortar_id_south.id())(\n                mortar_id_south.direction().opposite()),\n        element.id()};\n    const auto& south_received_messages =\n        ActionTesting::get_inbox_tag<\n            component<metavars>,\n            ::evolution::dg::Tags::BoundaryCorrectionAndGhostCellsInbox<\n                Dim, UseNodegroupDgElements>>(runner, mortar_id_south.id())\n            .messages.at(time_step_id);\n    const auto south_received_data =\n        alg::find_if(south_received_messages, [&](const auto& entry) {\n          return entry.first == south_neighbor_mortar_id;\n        });\n    REQUIRE(south_received_data != south_received_messages.end());\n    CHECK(south_received_data->second.validity_range == next_time_step_id);\n    CHECK(south_received_data->second.volume_mesh == south_orientation(mesh));\n    CHECK(south_received_data->second.boundary_correction_mesh.value() ==\n          ::dg::mortar_mesh(\n              mesh.slice_away(south_neighbor_mortar_id.direction().dimension()),\n              south_orientation(mesh).slice_away(\n                  south_neighbor_mortar_id.direction().dimension())));\n    CHECK_ITERABLE_APPROX(\n        south_received_data->second.boundary_correction_data.value(),\n        compute_expected_mortar_data(mortar_id_south.direction(),\n                                     mortar_id_south.id(), false));\n  }\n  if constexpr (LocalTimeStepping) {\n    for (const auto& mortar_data :\n         get_tag(::evolution::dg::Tags::MortarData<Dim>{})) {\n      // When doing local time stepping the MortarData should've been moved into\n      // the MortarDataHistory.\n      CHECK_FALSE(mortar_data.second.local().mortar_data.has_value());\n      CHECK_FALSE(mortar_data.second.neighbor().mortar_data.has_value());\n    }\n  }\n}\n\ntemplate <SystemType system_type, bool UsePrims, size_t Dim>\nvoid test() {\n  // The test impl is structured in the following way:\n  // - the static mesh volume contributions are computed \"by-hand\" and used more\n  //   or less as a regression test. This is relatively easy for Gauss-Lobatto\n  //   points, a bit more tedious for Gauss points. We also assume the solution\n  //   is constant in the y & z direction to make the math easier. The math\n  //   implemented and checked against are the static mesh contributions coded\n  //   up in TimeDerivativeTerms, with the addition of the strong or weak flux\n  //   divergence that are computed by generic code outside the\n  //   TimeDerivativeTerms struct.\n  //\n  //   The conservative equations are:\n  //     dt var1 = -d_i (var1**2 + var2^i) + var3**2\n  //     dt var2^j = -d_i (var1 * var2^i * var2^j + delta^i_j var1**3) +var3 * j\n  //   if there are primitive variables then:\n  //     dt var1 += prim_var1\n  //\n  //   The non-conservative equations are:\n  //     dt var1 = -var2^i d_i var1 + var3**2\n  //     dt var2^j = -var1 * var2^i d_i var2^j + var3 * j\n  //\n  //   The mixed conservative non-conservative equations are:\n  //     dt var1 = -var2^i d_i var1 + var3**2\n  //     dt var2^j = -d_i (var1 * var2^i * var2^j + delta^i_j var1**3) +var3 * j\n  //   if there are primitive variables then:\n  //     dt var1 += prim_var1\n  //\n  //   The mesh only has 2 points per dimension to make the math not too\n  //   terrible. The differentiation matrices & weights for Gauss points are:\n  //    D^{strong}: {{-0.866025403784438, 0.866025403784438},\n  //                 {-0.866025403784438, 0.866025403784438}}\n  //    w: {1.0, 1.0}\n  //    D^{weak}: {{-0.866025403784438, -0.866025403784438},\n  //               {0.866025403784438, 0.866025403784438}}\n  //\n  //   Gauss-Lobatto:\n  //    D^{strong}: {{-1/2, 1/2}, {-1/2, 1/2}}\n  //    w: {1.0, 1.0}\n  //    D^{weak}: {{-1/2, -1/2}, {1/2, 1/2}}\n  //\n  //   The weak divergence matrix is given by:\n  //     D_{i,l}^{weak} = (w_l/w_i) D_{l,i}^{strong}\n  //\n  // - the additional parts, such as moving mesh and boundary corrections become\n  //   increasingly tedious (and unmaintainable) to work out separately\n  //   (implementing the entire moving-mesh DG algorithm in Python is _a lot_ of\n  //   work and extra code), so instead we verify that the expected mathematical\n  //   manipulations occur. Specifically, we check adding the mesh velocity to\n  //   the fluxes, and lifting the boundary contributions to the volume, even\n  //   though the mesh velocity and boundary contributions are not the correct\n  //   DG values\n\n  constexpr bool use_nodegroup_dg_elements = false;\n\n  const auto invoke_tests_with_quadrature_and_formulation =\n      [](const Spectral::Quadrature quadrature,\n         const ::dg::Formulation local_dg_formulation) {\n        const auto moving_mesh_helper = [&local_dg_formulation,\n                                         &quadrature](auto moving_mesh) {\n          // PassVariables == false\n          test_impl<false, std::decay_t<decltype(moving_mesh)>::value, Dim,\n                    system_type, UsePrims, false, use_nodegroup_dg_elements>(\n              quadrature, local_dg_formulation);\n          test_impl<true, std::decay_t<decltype(moving_mesh)>::value, Dim,\n                    system_type, UsePrims, false, use_nodegroup_dg_elements>(\n              quadrature, local_dg_formulation);\n\n          // PassVariables == true\n          test_impl<false, std::decay_t<decltype(moving_mesh)>::value, Dim,\n                    system_type, UsePrims, true, use_nodegroup_dg_elements>(\n              quadrature, local_dg_formulation);\n          test_impl<true, std::decay_t<decltype(moving_mesh)>::value, Dim,\n                    system_type, UsePrims, true, use_nodegroup_dg_elements>(\n              quadrature, local_dg_formulation);\n        };\n        moving_mesh_helper(std::integral_constant<bool, false>{});\n        moving_mesh_helper(std::integral_constant<bool, true>{});\n      };\n  for (const auto dg_formulation :\n       {::dg::Formulation::StrongInertial, ::dg::Formulation::WeakInertial}) {\n    invoke_tests_with_quadrature_and_formulation(\n        Spectral::Quadrature::GaussLobatto, dg_formulation);\n    invoke_tests_with_quadrature_and_formulation(Spectral::Quadrature::Gauss,\n                                                 dg_formulation);\n  }\n}\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateCons1d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Conservative, false, 1>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateCons2d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Conservative, false, 2>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateCons3d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Conservative, false, 3>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateConsPrim1d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Conservative, true, 1>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateConsPrim2d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Conservative, true, 2>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateConsPrim3d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Conservative, true, 3>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixed1d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, false, 1>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixed2d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, false, 2>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixed3d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, false, 3>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixedPrim1d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, true, 1>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixedPrim2d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, true, 2>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixedPrim3d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, true, 3>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixedPrimPrim1d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, true, 1>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixedPrimPrim2d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, true, 2>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateMixedPrimPrim3d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Mixed, true, 3>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateNoncons1d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Nonconservative, false, 1>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateNoncons2d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Nonconservative, false, 2>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/InstantiateNoncons3d.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeImpl.tpp\"\n\nnamespace TestHelpers::evolution::dg::Actions {\ntemplate void test<SystemType::Nonconservative, false, 3>();\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/SystemType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Actions/SystemType.hpp\"\n\n#include <ostream>\n#include <string>\n\nnamespace TestHelpers::evolution::dg::Actions {\nstd::ostream& operator<<(std::ostream& os, const SystemType t) {\n  return os << (t == SystemType::Conservative ? std::string{\"Conservative\"}\n                : t == SystemType::Nonconservative\n                    ? std::string{\"Nonconservative\"}\n                    : std::string{\"Mixed\"});\n}\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Actions/SystemType.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <iosfwd>\n\nnamespace TestHelpers::evolution::dg::Actions {\nenum SystemType { Conservative, Nonconservative, Mixed };\n\nstd::ostream& operator<<(std::ostream& os, SystemType t);\n}  // namespace TestHelpers::evolution::dg::Actions\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/BoundaryConditions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <optional>\n#include <random>\n#include <regex>\n#include <stdexcept>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedContainers.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Domain/BoundaryConditions/Periodic.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/BoundaryConditions/Type.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivativeHelpers.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/PyppFundamentals.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/NormalVectors.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::evolution::dg {\n/// Tags for testing DG code\nnamespace Tags {\n/// Tag for a `TaggedTuple` that holds the name of the python function that\n/// gives the result for computing what `Tag` should be.\n///\n/// If a Tag is only needed for or different for a specific boundary correction,\n/// then the `BoundaryCorrection` template parameter must be set.\n///\n/// The `Tag` template parameter is an input to the `dg_package_data` function.\n/// That is, the tag is one of the evolved variables, fluxes, or any other\n/// `dg_package_data_temporary_tags` that the boundary correction needs.\ntemplate <typename Tag, typename BoundaryCorrection = NoSuchType>\nstruct PythonFunctionName {\n  using tag = Tag;\n  using boundary_correction = BoundaryCorrection;\n  using type = std::string;\n};\n\n/// The name of the python function that returns the error message.\n///\n/// If `BoundaryCorrection` is `NoSuchType` then the same function will be used\n/// for all boundary corrections.\n///\n/// The python function must return `None` if there shouldn't be an error\n/// message.\ntemplate <typename BoundaryCorrection = NoSuchType>\nstruct PythonFunctionForErrorMessage {\n  using boundary_correction = BoundaryCorrection;\n  using type = std::string;\n};\n}  // namespace Tags\n\nnamespace detail {\ntemplate <typename ConversionClassList, typename... Args>\nstatic std::optional<std::string> call_for_error_message(\n    const std::string& module_name, const std::string& function_name,\n    const Args&... t) {\n  CAPTURE(function_name);\n  static_assert(sizeof...(Args) > 0,\n                \"Call to python which returns a Tensor of DataVectors must \"\n                \"pass at least one argument\");\n  using ReturnType = std::optional<std::string>;\n\n  PyObject* python_module = PyImport_ImportModule(module_name.c_str());\n  if (python_module == nullptr) {\n    PyErr_Print();\n    throw std::runtime_error{std::string(\"Could not find python module.\\n\") +\n                             module_name};\n  }\n  PyObject* func = PyObject_GetAttrString(python_module, function_name.c_str());\n  if (func == nullptr or not PyCallable_Check(func)) {\n    if (PyErr_Occurred()) {\n      PyErr_Print();\n    }\n    throw std::runtime_error{\"Could not find python function in module.\\n\"};\n  }\n\n  const std::array<size_t, sizeof...(Args)> arg_sizes{\n      {pypp::detail::ContainerPackAndUnpack<\n          Args, ConversionClassList>::get_size(t)...}};\n  const size_t npts = *std::max_element(arg_sizes.begin(), arg_sizes.end());\n  for (size_t i = 0; i < arg_sizes.size(); ++i) {\n    if (gsl::at(arg_sizes, i) != 1 and gsl::at(arg_sizes, i) != npts) {\n      ERROR(\"Each argument must return size 1 or \"\n            << npts\n            << \" (the number of points in the DataVector), but argument number \"\n            << i << \" has size \" << gsl::at(arg_sizes, i));\n    }\n  }\n  ReturnType result{};\n\n  for (size_t s = 0; s < npts; ++s) {\n    PyObject* args = pypp::make_py_tuple(\n        pypp::detail::ContainerPackAndUnpack<Args, ConversionClassList>::unpack(\n            t, s)...);\n    auto ret =\n        pypp::detail::call_work<typename pypp::detail::ContainerPackAndUnpack<\n            ReturnType, ConversionClassList>::unpacked_container>(python_module,\n                                                                  func, args);\n    if (ret.has_value()) {\n      result = std::move(ret);\n      break;\n    }\n  }\n  Py_DECREF(func);           // NOLINT\n  Py_DECREF(python_module);  // NOLINT\n  return result;\n}\n\ntemplate <typename BoundaryCorrection, typename... PythonFunctionNameTags>\nconst std::string& get_python_error_message_function(\n    const tuples::TaggedTuple<PythonFunctionNameTags...>&\n        python_boundary_condition_functions) {\n  return tuples::get<tmpl::conditional_t<\n      tmpl::list_contains_v<tmpl::list<PythonFunctionNameTags...>,\n                            Tags::PythonFunctionForErrorMessage<NoSuchType>>,\n      Tags::PythonFunctionForErrorMessage<NoSuchType>,\n      Tags::PythonFunctionForErrorMessage<BoundaryCorrection>>>(\n      python_boundary_condition_functions);\n}\n\ntemplate <typename Tag, typename BoundaryCorrection,\n          typename... PythonFunctionNameTags>\nconst std::string& get_python_tag_function(\n    const tuples::TaggedTuple<PythonFunctionNameTags...>&\n        python_boundary_condition_functions) {\n  return tuples::get<tmpl::conditional_t<\n      tmpl::list_contains_v<tmpl::list<PythonFunctionNameTags...>,\n                            Tags::PythonFunctionName<Tag, NoSuchType>>,\n      Tags::PythonFunctionName<Tag, NoSuchType>,\n      Tags::PythonFunctionName<Tag, BoundaryCorrection>>>(\n      python_boundary_condition_functions);\n}\n\ntemplate <typename BoundaryConditionHelper, typename AllTagsOnFaceList,\n          typename... TagsFromFace, typename... VolumeArgs>\nvoid apply_boundary_condition_impl(\n    BoundaryConditionHelper& boundary_condition_helper,\n    const Variables<AllTagsOnFaceList>& fields_on_interior_face,\n    tmpl::list<TagsFromFace...> /*meta*/, const VolumeArgs&... volume_args) {\n  boundary_condition_helper(get<TagsFromFace>(fields_on_interior_face)...,\n                            volume_args...);\n}\n\ntemplate <typename BoundaryConditionHelper, typename AllTagsOnExteriorFaceList,\n          typename BoundaryCondition, typename AllTagsOnFaceList,\n          typename... BoundaryCorrectionPackagedDataInputTags,\n          typename... TagsFromFace, typename... VolumeArgs, size_t Dim>\nvoid apply_boundary_condition_dg_ghost_impl(\n    BoundaryConditionHelper& boundary_condition_helper,\n    const gsl::not_null<Variables<AllTagsOnExteriorFaceList>*>\n        fields_on_exterior_face,\n    const BoundaryCondition& boundary_condition,\n    const Variables<AllTagsOnFaceList>& fields_on_interior_face,\n    const std::optional<tnsr::I<DataVector, Dim>>& face_mesh_velocity,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& interior_normal_covector,\n    tmpl::list<BoundaryCorrectionPackagedDataInputTags...> /*meta*/,\n    tmpl::list<TagsFromFace...> /*meta*/, const VolumeArgs&... volume_args) {\n  const std::optional<std::string> error_message = boundary_condition.dg_ghost(\n      get<BoundaryCorrectionPackagedDataInputTags>(fields_on_exterior_face)...,\n      face_mesh_velocity, interior_normal_covector,\n      get<TagsFromFace>(fields_on_interior_face)..., volume_args...);\n  boundary_condition_helper(error_message,\n                            get<TagsFromFace>(fields_on_interior_face)...,\n                            volume_args...);\n}\n\ntemplate <typename System, typename ConversionClassList,\n          typename BoundaryCorrection, typename... PythonFunctionNameTags,\n          typename BoundaryCondition, size_t FaceDim, typename DbTagsList,\n          typename... RangeTags, typename... EvolvedVariablesTags,\n          typename... BoundaryCorrectionPackagedDataInputTags,\n          typename... BoundaryConditionVolumeTags,\n          typename... ExtraTagsForPythonFromDataBox>\nvoid test_boundary_condition_with_python_impl(\n    const gsl::not_null<std::mt19937*> generator,\n    const std::string& python_module,\n    const tuples::TaggedTuple<PythonFunctionNameTags...>&\n        python_boundary_condition_functions,\n    const BoundaryCondition& boundary_condition,\n    const Index<FaceDim>& face_points,\n    const db::DataBox<DbTagsList>& box_of_volume_data,\n    const tuples::TaggedTuple<Tags::Range<RangeTags>...>& ranges,\n    const bool use_moving_mesh, tmpl::list<EvolvedVariablesTags...> /*meta*/,\n    tmpl::list<BoundaryCorrectionPackagedDataInputTags...> /*meta*/,\n    tmpl::list<BoundaryConditionVolumeTags...> /*meta*/,\n    tmpl::list<ExtraTagsForPythonFromDataBox...> /*meta*/,\n    const double epsilon) {\n  CAPTURE(FaceDim);\n  CAPTURE(python_module);\n  CAPTURE(face_points);\n  CAPTURE(use_moving_mesh);\n  CAPTURE(epsilon);\n  CAPTURE(pretty_type::name<BoundaryCorrection>());\n  const size_t number_of_points_on_face = face_points.product();\n\n  using variables_tag = typename System::variables_tag;\n  using variables_tags = typename variables_tag::tags_list;\n  using flux_variables = typename System::flux_variables;\n  using dt_variables_tags = db::wrap_tags_in<::Tags::dt, variables_tags>;\n\n  constexpr bool uses_ghost =\n      BoundaryCondition::bc_type ==\n          ::evolution::BoundaryConditions::Type::Ghost or\n      BoundaryCondition::bc_type ==\n          ::evolution::BoundaryConditions::Type::GhostAndTimeDerivative;\n  constexpr bool uses_time_derivative_condition =\n      BoundaryCondition::bc_type ==\n          ::evolution::BoundaryConditions::Type::TimeDerivative or\n      BoundaryCondition::bc_type ==\n          ::evolution::BoundaryConditions::Type::GhostAndTimeDerivative;\n\n  // List that holds the inverse spatial metric if it's needed\n  using inverse_spatial_metric_list =\n      ::evolution::dg::Actions::detail::inverse_spatial_metric_tag<System>;\n  constexpr bool has_inv_spatial_metric =\n      ::evolution::dg::Actions::detail::has_inverse_spatial_metric_tag_v<\n          System>;\n\n  // Set up tags for boundary conditions\n  using bcondition_interior_temp_tags =\n      typename BoundaryCondition::dg_interior_temporary_tags;\n  using bcondition_interior_prim_tags =\n      typename ::evolution::dg::Actions::detail::get_primitive_vars<\n          System::has_primitive_and_conservative_vars>::\n          template boundary_condition_interior_tags<BoundaryCondition>;\n  using bcondition_interior_evolved_vars_tags =\n      typename BoundaryCondition::dg_interior_evolved_variables_tags;\n  using bcondition_interior_dt_evolved_vars_tags =\n      ::evolution::dg::Actions::detail::get_dt_vars_from_boundary_condition<\n          BoundaryCondition>;\n  using bcondition_interior_deriv_evolved_vars_tags =\n      ::evolution::dg::Actions::detail::get_deriv_vars_from_boundary_condition<\n          BoundaryCondition>;\n  using bcondition_interior_tags = tmpl::remove_duplicates<tmpl::append<\n      tmpl::conditional_t<has_inv_spatial_metric,\n                          tmpl::list<::evolution::dg::Actions::detail::\n                                         NormalVector<FaceDim + 1>>,\n                          tmpl::list<>>,\n      bcondition_interior_evolved_vars_tags, bcondition_interior_prim_tags,\n      bcondition_interior_temp_tags, bcondition_interior_dt_evolved_vars_tags,\n      bcondition_interior_deriv_evolved_vars_tags>>;\n\n  std::uniform_real_distribution<> dist(-1., 1.);\n\n  // Fill all fields with random values in [-1,1), then, for each tag with a\n  // specified range, overwrite with new random values in [min,max)\n  Variables<tmpl::remove_duplicates<\n      tmpl::append<bcondition_interior_tags, inverse_spatial_metric_list>>>\n      interior_face_fields{number_of_points_on_face};\n  fill_with_random_values(make_not_null(&interior_face_fields), generator,\n                          make_not_null(&dist));\n  tmpl::for_each<tmpl::list<RangeTags...>>([&generator, &interior_face_fields,\n                                            &ranges](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const std::array<double, 2>& range = tuples::get<Tags::Range<tag>>(ranges);\n    std::uniform_real_distribution<> local_dist(range[0], range[1]);\n    fill_with_random_values(make_not_null(&get<tag>(interior_face_fields)),\n                            generator, make_not_null(&local_dist));\n  });\n\n  auto interior_normal_covector =  // Unnormalized at this point\n      make_with_random_values<\n          tnsr::i<DataVector, FaceDim + 1, Frame::Inertial>>(\n          generator, make_not_null(&dist), number_of_points_on_face);\n  if constexpr (has_inv_spatial_metric) {\n    auto& inv_spatial_metric =\n        get<tmpl::front<inverse_spatial_metric_list>>(interior_face_fields);\n    detail::adjust_spatial_metric_or_inverse(\n        make_not_null(&inv_spatial_metric));\n    auto& normal_vector =\n        get<::evolution::dg::Actions::detail::NormalVector<FaceDim + 1>>(\n            interior_face_fields);\n    detail::normalize_vector_and_covector(\n        make_not_null(&interior_normal_covector), make_not_null(&normal_vector),\n        inv_spatial_metric);\n  } else {\n    const Scalar<DataVector> magnitude = ::magnitude(interior_normal_covector);\n    for (DataVector& component : interior_normal_covector) {\n      component /= get(magnitude);\n    }\n  }\n\n  // Set a random mesh velocity. We allow for velocities above 1 to test \"faster\n  // than light\" mesh movement.\n  std::optional<tnsr::I<DataVector, FaceDim + 1>> face_mesh_velocity{};\n  if (use_moving_mesh) {\n    std::uniform_real_distribution<> local_dist(-2., 2.);\n    face_mesh_velocity =\n        make_with_random_values<tnsr::I<DataVector, FaceDim + 1>>(\n            generator, make_not_null(&local_dist), number_of_points_on_face);\n  }\n\n  if constexpr (BoundaryCondition::bc_type ==\n                ::evolution::BoundaryConditions::Type::\n                    DemandOutgoingCharSpeeds) {\n    // DemandOutgoingCharSpeeds boundary conditions only check that all\n    // characteristic speeds are directed out of the element. If there are any\n    // inward directed fields then the boundary condition should error.\n    const auto apply_bc =\n        [&boundary_condition, &face_mesh_velocity, &interior_normal_covector,\n         &python_boundary_condition_functions,\n         &python_module](const auto&... face_and_volume_args) {\n          const std::optional<std::string> error_msg =\n              boundary_condition.dg_demand_outgoing_char_speeds(\n                  face_mesh_velocity, interior_normal_covector,\n                  face_and_volume_args...);\n          const std::string& python_error_msg_function =\n              get_python_error_message_function<BoundaryCorrection>(\n                  python_boundary_condition_functions);\n          const auto python_error_message =\n              call_for_error_message<ConversionClassList>(\n                  python_module, python_error_msg_function, face_mesh_velocity,\n                  interior_normal_covector, face_and_volume_args...);\n          CAPTURE(python_error_msg_function);\n          CAPTURE(python_error_message.value_or(\"\"));\n          CAPTURE(error_msg.value_or(\"\"));\n          REQUIRE(python_error_message.has_value() == error_msg.has_value());\n          if (python_error_message.has_value() and error_msg.has_value()) {\n            std::smatch matcher{};\n            CHECK(std::regex_search(*error_msg, matcher,\n                                    std::regex{*python_error_message}));\n          }\n        };\n    apply_boundary_condition_impl(\n        apply_bc, interior_face_fields, bcondition_interior_tags{},\n        db::get<BoundaryConditionVolumeTags>(box_of_volume_data)...);\n  }\n\n  if constexpr (uses_time_derivative_condition) {\n    Variables<dt_variables_tags> time_derivative_correction{\n        number_of_points_on_face};\n    auto apply_bc = [&boundary_condition, &box_of_volume_data, epsilon,\n                     &face_mesh_velocity, &interior_normal_covector,\n                     &python_boundary_condition_functions, &python_module,\n                     &time_derivative_correction](\n                        const auto&... interior_face_and_volume_args) {\n      (void)box_of_volume_data;\n      const std::optional<std::string> error_msg =\n          boundary_condition.dg_time_derivative(\n              make_not_null(&get<::Tags::dt<EvolvedVariablesTags>>(\n                  time_derivative_correction))...,\n              face_mesh_velocity, interior_normal_covector,\n              interior_face_and_volume_args...);\n\n      const std::string& python_error_msg_function =\n          get_python_error_message_function<BoundaryCorrection>(\n              python_boundary_condition_functions);\n      const auto python_error_message =\n          call_for_error_message<ConversionClassList>(\n              python_module, python_error_msg_function, face_mesh_velocity,\n              interior_normal_covector, interior_face_and_volume_args...,\n              db::get<ExtraTagsForPythonFromDataBox>(box_of_volume_data)...);\n      CAPTURE(python_error_msg_function);\n      CAPTURE(python_error_message.value_or(\"\"));\n      CAPTURE(error_msg.value_or(\"\"));\n      REQUIRE(python_error_message.has_value() == error_msg.has_value());\n      if (python_error_message.has_value() and error_msg.has_value()) {\n        std::smatch matcher{};\n        CHECK(std::regex_search(*error_msg, matcher,\n                                std::regex{*python_error_message}));\n      }\n\n      // Check that the values were set correctly\n      tmpl::for_each<dt_variables_tags>([&](auto dt_var_tag_v) {\n        using DtVarTag = tmpl::type_from<decltype(dt_var_tag_v)>;\n        // Use NoSuchType for the BoundaryCorrection since dt-type corrections\n        // should be boundary correction agnostic.\n        const std::string& python_tag_function =\n            get_python_tag_function<DtVarTag, NoSuchType>(\n                python_boundary_condition_functions);\n        CAPTURE(python_tag_function);\n        CAPTURE(pretty_type::short_name<DtVarTag>());\n        typename DtVarTag::type python_result{};\n        try {\n          python_result =\n              pypp::call<typename DtVarTag::type, ConversionClassList>(\n                  python_module, python_tag_function, face_mesh_velocity,\n                  interior_normal_covector, interior_face_and_volume_args...,\n                  db::get<ExtraTagsForPythonFromDataBox>(\n                      box_of_volume_data)...);\n        } catch (const std::exception& e) {\n          INFO(\"Failed python call with '\" << e.what() << \"'\");\n          // Use REQUIRE(false) to print all the CAPTURE variables\n          REQUIRE(false);\n        }\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            get<DtVarTag>(time_derivative_correction), python_result,\n            Approx::custom().epsilon(epsilon).scale(1.0));\n      });\n    };\n    apply_boundary_condition_impl(\n        apply_bc, interior_face_fields, bcondition_interior_tags{},\n        db::get<BoundaryConditionVolumeTags>(box_of_volume_data)...);\n  }\n\n  if constexpr (uses_ghost) {\n    using fluxes_tags =\n        db::wrap_tags_in<::Tags::Flux, flux_variables,\n                         tmpl::size_t<FaceDim + 1>, Frame::Inertial>;\n    using correction_temp_tags =\n        typename BoundaryCorrection::dg_package_data_temporary_tags;\n    using correction_prim_tags =\n        typename ::evolution::dg::Actions::detail::get_primitive_vars<\n            System::has_primitive_and_conservative_vars>::\n            template f<BoundaryCorrection>;\n    using tags_on_exterior_face = tmpl::remove_duplicates<tmpl::append<\n        variables_tags, fluxes_tags, correction_temp_tags, correction_prim_tags,\n        inverse_spatial_metric_list,\n        tmpl::list<\n            ::evolution::dg::Actions::detail::OneOverNormalVectorMagnitude,\n            ::evolution::dg::Actions::detail::NormalVector<FaceDim + 1>,\n            ::evolution::dg::Tags::NormalCovector<FaceDim + 1>>>>;\n    Variables<tags_on_exterior_face> exterior_face_fields{\n        number_of_points_on_face};\n    auto apply_bc = [&box_of_volume_data, epsilon, &exterior_face_fields,\n                     &face_mesh_velocity, &interior_normal_covector,\n                     &python_boundary_condition_functions,\n                     &python_module]<typename... Ts>(\n                        const std::optional<std::string>& error_msg,\n                        const Ts&... interior_face_and_volume_args) {\n      (void)box_of_volume_data;\n      const std::string& python_error_msg_function =\n          get_python_error_message_function<BoundaryCorrection>(\n              python_boundary_condition_functions);\n      const auto python_error_message =\n          call_for_error_message<ConversionClassList>(\n              python_module, python_error_msg_function, face_mesh_velocity,\n              interior_normal_covector, interior_face_and_volume_args...,\n              db::get<ExtraTagsForPythonFromDataBox>(box_of_volume_data)...);\n      CAPTURE(python_error_msg_function);\n      CAPTURE(python_error_message.value_or(\"\"));\n      CAPTURE(error_msg.value_or(\"\"));\n      REQUIRE(python_error_message.has_value() == error_msg.has_value());\n      if (python_error_message.has_value() and error_msg.has_value()) {\n        std::smatch matcher{};\n        CHECK(std::regex_search(*error_msg, matcher,\n                                std::regex{*python_error_message}));\n      }\n      if (python_error_message.has_value()) {\n        return;\n      }\n\n      // Check that the values were set correctly\n      tmpl::for_each<tmpl::list<BoundaryCorrectionPackagedDataInputTags...>>(\n          [&](auto boundary_correction_tag_v) {\n            using BoundaryCorrectionTag =\n                tmpl::type_from<decltype(boundary_correction_tag_v)>;\n            const std::string& python_tag_function =\n                get_python_tag_function<BoundaryCorrectionTag,\n                                        BoundaryCorrection>(\n                    python_boundary_condition_functions);\n            CAPTURE(python_tag_function);\n            CAPTURE(pretty_type::short_name<BoundaryCorrectionTag>());\n            typename BoundaryCorrectionTag::type python_result{};\n            try {\n              python_result = pypp::call<typename BoundaryCorrectionTag::type,\n                                         ConversionClassList>(\n                  python_module, python_tag_function, face_mesh_velocity,\n                  interior_normal_covector, interior_face_and_volume_args...,\n                  db::get<ExtraTagsForPythonFromDataBox>(\n                      box_of_volume_data)...);\n            } catch (const std::exception& e) {\n              INFO(\"Failed python call with '\" << e.what() << \"'\");\n              // Use REQUIRE(false) to print all the CAPTURE variables\n              REQUIRE(false);\n            }\n            CHECK_ITERABLE_CUSTOM_APPROX(\n                get<BoundaryCorrectionTag>(exterior_face_fields), python_result,\n                Approx::custom().epsilon(epsilon).scale(1.0));\n          });\n    };\n    // Since any of the variables in `exterior_face_fields` could be mutated\n    // from their projected state, we don't pass in the interior tags explicitly\n    // since that will likely give a false sense of \"no aliasing\"\n    apply_boundary_condition_dg_ghost_impl(\n        apply_bc, make_not_null(&exterior_face_fields), boundary_condition,\n        interior_face_fields, face_mesh_velocity, interior_normal_covector,\n        tmpl::append<tmpl::list<BoundaryCorrectionPackagedDataInputTags...>,\n                     inverse_spatial_metric_list>{},\n        bcondition_interior_tags{},\n        db::get<BoundaryConditionVolumeTags>(box_of_volume_data)...);\n  }\n}\n\ntemplate <typename T, typename = std::void_t<>>\nstruct get_boundary_conditions_impl {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename T>\nstruct get_boundary_conditions_impl<\n    T, std::void_t<typename T::boundary_conditions_base>> {\n  using type = typename T::boundary_conditions_base::creatable_classes;\n};\n\ntemplate <typename T>\nusing get_boundary_conditions = typename get_boundary_conditions_impl<T>::type;\n}  // namespace detail\n\n/*!\n * \\brief Test a boundary condition against python code and that it satisfies\n * the required interface.\n *\n * The boundary conditions return a `std::optional<std::string>` that is the\n * error message. For ghost cell boundary conditions they must also return the\n * arguments needed by the boundary correction's `dg_package_data` function by\n * `gsl::not_null`. Time derivative boundary conditions return the correction\n * added to the time derivatives by `gsl::not_null`, while\n * DemandOutgoingCharSpeeds boundary conditions should only check that all the\n * characteristics are outgoing. Therefore, the comparison implementation in\n * python must have a function for each of these. Which function is called is\n * specified using a `python_boundary_condition_functions` and\n * `Tags::PythonFunctionName`.\n *\n * The specific boundary condition and system need to be explicitly given.\n * Once the boundary condition and boundary correction base classes are\n * listed/available in the `System` class we can remove needing to specify\n * those. The `ConversionClassList` is forwarded to `pypp` to allow custom\n * conversions of classes to python, such as analytic solutions or equations of\n * state.\n *\n * - The random number generator is passed in so that the seed can be easily\n *   controlled externally. Use, e.g. `MAKE_GENERATOR(gen)` to create a\n *   generator.\n * - The python function names are given in a `TaggedTuple`. A\n *   `TestHelpers::evolution::dg::Tags::PythonFunctionForErrorMessage` tag must\n *   be given for specifying the error message that the boundary condition\n *   returns. In many cases this function will simply return `None`. The tag can\n *   optionally specify the boundary correction for which it is to be used, so\n *   that a different error message could be printed if a different boundary\n *   correction is used.\n *   The `TestHelpers::evolution::dg::Tags::PythonFunctionName` tag is used to\n *   give the name of the python function for each return argument. The tags are\n *   the input to the `package_data` function for Ghost boundary conditions, and\n *   `::Tags::dt<evolved_var_tag>` for time derivative boundary conditions.\n * - `factory_string` is a string used to create the boundary condition from the\n *   factory\n * - `face_points` is the grid points on the interface. Generally 5 grid points\n *   per dimension in 2d and 3d is recommended to catch indexing errors. In 1d\n *   there is only ever one point on the interface\n * - `box_of_volume_data` is a `db::DataBox` that contains all of the\n *   `dg_gridless_tags` of the boundary condition. This is not a `TaggedTuple`\n *   so that all the different types of tags, like base tags, can be supported\n *   and easily tested.\n * - `ranges` is a `TaggedTuple` of\n *   `TestHelpers::evolution::dg::Tags::Range<tag>` specifying a custom range in\n *   which to generate the random values. This can be used for ensuring that\n *   positive quantities are randomly generated on the interval\n *   `[lower_bound,upper_bound)`, choosing `lower_bound` to be `0` or some small\n *   number. The default interval if a tag is not listed is `[-1,1)`.\n * - `epsilon` is the relative tolerance to use in the random tests\n */\ntemplate <typename BoundaryCondition, typename BoundaryConditionBase,\n          typename System, typename BoundaryCorrectionsList,\n          typename ConversionClassList = tmpl::list<>,\n          typename ExtraTagsForPythonFromDataBox = tmpl::list<>,\n          typename Metavariables = NoSuchType, size_t FaceDim,\n          typename DbTagsList, typename... RangeTags,\n          typename... PythonFunctionNameTags>\nvoid test_boundary_condition_with_python(\n    const gsl::not_null<std::mt19937*> generator,\n    const std::string& python_module,\n    const tuples::TaggedTuple<PythonFunctionNameTags...>&\n        python_boundary_condition_functions,\n    const std::string& factory_string, const Index<FaceDim>& face_points,\n    const db::DataBox<DbTagsList>& box_of_volume_data,\n    const tuples::TaggedTuple<Tags::Range<RangeTags>...>& ranges,\n    const double epsilon = 1.0e-12) {\n  PUPable_reg(BoundaryCondition);\n  static_assert(std::is_final_v<std::decay_t<BoundaryCondition>>,\n                \"All boundary condition classes must be marked `final`.\");\n  static_assert(tt::is_a_v<tmpl::list, ExtraTagsForPythonFromDataBox>);\n  using variables_tags = typename System::variables_tag::tags_list;\n  using flux_variables = typename System::flux_variables;\n  using fluxes_tags =\n      db::wrap_tags_in<::Tags::Flux, flux_variables, tmpl::size_t<FaceDim + 1>,\n                       Frame::Inertial>;\n  const std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>\n      boundary_condition = [&factory_string]() {\n        if constexpr (std::is_same_v<Metavariables, NoSuchType>) {\n          return TestHelpers::test_factory_creation<BoundaryConditionBase,\n                                                    BoundaryCondition>(\n              factory_string);\n        } else {\n          return TestHelpers::test_creation<\n              std::unique_ptr<BoundaryConditionBase>, Metavariables>(\n              factory_string);\n        }\n      }();\n\n  REQUIRE_FALSE(\n      domain::BoundaryConditions::is_periodic(boundary_condition->get_clone()));\n\n  tmpl::for_each<BoundaryCorrectionsList>(\n      [&boundary_condition, &box_of_volume_data, epsilon, &face_points,\n       &generator, &python_boundary_condition_functions, &python_module,\n       &ranges](auto boundary_correction_v) {\n        using BoundaryCorrection =\n            tmpl::type_from<decltype(boundary_correction_v)>;\n        using package_data_input_tags = tmpl::append<\n            variables_tags, fluxes_tags,\n            typename BoundaryCorrection::dg_package_data_temporary_tags,\n            typename ::evolution::dg::Actions::detail::get_primitive_vars<\n                System::has_primitive_and_conservative_vars>::\n                template f<BoundaryCorrection>>;\n        using boundary_condition_dg_gridless_tags =\n            typename BoundaryCondition::dg_gridless_tags;\n        for (const auto use_moving_mesh : {false, true}) {\n          detail::test_boundary_condition_with_python_impl<\n              System, ConversionClassList, BoundaryCorrection>(\n              generator, python_module, python_boundary_condition_functions,\n              dynamic_cast<const BoundaryCondition&>(*boundary_condition),\n              face_points, box_of_volume_data, ranges, use_moving_mesh,\n              variables_tags{}, package_data_input_tags{},\n              boundary_condition_dg_gridless_tags{},\n              ExtraTagsForPythonFromDataBox{}, epsilon);\n          // Now serialize and deserialize and test again\n          INFO(\"Test boundary condition after serialization.\");\n          const auto deserialized_bc =\n              serialize_and_deserialize(boundary_condition);\n          detail::test_boundary_condition_with_python_impl<\n              System, ConversionClassList, BoundaryCorrection>(\n              generator, python_module, python_boundary_condition_functions,\n              dynamic_cast<const BoundaryCondition&>(*deserialized_bc),\n              face_points, box_of_volume_data, ranges, use_moving_mesh,\n              variables_tags{}, package_data_input_tags{},\n              boundary_condition_dg_gridless_tags{},\n              ExtraTagsForPythonFromDataBox{}, epsilon);\n        }\n      });\n}\n\n/// Test that a boundary condition is correctly identified as periodic.\ntemplate <typename BoundaryCondition, typename BoundaryConditionBase>\nvoid test_periodic_condition(const std::string& factory_string) {\n  PUPable_reg(BoundaryCondition);\n  const auto boundary_condition =\n      TestHelpers::test_factory_creation<BoundaryConditionBase,\n                                         BoundaryCondition>(factory_string);\n  REQUIRE(typeid(*boundary_condition.get()) == typeid(BoundaryCondition));\n  const auto bc_clone = boundary_condition->get_clone();\n  CHECK(domain::BoundaryConditions::is_periodic(bc_clone));\n  const auto bc_deserialized = serialize_and_deserialize(bc_clone);\n  CHECK(domain::BoundaryConditions::is_periodic(bc_deserialized));\n}\n}  // namespace TestHelpers::evolution::dg\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/BoundaryCorrections.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <random>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/NormalVectors.hpp\"\n#include \"Helpers/Evolution/DiscontinuousGalerkin/Range.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers/RetrieveUniquePtr.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n\nnamespace TestHelpers::evolution::dg {\n/// Indicate if the boundary correction should be zero when the solution is\n/// smooth (should pretty much always be `Yes`)\nenum class ZeroOnSmoothSolution { Yes, No };\n\nnamespace detail {\ntemplate <bool HasPrimitiveVars = false>\nstruct get_correction_primitive_vars_impl {\n  template <typename BoundaryCorrection>\n  using f = tmpl::list<>;\n};\n\ntemplate <>\nstruct get_correction_primitive_vars_impl<true> {\n  template <typename BoundaryCorrection>\n  using f = typename BoundaryCorrection::dg_package_data_primitive_tags;\n};\n\ntemplate <bool HasPrimitiveVars, typename BoundaryCorrection>\nusing get_correction_primitive_vars =\n    typename get_correction_primitive_vars_impl<HasPrimitiveVars>::template f<\n        BoundaryCorrection>;\n\ntemplate <bool HasPrimitiveVars = false>\nstruct get_system_primitive_vars_impl {\n  template <typename System>\n  using f = tmpl::list<>;\n};\n\ntemplate <>\nstruct get_system_primitive_vars_impl<true> {\n  template <typename System>\n  using f = typename System::primitive_variables_tag::tags_list;\n};\n\ntemplate <bool HasPrimitiveVars, typename System>\nusing get_system_primitive_vars = typename get_system_primitive_vars_impl<\n    HasPrimitiveVars>::template f<System>;\n\ntemplate <typename BoundaryCorrection, typename... PackageTags,\n          typename... FaceTags, typename... VolumeTags,\n          typename... FaceTagsToForward, typename... VolumeTagsToForward,\n          size_t Dim>\ndouble call_dg_package_data(\n    const gsl::not_null<Variables<tmpl::list<PackageTags...>>*> package_data,\n    const BoundaryCorrection& correction,\n    const Variables<tmpl::list<FaceTags...>>& face_variables,\n    const tuples::TaggedTuple<VolumeTags...>& volume_data,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_covector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    tmpl::list<FaceTagsToForward...> /*meta*/,\n    tmpl::list<VolumeTagsToForward...> /*meta*/) {\n  std::optional<Scalar<DataVector>> normal_dot_mesh_velocity{};\n  if (mesh_velocity.has_value()) {\n    normal_dot_mesh_velocity =\n        dot_product(*mesh_velocity, unit_normal_covector);\n  }\n  const double max_speed = correction.dg_package_data(\n      make_not_null(&get<PackageTags>(*package_data))...,\n      get<FaceTagsToForward>(face_variables)..., unit_normal_covector,\n      mesh_velocity, normal_dot_mesh_velocity,\n      StdHelpers::retrieve(get<VolumeTagsToForward>(volume_data))...);\n  return max_speed;\n}\n\ntemplate <typename BoundaryCorrection, typename... PackageTags,\n          typename... FaceTags, typename... VolumeTags,\n          typename... FaceTagsToForward, typename... VolumeTagsToForward,\n          size_t Dim>\ndouble call_dg_package_data(\n    const gsl::not_null<Variables<tmpl::list<PackageTags...>>*> package_data,\n    const BoundaryCorrection& correction,\n    const Variables<tmpl::list<FaceTags...>>& face_variables,\n    const tuples::TaggedTuple<VolumeTags...>& volume_data,\n    const tnsr::i<DataVector, Dim, Frame::Inertial>& unit_normal_covector,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& unit_normal_vector,\n    const std::optional<tnsr::I<DataVector, Dim, Frame::Inertial>>&\n        mesh_velocity,\n    tmpl::list<FaceTagsToForward...> /*meta*/,\n    tmpl::list<VolumeTagsToForward...> /*meta*/) {\n  std::optional<Scalar<DataVector>> normal_dot_mesh_velocity{};\n  if (mesh_velocity.has_value()) {\n    normal_dot_mesh_velocity =\n        dot_product(*mesh_velocity, unit_normal_covector);\n  }\n  const double max_speed = correction.dg_package_data(\n      make_not_null(&get<PackageTags>(*package_data))...,\n      get<FaceTagsToForward>(face_variables)..., unit_normal_covector,\n      unit_normal_vector, mesh_velocity, normal_dot_mesh_velocity,\n      StdHelpers::retrieve(tuples::get<VolumeTagsToForward>(volume_data))...);\n  return max_speed;\n}\n\ntemplate <typename BoundaryCorrection, typename... BoundaryCorrectionTags,\n          typename... PackageTags, typename... VolumeTags,\n          typename... VolumeTagsToForward>\nvoid call_dg_boundary_terms(\n    const gsl::not_null<Variables<tmpl::list<BoundaryCorrectionTags...>>*>\n        boundary_corrections,\n    const BoundaryCorrection& correction,\n    const tuples::TaggedTuple<VolumeTags...>& volume_data,\n    const Variables<tmpl::list<PackageTags...>>& interior_package_data,\n    const Variables<tmpl::list<PackageTags...>>& exterior_package_data,\n    const ::dg::Formulation dg_formulation,\n    tmpl::list<VolumeTagsToForward...> /*meta*/) {\n  correction.dg_boundary_terms(\n      make_not_null(&get<BoundaryCorrectionTags>(*boundary_corrections))...,\n      get<PackageTags>(interior_package_data)...,\n      get<PackageTags>(exterior_package_data)..., dg_formulation,\n      StdHelpers::retrieve(get<VolumeTagsToForward>(volume_data))...);\n}\n\ntemplate <typename System, typename BoundaryCorrection, size_t FaceDim,\n          typename... VolumeTags, typename... RangeTags>\nvoid test_boundary_correction_conservation_impl(\n    const gsl::not_null<std::mt19937*> generator,\n    const BoundaryCorrection& correction_in, const Mesh<FaceDim>& face_mesh,\n    const tuples::TaggedTuple<VolumeTags...>& volume_data,\n    const tuples::TaggedTuple<Tags::Range<RangeTags>...>& ranges,\n    const bool use_moving_mesh, const ::dg::Formulation dg_formulation,\n    const ZeroOnSmoothSolution zero_on_smooth_solution, const double eps) {\n  CAPTURE(use_moving_mesh);\n  CAPTURE(dg_formulation);\n  CAPTURE(FaceDim);\n  constexpr bool curved_background =\n      detail::has_inverse_spatial_metric_tag_v<System>;\n  CAPTURE(curved_background);\n  Approx custom_approx = Approx::custom().epsilon(eps).scale(1.0);\n\n  using variables_tags = typename System::variables_tag::tags_list;\n  using primitive_variables_tags = detail::get_system_primitive_vars<\n      System::has_primitive_and_conservative_vars, System>;\n  using flux_variables = typename System::flux_variables;\n  using flux_tags =\n      db::wrap_tags_in<::Tags::Flux, flux_variables, tmpl::size_t<FaceDim + 1>,\n                       Frame::Inertial>;\n  using temporary_tags =\n      typename System::compute_volume_time_derivative_terms::temporary_tags;\n  using dt_variables_tags = db::wrap_tags_in<::Tags::dt, variables_tags>;\n\n  using dg_package_field_tags =\n      typename BoundaryCorrection::dg_package_field_tags;\n  using dg_package_volume_tags =\n      typename BoundaryCorrection::dg_package_data_volume_tags;\n  using dg_boundary_terms_volume_tags =\n      typename BoundaryCorrection::dg_boundary_terms_volume_tags;\n  using package_temporary_tags =\n      typename BoundaryCorrection::dg_package_data_temporary_tags;\n  using package_primitive_tags = detail::get_correction_primitive_vars<\n      System::has_primitive_and_conservative_vars, BoundaryCorrection>;\n\n  const auto correction_base_ptr =\n      serialize_and_deserialize(correction_in.get_clone());\n  const auto& correction =\n      dynamic_cast<const BoundaryCorrection&>(*correction_base_ptr);\n\n  // Check that the temporary tags needed on the boundary\n  // (package_temporary_tags) are listed as temporary tags for the volume time\n  // derivative computation (temporary_tags).\n  static_assert(\n      std::is_same_v<\n          tmpl::list_difference<package_temporary_tags, temporary_tags>,\n          tmpl::list<>>,\n      \"There are temporary tags needed by the boundary correction that are not \"\n      \"computed as temporary tags by the system\");\n\n  // Check that the primitive tags needed on the boundary\n  // (package_primitive_tags) are listed as the primitive tags in\n  // the system (primitive_variables_tags).\n  static_assert(\n      std::is_same_v<tmpl::list_difference<package_primitive_tags,\n                                           primitive_variables_tags>,\n                     tmpl::list<>>,\n      \"There are primitive tags needed by the boundary correction that are not \"\n      \"listed in the system as being primitive variables\");\n\n  using face_tags =\n      tmpl::append<variables_tags, flux_tags, package_temporary_tags,\n                   package_primitive_tags>;\n  using face_tags_with_curved_background = tmpl::conditional_t<\n      curved_background,\n      tmpl::remove_duplicates<tmpl::push_back<\n          face_tags, typename detail::inverse_spatial_metric_tag<\n                         curved_background>::template f<System>>>,\n      face_tags>;\n\n  // Sanity check: make sure each range tag is a `dg_package_data` argument\n  using invalid_range_tags =\n      tmpl::list_difference<tmpl::list<RangeTags...>,\n                            face_tags_with_curved_background>;\n  static_assert(std::is_same_v<tmpl::list<>, invalid_range_tags>,\n                \"Received Tags::Range for Tags that aren't arguments to \"\n                \"dg_package_data\");\n\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  DataVector used_for_size{face_mesh.number_of_grid_points()};\n\n  // Fill all fields with random values in [-1,1), then, for each tag with a\n  // specified range, overwrite with new random values in [min,max)\n  Variables<dg_package_field_tags> interior_package_data{used_for_size.size()};\n  auto interior_fields_on_face =\n      make_with_random_values<Variables<face_tags_with_curved_background>>(\n          generator, make_not_null(&dist), used_for_size);\n  tmpl::for_each<tmpl::list<RangeTags...>>([&generator,\n                                            &interior_fields_on_face,\n                                            &ranges](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const std::array<double, 2>& range = tuples::get<Tags::Range<tag>>(ranges);\n    std::uniform_real_distribution<> local_dist(range[0], range[1]);\n    fill_with_random_values(make_not_null(&get<tag>(interior_fields_on_face)),\n                            generator, make_not_null(&local_dist));\n  });\n\n  // Same as above but now for external data\n  Variables<dg_package_field_tags> exterior_package_data{used_for_size.size()};\n  auto exterior_fields_on_face =\n      make_with_random_values<Variables<face_tags_with_curved_background>>(\n          generator, make_not_null(&dist), used_for_size);\n  tmpl::for_each<tmpl::list<RangeTags...>>([&generator,\n                                            &exterior_fields_on_face,\n                                            &ranges](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const std::array<double, 2>& range = tuples::get<Tags::Range<tag>>(ranges);\n    std::uniform_real_distribution<> local_dist(range[0], range[1]);\n    fill_with_random_values(make_not_null(&get<tag>(exterior_fields_on_face)),\n                            generator, make_not_null(&local_dist));\n  });\n\n  // Compute the interior and exterior normal vectors so they are pointing in\n  // opposite directions.\n  auto interior_unit_normal_covector = make_with_random_values<\n      tnsr::i<DataVector, FaceDim + 1, Frame::Inertial>>(\n      generator, make_not_null(&dist), used_for_size);\n  tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>\n      interior_unit_normal_vector{};\n\n  tnsr::i<DataVector, FaceDim + 1, Frame::Inertial>\n      exterior_unit_normal_covector;\n  tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>\n      exterior_unit_normal_vector{};\n  if constexpr (not curved_background) {\n    const Scalar<DataVector> interior_normal_magnitude =\n        magnitude(interior_unit_normal_covector);\n    for (auto& t : interior_unit_normal_covector) {\n      t /= get(interior_normal_magnitude);\n    }\n    exterior_unit_normal_covector = interior_unit_normal_covector;\n    for (auto& t : exterior_unit_normal_covector) {\n      t *= -1.0;\n    }\n  } else {\n    using inv_spatial_metric = typename detail::inverse_spatial_metric_tag<\n        curved_background>::template f<System>;\n    exterior_unit_normal_covector = interior_unit_normal_covector;\n    for (auto& t : exterior_unit_normal_covector) {\n      t *= -1.0;\n    }\n    detail::adjust_spatial_metric_or_inverse(\n        make_not_null(&get<inv_spatial_metric>(interior_fields_on_face)));\n\n    // make the exterior inverse spatial metric be close to the interior one\n    // since the solution should be smooth. We can't change too much because we\n    // want the spatial metric to have diagonal terms much larger than the\n    // off-diagonal terms.\n    std::uniform_real_distribution<> inv_metric_change_dist(0.999, 1.0);\n    for (size_t i = 0; i < FaceDim + 1; ++i) {\n      for (size_t j = i; j < FaceDim + 1; ++j) {\n        get<inv_spatial_metric>(exterior_fields_on_face).get(i, j) =\n            inv_metric_change_dist(*generator) *\n            get<inv_spatial_metric>(interior_fields_on_face).get(i, j);\n      }\n    }\n    detail::normalize_vector_and_covector(\n        make_not_null(&interior_unit_normal_covector),\n        make_not_null(&interior_unit_normal_vector),\n        get<inv_spatial_metric>(interior_fields_on_face));\n    detail::normalize_vector_and_covector(\n        make_not_null(&exterior_unit_normal_covector),\n        make_not_null(&exterior_unit_normal_vector),\n        get<inv_spatial_metric>(exterior_fields_on_face));\n\n    // if dg_package_data depends on the (not inverted) spatial metric, we also\n    // adjust the spatial metric. Note that for testing purposes, the metric and\n    // inverse metric don't need to be inverses of each other. However, we do\n    // want both to be physically reasonable, i.e., close to flat space.\n    using spatial_metric = gr::Tags::SpatialMetric<DataVector, FaceDim + 1>;\n    if constexpr (tmpl::list_contains_v<face_tags_with_curved_background,\n                                        spatial_metric>) {\n      detail::adjust_spatial_metric_or_inverse(\n          make_not_null(&get<spatial_metric>(interior_fields_on_face)));\n      // as for external inverse metric: we get the external metric by slightly\n      // tweaking the internal metric\n      for (size_t i = 0; i < FaceDim + 1; ++i) {\n        for (size_t j = i; j < FaceDim + 1; ++j) {\n          get<spatial_metric>(exterior_fields_on_face).get(i, j) =\n              inv_metric_change_dist(*generator) *\n              get<spatial_metric>(interior_fields_on_face).get(i, j);\n        }\n      }\n    }\n  }\n\n  std::optional<tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>>\n      mesh_velocity{};\n  if (use_moving_mesh) {\n    mesh_velocity = make_with_random_values<\n        tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>>(\n        generator, make_not_null(&dist), used_for_size);\n  }\n\n  if constexpr (curved_background) {\n    call_dg_package_data(make_not_null(&interior_package_data), correction,\n                         interior_fields_on_face, volume_data,\n                         interior_unit_normal_covector,\n                         interior_unit_normal_vector, mesh_velocity,\n                         face_tags{}, dg_package_volume_tags{});\n    call_dg_package_data(make_not_null(&exterior_package_data), correction,\n                         exterior_fields_on_face, volume_data,\n                         exterior_unit_normal_covector,\n                         exterior_unit_normal_vector, mesh_velocity,\n                         face_tags{}, dg_package_volume_tags{});\n  } else {\n    call_dg_package_data(make_not_null(&interior_package_data), correction,\n                         interior_fields_on_face, volume_data,\n                         interior_unit_normal_covector, mesh_velocity,\n                         face_tags{}, dg_package_volume_tags{});\n    call_dg_package_data(make_not_null(&exterior_package_data), correction,\n                         exterior_fields_on_face, volume_data,\n                         exterior_unit_normal_covector, mesh_velocity,\n                         face_tags{}, dg_package_volume_tags{});\n  }\n\n  Variables<dt_variables_tags> boundary_corrections{\n      face_mesh.number_of_grid_points()};\n  call_dg_boundary_terms(make_not_null(&boundary_corrections), correction,\n                         volume_data, interior_package_data,\n                         exterior_package_data, dg_formulation,\n                         dg_boundary_terms_volume_tags{});\n\n  if (dg_formulation == ::dg::Formulation::StrongInertial) {\n    // The strong form should be (WeakForm - (n_i F^i)_{interior}).\n    // Since we also test conservation for the weak form we just need to test\n    // that the strong form satisfies the above definition.\n\n    if constexpr (curved_background) {\n      call_dg_package_data(make_not_null(&interior_package_data), correction,\n                           interior_fields_on_face, volume_data,\n                           interior_unit_normal_covector,\n                           interior_unit_normal_vector, mesh_velocity,\n                           face_tags{}, dg_package_volume_tags{});\n      call_dg_package_data(make_not_null(&exterior_package_data), correction,\n                           exterior_fields_on_face, volume_data,\n                           exterior_unit_normal_covector,\n                           exterior_unit_normal_vector, mesh_velocity,\n                           face_tags{}, dg_package_volume_tags{});\n    } else {\n      call_dg_package_data(make_not_null(&interior_package_data), correction,\n                           interior_fields_on_face, volume_data,\n                           interior_unit_normal_covector, mesh_velocity,\n                           face_tags{}, dg_package_volume_tags{});\n      call_dg_package_data(make_not_null(&exterior_package_data), correction,\n                           exterior_fields_on_face, volume_data,\n                           exterior_unit_normal_covector, mesh_velocity,\n                           face_tags{}, dg_package_volume_tags{});\n    }\n\n    Variables<dt_variables_tags> expected_boundary_corrections{\n        face_mesh.number_of_grid_points()};\n    call_dg_boundary_terms(\n        make_not_null(&expected_boundary_corrections), correction, volume_data,\n        interior_package_data, exterior_package_data,\n        ::dg::Formulation::WeakInertial, dg_boundary_terms_volume_tags{});\n\n    tmpl::for_each<flux_variables>([&interior_package_data,\n                                    &expected_boundary_corrections](\n                                       auto flux_variable_tag_v) {\n      using flux_variable_tag = tmpl::type_from<decltype(flux_variable_tag_v)>;\n      using normal_dot_flux_tag = ::Tags::NormalDotFlux<flux_variable_tag>;\n      using dt_tag = ::Tags::dt<flux_variable_tag>;\n      const auto& normal_dot_flux =\n          get<normal_dot_flux_tag>(interior_package_data);\n      auto& expected_boundary_correction =\n          get<dt_tag>(expected_boundary_corrections);\n      for (size_t tensor_index = 0;\n           tensor_index < expected_boundary_correction.size(); ++tensor_index) {\n        expected_boundary_correction[tensor_index] -=\n            normal_dot_flux[tensor_index];\n      }\n    });\n    {\n      INFO(\"Check weak and strong boundary terms match.\");\n      CHECK_VARIABLES_CUSTOM_APPROX(\n          boundary_corrections, expected_boundary_corrections, custom_approx);\n    }\n\n    if (zero_on_smooth_solution == ZeroOnSmoothSolution::Yes) {\n      INFO(\n          \"Testing that if the solution is the same on both sides the \"\n          \"StrongInertial correction is identically zero.\");\n      Variables<dg_package_field_tags> interior_package_data_opposite_signs{\n          used_for_size.size()};\n      if constexpr (curved_background) {\n        // If the solution is the same on both sides then the interior and\n        // exterior normal vectors only differ by a sign.\n        for (size_t i = 0; i < FaceDim + 1; ++i) {\n          exterior_unit_normal_vector.get(i) =\n              -interior_unit_normal_vector.get(i);\n          exterior_unit_normal_covector.get(i) =\n              -interior_unit_normal_covector.get(i);\n        }\n        call_dg_package_data(\n            make_not_null(&interior_package_data_opposite_signs), correction,\n            interior_fields_on_face, volume_data, exterior_unit_normal_covector,\n            exterior_unit_normal_vector, mesh_velocity, face_tags{},\n            dg_package_volume_tags{});\n      } else {\n        call_dg_package_data(\n            make_not_null(&interior_package_data_opposite_signs), correction,\n            interior_fields_on_face, volume_data, exterior_unit_normal_covector,\n            mesh_velocity, face_tags{}, dg_package_volume_tags{});\n      }\n      Variables<dt_variables_tags> zero_boundary_correction{\n          face_mesh.number_of_grid_points()};\n      call_dg_boundary_terms(\n          make_not_null(&zero_boundary_correction), correction, volume_data,\n          interior_package_data, interior_package_data_opposite_signs,\n          ::dg::Formulation::StrongInertial, dg_boundary_terms_volume_tags{});\n      Variables<dt_variables_tags> expected_zero_boundary_correction{\n          face_mesh.number_of_grid_points(), 0.0};\n      tmpl::for_each<dt_variables_tags>([&custom_approx,\n                                         &expected_zero_boundary_correction,\n                                         &zero_boundary_correction](\n                                            auto dt_variables_tag_v) {\n        using dt_variables_tag = tmpl::type_from<decltype(dt_variables_tag_v)>;\n        const std::string tag_name = db::tag_name<dt_variables_tag>();\n        CAPTURE(tag_name);\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            get<dt_variables_tag>(zero_boundary_correction),\n            get<dt_variables_tag>(expected_zero_boundary_correction),\n            custom_approx);\n      });\n    }\n  } else if (dg_formulation == ::dg::Formulation::WeakInertial) {\n    INFO(\n        \"Checking that swapping the two sides results in an overall minus \"\n        \"sign.\");\n    Variables<dt_variables_tags> reverse_side_boundary_corrections{\n        face_mesh.number_of_grid_points()};\n    call_dg_boundary_terms(make_not_null(&reverse_side_boundary_corrections),\n                           correction, volume_data, exterior_package_data,\n                           interior_package_data, dg_formulation,\n                           dg_boundary_terms_volume_tags{});\n    // Check that the flux leaving one element equals the flux entering its\n    // neighbor, i.e., F*(interior, exterior) == -F*(exterior, interior)\n    reverse_side_boundary_corrections *= -1.0;\n    tmpl::for_each<flux_variables>([&boundary_corrections, &custom_approx,\n                                    &reverse_side_boundary_corrections](\n                                       auto flux_variable_tag_v) {\n      using flux_variable_tag = tmpl::type_from<decltype(flux_variable_tag_v)>;\n      const std::string tag_name = db::tag_name<flux_variable_tag>();\n      CAPTURE(tag_name);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          get<::Tags::dt<flux_variable_tag>>(reverse_side_boundary_corrections),\n          get<::Tags::dt<flux_variable_tag>>(boundary_corrections),\n          custom_approx);\n    });\n  } else {\n    ERROR(\"DG formulation must be StrongInertial or WeakInertial, not \"\n          << dg_formulation);\n  }\n}\n}  // namespace detail\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Checks that the boundary correction is conservative and that for\n * smooth solutions the strong-form correction is zero.\n *\n * By default, each input tensor for `dg_package_data` is randomly generated\n * from the interval `[-1,1)` (except the metric, which for systems with a\n * metric is generated to be close to flat space). The argument `ranges` is a\n * `TaggedTuple` of `TestHelpers::evolution::dg::Tags::Range<tag>` that\n * enables the caller to pick a custom interval for generating the input\n * `tag`. This is useful, for example, for tensors that need positive values.\n * Each `tag` in `ranges` must be an argument of `dg_package_data`.\n */\ntemplate <typename System, typename BoundaryCorrection, size_t FaceDim,\n          typename... VolumeTags, typename... RangeTags>\nvoid test_boundary_correction_conservation(\n    const gsl::not_null<std::mt19937*> generator,\n    const BoundaryCorrection& correction, const Mesh<FaceDim>& face_mesh,\n    const tuples::TaggedTuple<VolumeTags...>& volume_data,\n    const tuples::TaggedTuple<Tags::Range<RangeTags>...>& ranges,\n    const ZeroOnSmoothSolution zero_on_smooth_solution =\n        ZeroOnSmoothSolution::Yes,\n    const double eps = 1.0e-12) {\n  for (const auto use_moving_mesh : {true, false}) {\n    for (const auto& dg_formulation :\n         {::dg::Formulation::StrongInertial, ::dg::Formulation::WeakInertial}) {\n      detail::test_boundary_correction_conservation_impl<System>(\n          generator, correction, face_mesh, volume_data, ranges,\n          use_moving_mesh, dg_formulation, zero_on_smooth_solution, eps);\n    }\n  }\n}\n\nnamespace detail {\ntemplate <typename System, typename ConversionClassList, typename VariablesTags,\n          typename BoundaryCorrection, size_t FaceDim, typename... FaceTags,\n          typename... VolumeTags, typename... RangeTags,\n          typename... DgPackageDataTags>\nvoid test_with_python(\n    const gsl::not_null<std::mt19937*> generator,\n    const std::string& python_module,\n    const std::string& python_dg_package_data_function,\n    const std::string& python_dg_boundary_terms_function,\n    const BoundaryCorrection& correction, const Mesh<FaceDim>& face_mesh,\n    const tuples::TaggedTuple<VolumeTags...>& volume_data,\n    const tuples::TaggedTuple<Tags::Range<RangeTags>...>& ranges,\n    const bool use_moving_mesh, const ::dg::Formulation dg_formulation,\n    const double epsilon, tmpl::list<FaceTags...> /*meta*/,\n    tmpl::list<DgPackageDataTags...> /*meta*/) {\n  CAPTURE(face_mesh);\n  CAPTURE(dg_formulation);\n  CAPTURE(use_moving_mesh);\n  REQUIRE(face_mesh.number_of_grid_points() >= 1);\n  constexpr bool curved_background =\n      detail::has_inverse_spatial_metric_tag_v<System>;\n  using dg_package_field_tags =\n      typename BoundaryCorrection::dg_package_field_tags;\n  using dg_package_volume_tags =\n      typename BoundaryCorrection::dg_package_data_volume_tags;\n  using dg_boundary_terms_volume_tags =\n      typename BoundaryCorrection::dg_boundary_terms_volume_tags;\n\n  using face_tags = tmpl::list<FaceTags...>;\n  using face_tags_with_curved_background = tmpl::conditional_t<\n      curved_background,\n      tmpl::remove_duplicates<tmpl::push_back<\n          face_tags, typename TestHelpers::evolution::dg::detail::\n                         inverse_spatial_metric_tag<\n                             curved_background>::template f<System>>>,\n      face_tags>;\n\n  // Sanity check: make sure each range tag is a `dg_package_data` argument\n  using invalid_range_tags =\n      tmpl::list_difference<tmpl::list<RangeTags...>,\n                            face_tags_with_curved_background>;\n  static_assert(std::is_same_v<tmpl::list<>, invalid_range_tags>,\n                \"Received Tags::Range for Tags that aren't arguments to \"\n                \"dg_package_data\");\n\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  DataVector used_for_size{face_mesh.number_of_grid_points()};\n\n  // Fill all fields with random values in [-1,1), then, for each tag with a\n  // specified range, overwrite with new random values in [min,max)\n  auto fields_on_face =\n      make_with_random_values<Variables<face_tags_with_curved_background>>(\n          generator, make_not_null(&dist), used_for_size);\n  tmpl::for_each<tmpl::list<RangeTags...>>([&generator, &fields_on_face,\n                                            &ranges](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const std::array<double, 2>& range = tuples::get<Tags::Range<tag>>(ranges);\n    std::uniform_real_distribution<> local_dist(range[0], range[1]);\n    fill_with_random_values(make_not_null(&get<tag>(fields_on_face)), generator,\n                            make_not_null(&local_dist));\n  });\n\n  auto unit_normal_covector = make_with_random_values<\n      tnsr::i<DataVector, FaceDim + 1, Frame::Inertial>>(\n      generator, make_not_null(&dist), used_for_size);\n  tnsr::I<DataVector, FaceDim + 1, Frame::Inertial> unit_normal_vector{};\n\n  if constexpr (not curved_background) {\n    const Scalar<DataVector> normal_magnitude = magnitude(unit_normal_covector);\n    for (auto& t : unit_normal_covector) {\n      t /= get(normal_magnitude);\n    }\n  } else {\n    using inv_spatial_metric = typename detail::inverse_spatial_metric_tag<\n        curved_background>::template f<System>;\n    detail::adjust_spatial_metric_or_inverse(\n        make_not_null(&get<inv_spatial_metric>(fields_on_face)));\n    detail::normalize_vector_and_covector(\n        make_not_null(&unit_normal_covector),\n        make_not_null(&unit_normal_vector),\n        get<inv_spatial_metric>(fields_on_face));\n\n    using spatial_metric = gr::Tags::SpatialMetric<DataVector, FaceDim + 1>;\n    if constexpr (tmpl::list_contains_v<face_tags_with_curved_background,\n                                        spatial_metric>) {\n      detail::adjust_spatial_metric_or_inverse(\n          make_not_null(&get<spatial_metric>(fields_on_face)));\n    }\n  }\n\n  std::optional<tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>>\n      mesh_velocity{};\n  if (use_moving_mesh) {\n    mesh_velocity = make_with_random_values<\n        tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>>(\n        generator, make_not_null(&dist), used_for_size);\n  }\n  std::optional<Scalar<DataVector>> normal_dot_mesh_velocity{};\n  if (mesh_velocity.has_value()) {\n    normal_dot_mesh_velocity =\n        dot_product(*mesh_velocity, unit_normal_covector);\n  }\n\n  // Call C++ implementation of dg_package_data\n  Variables<dg_package_field_tags> package_data{used_for_size.size()};\n  if constexpr (curved_background) {\n    call_dg_package_data(make_not_null(&package_data), correction,\n                         fields_on_face, volume_data, unit_normal_covector,\n                         unit_normal_vector, mesh_velocity,\n                         tmpl::list<FaceTags...>{}, dg_package_volume_tags{});\n  } else {\n    call_dg_package_data(make_not_null(&package_data), correction,\n                         fields_on_face, volume_data, unit_normal_covector,\n                         mesh_velocity, tmpl::list<FaceTags...>{},\n                         dg_package_volume_tags{});\n  }\n\n  // Call python implementation of dg_package_data\n  try {\n    using ResultType =\n        tmpl::as_tuple<tmpl::transform<dg_package_field_tags,\n                                       tmpl::bind<tmpl::type_from, tmpl::_1>>>;\n    ResultType python_result{};\n    if constexpr (curved_background) {\n      python_result = pypp::call<ResultType, ConversionClassList>(\n          python_module, python_dg_package_data_function,\n          get<FaceTags>(fields_on_face)..., unit_normal_covector,\n          unit_normal_vector, mesh_velocity, normal_dot_mesh_velocity,\n          StdHelpers::retrieve(get<VolumeTags>(volume_data))...);\n    } else {\n      (void)unit_normal_vector;\n      python_result = pypp::call<ResultType, ConversionClassList>(\n          python_module, python_dg_package_data_function,\n          get<FaceTags>(fields_on_face)..., unit_normal_covector, mesh_velocity,\n          normal_dot_mesh_velocity,\n          StdHelpers::retrieve(get<VolumeTags>(volume_data))...);\n    }\n    tmpl::for_each<dg_package_field_tags>([&epsilon, &package_data,\n                                           &python_result](\n                                              auto package_data_tag_v) {\n      using package_data_tag = tmpl::type_from<decltype(package_data_tag_v)>;\n      const std::string tag_name = db::tag_name<package_data_tag>();\n      CAPTURE(tag_name);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          get<package_data_tag>(package_data),\n          (std::get<\n              tmpl::index_of<dg_package_field_tags, package_data_tag>::value>(\n              python_result)),\n          Approx::custom().epsilon(epsilon).scale(1.0));\n    });\n  } catch (const std::exception& e) {\n    INFO(\"On line \" << __LINE__ << \" Python call to \"\n                    << python_dg_package_data_function << \" in module \"\n                    << python_module << \" failed: \" << e.what());\n    REQUIRE(false);\n  }\n\n  // Now we need to check the dg_boundary_terms function.\n  //\n  // In order for the package data to have a self-consistent state (not all\n  // things being packaged are independent), we compute the packaged data.\n  // We reuse the previously computed packaged data as the interior packaged\n  // data.\n  const auto& interior_package_data = package_data;\n\n  auto exterior_fields_on_face =\n      make_with_random_values<Variables<face_tags_with_curved_background>>(\n          generator, make_not_null(&dist), used_for_size);\n  tmpl::for_each<tmpl::list<RangeTags...>>([&generator,\n                                            &exterior_fields_on_face,\n                                            &ranges](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const std::array<double, 2>& range = tuples::get<Tags::Range<tag>>(ranges);\n    std::uniform_real_distribution<> local_dist(range[0], range[1]);\n    fill_with_random_values(make_not_null(&get<tag>(exterior_fields_on_face)),\n                            generator, make_not_null(&local_dist));\n  });\n\n  tnsr::i<DataVector, FaceDim + 1, Frame::Inertial>\n      exterior_unit_normal_covector;\n  tnsr::I<DataVector, FaceDim + 1, Frame::Inertial>\n      exterior_unit_normal_vector{};\n  if constexpr (not curved_background) {\n    exterior_unit_normal_covector = unit_normal_covector;\n    for (auto& t : exterior_unit_normal_covector) {\n      t *= -1.0;\n    }\n  } else {\n    using inv_spatial_metric = typename detail::inverse_spatial_metric_tag<\n        curved_background>::template f<System>;\n    exterior_unit_normal_covector = unit_normal_covector;\n    for (auto& t : exterior_unit_normal_covector) {\n      t *= -1.0;\n    }\n\n    // make the exterior inverse spatial metric be close to the interior one\n    // since the solution should be smooth. We can't change too much because we\n    // want the spatial metric to have diagonal terms much larger than the\n    // off-diagonal terms.\n    std::uniform_real_distribution<> inv_metric_change_dist(0.999, 1.0);\n    for (size_t i = 0; i < FaceDim + 1; ++i) {\n      for (size_t j = i; j < FaceDim + 1; ++j) {\n        get<inv_spatial_metric>(exterior_fields_on_face).get(i, j) =\n            inv_metric_change_dist(*generator) *\n            get<inv_spatial_metric>(fields_on_face).get(i, j);\n      }\n    }\n    detail::normalize_vector_and_covector(\n        make_not_null(&exterior_unit_normal_covector),\n        make_not_null(&exterior_unit_normal_vector),\n        get<inv_spatial_metric>(exterior_fields_on_face));\n\n    // if dg_package_data depends on the (not inverted) spatial metric, we also\n    // adjust the spatial metric. Note that for testing purposes, the metric and\n    // inverse metric don't need to be inverses of each other. However, we do\n    // want both to be physically reasonable, i.e., close to flat space.\n    using spatial_metric = gr::Tags::SpatialMetric<DataVector, FaceDim + 1>;\n    if constexpr (tmpl::list_contains_v<face_tags_with_curved_background,\n                                        spatial_metric>) {\n      // as for external inverse metric: we get the external metric by slightly\n      // tweaking the internal metric\n      for (size_t i = 0; i < FaceDim + 1; ++i) {\n        for (size_t j = i; j < FaceDim + 1; ++j) {\n          get<spatial_metric>(exterior_fields_on_face).get(i, j) =\n              inv_metric_change_dist(*generator) *\n              get<spatial_metric>(fields_on_face).get(i, j);\n        }\n      }\n    }\n  }\n\n  Variables<dg_package_field_tags> exterior_package_data{used_for_size.size()};\n  if constexpr (curved_background) {\n    call_dg_package_data(make_not_null(&exterior_package_data), correction,\n                         exterior_fields_on_face, volume_data,\n                         exterior_unit_normal_covector,\n                         exterior_unit_normal_vector, mesh_velocity,\n                         face_tags{}, dg_package_volume_tags{});\n  } else {\n    call_dg_package_data(make_not_null(&exterior_package_data), correction,\n                         exterior_fields_on_face, volume_data,\n                         exterior_unit_normal_covector, mesh_velocity,\n                         face_tags{}, dg_package_volume_tags{});\n  }\n\n  // We don't need to prefix the VariablesTags with anything because we are not\n  // interacting with any code that cares about what the tags are, just that the\n  // types matched the evolved variables.\n  Variables<VariablesTags> boundary_corrections{\n      face_mesh.number_of_grid_points()};\n\n  // Call C++ implementation of dg_boundary_terms\n  call_dg_boundary_terms(make_not_null(&boundary_corrections), correction,\n                         volume_data, interior_package_data,\n                         exterior_package_data, dg_formulation,\n                         dg_boundary_terms_volume_tags{});\n\n  // Call python implementation of dg_boundary_terms\n  try {\n    using ResultType = tmpl::as_tuple<\n        tmpl::transform<VariablesTags, tmpl::bind<tmpl::type_from, tmpl::_1>>>;\n    INFO(\"Testing boundary terms\");\n    CAPTURE(python_module);\n    const std::string& python_function = python_dg_boundary_terms_function;\n    CAPTURE(python_function);\n    const ResultType python_result = pypp::call<ResultType>(\n        python_module, python_function,\n        get<DgPackageDataTags>(interior_package_data)...,\n        get<DgPackageDataTags>(exterior_package_data)...,\n        dg_formulation == ::dg::Formulation::StrongInertial);\n    tmpl::for_each<VariablesTags>([&python_result, epsilon,\n                                   &boundary_corrections](\n                                      auto boundary_correction_tag_v) {\n      using boundary_correction_tag =\n          tmpl::type_from<decltype(boundary_correction_tag_v)>;\n      const std::string tag_name = db::tag_name<boundary_correction_tag>();\n      CAPTURE(tag_name);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          get<boundary_correction_tag>(boundary_corrections),\n          (std::get<\n              tmpl::index_of<VariablesTags, boundary_correction_tag>::value>(\n              python_result)),\n          Approx::custom().epsilon(epsilon).scale(1.0));\n    });\n  } catch (const std::exception& e) {\n    INFO(\"On line \" << __LINE__ << \" Python call to \"\n                    << python_dg_boundary_terms_function << \" in module \"\n                    << python_module << \" failed: \" << e.what());\n    REQUIRE(false);\n  }\n}\n}  // namespace detail\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Tests that the `dg_package_data` and `dg_boundary_terms` functions\n * agree with the python implementation.\n *\n * The variables are filled with random numbers between zero and one before\n * being passed to the implementations. If in the future we need support for\n * negative numbers we can add the ability to specify a single range for all\n * random numbers or each individually.\n *\n * Please note the following:\n * - The `pypp::SetupLocalPythonEnvironment` must be created before the\n *   `test_boundary_correction_with_python` can be called.\n * - The `dg_formulation` is passed as a bool `use_strong_form` to the python\n *   functions since we don't want to rely on python bindings for the enum.\n * - You can convert custom types using the `ConversionClassList` template\n *   parameter, which is then passed to `pypp::call()`. This allows you to,\n *   e.g., convert an equation of state into an array locally in a test file.\n * - There must be one python function to compute the packaged data for each tag\n *   in `dg_package_field_tags`\n * - There must be one python function to compute the boundary correction for\n *   each tag in `System::variables_tag`\n * - The arguments to the python functions for computing the packaged data are\n *   the same as the arguments for the C++ `dg_package_data` function, excluding\n *   the `gsl::not_null` arguments.\n * - The arguments to the python functions for computing the boundary\n *   corrections are the same as the arguments for the C++ `dg_boundary_terms`\n *   function, excluding the `gsl::not_null` arguments.\n * - By default, each input tensor for `dg_package_data` is randomly generated\n *   from the interval `[-1,1)` (except the metric, which for systems with a\n *   metric is generated to be close to flat space). The argument `ranges` is a\n *   `TaggedTuple` of `TestHelpers::evolution::dg::Tags::Range<tag>` that\n *   enables the caller to pick a custom interval for generating the input\n *   `tag`. This is useful, for example, for tensors that need positive values.\n *   Each `tag` in `ranges` must be an argument of `dg_package_data`.\n */\ntemplate <typename System, typename ConversionClassList = tmpl::list<>,\n          typename BoundaryCorrection, size_t FaceDim, typename... VolumeTags,\n          typename... RangeTags>\nvoid test_boundary_correction_with_python(\n    const gsl::not_null<std::mt19937*> generator,\n    const std::string& python_module,\n    const std::string& python_dg_package_data_function,\n    const std::string& python_dg_boundary_terms_function,\n    const BoundaryCorrection& correction, const Mesh<FaceDim>& face_mesh,\n    const tuples::TaggedTuple<VolumeTags...>& volume_data,\n    const tuples::TaggedTuple<Tags::Range<RangeTags>...>& ranges,\n    const double epsilon = 1.0e-12) {\n  static_assert(std::is_final_v<std::decay_t<BoundaryCorrection>>,\n                \"All boundary correction classes must be marked `final`.\");\n  using package_temporary_tags =\n      typename BoundaryCorrection::dg_package_data_temporary_tags;\n  using package_primitive_tags = detail::get_correction_primitive_vars<\n      System::has_primitive_and_conservative_vars, BoundaryCorrection>;\n  using variables_tags = typename System::variables_tag::tags_list;\n  using flux_variables = typename System::flux_variables;\n  using flux_tags =\n      db::wrap_tags_in<::Tags::Flux, flux_variables, tmpl::size_t<FaceDim + 1>,\n                       Frame::Inertial>;\n\n  for (const auto use_moving_mesh : {\n           false  // , true\n       }) {\n    for (const auto dg_formulation : {\n             ::dg::Formulation::\n                 StrongInertial  // , ::dg::Formulation::WeakInertial\n         }) {\n      detail::test_with_python<System, ConversionClassList, variables_tags>(\n          generator, python_module, python_dg_package_data_function,\n          python_dg_boundary_terms_function, correction, face_mesh, volume_data,\n          ranges, use_moving_mesh, dg_formulation, epsilon,\n          tmpl::append<variables_tags, flux_tags, package_temporary_tags,\n                       package_primitive_tags>{},\n          typename BoundaryCorrection::dg_package_field_tags{});\n    }\n  }\n}\n}  // namespace TestHelpers::evolution::dg\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Actions)\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Limiters/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <unordered_set>\n\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n\nnamespace TestHelpers {\nnamespace Limiters {\n\n// Construct a Neighbors object with one neighboring element.\ntemplate <size_t VolumeDim>\nNeighbors<VolumeDim> make_neighbor_with_id(const size_t id) {\n  return {std::unordered_set<ElementId<VolumeDim>>{ElementId<VolumeDim>(id)},\n          OrientationMap<VolumeDim>::create_aligned()};\n}\n\n// Construct an Element with one neighboring element in each direction.\n//\n// The optional argument specifies directions to external boundaries, i.e.,\n// directions where there is no neighboring element.\ntemplate <size_t VolumeDim>\nElement<VolumeDim> make_element(const std::unordered_set<Direction<VolumeDim>>&\n                                    directions_of_external_boundaries = {}) {\n  typename Element<VolumeDim>::Neighbors_t neighbors;\n  for (const auto& dir : Direction<VolumeDim>::all_directions()) {\n    // Element has neighbors in directions with internal boundaries\n    if (directions_of_external_boundaries.count(dir) == 0) {\n      const size_t index =\n          1 + 2 * dir.dimension() + (dir.side() == Side::Upper ? 1 : 0);\n      neighbors[dir] = make_neighbor_with_id<VolumeDim>(index);\n    }\n  }\n  return Element<VolumeDim>{ElementId<VolumeDim>{0}, neighbors};\n}\n\n}  // namespace Limiters\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/NormalVectors.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n\nnamespace TestHelpers::evolution::dg::detail {\nCREATE_HAS_TYPE_ALIAS(inverse_spatial_metric_tag)\nCREATE_HAS_TYPE_ALIAS_V(inverse_spatial_metric_tag)\n\ntemplate <bool HasInverseSpatialMetricTag = false>\nstruct inverse_spatial_metric_tag {\n  template <typename System>\n  using f = tmpl::list<>;\n};\n\ntemplate <>\nstruct inverse_spatial_metric_tag<true> {\n  template <typename System>\n  using f = typename System::inverse_spatial_metric_tag;\n};\n\n// Takes in a spatial metric, or inverse spatial metric, with random components\n// in the range [-1,1]. Adjusts the metric (or inverse) by scaling all\n// components to lie in [0,0.01] and then adding 1 to the diagonal.\n// This is to give an inverse spatial metric of the form:\n//  \\delta^{ij} + small^{ij}\n// This is done to give a physically reasonable spatial metric (or inverse)\ntemplate <typename Index>\nvoid adjust_spatial_metric_or_inverse(\n    const gsl::not_null<\n        Tensor<DataVector, Symmetry<1, 1>, index_list<Index, Index>>*>\n        spatial_metric_or_inverse) {\n  for (size_t i = 0; i < Index::dim; ++i) {\n    for (size_t j = i; j < Index::dim; ++j) {\n      spatial_metric_or_inverse->get(i, j) += 1.0;\n      spatial_metric_or_inverse->get(i, j) *= 0.005;\n    }\n  }\n  for (size_t i = 0; i < Index::dim; ++i) {\n    spatial_metric_or_inverse->get(i, i) += 1.0;\n  }\n}\n\n// On input the `unit_normal_covector` is the unnormalized normal covector. On\n// output `unit_normal_covector` is the normalized (and hence actually unit)\n// normal covector, and `unit_normal_vector` is the unit normal vector. The\n// inverse spatial metric is used for computing the magnitude of the\n// unnormalized normal vector.\ntemplate <size_t Dim>\nvoid normalize_vector_and_covector(\n    const gsl::not_null<tnsr::i<DataVector, Dim>*> unit_normal_covector,\n    const gsl::not_null<tnsr::I<DataVector, Dim>*> unit_normal_vector,\n    const tnsr::II<DataVector, Dim>& inv_spatial_metric) {\n  for (size_t i = 0; i < Dim; ++i) {\n    unit_normal_vector->get(i) =\n        inv_spatial_metric.get(i, 0) * get<0>(*unit_normal_covector);\n    for (size_t j = 1; j < Dim; ++j) {\n      unit_normal_vector->get(i) +=\n          inv_spatial_metric.get(i, j) * unit_normal_covector->get(j);\n    }\n  }\n\n  const DataVector normal_magnitude =\n      sqrt(get(dot_product(*unit_normal_covector, *unit_normal_vector)));\n  for (auto& t : *unit_normal_covector) {\n    t /= normal_magnitude;\n  }\n  for (auto& t : *unit_normal_vector) {\n    t /= normal_magnitude;\n  }\n}\n}  // namespace TestHelpers::evolution::dg::detail\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/DiscontinuousGalerkin/Range.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\nnamespace TestHelpers::evolution::dg::Tags {\n\n/// Tag for a `TaggedTuple` that holds the range of validity for the variable\n/// associated with `Tag`.\ntemplate <typename Tag>\nstruct Range {\n  using tag = Tag;\n  using type = std::array<double, 2>;\n};\n\n}  // namespace TestHelpers::evolution::dg::Tags\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Imex/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"ImexHelpers\")\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  DoImplicitStepSector.hpp\n  TestSector.hpp\n  )\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  DoImplicitStepInstantiate.cpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Imex\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Imex/DoImplicitStepInstantiate.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Imex/CleanHistory.hpp\"\n#include \"Evolution/Imex/CleanHistory.tpp\"\n#include \"Evolution/Imex/SolveImplicitSector.hpp\"\n#include \"Evolution/Imex/SolveImplicitSector.tpp\"\n#include \"Helpers/Evolution/Imex/DoImplicitStepSector.hpp\"\n\nnamespace helpers = do_implicit_step_helpers;\ntemplate struct imex::CleanHistory<helpers::System>;\ntemplate struct imex::SolveImplicitSector<helpers::System::variables_tag,\n                                          helpers::Sector<helpers::Var1>>;\ntemplate struct imex::SolveImplicitSector<helpers::System::variables_tag,\n                                          helpers::Sector<helpers::Var2>>;\ntemplate struct imex::SolveImplicitSector<\n    helpers::NonautonomousSystem::variables_tag, helpers::NonautonomousSector>;\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Imex/DoImplicitStepSector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <vector>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Protocols/ImexSystem.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSource.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// Separated from Test_DoImplicitStep.cpp to test explicit\n// instantiation of SolveSimplcitSector (in\n// DoImplicitStepInstantiate.cpp).\nnamespace do_implicit_step_helpers {\ntemplate <typename Var>\n// [simple_sector]\nstruct Sector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var>;\n\n  struct initial_guess {\n    using return_tags = tmpl::list<Var>;\n    using argument_tags = tmpl::list<>;\n    static std::vector<imex::GuessResult> apply(\n        const gsl::not_null<Scalar<DataVector>*> var,\n        const Variables<tmpl::list<Var>>& inhomogeneous_terms,\n        const double implicit_weight) {\n      get(*var) = get(get<Var>(inhomogeneous_terms)) / (1.0 + implicit_weight);\n      return {get(*var).size(), imex::GuessResult::ExactSolution};\n    }\n  };\n\n  struct SolveAttempt {\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<Tags::Source<Var>>;\n      using argument_tags = tmpl::list<Var>;\n      static void apply(const gsl::not_null<Scalar<DataVector>*> source,\n                        const Scalar<DataVector>& var) {\n        get(*source) = -get(var);\n      }\n    };\n\n    using jacobian = imex::NoJacobianBecauseSolutionIsAnalytic;\n\n    using tags_from_evolution = tmpl::list<>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n  };\n\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\n// [simple_sector]\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n// [ImexSystem]\nstruct System : tt::ConformsTo<imex::protocols::ImexSystem> {\n  using variables_tag = Tags::Variables<tmpl::list<Var1, Var2>>;\n\n  // Explicit evolution stuff here...\n\n  using implicit_sectors = tmpl::list<Sector<Var1>, Sector<Var2>>;\n};\n// [ImexSystem]\n\nstruct NonautonomousSector : tt::ConformsTo<imex::protocols::ImplicitSector> {\n  using tensors = tmpl::list<Var1>;\n\n  struct initial_guess {\n    using return_tags = tmpl::list<Var1>;\n    using argument_tags = tmpl::list<::Tags::Time>;\n    static std::vector<imex::GuessResult> apply(\n        const gsl::not_null<Scalar<DataVector>*> var, const double time,\n        const Variables<tmpl::list<Var1>>& inhomogeneous_terms,\n        const double implicit_weight) {\n      get(*var) = get(get<Var1>(inhomogeneous_terms)) + implicit_weight * time;\n      return {get(*var).size(), imex::GuessResult::ExactSolution};\n    }\n  };\n\n  struct SolveAttempt {\n    struct source : tt::ConformsTo<imex::protocols::ImplicitSource>,\n                    tt::ConformsTo<::protocols::StaticReturnApplyable> {\n      using return_tags = tmpl::list<Tags::Source<Var1>>;\n      using argument_tags = tmpl::list<::Tags::Time>;\n      static void apply(const gsl::not_null<Scalar<DataVector>*> source,\n                        const double time) {\n        get(*source) = time;\n      }\n    };\n\n    using jacobian = imex::NoJacobianBecauseSolutionIsAnalytic;\n\n    using tags_from_evolution = tmpl::list<::Tags::Time>;\n    using simple_tags = tmpl::list<>;\n    using compute_tags = tmpl::list<>;\n    using source_prep = tmpl::list<>;\n    using jacobian_prep = tmpl::list<>;\n  };\n\n  using solve_attempts = tmpl::list<SolveAttempt>;\n};\n\nstruct NonautonomousSystem : tt::ConformsTo<imex::protocols::ImexSystem> {\n  using variables_tag = Tags::Variables<tmpl::list<Var1>>;\n  using implicit_sectors = tmpl::list<NonautonomousSector>;\n};\n}  // namespace do_implicit_step_helpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Imex/TestSector.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/ContractFirstNIndices.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Imex/GuessResult.hpp\"\n#include \"Evolution/Imex/Protocols/ImplicitSector.hpp\"\n#include \"Evolution/Imex/Tags/Jacobian.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace TestHelpers::imex {\nnamespace test_sector_detail {\ntemplate <typename Tag>\nstruct ReadOnlySource : ::db::SimpleTag {\n  using type = typename Tag::type;\n};\n\ntemplate <typename Tag>\nstruct ReadOnly : Tag, ::db::ComputeTag {\n  using base = Tag;\n  using argument_tags = tmpl::list<ReadOnlySource<Tag>>;\n  static void function(const gsl::not_null<typename ReadOnly::type*> dest,\n                       const typename ReadOnly::type& source) {\n    *dest = source;\n  }\n};\n\ntemplate <typename T>\nstruct OnlyEntry;\n\ntemplate <typename T>\nstruct OnlyEntry<tmpl::list<T>> {\n  using type = T;\n};\n}  // namespace test_sector_detail\n\n/// Check that an implicit sector conforms to\n/// ::imex::protocols::ImplicitSector and that its `source` and\n/// `jacobian` are consistent with each other.\ntemplate <typename Sector, typename SolveAttempt = typename test_sector_detail::\n                               OnlyEntry<typename Sector::solve_attempts>::type>\nvoid test_sector(const double stencil_size, const double tolerance,\n                 const Variables<typename Sector::tensors>& explicit_values,\n                 tuples::tagged_tuple_from_typelist<\n                     typename SolveAttempt::tags_from_evolution>\n                     evolution_data) {\n  static_assert(\n      tt::assert_conforms_to_v<Sector, ::imex::protocols::ImplicitSector>);\n  static_assert(\n      tmpl::list_contains_v<typename Sector::solve_attempts, SolveAttempt>);\n\n  using sector_variables_tag = ::Tags::Variables<typename Sector::tensors>;\n  using SectorVariables = typename sector_variables_tag::type;\n  using sector_source_tag =\n      ::db::add_tag_prefix<::Tags::Source, sector_variables_tag>;\n\n  using sector_jacobian_tag = ::Tags::Variables<::imex::jacobian_tags<\n      typename Sector::tensors, typename sector_source_tag::type::tags_list>>;\n\n  // Make tags that are immutable in real use immutable here too.\n  using read_only_tags_from_evolution_source =\n      tmpl::transform<typename SolveAttempt::tags_from_evolution,\n                      tmpl::bind<test_sector_detail::ReadOnlySource, tmpl::_1>>;\n  using read_only_tags_from_evolution =\n      tmpl::transform<typename SolveAttempt::tags_from_evolution,\n                      tmpl::bind<test_sector_detail::ReadOnly, tmpl::_1>>;\n\n  using simple_tags = tmpl::append<\n      read_only_tags_from_evolution_source, typename SolveAttempt::simple_tags,\n      tmpl::list<sector_variables_tag, sector_source_tag, sector_jacobian_tag>>;\n  using compute_tags = tmpl::append<read_only_tags_from_evolution,\n                                    typename SolveAttempt::compute_tags>;\n  auto box = ::db::create<simple_tags, compute_tags>();\n  tmpl::for_each<typename SolveAttempt::tags_from_evolution>([&](auto tag) {\n    using Tag = tmpl::type_from<decltype(tag)>;\n    ::db::mutate<test_sector_detail::ReadOnlySource<Tag>>(\n        [&](const gsl::not_null<typename Tag::type*> box_value) {\n          *box_value = std::move(get<Tag>(evolution_data));\n        },\n        make_not_null(&box));\n  });\n  using variables_tags = tmpl::push_front<\n      tmpl::filter<typename SolveAttempt::simple_tags,\n                   tt::is_a<Variables, tmpl::bind<tmpl::type_from, tmpl::_1>>>,\n      sector_source_tag, sector_jacobian_tag>;\n  ::db::mutate_apply<variables_tags, tmpl::list<>>(\n      [](const auto... vars) { expand_pack((vars->initialize(1, 0.0), 0)...); },\n      make_not_null(&box));\n  ::db::mutate<sector_variables_tag>(\n      [&](const gsl::not_null<SectorVariables*> vars) {\n        *vars = explicit_values;\n      },\n      make_not_null(&box));\n\n  // Arbitrary values.  These would depend on the choice of time\n  // stepper and step size and such.\n  const SectorVariables inhomogeneous_terms = 1.1 * explicit_values;\n  const double implicit_weight = 0.4;\n\n  const std::vector<::imex::GuessResult> guess_type =\n      ::db::mutate_apply<typename Sector::initial_guess>(\n          make_not_null(&box), inhomogeneous_terms, implicit_weight);\n  CHECK(guess_type.size() <= 1);\n  const SectorVariables initial_guess = ::db::get<sector_variables_tag>(box);\n  if (guess_type == std::vector{::imex::GuessResult::ExactSolution}) {\n    // If the sector can always be solved analytically, the jacobian\n    // need not be coded.  Instead, we check that the supplied\n    // solution is correct.\n    tmpl::for_each<typename SolveAttempt::source_prep>([&](auto mutator) {\n      using Mutator = tmpl::type_from<decltype(mutator)>;\n      ::db::mutate_apply<Mutator>(make_not_null(&box));\n    });\n    ::db::mutate_apply<typename SolveAttempt::source>(make_not_null(&box));\n    const auto& source = ::db::get<sector_source_tag>(box);\n    const SectorVariables step_result =\n        inhomogeneous_terms + implicit_weight * source;\n    CHECK_VARIABLES_CUSTOM_APPROX(initial_guess, step_result,\n                                  Approx::custom().epsilon(tolerance));\n  } else {\n    // Check the jacobian numerically.\n    tmpl::for_each<typename SolveAttempt::jacobian_prep>([&](auto mutator) {\n      using Mutator = tmpl::type_from<decltype(mutator)>;\n      ::db::mutate_apply<Mutator>(make_not_null(&box));\n    });\n    ::db::mutate_apply<typename SolveAttempt::jacobian>(make_not_null(&box));\n    const auto jacobian = ::db::get<sector_jacobian_tag>(box);\n    tmpl::for_each<typename Sector::tensors>([&](auto varying_tensor_v) {\n      using varying_tensor = tmpl::type_from<decltype(varying_tensor_v)>;\n      CAPTURE(pretty_type::get_name<varying_tensor>());\n      for (size_t varying_component = 0;\n           varying_component < varying_tensor::type::size();\n           ++varying_component) {\n        ::db::mutate<sector_variables_tag>(\n            [&](const gsl::not_null<SectorVariables*> vars) {\n              *vars = initial_guess;\n            },\n            make_not_null(&box));\n        const auto finite_difference_derivative = numerical_derivative(\n            [&](const std::array<double, 1>& component_value) {\n              ::db::mutate<varying_tensor>(\n                  [&](const gsl::not_null<typename varying_tensor::type*> var) {\n                    (*var)[varying_component] = component_value[0];\n                  },\n                  make_not_null(&box));\n              tmpl::for_each<typename SolveAttempt::source_prep>(\n                  [&](auto mutator) {\n                    using Mutator = tmpl::type_from<decltype(mutator)>;\n                    ::db::mutate_apply<Mutator>(make_not_null(&box));\n                  });\n              ::db::mutate_apply<typename SolveAttempt::source>(\n                  make_not_null(&box));\n              return ::db::get<sector_source_tag>(box);\n            },\n            std::array{\n                get<varying_tensor>(initial_guess)[varying_component][0]},\n            0, stencil_size);\n        typename varying_tensor::type variation(1_st, 0.0);\n        variation[varying_component] = 1.0;\n        CAPTURE(variation);\n        tmpl::for_each<typename sector_source_tag::type::tags_list>(\n            [&](auto source_tensor_v) {\n              using source_tensor = tmpl::type_from<decltype(source_tensor_v)>;\n              CAPTURE(pretty_type::get_name<source_tensor>());\n              auto analytic_derivative = contract_first_n_indices<\n                  varying_tensor::type::num_tensor_indices>(\n                  variation,\n                  get<::imex::Tags::Jacobian<varying_tensor, source_tensor>>(\n                      jacobian));\n              // We want the derivative with respect to a single\n              // component, but with multiplicities all the equivalent\n              // components were actually set, so the result is larger\n              // than it should be.\n              for (auto& component : analytic_derivative) {\n                component /= variation.multiplicity(varying_component);\n              }\n              static_assert(\n                  std::is_same_v<std::decay_t<decltype(analytic_derivative)>,\n                                 typename source_tensor::type>);\n              CHECK_ITERABLE_CUSTOM_APPROX(\n                  analytic_derivative,\n                  get<source_tensor>(finite_difference_derivative),\n                  Approx::custom().epsilon(tolerance));\n            });\n      }\n    });\n  }\n}\n}  // namespace TestHelpers::imex\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Burgers/FiniteDifference/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/Systems/Burgers/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers {\n/*!\n * \\brief Defines functions useful for testing Burgers subcell\n */\nnamespace Burgers::fd {\ntemplate <typename F>\nDirectionalIdMap<1, evolution::dg::subcell::GhostData> compute_ghost_data(\n    const Mesh<1>& subcell_mesh,\n    const tnsr::I<DataVector, 1, Frame::ElementLogical>& volume_logical_coords,\n    const DirectionMap<1, Neighbors<1>>& neighbors,\n    const size_t ghost_zone_size, const F& compute_variables_of_neighbor_data) {\n  DirectionalIdMap<1, evolution::dg::subcell::GhostData> ghost_data{};\n  for (const auto& [direction, neighbors_in_direction] : neighbors) {\n    REQUIRE(neighbors_in_direction.size() ==\n            1);  // currently only support one neighbor in each direction\n    const ElementId<1>& neighbor_id = *neighbors_in_direction.begin();\n\n    auto neighbor_logical_coords = volume_logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    const auto neighbor_vars_for_reconstruction =\n        compute_variables_of_neighbor_data(neighbor_logical_coords);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars_for_reconstruction.data(),\n                       neighbor_vars_for_reconstruction.size()),\n        subcell_mesh.extents(), ghost_zone_size,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    ghost_data[DirectionalId<1>{direction, neighbor_id}] =\n        evolution::dg::subcell::GhostData{1};\n    ghost_data.at(DirectionalId<1>{direction, neighbor_id})\n        .neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n  return ghost_data;\n}\n\ntemplate <typename Reconstructor>\nvoid test_reconstructor(const size_t num_pts,\n                        const Reconstructor& derived_reconstructor) {\n  // 1. create U that will be reconstructed, being linear to x\n  // 2. send through reconstruction\n  // 3. check if U was reconstructed correctly\n\n  const ::Burgers::fd::Reconstructor& reconstructor = derived_reconstructor;\n  static_assert(tmpl::list_contains_v<\n                typename ::Burgers::fd::Reconstructor::creatable_classes,\n                Reconstructor>);\n\n  // create an element and its neighbor elements\n  DirectionMap<1, Neighbors<1>> neighbors{};\n  for (size_t i = 0; i < 2; ++i) {\n    neighbors[gsl::at(Direction<1>::all_directions(), i)] = Neighbors<1>{\n        {ElementId<1>{i + 1, {}}}, OrientationMap<1>::create_aligned()};\n  }\n  const Element<1> element{ElementId<1>{0, {}}, neighbors};\n\n  // a simple, linear form of the scalar field U for testing purpose\n  //   * U(x)   = x + 1       (for 1D)\n  const auto compute_solution = [](const auto& coords) {\n    Variables<tmpl::list<::Burgers::Tags::U>> vars{get<0>(coords).size(), 0.0};\n    get(get<::Burgers::Tags::U>(vars)) += coords.get(0) + 1.0;\n    return vars;\n  };\n\n  // compute neighbor data\n  const Mesh<1> subcell_mesh{num_pts, Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(subcell_mesh);\n  const DirectionalIdMap<1, evolution::dg::subcell::GhostData> ghost_data =\n      compute_ghost_data(subcell_mesh, logical_coords, element.neighbors(),\n                         reconstructor.ghost_zone_size(), compute_solution);\n\n  // create Variables on lower and upper faces to perform reconstruction\n  using dg_package_data_argument_tags = tmpl::list<\n      ::Burgers::Tags::U,\n      ::Tags::Flux<::Burgers::Tags::U, tmpl::size_t<1>, Frame::Inertial>>;\n  const size_t reconstructed_num_pts = subcell_mesh.number_of_grid_points() + 1;\n  std::array<Variables<dg_package_data_argument_tags>, 1> vars_on_lower_face =\n      make_array<1>(\n          Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n  std::array<Variables<dg_package_data_argument_tags>, 1> vars_on_upper_face =\n      make_array<1>(\n          Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n\n  Variables<tmpl::list<::Burgers::Tags::U>> volume_vars{\n      subcell_mesh.number_of_grid_points()};\n  volume_vars.assign_subset(compute_solution(logical_coords));\n\n  // call the reconstruction\n  dynamic_cast<const Reconstructor&>(reconstructor)\n      .reconstruct(make_not_null(&vars_on_lower_face),\n                   make_not_null(&vars_on_upper_face), volume_vars, element,\n                   ghost_data, subcell_mesh);\n\n  for (size_t dim = 0; dim < 1; ++dim) {\n    // construct face-centered coordinates\n    const auto basis = make_array<1>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<1>(Spectral::Quadrature::CellCentered);\n    auto extents = make_array<1>(num_pts);\n    gsl::at(extents, dim) = num_pts + 1;\n    gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n    const Mesh<1> face_centered_mesh{extents, basis, quadrature};\n    auto logical_coords_face_centered = logical_coordinates(face_centered_mesh);\n\n    // check reconstructed values for reconstruct() function\n    Variables<dg_package_data_argument_tags> expected_face_values{\n        face_centered_mesh.number_of_grid_points()};\n    expected_face_values.assign_subset(\n        compute_solution(logical_coords_face_centered));\n\n    CHECK_ITERABLE_APPROX(\n        get<::Burgers::Tags::U>(gsl::at(vars_on_lower_face, dim)),\n        get<::Burgers::Tags::U>(expected_face_values));\n    CHECK_ITERABLE_APPROX(\n        get<::Burgers::Tags::U>(gsl::at(vars_on_upper_face, dim)),\n        get<::Burgers::Tags::U>(expected_face_values));\n\n    // check reconstructed values for reconstruct_fd_neighbor() function\n    Variables<dg_package_data_argument_tags> vars_on_mortar_face{0};\n    Variables<dg_package_data_argument_tags> expected_mortar_face_values{0};\n\n    for (size_t upper_or_lower = 0; upper_or_lower < 2; ++upper_or_lower) {\n      // iterate over upper and lower direction\n      Direction<1> direction =\n          gsl::at(Direction<1>::all_directions(), 2 * dim + upper_or_lower);\n\n      // 1D mortar has only one face point\n      vars_on_mortar_face.initialize(1);\n      expected_mortar_face_values.initialize(1);\n\n      // call the reconstruction\n      dynamic_cast<const Reconstructor&>(reconstructor)\n          .reconstruct_fd_neighbor(make_not_null(&vars_on_mortar_face),\n                                   volume_vars, element, ghost_data,\n                                   subcell_mesh, direction);\n\n      // slice face-centered variables to get the values on the mortar\n      data_on_slice(\n          make_not_null(&get<::Burgers::Tags::U>(expected_mortar_face_values)),\n          get<::Burgers::Tags::U>(expected_face_values),\n          face_centered_mesh.extents(), direction.dimension(),\n          direction.side() == Side::Lower ? 0\n                                          : extents[direction.dimension()] - 1);\n\n      CHECK_ITERABLE_APPROX(\n          get<::Burgers::Tags::U>(vars_on_mortar_face),\n          get<::Burgers::Tags::U>(expected_mortar_face_values));\n    }\n  }\n}\n\n}  // namespace Burgers::fd\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Cce)\nadd_subdirectory(CurvedScalarWave)\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/Actions/CharacteristicInitialization.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n\nnamespace Cce::TestHelpers {\n\ntemplate <typename EvolutionComponent, typename Metavariables>\nvoid check_characteristic_initialization(\n    ActionTesting::MockRuntimeSystem<Metavariables>& runner,\n    const double start_time, const double target_step_size, const size_t l_max,\n    const size_t number_of_radial_points) {\n  // the tags inserted in the `EvolutionTags` step\n  const auto& time_step_id =\n      ActionTesting::get_databox_tag<EvolutionComponent, ::Tags::TimeStepId>(\n          runner, 0);\n  CHECK(time_step_id.substep_time() == start_time);\n  const auto& next_time_step_id =\n      ActionTesting::get_databox_tag<EvolutionComponent,\n                                     ::Tags::Next<::Tags::TimeStepId>>(runner,\n                                                                       0);\n  CHECK(next_time_step_id.substep_time() ==\n        approx(start_time + target_step_size * 0.75));\n  const auto& time_step =\n      ActionTesting::get_databox_tag<EvolutionComponent, ::Tags::TimeStep>(\n          runner, 0);\n  CHECK(time_step.value() == approx(target_step_size * 0.75));\n  const auto& coordinates_history = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::HistoryEvolvedVariables<\n          typename Metavariables::evolved_coordinates_variables_tag>>(runner,\n                                                                      0);\n  CHECK(coordinates_history.size() == 0);\n  const auto& evolved_swsh_history = ActionTesting::get_databox_tag<\n      EvolutionComponent, ::Tags::HistoryEvolvedVariables<::Tags::Variables<\n                              typename Metavariables::evolved_swsh_tags>>>(\n      runner, 0);\n  CHECK(evolved_swsh_history.size() == 0);\n\n  // the tensor storage variables inserted during the `CharacteristicTags` step\n  const auto& boundary_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<\n          tmpl::append<typename Metavariables::cce_boundary_communication_tags,\n                       typename Metavariables::cce_gauge_boundary_tags>>>(\n      runner, 0);\n  CHECK(boundary_variables.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const auto& coordinate_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      typename Metavariables::evolved_coordinates_variables_tag>(runner, 0);\n  CHECK(coordinate_variables.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const auto& dt_coordinate_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      db::add_tag_prefix<::Tags::dt, typename Metavariables::\n                                         evolved_coordinates_variables_tag>>(\n      runner, 0);\n  CHECK(dt_coordinate_variables.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const auto& angular_coordinates_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<typename Metavariables::cce_angular_coordinate_tags>>(\n      runner, 0);\n  CHECK(angular_coordinates_variables.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n  const auto& scri_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<typename Metavariables::cce_scri_tags>>(runner, 0);\n  CHECK(scri_variables.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n\n  const auto& volume_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<\n          tmpl::append<typename Metavariables::cce_integrand_tags,\n                       typename Metavariables::cce_integration_independent_tags,\n                       typename Metavariables::cce_temporary_equations_tags>>>(\n      runner, 0);\n  CHECK(volume_variables.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n            number_of_radial_points);\n\n  const auto& evolved_swsh_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<typename Metavariables::evolved_swsh_tags>>(runner, 0);\n  CHECK(evolved_swsh_variables.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n            number_of_radial_points);\n\n  const auto& evolved_swsh_dt_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<typename Metavariables::evolved_swsh_dt_tags>>(runner,\n                                                                       0);\n  CHECK(evolved_swsh_dt_variables.number_of_grid_points() ==\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n            number_of_radial_points);\n\n  const auto& pre_swsh_derivatives_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<typename Metavariables::cce_pre_swsh_derivatives_tags>>(\n      runner, 0);\n  const Variables<typename Metavariables::cce_pre_swsh_derivatives_tags>\n      expected_zeroed_pre_swsh_derivatives{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n              number_of_radial_points,\n          0.0};\n  CHECK(pre_swsh_derivatives_variables == expected_zeroed_pre_swsh_derivatives);\n\n  const auto& transform_buffer_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<typename Metavariables::cce_transform_buffer_tags>>(\n      runner, 0);\n  const Variables<typename Metavariables::cce_transform_buffer_tags>\n      expected_zeroed_transform_buffer{\n          Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max) *\n              number_of_radial_points,\n          0.0};\n  CHECK(transform_buffer_variables == expected_zeroed_transform_buffer);\n\n  const auto& swsh_derivatives_variables = ActionTesting::get_databox_tag<\n      EvolutionComponent,\n      ::Tags::Variables<typename Metavariables::cce_swsh_derivative_tags>>(\n      runner, 0);\n  const Variables<typename Metavariables::cce_swsh_derivative_tags>\n      expected_zeroed_swsh_derivatives{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max) *\n              number_of_radial_points,\n          0.0};\n  CHECK(swsh_derivatives_variables == expected_zeroed_swsh_derivatives);\n}\n}  // namespace Cce::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/Actions/WorldtubeBoundaryMocking.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Actions/InitializeWorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Components/WorldtubeBoundary.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce {\n/// \\cond\nnamespace {  // NOLINT\nstruct test_metavariables;\ntemplate <typename Metavariables>\nstruct mock_characteristic_evolution;\ntemplate <typename Metavariables>\nstruct mock_kg_characteristic_evolution;\n}  // namespace\nnamespace Actions {\nnamespace {  // NOLINT\ntemplate <typename BoundaryComponent, typename EvolutionComponent>\nstruct MockBoundaryComputeAndSendToEvolution;\n}  // namespace\n}  // namespace Actions\n/// \\endcond\n\ntemplate <typename Metavariables>\nstruct mock_h5_worldtube_boundary {\n  using component_being_mocked = H5WorldtubeBoundary<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<Actions::BoundaryComputeAndSendToEvolution<\n          H5WorldtubeBoundary<Metavariables>,\n          mock_characteristic_evolution<test_metavariables>>>;\n  using with_these_simple_actions =\n      tmpl::list<Actions::MockBoundaryComputeAndSendToEvolution<\n          H5WorldtubeBoundary<Metavariables>,\n          mock_characteristic_evolution<test_metavariables>>>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeWorldtubeBoundary<H5WorldtubeBoundary<Metavariables>>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_gh_worldtube_boundary {\n  using component_being_mocked = GhWorldtubeBoundary<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<Actions::BoundaryComputeAndSendToEvolution<\n          GhWorldtubeBoundary<Metavariables>,\n          mock_characteristic_evolution<test_metavariables>>>;\n  using with_these_simple_actions =\n      tmpl::list<Actions::MockBoundaryComputeAndSendToEvolution<\n          GhWorldtubeBoundary<Metavariables>,\n          mock_characteristic_evolution<test_metavariables>>>;\n\n  using initialize_action_list = tmpl::list<\n      Actions::InitializeWorldtubeBoundary<GhWorldtubeBoundary<Metavariables>>,\n      Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n    phase_dependent_action_list>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_klein_gordon_h5_worldtube_boundary {\n  using component_being_mocked = KleinGordonH5WorldtubeBoundary<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<Actions::BoundaryComputeAndSendToEvolution<\n          KleinGordonH5WorldtubeBoundary<Metavariables>,\n          mock_kg_characteristic_evolution<test_metavariables>>>;\n  using with_these_simple_actions =\n      tmpl::list<Actions::MockBoundaryComputeAndSendToEvolution<\n          KleinGordonH5WorldtubeBoundary<Metavariables>,\n          mock_kg_characteristic_evolution<test_metavariables>>>;\n\n  using initialize_action_list =\n      tmpl::list<Actions::InitializeWorldtubeBoundary<\n                     KleinGordonH5WorldtubeBoundary<Metavariables>>,\n                 Parallel::Actions::TerminatePhase>;\n  using simple_tags_from_options =\n      Parallel::get_simple_tags_from_options<initialize_action_list>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        initialize_action_list>,\n                 Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<>>>;\n};\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.hpp\"\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticBoundaryDataManager.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n\nnamespace Cce::Solutions::TestHelpers {\n\ntuples::TaggedTuple<Tags::BondiBeta, Tags::BondiU, Tags::BondiW, Tags::BondiJ>\nextract_bondi_scalars_from_cartesian_metric(\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const CartesianiSphericalJ& inverse_jacobian,\n    const double extraction_radius) {\n  const auto inverse_cartesian_metric =\n      determinant_and_inverse(spacetime_metric).second;\n  tnsr::AA<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n      inverse_spherical_metric{get<0, 0>(spacetime_metric).size(), 0.0};\n  get<0, 0>(inverse_spherical_metric) = get<0, 0>(inverse_cartesian_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t k = 0; k < 3; ++k) {\n      inverse_spherical_metric.get(0, i + 1) +=\n          inverse_jacobian.get(k, i) * inverse_cartesian_metric.get(0, k + 1);\n    }\n    for (size_t j = i; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t l = 0; l < 3; ++l) {\n          inverse_spherical_metric.get(i + 1, j + 1) +=\n              inverse_jacobian.get(k, i) * inverse_jacobian.get(l, j) *\n              inverse_cartesian_metric.get(k + 1, l + 1);\n        }\n      }\n    }\n  }\n  get<0, 0>(inverse_spherical_metric) +=\n      -2.0 * get<0, 1>(inverse_spherical_metric) +\n      get<1, 1>(inverse_spherical_metric);\n  for(size_t i = 0; i < 3; ++i) {\n    inverse_spherical_metric.get(0, i) -= inverse_spherical_metric.get(1, i);\n  }\n  Scalar<SpinWeighted<ComplexDataVector, 0>> bondi_beta{\n      get<0, 0>(spacetime_metric).size()};\n  get(bondi_beta).data() = std::complex<double>(1.0, 0.0) * 0.5 *\n                           log(-get<0, 1>(inverse_spherical_metric));\n  Scalar<SpinWeighted<ComplexDataVector, 0>> bondi_w{\n      get<0, 0>(spacetime_metric).size()};\n  get(bondi_w) = std::complex<double>(1.0, 0.0) *\n                 (-get<1, 1>(inverse_spherical_metric) /\n                      get<0, 1>(inverse_spherical_metric) -\n                  1.0) /\n                 extraction_radius;\n  Scalar<SpinWeighted<ComplexDataVector, 1>> bondi_u{\n      get<0, 0>(spacetime_metric).size()};\n  // note that the jacobian components are provided in 'pfaffian' form - so the\n  // extra factors of sin(theta) are omitted.\n  get(bondi_u) =\n      -(std::complex<double>(1.0, 0.0) * get<1, 2>(inverse_spherical_metric) +\n        std::complex<double>(0.0, 1.0) * get<1, 3>(inverse_spherical_metric)) /\n      get<0, 1>(inverse_spherical_metric);\n  Scalar<SpinWeighted<ComplexDataVector, 2>> bondi_j{\n      get<0, 0>(spacetime_metric).size()};\n  get(bondi_j) =\n      -0.5 * square(extraction_radius) *\n      (get<2, 2>(inverse_spherical_metric) -\n       get<3, 3>(inverse_spherical_metric) +\n       std::complex<double>(0.0, 2.0) * get<2, 3>(inverse_spherical_metric));\n  return {bondi_beta, bondi_u, bondi_w, bondi_j};\n}\n\ntuples::TaggedTuple<::Tags::dt<Tags::BondiBeta>, ::Tags::dt<Tags::BondiU>,\n                    ::Tags::dt<Tags::BondiW>, ::Tags::dt<Tags::BondiJ>>\nextract_dt_bondi_scalars_from_cartesian_metric(\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const CartesianiSphericalJ& inverse_jacobian,\n    const double extraction_radius) {\n  const auto inverse_cartesian_metric =\n      determinant_and_inverse(spacetime_metric).second;\n  tnsr::AA<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n      dt_inverse_cartesian_metric{get<0, 0>(spacetime_metric).size(), 0.0};\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      for (size_t c = 0; c < 4; ++c) {\n        for (size_t d = 0; d < 4; ++d) {\n          dt_inverse_cartesian_metric.get(a, b) +=\n              -inverse_cartesian_metric.get(a, c) *\n              inverse_cartesian_metric.get(b, d) *\n              dt_spacetime_metric.get(c, d);\n        }\n      }\n    }\n  }\n\n  tnsr::AA<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n      inverse_spherical_metric{get<0, 0>(spacetime_metric).size(), 0.0};\n  tnsr::AA<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n      dt_inverse_spherical_metric{get<0, 0>(spacetime_metric).size(), 0.0};\n  get<0, 0>(inverse_spherical_metric) = get<0, 0>(inverse_cartesian_metric);\n  get<0, 0>(dt_inverse_spherical_metric) =\n      get<0, 0>(dt_inverse_cartesian_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t k = 0; k < 3; ++k) {\n      inverse_spherical_metric.get(0, i + 1) +=\n          inverse_jacobian.get(k, i) * inverse_cartesian_metric.get(0, k + 1);\n      dt_inverse_spherical_metric.get(0, i + 1) +=\n          inverse_jacobian.get(k, i) *\n          dt_inverse_cartesian_metric.get(0, k + 1);\n    }\n    for (size_t j = i; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t l = 0; l < 3; ++l) {\n          inverse_spherical_metric.get(i + 1, j + 1) +=\n              inverse_jacobian.get(k, i) * inverse_jacobian.get(l, j) *\n              inverse_cartesian_metric.get(k + 1, l + 1);\n          dt_inverse_spherical_metric.get(i + 1, j + 1) +=\n              inverse_jacobian.get(k, i) * inverse_jacobian.get(l, j) *\n              dt_inverse_cartesian_metric.get(k + 1, l + 1);\n        }\n      }\n    }\n  }\n  get<0, 0>(inverse_spherical_metric) +=\n      -2.0 * get<0, 1>(inverse_spherical_metric) +\n      get<1, 1>(inverse_spherical_metric);\n  get<0, 0>(dt_inverse_spherical_metric) +=\n      -2.0 * get<0, 1>(dt_inverse_spherical_metric) +\n      get<1, 1>(dt_inverse_spherical_metric);\n  for(size_t i = 0; i < 3; ++i) {\n    inverse_spherical_metric.get(0, i) -= inverse_spherical_metric.get(1, i);\n    dt_inverse_spherical_metric.get(0, i) -=\n        dt_inverse_spherical_metric.get(1, i);\n  }\n\n  // The formulas for these scalars can be determined by differentiating the\n  // forms in `extract_bondi_scalars_from_cartesian_metric`\n  Scalar<SpinWeighted<ComplexDataVector, 0>> dt_bondi_beta{\n      get<0, 0>(spacetime_metric).size()};\n  get(dt_bondi_beta) = -std::complex<double>(0.5, 0.0) *\n                       get<0, 1>(dt_inverse_spherical_metric) /\n                       get<0, 1>(inverse_spherical_metric);\n  Scalar<SpinWeighted<ComplexDataVector, 0>> dt_bondi_w{\n      get<0, 0>(spacetime_metric).size()};\n  get(dt_bondi_w) = std::complex<double>(1.0, 0.0) *\n                    (-get<1, 1>(dt_inverse_spherical_metric) /\n                         get<0, 1>(inverse_spherical_metric) +\n                     get<1, 1>(inverse_spherical_metric) *\n                         get<0, 1>(dt_inverse_spherical_metric) /\n                         square(get<0, 1>(inverse_spherical_metric))) /\n                    extraction_radius;\n  Scalar<SpinWeighted<ComplexDataVector, 1>> dt_bondi_u{\n      get<0, 0>(spacetime_metric).size()};\n  // note that the jacobian components are provided in 'pfaffian' form - so the\n  // extra factors of sin(theta) are omitted.\n  get(dt_bondi_u) =\n      -(std::complex<double>(1.0, 0.0) *\n            get<1, 2>(dt_inverse_spherical_metric) +\n        std::complex<double>(0.0, 1.0) *\n            get<1, 3>(dt_inverse_spherical_metric)) /\n          get<0, 1>(inverse_spherical_metric) +\n      (std::complex<double>(1.0, 0.0) * get<1, 2>(inverse_spherical_metric) +\n       std::complex<double>(0.0, 1.0) * get<1, 3>(inverse_spherical_metric)) *\n          get<0, 1>(dt_inverse_spherical_metric) /\n          square(get<0, 1>(inverse_spherical_metric));\n  Scalar<SpinWeighted<ComplexDataVector, 2>> dt_bondi_j{\n      get<0, 0>(spacetime_metric).size()};\n  get(dt_bondi_j) = -square(extraction_radius) *\n                    std::complex<double>(0.5, 0.0) *\n                    (get<2, 2>(dt_inverse_spherical_metric) -\n                     get<3, 3>(dt_inverse_spherical_metric) +\n                     std::complex<double>(0.0, 2.0) *\n                         (get<2, 3>(dt_inverse_spherical_metric)));\n  return {dt_bondi_beta, dt_bondi_u, dt_bondi_w, dt_bondi_j};\n}\n\ntuples::TaggedTuple<Tags::Dr<Tags::BondiBeta>, Tags::Dr<Tags::BondiU>,\n                    Tags::Dr<Tags::BondiW>, Tags::Dr<Tags::BondiJ>>\nextract_dr_bondi_scalars_from_cartesian_metric(\n    const tnsr::aa<DataVector, 3>& dr_spacetime_metric,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const CartesianiSphericalJ& inverse_jacobian,\n    const CartesianiSphericalJ& dr_inverse_jacobian,\n    const double extraction_radius) {\n  const auto inverse_cartesian_metric =\n      determinant_and_inverse(spacetime_metric).second;\n  tnsr::AA<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n      dr_inverse_cartesian_metric{get<0, 0>(spacetime_metric).size(), 0.0};\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      for (size_t c = 0; c < 4; ++c) {\n        for (size_t d = 0; d < 4; ++d) {\n          dr_inverse_cartesian_metric.get(a, b) +=\n              -inverse_cartesian_metric.get(a, c) *\n              inverse_cartesian_metric.get(b, d) *\n              dr_spacetime_metric.get(c, d);\n        }\n      }\n    }\n  }\n\n  tnsr::AA<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n      inverse_spherical_metric{get<0, 0>(spacetime_metric).size(), 0.0};\n  tnsr::AA<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n      dr_inverse_spherical_metric{get<0, 0>(spacetime_metric).size(), 0.0};\n  get<0, 0>(inverse_spherical_metric) = get<0, 0>(inverse_cartesian_metric);\n  get<0, 0>(dr_inverse_spherical_metric) =\n      get<0, 0>(dr_inverse_cartesian_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t k = 0; k < 3; ++k) {\n      inverse_spherical_metric.get(0, i + 1) +=\n          inverse_jacobian.get(k, i) * inverse_cartesian_metric.get(0, k + 1);\n      dr_inverse_spherical_metric.get(0, i + 1) +=\n          inverse_jacobian.get(k, i) *\n              dr_inverse_cartesian_metric.get(0, k + 1) +\n          dr_inverse_jacobian.get(k, i) *\n              inverse_cartesian_metric.get(0, k + 1);\n    }\n    for (size_t j = i; j < 3; ++j) {\n      for (size_t k = 0; k < 3; ++k) {\n        for (size_t l = 0; l < 3; ++l) {\n          inverse_spherical_metric.get(i + 1, j + 1) +=\n              inverse_jacobian.get(k, i) * inverse_jacobian.get(l, j) *\n              inverse_cartesian_metric.get(k + 1, l + 1);\n          dr_inverse_spherical_metric.get(i + 1, j + 1) +=\n              inverse_jacobian.get(k, i) * inverse_jacobian.get(l, j) *\n                  dr_inverse_cartesian_metric.get(k + 1, l + 1) +\n              dr_inverse_jacobian.get(k, i) * inverse_jacobian.get(l, j) *\n                  inverse_cartesian_metric.get(k + 1, l + 1) +\n              inverse_jacobian.get(k, i) * dr_inverse_jacobian.get(l, j) *\n                  inverse_cartesian_metric.get(k + 1, l + 1);\n        }\n      }\n    }\n  }\n\n  get<0, 0>(inverse_spherical_metric) +=\n      -2.0 * get<0, 1>(inverse_spherical_metric) +\n      get<1, 1>(inverse_spherical_metric);\n  get<0, 0>(dr_inverse_spherical_metric) +=\n      -2.0 * get<0, 1>(dr_inverse_spherical_metric) +\n      get<1, 1>(dr_inverse_spherical_metric);\n  for (size_t i = 0; i < 3; ++i) {\n    inverse_spherical_metric.get(0, i) -= inverse_spherical_metric.get(1, i);\n    dr_inverse_spherical_metric.get(0, i) -=\n        dr_inverse_spherical_metric.get(1, i);\n  }\n\n  // The formulas for these scalars can be determined by differentiating the\n  // forms in `extract_bondi_scalars_from_cartesian_metric`\n  Scalar<SpinWeighted<ComplexDataVector, 0>> dr_bondi_beta{\n      get<0, 0>(spacetime_metric).size()};\n  get(dr_bondi_beta) = -std::complex<double>(0.5, 0.0) *\n                       get<0, 1>(dr_inverse_spherical_metric) /\n                       get<0, 1>(inverse_spherical_metric);\n  Scalar<SpinWeighted<ComplexDataVector, 0>> dr_bondi_w{\n      get<0, 0>(spacetime_metric).size()};\n  get(dr_bondi_w) = std::complex<double>(1.0, 0.0) *\n                    ((-get<1, 1>(dr_inverse_spherical_metric) /\n                          get<0, 1>(inverse_spherical_metric) +\n                      get<1, 1>(inverse_spherical_metric) *\n                          get<0, 1>(dr_inverse_spherical_metric) /\n                          square(get<0, 1>(inverse_spherical_metric))) /\n                         extraction_radius +\n                     (get<1, 1>(inverse_spherical_metric) /\n                          get<0, 1>(inverse_spherical_metric) +\n                      1.0) /\n                         square(extraction_radius));\n  Scalar<SpinWeighted<ComplexDataVector, 1>> dr_bondi_u{\n      get<0, 0>(spacetime_metric).size()};\n  // note that the jacobian components are provided in 'pfaffian' form - so the\n  // extra factors of sin(theta) are omitted.\n  get(dr_bondi_u) =\n      -(std::complex<double>(1.0, 0.0) *\n            get<1, 2>(dr_inverse_spherical_metric) +\n        std::complex<double>(0.0, 1.0) *\n            get<1, 3>(dr_inverse_spherical_metric)) /\n          get<0, 1>(inverse_spherical_metric) +\n      (std::complex<double>(1.0, 0.0) * get<1, 2>(inverse_spherical_metric) +\n       std::complex<double>(0.0, 1.0) * get<1, 3>(inverse_spherical_metric)) *\n          get<0, 1>(dr_inverse_spherical_metric) /\n          square(get<0, 1>(inverse_spherical_metric));\n  Scalar<SpinWeighted<ComplexDataVector, 2>> dr_bondi_j{\n      get<0, 0>(spacetime_metric).size()};\n  get(dr_bondi_j) =\n      -0.5 * square(extraction_radius) *\n          (get<2, 2>(dr_inverse_spherical_metric) -\n           get<3, 3>(dr_inverse_spherical_metric) +\n           std::complex<double>(0.0, 2.0) *\n               (get<2, 3>(dr_inverse_spherical_metric))) +\n      -extraction_radius * (get<2, 2>(inverse_spherical_metric) -\n                            get<3, 3>(inverse_spherical_metric) +\n                            std::complex<double>(0.0, 2.0) *\n                                (get<2, 3>(inverse_spherical_metric)));\n  return {dr_bondi_beta, dr_bondi_u, dr_bondi_w, dr_bondi_j};\n}\n\nvoid test_initialize_j(const size_t l_max, const size_t number_of_radial_points,\n                       const double extraction_radius, const double time,\n                       const std::unique_ptr<InitializeJ::InitializeJ<false>>\n                           expected_initialize_j,\n                       const std::unique_ptr<WorldtubeData> analytic_solution) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  Variables<Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>\n      boundary_variables{number_of_angular_points};\n  AnalyticBoundaryDataManager analytic_manager{l_max, extraction_radius,\n                                               analytic_solution->get_clone()};\n  analytic_manager.populate_hypersurface_boundary_data(\n      make_not_null(&boundary_variables), time);\n  Scalar<SpinWeighted<ComplexDataVector, 2>> expected_j{\n      number_of_angular_points * number_of_radial_points};\n  Scalar<SpinWeighted<ComplexDataVector, 2>> test_j{number_of_angular_points *\n                                                    number_of_radial_points};\n  tnsr::i<DataVector, 3> expected_cartesian_coordinates{\n      number_of_angular_points};\n  tnsr::i<DataVector, 3> test_cartesian_coordinates{number_of_angular_points};\n  tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>\n      expected_angular_coordinates{number_of_angular_points};\n  tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>\n      test_angular_coordinates{number_of_angular_points};\n  auto node_lock = Parallel::NodeLock{};\n  (*(analytic_solution->get_initialize_j(time)))(\n      make_not_null(&test_j), make_not_null(&test_cartesian_coordinates),\n      make_not_null(&test_angular_coordinates),\n      get<Tags::BoundaryValue<Tags::BondiJ>>(boundary_variables),\n      get<Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>>(boundary_variables),\n      get<Tags::BoundaryValue<Tags::BondiR>>(boundary_variables),\n      get<Tags::BoundaryValue<Tags::BondiBeta>>(boundary_variables), l_max,\n      number_of_radial_points, make_not_null(&node_lock));\n  (*expected_initialize_j)(\n      make_not_null(&expected_j),\n      make_not_null(&expected_cartesian_coordinates),\n      make_not_null(&expected_angular_coordinates),\n      get<Tags::BoundaryValue<Tags::BondiJ>>(boundary_variables),\n      get<Tags::BoundaryValue<Tags::Dr<Tags::BondiJ>>>(boundary_variables),\n      get<Tags::BoundaryValue<Tags::BondiR>>(boundary_variables),\n      get<Tags::BoundaryValue<Tags::BondiBeta>>(boundary_variables), l_max,\n      number_of_radial_points, make_not_null(&node_lock));\n  CHECK(test_j == expected_j);\n  CHECK(test_cartesian_coordinates == expected_cartesian_coordinates);\n  CHECK(test_angular_coordinates == expected_angular_coordinates);\n}\n}  // namespace Cce::Solutions::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/AnalyticSolutions/AnalyticDataHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Cce/AnalyticSolutions/SphericalMetricData.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Cce {\nnamespace Solutions {\nnamespace TestHelpers {\n\ntemplate <typename SphericalSolution>\nstruct SphericalSolutionWrapper : public SphericalSolution {\n  using taglist =\n      tmpl::list<gr::Tags::SpacetimeMetric<\n                     DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>,\n                 Tags::Dr<gr::Tags::SpacetimeMetric<\n                     DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>,\n                 ::Tags::dt<gr::Tags::SpacetimeMetric<\n                     DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>,\n                 Tags::News>;\n  using SphericalSolution::SphericalSolution;\n\n  template <typename... Args>\n  void test_spherical_metric(const std::string python_file, const size_t l_max,\n                             const double time, Approx custom_approx,\n                             const Args... args) const {\n    const size_t size =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n    Scalar<DataVector> sin_theta{size};\n    Scalar<DataVector> cos_theta{size};\n    const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n        Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n    for (const auto collocation_point : collocation) {\n      get(sin_theta)[collocation_point.offset] = sin(collocation_point.theta);\n      get(cos_theta)[collocation_point.offset] = cos(collocation_point.theta);\n    }\n    tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n        local_spherical_metric{size};\n    tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n        local_dr_spherical_metric{size};\n    tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n        local_dt_spherical_metric{size};\n    Scalar<SpinWeighted<ComplexDataVector, -2>> local_news{size};\n\n    this->spherical_metric(make_not_null(&local_spherical_metric), l_max, time);\n    this->dr_spherical_metric(make_not_null(&local_dr_spherical_metric), l_max,\n                              time);\n    this->dt_spherical_metric(make_not_null(&local_dt_spherical_metric), l_max,\n                              time);\n    this->variables_impl(make_not_null(&local_news), l_max, time,\n                         tmpl::type_<Tags::News>{});\n\n    // Pypp call expects all of the objects to be the same category -- here we\n    // need to use tensors, so we must pack up the `double` arguments into\n    // tensors.\n    Scalar<DataVector> time_vector;\n    get(time_vector) = DataVector{size, time};\n\n    const auto py_spherical_metric = pypp::call<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>(\n        python_file, \"spherical_metric\", sin_theta, cos_theta, time_vector,\n        Scalar<DataVector>{DataVector{size, args}}...);\n    const auto py_dt_spherical_metric = pypp::call<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>(\n        python_file, \"dt_spherical_metric\", sin_theta, cos_theta, time_vector,\n        Scalar<DataVector>{DataVector{size, args}}...);\n    const auto py_dr_spherical_metric = pypp::call<\n        tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>>(\n        python_file, \"dr_spherical_metric\", sin_theta, cos_theta, time_vector,\n        Scalar<DataVector>{DataVector{size, args}}...);\n    const auto py_news = pypp::call<Scalar<SpinWeighted<ComplexDataVector, 2>>>(\n        python_file, \"news\", sin_theta, time_vector,\n        Scalar<DataVector>{DataVector{size, args}}...);\n\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t b = a; b < 4; ++b) {\n        CAPTURE(a);\n        CAPTURE(b);\n        const auto& lhs = local_spherical_metric.get(a, b);\n        const auto& rhs = py_spherical_metric.get(a, b);\n        CHECK_ITERABLE_CUSTOM_APPROX(lhs, rhs, custom_approx);\n        const auto& dt_lhs = local_dt_spherical_metric.get(a, b);\n        const auto& dt_rhs = py_dt_spherical_metric.get(a, b);\n        CHECK_ITERABLE_CUSTOM_APPROX(dt_lhs, dt_rhs, custom_approx);\n        const auto& dr_lhs = local_dr_spherical_metric.get(a, b);\n        const auto& dr_rhs = py_dr_spherical_metric.get(a, b);\n        CHECK_ITERABLE_CUSTOM_APPROX(dr_lhs, dr_rhs, custom_approx);\n      }\n    }\n  }\n\n  void test_serialize_and_deserialize(const size_t l_max, const double time) {\n    const size_t size =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n    tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n        expected_spherical_metric{size};\n    this->spherical_metric(make_not_null(&expected_spherical_metric), l_max,\n                           time);\n    auto serialized_and_deserialized_solution =\n        serialize_and_deserialize(*this);\n    tnsr::aa<DataVector, 3, ::Frame::Spherical<::Frame::Inertial>>\n        local_spherical_metric{size};\n    serialized_and_deserialized_solution.spherical_metric(\n        make_not_null(&local_spherical_metric), l_max, time);\n    CHECK(expected_spherical_metric == local_spherical_metric);\n  }\n\n protected:\n  using SphericalSolution::extraction_radius_;\n};\n\n// Test the `InitializeJ` object produced by the `WorldtubeData` against an\n// expected `InitializeJ` object also provided in the arguments.\nvoid test_initialize_j(\n    size_t l_max, size_t number_of_radial_points, double extraction_radius,\n    double time,\n    std::unique_ptr<InitializeJ::InitializeJ<false>> expected_initialize_j,\n    std::unique_ptr<WorldtubeData> analytic_solution);\n\n// This function determines the Bondi-Sachs scalars from a Cartesian spacetime\n// metric, assuming that the metric is already in null form, so the spatial\n// coordinates are related to standard Bondi-Sachs coordinates by just the\n// standard Cartesian to spherical Jacobian.\ntuples::TaggedTuple<Tags::BondiBeta, Tags::BondiU, Tags::BondiW, Tags::BondiJ>\nextract_bondi_scalars_from_cartesian_metric(\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const CartesianiSphericalJ& inverse_jacobian, double extraction_radius);\n\n// This function determines the time derivative of the Bondi-Sachs scalars\n// from the time derivative of a Cartesian spacetime metric, the Cartesian\n// metric, and Jacobian factors. This procedure assumes that the metric is\n// already in null form, so the spatial coordinates are related to standard\n// Bondi-Sachs coordinates by just the standard cartesian to spherical Jacobian.\ntuples::TaggedTuple<::Tags::dt<Tags::BondiBeta>, ::Tags::dt<Tags::BondiU>,\n                    ::Tags::dt<Tags::BondiW>, ::Tags::dt<Tags::BondiJ>>\nextract_dt_bondi_scalars_from_cartesian_metric(\n    const tnsr::aa<DataVector, 3>& dt_spacetime_metric,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const CartesianiSphericalJ& inverse_jacobian, double extraction_radius);\n\n// This function determines the radial derivative of the Bondi-Sachs scalars\n// from the radial derivative of a Cartesian spacetime metric, the Cartesian\n// metric, and Jacobian factors. This procedure assumes that the metric is\n// already in null form, so the spatial coordinates are related to standard\n// Bondi-Sachs coordinates by just the standard cartesian to spherical Jacobian.\ntuples::TaggedTuple<Tags::Dr<Tags::BondiBeta>, Tags::Dr<Tags::BondiU>,\n                    Tags::Dr<Tags::BondiW>, Tags::Dr<Tags::BondiJ>>\nextract_dr_bondi_scalars_from_cartesian_metric(\n    const tnsr::aa<DataVector, 3>& dr_spacetime_metric,\n    const tnsr::aa<DataVector, 3>& spacetime_metric,\n    const CartesianiSphericalJ& inverse_jacobian,\n    const CartesianiSphericalJ& dr_inverse_jacobian, double extraction_radius);\n\n// This function checks the consistency of the 3+1 ADM quantities in the\n// `boundary_tuple` with quantities computed from `expected_spacetime_metric`,\n// `expected_dt_spacetime_metric`, and `expected_d_spacetime_metric`. If the\n// expected quantities are also extracted from the tuple, this simply checks\n// that the boundary computation has produced a consistent set of quantities\n// and has not generated NaNs or other pathological values (e.g. a degenerate\n// spacetime metric) in the process.\ntemplate <typename... TupleTags>\nvoid check_adm_metric_quantities(\n    const tuples::TaggedTuple<TupleTags...>& boundary_tuple,\n    const tnsr::aa<DataVector, 3>& expected_spacetime_metric,\n    const tnsr::aa<DataVector, 3>& expected_dt_spacetime_metric,\n    const tnsr::iaa<DataVector, 3>& expected_d_spacetime_metric) {\n  // check the 3+1 quantities are computed correctly in the abstract base class\n  // `WorldtubeData`\n  const auto& dr_cartesian_coordinates =\n      get<Tags::Dr<Tags::CauchyCartesianCoords>>(boundary_tuple);\n\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(boundary_tuple);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(boundary_tuple);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(boundary_tuple);\n\n  const auto expected_spatial_metric =\n      gr::spatial_metric(expected_spacetime_metric);\n  const auto expected_inverse_spatial_metric =\n      determinant_and_inverse(expected_spatial_metric).second;\n  const auto expected_shift =\n      gr::shift(expected_spacetime_metric, expected_inverse_spatial_metric);\n  const auto expected_lapse =\n      gr::lapse(expected_shift, expected_spacetime_metric);\n  CHECK_ITERABLE_APPROX(spatial_metric, expected_spatial_metric);\n  CHECK_ITERABLE_APPROX(shift, expected_shift);\n  CHECK_ITERABLE_APPROX(lapse, expected_lapse);\n\n  const auto& pi = get<gh::Tags::Pi<DataVector, 3>>(boundary_tuple);\n  const auto dt_spacetime_metric_from_pi =\n      gh::time_derivative_of_spacetime_metric(expected_lapse, expected_shift,\n                                              pi, expected_d_spacetime_metric);\n  CHECK_ITERABLE_APPROX(expected_dt_spacetime_metric,\n                        dt_spacetime_metric_from_pi);\n  // Check that the time derivative values are consistent with the Generalized\n  // Harmonic `pi` -- these are redundant if the boundary calculation uses `pi`\n  // to derive these, but it is not required to do so.\n  const auto& dt_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(boundary_tuple);\n  const auto& dt_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(boundary_tuple);\n  const auto& dt_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(boundary_tuple);\n  const auto expected_dt_spatial_metric = gh::time_deriv_of_spatial_metric(\n      expected_lapse, expected_shift, expected_d_spacetime_metric, pi);\n  const auto expected_spacetime_unit_normal =\n      gr::spacetime_normal_vector(expected_lapse, expected_shift);\n  const auto expected_dt_lapse = gh::time_deriv_of_lapse(\n      expected_lapse, expected_shift, expected_spacetime_unit_normal,\n      expected_d_spacetime_metric, pi);\n  const auto expected_dt_shift = gh::time_deriv_of_shift(\n      expected_lapse, expected_shift, expected_inverse_spatial_metric,\n      expected_spacetime_unit_normal, expected_d_spacetime_metric, pi);\n  CHECK_ITERABLE_APPROX(dt_lapse, expected_dt_lapse);\n  CHECK_ITERABLE_APPROX(dt_shift, expected_dt_shift);\n  CHECK_ITERABLE_APPROX(dt_spatial_metric, expected_dt_spatial_metric);\n\n  const auto& dr_lapse =\n      get<Tags::Dr<gr::Tags::Lapse<DataVector>>>(boundary_tuple);\n  const auto& dr_shift =\n      get<Tags::Dr<gr::Tags::Shift<DataVector, 3>>>(boundary_tuple);\n  const auto& dr_spatial_metric =\n      get<Tags::Dr<gr::Tags::SpatialMetric<DataVector, 3>>>(boundary_tuple);\n  const auto expected_spatial_derivative_of_lapse =\n      gh::spatial_deriv_of_lapse(expected_lapse, expected_spacetime_unit_normal,\n                                 expected_d_spacetime_metric);\n  const auto expected_inverse_spacetime_metric = gr::inverse_spacetime_metric(\n      expected_lapse, expected_shift, expected_inverse_spatial_metric);\n  const auto expected_spatial_derivative_of_shift = gh::spatial_deriv_of_shift(\n      expected_lapse, expected_inverse_spacetime_metric,\n      expected_spacetime_unit_normal, expected_d_spacetime_metric);\n  DataVector expected_buffer =\n      get<0>(dr_cartesian_coordinates) *\n          get<0>(expected_spatial_derivative_of_lapse) +\n      get<1>(dr_cartesian_coordinates) *\n          get<1>(expected_spatial_derivative_of_lapse) +\n      get<2>(dr_cartesian_coordinates) *\n          get<2>(expected_spatial_derivative_of_lapse);\n  CHECK_ITERABLE_APPROX(expected_buffer, get(dr_lapse));\n  for (size_t i = 0; i < 3; ++i) {\n    expected_buffer = get<0>(dr_cartesian_coordinates) *\n                          expected_spatial_derivative_of_shift.get(0, i) +\n                      get<1>(dr_cartesian_coordinates) *\n                          expected_spatial_derivative_of_shift.get(1, i) +\n                      get<2>(dr_cartesian_coordinates) *\n                          expected_spatial_derivative_of_shift.get(2, i);\n    CHECK_ITERABLE_APPROX(expected_buffer, dr_shift.get(i));\n    for (size_t j = i; j < 3; ++j) {\n      expected_buffer = get<0>(dr_cartesian_coordinates) *\n                            expected_d_spacetime_metric.get(0, i + 1, j + 1) +\n                        get<1>(dr_cartesian_coordinates) *\n                            expected_d_spacetime_metric.get(1, i + 1, j + 1) +\n                        get<2>(dr_cartesian_coordinates) *\n                            expected_d_spacetime_metric.get(2, i + 1, j + 1);\n      CHECK_ITERABLE_APPROX(expected_buffer, dr_spatial_metric.get(i, j));\n    }\n  }\n}\n\n}  // namespace TestHelpers\n}  // namespace Solutions\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/AnalyticSolutions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"CceAnalyticSolutionsHelpers\")\n\nset(LIBRARY_SOURCES\n  AnalyticDataHelpers.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Cce\n  DataStructures\n  Framework\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <boost/iterator/zip_iterator.hpp>\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp\"\n#include \"Evolution/Systems/Cce/WorldtubeModeRecorder.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/WriteToWorldtubeH5.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::TestHelpers {\ntemplate <typename... Structure>\nTensor<ComplexModalVector, Structure...> tensor_to_goldberg_coefficients(\n    const Tensor<DataVector, Structure...>& nodal_data, size_t l_max) {\n  Tensor<ComplexModalVector, Structure...> goldberg_modal_data{\n      square(l_max + 1)};\n  SpinWeighted<ComplexDataVector, 0> transform_buffer{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  for (size_t i = 0; i < nodal_data.size(); ++i) {\n    transform_buffer.data() = std::complex<double>(1.0, 0.0) * nodal_data[i];\n    goldberg_modal_data[i] =\n        Spectral::Swsh::libsharp_to_goldberg_modes(\n            Spectral::Swsh::swsh_transform(l_max, 1, transform_buffer), l_max)\n            .data();\n  }\n  return goldberg_modal_data;\n}\n\ntemplate <typename... Structure>\nTensor<ComplexModalVector, Structure...> tensor_to_libsharp_coefficients(\n    const Tensor<DataVector, Structure...>& nodal_data,\n    const size_t l_max)  // NOLINT(readability-avoid-const-params-in-decls)\n{\n  Tensor<ComplexModalVector, Structure...> libsharp_modal_data{\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max)};\n  SpinWeighted<ComplexDataVector, 0> transform_buffer{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  for (size_t i = 0; i < nodal_data.size(); ++i) {\n    transform_buffer.data() = std::complex<double>(1.0, 0.0) * nodal_data[i];\n    libsharp_modal_data[i] =\n        Spectral::Swsh::swsh_transform(l_max, 1, transform_buffer).data();\n  }\n  return libsharp_modal_data;\n}\n\ntemplate <typename AnalyticSolution>\nvoid create_fake_time_varying_gh_nodal_data(\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> spacetime_metric,\n    const gsl::not_null<tnsr::iaa<DataVector, 3>*> phi,\n    const gsl::not_null<tnsr::aa<DataVector, 3>*> pi,\n    const AnalyticSolution& solution, const double extraction_radius,\n    const double amplitude, const double frequency, const double time,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  // create the vector of collocation points that we want to interpolate to\n\n  tnsr::I<DataVector, 3> collocation_points{number_of_angular_points};\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get<0>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        sin(collocation_point.theta) * cos(collocation_point.phi);\n    get<1>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        sin(collocation_point.theta) * sin(collocation_point.phi);\n    get<2>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        cos(collocation_point.theta);\n  }\n\n  const auto kerr_schild_variables = solution.variables(\n      collocation_points, 0.0, gr::Solutions::KerrSchild::tags<DataVector>{});\n\n  const Scalar<DataVector>& lapse =\n      get<gr::Tags::Lapse<DataVector>>(kerr_schild_variables);\n  const Scalar<DataVector>& dt_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(kerr_schild_variables);\n  const auto& d_lapse = get<gr::Solutions::KerrSchild::DerivLapse<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(kerr_schild_variables);\n  const auto& d_shift = get<gr::Solutions::KerrSchild::DerivShift<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n          kerr_schild_variables);\n  const auto& d_spatial_metric =\n      get<gr::Solutions::KerrSchild::DerivSpatialMetric<DataVector>>(\n          kerr_schild_variables);\n\n  gr::spacetime_metric(spacetime_metric, lapse, shift, spatial_metric);\n  gh::phi(phi, lapse, d_lapse, shift, d_shift, spatial_metric,\n          d_spatial_metric);\n  gh::pi(pi, lapse, dt_lapse, shift, dt_shift, spatial_metric,\n         dt_spatial_metric, *phi);\n}\n\n// Overload for all quantities, including Adm and Z4c quantities\ntemplate <typename AnalyticSolution, typename T = ComplexModalVector>\nvoid create_fake_time_varying_data(\n    const gsl::not_null<tnsr::ii<T, 3>*> spatial_metric_coefficients,\n    const gsl::not_null<tnsr::ii<T, 3>*> dt_spatial_metric_coefficients,\n    const gsl::not_null<tnsr::ii<T, 3>*> dr_spatial_metric_coefficients,\n    const gsl::not_null<tnsr::I<T, 3>*> shift_coefficients,\n    const gsl::not_null<tnsr::I<T, 3>*> dt_shift_coefficients,\n    const gsl::not_null<tnsr::I<T, 3>*> dr_shift_coefficients,\n    const gsl::not_null<Scalar<T>*> lapse_coefficients,\n    const gsl::not_null<Scalar<T>*> dt_lapse_coefficients,\n    const gsl::not_null<Scalar<T>*> dr_lapse_coefficients,\n    const gsl::not_null<tnsr::ii<T, 3>*> extrinsic_curvature_coefficients,\n    const gsl::not_null<tnsr::I<T, 3>*> auxiliary_shift_coefficients,\n    const gsl::not_null<tnsr::I<T, 3>*> conformal_christoffel_coefficients,\n    const gsl::not_null<tnsr::ijj<T, 3>*> deriv_spatial_metric_coefficients,\n    const gsl::not_null<tnsr::iJ<T, 3>*> deriv_shift_coefficients,\n    const gsl::not_null<tnsr::i<T, 3>*> deriv_lapse_coefficients,\n    const AnalyticSolution& solution, const double extraction_radius,\n    const double amplitude, const double frequency, const double time,\n    const size_t l_max, const bool convert_to_goldberg = true,\n    const bool apply_normalization_bug = false) {\n  static_assert(std::is_same_v<T, ComplexModalVector> or\n                std::is_same_v<T, DataVector>);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  // create the vector of collocation points that we want to interpolate to\n\n  tnsr::I<DataVector, 3> collocation_points{number_of_angular_points};\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get<0>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        sin(collocation_point.theta) * cos(collocation_point.phi);\n    get<1>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        sin(collocation_point.theta) * sin(collocation_point.phi);\n    get<2>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        cos(collocation_point.theta);\n  }\n\n  const auto kerr_schild_variables = solution.variables(\n      collocation_points, 0.0, gr::Solutions::KerrSchild::tags<DataVector>{});\n\n  const Scalar<DataVector>& lapse =\n      get<gr::Tags::Lapse<DataVector>>(kerr_schild_variables);\n  const Scalar<DataVector>& dt_lapse =\n      get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(kerr_schild_variables);\n  const auto& d_lapse = get<gr::Solutions::KerrSchild::DerivLapse<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_shift =\n      get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(kerr_schild_variables);\n  const auto& d_shift = get<gr::Solutions::KerrSchild::DerivShift<DataVector>>(\n      kerr_schild_variables);\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(kerr_schild_variables);\n  const auto& dt_spatial_metric =\n      get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n          kerr_schild_variables);\n  const auto& d_spatial_metric =\n      get<gr::Solutions::KerrSchild::DerivSpatialMetric<DataVector>>(\n          kerr_schild_variables);\n\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(kerr_schild_variables);\n  const auto auxiliary_shift = [&shift]() {\n    auto result = shift;\n    get<0>(result) = 0.5;\n    get<1>(result) = 0.5;\n    get<2>(result) = 0.5;\n    return result;\n  }();\n  // The value in the KerrSchild vars isn't conformal, but this is just a test\n  // so we don't care\n  const auto& conformal_christoffel =\n      get<gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>(\n          kerr_schild_variables);\n\n  DataVector normalization_factor{number_of_angular_points, 1.0};\n  if (apply_normalization_bug) {\n    normalization_factor = 0.0;\n    const auto inverse_spatial_metric =\n        determinant_and_inverse(spatial_metric).second;\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        normalization_factor +=\n            inverse_spatial_metric.get(i, j) * collocation_points.get(i) *\n            collocation_points.get(j) /\n            square(extraction_radius *\n                   (1.0 + amplitude * sin(frequency * time)));\n      }\n    }\n    normalization_factor = sqrt(normalization_factor);\n  }\n\n  Scalar<DataVector> dr_lapse{number_of_angular_points};\n  get(dr_lapse) = (get<0>(collocation_points) * get<0>(d_lapse) +\n                   get<1>(collocation_points) * get<1>(d_lapse) +\n                   get<2>(collocation_points) * get<2>(d_lapse)) /\n                  (extraction_radius * normalization_factor);\n  tnsr::I<DataVector, 3> dr_shift{number_of_angular_points};\n  for (size_t i = 0; i < 3; ++i) {\n    dr_shift.get(i) = (get<0>(collocation_points) * d_shift.get(0, i) +\n                       get<1>(collocation_points) * d_shift.get(1, i) +\n                       get<2>(collocation_points) * d_shift.get(2, i)) /\n                      (extraction_radius * normalization_factor);\n  }\n  tnsr::ii<DataVector, 3> dr_spatial_metric{number_of_angular_points};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      dr_spatial_metric.get(i, j) =\n          (get<0>(collocation_points) * d_spatial_metric.get(0, i, j) +\n           get<1>(collocation_points) * d_spatial_metric.get(1, i, j) +\n           get<2>(collocation_points) * d_spatial_metric.get(2, i, j)) /\n          (extraction_radius * normalization_factor);\n    }\n  }\n\n  if constexpr (std::is_same_v<T, ComplexModalVector>) {\n    if (convert_to_goldberg) {\n      *lapse_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(lapse, l_max);\n      *dt_lapse_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(dt_lapse, l_max);\n      *dr_lapse_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(dr_lapse, l_max);\n\n      *shift_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(shift, l_max);\n      *dt_shift_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(dt_shift, l_max);\n      *dr_shift_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(dr_shift, l_max);\n\n      *spatial_metric_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(spatial_metric, l_max);\n      *dt_spatial_metric_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(dt_spatial_metric,\n                                                       l_max);\n      *dr_spatial_metric_coefficients =\n          TestHelpers::tensor_to_goldberg_coefficients(dr_spatial_metric,\n                                                       l_max);\n    } else {\n      *lapse_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(lapse, l_max);\n      *dt_lapse_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(dt_lapse, l_max);\n      *dr_lapse_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(dr_lapse, l_max);\n\n      *shift_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(shift, l_max);\n      *dt_shift_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(dt_shift, l_max);\n      *dr_shift_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(dr_shift, l_max);\n\n      *spatial_metric_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(spatial_metric, l_max);\n      *dt_spatial_metric_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(dt_spatial_metric,\n                                                       l_max);\n      *dr_spatial_metric_coefficients =\n          TestHelpers::tensor_to_libsharp_coefficients(dr_spatial_metric,\n                                                       l_max);\n    }\n    (void)extrinsic_curvature_coefficients;\n    (void)auxiliary_shift_coefficients;\n    (void)conformal_christoffel_coefficients;\n    (void)deriv_spatial_metric_coefficients;\n    (void)deriv_shift_coefficients;\n    (void)deriv_lapse_coefficients;\n  } else {\n    get(*lapse_coefficients) = get(lapse);\n    get(*dt_lapse_coefficients) = get(dt_lapse);\n    get(*dr_lapse_coefficients) = get(dr_lapse);\n\n    for (size_t i = 0; i < 3; i++) {\n      shift_coefficients->get(i) = shift.get(i);\n      dt_shift_coefficients->get(i) = dt_shift.get(i);\n      dr_shift_coefficients->get(i) = dr_shift.get(i);\n      auxiliary_shift_coefficients->get(i) = auxiliary_shift.get(i);\n      conformal_christoffel_coefficients->get(i) = conformal_christoffel.get(i);\n      deriv_lapse_coefficients->get(i) = d_lapse.get(i);\n\n      for (size_t j = 0; j < 3; j++) {\n        deriv_shift_coefficients->get(i, j) = d_shift.get(i, j);\n        if (j < i) {\n          continue;\n        }\n        spatial_metric_coefficients->get(i, j) = spatial_metric.get(i, j);\n        dt_spatial_metric_coefficients->get(i, j) = dt_spatial_metric.get(i, j);\n        dr_spatial_metric_coefficients->get(i, j) = dr_spatial_metric.get(i, j);\n        extrinsic_curvature_coefficients->get(i, j) =\n            extrinsic_curvature.get(i, j);\n\n        for (size_t k = 0; k < 3; k++) {\n          deriv_spatial_metric_coefficients->get(k, i, j) =\n              d_spatial_metric.get(k, i, j);\n        }\n      }\n    }\n  }\n}\n\n// Overload for only the strictly necessary quantities (time and radial derivs)\ntemplate <typename AnalyticSolution, typename T = ComplexModalVector>\nvoid create_fake_time_varying_data(\n    const gsl::not_null<tnsr::ii<T, 3>*> spatial_metric_coefficients,\n    const gsl::not_null<tnsr::ii<T, 3>*> dt_spatial_metric_coefficients,\n    const gsl::not_null<tnsr::ii<T, 3>*> dr_spatial_metric_coefficients,\n    const gsl::not_null<tnsr::I<T, 3>*> shift_coefficients,\n    const gsl::not_null<tnsr::I<T, 3>*> dt_shift_coefficients,\n    const gsl::not_null<tnsr::I<T, 3>*> dr_shift_coefficients,\n    const gsl::not_null<Scalar<T>*> lapse_coefficients,\n    const gsl::not_null<Scalar<T>*> dt_lapse_coefficients,\n    const gsl::not_null<Scalar<T>*> dr_lapse_coefficients,\n    const AnalyticSolution& solution, const double extraction_radius,\n    const double amplitude, const double frequency, const double time,\n    const size_t l_max, const bool convert_to_goldberg = true,\n    const bool apply_normalization_bug = false) {\n  tnsr::ii<T, 3> extrinsic_curvature_coefficients{};\n  tnsr::I<T, 3> auxiliary_shift_coefficients{};\n  tnsr::I<T, 3> conformal_christoffel_coefficients{};\n  tnsr::ijj<T, 3> deriv_spatial_metric_coefficients{};\n  tnsr::iJ<T, 3> deriv_shift_coefficients{};\n  tnsr::i<T, 3> deriv_lapse_coefficients{};\n  create_fake_time_varying_data(\n      spatial_metric_coefficients, dt_spatial_metric_coefficients,\n      dr_spatial_metric_coefficients, shift_coefficients, dt_shift_coefficients,\n      dr_shift_coefficients, lapse_coefficients, dt_lapse_coefficients,\n      dr_lapse_coefficients, make_not_null(&extrinsic_curvature_coefficients),\n      make_not_null(&auxiliary_shift_coefficients),\n      make_not_null(&conformal_christoffel_coefficients),\n      make_not_null(&deriv_spatial_metric_coefficients),\n      make_not_null(&deriv_shift_coefficients),\n      make_not_null(&deriv_lapse_coefficients), solution, extraction_radius,\n      amplitude, frequency, time, l_max, convert_to_goldberg,\n      apply_normalization_bug);\n}\n\ntemplate <typename T = ComplexModalVector, bool WriteBondi = true,\n          typename AnalyticSolution>\nvoid write_test_file(const AnalyticSolution& solution,\n                     const std::string& filename, const double target_time,\n                     const double extraction_radius, const double frequency,\n                     const double amplitude, const size_t l_max,\n                     const bool descending_m = true,\n                     const bool write_extra_adm_vars = false) {\n  const bool is_modal = std::is_same_v<T, ComplexModalVector>;\n  const size_t size =\n      is_modal ? square(l_max + 1)\n               : Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  (void)number_of_angular_points;\n  tnsr::ii<T, 3> spatial_metric_coefficients{size};\n  tnsr::ii<T, 3> dt_spatial_metric_coefficients{size};\n  tnsr::ii<T, 3> dr_spatial_metric_coefficients{size};\n  tnsr::I<T, 3> shift_coefficients{size};\n  tnsr::I<T, 3> dt_shift_coefficients{size};\n  tnsr::I<T, 3> dr_shift_coefficients{size};\n  Scalar<T> lapse_coefficients{size};\n  Scalar<T> dt_lapse_coefficients{size};\n  Scalar<T> dr_lapse_coefficients{size};\n  tnsr::ii<T, 3> extrinsic_curvature_coefficients{size};\n  tnsr::I<T, 3> auxiliary_shift_coefficients{size};\n  tnsr::I<T, 3> conformal_christoffel_coefficients{size};\n  tnsr::ijj<T, 3> deriv_spatial_metric_coefficients{size};\n  tnsr::iJ<T, 3> deriv_shift_coefficients{size};\n  tnsr::i<T, 3> deriv_lapse_coefficients{size};\n  Variables<Cce::Tags::characteristic_worldtube_boundary_tags<\n      Cce::Tags::BoundaryValue>>\n      boundary_data_variables{};\n  if constexpr (WriteBondi) {\n    boundary_data_variables.initialize(number_of_angular_points);\n  }\n\n  // write times to file for several steps before and after the target time\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n  // scoped to close the file\n  {\n    using RecorderType =\n        tmpl::conditional_t<WriteBondi and is_modal, Cce::WorldtubeModeRecorder,\n                            Cce::TestHelpers::WorldtubeModeRecorder>;\n    // NOLINTNEXTLINE(cppcoreguidelines-init-variables)\n    RecorderType recorder{l_max, filename};\n    for (size_t t = 0; t < 30; ++t) {\n      const double time = 0.1 * static_cast<double>(t) + target_time - 1.5;\n      TestHelpers::create_fake_time_varying_data(\n          make_not_null(&spatial_metric_coefficients),\n          make_not_null(&dt_spatial_metric_coefficients),\n          make_not_null(&dr_spatial_metric_coefficients),\n          make_not_null(&shift_coefficients),\n          make_not_null(&dt_shift_coefficients),\n          make_not_null(&dr_shift_coefficients),\n          make_not_null(&lapse_coefficients),\n          make_not_null(&dt_lapse_coefficients),\n          make_not_null(&dr_lapse_coefficients),\n          make_not_null(&extrinsic_curvature_coefficients),\n          make_not_null(&auxiliary_shift_coefficients),\n          make_not_null(&conformal_christoffel_coefficients),\n          make_not_null(&deriv_spatial_metric_coefficients),\n          make_not_null(&deriv_shift_coefficients),\n          make_not_null(&deriv_lapse_coefficients), solution, extraction_radius,\n          amplitude, frequency, time, l_max, not WriteBondi);\n\n      if constexpr (WriteBondi) {\n        (void)descending_m;\n        Cce::create_bondi_boundary_data(\n            make_not_null(&boundary_data_variables),\n            spatial_metric_coefficients, dt_spatial_metric_coefficients,\n            dr_spatial_metric_coefficients, shift_coefficients,\n            dt_shift_coefficients, dr_shift_coefficients, lapse_coefficients,\n            dt_lapse_coefficients, dr_lapse_coefficients, extraction_radius,\n            l_max);\n\n        // loop over the tags that we want to dump.\n        tmpl::for_each<Cce::Tags::worldtube_boundary_tags_for_writing<>>(\n            [&](auto tag_v) {\n              (void)tag_v;\n              using tag = typename decltype(tag_v)::type;\n\n              const ComplexDataVector& nodal_data =\n                  get(get<tag>(boundary_data_variables)).data();\n\n              if constexpr (is_modal) {\n                recorder.template append_modal_data<tag::type::type::spin>(\n                    Cce::dataset_label_for_tag<typename tag::tag>(), time,\n                    nodal_data, l_max);\n              } else {\n                recorder.append_worldtube_mode_data(\n                    Cce::dataset_label_for_tag<typename tag::tag>(), time,\n                    nodal_data, l_max);\n              }\n            });\n      } else {\n        const auto deriv_str = [](const std::string& var, const size_t index) {\n          return \"/D\"s + (index == 0 ? \"x\"s : (index == 1 ? \"y\"s : \"z\"s)) + var;\n        };\n        for (size_t i = 0; i < 3; ++i) {\n          for (size_t j = 0; j < 3; ++j) {\n            if (write_extra_adm_vars) {\n              recorder.append_worldtube_mode_data(\n                  detail::dataset_name_for_component(deriv_str(\"Shift\", i), j),\n                  time, deriv_shift_coefficients.get(i, j), descending_m);\n            }\n\n            if (j < i) {\n              continue;\n            }\n            recorder.append_worldtube_mode_data(\n                detail::dataset_name_for_component(\"/g\", i, j), time,\n                spatial_metric_coefficients.get(i, j), descending_m);\n            recorder.append_worldtube_mode_data(\n                detail::dataset_name_for_component(\"/Drg\", i, j), time,\n                dr_spatial_metric_coefficients.get(i, j), descending_m);\n            recorder.append_worldtube_mode_data(\n                detail::dataset_name_for_component(\"/Dtg\", i, j), time,\n                dt_spatial_metric_coefficients.get(i, j), descending_m);\n\n            if (write_extra_adm_vars) {\n              recorder.append_worldtube_mode_data(\n                  detail::dataset_name_for_component(\"/K\", i, j), time,\n                  extrinsic_curvature_coefficients.get(i, j), descending_m);\n\n              for (size_t k = 0; k < 3; k++) {\n                recorder.append_worldtube_mode_data(\n                    detail::dataset_name_for_component(deriv_str(\"g\", k), i, j),\n                    time, deriv_spatial_metric_coefficients.get(k, i, j));\n              }\n            }\n          }\n          recorder.append_worldtube_mode_data(\n              detail::dataset_name_for_component(\"/Shift\", i), time,\n              shift_coefficients.get(i), descending_m);\n          recorder.append_worldtube_mode_data(\n              detail::dataset_name_for_component(\"/DrShift\", i), time,\n              dr_shift_coefficients.get(i), descending_m);\n          recorder.append_worldtube_mode_data(\n              detail::dataset_name_for_component(\"/DtShift\", i), time,\n              dt_shift_coefficients.get(i), descending_m);\n\n          if (write_extra_adm_vars) {\n            recorder.append_worldtube_mode_data(\n                detail::dataset_name_for_component(\"/AuxiliaryShift\", i), time,\n                auxiliary_shift_coefficients.get(i), descending_m);\n            recorder.append_worldtube_mode_data(\n                detail::dataset_name_for_component(\"/ConformalChristoffel\", i),\n                time, conformal_christoffel_coefficients.get(i), descending_m);\n            recorder.append_worldtube_mode_data(\n                detail::dataset_name_for_component(deriv_str(\"Lapse\", i)), time,\n                deriv_lapse_coefficients.get(i), descending_m);\n          }\n        }\n        recorder.append_worldtube_mode_data(\n            detail::dataset_name_for_component(\"/Lapse\"), time,\n            get(lapse_coefficients), descending_m);\n        recorder.append_worldtube_mode_data(\n            detail::dataset_name_for_component(\"/DrLapse\"), time,\n            get(dr_lapse_coefficients), descending_m);\n        recorder.append_worldtube_mode_data(\n            detail::dataset_name_for_component(\"/DtLapse\"), time,\n            get(dt_lapse_coefficients), descending_m);\n      }\n    }\n  }\n}\n}  // namespace Cce::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(AnalyticSolutions)\n\nset(LIBRARY \"CceHelpers\")\n\nset(LIBRARY_SOURCES\n  CceComputationTestHelpers.cpp\n  KleinGordonBoundaryTestHelpers.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE} ${LIBRARY_SOURCES})\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  KleinGordonBoundaryTestHelpers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Cce\n  DataStructures\n  DataStructuresHelpers\n  Framework\n  H5\n  Spectral\n  SpinWeightedSphericalHarmonics\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/CceComputationTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Evolution/Systems/Cce/CceComputationTestHelpers.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Cce {\nnamespace TestHelpers {\n\nComplexDataVector power(const ComplexDataVector& value, const size_t exponent) {\n  ComplexDataVector return_value{value.size(), 1.0};\n  for (size_t i = 0; i < exponent; ++i) {\n    return_value *= value;\n  }\n  return return_value;\n}\n\nvoid volume_one_minus_y(\n    const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*>\n        one_minus_y,\n    const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  const size_t number_of_radial_points =\n      get(*one_minus_y).size() / number_of_angular_points;\n  const auto& one_minus_y_collocation =\n      1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n                number_of_radial_points);\n  // iterate through the angular 'chunks' and set them to their 1-y value\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    ComplexDataVector angular_view{\n        get(*one_minus_y).data().data() + number_of_angular_points * i,\n        number_of_angular_points};\n    angular_view = one_minus_y_collocation[i];\n  }\n}\n\nvoid generate_volume_data_from_separated_values(\n    const gsl::not_null<ComplexDataVector*> volume_data,\n    const gsl::not_null<ComplexDataVector*> one_divided_by_r,\n    const ComplexDataVector& angular_collocation,\n    const ComplexModalVector& radial_coefficients, const size_t l_max,\n    const size_t number_of_radial_grid_points) {\n  for (size_t i = 0; i < number_of_radial_grid_points; ++i) {\n    ComplexDataVector volume_angular_view{\n        volume_data->data() +\n            i * Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    ComplexDataVector one_divided_by_r_angular_view{\n        one_divided_by_r->data() +\n            i * Spectral::Swsh::number_of_swsh_collocation_points(l_max),\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n    volume_angular_view = angular_collocation * radial_coefficients[0];\n    for (size_t radial_power = 1; radial_power < radial_coefficients.size();\n         ++radial_power) {\n      volume_angular_view += angular_collocation *\n                             radial_coefficients[radial_power] *\n                             power(one_divided_by_r_angular_view, radial_power);\n    }\n  }\n}\n}  // namespace TestHelpers\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/CceComputationTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tags.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/IntegrandInputSteps.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Cce {\nnamespace TestHelpers {\n\n// For representing a primitive series of powers in inverse r for diagnostic\n// computations\ntemplate <typename Tag>\nstruct RadialPolyCoefficientsFor : db::SimpleTag, db::PrefixTag {\n  using type = Scalar<ComplexModalVector>;\n  using tag = Tag;\n};\n\n// For representing the angular function in a separable quantity.\ntemplate <typename Tag>\nstruct AngularCollocationsFor : db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n// shortcut method for evaluating a frequently used radial quantity in CCE\nvoid volume_one_minus_y(\n    gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 0>>*> one_minus_y,\n    size_t l_max);\n\n// explicit power method avoids behavior of Blaze to occasionally FPE on\n// powers of complex that operate fine when repeated multiplication is used\n// instead.\nComplexDataVector power(const ComplexDataVector& value, size_t exponent);\n\n// A utility for copying a set of tags from one DataBox to another. Useful in\n// the CCE tests, where often an expected input needs to be copied to the box\n// which is to be tested.\ntemplate <typename... Tags>\nstruct CopyDataBoxTags {\n  template <typename FromDataBox, typename ToDataBox>\n  static void apply(const gsl::not_null<ToDataBox*> to_data_box,\n                    const FromDataBox& from_data_box) {\n    db::mutate<Tags...>(\n        [](const gsl::not_null<typename Tags::type*>... to_value,\n           const typename Tags::type&... from_value) {\n          const auto assign = [](auto to, const auto& from) {\n            *to = from;\n            return 0;\n          };\n          expand_pack(assign(to_value, from_value)...);\n        },\n        to_data_box, db::get<Tags>(from_data_box)...);\n  }\n};\n\n// Given the angular and radial dependence separately, assembles the volume data\n// by computing the tensor product.\nvoid generate_volume_data_from_separated_values(\n    gsl::not_null<ComplexDataVector*> volume_data,\n    gsl::not_null<ComplexDataVector*> one_divided_by_r,\n    const ComplexDataVector& angular_collocation,\n    const ComplexModalVector& radial_coefficients, size_t l_max,\n    size_t number_of_radial_grid_points);\n\n// A utility for separately verifying the values in several computation\n// routines in the CCE quantity derivations. These are an independent\n// computation of similar quantities needed in the CCE systems under the\n// assumption that the values are separable, i.e. can be written as F(theta,\n// phi, r) = f(theta, phi) * g(r), and performing semi-analytic manipulations\n// using simplifications from separability and an explicit decomposition of g in\n// inverse powers of r.\ntemplate <typename Tag>\nstruct CalculateSeparatedTag;\n\ntemplate <typename Tag>\nstruct CalculateSeparatedTag<Tags::Dy<Tag>> {\n  // d x/dy = d x /dr * (2 R / (1-y)^2) = dx/dr * r^2 / (2 R)\n  // So, for x = r^(-n),\n  // d (r^(-n))/dy = -n * r^(-n - 1) * r^2 / (2 R) = -n / (2 R) r^(-n + 1)\n  // except when n=0, then the result is zero. So, the entire process actually\n  // moves the polynomials down in order\n  template <typename AngularTagList, typename RadialCoefficientTagList>\n  void operator()(\n      const gsl::not_null<Variables<AngularTagList>*> angular_collocation,\n      const gsl::not_null<Variables<RadialCoefficientTagList>*>\n          radial_coefficients,\n      const ComplexDataVector& /*one_divided_by_r*/,\n      const ComplexDataVector& boundary_r, const size_t /*l_max*/) {\n    get(get<AngularCollocationsFor<Tags::Dy<Tag>>>(*angular_collocation))\n        .data() =\n        get(get<AngularCollocationsFor<Tag>>(*angular_collocation)).data() /\n        (2.0 * boundary_r);\n\n    ComplexModalVector& dy_radial_values = get(\n        get<RadialPolyCoefficientsFor<Tags::Dy<Tag>>>(*radial_coefficients));\n    const ComplexModalVector& radial_values =\n        get(get<RadialPolyCoefficientsFor<Tag>>(*radial_coefficients));\n\n    for (size_t radial_power = 1;\n         radial_power < radial_coefficients->number_of_grid_points();\n         ++radial_power) {\n      dy_radial_values[radial_power - 1] =\n          -static_cast<double>(radial_power) * radial_values[radial_power];\n    }\n    dy_radial_values[radial_coefficients->number_of_grid_points() - 1] = 0.0;\n  }\n};\n\ntemplate <typename Tag, typename DerivKind>\nstruct CalculateSeparatedTag<Spectral::Swsh::Tags::Derivative<Tag, DerivKind>> {\n  template <typename AngularTagList, typename RadialCoefficientTagList>\n  void operator()(\n      const gsl::not_null<Variables<AngularTagList>*> angular_collocation,\n      const gsl::not_null<Variables<RadialCoefficientTagList>*>\n          radial_coefficients,\n      const ComplexDataVector& /*one_divided_by_r*/,\n      const ComplexDataVector& /*boundary_r*/, const size_t l_max) {\n    get(get<AngularCollocationsFor<\n            Spectral::Swsh::Tags::Derivative<Tag, DerivKind>>>(\n        *angular_collocation)) =\n        Spectral::Swsh::angular_derivative<DerivKind>(\n            l_max, 1,\n            get(get<AngularCollocationsFor<Tag>>(*angular_collocation)));\n    // The spin-weighted derivatives are evaluated at constant r, so the radial\n    // coefficients are unaltered.\n    get(get<RadialPolyCoefficientsFor<\n            Spectral::Swsh::Tags::Derivative<Tag, DerivKind>>>(\n        *radial_coefficients)) =\n        get(get<RadialPolyCoefficientsFor<Tag>>(*radial_coefficients));\n  }\n};\n\ntemplate <typename LhsTag, typename RhsTag>\nstruct CalculateSeparatedTag<::Tags::Multiplies<LhsTag, RhsTag>> {\n  template <typename AngularTagList, typename RadialCoefficientTagList>\n  void operator()(\n      const gsl::not_null<Variables<AngularTagList>*> angular_collocation,\n      const gsl::not_null<Variables<RadialCoefficientTagList>*>\n          radial_coefficients,\n      const ComplexDataVector& /*one_divided_by_r*/,\n      const ComplexDataVector& /*boundary_r*/, const size_t /*l_max*/) {\n    get(get<AngularCollocationsFor<::Tags::Multiplies<LhsTag, RhsTag>>>(\n        *angular_collocation)) =\n        get(get<AngularCollocationsFor<LhsTag>>(*angular_collocation)) *\n        get(get<AngularCollocationsFor<RhsTag>>(*angular_collocation));\n\n    ComplexModalVector& multiplied_radial_values =\n        get(get<RadialPolyCoefficientsFor<::Tags::Multiplies<LhsTag, RhsTag>>>(\n            *radial_coefficients));\n    const ComplexModalVector& lhs_radial_values =\n        get(get<RadialPolyCoefficientsFor<LhsTag>>(*radial_coefficients));\n    const ComplexModalVector& rhs_radial_values =\n        get(get<RadialPolyCoefficientsFor<RhsTag>>(*radial_coefficients));\n    multiplied_radial_values = 0.0;\n    for (size_t lhs_radial_power = 0;\n         lhs_radial_power < radial_coefficients->number_of_grid_points();\n         ++lhs_radial_power) {\n      for (size_t rhs_radial_power = 0;\n           rhs_radial_power <\n           radial_coefficients->number_of_grid_points() - lhs_radial_power;\n           ++rhs_radial_power) {\n        multiplied_radial_values[lhs_radial_power + rhs_radial_power] +=\n            lhs_radial_values[lhs_radial_power] *\n            rhs_radial_values[rhs_radial_power];\n      }\n    }\n  }\n};\n\ntemplate <>\nstruct CalculateSeparatedTag<Tags::BondiJbar> {\n  template <typename AngularTagList, typename RadialCoefficientTagList>\n  void operator()(\n      const gsl::not_null<Variables<AngularTagList>*> angular_collocation,\n      const gsl::not_null<Variables<RadialCoefficientTagList>*>\n          radial_coefficients,\n      const ComplexDataVector& /*one_divided_by_r*/,\n      const ComplexDataVector& /*boundary_r*/, const size_t /*l_max*/) {\n    get(get<AngularCollocationsFor<Tags::BondiJbar>>(*angular_collocation)) =\n        conj(get(\n            get<AngularCollocationsFor<Tags::BondiJ>>(*angular_collocation)));\n    get(get<RadialPolyCoefficientsFor<Tags::BondiJbar>>(*radial_coefficients)) =\n        conj(get(get<RadialPolyCoefficientsFor<Tags::BondiJ>>(\n            *radial_coefficients)));\n  }\n};\n\ntemplate <>\nstruct CalculateSeparatedTag<Tags::BondiUbar> {\n  template <typename AngularTagList, typename RadialCoefficientTagList>\n  void operator()(\n      const gsl::not_null<Variables<AngularTagList>*> angular_collocation,\n      const gsl::not_null<Variables<RadialCoefficientTagList>*>\n          radial_coefficients,\n      const ComplexDataVector& /*one_divided_by_r*/,\n      const ComplexDataVector& /*boundary_r*/, const size_t /*l_max*/) {\n    get(get<AngularCollocationsFor<Tags::BondiUbar>>(*angular_collocation)) =\n        conj(get(\n            get<AngularCollocationsFor<Tags::BondiU>>(*angular_collocation)));\n    get(get<RadialPolyCoefficientsFor<Tags::BondiUbar>>(*radial_coefficients)) =\n        conj(get(get<RadialPolyCoefficientsFor<Tags::BondiU>>(\n            *radial_coefficients)));\n  }\n};\n\ntemplate <>\nstruct CalculateSeparatedTag<Tags::BondiQbar> {\n  template <typename AngularTagList, typename RadialCoefficientTagList>\n  void operator()(\n      const gsl::not_null<Variables<AngularTagList>*> angular_collocation,\n      const gsl::not_null<Variables<RadialCoefficientTagList>*>\n          radial_coefficients,\n      const ComplexDataVector& /*one_divided_by_r*/,\n      const ComplexDataVector& /*boundary_r*/, const size_t /*l_max*/) {\n    get(get<AngularCollocationsFor<Tags::BondiQbar>>(*angular_collocation)) =\n        conj(get(\n            get<AngularCollocationsFor<Tags::BondiQ>>(*angular_collocation)));\n    get(get<RadialPolyCoefficientsFor<Tags::BondiQbar>>(*radial_coefficients)) =\n        conj(get(get<RadialPolyCoefficientsFor<Tags::BondiQ>>(\n            *radial_coefficients)));\n  }\n};\n\n// A generation function used in the tests of the CCE computations for the\n// inputs to the integrand computation (`Test_PreSwshDerivatives.cpp`,\n// and `Test_ComputeSwshDerivatives.cpp`). This function first generates the set\n// of inputs specified by `InputTagList`, then emulates the cascaded\n// computations of those utilities, using the tag lists as though the integrands\n// that we wanted to compute were of the tags in `TargetTagList`. Instead of\n// computing the values using the utilities in the main code base, though, the\n// generation creates the expected values using the separable computations\n// above. While this is also a fairly complicated computation (though simpler\n// due to the assumption of separability), it is a completely independent method\n// so can be regarded as a robust way of verifying the utilities in the main\n// code base.\ntemplate <typename InputTagList, typename TargetTagList,\n          typename PreSwshDerivativesTagList, typename SwshDerivativesTagList,\n          typename AngularCollocationTagList,\n          typename PreSwshDerivativesRadialModeTagList, typename Generator>\nvoid generate_separable_expected(\n    const gsl::not_null<Variables<PreSwshDerivativesTagList>*>\n        pre_swsh_derivatives,\n    const gsl::not_null<Variables<SwshDerivativesTagList>*> swsh_derivatives,\n    const gsl::not_null<Variables<AngularCollocationTagList>*>\n        angular_collocations,\n    const gsl::not_null<Variables<PreSwshDerivativesRadialModeTagList>*>\n        radial_modes,\n    const gsl::not_null<Generator*> generator,\n    const SpinWeighted<ComplexDataVector, 0>& boundary_r, const size_t l_max,\n    const size_t number_of_radial_points) {\n  const ComplexDataVector y = outer_product(\n      ComplexDataVector{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max), 1.0},\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(\n          number_of_radial_points));\n\n  // generate the separable variables\n  UniformCustomDistribution<double> dist(0.1, 1.0);\n\n  ComplexDataVector one_divided_by_r =\n      (1.0 - y) / (2.0 * create_vector_of_n_copies(boundary_r.data(),\n                                                   number_of_radial_points));\n\n  // the generation step for the 'input' tags\n  tmpl::for_each<InputTagList>([&angular_collocations, &radial_modes,\n                                &pre_swsh_derivatives, &dist, &generator,\n                                &l_max, &number_of_radial_points,\n                                &one_divided_by_r](auto tag_v) {\n    using tag = typename decltype(tag_v)::type;\n    using radial_polynomial_tag = RadialPolyCoefficientsFor<tag>;\n    using angular_collocation_tag = AngularCollocationsFor<tag>;\n    // generate the angular part randomly\n    get(get<angular_collocation_tag>(*angular_collocations)).data() =\n        make_with_random_values<ComplexDataVector>(\n            generator, make_not_null(&dist),\n            Spectral::Swsh::number_of_swsh_collocation_points(l_max));\n    Spectral::Swsh::filter_swsh_boundary_quantity(\n        make_not_null(\n            &get(get<angular_collocation_tag>(*angular_collocations))),\n        l_max, 2);\n    // generate the radial part\n    auto& radial_polynomial = get(get<radial_polynomial_tag>(*radial_modes));\n    fill_with_random_values(make_not_null(&radial_polynomial), generator,\n                            make_not_null(&dist));\n    // filtering to make sure the results are reasonable,\n    // filter function: exp(-10.0 * (i / N)**2)\n    for (size_t i = 0; i < radial_polynomial.size(); ++i) {\n      radial_polynomial[i] *= exp(\n          -10.0 * square(static_cast<double>(i) /\n                         static_cast<double>(radial_polynomial.size() - 1)));\n      // aggressive Heaviside needed to get acceptable test precision at the low\n      // resolutions needed to keep tests fast.\n      if (i > 3) {\n        radial_polynomial[i] = 0.0;\n      }\n    }\n\n    generate_volume_data_from_separated_values(\n        make_not_null(&get(get<tag>(*pre_swsh_derivatives)).data()),\n        make_not_null(&one_divided_by_r),\n        get(get<angular_collocation_tag>(*angular_collocations)).data(),\n        radial_polynomial, l_max, number_of_radial_points);\n  });\n\n  // Compute the expected versions using the separable mathematics from above\n  // utilities\n  const auto calculate_separable_for_pre_swsh_derivative =\n      [&angular_collocations, &radial_modes, &pre_swsh_derivatives, &l_max,\n       &number_of_radial_points, &one_divided_by_r,\n       &boundary_r](auto pre_swsh_derivative_tag_v) {\n        using pre_swsh_derivative_tag =\n            typename decltype(pre_swsh_derivative_tag_v)::type;\n        CalculateSeparatedTag<pre_swsh_derivative_tag>{}(\n            angular_collocations, radial_modes, one_divided_by_r,\n            boundary_r.data(), l_max);\n        generate_volume_data_from_separated_values(\n            make_not_null(\n                &get(get<pre_swsh_derivative_tag>(*pre_swsh_derivatives))\n                     .data()),\n            make_not_null(&one_divided_by_r),\n            get(get<AngularCollocationsFor<pre_swsh_derivative_tag>>(\n                    *angular_collocations))\n                .data(),\n            get(get<RadialPolyCoefficientsFor<pre_swsh_derivative_tag>>(\n                *radial_modes)),\n            l_max, number_of_radial_points);\n      };\n\n  const auto calculate_separable_for_swsh_derivative =\n      [&angular_collocations, &radial_modes, &swsh_derivatives, &l_max,\n       &number_of_radial_points, &one_divided_by_r,\n       &boundary_r](auto swsh_derivative_tag_v) {\n        using swsh_derivative_tag =\n            typename decltype(swsh_derivative_tag_v)::type;\n        CalculateSeparatedTag<swsh_derivative_tag>{}(\n            angular_collocations, radial_modes, one_divided_by_r,\n            boundary_r.data(), l_max);\n        generate_volume_data_from_separated_values(\n            make_not_null(\n                &get(get<swsh_derivative_tag>(*swsh_derivatives)).data()),\n            make_not_null(&one_divided_by_r),\n            get(get<AngularCollocationsFor<swsh_derivative_tag>>(\n                    *angular_collocations))\n                .data(),\n            get(get<RadialPolyCoefficientsFor<swsh_derivative_tag>>(\n                *radial_modes)),\n            l_max, number_of_radial_points);\n      };\n\n  tmpl::for_each<TargetTagList>([&calculate_separable_for_pre_swsh_derivative,\n                                 &calculate_separable_for_swsh_derivative](\n                                    auto target_tag_v) {\n    using target_tag = typename decltype(target_tag_v)::type;\n    tmpl::for_each<typename detail::TagsToComputeForImpl<\n        target_tag>::pre_swsh_derivative_tags>(\n        calculate_separable_for_pre_swsh_derivative);\n    tmpl::for_each<typename detail::TagsToComputeForImpl<\n        target_tag>::swsh_derivative_tags>(\n        calculate_separable_for_swsh_derivative);\n    tmpl::for_each<typename detail::TagsToComputeForImpl<\n        target_tag>::second_swsh_derivative_tags>(\n        calculate_separable_for_swsh_derivative);\n  });\n}\n}  // namespace TestHelpers\n}  // namespace Cce\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/KleinGordonBoundaryTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Evolution/Systems/Cce/KleinGordonBoundaryTestHelpers.hpp\"\n\nnamespace Cce::TestHelpers {\n\nvoid create_fake_time_varying_klein_gordon_data(\n    const gsl::not_null<Scalar<ComplexModalVector>*> kg_psi_modal,\n    const gsl::not_null<Scalar<ComplexModalVector>*> kg_pi_modal,\n    const gsl::not_null<Scalar<DataVector>*> kg_psi_nodal,\n    const gsl::not_null<Scalar<DataVector>*> kg_pi_nodal,\n    const double extraction_radius, const double amplitude,\n    const double frequency, const double time, const size_t l_max) {\n  const size_t number_of_angular_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n  tnsr::I<DataVector, 3> collocation_points{number_of_angular_points};\n\n  const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n      Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n  for (const auto collocation_point : collocation) {\n    get<0>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        sin(collocation_point.theta) * cos(collocation_point.phi);\n    get<1>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        sin(collocation_point.theta) * sin(collocation_point.phi);\n    get<2>(collocation_points)[collocation_point.offset] =\n        extraction_radius * (1.0 + amplitude * sin(frequency * time)) *\n        cos(collocation_point.theta);\n  }\n\n  const DataVector r = sqrt(square(get<0>(collocation_points)) +\n                            square(get<1>(collocation_points)) +\n                            square(get<2>(collocation_points)));\n  const DataVector dr_dt = r / (1.0 + amplitude * sin(frequency * time)) *\n                           amplitude * frequency * cos(frequency * time);\n\n  // Nodal data\n  get(*kg_psi_nodal) = sin(r - time);\n  get(*kg_pi_nodal) = cos(r - time) * (dr_dt - 1.0);\n\n  // Transform to modal data\n  *kg_psi_modal =\n      TestHelpers::tensor_to_goldberg_coefficients(*kg_psi_nodal, l_max);\n  *kg_pi_modal =\n      TestHelpers::tensor_to_goldberg_coefficients(*kg_pi_nodal, l_max);\n}\n\n}  // namespace Cce::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/KleinGordonBoundaryTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n\nnamespace Cce::TestHelpers {\n\n// The nodal data for the scalar field psi reads\n//\n// psi = sin(r - t)\n//\n// where r is a time-dependent radius\n//\n// r = (1 + A * sin ft) * R\n//\n// Its time derivative is given by\n//\n// dr/dt = A * f * cos ft * R\n//       =  r / (1 + A * sin ft) * A * f * cos ft\nvoid create_fake_time_varying_klein_gordon_data(\n    gsl::not_null<Scalar<ComplexModalVector>*> kg_psi_modal,\n    gsl::not_null<Scalar<ComplexModalVector>*> kg_pi_modal,\n    gsl::not_null<Scalar<DataVector>*> kg_psi_nodal,\n    gsl::not_null<Scalar<DataVector>*> kg_pi_nodal, double extraction_radius,\n    double amplitude, double frequency, double time, size_t l_max);\n\n// Dump tensor+scalar data into a specified HDF5 file named `filename`.\n// The tensor part comes from `AnalyticSolution` whereas the scalar part\n// from `create_fake_time_varying_klein_gordon_data`.\ntemplate <typename AnalyticSolution>\nvoid write_scalar_tensor_test_file(const AnalyticSolution& solution,\n                                   const std::string& filename,\n                                   const double target_time,\n                                   const double extraction_radius,\n                                   const double frequency,\n                                   const double amplitude, const size_t l_max) {\n  write_test_file(solution, filename, target_time, extraction_radius, frequency,\n                  amplitude, l_max);\n\n  const size_t goldberg_size = square(l_max + 1);\n  Scalar<ComplexModalVector> kg_psi_modal{goldberg_size};\n  Scalar<ComplexModalVector> kg_pi_modal{goldberg_size};\n  Scalar<DataVector> kg_psi_nodal;\n  Scalar<DataVector> kg_pi_nodal;\n\n  // scoped to close the file\n  {\n    TestHelpers::WorldtubeModeRecorder recorder{l_max, filename};\n    // write times to file for several steps before and after the target time\n    for (size_t t = 0; t < 30; ++t) {\n      const double time = 0.1 * static_cast<double>(t) + target_time - 1.5;\n      // create scalar data\n      TestHelpers::create_fake_time_varying_klein_gordon_data(\n          make_not_null(&kg_psi_modal), make_not_null(&kg_pi_modal),\n          make_not_null(&kg_psi_nodal), make_not_null(&kg_pi_nodal),\n          extraction_radius, amplitude, frequency, time, l_max);\n\n      // write scalar data\n      recorder.append_worldtube_mode_data(\n          detail::dataset_name_for_component(\"/KGPsi\"), time, get(kg_psi_modal),\n          false, true);\n      recorder.append_worldtube_mode_data(\n          detail::dataset_name_for_component(\"/dtKGPsi\"), time,\n          get(kg_pi_modal), false, true);\n    }\n  }\n}\n}  // namespace Cce::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/VolumeTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Evolution/Systems/Cce/BoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/GaugeTransformBoundaryData.hpp\"\n#include \"Evolution/Systems/Cce/Initialize/InitializeJ.hpp\"\n#include \"Evolution/Systems/Cce/NewmanPenrose.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Evolution/Systems/Cce/PreSwshDerivatives.hpp\"\n#include \"Evolution/Systems/Cce/PrecomputeCceDependencies.hpp\"\n#include \"Evolution/Systems/Cce/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Evolution/Systems/Cce/BoundaryTestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Cce::TestHelpers {\nstruct InverseCubicEvolutionGauge {\n  using boundary_tags =\n      tmpl::list<Tags::EvolutionGaugeBoundaryValue<Tags::BondiJ>,\n                 Tags::EvolutionGaugeBoundaryValue<Tags::Dr<Tags::BondiJ>>,\n                 Tags::EvolutionGaugeBoundaryValue<Tags::BondiR>>;\n\n  using mutate_tags = tmpl::list<Tags::BondiJ, Tags::CauchyCartesianCoords,\n                                 Tags::CauchyAngularCoords>;\n  using argument_tags =\n      tmpl::append<boundary_tags,\n                   tmpl::list<Tags::LMax, Tags::NumberOfRadialPoints>>;\n\n  InverseCubicEvolutionGauge() = default;\n\n  void operator()(\n      const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, 2>>*> j,\n      const gsl::not_null<tnsr::i<DataVector, 3>*> /*cartesian_x_of_x_tilde*/,\n      const gsl::not_null<\n          tnsr::i<DataVector, 2, ::Frame::Spherical<::Frame::Inertial>>*>\n      /*angular_cauchy_coordinates*/,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 2>>& boundary_dr_j,\n      const Scalar<SpinWeighted<ComplexDataVector, 0>>& r,\n      const size_t /*l_max*/, const size_t number_of_radial_points) const {\n    const DataVector one_minus_y_collocation =\n        1.0 - Spectral::collocation_points<Spectral::Basis::Legendre,\n                                           Spectral::Quadrature::GaussLobatto>(\n                  number_of_radial_points);\n    for (size_t i = 0; i < number_of_radial_points; i++) {\n      ComplexDataVector angular_view_j{\n          get(*j).data().data() + get(boundary_j).size() * i,\n          get(boundary_j).size()};\n      // auto is acceptable here as these two values are only used once in the\n      // below computation. `auto` causes an expression template to be\n      // generated, rather than allocating.\n      const auto one_minus_y_coefficient =\n          0.25 * (3.0 * get(boundary_j).data() +\n                  get(r).data() * get(boundary_dr_j).data());\n      const auto one_minus_y_cubed_coefficient =\n          -0.0625 *\n          (get(boundary_j).data() + get(r).data() * get(boundary_dr_j).data());\n      angular_view_j =\n          one_minus_y_collocation[i] * one_minus_y_coefficient +\n          pow<3>(one_minus_y_collocation[i]) * one_minus_y_cubed_coefficient;\n    }\n  }\n};\n\nstruct VariationAmplitude : db::SimpleTag {\n  using type = double;\n};\n\nstruct InertialVariationAmplitude : db::SimpleTag {\n  using type = double;\n};\n\nusing real_boundary_tags =\n    tmpl::list<Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords,\n               Tags::PartiallyFlatAngularCoords,\n               Tags::PartiallyFlatCartesianCoords,\n               ::Tags::dt<Tags::CauchyCartesianCoords>,\n               ::Tags::dt<Tags::PartiallyFlatCartesianCoords>>;\nusing spin_weighted_boundary_tags = tmpl::flatten<tmpl::list<\n    tmpl::list<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n               Tags::PartiallyFlatGaugeOmega, Tags::CauchyGaugeC,\n               Tags::CauchyGaugeD, Tags::CauchyGaugeOmega,\n               Tags::Du<Tags::PartiallyFlatGaugeOmega>,\n               Spectral::Swsh::Tags::Derivative<Tags::PartiallyFlatGaugeOmega,\n                                                Spectral::Swsh::Tags::Eth>,\n               Spectral::Swsh::Tags::Derivative<Tags::CauchyGaugeOmega,\n                                                Spectral::Swsh::Tags::Eth>,\n               Tags::BondiUAtScri>,\n    Tags::characteristic_worldtube_boundary_tags<\n        Tags::EvolutionGaugeBoundaryValue>>>;\nusing coordinate_variables_tag = ::Tags::Variables<real_boundary_tags>;\nusing spin_weighted_variables_tag =\n    ::Tags::Variables<spin_weighted_boundary_tags>;\nusing matching_variables_tag = ::Tags::Variables<\n    tmpl::list<Tags::BoundaryValue<Tags::Psi0Match>,\n               Tags::BoundaryValue<Tags::Dlambda<Tags::Psi0Match>>>>;\nusing volume_spin_weighted_variables_tag = ::Tags::Variables<\n    tmpl::list<Tags::BondiJ, Tags::Dy<Tags::BondiJ>, Tags::BondiJCauchyView,\n               Tags::Dy<Tags::BondiJCauchyView>,\n               Tags::Dy<Tags::Dy<Tags::BondiJCauchyView>>, Tags::Psi0Match,\n               Tags::OneMinusY, Tags::Dy<Tags::Psi0Match>, Tags::BondiU>>;\n\ntemplate <typename Generator>\nauto create_cce_volume_box(const gsl::not_null<Generator*> generator,\n                           const size_t l_max,\n                           const size_t number_of_radial_grid_points,\n                           const bool transform_gauge) {\n  const size_t number_of_angular_grid_points =\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max);\n\n  UniformCustomDistribution<double> value_dist{0.1, 0.5};\n\n  auto box = db::create<db::AddSimpleTags<\n      VariationAmplitude, InertialVariationAmplitude, coordinate_variables_tag,\n      spin_weighted_variables_tag,\n      ::Tags::Variables<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>,\n      volume_spin_weighted_variables_tag, Tags::LMax, matching_variables_tag,\n      Tags::NumberOfRadialPoints,\n      Spectral::Swsh::Tags::SwshInterpolator<Tags::CauchyAngularCoords>,\n      Spectral::Swsh::Tags::SwshInterpolator<\n          Tags::PartiallyFlatAngularCoords>>>(\n      0.0, 0.0,\n      typename coordinate_variables_tag::type{number_of_angular_grid_points},\n      typename spin_weighted_variables_tag::type{number_of_angular_grid_points},\n      Variables<\n          Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>{\n          number_of_angular_grid_points},\n      typename volume_spin_weighted_variables_tag::type{\n          number_of_angular_grid_points * number_of_radial_grid_points},\n      l_max,\n      typename matching_variables_tag::type{number_of_angular_grid_points},\n      number_of_radial_grid_points, Spectral::Swsh::SwshInterpolator{},\n      Spectral::Swsh::SwshInterpolator{});\n\n  db::mutate<VariationAmplitude, InertialVariationAmplitude>(\n      [&value_dist, &generator, &transform_gauge](\n          const gsl::not_null<double*> variation_amplitude,\n          const gsl::not_null<double*> inertial_variation_amplitude) {\n        *inertial_variation_amplitude = value_dist(*generator);\n        *variation_amplitude = transform_gauge ? 0.0 : value_dist(*generator);\n      },\n      make_not_null(&box));\n\n  // create analytic Cauchy data (a stationary Kerr BH)\n  // first prepare the input for the modal version\n  const double mass = value_dist(*generator);\n  const std::array<double, 3> spin{\n      {value_dist(*generator), value_dist(*generator), value_dist(*generator)}};\n  const std::array<double, 3> center{\n      {value_dist(*generator), value_dist(*generator), value_dist(*generator)}};\n  const gr::Solutions::KerrSchild solution{mass, spin, center};\n\n  const double extraction_radius = 100.0;\n\n  // acceptable parameters for the fake sinusoid variation in the input\n  // parameters\n  const double frequency = 0.1 * value_dist(*generator);\n  const double amplitude = 0.1 * value_dist(*generator);\n  const double target_time = 50.0 * value_dist(*generator);\n\n  const size_t libsharp_size =\n      Spectral::Swsh::size_of_libsharp_coefficient_vector(l_max);\n  tnsr::ii<ComplexModalVector, 3> spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dt_spatial_metric_coefficients{libsharp_size};\n  tnsr::ii<ComplexModalVector, 3> dr_spatial_metric_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dt_shift_coefficients{libsharp_size};\n  tnsr::I<ComplexModalVector, 3> dr_shift_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dt_lapse_coefficients{libsharp_size};\n  Scalar<ComplexModalVector> dr_lapse_coefficients{libsharp_size};\n  TestHelpers::create_fake_time_varying_data(\n      make_not_null(&spatial_metric_coefficients),\n      make_not_null(&dt_spatial_metric_coefficients),\n      make_not_null(&dr_spatial_metric_coefficients),\n      make_not_null(&shift_coefficients), make_not_null(&dt_shift_coefficients),\n      make_not_null(&dr_shift_coefficients), make_not_null(&lapse_coefficients),\n      make_not_null(&dt_lapse_coefficients),\n      make_not_null(&dr_lapse_coefficients), solution, extraction_radius,\n      amplitude, frequency, target_time, l_max, false);\n\n  db::mutate<::Tags::Variables<\n      Tags::characteristic_worldtube_boundary_tags<Tags::BoundaryValue>>>(\n      [&l_max, &spatial_metric_coefficients, &dt_spatial_metric_coefficients,\n       &dr_spatial_metric_coefficients, &shift_coefficients,\n       &dt_shift_coefficients, &dr_shift_coefficients, &lapse_coefficients,\n       &dt_lapse_coefficients, &dr_lapse_coefficients, &extraction_radius](\n          const gsl::not_null<\n              Variables<Tags::characteristic_worldtube_boundary_tags<\n                  Tags::BoundaryValue>>*>\n              spin_weighted_boundary_variables) {\n        create_bondi_boundary_data(\n            spin_weighted_boundary_variables, spatial_metric_coefficients,\n            dt_spatial_metric_coefficients, dr_spatial_metric_coefficients,\n            shift_coefficients, dt_shift_coefficients, dr_shift_coefficients,\n            lapse_coefficients, dt_lapse_coefficients, dr_lapse_coefficients,\n            extraction_radius, l_max);\n      },\n      make_not_null(&box));\n\n  // construct the coordinate transform quantities\n  const double variation_amplitude = db::get<VariationAmplitude>(box);\n  const double variation_amplitude_inertial =\n      db::get<InertialVariationAmplitude>(box);\n  db::mutate<Tags::CauchyCartesianCoords>(\n      [&l_max,\n       &variation_amplitude](const gsl::not_null<tnsr::i<DataVector, 3>*>\n                                 cauchy_cartesian_coordinates) {\n        tnsr::i<DataVector, 2> cauchy_angular_coordinates{\n            get<0>(*cauchy_cartesian_coordinates).size()};\n        const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n            Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n        for (const auto collocation_point : collocation) {\n          // Variation amplitude is 0 if transform_gauge is true\n          get<1>(cauchy_angular_coordinates)[collocation_point.offset] =\n              collocation_point.phi + 1.0e-2 * variation_amplitude *\n                                          cos(collocation_point.phi) *\n                                          sin(collocation_point.theta);\n          get<0>(cauchy_angular_coordinates)[collocation_point.offset] =\n              collocation_point.theta;\n        }\n        get<0>(*cauchy_cartesian_coordinates) =\n            sin(get<0>(cauchy_angular_coordinates)) *\n            cos(get<1>(cauchy_angular_coordinates));\n        get<1>(*cauchy_cartesian_coordinates) =\n            sin(get<0>(cauchy_angular_coordinates)) *\n            sin(get<1>(cauchy_angular_coordinates));\n        get<2>(*cauchy_cartesian_coordinates) =\n            cos(get<0>(cauchy_angular_coordinates));\n      },\n      make_not_null(&box));\n\n  db::mutate<Tags::PartiallyFlatCartesianCoords>(\n      [&l_max, &variation_amplitude_inertial](\n          const gsl::not_null<tnsr::i<DataVector, 3>*>\n              inertial_cartesian_coordinates) {\n        tnsr::i<DataVector, 2> inertial_angular_coordinates{\n            get<0>(*inertial_cartesian_coordinates).size()};\n        const auto& collocation = Spectral::Swsh::cached_collocation_metadata<\n            Spectral::Swsh::ComplexRepresentation::Interleaved>(l_max);\n        for (const auto& collocation_point : collocation) {\n          get<1>(inertial_angular_coordinates)[collocation_point.offset] =\n              collocation_point.phi + 1.0e-2 * variation_amplitude_inertial *\n                                          cos(collocation_point.phi) *\n                                          sin(collocation_point.theta);\n          get<0>(inertial_angular_coordinates)[collocation_point.offset] =\n              collocation_point.theta;\n        }\n        get<0>(*inertial_cartesian_coordinates) =\n            sin(get<0>(inertial_angular_coordinates)) *\n            cos(get<1>(inertial_angular_coordinates));\n        get<1>(*inertial_cartesian_coordinates) =\n            sin(get<0>(inertial_angular_coordinates)) *\n            sin(get<1>(inertial_angular_coordinates));\n        get<2>(*inertial_cartesian_coordinates) =\n            cos(get<0>(inertial_angular_coordinates));\n      },\n      make_not_null(&box));\n\n  if (transform_gauge) {\n    // Update various auxiliary boundary variables in order to prepare the\n    // boundary data for BondiJ. Operations are done for both Cauchy\n    // coordinates and partially flat coordinates.\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n        make_not_null(&box));\n    db::mutate_apply<GaugeUpdateAngularFromCartesian<\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&box));\n\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::CauchyAngularCoords>>(\n        make_not_null(&box));\n    db::mutate_apply<GaugeUpdateInterpolator<Tags::PartiallyFlatAngularCoords>>(\n        make_not_null(&box));\n\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n        Tags::CauchyAngularCoords, Tags::CauchyCartesianCoords>>(\n        make_not_null(&box));\n    db::mutate_apply<GaugeUpdateJacobianFromCoordinates<\n        Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n        Tags::PartiallyFlatAngularCoords, Tags::PartiallyFlatCartesianCoords>>(\n        make_not_null(&box));\n\n    db::mutate_apply<\n        GaugeUpdateOmega<Tags::PartiallyFlatGaugeC, Tags::PartiallyFlatGaugeD,\n                         Tags::PartiallyFlatGaugeOmega>>(make_not_null(&box));\n    db::mutate_apply<GaugeUpdateOmega<Tags::CauchyGaugeC, Tags::CauchyGaugeD,\n                                      Tags::CauchyGaugeOmega>>(\n        make_not_null(&box));\n  }\n\n  return box; // NRVO\n}\n}  // namespace Cce::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Cce/WriteToWorldtubeH5.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace Cce::TestHelpers {\n\n// records worldtube data in the SpEC h5 style.\nstruct WorldtubeModeRecorder {\n public:\n  WorldtubeModeRecorder(const size_t l_max, const std::string& filename)\n      : l_max_(l_max), output_file_{filename, true} {\n    // write the .ver that indicates that the derivatives are correctly\n    // normalized.\n    output_file_.try_insert<h5::Version>(\n        \"/VersionHist\", \"Bugfix in CCE radial derivatives (ticket 1096).\");\n    output_file_.close_current_object();\n    all_modal_file_legend_.emplace_back(\"time\");\n    all_nodal_file_legend_.emplace_back(\"time\");\n    real_file_legend_.emplace_back(\"time\");\n    complex_nodal_legend_.emplace_back(\"time\");\n\n    // Modal legends\n    for (int l = 0; l <= static_cast<int>(l_max_); ++l) {\n      for (int m = -l; m <= l; ++m) {\n        all_modal_file_legend_.push_back(\"Real Y_\" + std::to_string(l) + \",\" +\n                                         std::to_string(m));\n        all_modal_file_legend_.push_back(\"Imag Y_\" + std::to_string(l) + \",\" +\n                                         std::to_string(m));\n\n        if (m >= 0) {\n          real_file_legend_.push_back(\"Real Y_\" + std::to_string(l) + \",\" +\n                                      std::to_string(m));\n          if (m != 0) {\n            real_file_legend_.push_back(\"Imag Y_\" + std::to_string(l) + \",\" +\n                                        std::to_string(m));\n          }\n        }\n      }\n    }\n\n    const size_t num_points =\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max_);\n\n    // Nodal legends\n    for (size_t i = 0; i < num_points; i++) {\n      all_nodal_file_legend_.push_back(\"Point\" + std::to_string(i));\n\n      complex_nodal_legend_.push_back(\"Real Point\" + std::to_string(i));\n      complex_nodal_legend_.push_back(\"Imag Point\" + std::to_string(i));\n    }\n  }\n\n  // append to `dataset_path` the vector created by `time` followed by the\n  // `modes` rearranged to be compatible with SpEC h5 format.\n  void append_worldtube_mode_data(const std::string& dataset_path,\n                                  const double time,\n                                  const ComplexModalVector& modes,\n                                  const bool descending_m = true,\n                                  const bool is_real = false) {\n    const size_t modal_size = square(l_max_ + 1);\n    ASSERT(modes.size() == modal_size, \"Expected modes of size \"\n                                           << modal_size << \" but got \"\n                                           << modes.size() << \" instead.\");\n    auto& output_mode_dataset = output_file_.try_insert<h5::Dat>(\n        dataset_path, is_real ? real_file_legend_ : all_modal_file_legend_, 0);\n    const size_t output_size = (is_real ? 1 : 2) * modal_size;\n    std::vector<double> data_to_write(output_size + 1);\n    data_to_write[0] = time;\n    for (int l = 0; l <= static_cast<int>(l_max_); ++l) {\n      for (int m = (is_real ? 0 : -l); m <= l; ++m) {\n        const int em = descending_m ? -m : m;\n        const size_t to_write_index =\n            is_real ? static_cast<size_t>(m == 0 ? square(l) + 1\n                                                 : square(l) + 2 * abs(m))\n                    : (2 * Spectral::Swsh::goldberg_mode_index(\n                               l_max_, static_cast<size_t>(l), em) +\n                       1);\n        const size_t mode_index = Spectral::Swsh::goldberg_mode_index(\n            l_max_, static_cast<size_t>(l), m);\n        data_to_write[to_write_index] = real(modes[mode_index]);\n        if (not is_real or m != 0) {\n          data_to_write[to_write_index + 1] = imag(modes[mode_index]);\n        }\n      }\n    }\n\n    output_mode_dataset.append(data_to_write);\n    output_file_.close_current_object();\n  }\n\n  template <typename T>\n  void append_worldtube_mode_data(const std::string& dataset_path,\n                                  const double time, const T& nodes,\n                                  const bool /*unused*/ = true) {\n    static_assert(std::is_same_v<T, ComplexDataVector> or\n                  std::is_same_v<T, DataVector>);\n    constexpr bool is_complex = std::is_same_v<T, ComplexDataVector>;\n\n    auto& output_mode_dataset = output_file_.try_insert<h5::Dat>(\n        dataset_path,\n        is_complex ? complex_nodal_legend_ : all_nodal_file_legend_, 0);\n    const size_t output_size =\n        (is_complex ? 2 : 1) *\n        Spectral::Swsh::number_of_swsh_collocation_points(l_max_);\n\n    std::vector<double> data_to_write(output_size + 1);\n    data_to_write[0] = time;\n\n    if ((is_complex ? 2 : 1) * nodes.size() != output_size) {\n      ERROR(\"Trying to write test worldtube data. Data passed in has size \"\n            << (is_complex ? 2 : 1) * nodes.size() << \" but expected size \"\n            << output_size);\n    }\n    for (size_t i = 0; i < nodes.size(); i++) {\n      if constexpr (is_complex) {\n        data_to_write[2 * i + 1] = real(nodes[i]);\n        data_to_write[2 * i + 2] = imag(nodes[i]);\n      } else {\n        data_to_write[i + 1] = nodes[i];\n      }\n    }\n\n    output_mode_dataset.append(data_to_write);\n    output_file_.close_current_object();\n  }\n\n private:\n  size_t l_max_;\n  h5::H5File<h5::AccessType::ReadWrite> output_file_;\n  std::vector<std::string> all_modal_file_legend_;\n  std::vector<std::string> all_nodal_file_legend_;\n  std::vector<std::string> real_file_legend_;\n  std::vector<std::string> complex_nodal_legend_;\n};\n}  // namespace Cce::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/Ccz4/PrimReconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/Systems/Ccz4/ATilde.hpp\"\n#include \"Evolution/Systems/Ccz4/Christoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/DerivChristoffel.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/System.hpp\"\n#include \"Evolution/Systems/Ccz4/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/System.hpp\"\n#include \"Evolution/Systems/Ccz4/Tags.hpp\"\n#include \"Evolution/Systems/Ccz4/TimeDerivative.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::Ccz4::fd::detail {\nusing GhostData = evolution::dg::subcell::GhostData;\ntemplate <typename Fr = Frame::ElementLogical, typename F, typename... Args>\nDirectionalIdMap<3, GhostData> compute_ghost_data(\n    const Mesh<3>& subcell_mesh,\n    const tnsr::I<DataVector, 3, Fr>& volume_coords,\n    const DirectionMap<3, Neighbors<3>>& neighbors,\n    const size_t ghost_zone_size, const F& compute_variables_of_neighbor_data,\n    const std::array<double, 3>& coords_range, const Args&... args) {\n  DirectionalIdMap<3, GhostData> ghost_data{};\n  for (const auto& [direction, neighbors_in_direction] : neighbors) {\n    REQUIRE(neighbors_in_direction.size() == 1);\n    const ElementId<3>& neighbor_id = *neighbors_in_direction.begin();\n    auto neighbor_coords = volume_coords;\n    neighbor_coords.get(direction.dimension()) +=\n        direction.sign() * gsl::at(coords_range, direction.dimension());\n    const auto neighbor_vars_for_reconstruction =\n        compute_variables_of_neighbor_data(neighbor_coords, args...);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars_for_reconstruction.data(),\n                       neighbor_vars_for_reconstruction.size()),\n        subcell_mesh.extents(), ghost_zone_size,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    ghost_data[DirectionalId<3>{direction, neighbor_id}] = GhostData{1};\n    ghost_data.at(DirectionalId<3>{direction, neighbor_id})\n        .neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n  return ghost_data;\n}\n\ntemplate <typename Fr = Frame::ElementLogical, typename F, typename... Args>\nDirectionalIdMap<3, GhostData> compute_ghost_data(\n    const Mesh<3>& subcell_mesh,\n    const tnsr::I<DataVector, 3, Fr>& volume_coords,\n    const DirectionMap<3, Neighbors<3>>& neighbors,\n    const size_t ghost_zone_size, const F& compute_variables_of_neighbor_data,\n    Args&&... args) {\n  const std::array<double, 3> coords_range{2., 2., 2.};\n  return compute_ghost_data(subcell_mesh, volume_coords, neighbors,\n                            ghost_zone_size, compute_variables_of_neighbor_data,\n                            coords_range, std::forward<Args>(args)...);\n}\n\ninline Variables<::Ccz4::fd::Tags::spacetime_reconstruction_tags>\ncompute_prim_solution(\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords) {\n  using ConformalMetric = ::Ccz4::Tags::ConformalMetric<DataVector, 3>;\n  using ATilde = ::Ccz4::Tags::ATilde<DataVector, 3>;\n  using ConformalFactor = ::Ccz4::Tags::ConformalFactor<DataVector>;\n  using TraceExtrinsicCurvature = gr::Tags::TraceExtrinsicCurvature<DataVector>;\n  using Theta = ::Ccz4::Tags::Theta<DataVector>;\n  using GammaHat = ::Ccz4::Tags::GammaHat<DataVector, 3>;\n  using Lapse = gr::Tags::Lapse<DataVector>;\n  using Shift = gr::Tags::Shift<DataVector, 3>;\n  using AuxiliaryShiftB = ::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>;\n\n  Variables<::Ccz4::fd::Tags::spacetime_reconstruction_tags> vars{\n      get<0>(coords).size(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    get(get<ConformalFactor>(vars)) += coords.get(i);\n    get(get<TraceExtrinsicCurvature>(vars)) += coords.get(i);\n    get(get<Theta>(vars)) += coords.get(i);\n    get(get<Lapse>(vars)) += coords.get(i);\n    for (size_t j = 0; j < 3; ++j) {\n      get<GammaHat>(vars).get(j) += coords.get(i);\n      get<Shift>(vars).get(j) += coords.get(i);\n      get<AuxiliaryShiftB>(vars).get(j) += coords.get(i);\n    }\n  }\n  get(get<ConformalFactor>(vars)) += 2.0;\n  get(get<TraceExtrinsicCurvature>(vars)) += 15.0;\n  get(get<Theta>(vars)) += 30.0;\n  get(get<Lapse>(vars)) += 50.0;\n  for (size_t j = 0; j < 3; ++j) {\n    get<GammaHat>(vars).get(j) += 1.0e-2 * static_cast<double>((j + 2) + 10);\n    get<Shift>(vars).get(j) += 1.0e-2 * static_cast<double>((j + 2) + 60);\n    get<AuxiliaryShiftB>(vars).get(j) +=\n        1.0e-2 * static_cast<double>((j + 2) + 110);\n  }\n\n  auto& conformal_metric = get<ConformalMetric>(vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      conformal_metric.get(i, j) = (10 * i + 50 * j + 1) * coords.get(i);\n    }\n  }\n  auto& atilde = get<ATilde>(vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      atilde.get(i, j) = (1000 * i + 5000 * j + 1) * coords.get(i);\n    }\n  }\n  return vars;\n}\n\ninline Variables<::Ccz4::fd::Tags::spacetime_reconstruction_tags>\ncompute_prim_solution_for_second_deriv(\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords) {\n  using ConformalMetric = ::Ccz4::Tags::ConformalMetric<DataVector, 3>;\n  using ATilde = ::Ccz4::Tags::ATilde<DataVector, 3>;\n  using ConformalFactor = ::Ccz4::Tags::ConformalFactor<DataVector>;\n  using TraceExtrinsicCurvature = gr::Tags::TraceExtrinsicCurvature<DataVector>;\n  using Theta = ::Ccz4::Tags::Theta<DataVector>;\n  using GammaHat = ::Ccz4::Tags::GammaHat<DataVector, 3>;\n  using Lapse = gr::Tags::Lapse<DataVector>;\n  using Shift = gr::Tags::Shift<DataVector, 3>;\n  using AuxiliaryShiftB = ::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>;\n\n  Variables<::Ccz4::fd::Tags::spacetime_reconstruction_tags> vars{\n      get<0>(coords).size(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    get(get<ConformalFactor>(vars)) += square(coords.get(i)) + coords.get(i);\n    get(get<TraceExtrinsicCurvature>(vars)) +=\n        square(coords.get(i)) + coords.get(i);\n    get(get<Theta>(vars)) += square(coords.get(i)) + coords.get(i);\n    get(get<Lapse>(vars)) += square(coords.get(i)) + coords.get(i);\n    for (size_t j = 0; j < 3; ++j) {\n      get<GammaHat>(vars).get(j) += square(coords.get(i)) + coords.get(i);\n      get<Shift>(vars).get(j) += square(coords.get(i)) + coords.get(i);\n      get<AuxiliaryShiftB>(vars).get(j) +=\n          square(coords.get(i)) + coords.get(i);\n    }\n  }\n  get(get<ConformalFactor>(vars)) += 2.0;\n  get(get<TraceExtrinsicCurvature>(vars)) += 15.0;\n  get(get<Theta>(vars)) += 30.0;\n  get(get<Lapse>(vars)) += 50.0 + coords.get(0) * coords.get(2);\n  for (size_t j = 0; j < 3; ++j) {\n    get<GammaHat>(vars).get(j) += 1.0e-2 * static_cast<double>((j + 2) + 10);\n    get<Shift>(vars).get(j) += 1.0e-2 * static_cast<double>((j + 2) + 60);\n    get<AuxiliaryShiftB>(vars).get(j) +=\n        1.0e-2 * static_cast<double>((j + 2) + 110);\n  }\n\n  auto& conformal_metric = get<ConformalMetric>(vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      conformal_metric.get(i, j) =\n          (10 * i + 50 * j + 1) * square(coords.get(i)) +\n          (10 * i + 50 * j + 1) * coords.get(i);\n    }\n  }\n  auto& atilde = get<ATilde>(vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      atilde.get(i, j) = (1000 * i + 5000 * j + 1) * square(coords.get(i)) +\n                         (1000 * i + 5000 * j + 1) * coords.get(i);\n    }\n  }\n  return vars;\n}\n\ninline Element<3> set_element(const bool skip_last = false) {\n  DirectionMap<3, Neighbors<3>> neighbors{};\n  for (size_t i = 0; i < 6; ++i) {\n    if (skip_last and i == 5) {\n      ASSERT(gsl::at(Direction<3>::all_directions(), i) ==\n                 Direction<3>::upper_zeta(),\n             \"Last direction is not upper_zeta\");\n      break;\n    }\n    neighbors[gsl::at(Direction<3>::all_directions(), i)] = Neighbors<3>{\n        {ElementId<3>{i + 1, {}}}, OrientationMap<3>::create_aligned()};\n  }\n  return Element<3>{ElementId<3>{0, {}}, neighbors};\n}\n\ninline tnsr::I<DataVector, 3, Frame::ElementLogical> set_logical_coordinates(\n    const Mesh<3>& subcell_mesh) {\n  auto logical_coords = logical_coordinates(subcell_mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < 3; ++i) {\n    logical_coords.get(i) += static_cast<double>(4 * i);\n  }\n  return logical_coords;\n}\n\nnamespace Minkowski {\ntemplate <bool UsedForSommerfeldTest = false>\ninline Variables<::Ccz4::fd::Tags::spacetime_reconstruction_tags>\ncompute_prim_solution_for_Minkowski(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords) {\n  using FrameType = Frame::Inertial;\n  const size_t SpatialDim = 3;\n\n  Variables<typename ::Ccz4::fd::System::variables_tag::tags_list> evolved_vars{\n      get<0>(coords).size()};\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n  // Setup solution\n  const gr::Solutions::Minkowski<SpatialDim> solution{};\n  // Evaluate solution\n  const auto minkowski_vars = solution.variables(\n      coords, t,\n      typename gr::Solutions::Minkowski<SpatialDim>::tags<DataVector>{});\n  const DataVector used_for_size = DataVector(\n      get<0>(coords).size(), std::numeric_limits<double>::signaling_NaN());\n\n  get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(evolved_vars) =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim, FrameType>>(\n          minkowski_vars);  // conformal factor is 1\n\n  const auto det_spatial_metric = determinant(\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim, FrameType>>(\n          minkowski_vars));\n  const auto conformal_factor = pow(get(det_spatial_metric), -1. / 6.);\n  Scalar<DataVector> conformal_factor_squared{};\n  get(conformal_factor_squared) = square(conformal_factor);\n\n  get(get<::Ccz4::Tags::ConformalFactor<DataVector>>(evolved_vars)) =\n      conformal_factor;\n\n  const auto extrinsic_curvature =\n      make_with_value<tnsr::ii<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n  get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars) =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n\n  get<::Ccz4::Tags::ATilde<DataVector, 3>>(evolved_vars) = ::Ccz4::a_tilde(\n      conformal_factor_squared,\n      get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(evolved_vars),\n      extrinsic_curvature,\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars));\n\n  get<::Ccz4::Tags::Theta<DataVector>>(evolved_vars) =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n\n  const auto field_d =\n      make_with_value<tnsr::ijj<DataVector, SpatialDim, FrameType>>(\n          used_for_size, 0.0);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          minkowski_vars);\n  const auto& inverse_conformal_spatial_metric = inverse_spatial_metric;\n  const auto conformal_christoffel_second_kind =\n      ::Ccz4::conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, field_d);\n  const auto contracted_conformal_christoffel_second_kind =\n      ::Ccz4::contracted_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, conformal_christoffel_second_kind);\n  // If Z4 constraint is 0, then \\hat{\\gamma} == \\tilde{\\gamma}\n  get<::Ccz4::Tags::GammaHat<DataVector, 3>>(evolved_vars) =\n      contracted_conformal_christoffel_second_kind;\n\n  Scalar<DataVector> lapse{};\n  if constexpr (UsedForSommerfeldTest) {\n    get(lapse) = get<0>(coords);\n  } else {\n    lapse = get<gr::Tags::Lapse<DataVector>>(minkowski_vars);\n  }\n\n  get<gr::Tags::Lapse<DataVector>>(evolved_vars) = lapse;\n\n  get<gr::Tags::Shift<DataVector, 3>>(evolved_vars) =\n      get<gr::Tags::Shift<DataVector, SpatialDim, FrameType>>(minkowski_vars);\n\n  get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(evolved_vars) =\n      make_with_value<tnsr::I<DataVector, SpatialDim, FrameType>>(used_for_size,\n                                                                  0.0);\n  return evolved_vars;\n}\n}  // namespace Minkowski\n\nnamespace KerrSchild {\n// Compute the spatial derivative of the conformal spatial metric\n//\n// If \\tilde{\\gamma}_{ij} is the conformal metric and \\phi is the\n// conformal factor, \\tilde{\\gamma}_{ij} = \\phi^2 \\gamma_{ij}.\n// Therefore, the derivative of the conformal metric is:\n//   \\partial_k \\tilde{\\gamma}_{ij} =\n//       \\phi^2 \\partial_k \\gamma_{ij} +\n//       \\partial_k \\phi^2 \\gamma_{ij}\n//\n// Since \\phi = (det(\\gamma_{ij}))^{-1/6}:\n//   \\partial_k \\phi^2\n//        = \\partial_k ((det(\\gamma_{ij}))^{-1/6})^2\n//        = \\partial_k (det(\\gamma_{ij}))^{-1/3}\n//        = -(det(\\gamma_{ij}))^{-4/3}  \\partial_k (det(\\gamma_{ij}))/ 3\n//        = -\\phi^4 \\partial_k (det(\\gamma_{ij})) / 3\n//\n// Therefore:\n//   \\partial_k \\tilde{\\gamma}_{ij} =\n//       \\phi^2 \\partial_k \\gamma_{ij} -\n//       \\phi^4 \\partial_k (det(\\gamma_{ij})) \\gamma_{ij} / 3\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ijj<DataVector, SpatialDim, FrameType> get_d_conformal_spatial_metric(\n    const Scalar<DataVector>& conformal_factor_squared,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& d_spatial_metric,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_det_spatial_metric) {\n  tnsr::ijj<DataVector, SpatialDim, FrameType> d_conformal_spatial_metric(\n      get(conformal_factor_squared));\n  for (size_t k = 0; k < SpatialDim; k++) {\n    for (size_t i = 0; i < SpatialDim; i++) {\n      for (size_t j = i; j < SpatialDim; j++) {\n        d_conformal_spatial_metric.get(k, i, j) =\n            get(conformal_factor_squared) * d_spatial_metric.get(k, i, j) -\n            pow<4>(get(conformal_factor_squared)) *\n                d_det_spatial_metric.get(k) * spatial_metric.get(i, j) / 3.;\n      }\n    }\n  }\n  return d_conformal_spatial_metric;\n}\n\n// Compute D_{kij} from eq 6\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ijj<DataVector, SpatialDim, FrameType> get_field_d(\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>&\n        d_conformal_spatial_metric) {\n  tnsr::ijj<DataVector, SpatialDim, FrameType> field_d(\n      get<0, 0, 0>(d_conformal_spatial_metric));\n  for (size_t i = 0; i < field_d.size(); i++) {\n    field_d[i] = 0.5 * d_conformal_spatial_metric[i];\n  }\n  return field_d;\n}\n\n// Compute b^i for KerrSchild\n//\n// Solve eq 12c for b^i, where we assume \\partial_t \\beta^i = 0:\n//   0 = s f b + s \\beta^k \\partial_k \\beta^i\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::I<DataVector, SpatialDim, FrameType> get_b_kerr(\n    const bool evolve_shift,\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift,\n    const tnsr::iJ<DataVector, SpatialDim, FrameType>& d_shift,\n    const double f) {\n  tnsr::I<DataVector, SpatialDim, FrameType> b(get<0>(shift));\n  if (not evolve_shift or\n      not ::Ccz4::fd::System::shifting_shift) {\n    // s == 0\n    // pick b = 0\n    for (auto& component : b) {\n      component = 0.0;\n    }\n  } else {\n    // s == 1\n    // b = -(\\beta^k \\partial_k \\beta^i) / f\n    for (size_t i = 0; i < SpatialDim; i++) {\n      b.get(i) = -shift.get(0) * d_shift.get(0, i);\n      for (size_t k = 1; k < SpatialDim; k++) {\n        b.get(i) -= shift.get(k) * d_shift.get(k, i);\n      }\n      b.get(i) /= f;\n    }\n  }\n  return b;\n}\n\n// Compute the conformal spatial metric\n//\n// \\tilde{\\gamma}_{ij} = \\phi^2 \\gamma_{ij}\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ii<DataVector, SpatialDim, FrameType> get_conformal_spatial_metric(\n    const Scalar<DataVector>& conformal_factor_squared,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& spatial_metric) {\n  tnsr::ii<DataVector, SpatialDim, FrameType> conformal_spatial_metric(\n      get(conformal_factor_squared));\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = i; j < SpatialDim; j++) {\n      conformal_spatial_metric.get(i, j) =\n          get(conformal_factor_squared) * spatial_metric.get(i, j);\n    }\n  }\n  return conformal_spatial_metric;\n}\n\n// Compute the trace of the extrinsic curvature K = K_{ij} * \\gamma^ij\ntemplate <size_t SpatialDim, typename FrameType>\nScalar<DataVector> get_trace_extrinsic_curvature(\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& extrinsic_curvature,\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric) {\n  Scalar<DataVector> trace_extrinsic_curvature(get<0, 0>(extrinsic_curvature));\n  get(trace_extrinsic_curvature) = 0.0;\n  for (size_t i = 0; i < SpatialDim; i++) {\n    for (size_t j = 0; j < SpatialDim; j++) {\n      get(trace_extrinsic_curvature) +=\n          extrinsic_curvature.get(i, j) * inverse_spatial_metric.get(i, j);\n    }\n  }\n  return trace_extrinsic_curvature;\n}\n\n// Compute g(\\alpha)\ninline Scalar<DataVector> get_slicing_condition(\n    const ::Ccz4::SlicingConditionType slicing_condition_type,\n    const Scalar<DataVector>& lapse) {\n  Scalar<DataVector> slicing_condition(get(lapse));\n  if (slicing_condition_type == ::Ccz4::SlicingConditionType::Harmonic) {\n    // harmonic slicing condition: g(\\alpha) = 1.0\n    get(slicing_condition) = 1.0;\n  } else if (slicing_condition_type == ::Ccz4::SlicingConditionType::Log) {\n    // 1 + log slicing condition: g(\\alpha) = 2 / \\alpha\n    get(slicing_condition) = 2.0 / get(lapse);\n  } else {\n    ERROR(\"Unknown Ccz4::SlicingConditionType\");\n  }\n  return slicing_condition;\n}\n\n// Compute K_0 for KerrSchild\n//\n// Solve eq 4g for K_0, where we assume \\partial_t \\alpha = 0:\n//    0 = -\\alpha^2 g(\\alpha) (K - K_0 - 2 \\Theta) +\n//       \\beta^k \\partial_k \\alpha\n//   K_0 = -((\\beta^k \\partial_k \\alpha) / (\\alpha^2 * g(\\alpha)) -\n//           K + 2 \\Theta);\ntemplate <size_t SpatialDim, typename FrameType>\nScalar<DataVector> get_k_0_kerr(\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift,\n    const Scalar<DataVector>& lapse,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_lapse,\n    const Scalar<DataVector>& slicing_condition,\n    const Scalar<DataVector>& theta,\n    const Scalar<DataVector>& trace_extrinsic_curvature) {\n  Scalar<DataVector> k_0(get(lapse));\n  get(k_0) = get<0>(shift) * get<0>(d_lapse);\n  for (size_t k = 1; k < SpatialDim; k++) {\n    get(k_0) += shift.get(k) * d_lapse.get(k);\n  }\n  get(k_0) = -((get(k_0) / (square(get(lapse)) * get(slicing_condition))) -\n               get(trace_extrinsic_curvature) + 2.0 * get(theta));\n  return k_0;\n}\n\n// Compute expected value for LHS of eq 12i\n//\n// \\partial_t b will not be 0 for KerrSchild if evolve_shift == true\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::I<DataVector, SpatialDim, FrameType> get_dt_b_kerr_expected(\n    const bool evolve_shift, const Scalar<DataVector>& eta,\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift,\n    const tnsr::iJ<DataVector, SpatialDim, FrameType>& d_gamma_hat,\n    const tnsr::I<DataVector, SpatialDim, FrameType>& b,\n    const tnsr::iJ<DataVector, SpatialDim, FrameType>& d_b) {\n  tnsr::I<DataVector, SpatialDim, FrameType> dt_b_kerr_expected(get(eta));\n  if (evolve_shift) {\n    // s == 1\n    for (size_t i = 0; i < SpatialDim; i++) {\n      dt_b_kerr_expected.get(i) = -get(eta) * b.get(i);\n\n      if (::Ccz4::fd::System::shifting_shift) {\n        for (size_t k = 0; k < SpatialDim; k++) {\n          dt_b_kerr_expected.get(i) += shift.get(k) * d_b.get(k, i) -\n                                       shift.get(k) * d_gamma_hat.get(k, i);\n        }\n      }\n    }\n  } else {\n    // s == 0\n    for (auto& component : dt_b_kerr_expected) {\n      component = 0.0;\n    }\n  }\n  return dt_b_kerr_expected;\n}\n\ninline Variables<\n    ::Ccz4::fd::Tags::spacetime_reconstruction_tags>\ncompute_prim_solution_for_KerrSchild(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords, const double t,\n    const double f, const bool evolve_shift,\n    const gr::Solutions::KerrSchild& solution) {\n  // Evaluate solution\n  const auto kerrschild_vars = solution.variables(\n      coords, t, typename gr::Solutions::KerrSchild::tags<DataVector>{});\n\n  // get system evolved vars\n  Variables<typename ::Ccz4::fd::System::variables_tag::tags_list> evolved_vars{\n      get<0>(coords).size()};\n\n  const DataVector used_for_size = DataVector(\n      get<0>(coords).size(), std::numeric_limits<double>::signaling_NaN());\n\n  const size_t SpatialDim = 3;\n  using FrameType = Frame::Inertial;\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim, FrameType>>(\n          kerrschild_vars);\n  const auto det_spatial_metric = determinant(spatial_metric);\n  const auto conformal_factor = pow(get(det_spatial_metric), -1. / 6.);\n  Scalar<DataVector> conformal_factor_squared{};\n  get(conformal_factor_squared) = square(conformal_factor);\n  get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(evolved_vars) =\n      get_conformal_spatial_metric(conformal_factor_squared, spatial_metric);\n\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(kerrschild_vars);\n\n  get<gr::Tags::Shift<DataVector, 3>>(evolved_vars) =\n      get<gr::Tags::Shift<DataVector, SpatialDim, FrameType>>(kerrschild_vars);\n  const auto& d_shift =\n      get<Tags::deriv<gr::Tags::Shift<DataVector, SpatialDim, FrameType>,\n                      tmpl::size_t<SpatialDim>, FrameType>>(kerrschild_vars);\n\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim>>>(\n          kerrschild_vars);\n  const auto& d_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim>,\n                      tmpl::size_t<SpatialDim>, FrameType>>(kerrschild_vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          kerrschild_vars);\n  const auto extrinsic_curvature = gr::extrinsic_curvature(\n      lapse, get<gr::Tags::Shift<DataVector, 3>>(evolved_vars), d_shift,\n      spatial_metric, dt_spatial_metric, d_spatial_metric);\n  get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars) =\n      get_trace_extrinsic_curvature(extrinsic_curvature,\n                                    inverse_spatial_metric);\n\n  get<::Ccz4::Tags::ATilde<DataVector, 3>>(evolved_vars) = ::Ccz4::a_tilde(\n      conformal_factor_squared, spatial_metric, extrinsic_curvature,\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars));\n\n  get<::Ccz4::Tags::Theta<DataVector>>(evolved_vars) =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n\n  const auto inverse_conformal_spatial_metric =\n      determinant_and_inverse(\n          get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(evolved_vars))\n          .second;\n  const auto d_det_spatial_metric =\n      get<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          solution.variables(\n              coords, t,\n              tmpl::list<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim,\n                                                         FrameType>>{}));\n  const tnsr::ijj<DataVector, SpatialDim, FrameType>\n      d_conformal_spatial_metric = get_d_conformal_spatial_metric(\n          conformal_factor_squared, spatial_metric, d_spatial_metric,\n          d_det_spatial_metric);\n  const tnsr::ijj<DataVector, SpatialDim, FrameType> field_d =\n      get_field_d(d_conformal_spatial_metric);\n  const auto conformal_christoffel_second_kind =\n      ::Ccz4::conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, field_d);\n  const auto contracted_conformal_christoffel_second_kind =\n      ::Ccz4::contracted_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, conformal_christoffel_second_kind);\n  get<::Ccz4::Tags::GammaHat<DataVector, 3>>(evolved_vars) =\n      contracted_conformal_christoffel_second_kind;  // Z4 constraint is 0\n\n  get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(evolved_vars) =\n      get_b_kerr(evolve_shift,\n                 get<gr::Tags::Shift<DataVector, 3>>(evolved_vars), d_shift, f);\n\n  get<gr::Tags::Lapse<DataVector>>(evolved_vars) = lapse;\n\n  get(get<::Ccz4::Tags::ConformalFactor<DataVector>>(evolved_vars)) =\n      conformal_factor;\n\n  return evolved_vars;\n}\n}  // namespace KerrSchild\n\nnamespace GaugePlaneWave {\n// calculate the spatial derivative of the determinant of the spatial metric\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::i<DataVector, SpatialDim, FrameType>\nget_d_det_spatial_metric_gauge_plane_wave(\n    const Scalar<DataVector>& det_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& d_spatial_metric) {\n  tnsr::i<DataVector, SpatialDim, FrameType> d_det_spatial_metric;\n  ::tenex::evaluate<ti::i>(make_not_null(&d_det_spatial_metric),\n                           det_spatial_metric() *\n                               inverse_spatial_metric(ti::J, ti::K) *\n                               d_spatial_metric(ti::i, ti::j, ti::k));\n  return d_det_spatial_metric;\n}\n\n// calculate the exact time derivative of the gauge plane wave conformal spatial\n// metric\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ii<DataVector, SpatialDim, FrameType>\nget_dt_conformal_spatial_metric_gauge_plane_wave(\n    const Scalar<DataVector>& conformal_factor_squared,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& dt_spatial_metric) {\n  tnsr::ii<DataVector, SpatialDim, FrameType> dt_conformal_spatial_metric;\n  ::tenex::evaluate<ti::i, ti::j>(\n      make_not_null(&dt_conformal_spatial_metric),\n      conformal_factor_squared() * dt_spatial_metric(ti::i, ti::j) -\n          1.0 / 3.0 * conformal_factor_squared() *\n              spatial_metric(ti::i, ti::j) *\n              inverse_spatial_metric(ti::L, ti::K) *\n              dt_spatial_metric(ti::l, ti::k));\n  return dt_conformal_spatial_metric;\n}\n\n// calculate the exact time derivative of the gauge plane wave conforma factor\ntemplate <size_t SpatialDim, typename FrameType>\nScalar<DataVector> get_dt_conformal_factor_gauge_plane_wave(\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& dt_spatial_metric,\n    const Scalar<DataVector>& conformal_factor) {\n  Scalar<DataVector> dt_conformal_factor;\n  ::tenex::evaluate(make_not_null(&dt_conformal_factor),\n                    -1.0 / 6.0 * conformal_factor() *\n                        inverse_spatial_metric(ti::J, ti::K) *\n                        dt_spatial_metric(ti::j, ti::k));\n  return dt_conformal_factor;\n}\n\n// calculate the exact spatial derivative of the gauge plane wave conformal\n// factor\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::i<DataVector, SpatialDim, FrameType>\nget_d_conformal_factor_gauge_plane_wave(\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& d_spatial_metric,\n    const Scalar<DataVector>& conformal_factor) {\n  tnsr::i<DataVector, SpatialDim, FrameType> d_conformal_factor;\n  ::tenex::evaluate<ti::i>(make_not_null(&d_conformal_factor),\n                           -1.0 / 6.0 * conformal_factor() *\n                               inverse_spatial_metric(ti::J, ti::K) *\n                               d_spatial_metric(ti::i, ti::j, ti::k));\n  return d_conformal_factor;\n}\n\n// calculate the exact time derivative of the gauge plane wave trace-free\n// part of the extrinsic curvature\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ii<DataVector, SpatialDim, FrameType> get_dt_a_tilde_gauge_plane_wave(\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& conformal_factor_squared,\n    const Scalar<DataVector>& dt_conformal_factor,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& spatial_metric,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& dt_spatial_metric,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& extrinsic_curvature,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& dt_extrinsic_curvature,\n    const Scalar<DataVector>& trace_extrinsic_curvature,\n    const Scalar<DataVector>& dt_trace_extrinsic_curvature) {\n  tnsr::ii<DataVector, SpatialDim, FrameType> dt_a_tilde;\n  ::tenex::evaluate<ti::i, ti::j>(\n      make_not_null(&dt_a_tilde),\n      2.0 * conformal_factor() * dt_conformal_factor() *\n              (extrinsic_curvature(ti::i, ti::j) -\n               1.0 / 3.0 * trace_extrinsic_curvature() *\n                   spatial_metric(ti::i, ti::j)) +\n          conformal_factor_squared() *\n              (dt_extrinsic_curvature(ti::i, ti::j) -\n               1.0 / 3.0 * dt_trace_extrinsic_curvature() *\n                   spatial_metric(ti::i, ti::j) -\n               1.0 / 3.0 * trace_extrinsic_curvature() *\n                   dt_spatial_metric(ti::i, ti::j)));\n  return dt_a_tilde;\n}\n\n// calculate the time derivative of the inverse spatial metric\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::II<DataVector, SpatialDim, FrameType> get_dt_inverse_spatial_metric(\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& dt_spatial_metric) {\n  tnsr::II<DataVector, SpatialDim, FrameType> dt_inverse_spatial_metric;\n  ::tenex::evaluate<ti::L, ti::K>(make_not_null(&dt_inverse_spatial_metric),\n                                  -1.0 * inverse_spatial_metric(ti::I, ti::L) *\n                                      inverse_spatial_metric(ti::J, ti::K) *\n                                      dt_spatial_metric(ti::i, ti::j));\n  return dt_inverse_spatial_metric;\n}\n\n// calculate the time derivative of the inverse conformal spatial metric\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::II<DataVector, SpatialDim, FrameType>\nget_dt_inverse_conformal_spatial_metric(\n    const tnsr::II<DataVector, SpatialDim, FrameType>&\n        inverse_conformal_spatial_metric,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>&\n        dt_conformal_spatial_metric) {\n  tnsr::II<DataVector, SpatialDim, FrameType>\n      dt_inverse_conformal_spatial_metric;\n  ::tenex::evaluate<ti::L, ti::K>(\n      make_not_null(&dt_inverse_conformal_spatial_metric),\n      -1.0 * inverse_conformal_spatial_metric(ti::I, ti::L) *\n          inverse_conformal_spatial_metric(ti::J, ti::K) *\n          dt_conformal_spatial_metric(ti::i, ti::j));\n  return dt_inverse_conformal_spatial_metric;\n}\n\n// calculate the exact time derivative of the gauge plane wave trace of\n// extrinsic curvature\ntemplate <size_t SpatialDim, typename FrameType>\nScalar<DataVector> get_dt_trace_extrinsic_curvature_gauge_plane_wave(\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& extrinsic_curvature,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& dt_extrinsic_curvature,\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, FrameType>&\n        dt_inverse_spatial_metric) {\n  Scalar<DataVector> dt_trace_extrinsic_curvature;\n  ::tenex::evaluate(make_not_null(&dt_trace_extrinsic_curvature),\n                    inverse_spatial_metric(ti::I, ti::J) *\n                            dt_extrinsic_curvature(ti::i, ti::j) +\n                        dt_inverse_spatial_metric(ti::I, ti::J) *\n                            extrinsic_curvature(ti::i, ti::j));\n  return dt_trace_extrinsic_curvature;\n}\n\n// calculate 1 + H*\\omega^2 in the gauge plane wave\ninline Scalar<DataVector> get_one_plus_h_times_omega_squared(\n    const Scalar<DataVector>& h, const double omega) {\n  Scalar<DataVector> result;\n  ::tenex::evaluate(make_not_null(&result), 1.0 + h() * square(omega));\n  return result;\n}\n\n// calculate the exact time derivative of the gauge plane wave extrinsic\n// curvature\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ii<DataVector, SpatialDim, FrameType>\nget_dt_extrinsic_curvature_gauge_plane_wave(\n    const tnsr::i<DataVector, SpatialDim, FrameType>& k,\n    const Scalar<DataVector>& du_h, const Scalar<DataVector>& du_du_h,\n    const Scalar<DataVector>& one_plus_h_times_omega_squared,\n    const double omega) {\n  tnsr::ii<DataVector, SpatialDim, FrameType> dt_extrinsic_curvature;\n  ::tenex::evaluate<ti::i, ti::j>(\n      make_not_null(&dt_extrinsic_curvature),\n      1.0 / 2.0 * square(omega) * k(ti::i) * k(ti::j) * du_du_h() *\n              Scalar<DataVector>(\n                  pow(get(one_plus_h_times_omega_squared), -1.0 / 2.0))() -\n          1.0 / 4.0 * pow(omega, 4) * k(ti::i) * k(ti::j) * du_h() * du_h() *\n              Scalar<DataVector>(\n                  pow(get(one_plus_h_times_omega_squared), -3.0 / 2.0))());\n  return dt_extrinsic_curvature;\n}\n\n// calculate the exact time derivative of the spatial derivative of the\n// conformal factor\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::i<DataVector, SpatialDim, FrameType>\nget_dt_d_conformal_factor_gauge_plane_wave(\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& dt_conformal_factor,\n    const tnsr::II<DataVector, SpatialDim, FrameType>& inverse_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, FrameType>&\n        dt_inverse_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& d_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& dt_d_spatial_metric) {\n  tnsr::i<DataVector, SpatialDim, FrameType> dt_d_conformal_factor;\n  ::tenex::evaluate<ti::i>(\n      make_not_null(&dt_d_conformal_factor),\n      -1.0 / 6.0 *\n          (dt_conformal_factor() * inverse_spatial_metric(ti::J, ti::K) *\n               d_spatial_metric(ti::i, ti::j, ti::k) +\n           conformal_factor() * dt_inverse_spatial_metric(ti::J, ti::K) *\n               d_spatial_metric(ti::i, ti::j, ti::k) +\n           conformal_factor() * inverse_spatial_metric(ti::J, ti::K) *\n               dt_d_spatial_metric(ti::i, ti::j, ti::k)));\n  return dt_d_conformal_factor;\n}\n\n// calculate the exact time derivative of the spatial derivative of the\n// spatial metric\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ijj<DataVector, SpatialDim, FrameType>\nget_dt_d_spatial_metric_gauge_plane_wave(\n    const tnsr::i<DataVector, SpatialDim, FrameType>& k,\n    const Scalar<DataVector>& du_du_h, const double omega) {\n  tnsr::ijj<DataVector, SpatialDim, FrameType> dt_d_spatial_metric;\n  ::tenex::evaluate<ti::i, ti::j, ti::k>(\n      make_not_null(&dt_d_spatial_metric),\n      -1.0 * omega * k(ti::i) * k(ti::j) * k(ti::k) * du_du_h());\n  return dt_d_spatial_metric;\n}\n\n// calculate the exact time derivative of the spatial derivative of the\n// conformal spatial metric\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::ijj<DataVector, SpatialDim, FrameType>\nget_dt_d_conformal_spatial_metric_gauge_plane_wave(\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& spatial_metric,\n    const tnsr::ii<DataVector, SpatialDim, FrameType>& dt_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& d_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& dt_d_spatial_metric,\n    const Scalar<DataVector>& conformal_factor,\n    const Scalar<DataVector>& dt_conformal_factor,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_conformal_factor,\n    const tnsr::i<DataVector, SpatialDim, FrameType>& dt_d_conformal_factor) {\n  tnsr::ijj<DataVector, SpatialDim, FrameType> dt_d_conformal_spatial_metric;\n  ::tenex::evaluate<ti::l, ti::j, ti::k>(\n      make_not_null(&dt_d_conformal_spatial_metric),\n      2.0 * dt_spatial_metric(ti::j, ti::k) * conformal_factor() *\n              d_conformal_factor(ti::l) +\n          2.0 * spatial_metric(ti::j, ti::k) * dt_conformal_factor() *\n              d_conformal_factor(ti::l) +\n          2.0 * spatial_metric(ti::j, ti::k) * conformal_factor() *\n              dt_d_conformal_factor(ti::l) +\n          2.0 * conformal_factor() * dt_conformal_factor() *\n              d_spatial_metric(ti::l, ti::j, ti::k) +\n          conformal_factor() * conformal_factor() *\n              dt_d_spatial_metric(ti::l, ti::j, ti::k));\n  return dt_d_conformal_spatial_metric;\n}\n\n// calculate the exact time derivative of the gauge plane wave \\hat{\\Gamma}^i\n// the Z4 constraint is assumed to be zero\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::I<DataVector, SpatialDim, FrameType> get_dt_gamma_hat_gauge_plane_wave(\n    const tnsr::II<DataVector, SpatialDim, FrameType>&\n        inverse_conformal_spatial_metric,\n    const tnsr::II<DataVector, SpatialDim, FrameType>&\n        dt_inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>&\n        d_conformal_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>&\n        dt_d_conformal_spatial_metric) {\n  tnsr::I<DataVector, SpatialDim, FrameType> dt_gamma_hat;\n  ::tenex::evaluate<ti::I>(\n      make_not_null(&dt_gamma_hat),\n      dt_inverse_conformal_spatial_metric(ti::I, ti::J) *\n              inverse_conformal_spatial_metric(ti::K, ti::L) *\n              d_conformal_spatial_metric(ti::l, ti::j, ti::k) +\n          inverse_conformal_spatial_metric(ti::I, ti::J) *\n              dt_inverse_conformal_spatial_metric(ti::K, ti::L) *\n              d_conformal_spatial_metric(ti::l, ti::j, ti::k) +\n          inverse_conformal_spatial_metric(ti::I, ti::J) *\n              inverse_conformal_spatial_metric(ti::K, ti::L) *\n              dt_d_conformal_spatial_metric(ti::l, ti::j, ti::k));\n  return dt_gamma_hat;\n}\n\n// calculate the exact time derivative of the shift using Gamma driver\n// b^i is assumed to be zero.\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::I<DataVector, SpatialDim, FrameType> get_dt_shift_gauge_plane_wave(\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift,\n    const tnsr::iJ<DataVector, SpatialDim, FrameType>& d_shift) {\n  auto dt_shift = make_with_value<tnsr::I<DataVector, SpatialDim, FrameType>>(\n      get<0>(shift), 0.0);\n  if (::Ccz4::fd::System::shifting_shift) {\n    ::tenex::evaluate<ti::I>(make_not_null(&dt_shift),\n                             shift(ti::K) * d_shift(ti::k, ti::I));\n  }\n  return dt_shift;\n}\n\n// calculate the exact time derivative of the b^i using Gamma driver\n// b^i is assumed to be zero.\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::I<DataVector, SpatialDim, FrameType> get_dt_b_gauge_plane_wave_expected(\n    const tnsr::I<DataVector, SpatialDim, FrameType>& dt_gamma_hat,\n    const tnsr::iJ<DataVector, SpatialDim, FrameType>& d_gamma_hat,\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift) {\n  tnsr::I<DataVector, SpatialDim, FrameType> dt_b;\n  ::tenex::evaluate<ti::I>(make_not_null(&dt_b), dt_gamma_hat(ti::I));\n  if (::Ccz4::fd::System::shifting_shift) {\n    ::tenex::update<ti::I>(\n        make_not_null(&dt_b),\n        dt_b(ti::I) - shift(ti::K) * d_gamma_hat(ti::k, ti::I));\n  }\n  return dt_b;\n}\n\n// calculate the exact time derivative of the lapse using 1+log slicing\n// theta is assumed to be zero. K0 is the trace extrinsic curvature on the\n// initial time slice\ntemplate <size_t SpatialDim, typename FrameType>\nScalar<DataVector> get_dt_lapse_gauge_plane_wave(\n    const tnsr::i<DataVector, SpatialDim, FrameType>& d_lapse,\n    const tnsr::I<DataVector, SpatialDim, FrameType>& shift) {\n  Scalar<DataVector> dt_lapse;\n  ::tenex::evaluate(make_not_null(&dt_lapse), shift(ti::I) * d_lapse(ti::i));\n  return dt_lapse;\n}\n\n// Compute D_{k}^{ij} from eq 14\ntemplate <size_t SpatialDim, typename FrameType>\ntnsr::iJJ<DataVector, SpatialDim, FrameType> get_field_d_up(\n    const tnsr::II<DataVector, SpatialDim, FrameType>&\n        inverse_conformal_spatial_metric,\n    const tnsr::ijj<DataVector, SpatialDim, FrameType>& field_d) {\n  tnsr::iJJ<DataVector, SpatialDim, FrameType> field_d_up =\n      gr::deriv_inverse_spatial_metric(inverse_conformal_spatial_metric,\n                                       field_d);\n  for (size_t i = 0; i < field_d.size(); i++) {\n    field_d_up[i] *= -1.0;\n  }\n  return field_d_up;\n}\n\ninline Variables<\n    ::Ccz4::fd::Tags::spacetime_reconstruction_tags>\ncompute_prim_solution_for_GaugePlaneWave(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& coords, const double t,\n    const gr::Solutions::GaugePlaneWave<3>& solution,\n    const gr::Solutions::GaugePlaneWave<3>::IntermediateVars<DataVector>&\n        intermediate_sol) {\n  const Scalar<DataVector> h{intermediate_sol.h};\n  const Scalar<DataVector> du_h{intermediate_sol.du_h};\n  const Scalar<DataVector> du_du_h{intermediate_sol.du_du_h};\n\n  const size_t SpatialDim = 3;\n\n  // Setup solutions\n  const auto gauge_plane_wave_vars = solution.variables(\n      coords, t,\n      typename gr::Solutions::GaugePlaneWave<SpatialDim>::tags<DataVector>{});\n\n  // get system evolved vars\n  Variables<typename ::Ccz4::fd::System::variables_tag::tags_list> evolved_vars{\n      get<0>(coords).size()};\n\n  using FrameType = Frame::Inertial;\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim, FrameType>>(\n          gauge_plane_wave_vars);\n\n  const auto det_spatial_metric = determinant(spatial_metric);\n  const auto conformal_factor = pow(get(det_spatial_metric), -1. / 6.);\n  Scalar<DataVector> conformal_factor_squared{};\n  get(conformal_factor_squared) = square(conformal_factor);\n  get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(evolved_vars) =\n      KerrSchild::get_conformal_spatial_metric(conformal_factor_squared,\n                                               spatial_metric);\n\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(gauge_plane_wave_vars);\n\n  get<gr::Tags::Shift<DataVector, 3>>(evolved_vars) =\n      get<gr::Tags::Shift<DataVector, SpatialDim, FrameType>>(\n          gauge_plane_wave_vars);\n\n  get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars) =\n      KerrSchild::get_trace_extrinsic_curvature(\n          get<gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim, FrameType>>(\n              gauge_plane_wave_vars),\n          get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim,\n                                             FrameType>>(\n              gauge_plane_wave_vars));\n\n  get<::Ccz4::Tags::ATilde<DataVector, 3>>(evolved_vars) = ::Ccz4::a_tilde(\n      conformal_factor_squared, spatial_metric,\n      get<gr::Tags::ExtrinsicCurvature<DataVector, SpatialDim, FrameType>>(\n          gauge_plane_wave_vars),\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(evolved_vars));\n\n  const DataVector used_for_size = DataVector(\n      get<0>(coords).size(), std::numeric_limits<double>::signaling_NaN());\n  get<::Ccz4::Tags::Theta<DataVector>>(evolved_vars) =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n\n  const auto inverse_conformal_spatial_metric =\n      determinant_and_inverse(\n          get<::Ccz4::Tags::ConformalMetric<DataVector, 3>>(evolved_vars))\n          .second;\n  const auto d_det_spatial_metric = get_d_det_spatial_metric_gauge_plane_wave(\n      det_spatial_metric,\n      get<gr::Tags::InverseSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          gauge_plane_wave_vars),\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim>,\n                      tmpl::size_t<SpatialDim>, FrameType>>(\n          gauge_plane_wave_vars));\n  const auto& d_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, SpatialDim>,\n                      tmpl::size_t<SpatialDim>, FrameType>>(\n          gauge_plane_wave_vars);\n  const tnsr::ijj<DataVector, SpatialDim, FrameType>\n      d_conformal_spatial_metric = KerrSchild::get_d_conformal_spatial_metric(\n          conformal_factor_squared, spatial_metric, d_spatial_metric,\n          d_det_spatial_metric);\n  const tnsr::ijj<DataVector, SpatialDim, FrameType> field_d =\n      KerrSchild::get_field_d(d_conformal_spatial_metric);\n  const auto conformal_christoffel_second_kind =\n      ::Ccz4::conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, field_d);\n  const auto contracted_conformal_christoffel_second_kind =\n      ::Ccz4::contracted_conformal_christoffel_second_kind(\n          inverse_conformal_spatial_metric, conformal_christoffel_second_kind);\n  get<::Ccz4::Tags::GammaHat<DataVector, 3>>(evolved_vars) =\n      contracted_conformal_christoffel_second_kind;  // Z4 constraint is 0\n\n  get<::Ccz4::Tags::AuxiliaryShiftB<DataVector, 3>>(evolved_vars) =\n      make_with_value<tnsr::I<DataVector, SpatialDim, FrameType>>(used_for_size,\n                                                                  0.0);\n\n  get<gr::Tags::Lapse<DataVector>>(evolved_vars) = lapse;\n\n  get(get<::Ccz4::Tags::ConformalFactor<DataVector>>(evolved_vars)) =\n      conformal_factor;\n\n  return evolved_vars;\n}\n}  // namespace GaugePlaneWave\n}  // namespace TestHelpers::Ccz4::fd::detail\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/CurvedScalarWave/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"CurvedScalarWaveHelpers\")\n\nset(LIBRARY_SOURCES\n  TestHelpers.cpp\n  Worldtube/TestHelpers.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  DomainBoundaryConditions\n  DomainBoundaryConditionsHelpers\n  DomainCreators\n  Framework\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/CurvedScalarWave/TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Evolution/Systems/CurvedScalarWave/TestHelpers.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\ntemplate <typename DataType>\nScalar<DataType> make_pi(const DataType& used_for_size) {\n  return make_with_value<Scalar<DataType>>(used_for_size, -1.6);\n}\n\ntemplate <size_t Dim, typename DataType>\ntnsr::i<DataType, Dim> make_phi(const DataType& used_for_size) {\n  auto phi = make_with_value<tnsr::i<DataType, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    phi.get(i) = make_with_value<DataType>(used_for_size, -3. * i + 0.5);\n  }\n  return phi;\n}\n\ntemplate <size_t Dim, typename DataType>\ntnsr::i<DataType, Dim> make_d_psi(const DataType& used_for_size) {\n  auto d_psi = make_with_value<tnsr::i<DataType, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    d_psi.get(i) = make_with_value<DataType>(used_for_size, 2. * i + 0.5);\n  }\n  return d_psi;\n}\n\ntemplate <size_t Dim, typename DataType>\ntnsr::i<DataType, Dim> make_d_pi(const DataType& used_for_size) {\n  auto d_pi = make_with_value<tnsr::i<DataType, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    d_pi.get(i) = make_with_value<DataType>(used_for_size, 2.5 * i + 0.5);\n  }\n  return d_pi;\n}\n\ntemplate <size_t Dim, typename DataType>\ntnsr::ij<DataType, Dim> make_d_phi(const DataType& used_for_size) {\n  auto d_phi = make_with_value<tnsr::ij<DataType, Dim>>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      d_phi.get(i, j) =\n          make_with_value<DataType>(used_for_size, 5. * (i - 0.5) + 0.5 + j);\n    }\n  }\n  return d_phi;\n}\n\ntemplate <typename DataType>\nScalar<DataType> make_constraint_gamma1(const DataType& used_for_size) {\n  return make_with_value<Scalar<DataType>>(used_for_size, 5.8);\n}\n\ntemplate <typename DataType>\nScalar<DataType> make_constraint_gamma2(const DataType& used_for_size) {\n  return make_with_value<Scalar<DataType>>(used_for_size, -4.3);\n}\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                           \\\n  template tnsr::i<DataVector, DIM(data)> make_phi(    \\\n      const DataVector& used_for_size);                \\\n  template tnsr::i<DataVector, DIM(data)> make_d_psi(  \\\n      const DataVector& used_for_size);                \\\n  template tnsr::i<DataVector, DIM(data)> make_d_pi(   \\\n      const DataVector& used_for_size);                \\\n  template tnsr::ij<DataVector, DIM(data)> make_d_phi( \\\n      const DataVector& used_for_size);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (1, 2, 3))\n\ntemplate Scalar<DataVector> make_pi(const DataVector& used_for_size);\ntemplate Scalar<DataVector> make_constraint_gamma1(\n    const DataVector& used_for_size);\ntemplate Scalar<DataVector> make_constraint_gamma2(\n    const DataVector& used_for_size);\n\n#undef DIM\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/CurvedScalarWave/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions useful for testing curved scalar wave\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\ntemplate <typename DataType>\nScalar<DataType> make_pi(const DataType& used_for_size);\n\ntemplate <size_t Dim, typename DataType>\ntnsr::i<DataType, Dim> make_phi(const DataType& used_for_size);\n\ntemplate <size_t Dim, typename DataType>\ntnsr::i<DataType, Dim> make_d_psi(const DataType& used_for_size);\n\ntemplate <size_t Dim, typename DataType>\ntnsr::i<DataType, Dim> make_d_pi(const DataType& used_for_size);\n\ntemplate <size_t Dim, typename DataType>\ntnsr::ij<DataType, Dim> make_d_phi(const DataType& used_for_size);\n\ntemplate <typename DataType>\nScalar<DataType> make_constraint_gamma1(const DataType& used_for_size);\n\ntemplate <typename DataType>\nScalar<DataType> make_constraint_gamma2(const DataType& used_for_size);\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/CurvedScalarWave/Worldtube/TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Evolution/Systems/CurvedScalarWave/Worldtube/TestHelpers.hpp\"\n\n#include <cstddef>\n#include <sstream>\n#include <string>\n\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n\nnamespace TestHelpers::CurvedScalarWave::Worldtube {\n\ntemplate <bool EvolveOrbit>\nstd::unique_ptr<DomainCreator<3>> worldtube_binary_compact_object(\n    const double orbit_radius, const double worldtube_radius,\n    const double angular_velocity) {\n  ASSERT(orbit_radius > 4. * worldtube_radius,\n         \"Can't construct a domain with these parameters\");\n  std::stringstream angular_velocity_stream;\n  angular_velocity_stream << std::fixed << std::setprecision(16)\n                          << angular_velocity;\n  std::string binary_compact_object_options =\n      \"BinaryCompactObject:\\n\"\n      \"  ObjectA:\\n\"\n      \"    InnerRadius: \" +\n      std::to_string(worldtube_radius) +\n      \"\\n\"\n      \"    OuterRadius: \" +\n      std::to_string(2. * worldtube_radius) +\n      \"\\n\"\n      \"    XCoord: \" +\n      std::to_string(orbit_radius) +\n      \"\\n\"\n      \"    Interior:\\n\"\n      \"      ExciseWithBoundaryCondition:\\n\"\n      \"        None:\\n\"\n      \"    UseLogarithmicMap: false\\n\"\n      \"  ObjectB:\\n\"\n      \"    InnerRadius: 1.9\\n\"\n      \"    OuterRadius: 2.4\\n\"\n      \"    XCoord: -1e-99\\n\"\n      \"    Interior:\\n\"\n      \"      ExciseWithBoundaryCondition:\\n\"\n      \"        None\\n\"\n      \"    UseLogarithmicMap: false\\n\"\n      \"  CenterOfMassOffset: [0., 0.]\\n\"\n      \"  Envelope:\\n\"\n      \"    Radius: 30.0\\n\"\n      \"    RadialDistribution: Linear\\n\"\n      \"  OuterShell:\\n\"\n      \"    Radius: 50.0\\n\"\n      \"    RadialPartitioning: []\\n\"\n      \"    RadialDistribution: Linear\\n\"\n      \"    OpeningAngle: 90.0\\n\"\n      \"    BoundaryCondition:\\n\"\n      \"      None\\n\"\n      \"  InitialRefinement: 0\\n\"\n      \"  InitialGridPoints: 3\\n\"\n      \"  UseEquiangularMap: true\\n\"\n      \"  CubeScale: 1.0\\n\"\n      \"  TimeDependentMaps:\\n\"\n      \"    GridCenters: None\\n\"\n      \"    InitialTime: 0.0\\n\"\n      \"    ExpansionMap: \\n\"\n      \"      InitialValues: [1.0, 0.0, 0.0]\\n\"\n      \"      AsymptoticVelocityOuterBoundary: 0.0\\n\"\n      \"      DecayTimescaleOuterBoundary: 1.0\\n\"\n      \"    RotationMap:\\n\"\n      \"      InitialAngularVelocity: [0.0, 0.0, \" +\n      angular_velocity_stream.str() +\n      \"]\\n\"\n      \"    TranslationMap: None\\n\"\n      \"    SkewMap: None\\n\"\n      \"    ShapeMapA:\\n\"\n      \"      LMax: 2\\n\"\n      \"      CoefficientTruncationLimit: 0.\\n\"\n      \"      InitialValues: Spherical\\n\"\n      \"      SizeInitialValues: [0.0, 0.0, 0.0]\\n\"\n      \"      TransitionEndsAtCube: false\\n\"\n      \"    ShapeMapB:\\n\"\n      \"      LMax: 2\\n\"\n      \"      CoefficientTruncationLimit: 0.\\n\"\n      \"      InitialValues: Spherical\\n\"\n      \"      SizeInitialValues: [0.0, 0.0, 0.0]\\n\"\n      \"      TransitionEndsAtCube: false\\n\";\n  return ::TestHelpers::test_option_tag<::domain::OptionTags::DomainCreator<3>,\n                                        Metavariables<EvolveOrbit>>(\n      binary_compact_object_options);\n}\n\ntemplate std::unique_ptr<DomainCreator<3>>\nworldtube_binary_compact_object<true>(double, double, double);\n\ntemplate std::unique_ptr<DomainCreator<3>>\nworldtube_binary_compact_object<false>(double, double, double);\n}  // namespace TestHelpers::CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/CurvedScalarWave/Worldtube/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <string>\n\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/Domain/Creators/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::CurvedScalarWave::Worldtube {\ntemplate <bool EvolveOrbit>\nstruct Metavariables {\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithBoundaryConditions<3>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    // we set `UseWorldtube` to `false` here so the functions of time are valid\n    // which simplifies testing.\n    using factory_classes = tmpl::map<tmpl::pair<\n        DomainCreator<3>,\n        tmpl::list<::domain::creators::BinaryCompactObject<EvolveOrbit>>>>;\n  };\n};\n\n// returns a binary compact object domain creator with the excision spheres\n// corresponding to the circular worldtube setup: a central excision sphere\n// (the central black hole) and a worldtube excision sphere in circular orbit\n// around it with angular velocity R^{-3/2}, where R is the orbital radius.\ntemplate <bool EvolveOrbit>\nstd::unique_ptr<DomainCreator<3>> worldtube_binary_compact_object(\n    const double orbit_radius, const double worldtube_radius,\n    const double angular_velocity);\n\n}  // namespace TestHelpers::CurvedScalarWave::Worldtube\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/ForceFree/FiniteDifference/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ForceFree/FiniteDifference/Tags.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers {\n\n/*!\n * \\brief Defines functions useful for testing subcell in ForceFree evolution\n * system\n */\nnamespace ForceFree::fd {\n\nusing GhostData = evolution::dg::subcell::GhostData;\n\ntemplate <typename F>\nDirectionalIdMap<3, GhostData> compute_ghost_data(\n    const Mesh<3>& subcell_mesh,\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& volume_logical_coords,\n    const DirectionMap<3, Neighbors<3>>& neighbors,\n    const size_t ghost_zone_size, const F& compute_variables_of_neighbor_data) {\n  DirectionalIdMap<3, GhostData> ghost_data{};\n\n  for (const auto& [direction, neighbors_in_direction] : neighbors) {\n    REQUIRE(neighbors_in_direction.size() == 1);\n    const ElementId<3>& neighbor_id = *neighbors_in_direction.begin();\n    auto neighbor_logical_coords = volume_logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    const auto neighbor_vars_for_reconstruction =\n        compute_variables_of_neighbor_data(neighbor_logical_coords);\n\n    DirectionMap<3, bool> directions_to_slice{};\n    directions_to_slice[direction.opposite()] = true;\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars_for_reconstruction.data(),\n                       neighbor_vars_for_reconstruction.size()),\n        subcell_mesh.extents(), ghost_zone_size,\n        std::unordered_set{direction.opposite()}, 0, {});\n\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n\n    ghost_data[DirectionalId<3>{direction, neighbor_id}] = GhostData{1};\n    ghost_data.at(DirectionalId<3>{direction, neighbor_id})\n        .neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n  return ghost_data;\n}\n\ntemplate <typename Reconstructor>\nvoid test_reconstructor(const size_t points_per_dimension,\n                        const Reconstructor& derived_reconstructor) {\n  // 1. create the variables to be reconstructed (evolved variables and current\n  //    density TildeJ) being linear to coords\n  // 2. send through reconstruction\n  // 3. check if evolved variables were reconstructed correctly\n\n  const ::ForceFree::fd::Reconstructor& reconstructor = derived_reconstructor;\n  static_assert(tmpl::list_contains_v<\n                typename ::ForceFree::fd::Reconstructor::creatable_classes,\n                Reconstructor>);\n\n  // create an element and its neighbor elements\n  DirectionMap<3, Neighbors<3>> neighbors{};\n  for (size_t i = 0; i < 2 * 3; ++i) {\n    neighbors[gsl::at(Direction<3>::all_directions(), i)] = Neighbors<3>{\n        {ElementId<3>{i + 1, {}}}, OrientationMap<3>::create_aligned()};\n  }\n  const Element<3> element{ElementId<3>{0, {}}, neighbors};\n\n  using TildeE = ::ForceFree::Tags::TildeE;\n  using TildeB = ::ForceFree::Tags::TildeB;\n  using TildePsi = ::ForceFree::Tags::TildePsi;\n  using TildePhi = ::ForceFree::Tags::TildePhi;\n  using TildeQ = ::ForceFree::Tags::TildeQ;\n  using TildeJ = ::ForceFree::Tags::TildeJ;\n\n  using cons_tags = tmpl::list<TildeE, TildeB, TildePsi, TildePhi, TildeQ>;\n\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(subcell_mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < 3; ++i) {\n    logical_coords.get(i) += 4.0 * i;\n  }\n\n  // a simple, linear variables for testing purpose\n  const auto compute_solution = [](const auto& coords) {\n    Variables<::ForceFree::fd::tags_list_for_reconstruction> vars{\n        get<0>(coords).size(), 0.0};\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        get<TildeE>(vars).get(j) += 1.0 * coords.get(i);\n        get<TildeB>(vars).get(j) += 2.0 * coords.get(i);\n        get<TildeJ>(vars).get(j) += 3.0 * coords.get(i);\n      }\n      get(get<TildePsi>(vars)) += 4.0 * coords.get(i);\n      get(get<TildePhi>(vars)) += 5.0 * coords.get(i);\n      get(get<TildeQ>(vars)) += 6.0 * coords.get(i);\n    }\n    get(get<TildePsi>(vars)) += 2.0;\n    get(get<TildePhi>(vars)) += 3.0;\n    get(get<TildeQ>(vars)) += 40.0;\n    for (size_t j = 0; j < 3; ++j) {\n      get<TildeE>(vars).get(j) += 1.0e-2 * (j + 1.0) + 10.0;\n      get<TildeB>(vars).get(j) += 1.0e-2 * (j + 2.0) + 20.0;\n      get<TildeJ>(vars).get(j) += 1.0e-2 * (j + 3.0) + 30.0;\n    }\n    return vars;\n  };\n\n  const size_t num_subcell_grid_pts = subcell_mesh.number_of_grid_points();\n\n  Variables<::ForceFree::fd::tags_list_for_reconstruction>\n      volume_vars_and_tilde_j{num_subcell_grid_pts};\n  volume_vars_and_tilde_j.assign_subset(compute_solution(logical_coords));\n\n  Variables<cons_tags> volume_vars =\n      volume_vars_and_tilde_j.reference_subset<cons_tags>();\n  const tnsr::I<DataVector, 3> volume_tilde_j =\n      get<TildeJ>(volume_vars_and_tilde_j);\n\n  // compute ghost data from neighbor\n  const DirectionalIdMap<3, GhostData> ghost_data =\n      compute_ghost_data(subcell_mesh, logical_coords, element.neighbors(),\n                         reconstructor.ghost_zone_size(), compute_solution);\n\n  // create Variables on lower and upper faces to perform reconstruction\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n  using recons_tags = ::ForceFree::fd::tags_list_for_reconstruction;\n\n  std::array<Variables<recons_tags>, 3> vars_on_lower_face =\n      make_array<3>(Variables<recons_tags>(reconstructed_num_pts));\n  std::array<Variables<recons_tags>, 3> vars_on_upper_face =\n      make_array<3>(Variables<recons_tags>(reconstructed_num_pts));\n\n  // Now we have everything to call the reconstruction\n  dynamic_cast<const Reconstructor&>(reconstructor)\n      .reconstruct(make_not_null(&vars_on_lower_face),\n                   make_not_null(&vars_on_upper_face), volume_vars,\n                   volume_tilde_j, element, ghost_data, subcell_mesh);\n\n  for (size_t dim = 0; dim < 3; ++dim) {\n    CAPTURE(dim);\n\n    // construct face-centered coordinates\n    const auto basis = make_array<3>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<3>(Spectral::Quadrature::CellCentered);\n    auto extents = make_array<3>(points_per_dimension);\n    gsl::at(extents, dim) = points_per_dimension + 1;\n    gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n    const Mesh<3> face_centered_mesh{extents, basis, quadrature};\n    auto logical_coords_face_centered = logical_coordinates(face_centered_mesh);\n    for (size_t i = 1; i < 3; ++i) {\n      logical_coords_face_centered.get(i) =\n          logical_coords_face_centered.get(i) + 4.0 * i;\n    }\n\n    // check reconstructed values for reconstruct() function\n    Variables<recons_tags> expected_face_values{\n        face_centered_mesh.number_of_grid_points()};\n    expected_face_values.assign_subset(\n        compute_solution(logical_coords_face_centered));\n\n    tmpl::for_each<::ForceFree::fd::tags_list_for_reconstruction>(\n        [dim, &expected_face_values, &vars_on_lower_face,\n         &vars_on_upper_face](auto tag_to_check_v) {\n          using tag_to_check = tmpl::type_from<decltype(tag_to_check_v)>;\n          CAPTURE(db::tag_name<tag_to_check>());\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(gsl::at(vars_on_lower_face, dim)),\n              get<tag_to_check>(expected_face_values));\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(gsl::at(vars_on_upper_face, dim)),\n              get<tag_to_check>(expected_face_values));\n        });\n\n    // Test reconstruct_fd_neighbor\n    const size_t num_pts_on_mortar =\n        face_centered_mesh.slice_away(dim).number_of_grid_points();\n\n    Variables<recons_tags> upper_side_vars_on_mortar{num_pts_on_mortar};\n\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct_fd_neighbor(make_not_null(&upper_side_vars_on_mortar),\n                                 volume_vars, volume_tilde_j, element,\n                                 ghost_data, subcell_mesh,\n                                 Direction<3>{dim, Side::Upper});\n\n    Variables<recons_tags> lower_side_vars_on_mortar{num_pts_on_mortar};\n\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct_fd_neighbor(make_not_null(&lower_side_vars_on_mortar),\n                                 volume_vars, volume_tilde_j, element,\n                                 ghost_data, subcell_mesh,\n                                 Direction<3>{dim, Side::Lower});\n\n    tmpl::for_each<cons_tags>(\n        [dim, &expected_face_values, &lower_side_vars_on_mortar,\n         &face_centered_mesh, &upper_side_vars_on_mortar](auto tag_to_check_v) {\n          using tag_to_check = tmpl::type_from<decltype(tag_to_check_v)>;\n          CAPTURE(db::tag_name<tag_to_check>());\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(lower_side_vars_on_mortar),\n              data_on_slice(get<tag_to_check>(expected_face_values),\n                            face_centered_mesh.extents(), dim, 0));\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(upper_side_vars_on_mortar),\n              data_on_slice(get<tag_to_check>(expected_face_values),\n                            face_centered_mesh.extents(), dim,\n                            face_centered_mesh.extents(dim) - 1));\n        });\n  }\n}\n\n}  // namespace ForceFree::fd\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/PrimReconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/Tags.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Evolution/VariableFixing/FixToAtmosphere.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::grmhd::GhValenciaDivClean::fd {\nnamespace detail {\nusing GhostData = evolution::dg::subcell::GhostData;\ntemplate <typename F>\nDirectionalIdMap<3, GhostData> compute_ghost_data(\n    const Mesh<3>& subcell_mesh,\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& volume_logical_coords,\n    const DirectionMap<3, Neighbors<3>>& neighbors,\n    const size_t ghost_zone_size, const F& compute_variables_of_neighbor_data) {\n  DirectionalIdMap<3, GhostData> ghost_data{};\n  for (const auto& [direction, neighbors_in_direction] : neighbors) {\n    REQUIRE(neighbors_in_direction.size() == 1);\n    const ElementId<3>& neighbor_id = *neighbors_in_direction.begin();\n    auto neighbor_logical_coords = volume_logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    const auto neighbor_vars_for_reconstruction =\n        compute_variables_of_neighbor_data(neighbor_logical_coords);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars_for_reconstruction.data(),\n                       neighbor_vars_for_reconstruction.size()),\n        subcell_mesh.extents(), ghost_zone_size,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    ghost_data[DirectionalId<3>{direction, neighbor_id}] = GhostData{1};\n    ghost_data.at(DirectionalId<3>{direction, neighbor_id})\n        .neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n  return ghost_data;\n}\n\ninline Variables<::grmhd::GhValenciaDivClean::Tags::\n                     primitive_grmhd_and_spacetime_reconstruction_tags>\ncompute_prim_solution(\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& coords) {\n  using Rho = hydro::Tags::RestMassDensity<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using MagField = hydro::Tags::MagneticField<DataVector, 3>;\n  using Phi = hydro::Tags::DivergenceCleaningField<DataVector>;\n  using VelocityW =\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n  Variables<::grmhd::GhValenciaDivClean::Tags::\n                primitive_grmhd_and_spacetime_reconstruction_tags>\n      vars{get<0>(coords).size(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    get(get<Rho>(vars)) += coords.get(i);\n    get(get<ElectronFraction>(vars)) += coords.get(i);\n    get(get<Temperature>(vars)) += coords.get(i);\n    get(get<Phi>(vars)) += coords.get(i);\n    for (size_t j = 0; j < 3; ++j) {\n      get<VelocityW>(vars).get(j) += coords.get(i);\n      get<MagField>(vars).get(j) += coords.get(i);\n    }\n  }\n  get(get<Rho>(vars)) += 2.0;\n  get(get<ElectronFraction>(vars)) += 15.0;\n  get(get<Temperature>(vars)) += 30.0;\n  get(get<Phi>(vars)) += 50.0;\n  for (size_t j = 0; j < 3; ++j) {\n    get<VelocityW>(vars).get(j) += 1.0e-2 * (j + 2.0) + 10.0;\n    get<MagField>(vars).get(j) += 1.0e-2 * (j + 2.0) + 60.0;\n  }\n  auto& spacetime_metric = get<gr::Tags::SpacetimeMetric<DataVector, 3>>(vars);\n  spacetime_metric.get(0, 0) = -1.0;\n  for (size_t j = 1; j < 4; ++j) {\n    spacetime_metric.get(j, j) = 1.0;\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t k = 0; k <= j; ++k) {\n        spacetime_metric.get(j, k) += (k + 1) * j * 1.0e-3 * coords.get(i);\n      }\n    }\n  }\n  auto& phi = get<gh::Tags::Phi<DataVector, 3>>(vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t a = 0; a < 4; ++a) {\n      for (size_t b = a; b < 4; ++b) {\n        phi.get(i, a, b) = (10 * i + 50 * a + 1000 * b + 1) * coords.get(i);\n      }\n    }\n  }\n\n  auto& pi = get<gh::Tags::Pi<DataVector, 3>>(vars);\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = a; b < 4; ++b) {\n      pi.get(a, b) = (500 * a + 10000 * b + 1) * get<0>(coords);\n      for (size_t i = 1; i < 3; ++i) {\n        pi.get(a, b) += (500 * a + 10000 * b + 1 + i) * coords.get(i);\n      }\n    }\n  }\n  return vars;\n}\n\ninline Element<3> set_element(const bool skip_last = false) {\n  DirectionMap<3, Neighbors<3>> neighbors{};\n  for (size_t i = 0; i < 6; ++i) {\n    if (skip_last and i == 5) {\n      break;\n    }\n    neighbors[gsl::at(Direction<3>::all_directions(), i)] = Neighbors<3>{\n        {ElementId<3>{i + 1, {}}}, OrientationMap<3>::create_aligned()};\n  }\n  return Element<3>{ElementId<3>{0, {}}, neighbors};\n}\n\ninline tnsr::I<DataVector, 3, Frame::ElementLogical> set_logical_coordinates(\n    const Mesh<3>& subcell_mesh) {\n  auto logical_coords = logical_coordinates(subcell_mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < 3; ++i) {\n    logical_coords.get(i) += 4.0 * i;\n  }\n  return logical_coords;\n}\n\ntemplate <size_t ThermodynamicDim,\n          typename Reconstructor>  // DerivedReconstructor?\nvoid test_prim_reconstructor_impl(\n    const size_t points_per_dimension,\n    const Reconstructor& derived_reconstructor,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos,\n    const VariableFixing::FixToAtmosphere<3>& fix_to_atmopshere) {\n  // 1. Create linear prims to reconstruct\n  // 2. send through reconstruction\n  // 3. check prims and cons were computed correctly\n  namespace ghmhd = ::grmhd::GhValenciaDivClean;\n  using NeutrinoTransportSystem =\n      typename Reconstructor::system::neutrino_transport_system;\n  using System = ::grmhd::GhValenciaDivClean::System<NeutrinoTransportSystem>;\n\n  const ghmhd::fd::Reconstructor<System>& reconstructor = derived_reconstructor;\n  static_assert(tmpl::list_contains_v<\n                typename ghmhd::fd::Reconstructor<System>::creatable_classes,\n                Reconstructor>);\n\n  using Rho = hydro::Tags::RestMassDensity<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, 3>;\n  using MagField = hydro::Tags::MagneticField<DataVector, 3>;\n  using Phi = hydro::Tags::DivergenceCleaningField<DataVector>;\n  using VelocityW =\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using LorentzFactor = hydro::Tags::LorentzFactor<DataVector>;\n  using SpacetimeMetric = gr::Tags::SpacetimeMetric<DataVector, 3>;\n  using Lapse = gr::Tags::Lapse<DataVector>;\n  using Shift = gr::Tags::Shift<DataVector, 3>;\n  using SpatialMetric = gr::Tags::SpatialMetric<DataVector, 3>;\n  using InverseSpatialMetric = gr::Tags::InverseSpatialMetric<DataVector, 3>;\n  using SqrtDetSpatialMetric = gr::Tags::SqrtDetSpatialMetric<DataVector>;\n\n  using prims_tags = hydro::grmhd_tags<DataVector>;\n  using cons_tags = typename System::variables_tag::tags_list;\n  using spacetime_tags =\n      ::grmhd::GhValenciaDivClean::Tags::spacetime_reconstruction_tags;\n\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  const auto logical_coords = set_logical_coordinates(subcell_mesh);\n  const Element<3> element = set_element(true);\n\n  auto neighbors_for_data = element.neighbors();\n  neighbors_for_data[gsl::at(Direction<3>::all_directions(), 5)] =\n      Neighbors<3>{{ElementId<3>::external_boundary_id()},\n                   OrientationMap<3>::create_aligned()};\n  const DirectionalIdMap<3, GhostData> ghost_data = compute_ghost_data(\n      subcell_mesh, logical_coords, neighbors_for_data,\n      reconstructor.ghost_zone_size(), compute_prim_solution);\n\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n\n  using fd_package_data_argument_tags =\n      ::grmhd::GhValenciaDivClean::fd::tags_list_for_reconstruct;\n  using dg_package_data_argument_tags =\n      ::grmhd::GhValenciaDivClean::fd::tags_list_for_reconstruct_fd_neighbor;\n\n  std::array<Variables<fd_package_data_argument_tags>, 3> vars_on_lower_face =\n      make_array<3>(\n          Variables<fd_package_data_argument_tags>(reconstructed_num_pts));\n  std::array<Variables<fd_package_data_argument_tags>, 3> vars_on_upper_face =\n      make_array<3>(\n          Variables<fd_package_data_argument_tags>(reconstructed_num_pts));\n\n  Variables<prims_tags> volume_prims{subcell_mesh.number_of_grid_points()};\n  Variables<cons_tags> volume_cons_vars{subcell_mesh.number_of_grid_points()};\n  Variables<spacetime_tags> volume_spacetime_vars{\n      subcell_mesh.number_of_grid_points()};\n  {\n    const auto volume_prims_for_recons = compute_prim_solution(logical_coords);\n    tmpl::for_each<\n        tmpl::list<Rho, ElectronFraction, Temperature, MagField, Phi>>(\n        [&volume_prims, &volume_prims_for_recons](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          get<tag>(volume_prims) = get<tag>(volume_prims_for_recons);\n        });\n    get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_cons_vars) =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_prims_for_recons);\n    const auto spatial_metric = gr::spatial_metric(\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_cons_vars));\n\n    get(get<LorentzFactor>(volume_prims)) =\n        sqrt(1.0 + get(dot_product(get<VelocityW>(volume_prims_for_recons),\n                                   get<VelocityW>(volume_prims_for_recons),\n                                   spatial_metric)));\n    for (size_t i = 0; i < 3; ++i) {\n      get<Velocity>(volume_prims).get(i) =\n          get<VelocityW>(volume_prims_for_recons).get(i) /\n          get(get<LorentzFactor>(volume_prims));\n    }\n    get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_cons_vars) =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_prims_for_recons);\n    get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_spacetime_vars) =\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(volume_prims_for_recons);\n    get<gh::Tags::Phi<DataVector, 3>>(volume_spacetime_vars) =\n        get<gh::Tags::Phi<DataVector, 3>>(volume_prims_for_recons);\n    get<gh::Tags::Pi<DataVector, 3>>(volume_spacetime_vars) =\n        get<gh::Tags::Pi<DataVector, 3>>(volume_prims_for_recons);\n  }\n\n  // Now we have everything to call the reconstruction\n  if constexpr (Reconstructor::use_adaptive_order) {\n    std::array<std::vector<std::uint8_t>, 3> reconstruction_order_storage{};\n    std::optional<std::array<gsl::span<std::uint8_t>, 3>>\n        reconstruction_order{};\n    reconstruction_order.emplace();\n    for (size_t i = 0; i < 3; ++i) {\n      auto order_extents = subcell_mesh.extents();\n      order_extents[i] += 2;\n      gsl::at(reconstruction_order_storage, i).resize(order_extents.product());\n      // Ensure we have reset the values to max so the min calls are fine.\n      std::fill_n(gsl::at(reconstruction_order_storage, i).begin(),\n                  order_extents.product(),\n                  std::numeric_limits<std::uint8_t>::max());\n      gsl::at(reconstruction_order.value(), i) = gsl::span<std::uint8_t>{\n          gsl::at(reconstruction_order_storage, i).data(),\n          gsl::at(reconstruction_order_storage, i).size()};\n    }\n\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct(make_not_null(&vars_on_lower_face),\n                     make_not_null(&vars_on_upper_face),\n                     make_not_null(&reconstruction_order), volume_prims,\n                     volume_cons_vars, eos, element, ghost_data, subcell_mesh,\n                     fix_to_atmopshere);\n    for (size_t d = 0; d < 3; ++d) {\n      CAPTURE(d);\n      for (size_t i = 0; i < gsl::at(reconstruction_order_storage, d).size();\n           ++i) {\n        CAPTURE(i);\n        CHECK(gsl::at(reconstruction_order_storage, d)[i] >= 1);\n        CHECK(gsl::at(reconstruction_order_storage, d)[i] <= 9);\n      }\n    }\n  } else {\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct(make_not_null(&vars_on_lower_face),\n                     make_not_null(&vars_on_upper_face), volume_prims,\n                     volume_cons_vars, eos, element, ghost_data, subcell_mesh,\n                     fix_to_atmopshere);\n  }\n\n  for (size_t dim = 0; dim < 3; ++dim) {\n    CAPTURE(dim);\n    const auto basis = make_array<3>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<3>(Spectral::Quadrature::CellCentered);\n    auto extents = make_array<3>(points_per_dimension);\n    gsl::at(extents, dim) = points_per_dimension + 1;\n    gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n    const Mesh<3> face_centered_mesh{extents, basis, quadrature};\n    auto logical_coords_face_centered = logical_coordinates(face_centered_mesh);\n    for (size_t i = 1; i < 3; ++i) {\n      logical_coords_face_centered.get(i) =\n          logical_coords_face_centered.get(i) + 4.0 * i;\n    }\n    Variables<fd_package_data_argument_tags> expected_lower_face_values{\n        face_centered_mesh.number_of_grid_points()};\n    expected_lower_face_values.assign_subset(\n        compute_prim_solution(logical_coords_face_centered));\n    if constexpr (ThermodynamicDim == 2) {\n      get<SpecificInternalEnergy>(expected_lower_face_values) =\n          eos.specific_internal_energy_from_density_and_temperature(\n              get<Rho>(expected_lower_face_values),\n              get<Temperature>(expected_lower_face_values));\n      get<Pressure>(expected_lower_face_values) =\n          eos.pressure_from_density_and_energy(\n              get<Rho>(expected_lower_face_values),\n              get<SpecificInternalEnergy>(expected_lower_face_values));\n    } else {\n      get<SpecificInternalEnergy>(expected_lower_face_values) =\n          eos.specific_internal_energy_from_density(\n              get<Rho>(expected_lower_face_values));\n      get<Pressure>(expected_lower_face_values) =\n          eos.pressure_from_density(get<Rho>(expected_lower_face_values));\n    }\n\n    gr::spatial_metric(\n        make_not_null(&get<SpatialMetric>(expected_lower_face_values)),\n        get<SpacetimeMetric>(expected_lower_face_values));\n    determinant_and_inverse(\n        make_not_null(&get<SqrtDetSpatialMetric>(expected_lower_face_values)),\n        make_not_null(&get<InverseSpatialMetric>(expected_lower_face_values)),\n        get<SpatialMetric>(expected_lower_face_values));\n    get(get<SqrtDetSpatialMetric>(expected_lower_face_values)) =\n        sqrt(get(get<SqrtDetSpatialMetric>(expected_lower_face_values)));\n    gr::shift(make_not_null(&get<Shift>(expected_lower_face_values)),\n              get<SpacetimeMetric>(expected_lower_face_values),\n              get<InverseSpatialMetric>(expected_lower_face_values));\n    gr::lapse(make_not_null(&get<Lapse>(expected_lower_face_values)),\n              get<Shift>(expected_lower_face_values),\n              get<SpacetimeMetric>(expected_lower_face_values));\n\n    Variables<fd_package_data_argument_tags> expected_upper_face_values =\n        expected_lower_face_values;\n    gr::spatial_metric(\n        make_not_null(&get<SpatialMetric>(expected_upper_face_values)),\n        get<SpacetimeMetric>(expected_upper_face_values));\n    determinant_and_inverse(\n        make_not_null(&get<SqrtDetSpatialMetric>(expected_upper_face_values)),\n        make_not_null(&get<InverseSpatialMetric>(expected_upper_face_values)),\n        get<SpatialMetric>(expected_upper_face_values));\n    get(get<SqrtDetSpatialMetric>(expected_upper_face_values)) =\n        sqrt(get(get<SqrtDetSpatialMetric>(expected_upper_face_values)));\n    gr::shift(make_not_null(&get<Shift>(expected_upper_face_values)),\n              get<SpacetimeMetric>(expected_upper_face_values),\n              get<InverseSpatialMetric>(expected_upper_face_values));\n    gr::lapse(make_not_null(&get<Lapse>(expected_upper_face_values)),\n              get<Shift>(expected_upper_face_values),\n              get<SpacetimeMetric>(expected_upper_face_values));\n\n    get(get<LorentzFactor>(expected_lower_face_values)) = sqrt(\n        1.0 + get(dot_product(get<VelocityW>(expected_lower_face_values),\n                              get<VelocityW>(expected_lower_face_values),\n                              get<SpatialMetric>(expected_lower_face_values))));\n    get(get<LorentzFactor>(expected_upper_face_values)) = sqrt(\n        1.0 + get(dot_product(get<VelocityW>(expected_upper_face_values),\n                              get<VelocityW>(expected_upper_face_values),\n                              get<SpatialMetric>(expected_upper_face_values))));\n    for (size_t i = 0; i < 3; ++i) {\n      get<Velocity>(expected_lower_face_values).get(i) =\n          get<VelocityW>(expected_lower_face_values).get(i) /\n          get(get<LorentzFactor>(expected_lower_face_values));\n      get<Velocity>(expected_upper_face_values).get(i) =\n          get<VelocityW>(expected_upper_face_values).get(i) /\n          get(get<LorentzFactor>(expected_upper_face_values));\n    }\n\n    namespace mhd = ::grmhd::ValenciaDivClean;\n    mhd::ConservativeFromPrimitive::apply(\n        make_not_null(&get<mhd::Tags::TildeD>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildeYe>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildeTau>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildeS<>>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildeB<>>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildePhi>(expected_lower_face_values)),\n        get<Rho>(expected_lower_face_values),\n        get<ElectronFraction>(expected_lower_face_values),\n        get<SpecificInternalEnergy>(expected_lower_face_values),\n        get<Pressure>(expected_lower_face_values),\n        get<Velocity>(expected_lower_face_values),\n        get<LorentzFactor>(expected_lower_face_values),\n        get<MagField>(expected_lower_face_values),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n            expected_lower_face_values),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(expected_lower_face_values),\n        get<Phi>(expected_lower_face_values));\n    mhd::ConservativeFromPrimitive::apply(\n        make_not_null(&get<mhd::Tags::TildeD>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildeYe>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildeTau>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildeS<>>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildeB<>>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildePhi>(expected_upper_face_values)),\n        get<Rho>(expected_upper_face_values),\n        get<ElectronFraction>(expected_upper_face_values),\n        get<SpecificInternalEnergy>(expected_upper_face_values),\n        get<Pressure>(expected_upper_face_values),\n        get<Velocity>(expected_upper_face_values),\n        get<LorentzFactor>(expected_upper_face_values),\n        get<MagField>(expected_upper_face_values),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n            expected_upper_face_values),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(expected_upper_face_values),\n        get<Phi>(expected_upper_face_values));\n\n    using tags_to_test = tmpl::push_back<\n        tmpl::append<typename mhd::System::variables_tag::tags_list,\n                     prims_tags>,\n        Lapse, Shift, SqrtDetSpatialMetric, SpatialMetric,\n        InverseSpatialMetric>;\n    tmpl::for_each<tags_to_test>([dim, &expected_lower_face_values,\n                                  &expected_upper_face_values,\n                                  &vars_on_lower_face,\n                                  &vars_on_upper_face](auto tag_to_check_v) {\n      using tag_to_check = tmpl::type_from<decltype(tag_to_check_v)>;\n      CAPTURE(db::tag_name<tag_to_check>());\n      CHECK_ITERABLE_APPROX(get<tag_to_check>(gsl::at(vars_on_lower_face, dim)),\n                            get<tag_to_check>(expected_lower_face_values));\n      CHECK_ITERABLE_APPROX(get<tag_to_check>(gsl::at(vars_on_upper_face, dim)),\n                            get<tag_to_check>(expected_upper_face_values));\n    });\n\n    // Test reconstruct_fd_neighbor\n    const size_t num_pts_on_mortar =\n        face_centered_mesh.slice_away(dim).number_of_grid_points();\n    Variables<dg_package_data_argument_tags> upper_side_vars_on_mortar{\n        num_pts_on_mortar};\n    if (dim != 2) {\n      dynamic_cast<const Reconstructor&>(reconstructor)\n          .reconstruct_fd_neighbor(\n              make_not_null(&upper_side_vars_on_mortar), volume_prims,\n              volume_spacetime_vars, eos, element, ghost_data, subcell_mesh,\n              fix_to_atmopshere, Direction<3>{dim, Side::Upper});\n    }\n\n    Variables<dg_package_data_argument_tags> lower_side_vars_on_mortar{\n        num_pts_on_mortar};\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct_fd_neighbor(\n            make_not_null(&lower_side_vars_on_mortar), volume_prims,\n            volume_spacetime_vars, eos, element, ghost_data, subcell_mesh,\n            fix_to_atmopshere, Direction<3>{dim, Side::Lower});\n\n    tmpl::for_each<tmpl::append<tags_to_test, spacetime_tags>>(\n        [dim, &expected_lower_face_values, &expected_upper_face_values,\n         &lower_side_vars_on_mortar, &face_centered_mesh,\n         &upper_side_vars_on_mortar](auto tag_to_check_v) {\n          using tag_to_check = tmpl::type_from<decltype(tag_to_check_v)>;\n          CAPTURE(db::tag_name<tag_to_check>());\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(lower_side_vars_on_mortar),\n              data_on_slice(get<tag_to_check>(expected_lower_face_values),\n                            face_centered_mesh.extents(), dim, 0));\n          if (dim != 2) {\n            CHECK_ITERABLE_APPROX(\n                get<tag_to_check>(upper_side_vars_on_mortar),\n                data_on_slice(get<tag_to_check>(expected_upper_face_values),\n                              face_centered_mesh.extents(), dim,\n                              face_centered_mesh.extents(dim) - 1));\n          }\n        });\n  }\n}\n}  // namespace detail\n\ntemplate <typename Reconstructor>\nvoid test_prim_reconstructor(const size_t points_per_dimension,\n                             const Reconstructor& derived_reconstructor) {\n  VariableFixing::FixToAtmosphere<3> fix_to_atmosphere{};\n  detail::test_prim_reconstructor_impl(\n      points_per_dimension, derived_reconstructor,\n      EquationsOfState::IdealFluid<true>{1.4}, fix_to_atmosphere);\n  detail::test_prim_reconstructor_impl(\n      points_per_dimension, derived_reconstructor,\n      EquationsOfState::PolytropicFluid<true>{1.0, 2.0}, fix_to_atmosphere);\n}\n}  // namespace TestHelpers::grmhd::GhValenciaDivClean::fd\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/PrimReconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/NormalCovectorAndMagnitude.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/ReconstructWork.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::grmhd::ValenciaDivClean::fd {\nnamespace detail {\nusing GhostData = evolution::dg::subcell::GhostData;\ntemplate <typename F>\nDirectionalIdMap<3, GhostData> compute_ghost_data(\n    const Mesh<3>& subcell_mesh,\n    const tnsr::I<DataVector, 3, Frame::ElementLogical>& volume_logical_coords,\n    const DirectionMap<3, Neighbors<3>>& neighbors,\n    const size_t ghost_zone_size, const F& compute_variables_of_neighbor_data) {\n  DirectionalIdMap<3, GhostData> ghost_data{};\n  for (const auto& [direction, neighbors_in_direction] : neighbors) {\n    REQUIRE(neighbors_in_direction.size() == 1);\n    const ElementId<3>& neighbor_id = *neighbors_in_direction.begin();\n    auto neighbor_logical_coords = volume_logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    const auto neighbor_vars_for_reconstruction =\n        compute_variables_of_neighbor_data(neighbor_logical_coords);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars_for_reconstruction.data(),\n                       neighbor_vars_for_reconstruction.size()),\n        subcell_mesh.extents(), ghost_zone_size,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    ghost_data[DirectionalId<3>{direction, neighbor_id}] = GhostData{1};\n    ghost_data.at(DirectionalId<3>{direction, neighbor_id})\n        .neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n  return ghost_data;\n}\n\ntemplate <size_t ThermodynamicDim, typename Reconstructor>\nvoid test_prim_reconstructor_impl(\n    const size_t points_per_dimension,\n    const Reconstructor& derived_reconstructor,\n    const EquationsOfState::EquationOfState<true, ThermodynamicDim>& eos) {\n  // 1. Create linear prims to reconstruct\n  // 2. send through reconstruction\n  // 3. check prims and cons were computed correctly\n  namespace mhd = ::grmhd::ValenciaDivClean;\n  const mhd::fd::Reconstructor& reconstructor = derived_reconstructor;\n  static_assert(\n      tmpl::list_contains_v<typename mhd::fd::Reconstructor::creatable_classes,\n                            Reconstructor>);\n\n  using Rho = hydro::Tags::RestMassDensity<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n  using ElectronFraction = hydro::Tags::ElectronFraction<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, 3>;\n  using MagField = hydro::Tags::MagneticField<DataVector, 3>;\n  using Phi = hydro::Tags::DivergenceCleaningField<DataVector>;\n  using VelocityW =\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Temperature = hydro::Tags::Temperature<DataVector>;\n  using LorentzFactor = hydro::Tags::LorentzFactor<DataVector>;\n\n  using prims_tags = hydro::grmhd_tags<DataVector>;\n  using cons_tags = typename mhd::System::variables_tag::tags_list;\n\n  using prim_tags_for_reconstruction =\n      tmpl::list<Rho, ElectronFraction, Temperature, VelocityW, MagField, Phi>;\n\n  const Mesh<3> subcell_mesh{points_per_dimension,\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(subcell_mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < 3; ++i) {\n    logical_coords.get(i) += 4.0 * i;\n  }\n\n  DirectionMap<3, Neighbors<3>> neighbors{};\n  for (size_t i = 0; i < 2 * 3; ++i) {\n    neighbors[gsl::at(Direction<3>::all_directions(), i)] = Neighbors<3>{\n        {ElementId<3>{i + 1, {}}}, OrientationMap<3>::create_aligned()};\n  }\n  const Element<3> element{ElementId<3>{0, {}}, neighbors};\n  const auto compute_solution = [](const auto& coords) {\n    Variables<prim_tags_for_reconstruction> vars{get<0>(coords).size(), 0.0};\n    for (size_t i = 0; i < 3; ++i) {\n      get(get<Rho>(vars)) += coords.get(i);\n      get(get<Temperature>(vars)) += coords.get(i);\n      get(get<ElectronFraction>(vars)) += coords.get(i);\n      get(get<Phi>(vars)) += coords.get(i);\n      for (size_t j = 0; j < 3; ++j) {\n        get<VelocityW>(vars).get(j) += coords.get(i);\n        get<MagField>(vars).get(j) += coords.get(i);\n      }\n    }\n    get(get<Rho>(vars)) += 2.0;\n    get(get<Temperature>(vars)) += 30.0;\n    get(get<ElectronFraction>(vars)) += 40.0;\n    get(get<Phi>(vars)) += 50.0;\n    for (size_t j = 0; j < 3; ++j) {\n      get<VelocityW>(vars).get(j) += 1.0e-2 * (j + 2.0) + 10.0;\n      get<MagField>(vars).get(j) += 1.0e-2 * (j + 2.0) + 60.0;\n    }\n    return vars;\n  };\n\n  const DirectionalIdMap<3, evolution::dg::subcell::GhostData> ghost_data =\n      compute_ghost_data(subcell_mesh, logical_coords, element.neighbors(),\n                         reconstructor.ghost_zone_size(), compute_solution);\n\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n\n  using dg_package_data_argument_tags =\n      ::grmhd::ValenciaDivClean::fd::tags_list_for_reconstruct;\n\n  tnsr::ii<DataVector, 3, Frame::Inertial> lower_face_spatial_metric{\n      reconstructed_num_pts, 0.0};\n  tnsr::ii<DataVector, 3, Frame::Inertial> upper_face_spatial_metric{\n      reconstructed_num_pts, 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    lower_face_spatial_metric.get(i, i) = 1.0 + 0.01 * i;\n    upper_face_spatial_metric.get(i, i) = 1.0 - 0.01 * i;\n  }\n  const Scalar<DataVector> lower_face_sqrt_det_spatial_metric{\n      sqrt(get(determinant(lower_face_spatial_metric)))};\n  const Scalar<DataVector> upper_face_sqrt_det_spatial_metric{\n      sqrt(get(determinant(upper_face_spatial_metric)))};\n\n  std::array<Variables<dg_package_data_argument_tags>, 3> vars_on_lower_face =\n      make_array<3>(\n          Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n  std::array<Variables<dg_package_data_argument_tags>, 3> vars_on_upper_face =\n      make_array<3>(\n          Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n  for (size_t i = 0; i < 3; ++i) {\n    get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n        gsl::at(vars_on_lower_face, i)) = lower_face_sqrt_det_spatial_metric;\n    get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n        gsl::at(vars_on_upper_face, i)) = upper_face_sqrt_det_spatial_metric;\n\n    get<gr::Tags::SpatialMetric<DataVector, 3>>(\n        gsl::at(vars_on_lower_face, i)) = lower_face_spatial_metric;\n    get<gr::Tags::SpatialMetric<DataVector, 3>>(\n        gsl::at(vars_on_upper_face, i)) = upper_face_spatial_metric;\n  }\n\n  Variables<prims_tags> volume_prims{subcell_mesh.number_of_grid_points()};\n  {\n    const auto volume_prims_for_recons = compute_solution(logical_coords);\n    tmpl::for_each<\n        tmpl::list<Rho, ElectronFraction, Temperature, MagField, Phi>>(\n        [&volume_prims, &volume_prims_for_recons](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          get<tag>(volume_prims) = get<tag>(volume_prims_for_recons);\n        });\n    // Metric is identity at cell center\n    get(get<LorentzFactor>(volume_prims)) =\n        sqrt(1.0 + get(dot_product(get<VelocityW>(volume_prims_for_recons),\n                                   get<VelocityW>(volume_prims_for_recons))));\n    for (size_t i = 0; i < 3; ++i) {\n      get<Velocity>(volume_prims).get(i) =\n          get<VelocityW>(volume_prims_for_recons).get(i) /\n          get(get<LorentzFactor>(volume_prims));\n    }\n  }\n\n  // Now we have everything to call the reconstruction\n  if constexpr (Reconstructor::use_adaptive_order) {\n    std::array<std::vector<std::uint8_t>, 3> reconstruction_order_storage{};\n    std::optional<std::array<gsl::span<std::uint8_t>, 3>>\n        reconstruction_order{};\n    reconstruction_order.emplace();\n    for (size_t i = 0; i < 3; ++i) {\n      auto order_extents = subcell_mesh.extents();\n      order_extents[i] += 2;\n      gsl::at(reconstruction_order_storage, i).resize(order_extents.product());\n      // Ensure we have reset the values to max so the min calls are fine.\n      std::fill_n(gsl::at(reconstruction_order_storage, i).begin(),\n                  order_extents.product(),\n                  std::numeric_limits<std::uint8_t>::max());\n      gsl::at(reconstruction_order.value(), i) = gsl::span<std::uint8_t>{\n          gsl::at(reconstruction_order_storage, i).data(),\n          gsl::at(reconstruction_order_storage, i).size()};\n    }\n\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct(make_not_null(&vars_on_lower_face),\n                     make_not_null(&vars_on_upper_face),\n                     make_not_null(&reconstruction_order), volume_prims, eos,\n                     element, ghost_data, subcell_mesh);\n    for (size_t d = 0; d < 3; ++d) {\n      CAPTURE(d);\n      for (size_t i = 0; i < gsl::at(reconstruction_order_storage, d).size();\n           ++i) {\n        CAPTURE(i);\n        CHECK(gsl::at(reconstruction_order_storage, d)[i] >= 1);\n        CHECK(gsl::at(reconstruction_order_storage, d)[i] <= 9);\n      }\n    }\n  } else {\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct(make_not_null(&vars_on_lower_face),\n                     make_not_null(&vars_on_upper_face), volume_prims, eos,\n                     element, ghost_data, subcell_mesh);\n  }\n\n  for (size_t dim = 0; dim < 3; ++dim) {\n    CAPTURE(dim);\n    const auto basis = make_array<3>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<3>(Spectral::Quadrature::CellCentered);\n    auto extents = make_array<3>(points_per_dimension);\n    gsl::at(extents, dim) = points_per_dimension + 1;\n    gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n    const Mesh<3> face_centered_mesh{extents, basis, quadrature};\n    auto logical_coords_face_centered = logical_coordinates(face_centered_mesh);\n    for (size_t i = 1; i < 3; ++i) {\n      logical_coords_face_centered.get(i) =\n          logical_coords_face_centered.get(i) + 4.0 * i;\n    }\n    Variables<dg_package_data_argument_tags> expected_lower_face_values{\n        face_centered_mesh.number_of_grid_points()};\n    expected_lower_face_values.assign_subset(\n        compute_solution(logical_coords_face_centered));\n    if constexpr (ThermodynamicDim == 2) {\n      get<SpecificInternalEnergy>(expected_lower_face_values) =\n          eos.specific_internal_energy_from_density_and_temperature(\n              get<Rho>(expected_lower_face_values),\n              get<Temperature>(expected_lower_face_values));\n      get<Pressure>(expected_lower_face_values) =\n          eos.pressure_from_density_and_energy(\n              get<Rho>(expected_lower_face_values),\n              get<SpecificInternalEnergy>(expected_lower_face_values));\n    } else {\n      get<SpecificInternalEnergy>(expected_lower_face_values) =\n          eos.specific_internal_energy_from_density(\n              get<Rho>(expected_lower_face_values));\n      get<Pressure>(expected_lower_face_values) =\n          eos.pressure_from_density(get<Rho>(expected_lower_face_values));\n    }\n\n    Variables<dg_package_data_argument_tags> expected_upper_face_values =\n        expected_lower_face_values;\n    get(get<LorentzFactor>(expected_lower_face_values)) =\n        sqrt(1.0 + get(dot_product(get<VelocityW>(expected_lower_face_values),\n                                   get<VelocityW>(expected_lower_face_values),\n                                   lower_face_spatial_metric)));\n    get(get<LorentzFactor>(expected_upper_face_values)) =\n        sqrt(1.0 + get(dot_product(get<VelocityW>(expected_upper_face_values),\n                                   get<VelocityW>(expected_upper_face_values),\n                                   upper_face_spatial_metric)));\n    for (size_t i = 0; i < 3; ++i) {\n      get<Velocity>(expected_lower_face_values).get(i) =\n          get<VelocityW>(expected_lower_face_values).get(i) /\n          get(get<LorentzFactor>(expected_lower_face_values));\n      get<Velocity>(expected_upper_face_values).get(i) =\n          get<VelocityW>(expected_upper_face_values).get(i) /\n          get(get<LorentzFactor>(expected_upper_face_values));\n    }\n\n    get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n        expected_lower_face_values) = lower_face_sqrt_det_spatial_metric;\n    get<gr::Tags::SpatialMetric<DataVector, 3>>(expected_lower_face_values) =\n        lower_face_spatial_metric;\n    get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n        expected_upper_face_values) = upper_face_sqrt_det_spatial_metric;\n    get<gr::Tags::SpatialMetric<DataVector, 3>>(expected_upper_face_values) =\n        upper_face_spatial_metric;\n\n    mhd::ConservativeFromPrimitive::apply(\n        make_not_null(&get<mhd::Tags::TildeD>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildeYe>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildeTau>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildeS<>>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildeB<>>(expected_lower_face_values)),\n        make_not_null(&get<mhd::Tags::TildePhi>(expected_lower_face_values)),\n        get<Rho>(expected_lower_face_values),\n        get<ElectronFraction>(expected_lower_face_values),\n        get<SpecificInternalEnergy>(expected_lower_face_values),\n        get<Pressure>(expected_lower_face_values),\n        get<Velocity>(expected_lower_face_values),\n        get<LorentzFactor>(expected_lower_face_values),\n        get<MagField>(expected_lower_face_values),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n            expected_lower_face_values),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(expected_lower_face_values),\n        get<Phi>(expected_lower_face_values));\n    mhd::ConservativeFromPrimitive::apply(\n        make_not_null(&get<mhd::Tags::TildeD>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildeYe>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildeTau>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildeS<>>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildeB<>>(expected_upper_face_values)),\n        make_not_null(&get<mhd::Tags::TildePhi>(expected_upper_face_values)),\n        get<Rho>(expected_upper_face_values),\n        get<ElectronFraction>(expected_upper_face_values),\n        get<SpecificInternalEnergy>(expected_upper_face_values),\n        get<Pressure>(expected_upper_face_values),\n        get<Velocity>(expected_upper_face_values),\n        get<LorentzFactor>(expected_upper_face_values),\n        get<MagField>(expected_upper_face_values),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n            expected_upper_face_values),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(expected_upper_face_values),\n        get<Phi>(expected_upper_face_values));\n\n    tmpl::for_each<tmpl::append<cons_tags, prims_tags>>(\n        [dim, &expected_lower_face_values, &expected_upper_face_values,\n         &vars_on_lower_face, &vars_on_upper_face](auto tag_to_check_v) {\n          using tag_to_check = tmpl::type_from<decltype(tag_to_check_v)>;\n          CAPTURE(db::tag_name<tag_to_check>());\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(gsl::at(vars_on_lower_face, dim)),\n              get<tag_to_check>(expected_lower_face_values));\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(gsl::at(vars_on_upper_face, dim)),\n              get<tag_to_check>(expected_upper_face_values));\n        });\n\n    // Test reconstruct_fd_neighbor\n    const size_t num_pts_on_mortar =\n        face_centered_mesh.slice_away(dim).number_of_grid_points();\n    Variables<dg_package_data_argument_tags> upper_side_vars_on_mortar{\n        num_pts_on_mortar};\n    // Slice GR variables onto the mortar\n    data_on_slice(\n        make_not_null(&get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n            upper_side_vars_on_mortar)),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n            expected_upper_face_values),\n        face_centered_mesh.extents(), dim, face_centered_mesh.extents(dim) - 1);\n    data_on_slice(\n        make_not_null(&get<gr::Tags::SpatialMetric<DataVector, 3>>(\n            upper_side_vars_on_mortar)),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(expected_upper_face_values),\n        face_centered_mesh.extents(), dim, face_centered_mesh.extents(dim) - 1);\n\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct_fd_neighbor(make_not_null(&upper_side_vars_on_mortar),\n                                 volume_prims, eos, element, ghost_data,\n                                 subcell_mesh, Direction<3>{dim, Side::Upper});\n\n    Variables<dg_package_data_argument_tags> lower_side_vars_on_mortar{\n        num_pts_on_mortar};\n    // Slice GR variables onto the mortar\n    data_on_slice(\n        make_not_null(&get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n            lower_side_vars_on_mortar)),\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(\n            expected_lower_face_values),\n        face_centered_mesh.extents(), dim, 0);\n    data_on_slice(\n        make_not_null(&get<gr::Tags::SpatialMetric<DataVector, 3>>(\n            lower_side_vars_on_mortar)),\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(expected_lower_face_values),\n        face_centered_mesh.extents(), dim, 0);\n\n    dynamic_cast<const Reconstructor&>(reconstructor)\n        .reconstruct_fd_neighbor(make_not_null(&lower_side_vars_on_mortar),\n                                 volume_prims, eos, element, ghost_data,\n                                 subcell_mesh, Direction<3>{dim, Side::Lower});\n\n    tmpl::for_each<tmpl::append<cons_tags, prims_tags>>(\n        [dim, &expected_lower_face_values, &expected_upper_face_values,\n         &lower_side_vars_on_mortar, &face_centered_mesh,\n         &upper_side_vars_on_mortar](auto tag_to_check_v) {\n          using tag_to_check = tmpl::type_from<decltype(tag_to_check_v)>;\n          CAPTURE(db::tag_name<tag_to_check>());\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(lower_side_vars_on_mortar),\n              data_on_slice(get<tag_to_check>(expected_lower_face_values),\n                            face_centered_mesh.extents(), dim, 0));\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(upper_side_vars_on_mortar),\n              data_on_slice(get<tag_to_check>(expected_upper_face_values),\n                            face_centered_mesh.extents(), dim,\n                            face_centered_mesh.extents(dim) - 1));\n        });\n  }\n}\n}  // namespace detail\n\ntemplate <typename Reconstructor>\nvoid test_prim_reconstructor(const size_t points_per_dimension,\n                             const Reconstructor& in_derived_reconstructor) {\n  PUPable_reg(Reconstructor);\n  const auto base_recons =\n      serialize_and_deserialize(in_derived_reconstructor.get_clone());\n  const auto& derived_reconstructor =\n      dynamic_cast<const Reconstructor&>(*base_recons);\n\n  detail::test_prim_reconstructor_impl(points_per_dimension,\n                                       derived_reconstructor,\n                                       EquationsOfState::IdealFluid<true>{1.4});\n  detail::test_prim_reconstructor_impl(\n      points_per_dimension, derived_reconstructor,\n      EquationsOfState::PolytropicFluid<true>{1.0, 2.0});\n}\n}  // namespace TestHelpers::grmhd::ValenciaDivClean::fd\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/NewtonianEuler/FiniteDifference/PrimReconstructor.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::NewtonianEuler::fd {\nusing GhostData = evolution::dg::subcell::GhostData;\ntemplate <size_t Dim, typename F>\nDirectionalIdMap<Dim, evolution::dg::subcell::GhostData> compute_ghost_data(\n    const Mesh<Dim>& subcell_mesh,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n        volume_logical_coords,\n    const DirectionMap<Dim, Neighbors<Dim>>& neighbors,\n    const size_t ghost_zone_size, const F& compute_variables_of_neighbor_data) {\n  DirectionalIdMap<Dim, evolution::dg::subcell::GhostData> ghost_data{};\n  for (const auto& [direction, neighbors_in_direction] : neighbors) {\n    REQUIRE(neighbors_in_direction.size() == 1);\n    const ElementId<Dim>& neighbor_id = *neighbors_in_direction.begin();\n    auto neighbor_logical_coords = volume_logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    const auto neighbor_vars_for_reconstruction =\n        compute_variables_of_neighbor_data(neighbor_logical_coords);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars_for_reconstruction.data(),\n                       neighbor_vars_for_reconstruction.size()),\n        subcell_mesh.extents(), ghost_zone_size,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    ghost_data[DirectionalId<Dim>{direction, neighbor_id}] = GhostData{1};\n    ghost_data.at(DirectionalId<Dim>{direction, neighbor_id})\n        .neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n  return ghost_data;\n}\n\nnamespace detail {\ntemplate <size_t Dim, typename Reconstructor>\nvoid test_prim_reconstructor_impl(\n    const size_t points_per_dimension,\n    const Reconstructor& derived_reconstructor,\n    const EquationsOfState::EquationOfState<false, 2>& eos) {\n  // 1. Create linear prims to reconstruct\n  // 2. send through reconstruction\n  // 3. check prims and cons were computed correctly\n  namespace ne = ::NewtonianEuler;\n  const ne::fd::Reconstructor<Dim>& reconstructor = derived_reconstructor;\n  static_assert(tmpl::list_contains_v<\n                typename ne::fd::Reconstructor<Dim>::creatable_classes,\n                Reconstructor>);\n\n  using MassDensityCons = ne::Tags::MassDensityCons;\n  using EnergyDensity = ne::Tags::EnergyDensity;\n  using MomentumDensity = ne::Tags::MomentumDensity<Dim>;\n\n  // Primitive vars tags\n  using MassDensity = hydro::Tags::RestMassDensity<DataVector>;\n  using Velocity = hydro::Tags::SpatialVelocity<DataVector, Dim>;\n  using SpecificInternalEnergy =\n      hydro::Tags::SpecificInternalEnergy<DataVector>;\n  using Pressure = hydro::Tags::Pressure<DataVector>;\n\n  using prims_tags =\n      tmpl::list<MassDensity, Velocity, SpecificInternalEnergy, Pressure>;\n  using cons_tags = tmpl::list<MassDensityCons, MomentumDensity, EnergyDensity>;\n  using flux_tags = db::wrap_tags_in<::Tags::Flux, cons_tags, tmpl::size_t<Dim>,\n                                     Frame::Inertial>;\n  using prim_tags_for_reconstruction =\n      tmpl::list<MassDensity, Velocity, Pressure>;\n\n  const Mesh<Dim> subcell_mesh{points_per_dimension,\n                               Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(subcell_mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < Dim; ++i) {\n    logical_coords.get(i) += 4.0 * i;\n  }\n\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    neighbors[gsl::at(Direction<Dim>::all_directions(), i)] = Neighbors<Dim>{\n        {ElementId<Dim>{i + 1, {}}}, OrientationMap<Dim>::create_aligned()};\n  }\n  const Element<Dim> element{ElementId<Dim>{0, {}}, neighbors};\n  const auto compute_solution = [](const auto& coords) {\n    Variables<prim_tags_for_reconstruction> vars{get<0>(coords).size(), 0.0};\n    for (size_t i = 0; i < Dim; ++i) {\n      get(get<MassDensity>(vars)) += coords.get(i);\n      get(get<Pressure>(vars)) += coords.get(i);\n      for (size_t j = 0;j <Dim;++j) {\n        get<Velocity>(vars).get(j) += coords.get(i);\n      }\n    }\n    get(get<MassDensity>(vars)) += 2.0;\n    get(get<Pressure>(vars)) += 30.0;\n    for (size_t j = 0; j < Dim; ++j) {\n      get<Velocity>(vars).get(j) += 1.0e-2 * (j + 2.0) + 10.0;\n    }\n    return vars;\n  };\n\n  const DirectionalIdMap<Dim, GhostData> ghost_data =\n      compute_ghost_data(subcell_mesh, logical_coords, element.neighbors(),\n                         reconstructor.ghost_zone_size(), compute_solution);\n\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n\n  using dg_package_data_argument_tags =\n      tmpl::append<cons_tags, prims_tags, flux_tags>;\n  std::array<Variables<dg_package_data_argument_tags>, Dim> vars_on_lower_face =\n      make_array<Dim>(\n          Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n  std::array<Variables<dg_package_data_argument_tags>, Dim> vars_on_upper_face =\n      make_array<Dim>(\n          Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n\n  Variables<prims_tags> volume_prims{subcell_mesh.number_of_grid_points()};\n  volume_prims.assign_subset(compute_solution(logical_coords));\n\n  // Now we have everything to call the reconstruction\n  dynamic_cast<const Reconstructor&>(reconstructor)\n      .reconstruct(make_not_null(&vars_on_lower_face),\n                   make_not_null(&vars_on_upper_face), volume_prims, eos,\n                   element, ghost_data, subcell_mesh);\n\n  for (size_t dim = 0; dim < Dim; ++dim) {\n    CAPTURE(dim);\n    const auto basis = make_array<Dim>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<Dim>(Spectral::Quadrature::CellCentered);\n    auto extents = make_array<Dim>(points_per_dimension);\n    gsl::at(extents, dim) = points_per_dimension + 1;\n    gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n    const Mesh<Dim> face_centered_mesh{extents, basis, quadrature};\n    auto logical_coords_face_centered = logical_coordinates(face_centered_mesh);\n    for (size_t i = 1; i < Dim; ++i) {\n      logical_coords_face_centered.get(i) =\n          logical_coords_face_centered.get(i) + 4.0 * i;\n    }\n    Variables<dg_package_data_argument_tags> expected_face_values{\n        face_centered_mesh.number_of_grid_points()};\n    expected_face_values.assign_subset(\n        compute_solution(logical_coords_face_centered));\n    get<SpecificInternalEnergy>(expected_face_values) =\n        eos.specific_internal_energy_from_density_and_pressure(\n            get<MassDensity>(expected_face_values),\n            get<Pressure>(expected_face_values));\n    ne::ConservativeFromPrimitive<Dim>::apply(\n        make_not_null(&get<MassDensityCons>(expected_face_values)),\n        make_not_null(&get<MomentumDensity>(expected_face_values)),\n        make_not_null(&get<EnergyDensity>(expected_face_values)),\n        get<MassDensity>(expected_face_values),\n        get<Velocity>(expected_face_values),\n        get<SpecificInternalEnergy>(expected_face_values));\n\n    tmpl::for_each<tmpl::append<cons_tags, prims_tags>>(\n        [dim, &expected_face_values, &vars_on_lower_face,\n         &vars_on_upper_face](auto tag_to_check_v) {\n          using tag_to_check = tmpl::type_from<decltype(tag_to_check_v)>;\n          CAPTURE(db::tag_name<tag_to_check>());\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(gsl::at(vars_on_lower_face, dim)),\n              get<tag_to_check>(expected_face_values));\n          CHECK_ITERABLE_APPROX(\n              get<tag_to_check>(gsl::at(vars_on_upper_face, dim)),\n              get<tag_to_check>(expected_face_values));\n        });\n  }\n}\n}  // namespace detail\n\ntemplate <size_t Dim, typename Reconstructor>\nvoid test_prim_reconstructor(const size_t points_per_dimension,\n                             const Reconstructor& derived_reconstructor) {\n  detail::test_prim_reconstructor_impl<Dim>(\n      points_per_dimension, derived_reconstructor,\n      EquationsOfState::IdealFluid<false>{1.4});\n  detail::test_prim_reconstructor_impl<Dim>(\n      points_per_dimension, derived_reconstructor,\n      EquationsOfState::Barotropic2D{\n          EquationsOfState::PolytropicFluid<false>{1.0, 2.0}});\n}\n}  // namespace TestHelpers::NewtonianEuler::fd\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/NewtonianEuler/TimeDerivativeTerms.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/Source.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::NewtonianEuler {\nstruct FirstArg : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct SecondArg : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n\nstruct ThirdArg : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct FourthArg : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim>;\n};\n\n// Some source term where all three conservative quantities are sourced.\ntemplate <size_t Dim>\nstruct SomeSourceType : public ::NewtonianEuler::Sources::Source<Dim> {\n  void operator()() const override {}\n\n  static constexpr size_t volume_dim = Dim;\n  using sourced_variables =\n      tmpl::list<::NewtonianEuler::Tags::MassDensityCons,\n                 ::NewtonianEuler::Tags::MomentumDensity<Dim>,\n                 ::NewtonianEuler::Tags::EnergyDensity>;\n\n  using argument_tags =\n      tmpl::list<FirstArg, SecondArg<Dim>, ThirdArg, FourthArg<Dim>>;\n\n  void apply(\n      const gsl::not_null<Scalar<DataVector>*> source_mass_density_cons,\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> source_momentum_density,\n      const gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& first_arg,\n      const tnsr::I<DataVector, Dim>& second_arg,\n      const Scalar<DataVector>& third_arg,\n      const tnsr::i<DataVector, Dim>& fourth_arg) const {\n    get(*source_mass_density_cons) = exp(get(first_arg));\n    for (size_t i = 0; i < Dim; ++i) {\n      source_momentum_density->get(i) =\n          (get(first_arg) - 1.5 * get(third_arg)) * second_arg.get(i);\n    }\n    get(*source_energy_density) =\n        get(dot_product(second_arg, fourth_arg)) + 3.0 * get(third_arg);\n  }\n};\n\n// Some other source term where the mass density is not sourced (this is by\n// far the most common type of non-trivial source term for NewtonianEuler.)\ntemplate <size_t Dim>\nstruct SomeOtherSourceType {\n  static constexpr size_t volume_dim = Dim;\n  using sourced_variables =\n      tmpl::list<::NewtonianEuler::Tags::MomentumDensity<Dim>,\n                 ::NewtonianEuler::Tags::EnergyDensity>;\n\n  using argument_tags =\n      tmpl::list<FirstArg, SecondArg<Dim>, ThirdArg, FourthArg<Dim>>;\n\n  void apply(\n      const gsl::not_null<tnsr::I<DataVector, Dim>*> source_momentum_density,\n      const gsl::not_null<Scalar<DataVector>*> source_energy_density,\n      const Scalar<DataVector>& first_arg,\n      const tnsr::I<DataVector, Dim>& second_arg,\n      const Scalar<DataVector>& third_arg,\n      const tnsr::i<DataVector, Dim>& fourth_arg) const {\n    for (size_t i = 0; i < Dim; ++i) {\n      source_momentum_density->get(i) =\n          (get(first_arg) - 1.5 * get(third_arg)) * second_arg.get(i);\n    }\n    get(*source_energy_density) =\n        get(dot_product(second_arg, fourth_arg)) + 3.0 * get(third_arg);\n  }\n};\n\ntemplate <typename SourceTermType>\nstruct TestInitialData {\n  static constexpr size_t volume_dim = SourceTermType::volume_dim;\n  using source_term_type = SourceTermType;\n};\n}  // namespace TestHelpers::NewtonianEuler\n"
  },
  {
    "path": "tests/Unit/Helpers/Evolution/Systems/ScalarAdvection/FiniteDifference/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/FiniteDifference/Reconstructor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers {\n/*!\n * \\brief Defines functions useful for testing advection subcell\n */\nnamespace ScalarAdvection::fd {\nusing GhostData = evolution::dg::subcell::GhostData;\ntemplate <size_t Dim, typename F>\nDirectionalIdMap<Dim, GhostData> compute_ghost_data(\n    const Mesh<Dim>& subcell_mesh,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>&\n        volume_logical_coords,\n    const DirectionMap<Dim, Neighbors<Dim>>& neighbors,\n    const size_t ghost_zone_size, const F& compute_variables_of_neighbor_data) {\n  DirectionalIdMap<Dim, GhostData> ghost_data{};\n  for (const auto& [direction, neighbors_in_direction] : neighbors) {\n    REQUIRE(neighbors_in_direction.size() ==\n            1);  // currently only support one neighbor in each direction\n    const ElementId<Dim>& neighbor_id = *neighbors_in_direction.begin();\n\n    auto neighbor_logical_coords = volume_logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    const auto neighbor_vars_for_reconstruction =\n        compute_variables_of_neighbor_data(neighbor_logical_coords);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars_for_reconstruction.data(),\n                       neighbor_vars_for_reconstruction.size()),\n        subcell_mesh.extents(), ghost_zone_size,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    ghost_data[DirectionalId<Dim>{direction, neighbor_id}] = GhostData{1};\n    ghost_data.at(DirectionalId<Dim>{direction, neighbor_id})\n        .neighbor_ghost_data_for_reconstruction() =\n        sliced_data.at(direction.opposite());\n  }\n  return ghost_data;\n}\n\ntemplate <size_t Dim, typename Reconstructor>\nvoid test_reconstructor(const size_t points_per_dimension,\n                        const Reconstructor& derived_reconstructor) {\n  // 1. create the scalar field U to reconstruct being linear to coords\n  // 2. send through reconstruction\n  // 3. check if U was reconstructed correctly\n\n  const ::ScalarAdvection::fd::Reconstructor<Dim>& reconstructor =\n      derived_reconstructor;\n  static_assert(\n      tmpl::list_contains_v<\n          typename ::ScalarAdvection::fd::Reconstructor<Dim>::creatable_classes,\n          Reconstructor>);\n\n  // create an element and its neighbor elements\n  DirectionMap<Dim, Neighbors<Dim>> neighbors{};\n  for (size_t i = 0; i < 2 * Dim; ++i) {\n    neighbors[gsl::at(Direction<Dim>::all_directions(), i)] = Neighbors<Dim>{\n        {ElementId<Dim>{i + 1, {}}}, OrientationMap<Dim>::create_aligned()};\n  }\n  const Element<Dim> element{ElementId<Dim>{0, {}}, neighbors};\n\n  // a simple, linear form of the scalar field U for testing purpose\n  //   * U(x)   = x + 1       (for 1D)\n  //   * U(x,y) = x + 2y + 1  (for 2D)\n  using scalar_u = ::ScalarAdvection::Tags::U;\n  const auto compute_solution = [](const auto& coords) {\n    Variables<tmpl::list<scalar_u>> vars{get<0>(coords).size(), 0.0};\n    for (size_t i = 0; i < Dim; ++i) {\n      get(get<scalar_u>(vars)) += (i + 1) * coords.get(i);\n    }\n    get(get<scalar_u>(vars)) += 1.0;\n    return vars;\n  };\n\n  // compute neighbor data\n  const Mesh<Dim> subcell_mesh{points_per_dimension,\n                               Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(subcell_mesh);\n  const DirectionalIdMap<Dim, GhostData> ghost_data =\n      compute_ghost_data(subcell_mesh, logical_coords, element.neighbors(),\n                         reconstructor.ghost_zone_size(), compute_solution);\n\n  // create Variables on lower and upper faces to perform reconstruction\n  using dg_package_data_argument_tags =\n      tmpl::list<scalar_u,\n                 ::Tags::Flux<scalar_u, tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::ScalarAdvection::Tags::VelocityField<Dim>>;\n  const size_t reconstructed_num_pts =\n      (subcell_mesh.extents(0) + 1) *\n      subcell_mesh.extents().slice_away(0).product();\n  std::array<Variables<dg_package_data_argument_tags>, Dim> vars_on_lower_face =\n      make_array<Dim>(\n          Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n  std::array<Variables<dg_package_data_argument_tags>, Dim> vars_on_upper_face =\n      make_array<Dim>(\n          Variables<dg_package_data_argument_tags>(reconstructed_num_pts));\n\n  Variables<tmpl::list<scalar_u>> volume_vars{\n      subcell_mesh.number_of_grid_points()};\n  volume_vars.assign_subset(compute_solution(logical_coords));\n\n  // call the reconstruction\n  dynamic_cast<const Reconstructor&>(reconstructor)\n      .reconstruct(make_not_null(&vars_on_lower_face),\n                   make_not_null(&vars_on_upper_face), volume_vars, element,\n                   ghost_data, subcell_mesh);\n\n  for (size_t dim = 0; dim < Dim; ++dim) {\n    // construct face-centered coordinates\n    const auto basis = make_array<Dim>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<Dim>(Spectral::Quadrature::CellCentered);\n    auto extents = make_array<Dim>(points_per_dimension);\n    gsl::at(extents, dim) = points_per_dimension + 1;\n    gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n    const Mesh<Dim> face_centered_mesh{extents, basis, quadrature};\n    auto logical_coords_face_centered = logical_coordinates(face_centered_mesh);\n\n    // check reconstructed values for reconstruct() function\n    Variables<dg_package_data_argument_tags> expected_face_values{\n        face_centered_mesh.number_of_grid_points()};\n    expected_face_values.assign_subset(\n        compute_solution(logical_coords_face_centered));\n\n    CHECK_ITERABLE_APPROX(get<scalar_u>(gsl::at(vars_on_lower_face, dim)),\n                          get<scalar_u>(expected_face_values));\n    CHECK_ITERABLE_APPROX(get<scalar_u>(gsl::at(vars_on_upper_face, dim)),\n                          get<scalar_u>(expected_face_values));\n\n    // check reconstructed values for reconstruct_fd_neighbor() function\n    Variables<dg_package_data_argument_tags> vars_on_mortar_face{0};\n    Variables<dg_package_data_argument_tags> expected_mortar_face_values{0};\n\n    for (size_t upper_or_lower = 0; upper_or_lower < 2; ++upper_or_lower) {\n      // iterate over upper and lower direction\n      Direction<Dim> direction =\n          gsl::at(Direction<Dim>::all_directions(), 2 * dim + upper_or_lower);\n\n      if constexpr (Dim == 1) {\n        // 1D mortar has only one face point\n        vars_on_mortar_face.initialize(1);\n        expected_mortar_face_values.initialize(1);\n      } else {\n        const size_t num_mortar_face_pts =\n            subcell_mesh.extents().slice_away(direction.dimension()).product();\n        vars_on_mortar_face.initialize(num_mortar_face_pts);\n        expected_mortar_face_values.initialize(num_mortar_face_pts);\n      }\n\n      // call the reconstruction\n      dynamic_cast<const Reconstructor&>(reconstructor)\n          .reconstruct_fd_neighbor(make_not_null(&vars_on_mortar_face),\n                                   volume_vars, element, ghost_data,\n                                   subcell_mesh, direction);\n\n      // slice face-centered variables to get the values on the mortar\n      data_on_slice(make_not_null(&get<scalar_u>(expected_mortar_face_values)),\n                    get<scalar_u>(expected_face_values),\n                    face_centered_mesh.extents(), direction.dimension(),\n                    direction.side() == Side::Lower\n                        ? 0\n                        : extents[direction.dimension()] - 1);\n\n      CHECK_ITERABLE_APPROX(get<scalar_u>(vars_on_mortar_face),\n                            get<scalar_u>(expected_mortar_face_values));\n    }\n  }\n}\n\n}  // namespace ScalarAdvection::fd\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/IO/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"IoTestHelpers\")\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  VolumeData.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  VolumeData.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  ErrorHandling\n  Framework\n  H5\n  Utilities\n  )\n\nadd_subdirectory(Observers)\n"
  },
  {
    "path": "tests/Unit/Helpers/IO/Observers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"ObserverHelpers\")\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  MockH5.cpp\n)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  MockH5.hpp\n  MockWriteReductionDataRow.hpp\n  ObserverHelpers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Domain\n  Framework\n  Observer\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/IO/Observers/MockH5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/IO/Observers/MockH5.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace TestHelpers::observers {\nvoid MockDat::append(const std::vector<std::string>& legend,\n                     const std::vector<double>& new_data) {\n  auto& matrix = data_.second;\n  // If this isn't the first call, do some checks. Otherwise, assign the\n  // legend. Use `if` with ERROR rather than ASSERT so these bugs can be caught\n  // in Release mode\n  if (matrix.rows() != 0) {\n    if (matrix.columns() != new_data.size()) {\n      ERROR(\n          \"Size of supplied data does not match number of columns in the \"\n          \"existing matrix. Data size: \"\n          << new_data.size() << \", Matrix columns: \" << matrix.columns());\n    }\n    if (data_.first != legend) {\n      ERROR(\n          \"Supplied legend is not the same as the existing legend. Supplied \"\n          \"legend: \"\n          << legend << \", Existing legend: \" << data_.first);\n    }\n  } else {\n    data_.first = legend;\n  }\n\n  matrix.resize(matrix.rows() + 1, new_data.size(), true);\n  for (size_t i = 0; i < new_data.size(); i++) {\n    matrix(matrix.rows() - 1, i) = new_data[i];\n  }\n}\n\nvoid MockDat::check_data(const std::string& function_name) const {\n  // Use `if` with ERROR rather than ASSERT so these bugs can be caught in\n  // Release mode\n  if (data_.first.empty()) {\n    ERROR(\"Cannot get \" << function_name << \". Append some data first.\");\n  }\n}\n\nMockDat& MockH5File::get_dat(const std::string& subfile_path) {\n  check_subfile(subfile_path);\n  return subfiles_.at(subfile_path);\n}\n\nconst MockDat& MockH5File::get_dat(const std::string& subfile_path) const {\n  check_subfile(subfile_path);\n  return subfiles_.at(subfile_path);\n}\n\nMockDat& MockH5File::try_insert(const std::string& subfile_path) {\n  subfiles_.try_emplace(subfile_path);\n\n  return subfiles_.at(subfile_path);\n}\n\nvoid MockH5File::check_subfile(const std::string& subfile_path) const {\n  // Use `if` with ERROR rather than ASSERT so these bugs can be caught in\n  // Release mode\n  if (subfiles_.count(subfile_path) != 1) {\n    ERROR(\"Cannot get \" << subfile_path\n                        << \" from MockH5File. Path does not exist.\");\n  }\n}\n}  // namespace TestHelpers::observers\n"
  },
  {
    "path": "tests/Unit/Helpers/IO/Observers/MockH5.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n\nnamespace TestHelpers::observers {\n/*!\n * \\brief Class meant to mock h5::Dat in the testing framework.\n *\n * Currently, this class is only used inside a MockH5 object. The methods of\n * this class are similar to that of h5::Dat to keep a familiar interface, but\n * they may not be identical.\n */\nstruct MockDat {\n  void append(const std::vector<std::string>& legend,\n              const std::vector<double>& new_data);\n\n  const Matrix& get_data() const {\n    check_data(\"data\");\n    return data_.second;\n  }\n\n  const std::vector<std::string>& get_legend() const {\n    check_data(\"legend\");\n    return data_.first;\n  }\n\n  void pup(PUP::er& p) { p | data_; }  // NOLINT\n\n private:\n  void check_data(const std::string& function_name) const;\n  std::pair<std::vector<std::string>, Matrix> data_{};\n};\n\n/*!\n * \\brief Class meant to mock h5::H5File in the testing framework.\n *\n * The methods of this class are similar to that of h5::H5File to keep a\n * familiar interface, but they may not be identical.\n */\nstruct MockH5File {\n  MockDat& get_dat(const std::string& subfile_path);\n\n  const MockDat& get_dat(const std::string& subfile_path) const;\n\n  MockDat& try_insert(const std::string& subfile_path);\n\n  void pup(PUP::er& p) { p | subfiles_; }  // NOLINT\n\n private:\n  void check_subfile(const std::string& subfile_path) const;\n\n  std::unordered_map<std::string, MockDat> subfiles_{};\n};\n}  // namespace TestHelpers::observers\n"
  },
  {
    "path": "tests/Unit/Helpers/IO/Observers/MockWriteReductionDataRow.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/IO/Observers/MockH5.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::observers {\n/*!\n * Tag that holds a MockH5File object for the MockObserverWriter\n */\nstruct MockReductionFileTag : ::db::SimpleTag {\n  using type = MockH5File;\n};\n\n/*!\n * \\brief Action meant to mock WriteReductionDataRow.\n *\n * \\details Instead of writing to disk, this action will mutate the\n * MockReductionFileTag stored in the DataBox of the MockObserverWriter with the\n * values passed in. We want to avoid writing to disk in the testing framework\n * because IO is slow and we don't really care that things were written to disk,\n * just that the correct values were recorded.\n *\n * To check what values were written, get the MockDat file:\n *\n * \\snippet Test_MockWriteReductionDataRow.cpp check_mock_writer_data\n */\nstruct MockWriteReductionDataRow {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, typename... Ts>\n  static void apply(::db::DataBox<DbTagsList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const std::string& subfile_name,\n                    std::vector<std::string>&& legend,\n                    std::tuple<Ts...>&& in_reduction_data) {\n    if constexpr (::db::tag_is_retrievable_v<MockReductionFileTag,\n                                             ::db::DataBox<DbTagsList>>) {\n      ::db::mutate<MockReductionFileTag>(\n          [subfile_name, legend,\n           in_reduction_data](const gsl::not_null<MockH5File*> mock_h5_file) {\n            auto& dat_file = (*mock_h5_file).try_insert(subfile_name);\n\n            std::vector<double> data{};\n            tmpl::for_each<tmpl::range<size_t, 0, sizeof...(Ts)>>(\n                [&data, &in_reduction_data](auto size_holder) {\n                  constexpr size_t index =\n                      std::decay_t<decltype(size_holder)>::type::value;\n                  ::observers::ThreadedActions::ReductionActions_detail::\n                      append_to_reduction_data(\n                          make_not_null(&data),\n                          std::get<index>(in_reduction_data));\n                });\n\n            dat_file.append(legend, data);\n          },\n          make_not_null(&box));\n    } else {\n      (void)subfile_name;\n      (void)legend;\n      (void)in_reduction_data;\n      ERROR(\n          \"Wrong DataBox. Cannot retrieve the MockReductionFileTag. Expecting \"\n          \"DataBox for MockObserverWriter.\");\n    }\n  }\n};\n\n/*!\n * \\brief Component that mocks the ObserverWriter.\n *\n * \\details The only tag that is added to the DataBox is the\n * MockReductionFileTag. To initialize this component do\n *\n * \\snippet Test_MockWriteReductionDataRow.cpp initialize_component\n *\n * This component replaces the WriteReductionDataRow threaded action with the\n * MockWriteReductionDataRow threaded action.\n */\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 tmpl::list<MockReductionFileTag>>>>>;\n  using component_being_mocked = ::observers::ObserverWriter<Metavariables>;\n\n  using replace_these_threaded_actions =\n      tmpl::list<::observers::ThreadedActions::WriteReductionDataRow>;\n  using with_these_threaded_actions = tmpl::list<MockWriteReductionDataRow>;\n};\n}  // namespace TestHelpers::observers\n"
  },
  {
    "path": "tests/Unit/Helpers/IO/Observers/ObserverHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <functional>\n\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n/// \\endcond\n\nnamespace TestObservers_detail {\nusing ElementIdType = ElementId<2>;\n\ntemplate <observers::TypeOfObservation TypeOfObservation>\nstruct RegisterObservers {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& /*box*/,\n                const ArrayIndex& /*array_index*/) {\n    return {TypeOfObservation, observers::ObservationKey{\"/element_data.vol\"}};\n  }\n};\n\ntemplate <typename Metavariables, typename RegistrationActionsList>\nstruct element_component {\n  using component_being_mocked = void;  // Not needed\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementIdType;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Register,\n                                        RegistrationActionsList>>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<3>,\n                 domain::Tags::FunctionsOfTimeInitialize>;\n};\n\ntemplate <typename Metavariables>\nstruct observer_component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using simple_tags =\n      typename observers::Actions::Initialize<Metavariables>::simple_tags;\n  using compute_tags =\n      typename observers::Actions::Initialize<Metavariables>::compute_tags;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<observers::Actions::Initialize<Metavariables>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct observer_writer_component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = int;\n  using const_global_cache_tags = tmpl::list<observers::Tags::ReductionFileName,\n                                             observers::Tags::VolumeFileName>;\n\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using simple_tags =\n      typename observers::Actions::InitializeWriter<Metavariables>::simple_tags;\n  using compute_tags = typename observers::Actions::InitializeWriter<\n      Metavariables>::compute_tags;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<observers::Actions::InitializeWriter<Metavariables>>>>;\n};\n\nusing l2_error_datum = Parallel::ReductionDatum<double, funcl::Plus<>,\n                                                funcl::Sqrt<funcl::Divides<>>,\n                                                std::index_sequence<1>>;\nusing reduction_data_from_doubles = Parallel::ReductionData<\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n    Parallel::ReductionDatum<size_t, funcl::Plus<>>, l2_error_datum,\n    l2_error_datum>;\n\nusing reduction_data_from_vector = Parallel::ReductionData<\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n    Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n    Parallel::ReductionDatum<std::vector<double>,\n                             funcl::ElementWise<funcl::Plus<>>>>;\n\n// Nothing special about the order. We just want doubles and std::vector's.\nusing reduction_data_from_ds_and_vs = Parallel::ReductionData<\n    Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n    Parallel::ReductionDatum<size_t, funcl::Plus<>>, l2_error_datum,\n    Parallel::ReductionDatum<std::vector<double>,\n                             funcl::ElementWise<funcl::Plus<>>>,\n    Parallel::ReductionDatum<std::vector<double>,\n                             funcl::ElementWise<funcl::Plus<>>>,\n    l2_error_datum>;\n\ntemplate <typename RegistrationActionsList>\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n\n  using component_list =\n      tmpl::list<element_component<Metavariables, RegistrationActionsList>,\n                 observer_component<Metavariables>,\n                 observer_writer_component<Metavariables>>;\n\n  /// [make_reduction_data_tags]\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<reduction_data_from_doubles, reduction_data_from_vector,\n                 reduction_data_from_ds_and_vs>>;\n  /// [make_reduction_data_tags]\n\n};\n}  // namespace TestObservers_detail\n"
  },
  {
    "path": "tests/Unit/Helpers/IO/VolumeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/IO/VolumeData.hpp\"\n\n#include <algorithm>\n#include <boost/iterator/transform_iterator.hpp>\n#include <optional>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace TestHelpers::io::VolumeData {\ntemplate <typename DataType>\nvoid check_volume_data(\n    const std::string& h5_file_name, const uint32_t version_number,\n    const std::string& group_name, const size_t observation_id,\n    const double observation_value,\n    const std::optional<double>& observation_value_epsilon,\n    const std::vector<DataType>& tensor_components_and_coords,\n    const std::vector<std::string>& grid_names,\n    const std::vector<std::vector<Spectral::Basis>>& bases,\n    const std::vector<std::vector<Spectral::Quadrature>>& quadratures,\n    const std::vector<std::vector<size_t>>& extents,\n    const std::vector<std::string>& expected_components,\n    const std::vector<std::vector<size_t>>& grid_data_orders,\n    const std::optional<double>& components_comparison_precision,\n    const double factor_to_rescale_components,\n    const std::vector<std::string>& invalid_components) {\n  h5::H5File<h5::AccessType::ReadOnly> file_read{h5_file_name};\n  const auto& volume_file =\n      file_read.get<h5::VolumeData>(\"/\"s + group_name, version_number);\n\n  CHECK(extents == volume_file.get_extents(observation_id));\n  const double file_observation_value =\n      volume_file.get_observation_value(observation_id);\n  CHECK((observation_value_epsilon.has_value()\n             ? equal_within_roundoff(file_observation_value, observation_value,\n                                     observation_value_epsilon.value(),\n                                     file_observation_value)\n             : volume_file.get_observation_value(observation_id) ==\n                   observation_value));\n  CHECK(volume_file.find_observation_id(\n            observation_value, observation_value_epsilon) == observation_id);\n  // Check that all of the grid names were written correctly by checking their\n  // equality of elements\n  const std::vector<std::string> read_grid_names =\n      volume_file.get_grid_names(observation_id);\n  [&read_grid_names, &grid_names]() {\n    auto sortable_grid_names = grid_names;\n    auto sortable_read_grid_names = read_grid_names;\n    std::sort(sortable_grid_names.begin(), sortable_grid_names.end(),\n              std::less<>{});\n    std::sort(sortable_read_grid_names.begin(), sortable_read_grid_names.end(),\n              std::less<>{});\n    REQUIRE(sortable_read_grid_names == sortable_grid_names);\n  }();\n  // Find the order the grids were written in\n  std::vector<size_t> grid_positions(read_grid_names.size());\n  for (size_t i = 0; i < grid_positions.size(); i++) {\n    auto grid_name = grid_names[i];\n    auto position =\n        std::find(read_grid_names.begin(), read_grid_names.end(), grid_name);\n    // We know the grid name is in the read_grid_names because of the previous\n    // so we know `position` is an actual pointer to an element\n    grid_positions[i] =\n        static_cast<size_t>(std::distance(read_grid_names.begin(), position));\n  }\n  REQUIRE(volume_file.get_bases(observation_id) == bases);\n  CHECK(volume_file.get_quadratures(observation_id) == quadratures);\n\n  {\n    const auto read_components =\n        volume_file.list_tensor_components(observation_id);\n    CAPTURE(read_components);\n    CAPTURE(expected_components);\n    CHECK(alg::all_of(expected_components,\n                      [&read_components](const std::string& id) {\n                        return alg::found(read_components, id);\n                      }));\n  }\n  // Helper Function to get number of points on a particular grid\n  const auto accumulate_extents = [](const std::vector<size_t>& grid_extents) {\n    return alg::accumulate(grid_extents, 1, std::multiplies<>{});\n  };\n\n  const auto read_extents = volume_file.get_extents(observation_id);\n  std::vector<size_t> element_num_points(\n      boost::make_transform_iterator(read_extents.begin(), accumulate_extents),\n      boost::make_transform_iterator(read_extents.end(), accumulate_extents));\n  const auto read_points_by_element = [&element_num_points]() {\n    std::vector<size_t> read_points(element_num_points.size());\n    read_points[0] = 0;\n    for (size_t index = 1; index < element_num_points.size(); index++) {\n      read_points[index] =\n          read_points[index - 1] + element_num_points[index - 1];\n    }\n    return read_points;\n  }();\n  // Given a DataType, corresponding to contiguous data read out of a\n  // file, find the data which was written by the grid whose extents are\n  // found at position `grid_index` in the vector of extents.\n  const auto get_grid_data = [&element_num_points, &read_points_by_element](\n                                 const auto& all_data,\n                                 const size_t grid_index) {\n    DataType result(element_num_points[grid_index]);\n    // clang-tidy: do not use pointer arithmetic\n    std::copy(\n        &std::get<DataType>(all_data.data)[read_points_by_element[grid_index]],\n        &std::get<DataType>(\n            all_data.data)[read_points_by_element[grid_index]] +  // NOLINT\n            element_num_points[grid_index],\n        result.begin());\n    return result;\n  };\n  // The tensor components can be written in any order to the file, we loop\n  // over the expected components rather than the read components because they\n  // are in a particular order.\n  for (size_t i = 0; i < expected_components.size(); i++) {\n    const auto& component = expected_components[i];\n    const bool component_is_invalid =\n        (alg::find(invalid_components, component) != invalid_components.end());\n    CAPTURE(component);\n    CAPTURE(component_is_invalid);\n    // for each grid\n    for (size_t j = 0; j < grid_names.size(); j++) {\n      CAPTURE(grid_names[j]);\n      CAPTURE(grid_positions[j]);\n      const auto& data = get_grid_data(\n          volume_file.get_tensor_component(observation_id, component),\n          grid_positions[j]);\n      if (component_is_invalid) {\n        for (size_t s = 0; s < data.size(); ++s) {\n          CHECK_THAT(data[s], Catch::Matchers::IsNaN());\n        }\n      } else if (components_comparison_precision.has_value()) {\n        Approx custom_approx =\n            Approx::custom()\n                .epsilon(components_comparison_precision.value())\n                .scale(1.0);\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            data,\n            multiply(factor_to_rescale_components,\n                     tensor_components_and_coords[grid_data_orders[j][i]]),\n            custom_approx);\n      } else {\n        CHECK(data ==\n              multiply(factor_to_rescale_components,\n                       tensor_components_and_coords[grid_data_orders[j][i]]));\n      }\n    }\n  }\n\n  // Read volume data on meshes. We previously tested all the get functions so\n  // we can just use those now.\n  const auto volume_data =\n      volume_file.get_data_by_element(std::nullopt, std::nullopt, std::nullopt);\n  size_t observations_found = 0;\n  for (const auto& single_time_data : volume_data) {\n    if (std::get<0>(single_time_data) != observation_id) {\n      continue;\n    }\n    ++observations_found;\n    const double single_time_data_value = std::get<1>(single_time_data);\n    CHECK((observation_value_epsilon.has_value()\n               ? equal_within_roundoff(\n                     single_time_data_value, observation_value,\n                     observation_value_epsilon.value(), single_time_data_value)\n               : single_time_data_value == approx(observation_value)));\n\n    for (size_t j = 0; j < grid_names.size(); j++) {\n      const std::string& grid_name = grid_names[j];\n      // Find the element in the vector\n      const auto volume_data_it = alg::find_if(\n          std::get<2>(single_time_data),\n          [&grid_name](const ElementVolumeData& local_volume_data) {\n            return local_volume_data.element_name == grid_name;\n          });\n      REQUIRE(volume_data_it != std::get<2>(single_time_data).end());\n      CHECK(volume_data_it->element_name == grid_name);\n\n      for (size_t i = 0; i < expected_components.size(); i++) {\n        const auto& component = expected_components[i];\n        const bool component_is_valid =\n            (alg::find(invalid_components, component) ==\n             invalid_components.end());\n        const auto expected_data = get_grid_data(\n            volume_file.get_tensor_component(observation_id, component),\n            grid_positions[j]);\n        const auto component_data_it =\n            alg::find_if(volume_data_it->tensor_components,\n                         [&component](const TensorComponent& tensor_component) {\n                           return tensor_component.name == component;\n                         });\n        REQUIRE(component_data_it != volume_data_it->tensor_components.end());\n        if (component_is_valid) {\n          CHECK(std::get<DataType>(component_data_it->data) == expected_data);\n        }\n      }\n    }\n  }\n  CHECK(observations_found == 1);\n  if (invalid_components.empty()) {\n    // Test that the data is sorted by copying it, sorting it, and verifying\n    // everything is the same. First sort outer vector by observation value,\n    // then sort the vector of ElementVolumeData on each time slice.\n    auto volume_data_sorted = volume_data;\n    alg::sort(volume_data_sorted, [](const auto& lhs, const auto& rhs) {\n      return std::get<1>(lhs) < std::get<1>(rhs);\n    });\n    for (auto& data_at_time : volume_data_sorted) {\n      alg::sort(std::get<2>(data_at_time),\n                [](const auto& lhs, const auto& rhs) {\n                  return lhs.element_name < rhs.element_name;\n                });\n      for (auto& element_data : std::get<2>(data_at_time)) {\n        alg::sort(element_data.tensor_components,\n                  [](const auto& lhs, const auto& rhs) {\n                    return lhs.name < rhs.name;\n                  });\n      }\n    }\n    CHECK((volume_data == volume_data_sorted));\n  }\n}\n\n#define GET_DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATION(r, data)                                           \\\n  template void check_volume_data(                                       \\\n      const std::string& h5_file_name, const uint32_t version_number,    \\\n      const std::string& group_name, const size_t observation_id,        \\\n      const double observation_value,                                    \\\n      const std::optional<double>& observation_value_epsilon,            \\\n      const std::vector<GET_DTYPE(data)>& tensor_components_and_coords,  \\\n      const std::vector<std::string>& grid_names,                        \\\n      const std::vector<std::vector<Spectral::Basis>>& bases,            \\\n      const std::vector<std::vector<Spectral::Quadrature>>& quadratures, \\\n      const std::vector<std::vector<size_t>>& extents,                   \\\n      const std::vector<std::string>& expected_components,               \\\n      const std::vector<std::vector<size_t>>& grid_data_orders,          \\\n      const std::optional<double>& components_comparison_precision,      \\\n      const double factor_to_rescale_components,                         \\\n      const std::vector<std::string>& invalid_components);\n\nGENERATE_INSTANTIATIONS(INSTANTIATION, (DataVector, std::vector<float>))\n\n#undef INSTANTIATION\n#undef GET_DTYPE\n}  // namespace TestHelpers::io::VolumeData\n"
  },
  {
    "path": "tests/Unit/Helpers/IO/VolumeData.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <string>\n#include <vector>\n\n/// \\cond\nclass DataVector;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\n/// Functions for testing volume data output.\nnamespace TestHelpers::io::VolumeData {\n/// Helper function that multiplies a tensor component by a double, which is\n/// typically the observation time, to generate different tensor values at\n/// different times for testing.\ntemplate <typename T>\nT multiply(const double obs_value, const T& component) {\n  T result = component;\n  for (auto& t : result) {\n    t *= obs_value;\n  }\n  return result;\n}\n\n/// Helper function to check that volume data was written correctly.\n/// This function checks the following:\n///    0. That the provided observation_id is present in the file (possibly\n///       within an epsilon)\n///    1. That the grid_names provided are present in the file\n///    2. That the provided bases and quadratures agree with the bases\n///       and quadratures in the file.\n///    3. That the expected_components are present in the file.\n///    4. That the expected_components, after rescaling them by a constant\n///       factor_to_rescale_components, agree with the components in the file,\n///       except for those that are invalid_components which should be nans in\n///       the file.\n///       Note: if components_comparison_precision is defined, then the\n///       comparison is approximate, using *components_comparison_precision as\n///       the tolerance.\ntemplate <typename DataType>\nvoid check_volume_data(\n    const std::string& h5_file_name, const uint32_t version_number,\n    const std::string& group_name, const size_t observation_id,\n    const double observation_value,\n    const std::optional<double>& observation_value_epsilon,\n    const std::vector<DataType>& tensor_components_and_coords,\n    const std::vector<std::string>& grid_names,\n    const std::vector<std::vector<Spectral::Basis>>& bases,\n    const std::vector<std::vector<Spectral::Quadrature>>& quadratures,\n    const std::vector<std::vector<size_t>>& extents,\n    const std::vector<std::string>& expected_components,\n    const std::vector<std::vector<size_t>>& grid_data_orders,\n    const std::optional<double>& components_comparison_precision,\n    double factor_to_rescale_components = 1.0,\n    const std::vector<std::string>& invalid_components = {});\n}  // namespace TestHelpers::io::VolumeData\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Spectral)\nadd_subdirectory(SphericalHarmonics)\nadd_subdirectory(SpinWeightedSphericalHarmonics)\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/DiscontinuousGalerkin/NumericalFluxes/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NumericalFluxes/NumericalFluxHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/SimpleBoundaryData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers {\nnamespace NumericalFluxes {\n\nnamespace Tags {\nstruct Variable1 : ::db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Variable2 : ::db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n\ntemplate <size_t Dim>\nstruct Variable3 : ::db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim>;\n};\n\ntemplate <size_t Dim>\nstruct Variable4 : ::db::SimpleTag {\n  using type = tnsr::Ij<DataVector, Dim>;\n};\n\ntemplate <size_t Dim>\nstruct CharacteristicSpeeds : ::db::SimpleTag {\n  using type = std::array<DataVector, (Dim + 1) * (Dim + 1)>;\n};\n\n}  // namespace Tags\n\ntemplate <size_t Dim>\nstd::array<DataVector, (Dim + 1) * (Dim + 1)> characteristic_speeds(\n    const Scalar<DataVector>& var_1, const tnsr::I<DataVector, Dim>& var_2,\n    const tnsr::i<DataVector, Dim>& var_3) {\n  std::array<DataVector, (Dim + 1) * (Dim + 1)> result;\n  // Any expression for the characteristic speeds is fine.\n  for (size_t i = 0; i < result.size(); ++i) {\n    gsl::at(result, i) =\n        cos(static_cast<double>(i)) * get(var_1) -\n        (1.0 - sin(static_cast<double>(i))) * get(dot_product(var_2, var_3));\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::Variable1, Tags::Variable2<Dim>,\n                                   Tags::Variable3<Dim>, Tags::Variable4<Dim>>>;\n  using char_speeds_tag = Tags::CharacteristicSpeeds<Dim>;\n};\n\ntemplate <typename Var>\nusing n_dot_f = ::Tags::NormalDotFlux<Var>;\n\ntemplate <size_t Dim>\nusing n_dot_f_tags =\n    tmpl::list<n_dot_f<Tags::Variable1>, n_dot_f<Tags::Variable2<Dim>>,\n               n_dot_f<Tags::Variable3<Dim>>, n_dot_f<Tags::Variable4<Dim>>>;\n\ntemplate <typename FluxType, typename... Args>\nauto get_packaged_data(const FluxType& flux_computer,\n                       const DataVector& used_for_size, const Args&... args) {\n  dg::SimpleBoundaryData<typename FluxType::package_field_tags,\n                         typename FluxType::package_extra_tags>\n      packaged_data{used_for_size.size()};\n  dg::NumericalFluxes::package_data(make_not_null(&packaged_data),\n                                    flux_computer, args...);\n  return packaged_data;\n}\n\nnamespace detail {\ntemplate <size_t Dim, typename FluxType, typename... VariablesTags>\nvoid test_conservation(const FluxType& flux_computer,\n                       const DataVector& used_for_size,\n                       const tmpl::list<VariablesTags...> /*meta*/) {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0.0, 1.0);\n  const size_t num_points = used_for_size.size();\n\n  const auto variables_interior =\n      make_with_random_values<Variables<tmpl::list<VariablesTags...>>>(\n          make_not_null(&gen), make_not_null(&dist), used_for_size);\n  const auto variables_exterior =\n      make_with_random_values<Variables<tmpl::list<VariablesTags...>>>(\n          make_not_null(&gen), make_not_null(&dist), used_for_size);\n  const auto n_dot_f_interior =\n      make_with_random_values<Variables<tmpl::list<VariablesTags...>>>(\n          make_not_null(&gen), make_not_null(&dist), used_for_size);\n  const auto n_dot_f_exterior =\n      make_with_random_values<Variables<tmpl::list<VariablesTags...>>>(\n          make_not_null(&gen), make_not_null(&dist), used_for_size);\n\n  auto packaged_data_interior = get_packaged_data(\n      flux_computer, used_for_size, get<VariablesTags>(n_dot_f_interior)...,\n      get<VariablesTags>(variables_interior)...,\n      TestHelpers::NumericalFluxes::characteristic_speeds(\n          get<Tags::Variable1>(variables_interior),\n          get<Tags::Variable2<Dim>>(variables_interior),\n          get<Tags::Variable3<Dim>>(variables_interior)));\n  auto packaged_data_exterior = get_packaged_data(\n      flux_computer, used_for_size, get<VariablesTags>(n_dot_f_exterior)...,\n      get<VariablesTags>(variables_exterior)...,\n      TestHelpers::NumericalFluxes::characteristic_speeds(\n          get<Tags::Variable1>(variables_exterior),\n          get<Tags::Variable2<Dim>>(variables_exterior),\n          get<Tags::Variable3<Dim>>(variables_exterior)));\n\n  Variables<tmpl::list<VariablesTags...>> n_dot_num_flux_interior(\n      num_points, std::numeric_limits<double>::signaling_NaN());\n  dg::NumericalFluxes::normal_dot_numerical_fluxes(\n      make_not_null(&n_dot_num_flux_interior), flux_computer,\n      packaged_data_interior, packaged_data_exterior);\n\n  Variables<tmpl::list<VariablesTags...>> n_dot_num_flux_exterior(\n      num_points, std::numeric_limits<double>::signaling_NaN());\n  dg::NumericalFluxes::normal_dot_numerical_fluxes(\n      make_not_null(&n_dot_num_flux_exterior), flux_computer,\n      packaged_data_exterior, packaged_data_interior);\n\n  CHECK_VARIABLES_APPROX(n_dot_num_flux_interior, -n_dot_num_flux_exterior);\n}\n}  // namespace detail\n\ntemplate <size_t Dim, typename FluxType>\nvoid test_conservation(const FluxType& flux_computer,\n                       const DataVector& used_for_size) {\n  detail::test_conservation<Dim>(\n      flux_computer, used_for_size,\n      typename System<Dim>::variables_tag::tags_list{});\n}\n\n}  // namespace NumericalFluxes\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/FiniteDifference/Exact.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Reconstruct.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace TestHelpers::fd::reconstruction {\n/*!\n * \\brief Test that the reconstruction procedure can exactly reconstruct\n * polynomials of degree equal to the degree of the basis.\n *\n * For example, for a minmod, MC, or other second-order TVD limiter, we must be\n * able to reconstruct linear functions exactly. For a third-order WENO scheme,\n * we must be able to reconstruct quadratic functions exactly. This is done\n * by setting up the function\n *\n * \\f{align}{\n *  u=\\sum_{i=1}^p x^p + y^p + z^p\n * \\f}\n *\n * where \\f$(x,y,z)=(\\xi,\\eta+4,\\zeta+8)\\f$ and \\f$\\xi,\\eta,\\zeta\\f$ are the\n * logical coordinates. The translation is done to catch subtle bugs that may\n * arise from indexing in the wrong dimension.\n */\ntemplate <size_t Dim, typename F, typename F1>\nvoid test_reconstruction_is_exact_if_in_basis(\n    const size_t max_degree, const size_t points_per_dimension,\n    const size_t stencil_width, const F& invoke_recons,\n    const F1& invoke_reconstruct_neighbor) {\n  CAPTURE(Dim);\n  const size_t number_of_vars = 2;  // arbitrary, 2 is \"cheap but not trivial\"\n\n  const Mesh<Dim> mesh{points_per_dimension, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < Dim; ++i) {\n    logical_coords.get(i) += 4.0 * i;\n  }\n\n  // Compute polynomial on cell centers in FD cluster of points\n  const auto set_polynomial = [max_degree](\n                                  const gsl::not_null<DataVector*> var1_ptr,\n                                  const gsl::not_null<DataVector*> var2_ptr,\n                                  const auto& local_logical_coords) {\n    *var1_ptr = 0.0;\n    *var2_ptr = 100.0;  // some constant offset to distinguish the var values\n    for (size_t degree = 1; degree <= max_degree; ++degree) {\n      for (size_t i = 0; i < Dim; ++i) {\n        *var1_ptr += pow(local_logical_coords.get(i), degree);\n        if (number_of_vars == 2) {\n          *var2_ptr += pow(local_logical_coords.get(i), degree);\n        }\n      }\n    }\n  };\n  DataVector volume_vars{mesh.number_of_grid_points() * number_of_vars, 0.0};\n  DataVector var1(volume_vars.data(), mesh.number_of_grid_points());\n  DataVector var2(volume_vars.data() + mesh.number_of_grid_points(),  // NOLINT\n                  mesh.number_of_grid_points());\n  set_polynomial(&var1, &var2, logical_coords);\n\n  // Compute the polynomial at the cell center for the neighbor data that we\n  // \"received\".\n  //\n  // We do this by computing the solution in our entire neighbor, then using\n  // slice_data to get the subset of points that are needed.\n  DirectionMap<Dim, DataVector> neighbor_data{};\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    DataVector neighbor_vars{mesh.number_of_grid_points() * number_of_vars,\n                             0.0};\n    DataVector neighbor_var1(neighbor_vars.data(),\n                             mesh.number_of_grid_points());\n    DataVector neighbor_var2(\n        neighbor_vars.data() + mesh.number_of_grid_points(),  // NOLINT\n        mesh.number_of_grid_points());\n    set_polynomial(&neighbor_var1, &neighbor_var2, neighbor_logical_coords);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars.data(), neighbor_vars.size()),\n        mesh.extents(), (stencil_width - 1) / 2 + 1,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    neighbor_data[direction] = sliced_data.at(direction.opposite());\n  }\n\n  // Note: reconstructed_num_pts assumes isotropic extents\n  const size_t reconstructed_num_pts =\n      (mesh.extents(0) + 1) * mesh.slice_away(0).number_of_grid_points();\n  std::array<DataVector, Dim> reconstructed_upper_side_of_face_vars =\n      make_array<Dim>(DataVector{reconstructed_num_pts * number_of_vars});\n  std::array<DataVector, Dim> reconstructed_lower_side_of_face_vars =\n      make_array<Dim>(DataVector{reconstructed_num_pts * number_of_vars});\n\n  std::array<gsl::span<double>, Dim> recons_upper_side_of_face{};\n  std::array<gsl::span<double>, Dim> recons_lower_side_of_face{};\n  for (size_t i = 0; i < Dim; ++i) {\n    auto& upper = gsl::at(reconstructed_upper_side_of_face_vars, i);\n    auto& lower = gsl::at(reconstructed_lower_side_of_face_vars, i);\n    gsl::at(recons_upper_side_of_face, i) =\n        gsl::make_span(upper.data(), upper.size());\n    gsl::at(recons_lower_side_of_face, i) =\n        gsl::make_span(lower.data(), lower.size());\n  }\n\n  DirectionMap<Dim, gsl::span<const double>> ghost_cell_vars{};\n  for (const auto& [direction, data] : neighbor_data) {\n    ghost_cell_vars[direction] = gsl::make_span(data.data(), data.size());\n  }\n\n  invoke_recons(make_not_null(&recons_upper_side_of_face),\n                make_not_null(&recons_lower_side_of_face),\n                gsl::make_span(volume_vars.data(), volume_vars.size()),\n                ghost_cell_vars, mesh.extents(), number_of_vars);\n\n  for (size_t dim = 0; dim < Dim; ++dim) {\n    CAPTURE(dim);\n    // Since the reconstruction is to the same physical points the values should\n    // be the same if the polynomial is representable in the basis of the\n    // reconstructor.\n\n    CHECK_ITERABLE_APPROX(gsl::at(reconstructed_upper_side_of_face_vars, dim),\n                          gsl::at(reconstructed_lower_side_of_face_vars, dim));\n\n    // Compare to analytic solution on the faces.\n    const auto basis = make_array<Dim>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<Dim>(Spectral::Quadrature::CellCentered);\n    auto extents = make_array<Dim>(points_per_dimension);\n    gsl::at(extents, dim) = points_per_dimension + 1;\n    gsl::at(quadrature, dim) = Spectral::Quadrature::FaceCentered;\n    const Mesh<Dim> face_centered_mesh{extents, basis, quadrature};\n    auto logical_coords_face_centered = logical_coordinates(face_centered_mesh);\n    for (size_t i = 1; i < Dim; ++i) {\n      logical_coords_face_centered.get(i) =\n          logical_coords_face_centered.get(i) + 4.0 * i;\n    }\n\n    DataVector expected_volume_vars{\n        face_centered_mesh.number_of_grid_points() * number_of_vars, 0.0};\n    DataVector expected_var1(expected_volume_vars.data(),\n                             face_centered_mesh.number_of_grid_points());\n    DataVector expected_var2(\n        expected_volume_vars.data() +\n            face_centered_mesh.number_of_grid_points(),  // NOLINT\n        face_centered_mesh.number_of_grid_points());\n    set_polynomial(&expected_var1, &expected_var2,\n                   logical_coords_face_centered);\n    CHECK_ITERABLE_APPROX(gsl::at(reconstructed_upper_side_of_face_vars, dim),\n                          expected_volume_vars);\n\n    // Test fd::reconstruction::reconstruct_neighbor\n    Index<Dim> ghost_data_extents = mesh.extents();\n    ghost_data_extents[dim] = (stencil_width + 1) / 2;\n    Index<Dim> extents_with_faces = mesh.extents();\n    ++extents_with_faces[dim];\n\n    const Direction<Dim> upper_direction{dim, Side::Upper};\n    DataVector upper_face_var1{mesh.extents().slice_away(dim).product()};\n    DataVector upper_face_var2{mesh.extents().slice_away(dim).product()};\n    auto& upper_neighbor_data = neighbor_data.at(upper_direction);\n    DataVector upper_neighbor_var1{upper_neighbor_data.data(),\n                                   ghost_data_extents.product()};\n    DataVector upper_neighbor_var2{\n        // NOLINTNEXTLINE\n        upper_neighbor_data.data() + ghost_data_extents.product(),\n        ghost_data_extents.product()};\n    invoke_reconstruct_neighbor(make_not_null(&upper_face_var1), var1,\n                                upper_neighbor_var1, mesh.extents(),\n                                ghost_data_extents, upper_direction);\n    invoke_reconstruct_neighbor(make_not_null(&upper_face_var2), var2,\n                                upper_neighbor_var2, mesh.extents(),\n                                ghost_data_extents, upper_direction);\n    for (SliceIterator si(extents_with_faces, dim, extents_with_faces[dim] - 1);\n         si; ++si) {\n      INFO(\"Upper side\");\n      CAPTURE(si.slice_offset());\n      CAPTURE(si.volume_offset());\n      CHECK(approx(upper_face_var1[si.slice_offset()]) ==\n            expected_var1[si.volume_offset()]);\n      CHECK(approx(upper_face_var2[si.slice_offset()]) ==\n            expected_var2[si.volume_offset()]);\n    }\n\n    const Direction<Dim> lower_direction{dim, Side::Lower};\n    DataVector lower_face_var1{mesh.extents().slice_away(dim).product()};\n    DataVector lower_face_var2{mesh.extents().slice_away(dim).product()};\n    auto& lower_neighbor_data = neighbor_data.at(lower_direction);\n    DataVector lower_neighbor_var1{lower_neighbor_data.data(),\n                                   ghost_data_extents.product()};\n    DataVector lower_neighbor_var2{\n        // NOLINTNEXTLINE\n        lower_neighbor_data.data() + ghost_data_extents.product(),\n        ghost_data_extents.product()};\n    invoke_reconstruct_neighbor(make_not_null(&lower_face_var1), var1,\n                                lower_neighbor_var1, mesh.extents(),\n                                ghost_data_extents, lower_direction);\n    invoke_reconstruct_neighbor(make_not_null(&lower_face_var2), var2,\n                                lower_neighbor_var2, mesh.extents(),\n                                ghost_data_extents, lower_direction);\n    for (SliceIterator si(extents_with_faces, dim, 0); si; ++si) {\n      INFO(\"Lower side\");\n      CAPTURE(si.slice_offset());\n      CAPTURE(si.volume_offset());\n      CHECK(approx(lower_face_var1[si.slice_offset()]) ==\n            expected_var1[si.volume_offset()]);\n      CHECK(approx(lower_face_var2[si.slice_offset()]) ==\n            expected_var2[si.volume_offset()]);\n    }\n  }\n}\n}  // namespace TestHelpers::fd::reconstruction\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/FiniteDifference/Python.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace TestHelpers::fd::reconstruction {\n/*!\n * \\brief Compare the output of reconstruction done once in python and once in\n * C++.\n *\n * - `extents` The extents are the volume extents of the subcell grid in a\n * DG-subcell scheme. In general, this should be the size of the reconstruction\n * stencil plus one or larger to provide a good test.\n * - `stencil_width` the width of the reconstruction stencil. For example, for\n * minmod or MC this would be 3, for 5-point 5th-order WENO this would\n * be 5, and for three three-point stencil CWENO this would be 5 as well.\n * -`python_test_file` the file in which the python function to compare the\n * output result is defined in.\n * - `python_function` the python function whose output will be compared to\n * - `recons_function` an invokable (usually a lambda) that has the following\n * signature:\n * \\code{.cpp}\n *  (const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n *              reconstructed_upper_side_of_face_vars,\n *  const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n *              reconstructed_lower_side_of_face_vars,\n *  const gsl::span<const double>& volume_vars,\n *  const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n *  const Index<Dim>& volume_extents, const size_t number_of_variables)\n * \\endcode\n */\ntemplate <size_t Dim, typename ReconsFunction, typename F1,\n          typename... ExtraPyArgs>\nvoid test_with_python(const Index<Dim>& extents, const size_t stencil_width,\n                      const std::string& python_test_file,\n                      const std::string& python_function,\n                      const ReconsFunction& recons_function,\n                      const F1& invoke_reconstruct_neighbor,\n                      const ExtraPyArgs&... extra_py_args) {\n  CAPTURE(extents);\n  CAPTURE(Dim);\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n\n  const size_t ghost_zone_size = (stencil_width - 1) / 2;\n  const size_t ghost_cells_from_neighbor = ghost_zone_size + 1;\n  Index<Dim> extents_with_ghosts = extents;\n  for (size_t i = 0; i < Dim; ++i) {\n    // 2 because we extend in both upper and lower direction.\n    extents_with_ghosts[i] += 2 * ghost_cells_from_neighbor;\n  }\n  const size_t number_of_vars = 1;\n  std::vector<double> vars_with_ghosts(extents_with_ghosts.product() *\n                                       number_of_vars);\n  fill_with_random_values(make_not_null(&vars_with_ghosts), make_not_null(&gen),\n                          make_not_null(&dist));\n  // Copy out interior and neighbor data since that's what we will be passing\n  // to the C++ implementation.\n  const auto in_volume = [ghost_cells_from_neighbor, extents](\n                             const auto& idx, const size_t dim) {\n    return idx >= ghost_cells_from_neighbor and\n           idx < extents[dim] + ghost_cells_from_neighbor;\n  };\n  const auto in_upper_neighbor = [ghost_cells_from_neighbor, extents](\n                                     const auto& idx, const size_t dim) {\n    return idx >= extents[dim] + ghost_cells_from_neighbor;\n  };\n  const auto in_lower_neighbor = [ghost_cells_from_neighbor](const auto& idx) {\n    return idx < ghost_cells_from_neighbor;\n  };\n\n  DirectionMap<Dim, std::vector<double>> ghost_cells{};\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    ghost_cells[direction] = std::vector<double>{};\n  }\n  std::vector<double> volume_vars(extents.product() * number_of_vars);\n  for (size_t k = 0; k < (Dim > 2 ? extents_with_ghosts[2] : 1); ++k) {\n    for (size_t j = 0; j < (Dim > 1 ? extents_with_ghosts[1] : 1); ++j) {\n      for (size_t i = 0; i < extents_with_ghosts[0]; ++i) {\n        if constexpr (Dim > 1) {\n          // Check if we are in a Voronoi neighbor (ignore then)\n          std::vector<bool> has_neighbor(Dim);\n          has_neighbor.at(0) = in_lower_neighbor(i) or in_upper_neighbor(i, 0);\n          has_neighbor.at(1) = in_lower_neighbor(j) or in_upper_neighbor(j, 1);\n          if constexpr (Dim > 2) {\n            has_neighbor.at(2) =\n                in_lower_neighbor(k) or in_upper_neighbor(k, 2);\n          }\n          if (std::count(has_neighbor.begin(), has_neighbor.end(), true) >= 2) {\n            continue;\n          }\n        }\n\n        std::array volume_with_ghost_indices{i, j, k};\n        Index<Dim> volume_with_ghosts_index{};\n        for (size_t d = 0; d < Dim; ++d) {\n          volume_with_ghosts_index[d] = gsl::at(volume_with_ghost_indices, d);\n        }\n\n        // If in volume, copy over...\n        if (in_volume(i, 0) and\n            (Dim == 1 or (in_volume(j, 1) and (Dim == 2 or in_volume(k, 2))))) {\n          Index<Dim> volume_index{};\n          for (size_t d = 0; d < Dim; ++d) {\n            volume_index[d] = gsl::at(volume_with_ghost_indices, d) -\n                              (extents_with_ghosts[d] - extents[d]) / 2;\n          }\n\n          volume_vars[collapsed_index(volume_index, extents)] =\n              vars_with_ghosts[collapsed_index(volume_with_ghosts_index,\n                                               extents_with_ghosts)];\n        } else {\n          // We are dealing with something in the neighbor data. We need to\n          // identify which neighbor and then set.\n          Side side = Side::Lower;\n          size_t dimension = 0;\n          if (in_upper_neighbor(i, 0)) {\n            side = Side::Upper;\n          }\n          if (Dim > 1 and in_lower_neighbor(j)) {\n            dimension = 1;\n          }\n          if (Dim > 1 and in_upper_neighbor(j, 1)) {\n            side = Side::Upper;\n            dimension = 1;\n          }\n          if (Dim > 2 and in_lower_neighbor(k)) {\n            dimension = 2;\n          }\n          if (Dim > 2 and in_upper_neighbor(k, 2)) {\n            side = Side::Upper;\n            dimension = 2;\n          }\n\n          const Direction<Dim> direction{dimension, side};\n          ghost_cells.at(direction).push_back(vars_with_ghosts[collapsed_index(\n              volume_with_ghosts_index, extents_with_ghosts)]);\n        }\n      }\n    }\n  }\n\n  const size_t reconstructed_num_pts =\n      (extents[0] + 1) * extents.slice_away(0).product();\n  std::array<std::vector<double>, Dim> reconstructed_upper_side_of_face_vars =\n      make_array<Dim>(\n          std::vector<double>(reconstructed_num_pts * number_of_vars));\n  std::array<std::vector<double>, Dim> reconstructed_lower_side_of_face_vars =\n      make_array<Dim>(\n          std::vector<double>(reconstructed_num_pts * number_of_vars));\n\n  std::array<gsl::span<double>, Dim> recons_upper_side_of_face{};\n  std::array<gsl::span<double>, Dim> recons_lower_side_of_face{};\n  for (size_t i = 0; i < Dim; ++i) {\n    auto& upper = gsl::at(reconstructed_upper_side_of_face_vars, i);\n    auto& lower = gsl::at(reconstructed_lower_side_of_face_vars, i);\n    gsl::at(recons_upper_side_of_face, i) =\n        gsl::make_span(upper.data(), upper.size());\n    gsl::at(recons_lower_side_of_face, i) =\n        gsl::make_span(lower.data(), lower.size());\n  }\n\n  DirectionMap<Dim, gsl::span<const double>> ghost_cell_vars{};\n  for (const auto& [direction, data] : ghost_cells) {\n    ghost_cell_vars[direction] = gsl::make_span(data.data(), data.size());\n  }\n\n  recons_function(make_not_null(&recons_upper_side_of_face),\n                  make_not_null(&recons_lower_side_of_face),\n                  gsl::make_span(volume_vars), ghost_cell_vars, extents,\n                  number_of_vars);\n\n  const std::vector<size_t> extents_with_ghosts_vector(\n      extents_with_ghosts.begin(), extents_with_ghosts.end());\n  const std::vector<size_t> ghost_zones(Dim, ghost_zone_size);\n  for (size_t var_index = 0; var_index < number_of_vars; ++var_index) {\n    CAPTURE(var_index);\n    const std::vector<double> var(\n        vars_with_ghosts.begin() +\n            static_cast<std::ptrdiff_t>(var_index *\n                                        extents_with_ghosts.product()),\n        vars_with_ghosts.begin() +\n            static_cast<std::ptrdiff_t>((var_index + 1) *\n                                        extents_with_ghosts.product()));\n    const auto result =\n        pypp::call<std::vector<std::vector<std::vector<double>>>>(\n            python_test_file, python_function, var, extents_with_ghosts_vector,\n            Dim, extra_py_args...);\n\n    const std::vector<std::vector<double>>& python_recons_on_lower = result[0];\n    const std::vector<std::vector<double>>& python_recons_on_upper = result[1];\n    for (size_t d = 0; d < Dim; ++d) {\n      CAPTURE(d);\n      const std::vector<double> recons_upper_side_this_var(\n          gsl::at(recons_upper_side_of_face, d).begin() +\n              var_index * reconstructed_num_pts,\n          gsl::at(recons_upper_side_of_face, d).begin() +\n              (var_index + 1) * reconstructed_num_pts);\n      CHECK_ITERABLE_APPROX(recons_upper_side_this_var,\n                            python_recons_on_upper[d]);\n\n      const std::vector<double> recons_lower_side_this_var(\n          gsl::at(recons_lower_side_of_face, d).begin() +\n              var_index * reconstructed_num_pts,\n          gsl::at(recons_lower_side_of_face, d).begin() +\n              (var_index + 1) * reconstructed_num_pts);\n      CHECK_ITERABLE_APPROX(recons_lower_side_this_var,\n                            python_recons_on_lower[d]);\n\n      // Test fd::reconstruction::reconstruct_neighbor\n      Index<Dim> ghost_data_extents = extents;\n      ghost_data_extents[d] = (stencil_width + 1) / 2;\n      Index<Dim> extents_with_faces = extents;\n      ++extents_with_faces[d];\n      const DataVector dv_var{\n          const_cast<double*>(volume_vars.data() +\n                              var_index * extents.product()),\n          extents.product()};\n\n      const Direction<Dim> upper_direction{d, Side::Upper};\n      DataVector upper_face_var{extents.slice_away(d).product()};\n      auto& upper_neighbor_data = ghost_cells.at(upper_direction);\n      DataVector upper_neighbor_var{\n          // NOLINTNEXTLINE\n          upper_neighbor_data.data() + var_index * ghost_data_extents.product(),\n          ghost_data_extents.product()};\n      invoke_reconstruct_neighbor(make_not_null(&upper_face_var), dv_var,\n                                  upper_neighbor_var, extents,\n                                  ghost_data_extents, upper_direction);\n      for (SliceIterator si(extents_with_faces, d, extents_with_faces[d] - 1);\n           si; ++si) {\n        CHECK(approx(upper_face_var[si.slice_offset()]) ==\n              recons_upper_side_this_var[si.volume_offset()]);\n      }\n\n      const Direction<Dim> lower_direction{d, Side::Lower};\n      DataVector lower_face_var{extents.slice_away(d).product()};\n      auto& lower_neighbor_data = ghost_cells.at(lower_direction);\n      DataVector lower_neighbor_var{\n          // NOLINTNEXTLINE\n          lower_neighbor_data.data() + var_index * ghost_data_extents.product(),\n          ghost_data_extents.product()};\n      invoke_reconstruct_neighbor(make_not_null(&lower_face_var), dv_var,\n                                  lower_neighbor_var, extents,\n                                  ghost_data_extents, lower_direction);\n      for (SliceIterator si(extents_with_faces, d, 0); si; ++si) {\n        CHECK(approx(lower_face_var[si.slice_offset()]) ==\n              recons_lower_side_this_var[si.volume_offset()]);\n      }\n    }\n  }\n}\n}  // namespace TestHelpers::fd::reconstruction\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/FiniteDifference/Roundoff.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TestHelpers::fd::reconstruction {\n/*!\n * Test that the reconstruction procedure produces positive results in\n * the presence of a very large jump which may cause roundoff error.\n */\ntemplate <typename F>\nvoid test_positivity_with_roundoff(const size_t stencil_width,\n                                   const F& invoke_recons) {\n  const size_t num_fd_points = 4;\n  const Mesh<1> mesh{num_fd_points, Spectral::Basis::FiniteDifference,\n                     Spectral::Quadrature::CellCentered};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  std::vector<double> volume_vars(mesh.number_of_grid_points(), 0.0);\n  DataVector var(volume_vars.data(), mesh.number_of_grid_points());\n\n  const double small_value = 1.0e-16;\n  const double large_value = 1.0e10;\n  for (size_t i = 0; i < mesh.number_of_grid_points(); ++i) {\n    const double x = get<0>(logical_coords)[i];\n    var[i] = x > 0.0 ? x * large_value : small_value;\n  }\n\n  const size_t number_of_ghost_zones = (stencil_width - 1) / 2 + 1;\n  DirectionMap<1, std::vector<double>> neighbor_data{};\n  neighbor_data[Direction<1>::upper_xi()] =\n      std::vector<double>(number_of_ghost_zones, large_value);\n  neighbor_data[Direction<1>::lower_xi()] =\n      std::vector<double>(number_of_ghost_zones, small_value);\n\n  std::vector<double> reconstructed_upper_side_of_face_vars(\n      mesh.number_of_grid_points() + 1);\n  std::vector<double> reconstructed_lower_side_of_face_vars(\n      mesh.number_of_grid_points() + 1);\n\n  std::array<gsl::span<double>, 1> recons_upper_side_of_face{\n      gsl::make_span(reconstructed_upper_side_of_face_vars.data(),\n                     reconstructed_upper_side_of_face_vars.size())};\n  std::array<gsl::span<double>, 1> recons_lower_side_of_face{\n      gsl::make_span(reconstructed_lower_side_of_face_vars.data(),\n                     reconstructed_lower_side_of_face_vars.size())};\n\n  DirectionMap<1, gsl::span<const double>> ghost_cell_vars{};\n  for (const auto& [direction, data] : neighbor_data) {\n    ghost_cell_vars[direction] = gsl::make_span(data.data(), data.size());\n  }\n\n  invoke_recons(make_not_null(&recons_upper_side_of_face),\n                make_not_null(&recons_lower_side_of_face),\n                gsl::make_span(volume_vars.data(), volume_vars.size()),\n                ghost_cell_vars, mesh.extents(), 1);\n\n  for (const auto reconstructed : reconstructed_upper_side_of_face_vars) {\n    CHECK(reconstructed > 0.0);\n  }\n  for (const auto reconstructed : reconstructed_lower_side_of_face_vars) {\n    CHECK(reconstructed > 0.0);\n  }\n}\n}  // namespace TestHelpers::fd::reconstruction\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/LinearSolver/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <pup.h>\n\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n\nnamespace TestHelpers::LinearSolver {\n\ntemplate <typename ValueType>\nstruct ApplyMatrix {\n  blaze::DynamicMatrix<ValueType> matrix;\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable size_t invocations = 0;\n  template <typename ResultVectorType, typename OperandVectorType>\n  void operator()(const gsl::not_null<ResultVectorType*> result,\n                  const OperandVectorType& operand) const {\n    *result = matrix * operand;\n    ++invocations;\n  }\n};\n\n// Use the exact inverse of the matrix as preconditioner. This should solve\n// problems in 1 iteration.\nstruct ExactInversePreconditioner {\n  template <typename ValueType, typename SolutionVectorType,\n            typename SourceVectorType>\n  void solve(const gsl::not_null<SolutionVectorType*> solution,\n             const ApplyMatrix<ValueType>& linear_operator,\n             const SourceVectorType& source,\n             const std::tuple<>& /*operator_args*/) const {\n    if (not inv_matrix_.has_value()) {\n      inv_matrix_ = blaze::inv(linear_operator.matrix);\n    }\n    *solution = *inv_matrix_ * source;\n  }\n\n  void reset() { inv_matrix_.reset(); }\n\n  void pup(PUP::er& p) { p | inv_matrix_; }  // NOLINT\n\n  // Make option-creatable for factory tests\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\"halp\"};\n\n private:\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::optional<blaze::DynamicMatrix<double>> inv_matrix_{};\n};\n\n// Use the inverse of the diagonal as preconditioner.\nstruct JacobiPreconditioner {\n  template <typename ValueType, typename SolutionVectorType,\n            typename SourceVectorType>\n  void solve(const gsl::not_null<SolutionVectorType*> solution,\n             const ApplyMatrix<ValueType>& linear_operator,\n             const SourceVectorType& source,\n             const std::tuple<>& /*operator_args*/) const {\n    if (not inv_diagonal_.has_value()) {\n      inv_diagonal_ = blaze::DynamicVector<double>(source.size(), 1.);\n      for (size_t i = 0; i < source.size(); ++i) {\n        (*inv_diagonal_)[i] /= linear_operator.matrix(i, i);\n      }\n    }\n    *solution = source;\n    for (size_t i = 0; i < solution->size(); ++i) {\n      (*solution)[i] *= (*inv_diagonal_)[i];\n    }\n  }\n\n  void reset() { inv_diagonal_.reset(); }\n\n  void pup(PUP::er& p) { p | inv_diagonal_; }  // NOLINT\n\n private:\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable std::optional<blaze::DynamicVector<double>> inv_diagonal_{};\n};\n\n// Run a few Richardson iterations as preconditioner.\nstruct RichardsonPreconditioner {\n  RichardsonPreconditioner() = default;\n  RichardsonPreconditioner(const double relaxation_parameter,\n                           const size_t num_iterations)\n      : relaxation_parameter_(relaxation_parameter),\n        num_iterations_(num_iterations) {}\n\n  template <typename ValueType, typename SolutionVectorType,\n            typename SourceVectorType>\n  void solve(\n      const gsl::not_null<SolutionVectorType*> initial_guess_in_solution_out,\n      const ApplyMatrix<ValueType>& linear_operator,\n      const SourceVectorType& source,\n      const std::tuple<>& /*operator_args*/) const {\n    for (size_t i = 0; i < num_iterations_; ++i) {\n      linear_operator(make_not_null(&correction_buffer_),\n                      *initial_guess_in_solution_out);\n      correction_buffer_ *= -1.;\n      correction_buffer_ += source;\n      *initial_guess_in_solution_out +=\n          relaxation_parameter_ * correction_buffer_;\n    }\n  }\n\n  static void reset() {}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | relaxation_parameter_;\n    p | num_iterations_;\n  }\n\n private:\n  double relaxation_parameter_{};\n  size_t num_iterations_{};\n  // NOLINTNEXTLINE(spectre-mutable)\n  mutable blaze::DynamicVector<double> correction_buffer_{};\n};\n\n}  // namespace TestHelpers::LinearSolver\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/Spectral/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"SpectralHelpers\")\n\nset(LIBRARY_SOURCES\n  DiskTestFunctions.cpp\n  FourierTestFunctions.cpp\n  PolynomialTestFunctions.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Catch2::Catch2\n  DataStructures\n  DataStructuresHelpers\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/Spectral/DiskTestFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/NumericalAlgorithms/Spectral/DiskTestFunctions.hpp\"\n\n#include <cmath>\n#include <numbers>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace DiskTestFunctions {\n\nProductOfPolynomials::ProductOfPolynomials(const size_t pow_nx,\n                                           const size_t pow_ny)\n    : pow_nx_{pow_nx}, pow_ny_{pow_ny} {\n  ASSERT(\n      pow_nx_ / 2 + pow_ny_ / 2 + 1 <= 20,\n      \"Powers too large to compute factorials required for definite integral\");\n}\n\nDataVector ProductOfPolynomials::operator()(const DataVector& r,\n                                            const DataVector& phi) const {\n  return pow(r * cos(phi), static_cast<double>(pow_nx_)) *\n         pow(r * sin(phi), static_cast<double>(pow_ny_));\n}\n\ndouble ProductOfPolynomials::operator()(const double r,\n                                        const double phi) const {\n  return pow(r * cos(phi), static_cast<double>(pow_nx_)) *\n         pow(r * sin(phi), static_cast<double>(pow_ny_));\n}\n\nDataVector ProductOfPolynomials::df_dr(const DataVector& r,\n                                       const DataVector& phi) const {\n  if (pow_nx_ + pow_ny_ == 0) {\n    return DataVector{r.size(), 0.0};\n  }\n  return static_cast<double>(pow_nx_ + pow_ny_) *\n         pow(r, pow_nx_ + pow_ny_ - 1) *\n         pow(cos(phi), static_cast<double>(pow_nx_)) *\n         pow(sin(phi), static_cast<double>(pow_ny_));\n}\n\nDataVector ProductOfPolynomials::df_dph(const DataVector& r,\n                                        const DataVector& phi) const {\n  if (pow_nx_ + pow_ny_ == 0) {\n    return DataVector{r.size(), 0.0};\n  }\n  if (pow_nx_ == 0) {\n    return static_cast<double>(pow_ny_) * pow(r, pow_ny_) * cos(phi) *\n           pow(sin(phi), static_cast<double>(pow_ny_ - 1));\n  }\n  if (pow_ny_ == 0) {\n    return -static_cast<double>(pow_nx_) * pow(r, pow_nx_) *\n           pow(cos(phi), static_cast<double>(pow_nx_ - 1)) * sin(phi);\n  }\n  return static_cast<double>(pow_ny_) * pow(r, pow_nx_ + pow_ny_) *\n             pow(cos(phi), static_cast<double>(pow_nx_ + 1)) *\n             pow(sin(phi), static_cast<double>(pow_ny_ - 1)) -\n         static_cast<double>(pow_nx_) * pow(r, pow_nx_ + pow_ny_) *\n             pow(cos(phi), static_cast<double>(pow_nx_ - 1)) *\n             pow(sin(phi), static_cast<double>(pow_ny_ + 1));\n}\n\ndouble ProductOfPolynomials::definite_integral() const {\n  if ((pow_nx_ % 2 == 1) or (pow_ny_ % 2 == 1)) {\n    return 0.0;\n  }\n  if (pow_nx_ + pow_ny_ == 0) {\n    return std::numbers::pi;\n  }\n  if (pow_nx_ == 0) {\n    return std::numbers::pi * pow(2.0, 1 - static_cast<int>(pow_ny_)) *\n           static_cast<double>(factorial(pow_ny_ - 1)) /\n           static_cast<double>(factorial(pow_ny_ / 2 - 1)) /\n           static_cast<double>(factorial(pow_ny_ / 2 + 1));\n  }\n  if (pow_ny_ == 0) {\n    return std::numbers::pi * pow(2.0, 1 - static_cast<int>(pow_nx_)) *\n           static_cast<double>(factorial(pow_nx_ - 1)) /\n           static_cast<double>(factorial(pow_nx_ / 2 - 1)) /\n           static_cast<double>(factorial(pow_nx_ / 2 + 1));\n  }\n  return std::numbers::pi * pow(2.0, 2 - static_cast<int>(pow_nx_ + pow_ny_)) *\n         static_cast<double>(factorial(pow_nx_ - 1)) *\n         static_cast<double>(factorial(pow_ny_ - 1)) /\n         static_cast<double>(factorial(pow_nx_ / 2 - 1)) /\n         static_cast<double>(factorial(pow_ny_ / 2 - 1)) /\n         static_cast<double>(factorial(pow_nx_ / 2 + pow_ny_ / 2 + 1));\n}\n}  // namespace DiskTestFunctions\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/Spectral/DiskTestFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace DiskTestFunctions {\n\n/*!\n * \\brief Product of polynomials regular on the surface of a disk\n *\n * \\details Computes \\f$ n_x^{k_x} n_y^{k_y} \\f$ where \\f$n_x = r \\cos \\phi\\f$\n * and \\f$n_y = r \\sin \\phi\\f$.  The function and its first derivatives are\n * exactly representable by Fourier modes of order \\f$(M)\\f$ if \\f$M > k_x +\n * k_y\\f$.\n */\nclass ProductOfPolynomials {\n public:\n  ProductOfPolynomials(size_t pow_nx, size_t pow_ny);\n  DataVector operator()(const DataVector& r, const DataVector& phi) const;\n  double operator()(double r, double phi) const;\n  DataVector df_dr(const DataVector& r, const DataVector& phi) const;\n  DataVector df_dph(const DataVector& r, const DataVector& phi) const;\n  double definite_integral() const;\n\n private:\n  size_t pow_nx_;\n  size_t pow_ny_;\n};\n}  // namespace DiskTestFunctions\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/Spectral/FourierTestFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/NumericalAlgorithms/Spectral/FourierTestFunctions.hpp\"\n\n#include <cmath>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Fourier.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace FourierTestFunctions {\n\nProductOfPolynomials::ProductOfPolynomials(const size_t pow_nx,\n                                           const size_t pow_ny)\n    : pow_nx_{pow_nx}, pow_ny_{pow_ny} {}\n\nDataVector ProductOfPolynomials::operator()(const DataVector& phi) const {\n  return pow(cos(phi), static_cast<double>(pow_nx_)) *\n         pow(sin(phi), static_cast<double>(pow_ny_));\n}\n\ndouble ProductOfPolynomials::operator()(const double phi) const {\n  return pow(cos(phi), static_cast<double>(pow_nx_)) *\n         pow(sin(phi), static_cast<double>(pow_ny_));\n}\n\nDataVector ProductOfPolynomials::df_dph(const DataVector& phi) const {\n  if (pow_nx_ + pow_ny_ == 0) {\n    return DataVector{phi.size(), 0.0};\n  }\n  if (pow_nx_ == 0) {\n    return static_cast<double>(pow_ny_) * cos(phi) *\n           pow(sin(phi), static_cast<double>(pow_ny_ - 1));\n  }\n  if (pow_ny_ == 0) {\n    return -static_cast<double>(pow_nx_) *\n           pow(cos(phi), static_cast<double>(pow_nx_ - 1)) * sin(phi);\n  }\n  return static_cast<double>(pow_ny_) *\n             pow(cos(phi), static_cast<double>(pow_nx_ + 1)) *\n             pow(sin(phi), static_cast<double>(pow_ny_ - 1)) -\n         static_cast<double>(pow_nx_) *\n             pow(cos(phi), static_cast<double>(pow_nx_ - 1)) *\n             pow(sin(phi), static_cast<double>(pow_ny_ + 1));\n}\n\ndouble ProductOfPolynomials::definite_integral() const {\n  if ((pow_nx_ % 2 == 1) or (pow_ny_ % 2 == 1)) {\n    return 0.0;\n  }\n  double product = 1.0;\n  double m = 0.0;\n  for (size_t i = 1; i <= pow_nx_ / 2; ++i) {\n    m += 2.0;\n    product *= (m - 1.0) / m;\n  }\n  double n = 0.0;\n  for (size_t j = 1; j <= pow_ny_ / 2; ++j) {\n    n += 2.0;\n    product *= (n - 1.0) / (m + n);\n  }\n\n  return 2.0 * M_PI * product;\n}\n\nDataVector ProductOfPolynomials::modes() const {\n  const size_t p = pow_nx_;\n  const size_t q = pow_ny_;\n  const size_t j = p + q;\n  DataVector result{2 * j + 1, 0.0};\n  const bool q_is_odd = q % 2 == 1;\n  const double overall_sign = (q / 2) % 2 == 0 ? 1.0 : -1.0;\n  const auto two_to_the_j = static_cast<double>(two_to_the(j));\n  for (size_t s = 0; s <= p; ++s) {\n    const auto bin_p_s = static_cast<double>(binomial(p, s));\n    for (size_t l = 0; l <= q; ++l) {\n      const auto bin_q_l = static_cast<double>(binomial(q, l));\n      const double sign = (q - l) % 2 == 0 ? 1.0 : -1.0;\n      const int m = static_cast<int>(2 * s + 2 * l) - static_cast<int>(j);\n      const size_t storage_index =\n          Spectral::Fourier::modal_storage_index(q_is_odd ? -abs(m) : abs(m));\n      const double parity = (q_is_odd and m < 0) ? -1.0 : 1.0;\n      result[storage_index] += parity * sign * bin_q_l * bin_p_s;\n    }\n  }\n  result *= (overall_sign / two_to_the_j);\n  return result;\n}\n}  // namespace FourierTestFunctions\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/Spectral/FourierTestFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\n/// \\endcond\n\nnamespace FourierTestFunctions {\n\n/*!\n * \\brief Product of polynomials regular on the surface of a circle\n *\n * \\details Computes \\f$ n_x^{k_x} n_y^{k_y} \\f$ where \\f$n_x = \\cos \\phi\\f$\n * and \\f$n_y = \\sin \\phi\\f$.  The function and its first derivatives are\n * exactly representable by Fourier modes of order \\f$(M)\\f$ if \\f$M > k_x +\n * k_y\\f$.\n */\nclass ProductOfPolynomials {\n public:\n  ProductOfPolynomials(size_t pow_nx, size_t pow_ny);\n  DataVector operator()(const DataVector& phi) const;\n  double operator()(double phi) const;\n  DataVector df_dph(const DataVector& phi) const;\n  double definite_integral() const;\n  /*!\n   * \\brief A modal vector of the Fourier modes\n   *\n   * \\details The modal coefficients are stored in a ModalVector as\n   * \\f$\\{u_0, u_1, u_{-1}, u_2, u_{-2}, \\ldots, u_M, u_{-M}\\}\\f$.\n   *\n   * The modes can be determined from Equation 18 of \\cite Mathar2009\n   * \\f{align*}{\n   * \\cos^p \\phi \\sin^q \\phi = \\frac{(-1)^{q/2}}{2^{p+q}} \\sum_{s=0}^p\n   * \\sum_{\\ell=0}^q \\binom{p}{s} \\binom{q}{\\ell} (-1)^{q-\\ell} \\times &\n   *  \\begin{cases}\n   *     \\cos \\left[(2s-p+2\\ell-q)\\phi\\right],\\; q \\text{ is even} \\\\\n   *     \\sin \\left[(2s-p+2\\ell-q)\\phi\\right],\\; q \\text{ is odd}\n   *  \\end{cases}\n   * \\f}\n   */\n  DataVector modes() const;\n\n private:\n  size_t pow_nx_;\n  size_t pow_ny_;\n};\n}  // namespace FourierTestFunctions\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/Spectral/PolynomialTestFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/NumericalAlgorithms/Spectral/PolynomialTestFunctions.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/IntegrationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace PolynomialTestFunctions {\n\nnamespace {\nvoid test_definite_integral(const DataVector& u, const DataVector& weights,\n                            const Monomial& f) {\n  const double result = ddot_(u.size(), weights.data(), 1, u.data(), 1);\n  CHECK(f.definite_integral() == approx(result));\n}\n\nvoid test_derivative(const DataVector& u, const Matrix& m, const Monomial& f,\n                     const DataVector& xi) {\n  const auto custom_approx = Approx::custom().epsilon(1.0e-13).scale(1.0);\n  const DataVector expected_du = f.df_dx(xi);\n  const DataVector du = apply_matrix(m, u);\n  CHECK_ITERABLE_CUSTOM_APPROX(du, expected_du, custom_approx);\n}\n\nvoid test_indefinite_integral(const DataVector& u, const Matrix& m,\n                              const Monomial& f, const DataVector& xi) {\n  const DataVector expected_int_u = f.int_f(xi);\n  const DataVector int_u = apply_matrix(m, u);\n  CHECK_ITERABLE_APPROX(int_u, expected_int_u);\n}\n\nvoid test_interpolation(const DataVector& u, const DataVector& xi_target,\n                        const Matrix& m, const Monomial& f) {\n  const DataVector expected_u = f(xi_target);\n  const DataVector u_target = apply_matrix(m, u);\n  CHECK_ITERABLE_APPROX(u_target, expected_u);\n}\n\ntemplate <Spectral::Basis basis>\nvoid test_transforms(const DataVector& u, const Matrix& modal_to_nodal_matrix,\n                     const Matrix& nodal_to_modal_matrix, const Monomial& f) {\n  const DataVector expected_u_k = f.modes<basis>();\n  const DataVector u_k = apply_matrix(nodal_to_modal_matrix, u);\n  for (size_t i = 0; i < expected_u_k.size(); ++i) {\n    CHECK(u_k[i] == approx(expected_u_k[i]));\n  }\n  const DataVector transformed_u = apply_matrix(modal_to_nodal_matrix, u_k);\n  CHECK_ITERABLE_APPROX(u, transformed_u);\n}\n}  // namespace\n\ntemplate <Spectral::Basis basis, Spectral::Quadrature quadrature>\nvoid test_orthogonal_polynomial() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  const auto xi_target = make_with_random_values<DataVector>(\n      make_not_null(&generator), make_not_null(&xi_distribution), 5_st);\n  for (size_t n = Spectral::minimum_number_of_points<basis, quadrature>;\n       n <= Spectral::maximum_number_of_points<basis>; ++n) {\n    const DataVector& xi = Spectral::collocation_points<basis, quadrature>(n);\n    const Matrix& dm = Spectral::differentiation_matrix<basis, quadrature>(n);\n    const DataVector& integration_weights =\n        Spectral::quadrature_weights<basis, quadrature>(n);\n    const Matrix& interp_m =\n        Spectral::interpolation_matrix<basis, quadrature>(n, xi_target);\n    const Matrix& int_m = Spectral::integration_matrix<basis, quadrature>(n);\n    const Matrix& m_to_n =\n        Spectral::modal_to_nodal_matrix<basis, quadrature>(n);\n    const Matrix& n_to_m =\n        Spectral::nodal_to_modal_matrix<basis, quadrature>(n);\n    for (size_t pow_x = 0; pow_x < n; ++pow_x) {\n      const Monomial f{pow_x};\n      const DataVector u = f(xi);\n      test_definite_integral(u, integration_weights, f);\n      if (pow_x < n - 1) {\n        test_indefinite_integral(u, int_m, f, xi);\n      }\n      test_interpolation(u, xi_target, interp_m, f);\n      test_derivative(u, dm, f, xi);\n      test_transforms<basis>(u, m_to_n, n_to_m, f);\n    }\n  }\n}\n\ntemplate void test_orthogonal_polynomial<Spectral::Basis::Chebyshev,\n                                         Spectral::Quadrature::GaussLobatto>();\ntemplate void test_orthogonal_polynomial<Spectral::Basis::Chebyshev,\n                                         Spectral::Quadrature::Gauss>();\ntemplate void test_orthogonal_polynomial<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>();\ntemplate void test_orthogonal_polynomial<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::Gauss>();\n\nMonomial::Monomial(const size_t pow_x) : pow_x_(pow_x) {}\n\nDataVector Monomial::operator()(const DataVector& x) const {\n  return pow(x, static_cast<double>(pow_x_));\n}\n\ndouble Monomial::operator()(const double x) const {\n  return pow(x, static_cast<double>(pow_x_));\n}\n\nDataVector Monomial::df_dx(const DataVector& x) const {\n  if (pow_x_ == 0) {\n    return DataVector{x.size(), 0.0};\n  }\n  return static_cast<double>(pow_x_) * pow(x, static_cast<double>(pow_x_ - 1));\n}\n\nDataVector Monomial::int_f(const DataVector& x) const {\n  const auto n_plus_one = static_cast<double>(pow_x_ + 1);\n  const double constant =\n      pow_x_ % 2 == 0 ? 1.0 / n_plus_one : -1.0 / n_plus_one;\n  return pow(x, n_plus_one) / n_plus_one + constant;\n}\n\ndouble Monomial::definite_integral() const {\n  const auto n_plus_one = static_cast<double>(pow_x_ + 1);\n  return pow_x_ % 2 == 0 ? 2.0 / n_plus_one : 0.0;\n}\n\n// See, for example, Table of Integrals, Series, and Products 8.922\ntemplate <>\nDataVector Monomial::modes<Spectral::Basis::Legendre>() const {\n  DataVector result{pow_x_ + 1, 0.0};\n  const size_t n = pow_x_ / 2;\n  if (pow_x_ % 2 == 0) {\n    double coeff = 1.0 / (2.0 * static_cast<double>(n) + 1.0);\n    result[0] = coeff;\n    for (size_t k = 1; k <= pow_x_ / 2; ++k) {\n      coeff *=\n          2.0 * (static_cast<double>(n) - static_cast<double>(k) + 1.0) /\n          (2.0 * static_cast<double>(n) + 2.0 * static_cast<double>(k) + 1.0);\n      result[2 * k] = coeff * (4.0 * static_cast<double>(k) + 1.0);\n    }\n  } else {\n    double coeff = 1.0 / (2.0 * static_cast<double>(n) + 3.0);\n    result[1] = 3.0 * coeff;\n    for (size_t k = 1; k <= pow_x_ / 2; ++k) {\n      coeff *=\n          2.0 * (static_cast<double>(n) - static_cast<double>(k) + 1.0) /\n          (2.0 * static_cast<double>(n) + 2.0 * static_cast<double>(k) + 3.0);\n      result[2 * k + 1] = coeff * (4.0 * static_cast<double>(k) + 3.0);\n    }\n  }\n  return result;\n}\n\n// See, for example, Equation (2.14) of Chebyshev Polynomials by Mason and\n// Handscomb (2002)\ntemplate <>\nDataVector Monomial::modes<Spectral::Basis::Chebyshev>() const {\n  const size_t n = pow_x_;\n  if (n == 0) {\n    return DataVector{1.0};\n  }\n  DataVector result{n + 1, 0.0};\n  const double c = 2.0 / static_cast<double>(two_to_the(n));\n  for (size_t k = 0; k <= n / 2; ++k) {\n    result[n - 2 * k] = c * static_cast<double>(binomial(n, k));\n  }\n  if (n % 2 == 0) {\n    result[0] *= 0.5;\n  }\n  return result;\n}\n}  // namespace PolynomialTestFunctions\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/Spectral/PolynomialTestFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n/// \\cond\nclass DataVector;\nnamespace Spectral {\nenum class Basis : uint8_t;\nenum class Quadrature : uint8_t;\n}  // namespace Spectral\n/// \\endcond\n\nnamespace PolynomialTestFunctions {\n\ntemplate <Spectral::Basis basis, Spectral::Quadrature quadrature>\nvoid test_orthogonal_polynomial();\n\n/*!\n * \\brief The monomial \\f$x^n\\f$\n */\nclass Monomial {\n public:\n  explicit Monomial(size_t pow_x);\n  DataVector operator()(const DataVector& x) const;\n  double operator()(double x) const;\n  DataVector df_dx(const DataVector& x) const;\n  /// The indefinite integral, with constant of integration chosen so that\n  /// the integral is zero at \\f$x = -1\\f$\n  DataVector int_f(const DataVector& x) const;\n  /// The definite integral on the interval \\f$[-1,1]\\f$\n  double definite_integral() const;\n  /*!\n   * \\brief A modal vector of modes for the given basis\n   *\n   * \\details The modal coefficients are stored in a modal vector as\n   * \\f$\\{u_0, u_1, \\ldots, u_n\\}\\f$.\n   */\n  template <Spectral::Basis basis>\n  DataVector modes() const;\n\n private:\n  size_t pow_x_;\n};\n}  // namespace PolynomialTestFunctions\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SphericalHarmonics/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"SphericalHarmonicsHelpers\")\n\nset(LIBRARY_SOURCES\n  StrahlkorperTestHelpers.cpp\n  YlmTestFunctions.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Spectral\n  SphericalHarmonics\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SphericalHarmonics/StrahlkorperTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"StrahlkorperTestHelpers.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n\nnamespace ylm::TestHelpers {\nStrahlkorper<Frame::Inertial> create_strahlkorper_y11(\n    const double y11_amplitude, const double radius,\n    const std::array<double, 3>& center) {\n  static const size_t l_max = 4;\n  static const size_t m_max = 4;\n\n  Strahlkorper<Frame::Inertial> strahlkorper_sphere(l_max, m_max, radius,\n                                                    center);\n\n  auto coefs = strahlkorper_sphere.coefficients();\n  SpherepackIterator it(l_max, m_max);\n  // Conversion between SPHEREPACK b_lm and real valued harmonic coefficients:\n  // b_lm = (-1)^{m+1} sqrt(1/2pi) d_lm\n  coefs[it.set(1, -1)()] = y11_amplitude * sqrt(0.5 / M_PI);\n  return Strahlkorper<Frame::Inertial>(coefs, strahlkorper_sphere);\n}\n}  // namespace ylm::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SphericalHarmonics/StrahlkorperTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n/// \\endcond\n\nnamespace ylm::TestHelpers {\n// Create a strahlkorper with a Im(Y11) dependence, with\n// a given average radius and a given center.\nStrahlkorper<Frame::Inertial> create_strahlkorper_y11(\n    const double y11_amplitude, const double radius,\n    const std::array<double, 3>& center);\n}  // namespace ylm::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SphericalHarmonics/Test_ApplyTensorYlmFilter.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/CurvedScalarWave/ApplyTensorYlmFilter.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/ApplyTensorYlmFilter.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackCache.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlm.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmFilter.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ylm::TensorYlm {\n\ntemplate <typename VarsList>\nvoid test_apply_filter(const size_t num_to_kill) {\n  constexpr size_t radial_extents = 2;\n  constexpr size_t ell_max = 9;\n\n  const auto& ylm = ::ylm::get_spherepack_cache(ell_max);\n  const size_t spectral_mesh_size = ylm.spectral_size() * radial_extents;\n  const size_t physical_mesh_size = ylm.physical_size() * radial_extents;\n\n  // Fill modal variables with random numbers in each mode.\n  // Note that we fill only valid modes in the storage.\n  Variables<VarsList> inertial_modal_vars(spectral_mesh_size, 0.0);\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> dist{-1.0, 1.0};\n  ylm::SpherepackIterator it(ell_max, ell_max, radial_extents, true);\n  tmpl::for_each<VarsList>(\n      [&inertial_modal_vars, &it, &dist,\n       &generator]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n        auto& tensor = get<Tag>(inertial_modal_vars);\n        for (auto& component : tensor) {\n          for (size_t offset = 0; offset < radial_extents; ++offset) {\n            for (it.reset(); it; ++it) {\n              // Do not set modes with ell > ell_max - rank\n              // because those modes are incompletely represented by\n              // the TensorYlm basis.\n              if (it.l() <= ell_max - tensor.rank()) {\n                component[it() + offset] = dist(generator);\n              }\n            }\n          }\n        }\n      });\n\n  // Do modal to nodal.\n  Variables<VarsList> inertial_nodal_vars(physical_mesh_size);\n  filter_detail::modal_to_nodal_ylm(make_not_null(&inertial_nodal_vars),\n                                    inertial_modal_vars, ylm, radial_extents);\n\n  // Save a copy of the modal vars\n  const Variables<VarsList> test_inertial_modal_vars(inertial_modal_vars);\n\n  // Even if num_to_kill is zero, the filter does\n  // scalarylm->tensorylm->scalarylm without any additional cutting\n  // off of modes.  This isn't really doing nothing, because any modes\n  // in the scalarylm basis that are incompletely represented by the\n  // tensorylm basis will be modified, but it is the best we can do.\n  SimpleSparseMatrix filter_matrix_scalar;\n  SimpleSparseMatrix filter_matrix_i;\n  SimpleSparseMatrix filter_matrix_ii;\n  SimpleSparseMatrix filter_matrix_ij;\n  SimpleSparseMatrix filter_matrix_kii;\n  fill_filter<Scalar<DataVector>::structure>(\n      make_not_null(&filter_matrix_scalar), ell_max, num_to_kill, std::nullopt,\n      CoefficientNormalization::Spherepack);\n  fill_filter<tnsr::i<DataVector, 3>::structure>(\n      make_not_null(&filter_matrix_i), ell_max, num_to_kill, std::nullopt,\n      CoefficientNormalization::Spherepack);\n  if constexpr (std::is_same_v<\n                    VarsList, typename filter_detail::gh_spacetime_vars_list>) {\n    fill_filter<tnsr::ii<DataVector, 3>::structure>(\n        make_not_null(&filter_matrix_ii), ell_max, num_to_kill, std::nullopt,\n        CoefficientNormalization::Spherepack);\n    fill_filter<tnsr::ij<DataVector, 3>::structure>(\n        make_not_null(&filter_matrix_ij), ell_max, num_to_kill, std::nullopt,\n        CoefficientNormalization::Spherepack);\n    fill_filter<tnsr::ijj<DataVector, 3>::structure>(\n        make_not_null(&filter_matrix_kii), ell_max, num_to_kill, std::nullopt,\n        CoefficientNormalization::Spherepack);\n  }\n\n  // Make up bogus jacobians with random numbers, and make\n  // them diagonally dominant so that they are invertible.\n  std::uniform_real_distribution<double> positive_dist{0.5, 1.0};\n  InverseJacobian<DataVector, 3, Frame::Inertial, Frame::Grid>\n      jac_inertial_to_grid(physical_mesh_size);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      jac_inertial_to_grid.get(i, j) = 0.05 * dist(generator);\n    }\n    jac_inertial_to_grid.get(i, i) += positive_dist(generator);\n  }\n\n  // Invert the Jacobian\n  InverseJacobian<DataVector, 3, Frame::Grid, Frame::Inertial>\n      jac_grid_to_inertial(physical_mesh_size);\n  Scalar<DataVector> det(physical_mesh_size);\n  determinant_and_inverse(make_not_null(&det),\n                          make_not_null(&jac_grid_to_inertial),\n                          jac_inertial_to_grid);\n\n  // Now carry out the entire filtering algorithm,\n  // using inertial_modal_vars as temporary storage.\n  if constexpr (std::is_same_v<\n                    VarsList, typename filter_detail::gh_spacetime_vars_list>) {\n    apply_tensor_ylm_filter(make_not_null(&inertial_nodal_vars),\n                            make_not_null(&inertial_modal_vars),\n                            jac_inertial_to_grid, jac_grid_to_inertial,\n                            filter_matrix_scalar, filter_matrix_i,\n                            filter_matrix_ii, filter_matrix_ij,\n                            filter_matrix_kii, ell_max, radial_extents);\n  } else {\n    apply_tensor_ylm_filter(make_not_null(&inertial_nodal_vars),\n                            make_not_null(&inertial_modal_vars),\n                            jac_inertial_to_grid, jac_grid_to_inertial,\n                            filter_matrix_scalar, filter_matrix_i, ell_max,\n                            radial_extents);\n  }\n\n  // Do nodal to modal of the result (for comparison).\n  filter_detail::nodal_to_modal_ylm(make_not_null(&inertial_modal_vars),\n                                    inertial_nodal_vars, ylm, radial_extents);\n\n  // We should get back the original modal vars, to roundoff.\n  tmpl::for_each<VarsList>([&inertial_modal_vars, &test_inertial_modal_vars,\n                            &ell_max, &num_to_kill,\n                            &it]<class Tag>(const tmpl::type_<Tag> /*meta*/) {\n    constexpr size_t num_independent_components = Tag::type::structure::size();\n    const auto& tensor_a = get<Tag>(inertial_modal_vars);\n    const auto& tensor_b = get<Tag>(test_inertial_modal_vars);\n    for (size_t storage_index = 0; storage_index < num_independent_components;\n         ++storage_index) {\n      const auto& a = tensor_a[storage_index];\n      const auto& b = tensor_b[storage_index];\n      for (size_t offset = 0; offset < radial_extents; ++offset) {\n        for (it.reset(); it; ++it) {\n          // If num_to_kill is zero, then all the coefs\n          // should agree with the originals.\n          // If num_to_kill is nonzero, then:\n          //  - lcut is the largest mode that is LEFT ALONE\n          //    in the Spin-weighted basis.  So lcut=lmax-num_to_kill\n          //  - In the Cartesian basis, lcut+rank+1 is the smallest\n          //    mode that is zeroed.  This is lmax-num_to_kill+rank+1.\n          //  - In the Cartesian basis, lcut-rank is the largest mode\n          //    that is unaffected. This is lmax-num_to_kill-rank.\n          // Therefore all coefs\n          // with (ell <= ell_max - num_to_kill - rank) should agree\n          // with the originals because they have not been affected, and\n          // all the modes with (ell >= ell_max - num_to_kill + rank+1)\n          // should be zero because they have been killed by the filter.\n          // For modes between those cases, they are modified in some\n          // complicated way that we do not check here.\n          if (num_to_kill == 0 or\n              it.l() <= ell_max - num_to_kill - tensor_b.rank()) {\n            CAPTURE(ell_max);\n            CAPTURE(it.l());\n            CAPTURE(num_to_kill);\n            CAPTURE(tensor_b.rank());\n            CHECK(a[it() + offset] == approx(b[it() + offset]));\n          } else if (it.l() >= ell_max - num_to_kill + tensor_a.rank() + 1) {\n            // The two tensors have the same rank, so it doesn't matter\n            // whether we use tensor_a.rank() or tensor_b.rank() here.\n            CAPTURE(ell_max);\n            CAPTURE(it.l());\n            CAPTURE(num_to_kill);\n            CAPTURE(tensor_a.rank());\n            CHECK(0.0 == approx(a[it() + offset]));\n          }\n        }\n      }\n    }\n  });\n}\n}  // namespace ylm::TensorYlm\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace YlmTestFunctions {\n\nProductOfPolynomials::ProductOfPolynomials(const size_t pow_nx,\n                                           const size_t pow_ny,\n                                           const size_t pow_nz)\n    : pow_nx_{pow_nx}, pow_ny_{pow_ny}, pow_nz_{pow_nz} {}\n\nDataVector ProductOfPolynomials::operator()(const DataVector& theta,\n                                            const DataVector& phi) const {\n  return pow(sin(theta), pow_nx_ + pow_ny_) * pow(cos(theta), pow_nz_) *\n         pow(cos(phi), pow_nx_) * pow(sin(phi), pow_ny_);\n}\n\nDataVector ProductOfPolynomials::df_dth(const DataVector& theta,\n                                        const DataVector& phi) const {\n  if (pow_nx_ + pow_ny_ + pow_nz_ == 0) {\n    return DataVector{theta.size(), 0.0};\n  }\n  if (pow_nx_ + pow_ny_ == 0) {\n    return -static_cast<double>(pow_nz_) * sin(theta) *\n           pow(cos(theta), pow_nz_ - 1) * pow(cos(phi), pow_nx_) *\n           pow(sin(phi), pow_ny_);\n  }\n  if (pow_nz_ == 0) {\n    return static_cast<double>(pow_nx_ + pow_ny_) *\n           pow(sin(theta), pow_nx_ + pow_ny_ - 1) * cos(theta) *\n           pow(cos(phi), pow_nx_) * pow(sin(phi), pow_ny_);\n  }\n  return (static_cast<double>(pow_nx_ + pow_ny_) *\n              pow(sin(theta), pow_nx_ + pow_ny_ - 1) *\n              pow(cos(theta), pow_nz_ + 1) -\n          static_cast<double>(pow_nz_) *\n              pow(sin(theta), pow_nx_ + pow_ny_ + 1) *\n              pow(cos(theta), pow_nz_ - 1)) *\n         pow(cos(phi), pow_nx_) * pow(sin(phi), pow_ny_);\n}\n\nDataVector ProductOfPolynomials::df_dph(const DataVector& theta,\n                                        const DataVector& phi) const {\n  if (pow_nx_ + pow_ny_ == 0) {\n    return DataVector{theta.size(), 0.0};\n  }\n  if (pow_nx_ == 0) {\n    return static_cast<double>(pow_ny_) * pow(sin(theta), pow_nx_ + pow_ny_) *\n           pow(cos(theta), pow_nz_) * cos(phi) * pow(sin(phi), pow_ny_ - 1);\n  }\n  if (pow_ny_ == 0) {\n    return -static_cast<double>(pow_nx_) * pow(sin(theta), pow_nx_ + pow_ny_) *\n           pow(cos(theta), pow_nz_) * pow(cos(phi), pow_nx_ - 1) * sin(phi);\n  }\n  return pow(sin(theta), pow_nx_ + pow_ny_) * pow(cos(theta), pow_nz_) *\n         (static_cast<double>(pow_ny_) * pow(cos(phi), pow_nx_ + 1) *\n              pow(sin(phi), pow_ny_ - 1) -\n          static_cast<double>(pow_nx_) * *pow(cos(phi), pow_nx_ - 1) *\n              pow(sin(phi), pow_ny_ + 1));\n}\n\ndouble ProductOfPolynomials::definite_integral() const {\n  if ((pow_nx_ % 2 == 1) or (pow_ny_ % 2 == 1) or (pow_nz_ % 2 == 1)) {\n    return 0.0;\n  }\n  return 4.0 * M_PI *\n         static_cast<double>(falling_factorial(pow_nx_, pow_nx_ / 2)) *\n         static_cast<double>(falling_factorial(pow_ny_, pow_ny_ / 2)) *\n         static_cast<double>(falling_factorial(pow_nz_, pow_nz_ / 2)) /\n         static_cast<double>(\n             falling_factorial(pow_nx_ + pow_ny_ + pow_nz_ + 1,\n                               (pow_nx_ + pow_ny_ + pow_nz_) / 2 + 1));\n}\n\ntemplate <>\nDataVector Ylm<0, 0>::f() const {\n  return DataVector{n_pts_, 1.0 / sqrt(4.0 * M_PI)};\n}\n\ntemplate <>\nDataVector Ylm<0, 0>::df_dth() const {\n  return DataVector{n_pts_, 0.0};\n}\n\ntemplate <>\nDataVector Ylm<0, 0>::df_dph() const {\n  return DataVector{n_pts_, 0.0};\n}\n\ntemplate <>\nDataVector Ylm<1, 0>::f() const {\n  return DataVector{sqrt(0.75 / M_PI) * cos(theta_)};\n}\n\ntemplate <>\nDataVector Ylm<1, 0>::df_dth() const {\n  return DataVector{-sqrt(0.75 / M_PI) * sin(theta_)};\n}\n\ntemplate <>\nDataVector Ylm<1, 0>::df_dph() const {\n  return DataVector{n_pts_, 0.0};\n}\n\ntemplate <>\nDataVector Ylm<1, 1>::f() const {\n  return DataVector{sqrt(0.75 / M_PI) * sin(theta_) * cos(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<1, 1>::df_dth() const {\n  return DataVector{sqrt(0.75 / M_PI) * cos(theta_) * cos(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<1, 1>::df_dph() const {\n  return DataVector{-sqrt(0.75 / M_PI) * sin(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<1, -1>::f() const {\n  return DataVector{sqrt(0.75 / M_PI) * sin(theta_) * sin(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<1, -1>::df_dth() const {\n  return DataVector{sqrt(0.75 / M_PI) * cos(theta_) * sin(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<1, -1>::df_dph() const {\n  return DataVector{sqrt(0.75 / M_PI) * cos(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, 0>::f() const {\n  return DataVector{sqrt(1.25 / M_PI) * (1.5 * square(cos(theta_)) - 0.5)};\n}\n\ntemplate <>\nDataVector Ylm<2, 0>::df_dth() const {\n  return DataVector{-3.0 * sqrt(1.25 / M_PI) * sin(theta_) * cos(theta_)};\n}\n\ntemplate <>\nDataVector Ylm<2, 0>::df_dph() const {\n  return DataVector{n_pts_, 0.0};\n}\n\ntemplate <>\nDataVector Ylm<2, 1>::f() const {\n  return DataVector{sqrt(3.75 / M_PI) * sin(theta_) * cos(theta_) * cos(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, 1>::df_dth() const {\n  return DataVector{sqrt(3.75 / M_PI) *\n                    (square(cos(theta_)) - square(sin(theta_))) * cos(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, 1>::df_dph() const {\n  return DataVector{-sqrt(3.75 / M_PI) * cos(theta_) * sin(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, -1>::f() const {\n  return DataVector{sqrt(3.75 / M_PI) * sin(theta_) * cos(theta_) * sin(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, -1>::df_dth() const {\n  return DataVector{sqrt(3.75 / M_PI) *\n                    (square(cos(theta_)) - square(sin(theta_))) * sin(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, -1>::df_dph() const {\n  return DataVector{sqrt(3.75 / M_PI) * cos(theta_) * cos(phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, 2>::f() const {\n  return DataVector{0.25 * sqrt(15.0 / M_PI) * square(sin(theta_)) *\n                    cos(2.0 * phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, 2>::df_dth() const {\n  return DataVector{0.5 * sqrt(15.0 / M_PI) * sin(theta_) * cos(theta_) *\n                    cos(2.0 * phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, 2>::df_dph() const {\n  return DataVector{-0.5 * sqrt(15.0 / M_PI) * sin(theta_) * sin(2.0 * phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, -2>::f() const {\n  return DataVector{0.25 * sqrt(15.0 / M_PI) * square(sin(theta_)) *\n                    sin(2.0 * phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, -2>::df_dth() const {\n  return DataVector{0.5 * sqrt(15.0 / M_PI) * sin(theta_) * cos(theta_) *\n                    sin(2.0 * phi_)};\n}\n\ntemplate <>\nDataVector Ylm<2, -2>::df_dph() const {\n  return DataVector{0.5 * sqrt(15.0 / M_PI) * sin(theta_) * cos(2.0 * phi_)};\n}\n\nvoid Y00::func(const gsl::not_null<DataVector*> u, const size_t stride,\n               const size_t offset, const std::vector<double>& thetas,\n               const std::vector<double>& phis) const {\n  // Can't make inv_sqrt_4_pi constexpr because sqrt isn't constexpr.\n  static const double inv_sqrt_4_pi = 0.5 / sqrt(M_PI);\n  for (size_t j = 0, s = 0; j < phis.size(); ++j) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      (*u)[s * stride + offset] = inv_sqrt_4_pi;\n    }\n  }\n}\n\nvoid Y00::dfunc(const gsl::not_null<std::array<double*, 2>*> du,\n                const size_t stride, const size_t offset,\n                const std::vector<double>& thetas,\n                const std::vector<double>& phis) const {\n  for (size_t d = 0; d < du->size(); ++d) {\n    for (size_t j = 0, s = 0; j < phis.size(); ++j) {\n      for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n        gsl::at(*du, d)[s * stride + offset] = 0.0;\n      }\n    }\n  }\n}\n\nvoid Y00::ddfunc(const gsl::not_null<SecondDeriv*> ddu, const size_t stride,\n                 const size_t offset, const std::vector<double>& thetas,\n                 const std::vector<double>& phis) const {\n  for (size_t d = 0; d < 2; ++d) {\n    for (size_t e = 0; e < 2; ++e) {\n      for (size_t j = 0, s = 0; j < phis.size(); ++j) {\n        for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n          ddu->get(d, e)[s * stride + offset] = 0.0;\n        }\n      }\n    }\n  }\n}\n\nvoid Y00::scalar_laplacian(const gsl::not_null<DataVector*> slap,\n                           const size_t stride, const size_t offset,\n                           const std::vector<double>& thetas,\n                           const std::vector<double>& phis) const {\n  size_t s = 0;\n  for (size_t j = 0; j < phis.size(); ++j) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      (*slap)[s * stride + offset] = 0.0;\n    }\n  }\n}\n\nvoid Y10::func(const gsl::not_null<DataVector*> u, const size_t stride,\n               const size_t offset, const std::vector<double>& thetas,\n               const std::vector<double>& phis) const {\n  const double amplitude = sqrt(3.0 / 4.0 / M_PI);\n  for (size_t j = 0, s = 0; j < phis.size(); ++j) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      (*u)[s * stride + offset] = cos(thetas[i]) * amplitude;\n    }\n  }\n}\nvoid Y10::dfunc(const gsl::not_null<std::array<double*, 2>*> du,\n                const size_t stride, const size_t offset,\n                const std::vector<double>& thetas,\n                const std::vector<double>& phis) const {\n  const double amplitude = sqrt(3.0 / 4.0 / M_PI);\n  for (size_t j = 0, s = 0; j < phis.size(); ++j) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      gsl::at(*du, 0)[s * stride + offset] =\n          -sin(thetas[i]) * amplitude;             // d/dth\n      gsl::at(*du, 1)[s * stride + offset] = 0.0;  // sin^-1 theta d/dph\n    }\n  }\n}\n\nvoid Y10::ddfunc(const gsl::not_null<SecondDeriv*> ddu, const size_t stride,\n                 const size_t offset, const std::vector<double>& thetas,\n                 const std::vector<double>& phis) const {\n  const double amplitude = sqrt(3.0 / 4.0 / M_PI);\n  for (size_t j = 0, s = 0; j < phis.size(); ++j) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      ddu->get(0, 0)[s * stride + offset] = -cos(thetas[i]) * amplitude;\n      ddu->get(1, 1)[s * stride + offset] = 0.0;\n      ddu->get(1, 0)[s * stride + offset] = 0.0;\n      ddu->get(0, 1)[s * stride + offset] = 0.0;\n    }\n  }\n}\n\nvoid Y10::scalar_laplacian(const gsl::not_null<DataVector*> slap,\n                           const size_t stride, const size_t offset,\n                           const std::vector<double>& thetas,\n                           const std::vector<double>& phis) const {\n  const double amplitude = sqrt(3.0 / 4.0 / M_PI);\n  for (size_t j = 0, s = 0; j < phis.size(); ++j) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      (*slap)[s * stride + offset] = -2.0 * cos(thetas[i]) * amplitude;\n    }\n  }\n}\n\nvoid Y11::func(const gsl::not_null<DataVector*> u, const size_t stride,\n               const size_t offset, const std::vector<double>& thetas,\n               const std::vector<double>& phis) const {\n  const double amplitude = -sqrt(3.0 / 8.0 / M_PI);\n  size_t s = 0;\n  for (const auto& phi : phis) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      (*u)[s * stride + offset] = sin(thetas[i]) * sin(phi) * amplitude;\n    }\n  }\n}\nvoid Y11::dfunc(const gsl::not_null<std::array<double*, 2>*> du,\n                const size_t stride, const size_t offset,\n                const std::vector<double>& thetas,\n                const std::vector<double>& phis) const {\n  const double amplitude = -sqrt(3.0 / 8.0 / M_PI);\n  size_t s = 0;\n  for (const auto& phi : phis) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      // d/dth\n      gsl::at(*du, 0)[s * stride + offset] =\n          cos(thetas[i]) * sin(phi) * amplitude;\n      // sin^-1(theta) d/dph\n      gsl::at(*du, 1)[s * stride + offset] = cos(phi) * amplitude;\n    }\n  }\n}\n\nvoid Y11::ddfunc(const gsl::not_null<SecondDeriv*> ddu, const size_t stride,\n                 const size_t offset, const std::vector<double>& thetas,\n                 const std::vector<double>& phis) const {\n  const double amplitude = -sqrt(3.0 / 8.0 / M_PI);\n  size_t s = 0;\n  for (const auto& phi : phis) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      ddu->get(0, 0)[s * stride + offset] =\n          -sin(thetas[i]) * sin(phi) * amplitude;\n      ddu->get(1, 1)[s * stride + offset] =\n          -sin(phi) / sin(thetas[i]) * amplitude;\n      ddu->get(1, 0)[s * stride + offset] =\n          cos(thetas[i]) * cos(phi) * amplitude / sin(thetas[i]);\n      ddu->get(0, 1)[s * stride + offset] =\n          ddu->get(1, 0)[s * stride + offset] -\n          cos(thetas[i]) * cos(phi) * amplitude / sin(thetas[i]);\n    }\n  }\n}\n\nvoid Y11::scalar_laplacian(const gsl::not_null<DataVector*> slap,\n                           const size_t stride, const size_t offset,\n                           const std::vector<double>& thetas,\n                           const std::vector<double>& phis) const {\n  const double amplitude = -sqrt(3.0 / 8.0 / M_PI);\n  size_t s = 0;\n  for (const auto& phi : phis) {\n    for (size_t i = 0; i < thetas.size(); ++i, ++s) {\n      (*slap)[s * stride + offset] =\n          amplitude * (sin(phi) *\n                           (cos(thetas[i]) * cos(thetas[i]) -\n                            sin(thetas[i]) * sin(thetas[i])) /\n                           sin(thetas[i]) -\n                       sin(phi) / sin(thetas[i]));\n    }\n  }\n}\n\nDataVector FuncA::func(const std::vector<double>& thetas,\n                       const std::vector<double>& phis) const {\n  DataVector u(thetas.size() * phis.size());\n  size_t s = 0;\n  for (const auto& phi : phis) {\n    for (const auto& theta : thetas) {\n      const double sin_theta = sin(theta);\n      const double cos_theta = cos(theta);\n      u[s] = sqrt(969969.0 / M_PI) * pow<10>(sin_theta) * cos(10.0 * phi) /\n                 1024.0 -\n             sqrt(85085.0 / M_PI) * (3.0 / 512.0) * pow<7>(sin_theta) *\n                 (19.0 * cube(cos_theta) - 3.0 * cos_theta) * sin(7.0 * phi) +\n             sqrt(1365.0 / M_PI) / 64.0 * square(sin_theta) * cos(2.0 * phi) *\n                 (33.0 * pow<4>(cos_theta) - 18.0 * square(cos_theta) + 1.0);\n      ++s;\n    }\n  }\n  return u;\n}\n\nDataVector FuncB::func(const std::vector<double>& thetas,\n                       const std::vector<double>& phis) const {\n  DataVector u(thetas.size() * phis.size());\n  size_t s = 0;\n  for (const auto& phi : phis) {\n    for (const auto& theta : thetas) {\n      const double sin_theta = sin(theta);\n      const double cos_theta = cos(theta);\n      u[s] = -sqrt(85085.0 / M_PI) * (3.0 / 512.0) * pow<7>(sin_theta) *\n                 (19.0 * cube(cos_theta) - 3.0 * cos_theta) * sin(7.0 * phi) +\n             sqrt(1365.0 / M_PI) / 64.0 * square(sin_theta) * cos(2.0 * phi) *\n                 (33.0 * pow<4>(cos_theta) - 18.0 * square(cos_theta) + 1.0);\n      ++s;\n    }\n  }\n  return u;\n}\n\nDataVector FuncC::func(const std::vector<double>& thetas,\n                       const std::vector<double>& phis) const {\n  DataVector u(thetas.size() * phis.size());\n  size_t s = 0;\n  for (const auto& phi : phis) {\n    for (const auto& theta : thetas) {\n      const double sin_theta = sin(theta);\n      const double cos_theta = cos(theta);\n      u[s] = sqrt(1365.0 / M_PI) / 64.0 * square(sin_theta) * cos(2.0 * phi) *\n             (33.0 * pow<4>(cos_theta) - 18.0 * square(cos_theta) + 1.0);\n      ++s;\n    }\n  }\n  return u;\n}\n\n}  // namespace YlmTestFunctions\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace YlmTestFunctions {\n\n/*!\n * \\brief Product of polynomials regular on the surface of a sphere\n *\n * \\details Computes \\f$ n_x^{k_x} n_y^{k_y} n_z^{k_z} \\f$ where \\f$n_x = \\sin\n * \\theta \\cos \\phi\\f$, \\f$n_y = \\sin \\theta \\sin \\phi\\f$, and \\f$n_z = \\cos\n * \\theta\\f$.  The function and its first derivatives are exactly representable\n * by spherical harmonics of order \\f$(L,M)\\f$ if \\f$L > k_x + k_y + k_z\\f$ and\n * \\f$M > k_x + k_y\\f$.\n *\n */\nclass ProductOfPolynomials {\n public:\n  ProductOfPolynomials(size_t pow_nx, size_t pow_ny, size_t pow_nz);\n  DataVector operator()(const DataVector& theta, const DataVector& phi) const;\n  template <typename Fr>\n  DataVector operator()(const tnsr::I<DataVector, 2, Fr>& theta_and_phi) const {\n    return operator()(get<0>(theta_and_phi), get<1>(theta_and_phi));\n  }\n  DataVector df_dth(const DataVector& theta, const DataVector& phi) const;\n  template <typename Fr>\n  DataVector df_dth(const tnsr::I<DataVector, 2, Fr>& theta_and_phi) const {\n    return df_dth(get<0>(theta_and_phi), get<1>(theta_and_phi));\n  }\n  // This is the Pfaffiaan derivative (extra factor 1/sin(th))\n  DataVector df_dph(const DataVector& theta, const DataVector& phi) const;\n  template <typename Fr>\n  DataVector df_dph(const tnsr::I<DataVector, 2, Fr>& theta_and_phi) const {\n    return df_dph(get<0>(theta_and_phi), get<1>(theta_and_phi));\n  }\n  double definite_integral() const;\n\n private:\n  size_t pow_nx_;\n  size_t pow_ny_;\n  size_t pow_nz_;\n};\n\ntemplate <size_t l, int m>\nclass Ylm {\n public:\n  Ylm(size_t n_r, size_t L, size_t M);\n  DataVector f() const;\n  DataVector df_dth() const;\n  // This is the Pfaffiaan derivative (returns 1/sin(theta) df_dph)\n  DataVector df_dph() const;\n  DataVector modes() const;\n\n private:\n  size_t n_r_;\n  size_t size_of_modes_;\n  double c_lm_;\n  size_t n_pts_;\n  DataVector theta_;\n  DataVector phi_;\n  size_t offset_c_lm_;\n};\n\ntemplate <size_t l, int m>\nYlm<l, m>::Ylm(const size_t n_r, size_t L, size_t M)\n    : n_r_{n_r},\n      size_of_modes_{2 * n_r * (L + 1) * (M + 1)},\n      c_lm_{m == 0 ? sqrt(2.0 / M_PI)\n                   : (m < 0 ? -sqrt(1.0 / M_PI) : sqrt(1.0 / M_PI))} {\n  ASSERT(L > l, \"Cannot represent derivatives on mesh\");\n  ASSERT(static_cast<int>(M) >= std::abs(m),\n         \"Cannot represent derivatives on mesh\");\n  const Mesh<3> mesh{\n      {n_r, L + 1, 2 * M + 1},\n      {Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n       Spectral::Basis::SphericalHarmonic},\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss,\n       Spectral::Quadrature::Equiangular}};\n  const auto xi = logical_coordinates(mesh);\n  n_pts_ = mesh.number_of_grid_points();\n  theta_ = xi.get(1);\n  phi_ = xi.get(2);\n  ylm::SpherepackIterator it(L, M, n_r);\n  it.set(l, m);\n  offset_c_lm_ = it();\n}\n\ntemplate <size_t l, int m>\nDataVector Ylm<l, m>::modes() const {\n  DataVector result{size_of_modes_, 0.0};\n  for (size_t k = 0; k < n_r_; ++k) {\n    result[offset_c_lm_ + k] = c_lm_;\n  }\n  return result;\n}\n\nusing SecondDeriv = ylm::Spherepack::SecondDeriv;\n\nclass ScalarFunctionWithDerivs {\n public:\n  virtual ~ScalarFunctionWithDerivs() = default;\n  ScalarFunctionWithDerivs() = default;\n  ScalarFunctionWithDerivs(const ScalarFunctionWithDerivs&) = default;\n  ScalarFunctionWithDerivs(ScalarFunctionWithDerivs&&) = default;\n  ScalarFunctionWithDerivs& operator=(const ScalarFunctionWithDerivs&) =\n      default;\n  ScalarFunctionWithDerivs& operator=(ScalarFunctionWithDerivs&&) = default;\n  virtual void func(gsl::not_null<DataVector*> u, size_t stride, size_t offset,\n                    const std::vector<double>& thetas,\n                    const std::vector<double>& phis) const = 0;\n  virtual void dfunc(gsl::not_null<std::array<double*, 2>*> du, size_t stride,\n                     size_t offset, const std::vector<double>& thetas,\n                     const std::vector<double>& phis) const = 0;\n  virtual void ddfunc(gsl::not_null<SecondDeriv*> ddu, size_t stride,\n                      size_t offset, const std::vector<double>& thetas,\n                      const std::vector<double>& phis) const = 0;\n  virtual void scalar_laplacian(gsl::not_null<DataVector*> slap, size_t stride,\n                                size_t offset,\n                                const std::vector<double>& thetas,\n                                const std::vector<double>& phis) const = 0;\n  virtual double integral() const = 0;\n};\n\nclass Y00 : public ScalarFunctionWithDerivs {\n public:\n  ~Y00() override = default;\n  Y00() = default;\n  Y00(const Y00&) = default;\n  Y00(Y00&&) = default;\n  Y00& operator=(const Y00&) = default;\n  Y00& operator=(Y00&&) = default;\n  void func(gsl::not_null<DataVector*> u, size_t stride, size_t offset,\n            const std::vector<double>& thetas,\n            const std::vector<double>& phis) const override;\n  void dfunc(gsl::not_null<std::array<double*, 2>*> du, size_t stride,\n             size_t offset, const std::vector<double>& thetas,\n             const std::vector<double>& phis) const override;\n  void ddfunc(gsl::not_null<SecondDeriv*> ddu, size_t stride, size_t offset,\n              const std::vector<double>& thetas,\n              const std::vector<double>& phis) const override;\n  void scalar_laplacian(gsl::not_null<DataVector*> slap, size_t stride,\n                        size_t offset, const std::vector<double>& thetas,\n                        const std::vector<double>& phis) const override;\n  double integral() const override { return sqrt(4.0 * M_PI); }\n};\n\nclass Y10 : public ScalarFunctionWithDerivs {\n public:\n  ~Y10() override = default;\n  Y10() = default;\n  Y10(const Y10&) = default;\n  Y10(Y10&&) = default;\n  Y10& operator=(const Y10&) = default;\n  Y10& operator=(Y10&&) = default;\n  void func(gsl::not_null<DataVector*> u, size_t stride, size_t offset,\n            const std::vector<double>& thetas,\n            const std::vector<double>& phis) const override;\n  void dfunc(gsl::not_null<std::array<double*, 2>*> du, size_t stride,\n             size_t offset, const std::vector<double>& thetas,\n             const std::vector<double>& phis) const override;\n  void ddfunc(gsl::not_null<SecondDeriv*> ddu, size_t stride, size_t offset,\n              const std::vector<double>& thetas,\n              const std::vector<double>& phis) const override;\n  void scalar_laplacian(gsl::not_null<DataVector*> slap, size_t stride,\n                        size_t offset, const std::vector<double>& thetas,\n                        const std::vector<double>& phis) const override;\n  double integral() const override { return 0.0; }\n};\n\n// Im(Y11(theta,phi))\nclass Y11 : public ScalarFunctionWithDerivs {\n public:\n  ~Y11() override = default;\n  Y11() = default;\n  Y11(const Y11&) = default;\n  Y11(Y11&&) = default;\n  Y11& operator=(const Y11&) = default;\n  Y11& operator=(Y11&&) = default;\n  void func(gsl::not_null<DataVector*> u, size_t stride, size_t offset,\n            const std::vector<double>& thetas,\n            const std::vector<double>& phis) const override;\n  void dfunc(gsl::not_null<std::array<double*, 2>*> du, size_t stride,\n             size_t offset, const std::vector<double>& thetas,\n             const std::vector<double>& phis) const override;\n  void ddfunc(gsl::not_null<SecondDeriv*> ddu, size_t stride, size_t offset,\n              const std::vector<double>& thetas,\n              const std::vector<double>& phis) const override;\n  void scalar_laplacian(gsl::not_null<DataVector*> slap, size_t stride,\n                        size_t offset, const std::vector<double>& thetas,\n                        const std::vector<double>& phis) const override;\n  double integral() const override { return 0.0; }\n};\n\nclass SimpleScalarFunction {\n public:\n  virtual ~SimpleScalarFunction() = default;\n  SimpleScalarFunction() = default;\n  SimpleScalarFunction(const SimpleScalarFunction&) = default;\n  SimpleScalarFunction(SimpleScalarFunction&&) = default;\n  SimpleScalarFunction& operator=(const SimpleScalarFunction&) = default;\n  SimpleScalarFunction& operator=(SimpleScalarFunction&&) = default;\n  virtual DataVector func(const std::vector<double>& thetas,\n                          const std::vector<double>& phis) const = 0;\n};\n\n// Re Y(10,10) + Im Y(10,7) + Re Y(6,2)\nclass FuncA : public SimpleScalarFunction {\n public:\n  ~FuncA() override = default;\n  FuncA() = default;\n  FuncA(const FuncA&) = default;\n  FuncA(FuncA&&) = default;\n  FuncA& operator=(const FuncA&) = default;\n  FuncA& operator=(FuncA&&) = default;\n  DataVector func(const std::vector<double>& thetas,\n                  const std::vector<double>& phis) const override;\n};\n\n// Im Y(10,7)+ Re Y(6,2)\nclass FuncB : public SimpleScalarFunction {\n public:\n  ~FuncB() override = default;\n  FuncB() = default;\n  FuncB(const FuncB&) = default;\n  FuncB(FuncB&&) = default;\n  FuncB& operator=(const FuncB&) = default;\n  FuncB& operator=(FuncB&&) = default;\n  DataVector func(const std::vector<double>& thetas,\n                  const std::vector<double>& phis) const override;\n};\n\n// Re Y(6,2)\nclass FuncC : public SimpleScalarFunction {\n public:\n  ~FuncC() override = default;\n  FuncC() = default;\n  FuncC(const FuncC&) = default;\n  FuncC(FuncC&&) = default;\n  FuncC& operator=(const FuncC&) = default;\n  FuncC& operator=(FuncC&&) = default;\n  DataVector func(const std::vector<double>& thetas,\n                  const std::vector<double>& phis) const override;\n};\n\n}  // namespace YlmTestFunctions\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"SpinWeightedSphericalHarmonicsHelpers\")\n\nset(LIBRARY_SOURCES\n  SwshTestHelpers.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  DataStructures\n  Libsharp\n  Spectral\n  SpinWeightedSphericalHarmonics\n  Utilities\n\n  PRIVATE\n  Boost::boost\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n\n#include <boost/math/special_functions/binomial.hpp>\n#include <cmath>\n#include <complex>\n\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n\nnamespace Spectral {\nnamespace Swsh {\nnamespace TestHelpers {\n\ndouble factorial(const size_t arg) {\n  if (arg <= 1) {\n    return 1.0;\n  }\n  double factorial_result = 1.0;\n  for (size_t product_term = 1; product_term <= arg; ++product_term) {\n    factorial_result *= static_cast<double>(product_term);\n  }\n  return factorial_result;\n}\n\nstd::complex<double> spin_weighted_spherical_harmonic(const int s, const int l,\n                                                      const int m,\n                                                      const double theta,\n                                                      const double phi) {\n  if (l + s < 0 or l - s < 0) {\n    return 0.0;\n  }\n  std::complex<double> swshval = 0.0;\n  for (int r = 0; r <= l - s; ++r) {\n    if (r + s - m >= 0 and l - r + m >= 0) {\n      swshval +=\n          boost::math::binomial_coefficient<double>(\n              static_cast<unsigned>(l - s), static_cast<unsigned>(r)) *\n          boost::math::binomial_coefficient<double>(\n              static_cast<unsigned>(l + s), static_cast<unsigned>(r + s - m)) *\n          ((l - r - s) % 2 == 0 ? 1.0 : -1.0) *\n          pow(cos(theta / 2.0) / sin(theta / 2.0), 2.0 * r + s - m);\n    }\n  }\n  swshval *= (m % 2 == 0 ? 1.0 : -1.0) *\n             sqrt(factorial(static_cast<size_t>(l) + static_cast<size_t>(m)) *\n                  factorial(static_cast<size_t>(l - m)) * (2.0 * l + 1) /\n                  (4.0 * M_PI *\n                   factorial(static_cast<size_t>(l) + static_cast<size_t>(s)) *\n                   factorial(static_cast<size_t>(l - s)))) *\n             (std::complex<double>(cos(m * phi), sin(m * phi))) *\n             pow(sin(theta / 2.0), 2.0 * l);\n  return swshval;\n}\n\ntemplate <>\nstd::complex<double> derivative_of_spin_weighted_spherical_harmonic<Tags::Eth>(\n    const int s, const int l, const int m, const double theta,\n    const double phi) {\n  return sqrt(static_cast<std::complex<double>>((l - s) * (l + s + 1))) *\n         spin_weighted_spherical_harmonic(s + 1, l, m, theta, phi);\n}\n\ntemplate <>\nstd::complex<double>\nderivative_of_spin_weighted_spherical_harmonic<Tags::Ethbar>(const int s,\n                                                             const int l,\n                                                             const int m,\n                                                             const double theta,\n                                                             const double phi) {\n  return -sqrt(static_cast<std::complex<double>>((l + s) * (l - s + 1))) *\n         spin_weighted_spherical_harmonic(s - 1, l, m, theta, phi);\n}\n\ntemplate <>\nstd::complex<double>\nderivative_of_spin_weighted_spherical_harmonic<Tags::EthEth>(const int s,\n                                                             const int l,\n                                                             const int m,\n                                                             const double theta,\n                                                             const double phi) {\n  return sqrt(static_cast<std::complex<double>>((l - s) * (l + s + 1))) *\n         derivative_of_spin_weighted_spherical_harmonic<Tags::Eth>(s + 1, l, m,\n                                                                   theta, phi);\n}\n\ntemplate <>\nstd::complex<double>\nderivative_of_spin_weighted_spherical_harmonic<Tags::EthbarEth>(\n    const int s, const int l, const int m, const double theta,\n    const double phi) {\n  return sqrt(static_cast<std::complex<double>>((l - s) * (l + s + 1))) *\n         derivative_of_spin_weighted_spherical_harmonic<Tags::Ethbar>(\n             s + 1, l, m, theta, phi);\n}\n\ntemplate <>\nstd::complex<double>\nderivative_of_spin_weighted_spherical_harmonic<Tags::EthEthbar>(\n    const int s, const int l, const int m, const double theta,\n    const double phi) {\n  return -sqrt(static_cast<std::complex<double>>((l + s) * (l - s + 1))) *\n         derivative_of_spin_weighted_spherical_harmonic<Tags::Eth>(s - 1, l, m,\n                                                                   theta, phi);\n}\n\ntemplate <>\nstd::complex<double>\nderivative_of_spin_weighted_spherical_harmonic<Tags::EthbarEthbar>(\n    const int s, const int l, const int m, const double theta,\n    const double phi) {\n  return -sqrt(static_cast<std::complex<double>>((l + s) * (l - s + 1))) *\n         derivative_of_spin_weighted_spherical_harmonic<Tags::Ethbar>(\n             s - 1, l, m, theta, phi);\n}\n\ntemplate <>\nstd::complex<double>\nderivative_of_spin_weighted_spherical_harmonic<Tags::InverseEth>(\n    const int s, const int l, const int m, const double theta,\n    const double phi) {\n  return (l - s + 1) * (l + s) == 0\n             ? 0.0\n             : spin_weighted_spherical_harmonic(s - 1, l, m, theta, phi) /\n                   sqrt(static_cast<std::complex<double>>((l - s + 1) *\n                                                          (l + s)));\n}\n\ntemplate <>\nstd::complex<double>\nderivative_of_spin_weighted_spherical_harmonic<Tags::InverseEthbar>(\n    const int s, const int l, const int m, const double theta,\n    const double phi) {\n  return (l + s + 1) * (l - s) == 0\n             ? 0.0\n             : spin_weighted_spherical_harmonic(s + 1, l, m, theta, phi) /\n                   -sqrt(static_cast<std::complex<double>>((l + s + 1) *\n                                                           (l - s)));\n  ;\n}\n\n}  // namespace TestHelpers\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <sharp_cxx.h>\n\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\n/// \\cond\nclass ComplexDataVector;\n/// \\endcond\n\nnamespace Spectral {\nnamespace Swsh {\nnamespace TestHelpers {\n\n// returns the factorial of the argument as a double so that an approximate\n// value can be given for larger input quantities. Note that the spin-weighted\n// harmonic function requires a factorial of l + m, so harmonics above l~12\n// would be untestable if the factorial only returned size_t's.\ndouble factorial(size_t arg);\n\n// Note that the methods for computing the spin-weighted spherical harmonics and\n// their derivatives below are both 1) poorly optimized (they use many\n// computations per grid point evaluated) and 2) not terribly accurate (the\n// analytic expressions require evaluation of ratios of factorials, losing\n// numerical precision rapidly). However, they are comparatively easy to\n// manually check for correctness, which is critical to offer a reliable measure\n// of the spin-weighted transforms.\n\n// Analytic form for the spin-weighted spherical harmonic function, for testing\n// purposes. The formula is from [Goldberg\n// et. al.](https://aip.scitation.org/doi/10.1063/1.1705135)\nstd::complex<double> spin_weighted_spherical_harmonic(int s, int l, int m,\n                                                      double theta, double phi);\n\n// Returns the value of the spin-weighted derivative `DerivativeKind` of the\n// spherical harmonic basis function \\f${}_s Y_{l m}\\f$ at angular location\n// (`theta`, `phi`) using the recurrence identities for spin-weighted\n// derivatives.\ntemplate <typename DerivativeKind>\nstd::complex<double> derivative_of_spin_weighted_spherical_harmonic(\n    int s, int l, int m, double theta, double phi);\n\n// Performs the generation of spin-weighted coefficients into a supplied\n// ComplexModalVector `to_fill`, passed by pointer. The resulting random\n// coefficients must then be adjusted in two ways:\n// - The modes l < |s| are always zero, so must have zero coefficient to be\n//   compatible with tests\n// - Modes with m=0 obey reality conditions, so must also be adjusted\n// The libsharp coefficient representation is not currently presented as an\n// interface in SpECTRE. However, to help maintain the Swsh library, further\n// notes on the coefficient representation can be found in the comments for the\n// `Coefficients` class in `SwshCoefficients.hpp`.\ntemplate <int Spin, typename Distribution, typename Generator>\nvoid generate_swsh_modes(const gsl::not_null<ComplexModalVector*> to_fill,\n                         const gsl::not_null<Generator*> generator,\n                         const gsl::not_null<Distribution*> distribution,\n                         const size_t number_of_radial_points,\n                         const size_t l_max) {\n  fill_with_random_values(to_fill, generator, distribution);\n\n  auto* spherical_harmonic_lm =\n      cached_coefficients_metadata(l_max).get_sharp_alm_info();\n  for (size_t i = 0; i < number_of_radial_points; i++) {\n    // adjust the m = 0 modes for the reality conditions in libsharp\n    for (size_t l = 0; l < l_max + 1; l++) {\n      (*to_fill)[l + i * size_of_libsharp_coefficient_vector(l_max)] = real(\n          (*to_fill)[l + i * size_of_libsharp_coefficient_vector(l_max) / 2]);\n      (*to_fill)[l +\n                 (2 * i + 1) * size_of_libsharp_coefficient_vector(l_max) / 2] =\n          imag((*to_fill)[l + (2 * i + 1) *\n                                  size_of_libsharp_coefficient_vector(l_max) /\n                                  2]);\n    }\n    for (size_t m = 0; m < static_cast<size_t>(spherical_harmonic_lm->nm);\n         ++m) {\n      for (size_t l = m; l <= l_max; ++l) {\n        // clang-tidy do not use pointer arithmetic\n        // pointer arithmetic here is unavoidable as we are interacting with\n        // the libsharp structures\n        const auto m_start =\n            static_cast<size_t>(spherical_harmonic_lm->mvstart[m]);  // NOLINT\n        const auto l_stride =\n            static_cast<size_t>(spherical_harmonic_lm->stride);\n        // modes for l < |s| are zero\n        if (static_cast<int>(l) < abs(Spin)) {\n          (*to_fill)[m_start + l * l_stride +\n                     i * size_of_libsharp_coefficient_vector(l_max)] = 0.0;\n          (*to_fill)[m_start + l * l_stride +\n                     (2 * i + 1) * size_of_libsharp_coefficient_vector(l_max) /\n                         2] = 0.0;\n        }\n      }\n    }\n  }\n}\n\n// Computes a set of collocation points from a set of spin-weighted spherical\n// harmonic coefficients. This function takes care of the nastiness associated\n// with appropriately iterating over the sharp representation of coefficients\n// and storing the result computed from the input `basis_function` evaluated\n// with arguments (size_t s, size_t l, size_t m, double theta, double phi),\n// which is compatible with the above `spin_weighted_spherical_harmonic`\n// and `derivative_of_spin_weighted_spherical_harmonic` functions.\n// This function does not have easy test verification, but acts as an\n// independent computation of the spherical harmonic collocation values, so the\n// agreement (from `Test_SwshTransformJob.cpp`) with the libsharp transform\n// lends credibility to both methods.\ntemplate <int Spin, ComplexRepresentation Representation,\n          typename BasisFunction>\nvoid swsh_collocation_from_coefficients_and_basis_func(\n    const gsl::not_null<ComplexDataVector*> collocation_data,\n    const ComplexModalVector& coefficient_data, const size_t l_max,\n    const size_t number_of_radial_points, const BasisFunction basis_function) {\n  auto& spherical_harmonic_collocation =\n      cached_collocation_metadata<Representation>(l_max);\n  auto* spherical_harmonic_lm =\n      cached_coefficients_metadata(l_max).get_sharp_alm_info();\n\n  *collocation_data = 0.0;\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    for (auto j : spherical_harmonic_collocation) {\n      for (size_t m = 0; m < static_cast<size_t>(spherical_harmonic_lm->nm);\n           ++m) {\n        for (size_t l = m; l <= l_max; ++l) {\n          // clang-tidy do not use pointer arithmetic\n          // pointer arithmetic here is unavoidable as we are interacting with\n          // the libsharp structures\n          const auto m_start =\n              static_cast<size_t>(spherical_harmonic_lm->mvstart[m]);  // NOLINT\n          const auto l_stride =\n              static_cast<size_t>(spherical_harmonic_lm->stride);\n          // see the documentation associated with Swsh::TransformJob in\n          // SwshTransformJob.hpp for a detailed explanation of the libsharp\n          // collocation representation, and why there must be four steps with\n          // sign transformations.\n          (*collocation_data)[j.offset +\n                              i * spherical_harmonic_collocation.size()] +=\n              sharp_swsh_sign(Spin, m, true) *\n              coefficient_data[m_start + l * l_stride +\n                               i * size_of_libsharp_coefficient_vector(l_max)] *\n              basis_function(Spin, l, m, j.theta, j.phi);\n\n          if (m != 0) {\n            (*collocation_data)[j.offset +\n                                i * spherical_harmonic_collocation.size()] +=\n                sharp_swsh_sign(Spin, -m, true) *\n                conj(coefficient_data[m_start + l * l_stride +\n                                      i * size_of_libsharp_coefficient_vector(\n                                              l_max)]) *\n                basis_function(Spin, l, -m, j.theta, j.phi);\n          }\n          (*collocation_data)[j.offset +\n                              i * spherical_harmonic_collocation.size()] +=\n              sharp_swsh_sign(Spin, m, false) * std::complex<double>(0.0, 1.0) *\n              coefficient_data[m_start + l * l_stride +\n                               (2 * i + 1) *\n                                   size_of_libsharp_coefficient_vector(l_max) /\n                                   2] *\n              basis_function(Spin, l, m, j.theta, j.phi);\n          if (m != 0) {\n            (*collocation_data)[j.offset +\n                                i * spherical_harmonic_collocation.size()] +=\n                sharp_swsh_sign(Spin, -m, false) *\n                std::complex<double>(0.0, 1.0) *\n                conj(coefficient_data[m_start + l * l_stride +\n                                      (2 * i + 1) *\n                                          size_of_libsharp_coefficient_vector(\n                                              l_max) /\n                                          2]) *\n                basis_function(Spin, l, -m, j.theta, j.phi);\n          }\n        }\n      }\n    }\n  }\n}\n}  // namespace TestHelpers\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/Helpers/Parallel/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"ParallelHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  RoundRobinArrayElements.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Parallel/RoundRobinArrayElements.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <type_traits>\n#include <unordered_set>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n\n#include \"Parallel/GlobalCache.decl.h\"\n\nnamespace TestHelpers::Parallel {\n/// \\brief Used to assign elements to an array component.\n///\n/// \\details `ArrayIndex` can be used if the array index is something other than\n/// an int and can be indexed by the `i`th element (where `i` is a size_t).\ntemplate <typename Proxy, typename Metavariables, typename... InitTags,\n          typename ArrayIndex = int>\nvoid assign_array_elements_round_robin_style(\n    Proxy& array_proxy, const size_t num_elements, const size_t num_procs,\n    const tuples::TaggedTuple<InitTags...>& initialization_items,\n    ::Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n    const std::unordered_set<size_t>& procs_to_ignore,\n    const ArrayIndex /*meta*/ = {}) {\n  size_t which_proc = 0;\n  for (size_t i = 0; i < num_elements; i++) {\n    ArrayIndex index{};\n    if constexpr (std::is_same_v<ArrayIndex, int>) {\n      index = static_cast<int>(i);\n    } else {\n      index = ArrayIndex{i};\n    }\n    while (procs_to_ignore.find(which_proc) != procs_to_ignore.end()) {\n      which_proc = which_proc + 1 == num_procs ? 0 : which_proc + 1;\n    }\n    array_proxy[index].insert(global_cache, initialization_items, which_proc);\n    which_proc = which_proc + 1 == num_procs ? 0 : which_proc + 1;\n  }\n\n  array_proxy.doneInserting();\n}\n}  // namespace TestHelpers::Parallel\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/ApparentHorizonFinder/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::ah {\nstruct ExampleTimeTag : db::SimpleTag {\n  using type = LinkedMessageId<double>;\n};\n\nstruct ExampleTimeTagCompute : ExampleTimeTag, db::ComputeTag {\n  using base = ExampleTimeTag;\n  using return_type = LinkedMessageId<double>;\n  using argument_tags = tmpl::list<>;\n  static void function(const gsl::not_null<LinkedMessageId<double>*> result) {\n    *result = LinkedMessageId<double>{1.0, std::nullopt};\n  }\n};\n\n/// [HorizonFindCallback]\nstruct ExampleHorizonFindCallback : tt::ConformsTo<::ah::protocols::Callback> {\n  template <typename DbTags, typename Metavariables>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const FastFlow::Status status) {\n    const auto& functions_of_time =\n        Parallel::get<domain::Tags::FunctionsOfTime>(cache);\n\n    // Use these to run control system callbacks, observe quantities, or error\n    // if the horizon find failed.\n    (void)status;\n    (void)functions_of_time;\n  }\n};\n/// [HorizonFindCallback]\n\n/// [HorizonMetavars]\nstruct ExampleHorizonMetavars\n    : tt::ConformsTo<::ah::protocols::HorizonMetavars> {\n  using time_tag = ExampleTimeTag;\n\n  using frame = ::Frame::Distorted;\n\n  using horizon_find_callbacks = tmpl::list<ExampleHorizonFindCallback>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<ExampleTimeTagCompute>;\n\n  static constexpr ::ah::Destination destination =\n      ::ah::Destination::ControlSystem;\n\n  static std::string name() { return \"ExampleHorizon\"; }\n};\n/// [HorizonMetavars]\n}  // namespace TestHelpers::ah\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Events)\nadd_subdirectory(EventsAndDenseTriggers)\nadd_subdirectory(Interpolation)\nadd_subdirectory(LinearSolver)\nadd_subdirectory(NonlinearSolver)\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"EventsHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  ObserveFields.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  AnalyticSolutions\n  DataStructures\n  Domain\n  DomainStructure\n  Observer\n  Options\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/Events/ObserveFields.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace observers::Actions {\nstruct ContributeVolumeData;\n}  // namespace observers::Actions\n/// \\endcond\n\nnamespace TestHelpers::dg::Events::ObserveFields {\nstruct TestSectionIdTag {};\n\nstruct MockContributeVolumeData {\n  struct Results {\n    observers::ObservationId observation_id{};\n    std::string subfile_name{};\n    Parallel::ArrayComponentId array_component_id{};\n    ElementVolumeData received_volume_data{};\n    std::optional<std::vector<char>> serialized_functions_of_time{};\n    std::optional<std::string> dependency{};\n  };\n  static Results results;\n\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const observers::ObservationId& observation_id,\n      const std::string& subfile_name,\n      const Parallel::ArrayComponentId& array_component_id,\n      ElementVolumeData&& received_volume_data,\n      const std::optional<std::vector<char>>& serialized_functions_of_time =\n          std::nullopt,\n      const std::optional<std::string>& dependency = std::nullopt) {\n    results.observation_id = observation_id;\n    results.subfile_name = subfile_name;\n    results.array_component_id = array_component_id;\n    results.received_volume_data = std::move(received_volume_data);\n    results.serialized_functions_of_time = serialized_functions_of_time;\n    results.dependency = dependency;\n  }\n};\n\ninline MockContributeVolumeData::Results MockContributeVolumeData::results{};\n\ntemplate <typename Metavariables>\nstruct ElementComponent {\n  using component_being_mocked = void;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Metavariables::volume_dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverComponent {\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::ContributeVolumeData>;\n  using with_these_simple_actions = tmpl::list<MockContributeVolumeData>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename System, bool HasAnalyticSolution>\nstruct Metavariables {\n  using system = System;\n  static constexpr size_t volume_dim = system::volume_dim;\n  using component_list = tmpl::list<ElementComponent<Metavariables>,\n                                    MockObserverComponent<Metavariables>>;\n  using const_global_cache_tags =\n      tmpl::list<::Tags::AnalyticSolution<typename System::solution_for_test>>;\n  using initial_data =\n      tmpl::conditional_t<HasAnalyticSolution,\n                          typename System::solution_for_test, NoSuchType>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Event, tmpl::list<typename system::ObserveEvent>>>;\n  };\n};\n\n// Helper tags\nnamespace Tags {\ntemplate <typename DataType>\nstruct ScalarVarTimesTwo : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename ScalarVar>\nstruct ScalarVarTimesTwoCompute\n    : db::ComputeTag,\n      ScalarVarTimesTwo<typename ScalarVar::type::type> {\n  using DataType = typename ScalarVar::type::type;\n  using base = ScalarVarTimesTwo<DataType>;\n  using return_type = Scalar<DataType>;\n  using argument_tags = tmpl::list<ScalarVar>;\n  static void function(const gsl::not_null<Scalar<DataType>*> result,\n                       const Scalar<DataType>& scalar_var) {\n    get(*result) = 2.0 * get(scalar_var);\n  }\n};\n\ntemplate <typename DataType>\nstruct ScalarVarTimesThree : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename ScalarVar>\nstruct ScalarVarTimesThreeCompute\n    : db::ComputeTag,\n      ::Tags::Variables<\n          tmpl::list<ScalarVarTimesThree<typename ScalarVar::type::type>>> {\n  using DataType = typename ScalarVar::type::type;\n  using base = ::Tags::Variables<\n      tmpl::list<ScalarVarTimesThree<typename ScalarVar::type::type>>>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<ScalarVar>;\n  static void function(\n      const gsl::not_null<\n          ::Variables<tmpl::list<ScalarVarTimesThree<DataType>>>*>\n          result,\n      const Scalar<DataType>& scalar_var) {\n    result->initialize(get(scalar_var).size());\n    get(get<ScalarVarTimesThree<DataType>>(*result)) = 3.0 * get(scalar_var);\n  }\n};\n}  // namespace Tags\n\n// Test systems\n\ntemplate <template <size_t, class...> class ObservationEvent,\n          typename ArraySectionId = void, typename DataType = DataVector>\nstruct ScalarSystem {\n  using array_section_id = ArraySectionId;\n  using data_type = DataType;\n\n  struct ScalarVar : db::SimpleTag {\n    static std::string name() { return \"Scalar\"; }\n    using type = Scalar<DataType>;\n  };\n\n  static constexpr size_t volume_dim = 1;\n  using variables_tag = ::Tags::Variables<tmpl::list<ScalarVar>>;\n\n  template <typename CheckComponent>\n  static void check_data(const CheckComponent& check_component) {\n    check_component(\"Scalar\", ScalarVar{});\n    check_component(\"ScalarVarTimesTwo\", Tags::ScalarVarTimesTwo<DataType>{});\n    check_component(\"ScalarVarTimesThree\",\n                    Tags::ScalarVarTimesThree<DataType>{});\n  }\n\n  using all_vars_for_test = tmpl::list<ScalarVar>;\n  struct solution_for_test : public MarkAsAnalyticSolution {\n    using vars_for_test = typename variables_tag::tags_list;\n\n    template <typename CheckComponent>\n    static void check_data(const CheckComponent& check_component) {\n      check_component(\"Error(Scalar)\", ScalarVar{});\n    }\n    static tuples::tagged_tuple_from_typelist<vars_for_test> variables(\n\n        const tnsr::I<DataVector, 1>& x, const double t,\n        const vars_for_test /*meta*/) {\n      return {Scalar<DataType>{1.0 - t * get<0>(x)}};\n    }\n\n    void pup(PUP::er& /*p*/) {}  // NOLINT\n  };\n\n  using ObserveEvent = ObservationEvent<\n      volume_dim,\n      tmpl::push_back<all_vars_for_test,\n                      Tags::ScalarVarTimesTwoCompute<ScalarVar>,\n                      Tags::ScalarVarTimesThree<DataType>,\n                      ::domain::Tags::Coordinates<volume_dim, Frame::Inertial>,\n                      ::Tags::Error<ScalarVar>>,\n      tmpl::list<Tags::ScalarVarTimesThreeCompute<ScalarVar>,\n                 ::Tags::ErrorsCompute<tmpl::list<ScalarVar>>>,\n      ArraySectionId>;\n  static constexpr auto creation_string_for_test =\n      \"ObserveFields:\\n\"\n      \"  SubfileName: element_data\\n\"\n      \"  CoordinatesFloatingPointType: Double\\n\"\n      \"  VariablesToObserve: [Scalar, ScalarVarTimesTwo, ScalarVarTimesThree, \"\n      \"Error(Scalar)]\\n\"\n      \"  FloatingPointTypes: [Double]\\n\"\n      \"  BlocksToObserve: All\\n\";\n  static ObserveEvent make_test_object(\n      const std::optional<Mesh<volume_dim>>& interpolating_mesh,\n      const std::optional<Mesh<volume_dim>>& projection_mesh,\n      std::optional<std::vector<std::string>> active_block_or_block_groups =\n          std::nullopt,\n      std::optional<std::string> dependency = std::nullopt) {\n    return ObserveEvent{\n        \"element_data\",\n        FloatingPointType::Double,\n        {FloatingPointType::Double},\n        {\"Scalar\", \"ScalarVarTimesTwo\", \"ScalarVarTimesThree\", \"Error(Scalar)\"},\n        std::move(active_block_or_block_groups),\n        interpolating_mesh,\n        projection_mesh,\n        std::move(dependency)};\n  }\n};\n\ntemplate <template <size_t, class...> class ObservationEvent,\n          typename ArraySectionId = void, typename DataType = DataVector>\nstruct ComplicatedSystem {\n  using array_section_id = ArraySectionId;\n  using data_type = DataType;\n\n  struct ScalarVar : db::SimpleTag {\n    static std::string name() { return \"Scalar\"; }\n    using type = Scalar<DataType>;\n  };\n\n  struct VectorVar : db::SimpleTag {\n    static std::string name() { return \"Vector\"; }\n    using type = tnsr::I<DataType, 2>;\n  };\n\n  struct TensorVar : db::SimpleTag {\n    static std::string name() { return \"Tensor\"; }\n    using type = tnsr::ii<DataType, 2>;\n  };\n\n  struct TensorVar2 : db::SimpleTag {\n    static std::string name() { return \"Tensor2\"; }\n    using type = tnsr::ii<DataType, 2>;\n  };\n\n  struct UnobservedVar : db::SimpleTag {\n    static std::string name() { return \"Unobserved\"; }\n    using type = Scalar<DataType>;\n  };\n\n  struct UnobservedVar2 : db::SimpleTag {\n    static std::string name() { return \"Unobserved2\"; }\n    using type = Scalar<DataType>;\n  };\n\n  static constexpr size_t volume_dim = 2;\n  using variables_tag =\n      ::Tags::Variables<tmpl::list<TensorVar, ScalarVar, UnobservedVar>>;\n  using primitive_variables_tag =\n      ::Tags::Variables<tmpl::list<VectorVar, TensorVar2, UnobservedVar2>>;\n\n  template <typename CheckComponent>\n  static void check_data(const CheckComponent& check_component) {\n    check_component(\"Scalar\", ScalarVar{});\n    check_component(\"ScalarVarTimesTwo\", Tags::ScalarVarTimesTwo<DataType>{});\n    check_component(\"ScalarVarTimesThree\",\n                    Tags::ScalarVarTimesThree<DataType>{});\n    check_component(\"Tensor_xx\", TensorVar{}, 0, 0);\n    check_component(\"Tensor_yx\", TensorVar{}, 0, 1);\n    check_component(\"Tensor_yy\", TensorVar{}, 1, 1);\n    check_component(\"Vector_x\", VectorVar{}, 0);\n    check_component(\"Vector_y\", VectorVar{}, 1);\n    check_component(\"Tensor2_xx\", TensorVar2{}, 0, 0);\n    check_component(\"Tensor2_yx\", TensorVar2{}, 0, 1);\n    check_component(\"Tensor2_yy\", TensorVar2{}, 1, 1);\n  }\n\n  using all_vars_for_test = tmpl::list<TensorVar, ScalarVar, UnobservedVar,\n                                       VectorVar, TensorVar2, UnobservedVar2>;\n  struct solution_for_test : public MarkAsAnalyticSolution {\n    using vars_for_test = typename primitive_variables_tag::tags_list;\n\n    template <typename CheckComponent>\n    static void check_data(const CheckComponent& check_component) {\n      check_component(\"Error(Vector)_x\", VectorVar{}, 0);\n      check_component(\"Error(Vector)_y\", VectorVar{}, 1);\n      check_component(\"Error(Tensor2)_xx\", TensorVar2{}, 0, 0);\n      check_component(\"Error(Tensor2)_yx\", TensorVar2{}, 0, 1);\n      check_component(\"Error(Tensor2)_yy\", TensorVar2{}, 1, 1);\n    }\n\n    static tuples::tagged_tuple_from_typelist<vars_for_test> variables(\n        const tnsr::I<DataVector, 2>& x, const double t,\n        const vars_for_test /*meta*/) {\n      auto vector = make_with_value<tnsr::I<DataVector, 2>>(x, 0.0);\n      auto tensor = make_with_value<tnsr::ii<DataVector, 2>>(x, 0.0);\n      auto unobserved = make_with_value<Scalar<DataVector>>(x, 0.0);\n      // Arbitrary functions\n      get<0>(vector) = 1.0 - t * get<0>(x);\n      get<1>(vector) = 1.0 - t * get<1>(x);\n      get<0, 0>(tensor) = get<0>(x) + get<1>(x);\n      get<0, 1>(tensor) = get<0>(x) - get<1>(x);\n      get<1, 1>(tensor) = get<0>(x) * get<1>(x);\n      get(unobserved) = 2.0 * get<0>(x);\n      return {std::move(vector), std::move(tensor), std::move(unobserved)};\n    }\n\n    void pup(PUP::er& /*p*/) {}  // NOLINT\n  };\n\n  using ObserveEvent = ObservationEvent<\n      volume_dim,\n      tmpl::push_back<all_vars_for_test,\n                      Tags::ScalarVarTimesTwoCompute<ScalarVar>,\n                      Tags::ScalarVarTimesThree<DataType>,\n                      ::domain::Tags::Coordinates<volume_dim, Frame::Inertial>,\n                      ::Tags::Error<VectorVar>, ::Tags::Error<TensorVar2>>,\n      tmpl::list<\n          Tags::ScalarVarTimesThreeCompute<ScalarVar>,\n          ::Tags::ErrorsCompute<typename primitive_variables_tag::tags_list>>,\n      ArraySectionId>;\n  static constexpr auto creation_string_for_test =\n      \"ObserveFields:\\n\"\n      \"  SubfileName: element_data\\n\"\n      \"  CoordinatesFloatingPointType: Double\\n\"\n      \"  VariablesToObserve: [Scalar, ScalarVarTimesTwo, ScalarVarTimesThree,\"\n      \"                       Vector, Tensor, Tensor2,\"\n      \"                       Error(Vector), Error(Tensor2)]\\n\"\n      \"  FloatingPointTypes: [Double, Double, Double, Double, Float, Float,\"\n      \"                       Double, Float]\\n\"\n      \"  BlocksToObserve: All\\n\";\n\n  static ObserveEvent make_test_object(\n      const std::optional<Mesh<volume_dim>>& interpolating_mesh,\n      const std::optional<Mesh<volume_dim>>& projection_mesh,\n      std::optional<std::vector<std::string>> active_block_or_block_groups =\n          std::nullopt,\n      std::optional<std::string> dependency = std::nullopt) {\n    return ObserveEvent(\n        \"element_data\", FloatingPointType::Double,\n        {FloatingPointType::Double, FloatingPointType::Double,\n         FloatingPointType::Double, FloatingPointType::Double,\n         FloatingPointType::Float, FloatingPointType::Float,\n         FloatingPointType::Double, FloatingPointType::Float},\n        {\"Scalar\", \"ScalarVarTimesTwo\", \"ScalarVarTimesThree\", \"Vector\",\n         \"Tensor\", \"Tensor2\", \"Error(Vector)\", \"Error(Tensor2)\"},\n        std::move(active_block_or_block_groups), interpolating_mesh,\n        projection_mesh, std::move(dependency));\n  }\n};\n}  // namespace TestHelpers::dg::Events::ObserveFields\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/EventsAndDenseTriggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(DenseTriggers)\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"DenseTriggerHelpers\")\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  TestTrigger.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  TestTrigger.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Charmxx::pup\n  DataStructures\n  EventsAndDenseTriggers\n  Framework\n  Options\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/TestTrigger.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/TestTrigger.hpp\"\n\nnamespace TestHelpers::DenseTriggers {\nPUP::able::PUP_ID TestTrigger::my_PUP_ID = 0;  // NOLINT\n}  // namespace TestHelpers::DenseTriggers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/TestTrigger.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <ios>\n#include <optional>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n/// \\endcond\n\nnamespace TestHelpers::DenseTriggers {\nclass TestTrigger : public DenseTrigger {\n public:\n  /// \\cond\n  TestTrigger() = default;\n  explicit TestTrigger(CkMigrateMessage* const msg) : DenseTrigger(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TestTrigger);  // NOLINT\n  /// \\endcond\n\n  struct NotReady {\n    template <typename T>\n    static std::string format(const std::optional<T>& arg) {\n      if (arg.has_value()) {\n        return MakeString{} << std::boolalpha << arg;\n      } else {\n        return \"NotReady\";\n      }\n    }\n  };\n\n  struct IsTriggered {\n    using type = Options::Auto<bool, NotReady>;\n    constexpr static Options::String help = \"IsTriggered\";\n  };\n\n  struct NextCheck {\n    using type = Options::Auto<double, NotReady>;\n    constexpr static Options::String help = \"NextCheck\";\n  };\n\n  using options = tmpl::list<IsTriggered, NextCheck>;\n  constexpr static Options::String help = \"help\";\n\n  TestTrigger(const std::optional<bool>& is_triggered,\n              const std::optional<double>& next_check)\n      : is_triggered_(is_triggered), next_check_(next_check) {}\n\n  using is_triggered_return_tags = tmpl::list<>;\n  using is_triggered_argument_tags = tmpl::list<>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<bool> is_triggered(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/) const {\n    return is_triggered_;\n  }\n\n  using next_check_time_return_tags = tmpl::list<>;\n  using next_check_time_argument_tags = tmpl::list<>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<double> next_check_time(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/) const {\n    return next_check_;\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    DenseTrigger::pup(p);\n    p | is_triggered_;\n    p | next_check_;\n  }\n\n private:\n  std::optional<bool> is_triggered_;\n  std::optional<double> next_check_;\n};\n\ntemplate <typename Label>\nclass BoxTrigger : public DenseTrigger {\n public:\n  /// \\cond\n  BoxTrigger() = default;\n  explicit BoxTrigger(CkMigrateMessage* const msg) : DenseTrigger(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BoxTrigger);  // NOLINT\n  /// \\endcond\n\n  static std::string name() {\n    return \"BoxTrigger<\" + pretty_type::name<Label>() + \">\";\n  }\n\n  using options = tmpl::list<>;\n  constexpr static Options::String help = \"help\";\n\n  struct IsTriggered : db::SimpleTag {\n    using type = std::optional<bool>;\n  };\n\n  struct NextCheck : db::SimpleTag {\n    using type = std::optional<double>;\n  };\n\n  using is_triggered_return_tags = tmpl::list<>;\n  using is_triggered_argument_tags = tmpl::list<IsTriggered>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<bool> is_triggered(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/,\n      const std::optional<bool>& is_triggered) const {\n    return is_triggered;\n  }\n\n  using next_check_time_return_tags = tmpl::list<>;\n  using next_check_time_argument_tags = tmpl::list<NextCheck>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<double> next_check_time(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/,\n      const std::optional<double>& next_check) const {\n    return next_check;\n  }\n\n  void pup(PUP::er& p) { DenseTrigger::pup(p); }\n};\n\n/// \\cond\ntemplate <typename Label>\nPUP::able::PUP_ID BoxTrigger<Label>::my_PUP_ID = 0;  // NOLINT\n/// \\endcond\n}  // namespace TestHelpers::DenseTriggers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/Interpolation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"InterpolationHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  Examples.hpp\n  InterpolateOnElementTestHelpers.hpp\n  InterpolationTargetTestHelpers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  ApparentHorizonFinder\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  GeneralRelativity\n  Observer\n  Options\n  Parallel\n  ParallelInterpolation\n  Spectral\n  SphericalHarmonics\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/Interpolation/Examples.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeVarsToInterpolate.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Frame {\nstruct Grid;\n}  // namespace Frame\nnamespace Tags {\nstruct Time;\n}  // namespace Tags\n/// \\endcond\n\nnamespace intrp::TestHelpers {\nstruct FakeCacheTag : db::SimpleTag {\n  using type = int;\n};\nstruct FakeSimpleTag : db::SimpleTag {\n  using type = int;\n};\nstruct FakeComputeTag : db::ComputeTag {\n  using base = FakeSimpleTag;\n  using return_type = int;\n  using argument_tags = tmpl::list<>;\n  static void function() {}\n};\n\n/// [ComputeTargetPoints]\nstruct ExampleComputeTargetPoints\n    : tt::ConformsTo<intrp::protocols::ComputeTargetPoints> {\n  // This is not required by the protocol, but these tags will also be added to\n  // the global cache\n  using const_global_cache_tags = tmpl::list<FakeCacheTag>;\n\n  using is_sequential = std::false_type;\n\n  using frame = ::Frame::Grid;\n\n  // These are not required by the protocol, but these tags will be added to the\n  // InterpolationTarget DataBox.\n  using simple_tags = tmpl::list<FakeSimpleTag>;\n  using compute_tags = tmpl::list<FakeComputeTag>;\n\n  // This is not required by the protocol, but this function can be specified\n  // and will be run during the Initialization phase of the InterpolationTarget\n  // parallel component\n  template <typename DbTags, typename Metavariables>\n  static void initialize(\n      const gsl::not_null<db::DataBox<DbTags>*> /*box*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/) {\n    // Initialize FakeSimpleTag here\n  }\n\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static tnsr::I<DataVector, 3, frame> points(\n      const db::DataBox<DbTags>& /*box*/,\n      const tmpl::type_<Metavariables>& /*meta*/,\n      const TemporalId& /*temporal_id*/) {\n    // This function will compute points on a given surface that we are\n    // interpolating onto\n    return tnsr::I<DataVector, 3, frame>{};\n  }\n};\n/// [ComputeTargetPoints]\n\n/// [ComputeVarsToInterpolate]\nstruct ExampleComputeVarsToInterpolate\n    : tt::ConformsTo<intrp::protocols::ComputeVarsToInterpolate> {\n  template <typename SrcTagList, typename DestTagList, size_t Dim>\n  static void apply(\n      const gsl::not_null<Variables<DestTagList>*> /*target_vars*/,\n      const Variables<SrcTagList>& /*src_vars*/, const Mesh<Dim>& /*mesh*/) {\n    // Already in the same frame so no need to switch frames\n    // Do some GR calculations to get correct variables\n    // Then modify target_vars\n    return;\n  }\n\n  template <typename SrcTagList, typename DestTagList, size_t Dim,\n            typename TargetFrame>\n  static void apply(\n      const gsl::not_null<Variables<DestTagList>*> /*target_vars*/,\n      const Variables<SrcTagList>& /*src_vars*/, const Mesh<Dim>& /*mesh*/,\n      const Jacobian<DataVector, Dim, TargetFrame,\n                     Frame::Inertial>& /*jacobian_target_to_inertial*/,\n      const InverseJacobian<DataVector, Dim, TargetFrame, Frame::Inertial>&\n      /*inverse_jacobian_target_to_inertial*/,\n      const Jacobian<DataVector, 3, Frame::ElementLogical, TargetFrame>&\n      /*jac_logical_to_target*/,\n      const InverseJacobian<DataVector, 3, Frame::ElementLogical, TargetFrame>&\n      /*invjac_logical_to_target*/,\n      const tnsr::I<DataVector, 3, Frame::Inertial>& /*inertial_mesh_velocity*/,\n      const tnsr::I<DataVector, 3, TargetFrame>&\n      /*grid_to_target_frame_mesh_velocity*/) {\n    // Need to switch frames first\n    // Do some GR calculations to get correct variables\n    // Then modify target_vars\n    return;\n  }\n\n  // These need to exist but don't have to contain anything\n  using allowed_src_tags = tmpl::list<>;\n  using required_src_tags = tmpl::list<>;\n  template <typename Frame>\n  using allowed_dest_tags = tmpl::list<>;\n  template <typename Frame>\n  using required_dest_tags = tmpl::list<>;\n};\n/// [ComputeVarsToInterpolate]\n\n/// [PostInterpolationCallback]\nstruct ExamplePostInterpolationCallback\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {\n  // This is not required by the protocol, but can be specified.\n  static constexpr double fill_invalid_points_with = 0.0;\n\n  // Signature 1. This bool is false if another interpolation action is called\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static bool apply(\n      const gsl::not_null<db::DataBox<DbTags>*> /*box*/,\n      const gsl::not_null<Parallel::GlobalCache<Metavariables>*> /*cache*/,\n      const TemporalId& temporal_id) {\n    return intrp::InterpolationTarget_detail::get_temporal_id_value(\n               temporal_id) > 1.0;\n  }\n\n  // Signature 2. This is just as an example\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const TemporalId& /*temporal_id*/) {}\n\n  // Signature 3. This is just as an example\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const TemporalId& /*temporal_id*/) {}\n};\n/// [PostInterpolationCallback]\n\n/// [InterpolationTargetTag]\nstruct ExampleInterpolationTargetTag\n    : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  using temporal_id = ::Tags::Time;\n\n  using vars_to_interpolate_to_target = tmpl::list<\n      gr::Tags::SpatialMetric<DataVector, 3, ::Frame::Grid>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3, ::Frame::Grid>,\n      gr::Tags::ExtrinsicCurvature<DataVector, 3, ::Frame::Grid>,\n      gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, ::Frame::Grid>>;\n\n  // This is not necessary to conform to the protocol, but is often used\n  using compute_vars_to_interpolate =\n      intrp::ComputeExcisionBoundaryVolumeQuantities;\n\n  // This list will be a lot longer for apparent horizon finding\n  using compute_items_on_target =\n      tmpl::list<ylm::Tags::ThetaPhiCompute<::Frame::Grid>,\n                 ylm::Tags::RadiusCompute<::Frame::Grid>,\n                 ylm::Tags::RhatCompute<::Frame::Grid>>;\n\n  using compute_target_points = ExampleComputeTargetPoints;\n\n  using post_interpolation_callbacks =\n      tmpl::list<ExamplePostInterpolationCallback>;\n};\n/// [InterpolationTargetTag]\n}  // namespace intrp::TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/Interpolation/InterpolateOnElementTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependence/RotationAboutZAxis.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InterpolationTargetVarsFromElement.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/PointInfoTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeVarsToInterpolate.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// Holds code that is shared between multiple tests. Currently used by\n/// - Test_InterpolateWithoutInterpolatorComponent.\n/// - Test_SendNextTimeToCce.\nnamespace InterpolateOnElementTestHelpers {\n\nnamespace Tags {\n// Simple Variables tag for test.\nstruct TestSolution : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n// Tag holding inertial-frame target points for test.\nstruct TestTargetPoints : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n};\nstruct MultiplyByTwo : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n// Compute tag for test.\nstruct MultiplyByTwoCompute : MultiplyByTwo, db::ComputeTag {\n  static void function(const gsl::not_null<Scalar<DataVector>*> result,\n                       const Scalar<DataVector>& x) {\n    get<>(*result) = get<>(x) * 2.0;\n  }\n  using return_type = Scalar<DataVector>;\n  using argument_tags = tmpl::list<TestSolution>;\n  using base = MultiplyByTwo;\n};\n}  // namespace Tags\n\n// compute_vars_to_interpolate for test.\nstruct ComputeMultiplyByTwo\n    : tt::ConformsTo<intrp::protocols::ComputeVarsToInterpolate> {\n  // Although we know the explicit types for SrcTagList and DestTagList,\n  // we keep SrcTagList and DestTagList as template parameters so\n  // that Protocols work properly.\n  template <typename SrcTagList, typename DestTagList>\n  static void apply(const gsl::not_null<Variables<DestTagList>*> target_vars,\n                    const Variables<SrcTagList>& src_vars,\n                    const Mesh<3>& /*mesh*/) {\n    static_assert(std::is_same_v<SrcTagList, tmpl::list<Tags::TestSolution>>,\n                  \"SrcTagList must be only TestSolution\");\n    static_assert(std::is_same_v<DestTagList, tmpl::list<Tags::MultiplyByTwo>>,\n                  \"DestTagList must be only MultiplyByTwo\");\n    const auto& src = get<Tags::TestSolution>(src_vars);\n    auto& dest = get<Tags::MultiplyByTwo>(*target_vars);\n    get(dest) = get(src) * 2.0;\n  }\n  using allowed_src_tags = tmpl::list<Tags::TestSolution>;\n  using required_src_tags = tmpl::list<Tags::TestSolution>;\n  // The following are required to be templated on Frame by the protocol.\n  template <typename Frame>\n  using allowed_dest_tags = tmpl::list<Tags::MultiplyByTwo>;\n  template <typename Frame>\n  using required_dest_tags = tmpl::list<Tags::MultiplyByTwo>;\n};\n\ntemplate <typename TagName, typename VarsTagList>\nvoid fill_variables(const gsl::not_null<Variables<VarsTagList>*> vars,\n                    const tnsr::I<DataVector, 3, Frame::Inertial>& coords) {\n  // Some analytic solution used to fill the volume data and\n  // to test the interpolated data.\n  auto& solution = get<TagName>(*vars);\n  get(solution) =\n      (2.0 * get<0>(coords) + 3.0 * get<1>(coords) + 5.0 * get<2>(coords));\n\n  if constexpr (std::is_same_v<TagName, Tags::MultiplyByTwo>) {\n    get(solution) *= 2.0;\n  } else if constexpr (not std::is_same_v<TagName, Tags::TestSolution>) {\n    ERROR(\"Do not understand the given TagName\");\n  }\n}\n\ntemplate <typename InterpolationTargetTag>\nstruct MockInterpolationTargetVarsFromElement {\n  template <\n      typename ParallelComponent, typename DbTags, typename Metavariables,\n      typename ArrayIndex, typename TemporalId,\n      Requires<tmpl::list_contains_v<DbTags, Tags::TestTargetPoints>> = nullptr>\n  static void apply(\n      db::DataBox<DbTags>& box, Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const std::vector<Variables<\n          typename InterpolationTargetTag::vars_to_interpolate_to_target>>&\n          vars_src,\n      const std::vector<BlockLogicalCoords<Metavariables::volume_dim>>&\n          block_logical_coords,\n      const std::vector<std::vector<size_t>>& global_offsets,\n      const TemporalId& /*temporal_id*/) {\n    CHECK(global_offsets.size() == vars_src.size());\n    // global_offsets and vars_src always have a size of 1 for calls\n    // directly from the elements; the outer vector is used only by\n    // the Interpolator parallel component.\n    CHECK(global_offsets.size() == 1);\n\n    if constexpr (tt::is_a_v<\n                      intrp::TargetPoints::Sphere,\n                      typename InterpolationTargetTag::compute_target_points>) {\n      for (size_t offset : global_offsets[0]) {\n        CHECK(block_logical_coords[offset].has_value());\n      }\n    } else {\n      CHECK(alg::all_of(block_logical_coords,\n                        [](const auto& t) { return t.has_value(); }));\n    }\n\n    // Here we have received only some of the points.\n    const size_t num_pts_received = global_offsets[0].size();\n\n    // Create a new target_points containing only the ones we have received.\n    const auto& all_target_points = db::get<Tags::TestTargetPoints>(box);\n    tnsr::I<DataVector, 3, Frame::Inertial> target_points(num_pts_received);\n    for (size_t i = 0; i < num_pts_received; ++i) {\n      for (size_t d = 0; d < 3; ++d) {\n        target_points.get(d)[i] =\n            all_target_points.get(d)[global_offsets[0][i]];\n      }\n    }\n\n    static_assert(\n        tmpl::count<typename InterpolationTargetTag::\n                        vars_to_interpolate_to_target>::value == 1,\n        \"For this test, we assume only a single interpolated variable\");\n    using solution_tag = tmpl::front<\n        typename InterpolationTargetTag::vars_to_interpolate_to_target>;\n    const auto& test_solution = get<solution_tag>(vars_src[0]);\n\n    // Expected solution\n    Variables<tmpl::list<solution_tag>> expected_vars(vars_src[0].size());\n    fill_variables<solution_tag>(make_not_null(&expected_vars), target_points);\n    const auto& expected_solution = get<solution_tag>(expected_vars);\n\n    // We don't have that many points, so interpolation is good for\n    // only a few digits.\n    Approx custom_approx = Approx::custom().epsilon(1.e-5).scale(1.0);\n    CHECK_ITERABLE_CUSTOM_APPROX(test_solution, expected_solution,\n                                 custom_approx);\n  }\n};\n\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct mock_interpolation_target {\n  static_assert(\n      tt::assert_conforms_to_v<InterpolationTargetTag,\n                               intrp::protocols::InterpolationTargetTag>);\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using component_being_mocked =\n      intrp::InterpolationTarget<Metavariables, InterpolationTargetTag>;\n  using simple_tags = tmpl::list<Tags::TestTargetPoints>;\n  using const_global_cache_tags = tmpl::conditional_t<\n      tt::is_a_v<intrp::TargetPoints::Sphere,\n                 typename InterpolationTargetTag::compute_target_points>,\n      tmpl::list<intrp::Tags::Sphere<InterpolationTargetTag>,\n                 intrp::Tags::Verbosity>,\n      tmpl::list<intrp::Tags::Verbosity>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n  using replace_these_simple_actions =\n      tmpl::list<intrp::Actions::InterpolationTargetVarsFromElement<\n          InterpolationTargetTag>>;\n  using with_these_simple_actions = tmpl::list<\n      MockInterpolationTargetVarsFromElement<InterpolationTargetTag>>;\n};\n\ntemplate <typename ElemComponent, bool UseTimeDependentMaps,\n          typename DomainCreator, typename Runner, typename TemporalId>\nstd::tuple<Variables<tmpl::list<Tags::TestSolution>>, Mesh<3>,\n           tnsr::I<DataVector, 3, Frame::Inertial>>\nmake_volume_data_and_mesh(const DomainCreator& domain_creator, Runner& runner,\n                          const Domain<3>& domain,\n                          const ElementId<3>& element_id,\n                          const TemporalId& temporal_id) {\n  const auto& block = domain.blocks()[element_id.block_id()];\n  Mesh<3> mesh{domain_creator.initial_extents()[element_id.block_id()],\n               Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n  auto inertial_coords = [&element_id, &block, &mesh, &runner, &temporal_id]() {\n    if constexpr (UseTimeDependentMaps) {\n      const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(\n          ActionTesting::cache<ElemComponent>(runner, element_id));\n      ElementMap<3, ::Frame::Grid> map_logical_to_grid{\n          element_id, block.moving_mesh_logical_to_grid_map().get_clone()};\n      return block.moving_mesh_grid_to_inertial_map()(\n          map_logical_to_grid(logical_coordinates(mesh)),\n          temporal_id.substep_time(), functions_of_time);\n    } else {\n      (void)runner;\n      (void)temporal_id;\n      ElementMap<3, Frame::Inertial> map{element_id,\n                                         block.stationary_map().get_clone()};\n      return map(logical_coordinates(mesh));\n    }\n  }();\n\n  // create volume data\n  Variables<tmpl::list<Tags::TestSolution>> vars(mesh.number_of_grid_points());\n  fill_variables<Tags::TestSolution>(make_not_null(&vars), inertial_coords);\n  return std::make_tuple(std::move(vars), mesh, std::move(inertial_coords));\n}\n\n// This is the main test; Takes a functor as an argument so that\n// different tests can call it to do slightly different things.\ntemplate <typename Metavariables, typename elem_component, bool OffCenter,\n          typename Functor>\nvoid test_interpolate_on_element(\n    Functor initialize_elements_and_queue_simple_actions) {\n  using metavars = Metavariables;\n  using target_tag = typename metavars::InterpolationTargetA;\n  constexpr bool is_sphere =\n      tt::is_a_v<intrp::TargetPoints::Sphere,\n                 typename target_tag::compute_target_points>;\n  using target_component = mock_interpolation_target<metavars, target_tag>;\n\n  if constexpr (OffCenter) {\n    static_assert(is_sphere,\n                  \"Only sphere target can be off center for this test.\");\n  }\n\n  const std::unique_ptr<DomainCreator<3>> domain_creator_ptr = []() {\n    if constexpr (Metavariables::use_time_dependent_maps) {\n      static_assert(not OffCenter,\n                    \"Off-center test not supported for time dependent map test \"\n                    \"at this time.\");\n      return std::make_unique<domain::creators::Sphere>(\n          0.9, 2.9, domain::creators::Sphere::Excision{}, 2_st, 7_st, false,\n          std::nullopt, std::vector<double>{},\n          domain::CoordinateMaps::Distribution::Linear, ShellWedges::All,\n          // Rotation so points always remain in domain\n          std::make_unique<\n              domain::creators::time_dependence::RotationAboutZAxis<3>>(\n              0.0, 0.0, 0.1, 0.0));\n    } else {\n      if constexpr (OffCenter) {\n        using BCO = domain::creators::BinaryCompactObject<false>;\n        return std::make_unique<BCO>(\n            BCO::Object{0.9, 2.9, 4.0, true, true},\n            BCO::Object{0.9, 2.9, -4.0, true, true},\n            std::array<double, 2>{{0.1, 0.2}}, 20.0, 100.0, 1.0,\n            // Only object B needs to have the refinement for this test\n            std::unordered_map<std::string, std::array<size_t, 3>>{\n                {\"ObjectAShell\", std::array{0_st, 0_st, 0_st}},\n                {\"ObjectACube\", std::array{0_st, 0_st, 0_st}},\n                {\"ObjectBShell\", std::array{2_st, 2_st, 2_st}},\n                {\"ObjectBCube\", std::array{2_st, 2_st, 2_st}},\n                {\"Envelope\", std::array{0_st, 0_st, 0_st}},\n                {\"OuterShell0\", std::array{0_st, 0_st, 0_st}}},\n            7_st);\n      } else {\n        return std::make_unique<domain::creators::Sphere>(\n            0.9, 2.9, domain::creators::Sphere::Excision{}, 2_st, 7_st, false);\n      }\n    }\n  }();\n  const DomainCreator<3>& domain_creator = *domain_creator_ptr;\n  const auto domain = domain_creator.create_domain();\n  const double x_center = OffCenter ? -4.0 : 0.0;\n\n  Slab slab(0.0, 1.0);\n  TimeStepId temporal_id(true, 0, Time(slab, Rational(11, 15)));\n\n  // Create Element_ids.\n  const std::vector<ElementId<3>> element_ids =\n      initial_element_ids(domain_creator.initial_refinement_levels());\n\n  // This name must match the hard coded one in UniformTranslation\n  const std::string f_of_t_name = \"Translation\";\n  std::unordered_map<std::string, double> initial_expiration_times{};\n  initial_expiration_times[f_of_t_name] =\n      13.5 / 16.0;  // Arbitrary value greater than temporal_id above.\n\n  // Create target points and interp_point_info\n  const size_t ell = 4;\n  const size_t num_points = is_sphere ? 2 * (ell + 1) * (2 * ell + 1) : 10_st;\n  tnsr::I<DataVector, 3, Frame::Inertial> target_points = [&]() {\n    MAKE_GENERATOR(gen);\n    tnsr::I<DataVector, 3, Frame::Inertial> result(num_points);\n    std::uniform_real_distribution<> r_dist(0.9001, 2.8999);\n    std::uniform_real_distribution<> theta_dist(0.0, M_PI);\n    std::uniform_real_distribution<> phi_dist(0.0, 2 * M_PI);\n    for (size_t i = 0; i < num_points; ++i) {\n      const double r = r_dist(gen);\n      const double theta = theta_dist(gen);\n      const double phi = phi_dist(gen);\n      get<0>(result)[i] = x_center + r * sin(theta) * cos(phi);\n      get<1>(result)[i] = r * sin(theta) * sin(phi);\n      get<2>(result)[i] = r * cos(theta);\n    }\n    return result;\n  }();\n\n  tuples::tagged_tuple_from_typelist<tmpl::conditional_t<\n      is_sphere,\n      tmpl::list<domain::Tags::Domain<3>, intrp::Tags::Sphere<target_tag>,\n                 intrp::Tags::Verbosity>,\n      tmpl::list<domain::Tags::Domain<3>, intrp::Tags::Verbosity>>>\n      init_tuple{};\n\n  tuples::get<domain::Tags::Domain<3>>(init_tuple) =\n      std::move(domain_creator.create_domain());\n  tuples::get<intrp::Tags::Verbosity>(init_tuple) = ::Verbosity::Silent;\n\n  if constexpr (is_sphere) {\n    tuples::get<intrp::Tags::Sphere<target_tag>>(init_tuple) =\n        intrp::OptionHolders::Sphere{ell, std::array{x_center, 0.0, 0.0},\n                                     std::vector<double>{1.0, 2.5},\n                                     ylm::AngularOrdering::Cce};\n  }\n\n  // Emplace target component.\n  auto runner = [&domain_creator, &init_tuple, &initial_expiration_times]() {\n    if constexpr (Metavariables::use_time_dependent_maps) {\n      return ActionTesting::MockRuntimeSystem<metavars>(\n          std::move(init_tuple),\n          domain_creator.functions_of_time(initial_expiration_times));\n    } else {\n      (void)domain_creator;\n      (void)initial_expiration_times;\n      return ActionTesting::MockRuntimeSystem<metavars>(std::move(init_tuple));\n    }\n  }();\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component_and_initialize<target_component>(\n      &runner, 0, {target_points});\n\n  static_assert(\n      std::is_same_v<typename metavars::InterpolationTargetA::temporal_id::type,\n                     double> or\n          std::is_same_v<\n              typename metavars::InterpolationTargetA::temporal_id::type,\n              TimeStepId>,\n      \"Unsupported temporal_id type\");\n  if constexpr (std::is_same_v<\n                    typename metavars::InterpolationTargetA::temporal_id::type,\n                    double>) {\n    initialize_elements_and_queue_simple_actions(\n        domain_creator, domain, element_ids, target_points, runner,\n        temporal_id.substep_time());\n  } else if constexpr (std::is_same_v<typename metavars::InterpolationTargetA::\n                                          temporal_id::type,\n                                      TimeStepId>) {\n    initialize_elements_and_queue_simple_actions(domain_creator, domain,\n                                                 element_ids, target_points,\n                                                 runner, temporal_id);\n  }\n\n  // Only some of the actions/events just invoked on elements (those\n  // elements which contain target points) will queue a simple action on the\n  // InterpolationTarget.  Invoke those simple actions now.\n  while (not ActionTesting::is_simple_action_queue_empty<target_component>(\n      runner, 0)) {\n    runner.template invoke_queued_simple_action<target_component>(0);\n  }\n}\n}  // namespace InterpolateOnElementTestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/Interpolation/InterpolationTargetTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace InterpTargetTestHelpers {\nenum class ValidPoints { All, None, Some };\n\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct mock_interpolation_target {\n  static_assert(\n      tt::assert_conforms_to_v<InterpolationTargetTag,\n                               intrp::protocols::InterpolationTargetTag>);\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using component_being_mocked =\n      intrp::InterpolationTarget<Metavariables, InterpolationTargetTag>;\n  using const_global_cache_tags = tmpl::flatten<tmpl::append<\n      Parallel::get_const_global_cache_tags_from_actions<\n          tmpl::list<typename InterpolationTargetTag::compute_target_points>>,\n      tmpl::list<domain::Tags::Domain<Metavariables::volume_dim>>>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<intrp::Actions::InitializeInterpolationTarget<\n              Metavariables, InterpolationTargetTag>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n};\n\ntemplate <typename InterpolationTargetTag, size_t Dim>\nstruct MockMetavars {\n  static constexpr size_t volume_dim = Dim;\n  using interpolator_source_vars = tmpl::list<gr::Tags::Lapse<DataVector>>;\n  using interpolation_target_tags = tmpl::list<InterpolationTargetTag>;\n\n  using component_list =\n      tmpl::list<InterpTargetTestHelpers::mock_interpolation_target<\n          MockMetavars, InterpolationTargetTag>>;\n};\n\ntemplate <typename InterpolationTargetTag, size_t Dim,\n          typename InterpolationTargetOptionTag, typename BlockCoordHolder,\n          typename... ExtraCacheObjects>\nvoid test_interpolation_target(\n    typename InterpolationTargetOptionTag::type options,\n    const BlockCoordHolder& expected_block_coord_holders,\n    const ExtraCacheObjects&... extra_cache_objects) {\n  using metavars = MockMetavars<InterpolationTargetTag, Dim>;\n  using target_component =\n      mock_interpolation_target<metavars, InterpolationTargetTag>;\n  // Assert that all ComputeTargetPoints conform to the protocol\n  static_assert(tt::assert_conforms_to_v<\n                typename InterpolationTargetTag::compute_target_points,\n                intrp::protocols::ComputeTargetPoints>);\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {extra_cache_objects..., std::move(options),\n       Domain<metavars::volume_dim>{}, ::Verbosity::Silent}};\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<target_component>(&runner, 0);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<target_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& target_box =\n      ActionTesting::get_databox<target_component>(make_not_null(&runner), 0);\n  const auto& cache = ActionTesting::cache<target_component>(runner, 0_st);\n\n  Slab slab(0.0, 1.0);\n  TimeStepId temporal_id(true, 0, ::Time(slab, 0));\n\n  const auto block_coord_holders =\n      intrp::InterpolationTarget_detail::block_logical_coords<\n          InterpolationTargetTag>(target_box, cache, temporal_id);\n\n  const size_t number_of_points = expected_block_coord_holders.size();\n  for (size_t i = 0; i < number_of_points; ++i) {\n    if (block_coord_holders[i].has_value()) {\n      CHECK(block_coord_holders[i].value().id ==\n            expected_block_coord_holders[i].value().id);\n      CHECK_ITERABLE_APPROX(block_coord_holders[i].value().data,\n                            expected_block_coord_holders[i].value().data);\n    }\n  }\n}\n}  // namespace InterpTargetTestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/LinearSolver/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"LinearSolverHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_sources(\n  ${LIBRARY}\n  INTERFACE\n  DistributedLinearSolverAlgorithmTestHelpers.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  DistributedLinearSolverAlgorithmTestHelpers.hpp\n  LinearSolverAlgorithmTestHelpers.hpp\n  ResidualMonitorActionsTestHelpers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Actions\n  Convergence\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  EllipticDg\n  ErrorHandling\n  Initialization\n  Observer\n  Options\n  Parallel\n  ParallelLinearSolver\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp\"\n\nnamespace DistributedLinearSolverAlgorithmTestHelpers {\n\nblaze::DynamicMatrix<double> combine_matrix_slices(\n    const std::vector<blaze::DynamicMatrix<double>>& matrix_slices) {\n  const size_t num_slices = matrix_slices.size();\n  const size_t num_cols_per_slice = matrix_slices.begin()->columns();\n  const size_t total_num_points = num_slices * num_cols_per_slice;\n  blaze::DynamicMatrix<double> full_matrix(total_num_points, total_num_points);\n  for (size_t i = 0; i < num_slices; ++i) {\n    blaze::submatrix(full_matrix, 0, i * num_cols_per_slice, total_num_points,\n                     num_cols_per_slice) = gsl::at(matrix_slices, i);\n  }\n  return full_matrix;\n}\n\n}  // namespace DistributedLinearSolverAlgorithmTestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Actions/MakeIdentityIfSkipped.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\n\n/// Functionality to test parallel linear solvers on multiple elements\nnamespace DistributedLinearSolverAlgorithmTestHelpers {\n\nnamespace OptionTags {\n// This option expects a list of N matrices that each have N*M rows and M\n// columns, where N is the number of elements and M is a nonzero integer.\n// Therefore, this option specifies a (N*M,N*M) matrix that has its columns\n// split over all elements. In a context where the linear operator represents a\n// DG discretization, M is the number of collocation points per element.\nstruct LinearOperator {\n  static constexpr Options::String help = \"The linear operator A to invert.\";\n  using type = std::vector<blaze::DynamicMatrix<double>>;\n};\n// Both of the following options expect a list of N vectors that have a size of\n// M each, so that they constitute a vector of total size N*M (see above).\nstruct Source {\n  static constexpr Options::String help = \"The source b in the equation Ax=b.\";\n  using type = std::vector<blaze::DynamicVector<double>>;\n};\nstruct ExpectedResult {\n  static constexpr Options::String help = \"The solution x in the equation Ax=b\";\n  using type = std::vector<blaze::DynamicVector<double>>;\n};\n}  // namespace OptionTags\n\nstruct LinearOperator : db::SimpleTag {\n  using type = std::vector<blaze::DynamicMatrix<double>>;\n  using option_tags = tmpl::list<OptionTags::LinearOperator>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& linear_operator) {\n    return linear_operator;\n  }\n};\n\nstruct Source : db::SimpleTag {\n  using type = std::vector<blaze::DynamicVector<double>>;\n  using option_tags = tmpl::list<OptionTags::Source>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& source) { return source; }\n};\n\nstruct ExpectedResult : db::SimpleTag {\n  using type = std::vector<blaze::DynamicVector<double>>;\n  using option_tags = tmpl::list<OptionTags::ExpectedResult>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& expected_result) {\n    return expected_result;\n  }\n};\n\n// The vector `x` we want to solve for\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"ScalarField\"; }\n};\n\nusing fields_tag = Tags::Variables<tmpl::list<ScalarFieldTag>>;\nusing sources_tag = db::add_tag_prefix<::Tags::FixedSource, fields_tag>;\nusing operator_applied_to_fields_tag =\n    db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, fields_tag>;\n\n// We are working on a one-dimension domain where each element corresponds to an\n// entry of the vectors provided in the input file. This function translates the\n// element to an index into these vectors. We assume the domain is composed of a\n// single block.\ninline size_t get_index(const ElementId<1>& element_id) {\n  return element_id.segment_id(0).index();\n}\n\n// Assemble the full matrix from its per-element slices\nblaze::DynamicMatrix<double> combine_matrix_slices(\n    const std::vector<blaze::DynamicMatrix<double>>& matrix_slices);\n\n// In the following `ComputeOperatorAction` and `CollectOperatorAction` actions\n// we compute A(p)=sum_elements(A_element(p_element)) in a global reduction and\n// then broadcast the global A(p) back to the elements so that they can extract\n// their A_element(p). This is horribly inefficient parallelism but allows us to\n// just provide a global matrix A (represented by the `LinearOperator` tag) in\n// an input file.\n\n// Forward declare to keep these actions in the order they are used\ntemplate <typename OperandTag>\nstruct CollectOperatorAction;\n\ntemplate <typename OperandTag>\nstruct ComputeOperatorAction {\n private:\n  using local_operator_applied_to_operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, OperandTag>;\n\n public:\n  using const_global_cache_tags = tmpl::list<LinearOperator>;\n  using simple_tags = tmpl::list<local_operator_applied_to_operand_tag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ElementId<1>& element_id,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ActionList /*meta*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ParallelComponent* const /*meta*/) {\n    const size_t element_index = get_index(element_id);\n    const auto& operator_matrices = get<LinearOperator>(box);\n    const auto number_of_elements = operator_matrices.size();\n    const auto& linear_operator = gsl::at(operator_matrices, element_index);\n    const auto number_of_grid_points = linear_operator.columns();\n    const auto& operand = get<OperandTag>(box);\n\n    typename OperandTag::type operator_applied_to_operand{\n        number_of_grid_points * number_of_elements};\n    dgemv_('N', linear_operator.rows(), linear_operator.columns(), 1,\n           linear_operator.data(), linear_operator.spacing(), operand.data(), 1,\n           0, operator_applied_to_operand.data(), 1);\n\n    Parallel::contribute_to_reduction<CollectOperatorAction<OperandTag>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<typename OperandTag::type, funcl::Plus<>>>{\n            operator_applied_to_operand},\n        Parallel::get_parallel_component<ParallelComponent>(cache)[element_id],\n        Parallel::get_parallel_component<ParallelComponent>(cache));\n\n    // Terminate algorithm for now. The reduction will be broadcast to the\n    // next action which is responsible for restarting the algorithm.\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <typename OperandTag>\nstruct CollectOperatorAction {\n  using local_operator_applied_to_operand_tag =\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, OperandTag>;\n\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ScalarFieldOperandTag,\n            Requires<tmpl::list_contains_v<\n                DbTagsList, local_operator_applied_to_operand_tag>> = nullptr>\n  static void apply(\n      db::DataBox<DbTagsList>& box,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<1>& element_id,\n      const Variables<tmpl::list<ScalarFieldOperandTag>>& Ap_global_data) {\n    const size_t element_index = get_index(element_id);\n    // This could be generalized to work on the Variables instead of the\n    // Scalar, but it's only for the purpose of this test.\n    const auto number_of_grid_points = get<LinearOperator>(box)[0].columns();\n    const auto& Ap_global = get<ScalarFieldOperandTag>(Ap_global_data).get();\n    DataVector Ap_local{number_of_grid_points};\n    std::copy(Ap_global.begin() +\n                  static_cast<int>(element_index * number_of_grid_points),\n              Ap_global.begin() +\n                  static_cast<int>((element_index + 1) * number_of_grid_points),\n              Ap_local.begin());\n    db::mutate<local_operator_applied_to_operand_tag>(\n        [&Ap_local, &number_of_grid_points](auto Ap) {\n          *Ap = typename local_operator_applied_to_operand_tag::type{\n              number_of_grid_points};\n          get(get<LinearSolver::Tags::OperatorAppliedTo<ScalarFieldOperandTag>>(\n              *Ap)) = Ap_local;\n        },\n        make_not_null(&box));\n    // Proceed with algorithm\n    Parallel::get_parallel_component<ParallelComponent>(cache)[element_id]\n        .perform_algorithm(true);\n  }\n};\n\n// Checks for the correct solution after the algorithm has terminated.\ntemplate <typename OptionsGroup>\nstruct TestResult {\n  using const_global_cache_tags =\n      tmpl::list<ExpectedResult, helpers::ExpectedConvergenceReason>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ElementId<1>& element_id,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ActionList /*meta*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ParallelComponent* const /*meta*/) {\n    const size_t element_index = get_index(element_id);\n    const auto& has_converged =\n        get<Convergence::Tags::HasConverged<OptionsGroup>>(box);\n    SPECTRE_PARALLEL_REQUIRE(has_converged);\n    SPECTRE_PARALLEL_REQUIRE(has_converged.reason() ==\n                             get<helpers::ExpectedConvergenceReason>(box));\n    const auto& expected_result =\n        gsl::at(get<ExpectedResult>(box), element_index);\n    const auto& result = get<ScalarFieldTag>(box).get();\n    for (size_t i = 0; i < expected_result.size(); i++) {\n      SPECTRE_PARALLEL_REQUIRE(result[i] == approx(expected_result[i]));\n    }\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct InitializeElement {\n  using simple_tags_from_options =\n      tmpl::list<domain::Tags::InitialExtents<1>,\n                 domain::Tags::InitialRefinementLevels<1>>;\n  using const_global_cache_tags =\n      tmpl::list<Source, elliptic::dg::Tags::Quadrature>;\n  using simple_tags =\n      tmpl::list<domain::Tags::Mesh<1>, domain::Tags::Element<1>,\n                 domain::Tags::Coordinates<1, Frame::ElementLogical>,\n                 domain::Tags::Coordinates<1, Frame::Inertial>, fields_tag,\n                 sources_tag>;\n  using compute_tags = tmpl::list<>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<1>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Domain geometry\n    const auto& domain = db::get<domain::Tags::Domain<1>>(box);\n    const auto& initial_extents = db::get<domain::Tags::InitialExtents<1>>(box);\n    const auto& initial_refinement =\n        db::get<domain::Tags::InitialRefinementLevels<1>>(box);\n    auto element = domain::create_initial_element(element_id, domain.blocks(),\n                                                  initial_refinement);\n    auto mesh = domain::create_initial_mesh(\n        initial_extents, element, Spectral::Basis::Legendre,\n        Parallel::get<elliptic::dg::Tags::Quadrature>(cache));\n    auto logical_coords = logical_coordinates(mesh);\n    const ElementMap<1, Frame::Inertial> element_map{\n        element_id, domain.blocks()[element_id.block_id()]};\n    auto inertial_coords = element_map(logical_coords);\n    // Element data\n    const size_t element_index = get_index(element_id);\n    const auto& source = gsl::at(get<Source>(box), element_index);\n    const size_t num_points = source.size();\n    ::Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), std::move(mesh), std::move(element),\n        std::move(logical_coords), std::move(inertial_coords),\n        typename fields_tag::type{num_points, 0.},\n        typename sources_tag::type{source});\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nnamespace detail {\n\ntemplate <typename Preconditioner>\nstruct run_preconditioner_impl {\n  using type =\n      tmpl::list<ComputeOperatorAction<typename Preconditioner::fields_tag>,\n                 typename Preconditioner::template solve<ComputeOperatorAction<\n                     typename Preconditioner::operand_tag>>,\n                 LinearSolver::Actions::MakeIdentityIfSkipped<Preconditioner>>;\n};\n\ntemplate <>\nstruct run_preconditioner_impl<void> {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Preconditioner>\nusing run_preconditioner =\n    typename run_preconditioner_impl<Preconditioner>::type;\n\n}  // namespace detail\n\ntemplate <typename Metavariables,\n          typename LinearSolverType = typename Metavariables::linear_solver,\n          typename PreconditionerType = typename Metavariables::preconditioner>\nusing initialization_actions =\n    tmpl::list<InitializeElement, typename LinearSolverType::initialize_element,\n               ComputeOperatorAction<fields_tag>,\n               helpers::detail::init_preconditioner<PreconditionerType>,\n               Parallel::Actions::TerminatePhase>;\n\ntemplate <typename Metavariables,\n          typename LinearSolverType = typename Metavariables::linear_solver,\n          typename PreconditionerType = typename Metavariables::preconditioner>\nusing register_actions =\n    tmpl::list<typename LinearSolverType::register_element,\n               helpers::detail::register_preconditioner<PreconditionerType>,\n               Parallel::Actions::TerminatePhase>;\n\ntemplate <typename Metavariables,\n          typename LinearSolverType = typename Metavariables::linear_solver,\n          typename PreconditionerType = typename Metavariables::preconditioner>\nusing solve_actions = tmpl::list<\n    typename LinearSolverType::template solve<tmpl::list<\n        detail::run_preconditioner<PreconditionerType>,\n        ComputeOperatorAction<typename LinearSolverType::operand_tag>>>,\n    Parallel::Actions::TerminatePhase>;\n\ntemplate <typename Metavariables,\n          typename LinearSolverType = typename Metavariables::linear_solver,\n          typename PreconditionerType = typename Metavariables::preconditioner>\nusing test_actions =\n    tmpl::list<TestResult<typename LinearSolverType::options_group>>;\n\ntemplate <typename Metavariables>\nusing ElementArray = elliptic::DgElementArray<\n    Metavariables,\n    tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                      initialization_actions<Metavariables>>,\n               Parallel::PhaseActions<Parallel::Phase::Register,\n                                      register_actions<Metavariables>>,\n               Parallel::PhaseActions<Parallel::Phase::Solve,\n                                      solve_actions<Metavariables>>,\n               Parallel::PhaseActions<Parallel::Phase::Testing,\n                                      test_actions<Metavariables>>>>;\n\ntemplate <typename Metavariables>\nusing component_list =\n    tmpl::push_back<tmpl::append<helpers::detail::get_component_list<\n                                     typename Metavariables::linear_solver>,\n                                 helpers::detail::get_component_list<\n                                     typename Metavariables::preconditioner>>,\n                    ElementArray<Metavariables>,\n                    observers::Observer<Metavariables>,\n                    observers::ObserverWriter<Metavariables>,\n                    helpers::OutputCleaner<Metavariables>>;\n\n}  // namespace DistributedLinearSolverAlgorithmTestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Actions/MakeIdentityIfSkipped.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\nnamespace LinearSolverAlgorithmTestHelpers {\n\nnamespace OptionTags {\ntemplate <typename ValueType>\nstruct LinearOperator {\n  static constexpr Options::String help = \"The linear operator A to invert.\";\n  using type = blaze::DynamicMatrix<ValueType>;\n};\ntemplate <typename ValueType>\nstruct Source {\n  static constexpr Options::String help = \"The source b in the equation Ax=b.\";\n  using type = blaze::DynamicVector<ValueType>;\n};\ntemplate <typename ValueType>\nstruct InitialGuess {\n  static constexpr Options::String help = \"The initial guess for the vector x.\";\n  using type = blaze::DynamicVector<ValueType>;\n};\ntemplate <typename ValueType>\nstruct ExpectedResult {\n  static constexpr Options::String help = \"The solution x in the equation Ax=b\";\n  using type = blaze::DynamicVector<ValueType>;\n};\nstruct ExpectedConvergenceReason {\n  static std::string name() { return \"ConvergenceReason\"; }\n  static constexpr Options::String help = \"The expected convergence reason\";\n  using type = Convergence::Reason;\n};\n}  // namespace OptionTags\n\ntemplate <typename ValueType>\nstruct LinearOperator : db::SimpleTag {\n  using type = blaze::DynamicMatrix<ValueType>;\n  using option_tags = tmpl::list<OptionTags::LinearOperator<ValueType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& linear_operator) {\n    return linear_operator;\n  }\n};\n\ntemplate <typename ValueType>\nstruct Source : db::SimpleTag {\n  using type = blaze::DynamicVector<ValueType>;\n  using option_tags = tmpl::list<OptionTags::Source<ValueType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& source) { return source; }\n};\n\ntemplate <typename ValueType>\nstruct InitialGuess : db::SimpleTag {\n  using type = blaze::DynamicVector<ValueType>;\n  using option_tags = tmpl::list<OptionTags::InitialGuess<ValueType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& initial_guess) {\n    return initial_guess;\n  }\n};\n\ntemplate <typename ValueType>\nstruct ExpectedResult : db::SimpleTag {\n  using type = blaze::DynamicVector<ValueType>;\n  using option_tags = tmpl::list<OptionTags::ExpectedResult<ValueType>>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& expected_result) {\n    return expected_result;\n  }\n};\n\nstruct ExpectedConvergenceReason : db::SimpleTag {\n  using type = Convergence::Reason;\n  using option_tags = tmpl::list<OptionTags::ExpectedConvergenceReason>;\n\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& option_value) {\n    return option_value;\n  }\n};\n\n// The vector `x` we want to solve for\ntemplate <typename ValueType>\nstruct VectorTag : db::SimpleTag {\n  using type = blaze::DynamicVector<ValueType>;\n};\n\ntemplate <typename ValueType>\nusing fields_tag = VectorTag<ValueType>;\n\ntemplate <typename OperandTag>\nstruct ComputeOperatorAction {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const int /*array_index*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ActionList /*meta*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ParallelComponent* const /*meta*/) {\n    using ValueType =\n        tt::get_complex_or_fundamental_type_t<typename OperandTag::type>;\n    db::mutate<LinearSolver::Tags::OperatorAppliedTo<OperandTag>>(\n        [](const gsl::not_null<blaze::DynamicVector<ValueType>*>\n               operator_applied_to_operand,\n           const blaze::DynamicMatrix<ValueType>& linear_operator,\n           const blaze::DynamicVector<ValueType>& operand) {\n          *operator_applied_to_operand = linear_operator * operand;\n        },\n        make_not_null(&box), get<LinearOperator<ValueType>>(box),\n        get<OperandTag>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// Checks for the correct solution after the algorithm has terminated.\ntemplate <typename ValueType, typename OptionsGroup>\nstruct TestResult {\n  using const_global_cache_tags =\n      tmpl::list<ExpectedResult<ValueType>, ExpectedConvergenceReason>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const int /*array_index*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ActionList /*meta*/,\n      // NOLINTNEXTLINE(readability-avoid-const-params-in-decls)\n      const ParallelComponent* const /*meta*/) {\n    const auto& has_converged =\n        get<Convergence::Tags::HasConverged<OptionsGroup>>(box);\n    SPECTRE_PARALLEL_REQUIRE(has_converged);\n    SPECTRE_PARALLEL_REQUIRE(has_converged.reason() ==\n                             get<ExpectedConvergenceReason>(box));\n    const auto& result = get<VectorTag<ValueType>>(box);\n    const auto& expected_result = get<ExpectedResult<ValueType>>(box);\n    for (size_t i = 0; i < expected_result.size(); i++) {\n      if constexpr (std::is_same_v<ValueType, std::complex<double>>) {\n        SPECTRE_PARALLEL_REQUIRE(real(result[i]) ==\n                                 approx(real(expected_result[i])));\n        SPECTRE_PARALLEL_REQUIRE(imag(result[i]) ==\n                                 approx(imag(expected_result[i])));\n      } else {\n        SPECTRE_PARALLEL_REQUIRE(result[i] == approx(expected_result[i]));\n      }\n    }\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <typename ValueType>\nstruct InitializeElement {\n  using simple_tags = tmpl::list<VectorTag<ValueType>,\n                                 ::Tags::FixedSource<VectorTag<ValueType>>>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const int /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), get<InitialGuess<ValueType>>(box),\n        get<Source<ValueType>>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nnamespace detail {\n\ntemplate <typename Preconditioner>\nstruct init_preconditioner_impl {\n  using type = typename Preconditioner::initialize_element;\n};\n\ntemplate <>\nstruct init_preconditioner_impl<void> {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Preconditioner>\nusing init_preconditioner =\n    typename init_preconditioner_impl<Preconditioner>::type;\n\ntemplate <typename Preconditioner>\nstruct register_preconditioner_impl {\n  using type = typename Preconditioner::register_element;\n};\n\ntemplate <>\nstruct register_preconditioner_impl<void> {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Preconditioner>\nusing register_preconditioner =\n    typename register_preconditioner_impl<Preconditioner>::type;\n\ntemplate <typename Preconditioner>\nstruct run_preconditioner_impl {\n  using type =\n      tmpl::list<ComputeOperatorAction<typename Preconditioner::fields_tag>,\n                 // [make_identity_if_skipped]\n                 typename Preconditioner::template solve<ComputeOperatorAction<\n                     typename Preconditioner::operand_tag>>,\n                 LinearSolver::Actions::MakeIdentityIfSkipped<Preconditioner>\n                 // [make_identity_if_skipped]\n                 >;\n};\n\ntemplate <>\nstruct run_preconditioner_impl<void> {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename Preconditioner>\nusing run_preconditioner =\n    typename run_preconditioner_impl<Preconditioner>::type;\n\n}  // namespace detail\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using linear_solver = typename Metavariables::linear_solver;\n  using preconditioner = typename Metavariables::preconditioner;\n  using ValueType = tt::get_complex_or_fundamental_type_t<\n      typename linear_solver::fields_tag::type>;\n\n  // In each step of the algorithm we must provide A(p). The linear solver then\n  // takes care of updating x and p, as well as the internal variables r, its\n  // magnitude and the iteration step number.\n  /// [action_list]\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<InitializeElement<ValueType>,\n                     typename linear_solver::initialize_element,\n                     ComputeOperatorAction<fields_tag<ValueType>>,\n                     detail::init_preconditioner<preconditioner>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<typename linear_solver::register_element,\n                     detail::register_preconditioner<preconditioner>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Solve,\n          tmpl::list<\n              typename linear_solver::template solve<tmpl::list<\n                  detail::run_preconditioner<preconditioner>,\n                  ComputeOperatorAction<typename linear_solver::operand_tag>>>,\n              Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<\n              TestResult<ValueType, typename linear_solver::options_group>>>>;\n  /// [action_list]\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using const_global_cache_tags =\n      tmpl::list<LinearOperator<ValueType>, Source<ValueType>,\n                 InitialGuess<ValueType>, ExpectedResult<ValueType>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_items,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& /*procs_to_ignore*/ = {}) {\n    auto& local_component = Parallel::get_parallel_component<ElementArray>(\n        *Parallel::local_branch(global_cache));\n    local_component[0].insert(global_cache, initialization_items, 0);\n    local_component.doneInserting();\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_component = Parallel::get_parallel_component<ElementArray>(\n        *Parallel::local_branch(global_cache));\n    local_component.start_phase(next_phase);\n  }\n};\n\n// After the algorithm completes we perform a cleanup phase that checks the\n// expected output file was written and deletes it.\ntemplate <bool CheckExpectedOutput, bool ExpectReductions, bool ExpectVolume>\nstruct CleanOutput {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& reductions_file_name =\n        get<observers::Tags::ReductionFileName>(box) + \".h5\";\n    if (file_system::check_if_file_exists(reductions_file_name)) {\n      file_system::rm(reductions_file_name, true);\n    } else if (CheckExpectedOutput and ExpectReductions) {\n      ERROR(\"Expected reductions file '\" << reductions_file_name\n                                         << \"' does not exist\");\n    }\n    const auto& volume_file_name =\n        get<observers::Tags::VolumeFileName>(box) + \"0.h5\";\n    if (file_system::check_if_file_exists(volume_file_name)) {\n      file_system::rm(volume_file_name, true);\n    } else if (CheckExpectedOutput and ExpectVolume) {\n      ERROR(\"Expected volume file '\" << volume_file_name << \"' does not exist\");\n    }\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <typename Metavariables, bool ExpectReductions = true,\n          bool ExpectVolume = false>\nstruct OutputCleaner {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<CleanOutput<false, ExpectReductions, ExpectVolume>>>,\n\n      Parallel::PhaseActions<\n          Parallel::Phase::Cleanup,\n          tmpl::list<CleanOutput<true, ExpectReductions, ExpectVolume>>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_component = Parallel::get_parallel_component<OutputCleaner>(\n        *Parallel::local_branch(global_cache));\n    local_component.start_phase(next_phase);\n  }\n};\n\n// [default_phase_order_array]\nstatic constexpr std::array<Parallel::Phase, 8> default_phase_order{\n    {Parallel::Phase::Initialization, Parallel::Phase::Register,\n     Parallel::Phase::UpdateSections, Parallel::Phase::CheckDomain,\n     Parallel::Phase::Solve, Parallel::Phase::Testing, Parallel::Phase::Cleanup,\n     Parallel::Phase::Exit}};\n// [default_phase_order_array]\n\nnamespace detail {\n\ntemplate <typename LinearSolver>\nstruct get_component_list_impl {\n  using type = typename LinearSolver::component_list;\n};\n\ntemplate <>\nstruct get_component_list_impl<void> {\n  using type = tmpl::list<>;\n};\n\ntemplate <typename LinearSolver>\nusing get_component_list = typename get_component_list_impl<LinearSolver>::type;\n\n}  // namespace detail\n\ntemplate <typename Metavariables>\nusing component_list = tmpl::push_back<\n    tmpl::append<\n        detail::get_component_list<typename Metavariables::linear_solver>,\n        detail::get_component_list<typename Metavariables::preconditioner>>,\n    ElementArray<Metavariables>, observers::Observer<Metavariables>,\n    observers::ObserverWriter<Metavariables>, OutputCleaner<Metavariables>>;\n\ntemplate <typename Metavariables>\nusing observed_reduction_data_tags =\n    observers::collect_reduction_data_tags<tmpl::flatten<tmpl::list<\n        typename Metavariables::linear_solver,\n        tmpl::conditional_t<\n            std::is_same_v<typename Metavariables::preconditioner, void>,\n            tmpl::list<>, typename Metavariables::preconditioner>>>>;\n\n}  // namespace LinearSolverAlgorithmTestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/LinearSolver/Multigrid/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_HEADERS\n  ${LIBRARY_HEADERS}\n  Multigrid/Helpers.hpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/LinearSolver/Multigrid/Helpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/Tags/NeighborMesh.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/Tags.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers_distributed = DistributedLinearSolverAlgorithmTestHelpers;\n\nnamespace TestHelpers::LinearSolver::multigrid {\n\nnamespace OptionTags {\nstruct LinearOperator {\n  static constexpr Options::String help =\n      \"The linear operator A to invert. The outer list corresponds to \"\n      \"multigrid levels, ordered from finest to coarsest grid. The inner list \"\n      \"corresponds to the elements in the domain. The number of columns in \"\n      \"each matrix corresponds to the number of grid points in the element, \"\n      \"and the number of rows corresponds to the total number of grid points \"\n      \"in the domain.\";\n  using type = std::vector<std::vector<blaze::DynamicMatrix<double>>>;\n};\nstruct OperatorIsMassive {\n  static constexpr Options::String help =\n      \"Whether or not the linear operator includes a mass matrix. This is \"\n      \"relevant for projection operations between grids.\";\n  using type = bool;\n};\n}  // namespace OptionTags\n\nstruct LinearOperator : db::SimpleTag {\n  using type = std::vector<std::vector<blaze::DynamicMatrix<double>>>;\n  using option_tags = tmpl::list<OptionTags::LinearOperator>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& linear_operator) {\n    return linear_operator;\n  }\n};\n\nstruct OperatorIsMassive : db::SimpleTag {\n  using type = bool;\n  using option_tags = tmpl::list<OptionTags::OperatorIsMassive>;\n  static constexpr bool pass_metavariables = false;\n  static bool create_from_options(const bool value) { return value; }\n};\n\nusing fields_tag = helpers_distributed::fields_tag;\nusing sources_tag = helpers_distributed::sources_tag;\n\nstruct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {\n public:  // Iterable action\n  using simple_tags_from_options =\n      tmpl::list<::domain::Tags::InitialExtents<1>,\n                 ::domain::Tags::InitialRefinementLevels<1>>;\n  using const_global_cache_tags =\n      tmpl::list<helpers_distributed::Source, OperatorIsMassive,\n                 elliptic::dg::Tags::Quadrature>;\n  using simple_tags =\n      tmpl::list<::domain::Tags::Element<1>, ::domain::Tags::Mesh<1>,\n                 ::domain::Tags::NeighborMesh<1>,\n                 ::domain::Tags::Coordinates<1, Frame::Inertial>,\n                 ::domain::Tags::Element<1>, fields_tag, sources_tag>;\n  using compute_tags = tmpl::list<>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<1>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Initialize geometry\n    const auto& initial_extents =\n        db::get<::domain::Tags::InitialExtents<1>>(box);\n    const auto& domain = db::get<::domain::Tags::Domain<1>>(box);\n    const auto& block = domain.blocks()[element_id.block_id()];\n    auto mesh = ::domain::create_initial_mesh(\n        initial_extents, block, element_id, Spectral::Basis::Legendre,\n        Parallel::get<elliptic::dg::Tags::Quadrature>(cache));\n    const auto logical_coords = logical_coordinates(mesh);\n    const ElementMap<1, Frame::Inertial> element_map{\n        element_id, block.stationary_map().get_clone()};\n    auto inertial_coords = element_map(logical_coords);\n    // Only needed for element ID, so don't initialize neighbors\n    Element<1> element{element_id, {}};\n    DirectionalIdMap<1, Mesh<1>> neighbor_meshes{};\n    // Initialize data\n    const size_t element_index = helpers_distributed::get_index(element_id);\n    const bool is_finest_grid = db::get<::amr::Tags::ChildIds<1>>(box).empty();\n    auto source =\n        is_finest_grid\n            ? typename sources_tag::type(\n                  gsl::at(get<helpers_distributed::Source>(box), element_index))\n            : typename sources_tag::type{};\n    const size_t num_points = mesh.number_of_grid_points();\n    auto initial_fields = typename fields_tag::type{num_points, 0.};\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), std::move(element), std::move(mesh),\n        std::move(neighbor_meshes), std::move(inertial_coords),\n        std::move(element), std::move(initial_fields), std::move(source));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n public:  // amr::protocols::Projector\n  using argument_tags =\n      tmpl::list<::domain::Tags::Mesh<1>, ::domain::Tags::Element<1>,\n                 ::domain::Tags::Domain<1>>;\n  using return_tags =\n      tmpl::list<::domain::Tags::Coordinates<1, Frame::Inertial>>;\n\n  template <typename... AmrData>\n  static void apply(\n      const gsl::not_null<tnsr::I<DataVector, 1>*> inertial_coords,\n      const Mesh<1>& mesh, const Element<1>& element, const Domain<1>& domain,\n      const AmrData&... /*amr_data*/) {\n    const auto logical_coords = logical_coordinates(mesh);\n    const auto& element_id = element.id();\n    const auto& block = domain.blocks()[element_id.block_id()];\n    const ElementMap<1, Frame::Inertial> element_map{\n        element_id, block.stationary_map().get_clone()};\n    *inertial_coords = element_map(logical_coords);\n  }\n};\n\n// The next two actions are responsible for applying the linear operator to the\n// operand data. They apply a chunk of the full matrix on each element and\n// perform a reduction to obtain the full matrix-vector product.\n\ntemplate <typename OperandTag, typename OperatorAppliedToOperandTag>\nstruct CollectOperatorAction;\n\ntemplate <typename OperandTag, typename OperatorAppliedToOperandTag>\nstruct ComputeOperatorAction {\n  using const_global_cache_tags = tmpl::list<LinearOperator>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<1>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const size_t multigrid_level = element_id.grid_index();\n    const size_t element_index = helpers_distributed::get_index(element_id);\n    const auto& operator_matrices = get<LinearOperator>(box)[multigrid_level];\n    const size_t number_of_elements = operator_matrices.size();\n    const auto& linear_operator = gsl::at(operator_matrices, element_index);\n    const size_t number_of_grid_points = linear_operator.columns();\n    const auto& operand = get<OperandTag>(box);\n\n    typename OperandTag::type operator_applied_to_operand{\n        number_of_grid_points * number_of_elements};\n    // Could use apply_matrices here once it works with `blaze::DynamicMatrix`,\n    // or `Matrix` is option-creatable\n    dgemv_('N', linear_operator.rows(), linear_operator.columns(), 1,\n           linear_operator.data(), linear_operator.spacing(), operand.data(), 1,\n           0, operator_applied_to_operand.data(), 1);\n\n    auto& section = *db::get_mutable_reference<\n        Parallel::Tags::Section<ParallelComponent, ::amr::Tags::GridIndex>>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<\n        CollectOperatorAction<OperandTag, OperatorAppliedToOperandTag>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<typename OperandTag::type, funcl::Plus<>>,\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>>{\n            operator_applied_to_operand, multigrid_level},\n        section.proxy()[element_id], section.proxy(), make_not_null(&section));\n\n    // Pause algorithm for now. The reduction will be broadcast to the next\n    // action which is responsible for restarting the algorithm.\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <typename OperandTag, typename OperatorAppliedToOperandTag>\nstruct CollectOperatorAction {\n  template <\n      typename ParallelComponent, typename DbTagsList, typename Metavariables,\n      Requires<tmpl::list_contains_v<DbTagsList, OperatorAppliedToOperandTag>> =\n          nullptr>\n  static void apply(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<1>& element_id,\n      const typename OperandTag::type& operator_applied_to_operand_global_data,\n      const size_t broadcasting_multigrid_level) {\n    // We're receiving broadcasts also from reductions over other sections. See\n    // issue: https://github.com/sxs-collaboration/spectre/issues/3220\n    const size_t multigrid_level = element_id.grid_index();\n    if (multigrid_level != broadcasting_multigrid_level) {\n      return;\n    }\n    // Copy the slice of the global result corresponding to this element into\n    // the DataBox\n    const size_t element_index = helpers_distributed::get_index(element_id);\n    const size_t number_of_grid_points =\n        get<LinearOperator>(box)[multigrid_level][0].columns();\n    db::mutate<OperatorAppliedToOperandTag>(\n        [&operator_applied_to_operand_global_data, &number_of_grid_points,\n         &element_index](auto operator_applied_to_operand) {\n          operator_applied_to_operand->initialize(number_of_grid_points);\n          for (size_t i = 0; i < number_of_grid_points; ++i) {\n            operator_applied_to_operand->data()[i] =\n                operator_applied_to_operand_global_data\n                    .data()[i + element_index * number_of_grid_points];\n          }\n        },\n        make_not_null(&box));\n    // Proceed with algorithm\n    Parallel::get_parallel_component<ParallelComponent>(cache)[element_id]\n        .perform_algorithm(true);\n  }\n};\n\n// The next two actions solve the linear problem Ax = b directly by collecting\n// the source b from all elements and applying the inverse of the operator A to\n// it.\n\ntemplate <typename FieldsTag, typename SourceTag>\nstruct SolveDirectly;\n\ntemplate <typename FieldsTag, typename SourceTag>\nstruct PrepareDirectSolve {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<1>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& section = *db::get_mutable_reference<\n        Parallel::Tags::Section<ParallelComponent, ::amr::Tags::GridIndex>>(\n        make_not_null(&box));\n    Parallel::contribute_to_reduction<SolveDirectly<FieldsTag, SourceTag>>(\n        Parallel::ReductionData<\n            Parallel::ReductionDatum<\n                std::map<ElementId<1>, typename SourceTag::type>,\n                funcl::Merge<>>,\n            Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>>{\n            std::map<ElementId<1>, typename SourceTag::type>{\n                std::make_pair(element_id, get<SourceTag>(box))},\n            element_id.grid_index()},\n        section.proxy()[element_id], section.proxy(), make_not_null(&section));\n    // Pause algorithm for now. The reduction will be broadcast to the next\n    // action which is responsible for restarting the algorithm.\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <typename FieldsTag, typename SourceTag>\nstruct SolveDirectly {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables>\n  static void apply(\n      db::DataBox<DbTagsList>& box, Parallel::GlobalCache<Metavariables>& cache,\n      const ElementId<1>& element_id,\n      const std::map<ElementId<1>, typename SourceTag::type>& source_slices,\n      const size_t broadcasting_multigrid_level) {\n    // We're receiving broadcasts also from reductions over other sections.\n    // See issue: https://github.com/sxs-collaboration/spectre/issues/3220\n    const size_t multigrid_level = element_id.grid_index();\n    if (multigrid_level != broadcasting_multigrid_level) {\n      return;\n    }\n    // Assemble full source vector and matrix\n    const auto& operator_matrices = get<LinearOperator>(box)[multigrid_level];\n    const auto full_matrix =\n        helpers_distributed::combine_matrix_slices(operator_matrices);\n    const size_t num_points = operator_matrices[0].columns();\n    const size_t total_num_points = full_matrix.rows();\n    blaze::DynamicVector<double> source(total_num_points);\n    for (const auto& [local_element_id, source_slice] : source_slices) {\n      const size_t element_index =\n          helpers_distributed::get_index(local_element_id);\n      std::copy_n(source_slice.data(), source_slice.size(),\n                  source.data() + element_index * num_points);\n    }\n    // Solve the linear system\n    const blaze::DynamicVector<double> solution =\n        blaze::solve(full_matrix, source);\n    // Store solution in the DataBox\n    db::mutate<\n        FieldsTag,\n        db::add_tag_prefix<::LinearSolver::Tags::OperatorAppliedTo, FieldsTag>>(\n        [&solution, &element_id, &num_points](\n            auto solution_slice, auto operator_applied_to_operand_slice,\n            const auto& source_slice) {\n          const size_t element_index =\n              helpers_distributed::get_index(element_id);\n          std::copy_n(solution.data() + element_index * num_points, num_points,\n                      solution_slice->data());\n          // The linear problem is solved, so Ax = b\n          *operator_applied_to_operand_slice = source_slice;\n        },\n        make_not_null(&box), db::get<SourceTag>(box));\n    // Proceed with algorithm\n    Parallel::get_parallel_component<ParallelComponent>(cache)[element_id]\n        .perform_algorithm(true);\n  }\n};\n\ntemplate <typename OptionsGroup>\nstruct TestResult {\n  using const_global_cache_tags =\n      tmpl::list<helpers_distributed::ExpectedResult>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<1>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const bool is_finest_grid = db::get<::amr::Tags::ChildIds<1>>(box).empty();\n    if (not is_finest_grid) {\n      return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n    }\n    const size_t element_index = helpers_distributed::get_index(element_id);\n    const auto& expected_result =\n        gsl::at(get<helpers_distributed::ExpectedResult>(box), element_index);\n    const auto& result = get<helpers_distributed::ScalarFieldTag>(box).get();\n    for (size_t i = 0; i < expected_result.size(); i++) {\n      SPECTRE_PARALLEL_REQUIRE(result[i] == approx(expected_result[i]));\n    }\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n}  // namespace TestHelpers::LinearSolver::multigrid\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/LinearSolver/ResidualMonitorActionsTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cmath>\n#include <converse.h>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace ResidualMonitorActionsTestHelpers {\n\nstruct CheckObservationIdTag : db::SimpleTag {\n  using type = observers::ObservationId;\n};\n\nstruct CheckSubfileNameTag : db::SimpleTag {\n  using type = std::string;\n};\n\nstruct CheckReductionNamesTag : db::SimpleTag {\n  using type = std::vector<std::string>;\n};\n\nstruct CheckReductionDataTag : db::SimpleTag {\n  using type = std::tuple<size_t, double, double>;\n};\n\nusing observer_writer_tags =\n    tmpl::list<CheckObservationIdTag, CheckSubfileNameTag,\n               CheckReductionNamesTag, CheckReductionDataTag>;\n\nstruct MockWriteReductionData {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex,\n            typename... ReductionDatums,\n            Requires<tmpl::list_contains_v<DbTagsList, CheckObservationIdTag>> =\n                nullptr>\n  static void apply(\n      db::DataBox<DbTagsList>& box,  // NOLINT\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n      const observers::ObservationId& observation_id,\n      const size_t /*sender_node_number*/, const std::string& subfile_name,\n      std::vector<std::string>&& reduction_names,\n      Parallel::ReductionData<ReductionDatums...>&& in_reduction_data) {\n    db::mutate<CheckObservationIdTag, CheckSubfileNameTag,\n               CheckReductionNamesTag, CheckReductionDataTag>(\n        [observation_id, subfile_name, reduction_names, in_reduction_data](\n            const gsl::not_null<typename CheckObservationIdTag::type*>\n                check_observation_id,\n            const gsl::not_null<typename CheckSubfileNameTag::type*>\n                check_subfile_name,\n            const gsl::not_null<typename CheckReductionNamesTag::type*>\n                check_reduction_names,\n            const gsl::not_null<typename CheckReductionDataTag::type*>\n                check_reduction_data) {\n          *check_observation_id = observation_id;\n          *check_subfile_name = subfile_name;\n          *check_reduction_names = reduction_names;\n          *check_reduction_data = in_reduction_data.data();\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct MockWriteReductionDataRow {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex, typename... Ts,\n            Requires<tmpl::list_contains_v<DbTagsList, CheckSubfileNameTag>> =\n                nullptr>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const std::string& subfile_name,\n                    std::vector<std::string>&& legend,\n                    std::tuple<Ts...>&& in_reduction_data) {\n    db::mutate<CheckSubfileNameTag, CheckReductionNamesTag,\n               CheckReductionDataTag>(\n        [subfile_name, legend, in_reduction_data](\n            const auto check_subfile_name, const auto check_reduction_names,\n            const auto check_reduction_data) {\n          *check_subfile_name = subfile_name;\n          *check_reduction_names = legend;\n          *check_reduction_data = in_reduction_data;\n        },\n        make_not_null(&box));\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<observer_writer_tags>>>>;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n\n  using replace_these_threaded_actions =\n      tmpl::list<observers::ThreadedActions::WriteReductionData,\n                 observers::ThreadedActions::WriteReductionDataRow>;\n  using with_these_threaded_actions =\n      tmpl::list<MockWriteReductionData, MockWriteReductionDataRow>;\n};\n\n}  // namespace ResidualMonitorActionsTestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/NonlinearSolver/Algorithm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers::NonlinearSolver {\n\nstruct Source : db::SimpleTag {\n  static constexpr Options::String help = \"The source b in the equation Ax=b.\";\n  using type = blaze::DynamicVector<double>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<Source>;\n  static type create_from_options(const type& option) { return option; }\n};\nstruct InitialGuess : db::SimpleTag {\n  static constexpr Options::String help = \"The initial guess for the vector x.\";\n  using type = blaze::DynamicVector<double>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<InitialGuess>;\n  static type create_from_options(const type& option) { return option; }\n};\nstruct ExpectedResult : db::SimpleTag {\n  static constexpr Options::String help = \"The solution x in the equation Ax=b\";\n  using type = blaze::DynamicVector<double>;\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<ExpectedResult>;\n  static type create_from_options(const type& option) { return option; }\n};\n\n// The vector `x` we want to solve for\nstruct VectorTag : db::SimpleTag {\n  using type = blaze::DynamicVector<double>;\n  static std::string name() { return \"VectorTag\"; }\n};\n\nusing fields_tag = VectorTag;\nusing nonlinear_source_tag = ::Tags::FixedSource<fields_tag>;\n\n// Checks for the correct solution after the algorithm has terminated.\ntemplate <typename OptionsGroup>\nstruct TestResult {\n  using const_global_cache_tags = tmpl::list<ExpectedResult>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const int /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& has_converged =\n        get<Convergence::Tags::HasConverged<OptionsGroup>>(box);\n    SPECTRE_PARALLEL_REQUIRE(has_converged);\n    SPECTRE_PARALLEL_REQUIRE(has_converged.reason() ==\n                             Convergence::Reason::AbsoluteResidual);\n    const auto& result = get<VectorTag>(box);\n    const auto& expected_result = get<ExpectedResult>(cache);\n    for (size_t i = 0; i < expected_result.size(); i++) {\n      SPECTRE_PARALLEL_REQUIRE(result[i] == approx(expected_result[i]));\n    }\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct InitializeElement {\n  using const_global_cache_tags = tmpl::list<Source, InitialGuess>;\n  using simple_tags = tmpl::list<fields_tag, nonlinear_source_tag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const int /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& b = get<Source>(cache);\n    const auto& x0 = get<InitialGuess>(cache);\n\n    ::Initialization::mutate_assign<simple_tags>(make_not_null(&box), x0, b);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using array_index = int;\n  using metavariables = Metavariables;\n\n  using nonlinear_solver = typename Metavariables::nonlinear_solver;\n  using linear_solver = typename Metavariables::linear_solver;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<InitializeElement,\n                     typename nonlinear_solver::initialize_element,\n                     typename linear_solver::initialize_element,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<typename nonlinear_solver::register_element,\n                     typename linear_solver::register_element,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Solve,\n          tmpl::list<\n              typename nonlinear_solver::template solve<\n                  typename Metavariables::template apply_nonlinear_operator<\n                      typename nonlinear_solver::operand_tag>,\n                  typename linear_solver::template solve<\n                      typename Metavariables::\n                          template apply_linearized_operator<\n                              typename linear_solver::operand_tag,\n                              typename nonlinear_solver::fields_tag>>>,\n              Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<TestResult<typename nonlinear_solver::options_group>>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_items,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& /*procs_to_ignore*/ = {}) {\n    auto& local_component = Parallel::get_parallel_component<ElementArray>(\n        *Parallel::local_branch(global_cache));\n    local_component[0].insert(global_cache, initialization_items, 0);\n    local_component.doneInserting();\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_component = Parallel::get_parallel_component<ElementArray>(\n        *Parallel::local_branch(global_cache));\n    local_component.start_phase(next_phase);\n  }\n};\n\ntemplate <typename Metavariables>\nusing OutputCleaner =\n    LinearSolverAlgorithmTestHelpers::OutputCleaner<Metavariables>;\n\nstatic constexpr std::array<Parallel::Phase, 6> default_phase_order = {\n    {Parallel::Phase::Initialization, Parallel::Phase::Register,\n     Parallel::Phase::Solve, Parallel::Phase::Testing, Parallel::Phase::Cleanup,\n     Parallel::Phase::Exit}};\n\ntemplate <typename Metavariables>\nusing component_list = tmpl::flatten<tmpl::list<\n    typename Metavariables::nonlinear_solver::component_list,\n    typename Metavariables::linear_solver::component_list,\n    ElementArray<Metavariables>, observers::Observer<Metavariables>,\n    observers::ObserverWriter<Metavariables>, OutputCleaner<Metavariables>>>;\n\ntemplate <typename Metavariables>\nusing observed_reduction_data_tags = observers::collect_reduction_data_tags<\n    tmpl::list<typename Metavariables::linear_solver,\n               typename Metavariables::nonlinear_solver>>;\n\n}  // namespace TestHelpers::NonlinearSolver\n"
  },
  {
    "path": "tests/Unit/Helpers/ParallelAlgorithms/NonlinearSolver/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"NonlinearSolverHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit\n  HEADERS\n  Algorithm.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Actions\n  Convergence\n  DataStructures\n  ErrorHandling\n  Initialization\n  LinearSolverHelpers\n  Observer\n  Options\n  Parallel\n  ParallelLinearSolver\n  ParallelNonlinearSolver\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticData/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers {\n/// \\ingroup TestingFrameworkGroup\n/// Functions for testing analytic data\nnamespace AnalyticData {\n/// Checks that tags can be retrieved both individually and all at\n/// once.\ntemplate <typename Solution, typename Coords, typename TagsList>\nvoid test_tag_retrieval(const Solution& solution, const Coords& coords,\n                        const TagsList /*meta*/) {\n  const auto vars_from_all_tags = solution.variables(coords, TagsList{});\n  const auto vars_from_all_tags_reversed =\n      solution.variables(coords, tmpl::reverse<TagsList>{});\n  tmpl::for_each<TagsList>([&](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const auto single_var = solution.variables(coords, tmpl::list<tag>{});\n    CHECK_ITERABLE_APPROX(tuples::get<tag>(single_var),\n                          tuples::get<tag>(vars_from_all_tags));\n    CHECK_ITERABLE_APPROX(tuples::get<tag>(single_var),\n                          tuples::get<tag>(vars_from_all_tags_reversed));\n  });\n}\n}  // namespace AnalyticData\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/Burgers/CheckBurgersSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Burgers/Fluxes.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace Burgers {\nnamespace Tags {\nstruct U;\n}  // namespace Tags\n}  // namespace Burgers\nnamespace Tags {\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\n/// \\endcond\n\ntemplate <typename Solution>\nvoid check_burgers_solution(const Solution& solution,\n                            const DataVector& positions,\n                            const std::vector<double>& times) {\n  static_assert(is_analytic_solution_v<Solution>,\n                \"Solution was not derived from AnalyticSolution\");\n  // Check that different functions are consistent.\n  const tnsr::I<DataVector, 1> positions_tnsr{{{positions}}};\n  for (const double time : times) {\n    const auto value = solution.u(positions_tnsr, time);\n    const auto time_deriv = solution.du_dt(positions_tnsr, time);\n    CHECK(get<Burgers::Tags::U>(solution.variables(\n              positions_tnsr, time, tmpl::list<Burgers::Tags::U>{})) == value);\n    CHECK(get<Tags::dt<Burgers::Tags::U>>(solution.variables(\n              positions_tnsr, time,\n              tmpl::list<Tags::dt<Burgers::Tags::U>>{})) ==\n          time_deriv);\n    for (size_t point = 0; point < positions.size(); ++point) {\n      const tnsr::I<double, 1> xp{{{positions[point]}}};\n      const double up = get(value)[point];\n      const double dtup = get(time_deriv)[point];\n      CHECK(get(solution.u(xp, time)) == approx(up));\n      CHECK(get(solution.du_dt(xp, time)) == approx(dtup));\n      // Check that the time derivative is the derivative of the\n      // value.\n      const auto deriv_approx = Approx::custom().epsilon(1.e-10);\n      CHECK(numerical_derivative(\n                [&solution, &xp](const std::array<double, 1>& t) {\n                  return std::array<double, 1>{{get(solution.u(xp, t[0]))}};\n                },\n                std::array<double, 1>{{time}}, 0,\n                1e-4)[0] == deriv_approx(dtup));\n      // Check that the Burgers equation is satisfied.\n      CHECK(numerical_derivative(\n                [&solution, &time](const std::array<double, 1>& x) {\n                  tnsr::I<DataVector, 1> flux{{{DataVector(1)}}};\n                  Burgers::Fluxes::apply(\n                      &flux,\n                      solution.u(tnsr::I<DataVector, 1>{{{{x[0]}}}}, time));\n                  return std::array<double, 1>{{get<0>(flux)[0]}};\n                },\n                std::array<double, 1>{{get<0>(xp)}}, 0,\n                1e-4)[0] == deriv_approx(-dtup));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Elliptic/Systems/GetSourcesComputer.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Tuple.hpp\"\n\nnamespace FirstOrderEllipticSolutionsTestHelpers {\nnamespace detail {\nnamespace Tags {\n\ntemplate <typename Tag>\nstruct OperatorAppliedTo : db::SimpleTag, db::PrefixTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n};\n\n}  // namespace Tags\n\ntemplate <typename System, typename SolutionType, typename... Maps,\n          typename... FluxesArgs, typename... SourcesArgs,\n          typename... PrimalFields, typename... PrimalFluxes>\nvoid verify_solution_impl(\n    const SolutionType& solution, const Mesh<System::volume_dim>& mesh,\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial, Maps...>\n        coord_map,\n    const double tolerance, const std::tuple<FluxesArgs...>& fluxes_args,\n    const std::tuple<SourcesArgs...>& sources_args,\n    tmpl::list<PrimalFields...> /*meta*/,\n    tmpl::list<PrimalFluxes...> /*meta*/) {\n  static constexpr size_t Dim = System::volume_dim;\n  using all_fields = tmpl::list<PrimalFields...>;\n  using deriv_fields = tmpl::list<\n      ::Tags::deriv<PrimalFields, tmpl::size_t<Dim>, Frame::Inertial>...>;\n  using all_fluxes = tmpl::list<PrimalFluxes...>;\n  using FluxesComputer = typename System::fluxes_computer;\n  using SourcesComputer = typename System::sources_computer;\n  CAPTURE(mesh);\n\n  const size_t num_points = mesh.number_of_grid_points();\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto inertial_coords = coord_map(logical_coords);\n  const auto inv_jacobian = coord_map.inv_jacobian(logical_coords);\n  const auto solution_fields = variables_from_tagged_tuple(solution.variables(\n      inertial_coords, tmpl::append<all_fields, deriv_fields>{}));\n\n  {\n    INFO(\"Test partial derivatives\");\n    const auto numeric_derivs =\n        partial_derivatives<all_fields>(solution_fields, mesh, inv_jacobian);\n    Approx custom_approx = Approx::custom().epsilon(1.).scale(tolerance);\n    tmpl::for_each<deriv_fields>([&solution_fields, &numeric_derivs,\n                                  &custom_approx](auto deriv_field_tag_v) {\n      using deriv_field_tag = tmpl::type_from<decltype(deriv_field_tag_v)>;\n      const auto& numeric_deriv = get<deriv_field_tag>(numeric_derivs);\n      const auto& analytic_deriv = get<deriv_field_tag>(solution_fields);\n      CHECK_ITERABLE_CUSTOM_APPROX(numeric_deriv, analytic_deriv,\n                                   custom_approx);\n    });\n  }\n\n  // Apply operator to solution fields: -div(F) + S = f\n  Variables<all_fluxes> fluxes{num_points};\n  std::apply(\n      [&fluxes, &solution_fields](const auto&... expanded_fluxes_args) {\n        FluxesComputer::apply(\n            make_not_null(&get<PrimalFluxes>(fluxes))...,\n            expanded_fluxes_args..., get<PrimalFields>(solution_fields)...,\n            get<::Tags::deriv<PrimalFields, tmpl::size_t<Dim>,\n                              Frame::Inertial>>(solution_fields)...);\n      },\n      fluxes_args);\n  Variables<db::wrap_tags_in<Tags::OperatorAppliedTo, all_fields>>\n      operator_applied_to_fields{num_points};\n  divergence(make_not_null(&operator_applied_to_fields), fluxes, mesh,\n             inv_jacobian);\n  operator_applied_to_fields *= -1.;\n  if constexpr (not std::is_same_v<SourcesComputer, void>) {\n    std::apply(\n        [&operator_applied_to_fields, &solution_fields,\n         &fluxes](const auto&... expanded_sources_args) {\n          SourcesComputer::apply(\n              make_not_null(&get<Tags::OperatorAppliedTo<PrimalFields>>(\n                  operator_applied_to_fields))...,\n              expanded_sources_args..., get<PrimalFields>(solution_fields)...,\n              get<::Tags::deriv<PrimalFields, tmpl::size_t<Dim>,\n              Frame::Inertial>>(solution_fields)...,\n              get<PrimalFluxes>(fluxes)...);\n        },\n        sources_args);\n  }\n\n  // Set RHS to the solution's fixed sources f(x)\n  Variables<db::wrap_tags_in<::Tags::FixedSource, all_fields>> fixed_sources{\n      num_points};\n  fixed_sources.assign_subset(solution.variables(\n      inertial_coords, tmpl::list<::Tags::FixedSource<PrimalFields>...>{}));\n\n  // Check error norms against the given tolerance\n  tmpl::for_each<all_fields>([&operator_applied_to_fields, &fixed_sources,\n                              &tolerance](auto field_tag_v) {\n    using field_tag = tmpl::type_from<decltype(field_tag_v)>;\n    const auto& operator_applied_to_field =\n        get<Tags::OperatorAppliedTo<field_tag>>(operator_applied_to_fields);\n    const auto& fixed_source =\n        get<::Tags::FixedSource<field_tag>>(fixed_sources);\n    double l2_error_square = 0.;\n    double linf_error = 0.;\n    for (size_t i = 0; i < operator_applied_to_field.size(); ++i) {\n      const auto error = abs(operator_applied_to_field[i] - fixed_source[i]);\n      l2_error_square += alg::accumulate(square(error), 0.) / error.size();\n      const double component_linf_error = *alg::max_element(error);\n      if (component_linf_error > linf_error) {\n        linf_error = component_linf_error;\n      }\n    }\n    const double l2_error =\n        sqrt(l2_error_square / operator_applied_to_field.size());\n    CAPTURE(db::tag_name<field_tag>());\n    CAPTURE(l2_error);\n    CAPTURE(linf_error);\n    CHECK(l2_error < tolerance);\n    CHECK(linf_error < tolerance);\n  });\n}\n\n}  // namespace detail\n\n/// \\ingroup TestingFrameworkGroup\n/// Test that the `solution` numerically solves the `System` on the given grid\n/// for the given tolerance\n/// @{\ntemplate <typename System, typename SolutionType, typename... Maps,\n          typename... FluxesArgs, typename... SourcesArgs>\nvoid verify_solution(\n    const SolutionType& solution, const Mesh<System::volume_dim>& mesh,\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial, Maps...>\n        coord_map,\n    const double tolerance, const std::tuple<FluxesArgs...>& fluxes_args,\n    const std::tuple<SourcesArgs...>& sources_args = std::tuple<>{}) {\n  detail::verify_solution_impl<System>(\n      solution, mesh, coord_map, tolerance, fluxes_args, sources_args,\n      typename System::primal_fields{}, typename System::primal_fluxes{});\n}\n\ntemplate <typename System, typename SolutionType, typename... Maps>\nvoid verify_solution(\n    const SolutionType& solution, const Mesh<System::volume_dim>& mesh,\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial, Maps...>\n        coord_map,\n    const double tolerance) {\n  using argument_tags = tmpl::remove_duplicates<\n      tmpl::append<typename System::fluxes_computer::argument_tags,\n                   elliptic::get_sources_argument_tags<System, false>>>;\n  const auto background_fields = [&solution, &mesh, &coord_map]() {\n    if constexpr (tmpl::size<argument_tags>::value > 0) {\n      const auto logical_coords = logical_coordinates(mesh);\n      const auto inertial_coords = coord_map(logical_coords);\n      const auto inv_jacobian = coord_map.inv_jacobian(logical_coords);\n      return solution.variables(inertial_coords, mesh, inv_jacobian,\n                                argument_tags{});\n    } else {\n      (void)solution;\n      (void)mesh;\n      (void)coord_map;\n      return tuples::TaggedTuple<>{};\n    }\n  }();\n  const auto get_items = [](const auto&... args) {\n    return std::forward_as_tuple(args...);\n  };\n  verify_solution<System>(\n      solution, mesh, coord_map, tolerance,\n      tuples::apply<typename System::fluxes_computer::argument_tags>(\n          get_items, background_fields),\n      tuples::apply<elliptic::get_sources_argument_tags<System, false>>(\n          get_items, background_fields));\n}\n/// @}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * Test that the `solution` numerically solves the `System` on the given grid\n * and that the discretization error decreases as expected for a smooth\n * function.\n *\n * \\details We expect exponential convergence for a smooth solution, so the\n * tolerance is computed as\n *\n * \\f{equation}\n * C_1 \\exp{\\left(-C_2 * N_\\mathrm{points}\\right)}\n * \\f}\n *\n * where \\f$C_1\\f$ is the `tolerance_offset`, \\f$C_2\\f$ is the\n * `tolerance_scaling` and \\f$N_\\mathrm{points}\\f$ is the number of grid points\n * per dimension.\n */\ntemplate <typename System, typename SolutionType,\n          size_t Dim = System::volume_dim, typename... Maps,\n          typename PackageFluxesArgs>\nvoid verify_smooth_solution(\n    const SolutionType& solution,\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                Maps...>& coord_map,\n    const double tolerance_offset, const double tolerance_scaling,\n    PackageFluxesArgs&& package_fluxes_args) {\n  INFO(\"Verify smooth solution\");\n  const size_t max_points = std::min(\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>, 12_st);\n  for (size_t num_points = Spectral::minimum_number_of_points<\n           Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>;\n       num_points <= max_points; num_points++) {\n    CAPTURE(num_points);\n    const double tolerance =\n        tolerance_offset * exp(-tolerance_scaling * num_points);\n    CAPTURE(tolerance);\n    const Mesh<Dim> mesh{num_points, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto};\n    FirstOrderEllipticSolutionsTestHelpers::verify_solution<System>(\n        solution, mesh, coord_map, tolerance, package_fluxes_args(mesh));\n  }\n}\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * Test that the `solution` numerically solves the `System` on the given grid\n * and that the discretization error decreases as a power law.\n *\n * \\details The tolerance is computed as\n *\n * \\f{equation}\n * C \\left(N_\\mathrm{points}\\right)^{-p}\n * \\f}\n *\n * where \\f$C\\f$ is the `tolerance_offset`, \\f$p\\f$ is the `tolerance_pow` and\n * \\f$N_\\mathrm{points}\\f$ is the number of grid points per dimension.\n */\ntemplate <typename System, typename SolutionType,\n          size_t Dim = System::volume_dim, typename... Maps>\nvoid verify_solution_with_power_law_convergence(\n    const SolutionType& solution,\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                Maps...>& coord_map,\n    const double tolerance_offset, const double tolerance_pow) {\n  INFO(\"Verify solution with power-law convergence\");\n  const size_t max_points = std::min(\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>, 12_st);\n  for (size_t num_points = Spectral::minimum_number_of_points<\n           Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>;\n       num_points <= max_points; num_points++) {\n    CAPTURE(num_points);\n    const double tolerance = tolerance_offset * pow(num_points, -tolerance_pow);\n    CAPTURE(tolerance);\n    FirstOrderEllipticSolutionsTestHelpers::verify_solution<System>(\n        solution,\n        Mesh<Dim>{num_points, Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto},\n        coord_map, tolerance);\n  }\n}\n\n}  // namespace FirstOrderEllipticSolutionsTestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"GrSolutionsTestHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  CheckWrappedGrConsistency.hpp\n  VerifyGrSolution.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  CoordinateMaps\n  DataStructures\n  Domain\n  GeneralizedHarmonic\n  GeneralRelativity\n  GeneralRelativitySolutions\n  LinearOperators\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/CheckWrappedGrConsistency.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <typename WrappedGrSolution, typename ArgumentSolution, size_t Dim>\nvoid check_wrapped_gr_solution_consistency(\n    const WrappedGrSolution& wrapped_solution,\n    const ArgumentSolution& argument_solution,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& x, const double t) {\n  using argument_solution_tags =\n      typename ArgumentSolution::template tags<DataVector>;\n  const auto wrapped_vars =\n      wrapped_solution.variables(x, t, argument_solution_tags{});\n  const auto argument_vars =\n      argument_solution.variables(x, t, argument_solution_tags{});\n  tmpl::for_each<argument_solution_tags>(\n      [&wrapped_vars, &argument_vars](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        CHECK(get<tag>(wrapped_vars) == get<tag>(argument_vars));\n      });\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Constraints.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/DuDtTempTags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/AnalyticChristoffel.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/GaugeSourceFunctions/Gauges.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/TimeDerivative.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\n/// \\cond\n\n/// \\endcond\n\nnamespace TestHelpers {\n/// \\ingroup TestingFrameworkGroup\n/// Functions for testing GR analytic solutions\nnamespace VerifyGrSolution {\n\n/// Determines if the given `solution` is a time-independent solution\n/// of the Einstein equations. Uses numerical derivatives to compute\n/// the solution, on a grid extending from `lower_bound` to `upper_bound`\n/// with `grid_size_each_dimension` points in each dimension.\n/// The right-hand side of the Einstein equations must be zero within\n/// `error_tolerance`\ntemplate <typename Solution>\nvoid verify_time_independent_einstein_solution(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound, const double error_tolerance) {\n  static_assert(is_analytic_solution_v<Solution>,\n                \"Solution was not derived from AnalyticSolution\");\n  // Shorter names for tags.\n  using SpacetimeMetric = gr::Tags::SpacetimeMetric<DataVector, 3>;\n  using Pi = ::gh::Tags::Pi<DataVector, 3>;\n  using Phi = ::gh::Tags::Phi<DataVector, 3>;\n  using VariablesTags = tmpl::list<SpacetimeMetric, Pi, Phi>;\n  const gh::Solutions::WrappedGr<Solution> gh_solution{solution};\n  const std::unique_ptr<gh::gauges::GaugeCondition> gauge_condition =\n      std::make_unique<gh::gauges::AnalyticChristoffel>(\n          gh_solution.get_clone());\n\n  // Set up grid\n  const size_t data_size = pow<3>(grid_size_each_dimension);\n  const Mesh<3> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine3D =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  const auto inverse_jacobian = coord_map.inv_jacobian(x_logical);\n  const double time = 1.3;  // Arbitrary time for time-independent solution.\n\n  // Evaluate analytic solution\n  const auto vars = gh_solution.variables(\n      x, time, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n  const auto& d_shift =\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(vars);\n  const auto& g = get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& dt_g =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars);\n  const auto& d_g = get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>>(vars);\n\n  // Check those quantities that should vanish identically.\n  CHECK(get(dt_lapse) == make_with_value<DataVector>(x, 0.));\n  CHECK(dt_shift ==\n        make_with_value<cpp20::remove_cvref_t<decltype(dt_shift)>>(x, 0.));\n  CHECK(dt_g == make_with_value<cpp20::remove_cvref_t<decltype(dt_g)>>(x, 0.));\n\n  // Need upper spatial metric for many things below.\n  const auto upper_spatial_metric = determinant_and_inverse(g).second;\n\n  // Compute generalized harmonic quantities.  Put them in a Variables\n  // so that we can take numerical derivatives of them.\n  // Also put gauge_function into this list since we need a numerical\n  // derivative of it too.\n  Variables<VariablesTags> gh_vars(data_size);\n  gh_vars.assign_subset(gh_solution.variables(x, time, VariablesTags{}));\n  const auto& [spacetime_metric, pi, phi] = gh_vars;\n\n  // Compute numerical derivatives of spacetime_metric,pi,phi,H.\n  // Normally one should not take numerical derivatives of H for\n  // plugging into the RHS of the generalized harmonic equations, but\n  // here this is just a test.\n  const auto gh_derivs =\n      partial_derivatives<VariablesTags, VariablesTags, 3, Frame::Inertial>(\n          gh_vars, mesh, inverse_jacobian);\n  const auto& d_spacetime_metric =\n      get<Tags::deriv<SpacetimeMetric, tmpl::size_t<3>, Frame::Inertial>>(\n          gh_derivs);\n  const auto& d_pi =\n      get<Tags::deriv<Pi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n  const auto& d_phi =\n      get<Tags::deriv<Phi, tmpl::size_t<3>, Frame::Inertial>>(gh_derivs);\n\n  Approx numerical_approx =\n      Approx::custom().epsilon(error_tolerance).scale(1.0);\n\n  // Test 3-index constraint\n  CHECK_ITERABLE_CUSTOM_APPROX(d_spacetime_metric, phi, numerical_approx);\n\n  // Compute analytic derivatives of spacetime_metric, for use in computing\n  // christoffel symbols.\n  auto d4_spacetime_metric = make_with_value<tnsr::abb<DataVector, 3>>(x, 0.0);\n  for (size_t b = 0; b < 4; ++b) {\n    for (size_t c = b; c < 4; ++c) {  // symmetry\n      d4_spacetime_metric.get(0, b, c) = -get(lapse) * pi.get(b, c);\n      for (size_t k = 0; k < 3; ++k) {\n        d4_spacetime_metric.get(0, b, c) += shift.get(k) * phi.get(k, b, c);\n        d4_spacetime_metric.get(k + 1, b, c) = phi.get(k, b, c);\n      }\n    }\n  }\n\n  // Compute derived spacetime quantities\n  const auto upper_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, upper_spatial_metric);\n  const auto christoffel_first_kind =\n      gr::christoffel_first_kind(d4_spacetime_metric);\n  const auto christoffel_second_kind = raise_or_lower_first_index(\n      christoffel_first_kind, upper_spacetime_metric);\n  const auto trace_christoffel_first_kind =\n      trace_last_indices(christoffel_first_kind, upper_spacetime_metric);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n\n  // Test ADM evolution equation gives zero\n  auto dt_g_adm = make_with_value<tnsr::ii<DataVector, 3>>(x, 0.0);\n  {\n    auto ex_curv = gr::extrinsic_curvature(lapse, shift, d_shift, g, dt_g, d_g);\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {  // Symmetry\n        dt_g_adm.get(i, j) -= 2.0 * get(lapse) * ex_curv.get(i, j);\n        for (size_t k = 0; k < 3; ++k) {\n          dt_g_adm.get(i, j) += shift.get(k) * d_g.get(k, i, j) +\n                                g.get(k, i) * d_shift.get(j, k) +\n                                g.get(k, j) * d_shift.get(i, k);\n        }\n      }\n    }\n    CHECK_ITERABLE_APPROX(dt_g_adm,\n                          make_with_value<decltype(dt_g_adm)>(x, 0.0));\n  }\n\n  // Constraint-damping parameters: Set to arbitrary values.\n  // gamma = 0 (for all gammas) and gamma1 = -1 are special because,\n  // they zero out various terms in the equations, so don't choose those.\n  const auto gamma0 = make_with_value<Scalar<DataVector>>(x, 1.0);\n  const auto gamma1 = make_with_value<Scalar<DataVector>>(x, 1.0);\n  const auto gamma2 = make_with_value<Scalar<DataVector>>(x, 1.0);\n\n  // Compute RHS of generalized harmonic Einstein equations.\n  auto dt_spacetime_metric =\n      make_with_value<tnsr::aa<DataVector, 3, Frame::Inertial>>(x, 0.0);\n  auto dt_pi =\n      make_with_value<tnsr::aa<DataVector, 3, Frame::Inertial>>(x, 0.0);\n  auto dt_phi =\n      make_with_value<tnsr::iaa<DataVector, 3, Frame::Inertial>>(x, 0.0);\n  Variables<tmpl::list<\n      gh::Tags::ConstraintGamma1, gh::Tags::ConstraintGamma2,\n      gh::Tags::GaugeH<DataVector, 3>,\n      gh::Tags::SpacetimeDerivGaugeH<DataVector, 3>, gh::Tags::Gamma1Gamma2,\n      gh::Tags::HalfPiTwoNormals, gh::Tags::NormalDotOneIndexConstraint,\n      gh::Tags::Gamma1Plus1, gh::Tags::PiOneNormal<3>,\n      gh::Tags::GaugeConstraint<DataVector, 3>, gh::Tags::HalfPhiTwoNormals<3>,\n      gh::Tags::ShiftDotThreeIndexConstraint<3>,\n      gh::Tags::MeshVelocityDotThreeIndexConstraint<3>,\n      gh::Tags::PhiOneNormal<3>, gh::Tags::PiSecondIndexUp<3>,\n      gh::Tags::ThreeIndexConstraint<DataVector, 3>,\n      gh::Tags::PhiFirstIndexUp<3>, gh::Tags::PhiThirdIndexUp<3>,\n      gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<3>,\n      gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::DetSpatialMetric<DataVector>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      gr::Tags::InverseSpacetimeMetric<DataVector, 3>,\n      gr::Tags::SpacetimeChristoffelFirstKind<DataVector, 3>,\n      gr::Tags::SpacetimeChristoffelSecondKind<DataVector, 3>,\n      gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, 3>,\n      gr::Tags::SpacetimeNormalVector<DataVector, 3>,\n      gr::Tags::DerivativesOfSpacetimeMetric<DataVector, 3>>>\n      buffer(mesh.number_of_grid_points());\n\n  gh::TimeDerivative<gh::Solutions::all_solutions<3>, 3>::apply(\n      make_not_null(&dt_spacetime_metric), make_not_null(&dt_pi),\n      make_not_null(&dt_phi),\n      make_not_null(&get<gh::Tags::ConstraintGamma1>(buffer)),\n      make_not_null(&get<gh::Tags::ConstraintGamma2>(buffer)),\n      make_not_null(&get<gh::Tags::GaugeH<DataVector, 3>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeDerivGaugeH<DataVector, 3>>(buffer)),\n      make_not_null(&get<gh::Tags::Gamma1Gamma2>(buffer)),\n      make_not_null(&get<gh::Tags::HalfPiTwoNormals>(buffer)),\n      make_not_null(&get<gh::Tags::NormalDotOneIndexConstraint>(buffer)),\n      make_not_null(&get<gh::Tags::Gamma1Plus1>(buffer)),\n      make_not_null(&get<gh::Tags::PiOneNormal<3>>(buffer)),\n      make_not_null(&get<gh::Tags::GaugeConstraint<DataVector, 3>>(buffer)),\n      make_not_null(&get<gh::Tags::HalfPhiTwoNormals<3>>(buffer)),\n      make_not_null(&get<gh::Tags::ShiftDotThreeIndexConstraint<3>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::MeshVelocityDotThreeIndexConstraint<3>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiOneNormal<3>>(buffer)),\n      make_not_null(&get<gh::Tags::PiSecondIndexUp<3>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::ThreeIndexConstraint<DataVector, 3>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiFirstIndexUp<3>>(buffer)),\n      make_not_null(&get<gh::Tags::PhiThirdIndexUp<3>>(buffer)),\n      make_not_null(\n          &get<gh::Tags::SpacetimeChristoffelFirstKindThirdIndexUp<3>>(buffer)),\n      make_not_null(&get<gr::Tags::Lapse<DataVector>>(buffer)),\n      make_not_null(&get<gr::Tags::Shift<DataVector, 3>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(buffer)),\n      make_not_null(&get<gr::Tags::DetSpatialMetric<DataVector>>(buffer)),\n      make_not_null(&get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::InverseSpacetimeMetric<DataVector, 3>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelFirstKind<DataVector, 3>>(buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeChristoffelSecondKind<DataVector, 3>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, 3>>(\n              buffer)),\n      make_not_null(\n          &get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(buffer)),\n      d_spacetime_metric, d_pi, d_phi, spacetime_metric, pi, phi, gamma0,\n      gamma1, gamma2, *gauge_condition, mesh, time, x, inverse_jacobian,\n      std::nullopt);\n\n  const auto normal_one_form =\n      gr::spacetime_normal_one_form<DataVector, 3, Frame::Inertial>(\n          get<gr::Tags::Lapse<DataVector>>(buffer));\n  const auto gauge_constraint = gh::gauge_constraint(\n      get<gh::Tags::GaugeH<DataVector, 3>>(buffer), normal_one_form,\n      get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(buffer),\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(buffer),\n      get<gr::Tags::InverseSpacetimeMetric<DataVector, 3>>(buffer), pi, phi);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      gauge_constraint, make_with_value<decltype(gauge_constraint)>(x, 0.0),\n      numerical_approx);\n\n  // Make sure the RHS is zero.\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      dt_spacetime_metric,\n      make_with_value<decltype(dt_spacetime_metric)>(x, 0.0), numerical_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(dt_pi, make_with_value<decltype(dt_pi)>(x, 0.0),\n                               numerical_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      dt_phi, make_with_value<decltype(dt_phi)>(x, 0.0), numerical_approx);\n}\n\nnamespace detail {\ntemplate <typename Tag, typename Frame>\nusing deriv = Tags::deriv<Tag, tmpl::size_t<3>, Frame>;\n\ntemplate <typename Tag, typename Solution, typename Frame>\nauto time_derivative(const Solution& solution,\n                     const tnsr::I<double, 3, Frame>& x, const double time,\n                     const double dt) {\n  typename Tag::type result{};\n  for (auto it = result.begin(); it != result.end(); ++it) {\n    const auto index = result.get_tensor_index(it);\n    *it = numerical_derivative(\n        [&index, &solution, &x](const std::array<double, 1>& t_array) {\n          return std::array<double, 1>{\n              {get<Tag>(solution.variables(x, t_array[0], tmpl::list<Tag>{}))\n                   .get(index)}};\n        },\n        std::array<double, 1>{{time}}, 0, dt)[0];\n  }\n  return result;\n}\n\ntemplate <typename Tag, typename Solution, typename Frame>\nauto space_derivative(const Solution& solution,\n                      const tnsr::I<double, 3, Frame>& x, const double time,\n                      const double dx) {\n  typename deriv<Tag, Frame>::type result{};\n  for (auto it = result.begin(); it != result.end(); ++it) {\n    const auto index = result.get_tensor_index(it);\n    *it = numerical_derivative(\n        [&index, &solution, &time, &x](const std::array<double, 1>& offset) {\n          auto position = x;\n          position.get(index[0]) += offset[0];\n          return std::array<double, 1>{\n              {get<Tag>(solution.variables(position, time, tmpl::list<Tag>{}))\n                   .get(all_but_specified_element_of(index, 0))}};\n        },\n        std::array<double, 1>{{0.0}}, 0, dx)[0];\n  }\n  return result;\n}\n}  // namespace detail\n\n/// Check the consistency of dependent quantities returned by a\n/// solution.  This includes checking pointwise relations such as\n/// consistency of the metric and inverse and comparing returned and\n/// numerical derivatives.\ntemplate <typename Solution, typename Frame>\nvoid verify_consistency(const Solution& solution, const double time,\n                        const tnsr::I<double, 3, Frame>& position,\n                        const double derivative_delta,\n                        const double derivative_tolerance) {\n  using Lapse = gr::Tags::Lapse<double>;\n  using Shift = gr::Tags::Shift<double, 3, Frame>;\n  using SpatialMetric = gr::Tags::SpatialMetric<double, 3, Frame>;\n  using SqrtDetSpatialMetric = gr::Tags::SqrtDetSpatialMetric<double>;\n  using InverseSpatialMetric = gr::Tags::InverseSpatialMetric<double, 3, Frame>;\n  using ExtrinsicCurvature = gr::Tags::ExtrinsicCurvature<double, 3, Frame>;\n  using tags =\n      tmpl::list<SpatialMetric, SqrtDetSpatialMetric, InverseSpatialMetric,\n                 ExtrinsicCurvature, Lapse, Shift, detail::deriv<Shift, Frame>,\n                 Tags::dt<SpatialMetric>, detail::deriv<SpatialMetric, Frame>,\n                 Tags::dt<Lapse>, Tags::dt<Shift>, detail::deriv<Lapse, Frame>>;\n\n  auto derivative_approx = approx;\n  derivative_approx.epsilon(derivative_tolerance);\n\n  const auto vars = solution.variables(position, time, tags{});\n\n  const auto numerical_metric_det_and_inverse =\n      determinant_and_inverse(get<SpatialMetric>(vars));\n  CHECK_ITERABLE_APPROX(get(get<SqrtDetSpatialMetric>(vars)),\n                        sqrt(get(numerical_metric_det_and_inverse.first)));\n  CHECK_ITERABLE_APPROX(get<InverseSpatialMetric>(vars),\n                        numerical_metric_det_and_inverse.second);\n\n  CHECK_ITERABLE_APPROX(\n      get<ExtrinsicCurvature>(vars),\n      gr::extrinsic_curvature(get<Lapse>(vars), get<Shift>(vars),\n                              get<detail::deriv<Shift, Frame>>(vars),\n                              get<SpatialMetric>(vars),\n                              get<Tags::dt<SpatialMetric>>(vars),\n                              get<detail::deriv<SpatialMetric, Frame>>(vars)));\n\n  CHECK_ITERABLE_CUSTOM_APPROX(get<Tags::dt<Lapse>>(vars),\n                               detail::time_derivative<Lapse>(\n                                   solution, position, time, derivative_delta),\n                               derivative_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get<Tags::dt<Shift>>(vars),\n                               detail::time_derivative<Shift>(\n                                   solution, position, time, derivative_delta),\n                               derivative_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get<Tags::dt<SpatialMetric>>(vars),\n                               detail::time_derivative<SpatialMetric>(\n                                   solution, position, time, derivative_delta),\n                               derivative_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      SINGLE_ARG(get<detail::deriv<Lapse, Frame>>(vars)),\n      detail::space_derivative<Lapse>(solution, position, time,\n                                      derivative_delta),\n      derivative_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      SINGLE_ARG(get<detail::deriv<Shift, Frame>>(vars)),\n      detail::space_derivative<Shift>(solution, position, time,\n                                      derivative_delta),\n      derivative_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      SINGLE_ARG(get<detail::deriv<SpatialMetric, Frame>>(vars)),\n      detail::space_derivative<SpatialMetric>(solution, position, time,\n                                              derivative_delta),\n      derivative_approx);\n}\n\n/// Check the consistency of quantities dependent on other metric quantities\n/// returned by a solution.  This includes checking pointwise relations such as\n/// consistency of the metric and inverse and comparing returned and\n/// numerical derivatives.\ntemplate <typename Solution, typename Frame>\nvoid verify_spatial_consistency(const Solution& solution, const double time,\n                                const tnsr::I<double, 3, Frame>& position,\n                                const double derivative_delta,\n                                const double derivative_tolerance) {\n  using Lapse = gr::Tags::Lapse<double>;\n  using Shift = gr::Tags::Shift<double, 3, Frame>;\n  using SpatialMetric = gr::Tags::SpatialMetric<double, 3, Frame>;\n  using SqrtDetSpatialMetric = gr::Tags::SqrtDetSpatialMetric<double>;\n  using InverseSpatialMetric = gr::Tags::InverseSpatialMetric<double, 3, Frame>;\n  using ExtrinsicCurvature = gr::Tags::ExtrinsicCurvature<double, 3, Frame>;\n  using tags =\n      tmpl::list<SpatialMetric, SqrtDetSpatialMetric, InverseSpatialMetric,\n                 ExtrinsicCurvature, Lapse, Shift, detail::deriv<Shift, Frame>,\n                 Tags::dt<SpatialMetric>, detail::deriv<SpatialMetric, Frame>,\n                 Tags::dt<Lapse>, Tags::dt<Shift>, detail::deriv<Lapse, Frame>>;\n\n  auto derivative_approx = approx;\n  derivative_approx.epsilon(derivative_tolerance);\n\n  const auto vars = solution.variables(position, time, tags{});\n\n  const auto numerical_metric_det_and_inverse =\n      determinant_and_inverse(get<SpatialMetric>(vars));\n  CHECK_ITERABLE_APPROX(get(get<SqrtDetSpatialMetric>(vars)),\n                        sqrt(get(numerical_metric_det_and_inverse.first)));\n  CHECK_ITERABLE_APPROX(get<InverseSpatialMetric>(vars),\n                        numerical_metric_det_and_inverse.second);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      SINGLE_ARG(get<detail::deriv<Lapse, Frame>>(vars)),\n      detail::space_derivative<Lapse>(solution, position, time,\n                                      derivative_delta),\n      derivative_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      SINGLE_ARG(get<detail::deriv<Shift, Frame>>(vars)),\n      detail::space_derivative<Shift>(solution, position, time,\n                                      derivative_delta),\n      derivative_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      SINGLE_ARG(get<detail::deriv<SpatialMetric, Frame>>(vars)),\n      detail::space_derivative<SpatialMetric>(solution, position, time,\n                                              derivative_delta),\n      derivative_approx);\n}\n\n}  // namespace VerifyGrSolution\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/GrMhd/VerifyGrMhdSolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/ConservativeFromPrimitive.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Fluxes.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/Sources.hpp\"\n#include \"Evolution/Systems/GrMhd/ValenciaDivClean/System.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace VerifyGrMhdSolution_detail {\n\nusing valencia_tags = tmpl::list<grmhd::ValenciaDivClean::Tags::TildeD,\n                                 grmhd::ValenciaDivClean::Tags::TildeYe,\n                                 grmhd::ValenciaDivClean::Tags::TildeTau,\n                                 grmhd::ValenciaDivClean::Tags::TildeS<>,\n                                 grmhd::ValenciaDivClean::Tags::TildeB<>,\n                                 grmhd::ValenciaDivClean::Tags::TildePhi>;\n\n// compute the time derivative using a centered sixth-order stencil\ntemplate <typename Solution>\nVariables<valencia_tags> numerical_dt(\n    const Solution& solution, const tnsr::I<DataVector, 3, Frame::Inertial>& x,\n    const double time, const double delta_time) {\n  std::array<double, 6> six_times{\n      {time - 3.0 * delta_time, time - 2.0 * delta_time, time - delta_time,\n       time + delta_time, time + 2.0 * delta_time, time + 3.0 * delta_time}};\n  const size_t number_of_points = get<0>(x).size();\n  auto solution_at_six_times =\n      make_array<6>(Variables<valencia_tags>(number_of_points));\n\n  using solution_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificEnthalpy<DataVector>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>>;\n\n  for (size_t i = 0; i < 6; ++i) {\n    const auto vars =\n        solution.variables(x, gsl::at(six_times, i), solution_tags{});\n\n    const auto& rest_mass_density =\n        get<hydro::Tags::RestMassDensity<DataVector>>(vars);\n    const auto& electron_fraction =\n        get<hydro::Tags::ElectronFraction<DataVector>>(vars);\n    const auto& specific_internal_energy =\n        get<hydro::Tags::SpecificInternalEnergy<DataVector>>(vars);\n    const auto& spatial_velocity =\n        get<hydro::Tags::SpatialVelocity<DataVector, 3>>(vars);\n    const auto& magnetic_field =\n        get<hydro::Tags::MagneticField<DataVector, 3>>(vars);\n    const auto& divergence_cleaning_field =\n        get<hydro::Tags::DivergenceCleaningField<DataVector>>(vars);\n    const auto& lorentz_factor =\n        get<hydro::Tags::LorentzFactor<DataVector>>(vars);\n    const auto& pressure = get<hydro::Tags::Pressure<DataVector>>(vars);\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n    const auto& sqrt_det_spatial_metric =\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(vars);\n\n    grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n        make_not_null(&get<grmhd::ValenciaDivClean::Tags::TildeD>(\n            gsl::at(solution_at_six_times, i))),\n        make_not_null(&get<grmhd::ValenciaDivClean::Tags::TildeYe>(\n            gsl::at(solution_at_six_times, i))),\n        make_not_null(&get<grmhd::ValenciaDivClean::Tags::TildeTau>(\n            gsl::at(solution_at_six_times, i))),\n        make_not_null(&get<grmhd::ValenciaDivClean::Tags::TildeS<>>(\n            gsl::at(solution_at_six_times, i))),\n        make_not_null(&get<grmhd::ValenciaDivClean::Tags::TildeB<>>(\n            gsl::at(solution_at_six_times, i))),\n        make_not_null(&get<grmhd::ValenciaDivClean::Tags::TildePhi>(\n            gsl::at(solution_at_six_times, i))),\n        rest_mass_density, electron_fraction, specific_internal_energy,\n        pressure, spatial_velocity, lorentz_factor, magnetic_field,\n        sqrt_det_spatial_metric, spatial_metric, divergence_cleaning_field);\n  }\n\n  return (-1.0 / (60.0 * delta_time)) * solution_at_six_times[0] +\n         (3.0 / (20.0 * delta_time)) * solution_at_six_times[1] +\n         (-0.75 / delta_time) * solution_at_six_times[2] +\n         (0.75 / delta_time) * solution_at_six_times[3] +\n         (-3.0 / (20.0 * delta_time)) * solution_at_six_times[4] +\n         (1.0 / (60.0 * delta_time)) * solution_at_six_times[5];\n}\n}  // namespace VerifyGrMhdSolution_detail\n\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Determines if the given `solution` is a solution of the GRMHD\n/// equations.\n///\n/// Uses numerical derivatives to compute the solution, on the given `mesh` of\n/// the root Element of the given `block` at the given `time` using a\n/// sixth-order derivative in time for the given `delta_time`. The maximum\n/// residual of the GRMHD equations must be zero within `error_tolerance`\ntemplate <typename Solution>\nvoid verify_grmhd_solution(const Solution& solution, const Block<3>& block,\n                           const Mesh<3>& mesh, const double error_tolerance,\n                           const double time, const double delta_time) {\n  static_assert(is_analytic_solution_v<Solution>,\n                \"Solution was not derived from AnalyticSolution\");\n  // Set up coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  if (block.is_time_dependent()) {\n    ERROR(\"The block must be time-independent\");\n  }\n  const ElementMap<3, Frame::Inertial> element_map{\n      ElementId<3>{0}, block.stationary_map().get_clone()};\n  const auto x = element_map(x_logical);\n\n  // Evaluate analytic solution\n  using solution_tags = tmpl::list<\n      hydro::Tags::RestMassDensity<DataVector>,\n      hydro::Tags::ElectronFraction<DataVector>,\n      hydro::Tags::SpecificInternalEnergy<DataVector>,\n      hydro::Tags::SpatialVelocity<DataVector, 3>,\n      hydro::Tags::MagneticField<DataVector, 3>,\n      hydro::Tags::DivergenceCleaningField<DataVector>,\n      hydro::Tags::LorentzFactor<DataVector>, hydro::Tags::Pressure<DataVector>,\n      hydro::Tags::SpecificEnthalpy<DataVector>, gr::Tags::Lapse<DataVector>,\n      gr::Tags::Shift<DataVector, 3>, gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      gr::Tags::SqrtDetSpatialMetric<DataVector>,\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      gr::Tags::ExtrinsicCurvature<DataVector, 3>>;\n  const auto vars = solution.variables(x, time, solution_tags{});\n\n  const auto& rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(vars);\n  const auto& electron_fraction =\n      get<hydro::Tags::ElectronFraction<DataVector>>(vars);\n  const auto& specific_internal_energy =\n      get<hydro::Tags::SpecificInternalEnergy<DataVector>>(vars);\n  const auto& spatial_velocity =\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(vars);\n  const auto& magnetic_field =\n      get<hydro::Tags::MagneticField<DataVector, 3>>(vars);\n  const auto& divergence_cleaning_field =\n      get<hydro::Tags::DivergenceCleaningField<DataVector>>(vars);\n  const auto& lorentz_factor =\n      get<hydro::Tags::LorentzFactor<DataVector>>(vars);\n  const auto& pressure = get<hydro::Tags::Pressure<DataVector>>(vars);\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& d_lapse =\n      get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        Frame::Inertial>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(vars);\n  const auto& d_shift =\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& d_spatial_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(vars);\n  const auto& inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(vars);\n  const auto& sqrt_det_spatial_metric =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(vars);\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(vars);\n\n  const size_t number_of_points = mesh.number_of_grid_points();\n  Scalar<DataVector> tilde_d(number_of_points);\n  Scalar<DataVector> tilde_ye(number_of_points);\n  Scalar<DataVector> tilde_tau(number_of_points);\n  tnsr::i<DataVector, 3> tilde_s(number_of_points);\n  tnsr::I<DataVector, 3> tilde_b(number_of_points);\n  Scalar<DataVector> tilde_phi(number_of_points);\n\n  grmhd::ValenciaDivClean::ConservativeFromPrimitive::apply(\n      make_not_null(&tilde_d), make_not_null(&tilde_ye),\n      make_not_null(&tilde_tau), make_not_null(&tilde_s),\n      make_not_null(&tilde_b), make_not_null(&tilde_phi), rest_mass_density,\n      electron_fraction, specific_internal_energy, pressure, spatial_velocity,\n      lorentz_factor, magnetic_field, sqrt_det_spatial_metric, spatial_metric,\n      divergence_cleaning_field);\n\n  using flux_tags =\n      tmpl::list<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                            tmpl::size_t<3>, Frame::Inertial>,\n                 Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                            tmpl::size_t<3>, Frame::Inertial>,\n                 Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                            tmpl::size_t<3>, Frame::Inertial>,\n                 Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeS<>,\n                            tmpl::size_t<3>, Frame::Inertial>,\n                 Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeB<>,\n                            tmpl::size_t<3>, Frame::Inertial>,\n                 Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                            tmpl::size_t<3>, Frame::Inertial>>;\n  Variables<flux_tags> fluxes(number_of_points);\n  auto& flux_tilde_d =\n      get<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD, tmpl::size_t<3>,\n                     Frame::Inertial>>(fluxes);\n  auto& flux_tilde_ye =\n      get<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe, tmpl::size_t<3>,\n                     Frame::Inertial>>(fluxes);\n  auto& flux_tilde_tau =\n      get<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau, tmpl::size_t<3>,\n                     Frame::Inertial>>(fluxes);\n  auto& flux_tilde_s =\n      get<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeS<>, tmpl::size_t<3>,\n                     Frame::Inertial>>(fluxes);\n  auto& flux_tilde_b =\n      get<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeB<>, tmpl::size_t<3>,\n                     Frame::Inertial>>(fluxes);\n  auto& flux_tilde_phi =\n      get<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi, tmpl::size_t<3>,\n                     Frame::Inertial>>(fluxes);\n\n  grmhd::ValenciaDivClean::ComputeFluxes::apply(\n      make_not_null(&flux_tilde_d), make_not_null(&flux_tilde_ye),\n      make_not_null(&flux_tilde_tau), make_not_null(&flux_tilde_s),\n      make_not_null(&flux_tilde_b), make_not_null(&flux_tilde_phi), tilde_d,\n      tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, lapse, shift,\n      sqrt_det_spatial_metric, spatial_metric, inv_spatial_metric, pressure,\n      spatial_velocity, lorentz_factor, magnetic_field);\n\n  if (block.is_time_dependent()) {\n    ERROR(\"The block must be time-independent\");\n  }\n  const auto div_of_fluxes =\n      divergence(fluxes, mesh, element_map.inv_jacobian(x_logical));\n\n  const auto& div_flux_tilde_d =\n      get<Tags::div<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeD,\n                               tmpl::size_t<3>, Frame::Inertial>>>(\n          div_of_fluxes);\n  const auto& div_flux_tilde_ye =\n      get<Tags::div<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeYe,\n                               tmpl::size_t<3>, Frame::Inertial>>>(\n          div_of_fluxes);\n  const auto& div_flux_tilde_tau =\n      get<Tags::div<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeTau,\n                               tmpl::size_t<3>, Frame::Inertial>>>(\n          div_of_fluxes);\n  const auto& div_flux_tilde_s =\n      get<Tags::div<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeS<>,\n                               tmpl::size_t<3>, Frame::Inertial>>>(\n          div_of_fluxes);\n  const auto& div_flux_tilde_b =\n      get<Tags::div<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildeB<>,\n                               tmpl::size_t<3>, Frame::Inertial>>>(\n          div_of_fluxes);\n  const auto& div_flux_tilde_phi =\n      get<Tags::div<Tags::Flux<grmhd::ValenciaDivClean::Tags::TildePhi,\n                               tmpl::size_t<3>, Frame::Inertial>>>(\n          div_of_fluxes);\n\n  Scalar<DataVector> source_tilde_tau(number_of_points);\n  tnsr::i<DataVector, 3> source_tilde_s(number_of_points);\n  tnsr::I<DataVector, 3> source_tilde_b(number_of_points);\n  Scalar<DataVector> source_tilde_phi(number_of_points);\n  grmhd::ValenciaDivClean::ComputeSources::apply(\n      make_not_null(&source_tilde_tau), make_not_null(&source_tilde_s),\n      make_not_null(&source_tilde_b), make_not_null(&source_tilde_phi), tilde_d,\n      tilde_ye, tilde_tau, tilde_s, tilde_b, tilde_phi, spatial_velocity,\n      magnetic_field, rest_mass_density, electron_fraction,\n      specific_internal_energy, lorentz_factor, pressure, lapse, d_lapse,\n      d_shift, spatial_metric, d_spatial_metric, inv_spatial_metric,\n      sqrt_det_spatial_metric, extrinsic_curvature, 0.0);\n\n  Scalar<DataVector> residual_tilde_d(number_of_points, 0.);\n  Scalar<DataVector> residual_tilde_ye(number_of_points, 0.);\n  Scalar<DataVector> residual_tilde_tau(number_of_points, 0.);\n  tnsr::i<DataVector, 3> residual_tilde_s(number_of_points, 0.);\n  tnsr::I<DataVector, 3> residual_tilde_b(number_of_points, 0.);\n  Scalar<DataVector> residual_tilde_phi(number_of_points, 0.);\n\n  Variables<VerifyGrMhdSolution_detail::valencia_tags> dt_solution =\n      VerifyGrMhdSolution_detail::numerical_dt(solution, x, time, delta_time);\n\n  get(residual_tilde_d) =\n      get(get<grmhd::ValenciaDivClean::Tags::TildeD>(dt_solution)) +\n      get(div_flux_tilde_d);\n  get(residual_tilde_ye) =\n      get(get<grmhd::ValenciaDivClean::Tags::TildeYe>(dt_solution)) +\n      get(div_flux_tilde_ye);\n  get(residual_tilde_tau) =\n      get(get<grmhd::ValenciaDivClean::Tags::TildeTau>(dt_solution)) +\n      get(div_flux_tilde_tau) - get(source_tilde_tau);\n  get<0>(residual_tilde_s) =\n      get<0>(get<grmhd::ValenciaDivClean::Tags::TildeS<>>(dt_solution)) +\n      get<0>(div_flux_tilde_s) - get<0>(source_tilde_s);\n  get<1>(residual_tilde_s) =\n      get<1>(get<grmhd::ValenciaDivClean::Tags::TildeS<>>(dt_solution)) +\n      get<1>(div_flux_tilde_s) - get<1>(source_tilde_s);\n  get<2>(residual_tilde_s) =\n      get<2>(get<grmhd::ValenciaDivClean::Tags::TildeS<>>(dt_solution)) +\n      get<2>(div_flux_tilde_s) - get<2>(source_tilde_s);\n  get<0>(residual_tilde_b) =\n      get<0>(get<grmhd::ValenciaDivClean::Tags::TildeB<>>(dt_solution)) +\n      get<0>(div_flux_tilde_b) - get<0>(source_tilde_b);\n  get<1>(residual_tilde_b) =\n      get<1>(get<grmhd::ValenciaDivClean::Tags::TildeB<>>(dt_solution)) +\n      get<1>(div_flux_tilde_b) - get<1>(source_tilde_b);\n  get<2>(residual_tilde_b) =\n      get<2>(get<grmhd::ValenciaDivClean::Tags::TildeB<>>(dt_solution)) +\n      get<2>(div_flux_tilde_b) - get<2>(source_tilde_b);\n  get(residual_tilde_phi) +=\n      get(get<grmhd::ValenciaDivClean::Tags::TildePhi>(dt_solution)) +\n      get(div_flux_tilde_phi) - get(source_tilde_phi);\n\n  Approx numerical_approx =\n      Approx::custom().epsilon(error_tolerance).scale(1.0);\n  CHECK(max(abs(get(residual_tilde_d))) == numerical_approx(0.0));\n  CHECK(max(abs(get(residual_tilde_ye))) == numerical_approx(0.0));\n  CHECK(max(abs(get(residual_tilde_tau))) == numerical_approx(0.0));\n  CHECK(max(abs(get<0>(residual_tilde_s))) == numerical_approx(0.0));\n  CHECK(max(abs(get<1>(residual_tilde_s))) == numerical_approx(0.0));\n  CHECK(max(abs(get<2>(residual_tilde_s))) == numerical_approx(0.0));\n  CHECK(max(abs(get<0>(residual_tilde_b))) == numerical_approx(0.0));\n  CHECK(max(abs(get<1>(residual_tilde_b))) == numerical_approx(0.0));\n  CHECK(max(abs(get<2>(residual_tilde_b))) == numerical_approx(0.0));\n  CHECK(max(abs(get(residual_tilde_phi))) == numerical_approx(0.0));\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace TestHelpers {\n/// \\ingroup TestingFrameworkGroup\n/// Functions for testing analytic solutions\nnamespace AnalyticSolutions {\n/// Checks that tags can be retrieved both individually and all at\n/// once.\ntemplate <typename Solution, typename Coords, typename TagsList>\nvoid test_tag_retrieval(const Solution& solution, const Coords& coords,\n                        const double time, const TagsList /*meta*/) {\n  const auto vars_from_all_tags = solution.variables(coords, time, TagsList{});\n  const auto vars_from_all_tags_reversed =\n      solution.variables(coords, time, tmpl::reverse<TagsList>{});\n  tmpl::for_each<TagsList>([&](const auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    const auto single_var = solution.variables(coords, time, tmpl::list<tag>{});\n    CHECK(tuples::get<tag>(single_var) == tuples::get<tag>(vars_from_all_tags));\n    CHECK(tuples::get<tag>(single_var) ==\n          tuples::get<tag>(vars_from_all_tags_reversed));\n  });\n}\n}  // namespace AnalyticSolutions\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"XctsSolutionsTestHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  VerifySolution.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  CoordinateMaps\n  DataStructures\n  Domain\n  LinearOperators\n  Spectral\n  Utilities\n  Xcts\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Xcts/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/Xcts/SpacetimeQuantities.hpp\"\n\nnamespace TestHelpers::Xcts::Solutions {\n\nnamespace detail {\n\n// Verify the Hamiltonian and momentum constraints are satisfied. This is only a\n// consistency check of the solution, not a test of the system.\ntemplate <typename Solution>\nvoid verify_adm_constraints(const Solution& solution,\n                            const std::array<double, 3>& center,\n                            const double inner_radius,\n                            const double outer_radius, const double tolerance) {\n  INFO(\"ADM constraints\");\n  Approx custom_approx = Approx::custom().epsilon(tolerance).scale(1.0);\n\n  // Set up a grid for evaluating the solution and taking numerical derivatives\n  const Mesh<3> mesh{12, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const size_t num_points = mesh.number_of_grid_points();\n  using AffineMap = domain::CoordinateMaps::Affine;\n  using AffineMap3D =\n      domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n  const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                              AffineMap3D>\n      coord_map{\n          {{-1., 1., center[0] + inner_radius, center[0] + outer_radius},\n           {-1., 1., center[1] + inner_radius, center[1] + outer_radius},\n           {-1., 1., center[2] + inner_radius, center[2] + outer_radius}}};\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto x = coord_map(logical_coords);\n  const auto inv_jacobian = coord_map.inv_jacobian(logical_coords);\n\n  // Retrieve analytic variables\n  using analytic_tags = tmpl::list<\n      ::Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n      ::Xcts::Tags::ConformalFactor<DataVector>,\n      ::Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n      ::Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n      ::Tags::deriv<\n          ::Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n          tmpl::size_t<3>, Frame::Inertial>,\n      ::Xcts::Tags::ConformalChristoffelFirstKind<DataVector, 3,\n                                                  Frame::Inertial>,\n      ::Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                   Frame::Inertial>,\n      ::Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                   Frame::Inertial>,\n      ::Xcts::Tags::ConformalRicciTensor<DataVector, 3, Frame::Inertial>,\n      ::Xcts::Tags::ConformalRicciScalar<DataVector>,\n      ::Xcts::Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n      ::Xcts::Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n      ::Tags::deriv<\n          ::Xcts::Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n          tmpl::size_t<3>, Frame::Inertial>,\n      gr::Tags::Shift<DataVector, 3>, gr::Tags::Lapse<DataVector>,\n      ::Xcts::Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n      ::Xcts::Tags::LapseTimesConformalFactor<DataVector>,\n      gr::Tags::TraceExtrinsicCurvature<DataVector>,\n      ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                    tmpl::size_t<3>, Frame::Inertial>,\n      gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n      gr::Tags::SpatialMetric<DataVector, 3>,\n      gr::Tags::InverseSpatialMetric<DataVector, 3>,\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>,\n      ::Xcts::Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>,\n      ::Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n          DataVector, 3, Frame::Inertial>,\n      ::Tags::div<\n          ::Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n              DataVector, 3, Frame::Inertial>>,\n      gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>, 0>,\n      gr::Tags::Conformal<gr::Tags::MomentumDensity<DataVector, 3>, 0>>;\n  const auto analytic_vars =\n      solution.variables(x, mesh, inv_jacobian, analytic_tags{});\n  const auto& conformal_factor_minus_one =\n      get<::Xcts::Tags::ConformalFactorMinusOne<DataVector>>(analytic_vars);\n  const auto& conformal_factor =\n      get<::Xcts::Tags::ConformalFactor<DataVector>>(analytic_vars);\n  const auto& conformal_metric =\n      get<::Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>>(\n          analytic_vars);\n  const auto& inv_conformal_metric =\n      get<::Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>(\n          analytic_vars);\n  const auto& deriv_conformal_metric = get<::Tags::deriv<\n      ::Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n      tmpl::size_t<3>, Frame::Inertial>>(analytic_vars);\n  const auto& conformal_christoffel_first_kind =\n      get<::Xcts::Tags::ConformalChristoffelFirstKind<DataVector, 3,\n                                                      Frame::Inertial>>(\n          analytic_vars);\n  const auto& conformal_christoffel_second_kind =\n      get<::Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                       Frame::Inertial>>(\n          analytic_vars);\n  const auto& conformal_christoffel_contracted =\n      get<::Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                       Frame::Inertial>>(\n          analytic_vars);\n  const auto& conformal_ricci_tensor =\n      get<::Xcts::Tags::ConformalRicciTensor<DataVector, 3, Frame::Inertial>>(\n          analytic_vars);\n  const auto& conformal_ricci_scalar =\n      get<::Xcts::Tags::ConformalRicciScalar<DataVector>>(analytic_vars);\n  const auto& lapse_times_conformal_factor_minus_one =\n      get<::Xcts::Tags::LapseTimesConformalFactorMinusOne<DataVector>>(\n          analytic_vars);\n  const auto& lapse_times_conformal_factor =\n      get<::Xcts::Tags::LapseTimesConformalFactor<DataVector>>(analytic_vars);\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(analytic_vars);\n  const auto& shift_excess =\n      get<::Xcts::Tags::ShiftExcess<DataVector, 3, Frame::Inertial>>(\n          analytic_vars);\n  const auto& shift_background =\n      get<::Xcts::Tags::ShiftBackground<DataVector, 3, Frame::Inertial>>(\n          analytic_vars);\n  const auto& deriv_shift_background = get<::Tags::deriv<\n      ::Xcts::Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n      tmpl::size_t<3>, Frame::Inertial>>(analytic_vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(analytic_vars);\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(analytic_vars);\n  const auto& trace_extrinsic_curvature =\n      get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(analytic_vars);\n  const auto& deriv_trace_extrinsic_curvature =\n      get<::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                        tmpl::size_t<3>, Frame::Inertial>>(analytic_vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(analytic_vars);\n  const auto& inv_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(analytic_vars);\n  const auto& deriv_spatial_metric =\n      get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(analytic_vars);\n  const auto& longitudinal_shift_excess = get<\n      ::Xcts::Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>>(\n      analytic_vars);\n  const auto& longitudinal_shift_background_minus_dt_conformal_metric =\n      get<::Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n          DataVector, 3, Frame::Inertial>>(analytic_vars);\n  const auto& div_longitudinal_shift_background_minus_dt_conformal_metric =\n      get<::Tags::div<\n          ::Xcts::Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<\n              DataVector, 3, Frame::Inertial>>>(analytic_vars);\n  const auto& energy_density =\n      get<gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>, 0>>(\n          analytic_vars);\n  const auto& momentum_density =\n      get<gr::Tags::Conformal<gr::Tags::MomentumDensity<DataVector, 3>, 0>>(\n          analytic_vars);\n\n  // Compute Christoffels and Ricci\n  const auto spatial_christoffel =\n      gr::christoffel_second_kind(deriv_spatial_metric, inv_spatial_metric);\n  const auto deriv_spatial_christoffel =\n      partial_derivative(spatial_christoffel, mesh, inv_jacobian);\n  const auto spatial_ricci =\n      gr::ricci_tensor(spatial_christoffel, deriv_spatial_christoffel);\n  const auto ricci_scalar = trace(spatial_ricci, inv_spatial_metric);\n\n  // Check some identities\n  CHECK_ITERABLE_APPROX(trace(extrinsic_curvature, inv_spatial_metric),\n                        trace_extrinsic_curvature);\n  CHECK_ITERABLE_APPROX(get(lapse) * get(conformal_factor),\n                        get(lapse_times_conformal_factor));\n  CHECK_ITERABLE_APPROX(\n      tenex::evaluate<ti::I>(shift_background(ti::I) + shift_excess(ti::I)),\n      shift);\n\n  // Check extrinsic curvature decomposition\n  //   K_ij = \\psi^-2 \\bar{A}_ij + 1/3 \\gamma_ij K\n  // with\n  //   \\bar{A}^ij = \\psi^6 / (2 \\alpha) * (\\bar{L}\\beta^ij - \\bar{u}^ij)\n  tnsr::ii<DataVector, 3> composed_extcurv{};\n  tenex::evaluate<ti::i, ti::j>(\n      make_not_null(&composed_extcurv),\n      pow<4>(conformal_factor()) / (2. * lapse()) *\n              (longitudinal_shift_excess(ti::K, ti::L) +\n               longitudinal_shift_background_minus_dt_conformal_metric(ti::K,\n                                                                       ti::L)) *\n              conformal_metric(ti::i, ti::k) * conformal_metric(ti::l, ti::j) +\n          spatial_metric(ti::i, ti::j) * trace_extrinsic_curvature() / 3.);\n  CHECK_ITERABLE_APPROX(composed_extcurv, extrinsic_curvature);\n\n  // Check Hamiltonian constraint R + K^2 + K_ij K^ij = 16 \\pi \\rho, divided by\n  // two for consistency with SpEC (the factor of two doesn't really matter)\n  // here since we are comparing with zero)\n  const Scalar<DataVector> hamiltonian_constraint = tenex::evaluate(\n      0.5 * (ricci_scalar() + square(trace_extrinsic_curvature()) -\n             extrinsic_curvature(ti::i, ti::j) *\n                 extrinsic_curvature(ti::k, ti::l) *\n                 inv_spatial_metric(ti::I, ti::K) *\n                 inv_spatial_metric(ti::J, ti::L)) -\n      8. * M_PI * energy_density());\n  CHECK_ITERABLE_CUSTOM_APPROX(get(hamiltonian_constraint),\n                               DataVector(num_points, 0.), custom_approx);\n\n  // Check momentum constraint \\nabla_j (K^ij - K \\gamma^ij) = 8 \\pi S^i\n  tnsr::II<DataVector, 3> extcurv_min_trace{};\n  tenex::evaluate<ti::I, ti::J>(\n      make_not_null(&extcurv_min_trace),\n      extrinsic_curvature(ti::k, ti::l) * inv_spatial_metric(ti::I, ti::K) *\n              inv_spatial_metric(ti::J, ti::L) -\n          trace_extrinsic_curvature() * inv_spatial_metric(ti::I, ti::J));\n  const tnsr::iJJ<DataVector, 3> deriv_extcurv_min_trace =\n      partial_derivative(extcurv_min_trace, mesh, inv_jacobian);\n  const tnsr::I<DataVector, 3> momentum_constraint =\n      tenex::evaluate<ti::I>(deriv_extcurv_min_trace(ti::j, ti::I, ti::J) +\n                             spatial_christoffel(ti::I, ti::k, ti::j) *\n                                 extcurv_min_trace(ti::K, ti::J) +\n                             spatial_christoffel(ti::J, ti::k, ti::j) *\n                                 extcurv_min_trace(ti::I, ti::K) -\n                             8. * M_PI * momentum_density(ti::I));\n  CHECK_ITERABLE_CUSTOM_APPROX(momentum_constraint,\n                               (tnsr::I<DataVector, 3>(num_points, 0.)),\n                               custom_approx);\n\n  // Test things yet another way: compute quantities from XCTS variables alone\n  // (as they would come out of the elliptic solver) and compare to analytic\n  // solution\n  ::Xcts::SpacetimeQuantities spacetime_quantities{num_points};\n  const ::Xcts::SpacetimeQuantitiesComputer computer{\n      conformal_factor_minus_one,\n      lapse_times_conformal_factor_minus_one,\n      shift_excess,\n      conformal_metric,\n      inv_conformal_metric,\n      deriv_conformal_metric,\n      conformal_christoffel_first_kind,\n      conformal_christoffel_second_kind,\n      conformal_christoffel_contracted,\n      conformal_ricci_tensor,\n      conformal_ricci_scalar,\n      trace_extrinsic_curvature,\n      deriv_trace_extrinsic_curvature,\n      shift_background,\n      deriv_shift_background,\n      longitudinal_shift_background_minus_dt_conformal_metric,\n      div_longitudinal_shift_background_minus_dt_conformal_metric,\n      energy_density,\n      momentum_density,\n      mesh,\n      inv_jacobian};\n  const auto get_var = [&spacetime_quantities, &computer](const auto tag) {\n    return spacetime_quantities.get_var(computer, tag);\n  };\n  // Quantities that involve first numeric derivatives are compared with the\n  // given `tolerance`\n  CHECK_ITERABLE_APPROX(get_var(gr::Tags::Lapse<DataVector>{}), lapse);\n  CHECK_ITERABLE_APPROX(get_var(gr::Tags::Shift<DataVector, 3>{}), shift);\n  CHECK_ITERABLE_APPROX(get_var(gr::Tags::SpatialMetric<DataVector, 3>{}),\n                        spatial_metric);\n  CHECK_ITERABLE_APPROX(\n      get_var(gr::Tags::InverseSpatialMetric<DataVector, 3>{}),\n      inv_spatial_metric);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get_var(gr::Tags::ExtrinsicCurvature<DataVector, 3>{}),\n      extrinsic_curvature, custom_approx);\n  // These second derivatives (and dependent quantities) exceed the standard\n  // tolerance\n  Approx custom_approx2 = Approx::custom().epsilon(tolerance * 100).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(get_var(gr::Tags::SpatialRicci<DataVector, 3>{}),\n                               spatial_ricci, custom_approx2);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get_var(gr::Tags::HamiltonianConstraint<DataVector>{}),\n      Scalar<DataVector>(num_points, 0.), custom_approx2);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get_var(gr::Tags::MomentumConstraint<DataVector, 3>{}),\n      (tnsr::I<DataVector, 3>(num_points, 0.)), custom_approx2);\n}\n\ntemplate <::Xcts::Equations EnabledEquations,\n          ::Xcts::Geometry ConformalGeometry, int ConformalMatterScale,\n          typename Solution>\nvoid verify_solution_impl(const Solution& solution,\n                          const std::array<double, 3>& center,\n                          const double inner_radius, const double outer_radius,\n                          const double tolerance) {\n  CAPTURE(EnabledEquations);\n  CAPTURE(ConformalGeometry);\n  using system = ::Xcts::FirstOrderSystem<EnabledEquations, ConformalGeometry,\n                                          ConformalMatterScale>;\n  const Mesh<3> mesh{12, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  using AffineMap = domain::CoordinateMaps::Affine;\n  using AffineMap3D =\n      domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n  const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                              AffineMap3D>\n      coord_map{\n          {{-1., 1., center[0] + inner_radius, center[0] + outer_radius},\n           {-1., 1., center[1] + inner_radius, center[1] + outer_radius},\n           {-1., 1., center[2] + inner_radius, center[2] + outer_radius}}};\n  FirstOrderEllipticSolutionsTestHelpers::verify_solution<system>(\n      solution, mesh, coord_map, tolerance);\n}\n}  // namespace detail\n\n/*!\n * \\brief Verify the `solution` solves the XCTS equations numerically.\n *\n * \\tparam ConformalGeometry Specify `Xcts::Geometry::FlatCartesian` to test\n * both the flat _and_ the curved system, or `Xcts::Geometry::Curved` to only\n * test the curved system (for solutions on a curved conformal background).\n * \\tparam Solution The analytic solution to test (inferred)\n * \\param solution The analytic solution to test\n * \\param center offset for the \\p inner_radius and the \\p outer_radius\n * \\param inner_radius Lower-left corner of a cube on which to test\n * \\param outer_radius Upper-right corner of a cube on which to test\n * \\param tolerance Requested tolerance\n */\ntemplate <::Xcts::Geometry ConformalGeometry, int ConformalMatterScale,\n          typename Solution>\nvoid verify_solution(const Solution& solution,\n                     const std::array<double, 3>& center,\n                     const double inner_radius, const double outer_radius,\n                     const double tolerance) {\n  CAPTURE(tolerance);\n  detail::verify_adm_constraints(solution, center, inner_radius, outer_radius,\n                                 tolerance);\n  if constexpr (ConformalGeometry == ::Xcts::Geometry::FlatCartesian) {\n    INVOKE_TEST_FUNCTION(\n        detail::verify_solution_impl,\n        (solution, center, inner_radius, outer_radius, tolerance),\n        (::Xcts::Equations::Hamiltonian, ::Xcts::Equations::HamiltonianAndLapse,\n         ::Xcts::Equations::HamiltonianLapseAndShift),\n        (::Xcts::Geometry::FlatCartesian, ::Xcts::Geometry::Curved),\n        (ConformalMatterScale));\n  } else {\n    INVOKE_TEST_FUNCTION(\n        detail::verify_solution_impl,\n        (solution, center, inner_radius, outer_radius, tolerance),\n        (::Xcts::Equations::Hamiltonian, ::Xcts::Equations::HamiltonianAndLapse,\n         ::Xcts::Equations::HamiltonianLapseAndShift),\n        (::Xcts::Geometry::Curved), (ConformalMatterScale));\n  }\n}\n\n}  // namespace TestHelpers::Xcts::Solutions\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(AnalyticSolutions)\nadd_subdirectory(ConstraintDamping)\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(Hydro)\nadd_subdirectory(PostNewtonian)\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/ConstraintDamping/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"ConstraintDampingHelpers\")\n\nadd_spectre_library(${LIBRARY} INTERFACE)\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/tests/Unit/\n  HEADERS\n  TestHelpers.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  DataStructures\n  DataStructuresHelpers\n  Framework\n  FunctionsOfTime\n  Parallel\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/ConstraintDamping/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace TestHelpers::ConstraintDamping {\nnamespace detail {\ntemplate <size_t VolumeDim, typename Fr, class... MemberArgs, class T>\nvoid check_impl(\n    const std::unique_ptr<::ConstraintDamping::DampingFunction<VolumeDim, Fr>>&\n        in_damping_function,\n    const std::string& python_function_prefix, const T& used_for_size,\n    const std::array<std::pair<double, double>, 1> random_value_bounds,\n    const std::vector<std::string>& function_of_time_names,\n    const MemberArgs&... member_args) {\n  using GhDampingFunc = ::ConstraintDamping::DampingFunction<VolumeDim, Fr>;\n\n  const auto member_args_tuple = std::make_tuple(member_args...);\n  const auto helper = [&python_function_prefix, &random_value_bounds,\n                       &member_args_tuple, &function_of_time_names,\n                       &used_for_size](const std::unique_ptr<GhDampingFunc>&\n                                           gh_damping_function) {\n    INFO(\"Testing call operator...\");\n    // Make a lambda that calls the damping function's call operator\n    // with a hard-coded FunctionsOfTime, since check_with_random_values\n    // cannot convert a FunctionsOfTime into a python type.\n    // The FunctionsOfTime contains a single FunctionOfTime\n    // \\f$f(t) = a_0 + a_1 (t-t_0) + a_2 (t-t_0)^2 + a_3 (t-t_0)^3\\f$, where\n    // \\f$a_0 = 1.0\\f$, \\f$a_1 = 0.2\\f$, \\f$a_2 = 0.03,\\f$,\n    // \\f$a_3 = 0.004\\f$, and \\f$t_0\\f$ is the smallest possible value\n    // of the randomly selected time.\n    //\n    // The corresponding python function should use\n    // the same hard-coded coefficients to evaluate \\f$f(t)\\f$ as well\n    // as the same value of \\f$t_0\\f$.\n    // However, here the PiecewisePolynomial must be initialized not\n    // with the polynomial coefficients but with the values of \\f$f(t)\\f$\n    // and its derivatives evaluated at \\f$t=t_0\\f$: these are,\n    // respectively, \\f$a_0,a_1,2 a_2,6 a_3\\f$.\n    //\n    // Finally, note that the FunctionOfTime never expires.\n    const auto damping_function_call_operator_helper =\n        [&gh_damping_function, &random_value_bounds, &function_of_time_names](\n            const tnsr::I<T, VolumeDim, Fr>& coordinates, const double time) {\n          std::unordered_map<\n              std::string,\n              std::unique_ptr<::domain::FunctionsOfTime::FunctionOfTime>>\n              functions_of_time{};\n          for (const auto& function_of_time_name : function_of_time_names) {\n            if (function_of_time_name == \"GridCenters\") {\n              functions_of_time[function_of_time_name] = std::make_unique<\n                  ::domain::FunctionsOfTime::PiecewisePolynomial<3>>(\n                  std::min(gsl::at(random_value_bounds, 0).first,\n                           gsl::at(random_value_bounds, 0).second),\n                  std::array<DataVector, 4>{\n                      {{16.0, 0.0, 0.0, -16.0, 0.0, 0.0},\n                       {-0.001, 0.0, 0.0, 0.002, 0.0, 0.0},\n                       {0.0, 0.0, 0.0, 0.0, 0.0, 0.0},\n                       {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}}},\n                  std::numeric_limits<double>::max());\n            } else {\n              // The randomly selected time will be between the\n              // random_value_bounds, so set the earliest time of the\n              // function_of_times to the lower bound in\n              // random_value_bounds.\n              functions_of_time[function_of_time_name] = std::make_unique<\n                  ::domain::FunctionsOfTime::PiecewisePolynomial<3>>(\n                  std::min(gsl::at(random_value_bounds, 0).first,\n                           gsl::at(random_value_bounds, 0).second),\n                  std::array<DataVector, 4>{{{1.0}, {0.2}, {0.06}, {0.024}}},\n                  std::numeric_limits<double>::max());\n            }\n          }\n          // Default-construct the scalar, to test that the damping\n          // function's call operator correctly resizes it\n          // (in the case T is a DataVector)\n          // with set_number_of_grid_points()\n          Scalar<T> value_at_coordinates{};\n          gh_damping_function->operator()(make_not_null(&value_at_coordinates),\n                                          coordinates, time, functions_of_time);\n          return value_at_coordinates;\n        };\n\n    pypp::check_with_random_values<1>(\n        &decltype(damping_function_call_operator_helper)::operator(),\n        damping_function_call_operator_helper, python_function_prefix,\n        \"call_operator\", random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done testing call operator...\");\n    INFO(\"Done\\n\\n\");\n  };\n\n  helper(in_damping_function);\n  helper(serialize_and_deserialize(in_damping_function));\n}\n}  // namespace detail\n/// @{\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Test a DampingFunction by comparing to python functions\n *\n * The python functions must be added to modules under\n * tests/Unit/PointwiseFunctions/ConstraintDamping/Python/ named for the\n * `python_function_prefix` (for example, `constant`, `gaussian_plus_constant`).\n * Each python function for a corresponding DampingFunction should be named\n * `call_operator`. The prefix for each class of DampingFunction is arbitrary,\n * but should generally be descriptive (e.g. 'gaussian_plus_constant') of the\n * DampingFunction.\n *\n * The input parameter `function_of_time_name` is the name of the FunctionOfTime\n * that will be included in the FunctionsOfTime passed to the DampingFunction's\n * call operator. For time-dependent DampingFunctions, this parameter must be\n * consistent with the FunctionOfTime name that the call operator of\n * `in_damping_function` expects. For time-independent DampingFunctions,\n * `function_of_time_name` will be ignored.\n *\n * If a DampingFunction class has member variables set by its constructor, then\n * these member variables must be passed in as the last arguments to the `check`\n * function`. Each python function must take these same arguments as the\n * trailing arguments.\n */\ntemplate <class DampingFunctionType, class T, class... MemberArgs>\nvoid check(std::unique_ptr<DampingFunctionType> in_damping_function,\n           const std::string& python_function_prefix, const T& used_for_size,\n           const std::array<std::pair<double, double>, 1>& random_value_bounds,\n           const std::vector<std::string>& function_of_time_names,\n           const MemberArgs&... member_args) {\n  detail::check_impl(\n      std::unique_ptr<::ConstraintDamping::DampingFunction<\n          DampingFunctionType::volume_dim,\n          typename DampingFunctionType::frame>>(std::move(in_damping_function)),\n      python_function_prefix, used_for_size, random_value_bounds,\n      function_of_time_names, member_args...);\n}\n\ntemplate <class DampingFunctionType, class T, class... MemberArgs>\nvoid check(DampingFunctionType in_damping_function,\n           const std::string& python_function_prefix, const T& used_for_size,\n           const std::array<std::pair<double, double>, 1>& random_value_bounds,\n           const std::vector<std::string>& function_of_time_names,\n           const MemberArgs&... member_args) {\n  detail::check_impl(std::unique_ptr<::ConstraintDamping::DampingFunction<\n                         DampingFunctionType::volume_dim,\n                         typename DampingFunctionType::frame>>(\n                         std::make_unique<DampingFunctionType>(\n                             std::move(in_damping_function))),\n                     python_function_prefix, used_for_size, random_value_bounds,\n                     function_of_time_names, member_args...);\n}\n/// @}\n}  // namespace TestHelpers::ConstraintDamping\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"GeneralRelativityHelpers\")\n\nset(LIBRARY_SOURCES\n  TestHelpers.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructuresHelpers\n  Utilities\n\n  PUBLIC\n  DataStructures\n  )\n\nadd_subdirectory(Surfaces)\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/GeneralRelativity/Surfaces/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"GrSurfacesHelpers\")\n\nset(LIBRARY_SOURCES\n  TestHelpers.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  ApparentHorizonFinder\n  DataStructures\n  SphericalHarmonics\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/GeneralRelativity/Surfaces/TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/Surfaces/TestHelpers.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace TestHelpers {\nnamespace Schwarzschild {\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> spatial_ricci(\n    const tnsr::I<DataType, SpatialDim, Frame>& x, const double mass) {\n  auto ricci = make_with_value<tnsr::ii<DataType, SpatialDim, Frame>>(x, 0.);\n\n  constexpr auto dimensionality = index_dim<0>(ricci);\n\n  const DataType r = get(magnitude(x));\n\n  for (size_t i = 0; i < dimensionality; ++i) {\n    for (size_t j = i; j < dimensionality; ++j) {\n      ricci.get(i, j) -= (8.0 * mass + 3.0 * r) * x.get(i) * x.get(j);\n      if (i == j) {\n        ricci.get(i, j) += square(r) * (4.0 * mass + r);\n      }\n      ricci.get(i, j) *= mass;\n      ricci.get(i, j) /= pow<4>(r) * square(2.0 * mass + r);\n    }\n  }\n\n  return ricci;\n}\n}  // namespace Schwarzschild\n\nnamespace Minkowski {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> extrinsic_curvature_sphere(\n    const tnsr::I<DataType, SpatialDim, Frame>& x) {\n  auto extrinsic_curvature =\n      make_with_value<tnsr::ii<DataType, SpatialDim, Frame>>(x, 0.);\n\n  constexpr auto dimensionality = index_dim<0>(extrinsic_curvature);\n\n  const DataType one_over_r = 1.0 / get(magnitude(x));\n\n  for (size_t i = 0; i < dimensionality; ++i) {\n    extrinsic_curvature.get(i, i) += 1.0;\n    for (size_t j = i; j < dimensionality; ++j) {\n      extrinsic_curvature.get(i, j) -= x.get(i) * x.get(j) * square(one_over_r);\n      extrinsic_curvature.get(i, j) *= one_over_r;\n    }\n  }\n\n  return extrinsic_curvature;\n}\n}  // namespace Minkowski\n\nnamespace Kerr {\ntemplate <typename DataType>\nScalar<DataType> horizon_ricci_scalar(const Scalar<DataType>& horizon_radius,\n                                      const double mass,\n                                      const double dimensionless_spin_z) {\n  // Compute Kerr spin parameter a\n  // This is the magnitude of the dimensionless spin times the mass\n  double kerr_spin_a = mass * dimensionless_spin_z;\n\n  // Compute the Boyer-Lindquist horizon radius, r+\n  const double kerr_r_plus = mass + sqrt(square(mass) - square(kerr_spin_a));\n\n  // Get the Ricci scalar of the horizon, e.g. Eq. (119) of\n  // https://arxiv.org/abs/0706.0622\n  // The precise relation used here is derived in\n  // https://v2.overleaf.com/read/twdtxchyrtyv\n  Scalar<DataVector> ricci_scalar(\n      2.0 * (square(kerr_r_plus) + square(kerr_spin_a)) *\n      (3.0 * square(get(horizon_radius)) - 2.0 * square(kerr_r_plus) -\n       3.0 * square(kerr_spin_a)));\n  get(ricci_scalar) /= cube(-1.0 * square(get(horizon_radius)) +\n                            square(kerr_spin_a) + 2.0 * square(kerr_r_plus));\n  return ricci_scalar;\n}\n\ntemplate <typename DataType>\nScalar<DataType> horizon_ricci_scalar(\n    const Scalar<DataType>& horizon_radius_with_spin_on_z_axis,\n    const ylm::Spherepack& ylm_with_spin_on_z_axis, const ylm::Spherepack& ylm,\n    const double mass, const std::array<double, 3>& dimensionless_spin) {\n  // get the dimensionless spin magnitude and direction\n  const double spin_magnitude = magnitude(dimensionless_spin);\n  const double spin_theta =\n      atan2(sqrt(square(dimensionless_spin[0]) + square(dimensionless_spin[1])),\n            dimensionless_spin[2]);\n\n  // Return the aligned-spin result if spin is close enough to the z axis,\n  // to avoid a floating-point exception. The choice of eps here is arbitrary.\n  const double eps = 1.e-10;\n\n  // There are 2 ylm::Spherepacks: i) ylm, for the actual black hole, with\n  // spin in a generic direction, and ii) ylm_with_spin_on_z_axis, for a black\n  // hole with the same spin magnitude but with the spin in the +z direction. To\n  // get the horizon Ricci scalar for the actual black hole, do this:\n  //    1. Find the horizon Ricci scalar for the aligned spin\n  //    2. Let the generic spin point in direction (spin_theta, spin_phi).\n  //       Rotate the ylm.theta_phi_points by -spin_phi about the z axis and\n  //       then by -spin_theta about the y axis, so the point\n  //       (spin_theta, spin_phi) is mapped to (0, 0), the +z axis.\n  //    3. Interpolate the aligned-spin Ricci scalar from step 1 at each\n  //       rotated point from step 2 to get the horizon Ricci scalar\n  //       for the corresponding (unrotated) ylm_theta_phi_points.\n\n  // Get the ricci scalar for a Kerr black hole with spin in the +z direction\n  // but same mass and spin magnitude\n  auto ricci_scalar_with_spin_on_z_axis = horizon_ricci_scalar(\n      horizon_radius_with_spin_on_z_axis, mass, spin_magnitude);\n\n  // Is the spin aligned? If so, just return the aligned-spin scalar curvature\n  if (abs(spin_theta) < eps or abs(spin_theta - M_PI) < eps) {\n    return ricci_scalar_with_spin_on_z_axis;\n  }\n\n  const double spin_phi = atan2(dimensionless_spin[1], dimensionless_spin[0]);\n\n  // Get the theta and phi points on the original Strahlkorper, where the\n  // spin is not on the z axis\n  const auto theta_phi_points = ylm.theta_phi_points();\n\n  // Rotate each point\n  // Get thethas and phis on the actual horizon\n  const DataVector thetas = theta_phi_points[0];\n  const DataVector phis = theta_phi_points[1];\n\n  // Rotate the coordinates on the original Strahlkorper so that a point\n  // on the spin axis gets mapped to a point on the +z axis.\n  // This means the new coordinates are rotated from the old ones by\n  // -spin_phi about the z axis and then by -spin_theta about the y axis.\n  // The unrotated x,y,z coordinates are defined on the unit sphere:\n  // x = sin(theta)*cos(phi), y = sin(theta) * sin(phi), z = cos(theta)\n\n  const DataVector x_new =\n      cos(spin_theta) * cos(phis - spin_phi) * sin(thetas) -\n      cos(thetas) * sin(spin_theta);\n  const DataVector y_new = sin(thetas) * sin(phis - spin_phi);\n  const DataVector z_new = cos(thetas) * cos(spin_theta) +\n                           cos(phis - spin_phi) * sin(thetas) * sin(spin_theta);\n\n  // Since I'm rotating on the unit sphere, the radius of the unrotated and\n  // new points is unity.\n  DataVector thetas_new = atan2(sqrt(square(x_new) + square(y_new)), z_new);\n  DataVector phis_new(thetas_new.size());\n  for (size_t i = 0; i < thetas_new.size(); ++i) {\n    double phi_new =\n        (abs(thetas_new[i]) > eps and abs(thetas_new[i] - M_PI) > eps)\n            ? atan2(y_new[i], x_new[i])\n            : 0.0;\n    // Ensure phi_new is between 0 and 2 pi.\n    if (phi_new < 0.0) {\n      phi_new += 2.0 * M_PI;\n    }\n    phis_new[i] = phi_new;\n  }\n\n  std::array<DataVector, 2> points{std::move(thetas_new), std::move(phis_new)};\n\n  // Interpolate ricci_scalar_with_spin_on_z_axis onto the new points\n  auto interpolation_info =\n      ylm_with_spin_on_z_axis.set_up_interpolation_info(points);\n  DataVector ricci_scalar_interpolated(interpolation_info.size());\n  ylm_with_spin_on_z_axis.interpolate(\n      make_not_null(&ricci_scalar_interpolated),\n      get(ricci_scalar_with_spin_on_z_axis).data(), interpolation_info);\n\n  // Load the interpolated values into the DataVector ricci_scalar\n  Scalar<DataVector> ricci_scalar =\n      make_with_value<Scalar<DataVector>>(theta_phi_points[0], 0.0);\n  for (size_t i = 0; i < get(ricci_scalar_with_spin_on_z_axis).size(); ++i) {\n    get(ricci_scalar)[i] = ricci_scalar_interpolated[i];\n  }\n\n  return ricci_scalar;\n}\n\n}  // namespace Kerr\n}  // namespace TestHelpers\n\n#define DIM(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(1, data)\n#define FRAME(data) BOOST_PP_TUPLE_ELEM(2, data)\n#define INDEXTYPE(data) BOOST_PP_TUPLE_ELEM(3, data)\n\n#define INSTANTIATE(_, data)                                 \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>     \\\n  TestHelpers::Schwarzschild::spatial_ricci(                 \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x, \\\n      const double mass);                                    \\\n  template tnsr::ii<DTYPE(data), DIM(data), FRAME(data)>     \\\n  TestHelpers::Minkowski::extrinsic_curvature_sphere(        \\\n      const tnsr::I<DTYPE(data), DIM(data), FRAME(data)>& x);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (3), (double, DataVector),\n                        (Frame::Grid, Frame::Inertial))\n\n#undef DIM\n#undef DTYPE\n#undef FRAME\n#undef INDEXTYPE\n#undef INSTANTIATE\n\ntemplate Scalar<DataVector> TestHelpers::Kerr::horizon_ricci_scalar(\n    const Scalar<DataVector>& horizon_radius, const double mass,\n    const double dimensionless_spin_z);\ntemplate Scalar<DataVector> TestHelpers::Kerr::horizon_ricci_scalar(\n    const Scalar<DataVector>& horizon_radius_with_spin_on_z_axis,\n    const ylm::Spherepack& ylm_with_spin_on_z_axis, const ylm::Spherepack& ylm,\n    const double mass, const std::array<double, 3>& spin);\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/GeneralRelativity/Surfaces/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace ylm {\nclass Spherepack;\n}  // namespace ylm\n/// \\endcond\n\nnamespace TestHelpers {\nnamespace Schwarzschild {\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Schwarzschild (Kerr-Schild) spatial ricci tensor\n *\n * \\details\n * Computes \\f$R_{ij} = M \\frac{r^2(4M+r)\\delta_{ij}-(8M+3r)x_i x_j}\n * {r^4(2M+r^2)},\\f$\n * where \\f$r = x_i x_j \\delta^{ij}\\f$, \\f$x_i\\f$ is the\n * position vector in Cartesian coordinates, and M is the mass.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> spatial_ricci(\n    const tnsr::I<DataType, SpatialDim, Frame>& x, double mass);\n}  // namespace Schwarzschild\n\nnamespace Minkowski {\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Extrinsic curvature of 2D sphere in 3D flat space\n *\n * \\details\n * Computes \\f$K_{ij} = \\frac{1}{r}\\left(\\delta_{ij} -\n * \\frac{x_i x_j}{r}\\right),\\f$\n * where \\f$r = x_i x_j \\delta^{ij}\\f$ and \\f$x_i\\f$ is the\n * position vector in Cartesian coordinates.\n */\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> extrinsic_curvature_sphere(\n    const tnsr::I<DataType, SpatialDim, Frame>& x);\n}  // namespace Minkowski\n\nnamespace Kerr {\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Kerr (Kerr-Schild) horizon ricci scalar (spin on z axis)\n *\n * \\details\n * Computes the 2-dimensional Ricci scalar \\f$R\\f$ on the\n * horizon of a Kerr-Schild black hole with spin in the z direction\n * in terms of mass `mass` and dimensionless spin `dimensionless_spin_z`.\n */\ntemplate <typename DataType>\nScalar<DataType> horizon_ricci_scalar(const Scalar<DataType>& horizon_radius,\n                                      double mass, double dimensionless_spin_z);\n\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Kerr (Kerr-Schild) horizon ricci scalar (generic spin)\n *\n * \\details\n * Computes the 2-dimensional Ricci scalar \\f$R\\f$ on the\n * horizon of a Kerr-Schild black hole with generic spin\n * in terms of mass `mass` and dimensionless spin `dimensionless_spin`.\n */\ntemplate <typename DataType>\nScalar<DataType> horizon_ricci_scalar(\n    const Scalar<DataType>& horizon_radius_with_spin_on_z_axis,\n    const ylm::Spherepack& ylm_with_spin_on_z_axis, const ylm::Spherepack& ylm,\n    double mass, const std::array<double, 3>& dimensionless_spin);\n\n}  // namespace Kerr\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TestHelpers::gr {\ntemplate <typename DataType>\nScalar<DataType> random_lapse(const gsl::not_null<std::mt19937*> generator,\n                              const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(0.1, 3.0);\n  return make_with_random_values<Scalar<DataType>>(\n      generator, make_not_null(&distribution), used_for_size);\n}\n\ntemplate <size_t Dim, typename DataType>\ntnsr::I<DataType, Dim> random_shift(\n    const gsl::not_null<std::mt19937*> generator,\n    const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  return make_with_random_values<tnsr::I<DataType, Dim>>(\n      generator, make_not_null(&distribution), used_for_size);\n}\n\ntemplate <size_t Dim, typename DataType, typename Fr>\ntnsr::ii<DataType, Dim, Fr> random_spatial_metric(\n    const gsl::not_null<std::mt19937*> generator,\n    const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-0.05, 0.05);\n  auto spatial_metric = make_with_random_values<tnsr::ii<DataType, Dim, Fr>>(\n      generator, make_not_null(&distribution), used_for_size);\n  for (size_t d = 0; d < Dim; ++d) {\n    spatial_metric.get(d, d) += 1.0;\n  }\n  return spatial_metric;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                \\\n  template Scalar<DTYPE(data)> random_lapse(        \\\n      const gsl::not_null<std::mt19937*> generator, \\\n      const DTYPE(data) & used_for_size);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (double, DataVector))\n\n#define INSTANTIATE_TENSORS(_, data)                               \\\n  template tnsr::I<DTYPE(data), DIM(data)> random_shift(           \\\n      const gsl::not_null<std::mt19937*> generator,                \\\n      const DTYPE(data) & used_for_size);                          \\\n  template tnsr::ii<DTYPE(data), DIM(data)> random_spatial_metric( \\\n      const gsl::not_null<std::mt19937*> generator,                \\\n      const DTYPE(data) & used_for_size);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_TENSORS, (double, DataVector), (1, 2, 3))\n\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_TENSORS\n#undef DIM\n#undef DTYPE\n}  // namespace TestHelpers::gr\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions useful for testing general relativity\n\n#pragma once\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace TestHelpers {\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Make random GR variables which correct physical behavior,\n/// e.g. spatial metric will be positive definite\nnamespace gr {\ntemplate <typename DataType>\nScalar<DataType> random_lapse(gsl::not_null<std::mt19937*> generator,\n                              const DataType& used_for_size);\n\ntemplate <size_t Dim, typename DataType>\ntnsr::I<DataType, Dim> random_shift(gsl::not_null<std::mt19937*> generator,\n                                    const DataType& used_for_size);\n\ntemplate <size_t Dim, typename DataType, typename Fr = Frame::Inertial>\ntnsr::ii<DataType, Dim, Fr> random_spatial_metric(\n    gsl::not_null<std::mt19937*> generator, const DataType& used_for_size);\n}  // namespace gr\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/Hydro/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"HydroHelpers\")\n\nset(LIBRARY_SOURCES\n  TestHelpers.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  INTERFACE\n  Hydro\n\n  PRIVATE\n  DataStructuresHelpers\n\n  PUBLIC\n  DataStructures\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace TestHelpers {\nnamespace EquationsOfState {\nnamespace detail {\ntemplate <size_t ThermodynamicDim,\n          typename = std::make_index_sequence<ThermodynamicDim>>\nstruct CreateMemberFunctionPointer;\ntemplate <size_t ThermodynamicDim, size_t... Is>\nstruct CreateMemberFunctionPointer<ThermodynamicDim,\n                                   std::index_sequence<Is...>> {\n  template <class DataType, class EoS>\n  using f = Scalar<DataType> (EoS::*)(\n      const Scalar<std::remove_pointer_t<\n          decltype((void)Is, std::add_pointer_t<DataType>{nullptr})>>&...)\n      const;\n};\n\ntemplate <class T, typename EoS>\nusing Function = Scalar<T> (EoS::*)(const Scalar<T>&, const Scalar<T>&) const;\n\ntemplate <bool IsRelativistic, class... MemberArgs, class T>\nvoid check_impl(\n    const std::unique_ptr<\n        ::EquationsOfState::EquationOfState<IsRelativistic, 1>>& in_eos,\n    const std::string& python_file_name,\n    const std::string& python_function_prefix, const T& used_for_size,\n    const MemberArgs&... member_args) {\n  // Bounds for: density\n  const std::array<std::pair<double, double>, 1> random_value_bounds{\n      {{1.0e-4, 4.0}}};\n  MAKE_GENERATOR(generator, std::random_device{}());\n  std::uniform_real_distribution<> distribution(random_value_bounds[0].first,\n                                                random_value_bounds[0].second);\n  const auto rest_mass_density = make_with_random_values<Scalar<T>>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n  const auto specific_internal_energy = make_with_random_values<Scalar<T>>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n  using EoS = ::EquationsOfState::EquationOfState<IsRelativistic, 1>;\n  using Function = typename CreateMemberFunctionPointer<1>::template f<T, EoS>;\n  INFO(\"Testing \"s + (IsRelativistic ? \"relativistic\"s : \"Newtonian\"s) +\n       \" equation of state\"s);\n  const auto member_args_tuple = std::make_tuple(member_args...);\n  const auto helper = [&](const std::unique_ptr<EoS>& eos) {\n    // need func variable to work around GCC bug\n    Function func{&EoS::pressure_from_density};\n    INFO(\"Testing pressure_from_density...\");\n    pypp::check_with_random_values<1>(\n        func, *eos, python_file_name,\n        python_function_prefix + \"_pressure_from_density\", random_value_bounds,\n        member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting rest_mass_density_from_enthalpy...\");\n    pypp::check_with_random_values<1>(\n        func = &EoS::rest_mass_density_from_enthalpy, *eos, python_file_name,\n        IsRelativistic ? std::string(python_function_prefix +\n                                     \"_rel_rest_mass_density_from_enthalpy\")\n                       : std::string(python_function_prefix +\n                                     \"_newt_rest_mass_density_from_enthalpy\"),\n        {{{1, 1.0e4}}}, member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting specific_internal_energy_from_density...\");\n    pypp::check_with_random_values<1>(\n        func = &EoS::specific_internal_energy_from_density, *eos,\n        python_file_name,\n        python_function_prefix + \"_specific_internal_energy_from_density\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting temperature_from_density...\");\n    CHECK(make_with_value<Scalar<T>>(used_for_size, 0.0) ==\n          eos->temperature_from_density(rest_mass_density));\n    INFO(\"Done\\nTesting temperature_from_specific_internal_energy...\");\n    CHECK(make_with_value<Scalar<T>>(used_for_size, 0.0) ==\n          eos->temperature_from_specific_internal_energy(\n              specific_internal_energy));\n    INFO(\"Done\\nTesting chi_from_density...\");\n    pypp::check_with_random_values<1>(\n        func = &EoS::chi_from_density, *eos, python_file_name,\n        python_function_prefix + \"_chi_from_density\", random_value_bounds,\n        member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting kappa_times_p_over_rho_squared_from_density...\");\n    pypp::check_with_random_values<1>(\n        func = &EoS::kappa_times_p_over_rho_squared_from_density, *eos,\n        python_file_name,\n        python_function_prefix + \"_kappa_times_p_over_rho_squared_from_density\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done\\n\\n\");\n  };\n  helper(in_eos);\n  helper(serialize_and_deserialize(in_eos));\n}\n\ntemplate <bool IsRelativistic, class... MemberArgs, class T>\nvoid check_impl(\n    const std::unique_ptr<\n        ::EquationsOfState::EquationOfState<IsRelativistic, 2>>& in_eos,\n    const std::string& python_file_name,\n    const std::string& python_function_prefix, const T& used_for_size,\n    const MemberArgs&... member_args) {\n  // Bounds for: density, specific internal energy\n  // Caution! This configuration can produce nonsense\n  // configurations if the sampled rho, epsilon require a\n  // negative temperature.  If any EoS assumes T >= 0\n  // this will have to be modified.\n  std::array<std::pair<double, double>, 2> random_value_bounds{\n      {{1.0e-4, 4.0}, {0.0, 1.0e4}}};\n  const double specific_internal_energy_of_max_density{\n      in_eos->specific_internal_energy_lower_bound(\n          random_value_bounds[0].second)};\n  random_value_bounds[1].first = specific_internal_energy_of_max_density;\n  random_value_bounds[1].second =\n      1.0e4 + specific_internal_energy_of_max_density;\n  MAKE_GENERATOR(generator, std::random_device{}());\n  std::uniform_real_distribution<> distribution_density{\n      random_value_bounds[0].first, random_value_bounds[0].second};\n  std::uniform_real_distribution<> distribution_energy{\n      random_value_bounds[1].first, random_value_bounds[1].second};\n  const auto rest_mass_density = make_with_random_values<Scalar<T>>(\n      make_not_null(&generator), make_not_null(&distribution_density),\n      used_for_size);\n  const auto specific_internal_energy = make_with_random_values<Scalar<T>>(\n      make_not_null(&generator), make_not_null(&distribution_energy),\n      used_for_size);\n  using EoS = ::EquationsOfState::EquationOfState<IsRelativistic, 2>;\n  using Function = typename CreateMemberFunctionPointer<2>::template f<T, EoS>;\n  INFO(\"Testing \"s + (IsRelativistic ? \"relativistic\"s : \"Newtonian\"s) +\n       \" equation of state\"s);\n  const auto member_args_tuple = std::make_tuple(member_args...);\n  const auto helper = [&](const std::unique_ptr<EoS>& eos) {\n    // need func variable to work around GCC bug\n    Function func{&EoS::pressure_from_density_and_energy};\n    INFO(\"Testing pressure_from_density_and_energy...\");\n    pypp::check_with_random_values<2>(\n        func, *eos, python_file_name,\n        python_function_prefix + \"_pressure_from_density_and_energy\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting pressure_from_density_and_enthalpy...\");\n    pypp::check_with_random_values<2>(\n        func = &EoS::pressure_from_density_and_enthalpy, *eos, python_file_name,\n        IsRelativistic\n            ? std::string(python_function_prefix +\n                          \"_rel_pressure_from_density_and_enthalpy\")\n            : std::string(python_function_prefix +\n                          \"_newt_pressure_from_density_and_enthalpy\"),\n        {{{1.0e-4, 4.0},\n          {1.0 + random_value_bounds[1].first,\n           1.0 + random_value_bounds[1].second}}},\n        member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting temperature_from_density_and_specific_int_energy...\");\n    pypp::check_with_random_values<2>(\n        func = &EoS::temperature_from_density_and_energy, *eos,\n        python_file_name,\n        python_function_prefix + \"_temperature_from_density_and_energy\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting specific_int_energy_from_density_and_temperature...\");\n    Approx custom_approx = Approx::custom().epsilon(1.e-9);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        specific_internal_energy,\n        eos->specific_internal_energy_from_density_and_temperature(\n            rest_mass_density,\n            eos->temperature_from_density_and_energy(rest_mass_density,\n                                                     specific_internal_energy)),\n        custom_approx);\n    INFO(\"Done\\nTesting specific_internal_energy_from_density_and_pressure...\");\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        specific_internal_energy,\n        eos->specific_internal_energy_from_density_and_pressure(\n            rest_mass_density,\n            eos->pressure_from_density_and_energy(rest_mass_density,\n                                                  specific_internal_energy)),\n        custom_approx);\n    INFO(\"Done\\nTesting chi_from_density_and_energy...\");\n    pypp::check_with_random_values<2>(\n        func = &EoS::chi_from_density_and_energy, *eos, python_file_name,\n        python_function_prefix + \"_chi_from_density_and_energy\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\n        \"Done\\nTesting \"\n        \"kappa_times_p_over_rho_squared_from_density_and_energy...\");\n    pypp::check_with_random_values<2>(\n        func = &EoS::kappa_times_p_over_rho_squared_from_density_and_energy,\n        *eos, python_file_name,\n        python_function_prefix +\n            \"_kappa_times_p_over_rho_squared_from_density_and_energy\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done\\n\\n\");\n  };\n  helper(in_eos);\n  helper(serialize_and_deserialize(in_eos));\n}\ntemplate <bool IsRelativistic, class... MemberArgs, class T>\nvoid check_impl(\n    const std::unique_ptr<\n        ::EquationsOfState::EquationOfState<IsRelativistic, 3>>& in_eos,\n    const std::string& python_file_name,\n    const std::string& python_function_prefix, const T& used_for_size,\n    const MemberArgs&... member_args) {\n  // Bounds for: density, temperature, electron fraction\n  const std::array<std::pair<double, double>, 3> random_value_bounds{\n      {{1.0e-4, 1.0}, {0.0, 1.0}, {0.0, 0.5}}};\n  MAKE_GENERATOR(generator, std::random_device{}());\n  std::uniform_real_distribution<> distribution_density{\n      random_value_bounds[0].first, random_value_bounds[0].second};\n  std::uniform_real_distribution<> distribution_temperature{\n      random_value_bounds[1].first, random_value_bounds[1].second};\n  std::uniform_real_distribution<> distribution_electron_fraction{\n      random_value_bounds[2].first, random_value_bounds[2].second};\n  const auto rest_mass_density = make_with_random_values<Scalar<T>>(\n      make_not_null(&generator), make_not_null(&distribution_density),\n      used_for_size);\n  const auto temperature = make_with_random_values<Scalar<T>>(\n      make_not_null(&generator), make_not_null(&distribution_temperature),\n      used_for_size);\n  const auto electron_fraction = make_with_random_values<Scalar<T>>(\n      make_not_null(&generator), make_not_null(&distribution_electron_fraction),\n      used_for_size);\n\n  using EoS = ::EquationsOfState::EquationOfState<IsRelativistic, 3>;\n  using Function = typename CreateMemberFunctionPointer<3>::template f<T, EoS>;\n  INFO(\"Testing \"s + (IsRelativistic ? \"relativistic\"s : \"Newtonian\"s) +\n       \" equation of state\"s);\n  const auto specific_internal_energy =\n      in_eos.specific_internal_energy_from_density_and_temperautre(\n          rest_mass_density, temperature, electron_fraction);\n  CHECK(get(temperature) ==\n        get(in_eos.temperature_from_energy_and_density(\n            rest_mass_density, specific_internal_energy, electron_fraction)));\n  INFO(\"Done\\nTesting temperature_from_energy_and_density...\");\n  const auto member_args_tuple = std::make_tuple(member_args...);\n  const auto helper = [&](const std::unique_ptr<EoS>& eos) {\n    // need func variable to work around GCC bug\n    Function func{&EoS::temperature_from_density_and_energy};\n    INFO(\"Done\\nTesting specific_int_energy_from_density_and_temperature...\");\n    pypp::check_with_random_values<3>(\n        func = &EoS::specific_internal_energy_from_density_and_temperature,\n        *eos, python_file_name,\n        python_function_prefix + \"_energy_from_density_and_temperature\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting pressure_from_density_and_temperature...\");\n    pypp::check_with_random_values<3>(\n        func = &EoS::pressure_from_density_and_temperature, *eos,\n        python_file_name,\n        python_function_prefix + \"_pressure_from_density_and_temperature\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done\\nTesting sound_speed_from_density_and_temperature...\");\n    pypp::check_with_random_values<3>(\n        func = &EoS::sound_speed_squared_from_density_and_temperature, *eos,\n        python_file_name,\n        python_function_prefix +\n            \"_sound_speed_squared_from_density_and_temperature\",\n        random_value_bounds, member_args_tuple, used_for_size);\n    INFO(\"Done\\n\\n\");\n  };\n  helper(in_eos);\n  helper(serialize_and_deserialize(in_eos));\n}\n\n}  // namespace detail\n\n/// @{\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Test an equation of state by comparing to python functions\n *\n * The python functions must be added to\n * tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/TestFunctions.py. The\n * prefix for each class of equation of state is arbitrary, but should generally\n * be something like \"polytropic\" for polytropic fluids.\n *\n * The `python_function_prefix` argument passed to `check` must be `PREFIX`. If\n * an EoS class has member variables (these must be `double`s currently) that\n * are used to compute the quantities, such as the polytropic constant and\n * polytropic exponent for a fluid, then they must be passed in as the last\n * arguments to the `check` function`. Each python function must take these same\n * arguments as the trailing arguments.\n */\ntemplate <class EosType, class T, class... MemberArgs>\nvoid check(std::unique_ptr<EosType> in_eos, const std::string& python_file_name,\n           const std::string& python_function_prefix, const T& used_for_size,\n           const MemberArgs&... member_args) {\n  detail::check_impl(std::unique_ptr<::EquationsOfState::EquationOfState<\n                         EosType::is_relativistic, EosType::thermodynamic_dim>>(\n                         std::move(in_eos)),\n                     python_file_name, python_function_prefix, used_for_size,\n                     member_args...);\n}\n\ntemplate <class EosType, class T, class... MemberArgs>\nvoid check(EosType in_eos, const std::string& python_file_name,\n           const std::string& python_function_prefix, const T& used_for_size,\n           const MemberArgs&... member_args) {\n  detail::check_impl(std::unique_ptr<::EquationsOfState::EquationOfState<\n                         EosType::is_relativistic, EosType::thermodynamic_dim>>(\n                         std::make_unique<EosType>(std::move(in_eos))),\n                     python_file_name, python_function_prefix, used_for_size,\n                     member_args...);\n}\n/// @}\n\n/// Test that cloning is correct, and that the equality operator is implemented\n/// correctly\ntemplate <bool IsRelativistic, size_t ThermodynamicDim>\nvoid test_get_clone(\n    const ::EquationsOfState::EquationOfState<IsRelativistic, ThermodynamicDim>&\n        in_eos) {\n  auto cloned_eos = in_eos.get_clone();\n  CHECK(*cloned_eos == in_eos);\n}\n}  // namespace EquationsOfState\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/Hydro/TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/PointwiseFunctions/Hydro/TestHelpers.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace TestHelpers::hydro {\ntemplate <typename DataType>\nScalar<DataType> random_density(const gsl::not_null<std::mt19937*> generator,\n                                const DataType& used_for_size) {\n  // 1 g/cm^3 = 1.62e-18 in geometric units\n  // Most tests will work fine for any positive density, the exception being\n  // tests for primitive inversions in GR hydro and MHD, for which the tolerance\n  // would have to be loosened in order to allow for smaller densities.\n  constexpr double minimum_density = 1.0e-5;\n  constexpr double maximum_density = 1.0e-3;\n  std::uniform_real_distribution<> distribution(log(minimum_density),\n                                                log(maximum_density));\n  return Scalar<DataType>{exp(make_with_random_values<DataType>(\n      generator, make_not_null(&distribution), used_for_size))};\n}\n\ntemplate <typename DataType>\nScalar<DataType> random_electron_fraction(\n    const gsl::not_null<std::mt19937*> generator,\n    const DataType& used_for_size) {\n  // 1 g/cm^3 = 1.62e-18 in geometric units\n  // Most tests will work fine for any positive density, the exception being\n  // tests for primitive inversions in GR hydro and MHD, for which the tolerance\n  // would have to be loosened in order to allow for smaller densities.\n  constexpr double minimum_ye = 0.01;\n  constexpr double maximum_ye = 0.5;\n  std::uniform_real_distribution<> distribution(minimum_ye, maximum_ye);\n\n  return Scalar<DataType>{make_with_random_values<DataType>(\n      generator, make_not_null(&distribution), used_for_size)};\n}\n\ntemplate <typename DataType>\nScalar<DataType> random_lorentz_factor(\n    const gsl::not_null<std::mt19937*> generator,\n    const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-10.0, 3.0);\n  return Scalar<DataType>{\n      1.0 + exp(make_with_random_values<DataType>(\n                generator, make_not_null(&distribution), used_for_size))};\n}\n\ntemplate <typename DataType, size_t Dim>\ntnsr::I<DataType, Dim> random_velocity(\n    const gsl::not_null<std::mt19937*> generator,\n    const Scalar<DataType>& lorentz_factor,\n    const tnsr::ii<DataType, Dim>& spatial_metric) {\n  tnsr::I<DataType, Dim> spatial_velocity =\n      random_unit_normal(generator, spatial_metric);\n  const DataType v = sqrt(1.0 - 1.0 / square(get(lorentz_factor)));\n  for (size_t d = 0; d < Dim; ++d) {\n    spatial_velocity.get(d) *= v;\n  }\n  return spatial_velocity;\n}\n\n// temperature in MeV\ntemplate <typename DataType>\nScalar<DataType> random_temperature(\n    const gsl::not_null<std::mt19937*> generator,\n    const DataType& used_for_size) {\n  constexpr double minimum_temperature = 1.0;\n  constexpr double maximum_temperature = 50.0;\n  std::uniform_real_distribution<> distribution(log(minimum_temperature),\n                                                log(maximum_temperature));\n  return Scalar<DataType>{exp(make_with_random_values<DataType>(\n      generator, make_not_null(&distribution), used_for_size))};\n}\n\ntemplate <typename DataType>\nScalar<DataType> random_specific_internal_energy(\n    const gsl::not_null<std::mt19937*> generator,\n    const DataType& used_for_size) {\n  // assumes Ideal gas with gamma = 4/3\n  // For ideal fluid T = (m/k_b)(gamma - 1) epsilon\n  // where m = atomic mass unit, k_b = Boltzmann constant\n  // m/k_b ~ 933MeV in geometrized units so k_b/[m*(gamma - 1)] ~ 3.21e-3 MeV^-1\n  return Scalar<DataType>{3.21e-3 *\n                          get(random_temperature(generator, used_for_size))};\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> random_magnetic_field(\n    const gsl::not_null<std::mt19937*> generator,\n    const Scalar<DataType>& pressure,\n    const tnsr::ii<DataType, 3>& spatial_metric) {\n  tnsr::I<DataType, 3> magnetic_field =\n      random_unit_normal(generator, spatial_metric);\n  std::uniform_real_distribution<> distribution(-8.0, 14.0);\n  const size_t number_of_points = get_size(get(pressure));\n  for (size_t s = 0; s < number_of_points; ++s) {\n    // magnitude of B set to vary ratio of magnetic pressure to fluid pressure\n    const double B =\n        sqrt(get_element(get(pressure), s) * exp(distribution(*generator)));\n    get_element(get<0>(magnetic_field), s) *= B;\n    get_element(get<1>(magnetic_field), s) *= B;\n    get_element(get<2>(magnetic_field), s) *= B;\n  }\n  return magnetic_field;\n}\n\ntemplate <typename DataType>\nScalar<DataType> random_divergence_cleaning_field(\n    const gsl::not_null<std::mt19937*> generator,\n    const DataType& used_for_size) {\n  std::uniform_real_distribution<> distribution(-10.0, 10.0);\n  return make_with_random_values<Scalar<DataType>>(\n      generator, make_not_null(&distribution), used_for_size);\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n#define DIM(data) BOOST_PP_TUPLE_ELEM(1, data)\n\n#define INSTANTIATE_SCALARS(_, data)                             \\\n  template Scalar<DTYPE(data)> random_density(                   \\\n      const gsl::not_null<std::mt19937*> generator,              \\\n      const DTYPE(data) & used_for_size);                        \\\n  template Scalar<DTYPE(data)> random_electron_fraction(         \\\n      const gsl::not_null<std::mt19937*> generator,              \\\n      const DTYPE(data) & used_for_size);                        \\\n  template Scalar<DTYPE(data)> random_lorentz_factor(            \\\n      const gsl::not_null<std::mt19937*> generator,              \\\n      const DTYPE(data) & used_for_size);                        \\\n  template Scalar<DTYPE(data)> random_temperature(               \\\n      const gsl::not_null<std::mt19937*> generator,              \\\n      const DTYPE(data) & used_for_size);                        \\\n  template Scalar<DTYPE(data)> random_specific_internal_energy(  \\\n      const gsl::not_null<std::mt19937*> generator,              \\\n      const DTYPE(data) & used_for_size);                        \\\n  template Scalar<DTYPE(data)> random_divergence_cleaning_field( \\\n      const gsl::not_null<std::mt19937*> generator,              \\\n      const DTYPE(data) & used_for_size);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_SCALARS, (double, DataVector))\n\n#define INSTANTIATE_TENSORS(_, data)                        \\\n  template tnsr::I<DTYPE(data), DIM(data)> random_velocity( \\\n      const gsl::not_null<std::mt19937*> generator,         \\\n      const Scalar<DTYPE(data)>& lorentz_factor,            \\\n      const tnsr::ii<DTYPE(data), DIM(data)>& spatial_metric);\n\ntemplate tnsr::I<double, 3> random_magnetic_field(\n    const gsl::not_null<std::mt19937*> generator,\n    const Scalar<double>& pressure, const tnsr::ii<double, 3>& spatial_metric);\ntemplate tnsr::I<DataVector, 3> random_magnetic_field(\n    const gsl::not_null<std::mt19937*> generator,\n    const Scalar<DataVector>& pressure,\n    const tnsr::ii<DataVector, 3>& spatial_metric);\n\nGENERATE_INSTANTIATIONS(INSTANTIATE_TENSORS, (double, DataVector), (1, 2, 3))\n\n#undef INSTANTIATE_SCALARS\n#undef INSTANTIATE_TENSORS\n#undef DIM\n#undef DTYPE\n}  // namespace TestHelpers::hydro\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/Hydro/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <random>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n\n/// \\cond\nnamespace gsl {\ntemplate <typename T>\nclass not_null;\n}  // namespace gsl\n/// \\endcond\n\nnamespace TestHelpers {\n/// \\ingroup TestingFrameworkGroup\n/// \\brief Make random hydro variables which correct physical behavior,\n/// e.g. Lorentz factor will be greater or equal than one.\nnamespace hydro {\ntemplate <typename DataType>\nScalar<DataType> random_density(gsl::not_null<std::mt19937*> generator,\n                                const DataType& used_for_size);\n\ntemplate <typename DataType>\nScalar<DataType> random_lorentz_factor(gsl::not_null<std::mt19937*> generator,\n                                       const DataType& used_for_size);\n\ntemplate <typename DataType, size_t Dim>\ntnsr::I<DataType, Dim> random_velocity(\n    gsl::not_null<std::mt19937*> generator,\n    const Scalar<DataType>& lorentz_factor,\n    const tnsr::ii<DataType, Dim>& spatial_metric);\n\ntemplate <typename DataType>\nScalar<DataType> random_temperature(gsl::not_null<std::mt19937*> generator,\n                                    const DataType& used_for_size);\n\ntemplate <typename DataType>\nScalar<DataType> random_electron_fraction(\n    gsl::not_null<std::mt19937*> generator, const DataType& used_for_size);\n\ntemplate <typename DataType>\nScalar<DataType> random_specific_internal_energy(\n    gsl::not_null<std::mt19937*> generator, const DataType& used_for_size);\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3> random_magnetic_field(\n    gsl::not_null<std::mt19937*> generator, const Scalar<DataType>& pressure,\n    const tnsr::ii<DataType, 3>& spatial_metric);\n\ntemplate <typename DataType>\nScalar<DataType> random_divergence_cleaning_field(\n    gsl::not_null<std::mt19937*> generator, const DataType& used_for_size);\n}  // namespace hydro\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/MathFunctions/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace TestHelpers {\nnamespace MathFunctions {\nnamespace detail {\ntemplate <size_t VolumeDim, typename Fr, class... MemberArgs, class T>\nvoid check_impl(\n    const std::unique_ptr<MathFunction<VolumeDim, Fr>>& in_math_function,\n    const std::string& python_function_prefix, const T& used_for_size,\n    const std::array<std::pair<double, double>, 1> random_value_bounds,\n    const MemberArgs&... member_args) {\n  using MathFunc = MathFunction<VolumeDim, Fr>;\n  using CallOperatorFunction =\n      Scalar<T> (MathFunc::*)(const tnsr::I<T, VolumeDim, Fr>&) const;\n  using FirstDerivFunction =\n      tnsr::i<T, VolumeDim, Fr> (MathFunc::*)(const tnsr::I<T, VolumeDim, Fr>&)\n          const;\n  using SecondDerivFunction =\n      tnsr::ii<T, VolumeDim, Fr> (MathFunc::*)(const tnsr::I<T, VolumeDim, Fr>&)\n          const;\n  using ThirdDerivFunction = tnsr::iii<T, VolumeDim, Fr> (MathFunc::*)(\n      const tnsr::I<T, VolumeDim, Fr>&) const;\n\n  const auto member_args_tuple = std::make_tuple(member_args...);\n  const auto helper =\n      [&](const std::unique_ptr<MathFunc>& math_function) {\n        // need func variable to work around GCC bug\n        CallOperatorFunction func{&MathFunc::operator()};\n\n        INFO(\"Testing call operator...\");\n        pypp::check_with_random_values<1>(\n            func, *math_function, python_function_prefix, \"call_operator\",\n            random_value_bounds, member_args_tuple, used_for_size);\n        INFO(\"Done testing call operator...\");\n\n        FirstDerivFunction d_func{&MathFunc::first_deriv};\n        INFO(\"Testing first derivative...\");\n        pypp::check_with_random_values<1>(\n            d_func, *math_function, python_function_prefix, \"first_deriv\",\n            random_value_bounds, member_args_tuple, used_for_size);\n        INFO(\"Done testing first derivative...\");\n\n        SecondDerivFunction d2_func{&MathFunc::second_deriv};\n        INFO(\"Testing second derivative...\");\n        pypp::check_with_random_values<1>(\n            d2_func, *math_function, python_function_prefix, \"second_deriv\",\n            random_value_bounds, member_args_tuple, used_for_size);\n        INFO(\"Done testing second derivative...\");\n\n        ThirdDerivFunction d3_func{&MathFunc::third_deriv};\n        INFO(\"Testing third derivative...\");\n        pypp::check_with_random_values<1>(\n            d3_func, *math_function, python_function_prefix, \"third_deriv\",\n            random_value_bounds, member_args_tuple, used_for_size);\n        INFO(\"Done testing third derivative...\");\n\n        INFO(\"Done\\n\\n\");\n      };\n  helper(in_math_function);\n  helper(serialize_and_deserialize(in_math_function));\n\n  if constexpr (VolumeDim == 1) {\n    // Check that the tensor interface agrees with the double/DataVector\n    // interface\n\n    MAKE_GENERATOR(gen);\n    std::uniform_real_distribution<> real_dis(-1, 1);\n    const auto nn_generator = make_not_null(&gen);\n    const auto nn_distribution = make_not_null(&real_dis);\n\n    auto coords_tensor = make_with_random_values<tnsr::I<T, VolumeDim, Fr>>(\n        nn_generator, nn_distribution, used_for_size);\n    T coords_T = get<0>(coords_tensor);\n\n    CHECK_ITERABLE_APPROX(get((*in_math_function)(coords_tensor)),\n                          (*in_math_function)(coords_T));\n\n    const T deriv_from_tensor =\n        std::move(get<0>(in_math_function->first_deriv(coords_tensor)));\n    CHECK_ITERABLE_APPROX(deriv_from_tensor,\n                          in_math_function->first_deriv(coords_T));\n\n    const T second_deriv_from_tensor =\n        std::move(get<0, 0>(in_math_function->second_deriv(coords_tensor)));\n    CHECK_ITERABLE_APPROX(second_deriv_from_tensor,\n                          in_math_function->second_deriv(coords_T));\n\n    const T third_deriv_from_tensor =\n        std::move(get<0, 0, 0>(in_math_function->third_deriv(coords_tensor)));\n    CHECK_ITERABLE_APPROX(third_deriv_from_tensor,\n                          in_math_function->third_deriv(coords_T));\n  }\n}\n}  // namespace detail\n/// @{\n/*!\n * \\ingroup TestingFrameworkGroup\n * \\brief Test a MathFunction by comparing to python functions\n *\n * The python functions must be added to modules under\n * tests/Unit/PointwiseFunctions/MathFunctions/Python/ named for the\n * `python_function_prefix` (for example, `gaussian`, `sinusoid`, `pow_x`).\n * Each python function should be named `call_operator`, `first_deriv`,\n * `second_deriv`, or `third_deriv`. The prefix for each class of MathFunction\n * is arbitrary, but should generally be descriptive (e.g. 'gaussian',\n * 'sinusoid', 'pow_x') of the MathFunction.\n *\n * The `python_function_prefix` argument passed to `check` must be `PREFIX`. If\n * a MathFunction class has member variables set by its constructor, then these\n * member variables must be passed in as the last arguments to the `check`\n * function`. Each python function must take these same arguments as the\n * trailing arguments.\n */\ntemplate <class MathFunctionType, class T, class... MemberArgs>\nvoid check(std::unique_ptr<MathFunctionType> in_math_function,\n           const std::string& python_function_prefix, const T& used_for_size,\n           const std::array<std::pair<double, double>, 1> random_value_bounds,\n           const MemberArgs&... member_args) {\n  detail::check_impl(\n      std::unique_ptr<MathFunction<MathFunctionType::volume_dim,\n                                   typename MathFunctionType::frame>>(\n          std::move(in_math_function)),\n      python_function_prefix, used_for_size, random_value_bounds,\n      member_args...);\n}\n\ntemplate <class MathFunctionType, class T, class... MemberArgs>\nvoid check(MathFunctionType in_math_function,\n           const std::string& python_function_prefix, const T& used_for_size,\n           const std::array<std::pair<double, double>, 1> random_value_bounds,\n           const MemberArgs&... member_args) {\n  detail::check_impl(\n      std::unique_ptr<MathFunction<MathFunctionType::volume_dim,\n                                   typename MathFunctionType::frame>>(\n          std::make_unique<MathFunctionType>(std::move(in_math_function))),\n      python_function_prefix, used_for_size, random_value_bounds,\n      member_args...);\n}\n/// @}\n}  // namespace MathFunctions\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/PostNewtonian/BinaryTrajectories.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/PointwiseFunctions/PostNewtonian/BinaryTrajectories.hpp\"\n\n#include <array>\n#include <cmath>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nBinaryTrajectories::BinaryTrajectories(\n    double initial_separation,\n    const std::array<double, 3>& center_of_mass_velocity, bool newtonian)\n    : initial_separation_fourth_power_{square(square(initial_separation))},\n      center_of_mass_velocity_(center_of_mass_velocity),\n      newtonian_(newtonian) {}\n\ntemplate <typename DataType>\nDataType BinaryTrajectories::separation(const DataType& time) const {\n  const auto pn_correction_term = (newtonian_ ? 0.0 : 12.8) * time;\n  return pow(initial_separation_fourth_power_ - pn_correction_term, 0.25);\n}\n\ntemplate <typename DataType>\nDataType BinaryTrajectories::orbital_frequency(const DataType& time) const {\n  return pow(separation(time), -1.5);\n}\n\ntemplate <typename DataType>\nDataType BinaryTrajectories::angular_velocity(const DataType& time) const {\n  // This is d/dt(orbital_frequency) if we are using PN, but 0 if it's newtonian\n  const auto pn_correction_term =\n      (newtonian_ ? 0.0 : 4.8) *\n      pow(initial_separation_fourth_power_ - 12.8 * time, -1.375);\n  return orbital_frequency(time) + pn_correction_term * time;\n}\n\ntemplate <typename DataType>\nstd::array<tnsr::I<DataType, 3>, 2> BinaryTrajectories::positions(\n    const DataType& time) const {\n  return position_impl(time, separation(time));\n}\n\ntemplate <typename DataType>\nstd::array<tnsr::I<DataType, 3>, 2> BinaryTrajectories::positions_no_expansion(\n    const DataType& time) const {\n  // Separation stays constant while orbital frequency follows PN (or newtonian)\n  // values\n  const auto sep = make_with_value<DataType>(\n      time, pow(initial_separation_fourth_power_, 0.25));\n  return position_impl(time, sep);\n}\n\ntemplate <typename DataType>\nstd::array<tnsr::I<DataType, 3>, 2> BinaryTrajectories::position_impl(\n    const DataType& time, const DataType& separation) const {\n  std::array<tnsr::I<DataType, 3>, 2> result{};\n  const DataType orbital_freq = orbital_frequency(time);\n  get<0>(result[0]) = 0.5 * separation * cos(orbital_freq * time);\n  get<1>(result[0]) = 0.5 * separation * sin(orbital_freq * time);\n  get<0>(result[1]) = -get<0>(result[0]);\n  get<1>(result[1]) = -get<1>(result[0]);\n  // Apply COM velocity\n  for (size_t i = 0; i < 2; ++i) {\n    for (size_t j = 0; j < 2; ++j) {\n      gsl::at(result, i).get(j) += gsl::at(center_of_mass_velocity_, j) * time;\n    }\n  }\n  get<2>(result[0]) = center_of_mass_velocity_[2] * time;\n  get<2>(result[1]) = get<2>(result[0]);\n  return result;\n}\n\n#define DTYPE(data) BOOST_PP_TUPLE_ELEM(0, data)\n\n#define INSTANTIATE(_, data)                                              \\\n  template DTYPE(data) BinaryTrajectories::separation(const DTYPE(data)&) \\\n      const;                                                              \\\n  template DTYPE(data)                                                    \\\n      BinaryTrajectories::orbital_frequency(const DTYPE(data)&) const;    \\\n  template DTYPE(data)                                                    \\\n      BinaryTrajectories::angular_velocity(const DTYPE(data)&) const;     \\\n  template std::array<tnsr::I<DTYPE(data), 3>, 2>                         \\\n  BinaryTrajectories::positions(const DTYPE(data)&) const;                \\\n  template std::array<tnsr::I<DTYPE(data), 3>, 2>                         \\\n  BinaryTrajectories::positions_no_expansion(const DTYPE(data)&) const;\n\nGENERATE_INSTANTIATIONS(INSTANTIATE, (double, DataVector))\n\n#undef DTYPE\n#undef INSTANTIATE\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/PostNewtonian/BinaryTrajectories.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <array>\n#include <utility>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n\n/*!\n * \\brief Class to compute post-Newtonian trajectories\n *\n * \\details Computes the leading post-Newtonian trajectories \\f$x_1^i(t)\\f$\n * and \\f$x_2^i(t)\\f$ for an equal-mass binary with a total mass of 1.\n * Currently, this class implements the leading-order terms of the integral of\n * Eq. (226) and the square root of Eq. (228) of \\cite Blanchet:2013haa :\n * \\f{align}{\n *   r(t) &= \\left(r_0^4 - \\frac{64}{5}t\\right)^{1/4}, \\\\\n *   \\Omega(t) &= r^{-3/2}(t).\n * \\f}\n * In terms of these functions, the positions of objects 1 and 2 are\n * \\f{align}{\n *   x_1(t) &= -\\frac{r(t)}{2}\\cos\\left[\\Omega(t) t\\right] + v_x t, \\\\\n *   y_1(t) &= -\\frac{r(t)}{2}\\sin\\left[\\Omega(t) t\\right], + v_y t\\\\\n *   x_2(t) &= \\frac{r(t)}{2}\\cos\\left[\\Omega(t) t\\right], + v_x t\\\\\n *   y_2(t) &= \\frac{r(t)}{2}\\sin\\left[\\Omega(t) t\\right], + v_y t\\\\\n *   z_1(t) &= z_2(t) = v_z t.\n * \\f} These trajectories are useful for, e.g., testing a horizon-tracking\n * control system.\n *\n * \\parblock\n * \\note The trajectories could be generalized to higher post-Newtonian order if\n * needed.\n * \\endparblock\n * \\parblock\n * \\note If the `newtonian` argument is true, then this will just give Kepler's\n * third law\n * \\endparblock\n */\nclass BinaryTrajectories {\n public:\n  BinaryTrajectories(double initial_separation,\n                     const std::array<double, 3>& center_of_mass_velocity =\n                         std::array<double, 3>{{0.0, 0.0, 0.0}},\n                     bool newtonian = false);\n  BinaryTrajectories() = default;\n  BinaryTrajectories(BinaryTrajectories&&) = default;\n  BinaryTrajectories& operator=(BinaryTrajectories&&) = default;\n  BinaryTrajectories(const BinaryTrajectories&) = default;\n  BinaryTrajectories& operator=(const BinaryTrajectories&) = default;\n  ~BinaryTrajectories() = default;\n\n  /// Gives separation as function of time\n  template <typename DataType>\n  DataType separation(const DataType& time) const;\n\n  /// Gives orbital frequency \\f$f\\f$ as a function of time calculated from\n  /// Kepler's third law \\f$f^2\\propto\\frac{1}{a^3}\\f$ where \\f$a\\f$ is\n  /// calculated from `separation`.\n  template <typename DataType>\n  DataType orbital_frequency(const DataType& time) const;\n\n  /// Gives the angular velocity of the objects as a function of time.\n  /// Calculated by \\f$\\omega(t)=\\frac{d\\theta(t)}{dt}\\f$ where\n  /// \\f$\\theta(t)=f(t)t\\f$.\n  ///\n  /// \\note For the newtonian case, `orbital_frequency` and `angular_velocity`\n  /// give the same result because the orbital frequency is independent of time.\n  template <typename DataType>\n  DataType angular_velocity(const DataType& time) const;\n\n  /// Gives the positions of the two objects as a function of time.\n  template <typename DataType>\n  std::array<tnsr::I<DataType, 3>, 2> positions(const DataType& time) const;\n\n  /// Same as `positions`, except the separation remains constant (equal to the\n  /// initial separation).\n  ///\n  /// \\note This is useful for testing the rotation control system by itself,\n  /// because we want the frequency to vary, but the separation to remain the\n  /// same. This way, we don't have to worry about expansion effects.\n  template <typename DataType>\n  std::array<tnsr::I<DataType, 3>, 2> positions_no_expansion(\n      const DataType& time) const;\n\n private:\n  template <typename DataType>\n  std::array<tnsr::I<DataType, 3>, 2> position_impl(\n      const DataType& time, const DataType& separation) const;\n\n  double initial_separation_fourth_power_;\n  std::array<double, 3> center_of_mass_velocity_;\n  bool newtonian_;\n};\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/PostNewtonian/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PostNewtonianHelpers\")\n\nadd_spectre_library(${LIBRARY})\n\nspectre_target_sources(\n  ${LIBRARY}\n  PRIVATE\n  BinaryTrajectories.cpp\n  )\n\nspectre_target_headers(\n  ${LIBRARY}\n  INCLUDE_DIRECTORY ${CMAKE_SOURCE_DIR}/src\n  HEADERS\n  BinaryTrajectories.hpp\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Utilities\n  PUBLIC\n  DataStructures\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/PostNewtonian/Python/Bindings.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n#include <pybind11/numpy.h>\n#include <pybind11/pybind11.h>\n#include <pybind11/stl.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/PointwiseFunctions/PostNewtonian/BinaryTrajectories.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\nnamespace py = pybind11;\n\nPYBIND11_MODULE(_Pybindings, m) {  // NOLINT\n  enable_segfault_handler();\n  py::module::import(\"spectre.DataStructures\");\n  py::module::import(\"spectre.DataStructures.Tensor\");\n  py::class_<BinaryTrajectories>(m, \"BinaryTrajectories\")\n      .def(py::init<double, const std::array<double, 3>&, bool>(),\n           py::arg(\"initial_separation\"),\n           py::arg(\"center_of_mass_velocity\") =\n               std::array<double, 3>{{0.0, 0.0, 0.0}},\n           py::arg(\"newtonian\") = false)\n      .def(\"separation\", &BinaryTrajectories::separation<double>,\n           py::arg(\"time\"))\n      .def(\"separation\", &BinaryTrajectories::separation<DataVector>,\n           py::arg(\"time\"))\n      .def(\"orbital_frequency\", &BinaryTrajectories::orbital_frequency<double>,\n           py::arg(\"time\"))\n      .def(\"orbital_frequency\",\n           &BinaryTrajectories::orbital_frequency<DataVector>, py::arg(\"time\"))\n      .def(\"angular_velocity\", &BinaryTrajectories::angular_velocity<double>,\n           py::arg(\"time\"))\n      .def(\"angular_velocity\",\n           &BinaryTrajectories::angular_velocity<DataVector>, py::arg(\"time\"))\n      .def(\"positions\", &BinaryTrajectories::positions<double>, py::arg(\"time\"))\n      .def(\"positions\", &BinaryTrajectories::positions<DataVector>,\n           py::arg(\"time\"))\n      .def(\"positions_no_expansion\",\n           &BinaryTrajectories::positions_no_expansion<double>, py::arg(\"time\"))\n      .def(\"positions_no_expansion\",\n           &BinaryTrajectories::positions_no_expansion<DataVector>,\n           py::arg(\"time\"));\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/PostNewtonian/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"PyPostNewtonianHelpers\")\n\nspectre_python_add_module(\n  PostNewtonian\n  MODULE_PATH \"testing\"\n  LIBRARY_NAME ${LIBRARY}\n  SOURCES\n  Bindings.cpp\n  PYTHON_FILES\n  __init__.py\n  )\n\nspectre_python_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  PostNewtonianHelpers\n  pybind11::module\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/PointwiseFunctions/PostNewtonian/Python/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ._Pybindings import *\n"
  },
  {
    "path": "tests/Unit/Helpers/TestHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n/// \\brief Collection of classes and functions useful for testing.\nnamespace TestHelpers{}\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Helpers\")\n\nset(LIBRARY_SOURCES\n  Test_MakeWithRandomValues.cpp\n  Test_RandomUnitNormal.cpp\n  Test_MakeRandomVectorInMagnitudeRange.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  GeneralRelativityHelpers\n  Utilities\n  )\n\nadd_subdirectory(Domain)\nadd_subdirectory(IO)\nadd_subdirectory(PointwiseFunctions)\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Domain/Amr/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DomainAmrHelpers\")\n\nset(LIBRARY_SOURCES\n  Test_NeighborFlagHelpers.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Amr\n  DomainAmrHelpers\n  DomainStructure\n  DomainStructureHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Domain/Amr/Test_NeighborFlagHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Helpers/Domain/Amr/NeighborFlagHelpers.hpp\"\n#include \"Helpers/Domain/Structure/NeighborHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace TestHelpers::amr {\nnamespace {\nvoid test_valid_neighbor_info_1d() {\n  const ElementId<1> root{0};\n  const ElementId<1> neighbor_root{2};\n  const ElementId<1> lower_child{0, std::array{SegmentId{1, 0}}};\n  const ElementId<1> upper_child{0, std::array{SegmentId{1, 1}}};\n  const ElementId<1> neighbor_child{2, std::array{SegmentId{1, 0}}};\n  const ElementId<1> abutting_nibling{0, std::array{SegmentId{2, 2}}};\n  const auto split = std::array{::amr::Flag::Split};\n  const auto join = std::array{::amr::Flag::Join};\n  const auto stay = std::array{::amr::Flag::IncreaseResolution};\n  const Mesh<1> mesh{2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto all_allowed = [&join, &stay, &split,\n                            &mesh](const ElementId<1>& neighbor_id) {\n    return std::vector{neighbor_info_t<1>{{neighbor_id, {join, mesh}}},\n                       neighbor_info_t<1>{{neighbor_id, {stay, mesh}}},\n                       neighbor_info_t<1>{{neighbor_id, {split, mesh}}}};\n  };\n  const auto join_not_allowed = [&stay, &split,\n                                 &mesh](const ElementId<1>& neighbor_id) {\n    return std::vector{neighbor_info_t<1>{{neighbor_id, {stay, mesh}}},\n                       neighbor_info_t<1>{{neighbor_id, {split, mesh}}}};\n  };\n  const auto split_not_allowed = [&join, &stay,\n                                  &mesh](const ElementId<1>& neighbor_id) {\n    return std::vector{neighbor_info_t<1>{{neighbor_id, {join, mesh}}},\n                       neighbor_info_t<1>{{neighbor_id, {stay, mesh}}}};\n  };\n  const OrientationMap<1> aligned = OrientationMap<1>::create_aligned();\n  CHECK(valid_neighbor_info(\n            root, stay,\n            Neighbors<1>{std::unordered_set{neighbor_root}, aligned}) ==\n        join_not_allowed(neighbor_root));\n  CHECK(valid_neighbor_info(\n            upper_child, stay,\n            Neighbors<1>{std::unordered_set{neighbor_root}, aligned}) ==\n        join_not_allowed(neighbor_root));\n  CHECK(valid_neighbor_info(\n            lower_child, stay,\n            Neighbors<1>{std::unordered_set{upper_child}, aligned}) ==\n        join_not_allowed(upper_child));\n  CHECK(valid_neighbor_info(\n            lower_child, stay,\n            Neighbors<1>{std::unordered_set{abutting_nibling}, aligned}) ==\n        split_not_allowed(abutting_nibling));\n  CHECK(valid_neighbor_info(\n            root, stay,\n            Neighbors<1>{std::unordered_set{neighbor_child}, aligned}) ==\n        split_not_allowed(neighbor_child));\n  CHECK(valid_neighbor_info(\n            upper_child, stay,\n            Neighbors<1>{std::unordered_set{neighbor_child}, aligned}) ==\n        all_allowed(neighbor_child));\n  CHECK(valid_neighbor_info(\n            abutting_nibling, stay,\n            Neighbors<1>{std::unordered_set{lower_child}, aligned}) ==\n        join_not_allowed(lower_child));\n}\n\nvoid test_valid_neighbor_info_2d() {\n  ElementId<2> element_id{0, std::array{SegmentId{2, 3}, SegmentId{3, 4}}};\n  const auto join_join = std::array{::amr::Flag::Join, ::amr::Flag::Join};\n  const auto join_stay =\n      std::array{::amr::Flag::Join, ::amr::Flag::IncreaseResolution};\n  const auto stay_join =\n      std::array{::amr::Flag::IncreaseResolution, ::amr::Flag::Join};\n  const auto stay_stay = std::array{::amr::Flag::IncreaseResolution,\n                                    ::amr::Flag::IncreaseResolution};\n  const auto stay_split =\n      std::array{::amr::Flag::IncreaseResolution, ::amr::Flag::Split};\n  const auto split_stay =\n      std::array{::amr::Flag::Split, ::amr::Flag::IncreaseResolution};\n  const auto split_split = std::array{::amr::Flag::Split, ::amr::Flag::Split};\n  const OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n  ElementId<2> neighbor_0{1, std::array{SegmentId{2, 0}, SegmentId{3, 4}}};\n  const Neighbors<2> neighbors_0{std::unordered_set{neighbor_0}, aligned};\n  ::TestHelpers::domain::check_neighbors(neighbors_0, element_id,\n                                         Direction<2>::upper_xi());\n  const Mesh<2> mesh{\n      {{2, 3}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n  CHECK(valid_neighbor_info(element_id, stay_stay, neighbors_0) ==\n        std::vector{neighbor_info_t<2>{{neighbor_0, {join_join, mesh}}},\n                    neighbor_info_t<2>{{neighbor_0, {join_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_0, {stay_join, mesh}}},\n                    neighbor_info_t<2>{{neighbor_0, {stay_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_0, {stay_split, mesh}}},\n                    neighbor_info_t<2>{{neighbor_0, {split_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_0, {split_split, mesh}}}});\n  ElementId<2> neighbor_1{1, std::array{SegmentId{2, 0}, SegmentId{4, 8}}};\n  ElementId<2> neighbor_2{1, std::array{SegmentId{2, 0}, SegmentId{4, 9}}};\n  const Neighbors<2> neighbors_1_2{std::unordered_set{neighbor_1, neighbor_2},\n                                   aligned};\n  ::TestHelpers::domain::check_neighbors(neighbors_1_2, element_id,\n                                         Direction<2>::upper_xi());\n  CHECK(valid_neighbor_info(element_id, stay_stay, neighbors_1_2) ==\n        std::vector{neighbor_info_t<2>{{neighbor_1, {join_join, mesh}},\n                                       {neighbor_2, {join_join, mesh}}},\n                    neighbor_info_t<2>{{neighbor_1, {join_stay, mesh}},\n                                       {neighbor_2, {join_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_1, {join_stay, mesh}},\n                                       {neighbor_2, {stay_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_1, {stay_join, mesh}},\n                                       {neighbor_2, {stay_join, mesh}}},\n                    neighbor_info_t<2>{{neighbor_1, {stay_stay, mesh}},\n                                       {neighbor_2, {stay_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_1, {stay_stay, mesh}},\n                                       {neighbor_2, {split_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_1, {split_stay, mesh}},\n                                       {neighbor_2, {stay_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_1, {split_stay, mesh}},\n                                       {neighbor_2, {split_stay, mesh}}}});\n  ElementId<2> neighbor_3{1, std::array{SegmentId{2, 0}, SegmentId{2, 2}}};\n  const Neighbors<2> neighbors_3{std::unordered_set{neighbor_3}, aligned};\n  ::TestHelpers::domain::check_neighbors(neighbors_3, element_id,\n                                         Direction<2>::upper_xi());\n  CHECK(valid_neighbor_info(element_id, stay_stay, neighbors_3) ==\n        std::vector{neighbor_info_t<2>{{neighbor_3, {join_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_3, {stay_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_3, {stay_split, mesh}}},\n                    neighbor_info_t<2>{{neighbor_3, {split_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_3, {split_split, mesh}}}});\n  ElementId<2> neighbor_4{1, std::array{SegmentId{2, 2}, SegmentId{2, 3}}};\n  const OrientationMap<2> rotated{\n      std::array{Direction<2>::lower_eta(), Direction<2>::upper_xi()}};\n  const Neighbors<2> neighbors_4{std::unordered_set{neighbor_4}, rotated};\n  ::TestHelpers::domain::check_neighbors(neighbors_4, element_id,\n                                         Direction<2>::upper_xi());\n  CHECK(valid_neighbor_info(element_id, stay_stay, neighbors_4) ==\n        std::vector{neighbor_info_t<2>{{neighbor_4, {stay_join, mesh}}},\n                    neighbor_info_t<2>{{neighbor_4, {stay_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_4, {stay_split, mesh}}},\n                    neighbor_info_t<2>{{neighbor_4, {split_stay, mesh}}},\n                    neighbor_info_t<2>{{neighbor_4, {split_split, mesh}}}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"TestHelpers.Domain.Amr.NeighborFlagHelpers\",\n                  \"[Domain][Unit]\") {\n  test_valid_neighbor_info_1d();\n  test_valid_neighbor_info_2d();\n  // testing 3d does not test anything new\n}\n}  // namespace TestHelpers::amr\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Domain/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Amr)\nadd_subdirectory(Structure)\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Domain/Structure/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DomainStructureHelpers\")\n\nset(LIBRARY_SOURCES\n  Test_NeighborHelpers.cpp\n  Test_OrientationMapHelpers.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DomainStructure\n  DomainStructureHelpers\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Domain/Structure/Test_NeighborHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/Structure/NeighborHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace TestHelpers::domain {\nnamespace {\nvoid test_valid_sibling_side_segments() {\n  for (size_t l = 1; l < 5; ++l) {\n    for (size_t i = 0; i < two_to_the(l); ++i) {\n      SegmentId my_id{l, i};\n      const auto valid_segments = valid_neighbor_segments(my_id);\n      if (i % 2 == 0) {\n        CHECK(valid_segments ==\n              std::vector{SegmentId{l, i + 1}, SegmentId{l + 1, 2 * i + 2}});\n      } else {\n        CHECK(valid_segments ==\n              std::vector{SegmentId{l, i - 1}, SegmentId{l + 1, 2 * i - 1}});\n      }\n    }\n  }\n}\n\nvoid test_valid_nonsibling_side_segments() {\n  CHECK(valid_neighbor_segments(SegmentId{1, 0}, FaceType::Periodic) ==\n        std::vector{SegmentId{1, 1}, SegmentId{2, 3}});\n  CHECK(valid_neighbor_segments(SegmentId{1, 1}, FaceType::Periodic) ==\n        std::vector{SegmentId{1, 0}, SegmentId{2, 0}});\n  for (size_t l = 1; l < 5; ++l) {\n    SegmentId lower_boundary_segment{l, 0};\n    SegmentId upper_boundary_segment{l, two_to_the(l) - 1};\n    CHECK(valid_neighbor_segments(lower_boundary_segment, FaceType::External) ==\n          std::vector<SegmentId>{});\n    CHECK(valid_neighbor_segments(upper_boundary_segment, FaceType::External) ==\n          std::vector<SegmentId>{});\n    CHECK(valid_neighbor_segments(lower_boundary_segment, FaceType::Block) ==\n          std::vector{SegmentId{l - 1, two_to_the(l - 1) - 1},\n                      SegmentId{l, two_to_the(l) - 1},\n                      SegmentId{l + 1, two_to_the(l + 1) - 1}});\n    CHECK(\n        valid_neighbor_segments(upper_boundary_segment, FaceType::Block) ==\n        std::vector{SegmentId{l - 1, 0}, SegmentId{l, 0}, SegmentId{l + 1, 0}});\n    if (l > 1) {\n      CHECK(\n          valid_neighbor_segments(lower_boundary_segment, FaceType::Periodic) ==\n          std::vector{SegmentId{l - 1, two_to_the(l - 1) - 1},\n                      SegmentId{l, two_to_the(l) - 1},\n                      SegmentId{l + 1, two_to_the(l + 1) - 1}});\n      CHECK(\n          valid_neighbor_segments(upper_boundary_segment, FaceType::Periodic) ==\n          std::vector{SegmentId{l - 1, 0}, SegmentId{l, 0},\n                      SegmentId{l + 1, 0}});\n    }\n  }\n  for (size_t l = 2; l < 5; ++l) {\n    for (size_t i = 1; i < two_to_the(l) - 1; ++i) {\n      SegmentId my_id{l, i};\n      const auto valid_segments =\n          valid_neighbor_segments(my_id, FaceType::Internal);\n      if (i % 2 == 0) {\n        CHECK(valid_segments == std::vector{SegmentId{l - 1, i / 2 - 1},\n                                            SegmentId{l, i - 1},\n                                            SegmentId{l + 1, 2 * i - 1}});\n      } else {\n        CHECK(valid_segments == std::vector{SegmentId{l - 1, i / 2 + 1},\n                                            SegmentId{l, i + 1},\n                                            SegmentId{l + 1, 2 * i + 2}});\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nstd::vector<ElementId<Dim>> element_ids_to_test() {\n  static constexpr size_t max_level = 2;\n  std::vector<ElementId<Dim>> result{};\n  for (size_t lx = 0; lx <= max_level; ++lx) {\n    for (size_t ix = 0; ix < two_to_the(lx); ++ix) {\n      if constexpr (Dim == 1) {\n        result.emplace_back(0, std::array{SegmentId{lx, ix}});\n      } else {\n        for (size_t ly = 0; ly <= max_level; ++ly) {\n          for (size_t iy = 0; iy < two_to_the(ly); ++iy) {\n            if constexpr (Dim == 2) {\n              result.emplace_back(\n                  0, std::array{SegmentId{lx, ix}, SegmentId{ly, iy}});\n            } else {\n              for (size_t lz = 0; lz <= max_level; ++lz) {\n                for (size_t iz = 0; iz < two_to_the(lz); ++iz) {\n                  result.emplace_back(\n                      0, std::array{SegmentId{lx, ix}, SegmentId{ly, iy},\n                                    SegmentId{lz, iz}});\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test_valid_neighbors(const gsl::not_null<std::mt19937*> generator) {\n  for (const auto& element_id : element_ids_to_test<Dim>()) {\n    for (const auto& direction : Direction<Dim>::all_directions()) {\n      const size_t dim = direction.dimension();\n      const Side side = direction.side();\n      const SegmentId& normal_segment = element_id.segment_id(dim);\n      const double endpoint = normal_segment.endpoint(side);\n      if (endpoint == 1.0 or endpoint == -1.0) {\n        for (const auto face_type :\n             std::array{FaceType::Periodic, FaceType::Block}) {\n          for (const auto& neighbors :\n               valid_neighbors(generator, element_id, direction, face_type)) {\n            check_neighbors(neighbors, element_id, direction);\n          }\n        }\n      } else {\n        for (const auto& neighbors :\n             valid_neighbors(generator, element_id, direction)) {\n          check_neighbors(neighbors, element_id, direction);\n        }\n      }\n    }\n  }\n}\n\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"TestHelpers.Domain.Structure.NeighborHelpers\",\n                  \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test_valid_sibling_side_segments();\n  test_valid_nonsibling_side_segments();\n  test_valid_neighbors<1>(make_not_null(&generator));\n  test_valid_neighbors<2>(make_not_null(&generator));\n  test_valid_neighbors<3>(make_not_null(&generator));\n}\n}  // namespace TestHelpers::domain\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Domain/Structure/Test_OrientationMapHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace TestHelpers::domain {\nnamespace {\ntemplate <size_t Dim>\nvoid test_valid_orientation_maps() {\n  auto orientation_maps = valid_orientation_maps<Dim>();\n  constexpr size_t expected_size =\n      (Dim == 1 ? 2 : two_to_the(Dim - 1) * factorial(Dim));\n  CHECK(orientation_maps.size() == expected_size);\n  for (const auto& orientation_map : orientation_maps) {\n    if constexpr (Dim > 1) {\n      CHECK(get(determinant(discrete_rotation_jacobian(orientation_map))) ==\n            1.0);\n    }\n    CHECK(alg::count(orientation_maps, orientation_map) == 1);\n  }\n}\ntemplate <size_t Dim>\nvoid test_random_orientation_maps(gsl::not_null<std::mt19937*> generator) {\n  const auto valid_maps = valid_orientation_maps<Dim>();\n  for (size_t s = 0; s < 6; ++s) {\n    const auto random_maps = random_orientation_maps<Dim>(s, generator);\n    const size_t expected_size = std::min(s, valid_maps.size());\n    CHECK(random_maps.size() == expected_size);\n    for (const auto& random_map : random_maps) {\n      CHECK(alg::count(valid_maps, random_map) == 1);\n      CHECK(alg::count(random_maps, random_map) == 1);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"TestHelpers.Domain.OrientationMapHelpers\",\n                  \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test_valid_orientation_maps<1>();\n  test_valid_orientation_maps<2>();\n  test_valid_orientation_maps<3>();\n  test_random_orientation_maps<1>(make_not_null(&generator));\n  test_random_orientation_maps<2>(make_not_null(&generator));\n  test_random_orientation_maps<3>(make_not_null(&generator));\n}\n}  // namespace TestHelpers::domain\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/IO/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Observers)\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/IO/Observers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ObserverHelpers\")\n\nset(LIBRARY_SOURCES\n  Test_MockH5.cpp\n  Test_MockWriteReductionDataRow.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ObserverHelpers\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/IO/Observers/Test_MockH5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"Helpers/IO/Observers/MockH5.hpp\"\n\nnamespace TestHelpers::observers {\nnamespace {\nvoid test_mock_dat() {\n  MockDat my_dat{};\n\n  const std::vector<std::string> legend{\"This\", \"is\", \"SPARTA\"};\n  const std::vector<double> data{{0.0, 1.3, 6.7}};\n\n  my_dat.append(legend, data);\n\n  const auto& dat_legend = my_dat.get_legend();\n  CHECK(dat_legend == legend);\n\n  const auto& matrix_data = my_dat.get_data();\n  CHECK(matrix_data == Matrix{{0.0, 1.3, 6.7}});\n\n  CHECK_THROWS_WITH(\n      ([&my_dat, &legend]() {\n        const std::vector<double> bad_data(1, 0.0);\n        my_dat.append(legend, bad_data);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Size of supplied data does not match number of columns \"\n          \"in the existing matrix.\"));\n  CHECK_THROWS_WITH(\n      ([&my_dat, &data]() {\n        const std::vector<std::string> bad_legend{\"Bad\"};\n        my_dat.append(bad_legend, data);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Supplied legend is not the same as the existing legend.\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      MockDat dat{};\n                      auto& error_data = dat.get_data();\n                      (void)error_data;\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot get data. Append some data first.\"));\n  CHECK_THROWS_WITH(([]() {\n                      MockDat dat{};\n                      auto& error_legend = dat.get_legend();\n                      (void)error_legend;\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot get legend. Append some data first.\"));\n}\n\nvoid test_mock_h5() {\n  MockH5File file{};\n\n  const std::string path{\"/my/path\"};\n  file.try_insert(path);\n\n  auto& gotten_dat = file.get_dat(path);\n  const std::vector<std::string> legend{\"Time\"};\n  const std::vector<double> data{0.0};\n  gotten_dat.append(legend, data);\n\n  const auto& const_dat_ref = file.get_dat(path);\n  CHECK(const_dat_ref.get_legend() == legend);\n  CHECK(const_dat_ref.get_data() == Matrix{{0.0}});\n\n  CHECK_THROWS_WITH(\n      ([&file]() {\n        auto& nonexistent_dat = file.get_dat(\"/bad/path\");\n        (void)nonexistent_dat;\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot get /bad/path from MockH5File. Path does not exist.\"));\n  CHECK_THROWS_WITH(\n      ([&file]() {\n        const auto& nonexistent_dat = file.get_dat(\"/bad/path\");\n        (void)nonexistent_dat;\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot get /bad/path from MockH5File. Path does not exist.\"));\n}\n\nSPECTRE_TEST_CASE(\"Test.TestHelpers.IO.Observers.MockH5\", \"[IO][Unit]\") {\n  test_mock_dat();\n  test_mock_h5();\n}\n}  // namespace\n}  // namespace TestHelpers::observers\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/IO/Observers/Test_MockWriteReductionDataRow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/IO/Observers/MockH5.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TestHelpers::observers {\nnamespace {\nstruct TestMetavariables;\n\nusing mock_observer_writer = MockObserverWriter<TestMetavariables>;\nstruct TestMetavariables {\n  using component_list = tmpl::list<mock_observer_writer>;\n\n};\n\nvoid run_test() {\n  ActionTesting::MockRuntimeSystem<TestMetavariables> runner{{}};\n\n  // [initialize_component]\n  ActionTesting::emplace_nodegroup_component_and_initialize<\n      mock_observer_writer>(make_not_null(&runner), {});\n  // [initialize_component]\n\n  REQUIRE(ActionTesting::tag_is_retrievable<mock_observer_writer,\n                                            MockReductionFileTag>(runner, 0));\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& cache = ActionTesting::cache<mock_observer_writer>(runner, 0);\n\n  auto& proxy = Parallel::get_parallel_component<mock_observer_writer>(cache);\n\n  const std::string subfile_path{\"/path/to/something\"};\n  const std::vector<std::string> legend{{\"Time\"s, \"Value\"s}};\n\n  Parallel::threaded_action<\n      ::observers::ThreadedActions::WriteReductionDataRow>(\n      proxy[0], subfile_path, legend, std::make_tuple(1.0, 9.3));\n\n  ActionTesting::invoke_queued_threaded_action<mock_observer_writer>(\n      make_not_null(&runner), 0);\n\n  // [check_mock_writer_data]\n  auto& mock_h5_file =\n      ActionTesting::get_databox_tag<mock_observer_writer,\n                                     MockReductionFileTag>(runner, 0);\n\n  const auto& mock_dat_file = mock_h5_file.get_dat(subfile_path);\n  CHECK(mock_dat_file.get_legend() == legend);\n  CHECK(mock_dat_file.get_data() == Matrix{{1.0, 9.3}});\n  // [check_mock_writer_data]\n}\n\nSPECTRE_TEST_CASE(\"Test.TestHelpers.IO.Observers.MockWriteReductionDataRow\",\n                  \"[IO][Unit]\") {\n  run_test();\n}\n}  // namespace\n}  // namespace TestHelpers::observers\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/PointwiseFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(PostNewtonian)\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/PointwiseFunctions/PostNewtonian/BinaryTrajectories.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\ninitial_separation = 15.366\ninitial_velocity = np.array([0.1, -0.2, 0.3])\n\n\ndef separation(time, init_sep, newtonian):\n    correction = 0.0 if newtonian else 12.8 * time\n    return (init_sep**4 - correction) ** 0.25\n\n\ndef orbital_frequency(time, init_sep, newtonian):\n    return separation(time, init_sep, newtonian) ** -1.5\n\n\ndef angular_velocity(time, init_sep, newtonian):\n    correction = (\n        0.0 if newtonian else 4.8 * (init_sep**4 - 12.8 * time) ** -1.375\n    )\n    return orbital_frequency(time, init_sep, newtonian) + correction * time\n\n\ndef position_helper(time, init_sep, newtonian, sign, no_expansion):\n    sep = (\n        separation(time, init_sep, True)\n        if no_expansion\n        else separation(time, init_sep, newtonian)\n    )\n    freq = orbital_frequency(time, init_sep, newtonian)\n    return np.array(\n        [\n            sign * 0.5 * sep * np.cos(freq * time) + initial_velocity[0] * time,\n            sign * 0.5 * sep * np.sin(freq * time) + initial_velocity[1] * time,\n            initial_velocity[2] * time,\n        ]\n    )\n\n\ndef positions1(time, newtonian, no_expansion):\n    newt = True if newtonian > 0.0 else False\n    no_exp = True if no_expansion > 0.0 else False\n    return position_helper(time, initial_separation, newt, +1.0, no_exp)\n\n\ndef positions2(time, newtonian, no_expansion):\n    newt = True if newtonian > 0.0 else False\n    no_exp = True if no_expansion > 0.0 else False\n    return position_helper(time, initial_separation, newt, -1.0, no_exp)\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/PointwiseFunctions/PostNewtonian/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_PostNewtonianHelpers\")\n\nset(LIBRARY_SOURCES\n  Test_BinaryTrajectories.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  PostNewtonianHelpers\n  )\n\nspectre_add_python_bindings_test(\n  \"Unit.PostNewtonian.Python.BinaryTrajectories\"\n  \"Test_BinaryTrajectories.py\"\n  \"unit;Python\"\n  PyPostNewtonianHelpers\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/PointwiseFunctions/PostNewtonian/Test_BinaryTrajectories.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <random>\n#include <tuple>\n#include <utility>\n\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/PointwiseFunctions/PostNewtonian/BinaryTrajectories.hpp\"\n\nnamespace {\n// must match value in BinaryTrajectories.py\nconstexpr double initial_separation{15.366};\nconst std::array<double, 3> initial_velocity{0.1, -0.2, 0.3};\n\n// Since we can't pass bools to these functions, pass more doubles.\n// - newtonian: If it's positive, newtonian is true. If it's negative, newtonian\n//   is false.\n// - no_expansion: If it's positive, call the `positions_no_expansion` function.\n//   If it's negative, just call the `positions` function.\ntnsr::I<double, 3> positions1(const double time, const double newtonian,\n                              const double no_expansion) {\n  const bool newt = newtonian > 0.0 ? true : false;\n  BinaryTrajectories expected{initial_separation, initial_velocity, newt};\n  if (no_expansion > 0.0) {\n    return expected.positions_no_expansion(time)[0];\n  } else {\n    return expected.positions(time)[0];\n  }\n}\n\ntnsr::I<double, 3> positions2(const double time, const double newtonian,\n                              const double no_expansion) {\n  const bool newt = newtonian > 0.0 ? true : false;\n  BinaryTrajectories expected{initial_separation, initial_velocity, newt};\n  if (no_expansion > 0.0) {\n    return expected.positions_no_expansion(time)[1];\n  } else {\n    return expected.positions(time)[1];\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Test.TestHelpers.PostNewtonian.BinaryTrajectories\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"Helpers/Tests/PointwiseFunctions/PostNewtonian/\");\n\n  for (const bool newtonian : {true, false}) {\n    const BinaryTrajectories expected{initial_separation, initial_velocity,\n                                      newtonian};\n    pypp::check_with_random_values<1>(\n        &BinaryTrajectories::separation<double>,\n        BinaryTrajectories{initial_separation, initial_velocity, newtonian},\n        \"BinaryTrajectories\", {\"separation\"}, {{{-100.0, 100.0}}},\n        std::make_tuple(initial_separation, newtonian), initial_separation);\n    pypp::check_with_random_values<1>(\n        &BinaryTrajectories::orbital_frequency<double>,\n        BinaryTrajectories{initial_separation, initial_velocity, newtonian},\n        \"BinaryTrajectories\", {\"orbital_frequency\"}, {{{-100.0, 100.0}}},\n        std::make_tuple(initial_separation, newtonian), initial_separation);\n    pypp::check_with_random_values<1>(\n        &BinaryTrajectories::angular_velocity<double>,\n        BinaryTrajectories{initial_separation, initial_velocity, newtonian},\n        \"BinaryTrajectories\", {\"angular_velocity\"}, {{{-100.0, 100.0}}},\n        std::make_tuple(initial_separation, newtonian), initial_separation);\n  }\n  // Run these a few times so we get a good mix\n  for (size_t i = 0; i < 8; i++) {\n    pypp::check_with_random_values<1>(&positions1, \"BinaryTrajectories\",\n                                      \"positions1\", {{{-100.0, 100.0}}},\n                                      initial_separation);\n    pypp::check_with_random_values<1>(&positions2, \"BinaryTrajectories\",\n                                      \"positions2\", {{{-100.0, 100.0}}},\n                                      initial_separation);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/PointwiseFunctions/PostNewtonian/Test_BinaryTrajectories.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.testing.PostNewtonian import BinaryTrajectories\n\n\nclass TestBinaryTrajectories(unittest.TestCase):\n    def test_binary_trajectories(self):\n        # This class is tested in C++. Only check here that Pybindings work.\n        binary_trajectories = BinaryTrajectories(initial_separation=16)\n        self.assertEqual(binary_trajectories.separation(0.0), 16.0)\n        self.assertEqual(binary_trajectories.orbital_frequency(0.0), 0.015625)\n        self.assertEqual(binary_trajectories.angular_velocity(0.0), 0.015625)\n        npt.assert_allclose(\n            binary_trajectories.positions(0.0),\n            ([8.0, 0.0, 0.0], [-8.0, 0.0, 0.0]),\n        )\n        # Test vectorized interface\n        times = np.linspace(0.0, 10.0, 10)\n        npt.assert_allclose(\n            binary_trajectories.positions(times),\n            np.transpose(\n                [binary_trajectories.positions(t) for t in times],\n                axes=(1, 2, 0),\n            ),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Test_MakeRandomVectorInMagnitudeRange.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeRandomVectorInMagnitudeRange.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n\nnamespace {\nvoid check_random_values(const double v, const double bound1,\n                         const double bound2) {\n  CHECK(approx(v) >= std::min(bound1, bound2));\n  CHECK(approx(v) <= std::max(bound1, bound2));\n}\n\n// clang-tidy is wrong, this is a function definition\ntemplate <typename T>\nvoid check_random_values(const T& c,             // NOLINT\n                         const double bound1,    // NOLINT\n                         const double bound2) {  // NOLINT\n  for (const auto& v : c) {\n    check_random_values(v, bound1, bound2);\n  }\n}\n\ntemplate <typename... Tags>\nvoid check_random_values(const Variables<tmpl::list<Tags...>>& v,\n                         const double bound1, const double bound2) {\n  expand_pack((\n      check_random_values<decltype(get<Tags>(v))>(get<Tags>(v), bound1, bound2),\n      '0')...);\n}\n\n// Compute Magnitude\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> magnitude_auto_invert(\n    const tnsr::I<DataType, Dim, Fr>& vector,\n    const tnsr::ii<DataType, Dim, Fr>& metric) {\n  return magnitude(vector, metric);\n}\n\ntemplate <typename DataType, size_t Dim, typename Fr>\nScalar<DataType> magnitude_auto_invert(\n    const tnsr::i<DataType, Dim, Fr>& covector,\n    const tnsr::ii<DataType, Dim, Fr>& metric) {\n  const tnsr::II<DataType, Dim, Fr> inverse_metric =\n      determinant_and_inverse(metric).second;\n\n  return magnitude(covector, inverse_metric);\n}\n\ntemplate <size_t Dim, UpLo Ul, typename DataType>\nvoid test_range(const gsl::not_null<std::mt19937*> generator,\n                const DataType& used_for_size) {\n  using Fr = Frame::Inertial;\n\n  std::uniform_real_distribution<> distribution1(0.0, 100.0);\n  double r1 = distribution1(*generator);\n  std::uniform_real_distribution<> distribution2(r1, 100.0);\n  double r2 = distribution2(*generator);\n  INFO(\"Interval is [\" << r1 << \",\" << r2 << \"]\");\n\n  //****** FIRST TEST WITH EUCLIDIAN METRIC ******//\n\n  auto vector =\n      make_random_vector_in_magnitude_range_flat<DataType, Dim, Ul, Fr>(\n          generator, used_for_size, r1, r2);\n  auto vector_mag = magnitude(vector);\n\n  check_random_values(vector_mag, r1, r2);\n\n  //****** NOW TEST WITH RANDOM METRIC ******//\n\n  const tnsr::ii<DataType, Dim, Fr> metric =\n      TestHelpers::gr::random_spatial_metric<Dim, DataType, Fr>(generator,\n                                                                used_for_size);\n\n  vector = make_random_vector_in_magnitude_range<DataType, Dim, Ul, Fr>(\n      generator, metric, r1, r2);\n\n  vector_mag = magnitude_auto_invert(vector, metric);\n\n  check_random_values(vector_mag, r1, r2);\n\n  //****** TEST EDGE CASES ******//\n\n  r2 = r1;\n  INFO(\"Interval is [\" << r1 << \",\" << r2 << \"]\");\n\n  vector = make_random_vector_in_magnitude_range<DataType, Dim, Ul, Fr>(\n      generator, metric, r1, r2);\n\n  vector_mag = magnitude_auto_invert(vector, metric);\n\n  check_random_values(vector_mag, r1, r2);\n\n  r1 = 0.0;\n  r2 = 0.0;\n  INFO(\"Interval is [\" << r1 << \",\" << r2 << \"]\");\n\n  vector = make_random_vector_in_magnitude_range<DataType, Dim, Ul, Fr>(\n      generator, metric, r1, r2);\n\n  vector_mag = magnitude_auto_invert(vector, metric);\n\n  check_random_values(vector_mag, r1, r2);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Test.TestHelpers.MakeRandomVectorInMagnitudeRange\",\n                  \"[Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  SECTION(\"double\") {\n    const double d = std::numeric_limits<double>::signaling_NaN();\n    test_range<1, UpLo::Up>(&generator, d);\n    test_range<2, UpLo::Up>(&generator, d);\n    test_range<3, UpLo::Up>(&generator, d);\n\n    test_range<1, UpLo::Lo>(&generator, d);\n    test_range<2, UpLo::Lo>(&generator, d);\n    test_range<3, UpLo::Lo>(&generator, d);\n  }\n\n  SECTION(\"DataVector\") {\n    const DataVector dv(5);\n    test_range<1, UpLo::Up>(&generator, dv);\n    test_range<2, UpLo::Up>(&generator, dv);\n    test_range<3, UpLo::Up>(&generator, dv);\n\n    test_range<1, UpLo::Lo>(&generator, dv);\n    test_range<2, UpLo::Lo>(&generator, dv);\n    test_range<3, UpLo::Lo>(&generator, dv);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Test_MakeWithRandomValues.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstruct Var1 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Grid>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nusing one_var = tmpl::list<Var1<Dim>>;\n\ntemplate <size_t Dim>\nusing two_vars = tmpl::list<Var1<Dim>, Var2>;\n\nvoid check_random_values(\n    const gsl::not_null<std::unordered_set<double>*> values,\n    const gsl::not_null<size_t*> counter, const double v,\n    const double lower_bound, const double upper_bound) {\n  CHECK(v >= lower_bound);\n  CHECK(v <= upper_bound);\n  CHECK(values->insert(v).second);\n  ++(*counter);\n}\n\n// clang-tidy is wrong, this is a function definition\ntemplate <typename T>\nvoid check_random_values(\n    const gsl::not_null<std::unordered_set<double>*> values,  // NOLINT\n    const gsl::not_null<size_t*> counter, const T& c,         // NOLINT\n    const double lower_bound, const double upper_bound) {     // NOLINT\n  for (const auto& v : c) {\n    check_random_values(values, counter, v, lower_bound, upper_bound);\n  }\n}\n\ntemplate <typename... Tags>\nvoid check_random_values(\n    const gsl::not_null<std::unordered_set<double>*> values,\n    const gsl::not_null<size_t*> counter,\n    const Variables<tmpl::list<Tags...>>& v, const double lower_bound,\n    const double upper_bound) {\n  expand_pack((check_random_values<decltype(get<Tags>(v))>(\n                   values, counter, get<Tags>(v), lower_bound, upper_bound),\n               '0')...);\n}\n\ntemplate <typename T, typename U>\nvoid test_make_with_random_values(const U& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-10.0, 10.0);\n  // check that the data structure is filled with unique random values\n  // by adding them to a set and checking that the size of the set\n  // is equal to the number of doubles inserted\n  std::unordered_set<double> values;\n  size_t counter = 0;\n  const auto d = make_with_random_values<T>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n  check_random_values(make_not_null(&values), make_not_null(&counter), d, -10.0,\n                      10.0);\n  CHECK(values.size() == counter);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Test.TestHelpers.MakeWithRandomValues\", \"[Unit]\") {\n  const double d = std::numeric_limits<double>::signaling_NaN();\n  test_make_with_random_values<double>(d);\n  test_make_with_random_values<Scalar<double>>(d);\n  test_make_with_random_values<tnsr::A<double, 3>>(d);\n  test_make_with_random_values<tnsr::Abb<double, 3>>(d);\n  test_make_with_random_values<tnsr::aBcc<double, 3>>(d);\n\n  const DataVector dv(5);\n  test_make_with_random_values<DataVector>(dv);\n  test_make_with_random_values<Scalar<DataVector>>(dv);\n  test_make_with_random_values<tnsr::A<DataVector, 3>>(dv);\n  test_make_with_random_values<tnsr::Abb<DataVector, 3>>(dv);\n  test_make_with_random_values<tnsr::aBcc<DataVector, 3>>(dv);\n  test_make_with_random_values<Variables<one_var<3>>>(dv);\n  test_make_with_random_values<Variables<two_vars<3>>>(dv);\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/Tests/Test_RandomUnitNormal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim, typename DataType>\nvoid test_random_unit_normal(const gsl::not_null<std::mt19937*> generator,\n                             const DataType& used_for_size) {\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<Dim, DataType>(generator,\n                                                            used_for_size);\n  const auto unit_normal = random_unit_normal(generator, spatial_metric);\n  const auto expected_magnitude =\n      make_with_value<Scalar<DataType>>(used_for_size, 1.0);\n  const auto magnitude_of_unit_normal = magnitude(unit_normal, spatial_metric);\n  CHECK_ITERABLE_APPROX(expected_magnitude, magnitude_of_unit_normal);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Test.TestHelpers.RandomUnitNormal\", \"[Unit]\") {\n  MAKE_GENERATOR(generator);\n\n  const double d = std::numeric_limits<double>::signaling_NaN();\n  test_random_unit_normal<1>(&generator, d);\n  test_random_unit_normal<2>(&generator, d);\n  test_random_unit_normal<3>(&generator, d);\n\n  const DataVector dv(5);\n  test_random_unit_normal<1>(&generator, dv);\n  test_random_unit_normal<2>(&generator, dv);\n  test_random_unit_normal<3>(&generator, dv);\n}\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(TimeSteppers)\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"TimeStepperHelpers\")\n\nset(LIBRARY_SOURCES\n  ImexHelpers.cpp\n  LtsHelpers.cpp\n  RungeKutta.cpp\n  TimeStepperTestUtils.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Time\n  Utilities\n  PRIVATE\n  ErrorHandling\n  Framework\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/ImexHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Time/TimeSteppers/ImexHelpers.hpp\"\n\n#include <cmath>\n#include <cstdint>\n#include <fstream>\n#include <limits>\n\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/ImexTimeStepper.hpp\"\n\nnamespace TimeStepperTestUtils::imex {\nvoid check_convergence_order(const ImexTimeStepper& stepper,\n                             const std::pair<int32_t, int32_t>& step_range,\n                             const bool output) {\n  const auto do_integral = [&stepper](const int32_t num_steps) {\n    const Slab slab(0., 1.);\n    const TimeDelta step_size = slab.duration() / num_steps;\n\n    TimeStepId time_step_id(true, 0, slab.start());\n    double y = 1.;\n    TimeSteppers::History<double> history{\n        get<TimeSteppers::Tags::FixedOrder>(stepper.order())};\n    TimeSteppers::History<double> implicit_history{history.integration_order()};\n    const auto rhs = [](const double v, const double /*t*/) { return 3.0 * v; };\n    const auto implicit_rhs = [](const double v, const double /*t*/) {\n      return -2.0 * v;\n    };\n    const auto implicit_rhs_init =\n        [&implicit_rhs](const auto /*no_value*/, const double t) {\n          return implicit_rhs(exp(t), t);\n        };\n    initialize_history(\n        time_step_id.step_time(), make_not_null(&history),\n        [](const double t) { return exp(t); }, rhs, step_size,\n        stepper.number_of_past_steps());\n    initialize_history(\n        time_step_id.step_time(), make_not_null(&implicit_history),\n        [](const double /*t*/) {\n          return TimeSteppers::History<double>::no_value;\n        },\n        implicit_rhs_init, step_size, stepper.number_of_past_steps());\n    while (time_step_id.step_time() < slab.end()) {\n      history.insert(time_step_id, y, rhs(y, time_step_id.substep_time()));\n      implicit_history.insert(time_step_id,\n                              TimeSteppers::History<double>::no_value,\n                              implicit_rhs(y, time_step_id.substep_time()));\n      stepper.update_u(make_not_null(&y), history, step_size);\n      stepper.clean_history(make_not_null(&history));\n      // This system is simple enough that we can do the implicit\n      // solve analytically.\n\n      stepper.add_inhomogeneous_implicit_terms(make_not_null(&y),\n                                               implicit_history, step_size);\n\n      // Verify that the weight calculation only uses the history times.\n      auto implicit_history2 = implicit_history;\n      implicit_history2.map_entries([](const auto value) {\n        *value = std::numeric_limits<double>::signaling_NaN();\n      });\n      const double weight =\n          stepper.implicit_weight(implicit_history2, step_size);\n\n      y /= 1.0 + 2.0 * weight;\n      stepper.clean_history(make_not_null(&implicit_history));\n      time_step_id = stepper.next_time_id(time_step_id, step_size);\n    }\n    const double result = abs(y - exp(1.));\n    return result;\n  };\n  CHECK(convergence_rate(step_range, 1, do_integral, output) ==\n        approx(stepper.imex_order()).margin(0.4));\n}\n\nvoid check_bounded_dense_output(const ImexTimeStepper& stepper) {\n  const double decay_constant = 1.0e5;\n  const Slab slab(0.0, 1.0);\n  const auto time_step = slab.duration();\n  const auto rhs = [&](const double v, const double /*t*/) {\n    return -decay_constant * v;\n  };\n\n  TimeStepId time_step_id(true, 0, slab.start());\n  double y = 1.0;\n  TimeSteppers::History<double> history{\n      get<TimeSteppers::Tags::FixedOrder>(stepper.order())};\n  initialize_history(\n      time_step_id.step_time(), make_not_null(&history),\n      [&](const double t) { return exp(-decay_constant * t); }, rhs, time_step,\n      stepper.number_of_past_steps());\n  double test_time = 0.0;\n  for (;;) {\n    history.insert(time_step_id, TimeSteppers::History<double>::no_value,\n                   rhs(y, time_step_id.substep_time()));\n\n    while (test_time < 1.0) {\n      y = 1.0;\n      if (not stepper.dense_update_u(make_not_null(&y), history, test_time)) {\n        break;\n      }\n      CHECK(abs(y) < 5.0);\n      test_time += 0.1;\n    }\n\n    if (time_step_id.slab_number() != 0) {\n      break;\n    }\n\n    y = 1.0;\n    stepper.add_inhomogeneous_implicit_terms(make_not_null(&y), history,\n                                             time_step);\n    const double weight = stepper.implicit_weight(history, time_step);\n\n    y /= 1.0 + decay_constant * weight;\n    stepper.clean_history(make_not_null(&history));\n    time_step_id = stepper.next_time_id(time_step_id, time_step);\n  }\n  CHECK(test_time >= 1.0);\n}\n}  // namespace TimeStepperTestUtils::imex\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/ImexHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstdint>\n#include <utility>\n\n/// \\cond\nclass ImexTimeStepper;\n/// \\endcond\n\nnamespace TimeStepperTestUtils::imex {\nvoid check_convergence_order(const ImexTimeStepper& stepper,\n                             const std::pair<int32_t, int32_t>& step_range,\n                             bool output = false);\n\n/// Check that dense output does not have large oscillations.\nvoid check_bounded_dense_output(const ImexTimeStepper& stepper);\n}  // namespace TimeStepperTestUtils::imex\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/LtsHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Time/TimeSteppers/LtsHelpers.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <deque>\n#include <limits>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/BoundaryHistory.tpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nnamespace TimeStepperTestUtils::lts {\nnamespace {\nVariableOrderChoice fixed_order_choice(const LtsTimeStepper& stepper) {\n  REQUIRE(holds_alternative<TimeSteppers::Tags::FixedOrder>(stepper.order()));\n  const size_t order = get<TimeSteppers::Tags::FixedOrder>(stepper.order());\n  const size_t past_steps = stepper.number_of_past_steps();\n  return {order, past_steps, order, past_steps};\n}\n\nVariableOrderChoice flip(const VariableOrderChoice& choice) {\n  return {choice.remote_order, choice.remote_number_of_past_steps,\n          choice.local_order, choice.local_number_of_past_steps};\n}\n\nvoid arbitrary_compare_to_volume(const LtsTimeStepper& stepper,\n                                 const bool time_runs_forward,\n                                 std::deque<Rational> step_pattern,\n                                 std::deque<Rational> neighbor_step_pattern,\n                                 const double tolerance,\n                                 const VariableOrderChoice& order_choice) {\n  const auto local_approx = Approx::custom().epsilon(tolerance);\n  const auto step_sign = time_runs_forward ? 1 : -1;\n\n  // Arbitrary function that gives \"generic\" results.  We don't\n  // attempt to perform a meaningful integral.\n  const auto arbitrary_rhs = [](const TimeStepId& t) {\n    return 1.23 * static_cast<double>(t.substep()) + t.substep_time();\n  };\n\n  TimeSteppers::History<double> volume_history{order_choice.local_order};\n  TimeSteppers::BoundaryHistory<double, double, double> boundary_history{};\n\n  const Slab initial_slab(1.2, 3.4);\n  TimeStepId id(time_runs_forward, 0,\n                time_runs_forward ? initial_slab.start() : initial_slab.end());\n  {\n    auto init_step = step_sign * initial_slab.duration();\n    auto init_time = id.step_time();\n    int64_t slab_number = 0;\n    while (boundary_history.local().size() <\n               order_choice.local_number_of_past_steps or\n           boundary_history.remote().size() <\n               order_choice.remote_number_of_past_steps) {\n      --slab_number;\n      init_step =\n          init_step.with_slab(init_step.slab().advance_towards(-init_step));\n      init_time -= init_step;\n      const TimeStepId init_id(time_runs_forward, slab_number, init_time);\n      const double deriv = arbitrary_rhs(init_id);\n      if (boundary_history.local().size() <\n          order_choice.local_number_of_past_steps) {\n        volume_history.insert_initial(init_id, 0.0, deriv);\n        boundary_history.local().insert_initial(\n            init_id, order_choice.local_order, deriv);\n      }\n      if (boundary_history.remote().size() <\n          order_choice.remote_number_of_past_steps) {\n        boundary_history.remote().insert_initial(\n            init_id, order_choice.remote_order, deriv);\n      }\n    }\n  }\n\n  const bool equal_rate = step_pattern == neighbor_step_pattern;\n  const auto coupling = [&](const double local, const double remote) {\n    if (equal_rate) {\n      CHECK(local == remote);\n    }\n    return local;\n  };\n\n  auto neighbor_id = id;\n\n  const auto add_neighbor_entries = [&](const auto& limit) {\n    while (stepper.neighbor_data_required(limit, neighbor_id)) {\n      boundary_history.remote().insert(neighbor_id, order_choice.remote_order,\n                                       arbitrary_rhs(neighbor_id));\n      ASSERT(not neighbor_step_pattern.empty(), \"Test logic error\");\n      const TimeDelta neighbor_step = step_sign *\n                                      neighbor_step_pattern.front() *\n                                      neighbor_id.step_time().slab().duration();\n      neighbor_id = stepper.next_time_id(neighbor_id, neighbor_step);\n      if (neighbor_id.substep() == 0) {\n        neighbor_step_pattern.pop_front();\n      }\n    }\n  };\n\n  while (not step_pattern.empty()) {\n    const TimeDelta step =\n        step_sign * step_pattern.front() * id.step_time().slab().duration();\n    step_pattern.pop_front();\n\n    const double dense_test = (id.step_time() + step / 3).value();\n    bool dense_succeeded = false;\n    do {\n      {\n        const double deriv = arbitrary_rhs(id);\n        volume_history.insert(id, 0.0, deriv);\n        boundary_history.local().insert(id, order_choice.local_order, deriv);\n      }\n\n      // Check dense output\n      {\n        double volume_value = 0.0;\n        if (stepper.dense_update_u(make_not_null(&volume_value), volume_history,\n                                   dense_test)) {\n          CHECK(not dense_succeeded);\n          dense_succeeded = true;\n          add_neighbor_entries(dense_test);\n          double boundary_value = 0.0;\n          stepper.boundary_dense_output(make_not_null(&boundary_value),\n                                        boundary_history, dense_test, coupling);\n          CHECK(volume_value == local_approx(boundary_value));\n        }\n      }\n\n      id = stepper.next_time_id(id, step);\n      add_neighbor_entries(id);\n\n      // Check normal output\n      {\n        double volume_value = 0.0;\n        stepper.update_u(make_not_null(&volume_value), volume_history, step);\n        double boundary_value = 0.0;\n        stepper.add_boundary_delta(make_not_null(&boundary_value),\n                                   boundary_history, step, coupling);\n        CHECK(volume_value == local_approx(boundary_value));\n      }\n      stepper.clean_history(make_not_null(&volume_history));\n      stepper.clean_boundary_history(make_not_null(&boundary_history));\n    } while (id.substep() != 0);\n    CHECK(dense_succeeded);\n  }\n}\n}  // namespace\n\nvoid test_equal_rate(const LtsTimeStepper& stepper) {\n  INFO(\"test_equal_rate\");\n  const auto order_choice = fixed_order_choice(stepper);\n  const std::deque<Rational> step_pattern{{1}, {1, 4}, {1, 4}, {1, 2}};\n  arbitrary_compare_to_volume(stepper, true, step_pattern, step_pattern,\n                              1.0e-15, order_choice);\n  arbitrary_compare_to_volume(stepper, false, step_pattern, step_pattern,\n                              1.0e-15, order_choice);\n}\n\nvoid test_uncoupled(const LtsTimeStepper& stepper, const double tolerance,\n                    std::optional<VariableOrderChoice> variable_order_choice) {\n  INFO(\"test_uncoupled\");\n  if (not variable_order_choice.has_value()) {\n    variable_order_choice.emplace(fixed_order_choice(stepper));\n  }\n  const std::deque<Rational> step_pattern{{1}, {1, 4}, {1, 4}, {1, 2}};\n  const std::deque<Rational> neighbor_step_pattern{{1, 4}, {1, 4}, {1, 2}, {1}};\n  arbitrary_compare_to_volume(stepper, true, step_pattern,\n                              neighbor_step_pattern, tolerance,\n                              *variable_order_choice);\n  arbitrary_compare_to_volume(stepper, false, step_pattern,\n                              neighbor_step_pattern, tolerance,\n                              *variable_order_choice);\n}\n\nnamespace {\nstruct StepResult {\n  TimeStepId id;\n  size_t order;\n  double value;\n};\n\ntemplate <typename Coupling>\nclass Element {\n public:\n  template <typename AnalyticSelf, typename AnalyticNeighbor>\n  Element(const LtsTimeStepper& stepper,\n          const VariableOrderChoice& order_choice, Coupling coupling,\n          std::deque<Rational> step_pattern, const TimeStepId& time_step_id,\n          const Rational& neighbor_first_step,\n          const AnalyticSelf& analytic_self,\n          const AnalyticNeighbor& analytic_neighbor)\n      : stepper_(&stepper),\n        coupling_(std::move(coupling)),\n        step_pattern_(std::move(step_pattern)),\n        time_step_id_(time_step_id),\n        value_(analytic_self(time_step_id_.substep_time())),\n        step_size_(next_step_size()),\n        next_time_step_id_(stepper_->next_time_id(time_step_id_, step_size_)),\n        volume_history_(order_choice.local_order),\n        next_message_(stepper_->next_time_id(\n            time_step_id_, neighbor_first_step *\n                               time_step_id_.step_time().slab().duration())) {\n    auto init_step = (time_step_id_.time_runs_forward() ? 1 : -1) *\n                     time_step_id_.step_time().slab().duration();\n    auto init_time = time_step_id_.step_time();\n    int64_t slab_number = 0;\n    while (boundary_history_.local().size() <\n               order_choice.local_number_of_past_steps + 1 or\n           boundary_history_.remote().size() <\n               order_choice.remote_number_of_past_steps + 1) {\n      const TimeStepId init_id(time_step_id_.time_runs_forward(), slab_number,\n                               init_time);\n      if (boundary_history_.local().size() <\n          order_choice.local_number_of_past_steps + 1) {\n        volume_history_.insert_initial(init_id,\n                                       analytic_self(init_time.value()), 0.0);\n        boundary_history_.local().insert_initial(\n            init_id, order_choice.local_order,\n            analytic_self(init_time.value()));\n      }\n      if (boundary_history_.remote().size() <\n          order_choice.remote_number_of_past_steps + 1) {\n        boundary_history_.remote().insert_initial(\n            init_id, order_choice.remote_order,\n            analytic_neighbor(init_time.value()));\n      }\n      --slab_number;\n      init_step =\n          init_step.with_slab(init_step.slab().advance_towards(-init_step));\n      init_time -= init_step;\n    }\n  }\n\n  bool done() const { return step_pattern_.empty(); }\n  TimeStepId next_time_step_id() const { return next_time_step_id_; }\n  Time next_step_time() const { return time_step_id_.step_time() + step_size_; }\n\n  bool local_dense_output_ready(const double time) const {\n    const evolution_less<double> before{time_step_id_.time_runs_forward()};\n    if (not before(time, next_step_time().value())) {\n      return false;\n    }\n    double dummy_value = 0.0;\n    return stepper_->dense_update_u(make_not_null(&dummy_value),\n                                    volume_history_, time);\n  }\n\n  double dense_output(const double time) {\n    REQUIRE(process_messages(time));\n    double dense_result = *volume_history_.step_start(time).value;\n    // Can skip the volume update because all derivatives are zero.\n    stepper_->boundary_dense_output(make_not_null(&dense_result),\n                                    boundary_history_, time, coupling_);\n    return dense_result;\n  }\n\n  std::optional<StepResult> step() {\n    if (not process_messages(next_time_step_id_)) {\n      return std::nullopt;\n    }\n    stepper_->update_u(make_not_null(&value_), volume_history_, step_size_);\n    stepper_->add_boundary_delta(make_not_null(&value_), boundary_history_,\n                                 step_size_, coupling_);\n    stepper_->clean_history(make_not_null(&volume_history_));\n    stepper_->clean_boundary_history(make_not_null(&boundary_history_));\n\n    time_step_id_ = next_time_step_id_;\n    if (time_step_id_.substep() == 0) {\n      step_pattern_.pop_front();\n      if (not done()) {\n        step_size_ = next_step_size();\n      }\n      step_size_ = step_size_.with_slab(time_step_id_.step_time().slab());\n    }\n    next_time_step_id_ = stepper_->next_time_id(time_step_id_, step_size_);\n    volume_history_.insert(time_step_id_, value_, 0.0);\n    boundary_history_.local().insert(\n        time_step_id_, volume_history_.integration_order(), value_);\n    return {{time_step_id_, volume_history_.integration_order(), value_}};\n  }\n\n  void receive_data(const StepResult& message, const TimeStepId& next_id) {\n    ASSERT(message.id == next_message_, \"Test logic error.\");\n    messages_.emplace_back(message);\n    next_message_ = next_id;\n  }\n\n private:\n  TimeDelta next_step_size() const {\n    return step_pattern_.front() * time_step_id_.step_time().slab().duration();\n  }\n\n  template <typename T>\n  bool process_messages(const T& time) {\n    while (not messages_.empty() and\n           stepper_->neighbor_data_required(time, messages_.front().id)) {\n      boundary_history_.remote().insert(messages_.front().id,\n                                        messages_.front().order,\n                                        messages_.front().value);\n      messages_.pop_front();\n    }\n    return not(messages_.empty() and\n               stepper_->neighbor_data_required(time, next_message_));\n  }\n\n  gsl::not_null<const LtsTimeStepper*> stepper_;\n  Coupling coupling_;\n\n  std::deque<Rational> step_pattern_;\n  TimeStepId time_step_id_;\n  double value_;\n  TimeDelta step_size_;\n  TimeStepId next_time_step_id_;\n  TimeSteppers::History<double> volume_history_{};\n  TimeSteppers::BoundaryHistory<double, double, double> boundary_history_{};\n  std::deque<StepResult> messages_{};\n  TimeStepId next_message_;\n};\n\n// Test system:\n// dx/dt = x y\n// dy/dt = - x y\n//\n// Solution:\n// x = c / [1 + exp(-c (t - d))]\n// y = c - x\nnamespace product_system {\ndouble rhs_x(const double x, const double y) { return x * y; }\ndouble rhs_y(const double x, const double y) { return -x * y; }\n\n// Arbitrary\nconstexpr double conserved_sum = 0.7;  // c\nconstexpr double offset = 0.4;  // d\n\ndouble analytic_x(const double t) {\n  return conserved_sum / (1.0 + exp(-conserved_sum * (t - offset)));\n}\ndouble analytic_y(const double t) { return conserved_sum - analytic_x(t); }\n}  // namespace product_system\n\nvoid test_conservation_impl(const LtsTimeStepper& stepper,\n                            std::deque<Rational> step_pattern_x,\n                            std::deque<Rational> step_pattern_y,\n                            const VariableOrderChoice& order_choice) {\n  const bool time_runs_forward = step_pattern_x.front() > 0;\n  const evolution_less<Time> before{time_runs_forward};\n\n  const Slab initial_slab(1.2, 3.4);\n  const TimeStepId initial_id(\n      time_runs_forward, 0,\n      time_runs_forward ? initial_slab.start() : initial_slab.end());\n\n  const auto first_step_x = step_pattern_x.front();\n  Element element_x(\n      stepper, order_choice,\n      [](const double local, const double remote) {\n        return product_system::rhs_x(local, remote);\n      },\n      std::move(step_pattern_x), initial_id, step_pattern_y.front(),\n      product_system::analytic_x, product_system::analytic_y);\n  Element element_y(\n      stepper, flip(order_choice),\n      [](const double local, const double remote) {\n        return product_system::rhs_y(remote, local);\n      },\n      std::move(step_pattern_y), initial_id, first_step_x,\n      product_system::analytic_y, product_system::analytic_x);\n\n  double test_time = initial_id.step_time().value();\n\n  for (;;) {\n    const bool need_more_x = not element_x.local_dense_output_ready(test_time);\n    const bool need_more_y = not element_y.local_dense_output_ready(test_time);\n    // If an element can produce dense output, it must have\n    // transmitted enough data for its neighbor to do the same.\n    if (not(need_more_x or need_more_y)) {\n      const double dense_x = element_x.dense_output(test_time);\n      const double dense_y = element_y.dense_output(test_time);\n      CHECK(dense_x + dense_y == approx(product_system::conserved_sum));\n      test_time = std::min(element_x.next_step_time(),\n                           element_y.next_step_time(), before)\n                      .value();\n      continue;\n    }\n\n    if (element_x.done() and element_y.done()) {\n      break;\n    }\n\n    if (need_more_x) {\n      const auto step_result = element_x.step();\n      if (step_result.has_value()) {\n        element_y.receive_data(*step_result, element_x.next_time_step_id());\n        continue;\n      }\n    }\n\n    if (need_more_y) {\n      const auto step_result = element_y.step();\n      if (step_result.has_value()) {\n        element_x.receive_data(*step_result, element_y.next_time_step_id());\n        continue;\n      }\n    }\n\n    INFO(\"Deadlocked\");\n    REQUIRE(false);\n  }\n}\n\ndouble test_convergence_error(const LtsTimeStepper& stepper,\n                              const std::deque<Rational>& step_pattern_x,\n                              const std::deque<Rational>& step_pattern_y,\n                              const int32_t repeats, const bool test_dense,\n                              const VariableOrderChoice& order_choice) {\n  const bool time_runs_forward = step_pattern_x.front() > 0;\n\n  const auto initial_slab =\n      time_runs_forward ? Slab::with_duration_from_start(-10.0, 5.0 / repeats)\n                        : Slab::with_duration_to_end(-5.0, 5.0 / repeats);\n  // Arbitrary but not a nice rational portion of a slab.\n  const double dense_time = -7.91237;\n\n  const TimeStepId initial_id(\n      time_runs_forward, 0,\n      time_runs_forward ? initial_slab.start() : initial_slab.end());\n\n  std::deque<Rational> full_pattern_x{};\n  std::deque<Rational> full_pattern_y{};\n  for (int32_t i = 0; i < repeats; ++i) {\n    full_pattern_x.insert(full_pattern_x.end(), step_pattern_x.begin(),\n                          step_pattern_x.end());\n    full_pattern_y.insert(full_pattern_y.end(), step_pattern_y.begin(),\n                          step_pattern_y.end());\n  }\n\n  Element element_x(\n      stepper, order_choice,\n      [](const double local, const double remote) {\n        return product_system::rhs_x(local, remote);\n      },\n      std::move(full_pattern_x), initial_id, step_pattern_y.front(),\n      product_system::analytic_x, product_system::analytic_y);\n  Element element_y(\n      stepper, flip(order_choice),\n      [](const double local, const double remote) {\n        return product_system::rhs_y(remote, local);\n      },\n      std::move(full_pattern_y), initial_id, step_pattern_x.front(),\n      product_system::analytic_y, product_system::analytic_x);\n\n  for (;;) {\n    // Step y as much as possible so we don't have to worry about\n    // having enough data on the x side for dense output.\n    while (not element_y.done()) {\n      const auto step_result = element_y.step();\n      if (not step_result.has_value()) {\n        break;\n      }\n      element_x.receive_data(*step_result, element_y.next_time_step_id());\n    }\n\n    if (test_dense and element_x.local_dense_output_ready(dense_time)) {\n      return element_x.dense_output(dense_time) -\n             product_system::analytic_x(dense_time);\n    }\n\n    {\n      REQUIRE(not element_x.done());\n      const auto step_result = element_x.step();\n      if (step_result.has_value()) {\n        if (not test_dense and element_x.done()) {\n          return step_result->value -\n                 product_system::analytic_x(step_result->id.substep_time());\n        }\n        element_y.receive_data(*step_result, element_x.next_time_step_id());\n      }\n    }\n  }\n}\n\nvoid test_convergence_impl(\n    const LtsTimeStepper& stepper,\n    const std::pair<int32_t, int32_t>& number_of_steps_range,\n    const int32_t stride, const bool dense,\n    const VariableOrderChoice& order_choice) {\n  const size_t expected_order =\n      std::min(order_choice.local_order, order_choice.remote_order);\n  CAPTURE(dense);\n  std::deque<Rational> step_pattern_x{{1, 2}, {1, 8}, {1, 8}, {1, 4}};\n  std::deque<Rational> step_pattern_y{{1, 8}, {1, 8}, {1, 4}, {1, 2}};\n  CHECK(convergence_rate(\n            number_of_steps_range, stride, [&](const int32_t repeats) {\n              return test_convergence_error(stepper, step_pattern_x,\n                                            step_pattern_y, repeats, dense,\n                                            order_choice);\n            }) == approx(expected_order).margin(0.4));\n  alg::for_each(step_pattern_x, [](Rational& x) { x *= -1; });\n  alg::for_each(step_pattern_y, [](Rational& x) { x *= -1; });\n  CHECK(convergence_rate(\n            number_of_steps_range, stride, [&](const int32_t repeats) {\n              return test_convergence_error(stepper, step_pattern_x,\n                                            step_pattern_y, repeats, dense,\n                                            order_choice);\n            }) == approx(expected_order).margin(0.4));\n}\n}  // namespace\n\nvoid test_conservation(\n    const LtsTimeStepper& stepper,\n    std::optional<VariableOrderChoice> variable_order_choice) {\n  if (not variable_order_choice.has_value()) {\n    variable_order_choice.emplace(fixed_order_choice(stepper));\n  }\n  std::deque<Rational> step_pattern_x{{1}, {1, 4}, {1, 4}, {1, 2}};\n  std::deque<Rational> step_pattern_y{{1, 4}, {1, 4}, {1, 2}, {1}};\n  test_conservation_impl(stepper, step_pattern_x, step_pattern_y,\n                         *variable_order_choice);\n  alg::for_each(step_pattern_x, [](Rational& x) { x *= -1; });\n  alg::for_each(step_pattern_y, [](Rational& x) { x *= -1; });\n  test_conservation_impl(stepper, std::move(step_pattern_x),\n                         std::move(step_pattern_y), *variable_order_choice);\n}\n\nvoid test_convergence(\n    const LtsTimeStepper& stepper,\n    const std::pair<int32_t, int32_t>& number_of_steps_range,\n    const int32_t stride,\n    std::optional<VariableOrderChoice> variable_order_choice) {\n  if (not variable_order_choice.has_value()) {\n    variable_order_choice.emplace(fixed_order_choice(stepper));\n  }\n  test_convergence_impl(stepper, number_of_steps_range, stride, false,\n                        *variable_order_choice);\n}\n\nvoid test_dense_convergence(\n    const LtsTimeStepper& stepper,\n    const std::pair<int32_t, int32_t>& number_of_steps_range,\n    const int32_t stride,\n    std::optional<VariableOrderChoice> variable_order_choice) {\n  if (not variable_order_choice.has_value()) {\n    variable_order_choice.emplace(fixed_order_choice(stepper));\n  }\n  test_convergence_impl(stepper, number_of_steps_range, stride, true,\n                        *variable_order_choice);\n}\n\nvoid test_variable_order_consistency(const LtsTimeStepper& variable_stepper,\n                                     const LtsTimeStepper& fixed_stepper) {\n  REQUIRE(variants::holds_alternative<TimeSteppers::Tags::VariableOrder>(\n      variable_stepper.order()));\n  REQUIRE(variants::holds_alternative<TimeSteppers::Tags::FixedOrder>(\n      fixed_stepper.order()));\n\n  const size_t order =\n      variants::get<TimeSteppers::Tags::FixedOrder>(fixed_stepper.order());\n\n  const Slab slab(1.2, 3.4);\n  const TimeDelta step = slab.duration();\n  TimeSteppers::History<double> history{order};\n  // Values don't matter\n  TimeStepperTestUtils::initialize_history(\n      slab.start(), make_not_null(&history),\n      [](const double t) { return sin(t); },\n      [](const double /*y*/, const double t) { return cos(t); },\n      slab.duration(), fixed_stepper.number_of_past_steps());\n  TimeStepId step_id(true, 0, slab.start());\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-do-while)\n  do {\n    history.insert(\n        step_id,\n        sin(step_id.substep_time()) + static_cast<double>(step_id.substep()),\n        cos(step_id.substep_time()) + static_cast<double>(step_id.substep()));\n    double y_fixed = std::numeric_limits<double>::signaling_NaN();\n    double y_variable = std::numeric_limits<double>::signaling_NaN();\n    fixed_stepper.update_u(make_not_null(&y_fixed), history, step);\n    variable_stepper.update_u(make_not_null(&y_variable), history, step);\n    CHECK(y_fixed == y_variable);\n    const auto new_step_id = fixed_stepper.next_time_id(step_id, step);\n    CHECK(variable_stepper.next_time_id(step_id, step) == new_step_id);\n    step_id = new_step_id;\n  } while (step_id.substep() != 0);\n}\n\nvoid test_variable_order_boundary_consistency(const LtsTimeStepper& stepper) {\n  const auto stepper_order_variant = stepper.order();\n  REQUIRE(variants::holds_alternative<TimeSteppers::Tags::VariableOrder>(\n      stepper_order_variant));\n  const auto& stepper_order =\n      variants::get<TimeSteppers::Tags::VariableOrder>(stepper_order_variant);\n\n  const Slab slab(0.0, 1.0);\n  const auto make_id = [&](const Rational& frac) {\n    return TimeStepId(true, 0, slab.start() + frac * slab.duration());\n  };\n\n  // Test sequence:\n  //   0   -  2/16  raise order away from minimum\n  //  2/16 -  5/16  change local order with local step smaller\n  //  5/16 - 10/16  change remote order with local step smaller\n  // 10/16 - 11/16  change remote order with remote step smaller\n  // 12/16 - 15/16  change local order with remote step smaller\n  std::map<TimeStepId, TimeDelta> local_step_changes{\n      {make_id(0), slab.duration() / 16},\n      {make_id({2, 16}), slab.duration() / 32}};\n  std::map<TimeStepId, size_t> local_order_changes{{make_id({1, 16}), 2},\n                                                   {make_id({2, 16}), 3},\n                                                   {make_id({5, 32}), 4},\n                                                   {make_id({6, 32}), 5},\n                                                   {make_id({8, 32}), 4},\n                                                   {make_id({9, 32}), 3},\n                                                   {make_id({24, 32}), 4},\n                                                   {make_id({25, 32}), 5},\n                                                   {make_id({26, 32}), 6},\n                                                   {make_id({27, 32}), 7},\n                                                   {make_id({28, 32}), 6},\n                                                   {make_id({29, 32}), 5}};\n  std::map<TimeStepId, TimeDelta> remote_step_changes{\n      {make_id(0), slab.duration() / 16},\n      {make_id({10, 16}), slab.duration() / 64}};\n  std::map<TimeStepId, size_t> remote_order_changes{{make_id({1, 16}), 2},\n                                                    {make_id({2, 16}), 3},\n                                                    {make_id({5, 16}), 4},\n                                                    {make_id({6, 16}), 5},\n                                                    {make_id({7, 16}), 6},\n                                                    {make_id({8, 16}), 5},\n                                                    {make_id({9, 16}), 4},\n                                                    {make_id({40, 64}), 5},\n                                                    {make_id({41, 64}), 6},\n                                                    {make_id({42, 64}), 7},\n                                                    {make_id({43, 64}), 6},\n                                                    {make_id({44, 64}), 5}};\n\n  TimeSteppers::History<double> local_history{stepper_order.minimum};\n  TimeSteppers::History<double> remote_history{stepper_order.minimum};\n  TimeSteppers::History<DataVector> boundary_volume_history{\n      stepper_order.minimum};\n  TimeSteppers::BoundaryHistory<double, double, DataVector> boundary_history{};\n\n  const auto deriv = [](const TimeStepId& id) {\n    return id.substep_time() + static_cast<double>(id.substep());\n  };\n  const auto coupling = [](const double a, const double b) {\n    return DataVector{a, b};\n  };\n\n  TimeStepId local_id = make_id(0);\n  TimeStepId remote_id = make_id(0);\n  TimeDelta local_time_step{};\n  TimeDelta remote_time_step{};\n\n  double local_value = 1.0;\n  double remote_value = 2.0;\n  DataVector boundary_value{local_value, remote_value};\n\n  while (local_id.slab_number() == 0) {\n    local_history.insert(local_id, local_value, deriv(local_id));\n    boundary_volume_history.insert(local_id, boundary_value,\n                                   DataVector(2, 0.0));\n    boundary_history.local().insert(local_id, local_history.integration_order(),\n                                    deriv(local_id));\n    if (const auto change = local_step_changes.find(local_id);\n        change != local_step_changes.end()) {\n      local_time_step = change->second;\n      local_step_changes.erase(change);\n    }\n    local_id = stepper.next_time_id(local_id, local_time_step);\n\n    while (stepper.neighbor_data_required(local_id, remote_id)) {\n      remote_history.insert(remote_id, remote_value, deriv(remote_id));\n      boundary_history.remote().insert(\n          remote_id, remote_history.integration_order(), deriv(remote_id));\n      if (const auto change = remote_step_changes.find(remote_id);\n          change != remote_step_changes.end()) {\n        remote_time_step = change->second;\n        remote_step_changes.erase(change);\n      }\n      stepper.update_u(make_not_null(&remote_value), remote_history,\n                       remote_time_step);\n      remote_id = stepper.next_time_id(remote_id, remote_time_step);\n      if (const auto change = remote_order_changes.find(remote_id);\n          change != remote_order_changes.end()) {\n        remote_history.integration_order(std::clamp(\n            change->second, stepper_order.minimum, stepper_order.maximum));\n        remote_order_changes.erase(change);\n      }\n      stepper.clean_history(make_not_null(&remote_history));\n    }\n\n    stepper.update_u(make_not_null(&local_value), local_history,\n                     local_time_step);\n    stepper.update_u(make_not_null(&boundary_value), boundary_volume_history,\n                     local_time_step);\n    stepper.add_boundary_delta(make_not_null(&boundary_value), boundary_history,\n                               local_time_step, coupling);\n    if (const auto change = local_order_changes.find(local_id);\n        change != local_order_changes.end()) {\n      local_history.integration_order(std::clamp(\n          change->second, stepper_order.minimum, stepper_order.maximum));\n      boundary_volume_history.integration_order(std::clamp(\n          change->second, stepper_order.minimum, stepper_order.maximum));\n      local_order_changes.erase(change);\n    }\n    stepper.clean_history(make_not_null(&local_history));\n    stepper.clean_history(make_not_null(&boundary_volume_history));\n    stepper.clean_boundary_history(make_not_null(&boundary_history));\n    CHECK(boundary_value[0] == approx(local_value));\n    if (local_id == remote_id) {\n      CHECK(boundary_value[1] == approx(remote_value));\n    }\n  }\n  ASSERT(local_id == remote_id, \"Steps did not align at end\");\n  ASSERT(local_step_changes.empty(), \"Not all changes processed\");\n  ASSERT(local_order_changes.empty(), \"Not all changes processed\");\n  ASSERT(remote_step_changes.empty(), \"Not all changes processed\");\n  ASSERT(remote_order_changes.empty(), \"Not all changes processed\");\n}\n}  // namespace TimeStepperTestUtils::lts\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/LtsHelpers.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <utility>\n\n/// \\cond\nclass LtsTimeStepper;\n/// \\endcond\n\nnamespace TimeStepperTestUtils::lts {\nstruct VariableOrderChoice {\n  size_t local_order;\n  size_t local_number_of_past_steps;\n  size_t remote_order;\n  size_t remote_number_of_past_steps;\n};\n\n/// Test boundary computations with the same step size on both\n/// neighbors against the volume computation.\nvoid test_equal_rate(const LtsTimeStepper& stepper);\n\n/// Test uncoupled boundary computations against the volume\n/// computation.\nvoid test_uncoupled(\n    const LtsTimeStepper& stepper, double tolerance,\n    std::optional<VariableOrderChoice> variable_order_choice = std::nullopt);\n\n/// Test conservation of boundary dense output.\nvoid test_conservation(\n    const LtsTimeStepper& stepper,\n    std::optional<VariableOrderChoice> variable_order_choice = std::nullopt);\n\n// Test convergence rate of boundary integration.\nvoid test_convergence(\n    const LtsTimeStepper& stepper,\n    const std::pair<int32_t, int32_t>& number_of_steps_range, int32_t stride,\n    std::optional<VariableOrderChoice> variable_order_choice = std::nullopt);\n\n// Test convergence rate of boundary dense output.\nvoid test_dense_convergence(\n    const LtsTimeStepper& stepper,\n    const std::pair<int32_t, int32_t>& number_of_steps_range, int32_t stride,\n    std::optional<VariableOrderChoice> variable_order_choice = std::nullopt);\n\n// Check agreement between fixed and variable-order when the order is\n// not changing.\nvoid test_variable_order_consistency(const LtsTimeStepper& variable_stepper,\n                                     const LtsTimeStepper& fixed_stepper);\n\n// Check consistency of the boundary and volume integration under\n// varying order.\nvoid test_variable_order_boundary_consistency(const LtsTimeStepper& stepper);\n}  // namespace TimeStepperTestUtils::lts\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/RungeKutta.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <vector>\n\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace TestHelpers::RungeKutta {\nnamespace {\n// Check order for quadrature (RHS depends only on time).\nvoid check_quadrature_order(const std::vector<double>& substep_times,\n                            const std::vector<double>& coefficients,\n                            const double this_substep_time,\n                            const size_t expected) {\n  if (expected == 0) {\n    return;\n  }\n  CAPTURE(coefficients);\n  CAPTURE(expected);\n  // Split out first case to avoid 0^0 annoyance\n  CHECK(alg::accumulate(coefficients, 0.0) == approx(this_substep_time));\n  // Don't require the next order to be inconsistent, as the method\n  // may do better for quadrature than for an ODE.  Order 0 (i.e.,\n  // that the stepper is at least first order) was checked above.\n  for (size_t order = 1; order < expected; ++order) {\n    CAPTURE(order);\n    double integral = 0.0;\n    for (size_t substep = 1; substep < coefficients.size(); ++substep) {\n      integral +=\n          coefficients[substep] * std::pow(substep_times[substep - 1], order);\n    }\n    CHECK(integral ==\n          approx(pow(this_substep_time, order + 1.0) / (order + 1.0)));\n  }\n}\n}  // namespace\n\nvoid check_tableau(const TimeSteppers::RungeKutta::ButcherTableau& tableau,\n                   const size_t expected_order) {\n  INFO(\"check_tableau\");\n  const auto& substep_times = tableau.substep_times;\n  const auto& substep_coefficients = tableau.substep_coefficients;\n  const auto& result_coefficients = tableau.result_coefficients;\n  const auto& error_coefficients = tableau.error_coefficients;\n  const auto& dense_coefficients = tableau.dense_coefficients;\n\n  const size_t max_substeps =\n      std::max(result_coefficients.size(), error_coefficients.size());\n\n  // First substep is implicitly 0 with no coefficients.\n  CHECK(substep_times.size() + 1 == max_substeps);\n  CHECK(substep_coefficients.size() + 1 == max_substeps);\n  // Optional FSAL term at the end\n  CHECK((dense_coefficients.size() == result_coefficients.size() or\n         dense_coefficients.size() == max_substeps + 1));\n\n  // Results are order 1\n  CHECK(alg::accumulate(result_coefficients, 0.0) == approx(1.0));\n  CHECK(alg::accumulate(error_coefficients, 0.0) == approx(1.0));\n\n  for (size_t substep = 1; substep < max_substeps; ++substep) {\n    const auto& time = substep_times[substep - 1];\n    CHECK(time >= 0.0);\n    CHECK(time <= 1.0);\n    const auto& coefficients = substep_coefficients[substep - 1];\n    // Substep is explicit\n    CHECK(coefficients.size() <= substep);\n    // Substep is order 1\n    CHECK(alg::accumulate(coefficients, 0.0) == approx(time));\n  }\n\n  for (size_t substep = 0; substep < result_coefficients.size(); ++substep) {\n    CHECK(evaluate_polynomial(dense_coefficients[substep], 0.0) == 0.0);\n    CHECK(evaluate_polynomial(dense_coefficients[substep], 1.0) ==\n          approx(result_coefficients[substep]));\n  }\n  if (dense_coefficients.size() > result_coefficients.size()) {\n    CHECK(not dense_coefficients.back().empty());\n    CHECK(evaluate_polynomial(dense_coefficients.back(), 0.0) == 0.0);\n    CHECK(evaluate_polynomial(dense_coefficients.back(), 1.0) == approx(0.0));\n    for (size_t error_only_substep = result_coefficients.size();\n         error_only_substep < dense_coefficients.size() - 1;\n         ++error_only_substep) {\n      CHECK(dense_coefficients[error_only_substep].empty());\n    }\n  }\n\n  check_quadrature_order(substep_times, result_coefficients, 1.0,\n                         expected_order);\n  check_quadrature_order(substep_times, error_coefficients, 1.0,\n                         expected_order - 1);\n}\n\nvoid check_tableau(const TimeSteppers::RungeKutta& stepper) {\n  REQUIRE(holds_alternative<TimeSteppers::Tags::FixedOrder>(stepper.order()));\n  check_tableau(stepper.butcher_tableau(),\n                get<TimeSteppers::Tags::FixedOrder>(stepper.order()));\n}\n\nvoid check_implicit_tableau(\n    const TimeSteppers::RungeKutta::ButcherTableau& explicit_tableau,\n    const TimeSteppers::ImexRungeKutta::ImplicitButcherTableau&\n        implicit_tableau,\n    const size_t expected_stage_order, const bool stiffly_accurate) {\n  INFO(\"check_implicit_tableau\");\n  const auto& substep_times = explicit_tableau.substep_times;\n  const auto& implicit_coefficients = implicit_tableau.substep_coefficients;\n\n  const auto num_substeps = implicit_coefficients.size() + 1;\n\n  CHECK(num_substeps == explicit_tableau.result_coefficients.size());\n\n  if (stiffly_accurate) {\n    CHECK(implicit_coefficients.back() == explicit_tableau.result_coefficients);\n  }\n\n  // It is impossible to exceed second-order with a DIRK method.\n  CHECK(expected_stage_order <= 2);\n\n  for (size_t substep = 1; substep < num_substeps; ++substep) {\n    const auto& coefficients = implicit_coefficients[substep - 1];\n    // Substep is DIRK\n    CHECK(coefficients.size() <= substep + 1);\n    check_quadrature_order(substep_times, coefficients,\n                           substep_times[substep - 1], expected_stage_order);\n  }\n}\n\nvoid check_implicit_tableau(const TimeSteppers::ImexRungeKutta& stepper,\n                            const bool stiffly_accurate) {\n  check_implicit_tableau(stepper.butcher_tableau(),\n                         stepper.implicit_butcher_tableau(),\n                         stepper.implicit_stage_order(), stiffly_accurate);\n}\n}  // namespace TestHelpers::RungeKutta\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/RungeKutta.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <cstddef>\n\n#include \"Time/TimeSteppers/ImexRungeKutta.hpp\"\n#include \"Time/TimeSteppers/RungeKutta.hpp\"\n\nnamespace TestHelpers::RungeKutta {\n/// Sanity-check a Butcher tableau\nvoid check_tableau(const TimeSteppers::RungeKutta::ButcherTableau& tableau,\n                   size_t expected_order);\n/// Convenience wrapper for the previous function\nvoid check_tableau(const TimeSteppers::RungeKutta& stepper);\n\n/// Sanity-check an implicit Butcher tableau\nvoid check_implicit_tableau(\n    const TimeSteppers::RungeKutta::ButcherTableau& explicit_tableau,\n    const TimeSteppers::ImexRungeKutta::ImplicitButcherTableau&\n        implicit_tableau,\n    size_t expected_stage_order, bool stiffly_accurate);\n/// Convenience wrapper for the previous function\nvoid check_implicit_tableau(const TimeSteppers::ImexRungeKutta& stepper,\n                            bool stiffly_accurate);\n}  // namespace TestHelpers::RungeKutta\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/TimeStepperTestUtils.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n\n#include <algorithm>\n#include <blaze/math/DynamicMatrix.h>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <cstdint>\n#include <limits>\n#include <utility>\n\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace TimeStepperTestUtils {\n\nnamespace {\ntemplate <typename T, typename F>\nvoid take_step(\n    const gsl::not_null<Time*> time, const gsl::not_null<T*> y,\n    const gsl::not_null<TimeSteppers::History<T>*> history,\n    const TimeStepper& stepper, F&& rhs, const TimeDelta& step_size) {\n  TimeStepId time_id(step_size.is_positive(), 0, *time);\n  for (uint64_t substep = 0;\n       substep < stepper.number_of_substeps();\n       ++substep) {\n    CHECK(time_id.substep() == substep);\n    history->insert(time_id, *y, rhs(*y, time_id.substep_time()));\n    // There is no std::numeric_limits<complex>\n    *y = std::numeric_limits<double>::signaling_NaN();\n    stepper.update_u(y, *history, step_size);\n    stepper.clean_history(history);\n    time_id = stepper.next_time_id(time_id, step_size);\n  }\n  CHECK(time_id.substep() == 0);\n  CHECK(time_id.step_time() - *time == step_size);\n  *time = time_id.step_time();\n}\n\ntemplate <typename F>\nvoid take_step_and_check_error(\n    const gsl::not_null<Time*> time, const gsl::not_null<double*> y,\n    const gsl::not_null<double*> y_error,\n    const gsl::not_null<TimeSteppers::History<double>*> history,\n    const TimeStepper& stepper, F&& rhs, const TimeDelta& step_size) {\n  // Return the actual error, not a normalized version.\n  const StepperErrorTolerances tolerances{\n      .estimates = StepperErrorTolerances::Estimates::StepperOrder,\n      .absolute = 1.0,\n      .relative = 0.0};\n  TimeStepId time_id(step_size.is_positive(), 0, *time);\n  for (uint64_t substep = 0; substep < stepper.number_of_substeps_for_error();\n       ++substep) {\n    CHECK(time_id.substep() == substep);\n    history->insert(time_id, *y, rhs(*y, time_id.substep_time()));\n    *y = std::numeric_limits<double>::signaling_NaN();\n    const auto error = stepper.update_u(y, *history, step_size, tolerances);\n    {\n      auto y_without_tolerances = std::numeric_limits<double>::signaling_NaN();\n      const auto error_without_tolerances =\n          stepper.update_u(make_not_null(&y_without_tolerances), *history,\n                           step_size, StepperErrorTolerances{});\n      CHECK(not error_without_tolerances.has_value());\n      CHECK(y_without_tolerances == *y);\n    }\n    stepper.clean_history(history);\n    CAPTURE(substep);\n    REQUIRE((substep == stepper.number_of_substeps_for_error() - 1) ==\n            error.has_value());\n    if (error.has_value()) {\n      CHECK(error->step_time == time_id.step_time());\n      CHECK(error->step_size == step_size);\n      *y_error = error->step_error();\n      REQUIRE(*y_error >= 0.0);\n    }\n    time_id = stepper.next_time_id_for_error(time_id, step_size);\n  }\n  CHECK(time_id.substep() == 0);\n  CHECK(time_id.step_time() - *time == step_size);\n  *time = time_id.step_time();\n}\n}  // namespace\n\nvoid check_multistep_properties(const TimeStepper& stepper) {\n  CHECK(stepper.number_of_substeps() == 1);\n}\n\nvoid check_substep_properties(const TimeStepper& stepper) {\n  CHECK(stepper.number_of_past_steps() == 0);\n  REQUIRE(holds_alternative<TimeSteppers::Tags::FixedOrder>(stepper.order()));\n\n  const Slab slab(0., 1.);\n  TimeStepId id(true, 3, slab.start() + slab.duration() / 2);\n  const TimeSteppers::History<double> history{\n      get<TimeSteppers::Tags::FixedOrder>(stepper.order())};\n  CHECK(stepper.can_change_step_size(id, history));\n}\n\nvoid integrate_test(const TimeStepper& stepper, const size_t order,\n                    const size_t number_of_past_steps,\n                    const double integration_time, const double epsilon) {\n  auto analytic = [](const double t) { return sin(t); };\n  auto rhs = [](const double v, const double /*t*/) {\n    return sqrt(1. - square(v));\n  };\n\n  const uint64_t num_steps = 800;\n  const Slab slab = integration_time > 0\n      ? Slab::with_duration_from_start(0., integration_time)\n      : Slab::with_duration_to_end(0., -integration_time);\n  const TimeDelta step_size = integration_time > 0\n      ? slab.duration() / num_steps\n      : -slab.duration() / num_steps;\n\n  Time time = integration_time > 0 ? slab.start() : slab.end();\n  double y = analytic(time.value());\n  TimeSteppers::History<double> history{order};\n\n  initialize_history(time, make_not_null(&history), analytic, rhs, step_size,\n                     number_of_past_steps);\n\n  for (uint64_t i = 0; i < num_steps; ++i) {\n    take_step(&time, make_not_null(&y), make_not_null(&history), stepper, rhs,\n              step_size);\n    // This check needs a looser tolerance for lower-order time steppers.\n    CHECK(y == approx(analytic(time.value())).epsilon(epsilon));\n  }\n  // Make sure history is being cleaned up.  The limit of 20 is\n  // arbitrary, but much larger than the order of any integrators we\n  // care about and much smaller than the number of time steps in the\n  // test.\n  CHECK(history.size() < 20);\n}\n\nvoid integrate_test_explicit_time_dependence(const TimeStepper& stepper,\n                                             const size_t order,\n                                             const size_t number_of_past_steps,\n                                             const double integration_time,\n                                             const double epsilon) {\n  auto analytic = [](const double t) { return square(t); };\n  auto rhs = [](const double /*v*/, const double t) { return 2 * t; };\n\n  const uint64_t num_steps = 800;\n  const Slab slab = integration_time > 0\n                        ? Slab::with_duration_from_start(0., integration_time)\n                        : Slab::with_duration_to_end(0., -integration_time);\n  const TimeDelta step_size = integration_time > 0\n                                  ? slab.duration() / num_steps\n                                  : -slab.duration() / num_steps;\n\n  Time time = integration_time > 0 ? slab.start() : slab.end();\n  double y = analytic(time.value());\n  TimeSteppers::History<double> history{order};\n\n  initialize_history(time, make_not_null(&history), analytic, rhs, step_size,\n                     number_of_past_steps);\n\n  for (uint64_t i = 0; i < num_steps; ++i) {\n    take_step(&time, make_not_null(&y), make_not_null(&history), stepper, rhs,\n              step_size);\n    // This check needs a looser tolerance for lower-order time steppers.\n    CHECK(y == approx(analytic(time.value())).epsilon(epsilon));\n  }\n  // Make sure history is being cleaned up.  The limit of 20 is\n  // arbitrary, but much larger than the order of any integrators we\n  // care about and much smaller than the number of time steps in the\n  // test.\n  CHECK(history.size() < 20);\n}\n\nvoid integrate_error_test(const TimeStepper& stepper, const size_t order,\n                          const size_t number_of_past_steps,\n                          const double integration_time, const double epsilon,\n                          const size_t num_steps, const double error_factor) {\n  auto analytic = [](const double t) { return sin(t); };\n  auto rhs = [](const double v, const double /*t*/) {\n    return sqrt(1. - square(v));\n  };\n\n  const Slab slab = integration_time > 0\n      ? Slab::with_duration_from_start(0., integration_time)\n      : Slab::with_duration_to_end(0., -integration_time);\n  const TimeDelta step_size = integration_time > 0\n      ? slab.duration() / num_steps\n      : -slab.duration() / num_steps;\n\n  Time time = integration_time > 0 ? slab.start() : slab.end();\n  double y = analytic(time.value());\n  TimeSteppers::History<double> history{order};\n\n  initialize_history(time, make_not_null(&history), analytic, rhs, step_size,\n                     number_of_past_steps);\n  double y_error = std::numeric_limits<double>::signaling_NaN();\n  double previous_y = std::numeric_limits<double>::signaling_NaN();\n  double previous_time = std::numeric_limits<double>::signaling_NaN();\n  for (uint64_t i = 0; i < num_steps; ++i) {\n    take_step_and_check_error(make_not_null(&time), make_not_null(&y),\n                              make_not_null(&y_error), make_not_null(&history),\n                              stepper, rhs, step_size);\n    // This check needs a looser tolerance for lower-order time steppers.\n    CHECK(y == approx(analytic(time.value())).epsilon(epsilon));\n\n    // check that the error measure is a reasonable estimate of the deviation\n    // from the analytic solution. This solution is smooth, so the error should\n    // be dominated by the stepper.\n    if (i > num_steps / 2) {\n      double local_error = abs((y - analytic(time.value())) -\n                               (previous_y - analytic(previous_time)));\n      CHECK(local_error < std::max(y_error, 1e-14));\n      CHECK(local_error > error_factor * y_error);\n    }\n    previous_y = y;\n    previous_time = time.value();\n  }\n  // Make sure history is being cleaned up.  The limit of 20 is\n  // arbitrary, but much larger than the order of any integrators we\n  // care about and much smaller than the number of time steps in the\n  // test.\n  CHECK(history.size() < 20);\n}\n\nvoid integrate_variable_test(const TimeStepper& stepper, const size_t order,\n                             const size_t number_of_past_steps,\n                             const double epsilon) {\n  auto analytic = [](const double t) { return sin(t); };\n  auto rhs = [](const double v, const double /*t*/) {\n    return sqrt(1. - square(v));\n  };\n\n  const uint64_t num_steps = 800;\n  const double average_step = 1. / num_steps;\n\n  Slab slab = Slab::with_duration_to_end(0., average_step);\n  Time time = slab.end();\n  double y = analytic(time.value());\n\n  TimeSteppers::History<double> history{order};\n  initialize_history(time, make_not_null(&history), analytic, rhs,\n                     slab.duration(), number_of_past_steps);\n\n  for (uint64_t i = 0; i < num_steps; ++i) {\n    slab = slab.advance().with_duration_from_start(\n        (1. + 0.5 * sin(i)) * average_step);\n    time = time.with_slab(slab);\n\n    take_step(&time, make_not_null(&y), make_not_null(&history), stepper, rhs,\n              slab.duration());\n    // This check needs a looser tolerance for lower-order time steppers.\n    CHECK(y == approx(analytic(time.value())).epsilon(epsilon));\n  }\n}\n\nnamespace {\n// This checks the stability of dy/dt = [exp(i phase) - 1] y.  To do\n// this, it uses the fact that time steppers can be considered as\n// linear operators on their history values.  It constructs the\n// operator for the passed stepper with the given step and phase\n// explicitly, and then checks whether all the eigenvalues have\n// magnitude less than one.\nbool step_is_stable(const TimeStepper& stepper, const double step_size,\n                    const double phase) {\n  const size_t operator_size = stepper.number_of_past_steps() + 1;\n  const Slab slab(0.0, step_size * (operator_size + 1));\n  const auto step_delta = slab.duration() / (operator_size + 1);\n  const std::complex<double> coefficient = std::polar(1.0, phase) - 1.0;\n  const auto rhs = [&coefficient](const std::complex<double> v,\n                                  const double /*t*/) {\n    return coefficient * v;\n  };\n\n  blaze::DynamicMatrix<std::complex<double>, blaze::columnMajor>\n      stepper_operator(operator_size, operator_size, 0.0);\n  // Add entries for shifting the history backwards after the step.\n  for (size_t i = 0; i < operator_size - 1; ++i) {\n    stepper_operator(i, i + 1) = 1.0;\n  }\n\n  // Construct the entries for the linear combination forming the new\n  // value.\n  for (size_t step_to_test = 0; step_to_test < operator_size; ++step_to_test) {\n    TimeSteppers::History<std::complex<double>> history{\n        get<TimeSteppers::Tags::FixedOrder>(stepper.order())};\n    TimeStepId id(true, 0, slab.start());\n    for (size_t past_step = 0; past_step < operator_size - 1; ++past_step) {\n      if (past_step == step_to_test) {\n        history.insert(id, 1.0, rhs(1.0, 0.0));\n      } else {\n        history.insert(id, 0.0, 0.0);\n      }\n      id = id.next_step(step_delta);\n    }\n    Time time = id.step_time();\n    std::complex<double> y = step_to_test == operator_size - 1 ? 1.0 : 0.0;\n    take_step(make_not_null(&time), make_not_null(&y), make_not_null(&history),\n              stepper, rhs, step_delta);\n    stepper_operator(operator_size - 1, step_to_test) = y;\n  }\n\n  const blaze::DynamicVector<std::complex<double>> stepper_eigenvalues =\n      eigen(stepper_operator);\n  return alg::all_of(stepper_eigenvalues, [](const std::complex<double> e) {\n    return norm(e) <= 1.0;\n  });\n}\n}  // namespace\n\nvoid stability_test(const TimeStepper& stepper, const double phase) {\n  const double bracket_size = 1.001;\n\n  CHECK(step_is_stable(stepper, stepper.stable_step() / bracket_size, phase));\n  CHECK(\n      not step_is_stable(stepper, stepper.stable_step() * bracket_size, phase));\n\n  // Check that the phase is correct.  Doing a global check is too\n  // expensive, but we can check that the passed value is a local\n  // minimum.\n  CHECK(step_is_stable(stepper, stepper.stable_step() / bracket_size,\n                       phase + 0.1));\n  CHECK(step_is_stable(stepper, stepper.stable_step() / bracket_size,\n                       phase - 0.1));\n}\n\nvoid check_convergence_order(const TimeStepper& stepper,\n                             const std::pair<int32_t, int32_t>& step_range,\n                             const bool output) {\n  const size_t order = get<TimeSteppers::Tags::FixedOrder>(stepper.order());\n  const auto do_integral = [&order, &stepper](const int32_t num_steps) {\n    const Slab slab(0., 1.);\n    const TimeDelta step_size = slab.duration() / num_steps;\n\n    Time time = slab.start();\n    double y = 1.;\n    TimeSteppers::History<double> history{order};\n    const auto rhs = [](const double v, const double /*t*/) { return v; };\n    initialize_history(\n        time, make_not_null(&history), [](const double t) { return exp(t); },\n        rhs, step_size, stepper.number_of_past_steps());\n    while (time < slab.end()) {\n      take_step(&time, make_not_null(&y), make_not_null(&history), stepper, rhs,\n                step_size);\n    }\n    const double result = abs(y - exp(1.));\n    return result;\n  };\n  CHECK(convergence_rate(step_range, 1, do_integral, output) ==\n        approx(order).margin(0.4));\n}\n\nvoid check_dense_output(\n    const TimeStepper& stepper,\n    const std::pair<int32_t, int32_t>& convergence_step_range,\n    const int32_t stride, const bool check_backward_continuity) {\n  const size_t order = get<TimeSteppers::Tags::FixedOrder>(stepper.order());\n  const auto get_dense = [&order, &stepper](const TimeDelta& step_size,\n                                            const double time) {\n    const auto impl = [&order, &stepper, &step_size,\n                       &time](const bool use_error_methods) {\n      CAPTURE(use_error_methods);\n      TimeStepId time_id(step_size.is_positive(), 0,\n                         step_size.is_positive() ? step_size.slab().start()\n                                                 : step_size.slab().end());\n      const evolution_less<double> before{time_id.time_runs_forward()};\n      double y = 1.;\n      TimeSteppers::History<double> history{order};\n      initialize_history(\n          time_id.step_time(), make_not_null(&history),\n          [](const double t) { return exp(t); },\n          [](const double v, const double /*t*/) { return v; }, step_size,\n          stepper.number_of_past_steps());\n      auto step = step_size;\n      for (;;) {\n        history.insert(time_id, y, y);\n        if (before(time, (time_id.step_time() + step).value())) {\n          // Make sure the initial value is preserved.\n          y = 2.0 * *history.step_start(time).value;\n          if (stepper.dense_update_u(make_not_null(&y), history, time)) {\n            return y - *history.step_start(time).value;\n          }\n          REQUIRE(not before(time, time_id.step_time().value()));\n          CHECK(not stepper.monotonic());\n        }\n        y = std::numeric_limits<double>::signaling_NaN();\n        if (use_error_methods) {\n          stepper.update_u(make_not_null(&y), history, step,\n                           StepperErrorTolerances{});\n        } else {\n          stepper.update_u(make_not_null(&y), history, step);\n        }\n        stepper.clean_history(make_not_null(&history));\n        time_id = use_error_methods\n                      ? stepper.next_time_id_for_error(time_id, step)\n                      : stepper.next_time_id(time_id, step);\n        step = step.with_slab(time_id.step_time().slab());\n      }\n    };\n    const double with_error = impl(true);\n    const double without_error = impl(false);\n    CHECK(with_error == approx(without_error));\n    return without_error;\n  };\n\n  // Check that the dense output is continuous\n  {\n    const auto local_approx = Approx::custom().scale(1.0).epsilon(1e-12);\n    for (const auto time_step :\n         {Slab(0., 1.).duration(), -Slab(-1., 0.).duration()}) {\n      CAPTURE(time_step);\n      Time time = Slab(0., 1.).start().with_slab(time_step.slab());\n      double y = 1.;\n      TimeSteppers::History<double> history{order};\n      const auto rhs = [](const double v, const double /*t*/) { return v; };\n      initialize_history(\n          time, make_not_null(&history), [](const double t) { return exp(t); },\n          rhs, time_step, stepper.number_of_past_steps());\n      take_step(&time, make_not_null(&y), make_not_null(&history), stepper, rhs,\n                time_step);\n\n      // Some time steppers special-case the endpoints of the\n      // interval, so check just inside to trigger the main dense\n      // output path.\n      CHECK(get_dense(time_step, 0.0) == local_approx(1.));\n      CHECK(get_dense(time_step, std::numeric_limits<double>::epsilon() *\n                                     time_step.value()) == local_approx(1.));\n      if (check_backward_continuity) {\n        CHECK(\n            get_dense(time_step, (1. - std::numeric_limits<double>::epsilon()) *\n                                     time.value()) == local_approx(y));\n      }\n      CHECK(get_dense(time_step, time.value()) == local_approx(y));\n    }\n  }\n\n  // Test convergence\n  {\n    const auto error = [&get_dense](const int32_t steps) {\n      const Slab slab(0., 1.);\n      return abs(get_dense(slab.duration() / steps, 0.25 * M_PI) -\n                 exp(0.25 * M_PI));\n    };\n    CHECK(convergence_rate(convergence_step_range, stride, error) ==\n          approx(order).margin(0.4));\n\n    const auto error_backwards = [&get_dense](const int32_t steps) {\n      const Slab slab(-1., 0.);\n      return abs(get_dense(-slab.duration() / steps, -0.25 * M_PI) -\n                 exp(-0.25 * M_PI));\n    };\n    CHECK(convergence_rate(convergence_step_range, stride, error_backwards) ==\n          approx(order).margin(0.4));\n  }\n}\n\nvoid check_strong_stability_preservation(const TimeStepper& stepper,\n                                         const double step_size) {\n  // Check that each substep is a convex combination of partial Euler steps.\n  ASSERT(stepper.number_of_past_steps() == 0,\n         \"Unimplemented for multistep methods\");\n\n  const auto impl = [&](const size_t order,\n                        const uint64_t number_of_substeps_to_test,\n                        const auto update_u, const auto next_time_id) {\n    const Slab slab(0.0, step_size);\n    const auto time_step = slab.duration();\n\n    for (uint64_t substep_being_varied = 0;\n         substep_being_varied < number_of_substeps_to_test;\n         ++substep_being_varied) {\n      CAPTURE(substep_being_varied);\n      TimeStepId time_step_id(true, 0, slab.start());\n      TimeSteppers::History<double> value_history(order);\n      TimeSteppers::History<double> derivative_history(order);\n      for (uint64_t substep_being_tested = 0;\n           substep_being_tested < number_of_substeps_to_test;\n           ++substep_being_tested) {\n        CAPTURE(substep_being_tested);\n        if (substep_being_tested == substep_being_varied) {\n          value_history.insert(time_step_id, 1.0, 0.0);\n          derivative_history.insert(time_step_id, 0.0, 1.0);\n        } else {\n          value_history.insert(time_step_id, 0.0, 0.0);\n          derivative_history.insert(time_step_id, 0.0, 0.0);\n        }\n        double u = std::numeric_limits<double>::signaling_NaN();\n        update_u(make_not_null(&u), value_history, time_step);\n        stepper.clean_history(make_not_null(&value_history));\n        const double value_dependence = u;\n        update_u(make_not_null(&u), derivative_history, time_step);\n        stepper.clean_history(make_not_null(&derivative_history));\n        const double derivative_dependence = u;\n        CHECK(value_dependence >= 0.0);\n        CHECK(derivative_dependence >= 0.0);\n        CHECK(step_size * derivative_dependence <= approx(value_dependence));\n        time_step_id = next_time_id(time_step_id, time_step);\n      }\n    }\n  };\n\n  const size_t order = get<TimeSteppers::Tags::FixedOrder>(stepper.order());\n  {\n    INFO(\"Without error estimate\");\n    impl(\n        order, stepper.number_of_substeps(),\n        [&](const gsl::not_null<double*> u,\n            const TimeSteppers::History<double>& history,\n            const TimeDelta& time_step) {\n          stepper.update_u(u, history, time_step);\n        },\n        [&](const TimeStepId& time_step_id, const TimeDelta& time_step) {\n          return stepper.next_time_id(time_step_id, time_step);\n        });\n  }\n  {\n    INFO(\"With error estimate\");\n    impl(\n        order, stepper.number_of_substeps_for_error(),\n        [&](const gsl::not_null<double*> u,\n            const TimeSteppers::History<double>& history,\n            const TimeDelta& time_step) {\n          stepper.update_u(u, history, time_step, StepperErrorTolerances{});\n        },\n        [&](const TimeStepId& time_step_id, const TimeDelta& time_step) {\n          return stepper.next_time_id_for_error(time_step_id, time_step);\n        });\n  }\n}\n}  // namespace TimeStepperTestUtils\n"
  },
  {
    "path": "tests/Unit/Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <fstream>\n#include <utility>\n#include <vector>\n\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\n/// \\cond\nclass TimeStepper;\n/// \\endcond\n\nnamespace TimeStepperTestUtils {\n\nvoid check_multistep_properties(const TimeStepper& stepper);\n\nvoid check_substep_properties(const TimeStepper& stepper);\n\nvoid integrate_test(const TimeStepper& stepper, size_t order,\n                    size_t number_of_past_steps, double integration_time,\n                    double epsilon);\n\nvoid integrate_test_explicit_time_dependence(const TimeStepper& stepper,\n                                             size_t order,\n                                             size_t number_of_past_steps,\n                                             double integration_time,\n                                             double epsilon);\n\nvoid integrate_variable_test(const TimeStepper& stepper, size_t order,\n                             size_t number_of_past_steps, double epsilon);\n\nvoid integrate_error_test(const TimeStepper& stepper, size_t order,\n                          size_t number_of_past_steps, double integration_time,\n                          double epsilon, size_t num_steps,\n                          double error_factor);\n\ntemplate <typename F1, typename F2, typename EvolvedType>\nvoid initialize_history(\n    Time time,\n    const gsl::not_null<TimeSteppers::History<EvolvedType>*> history,\n    F1&& analytic, F2&& rhs, TimeDelta step_size,\n    const size_t number_of_past_steps) {\n  int64_t slab_number = -1;\n  for (size_t j = 0; j < number_of_past_steps; ++j) {\n    ASSERT(time.slab() == step_size.slab(), \"Slab mismatch\");\n    if ((step_size.is_positive() and time.is_at_slab_start()) or\n        (not step_size.is_positive() and time.is_at_slab_end())) {\n      const Slab new_slab = time.slab().advance_towards(-step_size);\n      time = time.with_slab(new_slab);\n      step_size = step_size.with_slab(new_slab);\n      --slab_number;\n    }\n    time -= step_size;\n    history->insert_initial(\n        TimeStepId(step_size.is_positive(), slab_number, time),\n        analytic(time.value()), rhs(analytic(time.value()), time.value()));\n  }\n}\n\n/// Check that the reported stable step is correct.  The \\p phase\n/// argument is the phase that is most unstable in the differential\n/// equation \\f$y' = (\\exp(i \\phi) - 1) y\\f$.  For most common time\n/// steppers this is \\f$\\pi\\f$.\nvoid stability_test(const TimeStepper& stepper, double phase = M_PI);\n\n/// Check that integration converges as expected.\n///\n/// The \\p step_range argument specifies the range of the number of\n/// steps used to produce a fit, and should be cover a factor of a few\n/// over which a log-log plot of the error is roughly linear.  An\n/// appropriate value can be determined by passing `true` as the \\p\n/// output argument, which will produce a `convergence.dat` file.\nvoid check_convergence_order(const TimeStepper& stepper,\n                             const std::pair<int32_t, int32_t>& step_range,\n                             bool output = false);\n\nvoid check_dense_output(\n    const TimeStepper& stepper,\n    const std::pair<int32_t, int32_t>& convergence_step_range, int32_t stride,\n    bool check_backward_continuity);\n\nvoid check_strong_stability_preservation(const TimeStepper& stepper,\n                                         double step_size);\n\ntemplate <typename F>\ndouble convergence_rate(\n    const std::pair<int32_t, int32_t>& number_of_steps_range,\n    const int32_t stride, F&& error, const bool output = false) {\n  // We do a least squares fit on a log-log error-vs-steps plot.  The\n  // unequal points caused by the log scale will introduce some bias,\n  // but the typical range this is used for is only a factor of a few,\n  // so it shouldn't be too bad.\n\n  // Make sure testing code is not left enabled.\n  CHECK(not output);\n\n  std::ofstream output_stream{};\n  if (output) {\n    output_stream.open(\"convergence.dat\");\n    output_stream.precision(18);\n  }\n\n  uint32_t num_tests = 0;\n  std::vector<double> log_steps;\n  std::vector<double> log_errors;\n  for (auto steps = number_of_steps_range.first;\n       steps <= number_of_steps_range.second;\n       steps += stride) {\n    const double this_error = abs(error(steps));\n    if (output) {\n      output_stream << steps << \"\\t\" << this_error << std::endl;\n    }\n    log_steps.push_back(log(steps));\n    log_errors.push_back(log(this_error));\n    ++num_tests;\n  }\n  const double average_log_steps = alg::accumulate(log_steps, 0.0) / num_tests;\n  const double average_log_errors =\n      alg::accumulate(log_errors, 0.0) / num_tests;\n  double numerator = 0.0;\n  double denominator = 0.0;\n  for (size_t i = 0; i < num_tests; ++i) {\n    numerator += (log_steps[i] - average_log_steps) *\n        (log_errors[i] - average_log_errors);\n    denominator += square(log_steps[i] - average_log_steps);\n  }\n  return -numerator / denominator;\n}\n}  // namespace TimeStepperTestUtils\n"
  },
  {
    "path": "tests/Unit/Helpers/Utilities/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Serialization)\n"
  },
  {
    "path": "tests/Unit/Helpers/Utilities/Serialization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"SerializationHelpers\")\n\nset(LIBRARY_SOURCES\n  Versioning.cpp\n  )\n\nadd_spectre_library(${LIBRARY} ${SPECTRE_TEST_LIBS_TYPE} ${LIBRARY_SOURCES})\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  Framework\n  Serialization\n  Utilities\n  PRIVATE\n  Informer\n  )\n"
  },
  {
    "path": "tests/Unit/Helpers/Utilities/Serialization/Versioning.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/Utilities/Serialization/Versioning.hpp\"\n\n#include <cstddef>\n#include <filesystem>\n#include <fstream>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/Base64.hpp\"\n\nnamespace TestHelpers::serialization::versioning_detail {\nnamespace {\nconstexpr char delimiter = ':';\n\nstd::filesystem::path full_path(const std::string& filename) {\n  return std::filesystem::path(unit_test_src_path()) / filename;\n}\n}  // namespace\n\nstd::vector<std::pair<std::string, std::vector<std::byte>>> read_serializations(\n    const std::string& filename) {\n  const auto file_path = full_path(filename);\n  std::ifstream file(file_path);\n  if (not file) {\n    return {};\n  }\n\n  std::vector<std::pair<std::string, std::vector<std::byte>>>\n      serializations_to_test{};\n  std::string line{};\n  while (std::getline(file, line)) {\n    auto label_end = line.rfind(delimiter);\n    {\n      INFO(\"Malformed version entry on line \" +\n           std::to_string(serializations_to_test.size() + 1));\n      REQUIRE(label_end != std::string::npos);\n    }\n    serializations_to_test.emplace_back(\n        line.substr(0, label_end), base64_decode(line.substr(label_end + 1)));\n  }\n  return serializations_to_test;\n}\n\nvoid write_serialization(const std::string& filename, const std::string& label,\n                         const std::vector<std::byte>& serialization) {\n  const auto file_path = full_path(filename);\n  std::ofstream file(file_path, std::ios_base::app);\n  {\n    INFO(\"Failed to open \" + file_path.string());\n    REQUIRE(file);\n  }\n  file << label << delimiter << base64_encode(serialization) << \"\\n\";\n  REQUIRE(file);\n  INFO(\"New entry written for label \" + label +\n       \".  Disable generate_new_entry for future runs.\");\n  CHECK(false);\n}\n}  // namespace TestHelpers::serialization::versioning_detail\n"
  },
  {
    "path": "tests/Unit/Helpers/Utilities/Serialization/Versioning.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace TestHelpers {\n/// \\brief Collection of classes and functions for testing serialization\nnamespace serialization {\nnamespace versioning_detail {\nstd::vector<std::pair<std::string, std::vector<std::byte>>> read_serializations(\n    const std::string& filename);\n\nvoid write_serialization(const std::string& filename, const std::string& label,\n                         const std::vector<std::byte>& serialization);\n\ntemplate <typename Compare>\nstruct ToCompare {\n  const Compare& operator()(const Compare& x) const { return x; }\n\n  template <typename Base>\n  const Compare& operator()(const std::unique_ptr<Base>& x) const {\n    return dynamic_cast<const Compare&>(*x);\n  }\n};\n\ntemplate <>\nstruct ToCompare<void> {\n  template <typename Serialize>\n  const Serialize& operator()(const Serialize& x) const {\n    static_assert(not tt::is_a_v<std::unique_ptr, Serialize>,\n                  \"Cannot compare unique_ptrs.  Pass a template argument to \"\n                  \"test_versioning to set type to compare.\");\n    return x;\n  }\n};\n}  // namespace versioning_detail\n\n/// Test serialization of a versioned class against old versions.\n///\n/// Old serializations of the class are stored in \\p filename\n/// (relative to the unit test source directory).  Each stored version\n/// has a label, where the current label must match \\p current_label.\n/// This function checks that\n///\n/// * The last entry matches \\p current_label and matches the result\n///   of serializing \\p current_object.\n///\n/// * Old lines in the file are successfully deserialized and give\n///   expected values.\n///\n/// By default, old versions are expected to deserialize to \\p\n/// current_object, but if the object represented by the serialized\n/// entries has changed (for example, because the \\p current_object\n/// has newer options enabled), objects can be passed in \\p\n/// old_objects, keyed by the last version they should match.  If\n/// deserialization support is dropped up to a specific entry, an\n/// error can be checked for by instead adding a string to \\p\n/// old_objects.\n///\n/// By default, objects are directly compared for equality.  If the\n/// objects being tested are stored in `std::unique_ptr`s the \\p\n/// Compare template parameter can be passed in to dereference and\n/// `dynamic_cast` the contained value before comparing.\n///\n/// If \\p generate_new_entry is true and \\p current_label and \\p\n/// current_object do not match the last serialization in the file, a\n/// new entry will be appended.\ntemplate <typename Compare = void, typename Serialize>\nvoid test_versioning(\n    const std::string& filename, const std::string& current_label,\n    const Serialize& current_object,\n    const std::unordered_map<std::string, std::variant<Serialize, std::string>>&\n        old_objects = {},\n    const bool generate_new_entry = false) {\n  const versioning_detail::ToCompare<Compare> to_compare{};\n\n  {\n    INFO(\"Current version should not be listed in old_objects.\");\n    CHECK(old_objects.count(current_label) == 0);\n  }\n\n  const auto serializations_to_test =\n      versioning_detail::read_serializations(filename);\n\n  std::unordered_set<std::string> unused_labels{};\n  for (const auto& old_entry : old_objects) {\n    unused_labels.insert(old_entry.first);\n  }\n\n  std::variant<const Serialize*, const std::string*> expected = &current_object;\n  for (auto serialization_test = serializations_to_test.rbegin();\n       serialization_test != serializations_to_test.rend();\n       ++serialization_test) {\n    const auto& [version, serialization] = *serialization_test;\n    CAPTURE(version);\n    if (const auto old_object = old_objects.find(version);\n        old_object != old_objects.end()) {\n      unused_labels.erase(version);\n      std::visit([&](const auto& old) { expected = &old; }, old_object->second);\n    }\n\n    if (std::holds_alternative<const Serialize*>(expected)) {\n      CHECK(to_compare(deserialize<Serialize>(serialization.data())) ==\n            to_compare(*std::get<const Serialize*>(expected)));\n    } else {\n      CHECK_THROWS_WITH(deserialize<Serialize>(serialization.data()),\n                        Catch::Matchers::ContainsSubstring(\n                            *std::get<const std::string*>(expected)));\n    }\n  }\n  {\n    CAPTURE(unused_labels);\n    CHECK(unused_labels.empty());\n  }\n\n  const std::vector<char> current_serialization_chars =\n      serialize(current_object);\n  std::vector<std::byte> current_serialization(\n      current_serialization_chars.size());\n  std::transform(current_serialization_chars.begin(),\n                 current_serialization_chars.end(),\n                 current_serialization.begin(),\n                 [](const char c) { return static_cast<std::byte>(c); });\n\n  if (generate_new_entry) {\n    {\n      INFO(\"Entry already present.\");\n      REQUIRE(\n          (serializations_to_test.empty() or\n           (current_label != serializations_to_test.back().first and\n            current_serialization != serializations_to_test.back().second)));\n    }\n    REQUIRE(to_compare(deserialize<Serialize>(current_serialization.data())) ==\n            to_compare(current_object));\n    versioning_detail::write_serialization(filename, current_label,\n                                           current_serialization);\n  } else {\n    REQUIRE(not serializations_to_test.empty());\n    CHECK(current_label == serializations_to_test.back().first);\n    CHECK(current_serialization == serializations_to_test.back().second);\n  }\n}\n}  // namespace serialization\n}  // namespace TestHelpers\n"
  },
  {
    "path": "tests/Unit/IO/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_IO\")\n\nset(LIBRARY_SOURCES\n  Test_ComposeTable.cpp\n  Test_Connectivity.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  IO\n  Informer\n  Utilities\n  )\n\nadd_subdirectory(Exporter)\nadd_subdirectory(External)\nadd_subdirectory(H5)\nadd_subdirectory(Importers)\nadd_subdirectory(Logging)\nadd_subdirectory(Observers)\n"
  },
  {
    "path": "tests/Unit/IO/Exporter/BundledExporter/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This is a standalone CMake project to test the SpECTRE exporter library in an\n# external project. It is not part of the SpECTRE build system.\n#\n# Instructions to build this project:\n# - Configure a build directory and set the `SPECTRE_ROOT` variable to the\n#   directory containing the compiled SpECTRE libraries, e.g. the build\n#   directory of the SpECTRE project:\n#\n#   mkdir build && cd build\n#   cmake -D SPECTRE_ROOT=/path/to/spectre/build/directory /path/to/this/project\n#\n# - Build and run the test executable:\n#\n#   make\n#   ./TestSpectreExporter --help\n\ncmake_minimum_required(VERSION 3.18)\nproject(TestSpectreExporter)\n\nset(CMAKE_CXX_STANDARD 17)\n\n# Find SpECTRE exporter lib\nfind_library(\n  SPECTRE_EXPORTER_LIB\n  NAMES BundledExporter\n  PATH_SUFFIXES lib\n  PATHS ${SPECTRE_ROOT})\nfind_path(\n  SPECTRE_EXPORTER_INCLUDE_DIR\n  NAMES spectre/Exporter.hpp\n  PATH_SUFFIXES include\n  PATHS ${SPECTRE_ROOT})\nadd_library(spectre::Exporter UNKNOWN IMPORTED)\nset_target_properties(\n  spectre::Exporter\n  PROPERTIES IMPORTED_LOCATION ${SPECTRE_EXPORTER_LIB}\n             INTERFACE_INCLUDE_DIRECTORIES ${SPECTRE_EXPORTER_INCLUDE_DIR})\n\n# Find external libs\nfind_package(HDF5 REQUIRED COMPONENTS C)\nfind_package(BLAS REQUIRED)\n\n# Define test executable\nadd_executable(TestSpectreExporter Test_BundledExporter.cpp)\ntarget_link_libraries(TestSpectreExporter PRIVATE spectre::Exporter)\n"
  },
  {
    "path": "tests/Unit/IO/Exporter/BundledExporter/Test_BundledExporter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cmath>\n#include <iostream>\n#include <string>\n\n#include <spectre/Exporter.hpp>\n\nint main(int argc, char** argv) {\n  // Parse CLI arguments\n  // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  if (argc != 9) {\n    std::cerr << \"Usage: \" << argv[0]\n              << \" FILES_GLOB_PATTERN SUBFILE_NAME STEP FIELD X Y Z EXPECTED\"\n              << std::endl;\n    return 1;\n  }\n  const std::string files_glob_pattern = argv[1];\n  const std::string subfile_name = argv[2];\n  const int step = std::stoi(argv[3]);\n  const std::string field = argv[4];\n  const double x = std::stod(argv[5]);\n  const double y = std::stod(argv[6]);\n  const double z = std::stod(argv[7]);\n  const double expected = std::stod(argv[8]);\n  // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  // Interpolate data\n  const auto interpolated_data = spectre::Exporter::interpolate_to_points<3>(\n      files_glob_pattern, subfile_name,\n      spectre::Exporter::ObservationStep{step}, {field}, {{{x}, {y}, {z}}});\n  const double result = interpolated_data[0][0];\n  // Check result\n  if (std::abs(result - expected) < 1.e-10) {\n    std::cout << \"SUCCESS\" << std::endl;\n    return 0;\n  } else {\n    std::cerr << \"FAILURE. Result is: \" << result << std::endl;\n    return 1;\n  }\n}\n"
  },
  {
    "path": "tests/Unit/IO/Exporter/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Exporter\")\n\nset(LIBRARY_SOURCES\n  Test_Exporter.cpp\n  Test_SpacetimeInterpolator.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainCreators\n  Exporter\n  H5\n  Informer\n  Serialization\n  Spectral\n  Utilities\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/IO/Exporter/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.Exporter.Python.InterpolateToPoints\"\n  Test_InterpolateToPoints.py\n  \"unit;python\"\n  PyExporter)\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.Exporter.Python.SpacetimeInterpolator\"\n  Test_SpacetimeInterpolator.py\n  \"unit;python\"\n  PyExporter)\n"
  },
  {
    "path": "tests/Unit/IO/Exporter/Python/Test_InterpolateToPoints.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom click.testing import CliRunner\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Frame, Scalar, tnsr\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.IO.Exporter import (\n    ObservationId,\n    PointwiseInterpolator3DInertial,\n    interpolate_tensors_to_points,\n)\nfrom spectre.IO.Exporter.InterpolateToPoints import (\n    interpolate_to_points_command,\n)\nfrom spectre.IO.H5 import list_observations, open_volfiles\n\n\nclass TestInterpolateToPoints(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization\", \"InterpolateToPoints\"\n        )\n        self.h5_filename = os.path.join(\n            unit_test_src_path(), \"Visualization/Python\", \"VolTestData0.h5\"\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_interpolate_tensors_to_points(self):\n        obs_id = list_observations(\n            open_volfiles(self.h5_filename, \"/element_data\")\n        )[0][0]\n        for frame in [Frame.Grid, Frame.Inertial]:\n            coords = tnsr.I[DataVector, 3, frame](\n                np.array([3 * [0.0], 3 * [2 * np.pi]]).T\n            )\n            (psi,) = interpolate_tensors_to_points(\n                self.h5_filename,\n                \"element_data\",\n                observation=ObservationId(obs_id),\n                tensor_names=[\"Psi\"],\n                tensor_types=[Scalar[DataVector]],\n                target_points=coords,\n            )\n            self.assertAlmostEqual(psi.get()[0], -0.07059806932542323)\n        interpolator = PointwiseInterpolator3DInertial(\n            self.h5_filename,\n            subfile_name=\"element_data\",\n            observation=ObservationId(obs_id),\n            tensor_components=[\"Psi\"],\n        )\n        self.assertAlmostEqual(\n            interpolator.interpolate_to_point(np.zeros(3))[0],\n            -0.07059806932542323,\n        )\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            interpolate_to_points_command,\n            [\n                self.h5_filename,\n                \"-d\",\n                \"element_data\",\n                \"--step\",\n                \"0\",\n                \"-y\",\n                \"Psi\",\n                \"-p\",\n                \"0,0,0\",\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertIn(\"-0.0705980\", result.output, result.output)\n\n        coords_file = os.path.join(self.test_dir, \"coords.txt\")\n        coords = np.array([3 * [0.0], 3 * [2 * np.pi]])\n        np.savetxt(coords_file, coords)\n        result_file = os.path.join(self.test_dir, \"result.txt\")\n        result = runner.invoke(\n            interpolate_to_points_command,\n            [\n                self.h5_filename,\n                \"-d\",\n                \"element_data\",\n                \"--step\",\n                \"0\",\n                \"-y\",\n                \"Psi\",\n                \"-t\",\n                coords_file,\n                \"-o\",\n                result_file,\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        result_data = np.loadtxt(result_file)\n        npt.assert_allclose(\n            result_data, np.hstack((coords, [[-0.07059807], [-0.06781784]]))\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/Exporter/Python/Test_SpacetimeInterpolator.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Frame, Scalar, tnsr\nfrom spectre.Domain import ElementId, serialize_domain\nfrom spectre.Domain.Creators import Brick\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.IO.Exporter import ObservationStep, SpacetimeInterpolator\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\nfrom spectre.Spectral import Basis, Mesh, Quadrature\n\n\nclass TestSpacetimeInterpolator(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"IO/Exporter/SpacetimeInterpolator\"\n        )\n        self.h5_filename = os.path.join(self.test_dir, \"VolumeData.h5\")\n        os.makedirs(self.test_dir, exist_ok=True)\n\n        # Write some volume data\n        domain = Brick(\n            lower_bounds=[0.0, 0.0, 0.0],\n            upper_bounds=[1.0, 1.0, 1.0],\n            initial_refinement_levels=[0, 0, 0],\n            initial_num_points=[4, 4, 4],\n            is_periodic=[False, False, False],\n        ).create_domain()\n        serialized_domain = serialize_domain(domain)\n        mesh = Mesh[3](4, Basis.Legendre, Quadrature.GaussLobatto)\n        with spectre_h5.H5File(self.h5_filename, \"w\") as open_h5file:\n            volfile = open_h5file.insert_vol(\"/VolumeData\", version=0)\n            for i in range(5):\n                volfile.write_volume_data(\n                    observation_id=i,\n                    observation_value=i,\n                    elements=[\n                        ElementVolumeData(\n                            ElementId[3](\"[B0,(L0I0,L0I0,L0I0)]\"),\n                            [TensorComponent(\"Psi\", np.ones(4**3) * i)],\n                            mesh,\n                        ),\n                    ],\n                    serialized_domain=serialized_domain,\n                )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_spacetime_interpolator(self):\n        interpolator = SpacetimeInterpolator[3](\n            self.h5_filename,\n            subfile_name=\"VolumeData\",\n            tensor_components=[\"Psi\"],\n        )\n        self.assertEqual(interpolator.max_time_bounds(), [1, 3])\n        interpolator.load_time_bounds([1.5, 3])\n        self.assertEqual(interpolator.time_bounds(), [1, 3])\n        (psi,) = interpolator.interpolate_to_point(np.zeros(3), time=2.5)\n        self.assertAlmostEqual(psi, 2.5)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/Exporter/Test_Exporter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <vector>\n#ifdef _OPENMP\n#include <omp.h>\n#endif  // _OPENMP\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/BinaryCompactObject.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"IO/Exporter/Exporter.hpp\"\n#include \"IO/Exporter/PointwiseInterpolator.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace spectre::Exporter {\n\nSPECTRE_TEST_CASE(\"Unit.IO.Exporter\", \"[Unit]\") {\n#ifdef _OPENMP\n  // Disable OpenMP multithreading since multiple unit tests may run in parallel\n  omp_set_num_threads(1);\n#endif\n\n  const auto check_scalar_data = [](const auto& interpolated_data) {\n    const auto& psi = interpolated_data[0];\n    CHECK(psi[0] == approx(-0.07059806932542323));\n    CHECK(psi[1] == approx(0.7869554122196492));\n    CHECK(psi[2] == approx(0.9876185584100299));\n    const auto& phi_y = interpolated_data[2];\n    CHECK(phi_y[0] == approx(1.0569673471948728));\n    CHECK(phi_y[1] == approx(0.6741524090220188));\n    CHECK(phi_y[2] == approx(0.2629752479142838));\n  };\n\n  {\n    INFO(\"Bundled volume data files\");\n    const auto interpolated_data = interpolate_to_points<3>(\n        unit_test_src_path() + \"/Visualization/Python/VolTestData*.h5\",\n        \"element_data\", ObservationStep{0}, {\"Psi\", \"Phi_x\", \"Phi_y\", \"Phi_z\"},\n        {{{0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}}});\n    check_scalar_data(interpolated_data);\n  }\n  {\n    INFO(\"Tensor interface\");\n    // [interpolate_tensors_to_points_example]\n    const tnsr::I<DataVector, 3> target_points{\n        {{{0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}}}};\n    const auto interpolated_data = interpolate_to_points<\n        tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Phi<3>>>(\n        unit_test_src_path() + \"/Visualization/Python/VolTestData*.h5\",\n        \"element_data\", ObservationStep{0}, target_points);\n    const auto& psi = get(get<ScalarWave::Tags::Psi>(interpolated_data));\n    // [interpolate_tensors_to_points_example]\n    CHECK(psi[0] == approx(-0.07059806932542323));\n    CHECK(psi[1] == approx(0.7869554122196492));\n    CHECK(psi[2] == approx(0.9876185584100299));\n    const auto& phi_y =\n        get<1>(get<ScalarWave::Tags::Phi<3>>(interpolated_data));\n    CHECK(phi_y[0] == approx(1.0569673471948728));\n    CHECK(phi_y[1] == approx(0.6741524090220188));\n    CHECK(phi_y[2] == approx(0.2629752479142838));\n  }\n  {\n    INFO(\"PointwiseInterpolator interface\");\n    const PointwiseInterpolator<3, Frame::Inertial> interpolator{\n        unit_test_src_path() + \"/Visualization/Python/VolTestData*.h5\",\n        \"element_data\",\n        ObservationStep{0},\n        {\"Psi\", \"Phi_x\", \"Phi_y\", \"Phi_z\"}};\n    CHECK(interpolator.time() == approx(0.04));\n    {\n      INFO(\"Multiple points\");\n      std::vector<DataVector> interpolated_data{};\n      interpolator.interpolate_to_points(\n          make_not_null(&interpolated_data),\n          tnsr::I<DataVector, 3>{\n              {{{0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}}}});\n      check_scalar_data(interpolated_data);\n    }\n    {\n      INFO(\"Single point\");\n      std::vector<double> interpolated_data{};\n      interpolator.interpolate_to_point(make_not_null(&interpolated_data),\n                                        tnsr::I<double, 3>{{0.0, 0.0, 0.0}});\n      CHECK(interpolated_data[0] == approx(-0.07059806932542323));\n      CHECK(interpolated_data[2] == approx(1.0569673471948728));\n    }\n  }\n  {\n    INFO(\"Single-precision volume data\");\n    const domain::creators::Rectangle domain_creator{\n        {{-1., -1.}}, {{1., 1.}}, {{0, 0}}, {{4, 4}}, {{false, false}}};\n    const auto domain = domain_creator.create_domain();\n    const ElementId<2> element_id{0};\n    const Mesh<2> mesh{4, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    // Manufacture linear data so interpolation is exact\n    const auto xi = logical_coordinates(mesh);\n    const DataVector psi = 1.0 + get<0>(xi) + 2. * get<1>(xi);\n    // Convert to single precision\n    std::vector<float> psi_float(psi.size());\n    for (size_t i = 0; i < psi.size(); ++i) {\n      psi_float[i] = static_cast<float>(psi[i]);\n    }\n    // Write to file\n    const std::string h5_file_name{\"Unit.IO.Exporter.Float.h5\"};\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n    {  // scope to open and close file\n      h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n      auto& volfile = h5_file.insert<h5::VolumeData>(\"/VolumeData\", 0);\n      volfile.write_volume_data(\n          123, 0.,\n          {ElementVolumeData{element_id,\n                             {TensorComponent{\"Psi\", std::move(psi_float)}},\n                             mesh}},\n          serialize(domain));\n    }\n    const auto interpolated_data =\n        interpolate_to_points<2>(h5_file_name, \"/VolumeData\",\n                                 ObservationId{123}, {\"Psi\"}, {{{0.}, {0.}}});\n    const spectre::Exporter::PointwiseInterpolator<2> interpolator{\n        h5_file_name, \"/VolumeData\", ObservationId{123}, {\"Psi\"}};\n    std::vector<double> interpolated_data2{};\n    interpolator.interpolate_to_point(make_not_null(&interpolated_data2),\n                                      tnsr::I<double, 2>{{0., 0.}});\n    // Compare to single precision\n    Approx custom_approx =\n        Approx::custom()\n            .epsilon(10. * std::numeric_limits<float>::epsilon())\n            .scale(1.0);\n    CHECK(interpolated_data[0][0] == custom_approx(1.));\n    CHECK(interpolated_data2[0] == custom_approx(1.));\n    // Delete the test file\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n  }\n  {\n    INFO(\"Extrapolation into BBH excisions\");\n    using Object = domain::creators::BinaryCompactObject<false>::Object;\n    const domain::creators::BinaryCompactObject domain_creator{\n        Object{1., 4., 8., true, true},\n        Object{0.8, 2.5, -6., true, true},\n        std::array<double, 2>{{0., 0.}},\n        60.,\n        300.,\n        1.0,\n        0_st,\n        6_st,\n        true,\n        domain::CoordinateMaps::Distribution::Projective,\n        std::vector<double>{},\n        domain::CoordinateMaps::Distribution::Inverse,\n        120.};\n    const auto domain = domain_creator.create_domain();\n    const auto functions_of_time = domain_creator.functions_of_time();\n    const double time = 1.0;\n    const auto element_ids =\n        initial_element_ids(domain_creator.initial_refinement_levels());\n    const Mesh<3> mesh{18, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const auto xi = logical_coordinates(mesh);\n    std::vector<ElementVolumeData> element_volume_data{};\n    const auto func = [](const auto& x) {\n      auto x1 = x;\n      get<0>(x1) -= 8;\n      auto x2 = x;\n      get<0>(x2) += 6;\n      const auto r1 = get(magnitude(x1));\n      const auto r2 = get(magnitude(x2));\n      return blaze::evaluate(exp(-square(r1)) + exp(-square(r2)));\n    };\n    for (const auto& element_id : element_ids) {\n      ElementMap<3, Frame::Inertial> element_map{\n          element_id, domain.blocks()[element_id.block_id()]};\n      const auto x = element_map(xi, time, functions_of_time);\n      DataVector psi = func(x);\n      element_volume_data.push_back(ElementVolumeData{\n          element_id, {TensorComponent{\"Psi\", std::move(psi)}}, mesh});\n    }\n    // Write to file\n    const std::string h5_file_name{\"Unit.IO.Exporter.Excision.h5\"};\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n    {  // scope to open and close file\n      h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n      auto& volfile = h5_file.insert<h5::VolumeData>(\"/VolumeData\", 0);\n      volfile.write_volume_data(123, time, element_volume_data,\n                                serialize(domain),\n                                serialize(functions_of_time));\n    }\n    // Interpolate\n    tnsr::I<DataVector, 3> target_points{{{{8.5, 8.6, 8.7, 8.9, 9., 10.},\n                                           {0., 0., 0., 0., 0., 0.},\n                                           {0., 0., 0., 0., 0., 0.}}}};\n    const size_t num_target_points = get<0>(target_points).size();\n    const auto interpolated_data = interpolate_to_points<3>(\n        h5_file_name, \"/VolumeData\", ObservationId{123}, {\"Psi\"}, target_points,\n        true);\n    CHECK(interpolated_data.size() == 1);\n    CHECK(interpolated_data[0].size() == num_target_points);\n    // Check result\n    DataVector psi_interpolated(num_target_points);\n    DataVector psi_expected(num_target_points);\n    for (size_t i = 0; i < num_target_points; ++i) {\n      psi_interpolated[i] = interpolated_data[0][i];\n      tnsr::I<double, 3> x_target{\n          {{get<0>(target_points)[i], get<1>(target_points)[i],\n            get<2>(target_points)[i]}}};\n      psi_expected[i] = func(x_target);\n    }\n    // These points are extrapolated and therefore less precise\n    Approx approx_extrapolated = Approx::custom().epsilon(1.e-1).scale(1.0);\n    for (size_t i = 0; i < 4; ++i) {\n      CHECK(psi_interpolated[i] == approx_extrapolated(psi_expected[i]));\n    }\n    // This point is exact\n    CHECK(psi_interpolated[4] == approx(psi_expected[4]));\n    // This point is interpolated\n    Approx approx_interpolated = Approx::custom().epsilon(1.e-6).scale(1.0);\n    CHECK(psi_interpolated[5] == approx_interpolated(psi_expected[5]));\n\n    // Do some benchmarks\n    // - Results ran on Apple M2 Pro chip (Nils Vu, May 2025)\n    // - First number is without h-refinement (as set in the domain creator\n    //   above so these tests run quickly). Second number is when setting the\n    //   h-refinement to 2.\n    // 9.5 ms / 576 ms\n    BENCHMARK(\"interpolate_to_points\") {\n      return interpolate_to_points<3>(h5_file_name, \"/VolumeData\",\n                                      ObservationId{123}, {\"Psi\"},\n                                      target_points, true);\n    };\n    {\n      PointwiseInterpolator<3, Frame::Inertial> interpolator{\n          h5_file_name, \"/VolumeData\", ObservationId{123}, {\"Psi\"}};\n      // 3.1 ms / 3.4 ms\n      BENCHMARK(\"PointwiseInterpolator::interpolate_to_points\") {\n        std::vector<DataVector> result{};\n        interpolator.interpolate_to_points(make_not_null(&result),\n                                           target_points, true);\n        return result;\n      };\n      // 147 us / 149 us\n      BENCHMARK(\"PointwiseInterpolator::interpolate_to_point\") {\n        std::vector<double> result{};\n        interpolator.interpolate_to_point(make_not_null(&result),\n                                          tnsr::I<double, 3>{{10., 0., 0.}});\n        return result;\n      };\n      std::vector<size_t> block_order;\n      // 137 us / 143 us\n      BENCHMARK(\n          \"PointwiseInterpolator::interpolate_to_point with block order\") {\n        std::vector<double> result{};\n        interpolator.interpolate_to_point(make_not_null(&result),\n                                          tnsr::I<double, 3>{{10., 0., 0.}},\n                                          make_not_null(&block_order));\n        return result;\n      };\n    }\n\n    // Delete the test file\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n  }\n}\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "tests/Unit/IO/Exporter/Test_SpacetimeInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"IO/Exporter/Exporter.hpp\"\n#include \"IO/Exporter/SpacetimeInterpolator.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace spectre::Exporter {\n\nnamespace {\n\nvoid write_data_to_file(const std::string& h5_file_name) {\n  // Create a domain\n  const std::array<double, 3> velocity{{1.0, 0.0, 0.0}};\n  const domain::creators::time_dependence::UniformTranslation<3, 0>\n      time_dependence{0.0, velocity};\n  const domain::creators::Sphere domain_creator{\n      1.0,\n      3.0,\n      domain::creators::Sphere::Excision{},\n      1_st,\n      6_st,\n      true,\n      {},\n      {},\n      domain::CoordinateMaps::Distribution::Linear,\n      ShellWedges::All,\n      time_dependence.get_clone()};\n  const auto domain = domain_creator.create_domain();\n  const auto functions_of_time = domain_creator.functions_of_time();\n\n  // Generate some volume data in the grid frame\n  const auto element_ids =\n      initial_element_ids(domain_creator.initial_refinement_levels());\n  const Mesh<3> mesh{6, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto xi = logical_coordinates(mesh);\n  std::vector<ElementVolumeData> element_volume_data{};\n  const auto func = [](const auto& x) { return get(magnitude(x)); };\n  for (const auto& element_id : element_ids) {\n    const ElementMap<3, Frame::Grid> element_map{\n        element_id, domain.blocks()[element_id.block_id()]};\n    const auto x = element_map(xi);\n    DataVector psi = func(x);\n    element_volume_data.push_back(ElementVolumeData{\n        element_id, {TensorComponent{\"Psi\", std::move(psi)}}, mesh});\n  }\n\n  // Write grid-frame data to file at multiple times. This means the data is\n  // moving with the grid, but since the SpacetimeInterpolator works in the\n  // grid frame the time interpolation should be exact.\n  const size_t num_times = 10;\n  std::vector<double> times(num_times);\n  std::iota(times.begin(), times.end(), 0.0);\n  h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n  auto& volfile = h5_file.insert<h5::VolumeData>(\"/VolumeData\", 0);\n  size_t obs_id = 0;\n  for (const double time : times) {\n    volfile.write_volume_data(obs_id, time, element_volume_data,\n                              serialize(domain), serialize(functions_of_time),\n                              serialize(functions_of_time));\n    ++obs_id;\n  }\n}\n\nvoid test_time_interpolation(\n    const SpacetimeInterpolator<3, Frame::Inertial>& interpolator) {\n  auto [tmin, tmax] = interpolator.time_bounds();\n  CHECK(tmin < tmax);\n  std::vector<double> interpolated_data{};\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      interpolator.interpolate_to_point(make_not_null(&interpolated_data),\n                                        tnsr::I<double, 3>{{0.0, 0.0, 0.0}},\n                                        tmin - 0.1),\n      Catch::Matchers::ContainsSubstring(\"outside the time bounds.\"));\n#endif  // SPECTRE_DEBUG\n  for (const double time : {tmin, tmin + 0.1, tmin + 1.0,\n                            tmin + 0.5 * (tmax - tmin), tmax - 0.1, tmax}) {\n    CAPTURE(time);\n    interpolator.interpolate_to_point(\n        make_not_null(&interpolated_data),\n        tnsr::I<double, 3>{{time + 1.0, 0.0, 0.0}}, time);\n    CHECK(interpolated_data[0] == approx(1.0));\n  }\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      interpolator.interpolate_to_point(make_not_null(&interpolated_data),\n                                        tnsr::I<double, 3>{{0.0, 0.0, 0.0}},\n                                        tmax + 0.1),\n      Catch::Matchers::ContainsSubstring(\"outside the time bounds.\"));\n#endif  // SPECTRE_DEBUG\n}\n\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.IO.Exporter.SpacetimeInterpolator\", \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  // Write sample data to file\n  const std::string h5_file_name{\"Unit.IO.Exporter.SpacetimeInterpolator.h5\"};\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  write_data_to_file(h5_file_name);\n\n  {\n    SpacetimeInterpolator<3, Frame::Inertial> interpolator{\n        h5_file_name, \"VolumeData\", {\"Psi\"}};\n    CHECK(interpolator.max_time_bounds() == std::array<double, 2>{{1.0, 8.0}});\n    interpolator.load_time_bounds({{1.0, 4.0}});\n    CHECK(interpolator.num_loaded_slices() == 6);\n    CHECK(interpolator.time_bounds() == std::array<double, 2>{{1.0, 4.0}});\n    // Load some time bounds that are already loaded\n    interpolator.load_time_bounds({{1.5, 4.0}});\n    CHECK(interpolator.num_loaded_slices() == 6);\n    CHECK(interpolator.time_bounds() == std::array<double, 2>{{1.0, 4.0}});\n    interpolator.load_time_bounds({{2.5, 4.0}});\n    CHECK(interpolator.num_loaded_slices() == 5);\n    CHECK(interpolator.time_bounds() == std::array<double, 2>{{2.0, 4.0}});\n    // Load some time bounds that don't overlap with the current bounds\n    interpolator.load_time_bounds({{7.5, 8.0}});\n    CHECK(interpolator.num_loaded_slices() == 4);\n    CHECK(interpolator.time_bounds() == std::array<double, 2>{{7.0, 8.0}});\n    // Interpolate\n    interpolator.load_time_bounds({{1.0, 4.0}});\n    CHECK(interpolator.num_loaded_slices() == 6);\n    CHECK(interpolator.time_bounds() == std::array<double, 2>{{1.0, 4.0}});\n    test_time_interpolation(interpolator);\n    interpolator.load_time_bounds({{4.0, 7.0}});\n    CHECK(interpolator.time_bounds() == std::array<double, 2>{{4.0, 7.0}});\n    test_time_interpolation(interpolator);\n    {\n      INFO(\"Test move constructor\");\n      const SpacetimeInterpolator<3, Frame::Inertial> moved_interpolator{\n          std::move(interpolator)};\n      CHECK(moved_interpolator.max_time_bounds() ==\n            std::array<double, 2>{{1.0, 8.0}});\n      CHECK(moved_interpolator.num_loaded_slices() == 6);\n      CHECK(moved_interpolator.time_bounds() ==\n            std::array<double, 2>{{4.0, 7.0}});\n      test_time_interpolation(moved_interpolator);\n    }\n  }\n\n  // Delete the test file\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  {\n    INFO(\"Test with time-independent domain\");\n    const auto domain = domain::creators::Brick{\n        {{0.0, 0.0, 0.0}},\n        {{1.0, 1.0, 1.0}},\n        {{0, 0, 0}},\n        {{4, 4, 4}}}.create_domain();\n    const Mesh<3> mesh{4, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_name};\n    auto& volfile = h5_file.insert<h5::VolumeData>(\"/VolumeData\", 0);\n    for (size_t i = 0; i < 5; ++i) {\n      const double t = 1.0 * static_cast<double>(i);\n      ElementVolumeData element_volume_data{\n          ElementId<3>{0}, {TensorComponent{\"Psi\", DataVector(64, t)}}, mesh};\n      volfile.write_volume_data(i, t, {std::move(element_volume_data)},\n                                serialize(domain));\n    }\n\n    SpacetimeInterpolator<3> interpolator{h5_file_name, \"VolumeData\", {\"Psi\"}};\n    interpolator.load_time_bounds({{1.5, 3}});\n    std::vector<double> result{};\n    interpolator.interpolate_to_point(\n        make_not_null(&result), tnsr::I<double, 3, Frame::Inertial>{{0, 0, 0}},\n        2.5);\n    CHECK(result[0] == approx(2.5));\n\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n  }\n}\n\n}  // namespace spectre::Exporter\n"
  },
  {
    "path": "tests/Unit/IO/External/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ExternalIO\")\n\nset(LIBRARY_SOURCES \"\")\n\nif (TARGET FUKA::Exporter)\n  list(APPEND LIBRARY_SOURCES\n    Test_InterpolateFromFuka.cpp\n  )\nendif()\n\nif (NOT LIBRARY_SOURCES)\n  return()\nendif()\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ExternalIO\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/IO/External/Test_InterpolateFromFuka.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <cstdlib>\n#include <mutex>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"IO/External/InterpolateFromFuka.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.IO.External.InterpolateFromFuka\", \"[Unit][IO]\") {\n  std::mutex fuka_lock{};\n  // Get example data directory from environment variable. The example ID is\n  // in `FUKA_ROOT/codes/PythonTools/Example_id` unless installed elsewhere.\n  const char* example_id_dir_ptr = std::getenv(\"FUKA_EXAMPLE_ID_DIR\");\n  REQUIRE(example_id_dir_ptr != nullptr);\n  const std::string example_id_dir{example_id_dir_ptr};\n  REQUIRE_FALSE(example_id_dir.empty());\n  CAPTURE(example_id_dir);\n  {\n    INFO(\"BH\");\n    const tnsr::I<DataVector, 3> coords{{{{2.0}, {0.0}, {0.0}}}};\n    const auto fuka_data = io::interpolate_from_fuka<io::FukaIdType::Bh>(\n        make_not_null(&fuka_lock),\n        example_id_dir + \"/converged_BH_TOTAL_BC.0.5.0.0.09.info\", coords);\n    CHECK_ITERABLE_APPROX(get(get<gr::Tags::Lapse<DataVector>>(fuka_data)),\n                          DataVector{0.78166130712794868});\n  }\n  {\n    INFO(\"BBH\");\n    const tnsr::I<DataVector, 3> coords{{{{0.0}, {0.0}, {0.0}}}};\n    const auto fuka_data = io::interpolate_from_fuka<io::FukaIdType::Bbh>(\n        make_not_null(&fuka_lock),\n        example_id_dir + \"/converged_BBH_TOTAL_BC.10.0.0.1.q1.0.0.09.info\",\n        coords);\n    CHECK_ITERABLE_APPROX(get(get<gr::Tags::Lapse<DataVector>>(fuka_data)),\n                          DataVector{0.82006289882662431});\n  }\n  {\n    INFO(\"NS\");\n    const tnsr::I<DataVector, 3> coords{{{{0.0}, {0.0}, {0.0}}}};\n    const auto fuka_data = io::interpolate_from_fuka<io::FukaIdType::Ns>(\n        make_not_null(&fuka_lock),\n        example_id_dir + \"/converged_NS_TOTAL_BC.togashi.2.23.-0.4.0.11.info\",\n        coords);\n    CHECK_ITERABLE_APPROX(\n        get(get<hydro::Tags::RestMassDensity<DataVector>>(fuka_data)),\n        DataVector{0.00404310450359371});\n  }\n  {\n    INFO(\"BNS\");\n    const tnsr::I<DataVector, 3> coords{{{{15.3}, {0.0}, {0.0}}}};\n    const auto fuka_data = io::interpolate_from_fuka<io::FukaIdType::Bns>(\n        make_not_null(&fuka_lock),\n        example_id_dir +\n            \"/converged_BNS_TOTAL.togashi.30.6.0.0.2.8.q1.0.0.09.info\",\n        coords);\n    CHECK_ITERABLE_APPROX(\n        get(get<hydro::Tags::RestMassDensity<DataVector>>(fuka_data)),\n        DataVector{0.00137492312500218});\n  }\n  {\n    INFO(\"BHNS\");\n    const tnsr::I<DataVector, 3> coords{{{{0.0}, {0.0}, {0.0}}}};\n    const auto fuka_data = io::interpolate_from_fuka<io::FukaIdType::Bhns>(\n        make_not_null(&fuka_lock),\n        example_id_dir +\n            \"/converged_BHNS_ECC_RED.togashi.35.0.6.0.52.3.6.q0.487603.0.1.11.\"\n            \"info\",\n        coords);\n    CHECK_ITERABLE_APPROX(get(get<gr::Tags::Lapse<DataVector>>(fuka_data)),\n                          DataVector{0.77494679614415585});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_H5\")\n\nset(LIBRARY_SOURCES\n  Test_Cce.cpp\n  Test_CheckH5PropertiesMatch.cpp\n  Test_CombineH5.cpp\n  Test_Dat.cpp\n  Test_EosTable.cpp\n  Test_H5.cpp\n  Test_H5File.cpp\n  Test_OpenGroup.cpp\n  Test_StellarCollapseEos.cpp\n  Test_TensorData.cpp\n  Test_Version.cpp\n  Test_VolumeData.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  Domain\n  DomainBoundaryConditionsHelpers\n  DomainCreators\n  H5\n  Informer\n  IO\n  IoTestHelpers\n  Parallel\n  Spectral\n  Utilities\n  )\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.CombineH5.Python\"\n  \"Test_CombineH5.py\"\n  \"unit;IO;H5;Python\"\n  PyH5\n  TIMEOUT 10\n  )\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python\"\n  \"Test_H5.py\"\n  \"unit;IO;H5;python\"\n  PyH5\n  )\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.VolumeData.Python\"\n  \"Test_VolumeData.py\"\n  \"unit;IO;H5;Python\"\n  PyH5\n  )\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.TensorData\"\n  \"Test_TensorData.py\"\n  \"unit;IO;H5;Python\"\n  PyH5\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# The HDF5_REPACK_EXECUTABLE is inserted in DeleteSubfiles.py by cmake, so this\n# test doesn't work in PY_DEV_MODE\nif (NOT PY_DEV_MODE)\n  spectre_add_python_bindings_test(\n    \"Unit.IO.H5.Python.DeleteSubfiles\"\n    Test_DeleteSubfiles.py\n    \"unit;IO;H5;python\"\n    PyH5)\nendif()\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.CombineH5Dat\"\n  Test_CombineH5Dat.py\n  \"unit;IO;H5;python\"\n  PyH5)\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.ExtendConnectivity\"\n  Test_ExtendConnectivity.py\n  \"unit;IO;H5;python\"\n  PyH5)\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.ExtractDatFromH5\"\n  Test_ExtractDatFromH5.py\n  \"unit;IO;H5;python\"\n  PyH5\n  )\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.ExtractInputSourceYAMLFromH5\"\n  Test_ExtractInputSourceYamlFromH5.py\n  \"unit;IO;H5;python\"\n  PyH5)\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.FunctionsOfTimeFromVolume\"\n  Test_FunctionsOfTimeFromVolume.py\n  \"unit;IO;H5;python\"\n  PyH5)\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.InterpolateToMesh\"\n  Test_InterpolateToMesh.py\n  \"unit;IO;H5;python\"\n  PyH5)\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.IterElements\"\n  Test_IterElements.py\n  \"unit;IO;H5;python\"\n  PyH5)\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.ReadH5\"\n  Test_ReadH5.py\n  \"unit;IO;H5;python\"\n  PyH5\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.IO.H5.Python.TransformVolumeData\"\n  Test_TransformVolumeData.py\n  \"unit;IO;H5;python\"\n  PyH5\n  TIMEOUT 10)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_CombineH5Dat.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport h5py\nimport numpy as np\nimport numpy.testing as npt\nfrom click.testing import CliRunner\n\nimport spectre.Informer as spectre_informer\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5.CombineH5Dat import combine_h5_dat, combine_h5_dat_command\n\n\nclass TestCombineH5Dat(unittest.TestCase):\n    def setUp(self):\n        unit_test_build_dir = spectre_informer.unit_test_build_path()\n        self.test_dir = os.path.join(unit_test_build_dir, \"IO/H5/Python\")\n        os.makedirs(self.test_dir, exist_ok=True)\n\n        # Set up directories to hold the input files and the joined output file\n        self.input_dir = os.path.join(self.test_dir, \"TestCombineH5DatInput\")\n        self.output_dir = os.path.join(self.test_dir, \"TestCombineH5DatOutput\")\n        if os.path.exists(self.input_dir):\n            shutil.rmtree(self.input_dir)\n        if os.path.exists(self.output_dir):\n            shutil.rmtree(self.output_dir)\n        os.makedirs(self.input_dir, exist_ok=True)\n        os.makedirs(self.output_dir, exist_ok=True)\n\n        self.input_file_paths = [\n            os.path.join(self.input_dir, \"TestDatSeg\" + str(i) + \".h5\")\n            for i in range(1, 4, 1)\n        ]\n        self.output_file_path = os.path.join(\n            self.output_dir, \"TestDatSegJoined.h5\"\n        )\n        self.output_file_path_cli = os.path.join(\n            self.output_dir, \"TestDatSegJoinedCLI.h5\"\n        )\n        self.expected_file_path = os.path.join(\n            self.output_dir, \"TestDatSegExpected.h5\"\n        )\n\n        # Generate sample dat data for H5 files to be joined\n        self.wave_1 = np.array(\n            [[t, np.sin(t), np.cos(t)] for t in np.arange(0, 10.0, 0.1)]\n        )\n        self.wave_2 = np.array(\n            [[t, np.sin(t), np.cos(t)] for t in np.arange(10.0, 20.0, 0.1)]\n        )\n        self.wave_3 = np.array(\n            [[t, np.sin(t), np.cos(t)] for t in np.arange(20.0, 30.0, 0.1)]\n        )\n        self.wave_joined = np.concatenate(\n            (self.wave_1, self.wave_2, self.wave_3), axis=0\n        )\n        self.pow_1 = np.array(\n            [[t, t**2, t**3, t**4] for t in np.arange(0, 10.0, 0.1)]\n        )\n        self.pow_2 = np.array(\n            [[t, t**2, t**3, t**4] for t in np.arange(10.0, 20.0, 0.1)]\n        )\n        self.pow_3 = np.array(\n            [[t, t**2, t**3, t**4] for t in np.arange(20.0, 30.0, 0.1)]\n        )\n        self.pow_joined = np.concatenate(\n            (self.pow_1, self.pow_2, self.pow_3), axis=0\n        )\n\n        # Generate 3 H5 files with two dat files inside each\n        with spectre_h5.H5File(\n            file_name=self.input_file_paths[0], mode=\"r+\"\n        ) as h5file:\n            wave_datfile = h5file.insert_dat(\n                path=\"/Waves\", legend=[\"Time\", \"Sin(t)\", \"Cos(t)\"], version=0\n            )\n            wave_datfile.append(self.wave_1)\n        with spectre_h5.H5File(\n            file_name=self.input_file_paths[0], mode=\"r+\"\n        ) as h5file:\n            pow_datfile = h5file.insert_dat(\n                path=\"/Powers/Pow\",\n                legend=[\"Time\", \"t*t\", \"t*t*t\", \"t*t*t*t\"],\n                version=0,\n            )\n            pow_datfile.append(self.pow_1)\n        with spectre_h5.H5File(\n            file_name=self.input_file_paths[1], mode=\"r+\"\n        ) as h5file:\n            wave_datfile = h5file.insert_dat(\n                path=\"/Waves\", legend=[\"Time\", \"Sin(t)\", \"Cos(t)\"], version=0\n            )\n            wave_datfile.append(self.wave_2)\n        with spectre_h5.H5File(\n            file_name=self.input_file_paths[1], mode=\"r+\"\n        ) as h5file:\n            pow_datfile = h5file.insert_dat(\n                path=\"/Powers/Pow\",\n                legend=[\"Time\", \"t*t\", \"t*t*t\", \"t*t*t*t\"],\n                version=0,\n            )\n            pow_datfile.append(self.pow_2)\n        with spectre_h5.H5File(\n            file_name=self.input_file_paths[2], mode=\"r+\"\n        ) as h5file:\n            wave_datfile = h5file.insert_dat(\n                path=\"/Waves\", legend=[\"Time\", \"Sin(t)\", \"Cos(t)\"], version=0\n            )\n            wave_datfile.append(self.wave_3)\n        with spectre_h5.H5File(\n            file_name=self.input_file_paths[2], mode=\"r+\"\n        ) as h5file:\n            pow_datfile = h5file.insert_dat(\n                path=\"/Powers/Pow\",\n                legend=[\"Time\", \"t*t\", \"t*t*t\", \"t*t*t*t\"],\n                version=0,\n            )\n            pow_datfile.append(self.pow_3)\n\n        self.test_yaml = \"\"\"\n        # Distributed under the MIT License.\n        # See LICENSE.txt for details.\n        Amplitude: 1.0 # Code units\n        Frequency: 1.0 # Hz\n        Phase: 0.0     # Rad\n        Powers: [1, 2, 3, 4]\n        \"\"\"\n        with h5py.File(self.input_file_paths[0], \"r+\") as h5file:\n            h5file.attrs.modify(\"InputSource.yaml\", self.test_yaml)\n        with h5py.File(self.input_file_paths[1], \"r+\") as h5file:\n            h5file.attrs.modify(\"InputSource.yaml\", self.test_yaml)\n        with h5py.File(self.input_file_paths[2], \"r+\") as h5file:\n            h5file.attrs.modify(\"InputSource.yaml\", self.test_yaml)\n\n    def tearDown(self):\n        if os.path.exists(self.input_dir):\n            shutil.rmtree(self.input_dir)\n        if os.path.exists(self.output_dir):\n            shutil.rmtree(self.output_dir)\n\n    def test_combine_h5_dat(self):\n        combine_h5_dat(\n            output=self.output_file_path,\n            h5files=self.input_file_paths,\n            force=None,\n        )\n\n        with h5py.File(self.output_file_path) as h5file:\n            npt.assert_allclose(h5file[\"Waves.dat\"], self.wave_joined)\n            npt.assert_allclose(h5file[\"Powers/Pow.dat\"], self.pow_joined)\n            self.assertEqual(h5file.attrs[\"InputSource.yaml\"], self.test_yaml)\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            combine_h5_dat_command,\n            [\"-o\", self.output_file_path_cli, *self.input_file_paths],\n            catch_exceptions=False,\n        )\n        with self.assertRaisesRegex(\n            ValueError, \"exists; to overwrite, use --force\"\n        ):\n            runner.invoke(\n                combine_h5_dat_command,\n                [\"-o\", self.output_file_path_cli, *self.input_file_paths],\n                catch_exceptions=False,\n            )\n        result_force = runner.invoke(\n            combine_h5_dat_command,\n            [\n                \"-o\",\n                self.output_file_path_cli,\n                \"--force\",\n                *self.input_file_paths,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_DeleteSubfiles.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport h5py\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.IO.H5.DeleteSubfiles import delete_subfiles_command\n\n\nclass TestDeleteSubfiles(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"IO/H5/Python/DeleteSubfile\"\n        )\n        self.h5_filename = os.path.join(self.test_dir, \"Test.h5\")\n        os.makedirs(self.test_dir, exist_ok=True)\n        shutil.copyfile(\n            os.path.join(\n                unit_test_src_path(), \"Visualization/Python\", \"DatTestData.h5\"\n            ),\n            self.h5_filename,\n        )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_delete_subfiles(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            delete_subfiles_command,\n            [self.h5_filename, \"-d\", \"TimeSteps2.dat\"],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(result.output, \"\")\n        with h5py.File(self.h5_filename, \"r\") as open_h5_file:\n            self.assertNotIn(\"TimeSteps2.dat\", open_h5_file.keys())\n\n    def test_delete_subfiles_and_repack(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            delete_subfiles_command,\n            [self.h5_filename, \"-d\", \"TimeSteps2.dat\", \"--repack\"],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n        self.assertEqual(result.output, \"\")\n        with h5py.File(self.h5_filename, \"r\") as open_h5_file:\n            self.assertNotIn(\"TimeSteps2.dat\", open_h5_file.keys())\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_ExtendConnectivity.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.IO.H5.ExtendConnectivityData import (\n    extend_connectivity_data_command,\n)\n\n\nclass TestExtendConnectivity(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"IO/H5/Python/ExtendConnectivityData\"\n        )\n\n        self.filename = os.path.join(self.test_dir, \"TestVolume.h5\")\n        os.makedirs(self.test_dir, exist_ok=True)\n        shutil.copyfile(\n            os.path.join(\n                unit_test_src_path(), \"Visualization/Python\", \"VolTestData0.h5\"\n            ),\n            self.filename,\n        )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    # Helper function to obtain the length of the connectivity in\n    # the volume data file.\n    def get_connectivity_length(self):\n        with spectre_h5.H5File(self.filename, \"r\") as open_h5_file:\n            volfile = open_h5_file.get_vol(\"/element_data\")\n            obs_id = volfile.list_observation_ids()[0]\n            h5_connectivity = volfile.get_tensor_component(\n                obs_id, \"connectivity\"\n            ).data\n        return len(h5_connectivity)\n\n    def test_cli(self):\n        runner = CliRunner()\n        initial_connectivity = self.get_connectivity_length()\n        result = runner.invoke(\n            extend_connectivity_data_command,\n            [self.filename, \"-d\", \"element_data.vol\"],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        final_connectivity = self.get_connectivity_length()\n\n        assert initial_connectivity <= final_connectivity\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_ExtractDatFromH5.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom click.testing import CliRunner\n\nimport spectre.Informer as spectre_informer\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5.ExtractDatFromH5 import (\n    extract_dat_command,\n    extract_dat_files,\n)\n\n\nclass TestExtractDatFromH5(unittest.TestCase):\n    def setUp(self):\n        self.unit_test_src_dir = spectre_informer.unit_test_src_path()\n        self.unit_test_build_dir = spectre_informer.unit_test_build_path()\n        self.h5_filename = os.path.join(\n            self.unit_test_src_dir, \"Visualization/Python\", \"DatTestData.h5\"\n        )\n        self.test_dir = os.path.join(self.unit_test_build_dir, \"IO/H5/Python\")\n        self.created_dir = os.path.join(self.test_dir, \"extracted_DatTestData\")\n\n        os.makedirs(self.test_dir, exist_ok=True)\n        if os.path.exists(self.created_dir):\n            shutil.rmtree(self.created_dir)\n\n    def tearDown(self):\n        if os.path.exists(self.created_dir):\n            shutil.rmtree(self.created_dir)\n\n    def test_extract_dat_files(self):\n        with spectre_h5.H5File(self.h5_filename, \"r\") as h5file:\n            expected_memory_data = np.array(\n                h5file.get_dat(\"/Group0/MemoryData\").get_data()\n            )\n            h5file.close_current_object()\n            expected_timestep_data = np.array(\n                h5file.get_dat(\"/TimeSteps2\").get_data()\n            )\n\n        # All defaults, data should be same as expected\n        extract_dat_files(\n            self.h5_filename,\n            out_dir=self.created_dir,\n            num_cores=1,\n            precision=16,\n        )\n\n        self.assertTrue(os.path.exists(self.created_dir))\n\n        memory_data_path = os.path.join(\n            self.created_dir, \"Group0\", \"MemoryData.dat\"\n        )\n        timestep_data_path = os.path.join(self.created_dir, \"TimeSteps2.dat\")\n\n        memory_data = np.loadtxt(memory_data_path)\n        timestep_data = np.loadtxt(timestep_data_path)\n\n        npt.assert_almost_equal(memory_data, expected_memory_data)\n        npt.assert_almost_equal(timestep_data, expected_timestep_data)\n\n        # Test that we get an error if we try to run again and 'force' flag is\n        # False.\n        self.assertRaises(\n            ValueError,\n            extract_dat_files,\n            self.h5_filename,\n            out_dir=self.created_dir,\n            num_cores=1,\n            precision=16,\n        )\n\n        # Try with 'force' flag True this time\n        extract_dat_files(\n            self.h5_filename,\n            out_dir=self.created_dir,\n            num_cores=1,\n            precision=16,\n            force=True,\n        )\n\n        memory_data = np.loadtxt(memory_data_path)\n        timestep_data = np.loadtxt(timestep_data_path)\n\n        npt.assert_almost_equal(memory_data, expected_memory_data)\n        npt.assert_almost_equal(timestep_data, expected_timestep_data)\n\n        if os.path.exists(self.created_dir):\n            shutil.rmtree(self.created_dir)\n\n        # Parallelize. Use 2 cores (this is all we get on CI)\n        extract_dat_files(\n            self.h5_filename,\n            out_dir=self.created_dir,\n            num_cores=2,\n            precision=16,\n        )\n\n        memory_data = np.loadtxt(memory_data_path)\n        timestep_data = np.loadtxt(timestep_data_path)\n\n        npt.assert_almost_equal(memory_data, expected_memory_data)\n        npt.assert_almost_equal(timestep_data, expected_timestep_data)\n\n        if os.path.exists(self.created_dir):\n            shutil.rmtree(self.created_dir)\n\n        # Extract single file to stdout\n        extract_dat_files(\n            self.h5_filename,\n            out_dir=None,\n            num_cores=1,\n            precision=16,\n            list=False,\n            force=False,\n            subfiles=[\"TimeSteps2.dat\"],\n        )\n\n        # We shouldn't have created an out directory\n        self.assertFalse(os.path.exists(self.created_dir))\n\n        self.assertRaises(\n            ValueError,\n            extract_dat_files,\n            self.h5_filename,\n            out_dir=None,\n            num_cores=1,\n            precision=16,\n            list=False,\n            force=False,\n            subfiles=[\"TimeSteps2.dat\", \"Group0/MemoryData.dat\"],\n        )\n\n        # We don't test the '--list' flag as this is effectively just\n        # available_subfiles()\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            extract_dat_command,\n            [self.h5_filename, self.created_dir],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_ExtractInputSourceYamlFromH5.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nfrom click.testing import CliRunner\n\nimport spectre.Informer as spectre_informer\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5.ExtractInputSourceYamlFromH5 import (\n    extract_input_source_from_h5_command,\n)\n\n\nclass TestExtractInputSourceYAMLFromH5(unittest.TestCase):\n    def test_cli(self):\n        h5_path = os.path.join(\n            spectre_informer.unit_test_src_path(),\n            \"Visualization/Python\",\n            \"SurfaceTestData.h5\",\n        )\n        output_path = os.path.join(\n            spectre_informer.unit_test_build_path(),\n            \"IO/H5\",\n            \"ExtractedInput.yaml\",\n        )\n\n        runner = CliRunner()\n        runner.invoke(\n            extract_input_source_from_h5_command,\n            [h5_path, output_path],\n            catch_exceptions=False,\n        )\n\n        with open(output_path, \"r\") as open_file:\n            extracted_input_source = open_file.read()\n        with spectre_h5.H5File(h5_path, \"r\") as open_file:\n            expected_input_source = open_file.input_source()\n        self.assertEqual(extracted_input_source, expected_input_source)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_FunctionsOfTimeFromVolume.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport math\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport numpy as np\nimport yaml\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre import Spectral\nfrom spectre.DataStructures import DataVector\nfrom spectre.Domain import (\n    PiecewisePolynomial3,\n    QuaternionFunctionOfTime,\n    serialize_functions_of_time,\n)\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\nfrom spectre.IO.H5.FunctionsOfTimeFromVolume import (\n    functions_of_time_from_volume,\n)\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestFunctionsOfTimeFromVolume(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(\n            unit_test_build_path(), \"IO/H5/Python/FunctionsOfTimeFromVolume\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.bin_dir = Path(unit_test_build_path(), \"../../bin\").resolve()\n\n        # Making volume data for functions of time to be extracted\n        rotation_fot = QuaternionFunctionOfTime(\n            0.0,\n            [DataVector(size=4, fill=1.0)],\n            4 * [DataVector(size=3, fill=0.0)],\n            math.inf,\n        )\n        expansion_fot = PiecewisePolynomial3(\n            0.0, 4 * [DataVector(size=1, fill=0.0)], math.inf\n        )\n        expansion_outer_fot = PiecewisePolynomial3(\n            0.0, 4 * [DataVector(size=1, fill=0.0)], math.inf\n        )\n        translation_fot = PiecewisePolynomial3(\n            0.0, 4 * [DataVector(size=3, fill=0.0)], math.inf\n        )\n        serialized_fots = serialize_functions_of_time(\n            {\n                \"Expansion\": expansion_fot,\n                \"ExpansionOuterBoundary\": expansion_outer_fot,\n                \"Rotation\": rotation_fot,\n                \"Translation\": translation_fot,\n            }\n        )\n        self.volume_data = self.test_dir / \"BbhVolume0.h5\"\n        obs_values = [0.0, 2.0, 4.0, 6.0, 8.0, 10.0]\n        with spectre_h5.H5File(self.volume_data, \"w\") as volume_file:\n            volfile = volume_file.insert_vol(\"ForContinuation\", version=0)\n            for x in range(0, 5):\n                volfile.write_volume_data(\n                    observation_id=x,\n                    observation_value=obs_values[x],\n                    elements=[\n                        ElementVolumeData(\n                            element_name=\"arthas\",\n                            components=[\n                                TensorComponent(\n                                    \"menethil\",\n                                    np.random.rand(3),\n                                ),\n                            ],\n                            extents=[3],\n                            basis=[Spectral.Basis.Legendre],\n                            quadrature=[Spectral.Quadrature.GaussLobatto],\n                        )\n                    ],\n                    serialized_observation_functions_of_time=serialized_fots,\n                )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_functions_of_time_from_volume(self):\n        expected_rot_fot = [\n            [0.5, 0.5, 0.5, 0.5],\n            [0.0, 0.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0, 0.0],\n        ]\n        expected_exp_fot = [0.0, 0.0, 0.0]\n        expected_exp_outer_bdry_fot = [0.0, 0.0, 0.0]\n        expected_translation_fot = [\n            [0.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0],\n            [0.0, 0.0, 0.0],\n        ]\n\n        fot_dict = functions_of_time_from_volume(\n            str(self.volume_data), \"ForContinuation\", 6.0\n        )\n\n        self.assertEqual(fot_dict[\"Expansion\"], expected_exp_fot)\n        self.assertEqual(\n            fot_dict[\"ExpansionOuterBoundary\"], expected_exp_outer_bdry_fot\n        )\n        self.assertEqual(fot_dict[\"Rotation\"], expected_rot_fot)\n        self.assertEqual(fot_dict[\"Translation\"], expected_translation_fot)\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_InterpolateToMesh.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre import Spectral\nfrom spectre.DataStructures import DataVector\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\nfrom spectre.IO.H5.InterpolateToMesh import (\n    interpolate_to_mesh,\n    interpolate_to_mesh_command,\n)\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestInterpolateToMesh(unittest.TestCase):\n    def setUp(self):\n        \"\"\"\n        Creates two files with 2 observations each with two elements each\n        with two tensor components each\n        \"\"\"\n\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization\", \"InterpolateToMesh\"\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n\n        self.file_name = os.path.join(self.test_dir, \"interpolation_1.h5\")\n        try:\n            os.remove(self.file_name)\n        except OSError:\n            pass\n\n        file1 = spectre_h5.H5File(self.file_name, \"a\")\n        self.volume_name = \"/VolumeData\"\n        vol_file = file1.insert_vol(self.volume_name, 0)\n\n        # interpolated solution\n        self.sol1 = np.array(\n            [\n                0.00363708,\n                0.00683545,\n                0.00720423,\n                0.00457117,\n                0.00143594,\n                0.00269867,\n                0.00284427,\n                0.00180472,\n                -0.00143594,\n                -0.00269867,\n                -0.00284427,\n                -0.00180472,\n                -0.00363708,\n                -0.00683545,\n                -0.00720423,\n                -0.00457117,\n                -0.00117537,\n                -0.00107168,\n                -0.000212089,\n                0.0010019,\n                -0.000464041,\n                -0.000423106,\n                -8.37339e-05,\n                0.000395554,\n                0.000464041,\n                0.000423106,\n                8.37339e-05,\n                -0.000395554,\n                0.00117537,\n                0.00107168,\n                0.000212089,\n                -0.0010019,\n                -0.00146994,\n                -0.00281943,\n                -0.00264259,\n                -0.00102202,\n                -0.00058034,\n                -0.00111313,\n                -0.00104331,\n                -0.000403497,\n                0.00058034,\n                0.00111313,\n                0.00104331,\n                0.000403497,\n                0.00146994,\n                0.00281943,\n                0.00264259,\n                0.00102202,\n                -0.000809575,\n                -0.00215823,\n                -0.00242105,\n                -0.00147528,\n                -0.000319624,\n                -0.000852079,\n                -0.000955844,\n                -0.00058245,\n                0.000319624,\n                0.000852079,\n                0.000955844,\n                0.00058245,\n                0.000809575,\n                0.00215823,\n                0.00242105,\n                0.00147528,\n            ]\n        )\n\n        self.sol2 = np.array(\n            [\n                0.0258164,\n                0.0113765,\n                0.00140198,\n                0.000552092,\n                0.0292701,\n                0.0168924,\n                0.00688523,\n                0.00392304,\n                0.0292701,\n                0.0168924,\n                0.00688523,\n                0.00392304,\n                0.0258164,\n                0.0113765,\n                0.00140198,\n                0.000552092,\n                -0.00127999,\n                0.00293799,\n                0.0044177,\n                0.00246795,\n                -0.00248599,\n                0.0020233,\n                0.0042722,\n                0.00321024,\n                -0.00248599,\n                0.0020233,\n                0.0042722,\n                0.00321024,\n                -0.00127999,\n                0.00293799,\n                0.0044177,\n                0.00246795,\n                -0.00682319,\n                -0.000959123,\n                0.00251636,\n                0.00197985,\n                -0.00819944,\n                -0.00322287,\n                0.000494581,\n                0.00121647,\n                -0.00819944,\n                -0.00322287,\n                0.000494581,\n                0.00121647,\n                -0.00682319,\n                -0.000959123,\n                0.00251636,\n                0.00197985,\n                -0.00535242,\n                -0.00189859,\n                0.000668578,\n                0.00114994,\n                -0.00605613,\n                -0.00360345,\n                -0.00119241,\n                5.07709e-05,\n                -0.00605613,\n                -0.00360345,\n                -0.00119241,\n                5.07709e-05,\n                -0.00535242,\n                -0.00189859,\n                0.000668578,\n                0.00114994,\n            ]\n        )\n\n        # some tensor data\n        tensor1 = np.array(\n            [\n                0.00486224,\n                0.0130364,\n                0.00474089,\n                0,\n                0,\n                0,\n                -0.00486224,\n                -0.0130364,\n                -0.00474089,\n                -0.000893302,\n                0.000415331,\n                0.00206496,\n                0,\n                0,\n                0,\n                0.000893302,\n                -0.000415331,\n                -0.00206496,\n                -0.000805675,\n                -0.00339055,\n                -0.000652415,\n                0,\n                0,\n                0,\n                0.000805675,\n                0.00339055,\n                0.000652415,\n                -0.000490711,\n                -0.00294194,\n                -0.00131963,\n                0,\n                0,\n                0,\n                0.000490711,\n                0.00294194,\n                0.00131963,\n            ]\n        )\n\n        tensor2 = np.array(\n            [\n                0.0444173,\n                0.00136075,\n                5.56948e-07,\n                0.051794,\n                0.0153307,\n                0.00474144,\n                0.0444173,\n                0.00136075,\n                5.56948e-07,\n                0.00163012,\n                0.00446936,\n                0.000948565,\n                0.000269645,\n                0.00490565,\n                0.00301353,\n                0.00163012,\n                0.00446936,\n                0.000948565,\n                -0.00783519,\n                0.00186237,\n                0.00148632,\n                -0.00905312,\n                -0.00176937,\n                0.000833901,\n                -0.00783519,\n                0.00186237,\n                0.00148632,\n                -0.00715078,\n                0.000296698,\n                0.00129651,\n                -0.00789121,\n                -0.00285195,\n                -2.31192e-05,\n                -0.00715078,\n                0.000296698,\n                0.00129651,\n            ]\n        )\n\n        # divide data at different locations to make sure it does not get mixed\n        tensors_element1 = np.array([tensor1, tensor2])\n        tensors_element2 = tensors_element1 / 2.0\n\n        tensors_observation1 = np.array([tensors_element1, tensors_element2])\n        tensors_observation2 = tensors_observation1 / 4.0\n\n        tensors_file1 = np.array([tensors_observation1, tensors_observation2])\n\n        self.element_names = [\"Element1\", \"Element2\"]\n        self.tensor_names = [\"tensor1\", \"tensor2\"]\n\n        self.observation_ids = [42, 1000]\n        self.observation_values = [1.0, 3.0]\n\n        for j, observation in enumerate(tensors_file1):\n            volume_data = []\n            for k, element in enumerate(observation):\n                components = []\n                for l, tensor in enumerate(element):\n                    components.append(\n                        TensorComponent(\n                            self.tensor_names[l], DataVector(tensor, copy=False)\n                        )\n                    )\n\n                # this matches the solution by sol\n                volume_data.append(\n                    ElementVolumeData(\n                        element_name=self.element_names[k],\n                        components=components,\n                        extents=[3, 3, 4],\n                        basis=3 * [Spectral.Basis.Legendre],\n                        quadrature=3 * [Spectral.Quadrature.GaussLobatto],\n                    )\n                )\n\n            vol_file.write_volume_data(\n                self.observation_ids[j], self.observation_values[j], volume_data\n            )\n        file1.close()\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_simple_interpolation(self):\n        mesh = Spectral.Mesh3D(\n            [4, 4, 4],\n            [Spectral.Basis.Legendre] * 3,\n            [Spectral.Quadrature.Gauss] * 3,\n        )\n\n        target_volume_name = \"/VolumeDataInterpolated\"\n        interpolate_to_mesh(\n            self.file_name,\n            mesh,\n            self.file_name,\n            self.volume_name,\n            target_volume_name,\n        )\n\n        file = spectre_h5.H5File(self.file_name, \"r\")\n\n        vol = file.get_vol(target_volume_name)\n\n        self.assertEqual(vol.get_dimension(), 3)\n        self.assertCountEqual(vol.list_observation_ids(), self.observation_ids)\n\n        for i, obs in enumerate(self.observation_ids):\n            self.assertEqual(vol.get_grid_names(obs), self.element_names)\n            self.assertEqual(\n                vol.get_observation_value(obs), self.observation_values[i]\n            )\n            self.assertEqual(vol.list_tensor_components(obs), self.tensor_names)\n            self.assertEqual(vol.get_extents(obs), [[4, 4, 4], [4, 4, 4]])\n            self.assertEqual(\n                vol.get_bases(obs), [[Spectral.Basis.Legendre] * 3] * 2\n            )\n            self.assertEqual(\n                vol.get_quadratures(obs), [[Spectral.Quadrature.Gauss] * 3] * 2\n            )\n\n        tensor1_obs_1 = np.asarray(vol.get_tensor_component(42, \"tensor1\").data)\n        tensor2_obs_1 = np.asarray(vol.get_tensor_component(42, \"tensor2\").data)\n\n        tensor1_obs_2 = np.asarray(\n            vol.get_tensor_component(1000, \"tensor1\").data\n        )\n        tensor2_obs_2 = np.asarray(\n            vol.get_tensor_component(1000, \"tensor2\").data\n        )\n\n        file.close()\n\n        self.assertTrue(np.allclose(tensor1_obs_1[:64], self.sol1, 1e-7, 1e-7))\n        self.assertTrue(\n            np.allclose(tensor1_obs_1[64:], self.sol1 / 2.0, 1e-7, 1e-7)\n        )\n        self.assertTrue(np.allclose(tensor2_obs_1[:64], self.sol2, 1e-7, 1e-7))\n        self.assertTrue(\n            np.allclose(tensor2_obs_1[64:], self.sol2 / 2.0, 1e-7, 1e-7)\n        )\n\n        self.assertTrue(\n            np.allclose(tensor1_obs_2[:64], self.sol1 / 4.0, 1e-7, 1e-7)\n        )\n        self.assertTrue(\n            np.allclose(tensor1_obs_2[64:], self.sol1 / 8.0, 1e-7, 1e-7)\n        )\n        self.assertTrue(\n            np.allclose(tensor2_obs_2[:64], self.sol2 / 4.0, 1e-7, 1e-7)\n        )\n        self.assertTrue(\n            np.allclose(tensor2_obs_2[64:], self.sol2 / 8.0, 1e-7, 1e-7)\n        )\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            interpolate_to_mesh_command,\n            [\n                \"--source-file-prefix\",\n                os.path.join(self.test_dir, \"interpolation_\"),\n                \"--source-subfile-name\",\n                self.volume_name,\n                \"--target-file-prefix\",\n                os.path.join(self.test_dir, \"interpolation_\"),\n                \"--target-subfile-name\",\n                # No leading slash but with .vol extension, to check that this\n                # is normalized correctly\n                \"VolumeDataInterpolated.vol\",\n                \"--target-extents\",\n                \"4,4,4\",\n                \"--target-basis\",\n                \"Legendre\",\n                \"--target-quadrature\",\n                \"Gauss\",\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_IterElements.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\nfrom dataclasses import FrozenInstanceError\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Domain import ElementId, deserialize_functions_of_time\nfrom spectre.Informer import unit_test_src_path\nfrom spectre.IO.H5.IterElements import (\n    include_element,\n    iter_elements,\n    stripped_element_name,\n)\nfrom spectre.Spectral import Basis, Mesh, Quadrature, logical_coordinates\n\n\nclass TestIterElements(unittest.TestCase):\n    def setUp(self):\n        self.volfile_name = os.path.join(\n            unit_test_src_path(), \"Visualization/Python/VolTestData0.h5\"\n        )\n        self.subfile_name = \"/element_data\"\n\n    def test_stripped_element_name(self):\n        self.assertEqual(\n            stripped_element_name(\"[B0,(L0I0,L1I0)]\"), \"B0,(L0I0,L1I0)\"\n        )\n        self.assertEqual(\n            stripped_element_name(\"B0,(L0I0,L1I0)\"), \"B0,(L0I0,L1I0)\"\n        )\n        self.assertEqual(stripped_element_name(ElementId[1](0)), \"B0,(L0I0)\")\n        self.assertEqual(\n            stripped_element_name(ElementId[2](\"[B34,(L0I0,L1I0)]\")),\n            \"B34,(L0I0,L1I0)\",\n        )\n        self.assertEqual(\n            stripped_element_name(ElementId[3](\"[B12,(L2I1,L1I0,L0I0)]\")),\n            \"B12,(L2I1,L1I0,L0I0)\",\n        )\n\n    def test_include_element(self):\n        self.assertTrue(include_element(\"[B0,(L0I0,L1I0)]\", None))\n        self.assertFalse(include_element(\"[B0,(L0I0,L1I0)]\", []))\n        self.assertTrue(include_element(\"[B0,(L0I0,L1I0)]\", [\"B*,(L0I0,L1I0)\"]))\n        self.assertTrue(include_element(\"[B0,(L0I0,L1I0)]\", [\"*\"]))\n        self.assertTrue(include_element(\"[B0,(L0I0,L1I0)]\", [\"B0,*\"]))\n        self.assertTrue(include_element(\"[B0,(L0I0,L1I0)]\", [\"B0,(L0I0,L1I0)\"]))\n        self.assertFalse(include_element(\"[B0,(L0I0,L1I0)]\", [\"B1,*\"]))\n        self.assertTrue(include_element(\"[B0,(L0I0,L1I0)]\", [\"B0,*\", \"B1,*\"]))\n        self.assertTrue(\n            include_element(ElementId[2](\"[B0,(L0I0,L1I0)]\"), [\"B0,*\", \"B1,*\"])\n        )\n\n    def test_iter_elements(self):\n        with spectre_h5.H5File(self.volfile_name, \"r\") as open_h5_file:\n            volfile = open_h5_file.get_vol(self.subfile_name)\n            all_obs_ids = volfile.list_observation_ids()\n            obs_id = all_obs_ids[0]\n            time = volfile.get_observation_value(obs_id)\n            functions_of_time = deserialize_functions_of_time(\n                volfile.get_functions_of_time(obs_id)\n            )\n\n            elements = list(iter_elements(volfile, obs_id))\n            self.assertEqual(len(elements), 2)\n            for element in elements:\n                self.assertEqual(element.dim, 3)\n                self.assertIn(\n                    element.id,\n                    [\n                        ElementId[3](\"[B0,(L1I0,L0I0,L0I0)]\"),\n                        ElementId[3](\"[B0,(L1I1,L0I0,L0I0)]\"),\n                    ],\n                )\n                self.assertEqual(\n                    element.mesh,\n                    Mesh[3](4, Basis.Legendre, Quadrature.GaussLobatto),\n                )\n\n            # Make sure we can't mutate properties, in particular the time\n            with self.assertRaises(FrozenInstanceError):\n                elements[0].time = 2.0\n\n            # Test fetching data, enumerating in a loop, determinism of\n            # iteration order, list of volfiles, all obs IDs\n            tensor_components = [\n                \"InertialCoordinates_x\",\n                \"InertialCoordinates_y\",\n                \"InertialCoordinates_z\",\n                \"Psi\",\n                \"Error(Psi)\",\n            ]\n            for i, (element, data) in enumerate(\n                iter_elements(\n                    [volfile],\n                    obs_ids=all_obs_ids,\n                    tensor_components=tensor_components,\n                )\n            ):\n                self.assertEqual(element.id, elements[i].id)\n                self.assertEqual(element.mesh, elements[i].mesh)\n                self.assertEqual(data.shape, (len(tensor_components), 4**3))\n                # Test coordinates (they're all the same because the domain\n                # has no time dependence)\n                npt.assert_allclose(element.grid_coordinates, data[:3])\n                npt.assert_allclose(element.distorted_coordinates, data[:3])\n                npt.assert_allclose(element.inertial_coordinates, data[:3])\n                # Test Jacobians. Domain is [0, 2 pi]^3 split in half along\n                # first dimension, so elements have size (pi, 2 pi, 2 pi).\n                # Logical size is 2, so Jacobian is diag(0.5, 1, 1) * pi.\n                npt.assert_allclose(element.jacobian.get(0, 0) / np.pi, 0.5)\n                npt.assert_allclose(element.jacobian.get(1, 1) / np.pi, 1.0)\n                npt.assert_allclose(element.jacobian.get(2, 2) / np.pi, 1.0)\n                npt.assert_allclose(element.inv_jacobian.get(0, 0) * np.pi, 2.0)\n                npt.assert_allclose(element.inv_jacobian.get(1, 1) * np.pi, 1.0)\n                npt.assert_allclose(element.inv_jacobian.get(2, 2) * np.pi, 1.0)\n                npt.assert_allclose(\n                    element.det_jacobian.get() / np.pi**3, 0.5\n                )\n                for j in range(3):\n                    for k in range(j):\n                        npt.assert_allclose(\n                            element.jacobian.get(j, k), 0.0, atol=1e-14\n                        )\n                        npt.assert_allclose(\n                            element.inv_jacobian.get(j, k), 0.0, atol=1e-14\n                        )\n\n            # Test filtering elements\n            self.assertEqual(\n                len(\n                    list(iter_elements(volfile, obs_id, element_patterns=None))\n                ),\n                2,\n            )\n            self.assertFalse(\n                list(iter_elements(volfile, obs_id, element_patterns=[\"B1,*\"]))\n            )\n            self.assertEqual(\n                len(\n                    list(\n                        iter_elements(\n                            volfile, obs_id, element_patterns=[\"B0,*\"]\n                        )\n                    )\n                ),\n                2,\n            )\n            self.assertEqual(\n                len(\n                    list(\n                        iter_elements(\n                            volfile, obs_id, element_patterns=[\"B0,(L1I1*)\"]\n                        )\n                    )\n                ),\n                1,\n            )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_ReadH5.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\nfrom pathlib import Path\n\nimport h5py\nimport numpy as np\nimport pandas.testing as pdt\n\nimport spectre.Informer as spectre_informer\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.IO.H5 import (\n    available_subfiles,\n    open_volfiles,\n    select_observation,\n    to_dataframe,\n)\n\n\nclass TestReadH5(unittest.TestCase):\n    def setUp(self):\n        self.data_dir = Path(\n            spectre_informer.unit_test_src_path(), \"Visualization/Python\"\n        )\n        self.vol_file = str(self.data_dir / \"VolTestData0.h5\")\n        self.dat_file = str(self.data_dir / \"DatTestData.h5\")\n\n    def test_available_subfiles(self):\n        # Test with open file\n        with h5py.File(self.data_dir / \"DatTestData.h5\", \"r\") as open_file:\n            self.assertEqual(\n                available_subfiles(open_file, extension=\".dat\"),\n                [\"Group0/MemoryData.dat\", \"TimeSteps2.dat\"],\n            )\n            self.assertEqual(\n                available_subfiles(open_file, extension=\".vol\"), []\n            )\n        # Test with path\n        self.assertEqual(\n            available_subfiles(\n                self.data_dir / \"VolTestData0.h5\", extension=\".dat\"\n            ),\n            [],\n        )\n        # Test with string\n        self.assertEqual(\n            available_subfiles(\n                os.path.join(self.data_dir, \"VolTestData0.h5\"), extension=\".vol\"\n            ),\n            [\"element_data.vol\"],\n        )\n        # Test with multiple files\n        self.assertEqual(\n            available_subfiles(\n                [\n                    self.data_dir / \"DatTestData.h5\",\n                    self.data_dir / \"VolTestData0.h5\",\n                ],\n                extension=\".vol\",\n            ),\n            [\"element_data.vol\"],\n        )\n\n    def test_to_dataframe(self):\n        # h5py subfile\n        with h5py.File(\n            os.path.join(self.data_dir, \"DatTestData.h5\"), \"r\"\n        ) as open_file:\n            df = to_dataframe(open_file[\"TimeSteps2.dat\"])\n            self.assertEqual(df.columns[0], \"Time\")\n\n            df_one_row = to_dataframe(\n                open_file[\"TimeSteps2.dat\"], slice=np.s_[1:]\n            )\n            num_rows, num_cols = df_one_row.shape\n            self.assertEqual(num_rows, 1)\n            self.assertEqual(num_cols, df.shape[1])\n        # SpECTRE subfile\n        with spectre_h5.H5File(\n            os.path.join(self.data_dir, \"DatTestData.h5\"), \"r\"\n        ) as open_file:\n            df2 = to_dataframe(open_file.get_dat(\"/TimeSteps2\"))\n            open_file.close_current_object()\n            pdt.assert_frame_equal(df2, df)\n            open_file.close_current_object()\n\n            df2_one_row = to_dataframe(\n                open_file.get_dat(\"/TimeSteps2\"), slice=np.s_[1:]\n            )\n            num_rows, num_cols = df2_one_row.shape\n            self.assertEqual(num_rows, 1)\n            self.assertEqual(num_cols, df2.shape[1])\n            pdt.assert_frame_equal(df2_one_row, df_one_row)\n\n    def test_select_observation(self):\n        with spectre_h5.H5File(\n            os.path.join(self.data_dir, \"VolTestData0.h5\"), \"r\"\n        ) as open_file:\n            open_volfile = open_file.get_vol(\"/element_data\")\n            obs_ids = open_volfile.list_observation_ids()\n            expected_obs_id = obs_ids[0]\n            expected_time = open_volfile.get_observation_value(expected_obs_id)\n            self.assertEqual(\n                select_observation(open_volfile, step=0),\n                (expected_obs_id, expected_time),\n            )\n            self.assertEqual(\n                select_observation(open_volfile, time=0.05),\n                (expected_obs_id, expected_time),\n            )\n\n    def test_open_volfiles_missing_subfile(self):\n        volfiles = list(\n            open_volfiles([self.vol_file, self.dat_file], \"/element_data\")\n        )\n        self.assertEqual(len(volfiles), 1)\n\n        with self.assertRaises(ValueError):\n            list(open_volfiles([self.dat_file], \"/element_data\"))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Python/Test_TransformVolumeData.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport inspect\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import InverseJacobian, Scalar, tnsr\nfrom spectre.Domain import ElementId\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.IO.H5.TransformVolumeData import (\n    Kernel,\n    parse_kernels,\n    parse_pybind11_signatures,\n    snake_case_to_camel_case,\n    transform_volume_data,\n    transform_volume_data_command,\n)\nfrom spectre.NumericalAlgorithms.LinearOperators import partial_derivative\nfrom spectre.PointwiseFunctions.Punctures import adm_mass_integrand\nfrom spectre.Spectral import Mesh\n\n\ndef adm_mass_integrand_signature(\n    field: Scalar[DataVector],\n    alpha: Scalar[DataVector],\n    beta: Scalar[DataVector],\n) -> Scalar[DataVector]:\n    pass\n\n\ndef psi_squared(psi: Scalar[DataVector]) -> Scalar[DataVector]:\n    return Scalar[DataVector](np.array(psi) ** 2)\n\n\ndef coordinate_radius(inertial_coordinates: tnsr.I[DataVector, 3]):\n    # Return a Numpy array\n    return np.linalg.norm(np.array(inertial_coordinates), axis=0)\n\n\ndef deriv_coords(\n    inertial_coords: tnsr.I[DataVector, 3],\n    mesh: Mesh[3],\n    inv_jacobian: InverseJacobian[DataVector, 3],\n) -> tnsr.iJ[DataVector, 3]:\n    # This should be delta_ij\n    return partial_derivative(inertial_coords, mesh, inv_jacobian)\n\n\ndef sinusoid(x: tnsr.I[DataVector, 3]) -> Scalar[DataVector]:\n    # The integral over [2 pi]^3 of this integrand is 4**3=64\n    return Scalar[DataVector](\n        np.expand_dims(np.prod(np.sin(0.5 * np.array(x)), axis=0), axis=0)\n    )\n\n\ndef square_component(component: DataVector):\n    # Return a DataVector\n    return component**2\n\n\ndef abs_and_max(component: DataVector):\n    # Return multiple datasets\n    return {\n        \"Abs\": component.abs(),\n        # Single number, should get expanded over the volume as constant scalar\n        \"Max\": component.max(),\n    }\n\n\ndef refinement_level(element_id: ElementId[3]):\n    return element_id.refinement_levels\n\n\nclass TestTransformVolumeData(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization/TransformVolumeData\"\n        )\n        self.h5_filename = os.path.join(self.test_dir, \"Test.h5\")\n        os.makedirs(self.test_dir, exist_ok=True)\n        shutil.copyfile(\n            os.path.join(\n                unit_test_src_path(), \"Visualization/Python/VolTestData0.h5\"\n            ),\n            self.h5_filename,\n        )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_parse_pybind11_signatures(self):\n        self.assertEqual(\n            list(parse_pybind11_signatures(adm_mass_integrand)),\n            [inspect.signature(adm_mass_integrand_signature)],\n        )\n\n    def test_snake_case_to_camel_case(self):\n        self.assertEqual(snake_case_to_camel_case(\"hello_world\"), \"HelloWorld\")\n\n    def test_transform_volume_data(self):\n        open_h5_files = [spectre_h5.H5File(self.h5_filename, \"a\")]\n        open_volfiles = [\n            h5file.get_vol(\"/element_data\") for h5file in open_h5_files\n        ]\n\n        kernels = [\n            Kernel(\n                psi_squared,\n                output_name=\"PsiSquared\",\n                map_input_names={\"psi\": \"Psi\"},\n            ),\n            Kernel(coordinate_radius, output_name=\"CoordinateRadius\"),\n            Kernel(\n                coordinate_radius,\n                elementwise=True,\n                output_name=\"CoordinateRadiusElementwise\",\n            ),\n            Kernel(deriv_coords, output_name=\"DerivCoords\"),\n            Kernel(\n                square_component,\n                output_name=\"SquareComponent\",\n                map_input_names={\"component\": \"InertialCoordinates_x\"},\n            ),\n            Kernel(\n                abs_and_max,\n                output_name=None,\n                map_input_names={\"component\": \"Psi\"},\n            ),\n            Kernel(refinement_level, output_name=\"RefinementLevel\"),\n        ]\n\n        transform_volume_data(volfiles=open_volfiles, kernels=kernels)\n        with self.assertRaisesRegex(RuntimeError, \"already exists\"):\n            transform_volume_data(volfiles=open_volfiles, kernels=kernels)\n        transform_volume_data(\n            volfiles=open_volfiles,\n            kernels=kernels,\n            force=True,\n            start_time=0,\n            stop_time=1,\n            stride=2,\n        )\n\n        obs_id = open_volfiles[0].list_observation_ids()[0]\n        result_psisq = (\n            open_volfiles[0].get_tensor_component(obs_id, \"PsiSquared\").data\n        )\n        result_radius = (\n            open_volfiles[0]\n            .get_tensor_component(obs_id, \"CoordinateRadius\")\n            .data\n        )\n        result_radius_elementwise = (\n            open_volfiles[0]\n            .get_tensor_component(obs_id, \"CoordinateRadiusElementwise\")\n            .data\n        )\n        psi = open_volfiles[0].get_tensor_component(obs_id, \"Psi\").data\n        x, y, z = [\n            np.array(\n                open_volfiles[0]\n                .get_tensor_component(obs_id, \"InertialCoordinates\" + xyz)\n                .data\n            )\n            for xyz in [\"_x\", \"_y\", \"_z\"]\n        ]\n        radius = np.sqrt(x**2 + y**2 + z**2)\n        npt.assert_allclose(np.array(result_psisq), np.array(psi) ** 2)\n        npt.assert_allclose(np.array(result_radius), radius)\n        npt.assert_allclose(np.array(result_radius_elementwise), radius)\n        for i in range(3):\n            result_deriv_coords = (\n                open_volfiles[0]\n                .get_tensor_component(\n                    obs_id, \"DerivCoords_\" + \"xyz\"[i] + \"xyz\"[i]\n                )\n                .data\n            )\n            npt.assert_allclose(result_deriv_coords, 1.0)\n            for j in range(i):\n                result_deriv_coords = (\n                    open_volfiles[0]\n                    .get_tensor_component(\n                        obs_id, \"DerivCoords_\" + \"xyz\"[i] + \"xyz\"[j]\n                    )\n                    .data\n                )\n                npt.assert_allclose(result_deriv_coords, 0.0, atol=1e-14)\n        result_square_component = (\n            open_volfiles[0]\n            .get_tensor_component(obs_id, \"SquareComponent\")\n            .data\n        )\n        npt.assert_allclose(np.array(result_square_component), x**2)\n        result_abs = open_volfiles[0].get_tensor_component(obs_id, \"Abs\").data\n        npt.assert_allclose(np.array(result_abs), np.abs(np.array(psi)))\n        result_max = open_volfiles[0].get_tensor_component(obs_id, \"Max\").data\n        npt.assert_allclose(\n            np.array(result_max), np.ones(len(radius)) * np.max(np.array(psi))\n        )\n        result_lev = (\n            open_volfiles[0]\n            .get_tensor_component(obs_id, \"RefinementLevel_1\")\n            .data\n        )\n        npt.assert_allclose(np.array(result_lev), np.ones(len(radius)))\n\n    def test_integrate(self):\n        open_h5_files = [spectre_h5.H5File(self.h5_filename, \"a\")]\n        open_volfiles = [\n            h5file.get_vol(\"/element_data\") for h5file in open_h5_files\n        ]\n\n        kernels = [\n            Kernel(sinusoid, output_name=\"Sinusoid\"),\n        ]\n\n        integrals = transform_volume_data(\n            volfiles=open_volfiles, kernels=kernels, integrate=True\n        )\n\n        npt.assert_allclose(integrals[\"Volume\"], (2 * np.pi) ** 3)\n        # The domain has pretty low resolution so the integral is not\n        # particularly precise\n        npt.assert_allclose(integrals[\"Sinusoid\"], 64.0, rtol=1e-2)\n\n    def test_parse_kernels(self):\n        (mesh_kernel,) = list(\n            parse_kernels(\n                [\"spectre.Spectral.Mesh3D:number_of_grid_points\"],\n                exec_files=[],\n                map_input_names={},\n                interactive=False,\n            )\n        )\n        self.assertEqual(mesh_kernel.callable, Mesh[3].number_of_grid_points)\n\n    def test_cli(self):\n        runner = CliRunner()\n        cli_flags = [\n            self.h5_filename,\n            \"-d\",\n            \"element_data.vol\",\n            \"-e\",\n            __file__,\n            \"--start-time\",\n            \"0\",\n            \"--stop-time\",\n            \"1\",\n            \"--stride\",\n            \"2\",\n        ]\n        result = runner.invoke(\n            transform_volume_data_command,\n            cli_flags\n            + [\n                \"-k\",\n                \"psi_squared\",\n                \"-k\",\n                \"coordinate_radius\",\n                \"-k\",\n                \"Element:grid_coordinates\",\n                \"-i\",\n                \"psi=Psi\",\n            ],\n            catch_exceptions=False,\n            input=\"PsiSquared\\nCoordinateRadius\\n\",\n        )\n        self.assertEqual(result.exit_code, 0, msg=result.output)\n        with spectre_h5.H5File(self.h5_filename, \"r\") as open_h5_file:\n            volfile = open_h5_file.get_vol(\"/element_data\")\n            obs_id = volfile.list_observation_ids()[0]\n            psi = volfile.get_tensor_component(obs_id, \"Psi\").data\n            result_psisq = volfile.get_tensor_component(\n                obs_id, \"PsiSquared\"\n            ).data\n            npt.assert_allclose(np.array(result_psisq), np.array(psi) ** 2)\n            x, y, z = [\n                np.array(\n                    volfile.get_tensor_component(\n                        obs_id, \"InertialCoordinates\" + xyz\n                    ).data\n                )\n                for xyz in [\"_x\", \"_y\", \"_z\"]\n            ]\n            radius = np.sqrt(x**2 + y**2 + z**2)\n            result_radius = volfile.get_tensor_component(\n                obs_id, \"CoordinateRadius\"\n            ).data\n            npt.assert_allclose(np.array(result_radius), radius)\n            result_grid_coords = volfile.get_tensor_component(\n                obs_id, \"GridCoordinates_x\"\n            ).data\n            npt.assert_allclose(np.array(result_grid_coords), x)\n\n        # Test integrals\n        result = runner.invoke(\n            transform_volume_data_command,\n            cli_flags\n            + [\n                \"-k\",\n                \"sinusoid\",\n                \"--integrate\",\n            ],\n            catch_exceptions=False,\n            input=\"Sinusoid\\n\",\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertIn(\"63.88\", result.output)\n\n        output_filename = os.path.join(self.test_dir, \"integrals.h5\")\n        result = runner.invoke(\n            transform_volume_data_command,\n            cli_flags\n            + [\n                \"-k\",\n                \"sinusoid\",\n                \"--integrate\",\n                \"--output\",\n                output_filename,\n                \"--output-subfile\",\n                \"integrals\",\n            ],\n            catch_exceptions=False,\n            input=\"Sinusoid\\n\",\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        with spectre_h5.H5File(output_filename, \"r\") as open_h5_file:\n            datfile = open_h5_file.get_dat(\"/integrals\")\n            self.assertEqual(\n                datfile.get_legend(), [\"Time\", \"Volume\", \"Sinusoid\"]\n            )\n            npt.assert_allclose(\n                datfile.get_data(),\n                [[0.04, (2.0 * np.pi) ** 3, 64.0]],\n                rtol=1e-2,\n            )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_Cce.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <hdf5.h>\n#include <memory>\n#include <random>\n#include <regex>\n#include <sstream>\n#include <string>\n#include <typeinfo>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"IO/Connectivity.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Cce.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Formaline.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test_errors() {\n  std::string file_name{\"./Unit.IO.H5.Cce.FileErrorObjectAlreadyExists.h5\"};\n  CHECK_THROWS_WITH(\n      ([&file_name]() {\n        const uint32_t version_number = 4;\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n        const size_t l_max = 4;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& cce_file =\n              my_file.insert<h5::Cce>(\"/Bondi\", l_max, version_number);\n          (void)cce_file;\n          my_file.close_current_object();\n        }\n        { my_file.insert<h5::Cce>(\"/Bondi//\", l_max, version_number); }\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot insert an Object that already exists. Failed to \"\n          \"add Object named: /Bondi\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.FileErrorObjectAlreadyOpenGet.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const size_t l_max = 4;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        auto& cce_file = my_file.insert<h5::Cce>(\"/DummyPath\", l_max);\n        auto& get_cce_file = my_file.get<h5::Cce>(\"/DummyPath\", l_max);\n        (void)cce_file;\n        (void)get_cce_file;\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Object /DummyPath already open. Cannot open object /DummyPath.\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.FileErrorObjectAlreadyOpenInsert.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const size_t l_max = 4;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        auto& cce_file = my_file.insert<h5::Cce>(\"/DummyPath\", l_max);\n        auto& cce_file2 = my_file.insert<h5::Cce>(\"/DummyPath2\", l_max);\n        (void)cce_file;\n        (void)cce_file2;\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Object /DummyPath already open. Cannot insert object \"\n          \"/DummyPath2.\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.FileErrorObjectAlreadyOpenTryInsert.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const size_t l_max = 4;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        auto& cce_file = my_file.insert<h5::Cce>(\"/DummyPath\", l_max);\n        auto& cce_file2 = my_file.try_insert<h5::Cce>(\"/DummyPath2\", l_max);\n        (void)cce_file;\n        (void)cce_file2;\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Object /DummyPath already open. Cannot try to insert \"\n          \"object /DummyPath2.\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.FileErrorObjectAlreadyOpen.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const size_t l_max = 4;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& cce_file = my_file.insert<h5::Cce>(\"/DummyPath\", l_max);\n          my_file.close_current_object();\n          (void)cce_file;\n          auto& cce_file2 = my_file.insert<h5::Cce>(\"/Dummy/Path2\", l_max);\n          my_file.close_current_object();\n          (void)cce_file2;\n        }\n        auto& cce_file2 = my_file.get<h5::Cce>(\"/Dummy/Path2\", l_max);\n        auto& cce_file = my_file.get<h5::Cce>(\"/DummyPath\", l_max);\n        (void)cce_file;\n        (void)cce_file2;\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Object /Dummy/Path2 already open. Cannot open object /DummyPath.\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.LegendsDontMatch.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const size_t l_max = 4;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& cce_file = my_file.insert<h5::Cce>(\"/DummyPath\", l_max);\n          my_file.close_current_object();\n          (void)cce_file;\n          auto& cce_file2 = my_file.get<h5::Cce>(\"/DummyPath\", l_max + 1);\n          (void)cce_file2;\n        }\n      }(),\n      Catch::Matchers::ContainsSubstring(\"l_max (4) from cce file\") and\n          Catch::Matchers::ContainsSubstring(\n              \"does not match l_max (5) in constructor\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.NoBondiVariable.h5\";\n  CHECK_THROWS_WITH(\n      ([&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        // Easy to make data\n        const size_t l_max = 0;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& cce_file = my_file.insert<h5::Cce>(\"/Bondi\", l_max);\n          std::unordered_map<std::string, std::vector<double>> data{};\n          data[\"News\"] = std::vector<double>(3, 0.0);\n          cce_file.append(data);\n        }\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Passed in data does not contain the bondi variable\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.IncorrectDataSize.h5\";\n  CHECK_THROWS_WITH(\n      ([&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        // Easy to make data\n        const size_t l_max = 0;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& cce_file = my_file.insert<h5::Cce>(\"/Bondi\", l_max);\n          std::unordered_map<std::string, std::vector<double>> data{};\n          // Incorrect vector length. Need all the vars or else we'll hit the\n          // previous error\n          data[\"EthInertialRetardedTime\"] = std::vector<double>(10, 0.0);\n          data[\"News\"] = std::vector<double>(10, 0.0);\n          data[\"Psi0\"] = std::vector<double>(10, 0.0);\n          data[\"Psi1\"] = std::vector<double>(10, 0.0);\n          data[\"Psi2\"] = std::vector<double>(10, 0.0);\n          data[\"Psi3\"] = std::vector<double>(10, 0.0);\n          data[\"Psi4\"] = std::vector<double>(10, 0.0);\n          data[\"Strain\"] = std::vector<double>(10, 0.0);\n          cce_file.append(data);\n        }\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot add columns to Cce files. Current number of columns is 3 but \"\n          \"received 10 entries\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.LTooLarge.h5\";\n  CHECK_THROWS_WITH(\n      ([&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        // Easy to make data\n        const size_t l_max = 0;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& cce_file = my_file.insert<h5::Cce>(\"/Bondi\", l_max);\n          std::unordered_map<std::string, std::vector<double>> data{};\n          data[\"EthInertialRetardedTime\"] = std::vector<double>(3, 0.0);\n          data[\"News\"] = std::vector<double>(3, 0.0);\n          data[\"Psi0\"] = std::vector<double>(3, 0.0);\n          data[\"Psi1\"] = std::vector<double>(3, 0.0);\n          data[\"Psi2\"] = std::vector<double>(3, 0.0);\n          data[\"Psi3\"] = std::vector<double>(3, 0.0);\n          data[\"Psi4\"] = std::vector<double>(3, 0.0);\n          data[\"Strain\"] = std::vector<double>(3, 0.0);\n          cce_file.append(data);\n          const auto subset = cce_file.get_data_subset(\"Psi0\", {2}, 0);\n          (void)subset;\n        }\n      }()),\n      Catch::Matchers::ContainsSubstring(\"One (or more) of the requested ells \"\n                                         \"(2) is larger than the l_max 0\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Cce.IncorrectBondiVarDim.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        // Easy to make data\n        const size_t l_max = 0;\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& cce_file = my_file.insert<h5::Cce>(\"/Bondi\", l_max);\n          const auto size = cce_file.get_dimensions(\"NonExistentBondiVar\");\n          (void)size;\n        }\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Requested bondi variable NonExistentBondiVar not available\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n}\n\nvoid check_written_data(\n    const h5::Cce& cce_file,\n    const std::unordered_map<std::string, std::vector<std::vector<double>>>\n        expected_data,\n    const std::unordered_set<std::string>& bondi_variables,\n    const std::vector<std::string>& legend, const size_t version_number) {\n  // Check version info is correctly retrieved from Cce file\n  CHECK(cce_file.get_version() == version_number);\n\n  // Check getting the header from Cce file\n  std::stringstream ss;\n  ss << \"# \";\n  auto build_info = info_from_build();\n  ss << std::regex_replace(build_info, std::regex{\"\\n\"}, \"\\n# \");\n  const auto& header = cce_file.get_header();\n  CHECK(header.starts_with(\"#\\n# File created on \"));\n  CHECK(header.ends_with(ss.str()));\n\n  CHECK(cce_file.get_legend() == legend);\n\n  // Check data is retrieved correctly from Cce file\n  std::array<hsize_t, 2> size_of_data{{4, legend.size()}};\n  for (const std::string& bondi_var : bondi_variables) {\n    CHECK(cce_file.get_dimensions(bondi_var) == size_of_data);\n  }\n\n  const std::unordered_map<std::string, Matrix> data_in_cce_file =\n      [&expected_data, &legend, &bondi_variables]() {\n        std::unordered_map<std::string, Matrix> result{};\n        for (const std::string& bondi_var : bondi_variables) {\n          Matrix matrix_result(4, legend.size());\n          for (size_t row = 0; row < matrix_result.rows(); row++) {\n            for (size_t col = 0; col < legend.size(); col++) {\n              matrix_result(row, col) = expected_data.at(bondi_var)[row][col];\n            }\n          }\n\n          result[bondi_var] = std::move(matrix_result);\n        }\n        return result;\n      }();\n\n  CHECK(cce_file.get_data() == data_in_cce_file);\n  for (const std::string& bondi_var : bondi_variables) {\n    CHECK(cce_file.get_data(bondi_var) == data_in_cce_file.at(bondi_var));\n  }\n\n  // Just ell = 2\n  {\n    const std::unordered_map<std::string, Matrix> expected_subset =\n        [&expected_data, &bondi_variables]() {\n          std::unordered_map<std::string, Matrix> result{};\n          for (const std::string& bondi_var : bondi_variables) {\n            Matrix matrix_result(2, 11);\n            size_t row = 1;\n            for (size_t row_idx = 0; row_idx < 2; row_idx++, row++) {\n              // time\n              matrix_result(row_idx, 0) = expected_data.at(bondi_var)[row][0];\n              size_t col = 9;\n              for (size_t col_idx = 1; col_idx < 11; col_idx++, col++) {\n                matrix_result(row_idx, col_idx) =\n                    expected_data.at(bondi_var)[row][col];\n              }\n            }\n\n            result[bondi_var] = std::move(matrix_result);\n          }\n\n          return result;\n        }();\n\n    const auto subset = cce_file.get_data_subset({2}, 1, 2);\n    CHECK(subset == expected_subset);\n    for (const std::string& bondi_var : bondi_variables) {\n      CHECK(cce_file.get_data_subset(bondi_var, {2}, 1, 2) ==\n            expected_subset.at(bondi_var));\n    }\n  }\n  // ell = 1,3\n  {\n    const std::unordered_map<std::string, Matrix> expected_subset =\n        [&expected_data, &bondi_variables]() {\n          std::unordered_map<std::string, Matrix> result{};\n          for (const std::string& bondi_var : bondi_variables) {\n            Matrix matrix_result(3, 21);\n            size_t row = 1;\n            for (size_t row_idx = 0; row_idx < 3; row_idx++, row++) {\n              // time\n              matrix_result(row_idx, 0) = expected_data.at(bondi_var)[row][0];\n              // ell = 1\n              size_t col = 3;\n              for (size_t col_idx = 1; col_idx < 7; col_idx++, col++) {\n                matrix_result(row_idx, col_idx) =\n                    expected_data.at(bondi_var)[row][col];\n              }\n              // ell = 3\n              col = 19;\n              for (size_t col_idx = 7; col_idx < 21; col_idx++, col++) {\n                matrix_result(row_idx, col_idx) =\n                    expected_data.at(bondi_var)[row][col];\n              }\n            }\n\n            result[bondi_var] = std::move(matrix_result);\n          }\n\n          return result;\n        }();\n\n    const auto subset = cce_file.get_data_subset({1, 3}, 1, 3);\n    CHECK(subset == expected_subset);\n    for (const std::string& bondi_var : bondi_variables) {\n      CHECK(cce_file.get_data_subset(bondi_var, {1, 3}, 1, 3) ==\n            expected_subset.at(bondi_var));\n    }\n  }\n  // ell = 0,2\n  {\n    const std::unordered_map<std::string, Matrix> expected_subset =\n        [&expected_data, &bondi_variables]() {\n          std::unordered_map<std::string, Matrix> result{};\n          for (const std::string& bondi_var : bondi_variables) {\n            Matrix matrix_result(2, 13);\n            size_t row = 0;\n            for (size_t row_idx = 0; row_idx < 2; row_idx++, row++) {\n              // time\n              matrix_result(row_idx, 0) = expected_data.at(bondi_var)[row][0];\n              // ell = 0\n              size_t col = 1;\n              for (size_t col_idx = 1; col_idx < 3; col_idx++, col++) {\n                matrix_result(row_idx, col_idx) =\n                    expected_data.at(bondi_var)[row][col];\n              }\n\n              // ell = 2\n              col = 9;\n              for (size_t col_idx = 3; col_idx < 13; col_idx++, col++) {\n                matrix_result(row_idx, col_idx) =\n                    expected_data.at(bondi_var)[row][col];\n              }\n            }\n\n            result[bondi_var] = std::move(matrix_result);\n          }\n\n          return result;\n        }();\n\n    const auto subset = cce_file.get_data_subset({0, 2}, 0, 2);\n    CHECK(subset == expected_subset);\n    for (const std::string& bondi_var : bondi_variables) {\n      CHECK(cce_file.get_data_subset(bondi_var, {0, 2}, 0, 2) ==\n            expected_subset.at(bondi_var));\n    }\n  }\n  // No ell\n  {\n    const auto subset = cce_file.get_data_subset({}, 0, 2);\n    const Matrix answer(2, 0, 0.0);\n    const std::unordered_map<std::string, Matrix> expected_subset =\n        [&answer, &bondi_variables]() {\n          std::unordered_map<std::string, Matrix> result{};\n          for (const std::string& bondi_var : bondi_variables) {\n            result[bondi_var] = answer;\n          }\n          return result;\n        }();\n    CHECK(subset == expected_subset);\n    for (const std::string& bondi_var : bondi_variables) {\n      CHECK(cce_file.get_data_subset(bondi_var, {}, 0, 2) == answer);\n    }\n  }\n  // No rows\n  {\n    const auto subset = cce_file.get_data_subset({0, 3}, 0, 0);\n    const Matrix answer(0, 17, 0.0);\n    const std::unordered_map<std::string, Matrix> expected_subset =\n        [&answer, &bondi_variables]() {\n          std::unordered_map<std::string, Matrix> result{};\n          for (const std::string& bondi_var : bondi_variables) {\n            result[bondi_var] = answer;\n          }\n          return result;\n        }();\n    CHECK(subset == expected_subset);\n    for (const std::string& bondi_var : bondi_variables) {\n      CHECK(cce_file.get_data_subset(bondi_var, {0, 3}, 0, 0) == answer);\n    }\n  }\n}\n\ntemplate <typename Generator>\nvoid test_core_functionality(const gsl::not_null<Generator*> generator) {\n  const std::unordered_set<std::string> bondi_variables{\n      \"EthInertialRetardedTime\",\n      \"News\",\n      \"Psi0\",\n      \"Psi1\",\n      \"Psi2\",\n      \"Psi3\",\n      \"Psi4\",\n      \"Strain\"};\n\n  const std::string h5_file_name(\"Unit.IO.H5.Cce.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const size_t l_max = 4;\n  std::vector<std::string> legend{\n      \"time\",        \"Real Y_0,0\",  \"Imag Y_0,0\",  \"Real Y_1,-1\", \"Imag Y_1,-1\",\n      \"Real Y_1,0\",  \"Imag Y_1,0\",  \"Real Y_1,1\",  \"Imag Y_1,1\",  \"Real Y_2,-2\",\n      \"Imag Y_2,-2\", \"Real Y_2,-1\", \"Imag Y_2,-1\", \"Real Y_2,0\",  \"Imag Y_2,0\",\n      \"Real Y_2,1\",  \"Imag Y_2,1\",  \"Real Y_2,2\",  \"Imag Y_2,2\",  \"Real Y_3,-3\",\n      \"Imag Y_3,-3\", \"Real Y_3,-2\", \"Imag Y_3,-2\", \"Real Y_3,-1\", \"Imag Y_3,-1\",\n      \"Real Y_3,0\",  \"Imag Y_3,0\",  \"Real Y_3,1\",  \"Imag Y_3,1\",  \"Real Y_3,2\",\n      \"Imag Y_3,2\",  \"Real Y_3,3\",  \"Imag Y_3,3\",  \"Real Y_4,-4\", \"Imag Y_4,-4\",\n      \"Real Y_4,-3\", \"Imag Y_4,-3\", \"Real Y_4,-2\", \"Imag Y_4,-2\", \"Real Y_4,-1\",\n      \"Imag Y_4,-1\", \"Real Y_4,0\",  \"Imag Y_4,0\",  \"Real Y_4,1\",  \"Imag Y_4,1\",\n      \"Real Y_4,2\",  \"Imag Y_4,2\",  \"Real Y_4,3\",  \"Imag Y_4,3\",  \"Real Y_4,4\",\n      \"Imag Y_4,4\"};\n\n  const auto create_data =\n      [&legend, &generator](const size_t row) -> std::vector<double> {\n    std::uniform_real_distribution<double> dist{static_cast<double>(row),\n                                                static_cast<double>(row + 1)};\n    std::vector<double> result(legend.size());\n    result[0] = static_cast<double>(row);\n    for (size_t i = 1; i < legend.size(); i++) {\n      result[i] = dist(*generator);\n    }\n    return result;\n  };\n\n  std::unordered_map<std::string, std::vector<std::vector<double>>>\n      expected_data{};\n  {\n    h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n    my_file.insert<h5::Cce>(\"/Bondi\", l_max, version_number);\n    my_file.close_current_object();\n\n    // Check that the Cce file is found to be a subgroup of the file.\n    const auto groups_in_file = my_file.groups();\n    CHECK(alg::find(groups_in_file, std::string{\"Bondi.cce\"}) !=\n          groups_in_file.end());\n\n    auto& cce_file = my_file.get<h5::Cce>(\"/Bondi\", l_max, version_number);\n\n    CHECK(legend == cce_file.get_legend());\n\n    std::unordered_map<std::string, std::vector<double>> tmp_data{};\n    for (const std::string& bondi_var : bondi_variables) {\n      expected_data[bondi_var] = std::vector<std::vector<double>>(4);\n      tmp_data[bondi_var];\n    }\n\n    for (size_t i = 0; i < 4; i++) {\n      for (const std::string& bondi_var : bondi_variables) {\n        tmp_data.at(bondi_var) = create_data(i);\n        expected_data.at(bondi_var)[i] = tmp_data.at(bondi_var);\n      }\n      cce_file.append(tmp_data);\n    }\n\n    // Test with ReadWrite access\n    check_written_data(cce_file, expected_data, bondi_variables, legend,\n                       version_number);\n  }\n\n  // Test with ReadOnly access\n  {\n    h5::H5File<h5::AccessType::ReadOnly> my_file(h5_file_name);\n    const auto& cce_file =\n        my_file.get<h5::Cce>(\"Bondi.cce\", l_max, version_number);\n\n    check_written_data(cce_file, expected_data, bondi_variables, legend,\n                       version_number);\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.IO.H5.Cce\", \"[Unit][IO][H5]\") {\n  MAKE_GENERATOR(generator);\n  test_errors();\n  test_core_functionality(make_not_null(&generator));\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_CheckH5PropertiesMatch.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5PropertiesMatch.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace {\n\n// Helper function to set up test files\ntemplate <typename DataType>\nvoid setup_test_files(const std::vector<std::string>& h5_file_names) {\n  REQUIRE(h5_file_names.size() == 3);\n\n  // Sample volume data\n  const std::string grid_name{\"AhA\"};\n  const std::vector<DataType> tensor_components_and_coords{{0.0, 1.0},\n                                                           {-78.9, -7.6}};\n  const std::vector<TensorComponent> tensor_components{\n      {\"InertialCoordinates_1D\", tensor_components_and_coords[0]},\n      {\"TestScalar\", tensor_components_and_coords[1]}};\n\n  const std::vector<size_t> observation_ids{2345, 3456, 4567};\n  const std::vector<double> observation_values{1.0, 2.0, 3.0};\n  const std::vector<Spectral::Basis> bases{Spectral::Basis::Legendre};\n  const std::vector<Spectral::Quadrature> quadratures{\n      Spectral::Quadrature::Gauss};\n  const std::vector<size_t> extents{2};\n\n  const uint32_t version_number = 4;\n\n  // Set up files with same observation ids and source archives\n  for (const auto& file_name : h5_file_names) {\n    // Remove any pre-existing file with the same name\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n\n    // Create a new file with the given name\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{file_name};\n    auto& volume_file =\n        h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n\n    // Write volume data to new file\n    for (size_t j = 0; j < 3; j++) {\n      volume_file.write_volume_data(\n          observation_ids[j], observation_values[j],\n          std::vector<ElementVolumeData>{\n              {grid_name, tensor_components, extents, bases, quadratures}});\n    }\n    h5_file.close_current_object();\n  }\n\n  // Braces to specify scope for H5 file\n  {\n    // Set up last file with additional observation id and data\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_names[2], true};\n    auto& volume_file =\n        h5_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    volume_file.write_volume_data(\n        5678, 4.0,\n        std::vector<ElementVolumeData>{\n            {grid_name, tensor_components, extents, bases, quadratures}});\n  }  // End of scope for H5 file\n}\n\ntemplate <typename DataType>\nvoid test_check_src_files_match() {\n  const std::vector<std::string> h5_file_names{\n      \"Unit.IO.H5.CheckH5PropertiesMatch0.h5\",\n      \"Unit.IO.H5.CheckH5PropertiesMatch1.h5\",\n      \"Unit.IO.H5.CheckH5PropertiesMatch2.h5\"};\n\n  setup_test_files<DataType>(h5_file_names);\n\n  // Check function for files with same source archive\n  CHECK(h5::check_src_files_match(h5_file_names) == true);\n\n  // Remove files after test\n  for (const auto& file_name : h5_file_names) {\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid test_check_observation_ids_match() {\n  // List of filenames whose ids all match\n  const std::vector<std::string> h5_file_names_match{\n      \"Unit.IO.H5.CheckH5PropertiesMatch0.h5\",\n      \"Unit.IO.H5.CheckH5PropertiesMatch1.h5\"};\n\n  // List of filenames whose ids do not all match\n  const std::vector<std::string> h5_file_names_diff{\n      \"Unit.IO.H5.CheckH5PropertiesMatch0.h5\",\n      \"Unit.IO.H5.CheckH5PropertiesMatch1.h5\",\n      \"Unit.IO.H5.CheckH5PropertiesMatch2.h5\"};\n\n  setup_test_files<DataType>(h5_file_names_diff);\n\n  // Check function for files with same observation ids\n  CHECK(h5::check_observation_ids_match(h5_file_names_match, \"/element_data\") ==\n        true);\n  // Check function for files with different observation ids\n  CHECK(h5::check_observation_ids_match(h5_file_names_diff, \"/element_data\") ==\n        false);\n\n  // Remove files after test\n  for (const auto& file_name : h5_file_names_diff) {\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.IO.H5.CheckH5PropertiesMatch\", \"[Unit][IO][H5]\") {\n  test_check_src_files_match<DataVector>();\n  test_check_src_files_match<std::vector<float>>();\n  test_check_observation_ids_match<DataVector>();\n  test_check_observation_ids_match<std::vector<float>>();\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_CombineH5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Cce.hpp\"\n#include \"IO/H5/CombineH5.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace {\nvoid test_single_file() {\n  const std::string combined_filename{\"CombinedSingle.h5\"};\n  const std::string original_filename{\"OriginalFileName.h5\"};\n  if (file_system::check_if_file_exists(combined_filename)) {\n    file_system::rm(combined_filename, true);\n  }\n  if (file_system::check_if_file_exists(original_filename)) {\n    file_system::rm(original_filename, true);\n  }\n\n  const std::string subfile_name{\"SubfileName\"};\n  const Matrix data{{0.0, 0.0}, {1.0, 1.0}, {2.0, 2.0}, {3.0, 3.0}};\n  const std::vector<std::string> legend{\"Time\", \"Data\"};\n  const uint32_t version = 4;\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{original_filename};\n    auto& dat_file = h5_file.insert<h5::Dat>(subfile_name, legend, version);\n    dat_file.append(data);\n  }\n\n  h5::combine_h5_dat({original_filename}, combined_filename, Verbosity::Quiet);\n\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> h5_file{combined_filename};\n    const auto& dat_file = h5_file.get<h5::Dat>(subfile_name);\n    CHECK(dat_file.get_version() == version);\n    CHECK(dat_file.get_legend() == legend);\n    CHECK(dat_file.get_data() == data);\n  }\n\n  if (file_system::check_if_file_exists(combined_filename)) {\n    file_system::rm(combined_filename, true);\n  }\n  if (file_system::check_if_file_exists(original_filename)) {\n    file_system::rm(original_filename, true);\n  }\n}\n\nvoid test() {\n  const std::string combined_filename{\"MightyMorphinPowerRangers.h5\"};\n  if (file_system::check_if_file_exists(combined_filename)) {\n    file_system::rm(combined_filename, true);\n  }\n  const std::vector<std::string> individual_filenames{\n      \"RedRanger.h5\", \"BlackRanger.h5\", \"BlueRanger.h5\", \"YellowRanger.h5\"};\n  const std::vector<std::string> subfile_names{\"Subfile1\", \"Subfile2\"};\n  // All subfiles can just share the same legend. The data doesn't matter, only\n  // the times for this test.\n  const std::vector<std::string> legend{\"Time\", \"Data\"};\n  const std::vector<std::vector<std::vector<double>>> data{\n      // This file doesn't keep the last time\n      {{0.0, 0.0}, {1.0, 1.0}, {2.0, 0.0}},\n      // This file keeps all times, but is unordered\n      {{2.1, 3.0}, {1.5, 2.0}},\n      // This file only keeps the earliest time, but is unordered\n      {{4.1, 0.0}, {3.0, 4.0}, {4.5, 0.0}},\n      // This file keeps all times\n      {{4.0, 5.0}, {5.5, 6.0}, {6.0, 7.0}}};\n\n  const Matrix expected_data{{0.0, 0.0}, {1.0, 1.0}, {1.5, 2.0}, {2.1, 3.0},\n                             {3.0, 4.0}, {4.0, 5.0}, {5.5, 6.0}, {6.0, 7.0}};\n\n  // Write the individual files\n  {\n    for (size_t i = 0; i < individual_filenames.size(); i++) {\n      const std::string& filename = individual_filenames[i];\n      if (file_system::check_if_file_exists(filename)) {\n        file_system::rm(filename, true);\n      }\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{filename};\n      for (const std::string& subfile_name : subfile_names) {\n        auto& dat_file = h5_file.insert<h5::Dat>(subfile_name, legend);\n        dat_file.append(data[i]);\n        h5_file.close_current_object();\n      }\n    }\n  }\n\n  // Combine the H5 files\n  h5::combine_h5_dat(individual_filenames, combined_filename,\n                     Verbosity::Verbose);\n\n  REQUIRE(file_system::check_if_file_exists(combined_filename));\n\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> h5_file{combined_filename};\n    for (const std::string& subfile_name : subfile_names) {\n      CAPTURE(subfile_name);\n      const auto& dat_file = h5_file.get<h5::Dat>(subfile_name);\n      const Matrix dat_data = dat_file.get_data();\n      CHECK(expected_data == dat_data);\n      h5_file.close_current_object();\n    }\n  }\n\n  if (file_system::check_if_file_exists(combined_filename)) {\n    file_system::rm(combined_filename, true);\n  }\n\n  for (const std::string& filename : individual_filenames) {\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, true);\n    }\n  }\n}\n\nvoid test_errors() {\n  const std::string fake_file{\"FakeFile.h5\"};\n  const std::string error_filename_1{\"CombineH5Error1.h5\"};\n  const std::string error_filename_2{\"CombineH5Error2.h5\"};\n  CHECK_THROWS_WITH(\n      h5::combine_h5_dat({}, fake_file),\n      Catch::Matchers::ContainsSubstring(\"No H5 files to combine!\"));\n\n  const auto delete_files = [&]() {\n    if (file_system::check_if_file_exists(error_filename_1)) {\n      file_system::rm(error_filename_1, true);\n    }\n    if (file_system::check_if_file_exists(error_filename_2)) {\n      file_system::rm(error_filename_2, true);\n    }\n    if (file_system::check_if_file_exists(fake_file)) {\n      file_system::rm(fake_file, true);\n    }\n  };\n\n  delete_files();\n\n  {\n    INFO(\"No dat files\");\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_1};\n      h5_file.insert<h5::Cce>(\"CceSubfile\", 4);\n    }\n    CHECK_THROWS_WITH(\n        h5::combine_h5_dat({error_filename_1}, fake_file),\n        Catch::Matchers::ContainsSubstring(\"No dat files in H5 file\"));\n  }\n\n  {\n    INFO(\"No times in dat file\");\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_1, true};\n      h5_file.insert<h5::Dat>(\"DatSubfile\",\n                              std::vector<std::string>{\"Time\", \"Blah\"});\n    }\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_2, true};\n      h5_file.insert<h5::Dat>(\"DatSubfile\",\n                              std::vector<std::string>{\"Time\", \"Blah\"});\n    }\n    CHECK_THROWS_WITH(\n        h5::combine_h5_dat({error_filename_1, error_filename_2}, fake_file),\n        Catch::Matchers::ContainsSubstring(\"No times in dat file\"));\n  }\n\n  delete_files();\n\n  {\n    INFO(\"Legends don't match\");\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_1, true};\n      auto& dat_file = h5_file.try_insert<h5::Dat>(\n          \"DatSubfile\", std::vector<std::string>{\"Time\", \"Blah\"});\n      dat_file.append(std::vector{0.0, 0.0});\n    }\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_2, true};\n      auto& dat_file = h5_file.try_insert<h5::Dat>(\n          \"DatSubfile\", std::vector<std::string>{\"Time\", \"DifferentBlah\"});\n      dat_file.append(std::vector{0.0, 0.0});\n    }\n    CHECK_THROWS_WITH(\n        h5::combine_h5_dat({error_filename_1, error_filename_2}, fake_file),\n        Catch::Matchers::ContainsSubstring(\"Legend of dat file\") and\n            Catch::Matchers::ContainsSubstring(\"doesn't match other H5 files\"));\n  }\n\n  delete_files();\n\n  {\n    INFO(\"Versions don't match\");\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_1, true};\n      auto& dat_file = h5_file.try_insert<h5::Dat>(\n          \"DatSubfile\", std::vector<std::string>{\"Time\", \"Blah\"}, 0);\n      dat_file.append(std::vector{0.0, 0.0});\n    }\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_2, true};\n      auto& dat_file = h5_file.try_insert<h5::Dat>(\n          \"DatSubfile\", std::vector<std::string>{\"Time\", \"Blah\"}, 1);\n      dat_file.append(std::vector{0.0, 0.0});\n    }\n    CHECK_THROWS_WITH(\n        h5::combine_h5_dat({error_filename_1, error_filename_2}, fake_file),\n        Catch::Matchers::ContainsSubstring(\"Version of dat file\") and\n            Catch::Matchers::ContainsSubstring(\"doesn't match other H5 files\"));\n  }\n\n  delete_files();\n\n  {\n    INFO(\"Non monotonically increasing H5 files\");\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_1, true};\n      auto& dat_file = h5_file.try_insert<h5::Dat>(\n          \"DatSubfile\", std::vector<std::string>{\"Time\", \"Blah\"}, 0);\n      dat_file.append(std::vector{1.0, 0.0});\n    }\n    {\n      h5::H5File<h5::AccessType::ReadWrite> h5_file{error_filename_2, true};\n      auto& dat_file = h5_file.try_insert<h5::Dat>(\n          \"DatSubfile\", std::vector<std::string>{\"Time\", \"Blah\"}, 0);\n      dat_file.append(std::vector{0.0, 0.0});\n    }\n    CHECK_THROWS_WITH(\n        h5::combine_h5_dat({error_filename_1, error_filename_2}, fake_file),\n        Catch::Matchers::ContainsSubstring(\n            \"are not monotonically increasing in their first \"\n            \"times for dat file\"));\n  }\n}\n}  // namespace\n\n// [[TimeOut, 15]]\nSPECTRE_TEST_CASE(\"Unit.IO.H5.CombineH5\", \"[Unit][IO][H5]\") {\n  test_single_file();\n  test_errors();\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_CombineH5.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre import Informer\nfrom spectre.DataStructures import DataVector\nfrom spectre.Domain import (\n    PiecewisePolynomial3,\n    serialize_domain,\n    serialize_functions_of_time,\n)\nfrom spectre.Domain.Creators import Brick\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent, combine_h5_vol\nfrom spectre.IO.H5.CombineH5 import combine_h5_command\nfrom spectre.Spectral import Basis, Quadrature\n\n\nclass TestCombineH5(unittest.TestCase):\n    # Test Fixtures\n    def setUp(self):\n        # The tests in this class combine 2 HDF5 files to generate one,\n        # using the Combine_H5 functionality.\n        self.file_name1 = os.path.join(\n            Informer.unit_test_build_path(), \"IO/TestVolumeData0.h5\"\n        )\n        self.file_name2 = os.path.join(\n            Informer.unit_test_build_path(), \"IO/TestVolumeData1.h5\"\n        )\n        self.file_names = [self.file_name1, self.file_name2]\n        self.subfile_name = \"/element_data\"\n\n        self.output_file = os.path.join(\n            Informer.unit_test_build_path(), \"IO/TestOutput.h5\"\n        )\n\n        if os.path.isfile(self.file_name1):\n            os.remove(self.file_name1)\n        if os.path.isfile(self.file_name2):\n            os.remove(self.file_name2)\n        if os.path.isfile(self.output_file):\n            os.remove(self.output_file)\n\n        # Initializing attributes\n        grid_names1 = [\"[B0,(L0I0,L0I0,L1I0)]\"]\n        grid_names2 = [\"[B1,(L1I0,L0I0,L0I0)]\"]\n        observation_values = {0: 7.0, 1: 1.3}\n        basis = Basis.Legendre\n        quad = Quadrature.Gauss\n        self.observation_ids = [0, 1]\n        domain_creator = Brick(\n            lower_bounds=[0.0, 0.0, 0.0],\n            upper_bounds=[1.0, 1.0, 1.0],\n            initial_refinement_levels=[0, 0, 0],\n            initial_num_points=[2, 2, 2],\n            is_periodic=[False, False, False],\n        )\n        self.serialized_domain = serialize_domain(\n            domain_creator.create_domain()\n        )\n\n        def make_translation_fot(fill_value, expiration):\n            coefficients = [\n                DataVector(size=3, fill=fill_value) for _ in range(4)\n            ]\n            return PiecewisePolynomial3(0.0, coefficients, expiration)\n\n        self.serialized_global_fots = serialize_functions_of_time(\n            {\"Translation\": make_translation_fot(0.0, 100.0)}\n        )\n        self.serialized_observation_fots = {\n            obs_id: serialize_functions_of_time(\n                {\n                    \"Translation\": make_translation_fot(\n                        observation_values[obs_id],\n                        observation_values[obs_id] + 0.01,\n                    )\n                }\n            )\n            for obs_id in self.observation_ids\n        }\n\n        # Writing ElementVolume data and TensorComponent Data to first file\n        self.h5_file1 = spectre_h5.H5File(file_name=self.file_name1, mode=\"a\")\n        self.tensor_component_data1 = np.array(\n            [\n                [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7],\n                [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08],\n            ]\n        )\n        self.h5_file1.insert_vol(self.subfile_name, version=0)\n        self.h5_file1.close_current_object()\n        self.vol_file1 = self.h5_file1.get_vol(self.subfile_name)\n\n        self.element_vol_data_file_1 = [\n            ElementVolumeData(\n                element_name=grid_names1[0],\n                components=[\n                    TensorComponent(\n                        \"field_1\", DataVector(self.tensor_component_data1[i])\n                    ),\n                    TensorComponent(\n                        \"field_2\",\n                        DataVector(self.tensor_component_data1[i]),\n                    ),\n                ],\n                extents=3 * [2],\n                basis=3 * [basis],\n                quadrature=3 * [quad],\n            )\n            for i, observation_id in enumerate(self.observation_ids)\n        ]\n\n        # Write extents and tensor volume data to the volfile\n        for i, observation_id in enumerate(self.observation_ids):\n            self.vol_file1.write_volume_data(\n                observation_id,\n                observation_values[observation_id],\n                [\n                    self.element_vol_data_file_1[i],\n                ],\n                self.serialized_domain,\n                self.serialized_observation_fots[observation_id],\n                self.serialized_global_fots,\n            )\n\n        # Store initial connectivity data\n        self.initial_h5_connectivity = self.vol_file1.get_tensor_component(\n            self.observation_ids[0], \"connectivity\"\n        ).data\n\n        self.h5_file1.close()\n\n        # Writing ElementVolume data and TensorComponent Data to second file\n        self.h5_file2 = spectre_h5.H5File(file_name=self.file_name2, mode=\"a\")\n        self.tensor_component_data2 = np.array(\n            [\n                [0.1, 0.12, 0.22, 0.32, 0.42, 0.52, 0.62, 0.72],\n                [0.011, 0.021, 0.031, 0.041, 0.051, 0.061, 0.079, 0.089],\n            ]\n        )\n        self.h5_file2.insert_vol(self.subfile_name, version=0)\n        self.h5_file2.close_current_object()\n        self.vol_file2 = self.h5_file2.get_vol(self.subfile_name)\n\n        self.element_vol_data_file_2 = [\n            ElementVolumeData(\n                element_name=grid_names2[0],\n                components=[\n                    TensorComponent(\n                        \"field_1\", DataVector(self.tensor_component_data2[i])\n                    ),\n                    TensorComponent(\n                        \"field_2\",\n                        DataVector(self.tensor_component_data2[i]),\n                    ),\n                ],\n                extents=3 * [2],\n                basis=3 * [basis],\n                quadrature=3 * [quad],\n            )\n            for i, observation_id in enumerate(self.observation_ids)\n        ]\n\n        # Write extents and tensor volume data to the volfile\n        for i, observation_id in enumerate(self.observation_ids):\n            self.vol_file2.write_volume_data(\n                observation_id,\n                observation_values[observation_id],\n                [\n                    self.element_vol_data_file_2[i],\n                ],\n                self.serialized_domain,\n                self.serialized_observation_fots[observation_id],\n                self.serialized_global_fots,\n            )\n        self.h5_file2.close()\n\n    def tearDown(self):\n        self.h5_file1.close()\n        self.h5_file2.close()\n        if os.path.isfile(self.file_name1):\n            os.remove(self.file_name1)\n        if os.path.isfile(self.file_name2):\n            os.remove(self.file_name2)\n        if os.path.isfile(self.output_file):\n            os.remove(self.output_file)\n\n    def test_combine_h5(self):\n        # Run the combine_h5 command and check if any feature (for eg.\n        # connectivity length has increased due to combining two files)\n\n        combine_h5_vol(\n            self.file_names,\n            self.subfile_name,\n            self.output_file,\n            None,\n            None,\n            None,\n            False,\n        )\n        h5_output = spectre_h5.H5File(file_name=self.output_file, mode=\"r\")\n        output_vol = h5_output.get_vol(self.subfile_name)\n\n        # Test observation ids\n\n        actual_obs_ids = output_vol.list_observation_ids()\n        actual_obs_ids.sort()\n\n        expected_obs_ids = [0, 1]\n\n        self.assertEqual(actual_obs_ids, expected_obs_ids)\n\n        # Test observation values\n\n        obs_id = actual_obs_ids[0]\n\n        actual_obs_value = output_vol.get_observation_value(obs_id)\n        expected_obs_value = 7.0\n\n        self.assertEqual(actual_obs_value, expected_obs_value)\n\n        for obs_to_check in actual_obs_ids:\n            self.assertEqual(output_vol.get_domain(), self.serialized_domain)\n            self.assertEqual(\n                output_vol.get_functions_of_time(obs_to_check),\n                self.serialized_observation_fots[obs_to_check],\n            )\n\n        self.assertEqual(\n            output_vol.get_global_functions_of_time(),\n            self.serialized_global_fots,\n        )\n        # Test tensor components\n\n        actual_tensor_component_names = output_vol.list_tensor_components(\n            obs_id\n        )\n\n        expected_tensor_component_names = [\"field_1\", \"field_2\"]\n\n        self.assertEqual(\n            actual_tensor_component_names, expected_tensor_component_names\n        )\n\n        expected_tensor_components = np.array(\n            [\n                (\n                    0,\n                    0.1,\n                    0.2,\n                    0.3,\n                    0.4,\n                    0.5,\n                    0.6,\n                    0.7,\n                    0.1,\n                    0.12,\n                    0.22,\n                    0.32,\n                    0.42,\n                    0.52,\n                    0.62,\n                    0.72,\n                ),\n                (\n                    0.01,\n                    0.02,\n                    0.03,\n                    0.04,\n                    0.05,\n                    0.06,\n                    0.07,\n                    0.08,\n                    0.011,\n                    0.021,\n                    0.031,\n                    0.041,\n                    0.051,\n                    0.061,\n                    0.079,\n                    0.089,\n                ),\n            ]\n        )\n        for i in range(len(expected_tensor_components)):\n            npt.assert_equal(\n                output_vol.get_tensor_component(\n                    i, actual_tensor_component_names[i]\n                ).data,\n                expected_tensor_components[i],\n            )\n\n    def test_cli(self):\n        # Checks if the CLI for CombineH5 runs properly\n        runner = CliRunner()\n        result = runner.invoke(\n            combine_h5_command,\n            [\n                \"vol\",\n                *self.file_names,\n                \"-d\",\n                self.subfile_name,\n                \"-o\",\n                self.output_file,\n                \"--check-src\",\n            ],\n            catch_exceptions=False,\n        )\n\n        h5_output = spectre_h5.H5File(file_name=self.output_file, mode=\"r\")\n        output_vol = h5_output.get_vol(self.subfile_name)\n\n        # Extracts the connectivity data from the volume file\n        # If length of final connectivity is more, combine_h5\n        # has successfully merged the two HDF5 files.\n\n        final_h5_connectivity = output_vol.get_tensor_component(\n            self.observation_ids[0], \"connectivity\"\n        ).data\n\n        assert len(self.initial_h5_connectivity) < len(final_h5_connectivity)\n        self.assertEqual(result.exit_code, 0, result.output)\n\n    def test_cli2(self):\n        # Checks that if no subfile name is given, the CLI prints\n        # available subfiles correctly\n        runner = CliRunner()\n        result = runner.invoke(\n            combine_h5_command,\n            [\n                *self.file_names,\n                \"-o\",\n                self.output_file,\n                \"--check-src\",\n            ],\n            catch_exceptions=False,\n        )\n        assert result.output is not None\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_Dat.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <hdf5.h>\n#include <memory>\n#include <regex>\n#include <sstream>\n#include <string>\n#include <type_traits>\n#include <typeinfo>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/Connectivity.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Formaline.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename T>\nT expected_data(const std::vector<double>& unformatted_data,\n                const std::array<size_t, 2>& size) {\n  REQUIRE(size[0] * size[1] == unformatted_data.size());\n  T result{};\n\n  if constexpr (std::is_same_v<T, Matrix>) {\n    result = Matrix(size[0], size[1]);\n  } else {\n    result = std::vector<std::vector<double>>(size[0]);\n    for (size_t i = 0; i < size[0]; i++) {\n      result[i].resize(size[1]);\n    }\n  }\n\n  for (size_t i = 0; i < size[0]; i++) {\n    for (size_t j = 0; j < size[1]; j++) {\n      if constexpr (std::is_same_v<T, Matrix>) {\n        result(i, j) = unformatted_data[j + i * size[1]];\n      } else {\n        result[i][j] = unformatted_data[j + i * size[1]];\n      }\n    }\n  }\n\n  return result;\n}\n\nvoid check_get_data(const h5::Dat& dat_file,\n                    const std::vector<double>& unformatted_data,\n                    const std::array<size_t, 2>& size) {\n  {\n    using T = Matrix;\n    const auto data = expected_data<T>(unformatted_data, size);\n    CHECK(dat_file.get_data<T>() == data);\n  }\n  {\n    using T = std::vector<std::vector<double>>;\n    const auto data = expected_data<T>(unformatted_data, size);\n    CHECK(dat_file.get_data<T>() == data);\n  }\n}\n\nvoid check_get_data_subset(const h5::Dat& dat_file,\n                           const std::vector<double>& unformatted_data,\n                           const std::array<size_t, 2>& size,\n                           const std::vector<size_t>& these_columns,\n                           size_t first_row, size_t num_rows) {\n  {\n    using T = Matrix;\n    const auto data = expected_data<T>(unformatted_data, size);\n    CHECK(dat_file.get_data_subset<T>(these_columns, first_row, num_rows) ==\n          data);\n  }\n  {\n    using T = std::vector<std::vector<double>>;\n    const auto data = expected_data<T>(unformatted_data, size);\n    CHECK(dat_file.get_data_subset<T>(these_columns, first_row, num_rows) ==\n          data);\n  }\n}\n\nvoid test_errors() {\n  std::string file_name{\"./Unit.IO.H5.Dat.FileErrorObjectAlreadyExists.h5\"};\n  CHECK_THROWS_WITH(\n      ([&file_name]() {\n        const uint32_t version_number = 4;\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n        const std::vector<std::string> legend{\"Time\", \"Error L2\", \"Error L1\",\n                                              \"Error\"};\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& error_file =\n              my_file.insert<h5::Dat>(\"/L2_errors///\", legend, version_number);\n          error_file.append(std::vector<double>{0, 0.1, 0.2, 0.3});\n          my_file.close_current_object();\n        }\n        { my_file.insert<h5::Dat>(\"/L2_errors//\", legend, version_number); }\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot insert an Object that already exists. Failed to \"\n          \"add Object named: /L2_errors\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Dat.FileErrorObjectAlreadyOpenGet.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const std::vector<std::string> legend{\"DummyLegend\"};\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        auto& dat_file = my_file.insert<h5::Dat>(\"/DummyPath\", legend);\n        auto& get_dat_file = my_file.get<h5::Dat>(\"/DummyPath\");\n        (void)dat_file;\n        (void)get_dat_file;\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Object /DummyPath already open. Cannot open object /DummyPath.\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Dat.FileErrorObjectAlreadyOpenInsert.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const std::vector<std::string> legend{\"DummyLegend\"};\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        auto& dat_file = my_file.insert<h5::Dat>(\"/DummyPath\", legend);\n        auto& dat_file2 = my_file.insert<h5::Dat>(\"/DummyPath2\");\n        (void)dat_file;\n        (void)dat_file2;\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Object /DummyPath already open. Cannot insert object /DummyPath2.\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Dat.FileErrorObjectAlreadyOpenTryInsert.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const std::vector<std::string> legend{\"DummyLegend\"};\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        auto& dat_file = my_file.insert<h5::Dat>(\"/DummyPath\", legend);\n        auto& dat_file2 = my_file.try_insert<h5::Dat>(\"/DummyPath2\");\n        (void)dat_file;\n        (void)dat_file2;\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Object /DummyPath already open. Cannot try to insert \"\n          \"object /DummyPath2.\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n\n  file_name = \"./Unit.IO.H5.Dat.FileErrorObjectAlreadyOpen.h5\";\n  CHECK_THROWS_WITH(\n      [&file_name]() {\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        const std::vector<std::string> legend{\"DummyLegend\"};\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        {\n          auto& dat_file = my_file.insert<h5::Dat>(\"/DummyPath\", legend);\n          my_file.close_current_object();\n          (void)dat_file;\n          auto& dat_file2 = my_file.insert<h5::Dat>(\"/Dummy/Path2\", legend);\n          my_file.close_current_object();\n          (void)dat_file2;\n        }\n        auto& dat_file2 = my_file.get<h5::Dat>(\"/Dummy/Path2\");\n        auto& dat_file = my_file.get<h5::Dat>(\"/DummyPath\");\n        (void)dat_file;\n        (void)dat_file2;\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Object /Dummy/Path2 already open. Cannot open object /DummyPath.\"));\n  if (file_system::check_if_file_exists(file_name)) {\n    file_system::rm(file_name, true);\n  }\n}\n\nvoid test_core_functionality() {\n  const std::string h5_file_name(\"Unit.IO.H5.Dat.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  std::vector<std::string> legend{\"Time\", \"Error L2\", \"Error L1\", \"Error\"};\n\n  h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n  my_file.insert<h5::Dat>(\"/L2_errors\", legend, version_number);\n  my_file.close_current_object();\n\n  // Check that the Dat file is found to be a subgroup of the file\n  const auto groups_in_file = my_file.groups();\n  CHECK(alg::find(groups_in_file, std::string{\"L2_errors.dat\"}) !=\n        groups_in_file.end());\n\n  // pass bad values to make sure the original ones aren't overridden.\n  auto evil_legend = legend;\n  evil_legend.emplace_back(\"Uh oh\");\n  auto& error_file =\n      my_file.try_insert<h5::Dat>(\"/L2_errors\", evil_legend, version_number);\n\n  error_file.append(std::vector<std::vector<double>>{{0.00, 0.10, 0.20, 0.30},\n                                                     {0.11, 0.40, 0.50, 0.60},\n                                                     {0.22, 0.55, 0.60, 0.80}});\n  error_file.append(std::vector<std::vector<double>>{});\n  error_file.append(std::vector<double>{0.33, 0.66, 0.77, 0.90});\n\n  // Check version info is correctly retrieved from Dat file\n  CHECK(error_file.get_version() == version_number);\n\n  // Check getting the header from Dat file\n  std::stringstream ss;\n  ss << \"# \";\n  auto build_info = info_from_build();\n  ss << std::regex_replace(build_info, std::regex{\"\\n\"}, \"\\n# \");\n  const auto& header = error_file.get_header();\n  CHECK(\"#\\n# File created on \"s ==\n        header.substr(0, header.find(\"File created on \") + 16));\n  CHECK(ss.str() == header.substr(header.find(\"# SpECTRE\")));\n\n  CHECK(error_file.get_legend() == legend);\n\n  // Check data is retrieved correctly from Dat file\n  std::array<hsize_t, 2> size_of_data{{4, 4}};\n  CHECK(error_file.get_dimensions() == size_of_data);\n\n  // For the docs\n  {\n    // [h5dat_get_data]\n    const Matrix matrix_data_in_dat_file = error_file.get_data();\n    const auto matrix_data_in_dat_file2 = error_file.get_data<Matrix>();\n    const auto vector_data_in_dat_file =\n        error_file.get_data<std::vector<std::vector<double>>>();\n    // [h5dat_get_data]\n    // [h5dat_get_subset]\n    const Matrix matrix_subset_data_in_dat_file =\n        error_file.get_data_subset({1, 3}, 1, 3);\n    const auto matrix_subset_data_in_dat_file2 =\n        error_file.get_data_subset<Matrix>({1, 3}, 1, 3);\n    const auto vector_subset_data_in_dat_file =\n        error_file.get_data_subset<std::vector<std::vector<double>>>({1, 3}, 1,\n                                                                     3);\n    // [h5dat_get_subset]\n    (void)matrix_data_in_dat_file;\n    (void)matrix_data_in_dat_file2;\n    (void)vector_data_in_dat_file;\n    (void)matrix_subset_data_in_dat_file;\n    (void)matrix_subset_data_in_dat_file2;\n    (void)vector_subset_data_in_dat_file;\n  }\n\n  check_get_data(error_file,\n                 {0.0, 0.1, 0.2, 0.3, 0.11, 0.4, 0.5, 0.6, 0.22, 0.55, 0.6, 0.8,\n                  0.33, 0.66, 0.77, 0.9},\n                 {4, 4});\n\n  check_get_data_subset(error_file, {0.4, 0.5, 0.55, 0.6}, {2, 2}, {1, 2}, 1,\n                        2);\n\n  check_get_data_subset(error_file, {0.4, 0.6, 0.55, 0.8, 0.66, 0.9}, {3, 2},\n                        {1, 3}, 1, 3);\n\n  check_get_data_subset(error_file, {0.0, 0.3, 0.11, 0.6}, {2, 2}, {0, 3}, 0,\n                        2);\n\n  check_get_data_subset(error_file, {}, {2, 0}, {}, 0, 2);\n\n  check_get_data_subset(error_file, {}, {0, 2}, {0, 3}, 0, 0);\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_dat_read() {\n  const std::string h5_file_name(\"Unit.IO.H5.DatRead.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  std::vector<std::string> legend{\"Time\", \"Error L2\", \"Error L1\", \"Error\"};\n  {\n    h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n    auto& error_file =\n        my_file.insert<h5::Dat>(\"/L2_errors\", legend, version_number);\n\n    error_file.append([]() {\n      Matrix result(3, 4);\n      result(0, 0) = 0.0;\n      result(0, 1) = 0.1;\n      result(0, 2) = 0.2;\n      result(0, 3) = 0.3;\n      result(1, 0) = 0.11;\n      result(1, 1) = 0.4;\n      result(1, 2) = 0.5;\n      result(1, 3) = 0.6;\n      result(2, 0) = 0.22;\n      result(2, 1) = 0.55;\n      result(2, 2) = 0.6;\n      result(2, 3) = 0.8;\n      return result;\n    }());\n    error_file.append(Matrix(0, 0, 0.0));\n    error_file.append(std::vector<double>{0.33, 0.66, 0.77, 0.90});\n  }\n  h5::H5File<h5::AccessType::ReadOnly> my_file(h5_file_name);\n  // No leading slash should also find the subfile, and a \".dat\" extension as\n  // well\n  const auto& error_file = my_file.get<h5::Dat>(\"L2_errors.dat\");\n  CHECK(error_file.subfile_path() == \"/L2_errors\");\n\n  // Check version info is correctly retrieved from Dat file\n  CHECK(error_file.get_version() == version_number);\n\n  // Check getting the header from Dat file\n  std::stringstream ss;\n  ss << \"# \";\n  auto build_info = info_from_build();\n  ss << std::regex_replace(build_info, std::regex{\"\\n\"}, \"\\n# \");\n  const auto& header = error_file.get_header();\n  CHECK(\"#\\n# File created on \"s ==\n        header.substr(0, header.find(\"File created on \") + 16));\n  CHECK(ss.str() == header.substr(header.find(\"# SpECTRE\")));\n\n  CHECK(error_file.get_legend() == legend);\n\n  // Check data is retrieved correctly from Dat file\n  std::array<hsize_t, 2> size_of_data{{4, 4}};\n  CHECK(error_file.get_dimensions() == size_of_data);\n\n  check_get_data(error_file,\n                 {0.0, 0.1, 0.2, 0.3, 0.11, 0.4, 0.5, 0.6, 0.22, 0.55, 0.6, 0.8,\n                  0.33, 0.66, 0.77, 0.9},\n                 {4, 4});\n\n  check_get_data_subset(error_file, {0.4, 0.5, 0.55, 0.6}, {2, 2}, {1, 2}, 1,\n                        2);\n\n  check_get_data_subset(error_file, {0.4, 0.6, 0.55, 0.8, 0.66, 0.9}, {3, 2},\n                        {1, 3}, 1, 3);\n\n  check_get_data_subset(error_file, {0.0, 0.3, 0.11, 0.6}, {2, 2}, {0, 3}, 0,\n                        2);\n\n  check_get_data_subset(error_file, {}, {2, 0}, {}, 0, 2);\n\n  check_get_data_subset(error_file, {}, {0, 2}, {0, 3}, 0, 0);\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.IO.H5.Dat\", \"[Unit][IO][H5]\") {\n  test_errors();\n  test_core_functionality();\n  test_dat_read();\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_EosTable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <hdf5.h>\n#include <memory>\n#include <regex>\n#include <sstream>\n#include <string>\n#include <typeinfo>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/Connectivity.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/EosTable.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Formaline.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test() {\n  const DataVector expected_pressure{\n      3.2731420000000011e-12, 3.1893989140625013e-12, 3.1999981000000014e-12,\n      1.2155360915010773e-9,  8.3016422052875601e-10, 2.1627258654874539e-9,\n      3.2099012875311601e-5,  2.4468017331204212e-4,  6.1349624337516350e-4,\n      2210.6053143615190,     2354.6115554098865,     2538.9119061115266,\n      52.861468710971884,     52.861468705720966,     52.861468710893689,\n      52.861692578286011,     52.861692577931898,     52.861692577582062,\n      52.861449400505300,     52.861440607424719,     52.861463344038555,\n      5535.2247866186935,     6793.9338434046485,     6544.9524934507153};\n  const DataVector expected_specific_internal_energy{\n      1.1372398000000006e-2,  3.5346163132812542e-3,  3.3070626000000045e-3,\n      -1.0169032790344480e-4, -7.7663669049909921e-3, -7.6041533773714091e-3,\n      1.2234741042710821e-4,  -2.9897660752421736e-3, 4.4546614168404601e-3,\n      0.77921157405081587,    0.86947269803694438,    1.0634124481653968,\n      193541104778.35413,     193541104733.22195,     193541104777.67715,\n      15626334.678079225,     15626334.678358778,     15626334.912313508,\n      1260.6482389767325,     1260.6475515928282,     1260.6487573789184,\n      1.1740182149625666,     -7.8404943805046665,    -6.7725747751703373};\n\n  const std::vector<std::string> expected_independent_variable_names{\n      \"number density\", \"temperature\", \"electron fraction\"};\n  const std::vector<std::array<double, 2>> expected_independent_variable_bounds{\n      {0.10000000000000001, 158.0}, {1.0e-12, 1.9}, {0.01, 0.6}};\n  const std::vector<size_t> expected_independent_variable_number_of_points{2, 4,\n                                                                           3};\n  const std::vector<bool> expected_independent_variable_uses_log_spacing{\n      true, true, false};\n  const bool expected_beta_equilibrium = false;\n\n  const std::string h5_file_name{\"Unit.IO.H5.EosTable.h5\"};\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  const auto check_table =\n      [&expected_independent_variable_names,\n       &expected_independent_variable_number_of_points,\n       &expected_specific_internal_energy, &expected_pressure,\n       &expected_beta_equilibrium,\n       &expected_independent_variable_uses_log_spacing,\n       &expected_independent_variable_bounds](const auto& eos_table) {\n        CHECK(eos_table.available_quantities() ==\n              std::vector<std::string>{\"pressure\", \"specific internal energy\"});\n        CHECK(eos_table.number_of_independent_variables() ==\n              expected_independent_variable_names.size());\n        CHECK(eos_table.independent_variable_names() ==\n              expected_independent_variable_names);\n        CHECK(eos_table.independent_variable_bounds() ==\n              expected_independent_variable_bounds);\n        CHECK(eos_table.independent_variable_number_of_points() ==\n              expected_independent_variable_number_of_points);\n        CHECK(eos_table.independent_variable_uses_log_spacing() ==\n              expected_independent_variable_uses_log_spacing);\n        CHECK(eos_table.beta_equilibrium() == expected_beta_equilibrium);\n        CHECK(eos_table.read_quantity(\"pressure\") == expected_pressure);\n        CHECK(eos_table.read_quantity(\"specific internal energy\") ==\n              expected_specific_internal_energy);\n        CHECK(eos_table.subfile_path() == \"/sfo\");\n      };\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> eos_file{h5_file_name};\n\n    auto& eos_table_written = eos_file.insert<h5::EosTable>(\n        \"/sfo\", expected_independent_variable_names,\n        expected_independent_variable_bounds,\n        expected_independent_variable_number_of_points,\n        expected_independent_variable_uses_log_spacing,\n        expected_beta_equilibrium, version_number);\n    eos_table_written.write_quantity(\"pressure\", expected_pressure);\n    eos_table_written.write_quantity(\"specific internal energy\",\n                                     expected_specific_internal_energy);\n    check_table(eos_table_written);\n    eos_file.close_current_object();\n\n    CHECK_THROWS_WITH(eos_file.get<h5::EosTable>(\n                          \"/sfo\", expected_independent_variable_names,\n                          expected_independent_variable_bounds,\n                          expected_independent_variable_number_of_points,\n                          expected_independent_variable_uses_log_spacing,\n                          expected_beta_equilibrium, version_number),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Opening an equation of state table with the \"\n                          \"constructor for writing a table, but the subfile \"));\n  }\n  {\n    h5::H5File<h5::AccessType::ReadOnly> eos_file{h5_file_name};\n    check_table(eos_file.get<h5::EosTable>(\"/sfo\"));\n    eos_file.close_current_object();\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.IO.H5.EosTable\", \"[Unit][IO][H5]\") { test(); }\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_H5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <hdf5.h>\n#include <memory>\n#include <regex>\n#include <sstream>\n#include <string>\n#include <typeinfo>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/Type.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Formaline.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// Test that we can read scalar, rank-1, rank-2, and rank-3 datasets\nnamespace {\nvoid test_types_equal() {\n  CHECK(h5::types_equal(h5::h5_type<double>(), h5::h5_type<double>()));\n  CHECK_FALSE(h5::types_equal(h5::h5_type<double>(), h5::h5_type<float>()));\n  CHECK_FALSE(h5::types_equal(h5::h5_type<char>(), h5::h5_type<float>()));\n  CHECK_FALSE(h5::types_equal(h5::h5_type<int>(), h5::h5_type<float>()));\n}\n\nvoid test_read_data() {\n  const std::string h5_file_name(\"Unit.IO.H5.ReadData.h5\");\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const hid_t file_id = H5Fcreate(h5_file_name.c_str(), h5::h5f_acc_trunc(),\n                                  h5::h5p_default(), h5::h5p_default());\n  h5::detail::OpenGroup my_group(file_id, \"ReadWrite\",\n                                 h5::AccessType::ReadWrite);\n  const hid_t group_id = my_group.id();\n\n  // Test writing rank1 datasets\n  {\n    const DataVector rank1_data{1.0, 2.0, 3.0};\n    const std::string dataset_name{\"rank1_dataset_datavector\"};\n    h5::write_data(group_id, DataVector{4.0, 5.0}, dataset_name);\n    CHECK_THROWS_WITH(h5::write_data(group_id, rank1_data, dataset_name),\n                      Catch::Matchers::ContainsSubstring(\"already exists\"));\n    h5::write_data(group_id, rank1_data, dataset_name, true);\n    const auto rank1_data_from_file =\n        h5::read_data<1, DataVector>(group_id, dataset_name);\n    CHECK(rank1_data_from_file == rank1_data);\n  }\n  const auto test_rank1_data = [&group_id](const auto& rank1_data,\n                                           const std::vector<size_t>& extents,\n                                           const std::string& dataset_name) {\n    h5::write_data(group_id, rank1_data, extents, dataset_name);\n    const auto rank1_data_from_file =\n        h5::read_data<1, std::decay_t<decltype(rank1_data)>>(group_id,\n                                                             dataset_name);\n    CHECK(rank1_data_from_file == rank1_data);\n  };\n  test_rank1_data(std::vector<double>{1.0, 2.0, 3.0}, std::vector<size_t>{3},\n                  \"rank1_dataset_vector_double\");\n  test_rank1_data(std::vector<float>{1.0, 2.0, 3.0}, std::vector<size_t>{3},\n                  \"rank1_dataset_vector_float\");\n\n  // Test writing and reading `std::vector`s from rank-0 to rank-3\n  using all_type_list =\n      tmpl::list<double, int, unsigned int, long, unsigned long, long long,\n                 unsigned long long>;\n\n  h5::write_data(group_id, std::vector<double>{4.0}, {},\n                 \"scalar_dataset\");\n  CHECK_THROWS_WITH((h5::write_data(group_id, std::vector<double>{1.0 / 3.0},\n                                    {}, \"scalar_dataset\")),\n                    Catch::Matchers::ContainsSubstring(\"already exists\"));\n  h5::write_data(group_id, std::vector<double>{1.0 / 3.0}, {},\n                 \"scalar_dataset\", true);\n  CHECK(h5::read_data<0, double>(group_id, \"scalar_dataset\") == 1.0 / 3.0);\n  h5::write_data(group_id, std::vector<float>{4.0}, {},\n                 \"scalar_dataset_float\");\n  CHECK_THROWS_WITH((h5::write_data(group_id, std::vector<float>{1.0f / 3.0f},\n                                    {}, \"scalar_dataset_float\")),\n                    Catch::Matchers::ContainsSubstring(\"already exists\"));\n  h5::write_data(group_id, std::vector<float>{1.0f / 3.0f}, {},\n                 \"scalar_dataset_float\", true);\n  CHECK(h5::read_data<0, float>(group_id, \"scalar_dataset_float\") ==\n        static_cast<float>(1.0 / 3.0));\n\n  tmpl::for_each<all_type_list>([&group_id](auto x) {\n    using DataType = typename decltype(x)::type;\n    const std::vector<DataType> rank1_data{1, 2, 3};\n    const std::vector<size_t> rank1_extents{3};\n    h5::write_data<DataType>(\n        group_id, rank1_data, rank1_extents,\n        \"rank1_dataset_\" + std::string(typeid(DataType).name()));\n\n    std::vector<DataType> rank1_data_from_file =\n        h5::read_data<1, std::vector<DataType>>(\n            group_id, \"rank1_dataset_\" + std::string(typeid(DataType).name()));\n    CHECK(rank1_data_from_file == std::vector<DataType>{1, 2, 3});\n  });\n\n  tmpl::for_each<all_type_list>([&group_id](auto x) {\n    using DataType = typename decltype(x)::type;\n    const std::vector<DataType> rank2_data{1, 2, 3, 4};\n    const std::vector<size_t> rank2_extents{2, 2};\n    h5::write_data<DataType>(\n        group_id, rank2_data, rank2_extents,\n        \"rank2_dataset_\" + std::string(typeid(DataType).name()));\n\n    boost::multi_array<DataType, 2> rank2_data_from_file =\n        h5::read_data<2, boost::multi_array<DataType, 2>>(\n            group_id, \"rank2_dataset_\" + std::string(typeid(DataType).name()));\n    boost::multi_array<DataType, 2> expected_rank2_data(boost::extents[2][2]);\n    expected_rank2_data[0][0] = static_cast<DataType>(1);\n    expected_rank2_data[0][1] = static_cast<DataType>(2);\n    expected_rank2_data[1][0] = static_cast<DataType>(3);\n    expected_rank2_data[1][1] = static_cast<DataType>(4);\n    CHECK(rank2_data_from_file == expected_rank2_data);\n  });\n\n  tmpl::for_each<all_type_list>([&group_id](auto x) {\n    using DataType = typename decltype(x)::type;\n    const std::vector<DataType> rank3_data{1, 2, 3, 4, 5, 6};\n    const std::vector<size_t> rank3_extents{1, 2, 3};\n    h5::write_data<DataType>(\n        group_id, rank3_data, rank3_extents,\n        \"rank3_dataset_\" + std::string(typeid(DataType).name()));\n\n    boost::multi_array<DataType, 3> rank3_data_from_file =\n        h5::read_data<3, boost::multi_array<DataType, 3>>(\n            group_id, \"rank3_dataset_\" + std::string(typeid(DataType).name()));\n    boost::multi_array<DataType, 3> expected_rank3_data(\n        boost::extents[1][2][3]);\n    expected_rank3_data[0][0][0] = static_cast<DataType>(1);\n    expected_rank3_data[0][0][1] = static_cast<DataType>(2);\n    expected_rank3_data[0][0][2] = static_cast<DataType>(3);\n    expected_rank3_data[0][1][0] = static_cast<DataType>(4);\n    expected_rank3_data[0][1][1] = static_cast<DataType>(5);\n    expected_rank3_data[0][1][2] = static_cast<DataType>(6);\n    CHECK(rank3_data_from_file == expected_rank3_data);\n  });\n\n  CHECK_H5(H5Fclose(file_id), \"Failed to close file: '\" << h5_file_name << \"'\");\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\n// Check that we can insert and open subfiles at the '/' level\nvoid test_check_if_object_exists() {\n  const std::string h5_file_name(\"Unit.IO.H5.check_if_object_exists.h5\");\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  {\n    h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n    my_file.insert<h5::Header>(\"/\");\n  }\n\n  // Reopen the file to check that the subfile '/' can be opened\n  h5::H5File<h5::AccessType::ReadWrite> reopened_file(h5_file_name, true);\n  reopened_file.get<h5::Header>(\"/\");\n  CHECK(file_system::check_if_file_exists(h5_file_name) == true);\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_contains_attribute_false() {\n  const std::string h5_file_name(\"Unit.IO.H5.contains_attribute_false.h5\");\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const hid_t file_id = H5Fcreate(h5_file_name.c_str(), h5::h5f_acc_trunc(),\n                                  h5::h5p_default(), h5::h5p_default());\n  CHECK_H5(file_id, \"Failed to open file: \" << h5_file_name);\n  CHECK_FALSE(h5::contains_attribute(file_id, \"/\", \"no_attr\"));\n  CHECK_H5(H5Fclose(file_id), \"Failed to close file: '\" << h5_file_name << \"'\");\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string file_name(\"Unit.IO.H5.read_data_error.h5\");\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n        const hid_t file_id = H5Fcreate(file_name.c_str(), h5::h5f_acc_trunc(),\n                                        h5::h5p_default(), h5::h5p_default());\n        CHECK_H5(file_id, \"Failed to open file: \" << file_name);\n        static_cast<void>(h5::read_data<1, DataVector>(file_id, \"no_dataset\"));\n        CHECK_H5(H5Fclose(file_id),\n                 \"Failed to close file: '\" << file_name << \"'\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed HDF5 operation: Failed to open dataset 'no_dataset'\"));\n  if (file_system::check_if_file_exists(\"Unit.IO.H5.read_data_error.h5\")) {\n    file_system::rm(\"Unit.IO.H5.read_data_error.h5\", true);\n  }\n}\n\nvoid test_list_files() {\n  const std::string h5_file_name(\"Unit.IO.H5.list_files.h5\");\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  // Insert a plethora of files to test listing with. Data doesn't matter\n  {\n    h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n    const std::vector<std::string> legend{{\"Fake\"}};\n    my_file.insert<h5::Dat>(\"/root_dat\", legend, 0);\n    my_file.close_current_object();\n    my_file.insert<h5::Dat>(\"/group0/next_dat\", legend, 0);\n    my_file.close_current_object();\n    my_file.insert<h5::Dat>(\"/group0/other_dat\", legend, 0);\n    my_file.close_current_object();\n    my_file.insert<h5::VolumeData>(\"/root_vol_1\", 0);\n    my_file.close_current_object();\n    my_file.insert<h5::VolumeData>(\"/root_vol_2\", 0);\n    my_file.close_current_object();\n    my_file.insert<h5::VolumeData>(\"/group0/sub_vol\", 0);\n  }\n\n  // Expected quantities. If ordering becomes an issue, use std::unordered_set\n  // instead\n  const std::vector<std::string> expected_all_files{\n      {\"/group0/next_dat.dat\", \"/group0/other_dat.dat\", \"/group0/sub_vol.vol\",\n       \"/root_dat.dat\", \"/root_vol_1.vol\", \"/root_vol_2.vol\", \"/src.tar.gz\"}};\n  const std::vector<std::string> expected_dat_files{\n      {\"/group0/next_dat.dat\", \"/group0/other_dat.dat\", \"/root_dat.dat\"}};\n  const std::vector<std::string> expected_vol_files{\n      {\"/group0/sub_vol.vol\", \"/root_vol_1.vol\", \"/root_vol_2.vol\"}};\n  const std::vector<std::string> expected_src_files{{\"/src.tar.gz\"}};\n\n  h5::H5File<h5::AccessType::ReadOnly> reopened_file(h5_file_name);\n  const auto all_files = reopened_file.all_files<>(\"/\");\n  const auto dat_files = reopened_file.all_files<h5::Dat>(\"/\");\n  const auto vol_files = reopened_file.all_files<h5::VolumeData>(\"/\");\n  const auto src_files = reopened_file.all_files<h5::SourceArchive>(\"/\");\n\n  CHECK(all_files == expected_all_files);\n  CHECK(dat_files == expected_dat_files);\n  CHECK(vol_files == expected_vol_files);\n  CHECK(src_files == expected_src_files);\n\n  const std::vector<std::string> expected_group0_dat_files{\n      \"/group0/next_dat.dat\", \"/group0/other_dat.dat\"};\n\n  const auto group0_dat_files = reopened_file.all_files<h5::Dat>(\"/group0\");\n\n  CHECK(expected_group0_dat_files == group0_dat_files);\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.IO.H5\", \"[Unit][IO][H5]\") {\n  test_types_equal();\n  test_read_data();\n  test_check_if_object_exists();\n  test_contains_attribute_false();\n  test_errors();\n  test_list_files();\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_H5.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\nfrom pathlib import Path\n\nimport h5py\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre import DataStructures\n\n\nclass TestIOH5File(unittest.TestCase):\n    # Test Fixtures\n    def setUp(self):\n        self.file_name = \"pythontest.h5\"\n        self.data_1 = [1.0, 3.5]\n        self.data_1_array = np.array(self.data_1)\n        self.data_2 = [3.0, 10.3]\n        self.data_2_array = np.array(self.data_2)\n        self.l_max = 2\n        self.bondi_variables = [\n            \"EthInertialRetardedTime\",\n            \"News\",\n            \"Psi0\",\n            \"Psi1\",\n            \"Psi2\",\n            \"Psi3\",\n            \"Psi4\",\n            \"Strain\",\n        ]\n        self.cce_data_1 = {\n            name: np.array([i] * 19)\n            for i, name in enumerate(self.bondi_variables)\n        }\n        self.cce_data_2 = {\n            name: np.array([i + 1] * 19)\n            for i, name in enumerate(self.bondi_variables)\n        }\n        if os.path.isfile(self.file_name):\n            os.remove(self.file_name)\n\n    def tearDown(self):\n        if os.path.isfile(self.file_name):\n            os.remove(self.file_name)\n\n    # Test whether an H5 file is created correctly,\n    def test_name(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            self.assertEqual(self.file_name, h5file.name())\n        # Also test with pathlib.Path\n        with spectre_h5.H5File(\n            file_name=Path(self.file_name), mode=\"a\"\n        ) as h5file:\n            self.assertEqual(self.file_name, h5file.name())\n\n    # Test whether a dat file can be added correctly\n    def test_insert_dat(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            datfile = h5file.insert_dat(\n                path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n            )\n            self.assertEqual(datfile.get_version(), 0)\n\n    # Test whether a cce file can be added correctly\n    def test_insert_cce(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            ccefile = h5file.insert_cce(path=\"/cce_data\", l_max=2, version=0)\n            self.assertEqual(ccefile.get_version(), 0)\n\n    # Test whether data can be added to the dat file correctly\n    def test_append_dat(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            datfile = h5file.insert_dat(\n                path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n            )\n            datfile.append(self.data_1)\n            outdata_array = np.asarray(datfile.get_data())\n            npt.assert_array_equal(outdata_array[0], self.data_1_array)\n\n    # Test whether data can be added to the cce file correctly\n    def test_append_cce(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            ccefile = h5file.insert_cce(\n                path=\"/cce_data\", l_max=self.l_max, version=0\n            )\n            ccefile.append(self.cce_data_1)\n            outdata_dict = ccefile.get_data()\n            for name in self.bondi_variables:\n                self.assertIn(name, outdata_dict)\n                npt.assert_array_equal(\n                    np.asarray(outdata_dict[name])[0], self.cce_data_1[name]\n                )\n                outdata = np.asarray(ccefile.get_data(name))[0]\n                npt.assert_array_equal(outdata, self.cce_data_1[name])\n\n    # More complicated test case for getting data subsets and dimensions\n    def test_get_data_subset_dat(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            datfile = h5file.insert_dat(\n                path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n            )\n            datfile.append(self.data_1)\n            datfile.append(self.data_2)\n            outdata_array = datfile.get_data_subset(\n                columns=[1], first_row=0, num_rows=2\n            )\n            npt.assert_array_equal(\n                outdata_array, np.array([self.data_1[1:2], self.data_2[1:2]])\n            )\n            self.assertEqual(datfile.get_dimensions()[0], 2)\n\n    # More complicated test case for getting cce subsets and dimensions\n    def test_get_data_subset_cce(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            ccefile = h5file.insert_cce(\n                path=\"/cce_data\", l_max=self.l_max, version=0\n            )\n            ccefile.append(self.cce_data_1)\n            ccefile.append(self.cce_data_2)\n            outdata_dict = ccefile.get_data_subset(\n                ell=[0, 1], first_row=0, num_rows=2\n            )\n            for name in self.bondi_variables:\n                self.assertIn(name, outdata_dict)\n                expected = np.array(\n                    [self.cce_data_1[name][:9], self.cce_data_2[name][:9]],\n                )\n                npt.assert_array_equal(outdata_dict[name], expected)\n                outdata = np.asarray(\n                    ccefile.get_data_subset(\n                        name, ell=[0, 1], first_row=0, num_rows=2\n                    )\n                )\n                npt.assert_array_equal(outdata, expected)\n                self.assertEqual(ccefile.get_dimensions(name)[0], 2)\n\n    # Getting Attributes\n    def test_get_legend(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            datfile = h5file.insert_dat(\n                path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n            )\n            self.assertEqual(datfile.get_legend(), [\"Time\", \"Value\"])\n            self.assertEqual(datfile.get_version(), 0)\n            h5file.close_current_object()\n            ccefile = h5file.insert_cce(\n                path=\"/cce_data\", l_max=self.l_max, version=0\n            )\n            self.assertEqual(\n                ccefile.get_legend(),\n                [\n                    \"time\",\n                    \"Real Y_0,0\",\n                    \"Imag Y_0,0\",\n                    \"Real Y_1,-1\",\n                    \"Imag Y_1,-1\",\n                    \"Real Y_1,0\",\n                    \"Imag Y_1,0\",\n                    \"Real Y_1,1\",\n                    \"Imag Y_1,1\",\n                    \"Real Y_2,-2\",\n                    \"Imag Y_2,-2\",\n                    \"Real Y_2,-1\",\n                    \"Imag Y_2,-1\",\n                    \"Real Y_2,0\",\n                    \"Imag Y_2,0\",\n                    \"Real Y_2,1\",\n                    \"Imag Y_2,1\",\n                    \"Real Y_2,2\",\n                    \"Imag Y_2,2\",\n                ],\n            )\n            self.assertEqual(ccefile.get_version(), 0)\n\n    # The header is not universal, just checking the part that is predictable\n    def test_get_header(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            datfile = h5file.insert_dat(\n                path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n            )\n            self.assertEqual(datfile.get_header()[0:16], \"#\\n# File created\")\n            h5file.close_current_object()\n            ccefile = h5file.insert_cce(\n                path=\"/cce_data\", l_max=self.l_max, version=0\n            )\n            self.assertEqual(ccefile.get_header()[0:16], \"#\\n# File created\")\n\n    def test_all_files(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            h5file.insert_vol(path=\"/root_vol_2\", version=0)\n            h5file.close_current_object()\n            h5file.insert_vol(path=\"/root_vol_1\", version=0)\n            h5file.close_current_object()\n            h5file.insert_vol(path=\"/group0/sub_vol\", version=0)\n            h5file.close_current_object()\n            legend = [\"Fake\"]\n            h5file.insert_dat(path=\"/root_dat\", legend=legend, version=0)\n            h5file.close_current_object()\n            h5file.insert_cce(path=\"/root_cce\", l_max=self.l_max, version=0)\n            h5file.close_current_object()\n            h5file.insert_dat(\n                path=\"/group0/sub_dat_1\", legend=legend, version=0\n            )\n            h5file.close_current_object()\n            h5file.insert_dat(\n                path=\"/group0/sub_dat_2\", legend=legend, version=0\n            )\n            h5file.close_current_object()\n            h5file.insert_cce(\n                path=\"/group0/sub_cce_1\", l_max=self.l_max, version=0\n            )\n            h5file.close_current_object()\n            h5file.insert_cce(\n                path=\"/group0/sub_cce_2\", l_max=self.l_max, version=0\n            )\n            h5file.close_current_object()\n\n            expected_all_files = [\n                \"/group0/sub_cce_1.cce\",\n                \"/group0/sub_cce_2.cce\",\n                \"/group0/sub_dat_1.dat\",\n                \"/group0/sub_dat_2.dat\",\n                \"/group0/sub_vol.vol\",\n                \"/root_cce.cce\",\n                \"/root_dat.dat\",\n                \"/root_vol_1.vol\",\n                \"/root_vol_2.vol\",\n                \"/src.tar.gz\",\n            ]\n            expected_dat_files = [\n                \"/group0/sub_dat_1.dat\",\n                \"/group0/sub_dat_2.dat\",\n                \"/root_dat.dat\",\n            ]\n            expected_cce_files = [\n                \"/group0/sub_cce_1.cce\",\n                \"/group0/sub_cce_2.cce\",\n                \"/root_cce.cce\",\n            ]\n            expected_vol_files = [\n                \"/group0/sub_vol.vol\",\n                \"/root_vol_1.vol\",\n                \"/root_vol_2.vol\",\n            ]\n\n            all_files = h5file.all_files()\n            dat_files = h5file.all_dat_files()\n            cce_files = h5file.all_cce_files()\n            vol_files = h5file.all_vol_files()\n\n            self.assertEqual(all_files, expected_all_files)\n            self.assertEqual(dat_files, expected_dat_files)\n            self.assertEqual(cce_files, expected_cce_files)\n            self.assertEqual(vol_files, expected_vol_files)\n\n    def test_groups(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            h5file.insert_dat(\n                path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n            )\n            h5file.close_current_object()\n            h5file.insert_dat(\n                path=\"/element_position\", legend=[\"x\", \"y\", \"z\"], version=0\n            )\n            h5file.close_current_object()\n            h5file.insert_dat(\n                path=\"/element_size\", legend=[\"Time\", \"Size\"], version=0\n            )\n            h5file.close_current_object()\n            h5file.insert_cce(path=\"/cce_1\", l_max=self.l_max, version=0)\n            h5file.close_current_object()\n            h5file.insert_cce(path=\"/cce_2\", l_max=self.l_max, version=0)\n            h5file.close_current_object()\n            h5file.insert_cce(path=\"/cce_3\", l_max=self.l_max, version=0)\n            h5file.close_current_object()\n            groups_spec = [\n                \"cce_1.cce\",\n                \"cce_2.cce\",\n                \"cce_3.cce\",\n                \"element_data.dat\",\n                \"element_position.dat\",\n                \"element_size.dat\",\n                \"src.tar.gz\",\n            ]\n            for group_name in groups_spec:\n                self.assertTrue(group_name in h5file.groups())\n\n    def test_exceptions(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            # create existing file\n            with self.assertRaisesRegex(\n                RuntimeError, \"File 'pythontest.h5' already exists and\"\n            ):\n                spectre_h5.H5File(file_name=self.file_name, mode=\"w-\")\n\n            h5file.insert_dat(\n                path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n            )\n\n            # insert existing data file\n            with self.assertRaisesRegex(\n                RuntimeError, \"/element_data already open. Cannot insert object\"\n            ):\n                h5file.insert_dat(\n                    path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n                )\n            h5file.close_current_object()\n\n            # grab non-existing data file\n            with self.assertRaisesRegex(\n                RuntimeError,\n                \"Cannot open the object '/element_dat.vol' because it\",\n            ):\n                h5file.get_vol(\"/element_dat\")\n\n            h5file.close_current_object()\n            h5file.insert_cce(path=\"/cce_data\", l_max=self.l_max, version=0)\n\n            # insert existing cce file\n            with self.assertRaisesRegex(\n                RuntimeError, \"/cce_data already open. Cannot insert object\"\n            ):\n                h5file.insert_cce(path=\"/cce_data\", l_max=self.l_max, version=0)\n            h5file.close_current_object()\n\n            # grab non-existing cce file\n            with self.assertRaisesRegex(\n                RuntimeError,\n                \"Cannot open the object '/cce.cce' because it\",\n            ):\n                h5file.get_cce(\"/cce\", self.l_max)\n\n            h5file.close_current_object()\n            h5file.insert_vol(path=\"/volume_data\", version=0)\n\n            # insert existing volume data file\n            with self.assertRaisesRegex(\n                RuntimeError, \"Object /volume_data already open. Cannot\"\n            ):\n                h5file.insert_vol(path=\"/volume_data\", version=0)\n            h5file.close_current_object()\n\n            # grab non-existing volume data file\n            with self.assertRaisesRegex(\n                RuntimeError,\n                \"Cannot open the object '/volume_dat.vol' because it\",\n            ):\n                h5file.get_vol(\"/volume_dat\")\n\n    def test_input_source(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            self.assertEqual(h5file.input_source(), \"\")\n\n    def test_context_manager(self):\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as f:\n            self.assertEqual(f.name(), self.file_name)\n\n    def test_simultaneous_access(self):\n        # Create the file and close it\n        with spectre_h5.H5File(file_name=self.file_name, mode=\"a\") as h5file:\n            datfile = h5file.insert_dat(\n                path=\"/element_data\", legend=[\"Time\", \"Value\"], version=0\n            )\n            datfile.append(self.data_1)\n\n        # Read with bindings and h5py simultaneously\n        h5file2 = spectre_h5.H5File(file_name=self.file_name, mode=\"r\")\n        datfile = h5file2.get_dat(\"/element_data\")\n        npt.assert_array_equal(np.array(datfile.get_data())[0], self.data_1)\n        h5file3 = h5py.File(self.file_name, \"r\", locking=False)\n        npt.assert_array_equal(\n            np.array(h5file3[\"element_data.dat\"])[0], self.data_1\n        )\n        h5file2.close()\n        h5file3.close()\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_H5File.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <cstdint>\n#include <hdf5.h>\n#include <memory>\n#include <regex>\n#include <sstream>\n#include <string>\n#include <typeinfo>\n#include <utility>\n#include <vector>\n\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Formaline.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace {\nvoid test_access_type() {\n  CHECK(get_output(h5::AccessType::ReadOnly) == \"ReadOnly\");\n  CHECK(get_output(h5::AccessType::ReadWrite) == \"ReadWrite\");\n}\n\nvoid test_core_functionality() {\n  const std::string h5_file_name(\"Unit.IO.H5.File.h5\");\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  // [h5file_readwrite_get_header]\n  std::string input_source_yaml{\"TestOption: 4\\n\\nOtherOption: 5\\n\\n\"};\n  // Add a giant set of comments to the h5 file, to test that we can\n  // read and write very long yaml files to H5 attributes\n  input_source_yaml += \"# \"s;\n  for (size_t i = 0; i < 1000000; ++i) {\n    input_source_yaml += \"abcdefghij\"s;\n    // Output lines < 80 chars, as in an actual yaml input file\n    if (i % 7 == 0) {\n      input_source_yaml += \"\\n# \"s;\n    }\n  }\n  input_source_yaml += \"\\n\\n\"s;\n\n  h5::H5File<h5::AccessType::ReadWrite> my_file0(h5_file_name, false,\n                                                 input_source_yaml);\n  // Check that the header was written correctly\n  const std::string header = my_file0.get<h5::Header>(\"/header\").get_header();\n  // [h5file_readwrite_get_header]\n  my_file0.close_current_object();\n\n  const auto check_header = [&h5_file_name,\n                             &input_source_yaml](const auto& my_file) {\n    const auto& header_obj = my_file.template get<h5::Header>(\"/header\");\n    CHECK(header_obj.subfile_path() == \"/header\");\n    // Check that the header was written correctly\n    const std::string& my_header = header_obj.get_header();\n\n    CHECK(my_file.name() == h5_file_name);\n\n    // Check that the subgroups were found correctly\n    std::vector<std::string> h5_file_groups{\"src.tar.gz\"};\n    CHECK(my_file.groups() == h5_file_groups);\n\n    CHECK(\"#\\n# File created on \"s ==\n          my_header.substr(0, my_header.find(\"File created on \") + 16));\n    CHECK(my_header.substr(my_header.find(\"# SpECTRE Build Information:\")) ==\n          std::string{MakeString{}\n                      << \"# \"\n                      << std::regex_replace(info_from_build(), std::regex{\"\\n\"},\n                                            \"\\n# \")});\n    CHECK(header_obj.get_env_variables() ==\n          formaline::get_environment_variables());\n    CHECK(header_obj.get_build_info() == formaline::get_build_info());\n\n    CHECK(my_file.input_source() == input_source_yaml);\n  };\n  check_header(my_file0);\n  my_file0.close_current_object();\n  check_header(h5::H5File<h5::AccessType::ReadOnly>(h5_file_name));\n  my_file0.close_current_object();\n\n  const auto check_source_archive = [](const auto& my_file) {\n    const std::vector<char> archive =\n        my_file.template get<h5::SourceArchive>(\"/src\").get_archive();\n    CHECK(archive == formaline::get_archive());\n  };\n  check_source_archive(my_file0);\n  my_file0.close_current_object();\n  check_source_archive(h5::H5File<h5::AccessType::ReadOnly>(h5_file_name));\n  my_file0.close_current_object();\n\n  my_file0.close();\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_file_move() {\n  const std::string h5_file_name(\"Unit.IO.H5.FileMove.h5\");\n  const std::string h5_file_name2(\"Unit.IO.H5.FileMove2.h5\");\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  if (file_system::check_if_file_exists(h5_file_name2)) {\n    file_system::rm(h5_file_name2, true);\n  }\n\n  auto my_file =\n      std::make_unique<h5::H5File<h5::AccessType::ReadWrite>>(h5_file_name);\n\n  auto my_file2 = std::make_unique<h5::H5File<h5::AccessType::ReadWrite>>(\n      std::move(*my_file));\n  my_file.reset();\n  CHECK(my_file == nullptr);\n\n  h5::H5File<h5::AccessType::ReadWrite> my_file3(h5_file_name2);\n  my_file3 = std::move(*my_file2);\n  my_file2.reset();\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  if (file_system::check_if_file_exists(h5_file_name2)) {\n    file_system::rm(h5_file_name2, true);\n  }\n}\n\nvoid test_error_messages() {\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string file_name = \"./Unit.IO.H5.FileErrorObjectNotExist.h5\";\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        };\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        my_file.get<h5::Header>(\"/Dummy\").get_header();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot open the object '/Dummy.hdr' because it does not exist.\"));\n  if (file_system::check_if_file_exists(\n          \"./Unit.IO.H5.FileErrorObjectNotExist.h5\")) {\n    file_system::rm(\"./Unit.IO.H5.FileErrorObjectNotExist.h5\", true);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string file_name = \"./Unit.IO.H5.FileErrorNotH5.h5ab\";\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n        h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"All HDF5 file names must end in '.h5'. The path and file name \"\n          \"'./Unit.IO.H5.FileErrorNotH5.h5ab' does not satisfy this\"));\n  if (file_system::check_if_file_exists(\"./Unit.IO.H5.FileErrorNotH5.h5ab\")) {\n    file_system::rm(\"./Unit.IO.H5.FileErrorNotH5.h5ab\", true);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string file_name = \"./Unit.IO.H5.FileErrorFileNotExist.h5\";\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n        h5::H5File<h5::AccessType::ReadOnly> my_file(file_name);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Trying to open the file './Unit.IO.H5.FileErrorFileNotExist.h5'\"));\n  if (file_system::check_if_file_exists(\n          \"./Unit.IO.H5.FileErrorFileNotExist.h5\")) {\n    file_system::rm(\"./Unit.IO.H5.FileErrorFileNotExist.h5\", true);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string file_name =\n            \"./Unit.IO.H5.FileErrorCannotAppendReadOnly.h5\";\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n        { h5::H5File<h5::AccessType::ReadWrite> my_file(file_name); }\n        h5::H5File<h5::AccessType::ReadOnly> my_file(file_name, true);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot append to a file opened in read-only mode. File name is: \"\n          \"./Unit.IO.H5.FileErrorCannotAppendReadOnly.h5\"));\n  if (file_system::check_if_file_exists(\n          \"./Unit.IO.H5.FileErrorCannotAppendReadOnly.h5\")) {\n    file_system::rm(\"./Unit.IO.H5.FileErrorCannotAppendReadOnly.h5\", true);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string file_name = \"./Unit.IO.H5.FileErrorExists.h5\";\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n        // Need to close file opened by my_file otherwise we get the error\n        // pure virtual method called\n        // terminate called recursively\n        {\n          // [h5file_readwrite_file]\n          h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n          // [h5file_readwrite_file]\n        }\n        h5::H5File<h5::AccessType::ReadWrite> my_file_2(file_name);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"File './Unit.IO.H5.FileErrorExists.h5' already exists and we are \"\n          \"not allowed to append. To reduce the risk of accidental deletion \"\n          \"you must explicitly delete the file first using the file_system \"\n          \"library in SpECTRE or through your shell.\"));\n  if (file_system::check_if_file_exists(\"./Unit.IO.H5.FileErrorExists.h5\")) {\n    file_system::rm(\"./Unit.IO.H5.FileErrorExists.h5\", true);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string file_name =\n            \"./Unit.IO.H5.FileErrorObjectNotExistConst.h5\";\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n        const h5::H5File<h5::AccessType::ReadWrite> my_file(file_name);\n        my_file.get<h5::Header>(\"/Dummy\").get_header();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot open the object '/Dummy.hdr' because it does not exist.\"));\n  if (file_system::check_if_file_exists(\n          \"./Unit.IO.H5.FileErrorObjectNotExistConst.h5\")) {\n    file_system::rm(\"./Unit.IO.H5.FileErrorObjectNotExistConst.h5\", true);\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.IO.H5.File\", \"[Unit][IO][H5]\") {\n  test_access_type();\n  test_core_functionality();\n  test_error_messages();\n  test_file_move();\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_OpenGroup.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <hdf5.h>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace {\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string file_name(\"Unit.IO.H5.OpenGroup_read_access.h5\");\n        if (file_system::check_if_file_exists(file_name)) {\n          file_system::rm(file_name, true);\n        }\n\n        const hid_t file_id = H5Fcreate(file_name.c_str(), h5::h5f_acc_trunc(),\n                                        h5::h5p_default(), h5::h5p_default());\n        CHECK_H5(file_id, \"Failed to open file: \" << file_name);\n        {\n          h5::detail::OpenGroup group(file_id, \"/group\",\n                                      h5::AccessType::ReadOnly);\n        }\n        CHECK_H5(H5Fclose(file_id),\n                 \"Failed to close file: '\" << file_name << \"'\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot create group 'group' in path: /group because the \"\n          \"access is ReadOnly\"));\n  if (file_system::check_if_file_exists(\n          \"Unit.IO.H5.OpenGroup_read_access.h5\")) {\n    file_system::rm(\"Unit.IO.H5.OpenGroup_read_access.h5\", true);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        h5::detail::OpenGroup group(-1, \"/group\", h5::AccessType::ReadWrite);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to open the group 'group' because the file_id passed in is \"\n          \"invalid, or because the group_id inside the OpenGroup constructor \"\n          \"got corrupted. It is most likely that the file_id is invalid.\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.IO.H5.OpenGroupMove\", \"[Unit][IO][H5]\") {\n  const std::string file_name(\"Unit.IO.H5.OpenGroupMove.h5\");\n  const std::string file_name2(\"Unit.IO.H5.OpenGroupMove2.h5\");\n\n  {\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n    const hid_t file_id = H5Fcreate(file_name.c_str(), h5::h5f_acc_trunc(),\n                                    h5::h5p_default(), h5::h5p_default());\n    CHECK_H5(file_id, \"Failed to open file: \" << file_name);\n    auto group = std::make_unique<h5::detail::OpenGroup>(\n        file_id, \"/group/group2/group3\", h5::AccessType::ReadWrite);\n    h5::detail::OpenGroup group2(std::move(*group));\n    group.reset();\n    CHECK(group == nullptr);\n    CHECK_H5(H5Fclose(file_id), \"Failed to close file: '\" << file_name << \"'\");\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n  }\n\n  {\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n    if (file_system::check_if_file_exists(file_name2)) {\n      file_system::rm(file_name2, true);\n    }\n    const hid_t file_id = H5Fcreate(file_name.c_str(), h5::h5f_acc_trunc(),\n                                    h5::h5p_default(), h5::h5p_default());\n    CHECK_H5(file_id, \"Failed to open file: \" << file_name);\n    const hid_t file_id2 = H5Fcreate(file_name2.c_str(), h5::h5f_acc_trunc(),\n                                     h5::h5p_default(), h5::h5p_default());\n    CHECK_H5(file_id2, \"Failed to open file: \" << file_name);\n    {\n      h5::detail::OpenGroup group(file_id, \"/group/group2/group3\",\n                                  h5::AccessType::ReadWrite);\n      h5::detail::OpenGroup group2(file_id2, \"/group/group2/group3\",\n                                   h5::AccessType::ReadWrite);\n      group2 = std::move(group);\n      h5::detail::OpenGroup group3(file_id2, \"/group/group2/group3\",\n                                   h5::AccessType::ReadWrite);\n    }\n    CHECK_H5(H5Fclose(file_id), \"Failed to close file: '\" << file_name << \"'\");\n    CHECK_H5(H5Fclose(file_id2),\n             \"Failed to close file: '\" << file_name2 << \"'\");\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n    if (file_system::check_if_file_exists(file_name2)) {\n      file_system::rm(file_name2, true);\n    }\n  }\n  {\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n    const hid_t file_id = H5Fcreate(file_name.c_str(), h5::h5f_acc_trunc(),\n                                    h5::h5p_default(), h5::h5p_default());\n    h5::detail::OpenGroup group(file_id, \"/\", h5::AccessType::ReadWrite);\n    CHECK(group.group_path_with_trailing_slash() == \"/\");\n    h5::detail::OpenGroup group2(file_id, \"/group/group2/group3\",\n                                 h5::AccessType::ReadWrite);\n    CHECK(group2.group_path_with_trailing_slash() == \"/group/group2/group3/\");\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n  }\n\n  test_errors();\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_StellarCollapseEos.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/StellarCollapseEos.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n\nnamespace {\n\n// For 1D arrays the numbers (in, for example, logrho_expected) are the first\n// and second numbers in the corresponding dataset of the HDF5 file.\n// For non-zero 3D arrays with no repeating numbers they are the first 2x2x2\n// block (all elements with indices [0][0][0] -> [1][1][1]) in the HDF5 file.\n// For 3D arrays with every element equal to zero (or with repeating numbers),\n// they are random numbers in the range [-10.0, 10.0]\nvoid test_tabulated(const std::string& file_path,\n                    const std::string& subgroup_path) {\n  h5::H5File<h5::AccessType::ReadOnly> sample_file(file_path);\n  const auto& sample_data =\n      sample_file.get<h5::StellarCollapseEos>(subgroup_path);\n  CHECK(sample_data.subfile_path() == subgroup_path);\n\n  // The group /sample_data has the additional dataset \"test_data\" which\n  // is read to verify that we are not in \"/\"\n  if (subgroup_path != \"/\") {\n    CHECK(sample_data.get_scalar_dataset<double>(\"test_data\") == 317);\n  }\n\n  CHECK(sample_data.get_scalar_dataset<double>(\"energy_shift\") == 317);\n  CHECK(sample_data.get_scalar_dataset<int>(\"have_rel_cs2\") == 1);\n  CHECK(sample_data.get_scalar_dataset<int>(\"pointsrho\") == 391);\n  CHECK(sample_data.get_scalar_dataset<int>(\"pointstemp\") == 163);\n  CHECK(sample_data.get_scalar_dataset<int>(\"pointsye\") == 66);\n\n  const std::vector<double> logrho_expected = {3.0239960056064277,\n                                               3.0573293389397609};\n  const auto logrho_from_file = sample_data.get_rank1_dataset(\"logrho\");\n  CHECK(logrho_from_file == logrho_expected);\n\n  const std::vector<double> logtemp_expected = {-3.0, -2.9666666666666668};\n  const auto logtemp_from_file = sample_data.get_rank1_dataset(\"logtemp\");\n  CHECK(logtemp_from_file == logtemp_expected);\n\n  const std::vector<double> ye_expected = {0.005, 0.015};\n  const auto ye_from_file = sample_data.get_rank1_dataset(\"ye\");\n  CHECK(ye_from_file == ye_expected);\n\n  boost::multi_array<double, 3> Abar_expected(boost::extents[2][2][2]);\n  Abar_expected[0][0][0] = 7.9795458306812677;\n  Abar_expected[0][0][1] = -0.3599944438171061;\n  Abar_expected[0][1][0] = 1.9845567482330431;\n  Abar_expected[0][1][1] = -7.3785193097452417;\n  Abar_expected[1][0][0] = -0.72424107655448644;\n  Abar_expected[1][0][1] = -5.1501202601984524;\n  Abar_expected[1][1][0] = 1.1758669985969643;\n  Abar_expected[1][1][1] = 3.0731761169814753;\n  CHECK(sample_data.get_rank3_dataset(\"Abar\") == Abar_expected);\n\n  boost::multi_array<double, 3> Albar_expected(boost::extents[2][2][2]);\n  Albar_expected[0][0][0] = 7.5184888339990898;\n  Albar_expected[0][0][1] = -0.94214490529767225;\n  Albar_expected[0][1][0] = -5.4890512683931973;\n  Albar_expected[0][1][1] = 7.1321598088866125;\n  Albar_expected[1][0][0] = 7.4871335289327519;\n  Albar_expected[1][0][1] = 4.872583973086309;\n  Albar_expected[1][1][0] = -0.11058300302998703;\n  Albar_expected[1][1][1] = -0.42315478232475989;\n  CHECK(sample_data.get_rank3_dataset(\"Albar\") == Albar_expected);\n\n  boost::multi_array<double, 3> Xa_expected(boost::extents[2][2][2]);\n  Xa_expected[0][0][0] = 0.89395326431166033;\n  Xa_expected[0][0][1] = 5.2981489245483004;\n  Xa_expected[0][1][0] = -8.2187962228781259;\n  Xa_expected[0][1][1] = 9.1349240360021753;\n  Xa_expected[1][0][0] = 6.8179106625235164;\n  Xa_expected[1][0][1] = -9.0687734224053429;\n  Xa_expected[1][1][0] = -0.026251730557362407;\n  Xa_expected[1][1][1] = -6.9882245067320836;\n  CHECK(sample_data.get_rank3_dataset(\"Xa\") == Xa_expected);\n\n  boost::multi_array<double, 3> Xh_expected(boost::extents[2][2][2]);\n  Xh_expected[0][0][0] = 0.015416666479933495;\n  Xh_expected[0][0][1] = 0.015416666761171319;\n  Xh_expected[0][1][0] = 0.01541666668806504;\n  Xh_expected[0][1][1] = 0.015416666496697482;\n  Xh_expected[1][0][0] = 0.046249999646911247;\n  Xh_expected[1][0][1] = 0.046249999756318716;\n  Xh_expected[1][1][0] = 0.046250000104935601;\n  Xh_expected[1][1][1] = 0.046250000039872653;\n  CHECK(sample_data.get_rank3_dataset(\"Xh\") == Xh_expected);\n\n  boost::multi_array<double, 3> Xl_expected(boost::extents[2][2][2]);\n  Xl_expected[0][0][0] = 7.3034402855828588;\n  Xl_expected[0][0][1] = 9.0348910681793164;\n  Xl_expected[0][1][0] = 7.535836352018535;\n  Xl_expected[0][1][1] = 1.9984514947941445;\n  Xl_expected[1][0][0] = 8.4566220303429489;\n  Xl_expected[1][0][1] = 4.4478998287751121;\n  Xl_expected[1][1][0] = 2.1875936108101861;\n  Xl_expected[1][1][1] = 6.9377000841409249;\n  CHECK(sample_data.get_rank3_dataset(\"Xl\") == Xl_expected);\n\n  boost::multi_array<double, 3> Xn_expected(boost::extents[2][2][2]);\n  Xn_expected[0][0][0] = 0.98458333330164904;\n  Xn_expected[0][0][1] = 0.98458333334956705;\n  Xn_expected[0][1][0] = 0.98458333333767578;\n  Xn_expected[0][1][1] = 0.98458333328657666;\n  Xn_expected[1][0][0] = 0.95375000000630239;\n  Xn_expected[1][0][1] = 0.95375000001765753;\n  Xn_expected[1][1][0] = 0.95375000002310728;\n  Xn_expected[1][1][1] = 0.95375000003230248;\n  CHECK(sample_data.get_rank3_dataset(\"Xn\") == Xn_expected);\n\n  boost::multi_array<double, 3> Xp_expected(boost::extents[2][2][2]);\n  Xp_expected[0][0][0] = -3.796313749453379;\n  Xp_expected[0][0][1] = -6.1582001413425598;\n  Xp_expected[0][1][0] = -6.8187240867986798;\n  Xp_expected[0][1][1] = -2.3102245214369166;\n  Xp_expected[1][0][0] = 3.1004045095839672;\n  Xp_expected[1][0][1] = 8.603539081758413;\n  Xp_expected[1][1][0] = 9.2598581731307092;\n  Xp_expected[1][1][1] = -5.5441516922376906;\n  CHECK(sample_data.get_rank3_dataset(\"Xp\") == Xp_expected);\n\n  boost::multi_array<double, 3> Zbar_expected(boost::extents[2][2][2]);\n  Zbar_expected[0][0][0] = 4.559131217700795;\n  Zbar_expected[0][0][1] = -4.2397342651521726;\n  Zbar_expected[0][1][0] = 2.393481280087542;\n  Zbar_expected[0][1][1] = -5.1014197051952941;\n  Zbar_expected[1][0][0] = -1.4240946022305412;\n  Zbar_expected[1][0][1] = 7.27848817794737;\n  Zbar_expected[1][1][0] = 9.6764051264858288;\n  Zbar_expected[1][1][1] = -6.0292384378110402;\n  CHECK(sample_data.get_rank3_dataset(\"Zbar\") == Zbar_expected);\n\n  boost::multi_array<double, 3> Zlbar_expected(boost::extents[2][2][2]);\n  Zlbar_expected[0][0][0] = 0.16485165186190187;\n  Zlbar_expected[0][0][1] = 6.7850358862748159;\n  Zlbar_expected[0][1][0] = 0.84432527731156881;\n  Zlbar_expected[0][1][1] = 8.5313505248650365;\n  Zlbar_expected[1][0][0] = 2.5371093297378415;\n  Zlbar_expected[1][0][1] = -8.0091698604208634;\n  Zlbar_expected[1][1][0] = 0.081681565040991444;\n  Zlbar_expected[1][1][1] = 6.6878158037490429;\n  CHECK(sample_data.get_rank3_dataset(\"Zlbar\") == Zlbar_expected);\n\n  boost::multi_array<double, 3> cs2_expected(boost::extents[2][2][2]);\n  cs2_expected[0][0][0] = 1577678648549693.8;\n  cs2_expected[0][0][1] = 1577667221164363.8;\n  cs2_expected[0][1][0] = 1703576033357332.5;\n  cs2_expected[0][1][1] = 1703564429793273.2;\n  cs2_expected[1][0][0] = 1543882126488769.0;\n  cs2_expected[1][0][1] = 1543838596546058.0;\n  cs2_expected[1][1][0] = 1667202534427290.2;\n  cs2_expected[1][1][1] = 1667158615466813.5;\n  CHECK(sample_data.get_rank3_dataset(\"cs2\") == cs2_expected);\n\n  boost::multi_array<double, 3> dedt_expected(boost::extents[2][2][2]);\n  dedt_expected[0][0][0] = 1.4207387553108902e+18;\n  dedt_expected[0][0][1] = 1.4206993403324593e+18;\n  dedt_expected[0][1][0] = 1.4208772178581018e+18;\n  dedt_expected[0][1][1] = 1.4208282620381565e+18;\n  dedt_expected[1][0][0] = 1.3914302855474371e+18;\n  dedt_expected[1][0][1] = 1.3913842153750838e+18;\n  dedt_expected[1][1][0] = 1.3915839621991084e+18;\n  dedt_expected[1][1][1] = 1.3915283697927603e+18;\n  CHECK(sample_data.get_rank3_dataset(\"dedt\") == dedt_expected);\n\n  boost::multi_array<double, 3> dpderho_expected(boost::extents[2][2][2]);\n  dpderho_expected[0][0][0] = 704.39241942245394;\n  dpderho_expected[0][0][1] = 760.59561843311644;\n  dpderho_expected[0][1][0] = 704.35793122929726;\n  dpderho_expected[0][1][1] = 760.56090764534656;\n  dpderho_expected[1][0][0] = 704.35394804365501;\n  dpderho_expected[1][0][1] = 760.55427457755161;\n  dpderho_expected[1][1][0] = 704.3156360818092;\n  dpderho_expected[1][1][1] = 760.5158477148301;\n  CHECK(sample_data.get_rank3_dataset(\"dpderho\") == dpderho_expected);\n\n  boost::multi_array<double, 3> dpdrhoe_expected(boost::extents[2][2][2]);\n  dpdrhoe_expected[0][0][0] = 946635080552566.0;\n  dpdrhoe_expected[0][0][1] = 946623484746662.75;\n  dpdrhoe_expected[0][1][0] = 1022196607501370.9;\n  dpdrhoe_expected[0][1][1] = 1022182353756068.2;\n  dpdrhoe_expected[1][0][0] = 926314324149490.25;\n  dpdrhoe_expected[1][0][1] = 926288146766470.0;\n  dpdrhoe_expected[1][1][0] = 1000313905133207.1;\n  dpdrhoe_expected[1][1][1] = 1000284548311442.8;\n  CHECK(sample_data.get_rank3_dataset(\"dpdrhoe\") == dpdrhoe_expected);\n\n  boost::multi_array<double, 3> entropy_expected(boost::extents[2][2][2]);\n  entropy_expected[0][0][0] = 12.439959418394057;\n  entropy_expected[0][0][1] = 12.363977600038989;\n  entropy_expected[0][1][0] = 12.553961989417337;\n  entropy_expected[0][1][1] = 12.477976613417658;\n  entropy_expected[1][0][0] = 12.142114662263879;\n  entropy_expected[1][0][1] = 12.067704524057188;\n  entropy_expected[1][1][0] = 12.25376620450713;\n  entropy_expected[1][1][1] = 12.179352008001388;\n  CHECK(sample_data.get_rank3_dataset(\"entropy\") == entropy_expected);\n\n  boost::multi_array<double, 3> gamma_expected(boost::extents[2][2][2]);\n  gamma_expected[0][0][0] = 1.6667180692156467;\n  gamma_expected[0][0][1] = 1.6667273271084526;\n  gamma_expected[0][1][0] = 1.66668659680834;\n  gamma_expected[0][1][1] = 1.6666969738652102;\n  gamma_expected[1][0][0] = 1.6671718466996817;\n  gamma_expected[1][0][1] = 1.6671938524936938;\n  gamma_expected[1][1][0] = 1.6671006949123433;\n  gamma_expected[1][1][1] = 1.6671232575231221;\n  CHECK(sample_data.get_rank3_dataset(\"gamma\") == gamma_expected);\n\n  boost::multi_array<double, 3> logenergy_expected(boost::extents[2][2][2]);\n  logenergy_expected[0][0][0] = 19.279083431017359;\n  logenergy_expected[0][0][1] = 19.279083430081528;\n  logenergy_expected[0][1][0] = 19.279086019802637;\n  logenergy_expected[0][1][1] = 19.279086018868753;\n  logenergy_expected[1][0][0] = 19.273645709972406;\n  logenergy_expected[1][0][1] = 19.273645706932353;\n  logenergy_expected[1][1][0] = 19.273648277270205;\n  logenergy_expected[1][1][1] = 19.273648274167705;\n  CHECK(sample_data.get_rank3_dataset(\"logenergy\") == logenergy_expected);\n\n  boost::multi_array<double, 3> logpress_expected(boost::extents[2][2][2]);\n  logpress_expected[0][0][0] = 18.000096394732644;\n  logpress_expected[0][0][1] = 18.033424170052783;\n  logpress_expected[0][1][0] = 18.033447660355776;\n  logpress_expected[0][1][1] = 18.066775331565321;\n  logpress_expected[1][0][0] = 17.990459396691801;\n  logpress_expected[1][0][1] = 18.0237747523636;\n  logpress_expected[1][1][0] = 18.023852243798054;\n  logpress_expected[1][1][1] = 18.057168258597677;\n  CHECK(sample_data.get_rank3_dataset(\"logpress\") == logpress_expected);\n\n  boost::multi_array<double, 3> meffn_expected(boost::extents[2][2][2]);\n  meffn_expected[0][0][0] = 2.4744534162653053;\n  meffn_expected[0][0][1] = -8.997614958121309;\n  meffn_expected[0][1][0] = 7.4562655881310071;\n  meffn_expected[0][1][1] = -7.8985993911032804;\n  meffn_expected[1][0][0] = 8.1298728332416808;\n  meffn_expected[1][0][1] = -1.5340273998086609;\n  meffn_expected[1][1][0] = -3.6860947464281306;\n  meffn_expected[1][1][1] = 8.1586649741865287;\n  CHECK(sample_data.get_rank3_dataset(\"meffn\") == meffn_expected);\n\n  boost::multi_array<double, 3> meffp_expected(boost::extents[2][2][2]);\n  meffp_expected[0][0][0] = 0.78824408539654733;\n  meffp_expected[0][0][1] = -9.5483672316917243;\n  meffp_expected[0][1][0] = 0.54136393120109716;\n  meffp_expected[0][1][1] = 2.6773060080962736;\n  meffp_expected[1][0][0] = -2.8801827291174398;\n  meffp_expected[1][0][1] = 5.2981584719540482;\n  meffp_expected[1][1][0] = 8.5371935011578408;\n  meffp_expected[1][1][1] = -4.1656335726133253;\n  CHECK(sample_data.get_rank3_dataset(\"meffp\") == meffp_expected);\n\n  boost::multi_array<double, 3> mu_e_expected(boost::extents[2][2][2]);\n  mu_e_expected[0][0][0] = 0.50689824836686137;\n  mu_e_expected[0][0][1] = 0.50697546449327213;\n  mu_e_expected[0][1][0] = 0.50644579668153178;\n  mu_e_expected[0][1][1] = 0.50652911816373358;\n  mu_e_expected[1][0][0] = 0.50800847018382267;\n  mu_e_expected[1][0][1] = 0.50808661150047807;\n  mu_e_expected[1][1][0] = 0.50764321962573911;\n  mu_e_expected[1][1][1] = 0.5077274312638953;\n  CHECK(sample_data.get_rank3_dataset(\"mu_e\") == mu_e_expected);\n\n  boost::multi_array<double, 3> mu_n_expected(boost::extents[2][2][2]);\n  mu_n_expected[0][0][0] = -0.010095301103328893;\n  mu_n_expected[0][0][1] = -0.010018547102765752;\n  mu_n_expected[0][1][0] = -0.011024970810644845;\n  mu_n_expected[0][1][1] = -0.010942093884027599;\n  mu_n_expected[1][0][0] = -0.010127118677803536;\n  mu_n_expected[1][0][1] = -0.010050364713731597;\n  mu_n_expected[1][1][0] = -0.011059326583649635;\n  mu_n_expected[1][1][1] = -0.010976449692047936;\n  CHECK(sample_data.get_rank3_dataset(\"mu_n\") == mu_n_expected);\n\n  boost::multi_array<double, 3> mu_p_expected(boost::extents[2][2][2]);\n  mu_p_expected[0][0][0] = -24.710273945544664;\n  mu_p_expected[0][0][1] = -24.710437248365768;\n  mu_p_expected[0][1][0] = -24.708423126879229;\n  mu_p_expected[0][1][1] = -24.708598930672853;\n  mu_p_expected[1][0][0] = -24.710274705168015;\n  mu_p_expected[1][0][1] = -24.710440937269624;\n  mu_p_expected[1][1][0] = -24.708414929439002;\n  mu_p_expected[1][1][1] = -24.708593662516304;\n  CHECK(sample_data.get_rank3_dataset(\"mu_p\") == mu_p_expected);\n\n  boost::multi_array<double, 3> muhat_expected(boost::extents[2][2][2]);\n  muhat_expected[0][0][0] = 24.700178644441333;\n  muhat_expected[0][0][1] = 24.700418701263001;\n  muhat_expected[0][1][0] = 24.697398156068584;\n  muhat_expected[0][1][1] = 24.697656836788827;\n  muhat_expected[1][0][0] = 24.70014758649021;\n  muhat_expected[1][0][1] = 24.700390572555893;\n  muhat_expected[1][1][0] = 24.697355602855353;\n  muhat_expected[1][1][1] = 24.697617212824255;\n  CHECK(sample_data.get_rank3_dataset(\"muhat\") == muhat_expected);\n\n  boost::multi_array<double, 3> munu_expected(boost::extents[2][2][2]);\n  munu_expected[0][0][0] = -24.193280396074471;\n  munu_expected[0][0][1] = -24.193443236769728;\n  munu_expected[0][1][0] = -24.190952359387051;\n  munu_expected[0][1][1] = -24.191127718625093;\n  munu_expected[1][0][0] = -24.192139116306389;\n  munu_expected[1][0][1] = -24.192303961055416;\n  munu_expected[1][1][0] = -24.189712383229615;\n  munu_expected[1][1][1] = -24.18988978156036;\n  CHECK(sample_data.get_rank3_dataset(\"munu\") == munu_expected);\n\n  boost::multi_array<double, 3> r_expected(boost::extents[2][2][2]);\n  r_expected[0][0][0] = -0.60525665755039704;\n  r_expected[0][0][1] = 2.2179733206970909;\n  r_expected[0][1][0] = -1.0549307927354015;\n  r_expected[0][1][1] = -6.0983303982526049;\n  r_expected[1][0][0] = -9.6472871040770141;\n  r_expected[1][0][1] = -9.5451610615235349;\n  r_expected[1][1][0] = -1.2051288530836537;\n  r_expected[1][1][1] = 6.0435070768646284;\n  CHECK(sample_data.get_rank3_dataset(\"r\") == r_expected);\n\n  boost::multi_array<double, 3> u_expected(boost::extents[2][2][2]);\n  u_expected[0][0][0] = 9.8002955742002342;\n  u_expected[0][0][1] = 2.7463512929327969;\n  u_expected[0][1][0] = -2.2719797545844012;\n  u_expected[0][1][1] = 0.12238635453091185;\n  u_expected[1][0][0] = 1.2619706692408634;\n  u_expected[1][0][1] = 4.8905663798386279;\n  u_expected[1][1][0] = 7.6749788245333903;\n  u_expected[1][1][1] = 7.7886279535935081;\n  CHECK(sample_data.get_rank3_dataset(\"u\") == u_expected);\n}\n\nSPECTRE_TEST_CASE(\"Unit.IO.H5.StellarCollapseEos\", \"[Unit][IO][H5]\") {\n  test_tabulated(unit_test_src_path() + \"/IO/H5/StellarCollapse2017Sample.h5\",\n                 \"/\");\n  test_tabulated(unit_test_src_path() + \"/IO/H5/StellarCollapse2017Sample.h5\",\n                 \"/sample_data\");\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_TensorData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nvoid test() {\n  TensorComponent tc0(\"T_x\", DataType{8.9, 7.6, -3.4, 9.0});\n  TensorComponent tc1(\"T_y\", DataType{8.9, 7.6, -3.4, 9.0});\n  TensorComponent tc2(\"T_x\", DataType{8.9, 7.6, -3.4, 9.1});\n  CHECK(get_output(tc0) == \"(T_x, (8.9,7.6,-3.4,9))\");\n  CHECK(tc0 == tc0);\n  CHECK(tc0 != tc1);\n  CHECK(tc0 != tc2);\n  CHECK(tc1 != tc2);\n  test_serialization(tc0);\n\n  const ElementVolumeData evd0(\n      \"Element0\", {tc0, tc1, tc2}, {2, 2},\n      {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev},\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto});\n  const auto after_evd0 = serialize_and_deserialize(evd0);\n  CHECK(after_evd0.extents == evd0.extents);\n  CHECK(after_evd0.tensor_components == evd0.tensor_components);\n  CHECK(after_evd0.basis == evd0.basis);\n  CHECK(after_evd0.quadrature == evd0.quadrature);\n  CHECK(after_evd0.element_name == evd0.element_name);\n  CHECK(after_evd0 == evd0);\n  // Check operator==\n  CHECK(evd0 !=\n        ElementVolumeData(\n            \"Element0\", {tc0, tc1, tc2}, {3, 2},\n            {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev},\n            {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}));\n  CHECK(evd0 !=\n        ElementVolumeData(\n            \"Element0\", {tc2, tc1, tc0}, {2, 2},\n            {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev},\n            {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}));\n  CHECK(evd0 !=\n        ElementVolumeData(\n            \"Element0\", {tc0, tc1, tc2}, {2, 2},\n            {Spectral::Basis::Chebyshev, Spectral::Basis::Chebyshev},\n            {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}));\n  CHECK(evd0 != ElementVolumeData(\n                    \"Element0\", {tc0, tc1, tc2}, {2, 2},\n                    {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev},\n                    {Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::GaussLobatto}));\n  CHECK(evd0 !=\n        ElementVolumeData(\n            \"Element1\", {tc0, tc1, tc2}, {2, 2},\n            {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev},\n            {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}));\n  CHECK(evd0 ==\n        ElementVolumeData(\n            \"Element0\", {tc0, tc1, tc2}, {2, 2},\n            {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev},\n            {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}));\n  CHECK(ElementVolumeData(ElementId<1>{0, {{{1, 1}}}}, {tc0, tc1, tc2},\n                          Mesh<1>{3, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto}) ==\n        ElementVolumeData(\"[B0,(L1I1)]\", {tc0, tc1, tc2}, {3},\n                          {Spectral::Basis::Legendre},\n                          {Spectral::Quadrature::GaussLobatto}));\n  CHECK(ElementVolumeData(\n            ElementId<2>{0, {{{1, 1}, {2, 0}}}}, {tc0, tc1, tc2},\n            Mesh<2>{{{3, 4}},\n                    {{Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}},\n                    {{Spectral::Quadrature::Gauss,\n                      Spectral::Quadrature::GaussLobatto}}}) ==\n        ElementVolumeData(\n            \"[B0,(L1I1,L2I0)]\", {tc0, tc1, tc2}, {3, 4},\n            {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev},\n            {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}));\n  CHECK(ElementVolumeData(\n            ElementId<3>{0, {{{1, 1}, {2, 0}, {2, 1}}}}, {tc0, tc1, tc2},\n            Mesh<3>{{{3, 4, 5}},\n                    {{Spectral::Basis::Legendre, Spectral::Basis::Chebyshev,\n                      Spectral::Basis::Legendre}},\n                    {{Spectral::Quadrature::Gauss,\n                      Spectral::Quadrature::GaussLobatto,\n                      Spectral::Quadrature::Gauss}}}) ==\n        ElementVolumeData(\n            \"[B0,(L1I1,L2I0,L2I1)]\", {tc0, tc1, tc2}, {3, 4, 5},\n            {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev,\n             Spectral::Basis::Legendre},\n            {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto,\n             Spectral::Quadrature::Gauss}));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.TensorData\", \"[Unit]\") {\n  test<DataVector>();\n  test<std::vector<float>>();\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_TensorData.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.Domain import ElementId\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\nfrom spectre.Spectral import Basis, Mesh, Quadrature\n\n\nclass TestTensorData(unittest.TestCase):\n    # Tests for TensorComponent functions\n    def test_tensor_component(self):\n        # Set up Tensor Component\n        tensor_component = TensorComponent(\n            \"tensor component\", DataVector([6.7, 3.2])\n        )\n        # Test name\n        self.assertEqual(tensor_component.name, \"tensor component\")\n        tensor_component.name = \"new tensor component\"\n        self.assertEqual(tensor_component.name, \"new tensor component\")\n        # Test data\n        npt.assert_array_almost_equal(\n            tensor_component.data, np.array([6.7, 3.2])\n        )\n        self.assertEqual(tensor_component.data.dtype, np.float64)\n        # Test str, repr\n        self.assertEqual(\n            str(tensor_component), \"(new tensor component, (6.7,3.2))\"\n        )\n        self.assertEqual(\n            repr(tensor_component), \"(new tensor component, (6.7,3.2))\"\n        )\n\n    def test_element_volume_data(self):\n        # Set up Extents and Tensor Volume data\n        tensor_component_1 = TensorComponent(\n            \"tensor component one\", DataVector([1.5, 1.1])\n        )\n        tensor_component_2 = TensorComponent(\n            \"tensor component two\", DataVector([7.1, 5])\n        )\n        basis = Basis.Legendre\n        quad = Quadrature.Gauss\n        element_data = ElementVolumeData(\n            element_name=\"grid_name\",\n            components=[tensor_component_1, tensor_component_2],\n            extents=[3, 4],\n            basis=2 * [basis],\n            quadrature=2 * [quad],\n        )\n        element_data_2 = ElementVolumeData(\n            ElementId[2](\"[B0,(L1I0,L0I0)]\"),\n            components=[tensor_component_1, tensor_component_2],\n            mesh=Mesh[2]([3, 4], basis, quad),\n        )\n\n        # Test extents\n        self.assertEqual(element_data.extents, [3, 4])\n        self.assertEqual(element_data_2.extents, [3, 4])\n        element_data.extents = [5, 6]\n        self.assertEqual(element_data.extents, [5, 6])\n        # Test tensor components\n        self.assertEqual(\n            element_data.tensor_components,\n            [tensor_component_1, tensor_component_2],\n        )\n        self.assertEqual(\n            element_data_2.tensor_components,\n            [tensor_component_1, tensor_component_2],\n        )\n        element_data.tensor_components = [\n            tensor_component_1,\n            tensor_component_1,\n        ]\n        self.assertEqual(\n            element_data.tensor_components,\n            [tensor_component_1, tensor_component_1],\n        )\n        # Test basis and quadrature\n        self.assertEqual(element_data.basis, [basis, basis])\n        self.assertEqual(element_data.quadrature, [quad, quad])\n        self.assertEqual(element_data.element_name, \"grid_name\")\n        self.assertEqual(element_data_2.basis, [basis, basis])\n        self.assertEqual(element_data_2.quadrature, [quad, quad])\n        self.assertEqual(element_data_2.element_name, \"[B0,(L1I0,L0I0)]\")\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_Version.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdint>\n#include <hdf5.h>\n#include <string>\n\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.IO.H5.Version\", \"[Unit][IO][H5]\") {\n  const std::string h5_file_name(\"Unit.IO.H5.Version.h5\");\n  const uint32_t version_number = 2;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  {\n    // [h5file_write_version]\n    h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n    my_file.insert<h5::Version>(\"/the_version\", version_number);\n    // [h5file_write_version]\n  }\n\n  h5::H5File<h5::AccessType::ReadOnly> my_file(h5_file_name);\n  // [h5file_read_version]\n  const auto& const_version = my_file.get<h5::Version>(\"/the_version\");\n  // [h5file_read_version]\n  CHECK(const_version.subfile_path() == \"/the_version\");\n  const auto const_version_val = const_version.get_version();\n  my_file.close_current_object();\n  CHECK(version_number == const_version_val);\n  auto& version = my_file.get<h5::Version>(\"/the_version\");\n  const auto version_val = version.get_version();\n  CHECK(version_number == version_val);\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_VolumeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <hdf5.h>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/CartoonCylinder.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/IO/VolumeData.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test_strahlkorper() {\n  constexpr size_t l_max = 8;\n  constexpr double sphere_radius = 4.0;\n  constexpr std::array<double, 3> center{{5.0, 6.0, 7.0}};\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper{l_max, l_max,\n                                                        sphere_radius, center};\n  const ylm::Spherepack& ylm = strahlkorper.ylm_spherepack();\n  const std::array<DataVector, 2> theta_phi = ylm.theta_phi_points();\n  const DataVector theta = theta_phi[0];\n  const DataVector phi = theta_phi[1];\n  const DataVector sin_theta = sin(theta);\n  const DataVector radius = ylm.spec_to_phys(strahlkorper.coefficients());\n  const std::string grid_name{\"AhA\"};\n  const std::vector<DataVector> tensor_and_coord_data{\n      radius * sin_theta * cos(phi), radius * sin_theta * sin(phi),\n      radius * cos(theta), cos(2.0 * theta)};\n  const std::vector<TensorComponent> tensor_components{\n      {\"InertialCoordinates_x\", tensor_and_coord_data[0]},\n      {\"InertialCoordinates_y\", tensor_and_coord_data[1]},\n      {\"InertialCoordinates_z\", tensor_and_coord_data[2]},\n      {\"TestScalar\", tensor_and_coord_data[3]}};\n\n  const std::vector<size_t> observation_ids{4444};\n  const std::vector<double> observation_values{1.0};\n  const std::vector<Spectral::Basis> bases{2,\n                                           Spectral::Basis::SphericalHarmonic};\n  const std::vector<Spectral::Quadrature> quadratures{\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Equiangular}};\n\n  const std::string h5_file_name{\"Unit.IO.H5.VolumeData.Strahlkorper.h5\"};\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  const std::vector<size_t> extents{\n      {ylm.physical_extents()[0], ylm.physical_extents()[1]}};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> strahlkorper_file{h5_file_name};\n    auto& volume_file = strahlkorper_file.insert<h5::VolumeData>(\n        \"/element_data\", version_number);\n    volume_file.write_volume_data(\n        observation_ids[0], observation_values[0],\n        std::vector<ElementVolumeData>{\n            {grid_name, tensor_components, extents, bases, quadratures}});\n    strahlkorper_file.close_current_object();\n\n    // Open the read volume file and check that the observation id and values\n    // are correct.\n    const auto& volume_file_read =\n        strahlkorper_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto read_observation_ids = volume_file_read.list_observation_ids();\n    CHECK(read_observation_ids == std::vector<size_t>{4444});\n    CHECK(volume_file_read.get_observation_value(observation_ids[0]) ==\n          observation_values[0]);\n  }\n\n  // Check exact observation value\n  TestHelpers::io::VolumeData::check_volume_data(\n      h5_file_name, version_number, \"element_data\"s, observation_ids[0],\n      observation_values[0], std::nullopt, tensor_and_coord_data, {{grid_name}},\n      {bases}, {quadratures}, {extents},\n      {\"InertialCoordinates_x\", \"InertialCoordinates_y\",\n       \"InertialCoordinates_z\", \"TestScalar\"},\n      {{0, 1, 2, 3}}, {}, observation_values[0]);\n\n  // Check observation value within epsilon\n  {\n    const std::optional<double> epsilon = 1.0e-8;\n    TestHelpers::io::VolumeData::check_volume_data(\n        h5_file_name, version_number, \"element_data\"s, observation_ids[0],\n        observation_values[0] + 0.1 * epsilon.value(), epsilon,\n        tensor_and_coord_data, {{grid_name}}, {bases}, {quadratures}, {extents},\n        {\"InertialCoordinates_x\", \"InertialCoordinates_y\",\n         \"InertialCoordinates_z\", \"TestScalar\"},\n        {{0, 1, 2, 3}}, {}, observation_values[0]);\n  }\n\n  // Check that pole triangles are now merged into the main connectivity.\n  // l_max=8 → extents=(9,17), regular quads=8*16=128, wrapping quads=8,\n  // pole triangles = 2*(2*8-1) = 30.  Total cells = 166.\n  // The pole triangle entries (4 ints each: tag=4, root, p2, p3) should appear\n  // at the tail of the connectivity array after the quads (5 ints each).\n  // clang-format off\n  const std::vector<int> expected_pole_entries = {\n      4, 0, 9,   18,    4, 8, 17,  26,\n      4, 0, 18,  27,    4, 8, 26,  35,\n      4, 0, 27,  36,    4, 8, 35,  44,\n      4, 0, 36,  45,    4, 8, 44,  53,\n      4, 0, 45,  54,    4, 8, 53,  62,\n      4, 0, 54,  63,    4, 8, 62,  71,\n      4, 0, 63,  72,    4, 8, 71,  80,\n      4, 0, 72,  81,    4, 8, 80,  89,\n      4, 0, 81,  90,    4, 8, 89,  98,\n      4, 0, 90,  99,    4, 8, 98,  107,\n      4, 0, 99,  108,   4, 8, 107, 116,\n      4, 0, 108, 117,   4, 8, 116, 125,\n      4, 0, 117, 126,   4, 8, 125, 134,\n      4, 0, 126, 135,   4, 8, 134, 143,\n      4, 0, 135, 144,   4, 8, 143, 152};\n  // clang-format on\n\n  {\n    h5::H5File<h5::AccessType::ReadOnly> strahlkorper_file{h5_file_name};\n    const auto& volume_file =\n        strahlkorper_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto h5_connectivity =\n        volume_file.get_tensor_component(4444, \"connectivity\").data;\n    const auto& conn = get<0>(h5_connectivity);\n    // Verify that the tail of the connectivity matches the expected pole\n    // entries\n    REQUIRE(conn.size() >= expected_pole_entries.size());\n    const size_t tail_start = conn.size() - expected_pole_entries.size();\n    for (size_t k = 0; k < expected_pole_entries.size(); ++k) {\n      CHECK(static_cast<int>(conn[tail_start + k]) == expected_pole_entries[k]);\n    }\n    // 128 regular quads × (1+4) + 8 wrapping quads × (1+4) +\n    // 30 pole triangles × (1+3) = 640 + 40 + 120 = 800\n    CHECK(conn.size() == 800);\n    strahlkorper_file.close_current_object();\n  }\n\n  // Verify that pole_connectivity is NOT written as a separate dataset\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> strahlkorper_file{h5_file_name};\n    const auto& volume_file =\n        strahlkorper_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    CHECK_THROWS_WITH(\n        volume_file.get_tensor_component(4444, \"pole_connectivity\"),\n        Catch::Matchers::ContainsSubstring(\"pole_connectivity\"));\n    strahlkorper_file.close_current_object();\n  }\n\n  // Verify element_id/block_id lengths match the total cell count, including\n  // wrapping quads and pole triangles.\n  // l_max=8 → extents=(9,17), regular quads=128, wrapping quads=8,\n  // pole triangles=30 → 166 total cells.\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> strahlkorper_file{h5_file_name};\n    const auto& volume_file =\n        strahlkorper_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto element_id_var =\n        volume_file.get_tensor_component(4444, \"ElementId\").data;\n    const auto& element_id = get<0>(element_id_var);\n    // 128 regular + 8 wrapping + 2*(2*8-1) pole triangles = 166\n    constexpr size_t expected_num_cells =\n        (l_max) * (2 * l_max) + l_max + 2 * (2 * l_max - 1);\n    CHECK(element_id.size() == expected_num_cells);\n    const auto block_id_var =\n        volume_file.get_tensor_component(4444, \"BlockId\").data;\n    const auto& block_id = get<0>(block_id_var);\n    CHECK(block_id.size() == expected_num_cells);\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\ntemplate <typename DataType>\nvoid test() {\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n  const std::vector<DataType> tensor_components_and_coords{\n      {8.9, 7.6, 3.9, 2.1, 18.9, 17.6, 13.9, 12.1},\n      {0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0},\n      {0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0},\n      {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0},\n      {-78.9, -7.6, -1.9, 8.1, 6.3, 8.7, 9.8, 0.2},\n      {-7.9, 7.6, 1.9, -8.1, -6.3, 2.7, 6.8, -0.2},\n      {17.9, 27.6, 21.9, -28.1, -26.3, 32.7, 26.8, -30.2}};\n  DataType extra_tensor_component(16);\n  std::iota(extra_tensor_component.begin(), extra_tensor_component.end(), 1.);\n  const std::vector<size_t> observation_ids{8435087234, size_t(-1)};\n  const std::vector<double> observation_values{8.0, -2.3};\n  const std::vector<std::string> grid_names{\"[[2,3,4]]\", \"[[5,6,7]]\"};\n  const std::vector<std::vector<Spectral::Basis>> bases{\n      {3, Spectral::Basis::Chebyshev}, {3, Spectral::Basis::Legendre}};\n  const std::vector<std::vector<Spectral::Quadrature>> quadratures{\n      {3, Spectral::Quadrature::Gauss},\n      {3, Spectral::Quadrature::GaussLobatto}};\n  const domain::creators::Brick domain_creator{\n      {{0., 0., 0.}},\n      {{1., 2., 3.}},\n      {{1, 0, 1}},\n      {{3, 4, 5}},\n      {{false, false, false}},\n      {},\n      std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3, 0>>(\n          1., std::array<double, 3>{{2., 3., 4.}})};\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  {\n    auto& volume_file =\n        my_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    const auto write_to_file = [&volume_file, &tensor_components_and_coords,\n                                &grid_names, &bases, &quadratures,\n                                &domain_creator, &extra_tensor_component](\n                                   const size_t observation_id,\n                                   const double observation_value) {\n      std::string first_grid = grid_names.front();\n      std::string last_grid = grid_names.back();\n      volume_file.write_volume_data(\n          observation_id, observation_value,\n          std::vector<ElementVolumeData>{\n              {first_grid,\n               {TensorComponent{\"S\", TestHelpers::io::VolumeData::multiply(\n                                         observation_value,\n                                         tensor_components_and_coords[0])},\n                TensorComponent{\n                    \"x-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[1])},\n                TensorComponent{\n                    \"y-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[2])},\n                TensorComponent{\n                    \"z-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[3])},\n                TensorComponent{\"T_x\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[4])},\n                TensorComponent{\"T_y\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[5])},\n                TensorComponent{\"T_z\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[6])}},\n               {2, 2, 2},\n               bases.front(),\n               quadratures.front()},\n              // Second Element Data\n              {last_grid,\n               {TensorComponent{\"S\", TestHelpers::io::VolumeData::multiply(\n                                         observation_value,\n                                         tensor_components_and_coords[1])},\n                TensorComponent{\n                    \"x-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[0])},\n                TensorComponent{\n                    \"y-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[5])},\n                TensorComponent{\n                    \"z-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[3])},\n                TensorComponent{\"T_x\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[6])},\n                TensorComponent{\"T_y\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[4])},\n                TensorComponent{\"T_z\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[2])}},\n               {2, 2, 2},\n               bases.back(),\n               quadratures.back()}},\n          serialize(domain_creator.create_domain()),\n          serialize(domain_creator.functions_of_time()),\n          serialize(domain_creator.functions_of_time()));\n      // Write another tensor component separately\n      volume_file.write_tensor_component(observation_id, \"U\",\n                                         DataType{1., 2., 3.});\n      CHECK_THROWS_WITH(volume_file.write_tensor_component(\n                            observation_id, \"U\", extra_tensor_component),\n                        Catch::Matchers::ContainsSubstring(\"already exists\"));\n      volume_file.write_tensor_component(observation_id, \"U\",\n                                         extra_tensor_component, true);\n    };\n    for (size_t i = 0; i < observation_ids.size(); ++i) {\n      write_to_file(observation_ids[i], observation_values[i]);\n    }\n    my_file.close_current_object();\n  }\n  // Open the read volume file and check that the observation id and values are\n  // correct. No leading slash should also find the subfile, and a \".vol\"\n  // extension as well.\n  auto& volume_file =\n      my_file.get<h5::VolumeData>(\"element_data.vol\", version_number);\n  CHECK(volume_file.subfile_path() == \"/element_data\");\n  const auto read_observation_ids = volume_file.list_observation_ids();\n  // The observation IDs should be sorted by their observation value\n  CHECK(read_observation_ids == std::vector<size_t>{size_t(-1), 8435087234});\n  CHECK(volume_file.has_domain());\n  CHECK(volume_file.has_global_functions_of_time());\n  const std::string subfile_group_path =\n      std::string(volume_file.subfile_path()) + h5::VolumeData::extension();\n  const hid_t read_only_file_id =\n      H5Fopen(h5_file_name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);\n  CHECK(read_only_file_id >= 0);\n  {\n    const h5::detail::OpenGroup subfile_group(\n        read_only_file_id, subfile_group_path, h5::AccessType::ReadOnly);\n    CHECK(h5::contains_dataset_or_group(subfile_group.id(), \"\", \"domain\"));\n    CHECK(h5::contains_dataset_or_group(subfile_group.id(), \"\",\n                                        \"global_functions_of_time\"));\n  }\n  {\n    const std::string observation_group_path =\n        subfile_group_path + \"/ObservationId\" +\n        std::to_string(observation_ids.front());\n    const h5::detail::OpenGroup observation_group(\n        read_only_file_id, observation_group_path, h5::AccessType::ReadOnly);\n    CHECK_FALSE(\n        h5::contains_dataset_or_group(observation_group.id(), \"\", \"domain\"));\n    CHECK(h5::contains_dataset_or_group(observation_group.id(), \"\",\n                                        \"functions_of_time\"));\n  }\n  CHECK_H5(H5Fclose(read_only_file_id), \"Failed to close HDF5 file\");\n  {\n    INFO(\"Test find_observation_id\");\n    std::vector<size_t> found_observation_ids(observation_values.size());\n    std::transform(observation_values.begin(), observation_values.end(),\n                   found_observation_ids.begin(),\n                   [&volume_file](const double observation_value) {\n                     return volume_file.find_observation_id(observation_value);\n                   });\n    CHECK(found_observation_ids == observation_ids);\n  }\n  CHECK(volume_file.get_domain() == serialize(domain_creator.create_domain()));\n  for (size_t i = 0; i < observation_ids.size(); ++i) {\n    TestHelpers::io::VolumeData::check_volume_data(\n        h5_file_name, version_number, \"element_data\"s, observation_ids[i],\n        observation_values[i], std::nullopt, tensor_components_and_coords,\n        grid_names, bases, quadratures, {{2, 2, 2}, {2, 2, 2}},\n        {\"S\", \"x-coord\", \"y-coord\", \"z-coord\", \"T_x\", \"T_y\", \"T_z\"},\n        {{0, 1, 2, 3, 4, 5, 6}, {1, 0, 5, 3, 6, 4, 2}}, {},\n        observation_values[i]);\n    CHECK(volume_file.get_functions_of_time(observation_ids[i]) ==\n          serialize(domain_creator.functions_of_time()));\n    const auto global_fot_buffer = volume_file.get_global_functions_of_time();\n    CHECK(global_fot_buffer.has_value());\n    CHECK(global_fot_buffer.value() ==\n          serialize(domain_creator.functions_of_time()));\n    CHECK(get<DataType>(\n              volume_file.get_tensor_component(observation_ids[i], \"U\").data) ==\n          extra_tensor_component);\n  }\n\n  {\n    INFO(\"offset_and_length_for_grid\");\n    const size_t observation_id = observation_ids.front();\n    // [find_offset]\n    const auto all_grid_names = volume_file.get_grid_names(observation_id);\n    const auto all_extents = volume_file.get_extents(observation_id);\n    const auto first_grid_offset_and_length = h5::offset_and_length_for_grid(\n        grid_names.front(), all_grid_names, all_extents);\n    // [find_offset]\n    CHECK(first_grid_offset_and_length.first == 0);\n    CHECK(first_grid_offset_and_length.second == 8);\n    const auto last_grid_offset_and_length = h5::offset_and_length_for_grid(\n        grid_names.back(), all_grid_names, all_extents);\n    CHECK(last_grid_offset_and_length.first == 8);\n    CHECK(last_grid_offset_and_length.second == 8);\n  }\n\n  {\n    INFO(\"mesh_for_grid\");\n    const size_t observation_id = observation_ids.front();\n    const auto all_grid_names = volume_file.get_grid_names(observation_id);\n    const auto all_extents = volume_file.get_extents(observation_id);\n    const auto all_bases = volume_file.get_bases(observation_id);\n    const auto all_quadratures = volume_file.get_quadratures(observation_id);\n    const auto first_mesh =\n        h5::mesh_for_grid<3>(grid_names.front(), all_grid_names, all_extents,\n                             all_bases, all_quadratures);\n    CHECK(first_mesh ==\n          Mesh<3>(2, Spectral::Basis::Chebyshev, Spectral::Quadrature::Gauss));\n    const auto last_mesh =\n        h5::mesh_for_grid<3>(grid_names.back(), all_grid_names, all_extents,\n                             all_bases, all_quadratures);\n    CHECK(last_mesh == Mesh<3>(2, Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto));\n  }\n\n  {\n    INFO(\"Functions of time overwrite ordering\");\n    const size_t fot_observation_id_base = 9100;\n    const double fot_observation_value_base = 12.5;\n    const std::vector<ElementVolumeData> simple_element_data{\n        {\"[FOTGrid]\",\n         {TensorComponent{\"SimpleScalar\", DataVector(8, 1.0)}},\n         {2, 2, 2},\n         {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n          Spectral::Basis::Legendre},\n         {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss,\n          Spectral::Quadrature::Gauss}}};\n    const auto serialized_domain = serialize(domain_creator.create_domain());\n    const auto make_serialized_functions_of_time =\n        [](const double expiration_time) {\n          domain::FunctionsOfTimeMap map{};\n          const std::array<DataVector, 3> initial_data{\n              DataVector{1.0}, DataVector{0.0}, DataVector{0.0}};\n          map[\"Translation\"] =\n              std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n                  0.0, initial_data, expiration_time);\n          return serialize(map);\n        };\n    const auto write_volume_with_data =\n        [&](const size_t observation_id, const double observation_value,\n            std::vector<char> serialized_functions_of_time) {\n          volume_file.write_volume_data(observation_id, observation_value,\n                                        simple_element_data, serialized_domain,\n                                        serialized_functions_of_time,\n                                        serialized_functions_of_time);\n        };\n    const auto read_observation_value_attribute = [&]() {\n      const hid_t read_file_id =\n          H5Fopen(h5_file_name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);\n      CHECK(read_file_id >= 0);\n      const h5::detail::OpenGroup read_subfile_group(\n          read_file_id, subfile_group_path, h5::AccessType::ReadOnly);\n      const auto stored_value = h5::read_value_attribute<double>(\n          read_subfile_group.id(),\n          \"global_functions_of_time_observation_value\");\n      CHECK_H5(H5Fclose(read_file_id), \"Failed to close HDF5 file\");\n      return stored_value;\n    };\n\n    const auto initial_fot = make_serialized_functions_of_time(5.0);\n    write_volume_with_data(fot_observation_id_base, fot_observation_value_base,\n                           initial_fot);\n\n    auto observation_fot =\n        volume_file.get_functions_of_time(fot_observation_id_base);\n    CHECK(observation_fot.has_value());\n    CHECK(observation_fot.value() == initial_fot);\n    auto global_fot = volume_file.get_global_functions_of_time();\n    CHECK(global_fot.has_value());\n    CHECK(global_fot.value() == initial_fot);\n    CHECK(read_observation_value_attribute() ==\n          approx(fot_observation_value_base));\n\n    const auto earlier_fot = make_serialized_functions_of_time(3.0);\n    write_volume_with_data(fot_observation_id_base + 1,\n                           fot_observation_value_base - 0.1, earlier_fot);\n    observation_fot =\n        volume_file.get_functions_of_time(fot_observation_id_base + 1);\n    CHECK(observation_fot.has_value());\n    // observation fot should just be written every time\n    CHECK(observation_fot.value() == earlier_fot);\n    global_fot = volume_file.get_global_functions_of_time();\n    CHECK(global_fot.has_value());\n    // global fot should not change because earlier_fot has an earlier\n    // observation_value\n    CHECK(global_fot.value() == initial_fot);\n    CHECK(read_observation_value_attribute() ==\n          approx(fot_observation_value_base));\n\n    const auto later_fot = make_serialized_functions_of_time(7.0);\n    write_volume_with_data(fot_observation_id_base + 2,\n                           fot_observation_value_base + 0.1, later_fot);\n    observation_fot =\n        volume_file.get_functions_of_time(fot_observation_id_base + 2);\n    CHECK(observation_fot.has_value());\n    CHECK(observation_fot.value() == later_fot);\n    global_fot = volume_file.get_global_functions_of_time();\n    CHECK(global_fot.has_value());\n    CHECK(global_fot.value() == later_fot);\n    CHECK(read_observation_value_attribute() ==\n          approx(fot_observation_value_base + 0.1));\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\ntemplate <size_t SpatialDim>\nvoid test_extend_connectivity_data() {\n  // Sample volume data\n  const std::vector<size_t>& observation_ids{2345};\n  const std::vector<double>& observation_values{1.0};\n\n  // Sample data with h-ref 1 & p-ref 2 for each SpatialDim\n  // Used for both number_of_elements and number_of_gridpoints\n  const size_t number_of_elements = two_to_the(SpatialDim);\n\n  // Instantiate components for write_volume_data\n  std::vector<std::vector<size_t>> extents(number_of_elements,\n                                           std::vector<size_t>(SpatialDim));\n  std::vector<std::vector<Spectral::Basis>> bases(\n      number_of_elements, std::vector<Spectral::Basis>(SpatialDim));\n  std::vector<std::vector<Spectral::Quadrature>> quadratures(\n      number_of_elements, std::vector<Spectral::Quadrature>(SpatialDim));\n  std::vector<std::string> grid_names(number_of_elements);\n  std::vector<std::vector<std::vector<float>>> tensor_components_and_coords(\n      number_of_elements, std::vector<std::vector<float>>(\n                              4, std::vector<float>(number_of_elements)));\n  std::vector<std::vector<TensorComponent>> tensor_components(\n      number_of_elements, std::vector<TensorComponent>(4));\n  std::vector<ElementVolumeData> element_data(number_of_elements);\n\n  // Base element spatial coordinates depending on SpatialDim\n  switch (SpatialDim) {\n    case 1:\n      tensor_components_and_coords[0][0] = {0.0, 1.0};\n      break;\n    case 2:\n      tensor_components_and_coords[0][0] = {0.0, 1.0, 0.0, 1.0};\n      tensor_components_and_coords[0][1] = {0.0, 0.0, 1.0, 1.0};\n      break;\n    case 3:\n      tensor_components_and_coords[0][0] = {0.0, 1.0, 0.0, 1.0,\n                                            0.0, 1.0, 0.0, 1.0};\n      tensor_components_and_coords[0][1] = {0.0, 0.0, 1.0, 1.0,\n                                            0.0, 0.0, 1.0, 1.0};\n      tensor_components_and_coords[0][2] = {0.0, 0.0, 0.0, 0.0,\n                                            1.0, 1.0, 1.0, 1.0};\n      break;\n    default:\n      ERROR(\"Invalid dimensionality\");\n  }\n\n  // Populate remain element spatial coordinates\n  for (size_t i = 0; i < 2; i++) {\n    size_t index = i;\n\n    for (size_t point_num = 0; point_num < number_of_elements; point_num++) {\n      tensor_components_and_coords[index][0][point_num] =\n          2 * i + tensor_components_and_coords[0][0][point_num];\n    }\n    grid_names[index] = \"[B0,(L1I\" + std::to_string(i) + \")]\";\n\n    if (SpatialDim > 1) {\n      for (size_t j = 0; j < 2; j++) {\n        index = 2 * j + i;\n\n        for (size_t point_num = 0; point_num < number_of_elements;\n             point_num++) {\n          tensor_components_and_coords[index][0][point_num] =\n              2 * i + tensor_components_and_coords[0][0][point_num];\n          tensor_components_and_coords[index][1][point_num] =\n              2 * j + tensor_components_and_coords[0][1][point_num];\n        }\n\n        grid_names[index] =\n            \"[B0,(L1I\" + std::to_string(i) + \",L1I\" + std::to_string(j) + \")]\";\n\n        if (SpatialDim == 3) {\n          for (size_t k = 0; k < 2; k++) {\n            index = 4 * k + 2 * j + i;\n\n            for (size_t point_num = 0; point_num < number_of_elements;\n                 point_num++) {\n              tensor_components_and_coords[index][0][point_num] =\n                  2 * i + tensor_components_and_coords[0][0][point_num];\n              tensor_components_and_coords[index][1][point_num] =\n                  2 * j + tensor_components_and_coords[0][1][point_num];\n              tensor_components_and_coords[index][2][point_num] =\n                  2 * k + tensor_components_and_coords[0][2][point_num];\n            }\n            grid_names[index] = \"[B0,(L1I\" + std::to_string(i) + \",L1I\" +\n                                std::to_string(j) + \",L1I\" + std::to_string(k) +\n                                \")]\";\n          }\n        }\n      }\n    }\n  }\n\n  // Populate remaining components required for writing\n  for (size_t i = 0; i < number_of_elements; i++) {\n    for (size_t j = 0; j < SpatialDim; j++) {\n      extents[i][j] = 2;\n      bases[i][j] = Spectral::Basis::Legendre;\n      quadratures[i][j] = Spectral::Quadrature::Gauss;\n    }\n    for (size_t point_num = 0; point_num < number_of_elements; point_num++) {\n      tensor_components_and_coords[i][SpatialDim][point_num] =\n          two_to_the(i + 1) + two_to_the(point_num);\n    }\n  }\n\n  // Create TensorComponent and ElementVolumeData vector depending on SpatialDim\n  for (size_t i = 0; i < number_of_elements; i++) {\n    switch (SpatialDim) {\n      case 1:\n        tensor_components[i] = {\n            {\"InertialCoordinates_x\", tensor_components_and_coords[i][0]},\n            {\"TestScalar\", tensor_components_and_coords[i][1]}};\n        break;\n      case 2:\n        tensor_components[i] = {\n            {\"InertialCoordinates_x\", tensor_components_and_coords[i][0]},\n            {\"InertialCoordinates_y\", tensor_components_and_coords[i][1]},\n            {\"TestScalar\", tensor_components_and_coords[i][2]}};\n        break;\n      case 3:\n        tensor_components[i] = {\n            {\"InertialCoordinates_x\", tensor_components_and_coords[i][0]},\n            {\"InertialCoordinates_y\", tensor_components_and_coords[i][1]},\n            {\"InertialCoordinates_z\", tensor_components_and_coords[i][2]},\n            {\"TestScalar\", tensor_components_and_coords[i][3]}};\n        break;\n      default:\n        ERROR(\"Invalid dimensionality\");\n    }\n\n    element_data[i] = {grid_names[i], tensor_components[i], extents[i],\n                       bases[i], quadratures[i]};\n  }  // End of sample volume data\n\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.ExtendConnectivity.h5\");\n  const uint32_t version_number = 4;\n\n  // Remove any pre-existing file with the same name\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  // Create a new file with the given name\n  h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_name};\n  auto& volume_file =\n      h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n\n  volume_file.write_volume_data(observation_ids[0], observation_values[0],\n                                element_data);\n  volume_file.extend_connectivity_data<SpatialDim>(observation_ids);\n\n  h5_file.close_current_object();\n\n  // Sample test connectivity. New format prepends an XDMF type tag per cell:\n  // Line=2, Quad=5, Hexahedron=9. Appended after the vertex indices for ease.\n  std::vector<size_t> expected_connectivity;\n  switch (SpatialDim) {\n    case 1:\n      // 3 cells, vertex indices + 3 type tags (tag=2 for Line)\n      expected_connectivity = {0, 1, 2, 3, 1, 2, 2, 2, 2};\n      break;\n    case 2:\n      // 9 cells, vertex indices + 9 type tags (tag=5 for Quad)\n      expected_connectivity = {0, 1, 3, 2, 2, 3, 9,  8,  8,  9,  11, 10,\n                               1, 4, 6, 3, 3, 6, 12, 9,  9,  12, 14, 11,\n                               4, 5, 7, 6, 6, 7, 13, 12, 12, 13, 15, 14,\n                               5, 5, 5, 5, 5, 5, 5,  5,  5};\n      break;\n    case 3:\n      // 27 cells (3x3x3 unique Gauss coords), vertex indices + 27 type tags\n      expected_connectivity = {\n          0, 1, 3, 2, 4, 5, 7, 6, 4, 5, 7, 6, 32, 33, 35, 34, 32, 33, 35, 34,\n          36, 37, 39, 38, 2, 3, 17, 16, 6, 7, 21, 20, 6, 7, 21, 20, 34, 35, 49,\n          48, 34, 35, 49, 48, 38, 39, 53, 52, 16, 17, 19, 18, 20, 21, 23, 22,\n          20, 21, 23, 22, 48, 49, 51, 50, 48, 49, 51, 50, 52, 53, 55, 54, 1, 8,\n          10, 3, 5, 12, 14, 7, 5, 12, 14, 7, 33, 40, 42, 35, 33, 40, 42, 35, 37,\n          44, 46, 39, 3, 10, 24, 17, 7, 14, 28, 21, 7, 14, 28, 21, 35, 42, 56,\n          49, 35, 42, 56, 49, 39, 46, 60, 53, 17, 24, 26, 19, 21, 28, 30, 23,\n          21, 28, 30, 23, 49, 56, 58, 51, 49, 56, 58, 51, 53, 60, 62, 55, 8, 9,\n          11, 10, 12, 13, 15, 14, 12, 13, 15, 14, 40, 41, 43, 42, 40, 41, 43,\n          42, 44, 45, 47, 46, 10, 11, 25, 24, 14, 15, 29, 28, 14, 15, 29, 28,\n          42, 43, 57, 56, 42, 43, 57, 56, 46, 47, 61, 60, 24, 25, 27, 26, 28,\n          29, 31, 30, 28, 29, 31, 30, 56, 57, 59, 58, 56, 57, 59, 58, 60, 61,\n          63, 62,\n          // 27 hexahedron type tags (3x3x3 cells from 4 unique coords/dir)\n          9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,\n          9, 9, 9, 9};\n      break;\n    default:\n      ERROR(\"Invalid dimensionality\");\n  }  // End of sample test connectivity\n\n  // Reopen h5 file and extract connectivity\n  const auto& volume_data = h5_file.get<h5::VolumeData>(\"/element_data\");\n  const auto h5_connectivity =\n      volume_data.get_tensor_component(2345, \"connectivity\").data;\n  const auto connectivity_data = get<0>(h5_connectivity);\n\n  // Store file connectivity in vector like expected_connectivity\n  std::vector<size_t> file_connectivity(connectivity_data.size());\n  for (size_t i = 0; i < connectivity_data.size(); i++) {\n    file_connectivity[i] = static_cast<size_t>(connectivity_data[i]);\n  }\n\n  h5_file.close_current_object();\n\n  // Sort to check connectivity is the same since elementwise comparison is not\n  // required or accurate\n  std::sort(file_connectivity.begin(), file_connectivity.end());\n  std::sort(expected_connectivity.begin(), expected_connectivity.end());\n\n  CHECK(file_connectivity == expected_connectivity);\n\n  // Remove all the created files\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_cylinder(const bool filled) {\n  // filled = true -> cylinder with ZernikeB2, close the angular edges with\n  // hexahedra (tag 9), fill in the inner circle with wedges (tag 8)\n  // filled = false -> hollow cylinder with Fourier, just close the angular\n  // edges with hexahedra (tag 9)\n  // All connectivity uses the mixed-topology format: XDMF type tag per cell.\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.Cylinder.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const std::string grid_name{\"Cylinder\"};\n  const std::vector<size_t> observation_ids{99901};\n  const std::vector<double> observation_values{2.2};\n  const auto bases =\n      filled ? std::vector<Spectral::Basis>{Spectral::Basis::ZernikeB2,\n                                            Spectral::Basis::ZernikeB2,\n                                            Spectral::Basis::Legendre}\n             : std::vector<Spectral::Basis>{Spectral::Basis::Legendre,\n                                            Spectral::Basis::Fourier,\n                                            Spectral::Basis::Legendre};\n  const std::vector<Spectral::Quadrature> quadratures{\n      filled ? Spectral::Quadrature::GaussRadauUpper\n             : Spectral::Quadrature::GaussLobatto,\n      Spectral::Quadrature::Equiangular, Spectral::Quadrature::GaussLobatto};\n  const std::vector<size_t> extents{2, 7, 2};\n\n  // Not using values, just checking connectivity\n  const std::vector<DataVector> tensor_and_coord_data{\n      {28, 0.0}, {28, 0.0}, {28, 0.0}, {28, 0.0}};\n  const std::vector<TensorComponent> tensor_components{\n      {\"InertialCoordinates_x\", tensor_and_coord_data[0]},\n      {\"InertialCoordinates_y\", tensor_and_coord_data[1]},\n      {\"InertialCoordinates_z\", tensor_and_coord_data[2]},\n      {\"TestScalar\", tensor_and_coord_data[3]}};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> cyl_file{h5_file_name};\n    auto& volume_file =\n        cyl_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    volume_file.write_volume_data(\n        observation_ids[0], observation_values[0],\n        std::vector<ElementVolumeData>{\n            {grid_name, tensor_components, extents, bases, quadratures}});\n    cyl_file.close_current_object();\n  }\n\n  // Check connectivity (Mixed format: 6 regular hexahedra + 1 phi-seam\n  // hexahedron, with XDMF type tags; for filled: also 5 center-fill wedges)\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> cyl_file{h5_file_name};\n    const auto& volume_file =\n        cyl_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto h5_connectivity =\n        volume_file.get_tensor_component(99901, \"connectivity\").data;\n    const DataVector connectivity_data = get<0>(h5_connectivity);\n    cyl_file.close_current_object();\n\n    // clang-format off\n    // 6 regular hexahedra (tag=9) + 1 phi-seam hexahedron (tag=9)\n    DataVector expected_connectivity{\n        9.,  0.,  1.,  3.,  2., 14., 15., 17., 16.,  // regular hex ip=0->1\n        9.,  2.,  3.,  5.,  4., 16., 17., 19., 18.,  // regular hex ip=1->2\n        9.,  4.,  5.,  7.,  6., 18., 19., 21., 20.,  // regular hex ip=2->3\n        9.,  6.,  7.,  9.,  8., 20., 21., 23., 22.,  // regular hex ip=3->4\n        9.,  8.,  9., 11., 10., 22., 23., 25., 24.,  // regular hex ip=4->5\n        9., 10., 11., 13., 12., 24., 25., 27., 26.,  // regular hex ip=5->6\n        9., 12., 13.,  1.,  0., 26., 27., 15., 14.}; // seam hex ip=6->0\n    if (filled) {\n      // Append 5 center-fill wedges (tag=8), one z layer (j_z=0)\n      // via recursive halving of 7-point inner ring: [0,2,4,6,8,10,12]\n      // (ring_hi = [14,16,18,20,22,24,26])\n      expected_connectivity = DataVector{\n          9.,  0.,  1.,  3.,  2., 14., 15., 17., 16.,\n          9.,  2.,  3.,  5.,  4., 16., 17., 19., 18.,\n          9.,  4.,  5.,  7.,  6., 18., 19., 21., 20.,\n          9.,  6.,  7.,  9.,  8., 20., 21., 23., 22.,\n          9.,  8.,  9., 11., 10., 22., 23., 25., 24.,\n          9., 10., 11., 13., 12., 24., 25., 27., 26.,\n          9., 12., 13.,  1.,  0., 26., 27., 15., 14.,\n          8.,  0.,  2.,  4., 14., 16., 18.,  // wedge i=0\n          8.,  4.,  6.,  8., 18., 20., 22.,  // wedge i=2\n          8.,  8., 10., 12., 22., 24., 26.,  // wedge i=4\n          8.,  0.,  4.,  8., 14., 18., 22.,  // halved ring wedge\n          8.,  8., 12.,  0., 22., 26., 14.}; // closing wedge (even)\n    }\n    // clang-format on\n    CHECK(connectivity_data == expected_connectivity);\n  }\n\n  // Verify ElementId and BlockId lengths match total cell count\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> cyl_file{h5_file_name};\n    const auto& volume_file =\n        cyl_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    // hollow: 6 regular + 1 seam = 7 cells\n    // filled: 7 hexahedra + 5 wedges = 12 cells\n    const size_t expected_num_cells = filled ? 12 : 7;\n    const auto element_id_var =\n        volume_file.get_tensor_component(99901, \"ElementId\").data;\n    CHECK(get<0>(element_id_var).size() == expected_num_cells);\n    const auto block_id_var =\n        volume_file.get_tensor_component(99901, \"BlockId\").data;\n    CHECK(get<0>(block_id_var).size() == expected_num_cells);\n    const auto expected_element_id = static_cast<double>(\n        static_cast<uint64_t>(std::hash<std::string>{}(grid_name)));\n    for (size_t idx = 0; idx < expected_num_cells; ++idx) {\n      CHECK(get<0>(element_id_var)[idx] == expected_element_id);\n      CHECK(get<0>(block_id_var)[idx] == 0.0);\n    }\n    cyl_file.close_current_object();\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_disk() {\n  // The disk has some extra connectivity: one being to close the angular\n  // edges, the other to fill in the inner circle\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.Disk.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const std::string grid_name{\"Disk\"};\n  const std::vector<size_t> observation_ids{12354};\n  const std::vector<double> observation_values{1.1};\n  const std::vector<Spectral::Basis> bases{2, Spectral::Basis::ZernikeB2};\n  const std::vector<Spectral::Quadrature> quadratures{\n      {Spectral::Quadrature::GaussRadauUpper,\n       Spectral::Quadrature::Equiangular}};\n  const std::vector<size_t> extents{2, 7};\n\n  // Not using values, just checking connectivity\n  const std::vector<DataVector> tensor_and_coord_data{\n      {14, 0.0}, {14, 0.0}, {14, 0.0}};\n  const std::vector<TensorComponent> tensor_components{\n      {\"InertialCoordinates_x\", tensor_and_coord_data[0]},\n      {\"InertialCoordinates_y\", tensor_and_coord_data[1]},\n      {\"TestScalar\", tensor_and_coord_data[2]}};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> disk_file{h5_file_name};\n    auto& volume_file =\n        disk_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    volume_file.write_volume_data(\n        observation_ids[0], observation_values[0],\n        std::vector<ElementVolumeData>{\n            {grid_name, tensor_components, extents, bases, quadratures}});\n    disk_file.close_current_object();\n\n    // Open the read volume file and check that the observation id and values\n    // are correct.\n    const auto& volume_file_read =\n        disk_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto read_observation_ids = volume_file_read.list_observation_ids();\n    CHECK(read_observation_ids == std::vector<size_t>{12354});\n    CHECK(volume_file_read.get_observation_value(observation_ids[0]) ==\n          observation_values[0]);\n  }\n\n  // Check connectivity (Mixed format: normal quads, wrapping quad, disk\n  // triangles all in one array with XDMF type tags)\n  DataVector connectivity_data{};\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> disk_file{h5_file_name};\n    const auto& volume_file =\n        disk_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto h5_connectivity =\n        volume_file.get_tensor_component(12354, \"connectivity\").data;\n    connectivity_data = get<0>(h5_connectivity);\n    disk_file.close_current_object();\n  }\n  // clang-format off\n  // 6 normal quads + 1 wrapping quad (type=5) + 5 disk triangles (type=4)\n  DataVector expected_connectivity = {\n     5.,  0.,  1.,  3.,  2.,   // Quad\n     5.,  2.,  3.,  5.,  4.,   // Quad\n     5.,  4.,  5.,  7.,  6.,   // Quad\n     5.,  6.,  7.,  9.,  8.,   // Quad\n     5.,  8.,  9., 11., 10.,   // Quad\n     5., 10., 11., 13., 12.,   // Quad\n     5.,  0.,  1., 13., 12.,   // wrapping Quad\n     4.,  0.,  2.,  4.,        // Triangle\n     4.,  4.,  6.,  8.,        // Triangle\n     4.,  8., 10., 12.,        // Triangle\n     4.,  0.,  4.,  8.,        // Triangle\n     4.,  8., 12.,  0.};       // Triangle (closing)\n  // clang-format on\n\n  CHECK(connectivity_data == expected_connectivity);\n\n  // Verify element_id and block_id datasets.\n  // n_r=2, n_phi=7: 6 normal quads + 1 wrapping quad + 5 disk triangles = 12\n  // cells\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> disk_file{h5_file_name};\n    const auto& volume_file =\n        disk_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    constexpr size_t expected_num_cells = 12;\n\n    const auto element_id_var =\n        volume_file.get_tensor_component(12354, \"ElementId\").data;\n    const auto& element_id = get<0>(element_id_var);\n    CHECK(element_id.size() == expected_num_cells);\n\n    const auto block_id_var =\n        volume_file.get_tensor_component(12354, \"BlockId\").data;\n    const auto& block_id = get<0>(block_id_var);\n    CHECK(block_id.size() == expected_num_cells);\n\n    // All cells belong to the single \"Disk\" element; block_id should be 0\n    // (grid name doesn't match [B<N>,... pattern)\n    const auto expected_element_id = static_cast<double>(\n        static_cast<uint64_t>(std::hash<std::string>{}(grid_name)));\n    for (size_t i = 0; i < expected_num_cells; ++i) {\n      CHECK(element_id[i] == expected_element_id);\n      CHECK(block_id[i] == 0.0);\n    }\n    disk_file.close_current_object();\n  }\n\n  // Verify get_data_by_element roundtrip\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> disk_file{h5_file_name};\n    const auto& volume_file =\n        disk_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto all_data = volume_file.get_data_by_element(\n        std::nullopt, std::nullopt,\n        std::vector<std::string>{\"InertialCoordinates_x\",\n                                 \"InertialCoordinates_y\", \"TestScalar\"});\n    REQUIRE(all_data.size() == 1);\n    const auto& [obs_id_rt, obs_val_rt, elements] = all_data[0];\n    CHECK(obs_val_rt == approx(1.1));\n    REQUIRE(elements.size() == 1);\n    const auto& elem = elements[0];\n    CHECK(elem.element_name == grid_name);\n    CHECK(elem.extents == extents);\n    CHECK(elem.basis == bases);\n    CHECK(elem.quadrature == quadratures);\n    REQUIRE(elem.tensor_components.size() == 3);\n    disk_file.close_current_object();\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_cartoon() {\n  // For a 2D computational domain Cartoon-basis evolution, we only want to\n  // write 2D data despite the simulation being 3D. This tests the appropriate\n  // dimension reduction\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n  const std::vector<DataVector> tensor_components_and_coords{\n      {8.9, 7.6, 3.9, 2.1},     {0.0, 1.0, 0.0, 1.0},\n      {0.0, 0.0, 1.0, 1.0},     {0.0, 0.0, 0.0, 0.0},\n      {-78.9, -7.6, -1.9, 8.1}, {-7.9, 7.6, 1.9, -8.1},\n      {17.9, 27.6, 21.9, -28.1}};\n  const std::vector<size_t> observation_ids{8435087234, size_t(-1)};\n  const std::vector<double> observation_values{8.0, -2.3};\n  const std::vector<std::string> grid_names{\"[B0,(L1I0,L1I0,L1I0)]\"};\n  const std::vector<std::vector<Spectral::Basis>> bases{\n      {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n       Spectral::Basis::Cartoon}};\n  const std::vector<std::vector<Spectral::Quadrature>> quadratures{\n      {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n       Spectral::Quadrature::SphericalSymmetry}};\n\n  const std::vector<std::vector<Spectral::Basis>> written_bases{\n      {Spectral::Basis::Legendre, Spectral::Basis::Legendre}};\n  const std::vector<std::vector<Spectral::Quadrature>> written_quadratures{\n      {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto}};\n\n  const TestHelpers::domain::BoundaryConditions::TestBoundaryCondition<3>\n      test_bc{Direction<3>::lower_xi(), 0};\n  const domain::creators::CartoonCylinder domain_creator{\n      {0.0, 1.0},\n      {1.5, 2.0},\n      {0, 1},\n      {5, 4},\n      {domain::CoordinateMaps::Distribution::Linear,\n       domain::CoordinateMaps::Distribution::Linear},\n      std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3, 0>>(\n          1., std::array<double, 3>{{2., 3., 4.}}),\n      {{{{test_bc.get_clone(), test_bc.get_clone()}},\n        {{test_bc.get_clone(), test_bc.get_clone()}}}}};\n\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  {\n    auto& volume_file =\n        my_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    const auto write_to_file = [&volume_file, &tensor_components_and_coords,\n                                &grid_names, &bases, &quadratures,\n                                &domain_creator](\n                                   const size_t observation_id,\n                                   const double observation_value) {\n      const std::string& first_grid = grid_names.front();\n      volume_file.write_volume_data(\n          observation_id, observation_value,\n          std::vector<ElementVolumeData>{\n              {first_grid,\n               {TensorComponent{\"S\", TestHelpers::io::VolumeData::multiply(\n                                         observation_value,\n                                         tensor_components_and_coords[0])},\n                TensorComponent{\n                    \"x-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[1])},\n                TensorComponent{\n                    \"y-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[2])},\n                TensorComponent{\n                    \"z-coord\",\n                    TestHelpers::io::VolumeData::multiply(\n                        observation_value, tensor_components_and_coords[3])},\n                TensorComponent{\"T_x\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[4])},\n                TensorComponent{\"T_y\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[5])},\n                TensorComponent{\"T_z\", TestHelpers::io::VolumeData::multiply(\n                                           observation_value,\n                                           tensor_components_and_coords[6])}},\n               {2, 2, 1},\n               bases.front(),\n               quadratures.front()}},\n          serialize(domain_creator.create_domain()),\n          serialize(domain_creator.functions_of_time()));\n    };\n    for (size_t i = 0; i < observation_ids.size(); ++i) {\n      write_to_file(observation_ids[i], observation_values[i]);\n    }\n    my_file.close_current_object();\n  }\n  // Open the read volume file and check that the observation id and values are\n  // correct. No leading slash should also find the subfile, and a \".vol\"\n  // extension as well.\n  const auto& volume_file =\n      my_file.get<h5::VolumeData>(\"element_data.vol\", version_number);\n  CHECK(volume_file.subfile_path() == \"/element_data\");\n  const auto read_observation_ids = volume_file.list_observation_ids();\n  // The observation IDs should be sorted by their observation value\n  CHECK(read_observation_ids == std::vector<size_t>{size_t(-1), 8435087234});\n  {\n    INFO(\"Test find_observation_id\");\n    std::vector<size_t> found_observation_ids(observation_values.size());\n    std::transform(observation_values.begin(), observation_values.end(),\n                   found_observation_ids.begin(),\n                   [&volume_file](const double observation_value) {\n                     return volume_file.find_observation_id(observation_value);\n                   });\n    CHECK(found_observation_ids == observation_ids);\n  }\n\n  CHECK(volume_file.get_domain() == serialize(domain_creator.create_domain()));\n  for (size_t i = 0; i < observation_ids.size(); ++i) {\n    TestHelpers::io::VolumeData::check_volume_data(\n        h5_file_name, version_number, \"element_data\"s, observation_ids[i],\n        observation_values[i], std::nullopt, tensor_components_and_coords,\n        grid_names, written_bases, written_quadratures, {{2, 2}},\n        {\"S\", \"x-coord\", \"y-coord\", \"z-coord\", \"T_x\", \"T_y\", \"T_z\"},\n        {{0, 1, 2, 3, 4, 5, 6}}, {}, observation_values[i]);\n    CHECK(volume_file.get_functions_of_time(observation_ids[i]) ==\n          serialize(domain_creator.functions_of_time()));\n  }\n\n  {\n    INFO(\"Cartoon dimension reduction\");\n    const auto dimension = volume_file.get_dimension();\n    CHECK(dimension == 2);\n  }\n\n  {\n    INFO(\"Cartoon offset_and_length_for_grid\");\n    const size_t observation_id = observation_ids.front();\n    const auto all_grid_names = volume_file.get_grid_names(observation_id);\n    const auto all_extents = volume_file.get_extents(observation_id);\n    const auto first_grid_offset_and_length = h5::offset_and_length_for_grid(\n        grid_names.front(), all_grid_names, all_extents);\n    CHECK(first_grid_offset_and_length.first == 0);\n    CHECK(first_grid_offset_and_length.second == 4);\n  }\n\n  {\n    INFO(\"Cartoon mesh_for_grid\");\n    const size_t observation_id = observation_ids.front();\n    const auto all_grid_names = volume_file.get_grid_names(observation_id);\n    const auto all_extents = volume_file.get_extents(observation_id);\n    const auto all_bases = volume_file.get_bases(observation_id);\n    const auto all_quadratures = volume_file.get_quadratures(observation_id);\n    const auto first_mesh =\n        h5::mesh_for_grid<2>(grid_names.front(), all_grid_names, all_extents,\n                             all_bases, all_quadratures);\n    CHECK(first_mesh ==\n          Mesh<2>({2, 2},\n                  {Spectral::Basis::Legendre, Spectral::Basis::Legendre},\n                  {Spectral::Quadrature::GaussLobatto,\n                   Spectral::Quadrature::GaussLobatto}));\n  }\n\n  {\n    INFO(\"Cartoon element_id uses numerical dim (3), not written dim (2)\");\n    const size_t observation_id = observation_ids.front();\n    const auto element_id_var =\n        volume_file.get_tensor_component(observation_id, \"ElementId\").data;\n    const auto block_id_var =\n        volume_file.get_tensor_component(observation_id, \"BlockId\").data;\n    const auto& element_id = get<0>(element_id_var);\n    const auto& block_id = get<0>(block_id_var);\n    // 2x2 element (written as 2D) has 1 quad cell\n    CHECK(element_id.size() == 1);\n    CHECK(block_id.size() == 1);\n    // Must parse as ElementId<3> (numerical dim), not ElementId<2> (file dim)\n    const auto expected_eid = ElementId<3>{grid_names.front()}.to_short_id();\n    CHECK(element_id[0] == approx(expected_eid));\n    CHECK(static_cast<uint64_t>(block_id[0]) == 0);\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n// Test that write_volume_data produces mixed-format connectivity with XDMF type\n// tags (9=Hexahedron) prepended to each cell's vertex indices.\nvoid test_mixed_connectivity_format() {\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.MixedConnectivity.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n  auto& volume_file =\n      h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n\n  // Write a single 2x2x2 element (1 hex cell)\n  volume_file.write_volume_data(\n      100, 1.0,\n      {{\"[B0,(L0I0,L0I0,L0I0)]\",\n        {TensorComponent{\"InertialCoordinates_x\",\n                         DataVector{0., 1., 0., 1., 0., 1., 0., 1.}},\n         TensorComponent{\"InertialCoordinates_y\",\n                         DataVector{0., 0., 1., 1., 0., 0., 1., 1.}},\n         TensorComponent{\"InertialCoordinates_z\",\n                         DataVector{0., 0., 0., 0., 1., 1., 1., 1.}}},\n        {2, 2, 2},\n        {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n         Spectral::Basis::Legendre},\n        {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n         Spectral::Quadrature::GaussLobatto}}});\n  h5_file.close_current_object();\n\n  const auto& vol_read = h5_file.get<h5::VolumeData>(\"/element_data\");\n  const auto connectivity_variant =\n      vol_read.get_tensor_component(100, \"connectivity\").data;\n  const auto& connectivity = get<0>(connectivity_variant);\n\n  // One hex cell: [9, 0, 1, 3, 2, 4, 5, 7, 6]\n  REQUIRE(connectivity.size() == 9);\n  CHECK(static_cast<int>(connectivity[0]) == 9);  // XDMF Hexahedron tag\n\n  // Write a 3x3x3 element (8 hex cells)\n  h5_file.close_current_object();\n  const std::string h5_file2(\"Unit.IO.H5.VolumeData.MixedConn3x3x3.h5\");\n  if (file_system::check_if_file_exists(h5_file2)) {\n    file_system::rm(h5_file2, true);\n  }\n  {\n    h5::H5File<h5::AccessType::ReadWrite> h5_file_second(h5_file2);\n    auto& volume_file_second =\n        h5_file_second.insert<h5::VolumeData>(\"/element_data\", version_number);\n    DataVector x(27, 0.0);\n    DataVector y(27, 0.0);\n    DataVector z(27, 0.0);\n    for (size_t k = 0; k < 3; ++k) {\n      for (size_t j = 0; j < 3; ++j) {\n        for (size_t i = 0; i < 3; ++i) {\n          const size_t idx = k * 9 + j * 3 + i;\n          x[idx] = static_cast<double>(i);\n          y[idx] = static_cast<double>(j);\n          z[idx] = static_cast<double>(k);\n        }\n      }\n    }\n    volume_file_second.write_volume_data(\n        200, 2.0,\n        {{\"[B0,(L0I0,L0I0,L0I0)]\",\n          {TensorComponent{\"InertialCoordinates_x\", x},\n           TensorComponent{\"InertialCoordinates_y\", y},\n           TensorComponent{\"InertialCoordinates_z\", z}},\n          {3, 3, 3},\n          {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n           Spectral::Basis::Legendre},\n          {Spectral::Quadrature::GaussLobatto,\n           Spectral::Quadrature::GaussLobatto,\n           Spectral::Quadrature::GaussLobatto}}});\n    h5_file_second.close_current_object();\n    const auto& volume_file_second_read =\n        h5_file_second.get<h5::VolumeData>(\"/element_data\");\n    const auto connectivity_3x3x3_variant =\n        volume_file_second_read.get_tensor_component(200, \"connectivity\").data;\n    const auto& connectivity_3x3x3 = get<0>(connectivity_3x3x3_variant);\n    // 8 cells * (1 tag + 8 vertices) = 72\n    CHECK(connectivity_3x3x3.size() == 72);\n    // Each group of 9 starts with tag 9\n    for (size_t i = 0; i < 8; ++i) {\n      CHECK(static_cast<int>(connectivity_3x3x3[i * 9]) == 9);\n    }\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  if (file_system::check_if_file_exists(h5_file2)) {\n    file_system::rm(h5_file2, true);\n  }\n}\n\n// Test that element_id and block_id datasets are written correctly.\nvoid test_element_id_and_block_id() {\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.ElementIdBlockId.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  const std::string name0 = \"[B0,(L1I0,L1I0,L1I0)]\";\n  const std::string name1 = \"[B3,(L1I1,L1I1,L1I1)]\";\n\n  h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n  auto& volume_file =\n      h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n  volume_file.write_volume_data(\n      300, 3.0,\n      {{name0,\n        {TensorComponent{\"S\", DataVector(8, 0.0)}},\n        {2, 2, 2},\n        {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n         Spectral::Basis::Legendre},\n        {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n         Spectral::Quadrature::GaussLobatto}},\n       {name1,\n        {TensorComponent{\"S\", DataVector(8, 1.0)}},\n        {2, 2, 2},\n        {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n         Spectral::Basis::Legendre},\n        {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n         Spectral::Quadrature::GaussLobatto}}});\n  h5_file.close_current_object();\n\n  const auto& volume_file_read = h5_file.get<h5::VolumeData>(\"/element_data\");\n\n  // Each 2x2x2 element has 1 hex cell\n  const auto element_id_var =\n      volume_file_read.get_tensor_component(300, \"ElementId\").data;\n  const auto block_id_var =\n      volume_file_read.get_tensor_component(300, \"BlockId\").data;\n  const auto& element_id = get<0>(element_id_var);\n  const auto& block_id = get<0>(block_id_var);\n\n  CHECK(element_id.size() == 2);\n  CHECK(block_id.size() == 2);\n\n  // element_id = hash of element name\n  const auto expected_element_id0 = ElementId<3>{name0}.to_short_id();\n  const auto expected_element_id1 = ElementId<3>{name1}.to_short_id();\n  CHECK(element_id[0] == approx(expected_element_id0));\n  CHECK(element_id[1] == approx(expected_element_id1));\n\n  // block_id = 0 for B0, 3 for B3\n  CHECK(static_cast<uint64_t>(block_id[0]) == 0);\n  CHECK(static_cast<uint64_t>(block_id[1]) == 3);\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\n// Test that non-standard grid names (e.g. \"AhA\") cause block_id to default\n// to 0 without crashing.\nvoid test_element_id_non_standard_names() {\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.NonStandardNames.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n  auto& volume_file =\n      h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n  volume_file.write_volume_data(\n      400, 4.0,\n      {{\"AhA\",\n        {TensorComponent{\"S\", DataVector(8, 0.0)}},\n        {2, 2, 2},\n        {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n         Spectral::Basis::Legendre},\n        {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n         Spectral::Quadrature::GaussLobatto}}});\n  h5_file.close_current_object();\n\n  const auto& volume_file_read = h5_file.get<h5::VolumeData>(\"/element_data\");\n  const auto block_id_var =\n      volume_file_read.get_tensor_component(400, \"BlockId\").data;\n  const auto& block_id = get<0>(block_id_var);\n  CHECK(block_id.size() == 1);\n  CHECK(static_cast<uint64_t>(block_id[0]) == 0);\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\n// Test that multiple elements with different extents produce correct cell\n// counts and per-element consistent element_id/block_id values.\nvoid test_mixed_connectivity_multi_element() {\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.MultiElement.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  const std::string name0 = \"[B0,(L0I0,L0I0,L0I0)]\";  // 2x2x2 → 1 cell\n  const std::string name1 = \"[B1,(L0I0,L0I0,L0I0)]\";  // 3x3x3 → 8 cells\n\n  h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n  auto& volume_file =\n      h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n  volume_file.write_volume_data(\n      500, 5.0,\n      {{name0,\n        {TensorComponent{\"S\", DataVector(8, 0.0)}},\n        {2, 2, 2},\n        {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n         Spectral::Basis::Legendre},\n        {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n         Spectral::Quadrature::GaussLobatto}},\n       {name1,\n        {TensorComponent{\"S\", DataVector(27, 0.0)}},\n        {3, 3, 3},\n        {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n         Spectral::Basis::Legendre},\n        {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n         Spectral::Quadrature::GaussLobatto}}});\n  h5_file.close_current_object();\n\n  const auto& volume_file_read = h5_file.get<h5::VolumeData>(\"/element_data\");\n\n  // 1 + 8 = 9 total cells\n  const auto element_id_var =\n      volume_file_read.get_tensor_component(500, \"ElementId\").data;\n  const auto block_id_var =\n      volume_file_read.get_tensor_component(500, \"BlockId\").data;\n  const auto& element_id = get<0>(element_id_var);\n  const auto& block_id = get<0>(block_id_var);\n  CHECK(element_id.size() == 9);\n  CHECK(block_id.size() == 9);\n\n  // First 1 cell → element 0\n  const auto expected_element_id0 = ElementId<3>{name0}.to_short_id();\n  const auto expected_element_id1 = ElementId<3>{name1}.to_short_id();\n  CHECK(element_id[0] == approx(expected_element_id0));\n  for (size_t i = 1; i < 9; ++i) {\n    CHECK(element_id[i] == approx(expected_element_id1));\n  }\n  CHECK(static_cast<uint64_t>(block_id[0]) == 0);\n  for (size_t i = 1; i < 9; ++i) {\n    CHECK(static_cast<uint64_t>(block_id[i]) == 1);\n  }\n\n  // Connectivity length: 1*9 + 8*9 = 81\n  const auto connectivity_var =\n      volume_file_read.get_tensor_component(500, \"connectivity\").data;\n  const auto& connectivity = get<0>(connectivity_var);\n  CHECK(connectivity.size() == 81);\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\n\n// Test 2D annulus: Legendre+Fourier basis, extents {3,5}.\n// Standard quads: (n_r-1)*(n_phi-1) = 2*4 = 8\n// Wrapping quads (Fourier seam): n_r-1 = 2\n// Total: 10 cells, connectivity size = 10*5 = 50\nvoid test_annulus() {\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.Annulus.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const std::string grid_name{\"Annulus\"};\n  const std::vector<Spectral::Basis> bases{Spectral::Basis::Legendre,\n                                           Spectral::Basis::Fourier};\n  const std::vector<Spectral::Quadrature> quadratures{\n      Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Equiangular};\n  const std::vector<size_t> extents{3, 5};  // n_r=3, n_phi=5, 15 points\n  const std::vector<TensorComponent> tensor_components{\n      {\"InertialCoordinates_x\", DataVector(15, 0.0)},\n      {\"InertialCoordinates_y\", DataVector(15, 0.0)}};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_name};\n    auto& volume_file =\n        h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    volume_file.write_volume_data(\n        12345, 1.0,\n        std::vector<ElementVolumeData>{\n            {grid_name, tensor_components, extents, bases, quadratures}});\n    h5_file.close_current_object();\n  }\n\n  // clang-format off\n  // idx(ir, ip) = ir + 3*ip  (n_r=3, n_phi=5)\n  // 8 standard quads (type=5, 5 values each), then 2 wrapping quads (type=5)\n  DataVector expected_connectivity = {\n    // Standard quads (outer loop: first/r cells, inner loop: second/phi cells)\n    5.,  0.,  1.,  4.,  3.,   // (ir=0..1, ip=0..1)\n    5.,  3.,  4.,  7.,  6.,   // (ir=0..1, ip=1..2)\n    5.,  6.,  7., 10.,  9.,   // (ir=0..1, ip=2..3)\n    5.,  9., 10., 13., 12.,   // (ir=0..1, ip=3..4)\n    5.,  1.,  2.,  5.,  4.,   // (ir=1..2, ip=0..1)\n    5.,  4.,  5.,  8.,  7.,   // (ir=1..2, ip=1..2)\n    5.,  7.,  8., 11., 10.,   // (ir=1..2, ip=2..3)\n    5., 10., 11., 14., 13.,   // (ir=1..2, ip=3..4)\n    // Wrapping quads: connect ip=(n_phi-1) back to ip=0\n    5.,  0.,  1., 13., 12.,   // j=0: ir=0,1 at ip=4 → ip=0\n    5.,  1.,  2., 14., 13.};  // j=1: ir=1,2 at ip=4 → ip=0\n  // clang-format on\n\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> h5_file{h5_file_name};\n    const auto& volume_file =\n        h5_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto connectivity_var =\n        volume_file.get_tensor_component(12345, \"connectivity\").data;\n    const auto& connectivity = get<0>(connectivity_var);\n    CHECK(connectivity == expected_connectivity);\n\n    constexpr size_t expected_cells = 10;\n    const auto element_id_var =\n        volume_file.get_tensor_component(12345, \"ElementId\").data;\n    const auto& element_id = get<0>(element_id_var);\n    CHECK(element_id.size() == expected_cells);\n\n    const auto block_id_var =\n        volume_file.get_tensor_component(12345, \"BlockId\").data;\n    const auto& block_id = get<0>(block_id_var);\n    CHECK(block_id.size() == expected_cells);\n\n    // All cells belong to the same element and block 0\n    const auto expected_eid =\n        static_cast<double>(std::hash<std::string>{}(grid_name));\n    for (size_t i = 0; i < expected_cells; ++i) {\n      CHECK(element_id[i] == approx(expected_eid));\n      CHECK(block_id[i] == 0.0);\n    }\n    h5_file.close_current_object();\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\n// Test mixed disk+annulus: one ZernikeB2 disk element and one Fourier annulus.\n// This mirrors the AngularDisk domain that triggered the original bug.\n// Element 0 \"InnerDisk\": ZernikeB2+ZernikeB2, extents {2,5} → 8 cells\n// Element 1 \"[B1,(L0I0)]\": Legendre+Fourier, extents {3,5} → 10 cells\n// Total: 18 cells\nvoid test_annulus_disk_mixed() {\n  const std::string h5_file_name(\n      \"Unit.IO.H5.VolumeData.AnnulusDiskMixed.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  const std::string name0 = \"InnerDisk\";      // block_id = 0 (no [B...])\n  const std::string name1 = \"[B1,(L0I0,L0I0)]\";  // block_id = 1\n\n  // Element 0: ZernikeB2 disk, 2*5=10 points\n  const std::vector<Spectral::Basis> disk_bases{2, Spectral::Basis::ZernikeB2};\n  const std::vector<Spectral::Quadrature> disk_quadratures{\n      {Spectral::Quadrature::GaussRadauUpper,\n       Spectral::Quadrature::Equiangular}};\n  // Element 1: Legendre+Fourier annulus, 3*5=15 points\n  const std::vector<Spectral::Basis> ann_bases{Spectral::Basis::Legendre,\n                                               Spectral::Basis::Fourier};\n  const std::vector<Spectral::Quadrature> ann_quadratures{\n      Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Equiangular};\n\n  h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n  auto& volume_file =\n      h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n  volume_file.write_volume_data(\n      5000, 2.0,\n      {{name0,\n        {TensorComponent{\"InertialCoordinates_x\", DataVector(10, 0.0)},\n         TensorComponent{\"InertialCoordinates_y\", DataVector(10, 0.0)}},\n        {2, 5},\n        disk_bases,\n        disk_quadratures},\n       {name1,\n        {TensorComponent{\"InertialCoordinates_x\", DataVector(15, 0.0)},\n         TensorComponent{\"InertialCoordinates_y\", DataVector(15, 0.0)}},\n        {3, 5},\n        ann_bases,\n        ann_quadratures}});\n  h5_file.close_current_object();\n\n  const auto& volume_file_read = h5_file.get<h5::VolumeData>(\"/element_data\");\n\n  // InnerDisk {2,5}: 4 standard quads + 1 wrap quad + 3 disk triangles = 8\n  // [B1,(L0I0)] {3,5}: 8 standard quads + 2 wrap quads = 10\n  constexpr size_t num_cells_0 = 8;\n  constexpr size_t num_cells_1 = 10;\n  constexpr size_t total_cells = num_cells_0 + num_cells_1;\n\n  const auto element_id_var =\n      volume_file_read.get_tensor_component(5000, \"ElementId\").data;\n  const auto block_id_var =\n      volume_file_read.get_tensor_component(5000, \"BlockId\").data;\n  const auto& element_id = get<0>(element_id_var);\n  const auto& block_id = get<0>(block_id_var);\n\n  CHECK(element_id.size() == total_cells);\n  CHECK(block_id.size() == total_cells);\n\n  const auto expected_eid0 =\n      static_cast<double>(std::hash<std::string>{}(name0));\n  const auto expected_eid1 = ElementId<2>{name1}.to_short_id();\n  CAPTURE(num_cells_0);\n  CAPTURE(num_cells_1);\n  for (size_t i = 0; i < num_cells_0; ++i) {\n    CAPTURE(i);\n    CHECK(element_id[i] == approx(expected_eid0));\n    CHECK(block_id[i] == 0.0);\n  }\n  for (size_t i = num_cells_0; i < total_cells; ++i) {\n    CAPTURE(i);\n    CHECK(element_id[i] == expected_eid1);\n    CHECK(block_id[i] == 1.0);\n  }\n\n  // Connectivity sizes:\n  // InnerDisk {2,5}: 4*5 + 1*5 + 3*4 = 20+5+12 = 37\n  // Annulus {3,5}:   8*5 + 2*5       = 40+10    = 50\n  // Total: 87\n  const auto connectivity_var =\n      volume_file_read.get_tensor_component(5000, \"connectivity\").data;\n  const auto& connectivity = get<0>(connectivity_var);\n  CHECK(connectivity.size() == 87);\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n// Test disk connectivity with minimum n_phi=3 (edge case).\n// extents {2,3}: 2 standard quads + 1 wrapping quad + 1 center triangle = 4\n// cells.\nvoid test_disk_min_phi() {\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.DiskMinPhi.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const std::string grid_name{\"DiskMin\"};\n  const std::vector<Spectral::Basis> bases{2, Spectral::Basis::ZernikeB2};\n  const std::vector<Spectral::Quadrature> quadratures{\n      {Spectral::Quadrature::GaussRadauUpper,\n       Spectral::Quadrature::Equiangular}};\n  const std::vector<size_t> extents{2, 3};  // n_r=2, n_phi=3\n  // 2*3 = 6 points\n  const std::vector<TensorComponent> tensor_components{\n      {\"InertialCoordinates_x\", DataVector(6, 0.0)},\n      {\"InertialCoordinates_y\", DataVector(6, 0.0)}};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> disk_file{h5_file_name};\n    auto& volume_file =\n        disk_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    volume_file.write_volume_data(\n        99999, 0.5,\n        std::vector<ElementVolumeData>{\n            {grid_name, tensor_components, extents, bases, quadratures}});\n    disk_file.close_current_object();\n  }\n\n  // clang-format off\n  // 2 standard quads + 1 wrapping quad (type=5) + 1 disk triangle (type=4)\n  DataVector expected_connectivity = {\n     5.,  0.,  1.,  3.,  2.,   // Quad (phi=0→1)\n     5.,  2.,  3.,  5.,  4.,   // Quad (phi=1→2)\n     5.,  0.,  1.,  5.,  4.,   // wrapping Quad\n     4.,  0.,  2.,  4.};       // Triangle (center)\n  // clang-format on\n\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> disk_file{h5_file_name};\n    const auto& volume_file =\n        disk_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto h5_connectivity =\n        volume_file.get_tensor_component(99999, \"connectivity\").data;\n    const auto& conn = get<0>(h5_connectivity);\n    CHECK(conn == expected_connectivity);\n\n    // Verify element_id/block_id: 4 cells, all from \"DiskMin\"\n    const auto element_id_var =\n        volume_file.get_tensor_component(99999, \"ElementId\").data;\n    const auto& element_id = get<0>(element_id_var);\n    CHECK(element_id.size() == 4);\n    const auto block_id_var =\n        volume_file.get_tensor_component(99999, \"BlockId\").data;\n    const auto& block_id = get<0>(block_id_var);\n    CHECK(block_id.size() == 4);\n    disk_file.close_current_object();\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\n// Test disk with two elements having different extents.\n// Element \"Disk0\": {2,5} → 4+1+3=8 cells.\n// Element \"Disk1\": {3,9} → 16+2+7=25 cells.\n// Total: 33 cells.\nvoid test_disk_multi_element() {\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.DiskMulti.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  const std::string name0 = \"Disk0\";  // block_id = 0\n  const std::string name1 = \"Disk1\";  // block_id = 0\n  const std::vector<Spectral::Basis> bases{2, Spectral::Basis::ZernikeB2};\n  const std::vector<Spectral::Quadrature> quadratures{\n      {Spectral::Quadrature::GaussRadauUpper,\n       Spectral::Quadrature::Equiangular}};\n\n  h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n  auto& volume_file =\n      h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n  // Element 0: {2,5} → 10 points\n  volume_file.write_volume_data(\n      1000, 1.0,\n      {{name0,\n        {TensorComponent{\"InertialCoordinates_x\", DataVector(10, 0.0)},\n         TensorComponent{\"InertialCoordinates_y\", DataVector(10, 0.0)}},\n        {2, 5},\n        bases,\n        quadratures},\n       {name1,\n        {TensorComponent{\"InertialCoordinates_x\", DataVector(27, 0.0)},\n         TensorComponent{\"InertialCoordinates_y\", DataVector(27, 0.0)}},\n        {3, 9},\n        bases,\n        quadratures}});\n  h5_file.close_current_object();\n\n  const auto& volume_file_read = h5_file.get<h5::VolumeData>(\"/element_data\");\n\n  // Element 0: (2-1)*(5-1) + (2-1) + 3 = 4+1+3 = 8 cells\n  // Element 1: (3-1)*(9-1) + (3-1) + 7 = 16+2+7 = 25 cells\n  constexpr size_t num_cells_0 = 8;\n  constexpr size_t num_cells_1 = 25;\n  constexpr size_t total_cells = num_cells_0 + num_cells_1;\n\n  const auto element_id_var =\n      volume_file_read.get_tensor_component(1000, \"ElementId\").data;\n  const auto block_id_var =\n      volume_file_read.get_tensor_component(1000, \"BlockId\").data;\n  const auto& element_id = get<0>(element_id_var);\n  const auto& block_id = get<0>(block_id_var);\n  CHECK(element_id.size() == total_cells);\n  CHECK(block_id.size() == total_cells);\n\n  const auto expected_eid0 =\n      static_cast<double>(std::hash<std::string>{}(name0));\n  const auto expected_eid1 =\n      static_cast<double>(std::hash<std::string>{}(name1));\n  // First num_cells_0 cells belong to Disk0\n  for (size_t i = 0; i < num_cells_0; ++i) {\n    CHECK(element_id[i] == approx(expected_eid0));\n    CHECK(block_id[i] == 0.0);\n  }\n  // Next num_cells_1 cells belong to Disk1\n  for (size_t i = num_cells_0; i < total_cells; ++i) {\n    CHECK(element_id[i] == approx(expected_eid1));\n    CHECK(block_id[i] == 0.0);\n  }\n\n  // Connectivity size:\n  // Disk0 {2,5}: 4 quads×5 + 1 wrap×5 + 3 tri×4 = 20+5+12 = 37\n  // Disk1 {3,9}: 16 quads×5 + 2 wrap×5 + 7 tri×4 = 80+10+28 = 118\n  // Total: 37 + 118 = 155\n  const auto connectivity_var =\n      volume_file_read.get_tensor_component(1000, \"connectivity\").data;\n  const auto& connectivity = get<0>(connectivity_var);\n\n  // clang-format off\n  // Disk0: n_r=2, n_phi=5, element_start=0, 10 points (indices 0-9)\n  // point(ir, iphi) = iphi * 2 + ir\n  // Disk1: n_r=3, n_phi=9, element_start=10, 27 points (indices 10-36)\n  // point(ir, iphi) = 10 + iphi * 3 + ir\n  const DataVector expected_connectivity = {\n    // --- Disk0 standard quads (ir=0, jphi=0..3) ---\n    5.,  0.,  1.,  3.,  2.,\n    5.,  2.,  3.,  5.,  4.,\n    5.,  4.,  5.,  7.,  6.,\n    5.,  6.,  7.,  9.,  8.,\n    // --- Disk0 wrapping quad (j=0) ---\n    5.,  0.,  1.,  9.,  8.,\n    // --- Disk0 center triangles (ring [0,2,4,6,8]) ---\n    4.,  0.,  2.,  4.,\n    4.,  4.,  6.,  8.,\n    4.,  0.,  4.,  8.,\n    // --- Disk1 standard quads (ir=0, jphi=0..7) ---\n    5., 10., 11., 14., 13.,\n    5., 13., 14., 17., 16.,\n    5., 16., 17., 20., 19.,\n    5., 19., 20., 23., 22.,\n    5., 22., 23., 26., 25.,\n    5., 25., 26., 29., 28.,\n    5., 28., 29., 32., 31.,\n    5., 31., 32., 35., 34.,\n    // --- Disk1 standard quads (ir=1, jphi=0..7) ---\n    5., 11., 12., 15., 14.,\n    5., 14., 15., 18., 17.,\n    5., 17., 18., 21., 20.,\n    5., 20., 21., 24., 23.,\n    5., 23., 24., 27., 26.,\n    5., 26., 27., 30., 29.,\n    5., 29., 30., 33., 32.,\n    5., 32., 33., 36., 35.,\n    // --- Disk1 wrapping quads (j=0,1) ---\n    5., 10., 11., 35., 34.,\n    5., 11., 12., 36., 35.,\n    // --- Disk1 center triangles (ring [10,13,16,19,22,25,28,31,34]) ---\n    4., 10., 13., 16.,\n    4., 16., 19., 22.,\n    4., 22., 25., 28.,\n    4., 28., 31., 34.,\n    4., 10., 16., 22.,\n    4., 22., 28., 34.,\n    4., 10., 22., 34.};\n  // clang-format on\n\n  CHECK(connectivity == expected_connectivity);\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_spherical_shell() {\n  // 3D spherical shell: n_r=2, n_theta=3, n_phi=5\n  // Basis: {Legendre, SphericalHarmonic, SphericalHarmonic}\n  // Quadrature: {GaussLobatto, Gauss, Equiangular}\n  // Standard hexahedra: (n_r-1)*(n_theta-1)*(n_phi-1) = 1*2*4 = 8\n  // Phi-wrapping hexahedra: (n_r-1)*(n_theta-1) = 2\n  // Pole wedges: 2 poles * 1 layer * 3 wedges/layer = 6\n  // Total cells: 16\n  const std::string h5_file_name(\"Unit.IO.H5.VolumeData.SphericalShell.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const std::string grid_name{\"Shell\"};\n  const std::vector<Spectral::Basis> bases{Spectral::Basis::Legendre,\n                                           Spectral::Basis::SphericalHarmonic,\n                                           Spectral::Basis::SphericalHarmonic};\n  const std::vector<Spectral::Quadrature> quadratures{\n      Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n      Spectral::Quadrature::Equiangular};\n  const std::vector<size_t> extents{2, 3, 5};  // n_r=2, n_theta=3, n_phi=5\n  // 2*3*5 = 30 points\n  const std::vector<TensorComponent> tensor_components{\n      {\"InertialCoordinates_x\", DataVector(30, 0.0)},\n      {\"InertialCoordinates_y\", DataVector(30, 0.0)},\n      {\"InertialCoordinates_z\", DataVector(30, 0.0)}};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_name};\n    auto& volume_file =\n        h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    volume_file.write_volume_data(\n        11111, 2.5,\n        std::vector<ElementVolumeData>{\n            {grid_name, tensor_components, extents, bases, quadratures}});\n    h5_file.close_current_object();\n  }\n\n  // clang-format off\n  // idx(ir, it, ip) = ir + 2*it + 6*ip  (n_r=2, n_theta=3)\n  // 8 standard hexahedra (type=9, 9 values each)\n  // 2 phi-wrapping hexahedra (type=9, 9 values each)\n  // 3 top-pole wedges (type=8, 7 values each)\n  // 3 bottom-pole wedges (type=8, 7 values each)\n  DataVector expected_connectivity = {\n    // Standard hexahedra (it=0, ip=0..3)\n    9.,  0.,  1.,  3.,  2.,  6.,  7.,  9.,  8.,\n    9.,  6.,  7.,  9.,  8., 12., 13., 15., 14.,\n    9., 12., 13., 15., 14., 18., 19., 21., 20.,\n    9., 18., 19., 21., 20., 24., 25., 27., 26.,\n    // Standard hexahedra (it=1, ip=0..3)\n    9.,  2.,  3.,  5.,  4.,  8.,  9., 11., 10.,\n    9.,  8.,  9., 11., 10., 14., 15., 17., 16.,\n    9., 14., 15., 17., 16., 20., 21., 23., 22.,\n    9., 20., 21., 23., 22., 26., 27., 29., 28.,\n    // Phi-wrapping hexahedra (it=0, it=1): ip=n_phi-1 to ip=0\n    9., 24., 25., 27., 26.,  0.,  1.,  3.,  2.,\n    9., 26., 27., 29., 28.,  2.,  3.,  5.,  4.,\n    // Top-pole wedges (it_pole=0, ir=0): halving of ring [0,6,12,18,24]\n    8.,  0.,  6., 12.,  1.,  7., 13.,\n    8., 12., 18., 24., 13., 19., 25.,\n    8.,  0., 12., 24.,  1., 13., 25.,\n    // Bottom-pole wedges (it_pole=2, ir=0): reversed ring [28,22,16,10,4]\n    8., 28., 22., 16., 29., 23., 17.,\n    8., 16., 10.,  4., 17., 11.,  5.,\n    8., 28., 16.,  4., 29., 17.,  5.};\n  // clang-format on\n\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> h5_file{h5_file_name};\n    const auto& volume_file =\n        h5_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto h5_connectivity =\n        volume_file.get_tensor_component(11111, \"connectivity\").data;\n    const auto& conn = get<0>(h5_connectivity);\n    CHECK(conn == expected_connectivity);\n\n    // Verify element_id and block_id: 16 cells\n    constexpr size_t expected_num_cells = 16;\n    const auto element_id_var =\n        volume_file.get_tensor_component(11111, \"ElementId\").data;\n    const auto& element_id = get<0>(element_id_var);\n    CHECK(element_id.size() == expected_num_cells);\n\n    const auto block_id_var =\n        volume_file.get_tensor_component(11111, \"BlockId\").data;\n    const auto& block_id = get<0>(block_id_var);\n    CHECK(block_id.size() == expected_num_cells);\n\n    const auto expected_eid = static_cast<double>(\n        static_cast<uint64_t>(std::hash<std::string>{}(grid_name)));\n    for (size_t i = 0; i < expected_num_cells; ++i) {\n      CHECK(element_id[i] == expected_eid);\n      CHECK(block_id[i] == 0.0);\n    }\n    h5_file.close_current_object();\n  }\n\n  // Verify get_data_by_element roundtrip\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> h5_file{h5_file_name};\n    const auto& volume_file =\n        h5_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto all_data = volume_file.get_data_by_element(\n        std::nullopt, std::nullopt,\n        std::vector<std::string>{\"InertialCoordinates_x\",\n                                 \"InertialCoordinates_y\",\n                                 \"InertialCoordinates_z\"});\n    REQUIRE(all_data.size() == 1);\n    const auto& [obs_id_rt, obs_val_rt, elements] = all_data[0];\n    CHECK(obs_val_rt == approx(2.5));\n    REQUIRE(elements.size() == 1);\n    const auto& elem = elements[0];\n    CHECK(elem.element_name == grid_name);\n    CHECK(elem.extents == extents);\n    CHECK(elem.basis == bases);\n    CHECK(elem.quadrature == quadratures);\n    REQUIRE(elem.tensor_components.size() == 3);\n    h5_file.close_current_object();\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_spherical_shell_min_phi() {\n  // 3D spherical shell: n_r=2, n_theta=3, n_phi=3 (minimum phi)\n  // Standard hexahedra: (n_r-1)*(n_theta-1)*(n_phi-1) = 1*2*2 = 4\n  // Phi-wrapping hexahedra: (n_r-1)*(n_theta-1) = 2\n  // Pole wedges: 2 poles * 1 layer * 1 wedge/layer = 2\n  // Total cells: 8\n  const std::string h5_file_name(\n      \"Unit.IO.H5.VolumeData.SphericalShellMinPhi.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n  const std::string grid_name{\"ShellMin\"};\n  const std::vector<Spectral::Basis> bases{Spectral::Basis::Legendre,\n                                           Spectral::Basis::SphericalHarmonic,\n                                           Spectral::Basis::SphericalHarmonic};\n  const std::vector<Spectral::Quadrature> quadratures{\n      Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n      Spectral::Quadrature::Equiangular};\n  const std::vector<size_t> extents{2, 3, 3};  // n_r=2, n_theta=3, n_phi=3\n  // 2*3*3 = 18 points\n  const std::vector<TensorComponent> tensor_components{\n      {\"InertialCoordinates_x\", DataVector(18, 0.0)},\n      {\"InertialCoordinates_y\", DataVector(18, 0.0)},\n      {\"InertialCoordinates_z\", DataVector(18, 0.0)}};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_name};\n    auto& volume_file =\n        h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    volume_file.write_volume_data(\n        22222, 3.0,\n        std::vector<ElementVolumeData>{\n            {grid_name, tensor_components, extents, bases, quadratures}});\n    h5_file.close_current_object();\n  }\n\n  // clang-format off\n  // idx(ir, it, ip) = ir + 2*it + 6*ip  (n_r=2, n_theta=3)\n  DataVector expected_connectivity = {\n    // Standard hexahedra\n    9.,  0.,  1.,  3.,  2.,  6.,  7.,  9.,  8.,\n    9.,  6.,  7.,  9.,  8., 12., 13., 15., 14.,\n    9.,  2.,  3.,  5.,  4.,  8.,  9., 11., 10.,\n    9.,  8.,  9., 11., 10., 14., 15., 17., 16.,\n    // Phi-wrapping hexahedra\n    9., 12., 13., 15., 14.,  0.,  1.,  3.,  2.,\n    9., 14., 15., 17., 16.,  2.,  3.,  5.,  4.,\n    // Top-pole wedge (it_pole=0, ir=0): ring [0,6,12]\n    8.,  0.,  6., 12.,  1.,  7., 13.,\n    // Bottom-pole wedge (it_pole=2, ir=0): reversed ring [16,10,4]\n    8., 16., 10.,  4., 17., 11.,  5.};\n  // clang-format on\n\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> h5_file{h5_file_name};\n    const auto& volume_file =\n        h5_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto h5_connectivity =\n        volume_file.get_tensor_component(22222, \"connectivity\").data;\n    const auto& conn = get<0>(h5_connectivity);\n    CHECK(conn == expected_connectivity);\n\n    // Verify element_id/block_id: 8 cells\n    const auto element_id_var =\n        volume_file.get_tensor_component(22222, \"ElementId\").data;\n    const auto& element_id = get<0>(element_id_var);\n    CHECK(element_id.size() == 8);\n    const auto block_id_var =\n        volume_file.get_tensor_component(22222, \"BlockId\").data;\n    const auto& block_id = get<0>(block_id_var);\n    CHECK(block_id.size() == 8);\n    h5_file.close_current_object();\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nvoid test_spherical_shell_multi_element() {\n  // Regression test: when a SphericalHarmonic shell element is NOT the first\n  // element in an observation (non-zero point offset), the extra connectivity\n  // (phi-wrapping hexahedra and pole-cap wedges) must use global point indices.\n  // Prior to the fix, local indices (0-based) were used, which caused the extra\n  // cells to reference points from previous elements instead of the shell.\n  //\n  // Element 0: Legendre {2,2,2} = 8 points, offset=0\n  // Element 1: SphericalHarmonic shell {2,3,3} = 18 points, offset=8\n  const std::string h5_file_name(\n      \"Unit.IO.H5.VolumeData.SphericalShellMulti.h5\");\n  const uint32_t version_number = 4;\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  const std::string name0 = \"[B0,(L0I0,L0I0,L0I0)]\";  // Legendre block 0\n  const std::string name1 = \"[B1,(L0I0,L0I0,L0I0)]\";  // SH shell block 1\n\n  const std::vector<Spectral::Basis> leg_bases{3, Spectral::Basis::Legendre};\n  const std::vector<Spectral::Quadrature> leg_quads{\n      3, Spectral::Quadrature::GaussLobatto};\n  const std::vector<size_t> leg_extents{2, 2, 2};\n\n  const std::vector<Spectral::Basis> sh_bases{\n      Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n      Spectral::Basis::SphericalHarmonic};\n  const std::vector<Spectral::Quadrature> sh_quads{\n      Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n      Spectral::Quadrature::Equiangular};\n  const std::vector<size_t> sh_extents{2, 3, 3};\n\n  {\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_name};\n    auto& volume_file =\n        h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n    volume_file.write_volume_data(\n        55555, 5.0,\n        std::vector<ElementVolumeData>{\n            {name0,\n             {TensorComponent{\"InertialCoordinates_x\", DataVector(8, 0.0)},\n              TensorComponent{\"InertialCoordinates_y\", DataVector(8, 0.0)},\n              TensorComponent{\"InertialCoordinates_z\", DataVector(8, 0.0)}},\n             leg_extents,\n             leg_bases,\n             leg_quads},\n            {name1,\n             {TensorComponent{\"InertialCoordinates_x\", DataVector(18, 0.0)},\n              TensorComponent{\"InertialCoordinates_y\", DataVector(18, 0.0)},\n              TensorComponent{\"InertialCoordinates_z\", DataVector(18, 0.0)}},\n             sh_extents,\n             sh_bases,\n             sh_quads}});\n    h5_file.close_current_object();\n  }\n\n  // clang-format off\n  // Element 0: Legendre {2,2,2}, idx_leg = ir + 2*it + 4*ip, offset=0\n  //   1 hex × 9 values = 9 values\n  // Element 1: SH {2,3,3}, idx_sh = 8 + ir + 2*it + 6*ip, offset=8\n  //   compute_cells order: it outer, ip inner (theta outer, phi inner)\n  //   4 std hex + 2 wrap hex + 1 top wedge + 1 bot wedge = 8 cells\n  //   4×9 + 2×9 + 1×7 + 1×7 = 68 values\n  // Total: 9 + 68 = 77 values, 9 cells\n  DataVector expected_connectivity = {\n    // Legendre hex (ir=0,it=0,ip=0)\n    9.,  0., 1., 3., 2., 4., 5., 7., 6.,\n    // SH standard hexahedra: it outer (0..1), ip inner (0..1), +8 offset\n    // it=0,ip=0: (8,9,11,10,14,15,17,16)\n    9.,  8.,  9., 11., 10., 14., 15., 17., 16.,\n    // it=0,ip=1: (14,15,17,16,20,21,23,22)\n    9., 14., 15., 17., 16., 20., 21., 23., 22.,\n    // it=1,ip=0: (10,11,13,12,16,17,19,18)\n    9., 10., 11., 13., 12., 16., 17., 19., 18.,\n    // it=1,ip=1: (16,17,19,18,22,23,25,24)\n    9., 16., 17., 19., 18., 22., 23., 25., 24.,\n    // SH phi-wrapping hexahedra (ip=2 -> ip=0)\n    9., 20., 21., 23., 22.,  8.,  9., 11., 10.,\n    9., 22., 23., 25., 24., 10., 11., 13., 12.,\n    // SH top-pole wedge (it_pole=0): ring [8,14,20] / [9,15,21]\n    8.,  8., 14., 20.,  9., 15., 21.,\n    // SH bottom-pole wedge (it_pole=2, reversed): ring [24,18,12] / [25,19,13]\n    8., 24., 18., 12., 25., 19., 13.};\n  // clang-format on\n\n  {\n    const h5::H5File<h5::AccessType::ReadOnly> h5_file{h5_file_name};\n    const auto& volume_file =\n        h5_file.get<h5::VolumeData>(\"/element_data\", version_number);\n    const auto h5_connectivity =\n        volume_file.get_tensor_component(55555, \"connectivity\").data;\n    const auto& conn = get<0>(h5_connectivity);\n    CHECK(conn == expected_connectivity);\n\n    // Verify element_id/block_id: 9 cells total\n    const auto element_id_var =\n        volume_file.get_tensor_component(55555, \"ElementId\").data;\n    const auto& element_id = get<0>(element_id_var);\n    CHECK(element_id.size() == 9);\n\n    const auto block_id_var =\n        volume_file.get_tensor_component(55555, \"BlockId\").data;\n    const auto& block_id = get<0>(block_id_var);\n    CHECK(block_id.size() == 9);\n    // First cell belongs to block 0 (Legendre)\n    CHECK(block_id[0] == 0.0);\n    // Cells 1..8 belong to block 1 (SH shell)\n    for (size_t i = 1; i < 9; ++i) {\n      CHECK(block_id[i] == 1.0);\n    }\n    h5_file.close_current_object();\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\n}  // namespace\n\n// [[TimeOut, 20]]\nSPECTRE_TEST_CASE(\"Unit.IO.H5.VolumeData\", \"[Unit][IO][H5]\") {\n  test<DataVector>();\n  test<std::vector<float>>();\n  test_cartoon();\n  test_strahlkorper();\n  test_disk();\n  test_cylinder(true);\n  test_cylinder(false);\n  test_disk_min_phi();\n  test_disk_multi_element();\n  test_annulus();\n  test_annulus_disk_mixed();\n  test_spherical_shell();\n  test_spherical_shell_min_phi();\n  test_spherical_shell_multi_element();\n  test_extend_connectivity_data<1>();\n  test_extend_connectivity_data<2>();\n  test_extend_connectivity_data<3>();\n  test_mixed_connectivity_format();\n  test_element_id_and_block_id();\n  test_element_id_non_standard_names();\n  test_mixed_connectivity_multi_element();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string h5_file_name(\n            \"Unit.IO.H5.VolumeData.ComponentFormat.h5\");\n        const uint32_t version_number = 4;\n        if (file_system::check_if_file_exists(h5_file_name)) {\n          file_system::rm(h5_file_name, true);\n        }\n        h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n        auto& volume_file =\n            my_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n        volume_file.write_volume_data(\n            100, 10.0,\n            {{\"grid_name\",\n              {TensorComponent{\"grid_name/S\", DataVector{1.0, 2.0}}},\n              {2},\n              {Spectral::Basis::Legendre},\n              {Spectral::Quadrature::Gauss}}});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The expected format of the tensor component names is \"\n          \"'COMPONENT_NAME' but found a '/' in\"));\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string h5_file_name(\"Unit.IO.H5.VolumeData.WriteTwice.h5\");\n        const uint32_t version_number = 4;\n        if (file_system::check_if_file_exists(h5_file_name)) {\n          file_system::rm(h5_file_name, true);\n        }\n        h5::H5File<h5::AccessType::ReadWrite> my_file(h5_file_name);\n        auto& volume_file =\n            my_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n        volume_file.write_volume_data(\n            100, 10.0,\n            {{\"grid_name\",\n              {TensorComponent{\"S\", DataVector{1.0, 2.0}},\n               TensorComponent{\"S\", DataVector{1.0, 2.0}}},\n              {2},\n              {Spectral::Basis::Legendre},\n              {Spectral::Quadrature::Gauss}}});\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Trying to write tensor component 'S' which already exists in HDF5 \"\n          \"file in group 'element_data.vol/ObservationId100'\"));\n#endif\n\n  CHECK_THROWS_WITH(\n      []() {\n        const std::string h5_file_name(\n            \"Unit.IO.H5.VolumeData.FindNoObservationId.h5\");\n        const uint32_t version_number = 4;\n        if (file_system::check_if_file_exists(h5_file_name)) {\n          file_system::rm(h5_file_name, true);\n        }\n        h5::H5File<h5::AccessType::ReadWrite> h5_file(h5_file_name);\n        auto& volume_file =\n            h5_file.insert<h5::VolumeData>(\"/element_data\", version_number);\n        volume_file.write_volume_data(\n            100, 10.0,\n            {{\"grid_name\",\n              {TensorComponent{\"S\", DataVector{1.0, 2.0}}},\n              {2},\n              {Spectral::Basis::Legendre},\n              {Spectral::Quadrature::Gauss}}});\n        volume_file.find_observation_id(11.0);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"No observation with value\"));\n}\n"
  },
  {
    "path": "tests/Unit/IO/H5/Test_VolumeData.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre import Informer\nfrom spectre.DataStructures import DataVector\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\nfrom spectre.Spectral import Basis, Quadrature\n\n\nclass TestVolumeDataWriting(unittest.TestCase):\n    # Test Fixtures\n    def setUp(self):\n        # The tests in this class involve inserting vol files, the h5 file\n        # will be deleted and recreated for each test\n        self.file_name = os.path.join(\n            Informer.unit_test_build_path(), \"IO/TestVolumeDataWriting.h5\"\n        )\n        if os.path.isfile(self.file_name):\n            os.remove(self.file_name)\n        self.h5_file = spectre_h5.H5File(file_name=self.file_name, mode=\"a\")\n\n    def tearDown(self):\n        self.h5_file.close()\n        if os.path.isfile(self.file_name):\n            os.remove(self.file_name)\n\n    # Testing the VolumeData Insert Function\n    def test_insert_vol(self):\n        self.h5_file.insert_vol(path=\"/element_data\", version=0)\n        self.h5_file.close_current_object()\n        vol_file = self.h5_file.get_vol(path=\"/element_data\")\n        self.assertEqual(vol_file.get_version(), 0)\n\n    # Test the header was generated correctly\n    def test_vol_get_header(self):\n        self.h5_file.insert_vol(path=\"/element_data\", version=0)\n        self.h5_file.close_current_object()\n        vol_file = self.h5_file.get_vol(path=\"/element_data\")\n        self.assertEqual(vol_file.get_header()[0:20], \"#\\n# File created on \")\n\n\nclass TestVolumeData(unittest.TestCase):\n    # Test Fixtures\n    def setUp(self):\n        # The tests in this class use a volume data file written using\n        # the write_volume_data() function\n        self.file_name = os.path.join(\n            Informer.unit_test_build_path(), \"IO/TestVolumeData.h5\"\n        )\n\n        if os.path.isfile(self.file_name):\n            os.remove(self.file_name)\n\n        self.h5_file = spectre_h5.H5File(file_name=self.file_name, mode=\"a\")\n        self.tensor_component_data = np.random.rand(4, 8)\n        observation_ids = [0, 1]\n        observation_values = {0: 7.0, 1: 1.3}\n        grid_names = [\"[B0,(L0I0,L0I0,L1I0)]\", \"[B0,(L0I0,L0I0,L1I1)]\"]\n        basis = Basis.Legendre\n        quad = Quadrature.Gauss\n\n        # Insert .vol file to h5 file\n        self.h5_file.insert_vol(\"/element_data\", version=0)\n        self.h5_file.close_current_object()\n        self.vol_file = self.h5_file.get_vol(path=\"/element_data\")\n\n        # Set TensorComponent and ElementVolumeData to be written\n\n        self.element_vol_data_grid_1 = [\n            ElementVolumeData(\n                element_name=grid_names[0],\n                components=[\n                    TensorComponent(\n                        \"field_1\", DataVector(self.tensor_component_data[2 * i])\n                    ),\n                    TensorComponent(\n                        \"field_2\",\n                        DataVector(self.tensor_component_data[2 * i + 1]),\n                    ),\n                ],\n                extents=3 * [2],\n                basis=3 * [basis],\n                quadrature=3 * [quad],\n            )\n            for i, observation_id in enumerate(observation_ids)\n        ]\n\n        self.element_vol_data_grid_2 = [\n            ElementVolumeData(\n                element_name=grid_names[1],\n                components=[\n                    TensorComponent(\n                        \"field_1\",\n                        DataVector(self.tensor_component_data[2 * i + 1]),\n                    ),\n                    TensorComponent(\n                        \"field_2\", DataVector(self.tensor_component_data[2 * i])\n                    ),\n                ],\n                extents=3 * [2],\n                basis=3 * [basis],\n                quadrature=3 * [quad],\n            )\n            for i, observation_id in enumerate(observation_ids)\n        ]\n\n        # Write extents and tensor volume data to volfile\n\n        for i, observation_id in enumerate(observation_ids):\n            self.vol_file.write_volume_data(\n                observation_id,\n                observation_values[observation_id],\n                [\n                    self.element_vol_data_grid_1[i],\n                    self.element_vol_data_grid_2[i],\n                ],\n            )\n\n    def tearDown(self):\n        self.h5_file.close()\n        if os.path.isfile(self.file_name):\n            os.remove(self.file_name)\n\n    # Test that observation ids and values are retrieved correctly\n    def test_observation_id(self):\n        # Test observation Ids\n        obs_ids = set(self.vol_file.list_observation_ids())\n        expected_obs_ids = set([0, 1])\n        self.assertEqual(obs_ids, expected_obs_ids)\n        # Test observation values\n        expected_obs_values = {0: 7.0, 1: 1.3}\n        for obs_id in expected_obs_ids:\n            self.assertEqual(\n                self.vol_file.get_observation_value(observation_id=obs_id),\n                expected_obs_values[obs_id],\n            )\n\n    # Test to make sure information about the computation elements was found\n    def test_grids(self):\n        obs_id = self.vol_file.list_observation_ids()[0]\n        # Test grid names\n        grid_names = self.vol_file.get_grid_names(observation_id=obs_id)\n        expected_grid_names = [\"[B0,(L0I0,L0I0,L1I0)]\", \"[B0,(L0I0,L0I0,L1I1)]\"]\n        self.assertEqual(grid_names, expected_grid_names)\n        # Test extents\n        extents = self.vol_file.get_extents(observation_id=obs_id)\n        expected_extents = [[2, 2, 2], [2, 2, 2]]\n        self.assertEqual(extents, expected_extents)\n        # Test bases\n        bases = self.vol_file.get_bases(obs_id)\n        expected_bases = 2 * [3 * [Basis.Legendre]]\n        self.assertEqual(bases, expected_bases)\n        # Test quadratures\n        quadratures = self.vol_file.get_quadratures(obs_id)\n        expected_quadratures = 2 * [3 * [Quadrature.Gauss]]\n        self.assertEqual(quadratures, expected_quadratures)\n        # Test dimension\n        dim = self.vol_file.get_dimension()\n        self.assertEqual(dim, 3)\n\n    # Test that the tensor components, and tensor data  are retrieved correctly\n    def test_tensor_components(self):\n        obs_id = 0\n        # Test tensor component names\n        tensor_component_names = set(\n            self.vol_file.list_tensor_components(observation_id=obs_id)\n        )\n        expected_tensor_component_names = [\"field_1\", \"field_2\"]\n        self.assertEqual(\n            tensor_component_names, set(expected_tensor_component_names)\n        )\n        # Test tensor component data at specified obs_id\n        for i, expected_tensor_component_data in enumerate(\n            self.tensor_component_data[:2]\n        ):\n            npt.assert_almost_equal(\n                np.asarray(\n                    self.vol_file.get_tensor_component(\n                        observation_id=obs_id,\n                        tensor_component=expected_tensor_component_names[i],\n                    ).data\n                )[0:8],\n                expected_tensor_component_data,\n            )\n\n    def test_get_data_by_element(self):\n        obs_id = 0\n        volume_data = self.vol_file.get_data_by_element(None, None, None)\n        self.assertEqual(len(volume_data), 2)\n\n        # Check both grids at first observation time\n        self.assertEqual(volume_data[0][0], 1)\n        self.assertEqual(volume_data[0][1], 1.3)\n        self.assertEqual(\n            volume_data[0][2][0].element_name, \"[B0,(L0I0,L0I0,L1I0)]\"\n        )\n        self.assertEqual(\n            volume_data[0][2][0].basis, self.element_vol_data_grid_1[1].basis\n        )\n        self.assertEqual(\n            volume_data[0][2][0].quadrature,\n            self.element_vol_data_grid_1[1].quadrature,\n        )\n        self.assertEqual(\n            volume_data[0][2][0].extents,\n            self.element_vol_data_grid_1[1].extents,\n        )\n        self.assertEqual(\n            volume_data[0][2][0].tensor_components,\n            self.element_vol_data_grid_1[1].tensor_components,\n        )\n\n        self.assertEqual(\n            volume_data[0][2][1].element_name, \"[B0,(L0I0,L0I0,L1I1)]\"\n        )\n        self.assertEqual(\n            volume_data[0][2][1].basis, self.element_vol_data_grid_2[1].basis\n        )\n        self.assertEqual(\n            volume_data[0][2][1].quadrature,\n            self.element_vol_data_grid_2[1].quadrature,\n        )\n        self.assertEqual(\n            volume_data[0][2][1].extents,\n            self.element_vol_data_grid_2[1].extents,\n        )\n        self.assertEqual(\n            volume_data[0][2][1].tensor_components,\n            self.element_vol_data_grid_2[1].tensor_components,\n        )\n\n        # Check both grids at second observation time\n        self.assertEqual(volume_data[1][0], 0)\n        self.assertEqual(volume_data[1][1], 7.0)\n        self.assertEqual(\n            volume_data[1][2][0].element_name, \"[B0,(L0I0,L0I0,L1I0)]\"\n        )\n        self.assertEqual(\n            volume_data[1][2][0].basis, self.element_vol_data_grid_1[1].basis\n        )\n        self.assertEqual(\n            volume_data[1][2][0].quadrature,\n            self.element_vol_data_grid_1[1].quadrature,\n        )\n        self.assertEqual(\n            volume_data[1][2][0].extents,\n            self.element_vol_data_grid_1[1].extents,\n        )\n        self.assertEqual(\n            volume_data[1][2][0].tensor_components,\n            self.element_vol_data_grid_1[0].tensor_components,\n        )\n\n        self.assertEqual(\n            volume_data[1][2][1].element_name, \"[B0,(L0I0,L0I0,L1I1)]\"\n        )\n        self.assertEqual(\n            volume_data[1][2][1].basis, self.element_vol_data_grid_2[1].basis\n        )\n        self.assertEqual(\n            volume_data[1][2][1].quadrature,\n            self.element_vol_data_grid_2[1].quadrature,\n        )\n        self.assertEqual(\n            volume_data[1][2][1].extents,\n            self.element_vol_data_grid_2[1].extents,\n        )\n        self.assertEqual(\n            volume_data[1][2][1].tensor_components,\n            self.element_vol_data_grid_2[0].tensor_components,\n        )\n\n    # Test that the offset and length for certain grid is retrieved correctly\n    def test_offset_and_length_for_grid(self):\n        obs_id = self.vol_file.list_observation_ids()[0]\n        all_grid_names = self.vol_file.get_grid_names(observation_id=obs_id)\n        all_extents = self.vol_file.get_extents(observation_id=obs_id)\n        self.assertEqual(\n            spectre_h5.offset_and_length_for_grid(\n                grid_name=\"[B0,(L0I0,L0I0,L1I0)]\",\n                all_grid_names=all_grid_names,\n                all_extents=all_extents,\n            ),\n            (0, 8),\n        )\n\n    # Tests that ExtendConnectivity generates the connectivity dataset\n    # length correctly\n    def test_extend_connectivity_data_3D(self):\n        obs_ids = self.vol_file.list_observation_ids()\n        # Applies the ExtendConnectivity functionality to the volume file\n        self.vol_file.extend_connectivity_data_3d(obs_ids)\n        # Extracts the connectivity data from the volume file\n        h5_connectivity = self.vol_file.get_tensor_component(\n            obs_ids[0], \"connectivity\"\n        ).data\n        # 3 extended cells, each with 1 XDMF type tag + 8 vertex indices = 9\n        expected_connectivity_length = 27\n        self.assertEqual(expected_connectivity_length, len(h5_connectivity))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/IO/Importers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_DataImporter\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  Test_VolumeDataReaderActions.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainStructure\n  IO\n  Importers\n  Options\n  ParallelHelpers\n  Spectral\n  Utilities\n  )\n\nfunction(add_algorithm_test TEST_NAME DIM)\n  set(TEST_BASE ${TEST_NAME}${DIM}D)\n  set(CPP_NAME Test_${TEST_NAME})\n  set(EXECUTABLE_NAME ${CPP_NAME}${DIM}D)\n  set(TEST_IDENTIFIER Integration.Importers.${TEST_BASE})\n\n  add_spectre_executable(\n    ${EXECUTABLE_NAME}\n    EXCLUDE_FROM_ALL\n    ${CPP_NAME}.cpp\n    )\n  target_compile_definitions(\n    ${EXECUTABLE_NAME}\n    PRIVATE\n    DIM=${DIM}\n    )\n  target_link_libraries(\n    ${EXECUTABLE_NAME}\n    PRIVATE\n    Charmxx::main\n    Catch2::Catch2\n    DataStructures\n    DgSubcell\n    Domain\n    DomainCreators\n    DomainStructure\n    ErrorHandling\n    Importers\n    Informer\n    IO\n    Options\n    Parallel\n    SystemUtilities\n    Utilities\n    )\n\n  add_standalone_test(\n    \"${TEST_IDENTIFIER}\"\n    INPUT_FILE \"Test_${TEST_BASE}.yaml\")\nendfunction()\n\nadd_algorithm_test(\"VolumeDataReaderAlgorithm\" 1)\nadd_algorithm_test(\"VolumeDataReaderAlgorithm\" 2)\nadd_algorithm_test(\"VolumeDataReaderAlgorithm\" 3)\n"
  },
  {
    "path": "tests/Unit/IO/Importers/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n\nnamespace {\nstruct ExampleVolumeData {\n  static std::string name() { return \"Example\"; }\n  static constexpr Options::String help = \"Example volume data\";\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.IO.Importers.Tags\", \"[Unit][IO]\") {\n  TestHelpers::db::test_simple_tag<importers::Tags::RegisteredElements<3>>(\n      \"RegisteredElements\");\n  TestHelpers::db::test_simple_tag<importers::Tags::ElementDataAlreadyRead>(\n      \"ElementDataAlreadyRead\");\n  TestHelpers::db::test_simple_tag<\n      importers::Tags::ImporterOptions<ExampleVolumeData>>(\"VolumeData\");\n\n  Options::Parser<\n      tmpl::list<importers::Tags::ImporterOptions<ExampleVolumeData>>>\n      opts(\"\");\n  opts.parse(\n      \"Example:\\n\"\n      \"  VolumeData:\\n\"\n      \"    FileGlob: File.name\\n\"\n      \"    Subgroup: data.group\\n\"\n      \"    ObservationValue: 1.\\n\"\n      \"    ObservationValueEpsilon: 1e-9\\n\"\n      \"    ElementsAreIdentical: True\");\n  const auto& options =\n      opts.get<importers::Tags::ImporterOptions<ExampleVolumeData>>();\n  using tuples::get;\n  CHECK(get<importers::OptionTags::FileGlob>(options) == \"File.name\");\n  CHECK(get<importers::OptionTags::Subgroup>(options) == \"data.group\");\n  CHECK(std::get<double>(\n            get<importers::OptionTags::ObservationValue>(options)) == 1.);\n  const std::optional<double> obs_val_eps =\n      get<importers::OptionTags::ObservationValueEpsilon>(options);\n  CHECK(obs_val_eps.has_value());\n  CHECK(obs_val_eps.value() == 1.0e-9);\n  CHECK(get<importers::OptionTags::ElementsAreIdentical>(options));\n\n  CHECK(\n      std::get<importers::ObservationSelector>(\n          TestHelpers::test_option_tag<importers::OptionTags::ObservationValue>(\n              \"First\")) == importers::ObservationSelector::First);\n  CHECK(\n      std::get<importers::ObservationSelector>(\n          TestHelpers::test_option_tag<importers::OptionTags::ObservationValue>(\n              \"Last\")) == importers::ObservationSelector::Last);\n}\n"
  },
  {
    "path": "tests/Unit/IO/Importers/Test_VolumeDataReaderActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DgSubcell/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/GetActiveTag.hpp\"\n#include \"Evolution/DgSubcell/Tags/ActiveGrid.hpp\"\n#include \"Evolution/DgSubcell/Tags/Coordinates.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/Actions/ReceiveVolumeData.hpp\"\n#include \"IO/Importers/Actions/RegisterWithElementDataReader.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace {\n\nstruct VectorTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, 2>;\n  static std::string name() { return \"V\"; }\n};\n\nstruct TensorTag : db::SimpleTag {\n  using type = tnsr::ij<DataVector, 2>;\n  static std::string name() { return \"T\"; }\n};\n\nusing import_tags_list = tmpl::list<VectorTag, TensorTag>;\n\nstruct TestVolumeData {};\n\nusing ElementIdType = ElementId<2>;\n\ntemplate <typename Metavariables, bool AddSubcell>\nstruct MockElementArray {\n  using component_being_mocked = void;  // Not needed\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementIdType;\n  using extra_tags_for_subcell = tmpl::conditional_t<\n      AddSubcell,\n      tmpl::list<evolution::dg::subcell::Tags::ActiveGrid,\n                 evolution::dg::subcell::Tags::Coordinates<2, Frame::Inertial>,\n                 evolution::dg::subcell::Tags::Mesh<2>>,\n      tmpl::list<>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::push_back<\n                         tmpl::append<import_tags_list, extra_tags_for_subcell>,\n                         domain::Tags::Coordinates<2, Frame::Inertial>,\n                         domain::Tags::Mesh<2>>>,\n                     importers::Actions::RegisterWithElementDataReader>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<importers::Actions::ReadVolumeData<TestVolumeData,\n                                                        import_tags_list>,\n                     importers::Actions::ReceiveVolumeData<import_tags_list>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockVolumeDataReader {\n  using component_being_mocked = importers::ElementDataReader<Metavariables>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<importers::detail::InitializeElementDataReader<\n          metavariables::volume_dim>>>>;\n};\n\ntemplate <bool AddSubcell>\nstruct Metavariables {\n  static constexpr size_t volume_dim = 2;\n  using component_list = tmpl::list<MockElementArray<Metavariables, AddSubcell>,\n                                    MockVolumeDataReader<Metavariables>>;\n};\n\ntemplate <bool AddSubcell>\nvoid test_actions(const std::variant<double, importers::ObservationSelector>&\n                      observation_selection,\n                  const bool p_refine, const bool subcell_is_active,\n                  const bool single_precision = false) {\n  CAPTURE(AddSubcell);\n  CAPTURE(subcell_is_active);\n  if (subcell_is_active) {\n    REQUIRE(AddSubcell);\n  }\n  using metavars = Metavariables<AddSubcell>;\n  using reader_component = MockVolumeDataReader<metavars>;\n  using element_array = MockElementArray<metavars, AddSubcell>;\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{{importers::ImporterOptions{\n      \"TestVolumeData*.h5\", \"element_data\", observation_selection,\n      Options::Auto<double>{}, true}}};\n\n  // Setup mock data file reader\n  ActionTesting::emplace_nodegroup_component<reader_component>(\n      make_not_null(&runner));\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<reader_component>(make_not_null(&runner), 0);\n  }\n\n  // Create a few elements with sample data\n  // Specific IDs have no significance, just need different IDs.\n  const std::vector<ElementId<2>> element_ids{{1, {{{1, 0}, {1, 0}}}},\n                                              {1, {{{1, 1}, {1, 0}}}},\n                                              {1, {{{1, 0}, {2, 3}}}},\n                                              {1, {{{1, 0}, {5, 4}}}},\n                                              {0, {{{1, 0}, {1, 0}}}}};\n  const Mesh<2> source_mesh{\n      2,\n      subcell_is_active ? Spectral::Basis::FiniteDifference\n                        : Spectral::Basis::Legendre,\n      subcell_is_active ? Spectral::Quadrature::FaceCentered\n                        : Spectral::Quadrature::GaussLobatto};\n  std::unordered_map<ElementId<2>,\n                     tuples::tagged_tuple_from_typelist<import_tags_list>>\n      all_sample_data{};\n  std::unordered_map<ElementId<2>, tnsr::I<DataVector, 2>> all_coords{};\n  for (const auto& id : element_ids) {\n    // Generate sample data\n    auto& sample_data =\n        all_sample_data\n            .emplace(id, tuples::tagged_tuple_from_typelist<import_tags_list>{})\n            .first->second;\n    auto& coords = all_coords[id];\n    const auto hashed_id = static_cast<double>(std::hash<ElementId<2>>{}(id));\n    const size_t num_points = 4;\n    get<0>(coords) = DataVector{1.5 * hashed_id, 2.3 * hashed_id,\n                                3.4 * hashed_id, 5.6 * hashed_id};\n    get<1>(coords) = DataVector{8.9 * hashed_id, 1.3 * hashed_id,\n                                2.4 * hashed_id, 6.7 * hashed_id};\n    tnsr::I<DataVector, 2> vector{num_points};\n    get<0>(vector) = DataVector{0.5 * hashed_id, 1.0 * hashed_id,\n                                3.0 * hashed_id, -2.0 * hashed_id};\n    get<1>(vector) = DataVector{-0.5 * hashed_id, -1.0 * hashed_id,\n                                -3.0 * hashed_id, 2.0 * hashed_id};\n    get<VectorTag>(sample_data) = std::move(vector);\n    tnsr::ij<DataVector, 2> tensor{num_points};\n    get<0, 0>(tensor) = DataVector{10.5 * hashed_id, 11.0 * hashed_id,\n                                   13.0 * hashed_id, -22.0 * hashed_id};\n    get<0, 1>(tensor) = DataVector{10.5 * hashed_id, -11.0 * hashed_id,\n                                   -13.0 * hashed_id, -22.0 * hashed_id};\n    get<1, 0>(tensor) = DataVector{-10.5 * hashed_id, 11.0 * hashed_id,\n                                   13.0 * hashed_id, 22.0 * hashed_id};\n    get<1, 1>(tensor) = DataVector{-10.5 * hashed_id, -11.0 * hashed_id,\n                                   -13.0 * hashed_id, 22.0 * hashed_id};\n    get<TensorTag>(sample_data) = std::move(tensor);\n\n    // Initialize element with no data, it should be populated from the volume\n    // data file\n    const Mesh<2> target_mesh =\n        p_refine\n            ? Mesh<2>{3,\n                      subcell_is_active ? Spectral::Basis::FiniteDifference\n                                        : Spectral::Basis::Legendre,\n                      subcell_is_active ? Spectral::Quadrature::FaceCentered\n                                        : Spectral::Quadrature::GaussLobatto}\n            : source_mesh;\n    if constexpr (AddSubcell) {\n      ActionTesting::emplace_component_and_initialize<element_array>(\n          make_not_null(&runner), ElementIdType{id},\n          {tnsr::I<DataVector, 2>{}, tnsr::ij<DataVector, 2>{},\n           subcell_is_active ? evolution::dg::subcell::ActiveGrid::Subcell\n                             : evolution::dg::subcell::ActiveGrid::Dg,\n           subcell_is_active ? coords : tnsr::I<DataVector, 2>{}, target_mesh,\n           subcell_is_active ? tnsr::I<DataVector, 2>{} : coords, target_mesh});\n    } else {\n      ActionTesting::emplace_component_and_initialize<element_array>(\n          make_not_null(&runner), ElementIdType{id},\n          {tnsr::I<DataVector, 2>{}, tnsr::ij<DataVector, 2>{}, coords,\n           target_mesh});\n    }\n\n    // Register element\n    ActionTesting::next_action<element_array>(make_not_null(&runner), id);\n    // Invoke the simple_action RegisterElementWithSelf that was called on the\n    // reader component by the RegisterWithElementDataReader action.\n    runner.template invoke_queued_simple_action<reader_component>(0);\n  }\n\n  const auto get_element_tag =\n      [&runner](auto tag_v, const ElementId<2>& local_id) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner, local_id);\n  };\n  const auto get_reader_tag = [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<reader_component, tag>(runner, 0);\n  };\n\n  // Collect the sample data from all elements.\n  // The outer vector represents a list of files that we split the element data\n  // into, so we can test loading volume data from multiple files.\n  std::vector<std::vector<ElementVolumeData>> all_element_data(2);\n  for (const auto& id : element_ids) {\n    std::vector<TensorComponent> tensor_data(8);\n    const auto& vector = get<VectorTag>(all_sample_data.at(id));\n    // Write one component as single precision if requested\n    if (single_precision) {\n      tensor_data[0] = TensorComponent(\n          \"V_x\"s,\n          std::vector<float>(get<0>(vector).begin(), get<0>(vector).end()));\n    } else {\n      tensor_data[0] = TensorComponent(\"V_x\"s, get<0>(vector));\n    }\n    tensor_data[1] = TensorComponent(\"V_y\"s, get<1>(vector));\n    const auto& tensor = get<TensorTag>(all_sample_data.at(id));\n    tensor_data[2] = TensorComponent(\"T_xx\"s, get<0, 0>(tensor));\n    tensor_data[3] = TensorComponent(\"T_xy\"s, get<0, 1>(tensor));\n    tensor_data[4] = TensorComponent(\"T_yx\"s, get<1, 0>(tensor));\n    tensor_data[5] = TensorComponent(\"T_yy\"s, get<1, 1>(tensor));\n    const auto& coords = all_coords.at(id);\n    tensor_data[6] = TensorComponent(\"InertialCoordinates_x\"s, get<0>(coords));\n    tensor_data[7] = TensorComponent(\"InertialCoordinates_y\"s, get<1>(coords));\n    all_element_data[id.block_id()].emplace_back(id, tensor_data, source_mesh);\n  }\n  // Write the sample data into an H5 file\n  for (size_t i = 0; i < all_element_data.size(); ++i) {\n    const std::string h5_file_name =\n        \"TestVolumeData\" + std::to_string(i) + \".h5\";\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n    h5::H5File<h5::AccessType::ReadWrite> h5_file{h5_file_name, false};\n    auto& volume_data = h5_file.insert<h5::VolumeData>(\"/element_data\", 0);\n    volume_data.write_volume_data(0, 0., all_element_data[i]);\n  }\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  bool first_invocation = true;\n  for (const auto& id : element_ids) {\n    // `ReadVolumeData`\n    ActionTesting::next_action<element_array>(make_not_null(&runner), id);\n    if (first_invocation) {\n      REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n          make_not_null(&runner), id));\n    }\n    CHECK(get_reader_tag(importers::Tags::ElementDataAlreadyRead{}).size() ==\n          (first_invocation ? 0 : 1));\n    // Invoke the simple_action `ReadAllVolumeDataAndDistribute` that was called\n    // on the reader component by the `ReadVolumeData` action.\n    runner.template invoke_queued_simple_action<reader_component>(0);\n    CAPTURE(get_reader_tag(importers::Tags::ElementDataAlreadyRead{}));\n    CHECK(get_reader_tag(importers::Tags::ElementDataAlreadyRead{}).size() ==\n          1);\n    // `ReceiveVolumeData` should be ready now\n    ActionTesting::next_action<element_array>(make_not_null(&runner), id);\n    // Check the received data\n    if (p_refine) {\n      // Check only a corner point, which should be the same despite the\n      // p-refinement\n      CHECK(get<0>(get_element_tag(VectorTag{}, id))[0] ==\n            get<0>(get<VectorTag>(all_sample_data.at(id)))[0]);\n    } else {\n      CHECK(get_element_tag(VectorTag{}, id) ==\n            get<VectorTag>(all_sample_data.at(id)));\n      CHECK(get_element_tag(TensorTag{}, id) ==\n            get<TensorTag>(all_sample_data.at(id)));\n    }\n    first_invocation = false;\n  }\n\n  for (size_t i = 0; i < all_element_data.size(); ++i) {\n    const std::string h5_file_name =\n        \"TestVolumeData\" + std::to_string(i) + \".h5\";\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 30]]\nSPECTRE_TEST_CASE(\"Unit.IO.Importers.VolumeDataReaderActions\", \"[Unit][IO]\") {\n  test_actions<false>(0., false, false);\n  test_actions<false>(0., true, false);\n  test_actions<false>(importers::ObservationSelector::First, false, false);\n  test_actions<false>(importers::ObservationSelector::Last, false, false);\n  CHECK_THROWS_WITH(test_actions<false>(0., false, false, true),\n                    Catch::Matchers::ContainsSubstring(\"not supported\"));\n\n  for (const bool subcell_is_active : {false, true}) {\n    test_actions<true>(0., false, subcell_is_active);\n    test_actions<true>(importers::ObservationSelector::First, false,\n                       subcell_is_active);\n    test_actions<true>(importers::ObservationSelector::Last, false,\n                       subcell_is_active);\n  }\n\n  // Spectral::interpolation_matrix used by intrp::RegularGrid doesn't support\n  // an FD basis at the moment. This can be added when needed.\n  CHECK_THROWS_WITH(\n      test_actions<true>(0., true, true),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot do barycentric interpolation with Basis::FiniteDifference\"));\n}\n"
  },
  {
    "path": "tests/Unit/IO/Importers/Test_VolumeDataReaderAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"IO/Importers/Test_VolumeDataReaderAlgorithm.hpp\"\n\n#include <vector>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// Parameters chosen in CMakeLists.txt\nusing metavariables = Metavariables<DIM>;\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &domain::creators::time_dependence::register_derived_with_charm,\n       &domain::FunctionsOfTime::register_derived_with_charm,\n       &register_factory_classes_with_charm<metavariables>},\n      {});\n}\n"
  },
  {
    "path": "tests/Unit/IO/Importers/Test_VolumeDataReaderAlgorithm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/*!\n * \\file\n * \\brief Test reading in and interpolating volume data from H5 files\n *\n * Input files specify a source domain and a target domain. Test data on the\n * source domain is constructed and written to H5 files, then read back in and\n * interpolated to the target domain.\n */\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <tuple>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Factory1D.hpp\"\n#include \"Domain/Creators/Factory2D.hpp\"\n#include \"Domain/Creators/Factory3D.hpp\"\n#include \"Domain/Creators/OptionTags.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/InitialExtents.hpp\"\n#include \"Domain/Creators/Tags/InitialRefinementLevels.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"IO/Importers/Actions/ReadVolumeData.hpp\"\n#include \"IO/Importers/Actions/ReceiveVolumeData.hpp\"\n#include \"IO/Importers/Actions/RegisterWithElementDataReader.hpp\"\n#include \"IO/Importers/ElementDataReader.hpp\"\n#include \"IO/Importers/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/SetData.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n/// \\cond\nnamespace PUP {\nclass er;\n}  // namespace PUP\n/// \\endcond\n\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"ScalarField\"; }\n};\n\ntemplate <size_t Dim>\nstruct VectorFieldTag : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n  static std::string name() { return \"VectorField\"; }\n};\n\n/// The source or target of the interpolation\nenum class SourceOrTarget {\n  /// The domain to interpolate FROM\n  Source,\n  /// The domain to interpolate TO\n  Target\n};\n\ninline std::ostream& operator<<(std::ostream& os,\n                                SourceOrTarget source_or_target) {\n  switch (source_or_target) {\n    case SourceOrTarget::Source:\n      return os << \"Source\";\n    case SourceOrTarget::Target:\n      return os << \"Target\";\n    default:\n      ERROR(\"Missing case for SourceOrTarget\");\n  }\n}\n\n// Tags for both the source and target domains of the interpolation\n\nnamespace OptionTags {\ntemplate <size_t Dim, SourceOrTarget WhichDomain>\nstruct DomainCreator {\n  static std::string name() {\n    return MakeString{} << WhichDomain << \"DomainCreator\";\n  }\n  using type = std::unique_ptr<::DomainCreator<Dim>>;\n  static constexpr Options::String help = {\"Choose a domain.\"};\n};\n}  // namespace OptionTags\n\nnamespace Tags {\ntemplate <size_t Dim, SourceOrTarget WhichDomain>\nstruct Domain : db::SimpleTag {\n  using type = ::Domain<Dim>;\n  using option_tags = tmpl::list<OptionTags::DomainCreator<Dim, WhichDomain>>;\n  static constexpr bool pass_metavariables = false;\n  static ::Domain<Dim> create_from_options(\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator) {\n    return domain_creator->create_domain();\n  }\n};\n\ntemplate <size_t Dim, SourceOrTarget WhichDomain>\nstruct FunctionsOfTime : db::SimpleTag {\n  using type = std::unordered_map<\n      std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>;\n  using option_tags = tmpl::list<OptionTags::DomainCreator<Dim, WhichDomain>>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator) {\n    return domain_creator->functions_of_time();\n  }\n};\n\ntemplate <size_t Dim, SourceOrTarget WhichDomain>\nstruct InitialExtents : db::SimpleTag {\n  using type = std::vector<std::array<size_t, Dim>>;\n  using option_tags = tmpl::list<OptionTags::DomainCreator<Dim, WhichDomain>>;\n  static constexpr bool pass_metavariables = false;\n  static std::vector<std::array<size_t, Dim>> create_from_options(\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator) {\n    return domain_creator->initial_extents();\n  }\n};\n\ntemplate <size_t Dim, SourceOrTarget WhichDomain>\nstruct InitialRefinementLevels : db::SimpleTag {\n  using type = std::vector<std::array<size_t, Dim>>;\n  using option_tags = tmpl::list<OptionTags::DomainCreator<Dim, WhichDomain>>;\n  static constexpr bool pass_metavariables = false;\n  static std::vector<std::array<size_t, Dim>> create_from_options(\n      const std::unique_ptr<::DomainCreator<Dim>>& domain_creator) {\n    return domain_creator->initial_refinement_levels();\n  }\n};\n}  // namespace Tags\n\n/// [option_group]\nstruct OptionsGroup {\n  static std::string name() { return \"Importers\"; }\n  static constexpr Options::String help = \"Numeric volume data\";\n};\n/// [option_group]\n\ntemplate <bool Check>\nvoid clean_test_data(const std::string& data_file_name) {\n  if (file_system::check_if_file_exists(data_file_name)) {\n    file_system::rm(data_file_name, true);\n  } else if (Check) {\n    ERROR(\"Expected test data file '\" << data_file_name << \"' does not exist\");\n  }\n}\n\ntemplate <size_t Dim>\nDataVector gaussian(const tnsr::I<DataVector, Dim>& x) {\n  return exp(-get(dot_product(x, x)));\n}\n\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim, Frame::Inertial> inertial_coordinates(\n    const ElementId<Dim>& element_id, const Mesh<Dim>& mesh,\n    const Block<Dim>& block, const double time,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time) {\n  const auto logical_coords = logical_coordinates(mesh);\n  if (block.is_time_dependent()) {\n    const ElementMap<Dim, Frame::Grid> element_map{\n        element_id, block.moving_mesh_logical_to_grid_map().get_clone()};\n    const auto grid_coords = element_map(logical_coords);\n    const auto& grid_to_inertial_map = block.moving_mesh_grid_to_inertial_map();\n    return grid_to_inertial_map(grid_coords, time, functions_of_time);\n  } else {\n    const ElementMap<Dim, Frame::Inertial> element_map{\n        element_id, block.stationary_map().get_clone()};\n    return element_map(logical_coords);\n  }\n}\n\ntemplate <size_t Dim>\nvoid write_test_data(\n    const std::string& data_file_name, const std::string& subgroup,\n    const double observation_value, const ::Domain<Dim>& domain,\n    const std::unordered_map<\n        std::string, std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>&\n        functions_of_time,\n    const std::vector<std::array<size_t, Dim>>& initial_refinement_levels,\n    const std::vector<std::array<size_t, Dim>>& initial_extents) {\n  // Open file for test data\n  h5::H5File<h5::AccessType::ReadWrite> data_file{data_file_name, true};\n  auto& test_data_file = data_file.insert<h5::VolumeData>(\"/\" + subgroup);\n\n  // Construct test data for all elements\n  const auto element_ids = initial_element_ids(initial_refinement_levels);\n  std::vector<ElementVolumeData> element_data{};\n  for (const auto& element_id : element_ids) {\n    const auto& block = domain.blocks()[element_id.block_id()];\n    const auto mesh = domain::create_initial_mesh(\n        initial_extents, block, element_id, Spectral::Basis::Legendre,\n        Spectral::Quadrature::GaussLobatto);\n    const size_t num_points = mesh.number_of_grid_points();\n    const auto inertial_coords = inertial_coordinates(\n        element_id, mesh, block, observation_value, functions_of_time);\n\n    std::vector<TensorComponent> tensor_components{};\n    for (size_t d = 0; d < Dim; ++d) {\n      tensor_components.push_back(\n          {\"InertialCoordinates\" + inertial_coords.component_suffix(\n                                       inertial_coords.get_tensor_index(d)),\n           inertial_coords[d]});\n    }\n    tensor_components.push_back({\"ScalarField\", gaussian(inertial_coords)});\n    for (size_t d = 0; d < Dim; d++) {\n      static const std::array<std::string, 3> dim_suffix{{\"x\", \"y\", \"z\"}};\n      tensor_components.push_back(\n          {\"VectorField_\" + dim_suffix[d], DataVector(num_points, 0.)});\n    }\n    element_data.push_back({element_id, std::move(tensor_components), mesh});\n  }\n  test_data_file.write_volume_data(0, observation_value,\n                                   std::move(element_data), serialize(domain),\n                                   serialize(functions_of_time));\n}\n\ntemplate <size_t Dim>\nstruct WriteTestData {\n  using const_global_cache_tags =\n      tmpl::list<Tags::Domain<Dim, SourceOrTarget::Source>,\n                 Tags::FunctionsOfTime<Dim, SourceOrTarget::Source>,\n                 Tags::InitialRefinementLevels<Dim, SourceOrTarget::Source>,\n                 Tags::InitialExtents<Dim, SourceOrTarget::Source>>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& options =\n        db::get<importers::Tags::ImporterOptions<OptionsGroup>>(box);\n    clean_test_data<false>(get<importers::OptionTags::FileGlob>(options));\n    write_test_data<Dim>(\n        get<importers::OptionTags::FileGlob>(options),\n        get<importers::OptionTags::Subgroup>(options),\n        std::get<double>(get<importers::OptionTags::ObservationValue>(options)),\n        db::get<Tags::Domain<Dim, SourceOrTarget::Source>>(box),\n        db::get<Tags::FunctionsOfTime<Dim, SourceOrTarget::Source>>(box),\n        db::get<Tags::InitialRefinementLevels<Dim, SourceOrTarget::Source>>(\n            box),\n        db::get<Tags::InitialExtents<Dim, SourceOrTarget::Source>>(box));\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct CleanTestData {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& options =\n        db::get<importers::Tags::ImporterOptions<OptionsGroup>>(box);\n    clean_test_data<true>(get<importers::OptionTags::FileGlob>(options));\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct TestDataWriter {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        tmpl::list<WriteTestData<Dim>>>,\n\n                 Parallel::PhaseActions<Parallel::Phase::Testing,\n                                        tmpl::list<CleanTestData>>>;\n  using simple_tags_from_options = tmpl::list<>;\n\n  static void initialize(\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_component = Parallel::get_parallel_component<TestDataWriter>(\n        *Parallel::local_branch(global_cache));\n    local_component.start_phase(next_phase);\n  }\n};\n\ntemplate <size_t Dim>\nstruct InitializeElement {\n  using const_global_cache_tags =\n      tmpl::list<Tags::Domain<Dim, SourceOrTarget::Target>,\n                 Tags::FunctionsOfTime<Dim, SourceOrTarget::Target>,\n                 Tags::InitialRefinementLevels<Dim, SourceOrTarget::Target>,\n                 Tags::InitialExtents<Dim, SourceOrTarget::Target>>;\n  using simple_tags =\n      tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 domain::Tags::Mesh<Dim>, ScalarFieldTag, VectorFieldTag<Dim>>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& domain =\n        db::get<Tags::Domain<Dim, SourceOrTarget::Target>>(box);\n    const auto& functions_of_time =\n        db::get<Tags::FunctionsOfTime<Dim, SourceOrTarget::Target>>(box);\n    const auto& options =\n        db::get<importers::Tags::ImporterOptions<OptionsGroup>>(box);\n    const double time =\n        std::get<double>(get<importers::OptionTags::ObservationValue>(options));\n    const auto& initial_extents =\n        db::get<Tags::InitialExtents<Dim, SourceOrTarget::Target>>(box);\n    const auto& block = domain.blocks()[element_id.block_id()];\n    const auto mesh = domain::create_initial_mesh(\n        initial_extents, block, element_id, Spectral::Basis::Legendre,\n        Spectral::Quadrature::GaussLobatto);\n    Initialization::mutate_assign<\n        tmpl::list<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                   domain::Tags::Mesh<Dim>>>(\n        make_not_null(&box),\n        inertial_coordinates(element_id, mesh, block, time, functions_of_time),\n        mesh);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <size_t Dim>\nstruct TestResult {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Dim>& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& inertial_coords =\n        get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box);\n    const auto& scalar_field = get<ScalarFieldTag>(box);\n    const auto expected_data = gaussian(inertial_coords);\n    SPECTRE_PARALLEL_REQUIRE(\n        equal_within_roundoff(get(scalar_field), expected_data, 1e-3));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <size_t Dim, typename Metavariables>\nstruct ElementArray {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using array_index = ElementId<Dim>;\n  using metavariables = Metavariables;\n  using simple_tags_from_options = tmpl::list<>;\n  using array_allocation_tags = tmpl::list<>;\n\n  using import_fields = tmpl::list<ScalarFieldTag, VectorFieldTag<Dim>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitializeElement<Dim>,\n                                        Parallel::Actions::TerminatePhase>>,\n      /// [import_actions]\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<importers::Actions::RegisterWithElementDataReader,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::ImportInitialData,\n          tmpl::list<\n              importers::Actions::ReadVolumeData<OptionsGroup, import_fields>,\n              importers::Actions::ReceiveVolumeData<import_fields>,\n              Parallel::Actions::TerminatePhase>>,\n      /// [import_actions]\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<TestResult<Dim>, Parallel::Actions::TerminatePhase>>>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_items,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& element_array =\n        Parallel::get_parallel_component<ElementArray>(local_cache);\n    const auto& domain =\n        get<Tags::Domain<Dim, SourceOrTarget::Target>>(local_cache);\n    const auto& initial_refinement_levels =\n        get<Tags::InitialRefinementLevels<Dim, SourceOrTarget::Target>>(\n            local_cache);\n    const size_t num_procs = static_cast<size_t>(sys::number_of_procs());\n    size_t which_proc = 0;\n    for (const auto& block : domain.blocks()) {\n      const std::vector<ElementId<Dim>> element_ids = initial_element_ids(\n          block.id(), initial_refinement_levels[block.id()]);\n      for (const auto& element_id : element_ids) {\n        while (procs_to_ignore.find(which_proc) != procs_to_ignore.end()) {\n          which_proc = which_proc + 1 == num_procs ? 0 : which_proc + 1;\n        }\n        element_array(element_id)\n            .insert(global_cache, initialization_items, which_proc);\n        which_proc = which_proc + 1 == num_procs ? 0 : which_proc + 1;\n      }\n    }\n    element_array.doneInserting();\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ElementArray>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\n/// [metavars]\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  struct system {};\n\n  using component_list =\n      tmpl::list<ElementArray<Dim, Metavariables>,\n                 TestDataWriter<Dim, Metavariables>,\n                 importers::ElementDataReader<Metavariables>>;\n\n  static constexpr const char* const help{\"Test the volume data reader\"};\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::ImportInitialData, Parallel::Phase::Testing,\n       Parallel::Phase::Exit}};\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DomainCreator<Dim>, domain_creators<Dim>>>;\n  };\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n/// [metavars]\n"
  },
  {
    "path": "tests/Unit/IO/Importers/Test_VolumeDataReaderAlgorithm1D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nSourceDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [4]\n    Distribution: [Linear]\n    IsPeriodicIn: [False]\n    InitialRefinement: [2]\n    InitialGridPoints: [8]\n    TimeDependence: None\n\nTargetDomainCreator:\n  Interval:\n    LowerBound: [-1]\n    UpperBound: [3]\n    Distribution: [Linear]\n    IsPeriodicIn: [False]\n    InitialRefinement: [1]\n    InitialGridPoints: [6]\n    TimeDependence:\n      UniformTranslation:\n        InitialTime: 0.\n        Velocity: [1.]\n\nImporters:\n  VolumeData:\n    FileGlob: \"Test_DataImporterAlgorithm1D.h5\"\n    Subgroup: \"TestData\"\n    ObservationValue: 1.\n    ObservationValueEpsilon: Auto\n    ElementsAreIdentical: False\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/IO/Importers/Test_VolumeDataReaderAlgorithm2D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nSourceDomainCreator: &source_domain\n  Disk:\n    InnerRadius: 1.\n    OuterRadius: 3.\n    InitialRefinement: 1\n    InitialGridPoints: [3, 3]\n    UseEquiangularMap: True\n\n# Load data directly onto the same domain without interpolation\nTargetDomainCreator: *source_domain\n\n# [importer_options]\nImporters:\n  VolumeData:\n    FileGlob: \"Test_DataImporterAlgorithm2D.h5\"\n    Subgroup: \"TestData\"\n    ObservationValue: 2.\n    ObservationValueEpsilon: Auto\n    ElementsAreIdentical: False\n# [importer_options]\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/IO/Importers/Test_VolumeDataReaderAlgorithm3D.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nSourceDomainCreator:\n  BinaryCompactObject:\n    ObjectA:\n      InnerRadius: 0.45\n      OuterRadius: 4.\n      XCoord: &x_right 7.683\n      ExciseInterior: True\n      UseLogarithmicMap: True\n    ObjectB:\n      InnerRadius: 0.45\n      OuterRadius: 4.\n      XCoord: &x_left -7.683\n      ExciseInterior: True\n      UseLogarithmicMap: True\n    CenterOfMassOffset: [0., 0.]\n    Envelope:\n      Radius: 60.\n      RadialDistribution: Projective\n    OuterShell:\n      Radius: 350.\n      RadialPartitioning: []\n      RadialDistribution: Inverse\n      OpeningAngle: 90.0\n    UseEquiangularMap: True\n    CubeScale: 1.0\n    InitialRefinement: 1\n    InitialGridPoints: 7\n    TimeDependentMaps: None\n\nTargetDomainCreator:\n  BinaryCompactObject:\n    ObjectA:\n      InnerRadius: 0.46\n      OuterRadius: 6.\n      XCoord: *x_right\n      ExciseInterior: True\n      UseLogarithmicMap: true\n    ObjectB:\n      InnerRadius: 0.46\n      OuterRadius: 6.\n      XCoord: *x_left\n      ExciseInterior: True\n      UseLogarithmicMap: true\n    CenterOfMassOffset: [0., 0.]\n    Envelope:\n      Radius: 100.\n      RadialDistribution: Projective\n    OuterShell:\n      Radius: 300.\n      RadialPartitioning: []\n      RadialDistribution: Linear\n      OpeningAngle: 90.0\n    UseEquiangularMap: True\n    CubeScale: 1.0\n    InitialRefinement: 0\n    InitialGridPoints: 3\n    TimeDependentMaps: None\n\nImporters:\n  VolumeData:\n    FileGlob: \"Test_DataImporterAlgorithm3D.h5\"\n    Subgroup: \"TestData\"\n    ObservationValue: 3.\n    ObservationValueEpsilon: Auto\n    ElementsAreIdentical: False\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/IO/Logging/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Logging\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  Test_Verbosity.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Logging\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/IO/Logging/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TestLabel {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Logging.Tags\", \"[Unit]\") {\n  TestHelpers::db::test_simple_tag<logging::Tags::Verbosity<TestLabel>>(\n      \"Verbosity(TestLabel)\");\n}\n"
  },
  {
    "path": "tests/Unit/IO/Logging/Test_Verbosity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct TestGroup {\n  static constexpr Options::String help = \"halp\";\n};\n\nvoid test_construct_from_options() {\n  Options::Parser<tmpl::list<logging::OptionTags::Verbosity<TestGroup>>> opts(\n      \"\");\n  opts.parse(\"TestGroup:\\n  Verbosity: Verbose\\n\");\n  CHECK(opts.get<logging::OptionTags::Verbosity<TestGroup>>() ==\n        Verbosity::Verbose);\n}\n\nvoid test_construct_from_options_fail() {\n  Options::Parser<tmpl::list<logging::OptionTags::Verbosity<TestGroup>>> opts(\n      \"\");\n  opts.parse(\"TestGroup:\\n  Verbosity: Braggadocious\\n\");  // Meant to fail.\n  CHECK_THROWS_WITH((opts.get<logging::OptionTags::Verbosity<TestGroup>>()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Failed to convert \\\"Braggadocious\\\" to Verbosity\"));\n}\n\nvoid test_ostream() {\n  CHECK(get_output(Verbosity::Silent) == \"Silent\");\n  CHECK(get_output(Verbosity::Quiet) == \"Quiet\");\n  CHECK(get_output(Verbosity::Verbose) == \"Verbose\");\n  CHECK(get_output(Verbosity::Debug) == \"Debug\");\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Logging.Verbosity\", \"[Unit]\") {\n  test_construct_from_options();\n  test_ostream();\n  test_construct_from_options_fail();\n}\n"
  },
  {
    "path": "tests/Unit/IO/Observers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Observer\")\n\nset(LIBRARY_SOURCES\n  Test_GetLockPointer.cpp\n  Test_Initialize.cpp\n  Test_ObservationId.cpp\n  Test_ReductionObserver.cpp\n  Test_RegisterElements.cpp\n  Test_RegisterEvents.cpp\n  Test_RegisterSingleton.cpp\n  Test_Tags.cpp\n  Test_TypeOfObservation.cpp\n  Test_VolumeObserver.cpp\n  Test_WriteSimpleData.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  DataStructuresHelpers\n  DomainStructure\n  EventsAndTriggers\n  ErrorHandling\n  IoTestHelpers\n  Observer\n  ObserverHelpers\n  Options\n  Parallel\n  Utilities\n  Spectral\n  )\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_GetLockPointer.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Observer/Actions/GetLockPointer.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nParallel::NodeLock* h5_lock_to_check;\n\ntemplate <typename LockTag>\nstruct mock_lock_retrieval_action {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(const db::DataBox<DbTagList>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    auto lock = Parallel::local_branch(\n                    Parallel::get_parallel_component<\n                        observers::ObserverWriter<Metavariables>>(cache))\n                    ->template local_synchronous_action<\n                        observers::Actions::GetLockPointer<LockTag>>();\n    h5_lock_to_check = lock;\n  }\n};\n\ntemplate <typename Metavariables>\nstruct mock_observer_writer {\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using simple_tags = tmpl::list<observers::Tags::H5FileLock>;\n\n  using const_global_cache_tags = tmpl::list<>;\n\n  using metavariables = Metavariables;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_array {\n  using chare_type = ActionTesting::MockArrayChare;\n  using replace_these_simple_actions = tmpl::list<>;\n  using with_these_simple_actions = tmpl::list<>;\n\n  using const_global_cache_tags = tmpl::list<>;\n\n  using metavariables = Metavariables;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct test_metavariables {\n  using component_list = tmpl::list<mock_observer_writer<test_metavariables>,\n                                    mock_array<test_metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.IO.Observer.GetNodeLockPointer\", \"[Unit][Cce]\") {\n  using array_component = mock_array<test_metavariables>;\n  using writer_component = mock_observer_writer<test_metavariables>;\n\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{{}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_array_component_and_initialize<writer_component>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0,\n      {Parallel::NodeLock{}});\n  ActionTesting::emplace_array_component<array_component>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0);\n\n  ActionTesting::simple_action<\n      array_component, mock_lock_retrieval_action<observers::Tags::H5FileLock>>(\n      make_not_null(&runner), 0);\n\n  db::mutate<observers::Tags::H5FileLock>(\n      [](const gsl::not_null<Parallel::NodeLock*> h5_lock) {\n        CHECK(h5_lock.get() == h5_lock_to_check);\n      },\n      make_not_null(&ActionTesting::get_databox<writer_component>(\n          make_not_null(&runner), 0)));\n}\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_Initialize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct observer_component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using simple_tags =\n      typename observers::Actions::Initialize<Metavariables>::simple_tags;\n  using compute_tags =\n      typename observers::Actions::Initialize<Metavariables>::compute_tags;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<observers::Actions::Initialize<Metavariables>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<observer_component<Metavariables>>;\n  using observed_reduction_data_tags = tmpl::list<>;\n\n};\n\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.Initialize\", \"[Unit][Observers]\") {\n  using obs_component = observer_component<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component<obs_component>(&runner, 0);\n  for (size_t i = 0; i < 2; ++i) {\n    runner.next_action<obs_component>(0);\n  }\n\n  CHECK(\n      ActionTesting::get_databox_tag<\n          obs_component, observers::Tags::ExpectedContributorsForObservations>(\n          runner, 0)\n          .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_component, observers::Tags::ContributorsOfReductionData>(runner,\n                                                                         0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_component, observers::Tags::ContributorsOfTensorData>(runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<obs_component,\n                                       observers::Tags::TensorData>(runner, 0)\n            .empty());\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_ObservationId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nnamespace observers {\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.ObservationId\", \"[Unit][Observers]\") {\n  ObservationId id0(4., \"ObservationType1\");\n  ObservationId id1(8., \"ObservationType1\");\n  ObservationId id2(8., \"ObservationType2\");\n  ObservationId id3(8., \"subtype3\");\n  CHECK(id0 == id0);\n  CHECK(id0.hash() == id0.hash());\n  CHECK(id0.observation_key() == id0.observation_key());\n  CHECK(id0 == ObservationId(4., \"ObservationType1\"));\n  CHECK(id0 != id1);\n  CHECK(id0.hash() != id1.hash());\n  CHECK(id0.observation_key() == id1.observation_key());\n  CHECK(id1 != id2);\n  CHECK(id1.hash() != id2.hash());\n  CHECK(id1.observation_key() != id2.observation_key());\n  CHECK(id0.value() == 4.0);\n  CHECK(id1.value() == 8.0);\n  CHECK(id2.value() == 8.0);\n  CHECK(id3 != id0);\n  CHECK(id3 != id1);\n  CHECK(id3 != id2);\n  CHECK(id3.hash() != id2.hash());\n  CHECK(id3.observation_key() != id2.observation_key());\n  CHECK(id3.value() == 8.0);\n\n  CHECK(get_output(id0) ==\n        std::string(MakeString{} << '(' << id0.observation_key() << \",\"\n                                 << id0.hash() << ',' << id0.value() << ')'));\n\n  // Test PUP\n  test_serialization(id0);\n  test_serialization(id1);\n  test_serialization(id2);\n  test_serialization(id3);\n\n  ObservationKey key0{\"ObservationType1\"};\n  ObservationKey key1{\"ObservationType2\"};\n  CHECK(key0 == key0);\n  CHECK(key0 == ObservationKey{\"ObservationType1\"});\n  CHECK(key0 != key1);\n  CHECK(key0 == id0.observation_key());\n  CHECK(get_output(key0) == \"(\" + get_output(key0.tag()) + \")\");\n  test_serialization(key0);\n}\n}  // namespace observers\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_ReductionObserver.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <functional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/IO/Observers/ObserverHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/ReductionActions.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace helpers = TestObservers_detail;\n\nnamespace {\n// [formatter_example]\nstruct FormatErrors\n    : tt::ConformsTo<observers::protocols::ReductionDataFormatter> {\n  using reduction_data = helpers::reduction_data_from_doubles;\n  std::string operator()(const double time, const size_t num_points,\n                         const double error1, const double error2) const {\n    return \"Errors at time \" + std::to_string(time) + \" over \" +\n           std::to_string(num_points) +\n           \" grid points:\\n  Field1: \" + std::to_string(error1) +\n           \"\\n  Field2: \" + std::to_string(error2) + \"\\n\";\n  }\n  // NOLINTNEXTLINE\n  void pup(PUP::er& /*p*/) {}\n};\n// [formatter_example]\nstatic_assert(tt::assert_conforms_to_v<\n              FormatErrors, observers::protocols::ReductionDataFormatter>);\n\nvoid test_reduction_observer(const bool observe_per_core) {\n  using registration_list = tmpl::list<\n      observers::Actions::RegisterWithObservers<\n          helpers::RegisterObservers<observers::TypeOfObservation::Reduction>>,\n      Parallel::Actions::TerminatePhase>;\n\n  using metavariables = helpers::Metavariables<registration_list>;\n  using obs_component = helpers::observer_component<metavariables>;\n  using obs_writer = helpers::observer_writer_component<metavariables>;\n  using element_comp =\n      helpers::element_component<metavariables, registration_list>;\n\n  const std::string output_file_prefix =\n      \"./Unit.IO.Observers.ReductionObserver\";\n  tuples::TaggedTuple<observers::Tags::ReductionFileName,\n                      observers::Tags::VolumeFileName, domain::Tags::Domain<3>,\n                      domain::Tags::FunctionsOfTimeInitialize>\n      cache_data{\n          output_file_prefix, \"\", Domain<3>{},\n          std::unordered_map<\n              std::string,\n              std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>{}};\n  const std::vector<size_t> num_cores_per_node{1, 2};\n  const size_t num_cores = alg::accumulate(num_cores_per_node, size_t{0});\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      std::move(cache_data), {}, num_cores_per_node};\n  ActionTesting::emplace_group_component<obs_component>(&runner);\n  for (size_t core_id = 0; core_id < num_cores; ++core_id) {\n    for (size_t i = 0; i < 2; ++i) {\n      ActionTesting::next_action<obs_component>(make_not_null(&runner),\n                                                core_id);\n    }\n  }\n  ActionTesting::emplace_nodegroup_component<obs_writer>(&runner);\n  for (size_t node_id = 0; node_id < num_cores_per_node.size(); ++node_id) {\n    for (size_t i = 0; i < 2; ++i) {\n      ActionTesting::next_action<obs_writer>(make_not_null(&runner), node_id);\n    }\n  }\n  // Use the element IDs to determine on which node and core to place the\n  // element. The Block ID is the node and the first segment index is the local\n  // core ID on that node.\n  const std::vector<ElementId<2>> element_ids{{1, {{{1, 0}, {1, 0}}}},\n                                              {1, {{{1, 1}, {1, 0}}}},\n                                              {1, {{{1, 0}, {2, 3}}}},\n                                              {1, {{{1, 0}, {5, 4}}}},\n                                              {0, {{{1, 0}, {1, 0}}}}};\n  const auto get_node_id = [](const ElementId<2>& id) { return id.block_id(); };\n  const auto get_local_core_id = [](const ElementId<2>& id) {\n    return id.segment_ids()[0].index();\n  };\n  const auto get_global_core_id = [](const ElementId<2>& id) {\n    // Only works for the specific setup of {1, 2} cores per node\n    return id.block_id() + id.segment_ids()[0].index();\n  };\n  for (const auto& id : element_ids) {\n    ActionTesting::emplace_array_component<element_comp>(\n        &runner, ActionTesting::NodeId{get_node_id(id)},\n        ActionTesting::LocalCoreId{get_local_core_id(id)}, id);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Register);\n\n  // Register elements\n  for (const auto& id : element_ids) {\n    ActionTesting::next_action<element_comp>(make_not_null(&runner), id);\n    // Invoke the simple_action RegisterContributorWithObserver that was called\n    // on the observer component by the RegisterWithObservers action.\n    ActionTesting::invoke_queued_simple_action<obs_component>(\n        make_not_null(&runner), get_global_core_id(id));\n    REQUIRE(ActionTesting::is_simple_action_queue_empty<obs_component>(\n        runner, get_global_core_id(id)));\n  }\n  // Invoke the simple_action RegisterReductionContributorWithObserverWriter.\n  for (size_t node_id = 0; node_id < num_cores_per_node.size(); ++node_id) {\n    ActionTesting::invoke_queued_simple_action<obs_writer>(\n        make_not_null(&runner), node_id);\n    ActionTesting::invoke_queued_simple_action<obs_writer>(\n        make_not_null(&runner), node_id);\n    REQUIRE(ActionTesting::is_simple_action_queue_empty<obs_writer>(runner,\n                                                                    node_id));\n  }\n  // Invoke the simple_action RegisterReductionNodeWithWritingNode.\n  ActionTesting::invoke_queued_simple_action<obs_writer>(make_not_null(&runner),\n                                                         0);\n  REQUIRE(ActionTesting::is_simple_action_queue_empty<obs_writer>(runner, 0));\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const auto make_fake_reduction_data = Overloader{\n      [](const Parallel::ArrayComponentId& id, const double time,\n         const helpers::reduction_data_from_doubles& /*meta*/) {\n        const auto hashed_id =\n            static_cast<double>(std::hash<Parallel::ArrayComponentId>{}(id));\n        constexpr size_t number_of_grid_points = 4;\n        const double error0 = 1.0e-10 * hashed_id + time;\n        const double error1 = 1.0e-12 * hashed_id + 2.0 * time;\n        return helpers::reduction_data_from_doubles{time, number_of_grid_points,\n                                                    error0, error1};\n      },\n      [](const Parallel::ArrayComponentId& id, const double time,\n         const helpers::reduction_data_from_vector& /*meta*/) {\n        const auto hashed_id =\n            static_cast<double>(std::hash<Parallel::ArrayComponentId>{}(id));\n        constexpr size_t number_of_grid_points = 4;\n        const std::vector<double> data{1.0e-10 * hashed_id + time,\n                                       1.0e-12 * hashed_id + 2.0 * time,\n                                       1.0e-11 * hashed_id + 3.0 * time};\n        return helpers::reduction_data_from_vector{time, number_of_grid_points,\n                                                   data};\n      },\n      [](const Parallel::ArrayComponentId& id, const double time,\n         const helpers::reduction_data_from_ds_and_vs& /*meta*/) {\n        const auto hashed_id =\n            static_cast<double>(std::hash<Parallel::ArrayComponentId>{}(id));\n        constexpr size_t number_of_grid_points = 4;\n        const double error0 = 1.0e-10 * hashed_id + time;\n        const double error1 = 1.0e-12 * hashed_id + 2.0 * time;\n        const std::vector<double> vector1{1.0e-10 * hashed_id + 3.0 * time,\n                                          1.0e-12 * hashed_id + 4.0 * time};\n        const std::vector<double> vector2{1.0e-11 * hashed_id + 5.0 * time,\n                                          1.0e-13 * hashed_id + 6.0 * time};\n        return helpers::reduction_data_from_ds_and_vs{\n            time, number_of_grid_points, error0, vector1, vector2, error1};\n      }};\n\n  using reduction_data_list =\n      tmpl::list<helpers::reduction_data_from_doubles,\n                 helpers::reduction_data_from_vector,\n                 helpers::reduction_data_from_ds_and_vs>;\n  tmpl::for_each<reduction_data_list>([&element_ids, &make_fake_reduction_data,\n                                       &runner, &output_file_prefix,\n                                       &observe_per_core, &num_cores_per_node,\n                                       &get_global_core_id](\n                                          auto reduction_data_v) {\n    using reduction_data = tmpl::type_from<decltype(reduction_data_v)>;\n\n    const double time = 3.0;\n    const auto legend = []() {\n      if constexpr (std::is_same_v<reduction_data,\n                                   helpers::reduction_data_from_doubles>) {\n        return std::vector<std::string>{\"Time\", \"NumberOfPoints\", \"Error0\",\n                                        \"Error1\"};\n      } else if constexpr (std::is_same_v<\n                               reduction_data,\n                               helpers::reduction_data_from_vector>) {\n        return std::vector<std::string>{\"Time\", \"NumberOfPoints\", \"Vec0\",\n                                        \"Vec1\", \"Vec2\"};\n      } else if constexpr (std::is_same_v<\n                               reduction_data,\n                               helpers::reduction_data_from_ds_and_vs>) {\n        return std::vector<std::string>{\"Time\",  \"NumberOfPoints\", \"Error0\",\n                                        \"Vec10\", \"Vec11\",          \"Vec20\",\n                                        \"Vec21\", \"Error1\"};\n      }\n    }();\n    const std::string h5_file_name = output_file_prefix + \".h5\";\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n    if (file_system::check_if_file_exists(output_file_prefix + \"0.h5\")) {\n      file_system::rm(output_file_prefix + \"0.h5\", true);\n    }\n    if (file_system::check_if_file_exists(output_file_prefix + \"1.h5\")) {\n      file_system::rm(output_file_prefix + \"1.h5\", true);\n    }\n\n    // Test passing reduction data.\n    for (const auto& id : element_ids) {\n      const Parallel::ArrayComponentId array_id =\n          Parallel::make_array_component_id<element_comp>(id);\n\n      auto reduction_data_fakes =\n          make_fake_reduction_data(array_id, time, reduction_data{});\n      auto formatter = []() {\n        if constexpr (std::is_same_v<reduction_data,\n                                     helpers::reduction_data_from_doubles>) {\n          return std::make_optional(FormatErrors{});\n        } else {\n          return std::optional<observers::NoFormatter>{std::nullopt};\n        }\n      }();\n      runner.simple_action<obs_component,\n                           observers::Actions::ContributeReductionData>(\n          get_global_core_id(id),\n          observers::ObservationId{time, \"/element_data.vol\"},\n          Parallel::make_array_component_id<element_comp>(id), \"/element_data\",\n          legend, std::move(reduction_data_fakes), std::move(formatter),\n          observe_per_core);\n    }\n    // Invoke the threaded action 'CollectReductionDataOnNode'\n    for (size_t node_id = 0; node_id < num_cores_per_node.size(); ++node_id) {\n      for (size_t local_core_id = 0;\n           local_core_id < num_cores_per_node.at(node_id); ++local_core_id) {\n        runner.invoke_queued_threaded_action<obs_writer>(node_id);\n      }\n    }\n    // Invoke the threaded action 'WriteReductionData' to write reduction data\n    // to disk.\n    runner.invoke_queued_threaded_action<obs_writer>(0);\n    runner.invoke_queued_threaded_action<obs_writer>(0);\n    REQUIRE(\n        ActionTesting::is_threaded_action_queue_empty<obs_writer>(runner, 0));\n\n    REQUIRE(file_system::check_if_file_exists(h5_file_name));\n    REQUIRE(file_system::check_if_file_exists(output_file_prefix + \"0.h5\") ==\n            observe_per_core);\n    REQUIRE(file_system::check_if_file_exists(output_file_prefix + \"1.h5\") ==\n            observe_per_core);\n\n    // Invoke the WriteReductionDataRow action to write a single row of\n    // data\n    std::vector<double> single_row_of_data(legend.size() - 2);\n    std::iota(single_row_of_data.begin(), single_row_of_data.end(), 2.);\n    runner.threaded_action<obs_writer,\n                           observers::ThreadedActions::WriteReductionDataRow>(\n        0, \"/element_data\", legend,\n        std::make_tuple(0., 1., single_row_of_data));\n\n    // Check that the H5 file was written correctly.\n    {\n      const auto file = h5::H5File<h5::AccessType::ReadOnly>(h5_file_name);\n      const auto& dat_file = file.get<h5::Dat>(\"/element_data\");\n      const Matrix written_data = dat_file.get_data();\n      const auto& written_legend = dat_file.get_legend();\n      CHECK(written_legend == legend);\n      const auto data = [&time]() {\n        if constexpr (std::is_same_v<reduction_data,\n                                     helpers::reduction_data_from_doubles>) {\n          return helpers::reduction_data_from_doubles(time, 0, 0., 0.);\n        } else if constexpr (std::is_same_v<\n                                 reduction_data,\n                                 helpers::reduction_data_from_vector>) {\n          return helpers::reduction_data_from_vector(\n              time, 0, std::vector<double>{0., 0., 0.});\n        } else if constexpr (std::is_same_v<\n                                 reduction_data,\n                                 helpers::reduction_data_from_ds_and_vs>) {\n          return helpers::reduction_data_from_ds_and_vs(\n              time, 0, 0., std::vector<double>{0., 0.},\n              std::vector<double>{0., 0.}, 0.);\n        }\n      }();\n      const auto expected =\n          alg::accumulate(\n              element_ids, data,\n              [&time, &make_fake_reduction_data](reduction_data state,\n                                                 const ElementId<2>& id) {\n                const Parallel::ArrayComponentId array_id =\n                    Parallel::make_array_component_id<element_comp>(id);\n                return state.combine(\n                    make_fake_reduction_data(array_id, time, reduction_data{}));\n              })\n              .finalize()\n              .data();\n      if constexpr (std::is_same_v<reduction_data,\n                                   helpers::reduction_data_from_doubles>) {\n        CHECK(std::get<0>(expected) == approx(written_data(0, 0)));\n        CHECK(std::get<1>(expected) == approx(written_data(0, 1)));\n        CHECK(std::get<2>(expected) == approx(written_data(0, 2)));\n        CHECK(std::get<3>(expected) == approx(written_data(0, 3)));\n      } else if constexpr (std::is_same_v<\n                               reduction_data,\n                               helpers::reduction_data_from_vector>) {\n        CHECK(std::get<0>(expected) == approx(written_data(0, 0)));\n        CHECK(std::get<1>(expected) == approx(written_data(0, 1)));\n        for (size_t i = 0; i < std::get<2>(expected).size(); ++i) {\n          CHECK(std::get<2>(expected)[i] == approx(written_data(0, i + 2)));\n        }\n      } else if constexpr (std::is_same_v<\n                               reduction_data,\n                               helpers::reduction_data_from_ds_and_vs>) {\n        CHECK(std::get<0>(expected) == approx(written_data(0, 0)));\n        CHECK(std::get<1>(expected) == approx(written_data(0, 1)));\n        CHECK(std::get<2>(expected) == approx(written_data(0, 2)));\n        for (size_t i = 0; i < std::get<3>(expected).size(); ++i) {\n          CHECK(std::get<3>(expected)[i] == approx(written_data(0, i + 3)));\n        }\n        for (size_t i = 0; i < std::get<4>(expected).size(); ++i) {\n          CHECK(std::get<4>(expected)[i] == approx(written_data(0, i + 5)));\n        }\n        CHECK(std::get<5>(expected) == approx(written_data(0, 7)));\n      }\n\n      // Check row of data written by WriteReductionDataRow\n      for (size_t i = 0; i < legend.size(); ++i) {\n        CHECK(written_data(1, i) == i);\n      }\n    }\n    // Check the per-core H5 files were written correctly. Only check the\n    // num-points reduction because we don't need to check here again that\n    // reductions work.\n    if (observe_per_core and\n        std::is_same_v<reduction_data, helpers::reduction_data_from_doubles>) {\n      const auto check_per_core_reduction =\n          [&output_file_prefix, &legend, &time](\n              const size_t node_id, const size_t global_core_id,\n              const size_t expected_num_points) {\n            const auto file = h5::H5File<h5::AccessType::ReadOnly>(\n                output_file_prefix + std::to_string(node_id) + \".h5\");\n            const auto& dat_file = file.get<h5::Dat>(\n                \"/Core\" + std ::to_string(global_core_id) + \"/element_data\");\n            const Matrix written_data = dat_file.get_data();\n            const auto& written_legend = dat_file.get_legend();\n            CHECK(written_legend == legend);\n            CHECK(written_data(0, 0) == approx(time));\n            CHECK(written_data(0, 1) == expected_num_points);\n          };\n      check_per_core_reduction(0, 0, 4);\n      check_per_core_reduction(1, 1, 12);\n      check_per_core_reduction(1, 2, 4);\n    }\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n    if (file_system::check_if_file_exists(output_file_prefix + \"0.h5\")) {\n      file_system::rm(output_file_prefix + \"0.h5\", true);\n    }\n    if (file_system::check_if_file_exists(output_file_prefix + \"1.h5\")) {\n      file_system::rm(output_file_prefix + \"1.h5\", true);\n    }\n  });\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.ReductionObserver\", \"[Unit][Observers]\") {\n  test_reduction_observer(false);\n  test_reduction_observer(true);\n}\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_RegisterElements.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/IO/Observers/ObserverHelpers.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n// NOLINTNEXTLINE(google-build-using-namespace)\nusing namespace TestObservers_detail;\n\ntemplate <observers::TypeOfObservation TypeOfObservation>\nvoid check_observer_registration() {\n  using registration_list =\n      tmpl::list<observers::Actions::RegisterWithObservers<\n                     RegisterObservers<TypeOfObservation>>,\n                 Parallel::Actions::TerminatePhase>;\n\n  using metavariables = Metavariables<registration_list>;\n  using obs_component = observer_component<metavariables>;\n  using obs_writer = observer_writer_component<metavariables>;\n  using element_comp = element_component<metavariables, registration_list>;\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n  ActionTesting::emplace_group_component<obs_component>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_nodegroup_component<obs_writer>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_writer>(make_not_null(&runner), 0);\n  }\n  // Specific IDs have no significance, just need different IDs.\n  const std::vector<ElementId<2>> element_ids{{1, {{{1, 0}, {1, 0}}}},\n                                              {1, {{{1, 1}, {1, 0}}}},\n                                              {1, {{{1, 0}, {2, 3}}}},\n                                              {1, {{{1, 0}, {5, 4}}}},\n                                              {0, {{{1, 0}, {1, 0}}}}};\n  for (const auto& id : element_ids) {\n    ActionTesting::emplace_component<element_comp>(&runner, id);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Register);\n\n  // Check observer component\n  CHECK(\n      ActionTesting::get_databox_tag<\n          obs_component, observers::Tags::ExpectedContributorsForObservations>(\n          runner, 0)\n          .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_component, observers::Tags::ContributorsOfReductionData>(runner,\n                                                                         0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_component, observers::Tags::ContributorsOfTensorData>(runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<obs_component,\n                                       observers::Tags::TensorData>(runner, 0)\n            .empty());\n\n  // Check observer writer component\n  CHECK(ActionTesting::get_databox_tag<\n            obs_writer, observers::Tags::ExpectedContributorsForObservations>(\n            runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_writer, observers::Tags::ContributorsOfReductionData>(runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_writer, observers::Tags::ContributorsOfTensorData>(runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<obs_writer, observers::Tags::TensorData>(\n            runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<obs_writer,\n                                       observers::Tags::InterpolatorTensorData>(\n            runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_writer, observers::Tags::NodesExpectedToContributeReductions>(\n            runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_writer, observers::Tags::NodesThatContributedReductions>(runner,\n                                                                         0)\n            .empty());\n\n  const size_t number_of_obs_writer_actions =\n      TypeOfObservation == observers::TypeOfObservation::Volume ? 1 : 2;\n\n  // Register elements\n  for (const auto& element_id : element_ids) {\n    ActionTesting::next_action<element_comp>(make_not_null(&runner),\n                                             element_id);\n    // Invoke the simple_action RegisterContributorWithObserver that was called\n    // on the observer component by the RegisterWithObservers action.\n    ActionTesting::invoke_queued_simple_action<obs_component>(\n        make_not_null(&runner), 0);\n  }\n  for (size_t j = 0; j < number_of_obs_writer_actions; ++j) {\n    ActionTesting::invoke_queued_simple_action<obs_writer>(\n        make_not_null(&runner), 0);\n  }\n  REQUIRE(ActionTesting::is_simple_action_queue_empty<obs_writer>(runner, 0));\n  REQUIRE(ActionTesting::is_threaded_action_queue_empty<obs_writer>(runner, 0));\n  REQUIRE(\n      ActionTesting::is_simple_action_queue_empty<obs_component>(runner, 0));\n  REQUIRE(\n      ActionTesting::is_threaded_action_queue_empty<obs_component>(runner, 0));\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Test registration occurred as expected\n  const observers::ObservationKey obs_id_key{\"/element_data.vol\"};\n  std::unordered_map<observers::ObservationKey,\n                     std::unordered_set<Parallel::ArrayComponentId>>\n      expected_obs_ids{};\n  for (const auto& id : element_ids) {\n    expected_obs_ids[obs_id_key].insert(\n        Parallel::make_array_component_id<element_comp>(id));\n  }\n  std::unordered_map<observers::ObservationKey,\n                     std::unordered_set<Parallel::ArrayComponentId>>\n      expected_obs_writer_ids{};\n  expected_obs_writer_ids[obs_id_key].insert(\n      Parallel::make_array_component_id<obs_component>(static_cast<int>(0)));\n\n  CHECK(\n      ActionTesting::get_databox_tag<\n          obs_component, observers::Tags::ExpectedContributorsForObservations>(\n          runner, 0) == expected_obs_ids);\n  CHECK(ActionTesting::get_databox_tag<\n            obs_component, observers::Tags::ContributorsOfReductionData>(runner,\n                                                                         0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_component, observers::Tags::ContributorsOfTensorData>(runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<obs_component,\n                                       observers::Tags::TensorData>(runner, 0)\n            .empty());\n\n  CHECK(ActionTesting::get_databox_tag<\n            obs_writer, observers::Tags::ExpectedContributorsForObservations>(\n            runner, 0) == expected_obs_writer_ids);\n  CHECK(ActionTesting::get_databox_tag<\n            obs_writer, observers::Tags::ContributorsOfReductionData>(runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            obs_writer, observers::Tags::ContributorsOfTensorData>(runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<obs_writer, observers::Tags::TensorData>(\n            runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<obs_writer,\n                                       observers::Tags::InterpolatorTensorData>(\n            runner, 0)\n            .empty());\n\n  {\n    INFO(\"Deregistration\");\n    // call the deregistration functions for each element\n    // note that these are not actions, because they are intended to be called\n    // from pup functions.\n    for (const auto& id : element_ids) {\n      observers::Actions::RegisterWithObservers<\n          RegisterObservers<TypeOfObservation>>::\n          template perform_deregistration<element_comp>(\n              ActionTesting::get_databox<element_comp>(make_not_null(&runner),\n                                                       id),\n              ActionTesting::cache<element_comp>(runner, id), id);\n      ActionTesting::invoke_queued_simple_action<obs_component>(\n          make_not_null(&runner), 0);\n    }\n    for (size_t j = 0; j < number_of_obs_writer_actions; ++j) {\n      ActionTesting::invoke_queued_simple_action<obs_writer>(\n          make_not_null(&runner), 0);\n    }\n    CHECK(ActionTesting::get_databox_tag<\n              obs_component,\n              observers::Tags::ExpectedContributorsForObservations>(runner, 0)\n              .empty());\n\n    CHECK(ActionTesting::get_databox_tag<\n              obs_writer, observers::Tags::ExpectedContributorsForObservations>(\n              runner, 0)\n              .empty());\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.RegisterElements\", \"[Unit][Observers]\") {\n  // Tests RegisterWithObservers as well\n  SECTION(\"Register as requiring reduction observer support\") {\n    check_observer_registration<observers::TypeOfObservation::Reduction>();\n  }\n  SECTION(\"Register as requiring volume observer support\") {\n    check_observer_registration<observers::TypeOfObservation::Volume>();\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_RegisterEvents.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/IO/Observers/ObserverHelpers.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nclass SomeEvent : public Event {\n public:\n  struct SubfileName {\n    using type = std::string;\n    static constexpr Options::String help = {\n        \"The The name of the subfile inside the HDF5 file\"};\n  };\n\n  static constexpr Options::String help = \"halp\";\n\n  using options = tmpl::list<SubfileName>;\n\n  SomeEvent() = default;\n  explicit SomeEvent(std::string subfile_path)\n      : subfile_path_(std::move(subfile_path)) {}\n\n  explicit SomeEvent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(SomeEvent);  // NOLINT\n#pragma GCC diagnostic pop\n\n  using observation_registration_tags = tmpl::list<>;\n  std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  get_observation_type_and_key_for_registration() const {\n    return {observers::TypeOfObservation::Reduction,\n            observers::ObservationKey(subfile_path_ + \".dat\")};\n  }\n\n  bool needs_evolved_variables() const override {\n    ERROR(\"Should not be called\");\n  }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Event::pup(p);\n    p | subfile_path_;\n  }\n\n private:\n  std::string subfile_path_;\n};\n\nPUP::able::PUP_ID SomeEvent::my_PUP_ID = 0;  // NOLINT\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using component_being_mocked = void;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using const_global_cache_tags =\n      tmpl::list<Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Testing,\n      tmpl::list<observers::Actions::RegisterEventsWithObservers>>>;\n};\n\nstruct MockRegisterContributorWithObserver {\n  struct Result {\n    observers::ObservationKey observation_key{};\n    Parallel::ArrayComponentId array_component_id{};\n    observers::TypeOfObservation type_of_observation{};\n  };\n  static Result result;\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const Parallel::ArrayComponentId& component_id,\n                    const observers::TypeOfObservation& type_of_observation) {\n    result.observation_key = observation_key;\n    result.array_component_id = component_id;\n    result.type_of_observation = type_of_observation;\n  }\n};\n\nstruct MockDeregisterContributorWithObserver {\n  struct Result {\n    observers::ObservationKey observation_key{};\n    Parallel::ArrayComponentId array_component_id{};\n    observers::TypeOfObservation type_of_observation{};\n  };\n  static Result result;\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const Parallel::ArrayComponentId& component_id,\n                    const observers::TypeOfObservation& type_of_observation) {\n    result.observation_key = observation_key;\n    result.array_component_id = component_id;\n    result.type_of_observation = type_of_observation;\n  }\n};\n\nMockRegisterContributorWithObserver::Result\n    MockRegisterContributorWithObserver::result{};\n\nMockDeregisterContributorWithObserver::Result\n    MockDeregisterContributorWithObserver::result{};\n\ntemplate <typename Metavariables>\nstruct MockObserverComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using const_global_cache_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::RegisterContributorWithObserver,\n                 observers::Actions::DeregisterContributorWithObserver>;\n  using with_these_simple_actions =\n      tmpl::list<MockRegisterContributorWithObserver,\n                 MockDeregisterContributorWithObserver>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>,\n                                    MockObserverComponent<Metavariables>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Event, tmpl::list<SomeEvent>>,\n                  tmpl::pair<Trigger, Triggers::logical_triggers>>;\n  };\n};\n\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.RegisterEvents\", \"[Unit][Observers]\") {\n  // Test pup\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto events_and_triggers =\n      TestHelpers::test_creation<EventsAndTriggers, Metavariables>(\n          \"- Trigger:\\n\"\n          \"    Not: Always\\n\"\n          \"  Events:\\n\"\n          \"    - SomeEvent:\\n\"\n          \"        SubfileName: element_data\\n\");\n\n  using my_component = Component<Metavariables>;\n  using obs_component = MockObserverComponent<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {serialize_and_deserialize(events_and_triggers)}};\n  ActionTesting::emplace_component<my_component>(&runner, 0);\n  ActionTesting::emplace_component<my_component>(&runner, 1);\n  ActionTesting::emplace_group_component<obs_component>(&runner);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  ActionTesting::next_action<my_component>(make_not_null(&runner), 0);\n  ActionTesting::next_action<my_component>(make_not_null(&runner), 1);\n  for (size_t i = 0; i < 2; ++i) {\n    CAPTURE(i);\n    REQUIRE(not ActionTesting::is_simple_action_queue_empty<obs_component>(\n        runner, 0));\n    ActionTesting::invoke_queued_simple_action<obs_component>(\n        make_not_null(&runner), 0);\n\n    CHECK(MockRegisterContributorWithObserver::result.observation_key ==\n          observers::ObservationKey(\"element_data.dat\"));\n    // Need an `or` because we don't know what order actions are run in\n    CHECK((MockRegisterContributorWithObserver::result.array_component_id ==\n               Parallel::make_array_component_id<my_component>(\n                   static_cast<int>(0)) or\n           MockRegisterContributorWithObserver::result.array_component_id ==\n               Parallel::make_array_component_id<my_component>(\n                   static_cast<int>(1))));\n    CHECK(MockRegisterContributorWithObserver::result.type_of_observation ==\n          observers::TypeOfObservation::Reduction);\n  }\n  {\n    INFO(\"Deregistration\");\n    // call the deregistration functions for each element\n    // note that these are not actions, because they are intended to be called\n    // from pup functions.\n    for (int i = 0; i < 2; ++i) {\n      observers::Actions::RegisterEventsWithObservers::\n          template perform_deregistration<my_component>(\n              ActionTesting::get_databox<my_component>(make_not_null(&runner),\n                                                       i),\n              ActionTesting::cache<my_component>(runner, i), i);\n      ActionTesting::invoke_queued_simple_action<obs_component>(\n          make_not_null(&runner), 0);\n\n      // The deregistration mock action just records its arguments like the\n      // registration mock action. The actual deregistration procedure is tested\n      // by Unit.IO.Observers.RegisterElements\n      CHECK(MockDeregisterContributorWithObserver::result.observation_key ==\n            observers::ObservationKey(\"element_data.dat\"));\n      // Need an `or` because we don't know what order actions are run in\n      CHECK((MockDeregisterContributorWithObserver::result.array_component_id ==\n                 Parallel::make_array_component_id<my_component>(\n                     static_cast<int>(0)) or\n             MockDeregisterContributorWithObserver::result.array_component_id ==\n                 Parallel::make_array_component_id<my_component>(\n                     static_cast<int>(1))));\n      CHECK(MockDeregisterContributorWithObserver::result.type_of_observation ==\n            observers::TypeOfObservation::Reduction);\n    }\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_RegisterSingleton.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/IO/Observers/ObserverHelpers.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterSingleton.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Phase.hpp\"\n\nnamespace {\nstruct RegistrationHelper {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex>\n  static std::pair<observers::TypeOfObservation, observers::ObservationKey>\n  register_info(const db::DataBox<DbTagsList>& /*box*/,\n                const ArrayIndex& /*array_index*/) {\n    return {observers::TypeOfObservation::Reduction,\n            observers::ObservationKey{\"Singleton\"}};\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using component_being_mocked = void;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using const_global_cache_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Testing,\n      tmpl::list<observers::Actions::RegisterSingletonWithObserverWriter<\n          RegistrationHelper>>>>;\n};\n\nstruct MockRegisterReductionContributorWithObserverWriter {\n  struct Result {\n    observers::ObservationKey observation_key{};\n    size_t caller_node_id{};\n  };\n  static Result result;\n\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationKey& observation_key,\n                    const size_t caller_node_id) {\n    result.observation_key = observation_key;\n    result.caller_node_id = caller_node_id;\n  }\n};\n\nMockRegisterReductionContributorWithObserverWriter::Result\n    MockRegisterReductionContributorWithObserverWriter::result{};\n\ntemplate <typename Metavariables>\nstruct MockObserverWriterComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using const_global_cache_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::RegisterReductionNodeWithWritingNode>;\n  using with_these_simple_actions =\n      tmpl::list<MockRegisterReductionContributorWithObserverWriter>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>,\n                                    MockObserverWriterComponent<Metavariables>>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.RegisterSingleton\", \"[Unit][Observers]\") {\n  using my_component = Component<Metavariables>;\n  using obs_component = MockObserverWriterComponent<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component<my_component>(&runner, 0);\n  ActionTesting::emplace_component<my_component>(&runner, 1);\n  ActionTesting::emplace_component<obs_component>(&runner, 0);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  ActionTesting::next_action<my_component>(make_not_null(&runner), 0);\n  REQUIRE(not ActionTesting::is_simple_action_queue_empty<obs_component>(runner,\n                                                                         0));\n  ActionTesting::invoke_queued_simple_action<obs_component>(\n      make_not_null(&runner), 0);\n  REQUIRE(\n      ActionTesting::is_simple_action_queue_empty<obs_component>(runner, 0));\n\n  CHECK(MockRegisterReductionContributorWithObserverWriter::result\n            .observation_key == observers::ObservationKey{\"Singleton\"});\n  CHECK(MockRegisterReductionContributorWithObserverWriter::result\n            .caller_node_id == 0);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace observers::Tags {\nnamespace {\nstruct TestTag {\n  using type = int;\n};\n}  // namespace\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.Tags\", \"[Unit][Observers]\") {\n  TestHelpers::db::test_simple_tag<ExpectedContributorsForObservations>(\n      \"ExpectedContributorsForObservations\");\n  TestHelpers::db::test_simple_tag<ContributorsOfReductionData>(\n      \"ContributorsOfReductionData\");\n  TestHelpers::db::test_simple_tag<NodesThatContributedReductions>(\n      \"NodesThatContributedReductions\");\n  TestHelpers::db::test_simple_tag<NodesExpectedToContributeReductions>(\n      \"NodesExpectedToContributeReductions\");\n  TestHelpers::db::test_simple_tag<ReductionDataLock>(\"ReductionDataLock\");\n  TestHelpers::db::test_simple_tag<ContributorsOfTensorData>(\n      \"ContributorsOfTensorData\");\n  TestHelpers::db::test_simple_tag<TensorData>(\"TensorData\");\n  TestHelpers::db::test_simple_tag<InterpolatorTensorData>(\n      \"InterpolatorTensorData\");\n  TestHelpers::db::test_simple_tag<SerializedFunctionsOfTime>(\n      \"SerializedFunctionsOfTime\");\n  TestHelpers::db::test_simple_tag<Dependencies>(\"Dependencies\");\n  TestHelpers::db::test_simple_tag<ReductionData<double>>(\"ReductionData\");\n  TestHelpers::db::test_simple_tag<ReductionDataNames<double>>(\n      \"ReductionDataNames\");\n  TestHelpers::db::test_simple_tag<H5FileLock>(\"H5FileLock\");\n  TestHelpers::db::test_simple_tag<ObservationKey<TestTag>>(\n      \"ObservationKey(TestTag)\");\n  TestHelpers::db::test_simple_tag<VolumeFileName>(\"VolumeFileName\");\n  TestHelpers::db::test_simple_tag<ReductionFileName>(\"ReductionFileName\");\n  TestHelpers::db::test_simple_tag<SurfaceFileName>(\"SurfaceFileName\");\n  static_assert(\n      std::is_same_v<typename ReductionData<double, int, char>::names_tag,\n                     ReductionDataNames<double, int, char>>,\n      \"Failed testing Observers tags\");\n  static_assert(\n      std::is_same_v<typename ReductionDataNames<double, int, char>::data_tag,\n                     ReductionData<double, int, char>>,\n      \"Failed testing Observers tags\");\n}\n}  // namespace observers::Tags\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_TypeOfObservation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace {\nenum class Phases0 { A, B, RegisterWithObserver };\nenum class Phases1 { A, B, C };\n\nstatic_assert(observers::has_register_with_observer_v<Phases0>,\n              \"Failed testing observers::has_register_with_observers\");\nstatic_assert(not observers::has_register_with_observer_v<Phases1>,\n              \"Failed testing observers::has_register_with_observers\");\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.TypeOfObservation\", \"[Unit][Observers]\") {\n  using observers::TypeOfObservation;\n  CHECK(get_output(TypeOfObservation::Reduction) == \"Reduction\");\n  CHECK(get_output(TypeOfObservation::Volume) == \"Volume\");\n}\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_VolumeObserver.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <boost/iterator/transform_iterator.hpp>\n#include <boost/range/combine.hpp>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n#include <limits>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/IO/Observers/ObserverHelpers.hpp\"\n#include \"Helpers/IO/VolumeData.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"IO/Observer/Actions/ObserverRegistration.hpp\"\n#include \"IO/Observer/Actions/RegisterWithObservers.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\n// NOLINTNEXTLINE(google-build-using-namespace)\nnamespace helpers = TestObservers_detail;\n\nnamespace {\nauto make_fake_volume_data(const Parallel::ArrayComponentId& id) {\n  const auto hashed_id =\n      static_cast<double>(std::hash<Parallel::ArrayComponentId>{}(id));\n  std::vector<TensorComponent> data(6);\n  data[0] =\n      TensorComponent(\"T_x\"s, DataVector{0.5 * hashed_id, 1.0 * hashed_id,\n                                         3.0 * hashed_id, -2.0 * hashed_id});\n  data[1] =\n      TensorComponent(\"T_y\"s, DataVector{-0.5 * hashed_id, -1.0 * hashed_id,\n                                         -3.0 * hashed_id, 2.0 * hashed_id});\n\n  data[2] =\n      TensorComponent(\"S_xx\"s, DataVector{10.5 * hashed_id, 11.0 * hashed_id,\n                                          13.0 * hashed_id, -22.0 * hashed_id});\n  data[3] = TensorComponent(\"S_xy\"s,\n                            DataVector{10.5 * hashed_id, -11.0 * hashed_id,\n                                       -13.0 * hashed_id, -22.0 * hashed_id});\n  data[4] =\n      TensorComponent(\"S_yx\"s, DataVector{-10.5 * hashed_id, 11.0 * hashed_id,\n                                          13.0 * hashed_id, 22.0 * hashed_id});\n  data[5] =\n      TensorComponent(\"S_yy\"s, DataVector{-10.5 * hashed_id, -11.0 * hashed_id,\n                                          -13.0 * hashed_id, 22.0 * hashed_id});\n\n  return std::make_tuple(Mesh<2>{{{2, 2}},\n                                 Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto},\n                         std::move(data));\n}\n\n// Check that WriteVolumeData correctly writes a single element of volume\n// data to a file.\ntemplate <typename Metavariables, typename ObsWriter, typename ElementComp>\nvoid check_write_volume_data(\n    const gsl::not_null<ActionTesting::MockRuntimeSystem<Metavariables>*>\n        runner,\n    const ElementId<2>& element_id,\n    const std::vector<std::string>& expected_tensor_names) {\n  const std::string h5_write_volume_file_name{\n      \"./Unit.IO.Observers.VolumeObserver.WriteVolumeData\"};\n  const std::string h5_write_volume_group_name{\"/element_data\"};\n  const std::string h5_write_volume_element_name{\"TestElement\"};\n\n  const Parallel::ArrayComponentId h5_write_volume_array_id =\n      Parallel::make_array_component_id<ElementComp>(element_id);\n\n  // Although the WriteVolumeData action would typically be writing\n  // 2D surface \"volume\" data from a Strahlkorper, to simplify this test, here\n  // just reuse make_fake_volume_data().\n  const auto [expected_mesh, fake_volume_data] =\n      make_fake_volume_data(h5_write_volume_array_id);\n\n  const std::vector<size_t> h5_write_volume_expected_extents{\n      {expected_mesh.extents(0), expected_mesh.extents(1)}};\n  const std::vector<Spectral::Basis> h5_write_volume_expected_bases{\n      {expected_mesh.basis(0), expected_mesh.basis(1)}};\n  const std::vector<Spectral::Quadrature> h5_write_volume_expected_quadratures{\n      {expected_mesh.quadrature(0), expected_mesh.quadrature(1)}};\n\n  const observers::ObservationId write_vol_observation_id{1.,\n                                                          \"/element_data.vol\"};\n\n  if (file_system::check_if_file_exists(h5_write_volume_file_name + \".h5\"s)) {\n    file_system::rm(h5_write_volume_file_name + \".h5\"s, true);\n  }\n\n  runner->template threaded_action<ObsWriter,\n                                   observers::ThreadedActions::WriteVolumeData>(\n      0, h5_write_volume_file_name, h5_write_volume_group_name,\n      write_vol_observation_id,\n      std::vector<ElementVolumeData>{\n          {h5_write_volume_element_name, fake_volume_data,\n           h5_write_volume_expected_extents, h5_write_volume_expected_bases,\n           h5_write_volume_expected_quadratures}});\n\n  {\n    std::vector<DataVector> h5_write_volume_expected_tensor_data{};\n    for (const auto& tensor_component : fake_volume_data) {\n      h5_write_volume_expected_tensor_data.push_back(\n          std::get<DataVector>(tensor_component.data));\n    }\n\n    // Expected_tensor_names order is Tx, Ty, Sxx, Syy, Sxy, Syx, but\n    // h5_write_volume_tensor_data is in order Tx, Ty, Sxx, Sxy, Syx, Syy.\n    // Ensuring that the tensor data components are checked in the correct order\n    // determines the order of components in the last argument to\n    // check_volume_data.\n    TestHelpers::io::VolumeData::check_volume_data(\n        h5_write_volume_file_name + \".h5\"s, 0, \"element_data\",\n        write_vol_observation_id.hash(), write_vol_observation_id.value(),\n        std::nullopt, h5_write_volume_expected_tensor_data,\n        {h5_write_volume_element_name}, {h5_write_volume_expected_bases},\n        {h5_write_volume_expected_quadratures},\n        {h5_write_volume_expected_extents}, expected_tensor_names,\n        {{0, 1, 2, 5, 3, 4}}, {});\n  }\n\n  if (file_system::check_if_file_exists(h5_write_volume_file_name + \".h5\"s)) {\n    file_system::rm(h5_write_volume_file_name + \".h5\"s, true);\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.VolumeObserver\", \"[Unit][Observers]\") {\n  using registration_list = tmpl::list<\n      observers::Actions::RegisterWithObservers<\n          helpers::RegisterObservers<observers::TypeOfObservation::Volume>>,\n      Parallel::Actions::TerminatePhase>;\n\n  using metavariables = helpers::Metavariables<registration_list>;\n  using obs_component = helpers::observer_component<metavariables>;\n  using obs_writer = helpers::observer_writer_component<metavariables>;\n  using element_comp =\n      helpers::element_component<metavariables, registration_list>;\n\n  const std::string output_file_prefix = \"./Unit.IO.Observers.VolumeObserver\";\n  const domain::creators::Brick domain_creator{\n      {{0., 0., 0.}},\n      {{1., 2., 3.}},\n      {{1, 0, 1}},\n      {{3, 4, 5}},\n      {{false, false, false}},\n      {},\n      std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3, 0>>(\n          1., std::array<double, 3>{{2., 3., 4.}})};\n  const double initial_expiration_time = 100.0;\n  const std::unordered_map<std::string, double> initial_expiration_times{\n      {\"Translation\", initial_expiration_time}};\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  tuples::TaggedTuple<observers::Tags::ReductionFileName,\n                      observers::Tags::VolumeFileName, domain::Tags::Domain<3>,\n                      domain::Tags::FunctionsOfTimeInitialize>\n      cache_data{\"\", output_file_prefix, domain_creator.create_domain(),\n                 domain_creator.functions_of_time(initial_expiration_times)};\n  ActionTesting::MockRuntimeSystem<metavariables> runner{std::move(cache_data)};\n  ActionTesting::emplace_group_component<obs_component>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_nodegroup_component<obs_writer>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_writer>(make_not_null(&runner), 0);\n  }\n  // Specific IDs have no significance, just need different IDs.\n  const std::vector<ElementId<2>> element_ids{{1, {{{1, 0}, {1, 0}}}},\n                                              {1, {{{1, 1}, {1, 0}}}},\n                                              {1, {{{1, 0}, {2, 3}}}},\n                                              {1, {{{1, 0}, {5, 4}}}},\n                                              {0, {{{1, 0}, {1, 0}}}}};\n  for (const auto& id : element_ids) {\n    ActionTesting::emplace_component<element_comp>(&runner, id);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Register);\n\n  // Register elements\n  for (const auto& id : element_ids) {\n    ActionTesting::next_action<element_comp>(make_not_null(&runner), id);\n    // Invoke the simple_action RegisterContributorWithObserver that was called\n    // on the observer component by the RegisterWithObservers action.\n    ActionTesting::invoke_queued_simple_action<obs_component>(\n        make_not_null(&runner), 0);\n  }\n  // Invoke the simple_action RegisterVolumeContributorWithObserverWriter.\n  ActionTesting::invoke_queued_simple_action<obs_writer>(make_not_null(&runner),\n                                                         0);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const std::string h5_file_name = output_file_prefix + \"0.h5\";\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  const std::vector<std::string> expected_tensor_names{\"T_x\",  \"T_y\",  \"S_xx\",\n                                                       \"S_yy\", \"S_xy\", \"S_yx\"};\n  CAPTURE(expected_tensor_names);\n\n  // Test passing volume data. Last three arguments are only used if dependency\n  // has a value\n  const auto test_volume_actions = [&](const double observation_time,\n                                       const bool should_have_observation_fots,\n                                       const std::optional<std::string>&\n                                           dependency = std::nullopt,\n                                       const bool write_volume_data = true,\n                                       const bool\n                                           send_dependency_before_elements =\n                                               true,\n                                       const bool test_error = false) {\n    const observers::ObservationId observation_id{observation_time,\n                                                  \"/element_data.vol\"};\n\n    CAPTURE(observation_id);\n    CAPTURE(dependency);\n    CAPTURE(write_volume_data);\n    CAPTURE(send_dependency_before_elements);\n\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n\n    const auto send_dependency = [&]() {\n      ActionTesting::threaded_action<\n          obs_writer, observers::ThreadedActions::ContributeDependency>(\n          make_not_null(&runner), 0, observation_id.value(),\n          test_error ? \"BadDependency\" : dependency.value(), \"element_data\",\n          write_volume_data);\n    };\n    if (dependency.has_value() and send_dependency_before_elements) {\n      send_dependency();\n    }\n\n    for (const auto& id : element_ids) {\n      const Parallel::ArrayComponentId array_id =\n          Parallel::make_array_component_id<element_comp>(id);\n\n      auto [mesh, fake_volume_data] = make_fake_volume_data(array_id);\n      observers::contribute_volume_data<true>(\n          ActionTesting::cache<obs_component>(runner, 0), observation_id,\n          std::string{\"/element_data\"}, array_id,\n          ElementVolumeData{id, std::move(fake_volume_data), mesh}, dependency);\n      ActionTesting::invoke_queued_simple_action<obs_component>(\n          make_not_null(&runner), 0);\n    }\n    // Invoke the simple action 'ContributeVolumeDataToWriter'\n    // to move the volume data to the Writer parallel component.\n    if (test_error) {\n      CHECK_THROWS_WITH(\n          ActionTesting::invoke_queued_threaded_action<obs_writer>(\n              make_not_null(&runner), 0),\n          Catch::Matchers::ContainsSubstring(\n              \"The dependency that was sent to the ObserverWriter from the \"\n              \"elements (TestDependency) does not match the dependency \"\n              \"received from ContributeDependency (BadDependency)\"));\n      return;\n    } else {\n      ActionTesting::invoke_queued_threaded_action<obs_writer>(\n          make_not_null(&runner), 0);\n    }\n    CHECK(ActionTesting::is_threaded_action_queue_empty<obs_writer>(runner, 0));\n\n    // If we have a dependency, volume data should still be in the writer if\n    // we are sending the dependency after all the elements\n    if (dependency.has_value()) {\n      const auto& all_volume_data =\n          ActionTesting::get_databox_tag<obs_writer,\n                                         observers::Tags::TensorData>(runner,\n                                                                      0);\n      REQUIRE(all_volume_data.contains(observation_id) !=\n              send_dependency_before_elements);\n\n      // Now we send the dependency if we didn't before\n      if (not send_dependency_before_elements) {\n        send_dependency();\n      }\n\n      // Now we should be guaranteed to have written the volume data (or\n      // deleted it because we aren't writing)\n      REQUIRE_FALSE(all_volume_data.contains(observation_id));\n\n      // Check that we indeed didn't write anything if we weren't supposed\n      // to\n      if (not write_volume_data) {\n        REQUIRE_FALSE(file_system::check_if_file_exists(h5_file_name));\n        return;\n      }\n    }\n\n    REQUIRE(file_system::check_if_file_exists(h5_file_name));\n    // Check that the H5 file was written correctly.\n    const h5::H5File<h5::AccessType::ReadOnly> my_file(h5_file_name);\n    const auto& volume_file = my_file.get<h5::VolumeData>(\"/element_data\");\n\n    const auto temporal_id = observation_id.hash();\n    CHECK(volume_file.list_observation_ids() ==\n          std::vector<size_t>{temporal_id});\n\n    const auto tensor_names = volume_file.list_tensor_components(temporal_id);\n    CAPTURE(tensor_names);\n    REQUIRE(alg::all_of(tensor_names,\n                        [&expected_tensor_names](const std::string& name) {\n                          return alg::found(expected_tensor_names, name);\n                        }));\n    REQUIRE(alg::all_of(expected_tensor_names,\n                        [&tensor_names](const std::string& name) {\n                          return alg::found(tensor_names, name);\n                        }));\n    const std::vector<std::string> grid_names =\n        volume_file.get_grid_names(temporal_id);\n    // A pair holds an element_id, and it's \"place\" in the string of grid\n    // names\n    std::vector<std::tuple<ElementId<2>, size_t>> pairs;\n    auto start = grid_names.begin();\n    auto end = grid_names.end();\n    for (const auto& element_id : element_ids) {\n      const auto place =\n          std::find(start, end, get_output<ElementId<2>>(element_id));\n      // Check that the grid was actually found\n      REQUIRE(place != end);\n      // Store a tuple of the element_id and its place\n      pairs.emplace_back(element_id, std::distance(start, place));\n    }\n    // Sort element_ids by place, this is necessary because the elements are\n    // written to file in an unpredictable order, so to extract this order,\n    // we look at the string of grid names, as it is written in the same\n    // order as the elements.\n    std::sort(pairs.begin(), pairs.end(),\n              [](const std::tuple<ElementId<2>, size_t>& pair_1,\n                 const std::tuple<ElementId<2>, size_t>& pair_2) {\n                return std::get<1>(pair_1) < std::get<1>(pair_2);\n              });\n    std::vector<ElementId<2>> sorted_element_ids;\n    sorted_element_ids.reserve(pairs.size());\n    for (const auto& pair : pairs) {\n      sorted_element_ids.push_back(std::get<0>(pair));\n    }\n    // Read the Tensor Data that was written to the file\n    std::unordered_map<std::string, DataVector> read_tensor_data;\n    for (const auto& tensor_name :\n         volume_file.list_tensor_components(temporal_id)) {\n      read_tensor_data[tensor_name] = std::get<DataVector>(\n          volume_file.get_tensor_component(temporal_id, tensor_name).data);\n    }\n    // Read the extents that were written to file\n    const std::vector<std::vector<size_t>> read_extents =\n        volume_file.get_extents(temporal_id);\n    const auto read_bases = volume_file.get_bases(temporal_id);\n    const auto read_quadratures = volume_file.get_quadratures(temporal_id);\n    // The data is stored contiguously, and each element has a subset of the\n    // data.  We need to keep track of how many points have already been\n    // checked so that we know where to look in the tensor component data\n    // for the current grid's data.\n    size_t points_processed = 0;\n    for (size_t i = 0; i < sorted_element_ids.size(); i++) {\n      const auto& element_id = sorted_element_ids[i];\n      const std::string grid_name = MakeString{} << element_id;\n      const Parallel::ArrayComponentId array_id =\n          Parallel::make_array_component_id<element_comp>(element_id);\n      const auto volume_data_fakes = make_fake_volume_data(array_id);\n      // Each element contains as many data points as the product of its\n      // extents, compute this number\n      const size_t stride =\n          alg::accumulate(std::get<0>(volume_data_fakes).extents().indices(),\n                          static_cast<size_t>(1), std::multiplies<>{});\n\n      // Check that the extents and tensor data were correctly written\n      CHECK(std::vector<size_t>{std::get<0>(volume_data_fakes).extents(0),\n                                std::get<0>(volume_data_fakes).extents(1)} ==\n            read_extents[i]);\n      for (const auto& tensor_component : std::get<1>(volume_data_fakes)) {\n        CHECK(std::get<DataVector>(tensor_component.data) ==\n              DataVector(\n                  &(read_tensor_data[tensor_component.name][points_processed]),\n                  stride));\n      }\n      points_processed += stride;\n    }\n\n    const auto observation_fots =\n        volume_file.get_functions_of_time(temporal_id);\n    CHECK(observation_fots.has_value() == should_have_observation_fots);\n    if (observation_fots.has_value()) {\n      auto observation_functions_of_time =\n          deserialize<domain::FunctionsOfTimeMap>(\n              observation_fots.value().data());\n      auto original_functions_of_time =\n          domain_creator.functions_of_time(initial_expiration_times);\n      CHECK(observation_functions_of_time.size() ==\n            original_functions_of_time.size());\n      const double expected_observation_time = observation_id.value();\n      const double expected_expiration_time =\n          expected_observation_time +\n          100.0 * std::numeric_limits<double>::epsilon() *\n              std::max(std::abs(expected_observation_time), 1.0);\n      for (const auto& [name, fot_ptr] : observation_functions_of_time) {\n        CHECK(original_functions_of_time.count(name) == 1);\n        const auto observation_bounds = fot_ptr->time_bounds();\n        const auto original_bounds =\n            original_functions_of_time.at(name)->time_bounds();\n        CHECK(observation_bounds[0] == approx(expected_observation_time));\n        CHECK(observation_bounds[0] >= original_bounds[0]);\n        if (std::isfinite(expected_observation_time)) {\n          CHECK(observation_bounds[1] == approx(expected_expiration_time));\n          CHECK(observation_bounds[1] <= original_bounds[1]);\n        } else {\n          CHECK(observation_bounds[1] == expected_observation_time);\n        }\n      }\n    }\n    const auto global_fot_buffer = volume_file.get_global_functions_of_time();\n    CHECK(global_fot_buffer.has_value());\n    CHECK(\n        global_fot_buffer.value() ==\n        serialize(domain_creator.functions_of_time(initial_expiration_times)));\n\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n  };\n\n  test_volume_actions(3., true);\n  test_volume_actions(0.5, false);\n  for (const auto& [write_volume_data, send_dependency_before_elements] :\n       cartesian_product(std::array{true, false}, std::array{true, false})) {\n    test_volume_actions(3., true, {\"TestDependency\"}, write_volume_data,\n                        send_dependency_before_elements);\n  }\n  test_volume_actions(3., true, {\"TestDependency\"}, true, true, true);\n\n  check_write_volume_data<metavariables, obs_writer, element_comp>(\n      make_not_null(&runner), element_ids[0], expected_tensor_names);\n}\n"
  },
  {
    "path": "tests/Unit/IO/Observers/Test_WriteSimpleData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/IO/Observers/ObserverHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"IO/Observer/WriteSimpleData.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\n// NOLINTNEXTLINE(google-build-using-namespace)\nnamespace helpers = TestObservers_detail;\n\nnamespace {\n\nstruct test_metavariables {\n  using component_list =\n      tmpl::list<helpers::observer_writer_component<test_metavariables>>;\n\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<helpers::reduction_data_from_doubles>>;\n\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.IO.Observers.WriteSimpleData\", \"[Unit][Observers]\") {\n  using obs_writer = helpers::observer_writer_component<test_metavariables>;\n\n  tuples::TaggedTuple<observers::Tags::ReductionFileName,\n                      observers::Tags::VolumeFileName>\n      cache_data{};\n  tuples::get<observers::Tags::VolumeFileName>(cache_data) =\n      \"./Unit.IO.Observers.WriteSimpleData\";\n  const auto& output_file_prefix =\n      tuples::get<observers::Tags::VolumeFileName>(cache_data);\n  ActionTesting::MockRuntimeSystem<test_metavariables> runner{cache_data};\n  ActionTesting::emplace_component<obs_writer>(&runner, 0);\n  for(size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_writer>(make_not_null(&runner), 0);\n  }\n  runner.set_phase(Parallel::Phase::Testing);\n\n  const std::string h5_file_name = output_file_prefix + \"0.h5\";\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> value_dist{1.0, 5.0};\n  const size_t vector_size = 10;\n  std::vector<double> data_row(vector_size);\n  fill_with_random_values(make_not_null(&data_row), make_not_null(&gen),\n                          make_not_null(&value_dist));\n  std::vector<std::string> legend(vector_size);\n  for (size_t i = 0; i < vector_size; ++i) {\n    legend[i] = std::to_string(i);\n  }\n  ActionTesting::threaded_action<obs_writer,\n                                 observers::ThreadedActions::WriteSimpleData>(\n      make_not_null(&runner), 0, legend, data_row, \"/simple_data.dat\");\n  // scoped to close the file\n  {\n    h5::H5File<h5::AccessType::ReadOnly> read_file{h5_file_name};\n    const auto& dataset = read_file.get<h5::Dat>(\"/simple_data\");\n    const auto cols = alg::iota(std::vector<size_t>(vector_size), 0_st);\n    const Matrix data_matrix = dataset.get_data_subset(cols, 0, 1);\n    for (size_t i = 0; i < vector_size; ++i) {\n      CHECK(approx(data_matrix(0, i)) == data_row[i]);\n    }\n    if (file_system::check_if_file_exists(h5_file_name)) {\n      file_system::rm(h5_file_name, true);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/IO/Test_ComposeTable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <fstream>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/ComposeTable.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test_table(const io::ComposeTable& compose_table) {\n  CHECK(compose_table.temperature_bounds()[0] ==\n        approx(0.10000000000000001));\n  CHECK(compose_table.temperature_bounds()[1] == approx(158.0));\n  CHECK(compose_table.number_density_bounds()[0] ==\n        approx(1.0000000000000000e-12));\n  CHECK(compose_table.number_density_bounds()[1] == approx(1.9));\n  CHECK(compose_table.electron_fraction_bounds()[0] == approx(0.01));\n  CHECK(compose_table.electron_fraction_bounds()[1] == approx(0.6));\n  CHECK(compose_table.number_density_number_of_points() == 4);\n  CHECK(compose_table.temperature_number_of_points() == 2);\n  CHECK(compose_table.electron_fraction_number_of_points() == 3);\n  CHECK(compose_table.beta_equilibrium() == false);\n  CHECK(compose_table.number_density_log_spacing() == true);\n  CHECK(compose_table.temperature_log_spacing() == true);\n  CHECK(compose_table.electron_fraction_log_spacing() == false);\n\n  CHECK(compose_table.available_quantities() ==\n        std::vector<std::string>{\n            \"pressure\", \"specific entropy\", \"baryon chemical potential\",\n            \"charge chemical potential\", \"lepton chemical potential\",\n            \"specific internal energy\", \"sound speed squared\", \"free energy\"});\n\n  CHECK(compose_table.data(\"pressure\") ==\n        DataVector{3.2731420000000011e-12, 3.1893989140625013e-12,\n                   3.1999981000000014e-12, 1.2155360915010773e-9,\n                   8.3016422052875601e-10, 2.1627258654874539e-9,\n                   3.2099012875311601e-5,  2.4468017331204212e-4,\n                   6.1349624337516350e-4,  2210.6053143615190,\n                   2354.6115554098865,     2538.9119061115266,\n                   52.861468710971884,     52.861468705720966,\n                   52.861468710893689,     52.861692578286011,\n                   52.861692577931898,     52.861692577582062,\n                   52.861449400505300,     52.861440607424719,\n                   52.861463344038555,     5535.2247866186935,\n                   6793.9338434046485,     6544.9524934507153});\n  CHECK(compose_table.data(\"specific entropy\") ==\n        DataVector{\n            158.12359000000009,    142.35139851562508,    143.93577000000008,\n            9.3829711464065095,    0.99124464633440812,   2.4034713737830846,\n            0.83152863391388587,   0.15564581028786784,   0.43268025604719001,\n            2.6550771478559753e-3, 3.4795982667156005e-3, 3.3019006656817246e-3,\n            1485445157219.0459,    1485445156918.3232,    1485445157214.1091,\n            119933515.67157803,    119933515.67303930,    119933517.06181532,\n            9683.2483710455308,    9683.2451082927018,    9683.2512877997542,\n            5.0965517306845944,    6.2536132566462319,    6.5582247883215183});\n  CHECK(compose_table.data(\"baryon chemical potential\") ==\n        DataVector{\n            -1.6556880602462847,  -1.8745694607276671,  -15.559631577364950,\n            -0.71347327189280818, -1.0456573497444996,  -16.605664215691274,\n            0.43805416019382620,  -0.19600929781720619, -18.714060368760126,\n            1895.9497406444118,   1909.6037477324869,   1873.0882690980980,\n            -939.56534999255268,  -939.56534999359724,  -939.56534999464191,\n            -939.56525776035880,  -939.56527069940967,  -939.56528363845996,\n            -938.41518441013659,  -938.58050505591427,  -938.74176940754023,\n            3214.4054109629301,   -4889.2134102447071,  -4340.8030471113798});\n  CHECK(\n      compose_table.data(\"charge chemical potential\") ==\n      DataVector{\n          -19.842156349184702,    -19.190385749183669,    12.436817954442299,\n          -22.670750207296898,    -21.646087184271057,    14.390424834977958,\n          -26.755199814299104,    -25.385280320216147,    17.192821769637060,\n          -198.22942467070547,    -28.325443917779911,    132.82602736094074,\n          -3.4007065215488760e-9, 7.4961466524481554e-10, 4.8998992671017037e-9,\n          -4.2118705904313029e-5, 9.2845115074833722e-6,  6.0687726877748120e-5,\n          -0.53075097669568272,   0.11399145866263270,    0.75613580630043975,\n          -137.87674894108289,    11.805107240332074,     131.01280476673062});\n  CHECK(\n      compose_table.data(\"lepton chemical potential\") ==\n      DataVector{\n          -19.841845353053849,    -19.180888622625869,   12.455422287937649,\n          -22.222766207600028,    -20.601886764725815,   15.658889679099508,\n          -19.700822293861712,    -3.3834089107048677,   44.759109646262324,\n          -35.332465886409203,    480.62811399608114,    770.54386620803041,\n          -3.3915057215320162e-9, 1.0312528875660281e-9, 5.4538797241667128e-9,\n          -4.2004349433156367e-5, 1.2772383912169461e-5, 6.7549115267934927e-5,\n          -0.52933460049891867,   0.15719093234876486,   0.84111837567640779,\n          -120.35548478633407,    366.21296279624414,    642.02667219177931});\n  CHECK(compose_table.data(\"specific internal energy\") ==\n        DataVector{1.1372398000000006e-2,  3.5346163132812542e-3,\n                   3.3070626000000045e-3,  -1.0169032790344480e-4,\n                   -7.7663669049909921e-3, -7.6041533773714091e-3,\n                   1.2234741042710821e-4,  -2.9897660752421736e-3,\n                   4.4546614168404601e-3,  0.77921157405081587,\n                   0.86947269803694438,    1.0634124481653968,\n                   193541104778.35413,     193541104733.22195,\n                   193541104777.67715,     15626334.678079225,\n                   15626334.678358778,     15626334.912313508,\n                   1260.6482389767325,     1260.6475515928282,\n                   1260.6487573789184,     1.1740182149625666,\n                   -7.8404943805046665,    -6.7725747751703373});\n  CHECK(compose_table.data(\"sound speed squared\") ==\n        DataVector{\n            4.2176792400813656e-3, 4.1456131080695067e-3, 4.1172877390651744e-3,\n            1.7336626487566469e-4, 9.2710083433018563e-5, 2.6630342167846473e-4,\n            3.4802175676683546e-4, 2.2675519460182546e-3, 5.6319393106579359e-3,\n            1.2305841985543764,    1.2418223794193011,    1.2195142464169275,\n            0.25395113490394422,   0.25395113613208825,   0.25395113559980687,\n            0.25368373066295680,   0.25368373062477667,   0.25368372026896685,\n            0.25365338177418945,   0.25365477067007891,   0.25365467228324629,\n            -28.866914059936207,   66.325790590411600,    -194.71183243952410});\n  CHECK(compose_table.data(\"free energy\") ==\n        DataVector{\n            934.43810148373984,  928.65121304890044,  928.27897438227694,\n            938.53150819022767,  932.16921623914311,  932.18040376414547,\n            939.59715052645186,  936.74070481647925,  943.70752751624718,\n            1671.6852726725444,  1756.4914144088082,  1938.7105006111608,\n            -52861469826937.250, -52861469821686.242, -52861469826858.977,\n            -4267988168.9958286, -4267988168.9672489, -4267988168.9389825,\n            -344589.38828202756, -344589.44304620347, -344589.29579705157,\n            1239.4909811346472,  -7413.7119665909195, -6460.7335245463910});\n\n  for (const auto& quantity_name : compose_table.available_quantities()) {\n    CHECK(compose_table.data(quantity_name) ==\n          compose_table.data().at(quantity_name));\n  }\n}\n\nvoid test() {\n  const io::ComposeTable compose_table(unit_test_src_path() + \"/IO\");\n  test_table(compose_table);\n  test_table(serialize_and_deserialize(compose_table));\n}\n\nvoid test_error_messages() {\n  const std::string directory = unit_test_build_path() + \"/TestComposeTable\";\n  if (file_system::check_if_dir_exists(directory)) {\n    file_system::rm(directory, true);\n  }\n\n  const auto replace_line = [&directory](const std::string& filename,\n                                         const std::string& old,\n                                         const std::string& new_str) {\n    std::ifstream f;\n    f.open(directory + filename, std::ios::ate);\n    const auto size = f.tellg();\n    std::string str(static_cast<size_t>(size), '\\0');\n    f.seekg(0);\n    REQUIRE(f.read(&str[0], size));\n    f.close();\n    str.replace(str.find(old.c_str()), old.size(), new_str);\n    file_system::rm(directory + filename, false);\n    std::ofstream(directory + filename) << str;\n  };\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.parameters\", directory);\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\"eos.quantities' does not exist.\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  replace_line(\"/eos.quantities\",\n               \" # number of regular, additional and derivative \", \"\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\"Read unexpected comment line: \"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  replace_line(\"/eos.quantities\",\n               \" # indices of regular, additional and derivative quantities\",\n               \"\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\"Read unexpected comment line: \"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  replace_line(\"/eos.quantities\", \"1 2 3 4 5 7 12 1\", \"1 2 3 4 5 7000 12 1\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Read in unknown quantity with number 7000\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  replace_line(\"/eos.quantities\", \"1 2 3 4 5 7 12 1\", \"1 2 3 4 5 7 12 10000\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Read in unknown quantity with number 10000\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  replace_line(\"/eos.quantities\", \"1 2 3 4 5 7 12 1\", \"1 2 3 4 5 1 12 1\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Found quantity 'pressure' more than once.\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\"eos.parameters' does not exist.\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.parameters\", directory);\n  replace_line(\"/eos.parameters\",\n               \" # order of interpolation in first, second and third index\",\n               \" # blah\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Read unexpected comment line: ' # blah'\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.parameters\", directory);\n  replace_line(\"/eos.parameters\",\n               \" # calculation of beta-equilibrium (1: yes, else: no) and for \"\n               \"given entropy (1: yes, else: no)\",\n               \" # herp\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Read unexpected comment line: ' # herp'\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.parameters\", directory);\n  replace_line(\n      \"/eos.parameters\",\n      \" # tabulation scheme (0 = explicit listing, 1 = loops, see manual)\",\n      \" # derp\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Read unexpected comment line: ' # derp'\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.parameters\", directory);\n  replace_line(\"/eos.parameters\",\n               \" # parameter values (first, second and third index) depending \"\n               \"on tabulation scheme\",\n               \" # blee\");\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Read unexpected comment line: ' # blee'\"));\n  file_system::rm(directory, true);\n\n  file_system::create_directory(directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.quantities\", directory);\n  file_system::copy(unit_test_src_path() + \"/IO/eos.parameters\", directory);\n  CHECK_THROWS_WITH(\n      ([&directory]() { const io::ComposeTable compose_table(directory); })(),\n      Catch::Matchers::ContainsSubstring(\"eos.table' does not exist.\"));\n  file_system::rm(directory, true);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.IO.ComposeTable\", \"[Unit][IO]\") {\n  test();\n  test_error_messages();\n}\n"
  },
  {
    "path": "tests/Unit/IO/Test_Connectivity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"IO/Connectivity.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.IO.Connectivity\", \"[Unit][IO][H5]\") {\n  CHECK(get_output(vis::detail::Topology::Line) == \"Line\"s);\n  CHECK(get_output(vis::detail::Topology::Triangle) == \"Triangle\"s);\n  CHECK(get_output(vis::detail::Topology::Quad) == \"Quad\"s);\n  CHECK(get_output(vis::detail::Topology::Wedge) == \"Wedge\"s);\n  CHECK(get_output(vis::detail::Topology::Hexahedron) == \"Hexahedron\"s);\n\n  CHECK(vis::detail::xdmf_topology_type(vis::detail::Topology::Line) == 2);\n  CHECK(vis::detail::xdmf_topology_type(vis::detail::Topology::Triangle) == 4);\n  CHECK(vis::detail::xdmf_topology_type(vis::detail::Topology::Quad) == 5);\n  CHECK(vis::detail::xdmf_topology_type(vis::detail::Topology::Wedge) == 8);\n  CHECK(vis::detail::xdmf_topology_type(vis::detail::Topology::Hexahedron) ==\n        9);\n}\n"
  },
  {
    "path": "tests/Unit/IO/eos.parameters",
    "content": " # order of interpolation in first, second and third index\n           3           3           3\n # calculation of beta-equilibrium (1: yes, else: no) and for given entropy (1: yes, else: no)\n           0           0\n # tabulation scheme (0 = explicit listing, 1 = loops, see manual)\n           1\n # parameter values (first, second and third index) depending on tabulation scheme\n  0.10000000000000001        1.0000000000000000E-012  0.01\n   158.00000000000000        1.90  0.6\n          2           4           3\n           1           1           0\n"
  },
  {
    "path": "tests/Unit/IO/eos.quantities",
    "content": " # number of regular, additional and derivative quantities (see table 7.1)\n           7           0           1\n # indices of regular, additional and derivative quantities\n          1 2 3 4 5 7 12 1\n # number of pairs and quadruples for composition data (see table 3.2/3.3)\n          0           0\n # indices for pairs and quadruples for composition data\n\n # number of microscopic quantities (see section 4.2.4)\n           0\n # indices of microscopic quantities\n\n # number of error quantities\n           0\n # indices of error quantities\n\n # format of output file, ASCII (1) or HDF5 (else)\n           1\n"
  },
  {
    "path": "tests/Unit/IO/eos.table",
    "content": "  0.10000000000000001        9.9999999999999998E-013   1.0000000000000000E-002   3.2731420000000011E-012   158.12359000000009       -1.6556880602462847       -19.842156349184702       -19.841845353053849        1.1372398000000006E-002   4.2176792400813656E-003   934.43810148373984     \n  0.10000000000000001        9.9999999999999998E-013  0.30499999999999999        3.1893989140625013E-012   142.35139851562508       -1.8745694607276671       -19.190385749183669       -19.180888622625869        3.5346163132812542E-003   4.1456131080695067E-003   928.65121304890044     \n  0.10000000000000001        9.9999999999999998E-013  0.59999999999999998        3.1999981000000014E-012   143.93577000000008       -15.559631577364950        12.436817954442299        12.455422287937649        3.3070626000000045E-003   4.1172877390651744E-003   928.27897438227694     \n  0.10000000000000001        1.2385623296301713E-008   1.0000000000000000E-002   1.2155360915010773E-009   9.3829711464065095      -0.71347327189280818       -22.670750207296898       -22.222766207600028       -1.0169032790344480E-004   1.7336626487566469E-004   938.53150819022767     \n  0.10000000000000001        1.2385623296301713E-008  0.30499999999999999        8.3016422052875601E-010  0.99124464633440812       -1.0456573497444996       -21.646087184271057       -20.601886764725815       -7.7663669049909921E-003   9.2710083433018563E-005   932.16921623914311     \n  0.10000000000000001        1.2385623296301713E-008  0.59999999999999998        2.1627258654874539E-009   2.4034713737830846       -16.605664215691274        14.390424834977958        15.658889679099508       -7.6041533773714091E-003   2.6630342167846473E-004   932.18040376414547     \n  0.10000000000000001        1.5340366443789173E-004   1.0000000000000000E-002   3.2099012875311601E-005  0.83152863391388587       0.43805416019382620       -26.755199814299104       -19.700822293861712        1.2234741042710821E-004   3.4802175676683546E-004   939.59715052645186     \n  0.10000000000000001        1.5340366443789173E-004  0.30499999999999999        2.4468017331204212E-004  0.15564581028786784      -0.19600929781720619       -25.385280320216147       -3.3834089107048677       -2.9897660752421736E-003   2.2675519460182546E-003   936.74070481647925     \n  0.10000000000000001        1.5340366443789173E-004  0.59999999999999998        6.1349624337516350E-004  0.43268025604719001       -18.714060368760126        17.192821769637060        44.759109646262324        4.4546614168404601E-003   5.6319393106579359E-003   943.70752751624718     \n  0.10000000000000001        1.9000000000000024        1.0000000000000000E-002   2210.6053143615190        2.6550771478559753E-003   1895.9497406444118       -198.22942467070547       -35.332465886409203       0.77921157405081587        1.2305841985543764        1671.6852726725444     \n  0.10000000000000001        1.9000000000000024       0.30499999999999999        2354.6115554098865        3.4795982667156005E-003   1909.6037477324869       -28.325443917779911        480.62811399608114       0.86947269803694438        1.2418223794193011        1756.4914144088082     \n  0.10000000000000001        1.9000000000000024       0.59999999999999998        2538.9119061115266        3.3019006656817246E-003   1873.0882690980980        132.82602736094074        770.54386620803041        1.0634124481653968        1.2195142464169275        1938.7105006111608     \n   158.00000000000006        9.9999999999999998E-013   1.0000000000000000E-002   52.861468710971884        1485445157219.0459       -939.56534999255268       -3.4007065215488760E-009  -3.3915057215320162E-009   193541104778.35413       0.25395113490394422       -52861469826937.250     \n   158.00000000000006        9.9999999999999998E-013  0.30499999999999999        52.861468705720966        1485445156918.3232       -939.56534999359724        7.4961466524481554E-010   1.0312528875660281E-009   193541104733.22195       0.25395113613208825       -52861469821686.242     \n   158.00000000000006        9.9999999999999998E-013  0.59999999999999998        52.861468710893689        1485445157214.1091       -939.56534999464191        4.8998992671017037E-009   5.4538797241667128E-009   193541104777.67715       0.25395113559980687       -52861469826858.977     \n   158.00000000000006        1.2385623296301713E-008   1.0000000000000000E-002   52.861692578286011        119933515.67157803       -939.56525776035880       -4.2118705904313029E-005  -4.2004349433156367E-005   15626334.678079225       0.25368373066295680       -4267988168.9958286     \n   158.00000000000006        1.2385623296301713E-008  0.30499999999999999        52.861692577931898        119933515.67303930       -939.56527069940967        9.2845115074833722E-006   1.2772383912169461E-005   15626334.678358778       0.25368373062477667       -4267988168.9672489     \n   158.00000000000006        1.2385623296301713E-008  0.59999999999999998        52.861692577582062        119933517.06181532       -939.56528363845996        6.0687726877748120E-005   6.7549115267934927E-005   15626334.912313508       0.25368372026896685       -4267988168.9389825     \n   158.00000000000006        1.5340366443789173E-004   1.0000000000000000E-002   52.861449400505300        9683.2483710455308       -938.41518441013659      -0.53075097669568272      -0.52933460049891867        1260.6482389767325       0.25365338177418945       -344589.38828202756     \n   158.00000000000006        1.5340366443789173E-004  0.30499999999999999        52.861440607424719        9683.2451082927018       -938.58050505591427       0.11399145866263270       0.15719093234876486        1260.6475515928282       0.25365477067007891       -344589.44304620347     \n   158.00000000000006        1.5340366443789173E-004  0.59999999999999998        52.861463344038555        9683.2512877997542       -938.74176940754023       0.75613580630043975       0.84111837567640779        1260.6487573789184       0.25365467228324629       -344589.29579705157     \n   158.00000000000006        1.9000000000000024        1.0000000000000000E-002   5535.2247866186935        5.0965517306845944        3214.4054109629301       -137.87674894108289       -120.35548478633407        1.1740182149625666       -28.866914059936207        1239.4909811346472     \n   158.00000000000006        1.9000000000000024       0.30499999999999999        6793.9338434046485        6.2536132566462319       -4889.2134102447071        11.805107240332074        366.21296279624414       -7.8404943805046665        66.325790590411600       -7413.7119665909195     \n   158.00000000000006        1.9000000000000024       0.59999999999999998        6544.9524934507153        6.5582247883215183       -4340.8030471113798        131.01280476673062        642.02667219177931       -6.7725747751703373       -194.71183243952410       -6460.7335245463910     \n"
  },
  {
    "path": "tests/Unit/Informer/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Informer.Python\"\n  \"Test_InfoAtCompile.py\"\n  \"Unit;Informer;Python\"\n  PyInformer\n  )\n"
  },
  {
    "path": "tests/Unit/Informer/Test_InfoAtCompile.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre import Informer\n\nVERSION_PATTERN = r\"\\d{4}\\.\\d{2}\\.\\d{2}(\\.\\d+)?\"\n\n\nclass TestInformer(unittest.TestCase):\n    def test_spectre_version(self):\n        self.assertRegex(Informer.spectre_version(), VERSION_PATTERN)\n\n    # The unit test src path and unit test build path are unpredictable,\n    # but the last 12 characters must be '/tests/Unit/'\n    def test_unit_test_src_path(self):\n        self.assertEqual(Informer.unit_test_src_path()[-12:], \"/tests/Unit/\")\n\n    def test_unit_test_build_path(self):\n        self.assertEqual(Informer.unit_test_build_path()[-12:], \"/tests/Unit/\")\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Convergence)\nadd_subdirectory(DiscontinuousGalerkin)\nadd_subdirectory(FiniteDifference)\nadd_subdirectory(Integration)\nadd_subdirectory(Interpolation)\nadd_subdirectory(LinearAlgebra)\nadd_subdirectory(LinearOperators)\nadd_subdirectory(LinearSolver)\nadd_subdirectory(OdeIntegration)\nadd_subdirectory(RootFinding)\nadd_subdirectory(Spectral)\nadd_subdirectory(SphericalHarmonics)\nadd_subdirectory(SpinWeightedSphericalHarmonics)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Convergence/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Convergence\")\n\nset(LIBRARY_SOURCES\n  Test_Criteria.cpp\n  Test_HasConverged.cpp\n  Test_Reason.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  Convergence\n  DataStructures\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Convergence/Test_Criteria.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Convergence.Criteria\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  const Convergence::Criteria criteria{2, 0.1, 0.5};\n  CHECK(criteria == Convergence::Criteria{2, 0.1, 0.5});\n  CHECK(criteria != Convergence::Criteria{3, 0.1, 0.5});\n  CHECK(criteria != Convergence::Criteria{2, 1., 0.5});\n  CHECK(criteria != Convergence::Criteria{2, 0.1, 0.6});\n  test_serialization(criteria);\n  test_copy_semantics(criteria);\n  const auto created_criteria =\n      TestHelpers::test_creation<Convergence::Criteria>(\n          \"MaxIterations: 2\\n\"\n          \"AbsoluteResidual: 0.1\\n\"\n          \"RelativeResidual: 0.5\\n\");\n  CHECK(created_criteria == criteria);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Convergence/Test_HasConverged.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <string>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Convergence.HasConverged\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  const Convergence::Criteria criteria{2, 0., 0.5};\n  CHECK_FALSE(static_cast<bool>(\n      serialize_and_deserialize(Convergence::HasConverged{})));\n\n  {\n    INFO(\"Convergence logic\");\n    CHECK_FALSE(Convergence::criteria_match(criteria, 1, 1., 1.));\n    CHECK(Convergence::criteria_match(criteria, 2, 1., 1.) ==\n          Convergence::Reason::MaxIterations);\n    CHECK(Convergence::criteria_match(criteria, 1, 0., 1.) ==\n          Convergence::Reason::AbsoluteResidual);\n    CHECK(Convergence::criteria_match(criteria, 1, 1., 2.) ==\n          Convergence::Reason::RelativeResidual);\n  }\n\n  {\n    INFO(\"HasNotConverged\");\n    const Convergence::HasConverged has_not_converged_by_default{};\n    CHECK_FALSE(has_not_converged_by_default);\n    test_serialization(has_not_converged_by_default);\n    test_copy_semantics(has_not_converged_by_default);\n    const Convergence::HasConverged has_not_converged{criteria, 1, 1., 1.};\n    CHECK_FALSE(has_not_converged);\n    CHECK(get_output(has_not_converged) == \"Not yet converged.\");\n    test_serialization(has_not_converged);\n    test_copy_semantics(has_not_converged);\n  }\n\n  {\n    INFO(\"HasConverged - MaxIterations\");\n    const Convergence::HasConverged has_converged{criteria, 2, 1., 1.5};\n    CHECK(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::MaxIterations);\n    CHECK(has_converged.num_iterations() == 2);\n    CHECK(has_converged.residual_magnitude() == 1.);\n    CHECK(has_converged.initial_residual_magnitude() == 1.5);\n    CHECK(get_output(has_converged) ==\n          \"Reached the maximum number of iterations (2).\");\n    test_serialization(has_converged);\n    test_copy_semantics(has_converged);\n  }\n\n  {\n    INFO(\"HasConverged - AbsoluteResidual\");\n    const Convergence::HasConverged has_converged{criteria, 1, 0., 1.};\n    CHECK(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n    CHECK(get_output(has_converged) ==\n          \"AbsoluteResidual - The residual magnitude has decreased to 0 or \"\n          \"below (0).\");\n    test_serialization(has_converged);\n    test_copy_semantics(has_converged);\n  }\n\n  {\n    INFO(\"HasConverged - RelativeResidual\");\n    const Convergence::HasConverged has_converged{criteria, 1, 1., 2.};\n    CHECK(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::RelativeResidual);\n    CHECK(get_output(has_converged) ==\n          \"RelativeResidual - The residual magnitude has decreased to a \"\n          \"fraction of 0.5 of its initial value or below (0.5).\");\n    test_serialization(has_converged);\n    test_copy_semantics(has_converged);\n  }\n\n  {\n    INFO(\"HasConverged - fixed number of iterations incomplete\");\n    const Convergence::HasConverged has_converged{1, 0};\n    CHECK_FALSE(has_converged);\n    test_serialization(has_converged);\n    test_copy_semantics(has_converged);\n  }\n\n  {\n    INFO(\"HasConverged - fixed number of iterations complete\");\n    const Convergence::HasConverged has_converged{1, 1};\n    CHECK(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::NumIterations);\n    test_serialization(has_converged);\n    test_copy_semantics(has_converged);\n  }\n\n  {\n    INFO(\"HasConverged - error\");\n    const Convergence::HasConverged has_converged{Convergence::Reason::Error,\n                                                  \"Something went wrong!\", 2};\n    CHECK(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::Error);\n    CHECK(has_converged.error_message() == \"Something went wrong!\");\n    CHECK(has_converged.num_iterations() == 2);\n    test_serialization(has_converged);\n    test_copy_semantics(has_converged);\n    CHECK_THROWS_WITH(\n        has_converged.check_for_error(),\n        Catch::Matchers::ContainsSubstring(\"Something went wrong!\"));\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const Convergence::HasConverged has_not_converged{\n            Convergence::Criteria{2, 0., 0.5}, 1, 1., 1.};\n        has_not_converged.reason();\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to retrieve the convergence reason, but \"\n          \"has not yet converged.\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Convergence/Test_Reason.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <Convergence::Reason TheConvergenceReason>\nvoid test_construct_from_options() {\n  const auto created = TestHelpers::test_creation<Convergence::Reason>(\n      get_output(TheConvergenceReason));\n  CHECK(created == TheConvergenceReason);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Convergence.Reason\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  CHECK(get_output(Convergence::Reason::NumIterations) == \"NumIterations\");\n  CHECK(get_output(Convergence::Reason::MaxIterations) == \"MaxIterations\");\n  CHECK(get_output(Convergence::Reason::AbsoluteResidual) ==\n        \"AbsoluteResidual\");\n  CHECK(get_output(Convergence::Reason::RelativeResidual) ==\n        \"RelativeResidual\");\n  CHECK(get_output(Convergence::Reason::Error) == \"Error\");\n\n  test_construct_from_options<Convergence::Reason::NumIterations>();\n  test_construct_from_options<Convergence::Reason::MaxIterations>();\n  test_construct_from_options<Convergence::Reason::AbsoluteResidual>();\n  test_construct_from_options<Convergence::Reason::RelativeResidual>();\n  test_construct_from_options<Convergence::Reason::Error>();\n\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<Convergence::Reason>(\"Miracle\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"Miracle\\\" to Convergence::Reason\"));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Convergence/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TestLabel {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Convergence.Tags\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  TestHelpers::db::test_simple_tag<Convergence::Tags::Criteria<TestLabel>>(\n      \"ConvergenceCriteria(TestLabel)\");\n  TestHelpers::db::test_simple_tag<Convergence::Tags::Iterations<TestLabel>>(\n      \"Iterations(TestLabel)\");\n  TestHelpers::db::test_simple_tag<Convergence::Tags::IterationId<TestLabel>>(\n      \"IterationId(TestLabel)\");\n  TestHelpers::db::test_simple_tag<Convergence::Tags::HasConverged<TestLabel>>(\n      \"HasConverged(TestLabel)\");\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Tags)\n\nset(LIBRARY \"Test_NumericalDiscontinuousGalerkin\")\n\nset(LIBRARY_SOURCES\n  Test_ApplyMassMatrix.cpp\n  Test_Formulation.cpp\n  Test_HasReceivedFromAllMortars.cpp\n  Test_InterpolateFromBoundary.cpp\n  Test_LiftFlux.cpp\n  Test_LiftFromBoundary.cpp\n  Test_MetricIdentityJacobian.cpp\n  Test_MortarHelpers.cpp\n  Test_MortarInterpolator.cpp\n  Test_NormalDotFlux.cpp\n  Test_ProjectToBoundary.cpp\n  Test_SimpleBoundaryData.cpp\n  Test_SimpleMortarData.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  DiscontinuousGalerkin\n  ErrorHandling\n  Interpolation\n  LinearOperators\n  Spectral\n  SphericalHarmonicsHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_NumericalDiscontinuousGalerkinTags\")\n\nset(LIBRARY_SOURCES\n  Test_Formulation.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DiscontinuousGalerkin\n  ErrorHandling\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Tags/Test_Formulation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags/Formulation.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DiscontinuousGalerkin.Tags.Formulation\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<dg::Tags::Formulation>(\"Formulation\");\n  CHECK(TestHelpers::test_option_tag<dg::OptionTags::Formulation>(\n            \"StrongInertial\") == dg::Formulation::StrongInertial);\n  CHECK(TestHelpers::test_option_tag<dg::OptionTags::Formulation>(\n            \"WeakInertial\") == dg::Formulation::WeakInertial);\n  CHECK(TestHelpers::test_option_tag<dg::OptionTags::Formulation>(\n            \"StrongLogical\") == dg::Formulation::StrongLogical);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_ApplyMassMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace dg {\nnamespace {\n\n// This function computes the logical mass matrix exactly using the (normalized)\n// Vandermonde matrix: M = (V * V^T)^-1. We don't currently use this way of\n// computing the mass matrix in the DG code because it is cheaper to apply the\n// mass matrix as a pointwise multiplication over the grid.\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nMatrix exact_logical_mass_matrix(const size_t num_points) {\n  auto normalized_vandermonde_matrix =\n      Spectral::modal_to_nodal_matrix<BasisType, QuadratureType>(num_points);\n  for (size_t j = 0; j < num_points; ++j) {\n    const double normalization = sqrt(\n        Spectral::compute_basis_function_normalization_square<BasisType>(j));\n    for (size_t i = 0; i < num_points; ++i) {\n      normalized_vandermonde_matrix(i, j) /= normalization;\n    }\n  }\n  return inv(normalized_vandermonde_matrix *\n             trans(normalized_vandermonde_matrix));\n}\n\ntemplate <size_t Dim>\nstd::array<Matrix, Dim> exact_logical_mass_matrix(const Mesh<Dim>& mesh) {\n  std::array<Matrix, Dim> result{};\n  for (size_t d = 0; d < Dim; ++d) {\n    ASSERT(mesh.basis(d) == Spectral::Basis::Legendre,\n           \"This function is currently only implemented for a Legendre basis.\");\n    switch (mesh.quadrature(d)) {\n      case Spectral::Quadrature::Gauss: {\n        gsl::at(result, d) =\n            exact_logical_mass_matrix<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::Gauss>(\n                mesh.extents(d));\n        break;\n      }\n      case Spectral::Quadrature::GaussLobatto: {\n        gsl::at(result, d) =\n            exact_logical_mass_matrix<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::GaussLobatto>(\n                mesh.extents(d));\n        break;\n      }\n      default:\n        ERROR(\n            \"This function is currently only implemented for Gauss and \"\n            \"Gauss-Lobatto quadrature.\");\n    }\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nstd::array<Matrix, Dim> diag_logical_mass_matrix(const Mesh<Dim>& mesh) {\n  std::array<Matrix, Dim> result{};\n  for (size_t d = 0; d < Dim; ++d) {\n    const size_t num_points = mesh.extents(d);\n    const auto& weights = Spectral::quadrature_weights(mesh.slice_through(d));\n    gsl::at(result, d) = Matrix(num_points, num_points, 0.);\n    for (size_t i = 0; i < num_points; ++i) {\n      gsl::at(result, d)(i, i) = weights[i];\n    }\n  }\n  return result;\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid test_apply_mass_matrix(\n    const Mesh<Dim>& mesh, const std::array<Matrix, Dim>& mass_matrix,\n    const gsl::not_null<std::mt19937*> gen,\n    const gsl::not_null<std::uniform_real_distribution<double>*> dist) {\n  CAPTURE(mesh);\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  const auto scalar_field =\n      make_with_random_values<DataType>(gen, dist, num_grid_points);\n  CAPTURE(scalar_field);\n  const auto expected_mass_matrix_times_scalar_field =\n      apply_matrices(mass_matrix, scalar_field, mesh.extents());\n  {\n    INFO(\"Test with scalar\");\n    auto result = scalar_field;\n    apply_mass_matrix(make_not_null(&result), mesh);\n    CHECK_ITERABLE_APPROX(result, expected_mass_matrix_times_scalar_field);\n    apply_inverse_mass_matrix(make_not_null(&result), mesh);\n    CHECK_ITERABLE_APPROX(result, scalar_field);\n  }\n  {\n    INFO(\"Test with Variables\");\n    using tag1 = ::Tags::TempScalar<0, DataType>;\n    using tag2 = ::Tags::TempScalar<1, DataType>;\n    Variables<tmpl::list<tag1, tag2>> vars{num_grid_points};\n    get<tag1>(vars) = Scalar<DataType>(scalar_field);\n    get<tag2>(vars) = Scalar<DataType>(scalar_field);\n    apply_mass_matrix(make_not_null(&vars), mesh);\n    CHECK_ITERABLE_APPROX(get(get<tag1>(vars)),\n                          expected_mass_matrix_times_scalar_field);\n    CHECK_ITERABLE_APPROX(get(get<tag2>(vars)),\n                          expected_mass_matrix_times_scalar_field);\n    apply_inverse_mass_matrix(make_not_null(&vars), mesh);\n    CHECK_ITERABLE_APPROX(get(get<tag1>(vars)), scalar_field);\n    CHECK_ITERABLE_APPROX(get(get<tag2>(vars)), scalar_field);\n  }\n}\n\ntemplate <typename DataType>\nvoid test_apply_mass_matrix() {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist(-1., 1.);\n  {\n    INFO(\"1D\");\n    {\n      INFO(\"Gauss quadrature (exact)\");\n      const Mesh<1> mesh{\n          {{4}}, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss};\n      test_apply_mass_matrix<DataType>(mesh, exact_logical_mass_matrix(mesh),\n                                       make_not_null(&gen),\n                                       make_not_null(&dist));\n    }\n    {\n      INFO(\"Gauss-Lobatto quadrature\");\n      const Mesh<1> mesh{\n          {{4}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n      test_apply_mass_matrix<DataType>(mesh, diag_logical_mass_matrix(mesh),\n                                       make_not_null(&gen),\n                                       make_not_null(&dist));\n    }\n    {\n      INFO(\"2D\");\n      {\n        INFO(\"Gauss quadrature (exact)\");\n        const Mesh<2> mesh{\n            {{4, 2}}, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss};\n        test_apply_mass_matrix<DataType>(mesh, exact_logical_mass_matrix(mesh),\n                                         make_not_null(&gen),\n                                         make_not_null(&dist));\n      }\n      {\n        INFO(\"Gauss-Lobatto quadrature\");\n        const Mesh<2> mesh{{{4, 2}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n        test_apply_mass_matrix<DataType>(mesh, diag_logical_mass_matrix(mesh),\n                                         make_not_null(&gen),\n                                         make_not_null(&dist));\n      }\n    }\n    {\n      INFO(\"3D\");\n      {\n        INFO(\"Gauss quadrature (exact)\");\n        const Mesh<3> mesh{{{4, 2, 3}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::Gauss};\n        test_apply_mass_matrix<DataType>(mesh, exact_logical_mass_matrix(mesh),\n                                         make_not_null(&gen),\n                                         make_not_null(&dist));\n      }\n      {\n        INFO(\"Gauss-Lobatto quadrature\");\n        const Mesh<3> mesh{{{4, 2, 3}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n        test_apply_mass_matrix<DataType>(mesh, diag_logical_mass_matrix(mesh),\n                                         make_not_null(&gen),\n                                         make_not_null(&dist));\n      }\n    }\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.DiscontinuousGalerkin.ApplyMassMatrix\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test_apply_mass_matrix<DataVector>();\n  test_apply_mass_matrix<ComplexDataVector>();\n}\n\n}  // namespace dg\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_Formulation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.DiscontinuousGalerkin.Formulation\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  CHECK(get_output(dg::Formulation::StrongInertial) == \"StrongInertial\");\n  CHECK(get_output(dg::Formulation::WeakInertial) == \"WeakInertial\");\n  CHECK(get_output(dg::Formulation::StrongInertial) == \"StrongInertial\");\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_HasReceivedFromAllMortars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <unordered_map>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/HasReceivedFromAllMortars.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\n// [inbox_example]\ntemplate <size_t Dim>\nstruct InboxTag {\n  using type = std::unordered_map<int, dg::MortarMap<Dim, double>>;\n};\n// [inbox_example]\n\ntemplate <size_t Dim>\nusing InboxType = tmpl::type_from<InboxTag<Dim>>;\n\ntemplate <size_t Dim>\nvoid test_has_received_from_all_mortars() {\n  CAPTURE(Dim);\n  const int time = 0;\n  const ElementId<Dim> self_id{1};\n  {\n    INFO(\"No neighbors\");\n    const Element<Dim> element{self_id, {}};\n    const tuples::TaggedTuple<InboxTag<Dim>> inbox{InboxType<Dim>{{time, {}}}};\n    CHECK(\n        dg::has_received_from_all_mortars<InboxTag<Dim>>(time, element, inbox));\n  }\n  const ElementId<Dim> left_id{0};\n  const ElementId<Dim> right_id{2};\n  const Element<Dim> element{\n      self_id,\n      {{Direction<Dim>::lower_xi(),\n        {{left_id}, OrientationMap<Dim>::create_aligned()}},\n       {Direction<Dim>::upper_xi(),\n        {{right_id}, OrientationMap<Dim>::create_aligned()}}}};\n  {\n    INFO(\"Complete data\");\n    const tuples::TaggedTuple<InboxTag<Dim>> inbox{\n        InboxType<Dim>{{time,\n                        {{{Direction<Dim>::lower_xi(), left_id}, 2.},\n                         {{Direction<Dim>::upper_xi(), right_id}, 3.}}}}};\n    CHECK(\n        dg::has_received_from_all_mortars<InboxTag<Dim>>(time, element, inbox));\n  }\n  {\n    INFO(\"Missing neighbor data\");\n    const tuples::TaggedTuple<InboxTag<Dim>> inbox{\n        InboxType<Dim>{{time, {{{Direction<Dim>::lower_xi(), left_id}, 2.}}}}};\n    CHECK_FALSE(\n        dg::has_received_from_all_mortars<InboxTag<Dim>>(time, element, inbox));\n  }\n  {\n    INFO(\"Missing temporal data\");\n    const tuples::TaggedTuple<InboxTag<Dim>> inbox{InboxType<Dim>{\n        {time + 1, {{{Direction<Dim>::lower_xi(), left_id}, 2.}}}}};\n    CHECK_FALSE(\n        dg::has_received_from_all_mortars<InboxTag<Dim>>(time, element, inbox));\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DiscontinuousGalerkin.HasReceivedFromAllMortars\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test_has_received_from_all_mortars<1>();\n  test_has_received_from_all_mortars<2>();\n  test_has_received_from_all_mortars<3>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_InterpolateFromBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/InterpolateFromBoundary.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nvoid test(const double eps) {\n  using dt_variables_tags = tmpl::list<Tags::dt<Var1>, Tags::dt<Var2<Dim>>>;\n  Mesh<Dim> volume_mesh{8, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::Gauss};\n  Mesh<Dim> volume_mesh_gl{8, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  CAPTURE(Dim);\n  CAPTURE(volume_mesh);\n  const double correction_value = 2.0e-3;\n\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    CAPTURE(direction);\n    Variables<dt_variables_tags> volume_dt{volume_mesh.number_of_grid_points(),\n                                           3.0};\n\n    Variables<dt_variables_tags> volume_dt_correction_gl{\n        volume_mesh_gl.number_of_grid_points(), 0.0};\n    for (SliceIterator si(volume_mesh.extents(), direction.dimension(),\n                          direction.side() == Side::Upper\n                              ? volume_mesh.extents(direction.dimension()) - 1\n                              : 0);\n         si; ++si) {\n      get(get<Tags::dt<Var1>>(volume_dt_correction_gl))[si.volume_offset()] =\n          correction_value;\n      for (size_t i = 0; i < Dim; ++i) {\n        get<Tags::dt<Var2<Dim>>>(volume_dt_correction_gl)\n            .get(i)[si.volume_offset()] = correction_value * (i + 2.0);\n      }\n    }\n    const Variables<dt_variables_tags> volume_dt_correction_gauss =\n        intrp::RegularGrid<Dim>{volume_mesh_gl, volume_mesh}.interpolate(\n            volume_dt_correction_gl);\n\n    const Variables<dt_variables_tags> expected_volume_dt_gauss =\n        volume_dt + volume_dt_correction_gauss;\n\n    Variables<dt_variables_tags> dt_correction_on_boundary{\n        volume_mesh.slice_away(direction.dimension()).number_of_grid_points(),\n        correction_value};\n    for (size_t i = 0; i < Dim; ++i) {\n      get<Tags::dt<Var2<Dim>>>(dt_correction_on_boundary).get(i) *= (2.0 + i);\n    }\n    ::dg::interpolate_dt_terms_gauss_points(make_not_null(&volume_dt),\n                                            volume_mesh, direction,\n                                            dt_correction_on_boundary);\n\n    Approx local_approx = Approx::custom().epsilon(eps).scale(1.0);\n    CHECK_ITERABLE_CUSTOM_APPROX(get<Tags::dt<Var1>>(volume_dt),\n                                 get<Tags::dt<Var1>>(expected_volume_dt_gauss),\n                                 local_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        get<Tags::dt<Var2<Dim>>>(volume_dt),\n        get<Tags::dt<Var2<Dim>>>(expected_volume_dt_gauss), local_approx);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.DiscontinuousGalerkin.InterpolateDtCorrection\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test<1>(1.0e-14);\n  test<2>(1.0e-14);\n  test<3>(1.0e-14);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_LiftFlux.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DiscontinuousGalerkin.LiftFlux\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  {\n    INFO(\"Testing with I1\");\n    for (const auto& basis :\n         {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      CAPTURE(basis);\n\n      const Mesh<2> mesh{{{3, 5}}, basis, Spectral::Quadrature::GaussLobatto};\n\n      using Affine = domain::CoordinateMaps::Affine;\n      const Affine xi_map(-1., 1., -5., 7.);\n      const Affine eta_map(-1., 1., 2., 5.);\n      const auto coordinate_map =\n          domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n              domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>(xi_map,\n                                                                     eta_map));\n      const double element_length = (eta_map(std::array<double, 1>{{1.}}) -\n                                     eta_map(std::array<double, 1>{{-1.}}))[0];\n\n      const double weight =\n          Spectral::quadrature_weights(mesh.slice_through(1))[0];\n      const auto boundary_mesh = mesh.slice_through(0);\n\n      const auto magnitude_of_face_normal = magnitude(unnormalized_face_normal(\n          boundary_mesh, coordinate_map, Direction<2>::lower_eta()));\n\n      Variables<tmpl::list<Tags::NormalDotNumericalFlux<Var>>> flux(\n          boundary_mesh.number_of_grid_points());\n      get(get<Tags::NormalDotNumericalFlux<Var>>(flux)) =\n          DataVector({2., 3., 5.});\n\n      const Variables<tmpl::list<Var>> expected =\n          -2. / (element_length * weight) * flux;\n\n      CAPTURE(dg::lift_flux(flux, mesh.extents(1), magnitude_of_face_normal,\n                            mesh.basis(1)));\n      CAPTURE(expected);\n      CHECK_VARIABLES_APPROX(\n          dg::lift_flux(flux, mesh.extents(1), magnitude_of_face_normal, basis),\n          expected);\n    }\n  }\n  {\n    INFO(\"Testing with Zernike\");\n    for (const auto& basis :\n         {Spectral::Basis::ZernikeB1, Spectral::Basis::ZernikeB2,\n          Spectral::Basis::ZernikeB3}) {\n      CAPTURE(basis);\n      const Mesh<2> mesh{{3, 5},\n                         {basis, Spectral::Basis::Legendre},\n                         {Spectral::Quadrature::GaussRadauUpper,\n                          Spectral::Quadrature::GaussLobatto}};\n\n      using Affine = domain::CoordinateMaps::Affine;\n      const Affine xi_map(-1., 1., 0, 7.);\n      const Affine eta_map(-1., 1., 2., 5.);\n      const auto coordinate_map =\n          domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n              domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>(xi_map,\n                                                                     eta_map));\n      const double element_length = (xi_map(std::array<double, 1>{{1.}}) -\n                                     xi_map(std::array<double, 1>{{-1.}}))[0];\n\n      const double weight =\n          Spectral::quadrature_weights(mesh.slice_through(0))[2];\n      const auto boundary_mesh = mesh.slice_through(1);\n\n      const auto magnitude_of_face_normal = magnitude(unnormalized_face_normal(\n          boundary_mesh, coordinate_map, Direction<2>::upper_xi()));\n\n      Variables<tmpl::list<Tags::NormalDotNumericalFlux<Var>>> flux(\n          boundary_mesh.number_of_grid_points());\n      get(get<Tags::NormalDotNumericalFlux<Var>>(flux)) =\n          DataVector({2., 3., 5., 8., 6.});\n\n      const Variables<tmpl::list<Var>> expected =\n          -2. / (element_length * weight) * flux;\n\n      CHECK_VARIABLES_APPROX(\n          dg::lift_flux(flux, mesh.extents(0), magnitude_of_face_normal, basis),\n          expected);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_LiftFromBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFromBoundary.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/WeakDivergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <typename TagsList, size_t Dim>\nauto polynomial_volume_fluxes(\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords,\n    const Index<Dim>& powers) {\n  Variables<db::wrap_tags_in<Tags::Flux, TagsList, tmpl::size_t<Dim>,\n                             Frame::Inertial>>\n      result(get<0>(coords).size(), 1.0);\n\n  if constexpr (tmpl::list_contains_v<TagsList, Var1>) {\n    for (size_t i = 1; i < Dim; ++i) {\n      get<Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>(result).get(i) =\n          0.0;\n    }\n  }\n\n  for (size_t power_index = 0; power_index < Dim; ++power_index) {\n    if constexpr (tmpl::list_contains_v<TagsList, Var1>) {\n      get<0>(get<Tags::Flux<Var1, tmpl::size_t<Dim>, Frame::Inertial>>(\n          result)) *= (1.0) * pow(coords.get(power_index), powers[power_index]);\n    }\n    if constexpr (tmpl::list_contains_v<TagsList, Var2<Dim>>) {\n      for (size_t i = 0; i < Dim; ++i) {\n        for (size_t j = 0; j < Dim; ++j) {\n          get<Tags::Flux<Var2<Dim>, tmpl::size_t<Dim>, Frame::Inertial>>(result)\n              .get(i, j) *= (i + 2.0) * (j + 3.0) *\n                            pow(coords.get(power_index), powers[power_index]);\n        }\n      }\n    }\n  }\n  return result;\n}\n\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t VolumeDim>\nauto make_map();\n\ntemplate <>\nauto make_map<1>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine{-1.0, 1.0, -0.3, 0.7});\n}\n\ntemplate <>\nauto make_map<2>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine2D{{-1.0, 1.0, -1.0, -0.9}, {-1.0, 1.0, -1.0, -0.9}},\n      domain::CoordinateMaps::Wedge<2>{\n          1.0, 2.0, 0.0, 1.0, OrientationMap<2>::create_aligned(), false});\n}\n\ntemplate <>\nauto make_map<3>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine3D{{-1.0, 1.0, -1.0, -0.9},\n               {-1.0, 1.0, -1.0, -0.9},\n               {-1.0, 1.0, -1.0, 1.0}},\n      domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::Wedge<2>,\n                                             Affine>{\n          {1.0, 2.0, 0.0, 1.0, OrientationMap<2>::create_aligned(), false},\n          {0.0, 1.0, 0.0, 1.0}});\n}\n\ntemplate <size_t Dim>\nvoid test(const double eps) {\n  // Allow specifying either Var1 or Var2 so that debugging is easier. This test\n  // will be useful when trying to better understand preserving the metric\n  // identities numerically.\n  //\n  // Currently the weak form with the lifting test as coded here does not\n  // satisfy the metric identities on curved meshes. This is likely because the\n  // boundary term does not have the metric identity satisfying normal and\n  // Jacobian terms.\n  using tags = tmpl::list<Var1, Var2<Dim>>;\n  Mesh<Dim> volume_mesh{8, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::Gauss};\n  CAPTURE(Dim);\n  CAPTURE(volume_mesh);\n\n  Index<Dim> powers{};\n  for (size_t i = 0; i < Dim; ++i) {\n    powers[i] = volume_mesh.extents(i) - 4 - i;\n  }\n\n  const auto map = make_map<Dim>();\n  const auto logical_coords = logical_coordinates(volume_mesh);\n  const auto inertial_coords = map(logical_coords);\n  const auto volume_inv_jacobian = map.inv_jacobian(logical_coords);\n  const auto [volume_det_inv_jacobian, volume_jacobian] =\n      determinant_and_inverse(volume_inv_jacobian);\n\n  auto volume_fluxes = polynomial_volume_fluxes<tags>(inertial_coords, powers);\n\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      det_jac_times_inverse_jacobian{volume_mesh.number_of_grid_points()};\n  dg::metric_identity_det_jac_times_inv_jac(\n      make_not_null(&det_jac_times_inverse_jacobian), volume_mesh,\n      inertial_coords, volume_jacobian);\n\n  Variables<db::wrap_tags_in<\n      Tags::div, typename std::decay_t<decltype(volume_fluxes)>::tags_list>>\n      weak_div_fluxes{volume_mesh.number_of_grid_points()};\n  weak_divergence(make_not_null(&weak_div_fluxes), volume_fluxes, volume_mesh,\n                  det_jac_times_inverse_jacobian);\n  weak_div_fluxes *= get(volume_det_inv_jacobian);\n\n  Variables<db::wrap_tags_in<Tags::dt, tags>> dt_vars_lifted_one_at_a_time{\n      volume_mesh.number_of_grid_points(), 0.0};\n  Variables<db::wrap_tags_in<Tags::dt, tags>> dt_vars_lifted_two_at_a_time{\n      volume_mesh.number_of_grid_points()};\n  Variables<db::wrap_tags_in<Tags::dt, tags>> dt_vars_lifted_separately{\n      volume_mesh.number_of_grid_points(), 0.};\n  Variables<db::wrap_tags_in<Tags::dt, tags>> dt_vars_buffer{\n      volume_mesh.number_of_grid_points()};\n  std::copy(weak_div_fluxes.data(),\n            weak_div_fluxes.data() + weak_div_fluxes.size(),\n            dt_vars_lifted_one_at_a_time.data());\n  std::copy(weak_div_fluxes.data(),\n            weak_div_fluxes.data() + weak_div_fluxes.size(),\n            dt_vars_lifted_two_at_a_time.data());\n  std::copy(weak_div_fluxes.data(),\n            weak_div_fluxes.data() + weak_div_fluxes.size(),\n            dt_vars_lifted_separately.data());\n\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    // compute geometric terms\n    const auto face_mesh = volume_mesh.slice_away(direction.dimension());\n    const auto face_logical_coords =\n        interface_logical_coordinates(face_mesh, direction);\n    const auto face_inertial_coords = map(face_logical_coords);\n    const auto face_det_jacobian =\n        determinant(map.jacobian(face_logical_coords));\n    const auto face_normal =\n        unnormalized_face_normal(face_mesh, map, direction);\n    const auto face_normal_magnitude = magnitude(face_normal);\n    auto unit_face_normal = face_normal;\n    for (auto& t : unit_face_normal) {\n      t /= get(face_normal_magnitude);\n    }\n\n    const auto boundary_corrections = normal_dot_flux<tags>(\n        unit_face_normal,\n        polynomial_volume_fluxes<tags>(face_inertial_coords, powers));\n\n    // Now perform lifting\n    ::dg::lift_boundary_terms_gauss_points(\n        make_not_null(&dt_vars_lifted_one_at_a_time), volume_det_inv_jacobian,\n        volume_mesh, direction, boundary_corrections, face_normal_magnitude,\n        face_det_jacobian);\n\n    auto boundary_corrections_times_face_jacobian = boundary_corrections;\n    boundary_corrections_times_face_jacobian *= get(face_det_jacobian);\n    boundary_corrections_times_face_jacobian *= get(face_normal_magnitude);\n    ::dg::apply_mass_matrix(\n        make_not_null(&boundary_corrections_times_face_jacobian), face_mesh);\n    dt_vars_buffer.initialize(volume_mesh.number_of_grid_points(), 0.0);\n    ::dg::lift_boundary_terms_gauss_points(\n        make_not_null(&dt_vars_buffer),\n        boundary_corrections_times_face_jacobian, volume_mesh, direction);\n    ::dg::apply_inverse_mass_matrix(make_not_null(&dt_vars_buffer),\n                                    volume_mesh);\n    dt_vars_buffer *= get(volume_det_inv_jacobian);\n    dt_vars_lifted_separately -= dt_vars_buffer;\n\n    if (direction.side() == Side::Upper) {\n      // Test lifting both upper and lower correction at the same time\n      const auto lower_direction = direction.opposite();\n      const auto lower_face_mesh =\n          volume_mesh.slice_away(lower_direction.dimension());\n      const auto lower_face_logical_coords =\n          interface_logical_coordinates(lower_face_mesh, lower_direction);\n      const auto lower_face_inertial_coords = map(lower_face_logical_coords);\n      const auto lower_face_det_jacobian =\n          determinant(map.jacobian(lower_face_logical_coords));\n      const auto lower_face_normal =\n          unnormalized_face_normal(lower_face_mesh, map, lower_direction);\n      const auto lower_face_normal_magnitude = magnitude(lower_face_normal);\n      auto lower_unit_face_normal = lower_face_normal;\n      for (auto& t : lower_unit_face_normal) {\n        t /= get(lower_face_normal_magnitude);\n      }\n\n      const auto lower_boundary_corrections = normal_dot_flux<tags>(\n          lower_unit_face_normal,\n          polynomial_volume_fluxes<tags>(lower_face_inertial_coords, powers));\n\n      // Now perform lifting\n      ::dg::lift_boundary_terms_gauss_points(\n          make_not_null(&dt_vars_lifted_two_at_a_time), volume_det_inv_jacobian,\n          volume_mesh, direction.dimension(), boundary_corrections,\n          face_normal_magnitude, face_det_jacobian, lower_boundary_corrections,\n          lower_face_normal_magnitude, lower_face_det_jacobian);\n    }\n  }\n\n  Variables<db::wrap_tags_in<\n      Tags::div, typename std::decay_t<decltype(volume_fluxes)>::tags_list>>\n      div_fluxes{volume_mesh.number_of_grid_points()};\n  divergence(make_not_null(&div_fluxes), volume_fluxes, volume_mesh,\n             volume_inv_jacobian);\n  Variables<db::wrap_tags_in<Tags::dt, tags>> expected_dt_vars{\n      volume_mesh.number_of_grid_points()};\n  std::copy(div_fluxes.data(), div_fluxes.data() + div_fluxes.size(),\n            expected_dt_vars.data());\n  expected_dt_vars *= -1.0;\n\n  Approx local_approx = Approx::custom().epsilon(eps).scale(1.0);\n  tmpl::for_each<typename decltype(expected_dt_vars)::tags_list>(\n      [&dt_vars_lifted_one_at_a_time, &dt_vars_lifted_two_at_a_time,\n       &dt_vars_lifted_separately, &expected_dt_vars,\n       &local_approx](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_CUSTOM_APPROX(get<tag>(dt_vars_lifted_one_at_a_time),\n                                     get<tag>(expected_dt_vars), local_approx);\n        CHECK_ITERABLE_CUSTOM_APPROX(get<tag>(dt_vars_lifted_two_at_a_time),\n                                     get<tag>(expected_dt_vars), local_approx);\n        CHECK_ITERABLE_CUSTOM_APPROX(get<tag>(dt_vars_lifted_separately),\n                                     get<tag>(expected_dt_vars), local_approx);\n      });\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DiscontinuousGalerkin.LiftFromBoundary\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  // We test the lifting procedure (and at the same time the weak divergence\n  // more thoroughly) by checking the\n  //    weak divergence + lifting boundary terms == strong divergence\n  //\n  // Currently the weak form with the lifting test as coded here does not\n  // satisfy the metric identities on curved meshes. This is likely because the\n  // boundary term does not have the metric identity satisfying normal and\n  // Jacobian terms.\n  test<1>(1.0e-12);\n  test<2>(1.0e-8);\n  test<3>(1.0e-8);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_MetricIdentityJacobian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <random>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t Dim>\nauto make_map() {\n  if constexpr (Dim == 1) {\n    return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n        Affine{-1.0, 1.0, -0.3, 0.7});\n  } else if constexpr (Dim == 2) {\n    return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n        Affine2D{{-1.0, 1.0, -1.0, -0.99}, {-1.0, 1.0, -1.0, -0.99}},\n        domain::CoordinateMaps::Wedge<2>{\n            1.0, 2.0, 0.0, 1.0, OrientationMap<2>::create_aligned(), false});\n  } else {\n    return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n        Affine3D{{-1.0, 1.0, -1.0, -0.99},\n                 {-1.0, 1.0, -1.0, -0.99},\n                 {-1.0, 1.0, -1.0, 1.0}},\n        domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::Wedge<2>,\n                                               Affine>{\n            {1.0, 2.0, 0.0, 1.0, OrientationMap<2>::create_aligned(), false},\n            {0.0, 1.0, 0.0, 1.0}});\n  }\n}\n\ntemplate <size_t Dim>\nvoid test(const Mesh<Dim>& mesh) {\n  CAPTURE(Dim);\n  CAPTURE(mesh);\n  std::uniform_real_distribution<double> dist(-1.0, 2.3);\n  MAKE_GENERATOR(gen);\n  {\n    tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords{\n        mesh.number_of_grid_points()};\n    Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial> jacobian{\n        mesh.number_of_grid_points()};\n\n    fill_with_random_values(make_not_null(&inertial_coords),\n                            make_not_null(&gen), make_not_null(&dist));\n    fill_with_random_values(make_not_null(&jacobian), make_not_null(&gen),\n                            make_not_null(&dist));\n\n    InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n        result{};\n\n    dg::metric_identity_det_jac_times_inv_jac(make_not_null(&result), mesh,\n                                              inertial_coords, jacobian);\n\n    // Check metric identities are satisfied by taking the numerical divergence\n    tnsr::i<DataVector, Dim, Frame::Inertial> divergence_terms{\n        mesh.number_of_grid_points(), 0.0};\n    DataVector buffer(mesh.number_of_grid_points());\n    std::array<Mesh<1>, Dim> meshes_1d{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(meshes_1d, i) = mesh.slice_through(i);\n    }\n    const Matrix identity{};\n\n    for (size_t i_hat = 0; i_hat < Dim; ++i_hat) {  // logical index\n      auto diff_matrices = make_array<Dim>(std::cref(identity));\n      gsl::at(diff_matrices, i_hat) =\n          Spectral::differentiation_matrix(gsl::at(meshes_1d, i_hat));\n      for (size_t i = 0; i < Dim; ++i) {  // inertial index\n        apply_matrices(make_not_null(&buffer), diff_matrices,\n                       result.get(i_hat, i), mesh.extents());\n        divergence_terms.get(i) += buffer;\n      }\n    }\n\n    Approx local_approx = Approx::custom().epsilon(1.0e-12).scale(1.);\n    const DataVector expected{mesh.number_of_grid_points(), 0.0};\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_CUSTOM_APPROX(divergence_terms.get(i), expected,\n                                   local_approx);\n    }\n  }\n\n  // Now check with a map that the terms of the inverse Jacobian (times the\n  // Jacobian determinent) are computed correctly. This is not expected to be\n  // super accurate unless the maps are very well resolved.\n  auto map = make_map<Dim>();\n  const InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      analytic_inverse_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      analytic_det_jac_times_inverse_jacobian = analytic_inverse_jacobian;\n  const Scalar<DataVector> analytic_det_jac{\n      1.0 / get(determinant(analytic_inverse_jacobian))};\n  for (auto& t : analytic_det_jac_times_inverse_jacobian) {\n    t *= get(analytic_det_jac);\n  }\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      det_jac_times_inverse_jacobian{};\n\n  dg::metric_identity_det_jac_times_inv_jac(\n      make_not_null(&det_jac_times_inverse_jacobian), mesh,\n      map(logical_coordinates(mesh)), map.jacobian(logical_coordinates(mesh)));\n\n  Approx coarse_local_approx = Approx::custom().epsilon(1.0e-9).scale(1.);\n  for (size_t i = 0; i < Dim; ++i) {\n    CAPTURE(i);\n    for (size_t j = 0; j < Dim; ++j) {\n      CAPTURE(j);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          det_jac_times_inverse_jacobian.get(i, j),\n          analytic_det_jac_times_inverse_jacobian.get(i, j),\n          coarse_local_approx);\n    }\n  }\n\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      det_jac_times_inverse_jacobian2{};\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian{};\n  Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial> jacobian =\n      map.jacobian(logical_coordinates(mesh));\n  const auto expected_jacobian = jacobian;\n  Scalar<DataVector> det_jacobian{};\n\n  dg::metric_identity_jacobian_quantities(\n      make_not_null(&det_jac_times_inverse_jacobian2),\n      make_not_null(&inverse_jacobian), make_not_null(&jacobian),\n      make_not_null(&det_jacobian), mesh, map(logical_coordinates(mesh)));\n\n  Approx identity_matrix_local_approx =\n      Approx::custom().epsilon(1.0e-12).scale(1.);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(det_jacobian), get(analytic_det_jac),\n                               coarse_local_approx);\n  for (size_t i = 0; i < Dim; ++i) {\n    CAPTURE(i);\n    for (size_t j = 0; j < Dim; ++j) {\n      CAPTURE(j);\n      CHECK_ITERABLE_APPROX(det_jac_times_inverse_jacobian.get(i, j),\n                            det_jac_times_inverse_jacobian2.get(i, j));\n      CHECK_ITERABLE_APPROX(\n          DataVector{get(det_jacobian) * inverse_jacobian.get(i, j)},\n          det_jac_times_inverse_jacobian2.get(i, j));\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          jacobian.get(i, j), expected_jacobian.get(i, j), coarse_local_approx);\n      DataVector jac_inv_jac_contracted =\n          jacobian.get(i, 0) * inverse_jacobian.get(0, j);\n      for (size_t k = 1; k < Dim; ++k) {\n        jac_inv_jac_contracted +=\n            jacobian.get(i, k) * inverse_jacobian.get(k, j);\n      }\n      const DataVector expected{mesh.number_of_grid_points(),\n                                i == j ? 1.0 : 0.0};\n      CHECK_ITERABLE_CUSTOM_APPROX(expected, jac_inv_jac_contracted,\n                                   identity_matrix_local_approx);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DG.MetricIdentityJacobian\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test(Mesh<1>{5, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto});\n  test(Mesh<1>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss});\n\n  test(Mesh<2>{5, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto});\n  test(Mesh<2>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss});\n\n  test(Mesh<3>{5, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto});\n  test(Mesh<3>{5, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss});\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_MortarHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <initializer_list>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/ChildSize.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/LiftFlux.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nMesh<Dim> lgl_mesh(const std::array<size_t, Dim>& extents) {\n  return {extents, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto};\n}\n\nvoid test_mortar_mesh() {\n  CHECK(dg::mortar_mesh(lgl_mesh<0>({}), lgl_mesh<0>({})) == lgl_mesh<0>({}));\n  CHECK(dg::mortar_mesh(lgl_mesh<1>({{3}}), lgl_mesh<1>({{5}})) ==\n        lgl_mesh<1>({{5}}));\n  CHECK(dg::mortar_mesh(lgl_mesh<2>({{2, 5}}), lgl_mesh<2>({{3, 4}})) ==\n        lgl_mesh<2>({{3, 5}}));\n}\n\ntemplate <size_t Dim>\nstd::array<Spectral::SegmentSize, Dim - 1> call_mortar_size(\n    const ElementId<Dim>& self, const ElementId<Dim>& neighbor,\n    const size_t dimension, const OrientationMap<Dim>& orientation) {\n  const auto size = dg::mortar_size(self, neighbor, dimension, orientation);\n  const auto segments =\n      dg::mortar_segments(self, neighbor, dimension, orientation);\n  CHECK(size ==\n        domain::child_size(segments, all_but_specified_element_of(\n                                         self.segment_ids(), dimension)));\n  return size;\n}\n\nvoid test_mortar_size() {\n  CHECK(call_mortar_size(ElementId<1>(0, {{{0, 0}}}),\n                         ElementId<1>(0, {{{5, 2}}}), 0,\n                         OrientationMap<1>::create_aligned()) ==\n        std::array<Spectral::SegmentSize, 0>{});\n\n  // Check the root segment to make sure the code doesn't try to get\n  // its parent.\n  CHECK(call_mortar_size(ElementId<2>(0, {{{0, 0}, {0, 0}}}),\n                         ElementId<2>(1, {{{0, 0}, {0, 0}}}), 1,\n                         OrientationMap<2>::create_aligned()) ==\n        std::array<Spectral::SegmentSize, 1>{{Spectral::SegmentSize::Full}});\n  CHECK(call_mortar_size(ElementId<2>(0, {{{1, 0}, {0, 0}}}),\n                         ElementId<2>(1, {{{0, 0}, {0, 0}}}), 1,\n                         OrientationMap<2>::create_aligned()) ==\n        std::array<Spectral::SegmentSize, 1>{{Spectral::SegmentSize::Full}});\n  CHECK(\n      call_mortar_size(ElementId<2>(0, {{{0, 0}, {0, 0}}}),\n                       ElementId<2>(1, {{{1, 0}, {0, 0}}}), 1,\n                       OrientationMap<2>::create_aligned()) ==\n      std::array<Spectral::SegmentSize, 1>{{Spectral::SegmentSize::LowerHalf}});\n\n  // Check all the aligned cases in 3D\n  const auto test_segment = [](const SegmentId& base, const size_t test) {\n    switch (test) {\n      case 0:\n        return base;\n      case 1:\n        return base.id_of_parent();\n      case 2:\n        return base.id_of_child(Side::Lower);\n      case 3:\n        return base.id_of_child(Side::Upper);\n      default:\n        ERROR(\"Test logic error\");\n    }\n  };\n  const auto expected_size = [](const size_t test) {\n    switch (test) {\n      case 0:\n      case 1:\n        return Spectral::SegmentSize::Full;\n      case 2:\n        return Spectral::SegmentSize::LowerHalf;\n      case 3:\n        return Spectral::SegmentSize::UpperHalf;\n      default:\n        ERROR(\"Test logic error\");\n    }\n  };\n\n  const SegmentId segment0(1, 1);\n  const SegmentId segment1(2, 0);\n  // We do not expect to actually have abutting elements with\n  // difference greater than one in perpendicular refinement levels,\n  // but this function should work even in that situation, so we test\n  // it here.\n  const SegmentId perp0(5, 1);\n  const SegmentId perp1(7, 20);\n\n  for (size_t dimension = 0; dimension < 3; ++dimension) {\n    using SegArray = std::array<SegmentId, 2>;\n    const ElementId<3> self(\n        0, insert_element(SegArray{{segment0, segment1}}, dimension, perp0));\n\n    for (size_t test0 = 0; test0 < 4; ++test0) {\n      for (size_t test1 = 0; test1 < 4; ++test1) {\n        const ElementId<3> neighbor(\n            0, insert_element(SegArray{{test_segment(segment0, test0),\n                                        test_segment(segment1, test1)}},\n                              dimension, perp1));\n        CAPTURE(neighbor);\n        const std::array<Spectral::SegmentSize, 2> expected{\n            {expected_size(test0), expected_size(test1)}};\n        CHECK(call_mortar_size(self, neighbor, dimension,\n                               OrientationMap<3>::create_aligned()) ==\n              expected);\n      }\n    }\n  }\n\n  // Check an orientation case\n  CHECK(call_mortar_size(ElementId<3>(1, {{{0, 0}, {3, 2}, {7, 5}}}),\n                         ElementId<3>(4, {{{6, 61}, {3, 0}, {4, 5}}}), 0,\n                         OrientationMap<3>{{{Direction<3>::upper_eta(),\n                                             Direction<3>::upper_zeta(),\n                                             Direction<3>::lower_xi()}}}) ==\n        std::array<Spectral::SegmentSize, 2>{\n            {Spectral::SegmentSize::UpperHalf, Spectral::SegmentSize::Full}});\n}\n\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\n// Scales the coordinates to a mortar\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim, Frame::ElementLogical> scaled_coords(\n    tnsr::I<DataVector, Dim, Frame::ElementLogical> coords,\n    const std::array<Spectral::SegmentSize, Dim>& mortar_size) {\n  for (size_t d = 0; d < Dim; ++d) {\n    switch (gsl::at(mortar_size, d)) {\n      case Spectral::SegmentSize::LowerHalf:\n        coords.get(d) = 0.5 * (coords.get(d) - 1.);\n        break;\n      case Spectral::SegmentSize::UpperHalf:\n        coords.get(d) = 0.5 * (coords.get(d) + 1.);\n        break;\n      default:\n        break;\n    }\n  }\n  return coords;\n}\n\n// There is no nice way to get the basis functions from the spectral\n// code (bug #801).  In each case here we want the first unresolved\n// basis function.\nDataVector basis5(const DataVector& coords) {\n  return 1. / 8. * coords *\n         (15. + square(coords) * (-70. + square(coords) * 63.));\n}\nDataVector basis6(const DataVector& coords) {\n  return 1. / 16. *\n         (-5. + square(coords) *\n                    (105. + square(coords) * (-315. + square(coords) * 231.)));\n}\n\nvoid test_projections() {\n  const auto all_mortar_sizes = {Spectral::SegmentSize::Full,\n                                 Spectral::SegmentSize::LowerHalf,\n                                 Spectral::SegmentSize::UpperHalf};\n  // Check 0D\n  {\n    Variables<tmpl::list<Var>> vars(1);\n    get(get<Var>(vars)) = 4.;\n    CHECK(get<Var>(dg::project_to_mortar(vars, lgl_mesh<0>({}), lgl_mesh<0>({}),\n                                         {})) == Scalar<DataVector>{{{{4.}}}});\n  }\n  // Check 1D\n  {\n    const auto mortar_mesh = lgl_mesh<1>({{7}});\n    const auto mortar_coords = logical_coordinates(mortar_mesh);\n    const auto func =\n        [](const tnsr::I<DataVector, 1, Frame::ElementLogical>& coords)\n        -> DataVector { return pow<3>(get<0>(coords)); };\n    for (const auto& face_mesh : {mortar_mesh, lgl_mesh<1>({{5}})}) {\n      CAPTURE(face_mesh);\n      const auto face_coords = logical_coordinates(face_mesh);\n      // face -> mortar\n      for (const auto& slice_size : all_mortar_sizes) {\n        const std::array<Spectral::SegmentSize, 1> mortar_size{{slice_size}};\n        CAPTURE(mortar_size);\n        Variables<tmpl::list<Var>> vars(face_mesh.number_of_grid_points());\n        get(get<Var>(vars)) = func(face_coords);\n        CHECK_ITERABLE_APPROX(get(get<Var>(dg::project_to_mortar(\n                                  vars, face_mesh, mortar_mesh, mortar_size))),\n                              func(scaled_coords(mortar_coords, mortar_size)));\n      }\n\n      const auto make_mortar_data =\n          [&face_mesh, &func, &mortar_coords, &mortar_mesh](\n              const std::array<Spectral::SegmentSize, 1>& mortar_size) {\n            Variables<tmpl::list<Var>> vars(\n                mortar_mesh.number_of_grid_points());\n            get(get<Var>(vars)) =\n                func(scaled_coords(mortar_coords, mortar_size));\n            if (face_mesh.extents(0) < mortar_mesh.extents(0)) {\n              // Add some data orthogonal to the function space on the\n              // face.  It should be projected to zero.\n              get(get<Var>(vars)) +=\n                  basis5(get<0>(scaled_coords(mortar_coords, mortar_size)));\n            }\n            return vars;\n          };\n\n      // full mortar -> face\n      if (face_mesh != mortar_mesh) {\n        const std::array<Spectral::SegmentSize, 1> mortar_size{\n            {Spectral::SegmentSize::Full}};\n        CAPTURE(mortar_size);\n        const auto vars = make_mortar_data(mortar_size);\n        CHECK_ITERABLE_APPROX(get(get<Var>(dg::project_from_mortar(\n                                  vars, face_mesh, mortar_mesh, mortar_size))),\n                              func(face_coords));\n      }\n      // half mortar -> face\n      {\n        const auto vars_lo =\n            make_mortar_data({{Spectral::SegmentSize::LowerHalf}});\n        const auto vars_hi =\n            make_mortar_data({{Spectral::SegmentSize::UpperHalf}});\n        CHECK_ITERABLE_APPROX(get(get<Var>(dg::project_from_mortar(\n                                  vars_lo, face_mesh, mortar_mesh,\n                                  {{Spectral::SegmentSize::LowerHalf}}))) +\n                                  get(get<Var>(dg::project_from_mortar(\n                                      vars_hi, face_mesh, mortar_mesh,\n                                      {{Spectral::SegmentSize::UpperHalf}}))),\n                              func(face_coords));\n      }\n    }\n  }\n  // Check 2D\n  {\n    const auto mortar_mesh = lgl_mesh<2>({{7, 8}});\n    const auto mortar_coords = logical_coordinates(mortar_mesh);\n    const auto func =\n        [](const tnsr::I<DataVector, 2, Frame::ElementLogical>& coords)\n        -> DataVector {\n      return pow<3>(get<0>(coords)) * pow<5>(get<1>(coords));\n    };\n    for (const auto& face_mesh :\n         {mortar_mesh, lgl_mesh<2>({{5, 8}}), lgl_mesh<2>({{7, 6}}),\n          lgl_mesh<2>({{5, 6}})}) {\n      CAPTURE(face_mesh);\n      const auto face_coords = logical_coordinates(face_mesh);\n      // face -> mortar\n      for (const auto& slice_size0 : all_mortar_sizes) {\n        for (const auto& slice_size1 : all_mortar_sizes) {\n          const std::array<Spectral::SegmentSize, 2> mortar_size{\n              {slice_size0, slice_size1}};\n          CAPTURE(mortar_size);\n          Variables<tmpl::list<Var>> vars(face_mesh.number_of_grid_points());\n          get(get<Var>(vars)) = func(face_coords);\n          CHECK_ITERABLE_APPROX(\n              get(get<Var>(dg::project_to_mortar(vars, face_mesh, mortar_mesh,\n                                                 mortar_size))),\n              func(scaled_coords(mortar_coords, mortar_size)));\n        }\n      }\n\n      const auto make_mortar_data =\n          [&face_mesh, &func, &mortar_coords, &mortar_mesh](\n              const std::array<Spectral::SegmentSize, 2>& mortar_size) {\n            Variables<tmpl::list<Var>> vars(\n                mortar_mesh.number_of_grid_points());\n            get(get<Var>(vars)) =\n                func(scaled_coords(mortar_coords, mortar_size));\n\n            // Add some data orthogonal to the function space on the face.\n            // It should be projected to zero.\n            if (face_mesh.extents(0) < mortar_mesh.extents(0)) {\n              get(get<Var>(vars)) += basis5(get<0>(mortar_coords));\n            }\n            if (face_mesh.extents(1) < mortar_mesh.extents(1)) {\n              get(get<Var>(vars)) += basis6(get<1>(mortar_coords));\n            }\n            return vars;\n          };\n\n      // full mortar -> face\n      if (face_mesh != mortar_mesh) {\n        const std::array<Spectral::SegmentSize, 2> mortar_size{\n            {Spectral::SegmentSize::Full, Spectral::SegmentSize::Full}};\n        const auto vars = make_mortar_data(mortar_size);\n        CHECK_ITERABLE_APPROX(get(get<Var>(dg::project_from_mortar(\n                                  vars, face_mesh, mortar_mesh, mortar_size))),\n                              func(face_coords));\n      }\n\n      // mortar -> face from a mortar long in one direction and short\n      // in the other\n      {\n        const auto vars_lo = make_mortar_data(\n            {{Spectral::SegmentSize::Full, Spectral::SegmentSize::LowerHalf}});\n        const auto vars_hi = make_mortar_data(\n            {{Spectral::SegmentSize::Full, Spectral::SegmentSize::UpperHalf}});\n        CHECK_ITERABLE_APPROX(get(get<Var>(dg::project_from_mortar(\n                                  vars_lo, face_mesh, mortar_mesh,\n                                  {{Spectral::SegmentSize::Full,\n                                    Spectral::SegmentSize::LowerHalf}}))) +\n                                  get(get<Var>(dg::project_from_mortar(\n                                      vars_hi, face_mesh, mortar_mesh,\n                                      {{Spectral::SegmentSize::Full,\n                                        Spectral::SegmentSize::UpperHalf}}))),\n                              func(face_coords));\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DG.MortarHelpers\", \"[Unit][NumericalAlgorithms]\") {\n  test_mortar_mesh();\n  test_mortar_size();\n  test_projections();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_MortarInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/Interval.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp\"\n#include \"Domain/CoordinateMaps/Wedge.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/NonconformingSphericalShells.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/BlockNeighbors.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/Topology.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarInterpolator.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nDataVector vars_shell(const Mesh<2>& shell_mortar_mesh) {\n  const YlmTestFunctions::ProductOfPolynomials f1(1, 2, 3);\n  const YlmTestFunctions::ProductOfPolynomials f2(3, 1, 2);\n  const auto shell_theta_phi = logical_coordinates(shell_mortar_mesh);\n  const DataVector f1_shell = f1(shell_theta_phi);\n  const DataVector f2_shell = f2(shell_theta_phi);\n  const size_t npts = shell_mortar_mesh.number_of_grid_points();\n  DataVector result{2 * npts};\n  std::copy(f1_shell.begin(), f1_shell.end(), result.begin());\n  std::copy(f2_shell.begin(), f2_shell.end(),\n            result.begin() + static_cast<ptrdiff_t>(npts));\n  return result;\n}\n\nDataVector vars_cubed_sphere(\n    const Domain<3>& domain, const ElementId<3>& neighbor_id,\n    const std::vector<std::array<size_t, 3>>& refinement_levels,\n    const Mesh<2>& cubed_sphere_mortar_mesh) {\n  const Element<3> cubed_sphere = domain::create_initial_element(\n      neighbor_id, domain.blocks(), refinement_levels);\n  const auto xi = interface_logical_coordinates(cubed_sphere_mortar_mesh,\n                                                Direction<3>::upper_zeta());\n  const ElementMap<3, Frame::Inertial> cubed_sphere_map{\n      neighbor_id, domain.blocks()[neighbor_id.block_id()]};\n  const auto x_inertial = cubed_sphere_map(xi);\n  const auto& x = get<0>(x_inertial);\n  const auto& y = get<1>(x_inertial);\n  const auto& z = get<2>(x_inertial);\n  const auto theta = atan2(hypot(x, y), z);\n  const auto phi = atan2(y, x);\n  const YlmTestFunctions::ProductOfPolynomials f1(1, 2, 3);\n  const YlmTestFunctions::ProductOfPolynomials f2(3, 1, 2);\n  const DataVector f1_cubed_sphere = f1(theta, phi);\n  const DataVector f2_cubed_sphere = f2(theta, phi);\n  const size_t npts = cubed_sphere_mortar_mesh.number_of_grid_points();\n  DataVector result{2 * npts};\n  std::copy(f1_cubed_sphere.begin(), f1_cubed_sphere.end(), result.begin());\n  std::copy(f2_cubed_sphere.begin(), f2_cubed_sphere.end(),\n            result.begin() + static_cast<ptrdiff_t>(npts));\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid insert_mortar_data(\n    DataVector& mortar_data, const Mesh<2>& target_mortar_mesh,\n    const DataVector& source_data,\n    const dg::MortarInterpolator<Dim>& interpolator) {\n  const auto& offsets = interpolator.interpolated_neighbor_data_offsets();\n  const DataVector subset_of_mortar_data =\n      interpolator.interpolate_to_neighbor(source_data);\n  for (size_t i = 0; i < offsets.size(); ++i) {\n    mortar_data[offsets[i]] = subset_of_mortar_data[i];\n    mortar_data[offsets[i] + target_mortar_mesh.number_of_grid_points()] =\n        subset_of_mortar_data[i + offsets.size()];\n  }\n}\n\nvoid test_non_conforming_spheres() {\n  const auto creator = domain::creators::NonconformingSphericalShells(\n      2.0, 3.0, 4.0, 0, 2, 5, 8, 11, nullptr, nullptr);\n  const auto domain = creator.create_domain();\n  const auto refinement_levels = creator.initial_refinement_levels();\n  const ElementId<3> shell_id{6};\n  const Element<3> shell = domain::create_initial_element(\n      shell_id, domain.blocks(), refinement_levels);\n  const auto& shell_neighbor_ids =\n      shell.neighbors().at(Direction<3>::lower_xi());\n  const Mesh<2> shell_mortar_mesh{\n      std::array{8_st, 15_st},\n      std::array{Spectral::Basis::SphericalHarmonic,\n                 Spectral::Basis::SphericalHarmonic},\n      std::array{Spectral::Quadrature::Gauss,\n                 Spectral::Quadrature::Equiangular}};\n  const Mesh<2> shell_mortar_mesh_2{\n      std::array{9_st, 17_st},\n      std::array{Spectral::Basis::SphericalHarmonic,\n                 Spectral::Basis::SphericalHarmonic},\n      std::array{Spectral::Quadrature::Gauss,\n                 Spectral::Quadrature::Equiangular}};\n  const Mesh<2> cubed_sphere_mortar_mesh{11_st, Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> cubed_sphere_mortar_mesh_2{12_st, Spectral::Basis::Legendre,\n                                           Spectral::Quadrature::GaussLobatto};\n  const DataVector v_shell = vars_shell(shell_mortar_mesh);\n  const DataVector v_shell_2 = vars_shell(shell_mortar_mesh_2);\n  DataVector interpolated_v_shell{2 * shell_mortar_mesh.number_of_grid_points(),\n                                  std::numeric_limits<double>::quiet_NaN()};\n  DataVector interpolated_v_shell_2{\n      2 * shell_mortar_mesh_2.number_of_grid_points(),\n      std::numeric_limits<double>::quiet_NaN()};\n  DataVector interpolated_v_shell_3{\n      2 * shell_mortar_mesh_2.number_of_grid_points(),\n      std::numeric_limits<double>::quiet_NaN()};\n  for (const auto& neighbor_id : shell_neighbor_ids) {\n    const DataVector v_cubed_sphere = vars_cubed_sphere(\n        domain, neighbor_id, refinement_levels, cubed_sphere_mortar_mesh);\n    dg::MortarInterpolator<3> interpolator{\n        neighbor_id, DirectionalId<3>{Direction<3>::upper_zeta(), shell_id},\n        domain, cubed_sphere_mortar_mesh, shell_mortar_mesh};\n    CHECK(interpolator.neighbor_mortar_mesh() == shell_mortar_mesh);\n    const DataVector interpolated_v_cubed_sphere =\n        interpolator.interpolate_to_host(v_shell);\n    CHECK_ITERABLE_APPROX(interpolated_v_cubed_sphere, v_cubed_sphere);\n    insert_mortar_data(interpolated_v_shell, shell_mortar_mesh, v_cubed_sphere,\n                       interpolator);\n    interpolator.reset_if_necessary(domain, cubed_sphere_mortar_mesh,\n                                    shell_mortar_mesh_2);\n    CHECK(interpolator.neighbor_mortar_mesh() == shell_mortar_mesh_2);\n    const DataVector interpolated_v_cubed_sphere_2 =\n        interpolator.interpolate_to_host(v_shell_2);\n    CHECK_ITERABLE_APPROX(interpolated_v_cubed_sphere_2, v_cubed_sphere);\n    insert_mortar_data(interpolated_v_shell_2, shell_mortar_mesh_2,\n                       v_cubed_sphere, interpolator);\n    interpolator.reset_if_necessary(domain, cubed_sphere_mortar_mesh_2,\n                                    shell_mortar_mesh_2);\n    CHECK(interpolator.neighbor_mortar_mesh() == shell_mortar_mesh_2);\n    const DataVector interpolated_v_cubed_sphere_3 =\n        interpolator.interpolate_to_host(v_shell_2);\n    const DataVector v_cubed_sphere_2 = vars_cubed_sphere(\n        domain, neighbor_id, refinement_levels, cubed_sphere_mortar_mesh_2);\n    CHECK_ITERABLE_APPROX(interpolated_v_cubed_sphere_3, v_cubed_sphere_2);\n    insert_mortar_data(interpolated_v_shell_3, shell_mortar_mesh_2,\n                       v_cubed_sphere_2, interpolator);\n    test_serialization(interpolator);\n    CHECK(interpolator.neighbor_mortar_mesh() == shell_mortar_mesh_2);\n  }\n  Approx custom_approx = Approx::custom().epsilon(1.0e-11).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(interpolated_v_shell, v_shell, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(interpolated_v_shell_2, v_shell_2,\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(interpolated_v_shell_3, v_shell_2,\n                               custom_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.DG.MortarInterpolator\", \"[Unit][Evolution]\") {\n  test_non_conforming_spheres();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_NormalDotFlux.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <numeric>\n#include <string>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/NormalDotFlux.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim, typename FluxTensor, typename ResultTensor>\nvoid check_normal_dot_flux(const tnsr::i<DataVector, Dim>& normal,\n                           const FluxTensor& flux_tensor,\n                           const ResultTensor& expected_result) {\n  ResultTensor result;\n  normal_dot_flux(make_not_null(&result), normal, flux_tensor);\n  for (auto it = result.begin(); it != result.end(); it++) {\n    CHECK_ITERABLE_APPROX(*it,\n                          expected_result.get(result.get_tensor_index(it)));\n  }\n}\n\ntemplate <size_t Dim, typename Fr, typename DataType, typename Symm,\n          typename... RemainingIndices>\nvoid test_with_random_values(\n    const DataVector& used_for_size,\n    Tensor<DataType, Symm,\n           index_list<SpatialIndex<Dim, UpLo::Up, Fr>, RemainingIndices...>>\n    /*meta*/) {\n  using NDotFluxTensor =\n      Tensor<DataType, tmpl::pop_front<Symm>, index_list<RemainingIndices...>>;\n  using FluxTensor =\n      Tensor<DataType, Symm,\n             index_list<SpatialIndex<Dim, UpLo::Up, Fr>, RemainingIndices...>>;\n  pypp::check_with_random_values<1>(\n      // This static_cast helps GCC figure out the type of the function\n      static_cast<void (*)(const gsl::not_null<NDotFluxTensor*>,\n                           const tnsr::i<DataVector, Dim, Fr>&,\n                           const FluxTensor&)>(\n          &normal_dot_flux<Dim, Fr, DataType, Symm, RemainingIndices...>),\n      \"NormalDotFlux\", {\"normal_dot_flux\"}, {{{-1.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(\n          const gsl::not_null<TensorMetafunctions::prepend_spatial_index<\n              NDotFluxTensor, Dim, UpLo::Lo, Fr>*>,\n          const tnsr::i<DataVector, Dim, Fr>&, const NDotFluxTensor&)>(\n          &normal_times_flux<Dim, Fr, DataType, tmpl::pop_front<Symm>,\n                             index_list<RemainingIndices...>>),\n      \"NormalDotFlux\", {\"normal_times_flux\"}, {{{-1.0, 1.0}}}, used_for_size);\n}\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim, typename Frame>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim, Frame>;\n};\n\ntemplate <size_t Dim, typename Frame>\nusing variables_tag = Tags::Variables<tmpl::list<Var1, Var2<Dim, Frame>>>;\n\ntemplate <size_t Dim, typename Frame>\nusing flux1 = Tags::Flux<Var1, tmpl::size_t<Dim>, Frame>;\ntemplate <size_t Dim, typename Frame>\nusing flux2 = Tags::Flux<Var2<Dim, Frame>, tmpl::size_t<Dim>, Frame>;\n\ntemplate <size_t Dim, typename Frame>\nusing flux_tag = db::add_tag_prefix<Tags::Flux, variables_tag<Dim, Frame>,\n                                    tmpl::size_t<Dim>, Frame>;\n\n// Copy the values in the components of Tensor<double, ...> `in` into\n// the `data_index` entry of DataVectors in the Tensor<DataVector,\n// ...> `out`, mapping the component (in0, in1, ...) to (in0, in1,\n// ..., `extra_indices`).\ntemplate <typename OutTensor, typename InTensor>\nvoid copy_into(const gsl::not_null<OutTensor*> out, const InTensor& in,\n               const std::array<size_t, OutTensor::rank() - InTensor::rank()>&\n                   extra_indices,\n               const size_t data_index) {\n  for (auto it = in.begin(); it != in.end(); ++it) {\n    const auto in_index = in.get_tensor_index(it);\n    std::array<size_t, OutTensor::rank()> out_index{};\n    for (size_t i = 0; i < InTensor::rank(); ++i) {\n      gsl::at(out_index, i) = gsl::at(in_index, i);\n    }\n    for (size_t i = 0; i < OutTensor::rank() - InTensor::rank(); ++i) {\n      gsl::at(out_index, i + InTensor::rank()) = gsl::at(extra_indices, i);\n    }\n    out->get(out_index)[data_index] = in.get(in_index);\n  }\n}\n\ntemplate <size_t Dim, typename Frame>\ntnsr::i<double, Dim, Frame> generate_normal(const size_t seed) {\n  tnsr::i<double, Dim, Frame> result{};\n  std::iota(result.begin(), result.end(), seed + 2.);\n  return result;\n}\n\ntemplate <size_t Dim, typename Frame>\ntnsr::I<double, Dim, Frame> generate_flux(const size_t seed) {\n  tnsr::I<double, Dim, Frame> result{};\n  std::iota(result.begin(), result.end(), seed + 3.);\n  return result;\n}\n\ntemplate <size_t Dim>\nScalar<double> generate_f_dot_n(const size_t normal_seed,\n                                const size_t flux_seed) {\n  double magnitude_normal = 0.;\n  double unnormalized_f_dot_n = 0.;\n  for (size_t i = 0; i < Dim; ++i) {\n    magnitude_normal += square(static_cast<double>(normal_seed + i) + 2.);\n    unnormalized_f_dot_n += (static_cast<double>(normal_seed + i) + 2.) *\n                            (static_cast<double>(flux_seed + i) + 3.);\n  }\n  magnitude_normal = sqrt(magnitude_normal);\n\n  return Scalar<double>(unnormalized_f_dot_n / magnitude_normal);\n}\n\ntemplate <size_t Dim>\nvoid test_with_variables() {\n  using Fr = Frame::Inertial;\n  constexpr size_t num_points = 5;\n  tnsr::i<DataVector, Dim, Fr> normal(num_points);\n  typename flux_tag<Dim, Fr>::type fluxes(num_points);\n  Var1::type expected1(num_points);\n  typename Var2<Dim, Fr>::type expected2(num_points);\n  for (size_t i = 0; i < num_points; ++i) {\n    copy_into(make_not_null(&normal), generate_normal<Dim, Fr>(i), {}, i);\n    copy_into(make_not_null(&get<flux1<Dim, Fr>>(fluxes)),\n              generate_flux<Dim, Fr>(i), {}, i);\n    copy_into(make_not_null(&expected1), generate_f_dot_n<Dim>(i, i), {}, i);\n    for (size_t j = 0; j < Dim; ++j) {\n      copy_into(make_not_null(&get<flux2<Dim, Fr>>(fluxes)),\n                generate_flux<Dim, Fr>(i + 10 * j), {{j}}, i);\n      copy_into(make_not_null(&expected2), generate_f_dot_n<Dim>(i, i + 10 * j),\n                {{j}}, i);\n    }\n  }\n\n  const auto magnitude_normal = magnitude(normal);\n  for (size_t d = 0; d < Dim; d++) {\n    normal.get(d) /= get(magnitude_normal);\n  }\n\n  const auto result =\n      normal_dot_flux<typename variables_tag<Dim, Fr>::tags_list>(normal,\n                                                                  fluxes);\n\n  CHECK_ITERABLE_APPROX(get<Tags::NormalDotFlux<Var1>>(result), expected1);\n  CHECK_ITERABLE_APPROX((get<Tags::NormalDotFlux<Var2<Dim, Fr>>>(result)),\n                        expected2);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DiscontinuousGalerkin.NormalDotFlux\",\n                  \"[Unit][Evolution]\") {\n  {\n    INFO(\"Explicit values\");\n    const size_t npts = 5;\n    const DataVector zero(npts, 0.0);\n    const DataVector one(npts, 1.0);\n    const DataVector two(npts, 2.0);\n    const DataVector three(npts, 3.0);\n    const DataVector four(npts, 4.0);\n    const DataVector five(npts, 5.0);\n    const DataVector six(npts, 6.0);\n    const DataVector seven(npts, 7.0);\n    const DataVector eight(npts, 8.0);\n    const DataVector nine(npts, 9.0);\n    const DataVector ten(npts, 10.0);\n    const DataVector eleven(npts, 11.0);\n    const DataVector twelve(npts, 12.0);\n    const DataVector fifteen(npts, 15.0);\n    const DataVector eighteen(npts, 18.0);\n\n    check_normal_dot_flux(tnsr::i<DataVector, 1>{{{one}}},\n                          tnsr::I<DataVector, 1>{{{two}}},\n                          Scalar<DataVector>{two});\n    check_normal_dot_flux(tnsr::i<DataVector, 2>{{{one, two}}},\n                          tnsr::I<DataVector, 2>{{{three, four}}},\n                          Scalar<DataVector>{eleven});\n    check_normal_dot_flux(tnsr::i<DataVector, 3>{{{one, two, three}}},\n                          tnsr::I<DataVector, 3>{{{-four, -two, three}}},\n                          Scalar<DataVector>{one});\n    check_normal_dot_flux(tnsr::i<DataVector, 2>{{{one, two}}},\n                          [&one, &two, &three, &four]() {\n                            tnsr::Ij<DataVector, 2> flux;\n                            get<0, 0>(flux) = one;\n                            get<0, 1>(flux) = two;\n                            get<1, 0>(flux) = three;\n                            get<1, 1>(flux) = four;\n                            return flux;\n                          }(),\n                          tnsr::i<DataVector, 2>{{{seven, ten}}});\n    check_normal_dot_flux(tnsr::i<DataVector, 2>{{{one, two}}},\n                          [&one, &two, &three]() {\n                            tnsr::II<DataVector, 2> flux;\n                            get<0, 0>(flux) = one;\n                            get<0, 1>(flux) = two;\n                            get<1, 1>(flux) = three;\n                            return flux;\n                          }(),\n                          tnsr::I<DataVector, 2>{{{five, eight}}});\n    check_normal_dot_flux(tnsr::i<DataVector, 3>{{{one, two, three}}},\n                          [&one, &two, &three, &four, &five, &six]() {\n                            tnsr::II<DataVector, 3> flux;\n                            get<0, 0>(flux) = one;\n                            get<0, 1>(flux) = two;\n                            get<0, 2>(flux) = -three;\n                            get<1, 1>(flux) = -four;\n                            get<1, 2>(flux) = five;\n                            get<2, 2>(flux) = -six;\n                            return flux;\n                          }(),\n                          tnsr::I<DataVector, 3>{{{-four, nine, -eleven}}});\n    check_normal_dot_flux(tnsr::i<DataVector, 2>{{{one, two}}},\n                          [&one, &two, &three, &four, &five, &six]() {\n                            tnsr::Iaa<DataVector, 2> flux;\n                            get<0, 0, 0>(flux) = one;\n                            get<0, 0, 1>(flux) = two;\n                            get<0, 0, 2>(flux) = -three;\n                            get<0, 1, 1>(flux) = -four;\n                            get<0, 1, 2>(flux) = five;\n                            get<0, 2, 2>(flux) = -six;\n                            get<1, 0, 0>(flux) = two;\n                            get<1, 0, 1>(flux) = one;\n                            get<1, 0, 2>(flux) = -three;\n                            get<1, 1, 1>(flux) = two;\n                            get<1, 1, 2>(flux) = three;\n                            get<1, 2, 2>(flux) = three;\n                            return flux;\n                          }(),\n                          [&five, &four, &nine, &zero, &eleven]() {\n                            tnsr::aa<DataVector, 2> result;\n                            get<0, 0>(result) = five;\n                            get<0, 1>(result) = four;\n                            get<0, 2>(result) = -nine;\n                            get<1, 1>(result) = zero;\n                            get<1, 2>(result) = eleven;\n                            get<2, 2>(result) = zero;\n                            return result;\n                          }());\n    check_normal_dot_flux(\n        tnsr::i<DataVector, 2>{{{one, two}}},\n        [&one, &two, &three, &four, &five, &six]() {\n          tnsr::Ijaa<DataVector, 2> flux;\n          get<0, 0, 0, 0>(flux) = one;\n          get<0, 0, 0, 1>(flux) = two;\n          get<0, 0, 0, 2>(flux) = -three;\n          get<0, 0, 1, 1>(flux) = -four;\n          get<0, 0, 1, 2>(flux) = five;\n          get<0, 0, 2, 2>(flux) = -six;\n          get<0, 1, 0, 0>(flux) = two;\n          get<0, 1, 0, 1>(flux) = one;\n          get<0, 1, 0, 2>(flux) = -three;\n          get<0, 1, 1, 1>(flux) = two;\n          get<0, 1, 1, 2>(flux) = three;\n          get<0, 1, 2, 2>(flux) = three;\n          get<1, 0, 0, 0>(flux) = one;\n          get<1, 0, 0, 1>(flux) = two;\n          get<1, 0, 0, 2>(flux) = -three;\n          get<1, 0, 1, 1>(flux) = -four;\n          get<1, 0, 1, 2>(flux) = five;\n          get<1, 0, 2, 2>(flux) = -six;\n          get<1, 1, 0, 0>(flux) = two;\n          get<1, 1, 0, 1>(flux) = one;\n          get<1, 1, 0, 2>(flux) = -three;\n          get<1, 1, 1, 1>(flux) = two;\n          get<1, 1, 1, 2>(flux) = three;\n          get<1, 1, 2, 2>(flux) = three;\n          return flux;\n        }(),\n        [&three, &six, &nine, &twelve, &fifteen, &eighteen]() {\n          tnsr::iaa<DataVector, 2> result;\n          get<0, 0, 0>(result) = three;\n          get<0, 0, 1>(result) = six;\n          get<0, 0, 2>(result) = -nine;\n          get<0, 1, 1>(result) = -twelve;\n          get<0, 1, 2>(result) = fifteen;\n          get<0, 2, 2>(result) = -eighteen;\n          get<1, 0, 0>(result) = six;\n          get<1, 0, 1>(result) = three;\n          get<1, 0, 2>(result) = -nine;\n          get<1, 1, 1>(result) = six;\n          get<1, 1, 2>(result) = nine;\n          get<1, 2, 2>(result) = nine;\n          return result;\n        }());\n  }\n  {\n    INFO(\"Random values\");\n    pypp::SetupLocalPythonEnvironment local_python_env{\"Domain\"};\n\n    GENERATE_UNINITIALIZED_DATAVECTOR;\n    test_with_random_values(dv, tnsr::I<DataVector, 1>{});\n    test_with_random_values(dv, tnsr::I<DataVector, 2>{});\n    test_with_random_values(dv, tnsr::I<DataVector, 3>{});\n    test_with_random_values(dv, tnsr::I<ComplexDataVector, 3>{});\n    test_with_random_values(dv, tnsr::II<DataVector, 1>{});\n    test_with_random_values(dv, tnsr::II<DataVector, 2>{});\n    test_with_random_values(dv, tnsr::II<DataVector, 3>{});\n    test_with_random_values(dv, tnsr::Ij<DataVector, 1>{});\n    test_with_random_values(dv, tnsr::Ij<DataVector, 2>{});\n    test_with_random_values(dv, tnsr::Ij<DataVector, 3>{});\n    test_with_random_values(dv, tnsr::Ij<ComplexDataVector, 3>{});\n    test_with_random_values(dv, tnsr::Ijk<DataVector, 1>{});\n    test_with_random_values(dv, tnsr::Ijk<DataVector, 2>{});\n    test_with_random_values(dv, tnsr::Ijk<DataVector, 3>{});\n    test_with_random_values(dv, tnsr::III<DataVector, 1>{});\n    test_with_random_values(dv, tnsr::III<DataVector, 2>{});\n    test_with_random_values(dv, tnsr::III<DataVector, 3>{});\n    test_with_random_values(dv, tnsr::Iaa<DataVector, 1>{});\n    test_with_random_values(dv, tnsr::Iaa<DataVector, 2>{});\n    test_with_random_values(dv, tnsr::Iaa<DataVector, 3>{});\n    test_with_random_values(dv, tnsr::Ijaa<DataVector, 1>{});\n    test_with_random_values(dv, tnsr::Ijaa<DataVector, 2>{});\n    test_with_random_values(dv, tnsr::Ijaa<DataVector, 3>{});\n  }\n  {\n    INFO(\"Variables\");\n    GENERATE_UNINITIALIZED_DATAVECTOR;\n    test_with_variables<1>();\n    test_with_variables<2>();\n    test_with_variables<3>();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_ProjectToBoundary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/SliceVariables.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ProjectToBoundary.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 2, Frame::Inertial>;\n};\n\nstruct Var3 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nVariables<tmpl::list<Var2, Var3>> polynomial_volume_data(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& coords,\n    const Index<Dim>& powers) {\n  Variables<tmpl::list<Var2, Var3>> result(get<0>(coords).size(), 1.0);\n  for (size_t i = 0; i < Dim; ++i) {\n    get(get<Var3>(result)) *= pow(coords.get(i), powers[i]);\n    get<0>(get<Var2>(result)) *= 2.0 * pow(coords.get(i), powers[i]);\n    get<1>(get<Var2>(result)) *= 3.0 * pow(coords.get(i), powers[i]);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test(const Spectral::Quadrature quadrature) {\n  CAPTURE(Dim);\n  CAPTURE(quadrature);\n\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{5, 10};\n\n  const bool using_zernike =\n      quadrature == Spectral::Quadrature::GaussRadauUpper;\n\n  const Mesh<Dim> volume_mesh{\n      sdist(gen),\n      using_zernike ? Spectral::Basis::ZernikeB2 : Spectral::Basis::Legendre,\n      quadrature};\n  Index<Dim> powers{};\n  for (size_t i = 0; i < Dim; ++i) {\n    powers[i] = volume_mesh.extents(i) - 2 - i;\n  }\n\n  const auto volume_data =\n      polynomial_volume_data(logical_coordinates(volume_mesh), powers);\n\n  // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n  const Scalar<DataVector> var3_volume = get<Var3>(volume_data);\n  // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n  const tnsr::I<DataVector, 2, Frame::Inertial> var2_volume =\n      get<Var2>(volume_data);\n\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    if (using_zernike and direction.side() != Side::Upper) {\n      continue;\n    }\n    const size_t sliced_dim = direction.dimension();\n    const size_t fixed_index = direction.side() == Side::Upper\n                                   ? volume_mesh.extents(sliced_dim) - 1\n                                   : 0;\n    const auto face_mesh = volume_mesh.slice_away(sliced_dim);\n    Variables<tmpl::list<Var2, Var3>> expected_face_values{};\n    if (quadrature == Spectral::Quadrature::GaussLobatto) {\n      expected_face_values = data_on_slice(volume_data, volume_mesh.extents(),\n                                           sliced_dim, fixed_index);\n    } else {\n      expected_face_values = polynomial_volume_data(\n          interface_logical_coordinates(face_mesh, direction), powers);\n    }\n    const Scalar<DataVector> expected_var1{face_mesh.number_of_grid_points(),\n                                           0.0};\n    const Scalar<DataVector> expected_var3{face_mesh.number_of_grid_points(),\n                                           0.0};\n\n    Variables<tmpl::list<Var1, Var2, Var3>> face_values{\n        face_mesh.number_of_grid_points(), 0.0};\n    ::dg::project_tensors_to_boundary<tmpl::list<Var2>>(\n        make_not_null(&face_values), volume_data, volume_mesh, direction);\n    CHECK_ITERABLE_APPROX(get<Var1>(face_values), expected_var1);\n    CHECK_ITERABLE_APPROX(get<Var2>(face_values),\n                          get<Var2>(expected_face_values));\n    CHECK_ITERABLE_APPROX(get<Var3>(face_values), expected_var3);\n\n    ::dg::project_tensors_to_boundary<tmpl::list<Var3>>(\n        make_not_null(&face_values), volume_data, volume_mesh, direction);\n    CHECK_ITERABLE_APPROX(get<Var1>(face_values), expected_var1);\n    CHECK_ITERABLE_APPROX(get<Var2>(face_values),\n                          get<Var2>(expected_face_values));\n    CHECK_ITERABLE_APPROX(get<Var3>(face_values),\n                          get<Var3>(expected_face_values));\n\n    face_values.initialize(face_mesh.number_of_grid_points(), 0.0);\n    ::dg::project_tensors_to_boundary<tmpl::list<Var2, Var3>>(\n        make_not_null(&face_values), volume_data, volume_mesh, direction);\n    CHECK_ITERABLE_APPROX(get<Var1>(face_values), expected_var1);\n    CHECK_ITERABLE_APPROX(get<Var2>(face_values),\n                          get<Var2>(expected_face_values));\n    CHECK_ITERABLE_APPROX(get<Var3>(face_values),\n                          get<Var3>(expected_face_values));\n\n    Variables<tmpl::list<Var1, Var2, Var3>> face_values_contiguous_project{\n        face_mesh.number_of_grid_points(), 0.0};\n    ::dg::project_contiguous_data_to_boundary(\n        make_not_null(&face_values_contiguous_project), volume_data,\n        volume_mesh, direction);\n    CHECK_ITERABLE_APPROX(get<Var1>(face_values_contiguous_project),\n                          expected_var1);\n    CHECK_ITERABLE_APPROX(get<Var2>(face_values_contiguous_project),\n                          get<Var2>(expected_face_values));\n    CHECK_ITERABLE_APPROX(get<Var3>(face_values_contiguous_project),\n                          get<Var3>(expected_face_values));\n\n    Scalar<DataVector> var3_face{face_mesh.number_of_grid_points()};\n    ::dg::project_tensor_to_boundary(make_not_null(&var3_face), var3_volume,\n                                     volume_mesh, direction);\n    CHECK_ITERABLE_APPROX(var3_face, get<Var3>(expected_face_values));\n\n    tnsr::I<DataVector, 2, Frame::Inertial> var2_face{\n        face_mesh.number_of_grid_points()};\n    ::dg::project_tensor_to_boundary(make_not_null(&var2_face), var2_volume,\n                                     volume_mesh, direction);\n    CHECK_ITERABLE_APPROX(var2_face, get<Var2>(expected_face_values));\n  }\n}\n\nvoid test_asserts() {\n#ifdef SPECTRE_DEBUG\n  const Mesh<1> mesh(5, Spectral::Basis::ZernikeB2,\n                     Spectral::Quadrature::GaussRadauUpper);\n  Variables<tmpl::list<Var1>> face{mesh.number_of_grid_points(), 0.0};\n  const Variables<tmpl::list<Var1>> volume{mesh.number_of_grid_points(), 0.0};\n\n  CHECK_THROWS_WITH(\n      ::dg::project_tensors_to_boundary<tmpl::list<Var1>>(\n          make_not_null(&face), volume, mesh, Direction<1>::lower_xi()),\n      Catch::Matchers::ContainsSubstring(\n          \"Got quadrature without boundary collocation point at\"));\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DiscontinuousGalerkin.ProjectToBoundary\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  for (const auto quadrature :\n       {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}) {\n    test<1>(quadrature);\n    test<2>(quadrature);\n    test<3>(quadrature);\n  }\n  test<1>(Spectral::Quadrature::GaussRadauUpper);\n  test_asserts();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_SimpleBoundaryData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/OrientationMapHelpers.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MortarHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/SimpleBoundaryData.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nstruct SomeField : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct ExtraData {\n  using type = int;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DG.SimpleBoundaryData\", \"[Unit][NumericalAlgorithms]\") {\n  const size_t num_points = 5;\n  dg::SimpleBoundaryData<tmpl::list<SomeField>, tmpl::list<ExtraData>> data{\n      num_points};\n  const Scalar<DataVector> field{num_points, 1.};\n  get<SomeField>(data.field_data) = field;\n  get<ExtraData>(data.extra_data) = 2;\n\n  // Test serialization\n  data = serialize_and_deserialize(data);\n  CHECK(get<SomeField>(data.field_data) == field);\n  CHECK(get<ExtraData>(data.extra_data) == 2);\n\n  // Test projections\n  const Mesh<1> face_mesh{num_points, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> mortar_mesh{num_points + 1, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n  const std::array<Spectral::SegmentSize, 1> mortar_size{\n      {Spectral::SegmentSize::UpperHalf}};\n  const auto projected_data =\n      data.project_to_mortar(face_mesh, mortar_mesh, mortar_size);\n  CHECK(projected_data.field_data ==\n        dg::project_to_mortar(data.field_data, face_mesh, mortar_mesh,\n                              mortar_size));\n  CHECK(projected_data.extra_data == data.extra_data);\n\n  // Test orientation\n  const size_t sliced_dim = 1;\n  const auto slice_extents = face_mesh.extents();\n  const OrientationMap<2> orientation_of_neighbor{\n      {{Direction<2>::lower_xi(), Direction<2>::lower_eta()}},\n      {{Direction<2>::upper_eta(), Direction<2>::lower_xi()}}};\n  auto oriented_data = data;\n  oriented_data.orient_on_slice(slice_extents, sliced_dim,\n                                orientation_of_neighbor);\n  CHECK(oriented_data.field_data ==\n        orient_variables_on_slice(data.field_data, slice_extents, sliced_dim,\n                                  orientation_of_neighbor));\n  CHECK(oriented_data.extra_data == data.extra_data);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_SimpleMortarData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <utility>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/SimpleMortarData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\n// for __decay_and_strip<>::__type\n\nSPECTRE_TEST_CASE(\"Unit.DG.SimpleMortarData\", \"[Unit][NumericalAlgorithms]\") {\n  {\n    dg::SimpleMortarData<size_t, std::string, double> data;\n    data = serialize_and_deserialize(data);\n    data.local_insert(0, \"string 1\");\n    data = serialize_and_deserialize(data);\n    data.remote_insert(0, 1.234);\n    CHECK(data.local_data(0) == \"string 1\");\n    CHECK(data.remote_data(0) == 1.234);\n    CHECK(data.extract() == std::make_pair(\"string 1\"s, 1.234));\n    data = serialize_and_deserialize(data);\n    data.remote_insert(1, 2.345);\n    data = serialize_and_deserialize(data);\n    data.local_insert(1, \"string 2\");\n    CHECK(data.extract() == std::make_pair(\"string 2\"s, 2.345));\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        dg::SimpleMortarData<size_t, std::string, double> data;\n        data.remote_insert(0, 1.234);\n        data.local_data(0);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Local data not available.\"));\n  CHECK_THROWS_WITH(([]() {\n                      dg::SimpleMortarData<size_t, std::string, double> data;\n                      data.local_insert(1, \"\");\n                      data.local_data(0);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Only have local data at temporal_id\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        dg::SimpleMortarData<size_t, std::string, double> data;\n        data.remote_insert(0, 0.);\n        data.local_insert(1, \"\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Received local data at 1, but already have remote data at 0\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        dg::SimpleMortarData<size_t, std::string, double> data;\n        data.local_insert(1, \"\");\n        data.remote_insert(0, 0.);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Received remote data at 0, but already have local data at 1\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        dg::SimpleMortarData<size_t, std::string, double> data;\n        data.local_insert(1, \"\");\n        data.local_insert(1, \"\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Already received local data\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        dg::SimpleMortarData<size_t, std::string, double> data;\n        data.remote_insert(0, 0.);\n        data.remote_insert(0, 0.);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Already received remote data\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        dg::SimpleMortarData<size_t, std::string, double> data;\n        data.extract();\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to extract boundary data, but do not have any data\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        dg::SimpleMortarData<size_t, std::string, double> data;\n        data.local_insert(1, \"\");\n        data.extract();\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to extract boundary data, but do not have remote data\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        dg::SimpleMortarData<size_t, std::string, double> data;\n        data.remote_insert(0, 0.);\n        data.extract();\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to extract boundary data, but do not have local data\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/DiscontinuousGalerkin/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n\nnamespace {\nstruct SomeType {};\n\nstruct SomeTag : db::SimpleTag {\n  using type = int;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DG.Tags\", \"[Unit][NumericalAlgorithms]\") {\n  TestHelpers::db::test_prefix_tag<Tags::Mortars<SomeTag, 3>>(\n      \"Mortars(SomeTag)\");\n  TestHelpers::db::test_simple_tag<Tags::MortarSize<2>>(\"MortarSize\");\n  TestHelpers::db::test_simple_tag<Tags::NumericalFlux<SomeType>>(\n      \"NumericalFlux\");\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/AoWeno53.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nimport Reconstruction\n\n\ndef test_aoweno53(u, extents, dim):\n    gamma_hi = 0.85\n    gamma_lo = 0.999\n    epsilon = 1.0e-12\n    exponent = 8\n\n    def aoweno53(q):\n        j = 2\n        moments_sr3_1 = [\n            1.041666666666666 * q[j]\n            - 0.08333333333333333 * q[j - 1]\n            + 0.04166666666666666 * q[j - 2],\n            0.5 * q[j - 2] - 2.0 * q[j - 1] + 1.5 * q[j],\n            0.5 * q[j - 2] - q[j - 1] + 0.5 * q[j],\n        ]\n        moments_sr3_2 = [\n            0.04166666666666666 * q[j + 1]\n            + 0.9166666666666666 * q[j]\n            + 0.04166666666666666 * q[j - 1],\n            0.5 * (q[j + 1] - q[j - 1]),\n            0.5 * q[j - 1] - q[j] + 0.5 * q[j + 1],\n        ]\n        moments_sr3_3 = [\n            0.04166666666666666 * q[j + 2]\n            - 0.08333333333333333 * q[j + 1]\n            + 1.04166666666666666 * q[j],\n            -1.5 * q[j] + 2.0 * q[j + 1] - 0.5 * q[j + 2],\n            0.5 * q[j] - q[j + 1] + 0.5 * q[j + 2],\n        ]\n        moments_sr5 = [\n            -2.95138888888888881e-03 * q[j - 2]\n            + 5.34722222222222196e-02 * q[j - 1]\n            + 8.98958333333333304e-01 * q[j]\n            + 5.34722222222222196e-02 * q[j + 1]\n            + -2.95138888888888881e-03 * q[j + 2],\n            7.08333333333333315e-02 * q[j - 2]\n            + -6.41666666666666718e-01 * q[j - 1]\n            + 6.41666666666666718e-01 * q[j + 1]\n            + -7.08333333333333315e-02 * q[j + 2],\n            -3.27380952380952397e-02 * q[j - 2]\n            + 6.30952380952380931e-01 * q[j - 1]\n            + -1.19642857142857140e00 * q[j]\n            + 6.30952380952380931e-01 * q[j + 1]\n            + -3.27380952380952397e-02 * q[j + 2],\n            -8.33333333333333287e-02 * q[j - 2]\n            + 1.66666666666666657e-01 * q[j - 1]\n            + -1.66666666666666657e-01 * q[j + 1]\n            + 8.33333333333333287e-02 * q[j + 2],\n            4.16666666666666644e-02 * q[j - 2]\n            + -1.66666666666666657e-01 * q[j - 1]\n            + 2.50000000000000000e-01 * q[j]\n            + -1.66666666666666657e-01 * q[j + 1]\n            + 4.16666666666666644e-02 * q[j + 2],\n        ]\n\n        beta_r3_1 = moments_sr3_1[1] ** 2 + 37.0 / 3.0 * moments_sr3_1[2] ** 2\n        beta_r3_2 = moments_sr3_2[1] ** 2 + 37.0 / 3.0 * moments_sr3_2[2] ** 2\n        beta_r3_3 = moments_sr3_3[1] ** 2 + 37.0 / 3.0 * moments_sr3_3[2] ** 2\n        beta_sr5 = (\n            moments_sr5[1] ** 2\n            + 61.0 / 5.0 * moments_sr5[1] * moments_sr5[3]\n            + 37.0 / 3.0 * moments_sr5[2] ** 2\n            + 1538.0 / 7.0 * moments_sr5[2] * moments_sr5[4]\n            + 8973.0 / 50.0 * moments_sr5[3] ** 2\n            + 167158.0 / 49.0 * moments_sr5[4] ** 2\n        )\n\n        linear_weights = [\n            gamma_hi,\n            0.5 * (1.0 - gamma_hi) * (1.0 - gamma_lo),\n            (1.0 - gamma_hi) * gamma_lo,\n            0.5 * (1.0 - gamma_hi) * (1.0 - gamma_lo),\n        ]\n        nonlinear_weights = np.asarray(\n            [\n                linear_weights[0] / (beta_sr5 + epsilon) ** exponent,\n                linear_weights[1] / (beta_r3_1 + epsilon) ** exponent,\n                linear_weights[2] / (beta_r3_2 + epsilon) ** exponent,\n                linear_weights[3] / (beta_r3_3 + epsilon) ** exponent,\n            ]\n        )\n        normalization = np.sum(nonlinear_weights)\n        nonlinear_weights = nonlinear_weights / normalization\n\n        moments = np.asarray(\n            [\n                nonlinear_weights[0]\n                / linear_weights[0]\n                * (\n                    moments_sr5[0]\n                    - linear_weights[1] * moments_sr3_1[0]\n                    - linear_weights[2] * moments_sr3_2[0]\n                    - linear_weights[3] * moments_sr3_3[0]\n                )\n                + nonlinear_weights[1] * moments_sr3_1[0]\n                + nonlinear_weights[2] * moments_sr3_2[0]\n                + nonlinear_weights[3] * moments_sr3_3[0],\n                nonlinear_weights[0]\n                / linear_weights[0]\n                * (\n                    moments_sr5[1]\n                    - linear_weights[1] * moments_sr3_1[1]\n                    - linear_weights[2] * moments_sr3_2[1]\n                    - linear_weights[3] * moments_sr3_3[1]\n                )\n                + nonlinear_weights[1] * moments_sr3_1[1]\n                + nonlinear_weights[2] * moments_sr3_2[1]\n                + nonlinear_weights[3] * moments_sr3_3[1],\n                nonlinear_weights[0]\n                / linear_weights[0]\n                * (\n                    moments_sr5[2]\n                    - linear_weights[1] * moments_sr3_1[2]\n                    - linear_weights[2] * moments_sr3_2[2]\n                    - linear_weights[3] * moments_sr3_3[2]\n                )\n                + nonlinear_weights[1] * moments_sr3_1[2]\n                + nonlinear_weights[2] * moments_sr3_2[2]\n                + nonlinear_weights[3] * moments_sr3_3[2],\n                nonlinear_weights[0] / linear_weights[0] * moments_sr5[3],\n                nonlinear_weights[0] / linear_weights[0] * moments_sr5[4],\n            ]\n        )\n\n        polys_at_plus_half = np.asarray(\n            [1.0, 0.5, 0.16666666666666666, 0.05, 0.014285714285714289]\n        )\n        polys_at_minus_half = np.asarray(\n            [1.0, -0.5, 0.16666666666666666, -0.05, 0.014285714285714289]\n        )\n        return [\n            np.sum(moments * polys_at_minus_half),\n            np.sum(moments * polys_at_plus_half),\n        ]\n\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        if dim_to_recons == 0:\n            lower, upper = aoweno53(\n                np.asarray(\n                    [\n                        v[i - 2, j, k],\n                        v[i - 1, j, k],\n                        v[i, j, k],\n                        v[i + 1, j, k],\n                        v[i + 2, j, k],\n                    ]\n                )\n            )\n            recons_lower_of_cell.append(lower)\n            recons_upper_of_cell.append(upper)\n        if dim_to_recons == 1:\n            lower, upper = aoweno53(\n                np.asarray(\n                    [\n                        v[i, j - 2, k],\n                        v[i, j - 1, k],\n                        v[i, j, k],\n                        v[i, j + 1, k],\n                        v[i, j + 2, k],\n                    ]\n                )\n            )\n            recons_lower_of_cell.append(lower)\n            recons_upper_of_cell.append(upper)\n        if dim_to_recons == 2:\n            lower, upper = aoweno53(\n                np.asarray(\n                    [\n                        v[i, j, k - 2],\n                        v[i, j, k - 1],\n                        v[i, j, k],\n                        v[i, j, k + 1],\n                        v[i, j, k + 2],\n                    ]\n                )\n            )\n            recons_lower_of_cell.append(lower)\n            recons_upper_of_cell.append(upper)\n\n    return Reconstruction.reconstruct(\n        u, extents, dim, [2, 2, 2], compute_face_values\n    )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_FiniteDifference\")\n\nset(LIBRARY_SOURCES\n  Test_AoWeno53.cpp\n  Test_DerivativeOrder.cpp\n  Test_FallbackReconstructorType.cpp\n  Test_Filter.cpp\n  Test_HighOrderFluxCorrection.cpp\n  Test_Minmod.cpp\n  Test_MonotonicityPreserving5.cpp\n  Test_MonotonisedCentral.cpp\n  Test_NeighborDataAsVariables.cpp\n  Test_NonUniform1D.cpp\n  Test_PartialDerivatives.cpp\n  Test_PositivityPreservingAdaptiveOrder.cpp\n  Test_SecondPartialDerivatives.cpp\n  Test_Unlimited.cpp\n  Test_Wcns5z.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  DgSubcell\n  Domain\n  FiniteDifference\n  ErrorHandling\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Minmod.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Reconstruction\n\n\ndef minmod(a, b):\n    if a * b <= 0.0:\n        return 0.0\n    if abs(a) < abs(b):\n        return a\n    return b\n\n\ndef test_minmod(u, extents, dim):\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        if dim_to_recons == 0:\n            slope = minmod(\n                v[i, j, k] - v[i - 1, j, k], v[i + 1, j, k] - v[i, j, k]\n            )\n            recons_lower_of_cell.append(v[i, j, k] - 0.5 * slope)\n            recons_upper_of_cell.append(v[i, j, k] + 0.5 * slope)\n        if dim_to_recons == 1:\n            slope = minmod(\n                v[i, j, k] - v[i, j - 1, k], v[i, j + 1, k] - v[i, j, k]\n            )\n            recons_lower_of_cell.append(v[i, j, k] - 0.5 * slope)\n            recons_upper_of_cell.append(v[i, j, k] + 0.5 * slope)\n        if dim_to_recons == 2:\n            slope = minmod(\n                v[i, j, k] - v[i, j, k - 1], v[i, j, k + 1] - v[i, j, k]\n            )\n            recons_lower_of_cell.append(v[i, j, k] - 0.5 * slope)\n            recons_upper_of_cell.append(v[i, j, k] + 0.5 * slope)\n\n    return Reconstruction.reconstruct(\n        u, extents, dim, [1, 1, 1], compute_face_values\n    )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/MonotonicityPreserving5.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Minmod  # for minmod() function with 2 args\nimport numpy as np\nimport Reconstruction\nfrom numpy import asarray, sign\n\n\ndef mp5(v):\n    # define the minmod() function with 4 args\n    def minmod_4(a, b, c, d):\n        return (\n            0.125\n            * (sign(a) + sign(b))\n            * np.abs((sign(a) + sign(c)) * (sign(a) + sign(d)))\n            * np.min(np.abs([a, b, c, d]))\n        )\n\n    def mp5_oneside(v, s):\n        alpha = 4.0\n        eps = 1e-10\n\n        j = 2\n\n        vor = (\n            3.0 * v[j - 2 * s]\n            - 20.0 * v[j - s]\n            + 90.0 * v[j]\n            + 60.0 * v[j + s]\n            - 5.0 * v[j + 2 * s]\n        ) / 128.0\n        vmp = v[j] + Minmod.minmod(v[j + s] - v[j], alpha * (v[j] - v[j - s]))\n\n        if (vor - v[j]) * (vor - vmp) > eps:\n            djm1 = v[j - 2 * s] - 2.0 * v[j - s] + v[j]\n            dj = v[j - s] - 2.0 * v[j] + v[j + s]\n            djp1 = v[j] - 2.0 * v[j + s] + v[j + 2 * s]\n            dm4jph = minmod_4(4.0 * dj - djp1, 4.0 * djp1 - dj, dj, djp1)\n            dm4jmh = minmod_4(4.0 * dj - djm1, 4.0 * djm1 - dj, dj, djm1)\n\n            vul = v[j] + alpha * (v[j] - v[j - s])\n            vav = 0.5 * (v[j] + v[j + s])\n            vmd = vav - 0.5 * dm4jph\n            vlc = v[j] + 0.5 * (v[j] - v[j - s]) + (4.0 / 3.0) * dm4jmh\n\n            vmin = max(np.min([v[j], v[j + s], vmd]), np.min([v[j], vul, vlc]))\n            vmax = min(np.max([v[j], v[j + s], vmd]), np.max([v[j], vul, vlc]))\n\n            return vor + Minmod.minmod(vmin - vor, vmax - vor)\n        else:\n            return vor\n\n    return [mp5_oneside(v, -1), mp5_oneside(v, 1)]\n\n\ndef test_mp5(u, extents, dim):\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        if dim_to_recons == 0:\n            lower, upper = mp5(\n                asarray(\n                    [\n                        v[i - 2, j, k],\n                        v[i - 1, j, k],\n                        v[i, j, k],\n                        v[i + 1, j, k],\n                        v[i + 2, j, k],\n                    ]\n                )\n            )\n            recons_lower_of_cell.append(lower)\n            recons_upper_of_cell.append(upper)\n        if dim_to_recons == 1:\n            lower, upper = mp5(\n                asarray(\n                    [\n                        v[i, j - 2, k],\n                        v[i, j - 1, k],\n                        v[i, j, k],\n                        v[i, j + 1, k],\n                        v[i, j + 2, k],\n                    ]\n                )\n            )\n            recons_lower_of_cell.append(lower)\n            recons_upper_of_cell.append(upper)\n        if dim_to_recons == 2:\n            lower, upper = mp5(\n                asarray(\n                    [\n                        v[i, j, k - 2],\n                        v[i, j, k - 1],\n                        v[i, j, k],\n                        v[i, j, k + 1],\n                        v[i, j, k + 2],\n                    ]\n                )\n            )\n            recons_lower_of_cell.append(lower)\n            recons_upper_of_cell.append(upper)\n\n    return Reconstruction.reconstruct(\n        u, extents, dim, [2, 2, 2], compute_face_values\n    )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/MonotonisedCentral.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Reconstruction\n\n\ndef monotonised_central(a, b):\n    sign = lambda x: -1 if x < 0 else 1\n    sign_a = sign(a)\n    sign_b = sign(b)\n    return (\n        0.5\n        * (sign_a + sign_b)\n        * min(0.5 * abs(a + b), min(2.0 * abs(a), 2.0 * abs(b)))\n    )\n\n\ndef test_monotonised_central(u, extents, dim):\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        if dim_to_recons == 0:\n            slope = monotonised_central(\n                v[i, j, k] - v[i - 1, j, k], v[i + 1, j, k] - v[i, j, k]\n            )\n            recons_lower_of_cell.append(v[i, j, k] - 0.5 * slope)\n            recons_upper_of_cell.append(v[i, j, k] + 0.5 * slope)\n        if dim_to_recons == 1:\n            slope = monotonised_central(\n                v[i, j, k] - v[i, j - 1, k], v[i, j + 1, k] - v[i, j, k]\n            )\n            recons_lower_of_cell.append(v[i, j, k] - 0.5 * slope)\n            recons_upper_of_cell.append(v[i, j, k] + 0.5 * slope)\n        if dim_to_recons == 2:\n            slope = monotonised_central(\n                v[i, j, k] - v[i, j, k - 1], v[i, j, k + 1] - v[i, j, k]\n            )\n            recons_lower_of_cell.append(v[i, j, k] - 0.5 * slope)\n            recons_upper_of_cell.append(v[i, j, k] + 0.5 * slope)\n\n    return Reconstruction.reconstruct(\n        u, extents, dim, [1, 1, 1], compute_face_values\n    )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/PositivityPreservingAdaptiveOrder.py",
    "content": "## Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Minmod\nimport MonotonisedCentral\nimport numpy as np\nimport Reconstruction\n\n\ndef _minmod(q):\n    j = 1\n    slope = Minmod.minmod(q[j] - q[j - 1], q[j + 1] - q[j])\n    return [q[j] - 0.5 * slope, q[j] + 0.5 * slope]\n\n\ndef _mc(q):\n    j = 1\n    slope = MonotonisedCentral.monotonised_central(\n        q[j] - q[j - 1], q[j + 1] - q[j]\n    )\n    return [q[j] - 0.5 * slope, q[j] + 0.5 * slope]\n\n\ndef _adaptive_order_5(\n    q,\n    keep_positive,\n    low_order_recons,\n    use_9th_order,\n    use_7th_order,\n    four_to_the_alpha_5,\n    six_to_the_alpha_7,\n    eight_to_the_alpha_9,\n):\n    j = 4 if use_9th_order else (3 if use_7th_order else 2)\n\n    if use_9th_order:\n        norm_top = (\n            -1.593380762005595 * q[j + 1]\n            + 0.7966903810027975 * q[j + 2]\n            - 0.22762582314365648 * q[j + 3]\n            + 0.02845322789295706 * q[j + 4]\n            - 1.593380762005595 * q[j - 1]\n            + 0.7966903810027975 * q[j - 2]\n            - 0.22762582314365648 * q[j - 3]\n            + 0.02845322789295706 * q[j - 4]\n            + 1.991725952506994 * q[j]\n        ) ** 2\n        norm_full = (\n            q[j + 1]\n            * (\n                25.393963433621668 * q[j + 1]\n                - 31.738453392103736 * q[j + 2]\n                + 14.315575523531798 * q[j + 3]\n                - 5.422933317103013 * q[j + 4]\n                + 45.309550145164756 * q[j - 1]\n                - 25.682667845756164 * q[j - 2]\n                + 10.394184200706238 * q[j - 3]\n                - 3.5773996341558414 * q[j - 4]\n                - 56.63693768145594 * q[j]\n            )\n            + q[j + 2]\n            * (\n                10.664627625179254 * q[j + 2]\n                - 9.781510753231265 * q[j + 3]\n                + 3.783820939683476 * q[j + 4]\n                - 25.682667845756164 * q[j - 1]\n                + 13.59830711617153 * q[j - 2]\n                - 5.064486634342602 * q[j - 3]\n                + 1.5850428636128617 * q[j - 4]\n                + 33.99576779042882 * q[j]\n            )\n            + q[j + 3]\n            * (\n                2.5801312593878514 * q[j + 3]\n                - 1.812843724346584 * q[j + 4]\n                + 10.394184200706238 * q[j - 1]\n                - 5.064486634342602 * q[j - 2]\n                + 1.6716163773782988 * q[j - 3]\n                - 0.4380794296257583 * q[j - 4]\n                - 14.626643302060115 * q[j]\n            )\n            + q[j + 4]\n            * (\n                0.5249097623867759 * q[j + 4]\n                - 3.5773996341558414 * q[j - 1]\n                + 1.5850428636128617 * q[j - 2]\n                - 0.4380794296257583 * q[j - 3]\n                + 0.07624062080823268 * q[j - 4]\n                + 5.336843456576288 * q[j]\n            )\n            + q[j - 1]\n            * (\n                25.393963433621668 * q[j - 1]\n                - 31.738453392103736 * q[j - 2]\n                + 14.315575523531798 * q[j - 3]\n                - 5.422933317103013 * q[j - 4]\n                - 56.63693768145594 * q[j]\n            )\n            + q[j - 2]\n            * (\n                10.664627625179254 * q[j - 2]\n                - 9.781510753231265 * q[j - 3]\n                + 3.783820939683476 * q[j - 4]\n                + 33.99576779042882 * q[j]\n            )\n            + q[j - 3]\n            * (\n                2.5801312593878514 * q[j - 3]\n                - 1.812843724346584 * q[j - 4]\n                - 14.626643302060115 * q[j]\n            )\n            + q[j - 4]\n            * (0.5249097623867759 * q[j - 4] + 5.336843456576288 * q[j])\n            + 33.758463458609164 * q[j] ** 2\n        )\n        if eight_to_the_alpha_9**2 * norm_top <= norm_full:\n            result = [\n                -0.179443359375 * q[j + 1]\n                + 0.0538330078125 * q[j + 2]\n                - 0.010986328125 * q[j + 3]\n                + 0.001068115234375 * q[j + 4]\n                + 0.538330078125 * q[j - 1]\n                - 0.0897216796875 * q[j - 2]\n                + 0.015380859375 * q[j - 3]\n                - 0.001373291015625 * q[j - 4]\n                + 0.67291259765625 * q[j],\n                0.538330078125 * q[j + 1]\n                - 0.0897216796875 * q[j + 2]\n                + 0.015380859375 * q[j + 3]\n                - 0.001373291015625 * q[j + 4]\n                - 0.179443359375 * q[j - 1]\n                + 0.0538330078125 * q[j - 2]\n                - 0.010986328125 * q[j - 3]\n                + 0.001068115234375 * q[j - 4]\n                + 0.67291259765625 * q[j],\n            ]\n            if (not keep_positive) or (result[0] > 0.0 and result[1] > 0.0):\n                return result\n\n    if use_7th_order:\n        norm_top = (\n            2\n            * (\n                16807 * q[j - 3] / 95040\n                - 16807 * q[j - 2] / 15840\n                + 16807 * q[j - 1] / 6336\n                - 16807 * q[j] / 4752\n                + 16807 * q[j + 1] / 6336\n                - 16807 * q[j + 2] / 15840\n                + 16807 * q[j + 3] / 95040\n            )\n            ** 2\n            / 13\n        )\n        norm_full = (\n            q[j + 1]\n            * (\n                3.93094886671763 * q[j + 1]\n                - 4.4887583031366605 * q[j + 2]\n                + 2.126671427664419 * q[j + 3]\n                + 6.081742742499426 * q[j - 1]\n                - 3.1180508323787337 * q[j - 2]\n                + 1.2660604719155235 * q[j - 3]\n                - 8.108990323332568 * q[j]\n            )\n            + q[j + 2]\n            * (\n                1.7504056695205172 * q[j + 2]\n                - 1.402086588589091 * q[j + 3]\n                - 3.1180508323787337 * q[j - 1]\n                + 1.384291080027286 * q[j - 2]\n                - 0.46498946172145633 * q[j - 3]\n                + 4.614303600090953 * q[j]\n            )\n            + q[j + 3]\n            * (\n                0.5786954880513824 * q[j + 3]\n                + 1.2660604719155235 * q[j - 1]\n                - 0.46498946172145633 * q[j - 2]\n                + 0.10352871936656591 * q[j - 3]\n                - 2.0705743873313183 * q[j]\n            )\n            + q[j - 1]\n            * (\n                3.93094886671763 * q[j - 1]\n                - 4.4887583031366605 * q[j - 2]\n                + 2.126671427664419 * q[j - 3]\n                - 8.108990323332568 * q[j]\n            )\n            + q[j - 2]\n            * (\n                1.7504056695205172 * q[j - 2]\n                - 1.402086588589091 * q[j - 3]\n                + 4.614303600090953 * q[j]\n            )\n            + q[j - 3]\n            * (0.5786954880513824 * q[j - 3] - 2.0705743873313183 * q[j])\n            + 5.203166203165525 * q[j] ** 2\n        )\n        if six_to_the_alpha_7**2 * norm_top <= norm_full:\n            result = [\n                -175 * q[j + 1] / 1024\n                + 21 * q[j + 2] / 512\n                - 5 * q[j + 3] / 1024\n                + 525 * q[j - 1] / 1024\n                - 35 * q[j - 2] / 512\n                + 7 * q[j - 3] / 1024\n                + 175 * q[j] / 256,\n                525 * q[j + 1] / 1024\n                - 35 * q[j + 2] / 512\n                + 7 * q[j + 3] / 1024\n                - 175 * q[j - 1] / 1024\n                + 21 * q[j - 2] / 512\n                - 5 * q[j - 3] / 1024\n                + 175 * q[j] / 256,\n            ]\n            if (not keep_positive) or (result[0] > 0.0 and result[1] > 0.0):\n                return result\n\n    norm_top = (\n        0.2222222222222222\n        * (\n            -1.4880952380952381 * q[j + 1]\n            + 0.37202380952380953 * q[j + 2]\n            - 1.4880952380952381 * q[j - 1]\n            + 0.37202380952380953 * q[j - 2]\n            + 2.232142857142857 * q[j]\n        )\n        ** 2\n    )\n    norm_full = (\n        q[j + 1]\n        * (\n            1.179711612654321 * q[j + 1]\n            - 0.963946414792769 * q[j + 2]\n            + 1.0904086750440918 * q[j - 1]\n            - 0.5030502507716049 * q[j - 2]\n            - 1.6356130125661377 * q[j]\n        )\n        + q[j + 2]\n        * (\n            0.6699388830329586 * q[j + 2]\n            - 0.5030502507716049 * q[j - 1]\n            + 0.154568572944224 * q[j - 2]\n            + 0.927411437665344 * q[j]\n        )\n        + q[j - 1]\n        * (\n            1.179711612654321 * q[j - 1]\n            - 0.963946414792769 * q[j - 2]\n            - 1.6356130125661377 * q[j]\n        )\n        + q[j - 2] * (0.6699388830329586 * q[j - 2] + 0.927411437665344 * q[j])\n        + 1.4061182415674602 * q[j] ** 2\n    )\n    if (four_to_the_alpha_5) ** 2 * norm_top <= norm_full:\n        result = [\n            -0.15625 * q[j + 1]\n            + 0.0234375 * q[j + 2]\n            + 0.46875 * q[j - 1]\n            - 0.0390625 * q[j - 2]\n            + 0.703125 * q[j],\n            0.46875 * q[j + 1]\n            - 0.0390625 * q[j + 2]\n            - 0.15625 * q[j - 1]\n            + 0.0234375 * q[j - 2]\n            + 0.703125 * q[j],\n        ]\n        if (not keep_positive) or (result[0] > 0.0 and result[1] > 0.0):\n            return result\n    low_order_result = low_order_recons([q[j - 1], q[j], q[j + 1]])\n    if (not keep_positive) or (\n        low_order_result[0] > 0.0 and low_order_result[1] > 0.0\n    ):\n        return low_order_result\n    return [q[j], q[j]]\n\n\ndef compute_face_values_t(\n    recons_upper_of_cell,\n    recons_lower_of_cell,\n    v,\n    i,\n    j,\n    k,\n    dim_to_recons,\n    keep_positive,\n    low_order_recons,\n    use_9th_order,\n    use_7th_order,\n    four_to_the_alpha_5,\n    six_to_the_alpha_7,\n    eight_to_the_alpha_9,\n):\n    stencil_half_width = 4 if use_9th_order else (3 if use_7th_order else 2)\n    v_stencil = []\n\n    if dim_to_recons == 0:\n        for l in range(-stencil_half_width, stencil_half_width + 1):\n            v_stencil.append(v[i + l, j, k])\n\n        lower, upper = _adaptive_order_5(\n            np.asarray(v_stencil),\n            keep_positive,\n            low_order_recons,\n            use_9th_order,\n            use_7th_order,\n            four_to_the_alpha_5,\n            six_to_the_alpha_7,\n            eight_to_the_alpha_9,\n        )\n        recons_lower_of_cell.append(lower)\n        recons_upper_of_cell.append(upper)\n    if dim_to_recons == 1:\n        for l in range(-stencil_half_width, stencil_half_width + 1):\n            v_stencil.append(v[i, j + l, k])\n\n        lower, upper = _adaptive_order_5(\n            np.asarray(v_stencil),\n            keep_positive,\n            low_order_recons,\n            use_9th_order,\n            use_7th_order,\n            four_to_the_alpha_5,\n            six_to_the_alpha_7,\n            eight_to_the_alpha_9,\n        )\n        recons_lower_of_cell.append(lower)\n        recons_upper_of_cell.append(upper)\n    if dim_to_recons == 2:\n        for l in range(-stencil_half_width, stencil_half_width + 1):\n            v_stencil.append(v[i, j, k + l])\n\n        lower, upper = _adaptive_order_5(\n            np.asarray(v_stencil),\n            keep_positive,\n            low_order_recons,\n            use_9th_order,\n            use_7th_order,\n            four_to_the_alpha_5,\n            six_to_the_alpha_7,\n            eight_to_the_alpha_9,\n        )\n        recons_lower_of_cell.append(lower)\n        recons_upper_of_cell.append(upper)\n\n\ndef test_adaptive_order_with_mc(\n    u,\n    extents,\n    dim,\n    use_9th_order,\n    use_7th_order,\n    four_to_the_alpha_5,\n    six_to_the_alpha_7,\n    eight_to_the_alpha_9,\n):\n    ghost_zone = 4 if use_9th_order else (3 if use_7th_order else 2)\n\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        return compute_face_values_t(\n            recons_upper_of_cell,\n            recons_lower_of_cell,\n            v,\n            i,\n            j,\n            k,\n            dim_to_recons,\n            False,\n            _mc,\n            use_9th_order,\n            use_7th_order,\n            four_to_the_alpha_5,\n            six_to_the_alpha_7,\n            eight_to_the_alpha_9,\n        )\n\n    return Reconstruction.reconstruct(\n        u,\n        extents,\n        dim,\n        [ghost_zone, ghost_zone, ghost_zone],\n        compute_face_values,\n    )\n\n\ndef test_adaptive_order_with_minmod(\n    u,\n    extents,\n    dim,\n    use_9th_order,\n    use_7th_order,\n    four_to_the_alpha_5,\n    six_to_the_alpha_7,\n    eight_to_the_alpha_9,\n):\n    ghost_zone = 4 if use_9th_order else (3 if use_7th_order else 2)\n\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        return compute_face_values_t(\n            recons_upper_of_cell,\n            recons_lower_of_cell,\n            v,\n            i,\n            j,\n            k,\n            dim_to_recons,\n            False,\n            _minmod,\n            use_9th_order,\n            use_7th_order,\n            four_to_the_alpha_5,\n            six_to_the_alpha_7,\n            eight_to_the_alpha_9,\n        )\n\n    return Reconstruction.reconstruct(\n        u,\n        extents,\n        dim,\n        [ghost_zone, ghost_zone, ghost_zone],\n        compute_face_values,\n    )\n\n\ndef test_positivity_preserving_adaptive_order_with_mc(\n    u,\n    extents,\n    dim,\n    use_9th_order,\n    use_7th_order,\n    four_to_the_alpha_5,\n    six_to_the_alpha_7,\n    eight_to_the_alpha_9,\n):\n    ghost_zone = 4 if use_9th_order else (3 if use_7th_order else 2)\n\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        return compute_face_values_t(\n            recons_upper_of_cell,\n            recons_lower_of_cell,\n            v,\n            i,\n            j,\n            k,\n            dim_to_recons,\n            True,\n            _mc,\n            use_9th_order,\n            use_7th_order,\n            four_to_the_alpha_5,\n            six_to_the_alpha_7,\n            eight_to_the_alpha_9,\n        )\n\n    return Reconstruction.reconstruct(\n        u,\n        extents,\n        dim,\n        [ghost_zone, ghost_zone, ghost_zone],\n        compute_face_values,\n    )\n\n\ndef test_positivity_preserving_adaptive_order_with_minmod(\n    u,\n    extents,\n    dim,\n    use_9th_order,\n    use_7th_order,\n    four_to_the_alpha_5,\n    six_to_the_alpha_7,\n    eight_to_the_alpha_9,\n):\n    ghost_zone = 4 if use_9th_order else (3 if use_7th_order else 2)\n\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        return compute_face_values_t(\n            recons_upper_of_cell,\n            recons_lower_of_cell,\n            v,\n            i,\n            j,\n            k,\n            dim_to_recons,\n            True,\n            _minmod,\n            use_9th_order,\n            use_7th_order,\n            four_to_the_alpha_5,\n            six_to_the_alpha_7,\n            eight_to_the_alpha_9,\n        )\n\n    return Reconstruction.reconstruct(\n        u,\n        extents,\n        dim,\n        [ghost_zone, ghost_zone, ghost_zone],\n        compute_face_values,\n    )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Reconstruction.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef reconstruct(u, extents, dim, ghost_zones, func):\n    # Need to push back extra vars to get test to be generic.\n    number_of_dims = len(extents)\n    if len(extents) < 3:\n        for i in range(len(extents), 3):\n            extents.append(1)\n\n    u = np.reshape(np.copy(np.asarray(u)), extents, order=\"F\")\n    recons_x_lower_face = []\n    recons_x_upper_face = []\n    # Reconstruction in x\n    for k in (\n        range(ghost_zones[2] + 1, extents[2] - ghost_zones[2] - 1)\n        if number_of_dims > 2\n        else range(1)\n    ):\n        for j in (\n            range(ghost_zones[1] + 1, extents[1] - ghost_zones[1] - 1)\n            if number_of_dims > 1\n            else range(1)\n        ):\n            recons_upper_of_cell = []\n            recons_lower_of_cell = []\n            for i in range(ghost_zones[0], extents[0] - ghost_zones[0]):\n                func(recons_upper_of_cell, recons_lower_of_cell, u, i, j, k, 0)\n            # By deleting the first entry in lower_of_cell and the last\n            # entry of upper_of_cell we convert to the face-based indices\n            recons_lower_of_cell.pop(0)\n            recons_upper_of_cell.pop(-1)\n            recons_x_lower_face = recons_x_lower_face + recons_upper_of_cell\n            recons_x_upper_face = recons_x_upper_face + recons_lower_of_cell\n\n    if dim == 1:\n        return [[recons_x_lower_face], [recons_x_upper_face]]\n\n    # Reconstruction in y\n    recons_y_lower_face = []\n    recons_y_upper_face = []\n    for k in (\n        range(ghost_zones[2] + 1, extents[2] - ghost_zones[2] - 1)\n        if number_of_dims > 2\n        else range(1)\n    ):\n        recons_upper_of_cell = []\n        recons_lower_of_cell = []\n        for j in range(ghost_zones[1], extents[1] - ghost_zones[1]):\n            for i in range(ghost_zones[0] + 1, extents[0] - ghost_zones[0] - 1):\n                func(recons_upper_of_cell, recons_lower_of_cell, u, i, j, k, 1)\n        # By deleting the first entry in lower_of_cell and the last\n        # entry of upper_of_cell we convert to the face-based indices\n        for i in range(ghost_zones[0] + 1, extents[0] - ghost_zones[0] - 1):\n            recons_lower_of_cell.pop(0)\n            recons_upper_of_cell.pop(-1)\n        recons_y_lower_face = recons_y_lower_face + recons_upper_of_cell\n        recons_y_upper_face = recons_y_upper_face + recons_lower_of_cell\n    if dim == 2:\n        return [\n            [recons_x_lower_face, recons_y_lower_face],\n            [recons_x_upper_face, recons_y_upper_face],\n        ]\n\n    # Reconstruction in z\n    recons_z_lower_face = []\n    recons_z_upper_face = []\n    for k in range(ghost_zones[2], extents[2] - ghost_zones[2]):\n        for j in range(ghost_zones[1] + 1, extents[1] - ghost_zones[1] - 1):\n            for i in range(ghost_zones[0] + 1, extents[0] - ghost_zones[0] - 1):\n                func(recons_z_lower_face, recons_z_upper_face, u, i, j, k, 2)\n    # By deleting the first entry in lower_of_cell and the last\n    # entry of upper_of_cell we convert to the face-based indices\n    for j in range(ghost_zones[1] + 1, extents[1] - ghost_zones[1] - 1):\n        for i in range(ghost_zones[0] + 1, extents[0] - ghost_zones[0] - 1):\n            recons_z_upper_face.pop(0)\n            recons_z_lower_face.pop(-1)\n\n    return [\n        [recons_x_lower_face, recons_y_lower_face, recons_z_lower_face],\n        [recons_x_upper_face, recons_y_upper_face, recons_z_upper_face],\n    ]\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_AoWeno53.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Exact.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Python.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/AoWeno.hpp\"\n\nnamespace {\ntemplate <size_t NonlinearWeightExponent, size_t Dim, bool UseExteriorCell>\nvoid test_function_pointers() {\n  const auto function_ptrs =\n      fd::reconstruction::aoweno_53_function_pointers<Dim>(\n          NonlinearWeightExponent);\n  CHECK(get<0>(function_ptrs) ==\n        &fd::reconstruction::aoweno_53<NonlinearWeightExponent, Dim>);\n  using function_type =\n      void (*)(gsl::not_null<DataVector*>, const DataVector&, const DataVector&,\n               const Index<Dim>&, const Index<Dim>&, const Direction<Dim>&,\n               const double&, const double&, const double&);\n  CHECK(get<1>(function_ptrs) ==\n        static_cast<function_type>(\n            &fd::reconstruction::reconstruct_neighbor<\n                Side::Lower,\n                ::fd::reconstruction::detail::AoWeno53Reconstructor<\n                    NonlinearWeightExponent>,\n                UseExteriorCell, Dim>));\n  CHECK(get<2>(function_ptrs) ==\n        static_cast<function_type>(\n            &fd::reconstruction::reconstruct_neighbor<\n                Side::Upper,\n                ::fd::reconstruction::detail::AoWeno53Reconstructor<\n                    NonlinearWeightExponent>,\n                UseExteriorCell, Dim>));\n}\n\ntemplate <size_t Dim>\nvoid test() {\n  const auto recons =\n      [](const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_upper_side_of_face_vars,\n         const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_lower_side_of_face_vars,\n         const gsl::span<const double>& volume_vars,\n         const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n         const Index<Dim>& volume_extents, const size_t number_of_variables) {\n        const double gamma_hi = 0.85;\n        const double gamma_lo = 0.999;\n        const double epsilon = 1.0e-12;\n        fd::reconstruction::aoweno_53<8>(\n            reconstructed_upper_side_of_face_vars,\n            reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n            volume_extents, number_of_variables, gamma_hi, gamma_lo, epsilon);\n      };\n  const auto recons_neighbor_data = [](const gsl::not_null<DataVector*>\n                                           face_data,\n                                       const DataVector& volume_data,\n                                       const DataVector& neighbor_data,\n                                       const Index<Dim>& volume_extents,\n                                       const Index<Dim>& ghost_data_extents,\n                                       const Direction<Dim>&\n                                           direction_to_reconstruct) {\n    const double gamma_hi = 0.85;\n    const double gamma_lo = 0.999;\n    const double epsilon = 1.0e-12;\n    if (direction_to_reconstruct.side() == Side::Upper) {\n      fd::reconstruction::reconstruct_neighbor<\n          Side::Upper, fd::reconstruction::detail::AoWeno53Reconstructor<8>>(\n          face_data, volume_data, neighbor_data, volume_extents,\n          ghost_data_extents, direction_to_reconstruct, gamma_hi, gamma_lo,\n          epsilon);\n    }\n    if (direction_to_reconstruct.side() == Side::Lower) {\n      fd::reconstruction::reconstruct_neighbor<\n          Side::Lower, fd::reconstruction::detail::AoWeno53Reconstructor<8>>(\n          face_data, volume_data, neighbor_data, volume_extents,\n          ghost_data_extents, direction_to_reconstruct, gamma_hi, gamma_lo,\n          epsilon);\n    }\n  };\n\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(2, 8, 5, recons, recons_neighbor_data);\n  TestHelpers::fd::reconstruction::test_with_python(\n      Index<Dim>{8}, 5, \"AoWeno53\", \"test_aoweno53\", recons,\n      recons_neighbor_data);\n\n  // Check the 5th order reconstruction is exact\n  const auto recons_5th_order_only =\n      [](const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_upper_side_of_face_vars,\n         const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_lower_side_of_face_vars,\n         const gsl::span<const double>& volume_vars,\n         const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n         const Index<Dim>& volume_extents, const size_t number_of_variables) {\n        const double gamma_hi = 1.0;\n        const double gamma_lo = 0.999;\n        const double epsilon = 1.0e-12;\n        fd::reconstruction::aoweno_53<8>(\n            reconstructed_upper_side_of_face_vars,\n            reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n            volume_extents, number_of_variables, gamma_hi, gamma_lo, epsilon);\n      };\n  const auto recons_neighbor_data_5th_order_only =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        const double gamma_hi = 1.0;\n        const double gamma_lo = 0.999;\n        const double epsilon = 1.0e-12;\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper,\n              fd::reconstruction::detail::AoWeno53Reconstructor<8>>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct, gamma_hi, gamma_lo,\n              epsilon);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower,\n              fd::reconstruction::detail::AoWeno53Reconstructor<8>>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct, gamma_hi, gamma_lo,\n              epsilon);\n        }\n      };\n  const auto recons_neighbor_data_5th_order_only_interior_cell =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        const double gamma_hi = 1.0;\n        const double gamma_lo = 0.999;\n        const double epsilon = 1.0e-12;\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper, fd::reconstruction::detail::AoWeno53Reconstructor<8>,\n              false>(face_data, volume_data, neighbor_data, volume_extents,\n                     ghost_data_extents, direction_to_reconstruct, gamma_hi,\n                     gamma_lo, epsilon);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower, fd::reconstruction::detail::AoWeno53Reconstructor<8>,\n              false>(face_data, volume_data, neighbor_data, volume_extents,\n                     ghost_data_extents, direction_to_reconstruct, gamma_hi,\n                     gamma_lo, epsilon);\n        }\n      };\n\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(4, 8, 5, recons_5th_order_only, recons_neighbor_data_5th_order_only);\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(4, 8, 5, recons_5th_order_only,\n           recons_neighbor_data_5th_order_only_interior_cell);\n\n  test_function_pointers<2, Dim, true>();\n  test_function_pointers<4, Dim, true>();\n  test_function_pointers<6, Dim, true>();\n  test_function_pointers<8, Dim, true>();\n  test_function_pointers<10, Dim, true>();\n  test_function_pointers<12, Dim, true>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.AoWeno53\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"NumericalAlgorithms/FiniteDifference/\");\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_DerivativeOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <fd::DerivativeOrder DerivOrder>\nvoid test_construct_from_options(const std::string& expected_output) {\n  CHECK(get_output(DerivOrder) == expected_output);\n  const auto created =\n      TestHelpers::test_creation<fd::DerivativeOrder>(get_output(DerivOrder));\n  CHECK(created == DerivOrder);\n}\n}  // namespace\n\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.DerivativeOrder\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test_construct_from_options<fd::DerivativeOrder::OneHigherThanRecons>(\n      \"OneHigherThanRecons\");\n  test_construct_from_options<\n      fd::DerivativeOrder::OneHigherThanReconsButFiveToFour>(\n      \"OneHigherThanReconsButFiveToFour\");\n  test_construct_from_options<fd::DerivativeOrder::Two>(\"2\");\n  test_construct_from_options<fd::DerivativeOrder::Four>(\"4\");\n  test_construct_from_options<fd::DerivativeOrder::Six>(\"6\");\n  test_construct_from_options<fd::DerivativeOrder::Eight>(\"8\");\n  test_construct_from_options<fd::DerivativeOrder::Ten>(\"10\");\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_FallbackReconstructorType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace fd::reconstruction {\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.FallbackReconstructorType\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  CHECK(FallbackReconstructorType::Minmod ==\n        TestHelpers::test_creation<FallbackReconstructorType>(\"Minmod\"));\n  CHECK(FallbackReconstructorType::MonotonisedCentral ==\n        TestHelpers::test_creation<FallbackReconstructorType>(\n            \"MonotonisedCentral\"));\n  CHECK(FallbackReconstructorType::None ==\n        TestHelpers::test_creation<FallbackReconstructorType>(\"None\"));\n\n  CHECK(get_output(FallbackReconstructorType::Minmod) == \"Minmod\");\n  CHECK(get_output(FallbackReconstructorType::MonotonisedCentral) ==\n        \"MonotonisedCentral\");\n  CHECK(get_output(FallbackReconstructorType::None) == \"None\");\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        TestHelpers::test_creation<FallbackReconstructorType>(\"BadType\");\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"BadType\\\" to FallbackReconstructorType\"));\n}\n\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_Filter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <iterator>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Filter.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\n// Compute polynomial on cell centers in FD cluster of points\ntemplate <size_t Dim>\nvoid set_polynomial(\n    const gsl::not_null<DataVector*> var1_ptr,\n    const gsl::not_null<DataVector*> var2_ptr,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& local_logical_coords,\n    const size_t degree) {\n  *var1_ptr = 0.0;\n  *var2_ptr = 100.0;  // some constant offset to distinguish the var values\n  for (size_t i = 0; i < Dim; ++i) {\n    *var1_ptr += pow(local_logical_coords.get(i), degree);\n    *var2_ptr += pow(local_logical_coords.get(i), degree);\n  }\n}\n\ntemplate <size_t Dim>\nvoid set_solution(\n    const gsl::not_null<DataVector*> volume_vars,\n    const gsl::not_null<DirectionMap<Dim, DataVector>*> neighbor_data,\n    const gsl::not_null<DirectionMap<Dim, gsl::span<const double>>*>\n        ghost_cell_vars,\n    const Mesh<Dim>& mesh, const size_t number_of_vars,\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& logical_coords,\n    const size_t deriv_order, const size_t degree) {\n  *volume_vars = DataVector{mesh.number_of_grid_points() * number_of_vars, 0.0};\n  DataVector var1(volume_vars->data(), mesh.number_of_grid_points());\n  DataVector var2(\n      std::next(volume_vars->data(),\n                static_cast<std::ptrdiff_t>(mesh.number_of_grid_points())),\n      mesh.number_of_grid_points());\n  set_polynomial(&var1, &var2, logical_coords, degree);\n\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    DataVector neighbor_vars{mesh.number_of_grid_points() * number_of_vars,\n                             0.0};\n    DataVector neighbor_var1(neighbor_vars.data(),\n                             mesh.number_of_grid_points());\n    DataVector neighbor_var2(\n        neighbor_vars.data() + mesh.number_of_grid_points(),  // NOLINT\n        mesh.number_of_grid_points());\n    set_polynomial(&neighbor_var1, &neighbor_var2, neighbor_logical_coords,\n                   degree);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars.data(), neighbor_vars.size()),\n        mesh.extents(), deriv_order / 2 + 1,\n        std::unordered_set{direction.opposite()}, 0, {});\n    CAPTURE(deriv_order / 2 + 1);\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    (*neighbor_data)[direction] = sliced_data.at(direction.opposite());\n    (*ghost_cell_vars)[direction] = gsl::make_span(\n        (*neighbor_data)[direction].data(), (*neighbor_data)[direction].size());\n    REQUIRE(neighbor_data->at(direction).size() ==\n            number_of_vars * (deriv_order / 2 + 1) *\n                mesh.slice_away(0).number_of_grid_points());\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_ko_dissipation() {\n  CAPTURE(Dim);\n  const size_t number_of_vars = 2;\n  const double epsilon = 0.1;\n\n  const Mesh<Dim> mesh{13, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < Dim; ++i) {\n    logical_coords.get(i) += 4.0 * i;\n  }\n\n  for (size_t deriv_order = 2; deriv_order < 12; deriv_order += 2) {\n    CAPTURE(deriv_order);\n    DataVector volume_vars{};\n    DirectionMap<Dim, DataVector> neighbor_data{};\n    DirectionMap<Dim, gsl::span<const double>> ghost_cell_vars{};\n    set_solution(make_not_null(&volume_vars), make_not_null(&neighbor_data),\n                 make_not_null(&ghost_cell_vars), mesh, number_of_vars,\n                 logical_coords, deriv_order, deriv_order - 1);\n\n    DataVector filtered_vars{mesh.number_of_grid_points() * number_of_vars,\n                             0.0};\n    auto filtered_vars_span =\n        gsl::make_span(filtered_vars.data(), filtered_vars.size());\n    fd::kreiss_oliger_filter(\n        make_not_null(&filtered_vars_span),\n        gsl::make_span(volume_vars.data(), volume_vars.size()), ghost_cell_vars,\n        mesh, number_of_vars, deriv_order, epsilon);\n\n    // Get only the KO dissipation term by subtracting off the volume\n    // variables from the filtered data.\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n    const DataVector view_volume_vars{const_cast<double*>(volume_vars.data()),\n                                      volume_vars.size()};\n    DataVector view_filtered_vars{filtered_vars.data(), filtered_vars.size()};\n    view_filtered_vars -= view_volume_vars;\n    if (Dim > 1 and deriv_order == 10) {\n      Approx custom_approx = Approx::custom().epsilon(1.0e-10).scale(1.0);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          view_filtered_vars,\n          DataVector(mesh.number_of_grid_points() * number_of_vars, 0.0),\n          custom_approx);\n    } else {\n      CHECK_ITERABLE_APPROX(\n          view_filtered_vars,\n          DataVector(mesh.number_of_grid_points() * number_of_vars, 0.0));\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_low_pass_filter() {\n  CAPTURE(Dim);\n  const size_t number_of_vars = 2;\n  const double epsilon = 1.0;\n\n  const Mesh<Dim> mesh{13, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < Dim; ++i) {\n    logical_coords.get(i) += 4.0 * i;\n  }\n\n  // Large-ish relative error in 3d\n  Approx custom_approx = Approx::custom().epsilon(1.0e-8).scale(1.0);\n  for (size_t deriv_order = 2; deriv_order < 10; deriv_order += 2) {\n    CAPTURE(deriv_order);\n    for (size_t degree = 0; degree < deriv_order; ++degree) {\n      CAPTURE(degree);\n      DataVector volume_vars{};\n      DirectionMap<Dim, DataVector> neighbor_data{};\n      DirectionMap<Dim, gsl::span<const double>> ghost_cell_vars{};\n      set_solution(make_not_null(&volume_vars), make_not_null(&neighbor_data),\n                   make_not_null(&ghost_cell_vars), mesh, number_of_vars,\n                   logical_coords, deriv_order, degree);\n\n      DataVector filtered_vars{mesh.number_of_grid_points() * number_of_vars,\n                               0.0};\n      auto filtered_vars_span =\n          gsl::make_span(filtered_vars.data(), filtered_vars.size());\n\n      fd::low_pass_filter(\n          make_not_null(&filtered_vars_span),\n          gsl::make_span(volume_vars.data(), volume_vars.size()),\n          ghost_cell_vars, mesh, number_of_vars, deriv_order, epsilon);\n\n      // Get only the low-pass filter term by subtracting off the volume\n      // variables from the filtered data.\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n      const DataVector view_volume_vars{const_cast<double*>(volume_vars.data()),\n                                        volume_vars.size()};\n      DataVector view_filtered_vars{filtered_vars.data(), filtered_vars.size()};\n      view_filtered_vars -= view_volume_vars;\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          view_filtered_vars,\n          DataVector(mesh.number_of_grid_points() * number_of_vars, 0.0),\n          custom_approx);\n    }\n  }\n}\n\n// Test filtering with Cartoon bases (axially or spherically symmetric meshes).\n//\n// For cartoon meshes, `filter_impl` determines a computational dimension\n// `comp_dim < Dim` and only filters along those directions.  We verify the\n// filter correction vanishes when the data is a low-degree polynomial in the\n// FD dimensions only.\ntemplate <bool Spherical>\nvoid test_cartoon_filter() {\n  CAPTURE(Spherical);\n  constexpr size_t comp_dim = Spherical ? 1 : 2;\n  const size_t number_of_vars = 2;\n  const double epsilon_ko = 0.1;\n  const double epsilon_lp = 1.0;\n\n  for (size_t deriv_order = 2; deriv_order < 10; deriv_order += 2) {\n    CAPTURE(deriv_order);\n    const size_t n = deriv_order + 3;  // enough points for the stencil\n\n    Mesh<3> mesh{};\n    if constexpr (Spherical) {\n      mesh = Mesh<3>{{n, 1, 1},\n                     {Spectral::Basis::FiniteDifference,\n                      Spectral::Basis::Cartoon, Spectral::Basis::Cartoon},\n                     {Spectral::Quadrature::CellCentered,\n                      Spectral::Quadrature::SphericalSymmetry,\n                      Spectral::Quadrature::SphericalSymmetry}};\n    } else {\n      mesh =\n          Mesh<3>{{n, n, 1},\n                  {Spectral::Basis::FiniteDifference,\n                   Spectral::Basis::FiniteDifference, Spectral::Basis::Cartoon},\n                  {Spectral::Quadrature::CellCentered,\n                   Spectral::Quadrature::CellCentered,\n                   Spectral::Quadrature::AxialSymmetry}};\n    }\n\n    // FD sub-mesh and its logical coordinates (comp_dim components).\n    // Because each Cartoon dim has exactly 1 grid point, the FD sub-mesh\n    // grid-point ordering matches the full 3D mesh ordering.\n    const Mesh<comp_dim> fd_submesh = [&]() {\n      if constexpr (Spherical) {\n        return mesh.slice_through(0);\n      } else {\n        return mesh.slice_through(0, 1);\n      }\n    }();\n    const auto fd_coords = logical_coordinates(fd_submesh);\n\n    const size_t num_pts = mesh.number_of_grid_points();\n\n    // Evaluate sum_d coords.get(d)^degree at given FD coords.\n    const auto poly_at =\n        [&](const size_t degree,\n            const tnsr::I<DataVector, comp_dim, Frame::ElementLogical>& coords)\n        -> DataVector {\n      DataVector val(num_pts, 0.0);\n      for (size_t d = 0; d < comp_dim; ++d) {\n        val += pow(coords.get(d), degree);\n      }\n      return val;\n    };\n    const auto poly = [&](const size_t degree) -> DataVector {\n      return poly_at(degree, fd_coords);\n    };\n\n    // Build ghost data for directions 0..comp_dim-1 only.\n    // For each neighbor, shift its logical coords by \\pm 2 in the FD direction\n    const auto make_ghost_data =\n        [&](const size_t degree,\n            const size_t ghost_pts) -> DirectionMap<3, DataVector> {\n      DirectionMap<3, DataVector> nbr_store{};\n\n      for (size_t d = 0; d < comp_dim; ++d) {\n        for (const auto side : {Side::Lower, Side::Upper}) {\n          auto nbr_coords = fd_coords;\n          nbr_coords.get(d) += (side == Side::Lower ? -1.0 : 1.0) * 2.0;\n\n          DataVector nbr_vars(num_pts * number_of_vars, 0.0);\n          for (size_t v = 0; v < number_of_vars; ++v) {\n            DataVector vview(nbr_vars.data() + v * num_pts, num_pts);\n            vview =\n                poly_at(degree, nbr_coords) + static_cast<double>(v) * 100.0;\n          }\n\n          const Direction<comp_dim> dir_comp{d, side};\n          const Direction<3> dir3{d, side};\n          const auto sliced = evolution::dg::subcell::detail::slice_data_impl(\n              gsl::make_span(nbr_vars.data(), nbr_vars.size()),\n              fd_submesh.extents(), ghost_pts,\n              std::unordered_set{dir_comp.opposite()}, 0, {});\n          REQUIRE(sliced.contains(dir_comp.opposite()));\n          nbr_store[dir3] = sliced.at(dir_comp.opposite());\n        }\n      }\n      return nbr_store;\n    };\n\n    const auto make_ghost_spans =\n        [](const DirectionMap<3, DataVector>& nbr_store)\n        -> DirectionMap<3, gsl::span<const double>> {\n      DirectionMap<3, gsl::span<const double>> ghost{};\n      for (const auto& [dir, data] : nbr_store) {\n        ghost[dir] = gsl::make_span(data.data(), data.size());\n      }\n      return ghost;\n    };\n\n    // Build volume vars for a given degree (two vars, second offset by 100)\n    const auto make_volume_vars = [&](const size_t degree) -> DataVector {\n      DataVector vvars(num_pts * number_of_vars, 0.0);\n      const DataVector p = poly(degree);\n      for (size_t v = 0; v < number_of_vars; ++v) {\n        DataVector vview(vvars.data() + v * num_pts, num_pts);\n        vview = p + static_cast<double>(v) * 100.0;\n      }\n      return vvars;\n    };\n\n    // Helper to apply a filter and return the correction (filtered - volume)\n    const auto filter_correction =\n        [&](const DataVector& volume_vars,\n            const DirectionMap<3, gsl::span<const double>>& ghost_cell_vars,\n            const bool use_ko) -> DataVector {\n      DataVector filtered(num_pts * number_of_vars, 0.0);\n      auto fspan = gsl::make_span(filtered.data(), filtered.size());\n      const auto vspan = gsl::make_span(volume_vars.data(), volume_vars.size());\n      if (use_ko) {\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost_cell_vars,\n                                 mesh, number_of_vars, deriv_order, epsilon_ko);\n      } else {\n        fd::low_pass_filter(make_not_null(&fspan), vspan, ghost_cell_vars, mesh,\n                            number_of_vars, deriv_order, epsilon_lp);\n      }\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)\n      const DataVector vview{const_cast<double*>(volume_vars.data()),\n                             volume_vars.size()};\n      DataVector fview{filtered.data(), filtered.size()};\n      fview -= vview;\n      return filtered;\n    };\n\n    const size_t ghost_pts = deriv_order / 2 + 1;\n\n    // KO dissipation: degree = deriv_order - 1 should give zero correction\n    {\n      const DataVector vvars = make_volume_vars(deriv_order - 1);\n      const DirectionMap<3, DataVector> nbr_store =\n          make_ghost_data(deriv_order - 1, ghost_pts);\n      const DirectionMap<3, gsl::span<const double>> ghost =\n          make_ghost_spans(nbr_store);\n      const DataVector corr = filter_correction(vvars, ghost, /*use_ko=*/true);\n      const Approx custom_approx = Approx::custom().epsilon(1.0e-14).scale(1.0);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          corr, DataVector(num_pts * number_of_vars, 0.0), custom_approx);\n    }\n    // Low-pass: all degrees < fd_order should give zero correction\n    {\n      const Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n      for (size_t degree = 0; degree < deriv_order; ++degree) {\n        CAPTURE(degree);\n        const DataVector vvars = make_volume_vars(degree);\n        const DirectionMap<3, DataVector> nbr_store =\n            make_ghost_data(degree, ghost_pts);\n        const DirectionMap<3, gsl::span<const double>> ghost =\n            make_ghost_spans(nbr_store);\n        const DataVector corr =\n            filter_correction(vvars, ghost, /*use_ko=*/false);\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            corr, DataVector(num_pts * number_of_vars, 0.0), custom_approx);\n      }\n    }\n  }\n}\n\n// Build minimal but valid ghost data for a 1D mesh so that asserts that fire\n// before the ghost-data checks can be reached cleanly.\nDirectionMap<1, gsl::span<const double>> make_valid_ghost_spans_1d(\n    const DirectionMap<1, DataVector>& store) {\n  DirectionMap<1, gsl::span<const double>> ghost{};\n  for (const auto& [dir, data] : store) {\n    ghost[dir] = gsl::make_span(data.data(), data.size());\n  }\n  return ghost;\n}\n\nvoid test_asserts() {\n  const size_t number_of_vars = 1;\n  const double epsilon = 0.1;\n  {\n    INFO(\"Unsupported fd_order value\");\n    const Mesh<1> mesh{5, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n    DataVector vol(5, 1.0);\n    const DataVector lower_ghost(2, 1.0);\n    const DataVector upper_ghost(2, 1.0);\n    DirectionMap<1, DataVector> store{};\n    store[Direction<1>::lower_xi()] = lower_ghost;\n    store[Direction<1>::upper_xi()] = upper_ghost;\n    const auto ghost = make_valid_ghost_spans_1d(store);\n    DataVector filtered(5, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                                 number_of_vars, /*fd_order=*/3, epsilon),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot do finite difference filter of order 3\"));\n    CHECK_THROWS_WITH(\n        fd::low_pass_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                            number_of_vars, /*fd_order=*/7, epsilon),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot do finite difference filter of order 7\"));\n  }\n#ifdef SPECTRE_DEBUG\n  {\n    INFO(\"Missing ghost data directions\");\n    const Mesh<1> mesh{5, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n    DataVector vol(5, 1.0);\n    DataVector filtered(5, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    {\n      INFO(\"Missing lower-xi ghost data\");\n      const DataVector upper_ghost(2, 1.0);\n      DirectionMap<1, DataVector> store{};\n      store[Direction<1>::upper_xi()] = upper_ghost;\n      const auto ghost = make_valid_ghost_spans_1d(store);\n      CHECK_THROWS_WITH(\n          fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                                   number_of_vars, 2, epsilon),\n          Catch::Matchers::ContainsSubstring(\n              \"Couldn't find lower ghost data in lower-xi\"));\n    }\n    {\n      INFO(\"Missing upper-xi ghost data\");\n      const DataVector lower_ghost(2, 1.0);\n      DirectionMap<1, DataVector> store{};\n      store[Direction<1>::lower_xi()] = lower_ghost;\n      const auto ghost = make_valid_ghost_spans_1d(store);\n      CHECK_THROWS_WITH(\n          fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                                   number_of_vars, 2, epsilon),\n          Catch::Matchers::ContainsSubstring(\n              \"Couldn't find upper ghost data in upper-xi\"));\n    }\n  }\n  {\n    INFO(\"Non-isotropic mesh (1D path)\");\n    const Mesh<1> mesh_aniso{5, Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n    // Override with a mesh whose extents differ from isotropic—only reachable\n    // for Dim == 1 when basis(0) != FiniteDifference.\n    // Easiest: wrong basis.\n    const Mesh<1> mesh_bad{5, Spectral::Basis::Chebyshev,\n                           Spectral::Quadrature::GaussLobatto};\n    const DataVector vol(5, 1.0);\n    const DataVector lower_ghost(2, 1.0);\n    const DataVector upper_ghost(2, 1.0);\n    DirectionMap<1, DataVector> store{};\n    store[Direction<1>::lower_xi()] = lower_ghost;\n    store[Direction<1>::upper_xi()] = upper_ghost;\n    const auto ghost = make_valid_ghost_spans_1d(store);\n    DataVector filtered(5, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh_bad,\n                                 number_of_vars, 2, epsilon),\n        Catch::Matchers::ContainsSubstring(\n            \"Mesh basis must be FiniteDifference\"));\n  }\n  {\n    INFO(\"Wrong quadrature\");\n    const Mesh<1> mesh_bad{5, Spectral::Basis::FiniteDifference,\n                           Spectral::Quadrature::FaceCentered};\n    const DataVector vol(5, 1.0);\n    const DataVector lower_ghost(2, 1.0);\n    const DataVector upper_ghost(2, 1.0);\n    DirectionMap<1, DataVector> store{};\n    store[Direction<1>::lower_xi()] = lower_ghost;\n    store[Direction<1>::upper_xi()] = upper_ghost;\n    const auto ghost = make_valid_ghost_spans_1d(store);\n    DataVector filtered(5, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh_bad,\n                                 number_of_vars, 2, epsilon),\n        Catch::Matchers::ContainsSubstring(\n            \"Mesh quadrature must be CellCentered\"));\n  }\n  {\n    INFO(\"volume_vars size mismatch\");\n    const Mesh<1> mesh{5, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n    const DataVector vol(4, 1.0);  // wrong: should be 5\n    const DataVector lower_ghost(2, 1.0);\n    const DataVector upper_ghost(2, 1.0);\n    DirectionMap<1, DataVector> store{};\n    store[Direction<1>::lower_xi()] = lower_ghost;\n    store[Direction<1>::upper_xi()] = upper_ghost;\n    const auto ghost = make_valid_ghost_spans_1d(store);\n    DataVector filtered(5, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                                 number_of_vars, 2, epsilon),\n        Catch::Matchers::ContainsSubstring(\n            \"The size of the volume vars must be the number of points\"));\n  }\n  {\n    INFO(\"filtered_data size mismatch\");\n    const Mesh<1> mesh{5, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n    const DataVector vol(5, 1.0);\n    const DataVector lower_ghost(2, 1.0);\n    const DataVector upper_ghost(2, 1.0);\n    DirectionMap<1, DataVector> store{};\n    store[Direction<1>::lower_xi()] = lower_ghost;\n    store[Direction<1>::upper_xi()] = upper_ghost;\n    const auto ghost = make_valid_ghost_spans_1d(store);\n    DataVector filtered(4, 0.0);  // wrong: should be 5\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                                 number_of_vars, 2, epsilon),\n        Catch::Matchers::ContainsSubstring(\"The filtered data must have size\"));\n  }\n  {\n    INFO(\"Volume extent too small for stencil (filter_fastest_dim assert)\");\n    const Mesh<1> mesh{1, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n    const DataVector vol(1, 1.0);\n    const DataVector lower_ghost(2, 1.0);\n    const DataVector upper_ghost(2, 1.0);\n    DirectionMap<1, DataVector> store{};\n    store[Direction<1>::lower_xi()] = lower_ghost;\n    store[Direction<1>::upper_xi()] = upper_ghost;\n    const auto ghost = make_valid_ghost_spans_1d(store);\n    DataVector filtered(1, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                                 number_of_vars, 2, epsilon),\n        Catch::Matchers::ContainsSubstring(\"Subcell volume extent\"));\n  }\n  {\n    INFO(\"Lower ghost data size not a multiple of number_of_stripes\");\n    // Use 2 variables so number_of_stripes = 2; ghost size 3 is not divisible\n    // by 2, triggering the assert before any other issue.\n    const Mesh<1> mesh{5, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n    const DataVector vol(10, 1.0);         // 5 pts * 2 vars\n    const DataVector lower_ghost(3, 1.0);  // not divisible by stripes=2\n    const DataVector upper_ghost(3, 1.0);\n    DirectionMap<1, DataVector> store{};\n    store[Direction<1>::lower_xi()] = lower_ghost;\n    store[Direction<1>::upper_xi()] = upper_ghost;\n    const auto ghost = make_valid_ghost_spans_1d(store);\n    DataVector filtered(10, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                                 /*number_of_variables=*/2, 2, epsilon),\n        Catch::Matchers::ContainsSubstring(\n            \"The lower ghost data must be a multiple of the number of \"\n            \"stripes\"));\n  }\n  {\n    INFO(\"Lower/upper ghost sizes differ\");\n    const Mesh<1> mesh{5, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n    const DataVector vol(5, 1.0);\n    const DataVector lower_ghost(2, 1.0);\n    const DataVector upper_ghost(4, 1.0);  // different size\n    DirectionMap<1, DataVector> store{};\n    store[Direction<1>::lower_xi()] = lower_ghost;\n    store[Direction<1>::upper_xi()] = upper_ghost;\n    const auto ghost = make_valid_ghost_spans_1d(store);\n    DataVector filtered(5, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost, mesh,\n                                 number_of_vars, 2, epsilon),\n        Catch::Matchers::ContainsSubstring(\"The lower ghost data size\"));\n  }\n  {\n    INFO(\"Non-isotropic 3D mesh (all FD dims)\");\n    const Mesh<3> mesh_aniso{{5, 7, 5},\n                             Spectral::Basis::FiniteDifference,\n                             Spectral::Quadrature::CellCentered};\n    const DataVector vol(5_st * 7_st * 5_st, 1.0);\n    // Provide ghost data for all directions with any size; assert fires before\n    // accessing it.\n    const DataVector ghost_data(1, 1.0);\n    DirectionMap<3, DataVector> store3{};\n    for (const auto& dir : Direction<3>::all_directions()) {\n      store3[dir] = ghost_data;\n    }\n    DirectionMap<3, gsl::span<const double>> ghost3{};\n    for (const auto& [dir, data] : store3) {\n      ghost3[dir] = gsl::make_span(data.data(), data.size());\n    }\n    DataVector filtered(5_st * 7_st * 5_st, 0.0);\n    auto fspan = gsl::make_span(filtered.data(), filtered.size());\n    const auto vspan = gsl::make_span(vol.data(), vol.size());\n    CHECK_THROWS_WITH(\n        fd::kreiss_oliger_filter(make_not_null(&fspan), vspan, ghost3,\n                                 mesh_aniso, number_of_vars, 2, epsilon),\n        Catch::Matchers::ContainsSubstring(\"The mesh must be isotropic\"));\n  }\n#endif  // SPECTRE_DEBUG\n}\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.Filter\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test_ko_dissipation<1>();\n  test_ko_dissipation<2>();\n  test_ko_dissipation<3>();\n\n  test_low_pass_filter<1>();\n  test_low_pass_filter<2>();\n  test_low_pass_filter<3>();\n\n  test_cartoon_filter<false>();\n  test_cartoon_filter<true>();\n\n  test_asserts();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_HighOrderFluxCorrection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <cstdint>\n#include <random>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Evolution/DgSubcell/CartesianFluxDivergence.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/DerivativeOrder.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/HighOrderFluxCorrection.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nstruct Scalar0 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Vector0 : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n\ntemplate <size_t Dim>\nvoid test(const fd::DerivativeOrder correction_order) {\n  CAPTURE(correction_order);\n  CAPTURE(Dim);\n  const size_t max_degree =\n      correction_order == fd::DerivativeOrder::OneHigherThanRecons\n          ? 6\n          : (correction_order ==\n                     fd::DerivativeOrder::OneHigherThanReconsButFiveToFour\n                 ? 4\n                 : static_cast<size_t>(correction_order));\n  const size_t points_per_dimension = static_cast<size_t>(max_degree) + 2;\n  const size_t stencil_width = max_degree + 1;\n  const size_t number_of_ghost_points = (stencil_width - 1) / 2 + 1;\n  CAPTURE(points_per_dimension);\n\n  using FluxTags = tmpl::list<Scalar0, Vector0<Dim>>;\n  using Scalar0Flux = ::Tags::Flux<Scalar0, tmpl::size_t<Dim>, Frame::Inertial>;\n  using Vector0Flux =\n      ::Tags::Flux<Vector0<Dim>, tmpl::size_t<Dim>, Frame::Inertial>;\n  using FluxVars =\n      Variables<db::wrap_tags_in<::Tags::Flux, FluxTags, tmpl::size_t<Dim>,\n                                 Frame::Inertial>>;\n  using CorrectionVars = Variables<FluxTags>;\n\n  const Mesh<Dim> mesh{points_per_dimension, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < Dim; ++i) {\n    logical_coords.get(i) += 4.0 * static_cast<double>(i);\n  }\n\n  // Compute polynomial on cell centers in FD cluster of points\n  const auto set_polynomial = Overloader{\n      [max_degree](const gsl::not_null<FluxVars*> vars_ptr,\n                   const auto& local_logical_coords) {\n        (void)max_degree;\n        for (size_t storage_index = 0;\n             storage_index < get<Scalar0Flux>(*vars_ptr).size();\n             ++storage_index) {\n          get<Scalar0Flux>(*vars_ptr)[storage_index] = 0.0;\n          for (size_t degree = 1; degree <= max_degree; ++degree) {\n            for (size_t i = 0; i < Dim; ++i) {\n              get<Scalar0Flux>(*vars_ptr)[storage_index] +=\n                  pow(local_logical_coords.get(i), degree);\n            }\n          }\n        }\n        for (size_t storage_index = 0;\n             storage_index < get<Vector0Flux>(*vars_ptr).size();\n             ++storage_index) {\n          get<Vector0Flux>(*vars_ptr)[storage_index] =\n              1.0 + 0.3 * static_cast<double>(storage_index);\n          for (size_t degree = 1; degree <= max_degree; ++degree) {\n            for (size_t i = 0; i < Dim; ++i) {\n              get<Vector0Flux>(*vars_ptr)[storage_index] +=\n                  pow(local_logical_coords.get(i), degree);\n            }\n          }\n        }\n      },\n      [max_degree](const gsl::not_null<CorrectionVars*> vars_ptr,\n                   const auto& local_logical_coords) {\n        (void)max_degree;\n        for (size_t storage_index = 0;\n             storage_index < get<Scalar0>(*vars_ptr).size(); ++storage_index) {\n          get<Scalar0>(*vars_ptr)[storage_index] = 0.0;\n          for (size_t degree = 1; degree <= max_degree; ++degree) {\n            for (size_t i = 0; i < Dim; ++i) {\n              get<Scalar0>(*vars_ptr)[storage_index] +=\n                  pow(local_logical_coords.get(i), degree);\n            }\n          }\n        }\n        for (size_t storage_index = 0;\n             storage_index < get<Vector0<Dim>>(*vars_ptr).size();\n             ++storage_index) {\n          get<Vector0<Dim>>(*vars_ptr)[storage_index] =\n              100.0 + 11.0 * static_cast<double>(storage_index);\n          for (size_t degree = 1; degree <= max_degree; ++degree) {\n            for (size_t i = 0; i < Dim; ++i) {\n              get<Vector0<Dim>>(*vars_ptr)[storage_index] +=\n                  pow(local_logical_coords.get(i), degree);\n            }\n          }\n        }\n      }};\n  const auto set_polynomial_divergence =\n      [max_degree](const gsl::not_null<CorrectionVars*> d_vars_ptr,\n                   const auto& local_logical_coords) {\n        (void)max_degree;\n        get(get<Scalar0>(*d_vars_ptr)) = 0.0;\n        for (size_t i = 0; i < Dim; ++i) {\n          // constant deriv is zero\n          get<Vector0<Dim>>(*d_vars_ptr).get(i) = 0.0;\n        }\n        // Compute divergence\n        for (size_t deriv_dim = 0; deriv_dim < Dim; ++deriv_dim) {\n          for (size_t degree = 1; degree <= max_degree; ++degree) {\n            get(get<Scalar0>(*d_vars_ptr)) +=\n                degree * pow(local_logical_coords.get(deriv_dim), degree - 1);\n            for (size_t i = 0; i < Dim; ++i) {\n              get<Vector0<Dim>>(*d_vars_ptr).get(i) +=\n                  degree * pow(local_logical_coords.get(deriv_dim), degree - 1);\n            }\n          }\n        }\n      };\n  std::optional<FluxVars> volume_vars(mesh.number_of_grid_points());\n  set_polynomial(&(volume_vars.value()), logical_coords);\n\n  CorrectionVars expected_divergence(mesh.number_of_grid_points());\n  set_polynomial_divergence(&expected_divergence, logical_coords);\n\n  // Compute the polynomial at the cell center for the neighbor data that we\n  // \"received\".\n  //\n  // We do this by computing the solution in our entire neighbor, then using\n  // slice_data to get the subset of points that are needed.\n  DirectionMap<Dim, FluxVars> neighbor_data{};\n  DirectionalIdMap<Dim, evolution::dg::subcell::GhostData>\n      reconstruction_ghost_data{};\n\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    FluxVars neighbor_vars(mesh.number_of_grid_points(), 0.0);\n    set_polynomial(&neighbor_vars, neighbor_logical_coords);\n\n    const auto sliced_data = evolution::dg::subcell::slice_data(\n        neighbor_vars, mesh.extents(), number_of_ghost_points,\n        std::unordered_set{direction.opposite()}, 0, {});\n    CAPTURE(number_of_ghost_points);\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    REQUIRE(sliced_data.at(direction.opposite()).size() %\n                FluxVars::number_of_independent_components ==\n            0);\n    neighbor_data[direction].initialize(\n        sliced_data.at(direction.opposite()).size() /\n        FluxVars::number_of_independent_components);\n    std::copy(sliced_data.at(direction.opposite()).begin(),\n              sliced_data.at(direction.opposite()).end(),\n              neighbor_data[direction].data());\n\n    const DirectionalId<Dim> mortar_id{direction, ElementId<Dim>{0}};\n    reconstruction_ghost_data[mortar_id] = evolution::dg::subcell::GhostData{1};\n    reconstruction_ghost_data[mortar_id]\n        .neighbor_ghost_data_for_reconstruction() =\n        DataVector{sliced_data.at(direction.opposite()).size()};\n    std::copy(sliced_data.at(direction.opposite()).begin(),\n              sliced_data.at(direction.opposite()).end(),\n              reconstruction_ghost_data[mortar_id]\n                  .neighbor_ghost_data_for_reconstruction()\n                  .data());\n  }\n\n  std::array<CorrectionVars, Dim> second_order_corrections{};\n  for (size_t i = 0; i < Dim; ++i) {\n    // Compare to analytic solution on the faces.\n    const auto basis = make_array<Dim>(Spectral::Basis::FiniteDifference);\n    auto quadrature = make_array<Dim>(Spectral::Quadrature::CellCentered);\n    auto extents = make_array<Dim>(points_per_dimension);\n    gsl::at(extents, i) = points_per_dimension + 1;\n    gsl::at(quadrature, i) = Spectral::Quadrature::FaceCentered;\n    const Mesh<Dim> face_centered_mesh{extents, basis, quadrature};\n    auto face_logical_coords = logical_coordinates(face_centered_mesh);\n    for (size_t j = 1; j < Dim; ++j) {\n      face_logical_coords.get(j) += 4.0 * static_cast<double>(j);\n    }\n    gsl::at(second_order_corrections, i)\n        .initialize(face_centered_mesh.number_of_grid_points());\n    set_polynomial(make_not_null(&gsl::at(second_order_corrections, i)),\n                   face_logical_coords);\n    // We use n_i F^i in the code, so need to negate to get sign to agree.\n    gsl::at(second_order_corrections, i) *= -1.0;\n  }\n\n  std::array<std::vector<std::uint8_t>, Dim> reconstruction_order_storage{};\n  std::array<gsl::span<std::uint8_t>, Dim> reconstruction_order{};\n  if (correction_order == fd::DerivativeOrder::OneHigherThanRecons or\n      correction_order ==\n          fd::DerivativeOrder::OneHigherThanReconsButFiveToFour) {\n    Index<Dim> recons_extents = mesh.extents();\n    recons_extents[0] += 2;\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(reconstruction_order_storage, i) =\n          std::vector<std::uint8_t>(recons_extents.product(), 5);\n      gsl::at(reconstruction_order, i) =\n          gsl::span(gsl::at(reconstruction_order_storage, i).data(),\n                    gsl::at(reconstruction_order_storage, i).size());\n    }\n  }\n\n  std::optional<std::array<CorrectionVars, Dim>> high_order_corrections{};\n  ::fd::cartesian_high_order_flux_corrections(\n      make_not_null(&high_order_corrections),\n\n      volume_vars, second_order_corrections, correction_order,\n      reconstruction_ghost_data, mesh, number_of_ghost_points,\n      reconstruction_order);\n\n  // Now compute the Cartesian derivative of the high_order_corrections to\n  // verify that it is computed sufficiently accurately.\n  const DataVector inv_jacobian{mesh.number_of_grid_points(), 1.0};\n  CorrectionVars flux_divergence{mesh.number_of_grid_points(), 0.0};\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto& corrections_in_dim =\n        high_order_corrections.has_value()\n            ? gsl::at(high_order_corrections.value(), d)\n            : gsl::at(second_order_corrections, d);\n    // Note: assumes isotropic mesh\n    const double one_over_delta_xi =\n        -1.0 / (logical_coords.get(0)[1] - logical_coords.get(0)[0]);\n    evolution::dg::subcell::add_cartesian_flux_divergence(\n        make_not_null(&get(get<Scalar0>(flux_divergence))), one_over_delta_xi,\n        inv_jacobian, get(get<Scalar0>(corrections_in_dim)), mesh.extents(), d);\n    for (size_t i = 0; i < Dim; ++i) {\n      evolution::dg::subcell::add_cartesian_flux_divergence(\n          make_not_null(&get<Vector0<Dim>>(flux_divergence).get(i)),\n          one_over_delta_xi, inv_jacobian,\n          get<Vector0<Dim>>(corrections_in_dim).get(i), mesh.extents(), d);\n    }\n  }\n\n  // With high-order corrections roundoff can accumulate.\n  Approx custom_approx = Approx::custom().epsilon(5.e-12);\n  CHECK_ITERABLE_CUSTOM_APPROX(get<Scalar0>(flux_divergence),\n                               get<Scalar0>(expected_divergence),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get<Vector0<Dim>>(flux_divergence),\n                               get<Vector0<Dim>>(expected_divergence),\n                               custom_approx);\n\n  // Test assertions\n#ifdef SPECTRE_DEBUG\n  if (correction_order != fd::DerivativeOrder::Two) {\n    std::optional<std::array<CorrectionVars, Dim>>\n        high_order_corrections_assert = make_array<Dim>(CorrectionVars{\n            second_order_corrections[0].number_of_grid_points()});\n    high_order_corrections_assert.value()[0].initialize(\n        second_order_corrections[0].number_of_grid_points() * 2);\n    CHECK_THROWS_WITH(\n        ::fd::cartesian_high_order_flux_corrections(\n            make_not_null(&high_order_corrections_assert), volume_vars,\n            second_order_corrections, correction_order,\n            reconstruction_ghost_data, mesh, number_of_ghost_points),\n        Catch::Matchers::ContainsSubstring(\n            \"The high_order_corrections must all have size\"));\n  }\n  if constexpr (Dim > 1) {\n    auto second_order_corrections_copy = second_order_corrections;\n    second_order_corrections_copy[0].initialize(\n        second_order_corrections_copy[0].number_of_grid_points() * 2);\n    CHECK_THROWS_WITH(\n        ::fd::cartesian_high_order_flux_corrections(\n            make_not_null(&high_order_corrections), volume_vars,\n            second_order_corrections_copy, correction_order,\n            reconstruction_ghost_data, mesh, number_of_ghost_points),\n        Catch::Matchers::ContainsSubstring(\n            \"All second-order boundary corrections must be of the same size\"));\n  }\n#endif  // SPECTRE_DEBUG\n}\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.CartesianHighOrderFluxCorrection\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  using DO = fd::DerivativeOrder;\n  for (const fd::DerivativeOrder correction_order :\n       {DO::Two, DO::Four, DO::Six, DO::Eight, DO::Ten, DO::OneHigherThanRecons,\n        DO::OneHigherThanReconsButFiveToFour}) {\n    test<1>(correction_order);\n    test<2>(correction_order);\n    test<3>(correction_order);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_Minmod.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Exact.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Python.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Roundoff.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Minmod.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test() {\n  const auto recons =\n      [](const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_upper_side_of_face_vars,\n         const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_lower_side_of_face_vars,\n         const gsl::span<const double>& volume_vars,\n         const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n         const Index<Dim>& volume_extents, const size_t number_of_variables) {\n        fd::reconstruction::minmod(reconstructed_upper_side_of_face_vars,\n                                   reconstructed_lower_side_of_face_vars,\n                                   volume_vars, ghost_cell_vars, volume_extents,\n                                   number_of_variables);\n      };\n  const auto recons_neighbor_data =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper, fd::reconstruction::detail::MinmodReconstructor>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower, fd::reconstruction::detail::MinmodReconstructor>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct);\n        }\n      };\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(1, 4, 3, recons, recons_neighbor_data);\n  TestHelpers::fd::reconstruction::test_with_python(\n      Index<Dim>{4}, 3, \"Minmod\", \"test_minmod\", recons, recons_neighbor_data);\n  if constexpr (Dim == 1) {\n    TestHelpers::fd::reconstruction::test_positivity_with_roundoff(3, recons);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.Minmod\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"NumericalAlgorithms/FiniteDifference/\");\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_MonotonicityPreserving5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Exact.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Python.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonicityPreserving5.hpp\"\n\nnamespace {\n\nconst double alpha = 4.0;\nconst double epsilon = 1e-10;\n\ntemplate <size_t Dim>\nvoid test() {\n  const auto recons =\n      [](const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_upper_side_of_face_vars,\n         const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_lower_side_of_face_vars,\n         const gsl::span<const double>& volume_vars,\n         const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n         const Index<Dim>& volume_extents, const size_t number_of_variables) {\n        fd::reconstruction::monotonicity_preserving_5(\n            reconstructed_upper_side_of_face_vars,\n            reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n            volume_extents, number_of_variables, alpha, epsilon);\n      };\n  const auto recons_neighbor_data =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper,\n              fd::reconstruction::detail::MonotonicityPreserving5Reconstructor>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct, alpha, epsilon);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower,\n              fd::reconstruction::detail::MonotonicityPreserving5Reconstructor>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct, alpha, epsilon);\n        }\n      };\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(4, 5, 5, recons, recons_neighbor_data);\n  TestHelpers::fd::reconstruction::test_with_python(\n      Index<Dim>{5}, 5, \"MonotonicityPreserving5\", \"test_mp5\", recons,\n      recons_neighbor_data);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.MonotonicityPreserving5\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"NumericalAlgorithms/FiniteDifference/\");\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_MonotonisedCentral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Exact.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Python.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Roundoff.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nvoid test() {\n  const auto recons =\n      [](const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_upper_side_of_face_vars,\n         const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_lower_side_of_face_vars,\n         const gsl::span<const double>& volume_vars,\n         const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n         const Index<Dim>& volume_extents, const size_t number_of_variables) {\n        fd::reconstruction::monotonised_central(\n            reconstructed_upper_side_of_face_vars,\n            reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n            volume_extents, number_of_variables);\n      };\n  const auto recons_neighbor_data =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper,\n              fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower,\n              fd::reconstruction::detail::MonotonisedCentralReconstructor>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct);\n        }\n      };\n  const auto recons_neighbor_data_interior_cell =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper,\n              fd::reconstruction::detail::MonotonisedCentralReconstructor,\n              false>(face_data, volume_data, neighbor_data, volume_extents,\n                     ghost_data_extents, direction_to_reconstruct);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower,\n              fd::reconstruction::detail::MonotonisedCentralReconstructor,\n              false>(face_data, volume_data, neighbor_data, volume_extents,\n                     ghost_data_extents, direction_to_reconstruct);\n        }\n      };\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(1, 4, 3, recons, recons_neighbor_data);\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(1, 4, 3, recons, recons_neighbor_data_interior_cell);\n  TestHelpers::fd::reconstruction::test_with_python(\n      Index<Dim>{4}, 3, \"MonotonisedCentral\", \"test_monotonised_central\",\n      recons, recons_neighbor_data);\n  if constexpr (Dim == 1) {\n    TestHelpers::fd::reconstruction::test_positivity_with_roundoff(3, recons);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.MonotonisedCentral\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"NumericalAlgorithms/FiniteDifference/\");\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_NeighborDataAsVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DgSubcell/GhostData.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/NeighborDataAsVariables.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct Scalar : db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim, Frame::Inertial>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nvoid test() {\n  using Vars = Variables<tmpl::list<Tags::Scalar, Tags::Vector<Dim>>>;\n  const size_t ghost_zone_size = 2;\n  const Mesh<Dim> subcell_mesh{5, Spectral::Basis::FiniteDifference,\n                               Spectral::Quadrature::CellCentered};\n  const size_t neighbor_mesh_size =\n      ghost_zone_size * subcell_mesh.extents().slice_away(0).product();\n  DirectionalIdMap<Dim, evolution::dg::subcell::GhostData> neighbor_data{};\n  for (size_t i = 0; i < Direction<Dim>::all_directions().size(); ++i) {\n    neighbor_data[DirectionalId<Dim>{\n                      gsl::at(Direction<Dim>::all_directions(), i),\n                      ElementId<Dim>{i}}]\n        .neighbor_ghost_data_for_reconstruction() =\n        DataVector{Vars::number_of_independent_components * neighbor_mesh_size,\n                   square(i + 1.0)};\n  }\n  DirectionalIdMap<Dim, Vars> neighbor_data_as_vars{};\n  fd::neighbor_data_as_variables(make_not_null(&neighbor_data_as_vars),\n                                 neighbor_data, ghost_zone_size, subcell_mesh);\n  REQUIRE(neighbor_data_as_vars.size() == neighbor_data.size());\n  for (const auto& [neighbor_id, vars] : neighbor_data_as_vars) {\n    REQUIRE(neighbor_data.find(neighbor_id) != neighbor_data.end());\n    CHECK(neighbor_data.at(neighbor_id)\n              .neighbor_ghost_data_for_reconstruction()\n              .data() == vars.data());\n    CHECK(vars.number_of_grid_points() == neighbor_mesh_size);\n    CHECK(vars.size() ==\n          Vars::number_of_independent_components * neighbor_mesh_size);\n  }\n#ifdef SPECTRE_DEBUG\n  if constexpr (Dim > 1) {\n    auto extents = make_array<Dim>(subcell_mesh.extents(0));\n    ++extents[0];\n    Mesh<Dim> non_istropic_mesh{extents, subcell_mesh.basis(),\n                                subcell_mesh.quadrature()};\n    CHECK_THROWS_WITH(fd::neighbor_data_as_variables(\n                          make_not_null(&neighbor_data_as_vars), neighbor_data,\n                          ghost_zone_size, non_istropic_mesh),\n                      Catch::Matchers::ContainsSubstring(\n                          \"subcell_mesh must be isotropic but got\"));\n  }\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.NeighborDataAsVariables\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_NonUniform1D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <deque>\n\n#include \"NumericalAlgorithms/FiniteDifference/NonUniform1D.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <size_t StencilSize>\nvoid test_impl() {\n  std::deque<double> times{};\n  times.emplace_front(4.0);\n  times.emplace_back(3.0);\n  if constexpr (StencilSize > 2) {\n    times.emplace_back(2.5);\n    if constexpr (StencilSize > 3) {\n      times.emplace_back(0.5);\n    }\n  }\n\n  const auto weights = fd::non_uniform_1d_weights<StencilSize>(times);\n\n  std::array<double, StencilSize> zeroth_weights{};\n  gsl::at(zeroth_weights, 0) = 1.0;\n  // All stencil sizes have this as the zeroth values\n  CHECK(gsl::at(weights, 0) == zeroth_weights);\n\n  if constexpr (StencilSize == 2) {\n    CHECK_ITERABLE_APPROX(gsl::at(weights, 1), (std::array{1.0, -1.0}));\n  } else if constexpr (StencilSize == 3) {\n    CHECK_ITERABLE_APPROX(gsl::at(weights, 1),\n                          (std::array{5.0 / 3.0, -3.0, 4.0 / 3.0}));\n    CHECK_ITERABLE_APPROX(gsl::at(weights, 2),\n                          (std::array{4.0 / 3.0, -4.0, 8.0 / 3.0}));\n  } else if constexpr (StencilSize == 4) {\n    CHECK_ITERABLE_APPROX(\n        gsl::at(weights, 1),\n        (std::array{41.0 / 21.0, -21.0 / 5.0, 7.0 / 3.0, -3.0 / 35.0}));\n    CHECK_ITERABLE_APPROX(gsl::at(weights, 2),\n                          (std::array{16.0 / 7.0, -8.0, 6.0, -2.0 / 7.0}));\n    CHECK_ITERABLE_APPROX(\n        gsl::at(weights, 3),\n        (std::array{8.0 / 7.0, -24.0 / 5.0, 4.0, -12.0 / 35.0}));\n  }\n}\n\ntemplate <size_t StencilSize>\nvoid test_errors() {\n#ifdef SPECTRE_DEBUG\n  std::deque<double> times{};\n  times.emplace_front(1.0);\n\n  CHECK_THROWS_WITH(\n      fd::non_uniform_1d_weights<StencilSize>(times),\n      Catch::Matchers::ContainsSubstring(\"The size of the times passed in\"));\n\n  times.emplace_back(2.0);\n  if constexpr (StencilSize > 2) {\n    times.emplace_back(0.5);\n    if constexpr (StencilSize > 3) {\n      times.emplace_back(0.3);\n    }\n  }\n\n  CHECK_THROWS_WITH(fd::non_uniform_1d_weights<StencilSize>(times),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Times must be monotonically decreasing\"));\n#endif\n}\n\ntemplate <size_t StencilSize>\nvoid test() {\n  test_impl<StencilSize>();\n  test_errors<StencilSize>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.NonUniform1D\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test<2>();\n  test<3>();\n  test<4>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_PartialDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const gsl::not_null<std::mt19937*> generator,\n          const gsl::not_null<std::uniform_real_distribution<>*> dist,\n          const size_t points_per_dimension, const size_t fd_order) {\n  CAPTURE(points_per_dimension);\n  CAPTURE(fd_order);\n  CAPTURE(Dim);\n  const size_t max_degree = fd_order - 1;\n  const size_t stencil_width = fd_order + 1;\n  const size_t number_of_vars = 2;  // arbitrary, 2 is \"cheap but not trivial\"\n\n  const Mesh<Dim> mesh{points_per_dimension, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 1; i < Dim; ++i) {\n    logical_coords.get(i) += 4.0 * i;\n  }\n\n  // Compute polynomial on cell centers in FD cluster of points\n  const auto set_polynomial = [max_degree](\n                                  const gsl::not_null<DataVector*> var1_ptr,\n                                  const gsl::not_null<DataVector*> var2_ptr,\n                                  const auto& local_logical_coords) {\n    *var1_ptr = 0.0;\n    *var2_ptr = 100.0;  // some constant offset to distinguish the var values\n    for (size_t degree = 1; degree <= max_degree; ++degree) {\n      for (size_t i = 0; i < Dim; ++i) {\n        *var1_ptr += pow(local_logical_coords.get(i), degree);\n        *var2_ptr += pow(local_logical_coords.get(i), degree);\n      }\n    }\n  };\n  const auto set_polynomial_derivative =\n      [max_degree](const gsl::not_null<std::array<DataVector, Dim>*> d_var1_ptr,\n                   const gsl::not_null<std::array<DataVector, Dim>*> d_var2_ptr,\n                   const auto& local_logical_coords) {\n        for (size_t deriv_dim = 0; deriv_dim < Dim; ++deriv_dim) {\n          gsl::at(*d_var1_ptr, deriv_dim) = 0.0;\n          // constant deriv is zero\n          gsl::at(*d_var2_ptr, deriv_dim) = 0.0;\n          for (size_t degree = 1; degree <= max_degree; ++degree) {\n            gsl::at(*d_var1_ptr, deriv_dim) +=\n                degree * pow(local_logical_coords.get(deriv_dim), degree - 1);\n            gsl::at(*d_var2_ptr, deriv_dim) +=\n                degree * pow(local_logical_coords.get(deriv_dim), degree - 1);\n          }\n        }\n      };\n\n  DataVector volume_vars{mesh.number_of_grid_points() * number_of_vars, 0.0};\n  DataVector var1(volume_vars.data(), mesh.number_of_grid_points());\n  DataVector var2(volume_vars.data() + mesh.number_of_grid_points(),  // NOLINT\n                  mesh.number_of_grid_points());\n  set_polynomial(&var1, &var2, logical_coords);\n\n  DataVector expected_deriv{Dim * volume_vars.size()};\n  std::array<DataVector, Dim> expected_d_var1{};\n  std::array<DataVector, Dim> expected_d_var2{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(expected_d_var1, i)\n        .set_data_ref(&expected_deriv[i * volume_vars.size()],\n                      mesh.number_of_grid_points());\n    gsl::at(expected_d_var2, i)\n        .set_data_ref(&expected_deriv[i * volume_vars.size() +\n                                      mesh.number_of_grid_points()],\n                      mesh.number_of_grid_points());\n  }\n  set_polynomial_derivative(&expected_d_var1, &expected_d_var2, logical_coords);\n\n  // Compute the polynomial at the cell center for the neighbor data that we\n  // \"received\".\n  //\n  // We do this by computing the solution in our entire neighbor, then using\n  // slice_data to get the subset of points that are needed.\n  DirectionMap<Dim, DataVector> neighbor_data{};\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    DataVector neighbor_vars{mesh.number_of_grid_points() * number_of_vars,\n                             0.0};\n    DataVector neighbor_var1(neighbor_vars.data(),\n                             mesh.number_of_grid_points());\n    DataVector neighbor_var2(\n        neighbor_vars.data() + mesh.number_of_grid_points(),  // NOLINT\n        mesh.number_of_grid_points());\n    set_polynomial(&neighbor_var1, &neighbor_var2, neighbor_logical_coords);\n\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars.data(), neighbor_vars.size()),\n        mesh.extents(), (stencil_width - 1) / 2 + 1,\n        std::unordered_set{direction.opposite()}, 0, {});\n    CAPTURE((stencil_width - 1) / 2 + 1);\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    neighbor_data[direction] = sliced_data.at(direction.opposite());\n    REQUIRE(neighbor_data.at(direction).size() ==\n            number_of_vars * (fd_order / 2 + 1) *\n                mesh.slice_away(0).number_of_grid_points());\n  }\n\n  // Note: reconstructed_num_pts assumes isotropic extents\n  DataVector logical_derivative_buffer{volume_vars.size() * Dim};\n  std::array<gsl::span<double>, Dim> logical_derivative_view{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(logical_derivative_view, i) = gsl::make_span(\n        &logical_derivative_buffer[i * volume_vars.size()], volume_vars.size());\n  }\n\n  DirectionMap<Dim, gsl::span<const double>> ghost_cell_vars{};\n  for (const auto& [direction, data] : neighbor_data) {\n    ghost_cell_vars[direction] = gsl::make_span(data.data(), data.size());\n  }\n\n  ::fd::logical_partial_derivatives(\n      make_not_null(&logical_derivative_view),\n      gsl::make_span(volume_vars.data(), volume_vars.size()), ghost_cell_vars,\n      mesh, number_of_vars, fd_order);\n\n  // Scale to volume_vars since that sets the subtraction error threshold.\n  Approx custom_approx = Approx::custom().epsilon(1.0e-14).scale(\n      *std::max_element(volume_vars.begin(), volume_vars.end()));\n\n  for (size_t i = 0; i < Dim; ++i) {\n    CAPTURE(i);\n    {\n      CAPTURE(var1);\n      const DataVector fd_d_var1(&gsl::at(logical_derivative_view, i)[0],\n                                 mesh.number_of_grid_points());\n      CHECK_ITERABLE_CUSTOM_APPROX(fd_d_var1, gsl::at(expected_d_var1, i),\n                                   custom_approx);\n    }\n    {\n      CAPTURE(var2);\n      const DataVector fd_d_var2(\n          &gsl::at(logical_derivative_view, i)[mesh.number_of_grid_points()],\n          mesh.number_of_grid_points());\n      CHECK_ITERABLE_CUSTOM_APPROX(fd_d_var2, gsl::at(expected_d_var2, i),\n                                   custom_approx);\n    }\n  }\n\n  // Test partial derivative with random Jacobian. We know we calculated the\n  // logical partial derivatives correctly, just need to make sure we forward to\n  // the other functions correctly.\n  const auto inverse_jacobian = make_with_random_values<\n      InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>>(\n      generator, dist, DataVector{mesh.number_of_grid_points()});\n\n  using derivative_tags = tmpl::list<Tags::TempScalar<0, DataVector>,\n                                     Tags::TempScalar<1, DataVector>>;\n  Variables<db::wrap_tags_in<Tags::deriv, derivative_tags, tmpl::size_t<Dim>,\n                             Frame::Inertial>>\n      partial_derivatives{mesh.number_of_grid_points()};\n  ::fd::partial_derivatives<derivative_tags>(\n      make_not_null(&partial_derivatives),\n      gsl::make_span(volume_vars.data(), volume_vars.size()), ghost_cell_vars,\n      mesh, number_of_vars, fd_order, inverse_jacobian);\n\n  std::array<const double*, Dim> expected_logical_derivs_ptrs{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(expected_logical_derivs_ptrs, i) =\n        gsl::at(expected_d_var1, i).data();\n  }\n  Variables<db::wrap_tags_in<Tags::deriv, derivative_tags, tmpl::size_t<Dim>,\n                             Frame::Inertial>>\n      expected_partial_derivatives{mesh.number_of_grid_points()};\n  ::partial_derivatives_detail::partial_derivatives_impl(\n      make_not_null(&expected_partial_derivatives),\n      expected_logical_derivs_ptrs,\n      Variables<derivative_tags>::number_of_independent_components,\n      inverse_jacobian);\n\n  using d_var1_tag = Tags::deriv<Tags::TempScalar<0, DataVector>,\n                                 tmpl::size_t<Dim>, Frame::Inertial>;\n  using d_var2_tag = Tags::deriv<Tags::TempScalar<1, DataVector>,\n                                 tmpl::size_t<Dim>, Frame::Inertial>;\n  CHECK_ITERABLE_CUSTOM_APPROX(get<d_var1_tag>(partial_derivatives),\n                               get<d_var1_tag>(expected_partial_derivatives),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get<d_var2_tag>(partial_derivatives),\n                               get<d_var2_tag>(expected_partial_derivatives),\n                               custom_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.PartialDerivatives\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist{-1.0, 1.0};\n  for (const size_t fd_order : {2_st, 4_st, 6_st, 8_st}) {\n    test<1>(make_not_null(&generator), make_not_null(&dist), fd_order + 2,\n            fd_order);\n    test<2>(make_not_null(&generator), make_not_null(&dist), fd_order + 2,\n            fd_order);\n    test<3>(make_not_null(&generator), make_not_null(&dist), fd_order + 2,\n            fd_order);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_PositivityPreservingAdaptiveOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Exact.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Python.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Minmod.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/PositivityPreservingAdaptiveOrder.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace fd::reconstruction {\nnamespace {\ntemplate <size_t Dim, typename FallbackReconstructor, bool PositivityPreserving,\n          bool Use9thOrder, bool Use7thOrder, bool UseExteriorCell,\n          bool ReturnReconstructionOrder>\nvoid test_function_pointers(const FallbackReconstructorType fallback_recons) {\n  const auto function_ptrs = fd::reconstruction::\n      positivity_preserving_adaptive_order_function_pointers<Dim, false>(\n          PositivityPreserving, Use9thOrder, Use7thOrder, fallback_recons);\n  CHECK(get<0>(function_ptrs) ==\n        static_cast<detail::ppao_recons_type<Dim, false>>(\n            &positivity_preserving_adaptive_order<\n                FallbackReconstructor, PositivityPreserving, Use9thOrder,\n                Use7thOrder, Dim>));\n  using function_type =\n      void (*)(gsl::not_null<DataVector*>, const DataVector&, const DataVector&,\n               const Index<Dim>&, const Index<Dim>&, const Direction<Dim>&,\n               const double&, const double&, const double&);\n  CHECK(get<1>(function_ptrs) ==\n        static_cast<function_type>(\n            &reconstruct_neighbor<\n                Side::Lower,\n                ::fd::reconstruction::detail::\n                    PositivityPreservingAdaptiveOrderReconstructor<\n                        FallbackReconstructor, PositivityPreserving,\n                        Use9thOrder, Use7thOrder>,\n                UseExteriorCell, Dim>));\n  CHECK(get<2>(function_ptrs) ==\n        static_cast<function_type>(\n            &reconstruct_neighbor<\n                Side::Upper,\n                ::fd::reconstruction::detail::\n                    PositivityPreservingAdaptiveOrderReconstructor<\n                        FallbackReconstructor, PositivityPreserving,\n                        Use9thOrder, Use7thOrder>,\n                UseExteriorCell, Dim>));\n}\n\ntemplate <size_t Dim, class FallbackReconstructor, bool PositivityPreserving,\n          bool Use9thOrder, bool Use7thOrder, bool ReturnReconstructionOrder>\nvoid test_impl(const FallbackReconstructorType fallback_recons) {\n  CAPTURE(PositivityPreserving);\n  CAPTURE(Use9thOrder);\n  CAPTURE(Use7thOrder);\n  CAPTURE(ReturnReconstructionOrder);\n  CAPTURE(fallback_recons);\n  using Recons = fd::reconstruction::detail::\n      PositivityPreservingAdaptiveOrderReconstructor<FallbackReconstructor,\n                                                     PositivityPreserving,\n                                                     Use9thOrder, Use7thOrder>;\n\n  const size_t num_points = Recons::stencil_width();\n\n  std::optional<std::array<std::vector<std::uint8_t>, Dim>>\n      reconstruction_order_storage{};\n  std::optional<std::array<gsl::span<std::uint8_t>, Dim>>\n      reconstruction_order{};\n\n  // Non-const because we want different values if we are checking exact\n  // reconstruction or matching to python\n  double four_to_the_alpha_5 = pow(4.0, 1.0);\n  double six_to_the_alpha_7 = pow(6.0, 1.0);\n  double eight_to_the_alpha_9 = pow(8.0, 1.0);\n\n  const auto recons =\n      [&four_to_the_alpha_5, &six_to_the_alpha_7, &eight_to_the_alpha_9,\n       &reconstruction_order, &reconstruction_order_storage](\n          const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n              reconstructed_upper_side_of_face_vars,\n          const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n              reconstructed_lower_side_of_face_vars,\n          const gsl::span<const double>& volume_vars,\n          const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n          const Index<Dim>& volume_extents, const size_t number_of_variables) {\n        if constexpr (ReturnReconstructionOrder) {\n          reconstruction_order_storage.emplace();\n          reconstruction_order.emplace();\n          for (size_t i = 0; i < Dim; ++i) {\n            auto order_extents = volume_extents;\n            order_extents[i] += 2;\n            gsl::at(reconstruction_order_storage.value(), i)\n                .resize(order_extents.product());\n            // Ensure we have reset the values to max so the min calls are fine.\n            std::fill_n(\n                gsl::at(reconstruction_order_storage.value(), i).begin(),\n                order_extents.product(),\n                std::numeric_limits<std::uint8_t>::max());\n            gsl::at(reconstruction_order.value(), i) = gsl::span<std::uint8_t>{\n                gsl::at(reconstruction_order_storage.value(), i).data(),\n                gsl::at(reconstruction_order_storage.value(), i).size()};\n          }\n          fd::reconstruction::positivity_preserving_adaptive_order<\n              FallbackReconstructor, PositivityPreserving, Use9thOrder,\n              Use7thOrder>(reconstructed_upper_side_of_face_vars,\n                           reconstructed_lower_side_of_face_vars,\n                           make_not_null(&reconstruction_order), volume_vars,\n                           ghost_cell_vars, volume_extents, number_of_variables,\n                           four_to_the_alpha_5, six_to_the_alpha_7,\n                           eight_to_the_alpha_9);\n        } else {\n          (void)reconstruction_order, (void)reconstruction_order_storage;\n          fd::reconstruction::positivity_preserving_adaptive_order<\n              FallbackReconstructor, PositivityPreserving, Use9thOrder,\n              Use7thOrder>(reconstructed_upper_side_of_face_vars,\n                           reconstructed_lower_side_of_face_vars, volume_vars,\n                           ghost_cell_vars, volume_extents, number_of_variables,\n                           four_to_the_alpha_5, six_to_the_alpha_7,\n                           eight_to_the_alpha_9);\n        }\n      };\n  const auto recons_neighbor_data =\n      [&four_to_the_alpha_5, &six_to_the_alpha_7, &eight_to_the_alpha_9](\n          const gsl::not_null<DataVector*> face_data,\n          const DataVector& volume_data, const DataVector& neighbor_data,\n          const Index<Dim>& volume_extents,\n          const Index<Dim>& ghost_data_extents,\n          const Direction<Dim>& direction_to_reconstruct) {\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<Side::Upper, Recons>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct, four_to_the_alpha_5,\n              six_to_the_alpha_7, eight_to_the_alpha_9);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<Side::Lower, Recons>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct, four_to_the_alpha_5,\n              six_to_the_alpha_7, eight_to_the_alpha_9);\n        }\n      };\n\n  if constexpr (not PositivityPreserving) {\n    // Since the solution is negative, doing positivity preservation drops the\n    // order.\n    TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n        Dim>(Recons::stencil_width() - 1, num_points, Recons::stencil_width(),\n             recons, recons_neighbor_data);\n    const auto check_recons_order = [&reconstruction_order_storage]() {\n      if (not reconstruction_order_storage.has_value()) {\n        return;\n      }\n      for (size_t d = 0; d < Dim; ++d) {\n        for (size_t i = 0;\n             i < gsl::at(reconstruction_order_storage.value(), d).size(); ++i) {\n          CHECK(static_cast<int>(\n                    gsl::at(reconstruction_order_storage.value(), d)[i]) ==\n                static_cast<int>(Use9thOrder ? 9 : (Use7thOrder ? 7 : 5)));\n        }\n      }\n    };\n    check_recons_order();\n\n    const auto recons_neighbor_data_interior_cell =\n        [&four_to_the_alpha_5, &six_to_the_alpha_7, &eight_to_the_alpha_9](\n            const gsl::not_null<DataVector*> face_data,\n            const DataVector& volume_data, const DataVector& neighbor_data,\n            const Index<Dim>& volume_extents,\n            const Index<Dim>& ghost_data_extents,\n            const Direction<Dim>& direction_to_reconstruct) {\n          if (direction_to_reconstruct.side() == Side::Upper) {\n            fd::reconstruction::reconstruct_neighbor<Side::Upper, Recons,\n                                                     false>(\n                face_data, volume_data, neighbor_data, volume_extents,\n                ghost_data_extents, direction_to_reconstruct,\n                four_to_the_alpha_5, six_to_the_alpha_7, eight_to_the_alpha_9);\n          }\n          if (direction_to_reconstruct.side() == Side::Lower) {\n            fd::reconstruction::reconstruct_neighbor<Side::Lower, Recons,\n                                                     false>(\n                face_data, volume_data, neighbor_data, volume_extents,\n                ghost_data_extents, direction_to_reconstruct,\n                four_to_the_alpha_5, six_to_the_alpha_7, eight_to_the_alpha_9);\n          }\n        };\n    TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n        Dim>(Recons::stencil_width() - 1, num_points, Recons::stencil_width(),\n             recons, recons_neighbor_data_interior_cell);\n    check_recons_order();\n  }\n\n  four_to_the_alpha_5 = pow(4.0, 4.0);\n  six_to_the_alpha_7 = pow(6.0, 4.3);\n  eight_to_the_alpha_9 = pow(8.0, 4.6);\n\n  const std::string ao_name =\n      PositivityPreserving\n          ? std::string{\"test_positivity_preserving_adaptive_order\"}\n          : std::string{\"test_adaptive_order\"};\n\n  if (fallback_recons == FallbackReconstructorType::Minmod) {\n    TestHelpers::fd::reconstruction::test_with_python(\n        Index<Dim>{num_points}, Recons::stencil_width(),\n        \"PositivityPreservingAdaptiveOrder\", ao_name + \"_with_minmod\", recons,\n        recons_neighbor_data, Use9thOrder, Use7thOrder, four_to_the_alpha_5,\n        six_to_the_alpha_7, eight_to_the_alpha_9);\n  } else if (fallback_recons == FallbackReconstructorType::MonotonisedCentral) {\n    TestHelpers::fd::reconstruction::test_with_python(\n        Index<Dim>{num_points}, Recons::stencil_width(),\n        \"PositivityPreservingAdaptiveOrder\", ao_name + \"_with_mc\", recons,\n        recons_neighbor_data, Use9thOrder, Use7thOrder, four_to_the_alpha_5,\n        six_to_the_alpha_7, eight_to_the_alpha_9);\n  } else {\n    ERROR(\"Fallback not yet implemented.\");\n  }\n  for (size_t d = 0; reconstruction_order_storage.has_value() and d < Dim;\n       ++d) {\n    for (size_t i = 0;\n         i < gsl::at(reconstruction_order_storage.value(), d).size(); ++i) {\n      CHECK(static_cast<int>(\n                gsl::at(reconstruction_order_storage.value(), d)[i]) >= 1);\n      CHECK(static_cast<int>(\n                gsl::at(reconstruction_order_storage.value(), d)[i]) <=\n            static_cast<int>(Use9thOrder ? 9 : (Use7thOrder ? 7 : 5)));\n    }\n  }\n\n  test_function_pointers<Dim, FallbackReconstructor, PositivityPreserving,\n                         Use9thOrder, Use7thOrder, true,\n                         ReturnReconstructionOrder>(fallback_recons);\n}\n\ntemplate <class FallbackReconstructor, bool ReturnReconstructionOrder>\nvoid test(const FallbackReconstructorType fallback_recons) {\n  const auto order_helper = [&fallback_recons](auto use_order_9,\n                                               auto use_order_7) {\n    test_impl<1, FallbackReconstructor, true, decltype(use_order_9)::value,\n              decltype(use_order_7)::value, ReturnReconstructionOrder>(\n        fallback_recons);\n    test_impl<2, FallbackReconstructor, true, decltype(use_order_9)::value,\n              decltype(use_order_7)::value, ReturnReconstructionOrder>(\n        fallback_recons);\n    test_impl<3, FallbackReconstructor, true, decltype(use_order_9)::value,\n              decltype(use_order_7)::value, ReturnReconstructionOrder>(\n        fallback_recons);\n\n    test_impl<1, FallbackReconstructor, false, decltype(use_order_9)::value,\n              decltype(use_order_7)::value, ReturnReconstructionOrder>(\n        fallback_recons);\n    test_impl<2, FallbackReconstructor, false, decltype(use_order_9)::value,\n              decltype(use_order_7)::value, ReturnReconstructionOrder>(\n        fallback_recons);\n    test_impl<3, FallbackReconstructor, false, decltype(use_order_9)::value,\n              decltype(use_order_7)::value, ReturnReconstructionOrder>(\n        fallback_recons);\n  };\n\n  order_helper(std::false_type{}, std::false_type{});\n  order_helper(std::false_type{}, std::true_type{});\n  order_helper(std::true_type{}, std::false_type{});\n  order_helper(std::true_type{}, std::true_type{});\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.PositivityPreservingAdaptiveOrder\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"NumericalAlgorithms/FiniteDifference/\");\n\n  test<detail::MinmodReconstructor, true>(FallbackReconstructorType::Minmod);\n  test<detail::MinmodReconstructor, false>(FallbackReconstructorType::Minmod);\n  test<detail::MonotonisedCentralReconstructor, true>(\n      FallbackReconstructorType::MonotonisedCentral);\n  test<detail::MonotonisedCentralReconstructor, false>(\n      FallbackReconstructorType::MonotonisedCentral);\n\n  CHECK_THROWS_WITH(\n      (fd::reconstruction::\n           positivity_preserving_adaptive_order_function_pointers<1, false>(\n               true, false, false, FallbackReconstructorType::None)),\n      Catch::Matchers::ContainsSubstring(\n          \"Can't have None as the low-order reconstructor in \"\n          \"positivity_preserving_adaptive_order.\"));\n}\n\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_SecondPartialDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_set>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Evolution/DgSubcell/SliceData.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/SecondPartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const size_t points_per_dimension, const size_t fd_order) {\n  // The following code assumes fd_order == 4 and Dim == 3\n  ASSERT((Dim == 3) and (fd_order == 4),\n         \"Only 3 spatial dims and 4th-order fd is supported right now!\");\n  CAPTURE(points_per_dimension);\n  CAPTURE(fd_order);\n  CAPTURE(Dim);\n\n  const size_t max_degree = fd_order - 1;\n  const size_t number_of_vars = 2;\n\n  const Mesh<Dim> mesh{points_per_dimension, Spectral::Basis::FiniteDifference,\n                       Spectral::Quadrature::CellCentered};\n  auto logical_coords = logical_coordinates(mesh);\n  // Make the logical coordinates different in each direction\n  for (size_t i = 0; i < Dim; ++i) {\n    logical_coords.get(i) += 4.0 * static_cast<double>(i + 1);\n  }\n\n  // Set up a polynomial of max_degree on cell centers in FD cluster of points\n  const auto set_polynomial = [max_degree](\n                                  const gsl::not_null<DataVector*> var1_ptr,\n                                  const gsl::not_null<DataVector*> var2_ptr,\n                                  const auto& local_logical_coords) {\n    *var1_ptr = 0.0;\n    *var2_ptr = 100.0;  // some constant offset to distinguish the var values\n    for (size_t degree_x = 0; degree_x <= max_degree; ++degree_x) {\n      for (size_t degree_y = 0; degree_y <= max_degree; ++degree_y) {\n        for (size_t degree_z = 0; degree_z <= max_degree; ++degree_z) {\n          if (degree_x + degree_y + degree_z <= max_degree) {\n            *var1_ptr += pow(local_logical_coords.get(0), degree_x) *\n                         pow(local_logical_coords.get(1), degree_y) *\n                         pow(local_logical_coords.get(2), degree_z);\n            *var2_ptr += pow(local_logical_coords.get(0), degree_x) *\n                         pow(local_logical_coords.get(1), degree_y) *\n                         pow(local_logical_coords.get(2), degree_z);\n          }\n        }\n      }\n    }\n  };\n\n  // Compute the expected pure second derivatives of the polynomial\n  const auto set_polynomial_pure_second_derivative =\n      [](const gsl::not_null<std::array<DataVector, Dim>*>\n             pure_second_d_var1_ptr,\n         const gsl::not_null<std::array<DataVector, Dim>*>\n             pure_second_d_var2_ptr,\n         const auto& local_logical_coords) {\n        for (size_t deriv_dim = 0; deriv_dim < Dim; ++deriv_dim) {\n          gsl::at(*pure_second_d_var1_ptr, deriv_dim) =\n              2 + 2 * local_logical_coords.get(0) +\n              2 * local_logical_coords.get(1) + 2 * local_logical_coords.get(2);\n          gsl::at(*pure_second_d_var1_ptr, deriv_dim) +=\n              4 * local_logical_coords.get(deriv_dim);\n\n          gsl::at(*pure_second_d_var2_ptr, deriv_dim) =\n              2 + 2 * local_logical_coords.get(0) +\n              2 * local_logical_coords.get(1) + 2 * local_logical_coords.get(2);\n          gsl::at(*pure_second_d_var2_ptr, deriv_dim) +=\n              4 * local_logical_coords.get(deriv_dim);\n        }\n      };\n\n  // Compute the expected mixed second derivatives of the polynomial\n  // deriv_dim = 0 -- xy derivative; 1 -- yz derivative; 2 -- xz derivative\n  const auto set_polynomial_mixed_second_derivative =\n      [](const gsl::not_null<std::array<DataVector, Dim>*>\n             mixed_second_d_var1_ptr,\n         const gsl::not_null<std::array<DataVector, Dim>*>\n             mixed_second_d_var2_ptr,\n         const auto& local_logical_coords) {\n        for (size_t deriv_dim = 0; deriv_dim < Dim; ++deriv_dim) {\n          gsl::at(*mixed_second_d_var1_ptr, deriv_dim) =\n              1 + local_logical_coords.get(0) + local_logical_coords.get(1) +\n              local_logical_coords.get(2);\n          gsl::at(*mixed_second_d_var1_ptr, deriv_dim) +=\n              local_logical_coords.get(deriv_dim % Dim) +\n              local_logical_coords.get((deriv_dim + 1) % Dim);\n          gsl::at(*mixed_second_d_var2_ptr, deriv_dim) =\n              1 + local_logical_coords.get(0) + local_logical_coords.get(1) +\n              local_logical_coords.get(2);\n          gsl::at(*mixed_second_d_var2_ptr, deriv_dim) +=\n              local_logical_coords.get(deriv_dim % Dim) +\n              local_logical_coords.get((deriv_dim + 1) % Dim);\n        }\n      };\n\n  DataVector volume_vars{mesh.number_of_grid_points() * number_of_vars, 0.0};\n  DataVector var1(volume_vars.data(), mesh.number_of_grid_points());\n  DataVector var2(volume_vars.data() + mesh.number_of_grid_points(),\n                  mesh.number_of_grid_points());\n  set_polynomial(&var1, &var2, logical_coords);\n\n  DataVector expected_pure_second_deriv{Dim * volume_vars.size()};\n  std::array<DataVector, Dim> expected_pure_second_d_var1{};\n  std::array<DataVector, Dim> expected_pure_second_d_var2{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(expected_pure_second_d_var1, i)\n        .set_data_ref(&expected_pure_second_deriv[i * volume_vars.size()],\n                      mesh.number_of_grid_points());\n    gsl::at(expected_pure_second_d_var2, i)\n        .set_data_ref(&expected_pure_second_deriv[i * volume_vars.size() +\n                                                  mesh.number_of_grid_points()],\n                      mesh.number_of_grid_points());\n  }\n  set_polynomial_pure_second_derivative(&expected_pure_second_d_var1,\n                                        &expected_pure_second_d_var2,\n                                        logical_coords);\n\n  DataVector expected_mixed_second_deriv{Dim * volume_vars.size()};\n  std::array<DataVector, Dim> expected_mixed_second_d_var1{};\n  std::array<DataVector, Dim> expected_mixed_second_d_var2{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(expected_mixed_second_d_var1, i)\n        .set_data_ref(&expected_mixed_second_deriv[i * volume_vars.size()],\n                      mesh.number_of_grid_points());\n    gsl::at(expected_mixed_second_d_var2, i)\n        .set_data_ref(\n            &expected_mixed_second_deriv[i * volume_vars.size() +\n                                         mesh.number_of_grid_points()],\n            mesh.number_of_grid_points());\n  }\n  set_polynomial_mixed_second_derivative(&expected_mixed_second_d_var1,\n                                         &expected_mixed_second_d_var2,\n                                         logical_coords);\n\n  // Compute the polynomial at the cell center for the neighbor data that we\n  // \"received\".\n  //\n  // We do this by computing the solution in our entire neighbor, then using\n  // slice_data to get the subset of points that are needed.\n  DirectionMap<Dim, DataVector> neighbor_data{};\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    DataVector neighbor_vars{mesh.number_of_grid_points() * number_of_vars,\n                             0.0};\n    DataVector neighbor_var1(neighbor_vars.data(),\n                             mesh.number_of_grid_points());\n    DataVector neighbor_var2(\n        neighbor_vars.data() + mesh.number_of_grid_points(),\n        mesh.number_of_grid_points());\n    set_polynomial(&neighbor_var1, &neighbor_var2, neighbor_logical_coords);\n\n    // We need two ghost points for 4th order\n    const size_t num_of_ghost_pts = 2;\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars.data(), neighbor_vars.size()),\n        mesh.extents(), num_of_ghost_pts,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    neighbor_data[direction] = sliced_data.at(direction.opposite());\n    REQUIRE(neighbor_data.at(direction).size() ==\n            number_of_vars * num_of_ghost_pts *\n                mesh.slice_away(0).number_of_grid_points());\n  }\n\n  // Note: reconstructed_num_pts assumes isotropic extents\n  DataVector pure_second_logical_derivative_buffer{volume_vars.size() * Dim};\n  std::array<gsl::span<double>, Dim> pure_second_logical_derivative_view{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(pure_second_logical_derivative_view, i) = gsl::make_span(\n        &pure_second_logical_derivative_buffer[i * volume_vars.size()],\n        volume_vars.size());\n  }\n\n  DataVector mixed_second_logical_derivative_buffer{volume_vars.size() * Dim};\n  std::array<gsl::span<double>, Dim> mixed_second_logical_derivative_view{};\n  for (size_t i = 0; i < Dim; ++i) {\n    gsl::at(mixed_second_logical_derivative_view, i) = gsl::make_span(\n        &mixed_second_logical_derivative_buffer[i * volume_vars.size()],\n        volume_vars.size());\n  }\n\n  DirectionMap<Dim, gsl::span<const double>> ghost_cell_vars{};\n  for (const auto& [direction, data] : neighbor_data) {\n    ghost_cell_vars[direction] = gsl::make_span(data.data(), data.size());\n  }\n\n  ::fd::second_logical_partial_derivatives(\n      make_not_null(&pure_second_logical_derivative_view),\n      make_not_null(&mixed_second_logical_derivative_view),\n      gsl::make_span(volume_vars.data(), volume_vars.size()), ghost_cell_vars,\n      mesh, number_of_vars, fd_order);\n\n  // Scale to volume_vars since that sets the subtraction error threshold.\n  Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(\n      *std::max_element(volume_vars.begin(), volume_vars.end()));\n\n  for (size_t i = 0; i < Dim; ++i) {\n    CAPTURE(i);\n    {\n      CAPTURE(logical_coords.get(0));\n      CAPTURE(var1);\n      const DataVector fd_pure_second_d_var1(\n          &gsl::at(pure_second_logical_derivative_view, i)[0],\n          mesh.number_of_grid_points());\n      CHECK_ITERABLE_CUSTOM_APPROX(fd_pure_second_d_var1,\n                                   gsl::at(expected_pure_second_d_var1, i),\n                                   custom_approx);\n    }\n    {\n      CAPTURE(var2);\n      const DataVector fd_pure_second_d_var2(\n          &gsl::at(pure_second_logical_derivative_view,\n                   i)[mesh.number_of_grid_points()],\n          mesh.number_of_grid_points());\n      CHECK_ITERABLE_CUSTOM_APPROX(fd_pure_second_d_var2,\n                                   gsl::at(expected_pure_second_d_var2, i),\n                                   custom_approx);\n    }\n    {\n      CAPTURE(var1);\n      const DataVector fd_mixed_second_d_var1(\n          &gsl::at(mixed_second_logical_derivative_view, i)[0],\n          mesh.number_of_grid_points());\n      CHECK_ITERABLE_CUSTOM_APPROX(fd_mixed_second_d_var1,\n                                   gsl::at(expected_mixed_second_d_var1, i),\n                                   custom_approx);\n    }\n    {\n      CAPTURE(var2);\n      const DataVector fd_mixed_second_d_var2(\n          &gsl::at(mixed_second_logical_derivative_view,\n                   i)[mesh.number_of_grid_points()],\n          mesh.number_of_grid_points());\n      CHECK_ITERABLE_CUSTOM_APPROX(fd_mixed_second_d_var2,\n                                   gsl::at(expected_mixed_second_d_var2, i),\n                                   custom_approx);\n    }\n  }\n\n  // Test second partial derivatives with Jacobian.\n  // We use inertial coords x = \\xi^2, y = \\eta, and z = \\xi + \\zeta\n  // We also change the first var to a tensor for generality\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian(mesh.number_of_grid_points(), 0.0);\n  inverse_jacobian.get(0, 0) = 0.5 / logical_coords.get(0);\n  inverse_jacobian.get(1, 1) = 1.0;\n  inverse_jacobian.get(2, 0) = -0.5 / logical_coords.get(0);\n  inverse_jacobian.get(2, 2) = 1.0;\n\n  InverseHessian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inverse_hessian(mesh.number_of_grid_points(), 0.0);\n  inverse_hessian.get(0, 0, 0) = -0.25 / pow<3>(logical_coords.get(0));\n  inverse_hessian.get(2, 0, 0) = 0.25 / pow<3>(logical_coords.get(0));\n\n  using derivative_tags =\n      tmpl::list<Tags::Tempi<0, Dim, Frame::Inertial, DataVector>,\n                 Tags::TempScalar<1, DataVector>>;\n\n  Variables<db::wrap_tags_in<Tags::second_deriv, derivative_tags,\n                             tmpl::size_t<Dim>, Frame::Inertial>>\n      second_partial_derivatives{mesh.number_of_grid_points()};\n\n  const size_t number_of_independent_components =\n      Variables<derivative_tags>::number_of_independent_components;\n\n  DataVector volume_vars_for_tensor(\n      mesh.number_of_grid_points() * number_of_independent_components, 0.0);\n  DataVector tensor_var_1(volume_vars_for_tensor.data(),\n                          mesh.number_of_grid_points());\n  DataVector tensor_var_2(\n      volume_vars_for_tensor.data() + mesh.number_of_grid_points(),\n      mesh.number_of_grid_points());\n  DataVector tensor_var_3(\n      volume_vars_for_tensor.data() + 2 * mesh.number_of_grid_points(),\n      mesh.number_of_grid_points());\n  DataVector scalar_var(\n      volume_vars_for_tensor.data() + Dim * mesh.number_of_grid_points(),\n      mesh.number_of_grid_points());\n\n  set_polynomial(&tensor_var_1, &tensor_var_2, logical_coords);\n  set_polynomial(&tensor_var_3, &scalar_var, logical_coords);\n\n  // Update ghost data as we include tensor variable\n  for (const auto& direction : Direction<Dim>::all_directions()) {\n    auto neighbor_logical_coords = logical_coords;\n    neighbor_logical_coords.get(direction.dimension()) +=\n        direction.sign() * 2.0;\n    DataVector neighbor_vars{\n        mesh.number_of_grid_points() * number_of_independent_components, 0.0};\n    DataVector neighbor_tensor_var1(neighbor_vars.data(),\n                                    mesh.number_of_grid_points());\n    DataVector neighbor_tensor_var2(\n        neighbor_vars.data() + mesh.number_of_grid_points(),\n        mesh.number_of_grid_points());\n    DataVector neighbor_tensor_var3(\n        neighbor_vars.data() + 2 * mesh.number_of_grid_points(),\n        mesh.number_of_grid_points());\n    DataVector neighbor_scalar_var(\n        neighbor_vars.data() + Dim * mesh.number_of_grid_points(),  // NOLINT\n        mesh.number_of_grid_points());\n    set_polynomial(&neighbor_tensor_var1, &neighbor_tensor_var2,\n                   neighbor_logical_coords);\n    set_polynomial(&neighbor_tensor_var3, &neighbor_scalar_var,\n                   neighbor_logical_coords);\n\n    // We need two ghost points for 4th order\n    const size_t num_of_ghost_pts = 2;\n    const auto sliced_data = evolution::dg::subcell::detail::slice_data_impl(\n        gsl::make_span(neighbor_vars.data(), neighbor_vars.size()),\n        mesh.extents(), num_of_ghost_pts,\n        std::unordered_set{direction.opposite()}, 0, {});\n    REQUIRE(sliced_data.size() == 1);\n    REQUIRE(sliced_data.contains(direction.opposite()));\n    neighbor_data[direction] = sliced_data.at(direction.opposite());\n    REQUIRE(neighbor_data.at(direction).size() ==\n            number_of_independent_components * num_of_ghost_pts *\n                mesh.slice_away(0).number_of_grid_points());\n  }\n\n  for (const auto& [direction, data] : neighbor_data) {\n    ghost_cell_vars[direction] = gsl::make_span(data.data(), data.size());\n  }\n\n  ::fd::second_partial_derivatives<derivative_tags>(\n      make_not_null(&second_partial_derivatives),\n      gsl::make_span(volume_vars_for_tensor.data(),\n                     volume_vars_for_tensor.size()),\n      ghost_cell_vars, mesh, number_of_independent_components, 4,\n      inverse_jacobian, inverse_hessian);\n\n  Variables<db::wrap_tags_in<Tags::second_deriv, derivative_tags,\n                             tmpl::size_t<Dim>, Frame::Inertial>>\n      expected_second_partial_derivatives{mesh.number_of_grid_points()};\n\n  // transform the expected derivs to inertial coords\n  gsl::at(expected_pure_second_d_var1, 0) =\n      (1 + 2 * logical_coords.get(0) + logical_coords.get(1) +\n       2 * logical_coords.get(2)) /\n          (2 * square(logical_coords.get(0))) +\n      // the only non-zero contribution from inverse_hessian\n      (0.25 * (logical_coords.get(2) - logical_coords.get(0)) *\n       (1 + 2 * logical_coords.get(0) + logical_coords.get(1) +\n        2 * logical_coords.get(2))) /\n          (pow<3>(logical_coords.get(0)));\n  gsl::at(expected_pure_second_d_var1, 1) =\n      2 * (1 + logical_coords.get(0) + 3 * logical_coords.get(1) +\n           logical_coords.get(2));\n  gsl::at(expected_pure_second_d_var1, 2) =\n      2 * (1 + logical_coords.get(0) + logical_coords.get(1) +\n           3 * logical_coords.get(2));\n  gsl::at(expected_pure_second_d_var2, 0) =\n      (1 + 2 * logical_coords.get(0) + logical_coords.get(1) +\n       2 * logical_coords.get(2)) /\n          (2 * square(logical_coords.get(0))) +\n      // the only non-zero contribution from inverse_hessian\n      (0.25 * (logical_coords.get(2) - logical_coords.get(0)) *\n       (1 + 2 * logical_coords.get(0) + logical_coords.get(1) +\n        2 * logical_coords.get(2))) /\n          (pow<3>(logical_coords.get(0)));\n  gsl::at(expected_pure_second_d_var2, 1) =\n      2 * (1 + logical_coords.get(0) + 3 * logical_coords.get(1) +\n           logical_coords.get(2));\n  gsl::at(expected_pure_second_d_var2, 2) =\n      2 * (1 + logical_coords.get(0) + logical_coords.get(1) +\n           3 * logical_coords.get(2));\n\n  gsl::at(expected_mixed_second_d_var1, 0) =\n      (logical_coords.get(0) - logical_coords.get(2)) /\n      (2 * logical_coords.get(0));\n  gsl::at(expected_mixed_second_d_var1, 1) = 1 + logical_coords.get(0) +\n                                             2 * logical_coords.get(1) +\n                                             2 * logical_coords.get(2);\n  gsl::at(expected_mixed_second_d_var1, 2) =\n      -(1 + logical_coords.get(1) + 4 * logical_coords.get(2)) /\n      (2 * logical_coords.get(0));\n  gsl::at(expected_mixed_second_d_var2, 0) =\n      (logical_coords.get(0) - logical_coords.get(2)) /\n      (2 * logical_coords.get(0));\n  gsl::at(expected_mixed_second_d_var2, 1) = 1 + logical_coords.get(0) +\n                                             2 * logical_coords.get(1) +\n                                             2 * logical_coords.get(2);\n  gsl::at(expected_mixed_second_d_var2, 2) =\n      -(1 + logical_coords.get(1) + 4 * logical_coords.get(2)) /\n      (2 * logical_coords.get(0));\n\n  // putting the expected pure and mixed second derivs into the variable\n  // expected_second_partial_derivatives\n  using second_d_var1_tag =\n      Tags::second_deriv<Tags::Tempi<0, Dim, Frame::Inertial, DataVector>,\n                         tmpl::size_t<Dim>, Frame::Inertial>;\n  using second_d_var2_tag =\n      Tags::second_deriv<Tags::TempScalar<1, DataVector>, tmpl::size_t<Dim>,\n                         Frame::Inertial>;\n  for (size_t deriv_index = 0; deriv_index < Dim; deriv_index++) {\n    for (size_t i = 0; i < Dim; ++i) {\n      (get<second_d_var1_tag>(expected_second_partial_derivatives))\n          .get(deriv_index, deriv_index, i) =\n          gsl::at(expected_pure_second_d_var1, deriv_index);\n      (get<second_d_var1_tag>(expected_second_partial_derivatives))\n          .get(deriv_index, (deriv_index + 1) % Dim, i) =\n          gsl::at(expected_mixed_second_d_var1, deriv_index);\n    }\n    (get<second_d_var2_tag>(expected_second_partial_derivatives))\n        .get(deriv_index, deriv_index) =\n        gsl::at(expected_pure_second_d_var2, deriv_index);\n\n    (get<second_d_var2_tag>(expected_second_partial_derivatives))\n        .get(deriv_index, (deriv_index + 1) % Dim) =\n        gsl::at(expected_mixed_second_d_var2, deriv_index);\n  }\n  {\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        get<second_d_var1_tag>(second_partial_derivatives),\n        get<second_d_var1_tag>(expected_second_partial_derivatives),\n        custom_approx);\n  }\n  {\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        get<second_d_var2_tag>(second_partial_derivatives),\n        get<second_d_var2_tag>(expected_second_partial_derivatives),\n        custom_approx);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.SecondPartialDerivatives\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  const size_t Dim = 3;\n  const size_t points_per_dimension = 5;\n  const size_t fd_deriv_order = 4;\n  test<Dim>(points_per_dimension, fd_deriv_order);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_Unlimited.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Exact.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Unlimited.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <size_t Degree, size_t Dim>\nvoid test_impl() {\n  CAPTURE(Degree);\n  CAPTURE(Dim);\n  const auto recons =\n      [](const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_upper_side_of_face_vars,\n         const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_lower_side_of_face_vars,\n         const gsl::span<const double>& volume_vars,\n         const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n         const Index<Dim>& volume_extents, const size_t number_of_variables) {\n        fd::reconstruction::unlimited<Degree>(\n            reconstructed_upper_side_of_face_vars,\n            reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n            volume_extents, number_of_variables);\n      };\n  const auto recons_neighbor_data =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper,\n              fd::reconstruction::detail::UnlimitedReconstructor<Degree>>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower,\n              fd::reconstruction::detail::UnlimitedReconstructor<Degree>>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct);\n        }\n      };\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(Degree, 8, Degree + 1, recons, recons_neighbor_data);\n  // No python test since if they can reconstruct their degree exactly then the\n  // coefficients are correct because there are non-linear terms to deal with\n  // shocks.\n}\n\ntemplate <size_t Degree>\nvoid test() {\n  test_impl<Degree, 1>();\n  test_impl<Degree, 2>();\n  test_impl<Degree, 3>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.Unlimited\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test<2>();\n  test<4>();\n  test<6>();\n  test<8>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Test_Wcns5z.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Exact.hpp\"\n#include \"Helpers/NumericalAlgorithms/FiniteDifference/Python.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/FallbackReconstructorType.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Minmod.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/MonotonisedCentral.hpp\"\n#include \"NumericalAlgorithms/FiniteDifference/Wcns5z.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace fd::reconstruction {\nnamespace {\n\ntemplate <size_t Dim, size_t NonlinearWeightExponent,\n          typename FallbackReconstructor, bool UseExteriorCell>\nvoid test_function_pointers(const FallbackReconstructorType fallback_recons) {\n  const auto function_ptrs = fd::reconstruction::wcns5z_function_pointers<Dim>(\n      NonlinearWeightExponent, fallback_recons);\n  CHECK(get<0>(function_ptrs) ==\n        &wcns5z<NonlinearWeightExponent, FallbackReconstructor, Dim>);\n  using function_type =\n      void (*)(gsl::not_null<DataVector*>, const DataVector&, const DataVector&,\n               const Index<Dim>&, const Index<Dim>&, const Direction<Dim>&,\n               const double&, const size_t&);\n  CHECK(get<1>(function_ptrs) ==\n        static_cast<function_type>(\n            &reconstruct_neighbor<\n                Side::Lower,\n                ::fd::reconstruction::detail::Wcns5zReconstructor<\n                    NonlinearWeightExponent, FallbackReconstructor>,\n                UseExteriorCell, Dim>));\n  CHECK(get<2>(function_ptrs) ==\n        static_cast<function_type>(\n            &reconstruct_neighbor<\n                Side::Upper,\n                ::fd::reconstruction::detail::Wcns5zReconstructor<\n                    NonlinearWeightExponent, FallbackReconstructor>,\n                UseExteriorCell, Dim>));\n}\n\ntemplate <size_t Dim, class FallbackReconstructor>\nvoid test(const FallbackReconstructorType fallback_recons) {\n  // test for NonlinearWeightExponent = 2 case\n\n  const auto recons =\n      [](const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_upper_side_of_face_vars,\n         const gsl::not_null<std::array<gsl::span<double>, Dim>*>\n             reconstructed_lower_side_of_face_vars,\n         const gsl::span<const double>& volume_vars,\n         const DirectionMap<Dim, gsl::span<const double>>& ghost_cell_vars,\n         const Index<Dim>& volume_extents, const size_t number_of_variables) {\n        const size_t max_number_of_extrema = 1;\n        const double epsilon = 2.0e-16;\n\n        fd::reconstruction::wcns5z<2, FallbackReconstructor>(\n            reconstructed_upper_side_of_face_vars,\n            reconstructed_lower_side_of_face_vars, volume_vars, ghost_cell_vars,\n            volume_extents, number_of_variables, epsilon,\n            max_number_of_extrema);\n      };\n  const auto recons_neighbor_data =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        const size_t max_number_of_extrema = 1;\n        const double epsilon = 2.0e-16;\n\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper, fd::reconstruction::detail::Wcns5zReconstructor<\n                               2, FallbackReconstructor>>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct, epsilon,\n              max_number_of_extrema);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower, fd::reconstruction::detail::Wcns5zReconstructor<\n                               2, FallbackReconstructor>>(\n              face_data, volume_data, neighbor_data, volume_extents,\n              ghost_data_extents, direction_to_reconstruct, epsilon,\n              max_number_of_extrema);\n        }\n      };\n  const auto recons_neighbor_data_interior_cell =\n      [](const gsl::not_null<DataVector*> face_data,\n         const DataVector& volume_data, const DataVector& neighbor_data,\n         const Index<Dim>& volume_extents, const Index<Dim>& ghost_data_extents,\n         const Direction<Dim>& direction_to_reconstruct) {\n        const size_t max_number_of_extrema = 1;\n        const double epsilon = 2.0e-16;\n\n        if (direction_to_reconstruct.side() == Side::Upper) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Upper,\n              fd::reconstruction::detail::Wcns5zReconstructor<\n                  2, FallbackReconstructor>,\n              false>(face_data, volume_data, neighbor_data, volume_extents,\n                     ghost_data_extents, direction_to_reconstruct, epsilon,\n                     max_number_of_extrema);\n        }\n        if (direction_to_reconstruct.side() == Side::Lower) {\n          fd::reconstruction::reconstruct_neighbor<\n              Side::Lower,\n              fd::reconstruction::detail::Wcns5zReconstructor<\n                  2, FallbackReconstructor>,\n              false>(face_data, volume_data, neighbor_data, volume_extents,\n                     ghost_data_extents, direction_to_reconstruct, epsilon,\n                     max_number_of_extrema);\n        }\n      };\n\n  // if there are more than one extrema in any of finite difference stencils,\n  // reconstruction would be switched to FallbackReconstructor which may be only\n  // exact up to the polynomial of degree 1. However, if we are testing with a\n  // single (global) degree 2 polynomial (which is used in\n  // `test_reconstruction_is_exact_if_in_basis`) there cannot be more than one\n  // extrema. So we are safe to use degree 2 case for our test here in order to\n  // confirm that Wcns5z + MC reconstruction _does_ reproduce the accuracy of\n  // Wcns5z if not switched to MC.\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(2, 5, 5, recons, recons_neighbor_data);\n  TestHelpers::fd::reconstruction::test_reconstruction_is_exact_if_in_basis<\n      Dim>(2, 5, 5, recons, recons_neighbor_data_interior_cell);\n\n  if (fallback_recons == FallbackReconstructorType::Minmod) {\n    TestHelpers::fd::reconstruction::test_with_python(\n        Index<Dim>{5}, 5, \"Wcns5z\", \"test_wcns5z_with_minmod\", recons,\n        recons_neighbor_data);\n  } else if (fallback_recons == FallbackReconstructorType::MonotonisedCentral) {\n    TestHelpers::fd::reconstruction::test_with_python(\n        Index<Dim>{5}, 5, \"Wcns5z\", \"test_wcns5z_with_mc\", recons,\n        recons_neighbor_data);\n  } else {\n    TestHelpers::fd::reconstruction::test_with_python(\n        Index<Dim>{5}, 5, \"Wcns5z\", \"test_wcns5z\", recons,\n        recons_neighbor_data);\n  }\n\n  test_function_pointers<Dim, 2, FallbackReconstructor, true>(fallback_recons);\n\n  // check for failing case (nonlinear weight exponent = 3)\n  CHECK_THROWS_WITH(([&fallback_recons]() {\n                      ::fd::reconstruction::wcns5z_function_pointers<Dim>(\n                          3, fallback_recons);\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Nonlinear weight exponent should be 1 or 2\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.FiniteDifference.Wcns5z\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"NumericalAlgorithms/FiniteDifference/\");\n\n  test<1, detail::MinmodReconstructor>(FallbackReconstructorType::Minmod);\n  test<1, detail::MonotonisedCentralReconstructor>(\n      FallbackReconstructorType::MonotonisedCentral);\n  test<1, void>(FallbackReconstructorType::None);\n\n  test<2, detail::MinmodReconstructor>(FallbackReconstructorType::Minmod);\n  test<2, detail::MonotonisedCentralReconstructor>(\n      FallbackReconstructorType::MonotonisedCentral);\n  test<2, void>(FallbackReconstructorType::None);\n\n  test<3, detail::MinmodReconstructor>(FallbackReconstructorType::Minmod);\n  test<3, detail::MonotonisedCentralReconstructor>(\n      FallbackReconstructorType::MonotonisedCentral);\n  test<3, void>(FallbackReconstructorType::None);\n}\n\n}  // namespace fd::reconstruction\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/Wcns5z.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport Minmod\nimport MonotonisedCentral\nimport numpy as np\nimport Reconstruction\nimport scipy.signal\n\n\ndef wcns5z(q):\n    def wcns5z_oneside(q, s):\n        j = 2\n\n        b0 = (\n            0.25 * (q[j - 2 * s] - 4.0 * q[j - s] + 3.0 * q[j]) ** 2\n            + (13.0 / 12.0) * (q[j - 2 * s] - 2.0 * q[j - s] + q[j]) ** 2\n        )\n        b1 = (\n            0.25 * (q[j + s] - q[j - s]) ** 2\n            + (13.0 / 12.0) * (q[j - s] + q[j + s] - 2.0 * q[j]) ** 2\n        )\n        b2 = (\n            0.25 * (q[j + 2 * s] - 4.0 * q[j + s] + 3.0 * q[j]) ** 2\n            + (13.0 / 12.0) * (q[j + 2 * s] - 2.0 * q[j + s] + q[j]) ** 2\n        )\n\n        e0 = 2e-16 * (1.0 + abs(q[j - 2 * s]) + abs(q[j - s]) + abs(q[j]))\n        e1 = 2e-16 * (1.0 + abs(q[j - s]) + abs(q[j]) + abs(q[j + s]))\n        e2 = 2e-16 * (1.0 + abs(q[j + 2 * s]) + abs(q[j + s]) + abs(q[j]))\n\n        beta = np.asarray([b0, b1, b2])\n        epsilon = np.asarray([e0, e1, e2])\n        tau5 = np.abs(b2 - b0)\n\n        alpha = (\n            np.asarray([1, 10, 5])\n            / 16.0\n            * (1.0 + (tau5 / (beta + epsilon)) ** 2)\n        )\n        nw = alpha / np.sum(alpha)\n\n        recons_stencils = (\n            np.asarray(\n                [\n                    3.0 * q[j - 2 * s] - 10.0 * q[j - s] + 15.0 * q[j],\n                    -q[j - s] + 6.0 * q[j] + 3.0 * q[j + s],\n                    3.0 * q[j] + 6.0 * q[j + s] - q[j + 2 * s],\n                ]\n            )\n            / 8.0\n        )\n\n        return np.sum(nw * recons_stencils)\n\n    return [wcns5z_oneside(q, -1), wcns5z_oneside(q, 1)]\n\n\ndef compute_face_values_t(\n    recons_upper_of_cell,\n    recons_lower_of_cell,\n    v,\n    i,\n    j,\n    k,\n    dim_to_recons,\n    wcns5z_type,\n):\n    if dim_to_recons == 0:\n        lower, upper = wcns5z_type(\n            np.asarray(\n                [\n                    v[i - 2, j, k],\n                    v[i - 1, j, k],\n                    v[i, j, k],\n                    v[i + 1, j, k],\n                    v[i + 2, j, k],\n                ]\n            )\n        )\n        recons_lower_of_cell.append(lower)\n        recons_upper_of_cell.append(upper)\n    if dim_to_recons == 1:\n        lower, upper = wcns5z_type(\n            np.asarray(\n                [\n                    v[i, j - 2, k],\n                    v[i, j - 1, k],\n                    v[i, j, k],\n                    v[i, j + 1, k],\n                    v[i, j + 2, k],\n                ]\n            )\n        )\n        recons_lower_of_cell.append(lower)\n        recons_upper_of_cell.append(upper)\n    if dim_to_recons == 2:\n        lower, upper = wcns5z_type(\n            np.asarray(\n                [\n                    v[i, j, k - 2],\n                    v[i, j, k - 1],\n                    v[i, j, k],\n                    v[i, j, k + 1],\n                    v[i, j, k + 2],\n                ]\n            )\n        )\n        recons_lower_of_cell.append(lower)\n        recons_upper_of_cell.append(upper)\n\n\ndef test_wcns5z(u, extents, dim):\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        return compute_face_values_t(\n            recons_upper_of_cell,\n            recons_lower_of_cell,\n            v,\n            i,\n            j,\n            k,\n            dim_to_recons,\n            wcns5z,\n        )\n\n    return Reconstruction.reconstruct(\n        u, extents, dim, [2, 2, 2], compute_face_values\n    )\n\n\ndef test_wcns5z_with_minmod(u, extents, dim):\n    def wcns5z_with_minmod(q):\n        j = 2\n\n        # indices of local maxima\n        idx_local_max = scipy.signal.argrelextrema(q, np.greater)[0]\n        # indices of local minima\n        idx_local_min = scipy.signal.argrelextrema(q, np.less)[0]\n        # number of local maxima and minima\n        n_extrema = len(idx_local_max) + len(idx_local_min)\n\n        if n_extrema <= 1:\n            return wcns5z(q)\n        else:\n            slope = Minmod.minmod(q[j] - q[j - 1], q[j + 1] - q[j])\n            return [q[j] - 0.5 * slope, q[j] + 0.5 * slope]\n\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        return compute_face_values_t(\n            recons_upper_of_cell,\n            recons_lower_of_cell,\n            v,\n            i,\n            j,\n            k,\n            dim_to_recons,\n            wcns5z_with_minmod,\n        )\n\n    return Reconstruction.reconstruct(\n        u, extents, dim, [2, 2, 2], compute_face_values\n    )\n\n\ndef test_wcns5z_with_mc(u, extents, dim):\n    def wcns5z_with_mc(q):\n        j = 2\n\n        # indices of local maxima\n        idx_local_max = scipy.signal.argrelextrema(q, np.greater)[0]\n        # indices of local minima\n        idx_local_min = scipy.signal.argrelextrema(q, np.less)[0]\n        # number of local maxima and minima\n        n_extrema = len(idx_local_max) + len(idx_local_min)\n\n        if n_extrema <= 1:\n            return wcns5z(q)\n        else:\n            slope = MonotonisedCentral.monotonised_central(\n                q[j] - q[j - 1], q[j + 1] - q[j]\n            )\n            return [q[j] - 0.5 * slope, q[j] + 0.5 * slope]\n\n    def compute_face_values(\n        recons_upper_of_cell, recons_lower_of_cell, v, i, j, k, dim_to_recons\n    ):\n        return compute_face_values_t(\n            recons_upper_of_cell,\n            recons_lower_of_cell,\n            v,\n            i,\n            j,\n            k,\n            dim_to_recons,\n            wcns5z_with_mc,\n        )\n\n    return Reconstruction.reconstruct(\n        u, extents, dim, [2, 2, 2], compute_face_values\n    )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/FiniteDifference/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Integration/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_NumericalIntegration\")\n\nset(LIBRARY_SOURCES\n  Test_GslQuadAdaptive.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Integration\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Integration/Test_GslQuadAdaptive.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Integration/GslQuadAdaptive.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n\nnamespace {\n// [integrated_function]\ndouble gaussian(const double x, const double mean, const double factor) {\n  return 2. * factor / sqrt(M_PI) * exp(-square(x - mean));\n}\n// [integrated_function]\n\ndouble integrable_singularity(const double x, const double factor) {\n  return factor * cos(sqrt(abs(x))) / sqrt(abs(x));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Integration.GslQuadAdaptive\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  const double absolute_tolerance = 1.e-10;\n  Approx custom_approx = Approx::custom().epsilon(absolute_tolerance);\n\n  const double error_causing_tolerance =\n      1.e-3 * std::numeric_limits<double>::epsilon();\n\n  {\n    INFO(\"StandardGaussKronrod\");\n    // Construct the integration and give an example\n    // [integration_example]\n    const integration::GslQuadAdaptive<\n        integration::GslIntegralType::StandardGaussKronrod>\n        integration{20};\n    const double mean = 5.;\n    const double factor = 2.;\n    const double lower_boundary = -4.;\n    const double upper_boundary = 10.;\n    const auto result = integration(\n        [&mean, &factor](const double x) { return gaussian(x, mean, factor); },\n        lower_boundary, upper_boundary, absolute_tolerance, 4);\n    // [integration_example]\n    CHECK(result == custom_approx(factor * erf(upper_boundary - mean) -\n                                  factor * erf(lower_boundary - mean)));\n    CHECK_THROWS_AS(\n        integration([&mean, &factor](\n                        const double x) { return gaussian(x, mean, factor); },\n                    lower_boundary, upper_boundary, error_causing_tolerance, 4),\n        convergence_error);\n  }\n\n  {\n    INFO(\"InfiniteInterval\");\n    const integration::GslQuadAdaptive<\n        integration::GslIntegralType::InfiniteInterval>\n        integration{20};\n    const double mean = 5.;\n    const double factor = 2.;\n    const auto result = integration(\n        [&mean, &factor](const double x) { return gaussian(x, mean, factor); },\n        absolute_tolerance);\n    CHECK(result == custom_approx(2. * factor));\n    CHECK_THROWS_AS(\n        integration([&mean, &factor](\n                        const double x) { return gaussian(x, mean, factor); },\n                    error_causing_tolerance),\n        convergence_error);\n  }\n\n  {\n    INFO(\"UpperBoundaryInfinite\");\n    const integration::GslQuadAdaptive<\n        integration::GslIntegralType::UpperBoundaryInfinite>\n        integration{20};\n    const double mean = 5.;\n    const double factor = 2.;\n    const double lower_boundary = -4.;\n    auto result = integration(\n        [&mean, &factor](const double x) { return gaussian(x, mean, factor); },\n        lower_boundary, absolute_tolerance);\n    CHECK(result == custom_approx(factor * (1. - erf(lower_boundary - mean))));\n    CHECK_THROWS_AS(\n        integration([&mean, &factor](\n                        const double x) { return gaussian(x, mean, factor); },\n                    lower_boundary, error_causing_tolerance),\n        convergence_error);\n  }\n\n  {\n    INFO(\"LowerBoundaryInfinite\");\n    const integration::GslQuadAdaptive<\n        integration::GslIntegralType::LowerBoundaryInfinite>\n        integration{20};\n    const double mean = 5.;\n    const double factor = 2.;\n    const double upper_boundary = 10.;\n    auto result = integration(\n        [&mean, &factor](const double x) { return gaussian(x, mean, factor); },\n        upper_boundary, absolute_tolerance);\n    CHECK(result == custom_approx(factor * (1. + erf(upper_boundary - mean))));\n    CHECK_THROWS_AS(\n        integration([&mean, &factor](\n                        const double x) { return gaussian(x, mean, factor); },\n                    upper_boundary, error_causing_tolerance),\n        convergence_error);\n  }\n\n  {\n    INFO(\"IntegrableSingularitiesPresent\");\n    const integration::GslQuadAdaptive<\n        integration::GslIntegralType::IntegrableSingularitiesPresent>\n        integration{20};\n    const double factor = 2.;\n    const double upper_boundary = square(0.5 * M_PI);\n    auto result = integration(\n        [&factor](const double x) { return integrable_singularity(x, factor); },\n        0., upper_boundary, absolute_tolerance);\n    CHECK(result == custom_approx(2. * factor * (sin(sqrt(upper_boundary)))));\n    CHECK_THROWS_AS(integration(\n                        [&factor](const double x) {\n                          return integrable_singularity(x, factor);\n                        },\n                        0., upper_boundary, error_causing_tolerance),\n                    convergence_error);\n  }\n\n  {\n    INFO(\"IntegrableSingularitiesKnown\");\n    const integration::GslQuadAdaptive<\n        integration::GslIntegralType::IntegrableSingularitiesKnown>\n        integration{30};\n    const double factor = 2.;\n    const double upper_boundary = square(0.5 * M_PI);\n    const std::vector<double> points{-upper_boundary, 0., upper_boundary};\n    auto result = integration(\n        [&factor](const double x) { return integrable_singularity(x, factor); },\n        points, absolute_tolerance);\n    CHECK(result == custom_approx(4. * factor * sin(sqrt(upper_boundary))));\n    CHECK_THROWS_AS(integration(\n                        [&factor](const double x) {\n                          return integrable_singularity(x, factor);\n                        },\n                        points, error_causing_tolerance),\n                    convergence_error);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Python)\n\nset(LIBRARY \"Test_NumericalInterpolation\")\n\nset(LIBRARY_SOURCES\n  Test_BarycentricRational.cpp\n  Test_CardinalInterpolator.cpp\n  Test_CubicSpline.cpp\n  Test_IrregularInterpolant.cpp\n  Test_LagrangePolynomial.cpp\n  Test_MultiLinearSpanInterpolation.cpp\n  Test_LinearLeastSquares.cpp\n  Test_LinearRegression.cpp\n  Test_PolynomialInterpolation.cpp\n  Test_PredictedZeroCrossing.cpp\n  Test_RegularGridInterpolant.cpp\n  Test_SendGhWorldtubeData.cpp\n  Test_SpanInterpolators.cpp\n  Test_ZeroCrossingPredictor.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  GeneralRelativitySolutions\n  Interpolation\n  Logging\n  MathFunctions\n  ParallelInterpolation\n  RelativisticEulerSolutions\n  Spectral\n  SpectralHelpers\n  SphericalHarmonicsHelpers\n  SpinWeightedSphericalHarmonics\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Interpolation.Python.BarycentricRational\"\n  Test_BarycentricRational.py\n  \"Unit;Interpolation;Python\"\n  PyInterpolation)\n\nspectre_add_python_bindings_test(\n  \"Unit.Interpolation.Python.CubicSpline\"\n  Test_CubicSpline.py\n  \"Unit;Interpolation;Python\"\n  PyInterpolation)\n\nspectre_add_python_bindings_test(\n  \"Unit.Interpolation.Python.RegularGridInterpolant\"\n  Test_RegularGridInterpolant.py\n  \"Unit;Interpolation;Python\"\n  PyInterpolation)\n\nspectre_add_python_bindings_test(\n  \"Unit.Interpolation.Python.IrregularInterpolant\"\n  Test_IrregularInterpolant.py\n  \"Unit;Interpolation;Python\"\n  PyInterpolation)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Python/Test_BarycentricRational.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nimport scipy.interpolate\n\nfrom spectre.Interpolation import BarycentricRational\n\n\nclass TestBarycentricRational(unittest.TestCase):\n    def test_barycentric_rational(self):\n        x_values = np.linspace(0, 10, 5)\n        y_values = x_values**2\n        interpolant = BarycentricRational(x_values, y_values, order=3)\n        npt.assert_array_equal(interpolant.x_values(), x_values)\n        npt.assert_array_equal(interpolant.y_values(), y_values)\n        self.assertEqual(interpolant.order(), 3)\n        # Compare to scipy implementation\n        scipy_interpolant = scipy.interpolate.BarycentricInterpolator(\n            x_values, y_values\n        )\n        sample_points = [0.0, 1.5, 9.0, 10.0]\n        for x in sample_points:\n            self.assertAlmostEqual(interpolant(x), scipy_interpolant(x))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Python/Test_CubicSpline.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nimport scipy.interpolate\n\nfrom spectre.Interpolation import CubicSpline\n\n\nclass TestCubicSpline(unittest.TestCase):\n    def test_cubic_spline(self):\n        x_values = np.linspace(0, 10, 5)\n        y_values = x_values**2\n        interpolant = CubicSpline(x_values, y_values)\n        npt.assert_array_equal(interpolant.x_values(), x_values)\n        npt.assert_array_equal(interpolant.y_values(), y_values)\n        # Compare to scipy implementation\n        scipy_interpolant = scipy.interpolate.CubicSpline(\n            x_values, y_values, bc_type=\"natural\"\n        )\n        sample_points = [0.0, 1.5, 9.0, 10.0]\n        for x in sample_points:\n            self.assertAlmostEqual(interpolant(x), scipy_interpolant(x))\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Python/Test_IrregularInterpolant.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.random\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.Interpolation import Irregular\nfrom spectre.Spectral import Basis, Mesh, Quadrature, logical_coordinates\n\n\nclass TestIrregular(unittest.TestCase):\n    # arbitrary polynomial functions of low order for exact interpolation\n    def polynomial(self, coords):\n        dim = len(coords)\n        if dim == 1:\n            x = coords[0]\n            return x**2 + x + 1.0\n        elif dim == 2:\n            x, y = coords\n            return 2.0 * x**2 + y**2 + y + x + 2.0\n        elif dim == 3:\n            x, y, z = coords\n            return 3.0 * x**2 + 2.0 * y**2 + z**2 + 2.0 * y + z + x + 2.0\n        else:\n            raise ValueError(\n                \"Coordinates must have shape (dim, N) where dim is 1, 2, or 3.\"\n            )\n\n    def test_irregular(self):\n        for dim in [1, 2, 3]:\n            for quadrature in [Quadrature.Gauss, Quadrature.GaussLobatto]:\n                for num_points in range(3, 10):\n                    source_mesh = Mesh[dim](\n                        num_points, Basis.Legendre, quadrature\n                    )\n                    target_logical_coords = (\n                        numpy.random.rand(dim, 3) * 2.0 - 1.0\n                    )\n\n                    interpolant = Irregular[dim](\n                        source_mesh=source_mesh,\n                        target_logical_coords=[\n                            DataVector(xi) for xi in target_logical_coords\n                        ],\n                    )\n\n                    source_logical_coords = np.array(\n                        logical_coordinates(source_mesh)\n                    )\n\n                    source_data = self.polynomial(source_logical_coords)\n                    target_data = self.polynomial(target_logical_coords)\n\n                    interpolated_data = interpolant.interpolate(\n                        DataVector(source_data)\n                    )\n                    npt.assert_allclose(interpolated_data, target_data, 1e-14)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Python/Test_RegularGridInterpolant.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.Interpolation import RegularGrid\nfrom spectre.Spectral import Basis, Mesh, Quadrature, logical_coordinates\n\n\nclass TestRegularGrid(unittest.TestCase):\n    # arbitrary polynomial functions of low order for exact interpolation\n    def polynomial(self, coords):\n        dim = len(coords)\n        if dim == 1:\n            x = coords[0]\n            return x**2 + x + 1.0\n\n        elif dim == 2:\n            x, y = coords\n            return 2.0 * x**2 + y**2 + y + x + 2.0\n\n        elif dim == 3:\n            x, y, z = coords\n            return 3.0 * x**2 + 2.0 * y**2 + z**2 + 2.0 * y + z + x + 2.0\n\n        else:\n            raise ValueError(\n                \"Coordinates must have shape (dim, N) where dim is 1, 2, or 3.\"\n            )\n\n    def test_regular_grid(self):\n        for dim in range(1, 4):\n            for quadrature in [Quadrature.Gauss, Quadrature.GaussLobatto]:\n                for num_points in range(3, 10):\n                    source_mesh = Mesh[dim](\n                        num_points, Basis.Legendre, quadrature\n                    )\n                    target_mesh = Mesh[dim](\n                        num_points + 2, Basis.Legendre, quadrature\n                    )\n                    interpolant = RegularGrid[dim](source_mesh, target_mesh)\n\n                    source_coords = np.array(logical_coordinates(source_mesh))\n                    target_coords = np.array(logical_coordinates(target_mesh))\n\n                    initial_data = self.polynomial(source_coords)\n                    target_data = self.polynomial(target_coords)\n\n                    interpolated_data = interpolant.interpolate(\n                        DataVector(initial_data)\n                    )\n                    npt.assert_allclose(\n                        target_data, interpolated_data, rtol=1e-14, atol=1e-14\n                    )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_BarycentricRational.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRational.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\ntemplate <class F>\nvoid test_barycentric_rational(const F& function, const double lower_bound,\n                               const double upper_bound, const size_t size,\n                               const size_t order) {\n  std::vector<double> x_values(size), y_values(size);\n  const double delta_x = (upper_bound - lower_bound) / size;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dis(0.0, delta_x);\n  for (size_t i = 0; i < size; ++i) {\n    x_values[i] = lower_bound + i * delta_x + dis(gen);\n    y_values[i] = function(x_values[i]);\n  }\n\n  intrp::BarycentricRational interpolant{x_values, y_values, order};\n  CHECK(interpolant.x_values() == x_values);\n  CHECK(interpolant.y_values() == y_values);\n\n  const auto deserialized_interpolant = serialize_and_deserialize(interpolant);\n  Approx custom_approx = Approx::custom().epsilon(3.e-12).scale(1.0);\n  for (size_t i = 0; i < 10 * size; ++i) {\n    const double x_value = lower_bound + i * delta_x * 0.1 + 0.1 * dis(gen);\n    CAPTURE(x_value);\n    CHECK_ITERABLE_CUSTOM_APPROX(function(x_value), interpolant(x_value),\n                                 custom_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        function(x_value), deserialized_interpolant(x_value), custom_approx);\n  }\n  CHECK(order == interpolant.order());\n  CHECK(order == deserialized_interpolant.order());\n}\n\nvoid single_call(const size_t number_of_points, const size_t order,\n                 const size_t expo) {\n  INFO(\"Polynomial degree := \" << expo);\n  test_barycentric_rational(\n      [expo](const auto& x) {\n        auto result = x;\n        for (size_t j = 2; j < expo; ++j) {\n          result += pow(x, j);\n        }\n        return result;\n      },\n      -1.0, 2.3, number_of_points, order);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.BarycentricRational\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  for (size_t order = 3; order < 6; ++order) {\n    INFO(\"Order:= \" << order);\n    {\n      const size_t number_of_points = 22 - 3 * order;\n      single_call(number_of_points, order, 1);\n      single_call(number_of_points, order, 2);\n    }\n    {\n      const size_t number_of_points = 22 - 2 * order;\n      single_call(number_of_points, order, 3);\n    }\n    if (order > 3) {\n      const size_t number_of_points = 22 - order;\n      single_call(number_of_points, order, 4);\n      single_call(number_of_points, order, 5);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_CardinalInterpolator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/DiskTestFunctions.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CardinalInterpolator.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace {\n// Polynomial of given degree with leading coefficinet \\f$a_0\\f$, and with\n// \\f$a_{n+1} = a_n / falloff\nclass Polynomial {\n public:\n  Polynomial(const size_t degree, const double a_0, const double falloff)\n      : coefficients_(degree + 1) {\n    double n = falloff * a_0;\n    std::generate(coefficients_.begin(), coefficients_.end(), [&falloff, &n]() {\n      n /= falloff;\n      return n;\n    });\n  }\n  Polynomial() = default;\n  DataVector operator()(const DataVector& x) const {\n    return evaluate_polynomial(coefficients_, x);\n  }\n\n private:\n  std::vector<double> coefficients_;\n};\n\ntemplate <size_t Dim>\nclass ProductOfPolynomials {\n public:\n  ProductOfPolynomials(const std::array<size_t, Dim>& degree,\n                       const std::array<double, Dim>& a_0,\n                       const std::array<double, Dim>& falloff) {\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(polynomials_, d) =\n          Polynomial{gsl::at(degree, d), gsl::at(a_0, d), gsl::at(falloff, d)};\n    }\n  }\n  DataVector operator()(\n      const tnsr::I<DataVector, Dim, Frame::ElementLogical>& x) const {\n    DataVector result = polynomials_[0](get<0>(x));\n    for (size_t d = 1; d < Dim; ++d) {\n      result *= gsl::at(polynomials_, d)(x.get(d));\n    }\n    return result;\n  }\n\n private:\n  std::array<Polynomial, Dim> polynomials_;\n};\n\nvoid test_1d(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  for (size_t n_target_points = 1; n_target_points < 101;\n       n_target_points += 11) {\n    const auto xi_target =\n        make_with_random_values<tnsr::I<DataVector, 1, Frame::ElementLogical>>(\n            generator, make_not_null(&xi_distribution), n_target_points);\n    const tnsr::I<double, 1, Frame::ElementLogical> xi_target_single{\n        {{get<0>(xi_target)[0]}}};\n    for (const auto basis :\n         std::array{Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n      for (const auto quadrature :\n           std::array{Spectral::Quadrature::Gauss,\n                      Spectral::Quadrature::GaussLobatto}) {\n        for (size_t n_xi = 2; n_xi < 21; ++n_xi) {\n          const Mesh<1> source_mesh{n_xi, basis, quadrature};\n          const Polynomial f{n_xi - 1, 1.5, 2.0};\n          const auto xi_source = logical_coordinates(source_mesh);\n          const DataVector f_source = f(get<0>(xi_source));\n          const DataVector f_expected = f(get<0>(xi_target));\n          {\n            const intrp::Cardinal<1> interpolator(source_mesh, xi_target);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n          }\n          if (n_target_points == 1) {\n            const intrp::Cardinal<1> interpolator(source_mesh,\n                                                  xi_target_single);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK(f_interpolated.size() == 1);\n            CHECK(f_interpolated[0] == approx(f_expected[0]));\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_2d_cartesian(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  const auto bases =\n      std::array{Spectral::Basis::Legendre, Spectral::Basis::Chebyshev};\n  const auto quadratures = std::array{Spectral::Quadrature::Gauss,\n                                      Spectral::Quadrature::GaussLobatto};\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    const auto xi_target =\n        make_with_random_values<tnsr::I<DataVector, 2, Frame::ElementLogical>>(\n            generator, make_not_null(&xi_distribution), n_target_points);\n    const tnsr::I<double, 2, Frame::ElementLogical> xi_target_single{\n        {{get<0>(xi_target)[0], get<1>(xi_target)[0]}}};\n    for (const auto xi_basis : bases) {\n      for (const auto xi_quadrature : quadratures) {\n        for (const auto eta_basis : bases) {\n          for (const auto eta_quadrature : quadratures) {\n            for (size_t n_xi = 2; n_xi < 21; n_xi += 3) {\n              for (size_t n_eta = 2; n_eta < 21; n_eta += 3) {\n                const Mesh<2> source_mesh{\n                    std::array{n_xi, n_eta}, std::array{xi_basis, eta_basis},\n                    std::array{xi_quadrature, eta_quadrature}};\n                const ProductOfPolynomials<2> f{std::array{n_xi - 1, n_eta - 1},\n                                                std::array{1.5, 2.5},\n                                                std::array{2.0, 4.0}};\n                const auto xi_source = logical_coordinates(source_mesh);\n                const DataVector f_source = f(xi_source);\n                const DataVector f_expected = f(xi_target);\n                {\n                  const intrp::Cardinal<2> interpolator(source_mesh, xi_target);\n                  const DataVector f_interpolated =\n                      interpolator.interpolate(f_source);\n                  CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n                }\n                if (n_target_points == 1) {\n                  const intrp::Cardinal<2> interpolator(source_mesh,\n                                                        xi_target_single);\n                  const DataVector f_interpolated =\n                      interpolator.interpolate(f_source);\n                  CHECK(f_interpolated.size() == 1);\n                  CHECK(f_interpolated[0] == approx(f_expected[0]));\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_2d_spherical(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 2, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = acos(make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target));\n    get<1>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    const tnsr::I<double, 2, Frame::ElementLogical> xi_target_single{\n        {{get<0>(xi_target)[0], get<1>(xi_target)[0]}}};\n    for (size_t n_z = 0; n_z < 4; ++n_z) {\n      for (size_t n_y = 0; n_y < 4; ++n_y) {\n        for (size_t n_x = 0; n_x < 4; ++n_x) {\n          const Mesh<2> source_mesh{\n              std::array{n_x + n_y + n_z + 2, 2 * (n_x + n_y) + 3},\n              std::array{Spectral::Basis::SphericalHarmonic,\n                         Spectral::Basis::SphericalHarmonic},\n              std::array{Spectral::Quadrature::Gauss,\n                         Spectral::Quadrature::Equiangular}};\n          const YlmTestFunctions::ProductOfPolynomials f(n_x, n_y, n_z);\n          const auto xi_source = logical_coordinates(source_mesh);\n          const DataVector f_source = f(xi_source);\n          const DataVector f_expected = f(xi_target);\n          {\n            const intrp::Cardinal<2> interpolator(source_mesh, xi_target);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n          }\n          if (n_target_points == 1) {\n            const intrp::Cardinal<2> interpolator(source_mesh,\n                                                  xi_target_single);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK(f_interpolated.size() == 1);\n            CHECK(f_interpolated[0] == approx(f_expected[0]));\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_3d_cartesian(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    const auto xi_target =\n        make_with_random_values<tnsr::I<DataVector, 3, Frame::ElementLogical>>(\n            generator, make_not_null(&xi_distribution), n_target_points);\n    const tnsr::I<double, 3, Frame::ElementLogical> xi_target_single{\n        {{get<0>(xi_target)[0], get<1>(xi_target)[0], get<2>(xi_target)[0]}}};\n    for (size_t n_xi = 2; n_xi < 21; n_xi += 3) {\n      for (size_t n_eta = 2; n_eta < 21; n_eta += 3) {\n        for (size_t n_zeta = 2; n_zeta < 21; n_zeta += 3) {\n          const Mesh<3> source_mesh{std::array{n_xi, n_eta, n_zeta},\n                                    Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto};\n          const ProductOfPolynomials<3> f{\n              std::array{n_xi - 1, n_eta - 1, n_zeta - 1},\n              std::array{1.5, 2.5, 3.5}, std::array{2.0, 4.0, 2.5}};\n          const auto xi_source = logical_coordinates(source_mesh);\n          const DataVector f_source = f(xi_source);\n          const DataVector f_expected = f(xi_target);\n          {\n            const intrp::Cardinal<3> interpolator(source_mesh, xi_target);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n          }\n          if (n_target_points == 1) {\n            const intrp::Cardinal<3> interpolator(source_mesh,\n                                                  xi_target_single);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK(f_interpolated.size() == 1);\n            CHECK(f_interpolated[0] == approx(f_expected[0]));\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_3d_spherical(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 3, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    get<1>(xi_target) = acos(make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target));\n    get<2>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    const tnsr::I<double, 3, Frame::ElementLogical> xi_target_single{\n        {{get<0>(xi_target)[0], get<1>(xi_target)[0], get<2>(xi_target)[0]}}};\n    for (size_t n_r = 2; n_r < 4; ++n_r) {\n      for (size_t n_z = 0; n_z < 4; ++n_z) {\n        for (size_t n_y = 0; n_y < 4; ++n_y) {\n          for (size_t n_x = 0; n_x < 4; ++n_x) {\n            const Mesh<3> source_mesh{\n                std::array{n_r, n_x + n_y + n_z + 2, 2 * (n_x + n_y) + 3},\n                std::array{Spectral::Basis::Legendre,\n                           Spectral::Basis::SphericalHarmonic,\n                           Spectral::Basis::SphericalHarmonic},\n                std::array{Spectral::Quadrature::GaussLobatto,\n                           Spectral::Quadrature::Gauss,\n                           Spectral::Quadrature::Equiangular}};\n            const Polynomial f_r{n_r - 1, 1.5, 2.0};\n            const YlmTestFunctions::ProductOfPolynomials f_a(n_x, n_y, n_z);\n            const auto xi_source = logical_coordinates(source_mesh);\n            const DataVector f_source =\n                f_r(get<0>(xi_source)) *\n                f_a(get<1>(xi_source), get<2>(xi_source));\n            const DataVector f_expected =\n                f_r(get<0>(xi_target)) *\n                f_a(get<1>(xi_target), get<2>(xi_target));\n            {\n              const intrp::Cardinal<3> interpolator(source_mesh, xi_target);\n              const DataVector f_interpolated =\n                  interpolator.interpolate(f_source);\n              CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n            }\n            if (n_target_points == 1) {\n              const intrp::Cardinal<3> interpolator(source_mesh,\n                                                    xi_target_single);\n              const DataVector f_interpolated =\n                  interpolator.interpolate(f_source);\n              CHECK(f_interpolated.size() == 1);\n              CHECK(f_interpolated[0] == approx(f_expected[0]));\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_2d_disk(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 2, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    get<1>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    const tnsr::I<double, 2, Frame::ElementLogical> xi_target_single{\n        {{get<0>(xi_target)[0], get<1>(xi_target)[0]}}};\n    for (size_t n_y = 0; n_y < 4; ++n_y) {\n      for (size_t n_x = 0; n_x < 4; ++n_x) {\n        const Mesh<2> source_mesh{\n            n_x + n_y == 0 ? std::array{1_st, 1_st}\n                           : std::array{n_x + n_y + 1, 2 * (n_x + n_y) + 1},\n            std::array{Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2},\n            std::array{Spectral::Quadrature::GaussRadauUpper,\n                       Spectral::Quadrature::Equiangular}};\n        const DiskTestFunctions::ProductOfPolynomials f{n_x, n_y};\n        const auto xi_source = logical_coordinates(source_mesh);\n        const DataVector f_source =\n            f(0.5 * (get<0>(xi_source) + 1.0), get<1>(xi_source));\n        const DataVector f_expected =\n            f(0.5 * (get<0>(xi_target) + 1.0), get<1>(xi_target));\n        {\n          const intrp::Cardinal<2> interpolator(source_mesh, xi_target);\n          const DataVector f_interpolated = interpolator.interpolate(f_source);\n          CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n        }\n        if (n_target_points == 1) {\n          const intrp::Cardinal<2> interpolator(source_mesh, xi_target_single);\n          const DataVector f_interpolated = interpolator.interpolate(f_source);\n          CHECK(f_interpolated.size() == 1);\n          CHECK(f_interpolated[0] == approx(f_expected[0]));\n        }\n      }\n    }\n  }\n}\n\nvoid test_3d_cylinder(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 3, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    get<1>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    get<2>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    const tnsr::I<double, 3, Frame::ElementLogical> xi_target_single{\n        {{get<0>(xi_target)[0], get<1>(xi_target)[0], get<2>(xi_target)[0]}}};\n    for (size_t n_z = 2; n_z < 4; ++n_z) {\n      for (size_t n_y = 0; n_y < 4; ++n_y) {\n        for (size_t n_x = 0; n_x < 4; ++n_x) {\n          const Mesh<3> source_mesh{\n              n_x + n_y == 0\n                  ? std::array{1_st, 1_st, n_z}\n                  : std::array{n_x + n_y + 1, 2 * (n_x + n_y) + 1, n_z},\n              std::array{Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2,\n                         Spectral::Basis::Legendre},\n              std::array{Spectral::Quadrature::GaussRadauUpper,\n                         Spectral::Quadrature::Equiangular,\n                         Spectral::Quadrature::GaussLobatto}};\n          const DiskTestFunctions::ProductOfPolynomials f{n_x, n_y};\n          const Polynomial f_z{n_z - 1, 1.5, 2.0};\n          const auto xi_source = logical_coordinates(source_mesh);\n          const DataVector f_source =\n              f(0.5 * (get<0>(xi_source) + 1.0), get<1>(xi_source)) *\n              f_z(get<2>(xi_source));\n          const DataVector f_expected =\n              f(0.5 * (get<0>(xi_target) + 1.0), get<1>(xi_target)) *\n              f_z(get<2>(xi_target));\n          {\n            const intrp::Cardinal<3> interpolator(source_mesh, xi_target);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n          }\n          if (n_target_points == 1) {\n            const intrp::Cardinal<3> interpolator(source_mesh,\n                                                  xi_target_single);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK(f_interpolated.size() == 1);\n            CHECK(f_interpolated[0] == approx(f_expected[0]));\n          }\n        }\n      }\n    }\n  }\n}\nvoid test_errors() {\n  {\n    INFO(\"Testing SphericalHarmonic with unsupported quadrature\");\n    CHECK_THROWS_WITH(\n        (intrp::Cardinal<2>{Mesh<2>{{3, 3},\n                                    {Spectral::Basis::SphericalHarmonic,\n                                     Spectral::Basis::SphericalHarmonic},\n                                    {Spectral::Quadrature::GaussLobatto,\n                                     Spectral::Quadrature::Equiangular}},\n                            tnsr::I<DataVector, 2, Frame::ElementLogical>{\n                                {{{1.0, 2.0}, {0.5, 1.5}}}}}),\n        Catch::Matchers::ContainsSubstring(\n            \"Quadrature must be Gauss or Equiangular for Basis \"\n            \"SphericalHarmonic\"));\n  }\n  {\n    INFO(\"Testing ZernikeB2 with unsupported quadrature\");\n    CHECK_THROWS_WITH(\n        (intrp::Cardinal<2>{\n            Mesh<2>{{3, 3},\n                    {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2},\n                    {Spectral::Quadrature::Gauss,\n                     Spectral::Quadrature::Equiangular}},\n            tnsr::I<DataVector, 2, Frame::ElementLogical>{\n                {{{0.5, 1.0}, {0.0, 1.5}}}}}),\n        Catch::Matchers::ContainsSubstring(\n            \"Quadrature must be GaussRadauUpper or Equiangular for Basis \"\n            \"ZernikeB2\"));\n  }\n  {\n    INFO(\"Testing 1D ZernikeB2 interpolate_zernike_b2 error\");\n    CHECK_THROWS_WITH(\n        (intrp::Cardinal<1>{Mesh<1>{3, Spectral::Basis::ZernikeB2,\n                                    Spectral::Quadrature::GaussRadauUpper},\n                            tnsr::I<DataVector, 1, Frame::ElementLogical>{\n                                {{{1.0, 2.0, 3.0}}}}}),\n        Catch::Matchers::ContainsSubstring(\n            \"ZernikeB2 interpolation is not supported for 1D\"));\n  }\n\n#ifdef SPECTRE_DEBUG\n  {\n    INFO(\"Testing N_phi odd assertion for ZernikeB2\");\n    CHECK_THROWS_WITH(\n        (intrp::Cardinal<2>{\n            Mesh<2>{{3, 4},\n                    {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2},\n                    {Spectral::Quadrature::GaussRadauUpper,\n                     Spectral::Quadrature::Equiangular}},\n            tnsr::I<DataVector, 2, Frame::ElementLogical>{\n                {{{0.5, 1.0}, {0.0, 1.5}}}}}),\n        Catch::Matchers::ContainsSubstring(\n            \"Need N_phi to be odd for stability\"));\n  }\n#endif\n}\n}  // namespace\n\n// [[Timeout, 20]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.Cardinal\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  MAKE_GENERATOR(generator);\n  test_1d(make_not_null(&generator));\n  test_2d_cartesian(make_not_null(&generator));\n  test_2d_spherical(make_not_null(&generator));\n  test_2d_disk(make_not_null(&generator));\n  test_3d_cartesian(make_not_null(&generator));\n  test_3d_spherical(make_not_null(&generator));\n  test_3d_cylinder(make_not_null(&generator));\n  test_errors();\n  {\n    INFO(\"Testing basic construction\");\n    const intrp::Cardinal<2> interpolant{\n        Mesh<2>{{{3, 2}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::Gauss,\n                  Spectral::Quadrature::GaussLobatto}}},\n        tnsr::I<DataVector, 2, Frame::ElementLogical>{\n            {{{1., 2., 3.}, {2., 3., 4.}}}}};\n    test_serialization(interpolant);\n  }\n  {\n    INFO(\"Testing Zernike construction\");\n    const intrp::Cardinal<3> interpolant{\n        Mesh<3>{{{3, 9, 4}},\n                {{Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2,\n                  Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::GaussRadauUpper,\n                  Spectral::Quadrature::Equiangular,\n                  Spectral::Quadrature::GaussLobatto}}},\n        tnsr::I<DataVector, 3, Frame::ElementLogical>{\n            {{{1., 2., 3.}, {2., 3., 4.}, {3., 4., 5.}}}}};\n    test_serialization(interpolant);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_CubicSpline.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <utility>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpline.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\ntemplate <class F>\nvoid test_cubic_spline(const F& function, const double lower_bound,\n                       const double upper_bound, const size_t size,\n                       const double tolerance,\n                       const double tolerance_interior) {\n  // Construct random points between lower and upper bound to interpolate\n  // through. Always include the bounds in the x-values.\n  std::vector<double> x_values(size);\n  std::vector<double> y_values(size);\n  const double delta_x = (upper_bound - lower_bound) / size;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(0., delta_x);\n  x_values.front() = lower_bound;\n  for (size_t i = 1; i < size - 1; ++i) {\n    x_values[i] = lower_bound + i * delta_x + dist(gen);\n  }\n  x_values.back() = upper_bound;\n  for (size_t i = 0; i < size; ++i) {\n    y_values[i] = function(x_values[i]);\n  }\n\n  Approx custom_approx = Approx::custom().epsilon(tolerance).scale(1.);\n  Approx custom_approx_interior =\n      Approx::custom().epsilon(tolerance_interior).scale(1.);\n\n  // Construct the interpolant and give an example\n  // [interpolate_example]\n  intrp::CubicSpline interpolant{x_values, y_values};\n  CHECK(interpolant.x_values() == x_values);\n  CHECK(interpolant.y_values() == y_values);\n  const double x_to_interpolate_to =\n      lower_bound + (upper_bound - lower_bound) / 2.;\n  CHECK(interpolant(x_to_interpolate_to) ==\n        custom_approx_interior(function(x_to_interpolate_to)));\n  // [interpolate_example]\n\n  // Check that the interpolation is exact at the datapoints\n  for (const auto& x_value : x_values) {\n    CHECK(interpolant(x_value) == approx(function(x_value)));\n  }\n\n  // Check that the interpolation matches the function within the given\n  // tolerance. Also check that the serialized-and-deserialized interpolant does\n  // the same.\n  const auto deserialized_interpolant = serialize_and_deserialize(interpolant);\n  CHECK(deserialized_interpolant == interpolant);\n  double max_error = 0.;\n  double max_error_x_value = std::numeric_limits<double>::signaling_NaN();\n  double max_error_interior = 0.;\n  double max_error_interior_x_value =\n      std::numeric_limits<double>::signaling_NaN();\n  for (size_t i = 0; i < 10 * size; ++i) {\n    const double x_value = lower_bound + i * delta_x * 0.1 + 0.1 * dist(gen);\n    CAPTURE(x_value);\n    const double y_value = function(x_value);\n    const double interpolated_y_value = interpolant(x_value);\n    CHECK(interpolated_y_value == custom_approx(y_value));\n    // Test the == operator\n    CHECK(deserialized_interpolant(x_value) == interpolated_y_value);\n    // Record max error for better test failure reports\n    const double error = abs(interpolated_y_value - y_value);\n    if (error >= max_error) {\n      max_error = error;\n      max_error_x_value = x_value;\n    }\n    // Test the interpolation away from the boundaries with a lower tolerance.\n    // Since this is a cubic spline interpolation, boundary effects should be\n    // confined to the outer three interpolation points.\n    const double boundary_fraction = 0.3;\n    if (i > 10. * size * boundary_fraction and\n        i < 10. * size * (1. - boundary_fraction)) {\n      CHECK(interpolated_y_value == custom_approx_interior(y_value));\n      if (error >= max_error_interior) {\n        max_error_interior = error;\n        max_error_interior_x_value = x_value;\n      }\n    }\n  }\n  // Check the copy constructors\n  {\n    intrp::CubicSpline interpolant_copy_assigned{{-1.0, 0.0, 1.0, 2.0},\n                                                 {0.0, 0.0, 0.0, 6.0}};\n    interpolant_copy_assigned = interpolant;\n    const auto interpolant_copy_constructed{interpolant};\n\n    CHECK(interpolant_copy_assigned == interpolant);\n    CHECK(interpolant_copy_constructed == interpolant);\n  }\n  // Output information on the precision the interpolation achieved when it\n  // failed to stay within the given tolerances\n  CAPTURE(max_error);\n  CAPTURE(max_error_interior);\n  // These checks are needed to trigger the max_error captures above\n  CHECK(interpolant(max_error_x_value) ==\n        custom_approx(function(max_error_x_value)));\n  CHECK(interpolant(max_error_interior_x_value) ==\n        custom_approx_interior(function(max_error_interior_x_value)));\n\n  // Make sure moving the interpolant doesn't break anything\n  const auto moved_interpolant = std::move(interpolant);\n  const double x_value = lower_bound + dist(gen) * size;\n  const double y_value = function(x_value);\n  CHECK(moved_interpolant(x_value) == custom_approx(y_value));\n}\n\nvoid test_with_polynomial(const size_t number_of_points,\n                          const size_t polynomial_degree,\n                          const double tolerance,\n                          const double tolerance_interior) {\n  CAPTURE(polynomial_degree);\n  CAPTURE(number_of_points);\n  std::vector<double> coeffs(polynomial_degree + 1, 1.);\n  test_cubic_spline(\n      [&coeffs](const auto& x) { return evaluate_polynomial(coeffs, x); }, -1.,\n      2.3, number_of_points, tolerance, tolerance_interior);\n}\n\nvoid test_with_natural_boundary(const size_t number_of_points,\n                                const double tolerance) {\n  CAPTURE(number_of_points);\n  // Precision at the boundaries should be the same as in the interior since\n  // the natural boundary conditions are correct for this function\n  test_cubic_spline([](const auto& x) { return cube(sin(x)); }, 0., M_PI,\n                    number_of_points, tolerance, tolerance);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.CubicSpline\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test_with_polynomial(10, 1, 1.e-14, 1.e-14);\n  test_with_polynomial(10, 2, 1.e-1, 1.e-2);\n  test_with_polynomial(100, 2, 1.e-3, 1.e-12);\n  test_with_polynomial(100, 3, 1.e-2, 1.e-12);\n  test_with_polynomial(1000, 3, 1.e-4, 1.e-12);\n  test_with_polynomial(1000, 4, 1.e-3, 1.e-9);\n  test_with_polynomial(1000, 5, 1.e-3, 1.e-8);\n  test_with_natural_boundary(10, 1.e-1);\n  test_with_natural_boundary(100, 1.e-5);\n  test_with_natural_boundary(1000, 1.e-9);\n\n  {\n    INFO(\"Test default-construction\");\n    const intrp::CubicSpline interpolant{};\n    test_serialization(interpolant);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_IrregularInterpolant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <numbers>\n#include <optional>\n#include <pup.h>\n#include <random>\n#include <string>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/DiskTestFunctions.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/FourierTestFunctions.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"PointwiseFunctions/MathFunctions/TensorProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Polynomial of given degree with leading coefficinet \\f$a_0\\f$, and with\n// \\f$a_{n+1} = a_n / falloff\nclass Polynomial {\n public:\n  Polynomial(const size_t degree, const double a_0, const double falloff)\n      : coefficients_(degree + 1) {\n    double n = falloff * a_0;\n    std::generate(coefficients_.begin(), coefficients_.end(), [&falloff, &n]() {\n      n /= falloff;\n      return n;\n    });\n  }\n  Polynomial() = default;\n  DataVector operator()(const DataVector& x) const {\n    return evaluate_polynomial(coefficients_, x);\n  }\n\n private:\n  std::vector<double> coefficients_;\n};\n\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\nconst double inertial_coord_min = -0.3;\nconst double inertial_coord_max = 0.7;\n\ntemplate <size_t VolumeDim>\nauto make_affine_map();\n\ntemplate <>\nauto make_affine_map<1>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max});\n}\n\ntemplate <>\nauto make_affine_map<2>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine2D{Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max},\n               Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max}});\n}\n\ntemplate <>\nauto make_affine_map<3>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine3D{Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max},\n               Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max},\n               Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max}});\n}\n\nnamespace TestTags {\n\ntemplate <typename DataType, size_t Dim>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim>;\n  static auto fill_values(const MathFunctions::TensorProduct<Dim>& f,\n                          const tnsr::I<DataVector, Dim>& x) {\n    auto result = make_with_value<tnsr::I<DataType, Dim>>(x, 0.);\n    const auto f_of_x = f(x);\n    for (size_t d = 0; d < Dim; ++d) {\n      result.get(d) = (d + 0.5) * get(f_of_x);\n    }\n    return result;\n  }\n};\n\ntemplate <typename DataType, size_t Dim>\nstruct SymmetricTensor : db::SimpleTag {\n  using type = tnsr::ii<DataType, Dim>;\n  static auto fill_values(const MathFunctions::TensorProduct<Dim>& f,\n                          const tnsr::I<DataVector, Dim>& x) {\n    auto result = make_with_value<tnsr::ii<DataType, Dim>>(x, 0.);\n    const auto f_of_x = f(x);\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = i; j < Dim; ++j) {  // Symmetry\n        result.get(i, j) = (static_cast<double>(i + j) + 0.33) * get(f_of_x);\n      }\n    }\n    return result;\n  }\n};\n\n}  // namespace TestTags\n\ntemplate <size_t Dim>\nvoid test_interpolate_to_points(const Mesh<Dim>& mesh) {\n  // Fill target interpolation coordinates with random values\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(inertial_coord_min, inertial_coord_max);\n\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_dist = make_not_null(&dist);\n\n  const size_t number_of_points = 6;\n  const auto target_x_inertial =\n      make_with_random_values<tnsr::I<DataVector, Dim>>(\n          nn_generator, nn_dist, DataVector(number_of_points));\n\n  const auto coordinate_map = make_affine_map<Dim>();\n  const auto target_x = [&target_x_inertial, &coordinate_map,\n                         &number_of_points]() {\n    tnsr::I<DataVector, Dim, Frame::ElementLogical> result(number_of_points);\n    for (size_t s = 0; s < number_of_points; ++s) {\n      tnsr::I<double, Dim> x_inertial_local{};\n      for (size_t d = 0; d < Dim; ++d) {\n        x_inertial_local.get(d) = target_x_inertial.get(d)[s];\n      }\n      const auto x_local = coordinate_map.inverse(x_inertial_local).value();\n      for (size_t d = 0; d < Dim; ++d) {\n        result.get(d)[s] = x_local.get(d);\n      }\n    }\n    return result;\n  }();\n\n  // Set up interpolator. Need do this only once.\n  const intrp::Irregular<Dim> irregular_interpolant(mesh, target_x);\n  test_serialization(irregular_interpolant);\n\n  // ... but we construct another interpolator to test operator!=\n  {\n    auto target_x_new = target_x;\n    target_x_new.get(0)[0] *= 0.98;  // Change one point slightly.\n    const intrp::Irregular<Dim> irregular_interpolant_new(mesh, target_x_new);\n    CHECK(irregular_interpolant_new != irregular_interpolant);\n  }\n\n  // ... and another to test the constructor from a single point\n  {\n    tnsr::I<double, Dim, Frame::ElementLogical> target_x_single{};\n    tnsr::I<DataVector, Dim, Frame::ElementLogical> target_x_single_dv(1_st);\n    for (size_t d = 0; d < Dim; ++d) {\n      target_x_single.get(d) = target_x.get(d)[0];\n      target_x_single_dv.get(d)[0] = target_x_single.get(d);\n    }\n    CHECK(intrp::Irregular<Dim>(mesh, target_x_single) ==\n          intrp::Irregular<Dim>(mesh, target_x_single_dv));\n  }\n\n  // Coordinates on the grid\n  const auto src_x = coordinate_map(logical_coordinates(mesh));\n\n  // Set up variables\n  using tags = tmpl::list<TestTags::Vector<DataVector, Dim>,\n                          TestTags::SymmetricTensor<DataVector, Dim>>;\n  Variables<tags> src_vars(mesh.number_of_grid_points());\n  Variables<tags> expected_dest_vars(number_of_points);\n\n  // We will make polynomials of the form x^a y^b z^c ...\n  // for all a,b,c, that result in exact interpolation.\n  // IndexIterator loops over \"a,b,c\"\n  for (IndexIterator<Dim> iter(mesh.extents()); iter; ++iter) {\n    // Set up analytic solution.  We fill a Variables with this solution,\n    // interpolate to arbitrary points, and then check that the\n    // values at arbitrary points match this solution.\n    // We choose polynomials so that interpolation is exact on an LGL grid.\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, Dim>\n        functions;\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(functions, d) =\n          std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(iter()[d]);\n    }\n    MathFunctions::TensorProduct<Dim> f(1.0, std::move(functions));\n\n    // Fill source and expected destination Variables with analytic solution.\n    tmpl::for_each<tags>([&f, &src_x, &target_x_inertial, &src_vars,\n                          &expected_dest_vars](auto tag) {\n      using Tag = tmpl::type_from<decltype(tag)>;\n      get<Tag>(src_vars) = Tag::fill_values(f, src_x);\n      get<Tag>(expected_dest_vars) = Tag::fill_values(f, target_x_inertial);\n    });\n\n    // Interpolate\n    // (g++ 7.2.0 does not allow `const auto dest_vars` here)\n    const Variables<tags> dest_vars =\n        irregular_interpolant.interpolate(src_vars);\n\n    CHECK_VARIABLES_APPROX(dest_vars, expected_dest_vars);\n\n    const DataVector result_dv = irregular_interpolant.interpolate(\n        get<0>(get<TestTags::Vector<DataVector, Dim>>(src_vars)));\n    CHECK_ITERABLE_APPROX(\n        result_dv,\n        get<0>(get<TestTags::Vector<DataVector, Dim>>(expected_dest_vars)));\n\n    {\n      INFO(\"Complex data\");\n      // Copy the real data above into a complex Variables\n      Variables<tmpl::list<TestTags::Vector<ComplexDataVector, Dim>>>\n          src_vars_complex(mesh.number_of_grid_points());\n      Variables<tmpl::list<TestTags::Vector<ComplexDataVector, Dim>>>\n          expected_complex(number_of_points);\n      const auto& src_vector = get<TestTags::Vector<DataVector, Dim>>(src_vars);\n      const auto& expected_vector =\n          get<TestTags::Vector<DataVector, Dim>>(expected_dest_vars);\n      for (size_t d = 0; d < Dim; ++d) {\n        for (size_t j = 0; j < src_vars.number_of_grid_points(); ++j) {\n          get<TestTags::Vector<ComplexDataVector, Dim>>(src_vars_complex)\n              .get(d)[j] = std::complex<double>(src_vector.get(d)[j],\n                                                2. * src_vector.get(d)[j]);\n        }\n        for (size_t j = 0; j < number_of_points; ++j) {\n          get<TestTags::Vector<ComplexDataVector, Dim>>(expected_complex)\n              .get(d)[j] = std::complex<double>(expected_vector.get(d)[j],\n                                                2. * expected_vector.get(d)[j]);\n        }\n      }\n      // Interpolate the complex data\n      const auto result_complex =\n          irregular_interpolant.interpolate(src_vars_complex);\n      CHECK_VARIABLES_APPROX(result_complex, expected_complex);\n      const auto result_cdv = irregular_interpolant.interpolate(get<0>(\n          get<TestTags::Vector<ComplexDataVector, Dim>>(src_vars_complex)));\n      CHECK_ITERABLE_APPROX(\n          result_cdv, get<0>(get<TestTags::Vector<ComplexDataVector, Dim>>(\n                          expected_complex)));\n      std::vector<std::complex<double>> result_data(expected_complex.size());\n      auto result_span = gsl::make_span(result_data);\n      irregular_interpolant.interpolate(\n          make_not_null(&result_span),\n          gsl::make_span(src_vars_complex.data(), src_vars_complex.size()));\n    }\n    {\n      INFO(\"Single precision data\");\n      // Copy the data above into single precision\n      std::vector<float> src_vars_single(src_vars.size());\n      std::vector<float> expected_dest_single(expected_dest_vars.size());\n      std::copy_n(src_vars.data(), src_vars.size(), src_vars_single.data());\n      std::copy_n(expected_dest_vars.data(), expected_dest_vars.size(),\n                  expected_dest_single.data());\n      // Interpolate the single precision data\n      std::vector<float> result_single(expected_dest_single.size());\n      auto result_single_span = gsl::make_span(result_single);\n      irregular_interpolant.interpolate(make_not_null(&result_single_span),\n                                        gsl::make_span(src_vars_single));\n      const Approx custom_approx =\n          Approx::custom()\n              .epsilon(std::numeric_limits<float>::epsilon() * 10.)\n              .scale(1.);\n      CHECK_ITERABLE_CUSTOM_APPROX(result_single, expected_dest_single,\n                                   custom_approx);\n    }\n  }\n}\n\ntemplate <Spectral::Basis Basis, Spectral::Quadrature Quadrature>\nvoid test_irregular_interpolant() {\n  const size_t start_points = 4;\n  const size_t end_points = 6;\n  for (size_t n0 = start_points; n0 < end_points; ++n0) {\n    test_interpolate_to_points<1>(Mesh<1>{n0, Basis, Quadrature});\n    for (size_t n1 = start_points; n1 < end_points; ++n1) {\n      test_interpolate_to_points<2>(Mesh<2>{{{n0, n1}}, Basis, Quadrature});\n      for (size_t n2 = start_points; n2 < end_points; ++n2) {\n        test_interpolate_to_points<3>(\n            Mesh<3>{{{n0, n1, n2}}, Basis, Quadrature});\n      }\n    }\n  }\n}\n\nvoid test_irregular_interpolant_mixed_quadrature() {\n  const size_t start_points = 4;\n  const size_t end_points = 6;\n  for (size_t n0 = start_points; n0 < end_points; ++n0) {\n    for (size_t n1 = start_points; n1 < end_points; ++n1) {\n      test_interpolate_to_points<2>(Mesh<2>{\n          {{n0, n1}},\n          {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n          {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss}}});\n      for (size_t n2 = start_points; n2 < end_points; ++n2) {\n        test_interpolate_to_points<3>(Mesh<3>{\n            {{n0, n1, n2}},\n            {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n              Spectral::Basis::Legendre}},\n            {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n              Spectral::Quadrature::GaussLobatto}}});\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nDomain<Dim> create_domain(double length,\n                          const std::array<size_t, Dim>& extents);\n\ntemplate <>\nDomain<3> create_domain<3>(const double length,\n                           const std::array<size_t, 3>& extents) {\n  const domain::creators::Brick creator{{{0.0, 0.0, 0.0}},\n                                        {{length, length, length}},\n                                        {{0, 0, 0}},\n                                        extents,\n                                        {{false, false, false}}};\n  return creator.create_domain();\n}\n\ntemplate <>\nDomain<2> create_domain<2>(const double length,\n                           const std::array<size_t, 2>& extents) {\n  const domain::creators::Rectangle creator{\n      {{0.0, 0.0}}, {{length, length}}, {{0, 0}}, extents, {{false, false}}};\n  return creator.create_domain();\n}\n\ntemplate <>\nDomain<1> create_domain<1>(const double length,\n                           const std::array<size_t, 1>& extents) {\n  const domain::creators::Interval creator{{{0.0}}, {{length}}, {{0}}, extents};\n  return creator.create_domain();\n}\n\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim, Frame::ElementLogical> create_target_points(\n    size_t n_random_target_points) {\n  tnsr::I<DataVector, Dim, Frame::ElementLogical> xi_target{\n      n_random_target_points + 2, -1.0};\n  for (size_t d = 0; d < Dim; ++d) {\n    xi_target.get(d)[1] = 1.0;\n  }\n  return xi_target;\n}\n\nnamespace Tags {\nstruct Scalar : ::db::SimpleTag {\n  using type = ::Scalar<DataVector>;\n};\n}  // namespace Tags\n\nusing var_tags = tmpl::list<Tags::Scalar>;\n\ntemplate <size_t Dim>\nVariables<var_tags> polynomial(\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& x, const size_t degree) {\n  tnsr::I<DataVector, Dim, Frame::Inertial> v = x;\n  for (size_t d = 0; d < Dim; ++d) {\n    for (size_t n = degree; n > 1; --n) {\n      v.get(d) *= (x.get(d) + 1.0);\n    }\n  }\n  Variables<var_tags> result{get<0>(x).size()};\n  get(get<Tags::Scalar>(result)) = get<0>(v);\n  for (size_t d = 1; d < Dim; ++d) {\n    get(get<Tags::Scalar>(result)) *= v.get(d);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test_polynomial_interpolant(const std::array<size_t, Dim>& extents,\n                                 const size_t max_degree) {\n  const size_t n_random_target_points = 10;\n  // use a small domain to avoid huge polynomial values for large max_degree\n  // which results in large absolute errors\n  const auto domain = create_domain<Dim>(1.3, extents);\n  const auto& block = domain.blocks()[0];\n  const ElementMap<Dim, Frame::Inertial> element_map{\n      ElementId<Dim>{0}, block.stationary_map().get_clone()};\n  Mesh<Dim> mesh(extents, Spectral::Basis::FiniteDifference,\n                 Spectral::Quadrature::CellCentered);\n\n  const auto source_xi = logical_coordinates(mesh);\n  const auto source_x = element_map(source_xi);\n  const auto target_xi = create_target_points<Dim>(n_random_target_points);\n  const auto target_x = element_map(target_xi);\n  const intrp::Irregular irregular_interp{mesh, target_xi,\n                                          std::optional<size_t>{max_degree}};\n\n  for (size_t degree = 0; degree <= max_degree; ++degree) {\n    const auto source_vars = polynomial<Dim>(source_x, degree);\n    const auto target_vars = irregular_interp.interpolate(source_vars);\n    const auto expected_vars = polynomial<Dim>(target_x, degree);\n    CAPTURE(Dim);\n    CAPTURE(max_degree);\n    CAPTURE(degree);\n    CHECK_VARIABLES_APPROX(target_vars, expected_vars);\n  }\n}\n\nvoid test_tov(const size_t max_degree, const bool specified_interp_order) {\n  const std::array<size_t, 3> isotropic_extents{{15, 15, 15}};\n  constexpr size_t n_resolutions = 4;\n  auto errors =\n      make_array<n_resolutions>(std::numeric_limits<double>::signaling_NaN());\n  const double central_density = 1.28e-3;\n  for (size_t i = 0; i < n_resolutions; ++i) {\n    const Domain<3> domain = create_domain<3>(\n        6.6666666666666666666 / two_to_the(i), isotropic_extents);\n    const Block<3>& cube = domain.blocks()[0];\n    Mesh<3> mesh(isotropic_extents, Spectral::Basis::FiniteDifference,\n                 Spectral::Quadrature::CellCentered);\n    const auto xi = logical_coordinates(mesh);\n    const ElementMap<3, Frame::Inertial> element_map{\n        ElementId<3>{0}, cube.stationary_map().get_clone()};\n    const auto x = element_map(xi);\n\n    RelativisticEuler::Solutions::TovStar tov_star(\n        central_density,\n        std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0));\n\n    using rho_tag = hydro::Tags::RestMassDensity<DataVector>;\n    auto vars = variables_from_tagged_tuple(\n        tov_star.variables(x, 0.0, tmpl::list<rho_tag>{}));\n\n    const tnsr::I<DataVector, 3, Frame::ElementLogical> xi_target{1_st, -1.0};\n    intrp::Irregular irregular_interp{mesh, xi_target};\n    if (specified_interp_order) {\n      irregular_interp =\n          intrp::Irregular{mesh, xi_target, std::optional<size_t>{max_degree}};\n    }\n\n    const auto target_vars = irregular_interp.interpolate(vars);\n    gsl::at(errors, i) =\n        fabs(central_density - get(get<rho_tag>(target_vars))[0]);\n  }\n\n  std::reverse(std::begin(errors), std::end(errors));\n  auto ratio_of_errors =\n      make_array<n_resolutions>(std::numeric_limits<double>::signaling_NaN());\n  std::adjacent_difference(std::begin(errors), std::end(errors),\n                           std::begin(ratio_of_errors), std::divides<>{});\n\n  Approx custom_approx = Approx::custom().epsilon(1.e-2).scale(1.);\n  for (size_t i = 1; i < n_resolutions; ++i) {\n    CAPTURE(max_degree);\n    CAPTURE(i);\n    // since \\rho is a symmetric function across the center, quadratic\n    // extrapolation has one order higher convergence rate at the center\n    CHECK((specified_interp_order\n               ? (max_degree == 2 ? 16.0 : two_to_the(max_degree + 1))\n               : 4.0) == custom_approx(gsl::at(ratio_of_errors, i)));\n  }\n}\n\nvoid test_2d_spherical(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 2, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = acos(make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target));\n    get<1>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    for (size_t n_z = 0; n_z < 4; ++n_z) {\n      for (size_t n_y = 0; n_y < 4; ++n_y) {\n        for (size_t n_x = 0; n_x < 4; ++n_x) {\n          const Mesh<2> source_mesh{\n              std::array{n_x + n_y + n_z + 2, 2 * (n_x + n_y) + 3},\n              std::array{Spectral::Basis::SphericalHarmonic,\n                         Spectral::Basis::SphericalHarmonic},\n              std::array{Spectral::Quadrature::Gauss,\n                         Spectral::Quadrature::Equiangular}};\n          const YlmTestFunctions::ProductOfPolynomials f(n_x, n_y, n_z);\n          const auto xi_source = logical_coordinates(source_mesh);\n          const DataVector f_source = f(xi_source);\n          const DataVector f_expected = f(xi_target);\n          const intrp::Irregular<2> interpolator(source_mesh, xi_target);\n          const DataVector f_interpolated = interpolator.interpolate(f_source);\n          CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n        }\n      }\n    }\n  }\n}\n\nvoid test_3d_spherical(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 3, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    get<1>(xi_target) = acos(make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target));\n    get<2>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    for (size_t n_r = 2; n_r < 4; ++n_r) {\n      for (size_t n_z = 0; n_z < 4; ++n_z) {\n        for (size_t n_y = 0; n_y < 4; ++n_y) {\n          for (size_t n_x = 0; n_x < 4; ++n_x) {\n            const Mesh<3> source_mesh{\n                std::array{n_r, n_x + n_y + n_z + 2, 2 * (n_x + n_y) + 3},\n                std::array{Spectral::Basis::Legendre,\n                           Spectral::Basis::SphericalHarmonic,\n                           Spectral::Basis::SphericalHarmonic},\n                std::array{Spectral::Quadrature::GaussLobatto,\n                           Spectral::Quadrature::Gauss,\n                           Spectral::Quadrature::Equiangular}};\n            const Polynomial f_r{n_r - 1, 1.5, 2.0};\n            const YlmTestFunctions::ProductOfPolynomials f_a(n_x, n_y, n_z);\n            const auto xi_source = logical_coordinates(source_mesh);\n            const DataVector f_source =\n                f_r(get<0>(xi_source)) *\n                f_a(get<1>(xi_source), get<2>(xi_source));\n            const DataVector f_expected =\n                f_r(get<0>(xi_target)) *\n                f_a(get<1>(xi_target), get<2>(xi_target));\n            const intrp::Irregular<3> interpolator(source_mesh, xi_target);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_2d_disk(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 2, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    get<1>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    for (size_t n_y = 0; n_y < 4; ++n_y) {\n      CAPTURE(n_y);\n      for (size_t n_x = 0; n_x < 4; ++n_x) {\n        CAPTURE(n_x);\n        const Mesh<2> source_mesh{\n            n_x + n_y == 0 ? std::array{1_st, 1_st}\n                           : std::array{n_x + n_y + 1, 2 * (n_x + n_y) + 1},\n            std::array{Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2},\n            std::array{Spectral::Quadrature::GaussRadauUpper,\n                       Spectral::Quadrature::Equiangular}};\n        const DiskTestFunctions::ProductOfPolynomials f{n_x, n_y};\n        const auto xi_source = logical_coordinates(source_mesh);\n        const DataVector f_source =\n            f(0.5 * (get<0>(xi_source) + 1.0), get<1>(xi_source));\n        const DataVector f_expected =\n            f(0.5 * (get<0>(xi_target) + 1.0), get<1>(xi_target));\n        const intrp::Irregular<2> interpolator(source_mesh, xi_target);\n        const DataVector f_interpolated = interpolator.interpolate(f_source);\n        CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n      }\n    }\n  }\n}\n\nvoid test_3d_cylinder(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 3, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    get<1>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    get<2>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    for (size_t n_z = 2; n_z < 4; ++n_z) {\n      CAPTURE(n_z);\n      for (size_t n_y = 0; n_y < 4; ++n_y) {\n        CAPTURE(n_y);\n        for (size_t n_x = 0; n_x < 4; ++n_x) {\n          CAPTURE(n_x);\n          const Mesh<3> source_mesh{\n              n_x + n_y == 0\n                  ? std::array{1_st, 1_st, n_z}\n                  : std::array{n_x + n_y + 1, 2 * (n_x + n_y) + 1, n_z},\n              std::array{Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2,\n                         Spectral::Basis::Legendre},\n              std::array{Spectral::Quadrature::GaussRadauUpper,\n                         Spectral::Quadrature::Equiangular,\n                         Spectral::Quadrature::GaussLobatto}};\n          const DiskTestFunctions::ProductOfPolynomials f{n_x, n_y};\n          const Polynomial f_z{n_z - 1, 1.5, 2.0};\n          const auto xi_source = logical_coordinates(source_mesh);\n          const DataVector f_source =\n              f(0.5 * (get<0>(xi_source) + 1.0), get<1>(xi_source)) *\n              f_z(get<2>(xi_source));\n          const DataVector f_expected =\n              f(0.5 * (get<0>(xi_target) + 1.0), get<1>(xi_target)) *\n              f_z(get<2>(xi_target));\n          const intrp::Irregular<3> interpolator(source_mesh, xi_target);\n          const DataVector f_interpolated = interpolator.interpolate(f_source);\n          CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n        }\n      }\n    }\n  }\n}\n\nvoid test_2d_hollow_disk(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0,\n                                                    2.0 * std::numbers::pi);\n  const Approx custom_approx = Approx::custom().epsilon(1e-12).scale(1.);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 2, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    get<1>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    for (size_t n_r = 2; n_r < 4; ++n_r) {\n      CAPTURE(n_r);\n      for (size_t n_y = 0; n_y < 4; ++n_y) {\n        CAPTURE(n_y);\n        for (size_t n_x = 0; n_x < 4; ++n_x) {\n          CAPTURE(n_x);\n          const Mesh<2> source_mesh{\n              std::array{n_r, 2 * (n_x + n_y) + 1},\n              std::array{Spectral::Basis::Legendre, Spectral::Basis::Fourier},\n              std::array{Spectral::Quadrature::GaussLobatto,\n                         Spectral::Quadrature::Equiangular}};\n          const Polynomial f_r{n_r - 1, 1.5, 2.0};\n          const FourierTestFunctions::ProductOfPolynomials f_m{n_x, n_y};\n          const auto xi_source = logical_coordinates(source_mesh);\n          const DataVector f_source =\n              f_r(get<0>(xi_source)) * f_m(get<1>(xi_source));\n          const DataVector f_expected =\n              f_r(get<0>(xi_target)) * f_m(get<1>(xi_target));\n          const intrp::Irregular<2> interpolator(source_mesh, xi_target);\n          const DataVector f_interpolated = interpolator.interpolate(f_source);\n          CHECK_ITERABLE_CUSTOM_APPROX(f_interpolated, f_expected,\n                                       custom_approx);\n        }\n      }\n    }\n  }\n}\n\nvoid test_3d_hollow_cylinder(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  std::uniform_real_distribution<> phi_distribution(0.0,\n                                                    2.0 * std::numbers::pi);\n  const Approx custom_approx = Approx::custom().epsilon(1e-12).scale(1.);\n  for (size_t n_target_points = 1; n_target_points < 13;\n       n_target_points += 11) {\n    tnsr::I<DataVector, 3, Frame::ElementLogical> xi_target{n_target_points};\n    get<0>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    get<1>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), xi_target);\n    get<2>(xi_target) = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), xi_target);\n    for (size_t n_r = 2; n_r < 4; ++n_r) {\n      CAPTURE(n_r);\n      for (size_t n_y = 0; n_y < 4; ++n_y) {\n        CAPTURE(n_y);\n        for (size_t n_x = 0; n_x < 4; ++n_x) {\n          CAPTURE(n_x);\n          for (size_t n_z = 2; n_z < 4; ++n_z) {\n            CAPTURE(n_z);\n            const Mesh<3> source_mesh{\n                std::array{n_r, 2 * (n_x + n_y) + 1, n_z},\n                std::array{Spectral::Basis::Legendre, Spectral::Basis::Fourier,\n                           Spectral::Basis::Legendre},\n                std::array{Spectral::Quadrature::GaussLobatto,\n                           Spectral::Quadrature::Equiangular,\n                           Spectral::Quadrature::GaussLobatto}};\n            const Polynomial f_r{n_r - 1, 1.5, 2.0};\n            const Polynomial f_z{n_z - 1, 1.6, 2.0};\n            const FourierTestFunctions::ProductOfPolynomials f_m{n_x, n_y};\n            const auto xi_source = logical_coordinates(source_mesh);\n            const DataVector f_source = f_r(get<0>(xi_source)) *\n                                        f_m(get<1>(xi_source)) *\n                                        f_z(get<2>(xi_source));\n            const DataVector f_expected = f_r(get<0>(xi_target)) *\n                                          f_m(get<1>(xi_target)) *\n                                          f_z(get<2>(xi_target));\n            const intrp::Irregular<3> interpolator(source_mesh, xi_target);\n            const DataVector f_interpolated =\n                interpolator.interpolate(f_source);\n            CHECK_ITERABLE_CUSTOM_APPROX(f_interpolated, f_expected,\n                                         custom_approx);\n          }\n        }\n      }\n    }\n  }\n}\n\n// Test that the Cartoon-basis interpolation in 3D delegates correctly to the\n// lower-dimensional interpolator.  For the FD+Cartoon (axisymmetric) case the\n// 3D result must match an Irregular<2> built from slice_through(0,1).  For the\n// Cartoon+Cartoon (spherically-symmetric) case it must match an Irregular<1>\n// built from slice_through(0).\nvoid test_cartoon_fd(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  const size_t n_target = 7;\n  {\n    INFO(\"FD+FD+Cartoon matches Irregular<2> on slice_through(0,1)\");\n    for (size_t n0 = 4; n0 <= 6; ++n0) {\n      for (size_t n1 = 4; n1 <= 6; ++n1) {\n        CAPTURE(n0);\n        CAPTURE(n1);\n        const Mesh<3> mesh3{\n            {{n0, n1, 1}},\n            {{Spectral::Basis::FiniteDifference,\n              Spectral::Basis::FiniteDifference, Spectral::Basis::Cartoon}},\n            {{Spectral::Quadrature::CellCentered,\n              Spectral::Quadrature::CellCentered,\n              Spectral::Quadrature::AxialSymmetry}}};\n        const Mesh<2> mesh2 = mesh3.slice_through(0, 1);\n\n        // Target points: xi2 is always zero (guaranteed by cartoon symmetry)\n        tnsr::I<DataVector, 3, Frame::ElementLogical> xi3{n_target};\n        get<0>(xi3) = make_with_random_values<DataVector>(\n            generator, make_not_null(&dist), DataVector(n_target));\n        get<1>(xi3) = make_with_random_values<DataVector>(\n            generator, make_not_null(&dist), DataVector(n_target));\n        get<2>(xi3) = DataVector(n_target, 0.0);\n\n        tnsr::I<DataVector, 2, Frame::ElementLogical> xi2{n_target};\n        get<0>(xi2) = get<0>(xi3);\n        get<1>(xi2) = get<1>(xi3);\n\n        // Source data: f(xi, eta) = (1 + xi) * (2 + eta) on the 2D slice,\n        // tiled trivially into 3D (only one point in dim 2)\n        const auto xi_src3 = logical_coordinates(mesh3);\n        DataVector f3(mesh3.number_of_grid_points());\n        for (size_t s = 0; s < mesh3.number_of_grid_points(); ++s) {\n          f3[s] = (1.0 + get<0>(xi_src3)[s]) * (2.0 + get<1>(xi_src3)[s]);\n        }\n        // The 2D slice has the same data layout (dim2 has only one point)\n        const DataVector f2(f3.data(), mesh2.number_of_grid_points());\n\n        const intrp::Irregular<3> interp3(mesh3, xi3);\n        const intrp::Irregular<2> interp2(mesh2, xi2);\n        CHECK_ITERABLE_APPROX(interp3.interpolate(f3), interp2.interpolate(f2));\n\n        tnsr::I<double, 3, Frame::ElementLogical> xi3_double{1};\n        tnsr::I<double, 2, Frame::ElementLogical> xi2_double{1};\n        get<0>(xi3_double) = get<0>(xi2_double) = get<0>(xi3)[0];\n        get<1>(xi3_double) = get<1>(xi2_double) = get<1>(xi3)[0];\n        get<2>(xi3_double) = get<2>(xi3)[0];\n        const intrp::Irregular<3> interp3_double(mesh3, xi3_double);\n        const intrp::Irregular<2> interp2_double(mesh2, xi2_double);\n        CHECK_ITERABLE_APPROX(interp3_double.interpolate(f3),\n                              interp2_double.interpolate(f2));\n      }\n    }\n  }\n  {\n    INFO(\"FD+Cartoon+Cartoon matches Irregular<1> on slice_through(0)\");\n    for (size_t n0 = 4; n0 <= 6; ++n0) {\n      CAPTURE(n0);\n      const Mesh<3> mesh3{\n          {{n0, 1, 1}},\n          {{Spectral::Basis::FiniteDifference, Spectral::Basis::Cartoon,\n            Spectral::Basis::Cartoon}},\n          {{Spectral::Quadrature::CellCentered,\n            Spectral::Quadrature::SphericalSymmetry,\n            Spectral::Quadrature::SphericalSymmetry}}};\n      const Mesh<1> mesh1 = mesh3.slice_through(0);\n\n      tnsr::I<DataVector, 3, Frame::ElementLogical> xi3{n_target};\n      get<0>(xi3) = make_with_random_values<DataVector>(\n          generator, make_not_null(&dist), DataVector(n_target));\n      get<1>(xi3) = DataVector(n_target, 0.0);\n      get<2>(xi3) = DataVector(n_target, 0.0);\n\n      tnsr::I<DataVector, 1, Frame::ElementLogical> xi1{n_target};\n      get<0>(xi1) = get<0>(xi3);\n\n      const auto xi_src3 = logical_coordinates(mesh3);\n      DataVector f3(mesh3.number_of_grid_points());\n      for (size_t s = 0; s < mesh3.number_of_grid_points(); ++s) {\n        f3[s] = 1.0 + 2.0 * get<0>(xi_src3)[s];\n      }\n      const DataVector f1(f3.data(), mesh1.number_of_grid_points());\n\n      const intrp::Irregular<3> interp3(mesh3, xi3);\n      const intrp::Irregular<1> interp1(mesh1, xi1);\n      CHECK_ITERABLE_APPROX(interp3.interpolate(f3), interp1.interpolate(f1));\n\n      tnsr::I<double, 3, Frame::ElementLogical> xi3_double{1};\n      tnsr::I<double, 1, Frame::ElementLogical> xi1_double{1};\n      get<0>(xi3_double) = get<0>(xi1_double) = get<0>(xi3)[0];\n      get<1>(xi3_double) = get<1>(xi3)[0];\n      get<2>(xi3_double) = get<2>(xi3)[0];\n      const intrp::Irregular<3> interp3_double(mesh3, xi3_double);\n      const intrp::Irregular<1> interp1_double(mesh1, xi1_double);\n      CHECK_ITERABLE_APPROX(interp3_double.interpolate(f3),\n                            interp1_double.interpolate(f1));\n    }\n  }\n}\n\nvoid test_cartoon_spherical(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n\n  const size_t number_of_points = 6;\n  tnsr::I<DataVector, 3, Frame::ElementLogical> xi_target{number_of_points};\n  get<0>(xi_target) = make_with_random_values<DataVector>(\n      generator, make_not_null(&xi_distribution), xi_target);\n  get<1>(xi_target) = DataVector(number_of_points, 0.0);\n  get<2>(xi_target) = DataVector(number_of_points, 0.0);\n  for (size_t n_x = 4; n_x < 6; ++n_x) {\n    const Mesh<3> source_mesh{\n        {{n_x, 1, 1}},\n        {{Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n          Spectral::Basis::Cartoon}},\n        {{Spectral::Quadrature::GaussLobatto,\n          Spectral::Quadrature::SphericalSymmetry,\n          Spectral::Quadrature::SphericalSymmetry}}};\n    const Polynomial f_r{n_x - 1, 1.5, 2.0};\n    const auto xi_source = logical_coordinates(source_mesh);\n    const DataVector f_source = f_r(get<0>(xi_source));\n    const DataVector f_expected = f_r(get<0>(xi_target));\n    const intrp::Irregular<3> interpolator(source_mesh, xi_target);\n    const DataVector f_interpolated = interpolator.interpolate(f_source);\n    CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n  }\n}\n\nvoid test_cartoon_axial(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n\n  const size_t number_of_points = 6;\n  tnsr::I<DataVector, 3, Frame::ElementLogical> xi_target{number_of_points};\n  get<0>(xi_target) = make_with_random_values<DataVector>(\n      generator, make_not_null(&xi_distribution), xi_target);\n  get<1>(xi_target) = make_with_random_values<DataVector>(\n      generator, make_not_null(&xi_distribution), xi_target);\n  get<2>(xi_target) = DataVector(number_of_points, 0.0);\n  for (size_t n_x = 4; n_x < 6; ++n_x) {\n    for (size_t n_y = 4; n_y < 6; ++n_y) {\n      const Mesh<3> source_mesh{\n          {{n_x, n_y, 1}},\n          {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n            Spectral::Basis::Cartoon}},\n          {{Spectral::Quadrature::GaussLobatto,\n            Spectral::Quadrature::GaussLobatto,\n            Spectral::Quadrature::AxialSymmetry}}};\n      const Polynomial f_x{n_x - 1, 1.5, 2.0};\n      const Polynomial f_y{n_y - 1, 2.5, 1.5};\n      const auto xi_source = logical_coordinates(source_mesh);\n      const DataVector f_source =\n          f_x(get<0>(xi_source)) * f_y(get<1>(xi_source));\n      const DataVector f_expected =\n          f_x(get<0>(xi_target)) * f_y(get<1>(xi_target));\n      const intrp::Irregular<3> interpolator(source_mesh, xi_target);\n      const DataVector f_interpolated = interpolator.interpolate(f_source);\n      CHECK_ITERABLE_APPROX(f_interpolated, f_expected);\n    }\n  }\n}\n\n#ifdef SPECTRE_DEBUG\nvoid test_errors() {\n  const tnsr::I<DataVector, 2, Frame::ElementLogical> target_coords_2d{\n      {{{0.5, 1.0}, {0.0, 1.5}}}};\n  const tnsr::I<DataVector, 3, Frame::ElementLogical> target_coords_3d{\n      {{{0.5, 1.0}, {0.0, 1.5}, {0.1, 0.8}}}};\n  {\n    INFO(\"Testing SphericalHarmonic basis consistency assertion for 2D\");\n    CHECK_THROWS_WITH(\n        (intrp::Irregular<2>{\n            Mesh<2>{\n                {3, 5},\n                {Spectral::Basis::SphericalHarmonic, Spectral::Basis::Legendre},\n                {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss}},\n            target_coords_2d}),\n        Catch::Matchers::ContainsSubstring(\n            \"Expected both dimensions to have spherical harmonic basis\"));\n  }\n  {\n    INFO(\"Testing ZernikeB2 basis consistency assertion for 2D\");\n    CHECK_THROWS_WITH(\n        (intrp::Irregular<2>{\n            Mesh<2>{{3, 3},\n                    {Spectral::Basis::ZernikeB2, Spectral::Basis::Legendre},\n                    {Spectral::Quadrature::GaussRadauUpper,\n                     Spectral::Quadrature::Gauss}},\n            target_coords_2d}),\n        Catch::Matchers::ContainsSubstring(\"Unexpected basis combination\"));\n  }\n  {\n    INFO(\"Testing SphericalHarmonic basis consistency assertion for 3D\");\n    CHECK_THROWS_WITH(\n        (intrp::Irregular<3>{\n            Mesh<3>{\n                {3, 3, 5},\n                {Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n                 Spectral::Basis::Legendre},\n                {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss,\n                 Spectral::Quadrature::Gauss}},\n            target_coords_3d}),\n        Catch::Matchers::ContainsSubstring(\n            \"Expected last two dimensions to each have spherical harmonic \"\n            \"basis\"));\n  }\n  {\n    INFO(\"Testing ZernikeB2 basis consistency assertion for 3D\");\n    CHECK_THROWS_WITH(\n        (intrp::Irregular<3>{\n            Mesh<3>{{3, 3, 3},\n                    {Spectral::Basis::ZernikeB2, Spectral::Basis::Legendre,\n                     Spectral::Basis::Legendre},\n                    {Spectral::Quadrature::GaussRadauUpper,\n                     Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss}},\n            target_coords_3d}),\n        Catch::Matchers::ContainsSubstring(\"Unexpected basis combination\"));\n  }\n  {\n    INFO(\"Testing N_phi odd assertion for ZernikeB2\");\n    CHECK_THROWS_WITH(\n        (intrp::Irregular<2>{\n            Mesh<2>{{3, 4},\n                    {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2},\n                    {Spectral::Quadrature::GaussRadauUpper,\n                     Spectral::Quadrature::Equiangular}},\n            target_coords_2d}),\n        Catch::Matchers::ContainsSubstring(\n            \"Need N_phi to be odd for stability\"));\n  }\n  {\n    INFO(\"Testing mixed FD and DG bases assertion for 2D\");\n    CHECK_THROWS_WITH(\n        (intrp::Irregular<2>{Mesh<2>{{3, 3},\n                                     {Spectral::Basis::FiniteDifference,\n                                      Spectral::Basis::Legendre},\n                                     {Spectral::Quadrature::FaceCentered,\n                                      Spectral::Quadrature::Gauss}},\n                             target_coords_2d}),\n        Catch::Matchers::ContainsSubstring(\n            \"Mixed FD and DG bases are not supported\"));\n  }\n  {\n    INFO(\"Testing fd_to_fd_interp_order nullopt assertion for non-FD mesh\");\n    CHECK_THROWS_WITH(\n        (intrp::Irregular<2>{\n            Mesh<2>{{3, 3},\n                    {Spectral::Basis::Legendre, Spectral::Basis::Legendre},\n                    {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss}},\n            target_coords_2d, std::optional<size_t>{2}}),\n        Catch::Matchers::ContainsSubstring(\n            \"fd_to_fd_interp_order only applies to FD meshes\"));\n  }\n  {\n    INFO(\"Testing mixed FD and DG bases assertion for 3D\");\n    CHECK_THROWS_WITH(\n        (intrp::Irregular<3>{Mesh<3>{{3, 3, 3},\n                                     {Spectral::Basis::FiniteDifference,\n                                      Spectral::Basis::FiniteDifference,\n                                      Spectral::Basis::Legendre},\n                                     {Spectral::Quadrature::FaceCentered,\n                                      Spectral::Quadrature::FaceCentered,\n                                      Spectral::Quadrature::Gauss}},\n                             target_coords_3d}),\n        Catch::Matchers::ContainsSubstring(\n            \"Mixed FD and DG bases are not supported\"));\n  }\n}\n#endif\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.IrregularInterpolant\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test_irregular_interpolant<Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto>();\n  test_irregular_interpolant<Spectral::Basis::Legendre,\n                             Spectral::Quadrature::Gauss>();\n  test_irregular_interpolant_mixed_quadrature();\n  for (size_t max_degree = 1; max_degree <= 3; ++max_degree) {\n    test_polynomial_interpolant<1>({{11}}, max_degree);\n    test_polynomial_interpolant<2>({{11, 11}}, max_degree);\n    test_polynomial_interpolant<2>({{11, 9}}, max_degree);\n    test_polynomial_interpolant<3>({{11, 11, 11}}, max_degree);\n    test_polynomial_interpolant<3>({{11, 9, 11}}, max_degree);\n    test_polynomial_interpolant<3>({{11, 11, 9}}, max_degree);\n    test_polynomial_interpolant<3>({{11, 9, 9}}, max_degree);\n    test_polynomial_interpolant<3>({{11, 9, 13}}, max_degree);\n    for (const bool specified_interp_order : {false, true}) {\n      test_tov(max_degree, specified_interp_order);\n    }\n  }\n  MAKE_GENERATOR(generator);\n  test_2d_spherical(make_not_null(&generator));\n  test_3d_spherical(make_not_null(&generator));\n  test_2d_disk(make_not_null(&generator));\n  test_3d_cylinder(make_not_null(&generator));\n  test_2d_hollow_disk(make_not_null(&generator));\n  test_3d_hollow_cylinder(make_not_null(&generator));\n  test_cartoon_spherical(make_not_null(&generator));\n  test_cartoon_axial(make_not_null(&generator));\n  test_cartoon_fd(make_not_null(&generator));\n#ifdef SPECTRE_DEBUG\n  test_errors();\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_LagrangePolynomial.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"NumericalAlgorithms/Interpolation/LagrangePolynomial.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.LagrangePolynomial\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  std::array<double, 4> control{{-1., 0., 1.5, 6.}};\n\n  for (size_t i = 0; i < control.size(); ++i) {\n    for (size_t j = 0; j < control.size(); ++j) {\n      CHECK(lagrange_polynomial(j, gsl::at(control, i),\n                                control.begin(), control.end())\n            == approx(i == j ? 1. : 0.));\n      CHECK(lagrange_polynomial(control.begin() + static_cast<ptrdiff_t>(j),\n                                gsl::at(control, i),\n                                control.begin(),\n                                control.end()) ==\n            approx(i == j ? 1. : 0.));\n    }\n  }\n\n  // Check interpolating a cubic\n  const auto func =\n      [](double x) { return 1.2 + x * (2.3 + x * (3.4 + x * 4.5)); };\n  const double test_point = 2.7;\n  double interpolated = 0.;\n  for (auto it = control.begin(); it != control.end(); ++it) {\n    interpolated +=\n        func(*it) * lagrange_polynomial(it, test_point, control.begin(),\n                                        control.end());\n  }\n  CHECK(interpolated == approx(func(test_point)));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_LinearLeastSquares.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearLeastSquares.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace {\ntemplate <size_t Order>\nvoid test_linear_least_squares_double(\n    const std::array<double, Order + 1>& coeffs,\n    const std::vector<double>& x_values) {\n  std::vector<double> y_values{};\n  for (size_t i = 0; i < x_values.size(); i++) {\n    double y_i = 0;\n    for (size_t j = 0; j < Order + 1; j++) {\n      y_i += gsl::at(coeffs, j) * pow(gsl::at(x_values, i), j);\n    }\n    y_values.push_back(y_i);\n  }\n\n  // Check that the coeffs determined match the ones used to\n  // produce the data points.\n  Approx my_approx = Approx::custom().epsilon(1.e-11).scale(1.0);\n  const std::array<double, Order + 1> computed_coeffs =\n      intrp::linear_least_squares<Order>(x_values, y_values);\n  CHECK_ITERABLE_CUSTOM_APPROX(coeffs, computed_coeffs, my_approx);\n  for (size_t i = 0; i < x_values.size(); i++) {\n    CHECK(evaluate_polynomial(computed_coeffs, gsl::at(x_values, i)) ==\n          my_approx(gsl::at(y_values, i)));\n  }\n}\n\nvoid test_linear_least_squares_datavector() {\n  const DataVector x_values{1.0, 2.0, 3.0, 4.0, 5.0};\n  const std::vector<std::array<double, 2>> coefficients{\n      {{6.0, 7.0}}, {{8.0, 9.0}}, {{10.0, 11.0}}, {{12.0, 13.0}}};\n  std::vector<DataVector> y_values{4, DataVector{5}};\n  for (size_t i = 0; i < y_values.size(); ++i) {\n    y_values[i] = x_values * coefficients[i][1] + coefficients[i][0];\n  }\n  const std::vector<std::array<double, 2>> computed_coefficients =\n      intrp::linear_least_squares<1>(x_values, y_values);\n  CHECK_ITERABLE_APPROX(coefficients, computed_coefficients);\n}\n\ntemplate <size_t Order>\nvoid test_linear_least_squares_datavector2(\n    const std::vector<std::array<double, Order + 1>>& coeffs,\n    const DataVector& x_values) {\n  std::vector<DataVector> y_values{coeffs.size(),\n                                   DataVector{x_values.size(), 0.0}};\n  for (size_t i = 0; i < y_values.size(); ++i) {\n    y_values[i] = evaluate_polynomial(coeffs[i], x_values);\n  }\n  const std::vector<std::array<double, Order + 1>> computed_coefficients =\n      intrp::linear_least_squares<Order>(x_values, y_values);\n  Approx my_approx = Approx::custom().epsilon(1.e-11).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(coeffs, computed_coefficients, my_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.LinearLeastSquares\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> coeff_dis(-10.0, 10.0);\n  const double coeff0 = coeff_dis(gen);\n  CAPTURE(coeff0);\n  const double coeff1 = coeff_dis(gen);\n  CAPTURE(coeff1);\n  const double coeff2 = coeff_dis(gen);\n  CAPTURE(coeff2);\n  const double coeff3 = coeff_dis(gen);\n  CAPTURE(coeff3);\n  const double coeff4 = coeff_dis(gen);\n  CAPTURE(coeff4);\n  const double coeff5 = coeff_dis(gen);\n  CAPTURE(coeff5);\n  const double coeff6 = coeff_dis(gen);\n  CAPTURE(coeff6);\n  const double coeff7 = coeff_dis(gen);\n  CAPTURE(coeff7);\n  const double coeff8 = coeff_dis(gen);\n  CAPTURE(coeff8);\n  const double coeff9 = coeff_dis(gen);\n  CAPTURE(coeff9);\n  const double coeff10 = coeff_dis(gen);\n  CAPTURE(coeff10);\n  const double coeff11 = coeff_dis(gen);\n  CAPTURE(coeff11);\n  const std::array<double, 4> coeffs = {coeff0, coeff1, coeff2, coeff3};\n  const std::vector<std::array<double, 4>> datavector_coeffs = {\n      {{coeff0, coeff1, coeff2, coeff3}},\n      {{coeff4, coeff5, coeff6, coeff7}},\n      {{coeff8, coeff9, coeff10, coeff11}}};\n  const std::vector<double> vecx = {0.0, 1.0, 3.0, 2.0, 4.0,\n                                    5.0, 6.0, 8.0, 7.0, 9.0};\n  const DataVector datavecx = {0.0, 1.0, 2.0, 3.0, 4.0,\n                               5.0, 8.0, 7.0, 6.0, 9.0};\n\n  test_linear_least_squares_double<3>(coeffs, vecx);\n  test_linear_least_squares_datavector();\n  test_linear_least_squares_datavector2<3>(datavector_coeffs, datavecx);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_LinearRegression.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearRegression.hpp\"\n\nnamespace {\n\nvoid test_linear_regression_datavector(const double expected_intercept,\n                                       const double expected_slope) {\n  const DataVector x_values{1.0, 2.0, 3.0, 4.0, 5.0};\n  const DataVector y_values = expected_slope * x_values + expected_intercept;\n  const auto result = intrp::linear_regression(x_values, y_values);\n\n  CHECK(result.intercept == approx(expected_intercept));\n  CHECK(result.slope == approx(expected_slope));\n  // Error bars are zero because we have exactly a straight line.\n  CHECK(result.delta_intercept == approx(0.0));\n  CHECK(result.delta_slope == approx(0.0));\n}\n\nvoid test_linear_regression_stdvectordouble(const double expected_intercept,\n                                            const double expected_slope) {\n  const std::vector<double> x_values{1.0, 2.0, 3.0, 4.0, 5.0};\n  std::vector<double> y_values;\n  for (size_t i = 0; i < x_values.size(); ++i) {\n    y_values.push_back(expected_slope * x_values[i] + expected_intercept);\n  }\n  const auto result = intrp::linear_regression(x_values, y_values);\n\n  CHECK(result.intercept == approx(expected_intercept));\n  CHECK(result.slope == approx(expected_slope));\n  // Error bars are zero because we have exactly a straight line.\n  CHECK(result.delta_intercept == approx(0.0));\n  CHECK(result.delta_slope == approx(0.0));\n}\n\nvoid test_linear_regression_error_bars() {\n  const DataVector x_values{1.0, 2.0, 3.0};\n  const DataVector y_values{2.0, 4.0, 5.0};\n\n  const auto result = intrp::linear_regression(x_values, y_values);\n  // The values below were worked out by hand for the above 3 points.\n  CHECK(result.intercept == approx(2.0 / 3.0));\n  CHECK(result.slope == approx(3.0 / 2.0));\n  CHECK(result.delta_intercept == approx(sqrt(7.0 / 18.0)));\n  CHECK(result.delta_slope == approx(sqrt(1.0 / 12.0)));\n}\n\nvoid test_linear_regression() {\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> coeff_dis(-10.0, 10.0);\n\n  const double intercept = coeff_dis(gen);\n  CAPTURE(intercept);\n  const double slope = coeff_dis(gen);\n  CAPTURE(slope);\n\n  test_linear_regression_stdvectordouble(intercept, slope);\n  test_linear_regression_datavector(intercept, slope);\n  test_linear_regression_error_bars();\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.LinearRegression\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test_linear_regression();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_MultiLinearSpanInterpolation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/MultiLinearSpanInterpolation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, size_t NumVar>\nvoid test() {\n  CAPTURE(Dim);\n  CAPTURE(NumVar);\n\n  MAKE_GENERATOR(gen);\n\n  std::uniform_real_distribution<> dist_func(-1., 1.);\n  std::uniform_int_distribution<size_t> dist_int(3, 5);\n\n  // Main idea: The table performs a multi-linear interpolation\n  // Each element in the table will be multi-linear function,\n  // so we can use this directly as a test.\n  // For simplicity, we will use the table to globally reproduce a\n  // single multi-linear function.\n\n  std::array<std::array<double, Dim>, NumVar> matrix{};\n  std::array<double, NumVar> vector_b{};\n\n  for (size_t i = 0; i < NumVar; ++i) {\n    gsl::at(vector_b, i) = dist_func(gen);\n    for (size_t j = 0; j < Dim; ++j) {\n      gsl::at(gsl::at(matrix, i), j) = dist_func(gen);\n    }\n  }\n\n  auto mock_function =\n      [&matrix, &vector_b](\n          const std::array<double, Dim>& x) -> std::array<double, NumVar> {\n    std::array<double, NumVar> res{};\n\n    for (size_t i = 0; i < NumVar; ++i) {\n      gsl::at(res, i) = gsl::at(vector_b, i);\n      for (size_t j = 0; j < Dim; ++j) {\n        gsl::at(res, i) += gsl::at(gsl::at(matrix, i), j) * gsl::at(x, j);\n      }\n    }\n    return res;\n  };\n\n  // Create data data structures\n  std::array<std::vector<double>, Dim> X_data{};\n  Index<Dim> num_x_points{};\n\n  // Initialize discrete grid for the tabulated interpolation routine\n\n  size_t total_num_points = 1;\n  for (size_t n = 0; n < Dim; ++n) {\n    num_x_points[n] = dist_int(gen);\n    total_num_points *= num_x_points[n];\n\n    gsl::at(X_data, n).resize(num_x_points[n]);\n\n    for (size_t m = 0; m < num_x_points[n]; m++) {\n      gsl::at(X_data, n)[m] =\n          -1. + static_cast<double>(m) * 2. /\n                    static_cast<double>(num_x_points[n] - 1);\n    }\n  }\n\n  // Correct for NumVars\n  total_num_points *= NumVar;\n\n  // Allocate storage for main table\n  std::vector<double> dependent_variables(total_num_points);\n\n  std::array<gsl::span<const double>, Dim> independent_data_view{};\n\n  // Fill dependent_variables\n  for (size_t ijk = 0; ijk < total_num_points / NumVar; ++ijk) {\n    Index<Dim> index{};\n    std::array<double, Dim> tmp{};\n\n    // Uncompress index\n    size_t myind = ijk;\n    for (size_t nn = 0; nn < Dim - 1; ++nn) {\n      index[nn] = myind % num_x_points[nn];\n      myind = (myind - index[nn]) / num_x_points[nn];\n      gsl::at(tmp, nn) = gsl::at(X_data, nn)[index[nn]];\n    }\n    index[Dim - 1] = myind;\n    gsl::at(tmp, Dim - 1) = gsl::at(X_data, Dim - 1)[index[Dim - 1]];\n\n    for (size_t nv = 0; nv < NumVar; ++nv) {\n      std::array<double, NumVar> Fx = mock_function(tmp);\n      dependent_variables[nv + NumVar * ijk] = gsl::at(Fx, nv);\n    }\n  }\n\n  std::array<double, Dim> point{};\n  std::array<size_t, NumVar> which_var{};\n\n  for (size_t which_Dim = 0; which_Dim < Dim; ++which_Dim) {\n    gsl::at(independent_data_view, which_Dim) = gsl::span<const double>{\n        gsl::at(X_data, which_Dim).data(), num_x_points[which_Dim]};\n    gsl::at(point, which_Dim) = dist_func(gen);\n  }\n\n  for (size_t nv = 0; nv < NumVar; ++nv) {\n    gsl::at(which_var, nv) = nv;\n  }\n\n  intrp::UniformMultiLinearSpanInterpolation<Dim, NumVar> uniform_intp(\n      independent_data_view, {dependent_variables.data(), total_num_points},\n      num_x_points);\n  intrp::GeneralMultiLinearSpanInterpolation<Dim, NumVar> general_intp(\n      independent_data_view, {dependent_variables.data(), total_num_points},\n      num_x_points);\n\n  for (size_t d = 0; d < Dim; ++d) {\n    uniform_intp.extrapolate_above_data(d, true);\n    general_intp.extrapolate_above_data(d, true);\n\n    uniform_intp.extrapolate_below_data(d, true);\n    general_intp.extrapolate_below_data(d, true);\n  }\n\n  auto y_expected = mock_function(point);\n\n  auto y_interpolated = uniform_intp.interpolate(which_var, point);\n  auto y_interpolated_gen = general_intp.interpolate(which_var, point);\n\n  const double epsilon = 1.0e-11;\n  CAPTURE(epsilon);\n  for (size_t nv = 0; nv < NumVar; ++nv) {\n    CAPTURE(gsl::at(y_expected, nv));\n    CAPTURE(gsl::at(y_interpolated, nv));\n    CAPTURE(gsl::at(y_interpolated_gen, nv));\n    CHECK(std::abs(gsl::at(y_expected, nv) - gsl::at(y_interpolated, nv)) <\n          epsilon * std::abs(gsl::at(y_expected, nv)));\n    CHECK(std::abs(gsl::at(y_expected, nv) - gsl::at(y_interpolated_gen, nv)) <\n          epsilon * std::abs(gsl::at(y_expected, nv)));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.MultiLinearSpanInterpolation\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test<1, 1>();\n  test<2, 3>();\n  test<3, 5>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_PolynomialInterpolation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/PolynomialInterpolation.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Degree>\nvoid test() {\n  CAPTURE(Degree);\n  const DataVector coords{0.1, 0.4, 0.5, 0.9, 1.2, 1.6, 1.8, 2.1};\n  const double target_x = 0.2;\n  DataVector y{coords.size(), 0.0};\n  double expected_y = 0.0;\n  for (size_t i = 0; i <= Degree; ++i) {\n    y += pow(coords, i);\n    expected_y += pow(target_x, i);\n  }\n  double error_in_y = 0.0;\n  double target_y = 0.0;\n  intrp::polynomial_interpolation<Degree>(\n      make_not_null(&target_y), make_not_null(&error_in_y), target_x,\n      gsl::make_span(y.data(), Degree + 1),\n      gsl::make_span(coords.data(), Degree + 1));\n  CHECK_ITERABLE_APPROX(target_y, expected_y);\n  // Nils Deppe: The error estimate is quite bad I thinks because we do not have\n  // any decrease in power as we go to higher degree polynomials and so the\n  // difference between the Degree and Degree-1 polynomial is generally fairly\n  // large.\n  CHECK(std::abs(error_in_y) < 0.5);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.PolynomialInterpolation\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n  test<4>();\n  test<5>();\n  test<6>();\n  test<7>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_PredictedZeroCrossing.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <deque>\n#include <random>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearLeastSquares.hpp\"\n#include \"NumericalAlgorithms/Interpolation/PredictedZeroCrossing.hpp\"\n\nnamespace {\n\nvoid test_predicted_zero_crossing_datavector() {\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-10., 10.);\n  std::uniform_real_distribution<> error_dist(-1e-8, 1e-8);\n\n  DataVector expected_zero_crossing_value(3, 0.);\n  for (size_t i = 0; i < expected_zero_crossing_value.size(); ++i) {\n    expected_zero_crossing_value[i] = dist(gen);\n  }\n  CAPTURE(expected_zero_crossing_value);\n\n  std::deque<double> slope{};\n  for (size_t i = 0; i < expected_zero_crossing_value.size(); ++i) {\n    slope.push_back(dist(gen));\n  }\n  CAPTURE(slope);\n\n  std::deque<double> y_intercept{};\n  for (size_t i = 0; i < expected_zero_crossing_value.size(); ++i) {\n    y_intercept.push_back(-slope[i] * expected_zero_crossing_value[i]);\n  }\n\n  std::deque<double> x_values{0., -1., -2., -3., -4., -5., -6., -7., -8., -9.};\n  std::deque<DataVector> y_values{};\n\n  for (size_t i = 0; i < x_values.size(); i++) {\n    DataVector tmp(expected_zero_crossing_value.size());\n    for (size_t j = 0; j < tmp.size(); j++) {\n      tmp[j] = y_intercept[j] + slope[j] * x_values[i] + error_dist(gen);\n    }\n    y_values.push_back(tmp);\n  }\n\n  DataVector compute_zero_crossing_value =\n      intrp::predicted_zero_crossing_value(x_values, y_values);\n\n  // Epsilon here was chosen empirically.  Note that above we add\n  // random errors to every single point, so we need to make the\n  // epsilon here large enough so that the test doesn't fail in the\n  // rare case that the errors reinforce each other.\n  Approx custom_approx = Approx::custom().epsilon(1e-5);\n  CHECK_ITERABLE_CUSTOM_APPROX(compute_zero_crossing_value,\n                               expected_zero_crossing_value, custom_approx);\n}\n\nvoid test_predicted_zero_crossing_indeterminate() {\n  // Here we set up points so that the error bars are large enough\n  // that it is not clear whether the zero crossing is in the\n  // past or in the future.\n  std::vector<double> x_values{0.0, -1.0, -2.0, -3.0};\n  std::vector<double> y_values{1.0, 2.1, 1.0, 2.0};\n  CHECK(intrp::predicted_zero_crossing_value(x_values, y_values) == 0.0);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.Interpolation.PredictedZeroCrossing\",\n    \"[Unit][NumericalAlgorithms]\") {\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-10., 10.);\n  std::uniform_real_distribution<> error_dist(-1e-8, 1e-8);\n\n  const double expected_zero_crossing_value = dist(gen);\n  CAPTURE(expected_zero_crossing_value);\n  const double slope = dist(gen);\n  CAPTURE(slope);\n\n  std::vector<double> x_values = {0.,  -1., -2., -3., -4.,\n                                  -5., -6., -7., -8., -9.};\n  std::vector<double> y_values{};\n  double b = -slope * expected_zero_crossing_value;\n  for (size_t i = 0; i < x_values.size(); i++) {\n    double epsilon = error_dist(gen);\n    y_values.push_back(slope * gsl::at(x_values, i) + b + epsilon);\n  }\n\n  auto compute_zero_crossing_value =\n      intrp::predicted_zero_crossing_value(x_values, y_values);\n\n  // Epsilon here was chosen empirically.  Note that above we add\n  // random errors to every single point, so we need to make the\n  // epsilon here large enough so that the test doesn't fail in the\n  // rare case that the errors reinforce each other.\n  const Approx custom_approx = Approx::custom().epsilon(1.e-4);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_zero_crossing_value,\n                               compute_zero_crossing_value, custom_approx);\n\n  // Test the zero crossing value for a set of datavectors\n  test_predicted_zero_crossing_datavector();\n\n  test_predicted_zero_crossing_indeterminate();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_RegularGridInterpolant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"PointwiseFunctions/MathFunctions/TensorProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\nconstexpr double inertial_coord_min = -0.3;\nconstexpr double inertial_coord_max = 0.7;\n\ntemplate <size_t Dim>\nauto make_affine_map();\n\ntemplate <>\nauto make_affine_map<1>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max});\n}\n\ntemplate <>\nauto make_affine_map<2>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine2D{Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max},\n               Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max}});\n}\n\ntemplate <>\nauto make_affine_map<3>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine3D{Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max},\n               Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max},\n               Affine{-1.0, 1.0, inertial_coord_min, inertial_coord_max}});\n}\n\nnamespace TestTags {\n\ntemplate <typename DataType>\nstruct ScalarTag : db::SimpleTag {\n  using type = Scalar<DataType>;\n  template <size_t Dim>\n  static auto fill_values(const MathFunctions::TensorProduct<Dim>& f,\n                          const tnsr::I<DataVector, Dim>& x) {\n    if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n      return Scalar<DataType>{\n          {{get(f(x)) + std::complex<double>{0., 1.} * get(f(x))}}};\n    } else {\n      return f(x);\n    }\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nstruct Vector : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim>;\n  static auto fill_values(const MathFunctions::TensorProduct<Dim>& f,\n                          const tnsr::I<DataVector, Dim>& x) {\n    auto result = make_with_value<tnsr::I<DataType, Dim>>(x, 0.);\n    const auto f_of_x = f(x);\n    for (size_t d = 0; d < Dim; ++d) {\n      result.get(d) = (d + 0.5) * get(f_of_x);\n    }\n    return result;\n  }\n};\n\n}  // namespace TestTags\n\n// Test interpolation from source_mesh onto target_mesh.\ntemplate <size_t Dim, typename DataType>\nvoid test_regular_interpolation(const Mesh<Dim>& source_mesh,\n                                const Mesh<Dim>& target_mesh) {\n  CAPTURE(source_mesh);\n  CAPTURE(target_mesh);\n  const auto map = make_affine_map<Dim>();\n  const auto source_coords = map(logical_coordinates(source_mesh));\n  const auto target_coords = map(logical_coordinates(target_mesh));\n\n  // Set up variables\n  using tags = tmpl::list<TestTags::ScalarTag<DataType>,\n                          TestTags::Vector<Dim, DataType>>;\n  Variables<tags> source_vars(source_mesh.number_of_grid_points());\n  Variables<tags> expected_result(target_mesh.number_of_grid_points());\n\n  // Set up interpolator\n  const intrp::RegularGrid<Dim> regular_grid_interpolant(source_mesh,\n                                                         target_mesh);\n\n  // We will make polynomials of the form x^a y^b z^c ...\n  // for all a,b,c, that result in exact interpolation.\n  // IndexIterator loops over \"a,b,c\"\n  for (IndexIterator<Dim> iter(source_mesh.extents()); iter; ++iter) {\n    // Set up analytic solution.  We fill a Variables with this solution,\n    // interpolate to arbitrary points, and then check that the\n    // values at arbitrary points match this solution.\n    // We choose polynomials so that interpolation is exact on an LGL grid.\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, Dim>\n        functions;\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(functions, d) =\n          std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(iter()[d]);\n    }\n    MathFunctions::TensorProduct<Dim> f(1.0, std::move(functions));\n\n    // Fill source and expected destination Variables with analytic solution.\n    tmpl::for_each<tags>([&f, &source_coords, &target_coords, &source_vars,\n                          &expected_result](auto tag) {\n      using Tag = tmpl::type_from<decltype(tag)>;\n      get<Tag>(source_vars) = Tag::fill_values(f, source_coords);\n      get<Tag>(expected_result) = Tag::fill_values(f, target_coords);\n    });\n\n    // Interpolate\n    // (g++ 7.2.0 does not allow `const auto result` here)\n    const Variables<tags> result =\n        regular_grid_interpolant.interpolate(source_vars);\n\n    tmpl::for_each<tags>([&result, &expected_result](auto tag) {\n      using Tag = tmpl::type_from<decltype(tag)>;\n      CHECK_ITERABLE_APPROX(get<Tag>(result), get<Tag>(expected_result));\n    });\n\n    const auto result_dv = regular_grid_interpolant.interpolate(\n        get(get<TestTags::ScalarTag<DataType>>(source_vars)));\n    CHECK_ITERABLE_APPROX(\n        result_dv, get(get<TestTags::ScalarTag<DataType>>(expected_result)));\n  }\n}\n\n// Test interpolation from source_mesh onto target_mesh, but with one dimension\n// (at a time) being overridden to specify new target points.\ntemplate <size_t Dim>\nvoid test_regular_interpolation_override(const Mesh<Dim>& source_mesh,\n                                         const Mesh<Dim>& target_mesh,\n                                         const DataVector& override_coords) {\n  CAPTURE(source_mesh);\n  CAPTURE(target_mesh);\n  CAPTURE(override_coords);\n  const auto map = make_affine_map<Dim>();\n  const auto source_coords = map(logical_coordinates(source_mesh));\n\n  const auto make_target_logical_coordinates = [&target_mesh, &override_coords](\n                                                   const size_t\n                                                       local_dim_to_override) {\n    std::array<DataVector, Dim> target_1d_logical_coords;\n    std::array<size_t, Dim> target_extents{};\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(target_1d_logical_coords, d) =\n          (d == local_dim_to_override\n               ? override_coords\n               : get<0>(logical_coordinates(gsl::at(target_mesh.slices(), d))));\n      gsl::at(target_extents, d) = gsl::at(target_1d_logical_coords, d).size();\n    }\n    const Index<Dim> target_index(target_extents);\n    auto result =\n        make_with_value<tnsr::I<DataVector, Dim, Frame::ElementLogical>>(\n            DataVector(target_index.product()), 0.0);\n    for (IndexIterator<Dim> iter(target_index); iter; ++iter) {\n      for (size_t d = 0; d < Dim; ++d) {\n        result.get(d)[iter.collapsed_index()] =\n            gsl::at(target_1d_logical_coords, d)[iter()[d]];\n      }\n    }\n    return result;\n  };\n\n  for (size_t dim_to_override = 0; dim_to_override < Dim; ++dim_to_override) {\n    auto target_logical_coords =\n        make_target_logical_coordinates(dim_to_override);\n    const auto target_coords = map(target_logical_coords);\n\n    // Set up variables\n    using tags = tmpl::list<TestTags::ScalarTag<DataVector>,\n                            TestTags::Vector<Dim, DataVector>>;\n    Variables<tags> source_vars(source_mesh.number_of_grid_points());\n    Variables<tags> expected_result(get<0>(target_coords).size());\n\n    // Set up interpolator\n    auto override_target_mesh_with_1d_coords = make_array<Dim>(DataVector{});\n    gsl::at(override_target_mesh_with_1d_coords, dim_to_override) =\n        override_coords;\n    const intrp::RegularGrid<Dim> regular_grid_interpolant(\n        source_mesh, target_mesh, override_target_mesh_with_1d_coords);\n\n    // Test only the highest-order polynomial x^a y^b z^c on the source mesh.\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, Dim>\n        functions;\n    for (size_t d = 0; d < Dim; ++d) {\n      gsl::at(functions, d) =\n          std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(\n              source_mesh.extents(d) - 1);\n    }\n    MathFunctions::TensorProduct<Dim> f(1.0, std::move(functions));\n\n    // Fill source and expected destination Variables with analytic solution.\n    tmpl::for_each<tags>([&f, &source_coords, &target_coords, &source_vars,\n                          &expected_result](auto tag) {\n      using Tag = tmpl::type_from<decltype(tag)>;\n      get<Tag>(source_vars) = Tag::fill_values(f, source_coords);\n      get<Tag>(expected_result) = Tag::fill_values(f, target_coords);\n    });\n\n    // Interpolate\n    // (g++ 7.2.0 does not allow `const auto result` here)\n    const Variables<tags> result =\n        regular_grid_interpolant.interpolate(source_vars);\n\n    // When extrapolating in multiple dimensions, the errors can grow to larger\n    // than the default tolerance. But in this test we extrapolate only one\n    // dimension at a time, so the default tolerance works:\n    tmpl::for_each<tags>([&result, &expected_result](auto tag) {\n      using Tag = tmpl::type_from<decltype(tag)>;\n      CHECK_ITERABLE_APPROX(get<Tag>(result), get<Tag>(expected_result));\n    });\n\n    const DataVector result_dv = regular_grid_interpolant.interpolate(\n        get(get<TestTags::ScalarTag<DataVector>>(source_vars)));\n    CHECK_ITERABLE_APPROX(\n        result_dv, get(get<TestTags::ScalarTag<DataVector>>(expected_result)));\n  }\n}\n\nvoid test_1d_regular_interpolation() {\n  const size_t start_points = 4;\n  const size_t end_points = 6;\n  const DataVector coords = {1.0, 1.5, 2.5, 3.0};  // some extrapolating points\n  for (size_t n = start_points; n < end_points; ++n) {\n    const auto mesh_lgl = Mesh<1>{n, Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto};\n    const auto mesh_lg_high_res =\n        Mesh<1>{n + 2, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss};\n    test_regular_interpolation<1, DataVector>(mesh_lgl, mesh_lgl);\n    test_regular_interpolation<1, DataVector>(mesh_lgl, mesh_lg_high_res);\n    test_regular_interpolation<1, ComplexDataVector>(mesh_lgl, mesh_lgl);\n    test_regular_interpolation<1, ComplexDataVector>(mesh_lgl,\n                                                     mesh_lg_high_res);\n    test_regular_interpolation_override(mesh_lgl, mesh_lgl, coords);\n    test_regular_interpolation_override(mesh_lgl, mesh_lg_high_res, coords);\n  }\n}\n\nvoid test_2d_regular_interpolation() {\n  const size_t start_points = 3;\n  const size_t end_points = 5;\n  const DataVector coords = {1.0, 1.5, 2.5, 3.0};  // some extrapolating points\n  for (size_t nx = start_points; nx < end_points; ++nx) {\n    for (size_t ny = start_points; ny < end_points; ++ny) {\n      const auto mesh_lgl = Mesh<2>{{{nx, ny}},\n                                    Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto};\n      const auto mesh_lg_high_res = Mesh<2>{{{nx + 2, ny + 3}},\n                                            Spectral::Basis::Legendre,\n                                            Spectral::Quadrature::Gauss};\n      test_regular_interpolation<2, DataVector>(mesh_lgl, mesh_lgl);\n      test_regular_interpolation<2, DataVector>(mesh_lgl, mesh_lgl);\n      test_regular_interpolation<2, ComplexDataVector>(mesh_lgl,\n                                                       mesh_lg_high_res);\n      test_regular_interpolation<2, ComplexDataVector>(mesh_lgl,\n                                                       mesh_lg_high_res);\n      test_regular_interpolation_override(mesh_lgl, mesh_lgl, coords);\n      test_regular_interpolation_override(mesh_lgl, mesh_lg_high_res, coords);\n    }\n  }\n}\n\nvoid test_3d_regular_interpolation() {\n  const size_t start_points = 2;\n  const size_t end_points = 4;\n  const DataVector coords = {1.0, 1.5, 2.5, 3.0};  // some extrapolating points\n  for (size_t nx = start_points; nx < end_points; ++nx) {\n    for (size_t ny = start_points; ny < end_points; ++ny) {\n      for (size_t nz = start_points; nz < end_points; ++nz) {\n        const auto mesh_lgl = Mesh<3>{{{nx, ny, nz}},\n                                      Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::GaussLobatto};\n        const auto mesh_lg_high_res = Mesh<3>{{{nx + 2, ny + 3, nz + 1}},\n                                              Spectral::Basis::Legendre,\n                                              Spectral::Quadrature::Gauss};\n        test_regular_interpolation<3, DataVector>(mesh_lgl, mesh_lgl);\n        test_regular_interpolation<3, DataVector>(mesh_lgl, mesh_lgl);\n        test_regular_interpolation<3, ComplexDataVector>(mesh_lgl,\n                                                         mesh_lg_high_res);\n        test_regular_interpolation<3, ComplexDataVector>(mesh_lgl,\n                                                         mesh_lg_high_res);\n        test_regular_interpolation_override(mesh_lgl, mesh_lgl, coords);\n        test_regular_interpolation_override(mesh_lgl, mesh_lg_high_res, coords);\n      }\n      const auto mesh_cartoon_axial =\n          Mesh<3>{{{nx, ny, 1}},\n                  {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n                    Spectral::Basis::Cartoon}},\n                  {{Spectral::Quadrature::GaussLobatto,\n                    Spectral::Quadrature::GaussLobatto,\n                    Spectral::Quadrature::AxialSymmetry}}};\n      test_regular_interpolation<3, DataVector>(mesh_cartoon_axial,\n                                                mesh_cartoon_axial);\n    }\n    const auto mesh_cartoon_spherical =\n        Mesh<3>{{{nx, 1, 1}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n                  Spectral::Basis::Cartoon}},\n                {{Spectral::Quadrature::GaussLobatto,\n                  Spectral::Quadrature::SphericalSymmetry,\n                  Spectral::Quadrature::SphericalSymmetry}}};\n    test_regular_interpolation<3, DataVector>(mesh_cartoon_spherical,\n                                              mesh_cartoon_spherical);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.RegularGridInterpolant\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test_1d_regular_interpolation();\n  test_2d_regular_interpolation();\n  test_3d_regular_interpolation();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_SendGhWorldtubeData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/Cce/Actions/BoundaryComputeAndSendToEvolution.hpp\"\n#include \"Evolution/Systems/Cce/Callbacks/SendGhWorldtubeData.hpp\"\n#include \"Evolution/Systems/Cce/Components/CharacteristicEvolution.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename InterpolationTargetTag, bool LocalTimeStepping>\nstruct dispatch_to_send_gh_worldtube_data {\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex,\n            typename Metavariables>\n  static void apply(const db::DataBox<tmpl::list<DbTags...>>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    if constexpr (tmpl2::flat_any_v<std::is_same_v<::Tags::Time, DbTags>...>) {\n      using post_intrp_callback = intrp::callbacks::SendGhWorldtubeData<\n          Cce::CharacteristicEvolution<Metavariables>, InterpolationTargetTag,\n          false, LocalTimeStepping>;\n      static_assert(tt::assert_conforms_to_v<\n                    post_intrp_callback,\n                    intrp::protocols::PostInterpolationCallback>);\n      post_intrp_callback::apply(box, cache, db::get<::Tags::Time>(box));\n    } else {\n      ERROR(\"Missing required tag ::Tag::Time\");\n    }\n  }\n};\n\ntnsr::aa<DataVector, 3> received_spacetime_metric;\ntnsr::iaa<DataVector, 3> received_phi;\ntnsr::aa<DataVector, 3> received_pi;\nstruct test_receive_gh_data {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(const db::DataBox<DbTagList>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /* cache*/,\n                    const ArrayIndex& /*array_index*/, const double /*time*/,\n                    const tnsr::aa<DataVector, 3>& spacetime_metric,\n                    const tnsr::iaa<DataVector, 3>& phi,\n                    const tnsr::aa<DataVector, 3>& pi) {\n    received_spacetime_metric = spacetime_metric;\n    received_phi = phi;\n    received_pi = pi;\n  }\n};\n\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct mock_interpolation_target {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tags =\n      tmpl::list<intrp::Tags::Sphere<InterpolationTargetTag>>;\n  using simple_tags = tmpl::list<\n      ::Tags::Variables<tmpl::list<::gr::Tags::SpacetimeMetric<DataVector, 3>,\n                                   gh::Tags::Phi<DataVector, 3>,\n                                   gh::Tags::Pi<DataVector, 3>>>,\n      ::Tags::Time>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags, tmpl::list<>>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct mock_gh_worldtube_boundary {\n  using metavariables = Metavariables;\n  using component_being_mocked = Cce::GhWorldtubeBoundary<Metavariables>;\n  using replace_these_simple_actions = tmpl::conditional_t<\n      Metavariables::local_time_stepping,\n      tmpl::list<Cce::Actions::ReceiveGhWorldtubeData<\n          Cce::CharacteristicEvolution<Metavariables>, false>>,\n      tmpl::list<Cce::Actions::SendToEvolution<\n          Cce::GhWorldtubeBoundary<Metavariables>,\n          Cce::CharacteristicEvolution<Metavariables>>>>;\n  using with_these_simple_actions = tmpl::list<test_receive_gh_data>;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <bool LocalTimeStepping>\nstruct test_metavariables {\n  struct Target : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target =\n        typename mock_interpolation_target<test_metavariables,\n                                           Target>::simple_tags;\n    using compute_target_points =\n        intrp::TargetPoints::Sphere<Target, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::SendGhWorldtubeData<\n            Cce::CharacteristicEvolution<test_metavariables>, Target, false,\n            LocalTimeStepping>>;\n    using compute_items_on_target = tmpl::list<>;\n  };\n\n  static constexpr bool local_time_stepping = LocalTimeStepping;\n  using component_list = tmpl::list<\n      mock_gh_worldtube_boundary<test_metavariables<LocalTimeStepping>>,\n      mock_interpolation_target<test_metavariables<LocalTimeStepping>, Target>>;\n};\n\ntemplate <bool LocalTimeStepping, typename Generator>\nvoid test_callback_function(const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<size_t> resolution_distribution{7, 10};\n  const size_t l_max = resolution_distribution(*gen);\n  UniformCustomDistribution<double> value_distribution{0.1, 1.0};\n  using spacetime_tags =\n      tmpl::list<::gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Phi<DataVector, 3>, gh::Tags::Pi<DataVector, 3>>;\n  using target = typename test_metavariables<LocalTimeStepping>::Target;\n  const ylm::AngularOrdering angular_ordering = ylm::AngularOrdering::Cce;\n  const double radius = 3.6;\n  const std::array<double, 3> center = {{0.05, 0.06, 0.07}};\n  // Options for Sphere\n  intrp::OptionHolders::Sphere sphere_opts(l_max, center, radius,\n                                           angular_ordering);\n  Variables<spacetime_tags> spacetime_variables{\n      Spectral::Swsh::number_of_swsh_collocation_points(l_max)};\n  tmpl::for_each<spacetime_tags>(\n      [&gen, &value_distribution, &spacetime_variables](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        fill_with_random_values(make_not_null(&get<tag>(spacetime_variables)),\n                                gen, make_not_null(&value_distribution));\n      });\n  ActionTesting::MockRuntimeSystem<test_metavariables<LocalTimeStepping>>\n      runner{{std::move(sphere_opts)}};\n  runner.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_component_and_initialize<\n      mock_interpolation_target<test_metavariables<LocalTimeStepping>, target>>(\n      &runner, 0_st, {spacetime_variables, 0.05});\n  ActionTesting::emplace_component<\n      mock_gh_worldtube_boundary<test_metavariables<LocalTimeStepping>>>(\n      &runner, 0_st);\n  runner.set_phase(Parallel::Phase::Testing);\n  ActionTesting::simple_action<\n      mock_interpolation_target<test_metavariables<LocalTimeStepping>, target>,\n      dispatch_to_send_gh_worldtube_data<target, LocalTimeStepping>>(\n      make_not_null(&runner), 0_st);\n  ActionTesting::invoke_queued_simple_action<\n      mock_gh_worldtube_boundary<test_metavariables<LocalTimeStepping>>>(\n      make_not_null(&runner), 0_st);\n  // check that the tags have been communicated properly (here they propagate\n  // through to the replaced simple action that stores them in the globals)\n  CHECK(get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(spacetime_variables) ==\n        received_spacetime_metric);\n  CHECK(get<gh::Tags::Phi<DataVector, 3>>(spacetime_variables) == received_phi);\n  CHECK(get<gh::Tags::Pi<DataVector, 3>>(spacetime_variables) == received_pi);\n\n  // Error test\n  intrp::OptionHolders::Sphere sphere_opts2(\n      l_max, center, std::vector<double>{3.6, 3.7}, angular_ordering);\n  ActionTesting::MockRuntimeSystem<test_metavariables<LocalTimeStepping>>\n      runner2{{std::move(sphere_opts2)}};\n  runner2.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_component_and_initialize<\n      mock_interpolation_target<test_metavariables<LocalTimeStepping>, target>>(\n      &runner2, 0_st, {spacetime_variables, 0.05});\n  ActionTesting::emplace_component<\n      mock_gh_worldtube_boundary<test_metavariables<LocalTimeStepping>>>(\n      &runner2, 0_st);\n  runner2.set_phase(Parallel::Phase::Testing);\n  CHECK_THROWS_WITH(\n      ([&runner2]() {\n        ActionTesting::simple_action<\n            mock_interpolation_target<test_metavariables<LocalTimeStepping>,\n                                      target>,\n            dispatch_to_send_gh_worldtube_data<target, LocalTimeStepping>>(\n            make_not_null(&runner2), 0_st);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"SendGhWorldtubeData expects a single worldtube radius, not\"));\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.Interpolation.SendGhWorldtubeDataCallback\",\n    \"[Unit][Cce]\") {\n  MAKE_GENERATOR(gen);\n  // For local time stepping\n  test_callback_function<true>(make_not_null(&gen));\n  // For global time stepping\n  test_callback_function<false>(make_not_null(&gen));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_SpanInterpolators.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Interpolation/BarycentricRationalSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/CubicSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/LinearSpanInterpolator.hpp\"\n#include \"NumericalAlgorithms/Interpolation/SpanInterpolator.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace intrp {\n\ntemplate <typename Generator>\nvoid test_linear_interpolator(const gsl::not_null<Generator*> gen) {\n  UniformCustomDistribution<double> value_dist{0.1, 1.0};\n  // cannot be const due to participation in the `span`\n  auto linear_interpolator_values = make_with_random_values<DataVector>(\n      gen, value_dist, static_cast<size_t>(2));\n  auto linear_interpolator_complex_values =\n      make_with_random_values<ComplexDataVector>(gen, value_dist,\n                                                 static_cast<size_t>(2));\n  const DataVector linear_interpolator_points = {{0.0, 1.0}};\n  const double target_point = value_dist(*gen);\n  const LinearSpanInterpolator test_linear_interpolator{};\n  const double real_linear_interpolation = test_linear_interpolator.interpolate(\n      gsl::span<const double>{linear_interpolator_points.data(),\n                              linear_interpolator_points.size()},\n      gsl::span<const double>{linear_interpolator_values.data(),\n                              linear_interpolator_values.size()},\n      target_point);\n  CHECK(real_linear_interpolation ==\n        approx(target_point * linear_interpolator_values[1] +\n               (1.0 - target_point) * linear_interpolator_values[0]));\n  const std::complex<double> complex_linear_interpolation =\n      test_linear_interpolator.interpolate(\n          gsl::span<const double>{linear_interpolator_points.data(),\n                                  linear_interpolator_points.size()},\n          gsl::span<const std::complex<double>>{\n              linear_interpolator_complex_values.data(),\n              linear_interpolator_complex_values.size()},\n          target_point);\n  CHECK_ITERABLE_APPROX(\n      complex_linear_interpolation,\n      target_point * linear_interpolator_complex_values[1] +\n          (1.0 - target_point) * linear_interpolator_complex_values[0]);\n}\n\ntemplate <typename VectorType, typename InterpolatorType, typename Generator>\nvoid test_interpolator_approximate_fidelity(\n    const gsl::not_null<Generator*> gen, const InterpolatorType& interpolator,\n    Approx interpolator_approx) {\n  UniformCustomDistribution<double> value_dist{0.1, 1.0};\n  DataVector interpolator_points{\n      2 * interpolator.required_number_of_points_before_and_after()};\n  VectorType interpolator_values{\n      2 * interpolator.required_number_of_points_before_and_after()};\n  const double frequency = value_dist(*gen);\n  const typename VectorType::value_type amplitude = value_dist(*gen);\n  // only sample a small span to give the low-order interpolators an easier time\n  for (size_t i = 0; i < interpolator_points.size(); ++i) {\n    interpolator_points[i] = 0.01 * i / interpolator_points.size();\n    interpolator_values[i] =\n        amplitude * cos(frequency * interpolator_points[i]);\n  }\n  const double target_time = value_dist(*gen) / 100.0;\n  const auto interpolator_result = interpolator.interpolate(\n      gsl::span<const double>{interpolator_points.data(),\n                              interpolator_points.size()},\n      gsl::span<const typename VectorType::value_type>{\n          interpolator_values.data(), interpolator_points.size()},\n      target_time);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(interpolator_result,\n                               amplitude * cos(frequency * target_time),\n                               interpolator_approx);\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Interpolation.SpanInterpolators\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  MAKE_GENERATOR(gen);\n  test_linear_interpolator(make_not_null(&gen));\n\n  {\n    // Linear interpolator will not get terribly close, but that's okay.\n    Approx interpolator_approx = Approx::custom().epsilon(1.0e-2).scale(1.0);\n\n    INFO(\"testing LinearSpanInterpolator\");\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen), LinearSpanInterpolator{}, interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen), LinearSpanInterpolator{}, interpolator_approx);\n\n    // verify the the construction from options is successful\n    const auto option_created_linear_interpolator =\n        TestHelpers::test_creation<std::unique_ptr<intrp::SpanInterpolator>>(\n            \"LinearSpanInterpolator\");\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen), *option_created_linear_interpolator,\n        interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen), *option_created_linear_interpolator,\n        interpolator_approx);\n\n    // verify that the interpolator can be serialized and deserialized\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen),\n        serialize_and_deserialize(LinearSpanInterpolator{}),\n        interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen),\n        serialize_and_deserialize(LinearSpanInterpolator{}),\n        interpolator_approx);\n  }\n\n  {\n    Approx interpolator_approx =\n        Approx::custom()\n            .epsilon(std::numeric_limits<double>::epsilon() * 1.0e8)\n            .scale(1.0);\n\n    INFO(\"testing CubicSpanInterpolator\");\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen), CubicSpanInterpolator{}, interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen), CubicSpanInterpolator{}, interpolator_approx);\n\n    // verify the the construction from options is successful\n    const auto option_created_cubic_interpolator =\n        TestHelpers::test_creation<std::unique_ptr<intrp::SpanInterpolator>>(\n            \"CubicSpanInterpolator\");\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen), *option_created_cubic_interpolator,\n        interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen), *option_created_cubic_interpolator,\n        interpolator_approx);\n\n    // verify that the interpolator can be serialized and deserialized\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen), serialize_and_deserialize(CubicSpanInterpolator{}),\n        interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen), serialize_and_deserialize(CubicSpanInterpolator{}),\n        interpolator_approx);\n  }\n\n  {\n    Approx interpolator_approx =\n        Approx::custom()\n            .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n            .scale(1.0);\n\n    INFO(\"testing BarycentricRationalSpanInterpolator\");\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen), BarycentricRationalSpanInterpolator{5u, 6u},\n        interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen), BarycentricRationalSpanInterpolator{5u, 6u},\n        interpolator_approx);\n\n    // verify the the construction from options is successful\n    const auto option_created_barycentric_interpolator =\n        TestHelpers::test_creation<std::unique_ptr<intrp::SpanInterpolator>>(\n            \"BarycentricRationalSpanInterpolator:\\n\"\n            \"  MinOrder: 5\\n\"\n            \"  MaxOrder: 6\");\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen), *option_created_barycentric_interpolator,\n        interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen), *option_created_barycentric_interpolator,\n        interpolator_approx);\n\n    // verify that the interpolator can be serialized and deserialized\n    test_interpolator_approximate_fidelity<DataVector>(\n        make_not_null(&gen),\n        serialize_and_deserialize(BarycentricRationalSpanInterpolator{5u, 6u}),\n        interpolator_approx);\n    test_interpolator_approximate_fidelity<ComplexDataVector>(\n        make_not_null(&gen),\n        serialize_and_deserialize(BarycentricRationalSpanInterpolator{5u, 6u}),\n        interpolator_approx);\n  }\n}\n}  // namespace intrp\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Interpolation/Test_ZeroCrossingPredictor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Interpolation/ZeroCrossingPredictor.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      intrp::ZeroCrossingPredictor(2, 3),\n      Catch::Matchers::ContainsSubstring(\"min_size must be >= 3,\"));\n  CHECK_THROWS_WITH(\n      intrp::ZeroCrossingPredictor(6, 5),\n      Catch::Matchers::ContainsSubstring(\"min_size must be <= max_size,\"));\n  CHECK_THROWS_WITH(\n      intrp::ZeroCrossingPredictor(4, 5).zero_crossing_time(0.0),\n      Catch::Matchers::ContainsSubstring(\"Invalid ZeroCrossingPredictor\"));\n}\n\nvoid test_zero_crossing_predictor() {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-10., 10.);\n  std::uniform_real_distribution<> error_dist(-1e-8, 1e-8);\n\n  const double expected_zero_crossing_value1 = dist(gen);\n  CAPTURE(expected_zero_crossing_value1);\n  const double expected_zero_crossing_value2 = dist(gen);\n  CAPTURE(expected_zero_crossing_value2);\n  const double slope1 = dist(gen);\n  CAPTURE(slope1);\n  const double slope2 = dist(gen);\n  CAPTURE(slope2);\n\n  std::vector<double> x_values = {-9., -8., -7., -6., -5.,\n                                  -4., -3., -2., -1., 0.};\n  std::vector<DataVector> y_values{};\n  const double intercept1 = -slope1 * expected_zero_crossing_value1;\n  const double intercept2 = -slope2 * expected_zero_crossing_value2;\n  for (size_t i = 0; i < x_values.size(); i++) {\n    double epsilon = error_dist(gen);\n    y_values.push_back({slope1 * gsl::at(x_values, i) + intercept1 + epsilon,\n                        slope2 * gsl::at(x_values, i) + intercept2 + epsilon});\n  }\n\n  constexpr size_t min_size = 4;\n  intrp::ZeroCrossingPredictor predictor(min_size, x_values.size());\n\n  // Check trivial case that min_positive_zero_crossing_time returns\n  // nullopt for an invalid predictor.\n  CHECK_FALSE(\n      predictor.min_positive_zero_crossing_time(x_values.back()).has_value());\n\n  // Fill points in predictor.\n  for (size_t i = 0; i < x_values.size(); i++) {\n    predictor.add(x_values[i], y_values[i]);\n    if (i < min_size - 1) {\n      CHECK_FALSE(predictor.is_valid());\n    } else {\n      CHECK(predictor.is_valid());\n    }\n  }\n\n  // Check serialization and operator== and operator!=\n  CHECK(predictor == serialize_and_deserialize(predictor));\n  CHECK_FALSE(predictor != serialize_and_deserialize(predictor));\n\n  test_copy_semantics(predictor);\n  test_move_semantics(serialize_and_deserialize(predictor),\n                      serialize_and_deserialize(predictor), min_size,\n                      x_values.size());\n\n  Approx custom_approx = Approx::custom().epsilon(1e-5).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      predictor.zero_crossing_time(x_values.back()),\n      SINGLE_ARG(DataVector{expected_zero_crossing_value1,\n                            expected_zero_crossing_value2}),\n      custom_approx);\n\n  const double adjusted_value1 = expected_zero_crossing_value1 < 0.0\n                                     ? std::numeric_limits<double>::infinity()\n                                     : expected_zero_crossing_value1;\n  const double adjusted_value2 = expected_zero_crossing_value2 < 0.0\n                                     ? std::numeric_limits<double>::infinity()\n                                     : expected_zero_crossing_value2;\n  const double expected_zero_crossing =\n      std::min(adjusted_value1, adjusted_value2);\n\n  if (expected_zero_crossing == std::numeric_limits<double>::infinity()) {\n    CHECK_FALSE(\n        predictor.min_positive_zero_crossing_time(x_values.back()).has_value());\n  } else {\n    CHECK(\n        predictor.min_positive_zero_crossing_time(x_values.back()).has_value());\n    CHECK(predictor.min_positive_zero_crossing_time(x_values.back()).value() ==\n          custom_approx(expected_zero_crossing));\n  }\n\n  // Re-add the first point.  Adding the first point should cause the\n  // (original) first point to be popped off the deque [and thus test\n  // this popping], so that the result should be identical to the\n  // previous result.\n  DataVector first_point = y_values.front();\n  predictor.add(x_values.front(), std::move(first_point));\n  if (expected_zero_crossing == std::numeric_limits<double>::infinity()) {\n    CHECK_FALSE(predictor.min_positive_zero_crossing_time(0.0).has_value());\n  } else {\n    CHECK(predictor.min_positive_zero_crossing_time(0.0).has_value());\n    CHECK(predictor.min_positive_zero_crossing_time(0.0).value() ==\n          custom_approx(expected_zero_crossing));\n  }\n\n  // Clear the predictor, and check that min_positive_zero_crossing_time\n  // returns nullopt again for the cleared (and now invalid) predictor.\n  predictor.clear();\n  CHECK_FALSE(\n      predictor.min_positive_zero_crossing_time(x_values.back()).has_value());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.Interpolation.ZeroCrossingPredictor\",\n    \"[Unit][NumericalAlgorithms]\") {\n  test_zero_crossing_predictor();\n  test_errors();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearAlgebra/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_LinearAlgebra\")\n\nset(LIBRARY_SOURCES\n  Test_FindGeneralizedEigenvalues.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  LinearAlgebra\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearAlgebra/Test_FindGeneralizedEigenvalues.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/LinearAlgebra/FindGeneralizedEigenvalues.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid test_find_generalized_eigenvalues(const Matrix& matrix_a,\n                                       const Matrix& matrix_b,\n                                       const size_t eigenvector_size,\n                                       const size_t eigenvalue_size) {\n  // Set up vectors and a matrix to store the eigenvalues and eigenvectors\n  DataVector eigenvalues_real_part(eigenvalue_size, 0.0);\n  DataVector eigenvalues_im_part(eigenvalue_size, 0.0);\n  Matrix eigenvectors(eigenvector_size, eigenvector_size, 0.0);\n\n  find_generalized_eigenvalues(&eigenvalues_real_part, &eigenvalues_im_part,\n                               &eigenvectors, matrix_a, matrix_b);\n\n  // Expected eigenvalues and eigenvectors\n  constexpr double expected_larger_eigenvalue = 10.099019513592784;\n  constexpr double expected_smaller_eigenvalue = -0.09901951359278449;\n  // We are testing a 2x2 matrix. Eigenvectors have two components.\n  // Normalize the eigenvectors so the second component is 1.\n  // Store the remaining components of the expected eigenvectors.\n  constexpr double expected_larger_eigenvector_value = 0.819803902718557;\n  constexpr double expected_smaller_eigenvector_value = -1.2198039027185574;\n\n  CHECK(eigenvalues_im_part[0] == approx(0.0));\n  CHECK(eigenvalues_im_part[1] == approx(0.0));\n  CHECK(max(eigenvalues_real_part) == approx(expected_larger_eigenvalue));\n  CHECK(min(eigenvalues_real_part) == approx(expected_smaller_eigenvalue));\n\n  // We don't know which order the eigenvalues are returned by lapack, so\n  // check which eigenvalue is larger and then verify that the corresponding\n  // eigenvector's nontrivial component has the expected value.\n  if (eigenvalues_real_part[0] > eigenvalues_real_part[1]) {\n    CHECK(eigenvectors(0, 0) / eigenvectors(1, 0) ==\n          approx(expected_larger_eigenvector_value));\n    CHECK(eigenvectors(0, 1) / eigenvectors(1, 1) ==\n          approx(expected_smaller_eigenvector_value));\n  } else {\n    CHECK(eigenvectors(0, 0) / eigenvectors(1, 0) ==\n          approx(expected_smaller_eigenvector_value));\n    CHECK(eigenvectors(0, 1) / eigenvectors(1, 1) ==\n          approx(expected_larger_eigenvector_value));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearAlgebra.GeneralizedEigenvalue\",\n                  \"[NumericalAlgorithms][LinearAlgebra][Unit]\") {\n  {\n    Matrix matrix_a{{1.0, 2.0}, {-3.0, -4.0}};\n    Matrix matrix_b{{4.0, -3.0}, {-2.0, 1.0}};\n    test_find_generalized_eigenvalues(matrix_a, matrix_b, 2, 2);\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        Matrix matrix_a{{1.0}, {-3.0}};\n        Matrix matrix_b{{4.0, -3.0}, {-2.0, 1.0}};\n        test_find_generalized_eigenvalues(matrix_a, matrix_b, 2, 2);\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Matrix A should be square\"));\n  CHECK_THROWS_WITH(([]() {\n                      Matrix matrix_a{{1.0, 2.0}, {-3.0, -4.0}};\n                      Matrix matrix_b{{4.0}};\n                      test_find_generalized_eigenvalues(matrix_a, matrix_b, 2,\n                                                        2);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Matrix A and matrix B should be the same size\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        Matrix matrix_a{{1.0, 2.0}, {-3.0, -4.0}};\n        Matrix matrix_b{{4.0, -3.0}, {-2.0, 1.0}};\n        test_find_generalized_eigenvalues(matrix_a, matrix_b, 1, 2);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Matrix A and matrix eigenvectors should have the same size\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        Matrix matrix_a{{1.0, 2.0}, {-3.0, -4.0}};\n        Matrix matrix_b{{4.0, -3.0}, {-2.0, 1.0}};\n        test_find_generalized_eigenvalues(matrix_a, matrix_b, 2, 1);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"eigenvalues DataVector sizes should equal number of columns\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_LinearOperators\")\n\nset(LIBRARY_SOURCES\n  Test_CoefficientTransforms.cpp\n  Test_DefiniteIntegral.cpp\n  Test_Divergence.cpp\n  Test_Filtering.cpp\n  Test_IndefiniteIntegral.cpp\n  Test_Linearize.cpp\n  Test_MeanValue.cpp\n  Test_PartialDerivatives.cpp\n  Test_PowerMonitors.cpp\n  Test_WeakDivergence.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Actions\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  DomainCreators\n  ErrorHandling\n  LinearOperators\n  MathFunctions\n  Spectral\n  SpectralHelpers\n  SphericalHarmonics\n  SphericalHarmonicsHelpers\n  Utilities\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.LinearOperators.Python.DefiniteIntegral\"\n  Test_DefiniteIntegral.py\n  \"Unit;LinearOperators;Python\"\n  PyLinearOperators)\n\n\nspectre_add_python_bindings_test(\n  \"Unit.LinearOperators.Python.PartialDerivatives\"\n  Test_PartialDerivatives.py\n  \"Unit;LinearOperators;Python\"\n  PyLinearOperators)\n\nspectre_add_python_bindings_test(\n  \"Unit.LinearOperators.Python.PowerMonitors\"\n  Test_PowerMonitors.py\n  \"Unit;LinearOperators;Python\"\n  PyLinearOperators)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Python/Test_DefiniteIntegral.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.NumericalAlgorithms.LinearOperators import definite_integral\nfrom spectre.Spectral import Basis, Mesh, Quadrature, collocation_points\n\n\n# Integral [-1, 1] is 2\ndef polynomial(x):\n    return 3.0 * x**2 + 2.0 * x\n\n\nclass TestDefiniteIntegral(unittest.TestCase):\n    def test_definite_integral(self):\n        mesh = Mesh[1](4, Basis.Legendre, Quadrature.GaussLobatto)\n        x = collocation_points(mesh)\n        integrand = polynomial(x)\n        integral = definite_integral(integrand, mesh)\n        self.assertAlmostEqual(integral, 2.0)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Python/Test_PartialDerivatives.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import InverseJacobian, Scalar\nfrom spectre.NumericalAlgorithms.LinearOperators import (\n    logical_partial_derivative,\n    partial_derivative,\n)\nfrom spectre.Spectral import Basis, Mesh, Quadrature, collocation_points\n\n\ndef polynomial(x):\n    return 2.0 * x**2 + 3.0 * x + 4.0\n\n\ndef deriv_polynomial(x):\n    return 4.0 * x + 3.0\n\n\nclass TestPartialDerivatives(unittest.TestCase):\n    def test_partial_derivative(self):\n        mesh = Mesh[1](4, Basis.Legendre, Quadrature.GaussLobatto)\n        x = collocation_points(mesh)\n        inv_jacobian = InverseJacobian[DataVector, 1](num_points=4, fill=1.0)\n        scalar = Scalar[DataVector]([polynomial(x)])\n        deriv = partial_derivative(scalar, mesh, inv_jacobian)\n        npt.assert_allclose(np.array(deriv), [deriv_polynomial(x)])\n        logical_deriv = logical_partial_derivative(scalar, mesh)\n        npt.assert_allclose(np.array(logical_deriv), [deriv_polynomial(x)])\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Python/Test_PowerMonitors.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.NumericalAlgorithms.LinearOperators import (\n    absolute_truncation_error,\n    convergence_rate_and_number_of_pile_up_modes,\n    power_monitors,\n    relative_truncation_error,\n)\nfrom spectre.Spectral import (\n    Basis,\n    Mesh1D,\n    Mesh2D,\n    Quadrature,\n    logical_coordinates,\n)\nfrom spectre.SphericalHarmonics import Frame, Strahlkorper, power_monitor\n\n\nclass TestPowerMonitors(unittest.TestCase):\n    # Check the case for a constant function where the power monitors\n    # should be given by the first basis function\n    def test_power_monitors(self):\n        num_points_per_dimension = 4\n\n        extent = num_points_per_dimension\n        basis = Basis.Legendre\n        quadrature = Quadrature.GaussLobatto\n        mesh = Mesh2D(extent, basis, quadrature)\n\n        test_vec = np.ones(mesh.number_of_grid_points())\n\n        test_array = power_monitors(test_vec, mesh)\n        np_test_array = np.asarray(test_array)\n\n        check_vec_0 = np.zeros(num_points_per_dimension)\n        check_vec_0[0] = 1.0 / np.sqrt(num_points_per_dimension)\n\n        check_vec_1 = np.zeros(num_points_per_dimension)\n        check_vec_1[0] = 1.0 / np.sqrt(num_points_per_dimension)\n\n        np_check_array = np.array([check_vec_0, check_vec_1])\n\n        np.testing.assert_allclose(np_test_array, np_check_array, 1e-12, 1e-12)\n\n    # Check that the truncation error for a straight line is consistent with the\n    # analytic expectation\n    def test_truncation_error(self):\n        mesh = Mesh1D(2, Basis.Legendre, Quadrature.GaussLobatto)\n        logical_coords = np.array(logical_coordinates(mesh))[0]\n\n        # Define the test function\n        slope, offset = 0.1, 1.0\n        test_data = slope * logical_coords + offset\n\n        # For a linear function the slope and offset correspond to the power\n        # monitor values\n        # The weighted average of the highest modes is\n        avg = np.log10(np.abs(slope)) * np.exp(-0.25) + np.log10(\n            np.abs(offset)\n        ) * np.exp(-0.25)\n        avg = avg / (np.exp(-0.25) + np.exp(-0.25))\n        expected_relative_truncation_error = np.power(10.0, avg)\n        expected_absolute_truncation_error = (\n            np.max(np.abs(test_data)) * expected_relative_truncation_error\n        )\n\n        # Test relative truncation_error\n        rel_error = relative_truncation_error(test_data, mesh)\n        np.testing.assert_allclose(\n            rel_error, expected_relative_truncation_error, 1e-12, 1e-12\n        )\n\n        # Test absolute truncation_error\n        abs_error = absolute_truncation_error(test_data, mesh)\n        np.testing.assert_allclose(\n            abs_error, expected_absolute_truncation_error, 1e-12, 1e-12\n        )\n\n    # Check that the convergence rate for a straight line is consistent with\n    # the analytic expectation\n    def test_convergence_rate_and_pile_up_modes(self):\n        slope, offset = -0.4, 1.4\n        modes = np.arange(0, 14, 1)\n        filtered_modes = 3\n        test_power_monitor = 10.0 ** (slope * modes + offset)\n        rate = convergence_rate_and_number_of_pile_up_modes(\n            test_power_monitor, filtered_modes\n        )[\"convergence_rate\"]\n        np.testing.assert_allclose(rate, -slope)\n\n        # Check that a straight line with some pile up modes added by hand\n        # recovers the correct integer number of pile up modes\n        expected_pile_up_modes = 5\n        modes_to_fill = filtered_modes + expected_pile_up_modes\n        for i in range(0, modes_to_fill, 1):\n            test_power_monitor[len(test_power_monitor) - i - 1] = (\n                test_power_monitor[len(test_power_monitor) - modes_to_fill - 1]\n            )\n        pile_up_modes = convergence_rate_and_number_of_pile_up_modes(\n            test_power_monitor, filtered_modes\n        )[\"number_of_pile_up_modes\"]\n        np.testing.assert_allclose(\n            np.floor(pile_up_modes), expected_pile_up_modes\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_CoefficientTransforms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n\n// These tests generate a function `u_nodal_expected` from a linear\n// superposition of the basis functions, which are then transformed to spectral\n// space (`u_modal`). The coefficients are compared to their expected values,\n// which is 1 if the coefficient is specified in `coeffs_to_include` and 0\n// otherwise. Finally, the modal coefficients are transformed back to the nodal\n// coefficients and compared to `u_nodal_expected`.\nnamespace {\ntemplate <typename ModalVectorType, typename NodalVectorType,\n          Spectral::Basis Basis, Spectral::Quadrature Quadrature, size_t Dim>\nvoid check_transforms(\n    const Mesh<Dim>& mesh,\n    const std::vector<std::array<size_t, Dim>>& coeffs_to_include) {\n  CAPTURE(Basis);\n  CAPTURE(Quadrature);\n  CAPTURE(mesh);\n  CAPTURE(coeffs_to_include);\n\n  MAKE_GENERATOR(generator);\n  UniformCustomDistribution<\n      tt::get_fundamental_type_t<typename ModalVectorType::ElementType>>\n      dist{0.5, 2.0};\n  const auto basis_factor =\n      make_with_random_values<typename ModalVectorType::ElementType>(\n          make_not_null(&generator), make_not_null(&dist));\n\n  // Construct a functions:\n  //\n  // 1D: u(\\xi) = sum_i c_i phi_i(\\xi)\n  // 2D: u(\\xi,\\eta) = sum_{i,j} c_{i,j} phi_i(\\xi) phi_j(\\eta)\n  // 3D: u(\\xi,\\eta,\\zeta) = sum_{i,j,k} c_{i,j,k} phi_i(\\xi)\n  //                                     phi_j(\\eta) phi_k(\\zeta)\n  //\n  // where the coefficients c_{i,j,k} are 1 if listed in `coeffs_to_include` and\n  // 0 otherwise.\n  const auto logical_coords = logical_coordinates(mesh);\n  NodalVectorType u_nodal_expected(mesh.number_of_grid_points(), 0.0);\n  for (const auto& coeff : coeffs_to_include) {\n    // additional * 1.0 in appropriate type necessary for being generic to\n    // complex values\n    NodalVectorType basis_function =\n        basis_factor * Spectral::compute_basis_function_value<Basis>(\n                           coeff[0], get<0>(logical_coords));\n    for (size_t dim = 1; dim < Dim; ++dim) {\n      basis_function *= typename ModalVectorType::ElementType{1.0} *\n                        Spectral::compute_basis_function_value<Basis>(\n                            gsl::at(coeff, dim), logical_coords.get(dim));\n    }\n    u_nodal_expected += basis_function;\n  }\n\n  // Transform to modal coefficients and check their values\n  const ModalVectorType u_modal = to_modal_coefficients(u_nodal_expected, mesh);\n  for (IndexIterator<Dim> index_it(mesh.extents()); index_it; ++index_it) {\n    CAPTURE(*index_it);\n    Approx local_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n    if (alg::found(coeffs_to_include, index_it->indices())) {\n      CHECK_ITERABLE_CUSTOM_APPROX(u_modal[index_it.collapsed_index()],\n                                   basis_factor, local_approx);\n    } else {\n      CHECK_ITERABLE_CUSTOM_APPROX(u_modal[index_it.collapsed_index()],\n                                   typename ModalVectorType::ElementType{0.0},\n                                   local_approx);\n    }\n  }\n\n  // Back to nodal coefficients, which should match what we set up initially\n  const NodalVectorType u_nodal = to_nodal_coefficients(u_modal, mesh);\n  Approx local_approx = Approx::custom().epsilon(1e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(u_nodal_expected, u_nodal, local_approx);\n}\n\ntemplate <typename ModalVectorType, typename NodalVectorType,\n          Spectral::Basis Basis, Spectral::Quadrature Quadrature,\n          typename Generator>\nvoid test_1d(const gsl::not_null<Generator*> generator) {\n  UniformCustomDistribution<size_t> dist{\n      1, Spectral::maximum_number_of_points<Basis> - 1};\n  const size_t order = dist(*generator);\n  // Start at 1st order so we are independent of the minimum number of\n  // coefficients.\n  CAPTURE(order);\n  const Mesh<1> mesh(order + 1, Basis, Quadrature);\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      mesh, {{{order}}});\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      mesh, {{{order}}, {{order / 2}}});\n  if (order > 4) {\n    check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n        mesh, {{{order}}, {{order / 3}}});\n  }\n}\n\ntemplate <typename ModalVectorType, typename NodalVectorType,\n          Spectral::Basis Basis, Spectral::Quadrature Quadrature,\n          typename Generator>\nvoid test_2d(const gsl::not_null<Generator*> generator) {\n  // Start at one higher order so we can drop one order in the y-direction.\n  UniformCustomDistribution<size_t> dist{\n      Spectral::minimum_number_of_points<Basis, Quadrature> + 1,\n      Spectral::maximum_number_of_points<Basis> - 1};\n  const size_t order = dist(*generator);\n  CAPTURE(order);\n  const Mesh<2> mesh({{order + 1, order}}, Basis, Quadrature);\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      mesh, {{{order, order - 1}}});\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      mesh, {{{order, order - 1}}, {{order / 2, order / 2}}});\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      Mesh<2>{{{order + 1, order + 1}}, Basis, Quadrature},\n      {{{order - 1, order}}, {{order / 2, order / 2}}});\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      Mesh<2>{{{order + 1, order + 1}}, Basis, Quadrature},\n      {{{order, order}}, {{order / 2, order / 2}}});\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      mesh, {{{order, order - 1}}, {{order / 3, order / 3}}});\n}\n\ntemplate <typename ModalVectorType, typename NodalVectorType,\n          Spectral::Basis Basis, Spectral::Quadrature Quadrature,\n          typename Generator>\nvoid test_3d(const gsl::not_null<Generator*> generator) {\n  // Start at two orders higher so we can drop one order in the y-direction and\n  // two in z-direction.\n  UniformCustomDistribution<size_t> dist{\n      Spectral::minimum_number_of_points<Basis, Quadrature> + 2,\n      Spectral::maximum_number_of_points<Basis> - 2};\n  const size_t order = dist(*generator);\n  CAPTURE(order);\n  const Mesh<3> mesh({{order + 1, order, order - 1}}, Basis, Quadrature);\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      mesh, {{{order, order - 1, order - 2}}});\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      mesh,\n      {{{order, order - 1, order - 2}}, {{order / 2, order / 2, order / 2}}});\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      Mesh<3>{{{order + 1, order + 1, order + 1}}, Basis, Quadrature},\n      {{{order, order, order}}});\n  check_transforms<ModalVectorType, NodalVectorType, Basis, Quadrature>(\n      mesh,\n      {{{order, order - 1, order - 2}}, {{order / 3, order / 3, order / 3}}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.CoefficientTransforms\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test_1d<ModalVector, DataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_1d<ModalVector, DataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n  test_2d<ModalVector, DataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_2d<ModalVector, DataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n  test_3d<ModalVector, DataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_3d<ModalVector, DataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n\n  test_1d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_1d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n  test_2d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_2d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n  test_3d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_3d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Legendre,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n\n  test_1d<ModalVector, DataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_1d<ModalVector, DataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n  test_2d<ModalVector, DataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_2d<ModalVector, DataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n  test_3d<ModalVector, DataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_3d<ModalVector, DataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n\n  test_1d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_1d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n  test_2d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_2d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n  test_3d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::GaussLobatto>(make_not_null(&generator));\n  test_3d<ComplexModalVector, ComplexDataVector, Spectral::Basis::Chebyshev,\n          Spectral::Quadrature::Gauss>(make_not_null(&generator));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_DefiniteIntegral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/DiskTestFunctions.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/FourierTestFunctions.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\nvoid test_definite_integral_0d() {\n  const double value = 1.234;\n  const DataVector data(1, value);\n  CHECK(value == approx(definite_integral(data, Mesh<0>{})));\n}\n\nvoid test_definite_integral_1d(const Mesh<1>& mesh) {\n  const DataVector& x = Spectral::collocation_points(mesh);\n  DataVector integrand(mesh.number_of_grid_points());\n  for (size_t a = 0; a < mesh.extents(0); ++a) {\n    for (size_t s = 0; s < integrand.size(); ++s) {\n      integrand[s] = pow(x[s], a);\n    }\n    if (0 == a % 2) {\n      CHECK(2.0 / (a + 1.0) == approx(definite_integral(integrand, mesh)));\n    } else {\n      CHECK(0.0 == approx(definite_integral(integrand, mesh)));\n    }\n  }\n}\n\n// Test for finite difference methods using midpoint rule.\nvoid test_midpoint_integral_1d(const Mesh<1>& mesh) {\n  const DataVector& x = Spectral::collocation_points(mesh);\n  const size_t number_of_points = mesh.number_of_grid_points();\n  DataVector integrand(number_of_points);\n  for (size_t a = 0; a < 3; ++a) {\n    for (size_t s = 0; s < integrand.size(); ++s) {\n      integrand[s] = pow(x[s], a);\n    }\n    if (0 == a) {\n      CHECK(2.0 == approx(definite_integral(integrand, mesh)));\n    }\n    if (1 == a) {\n      CHECK(0.0 == approx(definite_integral(integrand, mesh)));\n    }\n    if (2 ==a) {\n      // Correct error should be \\f$2/(3N^2)\\f$ for N points in the mesh\n      CHECK(2.0/3.0*(1.0-1.0/(number_of_points*number_of_points)) ==\n            approx(definite_integral(integrand, mesh)));\n    }\n  }\n}\n\n\nvoid test_definite_integral_2d(const Mesh<2>& mesh) {\n  const DataVector& x = Spectral::collocation_points(mesh.slice_through(0));\n  const DataVector& y = Spectral::collocation_points(mesh.slice_through(1));\n  DataVector integrand(mesh.number_of_grid_points());\n  for (size_t a = 0; a < mesh.extents(0); ++a) {\n    for (size_t b = 0; b < mesh.extents(1); ++b) {\n      for (IndexIterator<2> index_it(mesh.extents()); index_it; ++index_it) {\n        integrand[index_it.collapsed_index()] =\n            pow(x[index_it()[0]], a) * pow(y[index_it()[1]], b);\n      }\n      if (0 == a % 2 and 0 == b % 2) {\n        CHECK(4.0 / ((a + 1.0) * (b + 1.0)) ==\n              approx(definite_integral(integrand, mesh)));\n      } else {\n        CHECK(0.0 == approx(definite_integral(integrand, mesh)));\n      }\n    }\n  }\n}\n\nvoid test_definite_integral_3d(const Mesh<3>& mesh) {\n  const DataVector& x = Spectral::collocation_points(mesh.slice_through(0));\n  const DataVector& y = Spectral::collocation_points(mesh.slice_through(1));\n  const DataVector& z = Spectral::collocation_points(mesh.slice_through(2));\n  DataVector integrand(mesh.number_of_grid_points());\n  for (size_t a = 0; a < mesh.extents(0); ++a) {\n    for (size_t b = 0; b < mesh.extents(1); ++b) {\n      for (size_t c = 0; c < mesh.extents(2); ++c) {\n        for (IndexIterator<3> index_it(mesh.extents()); index_it; ++index_it) {\n          integrand[index_it.collapsed_index()] = pow(x[index_it()[0]], a) *\n                                                  pow(y[index_it()[1]], b) *\n                                                  pow(z[index_it()[2]], c);\n        }\n        if (0 == a % 2 and 0 == b % 2 and 0 == c % 2) {\n          CHECK(8.0 / ((a + 1.0) * (b + 1.0) * (c + 1.0)) ==\n                approx(definite_integral(integrand, mesh)));\n        } else {\n          CHECK(0.0 == approx(definite_integral(integrand, mesh)));\n        }\n      }\n    }\n  }\n}\n\nvoid test_definite_integral_spherical_shell(const size_t n_r, const size_t L) {\n  CAPTURE(n_r);\n  CAPTURE(L);\n  const Mesh<3> mesh{\n      {n_r, L + 1, 2 * L + 1},\n      {Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n       Spectral::Basis::SphericalHarmonic},\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss,\n       Spectral::Quadrature::Equiangular}};\n  const auto xi_vector = logical_coordinates(mesh);\n  const DataVector r = xi_vector.get(0) + 2.0;\n  for (size_t pow_nx = 0; pow_nx <= L; ++pow_nx) {\n    CAPTURE(pow_nx);\n    for (size_t pow_ny = 0; pow_ny <= L - pow_nx; ++pow_ny) {\n      CAPTURE(pow_ny);\n      for (size_t pow_nz = 0; pow_nz <= L - pow_nx - pow_ny; ++pow_nz) {\n        CAPTURE(pow_nz);\n        const YlmTestFunctions::ProductOfPolynomials f{pow_nx, pow_ny, pow_nz};\n\n        const DataVector integrand =\n            r * f(get<1>(xi_vector), get<2>(xi_vector));\n        CHECK(4.0 * f.definite_integral() ==\n              approx(definite_integral(integrand, mesh)));\n      }\n    }\n  }\n}\n\nvoid test_definite_integral_disk(const size_t n_r, const size_t n_ph) {\n  CAPTURE(n_r);\n  CAPTURE(n_ph);\n  const Mesh<2> mesh{{n_r, n_ph},\n                     {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2},\n                     {Spectral::Quadrature::GaussRadauUpper,\n                      Spectral::Quadrature::Equiangular}};\n  const size_t M = n_ph / 2;\n  const auto xi_vector = logical_coordinates(mesh);\n  const DataVector r = 0.5 * (get<0>(xi_vector) + 1.0);\n  const DataVector& phi = get<1>(xi_vector);\n  for (size_t pow_nx = 0; pow_nx <= M; ++pow_nx) {\n    CAPTURE(pow_nx);\n    for (size_t pow_ny = 0; pow_ny <= M - pow_nx; ++pow_ny) {\n      CAPTURE(pow_ny);\n      const DiskTestFunctions::ProductOfPolynomials f{pow_nx, pow_ny};\n      const DataVector integrand = r * f(r, phi);\n      CHECK(f.definite_integral() ==\n            approx(definite_integral(integrand, mesh)));\n    }\n  }\n}\n\nvoid test_definite_integral_cylinder(const size_t n_r, const size_t n_ph,\n                                     const size_t n_z) {\n  CAPTURE(n_r);\n  CAPTURE(n_ph);\n  CAPTURE(n_z);\n  const Mesh<3> mesh{\n      {n_r, n_ph, n_z},\n      {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2,\n       Spectral::Basis::Legendre},\n      {Spectral::Quadrature::GaussRadauUpper, Spectral::Quadrature::Equiangular,\n       Spectral::Quadrature::GaussLobatto}};\n  const size_t M = n_ph / 2;\n  const auto xi_vector = logical_coordinates(mesh);\n  const DataVector r = 0.5 * (get<0>(xi_vector) + 1.0);\n  const DataVector& phi = get<1>(xi_vector);\n  const DataVector& z = get<2>(xi_vector);\n  for (size_t pow_nx = 0; pow_nx <= M; ++pow_nx) {\n    CAPTURE(pow_nx);\n    for (size_t pow_ny = 0; pow_ny <= M - pow_nx; ++pow_ny) {\n      CAPTURE(pow_ny);\n      const DiskTestFunctions::ProductOfPolynomials f{pow_nx, pow_ny};\n      const double disk_integral = f.definite_integral();\n      for (size_t pow_nz = 0; pow_nz < n_z; ++pow_nz) {\n        CAPTURE(pow_nz);\n        const DataVector integrand = r * f(r, phi) * pow(z, pow_nz);\n        const double z_integral =\n            (0 == pow_nz % 2) ? 2.0 / (static_cast<double>(pow_nz) + 1.0) : 0.0;\n        CHECK(disk_integral * z_integral ==\n              approx(definite_integral(integrand, mesh)));\n      }\n    }\n  }\n}\n\nvoid test_definite_integral_hollow_disk(const size_t n_r, const size_t n_ph) {\n  CAPTURE(n_r);\n  CAPTURE(n_ph);\n  const Mesh<2> mesh{\n      {n_r, n_ph},\n      {Spectral::Basis::Legendre, Spectral::Basis::Fourier},\n      {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Equiangular}};\n  const size_t M = n_ph / 2;\n  const auto xi_vector = logical_coordinates(mesh);\n  const DataVector& r = get<0>(xi_vector);\n  const DataVector& phi = get<1>(xi_vector);\n  for (size_t pow_nx = 0; pow_nx <= M; ++pow_nx) {\n    CAPTURE(pow_nx);\n    for (size_t pow_ny = 0; pow_ny <= M - pow_nx; ++pow_ny) {\n      CAPTURE(pow_ny);\n      const FourierTestFunctions::ProductOfPolynomials f{pow_nx, pow_ny};\n      const double angular_integral = f.definite_integral();\n      for (size_t pow_nr = 0; pow_nr < n_r; ++pow_nr) {\n        CAPTURE(pow_nr);\n        const DataVector integrand = f(phi) * pow(r, pow_nr);\n        const double z_integral =\n            (0 == pow_nr % 2) ? 2.0 / (static_cast<double>(pow_nr) + 1.0) : 0.0;\n        CHECK(angular_integral * z_integral ==\n              approx(definite_integral(integrand, mesh)));\n      }\n    }\n  }\n}\n\nvoid test_definite_integral_hollow_cylinder(const size_t n_r, const size_t n_ph,\n                                            const size_t n_z) {\n  CAPTURE(n_r);\n  CAPTURE(n_ph);\n  CAPTURE(n_z);\n  const Mesh<3> mesh{\n      {n_r, n_ph, n_z},\n      {Spectral::Basis::Legendre, Spectral::Basis::Fourier,\n       Spectral::Basis::Legendre},\n      {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Equiangular,\n       Spectral::Quadrature::GaussLobatto}};\n  const size_t M = n_ph / 2;\n  const auto xi_vector = logical_coordinates(mesh);\n  const DataVector& r = get<0>(xi_vector);\n  const DataVector& phi = get<1>(xi_vector);\n  const DataVector& z = get<2>(xi_vector);\n  for (size_t pow_nx = 0; pow_nx <= M; ++pow_nx) {\n    CAPTURE(pow_nx);\n    for (size_t pow_ny = 0; pow_ny <= M - pow_nx; ++pow_ny) {\n      CAPTURE(pow_ny);\n      const FourierTestFunctions::ProductOfPolynomials f{pow_nx, pow_ny};\n      const double angular_integral = f.definite_integral();\n      for (size_t pow_nr = 0; pow_nr < n_r; ++pow_nr) {\n        CAPTURE(pow_nr);\n        for (size_t pow_nz = 0; pow_nz < n_z; ++pow_nz) {\n          CAPTURE(pow_nz);\n          const DataVector integrand = f(phi) * pow(r, pow_nr) * pow(z, pow_nz);\n          const double z_integral =\n              (0 == pow_nr % 2 and 0 == pow_nz % 2)\n                  ? 4.0 / ((static_cast<double>(pow_nr) + 1.0) *\n                           (static_cast<double>(pow_nz) + 1.0))\n                  : 0.0;\n          CHECK(angular_integral * z_integral ==\n                approx(definite_integral(integrand, mesh)));\n        }\n      }\n    }\n  }\n}\n\nvoid test_definite_integral_cartoon_spherical(const size_t n_x) {\n  const Mesh mesh =\n      Mesh<3>{{{n_x, 1, 1}},\n              {{Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n                Spectral::Basis::Cartoon}},\n              {{Spectral::Quadrature::GaussLobatto,\n                Spectral::Quadrature::SphericalSymmetry,\n                Spectral::Quadrature::SphericalSymmetry}}};\n  const DataVector& x = Spectral::collocation_points(mesh.slice_through(0));\n  DataVector integrand(mesh.number_of_grid_points());\n  for (size_t a = 0; a < mesh.extents(0); ++a) {\n    for (size_t s = 0; s < integrand.size(); ++s) {\n      integrand[s] = pow(x[s], static_cast<double>(a));\n    }\n    if (0 == a % 2) {\n      CHECK(8.0 * M_PI / (a + 1.0) ==\n            approx(definite_integral(integrand, mesh)));\n    } else {\n      CHECK(0.0 == approx(definite_integral(integrand, mesh)));\n    }\n  }\n}\nvoid test_definite_inegral_cartoon_axial(const size_t n_x, const size_t n_y) {\n  const Mesh mesh = Mesh<3>{\n      {{n_x, n_y, 1}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n        Spectral::Basis::Cartoon}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::AxialSymmetry}}};\n  const DataVector& x = Spectral::collocation_points(mesh.slice_through(0));\n  const DataVector& y = Spectral::collocation_points(mesh.slice_through(1));\n  DataVector integrand(mesh.number_of_grid_points());\n  for (size_t a = 0; a < mesh.extents(0); ++a) {\n    for (size_t b = 0; b < mesh.extents(1); ++b) {\n      for (IndexIterator<2> index_it(mesh.slice_through(0, 1).extents());\n           index_it; ++index_it) {\n        integrand[index_it.collapsed_index()] =\n            pow(x[index_it()[0]], static_cast<double>(a)) *\n            pow(y[index_it()[1]], static_cast<double>(b));\n      }\n      if (0 == a % 2 and 0 == b % 2) {\n        CHECK(8.0 * M_PI / ((a + 1.0) * (b + 1.0)) ==\n              approx(definite_integral(integrand, mesh)));\n      } else {\n        CHECK(0.0 == approx(definite_integral(integrand, mesh)));\n      }\n    }\n  }\n}\n\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.DefiniteIntegral\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  test_definite_integral_0d();\n\n  constexpr size_t min_extents =\n      Spectral::minimum_number_of_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>;\n  constexpr size_t max_extents =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n  for (size_t n0 = min_extents; n0 <= max_extents; ++n0) {\n    test_definite_integral_1d(Mesh<1>{n0, Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::GaussLobatto});\n  }\n  for (size_t n0 = min_extents; n0 <= max_extents; ++n0) {\n    for (size_t n1 = min_extents; n1 <= max_extents - 1; ++n1) {\n      test_definite_integral_2d(Mesh<2>{{{n0, n1}},\n                                        Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto});\n    }\n  }\n  for (size_t n0 = min_extents; n0 <= std::min(6_st, max_extents); ++n0) {\n    for (size_t n1 = min_extents; n1 <= std::min(7_st, max_extents); ++n1) {\n      for (size_t n2 = min_extents; n2 <= std::min(8_st, max_extents); ++n2) {\n        test_definite_integral_3d(Mesh<3>{{{n0, n1, n2}},\n                                          Spectral::Basis::Legendre,\n                                          Spectral::Quadrature::GaussLobatto});\n      }\n    }\n  }\n\n  for (size_t n_r = 2; n_r < 5; ++n_r) {\n    for (size_t L = 2; L < 9; ++L) {\n      test_definite_integral_spherical_shell(n_r, L);\n    }\n  }\n\n  for (size_t n_r = 1; n_r < 9; ++n_r) {\n    for (size_t n_ph = 3; n_ph < 15; n_ph += 2) {\n      // We enforce these restrictions for the disk\n      if (n_ph / 2 <= 2 * n_r - 2) {\n        test_definite_integral_disk(n_r, n_ph);\n        for (size_t n_z = 2; n_z < 5; ++n_z) {\n          test_definite_integral_cylinder(n_r, n_ph, n_z);\n        }\n      }\n    }\n  }\n\n  for (size_t n_r = 2; n_r < 9; ++n_r) {\n    for (size_t n_ph = 3; n_ph < 15; n_ph += 2) {\n      test_definite_integral_hollow_disk(n_r, n_ph);\n      for (size_t n_z = 2; n_z < 5; ++n_z) {\n        test_definite_integral_hollow_cylinder(n_r, n_ph, n_z);\n      }\n    }\n  }\n\n  for (size_t n_x = min_extents; n_x <= max_extents; ++n_x) {\n    test_definite_integral_cartoon_spherical(n_x);\n    for (size_t n_y = min_extents; n_y <= max_extents - 1; ++n_y) {\n      test_definite_inegral_cartoon_axial(n_x, n_y);\n    }\n  }\n\n  // Test finite difference integral\n  constexpr size_t min_extents_fd =\n      Spectral::minimum_number_of_points<Spectral::Basis::FiniteDifference,\n                                         Spectral::Quadrature::CellCentered>;\n  constexpr size_t max_extents_fd =\n    Spectral::maximum_number_of_points<Spectral::Basis::FiniteDifference>;\n  for (size_t n0 = min_extents_fd; n0 <= max_extents_fd; ++n0) {\n    test_midpoint_integral_1d(Mesh<1>{n0, Spectral::Basis::FiniteDifference,\n                                      Spectral::Quadrature::CellCentered});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_Divergence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <utility>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/Variables/FrameTransform.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"PointwiseFunctions/MathFunctions/TensorProduct.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Identity1D = domain::CoordinateMaps::Identity<1>;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t VolumeDim>\nauto make_affine_map();\n\ntemplate <>\nauto make_affine_map<1>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine{-1.0, 1.0, -0.3, 0.7});\n}\n\ntemplate <>\nauto make_affine_map<2>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine2D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55}});\n}\n\ntemplate <>\nauto make_affine_map<3>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine3D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n               Affine{-1.0, 1.0, 2.3, 2.8}});\n}\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Flux1 : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim, Frame>;\n  static auto flux(const MathFunctions::TensorProduct<Dim>& f,\n                   const tnsr::I<DataVector, Dim, Frame>& x) {\n    auto result = make_with_value<tnsr::I<DataType, Dim, Frame>>(x, 0.);\n    const auto f_of_x = f(x);\n    for (size_t d = 0; d < Dim; ++d) {\n      result.get(d) = (d + 0.5) * get(f_of_x);\n    }\n    if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n      for (size_t d = 0; d < Dim; ++d) {\n        result.get(d) +=\n            std::complex<double>(0., static_cast<double>(d) + 1.5) *\n            get(f_of_x);\n      }\n    }\n    return result;\n  }\n  static auto divergence_of_flux(const MathFunctions::TensorProduct<Dim>& f,\n                                 const tnsr::I<DataVector, Dim, Frame>& x) {\n    auto result = make_with_value<Scalar<DataType>>(x, 0.);\n    const auto df = f.first_derivatives(x);\n    for (size_t d = 0; d < Dim; ++d) {\n      get(result) += (d + 0.5) * df.get(d);\n    }\n    if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n      for (size_t d = 0; d < Dim; ++d) {\n        get(result) +=\n            std::complex<double>(0., static_cast<double>(d) + 1.5) * df.get(d);\n      }\n    }\n    return result;\n  }\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nstruct Flux2 : db::SimpleTag {\n  using type = tnsr::Ij<DataType, Dim, Frame>;\n  static auto flux(const MathFunctions::TensorProduct<Dim>& f,\n                   const tnsr::I<DataVector, Dim, Frame>& x) {\n    auto result = make_with_value<tnsr::Ij<DataType, Dim, Frame>>(x, 0.);\n    const auto f_of_x = f(x);\n    for (size_t d = 0; d < Dim; ++d) {\n      for (size_t j = 0; j < Dim; ++j) {\n        result.get(d, j) = (d + 0.5) * (j + 0.25) * get(f_of_x);\n      }\n    }\n    return result;\n  }\n  static auto divergence_of_flux(const MathFunctions::TensorProduct<Dim>& f,\n                                 const tnsr::I<DataVector, Dim, Frame>& x) {\n    auto result = make_with_value<tnsr::i<DataType, Dim, Frame>>(x, 0.);\n    const auto df = f.first_derivatives(x);\n    for (size_t j = 0; j < Dim; ++j) {\n      for (size_t d = 0; d < Dim; ++d) {\n        result.get(j) += (d + 0.5) * (j + 0.25) * df.get(d);\n      }\n    }\n    return result;\n  }\n};\n\ntemplate <typename DataType, size_t Dim, typename Frame>\nusing two_fluxes =\n    tmpl::list<Flux1<DataType, Dim, Frame>, Flux2<DataType, Dim, Frame>>;\n\ntemplate <typename DataType, size_t Dim, typename Frame = Frame::Inertial>\nvoid test_divergence_impl(\n    const Mesh<Dim>& mesh,\n    std::array<std::unique_ptr<MathFunction<1, Frame>>, Dim> functions) {\n  const auto coordinate_map = make_affine_map<Dim>();\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  const auto xi = logical_coordinates(mesh);\n  const auto x = coordinate_map(xi);\n  const auto inv_jacobian = coordinate_map.inv_jacobian(xi);\n  const auto det_jacobian = determinant(coordinate_map.jacobian(xi));\n  MathFunctions::TensorProduct<Dim> f(1.0, std::move(functions));\n  using flux_tags = two_fluxes<DataType, Dim, Frame>;\n  Variables<flux_tags> fluxes(num_grid_points);\n  Variables<db::wrap_tags_in<Tags::div, flux_tags>> expected_div_fluxes(\n      num_grid_points);\n  tmpl::for_each<flux_tags>([&x, &f, &fluxes, &expected_div_fluxes](auto tag) {\n    using FluxTag = tmpl::type_from<decltype(tag)>;\n    get<FluxTag>(fluxes) = FluxTag::flux(f, x);\n    using DivFluxTag = Tags::div<FluxTag>;\n    get<DivFluxTag>(expected_div_fluxes) = FluxTag::divergence_of_flux(f, x);\n  });\n  const auto div_fluxes = divergence(fluxes, mesh, inv_jacobian);\n  CHECK(div_fluxes.size() == expected_div_fluxes.size());\n  CHECK(Dim * div_fluxes.size() == fluxes.size());\n  Approx local_approx = Approx::custom().epsilon(1.e-11).scale(1.);\n  CHECK_VARIABLES_CUSTOM_APPROX(div_fluxes, expected_div_fluxes, local_approx);\n\n  // Test divergence of a single tensor\n  const auto div_vector =\n      divergence(get<Flux1<DataType, Dim, Frame>>(fluxes), mesh, inv_jacobian);\n  const auto& expected =\n      get<Tags::div<Flux1<DataType, Dim, Frame>>>(div_fluxes);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected, div_vector, local_approx);\n\n  // Test logical divergence\n  auto logical_fluxes =\n      transform::first_index_to_different_frame(fluxes, inv_jacobian);\n  Variables<db::wrap_tags_in<Tags::div, flux_tags>> logical_div_fluxes(\n      num_grid_points);\n  logical_divergence(make_not_null(&logical_div_fluxes), logical_fluxes, mesh);\n  CHECK_VARIABLES_CUSTOM_APPROX(logical_div_fluxes, expected_div_fluxes,\n                                local_approx);\n}\n\ntemplate <typename DataType>\nvoid test_divergence() {\n  using TensorTag = Flux1<DataType, 1, Frame::Inertial>;\n  TestHelpers::db::test_prefix_tag<Tags::div<TensorTag>>(\"div(Flux1)\");\n\n  const size_t n0 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2;\n  const size_t n1 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2 + 1;\n  const size_t n2 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2 - 1;\n  const Mesh<1> mesh_1d{\n      {{n0}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> mesh_2d{{{n0, n1}},\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> mesh_3d{{{n0, n1, n2}},\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  for (size_t a = 0; a < 5; ++a) {\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 1>\n        functions_1d{\n            {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a)}};\n    test_divergence_impl<DataType>(mesh_1d, std::move(functions_1d));\n    for (size_t b = 0; b < 4; ++b) {\n      std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 2>\n          functions_2d{\n              {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a),\n               std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(b)}};\n      test_divergence_impl<DataType>(mesh_2d, std::move(functions_2d));\n      for (size_t c = 0; c < 3; ++c) {\n        std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 3>\n            functions_3d{\n                {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a),\n                 std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(b),\n                 std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(c)}};\n        test_divergence_impl<DataType>(mesh_3d, std::move(functions_3d));\n      }\n    }\n  }\n}\n\ntemplate <class MapType>\nstruct MapTag : db::SimpleTag {\n  static constexpr size_t dim = MapType::dim;\n  using target_frame = typename MapType::target_frame;\n  using source_frame = typename MapType::source_frame;\n\n  using type = MapType;\n};\n\ntemplate <size_t Dim, typename Frame = Frame::Inertial>\nvoid test_divergence_compute_item_impl(\n    const Mesh<Dim>& mesh,\n    std::array<std::unique_ptr<MathFunction<1, Frame>>, Dim> functions) {\n  const auto coordinate_map = make_affine_map<Dim>();\n  using map_tag = MapTag<std::decay_t<decltype(coordinate_map)>>;\n  using mesh_tag = domain::Tags::Mesh<Dim>;\n  using inv_jac_tag = domain::Tags::InverseJacobianCompute<\n      map_tag, typename domain::Tags::LogicalCoordinates<Dim>::base>;\n  using flux_tags = two_fluxes<DataVector, Dim, Frame>;\n  using flux_tag = Tags::Variables<flux_tags>;\n  using div_tags = db::wrap_tags_in<Tags::div, flux_tags>;\n  TestHelpers::db::test_compute_tag<Tags::DivVariablesCompute<\n      flux_tag, mesh_tag, typename inv_jac_tag::base>>(\n      \"Variables(div(Flux1),div(Flux2))\");\n  TestHelpers::db::test_compute_tag<Tags::DivVectorCompute<\n      Flux1<DataVector, Dim, Frame>, mesh_tag, typename inv_jac_tag::base>>(\n      \"div(Flux1)\");\n\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  const auto xi = logical_coordinates(mesh);\n  const auto x = coordinate_map(xi);\n  const auto inv_jacobian = coordinate_map.inv_jacobian(xi);\n  MathFunctions::TensorProduct<Dim> f(1.0, std::move(functions));\n  Variables<flux_tags> fluxes(num_grid_points);\n  Variables<div_tags> expected_div_fluxes(num_grid_points);\n\n  tmpl::for_each<flux_tags>([&x, &f, &fluxes, &expected_div_fluxes](auto tag) {\n    using FluxTag = tmpl::type_from<decltype(tag)>;\n    get<FluxTag>(fluxes) = FluxTag::flux(f, x);\n    using DivFluxTag = Tags::div<FluxTag>;\n    get<DivFluxTag>(expected_div_fluxes) = FluxTag::divergence_of_flux(f, x);\n  });\n\n  auto box = db::create<\n      db::AddSimpleTags<mesh_tag, flux_tag, map_tag>,\n      db::AddComputeTags<domain::Tags::LogicalCoordinates<Dim>, inv_jac_tag,\n                         Tags::DivVariablesCompute<\n                             flux_tag, mesh_tag, typename inv_jac_tag::base>>>(\n      mesh, fluxes, coordinate_map);\n\n  const auto& div_fluxes = db::get<Tags::Variables<div_tags>>(box);\n\n  CHECK(div_fluxes.size() == expected_div_fluxes.size());\n  Approx local_approx = Approx::custom().epsilon(1.e-11).scale(1.);\n  CHECK_VARIABLES_CUSTOM_APPROX(div_fluxes, expected_div_fluxes, local_approx);\n\n  auto box2 = db::create<\n      db::AddSimpleTags<mesh_tag, flux_tag, map_tag>,\n      db::AddComputeTags<\n          domain::Tags::LogicalCoordinates<Dim>, inv_jac_tag,\n          Tags::DivVectorCompute<Flux1<DataVector, Dim, Frame>, mesh_tag,\n                                 typename inv_jac_tag::base>>>(mesh, fluxes,\n                                                               coordinate_map);\n\n  const auto& div_flux1 =\n      db::get<Tags::div<Flux1<DataVector, Dim, Frame>>>(box2);\n  const auto& expected =\n      get<Tags::div<Flux1<DataVector, Dim, Frame>>>(div_fluxes);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected, div_flux1, local_approx);\n}\n\nvoid test_divergence_compute() {\n  const size_t n0 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2;\n  const size_t n1 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2 + 1;\n  const size_t n2 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2 - 1;\n  const Mesh<1> mesh_1d{\n      {{n0}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> mesh_2d{{{n0, n1}},\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<3> mesh_3d{{{n0, n1, n2}},\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  for (size_t a = 0; a < 5; ++a) {\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 1>\n        functions_1d{\n            {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a)}};\n    test_divergence_compute_item_impl(mesh_1d, std::move(functions_1d));\n    for (size_t b = 0; b < 4; ++b) {\n      std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 2>\n          functions_2d{\n              {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a),\n               std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(b)}};\n      test_divergence_compute_item_impl(mesh_2d, std::move(functions_2d));\n      for (size_t c = 0; c < 3; ++c) {\n        std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 3>\n            functions_3d{\n                {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a),\n                 std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(b),\n                 std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(c)}};\n        test_divergence_compute_item_impl(mesh_3d, std::move(functions_3d));\n      }\n    }\n  }\n}\n\ntemplate <bool Spherical>\nDataVector cartoon_func(const tnsr::I<DataVector, 3, Frame::Inertial>& coords) {\n  if constexpr (Spherical) {\n    // a radial function, f(r) = f(x) because the computational domain is the x\n    // axis\n    return 0.01 * pow(get<0>(coords), 4) + 0.3 * pow(get<0>(coords), 3) -\n           0.1 * pow(get<0>(coords), 2) - 2.0 * get<0>(coords) - 1.5;\n  } else {\n    // an axially symmetric function about the y axis,\n    // f(\\sqrt{x^2 + z^2}, y) = f(x, y) because the computational domain is the\n    // x-y plane\n    return square(get<1>(coords)) + square(get<0>(coords)) * get<1>(coords);\n  }\n}\n\ntemplate <bool Spherical>\nDataVector cartoon_dfunc(\n    size_t deriv_index, const tnsr::I<DataVector, 3, Frame::Inertial>& coords) {\n  if constexpr (Spherical) {\n    if (deriv_index == 0) {\n      return 0.04 * pow(get<0>(coords), 3) + 0.9 * pow(get<0>(coords), 2) -\n             0.2 * get<0>(coords) - 2.0;\n    } else {\n      return {get<0>(coords).size(), 0.0};\n    }\n  } else {\n    if (deriv_index == 0) {\n      return 2.0 * get<0>(coords) * get<1>(coords);\n    } else if (deriv_index == 1) {\n      return 2.0 * get<1>(coords) + square(get<0>(coords));\n    } else {\n      return {get<0>(coords).size(), 0.0};\n    }\n  }\n}\n\ntemplate <bool Spherical>\nvoid test_cartoon(const double x_start) {\n  Mesh<3> mesh;\n  tnsr::I<DataVector, 3, Frame::Inertial> coords;\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inv_jacobian;\n\n  const Identity1D identity_cartoon_map;\n\n  if constexpr (Spherical) {\n    // spherical symmetry\n    const size_t num_grid_pts = 8;\n    const double x_end = 4.0;\n\n    mesh = Mesh<3>{{{num_grid_pts, 1, 1}},\n                   {{Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n                     Spectral::Basis::Cartoon}},\n                   {{Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::SphericalSymmetry,\n                     Spectral::Quadrature::SphericalSymmetry}}};\n\n    const Affine affine_x_map(-1.0, 1.0, x_start, x_end);\n\n    using Cartoon_map_combination =\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Identity1D>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                Cartoon_map_combination>\n        map{{affine_x_map, identity_cartoon_map, identity_cartoon_map}};\n    inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    coords = map(logical_coordinates(mesh));\n  } else {\n    // axial symmetry\n    const size_t num_x_grid_pts = 6;\n    const double x_end = 3.25;\n    const size_t num_y_grid_pts = 7;\n    const double y_start = -2.5;\n    const double y_end = 4.0;\n\n    mesh = Mesh<3>{{{num_x_grid_pts, num_y_grid_pts, 1}},\n                   {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n                     Spectral::Basis::Cartoon}},\n                   {{Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::AxialSymmetry}}};\n\n    const Affine affine_x_map(-1.0, 1.0, x_start, x_end);\n    const Affine affine_y_map(-1.0, 1.0, y_start, y_end);\n\n    using Cartoon_map_combination =\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity1D>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                Cartoon_map_combination>\n        map{{affine_x_map, affine_y_map, identity_cartoon_map}};\n    inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    coords = map(logical_coordinates(mesh));\n  }\n\n  using TempIjk =\n      ::Tags::TempTensor<0, tnsr::Ijk<DataVector, 3, Frame::Inertial>>;\n\n  using VarTags = tmpl::list<::Tags::TempI<0, 3, Frame::Inertial>,\n                             ::Tags::TempIj<0, 3, Frame::Inertial>, TempIjk>;\n\n  Variables<VarTags> vars{mesh.number_of_grid_points()};\n\n  using div_VarTags =\n      tmpl::transform<VarTags, tmpl::bind<::Tags::div, tmpl::_1>>;\n\n  Variables<div_VarTags> expected_div_vars{mesh.number_of_grid_points()};\n\n  // Here we create \"prefactors\", which serve to fill out our tensors by being\n  // mulitplied by our cartoon_func(), which themselves make our tensors have\n  // some nontrivial spatial derivative\n  // The point of these prefactors is to ensure the tensors respect the\n  // symmetry of the spacetime: it is not sufficient that each component\n  // follows the symmetry, rather the entire tensor must satisfy\n  // \\mathcal{L}_\\xi (tensor) = 0. Each rank has it's own form of prefactor\n  Variables<VarTags> prefactor_vars{mesh.number_of_grid_points()};\n\n  using TempiJkl =\n      ::Tags::TempTensor<0, TensorMetafunctions::prepend_spatial_index<\n                                tnsr::Ijk<DataVector, 3, Frame::Inertial>, 3,\n                                UpLo::Lo, Frame::Inertial>>;\n\n  // prepending a spatial index to VarTags (= PrefactorVarTags if it existed)\n  using d_PrefactorVarTags =\n      tmpl::list<::Tags::TempiJ<0, 3, Frame::Inertial>,\n                 ::Tags::TempiJk<0, 3, Frame::Inertial>, TempiJkl>;\n  Variables<d_PrefactorVarTags> d_prefactor_vars{mesh.number_of_grid_points()};\n\n  const size_t dv_size = get<0>(coords).size();\n\n  auto& vector = get<::Tags::TempI<0, 3>>(prefactor_vars);\n  auto& d_vector = get<::Tags::TempiJ<0, 3>>(d_prefactor_vars);\n  if constexpr (Spherical) {\n    // spherical case, vector/one form, using x^i\n    for (size_t i = 0; i < index_dim<0>(vector); ++i) {\n      vector.get(i) = coords.get(i);\n    }\n    // partial_i of x_j is \\delta_ij\n    for (size_t i = 0; i < index_dim<0>(d_vector); ++i) {\n      for (size_t j = 0; j < index_dim<1>(d_vector); ++j) {\n        if (i == j) {\n          d_vector.get(i, j) = DataVector(dv_size, 1.0);\n        } else {\n          d_vector.get(i, j) = DataVector(dv_size, 0.0);\n        }\n      }\n    }\n  } else {\n    // axial case, vector/one form, using x^i = (-z, 0, x) (pure rotation)\n    get<0>(vector) = -1.0 * coords.get(2);\n    get<1>(vector) = DataVector(dv_size, 0.0);\n    get<2>(vector) = get<0>(coords);\n\n    // \\partial_i of x_j is (\\delta_i2, 0, \\delta_i0)\n    for (size_t i = 0; i < index_dim<0>(d_vector); ++i) {\n      for (size_t j = 0; j < index_dim<1>(d_vector); ++j) {\n        if (i == 2 and j == 0) {\n          d_vector.get(i, j) = DataVector(dv_size, -1.0);\n        } else if (i == 0 and j == 2) {\n          d_vector.get(i, j) = DataVector(dv_size, 1.0);\n        } else {\n          d_vector.get(i, j) = DataVector(dv_size, 0.0);\n        }\n      }\n    }\n  }\n\n  auto& rank2 = get<::Tags::TempIj<0, 3>>(prefactor_vars);\n  auto& d_rank2 = get<::Tags::TempiJk<0, 3>>(d_prefactor_vars);\n  // Filling with (essenially) projector to tangent space of sphere\n  // P_ij = \\delta_ij + x_i x_j\n  // can have arbitrary function in from of each term, not doing here\n  // (the real projector is \\delta_ij - x_i x_j / r^2)\n  for (size_t i = 0; i < index_dim<0>(rank2); ++i) {\n    for (size_t j = 0; j < index_dim<1>(rank2); ++j) {\n      if (i == j) {\n        rank2.get(i, j) =\n            DataVector(dv_size, 1.0) + coords.get(i) * coords.get(j);\n      } else {\n        rank2.get(i, j) = coords.get(i) * coords.get(j);\n      }\n    }\n  }\n  // \\partial_i of (\\delta_jk + x_j x_k) is (x_j \\delta_ik + x_k \\delta_ij)\n  for (size_t i = 0; i < index_dim<0>(d_rank2); ++i) {\n    for (size_t j = 0; j < index_dim<1>(d_rank2); ++j) {\n      for (size_t k = 0; k < index_dim<2>(d_rank2); ++k) {\n        d_rank2.get(i, j, k) = DataVector(dv_size, 0.0);\n        if (i == k) {\n          d_rank2.get(i, j, k) += coords.get(j);\n        }\n        if (i == j) {\n          d_rank2.get(i, j, k) += coords.get(k);\n        }\n      }\n    }\n  }\n\n  auto& rank3 = get<TempIjk>(prefactor_vars);\n  auto& d_rank3 = get<TempiJkl>(d_prefactor_vars);\n  // x_i x_j x_k + \\delta_ij x_k \\delta_ik x_j \\delta_jk x_i\n  for (size_t i = 0; i < index_dim<0>(rank3); ++i) {\n    for (size_t j = 0; j < index_dim<1>(rank3); ++j) {\n      for (size_t k = 0; k < index_dim<2>(rank3); ++k) {\n        rank3.get(i, j, k) = coords.get(i) * coords.get(j) * coords.get(k);\n        if (i == j) {\n          rank3.get(i, j, k) += coords.get(k);\n        }\n        if (i == k) {\n          rank3.get(i, j, k) += coords.get(j);\n        }\n        if (j == k) {\n          rank3.get(i, j, k) += coords.get(i);\n        }\n      }\n    }\n  }\n  // \\partial_i of (x_j x_k x_l + \\delta_jk x_l \\delta_jl x_k \\delta_kl x_j) is\n  // \\delta_ij x_k x_l + \\delta_ik x_j x_l + \\delta_il x_j x_k +\n  //   \\delta_jk \\delta_il + \\delta_jl \\delta_ik + \\delta_kl \\delta_ij\n  for (size_t i = 0; i < index_dim<0>(d_rank3); ++i) {\n    for (size_t j = 0; j < index_dim<1>(d_rank3); ++j) {\n      for (size_t k = 0; k < index_dim<2>(d_rank3); ++k) {\n        for (size_t l = 0; l < index_dim<3>(d_rank3); ++l) {\n          d_rank3.get(i, j, k, l) = DataVector(dv_size, 0.0);\n          if (i == j) {\n            d_rank3.get(i, j, k, l) += coords.get(k) * coords.get(l);\n          }\n          if (i == k) {\n            d_rank3.get(i, j, k, l) += coords.get(j) * coords.get(l);\n          }\n          if (i == l) {\n            d_rank3.get(i, j, k, l) += coords.get(j) * coords.get(k);\n          }\n          if (j == k and i == l) {\n            d_rank3.get(i, j, k, l) += 1.0;\n          }\n          if (j == l and i == k) {\n            d_rank3.get(i, j, k, l) += 1.0;\n          }\n          if (k == l and i == j) {\n            d_rank3.get(i, j, k, l) += 1.0;\n          }\n        }\n      }\n    }\n  }\n\n  tmpl::for_each<VarTags>([&vars, &prefactor_vars, &expected_div_vars,\n                           &d_prefactor_vars, &coords]<typename tensor_tag>(\n                              tmpl::type_<tensor_tag> /*meta*/) {\n    auto& tensor = get<tensor_tag>(vars);\n    auto& prefactor_tensor = get<tensor_tag>(prefactor_vars);\n    using div_tensor_tag = ::Tags::div<tensor_tag>;\n    auto& div_tensor = get<div_tensor_tag>(expected_div_vars);\n    auto& d_prefactor_tensor =\n        get<tmpl::at<d_PrefactorVarTags, tmpl::index_of<VarTags, tensor_tag>>>(\n            d_prefactor_vars);\n\n    for (size_t storage_index = 0; storage_index < tensor.size();\n         ++storage_index) {\n      tensor[storage_index] =\n          prefactor_tensor[storage_index] * cartoon_func<Spherical>(coords);\n\n      const auto input_index = tensor.get_tensor_index(storage_index);\n      std::array<size_t, tensor_tag::type::rank() - 1> output_index{};\n      std::copy(input_index.begin() + 1, input_index.end(),\n                output_index.begin());\n\n      const size_t deriv_index = input_index[0];\n      if (deriv_index == 0) {\n        div_tensor.get(output_index) = 0.0 * tensor.get(input_index);\n      }\n      const auto d_input_index = prepend(input_index, deriv_index);\n\n      div_tensor.get(output_index) +=\n          prefactor_tensor.get(input_index) *\n              cartoon_dfunc<Spherical>(deriv_index, coords) +\n          cartoon_func<Spherical>(coords) *\n              d_prefactor_tensor.get(d_input_index);\n    }\n  });\n\n  Variables<div_VarTags> div_vars{mesh.number_of_grid_points()};\n  cartoon_divergence(make_not_null(&div_vars), vars, mesh, inv_jacobian,\n                     coords);\n\n  const Approx local_approx = Approx::custom().epsilon(1.0e-10).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(div_vars, expected_div_vars, local_approx);\n}\n\nvoid test_cartoon_chooser() {\n  // Here, \"chooser\" means the divergence function that take inertial_coords and\n  // then pick the appropriate cartoon/non-cartoon implementation\n  // Testing divergence (with Variables) chooser, i.e. verifying it calls the\n  // correct underlying implemenation (unfortunately a lot of set up code)\n  // With cartoon basis\n  const Identity1D identity_cartoon_map;\n  const Mesh<3> mesh_cartoon{\n      {{8, 1, 1}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n        Spectral::Basis::Cartoon}},\n      {{Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::SphericalSymmetry,\n        Spectral::Quadrature::SphericalSymmetry}}};\n\n  const Affine affine_x_map(-1.0, 1.0, 1.0, 4.0);\n  using Cartoon_map_combination =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Identity1D>;\n  const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                              Cartoon_map_combination>\n      map_cartoon{{affine_x_map, identity_cartoon_map, identity_cartoon_map}};\n  const auto inv_jacobian_cartoon =\n      map_cartoon.inv_jacobian(logical_coordinates(mesh_cartoon));\n  const auto inertial_coords_cartoon =\n      map_cartoon(logical_coordinates(mesh_cartoon));\n\n  using f_type = tnsr::I<DataVector, 3, Frame::Inertial>;\n  using div_f_type = Scalar<DataVector>;\n  f_type tnsr_cartoon{};\n  get<0>(tnsr_cartoon) = get<0>(inertial_coords_cartoon) *\n                         cartoon_func<true>(inertial_coords_cartoon);\n  get<1>(tnsr_cartoon) = get<2>(tnsr_cartoon) =\n      DataVector(mesh_cartoon.number_of_grid_points(), 0.0);\n  div_f_type div_tnsr_cartoon{};\n  div_f_type expected_div_tnsr_cartoon{};\n  get<>(expected_div_tnsr_cartoon) =\n      3.0 * cartoon_func<true>(inertial_coords_cartoon) +\n      get<0>(inertial_coords_cartoon) *\n          cartoon_dfunc<true>(0, inertial_coords_cartoon);\n\n  using CartoonTags = ::Tags::convert_to_temp_tensors<tmpl::list<f_type>, 0>;\n  using div_CartoonTags =\n      ::Tags::convert_to_temp_tensors<tmpl::list<div_f_type>, 0>;\n  Variables<CartoonTags> f_cartoon{mesh_cartoon.number_of_grid_points()};\n  get<tmpl::front<CartoonTags>>(f_cartoon) = tnsr_cartoon;\n  Variables<div_CartoonTags> div_f_cartoon{\n      mesh_cartoon.number_of_grid_points()};\n  Variables<div_CartoonTags> expected_div_f_cartoon{\n      mesh_cartoon.number_of_grid_points()};\n  get<tmpl::front<div_CartoonTags>>(expected_div_f_cartoon) =\n      expected_div_tnsr_cartoon;\n  cartoon_divergence(make_not_null(&div_f_cartoon), f_cartoon, mesh_cartoon,\n                     inv_jacobian_cartoon, inertial_coords_cartoon);\n  const Approx local_approx = Approx::custom().epsilon(1e-11).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(div_f_cartoon, expected_div_f_cartoon,\n                                local_approx);\n\n  // With non-cartoon\n  const size_t n0 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2;\n  const size_t n1 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2 + 1;\n  const size_t n2 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2 - 1;\n\n  const Mesh<3> mesh_lgl{{{n0, n1, n2}},\n                         Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto};\n  const size_t num_grid_points_lgl = mesh_lgl.number_of_grid_points();\n  const auto coordinate_map_lgl = make_affine_map<3>();\n  const auto xi_lgl = logical_coordinates(mesh_lgl);\n  const auto inertial_coords_lgl = coordinate_map_lgl(xi_lgl);\n  const auto inv_jacobian_lgl = coordinate_map_lgl.inv_jacobian(xi_lgl);\n\n  using flux_tags_lgl = two_fluxes<DataVector, 3, Frame::Inertial>;\n  Variables<flux_tags_lgl> f_lgl(num_grid_points_lgl);\n  Variables<db::wrap_tags_in<Tags::div, flux_tags_lgl>> expected_div_f_lgl(\n      num_grid_points_lgl);\n  for (size_t a = 0; a < 5; ++a) {\n    for (size_t b = 0; b < 4; ++b) {\n      for (size_t c = 0; c < 3; ++c) {\n        std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 3>\n            functions{\n                {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a),\n                 std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(b),\n                 std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(c)}};\n\n        MathFunctions::TensorProduct<3> func_lgl(1.0, std::move(functions));\n        tmpl::for_each<flux_tags_lgl>([&inertial_coords_lgl, &func_lgl, &f_lgl,\n                                       &expected_div_f_lgl](auto tag) {\n          using FluxTag = tmpl::type_from<decltype(tag)>;\n          get<FluxTag>(f_lgl) = FluxTag::flux(func_lgl, inertial_coords_lgl);\n          using DivFluxTag = Tags::div<FluxTag>;\n          get<DivFluxTag>(expected_div_f_lgl) =\n              FluxTag::divergence_of_flux(func_lgl, inertial_coords_lgl);\n        });\n        Variables<db::wrap_tags_in<Tags::div, flux_tags_lgl>> div_f_lgl{\n            num_grid_points_lgl};\n        divergence(make_not_null(&div_f_lgl), f_lgl, mesh_lgl, inv_jacobian_lgl,\n                   inertial_coords_lgl);\n        CHECK_VARIABLES_CUSTOM_APPROX(div_f_lgl, expected_div_f_lgl,\n                                      local_approx);\n      }\n    }\n  }\n  // Testing divergence (without Variables) through associated choosers\n  // With Cartoon basis\n  divergence(make_not_null(&div_tnsr_cartoon), tnsr_cartoon, mesh_cartoon,\n             inv_jacobian_cartoon, inertial_coords_cartoon);\n  CHECK_ITERABLE_CUSTOM_APPROX(div_tnsr_cartoon, expected_div_tnsr_cartoon,\n                               local_approx);\n\n  div_tnsr_cartoon = divergence(tnsr_cartoon, mesh_cartoon,\n                                inv_jacobian_cartoon, inertial_coords_cartoon);\n  CHECK_ITERABLE_CUSTOM_APPROX(div_tnsr_cartoon, expected_div_tnsr_cartoon,\n                               local_approx);\n\n  // With non-cartoon basis\n  auto& tnsr_lgl = get<tmpl::front<flux_tags_lgl>>(f_lgl);\n  using div_tnsr_lgl_tag =\n      tmpl::front<db::wrap_tags_in<Tags::div, flux_tags_lgl>>;\n  div_tnsr_lgl_tag::type div_tnsr_lgl{};\n  auto& expected_div_tnsr_lgl = get<div_tnsr_lgl_tag>(expected_div_f_lgl);\n  divergence(make_not_null(&div_tnsr_lgl), tnsr_lgl, mesh_lgl, inv_jacobian_lgl,\n             inertial_coords_lgl);\n  CHECK_ITERABLE_CUSTOM_APPROX(div_tnsr_lgl, expected_div_tnsr_lgl,\n                               local_approx);\n\n  div_tnsr_lgl =\n      divergence(tnsr_lgl, mesh_lgl, inv_jacobian_lgl, inertial_coords_lgl);\n  CHECK_ITERABLE_CUSTOM_APPROX(div_tnsr_lgl, expected_div_tnsr_lgl,\n                               local_approx);\n}\n\ntemplate <size_t CompDim, size_t Dim, typename T, Requires<Dim == 3> = nullptr>\nvoid test_cartoon_divergence_compute_item_impl(const T& coordinate_map) {\n  INFO(\"Cartoon Divergence Compute Item\");\n  CAPTURE(CompDim);\n  const bool Spherical = CompDim == 1;\n  using map_tag = MapTag<std::decay_t<decltype(coordinate_map)>>;\n  using mesh_tag = domain::Tags::Mesh<Dim>;\n  using inv_jac_tag = domain::Tags::InverseJacobianCompute<\n      map_tag, typename domain::Tags::LogicalCoordinates<Dim>::base>;\n  using coords_tag = domain::Tags::Coordinates<3, typename Frame::Inertial>;\n  using Frame = Frame::Inertial;\n  using flux_tags = tmpl::list<Flux1<DataVector, Dim, Frame>>;\n  using flux_tag = Tags::Variables<flux_tags>;\n  using div_tags = db::wrap_tags_in<Tags::div, flux_tags>;\n  TestHelpers::db::test_compute_tag<Tags::DivVectorCompute<\n      Flux1<DataVector, Dim, Frame>, mesh_tag, typename inv_jac_tag::base>>(\n      \"div(Flux1)\");\n\n  const std::array<size_t, 3> extents_array{{6, CompDim == 2 ? 5 : 1, 1}};\n  const auto pad_at_end = []<typename ElemType>(const ElemType default_val,\n                                                const ElemType padding_val) {\n    std::array<ElemType, Dim> arr{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(arr, i) = i < CompDim ? default_val : padding_val;\n    }\n    return arr;\n  };\n  const Mesh<Dim> mesh{\n      extents_array,\n      pad_at_end(Spectral::Basis::Legendre, Spectral::Basis::Cartoon),\n      pad_at_end(Spectral::Quadrature::GaussLobatto,\n                 CompDim == 1 ? Spectral::Quadrature::SphericalSymmetry\n                              : Spectral::Quadrature::AxialSymmetry)};\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  const auto xi = logical_coordinates(mesh);\n  const auto inertial_coords = coordinate_map(xi);\n  const auto inv_jacobian = coordinate_map.inv_jacobian(xi);\n  Variables<flux_tags> fluxes(num_grid_points);\n  Variables<div_tags> expected_div_fluxes(num_grid_points);\n\n  auto& flux = get<tmpl::front<flux_tags>>(fluxes);\n  auto& expected_div_flux =\n      get<Tags::div<tmpl::front<flux_tags>>>(expected_div_fluxes);\n  for (size_t i = 0; i < Dim; ++i) {\n    flux.get(i) = inertial_coords.get(i) *\n                  cartoon_func<Spherical>(inertial_coords);\n  }\n  get<>(expected_div_flux) =\n      3.0 * cartoon_func<Spherical>(inertial_coords) +\n      get<0>(inertial_coords) *\n          cartoon_dfunc<Spherical>(0, inertial_coords) +\n      get<1>(inertial_coords) *\n          cartoon_dfunc<Spherical>(1, inertial_coords);\n\n\n  auto box = db::create<\n      db::AddSimpleTags<mesh_tag, flux_tag, map_tag, coords_tag>,\n      db::AddComputeTags<\n          domain::Tags::LogicalCoordinates<Dim>, inv_jac_tag,\n          Tags::DivVectorCompute<Flux1<DataVector, Dim, Frame>, mesh_tag,\n                                 typename inv_jac_tag::base, coords_tag>>>(\n      mesh, fluxes, coordinate_map, inertial_coords);\n\n  const auto& div_flux1 =\n      db::get<Tags::div<Flux1<DataVector, Dim, Frame>>>(box);\n  const auto& expected =\n      get<Tags::div<Flux1<DataVector, Dim, Frame>>>(expected_div_fluxes);\n  const Approx local_approx = Approx::custom().epsilon(1.e-11).scale(1.);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected, div_flux1, local_approx);\n}\n\nvoid test_cartoon_divergence_compute() {\n  const auto map1 = domain::make_coordinate_map<Frame::ElementLogical,\n                                                Frame::Inertial>(\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Identity1D>{\n          Affine{-1.0, 1.0, 0.0, 1.7}, Identity1D{}, Identity1D{}});\n  const auto map2 =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity1D>{\n              Affine{-1.0, 1.0, 0.0, 1.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n              Identity1D{}});\n  test_cartoon_divergence_compute_item_impl<1, 3>(map1);\n  test_cartoon_divergence_compute_item_impl<2, 3>(map2);\n}\n}  // namespace\n\n// [[Timeout, 20]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.Divergence\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  test_divergence<DataVector>();\n  test_divergence<ComplexDataVector>();\n  test_divergence_compute();\n\n  test_cartoon<true>(0.0);\n  test_cartoon<true>(1.0);\n  test_cartoon<false>(0.0);\n  test_cartoon<false>(1.0);\n\n  test_cartoon_chooser();\n  test_cartoon_divergence_compute();\n\n  BENCHMARK_ADVANCED(\"Divergence of vector\")\n  (Catch::Benchmark::Chronometer meter) {\n    const Mesh<3> mesh{4, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const Affine map1d(-1.0, 1.0, -1.0, 1.0);\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                Affine3D>\n        map(Affine3D{map1d, map1d, map1d});\n    const auto inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    tnsr::I<DataVector, 3> input{mesh.number_of_grid_points(), 0.};\n    Scalar<DataVector> div{mesh.number_of_grid_points()};\n    meter.measure([&div, &input, &mesh, &inv_jacobian]() {\n      divergence(make_not_null(&div), input, mesh, inv_jacobian);\n    });\n  };\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_Filtering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <string>\n#include <unordered_set>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Tags/Filter.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/ExponentialFilter.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Filtering.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/FilterAction.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Blocks 0-2 do filtering (if enabled). Block 3 doesn't\nconstexpr size_t num_blocks = 4;\n\nnamespace Tags {\nstruct ScalarVar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct VectorVar : db::SimpleTag {\n  using type = tnsr::I<DataVector, Dim>;\n};\n}  // namespace Tags\n\ntemplate <size_t Dim>\nstruct System {\n  static constexpr size_t volume_dim = Dim;\n  using variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::ScalarVar, Tags::VectorVar<Dim>>>;\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t dim = metavariables::system::volume_dim;\n\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<dim>>;\n  using simple_tags =\n      db::AddSimpleTags<domain::Tags::Mesh<dim>, domain::Tags::Element<dim>,\n                        typename metavariables::system::variables_tag>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>,\n      // [action_list_example]\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::conditional_t<\n              metavariables::filter_individually,\n              tmpl::list<dg::Actions::Filter<Filters::Exponential<0>,\n                                             tmpl::list<Tags::ScalarVar>>,\n                         dg::Actions::Filter<Filters::Exponential<1>,\n                                             tmpl::list<Tags::VectorVar<dim>>>>,\n              tmpl::list<dg::Actions::Filter<\n                  Filters::Exponential<0>,\n                  tmpl::list<Tags::VectorVar<dim>, Tags::ScalarVar>>>>>>;\n  // [action_list_example]\n};\n\ntemplate <size_t Dim, bool FilterIndividually>\nstruct Metavariables {\n  static constexpr bool filter_individually = FilterIndividually;\n  static constexpr size_t dim = Dim;\n\n  using system = System<Dim>;\n  static constexpr bool local_time_stepping = true;\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nstd::vector<std::string> domain_block_names() {\n  std::vector<std::string> block_names{num_blocks};\n  for (size_t i = 0; i < num_blocks; i++) {\n    block_names[i] = \"Block\" + get_output(i);\n  }\n\n  return block_names;\n}\n\nstd::unordered_map<std::string, std::unordered_set<std::string>>\ndomain_block_groups() {\n  std::unordered_map<std::string, std::unordered_set<std::string>> groups{};\n  groups[\"Group1\"] = std::unordered_set<std::string>{{\"Block1\"s}};\n  groups[\"Group2\"] = std::unordered_set<std::string>{{\"Block1\"s}, {\"Block2\"s}};\n\n  return groups;\n}\n\ntemplate <size_t Dim>\nDomain<Dim> make_domain() {\n  using Identity = domain::CoordinateMaps::Identity<Dim>;\n  using Map =\n      domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial, Identity>;\n  register_classes_with_charm(tmpl::list<Map>{});\n  std::vector<std::unique_ptr<\n      domain::CoordinateMapBase<Frame::BlockLogical, Frame::Inertial, Dim>>>\n      maps{num_blocks};\n  for (size_t i = 0; i < num_blocks; i++) {\n    maps[i] = std::make_unique<Map>(Identity{});\n  }\n\n  return Domain<Dim>{\n      std::move(maps), {}, domain_block_names(), domain_block_groups()};\n}\n\nstd::optional<std::vector<std::string>> get_block_names() {\n  std::optional<std::vector<std::string>> names{\n      {{\"Block0\"}, {\"Group1\"}, {\"Group2\"}}};\n  return names;\n}\n\ntemplate <typename Metavariables,\n          Requires<Metavariables::filter_individually> = nullptr>\ntypename ActionTesting::MockRuntimeSystem<Metavariables>::CacheTuple\ncreate_cache_tuple(const double alpha, const unsigned half_power,\n                   const bool enable) {\n  return {make_domain<Metavariables::dim>(),\n          Filters::Exponential<0>{alpha, half_power, enable, get_block_names()},\n          Filters::Exponential<1>{2.0 * alpha, 2 * half_power, enable,\n                                  get_block_names()}};\n}\n\ntemplate <typename Metavariables,\n          Requires<not Metavariables::filter_individually> = nullptr>\ntypename ActionTesting::MockRuntimeSystem<Metavariables>::CacheTuple\ncreate_cache_tuple(const double alpha, const unsigned half_power,\n                   const bool enable) {\n  return {\n      make_domain<Metavariables::dim>(),\n      Filters::Exponential<0>{alpha, half_power, enable, get_block_names()}};\n}\n\ntemplate <size_t Dim, Spectral::Basis BasisType,\n          Spectral::Quadrature QuadratureType, bool FilterIndividually>\nvoid test_exponential_filter_action(const double alpha,\n                                    const unsigned half_power,\n                                    const bool enable) {\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n  CAPTURE(enable);\n\n  // Need to increase approx slightly on some hardware\n  Approx custom_approx = Approx::custom().epsilon(5.0e-13);\n\n  using metavariables = Metavariables<Dim, FilterIndividually>;\n  using component = Component<metavariables>;\n\n  // Division by Dim to reduce time of test\n  const size_t max_pts =\n      BasisType == Spectral::Basis::Fourier\n          ? Spectral::maximum_number_of_points<BasisType> / (3 * Dim)\n          : Spectral::maximum_number_of_points<BasisType> / Dim;\n  for (size_t num_pts =\n           Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       num_pts < max_pts; ++num_pts) {\n    CAPTURE(num_pts);\n    const Mesh<Dim> mesh(num_pts, BasisType, QuadratureType);\n\n    Variables<tmpl::list<Tags::ScalarVar, Tags::VectorVar<Dim>>> initial_vars(\n        mesh.number_of_grid_points());\n    for (size_t i = 0; i < mesh.number_of_grid_points(); ++i) {\n      get(get<Tags::ScalarVar>(initial_vars))[i] = pow(i, num_pts) * 0.5;\n      for (size_t d = 0; d < Dim; ++d) {\n        get<Tags::VectorVar<Dim>>(initial_vars).get(d)[i] =\n            d + pow(i, num_pts) * 0.75;\n      }\n    }\n\n    ActionTesting::MockRuntimeSystem<metavariables> runner(\n        create_cache_tuple<metavariables>(alpha, half_power, enable));\n    for (size_t block = 0; block < num_blocks; block++) {\n      ActionTesting::emplace_component_and_initialize<component>(\n          &runner, block,\n          {mesh, Element{ElementId<Dim>{block}, {}}, initial_vars});\n    }\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n    for (size_t block = 0; block < num_blocks; block++) {\n      CAPTURE(block);\n      ActionTesting::next_action<component>(make_not_null(&runner), block);\n      if (FilterIndividually) {\n        ActionTesting::next_action<component>(make_not_null(&runner), block);\n      }\n\n      std::array<Matrix, Dim> filter_scalar{};\n      std::array<Matrix, Dim> filter_vector{};\n      for (size_t d = 0; d < Dim; d++) {\n        if (enable and block < num_blocks - 1) {\n          gsl::at(filter_scalar, d) = Spectral::filtering::exponential_filter(\n              mesh.slice_through(d), alpha, half_power);\n          if (FilterIndividually) {\n            gsl::at(filter_vector, d) = Spectral::filtering::exponential_filter(\n                mesh.slice_through(d), 2.0 * alpha, 2 * half_power);\n          } else {\n            gsl::at(filter_vector, d) = gsl::at(filter_scalar, d);\n          }\n        } else {\n          gsl::at(filter_scalar, d) = Matrix{};\n          gsl::at(filter_vector, d) = Matrix{};\n        }\n      }\n\n      Scalar<DataVector> expected_scalar(mesh.number_of_grid_points(), 0.0);\n      tnsr::I<DataVector, Dim> expected_vector(mesh.number_of_grid_points(),\n                                               0.0);\n      apply_matrices(make_not_null(&get(expected_scalar)), filter_scalar,\n                     get(get<Tags::ScalarVar>(initial_vars)), mesh.extents());\n      for (size_t d = 0; d < Dim; d++) {\n        apply_matrices(make_not_null(&expected_vector.get(d)), filter_vector,\n                       get<Tags::VectorVar<Dim>>(initial_vars).get(d),\n                       mesh.extents());\n      }\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          expected_scalar,\n          (ActionTesting::get_databox_tag<component, Tags::ScalarVar>(runner,\n                                                                      block)),\n          custom_approx);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          expected_vector,\n          (ActionTesting::get_databox_tag<component, Tags::VectorVar<Dim>>(\n              runner, block)),\n          custom_approx);\n    }\n  }\n}\n\ntemplate <size_t Dim, bool FilterIndividually>\nvoid invoke_test_exponential_filter_action(const double alpha,\n                                           const unsigned half_power,\n                                           const bool enable) {\n  test_exponential_filter_action<Dim, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto,\n                                 FilterIndividually>(alpha, half_power, enable);\n  test_exponential_filter_action<Dim, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::Gauss,\n                                 FilterIndividually>(alpha, half_power, enable);\n  test_exponential_filter_action<Dim, Spectral::Basis::Chebyshev,\n                                 Spectral::Quadrature::GaussLobatto,\n                                 FilterIndividually>(alpha, half_power, enable);\n  test_exponential_filter_action<Dim, Spectral::Basis::Chebyshev,\n                                 Spectral::Quadrature::Gauss,\n                                 FilterIndividually>(alpha, half_power, enable);\n  test_exponential_filter_action<Dim, Spectral::Basis::Fourier,\n                                 Spectral::Quadrature::Equiangular,\n                                 FilterIndividually>(alpha, half_power, enable);\n}\n\ntemplate <size_t Dim>\nclass TestCreator : public DomainCreator<Dim> {\n public:\n  TestCreator(const bool use_block_names = true)\n      : use_block_names_(use_block_names) {}\n\n  Domain<Dim> create_domain() const override { return make_domain<Dim>(); }\n  std::vector<std::string> block_names() const override {\n    return use_block_names_ ? domain_block_names() : std::vector<std::string>{};\n  }\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n  block_groups() const override {\n    return use_block_names_\n               ? domain_block_groups()\n               : std::unordered_map<std::string,\n                                    std::unordered_set<std::string>>{};\n  }\n  std::vector<DirectionMap<\n      Dim, std::unique_ptr<domain::BoundaryConditions::BoundaryCondition>>>\n  external_boundary_conditions() const override {\n    ERROR(\"\");\n  }\n  std::vector<std::array<size_t, Dim>> initial_extents() const override {\n    ERROR(\"\");\n  }\n  std::vector<std::array<size_t, Dim>> initial_refinement_levels()\n      const override {\n    ERROR(\"\");\n  }\n  auto functions_of_time(const std::unordered_map<std::string, double>&\n                         /*initial_expiration_times*/\n                         = {}) const\n      -> std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>> override {\n    ERROR(\"\");\n  }\n\n private:\n  bool use_block_names_;\n};\n\ntemplate <size_t Dim>\nstruct Metavars {\n  static constexpr size_t volume_dim = Dim;\n  struct factory_creation {\n    using factory_classes = tmpl::map<\n        tmpl::pair<::DomainCreator<Dim>, tmpl::list<TestCreator<Dim>>>>;\n  };\n};\n\ntemplate <size_t Dim>\nvoid test_exponential_filter_creation() {\n  using Filter = Filters::Exponential<0>;\n  using AnotherFilter = Filters::Exponential<1>;\n\n  using tags =\n      tmpl::list<OptionTags::Filter<Filter>, OptionTags::Filter<AnotherFilter>,\n                 domain::OptionTags::DomainCreator<Dim>>;\n  Options::Parser<tags> options(\"\");\n  options.parse(\n      \"DomainCreator:\\n\"\n      \"  TestCreator\\n\"\n      // [multiple_exponential_filters]\n      \"Filtering:\\n\"\n      \"  ExpFilter0:\\n\"\n      \"    Alpha: 36\\n\"\n      \"    HalfPower: 32\\n\"\n      \"    Enable: True\\n\"\n      \"    BlocksToFilter: All\\n\"\n      \"  ExpFilter1:\\n\"\n      \"    Alpha: 36\\n\"\n      \"    HalfPower: 12\\n\"\n      \"    Enable: True\\n\"\n      \"    BlocksToFilter:\\n\"\n      \"      - Block0\\n\"\n      \"      - BlockGroup1\\n\"\n      // [multiple_exponential_filters]\n  );\n  const auto filter =\n      options.template get<OptionTags::Filter<Filter>, Metavars<Dim>>();\n\n  CHECK(filter == Filter{36.0, 32, true, {}});\n  CHECK_FALSE(filter == Filter{35.0, 32, true, {}});\n  CHECK_FALSE(filter == Filter{36.0, 33, true, {}});\n  CHECK_FALSE(filter == Filter{36.0, 32, false, {}});\n  CHECK_FALSE(filter == Filter{36.0, 32, true, {{\"Block0\"}}});\n\n  CHECK_FALSE(filter != Filter{36.0, 32, true, {}});\n  CHECK(filter != Filter{35.0, 32, true, {}});\n  CHECK(filter != Filter{36.0, 33, true, {}});\n  CHECK(filter != Filter{36.0, 32, false, {}});\n  CHECK(filter != Filter{36.0, 32, true, {{\"Block0\"}}});\n\n  const auto another_filter =\n      options.template get<OptionTags::Filter<AnotherFilter>, Metavars<Dim>>();\n\n  CHECK(another_filter ==\n        AnotherFilter{36.0, 12, true, {{\"Block0\", \"BlockGroup1\"}}});\n  CHECK_FALSE(another_filter !=\n              AnotherFilter{36.0, 12, true, {{\"Block0\", \"BlockGroup1\"}}});\n\n  CHECK_FALSE(another_filter == AnotherFilter{36.0, 12, true, {}});\n  CHECK(another_filter != AnotherFilter{36.0, 12, true, {}});\n\n  {\n    Options::Parser<tmpl::pop_front<tags>> error_options(\"\");\n    error_options.parse(\n        \"DomainCreator:\\n\"\n        \"  TestCreator\\n\"\n        \"Filtering:\\n\"\n        \"  ExpFilter1:\\n\"\n        \"    Alpha: 36\\n\"\n        \"    HalfPower: 12\\n\"\n        \"    Enable: True\\n\"\n        \"    BlocksToFilter:\\n\"\n        \"      - Block0\\n\"\n        \"      - Block0\\n\");\n\n    CHECK_THROWS_WITH(\n        (error_options\n             .template get<OptionTags::Filter<AnotherFilter>, Metavars<Dim>>()),\n        Catch::Matchers::ContainsSubstring(\"Duplicate block name\"));\n\n    CHECK_THROWS_WITH(\n        (Filters::Tags::Filter<AnotherFilter>::create_from_options<\n            Metavars<Dim>>(another_filter,\n                           std::make_unique<TestCreator<Dim>>())),\n        Catch::Matchers::ContainsSubstring(\n            \"is not a block name or a block group. Existing blocks are\"));\n\n    // These two are to check that we can pass just a block name or just a block\n    // group and the tag will create things correctly\n    CHECK_NOTHROW(\n        (Filters::Tags::Filter<Filter>::create_from_options<Metavars<Dim>>(\n            Filter{26.0, 23, true, {{\"Block0\"}}},\n            std::make_unique<TestCreator<Dim>>())));\n    CHECK_NOTHROW(\n        (Filters::Tags::Filter<Filter>::create_from_options<Metavars<Dim>>(\n            Filter{26.0, 23, true, {{\"Group1\"}}},\n            std::make_unique<TestCreator<Dim>>())));\n\n    CHECK_THROWS_WITH(\n        (Filters::Tags::Filter<AnotherFilter>::create_from_options<\n            Metavars<Dim>>(another_filter,\n                           std::make_unique<TestCreator<Dim>>(false))),\n        Catch::Matchers::ContainsSubstring(\n            \"The domain chosen doesn't use block names\"));\n  }\n}\n\nvoid test_cartoon_exponential_filter() {\n  const auto filter = Filters::Exponential<8>{0, 0, true, {}};\n  CHECK(filter.filter_matrix(\n            {1, Spectral::Basis::Cartoon,\n             Spectral::Quadrature::AxialSymmetry}) == Matrix(1, 1, 1.0));\n  CHECK(filter.filter_matrix(\n            {1, Spectral::Basis::Cartoon,\n             Spectral::Quadrature::SphericalSymmetry}) == Matrix(1, 1, 1.0));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.Filter\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  // Can't do a loop over different alpha and half_power values because matrices\n  // are cached in the action.\n  const double alpha = 10.0;\n  const unsigned half_power = 16;\n  tmpl::for_each<tmpl::make_sequence<tmpl::size_t<1>, 3>>(\n      [&alpha, &half_power](auto dim_v) {\n        constexpr size_t Dim = tmpl::type_from<decltype(dim_v)>::value;\n        for (const bool enable : make_array(true, false)) {\n          invoke_test_exponential_filter_action<Dim, true>(alpha, half_power,\n                                                           enable);\n          invoke_test_exponential_filter_action<Dim, false>(alpha, half_power,\n                                                            enable);\n        }\n      });\n\n  test_exponential_filter_creation<1>();\n  test_exponential_filter_creation<2>();\n  test_exponential_filter_creation<3>();\n\n  test_cartoon_exponential_filter();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_IndefiniteIntegral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/StripeIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/IndefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\nnamespace {\ntemplate <typename VectorType, size_t Dim, typename T>\nVectorType integrand(const std::array<size_t, Dim>& exponent, const T& x,\n                     const typename VectorType::ElementType overall_factor) {\n  VectorType integrand(get<0>(x).size(), overall_factor);\n  for (size_t d = 0; d < Dim; ++d) {\n    integrand *= pow(x.get(d), gsl::at(exponent, d));\n  }\n  return integrand;\n}\n\ntemplate <typename VectorType, size_t Dim, typename T>\nVectorType integral(const std::array<size_t, Dim>& exponent, const T& x,\n                    const Mesh<Dim>& mesh, const size_t only_this_dim,\n                    const typename VectorType::ElementType overall_factor) {\n  VectorType integral(get<0>(x).size(), overall_factor);\n  for (size_t d = 0; d < Dim; ++d) {\n    if (d == only_this_dim) {\n      integral *= pow(x.get(d), gsl::at(exponent, d) + 1) /\n                  (gsl::at(exponent, d) + 1.0);\n      // Adjust to zero integral on lower boundary.\n      for (StripeIterator stripe_it(mesh.extents(), d); stripe_it;\n           ++stripe_it) {\n        for (size_t count = 1, i = stripe_it.offset() + stripe_it.stride();\n             count < mesh.extents()[d]; ++count, i += stripe_it.stride()) {\n          integral[i] -= integral[stripe_it.offset()];\n        }\n        integral[stripe_it.offset()] = 0.0;\n      }\n    } else {\n      integral *= pow(x.get(d), gsl::at(exponent, d));\n    }\n  }\n  return integral;\n}\n\ntemplate <typename VectorType, Spectral::Basis BasisType,\n          Spectral::Quadrature QuadratureType>\nvoid test_zero_bc() {\n  const size_t min_pts = 2;\n  REQUIRE(5 <= Spectral::maximum_number_of_points<BasisType>);\n\n  MAKE_GENERATOR(generator);\n  UniformCustomDistribution<\n      tt::get_fundamental_type_t<typename VectorType::ElementType>>\n      dist{0.5, 2.0};\n  const auto overall_factor =\n      make_with_random_values<typename VectorType::ElementType>(\n          make_not_null(&generator), make_not_null(&dist));\n\n  for (size_t i = min_pts; i < 5; ++i) {\n    Mesh<1> mesh_1d(i, BasisType, QuadratureType);\n    const auto coords_1d = logical_coordinates(mesh_1d);\n    CHECK_ITERABLE_APPROX(\n        indefinite_integral(\n            integrand<VectorType, 1>({{i - 2}}, coords_1d, overall_factor),\n            mesh_1d, 0),\n        integral<VectorType>({{i - 2}}, coords_1d, mesh_1d, 0, overall_factor));\n\n    // 2d and 3d cases\n    for (size_t j = min_pts; j < 5; ++j) {\n      Mesh<2> mesh_2d({{i, j}}, {{BasisType, BasisType}},\n                      {{QuadratureType, QuadratureType}});\n      const auto coords_2d = logical_coordinates(mesh_2d);\n      for (size_t d = 0; d < 2; ++d) {\n        CHECK_ITERABLE_APPROX(\n            indefinite_integral(\n                integrand<VectorType, 2>({{i - 2, j - 2}}, coords_2d,\n                                         overall_factor),\n                mesh_2d, d),\n            integral<VectorType>({{i - 2, j - 2}}, coords_2d, mesh_2d, d,\n                                 overall_factor));\n      }\n\n      // 3d case\n      for (size_t k = min_pts; k < 5; ++k) {\n        Mesh<3> mesh_3d({{i, j, k}}, {{BasisType, BasisType, BasisType}},\n                        {{QuadratureType, QuadratureType, QuadratureType}});\n        const auto coords_3d = logical_coordinates(mesh_3d);\n        for (size_t d = 0; d < 3; ++d) {\n          CHECK_ITERABLE_APPROX(\n              indefinite_integral(\n                  integrand<VectorType, 3>({{i - 2, j - 2, k - 2}}, coords_3d,\n                                           overall_factor),\n                  mesh_3d, d),\n              integral<VectorType>({{i - 2, j - 2, k - 2}}, coords_3d, mesh_3d,\n                                   d, overall_factor));\n        }\n      }\n    }\n  }\n}\n\nvoid test_failing() {\n  {\n    INFO(\"Testing Cartoon throws error\");\n    const Mesh<3> mesh(\n        {2, 2, 1},\n        {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n         Spectral::Basis::Cartoon},\n        {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n         Spectral::Quadrature::AxialSymmetry});\n    CHECK_THROWS_WITH(\n        (indefinite_integral(DataVector{mesh.number_of_grid_points(), 1.0},\n                             mesh, 2)),\n        Catch::Matchers::ContainsSubstring(\n            \"An indefinite integral cannot be preformed on a Cartoon basis.\"));\n  }\n  {\n    INFO(\"Testing Fourier throws error\");\n    const Mesh<2> mesh({2, 5},\n                       {Spectral::Basis::Legendre, Spectral::Basis::Fourier},\n                       {Spectral::Quadrature::GaussLobatto,\n                        Spectral::Quadrature::Equiangular});\n    CHECK_THROWS_WITH(\n        (indefinite_integral(DataVector{mesh.number_of_grid_points(), 1.0},\n                             mesh, 1)),\n        Catch::Matchers::ContainsSubstring(\n            \"Indefinite integral matrix is not implemented for Fourier basis\"));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.IndefiniteIntegral\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  test_zero_bc<DataVector, Spectral::Basis::Chebyshev,\n               Spectral::Quadrature::GaussLobatto>();\n  test_zero_bc<DataVector, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto>();\n  test_zero_bc<ComplexDataVector, Spectral::Basis::Chebyshev,\n               Spectral::Quadrature::GaussLobatto>();\n  test_zero_bc<ComplexDataVector, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto>();\n  test_failing();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_Linearize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/StripeIterator.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Linearize.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Blas.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.Linearize\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  // The start and end are chosen to give fast tests\n  const size_t n_start = 2;\n  const size_t n_end = 5;\n  for (size_t nx = n_start; nx < n_end; ++nx) {\n    for (size_t ny = n_start; ny < n_end; ++ny) {\n      for (size_t nz = n_start; nz < n_end; ++nz) {\n        const Mesh<3> mesh{{{nx, ny, nz}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n        const DataVector& x =\n            Spectral::collocation_points(mesh.slice_through(0));\n        const DataVector& y =\n            Spectral::collocation_points(mesh.slice_through(1));\n        const DataVector& z =\n            Spectral::collocation_points(mesh.slice_through(2));\n        DataVector u(mesh.number_of_grid_points());\n        for (IndexIterator<3> i(mesh.extents()); i; ++i) {\n          u[i.collapsed_index()] =\n              exp(x[i()[0]]) * exp(y[i()[1]]) * exp(z[i()[2]]);\n        }\n        DataVector u_lin = linearize(u, mesh);\n        for (size_t d = 0; d < 3; ++d) {\n          for (StripeIterator s(mesh.extents(), d); s; ++s) {\n            const Matrix& inv_v =\n                Spectral::nodal_to_modal_matrix(mesh.slice_through(d));\n            const auto slice_points = mesh.extents(d);\n            DataVector u_s(slice_points);\n            dgemv_('N', slice_points, slice_points, 1., inv_v.data(),\n                   inv_v.spacing(), u_lin.data() + s.offset(),  // NOLINT\n                   s.stride(), 0.0, u_s.data(), 1);\n            for (size_t i = 2; i < slice_points; ++i) {\n              CHECK(0.0 == approx(u_s[i]));\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.LinearizeALinearFunction\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  const size_t n_start = 2;\n  const size_t n_end = 5;\n  for (size_t nx = n_start; nx < n_end; ++nx) {\n    for (size_t ny = n_start; ny < n_end; ++ny) {\n      for (size_t nz = n_start; nz < n_end; ++nz) {\n        const Mesh<3> mesh{{{nx, ny, nz}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n        const DataVector& x =\n            Spectral::collocation_points(mesh.slice_through(0));\n        const DataVector& y =\n            Spectral::collocation_points(mesh.slice_through(1));\n        const DataVector& z =\n            Spectral::collocation_points(mesh.slice_through(2));\n        DataVector u(mesh.number_of_grid_points());\n        for (IndexIterator<3> i(mesh.extents()); i; ++i) {\n          u[i.collapsed_index()] = 3 * x[i()[0]] + 5 * y[i()[1]] + z[i()[2]];\n        }\n        const DataVector u_lin = linearize(u, mesh);\n        CHECK_ITERABLE_APPROX(u, u_lin);\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.LinearizeInOneDim\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  // The start and end are chosen to give fast tests\n  const size_t n_start = 3;\n  const size_t n_end = 5;\n  for (size_t nx = n_start; nx < n_end; ++nx) {\n    for (size_t ny = n_start; ny < n_end; ++ny) {\n      for (size_t nz = n_start; nz < n_end; ++nz) {\n        const Mesh<3> mesh{{{nx, ny, nz}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n        const DataVector& x =\n            Spectral::collocation_points(mesh.slice_through(0));\n        const DataVector& y =\n            Spectral::collocation_points(mesh.slice_through(1));\n        const DataVector& z =\n            Spectral::collocation_points(mesh.slice_through(2));\n        DataVector u(mesh.number_of_grid_points());\n        for (IndexIterator<3> i(mesh.extents()); i; ++i) {\n          u[i.collapsed_index()] =\n              exp(x[i()[0]]) * exp(y[i()[1]]) * exp(z[i()[2]]);\n        }\n        for (size_t d = 0; d < 3; ++d) {\n          DataVector u_lin = linearize(u, mesh, d);\n          for (StripeIterator s(mesh.extents(), d); s; ++s) {\n            const Matrix& inv_v =\n                Spectral::nodal_to_modal_matrix(mesh.slice_through(d));\n            const auto slice_points = mesh.extents(d);\n            DataVector u_s(slice_points);\n            dgemv_('N', slice_points, slice_points, 1., inv_v.data(),\n                   inv_v.spacing(), u_lin.data() + s.offset(),  // NOLINT\n                   s.stride(), 0.0, u_s.data(), 1);\n            for (size_t i = 2; i < slice_points; ++i) {\n              CHECK(0.0 == approx(u_s[i]));\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_MeanValue.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <numeric>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/SliceIterator.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Linearize.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/MeanValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid test_mean_value() {\n  constexpr size_t min_extents =\n      Spectral::minimum_number_of_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>;\n  constexpr size_t max_extents =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n  for (size_t nx = min_extents; nx <= max_extents; ++nx) {\n    for (size_t ny = min_extents; ny <= max_extents; ++ny) {\n      for (size_t nz = min_extents; nz <= max_extents; ++nz) {\n        const Mesh<3> mesh{{{nx, ny, nz}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n        const DataVector& x =\n            Spectral::collocation_points(mesh.slice_through(0));\n        const DataVector& y =\n            Spectral::collocation_points(mesh.slice_through(1));\n        const DataVector& z =\n            Spectral::collocation_points(mesh.slice_through(2));\n        DataVector u(mesh.number_of_grid_points());\n        for (IndexIterator<3> i(mesh.extents()); i; ++i) {\n          u[i.collapsed_index()] =\n              exp(x[i()[0]]) * exp(y[i()[1]]) * exp(z[i()[2]]);\n        }\n        const DataVector u_lin = linearize(u, mesh);\n        double sum = std::accumulate(u_lin.begin(), u_lin.end(), 0.0);\n        CHECK(sum / mesh.number_of_grid_points() ==\n              approx(mean_value(u, mesh)));\n      }\n    }\n  }\n}\n\nvoid test_mean_value_on_boundary() {\n  constexpr size_t min_extents =\n      Spectral::minimum_number_of_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>;\n  constexpr size_t max_extents =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n  for (size_t nx = min_extents; nx <= max_extents; ++nx) {\n    for (size_t ny = min_extents; ny <= max_extents; ++ny) {\n      for (size_t nz = min_extents; nz <= max_extents; ++nz) {\n        const Mesh<3> mesh{{{nx, ny, nz}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n        const DataVector& x =\n            Spectral::collocation_points(mesh.slice_through(0));\n        const DataVector& y =\n            Spectral::collocation_points(mesh.slice_through(1));\n        const DataVector& z =\n            Spectral::collocation_points(mesh.slice_through(2));\n        const DataVector u_lin = [&mesh, &x, &y, &z]() {\n          DataVector temp(mesh.number_of_grid_points());\n          for (IndexIterator<3> i(mesh.extents()); i; ++i) {\n            temp[i.collapsed_index()] = x[i()[0]] + y[i()[1]] + z[i()[2]];\n          }\n          return temp;\n        }();\n        const DataVector u_quad = [&mesh, &x, &y]() {\n          DataVector temp(mesh.number_of_grid_points());\n          for (IndexIterator<3> i(mesh.extents()); i; ++i) {\n            temp[i.collapsed_index()] = x[i()[0]] * y[i()[1]];\n          }\n          return temp;\n        }();\n\n        const auto buffer_and_indices =\n            volume_and_slice_indices(mesh.extents());\n        const auto& indices = buffer_and_indices.second;\n\n        std::array<DataVector, 3> temp_buffers{\n            {DataVector(ny * nz), DataVector(nx * nz), DataVector(nx * ny)}};\n\n        // slice away x\n        CHECK(1.0 ==\n              approx(mean_value_on_boundary(u_lin, mesh, 0, Side::Upper)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(u_quad, mesh, 0, Side::Upper)));\n\n        CHECK(-1.0 ==\n              approx(mean_value_on_boundary(u_lin, mesh, 0, Side::Lower)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(u_quad, mesh, 0, Side::Lower)));\n\n        CHECK(1.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[0], indices[0].second,\n                                            u_lin, mesh, 0, Side::Upper)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[0], indices[0].second,\n                                            u_quad, mesh, 0, Side::Upper)));\n\n        CHECK(-1.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[0], indices[0].first,\n                                            u_lin, mesh, 0, Side::Lower)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[0], indices[0].first,\n                                            u_quad, mesh, 0, Side::Lower)));\n\n        // slice away y\n        CHECK(1.0 ==\n              approx(mean_value_on_boundary(u_lin, mesh, 1, Side::Upper)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(u_quad, mesh, 1, Side::Upper)));\n\n        CHECK(-1.0 ==\n              approx(mean_value_on_boundary(u_lin, mesh, 1, Side::Lower)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(u_quad, mesh, 1, Side::Lower)));\n\n        CHECK(1.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[1], indices[1].second,\n                                            u_lin, mesh, 1, Side::Upper)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[1], indices[1].second,\n                                            u_quad, mesh, 1, Side::Upper)));\n\n        CHECK(-1.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[1], indices[1].first,\n                                            u_lin, mesh, 1, Side::Lower)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[1], indices[1].first,\n                                            u_quad, mesh, 1, Side::Lower)));\n\n        // slice away z\n        CHECK(1.0 ==\n              approx(mean_value_on_boundary(u_lin, mesh, 2, Side::Upper)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(u_quad, mesh, 2, Side::Upper)));\n\n        CHECK(-1.0 ==\n              approx(mean_value_on_boundary(u_lin, mesh, 2, Side::Lower)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(u_quad, mesh, 2, Side::Lower)));\n\n        CHECK(1.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[2], indices[2].second,\n                                            u_lin, mesh, 2, Side::Upper)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[2], indices[2].second,\n                                            u_quad, mesh, 2, Side::Upper)));\n\n        CHECK(-1.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[2], indices[2].first,\n                                            u_lin, mesh, 2, Side::Lower)));\n        CHECK(0.0 ==\n              approx(mean_value_on_boundary(&temp_buffers[2], indices[2].first,\n                                            u_quad, mesh, 2, Side::Lower)));\n      }\n    }\n  }\n}\n\nvoid test_mean_value_on_boundary_1d() {\n  constexpr size_t min_extents =\n      Spectral::minimum_number_of_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>;\n  constexpr size_t max_extents =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n  for (size_t nx = min_extents; nx < max_extents; ++nx) {\n    const Mesh<1> mesh{nx, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const DataVector& x = Spectral::collocation_points(mesh);\n    const DataVector u_lin = [&mesh, &x]() {\n      DataVector temp(mesh.number_of_grid_points());\n      for (IndexIterator<1> i(mesh.extents()); i; ++i) {\n        temp[i.collapsed_index()] = x[i()[0]];\n      }\n      return temp;\n    }();\n    // slice away x\n    CHECK(1.0 == approx(mean_value_on_boundary(u_lin, mesh, 0, Side::Upper)));\n    CHECK(-1.0 == approx(mean_value_on_boundary(u_lin, mesh, 0, Side::Lower)));\n\n    // test overload with slice indices\n    DataVector temp{};\n    CHECK(1.0 == approx(mean_value_on_boundary(&temp, {}, u_lin, mesh, 0,\n                                               Side::Upper)));\n    CHECK(-1.0 == approx(mean_value_on_boundary(&temp, {}, u_lin, mesh, 0,\n                                                Side::Lower)));\n  }\n}\n}  // namespace\n\n// [[Timeout, 10]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.MeanValue\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  SECTION(\"Mean Value\") { test_mean_value(); }\n  SECTION(\"Mean Value on Boundary\") { test_mean_value_on_boundary(); }\n  SECTION(\"Mean Value on Boundary 1d\") { test_mean_value_on_boundary_1d(); }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_PartialDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Metafunctions.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/PolarToCartesian.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/CoordinateMaps/SphericalToCartesianPfaffian.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/DiskTestFunctions.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Fourier.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Zernike.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <typename DataType, size_t Dim, class Frame = ::Frame::Grid>\nstruct Var1 : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim, Frame>;\n  static auto f(const std::array<size_t, Dim>& coeffs,\n                const tnsr::I<DataVector, Dim, Frame>& x) {\n    tnsr::i<DataType, Dim, Frame> result(x.begin()->size(), 0.);\n    for (size_t i = 0; i < Dim; ++i) {\n      result.get(i) = (i + 2);\n      for (size_t d = 0; d < Dim; ++d) {\n        result.get(i) *= pow(x.get(d), gsl::at(coeffs, d));\n      }\n    }\n    // Set the imaginary part\n    if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n      for (size_t i = 0; i < Dim; ++i) {\n        ComplexDataVector imaginary_part(x.begin()->size(), 0.);\n        imaginary_part = std::complex<double>(0., static_cast<double>(i) + 3);\n        for (size_t d = 0; d < Dim; ++d) {\n          imaginary_part *=\n              (static_cast<double>(d) + 2) * pow(x.get(d), gsl::at(coeffs, d));\n        }\n        result.get(i) += imaginary_part;\n      }\n    }\n    return result;\n  }\n  static auto df(const std::array<size_t, Dim>& coeffs,\n                 const tnsr::I<DataVector, Dim, Frame>& x) {\n    tnsr::ij<DataType, Dim, Frame> result(x.begin()->size(), 0.);\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t j = 0; j < Dim; ++j) {\n        result.get(i, j) = (j + 2);\n        for (size_t d = 0; d < Dim; ++d) {\n          if (d == i) {\n            if (0 == gsl::at(coeffs, d)) {\n              result.get(i, j) = 0.;\n            } else {\n              result.get(i, j) *=\n                  gsl::at(coeffs, d) * pow(x.get(d), gsl::at(coeffs, d) - 1);\n            }\n          } else {\n            result.get(i, j) *= pow(x.get(d), gsl::at(coeffs, d));\n          }\n        }\n      }\n    }\n    // Set the imaginary part\n    if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n      for (size_t i = 0; i < Dim; ++i) {\n        for (size_t j = 0; j < Dim; ++j) {\n          ComplexDataVector imaginary_part(x.begin()->size(), 0.);\n          imaginary_part = std::complex<double>(0., static_cast<double>(j) + 3);\n          for (size_t d = 0; d < Dim; ++d) {\n            if (d == i) {\n              if (0 == gsl::at(coeffs, d)) {\n                imaginary_part = 0.;\n              } else {\n                imaginary_part *= (static_cast<double>(d) + 2) *\n                                  gsl::at(coeffs, d) *\n                                  pow(x.get(d), gsl::at(coeffs, d) - 1);\n              }\n            } else {\n              imaginary_part *= (static_cast<double>(d) + 2) *\n                                pow(x.get(d), gsl::at(coeffs, d));\n            }\n          }\n          result.get(i, j) += imaginary_part;\n        }\n      }\n    }\n    return result;\n  }\n};\n\ntemplate <typename DataType>\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataType>;\n  template <size_t Dim, class Frame>\n  static auto f(const std::array<size_t, Dim>& coeffs,\n                const tnsr::I<DataVector, Dim, Frame>& x) {\n    Scalar<DataType> result(x.begin()->size(), 1.);\n    for (size_t d = 0; d < Dim; ++d) {\n      result.get() *= pow(x.get(d), gsl::at(coeffs, d));\n    }\n    return result;\n  }\n  template <size_t Dim, class Frame>\n  static auto df(const std::array<size_t, Dim>& coeffs,\n                 const tnsr::I<DataVector, Dim, Frame>& x) {\n    tnsr::i<DataType, Dim, Frame> result(x.begin()->size(), 1.);\n    for (size_t i = 0; i < Dim; ++i) {\n      for (size_t d = 0; d < Dim; ++d) {\n        if (d == i) {\n          if (0 == gsl::at(coeffs, d)) {\n            result.get(i) = 0.0;\n          } else {\n            result.get(i) *=\n                gsl::at(coeffs, d) * pow(x.get(d), gsl::at(coeffs, d) - 1);\n          }\n        } else {\n          result.get(i) *= pow(x.get(d), gsl::at(coeffs, d));\n        }\n      }\n    }\n    return result;\n  }\n};\n\ntemplate <typename DataType, size_t Dim>\nusing two_vars = tmpl::list<Var1<DataType, Dim>, Var2<DataType>>;\n\ntemplate <typename DataType, size_t Dim>\nusing one_var = tmpl::list<Var1<DataType, Dim>>;\n\ntemplate <typename DataType>\nusing scalar_var = tmpl::list<Var2<DataType>>;\n\ntemplate <typename GradientTags, typename VariableTags, size_t Dim>\nvoid test_logical_partial_derivative_per_tensor(\n    const std::array<Variables<GradientTags>, Dim>& du,\n    const Variables<VariableTags>& u, const Mesh<Dim>& mesh) {\n  using VectorType = typename Variables<VariableTags>::vector_type;\n  using ValueType = typename Variables<VariableTags>::value_type;\n  tmpl::for_each<GradientTags>([&du, &mesh, &u](auto gradient_tag_v) {\n    using gradient_tag = tmpl::type_from<decltype(gradient_tag_v)>;\n    const auto single_du =\n        logical_partial_derivative(get<gradient_tag>(u), mesh);\n    for (size_t storage_index = 0; storage_index < get<gradient_tag>(u).size();\n         ++storage_index) {\n      for (size_t d = 0; d < Dim; ++d) {\n        const auto deriv_tensor_index =\n            prepend(get<gradient_tag>(u).get_tensor_index(storage_index), d);\n        CHECK_ITERABLE_APPROX(single_du.get(deriv_tensor_index),\n                              get<gradient_tag>(gsl::at(du, d))[storage_index]);\n      }\n    }\n    std::decay_t<decltype(single_du)> single_du_not_null{};\n    VectorType buffer{mesh.number_of_grid_points()};\n    gsl::span<ValueType> buffer_view{buffer.data(), buffer.size()};\n    logical_partial_derivative(make_not_null(&single_du_not_null),\n                               make_not_null(&buffer_view),\n                               get<gradient_tag>(u), mesh);\n    CHECK_ITERABLE_APPROX(single_du_not_null, single_du);\n\n    // Check we can do derivatives when the components of `u` aren't contiguous\n    // in memory.\n    // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n    std::decay_t<decltype(get<gradient_tag>(u))> non_contiguous_u =\n        get<gradient_tag>(u);\n    const auto non_contiguous_single_du =\n        logical_partial_derivative(get<gradient_tag>(u), mesh);\n    CHECK_ITERABLE_APPROX(non_contiguous_single_du, single_du);\n  });\n}\n\ntemplate <typename GradientTags, typename VariableTags, size_t Dim,\n          typename DerivativeFrame>\nvoid test_partial_derivative_per_tensor(\n    const Variables<GradientTags>& du, const Variables<VariableTags>& u,\n    const Mesh<Dim>& mesh,\n    const InverseJacobian<DataVector, Dim, Frame::ElementLogical,\n                          DerivativeFrame>& inverse_jacobian) {\n  tmpl::for_each<GradientTags>(\n      [&du, &mesh, &u, &inverse_jacobian](auto gradient_tag_v) {\n        using gradient_tag = tmpl::type_from<decltype(gradient_tag_v)>;\n        using var_tag = typename gradient_tag::tag;\n\n        const auto single_du =\n            partial_derivative(get<var_tag>(u), mesh, inverse_jacobian);\n\n        Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n        CHECK_ITERABLE_CUSTOM_APPROX(single_du, get<gradient_tag>(du),\n                                     local_approx);\n\n        // Check we can do derivatives when the components of `u` aren't\n        // contiguous in memory.\n        // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n        const auto non_contiguous_u = get<var_tag>(u);\n        const auto non_contiguous_single_du =\n            partial_derivative(non_contiguous_u, mesh, inverse_jacobian);\n        CHECK_ITERABLE_APPROX(non_contiguous_single_du, single_du);\n      });\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_logical_partial_derivatives_1d(const Mesh<1>& mesh) {\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const DataVector& xi = Spectral::collocation_points(mesh.slice_through(0));\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<GradientTags> du_expected(number_of_grid_points);\n  for (size_t a = 0; a < mesh.extents(0); ++a) {\n    for (size_t n = 0; n < u.number_of_independent_components; ++n) {\n      for (size_t s = 0; s < number_of_grid_points; ++s) {\n        u.data()[s + n * number_of_grid_points]  // NOLINT\n            = static_cast<double>(n + 1) * pow(xi[s], static_cast<double>(a));\n      }\n    }\n    // Generate expected data\n    for (size_t n = 0;\n         n < Variables<GradientTags>::number_of_independent_components; ++n) {\n      for (size_t s = 0; s < number_of_grid_points; ++s) {\n        du_expected.data()[s + n * number_of_grid_points] =\n            (0 == a ? 0.0\n                    : static_cast<double>(a * (n + 1)) *\n                          pow(xi[s], static_cast<double>(a - 1)));\n      }\n    }\n    CHECK_VARIABLES_APPROX(\n        (logical_partial_derivatives<GradientTags>(u, mesh)[0]), du_expected);\n    std::array<Variables<GradientTags>, 1> du{};\n    logical_partial_derivatives(make_not_null(&du), u, mesh);\n    CHECK_VARIABLES_APPROX(du[0], du_expected);\n    // We've checked that du is correct, now test that taking derivatives of\n    // individual tensors gets the matching result.\n    test_logical_partial_derivative_per_tensor(du, u, mesh);\n  }\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_logical_partial_derivatives_2d(const Mesh<2>& mesh) {\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const DataVector& xi = Spectral::collocation_points(mesh.slice_through(0));\n  const DataVector& eta = Spectral::collocation_points(mesh.slice_through(1));\n  Variables<VariableTags> u(mesh.number_of_grid_points());\n  std::array<Variables<GradientTags>, 2> du_expected{};\n  du_expected[0].initialize(mesh.number_of_grid_points());\n  du_expected[1].initialize(mesh.number_of_grid_points());\n  const size_t a = mesh.extents(0) - 1;\n  const size_t b = mesh.extents(1) - 1;\n  for (size_t n = 0; n < u.number_of_independent_components; ++n) {\n    for (IndexIterator<2> ii(mesh.extents()); ii; ++ii) {\n      u.data()[ii.collapsed_index() + n * number_of_grid_points] =  // NOLINT\n          static_cast<double>(n + 1) *\n          pow(xi[ii()[0]], static_cast<double>(a)) *\n          pow(eta[ii()[1]], static_cast<double>(b));\n    }\n  }\n  // Generate expected data\n  for (size_t n = 0;\n       n < Variables<GradientTags>::number_of_independent_components; ++n) {\n    for (IndexIterator<2> ii(mesh.extents()); ii; ++ii) {\n      du_expected[0].data()[ii.collapsed_index() + n * number_of_grid_points] =\n          (0 == a ? 0.0\n                  : static_cast<double>(a * (n + 1)) *\n                        pow(xi[ii()[0]], static_cast<double>(a - 1)) *\n                        pow(eta[ii()[1]], static_cast<double>(b)));\n      du_expected[1].data()[ii.collapsed_index() + n * number_of_grid_points] =\n          (0 == b ? 0.0\n                  : static_cast<double>(b * (n + 1)) *\n                        pow(xi[ii()[0]], static_cast<double>(a)) *\n                        pow(eta[ii()[1]],\n                            static_cast<double>(static_cast<double>(b - 1))));\n    }\n  }\n  const auto du = logical_partial_derivatives<GradientTags>(u, mesh);\n  CHECK_VARIABLES_APPROX(du[0], du_expected[0]);\n  CHECK_VARIABLES_APPROX(du[1], du_expected[1]);\n  // We've checked that du is correct, now test that taking derivatives of\n  // individual tensors gets the matching result.\n  test_logical_partial_derivative_per_tensor(du, u, mesh);\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_logical_partial_derivatives_3d(const Mesh<3>& mesh) {\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const DataVector& xi = Spectral::collocation_points(mesh.slice_through(0));\n  const DataVector& eta = Spectral::collocation_points(mesh.slice_through(1));\n  const DataVector& zeta = Spectral::collocation_points(mesh.slice_through(2));\n  Variables<VariableTags> u(number_of_grid_points);\n  std::array<Variables<GradientTags>, 3> du_expected{};\n  du_expected[0].initialize(number_of_grid_points);\n  du_expected[1].initialize(number_of_grid_points);\n  du_expected[2].initialize(number_of_grid_points);\n  const size_t a = mesh.extents(0) - 1;\n  const size_t b = mesh.extents(1) - 1;\n  const size_t c = mesh.extents(2) - 1;\n  for (size_t n = 0; n < u.number_of_independent_components; ++n) {\n    for (IndexIterator<3> ii(mesh.extents()); ii; ++ii) {\n      u.data()[ii.collapsed_index() + n * number_of_grid_points] =  // NOLINT\n          static_cast<double>(n + 1) *\n          pow(xi[ii()[0]], static_cast<double>(a)) *\n          pow(eta[ii()[1]], static_cast<double>(b)) *\n          pow(zeta[ii()[2]], static_cast<double>(c));\n    }\n  }\n  // Generate expected data\n  for (size_t n = 0;\n       n < Variables<GradientTags>::number_of_independent_components; ++n) {\n    for (IndexIterator<3> ii(mesh.extents()); ii; ++ii) {\n      du_expected[0].data()[ii.collapsed_index() + n * number_of_grid_points] =\n          (0 == a ? 0.0\n                  : static_cast<double>(a * (n + 1)) *\n                        pow(xi[ii()[0]], static_cast<double>(a - 1)) *\n                        pow(eta[ii()[1]], static_cast<double>(b)) *\n                        pow(zeta[ii()[2]], static_cast<double>(c)));\n      du_expected[1].data()[ii.collapsed_index() + n * number_of_grid_points] =\n          (0 == b ? 0.0\n                  : static_cast<double>(b * (n + 1)) *\n                        pow(xi[ii()[0]], static_cast<double>(a)) *\n                        pow(eta[ii()[1]], static_cast<double>(b - 1)) *\n                        pow(zeta[ii()[2]], static_cast<double>(c)));\n      du_expected[2].data()[ii.collapsed_index() + n * number_of_grid_points] =\n          (0 == c ? 0.0\n                  : static_cast<double>(c * (n + 1)) *\n                        pow(xi[ii()[0]], static_cast<double>(a)) *\n                        pow(eta[ii()[1]], static_cast<double>(b)) *\n                        pow(zeta[ii()[2]], static_cast<double>(c - 1)));\n    }\n  }\n  const auto du = logical_partial_derivatives<GradientTags>(u, mesh);\n  CHECK_VARIABLES_APPROX(du[0], du_expected[0]);\n  CHECK_VARIABLES_APPROX(du[1], du_expected[1]);\n  CHECK_VARIABLES_APPROX(du[2], du_expected[2]);\n  // We've checked that du is correct, now test that taking derivatives of\n  // individual tensors gets the matching result.\n  test_logical_partial_derivative_per_tensor(du, u, mesh);\n}\n\ntemplate <size_t l, int m, typename VariableTags,\n          typename GradientTags = VariableTags>\nvoid test_logical_partial_derivatives_spherical_shell(const size_t n_r,\n                                                      const size_t L) {\n  const Mesh<3> mesh{\n      {n_r, L + 1, 2 * L + 1},\n      {Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n       Spectral::Basis::SphericalHarmonic},\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss,\n       Spectral::Quadrature::Equiangular}};\n  const auto x = logical_coordinates(mesh);\n  const DataVector r = x[0] + 2.0;\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const YlmTestFunctions::Ylm<l, m> y_lm{n_r, L, L};\n  Variables<VariableTags> u{number_of_grid_points};\n  for (size_t n = 0; n < u.number_of_independent_components; ++n) {\n    DataVector component_ref(u.data() + n * number_of_grid_points,\n                             number_of_grid_points);\n    component_ref = (1.0 + static_cast<double>(n)) *\n                    pow(r, -1.0 + static_cast<double>(n_r)) * y_lm.f();\n  }\n  std::array<Variables<GradientTags>, 3> du_expected{};\n  du_expected[0].initialize(number_of_grid_points);\n  du_expected[1].initialize(number_of_grid_points);\n  du_expected[2].initialize(number_of_grid_points);\n  for (size_t n = 0;\n       n < Variables<GradientTags>::number_of_independent_components; ++n) {\n    DataVector du_dxi_ref(du_expected[0].data() + n * number_of_grid_points,\n                          number_of_grid_points);\n    if (n_r == 1) {\n      du_dxi_ref = 0.0;\n    } else {\n      du_dxi_ref = (-1.0 + static_cast<double>(n_r)) *\n                   (1.0 + static_cast<double>(n)) *\n                   pow(r, -2.0 + static_cast<double>(n_r)) * y_lm.f();\n    }\n    DataVector du_dth_ref(du_expected[1].data() + n * number_of_grid_points,\n                          number_of_grid_points);\n    du_dth_ref = (1.0 + static_cast<double>(n)) *\n                 pow(r, -1.0 + static_cast<double>(n_r)) * y_lm.df_dth();\n    DataVector du_dph_ref(du_expected[2].data() + n * number_of_grid_points,\n                          number_of_grid_points);\n    du_dph_ref = (1.0 + static_cast<double>(n)) *\n                 pow(r, -1.0 + static_cast<double>(n_r)) * y_lm.df_dph();\n  }\n  const auto du = logical_partial_derivatives<GradientTags>(u, mesh);\n  Approx local_approx = Approx::custom().epsilon(1.0e-13).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[0], du_expected[0], local_approx);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[1], du_expected[1], local_approx);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[2], du_expected[2], local_approx);\n  // We've checked that du is correct, now test that taking derivatives of\n  // individual tensors gets the matching result.\n  test_logical_partial_derivative_per_tensor(du, u, mesh);\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_logical_partial_derivatives_disk(const size_t n_r,\n                                           const size_t n_phi) {\n  CAPTURE(n_r);\n  CAPTURE(n_phi);\n  const Mesh<2> mesh{{n_r, n_phi},\n                     {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2},\n                     {Spectral::Quadrature::GaussRadauUpper,\n                      Spectral::Quadrature::Equiangular}};\n  const auto x = logical_coordinates(mesh);\n  const DataVector r = 0.5 * (x[0] + 1.0);\n  const DataVector& phi = x[1];\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const size_t k_max = n_phi / 2;\n  const size_t pow_nx = k_max / 2;\n  const size_t pow_ny = k_max - pow_nx;\n  const DiskTestFunctions::ProductOfPolynomials f1{pow_nx, pow_ny};\n  const DiskTestFunctions::ProductOfPolynomials f2{\n      pow_nx > 0 ? pow_nx - 1 : pow_nx, pow_ny};\n  const DiskTestFunctions::ProductOfPolynomials f3{\n      pow_nx, pow_ny > 0 ? pow_ny - 1 : pow_ny};\n  Variables<VariableTags> u{number_of_grid_points};\n  for (size_t n = 0; n < u.number_of_independent_components; ++n) {\n    DataVector component_ref(u.data() + n * number_of_grid_points,\n                             number_of_grid_points);\n    switch (n % 3) {\n      case 0:\n        component_ref = f1(r, phi);\n        break;\n      case 1:\n        component_ref = f2(r, phi);\n        break;\n      case 2:\n        component_ref = f3(r, phi);\n        break;\n      default:\n        ERROR(\"Cannot get here\");\n    }\n  }\n  std::array<Variables<GradientTags>, 2> du_expected{};\n  du_expected[0].initialize(number_of_grid_points);\n  du_expected[1].initialize(number_of_grid_points);\n  for (size_t n = 0;\n       n < Variables<GradientTags>::number_of_independent_components; ++n) {\n    DataVector du_dr_ref(du_expected[0].data() + n * number_of_grid_points,\n                         number_of_grid_points);\n    DataVector du_dph_ref(du_expected[1].data() + n * number_of_grid_points,\n                          number_of_grid_points);\n    // radial derivative factor of 1/2 from the fact that we provided an\n    // affine map to get from logical [-1,1] to actual logical [0,1]\n    // coordinates. This is correctly handled by the jacobian factor when\n    // one calls partial_derivatives()\n    switch (n % 3) {\n      case 0:\n        du_dr_ref = 0.5 * f1.df_dr(r, phi);\n        du_dph_ref = f1.df_dph(r, phi);\n        break;\n      case 1:\n        du_dr_ref = 0.5 * f2.df_dr(r, phi);\n        du_dph_ref = f2.df_dph(r, phi);\n        break;\n      case 2:\n        du_dr_ref = 0.5 * f3.df_dr(r, phi);\n        du_dph_ref = f3.df_dph(r, phi);\n        break;\n      default:\n        ERROR(\"Cannot get here\");\n    }\n  }\n  const auto du = logical_partial_derivatives<GradientTags>(u, mesh);\n  const Approx local_approx = Approx::custom().epsilon(1.0e-13).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[0], du_expected[0], local_approx);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[1], du_expected[1], local_approx);\n  // We've checked that du is correct, now test that taking derivatives of\n  // individual tensors gets the matching result.\n  test_logical_partial_derivative_per_tensor(du, u, mesh);\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_logical_partial_derivatives_cylinder(const size_t n_r,\n                                               const size_t n_phi,\n                                               const size_t n_z) {\n  CAPTURE(n_r);\n  CAPTURE(n_phi);\n  CAPTURE(n_z);\n  const Mesh<3> mesh{\n      {n_r, n_phi, n_z},\n      {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2,\n       Spectral::Basis::Legendre},\n      {Spectral::Quadrature::GaussRadauUpper, Spectral::Quadrature::Equiangular,\n       Spectral::Quadrature::GaussLobatto}};\n  const auto x = logical_coordinates(mesh);\n  const DataVector r = 0.5 * (x[0] + 1.0);\n  const DataVector& phi = x[1];\n  const DataVector& z = x[2];\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const size_t k_max = n_phi / 2;\n  const size_t pow_nx = k_max / 2;\n  const size_t pow_ny = k_max - pow_nx;\n  const DiskTestFunctions::ProductOfPolynomials f{pow_nx, pow_ny};\n  Variables<VariableTags> u{number_of_grid_points};\n  for (size_t n = 0; n < u.number_of_independent_components; ++n) {\n    DataVector component_ref(u.data() + n * number_of_grid_points,\n                             number_of_grid_points);\n    component_ref = (1.0 + static_cast<double>(n)) *\n                    pow(z, -1.0 + static_cast<double>(n_z)) * f(r, phi);\n  }\n  std::array<Variables<GradientTags>, 3> du_expected{};\n  du_expected[0].initialize(number_of_grid_points);\n  du_expected[1].initialize(number_of_grid_points);\n  du_expected[2].initialize(number_of_grid_points);\n  for (size_t n = 0;\n       n < Variables<GradientTags>::number_of_independent_components; ++n) {\n    DataVector du_dr_ref(du_expected[0].data() + n * number_of_grid_points,\n                         number_of_grid_points);\n    // radial derivative factor of 1/2 from the fact that we provided an\n    // affine map to get from logical [-1,1] to actual logical [0,1]\n    // coordinates. This is correctly handled by the jacobian factor when\n    // one calls partial_derivatives()\n    du_dr_ref = 0.5 * (1.0 + static_cast<double>(n)) *\n                pow(z, -1.0 + static_cast<double>(n_z)) * f.df_dr(r, phi);\n    DataVector du_dph_ref(du_expected[1].data() + n * number_of_grid_points,\n                          number_of_grid_points);\n    du_dph_ref = (1.0 + static_cast<double>(n)) *\n                 pow(z, -1.0 + static_cast<double>(n_z)) * f.df_dph(r, phi);\n    DataVector du_dzeta_ref(du_expected[2].data() + n * number_of_grid_points,\n                            number_of_grid_points);\n    if (n_z == 1) {\n      du_dzeta_ref = 0.0;\n    } else {\n      du_dzeta_ref = (-1.0 + static_cast<double>(n_z)) *\n                     (1.0 + static_cast<double>(n)) *\n                     pow(z, -2.0 + static_cast<double>(n_z)) * f(r, phi);\n    }\n  }\n  const auto du = logical_partial_derivatives<GradientTags>(u, mesh);\n  const Approx local_approx = Approx::custom().epsilon(1.0e-13).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[0], du_expected[0], local_approx);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[1], du_expected[1], local_approx);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[2], du_expected[2], local_approx);\n  // We've checked that du is correct, now test that taking derivatives of\n  // individual tensors gets the matching result.\n  test_logical_partial_derivative_per_tensor(du, u, mesh);\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_partial_derivatives_1d(const Mesh<1>& mesh) {\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const Affine x_map{-1.0, 1.0, -0.3, 0.7};\n  const auto map_1d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          Affine{x_map});\n  const auto x = map_1d(logical_coordinates(mesh));\n  const InverseJacobian<DataVector, 1, Frame::ElementLogical, Frame::Grid>\n      inverse_jacobian(number_of_grid_points, 2.0);\n\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, GradientTags, tmpl::size_t<1>, Frame::Grid>>\n      expected_du(number_of_grid_points);\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  for (size_t a = 0; a < mesh.extents(0); ++a) {\n    CAPTURE(a);\n    tmpl::for_each<VariableTags>([&a, &x, &u](auto tag) {\n      using Tag = tmpl::type_from<decltype(tag)>;\n      get<Tag>(u) = Tag::f({{a}}, x);\n    });\n    tmpl::for_each<GradientTags>([&a, &x, &expected_du](auto tag) {\n      using Tag = typename decltype(tag)::type;\n      using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<1>, Frame::Grid>;\n      get<DerivativeTag>(expected_du) = Tag::df({{a}}, x);\n    });\n\n    CHECK_VARIABLES_CUSTOM_APPROX(\n        (partial_derivatives<GradientTags>(u, mesh, inverse_jacobian)),\n        expected_du, local_approx);\n    using vars_type =\n        decltype(partial_derivatives<GradientTags>(u, mesh, inverse_jacobian));\n    vars_type du{};\n    partial_derivatives(make_not_null(&du), u, mesh, inverse_jacobian);\n    CHECK_VARIABLES_CUSTOM_APPROX(du, expected_du, local_approx);\n    partial_derivatives(make_not_null(&du),\n                        logical_partial_derivatives<GradientTags>(u, mesh),\n                        inverse_jacobian);\n    CHECK_VARIABLES_CUSTOM_APPROX(du, expected_du, local_approx);\n    // We've checked that du is correct, now test that taking derivatives of\n    // individual tensors gets the matching result.\n    test_partial_derivative_per_tensor(expected_du, u, mesh, inverse_jacobian);\n  }\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_partial_derivatives_2d(const Mesh<2>& mesh) {\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const auto prod_map2d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          Affine2D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55}});\n  const auto x = prod_map2d(logical_coordinates(mesh));\n  InverseJacobian<DataVector, 2, Frame::ElementLogical, Frame::Grid>\n      inverse_jacobian(number_of_grid_points, 0.0);\n  inverse_jacobian.get(0, 0) = 2.0;\n  inverse_jacobian.get(1, 1) = 8.0;\n\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, GradientTags, tmpl::size_t<2>, Frame::Grid>>\n      expected_du(number_of_grid_points);\n  Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  for (size_t a = 0; a < mesh.extents(0); ++a) {\n    for (size_t b = 0; b < mesh.extents(1); ++b) {\n      tmpl::for_each<VariableTags>([&a, &b, &x, &u](auto tag) {\n        using Tag = typename decltype(tag)::type;\n        get<Tag>(u) = Tag::f({{a, b}}, x);\n      });\n      tmpl::for_each<GradientTags>([&a, &b, &x, &expected_du](auto tag) {\n        using Tag = typename decltype(tag)::type;\n        using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<2>, Frame::Grid>;\n        get<DerivativeTag>(expected_du) = Tag::df({{a, b}}, x);\n      });\n\n      CHECK_VARIABLES_CUSTOM_APPROX(\n          (partial_derivatives<GradientTags>(u, mesh, inverse_jacobian)),\n          expected_du, local_approx);\n      using vars_type = decltype(partial_derivatives<GradientTags>(\n          u, mesh, inverse_jacobian));\n      vars_type du{};\n      partial_derivatives(make_not_null(&du), u, mesh, inverse_jacobian);\n      CHECK_VARIABLES_CUSTOM_APPROX(du, expected_du, local_approx);\n\n      vars_type du_with_logical{};\n      partial_derivatives(make_not_null(&du_with_logical),\n                          logical_partial_derivatives<GradientTags>(u, mesh),\n                          inverse_jacobian);\n      CHECK_VARIABLES_CUSTOM_APPROX(du_with_logical, expected_du, local_approx);\n\n      // We've checked that du is correct, now test that taking derivatives of\n      // individual tensors gets the matching result.\n      test_partial_derivative_per_tensor(du, u, mesh, inverse_jacobian);\n    }\n  }\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_partial_derivatives_3d(const Mesh<3>& mesh) {\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const auto prod_map3d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          Affine3D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n                   Affine{-1.0, 1.0, 2.3, 2.8}});\n  const auto x = prod_map3d(logical_coordinates(mesh));\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      inverse_jacobian(number_of_grid_points, 0.0);\n  inverse_jacobian.get(0, 0) = 2.0;\n  inverse_jacobian.get(1, 1) = 8.0;\n  inverse_jacobian.get(2, 2) = 4.0;\n\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, GradientTags, tmpl::size_t<3>, Frame::Grid>>\n      expected_du(number_of_grid_points);\n  Approx local_approx = Approx::custom().epsilon(1e-9).scale(1.0);\n  for (size_t a = 0; a < mesh.extents(0) / 2; ++a) {\n    for (size_t b = 0; b < mesh.extents(1) / 2; ++b) {\n      for (size_t c = 0; c < mesh.extents(2) / 2; ++c) {\n        tmpl::for_each<VariableTags>([&a, &b, &c, &x, &u](auto tag) {\n          using Tag = typename decltype(tag)::type;\n          get<Tag>(u) = Tag::f({{a, b, c}}, x);\n        });\n        tmpl::for_each<GradientTags>([&a, &b, &c, &x, &expected_du](auto tag) {\n          using Tag = typename decltype(tag)::type;\n          using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<3>, Frame::Grid>;\n          get<DerivativeTag>(expected_du) = Tag::df({{a, b, c}}, x);\n        });\n\n        CHECK_VARIABLES_CUSTOM_APPROX(\n            (partial_derivatives<GradientTags>(u, mesh, inverse_jacobian)),\n            expected_du, local_approx);\n        using vars_type = decltype(partial_derivatives<GradientTags>(\n            u, mesh, inverse_jacobian));\n        vars_type du{};\n        partial_derivatives(make_not_null(&du), u, mesh, inverse_jacobian);\n        CHECK_VARIABLES_CUSTOM_APPROX(du, expected_du, local_approx);\n\n        vars_type du_with_logical{};\n        partial_derivatives(make_not_null(&du_with_logical),\n                            logical_partial_derivatives<GradientTags>(u, mesh),\n                            inverse_jacobian);\n        CHECK_VARIABLES_CUSTOM_APPROX(du_with_logical, expected_du,\n                                      local_approx);\n\n        // We've checked that du is correct, now test that taking derivatives of\n        // individual tensors gets the matching result.\n        test_partial_derivative_per_tensor(du, u, mesh, inverse_jacobian);\n      }\n    }\n  }\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_partial_derivatives_spherical_shell() {\n  const size_t L = 4;\n  const size_t n_r = 4;\n  const Mesh<3> mesh{\n      {n_r, L + 1, 2 * L + 1},\n      {Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n       Spectral::Basis::SphericalHarmonic},\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss,\n       Spectral::Quadrature::Equiangular}};\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const auto prod_map3d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::ProductOf2Maps<\n              Affine, domain::CoordinateMaps::Identity<2>>{\n              Affine{-1.0, 1.0, 1.0, 1.5},\n              domain::CoordinateMaps::Identity<2>{}},\n          domain::CoordinateMaps::SphericalToCartesianPfaffian{});\n  const auto xi = logical_coordinates(mesh);\n  const auto x = prod_map3d(xi);\n  const InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      inverse_jacobian = prod_map3d.inv_jacobian(xi);\n\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, GradientTags, tmpl::size_t<3>, Frame::Grid>>\n      expected_du(number_of_grid_points);\n  Approx local_approx = Approx::custom().epsilon(1e-13).scale(1.0);\n  for (size_t a = 0; a < L; ++a) {\n    CAPTURE(a);\n    for (size_t b = 0; b < L - a; ++b) {\n      CAPTURE(b);\n      for (size_t c = 0; c < L - a - b; ++c) {\n        CAPTURE(c);\n        tmpl::for_each<VariableTags>([&a, &b, &c, &x, &u](auto tag) {\n          using Tag = typename decltype(tag)::type;\n          get<Tag>(u) = Tag::f({{a, b, c}}, x);\n        });\n        tmpl::for_each<GradientTags>([&a, &b, &c, &x, &expected_du](auto tag) {\n          using Tag = typename decltype(tag)::type;\n          using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<3>, Frame::Grid>;\n          get<DerivativeTag>(expected_du) = Tag::df({{a, b, c}}, x);\n        });\n\n        CHECK_VARIABLES_CUSTOM_APPROX(\n            (partial_derivatives<GradientTags>(u, mesh, inverse_jacobian)),\n            expected_du, local_approx);\n        using vars_type = decltype(partial_derivatives<GradientTags>(\n            u, mesh, inverse_jacobian));\n        vars_type du{};\n        partial_derivatives(make_not_null(&du), u, mesh, inverse_jacobian);\n        CHECK_VARIABLES_CUSTOM_APPROX(du, expected_du, local_approx);\n\n        vars_type du_with_logical{};\n        partial_derivatives(make_not_null(&du_with_logical),\n                            logical_partial_derivatives<GradientTags>(u, mesh),\n                            inverse_jacobian);\n        CHECK_VARIABLES_CUSTOM_APPROX(du_with_logical, expected_du,\n                                      local_approx);\n\n        // We've checked that du is correct, now test that taking derivatives of\n        // individual tensors gets the matching result.\n        test_partial_derivative_per_tensor(du, u, mesh, inverse_jacobian);\n      }\n    }\n  }\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_partial_derivatives_disk() {\n  const size_t n_r = 3;\n  const size_t n_phi = 9;\n  const size_t M = n_phi / 2;\n  const Mesh<2> mesh{{n_r, n_phi},\n                     {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2},\n                     {Spectral::Quadrature::GaussRadauUpper,\n                      Spectral::Quadrature::Equiangular}};\n\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const auto prod_map2d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::ProductOf2Maps<\n              Affine, domain::CoordinateMaps::Identity<1>>{\n              Affine{-1.0, 1.0, 0.0, 1.5},\n              domain::CoordinateMaps::Identity<1>{}},\n          domain::CoordinateMaps::PolarToCartesian{});\n  const auto xi = logical_coordinates(mesh);\n  auto x = prod_map2d(xi);\n  const InverseJacobian<DataVector, 2, Frame::ElementLogical, Frame::Grid>\n      inverse_jacobian = prod_map2d.inv_jacobian(xi);\n\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, GradientTags, tmpl::size_t<2>, Frame::Grid>>\n      expected_du(number_of_grid_points);\n  const Approx local_approx = Approx::custom().epsilon(1e-13).scale(1.0);\n  for (size_t a = 0; a <= M; ++a) {\n    CAPTURE(a);\n    for (size_t b = 0; b <= M - a; ++b) {\n      CAPTURE(b);\n      tmpl::for_each<VariableTags>(\n          [&a, &b, &x, &u]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n            get<Tag>(u) = Tag::f({{a, b}}, x);\n          });\n      tmpl::for_each<GradientTags>([&a, &b, &x, &expected_du]<typename Tag>(\n                                       tmpl::type_<Tag> /*meta*/) {\n        using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<2>, Frame::Grid>;\n        get<DerivativeTag>(expected_du) = Tag::df({{a, b}}, x);\n      });\n\n      CHECK_VARIABLES_CUSTOM_APPROX(\n          (partial_derivatives<GradientTags>(u, mesh, inverse_jacobian)),\n          expected_du, local_approx);\n\n      using vars_type = decltype(partial_derivatives<GradientTags>(\n          u, mesh, inverse_jacobian));\n      vars_type du{};\n      partial_derivatives(make_not_null(&du), u, mesh, inverse_jacobian);\n      CHECK_VARIABLES_CUSTOM_APPROX(du, expected_du, local_approx);\n\n      vars_type du_with_logical{};\n      partial_derivatives(make_not_null(&du_with_logical),\n                          logical_partial_derivatives<GradientTags>(u, mesh),\n                          inverse_jacobian);\n      CHECK_VARIABLES_CUSTOM_APPROX(du_with_logical, expected_du, local_approx);\n\n      // We've checked that du is correct, now test that taking derivatives of\n      // individual tensors gets the matching result.\n      test_partial_derivative_per_tensor(du, u, mesh, inverse_jacobian);\n    }\n  }\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_partial_derivatives_cylinder() {\n  const size_t n_r = 3;\n  const size_t n_phi = 9;\n  const size_t n_z = 4;\n  const size_t M = n_phi / 2;\n  const Mesh<3> mesh{\n      {n_r, n_phi, n_z},\n      {Spectral::Basis::ZernikeB2, Spectral::Basis::ZernikeB2,\n       Spectral::Basis::Legendre},\n      {Spectral::Quadrature::GaussRadauUpper, Spectral::Quadrature::Equiangular,\n       Spectral::Quadrature::GaussLobatto}};\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const auto prod_map3d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::ProductOf3Maps<\n              Affine, domain::CoordinateMaps::Identity<1>, Affine>{\n              Affine{-1.0, 1.0, 0.0, 2.5},\n              domain::CoordinateMaps::Identity<1>{},\n              Affine{-1.0, 1.0, -2.0, 2.0}},\n          domain::CoordinateMaps::ProductOf2Maps<\n              domain::CoordinateMaps::PolarToCartesian,\n              domain::CoordinateMaps::Identity<1>>{\n              domain::CoordinateMaps::PolarToCartesian{},\n              domain::CoordinateMaps::Identity<1>{}});\n  const auto xi = logical_coordinates(mesh);\n  auto x = prod_map3d(xi);\n  const InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      inverse_jacobian = prod_map3d.inv_jacobian(xi);\n\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, GradientTags, tmpl::size_t<3>, Frame::Grid>>\n      expected_du(number_of_grid_points);\n  const Approx local_approx = Approx::custom().epsilon(5e-13).scale(1.0);\n  for (size_t a = 0; a <= M; ++a) {\n    CAPTURE(a);\n    for (size_t b = 0; b < M - a; ++b) {\n      CAPTURE(b);\n      for (size_t c = 0; c < n_z; ++c) {\n        CAPTURE(c);\n        tmpl::for_each<VariableTags>(\n            [&a, &b, &c, &x, &u]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n              get<Tag>(u) = Tag::f({{a, b, c}}, x);\n            });\n        tmpl::for_each<GradientTags>([&a, &b, &c, &x,\n                                      &expected_du]<typename Tag>(\n                                         tmpl::type_<Tag> /*meta*/) {\n          using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<3>, Frame::Grid>;\n          get<DerivativeTag>(expected_du) = Tag::df({{a, b, c}}, x);\n        });\n\n        CHECK_VARIABLES_CUSTOM_APPROX(\n            (partial_derivatives<GradientTags>(u, mesh, inverse_jacobian)),\n            expected_du, local_approx);\n\n        using vars_type = decltype(partial_derivatives<GradientTags>(\n            u, mesh, inverse_jacobian));\n        vars_type du{};\n        partial_derivatives(make_not_null(&du), u, mesh, inverse_jacobian);\n        CHECK_VARIABLES_CUSTOM_APPROX(du, expected_du, local_approx);\n\n        vars_type du_with_logical{};\n        partial_derivatives(make_not_null(&du_with_logical),\n                            logical_partial_derivatives<GradientTags>(u, mesh),\n                            inverse_jacobian);\n        CHECK_VARIABLES_CUSTOM_APPROX(du_with_logical, expected_du,\n                                      local_approx);\n\n        // We've checked that du is correct, now test that taking derivatives of\n        // individual tensors gets the matching result.\n        test_partial_derivative_per_tensor(du, u, mesh, inverse_jacobian);\n      }\n    }\n  }\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_partial_derivatives_hollow_cylinder() {\n  const size_t n_r = 4;\n  const size_t n_ph = 9;\n  const size_t n_z = 5;\n  const size_t M = std::min(n_r, n_ph / 2);\n  const Mesh<3> mesh{\n      {n_r, n_ph, n_z},\n      {Spectral::Basis::Legendre, Spectral::Basis::Fourier,\n       Spectral::Basis::Legendre},\n      {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Equiangular,\n       Spectral::Quadrature::GaussLobatto}};\n\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const auto prod_map3d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::ProductOf3Maps<\n              Affine, domain::CoordinateMaps::Identity<1>, Affine>{\n              Affine{-1.0, 1.0, 1.5, 3.0},\n              domain::CoordinateMaps::Identity<1>{},\n              Affine{-1.0, 1.0, -2.0, 1.0}},\n          domain::CoordinateMaps::ProductOf2Maps<\n              domain::CoordinateMaps::PolarToCartesian,\n              domain::CoordinateMaps::Identity<1>>{\n              domain::CoordinateMaps::PolarToCartesian{},\n              domain::CoordinateMaps::Identity<1>{}});\n  const auto xi = logical_coordinates(mesh);\n  auto x = prod_map3d(xi);\n  const InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      inverse_jacobian = prod_map3d.inv_jacobian(xi);\n\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, GradientTags, tmpl::size_t<3>, Frame::Grid>>\n      expected_du(number_of_grid_points);\n  const Approx local_approx = Approx::custom().epsilon(5e-12).scale(1.0);\n  for (size_t a = 0; a <= M; ++a) {\n    CAPTURE(a);\n    for (size_t b = 0; b < M - a; ++b) {\n      CAPTURE(b);\n      for (size_t c = 0; c < n_z; ++c) {\n        CAPTURE(c);\n        tmpl::for_each<VariableTags>(\n            [&a, &b, &c, &x, &u]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n              get<Tag>(u) = Tag::f({{a, b, c}}, x);\n            });\n        tmpl::for_each<GradientTags>([&a, &b, &c, &x,\n                                      &expected_du]<typename Tag>(\n                                         tmpl::type_<Tag> /*meta*/) {\n          using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<3>, Frame::Grid>;\n          get<DerivativeTag>(expected_du) = Tag::df({{a, b, c}}, x);\n        });\n\n        CHECK_VARIABLES_CUSTOM_APPROX(\n            (partial_derivatives<GradientTags>(u, mesh, inverse_jacobian)),\n            expected_du, local_approx);\n\n        using vars_type = decltype(partial_derivatives<GradientTags>(\n            u, mesh, inverse_jacobian));\n        vars_type du{};\n        partial_derivatives(make_not_null(&du), u, mesh, inverse_jacobian);\n        CHECK_VARIABLES_CUSTOM_APPROX(du, expected_du, local_approx);\n\n        vars_type du_with_logical{};\n        partial_derivatives(make_not_null(&du_with_logical),\n                            logical_partial_derivatives<GradientTags>(u, mesh),\n                            inverse_jacobian);\n        CHECK_VARIABLES_CUSTOM_APPROX(du_with_logical, expected_du,\n                                      local_approx);\n\n        // We've checked that du is correct, now test that taking derivatives of\n        // individual tensors gets the matching result.\n        test_partial_derivative_per_tensor(du, u, mesh, inverse_jacobian);\n      }\n    }\n  }\n}\n\ntemplate <bool Spherical>\nDataVector cartoon_func(const tnsr::I<DataVector, 3, Frame::Grid>& coords) {\n  if constexpr (Spherical) {\n    // a radial function, f(r) = f(x) because the computational domain is the x\n    // axis\n    return 0.01 * pow(get<0>(coords), 4) + 0.3 * pow(get<0>(coords), 3) -\n           0.1 * pow(get<0>(coords), 2) - 2.0 * get<0>(coords) - 1.5;\n  } else {\n    // an axially symmetric function about the y axis,\n    // f(\\sqrt{x^2 + z^2}, y) = f(x, y) because the computational domain is the\n    // x-y plane\n    return square(get<1>(coords)) + square(get<0>(coords)) * get<1>(coords);\n  }\n}\n\ntemplate <bool Spherical>\nDataVector cartoon_dfunc(size_t deriv_index,\n                         const tnsr::I<DataVector, 3, Frame::Grid>& coords) {\n  if constexpr (Spherical) {\n    if (deriv_index == 0) {\n      return 0.04 * pow(get<0>(coords), 3) + 0.9 * pow(get<0>(coords), 2) -\n             0.2 * get<0>(coords) - 2.0;\n    } else {\n      return {get<0>(coords).size(), 0.0};\n    }\n  } else {\n    if (deriv_index == 0) {\n      return 2.0 * get<0>(coords) * get<1>(coords);\n    } else if (deriv_index == 1) {\n      return 2.0 * get<1>(coords) + square(get<0>(coords));\n    } else {\n      return {get<0>(coords).size(), 0.0};\n    }\n  }\n}\n\ntemplate <bool Spherical>\nvoid test_cartoon_partial_derivatives(const double x_start) {\n  Mesh<3> mesh;\n  tnsr::I<DataVector, 3, Frame::Grid> coords;\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      inv_jacobian;\n\n  using Identity1D = domain::CoordinateMaps::Identity<1>;\n  const Identity1D identity_cartoon_map;\n\n  if constexpr (Spherical) {\n    // spherical  symmetry\n    const size_t num_grid_pts = 8;\n    const double x_end = 4.0;\n\n    mesh = Mesh<3>{{{num_grid_pts, 1, 1}},\n                   {{Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n                     Spectral::Basis::Cartoon}},\n                   {{Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::SphericalSymmetry,\n                     Spectral::Quadrature::SphericalSymmetry}}};\n\n    const Affine affine_x_map(-1.0, 1.0, x_start, x_end);\n\n    using Cartoon_map_combination =\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Identity1D>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Grid,\n                                Cartoon_map_combination>\n        map{{affine_x_map, identity_cartoon_map, identity_cartoon_map}};\n    inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    coords = map(logical_coordinates(mesh));\n  } else {\n    // axial symmetry\n    const size_t num_x_grid_pts = 6;\n    const double x_end = 3.25;\n    const size_t num_y_grid_pts = 7;\n    const double y_start = -2.5;\n    const double y_end = 4.0;\n\n    mesh = Mesh<3>{{{num_x_grid_pts, num_y_grid_pts, 1}},\n                   {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n                     Spectral::Basis::Cartoon}},\n                   {{Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::AxialSymmetry}}};\n\n    const Affine affine_x_map(-1.0, 1.0, x_start, x_end);\n    const Affine affine_y_map(-1.0, 1.0, y_start, y_end);\n\n    using Cartoon_map_combination =\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity1D>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Grid,\n                                Cartoon_map_combination>\n        map{{affine_x_map, affine_y_map, identity_cartoon_map}};\n    inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    coords = map(logical_coordinates(mesh));\n  }\n\n  using Tempabc =\n      ::Tags::TempTensor<0, tnsr::abc<DataVector, 3, Frame::Inertial>>;\n\n  using VarTags =\n      tmpl::list<::Tags::TempScalar<0>, ::Tags::Tempa<0, 3, Frame::Inertial>,\n                 ::Tags::TempA<0, 3, Frame::Inertial>,\n                 ::Tags::TempAb<0, 3, Frame::Inertial>,\n                 ::Tags::Tempab<0, 3, Frame::Inertial>, Tempabc>;\n\n  Variables<VarTags> vars{mesh.number_of_grid_points()};\n\n  using d_VarTags =\n      tmpl::transform<VarTags, tmpl::bind<::Tags::deriv, tmpl::_1,\n                                          tmpl::size_t<3>, Frame::Grid>>;\n\n  Variables<d_VarTags> expected_d_vars{mesh.number_of_grid_points()};\n\n  // Here we create \"prefactors\", which serve to fill out our tensors by being\n  // multiplied by our cartoon_func(), which themselves make our tensors have\n  // some nontrivial spatial derivative\n  // The point of these prefactors is to ensure the tensors respect the\n  // symmetry of the spacetime: it is not sufficient that each component\n  // follows the symmetry, rather the entire tensor must satisfy\n  // \\mathcal{L}_\\xi (tensor) = 0. Each rank has it's own form of prefactor\n  Variables<VarTags> prefactor_vars{mesh.number_of_grid_points()};\n\n  using TempiA =\n      ::Tags::TempTensor<0, tnsr::iA<DataVector, 3, Frame::Inertial>>;\n  using TempiAb =\n      ::Tags::TempTensor<0, tnsr::iAb<DataVector, 3, Frame::Inertial>>;\n  using Tempiab =\n      ::Tags::TempTensor<0, tnsr::iab<DataVector, 3, Frame::Inertial>>;\n  using Tempiabc =\n      ::Tags::TempTensor<0, TensorMetafunctions::prepend_spatial_index<\n                                tnsr::abc<DataVector, 3, Frame::Inertial>, 3,\n                                UpLo::Lo, Frame::Inertial>>;\n\n  // prepending a spatial index to VarTags (= PrefactorVarTags if it existed)\n  using d_PrefactorVarTags = tmpl::list<::Tags::Tempi<0, 3, Frame::Inertial>,\n                                        ::Tags::Tempia<0, 3, Frame::Inertial>,\n                                        TempiA, TempiAb, Tempiab, Tempiabc>;\n\n  Variables<d_PrefactorVarTags> d_prefactor_vars{mesh.number_of_grid_points()};\n\n  const size_t dv_size = get<0>(coords).size();\n\n  // in the following, the time component of the indices is arbitrarily taken\n  // to be 1\n  auto& scalar = get<::Tags::TempScalar<0>>(prefactor_vars);\n  auto& d_scalar = get<::Tags::Tempi<0, 3>>(d_prefactor_vars);\n  // scalar, doing nothing\n  scalar.get() = DataVector(dv_size, 1.0);\n  // \\partial_i of 1 is zero\n  for (size_t i = 0; i < index_dim<0>(d_scalar); ++i) {\n    d_scalar.get(i) = DataVector(dv_size, 0.0);\n  }\n\n  auto& one_form = get<::Tags::Tempa<0, 3>>(prefactor_vars);\n  auto& d_one_form = get<::Tags::Tempia<0, 3>>(d_prefactor_vars);\n  auto& vector = get<::Tags::TempA<0, 3>>(prefactor_vars);\n  auto& d_vector = get<TempiA>(d_prefactor_vars);\n  if constexpr (Spherical) {\n    // spherical case, vector/one form, using x^a\n    get<0>(vector) = get<0>(one_form) = DataVector(dv_size, 1.0);\n    for (size_t i = 1; i < index_dim<0>(vector); ++i) {\n      vector.get(i) = one_form.get(i) = coords.get(i - 1);\n    }\n    // partial_i of x_a is \\delta_ia\n    for (size_t i = 0; i < index_dim<0>(d_vector); ++i) {\n      for (size_t a = 0; a < index_dim<1>(d_vector); ++a) {\n        if ((i + 1) == a and a != 0) {\n          d_vector.get(i, a) = d_one_form.get(i, a) = DataVector(dv_size, 1.0);\n        } else {\n          d_vector.get(i, a) = d_one_form.get(i, a) = DataVector(dv_size, 0.0);\n        }\n      }\n    }\n  } else {\n    // axial case, vector/one form, using x^a = (0, -z, 0, x) (pure rotation)\n    get<0>(vector) = get<0>(one_form) = DataVector(dv_size, 0.0);\n    get<1>(vector) = get<1>(one_form) = -1.0 * coords.get(2);\n    get<2>(vector) = get<2>(one_form) = DataVector(dv_size, 0.0);\n    get<3>(vector) = get<3>(one_form) = get<0>(coords);\n\n    // \\partial_i of x_a is (0, \\delta_i2, 0, \\delta_i0)\n    for (size_t i = 0; i < index_dim<0>(d_vector); ++i) {\n      for (size_t a = 0; a < index_dim<1>(d_vector); ++a) {\n        if (i == 2 and a == 1) {\n          d_vector.get(i, a) = d_one_form.get(i, a) = DataVector(dv_size, -1.0);\n        } else if (i == 0 and a == 3) {\n          d_vector.get(i, a) = d_one_form.get(i, a) = DataVector(dv_size, 1.0);\n        } else {\n          d_vector.get(i, a) = d_one_form.get(i, a) = DataVector(dv_size, 0.0);\n        }\n      }\n    }\n  }\n\n  auto& mixed = get<::Tags::TempAb<0, 3>>(prefactor_vars);\n  auto& d_mixed = get<TempiAb>(d_prefactor_vars);\n  auto& rank2 = get<::Tags::Tempab<0, 3>>(prefactor_vars);\n  auto& d_rank2 = get<Tempiab>(d_prefactor_vars);\n  // filling space with (essenially) projector to tangent space of sphere\n  // P_ab = \\delta_ab + x_a x_b\n  // can have arbitrary function in from of each term, not doing here\n  // (the real projector is \\delta_ij - x_i x_j / r^2)\n  for (size_t a = 0; a < index_dim<0>(rank2); ++a) {\n    for (size_t b = 0; b < index_dim<1>(rank2); ++b) {\n      if (a == 0 and b == 0) {\n        mixed.get(a, b) = rank2.get(a, b) = DataVector(dv_size, 1.0);\n      } else if (a == 0) {\n        mixed.get(a, b) = rank2.get(a, b) = coords.get(b - 1);\n      } else if (b == 0) {\n        mixed.get(a, b) = rank2.get(a, b) = coords.get(a - 1);\n      } else {\n        if (a == b) {\n          mixed.get(a, b) = rank2.get(a, b) =\n              DataVector(dv_size, 1.0) + coords.get(a - 1) * coords.get(b - 1);\n        } else {\n          mixed.get(a, b) = rank2.get(a, b) =\n              coords.get(a - 1) * coords.get(b - 1);\n        }\n      }\n    }\n  }\n  // \\partial_i of (\\delta_ab + x_a x_b) is (x_a \\delta_ib + x_b \\delta_ia)\n  for (size_t i = 0; i < index_dim<0>(d_rank2); ++i) {\n    for (size_t a = 0; a < index_dim<1>(d_rank2); ++a) {\n      for (size_t b = 0; b < index_dim<2>(d_rank2); ++b) {\n        d_mixed.get(i, a, b) = d_rank2.get(i, a, b) = DataVector(dv_size, 0.0);\n        if ((i + 1) == b) {\n          if (a == 0) {\n            d_mixed.get(i, a, b) += 1.0;\n            d_rank2.get(i, a, b) += 1.0;\n          } else {\n            d_mixed.get(i, a, b) += coords.get(a - 1);\n            d_rank2.get(i, a, b) += coords.get(a - 1);\n          }\n        }\n        if ((i + 1) == a) {\n          if (b == 0) {\n            d_mixed.get(i, a, b) += 1.0;\n            d_rank2.get(i, a, b) += 1.0;\n          } else {\n            d_mixed.get(i, a, b) += coords.get(b - 1);\n            d_rank2.get(i, a, b) += coords.get(b - 1);\n          }\n        }\n      }\n    }\n  }\n\n  auto& rank3 = get<Tempabc>(prefactor_vars);\n  auto& d_rank3 = get<Tempiabc>(d_prefactor_vars);\n  // Rank 3 is important because of \\Phi_iab, the spatial derivative of the\n  // metric, which though not a tensor mathematically still must obey\n  // \\mathcal{L}_\\xi \\Phi_{iab} = 0\n  // Using the form\n  // x_a x_b x_c + \\delta_ab x_c \\delta_ac x_b \\delta_bc x_a\n  for (size_t a = 0; a < index_dim<0>(rank3); ++a) {\n    for (size_t b = 0; b < index_dim<1>(rank3); ++b) {\n      for (size_t c = 0; c < index_dim<2>(rank3); ++c) {\n        rank3.get(a, b, c) =\n            (a == 0 ? DataVector(dv_size, 1.0) : coords.get(a - 1)) *\n            (b == 0 ? DataVector(dv_size, 1.0) : coords.get(b - 1)) *\n            (c == 0 ? DataVector(dv_size, 1.0) : coords.get(c - 1));\n        if (a == b) {\n          rank3.get(a, b, c) +=\n              (c == 0 ? DataVector(dv_size, 1.0) : coords.get(c - 1));\n        }\n        if (a == c) {\n          rank3.get(a, b, c) +=\n              (b == 0 ? DataVector(dv_size, 1.0) : coords.get(b - 1));\n        }\n        if (b == c) {\n          rank3.get(a, b, c) +=\n              (a == 0 ? DataVector(dv_size, 1.0) : coords.get(a - 1));\n        }\n      }\n    }\n  }\n  // \\partial_i of (x_a x_b x_c + \\delta_ab x_c \\delta_ac x_b \\delta_bc x_a) is\n  // \\delta_ia x_b x_c + \\delta_ib x_a x_c + \\delta_ic x_a x_b +\n  //   \\delta_ab \\delta_ic + \\delta_ac \\delta_ib + \\delta_bc \\delta_ia\n  for (size_t i = 0; i < index_dim<0>(d_rank3); ++i) {\n    for (size_t a = 0; a < index_dim<1>(d_rank3); ++a) {\n      for (size_t b = 0; b < index_dim<2>(d_rank3); ++b) {\n        for (size_t c = 0; c < index_dim<3>(d_rank3); ++c) {\n          d_rank3.get(i, a, b, c) = DataVector(dv_size, 0.0);\n          if ((i + 1) == a) {\n            d_rank3.get(i, a, b, c) +=\n                (b == 0 ? DataVector(dv_size, 1.0) : coords.get(b - 1)) *\n                (c == 0 ? DataVector(dv_size, 1.0) : coords.get(c - 1));\n          }\n          if ((i + 1) == b) {\n            d_rank3.get(i, a, b, c) +=\n                (a == 0 ? DataVector(dv_size, 1.0) : coords.get(a - 1)) *\n                (c == 0 ? DataVector(dv_size, 1.0) : coords.get(c - 1));\n          }\n          if ((i + 1) == c) {\n            d_rank3.get(i, a, b, c) +=\n                (a == 0 ? DataVector(dv_size, 1.0) : coords.get(a - 1)) *\n                (b == 0 ? DataVector(dv_size, 1.0) : coords.get(b - 1));\n          }\n          if (a == b and (i + 1) == c) {\n            d_rank3.get(i, a, b, c) += 1.0;\n          }\n          if (a == c and (i + 1) == b) {\n            d_rank3.get(i, a, b, c) += 1.0;\n          }\n          if (b == c and (i + 1) == a) {\n            d_rank3.get(i, a, b, c) += 1.0;\n          }\n        }\n      }\n    }\n  }\n\n  tmpl::for_each<VarTags>([&vars, &prefactor_vars, &expected_d_vars,\n                           &d_prefactor_vars, &coords]<typename tensor_tag>(\n                              tmpl::type_<tensor_tag> /*meta*/) {\n    auto& tensor = get<tensor_tag>(vars);\n    const auto& prefactor_tensor = get<tensor_tag>(prefactor_vars);\n    using deriv_tag = tmpl::apply<\n        tmpl::bind<::Tags::deriv, tmpl::_1, tmpl::size_t<3>, Frame::Grid>,\n        tensor_tag>;\n    auto& d_tensor = get<deriv_tag>(expected_d_vars);\n    const auto& d_prefactor_tensor =\n        get<tmpl::at<d_PrefactorVarTags, tmpl::index_of<VarTags, tensor_tag>>>(\n            d_prefactor_vars);\n\n    for (size_t storage_index = 0; storage_index < tensor.size();\n         ++storage_index) {\n      tensor[storage_index] =\n          prefactor_tensor[storage_index] * cartoon_func<Spherical>(coords);\n\n      const auto input_index = tensor.get_tensor_index(storage_index);\n      for (size_t deriv_index = 0; deriv_index < 3; ++deriv_index) {\n        const auto output_index = prepend(input_index, size_t{deriv_index});\n\n        d_tensor.get(output_index) =\n            prefactor_tensor.get(input_index) *\n                cartoon_dfunc<Spherical>(deriv_index, coords) +\n            cartoon_func<Spherical>(coords) *\n                d_prefactor_tensor.get(output_index);\n      }\n    }\n  });\n\n  const auto to_inertial =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<3>>{};\n  Variables<d_VarTags> d_vars{mesh.number_of_grid_points()};\n  cartoon_partial_derivatives(make_not_null(&d_vars), vars, mesh, inv_jacobian,\n                              to_inertial(coords));\n  // `d_vars` holds our partial derivatives we want to test against the truth\n  const Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(d_vars, expected_d_vars, local_approx);\n}\n\nvoid test_cartoon_partial_derivative_and_choosers() {\n  // here, \"chooser\" means the partial_derivative()/parital_derivatives()\n  // functions that take inertial_coords and then pick the appropriate\n  // cartoon/non-cartoon implementation\n  // Testing partial_derivatives chooser, i.e. verifying it calls the correct\n  // underlying implemenation (unfortunately a lot of set up code)\n  // With cartoon basis\n  using Identity1D = domain::CoordinateMaps::Identity<1>;\n  const Identity1D identity_cartoon_map;\n\n  const Mesh<3> mesh_cartoon{\n      {{8, 1, 1}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n        Spectral::Basis::Cartoon}},\n      {{Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::SphericalSymmetry,\n        Spectral::Quadrature::SphericalSymmetry}}};\n\n  const Affine affine_x_map(-1.0, 1.0, 1.0, 4.0);\n\n  using Cartoon_map_combination =\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Identity1D>;\n  const domain::CoordinateMap<Frame::ElementLogical, Frame::Grid,\n                              Cartoon_map_combination>\n      map{{affine_x_map, identity_cartoon_map, identity_cartoon_map}};\n  const auto inv_jacobian = map.inv_jacobian(logical_coordinates(mesh_cartoon));\n  const auto coords = map(logical_coordinates(mesh_cartoon));\n\n  Scalar<DataVector> tnsr_cartoon{};\n  get<>(tnsr_cartoon) = cartoon_func<true>(coords);\n  TensorMetafunctions::prepend_spatial_index<Scalar<DataVector>, 3, UpLo::Lo,\n                                             Frame::Grid>\n      expected_d_tnsr_cartoon{};\n  for (size_t i = 0; i < index_dim<0>(expected_d_tnsr_cartoon); ++i) {\n    expected_d_tnsr_cartoon.get(i) = cartoon_dfunc<true>(i, coords);\n  }\n\n  const auto to_inertial =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<3>>{};\n  TensorMetafunctions::prepend_spatial_index<Scalar<DataVector>, 3, UpLo::Lo,\n                                             Frame::Grid>\n      d_tnsr_cartoon{};\n  using u_type = Scalar<DataVector>;\n  using du_type =\n      TensorMetafunctions::prepend_spatial_index<u_type, 3, UpLo::Lo,\n                                                 Frame::Grid>;\n  using CartoonTags = ::Tags::convert_to_temp_tensors<tmpl::list<u_type>, 0>;\n  using d_CartoonTags = ::Tags::convert_to_temp_tensors<tmpl::list<du_type>, 0>;\n  Variables<CartoonTags> u_cartoon{mesh_cartoon.number_of_grid_points()};\n  get<tmpl::front<CartoonTags>>(u_cartoon) = tnsr_cartoon;\n  Variables<d_CartoonTags> d_u_cartoon{mesh_cartoon.number_of_grid_points()};\n  partial_derivatives(make_not_null(&d_u_cartoon), u_cartoon, mesh_cartoon,\n                      inv_jacobian, to_inertial(coords));\n  Variables<d_CartoonTags> expected_d_u_cartoon{\n      mesh_cartoon.number_of_grid_points()};\n  get<tmpl::front<d_CartoonTags>>(expected_d_u_cartoon) =\n      expected_d_tnsr_cartoon;\n  const Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(d_u_cartoon, expected_d_u_cartoon,\n                                local_approx);\n\n  // With non-cartoon basis\n  const Mesh<3> mesh_lgl{{{3, 4, 5}},\n                         Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto};\n  using VariableTags = two_vars<DataVector, 3>;\n  using GradientTags = two_vars<DataVector, 3>;\n  const size_t number_of_grid_points_lgl = mesh_lgl.number_of_grid_points();\n  const auto prod_map_lgl =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          Affine3D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n                   Affine{-1.0, 1.0, 2.3, 2.8}});\n  const auto x = prod_map_lgl(logical_coordinates(mesh_lgl));\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      inv_jacobian_lgl(number_of_grid_points_lgl, 0.0);\n  inv_jacobian_lgl.get(0, 0) = 2.0;\n  inv_jacobian_lgl.get(1, 1) = 8.0;\n  inv_jacobian_lgl.get(2, 2) = 4.0;\n\n  Variables<VariableTags> u_lgl(number_of_grid_points_lgl);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, GradientTags, tmpl::size_t<3>, Frame::Grid>>\n      expected_d_u_lgl(number_of_grid_points_lgl);\n  for (size_t a = 0; a < mesh_lgl.extents(0) / 2; ++a) {\n    for (size_t b = 0; b < mesh_lgl.extents(1) / 2; ++b) {\n      for (size_t c = 0; c < mesh_lgl.extents(2) / 2; ++c) {\n        tmpl::for_each<VariableTags>([&a, &b, &c, &x, &u_lgl](auto tag) {\n          using Tag = typename decltype(tag)::type;\n          get<Tag>(u_lgl) = Tag::f({{a, b, c}}, x);\n        });\n        tmpl::for_each<GradientTags>([&a, &b, &c, &x,\n                                      &expected_d_u_lgl](auto tag) {\n          using Tag = typename decltype(tag)::type;\n          using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<3>, Frame::Grid>;\n          get<DerivativeTag>(expected_d_u_lgl) = Tag::df({{a, b, c}}, x);\n        });\n\n        using vars_type = decltype(partial_derivatives<GradientTags>(\n            u_lgl, mesh_lgl, inv_jacobian_lgl));\n        vars_type d_u_lgl{};\n        partial_derivatives(make_not_null(&d_u_lgl), u_lgl, mesh_lgl,\n                            inv_jacobian_lgl, to_inertial(x));\n        CHECK_VARIABLES_CUSTOM_APPROX(d_u_lgl, expected_d_u_lgl, local_approx);\n      }\n    }\n  }\n  // Testing cartoon_partial_derivative() through associated choosers\n  // With Cartoon basis\n  partial_derivative(make_not_null(&d_tnsr_cartoon), tnsr_cartoon, mesh_cartoon,\n                     inv_jacobian, to_inertial(coords));\n  CHECK_ITERABLE_CUSTOM_APPROX(d_tnsr_cartoon, expected_d_tnsr_cartoon,\n                               local_approx);\n\n  d_tnsr_cartoon = partial_derivative(tnsr_cartoon, mesh_cartoon, inv_jacobian,\n                                      to_inertial(coords));\n  CHECK_ITERABLE_CUSTOM_APPROX(d_tnsr_cartoon, expected_d_tnsr_cartoon,\n                               local_approx);\n\n  // With non-cartoon basis\n  auto& tnsr_lgl = get<tmpl::front<VariableTags>>(u_lgl);\n  using d_tnsr_lgl_tag =\n      Tags::deriv<tmpl::front<GradientTags>, tmpl::size_t<3>, Frame::Grid>;\n  d_tnsr_lgl_tag::type d_tnsr_lgl{};\n  auto& expected_d_tnsr_lgl = get<d_tnsr_lgl_tag>(expected_d_u_lgl);\n  partial_derivative(make_not_null(&d_tnsr_lgl), tnsr_lgl, mesh_lgl,\n                     inv_jacobian_lgl, to_inertial(x));\n  CHECK_ITERABLE_CUSTOM_APPROX(d_tnsr_lgl, expected_d_tnsr_lgl, local_approx);\n\n  d_tnsr_lgl = partial_derivative(tnsr_lgl, mesh_lgl, inv_jacobian_lgl,\n                                  to_inertial(coords));\n  CHECK_ITERABLE_CUSTOM_APPROX(d_tnsr_lgl, expected_d_tnsr_lgl, local_approx);\n}\n\ntemplate <typename VariableTags, typename GradientTags = VariableTags>\nvoid test_logical_partial_derivatives_zernikeb1_1d(const size_t n_r) {\n  CAPTURE(n_r);\n  const Mesh<1> mesh{n_r, Spectral::Basis::ZernikeB1,\n                     Spectral::Quadrature::GaussRadauUpper};\n\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n  const DataVector radius =\n      0.5 * (Spectral::collocation_points(mesh.slice_through(0)) + 1.0);\n\n  // Get the parity information from the existing framework\n  constexpr auto parity_info = Spectral::compute_parity_list<VariableTags>();\n  const auto parity_list = std::get<0>(parity_info);\n\n  Variables<VariableTags> u(number_of_grid_points);\n  Variables<GradientTags> du_expected(number_of_grid_points);\n\n  // Fill tensor components according to their parity requirements\n  // Each component gets a different power based on its parity\n  size_t component_index = 0;\n  bool current_parity_is_even = true;\n  size_t parity_list_index = 0;\n  size_t even_power_counter = 0;\n  size_t odd_power_counter = 1;          // Start with r^1 for odd components\n  std::vector<size_t> component_powers;  // Store powers for derivatives later\n\n  const auto need_to_switch_parity = [component_index, &parity_list,\n                                      parity_list_index]() {\n    return component_index >= gsl::at(parity_list, parity_list_index) and\n           parity_list_index < parity_list.size() - 1 and\n           gsl::at(parity_list, parity_list_index + 1) > 0;\n  };\n\n  tmpl::for_each<VariableTags>([&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n    auto& tensor = get<Tag>(u);\n\n    for (size_t i = 0; i < tensor.size(); ++i) {\n      // Check if we need to switch parity based on the parity list\n      if (need_to_switch_parity()) {\n        component_index = 0;\n        ++parity_list_index;\n        current_parity_is_even = not current_parity_is_even;\n      }\n\n      // Choose power based on component parity, incrementing for each component\n      size_t power{};\n      if (current_parity_is_even) {\n        power = even_power_counter;\n        even_power_counter += 2;\n      } else {\n        power = odd_power_counter;\n        odd_power_counter += 2;\n      }\n      component_powers.push_back(power);\n\n      tensor[i] = pow(radius, static_cast<double>(power));\n      ++component_index;\n    }\n  });\n\n  // Compute expected derivatives\n  component_index = 0;\n  parity_list_index = 0;\n  size_t power_index = 0;\n\n  tmpl::for_each<GradientTags>([&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n    auto& deriv_tensor = get<Tag>(du_expected);\n\n    for (size_t i = 0; i < deriv_tensor.size(); ++i) {\n      if (need_to_switch_parity()) {\n        component_index = 0;\n        ++parity_list_index;\n      }\n\n      const size_t power = component_powers[power_index++];\n\n      // Compute analytical derivative: d/dxi(r^n) = dr/dxi * n * r^(n-1)\n      // where dr/dxi = 0.5 (from radius = 0.5*(xi + 1))\n      if (power == 0) {\n        deriv_tensor[i] = 0.0;\n      } else {\n        deriv_tensor[i] = 0.5 * static_cast<double>(power) *\n                          pow(radius, static_cast<double>(power - 1));\n      }\n      ++component_index;\n    }\n  });\n\n  // Compute actual derivatives\n  const auto du = logical_partial_derivatives<GradientTags>(u, mesh);\n\n  const Approx local_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(du[0], du_expected, local_approx);\n}\n\ntemplate <bool Spherical, typename VariableTags,\n          typename GradientTags = VariableTags>\nvoid test_partial_derivatives_zernikeb1_cartoon(const size_t n_x) {\n  CAPTURE(n_x, Spherical);\n\n  Mesh<3> mesh;\n  tnsr::I<DataVector, 3, Frame::Grid> coords;\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Grid>\n      inv_jacobian;\n\n  using Identity1D = domain::CoordinateMaps::Identity<1>;\n  const Identity1D identity_cartoon_map;\n\n  if constexpr (Spherical) {\n    // Spherical symmetry: ZernikeB1 + Cartoon + Cartoon\n    mesh = Mesh<3>{{{n_x, 1, 1}},\n                   {{Spectral::Basis::ZernikeB1, Spectral::Basis::Cartoon,\n                     Spectral::Basis::Cartoon}},\n                   {{Spectral::Quadrature::GaussRadauUpper,\n                     Spectral::Quadrature::SphericalSymmetry,\n                     Spectral::Quadrature::SphericalSymmetry}}};\n\n    const Affine affine_x_map(-1.0, 1.0, 0.0, 2.0);\n    using Cartoon_map_combination =\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Identity1D>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Grid,\n                                Cartoon_map_combination>\n        map{{affine_x_map, identity_cartoon_map, identity_cartoon_map}};\n    inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    coords = map(logical_coordinates(mesh));\n  } else {\n    // Axial symmetry: ZernikeB1 + Legendre + Cartoon\n    mesh = Mesh<3>{{{n_x, 5, 1}},\n                   {{Spectral::Basis::ZernikeB1, Spectral::Basis::Legendre,\n                     Spectral::Basis::Cartoon}},\n                   {{Spectral::Quadrature::GaussRadauUpper,\n                     Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::AxialSymmetry}}};\n\n    const Affine affine_x_map(-1.0, 1.0, 0.0, 2.0);\n    const Affine affine_y_map(-1.0, 1.0, -1.5, 2.0);\n    using Cartoon_map_combination =\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity1D>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Grid,\n                                Cartoon_map_combination>\n        map{{affine_x_map, affine_y_map, identity_cartoon_map}};\n    inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    coords = map(logical_coordinates(mesh));\n  }\n\n  const size_t number_of_grid_points = mesh.number_of_grid_points();\n\n  using d_VarTags =\n      tmpl::transform<GradientTags, tmpl::bind<::Tags::deriv, tmpl::_1,\n                                               tmpl::size_t<3>, Frame::Grid>>;\n\n  Variables<VariableTags> vars{number_of_grid_points};\n  Variables<d_VarTags> expected_d_vars{number_of_grid_points};\n\n  const DataVector& x_coord = get<0>(coords);\n  const DataVector& y_coord = get<1>(coords);\n  const DataVector& z_coord = get<2>(coords);\n\n  const DataVector r_coord =\n      sqrt(x_coord * x_coord + y_coord * y_coord + z_coord * z_coord);\n\n  // Fill tensors with test functions that respect ZernikeB1 parity\n  // Use f(r) = r^2 which has even parity (good for ZernikeB1)\n  // For vectors, multiply by coordinate prefactors that respect cartoon\n  // symmetry\n  tmpl::for_each<VariableTags>(\n      [&vars, &r_coord, &x_coord, &y_coord, &z_coord,\n       &number_of_grid_points]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        using TensorType = typename Tag::type;\n        auto& tensor = get<Tag>(vars);\n\n        if constexpr (TensorType::rank() == 0) {\n          // Scalar: f(r) = r^2 (even parity)\n          tensor.get() = r_coord * r_coord;\n          (void)x_coord;\n          (void)y_coord;\n          (void)number_of_grid_points;\n        } else if constexpr (TensorType::rank() == 1) {\n          // Vector: multiply by coordinate prefactors\n          if constexpr (TensorType::index_dim(0) == 4) {\n            // Spacetime vector\n            if constexpr (Spherical) {\n              // Spherical case: (1, x, y, z) * r^2\n              get<0>(tensor) = r_coord * r_coord;\n              get<1>(tensor) = x_coord * r_coord * r_coord;\n              get<2>(tensor) = y_coord * r_coord * r_coord;\n              get<3>(tensor) = z_coord * r_coord * r_coord;\n            } else {\n              // Axial case: (1, x, 1, z) * r^2 - matches spatial pattern with\n              // time component\n              get<0>(tensor) = r_coord * r_coord;\n              get<1>(tensor) = x_coord * r_coord * r_coord;\n              get<2>(tensor) = r_coord * r_coord;\n              get<3>(tensor) = z_coord * r_coord * r_coord;\n            }\n          } else {\n            // Spatial vector (3 components)\n            if constexpr (Spherical) {\n              // Spherical case: (x, y, z) * r^2\n              get<0>(tensor) = x_coord * r_coord * r_coord;\n              get<1>(tensor) = y_coord * r_coord * r_coord;\n              get<2>(tensor) = z_coord * r_coord * r_coord;\n            } else {\n              // Axial case, we need: (odd, even, odd) components\n              get<0>(tensor) = x_coord * r_coord * r_coord;\n              get<1>(tensor) = r_coord * r_coord;\n              get<2>(tensor) = z_coord * r_coord * r_coord;\n            }\n          }\n        } else {\n          ERROR(\"Higher rank tensors not yet supported in this test\");\n        }\n      });\n\n  // Compute expected derivatives analytically for the test functions\n  tmpl::for_each<GradientTags>(\n      [&expected_d_vars, &r_coord, &number_of_grid_points, &x_coord, &y_coord,\n       &z_coord]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        using TensorType = typename Tag::type;\n        using DerivTag = ::Tags::deriv<Tag, tmpl::size_t<3>, Frame::Grid>;\n        auto& d_tensor = get<DerivTag>(expected_d_vars);\n\n        if constexpr (TensorType::rank() == 0) {\n          // Scalar: f(r) = r^2, so df/dx_i = 2r * dr/dx_i = 2r * x_i/r = 2*x_i\n          for (size_t deriv_dir = 0; deriv_dir < 3; ++deriv_dir) {\n            if (deriv_dir == 0) {\n              d_tensor.get(deriv_dir) = 2.0 * x_coord;\n            } else if (deriv_dir == 1) {\n              d_tensor.get(deriv_dir) = 2.0 * y_coord;\n            } else {\n              d_tensor.get(deriv_dir) = 2.0 * z_coord;\n            }\n          }\n        } else if constexpr (TensorType::rank() == 1) {\n          // Vector: components are prefactor * r^2\n          // d(prefactor * r^2)/dx_i = d(prefactor)/dx_i * r^2 + prefactor *\n          // d(r^2)/dx_i where d(r^2)/dx_i = 2*x_i\n          for (size_t component_index = 0; component_index < TensorType::size();\n               ++component_index) {\n            const auto tensor_index =\n                TensorType::get_tensor_index(component_index);\n\n            for (size_t deriv_dir = 0; deriv_dir < 3; ++deriv_dir) {\n              const auto deriv_tensor_index = prepend(tensor_index, deriv_dir);\n\n              // Get prefactor and its derivative\n              DataVector prefactor(number_of_grid_points, 1.0);\n              DataVector prefactor_deriv(number_of_grid_points, 0.0);\n\n              const auto deriv_of_coord = [number_of_grid_points](\n                                              const size_t coord_index,\n                                              const size_t deriv_index) {\n                return DataVector(number_of_grid_points,\n                                  deriv_index == coord_index ? 1.0 : 0.0);\n              };\n\n              if constexpr (TensorType::index_dim(0) == 4) {\n                // Spacetime vector\n                if constexpr (Spherical) {\n                  if (gsl::at(tensor_index, 0) == 0) {\n                    prefactor = DataVector(number_of_grid_points, 1.0);\n                    prefactor_deriv = DataVector(number_of_grid_points, 0.0);\n                  } else if (gsl::at(tensor_index, 0) == 1) {\n                    prefactor = x_coord;\n                    prefactor_deriv = deriv_of_coord(0, deriv_dir);\n                  } else if (gsl::at(tensor_index, 0) == 2) {\n                    prefactor = y_coord;\n                    prefactor_deriv = deriv_of_coord(1, deriv_dir);\n                  } else {\n                    prefactor = z_coord;\n                    prefactor_deriv = deriv_of_coord(2, deriv_dir);\n                  }\n                } else {\n                  // Axial case: (1, x, 1, z) * r^2\n                  if (gsl::at(tensor_index, 0) == 0 or\n                      gsl::at(tensor_index, 0) == 2) {\n                    prefactor = DataVector(number_of_grid_points, 1.0);\n                    prefactor_deriv = DataVector(number_of_grid_points, 0.0);\n                  } else if (gsl::at(tensor_index, 0) == 1) {\n                    prefactor = x_coord;\n                    prefactor_deriv = deriv_of_coord(0, deriv_dir);\n                  } else {\n                    prefactor = z_coord;\n                    prefactor_deriv = deriv_of_coord(2, deriv_dir);\n                  }\n                }\n              } else {\n                // Spatial vector (3 components)\n                if constexpr (Spherical) {\n                  if (gsl::at(tensor_index, 0) == 0) {\n                    prefactor = x_coord;\n                    prefactor_deriv = deriv_of_coord(0, deriv_dir);\n                  } else if (gsl::at(tensor_index, 0) == 1) {\n                    prefactor = y_coord;\n                    prefactor_deriv = deriv_of_coord(1, deriv_dir);\n                  } else {\n                    prefactor = z_coord;\n                    prefactor_deriv = deriv_of_coord(2, deriv_dir);\n                  }\n                } else {\n                  // Axial case: (x, 1, z) * r^2\n                  if (gsl::at(tensor_index, 0) == 0) {\n                    prefactor = x_coord;\n                    prefactor_deriv = deriv_of_coord(0, deriv_dir);\n                  } else if (gsl::at(tensor_index, 0) == 1) {\n                    prefactor = DataVector(number_of_grid_points, 1.0);\n                    prefactor_deriv = DataVector(number_of_grid_points, 0.0);\n                  } else {\n                    prefactor = z_coord;\n                    prefactor_deriv = deriv_of_coord(2, deriv_dir);\n                  }\n                }\n              }\n              // Apply product rule: d(prefactor * r^2)/dx_i = d(prefactor)/dx_i\n              // * r^2 + prefactor * d(r^2)/dx_i\n              DataVector r_squared_deriv(number_of_grid_points);\n              if (deriv_dir == 0) {\n                r_squared_deriv = 2.0 * x_coord;\n              } else if (deriv_dir == 1) {\n                r_squared_deriv = 2.0 * y_coord;\n              } else {\n                r_squared_deriv = 2.0 * z_coord;\n              }\n              d_tensor.get(deriv_tensor_index) =\n                  prefactor_deriv * (r_coord * r_coord) +\n                  prefactor * r_squared_deriv;\n            }\n          }\n        } else {\n          ERROR(\"Higher rank tensors not yet supported in this test\");\n        }\n      });\n\n  const auto to_inertial =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<3>>{};\n\n  Variables<d_VarTags> computed_d_vars{number_of_grid_points};\n  cartoon_partial_derivatives(make_not_null(&computed_d_vars), vars, mesh,\n                              inv_jacobian, to_inertial(coords));\n\n  const Approx local_approx = Approx::custom().epsilon(1e-12).scale(1.0);\n  CHECK_VARIABLES_CUSTOM_APPROX(computed_d_vars, expected_d_vars, local_approx);\n}\n}  // namespace\n\n// [[Timeout, 60]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.LogicalDerivs\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  constexpr size_t min_points =\n      Spectral::minimum_number_of_points<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>;\n  constexpr size_t max_points =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2;\n  for (size_t n0 = min_points; n0 <= max_points; ++n0) {\n    // To keep test time reasonable we don't check all possible values.\n    if (n0 > 6 and n0 != max_points) {\n      continue;\n    }\n    const Mesh<1> mesh_1d{n0, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n    test_logical_partial_derivatives_1d<two_vars<DataVector, 1>>(mesh_1d);\n    test_logical_partial_derivatives_1d<two_vars<DataVector, 1>,\n                                        one_var<DataVector, 1>>(mesh_1d);\n    test_logical_partial_derivatives_1d<two_vars<ComplexDataVector, 1>>(\n        mesh_1d);\n    test_logical_partial_derivatives_1d<two_vars<ComplexDataVector, 1>,\n                                        one_var<ComplexDataVector, 1>>(mesh_1d);\n    for (size_t n1 = min_points; n1 <= max_points; ++n1) {\n      // To keep test time reasonable we don't check all possible values.\n      if (n1 > 6 and n1 != max_points) {\n        continue;\n      }\n      const Mesh<2> mesh_2d{{{n0, n1}},\n                            Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n      test_logical_partial_derivatives_2d<two_vars<DataVector, 2>>(mesh_2d);\n      test_logical_partial_derivatives_2d<two_vars<DataVector, 2>,\n                                          one_var<DataVector, 2>>(mesh_2d);\n      test_logical_partial_derivatives_2d<two_vars<ComplexDataVector, 2>>(\n          mesh_2d);\n      test_logical_partial_derivatives_2d<two_vars<ComplexDataVector, 2>,\n                                          one_var<ComplexDataVector, 2>>(\n          mesh_2d);\n      for (size_t n2 = min_points; n2 <= max_points; ++n2) {\n        // To keep test time reasonable we don't check all possible values.\n        if (n2 > 6 and n2 != max_points) {\n          continue;\n        }\n        const Mesh<3> mesh_3d{{{n0, n1, n2}},\n                              Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n        test_logical_partial_derivatives_3d<two_vars<DataVector, 3>>(mesh_3d);\n        test_logical_partial_derivatives_3d<two_vars<DataVector, 3>,\n                                            one_var<DataVector, 3>>(mesh_3d);\n        test_logical_partial_derivatives_3d<two_vars<ComplexDataVector, 3>>(\n            mesh_3d);\n        test_logical_partial_derivatives_3d<two_vars<ComplexDataVector, 3>,\n                                            one_var<ComplexDataVector, 3>>(\n            mesh_3d);\n      }\n    }\n  }\n  for (size_t n_r = 1; n_r < 5; ++n_r) {\n    for (size_t L = 2; L < 5; ++L) {\n      test_logical_partial_derivatives_spherical_shell<0, 0,\n                                                       two_vars<DataVector, 3>>(\n          n_r, L);\n      test_logical_partial_derivatives_spherical_shell<1, 0,\n                                                       two_vars<DataVector, 3>>(\n          n_r, L);\n      test_logical_partial_derivatives_spherical_shell<1, 1,\n                                                       two_vars<DataVector, 3>>(\n          n_r, L);\n      test_logical_partial_derivatives_spherical_shell<1, -1,\n                                                       two_vars<DataVector, 3>>(\n          n_r, L);\n      if (L > 2) {\n        test_logical_partial_derivatives_spherical_shell<\n            2, 0, two_vars<DataVector, 3>>(n_r, L);\n        test_logical_partial_derivatives_spherical_shell<\n            2, 1, two_vars<DataVector, 3>>(n_r, L);\n        test_logical_partial_derivatives_spherical_shell<\n            2, -1, two_vars<DataVector, 3>>(n_r, L);\n        test_logical_partial_derivatives_spherical_shell<\n            2, 2, two_vars<DataVector, 3>>(n_r, L);\n        test_logical_partial_derivatives_spherical_shell<\n            2, -2, two_vars<DataVector, 3>>(n_r, L);\n      }\n    }\n  }\n  for (size_t n_r = 2; n_r < 9; ++n_r) {\n    for (size_t n_phi = 3; n_phi < 15; n_phi += 2) {\n      // Restrictions enforced in the logical derivative\n      if (n_phi / 2 <= 2 * n_r - 2) {\n        test_logical_partial_derivatives_disk<two_vars<DataVector, 2>>(n_r,\n                                                                       n_phi);\n        for (size_t n_zeta = 2; n_zeta < 8; ++n_zeta) {\n          test_logical_partial_derivatives_cylinder<two_vars<DataVector, 3>>(\n              n_r, n_phi, n_zeta);\n        }\n      }\n    }\n  }\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      (test_logical_partial_derivatives_disk<two_vars<DataVector, 2>>(2, 7)),\n      Catch::Matchers::ContainsSubstring(\n          \"Zernike & Fourier on a disk have angular resolution limited by \"));\n  CHECK_THROWS_WITH(\n      (test_logical_partial_derivatives_disk<two_vars<DataVector, 2>>(2, 4)),\n      Catch::Matchers::ContainsSubstring(\n          \"Fourier with an even number of grid points can be unstable due \"));\n  CHECK_THROWS_WITH(\n      (test_logical_partial_derivatives_cylinder<two_vars<DataVector, 3>>(2, 7,\n                                                                          3)),\n      Catch::Matchers::ContainsSubstring(\n          \"Zernike & Fourier on a disk have angular resolution limited by \"));\n  CHECK_THROWS_WITH(\n      (test_logical_partial_derivatives_cylinder<two_vars<DataVector, 3>>(2, 4,\n                                                                          3)),\n      Catch::Matchers::ContainsSubstring(\n          \"Fourier with an even number of grid points can be unstable due \"));\n#endif  // SPECTRE_DEBUG\n\n  for (size_t n_r = 3; n_r <= 8; ++n_r) {\n    test_logical_partial_derivatives_zernikeb1_1d<two_vars<DataVector, 1>>(n_r);\n    test_logical_partial_derivatives_zernikeb1_1d<two_vars<DataVector, 1>,\n                                                  one_var<DataVector, 1>>(n_r);\n    test_logical_partial_derivatives_zernikeb1_1d<scalar_var<DataVector>>(n_r);\n  }\n}\n\n// [[Timeout, 120]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.PartialDerivs\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  test_partial_derivatives_spherical_shell<two_vars<DataVector, 3>>();\n  test_partial_derivatives_spherical_shell<two_vars<DataVector, 3>,\n                                           one_var<DataVector, 3>>();\n  test_partial_derivatives_disk<two_vars<DataVector, 2>>();\n  test_partial_derivatives_disk<two_vars<DataVector, 2>,\n                                one_var<DataVector, 2>>();\n  test_partial_derivatives_cylinder<two_vars<DataVector, 3>>();\n  test_partial_derivatives_cylinder<two_vars<DataVector, 3>,\n                                    one_var<DataVector, 3>>();\n  test_partial_derivatives_hollow_cylinder<two_vars<DataVector, 3>>();\n  test_partial_derivatives_hollow_cylinder<two_vars<DataVector, 3>,\n                                           one_var<DataVector, 3>>();\n  const size_t n0 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2;\n  const size_t n1 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2 + 1;\n  const size_t n2 =\n      Spectral::maximum_number_of_points<Spectral::Basis::Legendre> / 2 - 1;\n  const Mesh<1> mesh_1d{n0, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  test_partial_derivatives_1d<two_vars<DataVector, 1>>(mesh_1d);\n  test_partial_derivatives_1d<two_vars<DataVector, 1>, one_var<DataVector, 1>>(\n      mesh_1d);\n  test_partial_derivatives_1d<two_vars<ComplexDataVector, 1>>(mesh_1d);\n  const Mesh<2> mesh_2d{{{n0, n1}},\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  test_partial_derivatives_2d<two_vars<DataVector, 2>>(mesh_2d);\n  test_partial_derivatives_2d<two_vars<DataVector, 2>, one_var<DataVector, 2>>(\n      mesh_2d);\n  test_partial_derivatives_2d<two_vars<ComplexDataVector, 2>,\n                              one_var<ComplexDataVector, 2>>(mesh_2d);\n  const Mesh<3> mesh_3d{{{n0, n1, n2}},\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  test_partial_derivatives_3d<two_vars<DataVector, 3>>(mesh_3d);\n  test_partial_derivatives_3d<two_vars<DataVector, 3>, one_var<DataVector, 3>>(\n      mesh_3d);\n  test_partial_derivatives_3d<two_vars<ComplexDataVector, 3>,\n                              one_var<ComplexDataVector, 3>>(mesh_3d);\n\n  test_cartoon_partial_derivatives<true>(0.0);\n  test_cartoon_partial_derivatives<false>(0.0);\n  // testing without L'Hopital\n  test_cartoon_partial_derivatives<true>(1.0);\n  test_cartoon_partial_derivatives<false>(1.0);\n\n  test_cartoon_partial_derivative_and_choosers();\n\n  using spacetime_vector = ::Tags::Tempa<0, 3>;\n  using three_vars =\n      tmpl::list<spacetime_vector, ::Tags::Tempi<0, 3>, ::Tags::TempScalar<0>>;\n  for (size_t n_r = 3; n_r <= 6; ++n_r) {\n    test_partial_derivatives_zernikeb1_cartoon<true, three_vars>(n_r);\n    test_partial_derivatives_zernikeb1_cartoon<true, three_vars,\n                                               tmpl::list<spacetime_vector>>(\n        n_r);\n    test_partial_derivatives_zernikeb1_cartoon<false, three_vars>(n_r);\n    test_partial_derivatives_zernikeb1_cartoon<false, three_vars,\n                                               tmpl::list<spacetime_vector>>(\n        n_r);\n  }\n\n  TestHelpers::db::test_prefix_tag<\n      Tags::deriv<Var1<DataVector, 3>, tmpl::size_t<3>, Frame::Grid>>(\n      \"deriv(Var1)\");\n  TestHelpers::db::test_prefix_tag<\n      Tags::spacetime_deriv<Var1<DataVector, 3>, tmpl::size_t<3>, Frame::Grid>>(\n      \"spacetime_deriv(Var1)\");\n\n  BENCHMARK_ADVANCED(\"Partial derivatives\")\n  (Catch::Benchmark::Chronometer meter) {\n    const Mesh<3> mesh{4, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const Affine map1d(-1.0, 1.0, -1.0, 1.0);\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Grid, Affine3D>\n        map(Affine3D{map1d, map1d, map1d});\n    const auto inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    const Variables<tmpl::list<Var1<DataVector, 3>, Var2<DataVector>>> u{\n        mesh.number_of_grid_points(), 0.0};\n    Variables<tmpl::list<\n        ::Tags::deriv<Var1<DataVector, 3>, tmpl::size_t<3>, Frame::Grid>,\n        ::Tags::deriv<Var2<DataVector>, tmpl::size_t<3>, Frame::Grid>>>\n        du{mesh.number_of_grid_points()};\n    meter.measure([&du, &u, &mesh, &inv_jacobian]() {\n      partial_derivatives(make_not_null(&du), u, mesh, inv_jacobian);\n    });\n  };\n\n  BENCHMARK_ADVANCED(\"Partial derivatives complex\")\n  (Catch::Benchmark::Chronometer meter) {\n    const Mesh<3> mesh{4, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const Affine map1d(-1.0, 1.0, -1.0, 1.0);\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Grid, Affine3D>\n        map(Affine3D{map1d, map1d, map1d});\n    const auto inv_jacobian = map.inv_jacobian(logical_coordinates(mesh));\n    const Variables<\n        tmpl::list<Var1<ComplexDataVector, 3>, Var2<ComplexDataVector>>>\n        u{mesh.number_of_grid_points(), 0.0};\n    Variables<tmpl::list<\n        ::Tags::deriv<Var1<ComplexDataVector, 3>, tmpl::size_t<3>, Frame::Grid>,\n        ::Tags::deriv<Var2<ComplexDataVector>, tmpl::size_t<3>, Frame::Grid>>>\n        du{mesh.number_of_grid_points()};\n    meter.measure([&du, &u, &mesh, &inv_jacobian]() {\n      partial_derivatives(make_not_null(&du), u, mesh, inv_jacobian);\n    });\n  };\n}\n\nnamespace {\ntemplate <class MapType>\nstruct MapTag : db::SimpleTag {\n  static constexpr size_t dim = MapType::dim;\n  using target_frame = typename MapType::target_frame;\n  using source_frame = typename MapType::source_frame;\n\n  using type = MapType;\n};\n\ntemplate <typename Tag>\nstruct SomePrefix : db::PrefixTag, db::SimpleTag {\n  using type = typename Tag::type;\n  using tag = Tag;\n  static std::string name() {\n    return \"SomePrefix(\" + db::tag_name<Tag>() + \")\";\n  }\n};\n\ntemplate <size_t Dim, typename T>\nvoid test_partial_derivatives_compute_item(\n    const std::array<size_t, Dim> extents_array, const T& map) {\n  using vars_tags = tmpl::list<Var1<DataVector, Dim>, Var2<DataVector>>;\n  using map_tag = MapTag<std::decay_t<decltype(map)>>;\n  using inv_jac_tag = domain::Tags::InverseJacobianCompute<\n      map_tag, typename domain::Tags::LogicalCoordinates<Dim>::base>;\n  using deriv_tag =\n      Tags::DerivCompute<Tags::Variables<vars_tags>, domain::Tags::Mesh<Dim>,\n                         typename inv_jac_tag::base>;\n  using prefixed_variables_tag =\n      db::add_tag_prefix<SomePrefix, Tags::Variables<vars_tags>>;\n  using deriv_prefixed_tag =\n      Tags::DerivCompute<prefixed_variables_tag, domain::Tags::Mesh<Dim>,\n                         typename inv_jac_tag::base,\n                         tmpl::list<SomePrefix<Var1<DataVector, Dim>>>>;\n\n  TestHelpers::db::test_compute_tag<deriv_tag>(\n      \"Variables(deriv(Var1),deriv(Var2))\");\n\n  const std::array<size_t, Dim> array_to_functions{extents_array -\n                                                   make_array<Dim>(size_t{1})};\n  const Mesh<Dim> mesh{extents_array, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  Variables<vars_tags> u(num_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, vars_tags, tmpl::size_t<Dim>, Frame::Grid>>\n      expected_du(num_grid_points);\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = map(logical_coordinates(mesh));\n\n  tmpl::for_each<vars_tags>([&array_to_functions, &x, &u](auto tag) {\n    using Tag = tmpl::type_from<decltype(tag)>;\n    get<Tag>(u) = Tag::f(array_to_functions, x);\n  });\n  typename prefixed_variables_tag::type prefixed_vars(u);\n\n  tmpl::for_each<vars_tags>([&array_to_functions, &x, &expected_du](auto tag) {\n    using Tag = typename decltype(tag)::type;\n    using DerivativeTag = Tags::deriv<Tag, tmpl::size_t<Dim>, Frame::Grid>;\n    get<DerivativeTag>(expected_du) = Tag::df(array_to_functions, x);\n  });\n\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Mesh<Dim>, Tags::Variables<vars_tags>,\n                        prefixed_variables_tag, map_tag>,\n      db::AddComputeTags<domain::Tags::LogicalCoordinates<Dim>, inv_jac_tag,\n                         deriv_tag, deriv_prefixed_tag>>(mesh, u, prefixed_vars,\n                                                         map);\n\n  const auto& du = db::get<typename deriv_tag::base>(box);\n\n  for (size_t n = 0; n < du.size(); ++n) {\n    // clang-tidy: pointer arithmetic\n    CHECK(du.data()[n] == approx(expected_du.data()[n]));  // NOLINT\n  }\n\n  // Test prefixes are handled correctly\n  const auto& du_prefixed_vars = get<db::add_tag_prefix<\n      Tags::deriv,\n      db::add_tag_prefix<SomePrefix,\n                         Tags::Variables<tmpl::list<Var1<DataVector, Dim>>>>,\n      tmpl::size_t<Dim>, Frame::Grid>>(box);\n  const auto& du_prefixed =\n      get<Tags::deriv<SomePrefix<Var1<DataVector, Dim>>, tmpl::size_t<Dim>,\n                      Frame::Grid>>(du_prefixed_vars);\n  const auto& expected_du_prefixed =\n      get<Tags::deriv<Var1<DataVector, Dim>, tmpl::size_t<Dim>, Frame::Grid>>(\n          expected_du);\n  CHECK_ITERABLE_APPROX(du_prefixed, expected_du_prefixed);\n}\n\ntemplate <size_t Dim, typename T>\nvoid test_partial_derivatives_tensor_compute_item(\n    const std::array<size_t, Dim> extents_array, const T& map) {\n  using tensor_tag = Var1<DataVector, Dim>;\n  using map_tag = MapTag<std::decay_t<decltype(map)>>;\n  using inv_jac_tag = domain::Tags::InverseJacobianCompute<\n      map_tag, typename domain::Tags::LogicalCoordinates<Dim>::base>;\n  using deriv_tensor_tag =\n      Tags::DerivTensorCompute<tensor_tag, typename inv_jac_tag::base,\n                               domain::Tags::Mesh<Dim>>;\n\n  const std::array<size_t, Dim> array_to_functions{extents_array -\n                                                   make_array<Dim>(size_t{1})};\n  const Mesh<Dim> mesh{extents_array, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = map(logical_coordinates(mesh));\n\n  const auto u = tensor_tag::f(array_to_functions, x);\n  const auto expected_du = tensor_tag::df(array_to_functions, x);\n\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Mesh<Dim>, tensor_tag, map_tag>,\n      db::AddComputeTags<domain::Tags::LogicalCoordinates<Dim>, inv_jac_tag,\n                         deriv_tensor_tag>>(mesh, u, map);\n\n  const auto& du = db::get<typename deriv_tensor_tag::base>(box);\n\n  // CHECK_ITERABLE_APPROX(du, expected_du.data());\n  for (size_t n = 0; n < du.size(); ++n) {\n    CHECK_ITERABLE_APPROX(du[n], expected_du[n]);\n  }\n}\n\ntemplate <size_t CompDim, size_t Dim, typename T, Requires<Dim == 3> = nullptr>\nvoid test_cartoon_partial_derivatives_compute_item(\n    const std::array<size_t, Dim> extents_array, const T& map) {\n  using vars_tags =\n      tmpl::list<::Tags::TempScalar<0>, ::Tags::TempA<0, Dim, Frame::Inertial>>;\n  using map_tag = MapTag<std::decay_t<decltype(map)>>;\n  using inv_jac_tag = domain::Tags::InverseJacobianCompute<\n      map_tag, domain::Tags::LogicalCoordinates<Dim>>;\n  using deriv_tag =\n      Tags::DerivCompute<Tags::Variables<vars_tags>, domain::Tags::Mesh<Dim>,\n                         inv_jac_tag,\n                         typename Tags::Variables<vars_tags>::type::tags_list,\n                         domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n  using prefixed_variables_tag =\n      db::add_tag_prefix<SomePrefix, Tags::Variables<vars_tags>>;\n  using deriv_prefixed_tag =\n      Tags::DerivCompute<prefixed_variables_tag, domain::Tags::Mesh<Dim>,\n                         inv_jac_tag,\n                         tmpl::list<SomePrefix<::Tags::TempScalar<0>>>,\n                         domain::Tags::Coordinates<Dim, Frame::Inertial>>;\n\n  TestHelpers::db::test_compute_tag<deriv_tag>(\n      \"Variables(deriv(TempTensor0),deriv(TempTensor0))\");\n\n  const auto pad_at_end = []<typename ElemType>(const ElemType default_val,\n                                                const ElemType padding_val) {\n    std::array<ElemType, Dim> arr{};\n    for (size_t i = 0; i < Dim; ++i) {\n      gsl::at(arr, i) = i < CompDim ? default_val : padding_val;\n    }\n    return arr;\n  };\n  const Mesh<Dim> mesh{\n      extents_array,\n      pad_at_end(Spectral::Basis::Legendre, Spectral::Basis::Cartoon),\n      pad_at_end(Spectral::Quadrature::GaussLobatto,\n                 CompDim == 1 ? Spectral::Quadrature::SphericalSymmetry\n                              : Spectral::Quadrature::AxialSymmetry)};\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  Variables<vars_tags> u(num_grid_points);\n  Variables<\n      db::wrap_tags_in<Tags::deriv, vars_tags, tmpl::size_t<Dim>, Frame::Grid>>\n      expected_du(num_grid_points);\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = map(logical_coordinates(mesh));\n\n  // setting Tensors to allowed symmetry values (easiest to just set to our\n  // coordinates)\n  auto& scalar = get<::Tags::TempScalar<0>>(u);\n  get<>(scalar) = cartoon_func<CompDim == 1>(x);\n\n  auto& vector = get<::Tags::TempA<0, 3>>(u);\n  get<0>(vector) = 1.0 * cartoon_func<CompDim == 1>(x);\n  get<1>(vector) = get<0>(x) * cartoon_func<CompDim == 1>(x);\n  get<2>(vector) = get<1>(x) * cartoon_func<CompDim == 1>(x);\n  get<3>(vector) = get<2>(x) * cartoon_func<CompDim == 1>(x);\n\n  typename prefixed_variables_tag::type prefixed_vars(u);\n\n  auto& d_scalar =\n      get<Tags::deriv<::Tags::TempScalar<0>, tmpl::size_t<Dim>, Frame::Grid>>(\n          expected_du);\n  for (size_t i = 0; i < index_dim<0>(d_scalar); ++i) {\n    d_scalar.get(i) = cartoon_dfunc<CompDim == 1>(i, x);\n  }\n\n  auto& d_vector =\n      get<Tags::deriv<::Tags::TempA<0, Dim>, tmpl::size_t<Dim>, Frame::Grid>>(\n          expected_du);\n  for (size_t i = 0; i < index_dim<0>(d_vector); ++i) {\n    for (size_t a = 0; a < index_dim<1>(d_vector); ++a) {\n      if ((i + 1) == a) {\n        d_vector.get(i, a) = cartoon_func<CompDim == 1>(x);\n      } else {\n        d_vector.get(i, a) = DataVector(num_grid_points, 0.0);\n      }\n      d_vector.get(i, a) +=\n          (a == 0 ? DataVector(num_grid_points, 1.0) : x.get(a - 1)) *\n          cartoon_dfunc<CompDim == 1>(i, x);\n    }\n  }\n\n  const auto to_inertial =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<3>>{};\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Mesh<Dim>, Tags::Variables<vars_tags>,\n                        prefixed_variables_tag, map_tag,\n                        domain::Tags::Coordinates<Dim, Frame::Inertial>>,\n      db::AddComputeTags<domain::Tags::LogicalCoordinates<Dim>, inv_jac_tag,\n                         deriv_tag, deriv_prefixed_tag>>(mesh, u, prefixed_vars,\n                                                         map, to_inertial(x));\n\n  const auto& du = db::get<deriv_tag>(box);\n\n  for (size_t n = 0; n < du.size(); ++n) {\n    // clang-tidy: pointer arithmetic\n    CHECK(du.data()[n] == approx(expected_du.data()[n]));  // NOLINT\n  }\n\n  // Test prefixes are handled correctly\n  const auto& du_prefixed_vars = get<db::add_tag_prefix<\n      Tags::deriv,\n      db::add_tag_prefix<SomePrefix,\n                         Tags::Variables<tmpl::list<::Tags::TempScalar<0>>>>,\n      tmpl::size_t<Dim>, Frame::Grid>>(box);\n  const auto& du_prefixed =\n      get<Tags::deriv<SomePrefix<::Tags::TempScalar<0>>, tmpl::size_t<Dim>,\n                      Frame::Grid>>(du_prefixed_vars);\n  const auto& expected_du_prefixed =\n      get<Tags::deriv<::Tags::TempScalar<0>, tmpl::size_t<Dim>, Frame::Grid>>(\n          expected_du);\n  CHECK_ITERABLE_APPROX(du_prefixed, expected_du_prefixed);\n}\n\ntemplate <size_t CompDim, typename T>\nvoid test_cartoon_partial_derivatives_tensor_compute_item(\n    const std::array<size_t, 3> extents_array, const T& map) {\n  using tensor_tag = ::Tags::TempA<0, 3, Frame::Grid>;\n  using map_tag = MapTag<std::decay_t<decltype(map)>>;\n  using inv_jac_tag =\n      domain::Tags::InverseJacobianCompute<map_tag,\n                                           domain::Tags::LogicalCoordinates<3>>;\n  using deriv_tensor_tag =\n      Tags::DerivTensorCompute<tensor_tag, inv_jac_tag, domain::Tags::Mesh<3>,\n                               domain::Tags::Coordinates<3, Frame::Inertial>>;\n\n  const auto pad_at_end = []<typename ElemType>(const ElemType default_val,\n                                                const ElemType padding_val) {\n    std::array<ElemType, 3> arr{};\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(arr, i) = i < CompDim ? default_val : padding_val;\n    }\n    return arr;\n  };\n  const Mesh<3> mesh{\n      extents_array,\n      pad_at_end(Spectral::Basis::Legendre, Spectral::Basis::Cartoon),\n      pad_at_end(Spectral::Quadrature::GaussLobatto,\n                 CompDim == 1 ? Spectral::Quadrature::SphericalSymmetry\n                              : Spectral::Quadrature::AxialSymmetry)};\n  const size_t num_grid_points = mesh.number_of_grid_points();\n  const auto x = map(logical_coordinates(mesh));\n\n  // Filling with x^a (not testing internal cartoon derivative logic here)\n  typename tensor_tag::type u(num_grid_points);\n  get<0>(u) = cartoon_func<CompDim == 1>(x);\n  get<1>(u) = get<0>(x) * cartoon_func<CompDim == 1>(x);\n  get<2>(u) = get<1>(x) * cartoon_func<CompDim == 1>(x);\n  get<3>(u) = get<2>(x) * cartoon_func<CompDim == 1>(x);\n\n  TensorMetafunctions::prepend_spatial_index<typename tensor_tag::type, 3,\n                                             UpLo::Lo, Frame::Grid>\n      expected_du(num_grid_points);\n  for (size_t i = 0; i < index_dim<0>(expected_du); ++i) {\n    for (size_t a = 0; a < index_dim<1>(expected_du); ++a) {\n      if ((i + 1) == a) {\n        expected_du.get(i, a) = cartoon_func<CompDim == 1>(x);\n      } else {\n        expected_du.get(i, a) = DataVector(num_grid_points, 0.0);\n      }\n      expected_du.get(i, a) +=\n          (a == 0 ? DataVector(num_grid_points, 1.0) : x.get(a - 1)) *\n          cartoon_dfunc<CompDim == 1>(i, x);\n    }\n  }\n\n  const auto to_inertial =\n      domain::CoordinateMap<Frame::Grid, Frame::Inertial,\n                            domain::CoordinateMaps::Identity<3>>{};\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Mesh<3>, tensor_tag, map_tag,\n                        domain::Tags::Coordinates<3, Frame::Inertial>>,\n      db::AddComputeTags<domain::Tags::LogicalCoordinates<3>, inv_jac_tag,\n                         deriv_tensor_tag>>(mesh, u, map, to_inertial(x));\n\n  const auto& du = db::get<typename deriv_tensor_tag::base>(box);\n\n  CHECK_ITERABLE_APPROX(du, expected_du);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.PartialDerivs.ComputeItems\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  Index<3> max_extents{10, 10, 5};\n\n  for (size_t a = 1; a < max_extents[0]; ++a) {\n    test_partial_derivatives_compute_item(\n        std::array<size_t, 1>{{a + 1}},\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n            Affine{-1.0, 1.0, -0.3, 0.7}));\n    for (size_t b = 1; b < max_extents[1]; ++b) {\n      test_partial_derivatives_compute_item(\n          std::array<size_t, 2>{{a + 1, b + 1}},\n          domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n              Affine2D{Affine{-1.0, 1.0, -0.3, 0.7},\n                       Affine{-1.0, 1.0, 0.3, 0.55}}));\n      for (size_t c = 1; a < max_extents[0] / 2 and b < max_extents[1] / 2 and\n                         c < max_extents[2];\n           ++c) {\n        test_partial_derivatives_compute_item(\n            std::array<size_t, 3>{{a + 1, b + 1, c + 1}},\n            domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n                Affine3D{Affine{-1.0, 1.0, -0.3, 0.7},\n                         Affine{-1.0, 1.0, 0.3, 0.55},\n                         Affine{-1.0, 1.0, 2.3, 2.8}}));\n      }\n    }\n  }\n  using Identity1D = domain::CoordinateMaps::Identity<1>;\n  test_cartoon_partial_derivatives_compute_item<1>(\n      std::array<size_t, 3>{{8, 1, 1}},\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D,\n                                                 Identity1D>{\n              Affine{-1.0, 1.0, 0.0, 0.7}, Identity1D{}, Identity1D{}}));\n  test_cartoon_partial_derivatives_compute_item<2>(\n      std::array<size_t, 3>{{6, 7, 1}},\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity1D>{\n              Affine{-1.0, 1.0, 0.0, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n              Identity1D{}}));\n  test_cartoon_partial_derivatives_tensor_compute_item<1>(\n      std::array<size_t, 3>{{8, 1, 1}},\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D,\n                                                 Identity1D>{\n              Affine{-1.0, 1.0, 0.0, 0.7}, Identity1D{}, Identity1D{}}));\n  test_cartoon_partial_derivatives_tensor_compute_item<2>(\n      std::array<size_t, 3>{{6, 7, 1}},\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity1D>{\n              Affine{-1.0, 1.0, 0.0, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n              Identity1D{}}));\n  for (size_t a = 1; a < max_extents[0]; ++a) {\n    test_partial_derivatives_tensor_compute_item(\n        std::array<size_t, 1>{{a + 1}},\n        domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n            Affine{-1.0, 1.0, -0.3, 0.7}));\n    for (size_t b = 1; b < max_extents[1]; ++b) {\n      test_partial_derivatives_tensor_compute_item(\n          std::array<size_t, 2>{{a + 1, b + 1}},\n          domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n              Affine2D{Affine{-1.0, 1.0, -0.3, 0.7},\n                       Affine{-1.0, 1.0, 0.3, 0.55}}));\n      for (size_t c = 1; a < max_extents[0] / 2 and b < max_extents[1] / 2 and\n                         c < max_extents[2];\n           ++c) {\n        test_partial_derivatives_tensor_compute_item(\n            std::array<size_t, 3>{{a + 1, b + 1, c + 1}},\n            domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n                Affine3D{Affine{-1.0, 1.0, -0.3, 0.7},\n                         Affine{-1.0, 1.0, 0.3, 0.55},\n                         Affine{-1.0, 1.0, 2.3, 2.8}}));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_PowerMonitors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// \\file\n// Tests of power monitors.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PowerMonitors.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace {\n\nvoid test_power_monitors_impl() {\n  const size_t number_of_points_per_dimension = 4;\n  const size_t number_of_points = pow<2>(number_of_points_per_dimension);\n\n  // Test a constant function\n  const DataVector test_data_vector{number_of_points, 1.0};\n  const ComplexDataVector test_complex_data_vector{\n      number_of_points, std::complex<double>(1.0, 1.0)};\n\n  const Mesh<2_st> mesh{number_of_points_per_dimension,\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n\n  const auto test_power_monitors =\n      PowerMonitors::power_monitors(test_data_vector, mesh);\n  const auto test_power_monitors_complex =\n      PowerMonitors::power_monitors(test_complex_data_vector, mesh);\n\n  // The only non-zero modal coefficient of a constant is the one corresponding\n  // to the first Legendre polynomial\n  DataVector check_data_vector =\n      DataVector{number_of_points_per_dimension, 0.0};\n  check_data_vector[0] = 1.0 / sqrt(number_of_points_per_dimension);\n  DataVector check_data_vector_complex =\n      DataVector{number_of_points_per_dimension, 0.0};\n  check_data_vector_complex[0] = sqrt(2) / sqrt(number_of_points_per_dimension);\n\n  const std::array<DataVector, 2> expected_power_monitors{check_data_vector,\n                                                          check_data_vector};\n  const std::array<DataVector, 2> expected_power_monitors_complex{\n      check_data_vector_complex, check_data_vector_complex};\n\n  CHECK_ITERABLE_APPROX(test_power_monitors, expected_power_monitors);\n  CHECK_ITERABLE_APPROX(test_power_monitors_complex,\n                        expected_power_monitors_complex);\n}\n\nvoid test_power_monitors_second_impl() {\n  const size_t number_of_points_per_dimension = 4;\n\n  const Mesh<2_st> mesh{number_of_points_per_dimension,\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n\n  const auto logical_coords = logical_coordinates(mesh);\n\n  // Build a test function containing only one Legendre basis function\n  // per dimension\n  const size_t x_mode = 0;\n  const size_t y_mode = 1;\n  const std::array<size_t, 2> coeff = {x_mode, y_mode};\n\n  DataVector u_nodal(mesh.number_of_grid_points(), 1.0);\n  for (size_t dim = 0; dim < 2; ++dim) {\n    u_nodal *=\n        Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n            gsl::at(coeff, dim), logical_coords.get(dim));\n  }\n\n  const auto test_power_monitors = PowerMonitors::power_monitors(u_nodal, mesh);\n\n  // The only non-zero modal coefficient of a constant is the one corresponding\n  // to the specified Legendre polynomial\n\n  // In the x direction\n  DataVector check_data_vector_x =\n      DataVector{number_of_points_per_dimension, 0.0};\n  check_data_vector_x[x_mode] = 1.0 / sqrt(number_of_points_per_dimension);\n\n  // In the y direction\n  DataVector check_data_vector_y =\n      DataVector{number_of_points_per_dimension, 0.0};\n  check_data_vector_y[y_mode] = 1.0 / sqrt(number_of_points_per_dimension);\n\n  // We compare against the expected array\n  const std::array<DataVector, 2> expected_power_monitors{check_data_vector_x,\n                                                          check_data_vector_y};\n\n  CHECK_ITERABLE_APPROX(test_power_monitors, expected_power_monitors);\n}\n\nvoid test_relative_truncation_error_impl() {\n  // We recompute the truncation error for a function where we know the\n  // power monitors analytically\n  const size_t number_of_points_per_dimension = 8;\n  const Mesh<1_st> mesh{number_of_points_per_dimension,\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  // Build a test function with no zero power monitors\n  const std::vector<int> coeffs = {0, 1, 2, 3, 4, 5, 6, 7};\n  DataVector u_nodal(mesh.number_of_grid_points(), 0.0);\n  double ampl = 0.0;\n  for (auto coeff : coeffs) {\n    ampl = pow(10.0, -coeff);\n    u_nodal +=\n        ampl *\n        Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(\n            static_cast<size_t>(coeff), logical_coords.get(0_st));\n  }\n\n  // Compute the relative truncation error\n  const int last_coeff = 7;\n  double weight = 0.0;\n  double avg = 0.0;\n  double weight_sum = 0.0;\n  for (auto coeff : coeffs) {\n    ampl = pow(10.0, -coeff);\n    weight = exp(-square(coeff - last_coeff + 0.5));\n    avg += log10(ampl) * weight;\n    weight_sum += weight;\n  }\n  avg = avg / weight_sum;\n  // By construction the maximum of the magnitude of the first two modes is\n  // unity.\n  // We test the order of magnitude of the relative error\n  const double expected_relative_truncation_error = pow(10.0, avg);\n\n  const auto power_monitors = PowerMonitors::power_monitors(u_nodal, mesh);\n  const DataVector& power_monitor_x = gsl::at(power_monitors, 0_st);\n  // We use all of the modes as above\n  const double test_relative_truncation_error =\n      PowerMonitors::relative_truncation_error(power_monitor_x,\n                                               power_monitor_x.size());\n\n  CHECK_ITERABLE_APPROX(expected_relative_truncation_error,\n                        test_relative_truncation_error);\n\n  // Test truncation error\n  const double test_truncation_error =\n      PowerMonitors::absolute_truncation_error(u_nodal, mesh)[0];\n\n  // Compare with the result from the relative truncation error\n  const double expected_truncation_error_x =\n      max(abs(u_nodal)) * PowerMonitors::relative_truncation_error(\n                              power_monitor_x, power_monitor_x.size());\n\n  CHECK_ITERABLE_APPROX(test_truncation_error, expected_truncation_error_x);\n}\n\nvoid test_relative_truncation_error_with_symmetry() {\n  // Try to resolve half a period of a sinusoid\n  const size_t num_modes = 12;\n  const Mesh<1> mesh{num_modes, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto xi = Spectral::collocation_points(mesh);\n  const double wave_number = 0.5;\n  const DataVector u_nodal = sin((xi + 1.) * M_PI * wave_number);\n  CAPTURE(u_nodal);\n  auto modes = PowerMonitors::power_monitors(u_nodal, mesh)[0];\n  // Add some more noise to the modes\n  modes += 10. * std::numeric_limits<double>::epsilon();\n  CAPTURE(modes);\n  const double relative_truncation_error =\n      PowerMonitors::relative_truncation_error(modes, num_modes);\n  // Last mode should be zero by symmetry\n  REQUIRE(modes[num_modes - 1] == approx(0.));\n  // Expect the relative truncation error to be the ratio of the first and last\n  // nonzero modes\n  const double expected_relative_truncation_error =\n      modes[num_modes - 2] / modes[0];\n  const Approx custom_approx = Approx::custom().epsilon(5e-2);\n  CHECK(relative_truncation_error ==\n        custom_approx(expected_relative_truncation_error));\n}\n\nvoid test_relative_truncation_error_linear_function() {\n  // Resolve a linear function with a few modes. We technically need only 2.\n  const auto get_modes = [](const size_t num_modes) {\n    const Mesh<1> mesh{num_modes, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const auto xi = Spectral::collocation_points(mesh);\n    const DataVector u_nodal = (xi + 1.) * 0.5;\n    auto modes = PowerMonitors::power_monitors(u_nodal, mesh)[0];\n    // Add some noise to the modes\n    modes += 10. * std::numeric_limits<double>::epsilon();\n    const double relative_truncation_error =\n        PowerMonitors::relative_truncation_error(modes, num_modes);\n    return std::make_pair(modes, relative_truncation_error);\n  };\n  {\n    INFO(\"2 modes\");\n    const auto [modes, rel_error] = get_modes(2);\n    CAPTURE(modes);\n    CHECK_ITERABLE_APPROX(modes, (DataVector{0.5, 0.5}));\n    // We don't know for sure that we have resolved the function exactly,\n    // because we have two nonzero modes and nothing else.\n    CHECK(rel_error == approx(1.));\n  }\n  {\n    INFO(\"3 modes\");\n    const auto [modes, rel_error] = get_modes(3);\n    CAPTURE(modes);\n    CHECK_ITERABLE_APPROX(modes, (DataVector{0.5, 0.5, 0.}));\n    // The last mode is zero, but we still don't know if we have resolved the\n    // function because the last mode could be zero by symmetry.\n    CHECK(rel_error == approx(1.));\n  }\n  {\n    INFO(\"4 modes\");\n    const auto [modes, rel_error] = get_modes(4);\n    CAPTURE(modes);\n    CHECK_ITERABLE_APPROX(modes, (DataVector{0.5, 0.5, 0., 0.}));\n    // We have two zero modes, so we know we have resolved the function exactly.\n    CHECK(rel_error < 1.e-14);\n  }\n}\n\nvoid test_convergence_rate() {\n  // First, check that a power monitor with an exact, constant slope has the\n  // expected convergence rate\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> slope_dis(-4.0, -1.0);\n  const double expected_slope = slope_dis(gen);\n  std::uniform_real_distribution<> offset_dis(-0.4, -0.1);\n  const double offset = offset_dis(gen);\n  const size_t size_of_power_monitor{10};\n  DataVector power_monitor_with_known_slope{size_of_power_monitor};\n  for (size_t i = 0; i < size_of_power_monitor; ++i) {\n    power_monitor_with_known_slope[i] =\n        pow(10.0, static_cast<double>(i) * expected_slope + offset);\n  }\n  constexpr size_t filtered_modes = 2;\n\n  double convergence_rate =\n      PowerMonitors::convergence_rate_and_number_of_pile_up_modes(\n          power_monitor_with_known_slope, filtered_modes)\n          .convergence_rate;\n  CHECK(approx(convergence_rate) == -expected_slope);\n\n  // Change the filtered modes' power to a NaN, and ensure that this mode\n  // is ignored when computing the convergence rate.\n  power_monitor_with_known_slope[8] =\n      std::numeric_limits<double>::signaling_NaN();\n  power_monitor_with_known_slope[9] =\n      std::numeric_limits<double>::signaling_NaN();\n  convergence_rate =\n      PowerMonitors::convergence_rate_and_number_of_pile_up_modes(\n          power_monitor_with_known_slope, filtered_modes)\n          .convergence_rate;\n  CHECK(approx(convergence_rate) == -expected_slope);\n\n  // Test that adding noise of amplitude 1e-2 affects the slope recovered\n  // by no more than that amount\n  constexpr double noise_amp = 0.01;\n  std::uniform_real_distribution<> noise_dis(-noise_amp, noise_amp);\n  for (size_t i = 0; i < size_of_power_monitor - filtered_modes; ++i) {\n    power_monitor_with_known_slope[i] *= pow(10.0, noise_dis(gen));\n  }\n  convergence_rate =\n      PowerMonitors::convergence_rate_and_number_of_pile_up_modes(\n          power_monitor_with_known_slope, filtered_modes)\n          .convergence_rate;\n  // define custom approx for higher derivative checks\n  const Approx custom_approx = Approx::custom().epsilon(noise_amp).scale(1.0);\n  CHECK(custom_approx(convergence_rate) == -expected_slope);\n\n// Check assert that sufficient modes were provided\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      PowerMonitors::convergence_rate_and_number_of_pile_up_modes(\n          power_monitor_with_known_slope, size_of_power_monitor - 3),\n      Catch::Matchers::ContainsSubstring(\n          \"Power monitor needs at least 4 unfiltered modes to compute \"\n          \"convergence\"));\n#endif\n}\n\nvoid test_pile_up_modes() {\n  // Check assert that sufficient modes were provided\n  // First, check that a power monitor with an exact, constant slope has the\n  // vanishing pile up modes\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> slope_dis(-2.0, -1.0);\n  const double expected_slope = slope_dis(gen);\n  std::uniform_real_distribution<> offset_dis(-0.4, -0.1);\n  const double offset = offset_dis(gen);\n  const size_t size_of_power_monitor{20};\n  DataVector power_monitor_with_known_slope{size_of_power_monitor};\n  for (size_t i = 0; i < size_of_power_monitor; ++i) {\n    power_monitor_with_known_slope[i] =\n        pow(10.0, static_cast<double>(i) * expected_slope + offset);\n  }\n  constexpr size_t filtered_modes = 2;\n\n  const double pile_up_modes_known_slope =\n      PowerMonitors::convergence_rate_and_number_of_pile_up_modes(\n          power_monitor_with_known_slope, filtered_modes)\n          .number_of_pile_up_modes;\n  const Approx custom_approx = Approx::custom().epsilon(1.e-10).scale(1.0);\n  CHECK(custom_approx(pile_up_modes_known_slope) == 0.0);\n\n  // Revise the power monitor to artificially introduce pile up modes\n  // Set the top n modes to be equal to the n-1 power, so the slope is zero.\n  // Because the number of pile up modes is defined as a double, the computed\n  // pile up mode count will have a fractional part as well as the expected\n  // integer number of pile up modes. In the test, ignore the fractional part,\n  // but make sure that the expected integer number of pile up modes is\n  // recovered. Always leave at least 2 unfiltered modes not piled up.\n  for (size_t expected_pile_up_modes = 1;\n       expected_pile_up_modes < size_of_power_monitor - filtered_modes - 2;\n       ++expected_pile_up_modes) {\n    DataVector power_monitor_with_pile_up_modes =\n        power_monitor_with_known_slope;\n    for (size_t i =\n             size_of_power_monitor - filtered_modes - expected_pile_up_modes;\n         i < size_of_power_monitor - filtered_modes; ++i) {\n      power_monitor_with_pile_up_modes[i] =\n          power_monitor_with_pile_up_modes[size_of_power_monitor -\n                                           filtered_modes -\n                                           expected_pile_up_modes - 1];\n    }\n    // Ensure that filtered modes are not used by replacing them with NaN\n    for (size_t i = size_of_power_monitor - filtered_modes;\n         i < size_of_power_monitor; ++i) {\n      power_monitor_with_pile_up_modes[i] =\n          std::numeric_limits<double>::signaling_NaN();\n    }\n    const double pile_up_modes =\n        PowerMonitors::convergence_rate_and_number_of_pile_up_modes(\n            power_monitor_with_pile_up_modes, filtered_modes)\n            .number_of_pile_up_modes;\n    CHECK(static_cast<size_t>(std::floor(pile_up_modes)) ==\n          expected_pile_up_modes);\n  }\n\n  // Check that a power monitor with zero convergence rate returns zero piled up\n  // modes\n  power_monitor_with_known_slope = power_monitor_with_known_slope[0];\n  const double pile_up_modes_zero_convergence =\n      PowerMonitors::convergence_rate_and_number_of_pile_up_modes(\n          power_monitor_with_known_slope, filtered_modes)\n          .number_of_pile_up_modes;\n  CHECK(pile_up_modes_zero_convergence == 0.0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.PowerMonitors\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  test_power_monitors_impl();\n  test_power_monitors_second_impl();\n  test_relative_truncation_error_impl();\n  test_relative_truncation_error_with_symmetry();\n  test_relative_truncation_error_linear_function();\n  test_convergence_rate();\n  test_pile_up_modes();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearOperators/Test_WeakDivergence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/Variables/FrameTransform.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/MetricIdentityJacobian.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.tpp\"\n#include \"NumericalAlgorithms/LinearOperators/WeakDivergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nusing namespace std::complex_literals;\n\nnamespace {\ntemplate <typename DataType>\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType, size_t Dim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::i<DataType, Dim>;\n};\n\ntemplate <typename DataType, size_t Dim>\nvoid test_weak_divergence_random_jacobian(const Mesh<Dim>& mesh) {\n  CAPTURE(Dim);\n  CAPTURE(mesh);\n  CAPTURE(mesh.quadrature(0));\n  std::uniform_real_distribution<double> dist(-1.0, 2.3);\n  MAKE_GENERATOR(gen);\n  using flux_tags = tmpl::list<\n      Tags::Flux<Var1<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      Tags::Flux<Var2<DataType, Dim>, tmpl::size_t<Dim>, Frame::Inertial>>;\n  using div_tags = tmpl::list<\n      Tags::div<Tags::Flux<Var1<DataType>, tmpl::size_t<Dim>, Frame::Inertial>>,\n      Tags::div<\n          Tags::Flux<Var2<DataType, Dim>, tmpl::size_t<Dim>, Frame::Inertial>>>;\n\n  tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords{\n      mesh.number_of_grid_points()};\n  Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial> jacobian{\n      mesh.number_of_grid_points()};\n\n  fill_with_random_values(make_not_null(&inertial_coords), make_not_null(&gen),\n                          make_not_null(&dist));\n  fill_with_random_values(make_not_null(&jacobian), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      det_jac_times_inverse_jacobian{};\n\n  dg::metric_identity_det_jac_times_inv_jac(\n      make_not_null(&det_jac_times_inverse_jacobian), mesh, inertial_coords,\n      jacobian);\n  // Generate constant fluxes that aren't all the same.\n  Variables<flux_tags> fluxes{mesh.number_of_grid_points(), 2.0};\n  tmpl::for_each<flux_tags>([&fluxes](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    auto& flux = get<tag>(fluxes);\n    for (size_t storage_index = 0; storage_index < flux.size();\n         ++storage_index) {\n      flux[storage_index] = static_cast<double>(storage_index) + 3.0;\n      if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n        flux[storage_index] +=\n            1.0i * (static_cast<double>(storage_index) + 4.0);\n      }\n    }\n  });\n\n  Variables<div_tags> divergence_result{mesh.number_of_grid_points()};\n\n  weak_divergence(make_not_null(&divergence_result), fluxes, mesh,\n                  det_jac_times_inverse_jacobian);\n\n  Approx local_approx = Approx::custom().epsilon(1.0e-11).scale(1.);\n  const Variables<div_tags> expected_divergence_result{\n      mesh.number_of_grid_points(), 0.0};\n  tmpl::for_each<div_tags>([&divergence_result, &expected_divergence_result,\n                            &local_approx, &mesh](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    if (mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto) {\n      // Only the interior points are zero. Points on the boundary are non-zero\n      // because they need the flux lifted to be zero.\n      for (size_t i = 1; i < mesh.extents(0) - 1; ++i) {\n        CAPTURE(i);\n        for (size_t j = 1; j < ((Dim > 1) ? mesh.extents(1) - 1 : 2); ++j) {\n          CAPTURE(j);\n          for (size_t k = 1; k < ((Dim > 2) ? mesh.extents(2) - 1 : 2); ++k) {\n            CAPTURE(k);\n            for (size_t storage_index = 0;\n                 storage_index < get<tag>(divergence_result).size();\n                 ++storage_index) {\n              size_t collapsed_index = 0;\n              if constexpr (Dim == 3) {\n                collapsed_index =\n                    ::collapsed_index(Index<3>{i, j, k}, mesh.extents());\n              } else if constexpr (Dim == 2) {\n                collapsed_index =\n                    ::collapsed_index(Index<2>{i, j}, mesh.extents());\n              } else {\n                collapsed_index =\n                    ::collapsed_index(Index<1>{i}, mesh.extents());\n              }\n              CHECK_ITERABLE_CUSTOM_APPROX(\n                  get<tag>(divergence_result)[storage_index][collapsed_index],\n                  get<tag>(expected_divergence_result)[storage_index]\n                                                      [collapsed_index],\n                  local_approx);\n            }\n          }\n        }\n      }\n    } else {\n      for (size_t storage_index = 0;\n           storage_index < get<tag>(divergence_result).size() and\n           (Dim == 1 or mesh.extents(0) == 3);\n           ++storage_index) {\n        size_t collapsed_index = std::numeric_limits<size_t>::max();\n        if constexpr (Dim == 3) {\n          collapsed_index = ::collapsed_index(\n              Index<3>{mesh.extents(0) / 2, mesh.extents(1) / 2,\n                       mesh.extents(2) / 2},\n              mesh.extents());\n        } else if constexpr (Dim == 2) {\n          collapsed_index = ::collapsed_index(\n              Index<2>{mesh.extents(0) / 2, mesh.extents(1) / 2},\n              mesh.extents());\n        } else {\n          collapsed_index =\n              ::collapsed_index(Index<1>{mesh.extents(0) / 2}, mesh.extents());\n        }\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            get<tag>(divergence_result)[storage_index][collapsed_index],\n            get<tag>(\n                expected_divergence_result)[storage_index][collapsed_index],\n            local_approx);\n      }\n    }\n  });\n\n  {\n    INFO(\"Test logical weak divergence\");\n    Variables<div_tags> logical_divergence_result{mesh.number_of_grid_points()};\n    auto logical_fluxes = transform::first_index_to_different_frame(\n        fluxes, det_jac_times_inverse_jacobian);\n    ::dg::apply_mass_matrix(make_not_null(&logical_fluxes), mesh);\n    logical_weak_divergence(make_not_null(&logical_divergence_result),\n                            logical_fluxes, mesh);\n    ::dg::apply_inverse_mass_matrix(make_not_null(&logical_divergence_result),\n                                    mesh);\n    CHECK_VARIABLES_CUSTOM_APPROX(logical_divergence_result, divergence_result,\n                                  local_approx);\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid test_weak_divergence_constant_jacobian(const Mesh<Dim>& mesh) {\n  CAPTURE(Dim);\n  CAPTURE(mesh);\n  CAPTURE(mesh.quadrature(0));\n  std::uniform_real_distribution<double> dist(-1.0, 1.0);\n  MAKE_GENERATOR(gen);\n  using flux_tags = tmpl::list<\n      Tags::Flux<Var1<DataType>, tmpl::size_t<Dim>, Frame::Inertial>,\n      Tags::Flux<Var2<DataType, Dim>, tmpl::size_t<Dim>, Frame::Inertial>>;\n  using div_tags = tmpl::list<\n      Tags::div<Tags::Flux<Var1<DataType>, tmpl::size_t<Dim>, Frame::Inertial>>,\n      Tags::div<\n          Tags::Flux<Var2<DataType, Dim>, tmpl::size_t<Dim>, Frame::Inertial>>>;\n\n  const auto logical_coords = logical_coordinates(mesh);\n  tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords{\n      mesh.number_of_grid_points()};\n  Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial> jacobian{\n      mesh.number_of_grid_points(), 0.0};\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inverse_jacobian{mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < Dim; ++i) {\n    jacobian.get(i, i) = 2.0;\n    inertial_coords.get(i) = 2.0 * logical_coords.get(i);\n  }\n\n  InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      det_jac_times_inverse_jacobian{};\n\n  dg::metric_identity_det_jac_times_inv_jac(\n      make_not_null(&det_jac_times_inverse_jacobian), mesh, inertial_coords,\n      jacobian);\n  const auto det_jacobian = determinant(jacobian);\n  for (size_t i = 0; i < Dim; ++i) {\n    for (size_t j = 0; j < Dim; ++j) {\n      inverse_jacobian.get(i, j) =\n          det_jac_times_inverse_jacobian.get(i, j) / get(det_jacobian);\n    }\n  }\n\n  // Generate smooth fluxes\n  Variables<flux_tags> fluxes{mesh.number_of_grid_points()};\n  const auto compute_smooth_fluxes = [](const auto fluxes_ptr,\n                                        const auto& coords) {\n    auto& local_fluxes = *fluxes_ptr;\n    tmpl::for_each<flux_tags>([&local_fluxes, &coords](auto tag_v) {\n      using tag = tmpl::type_from<decltype(tag_v)>;\n      for (size_t tensor_index = 0;\n           tensor_index < get<tag>(local_fluxes).size(); ++tensor_index) {\n        auto& flux_component = get<tag>(local_fluxes)[tensor_index];\n        flux_component = std::sqrt(tensor_index + 1) * square(get<0>(coords));\n        if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n          flux_component +=\n              1.0i * std::sqrt(tensor_index + 2) * square(get<0>(coords));\n        }\n        for (size_t d = 1; d < Dim; ++d) {\n          flux_component += std::sqrt(tensor_index + 1) * coords.get(d);\n          if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n            flux_component +=\n                1.0i * std::sqrt(tensor_index + 2) * coords.get(d);\n          }\n        }\n      }\n    });\n  };\n  if (mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto) {\n    fill_with_random_values(make_not_null(&fluxes), make_not_null(&gen),\n                            make_not_null(&dist));\n  } else {\n    compute_smooth_fluxes(make_not_null(&fluxes), inertial_coords);\n  }\n\n  Variables<div_tags> divergence_result{mesh.number_of_grid_points()};\n  weak_divergence(make_not_null(&divergence_result), fluxes, mesh,\n                  det_jac_times_inverse_jacobian);\n\n  Variables<div_tags> expected_divergence = [&compute_smooth_fluxes,\n                                             &det_jacobian, &fluxes,\n                                             &inverse_jacobian, &mesh]() {\n    if (mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto) {\n      auto local_expected_divergence =\n          divergence(fluxes, mesh, inverse_jacobian);\n      local_expected_divergence *= -get(det_jacobian);\n      return local_expected_divergence;\n    } else {\n      // We are on a Gauss grid. Compute the weak divergence on a Gauss-Lobatto\n      // grid and interpolate it to the Gauss grid. In this case we explicitly\n      // didn't use random data so that the result all fits inside the basis\n      // function space.\n      const Mesh<Dim> gl_mesh{mesh.extents(0), mesh.basis(0),\n                              Spectral::Quadrature::GaussLobatto};\n      const auto gl_logical_coords = logical_coordinates(gl_mesh);\n      tnsr::I<DataVector, Dim, Frame::Inertial> gl_inertial_coords{\n          gl_mesh.number_of_grid_points()};\n      Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n          gl_jacobian{gl_mesh.number_of_grid_points(), 0.0};\n      for (size_t i = 0; i < Dim; ++i) {\n        gl_jacobian.get(i, i) = 2.0;\n        gl_inertial_coords.get(i) = 2.0 * gl_logical_coords.get(i);\n      }\n\n      InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n          gl_det_jac_times_inverse_jacobian{};\n\n      dg::metric_identity_det_jac_times_inv_jac(\n          make_not_null(&gl_det_jac_times_inverse_jacobian), gl_mesh,\n          gl_inertial_coords, gl_jacobian);\n\n      Variables<flux_tags> gl_fluxes{gl_mesh.number_of_grid_points()};\n      compute_smooth_fluxes(make_not_null(&gl_fluxes), gl_inertial_coords);\n\n      Variables<div_tags> gl_divergence_result{gl_mesh.number_of_grid_points()};\n      weak_divergence(make_not_null(&gl_divergence_result), gl_fluxes, gl_mesh,\n                      gl_det_jac_times_inverse_jacobian);\n\n      const Matrix interp_1d = Spectral::interpolation_matrix(\n          Mesh<1>{mesh.extents(0), mesh.basis(0),\n                  Spectral::Quadrature::GaussLobatto},\n          get<0>(logical_coordinates(Mesh<1>{mesh.extents(0), mesh.basis(0),\n                                             Spectral::Quadrature::Gauss})));\n      return apply_matrices(make_array<Dim>(interp_1d), gl_divergence_result,\n                            gl_mesh.extents());\n    }\n  }();\n\n  Approx local_approx = Approx::custom().epsilon(5.0e-12).scale(1.);\n  tmpl::for_each<div_tags>([&divergence_result, &expected_divergence,\n                            &local_approx, &mesh](auto tag_v) {\n    using tag = tmpl::type_from<decltype(tag_v)>;\n    if (mesh.quadrature(0) == Spectral::Quadrature::GaussLobatto) {\n      // Only the interior points are zero. Points on the boundary are non-zero\n      // because they need the flux lifted to be zero.\n      for (size_t i = 1; i < mesh.extents(0) - 1; ++i) {\n        CAPTURE(i);\n        for (size_t j = 1; j < ((Dim > 1) ? mesh.extents(1) - 1 : 2); ++j) {\n          CAPTURE(j);\n          for (size_t k = 1; k < ((Dim > 2) ? mesh.extents(2) - 1 : 2); ++k) {\n            CAPTURE(k);\n            for (size_t storage_index = 0;\n                 storage_index < get<tag>(divergence_result).size();\n                 ++storage_index) {\n              size_t collapsed_index = 0;\n              if constexpr (Dim == 3) {\n                collapsed_index =\n                    ::collapsed_index(Index<3>{i, j, k}, mesh.extents());\n              } else if constexpr (Dim == 2) {\n                collapsed_index =\n                    ::collapsed_index(Index<2>{i, j}, mesh.extents());\n              } else {\n                collapsed_index =\n                    ::collapsed_index(Index<1>{i}, mesh.extents());\n              }\n              CHECK_ITERABLE_CUSTOM_APPROX(\n                  get<tag>(divergence_result)[storage_index][collapsed_index],\n                  get<tag>(expected_divergence)[storage_index][collapsed_index],\n                  local_approx);\n            }\n          }\n        }\n      }\n    } else {\n      CHECK_ITERABLE_CUSTOM_APPROX(get<tag>(divergence_result),\n                                   get<tag>(expected_divergence), local_approx);\n    }\n  });\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid test() {\n  for (size_t num_pts = 3; num_pts < 9; num_pts += 2) {\n    for (const auto& quadrature :\n         {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}) {\n      test_weak_divergence_random_jacobian<DataType>(\n          Mesh<Dim>{num_pts, Spectral::Basis::Legendre, quadrature});\n      if constexpr (Dim == 1) {\n        // Haven't figured out a good test in 2d and 3d\n        test_weak_divergence_constant_jacobian<DataType>(\n            Mesh<Dim>{num_pts, Spectral::Basis::Legendre, quadrature});\n      }\n    }\n  }\n}\n}  // namespace\n\n// [[Timeout, 10]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearOperators.WeakDivergence\",\n                  \"[NumericalAlgorithms][LinearOperators][Unit]\") {\n  // We already have tests that verify that the matrix used in the weak\n  // divergence is correct. What we need to test is that the function\n  // weak_divergence behaves correctly. This is pretty tricky, unfortunately.\n  // The best way of doing so is to check that the strong divergence is\n  // identical to weak divergence plus the lifted boundary terms. However, we do\n  // not (yet) have the lifting terms coded up, so we need to do something else.\n  // We have two functions that do the hard work:\n  //\n  // - test_weak_divergence_random_jacobian\n  // - test_weak_divergence_constant_jacobian\n  //\n  // test_weak_divergence_random_jacobian:\n  //\n  // This generates a completely random Jacobian and verifies that the\n  // divergence of this Jacobian is zero. Again, this is tricky because the\n  // surface terms are not accounted for. With Gauss-Lobatto points this means\n  // the interior points are zero. For Gauss points the problem is much harder.\n  // Only the central grid point is zero and only when there is an odd number of\n  // grid points. Verifying that the metric identities are satisfied is a\n  // necessary but not sufficient condition.\n  //\n  // test_weak_divergence_constant_jacobian:\n  //\n  // This uses a constant but non-trivial Jacobian and verifies that the strong\n  // and weak divergence are equal. With Gauss-Lobatto points this is everywhere\n  // in the interior. For Gauss points we only check that interpolating the\n  // Gauss-Lobatto result to Gauss points gives the same weak divergence. Since\n  // we verify Gauss-Lobatto points separately this is a good test. We only do\n  // this check in 1d because 2d and 3d are annoying to get right, so much so\n  // that it's better to check everything exactly once the lifting terms are in.\n  test<DataVector, 1>();\n  test<ComplexDataVector, 1>();\n  test<DataVector, 2>();\n  test<ComplexDataVector, 2>();\n  test<DataVector, 3>();\n  test<ComplexDataVector, 3>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearSolver/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_LinearSolver\")\n\nset(LIBRARY_SOURCES\n  Test_BuildMatrix.cpp\n  Test_ExplicitInverse.cpp\n  Test_Gmres.cpp\n  Test_InnerProduct.cpp\n  Test_Lapack.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainStructure\n  LinearSolver\n  Logging\n  ParallelSchwarz\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearSolver/Test_BuildMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <blaze/math/CompressedMatrix.h>\n#include <blaze/math/DynamicMatrix.h>\n#include <blaze/math/DynamicVector.h>\n#include <blaze/math/StaticMatrix.h>\n#include <blaze/math/StaticVector.h>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/LinearSolver/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/BuildMatrix.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::LinearSolver;\n\nnamespace {\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace\n\nnamespace LinearSolver::Serial {\n\nSPECTRE_TEST_CASE(\"Unit.LinearSolver.Serial.BuildMatrix\",\n                  \"[Unit][NumericalAlgorithms][LinearSolver]\") {\n  {\n    INFO(\"Build a simple dense matrix\");\n    const blaze::DynamicMatrix<double> matrix{{4., 1.}, {3., 1.}};\n    const helpers::ApplyMatrix<double> linear_operator{matrix};\n    blaze::DynamicMatrix<double> matrix_representation(2, 2);\n    blaze::DynamicVector<double> operand_buffer(2, 0.);\n    blaze::DynamicVector<double> result_buffer(2, 0.);\n    build_matrix(make_not_null(&matrix_representation),\n                 make_not_null(&operand_buffer), make_not_null(&result_buffer),\n                 linear_operator);\n    CHECK_ITERABLE_APPROX(matrix_representation, matrix);\n    CHECK(linear_operator.invocations == 2);\n  }\n  {\n    INFO(\"Build a complex dense matrix\");\n    const blaze::DynamicMatrix<std::complex<double>> matrix{\n        {std::complex(4., 2.), std::complex(1., -1.)},\n        {std::complex(3., 0.), std::complex(1., 3.)}};\n    const helpers::ApplyMatrix<std::complex<double>> linear_operator{matrix};\n    blaze::DynamicMatrix<std::complex<double>> matrix_representation(2, 2);\n    blaze::DynamicVector<std::complex<double>> operand_buffer(2, 0.);\n    blaze::DynamicVector<std::complex<double>> result_buffer(2, 0.);\n    build_matrix(make_not_null(&matrix_representation),\n                 make_not_null(&operand_buffer), make_not_null(&result_buffer),\n                 linear_operator);\n    CHECK_ITERABLE_APPROX(matrix_representation, matrix);\n    CHECK(linear_operator.invocations == 2);\n  }\n  {\n    INFO(\"Build a simple sparse matrix\");\n    const blaze::StaticMatrix<double, 2, 2> matrix{{4., 0.}, {3., 0.}};\n    size_t operator_invocations = 0;\n    const auto linear_operator =\n        [&matrix, &operator_invocations](\n            const gsl::not_null<blaze::StaticVector<double, 2>*> local_result,\n            const blaze::StaticVector<double, 2>& local_operand) {\n          *local_result = matrix * local_operand;\n          ++operator_invocations;\n        };\n    blaze::CompressedMatrix<double> matrix_representation(2, 2);\n    blaze::StaticVector<double, 2> operand_buffer(0.);\n    blaze::StaticVector<double, 2> result_buffer(0.);\n    build_matrix(make_not_null(&matrix_representation),\n                 make_not_null(&operand_buffer), make_not_null(&result_buffer),\n                 linear_operator);\n    CHECK(matrix_representation == matrix);\n    CHECK(operator_invocations == 2);\n    CHECK(matrix_representation.nonZeros() == 2);\n  }\n  {\n    INFO(\"Build matrix from a heterogeneous data structure\");\n    using SubdomainData = ::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n        1, tmpl::list<ScalarFieldTag>>;\n\n    const Matrix matrix_element{{4., 1., 1.}, {1., 1., 3.}, {0., 2., 0.}};\n    const Matrix matrix_overlap{{4., 1.}, {3., 1.}};\n    Matrix expected_matrix(5, 5, 0.);\n    blaze::submatrix(expected_matrix, 0, 0, 3, 3) = matrix_element;\n    blaze::submatrix(expected_matrix, 3, 3, 2, 2) = matrix_overlap;\n    const ::LinearSolver::Schwarz::OverlapId<1> overlap_id{\n        Direction<1>::lower_xi(), ElementId<1>{0}};\n    const std::array<std::reference_wrapper<const Matrix>, 1> matrices_element{\n        matrix_element};\n    const std::array<std::reference_wrapper<const Matrix>, 1> matrices_overlap{\n        matrix_overlap};\n    const auto linear_operator = [&matrices_element, &matrices_overlap,\n                                  &overlap_id](\n                                     const gsl::not_null<SubdomainData*> result,\n                                     const SubdomainData& operand) {\n      apply_matrices(make_not_null(&result->element_data), matrices_element,\n                     operand.element_data, Index<1>{3});\n      apply_matrices(make_not_null(&result->overlap_data.at(overlap_id)),\n                     matrices_overlap, operand.overlap_data.at(overlap_id),\n                     Index<1>{2});\n    };\n\n    blaze::DynamicMatrix<double> matrix_representation(5, 5);\n    SubdomainData operand_buffer{3};\n    get(get<ScalarFieldTag>(operand_buffer.element_data)) = DataVector(3, 0.);\n    operand_buffer.overlap_data.emplace(overlap_id,\n                                        typename SubdomainData::OverlapData{2});\n    get(get<ScalarFieldTag>(operand_buffer.overlap_data.at(overlap_id))) =\n        DataVector(2, 0.);\n    auto result_buffer = make_with_value<SubdomainData>(operand_buffer, 0.);\n\n    build_matrix(make_not_null(&matrix_representation),\n                 make_not_null(&operand_buffer), make_not_null(&result_buffer),\n                 linear_operator);\n    CHECK_ITERABLE_APPROX(matrix_representation, expected_matrix);\n  }\n}\n\n}  // namespace LinearSolver::Serial\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearSolver/Test_ExplicitInverse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <blaze/math/DynamicMatrix.h>\n#include <blaze/math/DynamicVector.h>\n#include <functional>\n#include <utility>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/LinearSolver/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/ExplicitInverse.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace helpers = TestHelpers::LinearSolver;\n\nnamespace {\ntemplate <typename DataType>\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n}  // namespace\n\nnamespace LinearSolver::Serial {\n\nSPECTRE_TEST_CASE(\"Unit.LinearSolver.Serial.ExplicitInverse\",\n                  \"[Unit][NumericalAlgorithms][LinearSolver]\") {\n  {\n    INFO(\"Solve a simple matrix\");\n    const blaze::DynamicMatrix<double> matrix{{4., 1.}, {3., 1.}};\n    const helpers::ApplyMatrix<double> linear_operator{matrix};\n    const blaze::DynamicVector<double> source{1., 2.};\n    const blaze::DynamicVector<double> expected_solution{-1., 5.};\n    blaze::DynamicVector<double> solution(2);\n    const ExplicitInverse<double> solver{\"Matrix\"};\n    const auto has_converged =\n        solver.solve(make_not_null(&solution), linear_operator, source);\n    REQUIRE(has_converged);\n    CHECK_ITERABLE_APPROX(solver.matrix_representation(), blaze::inv(matrix));\n    CHECK_ITERABLE_APPROX(solution, expected_solution);\n    std::ifstream matrix_file(\"Matrix.txt\");\n    std::string matrix_csv((std::istreambuf_iterator<char>(matrix_file)),\n                           std::istreambuf_iterator<char>());\n    CHECK(matrix_csv == \"4 1\\n3 1\\n\");\n    {\n      INFO(\"Resetting\");\n      ExplicitInverse<double> resetting_solver{};\n      resetting_solver.solve(make_not_null(&solution), linear_operator, source);\n      // Solving a different operator after resetting should work\n      resetting_solver.reset();\n      const blaze::DynamicMatrix<double> matrix2{{4., 1.}, {1., 3.}};\n      const helpers::ApplyMatrix<double> linear_operator2{matrix2};\n      const blaze::DynamicVector<double> expected_solution2{0.0909090909090909,\n                                                            0.6363636363636364};\n      resetting_solver.solve(make_not_null(&solution), linear_operator2,\n                             source);\n      CHECK_ITERABLE_APPROX(resetting_solver.matrix_representation(),\n                            blaze::inv(matrix2));\n      CHECK_ITERABLE_APPROX(solution, expected_solution2);\n      // Without resetting, the solver should keep applying the cached\n      // inverse even when solving a different operator\n      solver.solve(make_not_null(&solution), linear_operator2, source);\n      // Still the inverse of the operator we solved first\n      CHECK_ITERABLE_APPROX(solver.matrix_representation(), blaze::inv(matrix));\n      CHECK_ITERABLE_APPROX(solution, expected_solution);\n    }\n  }\n  {\n    INFO(\"Solve a complex matrix\");\n    const blaze::DynamicMatrix<std::complex<double>> matrix{\n        {std::complex<double>(1., 2.), std::complex<double>(2., -1.)},\n        {std::complex<double>(3., 4.), std::complex<double>(4., 1.)}};\n    const helpers::ApplyMatrix<std::complex<double>> linear_operator{matrix};\n    const blaze::DynamicVector<std::complex<double>> source{\n        std::complex<double>(1., 1.), std::complex<double>(2., -3.)};\n    const blaze::DynamicVector<std::complex<double>> expected_solution{\n        std::complex<double>(0.45, -1.4), std::complex<double>(-1.2, 0.15)};\n    blaze::DynamicVector<std::complex<double>> solution(2);\n    const ExplicitInverse<std::complex<double>> solver{\"Matrix\"};\n    const auto has_converged =\n        solver.solve(make_not_null(&solution), linear_operator, source);\n    REQUIRE(has_converged);\n    CHECK_ITERABLE_APPROX(solver.matrix_representation(), blaze::inv(matrix));\n    CHECK_ITERABLE_APPROX(solution, expected_solution);\n    std::ifstream matrix_file(\"Matrix.txt\");\n    std::string matrix_csv((std::istreambuf_iterator<char>(matrix_file)),\n                           std::istreambuf_iterator<char>());\n    CHECK(matrix_csv == \"(1,2) (2,-1)\\n(3,4) (4,1)\\n\");\n    {\n      INFO(\"With complex shift\");\n      const ExplicitInverse<std::complex<double>> solver_shifted{\n          std::nullopt, /* complex_shift */ 0.01};\n      const auto has_converged_shifted = solver_shifted.solve(\n          make_not_null(&solution), linear_operator, source);\n      REQUIRE(has_converged_shifted);\n      CHECK_ITERABLE_APPROX(\n          solver_shifted.matrix_representation(),\n          blaze::inv(matrix -\n                     std::complex<double>(0., 0.01) *\n                         blaze::IdentityMatrix<std::complex<double>>(2)));\n    }\n  }\n  {\n    INFO(\"Solve a heterogeneous data structure\");\n    using SubdomainData = ::LinearSolver::Schwarz::ElementCenteredSubdomainData<\n        1, tmpl::list<ScalarFieldTag<DataVector>>>;\n\n    const Matrix matrix_element{{4., 1., 1.}, {1., 1., 3.}, {0., 2., 0.}};\n    const Matrix matrix_overlap{{4., 1.}, {3., 1.}};\n    const ::LinearSolver::Schwarz::OverlapId<1> overlap_id{\n        Direction<1>::lower_xi(), ElementId<1>{0}};\n    const std::array<std::reference_wrapper<const Matrix>, 1> matrices_element{\n        matrix_element};\n    const std::array<std::reference_wrapper<const Matrix>, 1> matrices_overlap{\n        matrix_overlap};\n    const auto linear_operator = [&matrices_element, &matrices_overlap,\n                                  &overlap_id](\n                                     const gsl::not_null<SubdomainData*> result,\n                                     const SubdomainData& operand) {\n      apply_matrices(make_not_null(&result->element_data), matrices_element,\n                     operand.element_data, Index<1>{3});\n      apply_matrices(make_not_null(&result->overlap_data.at(overlap_id)),\n                     matrices_overlap, operand.overlap_data.at(overlap_id),\n                     Index<1>{2});\n    };\n\n    SubdomainData source{3};\n    get(get<ScalarFieldTag<DataVector>>(source.element_data)) =\n        DataVector{1., 2., 1.};\n    source.overlap_data.emplace(overlap_id,\n                                typename SubdomainData::OverlapData{2});\n    get(get<ScalarFieldTag<DataVector>>(source.overlap_data.at(overlap_id))) =\n        DataVector{1., 2.};\n    auto expected_solution = make_with_value<SubdomainData>(source, 0.);\n    get(get<ScalarFieldTag<DataVector>>(expected_solution.element_data)) =\n        DataVector{0., 0.5, 0.5};\n    get(get<ScalarFieldTag<DataVector>>(\n        expected_solution.overlap_data.at(overlap_id))) = DataVector{-1., 5.};\n\n    const ExplicitInverse<double> solver{};\n    auto solution = make_with_value<SubdomainData>(source, 0.);\n    solver.solve(make_not_null(&solution), linear_operator, source);\n    CHECK(solver.size() == 5);\n    Matrix expected_matrix(5, 5, 0.);\n    blaze::submatrix(expected_matrix, 0, 0, 3, 3) = matrix_element;\n    blaze::submatrix(expected_matrix, 3, 3, 2, 2) = matrix_overlap;\n    blaze::invert(expected_matrix);\n    CHECK_ITERABLE_APPROX(solver.matrix_representation(), expected_matrix);\n    CHECK_VARIABLES_APPROX(solution.element_data,\n                           expected_solution.element_data);\n    CHECK_VARIABLES_APPROX(solution.overlap_data.at(overlap_id),\n                           expected_solution.overlap_data.at(overlap_id));\n  }\n}\n\n}  // namespace LinearSolver::Serial\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearSolver/Test_Gmres.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/LinearSolver/TestHelpers.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/Gmres.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace helpers = TestHelpers::LinearSolver;\n\nnamespace LinearSolver::Serial {\n\nnamespace {\ntemplate <typename DataType>\nstruct ScalarField : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\ntemplate <typename Tag>\nstruct SomePrefix : db::PrefixTag, db::SimpleTag {\n  using type = tmpl::type_from<Tag>;\n  using tag = Tag;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.LinearSolver.Serial.Gmres\",\n                  \"[Unit][NumericalAlgorithms][LinearSolver]\") {\n  {\n    // [gmres_example]\n    INFO(\"Solve a symmetric 2x2 matrix\");\n    blaze::DynamicMatrix<double> matrix{{4., 1.}, {1., 3.}};\n    const helpers::ApplyMatrix<double> linear_operator{std::move(matrix)};\n    const blaze::DynamicVector<double> source{1., 2.};\n    blaze::DynamicVector<double> initial_guess_in_solution_out{2., 1.};\n    const blaze::DynamicVector<double> expected_solution{0.0909090909090909,\n                                                         0.6363636363636364};\n    const Convergence::Criteria convergence_criteria{2, 1.e-14, 0.};\n    const Gmres<blaze::DynamicVector<double>> gmres{convergence_criteria,\n                                                    ::Verbosity::Verbose};\n    CHECK_FALSE(gmres.has_preconditioner());\n    std::vector<double> recorded_residuals;\n    const auto has_converged =\n        gmres.solve(make_not_null(&initial_guess_in_solution_out),\n                    linear_operator, source, std::tuple{},\n                    [&recorded_residuals](\n                        const Convergence::HasConverged& local_has_converged) {\n                      recorded_residuals.push_back(\n                          local_has_converged.residual_magnitude());\n                    });\n    REQUIRE(has_converged);\n    CHECK(linear_operator.invocations == 3);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n    CHECK(has_converged.num_iterations() == 2);\n    CHECK(has_converged.residual_magnitude() <= 1.e-14);\n    CHECK(has_converged.initial_residual_magnitude() ==\n          approx(8.54400374531753));\n    CHECK_ITERABLE_APPROX(initial_guess_in_solution_out, expected_solution);\n    // The residuals should decrease monotonically\n    CHECK(recorded_residuals[0] == has_converged.initial_residual_magnitude());\n    for (size_t i = 1; i < has_converged.num_iterations(); ++i) {\n      CHECK(recorded_residuals[i] <= recorded_residuals[i - 1]);\n    }\n    // [gmres_example]\n    {\n      INFO(\"Check that a solved system terminates early\");\n      linear_operator.invocations = 0;\n      const auto second_has_converged =\n          gmres.solve(make_not_null(&initial_guess_in_solution_out),\n                      linear_operator, source);\n      REQUIRE(second_has_converged);\n      CHECK(linear_operator.invocations == 1);\n      CHECK(second_has_converged.reason() ==\n            Convergence::Reason::AbsoluteResidual);\n      CHECK(second_has_converged.num_iterations() == 0);\n      CHECK_ITERABLE_APPROX(initial_guess_in_solution_out, expected_solution);\n    }\n    const auto check_second_solve = [&linear_operator](\n                                        const auto& local_gmres) {\n      linear_operator.invocations = 0;\n      blaze::DynamicVector<double> local_initial_guess_in_solution_out{0., 0.};\n      const auto local_has_converged = local_gmres.solve(\n          make_not_null(&local_initial_guess_in_solution_out), linear_operator,\n          blaze::DynamicVector<double>{2, 1});\n      REQUIRE(local_has_converged);\n      // The initial guess is zero, so the initial operator should have been\n      // skipped, leaving only two operator applications for two iterations\n      CHECK(linear_operator.invocations == 2);\n      CHECK(local_has_converged.reason() ==\n            Convergence::Reason::AbsoluteResidual);\n      CHECK(local_has_converged.num_iterations() == 2);\n      const blaze::DynamicVector<double> expected_local_solution{\n          0.454545454545455, 0.181818181818182};\n      CHECK_ITERABLE_APPROX(local_initial_guess_in_solution_out,\n                            expected_local_solution);\n    };\n    {\n      INFO(\"Check two successive solves with different sources\");\n      check_second_solve(gmres);\n    }\n    {\n      INFO(\"Check the solver still works after serialization\");\n      const auto serialized_gmres = serialize_and_deserialize(gmres);\n      check_second_solve(serialized_gmres);\n    }\n    {\n      INFO(\"Check the solver still works after copying\");\n      // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n      const auto copied_gmres = gmres;\n      check_second_solve(copied_gmres);\n    }\n  }\n  {\n    INFO(\"Solve a non-symmetric 2x2 matrix\");\n    blaze::DynamicMatrix<double> matrix{{4., 1.}, {3., 1.}};\n    const helpers::ApplyMatrix<double> linear_operator{std::move(matrix)};\n    const blaze::DynamicVector<double> source{1., 2.};\n    blaze::DynamicVector<double> initial_guess_in_solution_out{2., 1.};\n    const blaze::DynamicVector<double> expected_solution{-1., 5.};\n    const Convergence::Criteria convergence_criteria{2, 1.e-14, 0.};\n    const Gmres<blaze::DynamicVector<double>> gmres{convergence_criteria,\n                                                    ::Verbosity::Verbose};\n    const auto has_converged = gmres.solve(\n        make_not_null(&initial_guess_in_solution_out), linear_operator, source);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n    CHECK(has_converged.num_iterations() == 2);\n    CHECK_ITERABLE_APPROX(initial_guess_in_solution_out, expected_solution);\n  }\n  {\n    INFO(\"Solve a complex 2x2 matrix\");\n    blaze::DynamicMatrix<std::complex<double>> matrix{\n        {std::complex<double>(1., 2.), std::complex<double>(2., -1.)},\n        {std::complex<double>(3., 4.), std::complex<double>(4., 1.)}};\n    const helpers::ApplyMatrix<std::complex<double>> linear_operator{\n        std::move(matrix)};\n    const blaze::DynamicVector<std::complex<double>> source{\n        std::complex<double>(1., 1.), std::complex<double>(2., -3.)};\n    blaze::DynamicVector<std::complex<double>> initial_guess_in_solution_out{\n        0., 0.};\n    const blaze::DynamicVector<std::complex<double>> expected_solution{\n        std::complex<double>(0.45, -1.4), std::complex<double>(-1.2, 0.15)};\n    const Convergence::Criteria convergence_criteria{2, 1.e-14, 0.};\n    const Gmres<blaze::DynamicVector<std::complex<double>>> gmres{\n        convergence_criteria, ::Verbosity::Verbose};\n    const auto has_converged = gmres.solve(\n        make_not_null(&initial_guess_in_solution_out), linear_operator, source);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n    CHECK(has_converged.num_iterations() == 2);\n    CHECK_ITERABLE_APPROX(initial_guess_in_solution_out, expected_solution);\n  }\n  {\n    INFO(\"Solve a matrix-free linear operator with Variables\");\n    using Vars = Variables<tmpl::list<ScalarField<DataVector>>>;\n    constexpr size_t num_points = 2;\n    // This also tests that the linear operator can be a lambda\n    const auto linear_operator = [](const gsl::not_null<Vars*> result,\n                                    const Vars& operand) {\n      if (result->number_of_grid_points() != num_points) {\n        result->initialize(num_points);\n      }\n      const auto& data = get(get<ScalarField<DataVector>>(operand));\n      get(get<ScalarField<DataVector>>(*result)) =\n          DataVector{data[0] * 4. + data[1], data[0] * 3. + data[1]};\n    };\n    // Adding a prefix to make sure prefixed sources work as well\n    Variables<tmpl::list<SomePrefix<ScalarField<DataVector>>>> source{\n        num_points};\n    get(get<SomePrefix<ScalarField<DataVector>>>(source)) = DataVector{1., 2.};\n    Vars initial_guess_in_solution_out{num_points};\n    get(get<ScalarField<DataVector>>(initial_guess_in_solution_out)) =\n        DataVector{2., 1.};\n    const DataVector expected_solution{-1., 5.};\n    const Convergence::Criteria convergence_criteria{2, 1.e-14, 0.};\n    const Gmres<Vars> gmres{convergence_criteria, ::Verbosity::Verbose};\n    const auto has_converged = gmres.solve(\n        make_not_null(&initial_guess_in_solution_out), linear_operator, source);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n    CHECK(has_converged.num_iterations() == 2);\n    CHECK_ITERABLE_APPROX(\n        get(get<ScalarField<DataVector>>(initial_guess_in_solution_out)),\n        expected_solution);\n  }\n  {\n    INFO(\"Solve a complex matrix-free linear operator with Variables\");\n    using Vars = Variables<tmpl::list<ScalarField<ComplexDataVector>>>;\n    constexpr size_t num_points = 2;\n    const auto linear_operator = [](const gsl::not_null<Vars*> result,\n                                    const Vars& operand) {\n      if (result->number_of_grid_points() != num_points) {\n        result->initialize(num_points);\n      }\n      const auto& data = get(get<ScalarField<ComplexDataVector>>(operand));\n      get(get<ScalarField<ComplexDataVector>>(*result)) =\n          ComplexDataVector{data[0] * std::complex<double>(1., 2.) +\n                                data[1] * std::complex<double>(2., -1.),\n                            data[0] * std::complex<double>(3., 4.) +\n                                data[1] * std::complex<double>(4., 1.)};\n    };\n    Variables<tmpl::list<SomePrefix<ScalarField<ComplexDataVector>>>> source{\n        num_points};\n    get(get<SomePrefix<ScalarField<ComplexDataVector>>>(source)) =\n        ComplexDataVector{std::complex<double>(1., 1.),\n                          std::complex<double>(2., -3.)};\n    Vars initial_guess_in_solution_out{num_points};\n    get(get<ScalarField<ComplexDataVector>>(initial_guess_in_solution_out)) =\n        ComplexDataVector{std::complex<double>(0., 0.),\n                          std::complex<double>(0., 0.)};\n    const ComplexDataVector expected_solution{std::complex<double>(0.45, -1.4),\n                                              std::complex<double>(-1.2, 0.15)};\n    const Convergence::Criteria convergence_criteria{2, 1.e-14, 0.};\n    const Gmres<Vars> gmres{convergence_criteria, ::Verbosity::Verbose};\n    const auto has_converged = gmres.solve(\n        make_not_null(&initial_guess_in_solution_out), linear_operator, source);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n    CHECK(has_converged.num_iterations() == 2);\n    CHECK_ITERABLE_APPROX(\n        get(get<ScalarField<ComplexDataVector>>(initial_guess_in_solution_out)),\n        expected_solution);\n  }\n  {\n    INFO(\"Restarting\");\n    blaze::DynamicMatrix<double> matrix{\n        {4., 1., 1.}, {1., 1., 3.}, {0., 2., 0.}};\n    const helpers::ApplyMatrix<double> linear_operator{std::move(matrix)};\n    const blaze::DynamicVector<double> source{1., 2., 1.};\n    blaze::DynamicVector<double> initial_guess_in_solution_out{2., 1., 0.};\n    const blaze::DynamicVector<double> expected_solution{0., 0.5, 0.5};\n    const Convergence::Criteria convergence_criteria{100, 1.e-14, 0.};\n    // Restart every other iteration. The algorithm would converge in 3\n    // iterations without restarting, so restarting is of course ridiculously\n    // inefficient for this problem size. The number of iterations rises to 59.\n    const size_t restart = 2;\n    const Gmres<blaze::DynamicVector<double>> gmres{\n        convergence_criteria, ::Verbosity::Verbose, restart};\n    const auto has_converged = gmres.solve(\n        make_not_null(&initial_guess_in_solution_out), linear_operator, source);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n    CHECK(has_converged.num_iterations() == 59);\n    CHECK_ITERABLE_APPROX(initial_guess_in_solution_out, expected_solution);\n  }\n  {\n    INFO(\"Preconditioning\");\n    blaze::DynamicMatrix<double> matrix{{4., 1.}, {1., 3.}};\n    const helpers::ApplyMatrix<double> linear_operator{std::move(matrix)};\n    const blaze::DynamicVector<double> source{1., 2.};\n    const blaze::DynamicVector<double> expected_solution{0.0909090909090909,\n                                                         0.6363636363636364};\n    const Convergence::Criteria convergence_criteria{2, 1.e-14, 0.};\n    const auto check_solve = [&linear_operator, &source, &expected_solution](\n                                 const auto& local_gmres,\n                                 const size_t expected_num_iterations) {\n      REQUIRE(local_gmres.has_preconditioner());\n      blaze::DynamicVector<double> local_initial_guess_in_solution_out{2., 1.};\n      std::vector<double> local_recorded_residuals;\n      const auto local_has_converged = local_gmres.solve(\n          make_not_null(&local_initial_guess_in_solution_out), linear_operator,\n          source, std::tuple{},\n          [&local_recorded_residuals](\n              const Convergence::HasConverged& recorded_has_converged) {\n            local_recorded_residuals.push_back(\n                recorded_has_converged.residual_magnitude());\n          });\n      CAPTURE(local_recorded_residuals);\n      REQUIRE(local_has_converged);\n      CHECK(local_has_converged.reason() ==\n            Convergence::Reason::AbsoluteResidual);\n      CHECK(local_has_converged.num_iterations() == expected_num_iterations);\n      CHECK_ITERABLE_APPROX(local_initial_guess_in_solution_out,\n                            expected_solution);\n    };\n    {\n      INFO(\"Exact inverse preconditioner\");\n      // Use the exact inverse of the matrix as preconditioner. This\n      // should solve the problem in 1 iteration.\n      helpers::ExactInversePreconditioner preconditioner{};\n      const Gmres<blaze::DynamicVector<double>,\n                  helpers::ExactInversePreconditioner>\n          preconditioned_gmres{convergence_criteria, ::Verbosity::Verbose,\n                               std::nullopt, std::move(preconditioner)};\n      check_solve(preconditioned_gmres, 1);\n      // Check a second solve with the same solver and preconditioner works\n      check_solve(preconditioned_gmres, 1);\n      {\n        INFO(\"Check that serialization preserves the preconditioner\");\n        const auto serialized_gmres =\n            serialize_and_deserialize(preconditioned_gmres);\n        check_solve(serialized_gmres, 1);\n      }\n      {\n        INFO(\"Check that copying preserves the preconditioner\");\n        // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n        const auto copied_gmres = preconditioned_gmres;\n        check_solve(copied_gmres, 1);\n      }\n    }\n    {\n      INFO(\"Diagonal (Jacobi) preconditioner\");\n      // Use the inverse of the diagonal as preconditioner.\n      helpers::JacobiPreconditioner preconditioner{};\n      const Gmres<blaze::DynamicVector<double>, helpers::JacobiPreconditioner>\n          preconditioned_gmres{convergence_criteria, ::Verbosity::Verbose,\n                               std::nullopt, std::move(preconditioner)};\n      check_solve(preconditioned_gmres, 2);\n    }\n    {\n      INFO(\"Richardson preconditioner\");\n      helpers::RichardsonPreconditioner preconditioner{\n          // The optimal relaxation parameter for SPD matrices is 2 / (l_max +\n          // l_min) where l_max and l_min are the largest and smallest\n          // eigenvalues of the linear operator (see\n          // `LinearSolver::Richardson::Richardson`).\n          0.2857142857142857,\n          // Run two Richardson iterations\n          2};\n      const Gmres<blaze::DynamicVector<double>,\n                  helpers::RichardsonPreconditioner>\n          preconditioned_gmres{convergence_criteria, ::Verbosity::Verbose,\n                               std::nullopt, std::move(preconditioner)};\n      check_solve(preconditioned_gmres, 1);\n    }\n    {\n      INFO(\"Nested linear solver as preconditioner\");\n      // Running another GMRES solver for 2 iterations as preconditioner. It\n      // should already solve the problem, so the preconditioned solve only\n      // needs a single iteration.\n      const Gmres<blaze::DynamicVector<double>,\n                  Gmres<blaze::DynamicVector<double>>>\n          preconditioned_gmres{convergence_criteria,\n                               ::Verbosity::Verbose,\n                               std::nullopt,\n                               {{{2, 0., 0.}, ::Verbosity::Verbose}}};\n      check_solve(preconditioned_gmres, 1);\n    }\n    {\n      INFO(\"Nested factory-created linear solver as preconditioner\");\n      // Also running another GMRES solver as preconditioner, but passing it as\n      // a factory-created abstract `LinearSolver` type.\n      using LinearSolverRegistrars =\n          tmpl::list<Registrars::Gmres<blaze::DynamicVector<double>>>;\n      using LinearSolverFactory = LinearSolver<LinearSolverRegistrars>;\n      const Gmres<blaze::DynamicVector<double>, LinearSolverFactory,\n                  LinearSolverRegistrars>\n          preconditioned_gmres{\n              convergence_criteria, ::Verbosity::Verbose, std::nullopt,\n              std::make_unique<\n                  Gmres<blaze::DynamicVector<double>, LinearSolverFactory,\n                        LinearSolverRegistrars>>(\n                  Convergence::Criteria{2, 0., 0.}, ::Verbosity::Verbose)};\n      check_solve(preconditioned_gmres, 1);\n      {\n        INFO(\"Check that serialization preserves the preconditioner\");\n        register_derived_classes_with_charm<LinearSolverFactory>();\n        const auto serialized_gmres =\n            serialize_and_deserialize(preconditioned_gmres);\n        check_solve(serialized_gmres, 1);\n      }\n      {\n        INFO(\"Check that copying preserves the preconditioner\");\n        register_derived_classes_with_charm<LinearSolverFactory>();\n        // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n        const auto copied_gmres = preconditioned_gmres;\n        check_solve(copied_gmres, 1);\n      }\n    }\n  }\n  {\n    INFO(\"Option-creation\");\n    {\n      const auto solver =\n          TestHelpers::test_creation<Gmres<blaze::DynamicVector<double>>>(\n              \"ConvergenceCriteria:\\n\"\n              \"  MaxIterations: 2\\n\"\n              \"  AbsoluteResidual: 0.1\\n\"\n              \"  RelativeResidual: 0.5\\n\"\n              \"Restart: 50\\n\"\n              \"Verbosity: Verbose\\n\");\n      CHECK(solver.convergence_criteria() ==\n            Convergence::Criteria{2, 0.1, 0.5});\n      CHECK(solver.restart() == 50);\n      CHECK(solver.verbosity() == ::Verbosity::Verbose);\n      CHECK_FALSE(solver.has_preconditioner());\n    }\n    {\n      const auto solver = TestHelpers::test_creation<Gmres<\n          blaze::DynamicVector<double>, helpers::ExactInversePreconditioner>>(\n          \"ConvergenceCriteria:\\n\"\n          \"  MaxIterations: 2\\n\"\n          \"  AbsoluteResidual: 0.1\\n\"\n          \"  RelativeResidual: 0.5\\n\"\n          \"Restart: None\\n\"\n          \"Verbosity: Verbose\\n\"\n          \"Preconditioner: None\\n\");\n      CHECK(solver.convergence_criteria() ==\n            Convergence::Criteria{2, 0.1, 0.5});\n      CHECK(solver.restart() == 2);\n      CHECK(solver.verbosity() == ::Verbosity::Verbose);\n      CHECK_FALSE(solver.has_preconditioner());\n    }\n    {\n      const auto solver = TestHelpers::test_creation<Gmres<\n          blaze::DynamicVector<double>, helpers::ExactInversePreconditioner>>(\n          \"ConvergenceCriteria:\\n\"\n          \"  MaxIterations: 2\\n\"\n          \"  AbsoluteResidual: 0.1\\n\"\n          \"  RelativeResidual: 0.5\\n\"\n          \"Restart: None\\n\"\n          \"Verbosity: Verbose\\n\"\n          \"Preconditioner:\\n\");\n      CHECK(solver.convergence_criteria() ==\n            Convergence::Criteria{2, 0.1, 0.5});\n      CHECK(solver.restart() == 2);\n      CHECK(solver.verbosity() == ::Verbosity::Verbose);\n      CHECK(solver.has_preconditioner());\n    }\n    {\n      using LinearSolverRegistrars =\n          tmpl::list<Registrars::Gmres<blaze::DynamicVector<double>>>;\n      using LinearSolverFactory = LinearSolver<LinearSolverRegistrars>;\n      const auto solver =\n          TestHelpers::test_creation<std::unique_ptr<LinearSolverFactory>>(\n              \"Gmres:\\n\"\n              \"  ConvergenceCriteria:\\n\"\n              \"    MaxIterations: 2\\n\"\n              \"    AbsoluteResidual: 0.1\\n\"\n              \"    RelativeResidual: 0.5\\n\"\n              \"  Restart: 50\\n\"\n              \"  Verbosity: Verbose\\n\"\n              \"  Preconditioner:\\n\"\n              \"    Gmres:\\n\"\n              \"      ConvergenceCriteria:\\n\"\n              \"        MaxIterations: 1\\n\"\n              \"        AbsoluteResidual: 0.5\\n\"\n              \"        RelativeResidual: 0.9\\n\"\n              \"      Restart: None\\n\"\n              \"      Verbosity: Verbose\\n\"\n              \"      Preconditioner: None\\n\");\n      REQUIRE(solver);\n      using Derived = Gmres<blaze::DynamicVector<double>, LinearSolverFactory,\n                            LinearSolverRegistrars>;\n      REQUIRE_FALSE(nullptr == dynamic_cast<const Derived*>(solver.get()));\n      const auto& derived = dynamic_cast<const Derived&>(*solver);\n      CHECK(derived.convergence_criteria() ==\n            Convergence::Criteria{2, 0.1, 0.5});\n      CHECK(derived.restart() == 50);\n      CHECK(derived.verbosity() == ::Verbosity::Verbose);\n      REQUIRE(derived.has_preconditioner());\n      REQUIRE_FALSE(nullptr ==\n                    dynamic_cast<const Derived*>(&derived.preconditioner()));\n      const auto& preconditioner =\n          dynamic_cast<const Derived&>(derived.preconditioner());\n      CHECK(preconditioner.convergence_criteria() ==\n            Convergence::Criteria{1, 0.5, 0.9});\n      CHECK(preconditioner.restart() == 1);\n      CHECK(preconditioner.verbosity() == ::Verbosity::Verbose);\n      CHECK_FALSE(preconditioner.has_preconditioner());\n      {\n        INFO(\"Copy semantics\");\n        // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)\n        const auto copied_solver = derived;\n        CHECK(copied_solver.convergence_criteria() ==\n              Convergence::Criteria{2, 0.1, 0.5});\n        CHECK(copied_solver.restart() == 50);\n        CHECK(copied_solver.verbosity() == ::Verbosity::Verbose);\n        REQUIRE(copied_solver.has_preconditioner());\n        REQUIRE_FALSE(nullptr == dynamic_cast<const Derived*>(\n                                     &copied_solver.preconditioner()));\n        const auto& copied_preconditioner =\n            dynamic_cast<const Derived&>(copied_solver.preconditioner());\n        CHECK(copied_preconditioner.convergence_criteria() ==\n              Convergence::Criteria{1, 0.5, 0.9});\n        CHECK(copied_preconditioner.restart() == 1);\n        CHECK(copied_preconditioner.verbosity() == ::Verbosity::Verbose);\n        CHECK_FALSE(copied_preconditioner.has_preconditioner());\n      }\n    }\n  }\n}\n\n}  // namespace LinearSolver::Serial\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearSolver/Test_InnerProduct.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <blaze/math/DynamicVector.h>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename DataType>\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\ntemplate <typename DataType>\nstruct AnotherScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.LinearSolver.InnerProduct\",\n                  \"[Unit][NumericalAlgorithms][LinearSolver]\") {\n  {\n    INFO(\"Blaze vector\");\n    const blaze::DynamicVector<double> lhs{1., 0., 2.};\n    const blaze::DynamicVector<double> rhs{1.5, 1., 3.};\n    CHECK(LinearSolver::inner_product(lhs, rhs) == dot(lhs, rhs));\n    CHECK(LinearSolver::magnitude_square(lhs) == 5.);\n  }\n  {\n    INFO(\"Variables\");\n    const Variables<tmpl::list<ScalarFieldTag<DataVector>>> vars{3, 1.};\n    const Variables<tmpl::list<AnotherScalarFieldTag<DataVector>>> other_vars{\n        3, 2.};\n    CHECK(LinearSolver::inner_product(vars, other_vars) == 6.);\n    CHECK(LinearSolver::magnitude_square(vars) == 3.);\n  }\n  {\n    INFO(\"Complex Variables\");\n    const Variables<tmpl::list<ScalarFieldTag<ComplexDataVector>>> vars{\n        3, std::complex<double>(1., 2.)};\n    const Variables<tmpl::list<AnotherScalarFieldTag<ComplexDataVector>>>\n        other_vars{3, std::complex<double>(2., 3.)};\n    CHECK(LinearSolver::inner_product(vars, other_vars) ==\n          std::complex<double>(24., -3.));\n    CHECK(LinearSolver::magnitude_square(vars) == 15.);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/LinearSolver/Test_Lapack.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/Lapack.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\ntemplate <typename Generator>\nvoid test_square_general_matrix_linear_solve(\n    const gsl::not_null<Generator*> generator) {\n  UniformCustomDistribution<size_t> size_dist(2, 6);\n  // the size of the matrix in the linear solve\n  const size_t rows = size_dist(*generator);\n  const size_t columns = rows;\n\n  // number of distinct rhs that will be solved, as LAPACK can efficiently do\n  // several linear solves at once\n  const size_t number_of_rhs = size_dist(*generator);\n  // refer to the canonical linear system A x = b\n\n  UniformCustomDistribution<double> value_dist(0.1, 0.5);\n  // the vector x\n  auto expected_solution_vector = make_with_random_values<DataVector>(\n      generator, make_not_null(&value_dist), number_of_rhs * rows);\n  // the matrix A\n  Matrix operator_matrix{rows, columns};\n  for (size_t row = 0; row < rows; ++row) {\n    for (size_t column = 0; column < columns; ++column) {\n      operator_matrix(row, column) = value_dist(*generator);\n    }\n  }\n  for (size_t i = 0; i < rows; ++i) {\n    operator_matrix(i, i) += 1.0;\n  }\n  CAPTURE(operator_matrix);\n  // the vector b\n  auto input_vector = apply_matrices<DataVector, Matrix>(\n      {{operator_matrix, Matrix{}}}, expected_solution_vector,\n      Index<2>{rows, number_of_rhs});\n  CAPTURE(input_vector);\n  // version which copies the matrix, and preserves the input\n  DataVector solution_vector{number_of_rhs * rows, 0.0};\n  lapack::general_matrix_linear_solve(make_not_null(&solution_vector),\n                                      operator_matrix, input_vector);\n  CHECK_ITERABLE_APPROX(solution_vector, expected_solution_vector);\n\n  // solve only a subset of the rhs's given\n  DataVector single_column_solution_vector{rows};\n  lapack::general_matrix_linear_solve(\n      make_not_null(&single_column_solution_vector), operator_matrix,\n      input_vector, 1);\n  for (size_t i = 0; i < rows; ++i) {\n    CHECK(single_column_solution_vector[i] ==\n          approx(expected_solution_vector[i]));\n  }\n\n  // version which copies the matrix, overwrites the input\n  solution_vector = input_vector;\n  lapack::general_matrix_linear_solve(make_not_null(&solution_vector),\n                                      operator_matrix);\n  CHECK_ITERABLE_APPROX(solution_vector, expected_solution_vector);\n\n  // solve only a subset of the rhs's given\n  solution_vector = input_vector;\n  lapack::general_matrix_linear_solve(make_not_null(&solution_vector),\n                                      operator_matrix, 1);\n  for (size_t i = 0; i < rows; ++i) {\n    CHECK(solution_vector[i] == approx(expected_solution_vector[i]));\n  }\n  // the rest of the entries should not have been changed by the LAPACK call.\n  for (size_t i = rows; i < rows * number_of_rhs; ++i) {\n    CHECK(solution_vector[i] == input_vector[i]);\n  }\n\n  // version which overwrites the matrix but preserves the input\n  Matrix operator_matrix_copy = operator_matrix;\n  lapack::general_matrix_linear_solve(make_not_null(&solution_vector),\n                                      make_not_null(&operator_matrix_copy),\n                                      input_vector);\n  CHECK_ITERABLE_APPROX(solution_vector, expected_solution_vector);\n  CHECK(operator_matrix_copy != operator_matrix);\n\n  // version which overwrites the matrix and the input\n  operator_matrix_copy = operator_matrix;\n  solution_vector = input_vector;\n  lapack::general_matrix_linear_solve(make_not_null(&solution_vector),\n                                      make_not_null(&operator_matrix));\n  CHECK_ITERABLE_APPROX(solution_vector, expected_solution_vector);\n  CHECK(operator_matrix_copy != operator_matrix);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.LinearSolver.Lapack\",\n                  \"[Unit][NumericalAlgorithms][LinearSolver]\") {\n  MAKE_GENERATOR(gen);\n  {\n    INFO(\"Test general linear solve on invertible square matrix\");\n    test_square_general_matrix_linear_solve(make_not_null(&gen));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/OdeIntegration/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_OdeIntegration\")\n\nset(LIBRARY_SOURCES\n  Test_OdeIntegration.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  OdeIntegration\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/OdeIntegration/Test_OdeIntegration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/OdeIntegration/OdeIntegration.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.OdeIntegration\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  // explicit stepper for an array of fundamental types\n  // [explicit_fundamental_array_system]\n  const auto oscillatory_array_system = [](const std::array<double, 2>& state,\n                                           std::array<double, 2>& dt_state,\n                                           const double /*time*/) {\n    dt_state[0] = state[1];\n    dt_state[1] = -state[0];\n  };\n  // [explicit_fundamental_array_system]\n  // [explicit_fundamental_stepper_construction]\n  boost::numeric::odeint::runge_kutta4<std::array<double, 2>>\n      fixed_array_stepper;\n  // [explicit_fundamental_stepper_construction]\n  // [explicit_fundamental_stepper_use]\n  const double fixed_step = 0.001;\n  std::array<double, 2> fixed_step_array_state{{1.0, 0.0}};\n  for (size_t i = 0; i < 1000; ++i) {\n    CHECK(approx(fixed_step_array_state[0]) == cos(i * fixed_step));\n    CHECK(approx(fixed_step_array_state[1]) == -sin(i * fixed_step));\n    fixed_array_stepper.do_step(oscillatory_array_system,\n                                fixed_step_array_state, i * fixed_step,\n                                fixed_step);\n  }\n  // [explicit_fundamental_stepper_use]\n\n  // dense output stepper for a fundamental type\n  // [dense_output_fundamental_system]\n  const auto quadratic_system = [](const double /*state*/, double& dt_state,\n                                   const double time) {\n    dt_state = 2.0 * time;\n  };\n  // [dense_output_fundamental_system]\n  // [dense_output_fundamental_construction]\n  boost::numeric::odeint::dense_output_runge_kutta<\n      boost::numeric::odeint::controlled_runge_kutta<\n          boost::numeric::odeint::runge_kutta_dopri5<double>>>\n      dense_fundamental_stepper = boost::numeric::odeint::make_dense_output(\n          1.0e-12, 1.0e-12,\n          boost::numeric::odeint::runge_kutta_dopri5<double>{});\n  dense_fundamental_stepper.initialize(1.0, 1.0, 0.01);\n  // [dense_output_fundamental_construction]\n  // [dense_output_fundamental_stepper_use]\n  std::pair<double, double> step_range =\n      dense_fundamental_stepper.do_step(quadratic_system);\n  double state_output;\n  for(size_t i = 0; i < 50; ++i) {\n    while(step_range.second < 1.0 + 0.01 * square(i) ) {\n      step_range = dense_fundamental_stepper.do_step(quadratic_system);\n    }\n    dense_fundamental_stepper.calc_state(1.0 + 0.01 * square(i), state_output);\n    CHECK(approx(state_output) == square(1.0 + 0.01 * square(i)));\n  }\n  // [dense_output_fundamental_stepper_use]\n  // dense output stepper for an array of SpECTRE vectors\n  // [dense_output_vector_stepper]\n  const auto oscillatory_vector_system =\n      [](const std::array<DataVector, 2>& state,\n         std::array<DataVector, 2>& dt_state, const double /*time*/) {\n        dt_state[0] = state[1];\n        dt_state[1] = -state[0];\n      };\n  boost::numeric::odeint::dense_output_runge_kutta<\n      boost::numeric::odeint::controlled_runge_kutta<\n          boost::numeric::odeint::runge_kutta_dopri5<\n              std::array<DataVector, 2>>>>\n      dense_stepper =\n      boost::numeric::odeint::make_dense_output(1.0e-14, 1.0e-14,\n                            boost::numeric::odeint::runge_kutta_dopri5<\n                                std::array<DataVector, 2>>{});\n  const DataVector initial_vector = {{0.1, 0.2, 0.3, 0.4, 0.5}};\n  dense_stepper.initialize(\n      std::array<DataVector, 2>{{initial_vector, DataVector{5, 0.0}}}, 0.0,\n      0.01);\n  std::array<DataVector, 2> dense_output_vector_state{\n      {DataVector{5}, DataVector{5}}};\n  step_range = dense_stepper.do_step(oscillatory_vector_system);\n  for(size_t i = 0; i < 100; ++i) {\n    while (step_range.second < 0.01 * i) {\n      step_range = dense_stepper.do_step(oscillatory_vector_system);\n    }\n    dense_stepper.calc_state(0.01 * i, dense_output_vector_state);\n    for(size_t j = 0; j < 5; ++j) {\n      CHECK(approx(dense_output_vector_state[0][j]) ==\n            (0.1 * j + 0.1) * cos(0.01 * i));\n      CHECK(approx(dense_output_vector_state[1][j]) ==\n            -(0.1 * j + 0.1) * sin(0.01 * i));\n    }\n  }\n  // [dense_output_vector_stepper]\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/RootFinding/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_RootFinding\")\n\nset(LIBRARY_SOURCES\n  Test_GslMultiRoot.cpp\n  Test_QuadraticEquation.cpp\n  Test_RootBracketing.cpp\n  Test_TOMS748.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  Logging\n  RootFinding\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/RootFinding/Test_GslMultiRoot.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n#include <vector>\n\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/RootFinding/GslMultiRoot.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace {\n\n// Rosenbrock system of equations:\n// f_1 (x0, x1) = a (1 - x0)\n// f_2 (x0, x1) = b (x1 - x0**2)\nclass Rosenbrock {\n public:\n  Rosenbrock(const double a, const double b) : a_(a), b_(b) {}\n  std::array<double, 2> operator()(const std::array<double, 2>& x) const {\n    const double y0 = a_ * (1.0 - x[0]);\n    const double y1 = b_ * (x[1] - x[0] * x[0]);\n    return std::array<double, 2>{{y0, y1}};\n  }\n  std::array<std::array<double, 2>, 2> jacobian(\n      const std::array<double, 2>& x) const {\n    return std::array<std::array<double, 2>, 2>{\n        {{{-a_, 0.0}}, {{-2.0 * b_ * x[0], b_}}}};\n  }\n\n private:\n  double a_, b_;\n};\n\nclass RosenbrockNoJac {\n public:\n  RosenbrockNoJac(const double a, const double b) : a_(a), b_(b) {}\n  std::array<double, 2> operator()(const std::array<double, 2>& x) const {\n    const double y0 = a_ * (1.0 - x[0]);\n    const double y1 = b_ * (x[1] - x[0] * x[0]);\n    return std::array<double, 2>{{y0, y1}};\n  }\n\n private:\n  double a_, b_;\n};\n\n// Bad system of equations:\n// f_1 (x0, x1) = a*x0**2 + b*x1**2 + c**2\n// f_2 (x0, x1) = a*x0 + b*x1 + c\nclass BadFunction {\n public:\n  BadFunction(const double a, const double b, const double c)\n      : a_(a), b_(b), c_(c) {}\n  std::array<double, 2> operator()(const std::array<double, 2>& x) const {\n    const double y0 = a_ * x[0] * x[0] + b_ * x[1] * x[1] + c_ * c_;\n    const double y1 = a_ * x[0] + b_ * x[1] + c_;\n    return std::array<double, 2>{{y0, y1}};\n  }\n\n private:\n  double a_, b_, c_;\n};\n\ntemplate <typename Function>\nvoid test_gsl_multiroot(const RootFinder::StoppingCondition& condition,\n                        const Function& func,\n                        const std::array<double, 2> initial_guess) {\n  const double max_absolute_tolerance = 0.0;\n  const int maximum_iterations = 20;\n  const ::Verbosity verbosity = ::Verbosity::Silent;\n\n  std::vector<RootFinder::Method> methods_list{RootFinder::Method::Newton,\n                                               RootFinder::Method::Hybrid,\n                                               RootFinder::Method::Hybrids};\n  for (const auto& method : methods_list) {\n    std::array<double, 2> roots_array = RootFinder::gsl_multiroot(\n        func, initial_guess, condition, maximum_iterations, verbosity,\n        max_absolute_tolerance, method);\n    CHECK(roots_array[0] == approx(1.0));\n    CHECK(roots_array[1] == approx(1.0));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.RootFinding.GslMultiRoot\",\n                  \"[NumericalAlgorithms][RootFinding][Unit]\") {\n  const std::array<double, 2> good_initial_guess{{-10.0, -5.0}};\n  const std::array<double, 2> perfect_initial_guess{{1.0, 1.0}};\n  const Rosenbrock function_and_jac{1.0, 10.0};\n  const RosenbrockNoJac function{1.0, 10.0};\n\n  test_gsl_multiroot(\n      RootFinder::StoppingConditions::Convergence(1.0e-14, 1.0e-13),\n      function_and_jac, good_initial_guess);\n  test_gsl_multiroot(\n      RootFinder::StoppingConditions::Convergence(1.0e-14, 1.0e-13), function,\n      good_initial_guess);\n  test_gsl_multiroot(RootFinder::StoppingConditions::Residual(1.0e-14),\n                     function_and_jac, good_initial_guess);\n  test_gsl_multiroot(RootFinder::StoppingConditions::Residual(1.0e-14),\n                     function, good_initial_guess);\n\n  test_gsl_multiroot(\n      RootFinder::StoppingConditions::Convergence(1.0e-14, 1.0e-13),\n      function_and_jac, perfect_initial_guess);\n  test_gsl_multiroot(\n      RootFinder::StoppingConditions::Convergence(1.0e-14, 1.0e-13), function,\n      perfect_initial_guess);\n  test_gsl_multiroot(RootFinder::StoppingConditions::Residual(1.0e-14),\n                     function_and_jac, perfect_initial_guess);\n  test_gsl_multiroot(RootFinder::StoppingConditions::Residual(1.0e-14),\n                     function, perfect_initial_guess);\n\n  CHECK(get_output(RootFinder::Method::Hybrids) == \"Hybrids\");\n  CHECK(get_output(RootFinder::Method::Hybrid) == \"Hybrid\");\n  CHECK(get_output(RootFinder::Method::Newton) == \"Newton\");\n  CHECK(get_output(RootFinder::StoppingConditions::Convergence(1.5, 2.5)) ==\n        \"Convergence(abs=1.5, rel=2.5)\");\n  CHECK(get_output(RootFinder::StoppingConditions::Residual(1.5)) ==\n        \"Residual(abs=1.5)\");\n\n  CHECK_THROWS_MATCHES(\n      ([]() {\n        const std::array<double, 2> bad_initial_guess{{9.0e3, 2.0e5}};\n        test_gsl_multiroot(\n            RootFinder::StoppingConditions::Convergence(1.0e-14, 1.0e-13),\n            BadFunction{1.0, 1.0, 1.0}, bad_initial_guess);\n      }()),\n      convergence_error,\n      Catch::Matchers::MessageMatches(Catch::Matchers::ContainsSubstring(\n          \"The root find failed and was not forgiven. An exception has been \"\n          \"thrown.\\n\"\n          \"The gsl error returned is: the iteration has not converged yet\\n\"\n          \"Verbosity: Silent\\n\"\n          \"Method: Newton\\n\"\n          \"StoppingCondition: Convergence(abs=1e-14, rel=1e-13)\\n\"\n          \"Maximum absolute tolerance: 0\\n\"\n          \"Maximum number of iterations: 20\\n\"\n          \"Number of iterations reached: 20\\n\"\n          \"The last value of f in the root solver is:\\n\"\n          \"1.74\")));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/RootFinding/Test_QuadraticEquation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <typename T>\nvoid test_smallest_root_greater_than_value_within_roundoff(\n    const T& used_for_size) {\n  const auto a = make_with_value<T>(used_for_size, 2.0);\n  const auto b = make_with_value<T>(used_for_size, -11.0);\n  const auto c = make_with_value<T>(used_for_size, 5.0);\n  const auto expected_root_1 = make_with_value<T>(used_for_size, 0.5);\n  const auto expected_root_2 = make_with_value<T>(used_for_size, 5.0);\n\n  auto root = smallest_root_greater_than_value_within_roundoff(a, b, c, 0.3);\n  CHECK_ITERABLE_APPROX(expected_root_1, root);\n  root = smallest_root_greater_than_value_within_roundoff(a, b, c, 0.5);\n  CHECK_ITERABLE_APPROX(expected_root_1, root);\n  root = smallest_root_greater_than_value_within_roundoff(a, b, c, 0.6);\n  CHECK_ITERABLE_APPROX(expected_root_2, root);\n\n  // Note that 'within roundoff' is defined as 100*epsilon,\n  // so adding 10*epsilon should still be within roundoff.\n  root = smallest_root_greater_than_value_within_roundoff(\n      a, b, c, 0.5 * (1.0 + std::numeric_limits<double>::epsilon() * 10.0));\n  CHECK_ITERABLE_APPROX(expected_root_1, root);\n}\n\ntemplate <typename T>\nvoid test_largest_root_between_values_within_roundoff(const T& used_for_size) {\n  const auto a = make_with_value<T>(used_for_size, 2.0);\n  const auto b = make_with_value<T>(used_for_size, -11.0);\n  const auto c = make_with_value<T>(used_for_size, 5.0);\n  const auto expected_root_1 = make_with_value<T>(used_for_size, 0.5);\n  const auto expected_root_2 = make_with_value<T>(used_for_size, 5.0);\n\n  auto root = largest_root_between_values_within_roundoff(a, b, c, 0.3, 0.6);\n  CHECK_ITERABLE_APPROX(expected_root_1, root);\n  root = largest_root_between_values_within_roundoff(a, b, c, 0.3, 0.5);\n  CHECK_ITERABLE_APPROX(expected_root_1, root);\n  root = largest_root_between_values_within_roundoff(a, b, c, 0.3, 6.0);\n  CHECK_ITERABLE_APPROX(expected_root_2, root);\n  root = largest_root_between_values_within_roundoff(a, b, c, 0.5, 6.0);\n  CHECK_ITERABLE_APPROX(expected_root_2, root);\n  root = largest_root_between_values_within_roundoff(a, b, c, 0.6, 6.0);\n  CHECK_ITERABLE_APPROX(expected_root_2, root);\n  root = largest_root_between_values_within_roundoff(a, b, c, 0.6, 5.0);\n  CHECK_ITERABLE_APPROX(expected_root_2, root);\n  root = largest_root_between_values_within_roundoff(a, b, c, 0.3, 5.0);\n  CHECK_ITERABLE_APPROX(expected_root_2, root);\n\n  // Note that 'within roundoff' is defined as 100*epsilon,\n  // so adding/subtracting 10*epsilon should still be within roundoff.\n  root = largest_root_between_values_within_roundoff(\n      a, b, c, 0.3,\n      0.5 * (1.0 - std::numeric_limits<double>::epsilon() * 10.0));\n  CHECK_ITERABLE_APPROX(expected_root_1, root);\n  root = largest_root_between_values_within_roundoff(\n      a, b, c, 0.5 * (1.0 - std::numeric_limits<double>::epsilon() * 10.0),\n      4.0);\n  CHECK_ITERABLE_APPROX(expected_root_1, root);\n  root = largest_root_between_values_within_roundoff(\n      a, b, c, 0.5,\n      6.0 * (1.0 - std::numeric_limits<double>::epsilon() * 10.0));\n  CHECK_ITERABLE_APPROX(expected_root_2, root);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.RootFinding.QuadraticEquation\",\n                  \"[NumericalAlgorithms][RootFinding][Unit]\") {\n  // Positive root\n  CHECK(approx(6.31662479035539985) == positive_root(1.0, -6.0, -2.0));\n\n  // Real roots\n  const auto roots = real_roots(2.0, -11.0, 5.0);\n  REQUIRE(roots.has_value());\n  CHECK(approx(0.5) == (*roots)[0]);\n  CHECK(approx(5.0) == (*roots)[1]);\n\n  // Check accuracy with small roots\n  const auto small_positive = real_roots(1e-8, 1.0 - 1e-16, -1e-8);\n  REQUIRE(small_positive.has_value());\n  CHECK(approx(-1e8) == (*small_positive)[0]);\n  CHECK(approx(1e-8) == (*small_positive)[1]);\n\n  const auto small_negative = real_roots(1e-8, -(1.0 - 1e-16), -1e-8);\n  REQUIRE(small_negative.has_value());\n  CHECK(approx(-1e-8) == (*small_negative)[0]);\n  CHECK(approx(1e8) == (*small_negative)[1]);\n\n  CHECK(not real_roots(1.0, -3.0, 3.0).has_value());\n\n  const auto repeated_root = real_roots(1.0, -2.0, 1.0);\n  CHECK(repeated_root == std::optional{std::array{1.0, 1.0}});\n\n  test_smallest_root_greater_than_value_within_roundoff<double>(1.0);\n  test_smallest_root_greater_than_value_within_roundoff(DataVector(5));\n  test_largest_root_between_values_within_roundoff<double>(1.0);\n  test_largest_root_between_values_within_roundoff(DataVector(5));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      (positive_root(1.0, -3.0, 3.0)),\n      Catch::Matchers::ContainsSubstring(\"There are no real roots\"));\n  CHECK_THROWS_WITH(\n      (positive_root(1.0, -3.0, 2.0)),\n      Catch::Matchers::ContainsSubstring(\"There are two positive roots\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/RootFinding/Test_RootBracketing.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/RootFinding/RootBracketing.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nstd::optional<double> f_free(double x) {\n  return (x < 1.0 or x > 2.0) ? std::nullopt\n                              : std::optional<double>(2.0 - square(x));\n}\n// f_root_near_bounds is a case contrived to generate an error\n// because the root is within 1e-10 of the bounds.\nstd::optional<double> f_root_near_bounds(double x) {\n  return (x < 1.0 or x > 2.0) ? std::nullopt\n                              : std::optional<double>(x-1.0-1.e-10);\n}\nstruct F {\n  std::optional<double> operator()(double x) const {\n    return (x < 1.0 or x > 2.0) ? std::nullopt\n                                : std::optional<double>(2.0 - square(x));\n  }\n};\n\ntemplate <typename Function>\nvoid test_bracketing_simple_one_function(const Function& f,\n                                         const std::array<double, 2>& bounds,\n                                         const std::optional<double>& guess) {\n  double lower = bounds[0];\n  double upper = bounds[1];\n  double f_at_lower = std::numeric_limits<double>::signaling_NaN();\n  double f_at_upper = std::numeric_limits<double>::signaling_NaN();\n  if (guess.has_value()) {\n    RootFinder::bracket_possibly_undefined_function_in_interval(\n        &lower, &upper, &f_at_lower, &f_at_upper, f, guess.value());\n  } else {\n    RootFinder::bracket_possibly_undefined_function_in_interval(\n        &lower, &upper, &f_at_lower, &f_at_upper, f);\n  }\n  CHECK(f_at_lower * f_at_upper <= 0.0);\n  CHECK(lower <= sqrt(2.0));\n  CHECK(upper >= sqrt(2.0));\n}\n\nvoid test_bracketing_simple_multiple_functions(\n    const std::array<double, 2>& bounds, const std::optional<double>& guess) {\n  const auto f_lambda = [](double x) -> std::optional<double> {\n    return (x < 1.0 or x > 2.0) ? std::nullopt\n                                : std::optional<double>(2.0 - square(x));\n  };\n  const F f_functor{};\n\n  test_bracketing_simple_one_function(f_free, bounds, guess);\n  test_bracketing_simple_one_function(f_lambda, bounds, guess);\n  test_bracketing_simple_one_function(f_functor, bounds, guess);\n}\n\nvoid test_bracketing_simple() {\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n\n  // Set up random upper/lower bounds.\n  const std::array<double, 2> bounds_invalid{\n      {unit_dis(gen), 2.0 + unit_dis(gen)}};\n  // Lower bound is less than sqrt(2.0) but valid.\n  const std::array<double, 2> bounds_upper_is_invalid{\n      {1.0 + (sqrt(2.0) - 1.0 - std::numeric_limits<double>::epsilon()) *\n                 unit_dis(gen),\n       2.0 + unit_dis(gen)}};\n  // Upper bound is greater than sqrt(2.0) but valid.\n  const std::array<double, 2> bounds_lower_is_invalid{\n      {unit_dis(gen),\n       sqrt(2.0) + (2.0 - sqrt(2.0) + std::numeric_limits<double>::epsilon()) *\n                       unit_dis(gen)}};\n  // Both bounds bracket root but are valid.\n  const std::array<double, 2> bounds_valid{\n      {1.0 + (sqrt(2.0) - 1.0 - std::numeric_limits<double>::epsilon()) *\n                 unit_dis(gen),\n       sqrt(2.0) + (2.0 - sqrt(2.0) + std::numeric_limits<double>::epsilon()) *\n                       unit_dis(gen)}};\n\n  const auto test_with_and_without_guess =\n      [&gen, &unit_dis](const std::array<double, 2>& bounds) {\n        test_bracketing_simple_multiple_functions(bounds, std::nullopt);\n        test_bracketing_simple_multiple_functions(\n            bounds, bounds[0] + unit_dis(gen) * (bounds[1] - bounds[0]));\n      };\n\n  test_with_and_without_guess(bounds_invalid);\n  test_with_and_without_guess(bounds_upper_is_invalid);\n  test_with_and_without_guess(bounds_lower_is_invalid);\n  test_with_and_without_guess(bounds_valid);\n}\n\nvoid test_bracketing_datavector() {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n\n  const DataVector lower{\n      unit_dis(gen),\n      1.0 + (sqrt(2.0) - 1.0 - std::numeric_limits<double>::epsilon()) *\n                unit_dis(gen),\n      unit_dis(gen),\n      1.0 + (sqrt(2.0) - 1.0 - std::numeric_limits<double>::epsilon()) *\n                unit_dis(gen)};\n  const DataVector upper{\n      2.0 + unit_dis(gen), 2.0 + unit_dis(gen),\n      sqrt(2.0) + (2.0 - sqrt(2.0) + std::numeric_limits<double>::epsilon()) *\n                      unit_dis(gen),\n      sqrt(2.0) + (2.0 - sqrt(2.0) + std::numeric_limits<double>::epsilon()) *\n                      unit_dis(gen)};\n\n  const auto f_lambda = [](double x, size_t /*i*/) -> std::optional<double> {\n    return (x < 1.0 or x > 2.0) ? std::nullopt\n                                : std::optional<double>(2.0 - square(x));\n  };\n\n  const auto do_test = [&f_lambda](DataVector lower_l, DataVector upper_l,\n                                   const std::optional<DataVector>& guess) {\n    DataVector f_at_lower(lower_l.size(),\n                          std::numeric_limits<double>::signaling_NaN());\n    DataVector f_at_upper(upper_l.size(),\n                          std::numeric_limits<double>::signaling_NaN());\n    if (guess.has_value()) {\n      RootFinder::bracket_possibly_undefined_function_in_interval(\n          &lower_l, &upper_l, &f_at_lower, &f_at_upper, f_lambda,\n          guess.value());\n    } else {\n      RootFinder::bracket_possibly_undefined_function_in_interval(\n          &lower_l, &upper_l, &f_at_lower, &f_at_upper, f_lambda);\n    }\n    for (size_t s = 0; s < f_at_lower.size(); ++s) {\n      CHECK(f_at_lower[s] * f_at_upper[s] <= 0.0);\n      CHECK(lower_l[s] <= sqrt(2.0));\n      CHECK(upper_l[s] >= sqrt(2.0));\n    }\n  };\n\n  do_test(lower, upper, std::nullopt);\n  do_test(lower, upper, DataVector(lower + (upper - lower) * unit_dis(gen)));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.RootFinding.Bracketing\",\n                  \"[NumericalAlgorithms][RootFinding][Unit]\") {\n  test_bracketing_simple();\n  test_bracketing_datavector();\n\n  CHECK_THROWS_WITH((test_bracketing_simple_one_function(\n                        f_root_near_bounds, {{0.1, 1.1}}, std::nullopt)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"bracket_by_contracting: Cannot bracket root between\"));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/RootFinding/Test_TOMS748.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <stdexcept>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/RootFinding/TOMS748.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n\nnamespace {\ndouble f_free(double x) { return 2.0 - square(x); }\nstruct F {\n  template <typename T>\n  T operator()(const T& x) const {\n    return 2.0 - square(x);\n  }\n};\n\nvoid test_simple() {\n  const double abs_tol = 1e-15;\n  const double rel_tol = 1e-15;\n  const double upper = 2.0;\n  const double lower = 0.0;\n  const auto f_lambda = [](double x) { return 2.0 - square(x); };\n  const F f_functor{};\n  const auto root_from_lambda =\n      RootFinder::toms748(f_lambda, lower, upper, abs_tol, rel_tol);\n  const auto root_from_free =\n      RootFinder::toms748(f_free, lower, upper, abs_tol, rel_tol);\n  const auto root_from_functor =\n      RootFinder::toms748(f_functor, lower, upper, abs_tol, rel_tol);\n  CHECK(std::abs(root_from_lambda - sqrt(2.0)) < abs_tol);\n  CHECK(std::abs(root_from_lambda - sqrt(2.0)) / sqrt(2.0) < rel_tol);\n  CHECK(root_from_free == root_from_lambda);\n  CHECK(root_from_free == root_from_functor);\n}\n\nvoid test_bounds() {\n  // [double_root_find]\n  const double abs_tol = 1e-15;\n  const double rel_tol = 1e-15;\n  const double upper = 2.0;\n  const double lower = sqrt(2.0) - abs_tol;  // bracket surrounds root\n  const auto f_lambda = [](double x) { return 2.0 - square(x); };\n\n  auto root = RootFinder::toms748(f_lambda, lower, upper, abs_tol, rel_tol);\n  // [double_root_find]\n\n  CHECK(std::abs(root - sqrt(2.0)) < abs_tol);\n  CHECK(std::abs(root - sqrt(2.0)) / sqrt(2.0) < rel_tol);\n\n  // Test the overload where function values are supplied at lower\n  // and upper bounds.\n  auto root2 = RootFinder::toms748(f_lambda, lower, upper, f_lambda(lower),\n                                   f_lambda(upper), abs_tol, rel_tol);\n  CHECK(std::abs(root2 - sqrt(2.0)) < abs_tol);\n  CHECK(std::abs(root2 - sqrt(2.0)) / sqrt(2.0) < rel_tol);\n\n  // Check that the other tight-but-correct bracket works\n  CHECK(RootFinder::toms748(f_lambda, 0.0, sqrt(2.0) + abs_tol, abs_tol,\n                            rel_tol) == approx(root));\n\n  // Check that exception is thrown for various bad bracket possibilities\n  CHECK_THROWS_WITH(\n      RootFinder::toms748(f_lambda, 0.0, sqrt(2.0) - abs_tol, abs_tol, rel_tol),\n      Catch::Matchers::ContainsSubstring(\"Root not bracketed\"));\n\n  CHECK_THROWS_WITH(\n      RootFinder::toms748(f_lambda, sqrt(2.0) + abs_tol, 2.0, abs_tol, rel_tol),\n      Catch::Matchers::ContainsSubstring(\"Root not bracketed\"));\n\n  CHECK_THROWS_WITH(RootFinder::toms748(f_lambda, -1.0, 1.0, abs_tol, rel_tol),\n                    Catch::Matchers::ContainsSubstring(\"Root not bracketed\"));\n}\n\nvoid test_datavector() {\n  // [datavector_root_find]\n  const double abs_tol = 1e-15;\n  const double rel_tol = 1e-15;\n  const DataVector upper{2.0, 3.0, -sqrt(2.0) + abs_tol, -sqrt(2.0)};\n  const DataVector lower{sqrt(2.0) - abs_tol, sqrt(2.0), -2.0, -3.0};\n\n  const DataVector constant{2.0, 4.0, 2.0, 4.0};\n  const auto f_lambda = [&constant](const auto x, const size_t i) {\n    if constexpr (simd::is_batch<std::decay_t<decltype(x)>>::value) {\n      return simd::load_unaligned(&(constant[i])) - square(x);\n    } else {\n      return constant[i] - square(x);\n    }\n  };\n\n  const auto root_no_function_values =\n      RootFinder::toms748<true>(f_lambda, lower, upper, abs_tol, rel_tol);\n  // [datavector_root_find]\n\n  auto check_root = [&abs_tol, &rel_tol](const DataVector& root) {\n    CHECK(std::abs(root[0] - sqrt(2.0)) < abs_tol);\n    CHECK(std::abs(root[0] - sqrt(2.0)) / sqrt(2.0) < rel_tol);\n    CHECK(std::abs(root[1] - 2.0) < abs_tol);\n    CHECK(std::abs(root[1] - 2.0) / 2.0 < rel_tol);\n    CHECK(std::abs(root[2] + sqrt(2.0)) < abs_tol);\n    CHECK(std::abs(root[2] + sqrt(2.0)) / sqrt(2.0) < rel_tol);\n    CHECK(std::abs(root[3] + 2.0) < abs_tol);\n    CHECK(std::abs(root[3] + 2.0) / 2.0 < rel_tol);\n  };\n  check_root(root_no_function_values);\n\n  // Test the version of toms748 where function values are supplied\n  // at lower and upper bounds.\n  const auto generate_function_values = [&f_lambda](const DataVector& x) {\n    DataVector f(x.size());\n    for (size_t i = 0; i < x.size(); ++i) {\n      f[i] = f_lambda(x[i], i);\n    }\n    return f;\n  };\n  const auto root_function_values = RootFinder::toms748<true>(\n      f_lambda, lower, upper, generate_function_values(lower),\n      generate_function_values(upper), abs_tol, rel_tol);\n  check_root(root_function_values);\n}\n\nvoid test_convergence_error_double() {\n  CHECK_THROWS_AS(\n      []() {\n        const double abs_tol = 1e-15;\n        const double rel_tol = 1e-15;\n        const double upper = 2.0;\n        const double lower = 0.0;\n        const auto f = [](double x) { return 2.0 - square(x); };\n        RootFinder::toms748(f, lower, upper, abs_tol, rel_tol, 2);\n      }(),\n      convergence_error);\n}\n\nvoid test_convergence_error_datavector() {\n  CHECK_THROWS_AS(\n      ([]() {\n        const double abs_tol = 1e-15;\n        const double rel_tol = 1e-15;\n        const DataVector upper{2.0, 3.0, -sqrt(2.0) + abs_tol, -sqrt(2.0)};\n        const DataVector lower{sqrt(2.0) - abs_tol, sqrt(2.0), -2.0, -3.0};\n        const DataVector constant{2.0, 4.0, 2.0, 4.0};\n        const auto f = [&constant](const auto x, const size_t i) {\n          if constexpr (simd::is_batch<std::decay_t<decltype(x)>>::value) {\n            return simd::load_unaligned(&constant[i]) - square(x);\n          } else {\n            return constant[i] - square(x);\n          }\n        };\n        RootFinder::toms748(f, lower, upper, abs_tol, rel_tol, 2);\n      }()),\n      convergence_error);\n}\n\nvoid benchmark_root_find(const bool enable) {\n  if (not enable) {\n    return;\n  }\n\n#ifdef SPECTRE_USE_XSIMD\n  using SimdType = simd::batch<double>;\n  const auto benchmark = [](const int bound_type,\n                            Catch::Benchmark::Chronometer& meter) {\n    const auto f = [](const auto& x) {\n      using T = std::decay_t<decltype(x)>;\n      return simd::fms(x, simd::fma(x, (x - 6.0), T(11.0)), T(6.0));\n    };\n    const SimdType lower_bound(1.5);\n    SimdType upper_bound{};\n    if (bound_type == 0) {\n      upper_bound = SimdType(2.5 + 1.0e-100) + 1.0e-8;\n    } else if (bound_type == 1) {\n      ERROR(\"Need to adjust\");\n      // upper_bound = SimdType(2.1 + 1.0e-10, 2.1 + 1.0e-10, 2.1 + 1.0e-10) +\n      //               1.0e-8;\n    } else if (bound_type == 2) {\n      ERROR(\"Need to adjust\");\n      // upper_bound = SimdType(2.1 + 1.0e-10, 2.2 + 1.0e-10, 2.3 + 1.0e-10,\n      //                        2.4 + 1.0e-10, 2.5, 2.6, 2.7, 2.8) +\n      //               1.0e-8;\n    } else if (bound_type == 3) {\n      ERROR(\"Need to adjust\");\n      // const double base = 2.8 + 1.0e-8;\n      // upper_bound = SimdType(base + 1.0e-10, base + 2.0e-10, base + 3.0e-10,\n      //                        base + 4.0e-10, base + 5.0e-10, base + 6.0e-10,\n      //                        base + 7.0e-10, base + 8.0e-10);\n    }\n    const auto f_at_lower_bound = f(lower_bound);\n    const auto f_at_upper_bound = f(upper_bound);\n    meter.measure([&f, &lower_bound, &upper_bound, &f_at_lower_bound,\n                   &f_at_upper_bound]() {\n      return RootFinder::toms748(f, lower_bound, upper_bound, f_at_lower_bound,\n                                 f_at_upper_bound, 1.0e-14, 1.0e-12);\n    });\n  };\n\n  for (size_t i = 0; i < 4; ++i) {\n    // Can check other bound types, but those are currently hard-coded to\n    // specific vector widths, which won't let the code compile generically.\n    // The general support is left in case someone wants to look at this again.\n    BENCHMARK_ADVANCED(std::string(\"advanced \") + std::to_string(i))\n    (Catch::Benchmark::Chronometer meter) { benchmark(0, meter); };\n  }\n#endif // SPECTRE_USE_XSIMD\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.RootFinding.TOMS748\",\n                  \"[NumericalAlgorithms][RootFinding][Unit]\") {\n  test_simple();\n  test_bounds();\n  test_datavector();\n  test_convergence_error_double();\n  test_convergence_error_datavector();\n  benchmark_root_find(false);\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double abs_tol = 1e-15;\n        const double rel_tol = 0.5 * std::numeric_limits<double>::epsilon();\n        const DataVector upper{2.0, 3.0, -sqrt(2.0) + abs_tol, -sqrt(2.0)};\n        const DataVector lower{sqrt(2.0) - abs_tol, sqrt(2.0), -2.0, -3.0};\n\n        const DataVector constant{2.0, 4.0, 2.0, 4.0};\n        const auto f_lambda = [&constant](const auto x, const size_t i) {\n          if constexpr (simd::is_batch<std::decay_t<decltype(x)>>::value) {\n            return simd::load_unaligned(&constant[i]) - square(x);\n          } else {\n            return constant[i] - square(x);\n          }\n        };\n\n        RootFinder::toms748(f_lambda, lower, upper, abs_tol, rel_tol);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The relative tolerance is too small.\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        const double abs_tol = 1e-15;\n        const double rel_tol = 0.5 * std::numeric_limits<double>::epsilon();\n        double upper = 2.0;\n        double lower = sqrt(2.0) - abs_tol;\n        const auto f_lambda = [](double x) { return 2.0 - square(x); };\n\n        // NOLINTNEXTLINE(clang-analyzer-core)\n        RootFinder::toms748(f_lambda, lower, upper, abs_tol, rel_tol);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The relative tolerance is too small.\"));\n#endif\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/BasisFunctions/Test_Chebyshev.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/PolynomialTestFunctions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\nDataVector expected_modes(const size_t pow_x) {\n  switch (pow_x) {\n    case 0:\n      return DataVector{1.0};\n    case 1:\n      return DataVector{0.0, 1.0};\n    case 2:\n      return DataVector{0.5, 0.0, 0.5};\n    case 3:\n      return DataVector{0.0, 0.75, 0.0, 0.25};\n    case 4:\n      return DataVector{0.375, 0.0, 0.5, 0.0, 0.125};\n    default:\n      return DataVector{};\n  }\n}\n\nvoid test_modes() {\n  for (size_t pow_x = 0; pow_x < 5; ++pow_x) {\n    const PolynomialTestFunctions::Monomial f{pow_x};\n    CHECK_ITERABLE_APPROX(expected_modes(pow_x), f.modes<Basis::Chebyshev>());\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.BasisFunctions.Chebyshev\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  PolynomialTestFunctions::test_orthogonal_polynomial<\n      Basis::Chebyshev, Quadrature::GaussLobatto>();\n  PolynomialTestFunctions::test_orthogonal_polynomial<Basis::Chebyshev,\n                                                      Quadrature::Gauss>();\n  test_modes();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/BasisFunctions/Test_Fourier.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/FourierTestFunctions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Fourier.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n\nnamespace Spectral {\nnamespace {\n\nvoid test_mode_number_to_storage_index() {\n  for (size_t n = 0; n <= 3; ++n) {\n    CAPTURE(n);\n    CHECK(Fourier::modal_storage_index(Fourier::mode_at_storage_index(n)) == n);\n  }\n}\n\nvoid test_modal_to_nodal_matrix() {\n  constexpr Basis basis = Basis::Fourier;\n  constexpr Quadrature quadrature = Quadrature::Equiangular;\n  for (size_t n = 1; n <= 81; n += 1) {\n    CAPTURE(n);\n    const DataVector& x = collocation_points<basis, quadrature>(n);\n    const Matrix& m_to_n = modal_to_nodal_matrix<basis, quadrature>(n);\n    const Matrix& n_to_m = nodal_to_modal_matrix<basis, quadrature>(n);\n    for (size_t j = 0; j < n; ++j) {\n      CAPTURE(j);\n      DataVector u_k{n, 0.0};\n      u_k[j] = 1.0;\n      const DataVector u = apply_matrix(m_to_n, u_k);\n      const int k = Fourier::mode_at_storage_index(j);\n      const DataVector expected_u =\n          k < 0 ? DataVector{sin(-k * x)} : DataVector{cos(k * x)};\n      CHECK_ITERABLE_APPROX(u, expected_u);\n      const DataVector computed_u_k = apply_matrix(n_to_m, u);\n      CHECK_ITERABLE_APPROX(u_k, computed_u_k);\n    }\n  }\n}\n\nvoid test_definite_integral(\n    const DataVector& u, const DataVector& weights,\n    const FourierTestFunctions::ProductOfPolynomials& f) {\n  const double result = ddot_(u.size(), weights.data(), 1, u.data(), 1);\n  CHECK(f.definite_integral() == approx(result));\n}\n\nvoid test_derivative(const DataVector& u, const Matrix& m,\n                     const FourierTestFunctions::ProductOfPolynomials& f,\n                     const DataVector& phi) {\n  const auto custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n  const DataVector expected_du = f.df_dph(phi);\n  const DataVector du = apply_matrix(m, u);\n  CHECK_ITERABLE_CUSTOM_APPROX(du, expected_du, custom_approx);\n}\n\ntemplate <typename T>\nvoid test_interpolation(const DataVector& u, const T& target_points,\n                        const Matrix& m,\n                        const FourierTestFunctions::ProductOfPolynomials& f) {\n  const auto custom_approx = Approx::custom().epsilon(5.0e-11).scale(1.0);\n  const DataVector u_target = apply_matrix(m, u);\n  for (size_t i = 0; i < get_size(target_points); ++i) {\n    CHECK(get_element(u_target, i) ==\n          custom_approx(f(get_element(target_points, i))));\n  }\n}\n\nvoid test_transforms(const DataVector& u, const Matrix& modal_to_nodal_matrix,\n                     const Matrix& nodal_to_modal_matrix,\n                     const FourierTestFunctions::ProductOfPolynomials& f) {\n  const DataVector expected_u_k = f.modes();\n  const DataVector u_k = apply_matrix(nodal_to_modal_matrix, u);\n  for (size_t i = 0; i < expected_u_k.size(); ++i) {\n    CHECK(u_k[i] == approx(expected_u_k[i]));\n  }\n  const DataVector transformed_u = apply_matrix(modal_to_nodal_matrix, u_k);\n  CHECK_ITERABLE_APPROX(u, transformed_u);\n}\n\nvoid test() {\n  constexpr Basis basis = Basis::Fourier;\n  constexpr Quadrature quadrature = Quadrature::Equiangular;\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  const double phi_target = phi_distribution(generator);\n  const auto target_points = make_with_random_values<DataVector>(\n      make_not_null(&generator), make_not_null(&phi_distribution), 5_st);\n  CAPTURE(phi_target);\n  for (size_t num_points = 1; num_points < 65; ++num_points) {\n    CAPTURE(num_points);\n    const DataVector phi = collocation_points<basis, quadrature>(num_points);\n    const Matrix dm = differentiation_matrix<basis, quadrature>(num_points);\n    const DataVector integration_weights =\n        quadrature_weights<basis, quadrature>(num_points);\n    const Matrix interp_matrix =\n        interpolation_matrix<basis, quadrature>(num_points, phi_target);\n    const Matrix interp_matrix_dv =\n        interpolation_matrix<basis, quadrature>(num_points, target_points);\n    const Matrix& m_to_n = modal_to_nodal_matrix<basis, quadrature>(num_points);\n    const Matrix& n_to_m = nodal_to_modal_matrix<basis, quadrature>(num_points);\n    const size_t k_max = num_points / 2;\n    CAPTURE(k_max);\n    for (size_t pow_nx = 0; pow_nx < k_max; ++pow_nx) {\n      CAPTURE(pow_nx);\n      for (size_t pow_ny = 0; pow_ny < k_max - pow_nx; ++pow_ny) {\n        CAPTURE(pow_ny);\n        const FourierTestFunctions::ProductOfPolynomials f{pow_nx, pow_ny};\n        const DataVector u = f(phi);\n        CAPTURE(u);\n        test_definite_integral(u, integration_weights, f);\n        test_interpolation(u, phi_target, interp_matrix, f);\n        test_interpolation(u, target_points, interp_matrix_dv, f);\n        test_derivative(u, dm, f, phi);\n        test_transforms(u, m_to_n, n_to_m, f);\n      }\n    }\n  }\n}\n\nDataVector expected_modes(const size_t pow_nx, const size_t pow_ny) {\n  if (pow_nx == 0 and pow_ny == 0) {\n    return {1.0};\n  } else if (pow_nx == 1 and pow_ny == 0) {\n    return {0.0, 1.0, 0.0};\n  } else if (pow_nx == 0 and pow_ny == 1) {\n    return {0.0, 0.0, 1.0};\n  } else if (pow_nx == 2 and pow_ny == 0) {\n    return {0.5, 0.0, 0.0, 0.5, 0.0};\n  } else if (pow_nx == 1 and pow_ny == 1) {\n    return {0.0, 0.0, 0.0, 0.0, 0.5};\n  } else if (pow_nx == 0 and pow_ny == 2) {\n    return {0.5, 0.0, 0.0, -0.5, 0.0};\n  } else if (pow_nx == 3 and pow_ny == 0) {\n    return {0.0, 0.75, 0.0, 0.0, 0.0, 0.25, 0.0};\n  } else if (pow_nx == 2 and pow_ny == 1) {\n    return {0.0, 0.0, 0.25, 0.0, 0.0, 0.0, 0.25};\n  } else if (pow_nx == 1 and pow_ny == 2) {\n    return {0.0, 0.25, 0.0, 0.0, 0.0, -0.25, 0.0};\n  } else if (pow_nx == 0 and pow_ny == 3) {\n    return {0.0, 0.0, 0.75, 0.0, 0.0, 0.0, -0.25};\n  } else if (pow_nx == 4 and pow_ny == 0) {\n    return {0.375, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.125, 0.0};\n  } else if (pow_nx == 3 and pow_ny == 1) {\n    return {0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 0.0, 0.125};\n  } else if (pow_nx == 2 and pow_ny == 2) {\n    return {0.125, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.125, 0.0};\n  } else if (pow_nx == 1 and pow_ny == 3) {\n    return {0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 0.0, -0.125};\n  } else if (pow_nx == 0 and pow_ny == 4) {\n    return {0.375, 0.0, 0.0, -0.5, 0.0, 0.0, 0.0, 0.125, 0.0};\n  }\n  return DataVector{};\n}\n\nvoid test_modes() {\n  for (size_t k_max = 0; k_max < 5; ++k_max) {\n    for (size_t pow_nx = 0; pow_nx <= k_max; ++pow_nx) {\n      CAPTURE(pow_nx);\n      for (size_t pow_ny = 0; pow_ny <= k_max - pow_nx; ++pow_ny) {\n        CAPTURE(pow_ny);\n        const FourierTestFunctions::ProductOfPolynomials f{pow_nx, pow_ny};\n        CHECK_ITERABLE_APPROX(expected_modes(pow_nx, pow_ny), f.modes());\n      }\n    }\n  }\n}\n}  // namespace\n\n// [[Timeout, 10]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.BasisFunctions.Fourier\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_mode_number_to_storage_index();\n  test_modal_to_nodal_matrix();\n  test();\n  test_modes();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/BasisFunctions/Test_Jacobi.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Jacobi.hpp\"\n\nnamespace Spectral {\nnamespace {\ndouble binomial(const double alpha, const size_t k) {\n  double result = 1.0;\n  for (uint64_t j = 1; j <= k; ++j) {\n    const auto jj = static_cast<double>(j);\n    result *= alpha + 1.0 - jj;\n    result /= jj;\n  }\n  return result;\n}\n\nvoid test_values() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> x_distribution(0.0, 1.0);\n  const double x = x_distribution(generator);\n  CAPTURE(x);\n  for (size_t k = 0; k < 20; ++k) {\n    CHECK(Jacobi::basis_function_value(0.0, 0.0, k, x) ==\n          approx(compute_basis_function_value<Basis::Legendre>(k, x)));\n    CHECK(Jacobi::basis_function_value(-0.5, -0.5, k, x) /\n              Jacobi::basis_function_value(-0.5, -0.5, k, 1.0) ==\n          approx(compute_basis_function_value<Basis::Chebyshev>(k, x)));\n  }\n  std::uniform_real_distribution<> greek_distribution(-1.0, 10.0);\n  const double alpha = greek_distribution(generator);\n  const double beta = greek_distribution(generator);\n  CAPTURE(alpha);\n  CAPTURE(beta);\n  for (size_t n = 0; n < 10; ++n) {\n    CAPTURE(n);\n    const auto nn = static_cast<double>(n);\n    double expected_value = 0.0;\n    const double scale = pow(0.5, nn);\n    for (size_t k = 0; k <= n; ++k) {\n      const auto kk = static_cast<double>(k);\n      expected_value += scale * binomial(nn + alpha, k) *\n                        binomial(nn + beta, n - k) * pow(x - 1.0, nn - kk) *\n                        pow(x + 1.0, kk);\n    }\n    // the binomial formula is not an accurate way to compute the values...\n    const Approx custom_approx = Approx::custom().epsilon(1.0e-10).scale(1.0);\n    CHECK(Jacobi::basis_function_value(alpha, beta, n, x) ==\n          custom_approx(expected_value));\n  }\n}\n\nvoid test_errors() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      Jacobi::basis_function_value(-1.0, 0.1, 1, 1.0),\n      Catch::Matchers::ContainsSubstring(\"alpha must be > -1, got \"));\n  CHECK_THROWS_WITH(\n      Jacobi::basis_function_value(0.0, -2.5, 2, 1.0),\n      Catch::Matchers::ContainsSubstring(\"beta must be > -1, got \"));\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.BasisFunctions.Jacobi\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_values();\n  test_errors();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/BasisFunctions/Test_Legendre.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/PolynomialTestFunctions.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\nDataVector expected_modes(const size_t pow_x) {\n  switch (pow_x) {\n    case 0:\n      return DataVector{1.0};\n    case 1:\n      return DataVector{0.0, 1.0};\n    case 2:\n      return DataVector{1.0/3.0, 0.0, 2.0/3.0};\n    case 3:\n      return DataVector{0.0, 0.6, 0.0, 0.4};\n    case 4:\n      return DataVector{0.2, 0.0, 4.0/7.0, 0.0, 8.0/35.0};\n    default:\n      return DataVector{};\n  }\n}\n\nvoid test_modes() {\n  for (size_t pow_x = 0; pow_x < 5; ++pow_x) {\n    const PolynomialTestFunctions::Monomial f{pow_x};\n    CHECK_ITERABLE_APPROX(expected_modes(pow_x), f.modes<Basis::Legendre>());\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.BasisFunctions.Legendre\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  PolynomialTestFunctions::test_orthogonal_polynomial<\n      Basis::Legendre, Quadrature::GaussLobatto>();\n  PolynomialTestFunctions::test_orthogonal_polynomial<Basis::Legendre,\n                                                      Quadrature::Gauss>();\n  test_modes();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/BasisFunctions/Test_Zernike.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Jacobi.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Zernike.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace Spectral {\nnamespace {\n// Copy of normalization of basis from Zernike.cpp's anonymous namespace\ntemplate <size_t Dim>\ndouble I(const size_t n, const size_t m) {\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3);\n  ASSERT(n >= m, \"n = \" << n << \"; m = \" << m);\n  ASSERT((n + m) % 2 == 0,\n         \"n and m must have same parity, got n = \" << n << \" m = \" << m);\n  constexpr double betaM = Dim == 1 ? 0.0 : (Dim == 2 ? 1.0 : 2.0);\n  double result = 0.5 / (static_cast<double>(m) + 0.5 + 0.5 * betaM);\n  for (size_t i = m + 2; i <= n; i += 2) {\n    const auto ii = static_cast<double>(i);\n    result *= (2. * ii + betaM - 3.) / (2. * ii + betaM + 1.);\n  }\n  return result;\n}\n\n// The implemented Zernike basis is normalized, but we relate the basis to\n// known (unnnormalized) Jacobi polynomials, so we have to undo this\n// normalization to check against Jacobi implementation\ntemplate <size_t Dim>\ndouble Zernike_basis_function_value_unnormalized(size_t n, size_t m,\n                                                 const double xi) {\n  return Zernike<Dim>::basis_function_value(n, m, xi) * sqrt(I<Dim>(n, m));\n}\n\ntemplate <size_t Dim, typename T>\nT Zernike_basis_function_value_Jacobi(const size_t n, const size_t m,\n                                      const T& r) {\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3);\n  ASSERT(n >= m, \"m \" << m << \" must be at most n \" << n);\n  ASSERT((n + m) % 2 == 0, \"m \" << m << \" plus n \" << n << \" must be even\");\n  const size_t k = (n - m) / 2;\n  const auto mm = static_cast<double>(m);\n  const double beta = Dim == 1 ? mm - 0.5 : Dim == 2 ? mm : mm + 0.5;\n  const T x = 2.0 * square(r) - 1.0;\n  T result = pow(r, mm);\n  result *= Jacobi::basis_function_value(0.0, beta, k, x);\n  return result;\n}\n\nvoid test_values() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> r_distribution(0.0, 1.0);\n  {\n    INFO(\"Testing against analytic\");\n    const double r = r_distribution(generator);\n    const double xi = 2.0 * r - 1.0;\n    const Approx custom_approx = Approx::custom().epsilon(1.0e-11).scale(1.0);\n\n    CHECK(Zernike_basis_function_value_unnormalized<1>(0, 0, xi) ==\n          approx(1.0));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(1, 1, xi) == approx(r));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(2, 0, xi) ==\n          approx(1.5 * square(r) - 0.5));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(2, 2, xi) ==\n          approx(square(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(3, 1, xi) ==\n          approx(2.5 * cube(r) - 1.5 * r));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(3, 3, xi) ==\n          approx(cube(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(4, 0, xi) ==\n          approx(35.0 / 8.0 * pow<4>(r) - 15.0 / 4.0 * square(r) + 3.0 / 8.0));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(4, 2, xi) ==\n          approx(3.5 * pow<4>(r) - 2.5 * square(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(4, 4, xi) ==\n          approx(pow<4>(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(11, 1, xi) ==\n          custom_approx(\n              88179.0 / 256.0 * pow<11>(r) - 230945.0 / 256.0 * pow<9>(r) +\n              109395.0 / 128.0 * pow<7>(r) - 45045.0 / 128.0 * pow<5>(r) +\n              15015.0 / 256.0 * cube(r) - 693.0 / 256.0 * r));\n    CHECK(Zernike_basis_function_value_unnormalized<1>(12, 0, xi) ==\n          custom_approx(\n              676039.0 / 1024.0 * pow<12>(r) - 969969.0 / 512.0 * pow<10>(r) +\n              2078505.0 / 1024.0 * pow<8>(r) - 255255.0 / 256.0 * pow<6>(r) +\n              225225.0 / 1024.0 * pow<4>(r) - 9009.0 / 512.0 * square(r) +\n              231.0 / 1024.0));\n\n    CHECK(Zernike_basis_function_value_unnormalized<2>(0, 0, xi) ==\n          approx(1.0));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(1, 1, xi) == approx(r));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(2, 0, xi) ==\n          approx(2.0 * square(r) - 1.0));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(2, 2, xi) ==\n          approx(square(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(3, 1, xi) ==\n          approx(3.0 * cube(r) - 2.0 * r));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(3, 3, xi) ==\n          approx(cube(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(4, 0, xi) ==\n          approx(6.0 * pow<4>(r) - 6.0 * square(r) + 1.0));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(4, 2, xi) ==\n          approx(4.0 * pow<4>(r) - 3.0 * square(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(4, 4, xi) ==\n          approx(pow<4>(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(5, 1, xi) ==\n          approx(10.0 * pow<5>(r) - 12.0 * cube(r) + 3.0 * r));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(5, 3, xi) ==\n          approx(5.0 * pow<5>(r) - 4.0 * cube(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(5, 5, xi) ==\n          approx(pow<5>(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(6, 0, xi) ==\n          approx(20.0 * pow<6>(r) - 30.0 * pow<4>(r) + 12.0 * square(r) - 1.0));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(6, 2, xi) ==\n          approx(15.0 * pow<6>(r) - 20.0 * pow<4>(r) + 6.0 * square(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(6, 4, xi) ==\n          approx(6.0 * pow<6>(r) - 5.0 * pow<4>(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(6, 6, xi) ==\n          approx(pow<6>(r)));\n    CHECK(\n        Zernike_basis_function_value_unnormalized<2>(7, 1, xi) ==\n        approx(35.0 * pow<7>(r) - 60.0 * pow<5>(r) + 30.0 * cube(r) - 4.0 * r));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(7, 3, xi) ==\n          approx(21.0 * pow<7>(r) - 30.0 * pow<5>(r) + 10.0 * cube(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(7, 5, xi) ==\n          approx(7.0 * pow<7>(r) - 6.0 * pow<5>(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(7, 7, xi) ==\n          approx(pow<7>(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(12, 0, xi) ==\n          custom_approx(924.0 * pow<12>(r) - 2772.0 * pow<10>(r) +\n                        3150.0 * pow<8>(r) - 1680.0 * pow<6>(r) +\n                        420.0 * pow<4>(r) - 42.0 * square(r) + 1.0));\n    CHECK(Zernike_basis_function_value_unnormalized<2>(13, 1, xi) ==\n          custom_approx(1716.0 * pow<13>(r) - 5544.0 * pow<11>(r) +\n                        6930.0 * pow<9>(r) - 4200.0 * pow<7>(r) +\n                        1260.0 * pow<5>(r) - 168.0 * cube(r) + 7.0 * r));\n\n    CHECK(Zernike_basis_function_value_unnormalized<3>(0, 0, xi) ==\n          approx(1.0));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(1, 1, xi) == approx(r));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(2, 0, xi) ==\n          approx(2.5 * square(r) - 1.5));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(2, 2, xi) ==\n          approx(square(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(3, 1, xi) ==\n          approx(3.5 * cube(r) - 2.5 * r));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(3, 3, xi) ==\n          approx(cube(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(4, 0, xi) ==\n          approx(63.0 / 8.0 * pow<4>(r) - 70.0 / 8.0 * square(r) + 15.0 / 8.0));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(4, 2, xi) ==\n          approx(4.5 * pow<4>(r) - 3.5 * square(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(4, 4, xi) ==\n          approx(pow<4>(r)));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(12, 0, xi) ==\n          custom_approx(\n              1300075.0 / 1024.0 * pow<12>(r) -\n              4056234.0 / 1024.0 * pow<10>(r) + 4849845.0 / 1024.0 * pow<8>(r) -\n              2771340.0 / 1024.0 * pow<6>(r) + 765765.0 / 1024.0 * pow<4>(r) -\n              90090.0 / 1024.0 * square(r) + 3003.0 / 1024.0));\n    CHECK(Zernike_basis_function_value_unnormalized<3>(13, 1, xi) ==\n          custom_approx(2340135.0 / 1024.0 * pow<13>(r) -\n                        7800450.0 / 1024.0 * pow<11>(r) +\n                        10140585.0 / 1024.0 * pow<9>(r) -\n                        6466460.0 / 1024.0 * pow<7>(r) +\n                        2078505.0 / 1024.0 * pow<5>(r) -\n                        306306.0 / 1024.0 * cube(r) + 15015.0 / 1024.0 * r));\n  }\n  {\n    INFO(\"Testing against scaled Jacobi, all dimensions\");\n    const double r = r_distribution(generator);\n    const double xi = 2.0 * r - 1.0;\n    std::array<size_t, 8> all_k{};\n    std::iota(all_k.begin(), all_k.end(), 2);\n    for (const auto& k : random_sample<3>(all_k, make_not_null(&generator))) {\n      for (size_t m = k % 2; m <= k; m += 2) {\n        CHECK(Zernike_basis_function_value_unnormalized<1>(k, m, xi) ==\n              approx(Zernike_basis_function_value_Jacobi<1>(k, m, r)));\n        CHECK(Zernike_basis_function_value_unnormalized<2>(k, m, xi) ==\n              approx(Zernike_basis_function_value_Jacobi<2>(k, m, r)));\n        CHECK(Zernike_basis_function_value_unnormalized<3>(k, m, xi) ==\n              approx(Zernike_basis_function_value_Jacobi<3>(k, m, r)));\n      }\n    }\n  }\n}\n\nvoid test_errors() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      Zernike<1>::basis_function_value(2, 4, 1.0),\n      Catch::Matchers::ContainsSubstring(\"m, 4, must be at most n, 2\"));\n  CHECK_THROWS_WITH(\n      Zernike<2>::basis_function_value(4, 3, 1.0),\n      Catch::Matchers::ContainsSubstring(\"n, 4, plus m, 3, must be even.\"));\n#endif\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.BasisFunctions.Zernike\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_values();\n  test_errors();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(\"Python\")\n\nset(LIBRARY \"Test_Spectral\")\n\nset(LIBRARY_SOURCES\n  BasisFunctions/Test_Chebyshev.cpp\n  BasisFunctions/Test_Fourier.cpp\n  BasisFunctions/Test_Jacobi.cpp\n  BasisFunctions/Test_Legendre.cpp\n  BasisFunctions/Test_Zernike.cpp\n  Test_Basis.cpp\n  Test_BasisFunctionNormalizationSquare.cpp\n  Test_Cartoon.cpp\n  Test_ChebyshevGauss.cpp\n  Test_ChebyshevGaussLobatto.cpp\n  Test_Clenshaw.cpp\n  Test_CollocationPointsAndWeights.cpp\n  Test_DifferentiationMatrix.cpp\n  Test_Filtering.cpp\n  Test_FiniteDifference.cpp\n  Test_IndefiniteIntegral.cpp\n  Test_IntegrationMatrix.cpp\n  Test_InterpolationMatrix.cpp\n  Test_InterpolationWeights.cpp\n  Test_Legendre.cpp\n  Test_LegendreGauss.cpp\n  Test_LegendreGaussLobatto.cpp\n  Test_LinearFilterMatrix.cpp\n  Test_LogicalCoordinates.cpp\n  Test_Mesh.cpp\n  Test_ModalToNodalMatrix.cpp\n  Test_NodalToModalMatrix.cpp\n  Test_Parity.cpp\n  Test_ParityFromSymmetry.cpp\n  Test_Projection.cpp\n  Test_Quadrature.cpp\n  Test_QuadratureWeights.cpp\n  Test_SegmentSize.cpp\n  Test_Spectral.cpp\n  Test_ZernikeGaussRadauUpper.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  ErrorHandling\n  LinearOperators\n  Spectral\n  SpectralHelpers\n  SphericalHarmonics\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n        \"Unit.Spectral.Python.LogicalCoordinates\"\n        Test_LogicalCoordinates.py\n        \"Unit;Spectral;Python\"\n        PySpectral)\n\nspectre_add_python_bindings_test(\n        \"Unit.Spectral.Python.Mesh\"\n        Test_Mesh.py\n        \"Unit;Spectral;Python\"\n        PySpectral)\n\nspectre_add_python_bindings_test(\n        \"Unit.Spectral.Python.Spectral\"\n        Test_Spectral.py\n        \"Unit;Spectral;Python\"\n        PySpectral)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Python/Test_LogicalCoordinates.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\nfrom random import randint\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.Spectral import (\n    Basis,\n    Mesh,\n    Quadrature,\n    collocation_points,\n    logical_coordinates,\n)\n\n\nclass TestLogicalCoordinates(unittest.TestCase):\n    def test_logical_coordinates(self):\n        bases = [Basis.Legendre, Basis.Chebyshev]\n        quads = [Quadrature.Gauss, Quadrature.GaussLobatto]\n        for dim in [1, 2, 3]:\n            mesh = Mesh[dim](\n                [randint(3, 12) for i in range(dim)],\n                [bases[randint(0, len(bases) - 1)] for i in range(dim)],\n                [quads[randint(0, len(quads) - 1)] for i in range(dim)],\n            )\n\n            # Make sure we can convert to a Numpy array\n            logical_coords = np.array(logical_coordinates(mesh))\n            # Meshgrid is matrix-indexed, and flattened in Fortran order\n            expected = np.stack(\n                [\n                    xyz.flatten(order=\"F\")\n                    for xyz in np.meshgrid(\n                        *[\n                            collocation_points(mesh_1d)\n                            for mesh_1d in mesh.slices()\n                        ],\n                        indexing=\"ij\",\n                    )\n                ]\n            )\n            npt.assert_equal(logical_coords, expected)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Python/Test_Mesh.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport pickle\nimport random\nimport unittest\n\nimport numpy as np\n\nfrom spectre.DataStructures import Index\nfrom spectre.Spectral import Basis, Mesh, Quadrature\n\n\nclass TestMesh(unittest.TestCase):\n    def setUp(self):\n        random.seed(42)\n        self.bases = [Basis.Legendre, Basis.Chebyshev, Basis.FiniteDifference]\n        self.quadratures = [\n            Quadrature.Gauss,\n            Quadrature.GaussLobatto,\n            Quadrature.CellCentered,\n            Quadrature.FaceCentered,\n        ]\n        self.extents = range(12)\n\n    def check_extents(self, mesh, extents):\n        self.assertEqual(mesh.extents(0), extents[0])\n        self.assertEqual(mesh.extents(), extents)\n        self.assertEqual(mesh.number_of_grid_points(), np.prod(extents))\n\n    def check_basis(self, mesh, bases):\n        self.assertEqual(mesh.basis(0), bases[0])\n        self.assertEqual(mesh.basis(), bases)\n\n    def check_quadrature(self, mesh, quadratures):\n        self.assertEqual(mesh.quadrature(0), quadratures[0])\n        self.assertEqual(mesh.quadrature(), quadratures)\n\n    def test_uniform(self):\n        for dim in [1, 2, 3]:\n            for extent in range(12):\n                for basis in self.bases:\n                    for quadrature in self.quadratures:\n                        mesh = Mesh[dim](extent, basis, quadrature)\n                        self.assertEqual(mesh.dim, dim)\n                        self.check_extents(\n                            mesh, Index[dim]([extent for _ in range(dim)])\n                        )\n                        self.check_basis(mesh, [basis for _ in range(dim)])\n                        self.check_quadrature(\n                            mesh, [quadrature for _ in range(dim)]\n                        )\n\n    def test_nonuniform_extents(self):\n        for dim in [1, 2, 3]:\n            for basis in self.bases:\n                for quadrature in self.quadratures:\n                    for i in range(100):\n                        extents = [\n                            random.choice(self.extents) for _ in range(dim)\n                        ]\n                        mesh = Mesh[dim](extents, basis, quadrature)\n                        self.check_extents(mesh, Index[dim](extents))\n\n    def test_nonuniform_all_with_pickle(self):\n        for dim in [1, 2, 3]:\n            for i in range(100):\n                extents = [random.choice(self.extents) for _ in range(dim)]\n                bases = [random.choice(self.bases) for _ in range(dim)]\n                quadratures = [\n                    random.choice(self.quadratures) for _ in range(dim)\n                ]\n\n                mesh = Mesh[dim](extents, bases, quadratures)\n                mesh = pickle.loads(pickle.dumps(mesh))\n                self.check_extents(mesh, Index[dim](extents))\n                self.check_basis(mesh, bases)\n                self.check_quadrature(mesh, quadratures)\n\n    def test_equality(self):\n        for dim in [1, 2, 3]:\n            for basis in self.bases:\n                for quadrature in self.quadratures:\n                    extents = [random.choice(self.extents) for _ in range(dim)]\n                    mesh = Mesh[dim](extents, basis, quadrature)\n                    self.assertTrue(\n                        mesh == Mesh[dim](extents, basis, quadrature)\n                    )\n                    self.assertFalse(\n                        mesh != Mesh[dim](extents, basis, quadrature)\n                    )\n                    self.assertTrue(\n                        mesh\n                        != Mesh[dim](\n                            [ex + 1 for ex in extents], basis, quadrature\n                        )\n                    )\n                    self.assertFalse(\n                        mesh\n                        == Mesh[dim](\n                            [ex + 1 for ex in extents], basis, quadrature\n                        )\n                    )\n\n    def test_slices(self):\n        for dim in [1, 2, 3]:\n            for basis in self.bases:\n                for quadrature in self.quadratures:\n                    extents = [random.choice(self.extents) for _ in range(dim)]\n                    mesh = Mesh[dim](extents, basis, quadrature)\n                    self.assertEqual(\n                        mesh.slices(),\n                        [\n                            Mesh[1](extent, basis, quadrature)\n                            for extent in extents\n                        ],\n                    )\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Python/Test_Spectral.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom numpy.polynomial import chebyshev, legendre\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.Spectral import (\n    Basis,\n    Mesh1D,\n    Quadrature,\n    collocation_points,\n    differentiation_matrix,\n    exponential_filter,\n    interpolation_matrix,\n    logical_coordinates,\n    modal_to_nodal_matrix,\n    nodal_to_modal_matrix,\n    quadrature_weights,\n    zero_lowest_modes,\n)\n\n\nclass TestSpectral(unittest.TestCase):\n    def test_legendre_gauss(self):\n        for n in range(2, 13):\n            xi, w = legendre.leggauss(n)\n            mesh = Mesh1D(n, Basis.Legendre, Quadrature.Gauss)\n            npt.assert_allclose(collocation_points(mesh), xi, 1e-12, 1e-12)\n            npt.assert_allclose(quadrature_weights(mesh), w, 1e-12, 1e-12)\n\n    def test_chebyshev_gauss(self):\n        for num_coll_points in range(2, 13):\n            coefs = np.zeros(num_coll_points + 1)\n            coefs[-1] = 1.0\n            coll_points_numpy = chebyshev.chebroots(coefs)\n            mesh = Mesh1D(num_coll_points, Basis.Chebyshev, Quadrature.Gauss)\n            coll_points_spectre = collocation_points(mesh)\n            np.testing.assert_allclose(\n                coll_points_numpy, coll_points_spectre, 1e-12, 1e-12\n            )\n\n    def test_legendre_gauss_lobatto(self):\n        for num_coll_points in range(2, 13):\n            coefs = np.zeros(num_coll_points)\n            coefs[-1] = 1.0\n            coll_points_numpy = np.concatenate(\n                ([-1.0], legendre.Legendre(coefs).deriv().roots(), [1.0])\n            )\n            mesh = Mesh1D(\n                num_coll_points, Basis.Legendre, Quadrature.GaussLobatto\n            )\n            coll_points_spectre = collocation_points(mesh)\n            np.testing.assert_allclose(\n                coll_points_numpy, coll_points_spectre, 1e-12, 1e-12\n            )\n\n    def test_chebyshev_gauss_lobatto(self):\n        for num_coll_points in range(2, 13):\n            coefs = np.zeros(num_coll_points)\n            coefs[-1] = 1.0\n            coll_points_numpy = np.concatenate(\n                ([-1.0], chebyshev.Chebyshev(coefs).deriv().roots(), [1.0])\n            )\n            mesh = Mesh1D(\n                num_coll_points, Basis.Chebyshev, Quadrature.GaussLobatto\n            )\n            coll_points_spectre = collocation_points(mesh)\n            np.testing.assert_allclose(\n                coll_points_numpy, coll_points_spectre, 1e-12, 1e-12\n            )\n\n    def test_differentiation_matrix(self):\n        mesh = Mesh1D(3, Basis.Legendre, Quadrature.GaussLobatto)\n        xi = np.asarray(logical_coordinates(mesh)[0])\n        # The derivative of a quadratic polynomial should be exact\n        u = 3.0 * xi**2 + 2.0 * xi + 1.0\n        D = np.asarray(differentiation_matrix(mesh))\n        du = D @ u\n        npt.assert_allclose(du, 6.0 * xi + 2.0, 1e-12, 1e-12)\n\n    def test_interpolation_matric(self):\n        mesh = Mesh1D(3, Basis.Legendre, Quadrature.GaussLobatto)\n        xi = np.asarray(logical_coordinates(mesh)[0])\n        # Interpolation to midpoint should be exact because it's a collocation\n        # point\n        u = 3.0 * xi**2 + 2.0 * xi + 1.0\n        I = np.asarray(interpolation_matrix(mesh, target_points=[0.0]))\n        npt.assert_allclose(I @ u, 1.0, 1e-12, 1e-12)\n\n    def test_modal_to_nodal_matrix_legendre(self):\n        for quadrature in [Quadrature.Gauss, Quadrature.GaussLobatto]:\n            for num_coll_points in range(2, 13):\n                mesh = Mesh1D(num_coll_points, Basis.Legendre, quadrature)\n                coll_points = collocation_points(mesh)\n                mtn_numpy = legendre.legvander(coll_points, num_coll_points - 1)\n                mtn_spectre = modal_to_nodal_matrix(mesh)\n                np.testing.assert_allclose(mtn_spectre, mtn_numpy, 1e-12, 1e-12)\n                ntm_spectre = nodal_to_modal_matrix(mesh)\n                np.testing.assert_allclose(\n                    np.matmul(mtn_spectre, ntm_spectre),\n                    np.identity(num_coll_points),\n                    1e-12,\n                    1e-12,\n                )\n\n    def test_modal_to_nodal_matrix_chebyshev(self):\n        for quadrature in [Quadrature.Gauss, Quadrature.GaussLobatto]:\n            for num_coll_points in range(2, 13):\n                mesh = Mesh1D(num_coll_points, Basis.Chebyshev, quadrature)\n                coll_points = collocation_points(mesh)\n                mtn_numpy = chebyshev.chebvander(\n                    coll_points, num_coll_points - 1\n                )\n                mtn_spectre = modal_to_nodal_matrix(mesh)\n                np.testing.assert_allclose(mtn_spectre, mtn_numpy, 1e-12, 1e-12)\n                ntm_spectre = nodal_to_modal_matrix(mesh)\n                np.testing.assert_allclose(\n                    np.matmul(mtn_spectre, ntm_spectre),\n                    np.identity(num_coll_points),\n                    1e-12,\n                    1e-12,\n                )\n\n    def test_exponential_filter(self):\n        mesh = Mesh1D(4, Basis.Legendre, Quadrature.GaussLobatto)\n        filter_matrix = exponential_filter(mesh, alpha=10.0, half_power=2)\n        self.assertEqual(filter_matrix.shape, (4, 4))\n\n    def test_zero_lowest_modes(self):\n        mesh = Mesh1D(4, Basis.Legendre, Quadrature.GaussLobatto)\n        x = np.asarray(logical_coordinates(mesh)[0])\n        u = np.exp(x)\n        filter_matrix = zero_lowest_modes(mesh, 2)\n        u_filtered = filter_matrix @ u\n        filtered_modes = nodal_to_modal_matrix(mesh) @ u_filtered\n        npt.assert_allclose(filtered_modes[:2], 0.0, 1e-12, 1e-12)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Basis.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Spectral.Basis\", \"[NumericalAlgorithms][Unit]\") {\n  CHECK(get_output(Spectral::Basis::Uninitialized) == \"Uninitialized\");\n  CHECK(get_output(Spectral::Basis::Legendre) == \"Legendre\");\n  CHECK(get_output(Spectral::Basis::Chebyshev) == \"Chebyshev\");\n  CHECK(get_output(Spectral::Basis::FiniteDifference) == \"FiniteDifference\");\n  CHECK(get_output(Spectral::Basis::SphericalHarmonic) == \"SphericalHarmonic\");\n  CHECK(get_output(Spectral::Basis::Fourier) == \"Fourier\");\n  CHECK(get_output(Spectral::Basis::ZernikeB1) == \"ZernikeB1\");\n  CHECK(get_output(Spectral::Basis::ZernikeB2) == \"ZernikeB2\");\n  CHECK(get_output(Spectral::Basis::ZernikeB3) == \"ZernikeB3\");\n  CHECK(get_output(Spectral::Basis::Cartoon) == \"Cartoon\");\n\n  for (const auto basis : Spectral::all_bases()) {\n    CHECK(basis ==\n          TestHelpers::test_creation<Spectral::Basis>(get_output(basis)));\n    CHECK(basis == Spectral::to_basis(get_output(basis)));\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        TestHelpers::test_creation<Spectral::Basis>(\"Bad basis name\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          MakeString{} << \"Failed to convert \\\"Bad basis name\\\" to \"\n                          \"Spectral::Basis.\\nMust be one of \"\n                       << Spectral::all_bases() << \".\"));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_BasisFunctionNormalizationSquare.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionNormalizationSquare.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis basis, Quadrature quadrature>\nvoid test() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const auto& [xi, w] =\n        compute_collocation_points_and_weights<basis, quadrature>(n);\n    const size_t k_max =\n        quadrature == Quadrature::Gauss ? n - 1 : (n < 2 ? 0 : n - 2);\n    CAPTURE(k_max);\n    for (size_t k = 0; k <= k_max; ++k) {\n      CAPTURE(k);\n      const auto f = compute_basis_function_value<basis>(k, xi);\n      const double expected = sum(square(f) * w);\n      CHECK(approx(expected) ==\n            compute_basis_function_normalization_square<basis>(k));\n    }\n  }\n}\n\ntemplate <Basis basis, Quadrature quadrature>\nvoid test_two_index() {\n  static_assert(basis == Basis::ZernikeB1 or basis == Basis::ZernikeB2 or\n                basis == Basis::ZernikeB3);\n  const auto custom_approx = Approx::custom().epsilon(1e-12);\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const auto& [xi, w] =\n        compute_collocation_points_and_weights<basis, quadrature>(n);\n    const size_t k_max = n < 2 ? 0 : n - 2;\n    for (size_t k = 0; k <= k_max; ++k) {\n      CAPTURE(k);\n      for (size_t m = k % 2; m <= k; m += 2) {\n        CAPTURE(m);\n        const auto f = compute_basis_function_value<basis>(k, m, xi);\n        const double expected = sum(square(f) * w);\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            expected, compute_basis_function_normalization_square<basis>(k),\n            custom_approx);\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.BasisFunctionNormalizationSquare\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test<Basis::Legendre, Quadrature::Gauss>();\n  test<Basis::Legendre, Quadrature::GaussLobatto>();\n  test<Basis::Chebyshev, Quadrature::Gauss>();\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>();\n  test<Basis::Fourier, Quadrature::Equiangular>();\n  test_two_index<Basis::ZernikeB1, Quadrature::GaussRadauUpper>();\n  test_two_index<Basis::ZernikeB2, Quadrature::GaussRadauUpper>();\n  test_two_index<Basis::ZernikeB3, Quadrature::GaussRadauUpper>();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Cartoon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n\n// The Basis::Cartoon should never be called to generate collocation points or\n// weights, so it errors on everything\n\nSPECTRE_TEST_CASE(\n    \"Unit.Numerical.Spectral.CartoonAxialSymmetry.PointsAndWeights\",\n    \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Spectral::collocation_points<Spectral::Basis::Cartoon,\n                                    Spectral::Quadrature::AxialSymmetry>(1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n  CHECK_THROWS_WITH(\n      (Spectral::quadrature_weights<Spectral::Basis::Cartoon,\n                                    Spectral::Quadrature::AxialSymmetry>(1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.CartoonAxialSymmetry.DiffMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Spectral::differentiation_matrix<\n          Spectral::Basis::Cartoon, Spectral::Quadrature::AxialSymmetry>(1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.CartoonAxialSymmetry.ModalToNodal\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Spectral::modal_to_nodal_matrix<Spectral::Basis::Cartoon,\n                                       Spectral::Quadrature::AxialSymmetry>(1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.CartoonAxialSymmetry.NodalToModal\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Spectral::nodal_to_modal_matrix<Spectral::Basis::Cartoon,\n                                       Spectral::Quadrature::AxialSymmetry>(1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Numerical.Spectral.CartoonSphericalSymmetry.PointsAndWeights\",\n    \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Spectral::collocation_points<Spectral::Basis::Cartoon,\n                                    Spectral::Quadrature::SphericalSymmetry>(\n          1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n  CHECK_THROWS_WITH(\n      (Spectral::quadrature_weights<Spectral::Basis::Cartoon,\n                                    Spectral::Quadrature::SphericalSymmetry>(\n          1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.CartoonSphericalSymmetry.DiffMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Spectral::differentiation_matrix<\n          Spectral::Basis::Cartoon, Spectral::Quadrature::SphericalSymmetry>(\n          1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Numerical.Spectral.CartoonSphericalSymmetry.ModalToNodal\",\n    \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Spectral::modal_to_nodal_matrix<Spectral::Basis::Cartoon,\n                                       Spectral::Quadrature::SphericalSymmetry>(\n          1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Numerical.Spectral.CartoonSphericalSymmetry.NodalToModal\",\n    \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Spectral::nodal_to_modal_matrix<Spectral::Basis::Cartoon,\n                                       Spectral::Quadrature::SphericalSymmetry>(\n          1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Invalid to compute collocation points and weights for a Cartoon \"\n          \"basis.\"));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_ChebyshevGauss.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\n\nvoid test_points_and_weights(const size_t num_points,\n                             const DataVector expected_points,\n                             const DataVector expected_weights) {\n  const auto& points =\n      Spectral::collocation_points<Spectral::Basis::Chebyshev,\n                                   Spectral::Quadrature::Gauss>(num_points);\n  CHECK_ITERABLE_APPROX(expected_points, points);\n  // We test the \\f$w_k\\f$ here directly. Test_Spectral.cpp and\n  // Test_DefiniteIntegral.cpp take care of testing the\n  // `Spectral::quadrature_weights`.\n  const auto weights =\n      Spectral::compute_collocation_points_and_weights<\n          Spectral::Basis::Chebyshev, Spectral::Quadrature::Gauss>(num_points)\n          .second;\n  CHECK_ITERABLE_APPROX(expected_weights, weights);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.ChebyshevGauss.PointsAndWeights\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  // Compare Chebyshev-Gauss points and weights to the analytic expressions\n  // \\f$\\x_j=-\\cos{\\frac{2j+1}{2p+2}\\pi}\\f$ and \\f$w_k=\\frac{\\pi}{N}\\f$, where\n  // \\f$p=N-1\\f$ is the polynomial degree, \\f$N\\f$ the number of collocation\n  // points and \\f$0\\leq j \\leq p\\f$ (Kopriva, eqn. (1.128)).\n\n  SECTION(\"Check 1 point\") {\n    test_points_and_weights(1, DataVector{0.}, DataVector{3.141592653589793});\n  }\n\n  SECTION(\"Check 2 points\") {\n    test_points_and_weights(2,\n                            DataVector{-0.707106781186548, 0.7071067811865475},\n                            DataVector(size_t{2}, 1.570796326794897));\n  }\n  SECTION(\"Check 3 points\") {\n    test_points_and_weights(\n        3, DataVector{-0.866025403784439, 0., 0.866025403784439},\n        DataVector(size_t{3}, 1.047197551196598));\n  }\n  SECTION(\"Check 4 points\") {\n    test_points_and_weights(4,\n                            DataVector{-0.923879532511287, -0.382683432365090,\n                                       0.3826834323650898, 0.9238795325112868},\n                            DataVector(size_t{4}, 0.785398163397448));\n  }\n  SECTION(\"Check 5 points\") {\n    test_points_and_weights(\n        5,\n        DataVector{-0.951056516295154, -0.587785252292473, 0.,\n                   0.5877852522924731, 0.9510565162951536},\n        DataVector(size_t{5}, 0.628318530717959));\n  }\n  SECTION(\"Check 6 points\") {\n    test_points_and_weights(\n        6,\n        DataVector{-0.965925826289068, -0.707106781186548, -0.258819045102521,\n                   0.258819045102521, 0.7071067811865475, 0.965925826289068},\n        DataVector(size_t{6}, 0.523598775598299));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_ChebyshevGaussLobatto.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace {\n\nvoid test_points_and_weights(const size_t num_points,\n                             const DataVector expected_points,\n                             const DataVector expected_weights) {\n  const auto& points =\n      Spectral::collocation_points<Spectral::Basis::Chebyshev,\n                                   Spectral::Quadrature::GaussLobatto>(\n          num_points);\n  CHECK_ITERABLE_APPROX(expected_points, points);\n  // We test the \\f$w_k\\f$ here directly. Test_Spectral.cpp and\n  // Test_DefiniteIntegral.cpp take care of testing the\n  // Spectral::quadrature_weights.\n  const auto weights =\n      Spectral::compute_collocation_points_and_weights<\n          Spectral::Basis::Chebyshev, Spectral::Quadrature::GaussLobatto>(\n          num_points)\n          .second;\n  CHECK_ITERABLE_APPROX(expected_weights, weights);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Numerical.Spectral.ChebyshevGaussLobatto.PointsAndWeights\",\n    \"[NumericalAlgorithms][Spectral][Unit]\") {\n  // Compare Chebyshev-Gauss-Lobatto points and weights to the analytic\n  // expressions \\f$\\x_j=-\\cos{\\frac{j\\pi}{p}}\\f$ and \\f$w_k=\\frac{\\pi}{p}\\f$\n  // except for \\f$w_{0,p}=\\frac{\\pi}{2p}\\f$, where \\f$p=N-1\\f$ is the\n  // polynomial degree, \\f$N\\f$ the number of collocation points and \\f$0\\leq j\n  // \\leq p\\f$ (Kopriva, eqn. (1.130)).\n\n  SECTION(\"Check 2 points\") {\n    test_points_and_weights(2, DataVector{-1., 1.},\n                            DataVector(size_t{2}, 1.570796326794897));\n  }\n  SECTION(\"Check 3 points\") {\n    test_points_and_weights(\n        3, DataVector{-1., 0., 1.},\n        DataVector{0.785398163397448, 1.570796326794897, 0.785398163397448});\n  }\n  SECTION(\"Check 4 points\") {\n    test_points_and_weights(4, DataVector{-1., -0.5, 0.5, 1.},\n                            DataVector{0.523598775598299, 1.047197551196598,\n                                       1.047197551196598, 0.523598775598299});\n  }\n  SECTION(\"Check 5 points\") {\n    test_points_and_weights(\n        5, DataVector{-1., -0.707106781186548, 0., 0.7071067811865475, 1.},\n        DataVector{0.3926990816987242, 0.785398163397448, 0.785398163397448,\n                   0.785398163397448, 0.3926990816987242});\n  }\n  SECTION(\"Check 6 points\") {\n    test_points_and_weights(\n        6,\n        DataVector{-1., -0.809016994374947, -0.309016994374947,\n                   0.3090169943749474, 0.809016994374947, 1.},\n        DataVector{0.3141592653589793, 0.628318530717959, 0.628318530717959,\n                   0.628318530717959, 0.628318530717959, 0.3141592653589793});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Clenshaw.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Clenshaw.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <typename BasisFunction, typename DataType>\nDataType evaluate_with_function_basis(const std::vector<double>& coefficients,\n                                      const DataType& x,\n                                      const BasisFunction& f_of_k_and_x) {\n  size_t counter = 0;\n  return std::accumulate(\n      coefficients.begin(), coefficients.end(),\n      make_with_value<DataType>(x, 0.0),\n      [&x, &f_of_k_and_x, &counter](auto&& sum, const auto& coefficient) {\n        counter++;\n        return sum + coefficient * f_of_k_and_x(counter - 1, x);\n      });\n}\n// Check that for small arguments the clenshaw expansion and the direct cosine\n// evaluation agree.\n\ntemplate <typename DataType>\nvoid test_cosine_series(const std::vector<double>& coefficients,\n                        const DataType& x) {\n  // cos(nx) = 2*x * cos((n-1)x) - cos((n-2)x)\n  // cos(nx) = alpha * cos((n-1)x) + beta cos((n-2)x)\n  const DataType alpha = 2.0 * cos(x);\n  const DataType beta = make_with_value<DataType>(x, -1.0);\n  const DataType cos_0x = make_with_value<DataType>(x, 1.0);\n  const DataType cos_x = cos(x);\n  const DataType true_value = evaluate_with_function_basis(\n      coefficients, x, [](const size_t& k, const DataType& z) {\n        return cos(static_cast<double>(k) * z);\n      });\n  const DataType clenshaw_value =\n      Spectral::evaluate_clenshaw(coefficients, alpha, beta, cos_0x, cos_x);\n  CHECK_ITERABLE_APPROX(clenshaw_value, true_value);\n}\n// More broad testing allowing a custom approx, in general the clenshaw and\n// machine computed series values may not agree to within machine precision.\ntemplate <typename DataType>\nvoid test_approximate_cosine_series(const std::vector<double>& coefficients,\n                                    const DataType& x, Approx custom_approx) {\n  // cos(nx) = 2*cos(x) * cos((n-1)x) - cos((n-2)x)\n  // cos(nx) = alpha * cos((n-1)x) + beta cos((n-2)x)\n  const DataType alpha = 2.0 * cos(x);\n  const DataType beta = make_with_value<DataType>(x, -1.0);\n  const DataType cos_0x = make_with_value<DataType>(x, 1.0);\n  const DataType cos_x = cos(x);\n  const DataType true_value = evaluate_with_function_basis(\n      coefficients, x, [](const size_t& k, const DataType& z) {\n        return cos(static_cast<double>(k) * z);\n      });\n  const DataType clenshaw_value =\n      Spectral::evaluate_clenshaw(coefficients, alpha, beta, cos_0x, cos_x);\n  CHECK_ITERABLE_CUSTOM_APPROX(clenshaw_value, true_value, custom_approx);\n}\n\ntemplate <typename DataType>\nvoid test_sine_series(const std::vector<double>& coefficients,\n                      const DataType& x) {\n  // sin(nx) = 2*cos(x) * sin((n-1)x) - sin((n-2)x)\n  // sin(nx) = alpha * sin((n-1)x) + beta sin((n-2)x)\n  const DataType alpha = 2.0 * cos(x);\n  const DataType beta = make_with_value<DataType>(x, -1.0);\n  const DataType sin_0x = make_with_value<DataType>(x, 0.0);\n  const DataType sin_x = sin(x);\n  const DataType true_value = evaluate_with_function_basis(\n      coefficients, x, [](const size_t& k, const DataType& z) {\n        return sin(static_cast<double>(k) * z);\n      });\n  const DataType clenshaw_value =\n      Spectral::evaluate_clenshaw(coefficients, alpha, beta, sin_0x, sin_x);\n  CHECK_ITERABLE_APPROX(clenshaw_value, true_value);\n}\n\n}  // namespace\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.Clenshaw\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_cosine_series({}, M_PI);\n  test_cosine_series({1.0}, 1.0);\n  test_cosine_series({1.0, 2.0, 3.6}, M_PI);\n  test_cosine_series({1.0, 2.0, 3.6, -21.1, M_E}, 2.1214353);\n  test_cosine_series({0.0, -1.5, 0.0001, -1.1, M_E}, 32.1222);\n  test_cosine_series({3200.0, 2100.0, 5065.0, 200000.0, 2211.0}, M_PI * 2.0);\n\n  test_cosine_series(\n      {1.0, 2.0, 3.6, 9.5, M_E},\n      DataVector{1.0, M_PI, 1.04, 9.0211, 5.1, -92.2, -M_PI / 2.0});\n\n  Approx coarse_approx{1.0e-12};\n  CHECK(1.0 == coarse_approx(cos(M_PI * 100000000.0)));\n  test_approximate_cosine_series({1.0, 2.0, 3.6}, 200000 * M_PI, coarse_approx);\n  test_approximate_cosine_series(\n      {1.0, 2.0, 3.6, 9.5, M_E},\n      DataVector{100020.0, M_PI * 3000.0, -10002312.0, 1.0e-11, 3.2, -22.1,\n                 -M_PI / 2000.0},\n      coarse_approx);\n\n  test_sine_series({}, M_PI);\n  test_sine_series({1.0}, 1.0);\n  test_sine_series({1.0, 2.0, 3.6}, M_PI);\n  test_sine_series({1.0, 2.0, 3.6, -21.1, M_E}, 2.1214353);\n  test_sine_series({0.0, -1.5, 0.0001, -1.1, M_E}, 32.1222);\n  test_sine_series({3200.0, 2100.0, 5065.0, 200000.0, 2211.0}, M_PI * 2.0);\n\n  test_sine_series({1.0, 2.0, 3.6, 9.5, M_E},\n                   DataVector{1.0, M_PI, 1.04, 9.0211, 5.1, -9.2, -M_PI / 2.0});\n}\n\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_CollocationPointsAndWeights.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis basis, Quadrature quadrature>\nvoid test() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const auto& [xi, w] =\n        compute_collocation_points_and_weights<basis, quadrature>(n);\n    const size_t k_max = quadrature == Quadrature::Gauss ? n : n - 1;\n    CAPTURE(k_max);\n    for (size_t k = 1; k <= k_max; ++k) {\n      CAPTURE(k);\n      const auto f_k = compute_basis_function_value<basis>(k, xi);\n      for (size_t j = 0; j < k; ++j) {\n        CAPTURE(j);\n        const auto f_j = compute_basis_function_value<basis>(j, xi);\n        const double should_be_zero = sum(f_j * f_k * w);\n        CHECK(should_be_zero == approx(0.0));\n      }\n    }\n  }\n}\n\ntemplate <Basis basis>\nvoid test_radial_zernike() {\n  // Needs its own test because radial zernike has two indices\n  // Second index m must match parity of k, and satisfy m <= k\n  // Checking \\int_0^1 Q^m_n Q^m_{n'} \\propto \\delta_{n,n'} (no constraint on m)\n  const auto quadrature = Quadrature::GaussRadauUpper;\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  const Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const auto& [xi, w] =\n        compute_collocation_points_and_weights<basis, quadrature>(n);\n    const size_t k_max = n - 1;\n    CAPTURE(k_max);\n    for (size_t k = 1; k <= k_max; ++k) {\n      CAPTURE(k);\n      for (size_t j = k % 2; j < k; j += 2) {\n        CAPTURE(j);\n        for (size_t m = k % 2; m <= j; m += 2) {\n          CAPTURE(m);\n          const auto f_m_k = compute_basis_function_value<basis>(k, m, xi);\n          const auto f_m_j = compute_basis_function_value<basis>(j, m, xi);\n          const double should_be_zero = sum(f_m_j * f_m_k * w);\n          CHECK_ITERABLE_CUSTOM_APPROX(should_be_zero, 0.0, custom_approx);\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.CollocationPointsAndWeights\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test<Basis::Legendre, Quadrature::Gauss>();\n  test<Basis::Legendre, Quadrature::GaussLobatto>();\n  test<Basis::Chebyshev, Quadrature::Gauss>();\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>();\n  test<Basis::Fourier, Quadrature::Equiangular>();\n  test_radial_zernike<Basis::ZernikeB1>();\n  test_radial_zernike<Basis::ZernikeB2>();\n  test_radial_zernike<Basis::ZernikeB3>();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_DifferentiationMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis basis, Quadrature quadrature>\nvoid test() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  const auto custom_approx = Approx::custom().epsilon(5.0e-13).scale(1.0);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const Matrix& m = differentiation_matrix<basis, quadrature>(n);\n    const DataVector one{n, 1.0};\n    const DataVector should_be_zero = apply_matrix(m, one);\n    const DataVector zero{n, 0.0};\n    CHECK_ITERABLE_CUSTOM_APPROX(should_be_zero, zero, custom_approx);\n  }\n}\n\ntemplate <Basis basis, Quadrature quadrature>\nvoid test_with_parity() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  const auto custom_approx = Approx::custom().epsilon(5.0e-13).scale(1.0);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const Matrix& m_even =\n        differentiation_matrix<basis, quadrature>(n, Parity::Even);\n    const DataVector one{n, 1.0};\n    const DataVector should_be_zero = apply_matrix(m_even, one);\n    const DataVector zero{n, 0.0};\n    CHECK_ITERABLE_CUSTOM_APPROX(should_be_zero, zero, custom_approx);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.DifferentiationMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test<Basis::Legendre, Quadrature::Gauss>();\n  test<Basis::Legendre, Quadrature::GaussLobatto>();\n  test<Basis::Chebyshev, Quadrature::Gauss>();\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>();\n  test_with_parity<Basis::ZernikeB1, Quadrature::GaussRadauUpper>();\n  test_with_parity<Basis::ZernikeB2, Quadrature::GaussRadauUpper>();\n  test_with_parity<Basis::ZernikeB3, Quadrature::GaussRadauUpper>();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Filtering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Filtering.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Blas.hpp\"\n\nnamespace {\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nvoid test_exponential_filter(const double alpha, const unsigned half_power,\n                             const double eps) {\n  Approx local_approx = Approx::custom().epsilon(eps).scale(1.0);\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n  CAPTURE(eps);\n  for (size_t num_pts =\n           Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       num_pts <= Spectral::maximum_number_of_points<BasisType>; ++num_pts) {\n    CAPTURE(num_pts);\n    const Mesh<1> mesh{num_pts, BasisType, QuadratureType};\n    ModalVector initial_modal_coeffs(num_pts);\n    for (size_t i = 0; i < num_pts; ++i) {\n      initial_modal_coeffs = i + 1.0;\n    }\n    const DataVector initial_nodal_coeffs =\n        to_nodal_coefficients(initial_modal_coeffs, mesh);\n    DataVector filtered_nodal_coeffs(num_pts);\n    const Matrix filter_matrix =\n        Spectral::filtering::exponential_filter(mesh, alpha, half_power);\n    dgemv_('N', num_pts, num_pts, 1., filter_matrix.data(),\n           filter_matrix.spacing(), initial_nodal_coeffs.data(), 1, 0.0,\n           filtered_nodal_coeffs.data(), 1);\n    const ModalVector filtered_modal_coeffs =\n        to_modal_coefficients(filtered_nodal_coeffs, mesh);\n    const double basis_order = static_cast<double>(num_pts) - 1;\n    for (size_t i = 0; i < num_pts; ++i) {\n      CAPTURE(i);\n      if (num_pts == 1) {\n        // In the case of only 1 coefficient there should be no filtering.\n        CHECK(filtered_modal_coeffs[i] == initial_modal_coeffs[i]);\n      } else {\n        CHECK(filtered_modal_coeffs[i] ==\n              local_approx(initial_modal_coeffs[i] *\n                           exp(-alpha * pow(i / basis_order, 2 * half_power))));\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.ExponentialFilter\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  const std::vector<double> alphas{10.0, 20.0, 30.0, 40.0};\n  const std::vector<unsigned> half_powers{2, 4, 8, 16};\n  for (const double alpha : alphas) {\n    for (const unsigned half_power : half_powers) {\n      test_exponential_filter<Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto>(\n          alpha, half_power, 2.0e-12);\n      test_exponential_filter<Spectral::Basis::Legendre,\n                              Spectral::Quadrature::Gauss>(alpha, half_power,\n                                                           1.0e-10);\n      test_exponential_filter<Spectral::Basis::Chebyshev,\n                              Spectral::Quadrature::GaussLobatto>(\n          alpha, half_power, 2.0e-12);\n      test_exponential_filter<Spectral::Basis::Chebyshev,\n                              Spectral::Quadrature::Gauss>(alpha, half_power,\n                                                           1.0e-10);\n    }\n  }\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nvoid test_zero_lowest_modes() {\n  Approx local_approx = Approx::custom().epsilon(1.0e-11);\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n  for (size_t num_pts =\n           Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       num_pts <= Spectral::maximum_number_of_points<BasisType>; ++num_pts) {\n    CAPTURE(num_pts);\n    for (size_t number_of_modes_to_filter = 0;\n         number_of_modes_to_filter < num_pts; ++number_of_modes_to_filter) {\n      CAPTURE(number_of_modes_to_filter);\n      const Mesh<1> mesh{num_pts, BasisType, QuadratureType};\n      ModalVector initial_modal_coeffs(num_pts);\n      for (size_t i = 0; i < num_pts; ++i) {\n        initial_modal_coeffs = i + 1.0;\n      }\n      const DataVector initial_nodal_coeffs =\n          to_nodal_coefficients(initial_modal_coeffs, mesh);\n      DataVector filtered_nodal_coeffs(num_pts);\n      const Matrix& filter_matrix = Spectral::filtering::zero_lowest_modes(\n          mesh, number_of_modes_to_filter);\n      dgemv_('N', num_pts, num_pts, 1., filter_matrix.data(),\n             filter_matrix.spacing(), initial_nodal_coeffs.data(), 1, 0.0,\n             filtered_nodal_coeffs.data(), 1);\n      const ModalVector filtered_modal_coeffs =\n          to_modal_coefficients(filtered_nodal_coeffs, mesh);\n      for (size_t i = 0; i < num_pts; ++i) {\n        CAPTURE(i);\n        if (i < number_of_modes_to_filter) {\n          CHECK(fabs(filtered_modal_coeffs[i]) < 1.0e-11);\n        } else {\n          CHECK(local_approx(filtered_modal_coeffs[i]) ==\n                initial_modal_coeffs[i]);\n        }\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.ZeroLowestModesFilter\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_zero_lowest_modes<Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto>();\n  test_zero_lowest_modes<Spectral::Basis::Legendre,\n                         Spectral::Quadrature::Gauss>();\n  test_zero_lowest_modes<Spectral::Basis::Chebyshev,\n                         Spectral::Quadrature::GaussLobatto>();\n  test_zero_lowest_modes<Spectral::Basis::Chebyshev,\n                         Spectral::Quadrature::Gauss>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_FiniteDifference.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace Spectral {\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.Fd.Points\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(DataVector{-2.0 / 3.0, 0.0, 2.0 / 3.0}),\n      SINGLE_ARG(\n          collocation_points<Basis::FiniteDifference, Quadrature::CellCentered>(\n              3)));\n  CHECK(\n      DataVector{-0.75, -0.25, 0.25, 0.75} ==\n      collocation_points<Basis::FiniteDifference, Quadrature::CellCentered>(4));\n  CHECK(get_output(Basis::FiniteDifference) == \"FiniteDifference\");\n  CHECK(get_output(Quadrature::CellCentered) == \"CellCentered\");\n\n  CHECK(\n      DataVector{-1.0, 0.0, 1.0} ==\n      collocation_points<Basis::FiniteDifference, Quadrature::FaceCentered>(3));\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(DataVector{-1.0, -1.0 / 3.0, 1.0 / 3.0, 1.0}),\n      SINGLE_ARG(\n          collocation_points<Basis::FiniteDifference, Quadrature::FaceCentered>(\n              4)));\n  CHECK(get_output(Quadrature::FaceCentered) == \"FaceCentered\");\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_IndefiniteIntegral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/IntegrationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Blas.hpp\"\n\nnamespace Spectral {\nnamespace {\nDataVector integrate(const DataVector& u, const Mesh<1>& mesh) {\n  const size_t num_pts = mesh.number_of_grid_points();\n  DataVector result(num_pts, 0.0);\n  const Matrix& indef_int_with_constant = integration_matrix(mesh);\n  dgemv_('N', num_pts, num_pts, 1.0, indef_int_with_constant.data(),\n         indef_int_with_constant.spacing(), u.data(), 1, 0.0, result.data(), 1);\n  return result;\n}\n\ntemplate <Basis BasisType, Quadrature QuadratureType>\nvoid check_integration(const size_t min_pts, const size_t max_pts) {\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n  for (size_t num_pts = min_pts; num_pts < max_pts; ++num_pts) {\n    CAPTURE(num_pts);\n    const Mesh<1> mesh{num_pts, BasisType, QuadratureType};\n    const auto logical_coords = logical_coordinates(mesh);\n    DataVector u(num_pts);\n    DataVector u_int_exact(num_pts);\n    // Single term integrals\n    for (size_t i = 0; i < num_pts - 1; ++i) {\n      u = pow(get<0>(logical_coords), i);\n      u_int_exact = 1.0 / (i + 1.0) * pow(get<0>(logical_coords), i + 1);\n      u_int_exact -= 1.0 / (i + 1.0) * pow(-1.0, i + 1);\n      const auto u_int_computed = integrate(u, mesh);\n      CHECK_ITERABLE_APPROX(u_int_exact, u_int_computed);\n    }\n    // Full sum of terms\n    u = 1.0;\n    u_int_exact = get<0>(logical_coords);\n    double u_int_exact_boundary = -1.0;\n    for (size_t i = 1; i < num_pts - 1; ++i) {\n      u += pow(get<0>(logical_coords), i);\n      u_int_exact += 1.0 / (i + 1.0) * pow(get<0>(logical_coords), i + 1);\n      u_int_exact_boundary += 1.0 / (i + 1.0) * pow(-1.0, i + 1);\n    }\n    u_int_exact -= u_int_exact_boundary;\n    const auto u_int_computed = integrate(u, mesh);\n    CHECK_ITERABLE_APPROX(u_int_exact, u_int_computed);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.IndefiniteIntegral\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  check_integration<Basis::Chebyshev, Quadrature::GaussLobatto>(\n      2, maximum_number_of_points<Basis::Chebyshev>);\n  check_integration<Basis::Chebyshev, Quadrature::Gauss>(\n      2, maximum_number_of_points<Basis::Chebyshev>);\n  check_integration<Basis::Legendre, Quadrature::GaussLobatto>(\n      2, maximum_number_of_points<Basis::Legendre>);\n  check_integration<Basis::Legendre, Quadrature::Gauss>(\n      2, maximum_number_of_points<Basis::Legendre>);\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_IntegrationMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/IntegrationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis basis, Quadrature quadrature>\nvoid test() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const Matrix& m = integration_matrix<basis, quadrature>(n);\n    if (UNLIKELY(n == 1)) {\n      // Cannot represent the indefinite integral of a constant with a\n      // constant\n      CHECK(m == Matrix{n, n, 0.0});\n    } else {\n      // The integral of one should be xi plus a constant. The indefinite\n      // integral determines the integration constant by making the integral be\n      // zero at xi = - 1.  Therefore the expected answer is xi + 1.\n      const DataVector one{n, 1.0};\n      const DataVector should_be_xi_plus_one = apply_matrix(m, one);\n      const DataVector xi_plus_one =\n          one + collocation_points<basis, quadrature>(n);\n      CHECK_ITERABLE_APPROX(should_be_xi_plus_one, xi_plus_one);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.IntegrationMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test<Basis::Legendre, Quadrature::Gauss>();\n  test<Basis::Legendre, Quadrature::GaussLobatto>();\n  test<Basis::Chebyshev, Quadrature::Gauss>();\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>();\n  // no known form of an integraion matrix for Zernike\n  // there is no integration matrix for Fourier\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_InterpolationMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis basis, Quadrature quadrature>\nvoid test(const DataVector& target_pts) {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  const auto custom_approx = basis == Basis::Fourier\n                                 ? Approx::custom().epsilon(5.0e-12).scale(1.0)\n                                 : Approx::custom().epsilon(5.0e-13).scale(1.0);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    // If target points are source points, matrix should be identity\n    const DataVector xi = collocation_points<basis, quadrature>(n);\n    const Matrix should_be_identity =\n        interpolation_matrix<basis, quadrature>(n, xi);\n    Matrix identity{n, n, 0.0};\n    for (size_t i = 0; i < n; ++i) {\n      identity(i, i) = 1.0;\n    }\n    CHECK_ITERABLE_APPROX(should_be_identity, identity);\n    const Matrix m = interpolation_matrix<basis, quadrature>(n, target_pts);\n    REQUIRE(m.rows() == 5);\n    REQUIRE(m.columns() == n);\n    // Interpolating one should give one\n    const DataVector one{n, 1.0};\n    const DataVector should_be_one = apply_matrix(m, one);\n    for (size_t i = 0; i < m.rows(); ++i) {\n      CAPTURE(i);\n      CHECK(should_be_one[i] == custom_approx(1.0));\n    }\n    // Interpolating a basis function\n    for (size_t k = 0; k < n; ++k) {\n      CAPTURE(k);\n      const auto f_s = compute_basis_function_value<basis>(k, xi);\n      const auto f_t = compute_basis_function_value<basis>(k, target_pts);\n      const auto f_i = apply_matrix(m, f_s);\n      CHECK_ITERABLE_CUSTOM_APPROX(f_i, f_t, custom_approx);\n    }\n  }\n}\n}  // namespace\n\n// [[Timeout, 20]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.InterpolationMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  const auto xi_target_pts = make_with_random_values<DataVector>(\n      make_not_null(&generator), make_not_null(&xi_distribution), 5_st);\n  test<Basis::Legendre, Quadrature::Gauss>(xi_target_pts);\n  test<Basis::Legendre, Quadrature::GaussLobatto>(xi_target_pts);\n  test<Basis::Chebyshev, Quadrature::Gauss>(xi_target_pts);\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>(xi_target_pts);\n  std::uniform_real_distribution<> phi_distribution(0.0, 2.0 * M_PI);\n  const auto phi_target_pts = make_with_random_values<DataVector>(\n      make_not_null(&generator), make_not_null(&phi_distribution), 5_st);\n  test<Basis::Fourier, Quadrature::Equiangular>(phi_target_pts);\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_InterpolationWeights.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nvoid test_fornberg_matrix(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  for (size_t n_target_points = 1; n_target_points < 101;\n       n_target_points += 11) {\n    const auto x_target = make_with_random_values<DataVector>(\n        generator, make_not_null(&xi_distribution), n_target_points);\n    for (size_t n_source_points = 4; n_source_points < 20; ++n_source_points) {\n      // Check that we get the same matrices as Spectral::interpolation_matrix\n      // for collocation points of existing Basis and Quadrature\n      for (const auto basis :\n           std::array{Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}) {\n        for (const auto quadrature :\n             std::array{Spectral::Quadrature::Gauss,\n                        Spectral::Quadrature::GaussLobatto}) {\n          const Mesh<1> mesh{n_source_points, basis, quadrature};\n          const auto xi = logical_coordinates(mesh);\n          const Matrix spectral_matrix =\n              Spectral::interpolation_matrix(mesh, x_target);\n          const Matrix fornberg_matrix =\n              Spectral::fornberg_interpolation_matrix(x_target, get<0>(xi));\n          CHECK(fornberg_matrix.rows() == n_target_points);\n          CHECK(fornberg_matrix.columns() == n_source_points);\n          CHECK(spectral_matrix == fornberg_matrix);\n          if (n_target_points == 1) {\n            CHECK(Spectral::fornberg_interpolation_matrix(\n                      x_target[0], get<0>(xi)) == fornberg_matrix);\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_fornberg_derivative_coefficients(\n    const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> xi_distribution(-1.0, 1.0);\n  const size_t max_derivative = 4;\n  std::array<DataVector, max_derivative + 1> fornberg_weights{};\n  const size_t n_target_points = 5;\n  const auto x_targets = make_with_random_values<DataVector>(\n      generator, make_not_null(&xi_distribution), n_target_points);\n  for (size_t n_source_points = 3; n_source_points < 5; ++n_source_points) {\n    const Mesh<1> mesh{n_source_points, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::Gauss};\n    const auto xi = logical_coordinates(mesh);\n    for (auto x_target : x_targets) {\n      Spectral::fornberg_derivative_interpolation_weights<max_derivative>(\n          make_not_null(&fornberg_weights), x_target, get<0>(xi));\n      double coefficient = 1.0;\n      auto power = static_cast<int>(n_source_points - 1);\n      for (size_t derivative = 0; derivative <= n_source_points; ++derivative) {\n        const DataVector function = pow(get<0>(xi), power);\n        const double result = std::inner_product(\n            function.begin(), function.end(),\n            gsl::at(fornberg_weights, derivative).begin(), 0.0);\n        CHECK(approx(result) ==\n              coefficient *\n                  pow(x_target, power - static_cast<int>(derivative)));\n        coefficient *= power - static_cast<int>(derivative);\n      }\n    }\n  }\n}\n\nDataVector f_periodic(const DataVector& x) { return sin(M_PI * cos(x)); }\n\nvoid test_fourier_matrix(const gsl::not_null<std::mt19937*> generator) {\n  std::uniform_real_distribution<> phi_distribution(0.0, 2 * M_PI);\n  const size_t n_source_points = 48;\n  const DataVector x_source = get<0>(logical_coordinates(\n      Mesh<1>{n_source_points, Spectral::Basis::SphericalHarmonic,\n              Spectral::Quadrature::Equiangular}));\n  DataVector f_source = f_periodic(x_source);\n  for (size_t n_target_points = 1; n_target_points < 101;\n       n_target_points += 11) {\n    const auto x_target = make_with_random_values<DataVector>(\n        generator, make_not_null(&phi_distribution), n_target_points);\n    const DataVector f_target = f_periodic(x_target);\n    const Matrix m =\n        Spectral::fourier_interpolation_matrix(x_target, n_source_points);\n    DataVector f_interp{n_target_points};\n    dgemv_('N', n_target_points, n_source_points, 1.0, m.data(),\n           n_target_points, f_source.data(), 1, 0.0, f_interp.data(), 1);\n    for (size_t k = 0; k < n_target_points; ++k) {\n      CHECK_THAT(f_interp[k], Catch::Matchers::WithinAbs(f_target[k], 1.e-13));\n    }\n    if (n_target_points == 1) {\n      CHECK(Spectral::fourier_interpolation_matrix(x_target[0],\n                                                   n_source_points) == m);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Interpolation.Weights\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  MAKE_GENERATOR(generator);\n  test_fornberg_matrix(make_not_null(&generator));\n  test_fornberg_derivative_coefficients(make_not_null(&generator));\n  test_fourier_matrix(make_not_null(&generator));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Legendre.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Interpolation/IrregularInterpolant.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/Legendre.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ndouble evaluate_by_summing_legendre_basis(const ModalVector& coefficients,\n                                          const double x) {\n  double sum = 0.0;\n  for (size_t k = 0; k < coefficients.size(); ++k) {\n    sum +=\n        coefficients[k] *\n        Spectral::compute_basis_function_value<Spectral::Basis::Legendre>(k, x);\n  }\n  return sum;\n}\n\ntemplate <size_t Dim>\nvoid check_against_irregular(\n    const std::array<size_t, Dim>& extents_array,\n    const gsl::not_null<std::mt19937*> generator,\n    std::uniform_real_distribution<>& coefficient_distribution,\n    std::uniform_real_distribution<>& logical_distribution) {\n  const size_t num_test_points = 100;\n  const Mesh<Dim> mesh(extents_array, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const auto modal_coefficients = make_with_random_values<ModalVector>(\n      generator, make_not_null(&coefficient_distribution),\n      ModalVector{mesh.number_of_grid_points()});\n  const auto logical_point =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::ElementLogical>>(\n          generator, make_not_null(&logical_distribution), num_test_points);\n\n  const DataVector nodal_coefficients =\n      to_nodal_coefficients(modal_coefficients, mesh);\n  const intrp::Irregular<Dim> interpolant(mesh, logical_point);\n  const DataVector nodal_value = interpolant.interpolate(nodal_coefficients);\n  const Approx custom_approx = Approx::custom().epsilon(1.0e-12).scale(1.0);\n  for (size_t i = 0; i < num_test_points; ++i) {\n    tnsr::I<double, Dim, Frame::ElementLogical> single_logical_point{};\n    for (size_t d = 0; d < Dim; ++d) {\n      single_logical_point.get(d) = logical_point.get(d)[i];\n    }\n    CHECK(Spectral::evaluate_legendre_series<Dim>(modal_coefficients, mesh,\n                                                  single_logical_point) ==\n          custom_approx(nodal_value[i]));\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.Legendre.Series\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> coefficient_distribution(-2.0, 2.0);\n  std::uniform_real_distribution<> logical_distribution(-1.0, 1.0);\n  const auto evaluation_points = make_with_random_values<std::array<double, 5>>(\n      make_not_null(&generator), make_not_null(&logical_distribution),\n      std::array<double, 5>{});\n\n  const auto cubic_coefficients = make_with_random_values<ModalVector>(\n      make_not_null(&generator), make_not_null(&coefficient_distribution),\n      ModalVector{4});\n  const Mesh<1> cubic_mesh({{cubic_coefficients.size()}},\n                           Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto);\n  for (const double x : evaluation_points) {\n    CHECK(Spectral::evaluate_legendre_series<1>(\n              cubic_coefficients, cubic_mesh,\n              tnsr::I<double, 1, Frame::ElementLogical>{{{x}}}) ==\n          approx(evaluate_by_summing_legendre_basis(cubic_coefficients, x)));\n  }\n\n  const auto higher_order = make_with_random_values<ModalVector>(\n      make_not_null(&generator), make_not_null(&coefficient_distribution),\n      ModalVector{20});\n  const Mesh<1> higher_mesh({{higher_order.size()}}, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto);\n  for (const double x : evaluation_points) {\n    CHECK(Spectral::evaluate_legendre_series<1>(\n              higher_order, higher_mesh,\n              tnsr::I<double, 1, Frame::ElementLogical>{{{x}}}) ==\n          approx(evaluate_by_summing_legendre_basis(higher_order, x)));\n  }\n\n  check_against_irregular<1>({{17}}, make_not_null(&generator),\n                             coefficient_distribution, logical_distribution);\n  check_against_irregular<2>({{20, 19}}, make_not_null(&generator),\n                             coefficient_distribution, logical_distribution);\n  check_against_irregular<3>({{12, 10, 7}}, make_not_null(&generator),\n                             coefficient_distribution, logical_distribution);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_LegendreGauss.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n\nnamespace {\n\nvoid test_points_and_weights(const size_t num_points,\n                             const DataVector expected_points,\n                             const DataVector expected_weights) {\n  const auto& points =\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::Gauss>(num_points);\n  CHECK_ITERABLE_APPROX(expected_points, points);\n  const auto& weights =\n      Spectral::quadrature_weights<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::Gauss>(num_points);\n  CHECK_ITERABLE_APPROX(expected_weights, weights);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.LegendreGauss.PointsAndWeights\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  // Compare LG points to matlab code accompanying the\n  // book \"Nodal Discontinuous Galerkin Methods\" by Hesthaven and Warburton\n  // available at http://www.nudg.org/. To compute `num_points` LG points, run\n  // the routine `JacobiGQ(0, 0, num_points - 1)`.\n  // Quadrature weights computed in Mathematica, code available on request from\n  // nils.fischer@aei.mpg.de.\n\n  SECTION(\"Check 1 point\") {\n    test_points_and_weights(1, DataVector{0.}, DataVector{2.});\n  }\n\n  SECTION(\"Check 2 points\") {\n    test_points_and_weights(2,\n                            DataVector{-0.577350269189626, 0.577350269189626},\n                            DataVector{1., 1.});\n  }\n  SECTION(\"Check 3 points\") {\n    test_points_and_weights(\n        3, DataVector{-0.774596669241483, 0., 0.774596669241483},\n        DataVector{{0.555555555555556, 0.888888888888888, 0.555555555555556}});\n  }\n  SECTION(\"Check 4 points\") {\n    test_points_and_weights(4,\n                            DataVector{-0.861136311594053, -0.339981043584856,\n                                       0.339981043584856, 0.861136311594053},\n                            DataVector{{0.347854845137454, 0.652145154862546,\n                                        0.652145154862546, 0.347854845137454}});\n  }\n  SECTION(\"Check 5 points\") {\n    test_points_and_weights(\n        5,\n        DataVector{-0.906179845938664, -0.538469310105683, 0.,\n                   0.538469310105683, 0.906179845938664},\n        DataVector{{0.236926885056189, 0.478628670499366, 0.568888888888889,\n                    0.478628670499366, 0.236926885056189}});\n  }\n}\n\nnamespace {\n\nvoid test_diff_matrix(const size_t num_points, const Matrix& expected_matrix) {\n  const auto& diff_matrix =\n      Spectral::differentiation_matrix<Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::Gauss>(num_points);\n  CHECK_ITERABLE_APPROX(expected_matrix, diff_matrix);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.LegendreGauss.DiffMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  // Compare differentiation matrix values to matlab code accompanying the\n  // book \"Nodal Discontinuous Galerkin Methods\" by Hesthaven and Warburton\n  // http://www.nudg.org/\n  //\n  // Command to generate differentation matrix with the Matlab code:\n  // DN = Dmatrix1D(N, JacobiGQ(0,0,N), Vandermonde1D(N, JacobiGQ(0,0,N)))\n\n  SECTION(\"Check 1 point\") { test_diff_matrix(1, Matrix(1, 1, 0.)); }\n\n  SECTION(\"Check 2 points\") {\n    test_diff_matrix(2, Matrix({{-0.866025403784438, 0.866025403784438},\n                                {-0.866025403784438, 0.866025403784438}}));\n  }\n\n  SECTION(\"Check 3 points\") {\n    test_diff_matrix(\n        3, Matrix({{-1.93649167310371, 2.58198889747161, -0.645497224367903},\n                   {-0.645497224367903, 0, 0.645497224367903},\n                   {0.645497224367903, -2.58198889747161, 1.93649167310371}}));\n  }\n\n  SECTION(\"Check 4 points\") {\n    test_diff_matrix(4, Matrix({{-3.33200023635228, 4.86015441568520,\n                                 -2.10878234849518, 0.580628169162264},\n                                {-0.757557614799232, -0.384414392223212,\n                                 1.47067023128072, -0.328698224258274},\n                                {0.328698224258274, -1.47067023128072,\n                                 0.384414392223213, 0.757557614799232},\n                                {-0.580628169162264, 2.10878234849518,\n                                 -4.86015441568520, 3.33200023635228}}));\n  }\n\n  SECTION(\"Check 5 points\") {\n    test_diff_matrix(\n        5, Matrix({{-5.06704059565454, 7.70195208517225, -4.04354375438766,\n                    1.96039911583328, -0.551766850963317},\n                   {-0.960256023631957, -0.758353217167877, 2.40275065216430,\n                    -0.928558026643834, 0.244416615279368},\n                   {0.301168159727831, -1.43538824233474, 0, 1.43538824233474,\n                    -0.301168159727831},\n                   {-0.244416615279368, 0.928558026643835, -2.40275065216430,\n                    0.758353217167876, 0.960256023631957},\n                   {0.551766850963317, -1.96039911583328, 4.04354375438767,\n                    -7.70195208517225, 5.06704059565454}}));\n  }\n}\n\nnamespace {\n\nvoid test_modal_to_nodal_matrix(const size_t num_points,\n                                const Matrix& expected_matrix) {\n  const auto& matrix =\n      Spectral::modal_to_nodal_matrix<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::Gauss>(num_points);\n  CHECK_ITERABLE_APPROX(expected_matrix, matrix);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.LegendreGauss.ModalToNodal\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  SECTION(\"Check 1 point\") { test_modal_to_nodal_matrix(1, Matrix(1, 1, 1.)); }\n  SECTION(\"Check 2 points\") {\n    test_modal_to_nodal_matrix(\n        2, Matrix({{1., -0.5773502691896258}, {1., 0.5773502691896258}}));\n  }\n  SECTION(\"Check 3 points\") {\n    test_modal_to_nodal_matrix(3, Matrix({{1., -0.7745966692414830, 0.4},\n                                          {1., 0., -0.5},\n                                          {1., 0.7745966692414830, 0.4}}));\n  }\n  SECTION(\"Check 4 points\") {\n    test_modal_to_nodal_matrix(\n        4,\n        Matrix(\n            {{1., -0.8611363115940530, 0.6123336207187148, -0.3047469849552077},\n             {1., -0.3399810435848560, -0.3266193350044284, 0.4117279996728994},\n             {1., 0.3399810435848560, -0.3266193350044284, -0.4117279996728994},\n             {1., 0.8611363115940530, 0.6123336207187148,\n              0.3047469849552077}}));\n  }\n  SECTION(\"Check 5 points\") {\n    test_modal_to_nodal_matrix(\n        5, Matrix({{1., -0.9061798459386637, 0.7317428697781310,\n                    -0.5010311710446620, 0.2457354590949121},\n                   {1., -0.5384693101056831, -0.06507620311146464,\n                    0.4173821037266682, -0.3445008911936774},\n                   {1., 0., -0.4999999999999999, 0., 0.3749999999999999},\n                   {1., 0.5384693101056831, -0.06507620311146464,\n                    -0.4173821037266682, -0.3445008911936774},\n                   {1., 0.9061798459386637, 0.7317428697781310,\n                    0.5010311710446620, 0.2457354590949121}}));\n  }\n}\n\nnamespace {\n\nvoid test_nodal_to_modal_matrix(const size_t num_points,\n                                const Matrix& expected_matrix) {\n  const auto& matrix =\n      Spectral::nodal_to_modal_matrix<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::Gauss>(num_points);\n  CHECK_ITERABLE_APPROX(expected_matrix, matrix);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.LegendreGauss.NodalToModal\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  SECTION(\"Check 1 point\") { test_nodal_to_modal_matrix(1, Matrix(1, 1, 1.)); }\n  SECTION(\"Check 2 points\") {\n    test_nodal_to_modal_matrix(\n        2, Matrix({{0.5, 0.5}, {-0.8660254037844385, 0.8660254037844385}}));\n  }\n  SECTION(\"Check 3 points\") {\n    test_nodal_to_modal_matrix(\n        3,\n        Matrix({{0.2777777777777781, 0.4444444444444438, 0.2777777777777781},\n                {-0.6454972243679031, 0, 0.6454972243679031},\n                {0.5555555555555561, -1.111111111111112, 0.5555555555555561}}));\n  }\n  SECTION(\"Check 4 points\") {\n    test_nodal_to_modal_matrix(\n        4, Matrix({{0.1739274225687268, 0.3260725774312732, 0.3260725774312732,\n                    0.1739274225687269},\n                   {-0.4493256574676805, -0.3325754854784655,\n                    0.3325754854784655, 0.4493256574676804},\n                   {0.5325080420189108, -0.5325080420189108,\n                    -0.5325080420189108, 0.5325080420189108},\n                   {-0.3710270034019465, 0.9397724703777526,\n                    -0.9397724703777526, 0.3710270034019466}}));\n  }\n  SECTION(\"Check 5 points\") {\n    test_nodal_to_modal_matrix(\n        5,\n        Matrix({{0.1184634425280945, 0.2393143352496832, 0.2844444444444443,\n                 0.2393143352496832, 0.1184634425280947},\n                {-0.3220475522984176, -0.3865902750008912, 0.,\n                 0.3865902750008913, 0.3220475522984176},\n                {0.4334238969965231, -0.07786834144096745, -0.7111111111111112,\n                 -0.07786834144096744, 0.4334238969965231},\n                {-0.4154771413508326, 0.6991986448892331, 0.,\n                 -0.6991986448892332, 0.4154771413508326},\n                {0.2619960159204452, -0.7419960159204453, 0.96,\n                 -0.7419960159204452, 0.2619960159204453}}));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_LegendreGaussLobatto.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n\nnamespace {\n\nvoid test_points_and_weights(const size_t num_points,\n                             const DataVector expected_points,\n                             const DataVector expected_weights) {\n  const auto& points =\n      Spectral::collocation_points<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(\n          num_points);\n  CHECK_ITERABLE_APPROX(expected_points, points);\n  const auto& weights =\n      Spectral::quadrature_weights<Spectral::Basis::Legendre,\n                                   Spectral::Quadrature::GaussLobatto>(\n          num_points);\n  Approx local_approx = Approx::custom().epsilon(1.0e-8);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_weights, weights, local_approx);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Numerical.Spectral.LegendreGaussLobatto.PointsAndWeights\",\n    \"[NumericalAlgorithms][Spectral][Unit]\") {\n  // Compare LGL points to Matlab code accompanying the\n  // book \"Nodal Discontinuous Galerkin Methods\" by Hesthaven and Warburton\n  // available at http://www.nudg.org/. To compute `num_points` LGL points, run\n  // the routine `JacobiGL(0, 0, num_points - 1)`.\n  // Quadrature weights computed in Mathematica, code available on request from\n  // nils.fischer@aei.mpg.de.\n\n  SECTION(\"Check 2 points\") {\n    test_points_and_weights(2, DataVector{-1.0, 1.0}, DataVector{1.0, 1.0});\n  }\n  SECTION(\"Check 3 points\") {\n    test_points_and_weights(3, DataVector{-1.0, 0.0, 1.0},\n                            DataVector{1.0, 4.0, 1.0} / 3.0);\n  }\n  SECTION(\"Check 4 points\") {\n    test_points_and_weights(\n        4, DataVector{-1.0, -0.4472135954999579, 0.4472135954999579, 1.0},\n        DataVector{1.0, 5.0, 5.0, 1.0} / 6.0);\n  }\n  SECTION(\"Check 5 points\") {\n    test_points_and_weights(\n        5, DataVector{-1.0, -0.6546536707079773, 0.0, 0.6546536707079773, 1.0},\n        DataVector{9.0, 49.0, 64.0, 49.0, 9.0} / 90.0);\n  }\n  SECTION(\"Check 6 points\") {\n    test_points_and_weights(\n        6,\n        DataVector{-1.0, -0.7650553239294646, -0.2852315164806452,\n                   0.2852315164806452, 0.7650553239294646, 1.0},\n        DataVector{1.0 / 15.0, 0.378474956297847, 0.554858377035486,\n                   0.554858377035486, 0.378474956297847, 1.0 / 15.0});\n  }\n  SECTION(\"Check 7 points\") {\n    test_points_and_weights(\n        7,\n        DataVector{-1.0, -0.8302238962785669, -0.4688487934707142, 0.0,\n                   0.4688487934707142, 0.8302238962785669, 1.0},\n        DataVector{1.0 / 21.0, 0.276826047361566, 0.431745381209860,\n                   0.487619047619048, 0.431745381209862, 0.276826047361567,\n                   1.0 / 21.0});\n  }\n  SECTION(\"Check 8 points\") {\n    test_points_and_weights(\n        8,\n        DataVector{-1.0, -0.8717401485096066, -0.5917001814331421,\n                   -0.2092992179024791, 0.2092992179024791, 0.5917001814331421,\n                   0.8717401485096066, 1.0},\n        DataVector{1.0 / 28.0, 0.210704227143507, 0.341122692483504,\n                   0.412458794658705, 0.412458794658705, 0.341122692483504,\n                   0.210704227143507, 1.0 / 28.0});\n  }\n  SECTION(\"Check 9 points\") {\n    test_points_and_weights(\n        9,\n        DataVector{-1.0, -0.8997579954114600, -0.6771862795107377,\n                   -0.3631174638261783, 0.0, 0.3631174638261783,\n                   0.6771862795107377, 0.8997579954114600, 1.0},\n        DataVector{1.0 / 36.0, 0.165495361560806, 0.274538712500161,\n                   0.346428510973042, 0.371519274376423, 0.346428510973042,\n                   0.274538712500161, 0.165495361560806, 1.0 / 36.0});\n  }\n  SECTION(\"Check 10 points\") {\n    test_points_and_weights(\n        10,\n        DataVector{-1.0, -0.9195339081664589, -0.7387738651055048,\n                   -0.4779249498104444, -0.1652789576663869, 0.1652789576663869,\n                   0.4779249498104444, 0.7387738651055048, 0.9195339081664589,\n                   1.0},\n        DataVector{1.0 / 45.0, 0.133305990851069, 0.224889342063126,\n                   0.292042683679679, 0.327539761183898, 0.327539761183898,\n                   0.292042683679680, 0.224889342063126, 0.133305990851071,\n                   1.0 / 45.0});\n  }\n  if (Spectral::maximum_number_of_points<Spectral::Basis::Legendre> >= 20) {\n    SECTION(\"Check 20 points\") {\n      test_points_and_weights(\n          20,\n          DataVector{-1.0,\n                     -0.9807437048939139,\n                     -0.9359344988126655,\n                     -0.8668779780899503,\n                     -0.7753682609520557,\n                     -0.6637764022903113,\n                     -0.5349928640318858,\n                     -0.3923531837139090,\n                     -0.2395517059229869,\n                     -0.0805459372388217,\n                     0.08054593723882171,\n                     0.2395517059229869,\n                     0.3923531837139090,\n                     0.5349928640318858,\n                     0.6637764022903113,\n                     0.7753682609520557,\n                     0.8668779780899503,\n                     0.9359344988126655,\n                     0.9807437048939139,\n                     1.0},\n          DataVector{1.0 / 190.0,        0.0322371231888077, 0.0571818021276912,\n                     0.0806317640010999, 0.101991499698685,  0.120709227630445,\n                     0.136300482360142,  0.148361554072526,  0.156580102645661,\n                     0.160743286387015,  0.160743286386819,  0.156580102645406,\n                     0.148361554071531,  0.136300482358238,  0.120709227630445,\n                     0.101991499698478,  0.0806317639996452, 0.0571818021277683,\n                     0.0322371231896326, 1.0 / 190.0});\n    }\n  }\n}\n\nnamespace {\n\nvoid test_diff_matrix(const size_t num_points, const Matrix& expected_matrix) {\n  const auto& diff_matrix =\n      Spectral::differentiation_matrix<Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::GaussLobatto>(\n          num_points);\n  Approx diff_approx = approx;\n  diff_approx.margin(1.0e-13);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_matrix, diff_matrix, diff_approx);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.LegendreGaussLobatto.DiffMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  // Compare differentiation matrix values to matlab code accompanying the\n  // book \"Nodal Discontinuous Galerkin Methods\" by Hesthaven and Warburton\n  // available at http://www.nudg.org/.\n  //\n  // Command to generate differentation matrix with the Matlab code:\n  // DN = Dmatrix1D(N, JacobiGL(0,0,N), Vandermonde1D(N, JacobiGL(0,0,N)))\n\n  SECTION(\"Check 2 points\") {\n    test_diff_matrix(2, Matrix({{-0.5, 0.5}, {-0.5, 0.5}}));\n  }\n\n  SECTION(\"Check 3 points\") {\n    test_diff_matrix(\n        3, Matrix({{-1.5, 2., -0.5}, {-0.5, 0., 0.5}, {0.5, -2., 1.5}}));\n  }\n\n  SECTION(\"Check 4 points\") {\n    test_diff_matrix(\n        4,\n        Matrix({{-3., 4.04508497187474, -1.54508497187474, 0.5},\n                {-0.809016994374947, 0., 1.11803398874990, -0.309016994374947},\n                {0.309016994374947, -1.11803398874990, 0., 0.809016994374947},\n                {-0.5, 1.54508497187474, -4.04508497187474, 3.}}));\n  }\n\n  SECTION(\"Check 5 points\") {\n    test_diff_matrix(\n        5, Matrix({{-5., 6.75650248872424, -2.66666666666666, 1.41016417794243,\n                    -0.5},\n                   {-1.24099025303098, 0., 1.74574312188794, -0.763762615825973,\n                    0.259009746969017},\n                   {0.375, -1.33658457769545, 0., 1.33658457769545, -0.375},\n                   {-0.259009746969017, 0.763762615825973, -1.74574312188794,\n                    0., 1.24099025303098},\n                   {0.5, -1.41016417794243, 2.66666666666666, -6.75650248872424,\n                    5.}}));\n  }\n\n  SECTION(\"Check 6 points\") {\n    test_diff_matrix(\n        6, Matrix({{-7.5, 10.1414159363197, -4.03618727030535, 2.24468464817617,\n                    -1.34991331419049, 0.5},\n                   {-1.78636494833909, 0., 2.52342677742946, -1.15282815853593,\n                    0.653547507429800, -0.237781177984231},\n                   {0.484951047853569, -1.72125695283023, 0., 1.75296196636787,\n                    -0.786356672223241, 0.269700610832039},\n                   {-0.269700610832039, 0.786356672223241, -1.75296196636787,\n                    0., 1.72125695283023, -0.484951047853569},\n                   {0.237781177984231, -0.653547507429800, 1.15282815853593,\n                    -2.52342677742946, 0., 1.78636494833909},\n                   {-0.5, 1.34991331419049, -2.24468464817617, 4.03618727030535,\n                    -10.1414159363197, 7.5}}));\n  }\n\n  SECTION(\"Check 10 points\") {\n    test_diff_matrix(\n        10,\n        Matrix({{-22.5, 30.4381450292819, -12.1779467074298, 6.94378848513396,\n                 -4.59935476110314, 3.29464303374919, -2.45288417544269,\n                 1.82956393190325, -1.27595483609266, 0.5},\n                {-5.07406470297807, 1.90958360235527e-14, 7.18550286970582,\n                 -3.35166386274678, 2.07820799403642, -1.44494844875146,\n                 1.05915446364544, -0.783239293137909, 0.543753738235705,\n                 -0.212702758009189},\n                {1.20335199285221, -4.25929735496521, -1.95399252334028e-14,\n                 4.36867455701019, -2.10435017941316, 1.33491548387825,\n                 -0.936603213139449, 0.676797087196086, -0.464274958908157,\n                 0.180786585489250},\n                {-0.528369376820272, 1.52990263818160, -3.36412586829782, 0.,\n                 3.38731810120245, -1.64649408398706, 1.04618936550249,\n                 -0.721237312721603, 0.483462326333947, -0.186645789393736},\n                {0.312047255608411, -0.845813573406423, 1.44485031560166,\n                 -3.02021795819935, 0., 3.02518848775198, -1.46805550938999,\n                 0.916555180336434, -0.588082143045168, 0.223527944742453},\n                {-0.223527944742453, 0.588082143045168, -0.916555180336434,\n                 1.46805550938999, -3.02518848775198, 0., 3.02021795819935,\n                 -1.44485031560166, 0.845813573406422, -0.312047255608411},\n                {0.186645789393736, -0.483462326333947, 0.721237312721603,\n                 -1.04618936550249, 1.64649408398706, -3.38731810120245, 0.,\n                 3.36412586829782, -1.52990263818160, 0.528369376820272},\n                {-0.180786585489250, 0.464274958908157, -0.676797087196086,\n                 0.936603213139449, -1.33491548387825, 2.10435017941316,\n                 -4.36867455701019, 1.95399252334028e-14, 4.25929735496520,\n                 -1.20335199285221},\n                {0.212702758009189, -0.543753738235706, 0.783239293137910,\n                 -1.05915446364544, 1.44494844875146, -2.07820799403642,\n                 3.35166386274678, -7.18550286970582, -1.90958360235527e-14,\n                 5.07406470297807},\n                {-0.5, 1.27595483609266, -1.82956393190325, 2.45288417544269,\n                 -3.29464303374919, 4.59935476110314, -6.94378848513396,\n                 12.1779467074298, -30.4381450292819, 22.5}}));\n  }\n}\n\nnamespace {\n\nvoid test_modal_to_nodal_matrix(const size_t num_points,\n                                const Matrix& expected_matrix) {\n  const auto& matrix =\n      Spectral::modal_to_nodal_matrix<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::GaussLobatto>(\n          num_points);\n  CHECK_ITERABLE_APPROX(expected_matrix, matrix);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.LegendreGaussLobatto.ModalToNodal\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  SECTION(\"Check 2 points\") {\n    test_modal_to_nodal_matrix(2, Matrix({{1., -1.}, {1., 1.}}));\n  }\n  SECTION(\"Check 3 points\") {\n    test_modal_to_nodal_matrix(\n        3, Matrix({{1., -1., 1.}, {1., 0., -0.5}, {1., 1., 1.}}));\n  }\n  SECTION(\"Check 4 points\") {\n    test_modal_to_nodal_matrix(\n        4, Matrix({{1., -1., 1., -1.},\n                   {1., -0.4472135954999579, -0.2, 0.4472135954999580},\n                   {1., 0.4472135954999579, -0.2, -0.4472135954999580},\n                   {1., 1., 1., 1.}}));\n  }\n  SECTION(\"Check 5 points\") {\n    test_modal_to_nodal_matrix(\n        5, Matrix({{1., -1., 1., -1., 1.},\n                   {1., -0.6546536707079772, 0.1428571428571430,\n                    0.2805658588748472, -0.4285714285714287},\n                   {1., 0, -0.4999999999999999, 0, 0.3749999999999999},\n                   {1., 0.6546536707079772, 0.1428571428571430,\n                    -0.2805658588748472, -0.4285714285714287},\n                   {1., 1., 1., 1., 1.}}));\n  }\n  SECTION(\"Check 6 points\") {\n    test_modal_to_nodal_matrix(\n        6,\n        Matrix(\n            {{1., -1., 1., -1., 1., -1.},\n             {1., -0.7650553239294646, 0.3779644730092270, 0.02809732873313717,\n              -0.3210913738940153, 0.4196969341312872},\n             {1., -0.2852315164806452, -0.3779644730092271, 0.3698333106741464,\n              0.09886915167179271, -0.3466277250554177},\n             {1., 0.2852315164806452, -0.3779644730092271, -0.3698333106741464,\n              0.09886915167179271, 0.3466277250554177},\n             {1., 0.7650553239294646, 0.3779644730092270, -0.02809732873313717,\n              -0.3210913738940153, -0.4196969341312872},\n             {1., 1., 1., 1., 1., 1.}}));\n  }\n  SECTION(\"Check 10 points\") {\n    test_modal_to_nodal_matrix(\n        10,\n        Matrix(\n            {{1., -1., 1., -1., 1., -1., 1., -1., 1., -1.},\n             {1., -0.9195339081664589, 0.7683139124018223, -0.5644618855048774,\n              0.3320877919799225, -0.09808926498070079, -0.1113797504923236,\n              0.2742803620392504, -0.3754366431428354, 0.4082901563591628},\n             {1., -0.7387738651055048, 0.3186802356443899, 0.1001281943707717,\n              -0.3684613397656203, 0.4098747391204626, -0.2480909164487299,\n              -0.01093833236737723, 0.2322313407942435, -0.3143469900103700},\n             {1., -0.4779249498104444, -0.1573816135230263, 0.4439776327804282,\n              -0.2532927686189359, -0.1372832255111463, 0.3313642847298692,\n              -0.1764392879805111, -0.1318348656648675, 0.2758484689220680},\n             {1., -0.1652789576663869, -0.4590242992290690, 0.2366310679777983,\n              0.2758254859548065, -0.2713635222518218, -0.1476283247671612,\n              0.2779113224035852, 0.04305060850148148, -0.2604724104587964},\n             {1., 0.1652789576663869, -0.4590242992290690, -0.2366310679777983,\n              0.2758254859548065, 0.2713635222518218, -0.1476283247671612,\n              -0.2779113224035852, 0.04305060850148148, 0.2604724104587964},\n             {1., 0.4779249498104444, -0.1573816135230263, -0.4439776327804282,\n              -0.2532927686189359, 0.1372832255111463, 0.3313642847298692,\n              0.1764392879805111, -0.1318348656648675, -0.2758484689220680},\n             {1., 0.7387738651055048, 0.3186802356443899, -0.1001281943707717,\n              -0.3684613397656203, -0.4098747391204626, -0.2480909164487299,\n              0.01093833236737723, 0.2322313407942435, 0.3143469900103700},\n             {1., 0.9195339081664589, 0.7683139124018223, 0.5644618855048774,\n              0.3320877919799225, 0.09808926498070079, -0.1113797504923236,\n              -0.2742803620392504, -0.3754366431428354, -0.4082901563591628},\n             {1., 1., 1., 1., 1., 1., 1., 1., 1., 1.}}));\n  }\n}\n\nnamespace {\n\nvoid test_nodal_to_modal_matrix(const size_t num_points,\n                                const Matrix& expected_matrix) {\n  const auto& matrix =\n      Spectral::nodal_to_modal_matrix<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::GaussLobatto>(\n          num_points);\n  CHECK_ITERABLE_APPROX(expected_matrix, matrix);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.LegendreGaussLobatto.NodalToModal\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  SECTION(\"Check 2 points\") {\n    test_nodal_to_modal_matrix(2, Matrix({{0.5, 0.5}, {-0.5, 0.5}}));\n  }\n  SECTION(\"Check 3 points\") {\n    test_nodal_to_modal_matrix(3, Matrix({{1. / 6., 2. / 3., 1. / 6.},\n                                          {-1. / 2., 0., 1. / 2.},\n                                          {1. / 3., -2. / 3., 1. / 3.}}));\n  }\n  SECTION(\"Check 4 points\") {\n    test_nodal_to_modal_matrix(\n        4, Matrix({{0.08333333333333320, 0.4166666666666668, 0.4166666666666666,\n                    0.08333333333333337},\n                   {-0.25, -0.5590169943749476, 0.5590169943749476, 0.25},\n                   {0.4166666666666667, -0.4166666666666668,\n                    -0.4166666666666666, 0.4166666666666666},\n                   {-0.25, 0.5590169943749476, -0.5590169943749476, 0.25}}));\n  }\n  SECTION(\"Check 5 points\") {\n    test_nodal_to_modal_matrix(\n        5, Matrix({{0.05, 0.2722222222222222, 0.3555555555555557,\n                    0.2722222222222221, 0.05},\n                   {-0.15, -0.5346338310781814, 0., 0.5346338310781813, 0.15},\n                   {0.25, 0.1944444444444444, -0.8888888888888887,\n                    0.1944444444444443, 0.25},\n                   {-0.35, 0.5346338310781814, 0., -0.5346338310781813, 0.35},\n                   {0.2, -0.4666666666666666, 0.5333333333333330,\n                    -0.4666666666666665, 0.2}}));\n  }\n  SECTION(\"Check 6 points\") {\n    test_nodal_to_modal_matrix(\n        6,\n        Matrix(\n            {{0.03333333333333338, 0.1892374781489234, 0.2774291885177432,\n              0.2774291885177432, 0.1892374781489234, 0.03333333333333341},\n             {-0.1, -0.4343314204344590, -0.2373946444707316,\n              0.2373946444707316, 0.4343314204344590, 0.1},\n             {0.1666666666666666, 0.3576252185107653, -0.5242918851774320,\n              -0.5242918851774321, 0.3576252185107653, 0.1666666666666667},\n             {-0.2333333333333333, 0.03721947342526071, 0.7182178868701115,\n              -0.7182178868701115, -0.03721947342526071, 0.2333333333333333},\n             {0.2999999999999999, -0.5468626966596887, 0.2468626966596887,\n              0.2468626966596888, -0.5468626966596887, 0.2999999999999999},\n             {-0.1666666666666666, 0.3971119470091983, -0.4808232423993798,\n              0.4808232423993798, -0.3971119470091983, 0.1666666666666666}}));\n  }\n  SECTION(\"Check 10 points\") {\n    test_nodal_to_modal_matrix(\n        10,\n        Matrix(\n            {{0.01111111111111113, 0.06665299542553522, 0.1124446710315631,\n              0.1460213418398418, 0.1637698805919488, 0.1637698805919487,\n              0.1460213418398418, 0.1124446710315632, 0.06665299542553520,\n              0.01111111111111101},\n             {-0.03333333333333321, -0.1838690681239303, -0.2492135526855150,\n              -0.2093617274101802, -0.08120314548415815, 0.08120314548415815,\n              0.2093617274101802, 0.2492135526855150, 0.1838690681239303,\n              0.03333333333333320},\n             {0.05555555555555547, 0.2560521184434683, 0.1791694713064728,\n              -0.1149053719377589, -0.3758717733677376, -0.3758717733677376,\n              -0.1149053719377588, 0.1791694713064727, 0.2560521184434683,\n              0.05555555555555547},\n             {-0.07777777777777778, -0.2633615283071183, 0.07881217313904033,\n              0.4538114677983227, 0.2712712922294855, -0.2712712922294855,\n              -0.4538114677983228, -0.07881217313904021, 0.2633615283071183,\n              0.07777777777777772},\n             {0.1, 0.1992118147174238, -0.3728836272401474, -0.3328753495685899,\n              0.4065471620913135, 0.4065471620913135, -0.3328753495685899,\n              -0.3728836272401475, 0.1992118147174238, 0.1},\n             {-0.1222222222222223, -0.07191737663057941, 0.5069705322500303,\n              -0.2205090888136316, -0.4888528879981061, 0.4888528879981059,\n              0.2205090888136318, -0.5069705322500304, 0.07191737663057941,\n              0.1222222222222224},\n             {0.1444444444444446, -0.09650932200080796, -0.3626545193179547,\n              0.6290213474227139, -0.3143019505483959, -0.3143019505483958,\n              0.6290213474227141, -0.3626545193179548, -0.09650932200080796,\n              0.1444444444444446},\n             {-0.1666666666666666, 0.2742241157447441, -0.01844935777025255,\n              -0.3864585237627080, 0.6827025612777864, -0.6827025612777864,\n              0.3864585237627079, 0.01844935777025254, -0.2742241157447441,\n              0.1666666666666667},\n             {0.1888888888888889, -0.4254076065856193, 0.4439240042200661,\n              -0.3272619677562069, 0.1198566812328714, 0.1198566812328711,\n              -0.3272619677562070, 0.4439240042200662, -0.4254076065856192,\n              0.1888888888888887},\n             {-0.1, 0.2449238573168839, -0.3181197949333031, 0.3625178721881970,\n              -0.3839178200250077, 0.3839178200250077, -0.3625178721881971,\n              0.3181197949333031, -0.2449238573168839, 0.1}}));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_LinearFilterMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/LinearFilteringMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis basis, Quadrature quadrature>\nvoid test() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const DataVector xi = collocation_points<basis, quadrature>(n);\n    const Matrix m = linear_filter_matrix<basis, quadrature>(n);\n    for (size_t k = 0; k < n; ++k) {\n      CAPTURE(k);\n      const auto f = compute_basis_function_value<basis>(k, xi);\n      const auto filtered_f = apply_matrix(m, f);\n      DataVector f_expected{n, 0.0};\n      if (k < 2) {\n        f_expected = compute_basis_function_value<basis>(k, xi);\n      }\n      CHECK_ITERABLE_APPROX(filtered_f, f_expected);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.LinearFilterMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test<Basis::Legendre, Quadrature::Gauss>();\n  test<Basis::Legendre, Quadrature::GaussLobatto>();\n  test<Basis::Chebyshev, Quadrature::Gauss>();\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>();\n  test<Basis::Fourier, Quadrature::Equiangular>();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_LogicalCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <pup.h>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n\nnamespace {\n\nvoid test_spherical_logical_coords() {\n  for (size_t l = 2; l < 5; ++l) {\n    const size_t nth = l + 1;\n    const size_t nph = 2 * l + 1;\n    const Mesh<2> mesh_s2{\n        {nth, nph},\n        {Spectral::Basis::SphericalHarmonic,\n         Spectral::Basis::SphericalHarmonic},\n        {Spectral::Quadrature::Gauss, Spectral::Quadrature::Equiangular}};\n    const auto xi = logical_coordinates(mesh_s2);\n    const ylm::Spherepack ylm(l, l);\n    const auto xi_expected = ylm.theta_phi_points();\n    CHECK(get<0>(xi) == xi_expected[0]);\n    CHECK(get<1>(xi) == xi_expected[1]);\n  }\n}\n\nvoid test_radial_zernike_logical_coords() {\n  for (const auto& basis :\n       {Spectral::Basis::ZernikeB1, Spectral::Basis::ZernikeB2,\n        Spectral::Basis::ZernikeB3}) {\n    for (size_t n = 2; n < 5; ++n) {\n      const Mesh<1> mesh{n, basis, Spectral::Quadrature::GaussRadauUpper};\n      const auto xi = logical_coordinates(mesh);\n      CHECK(get<0>(xi)[n - 1] == approx(1.0));\n      if (n >= 4) {\n        // logical coordinates in [-1, 1] (with the mapping to [0, 1]\n        // internally taken care of)\n        CHECK(get<0>(xi)[0] < 0.0);\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.LogicalCoordinates\",\n                  \"[Domain][Unit]\") {\n  test_spherical_logical_coords();\n  test_radial_zernike_logical_coords();\n  using Affine2d =\n      domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::Affine,\n                                             domain::CoordinateMaps::Affine>;\n  using Affine3d =\n      domain::CoordinateMaps::ProductOf3Maps<domain::CoordinateMaps::Affine,\n                                             domain::CoordinateMaps::Affine,\n                                             domain::CoordinateMaps::Affine>;\n\n  const Mesh<1> mesh_1d{3, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> mesh_2d{\n      {{2, 3}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n  // [logical_coordinates_example]\n  const Mesh<3> mesh_3d{{{5, 3, 2}},\n                        Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n\n  const domain::CoordinateMaps::Affine x_map{-1.0, 1.0, -3.0, 7.0};\n  const domain::CoordinateMaps::Affine y_map{-1.0, 1.0, -13.0, 47.0};\n  const domain::CoordinateMaps::Affine z_map{-1.0, 1.0, -32.0, 74.0};\n\n  const auto map_3d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          Affine3d{x_map, y_map, z_map});\n\n  const auto x_3d = map_3d(logical_coordinates(mesh_3d));\n  // [logical_coordinates_example]\n\n  const auto map_1d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          domain::CoordinateMaps::Affine{x_map});\n  const auto map_2d =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Grid>(\n          Affine2d{x_map, y_map});\n  const auto x_1d = map_1d(logical_coordinates(mesh_1d));\n  const auto x_2d = map_2d(logical_coordinates(mesh_2d));\n\n  CHECK(x_1d[0][0] == -3.0);\n  CHECK(x_1d[0][1] == 2.0);\n  CHECK(x_1d[0][2] == 7.0);\n\n  CHECK(x_2d[0][0] == -3.0);\n  CHECK(x_2d[0][1] == 7.0);\n\n  CHECK(x_3d[0][0] == -3.0);\n  CHECK(x_3d[0][2] == 2.0);\n  CHECK(x_3d[0][4] == 7.0);\n\n  CHECK(x_2d[1][0] == -13.0);\n  CHECK(x_2d[1][2] == 17.0);\n  CHECK(x_2d[1][4] == 47.0);\n\n  CHECK(x_3d[1][0] == -13.0);\n  CHECK(x_3d[1][5] == 17.0);\n  CHECK(x_3d[1][10] == 47.0);\n\n  CHECK(x_3d[2][0] == -32.0);\n  CHECK(x_3d[2][15] == 74.0);\n\n  const Mesh<1> mesh_fourier{51, Spectral::Basis::Fourier,\n                             Spectral::Quadrature::Equiangular};\n  const Mesh<1> mesh_zernikeB2_equiangular{51, Spectral::Basis::ZernikeB2,\n                                           Spectral::Quadrature::Equiangular};\n  CHECK(logical_coordinates(mesh_fourier) ==\n        logical_coordinates(mesh_zernikeB2_equiangular));\n\n  const Mesh<3> mesh_spherical{\n      {3, 1, 1},\n      {Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n       Spectral::Basis::Cartoon},\n      {Spectral::Quadrature::GaussLobatto,\n       Spectral::Quadrature::SphericalSymmetry,\n       Spectral::Quadrature::SphericalSymmetry}};\n  using Affine = domain::CoordinateMaps::Affine;\n  using Identity = domain::CoordinateMaps::Identity<1>;\n  const Identity identity_cartoon_map;\n  const Affine affine_x_map(-1.0, 1.0, -1.0, 1.0);\n  const domain::CoordinateMap<\n      Frame::ElementLogical, Frame::Grid,\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Identity, Identity>>\n      map_spherical{{affine_x_map, identity_cartoon_map, identity_cartoon_map}};\n  const auto x_spherical = map_spherical(logical_coordinates(mesh_spherical));\n  CHECK(x_spherical[1][0] == 0.0);\n  CHECK(x_spherical[1][1] == 0.0);\n  CHECK(x_spherical[1][2] == 0.0);\n  CHECK(x_spherical[2][0] == 0.0);\n  CHECK(x_spherical[2][1] == 0.0);\n  CHECK(x_spherical[2][2] == 0.0);\n\n  const Mesh<3> mesh_axial{\n      {2, 3, 1},\n      {Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n       Spectral::Basis::Cartoon},\n      {Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n       Spectral::Quadrature::AxialSymmetry}};\n  const domain::CoordinateMap<\n      Frame::ElementLogical, Frame::Grid,\n      domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity>>\n      map_axial{{affine_x_map, affine_x_map, identity_cartoon_map}};\n  const auto x_axial = map_axial(logical_coordinates(mesh_axial));\n  CHECK(x_axial[2][0] == 0.0);\n  CHECK(x_axial[2][1] == 0.0);\n  CHECK(x_axial[2][2] == 0.0);\n  CHECK(x_axial[2][3] == 0.0);\n  CHECK(x_axial[2][4] == 0.0);\n  CHECK(x_axial[2][5] == 0.0);\n\n  const Mesh<1> mesh_cartoon_error{2, Spectral::Basis::Cartoon,\n                                   Spectral::Quadrature::AxialSymmetry};\n  CHECK_THROWS_WITH((logical_coordinates(mesh_cartoon_error)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Only 1 grid point is allowed in a Cartoon basis.\"));\n\n  TestHelpers::db::test_compute_tag<domain::Tags::LogicalCoordinates<1>>(\n      \"ElementLogicalCoordinates\");\n  TestHelpers::db::test_compute_tag<domain::Tags::LogicalCoordinates<2>>(\n      \"ElementLogicalCoordinates\");\n  TestHelpers::db::test_compute_tag<domain::Tags::LogicalCoordinates<3>>(\n      \"ElementLogicalCoordinates\");\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Mesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <numeric>\n#include <string>\n\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/IndexIterator.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_extents_basis_and_quadrature(\n    const Mesh<Dim>& mesh, const std::array<size_t, Dim>& extents,\n    const std::array<Spectral::Basis, Dim>& basis,\n    const std::array<Spectral::Quadrature, Dim>& quadrature) {\n  CAPTURE(Dim);\n  CAPTURE(extents);\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  CHECK(mesh.number_of_grid_points() ==\n        std::accumulate(extents.begin(), extents.end(), size_t{1},\n                        std::multiplies<size_t>()));\n  CHECK(mesh.extents() == Index<Dim>{extents});\n  CHECK(mesh.basis() == basis);\n  CHECK(mesh.quadrature() == quadrature);\n  for (size_t d = 0; d < Dim; d++) {\n    CHECK(mesh.extents(d) == gsl::at(extents, d));\n    CHECK(mesh.basis(d) == gsl::at(basis, d));\n    CHECK(mesh.quadrature(d) == gsl::at(quadrature, d));\n    CHECK(gsl::at(mesh.slices(), d) == mesh.slice_through(d));\n  }\n  CHECK(get_output(mesh) ==\n        std::string{MakeString{} << '[' << get_output(extents) << ','\n                                 << get_output(mesh.basis()) << ','\n                                 << get_output(mesh.quadrature()) << ']'});\n  for (IndexIterator<Dim> index_it(mesh.extents()); index_it; ++index_it) {\n    CAPTURE(*index_it);\n    Index<Dim> index{};\n    for (size_t d = 0; d < Dim; ++d) {\n      index[d] = (*index_it)[d];\n    }\n    CHECK(index_it.collapsed_index() == mesh.storage_index(index));\n  }\n}\n\nvoid test_uniform_lgl_mesh() {\n  INFO(\"Uniform LGL mesh\");\n  CHECK(is_isotropic(Mesh<0>{}));\n  const Mesh<1> mesh1d_lgl{3, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  test_extents_basis_and_quadrature(mesh1d_lgl, {{3}},\n                                    {{Spectral::Basis::Legendre}},\n                                    {{Spectral::Quadrature::GaussLobatto}});\n  CHECK(is_isotropic(mesh1d_lgl));\n  const Mesh<2> mesh2d_lgl{3, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  test_extents_basis_and_quadrature(\n      mesh2d_lgl, {{3, 3}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::GaussLobatto}});\n  CHECK(is_isotropic(mesh2d_lgl));\n  const Mesh<3> mesh3d_lgl{3, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  test_extents_basis_and_quadrature(\n      mesh3d_lgl, {{3, 3, 3}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n        Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::GaussLobatto}});\n  CHECK(is_isotropic(mesh3d_lgl));\n}\n\nvoid test_explicit_choices_per_dimension() {\n  INFO(\"Explicit choices per dimension\");\n  CHECK(Mesh<0>{}.slice_through() == Mesh<0>{});\n  const Mesh<1> mesh1d{{{2}},\n                       {{Spectral::Basis::Legendre}},\n                       {{Spectral::Quadrature::GaussLobatto}}};\n  test_extents_basis_and_quadrature(mesh1d, {{2}},\n                                    {{Spectral::Basis::Legendre}},\n                                    {{Spectral::Quadrature::GaussLobatto}});\n  CHECK(mesh1d.slice_away(0) == Mesh<0>{});\n  CHECK(mesh1d.slice_through() == Mesh<0>{});\n  CHECK(mesh1d.slice_through(0) == mesh1d);\n\n  const Mesh<2> mesh2d{\n      {{2, 3}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}}};\n  test_extents_basis_and_quadrature(\n      mesh2d, {{2, 3}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}});\n  CHECK(mesh2d.slice_away(0) == Mesh<1>{3, Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto});\n  CHECK(mesh2d.slice_away(1) ==\n        Mesh<1>{2, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss});\n  CHECK(mesh2d.slice_through() == Mesh<0>{});\n  CHECK(mesh2d.slice_through(0) == mesh2d.slice_away(1));\n  CHECK(mesh2d.slice_through(1) == mesh2d.slice_away(0));\n  CHECK(mesh2d.slice_through(0, 1) == mesh2d);\n  CHECK(mesh2d.slice_through(1, 0) ==\n        Mesh<2>{{{3, 2}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::GaussLobatto,\n                  Spectral::Quadrature::Gauss}}});\n\n  const Mesh<3> mesh3d{\n      {{2, 3, 4}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n        Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n        Spectral::Quadrature::GaussLobatto}}};\n  test_extents_basis_and_quadrature(\n      mesh3d, {{2, 3, 4}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n        Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n        Spectral::Quadrature::GaussLobatto}});\n  CHECK(mesh3d.slice_away(0) ==\n        Mesh<2>{{{3, 4}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::Gauss,\n                  Spectral::Quadrature::GaussLobatto}}});\n  CHECK(mesh3d.slice_away(1) == Mesh<2>{{{2, 4}},\n                                        Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto});\n  CHECK(mesh3d.slice_away(2) ==\n        Mesh<2>{{{2, 3}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::GaussLobatto,\n                  Spectral::Quadrature::Gauss}}});\n  CHECK(mesh3d.slice_through() == Mesh<0>{});\n  CHECK(mesh3d.slice_through(0) == Mesh<1>{2, Spectral::Basis::Legendre,\n                                           Spectral::Quadrature::GaussLobatto});\n  CHECK(mesh3d.slice_through(1) ==\n        Mesh<1>{3, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss});\n  CHECK(mesh3d.slice_through(2) == Mesh<1>{4, Spectral::Basis::Legendre,\n                                           Spectral::Quadrature::GaussLobatto});\n  CHECK(mesh3d.slice_through(0, 1) == mesh3d.slice_away(2));\n  CHECK(mesh3d.slice_through(0, 2) == mesh3d.slice_away(1));\n  CHECK(mesh3d.slice_through(1, 2) == mesh3d.slice_away(0));\n  CHECK(mesh3d.slice_through(0, 1, 2) == mesh3d);\n  CHECK(mesh3d.slice_through(2, 0, 1) ==\n        Mesh<3>{{{4, 2, 3}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n                  Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::GaussLobatto,\n                  Spectral::Quadrature::GaussLobatto,\n                  Spectral::Quadrature::Gauss}}});\n}\n\nvoid test_equality() {\n  INFO(\"Equality\");\n  // For a zero-d Mesh any isotropic arguments are ignored...\n  CHECK(Mesh<0>{4, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<0>{2, Spectral::Basis::Chebyshev, Spectral::Quadrature::Gauss});\n  CHECK(Mesh<0>{4, Spectral::Basis::Chebyshev,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<0>{{{}}, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss});\n  CHECK(Mesh<0>{4, Spectral::Basis::Chebyshev,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<0>{{{}}, {{}}, {{}}});\n  CHECK(Mesh<0>{{{}},\n                Spectral::Basis::Chebyshev,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<0>{{{}}, {{}}, {{}}});\n  CHECK(Mesh<0>{{{}},\n                Spectral::Basis::Chebyshev,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<0>{{{}}, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss});\n  CHECK(Mesh<1>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<1>{{{3}},\n                {{Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::GaussLobatto}}});\n  CHECK(Mesh<1>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} !=\n        Mesh<1>{{{2}},\n                Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto});\n  CHECK(Mesh<1>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} !=\n        Mesh<1>{{{3}},\n                {{Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::Gauss}}});\n  CHECK(Mesh<2>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<2>{{{3, 3}},\n                Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto});\n  CHECK(Mesh<2>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<2>{{{3, 3}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::GaussLobatto,\n                  Spectral::Quadrature::GaussLobatto}}});\n  CHECK(Mesh<2>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} !=\n        Mesh<2>{{{3, 2}},\n                Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto});\n  CHECK(Mesh<2>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} !=\n        Mesh<2>{{{3, 3}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::Gauss,\n                  Spectral::Quadrature::GaussLobatto}}});\n  CHECK(Mesh<3>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<3>{{{3, 3, 3}},\n                Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto});\n  CHECK(Mesh<3>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} ==\n        Mesh<3>{{{3, 3, 3}},\n                {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n                  Spectral::Basis::Legendre}},\n                {{Spectral::Quadrature::GaussLobatto,\n                  Spectral::Quadrature::GaussLobatto,\n                  Spectral::Quadrature::GaussLobatto}}});\n  CHECK(Mesh<3>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto} !=\n        Mesh<3>{{{3, 2, 3}},\n                Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto});\n  CHECK(\n      Mesh<3>{3, Spectral::Basis::Legendre,\n              Spectral::Quadrature::GaussLobatto} !=\n      Mesh<3>{{{3, 3, 3}},\n              {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n                Spectral::Basis::Legendre}},\n              {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n                Spectral::Quadrature::GaussLobatto}}});\n}\n\nvoid test_serialization() {\n  INFO(\"Serialization\");\n  test_serialization(Mesh<1>{3, Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto});\n  test_serialization(Mesh<2>{\n      {{3, 2}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}}});\n  test_serialization(\n      Mesh<3>{{{3, 2, 4}},\n              {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n                Spectral::Basis::Legendre}},\n              {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::Gauss,\n                Spectral::Quadrature::GaussLobatto}}});\n  // Mesh should always be 6 bytes.\n  constexpr size_t expected_1d_mesh = 2;\n  constexpr size_t expected_2d_mesh = 4;\n  constexpr size_t expected_3d_mesh = 6;\n  static_assert(sizeof(Mesh<1>) == expected_1d_mesh);\n  static_assert(sizeof(Mesh<2>) == expected_2d_mesh);\n  static_assert(sizeof(Mesh<3>) == expected_3d_mesh);\n  CHECK(size_of_object_in_bytes(Mesh<1>{3, Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto}) ==\n        expected_1d_mesh);\n  CHECK(size_of_object_in_bytes(Mesh<2>{3, Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto}) ==\n        expected_2d_mesh);\n  CHECK(size_of_object_in_bytes(Mesh<3>{3, Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto}) ==\n        expected_3d_mesh);\n}\n\ntemplate <size_t Dim>\nvoid test_option_parsing() {\n  INFO(\"Option Parsing creation\");\n\n  const std::array<Spectral::Basis, 3> bases{Spectral::Basis::Chebyshev,\n                                             Spectral::Basis::Legendre,\n                                             Spectral::Basis::FiniteDifference};\n  const std::array<Spectral::Quadrature, 4> quadratures{\n      Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto,\n      Spectral::Quadrature::CellCentered, Spectral::Quadrature::FaceCentered};\n\n  for (size_t extent = 0; extent < 12; extent++) {  // extents\n    for (const auto basis : bases) {                // bases\n      for (const auto quadrature : quadratures) {   // quadratures\n        std::stringstream creation_string;\n        creation_string << \"Extents: \" << extent << \"\\nBasis: \" << basis\n                        << \"\\nQuadrature: \" << quadrature;\n        const auto mesh =\n            TestHelpers::test_creation<Mesh<Dim>>(creation_string.str());\n        CHECK(mesh == Mesh<Dim>{extent, basis, quadrature});\n      }\n    }\n  }\n}\n\nvoid test_is_isotropic() {\n  // Test non-isotropic meshes.\n  CHECK_FALSE(is_isotropic(\n      Mesh<2>{{{3, 2}},\n              {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n              {{Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss}}}));\n  CHECK_FALSE(is_isotropic(\n      Mesh<2>{{{3, 3}},\n              {{Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}},\n              {{Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss}}}));\n  CHECK_FALSE(is_isotropic(Mesh<2>{\n      {{3, 3}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}}}));\n\n  CHECK_FALSE(is_isotropic(\n      Mesh<3>{{{3, 2}},\n              {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n              {{Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss}}}));\n  CHECK_FALSE(is_isotropic(\n      Mesh<3>{{{3, 3}},\n              {{Spectral::Basis::Legendre, Spectral::Basis::Chebyshev}},\n              {{Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss}}}));\n  CHECK_FALSE(is_isotropic(Mesh<3>{\n      {{3, 3}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}}}));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.Mesh\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_uniform_lgl_mesh();\n  test_explicit_choices_per_dimension();\n  test_equality();\n  test_serialization();\n  test_option_parsing<1>();\n  test_option_parsing<2>();\n  test_option_parsing<3>();\n  test_is_isotropic();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      (Mesh<1>{2, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto}\n           .slice_through(1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to slice through non-existing dimension\"));\n  CHECK_THROWS_WITH(\n      (Mesh<1>{2, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto}\n           .slice_away(1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Tried to slice away non-existing dimension\"));\n  CHECK_THROWS_WITH(\n      (Mesh<3>{2, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto}\n           .slice_through(2, 1, 1)),\n      Catch::Matchers::ContainsSubstring(\n          \"Dimensions to slice through contain duplicates\"));\n#endif\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<Mesh<3>>(\"Extents: 5\\n\"\n                                           \"Basis: invalidBasis\\n\"\n                                           \"Quadrature: Gauss\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"invalidBasis\\\" to Spectral::Basis.\"));\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<Mesh<3>>(\"Extents: 5\\n\"\n                                           \"Basis: Chebyshev\\n\"\n                                           \"Quadrature: invalidQuadrature\")),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"invalidQuadrature\\\" to Spectral::Quadrature.\"));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_ModalToNodalMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Zernike.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis basis, Quadrature quadrature>\nvoid test() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const DataVector xi = collocation_points<basis, quadrature>(n);\n    const Matrix m = modal_to_nodal_matrix<basis, quadrature>(n);\n    for (size_t k = 0; k < n; ++k) {\n      CAPTURE(k);\n      const auto f_expected = compute_basis_function_value<basis>(k, xi);\n      DataVector f_k{n, 0.0};\n      f_k[k] = 1.0;\n      const auto f = apply_matrix(m, f_k);\n      CHECK_ITERABLE_APPROX(f, f_expected);\n    }\n  }\n}\n\ntemplate <Basis basis, Quadrature quadrature>\nvoid test_two_indexed() {\n  static_assert(basis == Basis::ZernikeB1 or basis == Basis::ZernikeB2 or\n                basis == Basis::ZernikeB3);\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const DataVector xi = collocation_points<basis, quadrature>(n);\n    for (size_t N = 0; N < 2 * n - 1; ++N) {\n      CAPTURE(N);\n      for (size_t m = 0; m <= N; ++m) {\n        CAPTURE(m);\n        const Matrix modal_to_nodal =\n            modal_to_nodal_matrix<basis, quadrature>(n, m, N);\n        // The spectal space is only odd or even modes, based on m parity\n        // Note the integer division\n        const size_t spectral_size = (N - m) / 2 + 1;\n        for (size_t k = m; k <= N; k += 2) {\n          CAPTURE(k);\n          const auto f_expected = compute_basis_function_value<basis>(k, m, xi);\n          DataVector f_k{spectral_size, 0.0};\n          // Index in this compressed space (of specific parity) must be mapped\n          const size_t index = (k - m) / 2;\n          f_k[index] = 1.0;\n          const auto f = apply_matrix(modal_to_nodal, f_k);\n          CHECK_ITERABLE_APPROX(f, f_expected);\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 30]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.ModalToNodalMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test<Basis::Legendre, Quadrature::Gauss>();\n  test<Basis::Legendre, Quadrature::GaussLobatto>();\n  test<Basis::Chebyshev, Quadrature::Gauss>();\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>();\n  test<Basis::Fourier, Quadrature::Equiangular>();\n  test_two_indexed<Basis::ZernikeB1, Quadrature::GaussRadauUpper>();\n  test_two_indexed<Basis::ZernikeB2, Quadrature::GaussRadauUpper>();\n  test_two_indexed<Basis::ZernikeB3, Quadrature::GaussRadauUpper>();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_NodalToModalMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n\nnamespace Spectral {\nnamespace {\ntemplate <Basis basis, Quadrature quadrature>\nvoid test() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const DataVector xi = collocation_points<basis, quadrature>(n);\n    const Matrix m = nodal_to_modal_matrix<basis, quadrature>(n);\n    for (size_t k = 0; k < n; ++k) {\n      CAPTURE(k);\n      const auto f = compute_basis_function_value<basis>(k, xi);\n      const auto f_k = apply_matrix(m, f);\n      DataVector f_k_expected{n, 0.0};\n      f_k_expected[k] = 1.0;\n      CHECK_ITERABLE_APPROX(f_k, f_k_expected);\n    }\n  }\n}\n\ntemplate <Basis basis, Quadrature quadrature>\nvoid test_two_indexed() {\n  static_assert(basis == Basis::ZernikeB1 or basis == Basis::ZernikeB2 or\n                basis == Basis::ZernikeB3);\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  const Approx custom_approx = Approx::custom().epsilon(1.0e-13).scale(1.0);\n  for (size_t n = minimum_number_of_points<basis, quadrature>;\n       n <= maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const DataVector xi = collocation_points<basis, quadrature>(n);\n    for (size_t N = 0; N < 2 * n - 1; ++N) {\n      CAPTURE(N);\n      for (size_t m = 0; m <= N; ++m) {\n        CAPTURE(m);\n        const Matrix nodal_to_modal =\n            nodal_to_modal_matrix<basis, quadrature>(n, m, N);\n        // The spectal space is only odd or even modes, based on m parity\n        // Note the integer division\n        const size_t spectral_size = (N - m) / 2 + 1;\n        for (size_t k = m; k <= N; k += 2) {\n          CAPTURE(k);\n          const auto f = compute_basis_function_value<basis>(k, m, xi);\n          const auto f_k = apply_matrix(nodal_to_modal, f);\n          DataVector f_k_expected{spectral_size, 0.0};\n          // Index in this compressed space (of specific parity) must be\n          // mapped\n          const size_t index = (k - m) / 2;\n          f_k_expected[index] = 1.0;\n          CHECK_ITERABLE_CUSTOM_APPROX(f_k, f_k_expected, custom_approx);\n        }\n      }\n    }\n  }\n}\n\n}  // namespace\n\n// [[TimeOut, 30]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.NodalToModalMatrix\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test<Basis::Legendre, Quadrature::Gauss>();\n  test<Basis::Legendre, Quadrature::GaussLobatto>();\n  test<Basis::Chebyshev, Quadrature::Gauss>();\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>();\n  test<Basis::Fourier, Quadrature::Equiangular>();\n  test_two_indexed<Basis::ZernikeB1, Quadrature::GaussRadauUpper>();\n  test_two_indexed<Basis::ZernikeB2, Quadrature::GaussRadauUpper>();\n  test_two_indexed<Basis::ZernikeB3, Quadrature::GaussRadauUpper>();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Parity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Spectral.Parity\", \"[NumericalAlgorithms][Unit]\") {\n  CHECK(get_output(Spectral::Parity::Uninitialized) == \"Uninitialized\");\n  CHECK(get_output(Spectral::Parity::Even) == \"Even\");\n  CHECK(get_output(Spectral::Parity::Odd) == \"Odd\");\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_ParityFromSymmetry.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/System.hpp\"\n#include \"NumericalAlgorithms/Spectral/ParityFromSymmetry.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Spectral {\nnamespace {\n\nvoid test_basic_functionality() {\n  const Scalar<double> scalar{1};\n  const auto [scalar_list, scalar_even, scalar_odd] =\n      compute_parity_list<Scalar<double>>();\n  const std::array<size_t, scalar.size() + 1> scalar_expected_list{1, 0};\n  CHECK(scalar_list == scalar_expected_list);\n  CHECK(scalar_even == 1);\n  CHECK(scalar_odd == 0);\n\n  const tnsr::a<double, 3> tensor_a{1};\n  const auto [tensor_a_list, a_even, a_odd] =\n      compute_parity_list<tnsr::a<double, 3>>();\n  const std::array<size_t, tensor_a.size() + 1> tensor_a_expected_list{1, 1, 2,\n                                                                       0, 0};\n  CHECK(tensor_a_list == tensor_a_expected_list);\n  CHECK(a_even == 3);\n  CHECK(a_odd == 1);\n\n  const tnsr::aI<double, 2> tensor_ai{1};\n  const auto [tensor_ai_list, ai_even, ai_odd] =\n      compute_parity_list<tnsr::aI<double, 2>>();\n  const std::array<size_t, tensor_ai.size() + 1> tensor_ai_expected_list{\n      0, 1, 1, 1, 1, 1, 1};\n  CHECK(tensor_ai_list == tensor_ai_expected_list);\n  CHECK(ai_even == 3);\n  CHECK(ai_odd == 3);\n\n  // Test edge cases\n  using empty_tags = tmpl::list<>;\n  const auto [empty_list, empty_even, empty_odd] =\n      compute_parity_list<empty_tags>();\n  const std::array<size_t, 1> empty_expected{0};\n  CHECK(empty_list == empty_expected);\n  CHECK(empty_even == 0);\n  CHECK(empty_odd == 0);\n\n  using single_tag = tmpl::list<::Tags::TempScalar<0>>;\n  const auto [single_list, single_even, single_odd] =\n      compute_parity_list<single_tag>();\n  const std::array<size_t, 2> single_expected{1, 0};\n  CHECK(single_list == single_expected);\n  CHECK(single_even == 1);\n  CHECK(single_odd == 0);\n}\n\nvoid test_expected_properties() {\n  const tnsr::ia<double, 2> tensor_ia{1};\n  const auto [ia_list, ia_even, ia_odd] =\n      compute_parity_list<tnsr::ia<double, 2>>();\n\n  // Total components = even_count + odd_count\n  CHECK(ia_even + ia_odd == tensor_ia.size());\n\n  // Sum of run lengths equals total components\n  size_t total_from_runs = 0;\n  for (const auto& count : ia_list) {\n    total_from_runs += count;\n  }\n  CHECK(total_from_runs == tensor_ia.size());\n\n  // Non-zero entries should be contiguous (no gaps)\n  bool first_index = true;\n  bool found_zero = false;\n  for (const auto& count : ia_list) {\n    if (first_index) {\n      first_index = false;\n    } else {\n      if (count == 0) {\n        found_zero = true;\n      } else if (found_zero) {\n        FAIL(\"Found non-zero entry after zero in run-length array\");\n      }\n    }\n  }\n}\n\ntemplate <typename VarsTags>\nvoid check_vars_scalar_constexpr(Variables<VarsTags> /*meta*/) {\n  constexpr auto res = compute_parity_list<VarsTags>();\n  static_assert(std::get<0>(res) == std::array<size_t, 2>{1, 0});\n  static_assert(std::get<1>(res) == 1 and std::get<2>(res) == 0);\n}\n\ntemplate <typename DataType, typename SymmList, typename IndexList>\nvoid check_scalar_constexpr(Tensor<DataType, SymmList, IndexList> /*meta*/) {\n  constexpr auto res =\n      compute_parity_list<Tensor<DataType, SymmList, IndexList>>();\n  static_assert(std::get<0>(res) == std::array<size_t, 2>{1, 0});\n  static_assert(std::get<1>(res) == 1 and std::get<2>(res) == 0);\n}\n\nvoid test_constexpr() {\n  // This test is trivial because it works, but it exists because when the\n  // compute_party_list is used in practice, the object's value is not known\n  // at compiletime, so it cannot be passed if we want the\n  // evaluation to be done at compiletime--it has to be purely template\n  // evaluation. As all the tests in this file are done with objects that can be\n  // fully determined at compiletime, this passing as a parameter to a function\n  // makes it actually test it's constexpr-ness\n  using test_tags = tmpl::list<::Tags::TempScalar<0>>;\n  const Variables<test_tags> scalar_vars(1);\n  check_vars_scalar_constexpr(scalar_vars);\n\n  const Scalar<DataVector> scalar(1_st);\n  check_scalar_constexpr(scalar);\n}\n\nvoid test_gh_system() {\n  using gh_tags = gh::System<3>::gradients_tags;\n  const auto [gh_list, gh_even, gh_odd] = compute_parity_list<gh_tags>();\n\n  const std::array<size_t,\n                   Variables<gh_tags>::number_of_independent_components + 1>\n      expected_list{1, 1, 3, 2, 4, 1, 3, 2, 3, 1, 3, 3, 2, 1, 2, 1, 3,\n                    2, 1, 3, 2, 1, 2, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n  CHECK(gh_list == expected_list);\n  CHECK(gh_even == 31);\n  CHECK(gh_odd == 19);\n  CHECK(gh_even + gh_odd ==\n        Variables<gh_tags>::number_of_independent_components);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.ParityFromSymmetry\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_basic_functionality();\n  test_expected_properties();\n  test_constexpr();\n  test_gh_system();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Projection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <initializer_list>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Helpers/NumericalAlgorithms/Spectral/FourierTestFunctions.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/ApplyMassMatrix.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace Spectral {\nnamespace {\nconstexpr auto quadratures = {Spectral::Quadrature::Gauss,\n                              Spectral::Quadrature::GaussLobatto};\n\nDataVector apply_matrix(const Matrix& m, const DataVector& v) {\n  ASSERT(m.columns() == v.size(), \"Bad apply_matrix\");\n  DataVector result(m.rows(), 0.);\n  for (size_t i = 0; i < m.rows(); ++i) {\n    for (size_t j = 0; j < m.columns(); ++j) {\n      result[i] += m(i, j) * v[j];\n    }\n  }\n  return result;\n}\n\nvoid test_mortar_size() {\n  CHECK(get_output(Spectral::SegmentSize::Full) == \"Full\");\n  CHECK(get_output(Spectral::SegmentSize::UpperHalf) == \"UpperHalf\");\n  CHECK(get_output(Spectral::SegmentSize::LowerHalf) == \"LowerHalf\");\n}\n\nvoid test_needs_projection() {\n  INFO(\"Needs projection\");\n  CHECK_FALSE(needs_projection<0>({}, {}, {}));\n  CHECK_FALSE(\n      needs_projection<1>({3, Basis::Legendre, Quadrature::GaussLobatto},\n                          {3, Basis::Legendre, Quadrature::GaussLobatto},\n                          make_array<1>(SegmentSize::Full)));\n  CHECK_FALSE(\n      needs_projection<2>({3, Basis::Legendre, Quadrature::GaussLobatto},\n                          {3, Basis::Legendre, Quadrature::GaussLobatto},\n                          make_array<2>(SegmentSize::Full)));\n  CHECK_FALSE(\n      needs_projection<3>({3, Basis::Legendre, Quadrature::GaussLobatto},\n                          {3, Basis::Legendre, Quadrature::GaussLobatto},\n                          make_array<3>(SegmentSize::Full)));\n  CHECK(needs_projection<1>({3, Basis::Legendre, Quadrature::GaussLobatto},\n                            {4, Basis::Legendre, Quadrature::GaussLobatto},\n                            make_array<1>(SegmentSize::Full)));\n  CHECK(needs_projection<1>({3, Basis::Legendre, Quadrature::GaussLobatto},\n                            {3, Basis::Legendre, Quadrature::Gauss},\n                            make_array<1>(SegmentSize::Full)));\n  CHECK(needs_projection<1>({3, Basis::Legendre, Quadrature::GaussLobatto},\n                            {3, Basis::Legendre, Quadrature::GaussLobatto},\n                            {{SegmentSize::LowerHalf}}));\n  CHECK(needs_projection<2>({3, Basis::Legendre, Quadrature::GaussLobatto},\n                            {3, Basis::Legendre, Quadrature::GaussLobatto},\n                            {{SegmentSize::Full, SegmentSize::LowerHalf}}));\n  CHECK(needs_projection<3>(\n      {3, Basis::Legendre, Quadrature::GaussLobatto},\n      {3, Basis::Legendre, Quadrature::GaussLobatto},\n      {{SegmentSize::Full, SegmentSize::Full, SegmentSize::UpperHalf}}));\n}\n\nvoid test_p_mortar_to_element() {\n  INFO(\"p - mortar to element\");\n  for (const auto& quadrature_dest : quadratures) {\n    for (size_t num_points_dest = 2;\n         num_points_dest <=\n         Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n         ++num_points_dest) {\n      const Mesh<1> mesh_dest(num_points_dest, Spectral::Basis::Legendre,\n                              quadrature_dest);\n      const auto& points_dest = Spectral::collocation_points(mesh_dest);\n      CAPTURE(mesh_dest);\n      for (const auto& quadrature_source : quadratures) {\n        for (size_t num_points_source = 2;\n             num_points_source <=\n             Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n             ++num_points_source) {\n          const Mesh<1> mesh_source(\n              num_points_source, Spectral::Basis::Legendre, quadrature_source);\n          CAPTURE(mesh_source);\n          const auto& points_source = Spectral::collocation_points(mesh_source);\n          const auto& projection = projection_matrix_child_to_parent(\n              mesh_source, mesh_dest, Spectral::SegmentSize::Full);\n          if (num_points_source <= num_points_dest) {\n            const auto& parent_to_child_projection =\n                projection_matrix_parent_to_child(mesh_source, mesh_dest,\n                                                  Spectral::SegmentSize::Full);\n            CHECK(projection == parent_to_child_projection);\n          }\n          for (size_t test_order = 0; test_order < num_points_source;\n               ++test_order) {\n            CAPTURE(test_order);\n            const DataVector source_data = pow(points_source, test_order);\n            const DataVector projected_data =\n                apply_matrix(projection, source_data);\n            if (num_points_source > num_points_dest) {\n              // Projection matrices can be defined as the matrices which\n              // make the error in the destination space orthogonal to the\n              // destination space.  We interpolate back to the higher\n              // dimensional source space to check.\n              const DataVector interpolated_projected_data = apply_matrix(\n                  Spectral::interpolation_matrix(mesh_dest, points_source),\n                  projected_data);\n              const DataVector error =\n                  interpolated_projected_data - source_data;\n\n              for (size_t orthogonality_test_order = 0;\n                   orthogonality_test_order < num_points_dest;\n                   ++orthogonality_test_order) {\n                // This integral might not be evaluated exactly for the\n                // highest order polynomials, but it will correctly\n                // determine orthogonality.\n                CHECK(definite_integral(\n                          error * pow(points_source, orthogonality_test_order),\n                          mesh_source) == approx(0.));\n              }\n            } else {\n              // The function can be represented exactly in both spaces.\n              auto local_approx = Approx::custom().scale(1.0).epsilon(1.0e-13);\n              CHECK_ITERABLE_CUSTOM_APPROX(\n                  projected_data, pow(points_dest, test_order), local_approx);\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_p_element_to_mortar() {\n  INFO(\"p - element to mortar\");\n  for (const auto& quadrature_dest : quadratures) {\n    for (size_t num_points_dest = 2;\n         num_points_dest <=\n         Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n         ++num_points_dest) {\n      const Mesh<1> mesh_dest(num_points_dest, Spectral::Basis::Legendre,\n                              quadrature_dest);\n      CAPTURE(mesh_dest);\n      const auto& points_dest = Spectral::collocation_points(mesh_dest);\n      for (const auto& quadrature_source : quadratures) {\n        for (size_t num_points_source = 2; num_points_source <= num_points_dest;\n             ++num_points_source) {\n          const Mesh<1> mesh_source(\n              num_points_source, Spectral::Basis::Legendre, quadrature_source);\n          CAPTURE(mesh_source);\n          const auto& points_source = Spectral::collocation_points(mesh_source);\n          const auto& projection = projection_matrix_parent_to_child(\n              mesh_source, mesh_dest, Spectral::SegmentSize::Full);\n          for (size_t test_order = 0; test_order < num_points_source;\n               ++test_order) {\n            CAPTURE(test_order);\n            const DataVector source_data = pow(points_source, test_order);\n            const DataVector projected_data =\n                apply_matrix(projection, source_data);\n            // The function is contained in the destination space, so\n            // projection should not alter it.\n            CHECK_ITERABLE_APPROX(projected_data, pow(points_dest, test_order));\n          }\n        }\n      }\n    }\n  }\n}\n\nDataVector to_upper_half(const DataVector& p) { return 0.5 * (p + 1.); }\n\nDataVector to_lower_half(const DataVector& p) { return 0.5 * (p - 1.); }\n\n// `to_element_self` (`to_element_other`) is the function mapping the\n// [-1,1] interval to the half that we are (are not) interpolating\n// from.\ntemplate <typename F1, typename F2>\nvoid check_mortar_to_element_projection(const Spectral::SegmentSize mortar_size,\n                                        const Mesh<1>& mesh_element,\n                                        const Mesh<1>& mesh_self_mortar,\n                                        F1&& to_element_self,\n                                        F2&& to_element_other) {\n  // Notation for variables in this function:\n  // _self indicates the half of the interval we projected from.\n  // _other indicates the half of the interval we did not project from.\n  // _element indicates the coordinate system on the large interval.\n  // _mortar indicates the coordinate system on one of the small intervals.\n\n  const auto& projection = projection_matrix_child_to_parent(\n      mesh_self_mortar, mesh_element, mortar_size);\n\n  const size_t num_points_self_mortar = mesh_self_mortar.extents(0);\n  const size_t num_points_element = mesh_element.extents(0);\n  const auto& points_self_mortar =\n      Spectral::collocation_points(mesh_self_mortar);\n\n  for (size_t test_order = 0; test_order < num_points_self_mortar;\n       ++test_order) {\n    CAPTURE(test_order);\n    const auto test_func_self_mortar = [test_order](const auto& x) {\n      return pow(x, test_order);\n    };\n\n    const DataVector data_self_mortar =\n        test_func_self_mortar(points_self_mortar);\n    const DataVector projected_data_element =\n        apply_matrix(projection, data_self_mortar);\n\n    // Test points for each half in each coordinate system.  These\n    // have to have one extra point because LGL quadrature is not\n    // sufficiently good.\n    const Mesh<1> test_mesh_self_mortar(\n        std::max(mesh_self_mortar.extents(0), mesh_element.extents(0)) + 1,\n        mesh_self_mortar.basis(0), mesh_self_mortar.quadrature(0));\n    const auto& test_points_self_mortar =\n        Spectral::collocation_points(test_mesh_self_mortar);\n    // We don't need to represent the initial function on the other\n    // mortar.\n    const Mesh<1> test_mesh_element(mesh_element.extents(0) + 1,\n                                    mesh_element.basis(0),\n                                    mesh_element.quadrature(0));\n    const auto& test_points_other_mortar =\n        Spectral::collocation_points(test_mesh_element);\n    const auto& test_points_self_element =\n        to_element_self(test_points_self_mortar);\n    const auto& test_points_other_element =\n        to_element_other(test_points_other_mortar);\n\n    // To get the error for the half we projected from, we first\n    // interpolate to the mortar at the test points, and then subtract\n    // the test function at those points.\n    const DataVector error_self_mortar =\n        apply_matrix(Spectral::interpolation_matrix(mesh_element,\n                                                    test_points_self_element),\n                     projected_data_element) -\n        test_func_self_mortar(test_points_self_mortar);\n    // For the other half's error we can just interpolate, since the\n    // source function is zero.\n    const DataVector error_other_mortar = apply_matrix(\n        Spectral::interpolation_matrix(mesh_element, test_points_other_element),\n        projected_data_element);\n\n    for (size_t orthogonality_test_order = 0;\n         orthogonality_test_order < num_points_element;\n         ++orthogonality_test_order) {\n      CAPTURE(orthogonality_test_order);\n      // Make sure we're using the same test function for both halves.\n      // This does not have to be the same as the test function above.\n      const DataVector test_function_self_mortar =\n          pow(test_points_self_element, orthogonality_test_order);\n      const DataVector test_function_other_mortar =\n          pow(test_points_other_element, orthogonality_test_order);\n      CHECK(definite_integral(error_self_mortar * test_function_self_mortar,\n                              test_mesh_self_mortar) ==\n            approx(-definite_integral(\n                error_other_mortar * test_function_other_mortar,\n                test_mesh_element)));\n    }\n  }\n}\n\nvoid test_h_mortar_to_element() {\n  INFO(\"h - mortar to element\");\n  for (const auto& quadrature_dest : quadratures) {\n    for (size_t num_points_dest = 2;\n         // We need one extra point to do the quadrature later.\n         num_points_dest <=\n         Spectral::maximum_number_of_points<Spectral::Basis::Legendre> - 1;\n         ++num_points_dest) {\n      const Mesh<1> mesh_dest(num_points_dest, Spectral::Basis::Legendre,\n                              quadrature_dest);\n      CAPTURE(mesh_dest);\n      for (const auto& quadrature_source : quadratures) {\n        for (size_t num_points_source = 2;\n             num_points_source <=\n             Spectral::maximum_number_of_points<Spectral::Basis::Legendre> - 1;\n             ++num_points_source) {\n          const Mesh<1> mesh_source(\n              num_points_source, Spectral::Basis::Legendre, quadrature_source);\n          CAPTURE(mesh_source);\n          check_mortar_to_element_projection(Spectral::SegmentSize::UpperHalf,\n                                             mesh_dest, mesh_source,\n                                             to_upper_half, to_lower_half);\n          check_mortar_to_element_projection(Spectral::SegmentSize::LowerHalf,\n                                             mesh_dest, mesh_source,\n                                             to_lower_half, to_upper_half);\n        }\n      }\n    }\n  }\n}\n\nvoid test_h_element_to_mortar() {\n  INFO(\"h - element to mortar\");\n  for (const auto& quadrature_dest : quadratures) {\n    for (size_t num_points_dest = 2;\n         num_points_dest <=\n         Spectral::maximum_number_of_points<Spectral::Basis::Legendre>;\n         ++num_points_dest) {\n      const Mesh<1> mesh_dest(num_points_dest, Spectral::Basis::Legendre,\n                              quadrature_dest);\n      CAPTURE(mesh_dest);\n      const auto& points_dest = Spectral::collocation_points(mesh_dest);\n      for (const auto& quadrature_source : quadratures) {\n        for (size_t num_points_source = 2; num_points_source <= num_points_dest;\n             ++num_points_source) {\n          const Mesh<1> mesh_source(\n              num_points_source, Spectral::Basis::Legendre, quadrature_source);\n          CAPTURE(mesh_source);\n          const auto& points_source = Spectral::collocation_points(mesh_source);\n          for (size_t test_order = 0; test_order < num_points_source;\n               ++test_order) {\n            CAPTURE(test_order);\n            const DataVector source_data = pow(points_source, test_order);\n\n            // The function is contained in the destination space, so\n            // projection should not alter it.\n            {\n              const auto& projection = projection_matrix_parent_to_child(\n                  mesh_source, mesh_dest, Spectral::SegmentSize::UpperHalf);\n              const DataVector projected_data =\n                  apply_matrix(projection, source_data);\n              CHECK_ITERABLE_APPROX(\n                  projected_data, pow(to_upper_half(points_dest), test_order));\n            }\n            {\n              const auto& projection = projection_matrix_parent_to_child(\n                  mesh_source, mesh_dest, Spectral::SegmentSize::LowerHalf);\n              const DataVector projected_data =\n                  apply_matrix(projection, source_data);\n              CHECK_ITERABLE_APPROX(\n                  projected_data, pow(to_lower_half(points_dest), test_order));\n            }\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_massive_restriction(const size_t parent_num_points,\n                              const size_t child_num_points) {\n  INFO(\"Massive restriction operator\");\n  CAPTURE(parent_num_points);\n  CAPTURE(child_num_points);\n  REQUIRE(parent_num_points < child_num_points);\n  // Using Gauss quadrature so the diagonal mass-matrix approximation used in\n  // `::dg::apply_mass_matrix` is exact. Note that for Gauss-Lobatto quadrature\n  // the mass matrix is diagonally approximated in most places in the code, but\n  // the `projection_matrix_child_to_parent` uses the exact mass matrix because\n  // it is implemented in terms of Vandermonde matrices.\n  const Mesh<1> parent_mesh{parent_num_points, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::Gauss};\n  const Mesh<1> child_mesh{child_num_points, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::Gauss};\n  const auto& x_child = Spectral::collocation_points(child_mesh);\n  DataVector child_data = square(x_child) + x_child + 1.;\n  for (const SegmentSize child_size :\n       {SegmentSize::Full, SegmentSize::LowerHalf, SegmentSize::UpperHalf}) {\n    CAPTURE(child_size);\n    // Check R = M_coarse^-1 * I^T * M_fine and R_massive = I^T\n    // => M_coarse * R * f = R_massive * M_fine * f\n    //\n    // (i) Compute l.h.s.\n    const auto& restriction_operator =\n        projection_matrix_child_to_parent(child_mesh, parent_mesh, child_size);\n    auto lhs = apply_matrix(restriction_operator, child_data);\n    ::dg::apply_mass_matrix(make_not_null(&lhs), parent_mesh);\n    // (ii) Compute r.h.s.\n    auto massive_child_data = child_data;\n    if (child_size != SegmentSize::Full) {\n      // This is the Jacobian from logical to inertial coordinates (we take the\n      // parent logical coordinates as inertial so don't have to apply a\n      // Jacobian above). The `apply_mass_matrix` function requires\n      // pre-multiplying by the Jacobian.\n      massive_child_data *= 0.5;\n    }\n    ::dg::apply_mass_matrix(make_not_null(&massive_child_data), child_mesh);\n    const auto& restriction_operator_massive =\n        projection_matrix_child_to_parent(child_mesh, parent_mesh, child_size,\n                                          true);\n    const auto rhs =\n        apply_matrix(restriction_operator_massive, massive_child_data);\n    Approx local_approx = Approx::custom().epsilon(1.0e-9);\n    CHECK_ITERABLE_CUSTOM_APPROX(lhs, rhs, local_approx);\n  }\n}\n\nvoid test_exact_restriction() {\n  INFO(\"Exact restriction\");\n  const Mesh<1> child_mesh{3, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::Gauss};\n  const Mesh<1> parent_mesh{3, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::Gauss};\n  const auto& x_parent = Spectral::collocation_points(parent_mesh);\n  const auto& xi_child = Spectral::collocation_points(child_mesh);\n  const DataVector x_child_left = xi_child / 2. - 0.5;\n  const DataVector x_child_right = xi_child / 2. + 0.5;\n  // This polynomial is exactly represented on both the child and the parent\n  // meshes\n  const auto func = [](const DataVector& x) -> DataVector {\n    return cube(x) + square(x) + x + 1.;\n  };\n  DataVector child_data_left = func(x_child_left);\n  DataVector child_data_right = func(x_child_right);\n  DataVector parent_data = func(x_parent);\n\n  // Restrict function values\n  const auto& restriction_operator_left = projection_matrix_child_to_parent(\n      child_mesh, parent_mesh, SegmentSize::LowerHalf);\n  const auto& restriction_operator_right = projection_matrix_child_to_parent(\n      child_mesh, parent_mesh, SegmentSize::UpperHalf);\n  auto restricted_data =\n      apply_matrix(restriction_operator_left, child_data_left);\n  restricted_data += apply_matrix(restriction_operator_right, child_data_right);\n  CHECK_ITERABLE_APPROX(parent_data, restricted_data);\n\n  // Restrict massive data\n  ::dg::apply_mass_matrix(make_not_null(&parent_data), parent_mesh);\n  // This is the Jacobian from logical to inertial coordinates (we take the\n  // parent logical coordinates as inertial so don't have to apply a Jacobian\n  // above). The `apply_mass_matrix` function requires pre-multiplying by the\n  // Jacobian.\n  child_data_left *= 0.5;\n  child_data_right *= 0.5;\n  ::dg::apply_mass_matrix(make_not_null(&child_data_left), child_mesh);\n  ::dg::apply_mass_matrix(make_not_null(&child_data_right), child_mesh);\n  const auto& restriction_operator_left_massive =\n      projection_matrix_child_to_parent(child_mesh, parent_mesh,\n                                        SegmentSize::LowerHalf, true);\n  const auto& restriction_operator_right_massive =\n      projection_matrix_child_to_parent(child_mesh, parent_mesh,\n                                        SegmentSize::UpperHalf, true);\n  restricted_data =\n      apply_matrix(restriction_operator_left_massive, child_data_left);\n  restricted_data +=\n      apply_matrix(restriction_operator_right_massive, child_data_right);\n  CHECK_ITERABLE_APPROX(parent_data, restricted_data);\n}\n\ntemplate <size_t Dim>\nvoid test_higher_dimensions() {\n  INFO(\"Higher-dimensional operators\");\n  CAPTURE(Dim);\n  // Higher-dimensional operators are just Cartesian products of the 1D\n  // matrices, we only test here if they are constructed correctly.\n  // The particular basis and quadrature don't matter for this test.\n  const auto basis = Spectral::Basis::Legendre;\n  const auto quadrature = Spectral::Quadrature::GaussLobatto;\n  {\n    INFO(\"Identity\");\n    const auto restriction_identity =\n        Spectral::projection_matrix_child_to_parent(\n            {3, basis, quadrature}, {3, basis, quadrature},\n            make_array<Dim>(Spectral::SegmentSize::Full));\n    const auto prolongation_identity =\n        Spectral::projection_matrix_parent_to_child(\n            {3, basis, quadrature}, {3, basis, quadrature},\n            make_array<Dim>(Spectral::SegmentSize::Full));\n    for (size_t d = 0; d < Dim; ++d) {\n      CHECK(gsl::at(restriction_identity, d).get() == Matrix{});\n      CHECK(gsl::at(prolongation_identity, d).get() == Matrix{});\n    }\n  }\n  {\n    const size_t parent_extents = 3;\n    std::array<size_t, Dim> child_extents{};\n    std::iota(child_extents.begin(), child_extents.end(), size_t{3});\n    auto child_sizes = make_array<Dim>(Spectral::SegmentSize::Full);\n    if constexpr (Dim > 1) {\n      child_sizes[1] = Spectral::SegmentSize::UpperHalf;\n    }\n    const auto projection_matrix = Spectral::projection_matrix_child_to_parent(\n        {child_extents, basis, quadrature}, {parent_extents, basis, quadrature},\n        child_sizes);\n    CHECK(projection_matrix[0].get() == Matrix{});\n    if constexpr (Dim > 1) {\n      CHECK(&projection_matrix[1].get() ==\n            &Spectral::projection_matrix_child_to_parent(\n                {4, basis, quadrature}, {3, basis, quadrature},\n                Spectral::SegmentSize::UpperHalf));\n    }\n    if constexpr (Dim > 2) {\n      CHECK(&projection_matrix[2].get() ==\n            &Spectral::projection_matrix_child_to_parent(\n                {5, basis, quadrature}, {3, basis, quadrature},\n                Spectral::SegmentSize::Full));\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_p_projection_matrices() {\n  INFO(\"p-projection operators\");\n  CAPTURE(Dim);\n  // Higher-dimensional operators are just Cartesian products of the 1D\n  // matrices, we only test here if they are constructed correctly.\n  // The particular basis and quadrature don't matter for this test.\n  const auto basis = Spectral::Basis::Legendre;\n  const auto quadrature = Spectral::Quadrature::GaussLobatto;\n  {\n    INFO(\"Identity\");\n    const auto identity = Spectral::p_projection_matrices(\n        Mesh<Dim>{3, basis, quadrature}, Mesh<Dim>{3, basis, quadrature});\n    for (size_t d = 0; d < Dim; ++d) {\n      CHECK(gsl::at(identity, d).get() == Matrix{});\n    }\n  }\n  {\n    const size_t source_extents = 4;\n    std::array<size_t, Dim> target_extents{};\n    std::iota(target_extents.begin(), target_extents.end(), size_t{3});\n    const auto projection_matrix = Spectral::p_projection_matrices(\n        Mesh<Dim>{source_extents, basis, quadrature},\n        Mesh<Dim>{target_extents, basis, quadrature});\n    CHECK(&projection_matrix[0].get() ==\n          &Spectral::projection_matrix_child_to_parent(\n              {4, basis, quadrature}, {3, basis, quadrature},\n              Spectral::SegmentSize::Full));\n    if constexpr (Dim > 1) {\n      CHECK(projection_matrix[1].get() == Matrix{});\n    }\n    if constexpr (Dim > 2) {\n      CHECK(&projection_matrix[2].get() ==\n            &Spectral::projection_matrix_child_to_parent(\n                {4, basis, quadrature}, {5, basis, quadrature},\n                Spectral::SegmentSize::Full));\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_projection_matrices() {\n  INFO(\"generic projection operators\");\n  CAPTURE(Dim);\n  // Higher-dimensional operators are just Cartesian products of the 1D\n  // matrices, we only test here if they are constructed correctly.\n  // The particular basis and quadrature don't matter for this test.\n  const auto basis = Spectral::Basis::Legendre;\n  const auto quadrature = Spectral::Quadrature::GaussLobatto;\n  {\n    INFO(\"Identity\");\n    const auto identity = Spectral::projection_matrices(\n        Mesh<Dim>{3, basis, quadrature}, Mesh<Dim>{3, basis, quadrature},\n        make_array<Dim>(SegmentSize::Full), make_array<Dim>(SegmentSize::Full));\n    for (size_t d = 0; d < Dim; ++d) {\n      CHECK(gsl::at(identity, d).get() == Matrix{});\n    }\n  }\n  {\n    const size_t source_extents = 4;\n    std::array<size_t, Dim> target_extents{};\n    std::iota(target_extents.begin(), target_extents.end(), size_t{3});\n    std::array<SegmentSize, Dim> source_sizes{};\n    std::array<SegmentSize, Dim> target_sizes{};\n    source_sizes[0] = SegmentSize::LowerHalf;\n    target_sizes[0] = SegmentSize::LowerHalf;\n    if constexpr (Dim > 1) {\n      source_sizes[1] = SegmentSize::Full;\n      target_sizes[1] = SegmentSize::UpperHalf;\n    }\n    if constexpr (Dim > 2) {\n      source_sizes[2] = SegmentSize::LowerHalf;\n      target_sizes[2] = SegmentSize::Full;\n    }\n    const auto projection_matrix = Spectral::projection_matrices(\n        Mesh<Dim>{source_extents, basis, quadrature},\n        Mesh<Dim>{target_extents, basis, quadrature}, source_sizes,\n        target_sizes);\n    CHECK(&projection_matrix[0].get() ==\n          &Spectral::projection_matrix_child_to_parent(\n              {4, basis, quadrature}, {3, basis, quadrature},\n              Spectral::SegmentSize::Full));\n    if constexpr (Dim > 1) {\n      CHECK(&projection_matrix[1].get() ==\n            &Spectral::projection_matrix_parent_to_child(\n                {4, basis, quadrature}, {4, basis, quadrature},\n                Spectral::SegmentSize::UpperHalf));\n    }\n    if constexpr (Dim > 2) {\n      CHECK(&projection_matrix[2].get() ==\n            &Spectral::projection_matrix_child_to_parent(\n                {4, basis, quadrature}, {5, basis, quadrature},\n                Spectral::SegmentSize::LowerHalf));\n    }\n  }\n}\n\nvoid test_fourier_p_projections() {\n  // Test both child_to_parent and parent_to_child projections for Fourier basis\n  // For Fourier basis, SegmentSize::Full only--Fourier does not support\n  // h-refinement\n  INFO(\"Fourier p-projections\");\n  const auto local_approx = Approx::custom().scale(1.0).epsilon(1.0e-14);\n  constexpr size_t max_points_f =\n      Spectral::maximum_number_of_points<Spectral::Basis::Fourier>;\n  const std::vector<std::pair<unsigned, unsigned>> test_funcs = {\n      {1_st, 0_st}, {0_st, 1_st}, {1_st, 1_st}, {5_st, 4_st}, {13_st, 15_st}};\n\n  // source <= dest (prolongation)\n  constexpr size_t stride = 5;\n  for (size_t num_points_source = 2; num_points_source <= max_points_f;\n       num_points_source += stride) {\n    const Mesh<1> mesh_source(num_points_source, Spectral::Basis::Fourier,\n                              Spectral::Quadrature::Equiangular);\n    const auto& points_source = Spectral::collocation_points(mesh_source);\n    CAPTURE(mesh_source);\n    for (size_t num_points_dest = num_points_source + stride;\n         num_points_dest <= std::min(3 * num_points_source, max_points_f);\n         num_points_dest += stride) {\n      const Mesh<1> mesh_dest(num_points_dest, Spectral::Basis::Fourier,\n                              Spectral::Quadrature::Equiangular);\n      const auto& points_dest = Spectral::collocation_points(mesh_dest);\n      CAPTURE(mesh_dest);\n\n      // Test child_to_parent (mortar to element)\n      const auto& projection_child_to_parent =\n          projection_matrix_child_to_parent(mesh_source, mesh_dest,\n                                            Spectral::SegmentSize::Full);\n      // Test parent_to_child (element to mortar)\n      const auto& projection_parent_to_child =\n          projection_matrix_parent_to_child(mesh_source, mesh_dest,\n                                            Spectral::SegmentSize::Full);\n\n      // For Fourier with SegmentSize::Full, these should be the same matrix\n      CHECK(&projection_child_to_parent == &projection_parent_to_child);\n\n      for (const auto& [pow_nx, pow_ny] : test_funcs) {\n        const size_t required_modes = pow_nx + pow_ny;\n        if (required_modes >= num_points_source / 2) {\n          continue;\n        }\n        CAPTURE(pow_nx);\n        CAPTURE(pow_ny);\n        const FourierTestFunctions::ProductOfPolynomials func(pow_nx, pow_ny);\n\n        const DataVector projected_data_ctp =\n            apply_matrix(projection_child_to_parent, func(points_source));\n        CHECK_ITERABLE_CUSTOM_APPROX(projected_data_ctp, func(points_dest),\n                                     local_approx);\n      }\n    }\n  }\n\n  // source > dest (restriction)\n  for (size_t num_points_dest = 2; num_points_dest <= max_points_f;\n       num_points_dest += stride) {\n    const Mesh<1> mesh_dest(num_points_dest, Spectral::Basis::Fourier,\n                            Spectral::Quadrature::Equiangular);\n    CAPTURE(mesh_dest);\n    for (size_t num_points_source = num_points_dest + stride;\n         num_points_source <= std::min(3 * num_points_dest, max_points_f);\n         num_points_source += stride) {\n      const Mesh<1> mesh_source(num_points_source, Spectral::Basis::Fourier,\n                                Spectral::Quadrature::Equiangular);\n      const auto& points_source = Spectral::collocation_points(mesh_source);\n      CAPTURE(mesh_source);\n\n      // Test both projection functions\n      const auto& projection_child_to_parent =\n          projection_matrix_child_to_parent(mesh_source, mesh_dest,\n                                            Spectral::SegmentSize::Full);\n      const auto& projection_parent_to_child =\n          projection_matrix_parent_to_child(mesh_source, mesh_dest,\n                                            Spectral::SegmentSize::Full);\n\n      // For Fourier with SegmentSize::Full, these should be the same matrix\n      CHECK(&projection_child_to_parent == &projection_parent_to_child);\n\n      for (const auto& [pow_nx, pow_ny] : test_funcs) {\n        const size_t required_modes = pow_nx + pow_ny;\n        if (required_modes >= num_points_source / 2) {\n          continue;\n        }\n        CAPTURE(pow_nx);\n        CAPTURE(pow_ny);\n        const FourierTestFunctions::ProductOfPolynomials func(pow_nx, pow_ny);\n        const DataVector projected_data =\n            apply_matrix(projection_child_to_parent, func(points_source));\n        // Interpolate projected result back to the fine grid and measure the\n        // truncation error.\n        const DataVector interpolated_projected_data = apply_matrix(\n            Spectral::interpolation_matrix(mesh_dest, points_source),\n            projected_data);\n        const DataVector error =\n            interpolated_projected_data - func(points_source);\n        // The error must be L2-orthogonal to every basis function that fits\n        // in the destination mesh\n        for (size_t index = 0; index < num_points_dest; ++index) {\n          CAPTURE(index);\n          const DataVector basis_func_values =\n              Spectral::compute_basis_function_value<Spectral::Basis::Fourier>(\n                  index, points_source);\n          CHECK(definite_integral(error * basis_func_values, mesh_source) ==\n                local_approx(0.0));\n        }\n      }\n    }\n  }\n}\n\nvoid test_zernike_b2_fourier_equivalence() {\n  INFO(\"ZernikeB2-Fourier equivalence\");\n  // Test that ZernikeB2 with Equiangular behaves identically to Fourier\n  const size_t parent_size = 5;\n  const size_t child_size = 9;\n  const Mesh<1> fourier_parent(parent_size, Spectral::Basis::Fourier,\n                               Spectral::Quadrature::Equiangular);\n  const Mesh<1> fourier_child(child_size, Spectral::Basis::Fourier,\n                              Spectral::Quadrature::Equiangular);\n  const Mesh<1> zernike_parent(parent_size, Spectral::Basis::ZernikeB2,\n                               Spectral::Quadrature::Equiangular);\n  const Mesh<1> zernike_child(child_size, Spectral::Basis::ZernikeB2,\n                              Spectral::Quadrature::Equiangular);\n\n  const auto& f_to_f_child_to_parent = projection_matrix_child_to_parent(\n      fourier_child, fourier_parent, Spectral::SegmentSize::Full);\n  const auto& f_to_z_child_to_parent = projection_matrix_child_to_parent(\n      fourier_child, zernike_parent, Spectral::SegmentSize::Full);\n  CHECK(&f_to_f_child_to_parent == &f_to_z_child_to_parent);\n  const auto& z_to_z_child_to_parent = projection_matrix_child_to_parent(\n      zernike_child, zernike_parent, Spectral::SegmentSize::Full);\n  CHECK(&f_to_f_child_to_parent == &z_to_z_child_to_parent);\n\n  const auto& f_to_f_parent_to_child = projection_matrix_child_to_parent(\n      fourier_parent, fourier_child, Spectral::SegmentSize::Full);\n  const auto& f_to_z_parent_to_child = projection_matrix_child_to_parent(\n      fourier_parent, zernike_child, Spectral::SegmentSize::Full);\n  CHECK(&f_to_f_parent_to_child == &f_to_z_parent_to_child);\n  const auto& z_to_z_parent_to_child = projection_matrix_child_to_parent(\n      zernike_parent, zernike_child, Spectral::SegmentSize::Full);\n  CHECK(&f_to_f_parent_to_child == &z_to_z_parent_to_child);\n}\n\n#ifdef SPECTRE_DEBUG\nvoid test_fourier_and_zernikeb2_asserts() {\n  {\n    INFO(\"Fourier h-refinement asserts\");\n    const Mesh<1> mesh(5, Spectral::Basis::Fourier,\n                       Spectral::Quadrature::Equiangular);\n    CHECK_THROWS_WITH(projection_matrix_child_to_parent(\n                          mesh, mesh, Spectral::SegmentSize::UpperHalf),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Unsupported projection combination\"));\n    CHECK_THROWS_WITH(projection_matrix_child_to_parent(\n                          mesh, mesh, Spectral::SegmentSize::LowerHalf),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Unsupported projection combination\"));\n  }\n  {\n    INFO(\"ZernikeB2 projection asserts\");\n\n    // Test that ZernikeB2 without Equiangular quadrature is rejected\n    const Mesh<1> zernike_gauss{4, Basis::ZernikeB2, Quadrature::Gauss};\n    const Mesh<1> fourier_mesh{4, Basis::Fourier, Quadrature::Equiangular};\n\n    CHECK_THROWS_WITH(projection_matrix_child_to_parent(\n                          zernike_gauss, fourier_mesh, SegmentSize::Full),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Unsupported projection combination\"));\n\n    // Test that ZernikeB2 with h-refinement is rejected\n    const Mesh<1> zernike_equi{4, Basis::ZernikeB2, Quadrature::Equiangular};\n\n    CHECK_THROWS_WITH(projection_matrix_child_to_parent(\n                          zernike_equi, zernike_equi, SegmentSize::UpperHalf),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Unsupported projection combination\"));\n\n    CHECK_THROWS_WITH(projection_matrix_child_to_parent(\n                          zernike_equi, zernike_equi, SegmentSize::LowerHalf),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Unsupported projection combination\"));\n  }\n}\n#endif  // SPECTRE_DEBUG\n\nvoid test_hash() {\n  CHECK(Spectral::MortarSizeHash<1>{}({}) == 0);\n\n  CHECK(Spectral::MortarSizeHash<2>{}({Spectral::SegmentSize::LowerHalf}) == 0);\n  CHECK(Spectral::MortarSizeHash<2>{}({Spectral::SegmentSize::Full}) == 0);\n  CHECK(Spectral::MortarSizeHash<2>{}({Spectral::SegmentSize::UpperHalf}) == 1);\n\n  CHECK(Spectral::MortarSizeHash<3>{}({Spectral::SegmentSize::LowerHalf,\n                                       Spectral::SegmentSize::LowerHalf}) == 0);\n  CHECK(Spectral::MortarSizeHash<3>{}({Spectral::SegmentSize::UpperHalf,\n                                       Spectral::SegmentSize::LowerHalf}) == 1);\n  CHECK(Spectral::MortarSizeHash<3>{}({Spectral::SegmentSize::Full,\n                                       Spectral::SegmentSize::LowerHalf}) == 0);\n\n  CHECK(Spectral::MortarSizeHash<3>{}({Spectral::SegmentSize::LowerHalf,\n                                       Spectral::SegmentSize::UpperHalf}) == 2);\n  CHECK(Spectral::MortarSizeHash<3>{}({Spectral::SegmentSize::UpperHalf,\n                                       Spectral::SegmentSize::UpperHalf}) == 3);\n  CHECK(Spectral::MortarSizeHash<3>{}({Spectral::SegmentSize::Full,\n                                       Spectral::SegmentSize::UpperHalf}) == 2);\n\n  CHECK(Spectral::MortarSizeHash<3>{}({Spectral::SegmentSize::LowerHalf,\n                                       Spectral::SegmentSize::Full}) == 0);\n  CHECK(Spectral::MortarSizeHash<3>{}({Spectral::SegmentSize::UpperHalf,\n                                       Spectral::SegmentSize::Full}) == 1);\n  CHECK(Spectral::MortarSizeHash<3>{}(\n            {Spectral::SegmentSize::Full, Spectral::SegmentSize::Full}) == 0);\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.Projection\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_mortar_size();\n  test_needs_projection();\n  test_p_mortar_to_element();\n  test_p_element_to_mortar();\n  test_h_mortar_to_element();\n  test_h_element_to_mortar();\n  for (size_t child_num_points = 4;\n       child_num_points <= maximum_number_of_points<Spectral::Basis::Legendre>;\n       ++child_num_points) {\n    for (size_t parent_num_points = 3; parent_num_points < child_num_points;\n         ++parent_num_points) {\n      test_massive_restriction(parent_num_points, child_num_points);\n    }\n  }\n  test_exact_restriction();\n  test_higher_dimensions<1>();\n  test_higher_dimensions<2>();\n  test_higher_dimensions<3>();\n  test_p_projection_matrices<1>();\n  test_p_projection_matrices<2>();\n  test_p_projection_matrices<3>();\n  test_projection_matrices<1>();\n  test_projection_matrices<2>();\n  test_projection_matrices<3>();\n  test_fourier_p_projections();\n  test_zernike_b2_fourier_equivalence();\n#ifdef SPECTRE_DEBUG\n  test_fourier_and_zernikeb2_asserts();\n#endif  // SPECTRE_DEBUG\n  test_hash();\n}\n\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Quadrature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.SpatialDiscreitization.Quadrature\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  CHECK(get_output(Spectral::Quadrature::Uninitialized) == \"Uninitialized\");\n  CHECK(get_output(Spectral::Quadrature::Gauss) == \"Gauss\");\n  CHECK(get_output(Spectral::Quadrature::GaussLobatto) == \"GaussLobatto\");\n  CHECK(get_output(Spectral::Quadrature::CellCentered) == \"CellCentered\");\n  CHECK(get_output(Spectral::Quadrature::FaceCentered) == \"FaceCentered\");\n  CHECK(get_output(Spectral::Quadrature::Equiangular) == \"Equiangular\");\n  CHECK(get_output(Spectral::Quadrature::GaussRadauLower) == \"GaussRadauLower\");\n  CHECK(get_output(Spectral::Quadrature::GaussRadauUpper) == \"GaussRadauUpper\");\n  CHECK(get_output(Spectral::Quadrature::AxialSymmetry) == \"AxialSymmetry\");\n  CHECK(get_output(Spectral::Quadrature::SphericalSymmetry) ==\n        \"SphericalSymmetry\");\n  for (const auto quadrature : Spectral::all_quadratures()) {\n    CHECK(quadrature == TestHelpers::test_creation<Spectral::Quadrature>(\n                            get_output(quadrature)));\n    CHECK(quadrature == Spectral::to_quadrature(get_output(quadrature)));\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        TestHelpers::test_creation<Spectral::Quadrature>(\"Bad quadrature name\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          MakeString{} << \"Failed to convert \\\"Bad quadrature name\\\" to \"\n                          \"Spectral::Quadrature.\\nMust be one of \"\n                       << Spectral::all_quadratures() << \".\"));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_QuadratureWeights.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n\nnamespace Spectral {\nnamespace {\n\ntemplate <Basis basis, Quadrature quadrature>\nvoid test() {\n  CAPTURE(basis);\n  CAPTURE(quadrature);\n  // Cannot represent the integral of a constant with a single point\n  for (size_t n = 2; n <= maximum_number_of_points<basis>; ++n) {\n    const DataVector& weights_n = quadrature_weights<basis, quadrature>(n);\n    const Mesh<1> mesh{n, basis, quadrature};\n    const DataVector& weights_m = quadrature_weights(mesh);\n    CHECK(weights_n == weights_m);\n    CHECK(weights_n.data() == weights_m.data());\n    if constexpr (basis == Basis::ZernikeB1) {\n      // The integral of a constant is xi. Actual logical coordinates from\n      // [0,1], with a orthogonality weighting of 1, thus 1.\n      CHECK(sum(weights_n) == approx(1.0));\n    } else if constexpr (basis == Basis::ZernikeB2) {\n      // The integral of a constant is xi. Actual logical coordinates from\n      // [0,1], with a orthogonality weighting of r, thus 1/2.\n      CHECK(sum(weights_n) == approx(0.5));\n    } else if constexpr (basis == Basis::ZernikeB3) {\n      // The integral of a constant is xi. Actual logical coordinates from\n      // [0,1], with a orthogonality weighting of r^2, thus 1/3.\n      CHECK(sum(weights_n) == approx(1. / 3.));\n    } else {\n      // The integral of a constant is xi.  Logical coordintes from [-1, 1].\n      // The definite integral is thus 2.\n      CHECK(sum(weights_n) == approx(2.0));\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral.QuadratureWeights\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test<Basis::Legendre, Quadrature::Gauss>();\n  test<Basis::Legendre, Quadrature::GaussLobatto>();\n  test<Basis::Chebyshev, Quadrature::Gauss>();\n  test<Basis::Chebyshev, Quadrature::GaussLobatto>();\n  test<Basis::ZernikeB1, Quadrature::GaussRadauUpper>();\n  test<Basis::ZernikeB2, Quadrature::GaussRadauUpper>();\n  test<Basis::ZernikeB3, Quadrature::GaussRadauUpper>();\n}\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_SegmentSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Spectral.SegmentSize\", \"[NumericalAlgorithms][Unit]\") {\n  CHECK(get_output(Spectral::SegmentSize::Uninitialized) == \"Uninitialized\");\n  CHECK(get_output(Spectral::SegmentSize::Full) == \"Full\");\n  CHECK(get_output(Spectral::SegmentSize::UpperHalf) == \"UpperHalf\");\n  CHECK(get_output(Spectral::SegmentSize::LowerHalf) == \"LowerHalf\");\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_Spectral.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// \\file\n// Tests of spectral operations that should work for any basis and quadrature.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationMatrices.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryInterpolationTerm.hpp\"\n#include \"NumericalAlgorithms/Spectral/BoundaryLiftingTerm.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/InterpolationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/InverseWeightFunctionValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/LinearFilteringMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/MinimumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n#include \"Utilities/Blas.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace {\nDataVector unit_polynomial(const size_t deg, const DataVector& x) {\n  // Choose all polynomial coefficients to be one\n  const std::vector<double> coeffs(deg + 1, 1.);\n  return evaluate_polynomial(coeffs, x);\n}\nDataVector unit_polynomial_derivative(const size_t deg, const DataVector& x) {\n  std::vector<double> coeffs(deg);\n  for (size_t p = 0; p < coeffs.size(); p++) {\n    coeffs[p] = p + 1;\n  }\n  return evaluate_polynomial(coeffs, x);\n}\ndouble unit_polynomial_integral(const size_t deg) {\n  std::vector<double> coeffs(deg + 2);\n  coeffs[0] = 0.;\n  for (size_t p = 1; p < coeffs.size(); p++) {\n    coeffs[p] = 1. / p;\n  }\n  const auto integrals = evaluate_polynomial(coeffs, DataVector{-1., 1.});\n  return integrals[1] - integrals[0];\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType,\n          typename Function>\nvoid test_exact_differentiation_impl(const Function& max_poly_deg) {\n  INFO(\"Test exact differentiation.\");\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n  for (size_t n = Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       n <= Spectral ::maximum_number_of_points<BasisType>; n++) {\n    CAPTURE(n);\n    for (size_t p = 0; p <= max_poly_deg(n); p++) {\n      CAPTURE(p);\n      const auto& collocation_pts =\n          Spectral::collocation_points<BasisType, QuadratureType>(n);\n      const auto& diff_matrix =\n          Spectral::differentiation_matrix<BasisType, QuadratureType>(n);\n      const auto u = unit_polynomial(p, collocation_pts);\n      DataVector numeric_derivative{n};\n      // [matrix_spacing]\n      dgemv_('N', n, n, 1., diff_matrix.data(), diff_matrix.spacing(), u.data(),\n             1, 0.0, numeric_derivative.data(), 1);\n      // [matrix_spacing]\n      const auto analytic_derivative =\n          unit_polynomial_derivative(p, collocation_pts);\n      if (BasisType == Spectral::Basis::FiniteDifference and n > 20) {\n        Approx local_approx = Approx::custom().epsilon(1.0e-11).scale(1.0);\n        CHECK_ITERABLE_CUSTOM_APPROX(analytic_derivative, numeric_derivative,\n                                     local_approx);\n      } else {\n        Approx local_approx = Approx::custom().epsilon(1.0e-11).scale(1.0);\n        CHECK_ITERABLE_CUSTOM_APPROX(analytic_derivative, numeric_derivative,\n                                     local_approx);\n      }\n    }\n  }\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nvoid test_weak_differentiation() {\n  static_assert(BasisType == Spectral::Basis::Legendre,\n                \"test_weak_differentiation_matrix may not be correct for \"\n                \"non-Legendre basis functions.\");\n  INFO(\"Test weak differentiation.\");\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n\n  for (size_t n = Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       n <= Spectral::maximum_number_of_points<BasisType>; n++) {\n    CAPTURE(n);\n    const DataVector& quad_weights =\n        Spectral::quadrature_weights<BasisType, QuadratureType>(n);\n    const Matrix& weak_diff_matrix =\n        Spectral::weak_flux_differentiation_matrix<BasisType, QuadratureType>(\n            n);\n    const Matrix& diff_matrix =\n        Spectral::differentiation_matrix<BasisType, QuadratureType>(n);\n    Matrix expected_weak_diff_matrix(diff_matrix.rows(), diff_matrix.columns());\n\n    for (size_t i = 0; i < n; ++i) {\n      for (size_t l = 0; l < n; ++l) {\n        expected_weak_diff_matrix(i, l) =\n            quad_weights[l] / quad_weights[i] * diff_matrix(l, i);\n      }\n    }\n    for (size_t i = 0; i < n; ++i) {\n      for (size_t l = 0; l < n; ++l) {\n        CAPTURE(i);\n        CAPTURE(l);\n        Approx local_approx = Approx::custom().epsilon(1e-10);\n        CHECK(expected_weak_diff_matrix(i, l) ==\n              local_approx(weak_diff_matrix(i, l)));\n      }\n    }\n\n    if (QuadratureType == Spectral::Quadrature::GaussLobatto) {\n      for (size_t p = 0; p <= n - 1; p++) {\n        const auto& collocation_pts =\n            Spectral::collocation_points<BasisType, QuadratureType>(n);\n        const auto u = unit_polynomial(p, collocation_pts);\n        DataVector numeric_derivative{n};\n        dgemv_('N', n, n, 1., weak_diff_matrix.data(),\n               weak_diff_matrix.spacing(), u.data(), 1, 0.0,\n               numeric_derivative.data(), 1);\n        const auto analytic_derivative =\n            unit_polynomial_derivative(p, collocation_pts);\n        for (size_t i = 1; i < n - 1; ++i) {\n          Approx local_approx = Approx::custom().epsilon(1e-10).scale(1.);\n          CHECK(local_approx(numeric_derivative[i]) == -analytic_derivative[i]);\n        }\n      }\n    }\n  }\n}\n\nvoid test_exact_differentiation_matrices() {\n  const auto minus_one = [](const size_t n) { return n - 1; };\n  test_exact_differentiation_impl<Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::Gauss>(minus_one);\n  test_exact_differentiation_impl<Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto>(\n      minus_one);\n  test_exact_differentiation_impl<Spectral::Basis::Chebyshev,\n                                  Spectral::Quadrature::Gauss>(minus_one);\n  test_exact_differentiation_impl<Spectral::Basis::Chebyshev,\n                                  Spectral::Quadrature::GaussLobatto>(\n      minus_one);\n\n  test_weak_differentiation<Spectral::Basis::Legendre,\n                            Spectral::Quadrature::Gauss>();\n  test_weak_differentiation<Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto>();\n\n  // Test summation by parts cell-centered FD.\n  test_exact_differentiation_impl<Spectral::Basis::FiniteDifference,\n                                  Spectral::Quadrature::CellCentered>(\n      [](const size_t n) -> size_t {\n        return n >= 10 ? 5 : (n >= 7 ? 3 : (n == 6 ? 2 : (n == 1 ? 0 : 1)));\n      });\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nvoid test_linear_filter_impl() {\n  CAPTURE(BasisType);\n  CAPTURE(QuadratureType);\n  for (size_t n = Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       n <= Spectral::maximum_number_of_points<BasisType>; n++) {\n    const auto& filter_matrix =\n        Spectral::linear_filter_matrix<BasisType, QuadratureType>(n);\n    const auto& nodal_to_modal_matrix =\n        Spectral::nodal_to_modal_matrix<BasisType, QuadratureType>(n);\n    const auto& collocation_pts =\n        Spectral::collocation_points<BasisType, QuadratureType>(n);\n    const DataVector u = exp(collocation_pts);\n    DataVector u_filtered(n);\n    dgemv_('N', n, n, 1.0, filter_matrix.data(), filter_matrix.spacing(),\n           u.data(), 1, 0.0, u_filtered.data(), 1);\n    DataVector u_filtered_spectral(n);\n    dgemv_('N', n, n, 1.0, nodal_to_modal_matrix.data(),\n           nodal_to_modal_matrix.spacing(), u_filtered.data(), 1, 0.0,\n           u_filtered_spectral.data(), 1);\n    Approx local_approx = Approx::custom().epsilon(1e-13).scale(1.0);\n    for (size_t s = 2; s < n; ++s) {\n      CHECK(0.0 == local_approx(u_filtered_spectral[s]));\n    }\n  }\n}\n\nvoid test_linear_filter() {\n  test_linear_filter_impl<Spectral::Basis::Legendre,\n                          Spectral::Quadrature::Gauss>();\n  test_linear_filter_impl<Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto>();\n  test_linear_filter_impl<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::Gauss>();\n  test_linear_filter_impl<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::GaussLobatto>();\n}\n\n// By default, uses the default tolerance for floating-point comparisons. When\n// a non-zero eps is passed in as last argument, uses that tolerance instead.\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType,\n          typename Function>\nvoid test_interpolation_matrix(const DataVector& target_points,\n                               const Function& max_poly_deg,\n                               const double eps = 0.) {\n  DataVector interpolated_u(target_points.size(), 0.);\n  for (size_t n = Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       n <= std::min(Spectral::maximum_number_of_points<BasisType>, 12_st);\n       n++) {\n    const auto& collocation_pts =\n        Spectral::collocation_points<BasisType, QuadratureType>(n);\n    const auto interp_matrix =\n        Spectral::interpolation_matrix<BasisType, QuadratureType>(\n            n, target_points);\n    for (size_t p = 0; p <= max_poly_deg(n); p++) {\n      const DataVector u = unit_polynomial(p, collocation_pts);\n      dgemv_('n', target_points.size(), n, 1., interp_matrix.data(),\n             interp_matrix.spacing(), u.data(), 1, 0., interpolated_u.data(),\n             1);\n      CHECK(interpolated_u.size() == target_points.size());\n      if (eps <= 0.) {\n        CHECK_ITERABLE_APPROX(unit_polynomial(p, target_points),\n                              interpolated_u);\n      } else {\n        Approx local_approx = Approx::custom().epsilon(eps).scale(1.);\n        CHECK_ITERABLE_CUSTOM_APPROX(unit_polynomial(p, target_points),\n                                     interpolated_u, local_approx);\n      }\n    }\n  }\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType,\n          typename Function>\nvoid test_exact_interpolation_impl(const Function& max_poly_deg) {\n  const DataVector target_points{-0.5, -0.4837, 0.5, 0.9378, 1.};\n  test_interpolation_matrix<BasisType, QuadratureType>(target_points,\n                                                       max_poly_deg);\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType,\n          typename Function>\nvoid test_exact_extrapolation_impl(const Function& max_poly_deg) {\n  const DataVector target_points{-1.66, 1., 1.5, 1.98, 2.};\n  // Errors are larger when extrapolating outside of the original grid:\n  // If increasing the number of points tested, the tolerance must be lowered.\n  // 14 pts requires 1.e-8\n  // 15 pts requires 1.e-8\n  // 16 pts requires 1.e-7\n  // 17 pts requires 1.e-7\n  // 18 pts requires 1.e-6\n  // 19 pts requires 1.e-6\n  // 20 pts requires 1.e-5\n  const double eps = 1.e-9;\n  test_interpolation_matrix<BasisType, QuadratureType>(target_points,\n                                                       max_poly_deg, eps);\n}\n\nvoid test_exact_extrapolation() {\n  const auto minus_one = [](const size_t n) { return n - 1; };\n  {\n    INFO(\n        \"Legendre-Gauss interpolation is exact to polynomial order \"\n        \"num_points-1\");\n    test_exact_interpolation_impl<Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::Gauss>(minus_one);\n    test_exact_extrapolation_impl<Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::Gauss>(minus_one);\n  }\n  {\n    INFO(\n        \"Legendre-Gauss-Lobatto interpolation is exact to polynomial \"\n        \"order num_points-1\");\n    test_exact_interpolation_impl<Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto>(\n        minus_one);\n    test_exact_extrapolation_impl<Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto>(\n        minus_one);\n  }\n  {\n    INFO(\n        \"Chebyshev-Gauss interpolation is exact to polynomial \"\n        \"order num_points-1\");\n    test_exact_interpolation_impl<Spectral::Basis::Chebyshev,\n                                  Spectral::Quadrature::Gauss>(minus_one);\n    test_exact_extrapolation_impl<Spectral::Basis::Chebyshev,\n                                  Spectral::Quadrature::Gauss>(minus_one);\n  }\n  {\n    INFO(\n        \"Chebyshev-Gauss-Lobatto interpolation is exact to polynomial \"\n        \"order num_points-1\");\n    test_exact_interpolation_impl<Spectral::Basis::Chebyshev,\n                                  Spectral::Quadrature::GaussLobatto>(\n        minus_one);\n    test_exact_extrapolation_impl<Spectral::Basis::Chebyshev,\n                                  Spectral::Quadrature::GaussLobatto>(\n        minus_one);\n  }\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nvoid test_exact_quadrature(const size_t n, const size_t p,\n                           const double analytic_quadrature) {\n  const auto& collocation_pts =\n      Spectral::collocation_points<BasisType, QuadratureType>(n);\n  // Get the \\f$w_k\\f$, as opposed to the Spectral::quadrature_weights that are\n  // used to compute definite integrals (see test below).\n  const auto w_k =\n      Spectral::compute_collocation_points_and_weights<BasisType,\n                                                       QuadratureType>(n)\n          .second;\n  const DataVector u = unit_polynomial(p, collocation_pts);\n  const double numeric_quadrature = ddot_(n, u.data(), 1, w_k.data(), 1);\n  CHECK_ITERABLE_APPROX(analytic_quadrature, numeric_quadrature);\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType,\n          typename Function>\nvoid test_exact_unit_weight_quadrature(const Function& max_poly_deg) {\n  for (size_t n = Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       n <= Spectral::maximum_number_of_points<BasisType>; n++) {\n    for (size_t p = 0; p <= max_poly_deg(n); p++) {\n      const double analytic_quadrature = unit_polynomial_integral(p);\n      test_exact_quadrature<BasisType, QuadratureType>(n, p,\n                                                       analytic_quadrature);\n    }\n  }\n}\n\nvoid test_exact_quadrature() {\n  {\n    INFO(\n        \"Legendre-Gauss quadrature is exact to polynomial order \"\n        \"2*num_points-1\");\n    test_exact_unit_weight_quadrature<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::Gauss>(\n        [](const size_t n) { return 2 * n - 1; });\n  }\n  {\n    INFO(\n        \"Legendre-Gauss-Lobatto quadrature is exact to polynomial order \"\n        \"2*num_points-3\");\n    test_exact_unit_weight_quadrature<Spectral::Basis::Legendre,\n                                      Spectral::Quadrature::GaussLobatto>(\n        [](const size_t n) { return 2 * n - 3; });\n  }\n  // For a function \\f$f(x)\\f$ the exact quadrature is\n  // \\f$\\int_{-1}^{1}f(x)w(x)\\mathrm{d}x\\f$ where \\f$w(x)=1/sqrt(1-x^2)\\f$ is\n  // the Chebyshev weight. We test this here for polynomials with unit\n  // coefficients \\f$f(x)=1+x+x^2+\\ldots\\f$.\n  {\n    INFO(\n        \"Chebyshev-Gauss quadrature is exact to polynomial order \"\n        \"2*num_points-1\");\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::Gauss>(1, 1, M_PI);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::Gauss>(2, 3, 3. * M_PI / 2.);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::Gauss>(3, 5, 15. * M_PI / 8.);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::Gauss>(4, 7, 35. * M_PI / 16.);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::Gauss>(5, 9,\n                                                       315. * M_PI / 128.);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::Gauss>(6, 11,\n                                                       693. * M_PI / 256.);\n  }\n  {\n    INFO(\n        \"Chebyshev-Gauss-Lobatto quadrature is exact to polynomial order \"\n        \"2*num_points-3\");\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::GaussLobatto>(2, 1, M_PI);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::GaussLobatto>(3, 3,\n                                                              3. * M_PI / 2.);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::GaussLobatto>(4, 5,\n                                                              15. * M_PI / 8.);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::GaussLobatto>(5, 7,\n                                                              35. * M_PI / 16.);\n    test_exact_quadrature<Spectral::Basis::Chebyshev,\n                          Spectral::Quadrature::GaussLobatto>(\n        6, 9, 315. * M_PI / 128.);\n  }\n}\n\ntemplate <Spectral::Basis BasisType, Spectral::Quadrature QuadratureType>\nvoid test_spectral_quantities_for_mesh_impl(const Mesh<1>& slice) {\n  const auto num_points = slice.extents(0);\n  const auto& expected_points =\n      Spectral::collocation_points<BasisType, QuadratureType>(num_points);\n  CHECK(Spectral::collocation_points(slice) == expected_points);\n  const auto& expected_weights =\n      Spectral::quadrature_weights<BasisType, QuadratureType>(num_points);\n  CHECK(Spectral::quadrature_weights(slice) == expected_weights);\n  const auto& expected_diff_matrix =\n      Spectral::differentiation_matrix<BasisType, QuadratureType>(num_points);\n  CHECK(Spectral::differentiation_matrix(slice) == expected_diff_matrix);\n  const DataVector target_points{-0.5, -0.1, 0., 0.75, 0.9888, 1.};\n  const auto expected_interp_matrix_points =\n      Spectral::interpolation_matrix<BasisType, QuadratureType>(num_points,\n                                                                target_points);\n  CHECK(Spectral::interpolation_matrix(slice, target_points) ==\n        expected_interp_matrix_points);\n  const auto& expected_vand_matrix =\n      Spectral::modal_to_nodal_matrix<BasisType, QuadratureType>(num_points);\n  CHECK(Spectral::modal_to_nodal_matrix(slice) == expected_vand_matrix);\n  const auto& expected_inv_vand_matrix =\n      Spectral::nodal_to_modal_matrix<BasisType, QuadratureType>(num_points);\n  CHECK(Spectral::nodal_to_modal_matrix(slice) == expected_inv_vand_matrix);\n  const auto& expected_lin_matrix =\n      Spectral::linear_filter_matrix<BasisType, QuadratureType>(num_points);\n  CHECK(Spectral::linear_filter_matrix(slice) == expected_lin_matrix);\n}\n\nvoid test_spectral_quantities_for_mesh() {\n  // [get_points_for_mesh]\n  const Mesh<2> mesh2d{\n      {{3, 4}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto}}};\n  const auto collocation_points_in_first_dim =\n      Spectral::collocation_points(mesh2d.slice_through(0));\n  // [get_points_for_mesh]\n  test_spectral_quantities_for_mesh_impl<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::Gauss>(\n      mesh2d.slice_through(0));\n  test_spectral_quantities_for_mesh_impl<Spectral::Basis::Legendre,\n                                         Spectral::Quadrature::GaussLobatto>(\n      mesh2d.slice_through(1));\n}\n\nvoid test_gauss_points_boundary_interpolation_and_lifting() {\n  const auto max_poly_deg = [](const size_t num_pts) { return num_pts - 1; };\n  constexpr Spectral::Basis BasisType = Spectral::Basis::Legendre;\n  constexpr Spectral::Quadrature QuadratureType = Spectral::Quadrature::Gauss;\n\n  DataVector interpolated_u_lower(1, 0.);\n  DataVector interpolated_u_upper(1, 0.);\n  for (size_t n = Spectral::minimum_number_of_points<BasisType, QuadratureType>;\n       n < Spectral::maximum_number_of_points<BasisType>; n++) {\n    CAPTURE(n);\n    const auto& collocation_pts =\n        Spectral::collocation_points<BasisType, QuadratureType>(n);\n    Mesh<1> local_mesh{n, BasisType, QuadratureType};\n    const std::pair<Matrix, Matrix>& boundary_interp_matrices =\n        Spectral::boundary_interpolation_matrices(local_mesh);\n    const std::pair<DataVector, DataVector>& lifting_terms =\n        Spectral::boundary_lifting_term(local_mesh);\n    const DataVector& quad_weights = Spectral::quadrature_weights(local_mesh);\n    for (size_t p = 0; p <= max_poly_deg(n); p++) {\n      CAPTURE(p);\n      const DataVector u = unit_polynomial(p, collocation_pts);\n\n      dgemv_('n', 1, n, 1., boundary_interp_matrices.first.data(),\n             boundary_interp_matrices.first.spacing(), u.data(), 1, 0.,\n             interpolated_u_lower.data(), 1);\n      CHECK_ITERABLE_APPROX(unit_polynomial(p, DataVector{-1.0}),\n                            interpolated_u_lower);\n      dgemv_('n', 1, n, 1., boundary_interp_matrices.second.data(),\n             boundary_interp_matrices.second.spacing(), u.data(), 1, 0.,\n             interpolated_u_upper.data(), 1);\n      CHECK_ITERABLE_APPROX(unit_polynomial(p, DataVector{1.0}),\n                            interpolated_u_upper);\n\n      DataVector lift_lagrange_lower(u.size());\n      for (size_t i = 0; i < lift_lagrange_lower.size(); ++i) {\n        lift_lagrange_lower[i] = boundary_interp_matrices.first(0, i);\n      }\n      CHECK_ITERABLE_APPROX(DataVector{lift_lagrange_lower / quad_weights},\n                            lifting_terms.first);\n\n      DataVector lift_lagrange_upper(u.size());\n      for (size_t i = 0; i < lift_lagrange_upper.size(); ++i) {\n        lift_lagrange_upper[i] = boundary_interp_matrices.second(0, i);\n      }\n      CHECK_ITERABLE_APPROX(DataVector{lift_lagrange_upper / quad_weights},\n                            lifting_terms.second);\n    }\n\n    const std::pair<DataVector, DataVector>& interp_terms =\n        Spectral::boundary_interpolation_term(local_mesh);\n    const Matrix interp_to_gauss_matrix = Spectral::interpolation_matrix(\n        Mesh<1>{n == 1 ? 2 : n, BasisType, Spectral::Quadrature::GaussLobatto},\n        Spectral::collocation_points<BasisType, QuadratureType>(n));\n\n    for (size_t i = 0; i < n; ++i) {\n      CHECK(approx(interp_terms.first[i]) == interp_to_gauss_matrix(i, 0));\n      CHECK(approx(interp_terms.second[i]) == interp_to_gauss_matrix(i, n - 1));\n    }\n  }\n}\nvoid test_double_instantiation() {\n  // generate an interpolation matrix using a double and a std::vector of size\n  // 1, and compare.\n  const Mesh<1> mesh1d{\n      {{4}}, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n  const double interpolation_target = 0.2;\n  const auto double_matrix =\n      Spectral::interpolation_matrix(mesh1d, interpolation_target);\n  const auto vector_matrix = Spectral::interpolation_matrix(\n      mesh1d, std::vector<double>{interpolation_target});\n  CHECK_ITERABLE_APPROX(double_matrix, vector_matrix);\n}\n\nvoid test_fd_interpolation_fails() {\n  CHECK_THROWS_WITH(\n      ([]() {\n        const Mesh<1> fd_mesh{5, Spectral::Basis::FiniteDifference,\n                              Spectral::Quadrature::CellCentered};\n        const double interpolation_target = 0.2;\n        const auto matrix =\n            Spectral::interpolation_matrix(fd_mesh, interpolation_target);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot do barycentric interpolation with Basis::FiniteDifference\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Numerical.Spectral\",\n                  \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_exact_differentiation_matrices();\n  test_linear_filter();\n  test_exact_extrapolation();\n  test_exact_quadrature();\n  test_spectral_quantities_for_mesh();\n  test_gauss_points_boundary_interpolation_and_lifting();\n  test_double_instantiation();\n  test_fd_interpolation_fails();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/Spectral/Test_ZernikeGaussRadauUpper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Blaze/IntegerPow.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Helpers/DataStructures/ApplyMatrix.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctionValue.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Jacobi.hpp\"\n#include \"NumericalAlgorithms/Spectral/BasisFunctions/Zernike.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPointsAndWeights.hpp\"\n#include \"NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/ModalToNodalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/NodalToModalMatrix.hpp\"\n#include \"NumericalAlgorithms/Spectral/Parity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/Spectral/QuadratureWeights.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_derivatives(const size_t num_points) {\n  const Approx custom_approx =\n      num_points > 9 ? Approx::custom().epsilon(1.0e-12).scale(1.0)\n                     : Approx::custom().epsilon(1.0e-13).scale(1.0);\n  // Testing differeniation for polynomials exactly representable, so\n  // up to power = num_points\n  constexpr Spectral::Basis basis = Dim == 1   ? Spectral::Basis::ZernikeB1\n                                    : Dim == 2 ? Spectral::Basis::ZernikeB2\n                                               : Spectral::Basis::ZernikeB3;\n  CAPTURE(basis);\n  CAPTURE(num_points);\n  const Mesh<1> mesh{num_points, basis, Spectral::Quadrature::GaussRadauUpper};\n  const auto coords = logical_coordinates(mesh);\n  const DataVector r = 0.5 * (get<0>(coords) + 1.0);\n  const auto f = [](const size_t power, const DataVector& x) {\n    return integer_pow(x, static_cast<int>(power));\n  };\n  const auto d_f = [](const size_t power, const DataVector& x) {\n    return static_cast<double>(power) *\n           integer_pow(x, static_cast<int>(power) - 1);\n  };\n  auto& diff_matrix_even =\n      Spectral::differentiation_matrix<basis,\n                                       Spectral::Quadrature::GaussRadauUpper>(\n          num_points, Spectral::Parity::Even);\n  auto& diff_matrix_odd =\n      Spectral::differentiation_matrix<basis,\n                                       Spectral::Quadrature::GaussRadauUpper>(\n          num_points, Spectral::Parity::Odd);\n  for (size_t power = 0; power <= num_points; ++power) {\n    CAPTURE(power);\n\n    DataVector my_function(num_points);\n    my_function = f(power, r);\n\n    DataVector expected_derivative(num_points);\n    expected_derivative =\n        power > 0 ? d_f(power, r) : DataVector(num_points, 0.0);\n\n    DataVector evaluated_derivative(num_points);\n    // manually inserting 2 from above affine map (handled automatically\n    // in partial_derivatives())\n    evaluated_derivative =\n        2.0 * (power % 2 == 0 ? apply_matrix(diff_matrix_even, my_function)\n                              : apply_matrix(diff_matrix_odd, my_function));\n    CHECK_ITERABLE_CUSTOM_APPROX(evaluated_derivative, expected_derivative,\n                                 custom_approx);\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_transform_identity() {\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3);\n  const Spectral::Quadrature quadrature{Spectral::Quadrature::GaussRadauUpper};\n  constexpr Spectral::Basis basis = Dim == 1   ? Spectral::Basis::ZernikeB1\n                                    : Dim == 2 ? Spectral::Basis::ZernikeB2\n                                               : Spectral::Basis::ZernikeB3;\n  CAPTURE(basis);\n  const Approx custom_approx = Approx::custom().epsilon(2.0e-12).scale(1.0);\n  for (size_t n = 2; n <= Spectral::maximum_number_of_points<basis> / 2; ++n) {\n    for (size_t N = 0; N < 2 * n - 1; ++N) {\n      CAPTURE(N);\n      const DataVector xi = Spectral::collocation_points<basis, quadrature>(n);\n      CAPTURE(0.5 * (xi + 1.0));\n      for (size_t m = 0; m < N; ++m) {\n        CAPTURE(m);\n        // Note the integer division\n        const size_t spectral_size = (N - m) / 2 + 1;\n        Matrix identity(spectral_size, spectral_size, 0.0);\n        for (size_t i = 0; i < spectral_size; ++i) {\n          identity(i, i) = 1.0;\n        }\n        const Matrix modal_to_nodal =\n            Spectral::modal_to_nodal_matrix<basis, quadrature>(n, m, N);\n        const Matrix nodal_to_modal =\n            Spectral::nodal_to_modal_matrix<basis, quadrature>(n, m, N);\n        // Going from M->N->N is the identity as a matrix\n        CHECK_ITERABLE_CUSTOM_APPROX(nodal_to_modal * modal_to_nodal, identity,\n                                     custom_approx);\n        for (size_t k = m; k <= N; k += 2) {\n          CAPTURE(k);\n          const auto f =\n              Spectral::compute_basis_function_value<basis>(k, m, xi);\n          // Going from N->M->N is the identity operation, but not is not the\n          // identity when viewed as a matrix, so testing bases are invariant\n          CHECK_ITERABLE_CUSTOM_APPROX(\n              apply_matrix(modal_to_nodal, apply_matrix(nodal_to_modal, f)), f,\n              custom_approx);\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_weight_at_upper() {\n  // used in dg::lift_flux()\n  static_assert(Dim == 1 or Dim == 2 or Dim == 3);\n  const Spectral::Quadrature quadrature{Spectral::Quadrature::GaussRadauUpper};\n  constexpr Spectral::Basis basis = Dim == 1   ? Spectral::Basis::ZernikeB1\n                                    : Dim == 2 ? Spectral::Basis::ZernikeB2\n                                               : Spectral::Basis::ZernikeB3;\n  CAPTURE(basis);\n  auto weight_at_upper = [](const size_t num_points) {\n    if constexpr (Dim == 1) {\n      return 1. / static_cast<double>(num_points * (2 * num_points - 1));\n    } else if constexpr (Dim == 2) {\n      return 1. / static_cast<double>(2 * square(num_points));\n    } else {\n      return 1. / static_cast<double>(num_points * (2 * num_points + 1));\n    }\n  };\n\n  for (size_t n = 2; n < Spectral::maximum_number_of_points<basis>; ++n) {\n    CAPTURE(n);\n    const DataVector& weights =\n        Spectral::quadrature_weights<basis, quadrature>(n);\n    CHECK(weights[n - 1] == approx(weight_at_upper(n)));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Numerical.Spectral.ZernikeGaussRadauUpper.PointsAndWeights\",\n    \"[NumericalAlgorithms][Spectral][Unit]\") {\n  // Being unaware of tabulated points and weights for Zernike\n  // basis functions specifically with Gauss-Radau quadrature, we\n  // test whether the basis can correctly take the derivatives of exactly\n  // representable function\n  for (size_t i = 1;\n       i <= Spectral::maximum_number_of_points<Spectral::Basis::ZernikeB2>;\n       ++i) {\n    test_derivatives<1>(i);\n    test_derivatives<2>(i);\n    test_derivatives<3>(i);\n  }\n  test_weight_at_upper<1>();\n  test_weight_at_upper<2>();\n  test_weight_at_upper<3>();\n}\n\n// [[TimeOut, 30]]\nSPECTRE_TEST_CASE(\n    \"Unit.Numerical.Spectral.ZernikeGaussRadauUpper.SpectralTransforms\",\n    \"[NumericalAlgorithms][Spectral][Unit]\") {\n  test_transform_identity<1>();\n  test_transform_identity<2>();\n  test_transform_identity<3>();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_SphericalHarmonics\")\n\nset(LIBRARY_SOURCES\n  Test_AngularOrdering.cpp\n  Test_ChangeCenterOfStrahlkorper.cpp\n  Test_RealSphericalHarmonics.cpp\n  Test_Spherepack.cpp\n  Test_SpherepackIterator.cpp\n  Test_Strahlkorper.cpp\n  Test_StrahlkorperDataBox.cpp\n  Test_StrahlkorperFunctions.cpp\n  Test_Tags.cpp\n  Test_TensorYlmCartToSphere.cpp\n  Test_TensorYlmFilter.cpp\n  Test_TensorYlmSphereToCart.cpp\n  Test_WignerThreeJ.cpp\n  Test_YlmToStf.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  GrSurfaces\n  H5\n  RootFinding\n  SphericalHarmonics\n  SphericalHarmonicsHelpers\n  SphericalHarmonicsIO\n  SpinWeightedSphericalHarmonics\n  Utilities\n  )\n\nadd_subdirectory(IO)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/IO/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_SphericalHarmonicsIO\")\n\nset(LIBRARY_SOURCES\n  Test_FillYlmLegendAndData.cpp\n  Test_ReadSurfaceYlm.cpp\n  Test_StrahlkorperCoordsToTextFile.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  H5\n  ParallelInterpolation\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/IO/Test_FillYlmLegendAndData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <typename Generator>\nvoid test(const gsl::not_null<Generator*> generator) {\n  std::vector<std::string> legend{};\n  std::vector<double> data{};\n  const size_t strahlkorper_l_max = 6;\n  const size_t larger_l_max = strahlkorper_l_max + 2;\n  const double time = 6.7;\n\n  std::uniform_real_distribution<double> dist{0.1, 2.0};\n\n  const size_t radii_size =\n      ylm::Spherepack::physical_size(strahlkorper_l_max, strahlkorper_l_max);\n  auto radii = make_with_random_values<DataVector>(\n      generator, make_not_null(&dist),\n      ModalVector{radii_size, std::numeric_limits<double>::signaling_NaN()});\n\n  ylm::Strahlkorper<Frame::Inertial> strahlkorper{\n      strahlkorper_l_max, strahlkorper_l_max, radii, std::array{0.9, 0.8, 0.7}};\n  ylm::SpherepackIterator iter{strahlkorper_l_max, strahlkorper_l_max};\n  std::vector<double> expected_coefs{};\n  for (size_t l = 0; l <= strahlkorper_l_max; l++) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n      expected_coefs.emplace_back(\n          strahlkorper.coefficients()[iter.set(l, m)()]);\n    }\n  }\n\n  // Where max_l is the same as strahlkorper.l_max()\n  {\n    ylm::fill_ylm_legend_and_data(make_not_null(&legend), make_not_null(&data),\n                                  strahlkorper, time, strahlkorper_l_max);\n\n    // +5 for time, l_max, and 3 components of the center\n    const size_t expected_size = expected_coefs.size() + 5;\n    CHECK(legend.size() == expected_size);\n    CHECK(data.size() == expected_size);\n    CHECK(data[0] == time);\n    for (size_t i = 0; i < 3; i++) {\n      CHECK(data[i + 1] == gsl::at(strahlkorper.expansion_center(), i));\n    }\n    CHECK(data[4] == strahlkorper_l_max);\n    for (size_t i = 5; i < expected_size; i++) {\n      CHECK(data[i] == expected_coefs[i - 5]);\n    }\n  }\n\n  legend.clear();\n  data.clear();\n  iter.reset();\n\n  // Where max_l is larger than strahlkorper.l_max()\n  {\n    std::vector<std::string> compare_legend{};\n    std::vector<double> compare_data{};\n    ylm::fill_ylm_legend_and_data(make_not_null(&legend), make_not_null(&data),\n                                  strahlkorper, time, larger_l_max);\n    ylm::fill_ylm_legend_and_data(make_not_null(&compare_legend),\n                                  make_not_null(&compare_data),\n                                  ylm::Strahlkorper<Frame::Inertial>{\n                                      larger_l_max, larger_l_max, strahlkorper},\n                                  time, larger_l_max);\n    CHECK(compare_legend == legend);\n    // This is the only thing that should be different so we just check this\n    // specifically first, then overwrite it and check the rest of the vector\n    CHECK(data[4] == static_cast<double>(strahlkorper_l_max));\n    CHECK(compare_data[4] == static_cast<double>(larger_l_max));\n    data[4] = static_cast<double>(larger_l_max);\n    CHECK(compare_data == data);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.SphericalHarmonics.IO.FillYlmLegendAndData\",\n    \"[Domain][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test(make_not_null(&generator));\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/IO/Test_ReadSurfaceYlm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/ReadSurfaceYlm.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveSurfaceData.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nconst size_t l_max_column_number = 4;\n\n// generate test Strahlkorpers with a random radius function and given l_maxes\n// and expansion center\ntemplate <typename Frame, size_t NumTimes>\nstd::array<ylm::Strahlkorper<Frame>, NumTimes> generate_test_strahlkorpers(\n    const std::array<double, 3> expansion_center,\n    const std::array<size_t, NumTimes> l_maxes) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.1, 2.0);\n\n  std::array<ylm::Strahlkorper<Frame>, NumTimes> strahlkorpers{};\n  for (size_t i = 0; i < NumTimes; i++) {\n    const auto radius = make_with_random_values<DataVector>(\n        make_not_null(&generator), distribution,\n        DataVector(ylm::Spherepack::physical_size(gsl::at(l_maxes, i),\n                                                  gsl::at(l_maxes, i)),\n                   std::numeric_limits<double>::signaling_NaN()));\n    gsl::at(strahlkorpers, i) = ylm::Strahlkorper<Frame>(\n        gsl::at(l_maxes, i), gsl::at(l_maxes, i), radius, expansion_center);\n  }\n\n  return strahlkorpers;\n}\n\n// Generate the data to be written using the helper function for\n// ::intrp::callbacks::ObserveSurfaceData instead of spinning up the Action\n// Testing Framework (AFT) to write it since the helper function handles all\n// of the legend and data building logic and ObserveSurfaceData simply writes\n// what it generates\ntemplate <typename Frame, size_t NumTimes>\nvoid write_test_strahlkorpers(\n    const std::array<ylm::Strahlkorper<Frame>, NumTimes>& strahlkorpers,\n    const std::string& test_filename, const std::string& subfile_name,\n    const std::array<double, NumTimes> times, const size_t max_l) {\n  std::vector<std::string> legend{};\n  std::vector<std::vector<double>> data(NumTimes);\n  for (size_t i = 0; i < NumTimes; i++) {\n    legend.resize(0);  // clear and reuse for next row of data\n    ylm::fill_ylm_legend_and_data(\n        make_not_null(&legend), make_not_null(&(data[i])),\n        gsl::at(strahlkorpers, i), gsl::at(times, i), max_l);\n  }\n\n  h5::H5File<h5::AccessType::ReadWrite> test_file(test_filename, true);\n  auto& file = test_file.insert<h5::Dat>(\"/\" + subfile_name, legend);\n  file.append(data);\n  test_file.close_current_object();\n}\n\n// test reading in the n last times of data expected to have been written\n// (expected_strahlkorpers), where n = num_times_requested\ntemplate <typename Frame, size_t NumTimes>\nvoid check_read_ylm_data(const std::string& test_filename,\n                         const std::string& surface_subfile_name,\n                         const size_t num_times_requested,\n                         const std::array<ylm::Strahlkorper<Frame>, NumTimes>&\n                             expected_strahlkorpers) {\n  ASSERT(\n      NumTimes >= num_times_requested,\n      \"Requesting to read more rows of Ylm test data than the total number of \"\n      \"rows expected to be written and therefore able to be read.\");\n\n  const std::vector<ylm::Strahlkorper<Frame>> strahlkorpers =\n      ylm::read_surface_ylm<Frame>(test_filename, surface_subfile_name,\n                                   num_times_requested);\n  CHECK(strahlkorpers.size() == num_times_requested);\n\n  // check n last times where n = num_times_requested\n  for (size_t i = 0, expected_row_number = NumTimes - num_times_requested;\n       i < num_times_requested; i++, expected_row_number++) {\n    const auto& expected_strahlkorper =\n        gsl::at(expected_strahlkorpers, expected_row_number);\n    const std::array<double, 3> expected_expansion_center =\n        expected_strahlkorper.expansion_center();\n    const size_t expected_l_max = expected_strahlkorper.l_max();\n    const DataVector& expected_spectral_coefficients =\n        expected_strahlkorper.coefficients();\n    const size_t expected_spectral_size = expected_spectral_coefficients.size();\n\n    const auto& strahlkorper = strahlkorpers[i];\n    CHECK(strahlkorper.expansion_center() == expected_expansion_center);\n    CHECK(strahlkorper.l_max() == expected_l_max);\n    CHECK(strahlkorper.coefficients().size() == expected_spectral_size);\n    CHECK(strahlkorper.coefficients() == expected_spectral_coefficients);\n  }\n}\n\n// Write and read in a file containing a legend or data that is expected to\n// generate an error upon attempting to read\ntemplate <typename Frame>\nvoid write_error_file_and_try_to_read(\n    const std::string& filename, const std::string& subfile_name,\n    const std::vector<std::string>& legend,\n    const std::vector<std::vector<double>>& data,\n    const size_t num_times_to_read) {\n  h5::H5File<h5::AccessType::ReadWrite> test_file{filename, true};\n  auto& file = test_file.insert<h5::Dat>(\"/\" + subfile_name, legend);\n  file.append(data);\n  test_file.close_current_object();\n\n  const auto strahlkorpers =\n      ylm::read_surface_ylm<Frame>(filename, subfile_name, num_times_to_read);\n}\n\nvoid test_errors() {\n  const std::vector<std::string> ylm_legend_without_coefs{\n      \"Time\", \"InertialExpansionCenter_x\", \"InertialExpansionCenter_y\",\n      \"InertialExpansionCenter_z\", \"Lmax\"};\n\n  const std::vector<std::string> l_max_3_coef_headers = {\n      \"coef(0,0)\",  \"coef(1,-1)\", \"coef(1,0)\",  \"coef(1,1)\",\n      \"coef(2,-2)\", \"coef(2,-1)\", \"coef(2,0)\",  \"coef(2,1)\",\n      \"coef(2,2)\",  \"coef(3,-3)\", \"coef(3,-2)\", \"coef(3,-1)\",\n      \"coef(3,0)\",  \"coef(3,1)\",  \"coef(3,2)\",  \"coef(3,3)\"};\n\n  // construct a legend that is properly formatted\n  std::vector<std::string> good_legend = ylm_legend_without_coefs;\n  good_legend.insert(good_legend.end(), l_max_3_coef_headers.begin(),\n                     l_max_3_coef_headers.end());\n\n  // construct data that is properly formatted\n  const std::array<double, 3> times{{0.1, 0.2, 0.3}};\n  const std::array<double, 3> l_maxes{{2.0, 3.0, 2.0}};\n  std::vector<std::vector<double>> good_data{\n      times.size(), std::vector<double>(good_legend.size(), 0.0)};\n  good_data[0][l_max_column_number] = gsl::at(l_maxes, 0);\n  good_data[1][l_max_column_number] = gsl::at(l_maxes, 0);\n  good_data[2][l_max_column_number] = gsl::at(l_maxes, 0);\n\n  const std::string filename{\"TestReadYlmErrors.h5\"};\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n\n  // expansion center legend errors\n\n  CHECK_THROWS_WITH(([&filename, &good_legend, &good_data]() {\n                      std::vector<std::string> bad_legend = good_legend;\n                      bad_legend[2] = \"InertialExpansionCenter_z\";\n\n                      write_error_file_and_try_to_read<Frame::Inertial>(\n                          filename, \"WrongCoord\", bad_legend, good_data, 3);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"In column 2 of the Ylm legend, expected header \"\n                        \"InertialExpansionCenter_y but got header \"\n                        \"InertialExpansionCenter_z\"));\n\n  CHECK_THROWS_WITH(\n      ([&filename, &good_legend, &good_data]() {\n        std::vector<std::string> bad_legend = good_legend;\n        bad_legend[3] = \"ExpansionCenter_z\";\n\n        write_error_file_and_try_to_read<Frame::Inertial>(\n            filename, \"NoFrame\", bad_legend, good_data, 3);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"In column 3 of the Ylm legend, expected header \"\n          \"InertialExpansionCenter_z but got header ExpansionCenter_z\"));\n\n  CHECK_THROWS_WITH(([&filename, &good_legend, &good_data]() {\n                      write_error_file_and_try_to_read<Frame::Grid>(\n                          filename, \"WrongFrame\", good_legend, good_data, 3);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"In column 1 of the Ylm legend, expected header \"\n                        \"GridExpansionCenter_x but got header \"\n                        \"InertialExpansionCenter_x\"));\n\n  // coefficient legend errors\n\n  CHECK_THROWS_WITH(\n      ([&filename, &good_legend, &good_data]() {\n        std::vector<std::string> bad_legend = good_legend;\n        bad_legend[8] = \"1, 1\";\n\n        write_error_file_and_try_to_read<Frame::Inertial>(\n            filename, \"WrongCoefFormat\", bad_legend, good_data, 3);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"In column 8 of the Ylm legend, expected header coef(1,1) but got \"\n          \"header 1, 1\"));\n\n  CHECK_THROWS_WITH(([&filename, &good_legend, &good_data]() {\n                      std::vector<std::string> bad_legend = good_legend;\n                      bad_legend[12] = \"coef(2,2)\";\n\n                      write_error_file_and_try_to_read<Frame::Inertial>(\n                          filename, \"WrongCoefOrder\", bad_legend, good_data, 3);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"In column 12 of the Ylm legend, expected header \"\n                        \"coef(2,1) but got header coef(2,2)\"));\n\n  // data errors\n\n  CHECK_THROWS_WITH(([&filename, &good_legend]() {\n                      const std::vector<std::vector<double>> bad_data{};\n                      write_error_file_and_try_to_read<Frame::Inertial>(\n                          filename, \"EmptyData\", good_legend, bad_data, 1);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The Ylm data to read from contain 0 rows\"));\n\n  CHECK_THROWS_WITH(([&filename, &good_legend, &good_data]() {\n                      write_error_file_and_try_to_read<Frame::Inertial>(\n                          filename, \"NotEnoughTimesWritten\", good_legend,\n                          good_data, 4);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The requested number of time values (4) is more than \"\n                        \"the number of rows in the Ylm data\"));\n\n  CHECK_THROWS_WITH(([&filename, &good_legend, &good_data]() {\n                      std::vector<std::vector<double>> bad_data = good_data;\n                      bad_data[0][l_max_column_number] = 1.2;\n\n                      write_error_file_and_try_to_read<Frame::Inertial>(\n                          filename, \"NonIntegralLmax\", good_legend, bad_data,\n                          3);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Row 0 of the Ylm data has an invalid Lmax value\"));\n\n  CHECK_THROWS_WITH(([&filename, &good_legend, &good_data]() {\n                      std::vector<std::vector<double>> bad_data = good_data;\n                      bad_data[1][l_max_column_number] = -1.0;\n\n                      write_error_file_and_try_to_read<Frame::Inertial>(\n                          filename, \"NegativeLmax\", good_legend, bad_data, 3);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Row 1 of the Ylm data has an invalid Lmax value\"));\n\n  CHECK_THROWS_WITH(([&filename, &good_legend, &good_data]() {\n                      std::vector<std::vector<double>> bad_data = good_data;\n                      // set an Lmax that requires more columns of data than\n                      // given, i.e. `good_legend` only has room for coefs for\n                      // l <= 3\n                      bad_data[2][l_max_column_number] = 4;\n\n                      write_error_file_and_try_to_read<Frame::Inertial>(\n                          filename, \"NotEnoughColumnsForLmax\", good_legend,\n                          bad_data, 3);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Row 2 of the Ylm data does not have enough \"\n                        \"coefficients for the Lmax. For Lmax = 4, expected at \"\n                        \"least 25 coefficient columns and 30 total columns\"));\n\n  CHECK_THROWS_WITH(\n      ([&filename, &good_legend, &good_data]() {\n        std::vector<std::vector<double>> bad_data = good_data;\n        bad_data[0].back() = 0.8;\n\n        write_error_file_and_try_to_read<Frame::Inertial>(\n            filename, \"NonZeroHigherCoefs\", good_legend, bad_data, 3);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Row 0 of the Ylm data has Lmax = 2 but non-zero coefficients for \"\n          \"l > Lmax\"));\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n\nvoid test_single_time(const std::string& test_filename) {\n  INFO(\"Test Single Time\");\n\n  using frame = Frame::Inertial;\n  const std::string subfile_name = \"SurfaceA_SingleTime_Ylm\";\n  const std::array<double, 3> expansion_center{{-0.5, -0.1, 0.3}};\n  const size_t max_l = 3;\n\n  constexpr size_t number_of_times = 3;\n  const std::array<double, number_of_times> written_times{{0.0, 0.1, 0.2}};\n  const std::array<size_t, number_of_times> l_maxes{{3, 3, 3}};\n\n  const auto expected_strahlkorpers =\n      generate_test_strahlkorpers<frame>(expansion_center, l_maxes);\n  write_test_strahlkorpers(expected_strahlkorpers, test_filename, subfile_name,\n                           written_times, max_l);\n\n  {\n    const ylm::Strahlkorper<frame> read_in_strahlkorper =\n        ylm::read_surface_ylm_single_time<frame>(test_filename, subfile_name,\n                                                 0.1, 1e-12);\n\n    CHECK(expected_strahlkorpers[1] == read_in_strahlkorper);\n  }\n\n  {\n    const ylm::Strahlkorper<frame> read_in_strahlkorper =\n        ylm::read_surface_ylm_single_time<frame>(test_filename, subfile_name,\n                                                 0.2, 1e-2);\n\n    CHECK(expected_strahlkorpers[2] == read_in_strahlkorper);\n  }\n\n  {\n    const ylm::Strahlkorper<Frame::Grid> read_in_strahlkorper =\n        ylm::read_surface_ylm_single_time<Frame::Grid>(\n            test_filename, subfile_name, 0.2, 1e-2, false);\n\n    // Just check the coefficients since == won't work with different frames\n    CHECK(expected_strahlkorpers[2].coefficients() ==\n          read_in_strahlkorper.coefficients());\n  }\n\n  CHECK_THROWS_WITH(\n      ylm::read_surface_ylm_single_time<frame>(test_filename, subfile_name, 0.3,\n                                               1e-12),\n      Catch::Matchers::ContainsSubstring(\"Could not find time\") and\n          Catch::Matchers::ContainsSubstring(\"Available times are:\"));\n\n  CHECK_THROWS_WITH(ylm::read_surface_ylm_single_time<frame>(\n                        test_filename, subfile_name, 0.2, 1.0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Found more than one time in the subfile\") and\n                        Catch::Matchers::ContainsSubstring(\n                            \"that is within a relative_epsilon of\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.ReadSurfaceYlm\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test_errors();\n\n  // Temporary file with test data to read in\n  const std::string test_filename{\"TestReadYlm.h5\"};\n  if (file_system::check_if_file_exists(test_filename)) {\n    file_system::rm(test_filename, true);\n  }\n\n  {\n    INFO(\"Test A\");\n\n    // first test surface\n    using frame_a = Frame::Grid;\n    const std::string subfile_name_a = \"SurfaceA_Ylm\";\n    const std::array<double, 3> expansion_center_a{{-0.5, -0.1, 0.3}};\n    const size_t max_l_a = 3;\n\n    constexpr size_t number_of_times_a = 3;\n    const std::array<double, number_of_times_a> written_times_a{\n        {0.0, 0.1, 0.2}};\n    // test that a mix of different l_max values can be read from the same\n    // subfile\n    const std::array<size_t, number_of_times_a> l_maxes_a{{3, 2, 3}};\n\n    const auto strahlkorpers_a =\n        generate_test_strahlkorpers<frame_a>(expansion_center_a, l_maxes_a);\n    write_test_strahlkorpers(strahlkorpers_a, test_filename, subfile_name_a,\n                             written_times_a, max_l_a);\n\n    check_read_ylm_data<frame_a>(test_filename, subfile_name_a, 1,\n                                 strahlkorpers_a);\n    check_read_ylm_data<frame_a>(test_filename, subfile_name_a, 2,\n                                 strahlkorpers_a);\n    check_read_ylm_data<frame_a>(test_filename, subfile_name_a, 3,\n                                 strahlkorpers_a);\n  }\n\n  {\n    INFO(\"Test B\");\n\n    // second test surface\n    using frame_b = Frame::Distorted;\n    const std::string subfile_name_b = \"SurfaceB\";\n    const std::array<double, 3> expansion_center_b{{0.0, 0.2, -0.6}};\n    const size_t max_l_b = 4;\n\n    // edge case: only one row written\n    constexpr size_t number_of_times_b = 1;\n    const std::array<double, number_of_times_b> written_times_b{{0.7}};\n    const std::array<size_t, number_of_times_b> l_maxes_b{{3}};\n\n    const auto strahlkorpers_b =\n        generate_test_strahlkorpers<frame_b>(expansion_center_b, l_maxes_b);\n    write_test_strahlkorpers(strahlkorpers_b, test_filename, subfile_name_b,\n                             written_times_b, max_l_b);\n\n    check_read_ylm_data<frame_b>(test_filename, subfile_name_b, 1,\n                                 strahlkorpers_b);\n  }\n\n  test_single_time(test_filename);\n\n  // Delete the temporary file created for this test\n  file_system::rm(test_filename, true);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/IO/Test_StrahlkorperCoordsToTextFile.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <fstream>\n#include <istream>\n#include <sstream>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Transpose.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/StrahlkorperCoordsToTextFile.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace {\nstd::array<DataVector, 3> read_text_file(const std::string& filename) {\n  std::array<std::vector<double>, 3> vector_result{};\n  std::ifstream file(filename);\n  if (not file.is_open()) {\n    ERROR(\"Unable to open text file \" << filename);\n  }\n  std::string line{};\n  double value = 0.0;\n\n  while (std::getline(file, line)) {\n    std::stringstream ss(line);\n    for (size_t i = 0; i < 3; i++) {\n      ss >> value;\n      gsl::at(vector_result, i).push_back(value);\n    }\n  }\n\n  const size_t num_points = vector_result[0].size();\n\n  std::array<DataVector, 3> result{\n      DataVector{num_points}, DataVector{num_points}, DataVector{num_points}};\n\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < num_points; j++) {\n      gsl::at(result, i)[j] = gsl::at(vector_result, i)[j];\n    }\n  }\n\n  return result;\n}\n\nvoid test(const ylm::AngularOrdering ordering) {\n  const std::string filename{\"StrahlkorperCoords.txt\"};\n  const double radius = 1.5;\n  const size_t l_max = 16;\n  const std::array<double, 3> center{-0.1, -0.2, -0.3};\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper{l_max, radius, center};\n\n  tnsr::I<DataVector, 3, Frame::Inertial> expected_points =\n      ylm::cartesian_coords(strahlkorper);\n  if (ordering == ylm::AngularOrdering::Cce) {\n    const auto physical_extents =\n        strahlkorper.ylm_spherepack().physical_extents();\n    auto transpose_expected_points =\n        tnsr::I<DataVector, 3, Frame::Inertial>(get<0>(expected_points).size());\n    for (size_t i = 0; i < 3; ++i) {\n      transpose(make_not_null(&transpose_expected_points.get(i)),\n                expected_points.get(i), physical_extents[0],\n                physical_extents[1]);\n    }\n\n    expected_points = std::move(transpose_expected_points);\n  }\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n\n  {\n    ylm::write_strahlkorper_coords_to_text_file(strahlkorper, filename,\n                                                ordering);\n\n    std::array<DataVector, 3> points_from_file = read_text_file(filename);\n\n    for (size_t i = 0; i < 3; i++) {\n      CHECK(expected_points.get(i) == gsl::at(points_from_file, i));\n    }\n\n    CHECK_THROWS_WITH((ylm::write_strahlkorper_coords_to_text_file(\n                          strahlkorper, filename, ordering)),\n                      Catch::Matchers::ContainsSubstring(\n                          \"The output file \" + filename + \" already exists\"));\n\n    ylm::write_strahlkorper_coords_to_text_file(strahlkorper, filename,\n                                                ordering, true);\n\n    for (size_t i = 0; i < 3; i++) {\n      CHECK(expected_points.get(i) == gsl::at(points_from_file, i));\n    }\n  }\n\n  {\n    ylm::write_strahlkorper_coords_to_text_file(radius, l_max, center, filename,\n                                                ordering, true);\n\n    const std::array<DataVector, 3> points_from_file = read_text_file(filename);\n\n    for (size_t i = 0; i < 3; i++) {\n      CHECK(expected_points.get(i) == gsl::at(points_from_file, i));\n    }\n  }\n\n  if (file_system::check_if_file_exists(filename)) {\n    file_system::rm(filename, true);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.StrahlkorperCoordsToTextFile\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test(ylm::AngularOrdering::Strahlkorper);\n  test(ylm::AngularOrdering::Cce);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Ylm.Python.Strahlkorper\"\n  Test_Strahlkorper.py\n  \"Unit;Python\"\n  PySphericalHarmonics)\n\n  spectre_add_python_bindings_test(\n  \"Unit.Ylm.Python.Spherepack\"\n  Test_Spherepack.py\n  \"Unit;Python\"\n  PySphericalHarmonics)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Python/Test_Spherepack.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\nimport random\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\ntry:\n    from scipy.special import sph_harm_y\nexcept ImportError:\n    # SciPy < 1.15\n    from scipy.special import sph_harm\n\n    def sph_harm_y(n, m, theta, phi):\n        return sph_harm(m, n, phi, theta)\n\n\nfrom spectre.SphericalHarmonics import Spherepack, SpherepackIterator\n\n\ndef convert_coef_to_spherepack(complex_coef, l, m):\n    \"\"\"\n    converts spherical harmonic coefficients to spherepack coefficients,\n    see https://spectre-code.org/classylm_1_1Spherepack.html\n    \"\"\"\n    sign = 1.0 if m % 2 == 0 else -1.0\n    part = np.real(complex_coef) if m >= 0 else np.imag(complex_coef)\n    return sign * np.sqrt(2.0 / np.pi) * part\n\n\nclass TestStrahlkorper(unittest.TestCase):\n    def test_sizes(self):\n        for l in range(2, 6):\n            for m in range(2, l + 1):\n                spherepack = Spherepack(l, m)\n                self.assertEqual(\n                    spherepack.physical_size, (l + 1) * (2 * m + 1)\n                )\n                self.assertEqual(\n                    spherepack.spectral_size, 2 * (l + 1) * (m + 1)\n                )\n\n    def test_spec_to_phys_and_back(self):\n        l_max = random.randint(2, 12)\n        m_max = random.randint(2, l_max)\n        spherepack = Spherepack(l_max, m_max)\n        thetas, phis = spherepack.theta_phi_points\n        iterator = SpherepackIterator(l_max, m_max)\n        spherepack_coefs = np.zeros(spherepack.spectral_size)\n        scipy_collocation_values = np.zeros(\n            np.shape(thetas), dtype=np.complex128\n        )\n\n        for l in range(0, l_max + 1):\n            for m in range(0, min(l + 1, m_max + 1)):\n                coef = random.uniform(-1.0, 1.0)\n                if m > 0:\n                    coef += random.uniform(-1.0, 1.0) * 1j\n                    iterator.set(l, -m)\n                    spherepack_coefs[iterator()] = convert_coef_to_spherepack(\n                        coef, l, -m\n                    )\n                    # we are checking for a real field, so for the\n                    # conjugate: a*(l,m) = (-1)^m a(l,m)\n                    sign = 1.0 if m % 2 == 0 else -1.0\n                    scipy_collocation_values += (\n                        sign\n                        * coef.conjugate()\n                        * sph_harm_y(l, -m, thetas, phis)\n                    )\n\n                iterator.set(l, m)\n                spherepack_coefs[iterator()] = convert_coef_to_spherepack(\n                    coef, l, m\n                )\n                scipy_collocation_values += coef * sph_harm_y(\n                    l, m, thetas, phis\n                )\n\n        npt.assert_array_almost_equal(\n            np.zeros_like(thetas), np.imag(scipy_collocation_values)\n        )\n        collocation_values = np.asarray(\n            spherepack.spec_to_phys(spherepack_coefs)\n        )\n        npt.assert_array_almost_equal(\n            collocation_values, np.real(scipy_collocation_values)\n        )\n\n        recovered_coefs = np.asarray(\n            spherepack.phys_to_spec(collocation_values)\n        )\n        npt.assert_array_almost_equal(\n            recovered_coefs, np.asarray(spherepack_coefs)\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Python/Test_Strahlkorper.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.Informer as spectre_informer\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.SphericalHarmonics import (\n    AngularOrdering,\n    Frame,\n    Strahlkorper,\n    cartesian_coords,\n    power_monitor,\n    read_surface_ylm,\n    read_surface_ylm_single_time,\n    write_sphere_of_points_to_text_file,\n    ylm_legend_and_data,\n)\n\n\nclass TestStrahlkorper(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            spectre_informer.unit_test_build_path(),\n            \"NumericalAlgorithms/Strahlkorper/Python\",\n        )\n        self.filename = os.path.join(self.test_dir, \"Strahlkorper.h5\")\n        self.text_filename = os.path.join(\n            self.test_dir, \"PyStrahlkorperCoords.txt\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_strahlkorper(self):\n        strahlkorper = Strahlkorper[Frame.Inertial](\n            l_max=12, radius=1.0, center=[0.0, 0.0, 0.0]\n        )\n        self.assertEqual(strahlkorper.l_max, 12)\n        self.assertEqual(strahlkorper.m_max, 12)\n        self.assertEqual(strahlkorper.physical_extents, [13, 25])\n        self.assertEqual(strahlkorper.expansion_center, [0.0, 0.0, 0.0])\n        self.assertEqual(strahlkorper.physical_center, [0.0, 0.0, 0.0])\n        self.assertAlmostEqual(strahlkorper.average_radius, 1.0)\n        self.assertAlmostEqual(strahlkorper.radius(0.0, 0.0), 1.0)\n        self.assertTrue(strahlkorper.point_is_contained([0.5, 0.0, 0.0]))\n        x = np.array(cartesian_coords(strahlkorper))\n        r = np.linalg.norm(x, axis=0)\n        npt.assert_allclose(r, 1.0)\n\n        power = np.array(power_monitor(strahlkorper))\n        for i, power_in_mode in enumerate(power):\n            if i > 0:\n                self.assertAlmostEqual(power_in_mode, 0.0)\n            else:\n                self.assertAlmostEqual(power_in_mode, 2.0 * np.sqrt(2.0))\n\n        legend, ylm_data = ylm_legend_and_data(strahlkorper, 1.0, 12)\n        self.assertEqual(len(legend), 174)\n        self.assertEqual(\n            legend[:7],\n            [\n                \"Time\",\n                \"InertialExpansionCenter_x\",\n                \"InertialExpansionCenter_y\",\n                \"InertialExpansionCenter_z\",\n                \"Lmax\",\n                \"coef(0,0)\",\n                \"coef(1,-1)\",\n            ],\n        )\n        self.assertEqual(ylm_data[:5], [1.0, 0.0, 0.0, 0.0, 12.0])\n\n        with spectre_h5.H5File(self.filename, \"w\") as h5file:\n            datfile = h5file.insert_dat(\n                \"/Strahlkorper\", legend=legend, version=0\n            )\n            datfile.append(ylm_data)\n\n        self.assertEqual(\n            read_surface_ylm(self.filename, \"Strahlkorper\", 1)[0], strahlkorper\n        )\n        self.assertEqual(\n            read_surface_ylm_single_time(\n                self.filename, \"Strahlkorper\", 1.0, 0.0, True\n            ),\n            strahlkorper,\n        )\n\n        l_max = 4\n        print(\"hello\")\n        # First write with wrong l_max\n        write_sphere_of_points_to_text_file(\n            radius=1.2,\n            l_max=l_max - 1,\n            center=[-0.1, -0.2, -0.3],\n            output_file_name=self.text_filename,\n            ordering=AngularOrdering.Cce,\n        )\n        # Test that if overwrite_file = False (the default) an exception is\n        # raised\n        self.assertRaises(\n            RuntimeError,\n            write_sphere_of_points_to_text_file,\n            radius=1.2,\n            l_max=l_max - 1,\n            center=[-0.1, -0.2, -0.3],\n            output_file_name=self.text_filename,\n            ordering=AngularOrdering.Cce,\n        )\n        # Finally write the correct l_max so we can check that overwrite_file\n        # works\n        write_sphere_of_points_to_text_file(\n            radius=1.2,\n            l_max=l_max,\n            center=[-0.1, -0.2, -0.3],\n            output_file_name=self.text_filename,\n            ordering=AngularOrdering.Cce,\n            overwrite_file=True,\n        )\n\n        with open(self.text_filename, \"r\") as text_file:\n            # Physical size of ylm::Spherepack\n            num_points = (l_max + 1) * (2 * l_max + 1)\n            all_lines = text_file.readlines()\n            self.assertEqual(num_points, len(all_lines))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_AngularOrdering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace ylm {\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.SphericalHarmonics.AngularOrdering\",\n                  \"[Unit]\") {\n  CHECK(get_output(AngularOrdering::Cce) == \"Cce\");\n  CHECK(get_output(AngularOrdering::Strahlkorper) == \"Strahlkorper\");\n  CHECK(TestHelpers::test_creation<AngularOrdering>(\"Cce\") ==\n        AngularOrdering::Cce);\n  CHECK(TestHelpers::test_creation<AngularOrdering>(\"Strahlkorper\") ==\n        AngularOrdering::Strahlkorper);\n}\n}  // namespace ylm\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_ChangeCenterOfStrahlkorper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <random>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/ChangeCenterOfStrahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nnamespace ylm {\nnamespace {\n\nStrahlkorper<Frame::Inertial> make_strahlkorper() {\n  const std::array<double, 3> center{{0.1, 0.2, 0.3}};\n  const size_t l_max = 18;\n  const double avg_radius = 2.0;\n  const double delta_radius = 0.1;\n\n  std::uniform_real_distribution<double> interval_dis(-1.0, 1.0);\n  MAKE_GENERATOR(gen);\n\n  // First make a sphere.\n  Strahlkorper<Frame::Inertial> sk(l_max, l_max, avg_radius, center);\n  // Now adjust the coefficients randomly, but in a manner so that\n  // coefficients decay like exp(-l).\n  auto coefs = sk.coefficients();\n  for (SpherepackIterator it(l_max, l_max); it; ++it) {\n    coefs[it()] +=\n        interval_dis(gen) * delta_radius * exp(-static_cast<double>(it.l()));\n  }\n  return Strahlkorper<Frame::Inertial>(coefs, sk);\n}\n\nvoid test_change_center_of_strahlkorper() {\n  auto strahlkorper = make_strahlkorper();\n  const auto original_strahlkorper = strahlkorper;\n  const auto original_physical_center = strahlkorper.physical_center();\n\n  const std::array<double, 3> new_center{{0.12, 0.21, 0.33}};\n  change_expansion_center_of_strahlkorper(make_not_null(&strahlkorper),\n                                          new_center);\n\n  // Center should have changed\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(gsl::at(new_center, i) ==\n          gsl::at(strahlkorper.expansion_center(), i));\n  }\n\n  // Physical center should not change (to lowest order only, since physical\n  // center is only computed to l=1.  Therefore we use a custom approx here.)\n  const auto new_physical_center = strahlkorper.physical_center();\n  Approx custom_approx_three = Approx::custom().epsilon(1.e-3).scale(1.);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(custom_approx_three(gsl::at(original_physical_center, i)) ==\n          gsl::at(new_physical_center, i));\n  }\n\n  // Now transform back\n  change_expansion_center_of_strahlkorper(\n      make_not_null(&strahlkorper), original_strahlkorper.expansion_center());\n\n  // The original center and radii should have been restored.\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(gsl::at(original_strahlkorper.expansion_center(), i) ==\n          gsl::at(strahlkorper.expansion_center(), i));\n  }\n  // Radii are not the same to machine precision, but only to the\n  // specified l_max.  If l_max is changed at the top of this file, then the\n  // approx below may need to change.\n  Approx custom_approx_nine = Approx::custom().epsilon(1.e-9).scale(1.);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      strahlkorper.ylm_spherepack().spec_to_phys(strahlkorper.coefficients()),\n      original_strahlkorper.ylm_spherepack().spec_to_phys(\n          original_strahlkorper.coefficients()),\n      custom_approx_nine);\n}\n\nvoid test_change_center_of_strahlkorper_to_physical() {\n  auto strahlkorper = make_strahlkorper();\n\n  change_expansion_center_of_strahlkorper_to_physical(\n      make_not_null(&strahlkorper));\n\n  auto new_physical_center = strahlkorper.physical_center();\n  auto new_center = strahlkorper.expansion_center();\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(approx(gsl::at(new_physical_center, i)) == gsl::at(new_center, i));\n  }\n\n  // Checking relative tolerance\n  auto strahlkorper_with_higher_tolerance = make_strahlkorper();\n  const double relative_tolerance = 1e-8;\n  const Approx custom_approx =\n      Approx::custom().epsilon(relative_tolerance).scale(10.0);\n  change_expansion_center_of_strahlkorper_to_physical(\n      make_not_null(&strahlkorper_with_higher_tolerance), relative_tolerance);\n  new_physical_center = strahlkorper_with_higher_tolerance.physical_center();\n  new_center = strahlkorper_with_higher_tolerance.expansion_center();\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK_ITERABLE_CUSTOM_APPROX(gsl::at(new_physical_center, i),\n                                 gsl::at(new_center, i), custom_approx);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Strahlkorper.ChangeCenter\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_change_center_of_strahlkorper();\n  test_change_center_of_strahlkorper_to_physical();\n}\n}  // namespace\n}  // namespace ylm\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_RealSphericalHarmonics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n\nnamespace ylm {\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.RealSphericalHarmonics\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  const size_t l_max = 10;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> phi_distribution(0., 2. * M_PI);\n  std::uniform_real_distribution<> theta_distribution(0., M_PI);\n  const size_t num_points = 100;\n  const auto thetas = make_with_random_values<DataVector>(\n      make_not_null(&gen), make_not_null(&theta_distribution),\n      DataVector(num_points));\n  const auto phis = make_with_random_values<DataVector>(\n      make_not_null(&gen), make_not_null(&phi_distribution),\n      DataVector(num_points));\n  Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n\n  for (size_t l = 0; l <= l_max; ++l) {\n    for (int m = -l; m <= static_cast<int>(l); ++m) {\n      const auto spherical_harmonic =\n          real_spherical_harmonic(thetas, phis, l, m);\n      if (m == 0) {\n        const Spectral::Swsh::SpinWeightedSphericalHarmonic\n            complex_spherical_harmonic(0, l, m);\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            real(complex_spherical_harmonic.evaluate(\n                thetas, phis, sin(thetas / 2.), cos(thetas / 2.))),\n            spherical_harmonic, custom_approx);\n      } else if (m > 0) {\n        const Spectral::Swsh::SpinWeightedSphericalHarmonic\n            complex_spherical_harmonic(0, l, m);\n        const int sign = (m % 2 == 0) ? 1 : -1;\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            real(complex_spherical_harmonic.evaluate(\n                thetas, phis, sin(thetas / 2.), cos(thetas / 2.))) *\n                M_SQRT2 * sign,\n            spherical_harmonic, custom_approx);\n      } else {\n        const Spectral::Swsh::SpinWeightedSphericalHarmonic\n            complex_spherical_harmonic(0, l, abs(m));\n        const int sign = (m % 2 == 0) ? 1 : -1;\n        CHECK_ITERABLE_CUSTOM_APPROX(\n            imag(complex_spherical_harmonic.evaluate(\n                thetas, phis, sin(thetas / 2.), cos(thetas / 2.))) *\n                M_SQRT2 * sign,\n            spherical_harmonic, custom_approx);\n      }\n    }\n  }\n}\n}  // namespace ylm\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_Spherepack.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackHelper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace ylm {\nnamespace {\n\n// Since we are sharing this code with users and it is intentionally design to\n// be very C-style, we just exclude the entire block from clang-tidy.\n// NOLINTBEGIN\n\n// [spectre_cce_grid_point_locations]\nstruct SpectreCceGridPointLocations {\n  int number_of_theta_points;\n  int number_of_phi_points;\n  // We use C-style arrays to avoid dealing with malloc/free and new/delete\n  // interoperability between different libraries.\n  double theta_points[33];\n  double phi_points[2 * 33 + 1];\n};\n\nSpectreCceGridPointLocations spectre_ylm_theta_phi_points(const int l_max) {\n  SpectreCceGridPointLocations result{};\n  if (l_max < 4 or l_max > 32) {\n    result.number_of_theta_points = 0;\n    result.number_of_phi_points = 0;\n    return result;\n  }\n  result.number_of_theta_points = l_max + 1;\n  result.number_of_phi_points = 2 * l_max + 1;\n\n  for (int i = 0; i < result.number_of_theta_points; ++i) {\n    switch (result.number_of_theta_points) {\n      case 5: {\n        const double temp[5] = {\n            4.366349492255221509e-01, 1.002176803643121561e+00,\n            1.570796326794896558e+00, 2.139415849946671777e+00,\n            2.704957704364271187e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 6: {\n        const double temp[6] = {\n            3.696066519448289456e-01, 8.483666264874876184e-01,\n            1.329852612388110256e+00, 1.811740041201682860e+00,\n            2.293226027102305498e+00, 2.771986001644964226e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 7: {\n        const double temp[7] = {\n            3.204050902900620335e-01, 7.354466143229519970e-01,\n            1.152892953722227221e+00, 1.570796326794896558e+00,\n            1.988699699867565895e+00, 2.406146039266841008e+00,\n            2.821187563299730972e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 8: {\n        const double temp[8] = {\n            2.827570635937968202e-01, 6.490365804607796107e-01,\n            1.017455539490153438e+00, 1.386317078892131294e+00,\n            1.755275574697661822e+00, 2.124137114099639678e+00,\n            2.492556073129013505e+00, 2.858835589995996074e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 9: {\n        const double temp[9] = {\n            2.530224166119307005e-01, 5.807869795060065510e-01,\n            9.104740292261472856e-01, 1.240573923404363343e+00,\n            1.570796326794896558e+00, 1.901018730185429773e+00,\n            2.231118624363645608e+00, 2.560805674083786343e+00,\n            2.888570236977862304e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 10: {\n        const double temp[10] = {\n            2.289442988470259954e-01, 5.255196555285001070e-01,\n            8.238386589997556131e-01, 1.122539327631709494e+00,\n            1.421366498439524895e+00, 1.720226155150268221e+00,\n            2.019053325958083622e+00, 2.317753994590037614e+00,\n            2.616072998061293120e+00, 2.912648354742767065e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 11: {\n        const double temp[11] = {\n            2.090492874137409585e-01, 4.798534223256742948e-01,\n            7.522519395990820978e-01, 1.025003226369574749e+00,\n            1.297877729331450292e+00, 1.570796326794896558e+00,\n            1.843714924258342824e+00, 2.116589427220218589e+00,\n            2.389340713990710796e+00, 2.661739231264118821e+00,\n            2.932543366176052047e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 12: {\n        const double temp[12] = {\n            1.923346793046672165e-01, 4.414870814893317452e-01,\n            6.921076988818409825e-01, 9.430552870605736215e-01,\n            1.194120375947706592e+00, 1.445233238471440140e+00,\n            1.696359415118352976e+00, 1.947472277642086524e+00,\n            2.198537366529219383e+00, 2.449484954707952244e+00,\n            2.700105572100461426e+00, 2.949257974285125705e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 13: {\n        const double temp[13] = {\n            1.780944581262765558e-01, 4.088002373420211999e-01,\n            6.408663264733868159e-01, 8.732366099401630555e-01,\n            1.105718066248490006e+00, 1.338247676100454475e+00,\n            1.570796326794896558e+00, 1.803344977489338641e+00,\n            2.035874587341303332e+00, 2.268356043649629949e+00,\n            2.500726327116406189e+00, 2.732792416247772138e+00,\n            2.963498195463516449e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 14: {\n        const double temp[14] = {\n            1.658171411523663707e-01, 3.806189306666775130e-01,\n            5.966877608172733716e-01, 8.130407055389454740e-01,\n            1.029498592525136758e+00, 1.246003586776677663e+00,\n            1.462529992921481892e+00, 1.679062660668311224e+00,\n            1.895589066813115453e+00, 2.112094061064656358e+00,\n            2.328551948050847642e+00, 2.544904892772519744e+00,\n            2.760973722923115492e+00, 2.975775512437426773e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 15: {\n        const double temp[15] = {\n            1.551231069747375235e-01, 3.560718303314724942e-01,\n            5.582062109125313087e-01, 7.606069572889918584e-01,\n            9.631067821301482201e-01, 1.165652065603030252e+00,\n            1.368219536992351770e+00, 1.570796326794896558e+00,\n            1.773373116597441346e+00, 1.975940587986762864e+00,\n            2.178485871459645118e+00, 2.380985696300801369e+00,\n            2.583386442677261918e+00, 2.785520823258320622e+00,\n            2.986469546615055481e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 16: {\n        const double temp[16] = {\n            1.457246820036738055e-01, 3.344986386876292461e-01,\n            5.243866409035942144e-01, 7.145252532340252705e-01,\n            9.047575323895165056e-01, 1.095033401803444439e+00,\n            1.285331444322965311e+00, 1.475640280808194316e+00,\n            1.665952372781598800e+00, 1.856261209266827805e+00,\n            2.046559251786348455e+00, 2.236835121200276610e+00,\n            2.427067400355767735e+00, 2.617206012686199124e+00,\n            2.807094014902163703e+00, 2.995867971586119172e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 17: {\n        const double temp[17] = {\n            1.373998952992547817e-01, 3.153898594929282484e-01,\n            4.944303818194982769e-01, 6.737074594242522529e-01,\n            8.530732514258505539e-01, 1.032480728417239479e+00,\n            1.211909966211469625e+00, 1.391350647015287434e+00,\n            1.570796326794896558e+00, 1.750242006574505682e+00,\n            1.929682687378323491e+00, 2.109111925172553637e+00,\n            2.288519402163942562e+00, 2.467885194165540863e+00,\n            2.647162271770294950e+00, 2.826202794096865034e+00,\n            3.004192758290538556e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 18: {\n        const double temp[18] = {\n            1.299747364196768562e-01, 2.983460782092324792e-01,\n            4.677113145328286592e-01, 6.373005058706191495e-01,\n            8.069738930788195042e-01, 9.766871104439832640e-01,\n            1.146421481056642211e+00, 1.316167494718022635e+00,\n            1.485919440392653001e+00, 1.655673213197140115e+00,\n            1.825425158871770481e+00, 1.995171172533150905e+00,\n            2.164905543145809741e+00, 2.334618760510973612e+00,\n            2.504292147719174189e+00, 2.673881339056964457e+00,\n            2.843246575380560692e+00, 3.011617917170116066e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 19: {\n        const double temp[19] = {\n            1.233108673082312784e-01, 2.830497588453067537e-01,\n            4.437316659960951482e-01, 6.046261769405451014e-01,\n            7.656007620508340494e-01, 9.266134127998189030e-01,\n            1.087646521650454945e+00, 1.248691224331339278e+00,\n            1.409742336767428883e+00, 1.570796326794896558e+00,\n            1.731850316822364233e+00, 1.892901429258453838e+00,\n            2.053946131939338393e+00, 2.214979240789974213e+00,\n            2.375991891538959067e+00, 2.536966476649248126e+00,\n            2.697860987593697857e+00, 2.858542894744486418e+00,\n            3.018281786281561629e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 20: {\n        const double temp[20] = {\n            1.172969277059561499e-01, 2.692452880289302186e-01,\n            4.220907301111165855e-01, 5.751385026314284055e-01,\n            7.282625848696072657e-01, 8.814230742890135639e-01,\n            1.034603297590104276e+00, 1.187794926634099024e+00,\n            1.340993178589955148e+00, 1.494194914310399636e+00,\n            1.647397739279393480e+00, 1.800599474999837968e+00,\n            1.953797726955694092e+00, 2.106989355999688840e+00,\n            2.260169579300779663e+00, 2.413330068720185739e+00,\n            2.566454150958364711e+00, 2.719501923478676364e+00,\n            2.872347365560862897e+00, 3.024295725883836994e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 21: {\n        const double temp[21] = {\n            1.118422651428890996e-01, 2.567245837448891010e-01,\n            4.024623099018152517e-01, 5.483930281810389662e-01,\n            6.943966110110700862e-01, 8.404350520135058789e-01,\n            9.864925055883793092e-01, 1.132561101012537597e+00,\n            1.278636375242898637e+00, 1.424715475176742796e+00,\n            1.570796326794896558e+00, 1.716877178413050320e+00,\n            1.862956278346894479e+00, 2.009031552577255297e+00,\n            2.155100148001413807e+00, 2.301157601576287348e+00,\n            2.447196042578723141e+00, 2.593199625408754372e+00,\n            2.739130343687977920e+00, 2.884868069844904070e+00,\n            3.029750388446903919e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 22: {\n        const double temp[22] = {\n            1.068723357985259942e-01, 2.453165389983613109e-01,\n            3.845781703583910915e-01, 5.240242709487281658e-01,\n            6.635400754448063099e-01, 8.030892957063359150e-01,\n            9.426568273796608333e-01, 1.082235198111836771e+00,\n            1.221820208990359813e+00, 1.361409225664372169e+00,\n            1.501000399130816065e+00, 1.640592254458977051e+00,\n            1.780183427925420947e+00, 1.919772444599433303e+00,\n            2.059357455477956123e+00, 2.198935826210132394e+00,\n            2.338503357883457312e+00, 2.478052578144986917e+00,\n            2.617568382641064950e+00, 2.757014483231401858e+00,\n            2.896276114591431750e+00, 3.034720317791267163e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 23: {\n        const double temp[23] = {\n            1.023252788872632685e-01, 2.348791589702580174e-01,\n            3.682157131008290119e-01, 5.017289283414202439e-01,\n            6.353089402976822564e-01, 7.689210263823624825e-01,\n            9.025507517347876041e-01, 1.036190996404462217e+00,\n            1.169837785762829929e+00, 1.303488659735581257e+00,\n            1.437141935303526186e+00, 1.570796326794896558e+00,\n            1.704450718286266930e+00, 1.838103993854211859e+00,\n            1.971754867826963187e+00, 2.105401657185330677e+00,\n            2.239041901855005623e+00, 2.372671627207430411e+00,\n            2.506283713292110971e+00, 2.639863725248372983e+00,\n            2.773376940488963882e+00, 2.906713494619534988e+00,\n            3.039267374702530056e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 24: {\n        const double temp[24] = {\n            9.814932949793685191e-02, 2.252936226353075833e-01,\n            3.531886675690780741e-01, 4.812531951313686607e-01,\n            6.093818382449566196e-01, 7.375413075437535770e-01,\n            8.657177770401081052e-01, 9.939044422989454786e-01,\n            1.122097523267250763e+00, 1.250294703417273112e+00,\n            1.378494427506219200e+00, 1.506695545558101035e+00,\n            1.634897108031692081e+00, 1.763098226083573916e+00,\n            1.891297950172520004e+00, 2.019495130322542131e+00,\n            2.147688211290847526e+00, 2.275874876549685233e+00,\n            2.404051346046039761e+00, 2.532210815344836607e+00,\n            2.660339458458424566e+00, 2.788403986020715042e+00,\n            2.916299030954485616e+00, 3.043443324091856361e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 25: {\n        const double temp[25] = {\n            9.430083986305520805e-02, 2.164597408964339387e-01,\n            3.393399712563371362e-01, 4.623830630132757524e-01,\n            5.854877911108011812e-01, 7.086221837538611013e-01,\n            8.317729718814276252e-01, 9.549336362382321308e-01,\n            1.078100568411879845e+00, 1.201271573324181219e+00,\n            1.324445197736386692e+00, 1.447620393135667038e+00,\n            1.570796326794896558e+00, 1.693972260454126078e+00,\n            1.817147455853406424e+00, 1.940321080265611897e+00,\n            2.063492085177913271e+00, 2.186659017351560763e+00,\n            2.309819681708365380e+00, 2.432970469835932015e+00,\n            2.556104862478991713e+00, 2.679209590576517197e+00,\n            2.802252682333456146e+00, 2.925132912693359177e+00,\n            3.047291813726737963e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 26: {\n        const double temp[26] = {\n            9.074274842993197698e-02, 2.082924425598466356e-01,\n            3.265362611165358309e-01, 4.449368152119130282e-01,\n            5.633967073169293682e-01, 6.818851814129298639e-01,\n            8.003894803353296394e-01, 9.189033445598993044e-01,\n            1.037423319077439121e+00, 1.155947313793812103e+00,\n            1.274473959424494041e+00, 1.393002286179807925e+00,\n            1.511531546703289264e+00, 1.630061106886503852e+00,\n            1.748590367409985191e+00, 1.867118694165299075e+00,\n            1.985645339795981013e+00, 2.104169334512353995e+00,\n            2.222689309029894034e+00, 2.341203173254463366e+00,\n            2.459707472176863252e+00, 2.578195946272863637e+00,\n            2.696655838377880254e+00, 2.815056392473257230e+00,\n            2.933300211029946425e+00, 3.050849905159861208e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 27: {\n        const double temp[27] = {\n            8.744338280630299665e-02, 2.007190266590380412e-01,\n            3.146635662674374112e-01, 4.287591577660783693e-01,\n            5.429119513798658092e-01, 6.570923167092416195e-01,\n            7.712879690777516561e-01, 8.854928869950799974e-01,\n            9.997037539874953360e-01, 1.113918572282611930e+00,\n            1.228136043468909699e+00, 1.342355260834552144e+00,\n            1.456575541704195897e+00, 1.570796326794896558e+00,\n            1.685017111885597219e+00, 1.799237392755240972e+00,\n            1.913456610120883417e+00, 2.027674081307181186e+00,\n            2.141888899602297780e+00, 2.256099766594712897e+00,\n            2.370304684512041682e+00, 2.484500336880551608e+00,\n            2.598680702209927418e+00, 2.712833495823714802e+00,\n            2.826929087322355816e+00, 2.940873626930755158e+00,\n            3.054149270783490078e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 28: {\n        const double temp[28] = {\n            8.437551461511597073e-02, 1.936769929947376179e-01,\n            3.036239070914333316e-01, 4.137165857369637378e-01,\n            5.238644768825679865e-01, 6.340389954584301213e-01,\n            7.442282945111358128e-01, 8.544265718392254350e-01,\n            9.646306371285441328e-01, 1.074838574917869272e+00,\n            1.185049147889021492e+00, 1.295261501292316098e+00,\n            1.405475003062348627e+00, 1.515689149557281068e+00,\n            1.625903504032512048e+00, 1.736117650527444489e+00,\n            1.846331152297477018e+00, 1.956543505700771624e+00,\n            2.066754078671923622e+00, 2.176962016461248872e+00,\n            2.287166081750567681e+00, 2.397364359078657081e+00,\n            2.507553658131362884e+00, 2.617728176707225352e+00,\n            2.727876067852829323e+00, 2.837968746498359618e+00,\n            2.947915660595055609e+00, 3.057217138974677173e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 29: {\n        const double temp[29] = {\n            8.151560650977880684e-02, 1.871123137498062194e-01,\n            2.933325857619472621e-01, 3.996936914666951446e-01,\n            5.061081521563000063e-01, 6.125483562383020608e-01,\n            7.190028636037067988e-01, 8.254660749671546283e-01,\n            9.319349156915986976e-01, 1.038407544520296710e+00,\n            1.144882777708662536e+00, 1.251359804334884807e+00,\n            1.357838033080061679e+00, 1.464317002991565309e+00,\n            1.570796326794896558e+00, 1.677275650598227807e+00,\n            1.783754620509731437e+00, 1.890232849254908309e+00,\n            1.996709875881130580e+00, 2.103185109069496406e+00,\n            2.209657737898194529e+00, 2.316126578622638377e+00,\n            2.422589789986086206e+00, 2.529044297351490833e+00,\n            2.635484501433492888e+00, 2.741898962123098027e+00,\n            2.848260067827845798e+00, 2.954480339839987035e+00,\n            3.060077047080014268e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 30: {\n        const double temp[30] = {\n            7.884320726554944203e-02, 1.809780449917272049e-01,\n            2.837160095793466730e-01, 3.865901987860504985e-01,\n            4.895160050896970039e-01, 5.924667257887386018e-01,\n            6.954313000299366943e-01, 7.984043170121235544e-01,\n            9.013828087667156153e-01, 1.004365001539081037e+00,\n            1.107349759228459130e+00, 1.210336308624476498e+00,\n            1.313324092045794700e+00, 1.416312682230741693e+00,\n            1.519301729274526558e+00, 1.622290924315266558e+00,\n            1.725279971359051423e+00, 1.828268561543998416e+00,\n            1.931256344965316618e+00, 2.034242894361334208e+00,\n            2.137227652050712301e+00, 2.240209844823077390e+00,\n            2.343188336577669340e+00, 2.446161353559856533e+00,\n            2.549125927801054736e+00, 2.652076648500095946e+00,\n            2.755002454803742395e+00, 2.857876644010446388e+00,\n            2.960614608598065800e+00, 3.062749446324243507e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 31: {\n        const double temp[31] = {\n            7.634046205384431572e-02, 1.752332025619508515e-01,\n            2.747099287638327669e-01, 3.743185619229329464e-01,\n            4.739771829190733698e-01, 5.736599396529727946e-01,\n            6.733561257504194764e-01, 7.730605060747958168e-01,\n            8.727702114891848773e-01, 9.724835301003497134e-01,\n            1.072199368669106478e+00, 1.171916986981363706e+00,\n            1.271635855736122256e+00, 1.371355574944659095e+00,\n            1.471075823713997366e+00, 1.570796326794896558e+00,\n            1.670516829875795750e+00, 1.770237078645134021e+00,\n            1.869956797853670860e+00, 1.969675666608429410e+00,\n            2.069393284920686860e+00, 2.169109123489443292e+00,\n            2.268822442100608239e+00, 2.368532147514997188e+00,\n            2.468236527839373640e+00, 2.567932713936820210e+00,\n            2.667615470670719802e+00, 2.767274091666860336e+00,\n            2.866882724825960516e+00, 2.966359451027842375e+00,\n            3.065252191535948967e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 32: {\n        const double temp[32] = {\n            7.399171309970958843e-02, 1.698418454282150103e-01,\n            2.662579994723859311e-01, 3.628020075350028018e-01,\n            4.593944730762095641e-01, 5.560103418005302167e-01,\n            6.526392394594561219e-01, 7.492760951181414164e-01,\n            8.459181315837993598e-01, 9.425636940046776546e-01,\n            1.039211728068951679e+00, 1.135861522840293736e+00,\n            1.232512573416362889e+00, 1.329164502391080749e+00,\n            1.425817011963825376e+00, 1.522469852641529231e+00,\n            1.619122800948263885e+00, 1.715775641625967740e+00,\n            1.812428151198712367e+00, 1.909080080173430227e+00,\n            2.005731130749499158e+00, 2.102380925520841437e+00,\n            2.199028959585115572e+00, 2.295674522005993978e+00,\n            2.392316558471651700e+00, 2.488953414130337105e+00,\n            2.585582311789262899e+00, 2.682198180513583718e+00,\n            2.778790646054790425e+00, 2.875334654117406963e+00,\n            2.971750808161578217e+00, 3.067600940490083694e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      case 33: {\n        const double temp[33] = {\n            7.178317184275122276e-02, 1.647723231643112574e-01,\n            2.583106041071417946e-01, 3.519729273095236199e-01,\n            4.456822679082866334e-01, 5.394143214244183637e-01,\n            6.331590254855161692e-01, 7.269114630504562857e-01,\n            8.206689427646121082e-01, 9.144298626454031576e-01,\n            1.008193204014774080e+00, 1.101958282220461438e+00,\n            1.195724613675799519e+00, 1.289491840051302685e+00,\n            1.383259682348271680e+00, 1.477027911291552309e+00,\n            1.570796326794896558e+00, 1.664564742298240807e+00,\n            1.758332971241521436e+00, 1.852100813538490431e+00,\n            1.945868039913993597e+00, 2.039634371369331678e+00,\n            2.133399449575018814e+00, 2.227162790944389847e+00,\n            2.320923710825181008e+00, 2.414681190539337052e+00,\n            2.508433628104277169e+00, 2.602178332165374641e+00,\n            2.695910385681506316e+00, 2.789619726280269330e+00,\n            2.883282049482651210e+00, 2.976820330425481664e+00,\n            3.069809481747042046e+00};\n        result.theta_points[i] = temp[i];\n        break;\n      }\n      default: {\n        result.number_of_theta_points = 0;\n        result.number_of_phi_points = 0;\n        break;\n      }\n    }\n  }\n\n  const double d_phi = (2.0 * M_PI) / result.number_of_phi_points;\n  for (int i = 0; i < result.number_of_phi_points; ++i) {\n    result.phi_points[i] = i * d_phi;\n  }\n  return result;\n}\n// [spectre_cce_grid_point_locations]\n\n// NOLINTEND\n\nvoid test_spectre_cce_grid_point_locations() {\n  for (size_t l_max = 4; l_max < 33; ++l_max) {\n    const SpectreCceGridPointLocations t =\n        spectre_ylm_theta_phi_points(static_cast<int>(l_max));\n    const Spherepack ylm{l_max, l_max};\n    CHECK(ylm.theta_points().size() ==\n          static_cast<size_t>(t.number_of_theta_points));\n    CHECK(ylm.phi_points().size() ==\n          static_cast<size_t>(t.number_of_phi_points));\n    for (size_t i = 0; i < ylm.theta_points().size(); ++i) {\n      // NOLINTNEXTLINE\n      CHECK(ylm.theta_points()[i] == approx(t.theta_points[i]));\n    }\n    for (size_t i = 0; i < ylm.phi_points().size(); ++i) {\n      // NOLINTNEXTLINE\n      CHECK(ylm.phi_points()[i] == approx(t.phi_points[i]));\n    }\n  }\n}\n\nusing SecondDeriv = Spherepack::SecondDeriv;\n\nvoid test_prolong_restrict() {\n  Spherepack ylm_a(10, 10);\n\n  const YlmTestFunctions::FuncA func_a{};\n  const YlmTestFunctions::FuncB func_b{};\n  const YlmTestFunctions::FuncC func_c{};\n\n  const auto& theta = ylm_a.theta_points();\n  const auto& phi = ylm_a.phi_points();\n\n  const auto u_a = func_a.func(theta, phi);\n  const auto u_b = func_b.func(theta, phi);\n  const auto u_c = func_c.func(theta, phi);\n\n  const auto u_coef_a = ylm_a.phys_to_spec(u_a);\n  {\n    Spherepack ylm_b(10, 7);\n    const auto u_coef_a2b = ylm_a.prolong_or_restrict(u_coef_a, ylm_b);\n    const auto u_coef_a2b2a = ylm_b.prolong_or_restrict(u_coef_a2b, ylm_a);\n    const auto u_b_test = ylm_a.spec_to_phys(u_coef_a2b2a);\n    CHECK_ITERABLE_APPROX(u_b, u_b_test);\n\n    const auto u_coef_a2b_direct = Spherepack::prolong_or_restrict(\n        u_coef_a, ylm_a.l_max(), ylm_a.m_max(), ylm_b.l_max(), ylm_b.m_max());\n    CHECK_ITERABLE_APPROX(u_coef_a2b, u_coef_a2b_direct);\n  }\n\n  {\n    Spherepack ylm_c(6, 2);\n    const auto u_coef_a2c = ylm_a.prolong_or_restrict(u_coef_a, ylm_c);\n    const auto u_coef_a2c2a = ylm_c.prolong_or_restrict(u_coef_a2c, ylm_a);\n    const auto u_c_test = ylm_a.spec_to_phys(u_coef_a2c2a);\n    CHECK_ITERABLE_APPROX(u_c, u_c_test);\n  }\n}\n\nvoid test_loop_over_offset(\n    const size_t l_max, const size_t m_max, const size_t physical_stride,\n    const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  Spherepack ylm_spherepack(l_max, m_max);\n\n  // Fill data vectors\n  const size_t physical_size = ylm_spherepack.physical_size() * physical_stride;\n  const size_t spectral_size = ylm_spherepack.spectral_size() * physical_stride;\n  DataVector u(physical_size);\n  DataVector u_spec(spectral_size);\n\n  // Fill analytic solution\n  const std::vector<double>& theta = ylm_spherepack.theta_points();\n  const std::vector<double>& phi = ylm_spherepack.phi_points();\n  for (size_t off = 0; off < physical_stride; ++off) {\n    func.func(&u, physical_stride, off, theta, phi);\n  }\n\n  // Evaluate spectral coefficients of initial scalar function\n  ylm_spherepack.phys_to_spec_all_offsets(u_spec.data(), u.data(),\n                                          physical_stride);\n\n  // Test whether phys_to_spec and spec_to_phys are inverses.\n  {\n    std::vector<double> u_test(physical_size);\n    ylm_spherepack.spec_to_phys_all_offsets(u_test.data(), u_spec.data(),\n                                            physical_stride);\n    for (size_t s = 0; s < physical_size; ++s) {\n      CHECK(u[s] == approx(u_test[s]));\n    }\n  }\n\n  // Test simplified interface\n  {\n    auto u_spec_simple =\n        ylm_spherepack.phys_to_spec_all_offsets(u, physical_stride);\n    CHECK_ITERABLE_APPROX(u_spec, u_spec_simple);\n\n    auto u_test =\n        ylm_spherepack.spec_to_phys_all_offsets(u_spec_simple, physical_stride);\n    CHECK_ITERABLE_APPROX(u, u_test);\n  }\n\n  // Test gradient\n  {\n    std::vector<std::vector<double>> duteststor(2);\n    std::vector<std::vector<double>> dustor(2);\n    std::vector<std::vector<double>> duSpecstor(2);\n    for (size_t i = 0; i < 2; ++i) {\n      dustor[i].resize(physical_size);\n      duteststor[i].resize(physical_size);\n      duSpecstor[i].resize(physical_size);\n    }\n    std::array<double*, 2> du({{dustor[0].data(), dustor[1].data()}});\n    std::array<double*, 2> duSpec(\n        {{duSpecstor[0].data(), duSpecstor[1].data()}});\n    std::array<double*, 2> dutest(\n        {{duteststor[0].data(), duteststor[1].data()}});\n\n    // Fill analytic result\n    for (size_t off = 0; off < physical_stride; ++off) {\n      func.dfunc(&dutest, physical_stride, off, theta, phi);\n    }\n\n    // Differentiate\n    ylm_spherepack.gradient_from_coefs_all_offsets(duSpec, u_spec.data(),\n                                                   physical_stride);\n    ylm_spherepack.gradient_all_offsets(du, u.data(), physical_stride);\n\n    // Test vs analytic result\n    for (size_t d = 0; d < 2; ++d) {\n      for (size_t s = 0; s < physical_size; ++s) {\n        CHECK(gsl::at(dutest, d)[s] == approx(gsl::at(du, d)[s]));\n        CHECK(gsl::at(dutest, d)[s] == approx(gsl::at(duSpec, d)[s]));\n      }\n    }\n\n    // Test simplified interface of gradient\n    {\n      auto du_simple = ylm_spherepack.gradient_all_offsets(u, physical_stride);\n      for (size_t d = 0; d < 2; ++d) {\n        for (size_t s = 0; s < physical_size; ++s) {\n          CHECK(gsl::at(dutest, d)[s] == approx(du_simple.get(d)[s]));\n        }\n      }\n      du_simple = ylm_spherepack.gradient_from_coefs_all_offsets(\n          u_spec, physical_stride);\n      for (size_t d = 0; d < 2; ++d) {\n        for (size_t s = 0; s < physical_size; ++s) {\n          CHECK(gsl::at(dutest, d)[s] == approx(du_simple.get(d)[s]));\n        }\n      }\n    }\n  }\n}\n\nvoid test_theta_phi_points(\n    const size_t l_max, const size_t m_max,\n    const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  Spherepack ylm_spherepack(l_max, m_max);\n\n  // Fill with analytic function\n  const auto& theta = ylm_spherepack.theta_points();\n  const auto& phi = ylm_spherepack.phi_points();\n  DataVector u(ylm_spherepack.physical_size());\n  func.func(&u, 1, 0, theta, phi);\n\n  const auto theta_phi = ylm_spherepack.theta_phi_points();\n  DataVector u_test(ylm_spherepack.physical_size());\n  // fill pointwise using offset\n  for (size_t s = 0; s < ylm_spherepack.physical_size(); ++s) {\n    func.func(&u_test, 1, s, {gsl::at(theta_phi, 0)[s]},\n              {gsl::at(theta_phi, 1)[s]});\n  }\n  CHECK_ITERABLE_APPROX(u, u_test);\n}\n\nvoid test_phys_to_spec(const size_t l_max, const size_t m_max,\n                       const size_t physical_stride,\n                       const size_t spectral_stride,\n                       const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  const size_t n_th = l_max + 1;\n  const size_t n_ph = 2 * m_max + 1;\n  const size_t physical_size = n_th * n_ph * physical_stride;\n  const size_t spectral_size = 2 * (l_max + 1) * (m_max + 1) * spectral_stride;\n\n  Spherepack ylm_spherepack(l_max, m_max);\n  CHECK(physical_size == ylm_spherepack.physical_size() * physical_stride);\n  CHECK(spectral_size == ylm_spherepack.spectral_size() * spectral_stride);\n\n  const auto& theta = ylm_spherepack.theta_points();\n  const auto& phi = ylm_spherepack.phi_points();\n\n  DataVector u(physical_size);\n  DataVector u_spec(spectral_size);\n\n  // Fill with analytic function\n  func.func(&u, physical_stride, 0, theta, phi);\n\n  // Evaluate spectral coefficients of initial scalar function\n  ylm_spherepack.phys_to_spec(u_spec.data(), u.data(), physical_stride, 0,\n                              spectral_stride, 0);\n\n  // Test whether phys_to_spec and spec_to_phys are inverses.\n  {\n    std::vector<double> u_test(physical_size);\n    std::vector<double> u_spec_test(spectral_size);\n    ylm_spherepack.phys_to_spec(u_spec_test.data(), u.data(), physical_stride,\n                                0, spectral_stride, 0);\n    ylm_spherepack.spec_to_phys(u_test.data(), u_spec.data(), spectral_stride,\n                                0, physical_stride, 0);\n    for (size_t s = 0; s < physical_size; s += physical_stride) {\n      CHECK(u[s] == approx(u_test[s]));\n    }\n    for (size_t s = 0; s < spectral_size; s += spectral_stride) {\n      CHECK(u_spec[s] == u_spec_test[s]);\n    }\n  }\n\n  // Test simplified interface of phys_to_spec/spec_to_phys\n  if (physical_stride == 1 and spectral_stride == 1) {\n    auto u_spec_simple = ylm_spherepack.phys_to_spec(u);\n    CHECK_ITERABLE_APPROX(u_spec, u_spec_simple);\n\n    auto u_test = ylm_spherepack.spec_to_phys(u_spec_simple);\n    CHECK_ITERABLE_APPROX(u, u_test);\n  }\n}\n\nvoid test_gradient(const size_t l_max, const size_t m_max,\n                   const size_t physical_stride, const size_t spectral_stride,\n                   const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  Spherepack ylm_spherepack(l_max, m_max);\n  const size_t physical_size = ylm_spherepack.physical_size() * physical_stride;\n  const size_t spectral_size = ylm_spherepack.spectral_size() * spectral_stride;\n\n  const auto& theta = ylm_spherepack.theta_points();\n  const auto& phi = ylm_spherepack.phi_points();\n\n  DataVector u(physical_size);\n  DataVector u_spec(spectral_size);\n\n  // Fill with analytic function\n  func.func(&u, physical_stride, 0, theta, phi);\n\n  // Evaluate spectral coefficients of initial scalar function\n  ylm_spherepack.phys_to_spec(u_spec.data(), u.data(), physical_stride, 0,\n                              spectral_stride, 0);\n\n  // Test gradient\n  {\n    std::vector<std::vector<double>> duteststor(2);\n    std::vector<std::vector<double>> dustor(2);\n    std::vector<std::vector<double>> duSpecstor(2);\n    for (size_t i = 0; i < 2; ++i) {\n      dustor[i].resize(physical_size);\n      duteststor[i].resize(physical_size);\n      duSpecstor[i].resize(physical_size);\n    }\n    std::array<double*, 2> du({{dustor[0].data(), dustor[1].data()}});\n    std::array<double*, 2> duSpec(\n        {{duSpecstor[0].data(), duSpecstor[1].data()}});\n    std::array<double*, 2> dutest(\n        {{duteststor[0].data(), duteststor[1].data()}});\n\n    // Differentiate\n    ylm_spherepack.gradient_from_coefs(duSpec, u_spec.data(), spectral_stride,\n                                       0, physical_stride, 0);\n    ylm_spherepack.gradient(du, u.data(), physical_stride, 0);\n\n    // Test vs analytic result\n    func.dfunc(&dutest, physical_stride, 0, theta, phi);\n    for (size_t d = 0; d < 2; ++d) {\n      for (size_t s = 0; s < physical_size; s += physical_stride) {\n        CHECK(gsl::at(dutest, d)[s] == approx(gsl::at(du, d)[s]));\n        CHECK(gsl::at(dutest, d)[s] == approx(gsl::at(duSpec, d)[s]));\n      }\n    }\n\n    if (physical_stride == 1 && spectral_stride == 1) {\n      // Without strides and offsets.\n      ylm_spherepack.gradient_from_coefs(duSpec, u_spec.data());\n      ylm_spherepack.gradient(du, u.data());\n      for (size_t d = 0; d < 2; ++d) {\n        for (size_t s = 0; s < physical_size; ++s) {\n          CHECK(gsl::at(dutest, d)[s] == approx(gsl::at(du, d)[s]));\n          CHECK(gsl::at(dutest, d)[s] == approx(gsl::at(duSpec, d)[s]));\n        }\n      }\n\n      // Test simplified interface of gradient\n      auto du_simple = ylm_spherepack.gradient(u);\n      for (size_t d = 0; d < 2; ++d) {\n        for (size_t s = 0; s < physical_size; ++s) {\n          CHECK(gsl::at(dutest, d)[s] == approx(du_simple.get(d)[s]));\n        }\n      }\n      du_simple = ylm_spherepack.gradient_from_coefs(u_spec);\n      for (size_t d = 0; d < 2; ++d) {\n        for (size_t s = 0; s < physical_size; ++s) {\n          CHECK(gsl::at(dutest, d)[s] == approx(du_simple.get(d)[s]));\n        }\n      }\n    } else {\n      // Test simplified interface of gradient for non-unit stride\n      auto du_simple = ylm_spherepack.gradient(u, physical_stride);\n      for (size_t d = 0; d < 2; ++d) {\n        for (size_t s = 0; s < ylm_spherepack.physical_size(); ++s) {\n          CHECK(gsl::at(dutest, d)[s * physical_stride] ==\n                approx(du_simple.get(d)[s]));\n        }\n      }\n      du_simple = ylm_spherepack.gradient_from_coefs(u_spec, spectral_stride);\n      for (size_t d = 0; d < 2; ++d) {\n        for (size_t s = 0; s < ylm_spherepack.physical_size(); ++s) {\n          CHECK(gsl::at(dutest, d)[s * physical_stride] ==\n                approx(du_simple.get(d)[s]));\n        }\n      }\n    }\n  }\n}\n\nvoid test_second_derivative(\n    const size_t l_max, const size_t m_max, const size_t physical_stride,\n    const size_t spectral_stride,\n    const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  Spherepack ylm_spherepack(l_max, m_max);\n  const size_t physical_size = ylm_spherepack.physical_size() * physical_stride;\n  const size_t spectral_size = ylm_spherepack.spectral_size() * spectral_stride;\n\n  const auto& theta = ylm_spherepack.theta_points();\n  const auto& phi = ylm_spherepack.phi_points();\n\n  DataVector u(physical_size);\n  DataVector u_spec(spectral_size);\n\n  // Fill with analytic function\n  func.func(&u, physical_stride, 0, theta, phi);\n\n  // Evaluate spectral coefficients of initial scalar function\n  ylm_spherepack.phys_to_spec(u_spec.data(), u.data(), physical_stride, 0,\n                              spectral_stride, 0);\n\n  // Test second_derivative\n  {\n    SecondDeriv ddu(physical_size);\n    SecondDeriv ddutest(physical_size);\n\n    std::vector<std::vector<double>> dustor(2);\n    for (size_t i = 0; i < 2; ++i) {\n      dustor[i].resize(physical_size);\n    }\n    std::array<double*, 2> du{{dustor[0].data(), dustor[1].data()}};\n\n    // Differentiate\n    ylm_spherepack.second_derivative(du, &ddu, u.data(), physical_stride, 0);\n\n    // Test ylm_spherepack derivative against func analytical result\n    func.ddfunc(&ddutest, physical_stride, 0, theta, phi);\n    for (size_t i = 0; i < 2; ++i) {\n      for (size_t j = 0; j < 2; ++j) {\n        for (size_t s = 0; s < physical_size; s += physical_stride) {\n          CHECK(ddutest.get(i, j)[s] == approx(ddu.get(i, j)[s]));\n        }\n      }\n    }\n\n    if (physical_stride == 1 && spectral_stride == 1) {\n      ylm_spherepack.second_derivative(du, &ddu, u.data());\n      for (size_t i = 0; i < 2; ++i) {\n        for (size_t j = 0; j < 2; ++j) {\n          CHECK_ITERABLE_APPROX(ddutest.get(i, j), ddu.get(i, j));\n        }\n      }\n\n      // Test first_and_second_derivative\n      auto deriv_test = ylm_spherepack.first_and_second_derivative(u);\n      for (size_t i = 0; i < 2; ++i) {\n        for (size_t s = 0; s < physical_size; ++s) {\n          CHECK(std::get<0>(deriv_test).get(i)[s] == approx(gsl::at(du, i)[s]));\n        }\n        for (size_t j = 0; j < 2; ++j) {\n          CHECK_ITERABLE_APPROX(std::get<1>(deriv_test).get(i, j),\n                                ddu.get(i, j));\n        }\n      }\n    }\n  }\n}\n\nvoid test_scalar_laplacian(\n    const size_t l_max, const size_t m_max, const size_t physical_stride,\n    const size_t spectral_stride,\n    const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  Spherepack ylm_spherepack(l_max, m_max);\n  const size_t physical_size = ylm_spherepack.physical_size() * physical_stride;\n  const size_t spectral_size = ylm_spherepack.spectral_size() * spectral_stride;\n\n  const auto& theta = ylm_spherepack.theta_points();\n  const auto& phi = ylm_spherepack.phi_points();\n\n  DataVector u(physical_size);\n  DataVector u_spec(spectral_size);\n\n  // Fill with analytic function\n  func.func(&u, physical_stride, 0, theta, phi);\n\n  // Evaluate spectral coefficients of initial scalar function\n  ylm_spherepack.phys_to_spec(u_spec.data(), u.data(), physical_stride, 0,\n                              spectral_stride, 0);\n\n  // Test scalar_laplacian\n  {\n    DataVector slaptest(physical_size);\n    DataVector slap(physical_size);\n    DataVector slapSpec(physical_size);\n\n    // Differentiate\n    ylm_spherepack.scalar_laplacian(slap.data(), u.data(), physical_stride, 0);\n    ylm_spherepack.scalar_laplacian_from_coefs(\n        slapSpec.data(), u_spec.data(), spectral_stride, 0, physical_stride, 0);\n\n    // Test ylm_spherepack derivative against func analytical result\n    func.scalar_laplacian(&slaptest, physical_stride, 0, theta, phi);\n    for (size_t s = 0; s < physical_size; s += physical_stride) {\n      CHECK(slaptest[s] == approx(slap[s]));\n      CHECK(slaptest[s] == approx(slapSpec[s]));\n    }\n\n    // Test the default arguments for stride and offset\n    if (physical_stride == 1 && spectral_stride == 1) {\n      ylm_spherepack.scalar_laplacian(slap.data(), u.data());\n      ylm_spherepack.scalar_laplacian_from_coefs(slapSpec.data(),\n                                                 u_spec.data());\n      CHECK_ITERABLE_APPROX(slaptest, slap);\n      CHECK_ITERABLE_APPROX(slaptest, slapSpec);\n\n      // Test simplified interface of scalar_laplacian\n      auto slap1 = ylm_spherepack.scalar_laplacian(u);\n      auto slap2 = ylm_spherepack.scalar_laplacian_from_coefs(u_spec);\n      CHECK_ITERABLE_APPROX(slaptest, slap1);\n      CHECK_ITERABLE_APPROX(slaptest, slap2);\n    }\n  }\n}\n\nvoid test_interpolation(\n    const size_t l_max, const size_t m_max, const size_t physical_stride,\n    const size_t spectral_stride,\n    const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  Spherepack ylm_spherepack(l_max, m_max);\n  // test with a seperate instance if it can use interpolation_info from the\n  // first one.\n  Spherepack ylm_spherepack_2(l_max, m_max);\n  const size_t physical_size = ylm_spherepack.physical_size() * physical_stride;\n  const size_t spectral_size = ylm_spherepack.spectral_size() * spectral_stride;\n\n  const auto& theta = ylm_spherepack.theta_points();\n  const auto& phi = ylm_spherepack.phi_points();\n\n  DataVector u(physical_size);\n  DataVector u_spec(spectral_size);\n\n  // Fill with analytic function\n  func.func(&u, physical_stride, 0, theta, phi);\n\n  // Evaluate spectral coefficients of initial scalar function\n  ylm_spherepack.phys_to_spec(u_spec.data(), u.data(), physical_stride, 0,\n                              spectral_stride, 0);\n\n  // Test interpolation\n  {\n    // Choose random points\n    DataVector thetas(50);\n    DataVector phis(50);\n    {\n      std::uniform_real_distribution<double> ran(0.0, 1.0);\n      MAKE_GENERATOR(gen);\n      // Here we generate 10 * 5 different random (theta, phi) pairs. Each\n      // iteration adds five more elements to the vectors of `thetas` and\n      // `phis`, so the index increases by five.\n      for (size_t n = 0; n < 10; ++n) {\n        const double th = (2.0 * ran(gen) - 1.0) * M_PI;\n        const double ph = 2.0 * ran(gen) * M_PI;\n\n        thetas.at(n * 5) = th;\n        phis.at(n * 5) = ph;\n\n        // For the next point, increase ph by 2pi so it is out of range.\n        // Should be equivalent to the first point.\n        thetas.at(n * 5 + 1) = th;\n        phis.at(n * 5 + 1) = ph + 2.0 * M_PI;\n\n        // For the next point, decrease ph by 2pi so it is out of range.\n        // Should be equivalent to the first point.\n        thetas.at(n * 5 + 2) = th;\n        phis.at(n * 5 + 2) = ph - 2.0 * M_PI;\n\n        // For the next point, use negative theta so it is out of range,\n        // and also add pi to phi.\n        // Should be equivalent to the first point.\n        thetas.at(n * 5 + 3) = -th;\n        phis.at(n * 5 + 3) = ph + M_PI;\n\n        // For the next point, theta -> 2pi - theta so that theta is out of\n        // range.  Also add pi to Phi.\n        // Should be equivalent to the first point.\n        thetas.at(n * 5 + 4) = 2.0 * M_PI - th;\n        phis.at(n * 5 + 4) = ph + M_PI;\n      }\n    }\n\n    std::array<DataVector, 2> points{std::move(thetas), std::move(phis)};\n\n    // Get interp info\n    auto interpolation_info = ylm_spherepack.set_up_interpolation_info(points);\n\n    // Interpolate\n    DataVector uintPhys(interpolation_info.size());\n    DataVector uintSpec(interpolation_info.size());\n\n    DataVector uintPhys2(interpolation_info.size());\n    DataVector uintSpec2(interpolation_info.size());\n\n    ylm_spherepack.interpolate(make_not_null(&uintPhys), u.data(),\n                               interpolation_info, physical_stride, 0);\n    ylm_spherepack.interpolate_from_coefs(make_not_null(&uintSpec), u_spec,\n                                          interpolation_info, spectral_stride);\n\n    ylm_spherepack_2.interpolate(make_not_null(&uintPhys2), u.data(),\n                                 interpolation_info, physical_stride, 0);\n    ylm_spherepack_2.interpolate_from_coefs(\n        make_not_null(&uintSpec2), u_spec, interpolation_info, spectral_stride);\n\n    // Test vs analytic solution\n    DataVector uintanal(interpolation_info.size());\n    for (size_t s = 0; s < uintanal.size(); ++s) {\n      func.func(&uintanal, 1, s, {points[0][s]}, {points[1][s]});\n      CHECK(uintanal[s] == approx(uintPhys[s]));\n      CHECK(uintanal[s] == approx(uintSpec[s]));\n\n      CHECK(uintanal[s] == approx(uintPhys2[s]));\n      CHECK(uintanal[s] == approx(uintSpec2[s]));\n    }\n\n    // Test for angles out of range.\n    for (size_t s = 0; s < uintanal.size() / 5; ++s) {\n      // All answers should agree in each group of five, since the values\n      // of all the out-of-range angles should represent the same point.\n      CHECK(uintPhys[5 * s + 1] == approx(uintPhys[5 * s]));\n      CHECK(uintPhys[5 * s + 2] == approx(uintPhys[5 * s]));\n      CHECK(uintPhys[5 * s + 3] == approx(uintPhys[5 * s]));\n      CHECK(uintPhys[5 * s + 4] == approx(uintPhys[5 * s]));\n\n      CHECK(uintPhys2[5 * s + 1] == approx(uintPhys2[5 * s]));\n      CHECK(uintPhys2[5 * s + 2] == approx(uintPhys2[5 * s]));\n      CHECK(uintPhys2[5 * s + 3] == approx(uintPhys2[5 * s]));\n      CHECK(uintPhys2[5 * s + 4] == approx(uintPhys2[5 * s]));\n    }\n\n    // Tests default values of stride and offset.\n    if (physical_stride == 1 && spectral_stride == 1) {\n      ylm_spherepack.interpolate(make_not_null(&uintPhys), u.data(),\n                                 interpolation_info);\n      ylm_spherepack_2.interpolate(make_not_null(&uintPhys2), u.data(),\n                                   interpolation_info);\n      for (size_t s = 0; s < uintanal.size(); ++s) {\n        CHECK(uintanal[s] == approx(uintPhys[s]));\n        CHECK(uintanal[s] == approx(uintPhys2[s]));\n      }\n    }\n\n    // Test simplified interpolation interface\n    if (physical_stride == 1) {\n      auto test_interp = ylm_spherepack.interpolate(u, points);\n      auto test_interp_2 = ylm_spherepack_2.interpolate(u, points);\n\n      for (size_t s = 0; s < uintanal.size(); ++s) {\n        CHECK(uintanal[s] == approx(test_interp[s]));\n        CHECK(uintanal[s] == approx(test_interp_2[s]));\n      }\n    }\n    if (spectral_stride == 1) {\n      auto test_interp = ylm_spherepack.interpolate_from_coefs(u_spec, points);\n      auto test_interp_2 =\n          ylm_spherepack_2.interpolate_from_coefs(u_spec, points);\n      for (size_t s = 0; s < uintanal.size(); ++s) {\n        CHECK(uintanal[s] == approx(test_interp[s]));\n        CHECK(uintanal[s] == approx(test_interp_2[s]));\n      }\n    }\n  }\n}\n\nvoid test_integral(const size_t l_max, const size_t m_max,\n                   const size_t physical_stride, const size_t spectral_stride,\n                   const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  Spherepack ylm_spherepack(l_max, m_max);\n  const size_t physical_size = ylm_spherepack.physical_size() * physical_stride;\n  const size_t spectral_size = ylm_spherepack.spectral_size() * spectral_stride;\n\n  const auto& theta = ylm_spherepack.theta_points();\n  const auto& phi = ylm_spherepack.phi_points();\n\n  DataVector u(physical_size);\n  DataVector u_spec(spectral_size);\n\n  // Fill with analytic function\n  func.func(&u, physical_stride, 0, theta, phi);\n\n  // Evaluate spectral coefficients of initial scalar function\n  ylm_spherepack.phys_to_spec(u_spec.data(), u.data(), physical_stride, 0,\n                              spectral_stride, 0);\n\n  // Test integral\n  if (physical_stride == 1) {\n    const double integral1 = ylm_spherepack.definite_integral(u.data());\n    const auto& weights = ylm_spherepack.integration_weights();\n    double integral2 = 0.0;\n    for (size_t s = 0; s < physical_size; ++s) {\n      integral2 += u[s] * weights[s];\n    }\n    const double integral_test = func.integral();\n    CHECK(integral1 == approx(integral_test));\n    CHECK(integral2 == approx(integral_test));\n  }\n\n  // Test average\n  if (spectral_stride == 1) {\n    const auto avg = ylm_spherepack.average(u_spec);\n    const double avg_test = func.integral() / (4.0 * M_PI);\n    CHECK(avg == approx(avg_test));\n  }\n\n  // Test add_constant\n  if (spectral_stride == 1) {\n    const double value_to_add = 1.367;\n    DataVector u_spec_plus_value = u_spec;\n    ylm_spherepack.add_constant(&u_spec_plus_value, value_to_add);\n    const auto avg = ylm_spherepack.average(u_spec_plus_value);\n    const double avg_test = func.integral() / (4.0 * M_PI) + value_to_add;\n    CHECK(avg == approx(avg_test));\n  }\n}\n\nvoid test_Spherepack(const size_t l_max, const size_t m_max,\n                     const size_t physical_stride, const size_t spectral_stride,\n                     const YlmTestFunctions::ScalarFunctionWithDerivs& func) {\n  test_phys_to_spec(l_max, m_max, physical_stride, spectral_stride, func);\n  test_gradient(l_max, m_max, physical_stride, spectral_stride, func);\n  test_second_derivative(l_max, m_max, physical_stride, spectral_stride, func);\n  test_scalar_laplacian(l_max, m_max, physical_stride, spectral_stride, func);\n  test_interpolation(l_max, m_max, physical_stride, spectral_stride, func);\n  test_integral(l_max, m_max, physical_stride, spectral_stride, func);\n  if (physical_stride == 1) {\n    test_theta_phi_points(l_max, m_max, func);\n  }\n}\n\nvoid test_memory_pool() {\n  const size_t n_pts = 100;\n  Spherepack_detail::MemoryPool pool;\n\n  // Fill all the temps.\n  std::vector<double>& tmp1 = pool.get(n_pts);\n  std::vector<double>& tmp2 = pool.get(n_pts);\n  std::vector<double>& tmp3 = pool.get(n_pts);\n  std::vector<double>& tmp4 = pool.get(n_pts);\n  std::vector<double>& tmp5 = pool.get(n_pts);\n  std::vector<double>& tmp6 = pool.get(n_pts);\n  std::vector<double>& tmp7 = pool.get(n_pts);\n  std::vector<double>& tmp8 = pool.get(n_pts);\n  std::vector<double>& tmp9 = pool.get(n_pts);\n\n  // Allocate more than the number of available temps\n  CHECK_THROWS_WITH((pool.get(n_pts)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Attempt to allocate more than 9 temps.\"));\n\n  // Clear too early.\n  CHECK_THROWS_WITH((pool.clear()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Attempt to clear element that is in use\"));\n\n  // Free all the temps (not necessarily in the same order as get).\n  pool.free(tmp1);\n  pool.free(tmp3);\n  pool.free(tmp2);\n  pool.free(tmp5);\n  pool.free(tmp4);\n  pool.free(tmp6);\n  pool.free(tmp8);\n  pool.free(tmp9);\n  pool.free(tmp7);\n\n  // Get a vector of a smaller size.  Here the vector returned will\n  // still have size n_pts, since there is only a resize when the\n  // vector is larger than the current size.\n  auto& vec1 = pool.get(n_pts / 2);\n  CHECK(vec1.size() == n_pts);\n  pool.free(vec1);\n\n  // Get a vector of a larger size.  Here the vector returned will\n  auto& vec2 = pool.get(n_pts * 2);\n  CHECK(vec2.size() == n_pts * 2);\n  pool.free(vec2);\n\n  // Clearing the temps resets all the sizes.\n  pool.clear();\n\n  // Now the size should be n_pts/2.\n  auto& vec3 = pool.get(n_pts / 2);\n  CHECK(vec3.size() == n_pts / 2);\n  pool.free(vec3);\n  pool.clear();\n\n  std::vector<double> dum1(1, 0.0);\n  CHECK_THROWS_WITH((pool.free(dum1)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Attempt to free temp that was never allocated.\"));\n  CHECK_THROWS_WITH((pool.free(make_not_null(dum1.data()))),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Attempt to free temp that was never allocated.\"));\n\n  std::vector<double> dum2;\n  CHECK_THROWS_WITH((pool.free(dum2)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Attempt to free temp that was never allocated.\"));\n}\n\nvoid test_ylm_errors() {\n  CHECK_THROWS_WITH((Spherepack(1, 1)), Catch::Matchers::ContainsSubstring(\n                                            \"Must use l_max>=2, not l_max=1\"));\n  CHECK_THROWS_WITH((Spherepack(2, 1)), Catch::Matchers::ContainsSubstring(\n                                            \"Must use m_max>=2, not m_max=1\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        Spherepack ylm(4, 3);\n        const auto interp_info =\n            ylm.set_up_interpolation_info(std::array<DataVector, 2>{\n                DataVector{0.1, 0.3}, DataVector{0.2, 0.3}});\n        Spherepack ylm_wrong_l_max(5, 3);\n        DataVector res{2};\n        // no need to initialize as the values should not be accessed\n        const DataVector spectral_values{ylm_wrong_l_max.spectral_size()};\n        ylm_wrong_l_max.interpolate_from_coefs(make_not_null(&res),\n                                               spectral_values, interp_info);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Different l_max for InterpolationInfo (4) \"\n          \"and Spherepack instance (5)\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        Spherepack ylm(4, 3);\n        const auto interp_info =\n            ylm.set_up_interpolation_info(std::array<DataVector, 2>{\n                DataVector{0.1, 0.3}, DataVector{0.2, 0.3}});\n        Spherepack ylm_wrong_m_max(4, 4);\n        DataVector res{2};\n        // no need to initialize as the values should not be accessed\n        const DataVector spectral_values{ylm_wrong_m_max.spectral_size()};\n        ylm_wrong_m_max.interpolate_from_coefs(make_not_null(&res),\n                                               spectral_values, interp_info);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Different m_max for InterpolationInfo (3) \"\n          \"and Spherepack instance (4)\"));\n}\n\ntemplate <size_t l, int m>\nvoid test_spherepack(const size_t n_r, const size_t L, const size_t M) {\n  CAPTURE(l);\n  CAPTURE(m);\n  CAPTURE(n_r);\n  CAPTURE(L);\n  CAPTURE(M);\n  const Mesh<3> mesh{\n      {n_r, L + 1, 2 * M + 1},\n      {Spectral::Basis::Legendre, Spectral::Basis::SphericalHarmonic,\n       Spectral::Basis::SphericalHarmonic},\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Gauss,\n       Spectral::Quadrature::Equiangular}};\n  const auto x = logical_coordinates(mesh);\n  const DataVector& theta = x[1];\n  const DataVector& phi = x[2];\n  const size_t n_pts = mesh.number_of_grid_points();\n  const YlmTestFunctions::Ylm<l, m> y_lm{n_r, L, M};\n  const DataVector expected_y_lm =\n      ylm::real_spherical_harmonic(theta, phi, l, m);\n  CHECK_ITERABLE_APPROX(y_lm.f(), expected_y_lm);\n  DataVector du_dth{n_pts, -1.0};\n  DataVector du_dph{n_pts, -1.0};\n  const auto du = std::array{du_dth.data(), du_dph.data()};\n  const ylm::Spherepack spherepack{L, M};\n  spherepack.gradient_all_offsets(du, y_lm.f().data(), n_r);\n  CHECK_ITERABLE_APPROX(du_dth, y_lm.df_dth());\n  CHECK_ITERABLE_APPROX(du_dph, y_lm.df_dph());\n  DataVector u_lm{spherepack.spectral_size() * n_r};\n  spherepack.phys_to_spec_all_offsets(u_lm.data(), y_lm.f().data(), n_r);\n  CHECK_ITERABLE_APPROX(u_lm, y_lm.modes());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Spherepack\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_spectre_cce_grid_point_locations();\n\n  test_memory_pool();\n  test_ylm_errors();\n\n  for (size_t l_max = 3; l_max < 5; ++l_max) {\n    for (size_t m_max = 2; m_max <= l_max; ++m_max) {\n      for (size_t physical_stride = 1; physical_stride <= 4;\n           physical_stride += 3) {\n        for (size_t spectral_stride = 1; spectral_stride <= 4;\n             spectral_stride += 3) {\n          test_Spherepack(l_max, m_max, physical_stride, spectral_stride,\n                          YlmTestFunctions::Y00());\n          test_Spherepack(l_max, m_max, physical_stride, spectral_stride,\n                          YlmTestFunctions::Y10());\n          test_Spherepack(l_max, m_max, physical_stride, spectral_stride,\n                          YlmTestFunctions::Y11());\n        }\n      }\n    }\n  }\n\n  for (size_t l_max = 3; l_max < 5; ++l_max) {\n    for (size_t m_max = 2; m_max <= l_max; ++m_max) {\n      test_loop_over_offset(l_max, m_max, 3, YlmTestFunctions::Y00());\n      test_loop_over_offset(l_max, m_max, 3, YlmTestFunctions::Y10());\n      test_loop_over_offset(l_max, m_max, 3, YlmTestFunctions::Y11());\n      test_loop_over_offset(l_max, m_max, 1, YlmTestFunctions::Y11());\n    }\n  }\n\n  test_prolong_restrict();\n\n  Spherepack s(4, 4);\n  auto s_copy(s);\n  CHECK(s_copy == s);\n  test_move_semantics(std::move(s), s_copy, 6_st, 5_st);\n\n  for (size_t n_r = 1; n_r < 5; ++n_r) {\n    for (size_t L = 2; L < 5; ++L) {\n      for (size_t M = 2; M <= L; ++M) {\n        test_spherepack<0, 0>(n_r, L, M);\n        test_spherepack<1, 0>(n_r, L, M);\n        test_spherepack<1, 1>(n_r, L, M);\n        test_spherepack<1, -1>(n_r, L, M);\n        if (L > 2) {\n          test_spherepack<2, 0>(n_r, L, M);\n          test_spherepack<2, 1>(n_r, L, M);\n          test_spherepack<2, -1>(n_r, L, M);\n          test_spherepack<2, 2>(n_r, L, M);\n          test_spherepack<2, -2>(n_r, L, M);\n        }\n      }\n    }\n  }\n}\n\n}  // namespace ylm\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_SpherepackIterator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\nvoid test_spherepack_iterator(const std::vector<size_t>& test_l,\n                              const std::vector<size_t>& test_m,\n                              const std::vector<size_t>& test_index,\n                              const bool zero_m_is_real) {\n  // [spherepack_iterator_example]\n  const size_t l_max = 4;\n  const size_t m_max = 2;\n  const size_t stride = 5;\n  ylm::SpherepackIterator iter(l_max, m_max, stride, zero_m_is_real);\n  // Allocate space for a SPHEREPACK array\n  std::vector<double> array(iter.spherepack_array_size() * stride);\n  // Set each array element equal to l+m for real part\n  // and l-m for imaginary part.\n  size_t i = 0;\n  for (iter.reset(); iter; ++iter, ++i) {\n    if (iter.coefficient_array() ==\n        ylm::SpherepackIterator::CoefficientArray::a) {\n      array[iter()] = iter.l() + iter.m();\n    } else {\n      array[iter()] = iter.l() - iter.m();\n    }\n    CHECK(iter.l() == test_l[i]);\n    CHECK(iter.m() == test_m[i]);\n    CHECK(iter() == test_index[i]);\n  }\n  // [spherepack_iterator_example]\n  CHECK(iter.l_max() == 4);\n  CHECK(iter.m_max() == 2);\n  CHECK(iter.n_th() == 5);\n  CHECK(iter.n_ph() == 5);\n  for (i = 0; i < test_index.size(); ++i) {\n    auto j = test_index[i];\n    if (i > 11) {  // For specific test_index chosen above.\n      // imag part\n      CHECK(array[j] == test_l[i] - test_m[i]);\n    } else {\n      // real part\n      CHECK(array[j] == test_l[i] + test_m[i]);\n    }\n  }\n\n  // Check compact index\n  iter.reset();\n  for (size_t k = 0; k < array.size(); k++) {\n    const auto compact_index = iter.compact_index(k);\n    const size_t current_compact_index = iter.current_compact_index();\n    if (compact_index) {\n      CHECK(*compact_index == current_compact_index);\n      ++iter;\n    }\n  }\n\n  // Test set functions\n  CHECK(iter.set(2, 1, ylm::SpherepackIterator::CoefficientArray::b)() ==\n        (zero_m_is_real ? test_index[13] : test_index[16]));\n  // Test the set function for the case l>m_max+1\n  CHECK(iter.set(4, 1, ylm::SpherepackIterator::CoefficientArray::a)() ==\n        test_index[10]);\n  CHECK(iter.set(4, 1, ylm::SpherepackIterator::CoefficientArray::b)() ==\n        (zero_m_is_real ? test_index[17] : test_index[22]));\n  CHECK(iter.reset()() == 0);\n  CHECK(iter.set(2, 1)() == test_index[4]);\n  CHECK(iter.set(2, -1)() ==\n        (zero_m_is_real ? test_index[13] : test_index[16]));\n  // Test setting the current compact index\n  CHECK(iter.set(0)() == test_index[0]);\n  CHECK(iter.set(3)() == test_index[3]);\n  CHECK(iter.set(18)() == test_index[18]);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([&iter]() { iter.set(100); })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Trying to set the current compact index to 100 which is \"\n          \"beyond the size of the offset array\"));\n#endif  // SPECTRE_DEBUG\n\n  // Test coefficient_array stream operator (assumes output of last 'set').\n  CHECK(get_output(iter.coefficient_array()) == \"b\");\n\n  // Test inequality\n  const ylm::SpherepackIterator iter2(3, 2, 5,\n                                      zero_m_is_real);  // Different lmax,mmax\n  const ylm::SpherepackIterator iter3(4, 2, 4,\n                                      zero_m_is_real);  // Different stride\n  const ylm::SpherepackIterator iter4(\n      4, 2, 5,\n      zero_m_is_real);  // Different current state\n  CHECK(iter2 != iter);\n  CHECK(iter != iter2);\n  CHECK(iter != iter3);\n  CHECK(iter3 != iter);\n  CHECK(iter4 != iter);\n  CHECK(iter != iter4);\n\n  const auto iter_copy = iter;\n  CHECK(iter_copy == iter);\n  test_move_semantics(std::move(iter), iter_copy, 3_st, 2_st, 3_st);\n}\n}  // namespace\n\nnamespace ylm {\n\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.SpherepackIterator\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test_spherepack_iterator(\n      {{0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 1, 2, 2, 3, 3, 4, 4}},\n      {{0, 0, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2, 1, 1, 2, 1, 2, 1, 2}},\n      {{0, 15, 20, 30, 35, 40, 45, 50, 55, 60, 65, 70, 95, 110, 115, 125, 130,\n        140, 145}},\n      true);\n  test_spherepack_iterator(\n      {{0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4,\n        0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4}},\n      {{0, 0, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2,\n        0, 0, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2}},\n      {{0,  15, 20, 30,  35,  40,  45,  50,  55,  60,  65,  70,\n        75, 90, 95, 105, 110, 115, 120, 125, 130, 135, 140, 145}},\n      false);\n}\n}  // namespace ylm\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_Strahlkorper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/StrahlkorperTestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"NumericalAlgorithms/RootFinding/QuadraticEquation.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nnamespace ylm {\nnamespace {\nvoid test_invert_spec_phys_transform() {\n  const double avg_radius = 1.0;\n  const double delta_radius = 0.1;\n  const size_t l_grid = 33;\n  const auto l_grid_high_res = static_cast<size_t>(l_grid * 1.5);\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n\n  // Create radius as a function of angle\n  DataVector radius(ylm::Spherepack::physical_size(l_grid, l_grid), avg_radius);\n  {\n    std::uniform_real_distribution<double> ran(0.0, 1.0);\n    MAKE_GENERATOR(gen);\n    for (auto& r : radius) {\n      r += delta_radius * ran(gen);\n    }\n  }\n  CAPTURE(radius);\n\n  // Initialize a strahlkorper of l_max=l_grid\n  const Strahlkorper<Frame::Inertial> sk(l_grid, l_grid, radius, center);\n\n  // Put that Strahlkorper onto a larger grid\n  const Strahlkorper<Frame::Inertial> sk_high_res(l_grid_high_res,\n                                                  l_grid_high_res, sk);\n\n  // Compare coefficients\n  SpherepackIterator iter(sk.l_max(), sk.m_max());\n  SpherepackIterator iter_high_res(sk_high_res.l_max(), sk_high_res.m_max());\n  const auto& init_coefs = sk.coefficients();\n  const auto& final_coefs = sk_high_res.coefficients();\n\n  for (size_t l = 0; l <= sk.ylm_spherepack().l_max(); ++l) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); ++m) {\n      CHECK(init_coefs[iter.set(l, m)()] ==\n            approx(final_coefs[iter_high_res.set(l, m)()]));\n    }\n  }\n\n  for (size_t l = sk.ylm_spherepack().l_max() + 1;\n       l <= sk_high_res.ylm_spherepack().l_max(); ++l) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); ++m) {\n      CHECK(final_coefs[iter_high_res.set(l, m)()] == approx(0.0));\n    }\n  }\n}\n\nvoid test_phys_spec_constructor_consistency() {\n  const size_t l_max = 12;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const size_t physical_size = ylm::Spherepack::physical_size(l_max, l_max);\n\n  std::uniform_real_distribution<double> distribution(0.0, 1.0);\n  MAKE_GENERATOR(generator);\n\n  // create Strahlkorper using radius at collocation points\n  const auto radius = make_with_random_values<DataVector>(\n      make_not_null(&generator), distribution,\n      DataVector(physical_size, std::numeric_limits<double>::signaling_NaN()));\n  const Strahlkorper<Frame::Inertial> strahlkorper_physical(l_max, l_max,\n                                                            radius, center);\n\n  // create Strahlkorper using spectral coefficients\n  const size_t spectral_size = ylm::Spherepack::spectral_size(l_max, l_max);\n  ModalVector spectral_coefficients(\n      spectral_size, std::numeric_limits<double>::signaling_NaN());\n  std::copy(strahlkorper_physical.coefficients().begin(),\n            strahlkorper_physical.coefficients().end(),\n            spectral_coefficients.begin());\n  const Strahlkorper<Frame::Inertial> strahlkorper_spectral(\n      l_max, l_max, spectral_coefficients, center);\n\n  CHECK(strahlkorper_physical == strahlkorper_spectral);\n}\n\nvoid test_average_radius() {\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const double r = 3.0;\n  Strahlkorper<Frame::Inertial> s(4, 4, r, center);\n  CHECK(s.average_radius() == approx(r));\n}\n\nvoid test_copy_and_move() {\n  Strahlkorper<Frame::Inertial> s(4, 4, 3.0, {{0.1, 0.2, 0.3}});\n\n  test_copy_semantics(s);\n  auto s_copy = s;\n  test_move_semantics(std::move(s), s_copy);\n}\n\nvoid test_physical_center() {\n  const std::array<double, 3> physical_center = {{1.5, 0.5, 1.0}};\n  const std::array<double, 3> expansion_center = {{0.0, 0.0, 0.0}};\n  const double radius = 5.0;\n  const int l_max = 9;\n\n  Strahlkorper<Frame::Inertial> sk(l_max, l_max, radius, expansion_center);\n  DataVector r(sk.ylm_spherepack().physical_size(), 0.);\n\n  for (size_t s = 0; s < r.size(); ++s) {\n    const double theta = sk.ylm_spherepack().theta_phi_points()[0][s];\n    const double phi = sk.ylm_spherepack().theta_phi_points()[1][s];\n    // Compute the distance (radius as a function of theta,phi) from\n    // the expansion_center to a spherical surface of radius `radius`\n    // centered at physical_center.\n    const double a = 1.0;\n    const double b = -2.0 * cos(phi) * sin(theta) * physical_center[0] -\n                     2.0 * sin(phi) * sin(theta) * physical_center[1] -\n                     2.0 * cos(theta) * physical_center[2];\n    const double c = square(physical_center[0]) + square(physical_center[1]) +\n                     square(physical_center[2]) - square(radius);\n    auto roots = *real_roots(a, b, c);\n    r[s] = std::max(roots[0], roots[1]);\n  }\n  // Construct a new Strahlkorper sk_test with the radius computed\n  // above, centered at expansion_center, so that\n  // sk_test.physical_center() should recover the physical center of\n  // this surface.\n  Strahlkorper<Frame::Inertial> sk_test(l_max, l_max, r, expansion_center);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(approx(gsl::at(physical_center, i)) ==\n          gsl::at(sk_test.physical_center(), i));\n  }\n}\n\nvoid test_point_is_contained() {\n  // Construct a spherical Strahlkorper\n  const double radius = 2.;\n  const std::array<double, 3> center = {{-1.2, 3., 4.}};\n  const Strahlkorper<Frame::Inertial> sphere(3, 2, radius, center);\n\n  // Check whether two known points are contained.\n  const std::array<double, 3> point_inside = {{-1.2, 1.01, 4.}};\n  const std::array<double, 3> point_outside = {{-1.2, 3., 6.01}};\n  CHECK(sphere.point_is_contained(point_inside));\n  CHECK_FALSE(sphere.point_is_contained(point_outside));\n}\n\ntemplate <typename Func>\nvoid test_constructor_with_different_coefs(Func function) {\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const double r = 3.0;\n  const double add_to_r = 1.34;\n  Strahlkorper<Frame::Inertial> strahlkorper(4, 4, r, center);\n  const Strahlkorper<Frame::Inertial> strahlkorper_test1(4, 4, r + add_to_r,\n                                                         center);\n\n  // Modify the 0,0 coefficient to add a constant to the radius.\n  const auto strahlkorper_test2 = function(strahlkorper, add_to_r);\n\n  CHECK_ITERABLE_APPROX(strahlkorper_test1.coefficients(),\n                        strahlkorper_test2.coefficients());\n}\n\n// Helper function to create a random Strahlkorper and write it to an H5 file\nStrahlkorper<Frame::Inertial> create_and_write_test_strahlkorper(\n    const std::string& filename, const std::string& subfile_name,\n    const size_t l_max, const std::array<double, 3>& center,\n    const double time) {\n  MAKE_GENERATOR(gen);\n  const std::uniform_real_distribution<> dist(0.5, 2.0);\n  const auto radius = make_with_random_values<DataVector>(\n      make_not_null(&gen), dist,\n      DataVector(ylm::Spherepack::physical_size(l_max, l_max),\n                 std::numeric_limits<double>::signaling_NaN()));\n  Strahlkorper<Frame::Inertial> strahlkorper(l_max, l_max, radius, center);\n\n  h5::H5File<h5::AccessType::ReadWrite> h5_file(filename, true);\n  std::vector<std::string> legend{};\n  std::vector<double> data{};\n  ylm::fill_ylm_legend_and_data(make_not_null(&legend), make_not_null(&data),\n                                strahlkorper, time, l_max);\n  auto& dat_file = h5_file.insert<h5::Dat>(\"/\" + subfile_name, legend);\n  dat_file.append(std::vector<std::vector<double>>{data});\n  h5_file.close_current_object();\n\n  return strahlkorper;\n}\n\n// Helper function to parse Strahlkorper options from a string\nStrahlkorper<Frame::Inertial> parse_strahlkorper_from_options(\n    const std::string& options_string) {\n  Options::Parser<tmpl::list<OptionTags::Strahlkorper<Frame::Inertial>>> opts(\n      \"\");\n  opts.parse(options_string);\n  return opts.get<OptionTags::Strahlkorper<Frame::Inertial>>();\n}\n\nvoid test_construct_from_options() {\n  // Test construction from Radius and Center\n  {\n    CHECK(parse_strahlkorper_from_options(\"Strahlkorper:\\n\"\n                                          \" LMax : 6\\n\"\n                                          \" Center: [1.,2.,3.]\\n\"\n                                          \" Radius: 4.5\\n\") ==\n          Strahlkorper<Frame::Inertial>(6, 6, 4.5, {{1., 2., 3.}}));\n  }\n\n  // Test construction from file\n  {\n    const std::string test_filename = \"TestStrahlkorperOptions.h5\";\n    const std::string subfile_name = \"TestSurface_Ylm\";\n    const std::array<double, 3> expansion_center{{1.5, -0.5, 2.0}};\n    const size_t l_max_original = 4;\n    const double time = 1.23;\n\n    if (file_system::check_if_file_exists(test_filename)) {\n      file_system::rm(test_filename, true);\n    }\n\n    const auto original_strahlkorper = create_and_write_test_strahlkorper(\n        test_filename, subfile_name, l_max_original, expansion_center, time);\n\n    // Test reading from file with the same l_max (no prolong/restrict)\n    {\n      const auto read_strahlkorper = parse_strahlkorper_from_options(\n          \"Strahlkorper:\\n\"\n          \" LMax : 4\\n\"\n          \" H5Filename: TestStrahlkorperOptions.h5\\n\"\n          \" SubfileName: TestSurface_Ylm\\n\"\n          \" Time: 1.23\\n\"\n          \" TimeEpsilon: 1.0e-10\\n\"\n          \" CheckFrame: true\\n\");\n\n      CHECK(read_strahlkorper.l_max() == l_max_original);\n      CHECK(read_strahlkorper.m_max() == l_max_original);\n      CHECK(read_strahlkorper.expansion_center() == expansion_center);\n      CHECK_ITERABLE_APPROX(read_strahlkorper.coefficients(),\n                            original_strahlkorper.coefficients());\n    }\n\n    // Test reading from file with prolong to higher l_max\n    {\n      const size_t l_max_requested = 6;\n      const auto read_strahlkorper = parse_strahlkorper_from_options(\n          \"Strahlkorper:\\n\"\n          \" LMax : 6\\n\"\n          \" H5Filename: TestStrahlkorperOptions.h5\\n\"\n          \" SubfileName: TestSurface_Ylm\\n\"\n          \" Time: 1.23\\n\"\n          \" TimeEpsilon: 1.0e-10\\n\"\n          \" CheckFrame: true\\n\");\n\n      CHECK(read_strahlkorper.l_max() == l_max_requested);\n      CHECK(read_strahlkorper.m_max() == l_max_requested);\n      CHECK(read_strahlkorper.expansion_center() == expansion_center);\n\n      const Strahlkorper<Frame::Inertial> expected_prolonged(\n          l_max_requested, l_max_requested, original_strahlkorper);\n      CHECK_ITERABLE_APPROX(read_strahlkorper.coefficients(),\n                            expected_prolonged.coefficients());\n    }\n\n    // Test reading from file with restrict to lower l_max\n    {\n      const auto read_strahlkorper = parse_strahlkorper_from_options(\n          \"Strahlkorper:\\n\"\n          \" LMax : 2\\n\"\n          \" H5Filename: TestStrahlkorperOptions.h5\\n\"\n          \" SubfileName: TestSurface_Ylm\\n\"\n          \" Time: 1.23\\n\"\n          \" TimeEpsilon: 1.0e-10\\n\"\n          \" CheckFrame: true\\n\");\n\n      CHECK(read_strahlkorper.l_max() == 2);\n      CHECK(read_strahlkorper.m_max() == 2);\n      CHECK(read_strahlkorper.expansion_center() == expansion_center);\n\n      const Strahlkorper<Frame::Inertial> expected_restricted(\n          2, 2, original_strahlkorper);\n      CHECK_ITERABLE_APPROX(read_strahlkorper.coefficients(),\n                            expected_restricted.coefficients());\n    }\n\n    file_system::rm(test_filename, true);\n  }\n\n  // Test failure case: missing H5 file\n  {\n    Options::Parser<tmpl::list<OptionTags::Strahlkorper<Frame::Inertial>>> opts(\n        \"\");\n    opts.parse(\n        \"Strahlkorper:\\n\"\n        \" LMax : 4\\n\"\n        \" H5Filename: NonexistentFile.h5\\n\"\n        \" SubfileName: TestSurface_Ylm\\n\"\n        \" Time: 1.23\\n\"\n        \" TimeEpsilon: 1.0e-10\\n\"\n        \" CheckFrame: true\\n\");\n    CHECK_THROWS_WITH(opts.get<OptionTags::Strahlkorper<Frame::Inertial>>(),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Trying to open the file 'NonexistentFile.h5'\") &&\n                          Catch::Matchers::ContainsSubstring(\"does not exist\"));\n  }\n\n  // Test failure case: error reading from file (invalid subfile)\n  {\n    const std::string test_filename = \"TestStrahlkorperOptionsFailure.h5\";\n\n    if (file_system::check_if_file_exists(test_filename)) {\n      file_system::rm(test_filename, true);\n    }\n\n    create_and_write_test_strahlkorper(test_filename, \"ValidSurface\", 4,\n                                       {{1.5, -0.5, 2.0}}, 1.23);\n\n    Options::Parser<tmpl::list<OptionTags::Strahlkorper<Frame::Inertial>>> opts(\n        \"\");\n    opts.parse(\n        \"Strahlkorper:\\n\"\n        \" LMax : 4\\n\"\n        \" H5Filename: TestStrahlkorperOptionsFailure.h5\\n\"\n        \" SubfileName: InvalidSurface\\n\"\n        \" Time: 1.23\\n\"\n        \" TimeEpsilon: 1.0e-10\\n\"\n        \" CheckFrame: true\\n\");\n    CHECK_THROWS_WITH(\n        opts.get<OptionTags::Strahlkorper<Frame::Inertial>>(),\n        Catch::Matchers::ContainsSubstring(\"Cannot open the object\"));\n\n    file_system::rm(test_filename, true);\n  }\n\n  // Test failure case: time differs by more than epsilon.\n  {\n    const std::string test_filename = \"TestStrahlkorperOptionsTimeEpsilon.h5\";\n\n    if (file_system::check_if_file_exists(test_filename)) {\n      file_system::rm(test_filename, true);\n    }\n\n    create_and_write_test_strahlkorper(test_filename, \"TestSurface_Ylm\", 4,\n                                       {{1.5, -0.5, 2.0}}, 1.23);\n\n    Options::Parser<tmpl::list<OptionTags::Strahlkorper<Frame::Inertial>>> opts(\n        \"\");\n    opts.parse(\n        \"Strahlkorper:\\n\"\n        \" LMax : 4\\n\"\n        \" H5Filename: TestStrahlkorperOptionsTimeEpsilon.h5\\n\"\n        \" SubfileName: TestSurface_Ylm\\n\"\n        \" Time: 1.2300000003\\n\"  // Differs by 3.0e-10 from actual time\n        \" TimeEpsilon: 1.0e-10\\n\"\n        \" CheckFrame: true\\n\");\n    CHECK_THROWS_WITH(\n        opts.get<OptionTags::Strahlkorper<Frame::Inertial>>(),\n        Catch::Matchers::ContainsSubstring(\"Could not find time\"));\n\n    file_system::rm(test_filename, true);\n  }\n}\n\nvoid test_strahlkorper_from_other_strahlkorper() {\n  const Strahlkorper<Frame::Inertial> inertial_strahlkorper{\n      4_st, 1.2, std::array{1.0, 2.0, 3.0}};\n  Strahlkorper<Frame::Grid> grid_strahlkorper{inertial_strahlkorper};\n\n  const auto check_equal = [](const auto& s1, const auto& s2) {\n    CHECK(s1.coefficients() == s2.coefficients());\n    CHECK(s1.l_max() == s2.l_max());\n    CHECK(s1.m_max() == s2.m_max());\n    CHECK(s1.expansion_center() == s2.expansion_center());\n  };\n\n  check_equal(inertial_strahlkorper, grid_strahlkorper);\n\n  grid_strahlkorper = Strahlkorper<Frame::Grid>{\n      inertial_strahlkorper.coefficients(), inertial_strahlkorper};\n\n  check_equal(inertial_strahlkorper, grid_strahlkorper);\n\n  grid_strahlkorper =\n      Strahlkorper<Frame::Grid>{8_st, 8_st, inertial_strahlkorper};\n\n  check_equal(Strahlkorper<Frame::Grid>(8_st, 1.2, std::array{1.0, 2.0, 3.0}),\n              grid_strahlkorper);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Strahlkorper\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_invert_spec_phys_transform();\n  test_phys_spec_constructor_consistency();\n  test_copy_and_move();\n  test_average_radius();\n  test_physical_center();\n  test_point_is_contained();\n  test_constructor_with_different_coefs(\n      [](Strahlkorper<Frame::Inertial>& sk, double add_to_r) {\n        auto coefs = sk.coefficients();  // make a copy\n        coefs[0] += sqrt(8.0) * add_to_r;\n        return Strahlkorper<Frame::Inertial>(coefs, sk);\n      });\n  test_constructor_with_different_coefs(\n      [](const Strahlkorper<Frame::Inertial>& sk, double add_to_r) {\n        auto coefs = sk.coefficients();  // make a copy\n        coefs[0] += sqrt(8.0) * add_to_r;\n        return Strahlkorper<Frame::Inertial>(coefs, sk);\n      });\n  test_constructor_with_different_coefs(\n      [](Strahlkorper<Frame::Inertial>& sk, double add_to_r) {\n        auto& coefs = sk.coefficients();  // no copy\n        coefs[0] += sqrt(8.0) * add_to_r;\n        return Strahlkorper<Frame::Inertial>(std::move(sk));\n      });\n  test_construct_from_options();\n  test_strahlkorper_from_other_strahlkorper();\n  {\n    Strahlkorper<Frame::Inertial> s(4, 4, 2.0, {{1.0, 2.0, 3.0}});\n    test_serialization(s);\n  }\n}\n}  // namespace ylm\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_StrahlkorperDataBox.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <climits>\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/StrahlkorperTestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test_normals() {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper =\n      ylm::TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n\n  const auto theta_phi = strahlkorper.ylm_spherepack().theta_phi_points();\n  const auto n_pts = theta_phi[0].size();\n\n  const double amp = -sqrt(3.0 / 8.0 / M_PI) * y11_amplitude;\n  const auto& theta = theta_phi[0];\n  const auto& phi = theta_phi[1];\n  const DataVector cos_phi = cos(phi);\n  const DataVector sin_phi = sin(phi);\n  const DataVector cos_theta = cos(theta);\n  const DataVector sin_theta = sin(theta);\n\n  auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  // Test surface_normal_magnitude.\n  tnsr::II<DataVector, 3, Frame::Inertial> invg(n_pts);\n  invg.get(0, 0) = 1.0;\n  invg.get(1, 0) = 0.1;\n  invg.get(2, 0) = 0.2;\n  invg.get(1, 1) = 2.0;\n  invg.get(1, 2) = 0.3;\n  invg.get(2, 2) = 3.0;\n\n  const auto expected_normal_mag = [&]() -> DataVector {\n    const auto& r = get(db::get<ylm::Tags::Radius<Frame::Inertial>>(box));\n\n    // Nasty expression Mark Scheel computed in Mathematica.\n    const DataVector normsquared =\n        (-0.3 * cos_theta * (r + amp * sin_phi * sin_theta) *\n             (1. * amp * square(cos_phi) +\n              1. * amp * square(cos_theta) * square(sin_phi) -\n              1. * r * sin_phi * sin_theta +\n              cos_phi * (2.0 / 3.0) *\n                  (amp * (-1. + square(cos_theta)) * sin_phi - r * sin_theta) +\n              cos_theta * (-10. * r - 10. * amp * sin_phi * sin_theta)) +\n         0.1 * cos_phi *\n             (amp * (-1. + 1. * square(cos_theta)) * sin_phi -\n              1. * r * sin_theta) *\n             (1. * amp * square(cos_phi) +\n              1. * amp * square(cos_theta) * square(sin_phi) -\n              1. * r * sin_phi * sin_theta +\n              cos_phi * (amp * (-10. + 10. * square(cos_theta)) * sin_phi -\n                         10. * r * sin_theta) +\n              cos_theta * (-2. * r - 2. * amp * sin_phi * sin_theta)) +\n         2. *\n             (amp * square(cos_phi) +\n              sin_phi *\n                  (amp * square(cos_theta) * sin_phi - 1. * r * sin_theta)) *\n             (amp * square(cos_phi) +\n              amp * square(cos_theta) * square(sin_phi) -\n              1. * r * sin_phi * sin_theta +\n              cos_phi * 0.05 *\n                  (amp * (-1. + square(cos_theta)) * sin_phi - r * sin_theta) +\n              cos_theta * (-0.15 * r - 0.15 * amp * sin_phi * sin_theta))) /\n        square(r);\n    return sqrt(normsquared);\n  }();\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const auto normal_mag = magnitude(normal_one_form, invg);\n  CHECK_ITERABLE_APPROX(expected_normal_mag, get(normal_mag));\n}\n\nvoid test_max_ricci_scalar() {\n  // Test max_ricci_scalar\n  const Scalar<DataVector> d{{{{1., 2., 3.}}}};\n  const double expected_max{3.};\n  double max{std::numeric_limits<double>::signaling_NaN()};\n  ylm::Tags::MaxRicciScalarCompute::function(make_not_null(&max), d);\n  CHECK(expected_max == max);\n}\n\nvoid test_min_ricci_scalar() {\n  // Test min_ricci_scalar\n  const Scalar<DataVector> d{{{{1., 2., 3.}}}};\n  const double expected_min{1.};\n  double min{std::numeric_limits<double>::signaling_NaN()};\n  ylm::Tags::MinRicciScalarCompute::function(make_not_null(&min), d);\n  CHECK(expected_min == min);\n}\n\nvoid test_dimensionful_spin_vector_compute_tag() {\n  const double y11_amplitude = 1.0;\n  const double y11_radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper = ylm::TestHelpers::create_strahlkorper_y11(\n      y11_amplitude, y11_radius, center);\n  const size_t ylm_physical_size =\n      strahlkorper.ylm_spherepack().physical_size();\n  const DataVector used_for_size(ylm_physical_size,\n                                 std::numeric_limits<double>::signaling_NaN());\n\n  // Creates a variable named generator that can be used to generate random\n  // values\n  MAKE_GENERATOR(generator);\n  // Creates a uniform distribution, which will be used to generate random\n  // numbers\n  std::uniform_real_distribution<> dist(-1., 1.);  // NOLINT\n\n  const double dimensionful_spin_magnitude{5.0};\n  // Create the tensor arguments to spin_vector by having them contain\n  // DataVectors of size == ylm physical size, where values are random\n  const auto area_element = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), dist, used_for_size);\n  const auto radius = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), dist, used_for_size);\n  const auto r_hat =\n      make_with_random_values<tnsr::i<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), dist, used_for_size);\n  const auto ricci_scalar = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), dist, used_for_size);\n  const auto spin_function = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), dist, used_for_size);\n  const auto strahlkorper_cartesian_coords =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), dist, used_for_size);\n  const auto box = db::create<\n      db::AddSimpleTags<\n          gr::surfaces::Tags::DimensionfulSpinMagnitude,\n          gr::surfaces::Tags::AreaElement<Frame::Inertial>,\n          ylm::Tags::Radius<Frame::Inertial>, ylm::Tags::Rhat<Frame::Inertial>,\n          ylm::Tags::RicciScalar, gr::surfaces::Tags::SpinFunction,\n          ylm::Tags::Strahlkorper<Frame::Inertial>,\n          ylm::Tags::CartesianCoords<Frame::Inertial>>,\n      db::AddComputeTags<gr::surfaces::Tags::DimensionfulSpinVectorCompute<\n          Frame::Inertial, Frame::Inertial>>>(\n      dimensionful_spin_magnitude, area_element, radius, r_hat, ricci_scalar,\n      spin_function, strahlkorper, strahlkorper_cartesian_coords);\n  // LHS of the == in the CHECK is retrieving the computed dimensionful spin\n  // vector from your DimensionfulSpinVectorCompute tag and RHS of ==\n  // should be same logic as DimensionfulSpinVectorCompute::function\n  CHECK(db::get<gr::surfaces::Tags::DimensionfulSpinVector<Frame::Inertial>>(\n            box) ==\n        gr::surfaces::spin_vector<Frame::Inertial, Frame::Inertial>(\n            dimensionful_spin_magnitude, area_element, ricci_scalar,\n            spin_function, strahlkorper, strahlkorper_cartesian_coords));\n}\n\nvoid test_dimensionless_spin_magnitude_compute_tag() {\n  const double dimensionful_spin_magnitude{5.0};\n  const double christodoulou_mass = 4.444;\n\n  const auto box = db::create<\n      db::AddSimpleTags<gr::surfaces::Tags::DimensionfulSpinMagnitude,\n                        gr::surfaces::Tags::ChristodoulouMass>,\n      db::AddComputeTags<gr::surfaces::Tags::DimensionlessSpinMagnitudeCompute<\n          Frame::Inertial>>>(dimensionful_spin_magnitude, christodoulou_mass);\n  // LHS of the == in the CHECK is retrieving the computed dimensionless spin\n  // magnitude from your DimensionlessSpinMagnitudeCompute tag and RHS of ==\n  // should be same logic as DimensionlessSpinMagnitudeCompute::function\n  CHECK(\n      db::get<gr::surfaces::Tags::DimensionlessSpinMagnitude<Frame::Inertial>>(\n          box) ==\n      gr::surfaces::dimensionless_spin_magnitude(dimensionful_spin_magnitude,\n                                                 christodoulou_mass));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.StrahlkorperDataBox\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_normals();\n  test_max_ricci_scalar();\n  test_min_ricci_scalar();\n  test_dimensionful_spin_vector_compute_tag();\n  test_dimensionless_spin_magnitude_compute_tag();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_StrahlkorperFunctions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <deque>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/StrahlkorperTestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nnamespace ylm {\nnamespace {\nvoid test_radius_and_derivs() {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper =\n      TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n\n  // Now construct a Y00 + Im(Y11) surface by hand.\n  const auto& theta_points = strahlkorper.ylm_spherepack().theta_points();\n  const auto& phi_points = strahlkorper.ylm_spherepack().phi_points();\n  const auto n_pts = theta_points.size() * phi_points.size();\n  YlmTestFunctions::Y11 y_11;\n  DataVector expected_radius{n_pts};\n  y_11.func(&expected_radius, 1, 0, theta_points, phi_points);\n  for (size_t s = 0; s < n_pts; ++s) {\n    expected_radius[s] *= y11_amplitude;\n    expected_radius[s] += radius;\n  }\n\n  const DataVector strahlkorper_radius{get(ylm::radius(strahlkorper))};\n  CHECK_ITERABLE_APPROX(strahlkorper_radius, expected_radius);\n\n  // Test derivative of radius\n  const auto theta_phi = strahlkorper.ylm_spherepack().theta_phi_points();\n  tnsr::i<DataVector, 3> expected_dx_radius(n_pts);\n  for (size_t s = 0; s < n_pts; ++s) {\n    // Analytic solution Mark Scheel computed in Mathematica\n    const double theta = theta_phi[0][s];\n    const double phi = theta_phi[1][s];\n    const double r = expected_radius[s];\n    const double sin_phi = sin(phi);\n    const double cos_phi = cos(phi);\n    const double sin_theta = sin(theta);\n    const double cos_theta = cos(theta);\n    const double amp = -sqrt(3.0 / 8.0 / M_PI) * y11_amplitude;\n\n    expected_dx_radius.get(0)[s] = -((cos_phi * sin_phi) / r) +\n                                   (cos_phi * square(cos_theta) * sin_phi) / r;\n    expected_dx_radius.get(1)[s] =\n        square(cos_phi) / r + (square(cos_theta) * square(sin_phi)) / r;\n    expected_dx_radius.get(2)[s] = -((cos_theta * sin_phi * sin_theta) / r);\n    for (auto& a : expected_dx_radius) {\n      a[s] *= amp;\n    }\n  }\n  CHECK_ITERABLE_APPROX(\n      expected_dx_radius,\n      ylm::cartesian_derivs_of_scalar(\n          ylm::radius(strahlkorper), strahlkorper, ylm::radius(strahlkorper),\n          ylm::inv_jacobian(ylm::theta_phi(strahlkorper))));\n\n  // Test second derivatives of radius\n  tnsr::ii<DataVector, 3> expected_d2x_radius(n_pts);\n  for (size_t s = 0; s < n_pts; ++s) {\n    // Messy analytic solution Mark Scheel computed in Mathematica\n    const double theta = theta_phi[0][s];\n    const double phi = theta_phi[1][s];\n    const double r = expected_radius[s];\n    const double sin_theta = sin(theta);\n    const double cos_theta = cos(theta);\n    const double sin_phi = sin(phi);\n    const double cos_phi = cos(phi);\n    const double cos_2_theta = cos(2. * theta);\n    const double amp = -sqrt(3. / 8. / M_PI) * y11_amplitude;\n    expected_d2x_radius.get(0, 0)[s] =\n        (9. * sin(3. * phi) * sin_theta -\n         sin_phi * (7. * sin_theta + 12. * square(cos_phi) * sin(3. * theta))) /\n        (16. * square(r));\n    expected_d2x_radius.get(0, 1)[s] = -(\n        (cos_phi *\n         (square(cos_phi) + ((-1. + 3. * cos_2_theta) * square(sin_phi)) / 2.) *\n         sin_theta) /\n        square(r));\n    expected_d2x_radius.get(0, 2)[s] =\n        (3. * cos_phi * cos_theta * sin_phi * square(sin_theta)) / square(r);\n    expected_d2x_radius.get(1, 1)[s] =\n        (-3. * (7. * sin_phi * sin_theta + 3. * sin(3. * phi) * sin_theta +\n                4. * pow<3>(sin_phi) * sin(3. * theta))) /\n        (16. * square(r));\n    expected_d2x_radius.get(1, 2)[s] =\n        -((cos_theta * (square(cos_phi) +\n                        ((-1. + 3. * cos_2_theta) * square(sin_phi)) / 2.)) /\n          square(r));\n    expected_d2x_radius.get(2, 2)[s] =\n        ((1. + 3. * cos_2_theta) * sin_phi * sin_theta) / (2. * square(r));\n    for (auto& a : expected_d2x_radius) {\n      a[s] *= amp;\n    }\n  }\n  CHECK_ITERABLE_APPROX(\n      expected_d2x_radius,\n      ylm::cartesian_second_derivs_of_scalar(\n          ylm::radius(strahlkorper), strahlkorper, ylm::radius(strahlkorper),\n          ylm::inv_jacobian(ylm::theta_phi(strahlkorper)),\n          ylm::inv_hessian(ylm::theta_phi(strahlkorper))));\n\n  // Test laplacian\n  DataVector expected_laplacian(n_pts);\n  for (size_t s = 0; s < n_pts; ++s) {\n    y_11.scalar_laplacian(&expected_laplacian, 1, s, {theta_phi[0][s]},\n                          {theta_phi[1][s]});\n    expected_laplacian[s] *= y11_amplitude;\n  }\n  CHECK_ITERABLE_APPROX(\n      expected_laplacian,\n      get(ylm::laplacian_of_scalar(ylm::radius(strahlkorper), strahlkorper,\n                                   ylm::theta_phi(strahlkorper))));\n}\n\nvoid test_theta_phi() {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper =\n      TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n\n  const auto expected_theta_phi =\n      strahlkorper.ylm_spherepack().theta_phi_points();\n  const auto theta_phi = ylm::theta_phi(strahlkorper);\n  CHECK_ITERABLE_APPROX(get<0>(theta_phi), expected_theta_phi[0]);\n  CHECK_ITERABLE_APPROX(get<1>(theta_phi), expected_theta_phi[1]);\n}\n\nvoid test_rhat_jacobian_hessian() {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper =\n      TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n\n  const auto theta_phi = strahlkorper.ylm_spherepack().theta_phi_points();\n  const auto& theta = theta_phi[0];\n  const auto& phi = theta_phi[1];\n  const DataVector cos_phi = cos(phi);\n  const DataVector sin_phi = sin(phi);\n  const DataVector cos_theta = cos(theta);\n  const DataVector sin_theta = sin(theta);\n\n  tnsr::i<DataVector, 3> expected_rhat(theta.size());\n  get<0>(expected_rhat) = sin_theta * cos_phi;\n  get<1>(expected_rhat) = sin_theta * sin_phi;\n  get<2>(expected_rhat) = cos_theta;\n  CHECK_ITERABLE_APPROX(expected_rhat, ylm::rhat(ylm::theta_phi(strahlkorper)));\n\n  ylm::Tags::aliases::Jacobian<Frame::Inertial> expected_jac(theta.size());\n  get<0, 0>(expected_jac) = cos_theta * cos_phi;  // 1/R dx/dth\n  get<1, 0>(expected_jac) = cos_theta * sin_phi;  // 1/R dy/dth\n  get<2, 0>(expected_jac) = -sin(theta);          // 1/R dz/dth\n  get<0, 1>(expected_jac) = -sin_phi;             // 1/(R sin(th)) dx/dph\n  get<1, 1>(expected_jac) = cos_phi;              // 1/(R sin(th)) dy/dph\n  get<2, 1>(expected_jac) = 0.0;                  // 1/(R sin(th)) dz/dph\n  CHECK_ITERABLE_APPROX(expected_jac,\n                        ylm::jacobian(ylm::theta_phi(strahlkorper)));\n\n  ylm::Tags::aliases::InvJacobian<Frame::Inertial> expected_inv_jac(\n      theta.size());\n  get<0, 0>(expected_inv_jac) = cos_theta * cos_phi;  // R dth/dx\n  get<0, 1>(expected_inv_jac) = cos_theta * sin_phi;  // R dth/dy\n  get<0, 2>(expected_inv_jac) = -sin(theta);          // R dth/dz\n  get<1, 0>(expected_inv_jac) = -sin_phi;             // R sin(th) dph/dx\n  get<1, 1>(expected_inv_jac) = cos_phi;              // R sin(th) dph/dy\n  get<1, 2>(expected_inv_jac) = 0.0;                  // R sin(th) dph/dz\n  CHECK_ITERABLE_APPROX(expected_inv_jac,\n                        ylm::inv_jacobian(ylm::theta_phi(strahlkorper)));\n\n  ylm::Tags::aliases::InvHessian<Frame::Inertial> expected_inv_hess(\n      theta.size());\n  // Note that here expected_inv_hess is computed in a much more\n  // straightforward way than in StrahlkorperFunctions.cpp, where it\n  // is computed in a way that avoids allocations.\n  const DataVector sin_sq_theta = square(sin_theta);\n  const DataVector cos_sq_theta = square(cos_theta);\n  const DataVector sin_theta_cos_theta = sin_theta * cos_theta;\n  const DataVector sin_sq_phi = square(sin_phi);\n  const DataVector cos_sq_phi = square(cos_phi);\n  const DataVector sin_phi_cos_phi = sin_phi * cos_phi;\n  const DataVector csc_theta = 1.0 / sin_theta;\n  const DataVector f1 = 1.0 + 2.0 * sin_sq_theta;\n  const DataVector cot_theta = cos_theta * csc_theta;\n  // R^2 d^2 th/(dx^2)\n  get<0, 0, 0>(expected_inv_hess) = cot_theta * (1.0 - cos_sq_phi * f1);\n  // R^2 d^2 th/(dxdy)\n  get<0, 0, 1>(expected_inv_hess) = -cot_theta * sin_phi_cos_phi * f1;\n  // R^2 d^2 th/(dxdz)\n  get<0, 0, 2>(expected_inv_hess) = (sin_sq_theta - cos_sq_theta) * cos_phi;\n  // R^2 d^2 th/(dydx)\n  get<0, 1, 0>(expected_inv_hess) = get<0, 0, 1>(expected_inv_hess);\n  // R^2 d^2 th/(dy^2)\n  get<0, 1, 1>(expected_inv_hess) = cot_theta * (1.0 - sin_sq_phi * f1);\n  // R^2 d^2 th/(dydz)\n  get<0, 1, 2>(expected_inv_hess) = (sin_sq_theta - cos_sq_theta) * sin_phi;\n  // R^2 d^2 th/(dzdx)\n  get<0, 2, 0>(expected_inv_hess) = get<0, 0, 2>(expected_inv_hess);\n  // R^2 d^2 th/(dzdy)\n  get<0, 2, 1>(expected_inv_hess) = get<0, 1, 2>(expected_inv_hess);\n  // R^2 d^2 th/(dz^2)\n  get<0, 2, 2>(expected_inv_hess) = 2.0 * sin_theta_cos_theta;\n  // R^2 d/dx (sin(th) dph/dx)\n  get<1, 0, 0>(expected_inv_hess) =\n      sin_phi_cos_phi * (1.0 + sin_sq_theta) * csc_theta;\n  // R^2 d/dx (sin(th) dph/dy)\n  get<1, 0, 1>(expected_inv_hess) =\n      (sin_sq_phi - sin_sq_theta * cos_sq_phi) * csc_theta;\n  // R^2 d/dx (sin(th) dph/dz)\n  get<1, 0, 2>(expected_inv_hess) = 0.0;\n  // R^2 d/dy (sin(th) dph/dx)\n  get<1, 1, 0>(expected_inv_hess) =\n      (sin_sq_theta * sin_sq_phi - cos_sq_phi) * csc_theta;\n  // R^2 d/dy (sin(th) dph/dy)\n  get<1, 1, 1>(expected_inv_hess) = -get<1, 0, 0>(expected_inv_hess);\n  // R^2 d/dy (sin(th) dph/dz)\n  get<1, 1, 2>(expected_inv_hess) = 0.0;\n  // R^2 d/dz (sin(th) dph/dx)\n  get<1, 2, 0>(expected_inv_hess) = cos_theta * sin_phi;\n  // R^2 d/dz (sin(th) dph/dy)\n  get<1, 2, 1>(expected_inv_hess) = -cos_theta * cos_phi;\n  // R^2 d/dz (sin(th) dph/dz)\n  get<1, 2, 2>(expected_inv_hess) = 0.0;\n  CHECK_ITERABLE_APPROX(expected_inv_hess,\n                        ylm::inv_hessian(ylm::theta_phi(strahlkorper)));\n}\n\nvoid test_cartesian_coords() {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper =\n      TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n\n  const auto theta_phi = strahlkorper.ylm_spherepack().theta_phi_points();\n  const auto& theta = theta_phi[0];\n  const auto& phi = theta_phi[1];\n  const DataVector cos_phi = cos(phi);\n  const DataVector sin_phi = sin(phi);\n  const DataVector cos_theta = cos(theta);\n  const DataVector sin_theta = sin(theta);\n  const auto n_pts = theta_phi[0].size();\n\n  const double amp = -sqrt(3.0 / 8.0 / M_PI) * y11_amplitude;\n  tnsr::I<DataVector, 3> expected_cartesian_coords(n_pts);\n  const DataVector temp = radius + amp * sin_phi * sin_theta;\n  expected_cartesian_coords.get(0) = cos_phi * sin_theta * temp + center[0];\n  expected_cartesian_coords.get(1) = sin_phi * sin_theta * temp + center[1];\n  expected_cartesian_coords.get(2) = cos_theta * temp + center[2];\n\n  CHECK_ITERABLE_APPROX(expected_cartesian_coords,\n                        ylm::cartesian_coords<Frame::Inertial>(\n                            strahlkorper, ylm::radius(strahlkorper),\n                            ylm::rhat(ylm::theta_phi(strahlkorper))));\n  CHECK_ITERABLE_APPROX(expected_cartesian_coords,\n                        ylm::cartesian_coords<Frame::Inertial>(strahlkorper));\n}\n\nvoid test_normals() {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper =\n      TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n\n  const auto theta_phi = strahlkorper.ylm_spherepack().theta_phi_points();\n  const auto n_pts = theta_phi[0].size();\n\n  // Test surface_tangents\n  ylm::Tags::aliases ::Jacobian<Frame::Inertial> expected_surface_tangents(\n      n_pts);\n  const double amp = -sqrt(3.0 / 8.0 / M_PI) * y11_amplitude;\n\n  const auto& theta = theta_phi[0];\n  const auto& phi = theta_phi[1];\n  const DataVector cos_phi = cos(phi);\n  const DataVector sin_phi = sin(phi);\n  const DataVector cos_theta = cos(theta);\n  const DataVector sin_theta = sin(theta);\n\n  expected_surface_tangents.get(0, 0) =\n      cos_phi * cos_theta * (radius + 2. * amp * sin_phi * sin_theta);\n  expected_surface_tangents.get(1, 0) =\n      cos_theta * sin_phi * (radius + 2. * amp * sin_phi * sin_theta);\n  expected_surface_tangents.get(2, 0) =\n      amp * square(cos_theta) * sin_phi -\n      sin_theta * (radius + amp * sin_phi * sin_theta);\n  expected_surface_tangents.get(0, 1) =\n      -radius * sin_phi + amp * sin_theta * (2. * square(cos_phi) - 1);\n  expected_surface_tangents.get(1, 1) =\n      cos_phi * (radius + 2. * amp * sin_phi * sin_theta);\n  expected_surface_tangents.get(2, 1) = amp * cos_phi * cos_theta;\n  CHECK_ITERABLE_APPROX(\n      expected_surface_tangents,\n      ylm::tangents(strahlkorper, ylm::radius(strahlkorper),\n                    ylm::rhat(ylm::theta_phi(strahlkorper)),\n                    ylm::jacobian(ylm::theta_phi(strahlkorper))));\n\n  // Test normal_one_form\n  tnsr::i<DataVector, 3> expected_normal_one_form(n_pts);\n  {\n    const auto r = get(ylm::radius(strahlkorper));\n    const DataVector one_over_r = 1.0 / r;\n    const DataVector temp = 1.0 + one_over_r * amp * sin_phi * sin_theta;\n    expected_normal_one_form.get(0) = cos_phi * sin_theta * temp;\n    expected_normal_one_form.get(1) =\n        sin_phi * sin_theta * temp - amp * one_over_r;\n    expected_normal_one_form.get(2) = cos_theta * temp;\n  }\n  CHECK_ITERABLE_APPROX(\n      expected_normal_one_form,\n      ylm::normal_one_form(ylm::cartesian_derivs_of_scalar(\n                               ylm::radius(strahlkorper), strahlkorper,\n                               ylm::radius(strahlkorper),\n                               ylm::inv_jacobian(ylm::theta_phi(strahlkorper))),\n                           ylm::rhat(ylm::theta_phi(strahlkorper))));\n}\n\nvoid test_fit_ylm_coeffs_same() {\n  const double l_max = 2;\n  const double m_max = l_max;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.0, 0.0, 0.0}};\n  const double y00 = radius * sqrt(8.0);\n  const auto strahlkorper0 =\n      Strahlkorper<Frame::Inertial>(l_max, m_max, radius, center);\n  const auto strahlkorper1 = Strahlkorper<Frame::Inertial>(\n      {y00, 0.0, 1.0, -1.0, -5.0, 12.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n       0.0, 0.0, 0.0, 0.01},\n      strahlkorper0);\n  const auto strahlkorper2 = Strahlkorper<Frame::Inertial>(\n      {y00, 0.0, 2.0, -3.0, -3.0, 17.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n       0.0, 0.0, 0.0, 0.04},\n      strahlkorper0);\n  const auto strahlkorper3 = Strahlkorper<Frame::Inertial>(\n      {y00, 0.0, 3.0, -7.0, -1.0, 17.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n       0.0, 0.0, 0.0, 0.09},\n      strahlkorper0);\n  const auto strahlkorper4 = Strahlkorper<Frame::Inertial>(\n      {y00, 0.0, 4.0, -13.0, 1.0, 12.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n       0.0, 0.0, 0.0, 0.16},\n      strahlkorper0);\n\n  const DataVector times{0.0, 0.1, 0.2, 0.3, 0.4};\n  std::vector<Strahlkorper<Frame::Inertial>> strahlkorpers{\n      strahlkorper0, strahlkorper1, strahlkorper2, strahlkorper3,\n      strahlkorper4};\n  std::vector<std::array<double, 4>> result =\n      ylm::fit_ylm_coeffs<Frame::Inertial>(times, strahlkorpers);\n  const std::vector<std::array<double, 4>> expected_result = {\n      {y00, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 10.0, 0.0, 0.0},\n      {-1.0 / 70.0, -4.88095238095232631, -35.71428571428607768, -250.0 / 3.0},\n      {-0.1, -505.0 / 6.0, 450.0, -1750.0 / 3.0},\n      {0.02857142857142869, 154.76190476190464551, -378.57142857142787307,\n       500.0 / 3.0},\n      {0.0142857142857146, 14.88095238095235473, -64.28571428571413549,\n       250.0 / 3.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 1.0, 0.0}};\n  Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(result, expected_result, custom_approx);\n}\n\nvoid test_fit_ylm_coeffs_diff() {\n  const double l_max = 3;\n  const double m_max = 2;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.0, 0.0, 0.0}};\n  const double y00 = radius * sqrt(8.0);\n  const auto strahlkorper0 =\n      Strahlkorper<Frame::Inertial>(l_max, m_max, radius, center);\n  const auto strahlkorper1 = Strahlkorper<Frame::Inertial>(\n      {y00, 0.0, 1.0, -1.0, -5.0, 12.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n       0.0, 0.0, 0.0, 0.0,  0.0,  0.0,  0.0, 0.0, 0.0, 0.0, 0.0, 0.01},\n      strahlkorper0);\n  const auto strahlkorper2 = Strahlkorper<Frame::Inertial>(\n      {y00, 0.0, 2.0, -3.0, -3.0, 17.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n       0.0, 0.0, 0.0, 0.0,  0.0,  0.0,  0.0, 0.0, 0.0, 0.0, 0.0, 0.04},\n      strahlkorper0);\n  const auto strahlkorper3 = Strahlkorper<Frame::Inertial>(\n      {y00, 0.0, 3.0, -7.0, -1.0, 17.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n       0.0, 0.0, 0.0, 0.0,  0.0,  0.0,  0.0, 0.0, 0.0, 0.0, 0.0, 0.09},\n      strahlkorper0);\n  const auto strahlkorper4 = Strahlkorper<Frame::Inertial>(\n      {y00, 0.0, 4.0, -13.0, 1.0, 12.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n       0.0, 0.0, 0.0, 0.0,   0.0, 0.0,  0.0, 0.0, 0.0, 0.0, 0.0, 0.16},\n      strahlkorper0);\n\n  const DataVector times{0.0, 0.1, 0.2, 0.3, 0.4};\n  std::vector<Strahlkorper<Frame::Inertial>> strahlkorpers{\n      strahlkorper0, strahlkorper1, strahlkorper2, strahlkorper3,\n      strahlkorper4};\n  std::vector<std::array<double, 4>> result =\n      ylm::fit_ylm_coeffs<Frame::Inertial>(times, strahlkorpers);\n  const std::vector<std::array<double, 4>> expected_result = {\n      {y00, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 10.0, 0.0, 0.0},\n      {-1.0 / 70.0, -4.88095238095232631, -35.71428571428607768, -250.0 / 3.0},\n      {-0.1, -505.0 / 6.0, 450.0, -1750.0 / 3.0},\n      {0.02857142857142869, 154.76190476190464551, -378.57142857142787307,\n       500.0 / 3.0},\n      {0.0142857142857146, 14.88095238095235473, -64.28571428571413549,\n       250.0 / 3.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 0.0, 0.0},\n      {0.0, 0.0, 1.0, 0.0}};\n  Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(result, expected_result, custom_approx);\n}\n\nvoid test_time_deriv_strahlkorper() {\n  const size_t l_max = 2;\n  Strahlkorper<Frame::Inertial> strahlkorper{l_max, l_max, 1.0,\n                                             std::array{0.0, 0.0, 0.0}};\n  SpherepackIterator iter{l_max, l_max};\n  // Set a random coefficient non-zero\n  strahlkorper.coefficients()[iter.set(2, 1)()] = 1.3;\n\n  for (size_t num_times = 1; num_times <= 4; num_times++) {\n    CAPTURE(num_times);\n    std::deque<std::pair<double, Strahlkorper<Frame::Inertial>>>\n        previous_strahlkorpers{};\n\n    // Set all strahlkorpers to be the same\n    for (size_t i = 0; i < num_times; i++) {\n      previous_strahlkorpers.emplace_front(static_cast<double>(i),\n                                           strahlkorper);\n    }\n\n    auto time_deriv = strahlkorper;\n\n    ylm::time_deriv_of_strahlkorper(make_not_null(&time_deriv),\n                                    previous_strahlkorpers);\n\n    const DataVector& time_deriv_strahlkorper_coefs = time_deriv.coefficients();\n\n    // Since we made all the Strahlkorpers the same (or there is a NaN time),\n    // the time deriv should be zero.\n    CHECK_ITERABLE_APPROX(\n        time_deriv_strahlkorper_coefs,\n        (DataVector{time_deriv_strahlkorper_coefs.size(), 0.0}));\n\n    // Check that the deriv works for 2 times (easy to calculate by hand)\n    if (num_times == 2) {\n      // Set a single coefficient to a random value for each previous\n      // strahlkorper\n      previous_strahlkorpers.front().second.coefficients()[iter.set(2, -1)()] =\n          2.0;\n      previous_strahlkorpers.back().second.coefficients()[iter()] = 2.5;\n\n      ylm::time_deriv_of_strahlkorper(make_not_null(&time_deriv),\n                                      previous_strahlkorpers);\n\n      const DataVector& coefs = time_deriv.coefficients();\n\n      DataVector expected_coefs{coefs.size(), 0.0};\n      expected_coefs[iter()] = -0.5;\n\n      CHECK_ITERABLE_APPROX(coefs, expected_coefs);\n    }\n  }\n\n  // Check that time derivative works when previous strahlkorpers have a\n  // different resolution than the output Strahlkorper. First, repeat the\n  // test that the time derivative should vanish, but this time, have\n  // use different l\n  constexpr size_t l_max_mid = 6;\n  constexpr size_t l_max_min = l_max_mid - 2;\n  constexpr size_t l_max_max = l_max_mid + 2;\n\n  Strahlkorper<Frame::Inertial> strahlkorper_mid{l_max_mid, l_max_mid, 1.0,\n                                                 std::array{0.0, 0.0, 0.0}};\n  Strahlkorper<Frame::Inertial> strahlkorper_min{l_max_min, l_max_min, 1.0,\n                                                 std::array{0.0, 0.0, 0.0}};\n  Strahlkorper<Frame::Inertial> strahlkorper_max{l_max_max, l_max_max, 1.0,\n                                                 std::array{0.0, 0.0, 0.0}};\n\n  // Set a random coefficient of each Strahlorper to be 1.3\n  SpherepackIterator iter_mid{l_max_mid, l_max_mid};\n  SpherepackIterator iter_min{l_max_min, l_max_min};\n  SpherepackIterator iter_max{l_max_max, l_max_max};\n\n  strahlkorper_mid.coefficients()[iter_mid.set(2, 1)()] = 1.3;\n  strahlkorper_min.coefficients()[iter_min.set(2, 1)()] = 1.3;\n  strahlkorper_max.coefficients()[iter_max.set(2, 1)()] = 1.3;\n\n  // Set all Strahlkorpers to have the same coefficients, and make sure\n  // that the time derivative is zero\n  std::deque<std::pair<double, Strahlkorper<Frame::Inertial>>>\n      previous_strahlkorpers_different_resolutions{};\n  previous_strahlkorpers_different_resolutions.emplace_front(0.0,\n                                                             strahlkorper_mid);\n  previous_strahlkorpers_different_resolutions.emplace_front(1.0,\n                                                             strahlkorper_min);\n  previous_strahlkorpers_different_resolutions.emplace_front(2.0,\n                                                             strahlkorper_max);\n  auto time_deriv_different_resolutions = strahlkorper_mid;\n  ylm::time_deriv_of_strahlkorper(\n      make_not_null(&time_deriv_different_resolutions),\n      previous_strahlkorpers_different_resolutions);\n  CHECK_ITERABLE_APPROX(\n      time_deriv_different_resolutions.coefficients(),\n      (DataVector{time_deriv_different_resolutions.coefficients().size(),\n                  0.0}));\n\n  // Now, test that a time derivative with two horizons with different\n  // resolutions returns the expected value\n  previous_strahlkorpers_different_resolutions.pop_front();\n  previous_strahlkorpers_different_resolutions.front()\n      .second.coefficients()[iter_min.set(2, 1)()] = 4.0;\n  previous_strahlkorpers_different_resolutions.back()\n      .second.coefficients()[iter_mid.set(2, 1)()] = 4.5;\n  auto time_deriv_2_resolutions = strahlkorper_max;\n  ylm::time_deriv_of_strahlkorper(make_not_null(&time_deriv_2_resolutions),\n                                  previous_strahlkorpers_different_resolutions);\n  const DataVector& coefs_different_resolutions =\n      time_deriv_2_resolutions.coefficients();\n  DataVector expected_coefs_different_resolutions{\n      coefs_different_resolutions.size(), 0.0};\n  expected_coefs_different_resolutions[iter_max.set(2, 1)()] = -0.5;\n\n  CHECK_ITERABLE_APPROX(coefs_different_resolutions,\n                        expected_coefs_different_resolutions);\n}\n\nvoid test_power_monitor() {\n  constexpr size_t l_max = 5;\n  constexpr size_t m_max = 4;\n\n  // Check that all power monitors for a spherical Strahlkorper vanish, except\n  // for the l=0 power monitor, which has a value of 2 sqrt(2) * radius.\n  // This is because f = (1/2)(1/sqrt(2)) P_0^0 a00, and the associated\n  // Legendre polynomial P_0^0 == 1.\n  constexpr double radius = 4.567;\n  const std::array<double, 3> center{0.1, -1.2, 2.3};\n  Strahlkorper<Frame::Inertial> spherical_strahlkorper{l_max, m_max, radius,\n                                                       center};\n  const DataVector power_spherical = power_monitor(spherical_strahlkorper);\n  DataVector expected_power_spherical{l_max + 1, 0.0};\n  expected_power_spherical[0] = 2.0 * sqrt(2.0) * radius;\n  CHECK_ITERABLE_APPROX(power_spherical, expected_power_spherical);\n\n  // Check that power monitor behaves as expected for Strahlkorper that is\n  // not spherical, performing the calculation in a slightly different way\n  // from the code being tested\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-0.01, 0.01);\n  const auto nonspherical_coefs = make_with_random_values<ModalVector>(\n      make_not_null(&generator), make_not_null(&distribution),\n      spherical_strahlkorper.coefficients());\n  const Strahlkorper<Frame::Inertial> nonspherical_strahlkorper{\n      l_max, m_max, nonspherical_coefs, center};\n  const DataVector power_nonspherical =\n      power_monitor(nonspherical_strahlkorper);\n  const auto nonspherical_coefs_sq = square(nonspherical_coefs);\n  DataVector power_nonspherical_expected = DataVector{l_max + 1, 0.0};\n  for (SpherepackIterator it(l_max, m_max); it; ++it) {\n    power_nonspherical_expected[it.l()] +=\n        nonspherical_coefs_sq[it()] /\n        (2.0 * static_cast<double>(std::min(it.l(), m_max)) + 1.0);\n  }\n  power_nonspherical_expected = sqrt(power_nonspherical_expected);\n\n  CHECK_ITERABLE_APPROX(power_nonspherical, power_nonspherical_expected);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.StrahlkorperFunctions\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_theta_phi();\n  test_rhat_jacobian_hessian();\n  test_cartesian_coords();\n  test_radius_and_derivs();\n  test_normals();\n  test_fit_ylm_coeffs_same();\n  test_fit_ylm_coeffs_diff();\n  test_time_deriv_strahlkorper();\n  test_power_monitor();\n}\n}  // namespace ylm\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <climits>\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/StrahlkorperTestHelpers.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/YlmTestFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test_average_radius() {\n  // Create spherical Strahlkorper\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const double r = 3.0;\n  ylm::Strahlkorper<Frame::Inertial> s(4, 4, r, center);\n  CHECK(s.average_radius() == approx(r));\n}\n\nvoid test_radius_and_derivs() {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper =\n      ylm::TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n\n  // Now construct a Y00 + Im(Y11) surface by hand.\n  const auto theta_phi = strahlkorper.ylm_spherepack().theta_phi_points();\n  const auto& theta_points = strahlkorper.ylm_spherepack().theta_points();\n  const auto& phi_points = strahlkorper.ylm_spherepack().phi_points();\n  const auto n_pts = theta_phi[0].size();\n  YlmTestFunctions::Y11 y_11;\n  DataVector expected_radius(n_pts);\n  y_11.func(&expected_radius, 1, 0, theta_points, phi_points);\n  for (size_t s = 0; s < n_pts; ++s) {\n    expected_radius[s] *= y11_amplitude;\n    expected_radius[s] += radius;\n  }\n\n  // Create DataBox\n  auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<\n          tmpl::push_back<ylm::Tags::compute_items_tags<Frame::Inertial>>,\n          ylm::Tags::PhysicalCenterCompute<Frame::Inertial>>>(strahlkorper);\n\n  // Test radius\n  const auto& strahlkorper_radius =\n      get(db::get<ylm::Tags::Radius<Frame::Inertial>>(box));\n  CHECK_ITERABLE_APPROX(strahlkorper_radius, expected_radius);\n\n  // Test physical center tag\n  const auto& strahlkorper_physical_center =\n      db::get<ylm::Tags::PhysicalCenter<Frame::Inertial>>(box);\n  CHECK(strahlkorper_physical_center == strahlkorper.physical_center());\n\n  // Test derivative of radius\n  tnsr::i<DataVector, 3> expected_dx_radius(n_pts);\n  for (size_t s = 0; s < n_pts; ++s) {\n    // Analytic solution Mark Scheel computed in Mathematica\n    const double theta = theta_phi[0][s];\n    const double phi = theta_phi[1][s];\n    const double r = expected_radius[s];\n    const double sin_phi = sin(phi);\n    const double cos_phi = cos(phi);\n    const double sin_theta = sin(theta);\n    const double cos_theta = cos(theta);\n    const double amp = -sqrt(3.0 / 8.0 / M_PI) * y11_amplitude;\n\n    expected_dx_radius.get(0)[s] = -((cos_phi * sin_phi) / r) +\n                                   (cos_phi * square(cos_theta) * sin_phi) / r;\n    expected_dx_radius.get(1)[s] =\n        square(cos_phi) / r + (square(cos_theta) * square(sin_phi)) / r;\n    expected_dx_radius.get(2)[s] = -((cos_theta * sin_phi * sin_theta) / r);\n    for (auto& a : expected_dx_radius) {\n      a[s] *= amp;\n    }\n  }\n  const auto& strahlkorper_dx_radius =\n      db::get<ylm::Tags::DxRadius<Frame::Inertial>>(box);\n  CHECK_ITERABLE_APPROX(strahlkorper_dx_radius, expected_dx_radius);\n\n  // Test second derivatives.\n  tnsr::ii<DataVector, 3> expected_d2x_radius(n_pts);\n  for (size_t s = 0; s < n_pts; ++s) {\n    // Messy analytic solution Mark Scheel computed in Mathematica\n    const double theta = theta_phi[0][s];\n    const double phi = theta_phi[1][s];\n    const double r = expected_radius[s];\n    const double sin_theta = sin(theta);\n    const double cos_theta = cos(theta);\n    const double sin_phi = sin(phi);\n    const double cos_phi = cos(phi);\n    const double cos_2_theta = cos(2. * theta);\n    const double amp = -sqrt(3. / 8. / M_PI) * y11_amplitude;\n    expected_d2x_radius.get(0, 0)[s] =\n        (9. * sin(3. * phi) * sin_theta -\n         sin_phi * (7. * sin_theta + 12. * square(cos_phi) * sin(3. * theta))) /\n        (16. * square(r));\n    expected_d2x_radius.get(0, 1)[s] = -(\n        (cos_phi *\n         (square(cos_phi) + ((-1. + 3. * cos_2_theta) * square(sin_phi)) / 2.) *\n         sin_theta) /\n        square(r));\n    expected_d2x_radius.get(0, 2)[s] =\n        (3. * cos_phi * cos_theta * sin_phi * square(sin_theta)) / square(r);\n    expected_d2x_radius.get(1, 1)[s] =\n        (-3. * (7. * sin_phi * sin_theta + 3. * sin(3. * phi) * sin_theta +\n                4. * pow<3>(sin_phi) * sin(3. * theta))) /\n        (16. * square(r));\n    expected_d2x_radius.get(1, 2)[s] =\n        -((cos_theta * (square(cos_phi) +\n                        ((-1. + 3. * cos_2_theta) * square(sin_phi)) / 2.)) /\n          square(r));\n    expected_d2x_radius.get(2, 2)[s] =\n        ((1. + 3. * cos_2_theta) * sin_phi * sin_theta) / (2. * square(r));\n    for (auto& a : expected_d2x_radius) {\n      a[s] *= amp;\n    }\n  }\n  const auto& strahlkorper_d2x_radius =\n      db::get<ylm::Tags::D2xRadius<Frame::Inertial>>(box);\n  CHECK_ITERABLE_APPROX(expected_d2x_radius, strahlkorper_d2x_radius);\n\n  // Test nabla squared\n  DataVector expected_laplacian(n_pts);\n  for (size_t s = 0; s < n_pts; ++s) {\n    y_11.scalar_laplacian(&expected_laplacian, 1, s, {theta_phi[0][s]},\n                          {theta_phi[1][s]});\n    expected_laplacian[s] *= y11_amplitude;\n  }\n  const auto& strahlkorper_laplacian =\n      db::get<ylm::Tags::LaplacianRadius<Frame::Inertial>>(box);\n  CHECK_ITERABLE_APPROX(get(strahlkorper_laplacian), expected_laplacian);\n}\n\nvoid test_normals() {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper =\n      ylm::TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n\n  const auto theta_phi = strahlkorper.ylm_spherepack().theta_phi_points();\n  const auto n_pts = theta_phi[0].size();\n\n  // Test surface_tangents\n\n  ylm::Tags::aliases ::Jacobian<Frame::Inertial> expected_surface_tangents(\n      n_pts);\n  const double amp = -sqrt(3.0 / 8.0 / M_PI) * y11_amplitude;\n\n  const auto& theta = theta_phi[0];\n  const auto& phi = theta_phi[1];\n  const DataVector cos_phi = cos(phi);\n  const DataVector sin_phi = sin(phi);\n  const DataVector cos_theta = cos(theta);\n  const DataVector sin_theta = sin(theta);\n\n  expected_surface_tangents.get(0, 0) =\n      cos_phi * cos_theta * (radius + 2. * amp * sin_phi * sin_theta);\n  expected_surface_tangents.get(1, 0) =\n      cos_theta * sin_phi * (radius + 2. * amp * sin_phi * sin_theta);\n  expected_surface_tangents.get(2, 0) =\n      amp * square(cos_theta) * sin_phi -\n      sin_theta * (radius + amp * sin_phi * sin_theta);\n  expected_surface_tangents.get(0, 1) =\n      -radius * sin_phi + amp * sin_theta * (2. * square(cos_phi) - 1);\n  expected_surface_tangents.get(1, 1) =\n      cos_phi * (radius + 2. * amp * sin_phi * sin_theta);\n  expected_surface_tangents.get(2, 1) = amp * cos_phi * cos_theta;\n\n  // Create DataBox\n  auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  const auto& surface_tangents =\n      db::get<ylm::Tags::Tangents<Frame::Inertial>>(box);\n  CHECK_ITERABLE_APPROX(surface_tangents, expected_surface_tangents);\n\n  // Test surface_cartesian_coordinates\n  tnsr::I<DataVector, 3> expected_cart_coords(n_pts);\n\n  {\n    const DataVector temp = radius + amp * sin_phi * sin_theta;\n    expected_cart_coords.get(0) = cos_phi * sin_theta * temp + center[0];\n    expected_cart_coords.get(1) = sin_phi * sin_theta * temp + center[1];\n    expected_cart_coords.get(2) = cos_theta * temp + center[2];\n  }\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n  CHECK_ITERABLE_APPROX(expected_cart_coords, cart_coords);\n\n  // Test surface_normal_one_form\n  tnsr::i<DataVector, 3> expected_normal_one_form(n_pts);\n  {\n    const auto& r = get(db::get<ylm::Tags::Radius<Frame::Inertial>>(box));\n    const DataVector one_over_r = 1.0 / r;\n    const DataVector temp = 1.0 + one_over_r * amp * sin_phi * sin_theta;\n    expected_normal_one_form.get(0) = cos_phi * sin_theta * temp;\n    expected_normal_one_form.get(1) =\n        sin_phi * sin_theta * temp - amp * one_over_r;\n    expected_normal_one_form.get(2) = cos_theta * temp;\n  }\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  CHECK_ITERABLE_APPROX(expected_normal_one_form, normal_one_form);\n}\n\nstruct SomeType {};\nstruct SomeTag : db::SimpleTag {\n  using type = SomeType;\n};\nstruct DummyScalar : db::SimpleTag {\n  using type = Scalar<double>;\n};\n\nvoid test_stf_tensor_tag() {\n  static_assert(std::is_same_v<\n                Stf::Tags::StfTensor<DummyScalar, 0, 3, Frame::Inertial>::type,\n                Scalar<double>>);\n  static_assert(std::is_same_v<\n                Stf::Tags::StfTensor<DummyScalar, 1, 3, Frame::Inertial>::type,\n                tnsr::i<double, 3, Frame::Inertial>>);\n  static_assert(std::is_same_v<\n                Stf::Tags::StfTensor<DummyScalar, 2, 3, Frame::Inertial>::type,\n                tnsr::ii<double, 3, Frame::Inertial>>);\n  static_assert(std::is_same_v<\n                Stf::Tags::StfTensor<DummyScalar, 3, 3, Frame::Inertial>::type,\n                tnsr::iii<double, 3, Frame::Inertial>>);\n  TestHelpers::db::test_simple_tag<\n      Stf::Tags::StfTensor<DummyScalar, 0, 3, Frame::Inertial>>(\n      \"StfTensor(DummyScalar,0)\");\n  TestHelpers::db::test_simple_tag<\n      Stf::Tags::StfTensor<DummyScalar, 1, 3, Frame::Inertial>>(\n      \"StfTensor(DummyScalar,1)\");\n  TestHelpers::db::test_simple_tag<\n      Stf::Tags::StfTensor<DummyScalar, 2, 3, Frame::Inertial>>(\n      \"StfTensor(DummyScalar,2)\");\n  TestHelpers::db::test_simple_tag<\n      Stf::Tags::StfTensor<DummyScalar, 3, 3, Frame::Inertial>>(\n      \"StfTensor(DummyScalar,3)\");\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.Tags\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_average_radius();\n  test_radius_and_derivs();\n  test_normals();\n  test_stf_tensor_tag();\n  TestHelpers::db::test_simple_tag<ylm::Tags::Strahlkorper<Frame::Inertial>>(\n      \"Strahlkorper\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::ThetaPhi<Frame::Inertial>>(\n      \"ThetaPhi\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::Rhat<Frame::Inertial>>(\"Rhat\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::Jacobian<Frame::Inertial>>(\n      \"Jacobian\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::InvJacobian<Frame::Inertial>>(\n      \"InvJacobian\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::InvHessian<Frame::Inertial>>(\n      \"InvHessian\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::Radius<Frame::Inertial>>(\n      \"Radius\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::CartesianCoords<Frame::Inertial>>(\n      \"CartesianCoords\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::DxRadius<Frame::Inertial>>(\n      \"DxRadius\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::D2xRadius<Frame::Inertial>>(\n      \"D2xRadius\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::LaplacianRadius<Frame::Inertial>>(\n      \"LaplacianRadius\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::NormalOneForm<Frame::Inertial>>(\n      \"NormalOneForm\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::Tangents<Frame::Inertial>>(\n      \"Tangents\");\n  TestHelpers::db::test_simple_tag<\n      ylm::Tags::TimeDerivStrahlkorper<Frame::Inertial>>(\n      \"TimeDerivStrahlkorper\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::ThetaPhiCompute<Frame::Inertial>>(\"ThetaPhi\");\n  TestHelpers::db::test_compute_tag<ylm::Tags::RhatCompute<Frame::Inertial>>(\n      \"Rhat\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::JacobianCompute<Frame::Inertial>>(\"Jacobian\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::InvJacobianCompute<Frame::Inertial>>(\"InvJacobian\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::InvHessianCompute<Frame::Inertial>>(\"InvHessian\");\n  TestHelpers::db::test_compute_tag<ylm::Tags::RadiusCompute<Frame::Inertial>>(\n      \"Radius\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::PhysicalCenterCompute<Frame::Inertial>>(\"PhysicalCenter\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::CartesianCoordsCompute<Frame::Inertial>>(\"CartesianCoords\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::DxRadiusCompute<Frame::Inertial>>(\"DxRadius\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::D2xRadiusCompute<Frame::Inertial>>(\"D2xRadius\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::LaplacianRadiusCompute<Frame::Inertial>>(\"LaplacianRadius\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::NormalOneFormCompute<Frame::Inertial>>(\"NormalOneForm\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::TangentsCompute<Frame::Inertial>>(\"Tangents\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::TimeDerivStrahlkorperCompute<Frame::Inertial>>(\n      \"TimeDerivStrahlkorper\");\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_TensorYlmCartToSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <filesystem>\n#include <fstream>\n#include <optional>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmCartToSphere.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <typename TensorStructure>\nstd::string symm_to_string() {\n  if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                               Symmetry<1>>) {\n    return \"a\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<1, 1>>) {\n    return \"aa\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<2, 1>>) {\n    return \"ab\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<3, 2, 1>>) {\n    return \"abc\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<2, 1, 1>>) {\n    return \"abb\";\n  }\n  return \"\";\n}\n\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid test_tensorylm_cart_to_sphere_vs_spec(\n    const size_t ell_max,\n    const ylm::TensorYlm::CoefficientNormalization coefficient_normalization) {\n  // There are two \"modes\" for this test.  If test_all_elements is\n  // true, it tests all the matrix elements vs SpEC, reading .txt\n  // files that were output by SpEC. This test was done by Mark\n  // Scheel, and is not done in CI.\n  //\n  // Otherwise, it tests a random subset of the matrix elements vs\n  // SpEC here (the random numbers being previously determined by\n  // SpEC), using inlined values, which is done in CI.\n  constexpr bool test_all_elements = false;\n\n  std::vector<double> spec_matrix_elements;\n  std::vector<size_t> spec_src_indices;\n  std::vector<size_t> spec_dest_indices;\n\n  if constexpr (test_all_elements) {\n    // Read from simple file that was output by SpEC.\n    // This test was run by Mark Scheel after producing the SpEC\n    // files.\n    // Because the SpEC files are so large (16MB for this test),\n    // we don't run this test in CI.\n    //\n    // File has a simple binary format:\n    //   size (a size_t)\n    //   matrix_elements (a vector of doubles of length size)\n    //   src_indices (a vector of size_t of length size)\n    //   dest_indices (a vector of size_t of length size)\n    const std::string filename =\n        \"TensorYlmCartToSphere_\" + symm_to_string<TensorStructure>() + \".txt\";\n    std::ifstream file;\n    file.open(filename, std::ios::in | std::ios::binary);\n    size_t num_indices = 0;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(&num_indices), sizeof(size_t));\n    spec_matrix_elements.resize(num_indices);\n    spec_src_indices.resize(num_indices);\n    spec_dest_indices.resize(num_indices);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_matrix_elements.data()),\n              static_cast<std::streamsize>(sizeof(double) * num_indices));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_src_indices.data()),\n              static_cast<std::streamsize>(sizeof(size_t) * num_indices));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_dest_indices.data()),\n              static_cast<std::streamsize>(sizeof(size_t) * num_indices));\n  } else {\n    if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                 Symmetry<1>>) {\n      spec_matrix_elements = {\n          -0.15498260496951663,  -0.204124145231931481,  -0.0937614461876990629,\n          -0.223606797749978908, 0.308606699924183769,   0.494727444918153625,\n          0.120701137396316868,  0.272772362794990453,   -0.207019667802706198,\n          -0.340306954864886213, -0.0292889593064503025, -0.328394788714592656,\n          -0.152189896858767659, -0.345032779671177237,  0.485071250072665772,\n          -0.350823207722811559, 0.229667700705285721,   -0.218217890235992279,\n          0.308606699924183825,  0.433860915637312272,   -0.255883157859579546,\n          -0.092619821704368499, -0.359738467092250658,  0.267261241912424286,\n          0.176776695296636865,  -0.223606797749978992,  -0.333333333333333315,\n          -0.133630620956212198, 0.310971536805696502,   0.258198889747161042,\n          -0.166666666666666685, 0.375533808099405231,   -0.218217890235992279,\n          0.160422236979936905,  -0.0449466574975494748, -0.341565025531986588,\n          -0.166666666666666657, -0.281718084909505617,  -0.0591312395989082445,\n          -0.119522860933439307, 0.147828098997270674,   0.0506369683541833121,\n          0.464095480892257051,  -0.206959338596178793,  0.0545544725589980906,\n          -0.298807152333598391, -0.240906027914857851,  -0.283069258536148949,\n          -0.128623938856881587, -0.328394788714592545,  0.0704295212273763765,\n          -0.196116135138184017, 0.316227766016837941,   -0.092619821704368499,\n          -0.387298334620741758, 0.0690065559342354085,  -0.223606797749978992,\n          0.485071250072665772,  0.144841364875580275,   -0.328394788714592545,\n          -0.353553390593273786, -0.256045778249594624,  0.338061701891406607,\n          0.204124145231931481,  -0.0661107356684931258, 0.292770021884559861,\n          0.124034734589208398,  -0.134839972492648424,  -0.654653670707976976,\n          0.258673084618176563,  0.28867513459481281,    0.311399577664609239,\n          -0.316808533753154264, 0.240906027914857851,   0.339683110243378661,\n          0.089087080637474822,  0.142815511749495833,   -0.353553390593273842,\n          0.273861278752583037,  -0.102418311299837833,  -0.24852506087385412,\n          -0.363803437554499343, -0.222783197169751673,  -0.39666441401095881,\n          0.0944911182523067994, -0.347182537414706671,  -0.196476311998343894,\n          -0.377964473009227031, -0.283876875750879998,  0.496138938356833814,\n          0.279398864511286094,  -0.313391585264004291,  0.377964473009227142,\n          -0.283069258536148949, -0.325669473639464746,  0.310971536805696502,\n          0.222783197169751673,  -0.29128763250176759,   -0.12104550653376045,\n          -0.353553390593273786};\n      spec_src_indices = {\n          72,  444, 139, 343, 344, 460, 298, 28,  189, 312, 321, 156, 227,\n          280, 232, 307, 473, 110, 19,  481, 301, 318, 76,  273, 313, 120,\n          400, 117, 139, 465, 242, 310, 29,  484, 291, 128, 322, 270, 220,\n          189, 135, 311, 55,  288, 119, 442, 150, 221, 309, 146, 30,  299,\n          253, 156, 10,  109, 282, 479, 129, 308, 315, 139, 140, 444, 300,\n          231, 309, 281, 384, 320, 263, 444, 230, 312, 309, 192, 209, 45,\n          210, 292, 393, 151, 226, 58,  199, 307, 236, 391, 138, 388, 159,\n          148, 101, 221, 280, 382, 64,  371, 300, 27};\n      spec_dest_indices = {\n          226, 273, 471, 172, 191, 127, 371, 362, 100, 241, 232, 146, 478,\n          189, 161, 216, 464, 424, 353, 148, 383, 229, 66,  344, 393, 283,\n          400, 271, 473, 150, 484, 57,  343, 475, 202, 451, 404, 37,  131,\n          424, 451, 60,  63,  217, 435, 271, 311, 454, 236, 156, 200, 64,\n          342, 310, 182, 425, 202, 146, 137, 75,  397, 453, 474, 435, 211,\n          302, 58,  208, 222, 391, 343, 291, 483, 392, 56,  119, 298, 370,\n          292, 57,  402, 323, 479, 48,  272, 227, 469, 391, 310, 55,  311,\n          309, 435, 454, 47,  229, 398, 200, 227, 190};\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<1, 1>>) {\n      spec_matrix_elements = {-0.0247715841815203917,  -0.0596663748467176208,\n                              -0.0149058293578653214,  -0.00401998884052646565,\n                              0.116023870223064249,    0.202999485735287533,\n                              -0.13093073414159534,    0.0568114573750766322,\n                              0.234311674451602364,    -0.128623938856881587,\n                              0.0524142418360958942,   -0.172516389835588646,\n                              0.205971460217774849,    -0.172132593164774,\n                              -0.00274617518190544731, 0.182701994171208443,\n                              -0.0230168037973560551,  0.375438596491228127,\n                              -0.13468700594029473,    0.153960071783900143,\n                              -0.238719008791738796,   -0.163178487966126301,\n                              -0.0922138891954146916,  0.284267621807480608,\n                              0.145643816250883795,    0.0516139774092508954,\n                              -0.0140634994209774978,  0.246885359939347043,\n                              -0.00778467491564933251, 0.178501386823854108,\n                              0.288675134594812921,    -0.189352427332460271,\n                              0.235222735295432445,    0.0109649122807017763,\n                              0.0212352996432344059,   -0.292770021884559972,\n                              0.114800385729944041,    -0.160422236979936877,\n                              -0.0172005229038445298,  -0.124549048065255075,\n                              0.0712634947775084016,   0.00993072652873696303,\n                              -0.11651034560709253,    0.176495516980754746,\n                              0.0622745240326275235,   0.00611801687013120796,\n                              -0.0263523138347364731,  0.0673435029701473925,\n                              0.106904496764969714,    -0.225876975726312906,\n                              0.102899151085505236,    0.345744922388938558,\n                              0.208001961510195205,    -0.339920900339174381,\n                              -0.0769230769230768996,  0.308606699924183769,\n                              0.0800640769025435656,   -0.243252127705259863,\n                              0.230283093235919112,    0.315511507416476211,\n                              0.0782780363856436651,   0.235644256163828114,\n                              -0.00805822964025379798, -0.106453828406580006,\n                              -0.0828741930164744595,  -0.0545544725589980906,\n                              0.163178487966126384,    0.0584213790551420323,\n                              0.129099444873580549,    -0.0111166021473302189,\n                              0.0234069469019301535,   0.289473684210526438,\n                              -0.11952286093343939,    -0.16903085094570322,\n                              -0.226455406828919154,   -0.101499742867643794,\n                              0.0923832445064916297,   -0.213980246255456391,\n                              -0.0243698872117811029,  0.102062072615965699,\n                              -0.180648920932378193,   -0.171887912738082155,\n                              0.134303827337563381,    0.370479286817473996,\n                              0.0346324913051441474,   0.0923832445064916297,\n                              0.136169247617108663,    -0.111187397499165133,\n                              -0.180648920932378193,   0.402597402597402454,\n                              -0.126131244777378249,   -0.00325506760927014903,\n                              -0.0925820099772550753,  -0.0797325472097299426,\n                              0.0162985719454690065,   0.175324675324675272,\n                              0.119769068332542,       0.00820002755213886103,\n                              0.123302959857021499,    0.266154565349599859};\n      spec_src_indices = {\n          939, 606, 561, 238, 536, 298, 9,   47,  323, 626, 551, 587, 281,\n          271, 60,  633, 622, 645, 847, 757, 883, 796, 344, 678, 120, 128,\n          283, 414, 545, 634, 352, 967, 442, 160, 126, 464, 711, 970, 616,\n          153, 399, 533, 542, 280, 72,  635, 109, 767, 9,   747, 869, 443,\n          765, 307, 49,  768, 630, 455, 128, 804, 559, 471, 139, 542, 471,\n          433, 129, 76,  415, 69,  369, 160, 423, 960, 792, 622, 563, 878,\n          138, 39,  769, 381, 450, 485, 392, 644, 765, 138, 769, 848, 596,\n          542, 432, 135, 524, 117, 469, 632, 217, 462};\n      spec_dest_indices = {\n          939, 624, 397, 627, 718, 228, 27,  873, 708, 646, 380, 607, 373,\n          30,  724, 968, 120, 159, 181, 27,  883, 696, 361, 130, 615, 461,\n          866, 433, 561, 969, 515, 958, 605, 322, 434, 960, 775, 313, 452,\n          155, 722, 371, 74,  219, 722, 313, 273, 505, 351, 523, 239, 930,\n          379, 885, 67,  20,  461, 130, 927, 56,  876, 632, 303, 229, 947,\n          281, 149, 220, 263, 393, 694, 808, 433, 465, 865, 631, 392, 869,\n          478, 354, 869, 373, 433, 961, 391, 473, 703, 957, 545, 38,  940,\n          850, 739, 441, 202, 929, 452, 778, 154, 623};\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<2, 1>>) {\n      spec_matrix_elements = {0.0998868137724437249,   -0.0368268860757696992,\n                              0.0612372435695794151,   -0.0486152792664123132,\n                              0.120659949980976539,    -0.0304483694116856156,\n                              0.0119047619047619058,   -0.132221471336986252,\n                              0.141520356148795073,    0.00424705992864687736,\n                              -0.239045721866878669,   -0.0890870806374747803,\n                              0.0471704110602518753,   0.226455406828919126,\n                              -0.0537462134683446627,  0.0583211843519804221,\n                              0.275240941281590101,    0.070710678118654724,\n                              -0.151694353412810595,   -0.00424705992864687736,\n                              -0.0573350763461484281,  0.0479699664971018,\n                              0.119912822364083571,    -0.100854581131859855,\n                              0.0689284500168069886,   -0.174574312188793851,\n                              -0.0339513395963365258,  -0.15724272550828769,\n                              -0.0912870929175276513,  -0.257132681931364127,\n                              -0.148877473663755366,   0.142857142857142821,\n                              -0.0263523138347364731,  0.0704295212273763904,\n                              0.0346237086228670596,   0.0367762649025967156,\n                              0.00480458383776462619,  -0.0215823348736865483,\n                              0.160422236979936905,    0.112758849626553137,\n                              0.0176340014345247806,   -0.0541331961960766841,\n                              0.105194378270434999,    0.333333333333333259,\n                              -0.0861209939483038034,  0.0346770991073533028,\n                              -0.0292879710420045659,  0.0800640769025435517,\n                              -0.0203278907045435255,  -0.0518615278817878556,\n                              0.0588489886336499637,   0.0813115628181741434,\n                              -0.0577326943319701677,  -0.109108945117996167,\n                              0.0714285714285714107,   -0.0912870929175276791,\n                              -0.110781892280476205,   -0.302371578407381658,\n                              -0.00411926277285817096, 0.205688337801860466,\n                              -0.0150584650484208559,  0.154303349962091885,\n                              -0.0977914316728140109,  0.0798595706249925347,\n                              -0.159183436688108665,   -0.0539791896359412895,\n                              0.15191090506254995,     -0.0433904672936743091,\n                              0.224733287487747319,    -0.0525971891352174925,\n                              -0.204124145231931481,   0.289473684210526438,\n                              0.155686310081568774,    -0.223606797749978853,\n                              -0.0652050663696626259,  0.0771516749810459285,\n                              0.129336542309088254,    -0.015953836117931882,\n                              -0.0756237677477524795,  -0.0385758374905229504,\n                              -0.0231931803521356547,  0.063449337838824213,\n                              0.081464720917387351,    -0.177393718796724692,\n                              0.0607411913437327991,   -0.366515837104072373,\n                              -0.0162465914745207329,  -0.103723055763575711,\n                              -0.0379868588198792947,  -0.187256335179707689,\n                              0.117611367647716222,    0.211288563682129032,\n                              0.135486690699283596,    -0.0794986038317936766,\n                              0.0797325472097299426,   -0.118829688587177823,\n                              0.143486010795887797,    0.2192645048267573,\n                              0.172241987896607635,    -0.162098585919475013};\n      spec_src_indices = {\n          864,  947,  676,  1289, 1192, 541,  442,  128,  77,   289,  181,  363,\n          220,  624,  1128, 99,   1180, 414,  939,  532,  48,   290,  1281, 868,\n          56,   1306, 147,  1110, 423,  307,  1111, 1009, 30,   1010, 1262, 131,\n          647,  637,  560,  1252, 549,  1281, 1181, 1406, 1417, 288,  550,  389,\n          297,  470,  920,  1092, 1128, 474,  1234, 718,  534,  1000, 303,  229,\n          198,  829,  283,  585,  317,  1423, 293,  792,  1171, 1101, 891,  727,\n          484,  1315, 101,  361,  563,  625,  1122, 37,   1038, 392,  886,  936,\n          1010, 632,  219,  1200, 1163, 868,  928,  100,  1179, 1211, 702,  64,\n          643,  1102, 1270, 879};\n      spec_dest_indices = {\n          640,  525,  352,  1038, 644,  928,  1397, 1107, 1362, 1326,\n          1243, 344,  767,  876,  1442, 911,  927,  253,  695,  1407,\n          50,   531,  1048, 444,  558,  352,  1425, 794,  901,  1027,\n          302,  828,  190,  1000, 1353, 473,  240,  1202, 470,  864,\n          308,  544,  1443, 920,  1111, 29,   469,  1191, 542,  775,\n          1018, 607,  785,  788,  830,  383,  127,  9,    562,  949,\n          1073, 596,  1334, 200,  54,   622,  718,  128,  1260, 1118,\n          1144, 889,  809,  334,  605,  1163, 473,  236,  322,  1002,\n          859,  1203, 948,  1027, 1173, 1359, 964,  1100, 1261, 788,\n          686,  759,  946,  798,  522,  370,  1039, 1274, 360,  799};\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<2, 1, 1>>) {\n      spec_matrix_elements = {-0.236524958395633006,   -0.033693150757322779,\n                              -0.0314970394174355975,  -0.0121482382687465792,\n                              0.0434828276739336328,   -0.0328001102085554302,\n                              -0.0107713852617539116,  -0.114169960726483777,\n                              -0.0554028602268612502,  -0.0105537590856928463,\n                              0.216926753278579021,    0.014678246079545194,\n                              -0.0240121631470314213,  -0.0935876622158665628,\n                              0.113080558838435097,    -0.0912870929175276791,\n                              -0.0159103904613852043,  0.0489542047348339796,\n                              -0.0151835006529177311,  0.11914886517825013,\n                              0.100156451809192665,    0.0314812036701782733,\n                              -0.0512091556499188819,  -0.02592814894208657,\n                              -0.256536972630697135,   0.0276641667586243999,\n                              0.0324931829490414451,   -0.0331713515810334036,\n                              -0.0497219790389313368,  -0.00278942469585241083,\n                              -0.086307200360850142,   -0.00891436977900835967,\n                              -0.0710669054518701243,  -0.107565086965447532,\n                              -0.00451938453788867586, -0.0265897268632198022,\n                              0.0219298245614034937,   0.111747539066918561,\n                              0.0666173387526491356,   0.0307017543859648759,\n                              0.0285390896492696416,   -0.147499226289569962,\n                              0.0287563160680143586,   -0.175344715866013495,\n                              0.192538511553851321,    -0.049204013844837538,\n                              0.00130557603566829094,  -0.00991899501072693313,\n                              -0.0104366498437823522,  -0.0267431093370250894,\n                              -0.0366987921708786663,  0.29475045047344467,\n                              -0.0355334527259350413,  -0.0253184841770916456,\n                              0.0963460911639350548,   0.012897912048632788,\n                              0.0414941331443307374,   0.0364207071852280795,\n                              0.0737496131447849534,   -0.0255239280204499659,\n                              0.103203137423067151,    0.0656002204171108882,\n                              -0.128586124968409876,   -0.011100800288386687,\n                              0.104634187284983082,    0.0147493267086032612,\n                              0.0358402846713609322,   -0.0771516749810459285,\n                              0.179176378943197573,    -0.00265938185346856811,\n                              -0.207019667802706281,   -0.0455401634348317971,\n                              0.0169379040788670832,   0.0976932047012138705,\n                              -0.0667478331749710857,  0.0643951574733873378,\n                              -0.17604469755488264,    0.0225207709141969151,\n                              0.00168897164874002746,  0.0602491892595692427,\n                              0.0420098943759651677,   -0.033633639699815622,\n                              -0.00406883451158768488, 0.0706271450970323011,\n                              0.166361564022048908,    0.0190260597661797495,\n                              0.0167879784171954087,   -0.0277092943100922251,\n                              -0.0617457174241738405,  -0.0228390652007755476,\n                              0.0615457454896663272,   -0.100373172267978858,\n                              -0.0350877192982456468,  0.0707456228591711173,\n                              -0.00960916767552922116, 0.0555555555555555525,\n                              0.0354872753799466284,   0.0524950656957259912,\n                              0.120211230473384043,    0.0880949484043179604};\n      spec_src_indices = {\n          1343, 2470, 1498, 1273, 2345, 374,  2479, 808,  2405, 1372,\n          2225, 801,  1940, 637,  2641, 1458, 802,  2163, 364,  1121,\n          1018, 1999, 60,   2345, 1039, 101,  1941, 320,  1082, 303,\n          2424, 2082, 2541, 768,  1536, 1855, 1937, 364,  1103, 1126,\n          2156, 1019, 694,  1000, 705,  2575, 1613, 2243, 66,   702,\n          453,  1432, 1022, 140,  2637, 788,  445,  2727, 2162, 240,\n          635,  2145, 1731, 1595, 1680, 1290, 1856, 2064, 793,  394,\n          2045, 135,  2072, 2346, 1048, 1117, 758,  2093, 144,  558,\n          2021, 1616, 220,  1109, 2008, 1022, 1665, 1284, 390,  1981,\n          1181, 1333, 1211, 474,  78,   1161, 2244, 516,  57,   1595};\n      spec_dest_indices = {\n          57,   381,  1801, 1352, 2571, 525,  2019, 1619, 1200, 1184,\n          1910, 802,  1121, 1356, 1758, 1153, 1424, 2345, 1525, 2912,\n          687,  2253, 1335, 1923, 2659, 280,  940,  560,  1921, 2507,\n          551,  2782, 940,  2549, 707,  2649, 1937, 1356, 291,  316,\n          401,  1516, 2016, 2629, 1698, 1109, 1121, 623,  2499, 361,\n          1569, 2583, 2784, 2072, 2080, 1282, 1730, 550,  2313, 1281,\n          533,  1202, 1476, 2741, 2905, 1666, 711,  1153, 306,  1051,\n          1498, 1614, 2632, 2588, 49,   154,  1902, 2478, 768,  2728,\n          147,  614,  2379, 1111, 1909, 2021, 2377, 1048, 2638, 1927,\n          596,  2619, 2590, 1761, 725,  1568, 1596, 1397, 2335, 145};\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<3, 2, 1>>) {\n      spec_matrix_elements = {-0.0939336436627723897,  -0.00109934990475967138,\n                              0.0464578894448384044,   -0.0370096326403606124,\n                              -0.0700185391974654514,  0.0388096137776548361,\n                              -0.010196694783552002,   -0.019243342954807275,\n                              0.0912870929175276791,   -0.020759133108398227,\n                              -0.0832007846040780236,  -0.0517549169506765494,\n                              0.018698939800169137,    -0.0156269076979498392,\n                              0.0861209939483037479,   -0.0964236519799837738,\n                              -0.0149065175786269096,  -0.0630656223886891104,\n                              0.0449664133206483588,   0.0021275054827748531,\n                              0.0110498924021965907,   -0.0489542047348339796,\n                              -0.00146248575911424317, 0.029737210435323215,\n                              -0.0715660333534588744,  0.0243586955293484883,\n                              -0.109108945117996126,   -0.0763618278185862476,\n                              0.0506995063159608531,   0.0887882789088388497,\n                              0.00893593559191470203,  0.0936102412420934393,\n                              -0.0728219081254418837,  0.0261617405880388496,\n                              0.144841364875580247,    -0.11267292641071408,\n                              -0.018601633295108097,   0.070710678118654724,\n                              -0.0504499419471959379,  -0.01722419878966076,\n                              0.0797325472097299148,   -0.0042022742138274902,\n                              -0.0429651565115148903,  0.0340206908719885501,\n                              -0.0140316454742531495,  -0.0105861900492919409,\n                              0.00495949750536344228,  0.0111169260529303503,\n                              0.0397229061149478452,   -0.0303670013058354796,\n                              -0.0306816936622102132,  0.0138451413925856654,\n                              -0.244327219279265345,   0.0753778361444408657,\n                              -0.0371432074125348441,  -0.0425128284555631869,\n                              0.0837073498279864991,   0.00775403508665392475,\n                              0.175213675213675202,    0.026577515736576629,\n                              0.0322993157588601781,   -0.049204013844837545,\n                              -0.0431992429765821959,  -0.00718733218694087191,\n                              0.0238882962546999311,   0.0375862832088510457,\n                              0.0288021282940771027,   -0.0115248307462979538,\n                              0.07288942961247942,     0.00421887936996421121,\n                              0.044165297066775612,    -0.00807482893971504627,\n                              -0.0509647191437625213,  0.0402911482012690003,\n                              0.00741211500712467362,  0.0707106781186547101,\n                              0.0631578947368420796,   -0.100732364854311196,\n                              -0.0329818296612102227,  0.00210943968498210734,\n                              0.0130103488787217322,   0.0234459982817776559,\n                              -0.00423447601971677079, 0.0421765951582413409,\n                              0.0397229061149478452,   -0.0194048068888274215,\n                              0.0374757353170602839,   0.0426364519632096012,\n                              0.0279848798118845707,   -0.0162764291332195936,\n                              -0.0234638890458728142,  0.0415637399793525597,\n                              0.0858908501906280319,   -0.0355334527259350622,\n                              -0.00630341132074123574, 0.0120701137396316896,\n                              0.00719117465296618789,  0.0865592715571676247,\n                              -0.0252496390847059375,  -0.025249639084705934};\n      spec_src_indices = {\n          1860, 3558, 855,  3126, 460,  1039, 3067, 1612, 1505, 3317,\n          929,  3189, 2985, 291,  3781, 1569, 1291, 685,  2548, 3148,\n          128,  3135, 3624, 140,  1362, 4025, 1993, 594,  4028, 3798,\n          1032, 2176, 1262, 393,  2328, 146,  1447, 1548, 4118, 2729,\n          3213, 2893, 3288, 3513, 2988, 3928, 2134, 2982, 2335, 4090,\n          3521, 2419, 303,  919,  765,  3390, 2095, 128,  2559, 2567,\n          3278, 2899, 2660, 2227, 3310, 3349, 2812, 40,   4005, 317,\n          3207, 2226, 1314, 2126, 2865, 3249, 1126, 784,  946,  535,\n          156,  4258, 2738, 3722, 2335, 2012, 633,  3967, 2882, 3542,\n          1758, 1767, 1181, 2135, 2646, 137,  2506, 3766, 1008, 2304};\n      spec_dest_indices = {\n          1122, 2804, 785,  1589, 309,  2954, 2741, 2668, 1423, 1993,\n          3782, 3736, 2632, 384,  2385, 2142, 1352, 2801, 3772, 2102,\n          3866, 482,  3307, 310,  1361, 3034, 3938, 2287, 1120, 622,\n          1201, 323,  1496, 391,  364,  2264, 465,  3088, 3237, 2711,\n          198,  2136, 2182, 516,  533,  2802, 605,  3785, 211,  4361,\n          3145, 320,  232,  4258, 2728, 1121, 3057, 3340, 1830, 2781,\n          1522, 380,  2174, 1504, 564,  1919, 2748, 2305, 4330, 1182,\n          2170, 1035, 1342, 2399, 3620, 172,  1288, 1280, 46,   802,\n          4038, 3259, 4180, 309,  373,  4088, 58,   1374, 2343, 4120,\n          2423, 3870, 2414, 455,  2792, 1263, 303,  2732, 351,  999};\n    }\n  }\n\n  SparseMatrixType matrix;\n  ylm::TensorYlm::fill_cart_to_sphere<TensorStructure>(\n      make_not_null(&matrix), ell_max, coefficient_normalization);\n\n  CAPTURE(matrix.size());\n\n  // Loop over spec_matrix_elements and make sure all the cases agree.\n  if (coefficient_normalization ==\n      ylm::TensorYlm::CoefficientNormalization::Standard) {\n    for (size_t i = 0; i < spec_matrix_elements.size(); ++i) {\n      CAPTURE(spec_dest_indices[i]);\n      CAPTURE(spec_src_indices[i]);\n      CHECK(matrix(spec_dest_indices[i], spec_src_indices[i]) ==\n            approx(spec_matrix_elements[i]));\n    }\n  } else {\n    CHECK(coefficient_normalization ==\n          ylm::TensorYlm::CoefficientNormalization::Spherepack);\n    // Here the expected coefficients are off by a factor of (-1)^{m+m'),\n    // So we must multiply the matrix elements by this factor.\n    // Most of the logic below is for recovering m and m' from the\n    // SparseMatrix indices.\n    ylm::SpherepackIterator src_iter(ell_max, ell_max, 1, false);\n    ylm::SpherepackIterator dest_iter(ell_max, ell_max, 1, false);\n    for (size_t i = 0; i < spec_matrix_elements.size(); ++i) {\n      CAPTURE(spec_dest_indices[i]);\n      CAPTURE(spec_src_indices[i]);\n      // We need to recover the azimuthal index m from the\n      // SparseMatrix indices.\n      //\n      // First compute offsets into arrays.\n      // The SparseMatrices are indexed by\n      // k = iter() + component_index*iter.spherepack_array_size()\n      // where iter is a SpherepackIterator.\n      // The offset is the value returned by iter().\n      const size_t dest_offset =\n          spec_dest_indices[i] % dest_iter.spherepack_array_size();\n      const size_t src_offset =\n          spec_src_indices[i] % src_iter.spherepack_array_size();\n      // Reset the iterators so we can query them for m and m'.\n      src_iter.set(src_iter.compact_index(src_offset).value());\n      dest_iter.set(dest_iter.compact_index(dest_offset).value());\n      // factor is (-1)^{m+m'}\n      const double factor =\n          ((src_iter.m() + dest_iter.m()) % 2 == 0 ? 1.0 : -1.0);\n      CHECK(matrix(spec_dest_indices[i], spec_src_indices[i]) ==\n            approx(factor * spec_matrix_elements[i]));\n    }\n  }\n}\n}  // namespace\n\n// For debug builds this test sometimes times out if you don't increase\n// the timeout.\n// [[TimeOut, 40]]\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.TensorYlmCartToSphere\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  const size_t ell_max = 8;\n\n  for (const auto norm :\n       std::vector<ylm::TensorYlm::CoefficientNormalization>{\n           {ylm::TensorYlm::CoefficientNormalization::Standard,\n            ylm::TensorYlm::CoefficientNormalization::Spherepack}}) {\n    test_tensorylm_cart_to_sphere_vs_spec<\n        typename tnsr::i<DataVector, 3>::structure, SimpleSparseMatrix>(ell_max,\n                                                                        norm);\n    test_tensorylm_cart_to_sphere_vs_spec<\n        typename tnsr::ii<DataVector, 3>::structure, SimpleSparseMatrix>(\n        ell_max, norm);\n    test_tensorylm_cart_to_sphere_vs_spec<\n        typename tnsr::ij<DataVector, 3>::structure, SimpleSparseMatrix>(\n        ell_max, norm);\n    test_tensorylm_cart_to_sphere_vs_spec<\n        typename tnsr::ijj<DataVector, 3>::structure, SimpleSparseMatrix>(\n        ell_max, norm);\n    test_tensorylm_cart_to_sphere_vs_spec<\n        typename tnsr::ijk<DataVector, 3>::structure, SimpleSparseMatrix>(\n        ell_max, norm);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_TensorYlmFilter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstring>\n#include <filesystem>\n#include <fstream>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmCartToSphere.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmFilter.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmSphereToCart.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace {\n\ntemplate <typename TensorStructure>\nstd::string symm_to_string() {\n  if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                               Symmetry<1>>) {\n    return \"a\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<1, 1>>) {\n    return \"aa\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<2, 1>>) {\n    return \"ab\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<3, 2, 1>>) {\n    return \"abc\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<2, 1, 1>>) {\n    return \"abb\";\n  }\n  return \"\";\n}\n\ntemplate <typename TensorType>\nstruct MyTag : db::SimpleTag {\n  using type = TensorType;\n  static std::string name() {\n    return \"MyTag\" + symm_to_string<typename TensorType::structure>();\n  }\n};\n\n// Tests that applying the filter matrix is equivalent to doing a\n// cart-to-sphere, then a truncation filter, then a sphere-to-cart.\ntemplate <typename TensorType>\nvoid test_filter_vs_transforms(\n    const size_t ell_max, const size_t number_of_ell_modes_to_kill,\n    const std::optional<size_t> half_power,\n    const ylm::TensorYlm::CoefficientNormalization coefficient_normalization) {\n  ylm::SpherepackIterator it(ell_max, ell_max, 1, false);\n  const size_t spectral_size = it.spherepack_array_size();\n\n  Variables<tmpl::list<MyTag<TensorType>>> A(spectral_size, 0.0);\n  Variables<tmpl::list<MyTag<TensorType>>> B(spectral_size, 0.0);\n  Variables<tmpl::list<MyTag<TensorType>>> C(spectral_size, 0.0);\n  Variables<tmpl::list<MyTag<TensorType>>> D(spectral_size, 0.0);\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n\n  // Fill A with random numbers\n  for (auto& a : get<MyTag<TensorType>>(A)) {\n    for (it.reset(); it; ++it) {\n      if (it.coefficient_array() !=\n              ylm::SpherepackIterator::CoefficientArray::b or\n          it.m() != 0) {\n        a[it()] = unit_dis(gen);\n      }\n    }\n  }\n\n  // Convert A to B.\n  const gsl::span<double> a_span(A.data(), A.size());\n  gsl::span<double> b_span(B.data(), B.size());\n  SimpleSparseMatrix cart_to_sphere;\n  if constexpr (TensorType::structure::rank() == 0) {\n    // Rank zero doesn't have any difference between cartesian and spherical.\n    std::memcpy(B.data(), A.data(), A.size() * sizeof(double));\n  } else {\n    ylm::TensorYlm::fill_cart_to_sphere<typename TensorType::structure>(\n        make_not_null(&cart_to_sphere), ell_max, coefficient_normalization);\n    cart_to_sphere.increment_multiply_on_right(make_not_null(&b_span), 0, 1,\n                                               a_span, 0, 1);\n  }\n\n  // Apply the filter to B.\n  static constexpr double alpha = 36.0;\n  const auto lcutplus =\n      static_cast<size_t>(ell_max - number_of_ell_modes_to_kill);\n  const size_t lcutminus =\n      half_power.has_value()\n          ? static_cast<size_t>(std::ceil(\n                static_cast<double>(lcutplus + 1) *\n                pow(std::numeric_limits<double>::epsilon() / alpha,\n                    1.0 / (2.0 * static_cast<double>(half_power.value())))))\n          : lcutplus + 1;\n\n  for (auto& b : get<MyTag<TensorType>>(B)) {\n    for (it.reset(); it; ++it) {\n      if (it.l() < lcutminus) {\n        continue;\n      }\n      if (it.l() <= lcutplus) {\n        b[it()] *=\n            exp(-alpha * integer_pow(static_cast<double>(it.l()) /\n                                         static_cast<double>(lcutplus + 1),\n                                     2 * static_cast<int>(half_power.value())));\n      } else {\n        b[it()] = 0.0;\n      }\n    }\n  }\n\n  // Convert B to C\n  gsl::span<double> c_span(C.data(), C.size());\n  SimpleSparseMatrix sphere_to_cart;\n  if constexpr (TensorType::structure::rank() == 0) {\n    // Rank zero doesn't have any difference between cartesian and spherical.\n    std::memcpy(C.data(), B.data(), B.size() * sizeof(double));\n  } else {\n    ylm::TensorYlm::fill_sphere_to_cart<typename TensorType::structure>(\n        make_not_null(&sphere_to_cart), ell_max, coefficient_normalization);\n    sphere_to_cart.increment_multiply_on_right(make_not_null(&c_span), 0, 1,\n                                               b_span, 0, 1);\n  }\n\n  // Apply the filter to A directly, output into D.\n  // First set D equal to A (thus computing the delta term),\n  // since the matrix encodes only the non-delta part.\n  get<MyTag<TensorType>>(D) = get<MyTag<TensorType>>(A);\n  gsl::span<double> d_span(D.data(), D.size());\n  SimpleSparseMatrix filter_matrix;\n  ylm::TensorYlm::fill_filter<typename TensorType::structure>(\n      make_not_null(&filter_matrix), ell_max, number_of_ell_modes_to_kill,\n      half_power, coefficient_normalization);\n  filter_matrix.increment_multiply_on_right(make_not_null(&d_span), 0, 1,\n                                            a_span, 0, 1);\n\n  // C should equal D.\n  const auto& d_vector = get<MyTag<TensorType>>(D);\n  const auto& c_vector = get<MyTag<TensorType>>(C);\n  CAPTURE(symm_to_string<typename TensorType::structure>());\n  CAPTURE(number_of_ell_modes_to_kill);\n  CAPTURE(half_power);\n  for (size_t i = 0; i < d_vector.size(); ++i) {\n    CAPTURE(i);\n    for (it.reset(); it; ++it) {\n      CAPTURE(it.l());\n      CAPTURE(it.m());\n      CAPTURE(it.coefficient_array());\n      CHECK(d_vector[i][it()] == approx(c_vector[i][it()]));\n    }\n  }\n}\n\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid test_tensorylm_filter_vs_spec(\n    const size_t ell_max, const size_t number_of_ell_modes_to_kill,\n    const std::optional<size_t> half_power,\n    const ylm::TensorYlm::CoefficientNormalization coefficient_normalization) {\n  // There are two \"modes\" for this test.  If test_all_elements is\n  // true, it tests all the matrix elements vs SpEC, reading .txt\n  // files that were output by SpEC. This test was done by Mark\n  // Scheel, and is not done in CI.\n  //\n  // Otherwise, it tests a random subset of the matrix elements vs\n  // SpEC here (the random numbers being previously determined by\n  // SpEC), using inlined values, which is done in CI.\n  constexpr bool test_all_elements = false;\n\n  std::vector<double> spec_matrix_elements;\n  std::vector<size_t> spec_src_indices;\n  std::vector<size_t> spec_dest_indices;\n\n  if constexpr (test_all_elements) {\n    // Read from simple file that was output by SpEC.\n    // This test was run by Mark Scheel after producing the SpEC\n    // files.\n    // Because the SpEC files are so large, we don't run this\n    // test in CI.\n    //\n    // File has a simple binary format:\n    //   size (a size_t)\n    //   matrix_elements (a vector of doubles of length size)\n    //   src_indices (a vector of size_t of length size)\n    //   dest_indices (a vector of size_t of length size)\n    const std::string filename =\n        \"TensorYlmCoefs_\" + symm_to_string<TensorStructure>() + \"_\" +\n        std::to_string(half_power.value_or(0)) + \".txt\";\n    std::ifstream file;\n    file.open(filename, std::ios::in | std::ios::binary);\n    size_t num_indices = 0;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(&num_indices), sizeof(size_t));\n    spec_matrix_elements.resize(num_indices);\n    spec_src_indices.resize(num_indices);\n    spec_dest_indices.resize(num_indices);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_matrix_elements.data()),\n              static_cast<std::streamsize>(sizeof(double) * num_indices));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_src_indices.data()),\n              static_cast<std::streamsize>(sizeof(size_t) * num_indices));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_dest_indices.data()),\n              static_cast<std::streamsize>(sizeof(size_t) * num_indices));\n  } else {\n    if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                 Symmetry<1>>) {\n      if (half_power.value_or(0) == 28) {\n        spec_src_indices = {\n            373, 120, 354, 361, 289, 303, 210, 290, 370, 310, 344, 363, 200,\n            192, 293, 119, 66,  73,  49,  228, 445, 463, 434, 393, 47,  370,\n            292, 199, 280, 50,  111, 471, 319, 57,  45,  361, 343, 443, 74,\n            126, 283, 270, 303, 306, 190, 18,  279, 369, 232, 117, 30,  129,\n            396, 371, 110, 283, 129, 435, 100, 190, 217, 158, 443, 149, 50,\n            262, 472, 380, 27,  454, 236, 451, 29,  363, 119, 281, 343, 283,\n            190, 72,  18,  128, 262, 283, 288, 289, 370, 64,  27,  121, 370,\n            49,  19,  270, 30,  50,  129, 470, 468, 182};\n        spec_dest_indices = {\n            293, 443, 29,  36,  369, 303, 210, 370, 290, 310, 19,  363, 444,\n            111, 48,  202, 66,  73,  49,  228, 445, 463, 190, 393, 370, 47,\n            49,  120, 37,  48,  190, 471, 319, 57,  45,  36,  20,  199, 74,\n            128, 283, 352, 303, 306, 190, 343, 361, 369, 232, 200, 273, 131,\n            396, 289, 110, 363, 127, 435, 181, 192, 217, 158, 201, 149, 293,\n            344, 472, 380, 27,  129, 236, 128, 27,  363, 202, 283, 18,  40,\n            192, 72,  18,  453, 344, 363, 370, 289, 290, 64,  272, 119, 47,\n            49,  19,  29,  271, 293, 127, 470, 468, 424};\n        spec_matrix_elements = {0.258696961803670078,\n                                -0.12474710975541925,\n                                4.12380498300465295e-05,\n                                0.596254677978356185,\n                                0.49786253511962425,\n                                -1,\n                                -0.736399143678998058,\n                                0.288588542569257755,\n                                0.288588542569257755,\n                                -0.999999999999999556,\n                                4.52534306324044533e-12,\n                                -0.355616162872347152,\n                                -0.332553972411752219,\n                                6.49364063228479566e-05,\n                                0.060975458654577458,\n                                0.0587766567045578323,\n                                -0.999999999999999778,\n                                -1,\n                                -0.672771350773928578,\n                                -0.999999999999999778,\n                                -0.200107746336269959,\n                                -0.999999999999999556,\n                                -4.56326525141256172e-05,\n                                -1,\n                                -0.288588542569257811,\n                                -0.288588542569257811,\n                                -0.327228649226071311,\n                                -0.088164985056836731,\n                                -0.0111414147649815665,\n                                -0.060975458654577458,\n                                -9.31472472856344617e-06,\n                                -0.999999999999999889,\n                                -0.999999999999999889,\n                                -0.999999999999999889,\n                                -0.818206305985515914,\n                                0.596254677978356185,\n                                -1.81013722529617853e-12,\n                                -0.32995459399023791,\n                                -1,\n                                0.131722045502268681,\n                                -0.511124579408424373,\n                                -4.16567232076563951e-05,\n                                -1,\n                                0.999999999999999778,\n                                -3.60757918496048847e-05,\n                                4.43391256639299217e-12,\n                                -0.298127338989178092,\n                                -0.545515764963789951,\n                                -0.999999999999999778,\n                                -0.105377312481470586,\n                                -6.49364063228479566e-05,\n                                -0.060975458654577458,\n                                -1,\n                                0.144294271284628878,\n                                -5.77212560985444827e-05,\n                                0.0628825066636218566,\n                                -0.1178157791513572,\n                                -3.36707402663893738e-05,\n                                4.52534306324044613e-12,\n                                -9.31472472856344787e-06,\n                                -0.999999999999999889,\n                                -0.999999999999999889,\n                                -0.12474710975541925,\n                                -0.999999999999999889,\n                                -0.409035811532589,\n                                4.52534306324044613e-12,\n                                -0.999999999999999445,\n                                -0.999999999999999778,\n                                -4.81010497656824391e-05,\n                                0.038564266144378162,\n                                -1,\n                                -0.288588542569257755,\n                                2.63460200818129502e-05,\n                                -0.355616162872347152,\n                                0.0587766567045578323,\n                                -0.0587766567045578323,\n                                8.86782513278598433e-12,\n                                -0.488875420591575571,\n                                -9.31472472856344787e-06,\n                                -1,\n                                -5.4304116758885352e-12,\n                                0.0890603577582286393,\n                                4.52534306324044613e-12,\n                                0.0628825066636218566,\n                                -0.199145014047849567,\n                                -0.672771350773928467,\n                                0.288588542569257755,\n                                -0.999999999999999778,\n                                -1.31730100409064734e-05,\n                                0.0587766567045578323,\n                                -0.288588542569257811,\n                                -0.672771350773928578,\n                                -7.24054890118471414e-12,\n                                -1.31730100409064734e-05,\n                                9.31472472856344617e-06,\n                                -0.409035811532589,\n                                -0.1178157791513572,\n                                -0.999999999999999667,\n                                0.999999999999999778,\n                                -1.81013722529617853e-12};\n      } else {\n        spec_src_indices = {\n            211, 323, 47,  293, 371, 40,  369, 49,  288, 36,  120, 306, 65,\n            210, 382, 46,  128, 118, 210, 49,  200, 119, 289, 280, 443, 154,\n            198, 131, 137, 70,  371, 73,  374, 280, 469, 156, 212, 291, 378,\n            208, 454, 443, 477, 454, 37,  128, 130, 281, 463, 290, 280, 126,\n            360, 38,  121, 48,  207, 398, 290, 451, 363, 129, 290, 72,  362,\n            118, 281, 140, 199, 485, 378, 323, 293, 40,  207, 312, 202, 49,\n            364, 443, 208, 200, 135, 46,  198, 373, 279, 364, 441, 210, 234,\n            45,  130, 370, 121, 64,  200, 119, 288, 363};\n        spec_dest_indices = {\n            130, 323, 292, 373, 48,  281, 289, 292, 290, 361, 201, 306, 65,\n            208, 382, 289, 451, 118, 208, 372, 200, 121, 291, 39,  118, 154,\n            119, 454, 137, 70,  289, 73,  374, 280, 469, 156, 454, 50,  378,\n            452, 212, 199, 477, 131, 39,  451, 130, 36,  463, 45,  360, 128,\n            37,  363, 202, 293, 207, 398, 290, 128, 281, 127, 45,  72,  280,\n            201, 36,  140, 443, 485, 378, 323, 48,  363, 207, 312, 121, 372,\n            282, 199, 208, 119, 135, 369, 198, 291, 279, 39,  199, 452, 234,\n            290, 128, 370, 444, 64,  202, 119, 288, 40};\n        spec_matrix_elements = {0.327272727272727215,\n                                -1,\n                                0.0944754985946660436,\n                                0.258731808559231058,\n                                -0.311753239990586239,\n                                0.0587944735792131287,\n                                0.248964798865984605,\n                                -0.327272727272727215,\n                                -0.131739788601722196,\n                                0.29814239699997197,\n                                0.366666666666666585,\n                                0.999999999999999889,\n                                -0.999999999999999667,\n                                0.117831649061961044,\n                                -0.999999999999999778,\n                                -0.218181818181818088,\n                                -0.288627415752500727,\n                                -0.233333333333333254,\n                                0.117831649061961044,\n                                -0.308555686335947987,\n                                -0.377777777777777768,\n                                0.0587944735792131287,\n                                0.117831649061961086,\n                                0.0881917103688196757,\n                                0.329983164553722008,\n                                -0.999999999999999778,\n                                -0.10540925533894599,\n                                -0.258731808559231058,\n                                -0.999999999999999889,\n                                -0.999999999999999667,\n                                0.144313707876250391,\n                                -1,\n                                -0.999999999999999778,\n                                -0.455555555555555436,\n                                -1.00000000000000022,\n                                -1,\n                                -0.258731808559231058,\n                                -0.0609836721136306215,\n                                -0.999999999999999556,\n                                -0.144313707876250419,\n                                -0.258731808559231058,\n                                -0.329983164553722008,\n                                1,\n                                -0.258731808559231058,\n                                0.0881917103688196757,\n                                -0.288627415752500727,\n                                -0.672727272727272729,\n                                -0.210818510677891952,\n                                -0.999999999999999778,\n                                0.263479577203444448,\n                                0.496903994999953136,\n                                0.131739788601722196,\n                                -0.248451997499976596,\n                                0.332591767713239228,\n                                0.488888888888888873,\n                                0.0609836721136306215,\n                                -0.818181818181818232,\n                                -0.999999999999999667,\n                                -0.781818181818181746,\n                                -0.288627415752500782,\n                                0.332591767713239228,\n                                -0.117831649061961044,\n                                0.263479577203444448,\n                                -1,\n                                0.329983164553722008,\n                                0.0881917103688196757,\n                                -0.210818510677891952,\n                                -0.999999999999999889,\n                                -0.329983164553722008,\n                                -0.999999999999999889,\n                                -0.999999999999999556,\n                                -1,\n                                0.0609836721136306215,\n                                -0.062853936105470895,\n                                -0.818181818181818232,\n                                -0.999999999999999889,\n                                0.488888888888888873,\n                                -0.308555686335947987,\n                                0.282842712474618951,\n                                -0.329983164553722008,\n                                -0.945454545454545103,\n                                0.244444444444444464,\n                                0.999999999999999778,\n                                -0.497929597731969209,\n                                -0.33333333333333337,\n                                0.0385694607919934707,\n                                0.33333333333333337,\n                                0.282842712474618951,\n                                0.248451997499976596,\n                                -0.311753239990586239,\n                                -1,\n                                0.131739788601722196,\n                                -0.0944754985946660436,\n                                -0.563636363636363713,\n                                -0.062853936105470895,\n                                -0.999999999999999667,\n                                -0.0587944735792131287,\n                                -0.377777777777777768,\n                                0.818181818181818232,\n                                -0.062853936105470895};\n      }\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<1, 1>>) {\n      if (half_power.value_or(0) == 28) {\n        spec_src_indices = {\n            839, 38,  911, 293, 48,  757, 344, 298, 546, 182, 949, 693, 216,\n            623, 463, 540, 617, 322, 694, 75,  380, 283, 472, 803, 847, 293,\n            786, 280, 513, 542, 546, 535, 668, 402, 676, 930, 956, 880, 190,\n            695, 788, 302, 192, 202, 948, 929, 848, 524, 309, 775, 765, 57,\n            108, 45,  302, 577, 464, 45,  463, 453, 361, 380, 361, 616, 838,\n            217, 623, 697, 705, 297, 27,  40,  131, 604, 666, 586, 108, 769,\n            857, 496, 685, 119, 382, 685, 524, 343, 947, 603, 603, 783, 542,\n            534, 432, 756, 180, 363, 454, 697, 848, 101};\n        spec_dest_indices = {\n            27,  36,  667, 50,  293, 27,  262, 787, 303, 587, 137, 129, 460,\n            705, 463, 381, 212, 322, 128, 75,  785, 36,  472, 803, 280, 293,\n            299, 769, 27,  786, 60,  779, 181, 402, 676, 199, 956, 880, 595,\n            695, 381, 59,  192, 121, 221, 685, 524, 846, 309, 857, 280, 378,\n            110, 49,  545, 577, 623, 855, 624, 940, 36,  382, 526, 451, 351,\n            702, 220, 613, 707, 56,  757, 361, 452, 686, 666, 668, 596, 525,\n            775, 820, 685, 931, 298, 202, 766, 20,  220, 200, 121, 380, 383,\n            856, 190, 514, 667, 38,  127, 127, 361, 182};\n        spec_matrix_elements = {\n            -0.182532032193009869,    0.0143507581949818136,\n            -1.92404171910671323e-05, -0.454527088688361447,\n            -0.01354371367795313,     0.0137630976678983774,\n            2.88606244289977984e-05,  0.0242212241945165413,\n            0.173053612571479998,     -6.73414588111320765e-05,\n            0.00382971180861246124,   0.0716883272081236256,\n            -0.0226568806060302025,   0.0597821570701475985,\n            -0.8321904362943221,      0.0214942042168655145,\n            -0.227263544344180723,    -0.999999999999999556,\n            -0.0961897019931985953,   -1,\n            -0.134247650964542165,    -0.0760336607435094236,\n            -0.999999999999999556,    -0.999999999999999334,\n            -0.0151366475571672252,   -0.54547291131163822,\n            0.00209761954632103897,   -0.0906862301222128575,\n            0.00399711459117454287,   -0.0597821570701475985,\n            -0.173053612571480192,    -0.086232320601223364,\n            2.88606244289977984e-05,  -1.00000000000000022,\n            -0.250050506096823555,    -0.0801694230049684042,\n            -0.999999999999998002,    -0.999999999999999223,\n            -0.0595478600450720374,   -0.684868075093746587,\n            0.0269444109340468765,    -0.230738150095306738,\n            -0.502383838444662278,    0.48787144153556683,\n            0.107777643736187576,     -0.104999095820698912,\n            0.190866062340942888,     0.0957934083764296201,\n            -0.999999999999999667,    0.288601500295699143,\n            0.203273040287282353,     0.0859768168674619471,\n            -0.0673680301706471307,   -0.0380223182189181538,\n            0.230738150095306738,     -5.4304116758885352e-12,\n            0.00898147031134895885,   -0.101010101008729952,\n            0.0919130834066997915,    0.154257064577512704,\n            0.697884744693425541,     -0.0612753889377998864,\n            0.00567801312189070498,   0.0416485573242138668,\n            0.329897776847645929,     -0.0906275224241209765,\n            -0.0536159653205749154,   0.0555407611991799166,\n            0.0269444109340468176,    0.0286589389558206259,\n            0.00344077441697460129,   -0.00283900656094535379,\n            -0.0331869236893158059,   0.117845007934657048,\n            -5.77212542884072375e-05, 2.4050519452429545e-05,\n            -0.0238979859258578176,   -0.119999938439513149,\n            0.144300750147849627,     9.05068612648088863e-13,\n            -0.598495474446634868,    0.0961961808564191501,\n            -0.0242212241945165135,   0.00567801312189070498,\n            -0.0953600700397713302,   -2.69365840674939825e-05,\n            0.107231930641149859,     -0.023948352094107405,\n            0.0190084151858773594,    0.114635755823282615,\n            0.0044907351556744933,    -0.0392851513079558595,\n            0.0824952725735153269,    0.247444160997338236,\n            2.35646022781940566e-05,  0.266431070974043394,\n            -0.0555407611991799166,   -0.0555407611991799166,\n            0.104999095820698912,     3.36707294055660383e-05};\n      } else {\n        spec_src_indices = {\n            372, 848, 838, 775, 381, 271, 694, 849, 839, 362, 460, 369, 705,\n            846, 301, 769, 765, 117, 939, 595, 603, 299, 360, 702, 596, 127,\n            117, 482, 525, 443, 758, 523, 707, 288, 540, 535, 678, 697, 370,\n            532, 513, 140, 535, 128, 137, 544, 382, 57,  36,  937, 364, 290,\n            757, 129, 49,  888, 645, 191, 947, 543, 216, 464, 778, 381, 293,\n            607, 532, 533, 120, 46,  381, 442, 202, 45,  302, 158, 382, 138,\n            837, 617, 768, 221, 677, 622, 378, 788, 454, 149, 617, 128, 786,\n            30,  621, 282, 272, 622, 949, 271, 462, 606};\n        spec_dest_indices = {\n            533, 524, 273, 531, 540, 353, 616, 767, 27,  767, 625, 534, 623,\n            846, 786, 525, 37,  603, 452, 595, 200, 546, 280, 138, 433, 937,\n            603, 482, 767, 928, 840, 767, 627, 47,  56,  47,  110, 615, 533,\n            289, 837, 217, 290, 130, 139, 788, 59,  543, 768, 129, 37,  47,\n            759, 208, 45,  888, 645, 110, 137, 300, 703, 218, 373, 54,  534,\n            200, 858, 531, 201, 776, 784, 607, 442, 47,  541, 158, 55,  946,\n            837, 452, 766, 136, 109, 136, 543, 381, 127, 149, 613, 614, 54,\n            353, 621, 282, 352, 622, 137, 514, 464, 928};\n        spec_matrix_elements = {\n            0.0816496580927723592,   0.190909090909090695,\n            0.0184427778390829286,   -0.453669189044683008,\n            0.0859883980360244826,   0.165643115532629392,\n            -0.0833195580901061883,  0.136060268609961371,\n            -0.182574185835055303,   -0.00606060606060608575,\n            -0.0847857038670079277,  0.0717100579769650026,\n            0.119580419580419539,    -0.303030303030302817,\n            -0.160869562344174599,   -0.119993878019535233,\n            -0.0948634899545365284,  -0.189393939393939448,\n            0.128660067297702241,    -0.244047619047618958,\n            -0.0239566489406695358,  -0.015558458364536348,\n            0.203278907045435386,    -0.0859883980360246214,\n            -0.0790569415042094437,  -0.0181818181818185209,\n            -0.189393939393939448,   -0.999999999999999556,\n            -0.119052735033716217,   -0.325697668910167215,\n            0.217732421580726837,    -0.224988521286628718,\n            0.199852016257947379,    0.0878265257344815198,\n            -0.0214970995090061519,  -0.0157459164324443533,\n            -0.204124145231931564,   0.137135860593754738,\n            0.016034856430694619,    0.00606060606060603718,\n            0.158730158730158638,    -0.0142028643437691754,\n            -0.0157459164324443811,  -0.0157459164324443429,\n            -0.0555383012854888392,  -0.0246000826564165692,\n            -0.196800661251332693,   -0.018881118881119055,\n            -0.0582644221062840612,  0.0392772163539870101,\n            0.0907068457399743122,   -0.0969696969696969557,\n            -0.0645497224367902606,  -0.0163655068141612819,\n            -0.076060002412188632,   -0.999999999999998446,\n            -0.999999999999999556,   0.299999999999999933,\n            -0.0783216783216784673,  0.0440559440559441309,\n            0.0226599325119155454,   0.008982680124940641,\n            -0.181818181818181768,   -0.0859883980360244826,\n            -0.0271038542727247145,  0.0481045692920834336,\n            -0.0654620272566450306,  0.161015297179882777,\n            0.228030303030302972,    -0.0160348564306944247,\n            -0.092878085123826476,   0.00566917785874839278,\n            -0.00566917785874839538, -0.080507648589941333,\n            0.0284057286875383508,   -0.999999999999999556,\n            -0.0242244868191450052,  0.0928780851238265315,\n            -0.317460317460317221,   0.0331953065154646024,\n            -0.0120261423230208601,  0.0284057286875383508,\n            -0.165643115532629448,   0.0349650349650348496,\n            -0.0859883980360246214,  0.0269480403748219369,\n            -0.0555463720600708477,  -1,\n            -0.0219566314336203626,  -0.100000000000000006,\n            0.0859883980360244826,   -0.0602652238303797647,\n            0.937062937062936863,    -0.543939393939393834,\n            0.0752923252421042605,   -0.888111888111888126,\n            0.00383022767486127652,  0.2023809523809523,\n            -0.026948040374821864,   0.0561219975074306876};\n      }\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<2, 1>>) {\n      if (half_power.value_or(0) == 28) {\n        spec_matrix_elements = {-0.0685627356488548168,\n                                -0.0453431150611064288,\n                                0.100745288127242039,\n                                -2.35645989527596294e-05,\n                                0.129348480901835039,\n                                -0.440447768627040659,\n                                0.0685627356488547335,\n                                -0.090627522424120921,\n                                -0.0298496494388849863,\n                                0.024596769445443846,\n                                3.84808117554189404e-06,\n                                -0.0983870777817755227,\n                                -0.0500877543454300508,\n                                0.0416485573242138668,\n                                -0.182532032193009869,\n                                0.0322674094368596287,\n                                0.0179275144939977961,\n                                -0.128015785262174636,\n                                -0.228022038395235105,\n                                0.0044907351556744933,\n                                -0.0503428691117034088,\n                                -0.104296490962404731,\n                                -3.848085700884957e-06,\n                                0.151481918067276011,\n                                0.0358441636040618128,\n                                -0.207555817507032198,\n                                0.0439088270922283475,\n                                -0.0101384944738848182,\n                                -0.0132665108617266679,\n                                -0.0423871423404041242,\n                                -0.0629285863896293929,\n                                0.0286589389558206536,\n                                -0.128559674657807849,\n                                -0.0219521956589060242,\n                                -0.0969611262379501959,\n                                -0.999999999999999778,\n                                0.0877999110867914434,\n                                0.0196214341753331439,\n                                -0.0771285322887563102,\n                                -0.172464641202446645,\n                                -0.0277703805995899583,\n                                0.19089623481085366,\n                                0.0219521956589060242,\n                                0.098194753210568253,\n                                -0.112488633108771066,\n                                0.00449073515567447942,\n                                0.100745288127242039,\n                                -0.00382971180861246124,\n                                0.0771285322887563102,\n                                0.100745288127242039,\n                                -0.0674779422316604122,\n                                0.0169980438467623729,\n                                -0.144312573260346577,\n                                0.090627522424120921,\n                                -0.013472205467023466,\n                                0.115247759028654945,\n                                -0.115369075047653341,\n                                -0.745480257649291711,\n                                -0.989511902268394694,\n                                -0.0474271493509478281,\n                                -0.0114891354258373976,\n                                -0.00344077441697460129,\n                                0.00283900656094535249,\n                                0.0314412533318109422,\n                                0.228022038395235105,\n                                -0.230939504825335262,\n                                -0.0453137612120605021,\n                                0.0359258812453958493,\n                                -1.92404162859985199e-05,\n                                -0.0983870777817755227,\n                                -1.00000000000000022,\n                                0.0353773622979378782,\n                                0.0322674094368596218,\n                                -0.951520049076055585,\n                                0.0104880977316047473,\n                                0.104880977316048479,\n                                -0.0331869236893158059,\n                                0.0983870777817754394,\n                                0.0219521956589060242,\n                                -0.0414077367053690207,\n                                0.0160364761462819944,\n                                -3.62027445059235586e-12,\n                                -1,\n                                -0.0538888218680937808,\n                                -0.0414077367053690068,\n                                -0.45458043712518259,\n                                0.03227672416158818,\n                                -3.62027445059235586e-12,\n                                0.158107798654273296,\n                                -0.0291898872398701527,\n                                -0.0500877543454300508,\n                                -0.0453137612120604605,\n                                -0.0755143036675549328,\n                                0.098194753210568253,\n                                0.0962123780144711338,\n                                0.0277703805995899583,\n                                0.0420845564919055184,\n                                -0.0597821570701475985,\n                                0.113389283733575791,\n                                -0.062616117678111316};\n        spec_src_indices = {\n            696,  445,  685,  1072, 292,  929,  454, 459,  136,  139,\n            587,  707,  930,  856,  1325, 1002, 693, 1009, 282,  785,\n            57,   1244, 587,  1332, 936,  605,  207, 56,   703,  1270,\n            139,  137,  372,  293,  208,  241,  371, 702,  1426, 860,\n            1264, 616,  617,  39,   1254, 1112, 280, 1435, 1345, 604,\n            40,   120,  1081, 1026, 383,  201,  626, 1264, 216,  441,\n            1435, 675,  928,  525,  687,  279,  54,  380,  992,  59,\n            390,  298,  1000, 127,  216,  135,  452, 626,  293,  1000,\n            127,  901,  1289, 138,  29,   846,  433, 1387, 352,  111,\n            930,  541,  380,  685,  938,  940,  766, 704,  930,  443};\n        spec_dest_indices = {\n            1264, 199,  280,  180,  374,  929,  777,  1432, 138,  869,\n            343,  949,  1252, 211,  27,   1243, 372,  1252, 39,   869,\n            1353, 999,  910,  684,  48,   38,   776,  704,  1353, 625,\n            787,  216,  49,   694,  775,  241,  369,  625,  858,  1426,\n            694,  697,  46,   685,  688,  218,  685,  542,  1263, 685,\n            930,  1172, 189,  1351, 381,  848,  59,   1264, 216,  766,\n            1191, 1000, 688,  445,  606,  361,  946,  59,   344,  1030,\n            390,  1271, 1002, 127,  540,  1431, 536,  1030, 289,  677,\n            857,  1225, 1289, 221,  919,  846,  1164, 1387, 1325, 757,\n            1252, 864,  57,   39,   1342, 613,  768,  948,  605,  1091};\n      } else {\n        spec_matrix_elements = {-0.0587412587412586493,\n                                0.0630929176171425299,\n                                -0.0392481544067196514,\n                                0.148453923805041105,\n                                0.104761904761904806,\n                                0.104972776216295513,\n                                0.00461069445977073389,\n                                -0.0226599325119156217,\n                                0.0157459164324443811,\n                                0.0501598988543476773,\n                                0.12856486930664493,\n                                -0.152331136091597596,\n                                -0.0492001653128330968,\n                                0.0142028643437691754,\n                                0.0878265257344814643,\n                                0.00534495214356485449,\n                                0.102062072615965754,\n                                -0.0212958854999980018,\n                                -0.0587412587412587603,\n                                -0.0114906830245838781,\n                                0.0919254641966712888,\n                                -0.0106479427499990009,\n                                -0.0304918360568153628,\n                                -0.0376461626210521788,\n                                0.0501089263459202419,\n                                -0.0538960807496439431,\n                                -0.0239085243355429619,\n                                -0.248809523809523786,\n                                0.179990817029302808,\n                                -0.0135519271363623572,\n                                0.0909090909090908561,\n                                -0.0481045692920834336,\n                                0.122474487139158913,\n                                -0.091925464196671261,\n                                -0.190476190476190493,\n                                -0.108333333333333365,\n                                -0.137373737373737648,\n                                -0.0142028643437691754,\n                                -0.0248964798865984778,\n                                -0.745454545454545259,\n                                -0.0134740201874108973,\n                                0.0716569983633538465,\n                                0.0517080736106276043,\n                                -0.0359307204997625917,\n                                0.0574959574576068694,\n                                -0.0380300012060943299,\n                                -0.0248964798865984743,\n                                0.02777318603003541,\n                                -1,\n                                0.112494260643314317,\n                                0.247435829652696782,\n                                0.0106479427499990009,\n                                0.0903507902905250487,\n                                -0.999999999999999778,\n                                0.02777318603003541,\n                                -0.00777922918226818094,\n                                -0.128030303030302939,\n                                0.00777922918226818094,\n                                -0.0453534228699871561,\n                                -0.0979020979020979454,\n                                0.135959595071493744,\n                                0.0106479427499990009,\n                                -0.0521640530957300924,\n                                -0.0484848484848484154,\n                                -0.0286627993453415462,\n                                -0.0621396868315451031,\n                                0.22896825396825396,\n                                0.0142028643437691754,\n                                -0.076060002412188632,\n                                -0.0587412587412587603,\n                                0.109280138910648181,\n                                0.0358550289884823695,\n                                -0.0380827840228993852,\n                                0.0839160839160837113,\n                                -0.192418277168333873,\n                                0.0862439361864102694,\n                                0.0358284991816769163,\n                                -0.076661276610142548,\n                                0.087826525734481492,\n                                -0.00449134006247033438,\n                                -0.00534495214356485449,\n                                -0.0380300012060943299,\n                                0.0359307204997625917,\n                                0.0862439361864102694,\n                                -0.163920208365972292,\n                                0.123717914826348363,\n                                0.0839160839160837529,\n                                0.136060268609961343,\n                                0.0359307204997625917,\n                                -0.0862439361864102694,\n                                0.137135860593754655,\n                                -0.0244755244755244794,\n                                -0.0200435705383681037,\n                                -0.0582644221062840612,\n                                -0.152331136091597624,\n                                -0.843356643356641955,\n                                -0.0746894396597953536,\n                                -0.11065102171821535,\n                                -0.449747474747474674,\n                                -0.050349650349650471};\n        spec_src_indices = {\n            1271, 47,   625,  1019, 839,  938,  271,  1188, 778,  135,\n            858,  290,  1435, 626,  1017, 1255, 29,   111,  1109, 1191,\n            949,  999,  455,  757,  363,  140,  675,  759,  1336, 48,\n            1423, 209,  354,  1434, 999,  921,  1344, 55,   1022, 940,\n            948,  299,  137,  221,  779,  202,  614,  532,  72,   769,\n            757,  1080, 920,  1211, 46,   546,  1009, 303,  850,  946,\n            784,  918,  1161, 47,   297,  766,  678,  788,  49,   1109,\n            616,  453,  1092, 949,  209,  130,  783,  929,  776,  704,\n            364,  202,  461,  374,  849,  1242, 868,  1253, 545,  49,\n            1264, 1108, 607,  117,  47,   1433, 1191, 1341, 525,  786};\n        spec_dest_indices = {\n            624,  1020, 54,   615,  1082, 1264, 273,  541,  209,  137,\n            778,  1261, 1193, 703,  938,  1253, 1245, 1161, 543,  1435,\n            1353, 678,  453,  1082, 1252, 138,  29,   516,  363,  293,\n            532,  856,  272,  1192, 999,  921,  696,  626,  290,  940,\n            1031, 54,   787,  866,  859,  522,  374,  1102, 72,   1173,\n            837,  759,  1324, 1211, 1264, 542,  1252, 704,  766,  1027,\n            864,  678,  1082, 290,  1352, 443,  30,   217,  45,   543,\n            1020, 531,  1090, 1273, 1180, 1184, 218,  1008, 207,  1274,\n            362,  522,  140,  697,  526,  676,  1111, 1335, 1109, 1265,\n            1344, 865,  38,   444,  1018, 1433, 139,  1018, 525,  1434};\n      }\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<2, 1, 1>>) {\n      if (half_power.value_or(0) == 28) {\n        spec_matrix_elements = {\n            -0.00269208216672631954, -0.010750234917742383,\n            -0.0146476195813102089,  -0.0602664020620640173,\n            0.00659251857415165538,  0.0539518996141695045,\n            -0.142210438327501881,   -0.0645579990594676439,\n            0.0318035062311413236,   0.01357925784500464,\n            0.0500982824981638097,   0.00699865802396499453,\n            0.033976079072793236,    0.00136843792358644248,\n            -7.6899526008800817e-06, -0.0372255613269783722,\n            -0.0145616396277683646,  -0.0141637324818305703,\n            -0.0573178779116412448,  0.171664688563884366,\n            0.0131850371483030766,   -0.0270304681379216552,\n            0.0440416857990435923,   -0.000849743073471963674,\n            0.0311793329099579082,   0.0247219446530685637,\n            -0.00524404886580241529, -0.132260665413839684,\n            -0.0125920238112098709,  0.00702764841298396305,\n            -0.095821505220735223,   -0.0408578194895757585,\n            -0.0648660195655660077,  -0.0167893650816131682,\n            0.0136807335923500861,   0.0168663561911615023,\n            -0.042745039705691365,   -0.0128772136626850581,\n            0.00824064821768948769,  -0.0331943484789715831,\n            -0.00214698205400234033, -0.00918088144186170879,\n            -0.0033932618637299173,  -0.0302117173960217457,\n            -0.705389560520567227,   -0.0616891862520300149,\n            0.0278280679522486529,   -0.0583211843497634178,\n            0.0301278989943647262,   -0.00971430624244328661,\n            0.0203208505974582718,   0.000897221620103715034,\n            0.00281105936519348339,  -0.135667451041496856,\n            -0.528163365770816129,   -0.0426028543815664962,\n            0.0294867817127337922,   -0.0477959718517156212,\n            0.0165496092159751473,   0.0306414075528770834,\n            0.0283367267957105595,   0.0414631256366054038,\n            -0.00200019206995420695, -0.030210191443232795,\n            -0.0128134490315224975,  0.0855000336888821072,\n            0.000573805090116366774, -0.0072808198138841259,\n            0.0281105936519358661,   0.0131850371483034114,\n            -0.0602472303365140238,  0.0801675739163611734,\n            0.0428196642010204576,   0.000642592180453828085,\n            -0.0213781888581638171,  -0.0408154032054749635,\n            -0.0336153808533220314,  0.0105883175838285843,\n            -0.0135997961883894992,  -0.106899042869845223,\n            0.0556650800569035847,   0.0207386412698867713,\n            0.00404540912504727096,  0.0373390348844910153,\n            0.0167893650816131682,   -0.0620814263937701644,\n            -0.0172337031387562248,  0.00128200860054954527,\n            0.0224884749215487172,   0.0460586826146067302,\n            -0.0606772391572587139,  -0.0193692191688487336,\n            0.0302532468917728024,   0.0220589020317404558,\n            -0.020397710544180811,   -0.00584368937966095459,\n            0.0102897810988905776,   -0.014496840555266394,\n            0.0101324650386360408,   0.00281105936519360049};\n        spec_src_indices = {\n            2145, 65,   2576, 1082, 1849, 1801, 452,  1271, 959,  793,\n            199,  949,  1660, 307,  2712, 1584, 2729, 2161, 2727, 1588,\n            1364, 2394, 1427, 2558, 2305, 1923, 2642, 2044, 60,   1113,\n            604,  181,  1738, 2729, 293,  878,  2632, 1488, 1598, 1971,\n            1683, 1117, 432,  2809, 2883, 1494, 876,  2772, 342,  1719,\n            192,  1270, 1118, 2639, 2387, 2004, 1588, 1973, 612,  2487,\n            2225, 1037, 2631, 2566, 2160, 1316, 2741, 1674, 463,  2741,\n            50,   960,  1598, 1596, 38,   828,  2578, 702,  2478, 2314,\n            2864, 2246, 795,  1010, 1761, 1679, 536,  219,  1525, 1802,\n            2638, 1971, 378,  1017, 2476, 783,  1009, 1678, 2723, 308};\n        spec_dest_indices = {\n            2468, 312,  225,  516,  2498, 1234, 129,  2164, 1038, 1118,\n            2307, 2083, 1415, 637,  1010, 453,  54,   1029, 299,  1669,\n            2173, 2720, 1991, 1994, 1984, 2004, 779,  1476, 56,   1675,\n            280,  1152, 685,  627,  696,  1685, 200,  2379, 2327, 2864,\n            1931, 2255, 2622, 702,  2883, 684,  1687, 1720, 992,  2450,\n            1161, 1029, 2902, 2881, 2387, 1192, 1184, 1161, 1020, 55,\n            1578, 68,   766,  864,  1026, 1476, 307,  1433, 2327, 2417,\n            2155, 1771, 1113, 1679, 2389, 425,  798,  625,  1342, 857,\n            111,  1918, 228,  2386, 1352, 2245, 1181, 545,  2414, 911,\n            777,  2620, 1271, 2478, 615,  2246, 2143, 785,  613,  1850};\n      } else {\n        spec_matrix_elements = {\n            0.00730107396287076049,   -0.0209142590500362054,\n            0.0385694607919933249,    -0.0314485451016575657,\n            0.0208306503344603405,    -0.00704295704295696684,\n            0.00459105905817224456,   0.00419831416185901477,\n            -0.0122842938209771803,   -0.0142265599351991019,\n            -0.0111111111111111133,   0.0197802197802197863,\n            0.0161616161616162032,    0.0160348564306944594,\n            -0.0553584329154928056,   -0.0377622377622376243,\n            -0.0634450845263324625,   -0.0191808191808196596,\n            0.00913717355809758047,   0.00335242617200756047,\n            -0.0999999999999999223,   -0.0169399089204529674,\n            0.13889006525221631,      -0.0340740699152258172,\n            -0.0975774392686331316,   0.0712121212121212543,\n            -0.127898765124342839,    -0.028550288036849801,\n            0.0340464682209893066,    0.161194023586505086,\n            0.019248600267729829,     0.052525252525252468,\n            -0.0585477658871656481,   -0.0321374134072058212,\n            0.0244779524825216518,    0.0741341991341991774,\n            0.0145165278255580121,    0.0400871410767361519,\n            0.164502322725930766,     -0.0193795894553161346,\n            -0.00840497722020788242,  -0.0681073369498584463,\n            0.0106899042871296673,    -0.0952380952380952328,\n            -0.46428571428571358,     -0.0388611388611388026,\n            -0.00119880119880115023,  -0.0749961737622095725,\n            -0.0247252747252745611,   0.0438095184624330836,\n            0.0650023729647062343,    -0.0348368326618337665,\n            0.00772005772005764524,   -0.0159038950986072555,\n            0.0306816936622102202,    -0.0429570429570430429,\n            0.0131868131868131885,    -0.260317460317460281,\n            -0.138454474637925234,    -0.0178743958160194547,\n            -0.00633833353434905874,  -0.0357837639718663614,\n            0.00333395536382923019,   -0.030213243349220785,\n            0.0571005760736992343,    -0.0346136186594813086,\n            -0.0263736263736264187,   0.0336199088808315158,\n            -0.0063383335343490544,   -0.000676326873512825971,\n            -0.0686813186813186455,   -0.0750000000000000666,\n            -0.06686800913109954,     -0.0122245030007071438,\n            -0.0473340812471125874,   0.176289034518486432,\n            -0.0275315401161562029,   -0.0334665334665335962,\n            -0.000466200466200815747, -0.10130240123483078,\n            -0.00645986315177194889,  0.015724272550828769,\n            -0.0244656180390427046,   -0.0618589574131741815,\n            -0.00459105905817226712,  -0.0080988255124977114,\n            0.94125874125874176,      -0.0191511383743065006,\n            0.00419831416185901737,   0.00958265957626779769,\n            -0.0223776223776221764,   -0.116112176118942811,\n            -0.0478547013530289389,   0.0477830705237365094,\n            0.0105548663273591532,    -0.00653782644395723561,\n            0.0233112125665895162,    0.204095904095904029,\n            -0.00510697023314839703,  0.0615677102445924829};\n        spec_src_indices = {\n            757,  1433, 2640, 637,  2075, 712,  1846, 218,  127,  2135,\n            182,  1687, 677,  1822, 283,  1912, 789,  1929, 1604, 151,\n            1152, 192,  1829, 1578, 2560, 696,  2559, 1760, 2639, 2054,\n            786,  200,  2164, 1360, 1827, 676,  1767, 362,  2296, 955,\n            310,  1566, 2632, 1002, 36,   2243, 227,  1578, 2732, 2145,\n            2236, 704,  2710, 471,  864,  1597, 1120, 1315, 859,  2394,\n            441,  1119, 1596, 696,  2085, 211,  392,  1527, 1008, 1516,\n            59,   232,  56,   2004, 2071, 371,  1899, 624,  370,  1415,\n            1748, 797,  703,  1080, 1846, 769,  2250, 1346, 1518, 927,\n            2315, 1001, 1579, 1658, 1668, 1189, 2577, 2000, 536,  542};\n        spec_dest_indices = {\n            1892, 1597, 454,  2173, 2071, 631,  1283, 141,  859,  1809,\n            1963, 878,  2378, 1820, 2711, 2884, 2246, 2901, 796,  875,\n            1962, 837,  1749, 199,  1911, 1506, 2882, 1032, 860,  595,\n            2570, 2630, 1515, 1849, 291,  1324, 2090, 1255, 2216, 957,\n            1527, 1891, 2306, 354,  36,   866,  2657, 1174, 1355, 2143,\n            1910, 1759, 1738, 1202, 1679, 139,  1445, 1315, 1425, 370,\n            1174, 553,  1113, 1747, 1760, 858,  1850, 1040, 1012, 60,\n            707,  1690, 379,  1190, 372,  291,  201,  57,   2800, 198,\n            1345, 799,  1838, 1648, 392,  1090, 2250, 371,  623,  2144,\n            1667, 1488, 1902, 849,  779,  1112, 2252, 1596, 1100, 1918};\n      }\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<3, 2, 1>>) {\n      if (half_power.value_or(0) == 28) {\n        spec_matrix_elements = {\n            0.0552360483676365421,    -0.00474395532910534823,\n            -0.000677351632282652098, -0.946511562564075093,\n            0.00592554949044422213,   -0.0101504034722415294,\n            0.0453813179835835454,    -0.00223312035422314377,\n            -0.0204100580629653257,   0.0242114421424963304,\n            -0.0328348644570740644,   0.0122833072960185398,\n            -0.0389935421892647835,   0.0100902735502071822,\n            -0.00584368937966095112,  0.0113011350830366369,\n            -4.81010678670547062e-06, 0.00202033854705117389,\n            -0.0313942946805330844,   -0.0157619087216946188,\n            0.011042284383748581,     0.0202708373591574641,\n            -0.00419734127040329204,  0.00774636871657083566,\n            -0.0202708373591574606,   0.000200076279947195186,\n            -0.0196774155563550698,   -0.0268593219651420308,\n            0.00384920156200663915,   0.0685627356488547612,\n            -0.0242114421424963339,   0.0342539837531646713,\n            -0.0830765005161016729,   0.000317837358332816178,\n            0.0161579429912123845,    0.00478478809849765957,\n            1.36050294686617868e-06,  -0.083075737539706998,\n            0.00335197465746050159,   -0.0135941283636181482,\n            -0.006562692000699504,    0.00342600031231792869,\n            0.00878029864353442949,   -0.0124077443435509777,\n            0.00533986160838958047,   -0.0336153808533218856,\n            -0.00633430911290463593,  0.0150008222935436968,\n            0.00420192260666522183,   0.0260273127084711606,\n            -0.0699877776831585585,   0.0100005190109536245,\n            -0.00268577583809998313,  -0.00442006577685086742,\n            -0.00774636871657077321,  -0.103400199379821145,\n            0.0247219446530685949,    0.00695701698806219099,\n            0.00179170581962374321,   0.0035138242064919837,\n            0.0480838932535115338,    -0.00957961739954195368,\n            0.000571193049292275344,  0.0130787535937552171,\n            0.0228421243730709614,    -0.069303972896579108,\n            0.0868600286799977028,    -0.015524959155553672,\n            -0.0252115356399914906,   -0.0301205253969969861,\n            0.00788981998909990545,   -0.0160665425232580268,\n            0.0323513104842895055,    0.0760822509098668803,\n            -0.0226836858896875757,   0.0483360011186148575,\n            -0.00238383844285221941,  1.36050294686618545e-06,\n            0.0320664734293432188,    -0.0110422843837485532,\n            0.00264216592239322444,   0.0267439487356765648,\n            -0.00693548917129768565,  -0.00119863974075488195,\n            -0.00169106767377779493,  4.81010678670546638e-06,\n            0.00906275224241211326,   0.0151005178632496791,\n            0.0975602569730824043,    -0.000641533593667779987,\n            0.0230738150095308042,    -1.92404199062729831e-05,\n            -0.00871077980745948131,  0.0174743470329419281,\n            0.0113308293805446138,    0.0333350633698454579,\n            -0.00560744556397530392,  -0.0189618008099077251,\n            0.0335567738203970956,    -0.0260558318421210897};\n        spec_src_indices = {\n            696,  3620, 301,  2648, 66,   546,  2244, 936,  828,  3370,\n            3775, 2479, 2142, 2890, 950,  758,  3169, 2242, 2412, 787,\n            2945, 3063, 1757, 2170, 1770, 1100, 470,  1728, 1677, 1263,\n            1099, 1928, 4104, 147,  2783, 4096, 1062, 3132, 799,  2172,\n            2457, 3700, 3359, 288,  1984, 1363, 3114, 293,  3228, 2882,\n            342,  384,  4168, 1270, 550,  1586, 2976, 795,  2742, 136,\n            3610, 1980, 3377, 2889, 1092, 3935, 524,  2970, 1689, 362,\n            2089, 3304, 293,  301,  3052, 1585, 423,  3006, 1666, 2540,\n            3043, 4350, 2313, 713,  1670, 2278, 1197, 55,   2640, 3134,\n            958,  1305, 3432, 3693, 3440, 2976, 139,  4077, 3379, 3279};\n        spec_dest_indices = {\n            371,  382,  1680, 2648, 631,  3133, 2322, 1990, 1477, 2476,\n            209,  2152, 2144, 1921, 54,   2703, 2926, 3541, 2818, 2485,\n            2540, 231,  546,  3064, 2415, 3127, 3145, 3673, 3866, 4180,\n            3370, 3304, 3133, 3143, 1730, 1506, 1468, 3376, 1118, 2493,\n            1729, 3702, 121,  3531, 686,  717,  445,  2963, 635,  290,\n            19,   3539, 3925, 1678, 3712, 1909, 1923, 3469, 2738, 1194,\n            45,   1658, 464,  1678, 3766, 1993, 3359, 2001, 2254, 1008,\n            230,  958,  617,  706,  2812, 3936, 3663, 4060, 4259, 2297,\n            451,  460,  293,  2900, 4177, 4222, 3387, 2322, 3856, 545,\n            2578, 1305, 513,  3045, 120,  1353, 216,  1406, 3702, 4168};\n      } else {\n        spec_matrix_elements = {\n            -0.0793684900224774642,  -0.924999999999998268,\n            0.157491964900640075,    -0.000573882382271528185,\n            0.00957556918715324681,  0.0059806711476906721,\n            -0.0314858814648800098,  -0.0242371004554172639,\n            0.0290693841829741048,   -0.00329670329670330711,\n            -0.0685679302968774107,  0.093209530247317568,\n            -0.00276422872392336968, -0.0200314958773677651,\n            -0.0199375481293777232,  0.0520381613860228798,\n            0.0656002204171108327,   0.0455988455988455207,\n            0.0129937752957175259,   0.0263736263736263701,\n            -0.00728894296124792812, 0.0246000826564165415,\n            0.0299122542227263435,   -0.0105428925670356596,\n            -0.0989593883164580634,  -0.0263008714036432785,\n            1.00000000000000044,     0.0209235209235208719,\n            -0.105772005772005712,   -0.100732600732600874,\n            -0.0559440559440556387,  0.0118099919012113769,\n            -0.761247086247087146,   0.268680320077423263,\n            0.0286627993453415879,   0.00844485824370012905,\n            0.00424725937263148879,  0.00446859895400490009,\n            0.0385694607919934568,   0.00336717514850729503,\n            -0.0180337846320302517,  -0.0390477502482802635,\n            -0.0814526063982761173,  -0.0408248290463862698,\n            -0.0250231803480488388,  -0.0405182240404094174,\n            -0.00968979472765794936, -0.0111111111111111115,\n            0.013082718135573216,    0.0410983101193406961,\n            0.0118540635158372147,   -0.0172872461194469168,\n            0.0260566416998785355,   -0.00659340659340660468,\n            0.0336199088808315227,   -0.0167832167832168075,\n            0.050950644736990805,    -0.01308271813557319,\n            0.102976804415260553,    -0.00728894296124792812,\n            0.0317225422631662521,   0.0787878787878787123,\n            0.00292206606306765274,  -0.00659340659340658473,\n            0.0135198135198135588,   0.0271919190142987169,\n            0.0167832167832166063,   0.0431818181818181962,\n            0.0230769230769230713,   0.00786213627541438101,\n            -0.0248657700193693418,  -0.00547448901451356822,\n            0.0242125211543898454,   0.00553702555866177667,\n            0.00999803692948069908,  0.0194250194250194519,\n            0.0101639453522717679,   -0.00751544939167995106,\n            -0.0533799533799534168,  0.0339715427160038191,\n            0.00358284991816768851,  0.00760037241933012291,\n            0.0169399089204529812,   0.0133200133200133113,\n            0.0162543735594164393,   -0.00363266836224360873,\n            -0.0538960807496437627,  -0.000489408540016634044,\n            0.00716569983633540998,  -0.0354684046235406308,\n            -0.0169571407734016057,  -0.0126672873655502092,\n            0.0186257768684297033,   0.0069381590325120427,\n            0.00335242617200756047,  -0.0290801490098363674,\n            0.0374902772201055612,   0.0538960807496438807,\n            0.0044078016893244823,   -0.00335242617200756047};\n        spec_src_indices = {\n            2550, 2176, 2711, 2170, 4181, 2494, 1413, 4098, 1929, 4199,\n            3289, 766,  212,  1037, 1270, 2872, 2084, 596,  516,  1444,\n            1739, 2894, 3054, 2090, 2156, 2413, 1449, 3430, 3431, 2975,\n            3297, 2250, 534,  1729, 2889, 3690, 2728, 1908, 1740, 1819,\n            1750, 706,  1172, 1233, 3947, 2053, 1929, 506,  2889, 288,\n            2649, 2304, 2064, 1931, 4117, 3468, 2556, 2569, 534,  1739,\n            2085, 3441, 702,  1444, 1993, 3549, 1848, 2062, 2174, 799,\n            2637, 1689, 856,  955,  2394, 2882, 3429, 3708, 615,  1760,\n            864,  2071, 1818, 4189, 2408, 3754, 3542, 1198, 2082, 369,\n            3530, 2071, 149,  1098, 70,   2305, 1648, 2732, 2083, 470};\n        spec_dest_indices = {\n            1982, 2176, 1738, 4037, 1262, 549,  2144, 779,  1036, 1607,\n            939,  3035, 1102, 635,  3296, 3521, 3460, 1649, 2134, 3388,\n            1173, 2569, 1759, 1525, 2398, 471,  1449, 1810, 3674, 3623,\n            4268, 2011, 534,  3105, 3216, 3125, 542,  3934, 4333, 1415,\n            533,  1274, 3438, 1315, 3864, 2136, 1279, 3989, 1597, 3935,\n            2004, 2226, 2791, 715,  231,  2739, 2315, 378,  3772, 1173,\n            3137, 1821, 2651, 1525, 2884, 63,   3468, 199,  2497, 2093,\n            3532, 2007, 4099, 4278, 129,  3935, 516,  1526, 3288, 3055,\n            1758, 212,  766,  2893, 3702, 111,  1272, 1523, 1188, 2558,\n            373,  2075, 2738, 1508, 2333, 1334, 4239, 3135, 218,  151};\n      }\n    }\n  }\n\n  SparseMatrixType matrix;\n  ylm::TensorYlm::fill_filter<TensorStructure>(\n      make_not_null(&matrix), ell_max, number_of_ell_modes_to_kill, half_power,\n      coefficient_normalization);\n\n  // Loop over spec_matrix_elements and make sure all the cases agree\n  if (coefficient_normalization ==\n      ylm::TensorYlm::CoefficientNormalization::Standard) {\n    for (size_t i = 0; i < spec_matrix_elements.size(); ++i) {\n      CAPTURE(spec_dest_indices[i]);\n      CAPTURE(spec_src_indices[i]);\n      CHECK(matrix(spec_dest_indices[i], spec_src_indices[i]) ==\n            approx(spec_matrix_elements[i]));\n    }\n  } else {\n    CHECK(coefficient_normalization ==\n          ylm::TensorYlm::CoefficientNormalization::Spherepack);\n    // Here the expected coefficients are off by a factor of (-1)^{m+m'),\n    // So we must multiply the matrix elements by this factor.\n    // Most of the logic below is for recovering m and m' from the\n    // SparseMatrix indices.\n    ylm::SpherepackIterator src_iter(ell_max, ell_max, 1, false);\n    ylm::SpherepackIterator dest_iter(ell_max, ell_max, 1, false);\n    for (size_t i = 0; i < spec_matrix_elements.size(); ++i) {\n      CAPTURE(spec_dest_indices[i]);\n      CAPTURE(spec_src_indices[i]);\n      // We need to recover the azimuthal index m from the\n      // SparseMatrix indices.\n      //\n      // First compute offsets into arrays.\n      // The SparseMatrices are indexed by\n      // k = iter() + component_index*iter.spherepack_array_size()\n      // where iter is a SpherepackIterator.\n      // The offset is the value returned by iter().\n      const size_t dest_offset =\n          spec_dest_indices[i] % dest_iter.spherepack_array_size();\n      const size_t src_offset =\n          spec_src_indices[i] % src_iter.spherepack_array_size();\n      // Reset the iterators so we can query them for m and m'.\n      src_iter.set(src_iter.compact_index(src_offset).value());\n      dest_iter.set(dest_iter.compact_index(dest_offset).value());\n      // factor is (-1)^{m+m'}\n      const double factor =\n          ((src_iter.m() + dest_iter.m()) % 2 == 0 ? 1.0 : -1.0);\n      CHECK(matrix(spec_dest_indices[i], spec_src_indices[i]) ==\n            approx(factor * spec_matrix_elements[i]));\n    }\n  }\n}\n\nvoid test(\n    const std::optional<size_t>& half_power,\n    const ylm::TensorYlm::CoefficientNormalization coefficient_normalization) {\n  const size_t ell_max = 8;\n  const size_t num_to_kill = 4;\n\n  test_tensorylm_filter_vs_spec<typename tnsr::i<DataVector, 3>::structure,\n                                SimpleSparseMatrix>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_tensorylm_filter_vs_spec<typename tnsr::ii<DataVector, 3>::structure,\n                                SimpleSparseMatrix>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_tensorylm_filter_vs_spec<typename tnsr::ij<DataVector, 3>::structure,\n                                SimpleSparseMatrix>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_tensorylm_filter_vs_spec<typename tnsr::ijj<DataVector, 3>::structure,\n                                SimpleSparseMatrix>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_tensorylm_filter_vs_spec<typename tnsr::ijk<DataVector, 3>::structure,\n                                SimpleSparseMatrix>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_filter_vs_transforms<typename tnsr::i<DataVector, 3>>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_filter_vs_transforms<typename tnsr::ii<DataVector, 3>>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_filter_vs_transforms<typename tnsr::ij<DataVector, 3>>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_filter_vs_transforms<typename tnsr::ijj<DataVector, 3>>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_filter_vs_transforms<typename tnsr::ijk<DataVector, 3>>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n  test_filter_vs_transforms<Scalar<DataVector>>(\n      ell_max, num_to_kill, half_power, coefficient_normalization);\n}\n}  // namespace\n\n// For debug builds, test_tensorylm_filter_vs_spec is slow even for\n// testing ell_max = 8 for all ranks, so we need to increase the\n// timeout to 120.  Release builds are still under 10 seconds (barely).  The\n// function test_filter_vs_transforms is much slower than\n// test_tensorylm_filter_vs_spec, so we increase the timeout to 360.\n// Test then split in fourths to allow running in parallel.\n\n// [[TimeOut, 180]]\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.TensorYlmFilter1\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test(std::optional<size_t>(),\n       ylm::TensorYlm::CoefficientNormalization::Standard);\n}\n\n// [[TimeOut, 180]]\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.TensorYlmFilter2\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test(std::optional<size_t>(28),\n       ylm::TensorYlm::CoefficientNormalization::Standard);\n}\n\n// [[TimeOut, 180]]\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.TensorYlmFilter3\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test(std::optional<size_t>(),\n       ylm::TensorYlm::CoefficientNormalization::Spherepack);\n}\n\n// [[TimeOut, 180]]\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.TensorYlmFilter4\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test(std::optional<size_t>(28),\n       ylm::TensorYlm::CoefficientNormalization::Spherepack);\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_TensorYlmSphereToCart.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <filesystem>\n#include <fstream>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SimpleSparseMatrix.hpp\"\n#include \"DataStructures/Tensor/Structure.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmCartToSphere.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/TensorYlmSphereToCart.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <typename TensorStructure>\nstd::string symm_to_string() {\n  if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                               Symmetry<1>>) {\n    return \"a\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<1, 1>>) {\n    return \"aa\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<2, 1>>) {\n    return \"ab\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<3, 2, 1>>) {\n    return \"abc\";\n  } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                      Symmetry<2, 1, 1>>) {\n    return \"abb\";\n  }\n  return \"\";\n}\n\ntemplate <typename TensorType>\nstruct MyTag : db::SimpleTag {\n  using type = TensorType;\n  static std::string name() {\n    return \"MyTag\" + symm_to_string<typename TensorType::structure>();\n  }\n};\n\ntemplate <typename TensorType>\nvoid test_inverse(\n    const size_t ell_max,\n    const ylm::TensorYlm::CoefficientNormalization coefficient_normalization) {\n  ylm::SpherepackIterator it(ell_max, ell_max, 1, false);\n  const size_t spectral_size = it.spherepack_array_size();\n\n  Variables<tmpl::list<MyTag<TensorType>>> A(spectral_size, 0.0);\n  Variables<tmpl::list<MyTag<TensorType>>> B(spectral_size, 0.0);\n  Variables<tmpl::list<MyTag<TensorType>>> C(spectral_size, 0.0);\n\n  // Set up random number generator\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> unit_dis(0.0, 1.0);\n\n  // Fill A with random numbers\n  for (auto& a : get<MyTag<TensorType>>(A)) {\n    for (it.reset(); it; ++it) {\n      // Here we don't set high ell values because of truncation when\n      // we go between cartesian scalar ylm components and\n      // spin-weighted components.\n      if (it.l() < ell_max + 1 - get<MyTag<TensorType>>(A).rank() and\n          (it.coefficient_array() !=\n               ylm::SpherepackIterator::CoefficientArray::b or\n           it.m() != 0)) {\n        a[it()] = unit_dis(gen);\n      }\n    }\n  }\n\n  // Convert A to B.\n  const gsl::span<double> a_span(A.data(), A.size());\n  gsl::span<double> b_span(B.data(), B.size());\n  SimpleSparseMatrix cart_to_sphere;\n  ylm::TensorYlm::fill_cart_to_sphere<typename TensorType::structure>(\n      make_not_null(&cart_to_sphere), ell_max, coefficient_normalization);\n  cart_to_sphere.increment_multiply_on_right(make_not_null(&b_span), 0, 1,\n                                             a_span, 0, 1);\n\n  // Convert B to C\n  gsl::span<double> c_span(C.data(), C.size());\n  SimpleSparseMatrix sphere_to_cart;\n  ylm::TensorYlm::fill_sphere_to_cart<typename TensorType::structure>(\n      make_not_null(&sphere_to_cart), ell_max, coefficient_normalization);\n  sphere_to_cart.increment_multiply_on_right(make_not_null(&c_span), 0, 1,\n                                             b_span, 0, 1);\n  // C should equal A.\n  const auto& a_vector = get<MyTag<TensorType>>(A);\n  const auto& c_vector = get<MyTag<TensorType>>(C);\n  for (size_t i = 0; i < a_vector.size(); ++i) {\n    for (it.reset(); it; ++it) {\n      CHECK(a_vector[i][it()] == approx(c_vector[i][it()]));\n    }\n  }\n}\n\ntemplate <typename TensorStructure, typename SparseMatrixType>\nvoid test_tensorylm_sphere_to_cart_vs_spec(\n    const size_t ell_max,\n    const ylm::TensorYlm::CoefficientNormalization coefficient_normalization) {\n  // There are two \"modes\" for this test.  If test_all_elements is\n  // true, it tests all the matrix elements vs SpEC, reading .txt\n  // files that were output by SpEC. This test was done by Mark\n  // Scheel, and is not done in CI.\n  //\n  // Otherwise, it tests a random subset of the matrix elements vs\n  // SpEC here (the random numbers being previously determined by\n  // SpEC), using inlined values, which is done in CI.\n  constexpr bool test_all_elements = false;\n\n  std::vector<double> spec_matrix_elements;\n  std::vector<size_t> spec_src_indices;\n  std::vector<size_t> spec_dest_indices;\n\n  if constexpr (test_all_elements) {\n    // Read from simple file that was output by SpEC.\n    // This test was run by Mark Scheel after producing the SpEC\n    // files.\n    // Because the SpEC files are so large (16MB for this test),\n    // we don't run this test in CI.\n    //\n    // File has a simple binary format:\n    //   size (a size_t)\n    //   matrix_elements (a vector of doubles of length size)\n    //   src_indices (a vector of size_t of length size)\n    //   dest_indices (a vector of size_t of length size)\n    const std::string filename =\n        \"TensorYlmSphereToCart_\" + symm_to_string<TensorStructure>() + \".txt\";\n    std::ifstream file;\n    file.open(filename, std::ios::in | std::ios::binary);\n    size_t num_indices = 0;\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(&num_indices), sizeof(size_t));\n    spec_matrix_elements.resize(num_indices);\n    spec_src_indices.resize(num_indices);\n    spec_dest_indices.resize(num_indices);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_matrix_elements.data()),\n              static_cast<std::streamsize>(sizeof(double) * num_indices));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_src_indices.data()),\n              static_cast<std::streamsize>(sizeof(size_t) * num_indices));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)\n    file.read(reinterpret_cast<char*>(spec_dest_indices.data()),\n              static_cast<std::streamsize>(sizeof(size_t) * num_indices));\n  } else {\n    if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                 Symmetry<1>>) {\n      spec_matrix_elements = {-0.411376675603721043,\n                              -0.338501600193164887,\n                              0.500979432868119412,\n                              -0.61237243569579447,\n                              0.248525060873854176,\n                              -0.37573457465108967,\n                              -0.338501600193164887,\n                              -0.128388147753273868,\n                              -0.273861278752582982,\n                              -0.426401432711221107,\n                              -0.288675134594812866,\n                              0.223606797749978908,\n                              0.308606699924183825,\n                              0.202919862478356861,\n                              -0.411376675603721043,\n                              -0.340306954864886213,\n                              0.309965209939033259,\n                              -0.341565025531986477,\n                              -0.353553390593273731,\n                              -0.0331496772065897824,\n                              0.447213595499957817,\n                              0.483045891539647942,\n                              -0.335410196624968404,\n                              0.50395263067896956,\n                              -0.377964473009227309,\n                              -0.322748612183951289,\n                              -0.288675134594812921,\n                              -0.547722557505166185,\n                              -0.267261241912424286,\n                              -0.61237243569579447,\n                              0.483045891539647942,\n                              -0.327326835353988432,\n                              -0.471404520791031623,\n                              -0.35355339059327362,\n                              -0.29880715233359828,\n                              0.0332105582077535655,\n                              -0.292770021884559861,\n                              0.313391585264004291,\n                              0.240906027914857906,\n                              0.301511344577763574,\n                              -0.37573457465108967,\n                              -0.288675134594812755,\n                              -0.313391585264004291,\n                              -0.36927447293799831,\n                              -0.342368394008730204,\n                              -0.36927447293799831,\n                              0.297044262893002231,\n                              -0.323875137815647807,\n                              -0.240906027914857962,\n                              -0.422411378337539001,\n                              -0.531368931324057159,\n                              -0.324799177176459952,\n                              -0.223606797749978964,\n                              -0.3038218101250999,\n                              0.301511344577763518,\n                              -0.218217890235992334,\n                              0.269679944985296738,\n                              -0.353553390593273842,\n                              0.0813489216819960059,\n                              -0.270030862433660812,\n                              0.25,\n                              -0.148249863332220178,\n                              -0.0944911182523067578,\n                              0.0331496772065897824,\n                              -0.313391585264004291,\n                              -0.228217732293819198,\n                              -0.196476311998343894,\n                              -0.248525060873854176,\n                              0.269803850319187966,\n                              0.152189896858767632,\n                              -0.335410196624968404,\n                              0.249999999999999917,\n                              -0.322748612183951289,\n                              -0.267261241912424286,\n                              0.256045778249594569,\n                              0.0890870806374747803,\n                              0.174077655955697852,\n                              -0.338061701891406552,\n                              0.237944630237422072,\n                              -0.36927447293799831,\n                              -0.325669473639464746,\n                              0.250000000000000056,\n                              -0.19069251784911842,\n                              0.179028718509858131,\n                              0.269803850319187966,\n                              -0.134218873048289805,\n                              -0.0944911182523067994,\n                              -0.325669473639464691,\n                              -0.353553390593273842,\n                              -0.398409536444797874,\n                              -0.308606699924183825,\n                              -0.249999999999999917,\n                              -0.280484097002537125,\n                              -0.348608344389198022,\n                              0.707106781186547906,\n                              0.301511344577763518,\n                              0.224733287487747374,\n                              0.494727444918153625,\n                              0.174912565570360251,\n                              0.239045721866878697};\n      spec_src_indices = {\n          58,  479, 144, 354, 321, 477, 317, 299, 453, 208, 182, 415, 353,\n          65,  139, 241, 388, 371, 441, 465, 10,  60,  362, 36,  192, 400,\n          343, 424, 182, 273, 60,  381, 40,  297, 300, 321, 261, 390, 474,\n          283, 396, 354, 228, 212, 390, 212, 64,  207, 311, 159, 73,  217,\n          172, 55,  49,  461, 282, 270, 319, 402, 353, 226, 272, 465, 471,\n          241, 388, 402, 481, 397, 200, 272, 400, 425, 210, 38,  38,  312,\n          228, 212, 118, 435, 283, 455, 481, 308, 469, 47,  351, 441, 191,\n          353, 210, 316, 433, 130, 126, 55,  288, 262};\n      spec_dest_indices = {\n          68,  156, 477, 354, 474, 468, 156, 145, 211, 36,  262, 424, 19,\n          316, 230, 69,  72,  289, 199, 230, 343, 313, 37,  351, 20,  75,\n          20,  171, 273, 435, 313, 381, 30,  217, 220, 151, 432, 310, 230,\n          212, 387, 40,  67,  40,  381, 40,  317, 378, 231, 149, 63,  388,\n          263, 65,  364, 461, 211, 109, 230, 77,  273, 299, 118, 149, 229,\n          78,  74,  393, 147, 308, 37,  201, 318, 192, 58,  30,  46,  140,\n          76,  40,  209, 191, 454, 464, 147, 154, 469, 280, 28,  432, 19,\n          39,  381, 155, 189, 445, 199, 370, 217, 189};\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<1, 1>>) {\n      spec_matrix_elements = {0.151247535495505014,    -0.25087260300212727,\n                              -0.296499726664440411,   0.0284256136943051331,\n                              0.0139256498353034913,   -0.183211444965737796,\n                              0.108465228909328096,    -0.0346324913051441474,\n                              -0.126622862732931024,   -0.0377964473009226934,\n                              0.0635933773836460658,   0.0745355992499929926,\n                              0.246702684842237924,    -0.0379868588198793294,\n                              -0.131650798604145414,   0.176368151428799164,\n                              0.0867809345873486598,   0.248410927659626662,\n                              -0.0319076722358637849,  0.258198889747161042,\n                              -0.0754216760476786457,  0.694736842105263319,\n                              0.0833333333333333148,   -0.0980580675690919945,\n                              0.160538213769215704,    0.114991914915213767,\n                              -0.0695795410564069849,  -0.13093073414159534,\n                              -0.0892401305746320234,  -0.1999999999999999,\n                              -0.184210526315789463,   -0.015870392017163077,\n                              -0.348155311911395704,   -0.175733755838701877,\n                              -0.069006555934235464,   0.228025872039397642,\n                              0.110400931386331724,    0.150755672288881842,\n                              0.0276002328465829554,   -0.187867287325544946,\n                              0.235702260395515811,    0.245844585947220834,\n                              -0.0212717814905758439,  0.164957219768464447,\n                              -0.0947994098324201617,  -0.00812367079997208287,\n                              -0.0946762136662301773,  -0.126916601751431546,\n                              -0.251131176425289815,   0.0782780363856437483,\n                              -0.117155837225801196,   0.101056510431805385,\n                              0.185239643408737137,    0.000877192982456142969,\n                              -0.235702260395515811,   -0.0354846094688600067,\n                              -0.365148371670110716,   0.174376718110545142,\n                              0.221248839434355027,    -0.135332990490191707,\n                              0.0122360337402624142,   -0.163178487966126329,\n                              0.202030508910442053,    -0.0326585437266450132,\n                              0.0297569850321807682,   -0.0125524111313358409,\n                              0.0131578947368421097,   -0.0418121005003545196,\n                              0.162948726848967246,    -0.125757521749169099,\n                              0.0213349964337735711,   0.25087260300212727,\n                              -0.0189934294099396578,  -0.0745867737148270399,\n                              -0.175733755838701877,   -0.0303670013058354969,\n                              -0.12420546382981329,    -0.123091490979332696,\n                              -0.358057437019716374,   -0.18257418583505533,\n                              -0.00480144449178737751, 0.0653170874532900125,\n                              0.0284057286875383265,   -0.202172457130404604,\n                              0.0717711564704018173,   -0.235702260395515811,\n                              0.0865592715571676524,   -0.202919862478356944,\n                              -0.213200716355610415,   -0.12219708137608104,\n                              -0.106284504570424224,   0.0845154254728516102,\n                              0.136653343615131234,    -0.17496355305594119,\n                              0.094012679136294422,    0.0418717894679311656,\n                              -0.0449466574975494748,  0.155699788832304509,\n                              -0.0156556072771287351,  0.106118003615914683};\n      spec_src_indices = {\n          242, 452, 381, 624, 545, 948, 879, 310, 108, 383, 625, 432, 311,\n          513, 955, 561, 776, 137, 236, 424, 463, 805, 354, 540, 937, 138,\n          633, 9,   76,  172, 882, 460, 455, 468, 181, 307, 157, 524, 398,\n          875, 272, 300, 58,  343, 479, 473, 634, 129, 720, 633, 141, 559,\n          637, 320, 353, 950, 910, 315, 288, 379, 230, 777, 262, 949, 380,\n          563, 399, 607, 239, 45,  958, 290, 273, 554, 322, 552, 135, 353,\n          374, 504, 876, 544, 129, 637, 543, 191, 949, 59,  847, 704, 46,\n          513, 965, 19,  939, 383, 596, 454, 961, 65};\n      spec_dest_indices = {\n          312, 625, 876, 479, 805, 127, 564, 473, 612, 789, 159, 191, 129,\n          370, 642, 73,  225, 216, 544, 91,  723, 157, 758, 793, 129, 622,\n          208, 513, 58,  820, 560, 622, 111, 721, 514, 237, 641, 45,  76,\n          72,  202, 230, 560, 505, 229, 149, 643, 454, 864, 154, 727, 552,\n          970, 158, 283, 147, 91,  703, 218, 550, 313, 230, 604, 643, 381,\n          789, 75,  707, 68,  549, 482, 220, 127, 384, 465, 778, 137, 533,\n          869, 0,   536, 886, 145, 151, 307, 526, 455, 322, 369, 76,  552,\n          334, 956, 37,  109, 787, 453, 607, 158, 553};\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<2, 1>>) {\n      spec_matrix_elements = {-0.0302613766334401332,  -0.130410132739325252,\n                              -0.0205152484965554459,  0.00990147542976673768,\n                              0.0414370965082372436,   -0.191607115507975601,\n                              0.0424705992864687909,   0.164197394357296217,\n                              -0.202030508910442108,   -0.101015254455221026,\n                              -0.0997734991429172907,  -0.122560223577477798,\n                              -0.0973123680201903596,  0.148647097502640796,\n                              -0.0231931803521356547,  -0.117851130197757892,\n                              -0.16292944183477473,    0.123091490979332738,\n                              -0.213200716355610304,   0.186989398001691481,\n                              -0.0581483804629580325,  -0.134303827337563381,\n                              -0.049236596391733084,   -0.0206196524710580698,\n                              -0.105409255338945948,   -0.0587800047817492108,\n                              0.050507627227610534,    -0.172872461194469251,\n                              0.204124145231931481,    -0.0172005229038445367,\n                              -0.137716809798999501,   -0.0427960492510913032,\n                              -0.00778431550407844303, 0.173162456525720626,\n                              -0.0925820099772551308,  0.150755672288881815,\n                              -0.00200999442026323499, 0.0478474376469345541,\n                              0.0368268860757696923,   0.0965609099170535168,\n                              -0.172516389835588591,   -0.061545745489666355,\n                              0.375438596491228127,    -0.151910905062549922,\n                              -0.0995859195463938418,  -0.148191714726989665,\n                              -0.0870388279778489399,  0.149483567748356388,\n                              -0.181818181818181823,   0.10533126105868576,\n                              -0.242535625036332914,   -0.0127625532460454352,\n                              -0.207581746775425124,   -0.0755928946018454284,\n                              0.160128153805087103,    -0.120659949980976552,\n                              0.181568259800640702,    -0.0221403721385023781,\n                              0.236227795630767012,    0.151522881682831623,\n                              -0.0817015485702651811,  0.240192230707630683,\n                              0.059131239598908418,    0.0199331368024324752,\n                              0.0582551728035462651,   -0.157755753708238161,\n                              0.0828416869579513687,   -0.00821370820822955872,\n                              0.26989594817970658,     0.120467720387366792,\n                              -0.0335242617200755683,  0.162697843363992067,\n                              -0.148249863332220233,   -0.164463742389585443,\n                              -0.0258069887046254512,  0.123091490979332682,\n                              0.322748612183951289,    -0.14916593711679399,\n                              -0.0206196524710580663,  0.0688502554936011313,\n                              -0.0336717514850736754,  0.0597614304667196602,\n                              0.106600358177805221,    -0.0980580675690920084,\n                              0.0430331482911934862,   -0.122197081376081082,\n                              0.0550243733349108782,   -0.0218217890235992244,\n                              0.0115084018986780258,   0.319504825211346821,\n                              0.196800661251332665,    0.268372520060846609,\n                              -0.156446554693685902,   -0.100549958317480481,\n                              0.267261241912424397,    -0.0644181376429710517,\n                              -0.126898675677648426,   0.151522881682831623,\n                              0.0350070021007002491,   -0.128388147753273868};\n      spec_src_indices = {\n          227,  1334, 840,  1275, 551,  378, 515,  875,  585,  1072, 156, 390,\n          768,  1343, 1281, 757,  76,   371, 1264, 59,   634,  433,  139, 1089,\n          758,  1198, 585,  768,  1134, 209, 1454, 1346, 565,  714,  172, 1091,\n          1437, 1112, 1092, 454,  279,  369, 159,  946,  58,   78,   435, 238,\n          1350, 372,  971,  309,  809,  999, 380,  239,  1118, 969,  230, 585,\n          1272, 1184, 613,  1188, 1289, 399, 564,  605,  1108, 586,  623, 643,\n          1110, 644,  301,  434,  293,  58,  765,  624,  182,  1154, 687, 1264,\n          919,  1129, 961,  1113, 1189, 394, 129,  1081, 1190, 318,  992, 714,\n          69,   261,  561,  1436};\n      spec_dest_indices = {\n          381,  910,  1262, 887,  625,  288,  693,  1047, 1072, 766,  541,  725,\n          1181, 1011, 859,  120,  1029, 352,  444,  282,  130,  855,  141,  182,\n          526,  714,  749,  1172, 334,  49,   149,  393,  644,  631,  351,  531,\n          238,  1201, 461,  787,  1081, 38,   807,  875,  299,  950,  1183, 399,\n          704,  68,   880,  230,  809,  334,  711,  1030, 542,  1204, 311,  424,\n          1172, 1275, 1188, 281,  137,  1353, 717,  119,  532,  846,  211,  553,\n          552,  481,  455,  1424, 536,  686,  749,  156,  1252, 352,  617,  150,\n          192,  867,  1122, 141,  606,  384,  1443, 505,  370,  226,  1002, 48,\n          959,  1072, 716,  1102};\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<2, 1, 1>>) {\n      spec_matrix_elements = {0.00942337990320292647,  0.00495949750536344228,\n                              0.0472455591261534136,   -0.0722289433713774881,\n                              0.00138605763491624458,  -0.0337536661172653032,\n                              -0.0281718084909505471,  0.109770164309642371,\n                              0.00494480266564018868,  0.0289215342951656788,\n                              -0.0943456353049726831,  0.128933429115530074,\n                              0.0325010801864771132,   -0.0213896315973249145,\n                              0.126367378526059115,    0.168358757425368433,\n                              0.0200353282145267,      0.0438309909460145794,\n                              -0.0584415584415584263,  0.0803778736107868819,\n                              -0.0199204768222399006,  -0.0427960492510912754,\n                              -0.0215427705235078162,  0.0345977649452097122,\n                              -0.0542380841170928898,  -0.0755928946018454423,\n                              0.0217414138369668095,   -0.137842872366313984,\n                              0.0341394370999459212,   -0.229069377581349709,\n                              -0.0468807230938495245,  0.0379587516322943616,\n                              0.0242020463448343152,   -0.13153341044116415,\n                              0.0176910638331098102,   0.0129034943523127291,\n                              0.0484489736382902603,   -0.0958314847499909389,\n                              0.0341148008467194105,   0.0104166157315489748,\n                              -0.0719447866171068484,  0.0047342881145896621,\n                              0.0358143539566835728,   -0.0571262046997983303,\n                              -0.186701500374436224,   0.0795551664622900917,\n                              -0.0150045140368355555,  -0.0112389501461670309,\n                              -0.0414892223054302706,  0.0962692557769256191,\n                              0.054885082154821227,    -0.0351349988790785606,\n                              0.0927727214085426466,   -0.037841577169582849,\n                              -0.0574001928649719995,  -0.0216952336468371407,\n                              0.0807886534533564515,   0.077310914933705524,\n                              -0.181915528376361019,   -0.0432412545398815074,\n                              0.0443590942249333098,   -0.182574185835055303,\n                              0.0858908501906280736,   0.131078902650808932,\n                              0.0154965954620265405,   0.0790874310517531015,\n                              -0.0317808022613075766,  0.00548293079133140449,\n                              -0.0820576785914368667,  0.0011891700267290398,\n                              -0.0638876564999942137,  0.05281588940966115,\n                              0.0541946762797134649,   0.0229871555356053157,\n                              0.0680413817439771557,   -0.0393666396707045216,\n                              0.00821370820822955351,  0.0234030776652888184,\n                              -0.00256890086318321614, -0.0310161403466156955,\n                              0.0934946990008457268,   0.0614063438486343038,\n                              -0.0463863607042712955,  0.0445435403187373763,\n                              0.0308516766452967967,   0.0184982900882844081,\n                              -0.18127775750517866,    -0.0594343883172151824,\n                              0.0514671379156634232,   0.0615660308508153842,\n                              0.076066215460514508,    0.0400320384512717897,\n                              0.0553909461402380815,   0.0578053191770906832,\n                              -0.0561103090771243387,  0.0243975018237133148,\n                              0.0642624698351923901,   0.0215302484870759474,\n                              -0.0440165478304583163,  -0.00460336075947121587};\n      spec_src_indices = {\n          217,  198,  1791, 1445, 2875, 1685, 504,  1099, 2009, 2808,\n          222,  1028, 136,  2184, 1851, 1072, 309,  2549, 603,  159,\n          352,  1508, 2074, 2740, 1659, 546,  2571, 210,  130,  1406,\n          636,  1818, 202,  869,  282,  1751, 1442, 261,  2503, 1777,\n          2175, 1913, 2395, 126,  632,  1434, 1767, 2260, 687,  2913,\n          1101, 706,  1040, 1116, 873,  2151, 774,  1759, 2346, 1050,\n          1351, 1215, 2333, 1102, 389,  1121, 2809, 2151, 722,  1524,\n          1954, 1021, 2245, 2225, 353,  39,   2711, 1010, 462,  2702,\n          1181, 1345, 1279, 2205, 2646, 1042, 959,  561,  1192, 717,\n          1424, 293,  937,  2072, 2566, 2631, 1191, 1756, 2488, 525};\n      spec_dest_indices = {\n          616,  2054, 1486, 2344, 797,  707,  595,  876,  1822, 2494,\n          1751, 2819, 1117, 2166, 1375, 504,  1207, 1670, 1901, 230,\n          666,  1770, 2586, 884,  1832, 626,  2345, 2720, 2000, 1980,\n          2318, 1262, 1344, 717,  2657, 785,  766,  1062, 1370, 2252,\n          2417, 202,  2295, 1596, 855,  2865, 455,  1606, 1363, 1596,\n          549,  1533, 38,   2018, 2395, 64,   1107, 2243, 2750, 2186,\n          1027, 2611, 1262, 2559, 2074, 2668, 1775, 1731, 1758, 2163,\n          20,   1738, 212,  775,  1001, 2071, 1172, 1524, 1683, 775,\n          703,  1537, 2315, 1163, 1262, 1374, 159,  1846, 28,   1048,\n          2909, 231,  1101, 2263, 785,  1316, 1525, 838,  1822, 1514};\n    } else if constexpr (std::is_same_v<typename TensorStructure::symmetry,\n                                        Symmetry<3, 2, 1>>) {\n      spec_matrix_elements = {-0.00607411913437329136, 0.0128795882848457,\n                              0.0682183231411353508,   -0.0126622862732931052,\n                              -0.0272338495234217201,  0.0255239280204499694,\n                              -0.0240249990052148962,  0.0249319197335588563,\n                              0.0238882962546999381,   0.0120427748285605648,\n                              -0.0186989398001691336,  -0.161688242248931663,\n                              -0.021567237051010768,   -0.0178287395580167263,\n                              -0.0633865691046387458,  0.0238095238095237943,\n                              -0.0350689431732027143,  0.0338758081577341594,\n                              -0.0175323963784058227,  0.0500317762980915515,\n                              0.0313112145542574632,   -0.0255239280204499555,\n                              -0.0444726900427480157,  0.0785674201318385668,\n                              -0.0127034280591503089,  0.054494880786523682,\n                              -0.0190260597661797565,  0.0397766725054934933,\n                              -0.0797957515916724036,  0.0343285767029624794,\n                              -0.01693990892045295,    -0.0280916609359684183,\n                              0.0925820099772551308,   0.0121793477646742476,\n                              0.0106819946269366903,   0.0576923076923077094,\n                              0.0102112511004325314,   0.0820083154834042732,\n                              -0.0923585743576576523,  -0.0755928946018454284,\n                              -0.0226971798453877056,  0.0107958379271882524,\n                              0.0261357534070577185,   -0.00155901628788543065,\n                              -0.0185102581130696386,  -0.233333333333333309,\n                              -0.0187422162347872928,  0.0492001653128331523,\n                              -0.0304483694116856121,  -0.0796586865665405153,\n                              -0.0741249316661101165,  -0.0638762859688007528,\n                              -0.0246000826564165762,  -0.0179653602498812959,\n                              0.0934512354796392941,   -0.0742484888197975784,\n                              -0.0185017574200321035,  0.0219317231653256214,\n                              0.0175523082489666173,   -0.0310161403466156713,\n                              -0.00614063438486343004, -0.0117560009563498526,\n                              0.0734360634462503659,   -0.00114485138847282759,\n                              0.0234174332451739971,   -0.0843239973097885259,\n                              -0.019026059766179753,   -0.0782972395634450591,\n                              -0.0195739672123620527,  0.0383120392292914877,\n                              -0.121742062266810169,   -0.00524115678447243088,\n                              0.139533710469388672,    0.222845773464851321,\n                              -0.0970512781266708591,  -0.177570104095571446,\n                              -0.0588054451658798494,  -0.105474616099695578,\n                              -0.0257777787476648132,  0.0196196231582257755,\n                              -0.0180109539910527534,  -0.0618589574131741746,\n                              0.00395765965713481844,  0.0841654636156864977,\n                              -0.0147013612914699728,  0.0230496614925959076,\n                              -0.029903355738453069,   -0.00155901628788543065,\n                              -0.0560968194005073972,  -0.0917390379971574077,\n                              0.103509833901353099,    -0.0254823595718812607,\n                              -0.0286478453742500042,  0.0688738748300898179,\n                              -0.0178158736943771316,  -0.0506265524395705518,\n                              0.0628787608745844939,   0.0287201875070028888,\n                              0.0492001653128331523,   0.0456273770493438313};\n      spec_src_indices = {\n          1677, 806,  3885, 1721, 3942, 3225, 2152, 1746, 645,  1019,\n          769,  1361, 3474, 2389, 708,  3826, 2062, 694,  1912, 280,\n          3381, 1293, 706,  676,  4259, 4208, 886,  1111, 1523, 1099,\n          677,  2620, 2278, 2062, 2979, 796,  4352, 2011, 1660, 2733,\n          1607, 3540, 200,  318,  4285, 2571, 1769, 1173, 4104, 1109,\n          1437, 4361, 4257, 1587, 3861, 208,  198,  1478, 2306, 3126,\n          2723, 2900, 2821, 2974, 1202, 1776, 3559, 2019, 965,  2656,\n          4178, 1999, 2008, 4186, 2738, 3228, 1414, 451,  531,  2244,\n          3947, 199,  3883, 2459, 3204, 364,  1605, 480,  3532, 1612,\n          2065, 545,  4278, 158,  720,  1775, 2574, 2884, 849,  919};\n      spec_dest_indices = {\n          58,   796,  1778, 4098, 2549, 1779, 3583, 2080, 3796, 2019,\n          2742, 2961, 2233, 2407, 626,  4088, 3025, 4276, 1091, 787,\n          2427, 2172, 2621, 354,  362,  231,  1679, 3066, 3235, 110,\n          1578, 697,  1323, 785,  685,  775,  150,  2589, 2630, 4028,\n          1777, 860,  3855, 2498, 3205, 3462, 885,  3937, 2306, 532,\n          3451, 3157, 3548, 2712, 1765, 1368, 1686, 2315, 66,   2935,\n          482,  3465, 3215, 313,  970,  2728, 545,  3962, 2416, 3234,\n          2143, 3513, 2341, 1512, 3071, 1760, 1507, 4330, 398,  3151,\n          1933, 334,  1427, 3925, 768,  37,   444,  3713, 1111, 803,\n          3422, 704,  401,  1525, 318,  1827, 1179, 1537, 3937, 522};\n    }\n  }\n\n  SparseMatrixType matrix;\n  ylm::TensorYlm::fill_sphere_to_cart<TensorStructure>(\n      make_not_null(&matrix), ell_max, coefficient_normalization);\n  CAPTURE(symm_to_string<TensorStructure>());\n\n  // Loop over spec_matrix_elements and make sure all the cases agree.\n  if (coefficient_normalization ==\n      ylm::TensorYlm::CoefficientNormalization::Standard) {\n    for (size_t i = 0; i < spec_matrix_elements.size(); ++i) {\n      CAPTURE(spec_dest_indices[i]);\n      CAPTURE(spec_src_indices[i]);\n      CHECK(matrix(spec_dest_indices[i], spec_src_indices[i]) ==\n            approx(spec_matrix_elements[i]));\n    }\n  } else {\n    CHECK(coefficient_normalization ==\n          ylm::TensorYlm::CoefficientNormalization::Spherepack);\n    // Here the expected coefficients are off by a factor of (-1)^{m+m'),\n    // So we must multiply the matrix elements by this factor.\n    // Most of the logic below is for recovering m and m' from the\n    // SparseMatrix indices.\n    ylm::SpherepackIterator src_iter(ell_max, ell_max, 1, false);\n    ylm::SpherepackIterator dest_iter(ell_max, ell_max, 1, false);\n    for (size_t i = 0; i < spec_matrix_elements.size(); ++i) {\n      CAPTURE(spec_dest_indices[i]);\n      CAPTURE(spec_src_indices[i]);\n      // We need to recover the azimuthal index m from the\n      // SparseMatrix indices.\n      //\n      // First compute offsets into arrays.\n      // The SparseMatrices are indexed by\n      // k = iter() + component_index*iter.spherepack_array_size()\n      // where iter is a SpherepackIterator.\n      // The offset is the value returned by iter().\n      const size_t dest_offset =\n          spec_dest_indices[i] % dest_iter.spherepack_array_size();\n      const size_t src_offset =\n          spec_src_indices[i] % src_iter.spherepack_array_size();\n      // Reset the iterators so we can query them for m and m'.\n      src_iter.set(src_iter.compact_index(src_offset).value());\n      dest_iter.set(dest_iter.compact_index(dest_offset).value());\n      // factor is (-1)^{m+m'}\n      const double factor =\n          ((src_iter.m() + dest_iter.m()) % 2 == 0 ? 1.0 : -1.0);\n      CHECK(matrix(spec_dest_indices[i], spec_src_indices[i]) ==\n            approx(factor * spec_matrix_elements[i]));\n    }\n  }\n}\n}  // namespace\n\n// [[TimeOut, 80]]\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.TensorYlmSphereToCart\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  const size_t ell_max = 8;\n\n  for (const auto norm :\n       std::vector<ylm::TensorYlm::CoefficientNormalization>{\n           {ylm::TensorYlm::CoefficientNormalization::Standard,\n            ylm::TensorYlm::CoefficientNormalization::Spherepack}}) {\n    test_tensorylm_sphere_to_cart_vs_spec<\n        typename tnsr::i<DataVector, 3>::structure, SimpleSparseMatrix>(ell_max,\n                                                                        norm);\n    test_tensorylm_sphere_to_cart_vs_spec<\n        typename tnsr::ii<DataVector, 3>::structure, SimpleSparseMatrix>(\n        ell_max, norm);\n    test_tensorylm_sphere_to_cart_vs_spec<\n        typename tnsr::ij<DataVector, 3>::structure, SimpleSparseMatrix>(\n        ell_max, norm);\n    test_tensorylm_sphere_to_cart_vs_spec<\n        typename tnsr::ijj<DataVector, 3>::structure, SimpleSparseMatrix>(\n        ell_max, norm);\n    test_tensorylm_sphere_to_cart_vs_spec<\n        typename tnsr::ijk<DataVector, 3>::structure, SimpleSparseMatrix>(\n        ell_max, norm);\n\n    test_inverse<typename tnsr::i<DataVector, 3>>(ell_max, norm);\n    test_inverse<typename tnsr::ii<DataVector, 3>>(ell_max, norm);\n    test_inverse<typename tnsr::ij<DataVector, 3>>(ell_max, norm);\n    test_inverse<typename tnsr::ijj<DataVector, 3>>(ell_max, norm);\n    test_inverse<typename tnsr::ijk<DataVector, 3>>(ell_max, norm);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_WignerThreeJ.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <vector>\n\n#include \"NumericalAlgorithms/SphericalHarmonics/WignerThreeJ.hpp\"\n\nnamespace {\nvoid test_wigner_three_j() {\n  // For these, tests, Mark computed a few Wigner 3J symbols\n  // analytically using an online tool.\n  WignerThreeJ coefa(3, 2, 4, -3);\n  CHECK(coefa.l1_min() == 1);\n  CHECK(coefa.l1_max() == 7);\n  CHECK(coefa(0) == 0.0);  // Triangle condition violated, return 0 no error.\n  CHECK(coefa(1) == approx(-0.5 * sqrt(1.0 / 3.0)));\n  CHECK(coefa(2) == approx(-(1.0 / 6.0) * sqrt(1.0 / 5.0)));\n  CHECK(coefa(3) == approx((1.0 / 3.0) * sqrt(1.0 / 11.0)));\n  CHECK(coefa(4) == approx(sqrt(1.0 / 33.0)));\n  CHECK(coefa(5) == approx((1.0 / 6.0) * sqrt(11.0 / 13.0)));\n  CHECK(coefa(6) == approx((17.0 / 6.0) * sqrt(1.0 / 1001.0)));\n  CHECK(coefa(7) == approx(4.0 * sqrt(1.0 / 15015.0)));\n  CHECK(coefa(8) == 0.0);  // Triangle condition violated, return 0 no error.\n\n  WignerThreeJ coefb(1, -1, 2, -1);\n  CHECK(coefb.l1_min() == 2);\n  CHECK(coefb.l1_max() == 3);\n  CHECK(coefb(1) == 0.0);  // Triangle condition violated, return 0 no error.\n  CHECK(coefb(2) == approx(sqrt(1.0 / 15.0)));\n  CHECK(coefb(3) == approx(-sqrt(2.0 / 21.0)));\n  CHECK(coefb(4) == 0.0);  // Triangle condition violated, return 0 no error.\n\n  // Intentionally pass |m| > l to get error.\n  CHECK_THROWS_WITH(WignerThreeJ(3, 4, 4, -3),\n                    Catch::Matchers::ContainsSubstring(\"m2 is 4 but l2 is 3\"));\n  CHECK_THROWS_WITH(WignerThreeJ(3, 3, 4, -5),\n                    Catch::Matchers::ContainsSubstring(\"m3 is -5 but l3 is 4\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.WignerThreeJ\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  test_wigner_three_j();\n}\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SphericalHarmonics/Test_YlmToStf.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/RealSphericalHarmonics.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/YlmToStf.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ylm {\nSPECTRE_TEST_CASE(\"Unit.SphericalHarmonics.YlmToStf\",\n                  \"[NumericalAlgorithms][Unit]\") {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> coef_distribution(-0.1, 0.1);\n\n  std::uniform_real_distribution<double> theta_distribution(0., M_PI);\n  std::uniform_real_distribution<double> phi_distribution(0., 2 * M_PI);\n\n  const size_t num_points = 10000;\n\n  const auto theta = make_with_random_values<DataVector>(\n      make_not_null(&generator), make_not_null(&theta_distribution),\n      DataVector(num_points));\n  const auto phi = make_with_random_values<DataVector>(\n      make_not_null(&generator), make_not_null(&phi_distribution),\n      DataVector(num_points));\n\n  tnsr::I<DataVector, 3> normal_vector(num_points);\n  get<0>(normal_vector) = sin(theta) * cos(phi);\n  get<1>(normal_vector) = sin(theta) * sin(phi);\n  get<2>(normal_vector) = cos(theta);\n\n  {\n    // l=0\n    const auto ylm_l0_coefs = make_with_random_values<ModalVector>(\n        make_not_null(&generator), make_not_null(&coef_distribution),\n        ModalVector(1));\n    const auto stf_l0_coefs = ylm_to_stf_0(ylm_l0_coefs);\n\n    DataVector stf_l0_at_points(num_points, get(stf_l0_coefs));\n    const DataVector ylm_l0_at_points =\n        ylm_l0_coefs.at(0) * real_spherical_harmonic(theta, phi, 0, 0);\n    CHECK_ITERABLE_APPROX(stf_l0_at_points, ylm_l0_at_points);\n  }\n  {\n    // l=1\n    const auto ylm_l1_coefs = make_with_random_values<ModalVector>(\n        make_not_null(&generator), make_not_null(&coef_distribution),\n        ModalVector(3));\n    const auto stf_l1_coefs = ylm_to_stf_1<Frame::Grid>(ylm_l1_coefs);\n\n    DataVector stf_l1_at_points(num_points, 0.);\n    DataVector ylm_l1_at_points(num_points, 0.);\n    for (size_t i = 0; i < 3; ++i) {\n      stf_l1_at_points += stf_l1_coefs.get(i) * normal_vector.get(i);\n      ylm_l1_at_points +=\n          ylm_l1_coefs.at(i) * real_spherical_harmonic(theta, phi, 1, i - 1);\n    }\n    CHECK_ITERABLE_APPROX(stf_l1_at_points, ylm_l1_at_points);\n  }\n  {\n    // l=2\n    const auto ylm_l2_coefs = make_with_random_values<ModalVector>(\n        make_not_null(&generator), make_not_null(&coef_distribution),\n        ModalVector(5));\n    const auto stf_l2_coefs = ylm_to_stf_2<Frame::Grid>(ylm_l2_coefs);\n\n    // check trace is zero\n    CHECK((stf_l2_coefs.get(0, 0) + stf_l2_coefs.get(1, 1) +\n           stf_l2_coefs.get(2, 2)) == approx(0.));\n    DataVector stf_l2_at_points(num_points, 0.);\n    DataVector ylm_l2_at_points(num_points, 0.);\n    for (size_t i = 0; i < 5; ++i) {\n      ylm_l2_at_points +=\n          ylm_l2_coefs.at(i) * real_spherical_harmonic(theta, phi, 2, i - 2);\n    }\n\n    // we don't need to compute the trace-free part of the second order normal\n    // vector combination because the trace is automatically removed by\n    // contracting with the trace-free coefficient tensor.\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        stf_l2_at_points += stf_l2_coefs.get(i, j) * normal_vector.get(i) *\n                            normal_vector.get(j);\n      }\n    }\n    CHECK_ITERABLE_APPROX(stf_l2_at_points, ylm_l2_at_points);\n  }\n}\n}  // namespace ylm\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_SpinWeightedSphericalHarmonics\")\n\nset(LIBRARY_SOURCES\n  Test_ComplexDataView.cpp\n  Test_SwshCoefficients.cpp\n  Test_SwshCollocation.cpp\n  Test_SwshDerivatives.cpp\n  Test_SwshFiltering.cpp\n  Test_SwshInterpolation.cpp\n  Test_SwshTags.cpp\n  Test_SwshTestHelpers.cpp\n  Test_SwshTransform.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  DataStructures\n  DiscontinuousGalerkin\n  Domain\n  ErrorHandling\n  LinearOperators\n  Spectral\n  SpinWeightedSphericalHarmonics\n  SpinWeightedSphericalHarmonicsHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_ComplexDataView.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace Spectral::Swsh::detail {\nnamespace {\n\ntemplate <ComplexRepresentation Representation>\nvoid test_basic_view_functionality(const ComplexDataView<Representation>& view,\n                                   std::complex<double>* const source_vec_data,\n                                   const size_t view_size,\n                                   const size_t offset) {\n  CHECK(view.size() == view_size);\n  // clang-tidy: This class uses manual memory management deliberately,\n  // so silence complaints about pointer math and casts.\n  // The reinterpret casts are intentional. See\n  // NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.cpp\n  // for an explanation\n  if (Representation == ComplexRepresentation::Interleaved) {\n    CHECK(view.real_data() ==\n          reinterpret_cast<double*>(source_vec_data + offset));  // NOLINT\n    CHECK(view.real_data() ==\n          reinterpret_cast<double*>(source_vec_data + offset));  // NOLINT\n  }\n  CHECK(view.stride() ==\n        (Representation == ComplexRepresentation::Interleaved ? 2 : 1));\n}\n\ntemplate <ComplexRepresentation Representation>\nvoid test_view() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> dist{-100.0, 100.0};\n  UniformCustomDistribution<size_t> sdist{5, 50};\n  const size_t overall_size = sdist(gen);\n  const size_t view_size = sdist(gen) % (overall_size) + 1;\n  const size_t offset = sdist(gen) % (overall_size - view_size + 1);\n  const size_t stride = ComplexDataView<Representation>::stride();\n  ComplexDataVector source_vec{overall_size};\n  std::vector<std::complex<double>> ptr_source_vec(overall_size);\n  ComplexDataVector assign_vec_1{view_size};\n  ComplexDataVector assign_vec_2{view_size};\n  ComplexDataVector assign_view_source{view_size};\n\n  fill_with_random_values(make_not_null(&source_vec), make_not_null(&gen),\n                          make_not_null(&dist));\n  fill_with_random_values(make_not_null(&ptr_source_vec), make_not_null(&gen),\n                          make_not_null(&dist));\n\n  ComplexDataView<Representation> vector_view{make_not_null(&source_vec),\n                                              view_size, offset};\n  test_basic_view_functionality(vector_view, source_vec.data(), view_size,\n                                offset);\n\n  const ComplexDataVector source_vec_copy = source_vec;\n  // check conjugation utility\n  vector_view.conjugate();\n  for (size_t i = 0; i < view_size; i++) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(vector_view.real_data()[stride * i] ==\n          real(source_vec_copy[i + offset]));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(vector_view.imag_data()[stride * i] ==\n          -imag(source_vec_copy[i + offset]));\n  }\n\n  vector_view.copy_back_to_source();\n  for (size_t i = 0; i < view_size; i++) {\n    CHECK(source_vec[i + offset] == conj(source_vec_copy)[i + offset]);\n  }\n\n  // reverse the conjugation and check again\n  vector_view.conjugate();\n  for (size_t i = 0; i < view_size; i++) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(vector_view.real_data()[stride * i] ==\n          real(source_vec_copy[i + offset]));\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(vector_view.imag_data()[stride * i] ==\n          imag(source_vec_copy[i + offset]));\n  }\n\n  vector_view.copy_back_to_source();\n  const ComplexDataView<Representation> ptr_view{\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      ptr_source_vec.data() + offset, view_size};\n  test_basic_view_functionality(ptr_view, ptr_source_vec.data(), view_size,\n                                offset);\n\n  const ComplexDataVector pre_change =\n      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n      ComplexDataVector{source_vec.data() + offset, view_size};\n  // check the various assignment operators\n  for (size_t i = 0; i < view_size; i++) {\n    assign_vec_1[i] = source_vec[i + offset] + std::complex<double>{1.0, 1.0};\n    assign_vec_2[i] = source_vec[i + offset] + std::complex<double>{2.0, 2.0};\n  }\n\n  // real assignment\n  vector_view.assign_real(real(assign_vec_1));\n  for (size_t i = 0; i < view_size; i++) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(vector_view.real_data()[stride * i] == real(assign_vec_1)[i]);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(vector_view.imag_data()[stride * i] == imag(pre_change)[i]);\n  }\n\n  vector_view.copy_back_to_source();\n  for (size_t i = 0; i < view_size; i++) {\n    CHECK(real(assign_vec_1[i]) == real(source_vec[i + offset]));\n    CHECK(imag(pre_change[i]) == imag(source_vec[i + offset]));\n  }\n\n  // imag assignment\n  vector_view.assign_imag(imag(assign_vec_2));\n  for (size_t i = 0; i < view_size; i++) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(vector_view.real_data()[stride * i] == real(assign_vec_1)[i]);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(vector_view.imag_data()[stride * i] == imag(assign_vec_2)[i]);\n  }\n\n  vector_view.copy_back_to_source();\n  for (size_t i = 0; i < view_size; i++) {\n    CHECK(real(assign_vec_1[i]) == real(source_vec[i + offset]));\n    CHECK(imag(assign_vec_2[i]) == imag(source_vec[i + offset]));\n  }\n\n  // check self-assignment from the referenced vector\n#if defined(__clang__) && __clang_major__ > 6\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wself-assign-overloaded\"\n#endif  // defined(__clang__) && __clang_major__ > 6\n  assign_vec_1 = assign_vec_1;\n#if defined(__clang__) && __clang_major__ > 6\n#pragma GCC diagnostic pop\n#endif  // defined(__clang__) && __clang_major__ > 6\n  for (size_t i = 0; i < view_size; i++) {\n    CHECK(real(assign_vec_1[i]) == real(source_vec[i + offset]));\n    CHECK(imag(assign_vec_1[i]) == imag(assign_vec_1[i]));\n  }\n\n  // full assignment from vector\n  fill_with_random_values(make_not_null(&assign_vec_1), make_not_null(&gen),\n                          make_not_null(&dist));\n  vector_view = assign_vec_1;\n  vector_view.copy_back_to_source();\n  for (size_t i = 0; i < view_size; i++) {\n    CHECK(source_vec[i + offset] == assign_vec_1[i]);\n  }\n\n  // full assignment from a view\n  fill_with_random_values(make_not_null(&assign_view_source),\n                          make_not_null(&gen), make_not_null(&dist));\n  ComplexDataView<Representation> assign_view{\n      make_not_null(&assign_view_source), assign_view_source.size()};\n  vector_view = assign_view;\n  vector_view.copy_back_to_source();\n  for (size_t i = 0; i < view_size; i++) {\n    CHECK(source_vec[i + offset] == assign_view_source[i]);\n  }\n\n  // check self-assignment from the view\n#if defined(__clang__) && __clang_major__ > 6\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wself-assign-overloaded\"\n#endif  // defined(__clang__) && __clang_major__ > 6\n  // clang-tidy and gcc ignore for allowing the intentional self-assignment\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wextra\"\n  vector_view = vector_view;  // NOLINT\n#pragma GCC diagnostic pop\n#if defined(__clang__) && __clang_major__ > 6\n#pragma GCC diagnostic pop\n#endif  // defined(__clang__) && __clang_major__ > 6\n  for (size_t i = 0; i < view_size; i++) {\n    CHECK(source_vec[i + offset] == assign_view_source[i]);\n  }\n  vector_view.copy_back_to_source();\n  for (size_t i = 0; i < view_size; i++) {\n    CHECK(source_vec[i + offset] == assign_view_source[i]);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.ComplexDataView\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  {\n    INFO(\"Test Interleaved view\");\n    test_view<ComplexRepresentation::Interleaved>();\n  }\n  {\n    INFO(\"Test RealsThenImags view\");\n    test_view<ComplexRepresentation::RealsThenImags>();\n  }\n\n#ifdef SPECTRE_DEBUG\n  // spot-test two assignment asserts - they all call the same size-checking\n  // function, so this should be sufficiently robust.\n  CHECK_THROWS_WITH(\n      ([]() {\n        MAKE_GENERATOR(gen);\n        UniformCustomDistribution<size_t> sdist{5, 50};\n        const size_t overall_size = sdist(gen);\n        const size_t view_size = sdist(gen) % (overall_size - 1) + 1;\n        const size_t offset = sdist(gen) % (overall_size - view_size + 1);\n\n        ComplexDataVector source_vec{overall_size};\n        ComplexDataView<ComplexRepresentation::Interleaved> vector_view_1{\n            make_not_null(&source_vec), view_size, offset};\n        ComplexDataView<ComplexRepresentation::Interleaved> vector_view_2{\n            make_not_null(&source_vec), view_size + 1, offset};\n\n        // this line should fail the size assert\n        vector_view_1 = vector_view_2;\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Assignment must be to the same size\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        MAKE_GENERATOR(gen);\n        UniformCustomDistribution<size_t> sdist{5, 50};\n        const size_t overall_size = sdist(gen);\n        const size_t view_size = sdist(gen) % (overall_size - 1) + 1;\n        const size_t offset = sdist(gen) % (overall_size - view_size + 1);\n\n        ComplexDataVector source_vec{overall_size};\n        ComplexDataView<ComplexRepresentation::RealsThenImags> vector_view_1{\n            make_not_null(&source_vec), view_size, offset};\n\n        // this line should fail the size assert\n        vector_view_1.assign_real(real(source_vec));\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Assignment must be to the same size\"));\n#endif\n}\n\n}  // namespace\n}  // namespace Spectral::Swsh::detail\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_SwshCoefficients.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <sharp_cxx.h>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace Spectral::Swsh {\nnamespace {\n\nvoid test_swsh_coefficients_class_interface() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{8, 64};\n  const size_t l_max = sdist(gen);\n\n  CAPTURE(l_max);\n  const CoefficientsMetadata& precomputed_libsharp_lm =\n      cached_coefficients_metadata(l_max);\n\n  const CoefficientsMetadata& another_precomputed_libsharp_lm =\n      cached_coefficients_metadata(l_max);\n\n  // checks that the same pointer is in both\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info() ==\n        another_precomputed_libsharp_lm.get_sharp_alm_info());\n\n  const CoefficientsMetadata computed_coefficients{l_max};\n\n  CHECK(precomputed_libsharp_lm.l_max() == l_max);\n  CHECK(computed_coefficients.l_max() == l_max);\n\n  sharp_alm_info* expected_sharp_alm_info = nullptr;\n  sharp_make_triangular_alm_info(l_max, l_max, 1, &expected_sharp_alm_info);\n\n  // check that all of the precomputed coefficients, the directly constructed\n  // coefficients, and the manually created sharp_alm_info* all contain the same\n  // data\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->lmax ==\n        computed_coefficients.get_sharp_alm_info()->lmax);\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->lmax ==\n        expected_sharp_alm_info->lmax);\n  CHECK(\n      static_cast<size_t>(precomputed_libsharp_lm.get_sharp_alm_info()->lmax) ==\n      computed_coefficients.l_max());\n\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->nm ==\n        computed_coefficients.get_sharp_alm_info()->nm);\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->nm ==\n        expected_sharp_alm_info->nm);\n\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->flags ==\n        computed_coefficients.get_sharp_alm_info()->flags);\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->flags ==\n        expected_sharp_alm_info->flags);\n\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->stride ==\n        computed_coefficients.get_sharp_alm_info()->stride);\n  CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->stride ==\n        expected_sharp_alm_info->stride);\n\n  for (size_t m_index = 0;\n       m_index <\n       static_cast<size_t>(precomputed_libsharp_lm.get_sharp_alm_info()->nm);\n       ++m_index) {\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->mval[m_index] ==\n          computed_coefficients.get_sharp_alm_info()->mval[m_index]);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->mval[m_index] ==\n          expected_sharp_alm_info->mval[m_index]);\n\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->mvstart[m_index] ==\n          computed_coefficients.get_sharp_alm_info()->mvstart[m_index]);\n    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n    CHECK(precomputed_libsharp_lm.get_sharp_alm_info()->mvstart[m_index] ==\n          expected_sharp_alm_info->mvstart[m_index]);\n  }\n\n  CHECK(precomputed_libsharp_lm.begin() == precomputed_libsharp_lm.cbegin());\n  CHECK(precomputed_libsharp_lm.end() == precomputed_libsharp_lm.cend());\n  CHECK(precomputed_libsharp_lm.begin() != precomputed_libsharp_lm.end());\n\n  size_t offset_counter = 0;\n  size_t expected_l = 0;\n  size_t expected_m = 0;\n  for (const auto coefficient_info : precomputed_libsharp_lm) {\n    CHECK(coefficient_info.transform_of_real_part_offset == offset_counter);\n    CHECK(coefficient_info.transform_of_imag_part_offset -\n              coefficient_info.transform_of_real_part_offset ==\n          size_of_libsharp_coefficient_vector(l_max) / 2);\n    CHECK(coefficient_info.l == expected_l);\n    CHECK(coefficient_info.m == expected_m);\n    ++offset_counter;\n    if (expected_l == l_max) {\n      ++expected_m;\n      expected_l = expected_m;\n    } else {\n      ++expected_l;\n    }\n  }\n}\n\ntemplate <int Spin, ComplexRepresentation Representation =\n                        ComplexRepresentation::Interleaved>\nvoid check_goldberg_mode_conversion() {\n  MAKE_GENERATOR(gen);\n  // limited l_max distribution because Goldberg test depends on an analytic\n  // basis function with factorials.\n  UniformCustomDistribution<size_t> sdist{4, 8};\n  const size_t l_max = sdist(gen);\n  UniformCustomDistribution<double> coefficient_distribution{-10.0, 10.0};\n\n  CAPTURE(l_max);\n  const CoefficientsMetadata& precomputed_libsharp_lm =\n      cached_coefficients_metadata(l_max);\n\n  // low value to limit test time\n  size_t number_of_radial_points = 2;\n\n  auto expected_goldberg_modes = make_with_random_values<ComplexModalVector>(\n      make_not_null(&gen), make_not_null(&coefficient_distribution),\n      square(l_max + 1) * number_of_radial_points);\n\n  // set to zero all modes for l < Spin (the basis functions are zero for those\n  // modes)\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    for (size_t j = 0; j < static_cast<size_t>(square(Spin)); ++j) {\n      expected_goldberg_modes[j + square(l_max + 1) * i] = 0.0;\n    }\n  }\n\n  SpinWeighted<ComplexDataVector, Spin> goldberg_collocation_points;\n  goldberg_collocation_points.data() = ComplexDataVector{\n      number_of_radial_points * number_of_swsh_collocation_points(l_max), 0.0};\n\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    for (int l = 0; l <= static_cast<int>(l_max); ++l) {\n      for (int m = -l; m <= l; ++m) {\n        for (const auto collocation_point :\n             cached_collocation_metadata<Representation>(l_max)) {\n          goldberg_collocation_points\n              .data()[collocation_point.offset +\n                      i * number_of_swsh_collocation_points(l_max)] +=\n              expected_goldberg_modes[static_cast<size_t>(square(l) + m + l) +\n                                      i * square(l_max + 1)] *\n              TestHelpers::spin_weighted_spherical_harmonic(\n                  Spin, l, m, collocation_point.theta, collocation_point.phi);\n        }\n      }\n    }\n  }\n\n  SpinWeighted<ComplexModalVector, Spin> test_modes =\n      swsh_transform<Representation>(l_max, number_of_radial_points,\n                                     goldberg_collocation_points);\n\n  Approx swsh_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n          .scale(1.0);\n\n  const auto goldberg_modes = libsharp_to_goldberg_modes(test_modes, l_max);\n  CHECK_ITERABLE_CUSTOM_APPROX(goldberg_modes.data(), expected_goldberg_modes,\n                               swsh_approx);\n\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    for (const auto coefficient_info : precomputed_libsharp_lm) {\n      auto goldberg_mode_plus_m =\n          libsharp_mode_to_goldberg_plus_m(coefficient_info, test_modes, i);\n      // should be the same value computed using the other interface\n      auto alternative_mode_plus_m = libsharp_mode_to_goldberg(\n          coefficient_info.l, static_cast<int>(coefficient_info.m), l_max,\n          test_modes, i);\n      auto goldberg_mode_plus_m_from_full_set =\n          goldberg_modes.data()[goldberg_mode_index(\n              l_max, coefficient_info.l, static_cast<int>(coefficient_info.m),\n              i)];\n\n      auto goldberg_mode_minus_m =\n          libsharp_mode_to_goldberg_minus_m(coefficient_info, test_modes, i);\n      // should be the same value computed using the other interface\n      auto alternative_mode_minus_m = libsharp_mode_to_goldberg(\n          coefficient_info.l, -static_cast<int>(coefficient_info.m), l_max,\n          test_modes, i);\n      auto goldberg_mode_minus_m_from_full_set =\n          goldberg_modes.data()[goldberg_mode_index(\n              l_max, coefficient_info.l, -static_cast<int>(coefficient_info.m),\n              i)];\n\n      CHECK_ITERABLE_CUSTOM_APPROX(goldberg_mode_plus_m,\n                                   alternative_mode_plus_m, swsh_approx);\n      CHECK_ITERABLE_CUSTOM_APPROX(goldberg_mode_plus_m,\n                                   goldberg_mode_plus_m_from_full_set,\n                                   swsh_approx);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          goldberg_mode_plus_m,\n          expected_goldberg_modes[goldberg_mode_index(\n              l_max, coefficient_info.l, static_cast<int>(coefficient_info.m),\n              i)],\n          swsh_approx);\n\n      CHECK_ITERABLE_CUSTOM_APPROX(goldberg_mode_minus_m,\n                                   alternative_mode_minus_m, swsh_approx);\n      CHECK_ITERABLE_CUSTOM_APPROX(goldberg_mode_minus_m,\n                                   goldberg_mode_minus_m_from_full_set,\n                                   swsh_approx);\n      CHECK_ITERABLE_CUSTOM_APPROX(\n          goldberg_mode_minus_m,\n          expected_goldberg_modes[goldberg_mode_index(\n              l_max, coefficient_info.l, -static_cast<int>(coefficient_info.m),\n              i)],\n          swsh_approx);\n\n      goldberg_modes_to_libsharp_modes_single_pair(\n          coefficient_info, make_not_null(&test_modes), i,\n          expected_goldberg_modes[goldberg_mode_index(\n              l_max, coefficient_info.l, static_cast<int>(coefficient_info.m),\n              i)],\n          expected_goldberg_modes[goldberg_mode_index(\n              l_max, coefficient_info.l, -static_cast<int>(coefficient_info.m),\n              i)]);\n    }\n  }\n  const auto full_libsharp_conversion =\n      goldberg_to_libsharp_modes(goldberg_modes, l_max);\n  CHECK_ITERABLE_CUSTOM_APPROX(full_libsharp_conversion, test_modes,\n                               swsh_approx);\n  const auto inverse_transform_set_from_goldberg =\n      inverse_swsh_transform<Representation>(l_max, number_of_radial_points,\n                                             test_modes);\n  CHECK_ITERABLE_CUSTOM_APPROX(inverse_transform_set_from_goldberg.data(),\n                               goldberg_collocation_points.data(), swsh_approx);\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.SwshCoefficients\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  {\n    INFO(\"Checking Coefficients for libsharp interoperability\");\n    test_swsh_coefficients_class_interface();\n  }\n  {\n    INFO(\"Checking Goldberg mode conversions\");\n    check_goldberg_mode_conversion<-1>();\n    check_goldberg_mode_conversion<0>();\n    check_goldberg_mode_conversion<2>();\n  }\n\n  CHECK_THROWS_WITH(\n      (cached_coefficients_metadata(detail::coefficients_maximum_l_max + 1)),\n      Catch::Matchers::ContainsSubstring(\"Index out of range\"));\n}\n}  // namespace\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_SwshCollocation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <sharp_cxx.h>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace Spectral::Swsh {\nnamespace {\n\ntemplate <ComplexRepresentation Representation>\nvoid test_spherical_harmonic_collocation() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{8, 64};\n  const size_t l_max = sdist(gen);\n\n  CAPTURE(l_max);\n  const CollocationMetadata<Representation>& precomputed_collocation_value =\n      cached_collocation_metadata<Representation>(l_max);\n\n  const CollocationMetadata<Representation>& another_precomputed_collocation =\n      cached_collocation_metadata<Representation>(l_max);\n\n  // checks that the same pointer is in both\n  CHECK(precomputed_collocation_value.get_sharp_geom_info() ==\n        another_precomputed_collocation.get_sharp_geom_info());\n\n  const CollocationMetadata<Representation> computed_collocation{l_max};\n\n  CHECK(precomputed_collocation_value.l_max() == l_max);\n  CHECK(computed_collocation.l_max() == l_max);\n\n  const int expected_stride = detail::ComplexDataView<Representation>::stride();\n\n  sharp_geom_info* manual_sgi = nullptr;\n  sharp_make_gauss_geom_info(\n      static_cast<int>(l_max) + 1, 2 * static_cast<int>(l_max) + 1, 0.0,\n      expected_stride,\n      static_cast<unsigned long>(expected_stride) * (2 * l_max + 1),\n      &manual_sgi);\n\n  // clang-tidy doesn't like the pointer manipulation needed to work with the\n  // libsharp types.\n  CHECK(precomputed_collocation_value  // NOLINT\n            .get_sharp_geom_info()\n            ->pair[0]\n            .r1.stride == expected_stride);  // NOLINT\n  CHECK(precomputed_collocation_value        // NOLINT\n            .get_sharp_geom_info()\n            ->pair[0]\n            .r2.stride == expected_stride);  // NOLINT\n  CHECK(computed_collocation                 // NOLINT\n            .get_sharp_geom_info()\n            ->pair[0]\n            .r1.stride == expected_stride);  // NOLINT\n  CHECK(computed_collocation                 // NOLINT\n            .get_sharp_geom_info()\n            ->pair[0]\n            .r2.stride == expected_stride);  // NOLINT\n\n  size_t offset_counter = 0;\n\n  sharp_geom_info* computed_sgi = computed_collocation.get_sharp_geom_info();\n\n  // check iterator equivalence. The for loop below is also a check of the\n  // iterator functionality.\n  CHECK(precomputed_collocation_value.begin() ==\n        precomputed_collocation_value.cbegin());\n  CHECK(precomputed_collocation_value.end() ==\n        precomputed_collocation_value.cend());\n  CHECK(precomputed_collocation_value.begin() !=\n        precomputed_collocation_value.end());\n  for (const auto collocation_point : precomputed_collocation_value) {\n    CHECK(collocation_point.offset == offset_counter);\n    CHECK(collocation_point.theta ==\n          computed_collocation.theta(offset_counter));\n    CHECK(collocation_point.phi == computed_collocation.phi(offset_counter));\n    CAPTURE(offset_counter);\n    // check theta values:\n    if (offset_counter < (2 * l_max + 1) * (l_max / 2 + 1)) {\n      CAPTURE(offset_counter / (2 * l_max + 1));\n      CHECK(approx(computed_collocation.theta(offset_counter)) ==\n            computed_sgi  // NOLINT\n                ->pair[offset_counter / (2 * l_max + 1)]\n                .r1.theta);  // NOLINT\n      CHECK(approx(computed_collocation.theta(offset_counter)) ==\n            manual_sgi  // NOLINT\n                ->pair[offset_counter / (2 * l_max + 1)]\n                .r1.theta);  // NOLINT\n    } else {\n      CAPTURE(l_max - (offset_counter / (2 * l_max + 1)));\n      CHECK(approx(computed_collocation.theta(offset_counter)) ==\n            computed_sgi  // NOLINT\n                ->pair[l_max - offset_counter / (2 * l_max + 1)]\n                .r2.theta);  // NOLINT\n      CHECK(approx(computed_collocation.theta(offset_counter)) ==\n            manual_sgi  // NOLINT\n                ->pair[l_max - offset_counter / (2 * l_max + 1)]\n                .r2.theta);  // NOLINT\n    }\n\n    // check phi values:\n    CHECK(approx(computed_collocation.phi(offset_counter)) ==\n          2.0 * M_PI *\n              ((offset_counter % (2 * l_max + 1)) / (2.0 * l_max + 1.0)));\n    CHECK(approx(computed_collocation.phi(offset_counter)) ==\n          2.0 * M_PI *\n              ((offset_counter % (2 * l_max + 1)) / (2.0 * l_max + 1.0)));\n    offset_counter++;\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.SwshCollocation\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  {\n    INFO(\"CollocationMetadata based on contiguous complex data (Interleaved)\");\n    test_spherical_harmonic_collocation<ComplexRepresentation::Interleaved>();\n  }\n  {\n    INFO(\n        \"CollocationMetadata based on a pair of contiguous blocks representing \"\n        \"complex data (RealsThenImags)\");\n    test_spherical_harmonic_collocation<\n        ComplexRepresentation::RealsThenImags>();\n  }\n\n  CHECK_THROWS_WITH(\n      (cached_collocation_metadata<ComplexRepresentation::RealsThenImags>(\n          collocation_maximum_l_max + 1)),\n      Catch::Matchers::ContainsSubstring(\"Index out of range\"));\n}\n}  // namespace\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_SwshDerivatives.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <string>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshDerivatives.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Spectral::Swsh {\nnamespace {\n\ntemplate <size_t Index, int Spin>\nstruct TestTag : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Spin>>;\n};\n\n// This function verifies that the derivative coefficient routine works as\n// expected by comparing the result of transforming, multiplying by the\n// derivative operator in SWSH coefficients, then inverse transforming to an\n// analytical result for the derivative. This effectively tests a `detail`\n// feature, but the intermediate check has been important in catching subtle\n// bugs previously.\ntemplate <typename DerivativeKind, ComplexRepresentation Representation,\n          int Spin>\nvoid test_derivative_via_transforms() {\n  // generate coefficients for the transformation\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> size_distribution{2, 7};\n  const size_t l_max = size_distribution(gen);\n  const size_t number_of_radial_points = 2;\n  UniformCustomDistribution<double> coefficient_distribution{-10.0, 10.0};\n\n  ComplexModalVector generated_modes{\n      size_of_libsharp_coefficient_vector(l_max) * number_of_radial_points};\n  TestHelpers::generate_swsh_modes<Spin>(\n      make_not_null(&generated_modes), make_not_null(&gen),\n      make_not_null(&coefficient_distribution), number_of_radial_points, l_max);\n\n  // fill the expected collocation point data by evaluating the analytic\n  // functions. This is very slow and imprecise (due to factorial division), but\n  // comparatively simple to formulate.\n  SpinWeighted<ComplexDataVector, Spin> computed_collocation{\n      number_of_swsh_collocation_points(l_max) * number_of_radial_points};\n  ComplexDataVector expected_derivative_collocation{\n      number_of_swsh_collocation_points(l_max) * number_of_radial_points};\n\n  // Fill the collocation values for the original function from the generated\n  // mode coefficients\n  TestHelpers::swsh_collocation_from_coefficients_and_basis_func<\n      Spin, Representation>(&computed_collocation.data(), generated_modes,\n                            l_max, number_of_radial_points,\n                            TestHelpers::spin_weighted_spherical_harmonic);\n\n  // Fill the collocation values for the derivative of the function using the\n  // analytical form of the derivatives of the spin-weighted harmonics.\n  TestHelpers::swsh_collocation_from_coefficients_and_basis_func<\n      Spin, Representation>(\n      &expected_derivative_collocation, generated_modes, l_max,\n      number_of_radial_points,\n      TestHelpers::derivative_of_spin_weighted_spherical_harmonic<\n          DerivativeKind>);\n\n  auto transform_modes = swsh_transform<Representation>(\n      l_max, number_of_radial_points, computed_collocation);\n\n  SpinWeighted<ComplexModalVector,\n               Spin + Tags::derivative_spin_weight<DerivativeKind>>\n      derivative_modes{size_of_libsharp_coefficient_vector(l_max) *\n                       number_of_radial_points};\n  // apply the derivative operator to the coefficients\n  detail::compute_coefficients_of_derivative<DerivativeKind, Spin>(\n      make_not_null(&derivative_modes), make_not_null(&transform_modes), l_max,\n      number_of_radial_points);\n  ComplexDataVector transform_derivative_collocation =\n      inverse_swsh_transform<Representation>(l_max, number_of_radial_points,\n                                             derivative_modes)\n          .data();\n\n  // approximation needs to be a little loose to consistently accommodate the\n  // ratios of factorials in the analytic form\n  Approx angular_derivative_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n          .scale(1.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(transform_derivative_collocation,\n                               expected_derivative_collocation,\n                               angular_derivative_approx);\n}\n\n// This function verifies the operation of the compute_derivatives function for\n// processing collections of derivative operations which collect equal spin\n// objects during the forward and inverse transforms for transform\n// optimization. This calls the utility for two derivatives, `DerivativeKind0`\n// and `DerivativeKind1`, each applied to two scalars of spin-weight `Spin0` and\n// `Spin1`. The result is compared to analytical forms obtained by multiplying\n// the derivatives of the basis functions by the generated coefficients.\ntemplate <ComplexRepresentation Representation, int Spin0, int Spin1,\n          typename DerivativeKind0, typename DerivativeKind1>\nvoid test_compute_angular_derivatives() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> size_distribution{2, 7};\n  const size_t l_max = size_distribution(gen);\n  constexpr size_t number_of_radial_points = 2;\n  UniformCustomDistribution<double> coefficient_distribution{-10.0, 10.0};\n\n  using input_tag_list = tmpl::list<TestTag<0, Spin0>, TestTag<1, Spin1>>;\n  using derivative_tag_list =\n      tmpl::list<Tags::Derivative<TestTag<0, Spin0>, DerivativeKind0>,\n                 Tags::Derivative<TestTag<1, Spin1>, DerivativeKind0>,\n                 Tags::Derivative<TestTag<0, Spin0>, DerivativeKind1>,\n                 Tags::Derivative<TestTag<1, Spin1>, DerivativeKind1>>;\n  using collocation_variables_tag =\n      ::Tags::Variables<tmpl::append<input_tag_list, derivative_tag_list>>;\n  using coefficients_variables_tag = ::Tags::Variables<db::wrap_tags_in<\n      Tags::SwshTransform, tmpl::append<input_tag_list, derivative_tag_list>>>;\n\n  auto box = db::create<\n      db::AddSimpleTags<collocation_variables_tag, coefficients_variables_tag,\n                        Tags::LMax, Tags::NumberOfRadialPoints>,\n      db::AddComputeTags<>>(\n      typename collocation_variables_tag::type{\n          number_of_radial_points * number_of_swsh_collocation_points(l_max)},\n      typename coefficients_variables_tag::type{\n          size_of_libsharp_coefficient_vector(l_max) * number_of_radial_points},\n      l_max, number_of_radial_points);\n\n  ComplexModalVector expected_modes_spin_0{\n      number_of_radial_points * size_of_libsharp_coefficient_vector(l_max)};\n  TestHelpers::generate_swsh_modes<Spin0>(\n      make_not_null(&expected_modes_spin_0), make_not_null(&gen),\n      make_not_null(&coefficient_distribution), number_of_radial_points, l_max);\n\n  ComplexModalVector expected_modes_spin_1{\n      number_of_radial_points * size_of_libsharp_coefficient_vector(l_max)};\n  TestHelpers::generate_swsh_modes<Spin1>(\n      make_not_null(&expected_modes_spin_1), make_not_null(&gen),\n      make_not_null(&coefficient_distribution), number_of_radial_points, l_max);\n\n  const auto coefficients_to_analytic_collocation =\n      [&l_max](const auto computed_collocation,\n               const ComplexModalVector& expected_modes) {\n        constexpr int lambda_spin =\n            std::decay_t<decltype(*computed_collocation)>::type::spin;\n        TestHelpers::swsh_collocation_from_coefficients_and_basis_func<\n            lambda_spin, Representation>(\n            make_not_null(&get(*computed_collocation).data()), expected_modes,\n            l_max, number_of_radial_points,\n            TestHelpers::spin_weighted_spherical_harmonic);\n      };\n  // Put the collocation information derived from the generated modes in the\n  // DataBox\n  db::mutate<TestTag<0, Spin0>>(coefficients_to_analytic_collocation,\n                                make_not_null(&box), expected_modes_spin_0);\n  db::mutate<TestTag<1, Spin1>>(coefficients_to_analytic_collocation,\n                                make_not_null(&box), expected_modes_spin_1);\n\n  // these could be packed into a variables, but the current test wouldn't be\n  // much shorter. If the test is expanded to verify more than four results at a\n  // time, using a `Variables` for the expected results is recommended for\n  // brevity.\n  ComplexDataVector expected_derivative_0_collocation_spin_0{\n      number_of_radial_points * number_of_swsh_collocation_points(l_max)};\n  ComplexDataVector expected_derivative_0_collocation_spin_1{\n      number_of_radial_points * number_of_swsh_collocation_points(l_max)};\n  ComplexDataVector expected_derivative_1_collocation_spin_0{\n      number_of_radial_points * number_of_swsh_collocation_points(l_max)};\n  ComplexDataVector expected_derivative_1_collocation_spin_1{\n      number_of_radial_points * number_of_swsh_collocation_points(l_max)};\n\n  TestHelpers::swsh_collocation_from_coefficients_and_basis_func<\n      Spin0, Representation>(\n      make_not_null(&expected_derivative_0_collocation_spin_0),\n      expected_modes_spin_0, l_max, number_of_radial_points,\n      TestHelpers::derivative_of_spin_weighted_spherical_harmonic<\n          DerivativeKind0>);\n\n  TestHelpers::swsh_collocation_from_coefficients_and_basis_func<\n      Spin1, Representation>(\n      make_not_null(&expected_derivative_0_collocation_spin_1),\n      expected_modes_spin_1, l_max, number_of_radial_points,\n      TestHelpers::derivative_of_spin_weighted_spherical_harmonic<\n          DerivativeKind0>);\n\n  TestHelpers::swsh_collocation_from_coefficients_and_basis_func<\n      Spin0, Representation>(\n      make_not_null(&expected_derivative_1_collocation_spin_0),\n      expected_modes_spin_0, l_max, number_of_radial_points,\n      TestHelpers::derivative_of_spin_weighted_spherical_harmonic<\n          DerivativeKind1>);\n\n  TestHelpers::swsh_collocation_from_coefficients_and_basis_func<\n      Spin1, Representation>(\n      make_not_null(&expected_derivative_1_collocation_spin_1),\n      expected_modes_spin_1, l_max, number_of_radial_points,\n      TestHelpers::derivative_of_spin_weighted_spherical_harmonic<\n          DerivativeKind1>);\n\n  // the actual derivative call\n  db::mutate_apply<AngularDerivatives<derivative_tag_list, Representation>>(\n      make_not_null(&box));\n\n  // large tolerance due to the necessity of factorial division in the analytic\n  // basis function being compared to.\n  Approx swsh_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n          .scale(1.0);\n  {\n    INFO(\"Check the coefficient data intermediate step\");\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        expected_modes_spin_0,\n        get(db::get<Tags::SwshTransform<TestTag<0, Spin0>>>(box)).data(),\n        swsh_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        expected_modes_spin_1,\n        get(db::get<Tags::SwshTransform<TestTag<1, Spin1>>>(box)).data(),\n        swsh_approx);\n  }\n  {\n    INFO(\"Check the collocation derivatives final result\");\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        expected_derivative_0_collocation_spin_0,\n        get(db::get<Tags::Derivative<TestTag<0, Spin0>, DerivativeKind0>>(box))\n            .data(),\n        swsh_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        expected_derivative_0_collocation_spin_1,\n        get(db::get<Tags::Derivative<TestTag<1, Spin1>, DerivativeKind0>>(box))\n            .data(),\n        swsh_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        expected_derivative_1_collocation_spin_0,\n        get(db::get<Tags::Derivative<TestTag<0, Spin0>, DerivativeKind1>>(box))\n            .data(),\n        swsh_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(\n        expected_derivative_1_collocation_spin_1,\n        get(db::get<Tags::Derivative<TestTag<1, Spin1>, DerivativeKind1>>(box))\n            .data(),\n        swsh_approx);\n  }\n  {\n    INFO(\"Check the multiple argument function interface\");\n    SpinWeighted<ComplexDataVector,\n                 Spin0 + Tags::derivative_spin_weight<DerivativeKind0>>\n        function_output_0{number_of_radial_points *\n                          number_of_swsh_collocation_points(l_max)};\n    SpinWeighted<ComplexDataVector,\n                 Spin1 + Tags::derivative_spin_weight<DerivativeKind1>>\n        function_output_1{number_of_radial_points *\n                          number_of_swsh_collocation_points(l_max)};\n    angular_derivatives<tmpl::list<DerivativeKind0, DerivativeKind1>,\n                        Representation>(\n        l_max, number_of_radial_points, make_not_null(&function_output_0),\n        make_not_null(&function_output_1), get(db::get<TestTag<0, Spin0>>(box)),\n        get(db::get<TestTag<1, Spin1>>(box)));\n    CHECK_ITERABLE_CUSTOM_APPROX(expected_derivative_0_collocation_spin_0,\n                                 function_output_0.data(), swsh_approx);\n    CHECK_ITERABLE_CUSTOM_APPROX(expected_derivative_1_collocation_spin_1,\n                                 function_output_1.data(), swsh_approx);\n  }\n  {\n    INFO(\"Check the single argument function interface\");\n    auto function_return = angular_derivative<DerivativeKind0, Representation>(\n        l_max, number_of_radial_points, get(db::get<TestTag<0, Spin0>>(box)));\n    CHECK_ITERABLE_CUSTOM_APPROX(function_return.data(),\n                                 expected_derivative_0_collocation_spin_0,\n                                 swsh_approx);\n  }\n}\n\ntemplate <typename InverseDerivativeKind, typename DerivativeKind,\n          ComplexRepresentation Representation, int Spin>\nvoid test_inverse_derivative() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> size_distribution{2, 7};\n  const size_t l_max = size_distribution(gen);\n  constexpr size_t number_of_radial_points = 2;\n  UniformCustomDistribution<double> coefficient_distribution{0.1, 1.0};\n\n  // fill the expected collocation point data by evaluating the analytic\n  // functions. This is very slow and imprecise (due to factorial division), but\n  // comparatively simple to formulate.\n  SpinWeighted<ComplexModalVector, Spin> generated_modes{\n      size_of_libsharp_coefficient_vector(l_max) * number_of_radial_points};\n  TestHelpers::generate_swsh_modes<Spin>(\n      make_not_null(&generated_modes.data()), make_not_null(&gen),\n      make_not_null(&coefficient_distribution), number_of_radial_points, l_max);\n\n  const auto computed_collocation =\n      inverse_swsh_transform(l_max, number_of_radial_points, generated_modes);\n  const auto expected_derivative_collocation =\n      inverse_swsh_transform(l_max, number_of_radial_points, generated_modes);\n\n  // perform inverse derivative operator\n  const auto inverse_derivative_values =\n      angular_derivative<InverseDerivativeKind, Representation>(\n          l_max, number_of_radial_points, computed_collocation);\n\n  // perform forward derivative operator\n  const auto derivative_values =\n      angular_derivative<DerivativeKind, Representation>(\n          l_max, number_of_radial_points, inverse_derivative_values);\n\n  Approx swsh_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e3)\n          .scale(1.0);\n\n  // check if original is the same as the round-trip data.\n  CHECK_ITERABLE_CUSTOM_APPROX(derivative_values,\n                               expected_derivative_collocation, swsh_approx);\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.AngularDerivatives\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  // we do not test the full set of combinations of derivatives, spins, and\n  // slice kinds due to the slow execution time. We test a handful of each spin,\n  // each derivative, and of each slice type.\n  {\n    INFO(\"Test evaluation of Eth using generated values\");\n    test_derivative_via_transforms<Tags::Eth,\n                                   ComplexRepresentation::Interleaved, -2>();\n    test_derivative_via_transforms<Tags::Eth,\n                                   ComplexRepresentation::RealsThenImags, 0>();\n  }\n  {\n    INFO(\"Test evaluation of Ethbar using generated values\");\n    test_derivative_via_transforms<Tags::Ethbar,\n                                   ComplexRepresentation::RealsThenImags, -1>();\n    test_derivative_via_transforms<Tags::Ethbar,\n                                   ComplexRepresentation::Interleaved, 1>();\n  }\n  {\n    INFO(\"Test evaluation of EthEth using generated values\");\n    test_derivative_via_transforms<Tags::EthEth,\n                                   ComplexRepresentation::Interleaved, -2>();\n    test_derivative_via_transforms<Tags::EthEth,\n                                   ComplexRepresentation::RealsThenImags, 0>();\n  }\n  {\n    INFO(\"Test evaluation of EthbarEthbar using generated values\");\n    test_derivative_via_transforms<Tags::EthbarEthbar,\n                                   ComplexRepresentation::Interleaved, 0>();\n    test_derivative_via_transforms<Tags::EthbarEthbar,\n                                   ComplexRepresentation::RealsThenImags, 2>();\n  }\n  {\n    INFO(\"Test evaluation of EthEthbar using generated values\");\n    test_derivative_via_transforms<Tags::EthEthbar,\n                                   ComplexRepresentation::Interleaved, -2>();\n    test_derivative_via_transforms<Tags::EthEthbar,\n                                   ComplexRepresentation::RealsThenImags, 0>();\n    test_derivative_via_transforms<Tags::EthEthbar,\n                                   ComplexRepresentation::Interleaved, 2>();\n  }\n  {\n    INFO(\"Test evaluation of EthbarEth using generated values\");\n    test_derivative_via_transforms<Tags::EthbarEth,\n                                   ComplexRepresentation::RealsThenImags, -1>();\n    test_derivative_via_transforms<Tags::EthbarEth,\n                                   ComplexRepresentation::Interleaved, 0>();\n    test_derivative_via_transforms<Tags::EthbarEth,\n                                   ComplexRepresentation::RealsThenImags, 1>();\n  }\n  {\n    INFO(\n        \"Test evaluation of InverseEth and InverseEthbar using generated \"\n        \"values\");\n    test_compute_angular_derivatives<ComplexRepresentation::Interleaved, -1, 1,\n                                     Tags::InverseEth, Tags::InverseEthbar>();\n    test_compute_angular_derivatives<ComplexRepresentation::RealsThenImags, -1,\n                                     1, Tags::InverseEth,\n                                     Tags::InverseEthbar>();\n  }\n  {\n    INFO(\"Test inverse derivative operator InverseEth is inverse of Eth\");\n    test_inverse_derivative<Tags::InverseEth, Tags::Eth,\n                            ComplexRepresentation::RealsThenImags, 1>();\n    test_inverse_derivative<Tags::InverseEth, Tags::Eth,\n                            ComplexRepresentation::Interleaved, 2>();\n  }\n  {\n    INFO(\"Test inverse derivative operator InverseEthbar is inverse of Ethbar\");\n    test_inverse_derivative<Tags::InverseEthbar, Tags::Ethbar,\n                            ComplexRepresentation::Interleaved, -1>();\n    test_inverse_derivative<Tags::InverseEthbar, Tags::Ethbar,\n                            ComplexRepresentation::RealsThenImags, -2>();\n  }\n  {\n    INFO(\"Test angular_derivatives utility\");\n    test_compute_angular_derivatives<ComplexRepresentation::Interleaved, -1, 1,\n                                     Tags::Eth, Tags::Ethbar>();\n    test_compute_angular_derivatives<ComplexRepresentation::RealsThenImags, -1,\n                                     1, Tags::Eth, Tags::Ethbar>();\n    test_compute_angular_derivatives<ComplexRepresentation::Interleaved, -1, 1,\n                                     Tags::EthEthbar, Tags::EthbarEth>();\n    test_compute_angular_derivatives<ComplexRepresentation::RealsThenImags, -1,\n                                     1, Tags::EthEthbar, Tags::EthbarEth>();\n    test_compute_angular_derivatives<ComplexRepresentation::Interleaved, 0, 2,\n                                     Tags::Ethbar, Tags::EthbarEthbar>();\n    test_compute_angular_derivatives<ComplexRepresentation::RealsThenImags, -2,\n                                     0, Tags::Eth, Tags::EthEth>();\n  }\n}\n}  // namespace\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_SwshFiltering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/CoefficientTransforms.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshFiltering.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\nnamespace Spectral::Swsh {\ntemplate <int Spin>\nvoid test_angular_filtering() {\n  MAKE_GENERATOR(gen);\n  // limited l_max distribution because test depends on an analytic\n  // basis function with factorials.\n  UniformCustomDistribution<size_t> sdist{7, 10};\n  const size_t l_max = sdist(gen);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n  const size_t number_of_radial_points = 2;\n  CAPTURE(l_max);\n  CAPTURE(Spin);\n  // Generate data uniform in r with all angular modes\n  SpinWeighted<ComplexModalVector, Spin> generated_modes;\n  generated_modes.data() = make_with_random_values<ComplexModalVector>(\n      make_not_null(&gen), make_not_null(&coefficient_distribution),\n      size_of_libsharp_coefficient_vector(l_max));\n  for (const auto mode : cached_coefficients_metadata(l_max)) {\n    if (mode.l < static_cast<size_t>(abs(Spin))) {\n      generated_modes.data()[mode.transform_of_real_part_offset] = 0.0;\n      generated_modes.data()[mode.transform_of_imag_part_offset] = 0.0;\n    }\n    if (mode.m == 0) {\n      generated_modes.data()[mode.transform_of_real_part_offset] =\n          real(generated_modes.data()[mode.transform_of_real_part_offset]);\n      generated_modes.data()[mode.transform_of_imag_part_offset] =\n          real(generated_modes.data()[mode.transform_of_imag_part_offset]);\n    }\n  }\n  auto pre_filter_angular_data =\n      inverse_swsh_transform(l_max, 1, generated_modes);\n  SpinWeighted<ComplexDataVector, Spin> to_filter_with_max_l{\n      create_vector_of_n_copies(pre_filter_angular_data.data(),\n                                number_of_radial_points)};\n  SpinWeighted<ComplexDataVector, Spin> to_filter_with_min_l{\n      create_vector_of_n_copies(pre_filter_angular_data.data(),\n                                number_of_radial_points)};\n  SpinWeighted<ComplexDataVector, Spin> to_filter_with_min_and_max_l{\n      create_vector_of_n_copies(pre_filter_angular_data.data(),\n                                number_of_radial_points)};\n  SpinWeighted<ComplexDataVector, Spin> angular_to_filter_with_max_l =\n      pre_filter_angular_data;\n  SpinWeighted<ComplexDataVector, Spin> angular_to_filter_with_min_l =\n      pre_filter_angular_data;\n  SpinWeighted<ComplexDataVector, Spin> angular_to_filter_with_min_and_max_l =\n      pre_filter_angular_data;\n\n  auto generated_high_modes = generated_modes;\n  // remove the top few modes, emulating the filter process\n  for (const auto mode : cached_coefficients_metadata(l_max)) {\n    if (mode.l > (l_max - 3)) {\n      generated_modes.data()[mode.transform_of_real_part_offset] = 0.0;\n      generated_modes.data()[mode.transform_of_imag_part_offset] = 0.0;\n    }\n  }\n  const auto expected_post_filter_max_angular_data =\n      inverse_swsh_transform(l_max, 1, generated_modes);\n  const SpinWeighted<ComplexDataVector, Spin> expected_post_filter_max{\n      create_vector_of_n_copies(expected_post_filter_max_angular_data.data(),\n                                number_of_radial_points)};\n\n  // remove the bottom few modes, emulating the high-pass filter process\n  for (const auto mode : cached_coefficients_metadata(l_max)) {\n    if (mode.l < 2) {\n      generated_modes.data()[mode.transform_of_real_part_offset] = 0.0;\n      generated_modes.data()[mode.transform_of_imag_part_offset] = 0.0;\n      generated_high_modes.data()[mode.transform_of_real_part_offset] = 0.0;\n      generated_high_modes.data()[mode.transform_of_imag_part_offset] = 0.0;\n    }\n  }\n  const auto expected_post_filter_min_angular_data =\n      inverse_swsh_transform(l_max, 1, generated_high_modes);\n  const SpinWeighted<ComplexDataVector, Spin> expected_post_filter_min{\n      create_vector_of_n_copies(expected_post_filter_min_angular_data.data(),\n                                number_of_radial_points)};\n  const auto expected_post_filter_both_angular_data =\n      inverse_swsh_transform(l_max, 1, generated_modes);\n  const SpinWeighted<ComplexDataVector, Spin> expected_post_filter_both{\n      create_vector_of_n_copies(expected_post_filter_both_angular_data.data(),\n                                number_of_radial_points)};\n\n  filter_swsh_volume_quantity(make_not_null(&to_filter_with_max_l), l_max,\n                              l_max - 3, 5.0, 2);\n  filter_swsh_volume_quantity(make_not_null(&to_filter_with_min_l), l_max, 2,\n                              l_max, 5.0, 2);\n  filter_swsh_volume_quantity(make_not_null(&to_filter_with_min_and_max_l),\n                              l_max, 2, l_max - 3, 5.0, 2);\n  Approx angular_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(to_filter_with_max_l.data(),\n                               expected_post_filter_max.data(), angular_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(to_filter_with_min_l.data(),\n                               expected_post_filter_min.data(), angular_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(to_filter_with_min_and_max_l.data(),\n                               expected_post_filter_both.data(),\n                               angular_approx);\n\n  filter_swsh_boundary_quantity(make_not_null(&angular_to_filter_with_max_l),\n                                l_max, l_max - 3);\n  filter_swsh_boundary_quantity(make_not_null(&angular_to_filter_with_min_l),\n                                l_max, 2, l_max);\n  filter_swsh_boundary_quantity(\n      make_not_null(&angular_to_filter_with_min_and_max_l), l_max, 2,\n      l_max - 3);\n  CHECK_ITERABLE_CUSTOM_APPROX(angular_to_filter_with_max_l.data(),\n                               expected_post_filter_max_angular_data.data(),\n                               angular_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(angular_to_filter_with_min_l.data(),\n                               expected_post_filter_min_angular_data.data(),\n                               angular_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(angular_to_filter_with_min_and_max_l.data(),\n                               expected_post_filter_both_angular_data.data(),\n                               angular_approx);\n}\n\ntemplate <int Spin>\nvoid test_radial_filtering() {\n  MAKE_GENERATOR(generator);\n  UniformCustomDistribution<size_t> sdist{2, 5};\n  const size_t l_max = sdist(generator);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n  const size_t number_of_radial_points = sdist(generator);\n  CAPTURE(l_max);\n  CAPTURE(Spin);\n  auto modes = make_with_random_values<ComplexModalVector>(\n      make_not_null(&generator), make_not_null(&coefficient_distribution),\n      number_of_radial_points);\n  SpinWeighted<ComplexDataVector, Spin> to_filter{outer_product(\n      ComplexDataVector{\n          Spectral::Swsh::number_of_swsh_collocation_points(l_max), 1.0},\n      to_nodal_coefficients(modes,\n                            Mesh<1>{{{modes.size()}},\n                                    Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto}))};\n\n  const double alpha = 10.0;\n  const size_t half_power = 4;\n  for (size_t i = 0; i < modes.size(); ++i) {\n    modes[i] *= exp(-alpha * pow(i / static_cast<double>(modes.size() - 1),\n                                 2 * half_power));\n  }\n  const SpinWeighted<ComplexDataVector, Spin> expected_post_filter{\n      outer_product(\n          ComplexDataVector{\n              Spectral::Swsh::number_of_swsh_collocation_points(l_max), 1.0},\n          to_nodal_coefficients(modes,\n                                Mesh<1>{{{modes.size()}},\n                                        Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto}))};\n\n  filter_swsh_volume_quantity(make_not_null(&to_filter), l_max, l_max, alpha,\n                              half_power);\n\n  CHECK_ITERABLE_APPROX(to_filter.data(), expected_post_filter.data());\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.SwshFiltering\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  test_angular_filtering<-1>();\n  test_angular_filtering<0>();\n  test_angular_filtering<2>();\n  test_radial_filtering<0>();\n}\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_SwshInterpolation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <complex>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshInterpolation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace Spectral::Swsh {\nnamespace {\n\ntemplate <typename Generator>\nvoid test_basis_function(const gsl::not_null<Generator*> generator) {\n  UniformCustomDistribution<double> phi_dist{0.0, 2.0 * M_PI};\n  const double phi = phi_dist(*generator);\n  UniformCustomDistribution<double> theta_dist{0.01, M_PI - 0.01};\n  const double theta = theta_dist(*generator);\n  UniformCustomDistribution<int> spin_dist{-2, 2};\n  const int spin = spin_dist(*generator);\n  UniformCustomDistribution<size_t> l_dist{static_cast<size_t>(abs(spin)), 16};\n  const size_t l = l_dist(*generator);\n  UniformCustomDistribution<int> m_dist{-static_cast<int>(l),\n                                        static_cast<int>(l)};\n  const int m = m_dist(*generator);\n\n  std::complex<double> expected = TestHelpers::spin_weighted_spherical_harmonic(\n      spin, static_cast<int>(l), m, theta, phi);\n  const auto test_harmonic = SpinWeightedSphericalHarmonic{spin, l, m};\n  std::complex<double> test = test_harmonic.evaluate(theta, phi);\n  CAPTURE(spin);\n  CAPTURE(l);\n  CAPTURE(m);\n  CAPTURE(theta);\n  CAPTURE(phi);\n\n  // need a slightly loose approx to accommodate the explicit factorials in the\n  // simpler TestHelper form\n  Approx factorial_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e4)\n          .scale(1.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(test, expected, factorial_approx);\n  const auto deserialized_test_harmonic =\n      serialize_and_deserialize(test_harmonic);\n  test = deserialized_test_harmonic.evaluate(theta, phi);\n  CHECK_ITERABLE_CUSTOM_APPROX(test, expected, factorial_approx);\n}\n\ntemplate <int spin, typename Generator>\nvoid test_interpolation(const gsl::not_null<Generator*> generator) {\n  INFO(\"Testing interpolation for spin \" << spin);\n  UniformCustomDistribution<double> coefficient_distribution{-2.0, 2.0};\n  const size_t l_max = 16;\n  UniformCustomDistribution<double> phi_dist{0.0, 2.0 * M_PI};\n  UniformCustomDistribution<double> theta_dist{0.01, M_PI - 0.01};\n  const size_t number_of_target_points = 10;\n\n  const auto target_phi = make_with_random_values<DataVector>(\n      generator, make_not_null(&phi_dist), number_of_target_points);\n  const auto target_theta = make_with_random_values<DataVector>(\n      generator, make_not_null(&theta_dist), number_of_target_points);\n\n  SpinWeighted<ComplexModalVector, spin> generated_modes{\n      size_of_libsharp_coefficient_vector(l_max)};\n  TestHelpers::generate_swsh_modes<spin>(\n      make_not_null(&generated_modes.data()), generator,\n      make_not_null(&coefficient_distribution), 1, l_max);\n\n  const auto generated_collocation =\n      inverse_swsh_transform(l_max, 1, generated_modes);\n\n  const auto goldberg_modes =\n      libsharp_to_goldberg_modes(generated_modes, l_max);\n\n  SpinWeighted<ComplexDataVector, spin> expected{number_of_target_points, 0.0};\n  SpinWeighted<ComplexDataVector, spin> another_expected{\n      number_of_target_points, 0.0};\n  auto interpolator = SwshInterpolator{target_theta, target_phi, l_max};\n  const auto deserialized_interpolator =\n      serialize_and_deserialize(interpolator);\n  for (int l = 0; l <= static_cast<int>(l_max); ++l) {\n    for (int m = -l; m <= l; ++m) {\n      auto sYlm =\n          SpinWeightedSphericalHarmonic{spin, static_cast<size_t>(l), m};\n      if (l == std::max(abs(m), abs(spin))) {\n        SpinWeighted<ComplexDataVector, spin> harmonic_test;\n        interpolator.direct_evaluation_swsh_at_l_min(\n            make_not_null(&harmonic_test), m);\n        for (size_t i = 0; i < number_of_target_points; ++i) {\n          CHECK_ITERABLE_APPROX(sYlm.evaluate(target_theta[i], target_phi[i]),\n                                harmonic_test.data()[i]);\n        }\n      }\n      if (l == std::max(abs(m), abs(spin)) + 1) {\n        SpinWeighted<ComplexDataVector, spin> harmonic_test_l_min;\n        interpolator.direct_evaluation_swsh_at_l_min(\n            make_not_null(&harmonic_test_l_min), m);\n\n        SpinWeighted<ComplexDataVector, spin> harmonic_test_l_min_plus_one;\n        interpolator.evaluate_swsh_at_l_min_plus_one(\n            make_not_null(&harmonic_test_l_min_plus_one), harmonic_test_l_min,\n            m);\n\n        for (size_t i = 0; i < number_of_target_points; ++i) {\n          CHECK_ITERABLE_APPROX(sYlm.evaluate(target_theta[i], target_phi[i]),\n                                harmonic_test_l_min_plus_one.data()[i]);\n        }\n      }\n      if (l == std::max(abs(m), abs(spin)) and abs(m) > abs(spin)) {\n        if (m > 0) {\n          SpinWeighted<ComplexDataVector, spin> harmonic_test;\n          interpolator.direct_evaluation_swsh_at_l_min(\n              make_not_null(&harmonic_test), m - 1);\n          interpolator.evaluate_swsh_m_recurrence_at_l_min(\n              make_not_null(&harmonic_test), m);\n          INFO(\"checking l=\" << l << \" m=\" << m);\n          for (size_t i = 0; i < number_of_target_points; ++i) {\n            CHECK_ITERABLE_APPROX(sYlm.evaluate(target_theta[i], target_phi[i]),\n                                  harmonic_test.data()[i]);\n          }\n\n          // check the serialization hasn't harmed the interpolator\n          deserialized_interpolator.direct_evaluation_swsh_at_l_min(\n              make_not_null(&harmonic_test), m - 1);\n          deserialized_interpolator.evaluate_swsh_m_recurrence_at_l_min(\n              make_not_null(&harmonic_test), m);\n          INFO(\"checking l=\" << l << \" m=\" << m);\n          for (size_t i = 0; i < number_of_target_points; ++i) {\n            CHECK_ITERABLE_APPROX(sYlm.evaluate(target_theta[i], target_phi[i]),\n                                  harmonic_test.data()[i]);\n          }\n\n        } else {\n          SpinWeighted<ComplexDataVector, spin> harmonic_test;\n          interpolator.direct_evaluation_swsh_at_l_min(\n              make_not_null(&harmonic_test), m + 1);\n          interpolator.evaluate_swsh_m_recurrence_at_l_min(\n              make_not_null(&harmonic_test), m);\n          INFO(\"checking l=\" << l << \" m=\" << m);\n          for (size_t i = 0; i < number_of_target_points; ++i) {\n            CHECK_ITERABLE_APPROX(sYlm.evaluate(target_theta[i], target_phi[i]),\n                                  harmonic_test.data()[i]);\n          }\n\n          // check the serialization hasn't harmed the interpolator\n          deserialized_interpolator.direct_evaluation_swsh_at_l_min(\n              make_not_null(&harmonic_test), m + 1);\n          deserialized_interpolator.evaluate_swsh_m_recurrence_at_l_min(\n              make_not_null(&harmonic_test), m);\n          INFO(\"checking l=\" << l << \" m=\" << m);\n          for (size_t i = 0; i < number_of_target_points; ++i) {\n            CHECK_ITERABLE_APPROX(sYlm.evaluate(target_theta[i], target_phi[i]),\n                                  harmonic_test.data()[i]);\n          }\n        }\n      }\n      for (size_t i = 0; i < number_of_target_points; ++i) {\n        // gcc warns about the casts in ways that are impossible to satisfy\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"\n        expected.data()[i] +=\n            goldberg_modes.data()[square(static_cast<size_t>(l)) +\n                                  static_cast<size_t>(l + m)] *\n            TestHelpers::spin_weighted_spherical_harmonic(\n                spin, l, m, target_theta[i], target_phi[i]);\n        another_expected.data()[i] +=\n            goldberg_modes.data()[square(static_cast<size_t>(l)) +\n                                  static_cast<size_t>(l + m)] *\n            sYlm.evaluate(target_theta[i], target_phi[i]);\n      }\n    }\n  }\n\n  Approx factorial_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e5)\n          .scale(1.0);\n  // direct test Clenshaw sums\n  for (int m = -static_cast<int>(l_max); m <= static_cast<int>(l_max); ++m) {\n    SpinWeighted<ComplexDataVector, spin> expected_clenshaw_sum{\n        number_of_target_points, 0.0};\n    for (int l = std::max(abs(m), abs(spin)); l <= static_cast<int>(l_max);\n         ++l) {\n      auto sYlm =\n          SpinWeightedSphericalHarmonic{spin, static_cast<size_t>(l), m};\n      for (size_t i = 0; i < number_of_target_points; ++i) {\n        expected_clenshaw_sum.data()[i] +=\n            goldberg_modes.data()[square(static_cast<size_t>(l)) +\n                                  static_cast<size_t>(l + m)] *\n            sYlm.evaluate(target_theta[i], target_phi[i]);\n      }\n    }\n#pragma GCC diagnostic pop\n    SpinWeighted<ComplexDataVector, spin> clenshaw{number_of_target_points,\n                                                   0.0};\n\n    SpinWeighted<ComplexDataVector, spin> harmonic_test_l_min;\n    interpolator.direct_evaluation_swsh_at_l_min(\n        make_not_null(&harmonic_test_l_min), m);\n\n    SpinWeighted<ComplexDataVector, spin> harmonic_test_l_min_plus_one;\n    interpolator.evaluate_swsh_at_l_min_plus_one(\n        make_not_null(&harmonic_test_l_min_plus_one), harmonic_test_l_min, m);\n\n    interpolator.clenshaw_sum(\n        make_not_null(&clenshaw), harmonic_test_l_min,\n        harmonic_test_l_min_plus_one,\n        libsharp_to_goldberg_modes(\n            swsh_transform(l_max, 1, generated_collocation), l_max),\n        m);\n    INFO(\"checking clenshaw sum for m=\" << m);\n    for (size_t i = 0; i < number_of_target_points; ++i) {\n      CHECK_ITERABLE_CUSTOM_APPROX(clenshaw.data()[i],\n                                   expected_clenshaw_sum.data()[i],\n                                   factorial_approx);\n    }\n  }\n\n  SpinWeighted<ComplexDataVector, spin> clenshaw_interpolation;\n  interpolator.interpolate(\n      make_not_null(&clenshaw_interpolation),\n      libsharp_to_goldberg_modes(\n          swsh_transform(l_max, 1, generated_collocation), l_max));\n\n  CHECK_ITERABLE_CUSTOM_APPROX(expected, another_expected, factorial_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(clenshaw_interpolation, expected,\n                               factorial_approx);\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.SwshInterpolation\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  MAKE_GENERATOR(generator);\n  // test a few points on each run of the test, these are cheap.\n  for (size_t i = 0; i < 10; ++i) {\n    test_basis_function(make_not_null(&generator));\n  }\n\n  test_interpolation<-1>(make_not_null(&generator));\n  test_interpolation<0>(make_not_null(&generator));\n  test_interpolation<2>(make_not_null(&generator));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(([]() {\n                      SwshInterpolator interp{};\n                      SpinWeighted<ComplexDataVector, 1> interp_source{\n                          number_of_swsh_collocation_points(5_st)};\n                      SpinWeighted<ComplexDataVector, 1> interp_target{\n                          number_of_swsh_collocation_points(5_st)};\n                      interp.interpolate(make_not_null(&interp_target),\n                                         interp_source);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Attempting to perform interpolation\"));\n  CHECK_THROWS_WITH(([]() {\n                      SwshInterpolator interp{};\n                      SpinWeighted<ComplexDataVector, 1> interp_target{\n                          number_of_swsh_collocation_points(5_st)};\n                      interp.direct_evaluation_swsh_at_l_min(\n                          make_not_null(&interp_target), 1);\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Attempting to perform spin-weighted evaluation\"));\n#endif\n}\n}  // namespace\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_SwshTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Cce/OptionTags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nclass ComplexDataVector;\nclass ComplexModalVector;\n\nnamespace Spectral::Swsh::Tags {\nnamespace {\n\nstruct UnweightedTestTag : db::SimpleTag {\n  using type = Scalar<ComplexDataVector>;\n};\n\nstruct SpinMinus1TestTag : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n};\n\nstruct AnotherSpinMinus1TestTag : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, -1>>;\n};\n\nstruct Spin2TestTag : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, 2>>;\n};\n\nstatic_assert(Derivative<Spin2TestTag, Ethbar>::spin == 1,\n              \"failed testing DerivativeTag with DerivativeType Ethbar\");\n\nstatic_assert(\n    std::is_same_v<Derivative<SpinMinus1TestTag, Ethbar>::derivative_of,\n                   SpinMinus1TestTag>,\n    \"failed testing DerivativeTag with DerivativeType Ethbar\");\n\nstatic_assert(std::is_same_v<SwshTransform<Spin2TestTag>::type,\n                             Scalar<SpinWeighted<ComplexModalVector, 2>>>,\n              \"failed testing SwshTransform\");\n\nstatic_assert(\n    std::is_same_v<\n        SwshTransform<Derivative<Spin2TestTag, EthEthbar>>::transform_of,\n        Derivative<Spin2TestTag, EthEthbar>>,\n    \"failed testing SwshTransform\");\n\nnamespace test_spins_in_tag_list {\n// [spins_in_tag_list]\nusing TestVarTagList = tmpl::list<SpinMinus1TestTag, SpinMinus1TestTag,\n                                  Spin2TestTag, AnotherSpinMinus1TestTag>;\n\nstatic_assert(std::is_same_v<spins_in_tag_list<TestVarTagList>,\n                             tmpl::list<std::integral_constant<int, -1>,\n                                        std::integral_constant<int, 2>>>);\n\nusing TestDerivativeTagList =\n    tmpl::list<Derivative<SpinMinus1TestTag, Eth>,\n               Derivative<SpinMinus1TestTag, EthEthbar>,\n               Derivative<AnotherSpinMinus1TestTag, EthEth>,\n               Derivative<Spin2TestTag, Ethbar>>;\n\nstatic_assert(std::is_same_v<spins_in_tag_list<TestDerivativeTagList>,\n                             tmpl::list<std::integral_constant<int, -1>,\n                                        std::integral_constant<int, 0>,\n                                        std::integral_constant<int, 1>>>);\n// [spins_in_tag_list]\n}  // namespace test_spins_in_tag_list\n\nnamespace test_partition_tags_by_spin {\n// [partition_tags_by_spin]\nusing TestVarTagList = tmpl::list<SpinMinus1TestTag, SpinMinus1TestTag,\n                                  Spin2TestTag, AnotherSpinMinus1TestTag>;\n\nstatic_assert(\n    std::is_same_v<partition_tags_by_spin<TestVarTagList>,\n                   tmpl::list<tmpl::list<SpinMinus1TestTag, SpinMinus1TestTag,\n                                         AnotherSpinMinus1TestTag>,\n                              tmpl::list<Spin2TestTag>>>);\n\nusing TestDerivativeTagList =\n    tmpl::list<Derivative<SpinMinus1TestTag, Eth>,\n               Derivative<SpinMinus1TestTag, EthEthbar>,\n               Derivative<AnotherSpinMinus1TestTag, EthEth>,\n               Derivative<Spin2TestTag, Ethbar>>;\n\nstatic_assert(\n    std::is_same_v<\n        partition_tags_by_spin<TestDerivativeTagList>,\n        tmpl::list<tmpl::list<Derivative<SpinMinus1TestTag, EthEthbar>>,\n                   tmpl::list<Derivative<SpinMinus1TestTag, Eth>>,\n                   tmpl::list<Derivative<AnotherSpinMinus1TestTag, EthEth>,\n                              Derivative<Spin2TestTag, Ethbar>>>>);\n// [partition_tags_by_spin]\n}  // namespace test_partition_tags_by_spin\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.Tags\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  TestHelpers::db::test_prefix_tag<Derivative<SpinMinus1TestTag, Eth>>(\n      \"Eth(SpinMinus1TestTag)\");\n  TestHelpers::db::test_prefix_tag<Derivative<SpinMinus1TestTag, EthEth>>(\n      \"EthEth(SpinMinus1TestTag)\");\n  TestHelpers::db::test_prefix_tag<Derivative<SpinMinus1TestTag, EthEthbar>>(\n      \"EthEthbar(SpinMinus1TestTag)\");\n  TestHelpers::db::test_prefix_tag<Derivative<SpinMinus1TestTag, Ethbar>>(\n      \"Ethbar(SpinMinus1TestTag)\");\n  TestHelpers::db::test_prefix_tag<Derivative<SpinMinus1TestTag, EthbarEth>>(\n      \"EthbarEth(SpinMinus1TestTag)\");\n  TestHelpers::db::test_prefix_tag<Derivative<SpinMinus1TestTag, EthbarEthbar>>(\n      \"EthbarEthbar(SpinMinus1TestTag)\");\n  TestHelpers::db::test_prefix_tag<Derivative<SpinMinus1TestTag, NoDerivative>>(\n      \"NoDerivative(SpinMinus1TestTag)\");\n  TestHelpers::db::test_prefix_tag<SwshTransform<Spin2TestTag>>(\n      \"SwshTransform(Spin2TestTag)\");\n  TestHelpers::db::test_prefix_tag<SwshInterpolator<Spin2TestTag>>(\n      \"SwshInterpolator(Spin2TestTag)\");\n  TestHelpers::db::test_simple_tag<LMax>(\"LMax\");\n  TestHelpers::db::test_simple_tag<NumberOfRadialPoints>(\n      \"NumberOfRadialPoints\");\n  CHECK(TestHelpers::test_option_tag<OptionTags::LMax>(\"8\") == 8_st);\n  CHECK(TestHelpers::test_option_tag<OptionTags::NumberOfRadialPoints>(\"3\") ==\n        3_st);\n}\n}  // namespace\n}  // namespace Spectral::Swsh::Tags\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_SwshTestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <random>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Math.hpp\"\n\nnamespace Spectral {\nnamespace Swsh {\nnamespace TestHelpers {\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.SwshTestHelpers\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<double> theta_dist{-M_PI / 2.0, M_PI / 2.0};\n  UniformCustomDistribution<double> phi_dist{0, 2.0 * M_PI};\n  UniformCustomDistribution<size_t> factorial_dist{0, 20};\n\n  size_t test_factorial_value = factorial_dist(gen);\n  CHECK(approx(factorial(test_factorial_value)) ==\n        static_cast<double>(::factorial(test_factorial_value)));\n\n  const double theta_point = theta_dist(gen);\n  const double phi_point = phi_dist(gen);\n  const std::complex<double> expected_swsh_02m2 =\n      sqrt(15.0 / (32.0 * M_PI)) * sin(theta_point) * sin(theta_point) *\n      (std::complex<double>(cos(-2.0 * phi_point), sin(-2.0 * phi_point)));\n\n  const std::complex<double> expected_swsh_110 =\n      sqrt(3.0 / (8.0 * M_PI)) * sin(theta_point);\n\n  const std::complex<double> expected_swsh_111 =\n      -sqrt(3.0 / (16.0 * M_PI)) * (1 - cos(theta_point)) *\n      (std::complex<double>(cos(phi_point), sin(phi_point)));\n\n  CHECK(real(spin_weighted_spherical_harmonic(0, 2, -2, theta_point,\n                                              phi_point)) ==\n        approx(real(expected_swsh_02m2)));\n  CHECK(imag(spin_weighted_spherical_harmonic(0, 2, -2, theta_point,\n                                              phi_point)) ==\n        approx(imag(expected_swsh_02m2)));\n\n  CHECK(\n      real(spin_weighted_spherical_harmonic(1, 1, 0, theta_point, phi_point)) ==\n      approx(real(expected_swsh_110)));\n  CHECK(\n      imag(spin_weighted_spherical_harmonic(1, 1, 0, theta_point, phi_point)) ==\n      approx(imag(expected_swsh_110)));\n\n  CHECK(\n      real(spin_weighted_spherical_harmonic(1, 1, 1, theta_point, phi_point)) ==\n      approx(real(expected_swsh_111)));\n  CHECK(\n      imag(spin_weighted_spherical_harmonic(1, 1, 1, theta_point, phi_point)) ==\n      approx(imag(expected_swsh_111)));\n}\n}  // namespace TestHelpers\n}  // namespace Swsh\n}  // namespace Spectral\n"
  },
  {
    "path": "tests/Unit/NumericalAlgorithms/SpinWeightedSphericalHarmonics/Test_SwshTransform.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <string>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/ComplexModalVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/SpinWeighted.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/ComplexDataView.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCoefficients.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshCollocation.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTags.hpp\"\n#include \"NumericalAlgorithms/SpinWeightedSphericalHarmonics/SwshTransform.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace Spectral::Swsh {\nnamespace {\n\n// for storing a computed spin-weighted value during the transform test\ntemplate <size_t index, int Spin>\nstruct TestTag : db::SimpleTag {\n  using type = Scalar<SpinWeighted<ComplexDataVector, Spin>>;\n};\n\nusing TestDerivativeTagList =\n    tmpl::list<Tags::Derivative<TestTag<0, -1>, Tags::Eth>,\n               Tags::Derivative<TestTag<0, -1>, Tags::EthEthbar>,\n               Tags::Derivative<TestTag<1, -1>, Tags::EthEthbar>,\n               Tags::Derivative<TestTag<0, 2>, Tags::EthEth>>;\n\n// [make_transform_list]\nusing ExpectedInverseTransforms = tmpl::list<\n    SwshTransform<tmpl::list<Tags::Derivative<TestTag<0, -1>, Tags::EthEthbar>,\n                             Tags::Derivative<TestTag<1, -1>, Tags::EthEthbar>>,\n                  ComplexRepresentation::RealsThenImags>,\n    SwshTransform<tmpl::list<Tags::Derivative<TestTag<0, -1>, Tags::Eth>>,\n                  ComplexRepresentation::RealsThenImags>,\n    SwshTransform<tmpl::list<Tags::Derivative<TestTag<0, 2>, Tags::EthEth>>,\n                  ComplexRepresentation::RealsThenImags>>;\n\nstatic_assert(\n    std::is_same_v<make_transform_list<ComplexRepresentation::RealsThenImags,\n                                       TestDerivativeTagList>,\n                   ExpectedInverseTransforms>,\n    \"failed testing make_transform_list\");\n// [make_transform_list]\n\n// [make_transform_from_derivative_tags]\nusing ExpectedTransforms =\n    tmpl::list<SwshTransform<tmpl::list<TestTag<0, -1>, TestTag<1, -1>>,\n                             ComplexRepresentation::Interleaved>,\n               SwshTransform<tmpl::list<TestTag<0, 2>>,\n                             ComplexRepresentation::Interleaved>>;\n\nstatic_assert(std::is_same_v<make_transform_list_from_derivative_tags<\n                                 ComplexRepresentation::Interleaved,\n                                 TestDerivativeTagList>,\n                             ExpectedTransforms>,\n              \"failed testing make_transform_list_from_derivative_tags\");\n// [make_transform_from_derivative_tags]\n\ntemplate <ComplexRepresentation Representation, int S>\nvoid test_transform_and_inverse_transform() {\n  // generate parameters for the points to transform\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{2, 7};\n  const size_t l_max = sdist(gen);\n  const size_t number_of_radial_points = 2;\n  UniformCustomDistribution<double> coefficient_distribution{-10.0, 10.0};\n\n  // A DataBox of two tags to transform and their spin-weighted transforms, to\n  // verify the DataBox-compatible mutate interface\n  using collocation_variables_tag =\n      ::Tags::Variables<tmpl::list<TestTag<0, S>, TestTag<1, S>>>;\n  using coefficients_variables_tag =\n      ::Tags::Variables<tmpl::list<Tags::SwshTransform<TestTag<0, S>>,\n                                   Tags::SwshTransform<TestTag<1, S>>>>;\n  auto box = db::create<\n      db::AddSimpleTags<collocation_variables_tag, coefficients_variables_tag,\n                        Tags::LMax, Tags::NumberOfRadialPoints>,\n      db::AddComputeTags<>>(\n      typename collocation_variables_tag::type{\n          number_of_radial_points * number_of_swsh_collocation_points(l_max)},\n      typename coefficients_variables_tag::type{\n          size_of_libsharp_coefficient_vector(l_max) * number_of_radial_points},\n      l_max, number_of_radial_points);\n\n  ComplexModalVector expected_modes{number_of_radial_points *\n                                    size_of_libsharp_coefficient_vector(l_max)};\n  TestHelpers::generate_swsh_modes<S>(\n      make_not_null(&expected_modes), make_not_null(&gen),\n      make_not_null(&coefficient_distribution), number_of_radial_points, l_max);\n  ComplexModalVector two_times_expected_modes = 2.0 * expected_modes;\n\n  // fill the expected collocation point data by evaluating the analytic\n  // functions. This is very slow and rough (due to factorial division), but\n  // comparatively simple to formulate.\n  const auto coefficients_to_analytic_collocation =\n      [&l_max](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, S>>*>\n                   computed_collocation,\n               ComplexModalVector& modes) {\n        TestHelpers::swsh_collocation_from_coefficients_and_basis_func<\n            S, Representation>(\n            make_not_null(&get(*computed_collocation).data()), modes, l_max,\n            number_of_radial_points,\n            TestHelpers::spin_weighted_spherical_harmonic);\n      };\n  db::mutate<TestTag<0, S>>(coefficients_to_analytic_collocation,\n                            make_not_null(&box), expected_modes);\n  db::mutate<TestTag<1, S>>(coefficients_to_analytic_collocation,\n                            make_not_null(&box), two_times_expected_modes);\n\n  const auto source_collocation_copy = get(db::get<TestTag<0, S>>(box));\n  // transform using the DataBox mutate interface\n  db::mutate_apply<\n      SwshTransform<tmpl::list<TestTag<0, S>, TestTag<1, S>>, Representation>>(\n      make_not_null(&box));\n\n  // verify that the collocation points haven't been altered by the\n  // transformation\n  CHECK(source_collocation_copy == get(db::get<TestTag<0, S>>(box)));\n\n  // approximation needs to be a little loose to consistently accommodate\n  // the ratios of factorials in the analytic form\n  Approx transform_approx =\n      Approx::custom()\n          .epsilon(std::numeric_limits<double>::epsilon() * 1.0e6)\n          .scale(1.0);\n\n  // check transformed modes against the generated ones\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(db::get<Tags::SwshTransform<TestTag<0, S>>>(box)).data(),\n      expected_modes, transform_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(db::get<Tags::SwshTransform<TestTag<1, S>>>(box)).data(),\n      2.0 * expected_modes, transform_approx);\n\n  // check the function interface which transforms a single spin-weighted vector\n  // and returns the result\n  SpinWeighted<ComplexModalVector, S> transformed_modes_from_function_call =\n      swsh_transform<Representation>(l_max, number_of_radial_points,\n                                     get(db::get<TestTag<0, S>>(box)));\n  CHECK_ITERABLE_CUSTOM_APPROX(transformed_modes_from_function_call.data(),\n                               expected_modes, transform_approx);\n\n  // check the parameter-pack return by pointer function interface, which takes\n  // an arbitrary number of spin-weighted vectors to transform.\n  SpinWeighted<ComplexModalVector, S>\n      two_times_transformed_modes_from_function_call;\n  swsh_transform<Representation>(\n      l_max, number_of_radial_points,\n      make_not_null(&transformed_modes_from_function_call),\n      make_not_null(&two_times_transformed_modes_from_function_call),\n      get(db::get<TestTag<0, S>>(box)), get(db::get<TestTag<1, S>>(box)));\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      two_times_transformed_modes_from_function_call.data(),\n      2.0 * expected_modes, transform_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(transformed_modes_from_function_call.data(),\n                               expected_modes, transform_approx);\n\n  ComplexDataVector expected_collocation =\n      get(db::get<TestTag<0, S>>(box)).data();\n\n  // clear out the existing collocation data so we know we get the\n  // correct inverse transform\n  db::mutate<TestTag<0, S>, TestTag<1, S>>(\n      [](const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, S>>*>\n             collocation,\n         const gsl::not_null<Scalar<SpinWeighted<ComplexDataVector, S>>*>\n             another_collocation) {\n        get(*collocation).data() = 0.0;\n        get(*another_collocation).data() = 0.0;\n      },\n      make_not_null(&box));\n\n  // transform using the DataBox mutate interface\n  db::mutate_apply<InverseSwshTransform<\n      tmpl::list<TestTag<0, S>, TestTag<1, S>>, Representation>>(\n      make_not_null(&box));\n\n  CHECK_ITERABLE_CUSTOM_APPROX(get(db::get<TestTag<0, S>>(box)).data(),\n                               expected_collocation, transform_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(db::get<TestTag<1, S>>(box)).data(),\n                               2.0 * expected_collocation, transform_approx);\n\n  // check the function interface which transforms a single spin-weighted vector\n  // and returns the result\n  SpinWeighted<ComplexDataVector, S>\n      transformed_collocation_from_function_call =\n          inverse_swsh_transform<Representation>(\n              l_max, number_of_radial_points,\n              get(db::get<Tags::SwshTransform<TestTag<0, S>>>(box)));\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      transformed_collocation_from_function_call.data(), expected_collocation,\n      transform_approx);\n\n  // check the parameter-pack return by pointer function interface, which takes\n  // an arbitrary number of spin-weighted vectors to transform.\n  SpinWeighted<ComplexDataVector, S>\n      two_times_transformed_collocation_from_function_call;\n  inverse_swsh_transform<Representation>(\n      l_max, number_of_radial_points,\n      make_not_null(&transformed_collocation_from_function_call),\n      make_not_null(&two_times_transformed_collocation_from_function_call),\n      get(db::get<Tags::SwshTransform<TestTag<0, S>>>(box)),\n      get(db::get<Tags::SwshTransform<TestTag<1, S>>>(box)));\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      two_times_transformed_collocation_from_function_call.data(),\n      2.0 * expected_collocation, transform_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      transformed_collocation_from_function_call.data(), expected_collocation,\n      transform_approx);\n}\n\ntemplate <int Spin>\nvoid test_interpolate_to_collocation() {\n  // generate parameters for the points to transform\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> sdist{2, 10};\n  const size_t l_max = sdist(gen);\n  const size_t number_of_radial_points = 2;\n  UniformCustomDistribution<double> coefficient_distribution{0.1, 1.0};\n\n  SpinWeighted<ComplexModalVector, Spin> expected_modes{\n      number_of_radial_points * size_of_libsharp_coefficient_vector(l_max)};\n  TestHelpers::generate_swsh_modes<Spin>(\n      make_not_null(&expected_modes.data()), make_not_null(&gen),\n      make_not_null(&coefficient_distribution), number_of_radial_points, l_max);\n\n  const SpinWeighted<ComplexDataVector, Spin> collocation_data =\n      inverse_swsh_transform(l_max, number_of_radial_points, expected_modes);\n  const size_t target_l_max = sdist(gen);\n\n  SpinWeighted<ComplexDataVector, Spin> resampled_collocation{\n      number_of_swsh_collocation_points(target_l_max) *\n      number_of_radial_points};\n  interpolate_to_collocation(make_not_null(&resampled_collocation),\n                             collocation_data, target_l_max, l_max,\n                             number_of_radial_points);\n  const auto resampled_goldberg_modes = libsharp_to_goldberg_modes(\n      swsh_transform(target_l_max, number_of_radial_points,\n                     resampled_collocation),\n      target_l_max);\n  const auto expected_goldberg_modes =\n      libsharp_to_goldberg_modes(expected_modes, l_max);\n  for (size_t i = 0; i < number_of_radial_points; ++i) {\n    for (size_t j = 0; j < square(std::min(l_max, target_l_max) + 1); ++j) {\n      CAPTURE(i);\n      CAPTURE(j);\n      CHECK_ITERABLE_APPROX(\n          expected_goldberg_modes.data()[i * square(l_max + 1) + j],\n          resampled_goldberg_modes.data()[i * square(target_l_max + 1) + j]);\n    }\n  }\n  if (target_l_max > l_max) {\n    for (size_t i = 0; i < number_of_radial_points; ++i) {\n      for (size_t j = square(l_max + 1); j < square(target_l_max + 1); ++j) {\n        CAPTURE(i);\n        CAPTURE(j);\n        CHECK(approx(real(resampled_goldberg_modes\n                              .data()[i * square(target_l_max + 1) + j])) ==\n              0.0);\n        CHECK(approx(imag(resampled_goldberg_modes\n                              .data()[i * square(target_l_max + 1) + j])) ==\n              0.0);\n      }\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Spectral.SwshTransform\",\n                  \"[Unit][NumericalAlgorithms]\") {\n  {\n    INFO(\"Testing with ComplexRepresentation::Interleaved\");\n    test_transform_and_inverse_transform<ComplexRepresentation::Interleaved,\n                                         -2>();\n    test_transform_and_inverse_transform<ComplexRepresentation::Interleaved,\n                                         0>();\n    test_transform_and_inverse_transform<ComplexRepresentation::Interleaved,\n                                         1>();\n  }\n  {\n    INFO(\"Testing with ComplexRepresentation::RealsThenImags\");\n    test_transform_and_inverse_transform<ComplexRepresentation::RealsThenImags,\n                                         -1>();\n    test_transform_and_inverse_transform<ComplexRepresentation::RealsThenImags,\n                                         0>();\n    test_transform_and_inverse_transform<ComplexRepresentation::RealsThenImags,\n                                         2>();\n  }\n  {\n    INFO(\"Testing interpolate_to_collocation\");\n    test_interpolate_to_collocation<2>();\n    test_interpolate_to_collocation<0>();\n    test_interpolate_to_collocation<-1>();\n  }\n}\n}  // namespace\n}  // namespace Spectral::Swsh\n"
  },
  {
    "path": "tests/Unit/Options/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Options\")\n\nset(LIBRARY_SOURCES\n  Test_Auto.cpp\n  Test_Comparator.cpp\n  Test_CustomTypeConstruction.cpp\n  Test_Factory.cpp\n  Test_FactoryHelpers.cpp\n  Test_Options.cpp\n  Test_StdComplex.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Options\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/Options/Test_Auto.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Derived;\n\nstruct Base {\n  Base() = default;\n  Base(const Base&) = default;\n  Base& operator=(Base&&) = default;\n  Base(Base&&) = default;\n  Base& operator=(const Base&) = default;\n  virtual ~Base() = default;\n  using creatable_classes = tmpl::list<Derived>;\n};\n\nstruct Derived : Base {\n  using options = tmpl::list<>;\n  static constexpr Options::String help = \"halp\";\n};\n\ntemplate <typename T>\nvoid test_instance(Options::Auto<T> value, const std::optional<T> expected) {\n  CHECK(static_cast<const std::optional<T>&>(value) == expected);\n  CHECK(static_cast<std::optional<T>>(std::move(value)) == expected);\n}\n\nvoid test_class() {\n  test_instance(Options::Auto<int>{}, std::optional<int>{});\n  test_instance(Options::Auto<int>{3}, std::optional<int>{3});\n  test_instance(Options::Auto<std::unique_ptr<int>>{},\n                std::optional<std::unique_ptr<int>>{});\n  {\n    Options::Auto<std::unique_ptr<int>> value{std::make_unique<int>(3)};\n    const std::optional<std::unique_ptr<int>>& contained = value;\n    CHECK((contained and *contained and **contained == 3));\n    const std::optional<std::unique_ptr<int>> extracted = std::move(value);\n    CHECK((extracted and *extracted and **extracted == 3));\n  }\n\n  {\n    struct A {};\n    struct B {\n      // NOLINTNEXTLINE(google-explicit-constructor)\n      B(A /*unused*/) {}\n    };\n\n    // Test that converting the contained class compiles.\n    (void)static_cast<std::optional<B>>(Options::Auto<A>{});\n\n    // Similarly, but for the specific case we care about most.\n    (void)static_cast<std::optional<std::unique_ptr<Base>>>(\n        Options::Auto<std::unique_ptr<Derived>>{});\n  }\n\n  CHECK(Options::Auto<int>{} == Options::Auto<int>{});\n  CHECK_FALSE(Options::Auto<int>{} != Options::Auto<int>{});\n  CHECK(Options::Auto<int>{3} == Options::Auto<int>{3});\n  CHECK_FALSE(Options::Auto<int>{3} != Options::Auto<int>{3});\n  CHECK_FALSE(Options::Auto<int>{} == Options::Auto<int>{3});\n  CHECK(Options::Auto<int>{} != Options::Auto<int>{3});\n  CHECK_FALSE(Options::Auto<int>{3} == Options::Auto<int>{4});\n  CHECK(Options::Auto<int>{3} != Options::Auto<int>{4});\n\n  CHECK(get_output(Options::Auto<int>{}) == \"Auto\");\n  CHECK(get_output(Options::Auto<int>{3}) == \"3\");\n  CHECK(get_output(Options::Auto<std::vector<int>>{{1, 2}}) ==\n        get_output(std::vector<int>{1, 2}));\n}\n\ntemplate <typename T>\nvoid check_create(const std::string& creation_string,\n                  const std::optional<T>& expected) {\n  CAPTURE(creation_string);\n  CHECK(static_cast<std::optional<T>>(\n            TestHelpers::test_creation<Options::Auto<T>>(creation_string)) ==\n        expected);\n}\n\nvoid test_parsing() {\n  check_create<int>(\"3\", 3);\n  check_create<int>(\"Auto\", {});\n\n  check_create<std::string>(\"Auto\", {});\n  check_create<std::string>(\"Not Auto\", \"Not Auto\");\n\n  check_create<std::unique_ptr<Base>>(\"Auto\", {});\n  {\n    const std::optional<std::unique_ptr<Base>> created =\n        TestHelpers::test_creation<Options::Auto<std::unique_ptr<Base>>>(\n            \"Derived\");\n    CHECK(created);\n    CHECK(*created);\n  }\n\n  check_create<std::vector<int>>(\"Auto\", {});\n  check_create<std::vector<int>>(\"\", std::vector<int>{});\n  check_create<std::vector<int>>(\"[1, 2, 3]\", std::vector<int>{1, 2, 3});\n}\n\n// [example_class]\nclass ExampleClass {\n public:\n  ExampleClass() = default;\n\n  struct AutoArg {\n    using type = Options::Auto<int>;\n    static type suggested_value() { return {}; }\n    static constexpr Options::String help =\n        \"Integer that can be automatically chosen\";\n  };\n  struct OptionalArg {\n    using type = Options::Auto<double, Options::AutoLabel::None>;\n    static constexpr Options::String help = \"Optional parameter\";\n  };\n  struct AllArg {\n    using type = Options::Auto<std::vector<int>, Options::AutoLabel::All>;\n    static constexpr Options::String help = \"Optional parameter all\";\n  };\n\n  static constexpr Options::String help =\n      \"A class that can automatically choose an argument\";\n  using options = tmpl::list<AutoArg, OptionalArg, AllArg>;\n\n  explicit ExampleClass(std::optional<int> auto_arg,\n                        std::optional<double> opt_arg,\n                        std::optional<std::vector<int>> all_arg)\n      : value(auto_arg ? *auto_arg : -12),\n        optional_value(opt_arg),\n        all_value(all_arg) {}\n\n  int value{};\n  std::optional<double> optional_value{};\n  std::optional<std::vector<int>> all_value{};\n};\n// [example_class]\n\nclass NonCopyableArgument {\n public:\n  NonCopyableArgument() = default;\n\n  struct AutoArg {\n    using type = Options::Auto<std::unique_ptr<Base>>;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  static constexpr Options::String help = \"halp\";\n  using options = tmpl::list<AutoArg>;\n\n  explicit NonCopyableArgument(\n      std::optional<std::unique_ptr<Base>> /*auto_arg*/) {}\n};\n\nvoid test_use_as_option() {\n  // [example_create]\n  const auto example1 = TestHelpers::test_creation<ExampleClass>(\n      \"AutoArg: 7\\n\"\n      \"OptionalArg: 10.\\n\"\n      \"AllArg: [0, 1, 2]\");\n  CHECK(example1.value == 7);\n  CHECK(example1.optional_value == 10.);\n  CHECK(example1.all_value == std::vector<int>{{0, 1, 2}});\n  const auto example2 = TestHelpers::test_creation<ExampleClass>(\n      \"AutoArg: Auto\\n\"\n      \"OptionalArg: None\\n\"\n      \"AllArg: [0, 1, 2]\");\n  CHECK(example2.value == -12);\n  CHECK(example2.optional_value == std::nullopt);\n  CHECK(example2.all_value == std::vector<int>{{0, 1, 2}});\n  const auto example3 = TestHelpers::test_creation<ExampleClass>(\n      \"AutoArg: 7\\n\"\n      \"OptionalArg: 10.\\n\"\n      \"AllArg: All\");\n  CHECK(example3.value == 7);\n  CHECK(example3.optional_value == 10.);\n  CHECK(example3.all_value == std::nullopt);\n  // [example_create]\n\n  // Make sure this compiles.\n  TestHelpers::test_creation<NonCopyableArgument>(\"AutoArg: Auto\");\n}\n\nstruct LabelThatShouldPrint {\n  static std::string name() { return \"PrintedName\"; }\n};\n\nvoid test_error_message() {\n  CHECK_THROWS_WITH(\n      (TestHelpers::test_creation<Options::Auto<double, LabelThatShouldPrint>>(\n          \"NotValid\")),\n      Catch::Matchers::ContainsSubstring(\"PrintedName\") and\n          Catch::Matchers::ContainsSubstring(\"to type double\") and\n          Catch::Matchers::ContainsSubstring(\"NotValid\"));\n  CHECK_THROWS_WITH((TestHelpers::test_creation<\n                        Options::Auto<std::string, LabelThatShouldPrint>>(\n                        \"[not, a, string]\")),\n                    Catch::Matchers::ContainsSubstring(\"PrintedName\") and\n                        Catch::Matchers::ContainsSubstring(\"to type string\") and\n                        Catch::Matchers::ContainsSubstring(\"not, a, string\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Options.Auto\", \"[Unit][Options]\") {\n  test_class();\n  test_parsing();\n  test_use_as_option();\n  test_error_message();\n}\n"
  },
  {
    "path": "tests/Unit/Options/Test_Comparator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Comparator.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid check(const std::string& name, const bool expected_less,\n           const bool expected_equal, const bool expected_greater) {\n  const auto comparator = TestHelpers::test_creation<Options::Comparator>(name);\n  CHECK(comparator(2.0, 3.0) == expected_less);\n  CHECK(comparator(2.0, 2.0) == expected_equal);\n  CHECK(comparator(2.0, 1.0) == expected_greater);\n\n  const auto copy = serialize_and_deserialize(comparator);\n  CHECK(copy(2.0, 3.0) == expected_less);\n  CHECK(copy(2.0, 2.0) == expected_equal);\n  CHECK(copy(2.0, 1.0) == expected_greater);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Options.Comparator\", \"[Unit][Options]\") {\n  check(\"EqualTo\", false, true, false);\n  check(\"NotEqualTo\", true, false, true);\n  check(\"LessThan\", true, false, false);\n  check(\"GreaterThan\", false, false, true);\n  check(\"LessThanOrEqualTo\", true, true, false);\n  check(\"GreaterThanOrEqualTo\", false, true, true);\n}\n"
  },
  {
    "path": "tests/Unit/Options/Test_CustomTypeConstruction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <utility>\n\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// [class_creation_example]\nclass CreateFromOptions {\n public:\n  struct CfoOption {\n    using type = std::string;\n    static constexpr Options::String help = {\"Option help text\"};\n  };\n\n  static constexpr Options::String help = {\"Class help text\"};\n  using options = tmpl::list<CfoOption>;\n\n  CreateFromOptions() = default;\n  // The Options::Context argument can be left off if unneeded.\n  explicit CreateFromOptions(std::string str,\n                             const Options::Context& context = {})\n      : str_(std::move(str)) {\n    if (str_[0] != 'f') {\n      PARSE_ERROR(context,\n                  \"Option must start with an 'f' but is '\" << str_ << \"'\");\n    }\n  }\n\n  std::string str_{};\n};\n\nstruct Cfo {\n  using type = CreateFromOptions;\n  static constexpr Options::String help = {\"help\"};\n};\n\nconst char* const input_file_text = R\"(\nCfo:\n  CfoOption: foo\n)\";\n// [class_creation_example]\n\n// [class_creation_example_with_metavariables]\nclass CreateFromOptionsWithMetavariables {\n public:\n  template <typename Metavariables>\n  struct CfoOption {\n    static std::string name() { return Metavariables::option_name(); }\n    using type = std::string;\n    static constexpr Options::String help = {\"Option help text\"};\n  };\n\n  static constexpr Options::String help = {\"Class help text\"};\n  template <typename Metavariables>\n  using options = tmpl::list<CfoOption<Metavariables>>;\n\n  CreateFromOptionsWithMetavariables() = default;\n  template <typename Metavariables>\n  CreateFromOptionsWithMetavariables(std::string str,\n                                     const Options::Context& /*context*/,\n                                     Metavariables /*meta*/)\n      : str_(std::move(str)),\n        expected_(str_ == Metavariables::expected_string()) {}\n\n  std::string str_{};\n  bool expected_{false};\n};\n\nstruct Metavariables {\n  static std::string option_name() { return \"MetaName\"; }\n  static std::string expected_string() { return \"MetaString\"; }\n};\n\nstruct CfoWithMetavariables {\n  using type = CreateFromOptionsWithMetavariables;\n  static constexpr Options::String help = {\"help\"};\n};\n\nconst char* const input_file_text_with_metavariables = R\"(\nCfoWithMetavariables:\n  MetaName: MetaString\n)\";\n// [class_creation_example_with_metavariables]\n\nstruct CreateFromOptionsAggregate {\n  struct CfoOption {\n    using type = std::string;\n    static constexpr Options::String help = {\"Option help text\"};\n  };\n  static constexpr Options::String help = {\"Class help text\"};\n  using options = tmpl::list<CfoOption>;\n  // Define no constructors. The class can be aggregate-initialized.\n  std::string str{};\n};\n\nstruct CfoAggregate {\n  using type = CreateFromOptionsAggregate;\n  static constexpr Options::String help = {\"help\"};\n};\n\nconst char* const input_file_text_aggregate = R\"(\nCfoAggregate:\n  CfoOption: MetaString\n)\";\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Options.CustomType\", \"[Unit][Options]\") {\n  {\n    INFO(\"Type alias options is not a template\");\n    Options::Parser<tmpl::list<Cfo>> opts(\"\");\n    opts.parse(input_file_text);\n    CHECK(opts.get<Cfo>().str_ == \"foo\");\n  }\n  {\n    INFO(\"Type alias options is a template\");\n    Options::Parser<tmpl::list<CfoWithMetavariables>> opts(\"\");\n    opts.parse(input_file_text_with_metavariables);\n    CHECK(opts.get<CfoWithMetavariables, Metavariables>().str_ == \"MetaString\");\n    CHECK(opts.get<CfoWithMetavariables, Metavariables>().expected_);\n  }\n  {\n    INFO(\"Aggregate-initialization\");\n    Options::Parser<tmpl::list<CfoAggregate>> opts(\"\");\n    opts.parse(input_file_text_aggregate);\n    CHECK(opts.get<CfoAggregate>().str == \"MetaString\");\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Options.CustomType.error\", \"[Unit][Options]\") {\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Cfo>> opts(\"\");\n        opts.parse(\n            \"Cfo:\\n\"\n            \"  NotOption: foo\");\n        opts.get<Cfo>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 2 column 3:\\nOption 'NotOption' is not a valid option.\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Options.CustomType.custom_error\", \"[Unit][Options]\") {\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Cfo>> opts(\"\");\n        opts.parse(\n            \"Cfo:\\n\"\n            \"  CfoOption: zoo\");\n        opts.get<Cfo>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 2 column 3:\\nOption must start with an 'f' but is\"));\n}\n\n// [enum_creation_example]\nnamespace {\nenum class CreateFromOptionsAnimal { Cat, Dog };\n\nstruct CfoAnimal {\n  using type = CreateFromOptionsAnimal;\n  static constexpr Options::String help = {\"Option help text\"};\n};\n}  // namespace\n\ntemplate <>\nstruct Options::create_from_yaml<CreateFromOptionsAnimal> {\n  template <typename Metavariables>\n  static CreateFromOptionsAnimal create(const Options::Option& options) {\n    const auto animal = options.parse_as<std::string>();\n    if (animal == \"Cat\") {\n      return CreateFromOptionsAnimal::Cat;\n    }\n    if (animal == \"Dog\") {\n      return CreateFromOptionsAnimal::Dog;\n    }\n    PARSE_ERROR(options.context(),\n                \"CreateFromOptionsAnimal must be 'Cat' or 'Dog'\");\n  }\n};\n// [enum_creation_example]\n\nSPECTRE_TEST_CASE(\"Unit.Options.CustomType.specialized\", \"[Unit][Options]\") {\n  Options::Parser<tmpl::list<CfoAnimal>> opts(\"\");\n  opts.parse(\"CfoAnimal: Cat\");\n  CHECK(opts.get<CfoAnimal>() == CreateFromOptionsAnimal::Cat);\n}\n\n// [enum_void_creation_header_example]\nnamespace {\nenum class CreateFromOptionsExoticAnimal { MexicanWalkingFish, Platypus };\n\nstruct CfoExoticAnimal {\n  using type = CreateFromOptionsExoticAnimal;\n  static constexpr Options::String help = {\"Option help text\"};\n};\n}  // namespace\n\ntemplate <>\nstruct Options::create_from_yaml<CreateFromOptionsExoticAnimal> {\n  template <typename Metavariables>\n  static CreateFromOptionsExoticAnimal create(const Options::Option& options) {\n    return create<void>(options);\n  }\n};\ntemplate <>\nCreateFromOptionsExoticAnimal\nOptions::create_from_yaml<CreateFromOptionsExoticAnimal>::create<void>(\n    const Options::Option& options);\n// [enum_void_creation_header_example]\n\n// [enum_void_creation_cpp_example]\ntemplate <>\nCreateFromOptionsExoticAnimal\nOptions::create_from_yaml<CreateFromOptionsExoticAnimal>::create<void>(\n    const Options::Option& options) {\n  const auto animal = options.parse_as<std::string>();\n  if (animal == \"MexicanWalkingFish\") {\n    return CreateFromOptionsExoticAnimal::MexicanWalkingFish;\n  }\n  if (animal == \"Platypus\") {\n    return CreateFromOptionsExoticAnimal::Platypus;\n  }\n  PARSE_ERROR(options.context(),\n              \"CreateFromOptionsExoticAnimal must be 'MexicanWalkingFish' or \"\n              \"'Platypus'\");\n}\n// [enum_void_creation_cpp_example]\n\nSPECTRE_TEST_CASE(\"Unit.Options.CustomType.specialized_void\",\n                  \"[Unit][Options]\") {\n  const auto helper = [](const std::string& name,\n                         const CreateFromOptionsExoticAnimal expected) {\n    Options::Parser<tmpl::list<CfoExoticAnimal>> opts(\"\");\n    opts.parse(\"CfoExoticAnimal: \" + name);\n    CHECK(opts.get<CfoExoticAnimal>() == expected);\n  };\n  helper(\"Platypus\", CreateFromOptionsExoticAnimal::Platypus);\n  helper(\"MexicanWalkingFish\",\n         CreateFromOptionsExoticAnimal::MexicanWalkingFish);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Options.CustomType.specialized.error\",\n                  \"[Unit][Options]\") {\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<CfoAnimal>> opts(\"\");\n        opts.parse(\"CfoAnimal: Mouse\");\n        opts.get<CfoAnimal>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option CfoAnimal:\\nWhile creating a \"\n          \"CreateFromOptionsAnimal:\\nAt line 1 \"\n          \"column 12:\\nCreateFromOptionsAnimal must be 'Cat' or 'Dog'\"));\n}\n"
  },
  {
    "path": "tests/Unit/Options/Test_Factory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <map>\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"Options/Context.hpp\"\n#include \"Options/ParseError.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nclass OptionTest {\n public:\n  OptionTest() = default;\n  OptionTest(const OptionTest&) = default;\n  OptionTest(OptionTest&&) = default;\n  OptionTest& operator=(const OptionTest&) = default;\n  OptionTest& operator=(OptionTest&&) = default;\n  virtual ~OptionTest() = default;\n\n  virtual std::string derived_name() const = 0;\n};\n\nstruct OptionType {\n  using type = std::unique_ptr<OptionTest>;\n  static constexpr Options::String help = {\"The type of OptionTest\"};\n};\n\nclass Test1 : public OptionTest {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"A derived class\"};\n  Test1() = default;\n\n  std::string derived_name() const override { return \"Test1\"; }\n};\n\nclass Test2 : public OptionTest {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"\"};\n  Test2() = default;\n\n  std::string derived_name() const override { return \"Test2\"; }\n};\n\nclass TestWithArg : public OptionTest {\n public:\n  struct Arg {\n    using type = std::string;\n    static constexpr Options::String help = {\"halp\"};\n  };\n  using options = tmpl::list<Arg>;\n  static constexpr Options::String help = {\"\"};\n  TestWithArg() = default;\n  explicit TestWithArg(std::string arg) : arg_(std::move(arg)) {}\n\n  std::string derived_name() const override {\n    return \"TestWithArg(\" + arg_ + \")\";\n  }\n\n private:\n  std::string arg_;\n};\n\n// Same as TestWithArg, except there is an TestWithArg2::name() that\n// returns something other than \"TestWithArg2Arg\" to test that class\n// is named in the input file using pretty_type::name rather than\n// pretty_type::short_name\nclass TestWithArg2 : public OptionTest {\n public:\n  static std::string name() { return \"ThisIsArg\"; }\n  struct Arg {\n    using type = std::string;\n    static constexpr Options::String help = {\"halp\"};\n  };\n  using options = tmpl::list<Arg>;\n  static constexpr Options::String help = {\"\"};\n  TestWithArg2() = default;\n  explicit TestWithArg2(std::string arg) : arg_(std::move(arg)) {}\n\n  std::string derived_name() const override {\n    return \"TestWithArg2(\" + arg_ + \")\";\n  }\n\n private:\n  std::string arg_;\n};\n\nstruct TestWithMetavars : OptionTest {\n  struct Arg {\n    using type = std::string;\n    static constexpr Options::String help = {\"halp\"};\n  };\n  using options = tmpl::list<Arg>;\n  static constexpr Options::String help = {\"\"};\n\n  TestWithMetavars() = default;\n  template <typename Metavariables>\n  explicit TestWithMetavars(std::string arg, const Options::Context& /*unused*/,\n                            Metavariables /*meta*/)\n      : arg_(std::move(arg)), valid_(Metavariables::valid) {}\n\n  std::string derived_name() const override {\n    return \"TestWithArg(\" + arg_ + \")\" +\n           (valid_ ? std::string{\"yes\"} : std::string{\"no\"});\n  }\n\n private:\n  std::string arg_;\n  bool valid_{false};\n};\n\nclass OtherBase {\n protected:\n  OtherBase() = default;\n  OtherBase(const OtherBase&) = default;\n  OtherBase(OtherBase&&) = default;\n  OtherBase& operator=(const OtherBase&) = default;\n  OtherBase& operator=(OtherBase&&) = default;\n\n public:\n  virtual ~OtherBase() = default;\n};\n\nstruct OtherTag {\n  using type = std::unique_ptr<OtherBase>;\n  static constexpr Options::String help = {\"An OtherBase\"};\n};\n\nclass OtherDerived : public OtherBase {\n public:\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"\"};\n};\n\nstruct Vector {\n  using type = std::vector<std::unique_ptr<OptionTest>>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\nstruct Map {\n  using type = std::map<std::string, std::unique_ptr<OptionTest>>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\ntemplate <bool Valid>\nstruct Metavars {\n  static constexpr bool valid = Valid;\n  // [factory_creation]\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<OptionTest, tmpl::list<Test1, Test2, TestWithArg,\n                                          TestWithArg2, TestWithMetavars>>,\n        tmpl::pair<OtherBase, tmpl::list<OtherDerived>>>;\n  };\n  // [factory_creation]\n  static_assert(tt::assert_conforms_to_v<factory_creation,\n                                         Options::protocols::FactoryCreation>);\n};\n\nvoid test_factory() {\n  {\n    Options::Parser<tmpl::list<OptionType>> opts(\"\");\n    const std::string input = R\"(\n# [factory_without_arguments]\nOptionType: Test2\n# [factory_without_arguments]\n)\";\n    opts.parse(input);\n    CHECK(opts.get<OptionType, Metavars<true>>()->derived_name() == \"Test2\");\n  }\n\n  {\n    Options::Parser<tmpl::list<OtherTag>> opts(\"\");\n    opts.parse(\"OtherTag: OtherDerived\");\n    // Just verify we got a result.  There's only one valid value that\n    // can be returned anyway.\n    CHECK(opts.get<OtherTag, Metavars<true>>());\n  }\n}\n\nvoid test_factory_with_colon() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n  opts.parse(\n      \"OptionType:\\n\"\n      \"  Test2:\");\n  CHECK(opts.get<OptionType, Metavars<true>>()->derived_name() == \"Test2\");\n}\n\nvoid test_factory_with_arg() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n    const std::string input = R\"(\n# [factory_with_arguments]\nOptionType:\n  TestWithArg:\n    Arg: stuff\n# [factory_with_arguments]\n)\";\n  opts.parse(input);\n  CHECK(opts.get<OptionType, Metavars<true>>()->derived_name() ==\n        \"TestWithArg(stuff)\");\n}\n\nvoid test_factory_with_name_function() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n  opts.parse(\n      \"OptionType:\\n\"\n      \"  ThisIsArg:\\n\"\n      \"    Arg: stuff\");\n  CHECK(opts.get<OptionType, Metavars<true>>()->derived_name() ==\n        \"TestWithArg2(stuff)\");\n}\n\nvoid test_factory_with_metavars() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n  opts.parse(\n      \"OptionType:\\n\"\n      \"  TestWithMetavars:\\n\"\n      \"    Arg: stuff\");\n  CHECK(opts.get<OptionType, Metavars<true>>()->derived_name() ==\n        \"TestWithArg(stuff)yes\");\n  CHECK(opts.get<OptionType, Metavars<false>>()->derived_name() ==\n        \"TestWithArg(stuff)no\");\n  auto result_true = opts.apply<tmpl::list<OptionType>, Metavars<true>>(\n      [&](auto arg) { return arg; });\n  CHECK(result_true->derived_name() == \"TestWithArg(stuff)yes\");\n  auto result_false = opts.apply<tmpl::list<OptionType>, Metavars<false>>(\n      [&](auto arg) { return arg; });\n  CHECK(result_false->derived_name() == \"TestWithArg(stuff)no\");\n}\n\nvoid test_factory_object_vector() {\n  Options::Parser<tmpl::list<Vector>> opts(\"\");\n  opts.parse(\"Vector: [Test1, Test2, Test1]\");\n  const auto& arg = opts.get<Vector, Metavars<true>>();\n  CHECK(arg.size() == 3);\n  CHECK(arg[0]->derived_name() == \"Test1\");\n  CHECK(arg[1]->derived_name() == \"Test2\");\n  CHECK(arg[2]->derived_name() == \"Test1\");\n}\n\nvoid test_factory_object_map() {\n  Options::Parser<tmpl::list<Map>> opts(\"\");\n  opts.parse(\n      \"Map:\\n\"\n      \"  A: Test1\\n\"\n      \"  B: Test2\\n\"\n      \"  C: Test1\\n\");\n  const auto& arg = opts.get<Map, Metavars<true>>();\n  CHECK(arg.size() == 3);\n  CHECK(arg.at(\"A\")->derived_name() == \"Test1\");\n  CHECK(arg.at(\"B\")->derived_name() == \"Test2\");\n  CHECK(arg.at(\"C\")->derived_name() == \"Test1\");\n}\n\nclass IsCreatableBase {\n public:\n  IsCreatableBase() = default;\n  IsCreatableBase(const IsCreatableBase&) = default;\n  IsCreatableBase(IsCreatableBase&&) = default;\n  IsCreatableBase& operator=(const IsCreatableBase&) = default;\n  IsCreatableBase& operator=(IsCreatableBase&&) = default;\n  virtual ~IsCreatableBase() = default;\n};\n\nclass NotCreatable : public IsCreatableBase {\n public:\n  static constexpr bool factory_creatable = false;\n  // Test with no `help` or `options`.\n};\n\ntemplate <bool FactoryCreatable>\nclass MaybeCreatable : public IsCreatableBase {\n public:\n  static constexpr bool factory_creatable = FactoryCreatable;\n  static constexpr Options::String help = {\"halp\"};\n  using options = tmpl::list<>;\n};\n\n// Hack to capture option parser errors\nstruct IsCreatableMessageHack {\n  std::optional<std::string> message;\n};\n}  // namespace\n\ntemplate <>\nstruct Options::create_from_yaml<IsCreatableMessageHack> {\n  template <typename Metavariables>\n  static IsCreatableMessageHack create(const Options::Option& options) {\n    try {\n      options.parse_as<std::unique_ptr<IsCreatableBase>, Metavariables>();\n      return {};\n    } catch (const Options::detail::propagate_context& e) {\n      return {e.message()};\n    }\n  }\n};\n\nnamespace {\nstruct IsCreatableTag {\n  using type = IsCreatableMessageHack;\n  static constexpr Options::String help = {\"halp\"};\n};\n\ntemplate <bool MaybeCreatableCreatable>\nstruct IsCreatableMetavars {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        IsCreatableBase,\n        tmpl::list<NotCreatable, MaybeCreatable<MaybeCreatableCreatable>>>>;\n  };\n};\n\nvoid test_factory_not_creatable() {\n  const auto check_error_string = [](const auto creatable) {\n    Options::Parser<tmpl::list<IsCreatableTag>> parser(\"\");\n    parser.parse(\"IsCreatableTag: BadValue\");\n    const auto error =\n        parser.get<IsCreatableTag, IsCreatableMetavars<creatable>>();\n    REQUIRE(error.message.has_value());\n    const auto& message = *error.message;\n    CAPTURE(message);\n    CHECK(message.find(\"NotCreatable\") == std::string::npos);\n    CHECK((message.find(\"MaybeCreatable\") != std::string::npos) == creatable);\n  };\n  check_error_string(std::true_type{});\n  check_error_string(std::false_type{});\n\n  const auto check_creation = [](const auto creatable) {\n    Options::Parser<tmpl::list<IsCreatableTag>> parser(\"\");\n    parser.parse(\"IsCreatableTag: MaybeCreatable\");\n    const auto error =\n        parser.get<IsCreatableTag, IsCreatableMetavars<creatable>>();\n    REQUIRE(error.message.has_value() != creatable);\n    if (not creatable) {\n      CAPTURE(*error.message);\n      CHECK(error.message->find(\"Unknown Id 'MaybeCreatable'\") !=\n            std::string::npos);\n    }\n  };\n  check_creation(std::true_type{});\n  check_creation(std::false_type{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Options.Factory\", \"[Unit][Options]\") {\n  test_factory();\n  test_factory_with_arg();\n  test_factory_with_name_function();\n  test_factory_with_colon();\n  test_factory_with_metavars();\n  test_factory_object_vector();\n  test_factory_object_map();\n  test_factory_not_creatable();\n}\n\nnamespace {\nvoid trigger_missing() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n  opts.parse(\"OptionType:\");\n  opts.get<OptionType, Metavars<true>>();\n}\n\nvoid trigger_multiple() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n  opts.parse(\n      \"OptionType:\\n\"\n      \"  Test1:\\n\"\n      \"  Test2:\");\n  opts.get<OptionType, Metavars<true>>();\n}\n\nvoid trigger_vector() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n  opts.parse(\"OptionType: []\");\n  opts.get<OptionType, Metavars<true>>();\n}\n\nvoid trigger_unknown() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n  opts.parse(\"OptionType: Potato\");\n  opts.get<OptionType, Metavars<true>>();\n}\n\nvoid trigger_missing_arg() {\n  Options::Parser<tmpl::list<OptionType>> opts(\"\");\n  opts.parse(\n      \"OptionType:\\n\"\n      \"  TestWithArg:\");\n  opts.get<OptionType, Metavars<true>>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Options.Factory.missing\", \"[Unit][Options]\") {\n  CHECK_THROWS_WITH(trigger_missing(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"At line 1 column 1:\\nExpected a class name (and \"\n                        \"possibly options):\\nKnown Ids:\\n  Test1\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Options.Factory.multiple\", \"[Unit][Options]\") {\n  CHECK_THROWS_WITH(\n      trigger_multiple(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 2 column 3:\\nExpected a class name (and possibly options)\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Options.Factory.vector\", \"[Unit][Options]\") {\n  CHECK_THROWS_WITH(\n      trigger_vector(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 1 column 13:\\nExpected a class or a class with options\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Options.Factory.unknown\", \"[Unit][Options]\") {\n  CHECK_THROWS_WITH(trigger_unknown(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"At line 1 column 13:\\nUnknown Id 'Potato'\"));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Options.Factory.missing_arg\", \"[Unit][Options]\") {\n  CHECK_THROWS_WITH(\n      trigger_missing_arg(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 2 column 1:\\nYou did not specify the option (Arg)\"));\n}\n"
  },
  {
    "path": "tests/Unit/Options/Test_FactoryHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <type_traits>\n\n#include \"Options/FactoryHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Use a type for the label instead of an integer to catch any missing\n// tmpl::pins or such.\ntemplate <typename Label>\nstruct Base;\ntemplate <typename Label>\nstruct Derived;\ntemplate <int>\nstruct Label;\n\nusing original_classes =\n    tmpl::map<tmpl::pair<Base<Label<0>>,\n                         tmpl::list<Derived<Label<0>>, Derived<Label<1>>>>,\n              tmpl::pair<Base<Label<1>>, tmpl::list<Derived<Label<2>>>>>;\n\nstatic_assert(std::is_same_v<original_classes,\n                             Options::add_factory_classes<original_classes>>);\n\n// [add_factory_classes]\nusing new_classes = Options::add_factory_classes<\n    original_classes,\n    tmpl::pair<Base<Label<0>>,\n               tmpl::list<Derived<Label<3>>, Derived<Label<4>>>>,\n    tmpl::pair<Base<Label<2>>, tmpl::list<Derived<Label<5>>>>>;\n// [add_factory_classes]\n\n// tmpl::map doesn't guarantee an order for the keys, so we have to\n// check each entry separately.\nstatic_assert(tmpl::size<new_classes>::value == 3);\nstatic_assert(std::is_same_v<tmpl::at<new_classes, Base<Label<0>>>,\n                             tmpl::list<Derived<Label<0>>, Derived<Label<1>>,\n                                        Derived<Label<3>>, Derived<Label<4>>>>);\nstatic_assert(std::is_same_v<tmpl::at<new_classes, Base<Label<1>>>,\n                             tmpl::list<Derived<Label<2>>>>);\nstatic_assert(std::is_same_v<tmpl::at<new_classes, Base<Label<2>>>,\n                             tmpl::list<Derived<Label<5>>>>);\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Options/Test_Options.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <functional>\n#include <list>\n#include <map>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Context.hpp\"\n#include \"Options/Options.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"Options/Tags.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test_options_empty() {\n  INFO(\"Empty\");\n  {\n    // Check for no error.\n    Options::Parser<tmpl::list<>> opts(\"\");\n    opts.parse(\"\");\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<>> opts(\"\");\n        opts.parse(\"Option:\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 1 column 1:\\nOption 'Option' is not a valid option.\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<>> opts(\"\");\n        opts.parse(\"4\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\"\\n'4' does not look like options\"));\n}\n\nvoid test_options_syntax_error() {\n  INFO(\"Syntax error\");\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<>> opts(\"\");\n        opts.parse(\n            \"DomainCreator: CreateInterval:\\n\"\n            \"  IsPeriodicIn: [false]\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 1 column 30:\\nUnable to correctly parse the \"\n          \"input file because of a syntax error\"));\n}\n\nstruct Simple {\n  using type = int;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct NamedSimple {\n  using type = int;\n  static std::string name() { return \"SomeName\"; }\n  static constexpr Options::String help = {\n      \"halp halp halp halp halp halp halp halp halp halp halp halp\\n\"\n      \"halp halp halp halp halp halp halp halp halp halp halp halp\"\n      \"halp halp halp halp halp halp halp halp halp halp halp halp\"\n      \"halp halp halp halp halp halp halp halp halp halp halp halp\"\n      \"halp halp halp halp halp halp halp halp halp halp halp halp\"\n      \"halp halp halp halp halp halp halp halp halp halp halp halp\"};\n};\n\nvoid test_options_simple() {\n  INFO(\"Simple\");\n  {\n    Options::Parser<tmpl::list<Simple>> opts(\"\");\n    opts.parse(\"Simple: -4\");\n    CHECK(opts.get<Simple>() == -4);\n  }\n  {\n    Options::Parser<tmpl::list<NamedSimple>> opts(\"\");\n    opts.parse(\"SomeName: -4\");\n    CHECK(opts.get<NamedSimple>() == -4);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Simple>> opts(\"\");\n        opts.parse(\n            \"Simple: -4\\n\"\n            \"Simple: -3\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 2 column 1:\\nOption 'Simple' specified twice.\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<NamedSimple>> opts(\"\");\n        opts.parse(\n            \"SomeName: -4\\n\"\n            \"SomeName: -3\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 2 column 1:\\nOption 'SomeName' specified twice.\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Simple>> opts(\"\");\n        opts.parse(\"\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"In string:\\nYou did not specify the option (Simple)\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<NamedSimple>> opts(\"\");\n        opts.parse(\"\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"In string:\\nYou did not specify the option (SomeName)\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        Options::Parser<tmpl::list<NamedSimple, Simple>> opts(\"\");\n        opts.parse(\"\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"In string:\\nYou did not specify the options (SomeName,Simple)\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Simple>> opts(\"\");\n        opts.parse(\"Simple:\");\n        opts.get<Simple>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Simple:\\nAt line 1 column \"\n          \"1:\\nFailed to convert value to type int:\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<NamedSimple>> opts(\"\");\n        opts.parse(\"SomeName:\");\n        opts.get<NamedSimple>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option SomeName:\\nAt line 1 column \"\n          \"1:\\nFailed to convert value to type int:\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Simple>> opts(\"\");\n        opts.parse(\"Simple: 2.3\");\n        opts.get<Simple>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Simple:\\nAt line 1 column \"\n          \"9:\\nFailed to convert value to type int: 2.3\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<NamedSimple>> opts(\"\");\n        opts.parse(\"SomeName: 2.3\");\n        opts.get<NamedSimple>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option SomeName:\\nAt line 1 column \"\n          \"11:\\nFailed to convert value to type int: 2.3\"));\n}\n\nvoid test_options_print_long_help() {\n  Options::Parser<tmpl::list<NamedSimple>> opts(\"\");\n  CHECK(opts.help() ==\n        R\"(\n==== Description of expected options:\n\n\nOptions:\n  SomeName:\n    type=int\n    halp halp halp halp halp halp halp halp halp halp halp halp\n    halp halp halp halp halp halp halp halp halp halp halp halphalp halp halp\n    halp halp halp halp halp halp halp halp halphalp halp halp halp halp halp\n    halp halp halp halp halp halphalp halp halp halp halp halp halp halp halp\n    halp halp halphalp halp halp halp halp halp halp halp halp halp halp halp\n\n)\");\n}\n\n// [options_example_group]\nstruct Group {\n  static constexpr Options::String help = {\"Group halp\"};\n};\n\nstruct GroupedTag {\n  using type = int;\n  static constexpr Options::String help = {\"Tag halp\"};\n  using group = Group;\n};\n// [options_example_group]\n\nstruct OuterGroup {\n  static constexpr Options::String help = {\"Outer group halp\"};\n};\n\nstruct InnerGroup {\n  static constexpr Options::String help = {\"Inner group halp\"};\n  using group = OuterGroup;\n};\n\nstruct InnerGroupedTag {\n  using type = int;\n  static constexpr Options::String help = {\"Inner tag halp\"};\n  using group = InnerGroup;\n};\n\nstruct OuterGroupedTag {\n  using type = int;\n  static constexpr Options::String help = {\"Outer tag halp\"};\n  using group = OuterGroup;\n};\n\ntemplate <typename>\nstruct TemplatedGroup {\n  static constexpr Options::String help = {\"halp\"};\n};\n\nstruct TagWithTemplatedGroup {\n  using type = int;\n  static constexpr Options::String help = {\"halp\"};\n  using group = TemplatedGroup<int>;\n};\n\nvoid test_options_grouped() {\n  {\n    INFO(\"Option groups\");\n    Options::Parser<tmpl::list<GroupedTag, Simple>> opts(\"Overall help text\");\n    opts.parse(\n        \"Group:\\n\"\n        \"  GroupedTag: 3\\n\"\n        \"Simple: 2\");\n    CHECK(opts.get<GroupedTag>() == 3);\n    CHECK(opts.get<Simple>() == 2);\n  }\n  {\n    INFO(\"Nested option groups\");\n    Options::Parser<tmpl::list<InnerGroupedTag, OuterGroupedTag, Simple>> opts(\n        \"Overall help text\");\n    opts.parse(\n        \"OuterGroup:\\n\"\n        \"  InnerGroup:\\n\"\n        \"    InnerGroupedTag: 3\\n\"\n        \"  OuterGroupedTag: 1\\n\"\n        \"Simple: 2\\n\");\n    CHECK(opts.get<InnerGroupedTag>() == 3);\n    CHECK(opts.get<OuterGroupedTag>() == 1);\n    CHECK(opts.get<Simple>() == 2);\n  }\n  {\n    INFO(\"Templated option groups\");\n    Options::Parser<tmpl::list<TagWithTemplatedGroup>> opts(\"\");\n    opts.parse(\n        \"TemplatedGroup:\\n\"\n        \"  TagWithTemplatedGroup: 3\");\n    CHECK(opts.get<TagWithTemplatedGroup>() == 3);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<InnerGroupedTag>> opts(\"\");\n        opts.parse(\"\");\n        opts.get<InnerGroupedTag>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"In string:\\nYou did not specify the option (OuterGroup)\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<InnerGroupedTag>> opts(\"\");\n        opts.parse(\"OuterGroup:\");\n        opts.get<InnerGroupedTag>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"In group OuterGroup:\\nYou did not specify the option (InnerGroup)\"));\n}\n\n// [options_example_scalar_struct]\nstruct Bounded {\n  using type = int;\n  static constexpr Options::String help = {\n      \"Option with bounds and a suggested value\"};\n  // These are optional\n  static type suggested_value() { return 3; }\n  static type lower_bound() { return 2; }\n  static type upper_bound() { return 10; }\n};\n// [options_example_scalar_struct]\n\nstruct BadSuggestion {\n  using type = int;\n  static constexpr Options::String help = {\"halp\"};\n  static type suggested_value() { return 3; }\n  static type lower_bound() { return 4; }\n};\nstruct NamedBadSuggestion {\n  using type = int;\n  static std::string name() { return \"SomeName\"; }\n  static constexpr Options::String help = {\"halp\"};\n  static type suggested_value() { return 3; }\n  static type lower_bound() { return 4; }\n};\n\nvoid test_options_suggested() {\n  {\n    Options::Parser<tmpl::list<Bounded>> opts(\"Overall help text\");\n    opts.parse(\"Bounded: 8\");\n    CHECK(opts.get<Bounded>() == 8);\n  }\n\n  {\n    // [options_example_scalar_parse]\n    Options::Parser<tmpl::list<Bounded>> opts(\"Overall help text\");\n    opts.parse(\"Bounded: 3\");\n    CHECK(opts.get<Bounded>() == 3);\n    // [options_example_scalar_parse]\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<BadSuggestion>> opts(\"\");\n        opts.parse(\"BadSuggestion: 5\");\n        opts.get<BadSuggestion>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Checking SUGGESTED value for BadSuggestion:\\nValue 3 is \"\n          \"below the lower bound of 4\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<NamedBadSuggestion>> opts(\"\");\n        opts.parse(\"SomeName: 5\");\n        opts.get<NamedBadSuggestion>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Checking SUGGESTED value for SomeName:\\nValue 3 is \"\n          \"below the lower bound of 4\"));\n}\n\n// [[OutputRegex, Bounded, line 1:.  Specified: 5.  Suggested: 3]]\nSPECTRE_TEST_CASE(\"Unit.Options.suggestion_warning\", \"[Unit][Options]\") {\n  OUTPUT_TEST();\n  Options::Parser<tmpl::list<Bounded>> opts(\"\");\n  opts.parse(\"Bounded: 5\");\n  opts.get<Bounded>();\n}\n\nvoid test_options_bounded() {\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Bounded>> opts(\"\");\n        opts.parse(\"Bounded: 1\");\n        opts.get<Bounded>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Bounded:\\nAt line 1 column \"\n          \"10:\\nValue 1 is below the lower bound of 2\"));\n\n  {\n    Options::Parser<tmpl::list<Bounded>> opts(\"\");\n    opts.parse(\"Bounded: 2\");\n    CHECK(opts.get<Bounded>() == 2);\n  }\n\n  {\n    Options::Parser<tmpl::list<Bounded>> opts(\"\");\n    opts.parse(\"Bounded: 10\");\n    CHECK(opts.get<Bounded>() == 10);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Bounded>> opts(\"\");\n        opts.parse(\"Bounded: 11\");\n        opts.get<Bounded>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Bounded:\\nAt line 1 column \"\n          \"10:\\nValue 11 is above the upper bound of 10\"));\n}\n\n// [options_example_vector_struct]\nstruct VectorOption {\n  using type = std::vector<int>;\n  static constexpr Options::String help = {\"A vector with length limits\"};\n  // These are optional\n  static std::string name() {\n    return \"Vector\";  // defaults to \"VectorOption\"\n  }\n  static size_t lower_bound_on_size() { return 2; }\n  static size_t upper_bound_on_size() { return 5; }\n};\n// [options_example_vector_struct]\n\nvoid test_options_bounded_vector() {\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<VectorOption>> opts(\"\");\n        opts.parse(\"Vector: [2]\");\n        opts.get<VectorOption>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Vector:\\nAt line 1 column 9:\\nValue must have \"\n          \"at least 2 entries, but 1 were given.\"));\n\n  {\n    Options::Parser<tmpl::list<VectorOption>> opts(\"\");\n    opts.parse(\"Vector: [2,3]\");\n    CHECK(opts.get<VectorOption>() == (std::vector<int>{2, 3}));\n  }\n\n  {\n    Options::Parser<tmpl::list<VectorOption>> opts(\"\");\n    opts.parse(\"Vector: [2, 3, 3, 3, 5]\");\n    CHECK(opts.get<VectorOption>() == (std::vector<int>{2, 3, 3, 3, 5}));\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<VectorOption>> opts(\"\");\n        opts.parse(\"Vector: [2, 3, 3, 3, 5, 6]\");\n        opts.get<VectorOption>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Vector:\\nAt line 1 column 9:\\nValue must have \"\n          \"at most 5 entries, but 6 were given.\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<VectorOption>> opts(\"\");\n        opts.parse(\"Vector:\");\n        opts.get<VectorOption>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Vector:\\nAt line 1 column 1:\\nValue must have \"\n          \"at least 2 entries, but 0 were given.\"));\n}\n\nstruct Array {\n  using type = std::array<int, 3>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\nstruct ZeroArray {\n  using type = std::array<int, 0>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\nvoid test_options_array() {\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Array>> opts(\"\");\n        opts.parse(\"Array: [1, 2]\");\n        opts.get<Array>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Array:\\nAt line 1 column \"\n          \"8:\\nFailed to convert value to type [int x3]: [1, 2]\"));\n\n  {\n    Options::Parser<tmpl::list<Array>> opts(\"\");\n    opts.parse(\"Array: [1,2,3]\");\n    CHECK(opts.get<Array>() == (std::array<int, 3>{{1, 2, 3}}));\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Array>> opts(\"\");\n        opts.parse(\"Array: [1, 2, 3, 4]\");\n        opts.get<Array>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Array:\\nAt line 1 column 8:\\nFailed to convert \"\n          \"value to type [int x3]: [1, 2, 3, 4]\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Array>> opts(\"\");\n        opts.parse(\n            \"Array:\\n\"\n            \"  - 1\\n\"\n            \"  - 2\\n\"\n            \"  - 3\\n\"\n            \"  - 4\");\n        opts.get<Array>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Array:\\nAt line 2 column 3:\\nFailed to convert \"\n          \"value to type [int x3]:\\n  - 1\\n  - 2\\n  - 3\\n  - 4\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Array>> opts(\"\");\n        opts.parse(\"Array:\");\n        opts.get<Array>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Array:\\nAt line 1 column \"\n          \"1:\\nFailed to convert value to type [int x3]:\"));\n\n  {\n    Options::Parser<tmpl::list<ZeroArray>> opts(\"\");\n    opts.parse(\"ZeroArray:\");\n    opts.get<ZeroArray>();\n  }\n}\n\nstruct Map {\n  using type = std::map<std::string, int>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\nvoid test_options_map() {\n  {\n    Options::Parser<tmpl::list<Map>> opts(\"\");\n    opts.parse(\n        \"Map:\\n\"\n        \"  A: 3\\n\"\n        \"  Z: 2\");\n    std::map<std::string, int> expected;\n    expected.emplace(\"A\", 3);\n    expected.emplace(\"Z\", 2);\n    CHECK(opts.get<Map>() == expected);\n  }\n\n  {\n    Options::Parser<tmpl::list<Map>> opts(\"\");\n    opts.parse(\"Map:\");\n    CHECK(opts.get<Map>().empty());\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Map>> opts(\"\");\n        opts.parse(\"Map: string\");\n        opts.get<Map>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Map:\\nAt line 1 column 6:\\nFailed \"\n          \"to convert value to type {string: int}: string\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Map>> opts(\"\");\n        opts.parse(\n            \"Map:\\n\"\n            \"  A: string\");\n        opts.get<Map>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"While parsing option Map:\\nAt line 2 column 6:\\nFailed \"\n          \"to convert value to type {string: int}: A: string\"));\n}\n\nstruct UnorderedMap {\n  using type = std::unordered_map<std::string, int>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\nvoid test_options_unordered_map() {\n  Options::Parser<tmpl::list<UnorderedMap>> opts(\"\");\n  opts.parse(\n      \"UnorderedMap:\\n\"\n      \"  A: 3\\n\"\n      \"  Z: 2\");\n  std::unordered_map<std::string, int> expected;\n  expected.emplace(\"A\", 3);\n  expected.emplace(\"Z\", 2);\n  CHECK(opts.get<UnorderedMap>() == expected);\n}\n\ntemplate <typename... T>\nstruct VariantTag {\n  using type = std::variant<T...>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\nstruct VariantOption1 {\n  static constexpr Options::String help = {\"VariantOption1 halp\"};\n  struct Opt1 {\n    using type = int;\n    static constexpr Options::String help = {\"halp\"};\n  };\n  using options = tmpl::list<Opt1>;\n  VariantOption1() = default;\n  VariantOption1(int /*unused*/) {}\n};\n\nstruct VariantOption2 {\n  static constexpr Options::String help = {\"VariantOption2 halp\"};\n  struct Opt2 {\n    using type = int;\n    static constexpr Options::String help = {\"halp\"};\n  };\n  template <typename Metavariables>\n  using options =\n      tmpl::list<tmpl::conditional_t<Metavariables::valid, Opt2, void>>;\n  VariantOption2() = default;\n  VariantOption2(tmpl::list<Opt2> /*meta*/, int /*unused*/) {}\n};\n\nstruct VariantOption2Metavars {\n  static constexpr bool valid = true;\n};\n\nstruct VariantOptionWithGroup {\n  static constexpr Options::String help = {\"VariantOptionWithGroup halp\"};\n  struct Group {\n    static constexpr Options::String help = {\"halp\"};\n  };\n  struct Opt3 {\n    using type = int;\n    static constexpr Options::String help = {\"halp\"};\n    using group = Group;\n  };\n  using options = tmpl::list<Opt3>;\n  VariantOptionWithGroup() = default;\n  VariantOptionWithGroup(int /*unused*/) {}\n};\n\nvoid test_options_variant() {\n  {\n    const auto check = [](const std::string& input, const auto& expected) {\n      using Tag = tmpl::wrap<std::decay_t<decltype(expected)>, VariantTag>;\n      Options::Parser<tmpl::list<Tag>> opts(\"\");\n      opts.parse(\"VariantTag: \" + input);\n      CHECK(opts.template get<Tag>() == expected);\n    };\n    check(\"3\", std::variant<int>(std::in_place_type_t<int>{}, 3));\n    check(\"Hello\", std::variant<std::string>(\n                       std::in_place_type_t<std::string>{}, \"Hello\"));\n    check(\"3\", std::variant<int, std::string>(std::in_place_type_t<int>{}, 3));\n    check(\"Hello\", std::variant<int, std::string>(\n                       std::in_place_type_t<std::string>{}, \"Hello\"));\n    check(\"3\", std::variant<std::string, int>(\n                   std::in_place_type_t<std::string>{}, \"3\"));\n    check(\"Hello\", std::variant<std::string, int>(\n                       std::in_place_type_t<std::string>{}, \"Hello\"));\n\n    {\n      const std::string help_text =\n          Options::Parser<tmpl::list<VariantTag<int, std::string>>>(\"\").help();\n      CAPTURE(help_text);\n      CHECK(help_text.find(\"type=int or string\\n\") != std::string::npos);\n    }\n  }\n\n  {\n    using tag = VariantTag<int, std::string>;\n    CHECK_THROWS_WITH(\n        []() {\n          Options::Parser<tmpl::list<tag>> opts(\"\");\n          opts.parse(\"VariantTag: []\");\n          opts.get<tag>();\n        }(),\n        Catch::Matchers::ContainsSubstring(\n            \"While creating a variant:\\nAt line 1 column \"\n            \"13:\\nFailed to convert value to type int or string: \"\n            \"[]\") and\n            Catch::Matchers::ContainsSubstring(\n                \"At line 1 column 13:\\nFailed to convert value to \"\n                \"type int: []\") and\n            Catch::Matchers::ContainsSubstring(\n                \"At line 1 column 13:\\nFailed to convert value to \"\n                \"type string: []\"));\n  }\n\n  {\n    using tag = VariantTag<VariantOption1, VariantOption2>;\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\n          \"VariantTag:\\n\"\n          \"  Opt1: 3\");\n      CHECK(std::holds_alternative<VariantOption1>(\n          parser.get<tag, VariantOption2Metavars>()));\n    }\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\n          \"VariantTag:\\n\"\n          \"  Opt2: 3\");\n      CHECK(std::holds_alternative<VariantOption2>(\n          parser.get<tag, VariantOption2Metavars>()));\n    }\n    CHECK_THROWS_WITH(\n        ([]() {\n          Options::Parser<tmpl::list<tag>> parser(\"halp\");\n          parser.parse(\n              \"VariantTag:\\n\"\n              \"  OptZ: 3\");\n          parser.get<tag, VariantOption2Metavars>();\n        }()),\n        Catch::Matchers::ContainsSubstring(\"VariantOption1 halp\") and\n            Catch::Matchers::ContainsSubstring(\"VariantOption2 halp\") and\n            Catch::Matchers::ContainsSubstring(\"EITHER\") and\n            not Catch::Matchers::ContainsSubstring(\"Possible errors\"));\n  }\n  {\n    using tag = VariantTag<VariantOption1, int>;\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\n          \"VariantTag:\\n\"\n          \"  Opt1: 3\");\n      CHECK(std::holds_alternative<VariantOption1>(parser.get<tag>()));\n    }\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\"VariantTag: 3\");\n      CHECK(std::holds_alternative<int>(parser.get<tag>()));\n    }\n    CHECK_THROWS_WITH(\n        ([]() {\n          Options::Parser<tmpl::list<tag>> parser(\"halp\");\n          parser.parse(\n              \"VariantTag:\\n\"\n              \"  OptZ: 3\");\n          parser.get<tag>();\n        }()),\n        Catch::Matchers::ContainsSubstring(\n            \"Failed to convert value to type VariantOption1 or int:\") and\n            Catch::Matchers::ContainsSubstring(\"VariantOption1 halp\") and\n            Catch::Matchers::ContainsSubstring(\n                \"Failed to convert value to type int:\") and\n            not Catch::Matchers::ContainsSubstring(\"EITHER\") and\n            Catch::Matchers::ContainsSubstring(\"Possible errors\"));\n  }\n  {\n    using tag = VariantTag<VariantOption1, int, VariantOption2>;\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\n          \"VariantTag:\\n\"\n          \"  Opt1: 3\");\n      CHECK(std::holds_alternative<VariantOption1>(\n          parser.get<tag, VariantOption2Metavars>()));\n    }\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\"VariantTag: 3\");\n      CHECK(std::holds_alternative<int>(\n          parser.get<tag, VariantOption2Metavars>()));\n    }\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\n          \"VariantTag:\\n\"\n          \"  Opt2: 3\");\n      CHECK(std::holds_alternative<VariantOption2>(\n          parser.get<tag, VariantOption2Metavars>()));\n    }\n    CHECK_THROWS_WITH(\n        ([]() {\n          Options::Parser<tmpl::list<tag>> parser(\"halp\");\n          parser.parse(\n              \"VariantTag:\\n\"\n              \"  OptZ: 3\");\n          parser.get<tag, VariantOption2Metavars>();\n        }()),\n        Catch::Matchers::ContainsSubstring(\n            \"Failed to convert value to type VariantOption1 or int \"\n            \"or VariantOption2:\") and\n            Catch::Matchers::ContainsSubstring(\"VariantOption1 halp\") and\n            Catch::Matchers::ContainsSubstring(\"VariantOption2 halp\") and\n            Catch::Matchers::ContainsSubstring(\n                \"Failed to convert value to type int:\") and\n            Catch::Matchers::ContainsSubstring(\"EITHER\") and\n            Catch::Matchers::ContainsSubstring(\"Possible errors\"));\n  }\n  {\n    using tag =\n        VariantTag<VariantOption1, VariantOption2, VariantOptionWithGroup>;\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\n          \"VariantTag:\\n\"\n          \"  Opt1: 3\");\n      CHECK(std::holds_alternative<VariantOption1>(\n          parser.get<tag, VariantOption2Metavars>()));\n    }\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\n          \"VariantTag:\\n\"\n          \"  Opt2: 3\");\n      CHECK(std::holds_alternative<VariantOption2>(\n          parser.get<tag, VariantOption2Metavars>()));\n    }\n    {\n      Options::Parser<tmpl::list<tag>> parser(\"halp\");\n      parser.parse(\n          \"VariantTag:\\n\"\n          \"  Group:\\n\"\n          \"    Opt3: 3\");\n      CHECK(std::holds_alternative<VariantOptionWithGroup>(\n          parser.get<tag, VariantOption2Metavars>()));\n    }\n    CHECK_THROWS_WITH(\n        ([]() {\n          Options::Parser<tmpl::list<tag>> parser(\"halp\");\n          parser.parse(\n              \"VariantTag:\\n\"\n              \"  OptZ: 3\");\n          parser.get<tag, VariantOption2Metavars>();\n        }()),\n        Catch::Matchers::ContainsSubstring(\n            \"Failed to convert value to type VariantOption1 or \"\n            \"VariantOption2 or VariantOptionWithGroup:\") and\n            Catch::Matchers::ContainsSubstring(\"VariantOption1 halp\") and\n            Catch::Matchers::ContainsSubstring(\"VariantOption2 halp\") and\n            Catch::Matchers::ContainsSubstring(\n                \"VariantOptionWithGroup halp\") and\n            Catch::Matchers::ContainsSubstring(\"EITHER\") and\n            Catch::Matchers::ContainsSubstring(\"Possible errors\"));\n  }\n}\n\ntemplate <typename T>\nstruct Wrapped {\n  T data;\n};\n\n#define FORWARD_OP(op)                                         \\\n  template <typename T>                                        \\\n  bool operator op(const Wrapped<T>& a, const Wrapped<T>& b) { \\\n    return a.data op b.data;                                   \\\n  }\nFORWARD_OP(==)\nFORWARD_OP(!=)\nFORWARD_OP(<)\nFORWARD_OP(>)\nFORWARD_OP(<=)\nFORWARD_OP(>=)\n}  // namespace\n\nnamespace std {\ntemplate <typename T>\nstruct hash<Wrapped<T>> {\n  size_t operator()(const Wrapped<T>& x) const { return hash<T>{}(x.data); }\n};\n}  // namespace std\n\ntemplate <typename T>\nstruct Options::create_from_yaml<Wrapped<T>> {\n  template <typename Metavariables>\n  static Wrapped<T> create(const Options::Option& options) {\n    return Wrapped<T>{options.parse_as<T>()};\n  }\n};\n\nnamespace {\nstruct WrapMap {\n  using type = std::map<Wrapped<int>, Wrapped<std::string>>;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct WrapVector {\n  using type = std::vector<Wrapped<int>>;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct WrapList {\n  using type = std::list<Wrapped<int>>;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct WrapArray {\n  using type = std::array<Wrapped<int>, 2>;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct WrapPair {\n  using type = std::pair<Wrapped<int>, Wrapped<std::string>>;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct WrapUnorderedMap {\n  using type = std::unordered_map<Wrapped<int>, Wrapped<std::string>>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\nvoid test_options_complex_containers() {\n  Options::Parser<tmpl::list<WrapMap, WrapVector, WrapList, WrapArray, WrapPair,\n                             WrapUnorderedMap>>\n      opts(\"\");\n  opts.parse(\n      \"WrapMap: {1: A, 2: B}\\n\"\n      \"WrapVector: [1, 2, 3]\\n\"\n      \"WrapList: [1, 2, 3]\\n\"\n      \"WrapArray: [1, 2]\\n\"\n      \"WrapPair: [1, X]\\n\"\n      \"WrapUnorderedMap: {1: A, 2: B}\\n\");\n  CHECK(opts.get<WrapMap>() == (std::map<Wrapped<int>, Wrapped<std::string>>{\n                                   {{1}, {\"A\"}}, {{2}, {\"B\"}}}));\n  CHECK(opts.get<WrapVector>() == (std::vector<Wrapped<int>>{{1}, {2}, {3}}));\n  CHECK(opts.get<WrapList>() == (std::list<Wrapped<int>>{{1}, {2}, {3}}));\n  CHECK(opts.get<WrapArray>() == (std::array<Wrapped<int>, 2>{{{1}, {2}}}));\n  CHECK(opts.get<WrapPair>() ==\n        (std::pair<Wrapped<int>, Wrapped<std::string>>{{1}, {\"X\"}}));\n  CHECK(opts.get<WrapUnorderedMap>() ==\n        (std::unordered_map<Wrapped<int>, Wrapped<std::string>>{{{1}, {\"A\"}},\n                                                                {{2}, {\"B\"}}}));\n}\n\n#ifdef SPECTRE_DEBUG\nstruct Duplicate {\n  using type = int;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct NamedDuplicate {\n  using type = int;\n  static std::string name() { return \"Duplicate\"; }\n  static constexpr Options::String help = {\"halp\"};\n};\n\nstruct\n    ToooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooLong {\n  using type = int;\n  static constexpr Options::String help = {\"halp\"};\n};\nusing short_alias_for_too_long =\n    ToooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooLong;\nstruct NamedTooLong {\n  using type = int;\n  static std::string name() {\n    return \"Toooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\n           \"oLong\";\n  }\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct NoHelp {\n  using type = int;\n  static constexpr Options::String help = {\"\"};\n};\nstruct NamedNoHelp {\n  using type = int;\n  static std::string name() { return \"NoHelp\"; }\n  static constexpr Options::String help = {\"\"};\n};\n#endif\n\nvoid test_options_invalid_calls() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        Options::Parser<tmpl::list<Duplicate, NamedDuplicate>> opts(\"\");\n        opts.parse(\"\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Duplicate option name: Duplicate\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<short_alias_for_too_long>> opts(\"\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The option name \"\n          \"Tooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\n          \"ooooooooooooooLong is too long for nice formatting\"));\n  CHECK_THROWS_WITH(\n      []() { Options::Parser<tmpl::list<NamedTooLong>> opts(\"\"); }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The option name \"\n          \"Tooooooooooooooooooooooooooooooooooooooooooooooooooooo\"\n          \"ooooooooooooooLong is too long for nice formatting\"));\n\n  CHECK_THROWS_WITH(\n      Options::Parser<tmpl::list<NoHelp>>(\"\"),\n      Catch::Matchers::ContainsSubstring(\n          \"You must supply a help string of non-zero length for NoHelp\"));\n\n  CHECK_THROWS_WITH(\n      Options::Parser<tmpl::list<NamedNoHelp>>(\"\"),\n      Catch::Matchers::ContainsSubstring(\n          \"You must supply a help string of non-zero length for NoHelp\"));\n#endif\n}\n\nstruct Apply1 {\n  using type = int;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct Apply2 {\n  using type = std::string;\n  static constexpr Options::String help = {\"halp\"};\n};\nstruct Apply3 {\n  using type = std::vector<int>;\n  static constexpr Options::String help = {\"halp\"};\n};\n\nvoid test_options_apply() {\n  Options::Parser<tmpl::list<Apply1, Apply2, Apply3>> opts(\"\");\n  opts.parse(\n      \"Apply1: 2\\n\"\n      \"Apply2: str\\n\"\n      \"Apply3: [1, 2, 3]\");\n  // We do the checks outside the lambda to make sure it actually gets called.\n  std::vector<int> arg1;\n  int arg2 = 0;\n  opts.apply<tmpl::list<Apply3, Apply1>>([&](const auto& a, auto b) {\n    arg1 = a;\n    arg2 = b;\n  });\n  CHECK(arg1 == (std::vector<int>{1, 2, 3}));\n  CHECK(arg2 == 2);\n}\n\nvoid test_options_option_context_default_stream() {\n  CHECK(get_output(Options::Context{}).empty());\n}\n\n// Use formatted inner types to make sure nested formatting works\ntemplate <typename T>\nusing In = std::array<T, 0>;\nstruct FormatMap {\n  using type = std::map<In<int>, In<double>>;\n  static constexpr Options::String help = {\"halp\"};\n  static constexpr const char* const expected = \"{[int x0]: [double x0]}\";\n};\nstruct FormatVector {\n  using type = std::vector<In<int>>;\n  static constexpr Options::String help = {\"halp\"};\n  static constexpr const char* const expected = \"[[int x0], ...]\";\n};\nstruct FormatList {\n  using type = std::list<In<int>>;\n  static constexpr Options::String help = {\"halp\"};\n  static constexpr const char* const expected = \"[[int x0], ...]\";\n};\nstruct FormatArray {\n  using type = std::array<In<int>, 3>;\n  static constexpr Options::String help = {\"halp\"};\n  static constexpr const char* const expected = \"[[int x0] x3]\";\n};\nstruct FormatPair {\n  using type = std::pair<In<int>, In<double>>;\n  static constexpr Options::String help = {\"halp\"};\n  static constexpr const char* const expected = \"[[int x0], [double x0]]\";\n};\nstruct ArrayHash {\n  size_t operator()(const std::array<int, 0>& /*unused*/) const { return 0; }\n};\nstruct FormatUnorderedMap {\n  using type = std::unordered_map<In<int>, In<double>, ArrayHash>;\n  static constexpr Options::String help = {\"halp\"};\n  static constexpr const char* const expected = \"{[int x0]: [double x0]}\";\n};\n\nstruct ScalarWithLimits {\n  using type = int;\n  static constexpr Options::String help = \"ScalarHelp\";\n  static type suggested_value() { return 7; }\n  static type lower_bound() { return 2; }\n  static type upper_bound() { return 8; }\n};\n\nstruct VectorWithLimits {\n  using type = std::vector<int>;\n  static constexpr Options::String help = \"VectorHelp\";\n  static size_t lower_bound_on_size() { return 5; }\n  static size_t upper_bound_on_size() { return 9; }\n};\n\nstruct SuggestedBool {\n  using type = bool;\n  static type suggested_value() { return false; }\n  constexpr static Options::String help = \"halp\";\n};\n\nvoid test_options_format() {\n  {\n    const auto check = [](auto opt) {\n      using Opt = decltype(opt);\n      Options::Parser<tmpl::list<Opt>> opts(\"\");\n      INFO(\"Help string:\\n\"\n           << opts.help() << \"\\n\\nExpected to find:\\n\"\n           << \"  type=\"s + Opt::expected + \"\\n\");\n      // Add whitespace to check that we've got the entire type\n      CHECK(opts.help().find(\"type=\"s + Opt::expected + \"\\n\"s) !=\n            std::string::npos);\n    };\n    check(FormatMap{});\n    check(FormatVector{});\n    check(FormatList{});\n    check(FormatArray{});\n    check(FormatPair{});\n    check(FormatUnorderedMap{});\n  }\n\n  CHECK(Options::Parser<tmpl::list<ScalarWithLimits>>(\"\").help() == R\"(\n==== Description of expected options:\n\n\nOptions:\n  ScalarWithLimits:\n    type=int\n    suggested=7\n    min=2\n    max=8\n    ScalarHelp\n\n)\");\n  CHECK(Options::Parser<tmpl::list<VectorWithLimits>>(\"\").help() == R\"(\n==== Description of expected options:\n\n\nOptions:\n  VectorWithLimits:\n    type=[int, ...]\n    min size=5\n    max size=9\n    VectorHelp\n\n)\");\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<FormatUnorderedMap>> opts(\"\");\n        opts.parse(\"FormatUnorderedMap: X\");\n        opts.get<FormatUnorderedMap>();\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert value to type {[int x0]: [double x0]}:\"));\n\n  {\n    const auto help = Options::Parser<tmpl::list<SuggestedBool>>(\"\").help();\n    CAPTURE(help);\n    CHECK(help.find(\"suggested=false\") != std::string::npos);\n  }\n}\n\nstruct ExplicitObject {\n  explicit ExplicitObject() = default;\n};\n\nstruct ExplicitObjectTag {\n  using type = ExplicitObject;\n  static constexpr Options::String help = {\"halp\"};\n};\n}  // namespace\n\ntemplate <>\nstruct Options::create_from_yaml<ExplicitObject> {\n  template <typename Metavariables>\n  static ExplicitObject create(const Options::Option& /*options*/) {\n    return ExplicitObject{};\n  }\n};\n\nnamespace {\nvoid test_options_explicit_constructor() {\n  Options::Parser<tmpl::list<ExplicitObjectTag>> opts(\"\");\n  opts.parse(\"ExplicitObjectTag:\");\n  opts.get<ExplicitObjectTag>();\n}\n\nvoid test_options_input_source() {\n  Options::Parser<tmpl::list<Simple>> parser(\"\");\n  const std::string source = \"Simple: 3\";\n  const std::string overlay = \"Simple: 4\";\n  parser.parse(source);\n  parser.overlay<tmpl::list<Simple>>(overlay);\n  CHECK(parser.get<Options::Tags::InputSource>() ==\n        std::vector{source, overlay});\n}\n\nvoid check_for_lines(const std::string& text,\n                     const std::vector<std::string>& lines) {\n  CAPTURE(text);\n  const std::string search_text = \"\\n\" + text + \"\\n\";\n  size_t pos = 0;\n  for (const auto& line : lines) {\n    CAPTURE(line);\n    pos = search_text.find(\"\\n\" + line + \"\\n\");\n    if (pos == search_text.npos) {\n      CHECK(false);\n      // After failing to find a line none of the remaining lines will\n      // be found after it (because we are at the end of the string),\n      // so avoid printing an error for each of them.\n      break;\n    }\n  }\n}\n\nstruct Alternatives {\n  struct A {\n    using type = double;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct B {\n    using type = int;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct C {\n    using type = int;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct D {\n    using type = std::vector<int>;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct E {\n    using type = bool;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct F {\n    using type = std::string;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  static constexpr Options::String help = \"halp\";\n  Alternatives() = default;\n\n  // [alternatives]\n  using options = tmpl::list<\n      A, Options::Alternatives<tmpl::list<B>, tmpl::list<C>, tmpl::list<D, E>>,\n      F>;\n  Alternatives(tmpl::list<A, B, F> /*meta*/, double a, int b, std::string f)\n      : a_(a), b_(b), f_(std::move(f)) {}\n  Alternatives(tmpl::list<A, C, F> /*meta*/, double a, int c, std::string f)\n      : a_(a), c_(c), f_(std::move(f)) {}\n  Alternatives(double a, std::vector<int> d, bool e, std::string f)\n      : a_(a), d_(std::move(d)), e_(e), f_(std::move(f)) {}\n  // [alternatives]\n\n  double a_{-1.0};\n  int b_{-1};\n  int c_{-1};\n  std::vector<int> d_{};\n  bool e_{false};\n  std::string f_{};\n};\n\nstruct AlternativesTag {\n  using type = Alternatives;\n  static constexpr Options::String help = \"halp\";\n};\n\nstruct NestedAlternatives {\n  struct A {\n    using type = int;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct B {\n    using type = double;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct C {\n    using type = std::string;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct D {\n    using type = std::vector<int>;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  struct E {\n    using type = bool;\n    static constexpr Options::String help = \"halp\";\n  };\n\n  static constexpr Options::String help = \"halp\";\n  using options = tmpl::list<Options::Alternatives<\n      tmpl::list<A, Options::Alternatives<tmpl::list<B>, tmpl::list<C>>>,\n      tmpl::list<D, E>>>;\n\n  NestedAlternatives() = default;\n  NestedAlternatives(int a, double b) : a_(a), b_(b) {}\n  NestedAlternatives(int a, std::string c) : a_(a), c_(std::move(c)) {}\n  NestedAlternatives(std::vector<int> d, bool e) : d_(std::move(d)), e_(e) {}\n\n  int a_{-1};\n  double b_{-1.0};\n  std::string c_{};\n  std::vector<int> d_{};\n  bool e_{false};\n};\n\nstruct NestedAlternativesTag {\n  using type = NestedAlternatives;\n  static constexpr Options::String help = \"halp\";\n};\n\nvoid test_options_alternatives() {\n  {\n    Options::Parser<tmpl::list<AlternativesTag>> parser(\"\");\n    parser.parse(\n        \"AlternativesTag:\\n\"\n        \"  A: 7.8\\n\"\n        \"  B: 4\\n\"\n        \"  F: first\\n\");\n    const auto result = parser.get<AlternativesTag>();\n    CHECK(result.a_ == 7.8);\n    CHECK(result.b_ == 4);\n    CHECK(result.c_ == -1);\n    CHECK(result.d_.empty());\n    CHECK(result.e_ == false);\n    CHECK(result.f_ == \"first\");\n  }\n  {\n    Options::Parser<tmpl::list<AlternativesTag>> parser(\"\");\n    parser.parse(\n        \"AlternativesTag:\\n\"\n        \"  C: 5\\n\"\n        \"  A: 7.8\\n\"\n        \"  F: second\\n\");\n    const auto result = parser.get<AlternativesTag>();\n    CHECK(result.a_ == 7.8);\n    CHECK(result.b_ == -1);\n    CHECK(result.c_ == 5);\n    CHECK(result.d_.empty());\n    CHECK(result.e_ == false);\n    CHECK(result.f_ == \"second\");\n  }\n  {\n    Options::Parser<tmpl::list<AlternativesTag>> parser(\"\");\n    parser.parse(\n        \"AlternativesTag:\\n\"\n        \"  A: 7.8\\n\"\n        \"  D: [2, 3, 5, 7, 11]\\n\"\n        \"  E: true\\n\"\n        \"  F: third\\n\");\n    const auto result = parser.get<AlternativesTag>();\n    CHECK(result.a_ == 7.8);\n    CHECK(result.b_ == -1);\n    CHECK(result.c_ == -1);\n    CHECK(result.d_ == std::vector<int>{2, 3, 5, 7, 11});\n    CHECK(result.e_ == true);\n    CHECK(result.f_ == \"third\");\n  }\n\n  {\n    Options::Parser<tmpl::list<NestedAlternativesTag>> parser(\"\");\n    parser.parse(\n        \"NestedAlternativesTag:\\n\"\n        \"  A: 3\\n\"\n        \"  B: 7.8\\n\");\n    const auto result = parser.get<NestedAlternativesTag>();\n    CHECK(result.a_ == 3);\n    CHECK(result.b_ == 7.8);\n    CHECK(result.c_.empty());\n    CHECK(result.d_.empty());\n    CHECK(result.e_ == false);\n  }\n  {\n    Options::Parser<tmpl::list<NestedAlternativesTag>> parser(\"\");\n    parser.parse(\n        \"NestedAlternativesTag:\\n\"\n        \"  C: hi\\n\"\n        \"  A: 3\\n\");\n    const auto result = parser.get<NestedAlternativesTag>();\n    CHECK(result.a_ == 3);\n    CHECK(result.b_ == -1.0);\n    CHECK(result.c_ == \"hi\");\n    CHECK(result.d_.empty());\n    CHECK(result.e_ == false);\n  }\n  {\n    Options::Parser<tmpl::list<NestedAlternativesTag>> parser(\"\");\n    parser.parse(\n        \"NestedAlternativesTag:\\n\"\n        \"  D: [2, 3, 5, 7, 11]\\n\"\n        \"  E: true\\n\");\n    const auto result = parser.get<NestedAlternativesTag>();\n    CHECK(result.a_ == -1);\n    CHECK(result.b_ == -1.0);\n    CHECK(result.c_.empty());\n    CHECK(result.d_ == std::vector<int>{2, 3, 5, 7, 11});\n    CHECK(result.e_ == true);\n  }\n\n  // clang-format off\n  check_for_lines(Options::Parser<Alternatives::options>(\"\").help(),\n                  {\"Options:\",\n                   \"  A:\",\n                   \"  EITHER\",\n                   \"    B:\",\n                   \"  OR\",\n                   \"    C:\",\n                   \"  OR\",\n                   \"    D:\",\n                   \"    E:\",\n                   \"  F:\"});\n  check_for_lines(Options::Parser<NestedAlternatives::options>(\"\").help(),\n                  {\"Options:\",\n                   \"  EITHER\",\n                   \"    A:\",\n                   \"    EITHER\",\n                   \"      B:\",\n                   \"    OR\",\n                   \"      C:\",\n                   \"  OR\",\n                   \"    D:\",\n                   \"    E:\"});\n  // clang-format on\n}\n\nvoid test_options_overlay() {\n  {\n    Options::Parser<tmpl::list<Simple, InnerGroupedTag, OuterGroupedTag>>\n        parser(\"\");\n    parser.parse(\n        \"Simple: 1\\n\"\n        \"OuterGroup:\\n\"\n        \"  OuterGroupedTag: 2\\n\"\n        \"  InnerGroup:\\n\"\n        \"    InnerGroupedTag: 3\\n\");\n    CHECK(parser.get<Simple>() == 1);\n    CHECK(parser.get<OuterGroupedTag>() == 2);\n    CHECK(parser.get<InnerGroupedTag>() == 3);\n    parser.overlay<tmpl::list<Simple>>(\"Simple: 4\");\n    CHECK(parser.get<Simple>() == 4);\n    CHECK(parser.get<OuterGroupedTag>() == 2);\n    CHECK(parser.get<InnerGroupedTag>() == 3);\n    parser.overlay<tmpl::list<Simple>>(\"\");\n    CHECK(parser.get<Simple>() == 4);\n    CHECK(parser.get<OuterGroupedTag>() == 2);\n    CHECK(parser.get<InnerGroupedTag>() == 3);\n    parser.overlay<tmpl::list<InnerGroupedTag>>(\n        \"OuterGroup:\\n\"\n        \"  InnerGroup:\\n\"\n        \"    InnerGroupedTag: 5\\n\");\n    CHECK(parser.get<Simple>() == 4);\n    CHECK(parser.get<OuterGroupedTag>() == 2);\n    CHECK(parser.get<InnerGroupedTag>() == 5);\n    parser.overlay<tmpl::list<Simple, OuterGroupedTag>>(\"Simple: 6\\n\");\n    CHECK(parser.get<Simple>() == 6);\n    CHECK(parser.get<OuterGroupedTag>() == 2);\n    CHECK(parser.get<InnerGroupedTag>() == 5);\n    parser.overlay<tmpl::list<Simple, OuterGroupedTag>>(\n        \"OuterGroup:\\n\"\n        \"  OuterGroupedTag: 7\\n\");\n    CHECK(parser.get<Simple>() == 6);\n    CHECK(parser.get<OuterGroupedTag>() == 7);\n    CHECK(parser.get<InnerGroupedTag>() == 5);\n    parser.overlay<tmpl::list<Simple, OuterGroupedTag>>(\n        \"Simple: 8\\n\"\n        \"OuterGroup:\\n\"\n        \"  OuterGroupedTag: 9\\n\");\n    CHECK(parser.get<Simple>() == 8);\n    CHECK(parser.get<OuterGroupedTag>() == 9);\n    CHECK(parser.get<InnerGroupedTag>() == 5);\n  }\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Simple>> parser(\"\");\n        parser.parse(\"Simple: 1\");\n        parser.overlay<tmpl::list<Simple>>(\"NotSimple: 2\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 1 column 1:\\nOption 'NotSimple' is not a valid option.\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Simple>> parser(\"\");\n        parser.parse(\"Simple: 1\");\n        parser.overlay<tmpl::list<>>(\"Simple: 2\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 1 column 1:\\nOption 'Simple' is not overlayable.\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<Simple>> parser(\"\");\n        parser.parse(\"Simple: 1\");\n        parser.overlay<tmpl::list<Simple>>(\n            \"Simple: 2\\n\"\n            \"Simple: 2\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"At line 2 column 1:\\nOption 'Simple' specified twice.\"));\n\n  CHECK_THROWS_WITH(\n      []() {\n        Options::Parser<tmpl::list<OuterGroupedTag>> parser(\"\");\n        parser.parse(\n            \"OuterGroup:\\n\"\n            \"  OuterGroupedTag: 1\");\n        parser.overlay<tmpl::list<OuterGroupedTag>>(\n            \"OuterGroup:\\n\"\n            \"  OuterGroupedTag: 2\\n\"\n            \"  OuterGroupedTag: 2\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"In group OuterGroup:\\nAt line 3 column 3:\\nOption \"\n          \"'OuterGroupedTag' specified twice.\"));\n}\n\nvoid test_options_serialization() {\n  {\n    // Test serialization of an unparsed parser.\n    Options::Parser<tmpl::list<Simple>> parser(\"passed help\");\n    auto parser2 = serialize_and_deserialize(parser);\n    CHECK(parser2.get<Options::Tags::InputSource>().empty());\n    CHECK(parser2.help() == parser.help());\n    const std::string source = \"Simple: 4\";\n    parser2.parse(source);\n    const auto parser3 = serialize_and_deserialize(parser2);\n    CHECK(parser3.get<Options::Tags::InputSource>() == std::vector{source});\n    CHECK(parser3.get<Simple>() == 4);\n  }\n\n  const auto check_repeated = [](const auto& parser1, const auto& f) {\n    f(parser1);\n    const auto parser2 = serialize_and_deserialize(parser1);\n    f(parser2);\n    const auto parser3 = serialize_and_deserialize(parser2);\n    f(parser3);\n  };\n\n  {\n    // Groups\n    Options::Parser<tmpl::list<InnerGroupedTag, OuterGroupedTag, Simple>>\n        parser(\"Overall help text\");\n    const std::string source =\n        \"OuterGroup:\\n\"\n        \"  InnerGroup:\\n\"\n        \"    InnerGroupedTag: 3\\n\"\n        \"  OuterGroupedTag: 1\\n\"\n        \"Simple: 2\\n\";\n    parser.parse(source);\n    check_repeated(parser, [&source](const decltype(parser)& local_parser) {\n      CHECK(local_parser.get<Options::Tags::InputSource>() ==\n            std::vector{source});\n      CHECK(local_parser.get<InnerGroupedTag>() == 3);\n      CHECK(local_parser.get<OuterGroupedTag>() == 1);\n      CHECK(local_parser.get<Simple>() == 2);\n    });\n  }\n  {\n    // Overlays\n    Options::Parser<tmpl::list<Simple>> parser(\"\");\n    const std::string source = \"Simple: 3\";\n    const std::string overlay = \"Simple: 4\";\n    parser.parse(source);\n    parser.overlay<tmpl::list<Simple>>(overlay);\n    check_repeated(parser,\n                   [&source, &overlay](const decltype(parser)& local_parser) {\n                     CHECK(local_parser.get<Options::Tags::InputSource>() ==\n                           std::vector{source, overlay});\n                     CHECK(local_parser.get<Simple>() == 4);\n                   });\n  }\n  {\n    // Groups + Overlays\n    Options::Parser<tmpl::list<Simple, InnerGroupedTag, OuterGroupedTag>>\n        parser(\"\");\n    const std::string source =\n        \"Simple: 1\\n\"\n        \"OuterGroup:\\n\"\n        \"  OuterGroupedTag: 2\\n\"\n        \"  InnerGroup:\\n\"\n        \"    InnerGroupedTag: 3\\n\";\n    const std::string overlay =\n        \"OuterGroup:\\n\"\n        \"  InnerGroup:\\n\"\n        \"    InnerGroupedTag: 5\\n\";\n    parser.parse(source);\n    parser.overlay<tmpl::list<InnerGroupedTag>>(overlay);\n    check_repeated(parser,\n                   [&source, &overlay](const decltype(parser)& local_parser) {\n                     CHECK(local_parser.get<Options::Tags::InputSource>() ==\n                           std::vector{source, overlay});\n                     CHECK(local_parser.get<Simple>() == 1);\n                     CHECK(local_parser.get<OuterGroupedTag>() == 2);\n                     CHECK(local_parser.get<InnerGroupedTag>() == 5);\n                   });\n  }\n  {\n    // Alternatives\n    Options::Parser<tmpl::list<AlternativesTag>> parser(\"\");\n    const std::string source =\n        \"AlternativesTag:\\n\"\n        \"  A: 1.2\\n\"\n        \"  C: 7\\n\"\n        \"  F: required\\n\";\n    parser.parse(source);\n    check_repeated(parser, [&source](const decltype(parser)& local_parser) {\n      CHECK(local_parser.get<Options::Tags::InputSource>() ==\n            std::vector{source});\n      const auto result = local_parser.get<AlternativesTag>();\n      CHECK(result.a_ == 1.2);\n      CHECK(result.b_ == -1);\n      CHECK(result.c_ == 7);\n      CHECK(result.d_.empty());\n      CHECK(result.e_ == false);\n      CHECK(result.f_ == \"required\");\n    });\n  }\n}\n\nvoid test_load_and_check_yaml() {\n  INFO(\"Load and check YAML\");\n  {\n    const auto options = Options::detail::load_and_check_yaml(\"X: 1\", false);\n    CHECK(options[\"X\"].as<size_t>() == 1);\n  }\n  CHECK_THROWS_WITH(\n      []() { Options::detail::load_and_check_yaml(\"X: 1\", true); }(),\n      Catch::Matchers::ContainsSubstring(\"Missing metadata\"));\n  {\n    const auto options = Options::detail::load_and_check_yaml(\n        \"---\\n\"\n        \"---\\n\"\n        \"X: 1\\n\",\n        true);\n    CHECK(options[\"X\"].as<size_t>() == 1);\n  }\n  {\n    const auto options = Options::detail::load_and_check_yaml(\n        \"Executable: Test_Options\\n\"\n        \"---\\n\"\n        \"X: 1\\n\",\n        true);\n    CHECK(options[\"X\"].as<size_t>() == 1);\n  }\n  CHECK_THROWS_WITH(\n      []() {\n        Options::detail::load_and_check_yaml(\n            \"Executable: MyExec\\n\"\n            \"---\\n\"\n            \"X: 1\\n\",\n            true);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"the running executable is\"));\n}\n\ntemplate <typename T>\nstruct GenericTag {\n  using type = T;\n  static constexpr Options::String help = \"halp\";\n};\n\nvoid test_integer_scientific() {\n  tmpl::for_each<tmpl::list<int, unsigned, size_t, int64_t, uint64_t>>(\n      []<typename T>(tmpl::type_<T> /*meta*/) {\n        INFO(pretty_type::name<T>());\n        const auto check = [](const std::string& input, const T& expected) {\n          CAPTURE(input);\n          Options::Parser<tmpl::list<GenericTag<T>>> parser(\"\");\n          parser.parse(\"GenericTag: \" + input);\n          CHECK(parser.template get<GenericTag<T>>() == expected);\n        };\n        const auto check_fail = [](const std::string& input) {\n          CAPTURE(input);\n          Options::Parser<tmpl::list<GenericTag<T>>> parser(\"\");\n          parser.parse(\"GenericTag: \" + input);\n          CHECK_THROWS_WITH(parser.template get<GenericTag<T>>(),\n                            Catch::Matchers::ContainsSubstring(\n                                \"Failed to convert value to type\"));\n        };\n\n        check(\"1234\", 1234);\n        check(\"1e8\", 100'000'000);\n        check(\"1.23e5\", 123000);\n        if constexpr (std::is_signed_v<T>) {\n          check(\"-1234\", -1234);\n          check(\"-1e8\", -100'000'000);\n          check(\"-1.23e5\", -123000);\n        } else {\n          check_fail(\"-1234\");\n          check_fail(\"-1e8\");\n          check_fail(\"-1.23e5\");\n        }\n        check_fail(\"1.2\");\n        check_fail(\"\");\n        check_fail(\"bla\");\n        check_fail(\"1e-3\");\n        check_fail(\"1e100\");\n      });\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Options\", \"[Unit][Options]\") {\n  test_options_empty();\n  test_options_syntax_error();\n  test_options_simple();\n  test_options_print_long_help();\n  test_options_grouped();\n  test_options_suggested();\n  test_options_bounded();\n  test_options_bounded_vector();\n  test_options_array();\n  test_options_map();\n  test_options_unordered_map();\n  test_options_variant();\n  test_options_complex_containers();\n  test_options_invalid_calls();\n  test_options_apply();\n  test_options_option_context_default_stream();\n  test_options_format();\n  test_options_explicit_constructor();\n  test_options_input_source();\n  test_options_alternatives();\n  test_options_overlay();\n  test_options_serialization();\n  test_load_and_check_yaml();\n  test_integer_scientific();\n}\n"
  },
  {
    "path": "tests/Unit/Options/Test_StdComplex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/StdComplex.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Options.StdParsers\", \"[Unit][Options]\") {\n  CHECK(std::complex<double>(1.2, 2.4) ==\n        TestHelpers::test_creation<std::complex<double>>(\"[1.2, 2.4]\"));\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/ArrayCollection/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  ArrayCollection/Test_IsDgElementArrayMember.cpp\n  ArrayCollection/Test_IsDgElementCollection.cpp\n  ArrayCollection/Test_Tags.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Parallel/ArrayCollection/Test_IsDgElementArrayMember.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Parallel/ArrayCollection/DgElementArrayMemberBase.hpp\"\n#include \"Parallel/ArrayCollection/IsDgElementArrayMember.hpp\"\n\nnamespace Parallel {\nSPECTRE_TEST_CASE(\"Unit.Parallel.ArrayCollection.IsDgElementArrayMember\",\n                  \"[Unit][Parallel]\") {\n  CHECK(is_dg_element_array_member_v<DgElementArrayMemberBase<3>>);\n  CHECK(\n      is_dg_element_array_member_v<DgElementArrayMember<3, void, void, void>>);\n  CHECK_FALSE(is_dg_element_array_member_v<int>);\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "tests/Unit/Parallel/ArrayCollection/Test_IsDgElementCollection.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Parallel/ArrayCollection/IsDgElementCollection.hpp\"\n\nnamespace Parallel {\nSPECTRE_TEST_CASE(\"Unit.Parallel.ArrayCollection.IsDgElementCollection\",\n                  \"[Unit][Parallel]\") {\n  CHECK(is_dg_element_collection_v<DgElementCollection<3, void, void>>);\n  CHECK_FALSE(is_dg_element_collection_v<int>);\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "tests/Unit/Parallel/ArrayCollection/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementCollection.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocations.hpp\"\n#include \"Parallel/ArrayCollection/Tags/ElementLocationsReference.hpp\"\n#include \"Parallel/ArrayCollection/Tags/NumberOfElementsTerminated.hpp\"\n\nnamespace Parallel {\nSPECTRE_TEST_CASE(\"Unit.Parallel.ArrayCollection.Tags\", \"[Unit][Parallel]\") {\n  TestHelpers::db::test_simple_tag<\n      Tags::ElementCollection<3, void, void, void>>(\"ElementCollection\");\n  TestHelpers::db::test_simple_tag<Tags::ElementLocations<3>>(\n      \"ElementLocations\");\n  TestHelpers::db::test_reference_tag<\n      Tags::ElementLocationsReference<3, void, void>>(\"ElementLocations\");\n  TestHelpers::db::test_simple_tag<Tags::NumberOfElementsTerminated>(\n      \"NumberOfElementsTerminated\");\n}\n\n}  // namespace Parallel\n"
  },
  {
    "path": "tests/Unit/Parallel/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(PhaseControl)\nadd_subdirectory(Printf)\n\nset(ALGORITHM_TEST_LINK_LIBRARIES\n  Actions\n  Charmxx::main\n  DomainStructure\n  ErrorHandling\n  Evolution\n  H5\n  Informer\n  Observer\n  Options\n  Parallel\n  ParallelHelpers\n  PhaseControl\n  Printf\n  Serialization\n  Spectral\n  SystemUtilities\n  Time\n  Utilities\n  )\n\n# Add unit tests. For tests that result in a failure it is necessary to\n# redirect output from stderr to stdout. However, it was necessary at least on\n# some systems to do this redirect inside a shell command.\nfind_program(SHELL_EXECUTABLE \"sh\")\nif (NOT SHELL_EXECUTABLE)\n  message(FATAL_ERROR\n    \"Could not find 'sh' shell to execute failure tests of algorithms\")\nendif()\n\n# Run TEST_NAME twice: first with no command-line args, then with the\n# `+restart` command-line arg to restart from the written Charm++\n# checkpoint. The checkpoint file is then deleted\n# to avoid it getting in the way of future test invocations (which would try\n# to overwrite the prior checkpoint file and error).\nfunction(add_algorithm_test_with_restart_from_checkpoint TEST_NAME)\n  add_standalone_test_executable(\"Test_${TEST_NAME}\")\n  target_link_libraries(\n    \"Test_${TEST_NAME}\"\n    PRIVATE\n    \"${ALGORITHM_TEST_LINK_LIBRARIES}\")\n  # Executable writes checkpoint file into the dir where it runs:\n  set(CHECKPOINT\n    \"${CMAKE_BINARY_DIR}/tests/Unit/Parallel/${TEST_NAME}/\\\nCheckpoints/Checkpoint_0000\")\n  add_test(\n    NAME \"Unit.Parallel.${TEST_NAME}\"\n    COMMAND\n    ${SHELL_EXECUTABLE}\n    -c\n    \"mkdir -p ${TEST_NAME} \\\n    && cd ${TEST_NAME} \\\n    && rm -rf ${CHECKPOINT} \\\n    && TEST_IN_RESTART= ${SPECTRE_TEST_RUNNER} \\\n       ${CMAKE_BINARY_DIR}/bin/Test_${TEST_NAME} 2>&1 \\\n    && TEST_IN_RESTART=1 ${SPECTRE_TEST_RUNNER} \\\n       ${CMAKE_BINARY_DIR}/bin/Test_${TEST_NAME} +restart ${CHECKPOINT} 2>&1 \\\n    && rm -rf ${CHECKPOINT} \\\n    && cd ..\"\n    )\n  set_standalone_test_properties(\"Unit.Parallel.${TEST_NAME}\")\nendfunction()\n\nfunction(add_algorithm_message_test TEST_NAME)\n  add_standalone_test_executable(\"Test_${TEST_NAME}\")\n  target_link_libraries(\n    \"Test_${TEST_NAME}\"\n    PRIVATE\n    \"${ALGORITHM_TEST_LINK_LIBRARIES}\")\n  if (CHARM_USE_MPI)\n    set(CHARM_ARGS \"+n2 +p2\")\n  else()\n    set(CHARM_ARGS \"\")\n  endif()\n  add_test(\n    NAME \"Unit.Parallel.${TEST_NAME}\"\n    COMMAND\n    ${SHELL_EXECUTABLE}\n    -c\n    \"${CMAKE_BINARY_DIR}/bin/charmrun ${CMAKE_BINARY_DIR}/bin/Test_${TEST_NAME} \\\n        ${CHARM_ARGS} 2>&1\"\n    )\n  set_standalone_test_properties(\"Unit.Parallel.${TEST_NAME}\")\nendfunction()\n\nfunction(add_algorithm_test BASE_NAME)\n  add_standalone_test(\"Unit.Parallel.${BASE_NAME}\" ${ARGN})\n  target_link_libraries(\n    \"Test_${BASE_NAME}\"\n    PRIVATE\n    \"${ALGORITHM_TEST_LINK_LIBRARIES}\")\nendfunction()\n\n# Test GlobalCache\nadd_algorithm_test(\"GlobalCache\")\nadd_charm_module(Test_GlobalCache)\n\nadd_dependencies(\n  module_Test_GlobalCache\n  module_GlobalCache\n  )\n\nadd_dependencies(\n  Test_GlobalCache\n  module_Test_GlobalCache\n  )\n\nadd_algorithm_message_test(\"AlgorithmMessages\")\nadd_algorithm_test(\"AlgorithmCore\")\nadd_algorithm_test(\"AlgorithmLocalSyncAction\")\nadd_algorithm_test(\n  \"AlgorithmNestedApply1\"\n  REGEX_TO_MATCH\n  \"Already performing an Action and cannot execute additional Actions \\\nfrom inside of an Action. This is only possible if the simple_action \\\nfunction is not invoked via a proxy, which we do not allow.\")\nadd_algorithm_test(\n  \"AlgorithmNestedApply2\"\n  REGEX_TO_MATCH\n  \"Already performing an Action and cannot execute additional Actions \\\nfrom inside of an Action. This is only possible if the simple_action \\\nfunction is not invoked via a proxy, which we do not allow.\")\nadd_algorithm_test(\"AlgorithmNodelock\")\nadd_algorithm_test(\"AlgorithmParallel\"\n  INPUT_FILE \"Test_AlgorithmParallel.yaml\")\nadd_algorithm_test(\"AlgorithmReduction\")\nadd_algorithm_test(\"Callback\")\nadd_algorithm_test(\"DetectHangArray\"\n  REGEX_TO_MATCH\n  \"The following components did not terminate cleanly:\")\nadd_algorithm_test(\"DynamicInsertion\")\nadd_algorithm_test(\"PhaseChangeMain\")\nadd_algorithm_test(\n  \"SectionReductions\"\n  REGEX_TO_MATCH\n  \"Element 0 received reduction: Counted 2 odd elements.\")\n\nadd_algorithm_test(\n  \"AlgorithmGlobalCache\"\n  INPUT_FILE \"Test_AlgorithmGlobalCache.yaml\")\nadd_algorithm_test(\n  \"AlgorithmPhaseControlSingleton\"\n  INPUT_FILE \"Test_AlgorithmPhaseControlSingleton.yaml\")\nadd_algorithm_test(\n  \"AlgorithmPhaseControlNodegroup\"\n  INPUT_FILE \"Test_AlgorithmPhaseControlNodegroup.yaml\")\nadd_algorithm_test(\n  \"DynamicInsertionState\"\n  INPUT_FILE \"Test_DynamicInsertionState.yaml\")\n\nadd_algorithm_test_with_restart_from_checkpoint(\"CheckpointRestart\")\n\n# Tests that do not require their own Chare setup and can work with the\n# unit tests\nset(LIBRARY \"Test_Parallel\")\n\nset(LIBRARY_SOURCES\n  Test_ArrayComponentId.cpp\n  Test_DomainDiagnosticInfo.cpp\n  Test_ExitCode.cpp\n  Test_GlobalCacheDataBox.cpp\n  Test_FifoCache.cpp\n  Test_InboxInserters.cpp\n  Test_InitializationTag.cpp\n  Test_MemoryMonitor.cpp\n  Test_MultiReaderSpinlock.cpp\n  Test_NodeLock.cpp\n  Test_OutputInbox.cpp\n  Test_Parallel.cpp\n  Test_ParallelComponentHelpers.cpp\n  Test_Phase.cpp\n  Test_ResourceInfo.cpp\n  Test_StaticSpscQueue.cpp\n  Test_TypeTraits.cpp\n  )\n\nadd_subdirectory(ArrayCollection)\nadd_subdirectory(Tags)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\" WITH_CHARM)\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Actions\n  DataStructures\n  DataStructuresHelpers\n  DomainStructure\n  ObserverHelpers\n  Options\n  Parallel\n  Serialization\n  SystemUtilities\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/Parallel/PhaseControl/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_PhaseControl\")\n\nset(LIBRARY_SOURCES\n  Test_CheckpointAndExitAfterWallclock.cpp\n  Test_ExecutePhaseChange.cpp\n  Test_PhaseChange.cpp\n  Test_PhaseControlTags.cpp\n  Test_VisitAndReturn.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\" WITH_CHARM)\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Options\n  Parallel\n  PhaseControl\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Parallel/PhaseControl/Test_CheckpointAndExitAfterWallclock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ExitCode.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<PhaseChange,\n                   tmpl::list<PhaseControl::CheckpointAndExitAfterWallclock>>,\n        tmpl::pair<Trigger, tmpl::list<Triggers::Always>>>;\n  };\n\n};\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.PhaseControl.CheckpointAndExitAfterWallclock\",\n                  \"[Unit][Parallel]\") {\n  // note that the `contribute_phase_data_impl` function is currently untested\n  // in this unit test, because we do not have good support for reductions in\n  // the action testing framework.\n\n  const auto created_phase_changes = TestHelpers::test_option_tag<\n      PhaseControl::OptionTags::PhaseChangeAndTriggers, Metavariables>(\n      \" - Trigger: Always\\n\"\n      \"   PhaseChanges:\\n\"\n      \"     - CheckpointAndExitAfterWallclock:\\n\"\n      \"         WallclockHours: 0.0\");\n\n  Parallel::GlobalCache<Metavariables> cache{};\n\n  using PhaseChangeDecisionData = tuples::tagged_tuple_from_typelist<\n      PhaseControl::get_phase_change_tags<Metavariables>>;\n\n  const PhaseControl::CheckpointAndExitAfterWallclock phase_change0(0.0);\n  const PhaseControl::CheckpointAndExitAfterWallclock phase_change1(1.0);\n  {\n    INFO(\"Test initialize phase change decision data\");\n    PhaseChangeDecisionData phase_change_decision_data{\n        Parallel::Phase::Execute, true, true, Parallel::ExitCode::Complete};\n    phase_change0.initialize_phase_data<Metavariables>(\n        make_not_null(&phase_change_decision_data));\n    CHECK(phase_change_decision_data ==\n          PhaseChangeDecisionData{std::nullopt, false, true,\n                                  Parallel::ExitCode::Complete});\n  }\n  {\n    INFO(\"Wallclock time < big trigger time\");\n    // Check behavior when a checkpoint-and-exit has been requested\n    // First check case where wallclock time < trigger wallclock time, using\n    // the PhaseChange with a big trigger time.\n    // (this assumes the test doesn't take 1h to get here)\n    PhaseChangeDecisionData phase_change_decision_data{\n        std::nullopt, true, true, Parallel::ExitCode::Complete};\n    const auto decision_result = phase_change1.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Execute,\n        cache);\n    CHECK(decision_result == std::nullopt);\n    CHECK(phase_change_decision_data ==\n          PhaseChangeDecisionData{std::nullopt, false, true,\n                                  Parallel::ExitCode::Complete});\n  }\n  {\n    INFO(\"Wallclock time > small trigger time\");\n    // Now check case where wallclock time > trigger wallclock time, using\n    // the PhaseChange with a tiny trigger time.\n    // (this assumes the test takes at least a few cycles to get here)\n    PhaseChangeDecisionData phase_change_decision_data{\n        std::nullopt, true, true, Parallel::ExitCode::Complete};\n    const auto decision_result = phase_change0.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Execute,\n        cache);\n    CHECK(\n        decision_result ==\n        std::make_pair(Parallel::Phase::WriteCheckpoint,\n                       PhaseControl::ArbitrationStrategy::RunPhaseImmediately));\n    CHECK(phase_change_decision_data ==\n          PhaseChangeDecisionData{Parallel::Phase::Execute, false, true,\n                                  Parallel::ExitCode::Complete});\n  }\n  {\n    INFO(\"Restarting from checkpoint\");\n    // Check behavior following the checkpoint phase\n    const PhaseControl::CheckpointAndExitAfterWallclock phase_change_restart =\n        serialize_and_deserialize(phase_change0);\n    PhaseChangeDecisionData phase_change_decision_data{\n        Parallel::Phase::Execute, false, true, Parallel::ExitCode::Complete};\n    auto decision_result = phase_change_restart.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data),\n        Parallel::Phase::WriteCheckpoint, cache);\n    CHECK(decision_result ==\n          std::make_pair(\n              Parallel::Phase::UpdateOptionsAtRestartFromCheckpoint,\n              PhaseControl::ArbitrationStrategy::PermitAdditionalJumps));\n    CHECK(phase_change_decision_data ==\n          PhaseChangeDecisionData{Parallel::Phase::Execute, false, true,\n                                  Parallel::ExitCode::Complete});\n\n    // Next, from update phase, go to Restart\n    decision_result = phase_change_restart.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data),\n        Parallel::Phase::UpdateOptionsAtRestartFromCheckpoint, cache);\n    CHECK(decision_result ==\n          std::make_pair(\n              Parallel::Phase::Restart,\n              PhaseControl::ArbitrationStrategy::PermitAdditionalJumps));\n    CHECK(phase_change_decision_data ==\n          PhaseChangeDecisionData{Parallel::Phase::Execute, false, true,\n                                  Parallel::ExitCode::Complete});\n\n    // Finally, go back to Execute\n    decision_result = phase_change_restart.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Restart,\n        cache);\n    CHECK(decision_result ==\n          std::make_pair(\n              Parallel::Phase::Execute,\n              PhaseControl::ArbitrationStrategy::PermitAdditionalJumps));\n    CHECK(phase_change_decision_data ==\n          PhaseChangeDecisionData{std::nullopt, false, true,\n                                  Parallel::ExitCode::Complete});\n  }\n  {\n    INFO(\"Exiting after checkpoint\");\n    PhaseChangeDecisionData phase_change_decision_data{\n        Parallel::Phase::Execute, false, true, Parallel::ExitCode::Complete};\n    const auto decision_result = phase_change0.arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data),\n        Parallel::Phase::WriteCheckpoint, cache);\n    CHECK(\n        decision_result ==\n        std::make_pair(Parallel::Phase::Exit,\n                       PhaseControl::ArbitrationStrategy::RunPhaseImmediately));\n    CHECK(phase_change_decision_data ==\n          PhaseChangeDecisionData{Parallel::Phase::Execute, false, true,\n                                  Parallel::ExitCode::ContinueFromCheckpoint});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/PhaseControl/Test_ExecutePhaseChange.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/InitializePhaseChangeDecisionData.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\n\nnamespace TestTags {\ntemplate <PhaseControl::ArbitrationStrategy, size_t Index>\nstruct Request {\n  using type = bool;\n\n  using combine_method = funcl::Or<>;\n  using main_combine_method = funcl::Or<>;\n};\n}  // namespace TestTags\n\ntemplate <PhaseControl::ArbitrationStrategy Strategy, size_t Index>\nstruct TestPhaseChange : public PhaseChange {\n  TestPhaseChange() = default;\n  explicit TestPhaseChange(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TestPhaseChange);  // NOLINT\n  static std::string name() {\n    if constexpr (Strategy ==\n                  PhaseControl::ArbitrationStrategy::RunPhaseImmediately) {\n      if constexpr (Index == 0_st) {\n        return \"TestPhaseChange(RunPhaseImmediately, 0)\";\n      } else {\n        return \"TestPhaseChange(RunPhaseImmediately, 1)\";\n      }\n    } else {\n      if constexpr (Index == 0_st) {\n        return \"TestPhaseChange(PermitAdditionalJumps, 0)\";\n      } else if constexpr (Index == 1_st) {\n        return \"TestPhaseChange(PermitAdditionalJumps, 1)\";\n      } else {\n        return \"TestPhaseChange(PermitAdditionalJumps, 2)\";\n      }\n    }\n  }\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help{\"Phase change tester\"};\n\n  using argument_tags = tmpl::list<>;\n  using return_tags = tmpl::list<>;\n\n  using phase_change_tags_and_combines =\n      tmpl::list<TestTags::Request<Strategy, Index>>;\n  template <typename Metavariables>\n  using participating_components = tmpl::list<>;\n\n  template <typename... DecisionTags>\n  void initialize_phase_data_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data) const {\n    tuples::get<TestTags::Request<Strategy, Index>>(\n        *phase_change_decision_data) =\n        (Index % 2 == 0) xor\n        (Strategy == PhaseControl::ArbitrationStrategy::RunPhaseImmediately);\n  }\n\n  template <typename ParallelComponent, typename Metavariables,\n            typename ArrayIndex>\n  void contribute_phase_data_impl(\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/) const {}\n\n  template <typename... DecisionTags, typename Metavariables>\n  typename std::optional<\n      std::pair<Parallel::Phase, PhaseControl::ArbitrationStrategy>>\n  arbitrate_phase_change_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data,\n      const Parallel::Phase /*current_phase*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/) const {\n    if (tuples::get<TestTags::Request<Strategy, Index>>(\n            *phase_change_decision_data)) {\n      tuples::get<TestTags::Request<Strategy, Index>>(\n          *phase_change_decision_data) = false;\n      // Choose a unique phase, all after the first phase, for each choice of\n      // the template parameters.\n      return std::make_pair(\n          gsl::at(Metavariables::default_phase_order,\n                  1_st + static_cast<size_t>(Strategy) + Index * 2_st),\n          Strategy);\n    } else {\n      return std::nullopt;\n    }\n  }\n\n  void pup(PUP::er& /*p*/) override {}  // NOLINT\n};\n\ntemplate <PhaseControl::ArbitrationStrategy Strategy, size_t Index>\nPUP::able::PUP_ID TestPhaseChange<Strategy, Index>::my_PUP_ID = 0;\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        PhaseChange,\n        tmpl::list<\n            TestPhaseChange<\n                PhaseControl::ArbitrationStrategy::RunPhaseImmediately, 0_st>,\n            TestPhaseChange<\n                PhaseControl::ArbitrationStrategy::RunPhaseImmediately, 1_st>,\n            TestPhaseChange<\n                PhaseControl::ArbitrationStrategy::PermitAdditionalJumps, 0_st>,\n            TestPhaseChange<\n                PhaseControl::ArbitrationStrategy::PermitAdditionalJumps, 1_st>,\n            TestPhaseChange<\n                PhaseControl::ArbitrationStrategy::PermitAdditionalJumps,\n                2_st>>>>;\n  };\n  using const_global_cache_tags =\n      tmpl::list<PhaseControl::Tags::PhaseChangeAndTriggers>;\n\n  static constexpr std::array<Parallel::Phase, 7> default_phase_order{\n      {Parallel::Phase::Register, Parallel::Phase::Testing,\n       Parallel::Phase::Solve, Parallel::Phase::ImportInitialData,\n       Parallel::Phase::Evolve, Parallel::Phase::Execute,\n       Parallel::Phase::Cleanup}};\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.PhaseControl.ExecutePhaseChange\",\n                  \"[Unit][Parallel]\") {\n  // for a test of the `ExecutePhaseChange` action, see\n  // `tests/Unit/Parallel/Test_AlgorithmPhaseControl.hpp`. Currently, there is\n  // no way to test interactions with the `Main` chare via the action testing\n  // framework.\n  using phase_change_decision_data_type = tuples::TaggedTuple<\n      ::TestTags::Request<\n          PhaseControl::ArbitrationStrategy::RunPhaseImmediately, 0_st>,\n      ::TestTags::Request<\n          PhaseControl::ArbitrationStrategy::PermitAdditionalJumps, 0_st>,\n      ::TestTags::Request<\n          PhaseControl::ArbitrationStrategy::RunPhaseImmediately, 1_st>,\n      ::TestTags::Request<\n          PhaseControl::ArbitrationStrategy::PermitAdditionalJumps, 1_st>,\n      ::TestTags::Request<\n          PhaseControl::ArbitrationStrategy::PermitAdditionalJumps, 2_st>,\n      PhaseControl::TagsAndCombines::UsePhaseChangeArbitration>;\n\n  phase_change_decision_data_type phase_change_data{true, true, true,\n                                                    true, true, true};\n\n  std::vector<std::unique_ptr<PhaseChange>> phase_change_vector;\n  phase_change_vector.reserve(5);\n  phase_change_vector.emplace_back(\n      std::make_unique<TestPhaseChange<\n          PhaseControl::ArbitrationStrategy::RunPhaseImmediately, 0_st>>());\n  phase_change_vector.emplace_back(\n      std::make_unique<TestPhaseChange<\n          PhaseControl::ArbitrationStrategy::PermitAdditionalJumps, 0_st>>());\n  phase_change_vector.emplace_back(\n      std::make_unique<TestPhaseChange<\n          PhaseControl::ArbitrationStrategy::RunPhaseImmediately, 1_st>>());\n  phase_change_vector.emplace_back(\n      std::make_unique<TestPhaseChange<\n          PhaseControl::ArbitrationStrategy::PermitAdditionalJumps, 1_st>>());\n  phase_change_vector.emplace_back(\n      std::make_unique<TestPhaseChange<\n          PhaseControl::ArbitrationStrategy::PermitAdditionalJumps, 2_st>>());\n\n  using phase_change_and_triggers = PhaseControl::Tags::PhaseChangeAndTriggers;\n  phase_change_and_triggers::type vector_of_triggers_and_phase_changes;\n  vector_of_triggers_and_phase_changes.push_back(\n      {static_cast<std::unique_ptr<Trigger>>(\n           std::make_unique<Triggers::Always>()),\n       std::move(phase_change_vector)});\n\n  Parallel::GlobalCache<Metavariables> global_cache{\n      tuples::TaggedTuple<phase_change_and_triggers>{\n          std::move(vector_of_triggers_and_phase_changes)}};\n\n  {\n    INFO(\"Initialize phase change decision data\");\n    PhaseControl::initialize_phase_change_decision_data(\n        make_not_null(&phase_change_data), global_cache);\n    // checking against the formula:\n    // (Index % 2 == 0) xor\n    // (Strategy == PhaseControl::ArbitrationStrategy::RunPhaseImmediately)\n    CHECK(phase_change_data == phase_change_decision_data_type{\n                                   false, true, true, false, true, false});\n  }\n  {\n    INFO(\"Arbitrate based on phase change decision data\");\n    // if used, the phase change objects correspond to phases:\n    // Testing, Solve, ImportInitialData, Evolve, Cleanup  (in that\n    // order)\n    phase_change_data = phase_change_decision_data_type{false, false, false,\n                                                        false, false, false};\n    CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n              make_not_null(&phase_change_data), Parallel::Phase::Register,\n              global_cache)) == std::nullopt);\n    CHECK(phase_change_data == SINGLE_ARG(phase_change_decision_data_type{\n                                   false, false, false, false, false, false}));\n\n    phase_change_data = phase_change_decision_data_type{false, false, false,\n                                                        false, false, true};\n    CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n              make_not_null(&phase_change_data), Parallel::Phase::Register,\n              global_cache)) == Parallel::Phase::Register);\n    CHECK(phase_change_data == SINGLE_ARG(phase_change_decision_data_type{\n                                   false, false, false, false, false, false}));\n\n    phase_change_data =\n        phase_change_decision_data_type{true, false, true, true, false, true};\n    // test the versions that use `RunPhaseImmediately`, so the phase jumps to\n    // those phases without evaluating the other phase change objects\n    CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n              make_not_null(&phase_change_data), Parallel::Phase::Register,\n              global_cache)) == Parallel::Phase::Testing);\n    CHECK(phase_change_data == SINGLE_ARG(phase_change_decision_data_type{\n                                   false, false, true, true, false, false}));\n    phase_change_data =\n        phase_change_decision_data_type{true, false, true, true, false, true};\n    CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n              make_not_null(&phase_change_data), Parallel::Phase::Register,\n              global_cache)) == Parallel::Phase::Testing);\n    CHECK(phase_change_data == SINGLE_ARG(phase_change_decision_data_type{\n                                   false, false, true, true, false, false}));\n    CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n              make_not_null(&phase_change_data), Parallel::Phase::Register,\n              global_cache)) == Parallel::Phase::ImportInitialData);\n    CHECK(phase_change_data == SINGLE_ARG(phase_change_decision_data_type{\n                                   false, false, false, true, false, false}));\n    CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n              make_not_null(&phase_change_data), Parallel::Phase::Register,\n              global_cache)) == Parallel::Phase::Evolve);\n    CHECK(phase_change_data == SINGLE_ARG(phase_change_decision_data_type{\n                                   false, false, false, false, false, false}));\n\n    // test the versions that use `PermitAdditionalJumps`, so each are evaluated\n    // and the phase that is run is the last in the sequence, or the first\n    // `RunPhaseImmediately` that is encountered.\n    phase_change_data =\n        phase_change_decision_data_type{false, true, false, true, false, true};\n    CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n              make_not_null(&phase_change_data), Parallel::Phase::Register,\n              global_cache)) == Parallel::Phase::Evolve);\n    CHECK(phase_change_data == SINGLE_ARG(phase_change_decision_data_type{\n                                   false, false, false, false, false, false}));\n\n    phase_change_data =\n        phase_change_decision_data_type{false, true, false, true, true, true};\n    CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n              make_not_null(&phase_change_data), Parallel::Phase::Register,\n              global_cache)) == Parallel::Phase::Cleanup);\n    CHECK(phase_change_data == SINGLE_ARG(phase_change_decision_data_type{\n                                   false, false, false, false, false, false}));\n\n    // check that the result is the same starting from any phase\n    for (size_t i = 0; i < 7; ++i) {\n      phase_change_data = phase_change_decision_data_type{false, true,  true,\n                                                          false, false, true};\n      CHECK(SINGLE_ARG(PhaseControl::arbitrate_phase_change(\n                make_not_null(&phase_change_data),\n                gsl::at(Metavariables::default_phase_order, i),\n                global_cache)) == Parallel::Phase::ImportInitialData);\n      CHECK(phase_change_data ==\n            SINGLE_ARG(phase_change_decision_data_type{false, false, false,\n                                                       false, false, false}));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/PhaseControl/Test_PhaseChange.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct Request {\n  using type = bool;\n\n  using combine_method = funcl::Or<>;\n  using main_combine_method = funcl::Or<>;\n};\n\nstruct MutatedValue : db::SimpleTag {\n  using type = double;\n};\n\nstruct InputValue: db::SimpleTag {\n  using type = int;\n};\n}  // namespace Tags\n\nstruct Metavariables;\n\nstruct TestComponentAlpha {\n  using phase_dependent_action_list = tmpl::list<>;\n  using chare_type = Parallel::Algorithms::Array;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using simple_tags_from_options = tmpl::list<>;\n};\n\nstruct TestComponentBeta {\n  using phase_dependent_action_list = tmpl::list<>;\n  using chare_type = Parallel::Algorithms::Array;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using simple_tags_from_options = tmpl::list<>;\n};\n\nnamespace TestGlobalStateRecord {\nbool contributed = false;\n}  // namespace TestGlobalStateRecord\n\nstruct TestPhaseChange : public PhaseChange {\n  TestPhaseChange() = default;\n  explicit TestPhaseChange(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestPhaseChange);  // NOLINT\n#pragma GCC diagnostic pop\n\n  struct Factor {\n    using type = int;\n    static constexpr Options::String help {\"Multiplier to apply\"};\n  };\n\n  using options = tmpl::list<Factor>;\n  static constexpr Options::String help{\"Phase change tester\"};\n\n  using argument_tags = tmpl::list<Tags::InputValue>;\n  using return_tags = tmpl::list<Tags::MutatedValue>;\n\n  template <typename Metavariables>\n  using participating_components = tmpl::list<TestComponentAlpha>;\n\n  explicit TestPhaseChange(int multiplier) : stored_multiplier_{multiplier} {}\n\n  template <typename... DecisionTags>\n  void initialize_phase_data_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data) const {\n    tuples::get<Tags::Request>(*phase_change_decision_data) = false;\n  }\n\n  template <typename ParallelComponent, typename Metavariables,\n            typename ArrayIndex>\n  void contribute_phase_data_impl(\n      const gsl::not_null<double*> mutated_value, const int input_value,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/) const {\n    TestGlobalStateRecord::contributed = true;\n    *mutated_value =\n        static_cast<double>(square(input_value) * stored_multiplier_);\n  }\n\n  template <typename... DecisionTags, typename Metavariables>\n  typename std::optional<\n      std::pair<Parallel::Phase, PhaseControl::ArbitrationStrategy>>\n  arbitrate_phase_change_impl(\n      const gsl::not_null<tuples::TaggedTuple<DecisionTags...>*>\n          phase_change_decision_data,\n      const Parallel::Phase /*current_phase*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/) const {\n    if (tuples::get<Tags::Request>(*phase_change_decision_data)) {\n      tuples::get<Tags::Request>(*phase_change_decision_data) = false;\n      return std::make_pair(\n          Parallel::Phase::Solve,\n          PhaseControl::ArbitrationStrategy::RunPhaseImmediately);\n    } else {\n      return std::nullopt;\n    }\n  }\n\n  void pup(PUP::er& p) override { p | stored_multiplier_; }  // NOLINT\n\n private:\n  int stored_multiplier_ = 0;\n};\n\nPUP::able::PUP_ID TestPhaseChange::my_PUP_ID = 0;\n\nstruct Metavariables {\n  using component_list = tmpl::list<TestComponentAlpha, TestComponentBeta>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<PhaseChange, tmpl::list<TestPhaseChange>>>;\n  };\n\n};\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.PhaseControl.PhaseChange\",\n                  \"[Unit][Parallel]\") {\n  // create the necessary container types\n  Parallel::GlobalCache<Metavariables> cache{};\n  auto box =\n      db::create<db::AddSimpleTags<Tags::MutatedValue, Tags::InputValue>>(\n          std::numeric_limits<double>::signaling_NaN(), -5);\n  tuples::TaggedTuple<Tags::Request> phase_change_decision_data{true};\n\n  auto phase_change =\n      TestHelpers::test_creation<std::unique_ptr<PhaseChange>, Metavariables>(\n          \"TestPhaseChange:\\n\"\n          \"  Factor: 3\");\n\n  CHECK(tuples::get<Tags::Request>(phase_change_decision_data));\n  phase_change->initialize_phase_data<Metavariables>(\n      make_not_null(&phase_change_decision_data));\n  CHECK_FALSE(tuples::get<Tags::Request>(phase_change_decision_data));\n\n  CHECK_FALSE(TestGlobalStateRecord::contributed);\n  phase_change->contribute_phase_data<TestComponentBeta>(make_not_null(&box),\n                                                         cache, 0);\n  CHECK_FALSE(TestGlobalStateRecord::contributed);\n  phase_change->contribute_phase_data<TestComponentAlpha>(make_not_null(&box),\n                                                          cache, 0);\n  CHECK(TestGlobalStateRecord::contributed);\n  CHECK(db::get<Tags::MutatedValue>(box) == 75);\n\n  const auto first_decision = phase_change->arbitrate_phase_change(\n      make_not_null(&phase_change_decision_data), Parallel::Phase::Evolve,\n      cache);\n  CHECK_FALSE(first_decision.has_value());\n\n  tuples::get<Tags::Request>(phase_change_decision_data) = true;\n  const auto second_decision = phase_change->arbitrate_phase_change(\n      make_not_null(&phase_change_decision_data), Parallel::Phase::Evolve,\n      cache);\n  CHECK(second_decision.value() ==\n        std::make_pair(Parallel::Phase::Solve,\n                       PhaseControl::ArbitrationStrategy::RunPhaseImmediately));\n  CHECK_FALSE(tuples::get<Tags::Request>(phase_change_decision_data));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Parallel/PhaseControl/Test_PhaseControlTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <cstdint>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/ExitCode.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/Triggers/Slabs.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct DummyDecisionTag1 {\n  using type = int;\n  using combine_method = funcl::Plus<>;\n  using main_combine_method = combine_method;\n};\n\nstruct DummyDecisionTag2 {\n  using type = size_t;\n  using combine_method = funcl::Minus<>;\n  using main_combine_method = combine_method;\n};\n}  // namespace Tags\n\ntemplate <size_t Val>\nstruct TestCreatable : public PhaseChange {\n\n  TestCreatable() = default;\n  explicit TestCreatable(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TestCreatable);  // NOLINT\n\n  static std::string name() {\n    if constexpr(Val == 1_st) {\n      return \"TestCreatable(1)\";\n    } else if constexpr(Val == 2_st) {\n      return \"TestCreatable(2)\";\n    } else {\n      return \"TestCreatable(Unknown)\";\n    }\n  }\n\n  template <typename Metavariables>\n  using participating_components = tmpl::list<>;\n\n  static constexpr Options::String help{\"Creatable for test\"};\n\n  struct IntOption {\n    using type = int;\n    static constexpr Options::String help{\"Option parameter\"};\n  };\n\n  using options = tmpl::list<IntOption>;\n  using phase_change_tags_and_combines =\n      tmpl::conditional_t<(1_st < Val), tmpl::list<Tags::DummyDecisionTag1>,\n                          tmpl::list<Tags::DummyDecisionTag2>>;\n\n  explicit TestCreatable(int option_value) : option_value_{option_value} {}\n\n  void pup(PUP::er& p) override { p | option_value_; }  // NOLINT\n\n  int option_value_ = 0;\n};\n\ntemplate <size_t Val>\nPUP::able::PUP_ID TestCreatable<Val>::my_PUP_ID = 0;\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<PhaseChange, tmpl::list<TestCreatable<1_st>,\n                                                     TestCreatable<2_st>>>,\n                  tmpl::pair<TimeSequence<std::uint64_t>,\n                             TimeSequences::all_time_sequences<std::uint64_t>>,\n                  tmpl::pair<Trigger, tmpl::list<Triggers::Slabs>>>;\n  };\n};\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.PhaseControl.PhaseControlTags\",\n                  \"[Unit][Parallel]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  TestHelpers::db::test_simple_tag<PhaseControl::Tags::PhaseChangeAndTriggers>(\n      \"PhaseChangeAndTriggers\");\n\n  const auto created_phase_changes = TestHelpers::test_option_tag<\n      PhaseControl::OptionTags::PhaseChangeAndTriggers, Metavariables>(\n      \" - Trigger:\\n\"\n      \"     Slabs:\\n\"\n      \"       EvenlySpaced:\\n\"\n      \"         Interval: 2\\n\"\n      \"         Offset: 0\\n\"\n      \"   PhaseChanges:\\n\"\n      \"     - TestCreatable(1):\\n\"\n      \"         IntOption: 4\\n\"\n      \"     - TestCreatable(2):\\n\"\n      \"         IntOption: 2\");\n  CHECK(created_phase_changes.size() == 1_st);\n  const auto& first_creatable = created_phase_changes[0].phase_changes[0];\n  const auto& second_creatable = created_phase_changes[0].phase_changes[1];\n  REQUIRE(dynamic_cast<TestCreatable<1_st>*>(first_creatable.get()) != nullptr);\n  CHECK(dynamic_cast<TestCreatable<1_st>*>(first_creatable.get())\n            ->option_value_ == 4);\n\n  REQUIRE(dynamic_cast<TestCreatable<2_st>*>(second_creatable.get()) !=\n          nullptr);\n  CHECK(dynamic_cast<TestCreatable<2_st>*>(second_creatable.get())\n            ->option_value_ == 2);\n\n  static_assert(\n      std::is_same_v<\n          PhaseControl::get_phase_change_tags<Metavariables>,\n          tmpl::list<Tags::DummyDecisionTag2, Tags::DummyDecisionTag1,\n                     PhaseControl::TagsAndCombines::UsePhaseChangeArbitration,\n                     Parallel::Tags::ExitCode>>);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Parallel/PhaseControl/Test_VisitAndReturn.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ExitCode.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<PhaseControl::VisitAndReturn<Parallel::Phase::Evolve>,\n                       PhaseControl::VisitAndReturn<Parallel::Phase::Execute>>>,\n        tmpl::pair<Trigger, tmpl::list<Triggers::Always>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.PhaseControl.VisitAndReturn\",\n                  \"[Unit][Parallel]\") {\n  // note that the `contribute_phase_data_impl` function is currently untested\n  // in this unit test, because we do not have good support for reductions in\n  // the action testing framework. These are tested in the integration test\n  // `Parallel/Test_AlgorithmPhaseControl.cpp`\n  Parallel::GlobalCache<Metavariables> cache{};\n\n  const auto created_phase_changes = TestHelpers::test_option_tag<\n      PhaseControl::OptionTags::PhaseChangeAndTriggers, Metavariables>(\n      \" - Trigger: Always\\n\"\n      \"   PhaseChanges:\\n\"\n      \"     - VisitAndReturn(Evolve)\\n\"\n      \"     - VisitAndReturn(Execute)\");\n  using PhaseChangeDecisionData = tuples::tagged_tuple_from_typelist<\n      PhaseControl::get_phase_change_tags<Metavariables>>;\n\n  const auto& first_phase_change = created_phase_changes[0].phase_changes[0];\n  const auto& second_phase_change = created_phase_changes[0].phase_changes[1];\n  {\n    INFO(\"Test initialize phase change decision data\");\n    PhaseChangeDecisionData phase_change_decision_data{\n        Parallel::Phase::Solve,      true, Parallel::Phase::Solve, true, true,\n        Parallel::ExitCode::Complete};\n    first_phase_change->initialize_phase_data<Metavariables>(\n        make_not_null(&phase_change_decision_data));\n    // extra parens in the check prevent Catch from trying to stream the tuple\n    CHECK((phase_change_decision_data ==\n           PhaseChangeDecisionData{std::nullopt, false, Parallel::Phase::Solve,\n                                   true, true, Parallel::ExitCode::Complete}));\n\n    second_phase_change->initialize_phase_data<Metavariables>(\n        make_not_null(&phase_change_decision_data));\n    CHECK((phase_change_decision_data ==\n           PhaseChangeDecisionData{std::nullopt, false, std::nullopt, false,\n                                   true, Parallel::ExitCode::Complete}));\n\n    using first_phase_change_tuple_type = tuples::TaggedTuple<\n        PhaseControl::Tags::ReturnPhase<Parallel::Phase::Evolve>,\n        PhaseControl::Tags::TemporaryPhaseRequested<Parallel::Phase::Evolve>>;\n    first_phase_change_tuple_type tuple_for_first_phase_change{\n        Parallel::Phase::Solve, true};\n    PhaseControl::VisitAndReturn<Parallel::Phase::Evolve>{}\n        .initialize_phase_data_impl(\n            make_not_null(&tuple_for_first_phase_change));\n    CHECK((tuple_for_first_phase_change ==\n           first_phase_change_tuple_type{std::nullopt, false}));\n  }\n  {\n    INFO(\"Test arbitrate phase control\");\n    // In this test, we trace through the set of states that occur when\n    // resolving two simultaneous phase requests. This is a superset of states\n    // that occur in a single phase request\n    PhaseChangeDecisionData phase_change_decision_data{\n        std::nullopt, true, std::nullopt,\n        true,         true, Parallel::ExitCode::Complete};\n    const auto decision_result = first_phase_change->arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Execute,\n        cache);\n    // extra parens in the check prevent Catch from trying to stream the tuple\n    CHECK((decision_result ==\n           std::make_pair(\n               Parallel::Phase::Evolve,\n               PhaseControl::ArbitrationStrategy::RunPhaseImmediately)));\n    CHECK(\n        (phase_change_decision_data ==\n         PhaseChangeDecisionData{Parallel::Phase::Execute, false, std::nullopt,\n                                 true, true, Parallel::ExitCode::Complete}));\n  }\n  {\n    INFO(\"Multiple phase requests\");\n    // check a different starting phase\n    PhaseChangeDecisionData phase_change_decision_data{\n        std::nullopt, true, std::nullopt,\n        true,         true, Parallel::ExitCode::Complete};\n    auto decision_result = first_phase_change->arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Solve,\n        cache);\n    CHECK((decision_result ==\n           std::make_pair(\n               Parallel::Phase::Evolve,\n               PhaseControl::ArbitrationStrategy::RunPhaseImmediately)));\n    CHECK((phase_change_decision_data ==\n           PhaseChangeDecisionData{Parallel::Phase::Solve, false, std::nullopt,\n                                   true, true, Parallel::ExitCode::Complete}));\n\n    decision_result = first_phase_change->arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Evolve,\n        cache);\n    CHECK((decision_result ==\n           std::make_pair(\n               Parallel::Phase::Solve,\n               PhaseControl::ArbitrationStrategy::PermitAdditionalJumps)));\n    CHECK((phase_change_decision_data ==\n           PhaseChangeDecisionData{std::nullopt, false, std::nullopt, true,\n                                   true, Parallel::ExitCode::Complete}));\n\n    decision_result = second_phase_change->arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Solve,\n        cache);\n    CHECK((decision_result ==\n           std::make_pair(\n               Parallel::Phase::Execute,\n               PhaseControl::ArbitrationStrategy::RunPhaseImmediately)));\n    CHECK((phase_change_decision_data ==\n           PhaseChangeDecisionData{std::nullopt, false, Parallel::Phase::Solve,\n                                   false, true, Parallel::ExitCode::Complete}));\n\n    decision_result = PhaseControl::VisitAndReturn<Parallel::Phase::Execute>{}\n                          .arbitrate_phase_change_impl(\n                              make_not_null(&phase_change_decision_data),\n                              Parallel::Phase::Execute, cache);\n    CHECK((decision_result ==\n           std::make_pair(\n               Parallel::Phase::Solve,\n               PhaseControl::ArbitrationStrategy::PermitAdditionalJumps)));\n    CHECK((phase_change_decision_data ==\n           PhaseChangeDecisionData{std::nullopt, false, std::nullopt, false,\n                                   true, Parallel::ExitCode::Complete}));\n\n    decision_result = second_phase_change->arbitrate_phase_change(\n        make_not_null(&phase_change_decision_data), Parallel::Phase::Solve,\n        cache);\n    CHECK((decision_result == std::nullopt));\n    CHECK((phase_change_decision_data ==\n           PhaseChangeDecisionData{std::nullopt, false, std::nullopt, false,\n                                   true, Parallel::ExitCode::Complete}));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Printf/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Printf\")\n\nset(LIBRARY_SOURCES\n  Test_Printf.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Printf\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Parallel/Printf/Test_Printf.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <fstream>\n#include <ostream>\n#include <sstream>\n#include <vector>\n\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace {\nstruct TestStream {\n  double a{1.0};\n  std::vector<int> b{0, 4, 8, -7};\n};\n\nstd::ostream& operator<<(std::ostream& os, const TestStream& t) {\n  os << t.a << \" (\";\n  for (size_t i = 0; i < t.b.size() - 1; ++i) {\n    os << t.b[i] << \",\";\n  }\n  os << t.b[t.b.size() - 1] << \")\";\n  return os;\n}\n\nenum class TestEnum { Value1, Value2 };\n\nstd::ostream& operator<<(std::ostream& os, const TestEnum& t) {\n  switch (t) {\n    case TestEnum::Value1:\n      return os << \"Value 1\";\n    case TestEnum::Value2:\n      return os << \"Value 2\";\n    default:\n      return os;\n  }\n}\n\nvoid test_fprintf() {\n  const std::string test_file = \"Test_Printf_fprintf.out\";\n  file_system::rm(test_file, true);\n  Parallel::fprintf(test_file, \"%s %s\\n\", \"string\", TestEnum::Value1);\n  // In normal operation this would not be guaranteed to appear after\n  // the previous output, but the tests run without creating a real\n  // PrinterChare so everything is synchronous.\n  Parallel::fprintf(test_file, \"appended text\\n\");\n  {\n    std::ifstream file_stream(test_file);\n    std::ostringstream ss{};\n    file_stream >> ss.rdbuf();\n    CHECK(ss.str() == \"string Value 1\\nappended text\\n\");\n  }\n  file_system::rm(test_file, true);\n  file_system::create_directory(test_file);\n  CHECK_THROWS_WITH(\n      Parallel::fprintf(test_file, \"%s %s\\n\", \"string\", TestEnum::Value1),\n      Catch::Matchers::ContainsSubstring(\"Could not open '\" + test_file + \"'\"));\n  file_system::rm(test_file, true);\n}\n}  // namespace\n\n// [output_test_example]\n// [[OutputRegex, -100 3000000000 1.0000000000000000000e\\+00 \\(0,4,8,-7\\) test 1\n// 2 3 abf a o e u Value 2]]\nSPECTRE_TEST_CASE(\"Unit.Parallel.printf\", \"[Unit][Parallel]\") {\n  OUTPUT_TEST();\n  // clang-tidy doesn't want c-style arrays, but here we are trying\n  // to test them explicitly.\n  const char c_string0[40] = {\"test 1 2 3\"}; // NOLINT\n  // clang-tidy doesn't want raw pointers, wants gsl::owner<>.\n  auto* c_string1 = new char[80]; // NOLINT\n  // clang-tidy: do not use pointer arithmetic\n  c_string1[0] = 'a';   // NOLINT\n  c_string1[1] = 'b';   // NOLINT\n  c_string1[2] = 'f';   // NOLINT\n  c_string1[3] = '\\0';  // NOLINT\n  constexpr const char* const c_string2 = {\"a o e u\"};\n  Parallel::printf(\"%d %lld %s %s %s %s %s\\n\", -100, 3000000000, TestStream{},\n                   c_string0, c_string1, c_string2, TestEnum::Value2);\n  // clang-tidy doesn't want delete on anything without gsl::owner<>.\n  delete[] c_string1; // NOLINT\n\n  test_fprintf();\n}\n// [output_test_example]\n"
  },
  {
    "path": "tests/Unit/Parallel/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Tags/Test_ArrayIndex.cpp\n  Tags/Test_InputSource.cpp\n  Tags/Test_Section.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Parallel/Tags/Test_ArrayIndex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/Tags/ArrayIndex.hpp\"\n\nnamespace {\nstruct TestArrayIndex;\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.Tags.ArrayIndex\", \"[Unit][Parallel]\") {\n  TestHelpers::db::test_simple_tag<Parallel::Tags::ArrayIndex<TestArrayIndex>>(\n      \"ArrayIndex\");\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Tags/Test_InputSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/Tags/InputSource.hpp\"\n\nnamespace Parallel {\nSPECTRE_TEST_CASE(\"Unit.Parallel.Tags.InputSource\", \"[Unit][Parallel]\") {\n  TestHelpers::db::test_simple_tag<Tags::InputSource>(\"InputSource\");\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "tests/Unit/Parallel/Tags/Test_Section.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n\nnamespace Parallel {\nnamespace {\nstruct ParallelComponent;\nstruct SectionIdTag {\n  using type = int;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.Tags.Section\", \"[Unit][Parallel]\") {\n  TestHelpers::db::test_simple_tag<\n      Tags::Section<ParallelComponent, SectionIdTag>>(\"Section(SectionIdTag)\");\n}\n\n}  // namespace Parallel\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmCore.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n\nnamespace {\nstruct TestMetavariables;\ntemplate <class Metavariables>\nstruct NoOpsComponent;\n\nstruct TestAlgorithmArrayInstance {\n  explicit TestAlgorithmArrayInstance(int ii) : i(ii) {}\n  TestAlgorithmArrayInstance() = default;\n  int i = 0;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | i; }\n};\n\nbool operator==(const TestAlgorithmArrayInstance& lhs,\n                const TestAlgorithmArrayInstance& rhs) {\n  return lhs.i == rhs.i;\n}\n\nTestAlgorithmArrayInstance& operator++(TestAlgorithmArrayInstance& instance) {\n  instance.i++;\n  return instance;\n}\n}  // namespace\n\nnamespace std {\ntemplate <>\nstruct hash<TestAlgorithmArrayInstance> {\n  size_t operator()(const TestAlgorithmArrayInstance& t) const {\n    return hash<int>{}(t.i);\n  }\n};\n}  // namespace std\n\ntemplate <size_t Dim>\nusing BoundaryMessage = evolution::dg::BoundaryMessage<Dim>;\n\nnamespace {\nstruct ElementIndex {};\n\nstruct CountActionsCalled : db::SimpleTag {\n  static std::string name() { return \"CountActionsCalled\"; }\n  using type = int;\n};\n\nstruct Int0 : db::SimpleTag {\n  static std::string name() { return \"Int0\"; }\n  using type = int;\n};\n\nstruct Int1 : db::SimpleTag {\n  static std::string name() { return \"Int1\"; }\n  using type = int;\n};\n\nstruct TemporalId0 : db::SimpleTag {\n  static std::string name() { return \"TemporalId0\"; }\n  using type = TestAlgorithmArrayInstance;\n};\n\nstruct TemporalId1 : db::SimpleTag {\n  using type = ::TimeStepId;\n};\n\nstruct Vector0 : db::SimpleTag {\n  using type = DataVector;\n};\n\nstruct Vector1 : db::SimpleTag {\n  using type = DataVector;\n};\n\n//////////////////////////////////////////////////////////////////////\n// Test actions that do not add or remove from the DataBox\n//////////////////////////////////////////////////////////////////////\n\nnamespace no_op_test {\nstruct increment_count_actions_called {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        std::is_same_v<ParallelComponent, NoOpsComponent<TestMetavariables>>,\n        \"The ParallelComponent is not deduced to be the right type\");\n    db::mutate<CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n    static int a = 0;\n    return {(++a >= 5 ? Parallel::AlgorithmExecution::Pause\n                      : Parallel::AlgorithmExecution::Continue),\n            std::nullopt};\n  }\n};\n\nstruct no_op {\n  // [apply_iterative]\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/)\n  // [apply_iterative]\n  {\n    static_assert(\n        std::is_same_v<ParallelComponent, NoOpsComponent<TestMetavariables>>,\n        \"The ParallelComponent is not deduced to be the right type\");\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct initialize {\n  using simple_tags = tmpl::list<CountActionsCalled, Int0, Int1>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        std::is_same_v<ParallelComponent, NoOpsComponent<TestMetavariables>>,\n        \"The ParallelComponent is not deduced to be the right type\");\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 0, 1, 100);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct finalize {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl2::flat_any_v<\n                std::is_same_v<CountActionsCalled, DbTags>...>> = nullptr>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    static_assert(\n        std::is_same_v<ParallelComponent, NoOpsComponent<TestMetavariables>>,\n        \"The ParallelComponent is not deduced to be the right type\");\n    SPECTRE_PARALLEL_REQUIRE(db::get<CountActionsCalled>(box) == 5);\n    SPECTRE_PARALLEL_REQUIRE(db::get<Int0>(box) == 1);\n    SPECTRE_PARALLEL_REQUIRE(db::get<Int1>(box) == 100);\n  }\n};\n}  // namespace no_op_test\n\ntemplate <class Metavariables>\nstruct NoOpsComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        tmpl::list<no_op_test::initialize>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Register,\n                     tmpl::list<no_op_test::increment_count_actions_called,\n                                no_op_test::no_op>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    // [start_phase]\n    Parallel::get_parallel_component<NoOpsComponent>(local_cache)\n        .start_phase(next_phase);\n    // [start_phase]\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<no_op_test::finalize>(\n          Parallel::get_parallel_component<NoOpsComponent>(local_cache));\n    }\n  }\n};\n\n//////////////////////////////////////////////////////////////////////\n// Adding and remove elements from DataBox tests\n//////////////////////////////////////////////////////////////////////\n\nnamespace add_remove_test {\nstruct add_int_value_10 {\n  using simple_tags = tmpl::list<Int0>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n    static int a = 0;\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 10);\n    return {(++a >= 5 ? Parallel::AlgorithmExecution::Pause\n                      : Parallel::AlgorithmExecution::Continue),\n            std::nullopt};\n  }\n};\n\nstruct increment_int0 {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n    db::mutate<Int0>([](const gsl::not_null<int*> int0) { ++*int0; },\n                     make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct remove_int0 {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    SPECTRE_PARALLEL_REQUIRE(db::get<Int0>(box) == 11);\n    db::mutate<CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called, const int& int0) {\n          SPECTRE_PARALLEL_REQUIRE(int0 == 11);\n          ++*count_actions_called;\n        },\n        make_not_null(&box), db::get<Int0>(box));\n    // default assign to \"remove\"\n    Initialization::mutate_assign<tmpl::list<Int0>>(make_not_null(&box), 0);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct test_args {\n  // [requires_action]\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex,\n            Requires<db::tag_is_retrievable_v<CountActionsCalled,\n                                              db::DataBox<DbTags>>> = nullptr>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const double v0,\n                    std::vector<double>&& v1) {\n    // [requires_action]\n    SPECTRE_PARALLEL_REQUIRE(v0 == 4.82937);\n    SPECTRE_PARALLEL_REQUIRE(v1 == (std::vector<double>{3.2, -8.4, 7.5}));\n    SPECTRE_PARALLEL_REQUIRE(db::get<CountActionsCalled>(box) == 13);\n  }\n};\n\nstruct initialize {\n  using simple_tags = tmpl::list<CountActionsCalled, TemporalId0>;\n  template <\n      typename DbTagsList, typename... InboxTags, typename Metavariables,\n      typename ArrayIndex, typename ActionList, typename ParallelComponent,\n      Requires<not tmpl::list_contains_v<DbTagsList, CountActionsCalled>> =\n          nullptr>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 0,\n                                               TestAlgorithmArrayInstance{0});\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n\n  template <\n      typename DbTagsList, typename... InboxTags, typename Metavariables,\n      typename ArrayIndex, typename ActionList, typename ParallelComponent,\n      Requires<tmpl::list_contains_v<DbTagsList, CountActionsCalled>> = nullptr>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct finalize {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex,\n            Requires<tmpl2::flat_any_v<\n                std::is_same_v<CountActionsCalled, DbTags>...>> = nullptr>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    SPECTRE_PARALLEL_REQUIRE(db::get<CountActionsCalled>(box) == 13);\n  }\n};\n}  // namespace add_remove_test\n\ntemplate <class Metavariables>\nstruct MutateComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = ElementIndex;  // Just to test nothing breaks\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<add_remove_test::initialize>>,\n      Parallel::PhaseActions<Parallel::Phase::Solve,\n                             tmpl::list<add_remove_test::add_int_value_10,\n                                        add_remove_test::increment_int0,\n                                        add_remove_test::remove_int0>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<MutateComponent>(local_cache)\n        .start_phase(next_phase);\n    if (next_phase == Parallel::Phase::Evolve) {\n      // [simple_action_call]\n      Parallel::simple_action<add_remove_test::test_args>(\n          Parallel::get_parallel_component<MutateComponent>(local_cache),\n          4.82937, std::vector<double>{3.2, -8.4, 7.5});\n      // [simple_action_call]\n      Parallel::simple_action<add_remove_test::finalize>(\n          Parallel::get_parallel_component<MutateComponent>(local_cache));\n    }\n  }\n};\n\n//////////////////////////////////////////////////////////////////////\n// Test receiving data\n//////////////////////////////////////////////////////////////////////\n\nnamespace receive_data_test {\n// [int receive tag insert]\nstruct IntReceiveTag\n    : public Parallel::InboxInserters::MemberInsert<IntReceiveTag> {\n  using temporal_id = TestAlgorithmArrayInstance;\n  using type = std::unordered_map<temporal_id, std::unordered_multiset<int>>;\n};\n// [int receive tag insert]\n\nstruct BoundaryMessageReceiveTag {\n  using temporal_id = ::TimeStepId;\n  using type = std::unordered_map<\n      temporal_id,\n      std::map<DirectionalId<3>, std::unique_ptr<BoundaryMessage<3>>>>;\n  using message_type = BoundaryMessage<3>;\n\n  template <typename Inbox>\n  static bool insert_into_inbox(const gsl::not_null<Inbox*> inbox,\n                                BoundaryMessage<3>* boundary_message) {\n    auto& time_step_id = boundary_message->current_time_step_id;\n    auto& current_inbox = (*inbox)[time_step_id];\n\n    auto key = DirectionalId<3>{boundary_message->neighbor_direction,\n                                boundary_message->element_id};\n\n    current_inbox.insert_or_assign(\n        key, std::unique_ptr<BoundaryMessage<3>>(boundary_message));\n    return true;\n  }\n};\n\nstruct add_int0_to_box {\n  using simple_tags = tmpl::list<Int0>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<tmpl::list<Int0>>(make_not_null(&box), 0);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct add_vectors_to_box_and_send {\n  using simple_tags = tmpl::list<TemporalId1, Vector0, Vector1>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    TimeStepId time_step_id{true, 0, Slab{0.0, 1.1}.start()};\n\n    db::mutate<TemporalId1, Vector0, Vector1>(\n        [&time_step_id](const gsl::not_null<TimeStepId*> time_step_id_ptr,\n                        const gsl::not_null<DataVector*> vector_0_ptr,\n                        const gsl::not_null<DataVector*> vector_1_ptr) {\n          *time_step_id_ptr = time_step_id;\n          *vector_0_ptr = DataVector{-4.6, 9.8, 3.6, -1.7};\n          *vector_1_ptr = DataVector{};\n        },\n        make_not_null(&box));\n\n    BoundaryMessage<3>* boundary_message = new BoundaryMessage<3>(\n        0, 4, false, true, Parallel::my_node<size_t>(cache),\n        Parallel::my_proc<size_t>(cache), -2, 3, time_step_id, time_step_id, {},\n        {}, {}, {}, nullptr, const_cast<double*>(db::get<Vector0>(box).data()));\n\n    // Send to myself because everybody is on the same node and it's easier to\n    // check that pointers are the same between my own Vector0 and Vector1 than\n    // a different element. The pointer still goes through charm either way.\n    Parallel::receive_data<receive_data_test::BoundaryMessageReceiveTag>(\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],\n        boundary_message);\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct set_int0_from_receive {\n  using inbox_tags = tmpl::list<IntReceiveTag>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox = tuples::get<IntReceiveTag>(inboxes);\n    db::mutate<Int1>([](const gsl::not_null<int*> int1) { ++*int1; },\n                     make_not_null(&box));\n    // [retry_example]\n    if (inbox.count(db::get<TemporalId0>(box)) == 0) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    // [retry_example]\n\n    db::mutate<CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n    static int a = 0;\n    auto int0 = *inbox[db::get<TemporalId0>(box)].begin();\n    inbox.erase(db::get<TemporalId0>(box));\n    db::mutate<Int0>(\n        [&int0](const gsl::not_null<int*> int0_box) { *int0_box = int0; },\n        make_not_null(&box));\n    return {++a >= 5 ? Parallel::AlgorithmExecution::Pause\n                     : Parallel::AlgorithmExecution::Continue,\n            std::nullopt};\n  }\n};\n\nstruct set_vector1_from_receive {\n  using inbox_tags = tmpl::list<BoundaryMessageReceiveTag>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox = tuples::get<BoundaryMessageReceiveTag>(inboxes);\n\n    if (inbox.count(db::get<TemporalId1>(box)) == 0) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    db::mutate<CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n\n    auto& message_map = inbox[db::get<TemporalId1>(box)];\n    // We only sent one message so there should only be one in the inbox\n    SPECTRE_PARALLEL_REQUIRE(message_map.size() == 1);\n\n    auto& boundary_message = message_map.begin()->second;\n\n    // Set the data reference\n    db::mutate<Vector1>(\n        [&boundary_message](const gsl::not_null<DataVector*> vector1_box) {\n          vector1_box->set_data_ref(boundary_message->dg_flux_data,\n                                    boundary_message->dg_flux_data_size);\n        },\n        make_not_null(&box));\n\n    // We shouldn't have gone through the boundary_message::pack() function, so\n    // this shouldn't be true\n    SPECTRE_PARALLEL_REQUIRE_FALSE(boundary_message->owning);\n\n    // Only the boundary message gets destroyed, not the data it points to\n    inbox.erase(db::get<TemporalId1>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct update_instance {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<TemporalId0>(\n        [](const gsl::not_null<TestAlgorithmArrayInstance*> temporal_id) {\n          ++*temporal_id;\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct initialize {\n  using simple_tags =\n      tmpl::list<CountActionsCalled, Int1, TemporalId0, TemporalId1>;\n  template <\n      typename DbTagsList, typename... InboxTags, typename Metavariables,\n      typename ArrayIndex, typename ActionList, typename ParallelComponent,\n      Requires<not tmpl::list_contains_v<DbTagsList, CountActionsCalled>> =\n          nullptr>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), 0, 0, TestAlgorithmArrayInstance{0}, {});\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n\n  template <\n      typename DbTagsList, typename... InboxTags, typename Metavariables,\n      typename ArrayIndex, typename ActionList, typename ParallelComponent,\n      Requires<tmpl::list_contains_v<DbTagsList, CountActionsCalled>> = nullptr>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct finalize {\n  using inbox_tags = tmpl::list<IntReceiveTag>;\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex,\n            Requires<db::tag_is_retrievable_v<CountActionsCalled,\n                                              db::DataBox<DbTags>>> = nullptr>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    SPECTRE_PARALLEL_REQUIRE(db::get<TemporalId0>(box) ==\n                             TestAlgorithmArrayInstance{4});\n    SPECTRE_PARALLEL_REQUIRE(db::get<CountActionsCalled>(box) == 14);\n    SPECTRE_PARALLEL_REQUIRE(db::get<Int1>(box) == 10);\n\n    // Check that the data itself is equal\n    SPECTRE_PARALLEL_REQUIRE(db::get<Vector0>(box) == db::get<Vector1>(box));\n    // Now check that the pointers are equal, because they should be\n    SPECTRE_PARALLEL_REQUIRE(db::get<Vector0>(box).data() ==\n                             db::get<Vector1>(box).data());\n  }\n};\n}  // namespace receive_data_test\n\ntemplate <class Metavariables>\nstruct ReceiveComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = ElementId<3>;  // Just to test nothing breaks\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<receive_data_test::initialize>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::ImportInitialData,\n          tmpl::list<receive_data_test::add_int0_to_box,\n                     receive_data_test::set_int0_from_receive,\n                     add_remove_test::increment_int0,\n                     add_remove_test::remove_int0,\n                     receive_data_test::update_instance>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::AdjustDomain,\n          tmpl::list<receive_data_test::add_vectors_to_box_and_send,\n                     receive_data_test::set_vector1_from_receive,\n                     Parallel::Actions::TerminatePhase>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n          initialization_items,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        Parallel::get_parallel_component<ReceiveComponent>(\n            *Parallel::local_branch(global_cache)),\n        1, 1, initialization_items, global_cache, procs_to_ignore,\n        ElementId<3>{0});\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ReceiveComponent>(local_cache)\n        .start_phase(next_phase);\n    if (next_phase == Parallel::Phase::ImportInitialData) {\n      for (TestAlgorithmArrayInstance instance{0};\n           not(instance == TestAlgorithmArrayInstance{5}); ++instance) {\n        int dummy_int = 10;\n        Parallel::receive_data<receive_data_test::IntReceiveTag>(\n            Parallel::get_parallel_component<ReceiveComponent>(local_cache),\n            instance, dummy_int);\n      }\n    } else if (next_phase ==\n               Parallel::Phase::InitializeInitialDataDependentQuantities) {\n      Parallel::simple_action<receive_data_test::finalize>(\n          Parallel::get_parallel_component<ReceiveComponent>(local_cache));\n    }\n  }\n};\n\n//////////////////////////////////////////////////////////////////////\n// Test out of order execution of Actions\n//////////////////////////////////////////////////////////////////////\n\ntemplate <class Metavariables>\nstruct AnyOrderComponent;\n\nnamespace any_order {\nstruct iterate_increment_int0 {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        std::is_same_v<ParallelComponent, AnyOrderComponent<TestMetavariables>>,\n        \"The ParallelComponent is not deduced to be the right type\");\n    db::mutate<CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n    SPECTRE_PARALLEL_REQUIRE((db::get<CountActionsCalled>(box) - 1) / 2 ==\n                             db::get<Int0>(box) - 10);\n\n    const int max_int0_value = 25;\n    if (db::get<Int0>(box) < max_int0_value) {\n      return {\n          Parallel::AlgorithmExecution::Continue,\n          tmpl::index_of<ActionList, ::add_remove_test::increment_int0>::value};\n    }\n\n    SPECTRE_PARALLEL_REQUIRE(db::get<Int0>(box) == max_int0_value);\n    // [out_of_order_action]\n    return {Parallel::AlgorithmExecution::Pause,\n            tmpl::index_of<ActionList, iterate_increment_int0>::value + 1};\n    // [out_of_order_action]\n  }\n};\n\nstruct finalize {\n  template <\n      typename ParallelComponent, typename... DbTags, typename Metavariables,\n      typename ArrayIndex,\n      Requires<\n          tmpl2::flat_any_v<std::is_same_v<CountActionsCalled, DbTags>...> and\n          tmpl2::flat_any_v<std::is_same_v<Int0, DbTags>...>> = nullptr>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& box,\n                    const Parallel::GlobalCache<Metavariables>&\n                    /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    static_assert(\n        std::is_same_v<ParallelComponent, AnyOrderComponent<TestMetavariables>>,\n        \"The ParallelComponent is not deduced to be the right type\");\n    SPECTRE_PARALLEL_REQUIRE(db::get<TemporalId0>(box) ==\n                             TestAlgorithmArrayInstance{0});\n    SPECTRE_PARALLEL_REQUIRE(db::get<CountActionsCalled>(box) == 31);\n    SPECTRE_PARALLEL_REQUIRE(db::get<Int0>(box) == 25);\n  }\n};\n}  // namespace any_order\n\ntemplate <class Metavariables>\nstruct AnyOrderComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = ElementIndex;  // Just to test nothing breaks\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<add_remove_test::initialize>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute,\n                             tmpl::list<add_remove_test::add_int_value_10,\n                                        add_remove_test::increment_int0,\n                                        any_order::iterate_increment_int0,\n                                        add_remove_test::remove_int0,\n                                        receive_data_test::update_instance>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<AnyOrderComponent>(local_cache)\n        .start_phase(next_phase);\n    if (next_phase == Parallel::Phase::Cleanup) {\n      Parallel::simple_action<any_order::finalize>(\n          Parallel::get_parallel_component<AnyOrderComponent>(local_cache));\n    }\n  }\n};\n\nstruct TestMetavariables {\n  // [component_list_example]\n  using component_list = tmpl::list<NoOpsComponent<TestMetavariables>,\n                                    MutateComponent<TestMetavariables>,\n                                    ReceiveComponent<TestMetavariables>,\n                                    AnyOrderComponent<TestMetavariables>>;\n  // [component_list_example]\n\n  // [help_string_example]\n  static constexpr Options::String help =\n      \"An executable for testing the core functionality of the Algorithm. \"\n      \"Actions that do not perform any operations (no-ops), invoking simple \"\n      \"actions, mutating data in the DataBox, receiving data from other \"\n      \"parallel components, and out-of-order execution of Actions are all \"\n      \"tested. All tests are run just by running the executable, no input file \"\n      \"or command line arguments are required\";\n  // [help_string_example]\n\n  // These phases are just here to separate out execution of the test. The names\n  // of the phases don't correspond to what actually happens in the test except\n  // for Initialization and Exit. The rest are simply used as individual\n  // sections to test a particular feature.\n  static constexpr std::array<Parallel::Phase, 11> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::Testing, Parallel::Phase::Solve,\n       Parallel::Phase::Evolve, Parallel::Phase::ImportInitialData,\n       Parallel::Phase::AdjustDomain,\n       Parallel::Phase::InitializeInitialDataDependentQuantities,\n       Parallel::Phase::Execute, Parallel::Phase::Cleanup,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n}  // namespace\n\n// [charm_main_example]\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n// [charm_main_example]\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.Algorithm.NullptrConstructError\",\n                  \"[Parallel][Unit]\") {\n  CHECK_THROWS_WITH(\n      (Parallel::DistributedObject<\n          NoOpsComponent<TestMetavariables>,\n          tmpl::list<\n              Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                     tmpl::list<add_remove_test::initialize>>>>{\n          nullptr}),\n      Catch::Matchers::ContainsSubstring(\n          \"DistributedObject has been constructed with a nullptr\"));\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmGlobalCache.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Actions/GetLockPointer.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ContributeMemoryData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\ntemplate <class Metavariables>\nstruct MutateCacheComponent;\ntemplate <class Metavariables>\nstruct UseMutatedCacheComponent;\n\nnamespace mutate_cache {\n\n// An option tag is needed for every type the GlobalCache contains.\nnamespace OptionTags {\nstruct VectorOfDoubles {\n  static std::string name() { return \"VectorOfDoubles\"; }\n  static constexpr Options::String help = \"Options for vector of doubles\";\n  using type = std::vector<double>;\n};\n}  // namespace OptionTags\n\n// Tag to label the quantity in the GlobalCache.\nnamespace Tags {\nstruct VectorOfDoubles : db::SimpleTag {\n  using type = typename OptionTags::VectorOfDoubles::type;\n  using option_tags = tmpl::list<OptionTags::VectorOfDoubles>;\n  static constexpr bool pass_metavariables = false;\n  static type create_from_options(const type& input_type) { return input_type; }\n};\n}  // namespace Tags\n\n// Functions to be passed into GlobalCache::mutate\n// [mutate_global_cache_item_mutator]\nnamespace MutationFunctions {\nstruct add_stored_double {\n  static void apply(const gsl::not_null<std::vector<double>*> data,\n                    const double new_value) {\n    data->emplace_back(new_value);\n  }\n};\n}  // namespace MutationFunctions\n// [mutate_global_cache_item_mutator]\n\nnamespace Actions {\nstruct initialize {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const  // NOLINT const\n      /*meta*/) {\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct add_new_stored_double {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    // [mutate_global_cache_item]\n    Parallel::mutate<Tags::VectorOfDoubles,\n                     MutationFunctions::add_stored_double>(cache, 42.0);\n    // [mutate_global_cache_item]\n  }\n};\n\n// Global variables to make sure that certain functions have been called.\nsize_t number_of_calls_to_use_stored_double_is_ready = 0;\nsize_t number_of_calls_to_use_stored_double_apply = 0;\nsize_t number_of_calls_to_check_and_use_stored_double_is_ready = 0;\nsize_t number_of_calls_to_check_and_use_stored_double_apply = 0;\n\nstruct finalize {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    const std::vector<double> expected_result{42.0};\n    SPECTRE_PARALLEL_REQUIRE(Parallel::get<Tags::VectorOfDoubles>(cache) ==\n                             expected_result);\n    SPECTRE_PARALLEL_REQUIRE(number_of_calls_to_use_stored_double_is_ready ==\n                             2);\n    SPECTRE_PARALLEL_REQUIRE(number_of_calls_to_use_stored_double_apply == 1);\n    SPECTRE_PARALLEL_REQUIRE(\n        number_of_calls_to_check_and_use_stored_double_is_ready == 2);\n    SPECTRE_PARALLEL_REQUIRE(\n        number_of_calls_to_check_and_use_stored_double_apply == 1);\n  }\n};\n\nstruct simple_action_check_and_use_stored_double {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    ++number_of_calls_to_check_and_use_stored_double_is_ready;\n    auto& this_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    const auto array_component_id =\n        Parallel::make_array_component_id<ParallelComponent>(array_index);\n    const bool is_ready =\n        ::Parallel::mutable_cache_item_is_ready<Tags::VectorOfDoubles>(\n            cache, array_component_id,\n            [&this_proxy](const std::vector<double>& VectorOfDoubles)\n                -> std::unique_ptr<Parallel::Callback> {\n              return VectorOfDoubles.empty()\n                         ? std::unique_ptr<Parallel::Callback>(\n                               new Parallel::SimpleActionCallback<\n                                   simple_action_check_and_use_stored_double,\n                                   decltype(this_proxy)>(this_proxy))\n                         : std::unique_ptr<Parallel::Callback>{};\n            });\n\n    if (is_ready) {\n      ++number_of_calls_to_check_and_use_stored_double_apply;\n      const std::vector<double> expected_result{42.0};\n      SPECTRE_PARALLEL_REQUIRE(Parallel::get<Tags::VectorOfDoubles>(cache) ==\n                               expected_result);\n    }\n  }\n};\n\nstruct use_stored_double {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& /*box*/,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    ++number_of_calls_to_use_stored_double_is_ready;\n    // [check_mutable_cache_item_is_ready]\n    auto& this_proxy = Parallel::get_parallel_component<\n        UseMutatedCacheComponent<Metavariables>>(cache);\n    const Parallel::ArrayComponentId array_component_id =\n        Parallel::make_array_component_id<ParallelComponent>(array_index);\n    if (not ::Parallel::mutable_cache_item_is_ready<Tags::VectorOfDoubles>(\n            cache, array_component_id,\n            [&this_proxy](const std::vector<double>& VectorOfDoubles)\n                -> std::unique_ptr<Parallel::Callback> {\n              return VectorOfDoubles.empty()\n                         ? std::unique_ptr<Parallel::Callback>(\n                               new Parallel::PerformAlgorithmCallback(\n                                   this_proxy))\n                         : std::unique_ptr<Parallel::Callback>{};\n            })) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n    // [check_mutable_cache_item_is_ready]\n\n    ++number_of_calls_to_use_stored_double_apply;\n    const std::vector<double> expected_result{42.0};\n    // [retrieve_mutable_cache_item]\n    SPECTRE_PARALLEL_REQUIRE(Parallel::get<Tags::VectorOfDoubles>(cache) ==\n                             expected_result);\n    // [retrieve_mutable_cache_item]\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n}  // namespace Actions\n\n}  // namespace mutate_cache\n\n// We have five ParallelComponents:\n//\n// 1) MutateCacheComponent mutates the value in the GlobalCache using\n//    simple_actions, and then tests that the value in the GlobalCache\n//    is correct, using simple_actions.\n//\n// 2) UseMutatedCacheComponent has a single iterable_action that waits\n//    for the size of the value in the GlobalCache to be correct.\n//\n// 3) CheckAndUseMutatedCacheComponent has a simple_action that checks\n//    the size of the value in the GlobalCache, and then if the size\n//    is correct, it verifies that its value is correct.\n//\n// 4) CheckParallelInfo checks the parallel info functions of the GlobalCache\n//    against the Parallel:: and sys:: functions.\n//\n// 5) CheckMemoryMonitorRelatedMethods calls the\n//    `compute_size_for_memory_monitor` entry method of the GlobalCache which\n//    requires there to be a MemoryMonitor in the component list (and also an\n//    ObserverWriter for writing data)\ntemplate <class Metavariables>\nstruct MutateCacheComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using mutable_global_cache_tags =\n      tmpl::list<mutate_cache::Tags::VectorOfDoubles>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<mutate_cache::Actions::initialize>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<MutateCacheComponent>(local_cache)\n        .start_phase(next_phase);\n    if (next_phase == Parallel::Phase::Solve) {\n      Parallel::simple_action<mutate_cache::Actions::add_new_stored_double>(\n          Parallel::get_parallel_component<MutateCacheComponent>(local_cache));\n    } else if (next_phase == Parallel::Phase::Evolve) {\n      Parallel::simple_action<mutate_cache::Actions::finalize>(\n          Parallel::get_parallel_component<MutateCacheComponent>(local_cache));\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct UseMutatedCacheComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using mutable_global_cache_tags =\n      tmpl::list<mutate_cache::Tags::VectorOfDoubles>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<mutate_cache::Actions::initialize>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Solve,\n          tmpl::list<mutate_cache::Actions::use_stored_double>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<UseMutatedCacheComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct CheckAndUseMutatedCacheComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using mutable_global_cache_tags =\n      tmpl::list<mutate_cache::Tags::VectorOfDoubles>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<mutate_cache::Actions::initialize>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<CheckAndUseMutatedCacheComponent>(\n        local_cache)\n        .start_phase(next_phase);\n    if (next_phase == Parallel::Phase::Register) {\n      Parallel::simple_action<\n          mutate_cache::Actions::simple_action_check_and_use_stored_double>(\n          Parallel::get_parallel_component<CheckAndUseMutatedCacheComponent>(\n              local_cache));\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct CheckParallelInfo {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<CheckParallelInfo>(cache).start_phase(\n        next_phase);\n    if (next_phase == Parallel::Phase::Execute) {\n      // Check parallel info\n      SPECTRE_PARALLEL_REQUIRE(cache.number_of_procs() ==\n                               sys::number_of_procs());\n      SPECTRE_PARALLEL_REQUIRE(cache.number_of_nodes() ==\n                               sys::number_of_nodes());\n      SPECTRE_PARALLEL_REQUIRE(cache.procs_on_node(0) == sys::procs_on_node(0));\n      SPECTRE_PARALLEL_REQUIRE(cache.first_proc_on_node(0) ==\n                               sys::first_proc_on_node(0));\n      SPECTRE_PARALLEL_REQUIRE(cache.node_of(0) == sys::node_of(0));\n      SPECTRE_PARALLEL_REQUIRE(cache.local_rank_of(0) == sys::local_rank_of(0));\n      SPECTRE_PARALLEL_REQUIRE(cache.my_proc() == sys::my_proc());\n      SPECTRE_PARALLEL_REQUIRE(cache.my_node() == sys::my_node());\n      SPECTRE_PARALLEL_REQUIRE(cache.my_local_rank() == sys::my_local_rank());\n      SPECTRE_PARALLEL_REQUIRE(Parallel::number_of_procs<int>(cache) ==\n                               sys::number_of_procs());\n      SPECTRE_PARALLEL_REQUIRE(Parallel::number_of_nodes<int>(cache) ==\n                               sys::number_of_nodes());\n      SPECTRE_PARALLEL_REQUIRE(Parallel::procs_on_node<int>(0, cache) ==\n                               sys::procs_on_node(0));\n      SPECTRE_PARALLEL_REQUIRE(Parallel::first_proc_on_node<int>(0, cache) ==\n                               sys::first_proc_on_node(0));\n      SPECTRE_PARALLEL_REQUIRE(Parallel::node_of<int>(0, cache) ==\n                               sys::node_of(0));\n      SPECTRE_PARALLEL_REQUIRE(Parallel::local_rank_of<int>(0, cache) ==\n                               sys::local_rank_of(0));\n      SPECTRE_PARALLEL_REQUIRE(Parallel::my_proc<int>(cache) == sys::my_proc());\n      SPECTRE_PARALLEL_REQUIRE(Parallel::my_node<int>(cache) == sys::my_node());\n      SPECTRE_PARALLEL_REQUIRE(Parallel::my_local_rank<int>(cache) ==\n                               sys::my_local_rank());\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct CheckMemoryMonitorRelatedMethods {\n private:\n  static inline double cache_size_ = 0.0;\n  static inline std::string filename_{\"Test_AlgorithmGlobalCacheReduction.h5\"};\n\n public:\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<CheckMemoryMonitorRelatedMethods>(\n        local_cache)\n        .start_phase(next_phase);\n\n    const double time = 1.0;\n    if (next_phase == Parallel::Phase::Testing) {\n      // Remove the file from a previous test run if it exists\n      if (file_system::check_if_file_exists(filename_)) {\n        file_system::rm(filename_, true);\n      }\n      // This will broadcast to all branches of the global cache. This\n      // executable is run on 1 core so there is only 1 branch. The time is\n      // arbitrary\n      local_cache.compute_size_for_memory_monitor(time);\n\n      // Store the values for testing. The only reason this works is because we\n      // are running on 1 core, so we are always on the \"local\" branch of an\n      // object whether that object be a group or nodegroup. If this is run on\n      // more than one core, this will still run, but the values will be\n      // incorrect for the test\n      SPECTRE_PARALLEL_REQUIRE(Parallel::number_of_procs<int>(local_cache) ==\n                               1);\n      cache_size_ = size_of_object_in_bytes(local_cache) / 1.0e6;\n    } else if (next_phase == Parallel::Phase::Cleanup) {\n      auto hdf5_lock =\n          Parallel::local_branch(\n              Parallel::get_parallel_component<\n                  observers::ObserverWriter<Metavariables>>(local_cache))\n              ->template local_synchronous_action<\n                  observers::Actions::GetLockPointer<\n                      observers::Tags::H5FileLock>>();\n\n      hdf5_lock->lock();\n      SPECTRE_PARALLEL_REQUIRE(file_system::check_if_file_exists(filename_));\n\n      const h5::H5File<h5::AccessType::ReadOnly> read_file{filename_};\n\n      const std::vector<std::string> cache_legend{\n          {\"Time\", \"Size on node 0 (MB)\", \"Average size per node (MB)\"}};\n      const std::vector<std::string> mutable_cache_legend{\n          {\"Time\", \"Size on node 0 (MB)\", \"Proc of max size\",\n           \"Size on proc of max size (MB)\", \"Average size per node (MB)\"}};\n\n      const std::string cache_name{\"/MemoryMonitors/GlobalCache\"};\n\n      const auto check_caches = [&read_file, &time](\n                                    const std::string& name,\n                                    const std::vector<std::string>& legend,\n                                    const double check_size) {\n        const auto& dataset = read_file.get<h5::Dat>(name, legend);\n        const Matrix data = dataset.get_data();\n        const auto& legend_file = dataset.get_legend();\n\n        SPECTRE_PARALLEL_REQUIRE(legend == legend_file);\n        SPECTRE_PARALLEL_REQUIRE(data.columns() == legend.size());\n        SPECTRE_PARALLEL_REQUIRE(data.rows() == 1);\n        // First column is always time\n        SPECTRE_PARALLEL_REQUIRE(data(0, 0) == time);\n        // Second column is size on node 0 (since we are running on only one\n        // node)\n        SPECTRE_PARALLEL_REQUIRE(data(0, 1) == check_size);\n        // Last column is always average per node, but since we are running on\n        // one node, this should be the same as the size on node 0\n        SPECTRE_PARALLEL_REQUIRE(data(0, data.columns() - 1) == check_size);\n\n        read_file.close_current_object();\n      };\n\n      check_caches(cache_name, cache_legend, cache_size_);\n\n      hdf5_lock->unlock();\n\n      // Remove the file once we're done\n      if (file_system::check_if_file_exists(filename_)) {\n        file_system::rm(filename_, true);\n      }\n    }\n  }\n};\n\nstruct TestMetavariables {\n  using observed_reduction_data_tags = tmpl::list<>;\n  using component_list =\n      tmpl::list<MutateCacheComponent<TestMetavariables>,\n                 UseMutatedCacheComponent<TestMetavariables>,\n                 CheckAndUseMutatedCacheComponent<TestMetavariables>,\n                 CheckParallelInfo<TestMetavariables>,\n                 mem_monitor::MemoryMonitor<TestMetavariables>,\n                 CheckMemoryMonitorRelatedMethods<TestMetavariables>,\n                 observers::ObserverWriter<TestMetavariables>>;\n\n  static constexpr Options::String help =\n      \"An executable for testing mutable items in the GlobalCache.\";\n\n  static constexpr std::array<Parallel::Phase, 18> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::Solve, Parallel::Phase::Evolve,\n       Parallel::Phase::Execute, Parallel::Phase::Testing,\n       Parallel::Phase::Cleanup, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmGlobalCache.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\n# Initialize to empty vector.\nVectorOfDoubles: []\n\nObservers:\n  ReductionFileName: \"Test_AlgorithmGlobalCacheReduction\"\n  VolumeFileName: \"Test_AlgorithmGlobalCacheVolume\"\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmLocalSyncAction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace LocalSyncActionTest {\ntemplate <class Metavariables>\nstruct NodegroupComponent;\n\nstruct StepNumber : db::SimpleTag {\n  using type = size_t;\n};\n\nstruct InitializeNodegroup {\n  using simple_tags = tmpl::list<StepNumber>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // default initialization of SimpleTag is fine\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n// [synchronous_action_example]\nstruct SyncGetPointerFromNodegroup {\n  using return_type = size_t*;\n  template <typename ParallelComponent, typename DbTagsList>\n  static size_t* apply(db::DataBox<DbTagsList>& box,\n                       const gsl::not_null<Parallel::NodeLock*> node_lock) {\n    if constexpr (tmpl::list_contains_v<DbTagsList, StepNumber>) {\n      size_t* result = nullptr;\n      // We must lock access to the box, because box access is non-atomic and\n      // nodegroups can have multiple actions running in separate threads. Once\n      // we retrieve the pointer to data from the box, we can safely pass it\n      // along because we know that there are no compute tags that depend on\n      // `StepNumber`. Without that guarantee, this would not be a supported use\n      // of the box.\n      node_lock->lock();\n      db::mutate<StepNumber>(\n          [&result](const gsl::not_null<size_t*> step_number) {\n            result = step_number;\n          },\n          make_not_null(&box));\n      node_lock->unlock();\n      return result;\n    } else {\n      // avoid 'unused' warnings\n      (void)node_lock;\n      ERROR(\"Could not find required tag `StepNumber` in the databox\");\n    }\n  }\n};\n// [synchronous_action_example]\n\nstruct SyncGetConstRefFromNodegroup {\n  using return_type = const size_t&;\n  template <typename ParallelComponent, typename DbTagsList>\n  static const size_t& apply(\n      db::DataBox<DbTagsList>& box,\n      const gsl::not_null<Parallel::NodeLock*> node_lock) {\n    if constexpr (tmpl::list_contains_v<DbTagsList, StepNumber>) {\n      // We must lock access to the box, because box access is non-atomic and\n      // nodegroups can have multiple actions running in separate threads. Once\n      // we retrieve the pointer to data from the box, we can safely pass it\n      // along because we know that there are no compute tags that depend on\n      // `StepNumber`. Without that guarantee, this would not be a supported use\n      // of the box.\n      node_lock->lock();\n      const size_t& result = db::get<StepNumber>(box);\n      node_lock->unlock();\n      return result;\n    } else {\n      // avoid 'unused' warnings\n      (void)node_lock;\n      ERROR(\"Could not find required tag `StepNumber` in the databox\");\n    }\n  }\n};\n\nstruct IncrementNodegroupStep {\n  using return_type = void;\n  template <typename ParallelComponent, typename DbTagsList>\n  static void apply(db::DataBox<DbTagsList>& box,\n                    const gsl::not_null<Parallel::NodeLock*> node_lock) {\n    if constexpr (tmpl::list_contains_v<DbTagsList, StepNumber>) {\n      // We must lock access to the box, because box access is non-atomic and\n      // nodegroups can have multiple actions running in separate threads.\n      node_lock->lock();\n      db::mutate<StepNumber>(\n          [](const gsl::not_null<size_t*> step_number) { ++(*step_number); },\n          make_not_null(&box));\n      node_lock->unlock();\n    } else {\n      // avoid 'unused' warnings\n      (void)node_lock;\n      ERROR(\"Could not find required tag `StepNumber` in the databox\");\n    }\n  }\n};\n\nstruct TestSyncActionIncrement {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // [synchronous_action_invocation_example]\n    size_t* step_number =\n        Parallel::local_synchronous_action<SyncGetPointerFromNodegroup>(\n            Parallel::get_parallel_component<NodegroupComponent<Metavariables>>(\n                cache));\n    // [synchronous_action_invocation_example]\n    Parallel::local_synchronous_action<IncrementNodegroupStep>(\n        Parallel::get_parallel_component<NodegroupComponent<Metavariables>>(\n            cache));\n    SPECTRE_PARALLEL_REQUIRE(*step_number == 1);\n    ++(*step_number);\n    Parallel::local_synchronous_action<IncrementNodegroupStep>(\n        Parallel::get_parallel_component<NodegroupComponent<Metavariables>>(\n            cache));\n    SPECTRE_PARALLEL_REQUIRE(*step_number == 3);\n    SPECTRE_PARALLEL_REQUIRE(\n        *step_number ==\n        Parallel::local_synchronous_action<SyncGetConstRefFromNodegroup>(\n            Parallel::get_parallel_component<NodegroupComponent<Metavariables>>(\n                cache)));\n    SPECTRE_PARALLEL_REQUIRE(\n        step_number ==\n        &(Parallel::local_synchronous_action<SyncGetConstRefFromNodegroup>(\n            Parallel::get_parallel_component<NodegroupComponent<Metavariables>>(\n                cache))));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <class Metavariables>\nstruct NodegroupComponent {\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        tmpl::list<InitializeNodegroup>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<NodegroupComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct ArrayComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Evolve,\n      tmpl::list<TestSyncActionIncrement, Parallel::Actions::TerminatePhase>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& /*procs_to_ignore*/ = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayComponent>(local_cache);\n    // we only want one array component for this test.\n    array_proxy[0].insert(global_cache, tuples::TaggedTuple<>{}, 0);\n    array_proxy.doneInserting();\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ArrayComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace LocalSyncActionTest\n\nstruct TestMetavariables {\n  using component_list =\n      tmpl::list<LocalSyncActionTest::ArrayComponent<TestMetavariables>,\n                 LocalSyncActionTest::NodegroupComponent<TestMetavariables>>;\n\n  static constexpr Options::String help = \"\";\n\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Evolve,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmMessages.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <map>\n#include <sstream>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nstatic constexpr int total_number_of_array_elements = 12;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n\nnamespace {\ntemplate <size_t Dim>\nusing BoundaryMessage = evolution::dg::BoundaryMessage<Dim>;\n\nint node_of(const int element, const bool using_two_nodes) {\n  return using_two_nodes\n             ? (element < total_number_of_array_elements / 2 ? 0 : 1)\n             : 0;\n}\n\nnamespace Tags {\n// When we are running with two charm nodes, we want some elements to send to\n// the same node and some to send to a different node. Here's a chart:\n//\n// Element  MyNode  SendingTo  TheirNode  Inter/Intra-Communication\n//    0       0         1          0         Intra\n//    1       0         2          0         Intra\n//    2       0         3          0         Intra\n//    3       0         6          1         Inter\n//    4       0        11          1         Inter\n//    5       0        10          1         Inter\n//    6       1         7          1         Intra\n//    7       1         8          1         Intra\n//    8       1         9          1         Intra\n//    9       1         0          0         Inter\n//   10       1         4          0         Inter\n//   11       1         5          0         Inter\n//\n// Six elements will be doing inter-node communication, and six will be doing\n// intra-node communication\n//\n// When we are running with only one charm node, all twelve elements are on the\n// same node and will be doing intra-node communication\nstruct SendMap : db::SimpleTag {\n  using type = std::map<int, int>;\n  using option_tags = tmpl::list<>;\n  static constexpr bool pass_metavariables = false;\n\n  static type create_from_options() {\n    return std::map<int, int>{{0, 1}, {1, 2}, {2, 3}, {3, 6}, {4, 11}, {5, 10},\n                              {6, 7}, {7, 8}, {8, 9}, {9, 0}, {10, 4}, {11, 5}};\n  }\n};\n\n// Inverse of SendMap. Simple way to have a bidirectional mapping\nstruct ReceiveMap : db::SimpleTag {\n  using type = std::map<int, int>;\n  using option_tags = tmpl::list<>;\n  static constexpr bool pass_metavariables = false;\n\n  static type create_from_options() {\n    const std::map<int, int> send_map = SendMap::create_from_options();\n\n    std::map<int, int> receive_map{};\n\n    for (const auto& [sender_element, receiver_element] : send_map) {\n      receive_map[receiver_element] = sender_element;\n    }\n\n    return receive_map;\n  }\n};\n\n// We want to be able to compare Vector1.data() on the receiving element to the\n// Vector0.data() of the sending element. If the two elements are on the same\n// node, then those two addresses should be the same. If they are on different\n// nodes then they should be different. This is just a way to communicate\n// Vector0.data() from the sender to the receiver to compare.\nstruct AddressOfVector0OnSender : db::SimpleTag {\n  using type = std::string;\n};\n\n// Even though some of these can be accessed with functions, we add them all\n// here to have a uniform interface\n// {\nstruct MyElement : db::SimpleTag {\n  using type = int;\n};\n\nstruct MyNode : db::SimpleTag {\n  using type = int;\n};\n\nstruct ElementToSendTo : db::SimpleTag {\n  using type = int;\n};\n\nstruct NodeOfElementToSendTo : db::SimpleTag {\n  using type = int;\n};\n\nstruct ElementToReceiveFrom : db::SimpleTag {\n  using type = int;\n};\n\nstruct NodeOfElementToReceiveFrom : db::SimpleTag {\n  using type = int;\n};\n// }\n\nstruct Vector0 : db::SimpleTag {\n  using type = DataVector;\n};\n\nstruct Vector1 : db::SimpleTag {\n  using type = DataVector;\n};\n\n// We hijack the tci_status argument to be the array index of the receiving\n// element. Also this comment is up here so the docs look pretty\n// [charm message inbox tag]\nstruct BoundaryMessageReceiveTag {\n  using temporal_id = int;\n  using type =\n      std::unordered_map<temporal_id, std::unique_ptr<BoundaryMessage<3>>>;\n  using message_type = BoundaryMessage<3>;\n\n  template <typename Inbox>\n  static bool insert_into_inbox(const gsl::not_null<Inbox*> inbox,\n                                BoundaryMessage<3>* message) {\n    const int receiver_element = message->tci_status;\n    (*inbox)[receiver_element] = std::unique_ptr<BoundaryMessage<3>>(message);\n    return true;\n  }\n};\n// [charm message inbox tag]\n}  // namespace Tags\n\nstruct SendAddressOfVector0 {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const std::string& sent_address) {\n    db::mutate<Tags::AddressOfVector0OnSender>(\n        [&sent_address](const gsl::not_null<std::string*> address) {\n          *address = sent_address;\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct Initialize {\n  using simple_tags =\n      tmpl::list<Tags::MyElement, Tags::MyNode, Tags::ElementToSendTo,\n                 Tags::NodeOfElementToSendTo, Tags::ElementToReceiveFrom,\n                 Tags::NodeOfElementToReceiveFrom, Tags::Vector0, Tags::Vector1,\n                 Tags::AddressOfVector0OnSender>;\n  using const_global_cache_tags = tmpl::list<Tags::SendMap, Tags::ReceiveMap>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const auto& send_map = Parallel::get<Tags::SendMap>(cache);\n    const auto& receive_map = Parallel::get<Tags::ReceiveMap>(cache);\n    const int element_to_send_to = send_map.at(array_index);\n    const int element_to_receive_from = receive_map.at(array_index);\n    const bool using_two_nodes = Parallel::number_of_nodes<int>(cache) == 2;\n\n    Initialization::mutate_assign<\n        tmpl::list<Tags::MyElement, Tags::MyNode, Tags::ElementToSendTo,\n                   Tags::NodeOfElementToSendTo, Tags::ElementToReceiveFrom,\n                   Tags::NodeOfElementToReceiveFrom, Tags::Vector0>>(\n        make_not_null(&box), array_index, node_of(array_index, using_two_nodes),\n        element_to_send_to, node_of(element_to_send_to, using_two_nodes),\n        element_to_receive_from,\n        node_of(element_to_receive_from, using_two_nodes),\n        DataVector{static_cast<size_t>(array_index) + 1, 1.0});\n\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct SendMessage {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const int my_node = db::get<Tags::MyNode>(box);\n    const int element_to_send_to = db::get<Tags::ElementToSendTo>(box);\n\n    auto& proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n\n    // We hijack the tci_status argument to be the array index of the element\n    // that we are sending data to which will be the temporal id for the inbox\n    // tag\n    BoundaryMessage<3>* message = new BoundaryMessage<3>(\n        0, static_cast<size_t>(array_index + 1), false, true,\n        static_cast<size_t>(my_node), static_cast<size_t>(my_node),\n        element_to_send_to, 3, {}, {}, {}, {}, {}, {}, nullptr,\n        const_cast<double*>(db::get<Tags::Vector0>(box).data()));\n\n    std::stringstream ss{};\n    ss << message;\n    const std::string message_address{ss.str()};\n    ss.str(\"\");\n    ss << message->dg_flux_data;\n    const std::string data_address{ss.str()};\n\n    Parallel::receive_data<Tags::BoundaryMessageReceiveTag>(\n        proxy[element_to_send_to], message);\n\n    // Send the address of Vector0.data() to the receiver\n    Parallel::simple_action<SendAddressOfVector0>(proxy[element_to_send_to],\n                                                  data_address);\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct ReceiveMessage {\n  using inbox_tags = tmpl::list<Tags::BoundaryMessageReceiveTag>;\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    auto& inbox = tuples::get<Tags::BoundaryMessageReceiveTag>(inboxes);\n\n    if (inbox.count(array_index) == 0) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    // Scope this so we don't get a dangling reference\n    {\n      auto& boundary_message_ptr = inbox.at(array_index);\n\n      db::mutate<Tags::Vector1>(\n          [&boundary_message_ptr](const gsl::not_null<DataVector*> vector_1) {\n            vector_1->set_data_ref(boundary_message_ptr->dg_flux_data,\n                                   boundary_message_ptr->dg_flux_data_size);\n          },\n          make_not_null(&box));\n\n      const int my_node = db::get<Tags::MyNode>(box);\n      const int node_of_element_to_receive_from =\n          db::get<Tags::NodeOfElementToReceiveFrom>(box);\n\n      if (node_of_element_to_receive_from == my_node) {\n        SPECTRE_PARALLEL_REQUIRE_FALSE(boundary_message_ptr->owning);\n      } else {\n        SPECTRE_PARALLEL_REQUIRE(boundary_message_ptr->owning);\n      }\n    }\n\n    inbox.erase(array_index);\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct CheckMessage {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const int my_element = db::get<Tags::MyElement>(box);\n    const int my_node = db::get<Tags::MyNode>(box);\n    const int element_to_receive_from =\n        db::get<Tags::ElementToReceiveFrom>(box);\n    const int node_of_element_to_receive_from =\n        db::get<Tags::NodeOfElementToReceiveFrom>(box);\n\n    const auto& vector_0 = db::get<Tags::Vector0>(box);\n    const auto& vector_1 = db::get<Tags::Vector1>(box);\n\n    const auto& address_of_vector_0_on_sender =\n        db::get<Tags::AddressOfVector0OnSender>(box);\n    std::stringstream ss{};\n    ss << vector_1.data();\n    const std::string address_of_vector_1_on_receiver = ss.str();\n\n    // Regardless of whether we are on different nodes or not, this should be\n    // true\n    SPECTRE_PARALLEL_REQUIRE(vector_0.size() ==\n                             static_cast<size_t>(my_element) + 1);\n    SPECTRE_PARALLEL_REQUIRE(vector_1.size() ==\n                             static_cast<size_t>(element_to_receive_from) + 1);\n\n    // If we are on the same node, we should have the same memory address for\n    // our Vector1.data() and the senders Vector0.data(). But if we are on\n    // different nodes, they will be different.\n    if (node_of_element_to_receive_from == my_node) {\n      SPECTRE_PARALLEL_REQUIRE(address_of_vector_0_on_sender ==\n                               address_of_vector_1_on_receiver);\n    } else {\n      SPECTRE_PARALLEL_REQUIRE_FALSE(address_of_vector_0_on_sender ==\n                                     address_of_vector_1_on_receiver);\n    }\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <class Metavariables>\nstruct ArrayParallelComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<Initialize>>,\n      Parallel::PhaseActions<Parallel::Phase::Solve,\n                             tmpl::list<SendMessage, ReceiveMessage,\n                                        Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<CheckMessage, Parallel::Actions::TerminatePhase>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n  using array_index = int;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& /*procs_to_ignore*/ = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayParallelComponent>(local_cache);\n\n    if (sys::number_of_nodes() > 2) {\n      ERROR(\n          \"The Test_AlgorithmMessages test must be run on one (1) or two (2) \"\n          \"charm-nodes. For one node, you don't need any extra options. For 2 \"\n          \"nodes, you'll need to add `+n2 +p2` to the submit command.\");\n    }\n\n    for (int i = 0; i < total_number_of_array_elements; i++) {\n      const int node = node_of(i, sys::number_of_nodes() == 2);\n      array_proxy[i].insert(global_cache, {}, node);\n    }\n    array_proxy.doneInserting();\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ArrayParallelComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\nstruct TestMetavariables {\n  using component_list = tmpl::list<ArrayParallelComponent<TestMetavariables>>;\n\n  static constexpr const char* const help{\n      \"Test the receive_data entry method that uses Charm++ messages\"};\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Solve,\n       Parallel::Phase::Testing, Parallel::Phase::Cleanup,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmNestedApply1.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n\nstruct error_call_single_action_from_action {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    // [bad_recursive_call]\n    auto& local_parallel_component = *Parallel::local(\n        Parallel::get_parallel_component<ParallelComponent>(cache));\n    Parallel::simple_action<error_call_single_action_from_action>(\n        local_parallel_component);\n    // [bad_recursive_call]\n  }\n};\n\ntemplate <class Metavariables>\nstruct Component {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    if (next_phase == Parallel::Phase::Execute) {\n      auto& local_cache = *Parallel::local_branch(global_cache);\n      Parallel::simple_action<error_call_single_action_from_action>(\n          *Parallel::local(\n              Parallel::get_parallel_component<Component>(local_cache)));\n    }\n  }\n};\n\nstruct TestMetavariables {\n  using component_list = tmpl::list<Component<TestMetavariables>>;\n\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::Exit}};\n\n  static constexpr Options::String help = \"Executable for testing\";\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmNestedApply2.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n\nstruct another_action {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static auto apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {}\n};\n\nstruct error_call_single_action_from_action {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static auto apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    Parallel::simple_action<another_action>(*Parallel::local(\n        Parallel::get_parallel_component<ParallelComponent>(cache)));\n  }\n};\n\ntemplate <class Metavariables>\nstruct Component {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    if (next_phase == Parallel::Phase::Execute) {\n      auto& local_cache = *Parallel::local_branch(global_cache);\n      Parallel::simple_action<error_call_single_action_from_action>(\n          *Parallel::local(\n              Parallel::get_parallel_component<Component>(local_cache)));\n    }\n  }\n};\n\nstruct TestMetavariables {\n  using component_list = tmpl::list<Component<TestMetavariables>>;\n\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::Exit}};\n\n  static constexpr Options::String help = \"Executable for testing\";\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmNodelock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nstatic constexpr int number_of_1d_array_elements_per_core = 10;\n\nstruct TestMetavariables;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n\ntemplate <class Metavariables>\nstruct ArrayParallelComponent;\n\ntemplate <class Metavariables>\nstruct NodegroupParallelComponent;\n\nnamespace Tags {\nstruct vector_of_array_indexs : db::SimpleTag {\n  static std::string name() { return \"vector_of_array_indexs\"; }\n  using type = std::vector<int>;\n};\n\nstruct total_receives_on_node : db::SimpleTag {\n  static std::string name() { return \"total_receives_on_node\"; }\n  using type = int;\n};\n}  // namespace Tags\n\nstruct nodegroup_initialize {\n  using simple_tags =\n      tmpl::list<Tags::vector_of_array_indexs, Tags::total_receives_on_node>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 NodegroupParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box),\n        std::vector<int>(\n            static_cast<size_t>(number_of_1d_array_elements_per_core *\n                                sys::procs_on_node(sys::my_node()))),\n        0);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct nodegroup_receive {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex,\n            Requires<db::tag_is_retrievable_v<Tags::vector_of_array_indexs,\n                                              db::DataBox<DbTags>>> = nullptr>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const int& id_of_array) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 NodegroupParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    db::mutate<Tags::vector_of_array_indexs, Tags::total_receives_on_node>(\n        [&id_of_array](const gsl::not_null<std::vector<int>*> array_indexs,\n                       const gsl::not_null<int*> total_receives_on_node) {\n          if (static_cast<int>(array_indexs->size()) !=\n              number_of_1d_array_elements_per_core *\n                  sys::procs_on_node(sys::my_node())) {\n            (*array_indexs)[static_cast<size_t>(id_of_array)]++;\n          }\n          std::for_each(array_indexs->begin(), array_indexs->end(),\n                        [](int& t) { t++; });\n          ++*total_receives_on_node;\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct nodegroup_check_first_result {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex,\n            Requires<db::tag_is_retrievable_v<Tags::vector_of_array_indexs,\n                                              db::DataBox<DbTags>>> = nullptr>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 NodegroupParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    SPECTRE_PARALLEL_REQUIRE(db::get<Tags::total_receives_on_node>(box) ==\n                             number_of_1d_array_elements_per_core *\n                                 sys::procs_on_node(sys::my_node()));\n    decltype(auto) v = db::get<Tags::vector_of_array_indexs>(box);\n    SPECTRE_PARALLEL_REQUIRE(static_cast<int>(v.size()) ==\n                             number_of_1d_array_elements_per_core *\n                                 sys::procs_on_node(sys::my_node()));\n    std::for_each(v.begin(), v.end(), [](const int& value) {\n      SPECTRE_PARALLEL_REQUIRE(value == number_of_1d_array_elements_per_core *\n                                            sys::procs_on_node(sys::my_node()));\n    });\n  }\n};\n\nstruct nodegroup_threaded_receive {\n  // [threaded_action_example]\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex,\n            Requires<db::tag_is_retrievable_v<Tags::vector_of_array_indexs,\n                                              db::DataBox<DbTags>>> = nullptr>\n  static void apply(db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> node_lock,\n                    const int& id_of_array) {\n    // [threaded_action_example]\n    node_lock->lock();\n    db::mutate<Tags::vector_of_array_indexs, Tags::total_receives_on_node>(\n        [&id_of_array](const gsl::not_null<std::vector<int>*> array_indexs,\n                       const gsl::not_null<int*> total_receives_on_node) {\n          if (static_cast<int>(array_indexs->size()) !=\n              number_of_1d_array_elements_per_core *\n                  sys::procs_on_node(sys::my_node())) {\n            (*array_indexs)[static_cast<size_t>(id_of_array)]++;\n          }\n          std::for_each(array_indexs->begin(), array_indexs->end(),\n                        [](int& t) { t++; });\n          ++*total_receives_on_node;\n        },\n        make_not_null(&box));\n    node_lock->unlock();\n  }\n};\n\nstruct nodegroup_check_threaded_result {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex,\n            Requires<db::tag_is_retrievable_v<Tags::vector_of_array_indexs,\n                                              db::DataBox<DbTags>>> = nullptr>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 NodegroupParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    SPECTRE_PARALLEL_REQUIRE(db::get<Tags::total_receives_on_node>(box) ==\n                             2 * number_of_1d_array_elements_per_core *\n                                 sys::procs_on_node(sys::my_node()));\n    decltype(auto) v = db::get<Tags::vector_of_array_indexs>(box);\n    SPECTRE_PARALLEL_REQUIRE(static_cast<int>(v.size()) ==\n                             number_of_1d_array_elements_per_core *\n                                 sys::procs_on_node(sys::my_node()));\n    std::for_each(v.begin(), v.end(), [](const int& value) {\n      SPECTRE_PARALLEL_REQUIRE(value ==\n                               2 * number_of_1d_array_elements_per_core *\n                                   sys::procs_on_node(sys::my_node()));\n    });\n  }\n};\n\nstruct reduce_to_nodegroup {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(const db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 ArrayParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    auto& local_nodegroup = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            NodegroupParallelComponent<Metavariables>>(cache));\n    Parallel::simple_action<nodegroup_receive>(local_nodegroup, array_index);\n  }\n};\n\nstruct reduce_threaded_method {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(const db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 ArrayParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    auto& local_nodegroup = *Parallel::local_branch(\n        Parallel::get_parallel_component<\n            NodegroupParallelComponent<Metavariables>>(cache));\n    Parallel::threaded_action<nodegroup_threaded_receive>(local_nodegroup,\n                                                          array_index);\n  }\n};\n\ntemplate <class Metavariables>\nstruct ArrayParallelComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>,\n      Parallel::PhaseActions<Parallel::Phase::Register, tmpl::list<>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayParallelComponent>(local_cache);\n\n    const size_t number_of_procs = static_cast<size_t>(sys::number_of_procs());\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        array_proxy,\n        static_cast<size_t>(number_of_1d_array_elements_per_core) *\n            number_of_procs,\n        number_of_procs, {}, global_cache, procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayParallelComponent>(local_cache);\n    if (next_phase == Parallel::Phase::Register) {\n      Parallel::simple_action<reduce_to_nodegroup>(array_proxy);\n    }\n    if (next_phase == Parallel::Phase::Execute) {\n      Parallel::simple_action<reduce_threaded_method>(array_proxy);\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct NodegroupParallelComponent {\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<nodegroup_initialize>>,\n      Parallel::PhaseActions<Parallel::Phase::Register, tmpl::list<>>,\n      Parallel::PhaseActions<Parallel::Phase::Solve, tmpl::list<>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& nodegroup_proxy =\n        Parallel::get_parallel_component<NodegroupParallelComponent>(\n            local_cache);\n    if (next_phase == Parallel::Phase::Solve) {\n      Parallel::simple_action<nodegroup_check_first_result>(nodegroup_proxy);\n    }\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<nodegroup_check_threaded_result>(nodegroup_proxy);\n    }\n  }\n};\n\nstruct TestMetavariables {\n  using component_list =\n      tmpl::list<ArrayParallelComponent<TestMetavariables>,\n                 NodegroupParallelComponent<TestMetavariables>>;\n\n  static constexpr const char* const help{\"Test nodelocks in Algorithm\"};\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  static constexpr std::array<Parallel::Phase, 6> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::Solve, Parallel::Phase::Execute,\n       Parallel::Phase::Testing, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmParallel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <sstream>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmGroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nstatic constexpr int number_of_1d_array_elements = 14;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\n\nnamespace AlgorithmParallel_detail {\nstruct UnpackCounter {\n  UnpackCounter() = default;\n  ~UnpackCounter() = default;\n  UnpackCounter(const UnpackCounter& /*unused*/) = default;\n  UnpackCounter& operator=(const UnpackCounter& /*unused*/) = default;\n  UnpackCounter(UnpackCounter&& /*unused*/) = default;\n  UnpackCounter& operator=(UnpackCounter&& /*unused*/) = default;\n\n  explicit UnpackCounter(CkMigrateMessage* /*msg*/) {}\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) {\n    p | counter_value;\n    if (p.isUnpacking()) {\n      ++counter_value;\n    }\n  }\n  size_t counter_value = 0;\n};\n}  // namespace AlgorithmParallel_detail\n\nnamespace OptionTags {\nstruct NumberOfElements {\n  using type = int;\n  static constexpr Options::String help{\"Number of elements\"};\n};\n}  // namespace OptionTags\n\nnamespace Tags {\nstruct NumberOfElements : db::SimpleTag {\n  using type = int;\n  using option_tags = tmpl::list<OptionTags::NumberOfElements>;\n  static constexpr bool pass_metavariables = false;\n  static int create_from_options(const int number_of_elements) {\n    return number_of_elements;\n  }\n};\nstruct ReceiveArrayComponentsOnceMore : db::SimpleTag {\n  using type = bool;\n};\n\nstruct Int0 : db::SimpleTag {\n  static std::string name() { return \"Int0\"; }\n  using type = int;\n};\n\nstruct Int1 : db::SimpleTag {\n  static std::string name() { return \"Int1\"; }\n  using type = int;\n};\n\nstruct CountActionsCalled : db::SimpleTag {\n  static std::string name() { return \"CountActionsCalled\"; }\n  using type = int;\n};\n\nstruct UnpackCounter : db::SimpleTag {\n  using type = AlgorithmParallel_detail::UnpackCounter;\n};\n\n// [int_receive_tag]\nstruct IntReceiveTag {\n  using temporal_id = int;\n  using type = std::unordered_map<temporal_id, std::unordered_multiset<int>>;\n\n  template <typename Inbox, typename ReceiveDataType>\n  static bool insert_into_inbox(const gsl::not_null<Inbox*> inbox,\n                                const temporal_id& temporal_id_v,\n                                ReceiveDataType&& data) {\n    (*inbox)[temporal_id_v].insert(std::forward<ReceiveDataType>(data));\n    return true;\n  }\n\n  static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n\n    ss << pad << \"IntReceiveTagInbox:\\n\";\n    for (const auto& [time, set_of_ints] : inbox) {\n      ss << pad << \" Time: \" << time << \", All ints: \" << set_of_ints << \"\\n\";\n    }\n\n    return ss.str();\n  }\n};\n// [int_receive_tag]\n}  // namespace Tags\n\nstruct TestMetavariables;\n\ntemplate <class Metavariables>\nstruct SingletonParallelComponent;\n\ntemplate <class Metavariables>\nstruct ArrayParallelComponent;\n\ntemplate <class Metavariables>\nstruct GroupParallelComponent;\n\ntemplate <class Metavariables>\nstruct NodegroupParallelComponent;\n\nnamespace SingletonActions {\n\nstruct Initialize {\n  using simple_tags = tmpl::list<Tags::ReceiveArrayComponentsOnceMore>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 SingletonParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), false);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n// One component (A) may call this simple action on another component (B) so\n// that B then invokes the `perform_algorithm` entry method on A, which will\n// restart A if it has been terminated.\n// This helps to add artificial breaks to the iterable actions to better test\n// charm runtime processes that must wait for the components to be outside entry\n// methods, such as load balancing.\ntemplate <typename ComponentToRestart>\nstruct RestartMe {\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex,\n            typename IndexToRestart, typename Metavariables>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/,\n                    const IndexToRestart index_to_restart) {\n    Parallel::get_parallel_component<ComponentToRestart>(\n        cache)[index_to_restart]\n        .perform_algorithm(true);\n  }\n};\n\nstruct CountReceives {\n  // [int_receive_tag_list]\n  using inbox_tags = tmpl::list<Tags::IntReceiveTag>;\n  // [int_receive_tag_list]\n\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 SingletonParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    auto& int_receives = tuples::get<Tags::IntReceiveTag>(inboxes);\n    if (db::get<Tags::ReceiveArrayComponentsOnceMore>(box)) {\n      SPECTRE_PARALLEL_REQUIRE(int_receives.size() <= 14);\n      if (int_receives.size() != 14) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n    } else {\n      SPECTRE_PARALLEL_REQUIRE(int_receives.size() <= 686);\n      if (int_receives.size() != 686) {\n        return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n      }\n    }\n\n    for (const auto& p : int_receives) {\n      SPECTRE_PARALLEL_REQUIRE(p.second.size() == 1);\n      SPECTRE_PARALLEL_REQUIRE(*(p.second.begin()) % 3 == 0);\n    }\n    int_receives.clear();\n\n    if (not db::get<Tags::ReceiveArrayComponentsOnceMore>(box)) {\n      // Call to arrays, have them execute once then reduce something through\n      // groups and nodegroups\n      // We do not do a broadcast so that we can check inline entry methods on\n      // array work. We pass \"true\" as the second argument to start the\n      // algorithm up again on the arrays\n      // [call_on_indexed_array]\n      auto& array_parallel_component = Parallel::get_parallel_component<\n          ArrayParallelComponent<Metavariables>>(cache);\n      for (int i = 0; i < number_of_1d_array_elements; ++i) {\n        Parallel::receive_data<Tags::IntReceiveTag>(array_parallel_component[i],\n                                                    0, 101, true);\n      }\n      // [call_on_indexed_array]\n    }\n\n    db::mutate<Tags::ReceiveArrayComponentsOnceMore>(\n        [](bool* const receive_array_components_once_more) {\n          *receive_array_components_once_more = true;\n        },\n        make_not_null(&box));\n\n    // [return_with_termination]\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n    // [return_with_termination]\n  }\n};\n}  // namespace SingletonActions\n\nnamespace ArrayActions {\nstruct Initialize {\n  using simple_tags = tmpl::list<Tags::CountActionsCalled, Tags::UnpackCounter>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 ArrayParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box), 0, AlgorithmParallel_detail::UnpackCounter{});\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct AddIntValue10 {\n  using inbox_tags = tmpl::list<Tags::IntReceiveTag>;\n  using simple_tags = tmpl::list<Tags::Int0>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& inboxes,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 ArrayParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    auto& int_receives = tuples::get<Tags::IntReceiveTag>(inboxes);\n    SPECTRE_PARALLEL_REQUIRE(int_receives.empty() or int_receives.size() == 1);\n    if (int_receives.size() == 1) {\n      // [broadcast_to_group]\n      auto& group_parallel_component = Parallel::get_parallel_component<\n          GroupParallelComponent<Metavariables>>(cache);\n      Parallel::receive_data<Tags::IntReceiveTag>(\n          group_parallel_component,\n          db::get<Tags::CountActionsCalled>(box) + 100 * array_index,\n          db::get<Tags::CountActionsCalled>(box));\n      // [broadcast_to_group]\n    }\n    db::mutate<Tags::CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 10);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct CheckWasUnpacked {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Check to be sure the algorithm has been packed and unpacked at least a\n    // few times during the algorithm and retained functionality\n    if (sys::number_of_procs() > 1) {\n      // If this check fails with slightly too few unpacks counted, it may\n      // indicate that the test machine is too fast for the setting used in\n      // the balancer in the accompanying CMakeLists.txt. If that is the\n      // problem, you might solve it either by balancing even more often, or by\n      // doing more computational work during each iteration of the algorithm.\n      SPECTRE_PARALLEL_REQUIRE(db::get<Tags::UnpackCounter>(box).counter_value >\n                               2);\n    }\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct IncrementInt0 {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 ArrayParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    db::mutate<Tags::CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n    db::mutate<Tags::Int0>([](const gsl::not_null<int*> int0) { ++*int0; },\n                           make_not_null(&box));\n    // [iterable_action_return_continue_next_action]\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n    // [iterable_action_return_continue_next_action]\n  }\n};\n\nstruct RemoveInt0 {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box, tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 ArrayParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    SPECTRE_PARALLEL_REQUIRE(db::get<Tags::Int0>(box) == 11);\n    db::mutate<Tags::CountActionsCalled>(\n        [](const gsl::not_null<int*> count_actions_called) {\n          ++*count_actions_called;\n        },\n        make_not_null(&box));\n    // Run the iterable action sequence several times to ensure that\n    // load-balancing has a chance to be invoked multiple times.\n    if (db::get<Tags::CountActionsCalled>(box) < 150) {\n      // The simple action that runs on the singleton will invoke the\n      // `perform_algorithm` entry method on the present component, restarting\n      // it.\n      // Our use of the charm runtime system ensures that the return value of\n      // this iterable action will be processed before that entry method, and\n      // that the QD will not trigger when the entry method is waiting to be\n      // run.\n      Parallel::simple_action<SingletonActions::RestartMe<ParallelComponent>>(\n          Parallel::get_parallel_component<\n              SingletonParallelComponent<Metavariables>>(cache),\n          array_index);\n    }\n    // default assign Int0 to \"remove\" it\n    Initialization::mutate_assign<tmpl::list<Tags::Int0>>(make_not_null(&box),\n                                                          0);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct SendToSingleton {\n  using inbox_tags = tmpl::list<Tags::IntReceiveTag>;\n\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 ArrayParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    auto& singleton_parallel_component = Parallel::get_parallel_component<\n        SingletonParallelComponent<Metavariables>>(cache);\n    // Send CountActionsCalled to the SingletonParallelComponent several times\n    // [receive_broadcast]\n    Parallel::receive_data<Tags::IntReceiveTag>(\n        singleton_parallel_component,\n        db::get<Tags::CountActionsCalled>(box) + 1000 * array_index,\n        db::get<Tags::CountActionsCalled>(box), true);\n    // [receive_broadcast]\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace ArrayActions\n\nnamespace GroupActions {\nstruct Initialize {\n  using inbox_tags = tmpl::list<Tags::IntReceiveTag>;\n  using simple_tags = tmpl::list<Tags::CountActionsCalled>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 GroupParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 0);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct CheckComponentType {\n  using inbox_tags = tmpl::list<Tags::IntReceiveTag>;\n\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(\n        std::is_same_v<ParallelComponent,\n                       GroupParallelComponent<TestMetavariables>> or\n            std::is_same_v<ParallelComponent,\n                           NodegroupParallelComponent<TestMetavariables>>,\n        \"The ParallelComponent is not deduced to be the right type\");\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct ReduceInt {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 GroupParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n}  // namespace GroupActions\n\nnamespace NodegroupActions {\nstruct Initialize {\n  using simple_tags = tmpl::list<Tags::CountActionsCalled>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 NodegroupParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 0);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\n}  // namespace NodegroupActions\n\n// [singleton_parallel_component]\ntemplate <class Metavariables>\nstruct SingletonParallelComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<SingletonActions::Initialize>>,\n      Parallel::PhaseActions<Parallel::Phase::Solve,\n                             tmpl::list<SingletonActions::CountReceives>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<SingletonParallelComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n// [singleton_parallel_component]\n\n// [array_parallel_component]\ntemplate <class Metavariables>\nstruct ArrayParallelComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ArrayActions::Initialize>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Solve,\n          tmpl::list<ArrayActions::AddIntValue10, ArrayActions::IncrementInt0,\n                     ArrayActions::RemoveInt0, ArrayActions::SendToSingleton>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<ArrayActions::CheckWasUnpacked>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<Tags::NumberOfElements>;\n  using array_index = int;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n          array_allocation_items,\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayParallelComponent>(local_cache);\n    // This corrects that parsing array_allocation_tags works correctly\n    const auto number_of_elements =\n        tuples::get<Tags::NumberOfElements>(array_allocation_items);\n    SPECTRE_PARALLEL_REQUIRE(number_of_elements == number_of_1d_array_elements);\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        array_proxy, static_cast<size_t>(number_of_1d_array_elements),\n        static_cast<size_t>(sys::number_of_procs()), {}, global_cache,\n        procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ArrayParallelComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n// [array_parallel_component]\n\ntemplate <class Metavariables>\nstruct GroupParallelComponent {\n  using chare_type = Parallel::Algorithms::Group;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<GroupActions::Initialize, GroupActions::CheckComponentType>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase /*next_phase*/,\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n};\n\ntemplate <class Metavariables>\nstruct NodegroupParallelComponent {\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<NodegroupActions::Initialize,\n                                        GroupActions::CheckComponentType>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase /*next_phase*/,\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n};\n\nstruct TestMetavariables {\n  using component_list =\n      tmpl::list<SingletonParallelComponent<TestMetavariables>,\n                 ArrayParallelComponent<TestMetavariables>,\n                 GroupParallelComponent<TestMetavariables>,\n                 NodegroupParallelComponent<TestMetavariables>>;\n\n  static constexpr const char* const help{\"Test Algorithm in parallel\"};\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Solve,\n       Parallel::Phase::Testing, Parallel::Phase::Cleanup,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmParallel.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nNumberOfElements: 14\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmPhaseControl.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <functional>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Actions {\nstruct InitializePhaseRecord;\nstruct RecordCurrentPhase;\ntemplate <Parallel::Phase Phase>\nstruct RecordPhaseIteration;\ntemplate <typename ComponentToRestart>\nstruct RestartMe;\ntemplate <typename OtherComponent, size_t interval>\nstruct TerminateAndRestart;\nstruct Testing;\n}  // namespace Actions\n\nnamespace Tags {\nstruct PhaseRecord : db::SimpleTag {\n  using type = std::string;\n};\nstruct Step : db::SimpleTag {\n  using type = size_t;\n};\n}  // namespace Tags\n\ntemplate <typename Metavariables>\nstruct ComponentAlpha;\n\ntemplate <typename Metavariables>\nstruct ComponentBeta;\n\nstruct RegisterTrigger : public Trigger {\n  RegisterTrigger() = default;\n  explicit RegisterTrigger(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(RegisterTrigger);  // NOLINT\n\n  static constexpr Options::String help{\"Trigger for going to Register.\"};\n  using options = tmpl::list<>;\n\n  using argument_tags = tmpl::list<Tags::Step>;\n\n  bool operator()(const size_t step) const { return step % 5 == 0; }\n};\n\nstruct SolveTrigger : public Trigger {\n  SolveTrigger() = default;\n  explicit SolveTrigger(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(SolveTrigger);  // NOLINT\n\n  static constexpr Options::String help{\"Trigger for going to Solve.\"};\n  using options = tmpl::list<>;\n\n  using argument_tags = tmpl::list<Tags::Step>;\n\n  bool operator()(const size_t step) const { return step % 3 == 0; }\n};\n\nPUP::able::PUP_ID SolveTrigger::my_PUP_ID = 0;\nPUP::able::PUP_ID RegisterTrigger::my_PUP_ID = 0;\n\ntemplate <typename Metavariables>\nstruct ComponentAlpha {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<Actions::InitializePhaseRecord,\n                                        Actions::RecordPhaseIteration<\n                                            Parallel::Phase::Initialization>,\n                                        Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<Actions::RecordPhaseIteration<Parallel::Phase::Register>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Solve,\n          tmpl::list<Actions::RecordPhaseIteration<Parallel::Phase::Solve>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<\n              Actions::RecordPhaseIteration<Parallel::Phase::Evolve>,\n              Actions::TerminateAndRestart<ComponentBeta<Metavariables>, 2_st>,\n              PhaseControl::Actions::ExecutePhaseChange>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& /*procs_to_ignore*/ = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ComponentAlpha>(local_cache);\n\n    array_proxy[0].insert(global_cache, tuples::TaggedTuple<>{}, 0);\n    array_proxy.doneInserting();\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<Actions::Testing>(\n          Parallel::get_parallel_component<ComponentAlpha>(local_cache));\n    } else {\n      Parallel::get_parallel_component<ComponentAlpha>(local_cache)\n          .start_phase(next_phase);\n    }\n  }\n};\n\ntemplate <typename Metavariables>\nstruct ComponentBeta {\n  using chare_type = typename Metavariables::component_beta_chare_type;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<Actions::InitializePhaseRecord,\n                                        Actions::RecordPhaseIteration<\n                                            Parallel::Phase::Initialization>,\n                                        Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<Actions::RecordPhaseIteration<Parallel::Phase::Register>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Solve,\n          tmpl::list<Actions::RecordPhaseIteration<Parallel::Phase::Solve>,\n                     Parallel::Actions::TerminatePhase>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<Actions::RecordPhaseIteration<Parallel::Phase::Evolve>,\n                     PhaseControl::Actions::ExecutePhaseChange,\n                     Actions::TerminateAndRestart<ComponentAlpha<Metavariables>,\n                                                  3_st>>>>;\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<Actions::Testing>(\n          Parallel::get_parallel_component<ComponentBeta>(local_cache));\n    } else {\n      Parallel::get_parallel_component<ComponentBeta>(local_cache)\n          .start_phase(next_phase);\n    }\n  }\n};\n\nnamespace Actions {\n\nstruct InitializePhaseRecord {\n  using simple_tags = tmpl::list<Tags::PhaseRecord, Tags::Step>;\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// iterable action called during phases\ntemplate <Parallel::Phase Phase>\nstruct RecordPhaseIteration {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Tags::PhaseRecord>(\n        [](const gsl::not_null<std::string*> phase_log) {\n          *phase_log += MakeString{} << \"Running phase: \" << Phase << \"\\n\";\n        },\n        make_not_null(&box));\n    if (Phase == Parallel::Phase::Evolve) {\n      db::mutate<Tags::Step>(\n          [](const gsl::not_null<size_t*> step) { ++(*step); },\n          make_not_null(&box));\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename ComponentToRestart>\nstruct RestartMe {\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex,\n            typename Metavariables>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    Parallel::get_parallel_component<ComponentToRestart>(cache)\n        .perform_algorithm(true);\n  }\n};\n\ntemplate <typename OtherComponent, size_t interval>\nstruct TerminateAndRestart {\n  template <typename DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTags>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (db::get<Tags::Step>(box) % interval == 0) {\n      if (db::get<Tags::Step>(box) < 15) {\n        Parallel::simple_action<Actions::RestartMe<ParallelComponent>>(\n            Parallel::get_parallel_component<OtherComponent>(cache));\n\n        db::mutate<Tags::PhaseRecord>(\n            [](const gsl::not_null<std::string*> phase_log) {\n              *phase_log += \"Terminate and Restart\\n\";\n            },\n            make_not_null(&box));\n        return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n      } else {\n        db::mutate<Tags::PhaseRecord>(\n            [](const gsl::not_null<std::string*> phase_log) {\n              *phase_log += \"Terminate Completion\\n\";\n            },\n            make_not_null(&box));\n        return {Parallel::AlgorithmExecution::Halt, std::nullopt};\n      }\n    }\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct Testing {\n  template <\n      typename ParallelComponent, typename DbTagsList, typename ArrayIndex,\n      typename Metavariables,\n      Requires<tmpl::list_contains_v<DbTagsList, Tags::PhaseRecord>> = nullptr>\n  static auto apply(const db::DataBox<DbTagsList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    const std::string& log = db::get<Tags::PhaseRecord>(box);\n    SPECTRE_PARALLEL_REQUIRE(\n        log == Metavariables::expected_log(tmpl::type_<ParallelComponent>{}));\n  }\n\n  template <typename ParallelComponent, typename DbTagsList,\n            typename ArrayIndex, typename Metavariables,\n            Requires<not tmpl::list_contains_v<DbTagsList, Tags::PhaseRecord>> =\n                nullptr>\n  static auto apply(const db::DataBox<DbTagsList>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    SPECTRE_PARALLEL_REQUIRE(false);\n  }\n};\n}  // namespace Actions\n\n// two elements: alpha and beta\n// terminate and restart alpha on even time steps\n// terminate and restart beta on odd time steps divisible by 3\n// request sync actions on counts that are divisible by 5 or 7\n//\n// all events are recorded in a 'log' string that can be checked at the end.\n//\n// action request pattern:\n//     'alpha'    'beta'\n//  5:  none    |   none\n//  7: Register |   none\n// 10:  none    |     Solve\n// 14:  none    |  Register and Solve\ntemplate <typename ComponentBetaChareType>\nstruct TestMetavariables {\n  using component_beta_chare_type = ComponentBetaChareType;\n  using component_list =\n      tmpl::list<ComponentAlpha<TestMetavariables<ComponentBetaChareType>>,\n                 ComponentBeta<TestMetavariables<ComponentBetaChareType>>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            PhaseChange,\n            tmpl::list<PhaseControl::VisitAndReturn<Parallel::Phase::Register>,\n                       PhaseControl::VisitAndReturn<Parallel::Phase::Solve>>>,\n        tmpl::pair<Trigger, tmpl::list<RegisterTrigger, SolveTrigger>>>;\n  };\n\n  using const_global_cache_tags =\n      tmpl::list<PhaseControl::Tags::PhaseChangeAndTriggers>;\n\n  static constexpr Options::String help =\n      \"An executable for testing basic phase control flow.\";\n\n  static std::string repeat(const std::string& input, const size_t times) {\n    std::string output;\n    for (size_t i = 0; i < times; ++i) {\n      output += input;\n    }\n    return output;\n  }\n\n  static std::string expected_log(\n      tmpl::type_<\n          ComponentAlpha<TestMetavariables<ComponentBetaChareType>>> /*meta*/) {\n    return \"Running phase: Initialization\\n\" +\n           repeat(\"Running phase: Evolve\\n\", 2_st) +\n           \"Terminate and Restart\\n\"\n           \"Running phase: Evolve\\n\"  // step 3 -> Solve\n           \"Running phase: Solve\\n\"\n           \"Running phase: Evolve\\n\"  // step 4\n           \"Terminate and Restart\\n\"\n           \"Running phase: Evolve\\n\"  // step 5 -> Register\n           \"Running phase: Register\\n\"\n           \"Running phase: Evolve\\n\"  // step 6 -> Solve\n           \"Terminate and Restart\\n\"\n           \"Running phase: Solve\\n\" +\n           repeat(\"Running phase: Evolve\\n\", 2_st) +  // step 7-8\n           \"Terminate and Restart\\n\"\n           \"Running phase: Evolve\\n\"  // step 9 -> Solve\n           \"Running phase: Solve\\n\"\n           \"Running phase: Evolve\\n\"  // step 10 -> Register\n           \"Terminate and Restart\\n\"\n           \"Running phase: Register\\n\" +\n           repeat(\"Running phase: Evolve\\n\", 2_st) +  // step 11-12 -> Solve\n           \"Terminate and Restart\\n\"\n           \"Running phase: Solve\\n\" +\n           repeat(\"Running phase: Evolve\\n\", 2_st) +  // step 13-14\n           \"Terminate and Restart\\n\"\n           \"Running phase: Evolve\\n\"  // step 15 -> Solve then Register\n           \"Running phase: Register\\n\"\n           \"Running phase: Solve\\n\"\n           \"Running phase: Evolve\\n\"  // step 16\n           \"Terminate Completion\\n\";\n  }\n\n  static std::string expected_log(\n      tmpl::type_<\n          ComponentBeta<TestMetavariables<ComponentBetaChareType>>> /*meta*/) {\n    return \"Running phase: Initialization\\n\" +\n           repeat(\"Running phase: Evolve\\n\", 3_st) +  // steps 1-3 -> Solve\n           \"Running phase: Solve\\n\"\n           \"Terminate and Restart\\n\" +\n           repeat(\"Running phase: Evolve\\n\", 2_st) +  // steps 4-5 -> Register\n           \"Running phase: Register\\n\"\n           \"Running phase: Evolve\\n\"  // step 6 -> Solve\n           \"Running phase: Solve\\n\"\n           \"Terminate and Restart\\n\" +\n           repeat(\"Running phase: Evolve\\n\", 3_st) +  // steps 8-9 -> Solve\n           \"Running phase: Solve\\n\"\n           \"Terminate and Restart\\n\"\n           \"Running phase: Evolve\\n\"  // step 10 -> Register\n           \"Running phase: Register\\n\" +\n           repeat(\"Running phase: Evolve\\n\", 2_st) +  // steps 11-12 -> Solve\n           \"Running phase: Solve\\n\"\n           \"Terminate and Restart\\n\" +\n           repeat(\"Running phase: Evolve\\n\",\n                  3_st) +  // steps 13-15 -> Register then Solve\n           \"Running phase: Register\\n\"\n           \"Running phase: Solve\\n\"\n           \"Terminate Completion\\n\";\n  }\n\n  static constexpr std::array<Parallel::Phase, 4> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Evolve,\n       Parallel::Phase::Testing, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmPhaseControlNodegroup.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/Test_AlgorithmPhaseControl.hpp\"\n\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n\n// [charm_init_funcs_example]\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<\n      TestMetavariables<Parallel::Algorithms::Nodegroup>>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&register_factory_classes_with_charm<\n          TestMetavariables<Parallel::Algorithms::Nodegroup>>},\n      {});\n}\n// [charm_init_funcs_example]\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmPhaseControlNodegroup.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nPhaseChangeAndTriggers:\n  - Trigger: RegisterTrigger\n    PhaseChanges:\n      - VisitAndReturn(Register)\n  - Trigger: SolveTrigger\n    PhaseChanges:\n      - VisitAndReturn(Solve)\n\nResourceInfo:\n  AvoidGlobalProc0: false\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmPhaseControlSingleton.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Parallel/Test_AlgorithmPhaseControl.hpp\"\n\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<\n      TestMetavariables<Parallel::Algorithms::Singleton>>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&register_factory_classes_with_charm<\n          TestMetavariables<Parallel::Algorithms::Singleton>>},\n      {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmPhaseControlSingleton.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nPhaseChangeAndTriggers:\n  - Trigger: RegisterTrigger\n    PhaseChanges:\n      - VisitAndReturn(Register)\n  - Trigger: SolveTrigger\n    PhaseChanges:\n      - VisitAndReturn(Solve)\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_AlgorithmReduction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <ostream>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace db {\ntemplate <typename TagsList>\nclass DataBox;\n}  // namespace db\nstruct TestMetavariables;\n\n// The reason we use a 46 element array is that we want to be able to test on\n// two nodes to make sure multinode communication is working correctly.\nstatic constexpr int number_of_1d_array_elements = 46;\n\n// [reduce_sum_int_action]\nstruct ProcessReducedSumOfInts {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const int& value) {\n    SPECTRE_PARALLEL_REQUIRE(number_of_1d_array_elements *\n                                 (number_of_1d_array_elements - 1) / 2 ==\n                             value);\n  }\n};\n// [reduce_sum_int_action]\n\n// [reduce_rms_action]\nstruct ProcessErrorNorms {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const int points,\n                    const double error_u, const double error_v) {\n    SPECTRE_PARALLEL_REQUIRE(number_of_1d_array_elements * 3 == points);\n    SPECTRE_PARALLEL_REQUIRE(equal_within_roundoff(\n        error_u, sqrt(number_of_1d_array_elements * square(1.0e-3) / points)));\n    SPECTRE_PARALLEL_REQUIRE(equal_within_roundoff(\n        error_v, sqrt(number_of_1d_array_elements * square(1.0e-4) / points)));\n  }\n};\n// [reduce_rms_action]\n\nstruct ProcessCustomReductionAction {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, int reduced_int,\n                    std::unordered_map<std::string, int> reduced_map,\n                    std::vector<int>&& reduced_vector) {\n    SPECTRE_PARALLEL_REQUIRE(reduced_int == 10);\n    SPECTRE_PARALLEL_REQUIRE(reduced_map.at(\"unity\") ==\n                             number_of_1d_array_elements - 1);\n    SPECTRE_PARALLEL_REQUIRE(reduced_map.at(\"double\") ==\n                             2 * number_of_1d_array_elements - 2);\n    SPECTRE_PARALLEL_REQUIRE(reduced_map.at(\"negative\") == 0);\n    SPECTRE_PARALLEL_REQUIRE(\n        reduced_vector ==\n        (std::vector<int>{-reduced_int * number_of_1d_array_elements *\n                              (number_of_1d_array_elements - 1) / 2,\n                          -reduced_int * number_of_1d_array_elements * 10,\n                          8 * reduced_int * number_of_1d_array_elements}));\n  }\n};\n\ntemplate <class Metavariables>\nstruct SingletonParallelComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase /*next_phase*/,\n      const Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache*/) {}\n};\n\ntemplate <class Metavariables>\nstruct ArrayParallelComponent;\n\nstruct ArrayReduce {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(const db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    static_assert(std::is_same_v<ParallelComponent,\n                                 ArrayParallelComponent<TestMetavariables>>,\n                  \"The ParallelComponent is not deduced to be the right type\");\n    const auto& my_proxy =\n        Parallel::get_parallel_component<ArrayParallelComponent<Metavariables>>(\n            cache)[array_index];\n    const auto& array_proxy =\n        Parallel::get_parallel_component<ArrayParallelComponent<Metavariables>>(\n            cache);\n    const auto& singleton_proxy = Parallel::get_parallel_component<\n        SingletonParallelComponent<Metavariables>>(cache);\n    // [contribute_to_reduction_example]\n    Parallel::ReductionData<Parallel::ReductionDatum<int, funcl::Plus<>>>\n        my_send_int{array_index};\n    Parallel::contribute_to_reduction<ProcessReducedSumOfInts>(\n        my_send_int, my_proxy, singleton_proxy);\n    // [contribute_to_reduction_example]\n    // [contribute_to_broadcast_reduction]\n    Parallel::contribute_to_reduction<ProcessReducedSumOfInts>(\n        my_send_int, my_proxy, array_proxy);\n    // [contribute_to_broadcast_reduction]\n\n    // [contribute_to_rms_reduction]\n    using RmsRed = Parallel::ReductionDatum<double, funcl::Plus<>,\n                                            funcl::Sqrt<funcl::Divides<>>,\n                                            std::index_sequence<0>>;\n    Parallel::ReductionData<Parallel::ReductionDatum<size_t, funcl::Plus<>>,\n                            RmsRed, RmsRed>\n        error_reduction{3, square(1.0e-3), square(1.0e-4)};\n    Parallel::contribute_to_reduction<ProcessErrorNorms>(error_reduction,\n                                                         my_proxy, array_proxy);\n    // [contribute_to_rms_reduction]\n\n    std::unordered_map<std::string, int> my_send_map;\n    my_send_map[\"unity\"] = array_index;\n    my_send_map[\"double\"] = 2 * array_index;\n    my_send_map[\"negative\"] = -array_index;\n    struct {\n      int operator()(const int time_state, const int time) {\n        if (time_state != time) {\n          ERROR(\"Tried to reduce from different iteration values \"\n                << time_state << \" and \" << time);\n        }\n        return time_state;\n      }\n    } check_times_equal;\n    struct {\n      std::unordered_map<std::string, int> operator()(\n          std::unordered_map<std::string, int> state,\n          const std::unordered_map<std::string, int>& element) {\n        for (const auto& string_int : element) {\n          if (string_int.second > state.at(string_int.first)) {\n            state[string_int.first] = string_int.second;\n          }\n        }\n        return state;\n      }\n    } map_combine;\n    struct {\n      std::vector<int> operator()(std::vector<int> state,\n                                  const std::vector<int>& element) {\n        for (size_t i = 0; i < state.size(); ++i) {\n          state[i] += element[i];\n        }\n        return state;\n      }\n\n    } vector_combine;\n    struct {\n      std::vector<int> operator()(std::vector<int> data,\n                                  const int& first_reduction) {\n        std::transform(data.begin(), data.end(), data.begin(),\n                       [&first_reduction](const int t) {\n                         return -1 * t * first_reduction;\n                       });\n        return data;\n      }\n    } vector_finalize;\n    using ReductionType = Parallel::ReductionData<\n        Parallel::ReductionDatum<int, decltype(check_times_equal)>,\n        Parallel::ReductionDatum<std::unordered_map<std::string, int>,\n                                 decltype(map_combine)>,\n        Parallel::ReductionDatum<std::vector<int>, decltype(vector_combine),\n                                 decltype(vector_finalize),\n                                 std::index_sequence<0>>>;\n    Parallel::contribute_to_reduction<ProcessCustomReductionAction>(\n        ReductionType{10, my_send_map, std::vector<int>{array_index, 10, -8}},\n        my_proxy, singleton_proxy);\n    Parallel::contribute_to_reduction<ProcessCustomReductionAction>(\n        ReductionType{10, my_send_map, std::vector<int>{array_index, 10, -8}},\n        my_proxy, array_proxy);\n  }\n};\n\ntemplate <class Metavariables>\nstruct ArrayParallelComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayParallelComponent>(local_cache);\n\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        array_proxy, static_cast<size_t>(number_of_1d_array_elements),\n        static_cast<size_t>(sys::number_of_procs()), {}, global_cache,\n        procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<ArrayReduce>(\n          Parallel::get_parallel_component<ArrayParallelComponent>(\n              local_cache));\n    }\n  }\n};\n\nstruct TestMetavariables {\n  using component_list =\n      tmpl::list<SingletonParallelComponent<TestMetavariables>,\n                 ArrayParallelComponent<TestMetavariables>>;\n\n  static constexpr const char* const help{\"Test reductions using Algorithm\"};\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Testing,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_ArrayComponentId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <ckarrayindex.h>\n#include <functional>\n#include <type_traits>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n\nnamespace {\nstruct Component0 {};\nstruct Component1 {};\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.ArrayComponentId\", \"[Unit][Parallel]\") {\n  using ArrayComponentId = Parallel::ArrayComponentId;\n  using Hash = std::hash<ArrayComponentId>;\n  ArrayComponentId c0id0{std::add_pointer_t<Component0>{nullptr},\n                         Parallel::ArrayIndex<ElementId<1>>(ElementId<1>{0})};\n  ArrayComponentId c0id1{std::add_pointer_t<Component0>{nullptr},\n                         Parallel::ArrayIndex<ElementId<1>>(ElementId<1>{1})};\n\n  ArrayComponentId c1id0 =\n      Parallel::make_array_component_id<Component1>(ElementId<1>{0});\n  ArrayComponentId c1id1 =\n      Parallel::make_array_component_id<Component1>(ElementId<1>{1});\n\n  CHECK(c0id0 == c0id0);\n  CHECK_FALSE(c0id0 != c0id0);\n  CHECK(c0id0 != c0id1);\n  CHECK(c0id0 != c1id0);\n  CHECK(c0id0 != c1id1);\n\n  CHECK(c0id0.component_id() == c0id0.component_id());\n  CHECK_FALSE(c0id0.component_id() != c0id0.component_id());\n  CHECK(c0id0.component_id() == c0id1.component_id());\n  CHECK(c0id0.component_id() != c1id0.component_id());\n  CHECK(c0id0.component_id() != c1id1.component_id());\n\n  CHECK(c0id0.array_index() == c0id0.array_index());\n  CHECK_FALSE(c0id0.array_index() == c0id1.array_index());\n  CHECK(c0id0.array_index() == c1id0.array_index());\n  CHECK_FALSE(c0id0.array_index() == c1id1.array_index());\n\n  CHECK(Hash{}(c0id0) == Hash{}(c0id0));\n  CHECK(Hash{}(c0id0) != Hash{}(c0id1));\n  CHECK(Hash{}(c0id0) != Hash{}(c1id0));\n  CHECK(Hash{}(c0id0) != Hash{}(c1id1));\n  CHECK(Hash{}(c1id0) == Hash{}(c1id0));\n  CHECK(Hash{}(c1id0) != Hash{}(c1id1));\n\n  // Test PUP\n  test_serialization(c0id0);\n  test_serialization(c0id1);\n  test_serialization(c1id0);\n  test_serialization(c1id1);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_Callback.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Value : db::SimpleTag {\n  using type = double;\n};\n\nstruct TimesIterableActionCalled : db::SimpleTag {\n  using type = int;\n};\n\nstruct InitializeValue {\n  using simple_tags = tmpl::list<Value, TimesIterableActionCalled>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box),\n                                               array_index + 1.0, 0);\n    return {Parallel::AlgorithmExecution::Halt, std::nullopt};\n  }\n};\n\nstruct IncrementValue {\n  // Simple action call\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/) {\n    db::mutate<Value>([](const gsl::not_null<double*> value) { *value += 1.0; },\n                      make_not_null(&box));\n  }\n\n  // Threaded action call\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/) {\n    db::mutate<Value>([](const gsl::not_null<double*> value) { *value += 1.0; },\n                      make_not_null(&box));\n  }\n};\n\nstruct MultiplyValueByFactor {\n  // Simple action call\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const double factor) {\n    db::mutate<Value>(\n        [&factor](const gsl::not_null<double*> value) { *value *= factor; },\n        make_not_null(&box));\n  }\n\n  // Threaded action call\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const double factor) {\n    db::mutate<Value>(\n        [&factor](const gsl::not_null<double*> value) { *value *= factor; },\n        make_not_null(&box));\n  }\n};\n\nstruct DoubleValueOfElement0 {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<TimesIterableActionCalled>(\n        [](const gsl::not_null<int*> counter) { ++(*counter); },\n        make_not_null(&box));\n    if (array_index == 0) {\n      db::mutate<Value>(\n          [](const gsl::not_null<double*> value) { *value *= 2.0; },\n          make_not_null(&box));\n      if (db::get<TimesIterableActionCalled>(box) < 5) {\n        if constexpr (Parallel::is_array_v<ParallelComponent>) {\n          return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n        } else if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {\n          return {Parallel::AlgorithmExecution::Halt, std::nullopt};\n        } else {\n          ERROR(\n              \"Only know how to handle arrays and nodegroups in \"\n              \"DoubleValueOfElement0\");\n        }\n      }\n    }\n    return {Parallel::AlgorithmExecution::Halt, std::nullopt};\n  }\n};\n\nstruct CheckValue {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& array_index) {\n    const double value = db::get<Value>(box);\n    const int counter = db::get<TimesIterableActionCalled>(box);\n    CAPTURE_FOR_ERROR(value);\n    CAPTURE_FOR_ERROR(counter);\n    if (array_index == 0) {\n      if constexpr (Parallel::is_array_v<ParallelComponent>) {\n        SPECTRE_PARALLEL_REQUIRE(value == 32.0);\n        SPECTRE_PARALLEL_REQUIRE(counter == 5);\n      } else if constexpr (Parallel::is_nodegroup_v<ParallelComponent>) {\n        SPECTRE_PARALLEL_REQUIRE(value == 14.0);\n        SPECTRE_PARALLEL_REQUIRE(counter == 1);\n      } else {\n        ERROR(\"Only know how to handle arrays and nodegroups in CheckValue\");\n      }\n    } else {\n      SPECTRE_PARALLEL_REQUIRE(counter == 1);\n      SPECTRE_PARALLEL_REQUIRE(value == (array_index == 1 ? 6.0 : 27.0));\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct TestArray {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        tmpl::list<InitializeValue>>,\n                 Parallel::PhaseActions<Parallel::Phase::Execute,\n                                        tmpl::list<DoubleValueOfElement0>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<TestArray>(local_cache);\n    size_t number_of_elements = 3;\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        array_proxy, number_of_elements,\n        static_cast<size_t>(sys::number_of_procs()), {}, global_cache,\n        procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& my_proxy = Parallel::get_parallel_component<TestArray>(local_cache);\n    my_proxy.start_phase(next_phase);\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<CheckValue>(my_proxy);\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct TestNodegroup {\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = true;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        tmpl::list<InitializeValue>>,\n                 Parallel::PhaseActions<Parallel::Phase::Execute,\n                                        tmpl::list<DoubleValueOfElement0>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& my_proxy =\n        Parallel::get_parallel_component<TestNodegroup>(local_cache);\n    my_proxy.start_phase(next_phase);\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<CheckValue>(my_proxy);\n    }\n  }\n};\n\nstruct RunCallbacks {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& /*array_index*/) {\n    auto& array_proxy =\n        Parallel::get_parallel_component<TestArray<Metavariables>>(cache);\n    auto proxy_0 = array_proxy[0];\n    auto proxy_1 = array_proxy[1];\n    auto proxy_2 = array_proxy[2];\n    Parallel::PerformAlgorithmCallback<decltype(proxy_0)> callback_0(proxy_0);\n    Parallel::SimpleActionCallback<IncrementValue, decltype(proxy_1)>\n        callback_1(proxy_1);\n    Parallel::SimpleActionCallback<MultiplyValueByFactor, decltype(proxy_2),\n                                   double>\n        callback_2(proxy_2, 1.5);\n    SPECTRE_PARALLEL_REQUIRE(\n        callback_0.name().find(\"PerformAlgorithmCallback\") !=\n        std::string::npos);\n    SPECTRE_PARALLEL_REQUIRE(\n        (callback_1.name().find(\"SimpleActionCallback\") != std::string::npos and\n         callback_1.name().find(\"IncrementValue\") != std::string::npos));\n    SPECTRE_PARALLEL_REQUIRE(\n        (callback_2.name().find(\"SimpleActionCallback\") != std::string::npos and\n         callback_2.name().find(\"MultiplyValueByFactor\") != std::string::npos));\n    callback_0.invoke();\n    callback_1.invoke();\n    callback_2.invoke();\n    auto callback_3 = serialize_and_deserialize(callback_0);\n    auto callback_4 = serialize_and_deserialize(callback_1);\n    auto callback_5 = serialize_and_deserialize(callback_2);\n    SPECTRE_PARALLEL_REQUIRE(callback_0.is_equal_to(callback_3));\n    SPECTRE_PARALLEL_REQUIRE_FALSE(callback_0.is_equal_to(callback_4));\n    SPECTRE_PARALLEL_REQUIRE(callback_1.is_equal_to(callback_4));\n    SPECTRE_PARALLEL_REQUIRE_FALSE(callback_1.is_equal_to(callback_5));\n    callback_3.invoke();\n    callback_4.invoke();\n    callback_5.invoke();\n    const auto callback_6 = callback_0.get_clone();\n    const auto callback_7 = callback_1.get_clone();\n    const auto callback_8 = callback_2.get_clone();\n    SPECTRE_PARALLEL_REQUIRE(callback_0.is_equal_to(*callback_6));\n    SPECTRE_PARALLEL_REQUIRE(callback_1.is_equal_to(*callback_7));\n    SPECTRE_PARALLEL_REQUIRE(callback_2.is_equal_to(*callback_8));\n    std::vector<std::unique_ptr<Parallel::Callback>> callbacks;\n    callbacks.emplace_back(\n        std::make_unique<Parallel::PerformAlgorithmCallback<decltype(proxy_0)>>(\n            proxy_0));\n    callbacks.emplace_back(\n        std::make_unique<\n            Parallel::SimpleActionCallback<IncrementValue, decltype(proxy_1)>>(\n            proxy_1));\n    callbacks.emplace_back(\n        std::make_unique<Parallel::SimpleActionCallback<\n            MultiplyValueByFactor, decltype(proxy_2), double>>(proxy_2, 2.0));\n    SPECTRE_PARALLEL_REQUIRE_FALSE(callback_2.is_equal_to(*callbacks.back()));\n\n    auto& nodegroup_proxy =\n        Parallel::get_parallel_component<TestNodegroup<Metavariables>>(cache);\n    callbacks.emplace_back(\n        std::make_unique<Parallel::ThreadedActionCallback<\n            IncrementValue, std::decay_t<decltype(nodegroup_proxy)>>>(\n            nodegroup_proxy));\n    callbacks.emplace_back(\n        std::make_unique<Parallel::ThreadedActionCallback<\n            MultiplyValueByFactor, std::decay_t<decltype(nodegroup_proxy)>,\n            double>>(nodegroup_proxy, 2.0));\n    for (const auto& callback : callbacks) {\n      callback->register_with_charm();\n      callback->invoke();\n    }\n    auto pupped_callbacks = serialize_and_deserialize(callbacks);\n    for (const auto& callback : pupped_callbacks) {\n      callback->invoke();\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct TestSingleton {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& my_proxy =\n        Parallel::get_parallel_component<TestSingleton>(local_cache);\n    my_proxy.start_phase(next_phase);\n    if (next_phase == Parallel::Phase::Execute) {\n      Parallel::simple_action<RunCallbacks>(my_proxy);\n    }\n  }\n};\n\nstruct TestMetavariables {\n  using component_list = tmpl::list<TestSingleton<TestMetavariables>,\n                                    TestNodegroup<TestMetavariables>,\n                                    TestArray<TestMetavariables>>;\n\n  static constexpr Options::String help =\n      \"An executable for testing Paralell::Callbacks\";\n\n  static constexpr std::array<Parallel::Phase, 4> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::Testing, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n}  //  namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_CheckpointRestart.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <cstdlib>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmGroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Tags/ResourceInfo.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace CheckpointTest {\nnamespace Tags {\nstruct Log : db::SimpleTag {\n  using type = std::string;\n};\n}  // namespace Tags\n\nstruct InitializeLog {\n  using simple_tags = tmpl::list<Tags::Log>;\n\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const std::string component_name =\n        pretty_type::name<ParallelComponent>() + \" \" +\n        std::to_string(static_cast<int>(array_index));\n    Initialization::mutate_assign<simple_tags>(\n        make_not_null(&box),\n        component_name + \" invoked action InitializeLog\\n\");\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct MutateLog {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Tags::Log>(\n        [&array_index](const gsl::not_null<std::string*> log) {\n          const std::string component_name =\n              pretty_type::short_name<ParallelComponent>() + \" \" +\n              std::to_string(static_cast<int>(array_index));\n          log->append(component_name + \" invoked action MutateLog\\n\");\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct CheckLog {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    const std::string& log = db::get<Tags::Log>(box);\n    const std::string component_name =\n        pretty_type::short_name<ParallelComponent>() + \" \" +\n        std::to_string(static_cast<int>(array_index));\n    const std::string expected_log =\n        component_name + \" invoked action InitializeLog\\n\" + component_name +\n        \" invoked action MutateLog\\n\";\n    SPECTRE_PARALLEL_REQUIRE(log == expected_log);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct CheckNonCheckpointedLog {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // False positive.  Thread-safety of getenv is guaranteed in C++11.\n    // NOLINTNEXTLINE(concurrency-mt-unsafe)\n    const char* const in_restart_env = std::getenv(\"TEST_IN_RESTART\");\n    SPECTRE_PARALLEL_REQUIRE(in_restart_env != nullptr);\n    const bool in_restart = *in_restart_env != '\\0';\n    if (in_restart) {\n      const std::string& log = db::get<Tags::Log>(box);\n      SPECTRE_PARALLEL_REQUIRE(log.empty());\n    }\n    // Check that the global cache is set up correctly\n    SPECTRE_PARALLEL_REQUIRE(\n        db::get<Parallel::Tags::ResourceInfo<Metavariables>>(box) ==\n        cache.get_resource_info());\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <class Metavariables>\nstruct ArrayComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitializeLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<MutateLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<CheckLog>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayComponent>(local_cache);\n\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        array_proxy, 2, static_cast<size_t>(sys::number_of_procs()), {},\n        global_cache, procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ArrayComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct GroupComponent {\n  using chare_type = Parallel::Algorithms::Group;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitializeLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<MutateLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<CheckLog>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<GroupComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct NodegroupComponent {\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitializeLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<MutateLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<CheckLog>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<NodegroupComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct SingletonComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitializeLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<MutateLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<CheckLog>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<SingletonComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct NonCheckpointedComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = false;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitializeLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute, tmpl::list<MutateLog>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<CheckNonCheckpointedLog>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<NonCheckpointedComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace CheckpointTest\n\nstruct TestMetavariables {\n  using component_list =\n      tmpl::list<CheckpointTest::ArrayComponent<TestMetavariables>,\n                 CheckpointTest::GroupComponent<TestMetavariables>,\n                 CheckpointTest::NodegroupComponent<TestMetavariables>,\n                 CheckpointTest::SingletonComponent<TestMetavariables>,\n                 CheckpointTest::NonCheckpointedComponent<TestMetavariables>>;\n\n  static constexpr Options::String help = \"\";\n\n  static constexpr std::array<Parallel::Phase, 5> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::WriteCheckpoint, Parallel::Phase::Testing,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_DetectHangArray.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmGroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace DetectHang {\nstruct CheckNextIterableAction {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    if constexpr (Parallel::is_array_v<ParallelComponent>) {\n      const auto* local_object =\n          Parallel::local(Parallel::get_parallel_component<ParallelComponent>(\n              cache)[array_index]);\n      SPECTRE_PARALLEL_REQUIRE(local_object != nullptr);\n      SPECTRE_PARALLEL_REQUIRE(\n          local_object->deadlock_analysis_next_iterable_action() ==\n          std::string(\"Hang\"));\n    } else if constexpr (Parallel::is_singleton_v<ParallelComponent>) {\n      const auto* local_object = Parallel::local(\n          Parallel::get_parallel_component<ParallelComponent>(cache));\n      SPECTRE_PARALLEL_REQUIRE(local_object != nullptr);\n      SPECTRE_PARALLEL_REQUIRE(\n          local_object->deadlock_analysis_next_iterable_action() ==\n          std::string(\"Hang\"));\n    } else {\n      const auto* local_object = Parallel::local_branch(\n          Parallel::get_parallel_component<ParallelComponent>(cache));\n      SPECTRE_PARALLEL_REQUIRE(local_object != nullptr);\n      SPECTRE_PARALLEL_REQUIRE(\n          local_object->deadlock_analysis_next_iterable_action() ==\n          std::string(\"Hang\"));\n    }\n    Parallel::printf(\"Succeeded for %s\\n\",\n                     pretty_type::name<ParallelComponent>());\n  }\n};\n\nstruct Hang {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& /*box*/,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n  }\n};\n\ntemplate <class Metavariables>\nstruct NodegroupComponent {\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<Hang>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<NodegroupComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct GroupComponent {\n  using chare_type = Parallel::Algorithms::Group;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<Hang>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<GroupComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct SingletonComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<Hang>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<SingletonComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct ArrayComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Evolve, tmpl::list<Hang>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& /*procs_to_ignore*/ = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayComponent>(local_cache);\n    // we only want one array component for this test.\n    array_proxy[0].insert(global_cache, tuples::TaggedTuple<>{}, 0);\n    array_proxy.doneInserting();\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ArrayComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace DetectHang\n\nstruct TestMetavariables {\n  using component_list =\n      tmpl::list<DetectHang::ArrayComponent<TestMetavariables>,\n                 DetectHang::SingletonComponent<TestMetavariables>,\n                 DetectHang::GroupComponent<TestMetavariables>,\n                 DetectHang::NodegroupComponent<TestMetavariables>>;\n\n  static constexpr Options::String help = \"\";\n\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Evolve,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  // [deadlock_analysis_function]\n  static void run_deadlock_analysis_simple_actions(\n      Parallel::GlobalCache<TestMetavariables>& cache,\n      const std::vector<std::string>& deadlocked_components) {\n    // [deadlock_analysis_function]\n    SPECTRE_PARALLEL_REQUIRE(\n        alg::find(deadlocked_components, std::string{\"ArrayComponent\"}) !=\n        deadlocked_components.end());\n    SPECTRE_PARALLEL_REQUIRE(\n        alg::find(deadlocked_components, std::string{\"SingletonComponent\"}) !=\n        deadlocked_components.end());\n    SPECTRE_PARALLEL_REQUIRE(\n        alg::find(deadlocked_components, std::string{\"GroupComponent\"}) !=\n        deadlocked_components.end());\n    SPECTRE_PARALLEL_REQUIRE(\n        alg::find(deadlocked_components, std::string{\"NodegroupComponent\"}) !=\n        deadlocked_components.end());\n\n    Parallel::simple_action<DetectHang::CheckNextIterableAction>(\n        Parallel::get_parallel_component<\n            DetectHang::ArrayComponent<TestMetavariables>>(cache));\n    Parallel::simple_action<DetectHang::CheckNextIterableAction>(\n        Parallel::get_parallel_component<\n            DetectHang::SingletonComponent<TestMetavariables>>(cache));\n    Parallel::simple_action<DetectHang::CheckNextIterableAction>(\n        Parallel::get_parallel_component<\n            DetectHang::GroupComponent<TestMetavariables>>(cache));\n    Parallel::simple_action<DetectHang::CheckNextIterableAction>(\n        Parallel::get_parallel_component<\n            DetectHang::NodegroupComponent<TestMetavariables>>(cache));\n  }\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_DomainDiagnosticInfo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/DomainDiagnosticInfo.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Info.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace domain {\nnamespace {\nstruct EmptyMetavars {\n  using component_list = tmpl::list<>;\n};\n\nvoid test_diagnostic_info() {\n  std::vector<size_t> procs_per_node{2, 7, 5};\n  Parallel::GlobalCache<EmptyMetavars> cache{\n      tuples::TaggedTuple<>{}, {}, procs_per_node};\n\n  const size_t number_of_cores = Parallel::number_of_procs<size_t>(cache);\n  const size_t number_of_nodes = Parallel::number_of_nodes<size_t>(cache);\n\n  std::vector<size_t> elements_per_core(number_of_cores, 0_st);\n  std::vector<size_t> elements_per_node(number_of_nodes, 0_st);\n  std::vector<size_t> grid_points_per_core(number_of_cores, 0_st);\n  std::vector<size_t> grid_points_per_node(number_of_nodes, 0_st);\n\n  alg::iota(elements_per_core, 1_st);\n  alg::iota(grid_points_per_core, 10_st);\n\n  size_t starting_core = 0;\n  size_t ending_core = procs_per_node[0];\n  for (size_t i = 0; i < procs_per_node.size(); i++) {\n    ending_core = starting_core + procs_per_node[i];\n    for (size_t j = starting_core; j < ending_core; j++) {\n      elements_per_node[i] += elements_per_core[j];\n      grid_points_per_node[i] += grid_points_per_core[j];\n    }\n\n    starting_core = ending_core;\n  }\n\n  const size_t total_number_of_blocks = 13;\n\n  const std::string info = diagnostic_info(\n      total_number_of_blocks, cache, elements_per_core, elements_per_node,\n      grid_points_per_core, grid_points_per_node);\n\n  const std::string expected_info =\n      \"----- Domain Info -----\\n\"\n      \"Total blocks: 13\\n\"\n      \"Total elements: 105\\n\"\n      \"Total grid points: 231\\n\"\n      \"Number of cores: 14\\n\"\n      \"Number of nodes: 3\\n\"\n      \"Elements per core: (1,2,3,4,5,6,7,8,9,10,11,12,13,14)\\n\"\n      \"Elements per node: (3,42,60)\\n\"\n      \"Grid points per core: (10,11,12,13,14,15,16,17,18,19,20,21,22,23)\\n\"\n      \"Grid points per node: (21,105,105)\\n\"\n      \"-----------------------\\n\";\n\n  CHECK(info == expected_info);\n\n#ifdef SPECTRE_DEBUG\n  elements_per_core[0] += 1;\n  CHECK_THROWS_WITH(\n      ([&cache, &elements_per_core, &elements_per_node, &grid_points_per_core,\n        &grid_points_per_node]() {\n        [[maybe_unused]] const std::string bad_info = diagnostic_info(\n            total_number_of_blocks, cache, elements_per_core, elements_per_node,\n            grid_points_per_core, grid_points_per_node);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Number of elements determined from elements_per_core (\"));\n\n  elements_per_core[0] -= 1;\n  grid_points_per_core[0] += 1;\n  CHECK_THROWS_WITH(\n      ([&cache, &elements_per_core, &elements_per_node, &grid_points_per_core,\n        &grid_points_per_node]() {\n        [[maybe_unused]] const std::string bad_info = diagnostic_info(\n            total_number_of_blocks, cache, elements_per_core, elements_per_node,\n            grid_points_per_core, grid_points_per_node);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Number of grid points determined from grid_points_per_core (\"));\n\n  grid_points_per_core[0] -= 1;\n  // Pushing back 0 so the number of elements are the same, but the number of\n  // cores is different, otherwise we would hit a different ASSERT first.\n  elements_per_core.push_back(0);\n  CHECK_THROWS_WITH(\n      ([&cache, &elements_per_core, &elements_per_node, &grid_points_per_core,\n        &grid_points_per_node]() {\n        [[maybe_unused]] const std::string bad_info = diagnostic_info(\n            total_number_of_blocks, cache, elements_per_core, elements_per_node,\n            grid_points_per_core, grid_points_per_node);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Number of cores determined from elements_per_core (\"));\n\n  elements_per_core.pop_back();\n  grid_points_per_core.push_back(0);\n  CHECK_THROWS_WITH(\n      ([&cache, &elements_per_core, &elements_per_node, &grid_points_per_core,\n        &grid_points_per_node]() {\n        [[maybe_unused]] const std::string bad_info = diagnostic_info(\n            total_number_of_blocks, cache, elements_per_core, elements_per_node,\n            grid_points_per_core, grid_points_per_node);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Number of cores determined from grid_points_per_core (\"));\n\n  grid_points_per_core.pop_back();\n  elements_per_node.push_back(0);\n  CHECK_THROWS_WITH(\n      ([&cache, &elements_per_core, &elements_per_node, &grid_points_per_core,\n        &grid_points_per_node]() {\n        [[maybe_unused]] const std::string bad_info = diagnostic_info(\n            total_number_of_blocks, cache, elements_per_core, elements_per_node,\n            grid_points_per_core, grid_points_per_node);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Number of nodes determined from elements_per_node (\"));\n\n  elements_per_node.pop_back();\n  grid_points_per_node.push_back(0);\n  CHECK_THROWS_WITH(\n      ([&cache, &elements_per_core, &elements_per_node, &grid_points_per_core,\n        &grid_points_per_node]() {\n        [[maybe_unused]] const std::string bad_info = diagnostic_info(\n            total_number_of_blocks, cache, elements_per_core, elements_per_node,\n            grid_points_per_core, grid_points_per_node);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Number of nodes determined from grid_points_per_node (\"));\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Domain.DiagnosticInfo\", \"[Unit][Domain]\") {\n  test_diagnostic_info();\n}\n}  // namespace domain\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_DynamicInsertion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <deque>\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// This executable tests dynamic insertion and deletion of array chare elements.\n//\n// In Parallel::Phase::Initialization 92 elements are created with the simple\n// item Value.  The action InitializeValue is called which sets the Value to be\n// the array index of the element plus one.\n//\n// In Parallel::Phase::Execute, the simple action ChangeArray is called.\n//    - Elements with array index 0 and 1 and prime numbers > 46 are unchanged\n//    - Each element with an array index that is a power of 2 is split into a\n//      number of children equal to the array index.  Each child will eventually\n//      set Value to be (array index / number of children) while the parent will\n//      eventually be deleted.\n//    - Each element with an array index that is a prime number in the range\n//      [3,46] will be joined with those elements whose array index has prime\n//      factors less than or equal to it (e.g. 3 will join any element whose\n//      array index is 2^m 3^n for some (m,n)).  The newly created parent\n//      element will eventually set its Value to the sum of the Values of the\n//      children, while the children will eventually be deleted.\n//\n//   An element that is split will create its children sequentially, with the\n//   last created child element executing the callback of the simple action\n//   SendDataToChildren.  SendDataToChildren sends each child its share of Value\n//   by calling the simple action SetValue and then deletes the parent element.\n//\n//   The prime member of joining elements will create the new parent element and\n//   execute the callback CollectDataFromChildren on the prime member.\n//   CollectDataFromChildren will collect data from the child, either call\n//   CollectDataFromChildren on the next child or (when executing on the last\n//   child) call SetValue on the new parent element, and then delete the child\n//   element.\n//\n// In Parallel::Phase::Testing, the Values are summed over all the array\n// elements via the reduction action ArrayReduce which executes the\n// CheckReduction action on the singleton component.  The test succeeds if the\n// sum over values of the array has been preserved.\n\nnamespace {\nstatic constexpr int initial_number_of_1d_array_elements = 92;\nstatic const double expected_value = 0.5 * initial_number_of_1d_array_elements *\n                                     (initial_number_of_1d_array_elements + 1);\n\nstruct CheckReduction {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const double& value) {\n    SPECTRE_PARALLEL_REQUIRE(expected_value == value);\n  }\n};\n\nstruct Value : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <typename Metavariables>\nstruct TestAmrComponent {\n  using metavariables = Metavariables;\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using simple_tags_from_options = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase /*next_phase*/,\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache_proxy*/) {}\n};\n\nstruct InitializeValue {\n  using simple_tags = tmpl::list<Value>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& array_index, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box),\n                                               array_index + 1.0);\n    return {Parallel::AlgorithmExecution::Halt, std::nullopt};\n  }\n};\n\nstruct ArrayReduce {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(const db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    const auto& my_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index];\n    const auto& singleton_proxy =\n        Parallel::get_parallel_component<TestAmrComponent<Metavariables>>(\n            cache);\n    Parallel::ReductionData<Parallel::ReductionDatum<double, funcl::Plus<>>>\n        reduction_data{db::get<Value>(box)};\n    Parallel::contribute_to_reduction<CheckReduction>(reduction_data, my_proxy,\n                                                      singleton_proxy);\n  }\n};\n\nstruct SetValue {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const double value) {\n    db::mutate<Value>(\n        [&value](const gsl::not_null<double*> box_value) {\n          *box_value = value;\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct SendDataToChildren {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index,\n                    const std::vector<ArrayIndex>& new_ids) {\n    const double value = db::get<Value>(box) / new_ids.size();\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    for (const auto& new_id : new_ids) {\n      Parallel::simple_action<SetValue>(array_proxy[new_id], value);\n    }\n    array_proxy[array_index].ckDestroy();\n  }\n};\n\nstruct CollectDataFromChildren {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index,\n                    const ArrayIndex& parent_index,\n                    std::deque<ArrayIndex> additional_children_ids,\n                    double accumulated_value) {\n    accumulated_value += db::get<Value>(box);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    if (additional_children_ids.empty()) {\n      Parallel::simple_action<SetValue>(array_proxy[parent_index],\n                                        accumulated_value);\n    } else {\n      const auto next_child_id = additional_children_ids.front();\n      additional_children_ids.pop_front();\n      Parallel::simple_action<CollectDataFromChildren>(\n          array_proxy[next_child_id], parent_index, additional_children_ids,\n          accumulated_value);\n    }\n    array_proxy[array_index].ckDestroy();\n  }\n};\n\nstruct CreateChild {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex, typename ElementProxy,\n            typename ElementIndex>\n  static void apply(\n      db::DataBox<DbTagList>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ElementProxy element_proxy,\n      ElementIndex parent_id, ElementIndex child_id,\n      std::vector<ElementIndex> children_ids,\n      std::unordered_map<Parallel::Phase, size_t> phase_bookmarks) {\n    auto my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    ASSERT(\n        alg::count(children_ids, child_id) == 1,\n        \"Child \" << child_id << \" does not exist uniquely in \" << children_ids);\n    const auto child_it = alg::find(children_ids, child_id);\n    if (*child_it == children_ids.back()) {\n      auto parent_proxy = element_proxy(parent_id);\n      element_proxy(child_id).insert(\n          cache.thisProxy, Parallel::Phase::AdjustDomain, phase_bookmarks,\n          std::make_unique<Parallel::SimpleActionCallback<\n              SendDataToChildren, decltype(parent_proxy),\n              std::vector<ElementIndex>>>(parent_proxy,\n                                          std::move(children_ids)));\n    } else {\n      const auto next_child_it = std::next(child_it);\n      auto next_child = *next_child_it;\n      element_proxy(child_id).insert(\n          cache.thisProxy, Parallel::Phase::AdjustDomain, phase_bookmarks,\n          std::make_unique<Parallel::SimpleActionCallback<\n              CreateChild, decltype(my_proxy), ElementProxy, ElementIndex,\n              ElementIndex, std::vector<ElementIndex>,\n              std::unordered_map<Parallel::Phase, size_t>>>(\n              my_proxy, std::move(element_proxy), std::move(parent_id),\n              std::move(next_child), std::move(children_ids), phase_bookmarks));\n    }\n  }\n};\n\nstruct ChangeArray {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(const db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    auto my_proxy = array_proxy[array_index];\n    const auto& phase_bookmarks = Parallel::local(my_proxy)->phase_bookmarks();\n    auto create_children = [&cache, &array_index, &array_proxy,\n                            &phase_bookmarks](\n                               const size_t number_of_new_elements) {\n      std::vector<int> new_ids(number_of_new_elements);\n      std::iota(new_ids.begin(), new_ids.end(), 100 * array_index);\n      auto& singleton_proxy =\n          Parallel::get_parallel_component<TestAmrComponent<Metavariables>>(\n              cache);\n      Parallel::simple_action<CreateChild>(singleton_proxy, array_proxy,\n                                           array_index, new_ids.front(),\n                                           new_ids, phase_bookmarks);\n    };\n\n    auto create_parent = [&cache, &array_proxy, &my_proxy,\n                          &phase_bookmarks](std::deque<int> ids_to_join) {\n      int id_of_first_child = ids_to_join.front();\n      ids_to_join.pop_front();\n      int parent_id = 100 * id_of_first_child;\n      array_proxy(parent_id).insert(\n          cache.thisProxy, Parallel::Phase::Execute, phase_bookmarks,\n          std::make_unique<Parallel::SimpleActionCallback<\n              CollectDataFromChildren, decltype(my_proxy), int, std::deque<int>,\n              double>>(my_proxy, std::move(parent_id), std::move(ids_to_join),\n                       0.0));\n    };\n\n    std::vector<size_t> ids_to_split{2, 4, 8, 16, 32, 64};\n    std::vector<size_t> ids_to_join{3,  5,  7,  11, 13, 17, 19,\n                                    23, 29, 31, 37, 41, 43};\n\n    for (const auto id_to_split : ids_to_split) {\n      if (id_to_split == static_cast<size_t>(array_index)) {\n        create_children(id_to_split);\n      }\n    }\n\n    if (3 == array_index) {\n      create_parent({3, 6, 9, 12, 18, 24, 27, 36, 48, 54, 72, 81});\n    } else if (5 == array_index) {\n      create_parent({5, 10, 15, 20, 25, 30, 40, 45, 50, 60, 75, 80, 90});\n    } else if (7 == array_index) {\n      create_parent({7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 84});\n    } else if (11 == array_index) {\n      create_parent({11, 22, 33, 44, 55, 66, 77, 88});\n    } else if (13 == array_index) {\n      create_parent({13, 26, 39, 52, 65, 78, 91});\n    } else if (17 == array_index) {\n      create_parent({17, 34, 51, 68, 85});\n    } else if (19 == array_index) {\n      create_parent({19, 38, 57, 76});\n    } else if (23 == array_index) {\n      create_parent({23, 46, 69});\n    } else if (29 == array_index) {\n      create_parent({29, 58, 87});\n    } else if (31 == array_index) {\n      create_parent({31, 62});\n    } else if (37 == array_index) {\n      create_parent({37, 74});\n    } else if (41 == array_index) {\n      create_parent({41, 82});\n    } else if (43 == array_index) {\n      create_parent({43, 86});\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct TestArray {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        tmpl::list<InitializeValue>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<TestArray>(local_cache);\n\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        array_proxy, static_cast<size_t>(initial_number_of_1d_array_elements),\n        static_cast<size_t>(sys::number_of_procs()), {}, global_cache,\n        procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& my_proxy = Parallel::get_parallel_component<TestArray>(local_cache);\n    my_proxy.start_phase(next_phase);\n    if (next_phase == Parallel::Phase::Execute) {\n      Parallel::simple_action<ChangeArray>(my_proxy);\n    }\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<ArrayReduce>(my_proxy);\n    }\n  }\n};\n\nstruct TestMetavariables {\n  using component_list = tmpl::list<TestAmrComponent<TestMetavariables>,\n                                    TestArray<TestMetavariables>>;\n\n  static constexpr Options::String help =\n      \"An executable for testing the dynamic insertion and deletion of \"\n      \"elements of an array parallel component\";\n\n  static constexpr std::array<Parallel::Phase, 4> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::Testing, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nvoid register_callback() {\n  register_classes_with_charm(\n      tmpl::list<\n          Parallel::SimpleActionCallback<\n              CreateChild,\n              CProxy_AlgorithmSingleton<TestAmrComponent<TestMetavariables>,\n                                        int>,\n              CProxy_AlgorithmArray<TestArray<TestMetavariables>, int>, int,\n              int, std::vector<int>,\n              std::unordered_map<Parallel::Phase, size_t>>,\n          Parallel::SimpleActionCallback<\n              SendDataToChildren,\n              CProxyElement_AlgorithmArray<TestArray<TestMetavariables>, int>,\n              std::vector<int>>,\n          Parallel::SimpleActionCallback<\n              CollectDataFromChildren,\n              CProxyElement_AlgorithmArray<TestArray<TestMetavariables>, int>,\n              int, std::deque<int>, double>>{});\n}\n}  //  namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({&register_callback}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_DynamicInsertionState.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/CheckpointAndExitAfterWallclock.hpp\"\n#include \"Parallel/PhaseControl/ExecutePhaseChange.hpp\"\n#include \"Parallel/PhaseControl/Factory.hpp\"\n#include \"Parallel/PhaseControl/InitializePhaseChangeDecisionData.hpp\"\n#include \"Parallel/PhaseControl/PhaseChange.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Parallel/PhaseControl/VisitAndReturn.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// This executable tests that newly created elements are at the same step in\n// an iterable action list as the already existing elements when the algorithm\n// resumes.\n\nnamespace {\nconstexpr size_t number_of_iterations = 5;\nconstexpr size_t number_of_doubling_actions = 2;\nconstexpr size_t expected_value =\n    two_to_the(number_of_iterations * number_of_doubling_actions + 1);\n\nstruct CheckReduction {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const int& value) {\n    SPECTRE_PARALLEL_REQUIRE(expected_value == value);\n  }\n};\n\nstruct Value : db::SimpleTag {\n  using type = int;\n};\n\nstruct Iteration : db::SimpleTag {\n  using type = size_t;\n};\n\ntemplate <typename Metavariables>\nstruct TestAmrComponent {\n  using metavariables = Metavariables;\n  using chare_type = Parallel::Algorithms::Singleton;\n  static constexpr bool checkpoint_data = true;\n  using simple_tags_from_options = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase /*next_phase*/,\n      Parallel::CProxy_GlobalCache<Metavariables>& /*global_cache_proxy*/) {}\n};\n\nstruct Initialize {\n  using simple_tags = tmpl::list<Value, Iteration>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 2, 0_st);\n    return {Parallel::AlgorithmExecution::Halt, std::nullopt};\n  }\n};\n\ntemplate <size_t StepNumber>\nstruct DoubleValue {\n  using simple_tags = tmpl::list<Value>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Value>([](const gsl::not_null<int*> value) { *value *= 2; },\n                      make_not_null(&box));\n\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct CheckForTermination {\n  using simple_tags = tmpl::list<Value>;\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Iteration>(\n        [](const gsl::not_null<size_t*> iteration) { ++(*iteration); },\n        make_not_null(&box));\n\n    return {db::get<Iteration>(box) == number_of_iterations\n                ? Parallel::AlgorithmExecution::Halt\n                : Parallel::AlgorithmExecution::Continue,\n            std::nullopt};\n  }\n};\n\nstruct ArrayReduce {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(const db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    const auto& my_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache)[array_index];\n    const auto& singleton_proxy =\n        Parallel::get_parallel_component<TestAmrComponent<Metavariables>>(\n            cache);\n    Parallel::ReductionData<Parallel::ReductionDatum<int, funcl::Plus<>>>\n        reduction_data{db::get<Value>(box)};\n    Parallel::contribute_to_reduction<CheckReduction>(reduction_data, my_proxy,\n                                                      singleton_proxy);\n  }\n};\n\nstruct SetValue {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const int value,\n                    const size_t iteration) {\n    db::mutate<Value, Iteration>(\n        [&value, &iteration](const gsl::not_null<int*> box_value,\n                             const gsl::not_null<size_t*> box_iteration) {\n          *box_value = value;\n          *box_iteration = iteration;\n        },\n        make_not_null(&box));\n  }\n};\n\nstruct SendDataToChildren {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index,\n                    const std::vector<ArrayIndex>& new_ids) {\n    const int value = db::get<Value>(box) / 2;\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    for (const auto& new_id : new_ids) {\n      Parallel::simple_action<SetValue>(array_proxy[new_id], value,\n                                        db::get<Iteration>(box));\n    }\n    array_proxy[array_index].ckDestroy();\n  }\n};\n\nstruct CollectDataFromChildren {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagList>& box,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index,\n                    const ArrayIndex& parent_index,\n                    std::deque<ArrayIndex> additional_children_ids,\n                    int accumulated_value, size_t iteration) {\n    accumulated_value += db::get<Value>(box);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    if (additional_children_ids.empty()) {\n      Parallel::simple_action<SetValue>(array_proxy[parent_index],\n                                        accumulated_value, iteration);\n    } else {\n      const auto next_child_id = additional_children_ids.front();\n      additional_children_ids.pop_front();\n      Parallel::simple_action<CollectDataFromChildren>(\n          array_proxy[next_child_id], parent_index, additional_children_ids,\n          accumulated_value, iteration);\n    }\n    array_proxy[array_index].ckDestroy();\n  }\n};\n\nstruct CreateChild {\n  template <typename ParallelComponent, typename DbTagList,\n            typename Metavariables, typename ArrayIndex, typename ElementProxy,\n            typename ElementIndex>\n  static void apply(\n      db::DataBox<DbTagList>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ElementProxy element_proxy,\n      ElementIndex parent_id, ElementIndex child_id,\n      std::vector<ElementIndex> children_ids,\n      std::unordered_map<Parallel::Phase, size_t> phase_bookmarks) {\n    auto my_proxy = Parallel::get_parallel_component<ParallelComponent>(cache);\n    ASSERT(\n        alg::count(children_ids, child_id) == 1,\n        \"Child \" << child_id << \" does not exist uniquely in \" << children_ids);\n    const auto child_it = alg::find(children_ids, child_id);\n    if (*child_it == children_ids.back()) {\n      auto parent_proxy = element_proxy(parent_id);\n      element_proxy(child_id).insert(\n          cache.thisProxy, Parallel::Phase::AdjustDomain, phase_bookmarks,\n          std::make_unique<Parallel::SimpleActionCallback<\n              SendDataToChildren, decltype(parent_proxy),\n              std::vector<ElementIndex>>>(parent_proxy,\n                                          std::move(children_ids)));\n    } else {\n      const auto next_child_it = std::next(child_it);\n      auto next_child = *next_child_it;\n      element_proxy(child_id).insert(\n          cache.thisProxy, Parallel::Phase::AdjustDomain, phase_bookmarks,\n          std::make_unique<Parallel::SimpleActionCallback<\n              CreateChild, decltype(my_proxy), ElementProxy, ElementIndex,\n              ElementIndex, std::vector<ElementIndex>,\n              std::unordered_map<Parallel::Phase, size_t>>>(\n              my_proxy, element_proxy, std::move(parent_id),\n              std::move(next_child), std::move(children_ids), phase_bookmarks));\n    }\n  }\n};\n\nstruct ChangeArray {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(const db::DataBox<DbTags>& box,\n                    Parallel::GlobalCache<Metavariables>& cache,\n                    const ArrayIndex& array_index) {\n    auto& array_proxy =\n        Parallel::get_parallel_component<ParallelComponent>(cache);\n    auto my_proxy = array_proxy[array_index];\n\n    const auto& phase_bookmarks = Parallel::local(my_proxy)->phase_bookmarks();\n\n    auto create_children = [&cache, &array_index, &array_proxy,\n                            &phase_bookmarks]() {\n      std::vector new_ids{1, 2};\n      auto& singleton_proxy =\n          Parallel::get_parallel_component<TestAmrComponent<Metavariables>>(\n              cache);\n      Parallel::simple_action<CreateChild>(singleton_proxy, array_proxy,\n                                           array_index, new_ids.front(),\n                                           new_ids, phase_bookmarks);\n    };\n\n    auto create_parent = [&box, &cache, &array_proxy, &my_proxy,\n                          &phase_bookmarks](std::deque<int> ids_to_join) {\n      ids_to_join.pop_front();\n      const int parent_id = 0;\n      array_proxy(parent_id).insert(\n          cache.thisProxy, Parallel::Phase::AdjustDomain, phase_bookmarks,\n          std::make_unique<Parallel::SimpleActionCallback<\n              CollectDataFromChildren, decltype(my_proxy), int, std::deque<int>,\n              int, size_t>>(my_proxy, parent_id, std::move(ids_to_join), 0,\n                            db::get<Iteration>(box)));\n    };\n\n    if (0 == array_index) {\n      create_children();\n    } else if (1 == array_index) {\n      create_parent({1, 2});\n    }\n  }\n};\n\ntemplate <class Metavariables>\nstruct TestArray {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<Initialize>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Execute,\n          tmpl::list<DoubleValue<0>, PhaseControl::Actions::ExecutePhaseChange,\n                     DoubleValue<1>, CheckForTermination>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<TestArray>(local_cache);\n\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        array_proxy, 1_st, static_cast<size_t>(sys::number_of_procs()), {},\n        global_cache, procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& my_proxy = Parallel::get_parallel_component<TestArray>(local_cache);\n    my_proxy.start_phase(next_phase);\n    if (next_phase == Parallel::Phase::AdjustDomain) {\n      Parallel::simple_action<ChangeArray>(my_proxy);\n    }\n    if (next_phase == Parallel::Phase::Testing) {\n      Parallel::simple_action<ArrayReduce>(my_proxy);\n    }\n  }\n};\n\nstruct TestMetavariables {\n  using component_list = tmpl::list<TestAmrComponent<TestMetavariables>,\n                                    TestArray<TestMetavariables>>;\n\n  static constexpr Options::String help =\n      \"An executable for testing the dynamic insertion and deletion of \"\n      \"elements of an array parallel component\";\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<PhaseChange, PhaseControl::factory_creatable_classes>,\n        tmpl::pair<Trigger, tmpl::append<Triggers::logical_triggers>>>;\n  };\n\n  static constexpr std::array<Parallel::Phase, 4> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Execute,\n       Parallel::Phase::Testing, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nvoid register_callback() {\n  register_classes_with_charm(\n      tmpl::list<\n          Parallel::SimpleActionCallback<\n              CreateChild,\n              CProxy_AlgorithmSingleton<TestAmrComponent<TestMetavariables>,\n                                        int>,\n              CProxy_AlgorithmArray<TestArray<TestMetavariables>, int>, int,\n              int, std::vector<int>,\n              std::unordered_map<Parallel::Phase, size_t>>,\n          Parallel::SimpleActionCallback<\n              SendDataToChildren,\n              CProxyElement_AlgorithmArray<TestArray<TestMetavariables>, int>,\n              std::vector<int>>,\n          Parallel::SimpleActionCallback<\n              CollectDataFromChildren,\n              CProxyElement_AlgorithmArray<TestArray<TestMetavariables>, int>,\n              int, std::deque<int>, int, size_t>>{});\n}\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&register_factory_classes_with_charm<TestMetavariables>,\n       &register_callback},\n      {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_DynamicInsertionState.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nPhaseChangeAndTriggers:\n  - Trigger: Always\n    PhaseChanges:\n      - VisitAndReturn(AdjustDomain)\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_ExitCode.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Parallel/ExitCode.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.ExitCode\", \"[Parallel][Unit]\") {\n  CHECK(get_output(Parallel::ExitCode::Complete) == \"0 (Complete)\");\n  CHECK(get_output(Parallel::ExitCode::Abort) == \"1 (Abort)\");\n  CHECK(get_output(Parallel::ExitCode::ContinueFromCheckpoint) ==\n        \"2 (ContinueFromCheckpoint)\");\n  CHECK_THROWS_WITH(get_output(static_cast<Parallel::ExitCode>(3)),\n                    Catch::Matchers::ContainsSubstring(\"Unknown exit code: 3\"));\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_FifoCache.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <atomic>\n#include <chrono>\n#include <memory>\n#include <thread>\n\n#include \"Parallel/FifoCache.hpp\"\n\nnamespace {\nstruct T0 {\n  int a;\n  float b;\n  bool operator==(const T0& rhs) const = default;\n};\n\nvoid test_basic() {\n#if defined(SPECTRE_DEBUG)\n  {\n    CHECK_THROWS_WITH(Parallel::FifoCache<T0>(0u),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Must have a positive capacity but got 0\"));\n  }\n#endif\n  Parallel::FifoCache<T0> fc{3u};\n  CHECK_FALSE(\n      fc.find([](const T0& t0) -> bool { return t0.a == 1; }).has_value());\n  {\n    INFO(\"Test push.\");\n    const auto t0a1 =\n        fc.push(T0{1, 1.32}, [](const T0& t0) -> bool { return t0.a == 1; });\n    REQUIRE(t0a1.has_value());\n    CHECK(t0a1.value() == T0{1, 1.32});\n  }\n  {\n    INFO(\"Test finding pushed value more than once.\");\n    const auto t0a1 = fc.find([](const T0& t0) -> bool { return t0.a == 1; });\n    REQUIRE(t0a1.has_value());\n    CHECK(t0a1.value() == T0{1, 1.32});\n    const auto t0a1_2 = fc.find([](const T0& t0) -> bool { return t0.a == 1; });\n    REQUIRE(t0a1_2.has_value());\n    CHECK(std::addressof(t0a1) != std::addressof(t0a1_2));\n    CHECK(std::addressof(t0a1.value()) == std::addressof(t0a1_2.value()));\n    CHECK_FALSE(\n        fc.find([](const T0& t0) -> bool { return t0.a == 2; }).has_value());\n  }\n  {\n    INFO(\"Test pushing same value doesn't change exist object.\");\n    const auto t0a1 = fc.find([](const T0& t0) -> bool { return t0.a == 1; });\n    REQUIRE(t0a1.has_value());\n    CHECK(t0a1.value() == T0{1, 1.32});\n    const auto t0a1_2 =\n        fc.push(T0{1, 1.32}, [](const T0& t0) -> bool { return t0.a == 1; });\n    REQUIRE(t0a1_2.has_value());\n    CHECK(std::addressof(t0a1) != std::addressof(t0a1_2));\n    CHECK(std::addressof(t0a1.value()) == std::addressof(t0a1_2.value()));\n    CHECK_FALSE(\n        fc.find([](const T0& t0) -> bool { return t0.a == 2; }).has_value());\n  }\n  {\n    INFO(\"Insert to fill.\");\n    {\n      const auto a2 =\n          fc.push(T0{2, 2.32}, [](const T0& t0) -> bool { return t0.a == 2; });\n      REQUIRE(a2.has_value());\n      CHECK(a2.value() == T0{2, 2.32});\n    }\n    {\n      const auto a1 = fc.find([](const T0& t0) -> bool { return t0.a == 1; });\n      REQUIRE(a1.has_value());\n      CHECK(a1.value() == T0{1, 1.32});\n    }\n    {\n      const auto a3 =\n          fc.push(T0{3, 3.32}, [](const T0& t0) -> bool { return t0.a == 3; });\n      REQUIRE(a3.has_value());\n      CHECK(a3.value() == T0{3, 3.32});\n    }\n    {\n      const auto a1 = fc.find([](const T0& t0) -> bool { return t0.a == 1; });\n      REQUIRE(a1.has_value());\n      CHECK(a1.value() == T0{1, 1.32});\n      const auto a2 = fc.find([](const T0& t0) -> bool { return t0.a == 2; });\n      REQUIRE(a2.has_value());\n      CHECK(a2.value() == T0{2, 2.32});\n      const auto a3 = fc.find([](const T0& t0) -> bool { return t0.a == 3; });\n      REQUIRE(a3.has_value());\n      CHECK(a3.value() == T0{3, 3.32});\n    }\n    {\n      const auto a4 =\n          fc.push(T0{4, 4.32}, [](const T0& t0) -> bool { return t0.a == 4; });\n      REQUIRE(a4.has_value());\n      CHECK(a4.value() == T0{4, 4.32});\n    }\n    {\n      const auto a1 = fc.find([](const T0& t0) -> bool { return t0.a == 1; });\n      REQUIRE_FALSE(a1.has_value());\n      const auto a2 = fc.find([](const T0& t0) -> bool { return t0.a == 2; });\n      REQUIRE(a2.has_value());\n      CHECK(a2.value() == T0{2, 2.32});\n      const auto a3 = fc.find([](const T0& t0) -> bool { return t0.a == 3; });\n      REQUIRE(a3.has_value());\n      CHECK(a3.value() == T0{3, 3.32});\n      const auto a4 = fc.find([](const T0& t0) -> bool { return t0.a == 4; });\n      REQUIRE(a4.has_value());\n      CHECK(a4.value() == T0{4, 4.32});\n    }\n  }\n  {\n    INFO(\"Compute call is lazily evaluated.\");\n    {\n      bool invoked_compute_value = false;\n      const auto compute_value = [&invoked_compute_value]() {\n        CHECK_FALSE(invoked_compute_value);\n        invoked_compute_value = true;\n        return T0{5, 5.32};\n      };\n      const auto a5 = fc.push(compute_value,\n                              [](const T0& t0) -> bool { return t0.a == 5; });\n      REQUIRE(invoked_compute_value);\n      REQUIRE(a5.has_value());\n      CHECK(a5.value() == T0{5, 5.32});\n\n      invoked_compute_value = false;\n      const auto a5b = fc.push(compute_value,\n                               [](const T0& t0) -> bool { return t0.a == 5; });\n      REQUIRE_FALSE(invoked_compute_value);\n      REQUIRE(a5b.has_value());\n      CHECK(a5b.value() == T0{5, 5.32});\n\n      CHECK(std::addressof(a5) != std::addressof(a5b));\n      CHECK(std::addressof(a5.value()) == std::addressof(a5b.value()));\n    }\n  }\n}\n\nvoid test_non_copyable() {\n  Parallel::FifoCache<std::unique_ptr<T0>> fc{3u};\n  CHECK_FALSE(fc.find([](const std::unique_ptr<T0>& t0) -> bool {\n                  return t0->a == 1;\n                }).has_value());\n  const auto t0a1 =\n      fc.push(std::make_unique<T0>(T0{1, 1.32}),\n              [](const std::unique_ptr<T0>& t0) -> bool { return t0->a == 1; });\n  REQUIRE(t0a1.has_value());\n  REQUIRE(t0a1.value() != nullptr);\n  CHECK(*(t0a1.value()) == T0{1, 1.32});\n}\n\nvoid test_deadlock() {\n  Parallel::FifoCache<T0> fc{3u};\n  CHECK_FALSE(\n      fc.find([](const T0& t0) -> bool { return t0.a == 1; }).has_value());\n  auto t0a1 =\n      fc.push(T0{1, 1.32}, [](const T0& t0) -> bool { return t0.a == 1; });\n  REQUIRE(t0a1.has_value());\n  CHECK(t0a1.value() == T0{1, 1.32});\n\n  std::atomic<int> t0_state{0};\n  std::thread thread0{[&fc, &t0_state]() {\n    t0_state.store(1, std::memory_order_release);\n    const auto t0a2 =\n        fc.push(T0{2, 1.32}, [](const T0& t0) -> bool { return t0.a == 2; });\n    t0_state.store(2, std::memory_order_release);\n  }};\n\n  while (t0_state.load(std::memory_order_acquire) < 1) {\n  }\n  REQUIRE(t0_state.load(std::memory_order_acquire) == 1);\n\n  // Wait 1s to give other thread plenty of time to not be deadlocked.\n  std::this_thread::sleep_for(std::chrono::milliseconds(1000));\n  REQUIRE(t0_state.load(std::memory_order_acquire) == 1);\n\n  t0a1 = std::nullopt;\n  thread0.join();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.FifoCache\", \"[Parallel][Unit]\") {\n  test_basic();\n  test_non_copyable();\n  test_deadlock();\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_GlobalCache.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmainmodule Test_GlobalCache {\n  template <typename Metavariables>\n  mainchare [migratable] Test_GlobalCache {\n    entry Test_GlobalCache(CkArgMsg* msg);\n    entry void exit();\n  };\n\n  template <typename Metavariables>\n  array [1D] TestArrayChare {\n    entry TestArrayChare(\n        CProxy_Test_GlobalCache<Metavariables> & main_proxy,\n        Parallel::CProxy_GlobalCache<Metavariables> & global_cache_proxy);\n    entry void run_test_one();\n    entry void run_test_two();\n    entry void run_test_three();\n    entry void run_test_four();\n    entry void run_test_five();\n    entry void mutate_name();\n  };\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_GlobalCache.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Parallel/Test_GlobalCache.hpp\"\n\n#include <charm++.h>\n#include <cstddef>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmGroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Callback.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/ResourceInfo.hpp\"\n#include \"Parallel/Tags/ResourceInfo.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/System/Exit.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\n\nstruct name {\n  using type = std::string;\n};\n\nstruct age {\n  using type = int;\n};\n\nstruct height {\n  using type = double;\n};\n\nstruct weight {\n  using type = double;\n};\n\nstruct email {\n  using type = std::string;\n};\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\nclass Shape : public PUP::able {\n public:\n  Shape() = default;\n  virtual size_t number_of_sides() const = 0;\n  // clang-tidy: internal charm++ warnings\n  WRAPPED_PUPable_abstract(Shape);  // NOLINT\n};\n\nclass Triangle : public Shape {\n public:\n  Triangle() = default;\n  explicit Triangle(CkMigrateMessage* /*m*/) {}\n  size_t number_of_sides() const final { return 3; }\n  // clang-tidy: internal charm++ warnings\n  WRAPPED_PUPable_decl_base_template(Shape,  // NOLINT\n                                     Triangle);\n  void pup(PUP::er& p) override { Shape::pup(p); }\n};\n\nclass Square : public Shape {\n public:\n  Square() = default;\n  explicit Square(CkMigrateMessage* /*m*/) {}\n  size_t number_of_sides() const final { return 4; }\n  // clang-tidy: internal charm++ warnings\n  WRAPPED_PUPable_decl_base_template(Shape,  // NOLINT\n                                     Square);\n  void pup(PUP::er& p) override { Shape::pup(p); }\n};\n\nclass Animal : public PUP::able {\n public:\n  Animal() = default;\n  virtual size_t number_of_legs() const = 0;\n  virtual void set_number_of_legs(size_t) = 0;\n  // clang-tidy: internal charm++ warnings\n  WRAPPED_PUPable_abstract(Animal);  // NOLINT\n};\n\nclass Arthropod : public Animal {\n public:\n  Arthropod() = default;\n  explicit Arthropod(const size_t num_legs) : number_of_legs_(num_legs){};\n  explicit Arthropod(CkMigrateMessage* /*m*/) {}\n  size_t number_of_legs() const final { return number_of_legs_; }\n  void set_number_of_legs(const size_t num_legs) final {\n    number_of_legs_ = num_legs;\n  }\n  // clang-tidy: internal charm++ warnings\n  WRAPPED_PUPable_decl_base_template(Animal,  // NOLINT\n                                     Arthropod);\n\n  void pup(PUP::er& p) override {\n    Animal::pup(p);\n    p | number_of_legs_;\n  }\n\n private:\n  size_t number_of_legs_{0};\n};\n#pragma GCC diagnostic pop\n\nstruct shape_of_nametag_base {};\n\nstruct shape_of_nametag : shape_of_nametag_base {\n  using type = std::unique_ptr<Shape>;\n};\n\nstruct animal_base {};\n\nstruct animal : animal_base {\n  using type = std::unique_ptr<Animal>;\n};\n\ntemplate <typename T>\nstruct modify_value {\n  static void apply(const gsl::not_null<T*> value, const T& new_value) {\n    *value = new_value;\n  }\n};\n\nstruct modify_number_of_legs {\n  static void apply(const gsl::not_null<Animal*> animal_local,\n                    const size_t num_legs) {\n    animal_local->set_number_of_legs(num_legs);\n  }\n};\n\ntemplate <class Metavariables>\nstruct SingletonParallelComponent {\n  using chare_type = Parallel::Algorithms::Singleton;\n  using const_global_cache_tags = tmpl::list<age, height>;\n  using mutable_global_cache_tags = tmpl::list<weight, animal>;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n};\n\ntemplate <class Metavariables>\nstruct ArrayParallelComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  using const_global_cache_tags = tmpl::list<height, shape_of_nametag>;\n  using mutable_global_cache_tags = tmpl::list<email, weight>;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n};\n\ntemplate <class Metavariables>\nstruct GroupParallelComponent {\n  using chare_type = Parallel::Algorithms::Group;\n  using const_global_cache_tags = tmpl::list<>;\n  using mutable_global_cache_tags = tmpl::list<email, name>;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n};\n\ntemplate <class Metavariables>\nstruct NodegroupParallelComponent {\n  using chare_type = Parallel::Algorithms::Nodegroup;\n  using const_global_cache_tags = tmpl::list<height>;\n  using mutable_global_cache_tags = tmpl::list<animal>;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n};\n\nstruct TestMetavariables {\n  using const_global_cache_tags = tmpl::list<>;\n  using component_list =\n      tmpl::list<SingletonParallelComponent<TestMetavariables>,\n                 ArrayParallelComponent<TestMetavariables>,\n                 GroupParallelComponent<TestMetavariables>,\n                 NodegroupParallelComponent<TestMetavariables>>;\n};\n\n}  // namespace\n\n// Wraps a charm CkCallback.  UseCkCallbackAsCallback is in\n// Test_GlobalCache.cpp and not in Parallel/Callback.hpp because in\n// normal usage we should use SimpleActionCallback or\n// PerformAlgorithmCallback because they can be mocked.\nclass UseCkCallbackAsCallback : public Parallel::Callback {\n public:\n  WRAPPED_PUPable_decl(UseCkCallbackAsCallback);\n  UseCkCallbackAsCallback() = default;\n  explicit UseCkCallbackAsCallback(CkMigrateMessage* msg)\n      : Parallel::Callback(msg) {}\n  explicit UseCkCallbackAsCallback(const CkCallback& callback,\n                                   const size_t index)\n      : callback_(callback), index_(index) {}\n  using PUP::able::register_constructor;\n  void invoke() override { callback_.send(nullptr); }\n  void pup(PUP::er& p) override { p | callback_; }\n  // We shouldn't be pupping so registration doesn't matter\n  void register_with_charm() override {}\n  bool is_equal_to(const Parallel::Callback& rhs) const override {\n    const auto* downcast_ptr =\n        dynamic_cast<const UseCkCallbackAsCallback*>(&rhs);\n    if (downcast_ptr == nullptr) {\n      return false;\n    }\n    return index_ == downcast_ptr->index_;\n  }\n\n  std::string name() const override {\n    return \"UseCkCallbackAsCallback\" + std::to_string(index_);\n  }\n\n  std::unique_ptr<Callback> get_clone() override {\n    return std::make_unique<UseCkCallbackAsCallback>(callback_, index_);\n  }\n\n private:\n  CkCallback callback_;\n  size_t index_{};\n};\n\nsize_t calls_to_test_one = 0;  // NOLINT\n\ntemplate <typename Metavariables>\nvoid TestArrayChare<Metavariables>::mutate_name() {\n  auto& local_cache = *Parallel::local_branch(global_cache_proxy_);\n\n  // Turn Nobody into Somebody\n  Parallel::mutate<name, modify_value<std::string>>(local_cache,\n                                                    std::string(\"Somebody\"));\n}\n\ntemplate <typename Metavariables>\nvoid TestArrayChare<Metavariables>::run_test_one() {\n  // Test that the values are what we think they should be.\n  auto& local_cache = *Parallel::local_branch(global_cache_proxy_);\n  const std::string expected_name =\n      calls_to_test_one == 0 ? \"Nobody\" : \"Somebody\";\n  SPECTRE_PARALLEL_REQUIRE(expected_name == Parallel::get<name>(local_cache));\n  SPECTRE_PARALLEL_REQUIRE(178 == Parallel::get<age>(local_cache));\n  SPECTRE_PARALLEL_REQUIRE(2.2 == Parallel::get<height>(local_cache));\n  SPECTRE_PARALLEL_REQUIRE(\n      4 == Parallel::get<shape_of_nametag>(local_cache).number_of_sides());\n  SPECTRE_PARALLEL_REQUIRE(\n      4 == Parallel::get<shape_of_nametag_base>(local_cache).number_of_sides());\n  SPECTRE_PARALLEL_REQUIRE(160.0 == Parallel::get<weight>(local_cache));\n  SPECTRE_PARALLEL_REQUIRE(\"joe@somewhere.com\" ==\n                           Parallel::get<email>(local_cache));\n  SPECTRE_PARALLEL_REQUIRE(6 ==\n                           Parallel::get<animal>(local_cache).number_of_legs());\n  SPECTRE_PARALLEL_REQUIRE(\n      6 == Parallel::get<animal_base>(local_cache).number_of_legs());\n\n  const auto local_cache_from_proxy =\n      Parallel::local_branch(local_cache.get_this_proxy());\n  SPECTRE_PARALLEL_REQUIRE(local_cache_from_proxy ==\n                           Parallel::local_branch(global_cache_proxy_));\n\n  // test the serialization of the caches\n  Parallel::GlobalCache<TestMetavariables>\n      serialized_and_deserialized_global_cache{};\n  serialize_and_deserialize(\n      make_not_null(&serialized_and_deserialized_global_cache), local_cache);\n  SPECTRE_PARALLEL_REQUIRE(\n      expected_name ==\n      Parallel::get<name>(serialized_and_deserialized_global_cache));\n  SPECTRE_PARALLEL_REQUIRE(\n      178 == Parallel::get<age>(serialized_and_deserialized_global_cache));\n  SPECTRE_PARALLEL_REQUIRE(\n      2.2 == Parallel::get<height>(serialized_and_deserialized_global_cache));\n  SPECTRE_PARALLEL_REQUIRE(4 == Parallel::get<shape_of_nametag>(\n                                    serialized_and_deserialized_global_cache)\n                                    .number_of_sides());\n\n  // Only register callbacks on the first call to `run_test_one`\n  if (calls_to_test_one == 0) {\n    auto callback =\n        CkCallback(CkIndex_TestArrayChare<Metavariables>::run_test_one(),\n                   this->thisProxy[this->thisIndex]);\n    const auto array_component_id =\n        Parallel::make_array_component_id<TestArrayChare<Metavariables>>(\n            static_cast<int>(this->thisIndex));\n\n    const auto register_callback = [&](const size_t index) {\n      Parallel::mutable_cache_item_is_ready<name>(\n          local_cache, array_component_id,\n          [&callback, &index](const std::string& name_l)\n              -> std::unique_ptr<Parallel::Callback> {\n            return name_l == \"Somebody\"\n                       ? std::unique_ptr<Parallel::Callback>{}\n                       : std::unique_ptr<Parallel::Callback>(\n                             new UseCkCallbackAsCallback(callback, index));\n          });\n    };\n\n    // Register first callback for this function\n    register_callback(0);\n    // Register second callback for this function with different index to test\n    // that we can have two callbacks on the same element\n    register_callback(1);\n    // Try and register the first callback again. This shouldn't be registered\n    // and we should still only have two callbacks\n    register_callback(0);\n\n    // Do the mutate somehwere else so we can return here and have\n    // `run_test_one` be called again\n    this->thisProxy[0].mutate_name();\n\n    calls_to_test_one++;\n    return;\n  } else if (calls_to_test_one == 1) {\n    // Make sure we haven't mutated the weight yet, because that would move on\n    // to `run_test_two` then\n    SPECTRE_PARALLEL_REQUIRE(Parallel::get<weight>(local_cache) == 160.0);\n\n    calls_to_test_one++;\n    return;\n  } else {\n    // We have now called both registered callbacks\n    SPECTRE_PARALLEL_REQUIRE(calls_to_test_one == 2);\n\n    // The value will be checked in `run_test_two`\n    calls_to_test_one++;\n  }\n\n  // Mutate the weight to 150.\n  Parallel::mutate<weight, modify_value<double>>(local_cache, 150.0);\n}\n\ntemplate <typename Metavariables>\nvoid TestArrayChare<Metavariables>::run_test_two() {\n  // Move on when the weight is 150 (mutated in run_test_one)\n  auto callback =\n      CkCallback(CkIndex_TestArrayChare<Metavariables>::run_test_two(),\n                 this->thisProxy[this->thisIndex]);\n  const auto array_component_id =\n      Parallel::make_array_component_id<TestArrayChare<Metavariables>>(\n          static_cast<int>(this->thisIndex));\n  if (Parallel::mutable_cache_item_is_ready<weight>(\n          *Parallel::local_branch(global_cache_proxy_), array_component_id,\n          [&callback](\n              const double& weight_l) -> std::unique_ptr<Parallel::Callback> {\n            return weight_l == 150\n                       ? std::unique_ptr<Parallel::Callback>{}\n                       : std::unique_ptr<Parallel::Callback>(\n                             new UseCkCallbackAsCallback(callback, 0));\n          })) {\n    // One original call, and then two callbacks\n    SPECTRE_PARALLEL_REQUIRE(calls_to_test_one == 3);\n\n    auto& local_cache = *Parallel::local_branch(global_cache_proxy_);\n    SPECTRE_PARALLEL_REQUIRE(150 == Parallel::get<weight>(local_cache));\n\n    // Now the weight is 150, so mutate the email.\n    Parallel::mutate<email, modify_value<std::string>>(\n        local_cache, std::string(\"albert@einstein.de\"));\n    // ... and make the arthropod into a lobster.\n    Parallel::mutate<animal, modify_number_of_legs>(local_cache, 10_st);\n  }\n}\n\ntemplate <typename Metavariables>\nvoid TestArrayChare<Metavariables>::run_test_three() {\n  // Move on when the email is Albert's (mutated in run_test_two)\n  auto callback =\n      CkCallback(CkIndex_TestArrayChare<Metavariables>::run_test_three(),\n                 this->thisProxy[this->thisIndex]);\n  const auto array_component_id =\n      Parallel::make_array_component_id<TestArrayChare<Metavariables>>(\n          static_cast<int>(this->thisIndex));\n  if (Parallel::mutable_cache_item_is_ready<email>(\n          *Parallel::local_branch(global_cache_proxy_), array_component_id,\n          [&callback](const std::string& email_l)\n              -> std::unique_ptr<Parallel::Callback> {\n            return email_l == \"albert@einstein.de\"\n                       ? std::unique_ptr<Parallel::Callback>{}\n                       : std::unique_ptr<Parallel::Callback>(\n                             new UseCkCallbackAsCallback(callback, 0));\n          })) {\n    auto& local_cache = *Parallel::local_branch(global_cache_proxy_);\n    SPECTRE_PARALLEL_REQUIRE(\"albert@einstein.de\" ==\n                             Parallel::get<email>(local_cache));\n\n    // Now make the arthropod into a spider.\n    Parallel::mutate<animal, modify_number_of_legs>(local_cache, 8_st);\n  }\n}\n\ntemplate <typename Metavariables>\nvoid TestArrayChare<Metavariables>::run_test_four() {\n  // Move on when the animal has 8 legs (mutated in run_test_three)\n  auto callback =\n      CkCallback(CkIndex_TestArrayChare<Metavariables>::run_test_four(),\n                 this->thisProxy[this->thisIndex]);\n  const auto array_component_id =\n      Parallel::make_array_component_id<TestArrayChare<Metavariables>>(\n          static_cast<int>(this->thisIndex));\n  if (Parallel::mutable_cache_item_is_ready<animal>(\n          *Parallel::local_branch(global_cache_proxy_), array_component_id,\n          [&callback](\n              const Animal& animal_l) -> std::unique_ptr<Parallel::Callback> {\n            return animal_l.number_of_legs() == 8\n                       ? std::unique_ptr<Parallel::Callback>{}\n                       : std::unique_ptr<Parallel::Callback>(\n                             new UseCkCallbackAsCallback(callback, 0));\n          })) {\n    auto& local_cache = *Parallel::local_branch(global_cache_proxy_);\n    SPECTRE_PARALLEL_REQUIRE(\n        8 == Parallel::get<animal>(local_cache).number_of_legs());\n\n    // Make the arthropod into a Scutigera coleoptrata.\n    Parallel::mutate<animal_base, modify_number_of_legs>(local_cache, 30_st);\n  }\n}\n\ntemplate <typename Metavariables>\nvoid TestArrayChare<Metavariables>::run_test_five() {\n  // Move on when the animal has 30 legs (mutated in run_test_four)\n  auto callback =\n      CkCallback(CkIndex_TestArrayChare<Metavariables>::run_test_five(),\n                 this->thisProxy[this->thisIndex]);\n  const auto array_component_id =\n      Parallel::make_array_component_id<TestArrayChare<Metavariables>>(\n          static_cast<int>(this->thisIndex));\n  if (Parallel::mutable_cache_item_is_ready<animal_base>(\n          *Parallel::local_branch(global_cache_proxy_), array_component_id,\n          [&callback](\n              const Animal& animal_l) -> std::unique_ptr<Parallel::Callback> {\n            return animal_l.number_of_legs() == 30\n                       ? std::unique_ptr<Parallel::Callback>{}\n                       : std::unique_ptr<Parallel::Callback>(\n                             new UseCkCallbackAsCallback(callback, 0));\n          })) {\n    auto& local_cache = *Parallel::local_branch(global_cache_proxy_);\n    SPECTRE_PARALLEL_REQUIRE(\n        30 == Parallel::get<animal_base>(local_cache).number_of_legs());\n    main_proxy_.exit();\n  }\n}\n\n// run_single_core_test constructs and tests GlobalCache without\n// using proxies or parallelism.  run_single_core_test tests constructors\n// and member functions that are used in the action testing framework.\ntemplate <typename Metavariables>\nvoid Test_GlobalCache<Metavariables>::run_single_core_test() {\n  using const_tag_list =\n      typename Parallel::get_const_global_cache_tags<TestMetavariables>;\n  static_assert(\n      std::is_same_v<const_tag_list, tmpl::list<age, height, shape_of_nametag>>,\n      \"Wrong const_tag_list in GlobalCache test\");\n\n  using mutable_tag_list =\n      typename Parallel::get_mutable_global_cache_tags<TestMetavariables>;\n  static_assert(\n      std::is_same_v<mutable_tag_list, tmpl::list<weight, animal, email, name>>,\n      \"Wrong mutable_tag_list in GlobalCache test\");\n\n  tuples::tagged_tuple_from_typelist<const_tag_list> const_data_to_be_cached(\n      178, 2.2, std::make_unique<Square>());\n  tuples::tagged_tuple_from_typelist<mutable_tag_list>\n      mutable_data_to_be_cached(160, std::make_unique<Arthropod>(6),\n                                \"joe@somewhere.com\", \"Nobody\");\n\n  Parallel::GlobalCache<TestMetavariables> cache(\n      std::move(const_data_to_be_cached), std::move(mutable_data_to_be_cached));\n  SPECTRE_PARALLEL_REQUIRE(\"Nobody\" == Parallel::get<name>(cache));\n  SPECTRE_PARALLEL_REQUIRE(178 == Parallel::get<age>(cache));\n  SPECTRE_PARALLEL_REQUIRE(2.2 == Parallel::get<height>(cache));\n  SPECTRE_PARALLEL_REQUIRE(\n      4 == Parallel::get<shape_of_nametag>(cache).number_of_sides());\n  SPECTRE_PARALLEL_REQUIRE(\n      4 == Parallel::get<shape_of_nametag_base>(cache).number_of_sides());\n  SPECTRE_PARALLEL_REQUIRE(160 == Parallel::get<weight>(cache));\n  SPECTRE_PARALLEL_REQUIRE(\"joe@somewhere.com\" == Parallel::get<email>(cache));\n  SPECTRE_PARALLEL_REQUIRE(6 == Parallel::get<animal>(cache).number_of_legs());\n  SPECTRE_PARALLEL_REQUIRE(6 ==\n                           Parallel::get<animal_base>(cache).number_of_legs());\n\n  // Check that we can modify the non-const items.\n  Parallel::mutate<weight, modify_value<double>>(cache, 150.0);\n  Parallel::mutate<email, modify_value<std::string>>(\n      cache, std::string(\"nobody@nowhere.com\"));\n  SPECTRE_PARALLEL_REQUIRE(150 == Parallel::get<weight>(cache));\n  SPECTRE_PARALLEL_REQUIRE(\"nobody@nowhere.com\" == Parallel::get<email>(cache));\n  Parallel::mutate<email, modify_value<std::string>>(\n      cache, std::string(\"isaac@newton.com\"));\n  SPECTRE_PARALLEL_REQUIRE(\"isaac@newton.com\" == Parallel::get<email>(cache));\n  // Make the arthropod into a spider.\n  Parallel::mutate<animal, modify_number_of_legs>(cache, 8_st);\n  SPECTRE_PARALLEL_REQUIRE(8 == Parallel::get<animal>(cache).number_of_legs());\n  SPECTRE_PARALLEL_REQUIRE(8 ==\n                           Parallel::get<animal_base>(cache).number_of_legs());\n  // Make the arthropod into a Scutigera coleoptrata.\n  Parallel::mutate<animal_base, modify_number_of_legs>(cache, 30_st);\n  SPECTRE_PARALLEL_REQUIRE(30 == Parallel::get<animal>(cache).number_of_legs());\n  SPECTRE_PARALLEL_REQUIRE(30 ==\n                           Parallel::get<animal_base>(cache).number_of_legs());\n}\n\ntemplate <typename Metavariables>\nTest_GlobalCache<Metavariables>::Test_GlobalCache(CkArgMsg*\n                                                  /*msg*/) {\n  // Register the pup functions.\n  register_classes_with_charm<Triangle, Square, Arthropod,\n                              UseCkCallbackAsCallback>();\n\n  // Call the single core test before doing anything else.\n  run_single_core_test();\n\n  // Initialize number of elements\n  num_elements_ = 4;\n\n  // Create GlobalCache proxies.\n  using mutable_tag_list =\n      typename Parallel::get_mutable_global_cache_tags<TestMetavariables>;\n  static_assert(\n      std::is_same_v<mutable_tag_list, tmpl::list<weight, animal, email, name>>,\n      \"Wrong mutable_tag_list in GlobalCache test\");\n  static_assert(\n      Parallel::is_in_mutable_global_cache<TestMetavariables, animal>);\n  static_assert(Parallel::is_in_global_cache<TestMetavariables, animal>);\n  static_assert(\n      not Parallel::is_in_const_global_cache<TestMetavariables, animal>);\n  static_assert(\n      Parallel::is_in_mutable_global_cache<TestMetavariables, animal_base>);\n  static_assert(Parallel::is_in_global_cache<TestMetavariables, animal_base>);\n  static_assert(\n      not Parallel::is_in_const_global_cache<TestMetavariables, animal_base>);\n\n  using const_tag_list =\n      typename Parallel::get_const_global_cache_tags<TestMetavariables>;\n  static_assert(\n      std::is_same_v<const_tag_list, tmpl::list<age, height, shape_of_nametag>>,\n      \"Wrong const_tag_list in GlobalCache test\");\n  static_assert(\n      Parallel::is_in_const_global_cache<TestMetavariables, shape_of_nametag>);\n  static_assert(\n      Parallel::is_in_global_cache<TestMetavariables, shape_of_nametag>);\n  static_assert(not Parallel::is_in_mutable_global_cache<TestMetavariables,\n                                                         shape_of_nametag>);\n  static_assert(Parallel::is_in_const_global_cache<TestMetavariables,\n                                                   shape_of_nametag_base>);\n  static_assert(\n      Parallel::is_in_global_cache<TestMetavariables, shape_of_nametag_base>);\n  static_assert(\n      not Parallel::is_in_mutable_global_cache<TestMetavariables,\n                                               shape_of_nametag_base>);\n\n  // Arthropod begins as an insect.\n  tuples::tagged_tuple_from_typelist<mutable_tag_list>\n      mutable_data_to_be_cached(160, std::make_unique<Arthropod>(6),\n                                \"joe@somewhere.com\", \"Nobody\");\n  tuples::tagged_tuple_from_typelist<const_tag_list> const_data_to_be_cached(\n      178, 2.2, std::make_unique<Square>());\n  global_cache_proxy_ = Parallel::CProxy_GlobalCache<TestMetavariables>::ckNew(\n      std::move(const_data_to_be_cached), std::move(mutable_data_to_be_cached),\n      std::nullopt);\n\n  Parallel::ResourceInfo<TestMetavariables> resource_info{false};\n\n  const auto local_cache = Parallel::local_branch(global_cache_proxy_);\n\n  resource_info.build_singleton_map(*local_cache);\n\n  local_cache->set_resource_info(resource_info);\n\n  const auto& cache_resource_info = local_cache->get_resource_info();\n\n  SPECTRE_PARALLEL_REQUIRE(cache_resource_info == resource_info);\n\n  CkEntryOptions global_cache_dependency;\n  global_cache_dependency.setGroupDepID(global_cache_proxy_.ckGetGroupID());\n\n  // Create array\n  CProxy_TestArrayChare<Metavariables> array_proxy =\n      CProxy_TestArrayChare<Metavariables>::ckNew(\n          this->thisProxy, global_cache_proxy_, num_elements_);\n\n  array_proxy[3].run_test_five();\n  array_proxy[2].run_test_four();\n  array_proxy[1].run_test_three();\n  array_proxy[0].run_test_two();\n  array_proxy[3].run_test_one();\n}\n\ntemplate <typename Metavariables>\nvoid Test_GlobalCache<Metavariables>::exit() {\n  sys::exit();\n}\n\n// --------- registration stuff below -------\n\n// clang-format off\nPUPable_def(UseCkCallbackAsCallback)\n// clang-tidy: possibly throwing constructor static storage\n// clang-tidy: false positive: redundant declaration\nPUP::able::PUP_ID Triangle::my_PUP_ID = 0;  // NOLINT\nPUP::able::PUP_ID Square::my_PUP_ID = 0;        // NOLINT\nPUP::able::PUP_ID Arthropod::my_PUP_ID = 0;     // NOLINT\n// clang-format on\n\nextern \"C\" void CkRegisterMainModule() {\n  using charmxx_main_component = Test_GlobalCache<TestMetavariables>;\n  (void)charmxx_main_component{\n      Parallel::charmxx::MainChareRegistrationConstructor{}};\n  Parallel::charmxx::register_parallel_components();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_GlobalCache.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include <charm++.h>\n#include <set>\n\n#include \"Parallel/CharmRegistration.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n\n#include \"Parallel/Test_GlobalCache.decl.h\"\n\ntemplate <typename Metavariables>\nclass Test_GlobalCache : public CBase_Test_GlobalCache<Metavariables> {\n public:\n  /// \\cond HIDDEN_SYMBOLS\n  /// The constructor used to register the class\n  explicit Test_GlobalCache(\n      const Parallel::charmxx::\n          MainChareRegistrationConstructor& /*used 4 registration*/) {}\n  ~Test_GlobalCache() override {\n    (void)Parallel::charmxx::RegisterChare<\n        Test_GlobalCache<Metavariables>,\n        CkIndex_Test_GlobalCache<Metavariables>>::registrar;\n  }\n  Test_GlobalCache(const Test_GlobalCache&) = default;\n  Test_GlobalCache& operator=(const Test_GlobalCache&) = default;\n  Test_GlobalCache(Test_GlobalCache&&) = default;\n  Test_GlobalCache& operator=(Test_GlobalCache&&) = default;\n  /// \\endcond\n\n  explicit Test_GlobalCache(CkArgMsg* msg);\n  explicit Test_GlobalCache(CkMigrateMessage* /*msg*/) {}\n\n  void exit();\n\n  void run_single_core_test();\n\n private:\n  Parallel::CProxy_GlobalCache<Metavariables> global_cache_proxy_{};\n  size_t num_elements_{4};\n};\n\ntemplate <typename Metavariables>\nclass TestArrayChare : public CBase_TestArrayChare<Metavariables> {\n public:\n  explicit TestArrayChare(\n      CProxy_Test_GlobalCache<Metavariables> main_proxy,\n      Parallel::CProxy_GlobalCache<Metavariables> global_cache_proxy)\n      : main_proxy_(std::move(main_proxy)),\n        global_cache_proxy_(std::move(global_cache_proxy)) {}\n  explicit TestArrayChare(CkMigrateMessage* /*msg*/) {}\n  ~TestArrayChare() override {\n    (void)Parallel::charmxx::RegisterChare<\n        TestArrayChare<Metavariables>,\n        CkIndex_TestArrayChare<Metavariables>>::registrar;\n  }\n  TestArrayChare(const TestArrayChare&) = default;\n  TestArrayChare& operator=(const TestArrayChare&) = default;\n  TestArrayChare(TestArrayChare&&) = default;\n  TestArrayChare& operator=(TestArrayChare&&) = default;\n\n  void run_test_one();\n  void run_test_two();\n  void run_test_three();\n  void run_test_four();\n  void run_test_five();\n  void mutate_name();\n\n private:\n  CProxy_Test_GlobalCache<Metavariables> main_proxy_;\n  Parallel::CProxy_GlobalCache<Metavariables> global_cache_proxy_;\n};\n\n#define CK_TEMPLATES_ONLY\n#include \"Parallel/Test_GlobalCache.def.h\"\n#undef CK_TEMPLATES_ONLY\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_GlobalCacheDataBox.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\nnamespace Tags {\nstruct IntegerList : db::SimpleTag {\n  using type = std::array<int, 3>;\n};\n\nstruct UniquePtrIntegerList : db::SimpleTag {\n  using type = std::unique_ptr<std::array<int, 3>>;\n};\n}  // namespace Tags\n\nnamespace {\nstruct Metavars {\n  using const_global_cache_tags =\n      tmpl::list<Tags::IntegerList, Tags::UniquePtrIntegerList>;\n  using component_list = tmpl::list<>;\n};\n\nstruct EmptyMetavars {\n  using component_list = tmpl::list<>;\n};\n\nvoid test_mem_monitor_entry_method_error() {\n  CHECK_THROWS_WITH(\n      ([]() {\n        GlobalCache<EmptyMetavars> empty_cache{tuples::TaggedTuple<>{}};\n\n        empty_cache.compute_size_for_memory_monitor(0.0);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"GlobalCache::compute_size_for_memory_monitor can only be called if \"\n          \"the MemoryMonitor is in the component list in the \"\n          \"metavariables.\\n\"));\n}\n\nvoid test_resource_info_error() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        GlobalCache<EmptyMetavars> empty_cache{tuples::TaggedTuple<>{}};\n\n        empty_cache.set_resource_info(ResourceInfo<EmptyMetavars>{});\n        empty_cache.set_resource_info(ResourceInfo<EmptyMetavars>{});\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Can only set the resource info once\"));\n#endif\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.GlobalCacheDataBox\", \"[Unit][Parallel]\") {\n  test_mem_monitor_entry_method_error();\n  test_resource_info_error();\n\n  tuples::TaggedTuple<Tags::IntegerList, Tags::UniquePtrIntegerList> tuple{};\n  tuples::get<Tags::IntegerList>(tuple) = std::array<int, 3>{{-1, 3, 7}};\n  tuples::get<Tags::UniquePtrIntegerList>(tuple) =\n      std::make_unique<std::array<int, 3>>(std::array<int, 3>{{1, 5, -8}});\n  GlobalCache<Metavars> cache{std::move(tuple)};\n  Parallel::ResourceInfo<Metavars> resource_info{false};\n  resource_info.build_singleton_map(cache);\n  cache.set_resource_info(resource_info);\n  auto box = db::create<\n      db::AddSimpleTags<Tags::GlobalCache<Metavars>>,\n      db::AddComputeTags<\n          Tags::FromGlobalCache<Tags::IntegerList, Metavars>,\n          Tags::FromGlobalCache<Tags::UniquePtrIntegerList, Metavars>,\n          Tags::ResourceInfoReference<Metavars>>>(&cache);\n  CHECK(db::get<Tags::GlobalCache<Metavars>>(box) == &cache);\n  CHECK(std::array<int, 3>{{-1, 3, 7}} == db::get<Tags::IntegerList>(box));\n  CHECK(std::array<int, 3>{{1, 5, -8}} ==\n        db::get<Tags::UniquePtrIntegerList>(box));\n  CHECK(std::array<int, 3>{{1, 5, -8}} ==\n        db::get<Tags::UniquePtrIntegerList>(box));\n  CHECK(cache.get_resource_info() == resource_info);\n  CHECK(cache.get_resource_info() ==\n        db::get<Tags::ResourceInfo<Metavars>>(box));\n  CHECK(&Parallel::get<Tags::IntegerList>(cache) ==\n        &db::get<Tags::IntegerList>(box));\n  CHECK(&Parallel::get<Tags::UniquePtrIntegerList>(cache) ==\n        &db::get<Tags::UniquePtrIntegerList>(box));\n  CHECK(&Parallel::get<Tags::UniquePtrIntegerList>(cache) ==\n        &db::get<Tags::UniquePtrIntegerList>(box));\n  CHECK(&cache.get_resource_info() ==\n        &db::get<Tags::ResourceInfo<Metavars>>(box));\n\n  tuples::TaggedTuple<Tags::IntegerList, Tags::UniquePtrIntegerList> tuple2{};\n  tuples::get<Tags::IntegerList>(tuple2) = std::array<int, 3>{{10, -3, 700}};\n  tuples::get<Tags::UniquePtrIntegerList>(tuple2) =\n      std::make_unique<std::array<int, 3>>(std::array<int, 3>{{100, -7, -300}});\n  GlobalCache<Metavars> cache2{std::move(tuple2)};\n  db::mutate<Tags::GlobalCache<Metavars>>(\n      [&cache2](const gsl::not_null<Parallel::GlobalCache<Metavars>**> t) {\n        *t = std::addressof(cache2);\n      },\n      make_not_null(&box));\n\n  CHECK(db::get<Tags::GlobalCache<Metavars>>(box) == &cache2);\n  CHECK(std::array<int, 3>{{10, -3, 700}} == db::get<Tags::IntegerList>(box));\n  CHECK(std::array<int, 3>{{100, -7, -300}} ==\n        db::get<Tags::UniquePtrIntegerList>(box));\n  CHECK(std::array<int, 3>{{100, -7, -300}} ==\n        db::get<Tags::UniquePtrIntegerList>(box));\n  CHECK(&Parallel::get<Tags::IntegerList>(cache2) ==\n        &db::get<Tags::IntegerList>(box));\n  CHECK(&Parallel::get<Tags::UniquePtrIntegerList>(cache2) ==\n        &db::get<Tags::UniquePtrIntegerList>(box));\n  CHECK(&Parallel::get<Tags::UniquePtrIntegerList>(cache2) ==\n        &db::get<Tags::UniquePtrIntegerList>(box));\n\n  TestHelpers::db::test_simple_tag<Tags::GlobalCache<Metavars>>(\"GlobalCache\");\n  TestHelpers::db::test_reference_tag<\n      Tags::FromGlobalCache<Tags::IntegerList, Metavars>>(\"IntegerList\");\n  TestHelpers::db::test_reference_tag<\n      Tags::FromGlobalCache<Tags::UniquePtrIntegerList, Metavars>>(\n      \"UniquePtrIntegerList\");\n  TestHelpers::db::test_reference_tag<Tags::ResourceInfoReference<Metavars>>(\n      \"ResourceInfo\");\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_InboxInserters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct MapCounter : db::SimpleTag {\n  using type = size_t;\n};\n\nstruct MemberInsertCounter : db::SimpleTag {\n  using type = size_t;\n};\n\nstruct ValueCounter : db::SimpleTag {\n  using type = size_t;\n};\n\nstruct PushbackCounter : db::SimpleTag {\n  using type = size_t;\n};\n\n// [map tag]\nstruct MapTag : public Parallel::InboxInserters::Map<MapTag> {\n  using temporal_id = size_t;\n  using type = std::unordered_map<temporal_id, std::unordered_map<int, int>>;\n};\n// [map tag]\n\n// [member insert tag]\nstruct MemberInsertTag\n    : public Parallel::InboxInserters::MemberInsert<MemberInsertTag> {\n  using temporal_id = size_t;\n  using type = std::unordered_map<temporal_id, std::unordered_multiset<int>>;\n};\n// [member insert tag]\n\n// [value tag]\nstruct ValueTag : public Parallel::InboxInserters::Value<ValueTag> {\n  using temporal_id = size_t;\n  using type = std::unordered_map<temporal_id, int>;\n};\n// [value tag]\n\n// [pushback tag]\nstruct PushbackTag : public Parallel::InboxInserters::Pushback<PushbackTag> {\n  using temporal_id = size_t;\n  using type = std::unordered_map<temporal_id, std::vector<int>>;\n};\n// [pushback tag]\n}  // namespace Tags\n\nnamespace Actions {\nstruct SendMap {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Parallel::receive_data<Tags::MapTag>(\n        Parallel::get_parallel_component<ParallelComponent>(cache)[0], 1_st,\n        std::make_pair(10, 23));\n    db::mutate<Tags::MapCounter>(\n\n        [](const gsl::not_null<size_t*> map_counter) { (*map_counter)++; },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct ReceiveMap {\n  using inbox_tags = tmpl::list<Tags::MapTag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (get<Tags::MapTag>(inboxes).at(1_st).size() != 1) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    db::mutate<Tags::MapCounter>(\n\n        [](const gsl::not_null<size_t*> map_counter) { (*map_counter)++; },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct SendMemberInsert {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Parallel::receive_data<Tags::MemberInsertTag>(\n        Parallel::get_parallel_component<ParallelComponent>(cache)[0], 1_st,\n        23);\n    db::mutate<Tags::MemberInsertCounter>(\n\n        [](const gsl::not_null<size_t*> member_insert_counter) {\n          (*member_insert_counter)++;\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct ReceiveMemberInsert {\n  using inbox_tags = tmpl::list<Tags::MemberInsertTag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (get<Tags::MemberInsertTag>(inboxes).at(1_st).size() != 1) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    db::mutate<Tags::MemberInsertCounter>(\n\n        [](const gsl::not_null<size_t*> member_insert_counter) {\n          (*member_insert_counter)++;\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct SendValue {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Parallel::receive_data<Tags::ValueTag>(\n        Parallel::get_parallel_component<ParallelComponent>(cache)[0], 1_st,\n        23);\n    db::mutate<Tags::ValueCounter>(\n\n        [](const gsl::not_null<size_t*> value_counter) { (*value_counter)++; },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct ReceiveValue {\n  using inbox_tags = tmpl::list<Tags::ValueTag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (get<Tags::ValueTag>(inboxes).count(1_st) != 1) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    db::mutate<Tags::ValueCounter>(\n\n        [](const gsl::not_null<size_t*> value_counter) { (*value_counter)++; },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct SendPushback {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Parallel::receive_data<Tags::PushbackTag>(\n        Parallel::get_parallel_component<ParallelComponent>(cache)[0], 1_st,\n        23);\n    db::mutate<Tags::PushbackCounter>(\n        [](const gsl::not_null<size_t*> pushback_counter) {\n          (*pushback_counter)++;\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct ReceivePushback {\n  using inbox_tags = tmpl::list<Tags::PushbackTag>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& inboxes,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    if (get<Tags::PushbackTag>(inboxes).at(1_st).size() != 1) {\n      return {Parallel::AlgorithmExecution::Retry, std::nullopt};\n    }\n\n    db::mutate<Tags::PushbackCounter>(\n        [](const gsl::not_null<size_t*> pushback_counter) {\n          (*pushback_counter)++;\n        },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n}  // namespace Actions\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              db::AddSimpleTags<Tags::MapCounter, Tags::MemberInsertCounter,\n                                Tags::ValueCounter, Tags::PushbackCounter>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::SendMap, Actions::ReceiveMap,\n                     Actions::SendMemberInsert, Actions::ReceiveMemberInsert,\n                     Actions::SendValue, Actions::ReceiveValue,\n                     Actions::SendPushback, Actions::ReceivePushback>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.InboxInserters\", \"[Parallel][Unit]\") {\n  using metavars = Metavariables;\n  using component = Component<metavars>;\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}};\n  ActionTesting::emplace_component_and_initialize<component>(\n      &runner, 0, {0_st, 0_st, 0_st, 0_st});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Check map insertion\n  CHECK(ActionTesting::get_next_action_index<component>(runner, 0) == 0);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::MapCounter>(runner,\n                                                                    0) == 0);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_inbox_tag<component, Tags::MapTag>(runner, 0)\n            .at(1)\n            .size() == 1);\n  CHECK(\n      ActionTesting::get_inbox_tag<component, Tags::MapTag>(runner, 0).at(1).at(\n          10) == 23);\n  CHECK(ActionTesting::get_next_action_index<component>(runner, 0) == 1);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::MapCounter>(runner,\n                                                                    0) == 1);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::MapCounter>(runner,\n                                                                    0) == 2);\n  ActionTesting::get_inbox_tag<component, Tags::MapTag>(make_not_null(&runner),\n                                                        0)\n      .clear();\n\n  // Check member insertion\n  CHECK(ActionTesting::get_next_action_index<component>(runner, 0) == 2);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::MemberInsertCounter>(\n            runner, 0) == 0);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(\n      ActionTesting::get_inbox_tag<component, Tags::MemberInsertTag>(runner, 0)\n          .at(1)\n          .size() == 1);\n  CHECK(\n      ActionTesting::get_inbox_tag<component, Tags::MemberInsertTag>(runner, 0)\n          .at(1)\n          .count(23) == 1);\n  CHECK(ActionTesting::get_next_action_index<component>(runner, 0) == 3);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::MemberInsertCounter>(\n            runner, 0) == 1);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::MemberInsertCounter>(\n            runner, 0) == 2);\n  ActionTesting::get_inbox_tag<component, Tags::MemberInsertTag>(\n      make_not_null(&runner), 0)\n      .clear();\n\n  // Check value insertion\n  CHECK(ActionTesting::get_next_action_index<component>(runner, 0) == 4);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::ValueCounter>(runner,\n                                                                      0) == 0);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_inbox_tag<component, Tags::ValueTag>(runner, 0).at(\n            1) == 23);\n  CHECK(ActionTesting::get_next_action_index<component>(runner, 0) == 5);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::ValueCounter>(runner,\n                                                                      0) == 1);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::ValueCounter>(runner,\n                                                                      0) == 2);\n  ActionTesting::get_inbox_tag<component, Tags::ValueTag>(\n      make_not_null(&runner), 0)\n      .clear();\n\n  // Check pushback insertion\n  CHECK(ActionTesting::get_next_action_index<component>(runner, 0) == 6);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::PushbackCounter>(\n            runner, 0) == 0);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_inbox_tag<component, Tags::PushbackTag>(runner, 0)\n            .at(1)\n            .size() == 1);\n  CHECK(\n      ActionTesting::get_inbox_tag<component, Tags::PushbackTag>(runner, 0).at(\n          1)[0] == 23);\n  CHECK(ActionTesting::get_next_action_index<component>(runner, 0) == 7);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::PushbackCounter>(\n            runner, 0) == 1);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_databox_tag<component, Tags::PushbackCounter>(\n            runner, 0) == 2);\n  ActionTesting::get_inbox_tag<component, Tags::PushbackTag>(\n      make_not_null(&runner), 0)\n      .clear();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_InitializationTag.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagTraits.hpp\"\n#include \"Parallel/InitializationTag.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct NormalTag : db::SimpleTag {\n  using type = int;\n};\n\nstruct InitTag1 : db::SimpleTag {\n  static constexpr bool pass_metavariables = false;\n  using option_tags = tmpl::list<>;\n};\n\nstruct InitTag2 : db::SimpleTag {\n  static constexpr bool pass_metavariables = true;\n  template <typename Metavariables>\n  using option_tags =\n      tmpl::conditional_t<Metavariables::something, tmpl::list<>, tmpl::list<>>;\n};\n\nstruct BadInitTag1 : db::SimpleTag {\n  static constexpr bool pass_metavariables = false;\n};\n\nstruct BadInitTag2 : db::SimpleTag {\n  using option_tags = tmpl::list<>;\n};\n\nstruct BadInitTag3 : db::SimpleTag {\n  static constexpr bool pass_metavariables = true;\n  using option_tags = tmpl::list<>;\n};\n\nstruct BadInitTag4 : db::SimpleTag {\n  static constexpr bool pass_metavariables = false;\n  template <typename Metavariables>\n  using option_tags =\n      tmpl::conditional_t<Metavariables::something, tmpl::list<>, tmpl::list<>>;\n};\n\nstatic_assert(not Parallel::initialization_tag<NormalTag>);\nstatic_assert(Parallel::initialization_tag<InitTag1>);\nstatic_assert(Parallel::initialization_tag<InitTag2>);\n\nstatic_assert(not Parallel::templated_initialization_tag<NormalTag>);\nstatic_assert(not Parallel::templated_initialization_tag<InitTag1>);\nstatic_assert(Parallel::templated_initialization_tag<InitTag2>);\n\nstatic_assert(not Parallel::untemplated_initialization_tag<NormalTag>);\nstatic_assert(Parallel::untemplated_initialization_tag<InitTag1>);\nstatic_assert(not Parallel::untemplated_initialization_tag<InitTag2>);\n\nstatic_assert(not Parallel::initialization_tag<BadInitTag1>);\nstatic_assert(not Parallel::initialization_tag<BadInitTag2>);\nstatic_assert(not Parallel::initialization_tag<BadInitTag3>);\nstatic_assert(not Parallel::initialization_tag<BadInitTag4>);\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_MemoryMonitor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <type_traits>\n#include <unordered_map>\n\n#include \"DataStructures/Matrix.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Invoke.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/MemoryMonitor/MemoryMonitor.hpp\"\n#include \"Parallel/MemoryMonitor/Tags.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ContributeMemoryData.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ProcessArray.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ProcessGroups.hpp\"\n#include \"ParallelAlgorithms/Actions/MemoryMonitor/ProcessSingleton.hpp\"\n#include \"ParallelAlgorithms/Events/MonitorMemory.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct MockMemoryMonitor {\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using component_being_mocked = mem_monitor::MemoryMonitor<Metavariables>;\n  using metavariables = Metavariables;\n  using simple_tags = tmpl::list<mem_monitor::Tags::MemoryHolder>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct SingletonParallelComponent {\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct GroupParallelComponent {\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct NodegroupParallelComponent {\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct ArrayParallelComponent {\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\n// This component deserves special mention. It is supposed to be playing the\n// role of the DgElementArray, the component where we run the MonitorMemory\n// event. However, inside MonitorMemory, there is an `if constexpr` check if the\n// component we are monitoring is an array. If we are monitoring an array, then\n// Parallel::contribute_to_reduction is called. If we make this component a\n// MockArray, the test fails to build because the ATF doesn't support\n// reductions yet. To get around this, we make this component a MockSingleton\n// because the event only needs to be run on one \"element\". We also remove it\n// from the components to monitor. Once the ATF supports reductions, this can be\n// changed to a MockArray.\ntemplate <typename Metavariables>\nstruct FakeDgElementArray {\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using metavariables = Metavariables;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 tmpl::list<domain::Tags::Element<3>>>>>>;\n};\n\nstruct TestMetavariables {\n  using component_list =\n      tmpl::list<MockMemoryMonitor<TestMetavariables>,\n                 SingletonParallelComponent<TestMetavariables>,\n                 TestHelpers::observers::MockObserverWriter<TestMetavariables>,\n                 GroupParallelComponent<TestMetavariables>,\n                 FakeDgElementArray<TestMetavariables>,\n                 NodegroupParallelComponent<TestMetavariables>>;\n\n\n  void pup(PUP::er& /*p*/) {}\n};\n\nvoid test_tags() {\n  INFO(\"Test Tags\");\n  using holder_tag = mem_monitor::Tags::MemoryHolder;\n  TestHelpers::db::test_simple_tag<holder_tag>(\"MemoryHolder\");\n\n  const std::string subpath =\n      mem_monitor::subfile_name<MockMemoryMonitor<TestMetavariables>>();\n\n  CHECK(subpath == \"/MemoryMonitors/MockMemoryMonitor\");\n}\n\nstruct TestMetavarsActions {\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  using component_list = tmpl::list<\n      MockMemoryMonitor<TestMetavarsActions>,\n      TestHelpers::observers::MockObserverWriter<TestMetavarsActions>,\n      SingletonParallelComponent<TestMetavarsActions>,\n      GroupParallelComponent<TestMetavarsActions>,\n      ArrayParallelComponent<TestMetavarsActions>,\n      NodegroupParallelComponent<TestMetavarsActions>>;\n\n\n  void pup(PUP::er& /*p*/) {}\n};\n\nusing metavars = TestMetavarsActions;\ntemplate <typename Metavars>\nusing mem_mon_comp = MockMemoryMonitor<Metavars>;\ntemplate <typename Metavars>\nusing obs_writer_comp = TestHelpers::observers::MockObserverWriter<Metavars>;\ntemplate <typename Metavars>\nusing sing_comp = SingletonParallelComponent<Metavars>;\ntemplate <typename Metavars>\nusing group_comp = GroupParallelComponent<Metavars>;\ntemplate <typename Metavars>\nusing array_comp = ArrayParallelComponent<Metavars>;\ntemplate <typename Metavars>\nusing nodegroup_comp = NodegroupParallelComponent<Metavars>;\ntemplate <typename Metavars>\nusing dg_elem_comp = FakeDgElementArray<Metavars>;\n\ntemplate <typename Metavariables>\nvoid setup_runner(\n    const gsl::not_null<ActionTesting::MockRuntimeSystem<Metavariables>*>\n        runner) {\n  // Setup all components even if we aren't using all of them\n  ActionTesting::emplace_singleton_component<sing_comp<Metavariables>>(\n      runner, ActionTesting::NodeId{1}, ActionTesting::LocalCoreId{1});\n  ActionTesting::emplace_group_component<group_comp<Metavariables>>(runner);\n  ActionTesting::emplace_nodegroup_component<nodegroup_comp<Metavariables>>(\n      runner);\n  if constexpr (tmpl::list_contains_v<typename Metavariables::component_list,\n                                      array_comp<Metavariables>>) {\n    ActionTesting::emplace_array_component<array_comp<Metavariables>>(\n        runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0);\n  }\n\n  // ObserverWriter\n  ActionTesting::emplace_nodegroup_component_and_initialize<\n      obs_writer_comp<Metavariables>>(runner, {});\n\n  // MemoryMonitor\n  ActionTesting::emplace_singleton_component_and_initialize<\n      mem_mon_comp<Metavariables>>(runner, ActionTesting::NodeId{2},\n                                   ActionTesting::LocalCoreId{2}, {});\n\n  if constexpr (tmpl::list_contains_v<typename Metavariables::component_list,\n                                      dg_elem_comp<Metavariables>>) {\n    // FakeDgElementArray that's actually a singleton\n    const ElementId<3> element_id{0};\n    const Element<3> element{element_id, {}};\n    ActionTesting::emplace_singleton_component_and_initialize<\n        dg_elem_comp<Metavariables>>(runner, ActionTesting::NodeId{0},\n                                     ActionTesting::LocalCoreId{0}, {element});\n  }\n\n  runner->set_phase(Parallel::Phase::Testing);\n}\n\ntemplate <typename Component, typename Metavariables>\nvoid check_output(const ActionTesting::MockRuntimeSystem<Metavariables>& runner,\n                  const double time, const size_t num_nodes,\n                  const std::vector<double>& sizes) {\n  auto& read_file = ActionTesting::get_databox_tag<\n      obs_writer_comp<Metavariables>,\n      TestHelpers::observers::MockReductionFileTag>(runner, 0);\n  INFO(\"Checking output of \" + pretty_type::name<Component>());\n  const auto& dataset =\n      read_file.get_dat(mem_monitor::subfile_name<Component>());\n  const std::vector<std::string>& legend = dataset.get_legend();\n\n  size_t num_columns;\n  if constexpr (Parallel::is_singleton_v<Component>) {\n    // time, proc, size\n    num_columns = 3;\n  } else if constexpr (Parallel::is_group_v<Component>) {\n    // time, size on node 0, size on node 1, ...,  proc of max size, max size,\n    // avg per node\n    num_columns = num_nodes + 4;\n  } else {\n    // time, size on node 0, size on node 1, ..., avg per node\n    num_columns = num_nodes + 2;\n  }\n\n  CHECK(legend.size() == num_columns);\n\n  const Matrix data = dataset.get_data();\n  // We only wrote one line of data for all components\n  CHECK(data.rows() == 1);\n  CHECK(data.columns() == num_columns);\n\n  const double average =\n      alg::accumulate(sizes, 0.0) / static_cast<double>(num_nodes);\n\n  CHECK(data(0, 0) == time);\n  // Singletons don't report sizes on each node because there is only one\n  // measurement\n  if constexpr (not Parallel::is_singleton_v<Component>) {\n    for (size_t i = 0; i < num_nodes; i++) {\n      INFO(\"i = \" + get_output(i));\n      CHECK(data(0, i + 1) == sizes[i]);\n    }\n  }\n  CHECK(data(0, num_columns - 1) == average);\n}\n\ntemplate <typename Component, typename Metavariables>\nvoid run_group_actions(\n    const gsl::not_null<ActionTesting::MockRuntimeSystem<Metavariables>*>\n        runner,\n    const int num_nodes, const int num_procs, const double time,\n    const std::vector<double>& sizes) {\n  INFO(\"Checking actions of \" + pretty_type::name<Component>());\n  size_t num_branches;\n  if constexpr (Parallel::is_group_v<Component>) {\n    num_branches = static_cast<size_t>(num_procs);\n  } else {\n    num_branches = static_cast<size_t>(num_nodes);\n    // Avoid unused parameter warning\n    (void)num_procs;\n  }\n\n  CHECK(ActionTesting::number_of_queued_simple_actions<\n            mem_mon_comp<Metavariables>>(*runner, 0) == num_branches);\n\n  const auto& mem_holder_tag =\n      ActionTesting::get_databox_tag<mem_mon_comp<Metavariables>,\n                                     mem_monitor::Tags::MemoryHolder>(*runner,\n                                                                      0);\n\n  // Before we invoke the actions, the map for this component shouldn't exist\n  // because this is the first time we are calling it.\n  const std::string name = pretty_type::name<Component>();\n  CHECK(mem_holder_tag.count(name) == 0);\n\n  for (size_t i = 0; i < num_branches; i++) {\n    // unordered_map<name, unordered_map<time, unordered_map<node/proc, size>>>\n    // Putting the CHECK before the action invocation is arbitrary. The map at a\n    // specific time is empty both before the first action is invoked and after\n    // the last action is invoked. Because of this, we need to put the CHECK in\n    // an if statement to avoid an access error, whether the check happens\n    // before or after the invocation.\n    if (i != 0) {\n      CHECK(mem_holder_tag.at(name).at(time).size() == i);\n    }\n    ActionTesting::invoke_queued_simple_action<mem_mon_comp<Metavariables>>(\n        runner, 0);\n  }\n\n  // After we invoke the actions, the map for the current component should be\n  // empty because the time should have been erased\n  CHECK(mem_holder_tag.at(name).empty());\n\n  // The last action should have called a threaded action to write data\n  CHECK(ActionTesting::number_of_queued_threaded_actions<\n            obs_writer_comp<Metavariables>>(*runner, 0) == 1);\n  ActionTesting::invoke_queued_threaded_action<obs_writer_comp<Metavariables>>(\n      runner, 0);\n\n  check_output<Component>(*runner, time, static_cast<size_t>(num_nodes), sizes);\n}\n\n// Contribute memory data can only be used with groups or nodegroups\ntemplate <typename Gen>\nvoid test_contribute_memory_data(const gsl::not_null<Gen*> gen,\n                                 const bool use_process_component_actions) {\n  INFO(\"Test ContributeMemoryData with\" +\n       (use_process_component_actions ? \"\"s : \"out\"s) + \" Process(Node)Group\");\n  std::uniform_real_distribution<double> dist{0, 10.0};\n\n  // 4 mock nodes, 3 mock cores per node\n  const size_t num_nodes = 4;\n  const size_t num_procs_per_node = 3;\n  const size_t num_procs = num_nodes * num_procs_per_node;\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {}, {}, std::vector<size_t>(num_nodes, num_procs_per_node)};\n\n  setup_runner(make_not_null(&runner));\n\n  auto& cache = ActionTesting::cache<mem_mon_comp<metavars>>(runner, 0);\n  auto& mem_monitor_proxy =\n      Parallel::get_parallel_component<mem_mon_comp<metavars>>(cache);\n\n  const double time = 0.5;\n  std::vector<double> sizes(num_nodes);\n  size_t index = 0;\n  // Do the group first. If we are using the ProcessGroup action, invoke all the\n  // simple actions on the group and calculate the size of that component so we\n  // can compare, otherwise call the ContributeMemoryData action directly with a\n  // random size\n  if (use_process_component_actions) {\n    auto& group_proxy =\n        Parallel::get_parallel_component<group_comp<metavars>>(cache);\n    Parallel::simple_action<mem_monitor::ProcessGroups>(group_proxy, time);\n\n    for (size_t proc = 0; proc < num_procs; proc++) {\n      CHECK(\n          ActionTesting::number_of_queued_simple_actions<group_comp<metavars>>(\n              runner, proc) == 1);\n      if (proc != 0 and proc % num_procs_per_node == 0) {\n        ++index;\n      }\n      sizes[index] +=\n          size_of_object_in_bytes(*Parallel::local_branch(group_proxy)) / 1.0e6;\n      ActionTesting::invoke_queued_simple_action<group_comp<metavars>>(\n          make_not_null(&runner), proc);\n    }\n  } else {\n    for (size_t proc = 0; proc < num_procs; proc++) {\n      const double size = dist(*gen);\n      if (proc != 0 and proc % num_procs_per_node == 0) {\n        ++index;\n      }\n      sizes[index] += size;\n      Parallel::simple_action<\n          mem_monitor::ContributeMemoryData<group_comp<metavars>>>(\n          mem_monitor_proxy, time, static_cast<int>(proc), size);\n    }\n  }\n\n  run_group_actions<group_comp<metavars>>(\n      make_not_null(&runner), static_cast<int>(num_nodes),\n      static_cast<int>(num_procs), time, sizes);\n\n  sizes.clear();\n  sizes.resize(num_nodes);\n\n  // Now for the nodegroup\n  if (use_process_component_actions) {\n    auto& nodegroup_proxy =\n        Parallel::get_parallel_component<nodegroup_comp<metavars>>(cache);\n    Parallel::simple_action<mem_monitor::ProcessGroups>(nodegroup_proxy, time);\n\n    for (size_t node = 0; node < num_nodes; node++) {\n      CHECK(ActionTesting::number_of_queued_simple_actions<\n                nodegroup_comp<metavars>>(runner, node) == 1);\n      sizes[node] =\n          size_of_object_in_bytes(*Parallel::local_branch(nodegroup_proxy)) /\n          1.0e6;\n      ActionTesting::invoke_queued_simple_action<nodegroup_comp<metavars>>(\n          make_not_null(&runner), node);\n    }\n  } else {\n    for (size_t node = 0; node < num_nodes; node++) {\n      const double size = dist(*gen);\n      sizes[node] = size;\n      Parallel::simple_action<\n          mem_monitor::ContributeMemoryData<nodegroup_comp<metavars>>>(\n          mem_monitor_proxy, time, static_cast<int>(node), size);\n    }\n  }\n\n  run_group_actions<nodegroup_comp<metavars>>(\n      make_not_null(&runner), static_cast<int>(num_nodes),\n      static_cast<int>(num_procs), time, sizes);\n}\n\ntemplate <typename Gen>\nvoid test_process_array(const gsl::not_null<Gen*> gen) {\n  INFO(\"Test ProcessArray\");\n  std::uniform_real_distribution<double> dist{0, 10.0};\n\n  // 4 mock nodes, 3 mock cores per node\n  const size_t num_nodes = 4;\n  const size_t num_procs_per_node = 3;\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {}, {}, std::vector<size_t>(num_nodes, num_procs_per_node)};\n\n  setup_runner(make_not_null(&runner));\n\n  auto& cache = ActionTesting::cache<mem_mon_comp<metavars>>(runner, 0);\n  auto& mem_monitor_proxy =\n      Parallel::get_parallel_component<mem_mon_comp<metavars>>(cache);\n\n  const double time = 0.5;\n  std::vector<double> size_per_node(num_nodes);\n  fill_with_random_values(make_not_null(&size_per_node), gen,\n                          make_not_null(&dist));\n\n  Parallel::simple_action<mem_monitor::ProcessArray<array_comp<metavars>>>(\n      mem_monitor_proxy, time, size_per_node);\n  CHECK(ActionTesting::number_of_queued_simple_actions<mem_mon_comp<metavars>>(\n            runner, 0) == 1);\n  ActionTesting::invoke_queued_simple_action<mem_mon_comp<metavars>>(\n      make_not_null(&runner), 0);\n\n  CHECK(ActionTesting::number_of_queued_threaded_actions<\n            obs_writer_comp<metavars>>(runner, 0) == 1);\n  ActionTesting::invoke_queued_threaded_action<obs_writer_comp<metavars>>(\n      make_not_null(&runner), 0);\n\n  check_output<array_comp<metavars>>(runner, time, num_nodes, size_per_node);\n}\n\nvoid test_process_singleton() {\n  INFO(\"Test ProcessSingleton\");\n\n  // 4 mock nodes, 3 mock cores per node\n  const size_t num_nodes = 4;\n  const size_t num_procs_per_node = 3;\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {}, {}, std::vector<size_t>(num_nodes, num_procs_per_node)};\n\n  setup_runner(make_not_null(&runner));\n\n  auto& cache = ActionTesting::cache<sing_comp<metavars>>(runner, 0);\n  auto& singleton_proxy =\n      Parallel::get_parallel_component<sing_comp<metavars>>(cache);\n\n  const double time = 0.5;\n  std::vector<double> sizes(num_nodes, 0.0);\n\n  Parallel::simple_action<mem_monitor::ProcessSingleton>(singleton_proxy, time);\n  CHECK(ActionTesting::number_of_queued_simple_actions<sing_comp<metavars>>(\n            runner, 0) == 1);\n\n  // We multiply by the number of nodes here because in the checK_output()\n  // function, it takes an average over number of nodes to accommodate arrays\n  // and (node)groups. But for singletons, we don't need an average because it's\n  // just one measurement. So we \"undo\" the average here by multiplying by the\n  // number of nodes\n  sizes[0] = static_cast<double>(num_nodes) *\n             size_of_object_in_bytes(*Parallel::local(singleton_proxy)) / 1.0e6;\n\n  ActionTesting::invoke_queued_simple_action<sing_comp<metavars>>(\n      make_not_null(&runner), 0);\n\n  CHECK(ActionTesting::number_of_queued_threaded_actions<\n            obs_writer_comp<metavars>>(runner, 0) == 1);\n  ActionTesting::invoke_queued_threaded_action<obs_writer_comp<metavars>>(\n      make_not_null(&runner), 0);\n\n  check_output<sing_comp<metavars>>(runner, time, num_nodes, sizes);\n}\n\nstruct BadArrayChareMetavariables {\n  using component_list =\n      tmpl::list<ArrayParallelComponent<BadArrayChareMetavariables>>;\n\n};\n\nvoid test_event_construction() {\n  CHECK_THROWS_WITH(\n      ([]() {\n        std::vector<std::string> misspelled_component{\"GlabolCahce\"};\n\n        Events::MonitorMemory<1> event{\n            {misspelled_component}, Options::Context{}, metavars{}};\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot monitor memory usage of unknown parallel component\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        std::vector<std::string> array_component{\"ArrayParallelComponent\"};\n\n        Events::MonitorMemory<2> event{{array_component},\n                                       Options::Context{},\n                                       BadArrayChareMetavariables{}};\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Currently, the only Array parallel component allowed to \"\n          \"be monitored is the DgElementArray.\"));\n}\n\nvoid test_monitor_memory_event() {\n  using event_metavars = TestMetavariables;\n  INFO(\"Checking MonitorMemory event\");\n\n  // 4 mock nodes, 3 mock cores per node\n  const size_t num_nodes = 4;\n  const size_t num_procs_per_node = 3;\n  const size_t num_procs = num_nodes * num_procs_per_node;\n  ActionTesting::MockRuntimeSystem<event_metavars> runner{\n      {}, {}, std::vector<size_t>(num_nodes, num_procs_per_node)};\n\n  setup_runner(make_not_null(&runner));\n\n  auto& cache = ActionTesting::cache<mem_mon_comp<event_metavars>>(runner, 0);\n\n  std::vector<std::string> components_to_monitor{};\n  // Note that we don't monitor the global caches here. This is because the\n  // entry methods of the global caches used to compute memory and send it to\n  // the MemoryMonitor are not compatible with the ATF. Also don't monitor the\n  // DgElementArray or the array component (because we can't in the ATF)\n  using component_list =\n      tmpl::list_difference<typename event_metavars::component_list,\n                            tmpl::list<dg_elem_comp<event_metavars>>>;\n  tmpl::for_each<component_list>([&components_to_monitor](auto component_v) {\n    using component = tmpl::type_from<decltype(component_v)>;\n    components_to_monitor.emplace_back(pretty_type::name<component>());\n  });\n\n  // Create event\n  Events::MonitorMemory<3> monitor_memory{\n      {components_to_monitor}, Options::Context{}, metavars{}};\n\n  const auto& element =\n      ActionTesting::get_databox_tag<dg_elem_comp<event_metavars>,\n                                     domain::Tags::Element<3>>(runner, 0);\n\n  // Run the event. This will queue a lot of actions\n  const double time = 1.4;\n  monitor_memory(element, cache, 0,\n                 std::add_pointer_t<dg_elem_comp<event_metavars>>{},\n                 {\"TimeName\", time});\n\n  // Check how many simple actions are queued:\n  // - MemoryMonitor: 1\n  // - ObserverWriter: 4 (one per node)\n  // - Singleton: 1\n  // - Group: 12 (one per core)\n  // - NodeGroup: 4 (one per node)\n  tmpl::for_each<component_list>([&runner](auto component_v) {\n    using component = tmpl::type_from<decltype(component_v)>;\n    if constexpr (Parallel::is_singleton_v<component>) {\n      CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner,\n                                                                      0) == 1);\n    } else if constexpr (Parallel::is_group_v<component>) {\n      for (int i = 0; i < static_cast<int>(num_procs); i++) {\n        CHECK(ActionTesting::number_of_queued_simple_actions<component>(\n                  runner, i) == 1);\n      }\n    } else if constexpr (Parallel::is_nodegroup_v<component>) {\n      for (int i = 0; i < static_cast<int>(num_nodes); i++) {\n        CHECK(ActionTesting::number_of_queued_simple_actions<component>(\n                  runner, i) == 1);\n      }\n    }\n  });\n\n  // First invoke the simple actions on the singletons.\n  ActionTesting::invoke_queued_simple_action<mem_mon_comp<event_metavars>>(\n      make_not_null(&runner), 0);\n  ActionTesting::invoke_queued_simple_action<sing_comp<event_metavars>>(\n      make_not_null(&runner), 0);\n\n  // These immediately call the observer writer to write data to disk\n  CHECK(ActionTesting::number_of_queued_threaded_actions<\n            obs_writer_comp<event_metavars>>(runner, 0) == 2);\n\n  ActionTesting::invoke_queued_threaded_action<obs_writer_comp<event_metavars>>(\n      make_not_null(&runner), 0);\n  ActionTesting::invoke_queued_threaded_action<obs_writer_comp<event_metavars>>(\n      make_not_null(&runner), 0);\n\n  // Check that the data was written correctly\n  std::vector<double> sing_sizes(num_nodes, 0.0);\n  // We multiply by the number of nodes for same reason as in\n  // test_process_singleton()\n  auto& mem_mon_proxy =\n      Parallel::get_parallel_component<mem_mon_comp<event_metavars>>(cache);\n  sing_sizes[0] = static_cast<double>(num_nodes) *\n                  size_of_object_in_bytes(*Parallel::local(mem_mon_proxy)) /\n                  1.0e6;\n  check_output<mem_mon_comp<event_metavars>>(runner, time, num_nodes,\n                                             sing_sizes);\n\n  auto& sing_cache = ActionTesting::cache<sing_comp<event_metavars>>(runner, 0);\n  auto& sing_proxy =\n      Parallel::get_parallel_component<sing_comp<event_metavars>>(sing_cache);\n  sing_sizes[0] = static_cast<double>(num_nodes) *\n                  size_of_object_in_bytes(*Parallel::local(sing_proxy)) / 1.0e6;\n  check_output<sing_comp<event_metavars>>(runner, time, num_nodes, sing_sizes);\n\n  // Now for the groups and nodegroups\n  using group_list =\n      tmpl::list<obs_writer_comp<event_metavars>, group_comp<event_metavars>,\n                 nodegroup_comp<event_metavars>>;\n  tmpl::for_each<group_list>([&runner, &num_nodes, &num_procs, &time,\n                              &cache](auto component_v) {\n    using component = tmpl::type_from<decltype(component_v)>;\n    std::vector<double> sizes(num_nodes);\n    auto& proxy = Parallel::get_parallel_component<component>(cache);\n    Parallel::simple_action<mem_monitor::ProcessGroups>(proxy, time);\n\n    for (size_t node = 0; node < num_nodes; node++) {\n      if constexpr (Parallel::is_nodegroup_v<component>) {\n        // Need the cache of this specific component to get the proper size\n        auto& local_cache = ActionTesting::cache<component>(runner, node);\n        auto& local_proxy =\n            Parallel::get_parallel_component<component>(local_cache);\n        sizes[node] =\n            size_of_object_in_bytes(*Parallel::local_branch(local_proxy)) /\n            1.0e6;\n        ActionTesting::invoke_queued_simple_action<component>(\n            make_not_null(&runner), node);\n\n      } else {\n        for (size_t proc = 0; proc < num_procs_per_node; proc++) {\n          const size_t global_proc = node * num_procs_per_node + proc;\n          // Need the local cache to get the proper size\n          auto& local_cache =\n              ActionTesting::cache<component>(runner, global_proc);\n          auto& local_proxy =\n              Parallel::get_parallel_component<component>(local_cache);\n          sizes[node] +=\n              size_of_object_in_bytes(*Parallel::local_branch(local_proxy)) /\n              1.0e6;\n          ActionTesting::invoke_queued_simple_action<component>(\n              make_not_null(&runner), global_proc);\n        }\n      }\n    }\n\n    run_group_actions<component>(make_not_null(&runner), num_nodes, num_procs,\n                                 time, sizes);\n  });\n\n  // All actions should be completed now\n  CHECK(ActionTesting::number_of_queued_simple_actions<\n            mem_mon_comp<event_metavars>>(runner, 0) == 0);\n  CHECK(ActionTesting::number_of_queued_threaded_actions<\n            obs_writer_comp<event_metavars>>(runner, 0) == 0);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.MemoryMonitor\", \"[Unit][Parallel]\") {\n  MAKE_GENERATOR(gen);\n  test_tags();\n  // First only test the ContributeMemoryData action (second arg false)\n  test_contribute_memory_data(make_not_null(&gen), false);\n  // Then test the Process(Node)Group actions (second arg true)\n  test_contribute_memory_data(make_not_null(&gen), true);\n  test_process_array(make_not_null(&gen));\n  test_process_singleton();\n  test_event_construction();\n  test_monitor_memory_event();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_MultiReaderSpinlock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <atomic>\n#include <chrono>\n#include <thread>\n\n#include \"Parallel/MultiReaderSpinlock.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.MultiReaderSpinlock\", \"[Parallel][Unit]\") {\n  // It's very difficult to test a lock since they are designed to prevent race\n  // conditions (undefined behavior). We try to do the following just as a\n  // basic sanity check that attempts to make sure things \"work\"\n\n  Parallel::MultiReaderSpinlock mrsl{};\n  {\n    // Test two readers can lock at the same time\n    std::thread t0{[&mrsl]() {\n      mrsl.read_lock();\n      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n      mrsl.read_unlock();\n    }};\n    std::thread t1{[&mrsl]() {\n      const auto start = std::chrono::high_resolution_clock::now();\n      mrsl.read_lock();\n      const auto end = std::chrono::high_resolution_clock::now();\n      mrsl.read_unlock();\n      const std::chrono::duration<long, std::nano> diff{end - start};\n      // Verify it took less than 1 millisecond to lock and unlock the readlock.\n      // This is a lot less than the 10ms we sleep the other thread for, even in\n      // a debug build, and the lock should be acquired in about 1us in a debug\n      // build.\n      CHECK(diff.count() < 1000000);\n    }};\n    t0.join();\n    t1.join();\n  }\n  {\n    // Test that if a reader has a read-lock the writer cannot acquire a\n    // write-lock.\n    std::atomic<bool> locked{false};\n    std::thread t0{[&mrsl, &locked]() {\n      mrsl.read_lock();\n      locked.store(true, std::memory_order_release);\n      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n      mrsl.read_unlock();\n    }};\n    while (not locked.load(std::memory_order_acquire)) {\n    }\n    std::thread t1{[&mrsl]() {\n      const auto start = std::chrono::high_resolution_clock::now();\n      mrsl.write_lock();\n      const auto end = std::chrono::high_resolution_clock::now();\n      mrsl.write_unlock();\n      const std::chrono::duration<long, std::nano> diff{end - start};\n      // Verify it took more than 1ms to retrieve the lock.\n      CHECK(diff.count() > 1000000);\n    }};\n    t0.join();\n    t1.join();\n  }\n  {\n    // Test that if a writer has a write-lock another writer cannot acquire a\n    // write-lock.\n    std::atomic<bool> locked{false};\n    std::thread t0{[&mrsl, &locked]() {\n      mrsl.write_lock();\n      locked.store(true, std::memory_order_release);\n      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n      mrsl.write_unlock();\n    }};\n    while (not locked.load(std::memory_order_acquire)) {\n    }\n    std::thread t1{[&mrsl]() {\n      const auto start = std::chrono::high_resolution_clock::now();\n      mrsl.write_lock();\n      const auto end = std::chrono::high_resolution_clock::now();\n      mrsl.write_unlock();\n      const std::chrono::duration<long, std::nano> diff{end - start};\n      // Verify it took more than 1ms to retrieve the lock.\n      CHECK(diff.count() > 1000000);\n    }};\n    t0.join();\n    t1.join();\n  }\n  {\n    // Test that if a writer has a write-lock reader cannot acquire a\n    // read-lock.\n    std::atomic<bool> locked{false};\n    std::thread t0{[&mrsl, &locked]() {\n      mrsl.write_lock();\n      locked.store(true, std::memory_order_release);\n      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n      mrsl.write_unlock();\n    }};\n    while (not locked.load(std::memory_order_acquire)) {\n    }\n    std::thread t1{[&mrsl]() {\n      const auto start = std::chrono::high_resolution_clock::now();\n      mrsl.read_lock();\n      const auto end = std::chrono::high_resolution_clock::now();\n      mrsl.read_unlock();\n      const std::chrono::duration<long, std::nano> diff{end - start};\n      // Verify it took more than 1ms to retrieve the lock.\n      CHECK(diff.count() > 1000000);\n    }};\n    t0.join();\n    t1.join();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_NodeLock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/NodeLock.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test_two_locks() {\n  Parallel::NodeLock first{};\n  Parallel::NodeLock second{};\n  CHECK_FALSE(first.is_destroyed());\n  CHECK_FALSE(second.is_destroyed());\n\n  CHECK(first.try_lock());\n  CHECK_FALSE(first.try_lock());\n\n  CHECK(second.try_lock());\n  CHECK_FALSE(second.try_lock());\n\n  first.unlock();\n  CHECK_FALSE(second.try_lock());\n  CHECK(first.try_lock());\n\n  first.destroy();\n  CHECK(first.is_destroyed());\n  CHECK_FALSE(second.is_destroyed());\n  CHECK_FALSE(second.try_lock());\n\n  second.unlock();\n  second.lock();\n  CHECK_FALSE(second.try_lock());\n}\n\nvoid test_move_semantics() {\n  Parallel::NodeLock unlocked{};\n  Parallel::NodeLock move_of_unlocked(std::move(unlocked));\n  CHECK(move_of_unlocked.try_lock());\n  CHECK_FALSE(move_of_unlocked.try_lock());\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(unlocked.is_destroyed());\n  CHECK_FALSE(move_of_unlocked.is_destroyed());\n\n  Parallel::NodeLock locked{};\n  locked.lock();\n  CHECK_FALSE(locked.try_lock());\n  Parallel::NodeLock move_of_locked(std::move(locked));\n  CHECK_FALSE(move_of_locked.try_lock());\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(locked.is_destroyed());\n  CHECK_FALSE(move_of_locked.is_destroyed());\n\n  Parallel::NodeLock destroyed{};\n  destroyed.destroy();\n  CHECK(destroyed.is_destroyed());\n  Parallel::NodeLock move_of_destroyed(std::move(destroyed));\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(destroyed.is_destroyed());\n  CHECK(move_of_destroyed.is_destroyed());\n}\n\nvoid test_move_assign_semantics() {\n  Parallel::NodeLock unlocked_1{};\n  Parallel::NodeLock move_assign_from_unlocked_to_unlocked{};\n  move_assign_from_unlocked_to_unlocked = std::move(unlocked_1);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(unlocked_1.is_destroyed());\n  CHECK_FALSE(move_assign_from_unlocked_to_unlocked.is_destroyed());\n  CHECK(move_assign_from_unlocked_to_unlocked.try_lock());\n\n  Parallel::NodeLock unlocked_2{};\n  Parallel::NodeLock move_assign_from_unlocked_to_locked{};\n  move_assign_from_unlocked_to_locked.lock();\n  move_assign_from_unlocked_to_locked = std::move(unlocked_2);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(unlocked_2.is_destroyed());\n  CHECK_FALSE(move_assign_from_unlocked_to_locked.is_destroyed());\n  CHECK(move_assign_from_unlocked_to_locked.try_lock());\n\n  Parallel::NodeLock unlocked_3{};\n  Parallel::NodeLock move_assign_from_unlocked_to_destroyed{};\n  move_assign_from_unlocked_to_destroyed.destroy();\n  move_assign_from_unlocked_to_destroyed = std::move(unlocked_3);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(unlocked_3.is_destroyed());\n  CHECK_FALSE(move_assign_from_unlocked_to_destroyed.is_destroyed());\n  CHECK(move_assign_from_unlocked_to_destroyed.try_lock());\n\n  Parallel::NodeLock locked_1{};\n  locked_1.lock();\n  Parallel::NodeLock move_assign_from_locked_to_unlocked{};\n  move_assign_from_locked_to_unlocked = std::move(locked_1);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(locked_1.is_destroyed());\n  CHECK_FALSE(move_assign_from_locked_to_unlocked.is_destroyed());\n  CHECK_FALSE(move_assign_from_locked_to_unlocked.try_lock());\n\n  Parallel::NodeLock locked_2{};\n  locked_2.lock();\n  Parallel::NodeLock move_assign_from_locked_to_locked{};\n  move_assign_from_locked_to_locked.lock();\n  move_assign_from_locked_to_locked = std::move(locked_2);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(locked_2.is_destroyed());\n  CHECK_FALSE(move_assign_from_locked_to_locked.is_destroyed());\n  CHECK_FALSE(move_assign_from_locked_to_locked.try_lock());\n\n  Parallel::NodeLock locked_3{};\n  locked_3.lock();\n  Parallel::NodeLock move_assign_from_locked_to_destroyed{};\n  move_assign_from_locked_to_destroyed.destroy();\n  move_assign_from_locked_to_destroyed = std::move(locked_3);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(locked_3.is_destroyed());\n  CHECK_FALSE(move_assign_from_locked_to_destroyed.is_destroyed());\n  CHECK_FALSE(move_assign_from_locked_to_destroyed.try_lock());\n\n  Parallel::NodeLock destroyed_1{};\n  destroyed_1.destroy();\n  Parallel::NodeLock move_assign_from_destroyed_to_unlocked{};\n  move_assign_from_destroyed_to_unlocked = std::move(destroyed_1);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(destroyed_1.is_destroyed());\n  CHECK(move_assign_from_destroyed_to_unlocked.is_destroyed());\n\n  Parallel::NodeLock destroyed_2{};\n  destroyed_2.destroy();\n  Parallel::NodeLock move_assign_from_destroyed_to_locked{};\n  move_assign_from_destroyed_to_locked.lock();\n  move_assign_from_destroyed_to_locked = std::move(destroyed_2);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(destroyed_2.is_destroyed());\n  CHECK(move_assign_from_destroyed_to_locked.is_destroyed());\n\n  Parallel::NodeLock destroyed_3{};\n  destroyed_3.destroy();\n  Parallel::NodeLock move_assign_from_destroyed_to_destroyed{};\n  move_assign_from_destroyed_to_destroyed.destroy();\n  move_assign_from_destroyed_to_destroyed = std::move(destroyed_3);\n  // NOLINTNEXTLINE(clang-analyzer-cplusplus.Move,bugprone-use-after-move)\n  CHECK(destroyed_3.is_destroyed());\n  CHECK(move_assign_from_destroyed_to_destroyed.is_destroyed());\n}\n\nvoid test_serialization() {\n  Parallel::NodeLock unlocked{};\n  Parallel::NodeLock serialized_unlocked = serialize_and_deserialize(unlocked);\n  CHECK_FALSE(unlocked.is_destroyed());\n  CHECK_FALSE(serialized_unlocked.is_destroyed());\n  CHECK(unlocked.try_lock());\n  CHECK(serialized_unlocked.try_lock());\n\n  Parallel::NodeLock locked{};\n  locked.lock();\n  Parallel::NodeLock serialized_locked = serialize_and_deserialize(locked);\n  CHECK_FALSE(locked.is_destroyed());\n  CHECK_FALSE(serialized_locked.is_destroyed());\n  CHECK_FALSE(locked.try_lock());\n  // a locked NodeLock is deserialized unlocked\n  CHECK(serialized_locked.try_lock());\n\n  Parallel::NodeLock destroyed{};\n  destroyed.destroy();\n  Parallel::NodeLock serialized_destroyed =\n      serialize_and_deserialize(destroyed);\n  CHECK(destroyed.is_destroyed());\n  CHECK(serialized_destroyed.is_destroyed());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.NodeLock\", \"[Unit][Parallel]\") {\n  test_two_locks();\n  test_move_semantics();\n  test_move_assign_semantics();\n  test_serialization();\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        Parallel::NodeLock lock{};\n        lock.destroy();\n        lock.lock();\n      }()),\n      Catch::Matchers::ContainsSubstring(\"Trying to lock a destroyed lock\"));\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_OutputInbox.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <iomanip>\n#include <map>\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Parallel/OutputInbox.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace Parallel {\nnamespace {\nstruct TestInbox1 {\n  using temporal_id = double;\n  using type = std::map<temporal_id, std::pair<std::string, size_t>>;\n\n  static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n\n    ss << std::scientific << std::setprecision(10);\n    ss << pad << \"TestInbox1:\\n\";\n    for (const auto& [time, data] : inbox) {\n      ss << pad << \" Time: \" << time << \", data: \" << data << \"\\n\";\n    }\n\n    return ss.str();\n  }\n};\n\nstruct TestInbox2 {\n  using temporal_id = std::string;\n  using type = std::map<temporal_id, double>;\n\n  static std::string output_inbox(const type& inbox,\n                                  const size_t padding_size) {\n    std::stringstream ss{};\n    const std::string pad(padding_size, ' ');\n\n    ss << std::scientific << std::setprecision(10);\n    ss << pad << \"TestInbox2:\\n\";\n    for (const auto& [name, number] : inbox) {\n      ss << pad << \" Name: \" << name << \", number: \" << number << \"\\n\";\n    }\n\n    return ss.str();\n  }\n};\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.OutputInbox\", \"[Unit][Parallel]\") {\n  tuples::TaggedTuple<TestInbox1, TestInbox2> inboxes{};\n  std::map<double, std::pair<std::string, size_t>>& inbox_1 =\n      tuples::get<TestInbox1>(inboxes);\n  inbox_1[1.0] = std::make_pair(\"OneMississippi\", 1_st);\n  inbox_1[2.0] = std::make_pair(\"TwoMississippi\", 2_st);\n  std::map<std::string, double>& inbox_2 = tuples::get<TestInbox2>(inboxes);\n  inbox_2[\"Dog\"] = 5.5;\n  inbox_2[\"Human\"] = 0.6;\n  inbox_2[\"Cat\"] = 4.3;\n\n  std::string result = output_inbox<TestInbox1>(inboxes, 0_st);\n  std::string expected_result =\n      \"TestInbox1:\\n\"\n      \" Time: 1.0000000000e+00, data: (OneMississippi, 1)\\n\"\n      \" Time: 2.0000000000e+00, data: (TwoMississippi, 2)\\n\";\n\n  CHECK(result == expected_result);\n\n  result = output_inbox<TestInbox1>(inboxes, 1_st);\n  expected_result =\n      \" TestInbox1:\\n\"\n      \"  Time: 1.0000000000e+00, data: (OneMississippi, 1)\\n\"\n      \"  Time: 2.0000000000e+00, data: (TwoMississippi, 2)\\n\";\n\n  CHECK(result == expected_result);\n\n  result = output_inbox<TestInbox2>(inboxes, 3_st);\n  expected_result =\n      \"   TestInbox2:\\n\"\n      \"    Name: Cat, number: 4.3000000000e+00\\n\"\n      \"    Name: Dog, number: 5.5000000000e+00\\n\"\n      \"    Name: Human, number: 6.0000000000e-01\\n\";\n\n  CHECK(result == expected_result);\n}\n}  // namespace\n}  // namespace Parallel\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_Parallel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.NodeAndPes\", \"[Unit][Parallel]\") {\n  CHECK(1 == sys::number_of_procs());\n  CHECK(0 == sys::my_proc());\n  CHECK(1 == sys::number_of_nodes());\n  CHECK(0 == sys::my_node());\n  CHECK(1 == sys::procs_on_node(sys::my_node()));\n  CHECK(0 == sys::my_local_rank());\n  CHECK(0 == sys::first_proc_on_node(sys::my_node()));\n  CHECK(0 == sys::local_rank_of(sys::my_proc()));\n  CHECK(0 == sys::node_of(sys::my_proc()));\n  // We check that the wall time is greater than or equal to zero and less\n  // than 2 seconds, just to check the function actually returns something.\n  const double walltime = sys::wall_time();\n  CHECK((0 <= walltime and 2 >= walltime));\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_ParallelComponentHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/CreateFromOptions.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\nstruct Tag0 {};\nstruct Tag1 {};\nstruct Tag2 {};\nstruct Tag3 {};\nstruct Tag4 {};\nstruct Tag5 {};\nstruct Tag6 {};\nstruct Tag7 {};\n\nstruct Action0 {\n  using inbox_tags = tmpl::list<Tag0, Tag1>;\n};\nstruct Action1 {};\nstruct Action2 {\n  using inbox_tags = tmpl::list<Tag1, Tag2, Tag3>;\n  using const_global_cache_tags = tmpl::list<Tag6>;\n};\n\nstruct InitTag0 {};\nstruct InitTag1 {};\nstruct InitTag2 {};\nstruct InitTag3 {};\nstruct InitTag4 {};\nstruct InitTag5 {};\nstruct InitTag6 {};\nstruct InitTag7 {};\n\nstruct InitAction0 {\n  using simple_tags_from_options = tmpl::list<InitTag0>;\n};\nstruct InitAction1 {};\nstruct InitAction2 {\n  using simple_tags_from_options = tmpl::list<InitTag1, InitTag2>;\n};\nstruct InitAction3 {\n  using simple_tags_from_options = tmpl::list<InitTag0, InitTag1, InitTag3>;\n};\n\nstruct ComponentInit {\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<InitAction0, InitAction1, InitAction2>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n};\n\nstruct ComponentExecute {\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Execute,\n                                        tmpl::list<Action0, Action1>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using const_global_cache_tags = tmpl::list<Tag2, Tag4>;\n};\n\nstruct ComponentInitAndExecute {\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitAction3>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute,\n                             tmpl::list<InitAction2, Action0, Action2>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n};\n\nstruct ComponentInitWithAllocate {\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<InitAction0, InitAction1, InitAction2>>>;\n  using array_allocation_tags = tmpl::list<InitTag4, InitTag5>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using const_global_cache_tags = tmpl::list<Tag1, Tag5, Tag7>;\n};\n\nstruct ComponentExecuteWithAllocate {\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Execute,\n                                        tmpl::list<Action0, Action1>>>;\n  using array_allocation_tags = tmpl::list<InitTag6, InitTag7>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n};\n\nstruct ComponentInitAndExecuteWithAllocate {\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitAction3>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute,\n                             tmpl::list<InitAction2, Action0, Action2>>>;\n  using array_allocation_tags = tmpl::list<InitTag3, InitTag4>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using const_global_cache_tags = tmpl::list<Tag2, Tag4>;\n};\n\nstruct Metavariables0 {\n  using component_list = tmpl::list<ComponentInit>;\n};\n\nstruct Metavariables1 {\n  using const_global_cache_tags = tmpl::list<Tag0, Tag4>;\n  using component_list = tmpl::list<>;\n};\n\nstruct Metavariables2 {\n  using component_list = tmpl::list<ComponentInitWithAllocate>;\n};\n\nstruct Metavariables3 {\n  using const_global_cache_tags = tmpl::list<Tag0, Tag4>;\n  using component_list = tmpl::list<ComponentInitWithAllocate>;\n};\n\nstruct Metavariables4 {\n  using component_list = tmpl::list<ComponentInitAndExecute>;\n};\n\nstruct Metavariables5 {\n  using const_global_cache_tags = tmpl::list<Tag0, Tag4>;\n  using component_list = tmpl::list<ComponentInitAndExecute>;\n};\n\nstruct Metavariables6 {\n  using component_list = tmpl::list<ComponentInitAndExecuteWithAllocate>;\n};\n\nstruct Metavariables7 {\n  using const_global_cache_tags = tmpl::list<Tag0, Tag4>;\n  using component_list = tmpl::list<ComponentInitAndExecuteWithAllocate>;\n};\n\nstatic_assert(\n    std::is_same_v<\n        Parallel::get_inbox_tags<tmpl::list<Action0, Action1, Action2>>,\n        tmpl::list<Tag0, Tag1, Tag2, Tag3>>,\n    \"Failed testing get_inbox_tags\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_const_global_cache_tags<Metavariables0>,\n                   tmpl::list<>>,\n    \"Failed testing get_const_global_cache_tags\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_const_global_cache_tags<Metavariables1>,\n                   tmpl::list<Tag0, Tag4>>,\n    \"Failed testing get_const_global_cache_tags\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_const_global_cache_tags<Metavariables2>,\n                   tmpl::list<Tag1, Tag5, Tag7>>,\n    \"Failed testing get_const_global_cache_tags\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_const_global_cache_tags<Metavariables3>,\n                   tmpl::list<Tag0, Tag4, Tag1, Tag5, Tag7>>,\n    \"Failed testing get_const_global_cache_tags\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_const_global_cache_tags<Metavariables4>,\n                   tmpl::list<Tag6>>,\n    \"Failed testing get_const_global_cache_tags\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_const_global_cache_tags<Metavariables5>,\n                   tmpl::list<Tag0, Tag4, Tag6>>,\n    \"Failed testing get_const_global_cache_tags\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_const_global_cache_tags<Metavariables6>,\n                   tmpl::list<Tag2, Tag4, Tag6>>,\n    \"Failed testing get_const_global_cache_tags\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_const_global_cache_tags<Metavariables7>,\n                   tmpl::list<Tag0, Tag4, Tag2, Tag6>>,\n    \"Failed testing get_const_global_cache_tags\");\n\nstatic_assert(std::is_same_v<Parallel::get_initialization_actions_list<\n                                 ComponentInit::phase_dependent_action_list>,\n                             tmpl::list<InitAction0, InitAction1, InitAction2>>,\n              \"Failed testing get_intialization_actions_list\");\n\nstatic_assert(std::is_same_v<Parallel::get_initialization_actions_list<\n                                 ComponentExecute::phase_dependent_action_list>,\n                             tmpl::list<>>,\n              \"Failed testing get_intialization_actions_list\");\n\nstatic_assert(\n    std::is_same_v<Parallel::get_initialization_actions_list<\n                       ComponentInitAndExecute::phase_dependent_action_list>,\n                   tmpl::list<InitAction3>>,\n    \"Failed testing get_intialization_actions_list\");\n\nstatic_assert(std::is_same_v<ComponentInit::simple_tags_from_options,\n                             tmpl::list<InitTag0, InitTag1, InitTag2>>,\n              \"Failed testing get_simple_tags_from_options\");\n\nstatic_assert(\n    std::is_same_v<ComponentExecute::simple_tags_from_options, tmpl::list<>>,\n    \"Failed testing get_simple_tags_from_options\");\n\nstatic_assert(std::is_same_v<ComponentInitAndExecute::simple_tags_from_options,\n                             tmpl::list<InitTag0, InitTag1, InitTag3>>,\n              \"Failed testing get_simple_tags_from_options\");\n\nstatic_assert(\n    std::is_same_v<ComponentInitWithAllocate::simple_tags_from_options,\n                   tmpl::list<InitTag0, InitTag1, InitTag2>>,\n    \"Failed testing get_simple_tags_from_options\");\n\nstatic_assert(\n    std::is_same_v<ComponentExecuteWithAllocate::simple_tags_from_options,\n                   tmpl::list<>>,\n    \"Failed testing get_simple_tags_from_options\");\n\nstatic_assert(std::is_same_v<\n                  ComponentInitAndExecuteWithAllocate::simple_tags_from_options,\n                  tmpl::list<InitTag0, InitTag1, InitTag3>>,\n              \"Failed testing get_simple_tags_from_options\");\n\nnamespace OptionTags {\nstruct Yards {\n  using type = double;\n  static constexpr Options::String help = {\"halp_yards\"};\n};\nstruct Dim {\n  using type = size_t;\n  static constexpr Options::String help = {\"halp_size\"};\n};\nstruct Greeting {\n  using type = std::string;\n  static constexpr Options::String help = {\"halp_greeting\"};\n};\nstruct Name {\n  using type = std::string;\n  static constexpr Options::String help = {\"halp_name\"};\n};\n}  // namespace OptionTags\n\nnamespace Initialization {\nstruct MetavariablesGreeting {};\n\nnamespace Tags {\nstruct Yards : db::SimpleTag {\n  using option_tags = tmpl::list<OptionTags::Yards>;\n  using type = double;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double yards) { return yards; }\n};\nstruct Feet : db::SimpleTag {\n  using option_tags = tmpl::list<OptionTags::Yards>;\n  using type = double;\n\n  static constexpr bool pass_metavariables = false;\n  static double create_from_options(const double yards) { return 3.0 * yards; }\n};\nstruct Sides : db::SimpleTag {\n  using option_tags = tmpl::list<OptionTags::Yards, OptionTags::Dim>;\n  using type = std::vector<double>;\n\n  static constexpr bool pass_metavariables = false;\n  static std::vector<double> create_from_options(const double yards,\n                                                 const size_t dim) {\n    return std::vector<double>(dim, yards);\n  }\n};\nstruct FullGreeting : db::SimpleTag {\n  using type = std::string;\n\n  static constexpr bool pass_metavariables = true;\n  template <typename Metavariables>\n  using option_tags = tmpl::list<OptionTags::Greeting, OptionTags::Name>;\n  template <typename Metavariables>\n  static std::string create_from_options(const std::string& greeting,\n                                         const std::string& name) {\n    if (std::is_same<Metavariables, MetavariablesGreeting>::value) {\n      return \"A special \" + greeting + ' ' + name;\n    } else {\n      return greeting + ' ' + name;\n    }\n  }\n};\n}  // namespace Tags\n}  // namespace Initialization\n\nusing simple_tags_from_options_0 = tmpl::list<>;\n\nusing simple_tags_from_options_1 =\n    tmpl::list<Initialization::Tags::Yards, Initialization::Tags::Feet,\n               Initialization::Tags::Sides>;\n\nusing simple_tags_from_options_2 =\n    tmpl::list<Initialization::Tags::Yards, Initialization::Tags::Feet,\n               Initialization::Tags::FullGreeting>;\n\nstatic_assert(\n    std::is_same_v<\n        Parallel::get_option_tags<simple_tags_from_options_0, NoSuchType>,\n        tmpl::list<>>,\n    \"Failed testing get_option_tags\");\n\nstatic_assert(\n    std::is_same_v<\n        Parallel::get_option_tags<simple_tags_from_options_1, NoSuchType>,\n        tmpl::list<OptionTags::Yards, OptionTags::Dim>>,\n    \"Failed testing get_option_tags\");\n\nstatic_assert(\n    std::is_same_v<\n        Parallel::get_option_tags<simple_tags_from_options_2, NoSuchType>,\n        tmpl::list<OptionTags::Yards, OptionTags::Greeting, OptionTags::Name>>,\n    \"Failed testing get_option_tags\");\n\nusing all_option_tags = tmpl::list<OptionTags::Yards, OptionTags::Dim,\n                                   OptionTags::Greeting, OptionTags::Name>;\n\ntemplate <typename Metavariables, typename... InitializationTags>\nvoid check_initialization_items(\n    const Options::Parser<all_option_tags>& all_options,\n    const tuples::TaggedTuple<InitializationTags...>& expected_items) {\n  using simple_tags_from_options = tmpl::list<InitializationTags...>;\n  using option_tags =\n      Parallel::get_option_tags<simple_tags_from_options, Metavariables>;\n  const auto options = all_options.apply<option_tags>([](auto... args) {\n    return tuples::tagged_tuple_from_typelist<option_tags>(std::move(args)...);\n  });\n  CHECK(Parallel::create_from_options<Metavariables>(\n            options, simple_tags_from_options{}) == expected_items);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.ComponentHelpers\", \"[Unit][Parallel]\") {\n  Options::Parser<all_option_tags> all_options(\"\");\n  all_options.parse(\n      \"Yards: 2.0\\n\"\n      \"Dim: 3\\n\"\n      \"Greeting: Hello\\n\"\n      \"Name: World\\n\");\n  check_initialization_items<NoSuchType>(\n      all_options, tuples::TaggedTuple<Initialization::Tags::Yards,\n                                       Initialization::Tags::Feet,\n                                       Initialization::Tags::Sides>{\n                       2.0, 6.0, std::vector<double>{2.0, 2.0, 2.0}});\n  check_initialization_items<NoSuchType>(\n      all_options, tuples::TaggedTuple<Initialization::Tags::Yards,\n                                       Initialization::Tags::Feet,\n                                       Initialization::Tags::FullGreeting>{\n                       2.0, 6.0, \"Hello World\"});\n  check_initialization_items<Initialization::MetavariablesGreeting>(\n      all_options, tuples::TaggedTuple<Initialization::Tags::Yards,\n                                       Initialization::Tags::Feet,\n                                       Initialization::Tags::FullGreeting>{\n                       2.0, 6.0, \"A special Hello World\"});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_Phase.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.Phase\", \"[Parallel][Unit]\") {\n  // These two variables must correspond to the first and last\n  // enum values of Parallel::Phase for the test to work properly\n  const Parallel::Phase first_enum = Parallel::Phase::AdjustDomain;\n  const Parallel::Phase last_enum = Parallel::Phase::WriteCheckpoint;\n\n  using enum_t = std::underlying_type_t<Parallel::Phase>;\n  REQUIRE(enum_t(0) == static_cast<enum_t>(first_enum));\n  const auto known_phases = Parallel::known_phases();\n  REQUIRE(enum_t(known_phases.size()) == static_cast<enum_t>(last_enum) + 1);\n\n  for (const auto phase : known_phases) {\n    CHECK(phase ==\n          TestHelpers::test_creation<Parallel::Phase>(get_output(phase)));\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        TestHelpers::test_creation<Parallel::Phase>(\"Bad phase name\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"Bad phase name\\\" to Parallel::Phase.\"));\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_PhaseChangeMain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Helpers/Parallel/RoundRobinArrayElements.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmGroup.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/InboxInserters.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Main.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseControl/ContributeToPhaseChangeReduction.hpp\"\n#include \"Parallel/PhaseControl/PhaseControlTags.hpp\"\n#include \"Parallel/PhaseControlReductionHelpers.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace PhaseChangeTest {\ntemplate <class Metavariables>\nstruct ArrayComponent;\n\ntemplate <class Metavariables>\nstruct GroupComponent;\n\nstruct StepNumber : db::SimpleTag {\n  using type = size_t;\n};\n\nusing PhaseChangeStepNumber =\n    PhaseControl::TagAndCombine<PhaseChangeTest::StepNumber, funcl::Max<>>;\n\nstruct IsDone {\n  using type = bool;\n  using combine_method = funcl::And<>;\n  using main_combine_method = funcl::Or<>;\n};\n\nstruct InitializeStepTag {\n  using simple_tags = tmpl::list<StepNumber>;\n\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Initialization::mutate_assign<simple_tags>(make_not_null(&box), 0_st);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct IncrementStep {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<StepNumber>(\n        [](const gsl::not_null<size_t*> step_number) { ++(*step_number); },\n        make_not_null(&box));\n    SPECTRE_PARALLEL_REQUIRE(db::get<StepNumber>(box) < 31);\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct ReportArrayPhaseControlDataAndTerminate{\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache, const int array_index,\n      ActionList /*meta*/, const ParallelComponent* const /*meta*/) {\n    Parallel::contribute_to_phase_change_reduction<\n        ArrayComponent<Metavariables>>(\n        tuples::TaggedTuple<IsDone>{array_index == 0\n                                        ? db::get<StepNumber>(box) % 2 == 0\n                                        : db::get<StepNumber>(box) % 3 == 0},\n        cache, array_index);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct ReportGroupPhaseControlDataAndTerminate {\n  template <typename... DbTags, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<tmpl::list<DbTags...>>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    Parallel::contribute_to_phase_change_reduction<\n        GroupComponent<Metavariables>>(\n        tuples::TaggedTuple<IsDone, PhaseChangeStepNumber>{\n            false, db::get<StepNumber>(box)},\n        cache);\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\ntemplate <class Metavariables>\nstruct GroupComponent {\n  using chare_type = Parallel::Algorithms::Group;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitializeStepTag>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<IncrementStep, ReportGroupPhaseControlDataAndTerminate>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<GroupComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\ntemplate <class Metavariables>\nstruct ArrayComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<InitializeStepTag>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Evolve,\n          tmpl::list<IncrementStep, ReportArrayPhaseControlDataAndTerminate>>>;\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n  using array_allocation_tags = tmpl::list<>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      const tuples::tagged_tuple_from_typelist<simple_tags_from_options>&\n      /*initialization_items*/,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayComponent>(local_cache);\n\n    TestHelpers::Parallel::assign_array_elements_round_robin_style(\n        array_proxy, 2, static_cast<size_t>(sys::number_of_procs()), {},\n        global_cache, procs_to_ignore);\n  }\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ArrayComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n}  // namespace PhaseChangeTest\n\nstruct TestMetavariables {\n  // Two components, array component has two elements, group component has one\n  // element (run only on one core).\n  // Array component [0] asks to be done on steps divisible by 2\n  // Array component [1] asks to be done on steps divisible by 3\n  // The group component just submits its step number.\n  // Phase moves to Exit phase when the step is greater than 25 and all\n  // components with done states say they are done.\n  // Test errors if any component reaches step 31.\n\n  using component_list =\n      tmpl::list<PhaseChangeTest::ArrayComponent<TestMetavariables>,\n                 PhaseChangeTest::GroupComponent<TestMetavariables>>;\n\n  static constexpr Options::String help = \"\";\n\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Evolve,\n       Parallel::Phase::Exit}};\n\n  struct DummyPhaseChange : public PhaseChange {\n    using phase_change_tags_and_combines =\n        tmpl::list<PhaseChangeTest::IsDone,\n                   PhaseChangeTest::PhaseChangeStepNumber>;\n  };\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<PhaseChange, tmpl::list<DummyPhaseChange>>>;\n  };\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<TestMetavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_ResourceInfo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingletonDeclarations.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/ResourceInfo.hpp\"\n#include \"Parallel/Tags/ResourceInfo.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\nnamespace {\ntemplate <typename Metavariables, size_t Index>\nstruct FakeSingleton {\n  using chare_type = Parallel::Algorithms::Singleton;\n  using metavariables = Metavariables;\n  static std::string name() { return \"FakeSingleton\" + get_output(Index); }\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using simple_tags_from_options = tmpl::list<>;\n};\n\ntemplate <size_t... Indices>\nstruct Metavariables {\n  using component_list = tmpl::list<FakeSingleton<Metavariables, Indices>...>;\n};\n\nstruct EmptyMetavars {};\n\ntemplate <size_t Index>\nusing fake_singleton = FakeSingleton<EmptyMetavars, Index>;\n\nvoid test_singleton_info() {\n  SingletonInfoHolder<fake_singleton<0>> info_holder{};\n  CHECK(info_holder.proc() == std::nullopt);\n  CHECK_FALSE(info_holder.is_exclusive());\n\n  info_holder = TestHelpers::test_creation<\n      Parallel::SingletonInfoHolder<fake_singleton<0>>>(\n      \"Proc: 0\\n\"\n      \"Exclusive: false\\n\");\n  CHECK(info_holder.proc().value() == 0);\n  CHECK_FALSE(info_holder.is_exclusive());\n\n  auto serialized_info_holder = serialize_and_deserialize(info_holder);\n  CHECK(serialized_info_holder == info_holder);\n\n  info_holder = TestHelpers::test_creation<\n      Parallel::SingletonInfoHolder<fake_singleton<0>>>(\n      \"Proc: Auto\\n\"\n      \"Exclusive: false\\n\");\n  CHECK(info_holder.proc() == std::nullopt);\n  CHECK_FALSE(info_holder.is_exclusive());\n\n  const auto info_holder2 = TestHelpers::test_creation<\n      Parallel::SingletonInfoHolder<fake_singleton<0>>>(\n      \"Proc: 4\\n\"\n      \"Exclusive: true\\n\");\n  CHECK(info_holder2.proc().value() == 4);\n  CHECK(info_holder2.is_exclusive());\n\n  CHECK_FALSE(info_holder == info_holder2);\n  CHECK(info_holder != info_holder2);\n\n  CHECK_THROWS_WITH(([]() {\n                      auto info_holder_error = TestHelpers::test_creation<\n                          Parallel::SingletonInfoHolder<fake_singleton<0>>>(\n                          \"Proc: -2\\n\"\n                          \"Exclusive: true\\n\");\n                      (void)info_holder_error;\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Proc must be a non-negative integer.\"));\n}\n\nvoid test_singleton_pack() {\n  const auto info0 = TestHelpers::test_creation<\n      Parallel::SingletonInfoHolder<fake_singleton<0>>>(\n      \"Proc: Auto\\n\"\n      \"Exclusive: false\\n\");\n  const auto info1 = TestHelpers::test_creation<\n      Parallel::SingletonInfoHolder<fake_singleton<1>>>(\n      \"Proc: 1\\n\"\n      \"Exclusive: true\\n\");\n  const auto info2 = TestHelpers::test_creation<\n      Parallel::SingletonInfoHolder<fake_singleton<2>>>(\n      \"Proc: Auto\\n\"\n      \"Exclusive: true\\n\");\n\n  using pack_list =\n      tmpl::list<fake_singleton<0>, fake_singleton<1>, fake_singleton<2>>;\n  // Make one singleton constructed with auto\n  const auto singleton_pack =\n      TestHelpers::test_creation<SingletonPack<pack_list>>(\n          \"FakeSingleton0: Auto\\n\"\n          \"FakeSingleton1:\\n\"\n          \"  Proc: 1\\n\"\n          \"  Exclusive: true\\n\"\n          \"FakeSingleton2:\\n\"\n          \"  Proc: Auto\\n\"\n          \"  Exclusive: true\\n\");\n\n  const auto& pack_info0 = singleton_pack.get<fake_singleton<0>>();\n  const auto& pack_info1 = singleton_pack.get<fake_singleton<1>>();\n  const auto& pack_info2 = singleton_pack.get<fake_singleton<2>>();\n\n  CHECK(info0.proc() == pack_info0.proc());\n  CHECK(info0.is_exclusive() == pack_info0.is_exclusive());\n  CHECK(info1.proc() == pack_info1.proc());\n  CHECK(info1.is_exclusive() == pack_info1.is_exclusive());\n  CHECK(info2.proc() == pack_info2.proc());\n  CHECK(info2.is_exclusive() == pack_info2.is_exclusive());\n\n  const auto serialized_pack = serialize_and_deserialize(singleton_pack);\n\n  CHECK(serialized_pack == singleton_pack);\n  CHECK_FALSE(serialized_pack != singleton_pack);\n}\n\nvoid test_tags() {\n  using metavars = Metavariables<0>;\n\n  TestHelpers::db::test_simple_tag<Tags::ResourceInfo<metavars>>(\n      \"ResourceInfo\");\n\n  Parallel::GlobalCache<metavars> cache{};\n  const SingletonInfoHolder<FakeSingleton<metavars, 0>> info_holder{{0}, false};\n\n  ResourceInfo<metavars> resource_info{false, std::optional{info_holder}};\n  resource_info.build_singleton_map(cache);\n}\n\nvoid test_single_core() {\n  {\n    INFO(\"AvoidGlobalProc0 and Singletons\");\n    using metavars = Metavariables<0>;\n    Parallel::GlobalCache<metavars> cache{};\n    // Both of these should be identical since we are running on one proc\n    auto resource_info_0 =\n        TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n            \"AvoidGlobalProc0: false\\n\"\n            \"Singletons:\\n\"\n            \"  FakeSingleton0:\\n\"\n            \"    Proc: 0\\n\"\n            \"    Exclusive: false\\n\");\n    auto resource_info_auto =\n        TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n            \"AvoidGlobalProc0: false\\n\"\n            \"Singletons:\\n\"\n            \"  FakeSingleton0: Auto\\n\");\n\n    resource_info_0.build_singleton_map(cache);\n    resource_info_auto.build_singleton_map(cache);\n\n    CHECK_FALSE(resource_info_0 == resource_info_auto);\n    CHECK(resource_info_0 != resource_info_auto);\n\n    CHECK_FALSE(resource_info_0.avoid_global_proc_0());\n    CHECK_FALSE(resource_info_auto.avoid_global_proc_0());\n\n    const size_t proc_0 =\n        resource_info_0.template proc_for<FakeSingleton<metavars, 0>>();\n    const size_t proc_auto =\n        resource_info_auto.template proc_for<FakeSingleton<metavars, 0>>();\n\n    // Only running on once proc\n    CHECK(proc_0 == 0);\n    CHECK(proc_auto == 0);\n\n    const auto& info_0 =\n        resource_info_0.get_singleton_info<FakeSingleton<metavars, 0>>();\n    const auto& info_auto =\n        resource_info_auto.get_singleton_info<FakeSingleton<metavars, 0>>();\n\n    CHECK(info_0.proc().value() == 0);\n    CHECK(info_auto.proc().value() == 0);\n    CHECK_FALSE(info_0.is_exclusive());\n    CHECK_FALSE(info_auto.is_exclusive());\n\n    const auto serialized_resource_info_0 =\n        serialize_and_deserialize(resource_info_0);\n    const size_t serialized_proc_0 =\n        resource_info_0.template proc_for<FakeSingleton<metavars, 0>>();\n    const auto& serialized_info_0 =\n        resource_info_0.get_singleton_info<FakeSingleton<metavars, 0>>();\n\n    CHECK_FALSE(serialized_resource_info_0.avoid_global_proc_0());\n    CHECK(proc_0 == serialized_proc_0);\n    CHECK(info_0.proc() == serialized_info_0.proc());\n    CHECK(info_0.is_exclusive() == serialized_info_0.is_exclusive());\n  }\n  {\n    INFO(\"Singletons::Auto\");\n    using metavars = Metavariables<0, 1>;\n    auto resource_info =\n        TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n            \"AvoidGlobalProc0: false\\n\"\n            \"Singletons: Auto\\n\");\n    Parallel::GlobalCache<metavars> cache{};\n    resource_info.build_singleton_map(cache);\n    const size_t proc_0 =\n        resource_info.template proc_for<FakeSingleton<metavars, 0>>();\n    const size_t proc_1 =\n        resource_info.template proc_for<FakeSingleton<metavars, 1>>();\n    CHECK(proc_0 == 0);\n    CHECK(proc_1 == 0);\n\n    CHECK_FALSE(resource_info.avoid_global_proc_0());\n    const auto& info_0 =\n        resource_info.get_singleton_info<FakeSingleton<metavars, 0>>();\n    const auto& info_1 =\n        resource_info.get_singleton_info<FakeSingleton<metavars, 1>>();\n\n    CHECK(info_0.proc().value() == 0);\n    CHECK(info_1.proc().value() == 0);\n    CHECK_FALSE(info_0.is_exclusive());\n    CHECK_FALSE(info_1.is_exclusive());\n  }\n}\n\nvoid test_errors() {\n  using metavars = Metavariables<0>;\n  Parallel::GlobalCache<metavars> cache{};\n\n  // Check as many errors as we can on one proc\n  CHECK_THROWS_WITH(\n      ([]() {\n        const auto resource_info =\n            TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n                \"AvoidGlobalProc0: false\\n\"\n                \"Singletons:\\n\"\n                \"  FakeSingleton0:\\n\"\n                \"    Proc: -2\\n\"\n                \"    Exclusive: false\\n\");\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Proc must be a non-negative integer.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const auto resource_info =\n            TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n                \"AvoidGlobalProc0: true\\n\"\n                \"Singletons:\\n\"\n                \"  FakeSingleton0:\\n\"\n                \"    Proc: 0\\n\"\n                \"    Exclusive: true\\n\");\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"A singleton has requested to be exclusively on proc 0, \"\n          \"but the AvoidGlobalProc0 option is also set to true.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        const auto resource_info = TestHelpers::test_option_tag<\n            OptionTags::ResourceInfo<Metavariables<0, 1>>>(\n            \"AvoidGlobalProc0: false\\n\"\n            \"Singletons:\\n\"\n            \"  FakeSingleton0:\\n\"\n            \"    Proc: 0\\n\"\n            \"    Exclusive: true\\n\"\n            \"  FakeSingleton1:\\n\"\n            \"    Proc: 0\\n\"\n            \"    Exclusive: true\\n\");\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"Two singletons have requested to be on proc 0, but at least one of \"\n          \"them has requested to be exclusively on this proc.\"));\n\n  CHECK_THROWS_WITH(\n      ([&cache]() {\n        auto resource_info =\n            TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n                \"AvoidGlobalProc0: false\\n\"\n                \"Singletons:\\n\"\n                \"  FakeSingleton0:\\n\"\n                \"    Proc: 2\\n\"\n                \"    Exclusive: false\\n\");\n        resource_info.build_singleton_map(cache);\n      })(),\n      Catch::Matchers::ContainsSubstring(\"is beyond the last proc\"));\n\n  CHECK_THROWS_WITH(\n      ([&cache]() {\n        auto resource_info =\n            TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n                \"AvoidGlobalProc0: false\\n\"\n                \"Singletons:\\n\"\n                \"  FakeSingleton0:\\n\"\n                \"    Proc: 0\\n\"\n                \"    Exclusive: true\\n\");\n        resource_info.build_singleton_map(cache);\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"The total number of cores requested is less than or equal to the \"\n          \"number of cores that requested to be exclusive, i.e. without\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        auto resource_info =\n            TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n                \"AvoidGlobalProc0: true\\n\"\n                \"Singletons:\\n\"\n                \"  FakeSingleton0:\\n\"\n                \"    Proc: 0\\n\"\n                \"    Exclusive: false\\n\");\n        [[maybe_unused]] const size_t proc =\n            resource_info.template proc_for<FakeSingleton<metavars, 0>>();\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"The singleton map has not been built yet. You must call \"\n          \"build_singleton_map() before you call this function.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        auto resource_info =\n            TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n                \"AvoidGlobalProc0: true\\n\"\n                \"Singletons:\\n\"\n                \"  FakeSingleton0:\\n\"\n                \"    Proc: 0\\n\"\n                \"    Exclusive: false\\n\");\n        [[maybe_unused]] const auto& procs_to_ignore =\n            resource_info.procs_to_ignore();\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"The singleton map has not been built yet. You must call \"\n          \"build_singleton_map() before you call this function.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        auto resource_info =\n            TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n                \"AvoidGlobalProc0: true\\n\"\n                \"Singletons:\\n\"\n                \"  FakeSingleton0:\\n\"\n                \"    Proc: 0\\n\"\n                \"    Exclusive: false\\n\");\n        [[maybe_unused]] const auto& procs_to_ignore =\n            resource_info.procs_available_for_elements();\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"The singleton map has not been built yet. You must call \"\n          \"build_singleton_map() before you call this function.\"));\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        auto resource_info =\n            TestHelpers::test_option_tag<OptionTags::ResourceInfo<metavars>>(\n                \"AvoidGlobalProc0: true\\n\"\n                \"Singletons:\\n\"\n                \"  FakeSingleton0:\\n\"\n                \"    Proc: 0\\n\"\n                \"    Exclusive: false\\n\");\n        [[maybe_unused]] const auto singleton_info =\n            resource_info.get_singleton_info<FakeSingleton<metavars, 0>>();\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"The singleton map has not been built yet. You must call \"\n          \"build_singleton_map() before you call this function.\"));\n}\n\ntemplate <typename Metavariables>\nParallel::ResourceInfo<Metavariables> create_resource_info(\n    const bool avoid_global_proc_0,\n    const std::vector<std::pair<bool, int>>& singletons) {\n  std::string option_str =\n      \"AvoidGlobalProc0: \" + (avoid_global_proc_0 ? \"true\"s : \"false\"s) + \"\\n\";\n  option_str += \"Singletons:\\n\";\n  for (size_t i = 0; i < singletons.size(); i++) {\n    const bool exclusive = singletons[i].first;\n    const int proc = singletons[i].second;\n    option_str += \"  FakeSingleton\" + get_output(i) + \":\\n\";\n    option_str +=\n        \"    Proc: \" + (proc == -1 ? \"Auto\"s : get_output(proc)) + \"\\n\";\n    option_str += \"    Exclusive: \" + (exclusive ? \"true\"s : \"false\"s) + \"\\n\";\n  }\n\n  return serialize_and_deserialize(\n      TestHelpers::test_option_tag<OptionTags::ResourceInfo<Metavariables>>(\n          option_str));\n}\n\nconstexpr size_t num_singletons = 7;\nusing metavars = Metavariables<0, 1, 2, 3, 4, 5, 6>;\ntemplate <size_t Index>\nusing component = FakeSingleton<metavars, Index>;\n\nvoid check_resource_info(\n    const Parallel::GlobalCache<metavars>& cache, bool avoid_global_proc_0,\n    const std::vector<std::pair<bool, int>>& singletons,\n    const std::vector<std::pair<bool, int>>& expected_singletons) {\n  auto resource_info =\n      create_resource_info<metavars>(avoid_global_proc_0, singletons);\n  resource_info.build_singleton_map(cache);\n  const size_t num_procs = Parallel::number_of_procs<size_t>(cache);\n\n  std::unordered_set<size_t> expected_exclusive_procs{};\n  std::set<size_t> expected_procs_available_for_elements{};\n  for (auto& exclusive_and_proc : expected_singletons) {\n    if (exclusive_and_proc.first) {\n      expected_exclusive_procs.insert(\n          static_cast<size_t>(exclusive_and_proc.second));\n    }\n  }\n\n  if (avoid_global_proc_0) {\n    expected_exclusive_procs.insert(0);\n  }\n\n  // Construct the expected procs with elements slightly differently than inside\n  // ResourceInfo just to check we did it correctly. They should be equivalent\n  for (size_t i = 0; i < num_procs; i++) {\n    expected_procs_available_for_elements.insert(i);\n  }\n  for (size_t proc : expected_exclusive_procs) {\n    expected_procs_available_for_elements.erase(proc);\n  }\n\n  const auto& procs_to_ignore = resource_info.procs_to_ignore();\n  const auto& procs_available_for_elements =\n      resource_info.procs_available_for_elements();\n\n  CHECK(resource_info.avoid_global_proc_0() == avoid_global_proc_0);\n  CHECK(procs_to_ignore.size() == expected_exclusive_procs.size());\n  for (auto& exclusive_proc : expected_exclusive_procs) {\n    CHECK(procs_to_ignore.count(exclusive_proc) == 1);\n  }\n  CHECK(procs_available_for_elements.size() ==\n        num_procs - expected_exclusive_procs.size());\n  CHECK(procs_available_for_elements == expected_procs_available_for_elements);\n\n  tmpl::for_each<tmpl::range<size_t, 0, num_singletons>>(\n      [&expected_singletons, &resource_info](const auto size_holder) {\n        constexpr size_t index =\n            std::decay_t<decltype(size_holder)>::type::value;\n        INFO(\"Index: \" + get_output(index));\n        auto singleton_info =\n            resource_info.get_singleton_info<component<index>>();\n        CHECK(singleton_info.is_exclusive() ==\n              expected_singletons[index].first);\n        CHECK(static_cast<int>(singleton_info.proc().value()) ==\n              expected_singletons[index].second);\n        CHECK(resource_info.template proc_for<component<index>>() ==\n              static_cast<size_t>(expected_singletons[index].second));\n      });\n}\n\ntemplate <typename Gen>\nvoid test_single_node_multi_core(const gsl::not_null<Gen*> gen) {\n  // 1 node, 3 procs per node\n  Parallel::GlobalCache<metavars> cache{{}, {}, {3}};\n\n  INFO(\"1 node, 3 procs per node\");\n\n  {\n    INFO(\"AvoidGlobalProc0 false; All singletons Auto and not exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 0}, {false, 0}, {false, 0}, {false, 1},\n        {false, 1}, {false, 2}, {false, 2}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 true; All singletons Auto and not exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 1}, {false, 1}, {false, 1}, {false, 1},\n        {false, 2}, {false, 2}, {false, 2}};\n\n    check_resource_info(cache, true, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; One singleton exclusive Auto\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i == 4, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 1}, {false, 1}, {false, 1}, {false, 2},\n        {true, 0},  {false, 2}, {false, 2}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 true; One singleton exclusive Auto\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i == 6, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 2}, {false, 2}, {false, 2}, {false, 2},\n        {false, 2}, {false, 2}, {true, 1}};\n\n    check_resource_info(cache, true, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; All singletons requested and non-exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::uniform_int_distribution<int> dist{0, 2};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, dist(*gen)};\n    }\n\n    check_resource_info(cache, false, singletons, singletons);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; Two singletons exclusive, both Auto\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i == 2 or i == 4, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 2}, {false, 2}, {true, 0}, {false, 2},\n        {true, 1},  {false, 2}, {false, 2}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 false; Two singletons exclusive, one Auto one \"\n        \"requested\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i == 2 or i == 4, i == 2 ? 1 : -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 2}, {false, 2}, {true, 1}, {false, 2},\n        {true, 0},  {false, 2}, {false, 2}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; Two singletons exclusive, both requested\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] =\n          std::pair<bool, int>{i == 2 or i == 4, i == 2 ? 0 : i == 4 ? 2 : -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 1}, {false, 1}, {true, 0}, {false, 1},\n        {true, 2},  {false, 1}, {false, 1}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 false; One singleton exclusive Auto, one \"\n        \"nonexclusive requested\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i == 4, i == 5 ? 0 : -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 0}, {false, 0}, {false, 2}, {false, 2},\n        {true, 1},  {false, 0}, {false, 2}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; All random\");\n    const std::vector<std::pair<bool, int>> singletons{\n        {false, 2}, {false, 2},  {false, -1}, {false, 1},\n        {true, -1}, {false, -1}, {false, -1}};\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 2}, {false, 2}, {false, 1}, {false, 1},\n        {true, 0},  {false, 1}, {false, 2}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n}\n\ntemplate <typename Gen>\nvoid test_multi_node_multi_core(const gsl::not_null<Gen*> gen) {\n  // 3 nodes, 2 procs per node\n  Parallel::GlobalCache<metavars> cache{{}, {}, {2, 2, 2}};\n\n  INFO(\"3 nodes, 2 procs per node\");\n\n  {\n    INFO(\"AvoidGlobalProc0 false; All singletons Auto and not exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 0}, {false, 0}, {false, 1}, {false, 2},\n        {false, 3}, {false, 4}, {false, 5}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 true; All singletons Auto and not exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 1}, {false, 1}, {false, 1}, {false, 2},\n        {false, 3}, {false, 4}, {false, 5}};\n\n    check_resource_info(cache, true, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 false; All singletons specific procs and not \"\n        \"exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::uniform_int_distribution<int> dist{0, 5};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, dist(*gen)};\n    }\n\n    check_resource_info(cache, false, singletons, singletons);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 true; All singletons specific procs and not \"\n        \"exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::uniform_int_distribution<int> dist{1, 5};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, dist(*gen)};\n    }\n\n    check_resource_info(cache, true, singletons, singletons);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; Mix and match singletons #1: Exclusive Auto\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i < 3, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {true, 0},  {true, 2},  {true, 4}, {false, 1},\n        {false, 1}, {false, 3}, {false, 5}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 false; Mix and match singletons #2: Exclusive \"\n        \"specific\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::uniform_int_distribution<int> dist{3, 5};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] =\n          std::pair<bool, int>{i < 3, i < 3 ? static_cast<int>(i) : dist(*gen)};\n    }\n\n    check_resource_info(cache, false, singletons, singletons);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 true; Mix and match singletons #3: Exclusive Auto\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i < 3, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {true, 1},  {true, 2},  {true, 4}, {false, 3},\n        {false, 3}, {false, 5}, {false, 5}};\n\n    check_resource_info(cache, true, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 true; Mix and match singletons #4: Exclusive \"\n        \"specific\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::uniform_int_distribution<int> dist{4, 5};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{\n          i < 3, i < 3 ? static_cast<int>(i) + 1 : dist(*gen)};\n    }\n\n    check_resource_info(cache, true, singletons, singletons);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; Mix and match singletons #5: All random\");\n    std::vector<std::pair<bool, int>> singletons{\n        {false, -1}, {false, -1}, {false, 3}, {true, 2},\n        {false, 3},  {true, -1},  {false, -1}};\n    std::vector<std::pair<bool, int>> expected{\n        {false, 1}, {false, 4}, {false, 3}, {true, 2},\n        {false, 3}, {true, 0},  {false, 5}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 true; Mix and match singletons #6: All random\");\n    std::vector<std::pair<bool, int>> singletons{\n        {true, 1},  {false, 2},  {false, -1}, {false, 5},\n        {true, -1}, {false, -1}, {false, -1}};\n    std::vector<std::pair<bool, int>> expected{\n        {true, 1}, {false, 2}, {false, 2}, {false, 5},\n        {true, 3}, {false, 4}, {false, 4}};\n\n    check_resource_info(cache, true, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; Mix and match singletons #7: All random\");\n    std::vector<std::pair<bool, int>> singletons{\n        {true, 1},  {false, -1}, {true, 3},  {false, -1},\n        {true, -1}, {true, 5},   {false, -1}};\n    std::vector<std::pair<bool, int>> expected{\n        {true, 1}, {false, 2}, {true, 3}, {false, 2},\n        {true, 0}, {true, 5},  {false, 4}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; Mix and match singletons #8: All random\");\n    std::vector<std::pair<bool, int>> singletons{\n        {true, 1},  {false, 0}, {true, 3},  {false, -1},\n        {true, -1}, {true, 5},  {false, -1}};\n    std::vector<std::pair<bool, int>> expected{\n        {true, 1}, {false, 0}, {true, 3}, {false, 4},\n        {true, 2}, {true, 5},  {false, 4}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 false; Mix and match singletons #9: Different number \"\n        \"of procs on node\");\n    // 3 nodes, (1,2,2) procs on each node\n    Parallel::GlobalCache<metavars> local_cache{{}, {}, {1, 2, 2}};\n    std::vector<std::pair<bool, int>> singletons{\n        {false, -1}, {true, -1}, {false, -1}, {false, -1},\n        {true, -1},  {true, 0},  {true, 1}};\n    std::vector<std::pair<bool, int>> expected{\n        {false, 4}, {true, 2}, {false, 4}, {false, 4},\n        {true, 3},  {true, 0}, {true, 1}};\n\n    check_resource_info(local_cache, false, singletons, expected);\n  }\n}\n\ntemplate <typename Gen>\nvoid test_multi_node_multi_core_large(const gsl::not_null<Gen*> gen) {\n  // 8 nodes, 25 procs per node\n  const size_t num_nodes = 8;\n  const size_t num_procs_per_node = 25;\n  const size_t num_procs = num_nodes * num_procs_per_node;\n  Parallel::GlobalCache<metavars> cache{\n      {}, {}, std::vector<size_t>(num_nodes, num_procs_per_node)};\n\n  INFO(\"8 nodes, 25 procs per node\");\n\n  {\n    INFO(\"AvoidGlobalProc0 false; All singletons Auto and not exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 0},   {false, 25},  {false, 50}, {false, 75},\n        {false, 100}, {false, 125}, {false, 150}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 true; All singletons Auto and not exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {false, 1},   {false, 25},  {false, 50}, {false, 75},\n        {false, 100}, {false, 125}, {false, 150}};\n\n    check_resource_info(cache, true, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 false; All singletons specific procs and not \"\n        \"exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::uniform_int_distribution<int> dist{0, num_procs - 1};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, dist(*gen)};\n    }\n\n    check_resource_info(cache, false, singletons, singletons);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 true; All singletons specific procs and not \"\n        \"exclusive\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::uniform_int_distribution<int> dist{1, num_procs - 1};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{false, dist(*gen)};\n    }\n\n    check_resource_info(cache, true, singletons, singletons);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; Mix and match singletons #1: Exclusive Auto\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i < 3, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {true, 0},    {true, 25},   {true, 50},  {false, 75},\n        {false, 100}, {false, 125}, {false, 150}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 false; Mix and match singletons #2: Exclusive \"\n        \"specific\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::unordered_set<int> used_procs{};\n    std::uniform_int_distribution<int> dist_exclusive{0, 100};\n    std::uniform_int_distribution<int> dist_nonexclusive{101, num_procs - 1};\n    for (size_t i = 0; i < num_singletons; i++) {\n      int exclusive_proc = dist_exclusive(*gen);\n      while (used_procs.count(exclusive_proc) != 0) {\n        exclusive_proc = dist_exclusive(*gen);\n      }\n      used_procs.insert(exclusive_proc);\n      singletons[i] = std::pair<bool, int>{\n          i < 3, i < 3 ? exclusive_proc : dist_nonexclusive(*gen)};\n    }\n\n    check_resource_info(cache, false, singletons, singletons);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 true; Mix and match singletons #3: Exclusive Auto\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    for (size_t i = 0; i < num_singletons; i++) {\n      singletons[i] = std::pair<bool, int>{i < 3, -1};\n    }\n    const std::vector<std::pair<bool, int>> expected{\n        {true, 1},    {true, 25},   {true, 50},  {false, 75},\n        {false, 100}, {false, 125}, {false, 150}};\n\n    check_resource_info(cache, true, singletons, expected);\n  }\n  {\n    INFO(\n        \"AvoidGlobalProc0 true; Mix and match singletons #4: Exclusive \"\n        \"specific\");\n    std::vector<std::pair<bool, int>> singletons{num_singletons};\n    std::unordered_set<int> used_procs{};\n    std::uniform_int_distribution<int> dist_exclusive{1, 100};\n    std::uniform_int_distribution<int> dist_nonexclusive{101, num_procs - 1};\n    for (size_t i = 0; i < num_singletons; i++) {\n      int exclusive_proc = dist_exclusive(*gen);\n      while (used_procs.count(exclusive_proc) != 0) {\n        exclusive_proc = dist_exclusive(*gen);\n      }\n      used_procs.insert(exclusive_proc);\n      singletons[i] = std::pair<bool, int>{\n          i < 3, i < 3 ? exclusive_proc : dist_nonexclusive(*gen)};\n    }\n\n    check_resource_info(cache, true, singletons, singletons);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 false; Mix and match singletons #5: All random\");\n    std::vector<std::pair<bool, int>> singletons{\n        {false, 57}, {false, 56}, {true, -1}, {true, 149},\n        {false, -1}, {false, -1}, {false, -1}};\n    std::vector<std::pair<bool, int>> expected{\n        {false, 57}, {false, 56}, {true, 0},   {true, 149},\n        {false, 25}, {false, 75}, {false, 100}};\n\n    check_resource_info(cache, false, singletons, expected);\n  }\n  {\n    INFO(\"AvoidGlobalProc0 true; Mix and match singletons #6: All random\");\n    std::vector<std::pair<bool, int>> singletons{\n        {true, -1}, {true, 76}, {false, 77}, {false, 77},\n        {true, -1}, {true, -1}, {true, -1}};\n    std::vector<std::pair<bool, int>> expected{\n        {true, 1},  {true, 76}, {false, 77}, {false, 77},\n        {true, 25}, {true, 50}, {true, 100}};\n\n    check_resource_info(cache, true, singletons, expected);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.ResourceInfo\", \"[Unit][Parallel]\") {\n  MAKE_GENERATOR(gen);\n  test_singleton_info();\n  test_singleton_pack();\n  test_tags();\n  test_single_core();\n  test_single_node_multi_core(make_not_null(&gen));\n  test_multi_node_multi_core(make_not_null(&gen));\n  test_multi_node_multi_core_large(make_not_null(&gen));\n  test_errors();\n}\n}  // namespace Parallel\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_SectionReductions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <tuple>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Local.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"Parallel/Section.hpp\"\n#include \"Parallel/Tags/Section.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace {\n\n// In this test we create a few array elements, partition them in sections based\n// on their index, and then count the elements in each section separately in a\n// reduction.\n\nenum class EvenOrOdd { Even, Odd };\n\n// These don't need to be DataBox tags because they aren't placed in the\n// DataBox. they are used to identify the section. Note that in many practical\n// applications the section ID tag _is_ placed in the DataBox nonetheless.\nstruct EvenOrOddTag {\n  using type = EvenOrOdd;\n};\nstruct IsFirstElementTag {\n  using type = bool;\n};\n\ntemplate <typename ArraySectionIdTag>\nstruct ReceiveCount;\n\ntemplate <typename ArraySectionIdTag>\nstruct Count {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      Parallel::GlobalCache<Metavariables>& cache, const int array_index,\n      const ActionList /*meta*/, const ParallelComponent* const /*meta*/) {\n    const bool section_id = [&array_index]() {\n      if constexpr (std::is_same_v<ArraySectionIdTag, EvenOrOddTag>) {\n        return array_index % 2 == 0;\n      } else {\n        return array_index == 0;\n      }\n    }();\n    // [section_reduction]\n    auto& array_section = db::get_mutable_reference<\n        Parallel::Tags::Section<ParallelComponent, ArraySectionIdTag>>(\n        make_not_null(&box));\n    if (array_section.has_value()) {\n      // We'll just count the elements in each section\n      Parallel::ReductionData<\n          Parallel::ReductionDatum<bool, funcl::AssertEqual<>>,\n          Parallel::ReductionDatum<size_t, funcl::Plus<>>>\n          reduction_data{section_id, 1};\n      // Reduce over the section and broadcast to the full array\n      auto& array_proxy =\n          Parallel::get_parallel_component<ParallelComponent>(cache);\n      Parallel::contribute_to_reduction<ReceiveCount<ArraySectionIdTag>>(\n          std::move(reduction_data), array_proxy[array_index], array_proxy,\n          make_not_null(&*array_section));\n    }\n    // [section_reduction]\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename ArraySectionIdTag>\nstruct ReceiveCount {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables>\n  static void apply(db::DataBox<DbTagsList>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const int array_index, const bool section_id,\n                    const size_t count) {\n    if constexpr (std::is_same_v<ArraySectionIdTag, EvenOrOddTag>) {\n      const bool is_even = section_id;\n      Parallel::printf(\n          \"Element %d received reduction: Counted %zu %s elements.\\n\",\n          array_index, count, is_even ? \"even\" : \"odd\");\n      SPECTRE_PARALLEL_REQUIRE(count == (is_even ? 3 : 2));\n    } else {\n      const bool is_first_element = section_id;\n      Parallel::printf(\n          \"Element %d received reduction: Counted %zu element in \"\n          \"'IsFirstElement' section.\\n\",\n          array_index, count);\n      SPECTRE_PARALLEL_REQUIRE(is_first_element);\n      SPECTRE_PARALLEL_REQUIRE(count == 1);\n    }\n  }\n};\n\ntemplate <typename Metavariables>\nstruct ArrayComponent {\n  using chare_type = Parallel::Algorithms::Array;\n  static constexpr bool checkpoint_data = true;\n  using metavariables = Metavariables;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Count<EvenOrOddTag>,\n                     // Test performing a reduction over another section\n                     Count<IsFirstElementTag>,\n                     // Test performing multiple reductions asynchronously\n                     Count<EvenOrOddTag>, Count<EvenOrOddTag>,\n                     Count<IsFirstElementTag>,\n                     Parallel::Actions::TerminatePhase>>>;\n\n  // [sections_example]\n  using array_allocation_tags = tmpl::list<\n      // The section proxy will be stored in each element's DataBox in this tag\n      // for convenient access\n      Parallel::Tags::Section<ArrayComponent, EvenOrOddTag>,\n      Parallel::Tags::Section<ArrayComponent, IsFirstElementTag>>;\n  using simple_tags_from_options =\n      tmpl::append<Parallel::get_simple_tags_from_options<\n                       Parallel::get_initialization_actions_list<\n                           phase_dependent_action_list>>,\n                   array_allocation_tags>;\n\n  static void allocate_array(\n      Parallel::CProxy_GlobalCache<Metavariables>& global_cache,\n      tuples::tagged_tuple_from_typelist<simple_tags_from_options>\n          initialization_items,\n      const tuples::tagged_tuple_from_typelist<array_allocation_tags>&\n      /*array_allocation_items*/\n      = {},\n      const std::unordered_set<size_t>& procs_to_ignore = {}) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    auto& array_proxy =\n        Parallel::get_parallel_component<ArrayComponent>(local_cache);\n\n    // Create sections from element IDs (the corresponding elements will be\n    // created below)\n    const size_t num_elements = 5;\n    std::vector<CkArrayIndex> even_elements{};\n    std::vector<CkArrayIndex> odd_elements{};\n    for (size_t i = 0; i < num_elements; ++i) {\n      (i % 2 == 0 ? even_elements : odd_elements)\n          .push_back(Parallel::ArrayIndex<int>(static_cast<int>(i)));\n    }\n    std::vector<CkArrayIndex> first_element{};\n    first_element.push_back(Parallel::ArrayIndex<int>(0));\n    using EvenOrOddSection = Parallel::Section<ArrayComponent, EvenOrOddTag>;\n    const EvenOrOddSection even_section{\n        EvenOrOdd::Even, EvenOrOddSection::cproxy_section::ckNew(\n                             array_proxy.ckGetArrayID(), even_elements.data(),\n                             even_elements.size())};\n    const EvenOrOddSection odd_section{\n        EvenOrOdd::Odd, EvenOrOddSection::cproxy_section::ckNew(\n                            array_proxy.ckGetArrayID(), odd_elements.data(),\n                            odd_elements.size())};\n    using IsFirstElementSection =\n        Parallel::Section<ArrayComponent, IsFirstElementTag>;\n    const IsFirstElementSection is_first_element_section{\n        true, IsFirstElementSection::cproxy_section::ckNew(\n                  array_proxy.ckGetArrayID(), first_element.data(),\n                  first_element.size())};\n\n    // Create array elements, copying the appropriate section proxy into their\n    // DataBox\n    const size_t number_of_procs = static_cast<size_t>(sys::number_of_procs());\n    size_t which_proc = 0;\n    for (size_t i = 0; i < 5; ++i) {\n      tuples::get<Parallel::Tags::Section<ArrayComponent, EvenOrOddTag>>(\n          initialization_items) = i % 2 == 0 ? even_section : odd_section;\n      tuples::get<Parallel::Tags::Section<ArrayComponent, IsFirstElementTag>>(\n          initialization_items) =\n          i == 0 ? std::make_optional(is_first_element_section) : std::nullopt;\n      while (procs_to_ignore.find(which_proc) != procs_to_ignore.end()) {\n        which_proc = which_proc + 1 == number_of_procs ? 0 : which_proc + 1;\n      }\n      array_proxy[static_cast<int>(i)].insert(global_cache,\n                                              initialization_items, which_proc);\n      which_proc = which_proc + 1 == number_of_procs ? 0 : which_proc + 1;\n    }\n    array_proxy.doneInserting();\n  }\n  // [sections_example]\n\n  static void execute_next_phase(\n      const Parallel::Phase next_phase,\n      const Parallel::CProxy_GlobalCache<Metavariables>& global_cache) {\n    auto& local_cache = *Parallel::local_branch(global_cache);\n    Parallel::get_parallel_component<ArrayComponent>(local_cache)\n        .start_phase(next_phase);\n  }\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<ArrayComponent<Metavariables>>;\n\n  static constexpr Options::String help = \"Test section reductions\";\n\n  static constexpr std::array<Parallel::Phase, 3> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Testing,\n       Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_StaticSpscQueue.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Parallel/StaticSpscQueue.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.StaticSpscQueue\", \"[Unit][Parallel]\") {\n  // We can only test basic functionality, it's difficult to test proper\n  // threadsafety since that requires generating a race condition.\n  Parallel::StaticSpscQueue<int, 5> queue{};\n  CHECK(queue.empty());\n  CHECK(queue.size() == 0);  // NOLINT\n  CHECK(queue.capacity() == 5);\n  queue.emplace(3);\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 1);\n  CHECK(queue.capacity() == 5);\n  queue.push(5);\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 2);\n  CHECK(queue.capacity() == 5);\n  const int a = 7;\n  queue.push(a);\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 3);\n  CHECK(queue.capacity() == 5);\n\n  CHECK(queue.try_emplace(11));\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 4);\n  CHECK(queue.capacity() == 5);\n\n  CHECK(queue.try_push(15));\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 5);\n  CHECK(queue.capacity() == 5);\n\n  CHECK_FALSE(queue.try_push(a));\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 5);\n  CHECK(queue.capacity() == 5);\n\n  CHECK_FALSE(queue.try_push(19));\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 5);\n  CHECK(queue.capacity() == 5);\n\n  CHECK_FALSE(queue.try_emplace(21));\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 5);\n  CHECK(queue.capacity() == 5);\n\n  int* front = queue.front();\n  REQUIRE(front != nullptr);\n  REQUIRE(*front == 3);\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 5);\n  CHECK(queue.capacity() == 5);\n  queue.pop();\n  CHECK(queue.size() == 4);\n\n  front = queue.front();\n  REQUIRE(front != nullptr);\n  REQUIRE(*front == 5);\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 4);\n  CHECK(queue.capacity() == 5);\n  queue.pop();\n  CHECK(queue.size() == 3);\n\n  front = queue.front();\n  REQUIRE(front != nullptr);\n  REQUIRE(*front == 7);\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 3);\n  CHECK(queue.capacity() == 5);\n  queue.pop();\n  CHECK(queue.size() == 2);\n\n  front = queue.front();\n  REQUIRE(front != nullptr);\n  REQUIRE(*front == 11);\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 2);\n  CHECK(queue.capacity() == 5);\n  queue.pop();\n  CHECK(queue.size() == 1);\n\n  front = queue.front();\n  REQUIRE(front != nullptr);\n  REQUIRE(*front == 15);\n  CHECK_FALSE(queue.empty());\n  CHECK(queue.size() == 1);\n  CHECK(queue.capacity() == 5);\n  queue.pop();\n  CHECK(queue.empty());\n\n  front = queue.front();\n  REQUIRE(front == nullptr);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(queue.pop(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't pop an element from an empty queue.\"));\n\n#endif  // SPECTRE_DEBUG\n}\n"
  },
  {
    "path": "tests/Unit/Parallel/Test_TypeTraits.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <type_traits>\n\n#include \"Parallel/Algorithms/AlgorithmArray.hpp\"\n#include \"Parallel/Algorithms/AlgorithmGroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmNodegroup.hpp\"\n#include \"Parallel/Algorithms/AlgorithmSingleton.hpp\"\n#include \"Parallel/TypeTraits.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace {\nclass PupableClass {\n public:\n  void pup(PUP::er&) {}  // NOLINT\n};\n\nclass NonpupableClass {};\n\nstruct Metavariables {\n  enum class Phase { Initialization, Exit };\n  using component_list = tmpl::list<>;\n};\n\nstruct SingletonParallelComponent {\n  using metavariables = Metavariables;\n  using simple_tags_from_options = tmpl::list<>;\n  using chare_type = Parallel::Algorithms::Singleton;\n};\nstruct ArrayParallelComponent {\n  using metavariables = Metavariables;\n  using simple_tags_from_options = tmpl::list<>;\n  using chare_type = Parallel::Algorithms::Array;\n  using array_index = int;\n};\nstruct GroupParallelComponent {\n  using metavariables = Metavariables;\n  using simple_tags_from_options = tmpl::list<>;\n  using chare_type = Parallel::Algorithms::Group;\n};\nstruct NodegroupParallelComponent {\n  using metavariables = Metavariables;\n  using simple_tags_from_options = tmpl::list<>;\n  using chare_type = Parallel::Algorithms::Nodegroup;\n};\n\nusing singleton_proxy =\n    CProxy_AlgorithmSingleton<SingletonParallelComponent, int>;\nusing array_proxy = CProxy_AlgorithmArray<ArrayParallelComponent, int>;\nusing array_element_proxy =\n    CProxyElement_AlgorithmArray<ArrayParallelComponent, int>;\nusing group_proxy = CProxy_AlgorithmGroup<GroupParallelComponent, int>;\nusing nodegroup_proxy =\n    CProxy_AlgorithmNodegroup<NodegroupParallelComponent, int>;\n}  // namespace\n\nstatic_assert(Parallel::is_array_proxy<singleton_proxy>::value);\nstatic_assert(Parallel::is_array_proxy<array_proxy>::value);\nstatic_assert(not Parallel::is_array_proxy<array_element_proxy>::value);\nstatic_assert(not Parallel::is_array_proxy<group_proxy>::value);\nstatic_assert(not Parallel::is_array_proxy<nodegroup_proxy>::value);\n\nstatic_assert(not Parallel::is_array_element_proxy<singleton_proxy>::value);\nstatic_assert(not Parallel::is_array_element_proxy<array_proxy>::value);\nstatic_assert(Parallel::is_array_element_proxy<array_element_proxy>::value);\nstatic_assert(not Parallel::is_array_element_proxy<group_proxy>::value);\nstatic_assert(not Parallel::is_array_element_proxy<nodegroup_proxy>::value);\n\nstatic_assert(not Parallel::is_group_proxy<singleton_proxy>::value);\nstatic_assert(not Parallel::is_group_proxy<array_proxy>::value);\nstatic_assert(not Parallel::is_group_proxy<array_element_proxy>::value);\nstatic_assert(Parallel::is_group_proxy<group_proxy>::value);\nstatic_assert(not Parallel::is_group_proxy<nodegroup_proxy>::value);\n\nstatic_assert(not Parallel::is_node_group_proxy<singleton_proxy>::value);\nstatic_assert(not Parallel::is_node_group_proxy<array_proxy>::value);\nstatic_assert(not Parallel::is_node_group_proxy<array_element_proxy>::value);\nstatic_assert(not Parallel::is_node_group_proxy<group_proxy>::value);\nstatic_assert(Parallel::is_node_group_proxy<nodegroup_proxy>::value);\n\n// [has_pup_member_example]\nstatic_assert(Parallel::has_pup_member<PupableClass>::value);\nstatic_assert(Parallel::has_pup_member_t<PupableClass>::value);\nstatic_assert(Parallel::has_pup_member_v<PupableClass>);\nstatic_assert(not Parallel::has_pup_member<NonpupableClass>::value);\n// [has_pup_member_example]\n\n// [is_pupable_example]\nstatic_assert(Parallel::is_pupable<PupableClass>::value);\nstatic_assert(Parallel::is_pupable_t<PupableClass>::value);\nstatic_assert(Parallel::is_pupable_v<PupableClass>);\nstatic_assert(not Parallel::is_pupable<NonpupableClass>::value);\n// [is_pupable_example]\n\nstatic_assert(\n    std::is_same_v<\n        SingletonParallelComponent,\n        Parallel::get_parallel_component_from_proxy<singleton_proxy>::type>);\nstatic_assert(std::is_same_v<ArrayParallelComponent,\n                             Parallel::get_parallel_component_from_proxy<\n                                 array_element_proxy>::type>);\nstatic_assert(std::is_same_v<\n              GroupParallelComponent,\n              Parallel::get_parallel_component_from_proxy<group_proxy>::type>);\nstatic_assert(\n    std::is_same_v<\n        NodegroupParallelComponent,\n        Parallel::get_parallel_component_from_proxy<nodegroup_proxy>::type>);\n\nstatic_assert(Parallel::is_singleton_v<SingletonParallelComponent>);\nstatic_assert(Parallel::is_array_v<ArrayParallelComponent>);\nstatic_assert(Parallel::is_group_v<GroupParallelComponent>);\nstatic_assert(Parallel::is_nodegroup_v<NodegroupParallelComponent>);\n// This is special because it is a nodegroups, but it doesn't run the\n// Algorithm but it still has a `chare_type` type alias.\nstatic_assert(Parallel::is_nodegroup_v<Parallel::GlobalCache<Metavariables>>);\n\nstatic_assert(Parallel::is_singleton<SingletonParallelComponent>::value);\nstatic_assert(Parallel::is_array<ArrayParallelComponent>::value);\nstatic_assert(Parallel::is_group<GroupParallelComponent>::value);\nstatic_assert(Parallel::is_nodegroup<NodegroupParallelComponent>::value);\nstatic_assert(\n    Parallel::is_nodegroup<Parallel::GlobalCache<Metavariables>>::value);\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelAlgorithmsActions\")\n\nset(LIBRARY_SOURCES\n  Test_AddComputeTags.cpp\n  Test_AddSimpleTags.cpp\n  Test_Goto.cpp\n  Test_InitializeItems.cpp\n  Test_MutateApply.cpp\n  Test_RandomizeVariables.cpp\n  Test_SetData.cpp\n  Test_TerminatePhase.cpp\n  Test_UpdateMessageQueue.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainCreators\n  DomainStructure\n  FunctionsOfTime\n  Parallel\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_AddComputeTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/AddComputeTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct SomeNumber : db::SimpleTag {\n  using type = double;\n};\n\nstruct SquareNumber : db::SimpleTag {\n  using type = double;\n};\n\nstruct SquareNumberCompute : SquareNumber, db::ComputeTag {\n  static void function(const gsl::not_null<double*> result,\n                       const double some_number) {\n    *result = square(some_number);\n  }\n  using argument_tags = tmpl::list<SomeNumber>;\n  using base = SquareNumber;\n  using return_type = double;\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<SomeNumber>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<Initialization::Actions::AddComputeTags<\n                                 SquareNumberCompute>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Initialization.AddComputeTags\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  using component = Component<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component_and_initialize<component>(&runner, 0, {2.});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  for (size_t i = 0; i < 2; ++i) {\n    runner.template next_action<component>(0);\n  }\n\n  CHECK(ActionTesting::tag_is_retrievable<component, SquareNumber>(runner, 0));\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_AddSimpleTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Protocols/Mutator.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/AddSimpleTags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct SomeNumber : db::SimpleTag {\n  using type = double;\n};\n\nstruct SomeOtherNumber : db::SimpleTag {\n  using type = double;\n};\n\nstruct SquareNumber : db::SimpleTag {\n  using type = double;\n};\n\nstruct AddSomeAndOtherNumber : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<SomeNumber, SomeOtherNumber>;\n  using argument_tags = tmpl::list<>;\n\n  static void apply(const gsl::not_null<double*> some_number,\n                    const gsl::not_null<double*> some_other_number) {\n    *some_number = 2.;\n    *some_other_number = 3.;\n  }\n};\n\nstruct AddSquareNumber : tt::ConformsTo<db::protocols::Mutator> {\n  using return_tags = tmpl::list<SquareNumber>;\n  using argument_tags = tmpl::list<SomeNumber>;\n  static void apply(const gsl::not_null<double*> square_number,\n                    const double some_number) {\n    *square_number = some_number * some_number;\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Testing,\n      tmpl::list<Initialization::Actions::AddSimpleTags<\n          tmpl::list<AddSomeAndOtherNumber, AddSquareNumber>>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Initialization.AddSimpleTags\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  using component = Component<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_array_component<component>(make_not_null(&runner), {0},\n                                                    {0}, 0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  for (size_t i = 0; i < 2; ++i) {\n    runner.template next_action<component>(0);\n  }\n\n  CHECK(ActionTesting::tag_is_retrievable<component, SomeNumber>(runner, 0));\n  CHECK(\n      ActionTesting::tag_is_retrievable<component, SomeOtherNumber>(runner, 0));\n  CHECK(ActionTesting::tag_is_retrievable<component, SquareNumber>(runner, 0));\n  CHECK(ActionTesting::get_databox_tag<component, SomeNumber>(runner, 0) == 2.);\n  CHECK(ActionTesting::get_databox_tag<component, SomeOtherNumber>(runner, 0) ==\n        3.);\n  CHECK(ActionTesting::get_databox_tag<component, SquareNumber>(runner, 0) ==\n        4.);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_Goto.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Label1;\nstruct Label2;\n\nstruct Counter : db::SimpleTag {\n  using type = size_t;\n};\n\nstruct HasConverged : db::SimpleTag {\n  using type = bool;\n};\n\nstruct HasConvergedCompute : HasConverged, db::ComputeTag {\n  using argument_tags = tmpl::list<Counter>;\n  static void function(const gsl::not_null<bool*> result,\n                       const size_t counter) {\n    *result = (counter >= 2);\n  }\n  using return_type = bool;\n  using base = HasConverged;\n};\n\nstruct Increment {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<Counter>(\n        [](const gsl::not_null<size_t*> counter) { (*counter)++; },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\n// [component]\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using repeat_until_phase_action_list = tmpl::flatten<\n      tmpl::list<Actions::RepeatUntil<HasConverged, tmpl::list<Increment>>,\n                 Parallel::Actions::TerminatePhase>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 db::AddSimpleTags<Counter>,\n                                 db::AddComputeTags<HasConvergedCompute>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<Actions::Goto<Label1>, Actions::Label<Label2>,\n                     Actions::Label<Label1>, Actions::Goto<Label2>>>,\n      Parallel::PhaseActions<Parallel::Phase::Execute,\n                             repeat_until_phase_action_list>>;\n};\n// [component]\n\n// [metavariables]\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n\n};\n// [metavariables]\n}  // namespace\n\n// [test case]\nSPECTRE_TEST_CASE(\"Unit.Parallel.GotoAction\", \"[Unit][Parallel][Actions]\") {\n  using component = Component<Metavariables>;\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\n  MockRuntimeSystem runner{{}};\n  ActionTesting::emplace_component_and_initialize<component>(&runner, 0,\n                                                             {size_t{0}});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  runner.force_next_action_to_be<component, Actions::Label<Label1>>(0);\n  runner.next_action<component>(0);\n  CHECK(runner.get_next_action_index<component>(0) == 3);\n\n  runner.force_next_action_to_be<component, Actions::Goto<Label1>>(0);\n  runner.next_action<component>(0);\n  CHECK(runner.get_next_action_index<component>(0) == 2);\n\n  runner.force_next_action_to_be<component, Actions::Goto<Label2>>(0);\n  runner.next_action<component>(0);\n  CHECK(runner.get_next_action_index<component>(0) == 1);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Execute);\n  while (not ActionTesting::get_terminate<component>(runner, 0)) {\n    runner.next_action<component>(0);\n  }\n  CHECK(ActionTesting::get_databox_tag<component, HasConverged>(runner, 0));\n  CHECK(ActionTesting::get_databox_tag<component, Counter>(runner, 0) == 2);\n\n  // Test zero iterations of the `RepeatUntil` loop\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Execute);\n  runner.next_action<component>(0);\n  CHECK(runner.get_next_action_index<component>(0) ==\n        tmpl::index_of<typename component::repeat_until_phase_action_list,\n                       Parallel::Actions::TerminatePhase>::value);\n  // Make sure `Increment` was not called for this situation where the\n  // condition is already fulfilled at the start.\n  CHECK(ActionTesting::get_databox_tag<component, Counter>(runner, 0) == 2);\n}\n// [test case]\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_InitializeItems.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TagOne : db::SimpleTag {\n  using type = int;\n};\n\nstruct TagTwo : db::SimpleTag {\n  using type = std::string;\n};\n\nstruct TagThree : db::SimpleTag {\n  using type = double;\n};\n\nstruct Two : db::SimpleTag {\n  using type = double;\n};\n\nstruct Three : db::SimpleTag {\n  using type = double;\n};\n\nstruct Five : db::SimpleTag {\n  using type = double;\n};\n\nstruct Ten : db::SimpleTag {\n  using type = double;\n};\n\nstruct FiveCompute : Five, db::ComputeTag {\n  using return_type = double;\n  using base = Five;\n  using argument_tags = tmpl::list<Two, Three>;\n  static void function(const gsl::not_null<double*> result, const double two,\n                       const double three) {\n    *result = two + three;\n  }\n};\n\nstruct Duplicate : db::SimpleTag {\n  using type = int;\n};\n\nstruct DuplicateConstGlobalCache : db::SimpleTag {\n  using type = int;\n};\n\nstruct DuplicateMutableGlobalCache : db::SimpleTag {\n  using type = int;\n};\n\nstruct InitializeThree {\n  using const_global_cache_tags = tmpl::list<TagOne, DuplicateConstGlobalCache>;\n  using mutable_global_cache_tags =\n      tmpl::list<TagTwo, DuplicateMutableGlobalCache>;\n  using simple_tags_from_options = tmpl::list<>;\n  using simple_tags = tmpl::list<Three, Duplicate>;\n  using compute_tags = tmpl::list<>;\n  using return_tags = tmpl::list<Three>;\n  using argument_tags = tmpl::list<>;\n\n  static void apply(const gsl::not_null<double*> three) { *three = 3.; }\n};\n\nstruct InitializeTwo {\n  using const_global_cache_tags =\n      tmpl::list<TagThree, DuplicateConstGlobalCache>;\n  using mutable_global_cache_tags = tmpl::list<DuplicateMutableGlobalCache>;\n  using simple_tags_from_options = tmpl::list<Two>;\n  using simple_tags = tmpl::list<Duplicate>;\n  using compute_tags = tmpl::list<FiveCompute>;\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n  static void apply() {}\n};\n\nstruct InitializeTen {\n  using const_global_cache_tags = tmpl::list<DuplicateConstGlobalCache>;\n  using mutable_global_cache_tags = tmpl::list<DuplicateMutableGlobalCache>;\n  using simple_tags_from_options = tmpl::list<Two>;\n  using simple_tags = tmpl::list<Ten, Duplicate>;\n  using compute_tags = tmpl::list<FiveCompute>;\n  using return_tags = tmpl::list<Ten>;\n  using argument_tags = tmpl::list<Two, Five>;\n\n  static void apply(const gsl::not_null<double*> ten, const double two,\n                    const double five) {\n    *ten = two * five;\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using initialization_action =\n      Initialization::Actions::InitializeItems<InitializeTwo, InitializeThree,\n                                               InitializeTen>;\n\n  using phase_dependent_action_list =\n      tmpl::list<Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                        tmpl::list<initialization_action>>>;\n\n  static_assert(\n      std::is_same_v<\n          tmpl::count_if<initialization_action::const_global_cache_tags,\n                         std::is_same<tmpl::_1, DuplicateConstGlobalCache>>,\n          tmpl::size_t<1>>);\n  static_assert(\n      std::is_same_v<\n          tmpl::count_if<initialization_action::mutable_global_cache_tags,\n                         std::is_same<tmpl::_1, DuplicateMutableGlobalCache>>,\n          tmpl::size_t<1>>);\n  static_assert(std::is_same_v<\n                tmpl::count_if<initialization_action::simple_tags_from_options,\n                               std::is_same<tmpl::_1, Two>>,\n                tmpl::size_t<1>>);\n  static_assert(\n      std::is_same_v<tmpl::count_if<initialization_action::simple_tags,\n                                    std::is_same<tmpl::_1, Duplicate>>,\n                     tmpl::size_t<1>>);\n  static_assert(\n      std::is_same_v<tmpl::count_if<initialization_action::compute_tags,\n                                    std::is_same<tmpl::_1, FiveCompute>>,\n                     tmpl::size_t<1>>);\n\n  using simple_tags_from_options = Parallel::get_simple_tags_from_options<\n      Parallel::get_initialization_actions_list<phase_dependent_action_list>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Actions.InitializeItems\",\n\n                  \"[Unit][ParallelAlgorithms]\") {\n  using component = Component<Metavariables>;\n\n  tuples::TaggedTuple<TagOne, TagThree, DuplicateConstGlobalCache>\n      const_cache_items{7, -4., 8.};\n  tuples::TaggedTuple<TagTwo, DuplicateMutableGlobalCache> mutable_cache_items{\n      \"bla\", 9.};\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{const_cache_items,\n                                                         mutable_cache_items};\n  ActionTesting::emplace_array_component<component>(make_not_null(&runner), {0},\n                                                    {0}, 0, 2.);\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  for (size_t i = 0; i < 1; ++i) {\n    runner.template next_action<component>(0);\n  }\n\n  CHECK(ActionTesting::tag_is_retrievable<component, TagOne>(runner, 0));\n  CHECK(ActionTesting::tag_is_retrievable<component, TagThree>(runner, 0));\n  CHECK(ActionTesting::get_databox_tag<component, Two>(runner, 0) == 2.);\n  CHECK(ActionTesting::get_databox_tag<component, Three>(runner, 0) == 3.);\n  CHECK(ActionTesting::get_databox_tag<component, Ten>(runner, 0) == 10.);\n  CHECK(ActionTesting::get_databox_tag<component, Five>(runner, 0) == 5.);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_MutateApply.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct SomeNumber : db::SimpleTag {\n  using type = int;\n};\n\nstruct TestValue : db::SimpleTag {\n  using type = int;\n};\n\nstruct AddTheNumber {\n  using argument_tags = tmpl::list<SomeNumber>;\n  using return_tags = tmpl::list<TestValue>;\n  static void apply(const gsl::not_null<int*> test_value,\n                    const int& some_number) {\n    *test_value += some_number;\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 tmpl::list<TestValue, SomeNumber>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<::Actions::MutateApply<AddTheNumber>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Actions.MutateApply\", \"[Unit][Actions]\") {\n  using component = Component<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component_and_initialize<component>(&runner, 0,\n                                                             {1, 3});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_databox_tag<component, TestValue>(runner, 0) == 4);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_RandomizeVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/RandomizeVariables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nusing VariablesTag = ::Tags::Variables<tmpl::list<ScalarFieldTag>>;\n\nstruct RandomizeVariables {};\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n  using const_global_cache_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              tmpl::list<::Tags::Variables<tmpl::list<ScalarFieldTag>>>>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<Actions::RandomizeVariables<\n                                 VariablesTag, RandomizeVariables>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<ElementArray<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<>;\n};\n\nvoid test_randomize_variables(\n    std::optional<typename Actions::RandomizeVariables<\n        VariablesTag, RandomizeVariables>::RandomParameters>\n        params) {\n  const DataVector used_for_size{5};\n\n  // Which element we work with does not matter for this test\n  const ElementId<1> element_id{0, {{SegmentId{2, 1}}}};\n  // Generate some random field data\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> dist(-1., 1.);\n  const auto fields =\n      make_with_random_values<Variables<tmpl::list<ScalarFieldTag>>>(\n          make_not_null(&generator), make_not_null(&dist), used_for_size);\n\n  using element_array = ElementArray<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{params}};\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      &runner, element_id, {fields});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  const auto get_tag = [&runner, &element_id ](auto tag_v) -> const auto& {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n\n  const DataVector& fields_randomized = get(get_tag(ScalarFieldTag{}));\n  const DataVector& fields_original = get(get<ScalarFieldTag>(fields));\n  if (params.has_value()) {\n    // Test that the fields have changed, but differ only by the `amplitude`\n    CHECK(fields_randomized != fields_original);\n    for (size_t i = 0; i < fields.size(); ++i) {\n      CHECK(abs(fields_randomized[i] - fields_original[i]) <=\n            params.value().amplitude);\n    }\n  } else {\n    CHECK(fields_randomized == fields_original);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Actions.RandomizeVariables\",\n                  \"[Unit][ParallelAlgorithms][Actions]\") {\n  test_randomize_variables({{1.e-2, std::nullopt}});\n  test_randomize_variables(std::nullopt);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_SetData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/SetData.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct SomeNumber : db::SimpleTag {\n  using type = int;\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<SomeNumber>>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Actions.SetData\", \"[Unit][Actions]\") {\n  using component = Component<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component_and_initialize<component>(&runner, 0, {0});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  ActionTesting::simple_action<component,\n                               Actions::SetData<tmpl::list<SomeNumber>>>(\n      make_not_null(&runner), 0, tuples::TaggedTuple<SomeNumber>{3});\n  CHECK(ActionTesting::get_databox_tag<component, SomeNumber>(runner, 0) == 3);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_TerminatePhase.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// [component]\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Testing, tmpl::list<Parallel::Actions::TerminatePhase>>>;\n};\n// [component]\n\n// [metavariables]\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n// [metavariables]\n}  // namespace\n\n// [test case]\nSPECTRE_TEST_CASE(\"Unit.Parallel.Actions.TerminatePhase\",\n                  \"[Unit][Parallel][Actions]\") {\n  using component = Component<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component<component>(&runner, 0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  CHECK_FALSE(ActionTesting::get_terminate<component>(runner, 0));\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  CHECK(ActionTesting::get_terminate<component>(runner, 0));\n}\n// [test case]\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Actions/Test_UpdateMessageQueue.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/LinkedMessageQueue.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/UpdateMessageQueue.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavaiables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace {\nstruct Queue1 {\n  using type = double;\n};\n\nstruct Queue2 {\n  using type = double;\n};\n\nstruct LinkedMessageQueueTag : db::SimpleTag {\n  using type = LinkedMessageQueue<int, tmpl::list<Queue1, Queue2>>;\n};\n\nstruct ProcessorCalls : db::SimpleTag {\n  using type = std::vector<std::pair<int, tuples::TaggedTuple<Queue1, Queue2>>>;\n};\n\nstruct Processor {\n  // [Processor::apply]\n  template <typename DbTags, typename Metavariables, typename ArrayIndex>\n  static void apply(const gsl::not_null<db::DataBox<DbTags>*> box,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/, const int id,\n                    tuples::TaggedTuple<Queue1, Queue2> data) {\n    // [Processor::apply]\n    db::mutate<ProcessorCalls>(\n        [&id, &data](const gsl::not_null<ProcessorCalls::type*> calls) {\n          calls->emplace_back(id, std::move(data));\n        },\n        box);\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using simple_tags_from_options =\n      tmpl::list<LinkedMessageQueueTag, ProcessorCalls>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nusing FunctionMap = domain::Tags::FunctionsOfTimeInitialize::type;\nstruct UpdateFoT {\n  static void apply(const gsl::not_null<FunctionMap*> functions,\n                    const std::string& name, const double expiration) {\n    const double current_expiration = functions->at(name)->time_bounds()[1];\n    // Update value doesn't matter\n    (*functions)\n        .at(name)\n        ->update(current_expiration, DataVector{0.0}, expiration);\n  }\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Actions.UpdateMessageQueue\", \"[Unit][Actions]\") {\n  using component = Component<Metavariables>;\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  FunctionMap functions_of_time{};\n  const std::string name{\"Smaug\"};\n  functions_of_time[name] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<0>>(\n          0.0, std::array{DataVector{0.0}}, 1.0);\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {}, {std::move(functions_of_time)}};\n  ActionTesting::emplace_component<component>(\n      &runner, 0, LinkedMessageQueueTag::type{}, ProcessorCalls::type{});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& cache = ActionTesting::cache<component>(runner, 0);\n\n  const auto processed_by_call = [&runner](auto queue_v,\n                                           const LinkedMessageId<int>& id,\n                                           auto data) -> decltype(auto) {\n    ActionTesting::simple_action<\n        component, Actions::UpdateMessageQueue<LinkedMessageQueueTag, Processor,\n                                               decltype(queue_v)>>(\n        make_not_null(&runner), 0, id, std::move(data));\n    return db::mutate<ProcessorCalls>(\n        [](const gsl::not_null<ProcessorCalls::type*> calls) {\n          auto ret = std::move(*calls);\n          calls->clear();\n          return ret;\n        },\n        make_not_null(\n            &ActionTesting::get_databox<component>(make_not_null(&runner), 0)));\n  };\n\n  {\n    // Test two tags at once\n    ActionTesting::simple_action<\n        component, Actions::UpdateMessageQueue<LinkedMessageQueueTag, Processor,\n                                               Queue1, Queue2>>(\n        make_not_null(&runner), 0, LinkedMessageId<int>{0, {}}, 1.23, 2.34);\n    const auto processed = db::mutate<ProcessorCalls>(\n        [](const gsl::not_null<ProcessorCalls::type*> calls) {\n          auto ret = std::move(*calls);\n          calls->clear();\n          return ret;\n        },\n        make_not_null(\n            &ActionTesting::get_databox<component>(make_not_null(&runner), 0)));\n    CHECK(processed.size() == 1);\n\n    CHECK(processed[0].first == 0);\n    CHECK(get<Queue1>(processed[0].second) == 1.23);\n    CHECK(get<Queue2>(processed[0].second) == 2.34);\n  }\n  // Nothing should have been inserted because 2 is after expiration of 1.\n  CHECK(processed_by_call(Queue1{}, {2, 1}, 2.2).empty());\n  Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(cache, name, 5.0);\n  CHECK(ActionTesting::number_of_queued_simple_actions<component>(runner, 0) ==\n        1);\n  // Now things should have been inserted\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n  CHECK(ActionTesting::get_databox_tag<component, ProcessorCalls>(runner, 0)\n            .empty());\n  CHECK(processed_by_call(Queue2{}, {1, 0}, 1.1).empty());\n  CHECK(processed_by_call(Queue2{}, {2, 1}, 2.2).empty());\n  {\n    const auto processed = processed_by_call(Queue1{}, {1, 0}, 1.1);\n    CHECK(processed.size() == 2);\n\n    CHECK(processed[0].first == 1);\n    CHECK(get<Queue1>(processed[0].second) == 1.1);\n    CHECK(get<Queue2>(processed[0].second) == 1.1);\n\n    CHECK(processed[1].first == 2);\n    CHECK(get<Queue1>(processed[1].second) == 2.2);\n    CHECK(get<Queue2>(processed[1].second) == 2.2);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_AdjustDomain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <deque>\n#include <pup.h>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/AdjustDomain.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateParent.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct MockCreateParent {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ElementProxy>\n  static void apply(\n      DataBox& /*box*/, Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const int /*array_index*/, ElementProxy /*element_proxy*/,\n      ElementId<Metavariables::volume_dim> parent_id,\n      const ElementId<Metavariables::volume_dim>& child_id,\n      std::deque<ElementId<Metavariables::volume_dim>> sibling_ids_to_collect,\n      const std::unordered_map<Parallel::Phase, size_t>&\n          child_phase_bookmarks) {\n    CHECK(parent_id == ElementId<1>{0, std::array{SegmentId{2, 0}}});\n    CHECK(child_id == ElementId<1>{0, std::array{SegmentId{3, 0}}});\n    CHECK(sibling_ids_to_collect ==\n          std::deque{ElementId<1>{0, std::array{SegmentId{3, 1}}}});\n    CHECK(child_phase_bookmarks.empty());\n  }\n};\n\nstruct MockCreateChild {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename ElementProxy>\n  static void apply(\n      DataBox& /*box*/, Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const int /*array_index*/, ElementProxy /*element_proxy*/,\n      ElementId<Metavariables::volume_dim> parent_id,\n      std::vector<ElementId<Metavariables::volume_dim>> children_ids,\n      const size_t index_of_child_id,\n      const std::unordered_map<Parallel::Phase, size_t>&\n          parent_phase_bookmarks) {\n    if constexpr (Metavariables::amr::keep_coarse_grids) {\n      if (parent_id == ElementId<1>{0, std::array{SegmentId{3, 0}}}) {\n        // Element 1 does nothing\n        CHECK(children_ids ==\n              std::vector{ElementId<1>{0, std::array{SegmentId{3, 0}}, 1}});\n      } else if (parent_id == ElementId<1>{0, std::array{SegmentId{3, 1}}}) {\n        // Element 2 does nothing\n        CHECK(children_ids ==\n              std::vector{ElementId<1>{0, std::array{SegmentId{3, 1}}, 1}});\n      } else if (parent_id == ElementId<1>{0, std::array{SegmentId{2, 1}}}) {\n        // Element 3 increases resolution\n        CHECK(children_ids ==\n              std::vector{ElementId<1>{0, std::array{SegmentId{2, 1}}, 1}});\n      } else if (parent_id == ElementId<1>{0, std::array{SegmentId{1, 1}}}) {\n        // Element 4 splits\n        CHECK(children_ids ==\n              std::vector{ElementId<1>{0, std::array{SegmentId{2, 2}}, 1},\n                          ElementId<1>{0, std::array{SegmentId{2, 3}}, 1}});\n      } else {\n        ERROR(\"Unexpected parent id\");\n      }\n    } else {\n      CHECK(parent_id == ElementId<1>{0, std::array{SegmentId{1, 1}}});\n      if (index_of_child_id == 0) {\n        CHECK(children_ids ==\n              std::vector{ElementId<1>{0, std::array{SegmentId{2, 2}}},\n                          ElementId<1>{0, std::array{SegmentId{2, 3}}}});\n      } else {\n        CHECK(index_of_child_id == 1);\n        CHECK(children_ids ==\n              std::vector{ElementId<1>{0, std::array{SegmentId{2, 2}}},\n                          ElementId<1>{0, std::array{SegmentId{2, 3}}}});\n      }\n    }\n    CHECK(parent_phase_bookmarks.empty());\n  }\n};\n\ntemplate <typename Metavariables>\nstruct ArrayComponent {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags =\n      tmpl::list<logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>;\n  using simple_tags = tmpl::list<\n      domain::Tags::Element<volume_dim>, domain::Tags::Mesh<volume_dim>,\n      domain::Tags::NeighborMesh<volume_dim>, amr::Tags::Info<volume_dim>,\n      amr::Tags::NeighborInfo<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct SingletonComponent {\n  using metavariables = Metavariables;\n  using array_index = int;\n\n  using component_being_mocked = amr::Component<Metavariables>;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n  using replace_these_simple_actions =\n      tmpl::list<amr::Actions::CreateChild, amr::Actions::CreateParent>;\n  using with_these_simple_actions =\n      tmpl::list<MockCreateChild, MockCreateParent>;\n};\n\ntemplate <bool KeepCoarseGrids>\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<ArrayComponent<Metavariables>,\n                                    SingletonComponent<Metavariables>>;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using projectors = tmpl::list<::amr::projectors::DefaultInitialize<\n        Parallel::Tags::GlobalCache<Metavariables>,\n        domain::Tags::NeighborMesh<1>>>;\n    static constexpr bool keep_coarse_grids = KeepCoarseGrids;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n};\n\ntemplate <typename Metavariables>\nvoid check_box(const ActionTesting::MockRuntimeSystem<Metavariables>& runner,\n               const ElementId<1>& element_id,\n               const Element<1>& expected_element, const Mesh<1>& expected_mesh,\n               const DirectionalIdMap<1, Mesh<1>>& expected_neighbor_meshes,\n               const amr::Info<1>& expected_info,\n               const std::unordered_map<ElementId<1>, amr::Info<1>>&\n                   expected_neighbor_info) {\n  CAPTURE(element_id);\n  using array_component = ArrayComponent<Metavariables>;\n  CHECK(\n      ActionTesting::get_databox_tag<array_component, domain::Tags::Element<1>>(\n          runner, element_id) == expected_element);\n  CHECK(ActionTesting::get_databox_tag<array_component, domain::Tags::Mesh<1>>(\n            runner, element_id) == expected_mesh);\n  CHECK(ActionTesting::get_databox_tag<array_component, amr::Tags::Info<1>>(\n            runner, element_id) == expected_info);\n  CHECK(ActionTesting::get_databox_tag<array_component,\n                                       amr::Tags::NeighborInfo<1>>(\n            runner, element_id) == expected_neighbor_info);\n  const auto& neighbor_meshes =\n      ActionTesting::get_databox_tag<array_component,\n                                     domain::Tags::NeighborMesh<1>>(runner,\n                                                                    element_id);\n  for (const auto& [direction_id, mesh] : neighbor_meshes) {\n    CHECK(mesh == expected_neighbor_meshes.at(direction_id));\n  }\n}\n\ntemplate <bool KeepCoarseGrids>\nvoid test() {\n  using metavariables = Metavariables<KeepCoarseGrids>;\n  using array_component = ArrayComponent<metavariables>;\n  using singleton_component = SingletonComponent<metavariables>;\n\n  const OrientationMap<1> aligned = OrientationMap<1>::create_aligned();\n\n  const ElementId<1> element_1_id{0, std::array{SegmentId{3, 0}}};\n  const ElementId<1> element_2_id{0, std::array{SegmentId{3, 1}}};\n  const ElementId<1> element_3_id{0, std::array{SegmentId{2, 1}}};\n  const ElementId<1> element_4_id{0, std::array{SegmentId{1, 1}}};\n\n  const Element<1> element_1{\n      element_1_id,\n      DirectionMap<1, Neighbors<1>>{\n          {Direction<1>::upper_xi(),\n           Neighbors<1>{std::unordered_set{element_2_id}, aligned}}}};\n  const Element<1> element_2{\n      element_2_id,\n      DirectionMap<1, Neighbors<1>>{\n          {Direction<1>::lower_xi(),\n           Neighbors<1>{std::unordered_set{element_1_id}, aligned}},\n          {Direction<1>::upper_xi(),\n           Neighbors<1>{std::unordered_set{element_3_id}, aligned}}}};\n  const Element<1> element_3{\n      element_3_id,\n      DirectionMap<1, Neighbors<1>>{\n          {Direction<1>::lower_xi(),\n           Neighbors<1>{std::unordered_set{element_2_id}, aligned}},\n          {Direction<1>::upper_xi(),\n           Neighbors<1>{std::unordered_set{element_4_id}, aligned}}}};\n  const Element<1> element_4{\n      element_4_id,\n      DirectionMap<1, Neighbors<1>>{\n          {Direction<1>::lower_xi(),\n           Neighbors<1>{std::unordered_set{element_3_id}, aligned}}}};\n\n  const Mesh<1> element_1_mesh{std::array{3_st}, Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> element_2_mesh{std::array{4_st}, Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> element_3_mesh{std::array{5_st}, Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> element_4_mesh{std::array{7_st}, Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> element_3_mesh_post_refinement{\n      std::array{6_st}, Spectral::Basis::Legendre,\n      Spectral::Quadrature::GaussLobatto};\n\n  amr::Info<1> element_1_info{{amr::Flag::Join}, element_2_mesh};\n  amr::Info<1> element_2_info{{amr::Flag::Join}, element_2_mesh};\n  amr::Info<1> element_3_info{{amr::Flag::IncreaseResolution},\n                              element_3_mesh_post_refinement};\n  amr::Info<1> element_4_info{{amr::Flag::Split}, element_4_mesh};\n\n  if constexpr (KeepCoarseGrids) {\n    // Disable coarsening\n    const amr::Policies policies{amr::Isotropy::Anisotropic, amr::Limits{},\n                                 true, false};\n    amr::enforce_policies(make_not_null(&element_1_info.flags), policies,\n                          element_1_id, element_1_mesh);\n    amr::enforce_policies(make_not_null(&element_2_info.flags), policies,\n                          element_2_id, element_2_mesh);\n    amr::enforce_policies(make_not_null(&element_3_info.flags), policies,\n                          element_3_id, element_3_mesh);\n    amr::enforce_policies(make_not_null(&element_4_info.flags), policies,\n                          element_4_id, element_4_mesh);\n  }\n\n  std::unordered_map<ElementId<1>, amr::Info<1>> element_1_neighbor_info{\n      {element_2_id, element_2_info}};\n  std::unordered_map<ElementId<1>, amr::Info<1>> element_2_neighbor_info{\n      {element_1_id, element_1_info}, {element_3_id, element_3_info}};\n  std::unordered_map<ElementId<1>, amr::Info<1>> element_3_neighbor_info{\n      {element_2_id, element_2_info}, {element_4_id, element_4_info}};\n  std::unordered_map<ElementId<1>, amr::Info<1>> element_4_neighbor_info{\n      {element_3_id, element_3_info}};\n\n  using NeighborMeshes = DirectionalIdMap<1, Mesh<1>>;\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{::Verbosity::Debug}};\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, element_1_id,\n      {element_1, element_1_mesh, NeighborMeshes{}, element_1_info,\n       element_1_neighbor_info});\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, element_2_id,\n      {element_2, element_2_mesh, NeighborMeshes{}, element_2_info,\n       element_2_neighbor_info});\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, element_3_id,\n      {element_3, element_3_mesh, NeighborMeshes{}, element_3_info,\n       element_3_neighbor_info});\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, element_4_id,\n      {element_4, element_4_mesh, NeighborMeshes{}, element_4_info,\n       element_4_neighbor_info});\n  ActionTesting::emplace_component<singleton_component>(&runner, 0);\n\n  const auto check_for_empty_queues_on_elements =\n      [&element_1_id, &element_2_id, &element_3_id, &element_4_id, &runner]() {\n        for (const auto& id : std::vector{element_1_id, element_2_id,\n                                          element_3_id, element_4_id}) {\n          CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(\n              runner, id));\n        }\n      };\n  check_for_empty_queues_on_elements();\n  CHECK(ActionTesting::number_of_queued_simple_actions<singleton_component>(\n            runner, 0) == 0);\n\n  // This should queue CreateParent on the singleton\n  // If KeepCoarseGrids is true: this should queue CreateChild\n  ActionTesting::simple_action<array_component, amr::Actions::AdjustDomain>(\n      make_not_null(&runner), element_1_id);\n  check_for_empty_queues_on_elements();\n  CHECK(ActionTesting::number_of_queued_simple_actions<singleton_component>(\n            runner, 0) == 1);\n\n  // This should do nothing\n  // If KeepCoarseGrids is true: this should queue CreateChild\n  ActionTesting::simple_action<array_component, amr::Actions::AdjustDomain>(\n      make_not_null(&runner), element_2_id);\n  check_for_empty_queues_on_elements();\n  CHECK(ActionTesting::number_of_queued_simple_actions<singleton_component>(\n            runner, 0) == (KeepCoarseGrids ? 2 : 1));\n\n  // This should p-refine element_3\n  // If KeepCoarseGrids is true: this should queue CreateChild\n  ActionTesting::simple_action<array_component, amr::Actions::AdjustDomain>(\n      make_not_null(&runner), element_3_id);\n  check_for_empty_queues_on_elements();\n  CHECK(ActionTesting::number_of_queued_simple_actions<singleton_component>(\n            runner, 0) == (KeepCoarseGrids ? 3 : 1));\n\n  // This should queue CreateChild on the singleton\n  ActionTesting::simple_action<array_component, amr::Actions::AdjustDomain>(\n      make_not_null(&runner), element_4_id);\n  check_for_empty_queues_on_elements();\n  CHECK(ActionTesting::number_of_queued_simple_actions<singleton_component>(\n            runner, 0) == (KeepCoarseGrids ? 4 : 2));\n\n  // This will invoke the queued actions on the singleton calling the mock\n  // actions\n  for (size_t i = 0; i < (KeepCoarseGrids ? 4 : 2); ++i) {\n    ActionTesting::invoke_queued_simple_action<singleton_component>(\n        make_not_null(&runner), 0);\n    check_for_empty_queues_on_elements();\n    CHECK(ActionTesting::number_of_queued_simple_actions<singleton_component>(\n              runner, 0) == (KeepCoarseGrids ? 4 : 2) - i - 1);\n  }\n\n  check_box(runner, element_1_id, element_1, element_1_mesh, NeighborMeshes{},\n            element_1_info, element_1_neighbor_info);\n  check_box(runner, element_2_id, element_2, element_2_mesh, NeighborMeshes{},\n            element_2_info, element_2_neighbor_info);\n  check_box(runner, element_4_id, element_4, element_4_mesh, NeighborMeshes{},\n            element_4_info, element_4_neighbor_info);\n  if constexpr (KeepCoarseGrids) {\n    check_box(runner, element_3_id, element_3, element_3_mesh, NeighborMeshes{},\n              element_3_info, element_3_neighbor_info);\n  } else {\n    const ElementId<1> new_parent_id{\n        ElementId<1>{0, std::array{SegmentId{2, 0}}}};\n    const ElementId<1> new_lower_child_id{\n        ElementId<1>{0, std::array{SegmentId{2, 2}}}};\n    const Element<1> element_3_post_refinement{\n        element_3_id,\n        DirectionMap<1, Neighbors<1>>{\n            {Direction<1>::lower_xi(),\n             Neighbors<1>{std::unordered_set{new_parent_id}, aligned}},\n            {Direction<1>::upper_xi(),\n             Neighbors<1>{std::unordered_set{new_lower_child_id}, aligned}}}};\n    DirectionalIdMap<1, Mesh<1>> expected_element_3_neighbor_meshes;\n    expected_element_3_neighbor_meshes.emplace(\n        std::pair{DirectionalId{Direction<1>::lower_xi(), new_parent_id},\n                  element_2_mesh});\n    expected_element_3_neighbor_meshes.emplace(\n        std::pair{DirectionalId{Direction<1>::upper_xi(), new_lower_child_id},\n                  element_4_mesh});\n    check_box(\n        runner, element_3_id, element_3_post_refinement,\n        element_3_mesh_post_refinement, expected_element_3_neighbor_meshes,\n        {std::array{amr::Flag::Undefined}, element_3_mesh_post_refinement}, {});\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.AdjustDomain\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test<false>();\n  test<true>();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_CollectDataFromChildren.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <deque>\n#include <pup.h>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/Domain/Amr/RegistrationHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nconst ElementId<2> parent_id{0, std::array{SegmentId{0, 0}, SegmentId{0, 0}}};\nconst ElementId<2> child_1_id{0, std::array{SegmentId{1, 0}, SegmentId{1, 0}}};\nconst ElementId<2> child_2_id{0, std::array{SegmentId{1, 1}, SegmentId{1, 0}}};\nconst ElementId<2> child_3_id{0, std::array{SegmentId{1, 0}, SegmentId{1, 1}}};\nconst ElementId<2> child_4_id{0, std::array{SegmentId{1, 1}, SegmentId{1, 1}}};\nconst ElementId<2> neighbor_1_id{1,\n                                 std::array{SegmentId{1, 1}, SegmentId{0, 0}}};\nconst ElementId<2> neighbor_2_id{2,\n                                 std::array{SegmentId{0, 0}, SegmentId{1, 0}}};\nconst ElementId<2> neighbor_3_id{2,\n                                 std::array{SegmentId{1, 0}, SegmentId{1, 1}}};\n\nauto child_1_mesh() {\n  return Mesh<2>{std::array{3_st, 3_st}, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\nauto child_2_mesh() {\n  return Mesh<2>{std::array{3_st, 4_st}, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\nauto child_3_mesh() {\n  return Mesh<2>{std::array{4_st, 3_st}, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\nauto child_4_mesh() {\n  return Mesh<2>{std::array{4_st, 4_st}, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\n\nauto neighbor_1_mesh() {\n  return Mesh<2>{std::array{5_st, 4_st}, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\n\nauto neighbor_2_mesh() {\n  return Mesh<2>{std::array{2_st, 3_st}, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\n\nauto neighbor_3_mesh() {\n  return Mesh<2>{std::array{3_st, 5_st}, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\n\nauto child_info() {\n  return amr::Info<2>{std::array{amr::Flag::Join, amr::Flag::Join},\n                      child_4_mesh()};\n}\n\nauto neighbor_1_info() {\n  return amr::Info<2>{std::array{amr::Flag::Join, amr::Flag::DoNothing},\n                      neighbor_1_mesh()};\n}\n\nauto neighbor_2_info() {\n  return amr::Info<2>{std::array{amr::Flag::Split, amr::Flag::DoNothing},\n                      neighbor_2_mesh()};\n}\n\nauto neighbor_3_info() {\n  return amr::Info<2>{std::array{amr::Flag::Join, amr::Flag::DoNothing},\n                      neighbor_3_mesh()};\n}\n\nElement<2> child_1() {\n  static Element<2> result{\n      child_1_id, DirectionMap<2, Neighbors<2>>{\n                      {Direction<2>::lower_xi(),\n                       Neighbors<2>{std::unordered_set{neighbor_1_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_xi(),\n                       Neighbors<2>{std::unordered_set{child_2_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::lower_eta(),\n                       Neighbors<2>{std::unordered_set{child_3_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_eta(),\n                       Neighbors<2>{std::unordered_set{child_3_id},\n                                    OrientationMap<2>::create_aligned()}}}};\n  return result;\n}\n\nElement<2> child_2() {\n  static Element<2> result{\n      child_2_id, DirectionMap<2, Neighbors<2>>{\n                      {Direction<2>::lower_xi(),\n                       Neighbors<2>{std::unordered_set{child_1_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_xi(),\n                       Neighbors<2>{std::unordered_set{neighbor_2_id},\n                                    OrientationMap<2>{\n                                        std::array{Direction<2>::lower_eta(),\n                                                   Direction<2>::upper_xi()}}}},\n                      {Direction<2>::lower_eta(),\n                       Neighbors<2>{std::unordered_set{child_4_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_eta(),\n                       Neighbors<2>{std::unordered_set{child_4_id},\n                                    OrientationMap<2>::create_aligned()}}}};\n  return result;\n}\n\nElement<2> child_3() {\n  static Element<2> result{\n      child_3_id, DirectionMap<2, Neighbors<2>>{\n                      {Direction<2>::lower_xi(),\n                       Neighbors<2>{std::unordered_set{neighbor_1_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_xi(),\n                       Neighbors<2>{std::unordered_set{child_4_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::lower_eta(),\n                       Neighbors<2>{std::unordered_set{child_1_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_eta(),\n                       Neighbors<2>{std::unordered_set{child_1_id},\n                                    OrientationMap<2>::create_aligned()}}}};\n  return result;\n}\n\nElement<2> child_4() {\n  static Element<2> result{\n      child_4_id, DirectionMap<2, Neighbors<2>>{\n                      {Direction<2>::lower_xi(),\n                       Neighbors<2>{std::unordered_set{child_3_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_xi(),\n                       Neighbors<2>{std::unordered_set{neighbor_3_id},\n                                    OrientationMap<2>{\n                                        std::array{Direction<2>::lower_eta(),\n                                                   Direction<2>::upper_xi()}}}},\n                      {Direction<2>::lower_eta(),\n                       Neighbors<2>{std::unordered_set{child_2_id},\n                                    OrientationMap<2>::create_aligned()}},\n                      {Direction<2>::upper_eta(),\n                       Neighbors<2>{std::unordered_set{child_2_id},\n                                    OrientationMap<2>::create_aligned()}}}};\n  return result;\n}\n\nauto child_1_neighbor_info() {\n  return std::unordered_map<ElementId<2>, amr::Info<2>>{\n      {neighbor_1_id, neighbor_1_info()},\n      {child_2_id, child_info()},\n      {child_3_id, child_info()}};\n}\n\nauto child_2_neighbor_info() {\n  return std::unordered_map<ElementId<2>, amr::Info<2>>{\n      {child_1_id, child_info()},\n      {child_4_id, child_info()},\n      {neighbor_2_id, neighbor_2_info()}};\n}\n\nauto child_3_neighbor_info() {\n  return std::unordered_map<ElementId<2>, amr::Info<2>>{\n      {neighbor_1_id, neighbor_1_info()},\n      {child_1_id, child_info()},\n      {child_4_id, child_info()}};\n}\nauto child_4_neighbor_info() {\n  return std::unordered_map<ElementId<2>, amr::Info<2>>{\n      {child_2_id, child_info()},\n      {child_3_id, child_info()},\n      {neighbor_3_id, neighbor_3_info()}};\n}\n\nstruct MockInitializeParent {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename... Tags>\n  static void apply(\n      DataBox& /*box*/, const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Metavariables::volume_dim>& mock_parent_id,\n      const std::unordered_map<ElementId<Metavariables::volume_dim>,\n                               tuples::TaggedTuple<Tags...>>& children_items) {\n    CHECK(mock_parent_id == parent_id);\n    const auto& child_1_items = children_items.at(child_1_id);\n    CHECK(get<domain::Tags::Element<2>>(child_1_items) == child_1());\n    CHECK(get<domain::Tags::Mesh<2>>(child_1_items) == child_1_mesh());\n    CHECK(get<amr::Tags::Info<2>>(child_1_items) == child_info());\n    CHECK(get<amr::Tags::NeighborInfo<2>>(child_1_items) ==\n          child_1_neighbor_info());\n\n    const auto& child_2_items = children_items.at(child_2_id);\n    CHECK(get<domain::Tags::Element<2>>(child_2_items) == child_2());\n    CHECK(get<domain::Tags::Mesh<2>>(child_2_items) == child_2_mesh());\n    CHECK(get<amr::Tags::Info<2>>(child_2_items) == child_info());\n    CHECK(get<amr::Tags::NeighborInfo<2>>(child_2_items) ==\n          child_2_neighbor_info());\n\n    const auto& child_3_items = children_items.at(child_3_id);\n    CHECK(get<domain::Tags::Element<2>>(child_3_items) == child_3());\n    CHECK(get<domain::Tags::Mesh<2>>(child_3_items) == child_3_mesh());\n    CHECK(get<amr::Tags::Info<2>>(child_3_items) == child_info());\n    CHECK(get<amr::Tags::NeighborInfo<2>>(child_3_items) ==\n          child_3_neighbor_info());\n\n    const auto& child_4_items = children_items.at(child_4_id);\n    CHECK(get<domain::Tags::Element<2>>(child_4_items) == child_4());\n    CHECK(get<domain::Tags::Mesh<2>>(child_4_items) == child_4_mesh());\n    CHECK(get<amr::Tags::Info<2>>(child_4_items) == child_info());\n    CHECK(get<amr::Tags::NeighborInfo<2>>(child_4_items) ==\n          child_4_neighbor_info());\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags =\n      tmpl::list<domain::Tags::Element<volume_dim>,\n                 domain::Tags::Mesh<volume_dim>, amr::Tags::Info<volume_dim>,\n                 amr::Tags::NeighborInfo<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n  using replace_these_simple_actions =\n      tmpl::list<amr::Actions::InitializeParent>;\n  using with_these_simple_actions = tmpl::list<MockInitializeParent>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 2;\n  using component_list = tmpl::list<Component<Metavariables>,\n                                    TestHelpers::amr::Registrar<Metavariables>>;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<Component<Metavariables>,\n                             tmpl::list<TestHelpers::amr::RegisterElement>>>;\n  };\n};\n\nvoid test() {\n  using array_component = Component<Metavariables>;\n  using registrar = TestHelpers::amr::Registrar<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, child_1_id,\n      {child_1(), child_1_mesh(), child_info(), child_1_neighbor_info()});\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, child_2_id,\n      {child_2(), child_2_mesh(), child_info(), child_2_neighbor_info()});\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, child_3_id,\n      {child_3(), child_3_mesh(), child_info(), child_3_neighbor_info()});\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, child_4_id,\n      {child_4(), child_4_mesh(), child_info(), child_4_neighbor_info()});\n  ActionTesting::emplace_component<array_component>(&runner, parent_id);\n  ActionTesting::emplace_group_component_and_initialize<registrar>(\n      &runner,\n      std::unordered_set{child_1_id, child_2_id, child_3_id, child_4_id});\n\n  for (const auto& id :\n       std::vector{child_1_id, child_2_id, child_3_id, child_4_id, parent_id}) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(runner,\n                                                                       id));\n  }\n\n  ActionTesting::simple_action<array_component,\n                               amr::Actions::CollectDataFromChildren>(\n      make_not_null(&runner), child_1_id, parent_id,\n      std::deque{child_2_id, child_3_id});\n  for (const auto& id :\n       std::vector{child_1_id, child_3_id, child_4_id, parent_id}) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(runner,\n                                                                       id));\n  }\n  CHECK(ActionTesting::number_of_queued_simple_actions<array_component>(\n            runner, child_2_id) == 1);\n  ActionTesting::invoke_queued_simple_action<registrar>(make_not_null(&runner),\n                                                        0);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<2>>(\n            runner, 0) ==\n        std::unordered_set{child_2_id, child_3_id, child_4_id});\n\n  ActionTesting::invoke_queued_simple_action<array_component>(\n      make_not_null(&runner), child_2_id);\n  for (const auto& id :\n       std::vector{child_1_id, child_2_id, child_4_id, parent_id}) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(runner,\n                                                                       id));\n  }\n  CHECK(ActionTesting::number_of_queued_simple_actions<array_component>(\n            runner, child_3_id) == 1);\n  ActionTesting::invoke_queued_simple_action<registrar>(make_not_null(&runner),\n                                                        0);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<2>>(\n            runner, 0) == std::unordered_set{child_3_id, child_4_id});\n\n  ActionTesting::invoke_queued_simple_action<array_component>(\n      make_not_null(&runner), child_3_id);\n  for (const auto& id :\n       std::vector{child_1_id, child_2_id, child_3_id, parent_id}) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(runner,\n                                                                       id));\n  }\n  CHECK(ActionTesting::number_of_queued_simple_actions<array_component>(\n            runner, child_4_id) == 1);\n  ActionTesting::invoke_queued_simple_action<registrar>(make_not_null(&runner),\n                                                        0);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<2>>(\n            runner, 0) == std::unordered_set{child_4_id});\n\n  ActionTesting::invoke_queued_simple_action<array_component>(\n      make_not_null(&runner), child_4_id);\n  for (const auto& id :\n       std::vector{child_1_id, child_2_id, child_3_id, child_4_id}) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(runner,\n                                                                       id));\n  }\n  CHECK(ActionTesting::number_of_queued_simple_actions<array_component>(\n            runner, parent_id) == 1);\n  ActionTesting::invoke_queued_simple_action<registrar>(make_not_null(&runner),\n                                                        0);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<2>>(\n            runner, 0) == std::unordered_set<ElementId<2>>{});\n\n  ActionTesting::invoke_queued_simple_action<array_component>(\n      make_not_null(&runner), parent_id);\n  for (const auto& id :\n       std::vector{child_1_id, child_2_id, child_3_id, child_4_id, parent_id}) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(runner,\n                                                                       id));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.CollectDataFromChildren\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_CreateChild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <pup.h>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateChild.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\n\nstruct MockSendDataToChildren {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename... Tags>\n  static void apply(DataBox& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ElementId<Metavariables::volume_dim>& /*parent_id*/,\n                    const std::vector<ElementId<Metavariables::volume_dim>>&\n                        ids_of_children) {\n    CHECK(ids_of_children ==\n          std::vector{ElementId<1>{0, std::array{SegmentId{3, 2}}},\n                      ElementId<1>{0, std::array{SegmentId{3, 3}}}});\n  }\n};\n\ntemplate <typename Metavariables>\nstruct ArrayComponent {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags =\n      tmpl::list<domain::Tags::Element<volume_dim>,\n                 domain::Tags::Mesh<volume_dim>, amr::Tags::Info<volume_dim>,\n                 amr::Tags::NeighborInfo<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n  using replace_these_simple_actions =\n      tmpl::list<amr::Actions::SendDataToChildren>;\n  using with_these_simple_actions = tmpl::list<MockSendDataToChildren>;\n};\n\ntemplate <typename Metavariables>\nstruct SingletonComponent {\n  using metavariables = Metavariables;\n  using array_index = int;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<ArrayComponent<Metavariables>,\n                                    SingletonComponent<Metavariables>>;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nvoid test() {\n  using array_component = ArrayComponent<Metavariables>;\n  using singleton_component = SingletonComponent<Metavariables>;\n  const ElementId<1> parent_id{0, std::array{SegmentId{2, 1}}};\n  const ElementId<1> parent_lower_neighbor_id{0, std::array{SegmentId{2, 0}}};\n  const ElementId<1> parent_upper_neighbor_id{0, std::array{SegmentId{2, 2}}};\n  DirectionMap<1, Neighbors<1>> parent_neighbors{};\n  OrientationMap<1> aligned = OrientationMap<1>::create_aligned();\n  parent_neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors<1>{std::unordered_set{parent_lower_neighbor_id}, aligned});\n  parent_neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors<1>{std::unordered_set{parent_upper_neighbor_id}, aligned});\n  Element<1> parent{parent_id, std::move(parent_neighbors)};\n  Mesh<1> parent_mesh{3, Spectral::Basis::Legendre,\n                      Spectral::Quadrature::GaussLobatto};\n  Mesh<1> lower_neighbor_mesh{2, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n  Mesh<1> upper_neighbor_mesh{4, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n  amr::Info<1> parent_info{{amr::Flag::Split}, parent_mesh};\n  std::unordered_map<ElementId<1>, amr::Info<1>> parent_neighbor_info{};\n  parent_neighbor_info.emplace(\n      parent_lower_neighbor_id,\n      amr::Info<1>{std::array{amr::Flag::DoNothing}, lower_neighbor_mesh});\n  parent_neighbor_info.emplace(\n      parent_upper_neighbor_id,\n      amr::Info<1>{std::array{amr::Flag::DoNothing}, upper_neighbor_mesh});\n  const auto children_ids = amr::ids_of_children(parent_id, parent_info.flags);\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, parent_id,\n      {parent, parent_mesh, parent_info, parent_neighbor_info});\n  ActionTesting::emplace_component<singleton_component>(&runner, 0);\n  for (const auto& child_id : children_ids) {\n    ActionTesting::emplace_component<array_component>(&runner, child_id);\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(\n        runner, child_id));\n  }\n  CHECK(ActionTesting::is_simple_action_queue_empty<singleton_component>(runner,\n                                                                         0));\n  CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(\n      runner, parent_id));\n\n  auto& cache = ActionTesting::cache<array_component>(runner, parent_id);\n  auto& element_proxy =\n      Parallel::get_parallel_component<array_component>(cache);\n\n  // Call CreateChild, creating the first child and queueing CreateChild on\n  // the singleton component in order to create the second child\n  ActionTesting::simple_action<singleton_component, amr::Actions::CreateChild>(\n      make_not_null(&runner), 0, element_proxy, parent_id, children_ids, 0_st,\n      std::unordered_map<Parallel::Phase, size_t>{});\n  for (const auto& child_id : children_ids) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(\n        runner, child_id));\n  }\n  CHECK(ActionTesting::number_of_queued_simple_actions<singleton_component>(\n            runner, 0) == 1);\n  CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(\n      runner, parent_id));\n\n  // Call CreateChild, creating the second child and queueing SendDataToChildren\n  // on the parent element in order to send data to the first child\n  ActionTesting::invoke_queued_simple_action<singleton_component>(\n      make_not_null(&runner), 0);\n  for (const auto& child_id : children_ids) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(\n        runner, child_id));\n  }\n  CHECK(ActionTesting::is_simple_action_queue_empty<singleton_component>(runner,\n                                                                         0));\n  CHECK(ActionTesting::number_of_queued_simple_actions<array_component>(\n            runner, parent_id) == 1);\n  // Invoke the mock action to check that CreateChild sent the correct data to\n  // SendDataToChildren\n  ActionTesting::invoke_queued_simple_action<array_component>(\n      make_not_null(&runner), parent_id);\n  CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(\n      runner, parent_id));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.CreateChild\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_CreateParent.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <deque>\n#include <pup.h>\n#include <vector>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CollectDataFromChildren.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/CreateParent.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct MockCollectDataFromChildren {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename... Tags>\n  static void apply(\n      DataBox& /*box*/, Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Metavariables::volume_dim>& /*child_id*/,\n      const ElementId<Metavariables::volume_dim>& parent_id,\n      std::deque<ElementId<Metavariables::volume_dim>> sibling_ids_to_collect) {\n    CHECK(parent_id == ElementId<1>{0, std::array{SegmentId{2, 1}}});\n    CHECK(sibling_ids_to_collect ==\n          std::deque{ElementId<1>{0, std::array{SegmentId{3, 3}}}});\n  }\n};\n\ntemplate <typename Metavariables>\nstruct ArrayComponent {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n  using replace_these_simple_actions =\n      tmpl::list<amr::Actions::CollectDataFromChildren>;\n  using with_these_simple_actions = tmpl::list<MockCollectDataFromChildren>;\n};\n\ntemplate <typename Metavariables>\nstruct SingletonComponent {\n  using metavariables = Metavariables;\n  using array_index = int;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags = tmpl::list<>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<ArrayComponent<Metavariables>,\n                                    SingletonComponent<Metavariables>>;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\nvoid test() {\n  using array_component = ArrayComponent<Metavariables>;\n  using singleton_component = SingletonComponent<Metavariables>;\n  const ElementId<1> lower_child_id{0, std::array{SegmentId{3, 2}}};\n  const ElementId<1> upper_child_id{0, std::array{SegmentId{3, 3}}};\n  const ElementId<1> parent_id{0, std::array{SegmentId{2, 1}}};\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component<singleton_component>(&runner, 0);\n  for (const auto& id :\n       std::vector{lower_child_id, upper_child_id, parent_id}) {\n    ActionTesting::emplace_component<array_component>(&runner, id);\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(runner,\n                                                                       id));\n  }\n  CHECK(ActionTesting::is_simple_action_queue_empty<singleton_component>(runner,\n                                                                         0));\n\n  auto& cache = ActionTesting::cache<array_component>(runner, parent_id);\n  auto& element_proxy =\n      Parallel::get_parallel_component<array_component>(cache);\n\n  ActionTesting::simple_action<singleton_component, amr::Actions::CreateParent>(\n      make_not_null(&runner), 0, element_proxy, parent_id, lower_child_id,\n      std::deque{upper_child_id},\n      std::unordered_map<Parallel::Phase, size_t>{});\n  for (const auto& id : std::vector{upper_child_id, parent_id}) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(runner,\n                                                                       id));\n  }\n  CHECK(ActionTesting::number_of_queued_simple_actions<array_component>(\n            runner, lower_child_id) == 1);\n  ActionTesting::invoke_queued_simple_action<array_component>(\n      make_not_null(&runner), lower_child_id);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.CreateParent\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_EvaluateRefinementCriteria.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/EvaluateRefinementCriteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/IncreaseResolution.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nauto wants_to_join() {\n  return std::make_unique<\n      amr::Criteria::DriveToTarget<1, amr::Criteria::Type::h>>(\n      std::array{0_st}, std::array{amr::Flag::DoNothing});\n}\n\nauto wants_to_split() {\n  return std::make_unique<\n      amr::Criteria::DriveToTarget<1, amr::Criteria::Type::h>>(\n      std::array{8_st}, std::array{amr::Flag::DoNothing});\n}\n\nauto wants_to_increase_resolution() {\n  return std::make_unique<amr::Criteria::IncreaseResolution<1>>();\n}\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\ntemplate <size_t Dim>\nclass BadCriterion : public amr::Criterion {\n public:\n  using options = tmpl::list<>;\n\n  BadCriterion() = default;\n  explicit BadCriterion(CkMigrateMessage* msg) : Criterion(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BadCriterion);  // NOLINT\n\n  amr::Criteria::Type type() override { return amr::Criteria::Type::h; }\n\n  std::string observation_name() override { return \"BadCriterion\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename Metavariables>\n  auto operator()(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Metavariables::volume_dim>& /*element_id*/) const {\n    if constexpr (Dim == 1) {\n      return std::array{amr::Flag::DecreaseResolution};\n    } else {\n      return std::array{amr::Flag::DecreaseResolution,\n                        amr::Flag::IncreaseResolution};\n    }\n  }\n\n  void pup(PUP::er& p) override { Criterion::pup(p); }\n};\n\ntemplate <size_t Dim>\nPUP::able::PUP_ID BadCriterion<Dim>::my_PUP_ID = 0;  // NOLINT\n#pragma GCC diagnostic pop\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags =\n      tmpl::list<amr::Criteria::Tags::Criteria,\n                 amr::Tags::AmrBlocks<volume_dim>, amr::Tags::Policies>;\n  using simple_tags =\n      tmpl::list<domain::Tags::Element<volume_dim>,\n                 domain::Tags::Mesh<volume_dim>, amr::Tags::Info<volume_dim>,\n                 amr::Tags::NeighborInfo<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <size_t VolumeDim, bool IgnoreP>\nstruct Metavariables {\n  static constexpr size_t volume_dim = VolumeDim;\n\n  using component_list = tmpl::list<Component<Metavariables>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<amr::Criterion,\n                   tmpl::list<BadCriterion<volume_dim>,\n                              amr::Criteria::IncreaseResolution<volume_dim>,\n                              amr::Criteria::DriveToTarget<\n                                  volume_dim, amr::Criteria::Type::h>>>>;\n  };\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = Component<Metavariables>;\n    using projectors = tmpl::list<>;\n    static constexpr bool keep_coarse_grids = false;\n    static constexpr bool p_refine_only_in_event = IgnoreP;\n  };\n};\n\n// When AMR is run, the simple action EvaluateAmrCriteria is run on each\n// Element.  EvaluateAmrCriteria evaluates the criteria which set its own\n// amr::Tags::Info and then calls the simple action UpdateAmrDecision\n// on each neighboring Element of the Element sending the Info.\n// UpdateAmrDecision checks to see if an Elements Info need to change based on\n// the received NeighborInfo (e.g. if an element wants to join, but its\n// sibling does not the element must change its decision to do nothing).  If the\n// element's Info are changed, then it calls UpdateAmrDecision on its\n// neighbors, and the process continues until no Element wants to change its\n// decision.   This test manually runs this process on three elements until\n// EvaluateAmrCriteria has been called on each Element.  Note in a asynchronus\n// parallel environment, it is possible for an Element to execute\n// UpdateAmrDecision (triggered by a neighboring Element) prior to executing\n// EvaluateAmrCriteria\n//\n// If IgnoreP is true, only h-refinement criteria are evaluated\ntemplate <bool IgnoreP>\nvoid evaluate_criteria(std::vector<std::unique_ptr<amr::Criterion>> criteria,\n                       const std::array<amr::Flag, 1> expected_flags) {\n  using metavariables = Metavariables<1, IgnoreP>;\n  using my_component = Component<metavariables>;\n  CAPTURE(expected_flags);\n  const bool p_refined =\n      (expected_flags == std::array{amr::Flag::IncreaseResolution});\n  const ElementId<1> self_id(0, {{{1, 1}}});\n  const ElementId<1> lo_id(0, {{{1, 0}}});\n  const ElementId<1> up_id(1, {{{1, 0}}});\n  const ElementId<1> up_sibling_id(1, {{{1, 1}}});\n  const ElementId<1> disabled_block_id(2, {{{0, 0}}});\n  const Mesh<1> self_mesh{2_st, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> lo_mesh{3_st, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> up_mesh{4_st, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> up_sibling_mesh{5_st, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> p_self_mesh{3_st, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> p_lo_mesh{4_st, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> p_up_mesh{5_st, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  amr::Info<1> initial_info{std::array{amr::Flag::Undefined}, Mesh<1>{}};\n  std::unordered_map<ElementId<1>, amr::Info<1>> initial_neighbor_info;\n  const std::vector<size_t> amr_blocks{0, 1};\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {std::move(criteria), amr_blocks,\n       amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true}}};\n\n  const Element<1> self(self_id,\n                        {{{Direction<1>::lower_xi(),\n                           {{lo_id}, OrientationMap<1>::create_aligned()}},\n                          {Direction<1>::upper_xi(),\n                           {{up_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, self_id, {self, self_mesh, initial_info, initial_neighbor_info});\n\n  const Element<1> lo(lo_id,\n                      {{{Direction<1>::upper_xi(),\n                         {{self_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, lo_id, {lo, lo_mesh, initial_info, initial_neighbor_info});\n\n  const Element<1> up(\n      up_id, {{{Direction<1>::lower_xi(),\n                {{self_id}, OrientationMap<1>::create_aligned()}},\n               {Direction<1>::upper_xi(),\n                {{up_sibling_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, up_id, {up, up_mesh, initial_info, initial_neighbor_info});\n\n  const Element<1> up_sibling(\n      up_sibling_id,\n      {{{Direction<1>::lower_xi(),\n         {{up_id}, OrientationMap<1>::create_aligned()}},\n        {Direction<1>::upper_xi(),\n         {{disabled_block_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, up_sibling_id,\n      {up_sibling, up_sibling_mesh, initial_info, initial_neighbor_info});\n\n  const Element<1> disabled_block(\n      disabled_block_id,\n      {{{Direction<1>::lower_xi(),\n         {{up_sibling_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, disabled_block_id,\n      {disabled_block, up_sibling_mesh, initial_info, initial_neighbor_info});\n\n  runner.set_phase(Parallel::Phase::Testing);\n\n  for (const auto& id : {self_id, lo_id, up_id}) {\n    CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<1>>(\n              runner, id) == initial_info);\n    CHECK(ActionTesting::get_databox_tag<my_component,\n                                         amr::Tags::NeighborInfo<1>>(\n              runner, id) == initial_neighbor_info);\n    CHECK(\n        ActionTesting::is_simple_action_queue_empty<my_component>(runner, id));\n  }\n\n  // self runs EvaluateAmrCriteria, queueing UpdateAmrDecision on lo and hi\n  ActionTesting::simple_action<my_component,\n                               amr::Actions::EvaluateRefinementCriteria>(\n      make_not_null(&runner), self_id);\n\n  const amr::Info<1> self_info{expected_flags,\n                               p_refined ? p_self_mesh : self_mesh};\n  for (const auto& id : {self_id, lo_id, up_id}) {\n    CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<1>>(\n              runner, id) == (id == self_id ? self_info : initial_info));\n    CHECK(ActionTesting::get_databox_tag<my_component,\n                                         amr::Tags::NeighborInfo<1>>(\n              runner, id) == initial_neighbor_info);\n    CHECK(ActionTesting::number_of_queued_simple_actions<my_component>(\n              runner, id) == (id == self_id ? 0 : 1));\n  }\n\n  // lo runs EvaluateAmrCriteria, queuing UpdateAmrDecision on self\n  ActionTesting::simple_action<my_component,\n                               amr::Actions::EvaluateRefinementCriteria>(\n      make_not_null(&runner), lo_id);\n\n  const amr::Info<1> lo_info{expected_flags, p_refined ? p_lo_mesh : lo_mesh};\n  for (const auto& id : {self_id, lo_id, up_id}) {\n    CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<1>>(\n              runner, id) ==\n          (id == up_id ? initial_info : (id == self_id ? self_info : lo_info)));\n    CHECK(ActionTesting::get_databox_tag<my_component,\n                                         amr::Tags::NeighborInfo<1>>(\n              runner, id) == initial_neighbor_info);\n    CHECK(ActionTesting::number_of_queued_simple_actions<my_component>(\n              runner, id) == 1);\n  }\n\n  // up runs UpdateAmrDecision, which queues nothing\n  ActionTesting::invoke_queued_simple_action<my_component>(\n      make_not_null(&runner), up_id);\n  for (const auto& id : {self_id, lo_id, up_id}) {\n    CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<1>>(\n              runner, id) ==\n          (id == up_id ? initial_info : (id == self_id ? self_info : lo_info)));\n    CHECK(ActionTesting::get_databox_tag<my_component,\n                                         amr::Tags::NeighborInfo<1>>(runner,\n                                                                     id) ==\n          (id == up_id\n               ? std::unordered_map<ElementId<1>, amr::Info<1>>{{self_id,\n                                                                 self_info}}\n               : initial_neighbor_info));\n    CHECK(ActionTesting::number_of_queued_simple_actions<my_component>(\n              runner, id) == (id == up_id ? 0 : 1));\n  }\n\n  // up runs EvaluateAmrCriteria, queueing UpdateAmrDecision on self\n  ActionTesting::simple_action<my_component,\n                               amr::Actions::EvaluateRefinementCriteria>(\n      make_not_null(&runner), up_id);\n  const amr::Info<1> up_info{expected_flags, p_refined ? p_up_mesh : up_mesh};\n  for (const auto& id : {self_id, lo_id, up_id}) {\n    CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<1>>(\n              runner, id) ==\n          (id == up_id ? up_info : (id == self_id ? self_info : lo_info)));\n    CHECK(ActionTesting::get_databox_tag<my_component,\n                                         amr::Tags::NeighborInfo<1>>(runner,\n                                                                     id) ==\n          (id == up_id\n               ? std::unordered_map<ElementId<1>, amr::Info<1>>{{self_id,\n                                                                 self_info}}\n               : initial_neighbor_info));\n    CHECK(ActionTesting::number_of_queued_simple_actions<my_component>(\n              runner, id) == (id == self_id ? 2 : (id == lo_id ? 1 : 0)));\n  }\n\n  // disabled block runs EvaluateAmrCriteria, which queues nothing\n  ActionTesting::simple_action<my_component,\n                               amr::Actions::EvaluateRefinementCriteria>(\n      make_not_null(&runner), disabled_block_id);\n  const amr::Info<1> disabled_block_info{\n      std::array<amr::Flag, 1>{amr::Flag::DoNothing}, up_sibling_mesh};\n  for (const auto& id : {disabled_block_id}) {\n    CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<1>>(\n              runner, id) == disabled_block_info);\n    CHECK(ActionTesting::get_databox_tag<my_component,\n                                         amr::Tags::NeighborInfo<1>>(\n              runner, id) == initial_neighbor_info);\n    CHECK(ActionTesting::number_of_queued_simple_actions<my_component>(\n              runner, id) == 0);\n  }\n}\n\nvoid check_split_while_join_is_avoided() {\n  using metavariables = Metavariables<2, true>;\n  using my_component = Component<metavariables>;\n\n  // The part of action we are testing does not depend upon information\n  // from neighbors, so we just use a single Element setup on refinement\n  // levels (0, 1)\n  const ElementId<2> self_id(0, {{{0, 0}, {1, 1}}});\n  const Mesh<2> mesh{2_st, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  amr::Info<2> initial_info{\n      std::array{amr::Flag::Undefined, amr::Flag::Undefined}, Mesh<2>{}};\n  std::unordered_map<ElementId<2>, amr::Info<2>> initial_neighbor_info{};\n\n  // the refinement criteria wants to drive self to levels (1, 0) so\n  // it will return flags (Split, Join).\n  std::vector<std::unique_ptr<amr::Criterion>> criteria;\n  criteria.emplace_back(\n      std::make_unique<amr::Criteria::DriveToTarget<2, amr::Criteria::Type::h>>(\n          std::array{1_st, 0_st},\n          std::array{amr::Flag::DoNothing, amr::Flag::DoNothing}));\n\n  Parallel::GlobalCache<metavariables> empty_cache{};\n  auto databox = db::create<tmpl::list<::domain::Tags::Mesh<2>>>(mesh);\n  ObservationBox<tmpl::list<>, db::DataBox<tmpl::list<::domain::Tags::Mesh<2>>>>\n      box{make_not_null(&databox)};\n  auto flags_from_criterion =\n      criteria.front()->evaluate(box, empty_cache, self_id);\n  CHECK(flags_from_criterion == std::array{amr::Flag::Split, amr::Flag::Join});\n\n  // But we do not allow an Element to simultaneously split and join so the\n  // action should change the flags to (DoNothing, Split)\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      {std::move(criteria), std::nullopt,\n       amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true}}};\n\n  const Element<2> self(self_id, {});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, self_id, {self, mesh, initial_info, initial_neighbor_info});\n\n  runner.set_phase(Parallel::Phase::Testing);\n\n  CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<2>>(\n            runner, self_id) == initial_info);\n  CHECK(\n      ActionTesting::get_databox_tag<my_component, amr::Tags::NeighborInfo<2>>(\n          runner, self_id) == initial_neighbor_info);\n  CHECK(ActionTesting::is_simple_action_queue_empty<my_component>(runner,\n                                                                  self_id));\n\n  // self runs EvaluateAmrCriteria\n  ActionTesting::simple_action<my_component,\n                               amr::Actions::EvaluateRefinementCriteria>(\n      make_not_null(&runner), self_id);\n\n  amr::Info<2> expected_info{std::array{amr::Flag::Split, amr::Flag::DoNothing},\n                             mesh};\n  CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<2>>(\n            runner, self_id) == expected_info);\n  CHECK(\n      ActionTesting::get_databox_tag<my_component, amr::Tags::NeighborInfo<2>>(\n          runner, self_id) == initial_neighbor_info);\n  CHECK(ActionTesting::number_of_queued_simple_actions<my_component>(\n            runner, self_id) == 0);\n}\n}  //  namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.EvaluateRefinementCriteria\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  register_factory_classes_with_charm<Metavariables<2, true>>();\n  register_factory_classes_with_charm<Metavariables<1, true>>();\n  register_factory_classes_with_charm<Metavariables<1, false>>();\n  {\n    INFO(\"No criteria\");\n    evaluate_criteria<true>(std::vector<std::unique_ptr<amr::Criterion>>{},\n                            std::array{amr::Flag::DoNothing});\n    evaluate_criteria<false>(std::vector<std::unique_ptr<amr::Criterion>>{},\n                             std::array{amr::Flag::DoNothing});\n  }\n  {\n    INFO(\"Only one p-criteria\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria;\n    criteria.emplace_back(wants_to_increase_resolution());\n    evaluate_criteria<false>(std::move(criteria),\n                             std::array{amr::Flag::IncreaseResolution});\n  }\n  {\n    INFO(\"Only one p-criteria, ignored\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria;\n    criteria.emplace_back(wants_to_increase_resolution());\n    evaluate_criteria<true>(std::move(criteria),\n                            std::array{amr::Flag::DoNothing});\n  }\n  {\n    INFO(\"Should join\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria;\n    criteria.emplace_back(wants_to_join());\n    evaluate_criteria<false>(std::move(criteria), std::array{amr::Flag::Join});\n  }\n  {\n    INFO(\"Should join, p-ignored\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria;\n    criteria.emplace_back(wants_to_join());\n    criteria.emplace_back(wants_to_increase_resolution());\n    evaluate_criteria<true>(std::move(criteria), std::array{amr::Flag::Join});\n  }\n  {\n    INFO(\"Should split\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria;\n    criteria.emplace_back(wants_to_split());\n    criteria.emplace_back(wants_to_join());\n    evaluate_criteria<false>(std::move(criteria), std::array{amr::Flag::Split});\n  }\n  {\n    INFO(\"Should split, p-ignored\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria;\n    criteria.emplace_back(wants_to_join());\n    criteria.emplace_back(wants_to_increase_resolution());\n    criteria.emplace_back(wants_to_split());\n    evaluate_criteria<true>(std::move(criteria), std::array{amr::Flag::Split});\n  }\n#ifdef SPECTRE_DEBUG\n  {\n    INFO(\"Check ASSERT triggers\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria;\n    criteria.emplace_back(std::make_unique<BadCriterion<1>>());\n\n    CHECK_THROWS_WITH(\n        (evaluate_criteria<true>(std::move(criteria),\n                                 std::array{amr::Flag::DoNothing})),\n        Catch::Matchers::ContainsSubstring(\n            \"requested p-refinement, but claims to be for h-refinement\"));\n  }\n#endif\n  check_split_while_join_is_avoided();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_Initialize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = void;\n    using projectors = tmpl::list<>;\n    static constexpr bool keep_coarse_grids = false;\n    [[maybe_unused]] static constexpr bool p_refine_only_in_event = false;\n  };\n};\n\ntemplate <size_t Dim>\nvoid test() {\n  auto box = db::create<\n      db::AddSimpleTags<amr::Tags::Info<Dim>, amr::Tags::NeighborInfo<Dim>>>(\n      amr::Info<Dim>{}, std::unordered_map<ElementId<Dim>, amr::Info<Dim>>{});\n  db::mutate_apply<amr::Initialization::Initialize<Dim, Metavariables>>(\n      make_not_null(&box));\n  CHECK(db::get<amr::Tags::Info<Dim>>(box).flags ==\n        make_array<Dim>(amr::Flag::Undefined));\n  CHECK(db::get<amr::Tags::Info<Dim>>(box).new_mesh == Mesh<Dim>{});\n  CHECK(db::get<amr::Tags::NeighborInfo<Dim>>(box).empty());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAmr.Actions.Initialize\",\n                  \"[ParallelAlgorithms][Unit]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_InitializeChild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockRuntimeSystem.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Helpers/Domain/Amr/RegistrationHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/InitializeChild.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags = tmpl::list<\n      domain::Tags::Element<volume_dim>, domain::Tags::Mesh<volume_dim>,\n      domain::Tags::NeighborMesh<volume_dim>, amr::Tags::Info<volume_dim>,\n      amr::Tags::NeighborInfo<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 2;\n  using component_list = tmpl::list<Component<Metavariables>,\n                                    TestHelpers::amr::Registrar<Metavariables>>;\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<Component<Metavariables>,\n                             tmpl::list<TestHelpers::amr::RegisterElement>>>;\n  };\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using projectors = tmpl::list<>;\n    static constexpr bool keep_coarse_grids = false;\n    [[maybe_unused]] static constexpr bool p_refine_only_in_event = false;\n  };\n};\n\nvoid test() {\n  const ElementId<2> parent_id{0, std::array{SegmentId{2, 1}, SegmentId{0, 0}}};\n  const ElementId<2> parent_lower_neighbor_id{\n      0, std::array{SegmentId{2, 0}, SegmentId{0, 0}}};\n  const ElementId<2> parent_upper_neighbor_id{\n      0, std::array{SegmentId{2, 2}, SegmentId{0, 0}}};\n  DirectionMap<2, Neighbors<2>> parent_neighbors{};\n  OrientationMap<2> aligned = OrientationMap<2>::create_aligned();\n  parent_neighbors.emplace(\n      Direction<2>::lower_xi(),\n      Neighbors<2>{std::unordered_set{parent_lower_neighbor_id}, aligned});\n  parent_neighbors.emplace(\n      Direction<2>::upper_xi(),\n      Neighbors<2>{std::unordered_set{parent_upper_neighbor_id}, aligned});\n  Element<2> parent{parent_id, std::move(parent_neighbors)};\n  Mesh<2> parent_mesh{3, Spectral::Basis::Legendre,\n                      Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> expected_child_mesh{std::array{3_st, 4_st},\n                                    Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto};\n  amr::Info<2> parent_info{\n      std::array{amr::Flag::Split, amr::Flag::IncreaseResolution},\n      expected_child_mesh};\n  const Mesh<2> neighbor_mesh{std::array{4_st, 5_st}, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n  std::unordered_map<ElementId<2>, amr::Info<2>> parent_neighbor_info;\n  parent_neighbor_info.emplace(\n      parent_lower_neighbor_id,\n      amr::Info<2>{std::array{amr::Flag::DoNothing, amr::Flag::DoNothing},\n                   neighbor_mesh});\n  parent_neighbor_info.emplace(\n      parent_upper_neighbor_id,\n      amr::Info<2>{std::array{amr::Flag::DoNothing, amr::Flag::Split},\n                   neighbor_mesh});\n  const tuples::TaggedTuple<domain::Tags::Element<2>, domain::Tags::Mesh<2>,\n                            amr::Tags::Info<2>, amr::Tags::NeighborInfo<2>>\n      parent_items{std::move(parent), std::move(parent_mesh),\n                   std::move(parent_info), std::move(parent_neighbor_info)};\n\n  const ElementId<2> child_id{0, std::array{SegmentId{3, 3}, SegmentId{0, 0}}};\n  const ElementId<2> expected_child_upper_neighbor_id_0{\n      0, std::array{SegmentId{2, 2}, SegmentId{1, 0}}};\n  const ElementId<2> expected_child_upper_neighbor_id_1{\n      0, std::array{SegmentId{2, 2}, SegmentId{1, 1}}};\n  const ElementId<2> sibling_id{0,\n                                std::array{SegmentId{3, 2}, SegmentId{0, 0}}};\n  DirectionMap<2, Neighbors<2>> expected_child_neighbors{};\n  expected_child_neighbors.emplace(\n      Direction<2>::lower_xi(),\n      Neighbors<2>{std::unordered_set{sibling_id}, aligned});\n  expected_child_neighbors.emplace(\n      Direction<2>::upper_xi(),\n      Neighbors<2>{std::unordered_set{expected_child_upper_neighbor_id_0,\n                                      expected_child_upper_neighbor_id_1},\n                   aligned});\n  const Element<2> expected_child{child_id,\n                                  std::move(expected_child_neighbors)};\n  DirectionalIdMap<2, Mesh<2>> expected_child_neighbor_mesh{};\n  expected_child_neighbor_mesh.emplace(\n      DirectionalId<2>{Direction<2>::lower_xi(), sibling_id},\n      expected_child_mesh);\n  expected_child_neighbor_mesh.emplace(\n      DirectionalId<2>{Direction<2>::upper_xi(),\n                       expected_child_upper_neighbor_id_0},\n      neighbor_mesh);\n  expected_child_neighbor_mesh.emplace(\n      DirectionalId<2>{Direction<2>::upper_xi(),\n                       expected_child_upper_neighbor_id_1},\n      neighbor_mesh);\n\n  const amr::Info<2> expected_child_info{\n      std::array{amr::Flag::Undefined, amr::Flag::Undefined}, Mesh<2>{}};\n  const std::unordered_map<ElementId<2>, amr::Info<2>>\n      expected_child_neighbor_info{};\n\n  using array_component = Component<Metavariables>;\n  using registrar = TestHelpers::amr::Registrar<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_group_component<registrar>(&runner);\n  ActionTesting::emplace_component<array_component>(&runner, child_id);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<2>>(\n            runner, 0)\n            .empty());\n  ActionTesting::simple_action<array_component, amr::Actions::InitializeChild>(\n      make_not_null(&runner), child_id, parent_items);\n  CHECK(\n      ActionTesting::get_databox_tag<array_component, domain::Tags::Element<2>>(\n          runner, child_id) == expected_child);\n  CHECK(ActionTesting::get_databox_tag<array_component, domain::Tags::Mesh<2>>(\n            runner, child_id) == expected_child_mesh);\n  CHECK(ActionTesting::get_databox_tag<array_component,\n                                       domain::Tags::NeighborMesh<2>>(\n            runner, child_id) == expected_child_neighbor_mesh);\n  CHECK(ActionTesting::get_databox_tag<array_component, amr::Tags::Info<2>>(\n            runner, child_id) == expected_child_info);\n  CHECK(ActionTesting::get_databox_tag<array_component,\n                                       amr::Tags::NeighborInfo<2>>(\n            runner, child_id) == expected_child_neighbor_info);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<2>>(\n            runner, 0)\n            .empty());\n  ActionTesting::invoke_queued_simple_action<registrar>(make_not_null(&runner),\n                                                        0);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<2>>(\n            runner, 0) == std::unordered_set{child_id});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.InitializeChild\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_InitializeParent.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockRuntimeSystem.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"Helpers/Domain/Amr/RegistrationHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/InitializeParent.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags = tmpl::list<\n      domain::Tags::Element<volume_dim>, domain::Tags::Mesh<volume_dim>,\n      domain::Tags::NeighborMesh<volume_dim>, amr::Tags::Info<volume_dim>,\n      amr::Tags::NeighborInfo<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 3;\n  using component_list = tmpl::list<Component<Metavariables>,\n                                    TestHelpers::amr::Registrar<Metavariables>>;\n  // [registration_metavariables]\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<Component<Metavariables>,\n                             tmpl::list<TestHelpers::amr::RegisterElement>>>;\n  };\n  // [registration_metavariables]\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using projectors = tmpl::list<>;\n    [[maybe_unused]] static constexpr bool keep_coarse_grids = false;\n    [[maybe_unused]] static constexpr bool p_refine_only_in_event = false;\n  };\n};\n\n// Test setup showing xi and eta dimensions which are hp-refined; only\n// p-refinement in zeta dimension\n\n// Before refinement:                     After refinement:\n//\n//       |-----------|                         |-----------|\n//       |           |                         |           |\n//       |    N5     |                         |    N5     |\n//       |           |                         |           |\n// |-----|-----------|-----------|       |-----|-----------|-----------|\n// |     |           |           |       |     |           |           |\n// |  N6 |    C3     |           |       |  N6 |           |    N11    |\n// |     |           |           |       |     |           |           |\n// |--|--|-----|-----|    N4     |       |-----|     P     |-----------|\n//    |N7|     |     |           |       |     |           |           |\n//    |--|  C1 |  C2 |           |       | N12 |           |    N10    |\n//    |N8|     |     |           |       |     |           |           |\n//    |--|--|--|-----|-----------|       |-----|--|--|-----|-----------|\n//       |  |  |     |                         |     |     |\n//       |N1|N2|  N3 |                         |  N9 |  N3 |\n//       |  |  |     |                         |     |     |\n//       |-----|-----|                         |-----|-----|\n//\n// Block setup is:\n// Elements C1, C2, and C3 are in Block 0\n// Element N4 is in Block 1 which is aligned with Block 0\n// Element N5 is in Block 2 which is rotated by 90 degrees counter clockwise\n// Elments N6, N7, and N8 are in Block 3 which is anti-aligned with Block 0\n// Elements N1, N2, and N3 are in Block 4 which is rotated 90 deg. clockwise\nvoid test() {\n  OrientationMap<3> aligned = OrientationMap<3>::create_aligned();\n  OrientationMap<3> b1_orientation = OrientationMap<3>::create_aligned();\n  OrientationMap<3> b2_orientation{std::array{Direction<3>::lower_eta(),\n                                              Direction<3>::upper_xi(),\n                                              Direction<3>::upper_zeta()}};\n  OrientationMap<3> b3_orientation{std::array{Direction<3>::lower_xi(),\n                                              Direction<3>::lower_eta(),\n                                              Direction<3>::upper_zeta()}};\n  OrientationMap<3> b4_orientation{std::array{Direction<3>::upper_eta(),\n                                              Direction<3>::lower_xi(),\n                                              Direction<3>::upper_zeta()}};\n\n  SegmentId s_00{0, 0};\n  SegmentId s_10{1, 0};\n  SegmentId s_11{1, 1};\n  SegmentId s_20{2, 0};\n  SegmentId s_21{2, 1};\n  SegmentId s_22{2, 2};\n  SegmentId s_23{2, 3};\n\n  const ElementId<3> parent_id{0, std::array{s_00, s_00, s_00}};\n\n  const ElementId<3> child_1_id{0, std::array{s_10, s_10, s_00}};\n  const ElementId<3> child_2_id{0, std::array{s_11, s_10, s_00}};\n  const ElementId<3> child_3_id{0, std::array{s_00, s_11, s_00}};\n\n  const ElementId<3> neighbor_1_id{4, std::array{s_10, s_20, s_00}};\n  const ElementId<3> neighbor_2_id{4, std::array{s_10, s_21, s_00}};\n  const ElementId<3> neighbor_3_id{4, std::array{s_10, s_11, s_00}};\n  const ElementId<3> neighbor_9_id{4, std::array{s_10, s_10, s_00}};\n\n  const ElementId<3> neighbor_4_id{1, std::array{s_00, s_00, s_00}};\n  const ElementId<3> neighbor_10_id{1, std::array{s_00, s_10, s_00}};\n  const ElementId<3> neighbor_11_id{1, std::array{s_00, s_11, s_00}};\n\n  const ElementId<3> neighbor_5_id{2, std::array{s_10, s_00, s_00}};\n\n  const ElementId<3> neighbor_6_id{3, std::array{s_10, s_10, s_00}};\n  const ElementId<3> neighbor_7_id{3, std::array{s_20, s_22, s_00}};\n  const ElementId<3> neighbor_8_id{3, std::array{s_20, s_23, s_00}};\n  const ElementId<3> neighbor_12_id{3, std::array{s_10, s_11, s_00}};\n\n  const Mesh<3> expected_parent_mesh{4, Spectral::Basis::Legendre,\n                                     Spectral::Quadrature::GaussLobatto};\n\n  DirectionMap<3, Neighbors<3>> child_1_neighbors{};\n  child_1_neighbors.emplace(\n      Direction<3>::lower_xi(),\n      Neighbors<3>{std::unordered_set{neighbor_8_id, neighbor_7_id},\n                   b3_orientation});\n  child_1_neighbors.emplace(\n      Direction<3>::upper_xi(),\n      Neighbors<3>{std::unordered_set{child_2_id}, aligned});\n  child_1_neighbors.emplace(\n      Direction<3>::lower_eta(),\n      Neighbors<3>{std::unordered_set{neighbor_1_id, neighbor_2_id},\n                   b4_orientation});\n  child_1_neighbors.emplace(\n      Direction<3>::upper_eta(),\n      Neighbors<3>{std::unordered_set{child_3_id}, aligned});\n  Element<3> child_1{child_1_id, std::move(child_1_neighbors)};\n  Mesh<3> child_1_mesh{std::array{3_st, 4_st, 3_st}, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  amr::Info<3> child_1_info{std::array{amr::Flag::Join, amr::Flag::Join,\n                                       amr::Flag::IncreaseResolution},\n                            expected_parent_mesh};\n\n  DirectionMap<3, Neighbors<3>> child_2_neighbors{};\n  child_2_neighbors.emplace(\n      Direction<3>::lower_xi(),\n      Neighbors<3>{std::unordered_set{child_1_id}, aligned});\n  child_2_neighbors.emplace(\n      Direction<3>::upper_xi(),\n      Neighbors<3>{std::unordered_set{neighbor_4_id}, b1_orientation});\n  child_2_neighbors.emplace(\n      Direction<3>::lower_eta(),\n      Neighbors<3>{std::unordered_set{neighbor_3_id}, b4_orientation});\n  child_2_neighbors.emplace(\n      Direction<3>::upper_eta(),\n      Neighbors<3>{std::unordered_set{child_3_id}, aligned});\n  Element<3> child_2{child_2_id, std::move(child_2_neighbors)};\n  Mesh<3> child_2_mesh{std::array{4_st, 3_st, 4_st}, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  amr::Info<3> child_2_info{std::array{amr::Flag::Join, amr::Flag::Join,\n                                       amr::Flag::DecreaseResolution},\n                            expected_parent_mesh};\n\n  DirectionMap<3, Neighbors<3>> child_3_neighbors{};\n  child_3_neighbors.emplace(\n      Direction<3>::lower_xi(),\n      Neighbors<3>{std::unordered_set{neighbor_6_id}, b3_orientation});\n  child_3_neighbors.emplace(\n      Direction<3>::upper_xi(),\n      Neighbors<3>{std::unordered_set{neighbor_4_id}, b1_orientation});\n  child_3_neighbors.emplace(\n      Direction<3>::lower_eta(),\n      Neighbors<3>{std::unordered_set{child_2_id, child_1_id}, aligned});\n  child_3_neighbors.emplace(\n      Direction<3>::upper_eta(),\n      Neighbors<3>{std::unordered_set{neighbor_5_id}, b2_orientation});\n  Element<3> child_3{child_3_id, std::move(child_3_neighbors)};\n  Mesh<3> child_3_mesh{std::array{4_st, 4_st, 3_st}, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  amr::Info<3> child_3_info{\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::Join,\n                 amr::Flag::IncreaseResolution},\n      expected_parent_mesh};\n\n  Mesh<3> neighbor_mesh{std::array{5_st, 5_st, 5_st}, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n\n  std::unordered_map<ElementId<3>, amr::Info<3>> child_1_neighbor_info{\n      {neighbor_1_id,\n       amr::Info<3>{std::array{amr::Flag::DoNothing, amr::Flag::Join,\n                               amr::Flag::DoNothing},\n                    neighbor_mesh}},\n      {neighbor_2_id,\n       amr::Info<3>{std::array{amr::Flag::DoNothing, amr::Flag::Join,\n                               amr::Flag::DoNothing},\n                    neighbor_mesh}},\n      {child_2_id, child_2_info},\n      {child_3_id, child_3_info},\n      {neighbor_7_id, amr::Info<3>{std::array{amr::Flag::Join, amr::Flag::Join,\n                                              amr::Flag::DoNothing},\n                                   neighbor_mesh}},\n      {neighbor_8_id, amr::Info<3>{std::array{amr::Flag::Join, amr::Flag::Join,\n                                              amr::Flag::DoNothing},\n                                   neighbor_mesh}}};\n\n  std::unordered_map<ElementId<3>, amr::Info<3>> child_2_neighbor_info{\n      {neighbor_3_id,\n       amr::Info<3>{std::array{amr::Flag::DoNothing, amr::Flag::DoNothing,\n                               amr::Flag::DoNothing},\n                    neighbor_mesh}},\n      {neighbor_4_id,\n       amr::Info<3>{std::array{amr::Flag::DoNothing, amr::Flag::Split,\n                               amr::Flag::DoNothing},\n                    neighbor_mesh}},\n      {child_2_id, child_2_info},\n      {child_1_id, child_1_info}};\n\n  std::unordered_map<ElementId<3>, amr::Info<3>> child_3_neighbor_info{\n      {child_1_id, child_1_info},\n      {child_2_id, child_2_info},\n      {neighbor_4_id,\n       amr::Info<3>{std::array{amr::Flag::DoNothing, amr::Flag::Split,\n                               amr::Flag::DoNothing},\n                    neighbor_mesh}},\n      {neighbor_5_id,\n       amr::Info<3>{std::array{amr::Flag::DoNothing, amr::Flag::DoNothing,\n                               amr::Flag::DoNothing},\n                    neighbor_mesh}},\n      {neighbor_6_id,\n       amr::Info<3>{std::array{amr::Flag::DoNothing, amr::Flag::DoNothing,\n                               amr::Flag::DoNothing},\n                    neighbor_mesh}}};\n\n  using TaggedTupleType =\n      tuples::TaggedTuple<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                          Parallel::Tags::ArrayIndex<ElementId<3>>,\n                          Parallel::Tags::GlobalCache<Metavariables>,\n                          domain::Tags::Element<3>, domain::Tags::Mesh<3>,\n                          domain::Tags::NeighborMesh<3>, amr::Tags::Info<3>,\n                          amr::Tags::NeighborInfo<3>>;\n  std::unordered_map<ElementId<3>, TaggedTupleType> children_items;\n  DirectionalIdMap<3, Mesh<3>> unused_child_neighbor_mesh{};\n  children_items.emplace(\n      child_1_id,\n      TaggedTupleType{Metavariables{}, child_1_id, nullptr, std::move(child_1),\n                      std::move(child_1_mesh), unused_child_neighbor_mesh,\n                      std::move(child_1_info),\n                      std::move(child_1_neighbor_info)});\n  children_items.emplace(\n      child_2_id,\n      TaggedTupleType{Metavariables{}, child_2_id, nullptr, std::move(child_2),\n                      std::move(child_2_mesh), unused_child_neighbor_mesh,\n                      std::move(child_2_info),\n                      std::move(child_2_neighbor_info)});\n  children_items.emplace(\n      child_3_id,\n      TaggedTupleType{Metavariables{}, child_3_id, nullptr, std::move(child_3),\n                      std::move(child_3_mesh), unused_child_neighbor_mesh,\n                      std::move(child_3_info),\n                      std::move(child_3_neighbor_info)});\n\n  DirectionMap<3, Neighbors<3>> expected_parent_neighbors{};\n  expected_parent_neighbors.emplace(\n      Direction<3>::lower_xi(),\n      Neighbors<3>{std::unordered_set{neighbor_12_id, neighbor_6_id},\n                   b3_orientation});\n  expected_parent_neighbors.emplace(\n      Direction<3>::upper_xi(),\n      Neighbors<3>{std::unordered_set{neighbor_10_id, neighbor_11_id},\n                   b1_orientation});\n  expected_parent_neighbors.emplace(\n      Direction<3>::lower_eta(),\n      Neighbors<3>{std::unordered_set{neighbor_9_id, neighbor_3_id},\n                   b4_orientation});\n  expected_parent_neighbors.emplace(\n      Direction<3>::upper_eta(),\n      Neighbors<3>{std::unordered_set{neighbor_5_id}, b2_orientation});\n  Element<3> expected_parent{parent_id, std::move(expected_parent_neighbors)};\n\n  DirectionalIdMap<3, Mesh<3>> expected_parent_neighbor_mesh{};\n  expected_parent_neighbor_mesh.emplace(\n      DirectionalId<3>{Direction<3>::lower_xi(), neighbor_6_id}, neighbor_mesh);\n  expected_parent_neighbor_mesh.emplace(\n      DirectionalId<3>{Direction<3>::lower_xi(), neighbor_12_id},\n      neighbor_mesh);\n  expected_parent_neighbor_mesh.emplace(\n      DirectionalId<3>{Direction<3>::upper_xi(), neighbor_10_id},\n      neighbor_mesh);\n  expected_parent_neighbor_mesh.emplace(\n      DirectionalId<3>{Direction<3>::upper_xi(), neighbor_11_id},\n      neighbor_mesh);\n  expected_parent_neighbor_mesh.emplace(\n      DirectionalId<3>{Direction<3>::lower_eta(), neighbor_3_id},\n      neighbor_mesh);\n  expected_parent_neighbor_mesh.emplace(\n      DirectionalId<3>{Direction<3>::lower_eta(), neighbor_9_id},\n      neighbor_mesh);\n  expected_parent_neighbor_mesh.emplace(\n      DirectionalId<3>{Direction<3>::upper_eta(), neighbor_5_id},\n      neighbor_mesh);\n\n  const amr::Info<3> expected_parent_info{\n      std::array{amr::Flag::Undefined, amr::Flag::Undefined,\n                 amr::Flag::Undefined},\n      Mesh<3>{}};\n  const std::unordered_map<ElementId<3>, amr::Info<3>>\n      expected_parent_neighbor_info{};\n\n  using array_component = Component<Metavariables>;\n  using registrar = TestHelpers::amr::Registrar<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_group_component<registrar>(&runner);\n  ActionTesting::emplace_component<array_component>(&runner, parent_id);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<3>>(\n            runner, 0)\n            .empty());\n  ActionTesting::simple_action<array_component, amr::Actions::InitializeParent>(\n      make_not_null(&runner), parent_id, children_items);\n  CHECK(\n      ActionTesting::get_databox_tag<array_component, domain::Tags::Element<3>>(\n          runner, parent_id) == expected_parent);\n  CHECK(ActionTesting::get_databox_tag<array_component, domain::Tags::Mesh<3>>(\n            runner, parent_id) == expected_parent_mesh);\n  CHECK(ActionTesting::get_databox_tag<array_component,\n                                       domain::Tags::NeighborMesh<3>>(\n            runner, parent_id) == expected_parent_neighbor_mesh);\n  CHECK(ActionTesting::get_databox_tag<array_component, amr::Tags::Info<3>>(\n            runner, parent_id) == expected_parent_info);\n  CHECK(ActionTesting::get_databox_tag<array_component,\n                                       amr::Tags::NeighborInfo<3>>(\n            runner, parent_id) == expected_parent_neighbor_info);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<3>>(\n            runner, 0)\n            .empty());\n  ActionTesting::invoke_queued_simple_action<registrar>(make_not_null(&runner),\n                                                        0);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<3>>(\n            runner, 0) == std::unordered_set{parent_id});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.InitializeParent\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_SendDataToChildren.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <pup.h>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Helpers.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/Domain/Amr/RegistrationHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Protocols/RegistrationMetavariables.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendDataToChildren.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\n\nElement<1> create_parent() {\n  const ElementId<1> parent_id{0, std::array{SegmentId{2, 1}}};\n  const ElementId<1> parent_lower_neighbor_id{0, std::array{SegmentId{2, 0}}};\n  const ElementId<1> parent_upper_neighbor_id{0, std::array{SegmentId{2, 2}}};\n  DirectionMap<1, Neighbors<1>> parent_neighbors{};\n  OrientationMap<1> aligned = OrientationMap<1>::create_aligned();\n  parent_neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors<1>{std::unordered_set{parent_lower_neighbor_id}, aligned});\n  parent_neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors<1>{std::unordered_set{parent_upper_neighbor_id}, aligned});\n  return Element<1>{parent_id, std::move(parent_neighbors)};\n}\n\nMesh<1> create_parent_mesh() {\n  return Mesh<1>{3, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n}\n\namr::Info<1> create_parent_info() {\n  return amr::Info<1>{std::array{amr::Flag::Split},\n                      Mesh<1>{4, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto}};\n}\n\nstd::unordered_map<ElementId<1>, amr::Info<1>> create_parent_neighbor_info() {\n  const ElementId<1> parent_lower_neighbor_id{0, std::array{SegmentId{2, 0}}};\n  const ElementId<1> parent_upper_neighbor_id{0, std::array{SegmentId{2, 2}}};\n  const Mesh<1> neighbor_mesh{5, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n  std::unordered_map<ElementId<1>, amr::Info<1>> result{};\n  result.emplace(parent_lower_neighbor_id,\n                 amr::Info<1>{std::array{amr::Flag::DoNothing}, neighbor_mesh});\n  result.emplace(parent_upper_neighbor_id,\n                 amr::Info<1>{std::array{amr::Flag::DoNothing}, neighbor_mesh});\n  return result;\n}\n\nstruct MockInitializeChild {\n  template <typename ParallelComponent, typename DataBox,\n            typename Metavariables, typename... Tags>\n  static void apply(DataBox& /*box*/,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ElementId<Metavariables::volume_dim>& /*child_id*/,\n                    const tuples::TaggedTuple<Tags...>& parent_items) {\n    CHECK(get<domain::Tags::Element<1>>(parent_items) == create_parent());\n    CHECK(get<domain::Tags::Mesh<1>>(parent_items) == create_parent_mesh());\n    CHECK(get<amr::Tags::Info<1>>(parent_items) == create_parent_info());\n    CHECK(get<amr::Tags::NeighborInfo<1>>(parent_items) ==\n          create_parent_neighbor_info());\n  }\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags = tmpl::list<>;\n  using simple_tags =\n      tmpl::list<domain::Tags::Element<volume_dim>,\n                 domain::Tags::Mesh<volume_dim>, amr::Tags::Info<volume_dim>,\n                 amr::Tags::NeighborInfo<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n  using replace_these_simple_actions =\n      tmpl::list<amr::Actions::InitializeChild>;\n  using with_these_simple_actions = tmpl::list<MockInitializeChild>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<Component<Metavariables>,\n                                    TestHelpers::amr::Registrar<Metavariables>>;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n\n  struct registration\n      : tt::ConformsTo<Parallel::protocols::RegistrationMetavariables> {\n    using element_registrars =\n        tmpl::map<tmpl::pair<Component<Metavariables>,\n                             tmpl::list<TestHelpers::amr::RegisterElement>>>;\n  };\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = Component<Metavariables>;\n    using projectors = tmpl::list<>;\n    static constexpr bool keep_coarse_grids = false;\n    [[maybe_unused]] static constexpr bool p_refine_only_in_event = false;\n  };\n};\n\nvoid test() {\n  const auto parent = create_parent();\n  const ElementId<1>& parent_id = parent.id();\n  const auto parent_mesh = create_parent_mesh();\n  const auto parent_info = create_parent_info();\n  const auto parent_neighbor_info = create_parent_neighbor_info();\n  const auto children_ids = amr::ids_of_children(parent_id, parent_info.flags);\n\n  using array_component = Component<Metavariables>;\n  using registrar = TestHelpers::amr::Registrar<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component_and_initialize<array_component>(\n      &runner, parent_id,\n      {parent, parent_mesh, parent_info, parent_neighbor_info});\n  for (const auto& child_id : children_ids) {\n    ActionTesting::emplace_component<array_component>(&runner, child_id);\n    CHECK(ActionTesting::number_of_queued_simple_actions<array_component>(\n              runner, child_id) == 0);\n  }\n  ActionTesting::emplace_group_component_and_initialize<registrar>(\n      &runner, std::unordered_set{parent_id});\n\n  ActionTesting::simple_action<array_component,\n                               amr::Actions::SendDataToChildren>(\n      make_not_null(&runner), parent_id, children_ids);\n  // SendDataToChildren calls InitializeChild on each child element\n  for (const auto& child_id : children_ids) {\n    CHECK(ActionTesting::number_of_queued_simple_actions<array_component>(\n              runner, child_id) == 1);\n    // Invoke the mock action to check that SendDataToChildren sent the correct\n    // data\n    ActionTesting::invoke_queued_simple_action<array_component>(\n        make_not_null(&runner), child_id);\n    CHECK(ActionTesting::is_simple_action_queue_empty<array_component>(\n        runner, child_id));\n  }\n  CHECK(ActionTesting::number_of_queued_simple_actions<registrar>(runner, 0) ==\n        1);\n  ActionTesting::invoke_queued_simple_action<registrar>(make_not_null(&runner),\n                                                        0);\n  CHECK(ActionTesting::get_databox_tag<registrar,\n                                       TestHelpers::amr::RegisteredElements<1>>(\n            runner, 0) == std::unordered_set<ElementId<1>>{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.SendDataToChildren\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Actions/Test_UpdateAmrDecision.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Amr/Info.hpp\"\n#include \"Domain/Amr/Tags/Flags.hpp\"\n#include \"Domain/Amr/Tags/NeighborFlags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/UpdateAmrDecision.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  static constexpr size_t volume_dim = Metavariables::volume_dim;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<volume_dim>;\n  using const_global_cache_tags = tmpl::list<amr::Tags::Policies>;\n  using simple_tags =\n      tmpl::list<domain::Tags::Mesh<volume_dim>,\n                 domain::Tags::Element<volume_dim>, amr::Tags::Info<volume_dim>,\n                 amr::Tags::NeighborInfo<volume_dim>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<Component<Metavariables>>;\n};\n\nvoid check(const ActionTesting::MockRuntimeSystem<Metavariables>& runner,\n           const ElementId<1>& id, const amr::Info<1>& expected_info,\n           const std::unordered_map<ElementId<1>, amr::Info<1>>&\n               expected_neighbor_info,\n           const size_t expected_queued_actions) {\n  using my_component = Component<Metavariables>;\n  CHECK(ActionTesting::get_databox_tag<my_component, amr::Tags::Info<1>>(\n            runner, id) == expected_info);\n  CHECK(\n      ActionTesting::get_databox_tag<my_component, amr::Tags::NeighborInfo<1>>(\n          runner, id) == expected_neighbor_info);\n  CHECK(ActionTesting::number_of_queued_simple_actions<my_component>(\n            runner, id) == expected_queued_actions);\n}\n\n// When AMR is run, the simple action EvaluateAmrCriteria is run on each\n// Element.  EvaluateAmrCriteria evaluates the criteria which determine the\n// amr::Tags::Info of the Element, and then calls the simple action\n// UpdateAmrDecision on each neighboring Element of the Element sending the\n// Info. UpdateAmrDecision checks to see if an Elements Info need to change\n// based on the received NeighborInfo (e.g. if and element wants to join, but\n// its sibling does not the element must change its decision to do nothing).  If\n// the element's Info are changed, then it calls UpdateAmrDecision on its\n// neighbors, and the process continues until no Element wants to change its\n// decision.   This test manually runs this process on three elements assuming\n// each Element has already evaluted its own Info with the ones passed to\n// emplace_component_and_initialize.\nvoid test() {\n  using my_component = Component<Metavariables>;\n\n  const ElementId<1> self_id(0, {{{2, 1}}});\n  const ElementId<1> sibling_id(0, {{{2, 0}}});\n  const ElementId<1> cousin_id(1, {{{2, 2}}});\n  const Mesh<1> mesh{2_st, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  std::unordered_map<ElementId<1>, amr::Info<1>> initial_neighbor_info{};\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true}}};\n\n  const Element<1> self(\n      self_id, {{{Direction<1>::lower_xi(),\n                  {{sibling_id}, OrientationMap<1>::create_aligned()}},\n                 {Direction<1>::upper_xi(),\n                  {{cousin_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, self_id,\n      {mesh, self, amr::Info<1>{std::array{amr::Flag::Join}, mesh},\n       initial_neighbor_info});\n\n  const Element<1> sibling(\n      sibling_id, {{{Direction<1>::upper_xi(),\n                     {{self_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, sibling_id,\n      {mesh, sibling, amr::Info<1>{std::array{amr::Flag::Join}, mesh},\n       initial_neighbor_info});\n\n  const Element<1> cousin(\n      cousin_id, {{{Direction<1>::lower_xi(),\n                    {{self_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, cousin_id,\n      {mesh, cousin, amr::Info<1>{std::array{amr::Flag::Split}, mesh},\n       initial_neighbor_info});\n\n  check(runner, self_id, {{{amr::Flag::Join}}, mesh}, {}, 0);\n  check(runner, sibling_id, {{{amr::Flag::Join}}, mesh}, {}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh}, {}, 0);\n\n  ActionTesting::simple_action<my_component, amr::Actions::UpdateAmrDecision>(\n      make_not_null(&runner), self_id, sibling_id,\n      amr::Info<1>{std::array{amr::Flag::Join}, mesh});\n  // sibling told self it wants to join, no further action triggered\n  check(runner, self_id, {{{amr::Flag::Join}}, mesh},\n        {{sibling_id, {{{amr::Flag::Join}}, mesh}}}, 0);\n  check(runner, sibling_id, {{{amr::Flag::Join}}, mesh}, {}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh}, {}, 0);\n\n  ActionTesting::simple_action<my_component, amr::Actions::UpdateAmrDecision>(\n      make_not_null(&runner), sibling_id, self_id,\n      amr::Info<1>{std::array{amr::Flag::Join}, mesh});\n  // self told sibling it wants to join, no further action triggered\n  check(runner, self_id, {{{amr::Flag::Join}}, mesh},\n        {{sibling_id, {{{amr::Flag::Join}}, mesh}}}, 0);\n  check(runner, sibling_id, {{{amr::Flag::Join}}, mesh},\n        {{self_id, {{{amr::Flag::Join}}, mesh}}}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh}, {}, 0);\n\n  ActionTesting::simple_action<my_component, amr::Actions::UpdateAmrDecision>(\n      make_not_null(&runner), cousin_id, self_id,\n      amr::Info<1>{std::array{amr::Flag::Join}, mesh});\n  // self told cousin it wants to join, no further action triggered\n  check(runner, self_id, {{{amr::Flag::Join}}, mesh},\n        {{sibling_id, {{{amr::Flag::Join}}, mesh}}}, 0);\n  check(runner, sibling_id, {{{amr::Flag::Join}}, mesh},\n        {{self_id, {{{amr::Flag::Join}}, mesh}}}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh},\n        {{self_id, {{{amr::Flag::Join}}, mesh}}}, 0);\n\n  ActionTesting::simple_action<my_component, amr::Actions::UpdateAmrDecision>(\n      make_not_null(&runner), self_id, cousin_id,\n      amr::Info<1>{std::array{amr::Flag::Split}, mesh});\n  // cousin told self it wants to split; in order to maintain 2:1 refinement,\n  // self changes its decision to DoNothing and triggers actions to notify its\n  // neighbors\n  check(runner, self_id, {{{amr::Flag::DoNothing}}, mesh},\n        {{sibling_id, {{{amr::Flag::Join}}, mesh}},\n         {cousin_id, {{{amr::Flag::Split}}, mesh}}},\n        0);\n  check(runner, sibling_id, {{{amr::Flag::Join}}, mesh},\n        {{self_id, {{{amr::Flag::Join}}, mesh}}}, 1);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh},\n        {{self_id, {{{amr::Flag::Join}}, mesh}}}, 1);\n\n  ActionTesting::invoke_queued_simple_action<my_component>(\n      make_not_null(&runner), sibling_id);\n  // self told sibling it wants to DoNothing, so sibling changes its decision to\n  // DoNothing and triggers actions to notify its neighbors\n  check(runner, self_id, {{{amr::Flag::DoNothing}}, mesh},\n        {{sibling_id, {{{amr::Flag::Join}}, mesh}},\n         {cousin_id, {{{amr::Flag::Split}}, mesh}}},\n        1);\n  check(runner, sibling_id, {{{amr::Flag::DoNothing}}, mesh},\n        {{self_id, {{{amr::Flag::DoNothing}}, mesh}}}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh},\n        {{self_id, {{{amr::Flag::Join}}, mesh}}}, 1);\n\n  ActionTesting::invoke_queued_simple_action<my_component>(\n      make_not_null(&runner), cousin_id);\n  // self told cousin it wants to DoNothing, no further action triggered\n  check(runner, self_id, {{{amr::Flag::DoNothing}}, mesh},\n        {{sibling_id, {{{amr::Flag::Join}}, mesh}},\n         {cousin_id, {{{amr::Flag::Split}}, mesh}}},\n        1);\n  check(runner, sibling_id, {{{amr::Flag::DoNothing}}, mesh},\n        {{self_id, {{{amr::Flag::DoNothing}}, mesh}}}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh},\n        {{self_id, {{{amr::Flag::DoNothing}}, mesh}}}, 0);\n\n  ActionTesting::invoke_queued_simple_action<my_component>(\n      make_not_null(&runner), self_id);\n  // sibling told self it wants to DoNothing, no further action triggered\n  check(runner, self_id, {{{amr::Flag::DoNothing}}, mesh},\n        {{sibling_id, {{{amr::Flag::DoNothing}}, mesh}},\n         {cousin_id, {{{amr::Flag::Split}}, mesh}}},\n        0);\n  check(runner, sibling_id, {{{amr::Flag::DoNothing}}, mesh},\n        {{self_id, {{{amr::Flag::DoNothing}}, mesh}}}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh},\n        {{self_id, {{{amr::Flag::DoNothing}}, mesh}}}, 0);\n}\n\n// This test checks that asynchronus execution of simple actions is handled\n// correctly in case UpdateAmrDecision is called on an Element before\n// EvaluateAmrCriteria, or if two calls to UpdateAmrDecision are triggered by\n// receiving messages from a neighbor in the opposite order in which they were\n// sent\nvoid test_race_conditions() {\n  using my_component = Component<Metavariables>;\n\n  const ElementId<1> self_id(0, {{{2, 1}}});\n  const ElementId<1> sibling_id(0, {{{2, 0}}});\n  const ElementId<1> cousin_id(1, {{{2, 2}}});\n  const Mesh<1> mesh{2_st, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  std::unordered_map<ElementId<1>, amr::Info<1>> initial_neighbor_info{};\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n\n  const Element<1> self(\n      self_id, {{{Direction<1>::lower_xi(),\n                  {{sibling_id}, OrientationMap<1>::create_aligned()}},\n                 {Direction<1>::upper_xi(),\n                  {{cousin_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, self_id,\n      {mesh, self, amr::Info<1>{std::array{amr::Flag::Split}, mesh},\n       std::unordered_map<ElementId<1>, amr::Info<1>>{\n           {cousin_id, {{{amr::Flag::Split}}, mesh}}}});\n\n  const Element<1> sibling(\n      sibling_id, {{{Direction<1>::upper_xi(),\n                     {{self_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, sibling_id,\n      {mesh, sibling, amr::Info<1>{std::array{amr::Flag::Undefined}, Mesh<1>{}},\n       initial_neighbor_info});\n\n  const Element<1> cousin(\n      cousin_id, {{{Direction<1>::lower_xi(),\n                    {{self_id}, OrientationMap<1>::create_aligned()}}}});\n  ActionTesting::emplace_component_and_initialize<my_component>(\n      &runner, cousin_id,\n      {mesh, cousin, amr::Info<1>{std::array{amr::Flag::Split}, mesh},\n       initial_neighbor_info});\n\n  ActionTesting::simple_action<my_component, amr::Actions::UpdateAmrDecision>(\n      make_not_null(&runner), sibling_id, self_id,\n      amr::Info<1>{std::array{amr::Flag::Split}, mesh});\n  // self told sibling it wants to split, but sibling hasn't evaluated its own\n  // flags, store the received flags and exit\n  check(runner, sibling_id, {{{amr::Flag::Undefined}}, Mesh<1>{}},\n        {{self_id, {{{amr::Flag::Split}}, mesh}}}, 0);\n  check(runner, self_id, {{{amr::Flag::Split}}, mesh},\n        {{cousin_id, {{{amr::Flag::Split}}, mesh}}}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh}, initial_neighbor_info,\n        0);\n\n  ActionTesting::simple_action<my_component, amr::Actions::UpdateAmrDecision>(\n      make_not_null(&runner), self_id, cousin_id,\n      amr::Info<1>{std::array{amr::Flag::DoNothing}, mesh});\n  // cousin first chose to do nothing, then chose to split, but the messages\n  // were received in the reverse order. Therefore we ignore the lower priority\n  // message of doing nothing.\n  check(runner, sibling_id, {{{amr::Flag::Undefined}}, Mesh<1>{}},\n        {{self_id, {{{amr::Flag::Split}}, mesh}}}, 0);\n  check(runner, self_id, {{{amr::Flag::Split}}, mesh},\n        {{cousin_id, {{{amr::Flag::Split}}, mesh}}}, 0);\n  check(runner, cousin_id, {{{amr::Flag::Split}}, mesh}, initial_neighbor_info,\n        0);\n}\n\n// When AMR is run, the simple action EvaluateAmrCriteria is run on\n// each Element.  EvaluateAmrCriteria evaluates the criteria which\n// determine the amr::Tags::Info of the Element, and then calls the\n// simple action UpdateAmrDecision on each neighboring Element of the\n// Element sending the Info. UpdateAmrDecision checks to see if an\n// Elements Info need to change based on the received NeighborInfo\n// (e.g. if and element wants to join, but its sibling does not the\n// element must change its decision to do nothing).  If the element's\n// Info are changed, then it calls UpdateAmrDecision on its neighbors,\n// and the process continues until no Element wants to change its\n// decision.  This test manually runs this process on a set of\n// elements assuming each Element has already evaluted its own Info\n// with the ones passed to emplace_component_and_initialize.\n//\n// - We will create 7 elements in 1D labeled by Ids 0-6 from left to right\n// - N is the number of grid points of the initial element\n// - NewN is number of grid points the element (or its child/parent) will have\n//   after refinement\n// - Initial F is the amr::Flag corresponding to that chosen by\n//   EvaluateRefinementCriteria (which is assumed to have already been done)\n// - Final F is the amr::Flag the element should have after the chain of\n//   UpdateAmrDecision actions are executed\n// - J = Join, DR = DecreaseResolution, DN = DoNothing,\n//   IR = IncreaseResolution, S = Split\n//\n// Initial   Final state  Note\n// Id N F    NewN F\n//  0 7 DR    6   DR\n//  1 4 S     4   S\n//  2 6 J     6   DN      2 and 3 cannot join at Lev0 as 1 split into Lev2\n//  3 5 J     5   DN\n//  4 2 J     4   J       Joined 4 and 5 gets larger Mesh of the two\n//  5 4 J     4   J\n//  6 4 IR    5   IR\ntemplate <typename Generator>\nvoid test_mesh_update(gsl::not_null<Generator*> generator) {\n  using my_component = Component<Metavariables>;\n\n  const auto join = std::array{amr::Flag::Join};\n  const auto restrict = std::array{amr::Flag::DecreaseResolution};\n  const auto stay = std::array{amr::Flag::DoNothing};\n  const auto prolong = std::array{amr::Flag::IncreaseResolution};\n  const auto split = std::array{amr::Flag::Split};\n\n  // seven elements, first six at refinement level 1, last at level 0\n  const std::vector<ElementId<1>> ids{\n      {0, {{{1, 0}}}}, {0, {{{1, 1}}}}, {1, {{{1, 0}}}}, {1, {{{1, 1}}}},\n      {2, {{{1, 0}}}}, {2, {{{1, 1}}}}, {3, {{{0, 0}}}}};\n\n  std::vector<Element<1>> elements;\n  elements.reserve(7_st);\n  elements.emplace_back(Element<1>(\n      ids[0], {{{Direction<1>::upper_xi(),\n                 {{ids[1]}, OrientationMap<1>::create_aligned()}}}}));\n  for (size_t i = 1; i < 6; ++i) {\n    elements.emplace_back(Element<1>(\n        ids[i], {{{Direction<1>::lower_xi(),\n                   {{ids[i - 1]}, OrientationMap<1>::create_aligned()}},\n                  {Direction<1>::upper_xi(),\n                   {{ids[i + 1]}, OrientationMap<1>::create_aligned()}}}}));\n  }\n  elements.emplace_back(Element<1>(\n      ids[6], {{{Direction<1>::lower_xi(),\n                 {{ids[5]}, OrientationMap<1>::create_aligned()}}}}));\n\n  const auto initial_extents =\n      std::vector{7_st, 4_st, 6_st, 5_st, 2_st, 4_st, 4_st};\n\n  std::vector<Mesh<1>> initial_meshes;\n  initial_meshes.reserve(7_st);\n  for (size_t i = 0; i < 7; ++i) {\n    initial_meshes.emplace_back(initial_extents[i], Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto);\n  }\n\n  const auto initial_flags =\n      std::vector{restrict, split, join, join, join, join, prolong};\n\n  std::vector<amr::Info<1>> initial_infos;\n  initial_infos.reserve(7_st);\n  // first element is restricted, so needs mesh with extent 6\n  initial_infos.emplace_back(amr::Info<1>{initial_flags[0], initial_meshes[2]});\n  for (size_t i = 1; i < 6; ++i) {\n    initial_infos.emplace_back(\n        amr::Info<1>{initial_flags[i], initial_meshes[i]});\n  }\n  // last element is prolonged, so needs new_mesh with extent 5\n  initial_infos.emplace_back(amr::Info<1>{initial_flags[6], initial_meshes[3]});\n\n  std::unordered_map<ElementId<1>, amr::Info<1>> initial_neighbor_info;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n\n  // initialize components assuming EvaluateAmrCriteria has been executed\n  for (size_t i = 0; i < 7; ++i) {\n    ActionTesting::emplace_component_and_initialize<my_component>(\n        &runner, ids[i],\n        {initial_meshes[i], elements[i], initial_infos[i],\n         initial_neighbor_info});\n  }\n\n  // manually queue the actions that EvaluateAmrCriteria would have called\n  // this sends the amr::Info of an element to its neighbors\n  for (size_t i = 1; i < 7; ++i) {\n    ActionTesting::queue_simple_action<my_component,\n                                       amr::Actions::UpdateAmrDecision>(\n        make_not_null(&runner), ids[i], ids[i - 1], initial_infos[i - 1]);\n    ActionTesting::queue_simple_action<my_component,\n                                       amr::Actions::UpdateAmrDecision>(\n        make_not_null(&runner), ids[i - 1], ids[i], initial_infos[i]);\n  }\n\n  // Now execute a random queued action on a random component until no component\n  // has queued actions\n  auto array_indices_with_queued_simple_actions =\n      ActionTesting::array_indices_with_queued_simple_actions<\n          typename Metavariables::component_list>(make_not_null(&runner));\n\n  while (ActionTesting::number_of_elements_with_queued_simple_actions<\n             typename Metavariables::component_list>(\n             array_indices_with_queued_simple_actions) > 0) {\n    ActionTesting::invoke_random_queued_simple_action<\n        typename Metavariables::component_list>(\n        make_not_null(&runner), generator,\n        array_indices_with_queued_simple_actions);\n    array_indices_with_queued_simple_actions =\n        ActionTesting::array_indices_with_queued_simple_actions<\n            typename Metavariables::component_list>(make_not_null(&runner));\n  }\n\n  // check the final state of the info and neighbor info on each element\n  const auto expected_new_extents =\n      std::vector{6_st, 4_st, 6_st, 5_st, 4_st, 4_st, 5_st};\n  const auto expected_flags =\n      std::vector{restrict, split, stay, stay, join, join, prolong};\n  std::vector<amr::Info<1>> expected_infos;\n  expected_infos.reserve(7_st);\n  for (size_t i = 0; i < 7; ++i) {\n    expected_infos.emplace_back(\n        amr::Info<1>{expected_flags[i],\n                     Mesh<1>(expected_new_extents[i], Spectral::Basis::Legendre,\n                             Spectral::Quadrature::GaussLobatto)});\n  }\n\n  std::vector<std::unordered_map<ElementId<1>, amr::Info<1>>>\n      expected_neighbor_infos{7_st};\n  for (size_t i = 1; i < 7; ++i) {\n    expected_neighbor_infos[i - 1].emplace(ids[i], expected_infos[i]);\n    expected_neighbor_infos[i].emplace(ids[i - 1], expected_infos[i - 1]);\n  }\n\n  for (size_t i = 0; i < 7; ++i) {\n    check(runner, ids[i], expected_infos[i], expected_neighbor_infos[i], 0);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Actions.UpdateAmrDecision\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  MAKE_GENERATOR(generator);\n  test();\n  test_race_conditions();\n  for (size_t i = 0; i < 1000; ++i) {\n    test_mesh_update(make_not_null(&generator));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelAmr\")\n\nset(LIBRARY_SOURCES\n  Actions/Test_AdjustDomain.cpp\n  Actions/Test_CollectDataFromChildren.cpp\n  Actions/Test_CreateChild.cpp\n  Actions/Test_CreateParent.cpp\n  Actions/Test_EvaluateRefinementCriteria.cpp\n  Actions/Test_Initialize.cpp\n  Actions/Test_InitializeChild.cpp\n  Actions/Test_InitializeParent.cpp\n  Actions/Test_SendDataToChildren.cpp\n  Actions/Test_UpdateAmrDecision.cpp\n  Criteria/Test_Constraints.cpp\n  Criteria/Test_Criterion.cpp\n  Criteria/Test_DriveToTarget.cpp\n  Criteria/Test_IncreaseResolution.cpp\n  Criteria/Test_Loehner.cpp\n  Criteria/Test_Persson.cpp\n  Criteria/Test_Random.cpp\n  Criteria/Test_TruncationError.cpp\n  Criteria/Test_Type.cpp\n  Events/Test_ObserveAmrCriteria.cpp\n  Events/Test_RefineMesh.cpp\n  Policies/Test_EnforcePolicies.cpp\n  Policies/Test_Isotropy.cpp\n  Policies/Test_Limits.cpp\n  Policies/Test_Policies.cpp\n  Projectors/Test_CopyFromCreatorOrLeaveAsIs.cpp\n  Projectors/Test_DefaultInitialize.cpp\n  Projectors/Test_Mesh.cpp\n  Projectors/Test_Tensors.cpp\n  Projectors/Test_Variables.cpp\n  Test_Protocols.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Amr\n  AmrCriteria\n  AmrEvents\n  AmrPolicies\n  AmrProjectors\n  CoordinateMaps\n  Domain\n  DomainStructure\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_Constraints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/DiscreteRotation.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Constraints.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Criteria {\nnamespace {\n\ntemplate <size_t Dim>\nstruct TestVector : db::SimpleTag {\n  using type = tnsr::ia<DataVector, Dim>;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<amr::Criterion,\n                   tmpl::list<Constraints<Dim, tmpl::list<TestVector<Dim>>>>>>;\n  };\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.Constraints\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  static constexpr size_t Dim = 2;\n  using Affine = domain::CoordinateMaps::Affine;\n  using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n  register_factory_classes_with_charm<Metavariables<Dim>>();\n  const auto criterion = TestHelpers::test_factory_creation<\n      amr::Criterion, Constraints<Dim, tmpl::list<TestVector<Dim>>>>(\n      \"Constraints:\\n\"\n      \"  ConstraintsToMonitor: [TestVector]\\n\"\n      \"  AbsoluteTarget: 1.e-3\\n\"\n      \"  CoarseningFactor: 0.1\\n\");\n\n  // Set up a grid\n  const Mesh<Dim> mesh{4, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n  // Logical to inertial coords is a rotation by pi/2 so the directions are\n  // swapped\n  const auto map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine2D{Affine{-1., 1., -1., 1.}, Affine{-1., 1., -1., 1.}},\n          domain::CoordinateMaps::DiscreteRotation(OrientationMap<Dim>{\n              {{Direction<Dim>::lower_eta(), Direction<Dim>::upper_xi()}}}));\n  auto jacobian = map.jacobian(logical_coords);\n\n  // Manufacture some test data\n  tnsr::ia<DataVector, Dim> test_data{mesh.number_of_grid_points()};\n  // X-component (logical eta direction) is small\n  get<0, 0>(test_data) = 1.e-6;\n  get<0, 1>(test_data) = 1.e-7;\n  get<0, 2>(test_data) = 1.e-8;\n  // Y-component (logical xi direction) is large\n  get<1, 0>(test_data) = 1.e-1;\n  get<1, 1>(test_data) = 1.e-2;\n  get<1, 2>(test_data) = 1.e-3;\n\n  Parallel::GlobalCache<Metavariables<Dim>> empty_cache{};\n  auto databox =\n      db::create<tmpl::list<Events::Tags::ObserverJacobian<\n                                Dim, Frame::ElementLogical, Frame::Inertial>,\n                            TestVector<Dim>>>(std::move(jacobian),\n                                              std::move(test_data));\n  ObservationBox<\n      tmpl::list<>,\n      db::DataBox<tmpl::list<Events::Tags::ObserverJacobian<\n                                 Dim, Frame::ElementLogical, Frame::Inertial>,\n                             TestVector<Dim>>>>\n      box{make_not_null(&databox)};\n\n  const auto flags = criterion->evaluate(box, empty_cache, ElementId<Dim>{0});\n  CHECK(flags[0] == amr::Flag::IncreaseResolution);\n  CHECK(flags[1] == amr::Flag::DecreaseResolution);\n}\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_Criterion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// [criterion_examples]\nstruct FieldOne : db::SimpleTag {\n  using type = double;\n};\n\nstruct FieldTwo : db::SimpleTag {\n  using type = double;\n};\n\nstruct Constraint : db::SimpleTag {\n  using type = double;\n};\n\nstruct ConstraintCompute : db::ComputeTag, Constraint {\n  using base = Constraint;\n  using return_type = double;\n  using argument_tags = tmpl::list<FieldOne, FieldTwo>;\n  static void function(const gsl::not_null<double*> result,\n                       const double field_one, const double field_two) {\n    *result = field_one - field_two;\n  }\n};\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\nclass CriterionOne : public amr::Criterion {\n public:\n  struct CriticalValue {\n    using type = double;\n    static constexpr Options::String help = {\n        \"The critical value of field one .\"};\n  };\n  using options = tmpl::list<CriticalValue>;\n\n  static constexpr Options::String help = {\n      \"h-refine the grid if field one is above a critical value\"};\n\n  CriterionOne() = default;\n  explicit CriterionOne(const double critical_value)\n      : critical_value_(critical_value) {}\n  explicit CriterionOne(CkMigrateMessage* msg) : Criterion(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CriterionOne);  // NOLINT\n\n  amr::Criteria::Type type() override { return amr::Criteria::Type::h; }\n\n  std::string observation_name() override { return \"CriterionOne\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using argument_tags = tmpl::list<FieldOne>;\n\n  template <typename Metavariables>\n  auto operator()(\n      const double field_one, Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Metavariables::volume_dim>& /*element_id*/) const {\n    return field_one > critical_value_ ? std::array{amr::Flag::Split}\n                                       : std::array{amr::Flag::DoNothing};\n  }\n\n  void pup(PUP::er& p) override {\n    Criterion::pup(p);\n    p | critical_value_;\n  }\n\n private:\n  double critical_value_{0.0};\n};\n\nPUP::able::PUP_ID CriterionOne::my_PUP_ID = 0;  // NOLINT\n\nclass CriterionTwo : public amr::Criterion {\n public:\n  struct TargetValue {\n    using type = double;\n    static constexpr Options::String help = {\"The target value.\"};\n  };\n  using options = tmpl::list<TargetValue>;\n\n  static constexpr Options::String help = {\n      \"h-refine if the absolute value of the constraint is above the target \"\n      \"value.  h-coarsen if the constraint is an order of magnitude below the \"\n      \"target value\"};\n\n  CriterionTwo() = default;\n  explicit CriterionTwo(const double target_value)\n      : target_value_(target_value) {}\n  explicit CriterionTwo(CkMigrateMessage* /*msg*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CriterionTwo);  // NOLINT\n\n  amr::Criteria::Type type() override { return amr::Criteria::Type::h; }\n\n  std::string observation_name() override { return \"CriterionTwo\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<ConstraintCompute>;\n  using argument_tags = tmpl::list<Constraint>;\n\n  template <typename Metavariables>\n  auto operator()(\n      const double constraint, Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Metavariables::volume_dim>& /*element_id*/) const {\n    return std::abs(constraint) > target_value_\n               ? std::array{amr::Flag::Split}\n               : (std::abs(constraint) < 0.1 * target_value_\n                      ? std::array{amr::Flag::Join}\n                      : std::array{amr::Flag::DoNothing});\n  }\n\n  void pup(PUP::er& p) override {\n    Criterion::pup(p);\n    p | target_value_;\n  }\n\n private:\n  double target_value_{0.0};\n};\n\nPUP::able::PUP_ID CriterionTwo::my_PUP_ID = 0;  // NOLINT\n#pragma GCC diagnostic pop\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<amr::Criterion, tmpl::list<CriterionOne, CriterionTwo>>>;\n  };\n};\n// [criterion_examples]\n\nvoid test_criterion(const amr::Criterion& criterion, const double field_one,\n                    const double field_two, const amr::Flag expected_flag) {\n  Parallel::GlobalCache<Metavariables> empty_cache{};\n  using simple_tags = tmpl::list<FieldOne, FieldTwo>;\n  auto databox = db::create<simple_tags>(field_one, field_two);\n  // This list is the union of all compute_tags_for_observation_box for all\n  // criteria listed in Metavariables::factory_creation::factory_classes\n  // It can be constructed with a metafunction, but for this simple test\n  // we just explicitly list them\n  using compute_tags = tmpl::list<ConstraintCompute>;\n  ObservationBox<compute_tags, db::DataBox<simple_tags>> box{\n      make_not_null(&databox)};\n  ElementId<1> element_id{0};\n  auto flags = criterion.evaluate(box, empty_cache, element_id);\n  CHECK(flags == std::array{expected_flag});\n}\n\nvoid test() {\n  register_factory_classes_with_charm<Metavariables>();\n  const CriterionOne one{1.0};\n  test_criterion(one, 2.0, 0.0, amr::Flag::Split);\n\n  test_criterion(serialize_and_deserialize(one), 0.5, 0.0,\n                 amr::Flag::DoNothing);\n  const auto one_option =\n      TestHelpers::test_creation<std::unique_ptr<amr::Criterion>,\n                                 Metavariables>(\n          \"CriterionOne:\\n\"\n          \"  CriticalValue: 3.0\\n\");\n  test_criterion(*one_option, 2.0, 0.0, amr::Flag::DoNothing);\n  test_criterion(*serialize_and_deserialize(one_option), 2.0, 0.0,\n                 amr::Flag::DoNothing);\n  const CriterionTwo two{1.e-6};\n  test_criterion(two, 4.e-6, 6.e-6, amr::Flag::Split);\n  test_criterion(serialize_and_deserialize(two), 4.e-6, 4.5e-6,\n                 amr::Flag::DoNothing);\n  const auto two_option =\n      TestHelpers::test_creation<std::unique_ptr<amr::Criterion>,\n                                 Metavariables>(\n          \"CriterionTwo:\\n\"\n          \"  TargetValue: 1.e-5\\n\");\n  test_criterion(*two_option, 4.e-7, 3.e-7, amr::Flag::Join);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.Criterion\", \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_DriveToTarget.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = VolumeDim;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        amr::Criterion,\n        tmpl::list<\n            amr::Criteria::DriveToTarget<VolumeDim, amr::Criteria::Type::h>,\n            amr::Criteria::DriveToTarget<VolumeDim, amr::Criteria::Type::p>>>>;\n  };\n};\nstruct TestComponent {};\n\ntemplate <size_t VolumeDim>\nvoid test_criterion(\n    const amr::Criterion& criterion,\n    const std::array<size_t, VolumeDim> mesh_extents,\n    const std::array<size_t, VolumeDim>& element_refinement_levels,\n    const std::array<amr::Flag, VolumeDim>& expected_flags) {\n  Parallel::GlobalCache<Metavariables<VolumeDim>> empty_cache{};\n  auto databox = db::create<tmpl::list<::domain::Tags::Mesh<VolumeDim>>>(\n      Mesh<VolumeDim>{mesh_extents, Spectral::Basis::Legendre,\n                      Spectral::Quadrature::GaussLobatto});\n  ObservationBox<tmpl::list<>,\n                 db::DataBox<tmpl::list<::domain::Tags::Mesh<VolumeDim>>>>\n      box{make_not_null(&databox)};\n\n  std::array<SegmentId, VolumeDim> segment_ids;\n  alg::transform(element_refinement_levels, segment_ids.begin(),\n                 [](const size_t extent) {\n                   return SegmentId{extent, 0_st};\n                 });\n  ElementId<VolumeDim> element_id{0, segment_ids};\n  auto flags = criterion.evaluate(box, empty_cache, element_id);\n  CHECK(flags == expected_flags);\n}\n\ntemplate <amr::Criteria::Type CriteriaType, size_t VolumeDim>\nvoid test(\n    const std::array<size_t, VolumeDim>& target,\n    const std::array<amr::Flag, VolumeDim>& flags_at_target,\n    const std::vector<\n        std::tuple<std::array<size_t, VolumeDim>, std::array<size_t, VolumeDim>,\n                   std::array<amr::Flag, VolumeDim>>>& test_cases,\n    const std::string& option_string) {\n  register_factory_classes_with_charm<Metavariables<VolumeDim>>();\n\n  const amr::Criteria::DriveToTarget<VolumeDim, CriteriaType> criterion{\n      target, flags_at_target};\n  const auto criterion_from_option_string =\n      TestHelpers::test_creation<std::unique_ptr<amr::Criterion>,\n                                 Metavariables<VolumeDim>>(option_string);\n  for (const auto& [extents, levels, expected_flags] : test_cases) {\n    test_criterion(criterion, extents, levels, expected_flags);\n    test_criterion(*criterion_from_option_string, extents, levels,\n                   expected_flags);\n    test_criterion(*serialize_and_deserialize(criterion_from_option_string),\n                   extents, levels, expected_flags);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.DriveToTarget\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  {\n    INFO(\"1D h\");\n    const std::array target_levels{3_st};\n    const std::array flags_at_target{amr::Flag::Join};\n    const auto test_cases =\n        std::vector{std::tuple{std::array{4_st}, std::array{3_st},\n                               std::array{amr::Flag::Join}},\n                    std::tuple{std::array{3_st}, std::array{3_st},\n                               std::array{amr::Flag::Join}},\n                    std::tuple{std::array{5_st}, std::array{3_st},\n                               std::array{amr::Flag::Join}},\n                    std::tuple{std::array{4_st}, std::array{2_st},\n                               std::array{amr::Flag::Split}},\n                    std::tuple{std::array{3_st}, std::array{2_st},\n                               std::array{amr::Flag::Split}},\n                    std::tuple{std::array{5_st}, std::array{2_st},\n                               std::array{amr::Flag::Split}},\n                    std::tuple{std::array{4_st}, std::array{4_st},\n                               std::array{amr::Flag::Join}},\n                    std::tuple{std::array{3_st}, std::array{4_st},\n                               std::array{amr::Flag::Join}},\n                    std::tuple{std::array{5_st}, std::array{4_st},\n                               std::array{amr::Flag::Join}}};\n    const std::string option =\n        \"DriveToTargetRefinementLevels:\\n\"\n        \"  Target: [3]\\n\"\n        \"  OscillationAtTarget: [Join]\\n\";\n    test<amr::Criteria::Type::h>(target_levels, flags_at_target, test_cases,\n                                 option);\n    CHECK_THROWS_WITH(\n        (test<amr::Criteria::Type::h>(target_levels,\n                                      std::array{amr::Flag::IncreaseResolution},\n                                      test_cases, option)),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot use p-refinement flag in OscillationAtTarget\"));\n  }\n  {\n    INFO(\"1D p\");\n    const std::array target_extents{4_st};\n    const std::array flags_at_target{amr::Flag::DecreaseResolution};\n    const auto test_cases =\n        std::vector{std::tuple{std::array{4_st}, std::array{3_st},\n                               std::array{amr::Flag::DecreaseResolution}},\n                    std::tuple{std::array{3_st}, std::array{3_st},\n                               std::array{amr::Flag::IncreaseResolution}},\n                    std::tuple{std::array{5_st}, std::array{3_st},\n                               std::array{amr::Flag::DecreaseResolution}},\n                    std::tuple{std::array{4_st}, std::array{2_st},\n                               std::array{amr::Flag::DecreaseResolution}},\n                    std::tuple{std::array{3_st}, std::array{2_st},\n                               std::array{amr::Flag::IncreaseResolution}},\n                    std::tuple{std::array{5_st}, std::array{2_st},\n                               std::array{amr::Flag::DecreaseResolution}},\n                    std::tuple{std::array{4_st}, std::array{4_st},\n                               std::array{amr::Flag::DecreaseResolution}},\n                    std::tuple{std::array{3_st}, std::array{4_st},\n                               std::array{amr::Flag::IncreaseResolution}},\n                    std::tuple{std::array{5_st}, std::array{4_st},\n                               std::array{amr::Flag::DecreaseResolution}}};\n    const std::string option =\n        \"DriveToTargetNumberOfGridPoints:\\n\"\n        \"  Target: [4]\\n\"\n        \"  OscillationAtTarget: [DecreaseResolution]\\n\";\n    test<amr::Criteria::Type::p>(target_extents, flags_at_target, test_cases,\n                                 option);\n    CHECK_THROWS_WITH(\n        (test<amr::Criteria::Type::p>(\n            target_extents, std::array{amr::Flag::Join}, test_cases, option)),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot use h-refinement flag in OscillationAtTarget\"));\n  }\n  {\n    INFO(\"2D h\");\n    const std::array target_levels{8_st, 3_st};\n    const std::array flags_at_target{amr::Flag::Join, amr::Flag::Split};\n    const auto test_cases = std::vector{\n        std::tuple{std::array{4_st, 6_st}, std::array{8_st, 3_st},\n                   std::array{amr::Flag::Join, amr::Flag::Split}},\n        std::tuple{std::array{5_st, 6_st}, std::array{8_st, 3_st},\n                   std::array{amr::Flag::Join, amr::Flag::Split}},\n        std::tuple{std::array{3_st, 6_st}, std::array{7_st, 4_st},\n                   std::array{amr::Flag::Split, amr::Flag::Join}},\n        std::tuple{std::array{4_st, 6_st}, std::array{8_st, 2_st},\n                   std::array{amr::Flag::DoNothing, amr::Flag::Split}}};\n    const std::string option =\n        \"DriveToTargetRefinementLevels:\\n\"\n        \"  Target: [8, 3]\\n\"\n        \"  OscillationAtTarget: [Join, Split]\\n\";\n    test<amr::Criteria::Type::h>(target_levels, flags_at_target, test_cases,\n                                 option);\n    CHECK_THROWS_WITH(\n        (test<amr::Criteria::Type::h>(\n            target_levels,\n            std::array{amr::Flag::Split, amr::Flag::IncreaseResolution},\n            test_cases, option)),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot use p-refinement flag in OscillationAtTarget\"));\n  }\n  {\n    INFO(\"2D p\");\n    const std::array target_extents{4_st, 6_st};\n    const std::array flags_at_target{amr::Flag::IncreaseResolution,\n                                     amr::Flag::DecreaseResolution};\n    const auto test_cases = std::vector{\n        std::tuple{std::array{4_st, 6_st}, std::array{8_st, 3_st},\n                   std::array{amr::Flag::IncreaseResolution,\n                              amr::Flag::DecreaseResolution}},\n        std::tuple{\n            std::array{5_st, 6_st}, std::array{8_st, 3_st},\n            std::array{amr::Flag::DecreaseResolution, amr::Flag::DoNothing}},\n        std::tuple{\n            std::array{3_st, 6_st}, std::array{7_st, 4_st},\n            std::array{amr::Flag::IncreaseResolution, amr::Flag::DoNothing}},\n        std::tuple{\n            std::array{4_st, 5_st}, std::array{8_st, 2_st},\n            std::array{amr::Flag::DoNothing, amr::Flag::IncreaseResolution}}};\n    const std::string option =\n        \"DriveToTargetNumberOfGridPoints:\\n\"\n        \"  Target: [4, 6]\\n\"\n        \"  OscillationAtTarget: [IncreaseResolution, DecreaseResolution]\\n\";\n    test<amr::Criteria::Type::p>(target_extents, flags_at_target, test_cases,\n                                 option);\n    CHECK_THROWS_WITH(\n        (test<amr::Criteria::Type::p>(\n            target_extents,\n            std::array{amr::Flag::Join, amr::Flag::DecreaseResolution},\n            test_cases, option)),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot use h-refinement flag in OscillationAtTarget\"));\n  }\n  {\n    INFO(\"3D h\");\n    const std::array target_levels{5_st, 2_st, 4_st};\n    const std::array flags_at_target{amr::Flag::Split, amr::Flag::Join,\n                                     amr::Flag::DoNothing};\n    const auto test_cases = std::vector{\n        std::tuple{std::array{3_st, 9_st, 5_st}, std::array{5_st, 2_st, 4_st},\n                   std::array{amr::Flag::Split, amr::Flag::Join,\n                              amr::Flag::DoNothing}},\n        std::tuple{std::array{3_st, 9_st, 5_st}, std::array{5_st, 5_st, 4_st},\n                   std::array{amr::Flag::DoNothing, amr::Flag::Join,\n                              amr::Flag::DoNothing}},\n        std::tuple{std::array{3_st, 9_st, 3_st}, std::array{5_st, 2_st, 5_st},\n                   std::array{amr::Flag::DoNothing, amr::Flag::DoNothing,\n                              amr::Flag::Join}}};\n    const std::string option =\n        \"DriveToTargetRefinementLevels:\\n\"\n        \"  Target: [5, 2, 4]\\n\"\n        \"  OscillationAtTarget: [Split, Join, DoNothing]\\n\";\n    test<amr::Criteria::Type::h>(target_levels, flags_at_target, test_cases,\n                                 option);\n    CHECK_THROWS_WITH(\n        (test<amr::Criteria::Type::h>(\n            target_levels,\n            std::array{amr::Flag::Split, amr::Flag::IncreaseResolution,\n                       amr::Flag::DecreaseResolution},\n            test_cases, option)),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot use p-refinement flag in OscillationAtTarget\"));\n  }\n  {\n    INFO(\"3D p\");\n    const std::array target_extents{3_st, 9_st, 5_st};\n    const std::array flags_at_target{amr::Flag::IncreaseResolution,\n                                     amr::Flag::DecreaseResolution,\n                                     amr::Flag::DoNothing};\n    const auto test_cases = std::vector{\n        std::tuple{\n            std::array{3_st, 9_st, 5_st}, std::array{5_st, 2_st, 4_st},\n            std::array{amr::Flag::IncreaseResolution,\n                       amr::Flag::DecreaseResolution, amr::Flag::DoNothing}},\n        std::tuple{std::array{4_st, 8_st, 2_st}, std::array{5_st, 5_st, 4_st},\n                   std::array{amr::Flag::DecreaseResolution,\n                              amr::Flag::IncreaseResolution,\n                              amr::Flag::IncreaseResolution}},\n        std::tuple{std::array{3_st, 9_st, 3_st}, std::array{5_st, 2_st, 4_st},\n                   std::array{amr::Flag::DoNothing, amr::Flag::DoNothing,\n                              amr::Flag::IncreaseResolution}}};\n    const std::string option =\n        \"DriveToTargetNumberOfGridPoints:\\n\"\n        \"  Target: [3, 9, 5]\\n\"\n        \"  OscillationAtTarget: [IncreaseResolution, DecreaseResolution, \"\n        \"DoNothing]\\n\";\n    test<amr::Criteria::Type::p>(target_extents, flags_at_target, test_cases,\n                                 option);\n    CHECK_THROWS_WITH(\n        (test<amr::Criteria::Type::p>(\n            target_extents,\n            std::array{amr::Flag::Split, amr::Flag::Join,\n                       amr::Flag::DecreaseResolution},\n            test_cases, option)),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot use h-refinement flag in OscillationAtTarget\"));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_IncreaseResolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/IncreaseResolution.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace amr::Criteria {\nnamespace {\ntemplate <size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<amr::Criterion, tmpl::list<IncreaseResolution<Dim>>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.IncreaseResolution\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  const auto criterion =\n      TestHelpers::test_factory_creation<amr::Criterion, IncreaseResolution<2>>(\n          \"IncreaseResolution\");\n  Parallel::GlobalCache<Metavariables<2>> empty_cache{};\n  auto databox = db::create<tmpl::list<>>();\n  const ObservationBox<tmpl::list<>, db::DataBox<tmpl::list<>>> box{\n      make_not_null(&databox)};\n  const ElementId<2> element_id{0};\n  const auto flags = criterion->evaluate(box, empty_cache, element_id);\n  CHECK(flags == make_array<2>(amr::Flag::IncreaseResolution));\n}\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_Loehner.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Loehner.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nusing namespace std::complex_literals;\n\nnamespace amr::Criteria {\nnamespace {\n\ntemplate <typename DataType, size_t Dim>\nstruct TestVector : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim>;\n};\n\ntemplate <typename DataType, size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        amr::Criterion,\n        tmpl::list<Loehner<Dim, tmpl::list<TestVector<DataType, Dim>>>>>>;\n  };\n};\n\ntemplate <typename DataType, size_t Dim>\nvoid test() {\n  const Mesh<Dim> mesh{6, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  register_factory_classes_with_charm<Metavariables<DataType, Dim>>();\n\n  const auto criterion = TestHelpers::test_factory_creation<\n      amr::Criterion, Loehner<Dim, tmpl::list<TestVector<DataType, Dim>>>>(\n      \"Loehner:\\n\"\n      \"  VariablesToMonitor: [TestVector]\\n\"\n      \"  AbsoluteTolerance: 1.e-3\\n\"\n      \"  RelativeTolerance: 1.e-3\\n\"\n      \"  CoarseningFactor: 0.3\\n\");\n\n  // Manufacture some test data\n  tnsr::I<DataType, Dim> test_data{};\n  // X-component is linear in x and y\n  get<0>(test_data) = get<0>(logical_coords) + 2. * get<1>(logical_coords);\n  // Y-component is nonlinear in one dimension and linear in the other\n  get<1>(test_data) =\n      exp(-square(get<0>(logical_coords))) + 2. * get<1>(logical_coords);\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    get<0>(test_data) += 1.0i * get<0>(logical_coords);\n    get<1>(test_data) += 2.0i * get<1>(logical_coords);\n  }\n\n  Parallel::GlobalCache<Metavariables<DataType, Dim>> empty_cache{};\n  auto databox = db::create<\n      tmpl::list<::domain::Tags::Mesh<Dim>, TestVector<DataType, Dim>>>(\n      mesh, std::move(test_data));\n  const ObservationBox<tmpl::list<>,\n                       db::DataBox<tmpl::list<::domain::Tags::Mesh<Dim>,\n                                              TestVector<DataType, Dim>>>>\n      box{make_not_null(&databox)};\n\n  const auto flags = criterion->evaluate(box, empty_cache, ElementId<Dim>{0});\n  CHECK(flags[0] == amr::Flag::Split);\n  CHECK(flags[1] == amr::Flag::Join);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.Loehner\", \"[Unit][ParallelAlgorithms]\") {\n  static constexpr size_t Dim = 2;\n  const Mesh<Dim> mesh{6, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  {\n    INFO(\"Linear function in both dimensions\");\n    const DataVector test_data =\n        get<0>(logical_coords) + 2. * get<1>(logical_coords);\n    const auto indicator = loehner_smoothness_indicator(test_data, mesh);\n    CHECK(indicator[0] == approx(0.));\n    CHECK(indicator[1] == approx(0.));\n  }\n  {\n    INFO(\"Nonlinear in one dimension and linear in the other\");\n    const DataVector test_data =\n        exp(-square(get<0>(logical_coords))) + 2. * get<1>(logical_coords);\n    const auto indicator = loehner_smoothness_indicator(test_data, mesh);\n    CHECK(indicator[0] == approx(1.32070910780672479));\n    CHECK(indicator[1] == approx(0.));\n  }\n\n  test<DataVector, Dim>();\n  test<ComplexDataVector, Dim>();\n}\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_Persson.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Persson.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nusing namespace std::complex_literals;\n\nnamespace amr::Criteria {\nnamespace {\n\ntemplate <typename DataType, size_t Dim>\nstruct TestVector : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim>;\n};\n\ntemplate <typename DataType, size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        amr::Criterion,\n        tmpl::list<Persson<Dim, tmpl::list<TestVector<DataType, Dim>>>>>>;\n  };\n};\n\ntemplate <typename DataType, size_t Dim>\nvoid test() {\n  const Mesh<Dim> mesh{6, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  register_factory_classes_with_charm<Metavariables<DataType, Dim>>();\n\n  const auto criterion = TestHelpers::test_factory_creation<\n      amr::Criterion, Persson<Dim, tmpl::list<TestVector<DataType, Dim>>>>(\n      \"Persson:\\n\"\n      \"  VariablesToMonitor: [TestVector]\\n\"\n      \"  NumHighestModes: 2\\n\"\n      \"  AbsoluteTolerance: 1.e-3\\n\"\n      \"  Exponent: 4\\n\"\n      \"  CoarseningFactor: 0.1\\n\");\n\n  // Manufacture some test data\n  tnsr::I<DataType, Dim> test_data{};\n  // X-component is linear in x and y\n  get<0>(test_data) = get<0>(logical_coords) + 2. * get<1>(logical_coords);\n  // Y-component is nonlinear in one dimension and linear in the other\n  get<1>(test_data) =\n      exp(-square(get<0>(logical_coords))) + 2. * get<1>(logical_coords);\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    get<0>(test_data) += 1.0i * get<0>(logical_coords);\n    get<1>(test_data) += 2.0i * get<1>(logical_coords);\n  }\n\n  Parallel::GlobalCache<Metavariables<DataType, Dim>> empty_cache{};\n  auto databox = db::create<\n      tmpl::list<::domain::Tags::Mesh<Dim>, TestVector<DataType, Dim>>>(\n      mesh, std::move(test_data));\n  const ObservationBox<tmpl::list<>,\n                       db::DataBox<tmpl::list<::domain::Tags::Mesh<Dim>,\n                                              TestVector<DataType, Dim>>>>\n      box{make_not_null(&databox)};\n\n  const auto flags = criterion->evaluate(box, empty_cache, ElementId<Dim>{0});\n  CHECK(flags[0] == amr::Flag::Split);\n  CHECK(flags[1] == amr::Flag::Join);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.Persson\", \"[Unit][ParallelAlgorithms]\") {\n  static constexpr size_t Dim = 2;\n  const Mesh<Dim> mesh{6, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n\n  {\n    INFO(\"Linear function in both dimensions\");\n    const DataVector test_data =\n        get<0>(logical_coords) + 2. * get<1>(logical_coords);\n    // Only the lowest 2 modes are non-zero\n    const auto indicator = persson_smoothness_indicator(test_data, mesh, 4);\n    CHECK(indicator[0] == approx(0.));\n    CHECK(indicator[1] == approx(0.));\n  }\n  {\n    INFO(\"Nonlinear in one dimension and linear in the other\");\n    const DataVector test_data =\n        exp(-square(get<0>(logical_coords))) + 2. * get<1>(logical_coords);\n    const auto indicator = persson_smoothness_indicator(test_data, mesh, 2);\n    CHECK(indicator[0] == approx(0.04065096467876369));\n    CHECK(indicator[1] == approx(0.));\n  }\n\n  test<DataVector, Dim>();\n  test<ComplexDataVector, Dim>();\n}\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_Random.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <unordered_map>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Random.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = VolumeDim;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<amr::Criterion,\n                   tmpl::list<amr::Criteria::Random<amr::Criteria::Type::h>,\n                              amr::Criteria::Random<amr::Criteria::Type::p>>>>;\n  };\n};\nstruct TestComponent {};\n\ntemplate <size_t VolumeDim, amr::Criteria::Type CriteriaType>\nvoid test_criterion(const amr::Criterion& criterion) {\n  Parallel::GlobalCache<Metavariables<VolumeDim>> empty_cache{};\n  auto databox = db::create<db::AddSimpleTags<>>();\n  auto empty_box =\n      make_observation_box<db::AddComputeTags<>>(make_not_null(&databox));\n\n  const ElementId<VolumeDim> root_id{0};\n  auto flags = criterion.evaluate(empty_box, empty_cache, root_id);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    if constexpr (amr::Criteria::Type::h == CriteriaType) {\n      CHECK((gsl::at(flags, d) == amr::Flag::Split or\n             gsl::at(flags, d) == amr::Flag::DoNothing));\n    } else {\n      CHECK((gsl::at(flags, d) == amr::Flag::IncreaseResolution or\n             gsl::at(flags, d) == amr::Flag::DoNothing));\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid test_always_split() {\n  const amr::Criteria::Random<amr::Criteria::Type::h> criterion{\n      {{amr::Flag::Split, 1}}};\n  Parallel::GlobalCache<Metavariables<VolumeDim>> empty_cache{};\n  auto databox = db::create<db::AddSimpleTags<>>();\n  auto empty_box =\n      make_observation_box<db::AddComputeTags<>>(make_not_null(&databox));\n\n  const ElementId<VolumeDim> root_id{0};\n  auto flags = criterion.evaluate(empty_box, empty_cache, root_id);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    CHECK(gsl::at(flags, d) == amr::Flag::Split);\n  }\n}\n\ntemplate <size_t VolumeDim, amr::Criteria::Type CriteriaType>\nvoid test_always_do_nothing() {\n  const amr::Criteria::Random<CriteriaType> criterion{\n      {{amr::Flag::DoNothing, 1}}};\n\n  Parallel::GlobalCache<Metavariables<VolumeDim>> empty_cache{};\n  auto databox = db::create<db::AddSimpleTags<>>();\n  auto empty_box =\n      make_observation_box<db::AddComputeTags<>>(make_not_null(&databox));\n\n  for (size_t level = 0; level <= 5; ++level) {\n    ElementId<VolumeDim> id{0, make_array<VolumeDim>(SegmentId(level, 0))};\n    auto flags = criterion.evaluate(empty_box, empty_cache, id);\n    for (size_t d = 0; d < VolumeDim; ++d) {\n      CHECK(gsl::at(flags, d) == amr::Flag::DoNothing);\n    }\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid test_h() {\n  const amr::Criteria::Random<amr::Criteria::Type::h> criterion{\n      {{amr::Flag::Split, 1}, {amr::Flag::Join, 1}}};\n  Parallel::GlobalCache<Metavariables<VolumeDim>> empty_cache{};\n  auto databox = db::create<db::AddSimpleTags<>>();\n  auto empty_box =\n      make_observation_box<db::AddComputeTags<>>(make_not_null(&databox));\n\n  const ElementId<VolumeDim> root_id{0};\n  auto flags = criterion.evaluate(empty_box, empty_cache, root_id);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    CHECK((gsl::at(flags, d) != amr::Flag::DoNothing and\n           gsl::at(flags, d) != amr::Flag::Undefined));\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid test_p() {\n  const amr::Criteria::Random<amr::Criteria::Type::p> criterion{\n      {{amr::Flag::IncreaseResolution, 1}, {amr::Flag::DecreaseResolution, 1}}};\n  Parallel::GlobalCache<Metavariables<VolumeDim>> empty_cache{};\n  auto databox = db::create<db::AddSimpleTags<>>();\n  auto empty_box =\n      make_observation_box<db::AddComputeTags<>>(make_not_null(&databox));\n\n  const ElementId<VolumeDim> root_id{0};\n  auto flags = criterion.evaluate(empty_box, empty_cache, root_id);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    CHECK((gsl::at(flags, d) != amr::Flag::DoNothing and\n           gsl::at(flags, d) != amr::Flag::Undefined));\n  }\n}\n\ntemplate <size_t VolumeDim>\nvoid test() {\n  register_factory_classes_with_charm<Metavariables<VolumeDim>>();\n  test_always_split<VolumeDim>();\n  test_always_do_nothing<VolumeDim, amr::Criteria::Type::h>();\n  test_always_do_nothing<VolumeDim, amr::Criteria::Type::p>();\n  test_h<VolumeDim>();\n  test_p<VolumeDim>();\n  CHECK_THROWS_WITH(\n      (amr::Criteria::Random<amr::Criteria::Type::h>{\n          {{amr::Flag::Split, 1}, {amr::Flag::IncreaseResolution, 1}}}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot use p-refinement flag in ProbabilityWeights\"));\n  CHECK_THROWS_WITH(\n      (amr::Criteria::Random<amr::Criteria::Type::p>{\n          {{amr::Flag::IncreaseResolution, 1}, {amr::Flag::Join, 1}}}),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot use h-refinement flag in ProbabilityWeights\"));\n\n  const amr::Criteria::Random<amr::Criteria::Type::h> random_h_criterion{\n      {{amr::Flag::Split, 4}, {amr::Flag::DoNothing, 1}}};\n  test_criterion<VolumeDim, amr::Criteria::Type::h>(random_h_criterion);\n  test_criterion<VolumeDim, amr::Criteria::Type::h>(\n      serialize_and_deserialize(random_h_criterion));\n\n  const auto h_criterion =\n      TestHelpers::test_creation<std::unique_ptr<amr::Criterion>,\n                                 Metavariables<VolumeDim>>(\n          \"RandomH:\\n\"\n          \"  ProbabilityWeights:\\n\"\n          \"    Split: 4\\n\"\n          \"    DoNothing: 1\\n\");\n  test_criterion<VolumeDim, amr::Criteria::Type::h>(*h_criterion);\n  test_criterion<VolumeDim, amr::Criteria::Type::h>(\n      *serialize_and_deserialize(h_criterion));\n\n  const amr::Criteria::Random<amr::Criteria::Type::p> random_p_criterion{\n      {{amr::Flag::IncreaseResolution, 4}, {amr::Flag::DoNothing, 1}}};\n  test_criterion<VolumeDim, amr::Criteria::Type::p>(random_p_criterion);\n  test_criterion<VolumeDim, amr::Criteria::Type::p>(\n      serialize_and_deserialize(random_p_criterion));\n\n  const auto p_criterion =\n      TestHelpers::test_creation<std::unique_ptr<amr::Criterion>,\n                                 Metavariables<VolumeDim>>(\n          \"RandomP:\\n\"\n          \"  ProbabilityWeights:\\n\"\n          \"    IncreaseResolution: 4\\n\"\n          \"    DoNothing: 1\\n\");\n  test_criterion<VolumeDim, amr::Criteria::Type::p>(*p_criterion);\n  test_criterion<VolumeDim, amr::Criteria::Type::p>(\n      *serialize_and_deserialize(p_criterion));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.Random\", \"[Unit][ParallelAlgorithms]\") {\n  TestHelpers::db::test_simple_tag<amr::Criteria::Tags::Criteria>(\"Criteria\");\n  test<1>();\n  test<2>();\n  test<3>();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_TruncationError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/TruncationError.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nusing namespace std::complex_literals;\n\nnamespace amr::Criteria {\nnamespace {\n\ntemplate <typename DataType, size_t Dim>\nstruct TestVector : db::SimpleTag {\n  using type = tnsr::I<DataType, Dim>;\n};\n\ntemplate <typename DataType, size_t Dim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<amr::Criterion,\n                             tmpl::list<TruncationError<\n                                 Dim, tmpl::list<TestVector<DataType, Dim>>>>>>;\n  };\n};\n\ntemplate <typename DataType>\nvoid test() {\n  static constexpr size_t Dim = 2;\n  register_factory_classes_with_charm<Metavariables<DataType, Dim>>();\n  const TruncationError<Dim, tmpl::list<TestVector<DataType, Dim>>> criterion{\n      {\"TestVector\"}, 1.e-3, 0.0};\n  const auto criterion_from_option_string = TestHelpers::test_factory_creation<\n      amr::Criterion,\n      TruncationError<Dim, tmpl::list<TestVector<DataType, Dim>>>>(\n      \"TruncationError:\\n\"\n      \"  VariablesToMonitor: [TestVector]\\n\"\n      \"  AbsoluteTarget: 1.e-3\\n\"\n      \"  RelativeTarget: 0.0\\n\");\n\n  const auto evaluate_criterion = [&](const size_t num_points,\n                                      const bool zero_data = false) {\n    const Mesh<Dim> mesh{num_points, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto};\n    const auto logical_coords = logical_coordinates(mesh);\n    // Manufacture some test data\n    tnsr::I<DataType, Dim> test_data{};\n    if (zero_data) {\n      get<0>(test_data) = DataType{mesh.number_of_grid_points(), 0.0};\n      get<1>(test_data) = DataType{mesh.number_of_grid_points(), 0.0};\n    } else {\n      // X-component is linear in x and y, so it is exactly represented on the\n      // mesh\n      get<0>(test_data) = get<0>(logical_coords) + 2. * get<1>(logical_coords);\n      // Y-component is nonlinear in one dimension and linear in the other\n      get<1>(test_data) =\n          exp(sin(M_PI * get<0>(logical_coords))) + 2. * get<1>(logical_coords);\n      if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n        get<0>(test_data) += 1.0i * get<0>(logical_coords);\n        get<1>(test_data) += 2.0i * get<1>(logical_coords);\n      }\n    }\n\n    Parallel::GlobalCache<Metavariables<DataType, Dim>> empty_cache{};\n    auto databox = db::create<\n        tmpl::list<::domain::Tags::Mesh<Dim>, TestVector<DataType, Dim>>>(\n        mesh, std::move(test_data));\n    const ObservationBox<tmpl::list<>,\n                         db::DataBox<tmpl::list<::domain::Tags::Mesh<Dim>,\n                                                TestVector<DataType, Dim>>>>\n        box{make_not_null(&databox)};\n\n    const auto result = criterion.evaluate(box, empty_cache, ElementId<Dim>{0});\n    CHECK(criterion_from_option_string->evaluate(box, empty_cache,\n                                                 ElementId<Dim>{0}) == result);\n    return result;\n  };\n\n  // Expectation:\n  // - In the first dimension, one of the components is nonlinear, so we need\n  //   lots of resolution there.\n  // - In the second dimension, both components are linear. Technically, we need\n  //   only 2 modes to represent the functions exactly. However, if we have only\n  //   2 modes we can't be sure numerically that we're resolving the function.\n  //   Also with 3 modes we can't be sure, because the third mode might be zero\n  //   by symmetry. So we need 4 modes in this dimension.\n  {\n    INFO(\"3 modes\");\n    const auto flags = evaluate_criterion(3);\n    CHECK(flags[0] == amr::Flag::IncreaseResolution);\n    CHECK(flags[1] == amr::Flag::IncreaseResolution);\n  }\n  {\n    INFO(\"4 modes\");\n    const auto flags = evaluate_criterion(4);\n    CHECK(flags[0] == amr::Flag::IncreaseResolution);\n    CHECK(flags[1] == amr::Flag::DoNothing);\n  }\n  {\n    INFO(\"5 modes\");\n    const auto flags = evaluate_criterion(5);\n    CHECK(flags[0] == amr::Flag::IncreaseResolution);\n    CHECK(flags[1] == amr::Flag::DecreaseResolution);\n  }\n  {\n    INFO(\"Zero data\");\n    const auto flags = evaluate_criterion(4, true);\n    CHECK(flags[0] == amr::Flag::DoNothing);\n    CHECK(flags[1] == amr::Flag::DoNothing);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.TruncationError\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test<DataVector>();\n  test<ComplexDataVector>();\n}\n\n}  // namespace amr::Criteria\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Criteria/Test_Type.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Criteria.Type\", \"[Unit][ParallelAlgorithms]\") {\n  CHECK(get_output(amr::Criteria::Type::h) == \"h\");\n  CHECK(get_output(amr::Criteria::Type::p) == \"p\");\n\n  const std::vector known_amr_types{amr::Criteria::Type::h,\n                                    amr::Criteria::Type::p};\n\n  for (const auto type : known_amr_types) {\n    CHECK(type ==\n          TestHelpers::test_creation<amr::Criteria::Type>(get_output(type)));\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        TestHelpers::test_creation<amr::Criteria::Type>(\"Bad type name\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          MakeString{} << \"Failed to convert \\\"Bad type name\\\" to \"\n                          \"amr::Criteria::Type.\\nMust be one of \"\n                       << known_amr_types << \".\"));\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Events/Test_ObserveAmrCriteria.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/ParallelAlgorithms/Events/ObserveFields.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrCriteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<\n      TestHelpers::dg::Events::ObserveFields::ElementComponent<Metavariables>,\n      TestHelpers::dg::Events::ObserveFields::MockObserverComponent<\n          Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            amr::Criterion,\n            tmpl::list<\n                amr::Criteria::DriveToTarget<1, amr::Criteria::Type::h>,\n                amr::Criteria::DriveToTarget<1, amr::Criteria::Type::p>>>,\n        tmpl::pair<Event,\n                   tmpl::list<amr::Events::ObserveAmrCriteria<Metavariables>>>>;\n  };\n};\n\nvoid test() {\n  std::vector<std::unique_ptr<amr::Criterion>> criteria;\n  criteria.emplace_back(\n      std::make_unique<amr::Criteria::DriveToTarget<1, amr::Criteria::Type::h>>(\n          std::array{1_st}, std::array{amr::Flag::DoNothing}));\n  criteria.emplace_back(\n      std::make_unique<amr::Criteria::DriveToTarget<1, amr::Criteria::Type::p>>(\n          std::array{3_st}, std::array{amr::Flag::DoNothing}));\n  criteria.emplace_back(\n      std::make_unique<amr::Criteria::DriveToTarget<1, amr::Criteria::Type::p>>(\n          std::array{5_st}, std::array{amr::Flag::DoNothing}));\n  criteria.emplace_back(\n      std::make_unique<amr::Criteria::DriveToTarget<1, amr::Criteria::Type::h>>(\n          std::array{0_st}, std::array{amr::Flag::DoNothing}));\n  criteria.emplace_back(\n      std::make_unique<amr::Criteria::DriveToTarget<1, amr::Criteria::Type::h>>(\n          std::array{2_st}, std::array{amr::Flag::DoNothing}));\n  const size_t number_of_criteria = criteria.size();\n  const std::vector<double> expected_values{0.0, -1.0, 1.0, -2.0, 2.0};\n  register_factory_classes_with_charm<Metavariables>();\n  using element_component =\n      TestHelpers::dg::Events::ObserveFields::ElementComponent<Metavariables>;\n  using observer_component =\n      TestHelpers::dg::Events::ObserveFields::MockObserverComponent<\n          Metavariables>;\n  element_component* const element_component_p = nullptr;\n\n  const ElementId<1> element_id(0, make_array<1>(SegmentId(1, 0)));\n  const Mesh<1> mesh{4_st, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<Metavariables>;\n  MockRuntimeSystem runner{{}};\n  ActionTesting::emplace_component<element_component>(make_not_null(&runner),\n                                                      element_id);\n  ActionTesting::emplace_group_component<observer_component>(&runner);\n  auto& cache = ActionTesting::cache<element_component>(runner, element_id);\n\n  const auto event =\n      TestHelpers::test_creation<std::unique_ptr<Event>, Metavariables>(\n          \"ObserveAmrCriteria:\\n\"\n          \"  SubfileName: amr_criteria\\n\"\n          \"  CoordinatesFloatingPointType: Double\\n\"\n          \"  FloatingPointType: Float\");\n  const double time = 3.0;\n  Domain domain(make_vector(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<1>{})));\n  domain.inject_time_dependent_map_for_block(\n      0, domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n             domain::CoordinateMaps::TimeDependent::Translation<1>(\n                 \"translation\")));\n\n  domain::FunctionsOfTimeMap functions_of_time{};\n  functions_of_time.emplace(\n      \"translation\",\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<1>>(\n          1.0, std::array{DataVector(1, 2.0), DataVector(1, 5.0)}, 4.0));\n  const double expected_offset = 2.0 + (time - 1.0) * 5.0;\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::Time, domain::Tags::FunctionsOfTime,\n                        domain::Tags::Domain<1>, domain::Tags::Mesh<1>,\n                        amr::Criteria::Tags::Criteria, amr::Tags::Policies>>(\n      Metavariables{}, time, std::move(functions_of_time), std::move(domain),\n      mesh, std::move(criteria),\n      amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true});\n\n  const double observation_value = 1.23;\n\n  auto observation_box =\n      make_observation_box<typename amr::Events::ObserveAmrCriteria<\n          Metavariables>::compute_tags_for_observation_box>(\n          make_not_null(&box));\n\n  event->run(make_not_null(&observation_box), cache, element_id,\n             element_component_p, {\"value_name\", observation_value});\n\n  runner.template invoke_queued_simple_action<observer_component>(0);\n  CHECK(runner.template is_simple_action_queue_empty<observer_component>(0));\n\n  const auto& results =\n      TestHelpers::dg::Events::ObserveFields::MockContributeVolumeData::results;\n  CHECK(results.observation_id.value() == observation_value);\n  CHECK(results.observation_id.observation_key().tag() == \"/amr_criteria.vol\");\n  CHECK(results.subfile_name == \"/amr_criteria\");\n  CHECK(results.array_component_id ==\n        Parallel::make_array_component_id<element_component>(element_id));\n  CHECK(results.received_volume_data.element_name == get_output(element_id));\n  CHECK(results.received_volume_data.extents == std::vector<size_t>(1, 2));\n  const auto& components = results.received_volume_data.tensor_components;\n  REQUIRE(components.size() == 1 + number_of_criteria);\n  for (const auto& component : components) {\n    std::visit([](const auto& data) { CHECK(data.size() == 2); },\n               component.data);\n  }\n  CHECK(components[0].name == \"InertialCoordinates_x\");\n  std::visit(\n      [&](const auto& data) {\n        for (size_t i = 0; i < data.size(); ++i) {\n          CHECK(data[i] ==\n                (i % 2 < 1 ? -1.0 + expected_offset : expected_offset));\n        }\n      },\n      components[0].data);\n  for (size_t i = 0; i < number_of_criteria; ++i) {\n    CHECK(components[i + 1].name == \"DriveToTarget_x\");\n    std::visit(\n        [&](const auto& data) {\n          for (size_t j = 0; j < data.size(); ++j) {\n            CHECK(data[j] == expected_values[i]);\n          }\n        },\n        components[i + 1].data);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Amr.Events.ObserveAmrCriteria\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Events/Test_RefineMesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/DirectionalId.hpp\"\n#include \"Domain/Structure/DirectionalIdMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/OrientationMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/MortarTags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"IO/Observer/Protocols/ReductionDataFormatter.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/DriveToTarget.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/IncreaseResolution.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Type.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/ObserveAmrStats.hpp\"\n#include \"ParallelAlgorithms/Amr/Events/RefineMesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Tags.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstatic_assert(\n    tt::assert_conforms_to_v<amr::Events::detail::FormatAmrStatsOutput,\n                             observers::protocols::ReductionDataFormatter>);\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\nclass BadCriterion : public amr::Criterion {\n public:\n  using options = tmpl::list<>;\n\n  BadCriterion() = default;\n  explicit BadCriterion(CkMigrateMessage* msg) : Criterion(msg) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(BadCriterion);  // NOLINT\n\n  amr::Criteria::Type type() override { return amr::Criteria::Type::p; }\n\n  std::string observation_name() override { return \"BadCriterion\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename Metavariables>\n  auto operator()(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<Metavariables::volume_dim>& /*element_id*/) const {\n    return std::array{amr::Flag::Split};\n  }\n\n  void pup(PUP::er& p) override { Criterion::pup(p); }\n};\n\nPUP::able::PUP_ID BadCriterion::my_PUP_ID = 0;  // NOLINT\n#pragma GCC diagnostic pop\n\ntemplate <typename Metavariables>\nstruct ElementComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n  using const_global_cache_tags =\n      tmpl::list<amr::Criteria::Tags::Criteria,\n                 logging::Tags::Verbosity<amr::OptionTags::AmrGroup>>;\n  using simple_tags =\n      tmpl::list<domain::Tags::Element<1>, domain::Tags::Mesh<1>,\n                 amr::Tags::Policies, ::Tags::TimeStepId,\n                 evolution::dg::Tags::MortarNextTemporalId<1>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<simple_tags>>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockContributeReductionData {\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex,\n            typename ReductionData, typename Formatter>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationId& /*observation_id*/,\n                    Parallel::ArrayComponentId /*sender_array_id*/,\n                    const std::string& /*subfile_name*/,\n                    const std::vector<std::string>& legend,\n                    ReductionData&& reduction_data,\n                    std::optional<Formatter>&& /*formatter*/,\n                    const bool /*observe_per_core*/) {\n    CHECK(legend ==\n          std::vector<std::string>{\"Time\", \"NumElements\", \"TotalNumPoints\",\n                                   \"NumPointsPerDim_0\", \"MinPointsPerDim_0\",\n                                   \"MaxPointsPerDim_0\"});\n    // Time\n    CHECK(get<0>(reduction_data.data()) == -1.0);\n    // Total num elements\n    CHECK(get<1>(reduction_data.data()) == 1_st);\n    // Total num points\n    CHECK(get<2>(reduction_data.data()) == 4_st);\n    // Points per dim\n    CHECK(get<3>(reduction_data.data()) == std::vector<size_t>{4});\n    // Min/max points\n    CHECK(get<4>(reduction_data.data()) == std::vector<size_t>{4});\n    CHECK(get<5>(reduction_data.data()) == std::vector<size_t>{4});\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverComponent {\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::ContributeReductionData>;\n  using with_these_simple_actions =\n      tmpl::list<MockContributeReductionData<Metavariables>>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using component_list = tmpl::list<ElementComponent<Metavariables>,\n                                    MockObserverComponent<Metavariables>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Event, tmpl::list<amr::Events::RefineMesh,\n                                     amr::Events::ObserveAmrStats<volume_dim>>>,\n        tmpl::pair<\n            amr::Criterion,\n            tmpl::list<\n                BadCriterion, amr::Criteria::IncreaseResolution<1>,\n                amr::Criteria::DriveToTarget<1, amr::Criteria::Type::p>,\n                amr::Criteria::DriveToTarget<1, amr::Criteria::Type::h>>>>;\n  };\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using projectors =\n        tmpl::list<::amr::projectors::DefaultInitialize<\n                       Parallel::Tags::GlobalCache<Metavariables>>,\n                   ::amr::projectors::CopyFromCreatorOrLeaveAsIs<\n                       ::domain::Tags::Element<1>>>;\n  };\n};\n\nvoid test(const Event& event, const Event& observe_event) {\n  using element_component = ElementComponent<Metavariables>;\n  using observer_component = MockObserverComponent<Metavariables>;\n\n  CHECK(event.needs_evolved_variables());\n\n  const ElementId<1> element_id{0};\n  DirectionMap<1, Neighbors<1>> neighbors{};\n  neighbors.emplace(\n      Direction<1>::upper_xi(),\n      Neighbors({ElementId<1>{1}}, OrientationMap<1>::create_aligned()));\n  neighbors.emplace(\n      Direction<1>::lower_xi(),\n      Neighbors({ElementId<1>{2}}, OrientationMap<1>::create_aligned()));\n  const Element<1> element{element_id, neighbors};\n  const Mesh<1> mesh{std::array{3_st}, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const amr::Policies policies{amr::Isotropy::Anisotropic,\n                               amr::Limits{0, 0, 3, 5}, true, true};\n  const Slab slab(3.4, 6.7);\n  const TimeStepId time_step_id(true, 5, slab.start());\n  const auto later_time_step_id =\n      time_step_id.next_substep(slab.duration() / 2, 0.5);\n  DirectionalIdMap<1, TimeStepId> aligned_neighbor_times{};\n  for (const auto& [direction, neighbors_in_direction] : neighbors) {\n    for (const auto& neighbor : neighbors_in_direction) {\n      aligned_neighbor_times.emplace(DirectionalId(direction, neighbor),\n                                     time_step_id);\n    }\n  }\n\n  {\n    INFO(\"Basic function\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria{};\n    criteria.emplace_back(\n        std::make_unique<amr::Criteria::IncreaseResolution<1>>());\n    // this should be ignored...\n    criteria.emplace_back(\n        std::make_unique<\n            amr::Criteria::DriveToTarget<1, amr::Criteria::Type::h>>(\n            std::array{1_st}, std::array{amr::Flag::DoNothing}));\n    ActionTesting::MockRuntimeSystem<Metavariables> runner{\n        {std::move(criteria), ::Verbosity::Debug}};\n    ActionTesting::emplace_group_component<observer_component>(&runner);\n\n    ActionTesting::emplace_component_and_initialize<element_component>(\n        &runner, element_id,\n        {element, mesh, policies, time_step_id, aligned_neighbor_times});\n    auto& box = ActionTesting::get_databox<element_component>(\n        make_not_null(&runner), element_id);\n    auto obs_box = make_observation_box<tmpl::list<>>(make_not_null(&box));\n\n    event.run(make_not_null(&obs_box),\n              ActionTesting::cache<element_component>(runner, element_id),\n              element_id, std::add_pointer_t<element_component>{},\n              {\"Time\", -1.0});\n    observe_event.run(\n        make_not_null(&obs_box),\n        ActionTesting::cache<element_component>(runner, element_id), element_id,\n        std::add_pointer_t<element_component>{}, {\"Time\", -1.0});\n    runner.template invoke_queued_simple_action<observer_component>(0);\n\n    const Mesh<1> expected_mesh{std::array{4_st}, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto};\n    CHECK(ActionTesting::get_databox_tag<element_component,\n                                         domain::Tags::Mesh<1>>(\n              runner, element_id) == expected_mesh);\n    CHECK(ActionTesting::get_databox_tag<element_component,\n                                         domain::Tags::Element<1>>(\n              runner, element_id) == element);\n  }\n\n  {\n    INFO(\"Obey policies\");\n    // Try to drive to smaller number of grid points than we allow\n    std::vector<std::unique_ptr<amr::Criterion>> criteria{};\n    criteria.emplace_back(\n        std::make_unique<\n            amr::Criteria::DriveToTarget<1, amr::Criteria::Type::p>>(\n            std::array{1_st}, std::array{amr::Flag::DoNothing}));\n    ActionTesting::MockRuntimeSystem<Metavariables> runner{\n        {std::move(criteria), ::Verbosity::Debug}};\n    ActionTesting::emplace_group_component<observer_component>(&runner);\n\n    ActionTesting::emplace_component_and_initialize<element_component>(\n        &runner, element_id,\n        {element, mesh, policies, time_step_id, aligned_neighbor_times});\n    auto& box = ActionTesting::get_databox<element_component>(\n        make_not_null(&runner), element_id);\n    auto obs_box = make_observation_box<tmpl::list<>>(make_not_null(&box));\n\n    event.run(make_not_null(&obs_box),\n              ActionTesting::cache<element_component>(runner, element_id),\n              element_id, std::add_pointer_t<element_component>{},\n              {\"Unused\", -1.0});\n\n    const Mesh<1> expected_mesh = mesh;\n    CHECK(ActionTesting::get_databox_tag<element_component,\n                                         domain::Tags::Mesh<1>>(\n              runner, element_id) == expected_mesh);\n    CHECK(ActionTesting::get_databox_tag<element_component,\n                                         domain::Tags::Element<1>>(\n              runner, element_id) == element);\n\n    const amr::Policies error_policies{amr::Isotropy::Anisotropic,\n                                       amr::Limits{{{0, 0}}, {{3, 5}}, true},\n                                       true, true};\n    db::mutate<amr::Tags::Policies>(\n        [&](const gsl::not_null<amr::Policies*> box_policies) {\n          *box_policies = error_policies;\n        },\n        make_not_null(&box));\n\n    CHECK_THROWS_WITH(\n        (event.run(make_not_null(&obs_box),\n                   ActionTesting::cache<element_component>(runner, element_id),\n                   element_id, std::add_pointer_t<element_component>{},\n                   {\"Unused\", -1.0})),\n        Catch::Matchers::ContainsSubstring(\n            \"Tried refining beyond the AMR limits in element\"));\n  }\n\n  {\n    INFO(\"Test unaligned error\");\n    std::vector<std::unique_ptr<amr::Criterion>> criteria{};\n    criteria.emplace_back(\n        std::make_unique<amr::Criteria::IncreaseResolution<1>>());\n    ActionTesting::MockRuntimeSystem<Metavariables> runner{\n        {std::move(criteria), ::Verbosity::Debug}};\n\n    auto neighbor_times = aligned_neighbor_times;\n    neighbor_times.begin()->second = later_time_step_id;\n    ActionTesting::emplace_component_and_initialize<element_component>(\n        &runner, element_id,\n        {element, mesh, policies, time_step_id, neighbor_times});\n    auto& box = ActionTesting::get_databox<element_component>(\n        make_not_null(&runner), element_id);\n    auto obs_box = make_observation_box<tmpl::list<>>(make_not_null(&box));\n\n    CHECK_THROWS_WITH(\n        event.run(make_not_null(&obs_box),\n                  ActionTesting::cache<element_component>(runner, element_id),\n                  element_id, std::add_pointer_t<element_component>{},\n                  {\"Unused\", -1.0}),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot refine mesh when not temporally aligned with neighbors.\"));\n  }\n\n#ifdef SPECTRE_DEBUG\n  {\n    INFO(\"Test h-refinement error\");\n    // Try to use a bad criterion\n    std::vector<std::unique_ptr<amr::Criterion>> criteria{};\n    criteria.emplace_back(std::make_unique<BadCriterion>());\n    ActionTesting::MockRuntimeSystem<Metavariables> runner{\n        {std::move(criteria), ::Verbosity::Debug}};\n    ActionTesting::emplace_group_component<observer_component>(&runner);\n\n    ActionTesting::emplace_component_and_initialize<element_component>(\n        &runner, element_id,\n        {element, mesh, policies, time_step_id, aligned_neighbor_times});\n    auto& box = ActionTesting::get_databox<element_component>(\n        make_not_null(&runner), element_id);\n    auto obs_box = make_observation_box<tmpl::list<>>(make_not_null(&box));\n\n    CHECK_THROWS_WITH(\n        (event.run(make_not_null(&obs_box),\n                   ActionTesting::cache<element_component>(runner, element_id),\n                   element_id, std::add_pointer_t<element_component>{},\n                   {\"Unused\", -1.0})),\n        Catch::Matchers::ContainsSubstring(\n            \"requested h-refinement, but claims to be for p-refinement\"));\n  }\n#endif\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Events.RefineMesh\", \"[Unit][ParallelAlgorithms]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  const amr::Events::RefineMesh event{};\n  const amr::Events::ObserveAmrStats<1> observe_event{true, false};\n  test(event, observe_event);\n  test(serialize_and_deserialize(event),\n       serialize_and_deserialize(observe_event));\n  const auto option_event =\n      TestHelpers::test_creation<std::unique_ptr<Event>, Metavariables>(\n          \"RefineMesh\\n\");\n  const auto option_observe_event =\n      TestHelpers::test_creation<std::unique_ptr<Event>, Metavariables>(\n          \"ObserveAmrStats:\\n\"\n          \"  PrintToTerminal: True\\n\"\n          \"  ObservePerCore: False\");\n  test(*option_event, *option_observe_event);\n  test(*serialize_and_deserialize(option_event),\n       *serialize_and_deserialize(option_observe_event));\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Policies/Test_EnforcePolicies.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <catch2/matchers/catch_matchers.hpp>\n#include <catch2/matchers/catch_matchers_string.hpp>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/EnforcePolicies.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test_decision(const std::array<amr::Flag, Dim>& original_decision,\n                   const amr::Policies& amr_policies,\n                   const ElementId<Dim>& element_id, const Mesh<Dim>& mesh,\n                   const std::array<amr::Flag, Dim>& expected_decision,\n                   const bool error_beyond_limits = false) {\n  auto decision = original_decision;\n  if (error_beyond_limits) {\n    CHECK_THROWS_WITH((enforce_policies(make_not_null(&decision), amr_policies,\n                                        element_id, mesh)),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Tried refining beyond the AMR limits in element\"));\n  } else {\n    enforce_policies(make_not_null(&decision), amr_policies, element_id, mesh);\n    CHECK(decision == expected_decision);\n  }\n}\n\nvoid test_1d() {\n  const auto split = std::array{amr::Flag::Split};\n  const auto increase = std::array{amr::Flag::IncreaseResolution};\n  const auto stay = std::array{amr::Flag::DoNothing};\n  const auto decrease = std::array{amr::Flag::DecreaseResolution};\n  const auto join = std::array{amr::Flag::Join};\n  const auto all_flags = std::vector{split, increase, stay, decrease, join};\n\n  const amr::Policies isotropic{amr::Isotropy::Isotropic, amr::Limits{}, true,\n                                true};\n  const amr::Policies anisotropic{amr::Isotropy::Anisotropic, amr::Limits{},\n                                  true, true};\n  const auto policies = std::array{anisotropic, isotropic};\n  const ElementId<1> element_id{0, {{{1, 0}}}};\n  const Mesh<1> mesh{3, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss};\n  for (const auto flags : all_flags) {\n    for (const auto policy : policies) {\n      test_decision(flags, policy, element_id, mesh, flags);\n    }\n  }\n\n  {\n    INFO(\"Limits\");\n    const amr::Policies policy{amr::Isotropy::Anisotropic,\n                               amr::Limits{0, 5, 1, 12}, true, true};\n    test_decision(join, policy, ElementId<1>{0}, mesh, stay);\n    test_decision(split, policy, ElementId<1>{0, {{{5, 2}}}}, mesh, stay);\n    test_decision(\n        decrease, policy, element_id,\n        Mesh<1>{1, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n        stay);\n    test_decision(decrease, policy, element_id,\n                  Mesh<1>{2, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto},\n                  stay);\n    test_decision(\n        increase, policy, element_id,\n        Mesh<1>{12, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss},\n        stay);\n  }\n  {\n    INFO(\"No coarsening\");\n    const amr::Policies policy{amr::Isotropy::Anisotropic, amr::Limits{}, true,\n                               false};\n    test_decision(join, policy, element_id, mesh, stay);\n    test_decision(split, policy, element_id, mesh, split);\n    test_decision(decrease, policy, element_id, mesh, stay);\n    test_decision(increase, policy, element_id, mesh, increase);\n  }\n}\n\nvoid test_2d() {\n  const auto split_split = std::array{amr::Flag::Split, amr::Flag::Split};\n  const auto increase_split =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::Split};\n  const auto stay_split = std::array{amr::Flag::DoNothing, amr::Flag::Split};\n  const auto decrease_split =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::Split};\n  const auto join_split = std::array{amr::Flag::Join, amr::Flag::Split};\n  const auto split_increase =\n      std::array{amr::Flag::Split, amr::Flag::IncreaseResolution};\n  const auto increase_increase =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::IncreaseResolution};\n  const auto stay_increase =\n      std::array{amr::Flag::DoNothing, amr::Flag::IncreaseResolution};\n  const auto decrease_increase =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::IncreaseResolution};\n  const auto join_increase =\n      std::array{amr::Flag::Join, amr::Flag::IncreaseResolution};\n  const auto split_stay = std::array{amr::Flag::Split, amr::Flag::DoNothing};\n  const auto increase_stay =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::DoNothing};\n  const auto stay_stay = std::array{amr::Flag::DoNothing, amr::Flag::DoNothing};\n  const auto decrease_stay =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::DoNothing};\n  const auto join_stay = std::array{amr::Flag::Join, amr::Flag::DoNothing};\n  const auto split_decrease =\n      std::array{amr::Flag::Split, amr::Flag::DecreaseResolution};\n  const auto increase_decrease =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::DecreaseResolution};\n  const auto stay_decrease =\n      std::array{amr::Flag::DoNothing, amr::Flag::DecreaseResolution};\n  const auto decrease_decrease =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::DecreaseResolution};\n  const auto join_decrease =\n      std::array{amr::Flag::Join, amr::Flag::DecreaseResolution};\n  const auto split_join = std::array{amr::Flag::Split, amr::Flag::Join};\n  const auto increase_join =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::Join};\n  const auto stay_join = std::array{amr::Flag::DoNothing, amr::Flag::Join};\n  const auto decrease_join =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::Join};\n  const auto join_join = std::array{amr::Flag::Join, amr::Flag::Join};\n\n  const auto all_flags = std::vector{\n      split_split,       increase_split, stay_split,        decrease_split,\n      join_split,        split_increase, increase_increase, stay_increase,\n      decrease_increase, join_increase,  split_stay,        increase_stay,\n      stay_stay,         decrease_stay,  join_stay,         split_decrease,\n      increase_decrease, stay_decrease,  decrease_decrease, join_decrease,\n      split_join,        increase_join,  stay_join,         decrease_join,\n      join_join};\n\n  const amr::Policies isotropic{amr::Isotropy::Isotropic, amr::Limits{}, true,\n                                true};\n  const amr::Policies anisotropic{amr::Isotropy::Anisotropic, amr::Limits{},\n                                  true, true};\n\n  const ElementId<2> element_id{0, {{{1, 0}, {1, 0}}}};\n  const Mesh<2> mesh{3, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss};\n\n  for (const auto flags : all_flags) {\n    test_decision(flags, anisotropic, element_id, mesh, flags);\n  }\n\n  for (const auto flags : std::vector{\n           split_split, increase_split, stay_split, decrease_split, join_split,\n           split_increase, split_stay, split_decrease, split_join}) {\n    test_decision(flags, isotropic, element_id, mesh, split_split);\n  }\n  for (const auto flags : std::vector{\n           increase_increase, stay_increase, decrease_increase, join_increase,\n           increase_stay, increase_decrease, increase_join}) {\n    test_decision(flags, isotropic, element_id, mesh, increase_increase);\n  }\n  for (const auto flags : std::vector{stay_stay, stay_decrease, stay_join,\n                                      decrease_stay, join_stay}) {\n    test_decision(flags, isotropic, element_id, mesh, stay_stay);\n  }\n  for (const auto flags :\n       std::vector{decrease_decrease, decrease_join, join_decrease}) {\n    test_decision(flags, isotropic, element_id, mesh, decrease_decrease);\n  }\n  test_decision(join_join, isotropic, element_id, mesh, join_join);\n\n  {\n    INFO(\"No coarsening\");\n    const amr::Policies policy{amr::Isotropy::Anisotropic, amr::Limits{}, true,\n                               false};\n    for (const auto flags :\n         {stay_stay, decrease_stay, join_stay, stay_decrease, decrease_decrease,\n          join_decrease, stay_join, decrease_join, join_join}) {\n      test_decision(flags, policy, element_id, mesh, stay_stay);\n    }\n    for (const auto flags : {split_split, increase_split, stay_split,\n                             split_increase, increase_increase, stay_increase,\n                             split_stay, increase_stay, stay_stay}) {\n      test_decision(flags, policy, element_id, mesh, flags);\n    }\n    test_decision(decrease_split, policy, element_id, mesh, stay_split);\n    test_decision(join_split, policy, element_id, mesh, stay_split);\n    test_decision(decrease_increase, policy, element_id, mesh, stay_increase);\n    test_decision(join_increase, policy, element_id, mesh, stay_increase);\n    test_decision(split_decrease, policy, element_id, mesh, split_stay);\n    test_decision(increase_decrease, policy, element_id, mesh, increase_stay);\n    test_decision(split_join, policy, element_id, mesh, split_stay);\n    test_decision(increase_join, policy, element_id, mesh, increase_stay);\n  }\n}\n\nvoid test_3d() {\n  const auto stay_split_split =\n      std::array{amr::Flag::DoNothing, amr::Flag::Split, amr::Flag::Split};\n  const auto split_split_split =\n      std::array{amr::Flag::Split, amr::Flag::Split, amr::Flag::Split};\n  const auto join_split_split =\n      std::array{amr::Flag::Join, amr::Flag::Split, amr::Flag::Split};\n  amr::Policies isotropic{amr::Isotropy::Isotropic, amr::Limits{}, true, true};\n  amr::Policies anisotropic{amr::Isotropy::Anisotropic, amr::Limits{}, true,\n                            true};\n  ElementId<3> element_id{0, {{{1, 0}, {1, 0}, {1, 0}}}};\n  const Mesh<3> mesh{3, Spectral::Basis::Legendre, Spectral::Quadrature::Gauss};\n  test_decision(stay_split_split, anisotropic, element_id, mesh,\n                stay_split_split);\n  test_decision(stay_split_split, isotropic, element_id, mesh,\n                split_split_split);\n\n  // Test error beyond limits\n  isotropic = amr::Policies{amr::Isotropy::Isotropic,\n                            amr::Limits{{{0, 0}}, {{2, 8}}, true}, true, true};\n  anisotropic =\n      amr::Policies{amr::Isotropy::Anisotropic,\n                    amr::Limits{{{0, 0}}, {{2, 8}}, true}, true, true};\n  element_id = ElementId<3>{0};\n  test_decision(stay_split_split, anisotropic, element_id, mesh,\n                stay_split_split, true);\n  test_decision(stay_split_split, isotropic, element_id, mesh,\n                split_split_split, true);\n\n  {\n    INFO(\"No coarsening\");\n    const amr::Policies policy{amr::Isotropy::Anisotropic, amr::Limits{}, true,\n                               false};\n    for (const auto flags : {stay_split_split, split_split_split}) {\n      test_decision(flags, policy, element_id, mesh, flags);\n    }\n    test_decision(join_split_split, policy, element_id, mesh, stay_split_split);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Amr.EnforcePolicies\",\n                  \"[ParallelAlgorithms][Unit]\") {\n  test_1d();\n  test_2d();\n  test_3d();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Policies/Test_Isotropy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Amr.Isotropy\",\n                  \"[ParallelAlgorithms][Unit]\") {\n  CHECK(get_output(amr::Isotropy::Anisotropic) == \"Anisotropic\");\n  CHECK(get_output(amr::Isotropy::Isotropic) == \"Isotropic\");\n\n  const std::vector known_amr_isotropies{amr::Isotropy::Anisotropic,\n                                         amr::Isotropy::Isotropic};\n\n  for (const auto isotropy : known_amr_isotropies) {\n    CHECK(isotropy ==\n          TestHelpers::test_creation<amr::Isotropy>(get_output(isotropy)));\n  }\n\n  CHECK_THROWS_WITH(\n      ([]() {\n        TestHelpers::test_creation<amr::Isotropy>(\"Bad isotropy name\");\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          MakeString{} << \"Failed to convert \\\"Bad isotropy name\\\" to \"\n                          \"amr::Isotropy.\\nMust be one of \"\n                       << known_amr_isotropies << \".\"));\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Policies/Test_Limits.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/MaximumNumberOfPoints.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n\nnamespace {\nvoid test_equality() {\n  INFO(\"Equality\");\n  CHECK(amr::Limits{0, 3, 1, 5} == amr::Limits{0, 3, 1, 5});\n  CHECK(amr::Limits{0, 3, 1, 5} != amr::Limits{1, 3, 1, 5});\n  CHECK(amr::Limits{0, 3, 1, 5} != amr::Limits{0, 4, 1, 5});\n  CHECK(amr::Limits{0, 3, 1, 5} != amr::Limits{0, 3, 2, 5});\n  CHECK(amr::Limits{0, 3, 1, 5} != amr::Limits{0, 3, 1, 6});\n}\n\nvoid test_pup() {\n  INFO(\"Serialization\");\n  test_serialization(amr::Limits{0, 3, 1, 5});\n}\n\nvoid test_option_parsing() {\n  INFO(\"Option Parsing creation\");\n  {\n    const std::string creation_string_1 =\n        \"RefinementLevel: [0, 3]\\n\"\n        \"NumGridPoints: [1, 5]\\n\"\n        \"ErrorBeyondLimits: False\\n\";\n    const auto limits =\n        TestHelpers::test_creation<amr::Limits>(creation_string_1);\n    CHECK(limits == amr::Limits{0, 3, 1, 5});\n  }\n\n  {\n    const std::string creation_string_2 =\n        \"RefinementLevel: Auto\\n\"\n        \"NumGridPoints: [1, 5]\\n\"\n        \"ErrorBeyondLimits: False\\n\";\n    const auto limits =\n        TestHelpers::test_creation<amr::Limits>(creation_string_2);\n    CHECK(limits == amr::Limits{0, ElementId<1>::max_refinement_level, 1, 5});\n  }\n\n  {\n    const std::string creation_string_3 =\n        \"RefinementLevel: [0, 3]\\n\"\n        \"NumGridPoints: Auto\\n\"\n        \"ErrorBeyondLimits: False\\n\";\n    const auto limits =\n        TestHelpers::test_creation<amr::Limits>(creation_string_3);\n    CHECK(limits ==\n          amr::Limits{\n              0, 3, 1,\n              Spectral::maximum_number_of_points<Spectral::Basis::Legendre>});\n  }\n\n  {\n    const std::string creation_string_4 =\n        \"RefinementLevel: Auto\\n\"\n        \"NumGridPoints: Auto\\n\"\n        \"ErrorBeyondLimits: False\\n\";\n    const auto limits =\n        TestHelpers::test_creation<amr::Limits>(creation_string_4);\n    CHECK(limits == amr::Limits{});\n  }\n\n  {\n    const std::string creation_string_5 =\n        \"RefinementLevel: [0, 3]\\n\"\n        \"NumGridPoints: [1, 5]\\n\"\n        \"ErrorBeyondLimits: True\\n\";\n    const auto limits =\n        TestHelpers::test_creation<amr::Limits>(creation_string_5);\n    CHECK(limits == amr::Limits{{{0, 3}}, {{1, 5}}, true});\n  }\n\n  const std::string bad_creation_string_1 =\n      \"RefinementLevel: [255, 3]\\n\"\n      \"NumGridPoints: [1, 5]\\n\"\n      \"ErrorBeyondLimits: False\\n\";\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<amr::Limits>(bad_creation_string_1),\n      Catch::Matchers::ContainsSubstring(\n          \"RefinementLevel lower bound '255' cannot be larger than '\"));\n\n  const std::string bad_creation_string_2 =\n      \"RefinementLevel: [1, 255]\\n\"\n      \"NumGridPoints: [1, 5]\\n\"\n      \"ErrorBeyondLimits: False\\n\";\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<amr::Limits>(bad_creation_string_2),\n      Catch::Matchers::ContainsSubstring(\n          \"RefinementLevel upper bound '255' cannot be larger than '\"));\n\n  const std::string bad_creation_string_3 =\n      \"RefinementLevel: [0, 3]\\n\"\n      \"NumGridPoints: [0, 5]\\n\"\n      \"ErrorBeyondLimits: False\\n\";\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<amr::Limits>(bad_creation_string_3),\n      Catch::Matchers::ContainsSubstring(\n          \"NumGridPoints lower bound '0' cannot be smaller than '1'.\"));\n\n  const std::string bad_creation_string_4 =\n      \"RefinementLevel: [1, 3]\\n\"\n      \"NumGridPoints: [255, 5]\\n\"\n      \"ErrorBeyondLimits: False\\n\";\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<amr::Limits>(bad_creation_string_4),\n      Catch::Matchers::ContainsSubstring(\n          \"NumGridPoints lower bound '255' cannot be larger than '\"));\n\n  const std::string bad_creation_string_5 =\n      \"RefinementLevel: [1, 3]\\n\"\n      \"NumGridPoints: [1, 0]\\n\"\n      \"ErrorBeyondLimits: False\\n\";\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<amr::Limits>(bad_creation_string_5),\n      Catch::Matchers::ContainsSubstring(\n          \"NumGridPoints upper bound '0' cannot be smaller than '1'.\"));\n\n  const std::string bad_creation_string_6 =\n      \"RefinementLevel: [1, 3]\\n\"\n      \"NumGridPoints: [1, 255]\\n\"\n      \"ErrorBeyondLimits: False\\n\";\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<amr::Limits>(bad_creation_string_6),\n      Catch::Matchers::ContainsSubstring(\n          \"NumGridPoints upper bound '255' cannot be larger than '\"));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Amr.Limits\",\n                  \"[ParallelAlgorithms][Unit]\") {\n  test_equality();\n  test_pup();\n  test_option_parsing();\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        amr::Limits limits{2, 1, 1, 5};\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"The minimum refinement level '2' cannot be larger than \"\n          \"the maximum refinement level '1'\"));\n  CHECK_THROWS_WITH(([]() {\n                      amr::Limits limits{0, 3, 6, 4};\n                    }()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The minimum resolution '6' cannot be larger than the \"\n                        \"maximum resolution '4'\"));\n#endif  // SPECTRE_DEBUG\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Policies/Test_Policies.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <sstream>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Isotropy.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Limits.hpp\"\n#include \"ParallelAlgorithms/Amr/Policies/Policies.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n\nnamespace {\nvoid test_equality() {\n  INFO(\"Equality\");\n  CHECK(amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true} ==\n        amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true});\n  CHECK(amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true} !=\n        amr::Policies{amr::Isotropy::Isotropic, amr::Limits{}, true, true});\n  CHECK(amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true} !=\n        amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, false, true});\n  CHECK(amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true} !=\n        amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, false});\n}\n\nvoid test_pup() {\n  INFO(\"Serialization\");\n  test_serialization(\n      amr::Policies{amr::Isotropy::Anisotropic, amr::Limits{}, true, true});\n}\n\nvoid test_option_parsing() {\n  INFO(\"Option Parsing creation\");\n  const auto isotropies =\n      std::array{amr::Isotropy::Anisotropic, amr::Isotropy::Isotropic};\n  const amr::Limits default_limits{};\n  const amr::Limits specified_limits{1, 5, 3, 7};\n  for (const auto& [isotropy, enforce_two_to_one, allow_coarsening] :\n       cartesian_product(isotropies, std::array{true, false},\n                         std::array{true, false})) {\n    CAPTURE(isotropy);\n    CAPTURE(enforce_two_to_one);\n    CAPTURE(allow_coarsening);\n    {\n      INFO(\"specified limits\");\n      std::ostringstream creation_string;\n      creation_string << \"Isotropy: \" << isotropy << \"\\n\";\n      creation_string << \"Limits:\\n\"\n                      << \"  RefinementLevel: [1, 5]\\n\"\n                      << \"  NumGridPoints: [3, 7]\\n\"\n                      << \"  ErrorBeyondLimits: False\\n\";\n      creation_string << \"EnforceTwoToOneBalanceInNormalDirection: \"\n                      << std::boolalpha << enforce_two_to_one << \"\\n\";\n      creation_string << \"AllowCoarsening: \" << std::boolalpha\n                      << allow_coarsening << \"\\n\";\n      const auto policies =\n          TestHelpers::test_creation<amr::Policies>(creation_string.str());\n      CHECK(policies == amr::Policies{isotropy, specified_limits,\n                                      enforce_two_to_one, allow_coarsening});\n    }\n    {\n      INFO(\"default limits\");\n      std::ostringstream creation_string;\n      creation_string << \"Isotropy: \" << isotropy << \"\\n\";\n      creation_string << \"Limits:\\n\"\n                      << \"  RefinementLevel: Auto\\n\"\n                      << \"  NumGridPoints: Auto\\n\"\n                      << \"  ErrorBeyondLimits: False\\n\";\n      creation_string << \"EnforceTwoToOneBalanceInNormalDirection: \"\n                      << std::boolalpha << enforce_two_to_one << \"\\n\";\n      creation_string << \"AllowCoarsening: \" << std::boolalpha\n                      << allow_coarsening << \"\\n\";\n      const auto policies =\n          TestHelpers::test_creation<amr::Policies>(creation_string.str());\n      CHECK(policies == amr::Policies{isotropy, default_limits,\n                                      enforce_two_to_one, allow_coarsening});\n    }\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Amr.Policies\",\n                  \"[ParallelAlgorithms][Unit]\") {\n  test_equality();\n  test_pup();\n  test_option_parsing();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Projectors/Test_CopyFromCreatorOrLeaveAsIs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/CopyFromCreatorOrLeaveAsIs.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TagInt : db::SimpleTag {\n  using type = int;\n};\n\nstruct TagDouble : db::SimpleTag {\n  using type = double;\n};\n\nvoid test_p_refinement() {\n  const ElementId<1> element_id{0};\n  Element<1> element{element_id, DirectionMap<1, Neighbors<1>>{}};\n  const Mesh<1> mesh{2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  auto box = db::create<db::AddSimpleTags<TagInt, TagDouble>>(3, 4.2);\n  db::mutate_apply<\n      amr::projectors::CopyFromCreatorOrLeaveAsIs<TagInt, TagDouble>>(\n      make_not_null(&box), std::make_pair(mesh, element));\n  CHECK(db::get<TagInt>(box) == 3);\n  CHECK(db::get<TagDouble>(box) == 4.2);\n}\n\nvoid test_splitting() {\n  auto box = db::DataBox<tmpl::list<TagInt, TagDouble>>{};\n  tuples::TaggedTuple<TagInt, TagDouble> parent_items{3, 4.2};\n  db::mutate_apply<amr::projectors::CopyFromCreatorOrLeaveAsIs<\n      tmpl::list<TagInt, TagDouble>>>(make_not_null(&box),\n                                                    parent_items);\n  CHECK(db::get<TagInt>(box) == 3);\n  CHECK(db::get<TagDouble>(box) == 4.2);\n}\n\nvoid test_joining() {\n  auto box = db::DataBox<tmpl::list<TagInt, TagDouble>>{};\n  tuples::TaggedTuple<TagInt, TagDouble> child_one_items{3, 4.2};\n  tuples::TaggedTuple<TagInt, TagDouble> child_two_items{3, 4.2};\n  const std::unordered_map<ElementId<1>, tuples::TaggedTuple<TagInt, TagDouble>>\n      children_items{{ElementId<1>{0}, child_one_items},\n                     {ElementId<1>{1}, child_two_items}};\n  db::mutate_apply<amr::projectors::CopyFromCreatorOrLeaveAsIs<\n      tmpl::list<TagInt, TagDouble>>>(make_not_null(&box), children_items);\n  CHECK(db::get<TagInt>(box) == 3);\n  CHECK(db::get<TagDouble>(box) == 4.2);\n}\n\nvoid test_joining_assert() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      ([]() {\n        auto box = db::DataBox<tmpl::list<TagInt, TagDouble>>{};\n        tuples::TaggedTuple<TagInt, TagDouble> child_one_items{3, 4.2};\n        tuples::TaggedTuple<TagInt, TagDouble> child_two_items{3, 4.1};\n        const std::unordered_map<ElementId<1>,\n                                 tuples::TaggedTuple<TagInt, TagDouble>>\n            children_items{{ElementId<1>{0}, child_one_items},\n                           {ElementId<1>{1}, child_two_items}};\n        db::mutate_apply<amr::projectors::CopyFromCreatorOrLeaveAsIs<\n            tmpl::list<TagInt, TagDouble>>>(make_not_null(&box),\n                                            children_items);\n      })(),\n      Catch::Matchers::ContainsSubstring(\"Children do not agree\"));\n#endif  // #ifdef SPECTRE_DEBUG\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Projectors.CopyFromCreatorOrLeaveAsIs\",\n                  \"[Domain][Unit]\") {\n  static_assert(\n      tt::assert_conforms_to_v<amr::projectors::CopyFromCreatorOrLeaveAsIs<\n                                   tmpl::list<TagInt, TagDouble>>,\n                               amr::protocols::Projector>);\n  test_p_refinement();\n  test_splitting();\n  test_joining();\n  test_joining_assert();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Projectors/Test_DefaultInitialize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct DefaultTagInt : db::SimpleTag {\n  using type = int;\n};\n\nstruct DefaultTagDouble : db::SimpleTag {\n  using type = double;\n};\n\nvoid test_p_refinement() {\n  const ElementId<1> element_id{0};\n  Element<1> element{element_id, DirectionMap<1, Neighbors<1>>{}};\n  const Mesh<1> mesh{2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  auto box =\n      db::create<db::AddSimpleTags<DefaultTagInt, DefaultTagDouble>>(3, 4.2);\n  db::mutate_apply<\n      amr::projectors::DefaultInitialize<DefaultTagInt, DefaultTagDouble>>(\n      make_not_null(&box), std::make_pair(mesh, element));\n  CHECK(db::get<DefaultTagInt>(box) == int{});\n  CHECK(db::get<DefaultTagDouble>(box) == double{});\n}\n\nvoid test_h_refinement() {\n  auto box = db::DataBox<tmpl::list<DefaultTagInt, DefaultTagDouble>>{};\n  char unused_extra_arg{'Y'};\n  db::mutate_apply<amr::projectors::DefaultInitialize<\n      tmpl::list<DefaultTagInt, DefaultTagDouble>>>(make_not_null(&box),\n                                                    unused_extra_arg);\n  CHECK(db::get<DefaultTagInt>(box) == int{});\n  CHECK(db::get<DefaultTagDouble>(box) == double{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Projectors.DefaultInitialize\", \"[Domain][Unit]\") {\n  static_assert(\n      tt::assert_conforms_to_v<amr::projectors::DefaultInitialize<\n                                   tmpl::list<DefaultTagInt, DefaultTagDouble>>,\n                               amr::protocols::Projector>);\n  test_p_refinement();\n  test_h_refinement();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Projectors/Test_Mesh.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"Domain/Amr/Flag.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\nvoid test_mesh_1d() {\n  const auto legendre = Spectral::Basis::Legendre;\n  const auto gauss_lobatto = Spectral::Quadrature::GaussLobatto;\n  const auto refine = std::array{amr::Flag::IncreaseResolution};\n  const auto coarsen = std::array{amr::Flag::DecreaseResolution};\n  const auto split = std::array{amr::Flag::Split};\n  const auto join = std::array{amr::Flag::Join};\n  const auto stay = std::array{amr::Flag::DoNothing};\n  Mesh<1> mesh_3{std::array{3_st}, legendre, gauss_lobatto};\n  Mesh<1> mesh_4{std::array{4_st}, legendre, gauss_lobatto};\n  CHECK(amr::projectors::mesh(mesh_3, refine) == mesh_4);\n  CHECK(amr::projectors::mesh(mesh_4, coarsen) == mesh_3);\n  CHECK(amr::projectors::mesh(mesh_3, join) == mesh_3);\n  CHECK(amr::projectors::mesh(mesh_3, split) == mesh_3);\n  CHECK(amr::projectors::mesh(mesh_3, stay) == mesh_3);\n\n  CHECK(amr::projectors::parent_mesh(std::vector{mesh_3, mesh_4}) == mesh_4);\n}\n\nvoid test_mesh_2d() {\n  const auto legendre = Spectral::Basis::Legendre;\n  const auto gauss_lobatto = Spectral::Quadrature::GaussLobatto;\n  const auto refine_refine =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::IncreaseResolution};\n  const auto refine_stay =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::DoNothing};\n  const auto refine_coarsen =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::DecreaseResolution};\n  const auto coarsen_refine =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::IncreaseResolution};\n  const auto coarsen_stay =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::DoNothing};\n  const auto coarsen_coarsen =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::DecreaseResolution};\n  Mesh<2> mesh_3_5{std::array{3_st, 5_st}, legendre, gauss_lobatto};\n  Mesh<2> mesh_3_6{std::array{3_st, 6_st}, legendre, gauss_lobatto};\n  Mesh<2> mesh_4_5{std::array{4_st, 5_st}, legendre, gauss_lobatto};\n  Mesh<2> mesh_4_6{std::array{4_st, 6_st}, legendre, gauss_lobatto};\n  CHECK(amr::projectors::mesh(mesh_3_5, refine_refine) == mesh_4_6);\n  CHECK(amr::projectors::mesh(mesh_3_5, refine_stay) == mesh_4_5);\n  CHECK(amr::projectors::mesh(mesh_3_6, refine_coarsen) == mesh_4_5);\n  CHECK(amr::projectors::mesh(mesh_4_5, coarsen_refine) == mesh_3_6);\n  CHECK(amr::projectors::mesh(mesh_4_6, coarsen_stay) == mesh_3_6);\n  CHECK(amr::projectors::mesh(mesh_4_6, coarsen_coarsen) == mesh_3_5);\n\n  CHECK(amr::projectors::parent_mesh(\n            std::vector{mesh_3_5, mesh_4_5, mesh_3_6}) == mesh_4_6);\n#ifdef SPECTRE_DEBUG\n  Mesh<2> mesh_mismatch_basis{std::array{2_st, 4_st},\n                              std::array{legendre, Spectral::Basis::Chebyshev},\n                              std::array{gauss_lobatto, gauss_lobatto}};\n  CHECK_THROWS_WITH(\n      amr::projectors::parent_mesh(std::vector{mesh_3_5, mesh_mismatch_basis}),\n      Catch::Matchers::ContainsSubstring(\n          \"AMR does not currently support joining elements with \"\n          \"different quadratures or bases\"));\n  Mesh<2> mesh_mismatch_quadrature{\n      std::array{2_st, 4_st}, std::array{legendre, legendre},\n      std::array{Spectral::Quadrature::Gauss, gauss_lobatto}};\n  CHECK_THROWS_WITH(amr::projectors::parent_mesh(\n                        std::vector{mesh_3_5, mesh_mismatch_quadrature}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"AMR does not currently support joining elements with \"\n                        \"different quadratures or bases\"));\n#endif\n}\n\nvoid test_mesh_3d() {\n  const auto legendre = Spectral::Basis::Legendre;\n  const auto gauss_lobatto = Spectral::Quadrature::GaussLobatto;\n  const auto refine_refine_refine =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::IncreaseResolution,\n                 amr::Flag::IncreaseResolution};\n  const auto refine_stay_refine =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::DoNothing,\n                 amr::Flag::IncreaseResolution};\n  const auto refine_coarsen_refine =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::DecreaseResolution,\n                 amr::Flag::IncreaseResolution};\n  const auto coarsen_refine_refine =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::IncreaseResolution,\n                 amr::Flag::IncreaseResolution};\n  const auto coarsen_stay_refine =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::DoNothing,\n                 amr::Flag::IncreaseResolution};\n  const auto coarsen_coarsen_refine =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::DecreaseResolution,\n                 amr::Flag::IncreaseResolution};\n  const auto refine_refine_coarsen =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::IncreaseResolution,\n                 amr::Flag::DecreaseResolution};\n  const auto refine_stay_coarsen =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::DoNothing,\n                 amr::Flag::DecreaseResolution};\n  const auto refine_coarsen_coarsen =\n      std::array{amr::Flag::IncreaseResolution, amr::Flag::DecreaseResolution,\n                 amr::Flag::DecreaseResolution};\n  const auto coarsen_refine_coarsen =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::IncreaseResolution,\n                 amr::Flag::DecreaseResolution};\n  const auto coarsen_stay_coarsen =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::DoNothing,\n                 amr::Flag::DecreaseResolution};\n  const auto coarsen_coarsen_coarsen =\n      std::array{amr::Flag::DecreaseResolution, amr::Flag::DecreaseResolution,\n                 amr::Flag::DecreaseResolution};\n  Mesh<3> mesh_3_5_7{std::array{3_st, 5_st, 7_st}, legendre, gauss_lobatto};\n  Mesh<3> mesh_3_6_7{std::array{3_st, 6_st, 7_st}, legendre, gauss_lobatto};\n  Mesh<3> mesh_4_5_7{std::array{4_st, 5_st, 7_st}, legendre, gauss_lobatto};\n  Mesh<3> mesh_4_6_7{std::array{4_st, 6_st, 7_st}, legendre, gauss_lobatto};\n  Mesh<3> mesh_3_5_8{std::array{3_st, 5_st, 8_st}, legendre, gauss_lobatto};\n  Mesh<3> mesh_3_6_8{std::array{3_st, 6_st, 8_st}, legendre, gauss_lobatto};\n  Mesh<3> mesh_4_5_8{std::array{4_st, 5_st, 8_st}, legendre, gauss_lobatto};\n  Mesh<3> mesh_4_6_8{std::array{4_st, 6_st, 8_st}, legendre, gauss_lobatto};\n  CHECK(amr::projectors::mesh(mesh_3_5_7, refine_refine_refine) == mesh_4_6_8);\n  CHECK(amr::projectors::mesh(mesh_3_5_7, refine_stay_refine) == mesh_4_5_8);\n  CHECK(amr::projectors::mesh(mesh_3_6_7, refine_coarsen_refine) == mesh_4_5_8);\n  CHECK(amr::projectors::mesh(mesh_4_5_7, coarsen_refine_refine) == mesh_3_6_8);\n  CHECK(amr::projectors::mesh(mesh_4_6_7, coarsen_stay_refine) == mesh_3_6_8);\n  CHECK(amr::projectors::mesh(mesh_4_6_7, coarsen_coarsen_refine) ==\n        mesh_3_5_8);\n  CHECK(amr::projectors::mesh(mesh_3_5_8, refine_refine_coarsen) == mesh_4_6_7);\n  CHECK(amr::projectors::mesh(mesh_3_5_8, refine_stay_coarsen) == mesh_4_5_7);\n  CHECK(amr::projectors::mesh(mesh_3_6_8, refine_coarsen_coarsen) ==\n        mesh_4_5_7);\n  CHECK(amr::projectors::mesh(mesh_4_5_8, coarsen_refine_coarsen) ==\n        mesh_3_6_7);\n  CHECK(amr::projectors::mesh(mesh_4_6_8, coarsen_stay_coarsen) == mesh_3_6_7);\n  CHECK(amr::projectors::mesh(mesh_4_6_8, coarsen_coarsen_coarsen) ==\n        mesh_3_5_7);\n\n  CHECK(amr::projectors::parent_mesh(\n            std::vector{mesh_3_5_7, mesh_4_5_8, mesh_3_6_7}) == mesh_4_6_8);\n#ifdef SPECTRE_DEBUG\n  Mesh<3> mesh_mismatch_basis{\n      std::array{2_st, 4_st, 3_st},\n      std::array{legendre, Spectral::Basis::Chebyshev, legendre},\n      std::array{gauss_lobatto, gauss_lobatto, gauss_lobatto}};\n  CHECK_THROWS_WITH(amr::projectors::parent_mesh(\n                        std::vector{mesh_3_5_7, mesh_mismatch_basis}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"AMR does not currently support joining elements with \"\n                        \"different quadratures or bases\"));\n  Mesh<3> mesh_mismatch_quadrature{\n      std::array{2_st, 4_st, 3_st}, std::array{legendre, legendre, legendre},\n      std::array{Spectral::Quadrature::Gauss, gauss_lobatto, gauss_lobatto}};\n  CHECK_THROWS_WITH(amr::projectors::parent_mesh(\n                        std::vector{mesh_3_5_7, mesh_mismatch_quadrature}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"AMR does not currently support joining elements with \"\n                        \"different quadratures or bases\"));\n#endif\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Projectors.Mesh\", \"[ParallelAlgorithms][Unit]\") {\n  test_mesh_1d();\n  test_mesh_2d();\n  test_mesh_3d();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Projectors/Test_Tensors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <numeric>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Tensors.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct Tag0 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\ntemplate <size_t Dim>\nstruct Tag1 : db::SimpleTag {\n  using type = tnsr::i<DataVector, Dim>;\n};\n\ntemplate <size_t Dim>\nstruct Tag2 : db::SimpleTag {\n  using type = tnsr::iJ<DataVector, Dim>;\n};\n\ntemplate <typename T>\nT f(const T& x, const std::array<double, 3>& c) {\n  return c[0] + c[1] * x + c[2] * square(x);\n}\n\ntemplate <size_t Dim>\nDataVector make_component(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& x,\n    const double scale) {\n  const auto number_of_points = get<0>(x).size();\n  DataVector result{number_of_points, scale};\n  const auto x_coeffs = std::array{0.75, -1.75, 2.75};\n  result *= f(x[0], x_coeffs);\n  if constexpr (Dim > 1) {\n    const auto y_coeffs = std::array{-0.25, 1.25, -2.25};\n    result *= f(x[1], y_coeffs);\n  }\n  if constexpr (Dim > 2) {\n    const auto z_coeffs = std::array{0.125, -1.625, -2.875};\n    result *= f(x[2], z_coeffs);\n  }\n  return result;\n}\n\ntemplate <typename TensorType, size_t Dim>\nTensorType make_tensor(const tnsr::I<DataVector, Dim, Frame::ElementLogical>& x,\n                       const double tensor_scale) {\n  TensorType result = make_with_value<TensorType>(\n      x, std::numeric_limits<double>::signaling_NaN());\n  for (size_t i = 0; i < result.size(); ++i) {\n    result[i] = make_component(x, tensor_scale + i);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test_p_refine() {\n  const ElementId<Dim> element_id{0};\n  const Element<Dim> element{element_id, DirectionMap<Dim, Neighbors<Dim>>{}};\n  const Mesh<Dim> old_mesh{4, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  std::array<size_t, Dim> new_extents{};\n  std::iota(new_extents.begin(), new_extents.end(), 3_st);\n  const Mesh<Dim> new_mesh{new_extents, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  const auto x_old = logical_coordinates(old_mesh);\n  const auto x_new = logical_coordinates(new_mesh);\n  auto scalar = make_tensor<typename Tag0::type>(x_old, 1.0);\n  auto one_form = make_tensor<typename Tag1<Dim>::type>(x_old, 4.0);\n  auto deriv = make_tensor<typename Tag2<Dim>::type>(x_old, 8.0);\n  const auto expected_scalar = make_tensor<typename Tag0::type>(x_new, 1.0);\n  const auto expected_one_form =\n      make_tensor<typename Tag1<Dim>::type>(x_new, 4.0);\n  const auto expected_deriv = make_tensor<typename Tag2<Dim>::type>(x_new, 8.0);\n\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                        Tag0, Tag1<Dim>, Tag2<Dim>>>(\n      new_mesh, element, std::move(scalar), std::move(one_form),\n      std::move(deriv));\n\n  db::mutate_apply<amr::projectors::ProjectTensors<\n      Dim, tmpl::list<Tag0, Tag1<Dim>, Tag2<Dim>>>>(\n      make_not_null(&box), std::make_pair(old_mesh, element));\n  CHECK_ITERABLE_APPROX(db::get<Tag0>(box), expected_scalar);\n  CHECK_ITERABLE_APPROX(db::get<Tag1<Dim>>(box), expected_one_form);\n  CHECK_ITERABLE_APPROX(db::get<Tag2<Dim>>(box), expected_deriv);\n}\n\ntemplate <size_t Dim>\nvoid test_h_refine() {\n  const ElementId<Dim> parent_element_id{0};\n  std::array<size_t, Dim> extents{};\n  std::iota(extents.begin(), extents.end(), 3_st);\n  const Mesh<Dim> mesh{extents, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto parent_x = logical_coordinates(mesh);\n\n  using ElementData =\n      tuples::TaggedTuple<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                          Tag0, Tag1<Dim>, Tag2<Dim>>;\n  const ElementData parent_data{\n      Element<Dim>{parent_element_id, {}}, mesh,\n      make_tensor<typename Tag0::type>(parent_x, 1.0),\n      make_tensor<typename Tag1<Dim>::type>(parent_x, 4.0),\n      make_tensor<typename Tag2<Dim>::type>(parent_x, 8.0)};\n\n  std::unordered_map<ElementId<Dim>, ElementData> children_data{};\n  for (size_t child = 0; child < two_to_the(Dim); ++child) {\n    auto child_id = parent_element_id;\n    auto child_x = parent_x;\n    for (size_t d = 0; d < Dim; ++d) {\n      const auto side = (child & (1_st << d)) == 0 ? Side::Lower : Side::Upper;\n      child_id = child_id.id_of_child(d, side);\n      child_x.get(d) *= 0.5;\n      child_x.get(d) += side == Side::Upper ? 0.5 : -0.5;\n    }\n    children_data.emplace(\n        child_id,\n        ElementData{Element<Dim>{child_id, {}}, mesh,\n                    make_tensor<typename Tag0::type>(child_x, 1.0),\n                    make_tensor<typename Tag1<Dim>::type>(child_x, 4.0),\n                    make_tensor<typename Tag2<Dim>::type>(child_x, 8.0)});\n  }\n\n  for (const auto& [child_id, child_data] : children_data) {\n    auto box = db::create<\n        db::AddSimpleTags<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                          Tag0, Tag1<Dim>, Tag2<Dim>>>(\n        mesh, get<domain::Tags::Element<Dim>>(child_data),\n        typename Tag0::type{}, typename Tag1<Dim>::type{},\n        typename Tag2<Dim>::type{});\n\n    db::mutate_apply<amr::projectors::ProjectTensors<\n        Dim, tmpl::list<Tag0, Tag1<Dim>, Tag2<Dim>>>>(make_not_null(&box),\n                                                      parent_data);\n\n    const auto& expected = children_data.at(child_id);\n    CHECK_ITERABLE_APPROX(db::get<Tag0>(box), get<Tag0>(expected));\n    CHECK_ITERABLE_APPROX(db::get<Tag1<Dim>>(box), get<Tag1<Dim>>(expected));\n    CHECK_ITERABLE_APPROX(db::get<Tag2<Dim>>(box), get<Tag2<Dim>>(expected));\n  }\n\n  {\n    auto box = db::create<\n        db::AddSimpleTags<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                          Tag0, Tag1<Dim>, Tag2<Dim>>>(\n        mesh, get<domain::Tags::Element<Dim>>(parent_data),\n        typename Tag0::type{}, typename Tag1<Dim>::type{},\n        typename Tag2<Dim>::type{});\n\n    db::mutate_apply<amr::projectors::ProjectTensors<\n        Dim, tmpl::list<Tag0, Tag1<Dim>, Tag2<Dim>>>>(make_not_null(&box),\n                                                      children_data);\n\n    CHECK_ITERABLE_APPROX(db::get<Tag0>(box), get<Tag0>(parent_data));\n    CHECK_ITERABLE_APPROX(db::get<Tag1<Dim>>(box), get<Tag1<Dim>>(parent_data));\n    CHECK_ITERABLE_APPROX(db::get<Tag2<Dim>>(box), get<Tag2<Dim>>(parent_data));\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_nonuniform_join() {\n  const ElementId<Dim> parent_element_id{0};\n\n  using ElementData =\n      tuples::TaggedTuple<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                          Tag0, Tag1<Dim>, Tag2<Dim>>;\n\n  std::unordered_map<ElementId<Dim>, ElementData> children_data{};\n  std::vector<Mesh<Dim>> children_meshes{};\n  for (size_t child = 0; child < two_to_the(Dim); ++child) {\n    const Mesh<Dim> child_mesh{child + 3, Spectral::Basis::Legendre,\n                               Spectral::Quadrature::GaussLobatto};\n    children_meshes.push_back(child_mesh);\n    auto child_id = parent_element_id;\n    auto child_x = logical_coordinates(child_mesh);\n    for (size_t d = 0; d < Dim; ++d) {\n      const auto side = (child & (1_st << d)) == 0 ? Side::Lower : Side::Upper;\n      child_id = child_id.id_of_child(d, side);\n      child_x.get(d) *= 0.5;\n      child_x.get(d) += side == Side::Upper ? 0.5 : -0.5;\n    }\n    children_data.emplace(\n        child_id,\n        ElementData{Element<Dim>{child_id, {}}, child_mesh,\n                    make_tensor<typename Tag0::type>(child_x, 1.0),\n                    make_tensor<typename Tag1<Dim>::type>(child_x, 4.0),\n                    make_tensor<typename Tag2<Dim>::type>(child_x, 8.0)});\n  }\n\n  const auto parent_mesh = amr::projectors::parent_mesh(children_meshes);\n  const auto parent_x = logical_coordinates(parent_mesh);\n\n  const ElementData parent_data{\n      Element<Dim>{parent_element_id, {}}, parent_mesh,\n      make_tensor<typename Tag0::type>(parent_x, 1.0),\n      make_tensor<typename Tag1<Dim>::type>(parent_x, 4.0),\n      make_tensor<typename Tag2<Dim>::type>(parent_x, 8.0)};\n\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                        Tag0, Tag1<Dim>, Tag2<Dim>>>(\n      parent_mesh, get<domain::Tags::Element<Dim>>(parent_data),\n      typename Tag0::type{}, typename Tag1<Dim>::type{},\n      typename Tag2<Dim>::type{});\n\n  db::mutate_apply<amr::projectors::ProjectTensors<\n      Dim, tmpl::list<Tag0, Tag1<Dim>, Tag2<Dim>>>>(make_not_null(&box),\n                                                    children_data);\n\n  auto custom_approx = Approx::custom().scale(1.0).epsilon(1.e-13);\n  CHECK_ITERABLE_CUSTOM_APPROX(db::get<Tag0>(box), get<Tag0>(parent_data),\n                               custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(db::get<Tag1<Dim>>(box),\n                               get<Tag1<Dim>>(parent_data), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(db::get<Tag2<Dim>>(box),\n                               get<Tag2<Dim>>(parent_data), custom_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Projectors.Tensors\", \"[ParallelAlgorithms][Unit]\") {\n  static_assert(\n      tt::assert_conforms_to_v<amr::projectors::ProjectTensors<\n                                   1, tmpl::list<Tag0, Tag1<1>, Tag2<1>>>,\n                               amr::protocols::Projector>);\n  test_p_refine<1>();\n  test_p_refine<2>();\n  test_p_refine<3>();\n  test_h_refine<1>();\n  test_h_refine<2>();\n  test_h_refine<3>();\n  test_nonuniform_join<1>();\n  test_nonuniform_join<2>();\n  test_nonuniform_join<3>();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Projectors/Test_Variables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <numeric>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/DirectionMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Mesh.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Variables.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/Projector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing VariablesType =\n    Variables<tmpl::list<TestHelpers::Tags::Scalar<DataVector>>>;\n\ntemplate <size_t Label>\nstruct VariablesTag : db::SimpleTag {\n  using type = VariablesType;\n};\n\ntemplate <typename T>\nT f(const T& x, const std::array<double, 3>& c) {\n  return c[0] + c[1] * x + c[2] * square(x);\n}\n\ntemplate <size_t Dim>\nVariablesType make_vars(\n    const tnsr::I<DataVector, Dim, Frame::ElementLogical>& x,\n    const double scale) {\n  const auto number_of_points = get<0>(x).size();\n  VariablesType result{number_of_points, scale};\n  const auto x_coeffs = std::array{0.75, -1.75, 2.75};\n  DataVector& s = get(get<TestHelpers::Tags::Scalar<DataVector>>(result));\n  s *= f(x[0], x_coeffs);\n  if constexpr (Dim > 1) {\n    const auto y_coeffs = std::array{-0.25, 1.25, -2.25};\n    s *= f(x[1], y_coeffs);\n  }\n  if constexpr (Dim > 2) {\n    const auto z_coeffs = std::array{0.125, -1.625, -2.875};\n    s *= f(x[2], z_coeffs);\n  }\n  return result;\n}\n\ntemplate <size_t Dim>\nvoid test_p_refine() {\n  const ElementId<Dim> element_id{0};\n  const Element<Dim> element{element_id, DirectionMap<Dim, Neighbors<Dim>>{}};\n  const Mesh<Dim> old_mesh{4, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  std::array<size_t, Dim> new_extents{};\n  std::iota(new_extents.begin(), new_extents.end(), 3_st);\n  const Mesh<Dim> new_mesh{new_extents, Spectral::Basis::Legendre,\n                           Spectral::Quadrature::GaussLobatto};\n  const auto x_old = logical_coordinates(old_mesh);\n  const auto x_new = logical_coordinates(new_mesh);\n  auto var_0 = make_vars(x_old, 1.0);\n  auto var_1 = make_vars(x_old, 2.0);\n  const auto expected_var_0 = make_vars(x_new, 1.0);\n  const auto expected_var_1 = make_vars(x_new, 2.0);\n\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                        VariablesTag<0>, VariablesTag<1>>>(\n      new_mesh, element, std::move(var_0), std::move(var_1));\n\n  db::mutate_apply<amr::projectors::ProjectVariables<\n      Dim, tmpl::list<VariablesTag<0>, VariablesTag<1>>>>(\n      make_not_null(&box), std::make_pair(old_mesh, element));\n\n  CHECK_VARIABLES_APPROX(db::get<VariablesTag<0>>(box), expected_var_0);\n  CHECK_VARIABLES_APPROX(db::get<VariablesTag<1>>(box), expected_var_1);\n}\n\ntemplate <size_t Dim>\nvoid test_h_refine() {\n  const ElementId<Dim> parent_element_id{0};\n  const Element<Dim> parent_element{parent_element_id,\n                                    DirectionMap<Dim, Neighbors<Dim>>{}};\n  const std::array children_element_ids{\n      parent_element_id.id_of_child(0, Side::Lower),\n      parent_element_id.id_of_child(0, Side::Upper)};\n  const std::array children_elements{\n      Element<Dim>{children_element_ids[0],\n                   DirectionMap<Dim, Neighbors<Dim>>{}},\n      Element<Dim>{children_element_ids[1],\n                   DirectionMap<Dim, Neighbors<Dim>>{}}};\n  const Mesh<Dim> mesh{4, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto parent_logical_coords = logical_coordinates(mesh);\n  auto parent_var_0 = make_vars(parent_logical_coords, 1.0);\n  auto parent_var_1 = make_vars(parent_logical_coords, 2.0);\n  std::array children_logical_coords{parent_logical_coords,\n                                     parent_logical_coords};\n  get<0>(children_logical_coords[0]) =\n      0.5 * (get<0>(parent_logical_coords) - 1.0);\n  get<0>(children_logical_coords[1]) =\n      0.5 * (get<0>(parent_logical_coords) + 1.0);\n  const std::array children_var_0{make_vars(children_logical_coords[0], 1.0),\n                                  make_vars(children_logical_coords[1], 1.0)};\n  const std::array children_var_1{make_vars(children_logical_coords[0], 2.0),\n                                  make_vars(children_logical_coords[1], 2.0)};\n\n  for (size_t child = 0; child < 2; ++child) {\n    auto box = db::create<\n        db::AddSimpleTags<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                          VariablesTag<0>, VariablesTag<1>>>(\n        mesh, gsl::at(children_elements, child), VariablesType{},\n        VariablesType{});\n\n    db::mutate_apply<amr::projectors::ProjectVariables<\n        Dim, tmpl::list<VariablesTag<0>, VariablesTag<1>>>>(\n        make_not_null(&box),\n        tuples::TaggedTuple<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                            VariablesTag<0>, VariablesTag<1>>(\n            parent_element, mesh, parent_var_0, parent_var_1));\n\n    CHECK_VARIABLES_APPROX(db::get<VariablesTag<0>>(box),\n                           gsl::at(children_var_0, child));\n    CHECK_VARIABLES_APPROX(db::get<VariablesTag<1>>(box),\n                           gsl::at(children_var_1, child));\n  }\n\n  {\n    auto box = db::create<\n        db::AddSimpleTags<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                          VariablesTag<0>, VariablesTag<1>>>(\n        mesh, parent_element, VariablesType{}, VariablesType{});\n\n    std::unordered_map<\n        ElementId<Dim>,\n        tuples::TaggedTuple<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                            VariablesTag<0>, VariablesTag<1>>>\n        children_data{};\n    children_data.insert(\n        {children_element_ids[0],\n         {children_elements[0], mesh, children_var_0[0], children_var_1[0]}});\n    children_data.insert(\n        {children_element_ids[1],\n         {children_elements[1], mesh, children_var_0[1], children_var_1[1]}});\n\n    db::mutate_apply<amr::projectors::ProjectVariables<\n        Dim, tmpl::list<VariablesTag<0>, VariablesTag<1>>>>(make_not_null(&box),\n                                                            children_data);\n\n    CHECK_VARIABLES_APPROX(db::get<VariablesTag<0>>(box), parent_var_0);\n    CHECK_VARIABLES_APPROX(db::get<VariablesTag<1>>(box), parent_var_1);\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_nonuniform_join() {\n  const ElementId<Dim> parent_element_id{0};\n  const Element<Dim> parent_element{parent_element_id,\n                                    DirectionMap<Dim, Neighbors<Dim>>{}};\n  const std::array children_element_ids{\n      parent_element_id.id_of_child(0, Side::Lower),\n      parent_element_id.id_of_child(0, Side::Upper)};\n  const std::array children_elements{\n      Element<Dim>{children_element_ids[0],\n                   DirectionMap<Dim, Neighbors<Dim>>{}},\n      Element<Dim>{children_element_ids[1],\n                   DirectionMap<Dim, Neighbors<Dim>>{}}};\n  std::vector<Mesh<Dim>> children_meshes{};\n  {\n    std::array<size_t, Dim> extents{};\n    std::generate(extents.begin(), extents.end(),\n                  // NOLINTNEXTLINE(spectre-mutable) - false positive\n                  [value = 3]() mutable { return value++; });\n    children_meshes.emplace_back(extents, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto);\n    std::generate(extents.begin(), extents.end(),\n                  // NOLINTNEXTLINE(spectre-mutable) - false positive\n                  [value = 5]() mutable { return value--; });\n    children_meshes.emplace_back(extents, Spectral::Basis::Legendre,\n                                 Spectral::Quadrature::GaussLobatto);\n  }\n  const Mesh<Dim> parent_mesh = amr::projectors::parent_mesh(children_meshes);\n  const auto parent_logical_coords = logical_coordinates(parent_mesh);\n  auto parent_var_0 = make_vars(parent_logical_coords, 1.0);\n  auto parent_var_1 = make_vars(parent_logical_coords, 2.0);\n  std::array children_logical_coords{logical_coordinates(children_meshes[0]),\n                                     logical_coordinates(children_meshes[1])};\n  get<0>(children_logical_coords[0]) =\n      0.5 * (get<0>(children_logical_coords[0]) - 1.0);\n  get<0>(children_logical_coords[1]) =\n      0.5 * (get<0>(children_logical_coords[1]) + 1.0);\n  const std::array children_var_0{make_vars(children_logical_coords[0], 1.0),\n                                  make_vars(children_logical_coords[1], 1.0)};\n  const std::array children_var_1{make_vars(children_logical_coords[0], 2.0),\n                                  make_vars(children_logical_coords[1], 2.0)};\n\n  auto box = db::create<\n      db::AddSimpleTags<domain::Tags::Mesh<Dim>, domain::Tags::Element<Dim>,\n                        VariablesTag<0>, VariablesTag<1>>>(\n      parent_mesh, parent_element, VariablesType{}, VariablesType{});\n\n  std::unordered_map<\n      ElementId<Dim>,\n      tuples::TaggedTuple<domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n                          VariablesTag<0>, VariablesTag<1>>>\n      children_data{};\n  children_data.insert({children_element_ids[0],\n                        {children_elements[0], children_meshes[0],\n                         children_var_0[0], children_var_1[0]}});\n  children_data.insert({children_element_ids[1],\n                        {children_elements[1], children_meshes[1],\n                         children_var_0[1], children_var_1[1]}});\n\n  db::mutate_apply<amr::projectors::ProjectVariables<\n      Dim, tmpl::list<VariablesTag<0>, VariablesTag<1>>>>(make_not_null(&box),\n                                                          children_data);\n\n  CHECK_VARIABLES_APPROX(db::get<VariablesTag<0>>(box), parent_var_0);\n  CHECK_VARIABLES_APPROX(db::get<VariablesTag<1>>(box), parent_var_1);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Amr.Projectors.Variables\",\n                  \"[ParallelAlgorithms][Unit]\") {\n  static_assert(tt::assert_conforms_to_v<\n                amr::projectors::ProjectVariables<\n                    1, tmpl::list<VariablesTag<0>, VariablesTag<1>>>,\n                amr::protocols::Projector>);\n  test_p_refine<1>();\n  test_p_refine<2>();\n  test_p_refine<3>();\n  test_h_refine<1>();\n  test_h_refine<2>();\n  test_h_refine<3>();\n  test_nonuniform_join<1>();\n  test_nonuniform_join<2>();\n  test_nonuniform_join<3>();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Test_Protocols.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Evolution/Initialization/DgDomain.hpp\"\n#include \"Evolution/Initialization/Evolution.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct ElementArray;\n\nstruct Metavariables {\n  // [amr_projectors]\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = ElementArray;\n    using projectors =\n        tmpl::list<Initialization::ProjectTimeStepping<1>,\n                   evolution::dg::Initialization::ProjectDomain<1>>;\n    [[maybe_unused]] static constexpr bool keep_coarse_grids = false;\n  };\n  // [amr_projectors]\n};\nstatic_assert(tt::assert_conforms_to_v<Metavariables::amr,\n                                       ::amr::protocols::AmrMetavariables>);\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Amr/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/Amr/Tags.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Amr.Tags\",\n                  \"[ParallelAlgorithms][Unit]\") {\n  TestHelpers::db::test_simple_tag<amr::Tags::AmrBlocks<1>>(\"AmrBlocks\");\n  TestHelpers::db::test_simple_tag<amr::Tags::AllElementIds<1>>(\n      \"AllElementIds\");\n  TestHelpers::db::test_simple_tag<amr::Tags::ParentId<1>>(\"ParentId\");\n  TestHelpers::db::test_simple_tag<amr::Tags::ChildIds<1>>(\"ChildIds\");\n  TestHelpers::db::test_simple_tag<amr::Tags::ParentMesh<1>>(\"ParentMesh\");\n  TestHelpers::db::test_compute_tag<\n      amr::Tags::GridIndexObservationKeyCompute<1>>(\n      \"ObservationKey(GridIndex)\");\n  TestHelpers::db::test_compute_tag<\n      amr::Tags::IsFinestGridObservationKeyCompute<1>>(\n      \"ObservationKey(IsFinestGrid)\");\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ApparentHorizonFinder\")\n\nset(LIBRARY_SOURCES\n  Test_CleanUp.cpp\n  Test_ComputeCoords.cpp\n  Test_ComputeVarsToInterpolateToTarget.cpp\n  Test_CurrentTime.cpp\n  Test_Destination.cpp\n  Test_FastFlow.cpp\n  Test_FindApparentHorizon.cpp\n  Test_Initialization.cpp\n  Test_InterpolateVolumeVars.cpp\n  Test_OptionTags.cpp\n  Test_Storage.cpp\n  Test_Tags.cpp\n  )\n\nadd_subdirectory(Callbacks)\nadd_subdirectory(Events)\nadd_subdirectory(Protocols)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ApparentHorizonFinder\n  ApparentHorizonFinderCriteria\n  FiniteDifference\n  GeneralRelativity\n  GeneralRelativitySolutions\n  GrSurfacesHelpers\n  InterpolationHelpers\n  IoTestHelpers\n  LinearOperators\n  Logging\n  ObserverHelpers\n  Options\n  RootFinding\n  SphericalHarmonics\n  SphericalHarmonicsHelpers\n  Time\n  Utilities\n  )\n\nadd_subdirectory(Criteria)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Callbacks/Test_InvokeCallbacks.cpp\n  Callbacks/Test_FailedHorizonFind.cpp\n  Callbacks/Test_ObserveCenters.cpp\n  Callbacks/Test_ObserveFieldsAndTimeSeriesOnHorizon.cpp\n  Callbacks/Test_SendDependencyToObserverWriter.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/Test_FailedHorizonFind.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Fr, bool Ignore>\nstruct HorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using temporal_id_tag = ::Tags::TimeAndPrevious<0>;\n  using frame = Fr;\n\n  using horizon_find_callbacks = tmpl::list<>;\n  using horizon_find_failure_callbacks =\n      tmpl::list<ah::callbacks::FailedHorizonFind<HorizonMetavars, Ignore>>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::ControlSystem;\n\n  static std::string name() { return \"TestingHorizonMetavars\"; }\n};\n\nstruct EmptyMetavars {\n  using component_list = tmpl::list<>;\n};\n\ntemplate <typename Fr>\nvoid run_test() {\n  const Parallel::GlobalCache<EmptyMetavars> cache{};\n\n  const domain::creators::Sphere sphere_creator{\n      0.9, 2.0, domain::creators::Sphere::Excision{nullptr}, 0_st, 4_st, true};\n  const Domain<3> domain = sphere_creator.create_domain();\n\n  const size_t l_max = 6;\n  const LinkedMessageId<double> time{2.0, {1.0}};\n\n  ah::Storage::Iteration<Fr> current_iteration{};\n  current_iteration.compute_coords_retries = 2;\n  current_iteration.strahlkorper =\n      ylm::Strahlkorper<Fr>{l_max, 1.34, std::array{0.0, 0.0, 0.0}};\n  current_iteration.block_coord_holders = ::block_logical_coordinates(\n      domain, ylm::cartesian_coords(current_iteration.strahlkorper));\n\n  // Manually set 4 points to be invalid\n  for (size_t i = 0; i < 4; i++) {\n    current_iteration.block_coord_holders.value()[i * 4].reset();\n  }\n\n  ah::Storage::SingleTimeStorage<Fr> current_time_storage{};\n  current_time_storage.current_iteration = current_iteration;\n\n  std::unordered_map<LinkedMessageId<double>,\n                     ah::Storage::SingleTimeStorage<Fr>>\n      all_storage{};\n  all_storage[time] = current_time_storage;\n\n  auto box = db::create<\n      db::AddSimpleTags<tmpl::list<ah::Tags::CurrentTime, ah::Tags::Storage<Fr>,\n                                   ah::Tags::FastFlow, ah::Tags::Verbosity>>>(\n      std::optional{time}, all_storage,\n      FastFlow{FastFlow::FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5, 100},\n      ::Verbosity::Quiet);\n\n  static_assert(\n      tt::assert_conforms_to_v<\n          ah::callbacks::FailedHorizonFind<HorizonMetavars<Fr, true>, true>,\n          ah::protocols::Callback>);\n  static_assert(\n      tt::assert_conforms_to_v<\n          ah::callbacks::FailedHorizonFind<HorizonMetavars<Fr, false>, false>,\n          ah::protocols::Callback>);\n\n  // When we ignore the error, we just check that we can call things without an\n  // error since things are only printed using printf.\n  {\n    ah::callbacks::FailedHorizonFind<HorizonMetavars<Fr, true>, true>::apply(\n        box, cache, FastFlow::Status::MaxIts);\n    ah::callbacks::FailedHorizonFind<HorizonMetavars<Fr, true>, true>::apply(\n        box, cache, FastFlow::Status::InterpolationFailure);\n  }\n  // When erroring, check the message\n  {\n    CHECK_THROWS_WITH(\n        (ah::callbacks::FailedHorizonFind<HorizonMetavars<Fr, false>, false>::\n             apply(box, cache, FastFlow::Status::MaxIts)),\n        Catch::Matchers::ContainsSubstring(\"TestingHorizonMetavars\") and\n            Catch::Matchers::ContainsSubstring(\"Too many iterations\") and\n            Catch::Matchers::ContainsSubstring(\"retries = 2\") and\n            not Catch::Matchers::ContainsSubstring(\"Invalid points\"));\n    CHECK_THROWS_WITH(\n        (ah::callbacks::FailedHorizonFind<HorizonMetavars<Fr, false>, false>::\n             apply(box, cache, FastFlow::Status::InterpolationFailure)),\n        Catch::Matchers::ContainsSubstring(\"TestingHorizonMetavars\") and\n            Catch::Matchers::ContainsSubstring(\n                \"Cannot interpolate onto surface\") and\n            Catch::Matchers::ContainsSubstring(\"retries = 2\") and\n            Catch::Matchers::ContainsSubstring(\"Invalid points\"));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.FailedHorizonFind\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  domain::creators::register_derived_with_charm();\n  run_test<Frame::Grid>();\n  run_test<Frame::Inertial>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/Test_InvokeCallbacks.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <deque>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/InvokeCallbacks.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nconstexpr size_t l_max = 6;\nconstexpr double radius = 1.34;\n\ntemplate <typename HorizonMetavars>\nstruct TestCallback : tt::ConformsTo<ah::protocols::Callback> {\n private:\n  using Fr = typename HorizonMetavars::frame;\n\n public:\n  template <typename DbTags, typename Metavariables>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const FastFlow::Status failure_reason) {\n    const auto& time = db::get<ah::Tags::CurrentTime>(box);\n    CHECK(time == std::optional{LinkedMessageId<double>{2.0, {1.0}}});\n    CHECK(failure_reason == FastFlow::Status::TruncationTol);\n    CHECK(db::get<ah::Tags::FastFlow>(box) == FastFlow{FastFlow::FlowType::Fast,\n                                                       1.0, 0.5, 1.e-12, 1.e-2,\n                                                       1.2, 5, 100});\n    CHECK(db::get<ah::Tags::Dependency>(box) ==\n          std::optional{\"FakeDependency\"});\n\n    const auto& strahlkorper = db::get<ylm::Tags::Strahlkorper<Fr>>(box);\n    CHECK(strahlkorper ==\n          ylm::Strahlkorper<Fr>{l_max, radius, std::array{0.0, 0.0, 0.0}});\n\n    // Check that we restricted properly\n    const size_t expected_size = ylm::Spherepack::physical_size(l_max, l_max);\n    tmpl::for_each<ah::vars_to_interpolate_to_target<3, Fr>>(\n        [&]<typename Tag>(tmpl::type_<Tag>) {\n          const auto& var = db::get<Tag>(box);\n          for (size_t i = 0; i < var.size(); i++) {\n            CHECK(var[i].size() == expected_size);\n          }\n        });\n\n    // Time deriv is zero because there weren't any previous horizons to compute\n    // the time deriv with\n    CHECK(db::get<ylm::Tags::TimeDerivStrahlkorper<Fr>>(box).coefficients() ==\n          DataVector{ylm::Spherepack::spectral_size(l_max, l_max), 0.0});\n  }\n};\n\ntemplate <typename Fr>\nstruct HorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using temporal_id_tag = ::Tags::TimeAndPrevious<0>;\n  using frame = Fr;\n\n  using horizon_find_callbacks = tmpl::list<TestCallback<HorizonMetavars>>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::ControlSystem;\n\n  static std::string name() { return \"TestingHorizonMetavars\"; }\n};\n\ntemplate <typename Fr>\nstruct Metavariables {\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<3>>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 ah::Tags::PreviousSurface<HorizonMetavars<Fr>>>;\n  using component_list = tmpl::list<>;\n};\n\ntemplate <typename Fr>\nvoid run_test() {\n  const domain::creators::Sphere sphere_creator{\n      0.9, 2.0, domain::creators::Sphere::Excision{nullptr}, 0_st, 4_st, true};\n\n  Parallel::GlobalCache<Metavariables<Fr>> cache{\n      {sphere_creator.create_domain()},\n      {sphere_creator.functions_of_time(),\n       ah::Storage::LockedPreviousSurface<Fr>{}}};\n\n  const LinkedMessageId<double> time{2.0, {1.0}};\n  const FastFlow fast_flow{\n      FastFlow::FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5, 100};\n  const std::optional<std::string> dependency{\"FakeDependency\"};\n\n  ah::Storage::Iteration<Fr> current_iteration{};\n  current_iteration.strahlkorper =\n      ylm::Strahlkorper<Fr>{l_max, radius, std::array{0.0, 0.0, 0.0}};\n  current_iteration.intersecting_element_ids = {ElementId<3>{0},\n                                                ElementId<3>{1}};\n  const size_t l_mesh =\n      fast_flow.current_l_mesh(current_iteration.strahlkorper);\n  // The actual values don't matter for this test\n  current_iteration.interpolated_vars =\n      Variables<ah::vars_to_interpolate_to_target<3, Fr>>{\n          ylm::Spherepack::physical_size(l_mesh, l_mesh), 3.21};\n\n  ah::Storage::SingleTimeStorage<Fr> current_time_storage{};\n  current_time_storage.current_iteration = current_iteration;\n\n  std::unordered_map<LinkedMessageId<double>,\n                     ah::Storage::SingleTimeStorage<Fr>>\n      all_storage{};\n  all_storage[time] = current_time_storage;\n\n  const FastFlow::Status status = FastFlow::Status::TruncationTol;\n\n  auto box = db::create<db::AddSimpleTags<tmpl::list<\n      ah::Tags::CurrentTime, ah::Tags::Storage<Fr>,\n      ah::Tags::PreviousSurfaces<Fr>, ah::Tags::FastFlow, ah::Tags::Verbosity,\n      ylm::Tags::Strahlkorper<Fr>, ylm::Tags::TimeDerivStrahlkorper<Fr>,\n      ah::Tags::Dependency, ylm::Tags::CartesianCoords<Frame::Inertial>,\n      ::Tags::Variables<ah::vars_to_interpolate_to_target<3, Fr>>>>>(\n      std::optional{time}, all_storage,\n      std::deque<ah::Storage::PreviousSurface<Fr>>{},\n      FastFlow{FastFlow::FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5, 100},\n      ::Verbosity::Quiet, ylm::Strahlkorper<Fr>{}, ylm::Strahlkorper<Fr>{},\n      std::optional<std::string>{}, tnsr::I<DataVector, 3>{},\n      Variables<ah::vars_to_interpolate_to_target<3, Fr>>{});\n\n  ah::invoke_callbacks<HorizonMetavars<Fr>>(make_not_null(&box), cache,\n                                            dependency, status);\n\n  const auto& box_previous_surfaces =\n      db::get<ah::Tags::PreviousSurfaces<Fr>>(box);\n  REQUIRE_FALSE(box_previous_surfaces.empty());\n  CHECK(box_previous_surfaces.front().time == time);\n  CHECK(box_previous_surfaces.front().surface ==\n        current_iteration.strahlkorper);\n  CHECK(box_previous_surfaces.front().intersecting_element_ids ==\n        current_iteration.intersecting_element_ids);\n  const auto& cache_previous_surface =\n      get<ah::Tags::PreviousSurface<HorizonMetavars<Fr>>>(cache);\n  CHECK(cache_previous_surface.surface == box_previous_surfaces.front());\n  CHECK(db::get<ylm::Tags::Strahlkorper<Fr>>(box) ==\n        current_iteration.strahlkorper);\n  CHECK(db::get<ylm::Tags::TimeDerivStrahlkorper<Fr>>(box).coefficients() ==\n        DataVector{current_iteration.strahlkorper.coefficients().size(), 0.0});\n  CHECK(db::get<ah::Tags::Dependency>(box) == dependency);\n  CHECK(\n      db::get<::Tags::Variables<ah::vars_to_interpolate_to_target<3, Fr>>>(box)\n          .number_of_grid_points() ==\n      ylm::Spherepack::physical_size(l_max, l_max));\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.InvokeCallbacks\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  domain::creators::register_derived_with_charm();\n  run_test<Frame::Grid>();\n  run_test<Frame::Inertial>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/Test_ObserveCenters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/IO/Observers/MockWriteReductionDataRow.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveCenters.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename Fr>\nstruct HorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using temporal_id_tag = ::Tags::TimeAndPrevious<0>;\n  using frame = Fr;\n\n  using horizon_find_callbacks =\n      tmpl::list<ah::callbacks::ObserveCenters<HorizonMetavars>>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::ControlSystem;\n\n  static std::string name() {\n    return pretty_type::name<Fr>() + \"TestingHorizonMetavars\";\n  }\n};\n\nstruct TestMetavars {\n  using const_global_cache_tags = tmpl::list<ah::Tags::ObserveCenters>;\n  using component_list =\n      tmpl::list<TestHelpers::observers::MockObserverWriter<TestMetavars>>;\n};\n\nusing FoTPtr = std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>;\n\ntemplate <typename LocalHorizonMetavars>\nvoid test() {\n  using frame = typename LocalHorizonMetavars::frame;\n  using observer_writer =\n      TestHelpers::observers::MockObserverWriter<TestMetavars>;\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> center_dist{-10.0, 10.0};\n\n  // set up runner and stuff\n  ActionTesting::MockRuntimeSystem<TestMetavars> runner{{true}};\n  runner.set_phase(Parallel::Phase::Initialization);\n  ActionTesting::emplace_nodegroup_component_and_initialize<observer_writer>(\n      make_not_null(&runner), {});\n  auto& cache = ActionTesting::cache<observer_writer>(runner, 0);\n\n  runner.set_phase(Parallel::Phase::Execute);\n\n  const auto make_center = [&gen, &center_dist]() -> std::array<double, 3> {\n    return make_with_random_values<std::array<double, 3>>(\n        make_not_null(&gen), center_dist, std::array<double, 3>{});\n  };\n\n  // Lists of centers so we can check that the correct centers were written\n  std::vector<std::array<double, 3>> grid_centers{};\n  std::vector<std::array<double, 3>> inertial_centers{};\n\n  db::DataBox<tmpl::list<ah::Tags::CurrentTime, ylm::Tags::Strahlkorper<frame>,\n                         ylm::Tags::CartesianCoords<::Frame::Inertial>,\n                         ylm::Tags::EuclideanAreaElement<frame>>>\n      box{};\n\n  const auto update_stored_centers = [&make_center, &grid_centers,\n                                      &inertial_centers,\n                                      &box](const LinkedMessageId<double>&\n                                                current_time) {\n    const auto grid_center = make_center();\n    const auto inertial_center = make_center();\n\n    grid_centers.push_back(grid_center);\n    inertial_centers.push_back(inertial_center);\n\n    db::mutate<ah::Tags::CurrentTime, ylm::Tags::Strahlkorper<frame>,\n               ylm::Tags::CartesianCoords<::Frame::Inertial>,\n               ylm::Tags::EuclideanAreaElement<frame>>(\n        [&](const gsl::not_null<std::optional<LinkedMessageId<double>>*>\n                box_current_time,\n            const gsl::not_null<ylm::Strahlkorper<frame>*> box_grid_horizon,\n            const gsl::not_null<tnsr::I<DataVector, 3, ::Frame::Inertial>*>\n                box_inertial_coords,\n            const gsl::not_null<Scalar<DataVector>*> box_area_element) {\n          *box_current_time = current_time;\n          *box_grid_horizon = ylm::Strahlkorper<frame>{10, 1.0, grid_center};\n          const auto theta_phi = ylm::theta_phi(*box_grid_horizon);\n          const auto radius = ylm::radius(*box_grid_horizon);\n          const auto rhat = ylm::rhat(theta_phi);\n\n          // Set area element to the Euclidean area element for the test.\n          *box_area_element = gr::surfaces::euclidean_area_element(\n              ylm::jacobian(theta_phi),\n              ylm::normal_one_form(ylm::cartesian_derivs_of_scalar(\n                                       radius, *box_grid_horizon, radius,\n                                       ylm::inv_jacobian(theta_phi)),\n                                   rhat),\n              radius, rhat);\n\n          // Simply offset the inertial coords from the grid coords.\n          const auto grid_coords =\n              ylm::cartesian_coords(*box_grid_horizon, radius, rhat);\n          for (size_t i = 0; i < 3; ++i) {\n            box_inertial_coords->get(i) = grid_coords.get(i) +\n                                          gsl::at(inertial_center, i) -\n                                          gsl::at(grid_center, i);\n          }\n        },\n        make_not_null(&box));\n  };\n\n  // times to write\n  const std::vector<double> times{0.0, 0.1, 0.2, 0.3, 0.4, 0.5};\n\n  // write some data\n  for (size_t i = 0; i < times.size(); i++) {\n    const LinkedMessageId<double> current_time{\n        times[i], i == 0 ? std::nullopt : std::optional{times[i - 1]}};\n    update_stored_centers(current_time);\n\n    ah::callbacks::ObserveCenters<LocalHorizonMetavars>::apply(\n        box, cache, FastFlow::Status::AbsTol);\n\n    const size_t num_threaded_actions =\n        ActionTesting::number_of_queued_threaded_actions<observer_writer>(\n            runner, 0);\n    CHECK(num_threaded_actions == 1);\n    ActionTesting::invoke_queued_threaded_action<observer_writer>(\n        make_not_null(&runner), 0);\n  }\n\n  // These must be the same as in ObserveCenters\n  const std::vector<std::string> compare_legend{\n      {\"Time\", \"GridCenter_x\", \"GridCenter_y\", \"GridCenter_z\",\n       \"InertialCenter_x\", \"InertialCenter_y\", \"InertialCenter_z\"}};\n  const std::string subfile_name =\n      \"/ApparentHorizons/\" + LocalHorizonMetavars::name() + \"_Centers\";\n\n  auto& h5_file = ActionTesting::get_databox_tag<\n      observer_writer, TestHelpers::observers::MockReductionFileTag>(runner, 0);\n  const auto& dataset = h5_file.get_dat(subfile_name);\n  const Matrix data = dataset.get_data();\n  const std::vector<std::string>& legend = dataset.get_legend();\n\n  // Check legend is correct\n  for (size_t i = 0; i < legend.size(); i++) {\n    CHECK(legend[i] == compare_legend[i]);\n  }\n\n  // Check proper number of times were written\n  CHECK(data.rows() == times.size());\n\n  // Check centers\n  for (size_t i = 0; i < times.size(); i++) {\n    CHECK(data(i, 0) == times[i]);\n\n    const std::array<double, 3>& grid_center = grid_centers[i];\n    const std::array<double, 3>& inertial_center = inertial_centers[i];\n    for (size_t j = 0; j < grid_center.size(); j++) {\n      // Grid center is columns 2-4\n      CHECK(data(i, j + 1) == approx(gsl::at(grid_center, j)));\n      // Inertial center is columns 5-7\n      CHECK(data(i, j + 4) == approx(gsl::at(inertial_center, j)));\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.ObserveCenters\",\n                  \"[Unit][ApparentHorizonFinder]\") {\n  test<HorizonMetavars<::Frame::Grid>>();\n  test<HorizonMetavars<::Frame::Distorted>>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/Test_ObserveFieldsAndTimeSeriesOnHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <optional>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/IO/VolumeData.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/Surfaces/TestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveFieldsOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/ObserveTimeSeriesOnHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Initialization.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nconstexpr size_t low_l_max = 3;\nconstexpr size_t high_l_max = 16;\n\ntemplate <typename Fr>\nVariables<ah::vars_to_interpolate_to_target<3, Fr>> make_vars(\n    const ylm::Strahlkorper<Fr>& horizon, const LinkedMessageId<double>& time,\n    const double mass) {\n  const gr::Solutions::KerrSchild solution{mass, std::array{0.0, 0.0, 0.0},\n                                           std::array{0.0, 0.0, 0.0}};\n  const auto coords = ylm::cartesian_coords(horizon);\n  const auto vars = solution.variables(\n      coords, time.id,\n      tmpl::pop_back<ah::vars_to_interpolate_to_target<3, Fr>>{});\n  const size_t size =\n      get<0, 0>(get<gr::Tags::SpatialMetric<DataVector, 3, Fr>>(vars)).size();\n  Variables<ah::vars_to_interpolate_to_target<3, Fr>> result{size, 0.0};\n  result.assign_subset(vars);\n  get<gr::Tags::SpatialRicci<DataVector, 3, Fr>>(result) =\n      TestHelpers::Schwarzschild::spatial_ricci(coords, mass);\n\n  return result;\n}\n\nvoid check_ylm_data(\n    const std::string& h5_file_name,\n    const ylm::Strahlkorper<Frame::Inertial>& expected_surface) {\n  const std::vector<std::string> ylm_expected_legend{\n      \"Time\",\n      \"InertialExpansionCenter_x\",\n      \"InertialExpansionCenter_y\",\n      \"InertialExpansionCenter_z\",\n      \"Lmax\",\n      \"coef(0,0)\",\n      \"coef(1,-1)\",\n      \"coef(1,0)\",\n      \"coef(1,1)\",\n      \"coef(2,-2)\",\n      \"coef(2,-1)\",\n      \"coef(2,0)\",\n      \"coef(2,1)\",\n      \"coef(2,2)\",\n      \"coef(3,-3)\",\n      \"coef(3,-2)\",\n      \"coef(3,-1)\",\n      \"coef(3,0)\",\n      \"coef(3,1)\",\n      \"coef(3,2)\",\n      \"coef(3,3)\"};\n  const size_t expected_num_columns = ylm_expected_legend.size();\n\n  // Check that the H5 file was written correctly.\n  const auto file = h5::H5File<h5::AccessType::ReadOnly>(h5_file_name);\n  const auto& ylm_dat_file = file.get<h5::Dat>(\"/HorizonD_Ylm\");\n  const Matrix ylm_written_data = ylm_dat_file.get_data();\n  const auto& ylm_written_legend = ylm_dat_file.get_legend();\n\n  CHECK(ylm_written_legend.size() == expected_num_columns);\n  CHECK(ylm_written_data.columns() == expected_num_columns);\n  CHECK(ylm_written_legend == ylm_expected_legend);\n\n  // Center is origin\n  std::vector<double> ylm_expected_data{2.0, 0.0, 0.0, 0.0, low_l_max};\n\n  ylm::SpherepackIterator iter(low_l_max, low_l_max);\n  for (size_t l = 0; l <= low_l_max; l++) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n      iter.set(l, m);\n      ylm_expected_data.push_back(expected_surface.coefficients()[iter()]);\n    }\n  }\n\n  ASSERT(ylm_expected_data.size() == expected_num_columns,\n         \"The size of the constructed test Ylm legend (\"\n             << expected_num_columns\n             << \") and the number of columns in the constructed test Ylm data (\"\n             << ylm_expected_data.size() << \") do not match.\");\n\n  for (size_t i = 0; i < expected_num_columns; i++) {\n    CHECK(ylm_written_data(0, i) == ylm_expected_data[i]);\n  }\n}\n\nvoid check_surface_volume_data(\n    const std::string& surfaces_file_name,\n    const ylm::Strahlkorper<Frame::Inertial>& strahlkorper,\n    const LinkedMessageId<double>& time, const double mass) {\n  const ylm::Spherepack& ylm = strahlkorper.ylm_spherepack();\n  const std::vector<size_t> extents{\n      {ylm.physical_extents()[0], ylm.physical_extents()[1]}};\n  const std::string grid_name{\"HorizonD\"};\n\n  const auto coords = ylm::cartesian_coords(strahlkorper);\n  const auto all_vars = make_vars(strahlkorper, time, mass);\n  const std::vector<DataVector> tensor_and_coord_data{\n      get<0>(coords), get<1>(coords), get<2>(coords),\n      DataVector{get<0>(coords).size(), 0.5 / square(mass)}};\n  const std::vector<TensorComponent> tensor_components{\n      {grid_name + \"/InertialCoordinates_x\", tensor_and_coord_data[0]},\n      {grid_name + \"/InertialCoordinates_y\", tensor_and_coord_data[1]},\n      {grid_name + \"/InertialCoordinates_z\", tensor_and_coord_data[2]},\n      {grid_name + \"/RicciScalar\", tensor_and_coord_data[3]}};\n\n  const std::vector<Spectral::Basis> bases{2,\n                                           Spectral::Basis::SphericalHarmonic};\n  const std::vector<Spectral::Quadrature> quadratures{\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Equiangular}};\n  const observers::ObservationId observation_id{2., \"/HorizonD.vol\"};\n  TestHelpers::io::VolumeData::check_volume_data(\n      surfaces_file_name, 0, grid_name, observation_id.hash(),\n      observation_id.value(), std::nullopt, tensor_and_coord_data, {grid_name},\n      {bases}, {quadratures}, {extents},\n      {\"InertialCoordinates_x\"s, \"InertialCoordinates_y\"s,\n       \"InertialCoordinates_z\"s, \"RicciScalar\"s},\n      {{0, 1, 2, 3}}, std::optional{1.0e-10});\n}\n\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using const_global_cache_tags = tmpl::list<observers::Tags::ReductionFileName,\n                                             observers::Tags::SurfaceFileName>;\n  using simple_tags =\n      typename observers::Actions::InitializeWriter<Metavariables>::simple_tags;\n  using compute_tags = typename observers::Actions::InitializeWriter<\n      Metavariables>::compute_tags;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<observers::Actions::InitializeWriter<Metavariables>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n};\n\ntemplate <typename Metavariables, typename HorizonMetavars>\nstruct MockComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using component_being_mocked = ah::Component<Metavariables, HorizonMetavars>;\n\n  using frame = typename HorizonMetavars::frame;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<\n          typename ah::Initialize<HorizonMetavars>::simple_tags,\n          typename ah::Initialize<HorizonMetavars>::compute_tags>>>>;\n};\n\nstruct MockMetavariables {\n  template <typename Fr>\n  struct HorizonAB : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n    using temporal_id_tag = ::Tags::TimeAndPrevious<0>;\n    using frame = Fr;\n\n    using horizon_find_callbacks =\n        tmpl::list<ah::callbacks::ObserveTimeSeriesOnHorizon<\n            tmpl::list<gr::surfaces::Tags::AreaCompute<frame>>, HorizonAB>>;\n    using horizon_find_failure_callbacks = tmpl::list<>;\n\n    using compute_tags_on_element = tmpl::list<>;\n\n    static constexpr ah::Destination destination =\n        ah::Destination::ControlSystem;\n\n    static std::string name() {\n      return \"Horizon\"s + (std::is_same_v<Fr, Frame::Inertial> ? \"A\"s : \"B\"s);\n    }\n  };\n\n  using HorizonA = HorizonAB<Frame::Inertial>;\n  using HorizonB = HorizonAB<Frame::Grid>;\n\n  struct HorizonC : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n    using temporal_id_tag = ::Tags::TimeAndPrevious<0>;\n    using frame = Frame::Inertial;\n\n    using horizon_find_callbacks =\n        tmpl::list<ah::callbacks::ObserveTimeSeriesOnHorizon<\n            tmpl::list<ylm::Tags::MaxRicciScalarCompute>, HorizonC>>;\n    using horizon_find_failure_callbacks = tmpl::list<>;\n\n    using compute_tags_on_element = tmpl::list<>;\n\n    static constexpr ah::Destination destination =\n        ah::Destination::ControlSystem;\n\n    static std::string name() { return \"HorizonC\"; }\n  };\n\n  struct HorizonD : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n    using temporal_id_tag = ::Tags::TimeAndPrevious<0>;\n    using frame = Frame::Inertial;\n\n    using horizon_find_callbacks =\n        tmpl::list<ah::callbacks::ObserveFieldsOnHorizon<\n            tmpl::list<ylm::Tags::RicciScalar>, HorizonD>>;\n    using horizon_find_failure_callbacks = tmpl::list<>;\n\n    using compute_tags_on_element = tmpl::list<>;\n\n    static constexpr ah::Destination destination = ah::Destination::Observation;\n\n    static std::string name() { return \"HorizonD\"; }\n  };\n\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  using component_list = tmpl::list<MockObserverWriter<MockMetavariables>,\n                                    MockComponent<MockMetavariables, HorizonA>,\n                                    MockComponent<MockMetavariables, HorizonB>,\n                                    MockComponent<MockMetavariables, HorizonC>,\n                                    MockComponent<MockMetavariables, HorizonD>>;\n};\n\nvoid run_test() {\n  const auto remove_file_if_exists = [](const std::string& file_name) {\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n  };\n\n  // Check if either file generated by this test exists and remove them\n  // if so. Check for both files existing before the test runs, since\n  // both files get written when evaluating the list of post interpolation\n  // callbacks below.\n  const std::string h5_file_prefix = \"Test_ObserveFieldsAndTimeSeriesOnHorizon\";\n  const std::string h5_file_name = h5_file_prefix + \".h5\";\n  remove_file_if_exists(h5_file_name);\n  const std::string surfaces_file_prefix = \"Surfaces\";\n  const std::string surfaces_file_name = surfaces_file_prefix + \".h5\";\n  remove_file_if_exists(surfaces_file_name);\n\n  // Test That ObserveTimeSeriesOnSurface indeed does conform to its protocol\n  using metavars = MockMetavariables;\n  (void)metavars::HorizonA::destination;\n  (void)metavars::HorizonB::destination;\n  (void)metavars::HorizonC::destination;\n  (void)metavars::HorizonD::destination;\n  using horizon_a_callback =\n      tmpl::front<typename metavars::HorizonA::horizon_find_callbacks>;\n  using horizon_b_callback =\n      tmpl::front<typename metavars::HorizonB::horizon_find_callbacks>;\n  using horizon_c_callback =\n      tmpl::front<typename metavars::HorizonC::horizon_find_callbacks>;\n  using horizon_d_callback =\n      tmpl::front<typename metavars::HorizonD::horizon_find_callbacks>;\n  using protocol = ah::protocols::Callback;\n  static_assert(tt::assert_conforms_to_v<horizon_a_callback, protocol>);\n  static_assert(tt::assert_conforms_to_v<horizon_b_callback, protocol>);\n  static_assert(tt::assert_conforms_to_v<horizon_c_callback, protocol>);\n  static_assert(tt::assert_conforms_to_v<horizon_d_callback, protocol>);\n\n  using horizon_a_component = MockComponent<metavars, metavars::HorizonA>;\n  using horizon_b_component = MockComponent<metavars, metavars::HorizonB>;\n  using horizon_c_component = MockComponent<metavars, metavars::HorizonC>;\n  using horizon_d_component = MockComponent<metavars, metavars::HorizonD>;\n  using obs_writer = MockObserverWriter<metavars>;\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {h5_file_prefix, surfaces_file_prefix}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n\n  const LinkedMessageId<double> time{2.0, {1.0}};\n  const double mass = 0.5;\n  const double radius = 2.0 * mass;\n  const std::array initial_center{0.0, 0.0, 0.0};\n\n  const auto initialize_component =\n      [&]<typename Component>(Component /*component_v*/, const size_t l_max) {\n        using Fr = typename Component::frame;\n\n        const ylm::Strahlkorper<Fr> horizon{l_max, radius, initial_center};\n\n        ActionTesting::emplace_array_component<Component>(\n            make_not_null(&runner), ActionTesting::NodeId{0},\n            ActionTesting::LocalCoreId{0}, 0);\n\n        auto& box =\n            ActionTesting::get_databox<Component>(make_not_null(&runner), 0);\n\n        db::mutate<ylm::Tags::Strahlkorper<Fr>, ah::Tags::CurrentTime,\n                   ::Tags::Variables<ah::vars_to_interpolate_to_target<3, Fr>>>(\n            [&](const gsl::not_null<ylm::Strahlkorper<Fr>*> strahlkorper,\n                const gsl::not_null<std::optional<LinkedMessageId<double>>*>\n                    current_time,\n                const gsl::not_null<\n                    ::Variables<ah::vars_to_interpolate_to_target<3, Fr>>*>\n                    vars) {\n              (*strahlkorper) = horizon;\n              (*current_time) = time;\n              (*vars) = make_vars(horizon, time, mass);\n            },\n            make_not_null(&box));\n      };\n\n  const ylm::Strahlkorper<Frame::Inertial> low_l_horizon{low_l_max, radius,\n                                                         initial_center};\n  const ylm::Strahlkorper<Frame::Inertial> high_l_horizon{high_l_max, radius,\n                                                          initial_center};\n\n  initialize_component(horizon_a_component{}, low_l_max);\n  initialize_component(horizon_b_component{}, low_l_max);\n  initialize_component(horizon_c_component{}, high_l_max);\n  initialize_component(horizon_d_component{}, low_l_max);\n\n  ActionTesting::emplace_nodegroup_component<obs_writer>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_writer>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const FastFlow::Status status = FastFlow::Status::AbsTol;\n  auto& cache = ActionTesting::cache<obs_writer>(runner, 0_st);\n\n  const auto& horizon_a_box =\n      ActionTesting::get_databox<horizon_a_component>(runner, 0);\n  const auto& horizon_b_box =\n      ActionTesting::get_databox<horizon_b_component>(runner, 0);\n  const auto& horizon_c_box =\n      ActionTesting::get_databox<horizon_c_component>(runner, 0);\n  const auto& horizon_d_box =\n      ActionTesting::get_databox<horizon_d_component>(runner, 0);\n\n  // Call callbacks\n  horizon_a_callback::apply(horizon_a_box, cache, status);\n  horizon_b_callback::apply(horizon_b_box, cache, status);\n  horizon_c_callback::apply(horizon_c_box, cache, status);\n  horizon_d_callback::apply(horizon_d_box, cache, status);\n\n  // One for each ObserveTimeSeries and two for the ObserveFields\n  REQUIRE(ActionTesting::number_of_queued_threaded_actions<obs_writer>(runner,\n                                                                       0) == 5);\n\n  for (size_t i = 0; i < 5; i++) {\n    ActionTesting::invoke_queued_threaded_action<obs_writer>(\n        make_not_null(&runner), 0);\n  }\n\n  const std::vector<double> expected_integral_ab{4.0 * M_PI};\n  const std::vector<double> expected_integral_c{2.0 / square(radius)};\n  const std::vector<std::string> expected_legend_ab{\"Time\", \"Area\"};\n  const std::vector<std::string> expected_legend_c{\"Time\", \"MaxRicciScalar\"};\n\n  // Check that the H5 file was written correctly.\n  const auto file = h5::H5File<h5::AccessType::ReadOnly>(h5_file_name);\n  auto check_file_contents =\n      [&file](const std::vector<double>& expected_integral,\n              const std::vector<std::string>& expected_legend,\n              const std::string& group_name) {\n        CAPTURE(group_name);\n        CAPTURE(expected_legend);\n        file.close_current_object();\n        const auto& dat_file = file.get<h5::Dat>(group_name);\n        const Matrix written_data = dat_file.get_data();\n        const auto& written_legend = dat_file.get_legend();\n        CHECK(written_legend == expected_legend);\n        CHECK(2.0 == written_data(0, 0));\n        // The interpolation is not perfect because we use too few grid points.\n        const Approx custom_approx = Approx::custom().epsilon(1.e-4).scale(1.0);\n        for (size_t i = 0; i < expected_integral.size(); ++i) {\n          CAPTURE(i);\n          CHECK(expected_integral[i] == custom_approx(written_data(0, i + 1)));\n        }\n      };\n  check_file_contents(expected_integral_ab, expected_legend_ab, \"/HorizonA\");\n  check_file_contents(expected_integral_ab, expected_legend_ab, \"/HorizonB\");\n  check_file_contents(expected_integral_c, expected_legend_c, \"/HorizonC\");\n\n  // Check that the Ylm data were written correctly\n  // As this data depends only on the known target (a KerrHorizon) it\n  // uses no interpolated data\n  check_ylm_data(h5_file_name, low_l_horizon);\n\n  remove_file_if_exists(h5_file_name);\n\n  // Check that the Surfaces file contains the correct surface data\n  check_surface_volume_data(surfaces_file_name, low_l_horizon, time, mass);\n\n  remove_file_if_exists(surfaces_file_name);\n\n  // Verify adaptive resolution writes all requested coefficients\n  const std::string adaptive_horizon_reduction_file_prefix =\n      \"AdaptiveHorizonReduction\";\n  const std::string adaptive_horizon_surface_file_prefix =\n      \"AdaptiveHorizonSurfaceData\";\n  const std::string adaptive_horizon_reduction_file_name =\n      adaptive_horizon_reduction_file_prefix + \".h5\";\n  const std::string adaptive_horizon_surface_file_name =\n      adaptive_horizon_surface_file_prefix + \".h5\";\n  remove_file_if_exists(adaptive_horizon_reduction_file_name);\n  remove_file_if_exists(adaptive_horizon_surface_file_name);\n\n  constexpr size_t max_resolution_and_output_l = 6;\n  constexpr size_t low_l = 4;\n  constexpr size_t high_l = max_resolution_and_output_l;\n  const double base_radius = 1.7;\n  const std::array<double, 3> adaptive_center{{0.05, -0.01, 0.08}};\n\n  MAKE_GENERATOR(adaptive_generator);\n  const std::uniform_real_distribution<> radius_distribution{0.9 * base_radius,\n                                                             1.1 * base_radius};\n  const ylm::Spherepack spherepack_high(high_l, high_l);\n  const auto radius_high = make_with_random_values<DataVector>(\n      make_not_null(&adaptive_generator), radius_distribution,\n      DataVector{spherepack_high.physical_size(), 0.0});\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper_high{\n      high_l, high_l, radius_high, adaptive_center};\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper_low{low_l, low_l,\n                                                            strahlkorper_high};\n\n  struct AdaptiveHorizonMetavariables {\n    using observed_reduction_data_tags = tmpl::list<>;\n    using const_global_cache_tags = tmpl::list<ah::Tags::LMax>;\n    using component_list =\n        tmpl::list<MockObserverWriter<AdaptiveHorizonMetavariables>>;\n  };\n\n  using ObsWriter = MockObserverWriter<AdaptiveHorizonMetavariables>;\n  tuples::TaggedTuple<observers::Tags::ReductionFileName,\n                      observers::Tags::SurfaceFileName, ah::Tags::LMax>\n      adaptive_opts{adaptive_horizon_reduction_file_prefix,\n                    adaptive_horizon_surface_file_prefix,\n                    max_resolution_and_output_l};\n  ActionTesting::MockRuntimeSystem<AdaptiveHorizonMetavariables>\n      adaptive_runner{std::move(adaptive_opts)};\n\n  ActionTesting::set_phase(make_not_null(&adaptive_runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_nodegroup_component<ObsWriter>(&adaptive_runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<ObsWriter>(make_not_null(&adaptive_runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&adaptive_runner),\n                           Parallel::Phase::Testing);\n  auto& adaptive_cache = ActionTesting::cache<ObsWriter>(adaptive_runner, 0_st);\n\n  using HorizonMetavars = MockMetavariables::HorizonD;\n  using Callback =\n      ah::callbacks::ObserveFieldsOnHorizon<tmpl::list<>, HorizonMetavars>;\n\n  const auto make_box =\n      [](const ylm::Strahlkorper<Frame::Inertial>& strahlkorper,\n         const LinkedMessageId<double>& time_id) {\n        const auto coords = ylm::cartesian_coords(strahlkorper);\n        return db::create<tmpl::list<\n            ah::Tags::CurrentTime, ylm::Tags::Strahlkorper<Frame::Inertial>,\n            ylm::Tags::CartesianCoords<Frame::Inertial>>>(\n            std::optional<LinkedMessageId<double>>{time_id}, strahlkorper,\n            coords);\n      };\n\n  const LinkedMessageId<double> time1{1.5, std::nullopt};\n  const LinkedMessageId<double> time2{2.5, std::optional{time1.id}};\n\n  auto low_box = make_box(strahlkorper_low, time1);\n  auto high_box = make_box(strahlkorper_high, time2);\n\n  Callback::apply(low_box, adaptive_cache, FastFlow::Status::AbsTol);\n  Callback::apply(high_box, adaptive_cache, FastFlow::Status::AbsTol);\n\n  while (ActionTesting::number_of_queued_threaded_actions<ObsWriter>(\n             adaptive_runner, 0) > 0) {\n    ActionTesting::invoke_queued_threaded_action<ObsWriter>(\n        make_not_null(&adaptive_runner), 0);\n  }\n\n  const auto adaptive_horizon_reduction_file =\n      h5::H5File<h5::AccessType::ReadOnly>(\n          adaptive_horizon_reduction_file_name);\n  const std::string surface_name = pretty_type::name<HorizonMetavars>();\n  adaptive_horizon_reduction_file.close_current_object();\n  const auto& ylm_dat = adaptive_horizon_reduction_file.get<h5::Dat>(\n      std::string{\"/\"} + surface_name + \"_Ylm\");\n  const Matrix ylm_data = ylm_dat.get_data();\n  const auto& legend = ylm_dat.get_legend();\n  const size_t expected_columns = 5 + square(max_resolution_and_output_l + 1);\n\n  CHECK(ylm_data.rows() == 2);\n  CHECK(ylm_data.columns() == expected_columns);\n  CHECK(legend.size() == expected_columns);\n\n  const auto check_row =\n      [](const Matrix& data, const size_t row,\n         const LinkedMessageId<double>& time_id,\n         const ylm::Strahlkorper<Frame::Inertial>& strahlkorper,\n         const size_t expected_l_max) {\n        CHECK(data(row, 0) == time_id.id);\n        const auto& expansion_center = strahlkorper.expansion_center();\n        CHECK(data(row, 1) == expansion_center[0]);\n        CHECK(data(row, 2) == expansion_center[1]);\n        CHECK(data(row, 3) == expansion_center[2]);\n        CHECK(data(row, 4) == expected_l_max);\n        size_t column = 5;\n        for (size_t l = 0; l <= max_resolution_and_output_l; ++l) {\n          for (int m = -static_cast<int>(l); m <= static_cast<int>(l); ++m) {\n            double expected_value = 0.0;\n            if (l <= strahlkorper.l_max()) {\n              ylm::SpherepackIterator iterator{strahlkorper.l_max(),\n                                               strahlkorper.m_max()};\n              iterator.set(l, m);\n              expected_value = strahlkorper.coefficients()[iterator()];\n            }\n            CHECK(data(row, column) == expected_value);\n            ++column;\n          }\n        }\n      };\n\n  check_row(ylm_data, 0, time1, strahlkorper_low, low_l);\n  check_row(ylm_data, 1, time2, strahlkorper_high, high_l);\n\n  remove_file_if_exists(adaptive_horizon_reduction_file_name);\n  remove_file_if_exists(adaptive_horizon_surface_file_name);\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.ApparentHorizonFinder.ObserveFieldsAndTimeSeriesOnHorizon\",\n    \"[ApparentHorizonFinder][Unit]\") {\n  run_test();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Callbacks/Test_SendDependencyToObserverWriter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"IO/Observer/VolumeActions.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/SendDependencyToObserverWriter.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <bool WriteVolData>\nstruct HorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using temporal_id_tag = ::Tags::TimeAndPrevious<0>;\n  using frame = Frame::Inertial;\n\n  using horizon_find_callbacks = tmpl::list<>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::Observation;\n\n  static std::string name() { return \"TestingHorizonMetavars\"; }\n};\n\ntemplate <bool WriteVolData>\nstruct MockContributeDependency {\n  template <typename ParallelComponent, typename DbTagsList,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<DbTagsList>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const gsl::not_null<Parallel::NodeLock*> /*node_lock*/,\n                    const double time, const std::string& dependency,\n                    std::string volume_subfile_name,\n                    const bool write_volume_data) {\n    CHECK(time == 2.0);\n    CHECK(volume_subfile_name == \"FakeDependency\");\n    CHECK(dependency == \"TestingHorizonMetavars\");\n    CHECK(write_volume_data == WriteVolData);\n  }\n};\n\ntemplate <typename Metavariables, bool WriteVolData>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using const_global_cache_tags = tmpl::list<observers::Tags::ReductionFileName,\n                                             observers::Tags::SurfaceFileName>;\n  using simple_tags =\n      typename observers::Actions::InitializeWriter<Metavariables>::simple_tags;\n  using compute_tags = typename observers::Actions::InitializeWriter<\n      Metavariables>::compute_tags;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<observers::Actions::InitializeWriter<Metavariables>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n\n  using replace_these_threaded_actions =\n      tmpl::list<observers::ThreadedActions::ContributeDependency>;\n  using with_these_threaded_actions =\n      tmpl::list<MockContributeDependency<WriteVolData>>;\n};\n\ntemplate <bool WriteVolData>\nstruct Metavariables {\n  using observed_reduction_data_tags = tmpl::list<>;\n  using component_list =\n      tmpl::list<MockObserverWriter<Metavariables, WriteVolData>>;\n};\n\ntemplate <bool WriteVolData>\nvoid run_test() {\n  CAPTURE(WriteVolData);\n\n  using metavars = Metavariables<WriteVolData>;\n  using obs_writer = MockObserverWriter<metavars, WriteVolData>;\n  ActionTesting::MockRuntimeSystem<metavars> runner{{}};\n  ActionTesting::emplace_nodegroup_component<obs_writer>(\n      make_not_null(&runner));\n\n  auto& cache = ActionTesting::cache<obs_writer>(runner, 0_st);\n\n  const LinkedMessageId<double> time{2.0, {1.0}};\n\n  auto box =\n      db::create<db::AddSimpleTags<ah::Tags::CurrentTime, ah::Tags::Dependency,\n                                   ah::Tags::Verbosity>>(\n          std::optional{time},\n          WriteVolData ? std::optional{\"FakeDependency\"s} : std::nullopt,\n          ::Verbosity::Silent);\n\n  static_assert(\n      tt::assert_conforms_to_v<ah::callbacks::SendDependencyToObserverWriter<\n                                   HorizonMetavars<WriteVolData>, WriteVolData>,\n                               ah::protocols::Callback>);\n\n  ah::callbacks::SendDependencyToObserverWriter<\n      HorizonMetavars<WriteVolData>,\n      WriteVolData>::apply(box, cache, FastFlow::Status::TruncationTol);\n\n  REQUIRE(ActionTesting::number_of_queued_threaded_actions<obs_writer>(\n              runner, 0_st) == (WriteVolData ? 1 : 0));\n\n  if constexpr (WriteVolData) {\n    ActionTesting::invoke_queued_threaded_action<obs_writer>(\n        make_not_null(&runner), 0_st);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.SendDependencyToObserverWriter\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  run_test<true>();\n  run_test<false>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Criteria/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ApparentHorizonFinderCriteria\")\n\nset(LIBRARY_SOURCES\n  Test_Criterion.cpp\n  Test_IncreaseResolution.cpp\n  Test_Residual.cpp\n  Test_Shape.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ApparentHorizonFinder\n  ApparentHorizonFinderCriteria\n  Options\n  SphericalHarmonics\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Test_Criterion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct FactorOne : db::SimpleTag {\n  using type = double;\n};\n\nstruct FactorTwo : db::SimpleTag {\n  using type = double;\n};\n\nstruct Product : db::SimpleTag {\n  using type = double;\n};\n\nstruct ProductCompute : db::ComputeTag, Product {\n  using base = Product;\n  using return_type = double;\n  using argument_tags = tmpl::list<FactorOne, FactorTwo>;\n\n  static void function(const gsl::not_null<double*> result,\n                       const double field_one, const double field_two) {\n    *result = field_one * field_two;\n  }\n};\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\nclass CriterionOne : public ah::Criterion {\n public:\n  struct TargetValue {\n    using type = double;\n    static constexpr Options::String help = {\"The target value.\"};\n  };\n  using options = tmpl::list<TargetValue>;\n\n  static constexpr Options::String help = {\n      \"Increase surface resolution if the factor one is above a target value; \"\n      \"otherwise decreases the resolution\"};\n\n  CriterionOne() = default;\n  explicit CriterionOne(const double target_value)\n      : target_value_(target_value) {}\n  explicit CriterionOne(CkMigrateMessage* /*msg*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(CriterionOne);  // NOLINT\n\n  std::string observation_name() override { return \"CriterionOne\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using argument_tags = tmpl::list<FactorOne>;\n\n  template <typename Metavariables, typename Fr>\n  size_t operator()(const double field_one,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ylm::Strahlkorper<Fr>& strahlkorper,\n                    const FastFlow::IterInfo& /*info*/) const {\n    return field_one > target_value_ ? strahlkorper.l_max() + 1\n                                     : strahlkorper.l_max() - 1;\n  }\n\n  void pup(PUP::er& p) override {\n    Criterion::pup(p);\n    p | target_value_;\n  }\n\n  bool is_equal(const Criterion& other) const override {\n    const auto* other_criterion = dynamic_cast<const CriterionOne*>(&other);\n    if (other_criterion == nullptr) {\n      return false;\n    }\n    return target_value_ == other_criterion->target_value_;\n  }\n\n private:\n  double target_value_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nPUP::able::PUP_ID CriterionOne::my_PUP_ID = 0;  // NOLINT\n\nclass CriterionTwo : public ah::Criterion {\n public:\n  struct TargetValue {\n    using type = double;\n    static constexpr Options::String help = {\"The target value.\"};\n  };\n  using options = tmpl::list<TargetValue>;\n\n  static constexpr Options::String help = {\n      \"Increase surface resolution if the product of the two factors times the \"\n      \"(simulated) residual of a surface find is above \"\n      \"a target value; decrease resolution otherwise.\"};\n\n  CriterionTwo() = default;\n  explicit CriterionTwo(const double target_value)\n      : target_value_(target_value) {}\n  explicit CriterionTwo(CkMigrateMessage* /*msg*/) {}\n  using PUP::able::register_constructor;        // NOLINT\n  WRAPPED_PUPable_decl_template(CriterionTwo);  // NOLINT\n\n  std::string observation_name() override { return \"CriterionTwo\"; }\n\n  using compute_tags_for_observation_box = tmpl::list<ProductCompute>;\n  using argument_tags = tmpl::list<Product>;\n\n  template <typename Metavariables, typename Fr>\n  size_t operator()(const double product,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ylm::Strahlkorper<Fr>& strahlkorper,\n                    const FastFlow::IterInfo& info) const {\n    return product * info.max_residual > target_value_\n               ? strahlkorper.l_max() + 1\n               : strahlkorper.l_max() - 1;\n  }\n\n  void pup(PUP::er& p) override {\n    Criterion::pup(p);\n    p | target_value_;\n  }\n\n  bool is_equal(const Criterion& other) const override {\n    const auto* other_criterion = dynamic_cast<const CriterionTwo*>(&other);\n    if (other_criterion == nullptr) {\n      return false;\n    }\n    return target_value_ == other_criterion->target_value_;\n  }\n\n private:\n  double target_value_{std::numeric_limits<double>::signaling_NaN()};\n};\n\nPUP::able::PUP_ID CriterionTwo::my_PUP_ID = 0;  // NOLINT\n#pragma GCC diagnostic pop\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<ah::Criterion, tmpl::list<CriterionOne, CriterionTwo>>>;\n  };\n};\n\ntemplate <typename Fr>\nvoid test_criterion(const ah::Criterion& criterion, const double field_one,\n                    const double field_two, const size_t expected_l_max,\n                    const ylm::Strahlkorper<Fr>& strahlkorper,\n                    const FastFlow::IterInfo& info) {\n  Parallel::GlobalCache<Metavariables> empty_cache{};\n  using simple_tags = tmpl::list<FactorOne, FactorTwo>;\n  auto databox = db::create<simple_tags>(field_one, field_two);\n  using compute_tags = tmpl::list<ProductCompute>;\n  const auto box = make_observation_box<compute_tags>(make_not_null(&databox));\n\n  auto new_l_max = criterion.evaluate(box, empty_cache, strahlkorper, info);\n  CHECK(new_l_max == expected_l_max);\n}\n\nvoid test() {\n  register_factory_classes_with_charm<Metavariables>();\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper{\n      11, 4.5, {{0.1, 0.2, 0.3}}};\n  FastFlow::IterInfo info{};\n  info.max_residual = 1.e-6;\n\n  const CriterionOne criterion_one{2.3};\n  test_criterion(criterion_one, 3.4, 0.0, strahlkorper.l_max() + 1,\n                 strahlkorper, info);\n  test_criterion(serialize_and_deserialize(criterion_one), 3.4, 0.0,\n                 strahlkorper.l_max() + 1, strahlkorper, info);\n  const auto criterion_one_option =\n      TestHelpers::test_creation<std::unique_ptr<ah::Criterion>, Metavariables>(\n          \"CriterionOne:\\n\"\n          \"  TargetValue: 2.3\\n\");\n  test_criterion(*criterion_one_option, 1.8, 0.0, strahlkorper.l_max() - 1,\n                 strahlkorper, info);\n  test_criterion(*serialize_and_deserialize(criterion_one_option), 2.2, 0.0,\n                 strahlkorper.l_max() - 1, strahlkorper, info);\n\n  const CriterionTwo criterion_two{4.0e-6};\n  test_criterion(criterion_two, 2.0, 1.5, strahlkorper.l_max() - 1,\n                 strahlkorper, info);\n  test_criterion(serialize_and_deserialize(criterion_two), 2.0, 1.5,\n                 strahlkorper.l_max() - 1, strahlkorper, info);\n  const auto criterion_two_option =\n      TestHelpers::test_creation<std::unique_ptr<ah::Criterion>, Metavariables>(\n          \"CriterionTwo:\\n\"\n          \"  TargetValue: 4.0e-6\\n\");\n  test_criterion(*criterion_two_option, 2.0, 1.5, strahlkorper.l_max() - 1,\n                 strahlkorper, info);\n  test_criterion(*serialize_and_deserialize(criterion_two_option), 2.0, 1.5,\n                 strahlkorper.l_max() - 1, strahlkorper, info);\n\n  const CriterionOne criterion_one_same{2.3};\n  CHECK(criterion_one.is_equal(criterion_one_same));\n  CHECK(not(criterion_one.is_equal(criterion_two)));\n  const auto criterion_one_serialized =\n      serialize_and_deserialize(criterion_one);\n  CHECK(criterion_one.is_equal(criterion_one_serialized));\n\n  const std::unique_ptr<ah::Criterion> criterion_four =\n      std::make_unique<CriterionOne>(1.2e-3);\n  const std::unique_ptr<CriterionOne> criterion_five =\n      std::make_unique<CriterionOne>(1.2e-3);\n  const std::unique_ptr<CriterionTwo> criterion_six =\n      std::make_unique<CriterionTwo>(1.2e-3);\n  CHECK(criterion_four->is_equal(*criterion_five));\n  CHECK(not(criterion_four->is_equal(*criterion_six)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Criteria.Criterion\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Test_IncreaseResolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/IncreaseResolution.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Residual.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Criteria {\nnamespace {\ntemplate <typename Frame>\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<ah::Criterion, tmpl::list<IncreaseResolution>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Criteria.IncreaseResolution\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  TestHelpers::db::test_simple_tag<ah::Criteria::Tags::Criteria>(\"Criteria\");\n  const auto criterion =\n      TestHelpers::test_factory_creation<ah::Criterion, IncreaseResolution>(\n          \"IncreaseResolution\");\n  Parallel::GlobalCache<Metavariables<Frame::Inertial>> empty_cache{};\n  auto databox = db::create<tmpl::list<>>();\n  const ObservationBox<tmpl::list<>, db::DataBox<tmpl::list<>>> box{\n      make_not_null(&databox)};\n\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper{\n      11, 4.5, {{0.1, 0.2, 0.3}}};\n  const FastFlow::IterInfo iter_info{};\n\n  const size_t new_l_max =\n      criterion->evaluate(box, empty_cache, strahlkorper, iter_info);\n  CHECK(new_l_max == strahlkorper.l_max() + 2);\n\n  // Test equality and inequality operators\n  const IncreaseResolution criterion_one{};\n  const IncreaseResolution criterion_two{};\n  CHECK(criterion_one.is_equal(criterion_two));\n  const auto criterion_one_serialized =\n      serialize_and_deserialize(criterion_one);\n  CHECK(criterion_one.is_equal(criterion_one_serialized));\n\n  const std::unique_ptr<ah::Criterion> criterion_four =\n      std::make_unique<ah::Criteria::IncreaseResolution>();\n  const std::unique_ptr<ah::Criterion> criterion_five =\n      std::make_unique<ah::Criteria::IncreaseResolution>();\n  const std::unique_ptr<ah::Criterion> criterion_six =\n      std::make_unique<ah::Criteria::Residual>(1.0e-6, 1.0e-4, 4);\n  CHECK(criterion_four->is_equal(*criterion_five));\n  CHECK(not(criterion_four->is_equal(*criterion_six)));\n}\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Test_Residual.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/IncreaseResolution.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Residual.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Criteria {\nnamespace {\ntemplate <typename Frame>\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<ah::Tags::LMax>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<ah::Criterion, tmpl::list<Residual>>>;\n  };\n};\n\nFastFlow::IterInfo make_iter_info(double residual_ylm) {\n  FastFlow::IterInfo info{};\n  info.residual_ylm = residual_ylm;\n  return info;\n}\n\ntemplate <typename Fr>\nylm::Strahlkorper<Fr> make_strahlkorper(size_t l_max) {\n  return ylm::Strahlkorper<Fr>(l_max, 4.5, {{0.4, 0.5, 0.6}});\n}\n\ntemplate <typename Fr>\nvoid test_criterion_evaluation(\n    const std::unique_ptr<ah::Criterion>& criterion,\n    const ObservationBox<tmpl::list<>, db::DataBox<tmpl::list<>>>& box,\n    Parallel::GlobalCache<Metavariables<Frame::Inertial>>& cache, size_t l_max,\n    double residual_ylm, size_t expected_new_l_max) {\n  const ylm::Strahlkorper<Fr> strahlkorper = make_strahlkorper<Fr>(l_max);\n  const FastFlow::IterInfo iter_info = make_iter_info(residual_ylm);\n  const size_t new_l_max =\n      criterion->evaluate(box, cache, strahlkorper, iter_info);\n  CHECK(new_l_max == expected_new_l_max);\n}\n\nvoid test_residual() {\n  TestHelpers::db::test_simple_tag<ah::Criteria::Tags::Criteria>(\"Criteria\");\n  const auto criterion{\n      TestHelpers::test_factory_creation<ah::Criterion, Residual>(\n          \"Residual:\\n\"\n          \"  MinResidual: 1.0e-6\\n\"\n          \"  MaxResidual: 1.0e-4\\n\"\n          \"  MinResolutionL: 4\")};\n  // Test observation_name\n  CHECK(criterion->observation_name() == \"Residual\");\n\n  auto databox = db::create<tmpl::list<>>();\n  const ObservationBox<tmpl::list<>, db::DataBox<tmpl::list<>>> box{\n      make_not_null(&databox)};\n\n  constexpr double min_residual{1.0e-6};\n  constexpr double max_residual{1.0e-4};\n  constexpr size_t min_resolution_l{4};\n  constexpr size_t max_resolution_l{12};\n  constexpr size_t mid_l{8};\n  constexpr size_t min_l_allowed_by_strahlkorper{2};\n  constexpr double eps{std::numeric_limits<double>::epsilon()};\n\n  Parallel::GlobalCache<Metavariables<Frame::Inertial>> cache{\n      tuples::TaggedTuple<ah::Tags::LMax>{max_resolution_l}};\n\n  // Normal case - decrease resolution when residual is low\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             0.5 * min_residual, mid_l - 1);\n\n  // Normal case - increase resolution when residual is high\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             2.0 * max_residual, mid_l + 1);\n\n  // Normal case - keep resolution when residual is in range\n  const double in_range_residual{(0.3 * min_residual) + (0.7 * max_residual)};\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             in_range_residual, mid_l);\n\n  // Edge case - at minimum resolution, low residual\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, min_resolution_l, 0.5 * min_residual,\n      min_resolution_l);\n\n  // Edge case - at maximum resolution, high residual\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, max_resolution_l, 2.0 * max_residual,\n      max_resolution_l);\n\n  // Edge case - at minimum resolution, high residual\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, min_resolution_l, 2.0 * max_residual,\n      min_resolution_l + 1);\n\n  // Edge case - at maximum resolution, low residual\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, max_resolution_l, 0.5 * min_residual,\n      max_resolution_l - 1);\n\n  // Edge case - residual exactly at min_residual\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             min_residual, mid_l);\n\n  // Edge case - residual exactly at max_residual\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             max_residual, mid_l);\n\n  // Edge case - residual just below min_residual\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             min_residual - eps, mid_l - 1);\n\n  // Edge case - residual just above min_residual\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             min_residual + eps, mid_l);\n\n  // Edge case - residual just below max_residual\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             max_residual - eps, mid_l);\n\n  // Edge case - residual just above max_residual\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             max_residual + eps, mid_l + 1);\n\n  // Edge case - l_max = 2 (minimum allowed by Strahlkorper)\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, min_l_allowed_by_strahlkorper, 0.5 * min_residual,\n      min_l_allowed_by_strahlkorper);\n\n  // Edge case - l_max = 2, high residual\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, min_l_allowed_by_strahlkorper, 2.0 * max_residual,\n      min_l_allowed_by_strahlkorper + 1);\n\n  // Edge case - min_resolution_l == max_resolution_l\n  const std::unique_ptr<ah::Criterion> fixed_criterion{\n      std::make_unique<Residual>(min_residual, max_residual, mid_l)};\n  Parallel::GlobalCache<Metavariables<Frame::Inertial>> mid_cache{\n      tuples::TaggedTuple<ah::Tags::LMax>{mid_l}};\n  test_criterion_evaluation<Frame::Inertial>(fixed_criterion, box, mid_cache,\n                                             mid_l, 0.5 * min_residual, mid_l);\n\n  // Edge case - min_resolution_l == max_resolution_l but\n  // different from strahlkorper\n#ifdef SPECTRE_DEBUG\n  const ylm::Strahlkorper<Frame::Inertial> debug_strahlkorper{\n      make_strahlkorper<Frame::Inertial>(mid_l - 2)};\n  const FastFlow::IterInfo debug_iter_info{make_iter_info(0.5 * min_residual)};\n  CHECK_THROWS_WITH(\n      fixed_criterion->evaluate(box, mid_cache, debug_strahlkorper,\n                                debug_iter_info),\n      Catch::Matchers::ContainsSubstring(\"If MinResolutionL == LMax\"));\n#endif\n\n  // Very large l_max\n  const size_t very_large_l{100};\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache,\n                                             very_large_l, 0.5 * min_residual,\n                                             very_large_l - 1);\n\n  // Very small residual\n  const double very_small_residual{1.0e-12};\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             very_small_residual, mid_l - 1);\n\n  // Very large residual\n  const double very_large_residual{1.0};\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             very_large_residual, mid_l + 1);\n\n  // Test with different frame\n  test_criterion_evaluation<Frame::Grid>(criterion, box, cache, mid_l,\n                                         0.5 * min_residual, mid_l - 1);\n\n  // Test serialization\n  {\n    const Residual original{min_residual, max_residual, min_resolution_l};\n    const auto serialized{serialize_and_deserialize(original)};\n\n    // Test that the serialized object produces the same results\n    const ylm::Strahlkorper<Frame::Inertial> strahlkorper{\n        make_strahlkorper<Frame::Inertial>(mid_l)};\n    const FastFlow::IterInfo iter_info{make_iter_info(in_range_residual)};\n\n    const size_t recommended_l_original{\n        original.operator()(cache, strahlkorper, iter_info)};\n    const size_t recommended_l_serialized{\n        serialized.operator()(cache, strahlkorper, iter_info)};\n    CHECK(recommended_l_original == recommended_l_serialized);\n  }\n\n  const Residual criterion_one{1.0e-6, 1.0e-4, 4};\n  const Residual criterion_two{1.0e-6, 1.0e-4, 6};\n  const Residual criterion_one_same{1.0e-6, 1.0e-4, 4};\n  CHECK(criterion_one.is_equal(criterion_one_same));\n  CHECK(not(criterion_one.is_equal(criterion_two)));\n  const auto criterion_one_serialized =\n      serialize_and_deserialize(criterion_one);\n  CHECK(criterion_one.is_equal(criterion_one_serialized));\n\n  const std::unique_ptr<ah::Criterion> criterion_four =\n      std::make_unique<ah::Criteria::Residual>(1.0e-6, 1.0e-4, 4);\n  const std::unique_ptr<ah::Criterion> criterion_five =\n      std::make_unique<ah::Criteria::Residual>(1.0e-6, 1.0e-4, 4);\n  const std::unique_ptr<ah::Criterion> criterion_six =\n      std::make_unique<ah::Criteria::IncreaseResolution>();\n  CHECK(criterion_four->is_equal(*criterion_five));\n  CHECK(not(criterion_four->is_equal(*criterion_six)));\n}\n\nvoid test_residual_constructor_validation() {\n  // Define test constants\n  constexpr double min_residual = 1.0e-6;\n  constexpr double max_residual = 1.0e-4;\n  constexpr double zero = 0.0;\n  constexpr size_t min_resolution_l = 4;\n  constexpr size_t min_allowed_resolution = 2;\n\n  // Test valid construction\n  CHECK_NOTHROW((Residual{min_residual, max_residual, min_resolution_l}));\n\n  // Test invalid construction - min_residual >= max_residual\n  CHECK_THROWS_WITH((Residual{max_residual, min_residual, min_resolution_l}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"MinResidual must be less than MaxResidual\"));\n\n  // Test invalid construction - min_residual == max_residual\n  CHECK_THROWS_WITH((Residual{min_residual, min_residual, min_resolution_l}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"MinResidual must be less than MaxResidual\"));\n\n  // Test edge case - zero max_residual (should fail)\n  CHECK_THROWS_WITH((Residual{min_residual, zero, min_resolution_l}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"MinResidual must be less than MaxResidual\"));\n\n  // Test edge case - min_resolution_l below minimum allowed value (2)\n  CHECK_THROWS_WITH(\n      (Residual{min_residual, max_residual, min_allowed_resolution - 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"MinResolutionL must not be less than 2\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Criteria.Residual\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_residual();\n  test_residual_constructor_validation();\n}\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Criteria/Test_Shape.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Shape.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Tags/Criteria.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah::Criteria {\nnamespace {\ntemplate <typename Frame>\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<ah::Tags::LMax>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<ah::Criterion, tmpl::list<Shape>>>;\n  };\n};\n\ntemplate <typename Fr>\nylm::Strahlkorper<Fr> make_strahlkorper(size_t l_max,\n                                        const std::array<double, 3>& spin) {\n  const ylm::Strahlkorper<Fr>& sphere =\n      ylm::Strahlkorper<Fr>(l_max, 4.5, {{0.4, 0.5, 0.6}});\n  const std::array<DataVector, 2> theta_phi =\n      sphere.ylm_spherepack().theta_phi_points();\n  const auto radius = gr::Solutions::kerr_horizon_radius(theta_phi, 1.0, spin);\n  return ylm::Strahlkorper<Fr>{l_max, l_max, get(radius), {{0.4, 0.5, 0.6}}};\n}\n\ntemplate <typename Fr>\nvoid test_criterion_evaluation(\n    const std::unique_ptr<ah::Criterion>& criterion,\n    const ObservationBox<tmpl::list<>, db::DataBox<tmpl::list<>>>& box,\n    Parallel::GlobalCache<Metavariables<Frame::Inertial>>& cache, size_t l_max,\n    const std::array<double, 3>& spin, size_t expected_new_l_max) {\n  const ylm::Strahlkorper<Fr> strahlkorper = make_strahlkorper<Fr>(l_max, spin);\n  const size_t new_l_max =\n      criterion->evaluate(box, cache, strahlkorper, FastFlow::IterInfo{});\n  CHECK(new_l_max == expected_new_l_max);\n}\n\nvoid test_shape() {\n  TestHelpers::db::test_simple_tag<ah::Criteria::Tags::Criteria>(\"Criteria\");\n  const auto criterion{TestHelpers::test_factory_creation<ah::Criterion, Shape>(\n      \"Shape:\\n\"\n      \"  MinTruncationError: 1.0e-10\\n\"\n      \"  MaxTruncationError: 1.0e-8\\n\"\n      \"  MaxPileUpModes: 5\\n\"\n      \"  MinResolutionL: 4\")};\n  // Test observation_name\n  CHECK(criterion->observation_name() == \"Shape\");\n\n  auto databox = db::create<tmpl::list<>>();\n  const ObservationBox<tmpl::list<>, db::DataBox<tmpl::list<>>> box{\n      make_not_null(&databox)};\n\n  constexpr double min_truncation_error{1.0e-10};\n  constexpr double max_truncation_error{1.0e-8};\n  constexpr size_t max_pile_up_modes{5};\n  constexpr size_t min_resolution_l{4};\n  constexpr size_t max_resolution_l{12};\n  constexpr size_t mid_l{8};\n  const std::array<double, 3> zero_spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> low_aligned_spin{{0.0, 0.0, 0.1}};\n  const std::array<double, 3> low_spin{{0.1, 0.2, 0.3}};\n  const std::array<double, 3> high_spin{{0.4, 0.5, 0.6}};\n  constexpr size_t min_l_allowed_by_power_monitor{4};\n  constexpr double eps{std::numeric_limits<double>::epsilon()};\n\n  Parallel::GlobalCache<Metavariables<Frame::Inertial>> cache{\n      tuples::TaggedTuple<ah::Tags::LMax>{max_resolution_l}};\n\n  // Normal case - decrease resolution when truncation error is low\n  // Make truncation error low by passing in a spherical Strahlkorper, i.e.\n  // a horizon of a black hole with zero spin\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             zero_spin, mid_l - 1);\n\n  // Normal case - increase resolution when truncation error is high\n  // Make truncation error high (~1e-7) by passing in a spinning Kerr Schild\n  // horizon for the strahlkorper\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             high_spin, mid_l + 1);\n\n  // Normal case - keep resolution the same when truncation error is in range\n  // Do this by using a Strahlkorper of a Kerr Schild black hole spinning\n  // but not quite as fast\n  test_criterion_evaluation<Frame::Inertial>(criterion, box, cache, mid_l,\n                                             low_spin, mid_l);\n\n  // Normal case - keep resolution the same when you would have raised it\n  // except you have too many pile up modes. Here make lots of pile up modes by\n  // using high resolution for spin=0.1 Kerr-Schild black hole, and use\n  // very small truncation error ranges so that the criterion would otherwise\n  // want to increase resolution, if it weren't for the pileup modes\n  const std::unique_ptr<ah::Criterion> criterion_pile_up =\n      std::make_unique<ah::Criteria::Shape>(min_truncation_error * 1.e-10,\n                                            max_truncation_error * 1.e-10, 1,\n                                            min_resolution_l);\n  Parallel::GlobalCache<Metavariables<Frame::Inertial>> high_max_cache{\n      tuples::TaggedTuple<ah::Tags::LMax>{mid_l * 4}};\n  test_criterion_evaluation<Frame::Inertial>(criterion_pile_up, box,\n                                             high_max_cache, mid_l * 4,\n                                             low_aligned_spin, mid_l * 4);\n\n  // Edge case - at minimum resolution, low truncation error\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, min_resolution_l, zero_spin, min_resolution_l);\n\n  // Edge case - at max resolution, high truncation error\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, max_resolution_l, high_spin, max_resolution_l);\n\n  // Edge case - at minimum resolution, high truncation error\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, min_resolution_l, high_spin, min_resolution_l + 1);\n\n  // Edge case - at max resolution, low truncation error\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion, box, cache, max_resolution_l, zero_spin, max_resolution_l - 1);\n\n  // Edge case - truncation error exactly at min_truncation_error\n  const ylm::Strahlkorper<Frame::Inertial>\n      strahlkorper_with_known_truncation_error =\n          make_strahlkorper<Frame::Inertial>(mid_l, high_spin);\n  const DataVector power_monitor_known_truncation_error =\n      ylm::power_monitor(strahlkorper_with_known_truncation_error);\n  const double known_truncation_error =\n      PowerMonitors::relative_truncation_error(\n          power_monitor_known_truncation_error,\n          power_monitor_known_truncation_error.size());\n  const std::unique_ptr<ah::Criterion> criterion_min_truncation_error =\n      std::make_unique<ah::Criteria::Shape>(\n          known_truncation_error, known_truncation_error * 100.0,\n          max_pile_up_modes, min_resolution_l);\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_min_truncation_error, box, cache, mid_l, high_spin, mid_l);\n\n  // Edge case - truncation error just above min_truncation_error\n  const std::unique_ptr<ah::Criterion>\n      criterion_min_truncation_error_just_above =\n          std::make_unique<ah::Criteria::Shape>(\n              known_truncation_error - eps, known_truncation_error * 100.0,\n              max_pile_up_modes, min_resolution_l);\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_min_truncation_error_just_above, box, cache, mid_l, high_spin,\n      mid_l);\n\n  // Edge case - truncation error just below min_truncation_error\n  const std::unique_ptr<ah::Criterion>\n      criterion_min_truncation_error_just_below =\n          std::make_unique<ah::Criteria::Shape>(\n              known_truncation_error + eps, known_truncation_error * 100.0,\n              max_pile_up_modes, min_resolution_l);\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_min_truncation_error_just_below, box, cache, mid_l, high_spin,\n      mid_l - 1);\n\n  // Edge case - truncation error exactly at max_truncation_error\n  const std::unique_ptr<ah::Criterion> criterion_max_truncation_error =\n      std::make_unique<ah::Criteria::Shape>(\n          known_truncation_error / 100.0, known_truncation_error,\n          max_pile_up_modes, min_resolution_l);\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_max_truncation_error, box, cache, mid_l, high_spin, mid_l);\n\n  // Edge case - truncation error just below max_truncation_error\n  const std::unique_ptr<ah::Criterion>\n      criterion_max_truncation_error_just_below =\n          std::make_unique<ah::Criteria::Shape>(\n              known_truncation_error / 100.0, known_truncation_error + eps,\n              max_pile_up_modes, min_resolution_l);\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_max_truncation_error_just_below, box, cache, mid_l, high_spin,\n      mid_l);\n\n  // Edge case - truncation error just above max_truncation_error\n  const std::unique_ptr<ah::Criterion>\n      criterion_max_truncation_error_just_above =\n          std::make_unique<ah::Criteria::Shape>(\n              known_truncation_error / 100.0, known_truncation_error - eps,\n              max_pile_up_modes, min_resolution_l);\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_max_truncation_error_just_above, box, cache, mid_l, high_spin,\n      mid_l + 1);\n\n  // Edge case - l_min = 4 (minimum allowed by power monitor)\n  const std::unique_ptr<ah::Criterion> criterion_l_min_4 =\n      std::make_unique<ah::Criteria::Shape>(\n          min_truncation_error, max_truncation_error, max_pile_up_modes,\n          min_l_allowed_by_power_monitor);\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_l_min_4, box, cache, min_l_allowed_by_power_monitor, zero_spin,\n      min_l_allowed_by_power_monitor);\n\n  // Edge case - l_min = 4 (minimum allowed by power monitor), high residual\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_l_min_4, box, cache, min_l_allowed_by_power_monitor, high_spin,\n      min_l_allowed_by_power_monitor + 1);\n\n  // Edge case - min_resolution_l == max_resolution_l\n  const std::unique_ptr<ah::Criterion>\n      criterion_min_resolution_l_eq_max_resolution_l =\n          std::make_unique<ah::Criteria::Shape>(min_truncation_error,\n                                                max_truncation_error,\n                                                max_pile_up_modes, mid_l);\n  Parallel::GlobalCache<Metavariables<Frame::Inertial>> mid_shape_cache{\n      tuples::TaggedTuple<ah::Tags::LMax>{mid_l}};\n  test_criterion_evaluation<Frame::Inertial>(\n      criterion_min_resolution_l_eq_max_resolution_l, box, mid_shape_cache,\n      mid_l, high_spin, mid_l);\n\n  // Edge case - min_resolution_l == max_resolution_l but\n  // different from strahlkorper\n#ifdef SPECTRE_DEBUG\n  const ylm::Strahlkorper<Frame::Inertial> debug_strahlkorper{\n      make_strahlkorper<Frame::Inertial>(mid_l - 2, zero_spin)};\n  CHECK_THROWS_WITH(\n      criterion_min_resolution_l_eq_max_resolution_l->evaluate(\n          box, mid_shape_cache, debug_strahlkorper, FastFlow::IterInfo{}),\n      Catch::Matchers::ContainsSubstring(\"If MinResolutionL == LMax\"));\n#endif\n\n  // Test equality and serialization\n  const Shape criterion_one{1.0e-6, 1.0e-4, 5, 4};\n  const Shape criterion_two{1.0e-6, 1.0e-4, 5, 4};\n  const Shape criterion_three{1.0e-6, 1.0e-3, 5, 4};\n  CHECK(criterion_one.is_equal(criterion_two));\n  CHECK(not(criterion_one.is_equal(criterion_three)));\n  const auto criterion_one_serialized =\n      serialize_and_deserialize(criterion_one);\n  CHECK(criterion_one.is_equal(criterion_one_serialized));\n}\n\nvoid test_shape_constructor_validation() {\n  // Define test constants\n  constexpr double min_truncation_error = 1.0e-6;\n  constexpr double max_truncation_error = 1.0e-4;\n  const double zero = 0.0;\n  constexpr size_t max_pile_up_modes = 5;\n  constexpr size_t min_resolution_l = 4;\n  constexpr size_t min_allowed_resolution = 4;\n\n  // Test valid construction\n  CHECK_NOTHROW((Shape{min_truncation_error, max_truncation_error,\n                       max_pile_up_modes, min_resolution_l}));\n\n  // Test invalid construction - min_truncation_error >= max_truncation_error\n  CHECK_THROWS_WITH(\n      (Shape{max_truncation_error, min_truncation_error, max_pile_up_modes,\n             min_resolution_l}),\n      Catch::Matchers::ContainsSubstring(\n          \"MinTruncationError must be less than MaxTruncationError\"));\n\n  // Test invalid construction - min_truncation_error == max_truncation_error\n  CHECK_THROWS_WITH(\n      (Shape{min_truncation_error, min_truncation_error, max_pile_up_modes,\n             min_resolution_l}),\n      Catch::Matchers::ContainsSubstring(\n          \"MinTruncationError must be less than MaxTruncationError\"));\n\n  // Test edge case - zero max_truncation_error (should fail)\n  CHECK_THROWS_WITH(\n      (Shape{min_truncation_error, zero, max_pile_up_modes, min_resolution_l}),\n      Catch::Matchers::ContainsSubstring(\n          \"MinTruncationError must be less than MaxTruncationError\"));\n\n  // Test edge case - min_resolution_l below minimum allowed value (4)\n  CHECK_THROWS_WITH(\n      (Shape{min_truncation_error, max_truncation_error, max_pile_up_modes,\n             min_allowed_resolution - 1}),\n      Catch::Matchers::ContainsSubstring(\"MinResolutionL must be at least 4\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Criteria.Shape\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_shape();\n  test_shape_constructor_validation();\n}\n}  // namespace ah::Criteria\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Events/Test_FindApparentHorizon.cpp\n  Events/Test_FindCommonHorizon.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Events/Test_FindApparentHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"ControlSystem/UpdateFunctionOfTime.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/UniformTranslation.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct MockFindApparentHorizon {\n  using frame = ::Frame::Grid;\n\n  struct Results {\n    LinkedMessageId<double> time{};\n    ElementId<3> element_id{};\n    Mesh<3> mesh;\n    Variables<ah::vars_to_interpolate_to_target<3, frame>> vars;\n    std::optional<std::string> dependency;\n  };\n  static Results results;  // NOLINT\n\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTags>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const LinkedMessageId<double>& incoming_time,\n      const ElementId<3>& incoming_element_id, const ::Mesh<3>& incoming_mesh,\n      Variables<ah::vars_to_interpolate_to_target<3, frame>>&&\n          incoming_vars_to_interpolate,\n      const std::optional<std::string>& dependency,\n      const bool /*source_vars_have_already_been_received*/ = false) {\n    results.time = incoming_time;\n    results.element_id = incoming_element_id;\n    results.mesh = incoming_mesh;\n    results.vars = std::move(incoming_vars_to_interpolate);\n    results.dependency = dependency;\n  }\n};\n\nMockFindApparentHorizon::Results MockFindApparentHorizon::results{};  // NOLINT\n\nstruct MockHorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using time_tag = ::Tags::TimeAndPrevious<0>;\n\n  using frame = ::Frame::Grid;\n\n  // Don't need callbacks\n  using horizon_find_callbacks = tmpl::list<>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::ControlSystem;\n\n  static std::string name() { return \"MockHorizonMetavars\"; }\n};\n\ntemplate <typename Metavariables>\nstruct MockComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using component_being_mocked =\n      ah::Component<Metavariables, MockHorizonMetavars>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<3>, ah::Tags::BlocksForHorizonFind>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize,\n                 ah::Tags::PreviousSurface<MockHorizonMetavars>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n\n  using replace_these_simple_actions =\n      tmpl::list<ah::FindApparentHorizon<MockHorizonMetavars>>;\n  using with_these_simple_actions = tmpl::list<MockFindApparentHorizon>;\n};\n\ntemplate <typename Metavariables>\nstruct MockElement {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using initial_databox = db::compute_databox_type<db::AddSimpleTags<>>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n};\n\nstruct MockMetavariables {\n  using component_list = tmpl::list<MockComponent<MockMetavariables>,\n                                    MockElement<MockMetavariables>>;\n\n  using event = ah::Events::FindApparentHorizon<MockHorizonMetavars>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<Event, tmpl::list<event>>>;\n  };\n};\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.FindApparentHorizonEvent\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  (void)MockHorizonMetavars::destination;\n  ::domain::FunctionsOfTime::register_derived_with_charm();\n  ::domain::creators::register_derived_with_charm();\n  ::domain::creators::time_dependence::register_derived_with_charm();\n  using metavars = MockMetavariables;\n  const ElementId<3> element_id(2);\n\n  using component = MockComponent<metavars>;\n  using elem_component = MockElement<metavars>;\n  const double initial_time = 0.0;\n  const std::array<double, 3> translation_velocity{{0.01, 0.02, 0.03}};\n  std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n      time_dependence = std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          initial_time, translation_velocity);\n  const auto domain_creator = domain::creators::Sphere(\n      1.8, 2.2, domain::creators::Sphere::Excision{}, 1_st, 5_st, false,\n      std::nullopt, std::vector<double>{},\n      domain::CoordinateMaps::Distribution::Linear, ShellWedges::All,\n      std::optional{domain::creators::Sphere::TimeDepOptionType{\n          std::move(time_dependence)}});\n  const auto block_names = domain_creator.block_names();\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {domain_creator.create_domain(),\n       std::unordered_map<std::string, std::unordered_set<std::string>>{\n           {\"MockHorizonMetavars\", {block_names.begin(), block_names.end()}}}},\n      {domain_creator.functions_of_time(),\n       ah::Storage::LockedPreviousSurface<Frame::Grid>{}}};\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_array_component<component>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0);\n  ActionTesting::emplace_array_component<elem_component>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0},\n      element_id);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const Mesh<3> mesh(5, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto);\n  const LinkedMessageId<double> observation_time{2.0, {1.0}};\n  auto& cache = ActionTesting::cache<elem_component>(runner, element_id);\n\n  // Fill source vars with an analytic solution so that\n  // ah::vars_to_interpolate_to_target can be computed from\n  // the ah::source_vars. Previously, the source vars were all just set\n  // to a constant value in this test. But in that case, the call to\n  // FindApparentHorizon::apply() doesn't receive well-defined values, now\n  // that it receives vars_to_interpolate_to_target instead of source_vars.\n  Variables<ah::source_vars<3>> vars{};\n  {\n    const double mass = 1.0;\n    const std::array<double, 3> spin{{0.1, 0.2, 0.3}};\n    const gr::Solutions::KerrSchild solution(mass, spin, {0.0, 0.0, 0.0});\n\n    const auto& domain = Parallel::get<domain::Tags::Domain<3>>(cache);\n    const auto& block = domain.blocks()[element_id.block_id()];\n\n    const auto logical_coords = logical_coordinates(mesh);\n    InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n        inv_jacobian_logical_to_inertial{mesh.number_of_grid_points(), 0.0};\n    tnsr::I<DataVector, 3, Frame::Inertial> inertial_coords{};\n    if (block.is_time_dependent()) {\n      const ElementMap<3, Frame::Grid> map_logical_to_grid{\n          element_id, block.moving_mesh_logical_to_grid_map().get_clone()};\n      const auto& functions_of_time = domain_creator.functions_of_time();\n      inertial_coords = block.moving_mesh_grid_to_inertial_map()(\n          map_logical_to_grid(logical_coords), observation_time.id,\n          functions_of_time);\n\n      const auto inv_jacobian_logical_to_grid =\n          map_logical_to_grid.inv_jacobian(logical_coords);\n      const auto inv_jacobian_grid_to_inertial =\n          block.moving_mesh_grid_to_inertial_map().inv_jacobian(\n              map_logical_to_grid(logical_coords), observation_time.id,\n              functions_of_time);\n      inv_jacobian_logical_to_inertial = tenex::evaluate<ti::I, ti::j>(\n          inv_jacobian_logical_to_grid(ti::I, ti::k) *\n          inv_jacobian_grid_to_inertial(ti::K, ti::j));\n    } else {\n      const ElementMap<3, Frame::Inertial> map_logical_to_inertial{\n          element_id, block.stationary_map().get_clone()};\n      inertial_coords = map_logical_to_inertial(logical_coords);\n      inv_jacobian_logical_to_inertial =\n          map_logical_to_inertial.inv_jacobian(logical_coords);\n    }\n\n    const auto solution_vars = solution.variables(\n        inertial_coords, observation_time.id,\n        typename gr::Solutions::KerrSchild::tags<DataVector,\n                                                 Frame::Inertial>{});\n\n    const auto& lapse = get<gr::Tags::Lapse<DataVector>>(solution_vars);\n    const auto& dt_lapse =\n        get<Tags::dt<gr::Tags::Lapse<DataVector>>>(solution_vars);\n    const auto& d_lapse = get<typename gr::Solutions::KerrSchild::DerivLapse<\n        DataVector, Frame::Inertial>>(solution_vars);\n    const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(solution_vars);\n    const auto& dt_shift =\n        get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(solution_vars);\n    const auto& d_shift = get<typename gr::Solutions::KerrSchild::DerivShift<\n        DataVector, Frame::Inertial>>(solution_vars);\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(solution_vars);\n    const auto& dt_spatial_metric =\n        get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(solution_vars);\n    const auto& d_spatial_metric =\n        get<typename gr::Solutions::KerrSchild::DerivSpatialMetric<\n            DataVector, Frame::Inertial>>(solution_vars);\n\n    vars.initialize(get(lapse).size());\n    get<gr::Tags::SpacetimeMetric<DataVector, 3>>(vars) =\n        gr::spacetime_metric(lapse, shift, spatial_metric);\n    get<gh::Tags::Phi<DataVector, 3>>(vars) = gh::phi(\n        lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n    get<gh::Tags::Pi<DataVector, 3>>(vars) =\n        gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n               dt_spatial_metric, get<gh::Tags::Phi<DataVector, 3>>(vars));\n    get<Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>>(vars) =\n        partial_derivative(get<gh::Tags::Phi<DataVector, 3>>(vars), mesh,\n                           inv_jacobian_logical_to_inertial);\n  }\n  std::optional<std::string> dependency{\"FakeDependency\"};\n\n  // Test the event version\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<metavars>,\n      typename MockHorizonMetavars::time_tag, ::Events::Tags::ObserverMesh<3>,\n      domain::Tags::Element<3>, ::Tags::Variables<ah::source_vars<3>>>>(\n      metavars{}, observation_time, mesh, Element<3>{element_id, {}}, vars);\n\n  const metavars::event event{dependency};\n  const metavars::event serialized_event = serialize_and_deserialize(event);\n\n  CHECK(serialized_event.needs_evolved_variables());\n\n  auto obs_box = make_observation_box<\n      typename metavars::event::compute_tags_for_observation_box>(\n      make_not_null(&box));\n  serialized_event.run(make_not_null(&obs_box), cache, element_id,\n                       std::add_pointer_t<elem_component>{}, {});\n\n  const auto check_results = [&]() {\n    // Invoke all actions\n    runner.invoke_queued_simple_action<component>(0);\n\n    // No more queued simple actions.\n    CHECK(runner.is_simple_action_queue_empty<component>(0));\n    CHECK(runner.is_simple_action_queue_empty<elem_component>(element_id));\n\n    const auto& results = MockFindApparentHorizon::results;\n    CHECK(results.time == observation_time);\n    CHECK(results.element_id == element_id);\n    CHECK(results.mesh == mesh);\n\n    Variables<ah::vars_to_interpolate_to_target<3, ::Frame::Grid>> target_vars{\n        vars.number_of_grid_points()};\n    const auto functions_of_time = domain_creator.functions_of_time();\n    ah::compute_vars_to_interpolate_to_target(\n        make_not_null(&target_vars),\n        get<gr::Tags::SpacetimeMetric<DataVector, 3>>(vars),\n        get<gh::Tags::Pi<DataVector, 3>>(vars),\n        get<gh::Tags::Phi<DataVector, 3>>(vars),\n        get<Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(vars),\n        observation_time, domain_creator.create_domain(), mesh, element_id,\n        functions_of_time);\n    CHECK(results.vars == target_vars);\n\n    CHECK(results.dependency == dependency);\n  };\n\n  check_results();\n\n  MockFindApparentHorizon::results = MockFindApparentHorizon::Results{};\n  dependency.reset();\n\n  const auto option_event =\n      TestHelpers::test_creation<typename metavars::event>(\"\");\n  const metavars::event serialized_option_event =\n      serialize_and_deserialize(option_event);\n\n  serialized_option_event.run(make_not_null(&obs_box), cache, element_id,\n                              std::add_pointer_t<elem_component>{}, {});\n\n  check_results();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Events/Test_FindCommonHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <memory>\n#include <numeric>\n#include <optional>\n#include <pup.h>\n#include <string>\n#include <type_traits>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Events/FindCommonHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Mock actions only exist so we don't need all other action down the line. In\n// this test we'll only check that actions are queued since each individual\n// event is tested elsewhere, but we do check the dependencies\nstruct MockContributeVolumeData {\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(\n      db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const observers::ObservationId& /*observation_id*/,\n      const std::string& /*subfile_name*/,\n      const Parallel::ArrayComponentId& /*array_component_id*/,\n      ElementVolumeData&& /*received_volume_data*/,\n      const std::optional<std::vector<char>>& /*serialized_functions_of_time*/,\n      const std::optional<std::string>& dependency = std::nullopt) {\n    CHECK(dependency == std::optional{\"InterpolationTargetA\"});\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockObserver {\n  using component_being_mocked = observers::Observer<Metavariables>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::ContributeVolumeData>;\n  using with_these_simple_actions = tmpl::list<MockContributeVolumeData>;\n};\n\nstruct MockFindApparentHorizon {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(\n      db::DataBox<DbTags>& /*box*/,\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/,\n      const LinkedMessageId<double>& /*incoming_time*/,\n      const ElementId<3>& /*incoming_element_id*/,\n      const ::Mesh<3>& /*incoming_mesh*/,\n      Variables<ah::vars_to_interpolate_to_target<3, ::Frame::Grid>>&&\n      /*incoming_vars_to_interpolate*/,\n      const std::optional<std::string>& /*dependency*/,\n      const bool /*source_vars_have_already_been_received*/ = false) {}\n};\n\nstruct MockHorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using time_tag = ::Tags::TimeAndPrevious<0>;\n\n  using frame = ::Frame::Grid;\n\n  // Don't need callbacks\n  using horizon_find_callbacks = tmpl::list<>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::ControlSystem;\n\n  static std::string name() { return \"MockHorizonMetavars\"; }\n};\n\ntemplate <typename Metavariables>\nstruct MockHorizonComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using component_being_mocked =\n      ah::Component<Metavariables, MockHorizonMetavars>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<3>, ah::Tags::BlocksForHorizonFind>;\n  using mutable_global_cache_tags =\n      tmpl::list<ah::Tags::PreviousSurface<MockHorizonMetavars>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n\n  using replace_these_simple_actions =\n      tmpl::list<ah::FindApparentHorizon<MockHorizonMetavars>>;\n  using with_these_simple_actions = tmpl::list<MockFindApparentHorizon>;\n};\n\ntemplate <typename Metavariables>\nstruct MockElement {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<3>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using initial_databox = db::compute_databox_type<\n      db::AddSimpleTags<::ah::source_vars<Metavariables::volume_dim>>>;\n};\n\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = 3;\n  using component_list = tmpl::list<MockObserver<MockMetavariables>,\n                                    MockHorizonComponent<MockMetavariables>,\n                                    MockElement<MockMetavariables>>;\n};\n\nvoid common_horizon_event() {\n  (void)MockHorizonMetavars::destination;\n  ::domain::creators::register_derived_with_charm();\n  using metavars = MockMetavariables;\n  constexpr size_t Dim = metavars::volume_dim;\n  const ElementId<Dim> element_id(0);\n  const Element<Dim> element{element_id, {}};\n\n  using obs_component = MockObserver<metavars>;\n  using horizon_component = MockHorizonComponent<metavars>;\n  using elem_component = MockElement<metavars>;\n\n  const ::domain::creators::Brick brick{\n      {0.0, 0.0, 0.0}, {1.0, 1.0, 1.0}, {0, 0, 0}, {5, 5, 5}};\n  const auto block_names = brick.block_names();\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {brick.create_domain(),\n       std::unordered_map<std::string, std::unordered_set<std::string>>{\n           {\"MockHorizonMetavars\", {block_names.begin(), block_names.end()}}}},\n      {ah::Storage::LockedPreviousSurface<Frame::Grid>{}}};\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_group_component<obs_component>(make_not_null(&runner));\n  ActionTesting::emplace_array_component<horizon_component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n  ActionTesting::emplace_component<elem_component>(make_not_null(&runner),\n                                                   element_id);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const auto check_results = [&runner,\n                              &element_id](const size_t num_queued_actions) {\n    CHECK(ActionTesting::is_simple_action_queue_empty<elem_component>(\n        runner, element_id));\n    CHECK(ActionTesting::number_of_queued_simple_actions<obs_component>(\n              runner, 0) == num_queued_actions);\n    CHECK(ActionTesting::number_of_queued_simple_actions<horizon_component>(\n              runner, 0) == num_queued_actions);\n  };\n\n  // No events queued yet\n  check_results(0);\n\n  const Mesh<Dim> mesh(5, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto);\n  const double observation_time = 2.0;\n\n  // Formerly, every point for every component of every tensor in vars were set\n  // to a constant value of 1.0. But this leads to a metric that is not\n  // invertible, causing a floating-point exception. The following\n  // guarantees an invertible metric by supplying a flat spacetime instead.\n  // Since pi and phi are zero for Minkowski, it's actually less code to just\n  // manually set spacetime metric here than to get the analytic solution and\n  // call all the functions to assemble spacetime_metric, pi, phi.\n  Variables<ah::source_vars<Dim>> vars(mesh.number_of_grid_points(), 0.0);\n  auto& spacetime_metric =\n      get<gr::Tags::SpacetimeMetric<DataVector, Dim>>(vars);\n  get<0, 0>(spacetime_metric) = DataVector(mesh.number_of_grid_points(), -1.0);\n  for (size_t spatial_index = 0; spatial_index < Dim; ++spatial_index) {\n    spacetime_metric.get(spatial_index + 1, spatial_index + 1) =\n        DataVector(mesh.number_of_grid_points(), 1.0);\n  }\n  auto& cache = ActionTesting::cache<elem_component>(runner, element_id);\n\n  const LinkedMessageId<double> temporal_id{observation_time, std::nullopt};\n  const ::Event::ObservationValue observation_value{\"FindCommonHorizon\",\n                                                    observation_time};\n\n  // Actual coords don't matter\n  const auto logical_coords = logical_coordinates(mesh);\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<metavars>,\n      Parallel::Tags::GlobalCache<metavars>, MockHorizonMetavars::time_tag,\n      Tags::Time, ::Events::Tags::ObserverMesh<Dim>,\n      ::domain::Tags::Coordinates<3, ::Frame::Inertial>,\n      ::Tags::Variables<typename decltype(vars)::tags_list>>>(\n      metavars{}, &cache, temporal_id, observation_time, mesh,\n      tnsr::I<DataVector, 3, ::Frame::Inertial>{\n          std::array{logical_coords[0], logical_coords[1], logical_coords[2]}},\n      vars);\n\n  using FindCommonHorizon = ah::Events::FindCommonHorizon<\n      MockHorizonMetavars,\n      tmpl::push_back<ah::source_vars<Dim>,\n                      ::domain::Tags::Coordinates<3, ::Frame::Inertial>>>;\n\n  const FindCommonHorizon find_common_horizon{\"SubfileName\",\n                                              FloatingPointType::Double,\n                                              {FloatingPointType::Double},\n                                              {\"Pi\"}};\n\n  CHECK(find_common_horizon.needs_evolved_variables());\n  CHECK(find_common_horizon.is_ready(cache, element_id,\n                                     std::add_pointer_t<elem_component>{}));\n\n  // Only compute tags for cache items necessary for observation box since this\n  // test just puts the tags in the regular box\n  auto obs_box = make_observation_box<tmpl::list<\n      Parallel::Tags::FromGlobalCache<::domain::Tags::Domain<Dim>, metavars>>>(\n      make_not_null(&box));\n  find_common_horizon(obs_box, mesh, element, cache, element_id,\n                      std::add_pointer_t<elem_component>{}, observation_value);\n\n  // Since this event is a combination of two events, and those two events are\n  // individually tested, here we only check we have the correct number of\n  // queued actions on each component.\n  check_results(1);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.FindCommonHorizon\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  common_horizon_event();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Protocols/Test_Protocols.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Protocols/Test_Protocols.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/ParallelAlgorithms/ApparentHorizonFinder/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace TestHelpers::ah {\nstatic_assert(tt::assert_conforms_to_v<ExampleHorizonFindCallback,\n                                       ::ah::protocols::Callback>);\n\nstatic_assert(tt::assert_conforms_to_v<ExampleHorizonMetavars,\n                                       ::ah::protocols::HorizonMetavars>);\n}  // namespace TestHelpers::ah\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.ApparentHorizonFinder.Python\"\n  Test_FastFlow.py\n  \"Unit;Python\"\n  PyApparentHorizonFinder)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Python/Test_FastFlow.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.ApparentHorizonFinder import FastFlow, FlowType\n\n\nclass TestFastFlow(unittest.TestCase):\n    def test_fast_flow(self):\n        fast_flow = FastFlow(\n            FlowType.Fast,\n            alpha=1.0,\n            beta=0.5,\n            abs_tol=1e-12,\n            truncation_tol=0.01,\n            divergence_tol=1.2,\n            divergence_iter=5,\n            max_its=100,\n        )\n        # Can't test any iterations until we bind a Schwarzschild or Kerr\n        # solution in Python, or have numeric data with a horizon stored\n        # somewhere.\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_CleanUp.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/CleanUp.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace ah {\nnamespace {\ntemplate <typename Fr>\nvoid test_cleanup() {\n  LinkedMessageId<double> expected_current_time{3.0, {2.0}};\n  std::optional<LinkedMessageId<double>> current_time{expected_current_time};\n  std::unordered_map<LinkedMessageId<double>,\n                     ah::Storage::SingleTimeStorage<Fr>>\n      all_storage{};\n  all_storage[expected_current_time] = ah::Storage::SingleTimeStorage<Fr>{};\n  std::set<LinkedMessageId<double>> completed_times{\n      LinkedMessageId<double>{1.0, std::nullopt},\n      LinkedMessageId<double>{2.0, {1.0}}};\n  FastFlow fast_flow{\n      FastFlow::FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5, 100};\n\n  clean_up_horizon_finder(\n      make_not_null(&current_time), make_not_null(&all_storage),\n      make_not_null(&completed_times), make_not_null(&fast_flow));\n\n  CHECK_FALSE(current_time.has_value());\n  CHECK_FALSE(all_storage.contains(expected_current_time));\n  CHECK(completed_times.contains(expected_current_time));\n  CHECK(fast_flow.current_iteration() == 0);\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      clean_up_horizon_finder(\n          make_not_null(&current_time), make_not_null(&all_storage),\n          make_not_null(&completed_times), make_not_null(&fast_flow)),\n      Catch::Matchers::ContainsSubstring(\n          \"Current time must be set in order to clean up the horizon finder\"));\n#endif\n\n  // Check that completed times is limited to 1000 entries\n  expected_current_time = LinkedMessageId<double>{2000.0, {1999.0}};\n  current_time = expected_current_time;\n  all_storage[expected_current_time] = ah::Storage::SingleTimeStorage<Fr>{};\n  for (size_t i = 4; i < 2000; i++) {\n    completed_times.insert(LinkedMessageId<double>{\n        static_cast<double>(i), {static_cast<double>(i - 1)}});\n  }\n\n  clean_up_horizon_finder(\n      make_not_null(&current_time), make_not_null(&all_storage),\n      make_not_null(&completed_times), make_not_null(&fast_flow));\n\n  CHECK(completed_times.size() == 1000);\n  CHECK(*std::prev(completed_times.end()) == expected_current_time);\n  CHECK(*completed_times.begin() == LinkedMessageId<double>{1001.0, {1000.0}});\n  CHECK(fast_flow.current_iteration() == 0);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.CleanUp\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_cleanup<::Frame::Grid>();\n  test_cleanup<::Frame::Distorted>();\n  test_cleanup<::Frame::Inertial>();\n}\n}  // namespace ah\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_ComputeCoords.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <catch2/catch_test_macros.hpp>\n#include <cstddef>\n#include <deque>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeCoords.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace ah {\nnamespace {\nvoid test_compute_points() {\n  // Only test grid frame because the only difference is a different code path\n  // is taken within block logical coordinates which we aren't testing here\n  using Fr = ::Frame::Grid;\n\n  const size_t l_max = 4;\n  ah::Storage::Iteration<Fr> current_iteration{};\n\n  const domain::creators::Sphere sphere{\n      1.0,          3.0,  domain::creators::Sphere::Excision{nullptr},\n      0_st,         2_st, true,\n      std::nullopt, {2.0}};\n  const Domain<3> domain = sphere.create_domain();\n  const domain::FunctionsOfTimeMap functions_of_time =\n      sphere.functions_of_time();\n\n  const size_t max_compute_coords_retries = 3;\n  const LinkedMessageId<double> time{.id = 4.0, .previous = {3.0}};\n  ylm::Strahlkorper<Fr> initial_guess{l_max, 2.5, std::array{0.0, 0.0, 0.0}};\n  ylm::Strahlkorper<Fr> previous_iteration_surface =\n      current_iteration.strahlkorper;\n  std::deque<ah::Storage::PreviousSurface<Fr>> previous_surfaces{};\n  FastFlow fast_flow{\n      FastFlow::FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5, 100};\n  std::vector<size_t> block_order{};\n\n  // We should find points\n  {\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time);\n\n    // We don't check the actual points because the function basically just\n    // wraps block_logical_coordinates and that's already tested. Here we check\n    // the wrapping logic\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.compute_coords_retries == 0);\n  }\n\n  // This will succeed after failing once\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    const double initial_radius = 0.75;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, initial_radius, std::array{0.0, 0.0, 0.0}};\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.strahlkorper.coefficients()[0] ==\n          1.5 * sqrt(8.0) * initial_radius);\n    CHECK(current_iteration.compute_coords_retries == 1);\n  }\n\n  // This will fail to find points and will increase the radius of the\n  // strahlkorper by 50% 3 times.\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    const double initial_radius = 1.e-6;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, initial_radius, std::array{0.0, 0.0, 0.0}};\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time);\n\n    CHECK_FALSE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::none_of(current_iteration.block_coord_holders.value(),\n                       [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.strahlkorper.coefficients()[0] ==\n          approx(cube(1.5) * sqrt(8.0) * initial_radius));\n    CHECK(current_iteration.compute_coords_retries == 3);\n  }\n\n  // Force linear extrapolation of previous surfaces that will succeed\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, 1.0, std::array{0.0, 0.0, 0.0}};\n\n    const LinkedMessageId<double> prev_time_1{.id = 3.0, .previous = {2.0}};\n    const LinkedMessageId<double> prev_time_2{.id = 2.0, .previous = {1.0}};\n    previous_surfaces.emplace_front(\n        prev_time_2,\n        ylm::Strahlkorper<Fr>{l_max, 1.1, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n    previous_surfaces.emplace_front(\n        prev_time_1,\n        ylm::Strahlkorper<Fr>{l_max, 1.3, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.compute_coords_retries == 0);\n  }\n\n  // Force quadratic extrapolation of previous surfaces that will succeed\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n\n    const LinkedMessageId<double> prev_time{.id = 1.0,\n                                            .previous = std::nullopt};\n    previous_surfaces.emplace_back(\n        prev_time, ylm::Strahlkorper<Fr>{l_max, 1.0, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.compute_coords_retries == 0);\n  }\n\n  // Succeed on second iteration of fast flow, but we had to recompute once\n  {\n    previous_surfaces.clear();\n    current_iteration.block_coord_holders.reset();\n    const double initial_radius = 1.0;\n    current_iteration.strahlkorper =\n        ylm::Strahlkorper<Fr>{l_max, initial_radius, std::array{0.0, 0.0, 0.0}};\n    current_iteration.compute_coords_retries = 0;\n\n    // Use Schwarzschild solution to get valid tensors for a fast flow iteration\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    const gr::Solutions::KerrSchild analytic_solution{\n        1.0, std::array{0.0, 0.0, 0.0}, std::array{0.0, 0.0, 0.0}};\n    Variables<\n        tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, 3, Fr>,\n                   gr::Tags::ExtrinsicCurvature<DataVector, 3, Fr>,\n                   gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, Fr>>>\n        vars{ylm::Spherepack::physical_size(l_mesh, l_mesh)};\n    vars.assign_subset(analytic_solution.variables(\n        ylm::cartesian_coords(\n            ylm::Strahlkorper<Fr>{l_mesh, 1.0, std::array{0.0, 0.0, 0.0}}),\n        time.id, typename std::decay_t<decltype(vars)>::tags_list{}));\n\n    fast_flow.iterate_horizon_finder(\n        make_not_null(&current_iteration.strahlkorper),\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3, Fr>>(vars),\n        get<gr::Tags::ExtrinsicCurvature<DataVector, 3, Fr>>(vars),\n        get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, Fr>>(vars));\n\n    // Reset strahlkorpers so that the average of these two coefficients are\n    // used\n    current_iteration.strahlkorper =\n        ylm::Strahlkorper<Fr>{l_max, 0.9, std::array{0.0, 0.0, 0.0}};\n    previous_iteration_surface =\n        ylm::Strahlkorper<Fr>{l_max, 2.0, std::array{0.0, 0.0, 0.0}};\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.strahlkorper.coefficients()[0] ==\n          approx(sqrt(8.0) * (0.9 + 0.5 * 1.1)));\n    CHECK(current_iteration.compute_coords_retries == 1);\n  }\n}\n\nvoid test_compute_points_different_resolutions() {\n  // As in test_compute_points(), only test grid frame because the only\n  // difference is a different code path is taken within block logical\n  // coordinates which we aren't testing here.\n  using Fr = ::Frame::Grid;\n\n  const size_t l_max = 4;\n  const size_t higher_l_max = 6;\n  ah::Storage::Iteration<Fr> current_iteration{};\n\n  const domain::creators::Sphere sphere{\n      1.0,          3.0,  domain::creators::Sphere::Excision{nullptr},\n      0_st,         2_st, true,\n      std::nullopt, {2.0}};\n  const Domain<3> domain = sphere.create_domain();\n  const domain::FunctionsOfTimeMap functions_of_time =\n      sphere.functions_of_time();\n\n  const size_t max_compute_coords_retries = 3;\n  const LinkedMessageId<double> time{.id = 4.0, .previous = {3.0}};\n  ylm::Strahlkorper<Fr> initial_guess{l_max, 2.5, std::array{0.0, 0.0, 0.0}};\n  ylm::Strahlkorper<Fr> previous_iteration_surface =\n      current_iteration.strahlkorper;\n  std::deque<ah::Storage::PreviousSurface<Fr>> previous_surfaces{};\n  const FastFlow fast_flow{\n      FastFlow::FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5, 100};\n  std::vector<size_t> block_order{};\n\n  // Test case 1: current_resolution_l is set and different from initial_guess\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, 2.5, std::array{0.0, 0.0, 0.0}};\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time, higher_l_max,\n        false);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    REQUIRE(current_iteration.strahlkorper.l_max() == higher_l_max);\n    REQUIRE(current_iteration.compute_coords_retries == 0);\n\n    // Verify that the strahlkorper is set to the initial guess but at higher\n    // resolution\n    const ylm::Strahlkorper<Fr> expected_strahlkorper{\n        higher_l_max, initial_guess.average_radius(),\n        initial_guess.expansion_center()};\n    CHECK(current_iteration.strahlkorper == expected_strahlkorper);\n  }\n\n  // Test case 2: rerunning_with_higher_resolution is true\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, 2.5, std::array{0.0, 0.0, 0.0}};\n    previous_iteration_surface =\n        ylm::Strahlkorper<Fr>{l_max, 2.0, std::array{0.0, 0.0, 0.0}};\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time, higher_l_max,\n        true);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.strahlkorper.l_max() == higher_l_max);\n    CHECK(current_iteration.compute_coords_retries == 0);\n\n    // Verify that the strahlkorper is set to the previous surface but prolonged\n    // to higher resolution\n    const ylm::Strahlkorper<Fr> expected_strahlkorper{\n        higher_l_max, previous_iteration_surface.average_radius(),\n        previous_iteration_surface.expansion_center()};\n    CHECK(current_iteration.strahlkorper == expected_strahlkorper);\n  }\n\n  // Test case 3: Different resolutions in previous_surfaces for linear\n  // extrapolation\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, 1.0, std::array{0.0, 0.0, 0.0}};\n\n    const LinkedMessageId<double> prev_time_1{.id = 3.0, .previous = {2.0}};\n    const LinkedMessageId<double> prev_time_2{.id = 2.0, .previous = {1.0}};\n    previous_surfaces.emplace_front(\n        prev_time_2,\n        ylm::Strahlkorper<Fr>{l_max, 1.1, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n    previous_surfaces.emplace_front(\n        prev_time_1,\n        ylm::Strahlkorper<Fr>{higher_l_max, 1.3, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time, higher_l_max);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.compute_coords_retries == 0);\n\n    // Verify that current_iteration.strahlkorper has resolution higher_l_max\n    CHECK(current_iteration.strahlkorper.l_max() == higher_l_max);\n  }\n\n  // Test case 4: Different resolutions in previous_surfaces for quadratic\n  // extrapolation\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, 1.0, std::array{0.0, 0.0, 0.0}};\n\n    const LinkedMessageId<double> prev_time_1{.id = 3.0, .previous = {2.0}};\n    const LinkedMessageId<double> prev_time_2{.id = 2.0, .previous = {1.0}};\n    const LinkedMessageId<double> prev_time_3{.id = 1.0,\n                                              .previous = std::nullopt};\n    previous_surfaces.emplace_front(\n        prev_time_3,\n        ylm::Strahlkorper<Fr>{l_max, 1.0, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n    previous_surfaces.emplace_front(\n        prev_time_2,\n        ylm::Strahlkorper<Fr>{higher_l_max, 1.1, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n    previous_surfaces.emplace_front(\n        prev_time_1,\n        ylm::Strahlkorper<Fr>{l_max, 1.3, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time, higher_l_max);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.compute_coords_retries == 0);\n\n    // Verify that current_iteration.strahlkorper has resolution higher_l_max\n    CHECK(current_iteration.strahlkorper.l_max() == higher_l_max);\n  }\n\n  // Test case 5: current_resolution_l is set and different from\n  // previous_surfaces\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, 1.0, std::array{0.0, 0.0, 0.0}};\n\n    const LinkedMessageId<double> prev_time{.id = 1.0,\n                                            .previous = std::nullopt};\n    previous_surfaces.emplace_back(\n        prev_time, ylm::Strahlkorper<Fr>{l_max, 1.0, std::array{0.0, 0.0, 0.0}},\n        std::unordered_set<ElementId<3>>{});\n\n    const bool coords_set_successfully = set_current_iteration_coords(\n        make_not_null(&current_iteration), make_not_null(&block_order), time,\n        fast_flow, initial_guess, previous_iteration_surface, previous_surfaces,\n        max_compute_coords_retries, domain, functions_of_time, higher_l_max,\n        false);\n\n    REQUIRE(coords_set_successfully);\n    REQUIRE(current_iteration.block_coord_holders.has_value());\n    const size_t l_mesh =\n        fast_flow.current_l_mesh(current_iteration.strahlkorper);\n    REQUIRE(current_iteration.block_coord_holders.value().size() ==\n            ylm::Spherepack::physical_size(l_mesh, l_mesh));\n    CHECK(alg::all_of(current_iteration.block_coord_holders.value(),\n                      [](const auto& holder) { return holder.has_value(); }));\n    CHECK(current_iteration.strahlkorper.l_max() == higher_l_max);\n    CHECK(current_iteration.compute_coords_retries == 0);\n  }\n\n  // Test assertion failures\n  {\n    current_iteration.block_coord_holders.reset();\n    current_iteration.strahlkorper = ylm::Strahlkorper<Fr>{};\n    current_iteration.compute_coords_retries = 0;\n    initial_guess =\n        ylm::Strahlkorper<Fr>{l_max, 2.5, std::array{0.0, 0.0, 0.0}};\n    previous_iteration_surface =\n        ylm::Strahlkorper<Fr>{l_max, 2.0, std::array{0.0, 0.0, 0.0}};\n\n    // Test that rerunning_with_higher_resolution=true without\n    // current_resolution_l fails\n    CHECK_THROWS_WITH(\n        set_current_iteration_coords(\n            make_not_null(&current_iteration), make_not_null(&block_order),\n            time, fast_flow, initial_guess, previous_iteration_surface,\n            previous_surfaces, max_compute_coords_retries, domain,\n            functions_of_time, std::nullopt, true),\n        Catch::Matchers::ContainsSubstring(\"Current resolution L is not set\"));\n\n    // Test that rerunning_with_higher_resolution=true with current_resolution_l\n    // <= previous surface resolution fails\n    CHECK_THROWS_WITH(\n        set_current_iteration_coords(\n            make_not_null(&current_iteration), make_not_null(&block_order),\n            time, fast_flow, initial_guess, previous_iteration_surface,\n            previous_surfaces, max_compute_coords_retries, domain,\n            functions_of_time, l_max,\n            true),  // l_max is not > previous_iteration_surface.l_max()\n        Catch::Matchers::ContainsSubstring(\n            \"Previous iteration surface has resolution\"));\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.ComputeCoords\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_compute_points();\n  test_compute_points_different_resolutions();\n}\n}  // namespace ah\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_ComputeVarsToInterpolateToTarget.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/Sphere.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/TranslationMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeVarsToInterpolateToTarget.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <typename Fr>\nvoid test_compute_horizon_volume_quantities(const bool is_time_dependent) {\n  CAPTURE(is_time_dependent);\n  CAPTURE(pretty_type::name<Fr>());\n  const size_t number_of_grid_points = 12;\n\n  const double mass = 1.0;\n  const std::array spin{0.1, 0.2, 0.3};\n  const LinkedMessageId<double> time{0.01, std::nullopt};\n  // We have time dependent maps so the code takes the correct paths, but we\n  // make them the identity so the vars that we interpolate are the same in each\n  // frame which makes checking them much easier. Also we have small grid\n  // spacing so derivatives are more accurate.\n  const domain::creators::Sphere domain_creator{\n      0.9,\n      1.0,\n      domain::creators::Sphere::Excision{nullptr},\n      1_st,\n      number_of_grid_points,\n      true,\n      std::nullopt,\n      {0.95},\n      domain::CoordinateMaps::Distribution::Linear,\n      ShellWedges::All,\n      is_time_dependent\n          ? std::optional{domain::creators::sphere::TimeDependentMapOptions{\n                0.0,\n                domain::creators::time_dependent_options::ShapeMapOptions<\n                    false, ::domain::ObjectLabel::None>{8_st, std::nullopt},\n                std::nullopt, std::nullopt,\n                domain::creators::time_dependent_options::TranslationMapOptions<\n                    3>{std::array{std::array{0.0, 0.0, 0.0},\n                                  std::array{0.0, 0.0, 0.0},\n                                  std::array{0.0, 0.0, 0.0}}},\n                true}}\n          : std::nullopt};\n  const auto domain = domain_creator.create_domain();\n  const auto& functions_of_time = domain_creator.functions_of_time();\n\n  // Just use the first block, it has all maps and all frames\n  const auto& block = domain.blocks()[0];\n  const auto element_ids = initial_element_ids(\n      block.id(), domain_creator.initial_refinement_levels()[block.id()]);\n\n  // Set up target coordinates, and jacobian.\n  const Mesh mesh{domain_creator.initial_extents()[block.id()],\n                  Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n  tnsr::I<DataVector, 3, Fr> target_frame_coords{};\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coords{};\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inv_jacobian_logical_to_inertial{mesh.number_of_grid_points(), 0.0};\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Fr>\n      inv_jacobian_logical_to_frame{mesh.number_of_grid_points(), 0.0};\n  if (is_time_dependent) {\n    const ElementMap<3, Frame::Grid> map_logical_to_grid{\n        element_ids[0], block.moving_mesh_logical_to_grid_map().get_clone()};\n\n    inertial_coords = block.moving_mesh_grid_to_inertial_map()(\n        map_logical_to_grid(logical_coords), time.id, functions_of_time);\n\n    const auto inv_jacobian_logical_to_grid =\n        map_logical_to_grid.inv_jacobian(logical_coords);\n    const auto inv_jacobian_grid_to_inertial =\n        block.moving_mesh_grid_to_inertial_map().inv_jacobian(\n            map_logical_to_grid(logical_coords), time.id, functions_of_time);\n\n    inv_jacobian_logical_to_inertial = tenex::evaluate<ti::I, ti::j>(\n        inv_jacobian_logical_to_grid(ti::I, ti::k) *\n        inv_jacobian_grid_to_inertial(ti::K, ti::j));\n\n    if constexpr (std::is_same_v<Fr, Frame::Grid>) {\n      inv_jacobian_logical_to_frame = inv_jacobian_logical_to_grid;\n    } else if constexpr (std::is_same_v<Fr, Frame::Inertial>) {\n      inv_jacobian_logical_to_frame = inv_jacobian_logical_to_inertial;\n    } else {\n      const auto inv_jacobian_grid_to_distorted =\n          block.moving_mesh_grid_to_distorted_map().inv_jacobian(\n              map_logical_to_grid(logical_coords), time.id, functions_of_time);\n\n      inv_jacobian_logical_to_frame = tenex::evaluate<ti::I, ti::j>(\n          inv_jacobian_logical_to_grid(ti::I, ti::k) *\n          inv_jacobian_grid_to_distorted(ti::K, ti::j));\n    }\n\n    if constexpr (std::is_same_v<Fr, Frame::Grid>) {\n      target_frame_coords = map_logical_to_grid(logical_coords);\n    } else if constexpr (std::is_same_v<Fr, Frame::Distorted>) {\n      target_frame_coords = block.moving_mesh_grid_to_distorted_map()(\n          map_logical_to_grid(logical_coords), time.id, functions_of_time);\n    } else {\n      static_assert(std::is_same_v<Fr, Frame::Inertial>,\n                    \"Fr must be the Inertial frame\");\n      target_frame_coords = block.moving_mesh_grid_to_inertial_map()(\n          map_logical_to_grid(logical_coords), time.id, functions_of_time);\n    }\n\n  } else {\n    // time-independent.\n    if (std::is_same_v<Fr, Frame::Distorted>) {\n      ERROR(\"Can't have a time independent distorted frame.\");\n    }\n    // Grid == inertial so just get inertial and set the frame\n    const ElementMap<3, Frame::Inertial> map_logical_to_inertial{\n        element_ids[0], block.stationary_map().get_clone()};\n    inv_jacobian_logical_to_inertial =\n        map_logical_to_inertial.inv_jacobian(logical_coords);\n\n    inertial_coords = map_logical_to_inertial(logical_coords);\n    for (size_t i = 0; i < 3; i++) {\n      target_frame_coords[i] = inertial_coords[i];\n      for (size_t j = 0; j < 3; j++) {\n        inv_jacobian_logical_to_frame.get(i, j) =\n            inv_jacobian_logical_to_inertial.get(i, j);\n      }\n    }\n  }\n\n  // Set up analytic solution.\n  const gr::Solutions::KerrSchild solution(mass, spin, {0.0, 0.0, 0.0});\n  const auto solution_vars_inertial_frame = solution.variables(\n      inertial_coords, time.id,\n      typename gr::Solutions::KerrSchild::tags<DataVector, Frame::Inertial>{});\n\n  Variables<ah::source_vars<3>> source_vars{};\n  Variables<ah::vars_to_interpolate_to_target<3, Fr>> target_vars{};\n  // Set g, pi, and phi in the inertial frame\n  {\n    const auto& lapse =\n        get<gr::Tags::Lapse<DataVector>>(solution_vars_inertial_frame);\n    const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(\n        solution_vars_inertial_frame);\n    const auto& d_lapse =\n        get<typename gr::Solutions::KerrSchild::DerivLapse<DataVector>>(\n            solution_vars_inertial_frame);\n    const auto& shift =\n        get<gr::Tags::Shift<DataVector, 3>>(solution_vars_inertial_frame);\n    const auto& d_shift =\n        get<typename gr::Solutions::KerrSchild::DerivShift<DataVector>>(\n            solution_vars_inertial_frame);\n    const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(\n        solution_vars_inertial_frame);\n    const auto& spatial_metric = get<gr::Tags::SpatialMetric<DataVector, 3>>(\n        solution_vars_inertial_frame);\n    const auto& dt_spatial_metric =\n        get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n            solution_vars_inertial_frame);\n    const auto& d_spatial_metric =\n        get<typename gr::Solutions::KerrSchild::DerivSpatialMetric<DataVector>>(\n            solution_vars_inertial_frame);\n\n    source_vars.initialize(get(lapse).size(), 0.0);\n    get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(source_vars) =\n        gr::spacetime_metric(lapse, shift, spatial_metric);\n    get<::gh::Tags::Phi<DataVector, 3>>(source_vars) = gh::phi(\n        lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n    get<::gh::Tags::Pi<DataVector, 3>>(source_vars) = gh::pi(\n        lapse, dt_lapse, shift, dt_shift, spatial_metric, dt_spatial_metric,\n        get<::gh::Tags::Phi<DataVector, 3>>(source_vars));\n\n    // Need to compute numerical deriv of Phi.\n    get<Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                    Frame::Inertial>>(source_vars) =\n        partial_derivative(get<::gh::Tags::Phi<DataVector, 3>>(source_vars),\n                           mesh, inv_jacobian_logical_to_inertial);\n  }\n\n  // Compute other vars\n  ah::compute_vars_to_interpolate_to_target(\n      make_not_null(&target_vars),\n      get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(source_vars),\n      get<::gh::Tags::Pi<DataVector, 3>>(source_vars),\n      get<::gh::Tags::Phi<DataVector, 3>>(source_vars),\n      get<Tags::deriv<::gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(source_vars),\n      time, domain, mesh, element_ids[0], functions_of_time);\n\n  // Now make sure those computed vars are correct.\n  const auto solution_vars_target_frame = solution.variables(\n      target_frame_coords, 0.0,\n      tmpl::pop_back<ah::vars_to_interpolate_to_target<3, Fr>>{});\n  // Expected vars\n  const auto& expected_spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, Fr>>(\n          solution_vars_target_frame);\n  const auto& expected_inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3, Fr>>(\n          solution_vars_target_frame);\n  const auto& expected_extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3, Fr>>(\n          solution_vars_target_frame);\n  const auto& expected_christoffel =\n      get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, Fr>>(\n          solution_vars_target_frame);\n  const auto deriv_christoffel = partial_derivative(\n      expected_christoffel, mesh, inv_jacobian_logical_to_frame);\n  const auto expected_ricci =\n      gr::ricci_tensor(expected_christoffel, deriv_christoffel);\n\n  // Computed vars\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, Fr>>(target_vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3, Fr>>(target_vars);\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3, Fr>>(target_vars);\n  const auto& christoffel =\n      get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, Fr>>(\n          target_vars);\n  const auto& ricci =\n      get<gr::Tags::SpatialRicci<DataVector, 3, Fr>>(target_vars);\n\n  CHECK_ITERABLE_APPROX(expected_spatial_metric, spatial_metric);\n  CHECK_ITERABLE_APPROX(expected_inverse_spatial_metric,\n                        inverse_spatial_metric);\n  CHECK_ITERABLE_APPROX(expected_extrinsic_curvature, extrinsic_curvature);\n  // Larger tolerance because derivatives are inaccurate\n  const Approx custom_approx_1 = Approx::custom().epsilon(1.e-6).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_christoffel, christoffel,\n                               custom_approx_1);\n  const Approx custom_approx_2 = Approx::custom().epsilon(1.e-5).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected_ricci, ricci, custom_approx_2);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.ComputeVarsToInterpolateToTarget\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  // time-independent.\n  test_compute_horizon_volume_quantities<Frame::Grid>(false);\n  test_compute_horizon_volume_quantities<Frame::Inertial>(false);\n\n  // time-dependent.\n  test_compute_horizon_volume_quantities<Frame::Grid>(true);\n  test_compute_horizon_volume_quantities<Frame::Distorted>(true);\n  test_compute_horizon_volume_quantities<Frame::Inertial>(true);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_CurrentTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <set>\n#include <unordered_map>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"Domain/CoordinateMaps/Distribution.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RotationAboutZAxis.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/MockRuntimeSystemFreeFunctions.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/CurrentTime.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace ah {\nnamespace {\nvoid test_set_current_time() {\n  std::optional<LinkedMessageId<double>> current_time =\n      LinkedMessageId<double>{1.0, std::nullopt};\n  std::set<LinkedMessageId<double>> pending_times{};\n  std::set<LinkedMessageId<double>> completed_times{};\n  std::unordered_map<LinkedMessageId<double>,\n                     ah::Storage::SingleTimeStorage<Frame::Grid>>\n      all_storage{};\n  const std::unordered_map<ElementId<3>, Storage::VolumeVariables<Frame::Grid>>\n      unused_volume_vars{};\n  const Storage::Iteration<Frame::Grid> unused_interation{};\n  const ylm::Strahlkorper<Frame::Grid> unused_prev_strahlkorper{};\n  const ::Verbosity verbosity = ::Verbosity::Silent;\n  const std::string name{\"HorizonMetavars\"};\n\n  const auto add_to_all_storage = [&](const LinkedMessageId<double>& time,\n                                      const Destination destination) {\n    all_storage.emplace(time, ah::Storage::SingleTimeStorage<Frame::Grid>{\n                                  unused_volume_vars,\n                                  {},\n                                  unused_interation,\n                                  unused_prev_strahlkorper,\n                                  destination});\n  };\n\n  // Current time has value, so do nothing\n  {\n    set_current_time(make_not_null(&current_time),\n                     make_not_null(&pending_times), completed_times,\n                     all_storage, verbosity, name);\n    CHECK(current_time ==\n          std::optional{LinkedMessageId<double>{1.0, std::nullopt}});\n    CHECK(pending_times.empty());\n  }\n\n  // Current time doesn't have a value, but pending times is empty, so do\n  // nothing\n  {\n    current_time.reset();\n    set_current_time(make_not_null(&current_time),\n                     make_not_null(&pending_times), completed_times,\n                     all_storage, verbosity, name);\n    CHECK_FALSE(current_time.has_value());\n    CHECK(pending_times.empty());\n  }\n\n  // No completed ids, the pending id isn't the first, and this is a control\n  // system, so do nothing\n  {\n    const LinkedMessageId<double> pending_time{2.0, {1.0}};\n    pending_times.insert(pending_time);\n    add_to_all_storage(pending_time, Destination::ControlSystem);\n    set_current_time(make_not_null(&current_time),\n                     make_not_null(&pending_times), completed_times,\n                     all_storage, verbosity, name);\n    CHECK_FALSE(current_time.has_value());\n    CHECK(pending_times.contains(pending_time));\n  }\n\n  // No completed ids, the pending id isn't the first, and this is a\n  // observation, so make it the current time\n  {\n    all_storage.clear();\n    const LinkedMessageId<double> pending_time{2.0, {1.0}};\n    pending_times.insert(pending_time);\n    add_to_all_storage(pending_time, Destination::Observation);\n    set_current_time(make_not_null(&current_time),\n                     make_not_null(&pending_times), completed_times,\n                     all_storage, verbosity, name);\n    CHECK(current_time == std::optional{pending_time});\n    CHECK(pending_times.empty());\n  }\n\n  // No completed ids and the pending id is the first, so make it the current\n  // time\n  {\n    all_storage.clear();\n    current_time.reset();\n    const LinkedMessageId<double> pending_time{1.0, std::nullopt};\n    pending_times.insert(pending_time);\n    add_to_all_storage(pending_time, Destination::ControlSystem);\n    set_current_time(make_not_null(&current_time),\n                     make_not_null(&pending_times), completed_times,\n                     all_storage, verbosity, name);\n    CHECK(current_time == std::optional{pending_time});\n    CHECK(pending_times.empty());\n  }\n\n  // One completed id, this is a control system, but the pending id isn't next\n  // so do nothing\n  {\n    all_storage.clear();\n    current_time.reset();\n    pending_times.clear();\n    completed_times.insert(LinkedMessageId<double>{1.0, std::nullopt});\n    const LinkedMessageId<double> pending_time{3.0, {2.0}};\n    pending_times.insert(pending_time);\n    add_to_all_storage(pending_time, Destination::ControlSystem);\n    set_current_time(make_not_null(&current_time),\n                     make_not_null(&pending_times), completed_times,\n                     all_storage, verbosity, name);\n    CHECK_FALSE(current_time.has_value());\n    CHECK(pending_times.contains(pending_time));\n  }\n\n  // One completed id and this is an observation, so use the next pending id\n  {\n    all_storage.clear();\n    current_time.reset();\n    pending_times.clear();\n    completed_times.insert(LinkedMessageId<double>{1.0, std::nullopt});\n    const LinkedMessageId<double> pending_time{3.0, {2.0}};\n    pending_times.insert(pending_time);\n    add_to_all_storage(pending_time, Destination::Observation);\n    set_current_time(make_not_null(&current_time),\n                     make_not_null(&pending_times), completed_times,\n                     all_storage, verbosity, name);\n    CHECK(current_time == std::optional{pending_time});\n    CHECK(pending_times.empty());\n  }\n\n  // One completed id, and the pending id is next so make it the current time\n  {\n    all_storage.clear();\n    current_time.reset();\n    pending_times.clear();\n    completed_times.insert(LinkedMessageId<double>{1.0, std::nullopt});\n    const LinkedMessageId<double> pending_time{2.0, {1.0}};\n    pending_times.insert(pending_time);\n    add_to_all_storage(pending_time, Destination::ControlSystem);\n    set_current_time(make_not_null(&current_time),\n                     make_not_null(&pending_times), completed_times,\n                     all_storage, verbosity, name);\n    CHECK(current_time == std::optional{pending_time});\n    CHECK_FALSE(pending_times.contains(pending_time));\n  }\n}\n\nstruct UpdateFoT {\n  static void apply(\n      const gsl::not_null<std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n          f_of_t_list,\n      const std::string& f_of_t_name, const double update_time,\n      DataVector update_deriv, const double new_expiration_time) {\n    (*f_of_t_list)\n        .at(f_of_t_name)\n        ->update(update_time, std::move(update_deriv), new_expiration_time);\n  }\n};\n\nstruct MockHorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using time_tag = ::Tags::TimeAndPrevious<0>;\n\n  using frame = ::Frame::Grid;\n\n  // Don't need callbacks\n  using horizon_find_callbacks = tmpl::list<>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr Destination destination = Destination::ControlSystem;\n\n  static std::string name() { return \"MockHorizonMetavars\"; }\n};\n\nsize_t call_count = 0;  // NOLINT\nstruct MockFindApparentHorizon {\n  template <typename ParallelComponent, typename DbTags, typename Metavariables,\n            typename ArrayIndex>\n  static void apply(db::DataBox<DbTags>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const LinkedMessageId<double>& /*incoming_time*/,\n                    const ElementId<3>& /*incoming_element_id*/,\n                    const ::Mesh<3>& /*incoming_mesh*/,\n                    Variables<ah::vars_to_interpolate_to_target<\n                        3, MockHorizonMetavars::frame>>&& /*incoming_vars*/,\n                    const std::optional<std::string>& /*dependency*/,\n                    const bool vars_have_already_been_received = false) {\n    ++call_count;\n    CHECK(vars_have_already_been_received);\n  }\n};\n\ntemplate <typename Metavariables>\nstruct MockComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using component_being_mocked =\n      ah::Component<Metavariables, MockHorizonMetavars>;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<3>>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n\n  using replace_these_simple_actions =\n      tmpl::list<ah::FindApparentHorizon<MockHorizonMetavars>>;\n  using with_these_simple_actions = tmpl::list<MockFindApparentHorizon>;\n};\n\nstruct MockMetavariables {\n  using component_list = tmpl::list<MockComponent<MockMetavariables>>;\n};\n\nvoid test_check_current_time() {\n  (void)MockHorizonMetavars::destination;\n\n  const auto domain_creator = domain::creators::Sphere(\n      1.8, 2.2, domain::creators::Sphere::Excision{}, 1_st, 5_st, false,\n      std::nullopt, std::vector<double>{},\n      domain::CoordinateMaps::Distribution::Linear, ShellWedges::All,\n      {std::make_unique<\n          domain::creators::time_dependence::RotationAboutZAxis<3>>(0.0, 0.0,\n                                                                    0.1, 0.0)});\n\n  using component = MockComponent<MockMetavariables>;\n\n  ActionTesting::MockRuntimeSystem<MockMetavariables> runner{\n      {domain_creator.create_domain()},\n      {domain_creator.functions_of_time({{\"Rotation\", 2.0}})}};\n\n  ActionTesting::emplace_array_component<component>(\n      make_not_null(&runner), ActionTesting::NodeId{0},\n      ActionTesting::LocalCoreId{0}, 0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& cache = ActionTesting::cache<component>(runner, 0_st);\n\n  const LinkedMessageId<double> incoming_time{100.0, {99.0}};\n  const ElementId<3> incoming_element_id{1};\n  const Mesh<3> incoming_mesh{2, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto};\n  const std::optional<std::string> dependency{\"FakeDependency\"};\n  LinkedMessageId<double> current_time{0.0, {std::nullopt}};\n\n  // Current time is ready so no callback is registered\n  CHECK(check_if_current_time_is_ready<MockHorizonMetavars>(\n      current_time, cache, incoming_time, incoming_element_id, incoming_mesh,\n      dependency));\n\n  // Current time isn't ready so a callback should be registered\n  current_time = LinkedMessageId<double>{2.5, {1.5}};\n  CHECK_FALSE(check_if_current_time_is_ready<MockHorizonMetavars>(\n      current_time, cache, incoming_time, incoming_element_id, incoming_mesh,\n      dependency));\n\n  // Mutate the function of time which will call (queue) the simple action\n  Parallel::mutate<domain::Tags::FunctionsOfTime, UpdateFoT>(\n      cache, \"Rotation\"s, 2.0, DataVector{0.0}, 3.0);\n  REQUIRE(ActionTesting::number_of_queued_simple_actions<component>(runner,\n                                                                    0) == 1);\n  ActionTesting::invoke_queued_simple_action<component>(make_not_null(&runner),\n                                                        0);\n  CHECK(call_count == 1);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.CurrentTime\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n\n  test_set_current_time();\n  test_check_current_time();\n}\n}  // namespace ah\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_Destination.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace ah {\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Destination\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  CHECK(get_output(Destination::ControlSystem) == \"ControlSystem\");\n  CHECK(get_output(Destination::Observation) == \"Observation\");\n  CHECK(TestHelpers::test_creation<Destination>(\"ControlSystem\") ==\n        Destination::ControlSystem);\n  CHECK(TestHelpers::test_creation<Destination>(\"Observation\") ==\n        Destination::Observation);\n}\n}  // namespace ah\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_FastFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <random>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nFastFlow::Status do_iteration(\n    const gsl::not_null<ylm::Strahlkorper<Frame::Inertial>*> strahlkorper,\n    const gsl::not_null<FastFlow*> flow,\n    const gr::Solutions::KerrSchild& solution) {\n  FastFlow::Status status = FastFlow::Status::SuccessfulIteration;\n\n  while (status == FastFlow::Status::SuccessfulIteration) {\n    const auto l_mesh = flow->current_l_mesh(*strahlkorper);\n    const auto prolonged_strahlkorper =\n        ylm::Strahlkorper<Frame::Inertial>(l_mesh, l_mesh, *strahlkorper);\n\n    const auto box = db::create<\n        db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n        db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n        prolonged_strahlkorper);\n\n    const double t = 0.0;\n    const auto& cart_coords =\n        db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n    const auto vars = solution.variables(\n        cart_coords, t, gr::Solutions::KerrSchild::tags<DataVector>{});\n\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n    const auto& deriv_spatial_metric =\n        get<gr::Solutions::KerrSchild::DerivSpatialMetric<DataVector>>(vars);\n    const auto inverse_spatial_metric =\n        determinant_and_inverse(spatial_metric).second;\n\n    const auto status_and_info = flow->iterate_horizon_finder<Frame::Inertial>(\n        strahlkorper, inverse_spatial_metric,\n        gr::extrinsic_curvature(\n            get<gr::Tags::Lapse<DataVector>>(vars),\n            get<gr::Tags::Shift<DataVector, 3>>(vars),\n            get<gr::Solutions::KerrSchild::DerivShift<DataVector>>(vars),\n            spatial_metric,\n            get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars),\n            deriv_spatial_metric),\n        raise_or_lower_first_index(\n            gr::christoffel_first_kind(deriv_spatial_metric),\n            inverse_spatial_metric));\n    status = status_and_info.first;\n  }\n  return status;\n}\n\nvoid test_construct_from_options_fast() {\n  const auto created = TestHelpers::test_creation<FastFlow>(\n      \"Flow: Fast\\n\"\n      \"Alpha: 1.1\\n\"\n      \"Beta: 0.6\\n\"\n      \"AbsTol: 1.e-10\\n\"\n      \"TruncationTol: 1.e-3\\n\"\n      \"DivergenceTol: 1.1\\n\"\n      \"DivergenceIter: 6\\n\"\n      \"MaxIts: 200\");\n  CHECK(created ==\n        FastFlow(FastFlow::FlowType::Fast, 1.1, 0.6, 1e-10, 1e-3, 1.1, 6, 200));\n}\n\nvoid test_construct_from_options_jacobi() {\n  const auto created = TestHelpers::test_creation<FastFlow>(\n      \"Flow: Jacobi\\n\"\n      \"Alpha: 1.1\\n\"\n      \"Beta: 0.6\\n\"\n      \"AbsTol: 1.e-10\\n\"\n      \"TruncationTol: 1.e-3\\n\"\n      \"DivergenceTol: 1.1\\n\"\n      \"DivergenceIter: 6\\n\"\n      \"MaxIts: 200\");\n  CHECK(created == FastFlow(FastFlow::FlowType::Jacobi, 1.1, 0.6, 1e-10, 1e-3,\n                            1.1, 6, 200));\n}\n\nvoid test_construct_from_options_curvature() {\n  const auto created = TestHelpers::test_creation<FastFlow>(\n      \"Flow: Curvature\\n\"\n      \"Alpha: 1.1\\n\"\n      \"Beta: 0.6\\n\"\n      \"AbsTol: 1.e-10\\n\"\n      \"TruncationTol: 1.e-3\\n\"\n      \"DivergenceTol: 1.1\\n\"\n      \"DivergenceIter: 6\\n\"\n      \"MaxIts: 200\");\n  CHECK(created == FastFlow(FastFlow::FlowType::Curvature, 1.1, 0.6, 1e-10,\n                            1e-3, 1.1, 6, 200));\n}\n\nvoid test_serialize() {\n  FastFlow fastflow(FastFlow::FlowType::Jacobi, 1.1, 0.6, 1e-10, 1e-3, 1.1, 6,\n                    200);\n  test_serialization(fastflow);\n}\n\nvoid test_copy_and_move() {\n  FastFlow fastflow(FastFlow::FlowType::Curvature, 1.1, 0.6, 1e-10, 1e-3, 1.1,\n                    6, 200);\n  test_copy_semantics(fastflow);\n  auto fastflow_copy = fastflow;\n  // clang-tidy: std::move of triviable-copyable type\n  test_move_semantics(std::move(fastflow), fastflow_copy);  // NOLINT\n}\n\nvoid test_ostream() {\n  CHECK(get_output(FastFlow::FlowType::Curvature) == \"Curvature\");\n  CHECK(get_output(FastFlow::FlowType::Jacobi) == \"Jacobi\");\n  CHECK(get_output(FastFlow::FlowType::Fast) == \"Fast\");\n  CHECK(get_output(FastFlow::Status::SuccessfulIteration) == \"Still iterating\");\n  CHECK(get_output(FastFlow::Status::AbsTol) ==\n        \"Converged: Absolute tolerance\");\n  CHECK(get_output(FastFlow::Status::TruncationTol) ==\n        \"Converged: Truncation tolerance\");\n  CHECK(get_output(FastFlow::Status::MaxIts) == \"Failed: Too many iterations\");\n  CHECK(get_output(FastFlow::Status::NegativeRadius) ==\n        \"Failed: Negative radius\");\n  CHECK(get_output(FastFlow::Status::DivergenceError) == \"Failed: Diverging\");\n}\n\nvoid test_negative_radius_error() {\n  // Set initial Strahlkorper radius to negative on purpose to get\n  // error exit status.\n  ylm::Strahlkorper<Frame::Inertial> strahlkorper(5, 5, -1.0, {{0, 0, 0}});\n  FastFlow flow(FastFlow::FlowType::Fast, 1.0, 0.5, 1e-12, 1e-10, 1.2, 5, 100);\n\n  const gr::Solutions::KerrSchild solution(1.0, {{0., 0., 0.}}, {{0., 0., 0.}});\n\n  const auto status = do_iteration(&strahlkorper, &flow, solution);\n  CHECK(status == FastFlow::Status::NegativeRadius);\n}\n\nvoid test_too_many_iterations_error() {\n  ylm::Strahlkorper<Frame::Inertial> strahlkorper(5, 5, 3.0, {{0, 0, 0}});\n  // Set number of iterations to 1 on purpose to get error exit status.\n  FastFlow flow(FastFlow::FlowType::Fast, 1.0, 0.5, 1e-12, 1e-10, 1.2, 5, 1);\n\n  const gr::Solutions::KerrSchild solution(1.0, {{0., 0., 0.}}, {{0., 0., 0.}});\n\n  const auto status = do_iteration(&strahlkorper, &flow, solution);\n  CHECK(status == FastFlow::Status::MaxIts);\n}\n\nvoid test_schwarzschild(FastFlow::Flow::type type_of_flow,\n                        const size_t max_iterations) {\n  ylm::Strahlkorper<Frame::Inertial> strahlkorper(5, 5, 3.0, {{0, 0, 0}});\n  FastFlow flow(type_of_flow, 1.0, 0.5, 1e-12, 1e-10, 1.2, 5, max_iterations);\n\n  const gr::Solutions::KerrSchild solution(1.0, {{0., 0., 0.}}, {{0., 0., 0.}});\n\n  const auto iterate_and_check = [&strahlkorper, &flow, &solution]() {\n    const auto status = do_iteration(&strahlkorper, &flow, solution);\n    CHECK(converged(status));\n\n    const auto box = db::create<\n        db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n        db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n        strahlkorper);\n    const auto& rad = get(db::get<ylm::Tags::Radius<Frame::Inertial>>(box));\n    const auto r_minmax = std::minmax_element(rad.begin(), rad.end());\n    Approx custom_approx = Approx::custom().epsilon(1.e-11);\n    CHECK(*r_minmax.first == custom_approx(2.0));\n    CHECK(*r_minmax.second == custom_approx(2.0));\n  };\n\n  iterate_and_check();\n\n  // We have found the horizon once.  Now perturb the strahlkorper\n  // and find the horizon again. This checks that fastflow is reset\n  // correctly.\n  strahlkorper = [](const ylm::Strahlkorper<Frame::Inertial>& strahlkorper_l) {\n    MAKE_GENERATOR(generator);\n    std::uniform_real_distribution<> dist(0.0, 0.1);\n    auto coefs = strahlkorper_l.coefficients();\n    for (auto coef_iter = ylm::SpherepackIterator(strahlkorper_l.l_max(),\n                                                  strahlkorper_l.l_max());\n         coef_iter; ++coef_iter) {\n      // Change all components randomly, but make smaller changes\n      // to higher-order coefficients.\n      coefs[coef_iter()] *= 1.0 + dist(generator) / square(coef_iter.l() + 1.0);\n    }\n    return ylm::Strahlkorper<Frame::Inertial>(coefs, strahlkorper_l);\n  }(strahlkorper);\n\n  flow.reset_for_next_find();\n  iterate_and_check();\n}\n\nvoid test_kerr(FastFlow::Flow::type type_of_flow, const double mass,\n               const size_t max_iterations) {\n  ylm::Strahlkorper<Frame::Inertial> strahlkorper(8, 8, 2.0 * mass,\n                                                  {{0, 0, 0}});\n  FastFlow flow(type_of_flow, 1.0, 0.5, 1e-12, 1e-2, 1.2, 5, max_iterations);\n\n  const std::array<double, 3> spin = {{0.1, 0.2, 0.3}};\n  const gr::Solutions::KerrSchild solution(mass, spin, {{0., 0., 0.}});\n\n  const auto status = do_iteration(&strahlkorper, &flow, solution);\n  CHECK(converged(status));\n\n  const double spin_magnitude =\n      sqrt(square(spin[0]) + square(spin[1]) + square(spin[2]));\n  // Once we switch to c++17, use std::hypot(spin[0],spin[1],spin[2]);\n\n  // minimal radius of kerr horizon is M + sqrt(M^2-a^2) (obtained in\n  // direction parallel to Spin)\n  const double r_min_pt = strahlkorper.radius(acos(spin[2] / spin_magnitude),\n                                              atan2(spin[1], spin[0]));\n  const double r_min_val = mass * (1.0 + sqrt(1.0 - square(spin_magnitude)));\n\n  const std::array<double, 3> vector_normal_to_spin = {{0.0, -0.3, 0.2}};\n  const double vector_magnitude =\n      sqrt(square(vector_normal_to_spin[0]) + square(vector_normal_to_spin[1]) +\n           square(vector_normal_to_spin[2]));\n  // Once we switch to c++17, use std::hypot\n\n  // maximal radius of kerr horizon is sqrt(2M^2 + 2M sqrt(M^2-a^2))\n  // (obtained in direction orthogonal to spin)\n  const double r_max_pt = strahlkorper.radius(\n      acos(vector_normal_to_spin[2] / vector_magnitude),\n      atan2(vector_normal_to_spin[1], vector_normal_to_spin[0]));\n  const double r_max_val =\n      mass * sqrt(2.0 + 2.0 * sqrt(1.0 - square(spin_magnitude)));\n\n  Approx custom_approx = Approx::custom().epsilon(1.e-10).scale(1.);\n  CHECK(r_min_pt == custom_approx(r_min_val));\n  CHECK(r_max_pt == custom_approx(r_max_val));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.FastFlowSchwarzschild\",\n                  \"[Utilities][Unit]\") {\n  test_schwarzschild(FastFlow::FlowType::Fast, 100);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.JacobiSchwarzschild\",\n                  \"[Utilities][Unit]\") {\n  test_schwarzschild(FastFlow::FlowType::Jacobi, 200);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.CurvatureSchwarzschild\",\n                  \"[Utilities][Unit]\") {\n  test_schwarzschild(FastFlow::FlowType::Curvature, 200);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.FastFlowKerr\",\n                  \"[Utilities][Unit]\") {\n  test_kerr(FastFlow::FlowType::Fast, 2.0, 100);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.JacobiKerr\",\n                  \"[Utilities][Unit]\") {\n  // Keep mass at 1.0 so test doesn't timeout.\n  test_kerr(FastFlow::FlowType::Jacobi, 1.0, 200);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.CurvatureKerr\",\n                  \"[Utilities][Unit]\") {\n  // Keep mass at 1.0 so test doesn't timeout.\n  test_kerr(FastFlow::FlowType::Curvature, 1.0, 200);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.FastFlowMisc\",\n                  \"[Utilities][Unit]\") {\n  test_negative_radius_error();\n  test_too_many_iterations_error();\n  test_construct_from_options_fast();\n  test_construct_from_options_jacobi();\n  test_construct_from_options_curvature();\n  test_copy_and_move();\n  test_serialize();\n  test_ostream();\n\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<FastFlow>(\"Flow: Fast\\n\"\n                                           \"Alpha: 1.1\\n\"\n                                           \"Beta: 0.6\\n\"\n                                           \"AbsTol: 1.e-10\\n\"\n                                           \"TruncationTol: 1.e-3\\n\"\n                                           \"DivergenceTol: 0.5\\n\"\n                                           \"DivergenceIter: 6\\n\"\n                                           \"MaxIts: 200\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value 0.5 is below the lower bound of 1.\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<FastFlow>(\"Flow: Crud\\n\"\n                                           \"Alpha: 1.1\\n\"\n                                           \"Beta: 0.6\\n\"\n                                           \"AbsTol: 1.e-10\\n\"\n                                           \"TruncationTol: 1.e-3\\n\"\n                                           \"DivergenceTol: 1.1\\n\"\n                                           \"DivergenceIter: 6\\n\"\n                                           \"MaxIts: 200\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Failed to convert \\\"Crud\\\" to FastFlow::FlowType\"));\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_FindApparentHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependentOptions/ShapeMap.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Structure/ObjectLabel.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Callbacks/FailedHorizonFind.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeVarsToInterpolateToTarget.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Residual.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Shape.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FindApparentHorizon.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Initialization.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/Callback.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nsize_t callback_failure_count = 0;                                  // NOLINT\nFastFlow::Status callback_failure_mode = FastFlow::Status::AbsTol;  // NOLINT\ntemplate <typename HorizonMetavars, size_t Index>\nstruct TestHorizonFindFailureCallback\n    : tt::ConformsTo<ah::protocols::Callback> {\n private:\n  using Fr = typename HorizonMetavars::frame;\n\n public:\n  template <typename DbTags, typename Metavariables>\n  static void apply(db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& cache,\n                    const FastFlow::Status failure_reason) {\n    // Ignore error so we just increment the counter\n    ah::callbacks::FailedHorizonFind<HorizonMetavars, true>::apply(\n        box, cache, failure_reason);\n    ++callback_failure_count;\n    callback_failure_mode = failure_reason;\n  }\n};\n\nsize_t callback_count = 0;                   // NOLINT\nstd::vector<size_t> ah_found_resolutions{};  // NOLINT\ntemplate <typename HorizonMetavars, size_t Index>\nstruct TestHorizonFindCallback : tt::ConformsTo<ah::protocols::Callback> {\n private:\n  using Fr = typename HorizonMetavars::frame;\n\n public:\n  template <typename DbTags, typename Metavariables>\n  static void apply(const db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const FastFlow::Status /*status*/) {\n    ++callback_count;\n\n    const auto& strahlkorper = get<ylm::Tags::Strahlkorper<Fr>>(box);\n    // Test that InverseSpatialMetric can be retrieved from the\n    // DataBox and that its number of grid points is the same\n    // as that of the strahlkorper.\n    const auto& inv_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3, Fr>>(box);\n    CHECK(strahlkorper.ylm_spherepack().physical_size() ==\n          get<0, 0>(inv_metric).size());\n\n    const auto& current_resolution_l = strahlkorper.l_max();\n    ah_found_resolutions.push_back(current_resolution_l);\n  }\n};\n\ntemplate <typename Fr, ah::Destination Dest>\nstruct HorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using time_tag = ::Tags::TimeAndPrevious<0>;\n  using frame = Fr;\n\n  using horizon_find_callbacks =\n      tmpl::list<TestHorizonFindCallback<HorizonMetavars, 0>,\n                 TestHorizonFindCallback<HorizonMetavars, 1>>;\n  using horizon_find_failure_callbacks =\n      tmpl::list<TestHorizonFindFailureCallback<HorizonMetavars, 0>,\n                 TestHorizonFindFailureCallback<HorizonMetavars, 1>>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = Dest;\n\n  static std::string name() { return \"TestingHorizonMetavars\"; }\n};\n\ntemplate <typename Metavariables, typename HorizonMetavars>\nstruct MockComponent {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using component_being_mocked = ah::Component<Metavariables, HorizonMetavars>;\n\n  using frame = typename HorizonMetavars::frame;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<Initialization::Actions::InitializeItems<\n          ah::Initialize<HorizonMetavars>>>>>;\n};\n\ntemplate <typename Fr, ah::Destination Dest>\nstruct MockMetavariables {\n  using component_list =\n      tmpl::list<MockComponent<MockMetavariables, HorizonMetavars<Fr, Dest>>>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<3>,\n                 ah::Tags::ApparentHorizonOptions<HorizonMetavars<Fr, Dest>>,\n                 ah::Tags::LMax, ah::Tags::BlocksForHorizonFind>;\n  using mutable_global_cache_tags =\n      tmpl::list<domain::Tags::FunctionsOfTimeInitialize>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<ah::Criterion, ah::Criteria::standard_criteria>>;\n  };\n};\n\ntemplate <typename Callbacks>\nstruct check_callbacks;\n\ntemplate <typename... Callbacks>\nstruct check_callbacks<tmpl::list<Callbacks...>>\n    : std::integral_constant<bool,\n                             tmpl2::flat_all_v<tt::assert_conforms_to_v<\n                                 Callbacks, ah::protocols::Callback>...>> {};\n\ntemplate <typename Callbacks>\nconstexpr bool check_callbacks_v = check_callbacks<Callbacks>::value;\n\ntemplate <typename Fr, ah::Destination Dest = ah::Destination::ControlSystem,\n          bool MakeHorizonFinderFailOnPurpose = false>\nvoid test_apparent_horizon(\n    const size_t l_max, const size_t grid_points_each_dimension,\n    const double mass, const std::array<double, 3>& dimensionless_spin,\n    const bool is_time_dependent,\n    const std::optional<std::string>& dependency = std::nullopt,\n    const size_t max_its = 100_st,\n    std::vector<std::unique_ptr<ah::Criterion>> criteria = {}) {\n  using metavars = MockMetavariables<Fr, Dest>;\n  using horizon_metavars = HorizonMetavars<Fr, Dest>;\n  using component = MockComponent<metavars, horizon_metavars>;\n\n  // Assert the protocols\n  static_assert(tt::assert_conforms_to_v<horizon_metavars,\n                                         ah::protocols::HorizonMetavars>);\n  static_assert(\n      check_callbacks_v<typename horizon_metavars::horizon_find_callbacks>);\n  static_assert(check_callbacks_v<\n                typename horizon_metavars::horizon_find_failure_callbacks>);\n\n  // The initial guess for the horizon search is a sphere of radius 2.8M unless\n  // we are in the Distorted frame, then we pick 2.2 so it's within the blocks\n  // that actually have a distorted frame\n  ah::HorizonOptions<Fr> apparent_horizon_opts(\n      std::move(criteria),\n      ylm::Strahlkorper<Fr>{l_max,\n                            std::is_same_v<Fr, ::Frame::Distorted> ? 2.2 : 2.8,\n                            {{0.0, 0.0, 0.0}}},\n      FastFlow{FastFlow::FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5,\n               max_its},\n      Verbosity::Verbose, 3_st, std::nullopt);\n\n  std::unordered_map<std::string, std::unordered_set<std::string>>\n      blocks_for_interpolation{};\n\n  // The test finds an apparent horizon for a Schwarzschild or Kerr metric with\n  // M=1.  We choose a spherical shell domain extending from radius 1.9M to\n  // 2.9M; this ensures the horizon is inside the domain, and it gives a narrow\n  // domain so that we don't need a large number of grid points to resolve the\n  // horizon (which would make the test slower).\n  std::unique_ptr<DomainCreator<3>> domain_creator = std::make_unique<\n      domain::creators::Sphere>(\n      1.9, 2.9, domain::creators::Sphere::Excision{nullptr}, 1_st,\n      grid_points_each_dimension, true, std::nullopt, std::vector<double>{2.4},\n      domain::CoordinateMaps::Distribution::Linear, ShellWedges::All,\n      is_time_dependent\n          ? std::optional{domain::creators::sphere::TimeDependentMapOptions{\n                0.0,\n                domain::creators::time_dependent_options::ShapeMapOptions<\n                    false, ::domain::ObjectLabel::None>{\n                    l_max,\n                    domain::creators::time_dependent_options::\n                        KerrSchildFromBoyerLindquist{mass, dimensionless_spin}},\n                std::nullopt, std::nullopt,\n                domain::creators::time_dependent_options::TranslationMapOptions<\n                    3>{std::array{std::array{0.0, 0.0, 0.0},\n                                  std::array{0.0, 0.01, 0.0},\n                                  std::array{0.0, 0.0, 0.0}}},\n                true}}\n          : std::nullopt);\n\n  {\n    const Domain<3> domain_for_block_selection =\n        domain_creator->create_domain();\n    std::unordered_set<std::string> blocks_to_use{};\n    for (const auto& block : domain_for_block_selection.blocks()) {\n      if constexpr (std::is_same_v<Fr, ::Frame::Distorted>) {\n        if (not block.has_distorted_frame()) {\n          continue;\n        }\n      }\n      blocks_to_use.insert(block.name());\n    }\n    blocks_for_interpolation.emplace(\"TestingHorizonMetavars\",\n                                     std::move(blocks_to_use));\n  }\n\n  const size_t max_resolution_and_output_l =\n      std::max(l_max, static_cast<size_t>(12));\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {domain_creator->create_domain(), std::move(apparent_horizon_opts),\n       max_resolution_and_output_l, blocks_for_interpolation},\n      {domain_creator->functions_of_time(),\n       ah::Storage::LockedPreviousSurface<Fr>{}}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_array_component<component>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{0}, 0_st);\n  ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Register);\n\n  // Find horizon at three times.  The horizon find at the second time will use\n  // the result from the first time as an initial guess.  The horizon find at\n  // the third time will use an initial guess that was linearly extrapolated\n  // from the first two horizon finds. For the time-independent case, the volume\n  // data will not change between horizon finds, so the second and third horizon\n  // finds will take zero iterations.  Having three times tests some logic in\n  // the interpolator.\n  const std::vector<LinkedMessageId<double>> times{\n      {12.0 / 15.0, std::nullopt},\n      {13.0 / 15.0, {12.0 / 15.0}},\n      {14.0 / 15.0, {13.0 / 15.0}}};\n\n  // Create element_ids.\n  std::vector<ElementId<3>> element_ids{};\n  const Domain<3> domain = domain_creator->create_domain();\n  const auto& blocks_to_use =\n      blocks_for_interpolation.at(\"TestingHorizonMetavars\");\n  const domain::FunctionsOfTimeMap functions_of_time =\n      domain_creator->functions_of_time();\n  for (const auto& block : domain.blocks()) {\n    const auto initial_ref_levs =\n        domain_creator->initial_refinement_levels()[block.id()];\n    auto elem_ids = initial_element_ids(block.id(), initial_ref_levs);\n    element_ids.insert(element_ids.end(), elem_ids.begin(), elem_ids.end());\n  }\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Center of the analytic solution.\n  const auto analytic_solution_center = []() -> std::array<double, 3> {\n    if constexpr (MakeHorizonFinderFailOnPurpose) {\n      // Make the analytic solution off-center on purpose, so that the domain\n      // only partially contains the horizon and therefore the interpolation\n      // fails.\n      return {0.5, 0.0, 0.0};\n    }\n    return {0.0, 0.0, 0.0};\n  }();\n\n  // Create volume data and send it to the interpolator, for each time.\n  for (const auto& time : times) {\n    for (const auto& element_id : element_ids) {\n      const auto& block = domain.blocks()[element_id.block_id()];\n      // Only send volume data for blocks in blocks_to_use\n      if (blocks_to_use.find(block.name()) == blocks_to_use.end()) {\n        continue;\n      }\n      const ::Mesh<3> mesh{\n          domain_creator->initial_extents()[element_id.block_id()],\n          Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto};\n\n      tnsr::I<DataVector, 3, ::Frame::Inertial> inertial_mesh_coords{};\n      InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n          inv_jacobian_logical_to_inertial{mesh.number_of_grid_points(), 0.0};\n      const auto logical_coords = logical_coordinates(mesh);\n      if (domain.is_time_dependent()) {\n        const ElementMap<3, Frame::Grid> map_logical_to_grid{\n            element_id, block.moving_mesh_logical_to_grid_map().get_clone()};\n\n        inertial_mesh_coords = block.moving_mesh_grid_to_inertial_map()(\n            map_logical_to_grid(logical_coords), time.id, functions_of_time);\n\n        const auto inv_jacobian_logical_to_grid =\n            map_logical_to_grid.inv_jacobian(logical_coords);\n        const auto inv_jacobian_grid_to_inertial =\n            block.moving_mesh_grid_to_inertial_map().inv_jacobian(\n                map_logical_to_grid(logical_coords), time.id,\n                functions_of_time);\n\n        inv_jacobian_logical_to_inertial = tenex::evaluate<ti::I, ti::j>(\n            inv_jacobian_logical_to_grid(ti::I, ti::k) *\n            inv_jacobian_grid_to_inertial(ti::K, ti::j));\n      } else {\n        // Accounts for Grid vs Inertial\n        const ElementMap<3, ::Frame::Inertial> map{\n            element_id, block.stationary_map().get_clone()};\n        inertial_mesh_coords = map(logical_coords);\n\n        inv_jacobian_logical_to_inertial = map.inv_jacobian(logical_coords);\n      }\n\n      // Compute g, pi, phi for KerrSchild.\n      // Horizon is always at 0,0,0 in analytic_solution_coordinates.\n      const gr::Solutions::KerrSchild solution(mass, dimensionless_spin,\n                                               analytic_solution_center);\n      const auto solution_vars = solution.variables(\n          inertial_mesh_coords, 0.0,\n          typename gr::Solutions::KerrSchild::tags<DataVector,\n                                                   ::Frame::Inertial>{});\n\n      // Fill output variables with solution.\n      Variables<ah::source_vars<3>> source_vars(mesh.number_of_grid_points());\n\n      const auto& lapse = get<gr::Tags::Lapse<DataVector>>(solution_vars);\n      const auto& dt_lapse =\n          get<Tags::dt<gr::Tags::Lapse<DataVector>>>(solution_vars);\n      const auto& d_lapse = get<typename gr::Solutions::KerrSchild ::DerivLapse<\n          DataVector, ::Frame::Inertial>>(solution_vars);\n      const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(solution_vars);\n      const auto& d_shift = get<typename gr::Solutions::KerrSchild ::DerivShift<\n          DataVector, ::Frame::Inertial>>(solution_vars);\n      const auto& dt_shift =\n          get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(solution_vars);\n      const auto& g =\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(solution_vars);\n      const auto& dt_g =\n          get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(solution_vars);\n      const auto& d_g =\n          get<typename gr::Solutions::KerrSchild ::DerivSpatialMetric<\n              DataVector, ::Frame::Inertial>>(solution_vars);\n\n      get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(source_vars) =\n          gr::spacetime_metric(lapse, shift, g);\n      get<::gh::Tags::Phi<DataVector, 3>>(source_vars) =\n          gh::phi(lapse, d_lapse, shift, d_shift, g, d_g);\n      get<::gh::Tags::Pi<DataVector, 3>>(source_vars) =\n          gh::pi(lapse, dt_lapse, shift, dt_shift, g, dt_g,\n                 get<::gh::Tags::Phi<DataVector, 3>>(source_vars));\n\n      // Need to compute numerical deriv of Phi.\n      get<Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(source_vars) =\n          partial_derivative(get<::gh::Tags::Phi<DataVector, 3>>(source_vars),\n                             mesh, inv_jacobian_logical_to_inertial);\n\n      // TO-DO: make target_vars from the source_vars in the correct frame\n      Variables<ah::vars_to_interpolate_to_target<3, Fr>> target_vars{\n          get(lapse).size()};\n      ah::compute_vars_to_interpolate_to_target(\n          make_not_null(&target_vars),\n          get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(source_vars),\n          get<::gh::Tags::Pi<DataVector, 3>>(source_vars),\n          get<::gh::Tags::Phi<DataVector, 3>>(source_vars),\n          get<Tags::deriv<::gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                          Frame::Inertial>>(source_vars),\n          time, domain, mesh, element_id, functions_of_time);\n\n      // Queue the action so we can invoke in a random order below\n      ActionTesting::queue_simple_action<\n          component, ah::FindApparentHorizon<horizon_metavars>>(\n          make_not_null(&runner), 0, time, element_id, mesh, target_vars,\n          dependency);\n    }\n  }\n\n  // Invoke remaining actions in random order.\n  MAKE_GENERATOR(generator);\n  auto array_indices_with_queued_simple_actions =\n      ActionTesting::array_indices_with_queued_simple_actions<\n          typename metavars::component_list>(make_not_null(&runner));\n  while (ActionTesting::number_of_elements_with_queued_simple_actions<\n             typename metavars::component_list>(\n             array_indices_with_queued_simple_actions) > 0) {\n    ActionTesting::invoke_random_queued_simple_action<\n        typename metavars::component_list>(\n        make_not_null(&runner), make_not_null(&generator),\n        array_indices_with_queued_simple_actions);\n    array_indices_with_queued_simple_actions =\n        ActionTesting::array_indices_with_queued_simple_actions<\n            typename metavars::component_list>(make_not_null(&runner));\n  }\n}\n\n// [[TimeOut, 60]]\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.FindApparentHorizon\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  domain::creators::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  register_factory_classes_with_charm<\n      MockMetavariables<Frame::Inertial, ah::Destination::ControlSystem>>();\n\n  const std::optional<std::string> dependency{\"FakeDependency\"};\n\n  // Time-independent tests.\n  callback_failure_count = 0;\n  callback_count = 0;\n  test_apparent_horizon<Frame::Inertial>(3, 3, 1.0, {{0.0, 0.0, 0.0}}, false,\n                                         dependency);\n  test_apparent_horizon<Frame::Grid>(3, 4, 1.1, {{0.2, 0.1, -0.4}}, false);\n  test_apparent_horizon<Frame::Inertial, ah::Destination::Observation>(\n      3, 4, 1.1, {{0.2, 0.1, -0.4}}, false);\n  CHECK(callback_count == 18);\n  CHECK(callback_failure_count == 0);\n  CHECK(ah_found_resolutions == std::vector<size_t>(18, 3));\n  callback_count = 0;\n  ah_found_resolutions.clear();\n\n  // Time-dependent tests.\n  test_apparent_horizon<Frame::Inertial>(3, 5, 1.1, {{0.2, 0.1, -0.4}}, true);\n  test_apparent_horizon<Frame::Distorted>(3, 4, 1.1, {{0.2, 0.1, -0.4}}, true,\n                                          dependency);\n  test_apparent_horizon<Frame::Distorted, ah::Destination::Observation>(\n      3, 4, 1.1, {{0.2, 0.1, -0.4}}, true);\n  test_apparent_horizon<Frame::Grid>(3, 3, 1.0, {{0.0, 0.0, 0.0}}, true);\n  CHECK(callback_count == 24);\n  CHECK(callback_failure_count == 0);\n  CHECK(ah_found_resolutions == std::vector<size_t>(24, 3));\n  callback_count = 0;\n  ah_found_resolutions.clear();\n\n  // Adaptivity tests\n  // First, choose strict critera so the resolution increases\n  // by one each time.\n  const ah::Criteria::Residual residual_criterion{1.e-7, 9.e-5, 4};\n  const ah::Criteria::Shape shape_criterion{1.e-7, 9.e-5, 20, 4};\n  std::vector<std::unique_ptr<ah::Criterion>> criteria{};\n  criteria.emplace_back(\n      std::make_unique<ah::Criteria::Residual>(residual_criterion));\n  criteria.emplace_back(std::make_unique<ah::Criteria::Shape>(shape_criterion));\n\n  test_apparent_horizon<Frame::Inertial>(3, 3, 1.0, {{0.2, 0.2, 0.2}}, false,\n                                         dependency, 100_st,\n                                         std::move(criteria));\n  CHECK(callback_count == 6);\n  CHECK(callback_failure_count == 0);\n  CHECK(ah_found_resolutions == std::vector<size_t>(6, 8));\n\n  callback_count = 0;\n  ah_found_resolutions.clear();\n\n  // Second, choose loose critera so the resolution increases\n  // by one each time\n  const ah::Criteria::Residual residual_criterion_loose{1.0e8, 1.0e12, 4};\n  const ah::Criteria::Shape shape_criterion_loose{1.0e8, 1.0e12, 20, 4};\n  criteria.clear();\n  criteria.emplace_back(\n      std::make_unique<ah::Criteria::Residual>(residual_criterion_loose));\n  criteria.emplace_back(\n      std::make_unique<ah::Criteria::Shape>(shape_criterion_loose));\n\n  test_apparent_horizon<Frame::Inertial>(8, 8, 1.0, {{0.0, 0.0, 0.0}}, false,\n                                         dependency, 100_st,\n                                         std::move(criteria));\n  CHECK(callback_count == 6);\n  CHECK(callback_failure_count == 0);\n  CHECK(ah_found_resolutions == std::vector<size_t>{8, 8, 7, 7, 6, 6});\n  callback_count = 0;\n  ah_found_resolutions.clear();\n\n  // Failure tests\n  test_apparent_horizon<Frame::Inertial, ah::Destination::ControlSystem, true>(\n      3, 3, 1.0, {{0.0, 0.0, 0.0}}, true);\n  CHECK(callback_count == 0);\n  CHECK(callback_failure_count == 6);\n  CHECK(callback_failure_mode == FastFlow::Status::InterpolationFailure);\n  callback_failure_count = 0;\n\n  test_apparent_horizon<Frame::Grid, ah::Destination::ControlSystem, true>(\n      3, 3, 10.0, {{0.0, 0.0, 0.0}}, false);\n  CHECK(callback_count == 0);\n  CHECK(callback_failure_count == 6);\n  CHECK(callback_failure_mode == FastFlow::Status::InterpolationFailure);\n  callback_failure_count = 0;\n\n  test_apparent_horizon<Frame::Inertial, ah::Destination::ControlSystem, true>(\n      3, 3, 1.0, {{0.0, 0.0, 0.0}}, true, dependency, 1);\n  CHECK(callback_count == 0);\n  CHECK(callback_failure_count == 6);\n  CHECK(callback_failure_mode == FastFlow::Status::MaxIts);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_Initialization.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Initialization.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct MockHorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using time_tag = ::Tags::TimeAndPrevious<0>;\n\n  using frame = ::Frame::Grid;\n\n  // Don't need callbacks\n  using horizon_find_callbacks = tmpl::list<>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::ControlSystem;\n\n  static std::string name() { return \"MockHorizonMetavars\"; }\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Initialization\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  (void)MockHorizonMetavars::destination;\n\n  const FastFlow expected_fast_flow{\n      FastFlow::FlowType::Fast, 1.0, 0.5, 1.e-12, 1.e-2, 1.2, 5, 100};\n  const ah::HorizonOptions<Frame::Grid> horizon_options{\n      std::vector<std::unique_ptr<ah::Criterion>>{},\n      ylm::Strahlkorper<Frame::Grid>{4, 2.0, std::array{0.0, 0.0, 0.0}},\n      expected_fast_flow,\n      ::Verbosity::Debug,\n      3,\n      std::nullopt};\n\n  std::optional<size_t> current_resolution_l{4};\n  ::Verbosity verbosity{};\n  std::optional<LinkedMessageId<double>> current_time =\n      LinkedMessageId<double>{1.0, std::nullopt};\n  FastFlow fast_flow{};\n\n  ah::Initialize<MockHorizonMetavars>::apply(\n      make_not_null(&current_resolution_l), make_not_null(&verbosity),\n      make_not_null(&fast_flow), make_not_null(&current_time), horizon_options);\n\n  CHECK(verbosity == ::Verbosity::Debug);\n  CHECK_FALSE(current_time.has_value());\n  CHECK(fast_flow == expected_fast_flow);\n  CHECK_FALSE(current_resolution_l.has_value());\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_InterpolateVolumeVars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <unordered_map>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/DomainHelpers.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/ComputeVarsToInterpolateToTarget.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/InterpolateVolumeVars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nVariables<ah::source_vars<3>> compute_source_vars(\n    const gr::Solutions::KerrSchild& solution,\n    const LinkedMessageId<double>& time, const ElementId<3>& element_id,\n    const Block<3>& block, const Mesh<3>& mesh) {\n  const auto logical_coords = logical_coordinates(mesh);\n  const ElementMap<3, Frame::Inertial> map_logical_to_inertial{\n      element_id, block.stationary_map().get_clone()};\n  const auto inertial_coords = map_logical_to_inertial(logical_coords);\n\n  const InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inv_jacobian_logical_to_inertial =\n          map_logical_to_inertial.inv_jacobian(logical_coords);\n\n  const auto solution_vars = solution.variables(\n      inertial_coords, time.id,\n      typename gr::Solutions::KerrSchild::tags<DataVector, Frame::Inertial>{});\n\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(solution_vars);\n  const auto& dt_lapse =\n      get<Tags::dt<gr::Tags::Lapse<DataVector>>>(solution_vars);\n  const auto& d_lapse =\n      get<typename gr::Solutions::KerrSchild::DerivLapse<DataVector>>(\n          solution_vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3>>(solution_vars);\n  const auto& d_shift =\n      get<typename gr::Solutions::KerrSchild::DerivShift<DataVector>>(\n          solution_vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataVector, 3>>>(solution_vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(solution_vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(solution_vars);\n  const auto& d_spatial_metric =\n      get<typename gr::Solutions::KerrSchild::DerivSpatialMetric<DataVector>>(\n          solution_vars);\n\n  Variables<ah::source_vars<3>> result{get(lapse).size()};\n  get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(result) =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n  get<::gh::Tags::Phi<DataVector, 3>>(result) =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  get<::gh::Tags::Pi<DataVector, 3>>(result) =\n      gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n             dt_spatial_metric, get<::gh::Tags::Phi<DataVector, 3>>(result));\n\n  // Need to compute numerical deriv of Phi.\n  get<Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                  Frame::Inertial>>(result) =\n      partial_derivative(get<::gh::Tags::Phi<DataVector, 3>>(result), mesh,\n                         inv_jacobian_logical_to_inertial);\n\n  return result;\n}\n\nvoid test_interpolate_volume_vars() {\n  const size_t number_of_grid_points = 8;\n\n  const double mass = 1.0;\n  const std::array spin{0.1, 0.2, 0.3};\n  const std::array center{-0.01, -0.02, -0.03};\n  const LinkedMessageId<double> time{0.01, std::nullopt};\n  const domain::creators::Sphere domain_creator{\n      0.7,\n      6.0,\n      domain::creators::Sphere::Excision{nullptr},\n      0_st,\n      number_of_grid_points,\n      true};\n  const auto domain = domain_creator.create_domain();\n  const auto& functions_of_time = domain_creator.functions_of_time();\n\n  const auto& blocks = domain.blocks();\n  const auto element_ids = [&]() {\n    std::vector<ElementId<3>> result{};\n    for (const auto& block : blocks) {\n      const auto temp_element_ids = initial_element_ids(\n          block.id(), domain_creator.initial_refinement_levels()[block.id()]);\n      result.insert(result.end(), temp_element_ids.begin(),\n                    temp_element_ids.end());\n    }\n    return result;\n  }();\n\n  // For a quick test\n  const size_t l_max = 6;\n  const double radius = 2.0;\n\n  ah::Storage::Iteration<Frame::Inertial> current_iteration{};\n  current_iteration.strahlkorper =\n      ylm::Strahlkorper<Frame::Inertial>{l_max, radius, center};\n  const auto surface_coords =\n      ylm::cartesian_coords(current_iteration.strahlkorper);\n  current_iteration.block_coord_holders = ::block_logical_coordinates(\n      domain, surface_coords, time.id, functions_of_time);\n  const size_t expected_num_points =\n      current_iteration.block_coord_holders->size();\n\n  const gr::Solutions::KerrSchild solution(mass, spin, {0.0, 0.0, 0.0});\n  const auto solution_vars = solution.variables(\n      surface_coords, time.id,\n      typename gr::Solutions::KerrSchild::tags<DataVector, Frame::Inertial>{});\n\n  std::unordered_map<ElementId<3>,\n                     ah::Storage::VolumeVariables<Frame::Inertial>>\n      all_volume_variables{};\n\n  size_t num_previous_indices_interpolated_to = 0;\n  for (const auto& element_id : element_ids) {\n    const Mesh mesh{domain_creator.initial_extents()[element_id.block_id()],\n                    Spectral::Basis::Legendre,\n                    Spectral::Quadrature::GaussLobatto};\n\n    const auto source_vars = compute_source_vars(\n        solution, time, element_id, blocks[element_id.block_id()], mesh);\n\n    auto& volume_vars = all_volume_variables[element_id];\n    volume_vars.mesh = mesh;\n    ah::compute_vars_to_interpolate_to_target(\n        make_not_null(&volume_vars.vars_to_interpolate_to_target),\n        get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(source_vars),\n        get<::gh::Tags::Pi<DataVector, 3>>(source_vars),\n        get<::gh::Tags::Phi<DataVector, 3>>(source_vars),\n        get<Tags::deriv<::gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(source_vars),\n        time, domain, mesh, element_id, functions_of_time);\n\n    const bool interpolated_any_points = ah::interpolate_volume_data(\n        make_not_null(&current_iteration), volume_vars, element_id);\n\n    CHECK(current_iteration.intersecting_element_ids.contains(element_id) ==\n          interpolated_any_points);\n\n    // Check that we finished interpolation and that the points we interpolated\n    // to aren't the default fill value\n    // We could in theory figure out which points are in which element for a\n    // given l_max, but that's quite tedious and we don't need such a stringent\n    // test\n    const auto num_indices_interpolated_to = static_cast<size_t>(\n        alg::count_if(current_iteration.indices_interpolated_to_thus_far,\n                      [](const bool filled) { return filled; }));\n    CHECK(num_indices_interpolated_to > num_previous_indices_interpolated_to);\n    num_previous_indices_interpolated_to = num_indices_interpolated_to;\n    tmpl::for_each<ah::vars_to_interpolate_to_target<3, Frame::Inertial>>(\n        [&]<typename Tag>(tmpl::type_<Tag>) {\n          auto& interpolated_var =\n              get<Tag>(current_iteration.interpolated_vars);\n          for (size_t j = 0; j < interpolated_var.size(); j++) {\n            for (size_t index = 0; index < expected_num_points; index++) {\n              if (current_iteration.indices_interpolated_to_thus_far[index]) {\n                CHECK(interpolated_var[j][index] !=\n                      std::numeric_limits<double>::max());\n              }\n            }\n          }\n        });\n  }\n  CHECK(current_iteration.interpolation_is_complete());\n\n  const auto check_no_max = [&]() {\n    tmpl::for_each<ah::vars_to_interpolate_to_target<3, Frame::Inertial>>(\n        [&]<typename Tag>(tmpl::type_<Tag>) {\n          auto& interpolated_var =\n              get<Tag>(current_iteration.interpolated_vars);\n          for (size_t j = 0; j < interpolated_var.size(); j++) {\n            CHECK(alg::none_of(interpolated_var[j], [](const double value) {\n              return value == std::numeric_limits<double>::max();\n            }));\n          }\n        });\n  };\n\n  // Check all points have been interpolated to\n  check_no_max();\n}\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.InterpolateVolumeVars\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  // We don't need time dependent maps for this test. The actual time dependent\n  // parts are tested elsewhere\n  test_interpolate_volume_vars();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_OptionTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"Options/Auto.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Component.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Factory.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/IncreaseResolution.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Criteria/Residual.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/OptionTags.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct MockHorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using time_tag = ::Tags::TimeAndPrevious<0>;\n\n  using frame = ::Frame::Grid;\n\n  // Don't need callbacks\n  using horizon_find_callbacks = tmpl::list<>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::ControlSystem;\n\n  static std::string name() { return \"MockHorizonMetavars\"; }\n};\n\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = 3;\n\n  using component_list =\n      tmpl::list<ah::Component<MockMetavariables, MockHorizonMetavars>>;\n};\n\nstruct TestCreationMetavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<ah::Criterion, ah::Criteria::standard_criteria>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.OptionTags\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  (void)MockHorizonMetavars::destination;\n  domain::creators::register_derived_with_charm();\n\n  // Constants used in this test.\n  const size_t l_max = 12;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.05, 0.06, 0.07}};\n\n  // Options for ApparentHorizon\n  std::vector<std::unique_ptr<ah::Criterion>> criteria;\n  criteria.emplace_back(\n      std::make_unique<ah::Criteria::Residual>(1.e-12, 1.e-2, 2));\n  ah::HorizonOptions<::Frame::Grid> apparent_horizon_opts(\n      std::move(criteria),\n      ylm::Strahlkorper<Frame::Grid>{l_max, radius, center}, FastFlow{},\n      Verbosity::Verbose, 3_st, std::nullopt);\n\n  // Test creation of options\n  const auto created_opts =\n      TestHelpers::test_creation<ah::HorizonOptions<Frame::Grid>,\n                                 TestCreationMetavariables>(\n          \"Criteria:\\n\"\n          \"  - Residual:\\n\"\n          \"      MinResidual: 1.e-12\\n\"\n          \"      MaxResidual: 1.e-2\\n\"\n          \"      MinResolutionL: 2\\n\"\n          \"FastFlow:\\n\"\n          \"  Flow: Fast\\n\"\n          \"  Alpha: 1.0\\n\"\n          \"  Beta: 0.5\\n\"\n          \"  AbsTol: 1e-12\\n\"\n          \"  TruncationTol: 1e-2\\n\"\n          \"  DivergenceTol: 1.2\\n\"\n          \"  DivergenceIter: 5\\n\"\n          \"  MaxIts: 100\\n\"\n          \"Verbosity: Verbose\\n\"\n          \"InitialGuess:\\n\"\n          \"  Center: [0.05, 0.06, 0.07]\\n\"\n          \"  Radius: 2.0\\n\"\n          \"  LMax: 12\\n\"\n          \"MaxComputeCoordsRetries: 3\\n\"\n          \"BlocksForHorizonFind: All\");\n  CHECK(created_opts == apparent_horizon_opts);\n\n  const auto domain_creator = domain::creators::Sphere(\n      1.8, 2.2, domain::creators::Sphere::Excision{}, 1_st, 5_st, false);\n\n  {\n    const auto blocks_for_horizon_find =\n        ah::Tags::BlocksForHorizonFind::create_from_options<MockMetavariables>(\n            std::make_unique<domain::creators::Sphere>(\n                1.8, 2.2, domain::creators::Sphere::Excision{}, 1_st, 5_st,\n                false),\n            created_opts);\n    REQUIRE(blocks_for_horizon_find.contains(\"MockHorizonMetavars\"));\n    const auto block_names = domain_creator.block_names();\n    CHECK(blocks_for_horizon_find.at(\"MockHorizonMetavars\") ==\n          std::unordered_set<std::string>{block_names.begin(),\n                                          block_names.end()});\n  }\n  {\n    const auto new_created_opts =\n        TestHelpers::test_creation<ah::HorizonOptions<Frame::Grid>,\n                                   TestCreationMetavariables>(\n            \"Criteria:\\n\"\n            \"FastFlow:\\n\"\n            \"  Flow: Fast\\n\"\n            \"  Alpha: 1.0\\n\"\n            \"  Beta: 0.5\\n\"\n            \"  AbsTol: 1e-12\\n\"\n            \"  TruncationTol: 1e-2\\n\"\n            \"  DivergenceTol: 1.2\\n\"\n            \"  DivergenceIter: 5\\n\"\n            \"  MaxIts: 100\\n\"\n            \"Verbosity: Verbose\\n\"\n            \"InitialGuess:\\n\"\n            \"  Center: [0.05, 0.06, 0.07]\\n\"\n            \"  Radius: 2.0\\n\"\n            \"  LMax: 12\\n\"\n            \"MaxComputeCoordsRetries: 3\\n\"\n            \"BlocksForHorizonFind: [Shell0]\");\n    const auto blocks_for_horizon_find =\n        ah::Tags::BlocksForHorizonFind::create_from_options<MockMetavariables>(\n            std::make_unique<domain::creators::Sphere>(\n                1.8, 2.2, domain::creators::Sphere::Excision{}, 1_st, 5_st,\n                false, std::nullopt, std::vector{2.0}),\n            new_created_opts);\n    REQUIRE(blocks_for_horizon_find.contains(\"MockHorizonMetavars\"));\n    const auto block_names = domain_creator.block_names();\n    CHECK(blocks_for_horizon_find.at(\"MockHorizonMetavars\") ==\n          std::unordered_set<std::string>{\"Shell0UpperZ\", \"Shell0LowerZ\",\n                                          \"Shell0UpperY\", \"Shell0LowerY\",\n                                          \"Shell0UpperX\", \"Shell0LowerX\"});\n  }\n\n  // Test LMax option tag\n  {\n    constexpr size_t max_l_20 = 20_st;\n    const auto tag_from_20 = ah::Tags::LMax::create_from_options(max_l_20);\n    CHECK(tag_from_20 == max_l_20);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_Storage.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <deque>\n#include <optional>\n#include <set>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/HorizonAliases.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Storage.hpp\"\n\nnamespace ah {\nnamespace {\n// The only thing to test with the storage is the serialization since it's just\n// structs with public members\ntemplate <typename Fr>\nvoid test_storage() {\n  const ah::Storage::VolumeVariables<Fr> volume_variables{\n      Mesh<3>{3, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto},\n      Variables<ah::vars_to_interpolate_to_target<3, Fr>>{4, 4.321}};\n  test_serialization(volume_variables);\n\n  ah::Storage::Iteration<Fr> iteration{\n      ylm::Strahlkorper<Fr>{4_st, 3.0, std::array{0.0, 0.1, 0.2}},\n      std::optional<std::vector<BlockLogicalCoords<3>>>{{std::nullopt}},\n      Variables<ah::vars_to_interpolate_to_target<3, Fr>>{6, 9.876},\n      std::vector<bool>{false, true, false, false, true, true},\n      {},\n      {},\n      {},\n      {2}};\n  test_serialization(iteration);\n  CHECK_FALSE(iteration.interpolation_is_complete());\n  for (size_t i = 0; i < iteration.indices_interpolated_to_thus_far.size();\n       ++i) {\n    iteration.indices_interpolated_to_thus_far[i] = true;\n  }\n  CHECK(iteration.interpolation_is_complete());\n\n  const ah::Storage::SingleTimeStorage<Fr> single_time_storage{\n      std::unordered_map<ElementId<3>, ah::Storage::VolumeVariables<Fr>>{\n          {ElementId<3>{0}, volume_variables}},\n      {},\n      iteration,\n      iteration.strahlkorper,\n      Destination::ControlSystem};\n  test_serialization(single_time_storage);\n\n  const ah::Storage::PreviousSurface<Fr> previous_surface{\n      LinkedMessageId<double>{3.0, {2.0}},\n      iteration.strahlkorper,\n      {ElementId<3>{1}, ElementId<3>{3}}};\n  test_serialization(previous_surface);\n\n  // Check we can use PreviousSurface with `emplace`\n  std::deque<ah::Storage::PreviousSurface<Fr>> previous_surfaces{};\n  previous_surfaces.emplace_front(\n      LinkedMessageId<double>{1.0, std::nullopt}, iteration.strahlkorper,\n      std::unordered_set<ElementId<3>>{ElementId<3>{4}, ElementId<3>{5}});\n  CHECK(previous_surfaces.front().time ==\n        LinkedMessageId<double>{1.0, std::nullopt});\n  CHECK(previous_surfaces.front().surface == iteration.strahlkorper);\n  CHECK(previous_surfaces.front().intersecting_element_ids ==\n        std::unordered_set<ElementId<3>>{ElementId<3>{4}, ElementId<3>{5}});\n\n  ah::Storage::LockedPreviousSurface<Fr> locked_previous_surface{};\n  locked_previous_surface.surface = previous_surface;\n  test_serialization(locked_previous_surface);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Storage\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  test_storage<::Frame::Grid>();\n  test_storage<::Frame::Distorted>();\n  test_storage<::Frame::Inertial>();\n}\n}  // namespace ah\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/ApparentHorizonFinder/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/LinkedMessageId.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Destination.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Protocols/HorizonMetavars.hpp\"\n#include \"ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\nstruct MockHorizonMetavars : tt::ConformsTo<ah::protocols::HorizonMetavars> {\n  using time_tag = ::Tags::TimeAndPrevious<0>;\n\n  using frame = ::Frame::Grid;\n\n  // Don't need callbacks\n  using horizon_find_callbacks = tmpl::list<>;\n  using horizon_find_failure_callbacks = tmpl::list<>;\n\n  using compute_tags_on_element = tmpl::list<>;\n\n  static constexpr ah::Destination destination = ah::Destination::ControlSystem;\n\n  static std::string name() { return \"MockHorizonMetavars\"; }\n};\n\nvoid test_observation_time() {\n  LinkedMessageId<double> result{};\n  ah::Tags::ObservationTimeCompute<0>::function(make_not_null(&result), 1.2);\n  CHECK(result == LinkedMessageId<double>{1.2, std::nullopt});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ApparentHorizonFinder.Tags\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  (void)MockHorizonMetavars::destination;\n\n  TestHelpers::db::test_simple_tag<ah::Tags::CurrentResolutionL>(\n      \"CurrentResolutionL\");\n  TestHelpers::db::test_simple_tag<ah::Tags::Verbosity>(\"Verbosity\");\n  TestHelpers::db::test_simple_tag<ah::Tags::FastFlow>(\"FastFlow\");\n  TestHelpers::db::test_simple_tag<ah::Tags::CurrentTime>(\"CurrentTime\");\n  TestHelpers::db::test_simple_tag<ah::Tags::PendingTimes>(\"PendingTimes\");\n  TestHelpers::db::test_simple_tag<ah::Tags::CompletedTimes>(\"CompletedTimes\");\n  TestHelpers::db::test_simple_tag<ah::Tags::Storage<::Frame::Distorted>>(\n      \"Storage\");\n  TestHelpers::db::test_simple_tag<\n      ah::Tags::PreviousSurfaces<::Frame::Distorted>>(\"PreviousSurfaces\");\n  TestHelpers::db::test_simple_tag<\n      ah::Tags::ApparentHorizonOptions<MockHorizonMetavars>>(\n      \"ApparentHorizonOptions\");\n  TestHelpers::db::test_simple_tag<ah::Tags::BlocksForHorizonFind>(\n      \"BlocksForHorizonFind\");\n  TestHelpers::db::test_simple_tag<ah::Tags::ObservationTime<0>>(\n      \"AhObservationTime0\");\n  TestHelpers::db::test_compute_tag<ah::Tags::ObservationTimeCompute<0>>(\n      \"AhObservationTime0\");\n  test_observation_time();\n  TestHelpers::db::test_simple_tag<ah::Tags::ObserveCenters>(\"ObserveCenters\");\n  TestHelpers::db::test_simple_tag<ah::Tags::LMax>(\"LMax\");\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Actions)\nadd_subdirectory(ApparentHorizonFinder)\nadd_subdirectory(Amr)\nadd_subdirectory(Events)\nadd_subdirectory(EventsAndDenseTriggers)\nadd_subdirectory(EventsAndTriggers)\nadd_subdirectory(Initialization)\nadd_subdirectory(Interpolation)\nadd_subdirectory(LinearSolver)\nadd_subdirectory(NonlinearSolver)\nadd_subdirectory(RayTracer)\nadd_subdirectory(SurfaceFinder)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelAlgorithmsEvents\")\n\nset(LIBRARY_SOURCES\n  Test_Completion.cpp\n  Test_ErrorIfDataTooBig.cpp\n  Test_ObserveAdaptiveSteppingDiagnostics.cpp\n  Test_ObserveAtExtremum.cpp\n  Test_ObserveFields.cpp\n  Test_ObserveNorms.cpp\n  Test_ObserveTimeStep.cpp\n  Test_ObserveTimeStepVolume.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  ErrorHandling\n  Events\n  EventsAndTriggers\n  EventsHelpers\n  H5\n  Interpolation\n  Observer\n  Spectral\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_Completion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Event, tmpl::list<Events::Completion>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.EventsAndTriggers.Completion\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  const auto completion =\n      TestHelpers::test_creation<std::unique_ptr<Event>, Metavariables>(\n          \"Completion\");\n  CHECK(not completion->needs_evolved_variables());\n\n  using my_component = Component<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component<my_component>(&runner, 0);\n\n  auto databox = db::create<db::AddSimpleTags<>>();\n  auto obs_box =\n      make_observation_box<db::AddComputeTags<>>(make_not_null(&databox));\n  auto& cache = ActionTesting::cache<my_component>(runner, 0);\n  my_component* const component_ptr = nullptr;\n  completion->run(make_not_null(&obs_box), cache, 0, component_ptr,\n                  {\"Unused\", -1.0});\n\n  CHECK(ActionTesting::get_terminate<my_component>(runner, 0));\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_ErrorIfDataTooBig.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Events/ErrorIfDataTooBig.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct Inertial;\n}  // namespace Frame\n\nnamespace {\nnamespace TestTags {\nstruct ScalarVar : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct TensorVar : db::SimpleTag {\n  using type = tnsr::ii<DataVector, 2>;\n};\n\nstruct OptionalScalar : db::SimpleTag {\n  using type = std::optional<Scalar<DataVector>>;\n};\n\nusing VariablesOfTensor = ::Tags::Variables<tmpl::list<TensorVar>>;\n\nstruct VariablesOfTensorCompute : VariablesOfTensor, db::ComputeTag {\n  using base = VariablesOfTensor;\n  using argument_tags =\n      tmpl::list<::domain::Tags::Coordinates<2, Frame::Inertial>>;\n  static void function(\n      const gsl::not_null<::Variables<tmpl::list<TensorVar>>*> vars,\n      const tnsr::I<DataVector, 2>& coordinates) {\n    vars->initialize(get<0>(coordinates).size());\n    get<0, 0>(get<TensorVar>(*vars)) =\n        get<0>(coordinates) + get<1>(coordinates);\n    get<0, 1>(get<TensorVar>(*vars)) = get<0, 0>(get<TensorVar>(*vars)) + 1.0;\n    get<1, 1>(get<TensorVar>(*vars)) = get<0, 0>(get<TensorVar>(*vars)) + 2.0;\n  }\n};\n}  // namespace TestTags\n\nusing TooBig = Events::ErrorIfDataTooBig<\n    2,\n    tmpl::list<TestTags::ScalarVar, TestTags::TensorVar,\n               TestTags::OptionalScalar>,\n    tmpl::list<TestTags::VariablesOfTensorCompute>>;\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<Event, tmpl::list<TooBig>>>;\n  };\n};\n\nvoid run_event(\n    // NOLINTNEXTLINE(performance-unnecessary-value-param)\n    tnsr::I<DataVector, 2> coordinates,\n    // NOLINTNEXTLINE(performance-unnecessary-value-param)\n    Scalar<DataVector> scalar_var,\n    // NOLINTNEXTLINE(performance-unnecessary-value-param)\n    std::optional<Scalar<DataVector>> optional_scalar,\n    const std::string& creation_string) {\n  const auto too_big =\n      TestHelpers::test_creation<std::unique_ptr<Event>, Metavariables>(\n          creation_string);\n  CHECK(too_big->needs_evolved_variables());\n\n  using my_component = Component<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_component<my_component>(&runner, 0);\n\n  auto databox = db::create<db::AddSimpleTags<\n      domain::Tags::Element<2>, domain::Tags::Coordinates<2, Frame::Inertial>,\n      TestTags::ScalarVar, TestTags::OptionalScalar>>(\n      Element<2>{ElementId<2>{0}, {}}, std::move(coordinates),\n      std::move(scalar_var), std::move(optional_scalar));\n\n  auto obs_box = make_observation_box<tmpl::filter<\n      TooBig::compute_tags_for_observation_box, db::is_compute_tag<tmpl::_1>>>(\n      make_not_null(&databox));\n  auto& cache = ActionTesting::cache<my_component>(runner, 0);\n  my_component* const component_ptr = nullptr;\n  too_big->run(make_not_null(&obs_box), cache, 0, component_ptr,\n               {\"Unused\", -1.0});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Events.ErrorIfDataTooBig\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  run_event(tnsr::I<DataVector, 2>{{{{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}}},\n            Scalar<DataVector>{{{{1.0, 2.0, 3.0}}}}, std::nullopt,\n            \"ErrorIfDataTooBig:\\n\"\n            \"  VariablesToCheck: [ScalarVar, TensorVar, OptionalScalar]\\n\"\n            \"  Threshold: 8.5\");\n  run_event(tnsr::I<DataVector, 2>{{{{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}}},\n            Scalar<DataVector>{{{{1.0, 2.0, 3.0}}}},\n            std::optional{Scalar<DataVector>{{{{1.0, 2.0, 3.0}}}}},\n            \"ErrorIfDataTooBig:\\n\"\n            \"  VariablesToCheck: [ScalarVar, TensorVar, OptionalScalar]\\n\"\n            \"  Threshold: 8.5\");\n  run_event(tnsr::I<DataVector, 2>{{{{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}}},\n            Scalar<DataVector>{{{{1.0, 2.0, 9.0}}}}, std::nullopt,\n            \"ErrorIfDataTooBig:\\n\"\n            \"  VariablesToCheck: [TensorVar]\\n\"\n            \"  Threshold: 8.5\");\n  CHECK_THROWS_WITH(\n      run_event(tnsr::I<DataVector, 2>{{{{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}}},\n                Scalar<DataVector>{{{{1.0, 2.0, 9.0}}}}, std::nullopt,\n                \"ErrorIfDataTooBig:\\n\"\n                \"  VariablesToCheck: [ScalarVar, TensorVar, OptionalScalar]\\n\"\n                \"  Threshold: 8.5\"),\n      Catch::Matchers::ContainsSubstring(\"ScalarVar too big\") and\n          Catch::Matchers::ContainsSubstring(\"value T()=9\") and\n          Catch::Matchers::ContainsSubstring(\"at position\") and\n          Catch::Matchers::ContainsSubstring(\"T(0)=3\") and\n          Catch::Matchers::ContainsSubstring(\"T(1)=3\") and\n          Catch::Matchers::ContainsSubstring(\"with ElementId:\"));\n  CHECK_THROWS_WITH(\n      run_event(tnsr::I<DataVector, 2>{{{{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}}},\n                Scalar<DataVector>{{{{1.0, 2.0, 3.0}}}},\n                std::optional{Scalar<DataVector>{{{{1.0, 2.0, 9.0}}}}},\n                \"ErrorIfDataTooBig:\\n\"\n                \"  VariablesToCheck: [ScalarVar, TensorVar, OptionalScalar]\\n\"\n                \"  Threshold: 8.5\"),\n      Catch::Matchers::ContainsSubstring(\"OptionalScalar too big\") and\n          Catch::Matchers::ContainsSubstring(\"value T()=9\") and\n          Catch::Matchers::ContainsSubstring(\"at position\") and\n          Catch::Matchers::ContainsSubstring(\"T(0)=3\") and\n          Catch::Matchers::ContainsSubstring(\"T(1)=3\") and\n          Catch::Matchers::ContainsSubstring(\"with ElementId:\"));\n  CHECK_THROWS_WITH(\n      run_event(tnsr::I<DataVector, 2>{{{{1.0, 2.0, 3.0}, {1.0, 2.0, 3.0}}}},\n                Scalar<DataVector>{{{{1.0, 2.0, -9.0}}}}, std::nullopt,\n                \"ErrorIfDataTooBig:\\n\"\n                \"  VariablesToCheck: [ScalarVar, TensorVar, OptionalScalar]\\n\"\n                \"  Threshold: 8.5\"),\n      Catch::Matchers::ContainsSubstring(\"ScalarVar too big\") and\n          Catch::Matchers::ContainsSubstring(\"value T()=-9\") and\n          Catch::Matchers::ContainsSubstring(\"at position\") and\n          Catch::Matchers::ContainsSubstring(\"T(0)=3\") and\n          Catch::Matchers::ContainsSubstring(\"T(1)=3\") and\n          Catch::Matchers::ContainsSubstring(\"with ElementId:\"));\n  CHECK_THROWS_WITH(\n      run_event(tnsr::I<DataVector, 2>{{{{1.0, 5.0, 3.0}, {1.0, 2.0, 3.0}}}},\n                Scalar<DataVector>{{{{1.0, 2.0, 3.0}}}}, std::nullopt,\n                \"ErrorIfDataTooBig:\\n\"\n                \"  VariablesToCheck: [ScalarVar, TensorVar, OptionalScalar]\\n\"\n                \"  Threshold: 8.5\"),\n      Catch::Matchers::ContainsSubstring(\"TensorVar too big\") and\n          Catch::Matchers::ContainsSubstring(\"value T(0,0)=7\") and\n          Catch::Matchers::ContainsSubstring(\"T(1,0)=8\") and\n          Catch::Matchers::ContainsSubstring(\"T(1,1)=9\") and\n          Catch::Matchers::ContainsSubstring(\"at position\") and\n          Catch::Matchers::ContainsSubstring(\"T(0)=5\") and\n          Catch::Matchers::ContainsSubstring(\"T(1)=2\"));\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_ObserveAdaptiveSteppingDiagnostics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <cstdint>\n#include <memory>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveAdaptiveSteppingDiagnostics.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace observers::Actions {\nstruct ContributeReductionData;\n}  // namespace observers::Actions\n\nnamespace {\nstruct MockContributeReductionData {\n  using ReductionData =\n      tmpl::wrap<tmpl::front<Events::ObserveAdaptiveSteppingDiagnostics::\n                                 observed_reduction_data_tags>,\n                 Parallel::ReductionData>;\n  struct Results {\n    observers::ObservationId observation_id;\n    std::string subfile_name;\n    std::vector<std::string> reduction_names;\n    ReductionData reduction_data;\n  };\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static std::optional<Results> results;\n\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationId& observation_id,\n                    Parallel::ArrayComponentId /*sender_array_id*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& reduction_names,\n                    ReductionData&& reduction_data) {\n    if (results) {\n      CHECK(results->observation_id == observation_id);\n      CHECK(results->subfile_name == subfile_name);\n      CHECK(results->reduction_names == reduction_names);\n      results->reduction_data.combine(std::move(reduction_data));\n    } else {\n      results.emplace();\n      *results = {observation_id, subfile_name, reduction_names,\n                  std::move(reduction_data)};\n    }\n  }\n};\n\nstd::optional<MockContributeReductionData::Results>\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    MockContributeReductionData::results{};\n\ntemplate <typename Metavariables>\nstruct ElementComponent {\n  using component_being_mocked = void;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverComponent {\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::ContributeReductionData>;\n  using with_these_simple_actions = tmpl::list<MockContributeReductionData>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<ElementComponent<Metavariables>,\n                                    MockObserverComponent<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        Event, tmpl::list<Events::ObserveAdaptiveSteppingDiagnostics>>>;\n  };\n};\n\ntemplate <typename Observer>\nvoid test_observe(const Observer& observer) {\n  using element_component = ElementComponent<Metavariables>;\n  using observer_component = MockObserverComponent<Metavariables>;\n\n  auto& results = MockContributeReductionData::results;\n  results.reset();\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_group_component<observer_component>(&runner);\n\n  using tag_list = tmpl::list<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                              Tags::Time, Tags::AdaptiveSteppingDiagnostics>;\n  std::vector<db::compute_databox_type<tag_list>> element_boxes;\n\n  const double observation_time = 2.0;\n  const uint64_t num_slabs = 100;\n  const uint64_t num_slab_changes = 20;\n  uint64_t total_num_steps = 0;\n  uint64_t total_num_step_changes = 0;\n  uint64_t total_num_step_rejections = 0;\n\n  const auto create_element = [&](const uint64_t num_steps,\n                                  const uint64_t num_step_changes,\n                                  const uint64_t num_step_rejections) {\n    auto box = db::create<tag_list>(\n        Metavariables{}, observation_time,\n        AdaptiveSteppingDiagnostics{num_slabs, num_slab_changes, num_steps,\n                                    num_step_changes, num_step_rejections});\n    total_num_steps += num_steps;\n    total_num_step_changes += num_step_changes;\n    total_num_step_rejections += num_step_rejections;\n\n    const auto ids_to_register =\n        observers::get_registration_observation_type_and_key(observer, box);\n    CHECK(ids_to_register->first == observers::TypeOfObservation::Reduction);\n    CHECK(ids_to_register->second == observers::ObservationKey(\"/subfile.dat\"));\n\n    element_boxes.push_back(std::move(box));\n\n    ActionTesting::emplace_component<element_component>(\n        &runner, element_boxes.size() - 1);\n  };\n\n  create_element(100, 12, 5);\n  create_element(130, 90, 54);\n  create_element(18, 2, 2);\n\n  for (size_t index = 0; index < element_boxes.size(); ++index) {\n    CHECK(static_cast<const Event&>(observer).is_ready(\n        element_boxes[index],\n        ActionTesting::cache<element_component>(runner, index),\n        static_cast<element_component::array_index>(index),\n        std::add_pointer_t<element_component>{}));\n    auto obs_box = make_observation_box<db::AddComputeTags<>>(\n        make_not_null(&element_boxes[index]));\n    observer.run(make_not_null(&obs_box),\n                 ActionTesting::cache<element_component>(runner, index),\n                 static_cast<element_component::array_index>(index),\n                 std::add_pointer_t<element_component>{},\n                 {\"TimeName\", observation_time});\n  }\n\n  // Process the data\n  for (size_t i = 0; i < element_boxes.size(); ++i) {\n    REQUIRE(\n        not runner.template is_simple_action_queue_empty<observer_component>(\n            0));\n    runner.template invoke_queued_simple_action<observer_component>(0);\n  }\n  CHECK(runner.template is_simple_action_queue_empty<observer_component>(0));\n\n  REQUIRE(results);\n  auto& reduction_data = results->reduction_data;\n  reduction_data.finalize();\n\n  CHECK(results->observation_id.value() == observation_time);\n  CHECK(results->subfile_name == \"/subfile\");\n  CHECK(results->reduction_names[0] == \"TimeName\");\n  CHECK(std::get<0>(reduction_data.data()) == observation_time);\n  CHECK(results->reduction_names[1] == \"Number of slabs\");\n  CHECK(std::get<1>(reduction_data.data()) == num_slabs);\n  CHECK(results->reduction_names[2] == \"Number of slab size changes\");\n  CHECK(std::get<2>(reduction_data.data()) == num_slab_changes);\n  CHECK(results->reduction_names[3] == \"Total steps on all elements\");\n  CHECK(std::get<3>(reduction_data.data()) == total_num_steps);\n  CHECK(results->reduction_names[4] == \"Number of LTS step changes\");\n  CHECK(std::get<4>(reduction_data.data()) == total_num_step_changes);\n  CHECK(results->reduction_names[5] == \"Number of step rejections\");\n  CHECK(std::get<5>(reduction_data.data()) == total_num_step_rejections);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.ParallelAlgorithms.Events.ObserveAdaptiveSteppingDiagnostics\",\n    \"[Unit][ParallelAlgorithms]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  {\n    const Events::ObserveAdaptiveSteppingDiagnostics observer(\"subfile\");\n    CHECK(not observer.needs_evolved_variables());\n    test_observe(observer);\n    test_observe(serialize_and_deserialize(observer));\n  }\n  {\n    const auto event =\n        TestHelpers::test_creation<std::unique_ptr<Event>, Metavariables>(\n            \"ObserveAdaptiveSteppingDiagnostics:\\n\"\n            \"  SubfileName: subfile\");\n    test_observe(*event);\n    test_observe(*serialize_and_deserialize(event));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_ObserveAtExtremum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveAtExtremum.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var0 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var1 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n};\n\nstruct Var0TimesTwo : db::SimpleTag {\n  using type = std::optional<Scalar<DataVector>>;\n};\n\nstruct Var0TimesTwoCompute : db::ComputeTag, Var0TimesTwo {\n  using base = Var0TimesTwo;\n  using return_type = std::optional<Scalar<DataVector>>;\n  using argument_tags = tmpl::list<Var0>;\n  static void function(\n      const gsl::not_null<std::optional<Scalar<DataVector>>*> result,\n      const Scalar<DataVector>& scalar_var) {\n    *result = Scalar<DataVector>{DataVector{2.0 * get(scalar_var)}};\n  }\n};\n\nstruct Var0TimesThree : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var0TimesThreeCompute : db::ComputeTag,\n                               ::Tags::Variables<tmpl::list<Var0TimesThree>> {\n  using base = ::Tags::Variables<tmpl::list<Var0TimesThree>>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<Var0>;\n  static void function(\n      const gsl::not_null<::Variables<tmpl::list<Var0TimesThree>>*> result,\n      const Scalar<DataVector>& scalar_var) {\n    result->initialize(get(scalar_var).size());\n    get(get<Var0TimesThree>(*result)) = 3.0 * get(scalar_var);\n  }\n};\n\nstruct TestSectionIdTag {};\n\nstruct MockContributeReductionData {\n  struct Results {\n    observers::ObservationId observation_id;\n    std::string subfile_name;\n    std::vector<std::string> legend;\n    double time;\n    std::vector<double> reduced_vector;\n  };\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static Results results;\n\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex, typename... Ts>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationId& observation_id,\n                    Parallel::ArrayComponentId /*sender_array_id*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& legend,\n                    Parallel::ReductionData<Ts...>&& reduction_data) {\n    reduction_data.finalize();\n    results.observation_id = observation_id;\n    results.subfile_name = subfile_name;\n    results.legend = legend;\n    results.time = std::get<0>(reduction_data.data());\n    results.reduced_vector = std::get<1>(reduction_data.data());\n  }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nMockContributeReductionData::Results MockContributeReductionData::results{};\n\ntemplate <typename Metavariables>\nstruct ElementComponent {\n  using component_being_mocked = void;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Metavariables::volume_dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverComponent {\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::ContributeReductionData>;\n  using with_these_simple_actions = tmpl::list<MockContributeReductionData>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename ArraySectionIdTag>\nusing ObserveAtExtremumEvent = Events::ObserveAtExtremum<\n    tmpl::list<Var0, Var1, Var0TimesTwoCompute, Var0TimesThree>,\n    tmpl::list<Var0TimesThreeCompute>, ArraySectionIdTag>;\n\ntemplate <size_t Dim, typename ArraySectionIdTag>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<ElementComponent<Metavariables>,\n                                    MockObserverComponent<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        Event, tmpl::list<ObserveAtExtremumEvent<ArraySectionIdTag>>>>;\n  };\n};\n\ntemplate <typename ArraySectionIdTag, typename ObserveEvent>\nvoid test(const std::unique_ptr<ObserveEvent> observe,\n          const std::string& extremum_type, const Spectral::Basis basis,\n          const Spectral::Quadrature quadrature,\n          const std::optional<std::string>& section) {\n  CAPTURE(pretty_type::name<ArraySectionIdTag>());\n  CAPTURE(section);\n  using metavariables = Metavariables<3, ArraySectionIdTag>;\n  using element_component = ElementComponent<metavariables>;\n  using observer_component = MockObserverComponent<metavariables>;\n  const typename element_component::array_index array_index(0);\n  const Mesh<3> mesh{3, basis, quadrature};\n  const size_t num_points = mesh.number_of_grid_points();\n  const double observation_time = 2.0;\n\n  Variables<tmpl::list<Var0, Var1>> vars(num_points);\n  // Fill the variables with some data.  It doesn't matter much what,\n  // but integers are nice in that we don't have to worry about\n  // roundoff error.\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  // std::iota(vars.data(), vars.data() + vars.size(), 1.0);\n  std::iota(vars.data(), vars.data() + num_points + 5, 1.0);\n  std::iota(vars.data() + num_points + 5, vars.data() + vars.size(), -10.0);\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n  ActionTesting::emplace_component<element_component>(make_not_null(&runner),\n                                                      array_index);\n  ActionTesting::emplace_group_component<observer_component>(&runner);\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<metavariables>,\n                        ::Events::Tags::ObserverMesh<3>,\n                        Tags::Variables<typename decltype(vars)::tags_list>,\n                        observers::Tags::ObservationKey<ArraySectionIdTag>>>(\n      metavariables{}, mesh, vars, section);\n\n  const auto ids_to_register =\n      observers::get_registration_observation_type_and_key(*observe, box);\n  const std::string expected_subfile_name{\n      \"/reduction0\" +\n      (std::is_same_v<ArraySectionIdTag, void> ? \"\"\n                                               : section.value_or(\"Unused\"))};\n  const observers::ObservationKey expected_observation_key_for_reg(\n      expected_subfile_name + \".dat\");\n  if (std::is_same_v<ArraySectionIdTag, void> or section.has_value()) {\n    CHECK(ids_to_register->first == observers::TypeOfObservation::Reduction);\n    CHECK(ids_to_register->second == expected_observation_key_for_reg);\n  } else {\n    CHECK_FALSE(ids_to_register.has_value());\n  }\n\n  CHECK(static_cast<const Event&>(*observe).is_ready(\n      box, ActionTesting::cache<element_component>(runner, array_index),\n      array_index, std::add_pointer_t<element_component>{}));\n\n  auto obs_box = make_observation_box<\n      tmpl::filter<typename ObserveAtExtremumEvent<\n                       ArraySectionIdTag>::compute_tags_for_observation_box,\n                   db::is_compute_tag<tmpl::_1>>>(make_not_null(&box));\n  observe->run(make_not_null(&obs_box),\n               ActionTesting::cache<element_component>(runner, array_index),\n               array_index, std::add_pointer_t<element_component>{},\n               {\"TimeName\", observation_time});\n\n  // Process the data\n  runner.template invoke_queued_simple_action<observer_component>(0);\n  CHECK(runner.template is_simple_action_queue_empty<observer_component>(0));\n\n  const auto& results = MockContributeReductionData::results;\n  CHECK(results.observation_id.value() == observation_time);\n  CHECK(results.observation_id.observation_key() ==\n        expected_observation_key_for_reg);\n  CHECK(results.subfile_name == expected_subfile_name);\n  CHECK(results.time == observation_time);\n  CHECK(results.legend[0] == \"TimeName\");\n  if (extremum_type == \"Max\") {\n    CHECK(results.legend[1] == \"Max(Var0)\");\n    CHECK(results.legend[2] == \"AtVar0Max(Var0)\");\n    CHECK(results.legend[3] == \"AtVar0Max(Var1_x)\");\n    CHECK(results.legend[4] == \"AtVar0Max(Var1_y)\");\n    CHECK(results.legend[5] == \"AtVar0Max(Var1_z)\");\n    CHECK(results.reduced_vector ==\n          std::vector<double>{27.0, 27.0, 11.0, 38.0, 65.0});\n  } else {\n    CHECK(results.legend[1] == \"Min(Var0)\");\n    CHECK(results.legend[2] == \"AtVar0Min(Var0)\");\n    CHECK(results.legend[3] == \"AtVar0Min(Var1_x)\");\n    CHECK(results.legend[4] == \"AtVar0Min(Var1_y)\");\n    CHECK(results.legend[5] == \"AtVar0Min(Var1_z)\");\n    CHECK(results.reduced_vector ==\n          std::vector<double>{1.0, 1.0, 28.0, 12.0, 39.0});\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.ObserveAtExtremum\", \"[Unit][Evolution]\") {\n  test<TestSectionIdTag>(\n      std::make_unique<ObserveAtExtremumEvent<TestSectionIdTag>>(\n          ObserveAtExtremumEvent<TestSectionIdTag>{\n              \"reduction0\", {\"Var0\", \"Max\", {\"Var0\", \"Var1\"}}}),\n      \"Max\", Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto,\n      \"Section0\");\n  test<TestSectionIdTag>(\n      std::make_unique<ObserveAtExtremumEvent<TestSectionIdTag>>(\n          ObserveAtExtremumEvent<TestSectionIdTag>{\n              \"reduction0\", {\"Var0\", \"Min\", {\"Var0\", \"Var1\"}}}),\n      \"Min\", Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto,\n      \"Section0\");\n\n  INFO(\"create/serialize\");\n  register_factory_classes_with_charm<Metavariables<3, void>>();\n  const auto factory_event = TestHelpers::test_creation<std::unique_ptr<Event>,\n                                                        Metavariables<3, void>>(\n      // [input_file_examples]\n      R\"(\n  ObserveAtExtremum:\n    SubfileName: reduction0\n    TensorsToObserve:\n      Name: Var0\n      ExtremumType: Max\n      AdditionalData:\n      - Var0\n      - Var1\n        )\");\n  // [input_file_examples]\n  auto serialized_event = serialize_and_deserialize(factory_event);\n  test<void>(std::move(serialized_event), \"Max\", Spectral::Basis::Legendre,\n             Spectral::Quadrature::GaussLobatto, std::nullopt);\n\n  test<void>(std::make_unique<ObserveAtExtremumEvent<void>>(\n                 ObserveAtExtremumEvent<void>{\n                     \"reduction0\", {\"Var0\", \"Max\", {\"Var0\", \"Var1\"}}}),\n             \"Max\", Spectral::Basis::FiniteDifference,\n             Spectral::Quadrature::CellCentered, std::nullopt);\n\n  // Test that Max reduction has the desired behavior on vectors\n  {\n    using ReductionData = Parallel::ReductionData<\n        // Observation value\n        Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n        // Maximum of first component of a vector\n        Parallel::ReductionDatum<std::vector<double>, funcl::Max<>>>;\n    ReductionData first_data_set{1.0, std::vector<double>{1.0, 2.0, -1.0}};\n    ReductionData second_data_set{1.0, std::vector<double>{2.0, 1.0, -3.0}};\n    first_data_set.combine(std::move(second_data_set));\n    CHECK(std::get<0>(first_data_set.data()) == 1.0);\n    CHECK(std::get<1>(first_data_set.data()) ==\n          std::vector<double>{2.0, 1.0, -3.0});\n  }\n  // Test that Min reduction has the desired behavior on vectors\n  {\n    using ReductionData = Parallel::ReductionData<\n        // Observation value\n        Parallel::ReductionDatum<double, funcl::AssertEqual<>>,\n        // Maximum of first component of a vector\n        Parallel::ReductionDatum<std::vector<double>, funcl::Min<>>>;\n    ReductionData first_data_set{1.0, std::vector<double>{1.0, 2.0, -1.0}};\n    ReductionData second_data_set{1.0, std::vector<double>{2.0, 1.0, -3.0}};\n    first_data_set.combine(std::move(second_data_set));\n    CHECK(std::get<0>(first_data_set.data()) == 1.0);\n    CHECK(std::get<1>(first_data_set.data()) ==\n          std::vector<double>{1.0, 2.0, -1.0});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_ObserveFields.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <numeric>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/FloatingPointType.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/Events/ObserveFields.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Interpolation/RegularGridInterpolant.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Projection.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/ArrayIndex.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveFields.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\n// for Variables\n\ntemplate <size_t>\nclass Index;\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace observers::Actions {\nstruct ContributeVolumeData;\n}  // namespace observers::Actions\n\nnamespace {\n// NOLINTNEXTLINE(google-build-using-namespace)\nusing namespace TestHelpers::dg::Events::ObserveFields;\n\ntemplate <typename System, typename = void>\nstruct prim_vars_impl {\n  using type = tmpl::list<>;\n  static constexpr bool has_prims = false;\n};\n\ntemplate <typename System>\nstruct prim_vars_impl<System,\n                      std::void_t<typename System::primitive_variables_tag>> {\n  using type = typename System::primitive_variables_tag::tags_list;\n  static constexpr bool has_prims = true;\n};\n\ntemplate <typename System>\nusing prim_vars_list = typename prim_vars_impl<System>::type;\n\ntemplate <typename System, typename ArraySectionIdTag = void,\n          typename ObserveEvent>\nvoid test_observe(\n    const std::unique_ptr<ObserveEvent> observe,\n    const std::optional<Mesh<System::volume_dim>>& interpolating_mesh,\n    const std::optional<Mesh<System::volume_dim>>& projection_mesh,\n    const bool has_analytic_solutions, const bool test_specific_blocks,\n    const std::optional<std::string>& section = std::nullopt,\n    const std::optional<std::string>& dependency = std::nullopt) {\n  INFO(test_specific_blocks);\n  using metavariables = Metavariables<System, false>;\n  constexpr size_t volume_dim = System::volume_dim;\n  using DataType = typename System::data_type;\n  using element_component = ElementComponent<metavariables>;\n  using observer_component = MockObserverComponent<metavariables>;\n  using coordinates_tag =\n      domain::Tags::Coordinates<volume_dim, Frame::Inertial>;\n\n  const ElementId<volume_dim> element_id(0);\n  const Element<volume_dim> element(element_id, {});\n  // NOTE: The coordinate map is not actually what is used to compute the\n  // coordinates.\n  const domain::creators::Rectilinear<volume_dim> rectilinear{\n      make_array<volume_dim>(-2.0), make_array<volume_dim>(2.0),\n      make_array<volume_dim>(0_st), make_array<volume_dim>(5_st),\n      make_array<volume_dim>(true)};\n  const typename element_component::array_index array_index(element_id);\n  const Mesh<volume_dim> mesh(5, Spectral::Basis::Legendre,\n                              Spectral::Quadrature::GaussLobatto);\n\n  const bool do_projection = projection_mesh.has_value();\n  const Mesh<volume_dim>& target_mesh = do_projection\n                                            ? projection_mesh.value()\n                                            : interpolating_mesh.value_or(mesh);\n  const intrp::RegularGrid interpolant(mesh, target_mesh);\n  const std::optional<\n      std::array<std::reference_wrapper<const Matrix>, volume_dim>>\n      projection_matrices =\n          do_projection ? std::make_optional(Spectral::p_projection_matrices(\n                              mesh, target_mesh))\n                        : std::nullopt;\n  const double observation_time = 2.0;\n  Variables<typename System::variables_tag::tags_list> vars(\n      mesh.number_of_grid_points());\n  Variables<tmpl::list<coordinates_tag>> coordinate_vars(\n      mesh.number_of_grid_points());\n  Variables<prim_vars_list<System>> prim_vars(mesh.number_of_grid_points());\n  // Fill the variables with some data.  It doesn't matter much what,\n  // but integers are nice in that we don't have to worry about\n  // roundoff error.\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  std::iota(vars.data(), vars.data() + vars.size(), 1.0);\n  std::iota(coordinate_vars.data(),\n            coordinate_vars.data() + coordinate_vars.size(),\n            static_cast<double>(vars.size()));\n  if constexpr (not std::is_same_v<tmpl::list<>, prim_vars_list<System>>) {\n    std::iota(prim_vars.data(), prim_vars.data() + prim_vars.size(),\n              static_cast<double>(vars.size() + coordinate_vars.size()));\n  }\n\n  const typename System::solution_for_test analytic_solution{};\n  using solution_variables = typename System::solution_for_test::vars_for_test;\n  const Variables<\n      db::wrap_tags_in<::Tags::detail::AnalyticImpl, solution_variables>>\n      solutions{variables_from_tagged_tuple(\n          analytic_solution.variables(get<coordinates_tag>(coordinate_vars),\n                                      observation_time, solution_variables{}))};\n  const Variables<solution_variables> errors =\n      [&vars, &prim_vars, &solutions]() -> Variables<solution_variables> {\n    if constexpr (prim_vars_impl<System>::has_prims) {\n      (void)vars;\n      return prim_vars.template extract_subset<solution_variables>() -\n             solutions;\n    } else {\n      (void)prim_vars;\n      return vars.template extract_subset<solution_variables>() - solutions;\n    }\n  }();\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavariables>;\n  MockRuntimeSystem runner(\n      tuples::TaggedTuple<\n          ::Tags::AnalyticSolution<typename System::solution_for_test>>{\n          std::move(analytic_solution)});\n  ActionTesting::emplace_component<element_component>(make_not_null(&runner),\n                                                      element_id);\n  ActionTesting::emplace_group_component<observer_component>(&runner);\n\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<metavariables>,\n      domain::Tags::Domain<volume_dim>, domain::Tags::Element<volume_dim>,\n      domain::Tags::Mesh<volume_dim>,\n      ::Tags::Variables<typename decltype(vars)::tags_list>,\n      ::Tags::Variables<typename decltype(prim_vars)::tags_list>,\n      coordinates_tag, ::Tags::AnalyticSolutions<solution_variables>,\n      observers::Tags::ObservationKey<ArraySectionIdTag>>>(\n      metavariables{}, rectilinear.create_domain(), element, mesh, vars,\n      prim_vars, get<coordinates_tag>(coordinate_vars),\n      [&solutions, &has_analytic_solutions]() {\n        return has_analytic_solutions ? std::make_optional(solutions)\n                                      : std::nullopt;\n      }(),\n      section);\n\n  const auto ids_to_register =\n      observers::get_registration_observation_type_and_key(*observe, box);\n  const std::string expected_subfile_name{\n      \"/element_data\" +\n      (std::is_same_v<ArraySectionIdTag, void> ? \"\"\n                                               : section.value_or(\"Unused\"))};\n  const observers::ObservationKey expected_observation_key_for_reg(\n      expected_subfile_name + \".vol\");\n  if (std::is_same_v<ArraySectionIdTag, void> or section.has_value()) {\n    CHECK(ids_to_register->first == observers::TypeOfObservation::Volume);\n    CHECK(ids_to_register->second == expected_observation_key_for_reg);\n  } else {\n    CHECK_FALSE(ids_to_register.has_value());\n  }\n\n  auto obs_box = make_observation_box<tmpl::push_back<\n      tmpl::filter<\n          typename System::ObserveEvent::compute_tags_for_observation_box,\n          db::is_compute_tag<tmpl::_1>>,\n      ::Events::Tags::ObserverMeshCompute<volume_dim>>>(make_not_null(&box));\n  observe->run(make_not_null(&obs_box),\n               ActionTesting::cache<element_component>(runner, array_index),\n               array_index, std::add_pointer_t<element_component>{},\n               {\"TimeName\", observation_time});\n\n  if (not std::is_same_v<ArraySectionIdTag, void> and not section.has_value()) {\n    CHECK(runner.template is_simple_action_queue_empty<observer_component>(0));\n    return;\n  }\n\n  // Process the data\n  runner.template invoke_queued_simple_action<observer_component>(0);\n  CHECK(runner.template is_simple_action_queue_empty<observer_component>(0));\n\n  const auto& results = MockContributeVolumeData::results;\n  CHECK(results.observation_id.value() == observation_time);\n  CHECK(results.observation_id.observation_key() ==\n        expected_observation_key_for_reg);\n  CHECK(results.subfile_name == expected_subfile_name);\n  CHECK(results.dependency == dependency);\n  CHECK(results.array_component_id ==\n        Parallel::make_array_component_id<element_component>(array_index));\n  CHECK(results.received_volume_data.extents.size() == volume_dim);\n  CHECK(std::equal(results.received_volume_data.extents.begin(),\n                   results.received_volume_data.extents.end(),\n                   target_mesh.extents().begin()));\n  CHECK(std::equal(results.received_volume_data.basis.begin(),\n                   results.received_volume_data.basis.end(),\n                   target_mesh.basis().begin()));\n  CHECK(std::equal(results.received_volume_data.quadrature.begin(),\n                   results.received_volume_data.quadrature.end(),\n                   target_mesh.quadrature().begin()));\n\n  size_t num_components_observed = 0;\n  // gcc 6.4.0 gets confused if we try to capture tensor_data by\n  // reference and fails to compile because it wants it to be\n  // non-const, so we capture a pointer instead.\n  const auto check_component_impl =\n      [&num_components_observed,\n       tensor_data = &results.received_volume_data.tensor_components,\n       &interpolant, &projection_matrices, &mesh, do_projection](\n          const std::string& component, const DataVector& expected) {\n        CAPTURE(*tensor_data);\n        CAPTURE(component);\n        const DataVector interpolated_expected =\n            do_projection\n                ? apply_matrices(*projection_matrices, expected, mesh.extents())\n                : interpolant.interpolate(expected);\n        const auto it =\n            alg::find_if(*tensor_data, [&component](const TensorComponent& tc) {\n              return tc.name == component;\n            });\n        REQUIRE(it != tensor_data->end());\n        CAPTURE(component);\n        if (component.substr(0, 6) == \"Tensor\" or\n            component.substr(0, 7) == \"Tensor2\" or\n            component.substr(0, 14) == \"Error(Tensor2)\") {\n          CHECK(std::get<std::vector<float>>(it->data) ==\n                std::vector<float>{interpolated_expected.begin(),\n                                   interpolated_expected.end()});\n        } else {\n          CHECK(std::get<DataVector>(it->data) == interpolated_expected);\n        }\n        ++num_components_observed;\n      };\n  const auto check_component = [&check_component_impl](\n                                   const std::string& component,\n                                   const auto& expected) {\n    if constexpr (std::is_same_v<std::decay_t<decltype(expected)>,\n                                 ComplexDataVector>) {\n      check_component_impl(\"Re(\" + component + \")\", real(expected));\n      check_component_impl(\"Im(\" + component + \")\", imag(expected));\n    } else {\n      check_component_impl(component, expected);\n    }\n  };\n  for (size_t i = 0; i < volume_dim; ++i) {\n    check_component(\n        std::string(\"InertialCoordinates_\") + gsl::at({'x', 'y', 'z'}, i),\n        get<coordinates_tag>(coordinate_vars).get(i));\n  }\n  System::check_data([&check_component, &prim_vars, &vars](\n                         const std::string& name, auto tag_v,\n                         const auto... indices) {\n    using tag = decltype(tag_v);\n    if constexpr (std::is_same_v<tag, TestHelpers::dg::Events::ObserveFields::\n                                          Tags::ScalarVarTimesTwo<DataType>>) {\n      check_component(\n          name, DataType{2.0 * get<typename System::ScalarVar>(vars).get()});\n    } else if constexpr (std::is_same_v<\n                             tag, TestHelpers::dg::Events::ObserveFields::Tags::\n                                      ScalarVarTimesThree<DataType>>) {\n      check_component(\n          name, DataType{3.0 * get<typename System::ScalarVar>(vars).get()});\n    } else {\n      if constexpr (tmpl::list_contains_v<\n                        typename std::decay_t<decltype(prim_vars)>::tags_list,\n                        tag>) {\n        check_component(name, get<tag>(prim_vars).get(indices...));\n      } else {\n        check_component(name, get<tag>(vars).get(indices...));\n      }\n    }\n  });\n  if (has_analytic_solutions) {\n    System::solution_for_test::check_data(\n        [&check_component, &errors](const std::string& name, auto tag,\n                                    const auto... indices) {\n          check_component(name, get<decltype(tag)>(errors).get(indices...));\n        });\n  }\n  CHECK(results.received_volume_data.tensor_components.size() ==\n        num_components_observed);\n\n  CHECK(static_cast<const Event&>(*observe).is_ready(\n      box, ActionTesting::cache<element_component>(runner, array_index),\n      array_index, std::add_pointer_t<element_component>{}));\n  CHECK(observe->needs_evolved_variables());\n}\n\ntemplate <typename System>\nvoid test_system(\n    const std::string& mesh_creation_string,\n    const std::optional<Mesh<System::volume_dim>>& interpolating_mesh = {},\n    const std::optional<Mesh<System::volume_dim>>& projection_mesh = {},\n    const bool has_analytic_solutions = true,\n    const std::optional<std::string>& section = std::nullopt,\n    const std::optional<std::string>& dependency = std::nullopt) {\n  INFO(pretty_type::get_name<System>());\n  CAPTURE(has_analytic_solutions);\n  CAPTURE(mesh_creation_string);\n  using ArraySectionIdTag = typename System::array_section_id;\n  INFO(pretty_type::get_name<ArraySectionIdTag>());\n  CAPTURE(section);\n  CAPTURE(dependency);\n  using metavariables = Metavariables<System, false>;\n  for (const bool test_specific_blocks : {false, true}) {\n    test_observe<System, ArraySectionIdTag>(\n        std::make_unique<typename System::ObserveEvent>(\n            System::make_test_object(interpolating_mesh, projection_mesh,\n                                     std::nullopt, dependency)),\n        interpolating_mesh, projection_mesh, has_analytic_solutions,\n        test_specific_blocks, section, dependency);\n    INFO(\"create/serialize\");\n    register_factory_classes_with_charm<metavariables>();\n    {\n      const std::string creation_string =\n          System::creation_string_for_test + mesh_creation_string;\n      const auto factory_event =\n          TestHelpers::test_creation<std::unique_ptr<Event>, metavariables>(\n              creation_string);\n      auto serialized_event = serialize_and_deserialize(factory_event);\n      // No dependency from factory creation\n      test_observe<System, ArraySectionIdTag>(\n          std::move(serialized_event), interpolating_mesh, projection_mesh,\n          has_analytic_solutions, test_specific_blocks, section, std::nullopt);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.dG.ObserveFields\", \"[Unit][Evolution]\") {\n  {\n    INFO(\"No Interpolation\");\n    const std::string interpolating_mesh_str =\n        \"  InterpolateToMesh: None\\n\"\n        \"  ProjectToMesh: None\\n\";\n    using system_no_section = ScalarSystem<dg::Events::ObserveFields, void>;\n    using system_with_section =\n        ScalarSystem<dg::Events::ObserveFields, TestSectionIdTag>;\n    using system_complex =\n        ScalarSystem<dg::Events::ObserveFields, void, ComplexDataVector>;\n    INVOKE_TEST_FUNCTION(\n        test_system,\n        (interpolating_mesh_str, std::nullopt, std::nullopt, true,\n         std::nullopt),\n        (system_no_section, system_with_section, system_complex,\n         ComplicatedSystem<dg::Events::ObserveFields>));\n    INVOKE_TEST_FUNCTION(\n        test_system,\n        (interpolating_mesh_str, std::nullopt, std::nullopt, true, \"Section0\"),\n        (system_no_section, system_with_section,\n         ComplicatedSystem<dg::Events::ObserveFields>));\n    INVOKE_TEST_FUNCTION(test_system,\n                         (interpolating_mesh_str, std::nullopt, std::nullopt,\n                          false, std::nullopt, \"TestDependency\"),\n                         (ScalarSystem<dg::Events::ObserveFields>,\n                          ComplicatedSystem<dg::Events::ObserveFields>));\n  }\n\n  {\n    INFO(\"Interpolate to finer grid\");\n    const std::string interpolating_mesh_str =\n        \"  InterpolateToMesh:\\n\"\n        \"    Extents: 12\\n\"\n        \"    Basis: Legendre\\n\"\n        \"    Quadrature: GaussLobatto\\n\"\n        \"  ProjectToMesh: None\";\n    const Mesh<1> interpolating_mesh_1d{12, Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto};\n    INVOKE_TEST_FUNCTION(\n        test_system,\n        (interpolating_mesh_str, interpolating_mesh_1d, std::nullopt, true),\n        (ScalarSystem<dg::Events::ObserveFields>));\n    INVOKE_TEST_FUNCTION(\n        test_system,\n        (interpolating_mesh_str, interpolating_mesh_1d, std::nullopt, false),\n        (ScalarSystem<dg::Events::ObserveFields>));\n    const Mesh<2> interpolating_mesh_2d{12, Spectral::Basis::Legendre,\n                                        Spectral::Quadrature::GaussLobatto};\n    test_system<ComplicatedSystem<dg::Events::ObserveFields>>(\n        interpolating_mesh_str, interpolating_mesh_2d);\n  }\n\n  {\n    INFO(\"Interpolate to coarser grid\");\n    const std::string interpolating_mesh_str =\n        \"  InterpolateToMesh:\\n\"\n        \"    Extents: 3\\n\"\n        \"    Basis: Legendre\\n\"\n        \"    Quadrature: GaussLobatto\\n\"\n        \"  ProjectToMesh: None\";\n    const Mesh<1> interpolating_mesh_1{3, Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::GaussLobatto};\n    const Mesh<2> interpolating_mesh_2{3, Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::GaussLobatto};\n    test_system<ScalarSystem<dg::Events::ObserveFields>>(interpolating_mesh_str,\n                                                         interpolating_mesh_1);\n    test_system<ComplicatedSystem<dg::Events::ObserveFields>>(\n        interpolating_mesh_str, interpolating_mesh_2);\n  }\n\n  {\n    INFO(\"Interpolate to different basis\");\n    const std::string interpolating_mesh_str =\n        \"  InterpolateToMesh:\\n\"\n        \"    Extents: 5\\n\"\n        \"    Basis: Chebyshev\\n\"\n        \"    Quadrature: GaussLobatto\\n\"\n        \"  ProjectToMesh: None\";\n    const Mesh<1> interpolating_mesh_1{5, Spectral::Basis::Chebyshev,\n                                       Spectral::Quadrature::GaussLobatto};\n    const Mesh<2> interpolating_mesh_2{5, Spectral::Basis::Chebyshev,\n                                       Spectral::Quadrature::GaussLobatto};\n    test_system<ScalarSystem<dg::Events::ObserveFields>>(interpolating_mesh_str,\n                                                         interpolating_mesh_1);\n    test_system<ComplicatedSystem<dg::Events::ObserveFields>>(\n        interpolating_mesh_str, interpolating_mesh_2);\n  }\n\n  {\n    INFO(\"Interpolate to different quadrature\");\n    const std::string interpolating_mesh_str =\n        \"  InterpolateToMesh:\\n\"\n        \"    Extents: 5\\n\"\n        \"    Basis: Legendre\\n\"\n        \"    Quadrature: Gauss\\n\"\n        \"  ProjectToMesh: None\";\n    const Mesh<1> interpolating_mesh_1{5, Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::Gauss};\n    const Mesh<2> interpolating_mesh_2{5, Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::Gauss};\n    test_system<ScalarSystem<dg::Events::ObserveFields>>(interpolating_mesh_str,\n                                                         interpolating_mesh_1);\n    test_system<ComplicatedSystem<dg::Events::ObserveFields>>(\n        interpolating_mesh_str, interpolating_mesh_2);\n  }\n\n  {\n    INFO(\"Interpolate to different extents, basis and quadrature\");\n    const std::string interpolating_mesh_str =\n        \"  InterpolateToMesh:\\n\"\n        \"    Extents: 8\\n\"\n        \"    Basis: FiniteDifference\\n\"\n        \"    Quadrature: CellCentered\\n\"\n        \"  ProjectToMesh: None\";\n    const Mesh<1> interpolating_mesh_1{8, Spectral::Basis::FiniteDifference,\n                                       Spectral::Quadrature::CellCentered};\n    const Mesh<2> interpolating_mesh_2{8, Spectral::Basis::FiniteDifference,\n                                       Spectral::Quadrature::CellCentered};\n    test_system<ScalarSystem<dg::Events::ObserveFields>>(interpolating_mesh_str,\n                                                         interpolating_mesh_1);\n    test_system<ComplicatedSystem<dg::Events::ObserveFields>>(\n        interpolating_mesh_str, interpolating_mesh_2);\n  }\n\n  {\n    INFO(\"Project to coarser mesh\");\n    const std::string projecting_mesh_str =\n        \"  ProjectToMesh:\\n\"\n        \"    Extents: 3\\n\"\n        \"    Basis: Legendre\\n\"\n        \"    Quadrature: GaussLobatto\\n\"\n        \"  InterpolateToMesh: None\";\n    const Mesh<1> projection_mesh_1{3, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto};\n    const Mesh<2> projection_mesh_2{3, Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto};\n    test_system<ScalarSystem<dg::Events::ObserveFields>>(\n        projecting_mesh_str, std::nullopt, projection_mesh_1);\n    test_system<ComplicatedSystem<dg::Events::ObserveFields>>(\n        projecting_mesh_str, std::nullopt, projection_mesh_2);\n  }\n\n  {\n    INFO(\"Interpolate to non-uniform mesh\");\n    // test nonuniform mesh, these cannot be parsed yet\n    const Mesh<2> interpolating_mesh(\n        {3, 9}, {Spectral::Basis::Legendre, Spectral::Basis::Chebyshev},\n        {Spectral::Quadrature::Gauss, Spectral::Quadrature::GaussLobatto});\n\n    test_observe<ComplicatedSystem<dg::Events::ObserveFields>>(\n        std::make_unique<typename ComplicatedSystem<\n            dg::Events::ObserveFields>::ObserveEvent>(\n            ComplicatedSystem<dg::Events::ObserveFields>::make_test_object(\n                interpolating_mesh, std::nullopt)),\n        interpolating_mesh, std::nullopt, true, false);\n  }\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<\n          typename ScalarSystem<dg::Events::ObserveFields>::ObserveEvent>(\n          \"SubfileName: VolumeData\\n\"\n          \"CoordinatesFloatingPointType: Double\\n\"\n          \"VariablesToObserve: [NotAVar]\\n\"\n          \"FloatingPointTypes: [Double]\\n\"\n          \"InterpolateToMesh: None\\n\"\n          \"ProjectToMesh: None\\n\"\n          \"BlocksToObserve: All\\n\"),\n      Catch::Matchers::ContainsSubstring(\"Invalid selection: NotAVar\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<\n          typename ScalarSystem<dg::Events::ObserveFields>::ObserveEvent>(\n          \"SubfileName: VolumeData\\n\"\n          \"CoordinatesFloatingPointType: Double\\n\"\n          \"VariablesToObserve: [Scalar]\\n\"\n          \"FloatingPointTypes: [Double]\\n\"\n          \"InterpolateToMesh:\\n\"\n          \"  Extents: 5\\n\"\n          \"  Basis: Legendre\\n\"\n          \"  Quadrature: Gauss\\n\"\n          \"ProjectToMesh:\\n\"\n          \"  Extents: 3\\n\"\n          \"  Basis: Legendre\\n\"\n          \"  Quadrature: GaussLobatto\\n\"\n          \"BlocksToObserve: All\\n\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Specify only one of InterpolateToMesh or ProjectToMesh.\"));\n\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<\n          typename ScalarSystem<dg::Events::ObserveFields>::ObserveEvent>(\n          \"SubfileName: VolumeData\\n\"\n          \"CoordinatesFloatingPointType: Double\\n\"\n          \"VariablesToObserve: [Scalar, Scalar]\\n\"\n          \"FloatingPointTypes: [Double]\\n\"\n          \"InterpolateToMesh: None\\n\"\n          \"ProjectToMesh: None\\n\"\n          \"BlocksToObserve: All\\n\"),\n      Catch::Matchers::ContainsSubstring(\"Scalar specified multiple times\"));\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_ObserveNorms.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveNorms.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var0 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var1 : db::SimpleTag {\n  using type = tnsr::I<DataVector, 3, Frame::Inertial>;\n};\n\nstruct Var0TimesTwo : db::SimpleTag {\n  using type = std::optional<Scalar<DataVector>>;\n};\n\nstruct Var0TimesTwoCompute : db::ComputeTag, Var0TimesTwo {\n  using base = Var0TimesTwo;\n  using return_type = std::optional<Scalar<DataVector>>;\n  using argument_tags = tmpl::list<Var0>;\n  static void function(\n      const gsl::not_null<std::optional<Scalar<DataVector>>*> result,\n      const Scalar<DataVector>& scalar_var) {\n    *result = Scalar<DataVector>{DataVector{2.0 * get(scalar_var)}};\n  }\n};\n\nstruct Var0TimesThree : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var0TimesThreeCompute : db::ComputeTag,\n                               ::Tags::Variables<tmpl::list<Var0TimesThree>> {\n  using base = ::Tags::Variables<tmpl::list<Var0TimesThree>>;\n  using return_type = typename base::type;\n  using argument_tags = tmpl::list<Var0>;\n  static void function(\n      const gsl::not_null<::Variables<tmpl::list<Var0TimesThree>>*> result,\n      const Scalar<DataVector>& scalar_var) {\n    result->initialize(get(scalar_var).size());\n    get(get<Var0TimesThree>(*result)) = 3.0 * get(scalar_var);\n  }\n};\n\nstruct TestSectionIdTag {};\n\n// Name for option parsing\nstruct ObserveMyNorms {};\n\nstruct MockContributeReductionData {\n  struct Results {\n    observers::ObservationId observation_id;\n    std::string subfile_name;\n    std::vector<std::string> reduction_names;\n    double time;\n    size_t number_of_grid_points;\n    double volume;\n    std::vector<double> max_values;\n    std::vector<double> min_values;\n    std::vector<double> l1_norm_values;\n    std::vector<double> l1_integral_norm_values;\n    std::vector<double> l2_norm_values;\n    std::vector<double> l2_integral_norm_values;\n    std::vector<double> volume_integral_values;\n  };\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static Results results;\n\n  template <typename ParallelComponent, typename... DbTags,\n            typename Metavariables, typename ArrayIndex, typename... Ts>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationId& observation_id,\n                    Parallel::ArrayComponentId /*sender_array_id*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& reduction_names,\n                    Parallel::ReductionData<Ts...>&& reduction_data) {\n    reduction_data.finalize();\n    results.observation_id = observation_id;\n    results.subfile_name = subfile_name;\n    results.reduction_names = reduction_names;\n    results.time = std::get<0>(reduction_data.data());\n    results.number_of_grid_points = std::get<1>(reduction_data.data());\n    results.volume = std::get<2>(reduction_data.data());\n    results.max_values = std::get<3>(reduction_data.data());\n    results.min_values = std::get<4>(reduction_data.data());\n    results.l1_norm_values = std::get<5>(reduction_data.data());\n    results.l1_integral_norm_values = std::get<6>(reduction_data.data());\n    results.l2_norm_values = std::get<7>(reduction_data.data());\n    results.l2_integral_norm_values = std::get<8>(reduction_data.data());\n    results.volume_integral_values = std::get<9>(reduction_data.data());\n  }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nMockContributeReductionData::Results MockContributeReductionData::results{};\n\ntemplate <typename Metavariables>\nstruct ElementComponent {\n  using component_being_mocked = void;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Metavariables::volume_dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverComponent {\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::ContributeReductionData>;\n  using with_these_simple_actions = tmpl::list<MockContributeReductionData>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename ArraySectionIdTag, typename OptionName = void>\nusing ObserveNormsEvent = Events::ObserveNorms<\n    tmpl::list<Var0, Var1, Var0TimesTwoCompute, Var0TimesThree>,\n    tmpl::list<Var0TimesThreeCompute>, ArraySectionIdTag, OptionName>;\n\ntemplate <size_t Dim, typename ArraySectionIdTag, typename OptionName = void>\nstruct Metavariables {\n  static constexpr size_t volume_dim = Dim;\n  using component_list = tmpl::list<ElementComponent<Metavariables>,\n                                    MockObserverComponent<Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        Event, tmpl::list<ObserveNormsEvent<ArraySectionIdTag, OptionName>>>>;\n  };\n};\n\ntemplate <typename ArraySectionIdTag, typename ObserveEvent>\nvoid test(const std::unique_ptr<ObserveEvent> observe,\n          const Spectral::Basis basis, const Spectral::Quadrature quadrature,\n          const std::optional<std::string>& section) {\n  CAPTURE(pretty_type::name<ArraySectionIdTag>());\n  CAPTURE(section);\n  using metavariables = Metavariables<3, ArraySectionIdTag>;\n  using element_component = ElementComponent<metavariables>;\n  using observer_component = MockObserverComponent<metavariables>;\n  const typename element_component::array_index array_index(0);\n  const Mesh<3> mesh{3, basis, quadrature};\n  const size_t num_points = mesh.number_of_grid_points();\n  // Jacobian of a cube with side length 1, so expected volume is 1.\n  const Scalar<DataVector> det_inv_jacobian(num_points, cube(2.));\n  const double expected_volume = 1.;\n  const double observation_time = 2.0;\n  Variables<tmpl::list<Var0, Var1>> vars(num_points);\n  // Fill the variables with some data.  It doesn't matter much what,\n  // but integers are nice in that we don't have to worry about\n  // roundoff error.\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  std::iota(vars.data(), vars.data() + vars.size(), 1.0);\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n  ActionTesting::emplace_component<element_component>(make_not_null(&runner),\n                                                      array_index);\n  ActionTesting::emplace_group_component<observer_component>(&runner);\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<metavariables>,\n                        ::Events::Tags::ObserverMesh<3>,\n                        ::Events::Tags::ObserverDetInvJacobian<\n                            Frame::ElementLogical, Frame::Inertial>,\n                        Tags::Variables<typename decltype(vars)::tags_list>,\n                        observers::Tags::ObservationKey<ArraySectionIdTag>>>(\n      metavariables{}, mesh, det_inv_jacobian, vars, section);\n\n  const auto ids_to_register =\n      observers::get_registration_observation_type_and_key(*observe, box);\n  const std::string expected_subfile_name{\n      \"/reduction0\" +\n      (std::is_same_v<ArraySectionIdTag, void> ? \"\"\n                                               : section.value_or(\"Unused\"))};\n  const observers::ObservationKey expected_observation_key_for_reg(\n      expected_subfile_name + \".dat\");\n  if (std::is_same_v<ArraySectionIdTag, void> or section.has_value()) {\n    CHECK(ids_to_register->first == observers::TypeOfObservation::Reduction);\n    CHECK(ids_to_register->second == expected_observation_key_for_reg);\n  } else {\n    CHECK_FALSE(ids_to_register.has_value());\n  }\n\n  CHECK(static_cast<const Event&>(*observe).is_ready(\n      box, ActionTesting::cache<element_component>(runner, array_index),\n      array_index, std::add_pointer_t<element_component>{}));\n\n  auto obs_box = make_observation_box<\n      tmpl::filter<typename ObserveNormsEvent<\n                       ArraySectionIdTag>::compute_tags_for_observation_box,\n                   db::is_compute_tag<tmpl::_1>>>(make_not_null(&box));\n  observe->run(make_not_null(&obs_box),\n               ActionTesting::cache<element_component>(runner, array_index),\n               array_index, std::add_pointer_t<element_component>{},\n               {\"TimeName\", observation_time});\n\n  // Process the data\n  ActionTesting::invoke_queued_simple_action<observer_component>(\n      make_not_null(&runner), 0);\n  CHECK(ActionTesting::is_simple_action_queue_empty<observer_component>(\n      runner, 0));\n\n  const auto& results = MockContributeReductionData::results;\n  CHECK(results.observation_id.value() == observation_time);\n  CHECK(results.observation_id.observation_key() ==\n        expected_observation_key_for_reg);\n  CHECK(results.subfile_name == expected_subfile_name);\n  CHECK(results.reduction_names[0] == \"TimeName\");\n  CHECK(results.time == observation_time);\n  CHECK(results.reduction_names[1] == \"NumberOfPoints\");\n  CHECK(results.number_of_grid_points == num_points);\n  CHECK(results.reduction_names[2] == \"Volume\");\n  CHECK(results.volume == approx(expected_volume));\n\n  // Check max values\n  CHECK(results.reduction_names[3] == \"Max(Var0)\");\n  CHECK(results.reduction_names[4] == \"Max(Var0)\");\n  CHECK(results.reduction_names[5] == \"Max(Var0TimesTwo)\");\n  CHECK(results.reduction_names[6] == \"Max(Var0TimesThree)\");\n  CHECK(results.max_values == std::vector<double>{27.0, 27.0, 54.0, 81.0});\n\n  // Check min values\n  CHECK(results.reduction_names[7] == \"Min(Var1_x)\");\n  CHECK(results.reduction_names[8] == \"Min(Var1_y)\");\n  CHECK(results.reduction_names[9] == \"Min(Var1_z)\");\n  CHECK(results.reduction_names[10] == \"Min(Var1)\");\n  CHECK(results.min_values == std::vector<double>{28.0, 55.0, 82.0, 28.0});\n\n  // Check L1 norms\n  CHECK(results.reduction_names[11] == \"L1Norm(Var1)\");\n  CHECK(results.reduction_names[12] == \"L1Norm(Var1_x)\");\n  CHECK(results.reduction_names[13] == \"L1Norm(Var1_y)\");\n  CHECK(results.reduction_names[14] == \"L1Norm(Var1_z)\");\n  CHECK(results.l1_norm_values[0] == approx(204.0));\n  CHECK(results.l1_norm_values[1] == approx(41.0));\n  CHECK(results.l1_norm_values[2] == approx(68.0));\n  CHECK(results.l1_norm_values[3] == approx(95.0));\n\n  // Check L1 integral norms\n  CHECK(results.reduction_names[15] == \"L1IntegralNorm(Var1)\");\n  CHECK(results.reduction_names[16] == \"L1IntegralNorm(Var1_x)\");\n  CHECK(results.reduction_names[17] == \"L1IntegralNorm(Var1_y)\");\n  CHECK(results.reduction_names[18] == \"L1IntegralNorm(Var1_z)\");\n  // All Var1 values are positive, so L1IntegralNorm equals the volume integral\n  // divided by the volume (which is 1), giving the same values as L1Norm.\n  for (size_t i = 0; i < 4; i++) {\n    CHECK(results.l1_integral_norm_values[i] ==\n          approx(results.l1_norm_values[i]));\n  }\n\n  // Check L2 norms\n  CHECK(results.reduction_names[19] == \"L2Norm(Var1)\");\n  CHECK(results.reduction_names[20] == \"L2Norm(Var1_x)\");\n  CHECK(results.reduction_names[21] == \"L2Norm(Var1_y)\");\n  CHECK(results.reduction_names[22] == \"L2Norm(Var1_z)\");\n  CHECK(results.l2_norm_values[0] == approx(124.5471798155221137));\n  CHECK(results.l2_norm_values[1] == approx(41.73328008516305232));\n  CHECK(results.l2_norm_values[2] == approx(68.44462481938714404));\n  CHECK(results.l2_norm_values[3] == approx(95.3187634554008838));\n\n  // Check L2 integral norms\n  CHECK(results.reduction_names[23] == \"L2IntegralNorm(Var1)\");\n  CHECK(results.reduction_names[24] == \"L2IntegralNorm(Var1_x)\");\n  CHECK(results.reduction_names[25] == \"L2IntegralNorm(Var1_y)\");\n  CHECK(results.reduction_names[26] == \"L2IntegralNorm(Var1_z)\");\n  if (basis != Spectral::Basis::FiniteDifference) {\n    CHECK(results.l2_integral_norm_values[0] == approx(124.18131904598212145));\n    CHECK(results.l2_integral_norm_values[1] == approx(41.36826480931165406));\n    CHECK(results.l2_integral_norm_values[2] == approx(68.22267462752640199));\n    CHECK(results.l2_integral_norm_values[3] == approx(95.15951520123110186));\n  } else {\n    for (size_t i = 0; i < 4; i++) {\n      CHECK(results.l2_integral_norm_values[i] ==\n            approx(results.l2_norm_values[i]));\n    }\n  }\n\n  // Check volume integral norms\n  CHECK(results.reduction_names[27] == \"VolumeIntegral(Var1)\");\n  CHECK(results.reduction_names[28] == \"VolumeIntegral(Var1_x)\");\n  CHECK(results.reduction_names[29] == \"VolumeIntegral(Var1_y)\");\n  CHECK(results.reduction_names[30] == \"VolumeIntegral(Var1_z)\");\n  CHECK(results.volume_integral_values[0] == approx(204.0));\n  CHECK(results.volume_integral_values[1] == approx(41.0));\n  CHECK(results.volume_integral_values[2] == approx(68.0));\n  CHECK(results.volume_integral_values[3] == approx(95.0));\n}\n\ntemplate <bool Spherical, typename ArraySectionIdTag, typename ObserveEvent>\nvoid test_cartoon(const std::unique_ptr<ObserveEvent> observe,\n                  const std::optional<std::string>& section,\n                  const double x_inner = 0.0) {\n  // We are testing that the correct cartesian to spherical/cylindrical jacobian\n  // is being mulitplied\n  CAPTURE(pretty_type::name<ArraySectionIdTag>());\n  CAPTURE(section);\n  CAPTURE(Spherical);\n  using metavariables = Metavariables<3, ArraySectionIdTag>;\n  using element_component = ElementComponent<metavariables>;\n  using observer_component = MockObserverComponent<metavariables>;\n  const typename element_component::array_index array_index(0);\n  Mesh<3> mesh;\n  // DataBox needs inertial coordinates to do cartoon-basis integration\n  using Affine = domain::CoordinateMaps::Affine;\n  using Identity1D = domain::CoordinateMaps::Identity<1>;\n  double expected_volume{};\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coords;\n  Scalar<DataVector> det_inv_jacobian;\n  if constexpr (Spherical) {\n    mesh = Mesh<3>{{{5, 1, 1}},\n                   {{Spectral::Basis::Legendre, Spectral::Basis::Cartoon,\n                     Spectral::Basis::Cartoon}},\n                   {{Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::SphericalSymmetry,\n                     Spectral::Quadrature::SphericalSymmetry}}};\n    const domain::CoordinateMap<\n        Frame::ElementLogical, Frame::Inertial,\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Identity1D, Identity1D>>\n        map{{Affine{-1.0, 1.0, x_inner, 2.0}, Identity1D{}, Identity1D{}}};\n    inertial_coords = map(logical_coordinates(mesh));\n    det_inv_jacobian =\n        determinant(map.inv_jacobian(logical_coordinates((mesh))));\n    expected_volume = 4.0 * M_PI * (cube(2) - cube(x_inner)) / 3.0;\n  } else {\n    mesh = Mesh<3>{{{5, 4, 1}},\n                   {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n                     Spectral::Basis::Cartoon}},\n                   {{Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::GaussLobatto,\n                     Spectral::Quadrature::AxialSymmetry}}};\n    const domain::CoordinateMap<\n        Frame::ElementLogical, Frame::Inertial,\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Identity1D>>\n        map{{Affine{-1.0, 1.0, x_inner, 2.0}, Affine{-1.0, 1.0, 2.0, 3.0},\n             Identity1D{}}};\n    inertial_coords = map(logical_coordinates(mesh));\n    det_inv_jacobian =\n        determinant(map.inv_jacobian(logical_coordinates((mesh))));\n    expected_volume = M_PI * (square(2.0) - square(x_inner));\n  }\n  const size_t num_points = mesh.number_of_grid_points();\n\n  const double observation_time = 2.0;\n  Variables<tmpl::list<Var0, Var1>> vars(num_points);\n\n  auto& scalar = get<Var0>(vars);\n  get<>(scalar) = get<0>(inertial_coords);\n\n  get<Var1>(vars) = inertial_coords;\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n  ActionTesting::emplace_component<element_component>(make_not_null(&runner),\n                                                      array_index);\n  ActionTesting::emplace_group_component<observer_component>(&runner);\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<metavariables>,\n                        ::Events::Tags::ObserverMesh<3>,\n                        ::Events::Tags::ObserverDetInvJacobian<\n                            Frame::ElementLogical, Frame::Inertial>,\n                        ::Events::Tags::ObserverCoordinates<3, Frame::Inertial>,\n                        Tags::Variables<typename decltype(vars)::tags_list>,\n                        observers::Tags::ObservationKey<ArraySectionIdTag>>>(\n      metavariables{}, mesh, det_inv_jacobian, inertial_coords, vars, section);\n\n  auto obs_box = make_observation_box<\n      tmpl::filter<typename ObserveNormsEvent<\n                       ArraySectionIdTag>::compute_tags_for_observation_box,\n                   db::is_compute_tag<tmpl::_1>>>(make_not_null(&box));\n  observe->run(make_not_null(&obs_box),\n               ActionTesting::cache<element_component>(runner, array_index),\n               array_index, std::add_pointer_t<element_component>{},\n               {\"TimeName\", observation_time});\n\n  // Process the data\n  ActionTesting::invoke_queued_simple_action<observer_component>(\n      make_not_null(&runner), 0);\n  CHECK(ActionTesting::is_simple_action_queue_empty<observer_component>(\n      runner, 0));\n\n  const auto& results = MockContributeReductionData::results;\n\n  CHECK(results.reduction_names[0] == \"TimeName\");\n  CHECK(results.time == observation_time);\n  CHECK(results.reduction_names[1] == \"NumberOfPoints\");\n  CHECK(results.number_of_grid_points == num_points);\n  CHECK(results.reduction_names[2] == \"Volume\");\n  CHECK(results.volume == approx(expected_volume));\n\n  const auto integrate = [&mesh, &det_inv_jacobian](\n                             const DataVector& a,\n                             const DataVector& coord_jacobian) -> double {\n    const DataVector integrand = a * coord_jacobian / get<>(det_inv_jacobian);\n    return definite_integral(integrand, mesh);\n  };\n  const auto normalize_l1 = [&expected_volume](const double a) -> double {\n    return a / expected_volume;\n  };\n  const auto normalize_l2 = [&expected_volume](const double a) -> double {\n    return sqrt(a / expected_volume);\n  };\n\n  // Check L1 integral norms\n  // Var1 = inertial_coords; all non-z components are >= 0 in the test domain,\n  // so abs(Var1_x) = Var1_x, abs(Var1_y) = Var1_y, abs(Var1_z) = 0.\n  CHECK(results.reduction_names[3] == \"L1IntegralNorm(Var1)\");\n  CHECK(results.reduction_names[4] == \"L1IntegralNorm(Var1_x)\");\n  CHECK(results.reduction_names[5] == \"L1IntegralNorm(Var1_y)\");\n  CHECK(results.reduction_names[6] == \"L1IntegralNorm(Var1_z)\");\n  if constexpr (Spherical) {\n    const double l1_result = normalize_l1(\n        integrate(get<0>(get<Var1>(vars)), square(get<0>(inertial_coords))));\n    CHECK(results.l1_integral_norm_values[0] == approx(l1_result));\n    CHECK(results.l1_integral_norm_values[1] == approx(l1_result));\n    CHECK(results.l1_integral_norm_values[2] == approx(0.0));\n  } else {\n    CHECK(results.l1_integral_norm_values[0] ==\n          approx(normalize_l1(\n              integrate(get<0>(get<Var1>(vars)), get<0>(inertial_coords)) +\n              integrate(get<1>(get<Var1>(vars)), get<0>(inertial_coords)))));\n    CHECK(results.l1_integral_norm_values[1] ==\n          approx(normalize_l1(\n              integrate(get<0>(get<Var1>(vars)), get<0>(inertial_coords)))));\n    CHECK(results.l1_integral_norm_values[2] ==\n          approx(normalize_l1(\n              integrate(get<1>(get<Var1>(vars)), get<0>(inertial_coords)))));\n  }\n  CHECK(results.l1_integral_norm_values[3] == approx(0.0));\n\n  // Check L2 integral norms\n  CHECK(results.reduction_names[7] == \"L2IntegralNorm(Var1)\");\n  CHECK(results.reduction_names[8] == \"L2IntegralNorm(Var1_x)\");\n  CHECK(results.reduction_names[9] == \"L2IntegralNorm(Var1_y)\");\n  CHECK(results.reduction_names[10] == \"L2IntegralNorm(Var1_z)\");\n  if constexpr (Spherical) {\n    const double result = normalize_l2(integrate(\n        square(get<0>(get<Var1>(vars))), square(get<0>(inertial_coords))));\n    CHECK(results.l2_integral_norm_values[0] == approx(result));\n    CHECK(results.l2_integral_norm_values[1] == approx(result));\n    CHECK(results.l2_integral_norm_values[2] == approx(0.0));\n  } else {\n    CHECK(results.l2_integral_norm_values[0] ==\n          approx(normalize_l2(integrate(square(get<0>(get<Var1>(vars))),\n                                        get<0>(inertial_coords)) +\n                              integrate(square(get<1>(get<Var1>(vars))),\n                                        get<0>(inertial_coords)))));\n    CHECK(results.l2_integral_norm_values[1] ==\n          approx(normalize_l2(integrate(square(get<0>(get<Var1>(vars))),\n                                        get<0>(inertial_coords)))));\n    CHECK(results.l2_integral_norm_values[2] ==\n          approx(normalize_l2(integrate(square(get<1>(get<Var1>(vars))),\n                                        get<0>(inertial_coords)))));\n  }\n  CHECK(results.l2_integral_norm_values[3] == approx(0.0));\n\n  // Check volume integral norms\n  CHECK(results.reduction_names[11] == \"VolumeIntegral(Var1)\");\n  CHECK(results.reduction_names[12] == \"VolumeIntegral(Var1_x)\");\n  CHECK(results.reduction_names[13] == \"VolumeIntegral(Var1_y)\");\n  CHECK(results.reduction_names[14] == \"VolumeIntegral(Var1_z)\");\n  if constexpr (Spherical) {\n    const double result =\n        integrate(get<0>(get<Var1>(vars)), square(get<0>(inertial_coords)));\n    CHECK(results.volume_integral_values[0] == approx(result));\n    CHECK(results.volume_integral_values[1] == approx(result));\n    CHECK(results.volume_integral_values[2] == approx(0.0));\n  } else {\n    CHECK(results.volume_integral_values[0] ==\n          approx(integrate(get<0>(get<Var1>(vars)), get<0>(inertial_coords)) +\n                 integrate(get<1>(get<Var1>(vars)), get<0>(inertial_coords))));\n    CHECK(results.volume_integral_values[1] ==\n          approx(integrate(get<0>(get<Var1>(vars)), get<0>(inertial_coords))));\n    CHECK(results.volume_integral_values[2] ==\n          approx(integrate(get<1>(get<Var1>(vars)), get<0>(inertial_coords))));\n  }\n  CHECK(results.volume_integral_values[3] == approx(0.0));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.ObserveNorms\", \"[Unit][Evolution]\") {\n  test<TestSectionIdTag>(std::make_unique<ObserveNormsEvent<TestSectionIdTag>>(\n                             ObserveNormsEvent<TestSectionIdTag>{\n                                 \"reduction0\",\n                                 {{\"Var0\", \"Max\", \"Individual\"},\n                                  {\"Var1\", \"Min\", \"Individual\"},\n                                  {\"Var0\", \"Max\", \"Sum\"},\n                                  {\"Var0TimesTwo\", \"Max\", \"Individual\"},\n                                  {\"Var0TimesThree\", \"Max\", \"Individual\"},\n                                  {\"Var1\", \"L1Norm\", \"Sum\"},\n                                  {\"Var1\", \"L1IntegralNorm\", \"Sum\"},\n                                  {\"Var1\", \"L2Norm\", \"Sum\"},\n                                  {\"Var1\", \"L2IntegralNorm\", \"Sum\"},\n                                  {\"Var1\", \"VolumeIntegral\", \"Sum\"},\n                                  {\"Var1\", \"L1Norm\", \"Individual\"},\n                                  {\"Var1\", \"L1IntegralNorm\", \"Individual\"},\n                                  {\"Var1\", \"L2Norm\", \"Individual\"},\n                                  {\"Var1\", \"L2IntegralNorm\", \"Individual\"},\n                                  {\"Var1\", \"VolumeIntegral\", \"Individual\"},\n                                  {\"Var1\", \"Min\", \"Sum\"}}}),\n                         Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto, \"Section0\");\n\n  INFO(\"create/serialize\");\n  register_factory_classes_with_charm<Metavariables<3, void>>();\n  const auto factory_event = TestHelpers::test_creation<std::unique_ptr<Event>,\n                                                        Metavariables<3, void>>(\n      // [input_file_examples]\n      R\"(\n  ObserveNorms:\n    SubfileName: reduction0\n    TensorsToObserve:\n    - Name: Var0\n      NormType: Max\n      Components: Individual\n    - Name: Var1\n      NormType: Min\n      Components: Individual\n    - Name: Var0\n      NormType: Max\n      Components: Sum\n    - Name: Var0TimesTwo\n      NormType: Max\n      Components: Individual\n    - Name: Var0TimesThree\n      NormType: Max\n      Components: Individual\n    - Name: Var1\n      NormType: L1Norm\n      Components: Sum\n    - Name: Var1\n      NormType: L1IntegralNorm\n      Components: Sum\n    - Name: Var1\n      NormType: L2Norm\n      Components: Sum\n    - Name: Var1\n      NormType: L2IntegralNorm\n      Components: Sum\n    - Name: Var1\n      NormType: VolumeIntegral\n      Components: Sum\n    - Name: Var1\n      NormType: L1Norm\n      Components: Individual\n    - Name: Var1\n      NormType: L1IntegralNorm\n      Components: Individual\n    - Name: Var1\n      NormType: L2Norm\n      Components: Individual\n    - Name: Var1\n      NormType: L2IntegralNorm\n      Components: Individual\n    - Name: Var1\n      NormType: VolumeIntegral\n      Components: Individual\n    - Name: Var1\n      NormType: Min\n      Components: Sum\n        )\");\n  // [input_file_examples]\n  auto serialized_event = serialize_and_deserialize(factory_event);\n  test<void>(std::move(serialized_event), Spectral::Basis::Legendre,\n             Spectral::Quadrature::GaussLobatto, std::nullopt);\n\n  {\n    INFO(\"Test option name\");\n    // Test option name\n    TestHelpers::test_creation<std::unique_ptr<Event>,\n                               Metavariables<3, void, ObserveMyNorms>>(\n        R\"(\n  ObserveMyNorms:\n    SubfileName: reduction0\n    TensorsToObserve:\n    - Name: Var0\n      NormType: Max\n      Components: Individual\n        )\");\n  }\n\n  test<void>(std::make_unique<ObserveNormsEvent<void>>(ObserveNormsEvent<void>{\n                 \"reduction0\",\n                 {{\"Var0\", \"Max\", \"Individual\"},\n                  {\"Var1\", \"Min\", \"Individual\"},\n                  {\"Var0\", \"Max\", \"Sum\"},\n                  {\"Var0TimesTwo\", \"Max\", \"Individual\"},\n                  {\"Var0TimesThree\", \"Max\", \"Individual\"},\n                  {\"Var1\", \"L1Norm\", \"Sum\"},\n                  {\"Var1\", \"L1IntegralNorm\", \"Sum\"},\n                  {\"Var1\", \"L2Norm\", \"Sum\"},\n                  {\"Var1\", \"L2IntegralNorm\", \"Sum\"},\n                  {\"Var1\", \"VolumeIntegral\", \"Sum\"},\n                  {\"Var1\", \"L1Norm\", \"Individual\"},\n                  {\"Var1\", \"L1IntegralNorm\", \"Individual\"},\n                  {\"Var1\", \"L2Norm\", \"Individual\"},\n                  {\"Var1\", \"L2IntegralNorm\", \"Individual\"},\n                  {\"Var1\", \"VolumeIntegral\", \"Individual\"},\n                  {\"Var1\", \"Min\", \"Sum\"}}}),\n             Spectral::Basis::FiniteDifference,\n             Spectral::Quadrature::CellCentered, std::nullopt);\n\n  // Test that L1Norm and L1IntegralNorm correctly take absolute values by using\n  // a tensor with all-negative components. If abs() were dropped entirely, the\n  // computed values would be negative rather than positive.\n  {\n    INFO(\"Negative values test\");\n    using metavariables = Metavariables<3, void>;\n    using element_component = ElementComponent<metavariables>;\n    using observer_component = MockObserverComponent<metavariables>;\n    const typename element_component::array_index array_index(0);\n    const Mesh<3> mesh{3, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const size_t num_points = mesh.number_of_grid_points();\n    // det_inv_jacobian for a unit cube: volume = 1\n    const Scalar<DataVector> det_inv_jacobian(num_points, cube(2.0));\n    Variables<tmpl::list<Var0, Var1>> vars(num_points);\n    // Fill Var0 with all -1.0; abs should give 1.0 for every point\n    get(get<Var0>(vars)) = DataVector(num_points, -1.0);\n    get<0>(get<Var1>(vars)) = DataVector(num_points, 0.0);\n    get<1>(get<Var1>(vars)) = DataVector(num_points, 0.0);\n    get<2>(get<Var1>(vars)) = DataVector(num_points, 0.0);\n\n    ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n    ActionTesting::emplace_component<element_component>(make_not_null(&runner),\n                                                        array_index);\n    ActionTesting::emplace_group_component<observer_component>(&runner);\n\n    auto box = db::create<\n        db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<metavariables>,\n                          ::Events::Tags::ObserverMesh<3>,\n                          ::Events::Tags::ObserverDetInvJacobian<\n                              Frame::ElementLogical, Frame::Inertial>,\n                          Tags::Variables<typename decltype(vars)::tags_list>,\n                          observers::Tags::ObservationKey<void>>>(\n        metavariables{}, mesh, det_inv_jacobian, vars,\n        std::optional<std::string>{std::nullopt});\n\n    const auto observe = std::make_unique<ObserveNormsEvent<void>>(\n        ObserveNormsEvent<void>{\"reduction0\",\n                                {{\"Var0\", \"L1Norm\", \"Individual\"},\n                                 {\"Var0\", \"L1IntegralNorm\", \"Individual\"}}});\n\n    auto obs_box = make_observation_box<tmpl::filter<\n        typename ObserveNormsEvent<void>::compute_tags_for_observation_box,\n        db::is_compute_tag<tmpl::_1>>>(make_not_null(&box));\n    observe->run(make_not_null(&obs_box),\n                 ActionTesting::cache<element_component>(runner, array_index),\n                 array_index, std::add_pointer_t<element_component>{},\n                 {\"TimeName\", 2.0});\n\n    ActionTesting::invoke_queued_simple_action<observer_component>(\n        make_not_null(&runner), 0);\n\n    const auto& results = MockContributeReductionData::results;\n    // L1Norm = (1/N) * sum(|u_i|) = 1.0 (sum over finalize reduction divides\n    // by num_points). L1IntegralNorm = integral(|u|)/V = 1.0 (since all |-1|=1\n    // and V=1). Both should be 1.0, not -1.0, proving abs() is applied.\n    CHECK(results.l1_norm_values[0] == approx(1.0));\n    CHECK(results.l1_integral_norm_values[0] == approx(1.0));\n  }\n\n  // varrying `Spherical` to test both spherical and axial symmetry, as well\n  // as changing whether we include x=0\n  test_cartoon<true, void>(\n      std::make_unique<ObserveNormsEvent<void>>(\n          ObserveNormsEvent<void>{\"reduction0\",\n                                  {{\"Var1\", \"L1IntegralNorm\", \"Sum\"},\n                                   {\"Var1\", \"L1IntegralNorm\", \"Individual\"},\n                                   {\"Var1\", \"L2IntegralNorm\", \"Sum\"},\n                                   {\"Var1\", \"L2IntegralNorm\", \"Individual\"},\n                                   {\"Var1\", \"VolumeIntegral\", \"Sum\"},\n                                   {\"Var1\", \"VolumeIntegral\", \"Individual\"}}}),\n      std::nullopt, 0.0);\n  test_cartoon<true, void>(\n      std::make_unique<ObserveNormsEvent<void>>(\n          ObserveNormsEvent<void>{\"reduction0\",\n                                  {{\"Var1\", \"L1IntegralNorm\", \"Sum\"},\n                                   {\"Var1\", \"L1IntegralNorm\", \"Individual\"},\n                                   {\"Var1\", \"L2IntegralNorm\", \"Sum\"},\n                                   {\"Var1\", \"L2IntegralNorm\", \"Individual\"},\n                                   {\"Var1\", \"VolumeIntegral\", \"Sum\"},\n                                   {\"Var1\", \"VolumeIntegral\", \"Individual\"}}}),\n      std::nullopt, 0.5);\n  test_cartoon<false, void>(\n      std::make_unique<ObserveNormsEvent<void>>(\n          ObserveNormsEvent<void>{\"reduction0\",\n                                  {{\"Var1\", \"L1IntegralNorm\", \"Sum\"},\n                                   {\"Var1\", \"L1IntegralNorm\", \"Individual\"},\n                                   {\"Var1\", \"L2IntegralNorm\", \"Sum\"},\n                                   {\"Var1\", \"L2IntegralNorm\", \"Individual\"},\n                                   {\"Var1\", \"VolumeIntegral\", \"Sum\"},\n                                   {\"Var1\", \"VolumeIntegral\", \"Individual\"}}}),\n      std::nullopt, 0.0);\n  test_cartoon<false, void>(\n      std::make_unique<ObserveNormsEvent<void>>(\n          ObserveNormsEvent<void>{\"reduction0\",\n                                  {{\"Var1\", \"L1IntegralNorm\", \"Sum\"},\n                                   {\"Var1\", \"L1IntegralNorm\", \"Individual\"},\n                                   {\"Var1\", \"L2IntegralNorm\", \"Sum\"},\n                                   {\"Var1\", \"L2IntegralNorm\", \"Individual\"},\n                                   {\"Var1\", \"VolumeIntegral\", \"Sum\"},\n                                   {\"Var1\", \"VolumeIntegral\", \"Individual\"}}}),\n      std::nullopt, 1.5);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_ObserveTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/Observer/Actions/RegisterEvents.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Protocols/ReductionDataFormatter.hpp\"\n#include \"IO/Observer/TypeOfObservation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Parallel/Reduction.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStep.tpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\nnamespace observers::Actions {\nstruct ContributeReductionData;\n}  // namespace observers::Actions\n\nnamespace {\nstatic_assert(\n    tt::assert_conforms_to_v<Events::detail::FormatTimeOutput,\n                             observers::protocols::ReductionDataFormatter>);\n\ntemplate <typename Metavariables>\nstruct MockContributeReductionData {\n  using ReductionData = tmpl::wrap<\n      tmpl::front<typename Events::ObserveTimeStep<\n          typename Metavariables::system>::observed_reduction_data_tags>,\n      Parallel::ReductionData>;\n  struct Results {\n    observers::ObservationId observation_id;\n    std::string subfile_name;\n    std::vector<std::string> reduction_names;\n    ReductionData reduction_data;\n    bool formatter_is_set{};\n    bool observe_per_core{};\n  };\n\n  // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n  static std::optional<Results> results;\n\n  template <typename ParallelComponent, typename... DbTags, typename ArrayIndex,\n            typename Formatter>\n  static void apply(db::DataBox<tmpl::list<DbTags...>>& /*box*/,\n                    Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const ArrayIndex& /*array_index*/,\n                    const observers::ObservationId& observation_id,\n                    Parallel::ArrayComponentId /*sender_array_id*/,\n                    const std::string& subfile_name,\n                    const std::vector<std::string>& reduction_names,\n                    ReductionData&& reduction_data,\n                    std::optional<Formatter>&& formatter,\n                    const bool observe_per_core) {\n    if (results) {\n      CHECK(results->observation_id == observation_id);\n      CHECK(results->subfile_name == subfile_name);\n      CHECK(results->reduction_names == reduction_names);\n      CHECK(results->formatter_is_set == formatter.has_value());\n      CHECK(results->observe_per_core == observe_per_core);\n      results->reduction_data.combine(std::move(reduction_data));\n    } else {\n      results.emplace();\n      *results = {observation_id,        subfile_name,\n                  reduction_names,       std::move(reduction_data),\n                  formatter.has_value(), observe_per_core};\n    }\n\n    if (formatter.has_value()) {\n      const auto formatted_msg =\n          (*formatter)(0.123, 3, 1.560, 3.141, 2.7818, 1023.3, 9.32, 4.148);\n      CHECK(formatted_msg ==\n            \"Simulation time: 0.123000\\n\"\n            \"  Wall time: 00:00:09 (min) - 00:00:04 (max)\\n\");\n    }\n  }\n};\n\ntemplate <typename Metavariables>\nstd::optional<typename MockContributeReductionData<Metavariables>::Results>\n    // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\n    MockContributeReductionData<Metavariables>::results{};\n\ntemplate <typename Metavariables>\nstruct ElementComponent {\n  using component_being_mocked = void;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\ntemplate <typename Metavariables>\nstruct MockObserverComponent {\n  using component_being_mocked = observers::Observer<Metavariables>;\n  using replace_these_simple_actions =\n      tmpl::list<observers::Actions::ContributeReductionData>;\n  using with_these_simple_actions =\n      tmpl::list<MockContributeReductionData<Metavariables>>;\n\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockGroupChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n};\n\nstruct Var : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct System {\n  using variables_tag = Tags::Variables<tmpl::list<Var>>;\n};\n\nstruct Metavariables {\n  using system = System;\n  using component_list = tmpl::list<ElementComponent<Metavariables>,\n                                    MockObserverComponent<Metavariables>>;\n  using const_global_cache_tags = tmpl::list<>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        Event,\n        tmpl::list<Events::ObserveTimeStep<typename Metavariables::system>>>>;\n  };\n};\n\ntemplate <typename Observer>\nvoid test_observe(const Observer& observer, const bool backwards_in_time,\n                  const bool observe_per_core = false) {\n  using element_component = ElementComponent<Metavariables>;\n  using observer_component = MockObserverComponent<Metavariables>;\n\n  auto& results = MockContributeReductionData<Metavariables>::results;\n  results.reset();\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n  ActionTesting::emplace_group_component<observer_component>(&runner);\n\n  const double observation_time = 2.0;\n  const Slab slab(1.23, 4.56);\n\n  using tag_list = tmpl::list<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                              Tags::TimeStep, System::variables_tag>;\n  std::vector<db::compute_databox_type<tag_list>> element_boxes;\n\n  const auto create_element = [&backwards_in_time, &element_boxes, &observer,\n                               &runner,\n                               &slab](const size_t num_points,\n                                      TimeDelta::rational_t slab_fraction) {\n    if (backwards_in_time) {\n      slab_fraction *= -1;\n    }\n    auto box =\n        db::create<tag_list>(Metavariables{}, slab.duration() * slab_fraction,\n                             System::variables_tag::type(num_points));\n\n    const auto ids_to_register =\n        observers::get_registration_observation_type_and_key(observer, box);\n    CHECK(ids_to_register->first == observers::TypeOfObservation::Reduction);\n    CHECK(ids_to_register->second ==\n          observers::ObservationKey(\"/time_step_subfile.dat\"));\n\n    element_boxes.push_back(std::move(box));\n\n    ActionTesting::emplace_component<element_component>(\n        &runner, element_boxes.size() - 1);\n  };\n\n  create_element(5, {1, 20});\n  create_element(30, {1, 2});\n  create_element(10, {1, 2});\n  const size_t expected_num_points = 45;\n  const double expected_slab_size = slab.duration().value();\n  const double expected_min_step = expected_slab_size / 20.0;\n  const double expected_max_step = expected_slab_size / 2.0;\n  const double expected_effective_step = expected_slab_size / 4.0;\n\n  for (size_t index = 0; index < element_boxes.size(); ++index) {\n    CHECK(static_cast<const Event&>(observer).is_ready(\n        element_boxes[index],\n        ActionTesting::cache<element_component>(runner, index),\n        static_cast<element_component::array_index>(index),\n        std::add_pointer_t<element_component>{}));\n    auto obs_box = make_observation_box<db::AddComputeTags<>>(\n        make_not_null(&element_boxes[index]));\n    observer.run(make_not_null(&obs_box),\n                 ActionTesting::cache<element_component>(runner, index),\n                 static_cast<element_component::array_index>(index),\n                 std::add_pointer_t<element_component>{},\n                 {\"TimeName\", observation_time});\n  }\n\n  // Process the data\n  for (size_t i = 0; i < element_boxes.size(); ++i) {\n    REQUIRE(\n        not runner.template is_simple_action_queue_empty<observer_component>(\n            0));\n    runner.template invoke_queued_simple_action<observer_component>(0);\n  }\n  CHECK(runner.template is_simple_action_queue_empty<observer_component>(0));\n\n  REQUIRE(results);\n  auto& reduction_data = results->reduction_data;\n  reduction_data.finalize();\n\n  CHECK(results->observation_id.value() == observation_time);\n  CHECK(results->subfile_name == \"/time_step_subfile\");\n  CHECK(results->reduction_names[0] == \"TimeName\");\n  CHECK(std::get<0>(reduction_data.data()) == observation_time);\n  CHECK(results->reduction_names[1] == \"NumberOfPoints\");\n  CHECK(std::get<1>(reduction_data.data()) == expected_num_points);\n  CHECK(results->reduction_names[2] == \"Slab size\");\n  CHECK(std::get<2>(reduction_data.data()) == expected_slab_size);\n  CHECK(results->reduction_names[3] == \"Minimum time step\");\n  CHECK(std::get<3>(reduction_data.data()) == expected_min_step);\n  CHECK(results->reduction_names[4] == \"Maximum time step\");\n  CHECK(std::get<4>(reduction_data.data()) == expected_max_step);\n  CHECK(results->reduction_names[5] == \"Effective time step\");\n  CHECK(std::get<5>(reduction_data.data()) == approx(expected_effective_step));\n  CHECK(results->reduction_names[6] == \"Minimum Walltime\");\n  CHECK(results->reduction_names[7] == \"Maximum Walltime\");\n  CHECK(results->observe_per_core == observe_per_core);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.ObserveTimeStep\", \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  for (const bool print_to_terminal : {true, false}) {\n    const Events::ObserveTimeStep<typename Metavariables::system> observer(\n        \"time_step_subfile\", print_to_terminal, false);\n    CHECK(not observer.needs_evolved_variables());\n    test_observe(observer, false);\n    test_observe(observer, true);\n    test_observe(serialize_and_deserialize(observer), false);\n    test_observe(serialize_and_deserialize(observer), true);\n\n    const auto event =\n        TestHelpers::test_creation<std::unique_ptr<Event>, Metavariables>(\n            \"ObserveTimeStep:\\n\"\n            \"  SubfileName: time_step_subfile\\n\"\n            \"  PrintTimeToTerminal: \" +\n            std::string(print_to_terminal ? \"true\" : \"false\") +\n            \"\\n\"\n            \"  ObservePerCore: False\");\n    test_observe(*event, false);\n    test_observe(*event, true);\n    test_observe(*serialize_and_deserialize(event), false);\n    test_observe(*serialize_and_deserialize(event), true);\n  }\n  {\n    INFO(\"Observe per-core\");\n    const Events::ObserveTimeStep<typename Metavariables::system> observer(\n        \"time_step_subfile\", false, true);\n    test_observe(observer, false, true);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_ObserveTimeStepVolume.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <utility>\n#include <variant>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/TimeDependent/Translation.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Domain/MinimumGridSpacing.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/SegmentId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Helpers/ParallelAlgorithms/Events/ObserveFields.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/ArrayComponentId.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.hpp\"\n#include \"ParallelAlgorithms/Events/ObserveTimeStepVolume.tpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Frame {\nstruct BlockLogical;\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace {\ntemplate <size_t VolumeDim>\nstruct System {\n  constexpr static size_t volume_dim = VolumeDim;\n  using variables_tag =\n      Tags::Variables<tmpl::list<TestHelpers::Tags::Vector<>>>;\n};\n\ntemplate <size_t VolumeDim>\nstruct Metavariables {\n  static constexpr size_t volume_dim = VolumeDim;\n  using system = System<volume_dim>;\n  using component_list = tmpl::list<\n      TestHelpers::dg::Events::ObserveFields::ElementComponent<Metavariables>,\n      TestHelpers::dg::Events::ObserveFields::MockObserverComponent<\n          Metavariables>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Event,\n                   tmpl::list<dg::Events::ObserveTimeStepVolume<system>>>,\n        tmpl::pair<Trigger, tmpl::list<Triggers::Always>>>;\n  };\n};\n\ntemplate <size_t VolumeDim>\nvoid test() {\n  using metavars = Metavariables<VolumeDim>;\n  using element_component =\n      TestHelpers::dg::Events::ObserveFields::ElementComponent<metavars>;\n  using observer_component =\n      TestHelpers::dg::Events::ObserveFields::MockObserverComponent<metavars>;\n  element_component* const element_component_p = nullptr;\n\n  const ElementId<VolumeDim> element_id(0,\n                                        make_array<VolumeDim>(SegmentId(1, 0)));\n\n  using MockRuntimeSystem = ActionTesting::MockRuntimeSystem<metavars>;\n  MockRuntimeSystem runner{{}};\n  ActionTesting::emplace_component<element_component>(make_not_null(&runner),\n                                                      element_id);\n  ActionTesting::emplace_group_component<observer_component>(&runner);\n  auto& cache = ActionTesting::cache<element_component>(runner, element_id);\n\n  const auto events_and_triggers =\n      TestHelpers::test_creation<EventsAndTriggers, metavars>(\n          \"- Trigger: Always\\n\"\n          \"  Events:\\n\"\n          \"    - ObserveTimeStepVolume:\\n\"\n          \"        SubfileName: time_step\\n\"\n          \"        CoordinatesFloatingPointType: Double\\n\"\n          \"        FloatingPointType: Float\");\n\n  const double time = 3.0;\n  const auto time_step = Slab(4.0, 6.0).duration() / 4;\n  const size_t integration_order = 5;\n\n  Domain domain(make_vector(\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<VolumeDim>{})));\n  domain.inject_time_dependent_map_for_block(\n      0, domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n             domain::CoordinateMaps::TimeDependent::Translation<VolumeDim>(\n                 \"translation\")));\n\n  domain::FunctionsOfTimeMap functions_of_time{};\n  functions_of_time.emplace(\n      \"translation\",\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<1>>(\n          1.0,\n          std::array{DataVector(VolumeDim, 2.0), DataVector(VolumeDim, 5.0)},\n          4.0));\n  const double expected_offset = 2.0 + (time - 1.0) * 5.0;\n\n  using history_tag =\n      ::Tags::HistoryEvolvedVariables<typename metavars::system::variables_tag>;\n\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<metavars>, Tags::Time,\n      domain::Tags::FunctionsOfTime, domain::Tags::Domain<VolumeDim>,\n      Tags::TimeStep,\n      domain::Tags::MinimumGridSpacing<VolumeDim, Frame::Inertial>,\n      history_tag>>(metavars{}, time, std::move(functions_of_time),\n                    std::move(domain), time_step, 0.23,\n                    typename history_tag::type{integration_order});\n\n  const double observation_value = 1.23;\n\n  events_and_triggers.run_events(make_not_null(&box), cache, element_id,\n                                 element_component_p,\n                                 {\"value_name\", observation_value});\n\n  runner.template invoke_queued_simple_action<observer_component>(0);\n  CHECK(runner.template is_simple_action_queue_empty<observer_component>(0));\n\n  const auto& results =\n      TestHelpers::dg::Events::ObserveFields::MockContributeVolumeData::results;\n  CHECK(results.observation_id.value() == observation_value);\n  CHECK(results.observation_id.observation_key().tag() == \"/time_step.vol\");\n  CHECK(results.subfile_name == \"/time_step\");\n  CHECK(results.array_component_id ==\n        Parallel::make_array_component_id<element_component>(element_id));\n  CHECK(results.received_volume_data.element_name == get_output(element_id));\n  CHECK(results.received_volume_data.extents ==\n        std::vector<size_t>(VolumeDim, 2));\n  const auto& components = results.received_volume_data.tensor_components;\n  REQUIRE(components.size() == VolumeDim + 4);\n  for (const auto& component : components) {\n    std::visit(\n        [](const auto& data) { CHECK(data.size() == two_to_the(VolumeDim)); },\n        component.data);\n  }\n  {\n    // Element with SegmentId(1, 0)\n    const double expected_lower = -1.0 + expected_offset;\n    const double expected_upper = expected_offset;\n    CHECK(components[0].name == \"InertialCoordinates_x\");\n    std::visit(\n        [&](const auto& data) {\n          for (size_t i = 0; i < data.size(); ++i) {\n            CHECK(data[i] == (i % 2 < 1 ? expected_lower : expected_upper));\n          }\n        },\n        components[0].data);\n    if constexpr (VolumeDim >= 2) {\n      CHECK(components[1].name == \"InertialCoordinates_y\");\n      std::visit(\n          [&](const auto& data) {\n            for (size_t i = 0; i < data.size(); ++i) {\n              CHECK(data[i] == (i % 4 < 2 ? expected_lower : expected_upper));\n            }\n          },\n          components[1].data);\n    }\n    if constexpr (VolumeDim >= 3) {\n      CHECK(components[2].name == \"InertialCoordinates_z\");\n      std::visit(\n          [&](const auto& data) {\n            for (size_t i = 0; i < data.size(); ++i) {\n              CHECK(data[i] == (i % 8 < 4 ? expected_lower : expected_upper));\n            }\n          },\n          components[2].data);\n    }\n  }\n  CHECK(components[VolumeDim].name == \"Time step\");\n  std::visit(\n      [&](const auto& data) {\n        for (size_t i = 0; i < data.size(); ++i) {\n          CHECK(data[i] == time_step.value());\n        }\n      },\n      components[VolumeDim].data);\n  CHECK(components[VolumeDim + 1].name == \"Slab fraction\");\n  std::visit(\n      [&](const auto& data) {\n        for (size_t i = 0; i < data.size(); ++i) {\n          CHECK(data[i] == time_step.fraction().value());\n        }\n      },\n      components[VolumeDim + 1].data);\n  CHECK(components[VolumeDim + 2].name == \"Minimum grid spacing\");\n  std::visit(\n      [&](const auto& data) {\n        for (size_t i = 0; i < data.size(); ++i) {\n          CHECK(data[i] == 0.23f);\n        }\n      },\n      components[VolumeDim + 2].data);\n  CHECK(components[VolumeDim + 3].name == \"Integration order\");\n  std::visit(\n      [&](const auto& data) {\n        for (size_t i = 0; i < data.size(); ++i) {\n          CHECK(data[i] == integration_order);\n        }\n      },\n      components[VolumeDim + 3].data);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Events.ObserveTimeStepVolume\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Events/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nvoid test(const Mesh<Dim>& mesh) {\n  const tnsr::I<DataVector, Dim, Frame::Grid> grid_coords{5_st, 7.2};\n  const tnsr::I<DataVector, Dim, Frame::Inertial> inertial_coords{5_st, 7.2};\n  const InverseJacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial>\n      inv_jac{5_st, 9.3};\n  const Jacobian<DataVector, Dim, Frame::ElementLogical, Frame::Inertial> jac{\n      5_st, 10.3};\n  const Scalar<DataVector> det_inv_jac{5_st, 11.3};\n  const tnsr::I<DataVector, Dim, Frame::Inertial> mesh_velocity{5_st, 8.3};\n  const auto box = db::create<\n      db::AddSimpleTags<\n          domain::Tags::Mesh<Dim>, domain::Tags::Coordinates<Dim, Frame::Grid>,\n          domain::Tags::Coordinates<Dim, Frame::Inertial>,\n          domain::Tags::InverseJacobian<Dim, Frame::ElementLogical,\n                                        Frame::Inertial>,\n          domain::Tags::Jacobian<Dim, Frame::ElementLogical, Frame::Inertial>,\n          domain::Tags::DetInvJacobian<Frame::ElementLogical, Frame::Inertial>,\n          domain::Tags::MeshVelocity<Dim, Frame::Inertial>>,\n      db::AddComputeTags<\n          ::Events::Tags::ObserverMeshCompute<Dim>,\n          Events::Tags::ObserverCoordinatesCompute<Dim, Frame::Grid>,\n          Events::Tags::ObserverCoordinatesCompute<Dim, Frame::Inertial>,\n          Events::Tags::ObserverInverseJacobianCompute<\n              Dim, Frame::ElementLogical, Frame::Inertial>,\n          Events::Tags::ObserverJacobianCompute<Dim, Frame::ElementLogical,\n                                                Frame::Inertial>,\n          Events::Tags::ObserverDetInvJacobianCompute<Frame::ElementLogical,\n                                                      Frame::Inertial>,\n          Events::Tags::ObserverMeshVelocityCompute<Dim, Frame::Inertial>>>(\n      mesh, grid_coords, inertial_coords, inv_jac, jac, det_inv_jac,\n      std::optional{mesh_velocity});\n  CHECK(db::get<Events::Tags::ObserverMesh<Dim>>(box) == mesh);\n  CHECK(db::get<Events::Tags::ObserverCoordinates<Dim, Frame::Grid>>(box) ==\n        grid_coords);\n  CHECK(db::get<Events::Tags::ObserverCoordinates<Dim, Frame::Inertial>>(box) ==\n        inertial_coords);\n  CHECK(\n      db::get<Events::Tags::ObserverInverseJacobian<Dim, Frame::ElementLogical,\n                                                    Frame::Inertial>>(box) ==\n      inv_jac);\n  CHECK(db::get<Events::Tags::ObserverJacobian<Dim, Frame::ElementLogical,\n                                               Frame::Inertial>>(box) == jac);\n  CHECK(db::get<Events::Tags::ObserverDetInvJacobian<Frame::ElementLogical,\n                                                     Frame::Inertial>>(box) ==\n        det_inv_jac);\n  CHECK(db::get<Events::Tags::ObserverMeshVelocity<Dim, Frame::Inertial>>(\n            box) == mesh_velocity);\n  TestHelpers::db::test_simple_tag<Events::Tags::ObserverMesh<Dim>>(\n      \"ObserverMesh\");\n  TestHelpers::db::test_compute_tag<::Events::Tags::ObserverMeshCompute<Dim>>(\n      \"ObserverMesh\");\n  TestHelpers::db::test_simple_tag<\n      Events::Tags::ObserverCoordinates<Dim, Frame::Grid>>(\"GridCoordinates\");\n  TestHelpers::db::test_compute_tag<\n      Events::Tags::ObserverCoordinatesCompute<Dim, Frame::Grid>>(\n      \"GridCoordinates\");\n  TestHelpers::db::test_simple_tag<\n      Events::Tags::ObserverCoordinates<Dim, Frame::Inertial>>(\n      \"InertialCoordinates\");\n  TestHelpers::db::test_compute_tag<\n      Events::Tags::ObserverCoordinatesCompute<Dim, Frame::Inertial>>(\n      \"InertialCoordinates\");\n  TestHelpers::db::test_simple_tag<Events::Tags::ObserverInverseJacobian<\n      Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"InverseJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_compute_tag<\n      Events::Tags::ObserverInverseJacobianCompute<Dim, Frame::ElementLogical,\n                                                   Frame::Inertial>>(\n      \"InverseJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_simple_tag<Events::Tags::ObserverJacobian<\n      Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"Jacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_compute_tag<Events::Tags::ObserverJacobianCompute<\n      Dim, Frame::ElementLogical, Frame::Inertial>>(\n      \"Jacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_simple_tag<Events::Tags::ObserverDetInvJacobian<\n      Frame::ElementLogical, Frame::Inertial>>(\n      \"DetInvJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_compute_tag<Events::Tags::ObserverDetInvJacobianCompute<\n      Frame::ElementLogical, Frame::Inertial>>(\n      \"DetInvJacobian(ElementLogical,Inertial)\");\n  TestHelpers::db::test_simple_tag<\n      Events::Tags::ObserverMeshVelocity<Dim, Frame::Inertial>>(\n      \"InertialMeshVelocity\");\n  TestHelpers::db::test_compute_tag<\n      Events::Tags::ObserverMeshVelocityCompute<Dim, Frame::Inertial>>(\n      \"InertialMeshVelocity\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Events.Tags\", \"[Unit]\") {\n  test(Mesh<1>{3, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto});\n  test(Mesh<2>{3, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto});\n  test(Mesh<3>{3, Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndDenseTriggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EventsAndDenseTriggers\")\n\nset(LIBRARY_SOURCES\n  Test_EventsAndDenseTriggers.cpp\n  Test_Tags.cpp\n  )\n\nadd_subdirectory(DenseTriggers)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DenseTriggerHelpers\n  EventsAndDenseTriggers\n  EventsAndTriggers\n  Options\n  Parallel\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  DenseTriggers/Test_Filter.cpp\n  DenseTriggers/Test_Or.cpp\n  DenseTriggers/Test_Times.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Test_Filter.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n#include <sstream>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/TestTrigger.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Filter.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Times.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/Triggers/TimeCompares.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nclass TestTrigger : public Trigger {\n public:\n  TestTrigger() = default;\n  explicit TestTrigger(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestTrigger);  // NOLINT\n#pragma GCC diagnostic pop\n\n  struct Result {\n    using type = bool;\n    constexpr static Options::String help = \"Result\";\n  };\n\n  using options = tmpl::list<Result>;\n  constexpr static Options::String help = \"help\";\n\n  explicit TestTrigger(const bool result) : result_(result) {}\n\n  using argument_tags = tmpl::list<>;\n  bool operator()() const { return result_; }\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) override {\n    Trigger::pup(p);\n    p | result_;\n  }\n\n private:\n  bool result_{};\n};\n\nPUP::able::PUP_ID TestTrigger::my_PUP_ID = 0;  // NOLINT\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DenseTrigger,\n                   tmpl::list<DenseTriggers::Filter,\n                              TestHelpers::DenseTriggers::TestTrigger>>,\n        tmpl::pair<TimeSequence<double>,\n                   TimeSequences::all_time_sequences<double>>,\n        tmpl::pair<Trigger, tmpl::list<TestTrigger>>>;\n  };\n};\n\nvoid check(const std::optional<bool>& expected_is_triggered,\n           const std::optional<double>& expected_next_check,\n           const std::optional<bool>& dense_is_triggered,\n           const bool non_dense_is_triggered) {\n  using NotReady = TestHelpers::DenseTriggers::TestTrigger::NotReady;\n  std::stringstream creation_string;\n  creation_string << std::boolalpha;\n  creation_string << \"Filter:\\n\"\n                  << \"  Trigger:\\n\"\n                  << \"    TestTrigger:\\n\"\n                  << \"      IsTriggered: \"\n                  << NotReady::format(dense_is_triggered) << \"\\n\"\n                  << \"      NextCheck: \"\n                  << NotReady::format(expected_next_check) << \"\\n\"\n                  << \"  Filter:\\n\"\n                  << \"    TestTrigger:\\n\"\n                  << \"      Result: \" << non_dense_is_triggered;\n  CAPTURE(creation_string.str());\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<Metavariables>, ::Tags::Time>>(\n      Metavariables{}, 0.0);\n  Parallel::GlobalCache<Metavariables> cache{};\n  const int array_index = 0;\n  const void* component = nullptr;\n  const auto trigger = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<DenseTrigger>, Metavariables>(\n          creation_string.str()));\n  CHECK(trigger->is_triggered(make_not_null(&box), cache, array_index,\n                              component) == expected_is_triggered);\n  CHECK(trigger->next_check_time(make_not_null(&box), cache, array_index,\n                                 component) == expected_next_check);\n}\n\nstruct ExampleMetavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DenseTrigger, tmpl::list<DenseTriggers::Filter,\n                                                      DenseTriggers::Times>>,\n                  tmpl::pair<TimeSequence<double>,\n                             TimeSequences::all_time_sequences<double>>,\n                  tmpl::pair<Trigger, tmpl::list<Triggers::TimeCompares>>>;\n  };\n};\n\nvoid example() {\n  const std::string input = R\"(\n# [example]\nFilter:\n  Trigger:\n    Times:\n      EvenlySpaced:\n        Offset: 0\n        Interval: 10\n  Filter:\n    TimeCompares:\n      Comparison: GreaterThanOrEqualTo\n      Value: 100\n# [example]\n)\";\n  const auto trigger = TestHelpers::test_creation<std::unique_ptr<DenseTrigger>,\n                                                  ExampleMetavariables>(input);\n  const auto run_trigger = [&trigger](const double time) {\n    auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<ExampleMetavariables>, ::Tags::Time,\n        ::Tags::TimeStepId>>(ExampleMetavariables{}, time,\n                             TimeStepId(true, 0, Slab(0., 1.).start()));\n    Parallel::GlobalCache<Metavariables> cache{};\n    const int array_index = 0;\n    const void* component = nullptr;\n    return *trigger->is_triggered(make_not_null(&box), cache, array_index,\n                                  component);\n  };\n  CHECK(not run_trigger(90.));\n  CHECK(not run_trigger(95.));\n  CHECK(run_trigger(100.));\n  CHECK(not run_trigger(105.));\n  CHECK(run_trigger(110.));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.EventsAndDenseTriggers.DenseTriggers.Filter\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  for (const auto& next_check : {std::optional{3.5}, std::optional<double>{}}) {\n    check(std::nullopt, next_check, std::nullopt, false);\n    check(std::nullopt, next_check, std::nullopt, true);\n    check(false, next_check, false, false);\n    check(false, next_check, false, true);\n    check(false, next_check, true, false);\n    check(true, next_check, true, true);\n  }\n\n  example();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Test_Or.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <initializer_list>\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/TestTrigger.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Or.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        DenseTrigger, tmpl::list<DenseTriggers::Or,\n                                 TestHelpers::DenseTriggers::TestTrigger>>>;\n  };\n};\n\nvoid check(const bool time_runs_forward,\n           const std::optional<bool>& expected_is_triggered,\n           const std::optional<double>& expected_next_check_time,\n           const std::string& creation_string) {\n  CAPTURE(creation_string);\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::TimeStepId, Tags::Time>>(\n      Metavariables{}, TimeStepId(time_runs_forward, 0, Slab(0.0, 1.0).start()),\n      0.0);\n  Parallel::GlobalCache<Metavariables> cache{};\n  const int array_index = 0;\n  const void* component = nullptr;\n  const auto trigger = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<DenseTrigger>, Metavariables>(\n          creation_string));\n\n  CHECK(trigger->is_triggered(make_not_null(&box), cache, array_index,\n                              component) == expected_is_triggered);\n  CHECK(trigger->next_check_time(make_not_null(&box), cache, array_index,\n                                 component) == expected_next_check_time);\n}\n\nvoid check_permutations(const std::optional<bool>& expected_is_triggered,\n                        const std::optional<double>& expected_next_check,\n                        std::vector<std::optional<bool>> is_triggered,\n                        std::vector<std::optional<double>> next_checks) {\n  alg::sort(is_triggered);\n  alg::sort(next_checks);\n  do {\n    do {\n      for (const bool time_runs_forward : {true, false}) {\n        const double sign = time_runs_forward ? 1.0 : -1.0;\n        const auto set_sign =\n            [&sign](const std::optional<double>& arg) -> std::optional<double> {\n          if (arg.has_value()) {\n            return std::optional{sign * *arg};\n          } else {\n            return std::nullopt;\n          }\n        };\n        using NotReady = TestHelpers::DenseTriggers::TestTrigger::NotReady;\n        std::stringstream creation;\n        creation << std::boolalpha;\n        creation << \"Or:\\n\";\n        for (size_t i = 0; i < is_triggered.size(); ++i) {\n          creation << \"  - TestTrigger:\\n\"\n                   << \"      IsTriggered: \" << NotReady::format(is_triggered[i])\n                   << \"\\n\"\n                   << \"      NextCheck: \"\n                   << NotReady::format(set_sign(next_checks[i])) << \"\\n\";\n        }\n        check(time_runs_forward, expected_is_triggered,\n              set_sign(expected_next_check), creation.str());\n      }\n    } while (cpp20::next_permutation(next_checks.begin(), next_checks.end()));\n  } while (cpp20::next_permutation(is_triggered.begin(), is_triggered.end()));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.EventsAndDenseTriggers.DenseTriggers.Or\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  check_permutations(false, std::numeric_limits<double>::infinity(), {}, {});\n\n  {\n    const std::initializer_list<\n        std::pair<std::optional<bool>, std::vector<std::optional<bool>>>>\n        is_triggered_tests{\n            {std::nullopt, {std::nullopt}}, {false, {false}}, {true, {true}}};\n    const std::initializer_list<\n        std::pair<std::optional<double>, std::vector<std::optional<double>>>>\n        next_check_time_tests{{std::nullopt, {std::nullopt}}, {5.0, {5.0}}};\n    for (const auto& is_triggered_test : is_triggered_tests) {\n      for (const auto& next_check_time_test : next_check_time_tests) {\n        check_permutations(is_triggered_test.first, next_check_time_test.first,\n                           is_triggered_test.second,\n                           next_check_time_test.second);\n      }\n    }\n  }\n\n  {\n    const std::initializer_list<\n        std::pair<std::optional<bool>, std::vector<std::optional<bool>>>>\n        is_triggered_tests{{std::nullopt, {std::nullopt, std::nullopt}},\n                           {std::nullopt, {std::nullopt, false}},\n                           {true, {std::nullopt, true}},\n                           {false, {false, false}},\n                           {true, {false, true}},\n                           {true, {true, true}}};\n    const std::initializer_list<\n        std::pair<std::optional<double>, std::vector<std::optional<double>>>>\n        next_check_time_tests{{std::nullopt, {std::nullopt, std::nullopt}},\n                              {std::nullopt, {std::nullopt, 5.0}},\n                              {5.0, {5.0, 10.0}}};\n    for (const auto& is_triggered_test : is_triggered_tests) {\n      for (const auto& next_check_time_test : next_check_time_tests) {\n        check_permutations(is_triggered_test.first, next_check_time_test.first,\n                           is_triggered_test.second,\n                           next_check_time_test.second);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Test_Times.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <memory>\n#include <sstream>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/Times.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DenseTrigger, tmpl::list<DenseTriggers::Times>>,\n                  tmpl::pair<TimeSequence<double>,\n                             TimeSequences::all_time_sequences<double>>>;\n  };\n};\n\nvoid check_one_direction(const std::vector<double>& trigger_times,\n                         const double current_time,\n                         const bool expected_is_triggered,\n                         const double expected_next_check,\n                         const bool time_runs_forward) {\n  CAPTURE(time_runs_forward);\n\n  std::stringstream creation_string;\n  creation_string.precision(std::numeric_limits<double>::max_digits10);\n  creation_string << \"Times:\\n\"\n                  << \"  Specified:\\n\"\n                  << \"    Values: [\";\n  for (auto time : trigger_times) {\n    creation_string << time << \",\";\n  }\n  creation_string << \"]\";\n  CAPTURE(creation_string.str());\n\n  const auto trigger = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<DenseTrigger>, Metavariables>(\n          creation_string.str()));\n\n  const Slab slab(1.0e10, 2.0e10);\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::TimeStepId, Tags::Time>>(\n      Metavariables{}, TimeStepId(time_runs_forward, 100, slab.start()),\n      current_time);\n  Parallel::GlobalCache<Metavariables> cache{};\n  const int array_index = 0;\n  const void* component = nullptr;\n\n  CHECK_FALSE(trigger->previous_trigger_time().has_value());\n  CHECK(trigger->is_triggered(make_not_null(&box), cache, array_index,\n                              component) == expected_is_triggered);\n  CHECK(trigger->next_check_time(make_not_null(&box), cache, array_index,\n                                 component) ==\n        std::optional{expected_next_check});\n  if (expected_is_triggered == std::optional{true}) {\n    CHECK_FALSE(trigger->previous_trigger_time().has_value());\n    db::mutate<::Tags::Time>(\n        [](const gsl::not_null<double*> time) { *time += 0.01; },\n        make_not_null(&box));\n    CHECK(trigger->is_triggered(make_not_null(&box), cache, array_index,\n                                component) == std::optional{false});\n    REQUIRE(trigger->previous_trigger_time().has_value());\n    CHECK(trigger->previous_trigger_time().value() == current_time);\n  } else {\n    CHECK_FALSE(trigger->previous_trigger_time().has_value());\n    db::mutate<::Tags::Time>(\n        [](const gsl::not_null<double*> time) { *time += 0.01; },\n        make_not_null(&box));\n    CHECK(trigger->is_triggered(make_not_null(&box), cache, array_index,\n                                component) == std::optional{false});\n    CHECK_FALSE(trigger->previous_trigger_time().has_value());\n  }\n}\n\nvoid check_both_directions(std::vector<double> trigger_times,\n                           const double current_time,\n                           const bool expected_is_triggered,\n                           const double expected_next_check) {\n  check_one_direction(trigger_times, current_time, expected_is_triggered,\n                      expected_next_check, true);\n  alg::for_each(trigger_times, [](double& t) { t = -t; });\n  check_one_direction(trigger_times, -current_time, expected_is_triggered,\n                      -expected_next_check, false);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.EventsAndDenseTriggers.DenseTriggers.Times\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto infinity = std::numeric_limits<double>::infinity();\n\n  check_both_directions({}, 1.0, false, infinity);\n\n  check_both_directions({1.7}, 1.6, false, 1.7);\n  check_both_directions({1.7}, 1.7, true, infinity);\n  check_both_directions({1.7}, 1.8, false, infinity);\n\n  check_both_directions({1.7, 1.9}, 1.6, false, 1.7);\n  check_both_directions({1.7, 1.9}, 1.7, true, 1.9);\n  check_both_directions({1.7, 1.9}, 1.8, false, 1.9);\n  check_both_directions({1.7, 1.9}, 1.9, true, infinity);\n  check_both_directions({1.7, 1.9}, 2.0, false, infinity);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndDenseTriggers/Test_EventsAndDenseTriggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/EventsAndDenseTriggers/DenseTriggers/TestTrigger.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/DenseTrigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/EventsAndDenseTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace TriggerLabels {\nstruct A {};\nstruct B {};\n}  // namespace TriggerLabels\n\nusing TriggerA = TestHelpers::DenseTriggers::BoxTrigger<TriggerLabels::A>;\nusing TriggerB = TestHelpers::DenseTriggers::BoxTrigger<TriggerLabels::B>;\n\nstruct AddTwoToTime : db::SimpleTag {\n  using type = double;\n};\n\nstruct AddTwoToTimeCompute : db::ComputeTag, AddTwoToTime {\n  using base = AddTwoToTime;\n  using return_type = double;\n  using argument_tags = tmpl::list<::Tags::Time>;\n  static void function(const gsl::not_null<double*> result, const double time) {\n    *result = time + 2.0;\n  }\n};\n\nstruct EventCount : db::SimpleTag {\n  using type = int;\n};\n\ntemplate <typename Label>\nclass TestEvent : public Event {\n public:\n  explicit TestEvent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n  WRAPPED_PUPable_decl_template(TestEvent);  // NOLINT\n\n  static std::string name() {\n    return \"TestEvent<\" + pretty_type::name<Label>() + \">\";\n  }\n\n  using options = tmpl::list<>;\n  static constexpr Options::String help = \"help\";\n\n  TestEvent() = default;\n\n  using compute_tags_for_observation_box = tmpl::list<AddTwoToTimeCompute>;\n\n  using return_tags = tmpl::list<EventCount>;\n  using argument_tags =\n      tmpl::list<Tags::Time, ::Tags::PreviousTriggerTime, AddTwoToTime>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  void operator()(const gsl::not_null<int*> event_count, const double time,\n                  const std::optional<double>& previous_trigger_time,\n                  const double time_plus_two,\n                  Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ArrayIndex& /*array_index*/,\n                  const Component* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    ++*event_count;\n    event_ran = true;\n    time_during_event = time;\n    CHECK(time_plus_two == time + 2.0);\n    previous_time_during_event = previous_trigger_time;\n    CHECK(observation_value.name == \"Time\");\n    CHECK(observation_value.value == time);\n  }\n\n  struct IsReady : db::SimpleTag {\n    using type = bool;\n  };\n\n  using is_ready_argument_tags = tmpl::list<IsReady>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(const bool ready,\n                Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return ready;\n  }\n\n  bool needs_evolved_variables() const override {\n    return Label::needs_evolved_variables;\n  }\n\n  static bool event_ran;\n  static double time_during_event;\n  static std::optional<double> previous_time_during_event;\n};\n\ntemplate <typename Label>\nbool TestEvent<Label>::event_ran = false;\ntemplate <typename Label>\ndouble TestEvent<Label>::time_during_event =\n    std::numeric_limits<double>::signaling_NaN();\ntemplate <typename Label>\nstd::optional<double> TestEvent<Label>::previous_time_during_event =\n    std::nullopt;\n\ntemplate <typename Label>\nPUP::able::PUP_ID TestEvent<Label>::my_PUP_ID = 0;  // NOLINT\n\nnamespace EventLabels {\nstruct A {\n  static constexpr bool needs_evolved_variables = false;\n};\nstruct B {\n  static constexpr bool needs_evolved_variables = false;\n};\nstruct C {\n  static constexpr bool needs_evolved_variables = true;\n};\n}  // namespace EventLabels\n\nusing EventA = TestEvent<EventLabels::A>;\nusing EventB = TestEvent<EventLabels::B>;\nusing EventC = TestEvent<EventLabels::C>;\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DenseTrigger, tmpl::list<TriggerA, TriggerB>>,\n                  tmpl::pair<Event, tmpl::list<EventA, EventB, EventC>>>;\n  };\n};\n\nvoid do_test(const bool time_runs_forward, const bool add_event) {\n  const double time_sign = time_runs_forward ? 1.0 : -1.0;\n\n  Parallel::GlobalCache<Metavariables> cache{};\n  const int array_index = 0;\n  const int* component = nullptr;\n\n  std::string creation_string =\n      \"- Trigger: BoxTrigger<A>\\n\"\n      \"  Events:\\n\"\n      \"    - TestEvent<A>\\n\";\n  if (not add_event) {\n    creation_string +=\n        \"- Trigger: BoxTrigger<B>\\n\"\n        \"  Events:\\n\"\n        \"    - TestEvent<B>\\n\"\n        \"    - TestEvent<C>\\n\";\n  }\n\n  auto events_and_dense_triggers =\n      TestHelpers::test_creation<EventsAndDenseTriggers, Metavariables>(\n          creation_string);\n  if (add_event) {\n    events_and_dense_triggers.add_trigger_and_events(\n        std::make_unique<TriggerB>(),\n        make_vector<std::unique_ptr<Event>>(\n            std::make_unique<TestEvent<EventLabels::B>>(),\n            std::make_unique<TestEvent<EventLabels::C>>()));\n  }\n\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::TimeStepId,\n      Tags::Time, ::Tags::PreviousTriggerTime, TriggerA::IsTriggered,\n      TriggerA::NextCheck, TriggerB::IsTriggered, TriggerB::NextCheck,\n      EventA::IsReady, EventB::IsReady, EventC::IsReady, EventCount>>(\n      Metavariables{}, TimeStepId(time_runs_forward, 0, Slab(0.0, 1.0).start()),\n      -1.0 * time_sign, std::optional<double>{}, std::optional<bool>{},\n      std::optional<double>{}, std::optional<bool>{}, std::optional<double>{},\n      false, false, false, 0);\n\n  const auto set_tag = [&box](auto tag_v, const auto value) {\n    using Tag = decltype(tag_v);\n    db::mutate<Tag>(\n        [&value](const gsl::not_null<typename Tag::type*> var) {\n          *var = value;\n        },\n        make_not_null(&box));\n  };\n\n  const auto check_events = [&](const bool expected_a, const bool expected_b,\n                                const bool expected_c) {\n    CHECK(EventA::event_ran == expected_a);\n    CHECK(EventB::event_ran == expected_b);\n    CHECK(EventC::event_ran == expected_c);\n    EventA::event_ran = false;\n    EventB::event_ran = false;\n    EventC::event_ran = false;\n    CHECK(db::get<EventCount>(box) == expected_a + expected_b + expected_c);\n    set_tag(EventCount{}, 0);\n  };\n\n  using TriggeringState = EventsAndDenseTriggers::TriggeringState;\n  CHECK(events_and_dense_triggers.next_trigger(box) == -1.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::NotReady);\n  set_tag(TriggerA::IsTriggered{}, false);\n  CHECK(events_and_dense_triggers.next_trigger(box) == -1.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::NotReady);\n  set_tag(TriggerB::IsTriggered{}, false);\n  CHECK(events_and_dense_triggers.next_trigger(box) == -1.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::Ready);\n  events_and_dense_triggers.run_events(box, cache, array_index, component);\n  check_events(false, false, false);\n  events_and_dense_triggers.run_events(box, cache, array_index, component);\n  check_events(false, false, false);\n\n  CHECK(not events_and_dense_triggers.reschedule(make_not_null(&box), cache,\n                                                 array_index, component));\n  set_tag(TriggerA::NextCheck{}, 2.0 * time_sign);\n  CHECK(events_and_dense_triggers.next_trigger(box) == -1.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::Ready);\n  events_and_dense_triggers.run_events(box, cache, array_index, component);\n  check_events(false, false, false);\n  CHECK(not events_and_dense_triggers.reschedule(make_not_null(&box), cache,\n                                                 array_index, component));\n  set_tag(TriggerB::NextCheck{}, 3.0 * time_sign);\n  CHECK(events_and_dense_triggers.next_trigger(box) == -1.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::Ready);\n  events_and_dense_triggers.run_events(box, cache, array_index, component);\n  check_events(false, false, false);\n  CHECK(events_and_dense_triggers.reschedule(make_not_null(&box), cache,\n                                             array_index, component));\n  CHECK(events_and_dense_triggers.next_trigger(box) == 2.0 * time_sign);\n  set_tag(Tags::Time{}, 2.0 * time_sign);\n  set_tag(TriggerA::IsTriggered{}, std::nullopt);\n  set_tag(TriggerB::IsTriggered{}, std::nullopt);\n  CHECK(events_and_dense_triggers.next_trigger(box) == 2.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::NotReady);\n  set_tag(TriggerA::IsTriggered{}, true);\n  set_tag(TriggerA::NextCheck{}, std::nullopt);\n  CHECK(events_and_dense_triggers.next_trigger(box) == 2.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::NotReady);\n  set_tag(EventA::IsReady{}, true);\n  CHECK(events_and_dense_triggers.next_trigger(box) == 2.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::Ready);\n  events_and_dense_triggers.run_events(box, cache, array_index, component);\n  check_events(true, false, false);\n  events_and_dense_triggers.run_events(box, cache, array_index, component);\n  check_events(false, false, false);\n  CHECK(EventA::time_during_event == 2.0 * time_sign);\n  CHECK(EventA::previous_time_during_event == std::nullopt);\n  CHECK(not events_and_dense_triggers.reschedule(make_not_null(&box), cache,\n                                                 array_index, component));\n  CHECK(events_and_dense_triggers.next_trigger(box) == 2.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::Ready);\n  events_and_dense_triggers.run_events(box, cache, array_index, component);\n  check_events(false, false, false);\n  set_tag(TriggerA::NextCheck{}, 3.0 * time_sign);\n  CHECK(events_and_dense_triggers.next_trigger(box) == 2.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::Ready);\n  events_and_dense_triggers.run_events(box, cache, array_index, component);\n  check_events(false, false, false);\n  CHECK(events_and_dense_triggers.reschedule(make_not_null(&box), cache,\n                                             array_index, component));\n  set_tag(TriggerA::NextCheck{}, 4.0 * time_sign);\n  set_tag(EventA::IsReady{}, false);\n  set_tag(TriggerB::IsTriggered{}, true);\n  set_tag(TriggerB::NextCheck{}, 4.0 * time_sign);\n  CHECK(events_and_dense_triggers.next_trigger(box) == 3.0 * time_sign);\n  set_tag(Tags::Time{}, 3.0 * time_sign);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::NotReady);\n  set_tag(EventB::IsReady{}, true);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::NotReady);\n  set_tag(EventC::IsReady{}, true);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::NotReady);\n  set_tag(EventA::IsReady{}, true);\n  CHECK(events_and_dense_triggers.is_ready(make_not_null(&box), cache,\n                                           array_index, component) ==\n        TriggeringState::NeedsEvolvedVariables);\n\n  const auto finish_checks = [&array_index, &box, &cache, &check_events,\n                              &component,\n                              &time_sign](EventsAndDenseTriggers eadt) {\n    EventA::event_ran = false;\n    CHECK(eadt.next_trigger(box) == 3.0 * time_sign);\n    CHECK(eadt.is_ready(make_not_null(&box), cache, array_index, component) ==\n          TriggeringState::NeedsEvolvedVariables);\n    // In a real executable we might need to wait for data to do\n    // dense output at this point.\n    CHECK(eadt.next_trigger(box) == 3.0 * time_sign);\n    CHECK(eadt.is_ready(make_not_null(&box), cache, array_index, component) ==\n          TriggeringState::NeedsEvolvedVariables);\n    eadt.run_events(box, cache, array_index, component);\n    check_events(true, true, true);\n    eadt.run_events(box, cache, array_index, component);\n    check_events(false, false, false);\n    CHECK(EventA::time_during_event == 3.0 * time_sign);\n    CHECK(EventA::previous_time_during_event.value() == 2.0 * time_sign);\n  };\n\n  finish_checks(serialize_and_deserialize(events_and_dense_triggers));\n  finish_checks(std::move(events_and_dense_triggers));\n}\n\nclass MutatingTrigger : public DenseTrigger {\n public:\n  MutatingTrigger() = default;\n  explicit MutatingTrigger(CkMigrateMessage* const msg) : DenseTrigger(msg) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(MutatingTrigger);  // NOLINT\n#pragma GCC diagnostic pop\n\n  struct WasCalled : db::SimpleTag {\n    using type = bool;\n  };\n\n  using is_triggered_return_tags = tmpl::list<WasCalled>;\n  using is_triggered_argument_tags = tmpl::list<>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<bool> is_triggered(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/,\n      const gsl::not_null<bool*> was_called) const {\n    *was_called = true;\n    return false;\n  }\n\n  using next_check_time_argument_tags = tmpl::list<>;\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  std::optional<double> next_check_time(\n      Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const Component* /*component*/) const {\n    return {};\n  }\n};\n\nPUP::able::PUP_ID MutatingTrigger::my_PUP_ID = 0;  // NOLINT\n\nstruct MutatingMetavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<DenseTrigger, tmpl::list<MutatingTrigger>>,\n                  tmpl::pair<Event, tmpl::list<EventA>>>;\n  };\n};\n\nvoid test_mutating_trigger() {\n  EventsAndDenseTriggers events_and_dense_triggers(\n      make_vector(EventsAndDenseTriggers::TriggerAndEvents{\n          std::make_unique<MutatingTrigger>(),\n          make_vector<std::unique_ptr<Event>>(\n              std::make_unique<TestEvent<EventLabels::A>>())}));\n\n  Parallel::GlobalCache<MutatingMetavariables> cache{};\n  const int array_index = 0;\n  const int* component = nullptr;\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<MutatingMetavariables>,\n      Tags::TimeStepId, Tags::Time, ::Tags::PreviousTriggerTime,\n      EventA::IsReady, MutatingTrigger::WasCalled>>(\n      MutatingMetavariables{}, TimeStepId(true, 0, Slab(0.0, 1.0).start()),\n      -1.0, std::optional<double>{}, false, false);\n\n  events_and_dense_triggers.next_trigger(box);\n  events_and_dense_triggers.is_ready(make_not_null(&box), cache, array_index,\n                                     component);\n  CHECK(db::get<MutatingTrigger::WasCalled>(box));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.EventsAndDenseTriggers\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  do_test(true, false);\n  do_test(false, false);\n  do_test(true, true);\n  do_test(false, true);\n  test_mutating_trigger();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndDenseTriggers/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/EventsAndDenseTriggers/Tags.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.EventsAndDenseTriggers.Tags\",\n                  \"[Unit][Evolution]\") {\n  TestHelpers::db::test_simple_tag<::Tags::EventsAndDenseTriggers>(\n      \"EventsAndDenseTriggers\");\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndTriggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EventsAndTriggers\")\n\nset(LIBRARY_SOURCES\n  Test_EventsAndTriggers.cpp\n  Test_RunEventsOnFailure.cpp\n  Test_Tags.cpp\n  Test_WhenToCheck.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Events\n  EventsAndTriggers\n  ErrorHandling\n  Parallel\n  Time\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndTriggers/Test_EventsAndTriggers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// This file checks EventsAndTriggers and the basic logical\n// triggers (Always, And, Not, and Or).\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nnamespace Tags {\nstruct Data : db::SimpleTag {\n  using type = int;\n};\n\nstruct Observation : db::SimpleTag {\n  using type = int;\n};\n\nstruct ObservationCompute : Observation, db::ComputeTag {\n  using base = Observation;\n  using argument_tags = tmpl::list<Data>;\n  static void function(const gsl::not_null<int*> observation, const int data) {\n    *observation = data + 1;\n  }\n};\n\nstruct RunCount : db::SimpleTag {\n  using type = int;\n};\n}  // namespace Tags\n\nstruct TestEvent : public Event {\n public:\n  explicit TestEvent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(TestEvent);  // NOLINT\n#pragma GCC diagnostic pop\n\n  using compute_tags_for_observation_box = tmpl::list<Tags::ObservationCompute>;\n  using options = tmpl::list<>;\n  static constexpr Options::String help = \"\";\n\n  TestEvent() = default;\n\n  using return_tags = tmpl::list<Tags::RunCount>;\n  using argument_tags = tmpl::list<Tags::Data, Tags::Observation>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  void operator()(const gsl::not_null<int*> run_count, const int data,\n                  const int observation,\n                  Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ArrayIndex& /*array_index*/,\n                  const Component* const /*meta*/,\n                  const ObservationValue& observation_value) const {\n    CHECK(data == 2);\n    CHECK(observation == 3);\n    CHECK(observation_value.name == \"Name\");\n    CHECK(observation_value.value == 1234.5);\n    ++*run_count;\n  }\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n};\n\nPUP::able::PUP_ID TestEvent::my_PUP_ID = 0;  // NOLINT\n\nstruct Component {};\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Event, tmpl::list<TestEvent>>,\n                  tmpl::pair<Trigger, Triggers::logical_triggers>>;\n  };\n};\n\nvoid run_events_and_triggers(const EventsAndTriggers& events_and_triggers,\n                             const int expected) {\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::Data, Tags::RunCount>>(Metavariables{}, 2, 0);\n  Event::ObservationValue observation_value{\"Name\", 1234.5};\n\n  Parallel::GlobalCache<Metavariables> cache{};\n  Component* const component_ptr = nullptr;\n\n  events_and_triggers.run_events(make_not_null(&box), cache, 0, component_ptr,\n                                 observation_value);\n  CHECK(db::get<Tags::RunCount>(box) == expected);\n\n  db::mutate<Tags::RunCount>(\n      [](const gsl::not_null<int*> run_count) { *run_count = 0; },\n      make_not_null(&box));\n  serialize_and_deserialize(events_and_triggers)\n      .run_events(make_not_null(&box), cache, 0, component_ptr,\n                  observation_value);\n  CHECK(db::get<Tags::RunCount>(box) == expected);\n}\n\nvoid check_trigger(const int expected, const std::string& trigger_string) {\n  auto trigger =\n      TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n          trigger_string);\n\n  EventsAndTriggers::Storage events_and_triggers_input;\n  events_and_triggers_input.push_back(\n      {std::move(trigger),\n       make_vector<std::unique_ptr<Event>>(std::make_unique<TestEvent>())});\n  const EventsAndTriggers events_and_triggers(\n      std::move(events_and_triggers_input));\n\n  run_events_and_triggers(events_and_triggers, expected);\n}\n\nvoid test_basic_triggers() {\n  check_trigger(1, \"Always\");\n  check_trigger(0, \"Not: Always\");\n  check_trigger(1,\n                \"Not:\\n\"\n                \"  Not: Always\");\n\n  check_trigger(1,\n                \"And:\\n\"\n                \"  - Always\\n\"\n                \"  - Always\");\n  check_trigger(0,\n                \"And:\\n\"\n                \"  - Always\\n\"\n                \"  - Not: Always\");\n  check_trigger(0,\n                \"And:\\n\"\n                \"  - Not: Always\\n\"\n                \"  - Always\");\n  check_trigger(0,\n                \"And:\\n\"\n                \"  - Not: Always\\n\"\n                \"  - Not: Always\");\n  check_trigger(0,\n                \"And:\\n\"\n                \"  - Always\\n\"\n                \"  - Always\\n\"\n                \"  - Not: Always\");\n\n  check_trigger(1,\n                \"Or:\\n\"\n                \"  - Always\\n\"\n                \"  - Always\");\n  check_trigger(1,\n                \"Or:\\n\"\n                \"  - Always\\n\"\n                \"  - Not: Always\");\n  check_trigger(1,\n                \"Or:\\n\"\n                \"  - Not: Always\\n\"\n                \"  - Always\");\n  check_trigger(0,\n                \"Or:\\n\"\n                \"  - Not: Always\\n\"\n                \"  - Not: Always\");\n  check_trigger(1,\n                \"Or:\\n\"\n                \"  - Not: Always\\n\"\n                \"  - Not: Always\\n\"\n                \"  - Always\");\n}\n\nvoid test_factory() {\n  const auto events_and_triggers =\n      TestHelpers::test_creation<EventsAndTriggers, Metavariables>(\n          \"- Trigger:\\n\"\n          \"    Not: Always\\n\"\n          \"  Events:\\n\"\n          \"    - TestEvent\\n\"\n          \"- Trigger:\\n\"\n          \"    Or:\\n\"\n          \"      - Not: Always\\n\"\n          \"      - Always\\n\"\n          \"  Events:\\n\"\n          \"    - TestEvent\\n\"\n          \"    - TestEvent\\n\"\n          \"- Trigger:\\n\"\n          \"    Not: Always\\n\"\n          \"  Events:\\n\"\n          \"    - TestEvent\\n\");\n\n  run_events_and_triggers(events_and_triggers, 2);\n}\n\nvoid test_custom_check_trigger() {\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::Data, Tags::RunCount>>(Metavariables{}, 2, 0);\n  Event::ObservationValue observation_value{\"Name\", 1234.5};\n\n  Parallel::GlobalCache<Metavariables> cache{};\n  Component* const component_ptr = nullptr;\n\n  const EventsAndTriggers always_eat = []() {\n    EventsAndTriggers::Storage events_and_triggers_input;\n    events_and_triggers_input.push_back(\n        {std::make_unique<Triggers::Always>(),\n         make_vector<std::unique_ptr<Event>>(std::make_unique<TestEvent>())});\n    return EventsAndTriggers(std::move(events_and_triggers_input));\n  }();\n  const EventsAndTriggers never_eat = []() {\n    EventsAndTriggers::Storage events_and_triggers_input;\n    events_and_triggers_input.push_back(\n        {std::make_unique<Triggers::Not>(std::make_unique<Triggers::Always>()),\n         make_vector<std::unique_ptr<Event>>(std::make_unique<TestEvent>())});\n    return EventsAndTriggers(std::move(events_and_triggers_input));\n  }();\n\n  db::mutate<Tags::RunCount>(\n      [](const gsl::not_null<int*> run_count) { *run_count = 0; },\n      make_not_null(&box));\n  always_eat.run_events(make_not_null(&box), cache, 0, component_ptr,\n                        observation_value);\n  CHECK(db::get<Tags::RunCount>(box) == 1);\n\n  db::mutate<Tags::RunCount>(\n      [](const gsl::not_null<int*> run_count) { *run_count = 0; },\n      make_not_null(&box));\n  never_eat.run_events(make_not_null(&box), cache, 0, component_ptr,\n                       observation_value);\n  CHECK(db::get<Tags::RunCount>(box) == 0);\n\n  db::mutate<Tags::RunCount>(\n      [](const gsl::not_null<int*> run_count) { *run_count = 0; },\n      make_not_null(&box));\n  always_eat.run_events(make_not_null(&box), cache, 0, component_ptr,\n                        observation_value,\n                        [](const Trigger& /*trigger*/) { return false; });\n  CHECK(db::get<Tags::RunCount>(box) == 0);\n\n  db::mutate<Tags::RunCount>(\n      [](const gsl::not_null<int*> run_count) { *run_count = 0; },\n      make_not_null(&box));\n  never_eat.run_events(make_not_null(&box), cache, 0, component_ptr,\n                       observation_value,\n                       [](const Trigger& /*trigger*/) { return false; });\n  CHECK(db::get<Tags::RunCount>(box) == 0);\n\n  db::mutate<Tags::RunCount>(\n      [](const gsl::not_null<int*> run_count) { *run_count = 0; },\n      make_not_null(&box));\n  always_eat.run_events(make_not_null(&box), cache, 0, component_ptr,\n                        observation_value,\n                        [](const Trigger& /*trigger*/) { return true; });\n  CHECK(db::get<Tags::RunCount>(box) == 1);\n\n  db::mutate<Tags::RunCount>(\n      [](const gsl::not_null<int*> run_count) { *run_count = 0; },\n      make_not_null(&box));\n  never_eat.run_events(make_not_null(&box), cache, 0, component_ptr,\n                       observation_value,\n                       [](const Trigger& /*trigger*/) { return true; });\n  CHECK(db::get<Tags::RunCount>(box) == 1);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.EventsAndTriggers\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  test_basic_triggers();\n  test_factory();\n  test_custom_check_trigger();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndTriggers/Test_RunEventsOnFailure.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Events/Completion.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Actions/RunEventsOnFailure.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using const_global_cache_tags =\n      Actions::RunEventsOnFailure<Tags::Time>::const_global_cache_tags;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Testing,\n      tmpl::list<Actions::RunEventsOnFailure<Tags::Time>>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component<Metavariables>>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Event, tmpl::list<Events::Completion>>>;\n  };\n};\n\nSPECTRE_TEST_CASE(\"Unit.Evolution.EventsAndTriggers.RunEventsOnFailure\",\n                  \"[Unit][Evolution]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  const std::vector<std::unique_ptr<Event>> events =\n      make_vector<std::unique_ptr<Event>>(\n          std::make_unique<Events::Completion>());\n\n  using my_component = Component<Metavariables>;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {serialize_and_deserialize(events), 1234.5}};\n  ActionTesting::emplace_component<my_component>(&runner, 0);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  ActionTesting::next_action<my_component>(make_not_null(&runner), 0);\n\n  CHECK(ActionTesting::get_terminate<my_component>(runner, 0) == true);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndTriggers/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.EventsAndTriggers.Tags\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  TestHelpers::db::test_simple_tag<\n      Tags::EventsAndTriggers<Triggers::WhenToCheck::AtIterations>>(\n      \"EventsAndTriggersAtIterations\");\n  TestHelpers::db::test_simple_tag<\n      Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n      \"EventsAndTriggersAtSlabs\");\n  TestHelpers::db::test_simple_tag<\n      Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSteps>>(\n      \"EventsAndTriggersAtSteps\");\n  TestHelpers::db::test_simple_tag<Tags::EventsRunAtCleanup>(\n      \"EventsRunAtCleanup\");\n  TestHelpers::db::test_simple_tag<Tags::EventsRunAtCleanupObservationValue>(\n      \"EventsRunAtCleanupObservationValue\");\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/EventsAndTriggers/Test_WhenToCheck.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.EventsAndTriggers.WhenToCheck\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  CHECK(get_output(Triggers::WhenToCheck::AtIterations) == \"AtIterations\");\n  CHECK(get_output(Triggers::WhenToCheck::AtSlabs) == \"AtSlabs\");\n  CHECK(get_output(Triggers::WhenToCheck::AtSteps) == \"AtSteps\");\n  CHECK(get_output(Triggers::WhenToCheck::AtCheckpoints) == \"AtCheckpoints\");\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Initialization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Initialization\")\n\nset(LIBRARY_SOURCES\n  Test_MutateAssign.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ErrorHandling\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Initialization/Test_MutateAssign.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/DataBoxTag.hpp\"\n#include \"ParallelAlgorithms/Initialization/MutateAssign.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Tag0 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Tag1 : db::SimpleTag {\n  using type = std::vector<double>;\n};\n\nstruct Tag2 : db::SimpleTag {\n  using type = std::string;\n};\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.Initialization.MutateAssign\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  auto box = db::create<db::AddSimpleTags<Tag0, Tag1, Tag2>>(\n      0.5, std::vector<double>{1.0, 2.0}, std::string(\"original string value\"));\n  Initialization::mutate_assign<tmpl::list<Tag0>>(make_not_null(&box), 1.2);\n  CHECK(db::get<Tag0>(box) == 1.2);\n  CHECK(db::get<Tag1>(box) == std::vector<double>{1.0, 2.0});\n  Initialization::mutate_assign<tmpl::list<Tag0, Tag1>>(\n      make_not_null(&box), 1.5, std::vector<double>{1.6, 2.5, 3.0});\n  CHECK(db::get<Tag0>(box) == 1.5);\n  CHECK(db::get<Tag1>(box) == std::vector<double>{1.6, 2.5, 3.0});\n  Initialization::mutate_assign<tmpl::list<Tag2>>(make_not_null(&box),\n                                                  \"new string value\");\n  CHECK(db::get<Tag2>(box) == \"new string value\");\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelInterpolation\")\n\nset(LIBRARY_SOURCES\n  Test_ComputeExcisionBoundaryVolumeQuantities.cpp\n  Test_ElementReceiveInterpPoints.cpp\n  Test_InitializeInterpolationTarget.cpp\n  Test_InterpolateWithoutInterpComponent.cpp\n  Test_InterpolationTargetKerrHorizon.cpp\n  Test_InterpolationTargetLineSegment.cpp\n  Test_InterpolationTargetVarsFromElement.cpp\n  Test_InterpolationTargetSpecifiedPoints.cpp\n  Test_InterpolationTargetSphere.cpp\n  Test_InterpolationTargetWedgeSectionTorus.cpp\n  Test_ObserveLineSegment.cpp\n  Test_ObserveTimeSeriesAndSurfaceData.cpp\n  Test_Protocols.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  ControlSystem\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  Events\n  GeneralRelativitySolutions\n  GrSurfaces\n  H5\n  Interpolation\n  InterpolationHelpers\n  IoTestHelpers\n  Logging\n  MathFunctions\n  Observer\n  ParallelInterpolation\n  RelativisticEulerSolutions\n  Spectral\n  SphericalHarmonics\n  SphericalHarmonicsIO\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_ComputeExcisionBoundaryVolumeQuantities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/Block.hpp\"\n#include \"Domain/CoordinateMaps/Composition.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.hpp\"\n#include \"ParallelAlgorithms/Interpolation/ComputeExcisionBoundaryVolumeQuantities.tpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeVarsToInterpolate.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename IsTimeDependent, typename TargetFrame, typename SrcTags,\n          typename DestTags>\nvoid test_compute_excision_boundary_volume_quantities() {\n  const bool is_time_dependent = IsTimeDependent::value;\n  const std::string target_frame = pretty_type::name<TargetFrame>();\n  CAPTURE(is_time_dependent);\n  CAPTURE(target_frame);\n  const size_t number_of_grid_points = 8;\n  // Because we are taking numerical derivatives and have low resolution\n  [[maybe_unused]] Approx deriv_approx =  // NOLINT\n      Approx::custom().epsilon(1.0e-8).scale(1.0);\n\n  // slab and temporal_id used only in the TimeDependent version.\n  Slab slab(0.0, 1.0);\n  TimeStepId temporal_id{true, 0, Time(slab, Rational(13, 15))};\n\n  // Create a brick offset from the origin, so a KerrSchild solution\n  // doesn't have a singularity or excision_boundary in the domain.\n  domain::creators::Brick domain_creator;\n  if constexpr (is_time_dependent) {\n    std::unique_ptr<domain::creators::time_dependence::TimeDependence<3>>\n        time_dep{};\n    if constexpr (std::is_same_v<TargetFrame, Frame::Distorted>) {\n      time_dep = std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          0.0, std::array<double, 3>{0.01, 0.02, 0.03},\n          std::array<double, 3>{0.01, 0.02, 0.03});\n    } else {\n      time_dep = std::make_unique<\n          domain::creators::time_dependence::UniformTranslation<3>>(\n          0.0, std::array<double, 3>{0.01, 0.02, 0.03});\n    }\n    domain_creator = domain::creators::Brick(\n        std::array<double, 3>{3.1, 3.2, 3.3},\n        std::array<double, 3>{4.1, 4.2, 4.3}, std::array<size_t, 3>{0, 0, 0},\n        std::array<size_t, 3>{number_of_grid_points, number_of_grid_points,\n                              number_of_grid_points},\n        std::array<bool, 3>{false, false, false}, {}, std::move(time_dep));\n  } else {\n    domain_creator = domain::creators::Brick(\n        std::array<double, 3>{3.1, 3.2, 3.3},\n        std::array<double, 3>{4.1, 4.2, 4.3}, std::array<size_t, 3>{0, 0, 0},\n        std::array<size_t, 3>{number_of_grid_points, number_of_grid_points,\n                              number_of_grid_points},\n        std::array<bool, 3>{false, false, false});\n  }\n  const auto domain = domain_creator.create_domain();\n  ASSERT(domain.blocks().size() == 1, \"Expected a Domain with one block\");\n  const Block<3>& block = domain.blocks()[0];\n  const auto functions_of_time = domain_creator.functions_of_time();\n  const double time = temporal_id.step_time().value();\n  (void)time;  // For the time independent case\n\n  const auto element_ids = initial_element_ids(\n      block.id(), domain_creator.initial_refinement_levels()[block.id()]);\n  ASSERT(element_ids.size() == 1, \"Expected a Domain with only one element\");\n\n  // Set up target coordinates, and jacobians.\n  // We always compute our source quantities in the inertial frame.\n  // But we want our destination quantities in the target frame.\n  const Mesh mesh{domain_creator.initial_extents()[element_ids[0].block_id()],\n                  Spectral::Basis::Legendre,\n                  Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n  tnsr::I<DataVector, 3, TargetFrame> target_frame_coords{};\n  Jacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      jacobian_logical_to_inertial{};\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inv_jacobian_logical_to_inertial{};\n  Jacobian<DataVector, 3, TargetFrame, Frame::Inertial>\n      jacobian_target_to_inertial{};\n  InverseJacobian<DataVector, 3, TargetFrame, Frame::Inertial>\n      inv_jacobian_target_to_inertial{};\n  Jacobian<DataVector, 3, Frame::ElementLogical, TargetFrame>\n      jacobian_logical_to_target{};\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, TargetFrame>\n      inv_jacobian_logical_to_target{};\n  // NOLINTNEXTLINE(misc-const-correctness)\n  InverseJacobian<DataVector, 3, Frame::Grid, TargetFrame>\n      inv_jacobian_grid_to_target{};\n  tnsr::I<DataVector, 3, Frame::Inertial> frame_velocity_target_to_inertial{};\n  tnsr::I<DataVector, 3, TargetFrame> frame_velocity_grid_to_target{\n      mesh.number_of_grid_points(), 0.0};\n  if constexpr (is_time_dependent) {\n    ElementMap<3, Frame::Grid> map_logical_to_grid{\n        element_ids[0], block.moving_mesh_logical_to_grid_map().get_clone()};\n    const auto inv_jacobian_logical_to_grid =\n        map_logical_to_grid.inv_jacobian(logical_coords);\n    const auto inv_jacobian_grid_to_inertial =\n        block.moving_mesh_grid_to_inertial_map().inv_jacobian(\n            map_logical_to_grid(logical_coords), time, functions_of_time);\n    inv_jacobian_logical_to_inertial =\n        InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>(\n            mesh.number_of_grid_points(), 0.0);\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = 0; j < 3; ++j) {\n        for (size_t k = 0; k < 3; ++k) {\n          inv_jacobian_logical_to_inertial.get(i, j) +=\n              inv_jacobian_logical_to_grid.get(k, i) *\n              inv_jacobian_grid_to_inertial.get(j, k);\n        }\n      }\n    }\n\n    if constexpr (std::is_same_v<TargetFrame, Frame::Grid>) {\n      jacobian_target_to_inertial =\n          block.moving_mesh_grid_to_inertial_map().jacobian(\n              map_logical_to_grid(logical_coords), time, functions_of_time);\n      inv_jacobian_logical_to_target = inv_jacobian_logical_to_grid;\n      inv_jacobian_grid_to_target =\n          InverseJacobian<DataVector, 3, Frame::Grid, TargetFrame>{\n              mesh.number_of_grid_points(), 0.0};\n      get<0, 0>(inv_jacobian_grid_to_target) = 1.0;\n      get<1, 1>(inv_jacobian_grid_to_target) = 1.0;\n      get<2, 2>(inv_jacobian_grid_to_target) = 1.0;\n      target_frame_coords = map_logical_to_grid(logical_coords);\n    } else if constexpr (std::is_same_v<TargetFrame, Frame::Distorted>) {\n      const domain::CoordinateMaps::Composition map_logical_to_distorted{\n          domain::element_to_block_logical_map(element_ids[0]),\n          block.moving_mesh_logical_to_grid_map().get_clone(),\n          block.moving_mesh_grid_to_distorted_map().get_clone()};\n      jacobian_target_to_inertial =\n          block.moving_mesh_distorted_to_inertial_map().jacobian(\n              map_logical_to_distorted(logical_coords, time, functions_of_time),\n              time, functions_of_time);\n      inv_jacobian_logical_to_target = map_logical_to_distorted.inv_jacobian(\n          logical_coords, time, functions_of_time);\n      inv_jacobian_grid_to_target =\n          block.moving_mesh_grid_to_distorted_map().inv_jacobian(\n              map_logical_to_grid(logical_coords), time, functions_of_time);\n      target_frame_coords =\n          map_logical_to_distorted(logical_coords, time, functions_of_time);\n    } else {\n      static_assert(std::is_same_v<TargetFrame, Frame::Inertial>,\n                    \"TargetFrame must be the Inertial frame\");\n      inv_jacobian_logical_to_target = inv_jacobian_logical_to_inertial;\n      // jacobian_target_to_inertial is the identity in this case.\n      jacobian_target_to_inertial =\n          Jacobian<DataVector, 3, TargetFrame, Frame::Inertial>(\n              mesh.number_of_grid_points(), 0.0);\n      inv_jacobian_grid_to_target = inv_jacobian_grid_to_inertial;\n      for (size_t i = 0; i < 3; ++i) {\n        jacobian_target_to_inertial.get(i, i) = 1.0;\n      }\n      target_frame_coords = block.moving_mesh_grid_to_inertial_map()(\n          map_logical_to_grid(logical_coords), time, functions_of_time);\n    }\n  } else {\n    // time-independent.\n    static_assert(std::is_same_v<TargetFrame, Frame::Inertial>,\n                  \"TargetFrame must be the Inertial frame\");\n    // Don't need to define jacobian_target_to_inertial or\n    // inv_jacobian_grid_to_target\n    (void)jacobian_target_to_inertial;\n    (void)inv_jacobian_grid_to_target;\n    ElementMap<3, Frame::Inertial> map_logical_to_inertial{\n        element_ids[0], block.stationary_map().get_clone()};\n    target_frame_coords = map_logical_to_inertial(logical_coords);\n    inv_jacobian_logical_to_inertial =\n        map_logical_to_inertial.inv_jacobian(logical_coords);\n    inv_jacobian_logical_to_target = inv_jacobian_logical_to_inertial;\n  }\n\n  // Set up analytic solution.\n  gr::Solutions::KerrSchild solution(1.0, {{0.1, 0.2, 0.3}},\n                                     {{0.03, 0.01, 0.02}});\n  const auto solution_vars_target_frame = solution.variables(\n      target_frame_coords, 0.0,\n      typename gr::Solutions::KerrSchild::tags<DataVector, TargetFrame>{});\n  const auto& lapse =\n      get<gr::Tags::Lapse<DataVector>>(solution_vars_target_frame);\n  const auto& deriv_lapse = get<\n      ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>, TargetFrame>>(\n      solution_vars_target_frame);\n  const auto& shift = get<gr::Tags::Shift<DataVector, 3, TargetFrame>>(\n      solution_vars_target_frame);\n  const auto& deriv_shift =\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, 3, TargetFrame>,\n                        tmpl::size_t<3>, TargetFrame>>(\n          solution_vars_target_frame);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, TargetFrame>>(\n          solution_vars_target_frame);\n  const auto& spatial_christoffel =\n      get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, TargetFrame>>(\n          solution_vars_target_frame);\n\n  // Fill src vars with analytic solution.\n  Variables<SrcTags> src_vars(mesh.number_of_grid_points());\n\n  // Inertial metric variables. Needed only if TargetFrame is not Inertial.\n  // Set to zero size if TargetFrame is Inertial.\n  // Define them here, instead of inside the `if constexpr` below,\n  // because we might want to use them in later tests.\n  using inertial_metric_vars_tags =\n      tmpl::list<gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>>;\n  Variables<inertial_metric_vars_tags> inertial_metric_vars([&mesh]() {\n    if constexpr (std::is_same_v<TargetFrame, Frame::Inertial>) {\n      return 0;\n      // silence warning 'lambda capture mesh not used'.\n      (void)mesh;\n    } else {\n      return mesh.number_of_grid_points();\n    }\n  }());\n\n  if constexpr (std::is_same_v<TargetFrame, Frame::Inertial>) {\n    // Src vars are always in inertial frame. Here TargetFrame is inertial,\n    // so no frame transformation is needed.\n\n    get<::gr::Tags::SpacetimeMetric<DataVector, 3, TargetFrame>>(src_vars) =\n        gr::spacetime_metric(lapse, shift, spatial_metric);\n    const auto& deriv_spatial_metric =\n        get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3, TargetFrame>,\n                          tmpl::size_t<3>, TargetFrame>>(\n            solution_vars_target_frame);\n    get<::gh::Tags::Phi<DataVector, 3>>(src_vars) =\n        gh::phi(lapse, deriv_lapse, shift, deriv_shift, spatial_metric,\n                deriv_spatial_metric);\n  } else if constexpr (std::is_same_v<TargetFrame, Frame::Distorted>) {\n    // First figure out jacobians\n    const auto coords_frame_velocity_jacobians =\n        block.moving_mesh_distorted_to_inertial_map()\n            .coords_frame_velocity_jacobians(target_frame_coords, time,\n                                             functions_of_time);\n    const auto& inv_jacobian_distorted_to_inertial =\n        std::get<1>(coords_frame_velocity_jacobians);\n    const auto& jacobian_distorted_to_inertial =\n        std::get<2>(coords_frame_velocity_jacobians);\n    frame_velocity_target_to_inertial =\n        std::get<3>(coords_frame_velocity_jacobians);\n    inv_jacobian_target_to_inertial =\n        std::get<1>(coords_frame_velocity_jacobians);\n    jacobian_target_to_inertial = std::get<2>(coords_frame_velocity_jacobians);\n\n    ElementMap<3, Frame::Grid> map_logical_to_grid{\n        element_ids[0], block.moving_mesh_logical_to_grid_map().get_clone()};\n    frame_velocity_grid_to_target = std::get<3>(\n        block.moving_mesh_grid_to_distorted_map()\n            .coords_frame_velocity_jacobians(\n                map_logical_to_grid(logical_coords), time, functions_of_time));\n\n    // Now compute metric variables and transform them into the\n    // inertial frame.  We transform lapse, shift, 3-metric.  Then we\n    // will numerically differentiate transformed 3-metric because we\n    // don't have Hessians and therefore we cannot transform the GH\n    // Phi variable directly.\n\n    // Just copy lapse, since it doesn't transform. Need it for derivs.\n    get<gr::Tags::Lapse<DataVector>>(inertial_metric_vars) = lapse;\n\n    // Transform shift\n    auto& shift_inertial =\n        get<::gr::Tags::Shift<DataVector, 3>>(inertial_metric_vars);\n    for (size_t k = 0; k < 3; ++k) {\n      shift_inertial.get(k) = -frame_velocity_target_to_inertial.get(k);\n      for (size_t j = 0; j < 3; ++j) {\n        shift_inertial.get(k) +=\n            jacobian_distorted_to_inertial.get(k, j) * shift.get(j);\n      }\n    }\n\n    // Transform lower metric.\n    auto& lower_metric_inertial =\n        get<::gr::Tags::SpatialMetric<DataVector, 3>>(inertial_metric_vars);\n    transform::to_different_frame(make_not_null(&lower_metric_inertial),\n                                  spatial_metric,\n                                  inv_jacobian_distorted_to_inertial);\n\n    // Now fill src_vars\n    get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(src_vars) =\n        gr::spacetime_metric(lapse, shift_inertial, lower_metric_inertial);\n  } else {\n    static_assert(std::is_same_v<TargetFrame, Frame::Grid>,\n                  \"TargetFrame must be the Grid frame\");\n    // Src vars are always in inertial frame. Here TargetFrame is not\n    // inertial, so we need to transform to the inertial frame.\n\n    // First figure out jacobians\n    const auto coords_frame_velocity_jacobians =\n        block.moving_mesh_grid_to_inertial_map()\n            .coords_frame_velocity_jacobians(target_frame_coords, time,\n                                             functions_of_time);\n    const auto& inv_jacobian_grid_to_inertial =\n        std::get<1>(coords_frame_velocity_jacobians);\n    const auto& jacobian_grid_to_inertial =\n        std::get<2>(coords_frame_velocity_jacobians);\n    frame_velocity_target_to_inertial =\n        std::get<3>(coords_frame_velocity_jacobians);\n    inv_jacobian_target_to_inertial =\n        std::get<1>(coords_frame_velocity_jacobians);\n    jacobian_target_to_inertial = std::get<2>(coords_frame_velocity_jacobians);\n\n    // Now compute metric variables and transform them into the\n    // inertial frame.  We transform lapse, shift, 3-metric.  Then we\n    // will numerically differentiate transformed 3-metric because we\n    // don't have Hessians and therefore we cannot transform the GH\n    // Phi variable directly.\n\n    // Just copy lapse, since it doesn't transform. Need it for derivs.\n    get<gr::Tags::Lapse<DataVector>>(inertial_metric_vars) = lapse;\n\n    // Transform shift\n    auto& shift_inertial =\n        get<::gr::Tags::Shift<DataVector, 3>>(inertial_metric_vars);\n    for (size_t k = 0; k < 3; ++k) {\n      shift_inertial.get(k) = -frame_velocity_target_to_inertial.get(k);\n      for (size_t j = 0; j < 3; ++j) {\n        shift_inertial.get(k) +=\n            jacobian_grid_to_inertial.get(k, j) * shift.get(j);\n      }\n    }\n\n    // Transform lower metric.\n    auto& lower_metric_inertial =\n        get<::gr::Tags::SpatialMetric<DataVector, 3>>(inertial_metric_vars);\n    transform::to_different_frame(make_not_null(&lower_metric_inertial),\n                                  spatial_metric,\n                                  inv_jacobian_grid_to_inertial);\n\n    // Now fill src_vars\n    get<::gr::Tags::SpacetimeMetric<DataVector, 3>>(src_vars) =\n        gr::spacetime_metric(lapse, shift_inertial, lower_metric_inertial);\n  }\n\n  // Compute dest_vars\n  Variables<DestTags> dest_vars(mesh.number_of_grid_points());\n  if constexpr (is_time_dependent) {\n    intrp::ComputeExcisionBoundaryVolumeQuantities::apply(\n        make_not_null(&dest_vars), src_vars, mesh, jacobian_target_to_inertial,\n        inv_jacobian_target_to_inertial, jacobian_logical_to_target,\n        inv_jacobian_logical_to_target, inv_jacobian_grid_to_target,\n        frame_velocity_target_to_inertial, frame_velocity_grid_to_target);\n  } else {\n    // time-independent.\n    intrp::ComputeExcisionBoundaryVolumeQuantities::apply(\n        make_not_null(&dest_vars), src_vars, mesh);\n  }\n\n  // Now make sure that dest vars are correct.\n  if constexpr (tmpl::list_contains_v<\n                    DestTags,\n                    gr::Tags::SpatialMetric<DataVector, 3, TargetFrame>>) {\n    const auto& numerical_spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3, TargetFrame>>(dest_vars);\n    CHECK_ITERABLE_APPROX(spatial_metric, numerical_spatial_metric);\n  }\n\n  if constexpr (tmpl::list_contains_v<\n                    DestTags, gr::Tags::InverseSpatialMetric<DataVector, 3,\n                                                             TargetFrame>>) {\n    const auto& inv_spatial_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3, TargetFrame>>(\n            dest_vars);\n    CHECK_ITERABLE_APPROX(determinant_and_inverse(spatial_metric).second,\n                          inv_spatial_metric);\n  }\n\n  if constexpr (tmpl::list_contains_v<DestTags, gr::Tags::Lapse<DataVector>>) {\n    const auto& numerical_lapse = get<gr::Tags::Lapse<DataVector>>(dest_vars);\n    CHECK_ITERABLE_APPROX(lapse, numerical_lapse);\n  }\n\n  if constexpr (tmpl::list_contains_v<\n                    DestTags, ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                            tmpl::size_t<3>, TargetFrame>>) {\n    const auto& numerical_deriv_lapse =\n        get<::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                          TargetFrame>>(dest_vars);\n    CHECK_ITERABLE_CUSTOM_APPROX(deriv_lapse, numerical_deriv_lapse,\n                                 deriv_approx);\n  }\n\n  if constexpr (tmpl::list_contains_v<\n                    DestTags, gr::Tags::Shift<DataVector, 3, TargetFrame>>) {\n    const auto& numerical_shift =\n        get<gr::Tags::Shift<DataVector, 3, TargetFrame>>(dest_vars);\n    CHECK_ITERABLE_APPROX(shift, numerical_shift);\n  }\n\n  if constexpr (tmpl::list_contains_v<\n                    DestTags,\n                    ::Tags::deriv<gr::Tags::Shift<DataVector, 3, TargetFrame>,\n                                  tmpl::size_t<3>, TargetFrame>>) {\n    const auto& numerical_deriv_shift =\n        get<::Tags::deriv<gr::Tags::Shift<DataVector, 3, TargetFrame>,\n                          tmpl::size_t<3>, TargetFrame>>(dest_vars);\n    CHECK_ITERABLE_CUSTOM_APPROX(deriv_shift, numerical_deriv_shift,\n                                 deriv_approx);\n  }\n\n  if constexpr (tmpl::list_contains_v<\n                    DestTags,\n                    gr::Tags::ShiftyQuantity<DataVector, 3, TargetFrame>>) {\n    const auto& numerical_shifty_quantity =\n        get<gr::Tags::ShiftyQuantity<DataVector, 3, TargetFrame>>(dest_vars);\n    const auto shifty_quantity = tenex::evaluate<ti::I>(\n        shift(ti::I) + frame_velocity_grid_to_target(ti::I));\n    CHECK_ITERABLE_APPROX(shifty_quantity, numerical_shifty_quantity);\n  }\n\n  if constexpr (tmpl::list_contains_v<DestTags,\n                                      gr::Tags::SpatialChristoffelSecondKind<\n                                          DataVector, 3, TargetFrame>>) {\n    const auto& numerical_spatial_christoffel =\n        get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, TargetFrame>>(\n            dest_vars);\n    CHECK_ITERABLE_CUSTOM_APPROX(spatial_christoffel,\n                                 numerical_spatial_christoffel, deriv_approx);\n  }\n\n  // If TargetFrame is not the same as Inertial frame, we allow\n  // (optional) computation of destination quantities in the inertial frame.\n  // Test these here.\n  if constexpr (not std::is_same_v<TargetFrame, Frame::Inertial>) {\n    if constexpr (tmpl::list_contains_v<\n                      DestTags, gr::Tags::SpatialMetric<DataVector, 3>>) {\n      const auto& expected_inertial_spatial_metric =\n          get<::gr::Tags::SpatialMetric<DataVector, 3>>(inertial_metric_vars);\n      const auto& inertial_spatial_metric =\n          get<gr::Tags::SpatialMetric<DataVector, 3>>(dest_vars);\n      CHECK_ITERABLE_APPROX(expected_inertial_spatial_metric,\n                            inertial_spatial_metric);\n    }\n\n    if constexpr (tmpl::list_contains_v<\n                      DestTags,\n                      gr::Tags::InverseSpatialMetric<DataVector, 3>>) {\n      const auto expected_inertial_inverse_spatial_metric =\n          determinant_and_inverse(get<::gr::Tags::SpatialMetric<DataVector, 3>>(\n                                      inertial_metric_vars))\n              .second;\n      const auto& inertial_inverse_spatial_metric =\n          get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(dest_vars);\n      CHECK_ITERABLE_APPROX(expected_inertial_inverse_spatial_metric,\n                            inertial_inverse_spatial_metric);\n    }\n\n    if constexpr (tmpl::list_contains_v<DestTags,\n                                        gr::Tags::Shift<DataVector, 3>>) {\n      const auto& expected_inertial_shift =\n          get<gr::Tags::Shift<DataVector, 3>>(inertial_metric_vars);\n      const auto& inertial_shift =\n          get<gr::Tags::Shift<DataVector, 3>>(dest_vars);\n      CHECK_ITERABLE_APPROX(expected_inertial_shift, inertial_shift);\n    }\n\n    if constexpr (tmpl::list_contains_v<\n                      DestTags, domain::Tags::InverseJacobian<3, Frame::Grid,\n                                                              TargetFrame>>) {\n      const auto& numerical_inv_jacobian_grid_to_target =\n          get<domain::Tags::InverseJacobian<3, Frame::Grid, TargetFrame>>(\n              dest_vars);\n      CHECK_ITERABLE_APPROX(inv_jacobian_grid_to_target,\n                            numerical_inv_jacobian_grid_to_target);\n    }\n  }\n}\n\n// [[Timeout, 20]]\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.Interpolator.\"\n    \"ComputeExcisionBoundaryVolumeQuantities\",\n    \"[Unit]\") {\n  static_assert(\n      tt::assert_conforms_to_v<intrp::ComputeExcisionBoundaryVolumeQuantities,\n                               intrp::protocols::ComputeVarsToInterpolate>);\n  // time-independent.\n  // All possible tags.\n  test_compute_excision_boundary_volume_quantities<\n      std::false_type, Frame::Inertial,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                 Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                             Frame::Inertial>>,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::Lapse<DataVector>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 gr::Tags::Shift<DataVector, 3>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3, Frame::Inertial>,\n                               tmpl::size_t<3>, Frame::Inertial>,\n                 domain::Tags::InverseJacobian<3, Frame::Grid, Frame::Inertial>,\n                 gr::Tags::SpatialChristoffelSecondKind<DataVector, 3,\n                                                        Frame::Inertial>>>();\n\n  // Leave out a few tags.\n  test_compute_excision_boundary_volume_quantities<\n      std::false_type, Frame::Inertial,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::Lapse<DataVector>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 gr::Tags::SpatialChristoffelSecondKind<DataVector, 3,\n                                                        Frame::Inertial>>>();\n\n  test_compute_excision_boundary_volume_quantities<\n      std::false_type, Frame::Inertial,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>,\n      tmpl::list<\n          gr::Tags::SpacetimeMetric<DataVector, 3>,\n          gr::Tags::SpatialMetric<DataVector, 3>,\n          gr::Tags::Shift<DataVector, 3>,\n          ::Tags::deriv<gr::Tags::Shift<DataVector, 3, Frame::Inertial>,\n                        tmpl::size_t<3>, Frame::Inertial>,\n          domain::Tags::InverseJacobian<3, Frame::Grid, Frame::Inertial>>>();\n\n  // time-dependent.\n  // All possible tags.\n  test_compute_excision_boundary_volume_quantities<\n      std::true_type, Frame::Grid,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                 Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                             Frame::Inertial>>,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::Lapse<DataVector>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Grid>,\n                 gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::Shift<DataVector, 3, Frame::Grid>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3, Frame::Grid>,\n                               tmpl::size_t<3>, Frame::Grid>,\n                 domain::Tags::InverseJacobian<3, Frame::Grid, Frame::Grid>,\n                 gr::Tags::SpatialChristoffelSecondKind<DataVector, 3,\n                                                        Frame::Grid>>>();\n\n  // Distorted frame.\n  test_compute_excision_boundary_volume_quantities<\n      std::true_type, Frame::Distorted,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>,\n                 Tags::deriv<gh::Tags::Phi<DataVector, 3>, tmpl::size_t<3>,\n                             Frame::Inertial>>,\n      tmpl::list<\n          gr::Tags::SpacetimeMetric<DataVector, 3, Frame::Distorted>,\n          gr::Tags::SpatialMetric<DataVector, 3, Frame::Distorted>,\n          gr::Tags::Lapse<DataVector>,\n          ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                        Frame::Distorted>,\n          gr::Tags::ShiftyQuantity<DataVector, 3, Frame::Distorted>,\n          gr::Tags::Shift<DataVector, 3, Frame::Distorted>,\n          ::Tags::deriv<gr::Tags::Shift<DataVector, 3, Frame::Distorted>,\n                        tmpl::size_t<3>, Frame::Distorted>,\n          domain::Tags::InverseJacobian<3, Frame::Grid, Frame::Distorted>,\n          gr::Tags::SpatialChristoffelSecondKind<DataVector, 3,\n                                                 Frame::Distorted>>>();\n\n  // Leave out a few tags.\n  test_compute_excision_boundary_volume_quantities<\n      std::true_type, Frame::Grid,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gh::Tags::Pi<DataVector, 3>, gh::Tags::Phi<DataVector, 3>>,\n      tmpl::list<gr::Tags::SpacetimeMetric<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>>>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_ElementReceiveInterpPoints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/MockRuntimeSystem.hpp\"\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementInitInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/ElementReceiveInterpPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InterpolationTargetSendPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/LineSegment.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nnamespace {\n\n// Simple Variables tag for test.\nnamespace Tags {\nstruct TestSolution : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace Tags\n\ntemplate <typename Metavariables>\nstruct mock_element {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<intrp::Actions::ElementInitInterpPoints<\n          Metavariables::volume_dim,\n          typename Metavariables::interpolation_target_tags>>>>;\n\n  using component_being_mocked =\n      DgElementArray<Metavariables, phase_dependent_action_list>;\n};\n\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct mock_interpolation_target {\n  static_assert(\n      tt::assert_conforms_to_v<InterpolationTargetTag,\n                               intrp::protocols::InterpolationTargetTag>);\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tags = tmpl::push_back<\n      Parallel::get_const_global_cache_tags_from_actions<\n          tmpl::list<typename InterpolationTargetTag::compute_target_points>>,\n      domain::Tags::Domain<Metavariables::volume_dim>, intrp::Tags::Verbosity>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<intrp::Actions::InitializeInterpolationTarget<\n              Metavariables, InterpolationTargetTag>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<\n              intrp::Actions::InterpolationTargetSendTimeIndepPointsToElements<\n                  InterpolationTargetTag>>>>;\n  using component_being_mocked =\n      intrp::InterpolationTarget<Metavariables, InterpolationTargetTag>;\n};\n\nstruct MockMetavariables {\n  struct InterpolationTargetA\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using vars_to_interpolate_to_target = tmpl::list<Tags::TestSolution>;\n    using compute_items_on_target = tmpl::list<>;\n    using compute_target_points =\n        ::intrp::TargetPoints::LineSegment<InterpolationTargetA, 3,\n                                           Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<>, InterpolationTargetA>>;\n    template <typename Metavariables>\n    using interpolating_component = mock_element<Metavariables>;\n  };\n  struct InterpolationTargetB\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using vars_to_interpolate_to_target = tmpl::list<Tags::TestSolution>;\n    using compute_items_on_target = tmpl::list<>;\n    using compute_target_points =\n        ::intrp::TargetPoints::LineSegment<InterpolationTargetB, 3,\n                                           Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<>, InterpolationTargetA>>;\n    template <typename Metavariables>\n    using interpolating_component = mock_element<Metavariables>;\n  };\n  static constexpr size_t volume_dim = 3;\n  using interpolation_target_tags =\n      tmpl::list<InterpolationTargetA, InterpolationTargetB>;\n\n  using component_list = tmpl::list<\n      mock_interpolation_target<MockMetavariables, InterpolationTargetA>,\n      mock_interpolation_target<MockMetavariables, InterpolationTargetB>,\n      mock_element<MockMetavariables>>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Interpolator.ElementReceivePoints\",\n                  \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n\n  using metavars = MockMetavariables;\n  using target_component_a =\n      mock_interpolation_target<metavars,\n                                typename metavars::InterpolationTargetA>;\n  using target_component_b =\n      mock_interpolation_target<metavars,\n                                typename metavars::InterpolationTargetB>;\n  using elem_component = mock_element<metavars>;\n\n  // Options\n  const auto domain_creator = domain::creators::Sphere(\n      0.9, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false);\n  intrp::OptionHolders::LineSegment<3> line_segment_opts_a(\n      {{1.0, 1.0, 1.0}}, {{2.4, 2.4, 2.4}}, 15);\n  intrp::OptionHolders::LineSegment<3> line_segment_opts_b(\n      {{1.0, 1.0, 1.0}}, {{2.1, 2.1, 2.1}}, 12);\n  tuples::TaggedTuple<intrp::Tags::LineSegment<metavars::InterpolationTargetA,\n                                               metavars::volume_dim>,\n                      intrp::Tags::LineSegment<metavars::InterpolationTargetB,\n                                               metavars::volume_dim>,\n                      domain::Tags::Domain<metavars::volume_dim>,\n                      intrp::Tags::Verbosity>\n      tuple_of_opts{std::move(line_segment_opts_a),\n                    std::move(line_segment_opts_b),\n                    domain_creator.create_domain(), ::Verbosity::Silent};\n\n  // Initialization\n  ActionTesting::MockRuntimeSystem<metavars> runner{std::move(tuple_of_opts)};\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<target_component_a>(&runner, 0);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<target_component_a>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_component<target_component_b>(&runner, 0);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<target_component_b>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_component<elem_component>(&runner, 0);\n  ActionTesting::next_action<elem_component>(make_not_null(&runner), 0);\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Register);\n\n  using point_info_type = tnsr::I<\n      DataVector, metavars::volume_dim,\n      typename metavars::InterpolationTargetA::compute_target_points::frame>;\n\n  // element should contain an intrp::Tags::PointInfo for both A and B\n  // that contains a default-constructed tensor. This tests\n  // ElementInitInterpPoints.\n  CHECK(ActionTesting::get_databox_tag<\n            elem_component,\n            intrp::Tags::PointInfo<typename metavars::InterpolationTargetA,\n                                   tmpl::size_t<metavars::volume_dim>>>(\n            runner, 0) == point_info_type{});\n  CHECK(ActionTesting::get_databox_tag<\n            elem_component,\n            intrp::Tags::PointInfo<typename metavars::InterpolationTargetB,\n                                   tmpl::size_t<metavars::volume_dim>>>(\n            runner, 0) == point_info_type{});\n\n  // Now invoke the only Registration action (InterpolationTargetSendPoints).\n  ActionTesting::next_action<target_component_a>(make_not_null(&runner), 0);\n  ActionTesting::next_action<target_component_b>(make_not_null(&runner), 0);\n  // ... and the only queued simple action (ElementReceiveInterpPoints).\n  runner.invoke_queued_simple_action<elem_component>(0);\n  runner.invoke_queued_simple_action<elem_component>(0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // After registration, element should contain an\n  // intrp::Tags::InterpPointInfo<Metavariables> that has a\n  // point_info for InterpolationTargetA/B containing the\n  // expected point info.\n  const auto expected_point_info_a = []() {\n    const size_t n_pts = 15;\n    tnsr::I<DataVector, 3, Frame::Inertial> points(n_pts);\n    for (size_t d = 0; d < 3; ++d) {\n      for (size_t i = 0; i < n_pts; ++i) {\n        points.get(d)[i] = 1.0 + 0.1 * i;  // Worked out by hand.\n      }\n    }\n    return points;\n  }();\n  const auto expected_point_info_b = []() {\n    const size_t n_pts = 12;\n    tnsr::I<DataVector, 3, Frame::Inertial> points(n_pts);\n    for (size_t d = 0; d < 3; ++d) {\n      for (size_t i = 0; i < n_pts; ++i) {\n        points.get(d)[i] = 1.0 + 0.1 * i;  // Worked out by hand.\n      }\n    }\n    return points;\n  }();\n\n  CHECK_ITERABLE_APPROX(\n      (ActionTesting::get_databox_tag<\n          elem_component,\n          intrp::Tags::PointInfo<typename metavars::InterpolationTargetA,\n                                 tmpl::size_t<metavars::volume_dim>>>(runner,\n                                                                      0)),\n      expected_point_info_a);\n  CHECK_ITERABLE_APPROX(\n      (ActionTesting::get_databox_tag<\n          elem_component,\n          intrp::Tags::PointInfo<typename metavars::InterpolationTargetB,\n                                 tmpl::size_t<metavars::volume_dim>>>(runner,\n                                                                      0)),\n      expected_point_info_b);\n\n  // Should be no queued simple actions on either component.\n  CHECK(runner.is_simple_action_queue_empty<target_component_a>(0));\n  CHECK(runner.is_simple_action_queue_empty<target_component_b>(0));\n  CHECK(runner.is_simple_action_queue_empty<elem_component>(0));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_InitializeInterpolationTarget.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <deque>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/LineSegment.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass DataVector;\n\nnamespace {\n\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct mock_interpolation_target {\n  static_assert(\n      tt::assert_conforms_to_v<InterpolationTargetTag,\n                               intrp::protocols::InterpolationTargetTag>);\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = size_t;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<3>>;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<intrp::Actions::InitializeInterpolationTarget<\n          Metavariables, InterpolationTargetTag>>>>;\n};\n\nstruct Metavariables {\n  struct InterpolationTargetA\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using vars_to_interpolate_to_target =\n        tmpl::list<gr::Tags::Lapse<DataVector>>;\n    using compute_items_on_target = tmpl::list<>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<>, InterpolationTargetA>>;\n    using compute_target_points =\n        ::intrp::TargetPoints::LineSegment<InterpolationTargetA, 3,\n                                           Frame::Inertial>;\n  };\n\n  using component_list = tmpl::list<\n      mock_interpolation_target<Metavariables, InterpolationTargetA>>;\n};\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.InterpolationTarget.Initialize\",\n                  \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  using metavars = Metavariables;\n  using temporal_id_type =\n      typename metavars::InterpolationTargetA::temporal_id::type;\n  using component =\n      mock_interpolation_target<metavars,\n                                typename metavars::InterpolationTargetA>;\n  const auto domain_creator = domain::creators::Sphere(\n      0.9, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false);\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      {domain_creator.create_domain(), ::Verbosity::Silent}};\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_component<component>(&runner, 0);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  CHECK(ActionTesting::get_databox_tag<\n            component,\n            ::intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(\n            runner, 0)\n            .empty());\n  CHECK(ActionTesting::get_databox_tag<\n            component, ::intrp::Tags::TemporalIds<temporal_id_type>>(runner, 0)\n            .empty());\n\n  const auto& cache = ActionTesting::cache<component>(runner, 0_st);\n  CHECK(Parallel::get<domain::Tags::Domain<3>>(cache) ==\n        domain_creator.create_domain());\n\n  CHECK(ActionTesting::get_databox_tag<\n            component, ::intrp::Tags::InterpolatedVars<\n                           metavars::InterpolationTargetA, temporal_id_type>>(\n            runner, 0)\n            .empty());\n}\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_InterpolateWithoutInterpComponent.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/ObservationBox.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/Interpolation/InterpolateOnElementTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Events/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Events/InterpolateWithoutInterpComponent.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeVarsToInterpolate.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/LineSegment.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct mock_element {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Metavariables::volume_dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using initial_databox = db::compute_databox_type<db::AddSimpleTags<>>;\n};\n\ntemplate <typename Metavariables, typename ElemComponent>\nstruct initialize_elements_and_queue_simple_actions {\n  template <typename Runner, typename TemporalId>\n  void operator()(const DomainCreator<3>& domain_creator,\n                  const Domain<3>& domain,\n                  const std::vector<ElementId<3>>& element_ids,\n                  const tnsr::I<DataVector, 3, Frame::Inertial>& target_points,\n                  Runner& runner, const TemporalId& temporal_id) {\n    using metavars = Metavariables;\n    using elem_component = ElemComponent;\n    // Emplace elements.\n    for (const auto& element_id : element_ids) {\n      ActionTesting::emplace_component<elem_component>(&runner, element_id);\n      ActionTesting::next_action<elem_component>(make_not_null(&runner),\n                                                 element_id);\n    }\n    ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n    // Create event.\n    typename metavars::event event{};\n\n    CHECK(event.needs_evolved_variables());\n\n    // Run event on all elements.\n    for (const auto& element_id : element_ids) {\n      // 1. Get vars, mesh, and coords\n      const auto& [vars, mesh, inertial_coords] =\n          InterpolateOnElementTestHelpers::make_volume_data_and_mesh<\n              ElemComponent, Metavariables::use_time_dependent_maps>(\n              domain_creator, runner, domain, element_id, temporal_id);\n\n      // 2. Make a box\n      auto box = db::create<db::AddSimpleTags<\n          Parallel::Tags::MetavariablesImpl<metavars>,\n          typename metavars::InterpolationTargetA::temporal_id,\n          intrp::Tags::PointInfo<typename metavars::InterpolationTargetA,\n                                 tmpl::size_t<3>>,\n          ::Events::Tags::ObserverMesh<metavars::volume_dim>,\n          domain::Tags::Coordinates<metavars::volume_dim, Frame::Inertial>,\n          ::Tags::Variables<\n              typename std::remove_reference_t<decltype(vars)>::tags_list>>>(\n          metavars{}, temporal_id, target_points, mesh, inertial_coords, vars);\n\n      // 3. Run the event.  This will invoke simple actions on\n      // InterpolationTarget.\n      auto obs_box = make_observation_box<\n          typename metavars::event::compute_tags_for_observation_box>(\n          make_not_null(&box));\n      event.run(make_not_null(&obs_box),\n                ActionTesting::cache<elem_component>(runner, element_id),\n                element_id, std::add_pointer_t<elem_component>{}, {});\n    }\n  }\n};\n\ntemplate <bool HaveComputeVarsToInterpolate, bool UseTimeDependentMaps>\nstruct MockMetavariables {\n  static constexpr bool use_time_dependent_maps = UseTimeDependentMaps;\n  using const_global_cache_tags = tmpl::list<domain::Tags::Domain<3>>;\n  using mutable_global_cache_tags =\n      tmpl::conditional_t<use_time_dependent_maps,\n                          tmpl::list<domain::Tags::FunctionsOfTimeInitialize>,\n                          tmpl::list<>>;\n  struct InterpolationTargetAWithComputeVarsToInterpolate\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using compute_items_on_target = tmpl::list<>;\n    using vars_to_interpolate_to_target =\n        tmpl::list<InterpolateOnElementTestHelpers::Tags::MultiplyByTwo>;\n    using compute_vars_to_interpolate =\n        InterpolateOnElementTestHelpers::ComputeMultiplyByTwo;\n    // The following are not used in this test, but must be there to\n    // conform to the protocol.\n    using compute_target_points = ::intrp::TargetPoints::LineSegment<\n        InterpolationTargetAWithComputeVarsToInterpolate, 3, Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<>, InterpolationTargetAWithComputeVarsToInterpolate>>;\n  };\n  struct InterpolationTargetAWithoutComputeVarsToInterpolate\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using compute_items_on_target = tmpl::list<>;\n    using vars_to_interpolate_to_target =\n        tmpl::list<InterpolateOnElementTestHelpers::Tags::TestSolution>;\n    // The following are not used in this test, but must be there to\n    // conform to the protocol.\n    using compute_target_points = ::intrp::TargetPoints::Sphere<\n        InterpolationTargetAWithoutComputeVarsToInterpolate, Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<>, InterpolationTargetAWithoutComputeVarsToInterpolate>>;\n  };\n  using InterpolationTargetA =\n      tmpl::conditional_t<HaveComputeVarsToInterpolate,\n                          InterpolationTargetAWithComputeVarsToInterpolate,\n                          InterpolationTargetAWithoutComputeVarsToInterpolate>;\n  static constexpr size_t volume_dim = 3;\n  using interpolation_target_tags = tmpl::list<InterpolationTargetA>;\n\n  using component_list =\n      tmpl::list<InterpolateOnElementTestHelpers::mock_interpolation_target<\n                     MockMetavariables, InterpolationTargetA>,\n                 mock_element<MockMetavariables>>;\n\n  using event = intrp::Events::InterpolateWithoutInterpComponent<\n      volume_dim, InterpolationTargetA,\n      tmpl::list<InterpolateOnElementTestHelpers::Tags::TestSolution>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<Event, tmpl::list<event>>>;\n  };\n};\n\ntemplate <typename MockMetavariables, bool OffCenter = false>\nvoid run_test() {\n  using metavars = MockMetavariables;\n  using elem_component = mock_element<metavars>;\n  InterpolateOnElementTestHelpers::test_interpolate_on_element<\n      metavars, elem_component, OffCenter>(\n      initialize_elements_and_queue_simple_actions<metavars, elem_component>{});\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.Interpolator.InterpolateEventNoInterpolator\",\n    \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  domain::creators::time_dependence::register_derived_with_charm();\n  domain::FunctionsOfTime::register_derived_with_charm();\n  run_test<MockMetavariables<false, false>>();\n  run_test<MockMetavariables<true, false>>();\n  run_test<MockMetavariables<false, true>>();\n  run_test<MockMetavariables<true, true>>();\n\n  // Off-center test\n  run_test<MockMetavariables<false, false>, true>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_InterpolationTargetKerrHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/Interpolation/InterpolationTargetTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/KerrHorizon.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Spherepack.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\ndomain::creators::Sphere make_sphere() {\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::All) {\n    return {0.9, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n  }\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::None) {\n    return {4.9, 8.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n  }\n  return {3.4, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n}\n\nstruct KerrHorizonTargetTag\n    : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  using temporal_id = ::Tags::TimeStepId;\n  using vars_to_interpolate_to_target = tmpl::list<gr::Tags::Lapse<DataVector>>;\n  using compute_items_on_target = tmpl::list<>;\n  using compute_target_points =\n      ::intrp::TargetPoints::KerrHorizon<KerrHorizonTargetTag,\n                                         ::Frame::Inertial>;\n  using post_interpolation_callbacks = tmpl::list<>;\n};\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\nvoid test_interpolation_target_kerr_horizon(\n    const ylm::AngularOrdering angular_ordering) {\n  // Constants used in this test.\n  // We use l_max=18 to get enough points that the surface is\n  // represented to roundoff error; for smaller l_max we would need to\n  // modify InterpTargetTestHelpers::test_interpolation_target to\n  // handle a custom `approx`.\n  const size_t l_max = 18;\n  const double mass = 1.8;\n  const std::array<double, 3> center = {{0.05, 0.06, 0.07}};\n  const std::array<double, 3> dimless_spin = {{0.2, 0.3, 0.4}};\n\n  // Options for KerrHorizon\n  intrp::OptionHolders::KerrHorizon kerr_horizon_opts(\n      l_max, center, mass, dimless_spin, angular_ordering);\n\n  // Test creation of options\n  const auto created_opts =\n      TestHelpers::test_creation<intrp::OptionHolders::KerrHorizon>(\n          \"Center: [0.05, 0.06, 0.07]\\n\"\n          \"DimensionlessSpin: [0.2, 0.3, 0.4]\\n\"\n          \"LMax: 18\\n\"\n          \"Mass: 1.8\\n\"\n          \"AngularOrdering: \" +\n          std::string(MakeString{} << angular_ordering));\n  CHECK(created_opts == kerr_horizon_opts);\n\n  const auto domain_creator = make_sphere<ValidPoints>();\n\n  const auto expected_block_coord_holders = [&domain_creator, &mass, &center,\n                                             &dimless_spin,\n                                             &angular_ordering]() {\n    // How many points are supposed to be in a Strahlkorper,\n    // reproduced here by hand for the test.\n    const size_t n_theta = l_max + 1;\n    const size_t n_phi = 2 * l_max + 1;\n\n    // The theta points of a Strahlkorper are Gauss-Legendre points.\n    const std::vector<double> theta_points = []() {\n      std::vector<double> thetas(n_theta);\n      std::vector<double> work(n_theta + 1);\n      std::vector<double> unused_weights(n_theta);\n      int err = 0;\n      gaqd_(static_cast<int>(n_theta), thetas.data(), unused_weights.data(),\n            work.data(), static_cast<int>(n_theta + 1), &err);\n      return thetas;\n    }();\n\n    // Radius as function of theta, phi\n    const auto radius = [&mass, &dimless_spin](const double theta,\n                                               const double phi) {\n      // Recoding kerr_horizon_radius in a different way for the test.\n      const std::array<double, 3> spin_a = {{mass * dimless_spin[0],\n                                             mass * dimless_spin[1],\n                                             mass * dimless_spin[2]}};\n      const double spin_a_squared =\n          square(spin_a[0]) + square(spin_a[1]) + square(spin_a[2]);\n      const double a_dot_xhat_squared =\n          square(spin_a[0] * sin(theta) * cos(phi) +\n                 spin_a[1] * sin(theta) * sin(phi) + spin_a[2] * cos(theta));\n      const double r_boyer_lindquist_squared =\n          square(mass + sqrt(square(mass) - spin_a_squared));\n      return sqrt((r_boyer_lindquist_squared + spin_a_squared) /\n                  (1.0 + a_dot_xhat_squared / r_boyer_lindquist_squared));\n    };\n\n    const double two_pi_over_n_phi = 2.0 * M_PI / n_phi;\n    tnsr::I<DataVector, 3, Frame::Inertial> points(n_theta * n_phi);\n    size_t s = 0;\n    if (angular_ordering == ylm::AngularOrdering::Strahlkorper) {\n      for (size_t i_phi = 0; i_phi < n_phi; ++i_phi) {\n        const double phi = two_pi_over_n_phi * i_phi;\n        for (size_t i_theta = 0; i_theta < n_theta; ++i_theta) {\n          const double theta = theta_points[i_theta];\n          const double r = radius(theta, phi);\n          points.get(0)[s] = r * sin(theta) * cos(phi) + center[0];\n          points.get(1)[s] = r * sin(theta) * sin(phi) + center[1],\n          points.get(2)[s] = r * cos(theta) + center[2];\n          ++s;\n        }\n      }\n    } else {\n      for (size_t i_theta = 0; i_theta < n_theta; ++i_theta) {\n        for (size_t i_phi = 0; i_phi < n_phi; ++i_phi) {\n          const double phi = two_pi_over_n_phi * i_phi;\n          const double theta = theta_points[i_theta];\n          const double r = radius(theta, phi);\n          points.get(0)[s] = r * sin(theta) * cos(phi) + center[0];\n          points.get(1)[s] = r * sin(theta) * sin(phi) + center[1],\n          points.get(2)[s] = r * cos(theta) + center[2];\n          ++s;\n        }\n      }\n    }\n    return block_logical_coordinates(domain_creator.create_domain(), points);\n  }();\n\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::KerrHorizon<KerrHorizonTargetTag>>(\"KerrHorizon\");\n\n  InterpTargetTestHelpers::test_interpolation_target<\n      KerrHorizonTargetTag, 3, intrp::Tags::KerrHorizon<KerrHorizonTargetTag>>(\n      kerr_horizon_opts, expected_block_coord_holders);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.InterpolationTarget.KerrHorizon\",\n                  \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  test_interpolation_target_kerr_horizon<\n      InterpTargetTestHelpers::ValidPoints::All>(ylm::AngularOrdering::Cce);\n  test_interpolation_target_kerr_horizon<\n      InterpTargetTestHelpers::ValidPoints::All>(\n      ylm::AngularOrdering::Strahlkorper);\n  test_interpolation_target_kerr_horizon<\n      InterpTargetTestHelpers::ValidPoints::Some>(\n      ylm::AngularOrdering::Strahlkorper);\n  test_interpolation_target_kerr_horizon<\n      InterpTargetTestHelpers::ValidPoints::None>(\n      ylm::AngularOrdering::Strahlkorper);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_InterpolationTargetLineSegment.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/Interpolation/InterpolationTargetTestHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/LineSegment.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\ndomain::creators::Sphere make_sphere() {\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::All) {\n    return {0.9, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n  }\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::None) {\n    return {4.9, 8.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n  }\n  return {3.4, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n}\n\ntemplate <typename Frame>\nstruct LineSegmentTag\n    : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  using temporal_id = ::Tags::TimeStepId;\n  using vars_to_interpolate_to_target = tmpl::list<gr::Tags::Lapse<DataVector>>;\n  using compute_items_on_target = tmpl::list<>;\n  using compute_target_points =\n      ::intrp::TargetPoints::LineSegment<LineSegmentTag, 3, Frame>;\n  using post_interpolation_callbacks = tmpl::list<>;\n};\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\nvoid test() {\n  // Options for LineSegment\n  intrp::OptionHolders::LineSegment<3> line_segment_opts({{1.0, 1.0, 1.0}},\n                                                         {{2.4, 2.4, 2.4}}, 15);\n\n  // Test creation of options\n  const auto created_opts =\n      TestHelpers::test_creation<intrp::OptionHolders::LineSegment<3>>(\n          \"Begin: [1.0, 1.0, 1.0]\\n\"\n          \"End: [2.4, 2.4, 2.4]\\n\"\n          \"NumberOfPoints: 15\");\n  CHECK(created_opts == line_segment_opts);\n\n  const auto domain_creator = make_sphere<ValidPoints>();\n\n  const auto expected_block_coord_holders = [&domain_creator]() {\n    const size_t n_pts = 15;\n    tnsr::I<DataVector, 3, Frame::Inertial> points(n_pts);\n    for (size_t d = 0; d < 3; ++d) {\n      for (size_t i = 0; i < n_pts; ++i) {\n        points.get(d)[i] = 1.0 + 0.1 * i;  // Worked out by hand.\n      }\n    }\n    return block_logical_coordinates(domain_creator.create_domain(), points);\n  }();\n\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::LineSegment<LineSegmentTag<Frame::Grid>, 3>>(\"LineSegment\");\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::LineSegment<LineSegmentTag<Frame::Inertial>, 3>>(\n      \"LineSegment\");\n\n  InterpTargetTestHelpers::test_interpolation_target<\n      LineSegmentTag<Frame::Grid>, 3,\n      intrp::Tags::LineSegment<LineSegmentTag<Frame::Grid>, 3>>(\n      line_segment_opts, expected_block_coord_holders);\n  InterpTargetTestHelpers::test_interpolation_target<\n      LineSegmentTag<Frame::Inertial>, 3,\n      intrp::Tags::LineSegment<LineSegmentTag<Frame::Inertial>, 3>>(\n      line_segment_opts, expected_block_coord_holders);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.InterpolationTarget.LineSegment\",\n                  \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  test<InterpTargetTestHelpers::ValidPoints::All>();\n  test<InterpTargetTestHelpers::ValidPoints::Some>();\n  test<InterpTargetTestHelpers::ValidPoints::None>();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_InterpolationTargetSpecifiedPoints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/Interpolation/InterpolationTargetTestHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/SpecifiedPoints.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\ndomain::creators::Interval make_interval() {\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::All) {\n    return domain::creators::Interval({{-1.0}}, {{1.0}}, {{1}}, {{3}});\n  }\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::None) {\n    return domain::creators::Interval({{-1.0}}, {{0.0}}, {{1}}, {{3}});\n  }\n  return domain::creators::Interval({{-1.0}}, {{0.5}}, {{1}}, {{3}});\n}\n\ntemplate <size_t Dim>\nstruct SpecifiedPointsTag\n    : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  using temporal_id = ::Tags::TimeStepId;\n  using vars_to_interpolate_to_target = tmpl::list<gr::Tags::Lapse<DataVector>>;\n  using compute_items_on_target = tmpl::list<>;\n  using compute_target_points =\n      ::intrp::TargetPoints::SpecifiedPoints<SpecifiedPointsTag, Dim>;\n  using post_interpolation_callbacks = tmpl::list<>;\n};\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\nvoid test_1d() {\n  // Options for SpecifiedPoints\n  intrp::OptionHolders::SpecifiedPoints<1> points_opts(\n      std::vector<std::array<double, 1>>{\n          {std::array<double, 1>{{1.0}}, std::array<double, 1>{{0.3}}}});\n\n  // Test creation of options\n  const auto created_opts =\n      TestHelpers::test_creation<intrp::OptionHolders::SpecifiedPoints<1>>(\n          \"Points: [[1.0], [0.3]]\");\n  CHECK(created_opts == points_opts);\n\n  const auto domain_creator = make_interval<ValidPoints>();\n\n  const auto expected_block_coord_holders = [&domain_creator]() {\n    tnsr::I<DataVector, 1, Frame::Inertial> points;\n    get<0>(points) = DataVector({{1.0, 0.3}});\n    return block_logical_coordinates(domain_creator.create_domain(), points);\n  }();\n\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::SpecifiedPoints<SpecifiedPointsTag<1>, 1>>(\n      \"SpecifiedPoints\");\n\n  InterpTargetTestHelpers::test_interpolation_target<\n      SpecifiedPointsTag<1>, 1,\n      intrp::Tags::SpecifiedPoints<SpecifiedPointsTag<1>, 1>>(\n      created_opts, expected_block_coord_holders);\n}\n\nvoid test_2d() {\n  // Options for SpecifiedPoints\n  intrp::OptionHolders::SpecifiedPoints<2> points_opts(\n      std::vector<std::array<double, 2>>{{std::array<double, 2>{{0.0, 1.0}},\n                                          std::array<double, 2>{{-0.2, 0.1}},\n                                          std::array<double, 2>{{0.3, 1.9}}}});\n\n  // Test creation of options\n  const auto created_opts =\n      TestHelpers::test_creation<intrp::OptionHolders::SpecifiedPoints<2>>(\n          \"Points: [[0.0, 1.0], [-0.2, 0.1], [0.3, 1.9]]\");\n  CHECK(created_opts == points_opts);\n\n  const auto domain_creator = domain::creators::Rectangle(\n      {{-1.0, -1.0}}, {{1.0, 2.0}}, {{1, 1}}, {{3, 4}}, {{false, false}});\n\n  const auto expected_block_coord_holders = [&domain_creator]() {\n    tnsr::I<DataVector, 2, Frame::Inertial> points;\n    get<0>(points) = DataVector({{0.0, -0.2, 0.3}});\n    get<1>(points) = DataVector({{1.0, 0.1, 1.9}});\n    return block_logical_coordinates(domain_creator.create_domain(), points);\n  }();\n\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::SpecifiedPoints<SpecifiedPointsTag<2>, 2>>(\n      \"SpecifiedPoints\");\n\n  InterpTargetTestHelpers::test_interpolation_target<\n      SpecifiedPointsTag<2>, 2,\n      intrp::Tags::SpecifiedPoints<SpecifiedPointsTag<2>, 2>>(\n      created_opts, expected_block_coord_holders);\n}\n\nvoid test_3d() {\n  // Options for SpecifiedPoints\n  intrp::OptionHolders::SpecifiedPoints<3> points_opts(\n      std::vector<std::array<double, 3>>{\n          {std::array<double, 3>{{0.0, 0.0, 0.0}},\n           std::array<double, 3>{{1.0, -0.3, 0.2}},\n           std::array<double, 3>{{-0.8, 1.6, 2.4}},\n           std::array<double, 3>{{0.0, 1.0, 0.0}}}});\n\n  // Test creation of options\n  const auto created_opts =\n      TestHelpers::test_creation<intrp::OptionHolders::SpecifiedPoints<3>>(\n          \"Points: [[0.0, 0.0, 0.0], [1.0, -0.3, 0.2], \"\n          \"[-0.8, 1.6, 2.4], [0.0, 1.0, 0.0]]\");\n  CHECK(created_opts == points_opts);\n\n  const auto domain_creator = domain::creators::Brick(\n      {{-1.0, -1.0, -1.0}}, {{1.0, 2.0, 3.0}}, {{1, 1, 1}}, {{3, 4, 5}},\n      {{false, false, false}});\n\n  const auto expected_block_coord_holders = [&domain_creator]() {\n    tnsr::I<DataVector, 3, Frame::Inertial> points;\n    get<0>(points) = DataVector({{0.0, 1.0, -0.8, 0.0}});\n    get<1>(points) = DataVector({{0.0, -0.3, 1.6, 1.0}});\n    get<2>(points) = DataVector({{0.0, 0.2, 2.4, 0.0}});\n    return block_logical_coordinates(domain_creator.create_domain(), points);\n  }();\n\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::SpecifiedPoints<SpecifiedPointsTag<3>, 3>>(\n      \"SpecifiedPoints\");\n\n  InterpTargetTestHelpers::test_interpolation_target<\n      SpecifiedPointsTag<3>, 3,\n      intrp::Tags::SpecifiedPoints<SpecifiedPointsTag<3>, 3>>(\n      created_opts, expected_block_coord_holders);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.InterpolationTarget.SpecifiedPoints\", \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n\n  test_1d<InterpTargetTestHelpers::ValidPoints::All>();\n  test_1d<InterpTargetTestHelpers::ValidPoints::Some>();\n  test_1d<InterpTargetTestHelpers::ValidPoints::None>();\n  test_2d();\n  test_3d();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_InterpolationTargetSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <iomanip>\n#include <limits>\n#include <random>\n#include <sstream>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/Interpolation/InterpolationTargetTestHelpers.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Spherepack.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\ndomain::creators::Sphere make_sphere() {\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::All) {\n    return {0.9, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n  }\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::None) {\n    return {4.9, 8.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n  }\n  return {3.4, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n}\n\nstruct SphereTag : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  using temporal_id = ::Tags::TimeStepId;\n  using vars_to_interpolate_to_target = tmpl::list<gr::Tags::Lapse<DataVector>>;\n  using compute_items_on_target = tmpl::list<>;\n  using compute_target_points =\n      ::intrp::TargetPoints::Sphere<SphereTag, ::Frame::Inertial>;\n  using post_interpolation_callbacks = tmpl::list<>;\n};\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints, typename Generator>\nvoid test_interpolation_target_sphere(\n    const gsl::not_null<Generator*> generator, const size_t number_of_spheres,\n    const ylm::AngularOrdering angular_ordering) {\n  // Keep bounds a bit inside than inner and outer radius of shell below so the\n  // offset-sphere is still within the domain\n  std::uniform_real_distribution<double> dist{1.2, 4.5};\n  std::vector<double> radii(number_of_spheres);\n  for (size_t i = 0; i < number_of_spheres; i++) {\n    double radius = dist(*generator);\n    while (alg::find(radii, radius) != radii.end()) {\n      radius = dist(*generator);\n    }\n    radii[i] = radius;\n  }\n  const size_t l_max = 18;\n  const std::array<double, 3> center = {{0.05, 0.06, 0.07}};\n\n  CAPTURE(l_max);\n  CAPTURE(center);\n  CAPTURE(radii);\n  CAPTURE(number_of_spheres);\n  CAPTURE(angular_ordering);\n\n  // Options for Sphere\n  std::string radii_str;\n  intrp::OptionHolders::Sphere sphere_opts;\n  std::stringstream ss;\n  ss << std::setprecision(std::numeric_limits<double>::max_digits10);\n  if (number_of_spheres == 1) {\n    // Test the double variant\n    sphere_opts =\n        intrp::OptionHolders::Sphere(l_max, center, radii[0], angular_ordering);\n    ss << radii[0];\n  } else {\n    // Test the vector variant\n    sphere_opts =\n        intrp::OptionHolders::Sphere(l_max, center, radii, angular_ordering);\n    ss << \"[\" << radii[0];\n    for (size_t i = 1; i < number_of_spheres; i++) {\n      ss << \",\" << radii[i];\n    }\n    ss << \"]\";\n  }\n  radii_str = ss.str();\n\n  // Test creation of options\n  const auto created_opts =\n      TestHelpers::test_creation<intrp::OptionHolders::Sphere>(\n          \"Center: [0.05, 0.06, 0.07]\\n\"\n          \"Radius: \" +\n          radii_str +\n          \"\\n\"\n          \"LMax: 18\\n\"\n          \"AngularOrdering: \" +\n          std::string(MakeString{} << angular_ordering));\n  CHECK(created_opts == sphere_opts);\n\n  const auto domain_creator = make_sphere<ValidPoints>();\n\n  TestHelpers::db::test_simple_tag<intrp::Tags::Sphere<SphereTag>>(\"Sphere\");\n\n  const auto expected_block_coord_holders = [&domain_creator, &radii, &center,\n                                             &angular_ordering,\n                                             &number_of_spheres]() {\n    // How many points are supposed to be in a Strahlkorper,\n    // reproduced here by hand for the test.\n    const size_t n_theta = l_max + 1;\n    const size_t n_phi = 2 * l_max + 1;\n\n    // Have to turn this into a set to guarantee ordering\n    const std::set<double> radii_set(radii.begin(), radii.end());\n\n    tnsr::I<DataVector, 3, Frame::Inertial> points(number_of_spheres * n_theta *\n                                                   n_phi);\n\n    size_t s = 0;\n    for (const double radius : radii_set) {\n      // The theta points of a Strahlkorper are Gauss-Legendre points.\n      const std::vector<double> theta_points = []() {\n        std::vector<double> thetas(n_theta);\n        std::vector<double> work(n_theta + 1);\n        std::vector<double> unused_weights(n_theta);\n        int err = 0;\n        gaqd_(static_cast<int>(n_theta), thetas.data(), unused_weights.data(),\n              work.data(), static_cast<int>(n_theta + 1), &err);\n        return thetas;\n      }();\n\n      const double two_pi_over_n_phi = 2.0 * M_PI / n_phi;\n      if (angular_ordering == ylm::AngularOrdering::Strahlkorper) {\n        for (size_t i_phi = 0; i_phi < n_phi; ++i_phi) {\n          const double phi = two_pi_over_n_phi * i_phi;\n          for (size_t i_theta = 0; i_theta < n_theta; ++i_theta) {\n            const double theta = theta_points[i_theta];\n            points.get(0)[s] = radius * sin(theta) * cos(phi) + center[0];\n            points.get(1)[s] = radius * sin(theta) * sin(phi) + center[1],\n            points.get(2)[s] = radius * cos(theta) + center[2];\n            ++s;\n          }\n        }\n      } else {\n        for (size_t i_theta = 0; i_theta < n_theta; ++i_theta) {\n          for (size_t i_phi = 0; i_phi < n_phi; ++i_phi) {\n            const double phi = two_pi_over_n_phi * i_phi;\n            const double theta = theta_points[i_theta];\n            points.get(0)[s] = radius * sin(theta) * cos(phi) + center[0];\n            points.get(1)[s] = radius * sin(theta) * sin(phi) + center[1],\n            points.get(2)[s] = radius * cos(theta) + center[2];\n            ++s;\n          }\n        }\n      }\n    }\n    return block_logical_coordinates(domain_creator.create_domain(), points);\n  }();\n\n  InterpTargetTestHelpers::test_interpolation_target<\n      SphereTag, 3, intrp::Tags::Sphere<SphereTag>>(\n      created_opts, expected_block_coord_holders);\n}\n\nvoid test_sphere_errors() {\n  CHECK_THROWS_WITH(\n      ([]() {\n        const auto created_opts =\n            TestHelpers::test_creation<intrp::OptionHolders::Sphere>(\n                \"Center: [0.05, 0.06, 0.07]\\n\"\n                \"Radius: [1.0, 1.0]\\n\"\n                \"LMax: 18\\n\"\n                \"AngularOrdering: Cce\");\n      })(),\n      Catch::Matchers::ContainsSubstring(\n          \"into radii for Sphere interpolation target. It already \"\n          \"exists. Existing radii are\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        const auto created_opts =\n            TestHelpers::test_creation<intrp::OptionHolders::Sphere>(\n                \"Center: [0.05, 0.06, 0.07]\\n\"\n                \"Radius: [-1.0]\\n\"\n                \"LMax: 18\\n\"\n                \"AngularOrdering: Cce\");\n      })(),\n      Catch::Matchers::ContainsSubstring(\"Radius must be positive\"));\n  CHECK_THROWS_WITH(\n      ([]() {\n        const auto created_opts =\n            TestHelpers::test_creation<intrp::OptionHolders::Sphere>(\n                \"Center: [0.05, 0.06, 0.07]\\n\"\n                \"Radius: -1.0\\n\"\n                \"LMax: 18\\n\"\n                \"AngularOrdering: Cce\");\n      })(),\n      Catch::Matchers::ContainsSubstring(\"Radius must be positive\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.InterpolationTarget.Sphere\",\n                  \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  test_sphere_errors();\n  MAKE_GENERATOR(gen);\n  for (size_t num_spheres : {1_st, 2_st, 3_st}) {\n    test_interpolation_target_sphere<InterpTargetTestHelpers::ValidPoints::All>(\n        make_not_null(&gen), num_spheres, ylm::AngularOrdering::Cce);\n    test_interpolation_target_sphere<InterpTargetTestHelpers::ValidPoints::All>(\n        make_not_null(&gen), num_spheres, ylm::AngularOrdering::Strahlkorper);\n    test_interpolation_target_sphere<\n        InterpTargetTestHelpers::ValidPoints::None>(\n        make_not_null(&gen), num_spheres, ylm::AngularOrdering::Strahlkorper);\n    // ValidPoints::Some is not tested as the radii of the\n    // interpolation targets are set randomly so it is difficult to\n    // arrange that only a subset of the target points are\n    // valid/invalid.\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_InterpolationTargetVarsFromElement.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/Creators/TimeDependence/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/TimeDependence/RotationAboutZAxis.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/FunctionsOfTime/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/FunctionsOfTime/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InterpolationTargetVarsFromElement.hpp\"\n#include \"ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace {\n\nnamespace Tags {\n// Simple Variables tag for test.\nstruct TestSolution : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n// Simple DataBoxItem to test.\nstruct Square : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct SquareCompute : Square, db::ComputeTag {\n  static void function(gsl::not_null<Scalar<DataVector>*> result,\n                       const Scalar<DataVector>& x) {\n    get(*result) = square(get(x));\n  }\n  using argument_tags = tmpl::list<TestSolution>;\n  using base = Square;\n  using return_type = Scalar<DataVector>;\n};\n}  // namespace Tags\n\nstruct ResetFoT {\n  static void apply(\n      const gsl::not_null<std::unordered_map<\n          std::string,\n          std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>*>\n          f_of_t_list,\n      const std::string& f_of_t_name, const double new_expiration_time) {\n    const double initial_time = f_of_t_list->at(f_of_t_name)->time_bounds()[0];\n    std::array<DataVector, 4> init_func_and_2_derivs{\n        DataVector{3, 0.0}, DataVector{3, 0.0}, DataVector{3, 0.0},\n        DataVector{3, 0.0}};\n\n    f_of_t_list->erase(f_of_t_name);\n\n    (*f_of_t_list)[f_of_t_name] =\n        std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<3>>(\n            initial_time, std::move(init_func_and_2_derivs),\n            new_expiration_time);\n  }\n};\n\nstruct MockComputeTargetPoints\n    : tt::ConformsTo<intrp::protocols::ComputeTargetPoints> {\n  using is_sequential = std::false_type;\n  using frame = ::Frame::Inertial;\n  template <typename Metavariables, typename DbTags>\n  static tnsr::I<DataVector, 3, Frame::Inertial> points(\n      const db::DataBox<DbTags>& /*box*/,\n      const tmpl::type_<Metavariables>& /*meta*/) {\n    // Need 10 points to agree with test.\n    const size_t num_pts = 10;\n    // Doesn't matter what the points are; they are not used except\n    // that they need to be inside the domain.\n    tnsr::I<DataVector, 3, Frame::Inertial> target_points(num_pts);\n    for (size_t n = 0; n < num_pts; ++n) {\n      for (size_t d = 0; d < 3; ++d) {\n        target_points.get(d)[n] = 1.0 + 0.01 * n + 0.5 * d;\n      }\n    }\n    return target_points;\n  }\n\n  template <typename Metavariables, typename DbTags, typename TemporalId>\n  static tnsr::I<DataVector, 3, Frame::Inertial> points(\n      const db::DataBox<DbTags>& box, const tmpl::type_<Metavariables>& meta,\n      const TemporalId& /*temporal_id*/) {\n    return points(box, meta);\n  }\n};\n\n// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nsize_t num_callback_calls = 0;\n\nstruct MockPostInterpolationCallback\n    : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {\n  template <typename DbTags, typename Metavariables, typename TemporalId>\n  static void apply(const db::DataBox<DbTags>& box,\n                    const Parallel::GlobalCache<Metavariables>& /*cache*/,\n                    const TemporalId& temporal_id) {\n    static_assert(std::is_same_v<TemporalId, TimeStepId>,\n                  \"MockPostInterpolationCallback expects a TimeStepId as its \"\n                  \"temporal ID.\");\n    // This callback simply checks that the points are as expected.\n    Slab slab(0.0, 1.0);\n    const TimeStepId first_temporal_id(true, 0, Time(slab, Rational(13, 15)));\n    const TimeStepId second_temporal_id(true, 0, Time(slab, Rational(14, 15)));\n    if (temporal_id == first_temporal_id) {\n      const Scalar<DataVector> expected{\n          DataVector{{0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0, 81.0}}};\n      CHECK_ITERABLE_APPROX(expected, db::get<Tags::Square>(box));\n    } else if (temporal_id == second_temporal_id) {\n      const Scalar<DataVector> expected{DataVector{\n          {0.0, 4.0, 16.0, 36.0, 64.0, 100.0, 144.0, 196.0, 256.0, 324.0}}};\n      CHECK_ITERABLE_APPROX(expected, db::get<Tags::Square>(box));\n    } else {\n      // Should never get here.\n      CHECK(false);\n    }\n\n    ++num_callback_calls;\n  }\n};\n\nstruct MockPostInterpolationCallbackWithInvalidPoints\n    : public MockPostInterpolationCallback {\n  static constexpr double fill_invalid_points_with = 0.0;\n};\n\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct mock_interpolation_target {\n  static_assert(\n      tt::assert_conforms_to_v<InterpolationTargetTag,\n                               intrp::protocols::InterpolationTargetTag>);\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using component_being_mocked =\n      intrp::InterpolationTarget<Metavariables, InterpolationTargetTag>;\n  using const_global_cache_tags =\n      tmpl::list<domain::Tags::Domain<Metavariables::volume_dim>,\n                 intrp::Tags::Verbosity>;\n  using simple_tags = typename intrp::Actions::InitializeInterpolationTarget<\n      Metavariables, InterpolationTargetTag>::simple_tags;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<ActionTesting::InitializeDataBox<\n          simple_tags,\n          typename InterpolationTargetTag::compute_items_on_target>>>>;\n};\n\ntemplate <bool UseTimeDepMaps>\nstruct MockMetavariables {\n  struct InterpolationTargetA\n      : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using vars_to_interpolate_to_target = tmpl::list<Tags::TestSolution>;\n    using compute_items_on_target = tmpl::list<Tags::SquareCompute>;\n    using compute_target_points = MockComputeTargetPoints;\n    using post_interpolation_callbacks =\n        tmpl::list<MockPostInterpolationCallback,\n                   MockPostInterpolationCallbackWithInvalidPoints>;\n  };\n  static constexpr size_t volume_dim = 3;\n  using interpolation_target_tags = tmpl::list<InterpolationTargetA>;\n  using mutable_global_cache_tags =\n      tmpl::conditional_t<UseTimeDepMaps,\n                          tmpl::list<domain::Tags::FunctionsOfTimeInitialize>,\n                          tmpl::list<>>;\n\n  using component_list = tmpl::list<\n      mock_interpolation_target<MockMetavariables, InterpolationTargetA>>;\n};\n\ntemplate <bool UseTimeDepMaps>\nvoid test() {\n  domain::creators::register_derived_with_charm();\n  num_callback_calls = 0_st;\n\n  if constexpr (UseTimeDepMaps) {\n    domain::creators::time_dependence::register_derived_with_charm();\n    domain::FunctionsOfTime::register_derived_with_charm();\n  }\n\n  using metavars = MockMetavariables<UseTimeDepMaps>;\n  using temporal_id_type =\n      typename metavars::InterpolationTargetA::temporal_id::type;\n  using target_component =\n      mock_interpolation_target<metavars,\n                                typename metavars::InterpolationTargetA>;\n\n  Slab slab(0.0, 1.0);\n  const TimeStepId first_temporal_id(true, 0, Time(slab, Rational(13, 15)));\n  const TimeStepId second_temporal_id(true, 0, Time(slab, Rational(14, 15)));\n  const domain::creators::Sphere domain_creator = [&slab]() {\n    if constexpr (UseTimeDepMaps) {\n      return domain::creators::Sphere(\n          0.9, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false,\n          std::nullopt, {}, {domain::CoordinateMaps::Distribution::Linear},\n          ShellWedges::All,\n          // Doesn't actually have to move. Just needs to go through the time\n          // dependent code\n          std::make_unique<\n              domain::creators::time_dependence::RotationAboutZAxis<3>>(\n              slab.start().value(), 0.0, 0.0, 0.0));\n    } else {\n      (void)slab;\n      return domain::creators::Sphere(\n          0.9, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false);\n    }\n  }();\n\n  // Type alias for better readability below.\n  using vars_type = Variables<\n      typename metavars::InterpolationTargetA::vars_to_interpolate_to_target>;\n\n  std::unordered_map<std::string, double> init_expr_times{};\n  const std::string name = \"Rotation\";\n  init_expr_times[name] = slab.end().value();\n\n  // Initialization\n  ActionTesting::MockRuntimeSystem<metavars> runner = [&domain_creator,\n                                                       &init_expr_times]() {\n    if constexpr (UseTimeDepMaps) {\n      return ActionTesting::MockRuntimeSystem<metavars>(\n          {domain_creator.create_domain(), ::Verbosity::Silent},\n          {domain_creator.functions_of_time(init_expr_times)});\n    } else {\n      (void)init_expr_times;\n      return ActionTesting::MockRuntimeSystem<metavars>(\n          {domain_creator.create_domain(), ::Verbosity::Silent});\n    }\n  }();\n  ActionTesting::emplace_component_and_initialize<target_component>(\n      &runner, 0,\n      {std::unordered_map<temporal_id_type, std::unordered_set<size_t>>{},\n       std::unordered_map<temporal_id_type, std::unordered_set<size_t>>{},\n       std::unordered_set<temporal_id_type>{}, std::deque<temporal_id_type>{},\n       std::unordered_map<temporal_id_type,\n                          Variables<typename metavars::InterpolationTargetA::\n                                        vars_to_interpolate_to_target>>{},\n       // Default-constructed Variables cause problems, so below\n       // we construct the Variables with a single point.\n       vars_type{1}});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Now set up the vars and global offsets\n  std::vector<::Variables<\n      typename metavars::InterpolationTargetA::vars_to_interpolate_to_target>>\n      vars_src;\n  std::vector<std::vector<size_t>> global_offsets;\n\n  // Lambda that adds more data to vars_src and global_offsets.\n  auto add_to_vars_src = [&vars_src, &global_offsets](\n                             const std::vector<double>& vals,\n                             const std::vector<size_t>& offset_vals) {\n    vars_src.emplace_back(\n        ::Variables<typename metavars::InterpolationTargetA::\n                        vars_to_interpolate_to_target>{vals.size()});\n    global_offsets.emplace_back(offset_vals);\n    auto& scalar = get<Tags::TestSolution>(vars_src.back());\n    for (size_t i = 0; i < vals.size(); ++i) {\n      get<>(scalar)[i] = vals[i];\n    }\n  };\n\n  // Compute block_logical coordinates (which will not be used except\n  // for its size and for determining which points are valid/invalid).\n  // For this test, there are 10 points and all are valid; the call\n  // to intrp::InterpolationTarget_detail::block_logical_coords should\n  // set up those coordinates appropriately.\n  auto& target_box =\n      ActionTesting::get_databox<target_component>(make_not_null(&runner), 0);\n\n  std::vector<BlockLogicalCoords<3>> block_logical_coords{};\n  if constexpr (UseTimeDepMaps) {\n    block_logical_coords =\n        intrp::InterpolationTarget_detail::block_logical_coords<\n            typename metavars::InterpolationTargetA>(\n            target_box, ActionTesting::cache<target_component>(runner, 0),\n            first_temporal_id);\n  } else {\n    block_logical_coords =\n        intrp::InterpolationTarget_detail::block_logical_coords<\n            typename metavars::InterpolationTargetA>(target_box,\n                                                     tmpl::type_<metavars>{});\n  }\n\n  // Add points at first_temporal_id\n  add_to_vars_src({{3.0, 6.0}}, {{3, 6}});\n  add_to_vars_src({{2.0, 7.0}}, {{2, 7}});\n  ActionTesting::simple_action<\n      target_component, intrp::Actions::InterpolationTargetVarsFromElement<\n                            typename metavars::InterpolationTargetA>>(\n      make_not_null(&runner), 0, vars_src, block_logical_coords, global_offsets,\n      first_temporal_id);\n\n  // It should know about only one temporal_id\n  CHECK(ActionTesting::get_databox_tag<\n            target_component, intrp::Tags::TemporalIds<temporal_id_type>>(\n            runner, 0)\n            .size() == 1);\n  // It should have accumulated 4 points by now.\n  CHECK(\n      ActionTesting::get_databox_tag<\n          target_component,\n          intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(runner, 0)\n          .at(first_temporal_id)\n          .size() == 4);\n  CHECK(num_callback_calls == 0_st);\n\n  // Add some more points at first_temporal_id\n  vars_src.clear();\n  global_offsets.clear();\n  add_to_vars_src({{1.0, 888888.0}},\n                  {{1, 6}});  // 6 is repeated, point will be ignored.\n  add_to_vars_src({{8.0, 0.0, 4.0}}, {{8, 0, 4}});\n  ActionTesting::simple_action<\n      target_component, intrp::Actions::InterpolationTargetVarsFromElement<\n                            typename metavars::InterpolationTargetA>>(\n      make_not_null(&runner), 0, vars_src, block_logical_coords, global_offsets,\n      first_temporal_id);\n\n  // It should have interpolated 8 points by now. (The ninth point had\n  // a repeated global_offsets so it should be ignored)\n  CHECK(\n      ActionTesting::get_databox_tag<\n          target_component,\n          intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(runner, 0)\n          .at(first_temporal_id)\n          .size() == 8);\n\n  // Add points at second_temporal_id\n  vars_src.clear();\n  global_offsets.clear();\n  add_to_vars_src({{10.0, 16.0}}, {{5, 8}});\n  add_to_vars_src({{6.0, 8.0}}, {{3, 4}});\n  ActionTesting::simple_action<\n      target_component, intrp::Actions::InterpolationTargetVarsFromElement<\n                            typename metavars::InterpolationTargetA>>(\n      make_not_null(&runner), 0, vars_src, block_logical_coords, global_offsets,\n      second_temporal_id);\n\n  // It should know about two temporal_ids\n  CHECK(ActionTesting::get_databox_tag<\n            target_component, intrp::Tags::TemporalIds<temporal_id_type>>(\n            runner, 0)\n            .size() == 2);\n  // It should have accumulated 4 points for second_temporal_id.\n  CHECK(\n      ActionTesting::get_databox_tag<\n          target_component,\n          intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(runner, 0)\n          .at(second_temporal_id)\n          .size() == 4);\n  // ... and still have 8 points for first_temporal_id.\n  CHECK(\n      ActionTesting::get_databox_tag<\n          target_component,\n          intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(runner, 0)\n          .at(first_temporal_id)\n          .size() == 8);\n  CHECK(num_callback_calls == 0_st);\n\n  // Add more points at second_temporal_id\n  vars_src.clear();\n  global_offsets.clear();\n  add_to_vars_src({{2.0, 888888.0}},\n                  {{1, 5}});  // 5 is repeated, point will be ignored.\n  add_to_vars_src({{4.0, 0.0, 12.0}}, {{2, 0, 6}});\n  ActionTesting::simple_action<\n      target_component, intrp::Actions::InterpolationTargetVarsFromElement<\n                            typename metavars::InterpolationTargetA>>(\n      make_not_null(&runner), 0, vars_src, block_logical_coords, global_offsets,\n      second_temporal_id);\n\n  // It should have interpolated 8 points by now. (The ninth point had\n  // a repeated global_offsets so it should be ignored)\n  CHECK(\n      ActionTesting::get_databox_tag<\n          target_component,\n          intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(runner, 0)\n          .at(second_temporal_id)\n          .size() == 8);\n\n  // Reset FoT expiration to something before the current slab value\n  if constexpr (UseTimeDepMaps) {\n    Parallel::mutate<domain::Tags::FunctionsOfTime, ResetFoT>(\n        ActionTesting::cache<target_component>(runner, 0), name,\n        first_temporal_id.substep_time() / 2.0);\n  }\n\n  // Now add enough points at second_temporal_id so it triggers the\n  // callback (if the FoTs are ready).  (The first temporal id doesn't yet have\n  // enough points, so here we also test asynchronicity).\n  vars_src.clear();\n  global_offsets.clear();\n  add_to_vars_src({{18.0, 14.0}}, {{9, 7}});\n  ActionTesting::simple_action<\n      target_component, intrp::Actions::InterpolationTargetVarsFromElement<\n                            typename metavars::InterpolationTargetA>>(\n      make_not_null(&runner), 0, vars_src, block_logical_coords, global_offsets,\n      second_temporal_id);\n\n  if constexpr (UseTimeDepMaps) {\n    // The interpolation should have finished, but the callback shouldn't have\n    // been called and the target shouldn't have been cleaned up. So we should\n    // still have two temporal ids, and all of the filled points. And the first\n    // temporal id should not have been touched\n    CHECK(ActionTesting::get_databox_tag<\n              target_component,\n              intrp::Tags::CompletedTemporalIds<temporal_id_type>>(runner, 0)\n              .size() == 0);\n    CHECK(ActionTesting::get_databox_tag<\n              target_component, intrp::Tags::TemporalIds<temporal_id_type>>(\n              runner, 0)\n              .size() == 2);\n    CHECK(ActionTesting::get_databox_tag<\n              target_component,\n              intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(\n              runner, 0)\n              .count(second_temporal_id) == 1);\n    CHECK(ActionTesting::get_databox_tag<\n              target_component,\n              intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(\n              runner, 0)\n              .at(second_temporal_id)\n              .size() == 10);\n    CHECK(ActionTesting::get_databox_tag<\n              target_component,\n              intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(\n              runner, 0)\n              .count(first_temporal_id) == 1);\n    CHECK(ActionTesting::get_databox_tag<\n              target_component,\n              intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(\n              runner, 0)\n              .at(first_temporal_id)\n              .size() == 8);\n    CHECK(num_callback_calls == 0_st);\n\n    // Change the expiration back to the original value, which will cause a\n    // simple action to run on the target.\n    Parallel::mutate<domain::Tags::FunctionsOfTime, ResetFoT>(\n        ActionTesting::cache<target_component>(runner, 0), name,\n        init_expr_times.at(name));\n    // Should be a queued simple action on the target component\n    CHECK(ActionTesting::number_of_queued_simple_actions<target_component>(\n              runner, 0) == 1);\n    ActionTesting::invoke_queued_simple_action<target_component>(\n        make_not_null(&runner), 0);\n  }\n\n  // It should have interpolated all the points by now,\n  // and the list of points should have been cleaned up.\n  CHECK(\n      ActionTesting::get_databox_tag<\n          target_component,\n          intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(runner, 0)\n          .count(second_temporal_id) == 0);\n  // There should be only 1 temporal_id left.\n  // And its value should be first_temporal_id.\n  CHECK(ActionTesting::get_databox_tag<\n            target_component, intrp::Tags::TemporalIds<temporal_id_type>>(\n            runner, 0)\n            .size() == 1);\n  CHECK(ActionTesting::get_databox_tag<\n            target_component, intrp::Tags::TemporalIds<temporal_id_type>>(\n            runner, 0)\n            .contains(first_temporal_id));\n  // There should be 1 CompletedTemporalId, and its value\n  // should be second_temporal_id.\n  CHECK(ActionTesting::get_databox_tag<\n            target_component,\n            intrp::Tags::CompletedTemporalIds<temporal_id_type>>(runner, 0)\n            .size() == 1);\n  CHECK(ActionTesting::get_databox_tag<\n            target_component,\n            intrp::Tags::CompletedTemporalIds<temporal_id_type>>(runner, 0)\n            .front() == second_temporal_id);\n  // There are two callbacks\n  CHECK(num_callback_calls == 2_st);\n\n  // Now add enough points at first_temporal_id so it triggers the\n  // callback.\n  vars_src.clear();\n  global_offsets.clear();\n  add_to_vars_src({{9.0, 5.0}}, {{9, 5}});\n  ActionTesting::simple_action<\n      target_component, intrp::Actions::InterpolationTargetVarsFromElement<\n                            typename metavars::InterpolationTargetA>>(\n      make_not_null(&runner), 0, vars_src, block_logical_coords, global_offsets,\n      first_temporal_id);\n\n  // It should have interpolated all the points by now,\n  // and the list of points should have been cleaned up.\n  CHECK(\n      ActionTesting::get_databox_tag<\n          target_component,\n          intrp::Tags::IndicesOfFilledInterpPoints<temporal_id_type>>(runner, 0)\n          .count(first_temporal_id) == 0);\n  // There should be no temporal_ids left.\n  CHECK(ActionTesting::get_databox_tag<\n            target_component, intrp::Tags::TemporalIds<temporal_id_type>>(\n            runner, 0)\n            .empty());\n  // There should be 2 CompletedTemporalIds\n  CHECK(ActionTesting::get_databox_tag<\n            target_component,\n            intrp::Tags::CompletedTemporalIds<temporal_id_type>>(runner, 0)\n            .size() == 2);\n  CHECK(ActionTesting::get_databox_tag<\n            target_component,\n            intrp::Tags::CompletedTemporalIds<temporal_id_type>>(runner, 0)\n            .at(0) == second_temporal_id);\n  CHECK(ActionTesting::get_databox_tag<\n            target_component,\n            intrp::Tags::CompletedTemporalIds<temporal_id_type>>(runner, 0)\n            .at(1) == first_temporal_id);\n  // There are two more callbacks\n  CHECK(num_callback_calls == 4_st);\n\n  // Should be no queued simple action.\n  CHECK(\n      ActionTesting::is_simple_action_queue_empty<target_component>(runner, 0));\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Interpolator.TargetVarsFromElement\",\n                  \"[Unit]\") {\n  test<false>();\n  test<true>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_InterpolationTargetWedgeSectionTorus.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/Interpolation/InterpolationTargetTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/WedgeSectionTorus.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\ndomain::creators::Sphere make_sphere() {\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::All) {\n    return {0.9, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n  }\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::None) {\n    return {4.9, 8.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n  }\n  return {3.4, 4.9, domain::creators::Sphere::Excision{}, 1_st, 5_st, false};\n}\n\nstruct WedgeTargetTag\n    : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n  using temporal_id = ::Tags::TimeStepId;\n  using vars_to_interpolate_to_target = tmpl::list<gr::Tags::Lapse<DataVector>>;\n  using compute_items_on_target = tmpl::list<>;\n  using compute_target_points =\n      ::intrp::TargetPoints::WedgeSectionTorus<WedgeTargetTag>;\n  using post_interpolation_callbacks = tmpl::list<>;\n};\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\nvoid test_r_theta_lgl() {\n  const size_t num_radial = 3;\n  const size_t num_theta = 4;\n  const size_t num_phi = 5;\n  // Test with a torus that is not symmetric above/below the equator.\n  intrp::OptionHolders::WedgeSectionTorus wedge_section_torus_opts(\n      1.2, 4.0, 0.35 * M_PI, 0.55 * M_PI, num_radial, num_theta, num_phi, false,\n      false);\n\n  const auto domain_creator = make_sphere<ValidPoints>();\n\n  const size_t num_total = num_radial * num_theta * num_phi;\n  const auto expected_block_coord_holders = [&]() {\n    tnsr::I<DataVector, 3, Frame::Inertial> points(num_total);\n    for (size_t r = 0; r < num_radial; ++r) {\n      const double radius =\n          2.6 +\n          1.4 *\n              Spectral::collocation_points<Spectral::Basis::Legendre,\n                                           Spectral::Quadrature::GaussLobatto>(\n                  num_radial)[r];\n      for (size_t t = 0; t < num_theta; ++t) {\n        const double theta =\n            M_PI * (0.45 + 0.1 * Spectral::collocation_points<\n                                     Spectral::Basis::Legendre,\n                                     Spectral::Quadrature::GaussLobatto>(\n                                     num_theta)[t]);\n        for (size_t p = 0; p < num_phi; ++p) {\n          const double phi = 2.0 * M_PI * p / num_phi;\n          const size_t i = r + t * num_radial + p * num_theta * num_radial;\n          get<0>(points)[i] = radius * sin(theta) * cos(phi);\n          get<1>(points)[i] = radius * sin(theta) * sin(phi);\n          get<2>(points)[i] = radius * cos(theta);\n        }\n      }\n    }\n    return block_logical_coordinates(domain_creator.create_domain(), points);\n  }();\n\n  InterpTargetTestHelpers::test_interpolation_target<\n      WedgeTargetTag, 3, intrp::Tags::WedgeSectionTorus<WedgeTargetTag>>(\n      wedge_section_torus_opts, expected_block_coord_holders);\n}\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\nvoid test_r_theta_uniform() {\n  const size_t num_radial = 4;\n  const size_t num_theta = 5;\n  const size_t num_phi = 6;\n  // Test with a torus that is symmetric above/below the equator.\n  intrp::OptionHolders::WedgeSectionTorus wedge_section_torus_opts(\n      1.8, 3.6, 0.25 * M_PI, 0.75 * M_PI, num_radial, num_theta, num_phi, true,\n      true);\n\n  const auto domain_creator = make_sphere<ValidPoints>();\n\n  const size_t num_total = num_radial * num_theta * num_phi;\n  const auto expected_block_coord_holders = [&domain_creator, &num_total]() {\n    tnsr::I<DataVector, 3, Frame::Inertial> points(num_total);\n    for (size_t r = 0; r < num_radial; ++r) {\n      const double radius = 1.8 + 1.8 * r / (num_radial - 1.0);\n      for (size_t t = 0; t < num_theta; ++t) {\n        const double theta = M_PI * (0.25 + 0.5 * t / (num_theta - 1.0));\n        for (size_t p = 0; p < num_phi; ++p) {\n          const double phi = 2.0 * M_PI * p / num_phi;\n          const size_t i = r + t * num_radial + p * num_theta * num_radial;\n          get<0>(points)[i] = radius * sin(theta) * cos(phi);\n          get<1>(points)[i] = radius * sin(theta) * sin(phi);\n          get<2>(points)[i] = radius * cos(theta);\n        }\n      }\n    }\n    return block_logical_coordinates(domain_creator.create_domain(), points);\n  }();\n\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::WedgeSectionTorus<WedgeTargetTag>>(\"WedgeSectionTorus\");\n\n  InterpTargetTestHelpers::test_interpolation_target<\n      WedgeTargetTag, 3, intrp::Tags::WedgeSectionTorus<WedgeTargetTag>>(\n      wedge_section_torus_opts, expected_block_coord_holders);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.InterpolationTarget.WedgeSectionTorus\",\n    \"[Unit]\") {\n  domain::creators::register_derived_with_charm();\n  // Check creating the options\n  const auto created_torus =\n      TestHelpers::test_creation<intrp::OptionHolders::WedgeSectionTorus>(\n          \"MinRadius: 1.8\\n\"\n          \"MaxRadius: 20.\\n\"\n          \"MinTheta: 0.785\\n\"\n          \"MaxTheta: 2.356\\n\"\n          \"NumberRadialPoints: 20\\n\"\n          \"NumberThetaPoints: 10\\n\"\n          \"NumberPhiPoints: 20\\n\"\n          \"UniformRadialGrid: false\\n\"\n          \"UniformThetaGrid: true\\n\");\n  CHECK(created_torus == intrp::OptionHolders::WedgeSectionTorus(\n                             1.8, 20., 0.785, 2.356, 20, 10, 20, false, true));\n\n  // Check computing the points\n  test_r_theta_lgl<InterpTargetTestHelpers::ValidPoints::All>();\n  test_r_theta_lgl<InterpTargetTestHelpers::ValidPoints::Some>();\n  test_r_theta_lgl<InterpTargetTestHelpers::ValidPoints::None>();\n  test_r_theta_uniform<InterpTargetTestHelpers::ValidPoints::All>();\n  test_r_theta_uniform<InterpTargetTestHelpers::ValidPoints::Some>();\n  test_r_theta_uniform<InterpTargetTestHelpers::ValidPoints::None>();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_ObserveLineSegment.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/Creators/Tags/Domain.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/IO/VolumeData.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveLineSegment.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/LineSegment.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\n// Simple DataBoxItems for test.\nnamespace Tags {\nstruct TestSolution : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct Square : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct SquareCompute : Square, db::ComputeTag {\n  static void function(gsl::not_null<Scalar<DataVector>*> result,\n                       const Scalar<DataVector>& x) {\n    get(*result) = square(get(x));\n  }\n  using argument_tags = tmpl::list<TestSolution>;\n  using base = Square;\n  using return_type = Scalar<DataVector>;\n};\n}  // namespace Tags\n\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using const_global_cache_tags =\n      tmpl::list<observers::Tags::ReductionFileName>;\n  using simple_tags =\n      typename observers::Actions::InitializeWriter<Metavariables>::simple_tags;\n  using compute_tags = typename observers::Actions::InitializeWriter<\n      Metavariables>::compute_tags;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<observers::Actions::InitializeWriter<Metavariables>>>,\n      Parallel::PhaseActions<Parallel::Phase::Register, tmpl::list<>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n};\n\ntemplate <size_t Dim>\nstruct MockMetavariables {\n  static constexpr size_t volume_dim = Dim;\n\n  struct LineA : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target =\n        tmpl::list<Tags::TestSolution,\n                   gr::Tags::SpatialMetric<DataVector, volume_dim>,\n                   domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n    using compute_items_on_target = tmpl::list<Tags::SquareCompute>;\n    using compute_target_points =\n        intrp::TargetPoints::LineSegment<LineA, volume_dim, Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveLineSegment<\n            tmpl::append<vars_to_interpolate_to_target,\n                         tmpl::list<Tags::Square>>,\n            LineA>>;\n  };\n\n  struct LineB : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using vars_to_interpolate_to_target =\n        tmpl::list<Tags::TestSolution,\n                   gr::Tags::SpatialMetric<DataVector, volume_dim>,\n                   domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n    using compute_items_on_target = tmpl::list<Tags::SquareCompute>;\n    using compute_target_points =\n        intrp::TargetPoints::LineSegment<LineB, volume_dim, Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveLineSegment<\n            tmpl::append<vars_to_interpolate_to_target,\n                         tmpl::list<Tags::Square>>,\n            LineB>>;\n  };\n\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  using interpolator_source_vars =\n      tmpl::list<Tags::TestSolution,\n                 gr::Tags::SpatialMetric<DataVector, volume_dim>,\n                 domain::Tags::Coordinates<volume_dim, Frame::Inertial>>;\n  using interpolation_target_tags = tmpl::list<LineA, LineB>;\n  using component_list = tmpl::list<MockObserverWriter<MockMetavariables>>;\n};\n\n// test function which will be interpolated\ntemplate <size_t Dim>\nDataVector test_function(\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& coords) {\n  DataVector res = sin(coords.get(0));\n  if constexpr (Dim > 1) {\n    res += cos(coords.get(1));\n  }\n  if constexpr (Dim > 2) {\n    res += 3.5 * coords.get(2);\n  }\n  return res;\n}\n\ntemplate <size_t Dim, typename Spacetime>\nvoid run_test(const intrp::OptionHolders::LineSegment<Dim>& line_segment_opts_A,\n              const intrp::OptionHolders::LineSegment<Dim>& line_segment_opts_B,\n              const Spacetime& spacetime, const bool expect_nans = false) {\n  // Check if either file generated by this test exists and remove them\n  // if so. Check for both files existing before the test runs, since\n  // both files get written when evaluating the list of post interpolation\n  // callbacks below.\n  const std::string h5_file_prefix = \"Test_ObserveLineSegment\";\n  const auto h5_file_name = h5_file_prefix + \".h5\";\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n\n  using metavars = MockMetavariables<Dim>;\n\n  // Test That ObserveTimeSeriesOnSurface indeed does conform to its protocol\n  using callback_A =\n      tmpl::front<typename metavars::LineA::post_interpolation_callbacks>;\n  using callback_B =\n      tmpl::front<typename metavars::LineB::post_interpolation_callbacks>;\n  using obs_writer = MockObserverWriter<metavars>;\n  using protocol = intrp::protocols::PostInterpolationCallback;\n  static_assert(tt::assert_conforms_to_v<callback_A, protocol>);\n  static_assert(tt::assert_conforms_to_v<callback_B, protocol>);\n\n  tuples::TaggedTuple<observers::Tags::ReductionFileName> tuple_of_opts{\n      h5_file_prefix};\n\n  ActionTesting::MockRuntimeSystem<metavars> runner{std::move(tuple_of_opts)};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n\n  ActionTesting::emplace_nodegroup_component<obs_writer>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_writer>(make_not_null(&runner), 0_st);\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Register);\n\n  Slab slab(0.0, 1.0);\n  TimeStepId temporal_id(true, 0, Time(slab, 0));\n\n  const auto set_box = [&]<typename BoxType, typename TargetTag>(\n                           const gsl::not_null<BoxType*> box,\n                           const intrp::OptionHolders::LineSegment<Dim>&\n                               incoming_options,\n                           TargetTag /*target_tag_v*/\n                       ) {\n    db::mutate<intrp::Tags::LineSegment<TargetTag, Dim>>(\n        [&](const gsl::not_null<intrp::OptionHolders::LineSegment<Dim>*>\n                options) { (*options) = incoming_options; },\n        box);\n\n    auto inertial_coords = intrp::TargetPoints::LineSegment<\n        TargetTag, Dim, Frame::Inertial>::points(*box, tmpl::type_<metavars>{});\n\n    db::mutate<domain::Tags::Coordinates<Dim, Frame::Inertial>,\n               Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, Dim>>(\n        [&](const gsl::not_null<tnsr::I<DataVector, Dim>*> box_inertial_coords,\n            const gsl::not_null<Scalar<DataVector>*> test_solution,\n            const gsl::not_null<tnsr::ii<DataVector, Dim>*> spatial_metric) {\n          (*box_inertial_coords) = std::move(inertial_coords);\n          get(*test_solution) = test_function(*box_inertial_coords);\n          (*spatial_metric) =\n              get<gr::Tags::SpatialMetric<DataVector, Dim>>(spacetime.variables(\n                  *box_inertial_coords, 0.0,\n                  tmpl::list<gr::Tags::SpatialMetric<DataVector, Dim>>{}));\n\n          if (expect_nans) {\n            const ScopedFpeState scoped_fpe{};\n            get(*test_solution) = std::numeric_limits<double>::quiet_NaN();\n            for (size_t i = 0; i < Dim; i++) {\n              box_inertial_coords->get(i) =\n                  std::numeric_limits<double>::quiet_NaN();\n              for (size_t j = 0; j < Dim; j++) {\n                spatial_metric->get(i, j) =\n                    std::numeric_limits<double>::quiet_NaN();\n              }\n            }\n          }\n        },\n        box);\n  };\n\n  using BoxAType = db::compute_databox_type<\n      tmpl::list<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, Dim>,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 intrp::Tags::LineSegment<typename metavars::LineA, Dim>,\n                 Tags::SquareCompute>>;\n  using BoxBType = db::compute_databox_type<\n      tmpl::list<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, Dim>,\n                 domain::Tags::Coordinates<Dim, Frame::Inertial>,\n                 intrp::Tags::LineSegment<typename metavars::LineB, Dim>,\n                 Tags::SquareCompute>>;\n\n  BoxAType box_a{};\n  BoxBType box_b{};\n\n  set_box(make_not_null(&box_a), line_segment_opts_A,\n          typename metavars::LineA{});\n  set_box(make_not_null(&box_b), line_segment_opts_B,\n          typename metavars::LineB{});\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& cache = ActionTesting::cache<obs_writer>(runner, 0_st);\n\n  tmpl::front<typename metavars::LineA::post_interpolation_callbacks>::apply(\n      box_a, cache, temporal_id);\n  tmpl::front<typename metavars::LineB::post_interpolation_callbacks>::apply(\n      box_b, cache, temporal_id);\n\n  // There should be 2 more threaded actions, so invoke them and check\n  // that there are no more.  They should all be on node zero.\n  REQUIRE(ActionTesting::number_of_queued_threaded_actions<obs_writer>(runner,\n                                                                       0) == 2);\n  ActionTesting::invoke_queued_threaded_action<obs_writer>(\n      make_not_null(&runner), 0);\n  ActionTesting::invoke_queued_threaded_action<obs_writer>(\n      make_not_null(&runner), 0);\n\n  CHECK(ActionTesting::is_threaded_action_queue_empty<obs_writer>(runner, 0));\n\n  const auto file = h5::H5File<h5::AccessType::ReadOnly>(h5_file_name);\n\n  auto check_file_contents =\n      [&file, &spacetime](const std::string& group_name,\n                          const tnsr::I<DataVector, Dim>& interpolated_coords) {\n        file.close_current_object();\n        const auto& vol_file = file.get<h5::VolumeData>(group_name);\n        const auto& obs_ids = vol_file.list_observation_ids();\n        CHECK(obs_ids.size() == 1);\n        const auto& obs_value = vol_file.get_observation_value(obs_ids.at(0));\n        CHECK(obs_value == 0.);\n\n        // error due to low resolution of domain\n        Approx custom_approx = Approx::custom().epsilon(1.e-4).scale(1.0);\n\n        for (size_t i = 0; i < interpolated_coords.size(); ++i) {\n          const auto& written_component = vol_file.get_tensor_component(\n              obs_ids.at(0),\n              \"InertialCoordinates\" + interpolated_coords.component_suffix(i));\n          const auto& written_dv = std::get<DataVector>(written_component.data);\n          CHECK_ITERABLE_CUSTOM_APPROX(written_dv, interpolated_coords.get(i),\n                                       custom_approx);\n        }\n\n        const auto interpolated_metric =\n            get<gr::Tags::SpatialMetric<DataVector, Dim>>(spacetime.variables(\n                interpolated_coords, 0.0,\n                tmpl::list<gr::Tags::SpatialMetric<DataVector, Dim>>{}));\n        for (size_t i = 0; i < interpolated_metric.size(); ++i) {\n          const auto& written_component = vol_file.get_tensor_component(\n              obs_ids.at(0),\n              \"SpatialMetric\" + interpolated_metric.component_suffix(i));\n          const auto& written_dv = std::get<DataVector>(written_component.data);\n          CHECK_ITERABLE_CUSTOM_APPROX(written_dv, interpolated_metric[i],\n                                       custom_approx);\n        }\n\n        const auto interpolated_test_solution =\n            test_function(interpolated_coords);\n\n        const auto& written_test_solution_component =\n            vol_file.get_tensor_component(obs_ids.at(0), \"TestSolution\");\n        const auto& written_test_solution_dv =\n            std::get<DataVector>(written_test_solution_component.data);\n\n        CHECK_ITERABLE_CUSTOM_APPROX(written_test_solution_dv,\n                                     interpolated_test_solution, custom_approx);\n\n        const auto interpolated_square = square(interpolated_test_solution);\n        const auto& written_square_component =\n            vol_file.get_tensor_component(obs_ids.at(0), \"Square\");\n        const auto& written_square_dv =\n            std::get<DataVector>(written_square_component.data);\n\n        CHECK_ITERABLE_CUSTOM_APPROX(written_square_dv, interpolated_square,\n                                     custom_approx);\n      };\n\n  auto check_file_contents_are_nans =\n      [&file](const std::string& group_name,\n              const tnsr::I<DataVector, Dim>& interpolated_coords) {\n        file.close_current_object();\n        const auto& vol_file = file.get<h5::VolumeData>(group_name);\n        const auto& obs_ids = vol_file.list_observation_ids();\n        CHECK(obs_ids.size() == 1);\n        const auto& obs_value = vol_file.get_observation_value(obs_ids.at(0));\n        CHECK(obs_value == 0.);\n\n        for (size_t i = 0; i < interpolated_coords.size(); ++i) {\n          const auto& written_component = vol_file.get_tensor_component(\n              obs_ids.at(0),\n              \"InertialCoordinates\" + interpolated_coords.component_suffix(i));\n          const auto& written_dv = std::get<DataVector>(written_component.data);\n          for (size_t s = 0; s < written_dv.size(); ++s) {\n            CHECK_THAT(written_dv[s], Catch::Matchers::IsNaN());\n          }\n        }\n\n        const auto& written_test_solution_component =\n            vol_file.get_tensor_component(obs_ids.at(0), \"TestSolution\");\n        const auto& written_test_solution_dv =\n            std::get<DataVector>(written_test_solution_component.data);\n        for (double written_test_solution : written_test_solution_dv) {\n          CHECK_THAT(written_test_solution, Catch::Matchers::IsNaN());\n        }\n\n        const auto& written_square_component =\n            vol_file.get_tensor_component(obs_ids.at(0), \"Square\");\n        const auto& written_square_dv =\n            std::get<DataVector>(written_square_component.data);\n        for (double written_square : written_square_dv) {\n          CHECK_THAT(written_square, Catch::Matchers::IsNaN());\n        }\n      };\n\n  const auto& interpolated_coords_a =\n      db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box_a);\n  const auto& interpolated_coords_b =\n      db::get<domain::Tags::Coordinates<Dim, Frame::Inertial>>(box_b);\n\n  if (expect_nans) {\n    check_file_contents_are_nans(\"/LineA\", interpolated_coords_a);\n    check_file_contents_are_nans(\"/LineB\", interpolated_coords_b);\n  } else {\n    check_file_contents(\"/LineA\", interpolated_coords_a);\n    check_file_contents(\"/LineB\", interpolated_coords_b);\n  }\n\n  if (file_system::check_if_file_exists(h5_file_name)) {\n    file_system::rm(h5_file_name, true);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.NumericalAlgorithms.Interpolator.ObserveLineSegment\",\n                  \"[Unit]\") {\n  intrp::OptionHolders::LineSegment<1> line_segment_opts_A_1d({{0.0}}, {{1.0}},\n                                                              10);\n  intrp::OptionHolders::LineSegment<1> line_segment_opts_B_1d({{2.2}}, {{3.1}},\n                                                              10);\n  intrp::OptionHolders::LineSegment<2> line_segment_opts_A_2d({{0.0, 1.0}},\n                                                              {{0.0, 2.0}}, 10);\n  intrp::OptionHolders::LineSegment<2> line_segment_opts_B_2d({{1.0, 2.0}},\n                                                              {{2.0, 3.1}}, 10);\n  intrp::OptionHolders::LineSegment<3> line_segment_opts_A_3d(\n      {{0.0, 0.0, 1.0}}, {{0.0, 0.0, 2.0}}, 10);\n  intrp::OptionHolders::LineSegment<3> line_segment_opts_B_3d(\n      {{1.3, 1.0, 2.0}}, {{1.7, 2.0, 3.1}}, 10);\n\n  gr::Solutions::Minkowski<1> minkowski_1d{};\n  gr::Solutions::Minkowski<2> minkowski_2d{};\n  gr::Solutions::Minkowski<3> minkowski_3d{};\n  gr::Solutions::KerrSchild kerr_schild{1., {0.3, 0.4, 0.1}, {0., 0., 0.}};\n\n  run_test(line_segment_opts_A_1d, line_segment_opts_B_1d, minkowski_1d);\n  run_test(line_segment_opts_A_2d, line_segment_opts_B_2d, minkowski_2d);\n  run_test(line_segment_opts_A_3d, line_segment_opts_B_3d, minkowski_3d);\n  run_test(line_segment_opts_A_3d, line_segment_opts_B_3d, kerr_schild);\n\n  intrp::OptionHolders::LineSegment<1> line_segment_opts_N_1d({{4.2}}, {{6.1}},\n                                                              10);\n  run_test(line_segment_opts_A_1d, line_segment_opts_N_1d, minkowski_1d, true);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_ObserveTimeSeriesAndSurfaceData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/IO/VolumeData.hpp\"\n#include \"Helpers/ParallelAlgorithms/Interpolation/InterpolationTargetTestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/Initialize.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"IO/Observer/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/AngularOrdering.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/IO/FillYlmLegendAndData.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/SpherepackIterator.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"Parallel/ParallelComponentHelpers.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Actions/InitializeInterpolationTarget.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveSurfaceData.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Callbacks/ObserveTimeSeriesOnSurface.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/KerrHorizon.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace gr::surfaces::Tags {\ntemplate <typename Frame>\nstruct AreaElement;\ntemplate <typename IntegrandTag, typename Frame>\nstruct SurfaceIntegral;\n}  // namespace gr::surfaces::Tags\n\nnamespace {\nvoid check_ylm_data(const std::string& h5_file_name) {\n  // Parameters chosen to match SurfaceE choices below\n  constexpr size_t l_max = 3;\n  constexpr size_t max_resolution_and_output_l = 60;\n  constexpr std::array<double, 3> expansion_center{{0.04, 0.05, 0.06}};\n  constexpr double mass = 1.1;\n  constexpr std::array<double, 3> dimensionless_spin{{1.0, 0.0, 0.0}};\n\n  ylm::Strahlkorper<Frame::Inertial> expected_surface(\n      l_max, l_max,\n      get(gr::Solutions::kerr_horizon_radius(\n          ::ylm::Spherepack(l_max, l_max).theta_phi_points(), mass,\n          dimensionless_spin)),\n      expansion_center);\n\n  std::vector<std::string> ylm_expected_legend{};\n  std::vector<double> ylm_expected_data{};\n  ylm::fill_ylm_legend_and_data(make_not_null(&ylm_expected_legend),\n                                make_not_null(&ylm_expected_data),\n                                expected_surface, 0.0,\n                                max_resolution_and_output_l);\n  const size_t expected_num_columns = ylm_expected_legend.size();\n\n  // Check that the H5 file was written correctly.\n  const auto file = h5::H5File<h5::AccessType::ReadOnly>(h5_file_name);\n  const auto& ylm_dat_file = file.get<h5::Dat>(\"/SurfaceE_Ylm\");\n  const Matrix ylm_written_data = ylm_dat_file.get_data();\n  const auto& ylm_written_legend = ylm_dat_file.get_legend();\n\n  CHECK(ylm_written_legend.size() == expected_num_columns);\n  CHECK(ylm_written_data.columns() == expected_num_columns);\n  CHECK(ylm_written_legend == ylm_expected_legend);\n\n  ASSERT(ylm_expected_data.size() == expected_num_columns,\n         \"The size of the constructed test Ylm legend (\"\n             << expected_num_columns\n             << \") and the number of columns in the constructed test Ylm data (\"\n             << ylm_expected_data.size() << \") do not match.\");\n\n  for (size_t i = 0; i < expected_num_columns; i++) {\n    CHECK(ylm_written_data(0, i) == ylm_expected_data[i]);\n  }\n}\n\n// This tests the helper function for intrp::callbacks::ObserveSurfaceData that\n// fills in the Ylm legend and data to write to disk but with a max_l value that\n// is greater than the l_max of the surface to write. We test this separately\n// since currently, ObserveSurfaceData will only ever pass in the surface's\n// l_max as the max_l value since l_max for a surface does not change over the\n// course of a simulation.\nvoid check_ylm_data_with_greater_max_l() {\n  const double time = 2.2;\n  constexpr std::array<double, 3> expansion_center{{-1.0, -2.0, -3.0}};\n  constexpr size_t l_max = 3;\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  const auto radius = make_with_random_values<DataVector>(\n      make_not_null(&generator), distribution,\n      DataVector(ylm::Spherepack::physical_size(l_max, l_max),\n                 std::numeric_limits<double>::signaling_NaN()));\n  const ylm::Strahlkorper<Frame::Distorted> strahlkorper(l_max, l_max, radius,\n                                                         expansion_center);\n\n  const size_t max_l = 4;  // max_l > l_max\n  const std::vector<std::string> ylm_expected_legend{\n      \"Time\",\n      \"DistortedExpansionCenter_x\",\n      \"DistortedExpansionCenter_y\",\n      \"DistortedExpansionCenter_z\",\n      \"Lmax\",\n      \"coef(0,0)\",\n      \"coef(1,-1)\",\n      \"coef(1,0)\",\n      \"coef(1,1)\",\n      \"coef(2,-2)\",\n      \"coef(2,-1)\",\n      \"coef(2,0)\",\n      \"coef(2,1)\",\n      \"coef(2,2)\",\n      \"coef(3,-3)\",\n      \"coef(3,-2)\",\n      \"coef(3,-1)\",\n      \"coef(3,0)\",\n      \"coef(3,1)\",\n      \"coef(3,2)\",\n      \"coef(3,3)\",\n      \"coef(4,-4)\",\n      \"coef(4,-3)\",\n      \"coef(4,-2)\",\n      \"coef(4,-1)\",\n      \"coef(4,0)\",\n      \"coef(4,1)\",\n      \"coef(4,2)\",\n      \"coef(4,3)\",\n      \"coef(4,4)\"};\n  const size_t expected_num_columns = ylm_expected_legend.size();\n\n  std::vector<double> ylm_expected_data{time, expansion_center[0],\n                                        expansion_center[1],\n                                        expansion_center[2], l_max};\n\n  ylm::SpherepackIterator iter(l_max, l_max);\n  for (size_t l = 0; l <= l_max; l++) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n      iter.set(l, m);\n      ylm_expected_data.push_back(strahlkorper.coefficients()[iter()]);\n    }\n  }\n  for (size_t l = l_max + 1; l <= max_l; l++) {\n    for (int m = -static_cast<int>(l); m <= static_cast<int>(l); m++) {\n      ylm_expected_data.push_back(0.0);\n    }\n  }\n\n  ASSERT(ylm_expected_data.size() == expected_num_columns,\n         \"The size of the constructed test Ylm legend (\"\n             << expected_num_columns\n             << \") and the number of columns in the constructed test Ylm data (\"\n             << ylm_expected_data.size() << \") do not match.\");\n\n  std::vector<std::string> ylm_written_legend;\n  std::vector<double> ylm_written_data;\n\n  ylm::fill_ylm_legend_and_data(make_not_null(&ylm_written_legend),\n                                make_not_null(&ylm_written_data), strahlkorper,\n                                time, max_l);\n\n  CHECK(ylm_written_legend.size() == expected_num_columns);\n  CHECK(ylm_written_data.size() == expected_num_columns);\n  CHECK(ylm_written_legend == ylm_expected_legend);\n\n  for (size_t i = 0; i < expected_num_columns; i++) {\n    CHECK(ylm_written_data[i] == ylm_expected_data[i]);\n  }\n}\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\nvoid check_surface_volume_data(const std::string& surfaces_file_prefix) {\n  CAPTURE(ValidPoints);\n  // Parameters chosen to match SurfaceD choices below\n  constexpr size_t l_max = 10;\n  constexpr size_t m_max = 10;\n  constexpr double sphere_radius = 2.8;\n  constexpr std::array<double, 3> center{{0.01, 0.02, 0.03}};\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper{l_max, m_max,\n                                                        sphere_radius, center};\n  const ylm::Spherepack& ylm = strahlkorper.ylm_spherepack();\n  const std::vector<size_t> extents{\n      {ylm.physical_extents()[0], ylm.physical_extents()[1]}};\n  const std::array<DataVector, 2> theta_phi = ylm.theta_phi_points();\n  const DataVector& theta = theta_phi[0];\n  const DataVector& phi = theta_phi[1];\n  const DataVector sin_theta = sin(theta);\n  const DataVector radius = ylm.spec_to_phys(strahlkorper.coefficients());\n  const std::string grid_name{\"SurfaceD\"};\n  CAPTURE(grid_name);\n\n  const auto x{radius * sin_theta * cos(phi) + center[0]};\n  const auto y{radius * sin_theta * sin(phi) + center[1]};\n  const auto z{radius * cos(theta) + center[2]};\n  const std::vector<DataVector> tensor_and_coord_data{\n      x, y, z, square(2.0 * x + 3.0 * y + 5.0 * z)};\n  const std::vector<TensorComponent> tensor_components{\n      {grid_name + \"/InertialCoordinates_x\", tensor_and_coord_data[0]},\n      {grid_name + \"/InertialCoordinates_y\", tensor_and_coord_data[1]},\n      {grid_name + \"/InertialCoordinates_z\", tensor_and_coord_data[2]},\n      {grid_name + \"/Square\", tensor_and_coord_data[3]}};\n\n  const std::vector<Spectral::Basis> bases{2,\n                                           Spectral::Basis::SphericalHarmonic};\n  const std::vector<Spectral::Quadrature> quadratures{\n      {Spectral::Quadrature::Gauss, Spectral::Quadrature::Equiangular}};\n  const observers::ObservationId observation_id{0., \"/SurfaceD.vol\"};\n  if constexpr (ValidPoints == InterpTargetTestHelpers::ValidPoints::None) {\n    TestHelpers::io::VolumeData::check_volume_data(\n        surfaces_file_prefix + \".h5\"s, 0, grid_name, observation_id.hash(),\n        observation_id.value(), std::nullopt, tensor_and_coord_data,\n        {grid_name}, {bases}, {quadratures}, {extents},\n        {\"InertialCoordinates_x\"s, \"InertialCoordinates_y\"s,\n         \"InertialCoordinates_z\"s, \"Square\"s},\n        {{0, 1, 2, 3}}, 1.e-14, 1.0, {\"Square\"s});\n  } else {\n    TestHelpers::io::VolumeData::check_volume_data(\n        surfaces_file_prefix + \".h5\"s, 0, grid_name, observation_id.hash(),\n        observation_id.value(), std::nullopt, tensor_and_coord_data,\n        {grid_name}, {bases}, {quadratures}, {extents},\n        {\"InertialCoordinates_x\"s, \"InertialCoordinates_y\"s,\n         \"InertialCoordinates_z\"s, \"Square\"s},\n        {{0, 1, 2, 3}}, 1.e-2);  // loose tolerance because of low resolution\n                                 // in the volume, which limits interpolation\n                                 // accuracy\n  }\n}\n\n// Simple DataBoxItems for test.\nnamespace Tags {\nstruct TestSolution : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct Square : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct SquareCompute : Square, db::ComputeTag {\n  static void function(gsl::not_null<Scalar<DataVector>*> result,\n                       const Scalar<DataVector>& x) {\n    get(*result) = square(get(x));\n  }\n  using argument_tags = tmpl::list<TestSolution>;\n  using base = Square;\n  using return_type = Scalar<DataVector>;\n};\nstruct Negate : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\nstruct NegateCompute : Negate, db::ComputeTag {\n  static void function(gsl::not_null<Scalar<DataVector>*> result,\n                       const Scalar<DataVector>& x) {\n    get(*result) = -get(x);\n  }\n  using argument_tags = tmpl::list<Square>;\n  using base = Negate;\n  using return_type = Scalar<DataVector>;\n};\n}  // namespace Tags\n\ntemplate <typename Metavariables>\nstruct MockObserverWriter {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockNodeGroupChare;\n  using array_index = size_t;\n  using const_global_cache_tags = tmpl::list<observers::Tags::ReductionFileName,\n                                             observers::Tags::SurfaceFileName>;\n  using simple_tags =\n      typename observers::Actions::InitializeWriter<Metavariables>::simple_tags;\n  using compute_tags = typename observers::Actions::InitializeWriter<\n      Metavariables>::compute_tags;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<observers::Actions::InitializeWriter<Metavariables>>>,\n      Parallel::PhaseActions<Parallel::Phase::Register, tmpl::list<>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n\n  using component_being_mocked = observers::ObserverWriter<Metavariables>;\n};\n\ntemplate <typename Metavariables, typename InterpolationTargetTag>\nstruct MockInterpolationTarget {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = size_t;\n  using const_global_cache_tags =\n      Parallel::get_const_global_cache_tags_from_actions<\n          tmpl::flatten<tmpl::list<\n              typename InterpolationTargetTag::compute_target_points,\n              typename InterpolationTargetTag::post_interpolation_callbacks>>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<intrp::Actions::InitializeInterpolationTarget<\n              Metavariables, InterpolationTargetTag>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, tmpl::list<>>>;\n  using component_being_mocked =\n      intrp::InterpolationTarget<Metavariables, InterpolationTargetTag>;\n};\n\nstruct MockMetavariables {\n  using const_global_cache_tags = tmpl::list<ah::Tags::LMax>;\n  struct SurfaceA : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target =\n        tmpl::list<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, 3>>;\n    using compute_items_on_target =\n        tmpl::list<Tags::SquareCompute,\n                   gr::surfaces::Tags::AreaElementCompute<Frame::Inertial>,\n                   gr::surfaces::Tags::SurfaceIntegralCompute<\n                       Tags::Square, ::Frame::Inertial>>;\n    using compute_target_points =\n        intrp::TargetPoints::KerrHorizon<SurfaceA, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<gr::surfaces::Tags::SurfaceIntegral<Tags::Square,\n                                                           ::Frame::Inertial>>,\n            SurfaceA>>;\n  };\n  struct SurfaceB : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using vars_to_interpolate_to_target =\n        tmpl::list<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, 3>>;\n    using compute_items_on_target =\n        tmpl::list<Tags::SquareCompute, Tags::NegateCompute,\n                   gr::surfaces::Tags::AreaElementCompute<Frame::Inertial>,\n                   gr::surfaces::Tags::SurfaceIntegralCompute<Tags::Square,\n                                                              Frame::Inertial>,\n                   gr::surfaces::Tags::SurfaceIntegralCompute<Tags::Negate,\n                                                              Frame::Inertial>>;\n    using compute_target_points =\n        intrp::TargetPoints::KerrHorizon<SurfaceB, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<gr::surfaces::Tags::SurfaceIntegral<Tags::Square,\n                                                           ::Frame::Inertial>,\n                       gr::surfaces::Tags::SurfaceIntegral<Tags::Negate,\n                                                           ::Frame::Inertial>>,\n            SurfaceB>>;\n  };\n  struct SurfaceC : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::TimeStepId;\n    using vars_to_interpolate_to_target =\n        tmpl::list<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, 3>>;\n    using compute_items_on_target =\n        tmpl::list<Tags::SquareCompute, Tags::NegateCompute,\n                   gr::surfaces::Tags::AreaElementCompute<Frame::Inertial>,\n                   gr::surfaces::Tags::SurfaceIntegralCompute<\n                       Tags::Negate, ::Frame::Inertial>>;\n    using compute_target_points =\n        intrp::TargetPoints::KerrHorizon<SurfaceC, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveTimeSeriesOnSurface<\n            tmpl::list<gr::surfaces::Tags::SurfaceIntegral<Tags::Negate,\n                                                           ::Frame::Inertial>>,\n            SurfaceC>>;\n  };\n\n  struct SurfaceD : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target =\n        tmpl::list<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, 3>>;\n    using compute_items_on_target =\n        tmpl::list<Tags::SquareCompute,\n                   gr::surfaces::Tags::AreaElementCompute<Frame::Inertial>,\n                   gr::surfaces::Tags::SurfaceIntegralCompute<\n                       Tags::Square, ::Frame::Inertial>>;\n    using compute_target_points =\n        intrp::TargetPoints::KerrHorizon<SurfaceD, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveSurfaceData<\n            tmpl::list<Tags::Square>, SurfaceD, ::Frame::Inertial>>;\n  };\n\n  struct SurfaceE : tt::ConformsTo<intrp::protocols::InterpolationTargetTag> {\n    using temporal_id = ::Tags::Time;\n    using vars_to_interpolate_to_target =\n        tmpl::list<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, 3>>;\n    using compute_items_on_target =\n        tmpl::list<Tags::SquareCompute,\n                   gr::surfaces::Tags::AreaElementCompute<Frame::Inertial>,\n                   gr::surfaces::Tags::SurfaceIntegralCompute<\n                       Tags::Square, ::Frame::Inertial>>;\n    using compute_target_points =\n        intrp::TargetPoints::KerrHorizon<SurfaceE, ::Frame::Inertial>;\n    using post_interpolation_callbacks =\n        tmpl::list<intrp::callbacks::ObserveSurfaceData<\n            tmpl::list<Tags::Square>, SurfaceE, ::Frame::Inertial>>;\n  };\n\n  using observed_reduction_data_tags = tmpl::list<>;\n\n  using interpolator_source_vars =\n      tmpl::list<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, 3>>;\n  using interpolation_target_tags =\n      tmpl::list<SurfaceA, SurfaceB, SurfaceC, SurfaceD, SurfaceE>;\n  static constexpr size_t volume_dim = 3;\n  using component_list =\n      tmpl::list<MockObserverWriter<MockMetavariables>,\n                 MockInterpolationTarget<MockMetavariables, SurfaceA>,\n                 MockInterpolationTarget<MockMetavariables, SurfaceB>,\n                 MockInterpolationTarget<MockMetavariables, SurfaceC>,\n                 MockInterpolationTarget<MockMetavariables, SurfaceD>,\n                 MockInterpolationTarget<MockMetavariables, SurfaceE>>;\n};\n\nstruct AdaptiveSurfaceTarget {};\n\nstruct AdaptiveSurfaceMetavariables {\n  using observed_reduction_data_tags = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<ah::Tags::LMax>;\n  using component_list =\n      tmpl::list<MockObserverWriter<AdaptiveSurfaceMetavariables>>;\n};\n\ntemplate <InterpTargetTestHelpers::ValidPoints ValidPoints>\nvoid run_test() {\n  const auto remove_file_if_exists = [](const std::string& file_name) {\n    if (file_system::check_if_file_exists(file_name)) {\n      file_system::rm(file_name, true);\n    }\n  };\n\n  // Check if either file generated by this test exists and remove them\n  // if so. Check for both files existing before the test runs, since\n  // both files get written when evaluating the list of post interpolation\n  // callbacks below.\n  const std::string h5_file_prefix = \"Test_ObserveTimeSeriesOnSurface\";\n  const std::string h5_file_name = h5_file_prefix + \".h5\";\n  remove_file_if_exists(h5_file_name);\n  const std::string surfaces_file_prefix = \"Surfaces\";\n  const std::string surfaces_file_name = surfaces_file_prefix + \".h5\";\n  remove_file_if_exists(surfaces_file_name);\n\n  // Test That ObserveTimeSeriesOnSurface indeed does conform to its protocol\n  using callback_A = tmpl::front<\n      typename MockMetavariables::SurfaceA::post_interpolation_callbacks>;\n  using callback_B = tmpl::front<\n      typename MockMetavariables::SurfaceB::post_interpolation_callbacks>;\n  using callback_C = tmpl::front<\n      typename MockMetavariables::SurfaceC::post_interpolation_callbacks>;\n  using callback_D = tmpl::front<\n      typename MockMetavariables::SurfaceD::post_interpolation_callbacks>;\n  using callback_E = tmpl::front<\n      typename MockMetavariables::SurfaceE::post_interpolation_callbacks>;\n  using protocol = intrp::protocols::PostInterpolationCallback;\n  static_assert(tt::assert_conforms_to_v<callback_A, protocol>);\n  static_assert(tt::assert_conforms_to_v<callback_B, protocol>);\n  static_assert(tt::assert_conforms_to_v<callback_C, protocol>);\n  static_assert(tt::assert_conforms_to_v<callback_D, protocol>);\n  static_assert(tt::assert_conforms_to_v<callback_E, protocol>);\n\n  using metavars = MockMetavariables;\n  (void)metavars::volume_dim;\n  using target_a_component =\n      MockInterpolationTarget<metavars, metavars::SurfaceA>;\n  using target_b_component =\n      MockInterpolationTarget<metavars, metavars::SurfaceB>;\n  using target_c_component =\n      MockInterpolationTarget<metavars, metavars::SurfaceC>;\n  using target_d_component =\n      MockInterpolationTarget<metavars, metavars::SurfaceD>;\n  using target_e_component =\n      MockInterpolationTarget<metavars, metavars::SurfaceE>;\n  using obs_writer = MockObserverWriter<metavars>;\n\n  // Options for all InterpolationTargets.\n  intrp::OptionHolders::KerrHorizon kerr_horizon_opts_A(\n      10, {{0.0, 0.0, 0.0}}, 1.0, {{0.0, 0.0, 0.0}},\n      ylm::AngularOrdering::Strahlkorper);\n  intrp::OptionHolders::KerrHorizon kerr_horizon_opts_B(\n      10, {{0.0, 0.0, 0.0}}, 2.0, {{0.0, 0.0, 0.0}},\n      ylm::AngularOrdering::Strahlkorper);\n  intrp::OptionHolders::KerrHorizon kerr_horizon_opts_C(\n      10, {{0.0, 0.0, 0.0}}, 1.5, {{0.0, 0.0, 0.0}},\n      ylm::AngularOrdering::Strahlkorper);\n  intrp::OptionHolders::KerrHorizon kerr_horizon_opts_D(\n      10, {{0.01, 0.02, 0.03}}, 1.4, {{0.0, 0.0, 0.0}},\n      ylm::AngularOrdering::Strahlkorper);\n  // Surface for testing Ylm coefficients are written correctly. Using a\n  // non-zero spin because with zero spin, Y_{00} is the only term with a\n  // non-zero coefficient\n  intrp::OptionHolders::KerrHorizon kerr_horizon_opts_E(\n      3, {{0.04, 0.05, 0.06}}, 1.1, {{1.0, 0.0, 0.0}},\n      ylm::AngularOrdering::Strahlkorper);\n  tuples::TaggedTuple<\n      observers::Tags::ReductionFileName, observers::Tags::SurfaceFileName,\n      ::intrp::Tags::KerrHorizon<metavars::SurfaceA>,\n      ::intrp::Tags::KerrHorizon<metavars::SurfaceB>,\n      ::intrp::Tags::KerrHorizon<metavars::SurfaceC>,\n      ::intrp::Tags::KerrHorizon<metavars::SurfaceD>,\n      ::intrp::Tags::KerrHorizon<metavars::SurfaceE>, ah::Tags::LMax,\n      ::intrp::Tags::Verbosity>\n      global_cache_tuple{h5_file_prefix,      surfaces_file_prefix,\n                         kerr_horizon_opts_A, kerr_horizon_opts_B,\n                         kerr_horizon_opts_C, kerr_horizon_opts_D,\n                         kerr_horizon_opts_E, 60_st,\n                         ::Verbosity::Silent};\n\n  // Three mock nodes, with 2, 1, and 4 mock cores.\n  ActionTesting::MockRuntimeSystem<metavars> runner{\n      std::move(global_cache_tuple), {}, {2, 1, 4}};\n\n  ActionTesting::set_phase(make_not_null(&runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_singleton_component<target_a_component>(\n      &runner, ActionTesting::NodeId{0}, ActionTesting::LocalCoreId{1});\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<target_a_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_singleton_component<target_b_component>(\n      &runner, ActionTesting::NodeId{2}, ActionTesting::LocalCoreId{2});\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<target_b_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_singleton_component<target_c_component>(\n      &runner, ActionTesting::NodeId{2}, ActionTesting::LocalCoreId{1});\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<target_c_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_singleton_component<target_d_component>(\n      &runner, ActionTesting::NodeId{2}, ActionTesting::LocalCoreId{3});\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<target_d_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_singleton_component<target_e_component>(\n      &runner, ActionTesting::NodeId{2}, ActionTesting::LocalCoreId{0});\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<target_e_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_nodegroup_component<obs_writer>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    for (size_t node = 0; node < 2; ++node) {\n      ActionTesting::next_action<obs_writer>(make_not_null(&runner), node);\n    }\n  }\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Register);\n\n  auto& cache = ActionTesting::cache<obs_writer>(runner, 0_st);\n\n  Slab slab(0.0, 1.0);\n  TimeStepId temporal_id(true, 0, Time(slab, 0));\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  const auto setup_box = [&]<typename TargetComponent, typename TargetTag>(\n      TargetComponent /*target_component_v*/, TargetTag /*target_tag_v*/\n      ) -> const auto& {\n    auto& box =\n        ActionTesting::get_databox<TargetComponent>(make_not_null(&runner), 0);\n    TargetTag::compute_target_points::initialize(make_not_null(&box), cache);\n    db::mutate<Tags::TestSolution, gr::Tags::SpatialMetric<DataVector, 3>>(\n        [&](const gsl::not_null<Scalar<DataVector>*> test_solution,\n            const gsl::not_null<tnsr::ii<DataVector, 3>*> spatial_metric,\n            const tnsr::I<DataVector, 3>& inertial_coords) {\n          get(*test_solution) = 2.0 * get<0>(inertial_coords) +\n                                3.0 * get<1>(inertial_coords) +\n                                5.0 * get<2>(inertial_coords);\n\n          // Fill the metric with Minkowski for simplicity.  The\n          // InterpolationTarget is called \"KerrHorizon\" merely because the\n          // surface corresponds to where the horizon *would be* in a Kerr\n          // spacetime in Kerr-Schild coordinates; this in no way requires that\n          // there is an actual horizon or that the metric is Kerr.\n          const gr::Solutions::Minkowski<3> solution;\n          (*spatial_metric) =\n              get<gr::Tags::SpatialMetric<DataVector, 3>>(solution.variables(\n                  inertial_coords, 0.0,\n                  tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>>{}));\n\n          if (ValidPoints != InterpTargetTestHelpers::ValidPoints::All) {\n            const ScopedFpeState scoped_fpe{};\n            get(*test_solution) = std::numeric_limits<double>::quiet_NaN();\n            for (size_t i = 0; i < 3; i++) {\n              for (size_t j = 0; j < 3; j++) {\n                spatial_metric->get(i, j) =\n                    std::numeric_limits<double>::quiet_NaN();\n              }\n            }\n          }\n        },\n        make_not_null(&box),\n        db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box));\n\n    return box;\n  };\n\n  const auto& box_a =\n      setup_box(target_a_component{}, typename metavars::SurfaceA{});\n  const auto& box_b =\n      setup_box(target_b_component{}, typename metavars::SurfaceB{});\n  const auto& box_c =\n      setup_box(target_c_component{}, typename metavars::SurfaceC{});\n  const auto& box_d =\n      setup_box(target_d_component{}, typename metavars::SurfaceD{});\n  const auto& box_e =\n      setup_box(target_e_component{}, typename metavars::SurfaceE{});\n\n  tmpl::front<metavars::SurfaceA::post_interpolation_callbacks>::apply(\n      box_a, cache, temporal_id);\n  tmpl::front<metavars::SurfaceB::post_interpolation_callbacks>::apply(\n      box_b, cache, temporal_id);\n  tmpl::front<metavars::SurfaceC::post_interpolation_callbacks>::apply(\n      box_c, cache, temporal_id);\n  tmpl::front<metavars::SurfaceD::post_interpolation_callbacks>::apply(\n      box_d, cache, temporal_id);\n  tmpl::front<metavars::SurfaceE::post_interpolation_callbacks>::apply(\n      box_e, cache, temporal_id);\n\n  // There should be seven more threaded actions, so invoke them and check\n  // that there are no more.  They should all be on node zero.\n  REQUIRE(ActionTesting::number_of_queued_threaded_actions<obs_writer>(runner,\n                                                                       0) == 7);\n  for (size_t i = 0; i < 7; i++) {\n    ActionTesting::invoke_queued_threaded_action<obs_writer>(\n        make_not_null(&runner), 0);\n  }\n  CHECK(ActionTesting::is_threaded_action_queue_empty<obs_writer>(runner, 0));\n  CHECK(ActionTesting::is_threaded_action_queue_empty<obs_writer>(runner, 1));\n  CHECK(ActionTesting::is_threaded_action_queue_empty<obs_writer>(runner, 2));\n\n  // By hand compute integral(r^2 d(cos theta) dphi (2x+3y+5z)^2)\n  const std::vector<double> expected_integral_a{2432.0 * M_PI / 3.0};\n  // SurfaceB has a larger radius by a factor of 2 than SurfaceA,\n  // but the same function.  This results in a factor of 4 increase\n  // (because the integrand scales like r^2), and an additional factor of\n  // 4 (from the area element), for a net factor of 16.  There is also a\n  // minus sign for \"Negate\".\n  const std::vector<double> expected_integral_b{16.0 * 2432.0 * M_PI / 3.0,\n                                                -16.0 * 2432.0 * M_PI / 3.0};\n  // SurfaceC has a larger radius by a factor of 1.5 than SurfaceA,\n  // but the same function.  This results in a factor of 2.25 increase\n  // (because the integrand scales like r^2), and an additional factor of\n  // 2.25 (from the area element), for a net factor of 5.0625.  There is also a\n  // minus sign for \"Negate\".\n  const std::vector<double> expected_integral_c{-5.0625 * 2432.0 * M_PI / 3.0};\n  const std::vector<std::string> expected_legend_a{\"Time\",\n                                                   \"SurfaceIntegral(Square)\"};\n  const std::vector<std::string> expected_legend_b{\n      \"Time\", \"SurfaceIntegral(Square)\", \"SurfaceIntegral(Negate)\"};\n  const std::vector<std::string> expected_legend_c{\"Time\",\n                                                   \"SurfaceIntegral(Negate)\"};\n\n  // Check that the H5 file was written correctly.\n  const auto file = h5::H5File<h5::AccessType::ReadOnly>(h5_file_name);\n  auto check_file_contents =\n      [&file](const std::vector<double>& expected_integral,\n              const std::vector<std::string>& expected_legend,\n              const std::string& group_name) {\n        CAPTURE(group_name);\n        CAPTURE(expected_legend);\n        file.close_current_object();\n        const auto& dat_file = file.get<h5::Dat>(group_name);\n        const Matrix written_data = dat_file.get_data();\n        const auto& written_legend = dat_file.get_legend();\n        CHECK(written_legend == expected_legend);\n        CHECK(0.0 == written_data(0, 0));\n        // The interpolation is not perfect because I use too few grid points.\n        Approx custom_approx = Approx::custom().epsilon(1.e-4).scale(1.0);\n        for (size_t i = 0; i < expected_integral.size(); ++i) {\n          CAPTURE(i);\n          if constexpr (ValidPoints ==\n                        InterpTargetTestHelpers::ValidPoints::None) {\n            CHECK_THAT(written_data(0, i + 1), Catch::Matchers::IsNaN());\n          } else {\n            CHECK(expected_integral[i] ==\n                  custom_approx(written_data(0, i + 1)));\n          }\n        }\n      };\n  check_file_contents(expected_integral_a, expected_legend_a, \"/SurfaceA\");\n  check_file_contents(expected_integral_b, expected_legend_b, \"/SurfaceB\");\n  check_file_contents(expected_integral_c, expected_legend_c, \"/SurfaceC\");\n\n  // Check that the Ylm data were written correctly\n  // As this data depends only on the known target (a KerrHorizon) it\n  // uses no interpolated data\n  check_ylm_data(h5_file_name);\n\n  remove_file_if_exists(h5_file_name);\n\n  // Check that the Surfaces file contains the correct surface data\n  check_surface_volume_data<ValidPoints>(surfaces_file_prefix);\n\n  remove_file_if_exists(surfaces_file_name);\n\n  // This check also uses no interpolated data\n  check_ylm_data_with_greater_max_l();\n\n  // Verify that adaptive surface data writes all requested coefficients\n  const std::string adaptive_surface_reduction_file_prefix =\n      \"AdaptiveSurfaceReduction\";\n  const std::string adaptive_surface_file_prefix = \"AdaptiveSurfaceSurfaceData\";\n  const std::string adaptive_surface_reduction_file_name =\n      adaptive_surface_reduction_file_prefix + \".h5\";\n  const std::string adaptive_surface_file_name =\n      adaptive_surface_file_prefix + \".h5\";\n  remove_file_if_exists(adaptive_surface_reduction_file_name);\n  remove_file_if_exists(adaptive_surface_file_name);\n\n  constexpr size_t max_resolution_and_output_l = 6;\n  constexpr size_t low_l = 4;\n  constexpr size_t high_l = max_resolution_and_output_l;\n  const std::array<double, 3> adaptive_surface_center{{0.1, -0.2, 0.3}};\n\n  MAKE_GENERATOR(resolution_generator);\n  const std::uniform_real_distribution<> radius_distribution{0.8, 1.2};\n  const ylm::Spherepack spherepack_high{high_l, high_l};\n  const auto radius_high = make_with_random_values<DataVector>(\n      make_not_null(&resolution_generator), radius_distribution,\n      DataVector(spherepack_high.physical_size(), 0.0));\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper_high{\n      high_l, high_l, radius_high, adaptive_surface_center};\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper_low{low_l, low_l,\n                                                            strahlkorper_high};\n\n  using ObsWriter = MockObserverWriter<AdaptiveSurfaceMetavariables>;\n  tuples::TaggedTuple<observers::Tags::ReductionFileName,\n                      observers::Tags::SurfaceFileName, ah::Tags::LMax>\n      adaptive_cache_tuple{adaptive_surface_reduction_file_prefix,\n                           adaptive_surface_file_prefix,\n                           max_resolution_and_output_l};\n  ActionTesting::MockRuntimeSystem<AdaptiveSurfaceMetavariables>\n      adaptive_runner{std::move(adaptive_cache_tuple)};\n\n  ActionTesting::set_phase(make_not_null(&adaptive_runner),\n                           Parallel::Phase::Initialization);\n  ActionTesting::emplace_nodegroup_component<ObsWriter>(&adaptive_runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<ObsWriter>(make_not_null(&adaptive_runner), 0);\n  }\n  ActionTesting::set_phase(make_not_null(&adaptive_runner),\n                           Parallel::Phase::Testing);\n  auto& adaptive_cache = ActionTesting::cache<ObsWriter>(adaptive_runner, 0_st);\n\n  const auto make_box = [](const ylm::Strahlkorper<Frame::Inertial>&\n                               strahlkorper) {\n    const auto coords = ylm::cartesian_coords(strahlkorper);\n    return db::create<tmpl::list<ylm::Tags::Strahlkorper<Frame::Inertial>,\n                                 ylm::Tags::CartesianCoords<Frame::Inertial>>>(\n        strahlkorper, coords);\n  };\n\n  using Callback =\n      intrp::callbacks::ObserveSurfaceData<tmpl::list<>, AdaptiveSurfaceTarget,\n                                           ::Frame::Inertial>;\n\n  auto low_box = make_box(strahlkorper_low);\n  auto high_box = make_box(strahlkorper_high);\n  Callback::apply(low_box, adaptive_cache, 1.0);\n  Callback::apply(high_box, adaptive_cache, 2.0);\n\n  while (ActionTesting::number_of_queued_threaded_actions<ObsWriter>(\n             adaptive_runner, 0) > 0) {\n    ActionTesting::invoke_queued_threaded_action<ObsWriter>(\n        make_not_null(&adaptive_runner), 0);\n  }\n\n  const auto adaptive_surface_reduction_file =\n      h5::H5File<h5::AccessType::ReadOnly>(\n          adaptive_surface_reduction_file_name);\n  const std::string surface_name = pretty_type::name<AdaptiveSurfaceTarget>();\n  adaptive_surface_reduction_file.close_current_object();\n  const auto& ylm_dat = adaptive_surface_reduction_file.get<h5::Dat>(\n      std::string{\"/\"} + surface_name + \"_Ylm\");\n  const Matrix ylm_data = ylm_dat.get_data();\n  const auto& legend = ylm_dat.get_legend();\n  const size_t expected_columns = 5 + square(max_resolution_and_output_l + 1);\n\n  CHECK(ylm_data.rows() == 2);\n  CHECK(ylm_data.columns() == expected_columns);\n  CHECK(legend.size() == expected_columns);\n\n  const auto check_row =\n      [](const Matrix& data, const size_t row, const double expected_time,\n         const ylm::Strahlkorper<Frame::Inertial>& strahlkorper,\n         const size_t expected_l_max, const size_t max_l_to_write) {\n        CHECK(data(row, 0) == expected_time);\n        const auto& expansion_center = strahlkorper.expansion_center();\n        CHECK(data(row, 1) == expansion_center[0]);\n        CHECK(data(row, 2) == expansion_center[1]);\n        CHECK(data(row, 3) == expansion_center[2]);\n        CHECK(data(row, 4) == expected_l_max);\n        size_t column = 5;\n        for (size_t l = 0; l <= max_l_to_write; ++l) {\n          for (int m = -static_cast<int>(l); m <= static_cast<int>(l); ++m) {\n            double expected_value = 0.0;\n            if (l <= strahlkorper.l_max()) {\n              ylm::SpherepackIterator iterator(strahlkorper.l_max(),\n                                               strahlkorper.m_max());\n              iterator.set(l, m);\n              expected_value = strahlkorper.coefficients()[iterator()];\n            }\n            CHECK(data(row, column) == expected_value);\n            ++column;\n          }\n        }\n      };\n\n  check_row(ylm_data, 0, 1.0, strahlkorper_low, low_l,\n            max_resolution_and_output_l);\n  check_row(ylm_data, 1, 2.0, strahlkorper_high, high_l,\n            max_resolution_and_output_l);\n\n  remove_file_if_exists(adaptive_surface_reduction_file_name);\n  remove_file_if_exists(adaptive_surface_file_name);\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.NumericalAlgorithms.Interpolator.ObserveTimeSeriesAndSurfaceData\",\n    \"[Unit]\") {\n  run_test<InterpTargetTestHelpers::ValidPoints::All>();\n  run_test<InterpTargetTestHelpers::ValidPoints::None>();\n  // ValidPoints::Some is not tested as that would vastly increase the\n  // complexity of the test for limited gain.  In order to test\n  // properly would require passing in the list of valid points.  The\n  // only difference between the cases All and Some would be that\n  // invalid points print nan, which is only tested by None.\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_Protocols.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Helpers/ParallelAlgorithms/Interpolation/Examples.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeTargetPoints.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/ComputeVarsToInterpolate.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/InterpolationTargetTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n\nstatic_assert(\n    tt::assert_conforms_to_v<intrp::TestHelpers::ExampleComputeTargetPoints,\n                             intrp::protocols::ComputeTargetPoints>);\nstatic_assert(tt::assert_conforms_to_v<\n              intrp::TestHelpers::ExampleComputeVarsToInterpolate,\n              intrp::protocols::ComputeVarsToInterpolate>);\nstatic_assert(tt::assert_conforms_to_v<\n              intrp::TestHelpers::ExamplePostInterpolationCallback,\n              intrp::protocols::PostInterpolationCallback>);\nstatic_assert(\n    tt::assert_conforms_to_v<intrp::TestHelpers::ExampleInterpolationTargetTag,\n                             intrp::protocols::InterpolationTargetTag>);\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/Interpolation/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <type_traits>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"ParallelAlgorithms/Interpolation/PointInfoTag.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Tags.hpp\"\n#include \"ParallelAlgorithms/Interpolation/TagsMetafunctions.hpp\"\n#include \"ParallelAlgorithms/Interpolation/Targets/Sphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct SomeType {};\nstruct SomeTag {\n  using type = SomeType;\n};\nstruct Metavars {\n  using temporal_id = SomeTag;\n  static constexpr size_t volume_dim = 3;\n  using interpolation_target_tags = tmpl::list<>;\n};\nstruct InterpolationTargetTag {\n  using vars_to_interpolate_to_target = tmpl::list<>;\n  using compute_target_points =\n      intrp::TargetPoints::Sphere<InterpolationTargetTag, Frame::Inertial>;\n};\n\nvoid test_tags_metafunctions() {\n  static_assert(std::is_same_v<TensorMetafunctions::replace_frame_in_tag_t<\n                                   gh::Tags::Pi<DataVector, 3>, Frame::Grid>,\n                               gh::Tags::Pi<DataVector, 3, Frame::Grid>>,\n                \"Failed testing replace_frame_in_tag_t\");\n  static_assert(\n      not std::is_same_v<TensorMetafunctions::replace_frame_in_tag_t<\n                             gh::Tags::Pi<DataVector, 3>, Frame::Grid>,\n                         gh::Tags::Pi<DataVector, 3, Frame::Distorted>>,\n      \"Failed testing replace_frame_in_tag_t\");\n  static_assert(\n      std::is_same_v<TensorMetafunctions::replace_frame_in_tag_t<\n                         gr::Tags::SpacetimeMetric<DataVector, 3>, Frame::Grid>,\n                     gr::Tags::SpacetimeMetric<DataVector, 3, Frame::Grid>>,\n      \"Failed testing replace_frame_in_tag_t\");\n  static_assert(std::is_same_v<TensorMetafunctions::replace_frame_in_tag_t<\n                                   gr::Tags::Lapse<DataVector>, Frame::Grid>,\n                               gr::Tags::Lapse<DataVector>>,\n                \"Failed testing replace_frame_in_tag_t\");\n  static_assert(\n      std::is_same_v<TensorMetafunctions::replace_frame_in_tag_t<\n                         Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                     tmpl::size_t<3>, Frame::Inertial>,\n                         Frame::Grid>,\n                     Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                                 Frame::Grid>>,\n      \"Failed testing replace_frame_in_tag_t\");\n  static_assert(std::is_same_v<TensorMetafunctions::replace_frame_in_tag_t<\n                                   gh::Tags::ConstraintGamma0, Frame::Grid>,\n                               gh::Tags::ConstraintGamma0>,\n                \"Failed testing replace_frame_in_tag_t\");\n  static_assert(\n      std::is_same_v<TensorMetafunctions::replace_frame_in_tag_t<\n                         Tags::deriv<gh::Tags::Phi<DataVector, 3>,\n                                     tmpl::size_t<3>, Frame::Inertial>,\n                         Frame::Grid>,\n                     Tags::deriv<gh::Tags::Phi<DataVector, 3, Frame::Grid>,\n                                 tmpl::size_t<3>, Frame::Grid>>,\n      \"Failed testing replace_frame_in_tag_t\");\n  static_assert(\n      std::is_same_v<\n          TensorMetafunctions::replace_frame_in_taglist<\n              tmpl::list<Tags::deriv<gh::Tags::Phi<DataVector, 3>,\n                                     tmpl::size_t<3>, Frame::Inertial>,\n                         gh::Tags::Pi<DataVector, 3, Frame::Distorted>,\n                         Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                     tmpl::size_t<3>, Frame::Distorted>>,\n              Frame::Grid>,\n          tmpl::list<Tags::deriv<gh::Tags::Phi<DataVector, 3, Frame::Grid>,\n                                 tmpl::size_t<3>, Frame::Grid>,\n                     gh::Tags::Pi<DataVector, 3, Frame::Grid>,\n                     Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                                 Frame::Grid>>>,\n      \"Failed testing replace_frame_in_taglist\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Interpolation.Tags\", \"[Unit][NumericalAlgorithms]\") {\n  test_tags_metafunctions();\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::IndicesOfFilledInterpPoints<Metavars>>(\n      \"IndicesOfFilledInterpPoints\");\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::IndicesOfInvalidInterpPoints<Metavars>>(\n      \"IndicesOfInvalidInterpPoints\");\n  TestHelpers::db::test_simple_tag<\n      intrp::Tags::InterpolatedVars<InterpolationTargetTag, Metavars>>(\n      \"InterpolatedVars\");\n  TestHelpers::db::test_simple_tag<intrp::Tags::TemporalIds<Metavars>>(\n      \"TemporalIds\");\n  TestHelpers::db::test_simple_tag<intrp::Tags::CompletedTemporalIds<Metavars>>(\n      \"CompletedTemporalIds\");\n  TestHelpers::db::test_simple_tag<intrp::Tags::PointInfo<\n      InterpolationTargetTag, tmpl::size_t<Metavars::volume_dim>>>(\"PointInfo\");\n  TestHelpers::db::test_simple_tag<intrp::Tags::Verbosity>(\"Verbosity\");\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_LinearSolverActions\")\n\nset(LIBRARY_SOURCES\n  Test_MakeIdentityIfSkipped.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  DataStructures\n  Parallel\n  Utilities\n  )\n\nadd_standalone_test(\n  \"Integration.LinearSolver.BuildMatrix\"\n  INPUT_FILE \"Test_BuildMatrix.yaml\")\ntarget_link_libraries(\n  Test_BuildMatrix\n  PRIVATE\n  Charmxx::main\n  Convergence\n  DomainCreators\n  DomainStructure\n  H5\n  LinearSolverHelpers\n  Options\n  Parallel\n  ParallelLinearSolver\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Actions/Test_BuildMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Actions/BuildMatrix.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\nnamespace helpers_distributed = DistributedLinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct TestResult {\n  using const_global_cache_tags =\n      tmpl::list<helpers_distributed::ExpectedResult>;\n\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ElementId<1>& element_id, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    // Read the matrix from the file\n    h5::H5File<h5::AccessType::ReadOnly> h5file(\"Test_BuildMatrix_Volume0.h5\");\n    const auto matrix_data =\n        h5file.get<h5::VolumeData>(\"/Matrix\").get_data_by_element(\n            std::nullopt, std::nullopt, {{\"Variable_0\"}});\n    // Check the columns of the matrix corresponding to this element\n    const size_t element_index = helpers_distributed::get_index(element_id);\n    const auto& linear_operator =\n        gsl::at(get<helpers_distributed::LinearOperator>(box), element_index);\n    const size_t num_points = linear_operator.columns();\n    for (size_t col = 0; col < num_points; ++col) {\n      const auto& col_data =\n          get<2>(gsl::at(matrix_data, element_index * num_points + col));\n      size_t row = 0;\n      for (const auto& element_data : col_data) {\n        const auto& row_data =\n            get<DataVector>(element_data.tensor_components.front().data);\n        for (size_t i = 0; i < row_data.size(); ++i) {\n          SPECTRE_PARALLEL_REQUIRE(linear_operator(row, col) == row_data[i]);\n          ++row;\n        }\n      }\n      SPECTRE_PARALLEL_REQUIRE(row == linear_operator.rows());\n    }\n    // Also check the matrix stored in the DataBox\n    const auto& matrix_slice = db::get<LinearSolver::Tags::Matrix<double>>(box);\n    for (size_t row = 0; row < num_points; ++row) {\n      for (const auto& element_data : get<2>(matrix_data[0])) {\n        const size_t col_offset = helpers_distributed::get_index(\n                                      ElementId<1>(element_data.element_name)) *\n                                  num_points;\n        for (size_t col = 0; col < num_points; ++col) {\n          const auto& row_data = get<DataVector>(\n              get<2>(matrix_data[col_offset + col])[element_index]\n                  .tensor_components.front()\n                  .data);\n          SPECTRE_PARALLEL_REQUIRE(row_data[row] ==\n                                   matrix_slice(row, col_offset + col));\n        }\n      }\n    }\n    // Check solution\n    const auto& expected_result =\n        gsl::at(get<helpers_distributed::ExpectedResult>(box), element_index);\n    const auto& result = db::get<helpers_distributed::fields_tag>(box);\n    for (size_t i = 0; i < expected_result.size(); i++) {\n      SPECTRE_PARALLEL_REQUIRE(result.data()[i] == approx(expected_result[i]));\n    }\n    return {Parallel::AlgorithmExecution::Pause, std::nullopt};\n  }\n};\n\nstruct Metavariables {\n  static constexpr Options::String help{\n      \"Test building an explicit matrix representation of the linear operator\"};\n  static constexpr size_t volume_dim = 1;\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithoutBoundaryConditions<\n          volume_dim>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<1>, tmpl::list<domain::creators::Interval>>>;\n  };\n\n  using build_matrix = LinearSolver::Actions::BuildMatrix<\n      helpers_distributed::fields_tag,\n      db::add_tag_prefix<::Tags::FixedSource, helpers_distributed::fields_tag>,\n      helpers_distributed::fields_tag,\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo,\n                         helpers_distributed::fields_tag>,\n      domain::Tags::Coordinates<volume_dim, Frame::ElementLogical>>;\n\n  using build_matrix_actions = typename build_matrix::template actions<\n      helpers_distributed::ComputeOperatorAction<\n          helpers_distributed::fields_tag>>;\n\n  using element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<Parallel::PhaseActions<\n                     Parallel::Phase::Initialization,\n                     tmpl::list<helpers_distributed::InitializeElement,\n                                Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::Register,\n                     tmpl::push_back<build_matrix::register_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<\n                     Parallel::Phase::BuildMatrix,\n                     tmpl::push_back<build_matrix_actions,\n                                     Parallel::Actions::TerminatePhase>>,\n                 Parallel::PhaseActions<Parallel::Phase::Testing,\n                                        tmpl::list<TestResult>>>>;\n\n  using component_list = tmpl::flatten<tmpl::list<\n      element_array, typename build_matrix::component_list<Metavariables>,\n      observers::Observer<Metavariables>,\n      observers::ObserverWriter<Metavariables>,\n      helpers::OutputCleaner<Metavariables, false, true>>>;\n  using observed_reduction_data_tags = tmpl::list<>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n  static constexpr std::array<Parallel::Phase, 6> default_phase_order{\n      {Parallel::Phase::Initialization, Parallel::Phase::Register,\n       Parallel::Phase::BuildMatrix, Parallel::Phase::Testing,\n       Parallel::Phase::Cleanup, Parallel::Phase::Exit}};\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Actions/Test_BuildMatrix.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  The test problem being solved here is a DG-discretized 1D Poisson equation\n  -u''(x) = f(x) on the interval [0, pi] with source f(x)=sin(x) and homogeneous\n  Dirichlet boundary conditions such that the solution is u(x)=sin(x) as well.\n\n  Details:\n  - Domain decomposition: 2 elements with 3 LGL grid-points each\n  - \"Primal\" DG formulation (no auxiliary variable)\n  - Not multiplied by mass matrix so the operator is not symmetric\n  - Mass-lumping: inverse mass matrix is approximated by diagonal\n  - Internal penalty flux with sigma = 1.5 * (N_points - 1)^2 / h\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[20.26423672846756 ,  3.242277876554809, -2.836993141985458],\n      [ 0.810569469138702,  3.24227787655481 , -0.405284734569351],\n      [-2.836993141985458, -1.621138938277405, 12.969111506219237],\n      [ 1.215854203708053, -4.863416814832214, -7.295125222248322],\n      [ 0.               ,  0.               , -1.215854203708054],\n      [ 0.               ,  0.               ,  1.215854203708053]]\n  - [[ 1.215854203708053,  0.               ,  0.               ],\n      [-1.215854203708054,  0.               ,  0.               ],\n      [-7.295125222248322, -4.863416814832214,  1.215854203708053],\n      [12.969111506219237, -1.621138938277405, -2.836993141985458],\n      [-0.405284734569351,  3.24227787655481 ,  0.810569469138702],\n      [-2.836993141985458,  3.242277876554809, 20.26423672846756 ]]\n\nSource:\n  - [0., 0.7071067811865475, 1.]\n  - [1., 0.7071067811865476, 0.]\n\nExpectedResult:\n  - [-0.0363482510397858,  0.7235793356729757,  0.9928055333486293]\n  - [ 0.9928055333486292,  0.7235793356729758, -0.0363482510397858]\n\nBuildMatrix:\n  Verbosity: Debug\n  MatrixSubfileName: Matrix\n  EnableDirectSolve: True\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_BuildMatrix_Volume\"\n  ReductionFileName: \"Test_BuildMatrix_Reductions\"\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Actions/Test_MakeIdentityIfSkipped.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Actions/MakeIdentityIfSkipped.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct TestOptionsGroup {};\n\nstruct FieldsTag : db::SimpleTag {\n  using type = blaze::DynamicVector<double>;\n};\n\nstruct SourceTag : db::SimpleTag {\n  using type = blaze::DynamicVector<double>;\n};\n\nstruct CheckRunIfSkippedTag : db::SimpleTag {\n  using type = bool;\n};\n\nstruct TestLabel {};\n\nstruct TestLinearSolver {\n  using options_group = TestOptionsGroup;\n  using fields_tag = FieldsTag;\n  using source_tag = SourceTag;\n};\n\nstruct RunIfSkipped {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    db::mutate<CheckRunIfSkippedTag>(\n        [](const gsl::not_null<bool*> flag) { *flag = true; },\n        make_not_null(&box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename Metavariables, typename TestActions>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              tmpl::list<Convergence::Tags::HasConverged<TestOptionsGroup>,\n                         FieldsTag, SourceTag, CheckRunIfSkippedTag>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<TestActions, Parallel::Actions::TerminatePhase>>>;\n};\n\ntemplate <typename TestActions>\nstruct Metavariables {\n  using element_array = ElementArray<Metavariables, TestActions>;\n  using component_list = tmpl::list<element_array>;\n};\n\ntemplate <typename TestActions>\nvoid test_make_identity_if_skipped(const bool skipped,\n                                   const bool check_run_if_skipped = false) {\n  CAPTURE(skipped);\n  Convergence::HasConverged has_converged{skipped ? 0_st : 1_st, 0};\n  using metavariables = Metavariables<TestActions>;\n  using element_array = typename Metavariables<TestActions>::element_array;\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), 0,\n      {has_converged, blaze::DynamicVector<double>{3, 1.},\n       blaze::DynamicVector<double>{3, 2.}, false});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  while (not ActionTesting::get_terminate<element_array>(runner, 0)) {\n    ActionTesting::next_action<element_array>(make_not_null(&runner), 0);\n  }\n  const auto get_tag = [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner, 0);\n  };\n  CHECK(get_tag(SourceTag{}) == blaze::DynamicVector<double>{3, 2.});\n  CHECK(get_tag(FieldsTag{}) == (skipped\n                                     ? blaze::DynamicVector<double>{3, 2.}\n                                     : blaze::DynamicVector<double>{3, 1.}));\n  if (check_run_if_skipped) {\n    CHECK(get_tag(CheckRunIfSkippedTag{}) == skipped);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelLinearSolver.Actions.MakeIdentityIfSkipped\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  test_make_identity_if_skipped<\n      LinearSolver::Actions::MakeIdentityIfSkipped<TestLinearSolver>>(false);\n  test_make_identity_if_skipped<\n      LinearSolver::Actions::MakeIdentityIfSkipped<TestLinearSolver>>(true);\n  static_assert(\n      std::is_same_v<\n          LinearSolver::Actions::make_identity_if_skipped<TestLinearSolver>,\n          LinearSolver::Actions::MakeIdentityIfSkipped<TestLinearSolver>>);\n  test_make_identity_if_skipped<LinearSolver::Actions::make_identity_if_skipped<\n      TestLinearSolver, RunIfSkipped>>(false, true);\n  test_make_identity_if_skipped<LinearSolver::Actions::make_identity_if_skipped<\n      TestLinearSolver, RunIfSkipped>>(true, true);\n  test_make_identity_if_skipped<LinearSolver::Actions::make_identity_if_skipped<\n      TestLinearSolver, RunIfSkipped, TestLabel>>(false, true);\n  test_make_identity_if_skipped<LinearSolver::Actions::make_identity_if_skipped<\n      TestLinearSolver, RunIfSkipped, TestLabel>>(true, true);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/AsynchronousSolvers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_AsynchronousLinearSolvers\")\n\nset(LIBRARY_SOURCES\n  Test_ElementActions.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  DataStructures\n  H5\n  ParallelLinearSolver\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/AsynchronousSolvers/Test_ElementActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/IO/Observers/ObserverHelpers.hpp\"\n#include \"IO/H5/Dat.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/Observer/Helpers.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/SetData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/AsynchronousSolvers/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct TestSolver {};\n\nstruct VectorTag : db::SimpleTag {\n  using type = blaze::DynamicVector<double>;\n};\n\nusing fields_tag = VectorTag;\nusing source_tag = ::Tags::FixedSource<fields_tag>;\nusing operator_applied_to_fields_tag =\n    LinearSolver::Tags::OperatorAppliedTo<fields_tag>;\nusing residual_tag = LinearSolver::Tags::Residual<fields_tag>;\nusing residual_magnitude_square_tag =\n    LinearSolver::Tags::MagnitudeSquare<residual_tag>;\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n                         tmpl::list<fields_tag, source_tag>>,\n                     LinearSolver::async_solvers::InitializeElement<\n                         fields_tag, TestSolver, source_tag>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Register,\n          tmpl::list<LinearSolver::async_solvers::RegisterElement<\n              fields_tag, TestSolver, source_tag>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<LinearSolver::async_solvers::PrepareSolve<\n                         fields_tag, TestSolver, source_tag, TestSolver>,\n                     LinearSolver::async_solvers::CompleteStep<\n                         fields_tag, TestSolver, source_tag, TestSolver>>>>;\n};\n\nstruct Metavariables {\n  using component_list =\n      tmpl::list<TestObservers_detail::observer_component<Metavariables>,\n                 TestObservers_detail::observer_writer_component<Metavariables>,\n                 ElementArray<Metavariables>>;\n  using observed_reduction_data_tags = observers::make_reduction_data_tags<\n      tmpl::list<LinearSolver::async_solvers::reduction_data>>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelLinearSolver.Asynchronous.ElementActions\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  using element_array = ElementArray<Metavariables>;\n  using obs_component = TestObservers_detail::observer_component<Metavariables>;\n  using obs_writer =\n      TestObservers_detail::observer_writer_component<Metavariables>;\n\n  const std::string reduction_file_name{\n      \"Test_AsynchronousLinearSolvers_ElementActions_Reductions\"};\n  const std::string volume_file_name{\n      \"Test_AsynchronousLinearSolvers_ElementActions_Volume\"};\n  if (file_system::check_if_file_exists(reduction_file_name + \".h5\")) {\n    file_system::rm(reduction_file_name + \".h5\", true);\n  }\n  if (file_system::check_if_file_exists(volume_file_name + \".h5\")) {\n    file_system::rm(volume_file_name + \".h5\", true);\n  }\n\n  const size_t num_iterations = 1;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {reduction_file_name, volume_file_name, num_iterations,\n       Verbosity::Verbose}};\n\n  // Setup mock element array\n  const int element_id = 0;\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), element_id,\n      {blaze::DynamicVector<double>{}, blaze::DynamicVector<double>{}});\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n\n  // DataBox shortcuts\n  const auto get_tag = [&runner, &element_id](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner,\n                                                              element_id);\n  };\n  const auto tag_is_retrievable = [&runner, &element_id](auto tag_v) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::tag_is_retrievable<element_array, tag>(runner,\n                                                                 element_id);\n  };\n  const auto set_tag = [&runner, &element_id](auto tag_v, const auto& value) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    ActionTesting::simple_action<element_array,\n                                 ::Actions::SetData<tmpl::list<tag>>>(\n        make_not_null(&runner), element_id, value);\n  };\n\n  // Setup mock observers\n  ActionTesting::emplace_group_component<obs_component>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_component>(make_not_null(&runner), 0);\n  }\n  ActionTesting::emplace_nodegroup_component<obs_writer>(&runner);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<obs_writer>(make_not_null(&runner), 0);\n  }\n  // Register with observers\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Register);\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  ActionTesting::invoke_queued_simple_action<obs_component>(\n      make_not_null(&runner), 0);\n  ActionTesting::invoke_queued_simple_action<obs_writer>(make_not_null(&runner),\n                                                         0);\n  ActionTesting::invoke_queued_simple_action<obs_writer>(make_not_null(&runner),\n                                                         0);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  {\n    INFO(\"InitializeElement\");\n    CHECK(get_tag(Convergence::Tags::IterationId<TestSolver>{}) ==\n          std::numeric_limits<size_t>::max());\n    tmpl::for_each<tmpl::list<operator_applied_to_fields_tag, residual_tag,\n                              Convergence::Tags::HasConverged<TestSolver>>>(\n        [&tag_is_retrievable](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          CAPTURE(db::tag_name<tag>());\n          CHECK(tag_is_retrievable(tag{}));\n        });\n  }\n  {\n    INFO(\"PrepareSolve\");\n    set_tag(fields_tag{}, blaze::DynamicVector<double>{1., 2., 3.});\n    set_tag(source_tag{}, blaze::DynamicVector<double>{4., 5., 6.});\n    set_tag(operator_applied_to_fields_tag{},\n            blaze::DynamicVector<double>{7., 8., 9.});\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              element_id);\n    CHECK(get_tag(Convergence::Tags::IterationId<TestSolver>{}) == 0);\n    CHECK_FALSE(get_tag(Convergence::Tags::HasConverged<TestSolver>{}));\n    ActionTesting::invoke_queued_simple_action<obs_component>(\n        make_not_null(&runner), 0);\n    ActionTesting::invoke_queued_threaded_action<obs_writer>(\n        make_not_null(&runner), 0);\n  }\n  {\n    INFO(\"CompleteStep\");\n    set_tag(fields_tag{}, blaze::DynamicVector<double>{10., 11., 12.});\n    set_tag(operator_applied_to_fields_tag{},\n            blaze::DynamicVector<double>{13., 14., 15});\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              element_id);\n    CHECK(get_tag(Convergence::Tags::IterationId<TestSolver>{}) == 1);\n    CHECK(get_tag(Convergence::Tags::HasConverged<TestSolver>{}));\n    ActionTesting::invoke_queued_simple_action<obs_component>(\n        make_not_null(&runner), 0);\n    ActionTesting::invoke_queued_threaded_action<obs_writer>(\n        make_not_null(&runner), 0);\n    ActionTesting::invoke_queued_threaded_action<obs_writer>(\n        make_not_null(&runner), 0);\n    ActionTesting::invoke_queued_threaded_action<obs_writer>(\n        make_not_null(&runner), 0);\n  }\n\n  {\n    INFO(\"Reduction observations\");\n    REQUIRE(file_system::check_if_file_exists(reduction_file_name + \".h5\"));\n    {\n      const auto reductions_file =\n          h5::H5File<h5::AccessType::ReadOnly>(reduction_file_name + \".h5\");\n      const auto& reductions_subfile =\n          reductions_file.get<h5::Dat>(\"/TestSolverResiduals\");\n      const auto written_reductions = reductions_subfile.get_data();\n      const auto& written_legend = reductions_subfile.get_legend();\n      CHECK(written_legend ==\n            std::vector<std::string>{\"Iteration\", \"Residual\"});\n      const Matrix expected_reductions{{0., sqrt(27.)}, {1., sqrt(243.)}};\n      CHECK_ITERABLE_APPROX(written_reductions, expected_reductions);\n    }\n    file_system::rm(reduction_file_name + \".h5\", true);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelLinearSolver\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  DataStructures\n  Options\n  ParallelLinearSolver\n  )\n\nset(INTEGRATION_TEST_LINK_LIBRARIES\n  Charmxx::main\n  DataStructures\n  ErrorHandling\n  Informer\n  LinearSolverHelpers\n  Observer\n  Parallel\n  ParallelLinearSolver\n  )\n\nset(DISTRIBUTED_INTEGRATION_TEST_LINK_LIBRARIES\n  ${INTEGRATION_TEST_LINK_LIBRARIES}\n  Domain\n  DomainBoundaryConditionsHelpers\n  DomainCreators\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(AsynchronousSolvers)\nadd_subdirectory(ConjugateGradient)\nadd_subdirectory(Gmres)\nadd_subdirectory(Multigrid)\nadd_subdirectory(Richardson)\nadd_subdirectory(Schwarz)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/ConjugateGradient/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelConjugateGradient\")\n\nset(LIBRARY_SOURCES\n  Test_ElementActions.cpp\n  Test_ResidualMonitorActions.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  DataStructures\n  LinearSolverHelpers\n  Logging\n  Observer\n  ParallelLinearSolver\n)\n\nadd_standalone_test(\n  \"Integration.LinearSolver.ConjugateGradientAlgorithm\"\n  INPUT_FILE \"Test_ConjugateGradientAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_ConjugateGradientAlgorithm\"\n  PRIVATE\n  \"${INTEGRATION_TEST_LINK_LIBRARIES}\")\nadd_standalone_test(\n  \"Integration.LinearSolver.DistributedConjugateGradientAlgorithm\"\n  INPUT_FILE \"Test_DistributedConjugateGradientAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_DistributedConjugateGradientAlgorithm\"\n  PRIVATE\n  \"${DISTRIBUTED_INTEGRATION_TEST_LINK_LIBRARIES}\")\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/ConjugateGradient/Test_ConjugateGradientAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/ConjugateGradient.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct SerialCg {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the conjugate gradient linear solver algorithm\"};\n\n  using linear_solver = LinearSolver::cg::ConjugateGradient<\n      Metavariables, helpers::fields_tag<double>, SerialCg>;\n  using preconditioner = void;\n\n  using component_list = helpers::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/ConjugateGradient/Test_ConjugateGradientAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nLinearOperator: [[4, 1], [1, 3]]\nSource: [1, 2]\nInitialGuess: [2, 1]\nExpectedResult: [0.0909090909090909, 0.6363636363636364]\n\nObservers:\n  VolumeFileName: \"Test_ConjugateGradientAlgorithm_Volume\"\n  ReductionFileName: \"Test_ConjugateGradientAlgorithm_Reductions\"\n\nSerialCg:\n  ConvergenceCriteria:\n    MaxIterations: 2\n    AbsoluteResidual: 1e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n\nConvergenceReason: AbsoluteResidual\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/ConjugateGradient/Test_DistributedConjugateGradientAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/ConjugateGradient.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\nnamespace helpers_distributed = DistributedLinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct ParallelCg {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the conjugate gradient linear solver algorithm on multiple \"\n      \"elements\"};\n  static constexpr size_t volume_dim = 1;\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithoutBoundaryConditions<\n          volume_dim>;\n\n  using linear_solver = LinearSolver::cg::ConjugateGradient<\n      Metavariables, typename helpers_distributed::fields_tag, ParallelCg>;\n  using preconditioner = void;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<1>, tmpl::list<domain::creators::Interval>>>;\n  };\n\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n  using component_list = helpers_distributed::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &TestHelpers::domain::BoundaryConditions::register_derived_with_charm},\n      {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/ConjugateGradient/Test_DistributedConjugateGradientAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  The test problem being solved here is a DG-discretized 1D Poisson equation\n  -u''(x) = f(x) on the interval [0, pi] with source f(x)=sin(x) and homogeneous\n  Dirichlet boundary conditions such that the solution is u(x)=sin(x) as well.\n\n  Details:\n  - Domain decomposition: 2 elements with 3 LGL grid-points each\n  - \"Primal\" DG formulation (no auxiliary variable)\n  - Multiplied by mass matrix and no mass-lumping\n  - Internal penalty flux with sigma = 1.5 * (N_points - 1)^2 / h\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[ 5.305164769729845,  0.848826363156775, -0.742723067762178],\n      [ 0.848826363156775,  3.395305452627101, -0.424413181578388],\n      [-0.742723067762178, -0.424413181578388,  3.395305452627101],\n      [ 0.318309886183791, -1.273239544735163, -1.909859317102744],\n      [ 0.               ,  0.               , -1.273239544735163],\n      [ 0.               ,  0.               ,  0.318309886183791]]\n  - [[ 0.318309886183791,  0.               ,  0.               ],\n      [-1.273239544735163,  0.               ,  0.               ],\n      [-1.909859317102744, -1.273239544735163,  0.318309886183791],\n      [ 3.395305452627101, -0.424413181578388, -0.742723067762178],\n      [-0.424413181578388,  3.395305452627101,  0.848826363156775],\n      [-0.742723067762178,  0.848826363156775,  5.305164769729845]]\n\nSource:\n  - [0.                , 0.740480489693061, 0.2617993877991494]\n  - [0.2617993877991494, 0.740480489693061, 0.                ]\n\nExpectedResult:\n  - [-0.0363482510397858,  0.7235793356729757,  0.9928055333486293]\n  - [ 0.9928055333486292,  0.7235793356729758, -0.0363482510397858]\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_DistributedConjugateGradientAlgorithm_Volume\"\n  ReductionFileName: \"Test_DistributedConjugateGradientAlgorithm_Reductions\"\n\nParallelCg:\n  ConvergenceCriteria:\n    MaxIterations: 3\n    AbsoluteResidual: 1e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n\nConvergenceReason: AbsoluteResidual\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/ConjugateGradient/Test_ElementActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/SetData.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/InitializeElement.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/Tags/InboxTags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct DummyOptionsGroup {};\n\nstruct VectorTag : db::SimpleTag {\n  using type = blaze::DynamicVector<double>;\n};\n\nusing fields_tag = VectorTag;\nusing operator_applied_to_fields_tag =\n    LinearSolver::Tags::OperatorAppliedTo<fields_tag>;\nusing operand_tag = LinearSolver::Tags::Operand<fields_tag>;\nusing operator_applied_to_operand_tag =\n    LinearSolver::Tags::OperatorAppliedTo<operand_tag>;\nusing residual_tag = LinearSolver::Tags::Residual<fields_tag>;\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<VectorTag>>,\n                     LinearSolver::cg::detail::InitializeElement<\n                         fields_tag, DummyOptionsGroup>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<LinearSolver::cg::detail::InitializeHasConverged<\n                         fields_tag, DummyOptionsGroup, DummyOptionsGroup>,\n                     LinearSolver::cg::detail::UpdateOperand<\n                         fields_tag, DummyOptionsGroup, DummyOptionsGroup>,\n                     Parallel::Actions::TerminatePhase>>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<ElementArray<Metavariables>>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.ParallelAlgorithms.LinearSolver.ConjugateGradient.ElementActions\",\n    \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  using element_array = ElementArray<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{{}};\n\n  // Setup mock element array\n\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), 0, {blaze::DynamicVector<double>(3, 0.)});\n  ActionTesting::next_action<element_array>(make_not_null(&runner), 0);\n\n  // DataBox shortcuts\n  const auto get_tag = [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner, 0);\n  };\n  const auto tag_is_retrievable = [&runner](auto tag_v) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::tag_is_retrievable<element_array, tag>(runner, 0);\n  };\n  const auto set_tag = [&runner](auto tag_v, const auto& value) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    ActionTesting::simple_action<element_array,\n                                 ::Actions::SetData<tmpl::list<tag>>>(\n        make_not_null(&runner), 0, value);\n  };\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Can't test the other element actions because reductions are not yet\n  // supported. The full algorithm is tested in\n  // `Test_ConjugateGradientAlgorithm.cpp` and\n  // `Test_DistributedConjugateGradientAlgorithm.cpp`.\n\n  {\n    INFO(\"InitializeElement\");\n    CHECK(get_tag(Convergence::Tags::IterationId<DummyOptionsGroup>{}) ==\n          std::numeric_limits<size_t>::max());\n    tmpl::for_each<tmpl::list<operator_applied_to_fields_tag, operand_tag,\n                              operator_applied_to_operand_tag, residual_tag>>(\n        [&tag_is_retrievable](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          CAPTURE(db::tag_name<tag>());\n          CHECK(tag_is_retrievable(tag{}));\n        });\n    CHECK_FALSE(get_tag(Convergence::Tags::HasConverged<DummyOptionsGroup>{}));\n  }\n\n  const auto test_initialize_has_converged =\n      [&runner, &get_tag,\n       &set_tag](const Convergence::HasConverged& has_converged) {\n        const size_t iteration_id = 0;\n        set_tag(Convergence::Tags::IterationId<DummyOptionsGroup>{},\n                iteration_id);\n        REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n            make_not_null(&runner), 0));\n        auto& inbox = ActionTesting::get_inbox_tag<\n            element_array, LinearSolver::cg::detail::Tags::InitialHasConverged<\n                               DummyOptionsGroup>>(make_not_null(&runner), 0);\n        CAPTURE(has_converged);\n        inbox[iteration_id] = has_converged;\n        ActionTesting::next_action<element_array>(make_not_null(&runner), 0);\n        CHECK(get_tag(Convergence::Tags::HasConverged<DummyOptionsGroup>{}) ==\n              has_converged);\n        CHECK(ActionTesting::get_next_action_index<element_array>(runner, 0) ==\n              (has_converged ? 2 : 1));\n      };\n  SECTION(\"InitializeHasConverged (not yet converged: continue loop)\") {\n    test_initialize_has_converged(Convergence::HasConverged{1, 0});\n  }\n  SECTION(\"InitializeHasConverged (has converged: terminate loop)\") {\n    test_initialize_has_converged(Convergence::HasConverged{1, 1});\n  }\n\n  const auto test_update_operand = [&runner, &get_tag,\n                                    &set_tag](const Convergence::HasConverged&\n                                                  has_converged) {\n    const size_t iteration_id = 0;\n    set_tag(Convergence::Tags::IterationId<DummyOptionsGroup>{}, iteration_id);\n    set_tag(operand_tag{}, blaze::DynamicVector<double>(3, 2.));\n    set_tag(residual_tag{}, blaze::DynamicVector<double>(3, 1.));\n    runner.template force_next_action_to_be<\n        element_array, LinearSolver::cg::detail::UpdateOperand<\n                           fields_tag, DummyOptionsGroup, DummyOptionsGroup>>(\n        0);\n    REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n        make_not_null(&runner), 0));\n    auto& inbox = ActionTesting::get_inbox_tag<\n        element_array, LinearSolver::cg::detail::Tags::\n                           ResidualRatioAndHasConverged<DummyOptionsGroup>>(\n        make_not_null(&runner), 0);\n    const double res_ratio = 2.;\n    CAPTURE(has_converged);\n    inbox[iteration_id] = std::make_tuple(res_ratio, has_converged);\n    ActionTesting::next_action<element_array>(make_not_null(&runner), 0);\n    CHECK(get_tag(LinearSolver::Tags::Operand<VectorTag>{}) ==\n          blaze::DynamicVector<double>(3, 5.));\n    CHECK(get_tag(Convergence::Tags::IterationId<DummyOptionsGroup>{}) == 1);\n    CHECK(get_tag(Convergence::Tags::HasConverged<DummyOptionsGroup>{}) ==\n          has_converged);\n    CHECK(ActionTesting::get_next_action_index<element_array>(runner, 0) ==\n          (has_converged ? 2 : 1));\n  };\n  SECTION(\"UpdateOperand (not yet converged: continue loop)\") {\n    test_update_operand(Convergence::HasConverged{1, 0});\n  }\n  SECTION(\"UpdateOperand (has converged: terminate loop)\") {\n    test_update_operand(Convergence::HasConverged{1, 1});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/ConjugateGradient/Test_ResidualMonitorActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/ResidualMonitorActionsTestHelpers.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/ResidualMonitor.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/ConjugateGradient/ResidualMonitorActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Observe.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace helpers = ResidualMonitorActionsTestHelpers;\n\nnamespace {\n\nstruct TestLinearSolver {};\n\nstruct VectorTag : db::SimpleTag {\n  using type = blaze::DynamicVector<double>;\n};\n\nusing fields_tag = VectorTag;\nusing residual_square_tag = LinearSolver::Tags::MagnitudeSquare<\n    LinearSolver::Tags::Residual<fields_tag>>;\nusing initial_residual_magnitude_tag = ::Tags::Initial<\n    LinearSolver::Tags::Magnitude<LinearSolver::Tags::Residual<fields_tag>>>;\n\ntemplate <typename Metavariables>\nstruct MockResidualMonitor {\n  using component_being_mocked =\n      LinearSolver::cg::detail::ResidualMonitor<Metavariables, fields_tag,\n                                                TestLinearSolver>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using const_global_cache_tags =\n      typename LinearSolver::cg::detail::ResidualMonitor<\n          Metavariables, fields_tag, TestLinearSolver>::const_global_cache_tags;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<LinearSolver::cg::detail::InitializeResidualMonitor<\n          fields_tag, TestLinearSolver>>>>;\n};\n\n// This is used to receive action calls from the residual monitor\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using component_being_mocked = void;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using inbox_tags = tmpl::list<\n      LinearSolver::cg::detail::Tags::InitialHasConverged<TestLinearSolver>,\n      LinearSolver::cg::detail::Tags::Alpha<TestLinearSolver>,\n      LinearSolver::cg::detail::Tags::ResidualRatioAndHasConverged<\n          TestLinearSolver>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<MockResidualMonitor<Metavariables>,\n                                    MockElementArray<Metavariables>,\n                                    helpers::MockObserverWriter<Metavariables>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.Parallel.LinearSolver.ConjugateGradient.ResidualMonitorActions\",\n    \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  using residual_monitor = MockResidualMonitor<Metavariables>;\n  using element_array = MockElementArray<Metavariables>;\n  using observer_writer = helpers::MockObserverWriter<Metavariables>;\n\n  const Convergence::Criteria convergence_criteria{2, 0., 0.5};\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {::Verbosity::Verbose, convergence_criteria}};\n\n  // Setup mock residual monitor\n  ActionTesting::emplace_component<residual_monitor>(&runner, 0);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<residual_monitor>(make_not_null(&runner), 0);\n  }\n\n  // Setup mock element array\n  ActionTesting::emplace_component<element_array>(&runner, 0);\n\n  // Setup mock observer writer\n  ActionTesting::emplace_component_and_initialize<observer_writer>(\n      &runner, 0,\n      {observers::ObservationId{}, std::string{}, std::vector<std::string>{},\n       std::tuple<size_t, double, double>{\n           0, std::numeric_limits<double>::signaling_NaN(),\n           std::numeric_limits<double>::signaling_NaN()}});\n\n  // DataBox shortcuts\n  const auto get_residual_monitor_tag =\n      [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<residual_monitor, tag>(runner, 0);\n  };\n  const auto get_element_inbox_tag = [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_inbox_tag<element_array, tag>(runner, 0);\n  };\n  const auto get_observer_writer_tag = [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<observer_writer, tag>(runner, 0);\n  };\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  SECTION(\"InitializeResidual\") {\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::InitializeResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 4.);\n    ActionTesting::invoke_queued_threaded_action<observer_writer>(\n        make_not_null(&runner), 0);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(residual_square_tag{}) == 4.);\n    CHECK(get_residual_monitor_tag(initial_residual_magnitude_tag{}) == 2.);\n    // Test element state\n    CHECK_FALSE(get_element_inbox_tag(\n                    LinearSolver::cg::detail::Tags::InitialHasConverged<\n                        TestLinearSolver>{})\n                    .at(0));\n    // Test observer writer state\n    CHECK(get_observer_writer_tag(helpers::CheckSubfileNameTag{}) ==\n          \"/TestLinearSolverResiduals\");\n    CHECK(get_observer_writer_tag(helpers::CheckReductionNamesTag{}) ==\n          std::vector<std::string>{\"Iteration\", \"Walltime\", \"Residual\"});\n    CHECK(get<0>(get_observer_writer_tag(helpers::CheckReductionDataTag{})) ==\n          0);\n    CHECK(get<2>(get_observer_writer_tag(helpers::CheckReductionDataTag{})) ==\n          approx(2.));\n  }\n\n  SECTION(\"InitializeResidualAndConverge\") {\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::InitializeResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 0.);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(residual_square_tag{}) == 0.);\n    CHECK(get_residual_monitor_tag(initial_residual_magnitude_tag{}) == 0.);\n    // Test element state\n    CHECK(get_element_inbox_tag(\n              LinearSolver::cg::detail::Tags::InitialHasConverged<\n                  TestLinearSolver>{})\n              .at(0));\n  }\n\n  SECTION(\"ComputeAlpha\") {\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::InitializeResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::ComputeAlpha<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 0_st, 2.);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(residual_square_tag{}) == 1.);\n    // Test element state\n    CHECK(get_element_inbox_tag(\n              LinearSolver::cg::detail::Tags::Alpha<TestLinearSolver>{})\n              .at(0) == 0.5);\n  }\n\n  SECTION(\"UpdateResidual\") {\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::InitializeResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 9.);\n    ActionTesting::invoke_queued_threaded_action<observer_writer>(\n        make_not_null(&runner), 0);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::UpdateResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 0_st, 4.);\n    ActionTesting::invoke_queued_threaded_action<observer_writer>(\n        make_not_null(&runner), 0);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(residual_square_tag{}) == 4.);\n    CHECK(get_residual_monitor_tag(initial_residual_magnitude_tag{}) == 3.);\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::cg::detail::Tags::ResidualRatioAndHasConverged<\n                TestLinearSolver>{})\n            .at(0);\n    CHECK(get<0>(element_inbox) == approx(4. / 9.));\n    const auto& has_converged = get<1>(element_inbox);\n    CHECK_FALSE(has_converged);\n    // Test observer writer state\n    CHECK(get_observer_writer_tag(helpers::CheckSubfileNameTag{}) ==\n          \"/TestLinearSolverResiduals\");\n    CHECK(get_observer_writer_tag(helpers::CheckReductionNamesTag{}) ==\n          std::vector<std::string>{\"Iteration\", \"Walltime\", \"Residual\"});\n    CHECK(get<0>(get_observer_writer_tag(helpers::CheckReductionDataTag{})) ==\n          1);\n    CHECK(get<2>(get_observer_writer_tag(helpers::CheckReductionDataTag{})) ==\n          approx(2.));\n  }\n\n  SECTION(\"ConvergeByAbsoluteResidual\") {\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::InitializeResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::UpdateResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 0_st, 0.);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(residual_square_tag{}) == 0.);\n    CHECK(get_residual_monitor_tag(initial_residual_magnitude_tag{}) == 1.);\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::cg::detail::Tags::ResidualRatioAndHasConverged<\n                TestLinearSolver>{})\n            .at(0);\n    CHECK(get<0>(element_inbox) == 0.);\n    const auto& has_converged = get<1>(element_inbox);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n  }\n\n  SECTION(\"ConvergeByMaxIterations\") {\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::InitializeResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::UpdateResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 1.);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(residual_square_tag{}) == 1.);\n    CHECK(get_residual_monitor_tag(initial_residual_magnitude_tag{}) == 1.);\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::cg::detail::Tags::ResidualRatioAndHasConverged<\n                TestLinearSolver>{})\n            .at(1);\n    CHECK(get<0>(element_inbox) == 1.);\n    const auto& has_converged = get<1>(element_inbox);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::MaxIterations);\n  }\n\n  SECTION(\"ConvergeByRelativeResidual\") {\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::InitializeResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::cg::detail::UpdateResidual<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 0_st, 0.25);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(residual_square_tag{}) == 0.25);\n    CHECK(get_residual_monitor_tag(initial_residual_magnitude_tag{}) == 1.);\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::cg::detail::Tags::ResidualRatioAndHasConverged<\n                TestLinearSolver>{})\n            .at(0);\n    CHECK(get<0>(element_inbox) == 0.25);\n    const auto& has_converged = get<1>(element_inbox);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::RelativeResidual);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelGmres\")\n\nset(LIBRARY_SOURCES\n  Test_ElementActions.cpp\n  Test_ResidualMonitorActions.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  DataStructures\n  LinearSolverHelpers\n  Logging\n  Observer\n  ParallelLinearSolver\n)\n\nadd_standalone_test(\n  \"Integration.LinearSolver.GmresAlgorithm\"\n  INPUT_FILE \"Test_GmresAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_GmresAlgorithm\"\n  PRIVATE\n  \"${INTEGRATION_TEST_LINK_LIBRARIES}\")\nadd_standalone_test(\n  \"Integration.LinearSolver.ComplexGmresAlgorithm\"\n  INPUT_FILE \"Test_ComplexGmresAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_ComplexGmresAlgorithm\"\n  PRIVATE\n  \"${INTEGRATION_TEST_LINK_LIBRARIES}\")\nadd_standalone_test(\n  \"Integration.LinearSolver.GmresPreconditionedAlgorithm\"\n  INPUT_FILE \"Test_GmresPreconditionedAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_GmresPreconditionedAlgorithm\"\n  PRIVATE\n  \"${INTEGRATION_TEST_LINK_LIBRARIES}\")\nadd_standalone_test(\n  \"Integration.LinearSolver.DistributedGmresAlgorithm\"\n  INPUT_FILE \"Test_DistributedGmresAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_DistributedGmresAlgorithm\"\n  PRIVATE\n  \"${DISTRIBUTED_INTEGRATION_TEST_LINK_LIBRARIES}\")\nadd_standalone_test(\n  \"Integration.LinearSolver.DistributedGmresPreconditionedAlgorithm\"\n  INPUT_FILE \"Test_DistributedGmresPreconditionedAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_DistributedGmresPreconditionedAlgorithm\"\n  PRIVATE\n  \"${DISTRIBUTED_INTEGRATION_TEST_LINK_LIBRARIES}\")\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_ComplexGmresAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct SerialGmres {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the GMRES linear solver algorithm\"};\n\n  using linear_solver =\n      LinearSolver::gmres::Gmres<Metavariables,\n                                 helpers::fields_tag<std::complex<double>>,\n                                 SerialGmres, false>;\n  using preconditioner = void;\n\n  using component_list = helpers::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_ComplexGmresAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nLinearOperator:\n  - [[1, 2], [2, -1]]\n  - [[3, 4], [4, 1]]\nSource: [[1, 1], [2, -3]]\nInitialGuess: [[0, 0], [0, 0]]\nExpectedResult: [[0.45, -1.4], [-1.2, 0.15]]\n\nObservers:\n  VolumeFileName: \"Test_ComplexGmresAlgorithm_Volume\"\n  ReductionFileName: \"Test_ComplexGmresAlgorithm_Reductions\"\n\nSerialGmres:\n  ConvergenceCriteria:\n    MaxIterations: 2\n    AbsoluteResidual: 1e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n\nConvergenceReason: AbsoluteResidual\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_DistributedGmresAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\nnamespace helpers_distributed = DistributedLinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct ParallelGmres {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the GMRES linear solver algorithm on multiple elements\"};\n  static constexpr size_t volume_dim = 1;\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithoutBoundaryConditions<\n          volume_dim>;\n\n  using linear_solver =\n      LinearSolver::gmres::Gmres<Metavariables, helpers_distributed::fields_tag,\n                                 ParallelGmres, false>;\n  using preconditioner = void;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<1>, tmpl::list<domain::creators::Interval>>>;\n  };\n\n  using component_list = helpers_distributed::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_DistributedGmresAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  The test problem being solved here is a DG-discretized 1D Poisson equation\n  -u''(x) = f(x) on the interval [0, pi] with source f(x)=sin(x) and homogeneous\n  Dirichlet boundary conditions such that the solution is u(x)=sin(x) as well.\n\n  Details:\n  - Domain decomposition: 2 elements with 3 LGL grid-points each\n  - \"Primal\" DG formulation (no auxiliary variable)\n  - Not multiplied by mass matrix so the operator is not symmetric\n  - Mass-lumping: inverse mass matrix is approximated by diagonal\n  - Internal penalty flux with sigma = 1.5 * (N_points - 1)^2 / h\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[20.26423672846756 ,  3.242277876554809, -2.836993141985458],\n      [ 0.810569469138702,  3.24227787655481 , -0.405284734569351],\n      [-2.836993141985458, -1.621138938277405, 12.969111506219237],\n      [ 1.215854203708053, -4.863416814832214, -7.295125222248322],\n      [ 0.               ,  0.               , -1.215854203708054],\n      [ 0.               ,  0.               ,  1.215854203708053]]\n  - [[ 1.215854203708053,  0.               ,  0.               ],\n      [-1.215854203708054,  0.               ,  0.               ],\n      [-7.295125222248322, -4.863416814832214,  1.215854203708053],\n      [12.969111506219237, -1.621138938277405, -2.836993141985458],\n      [-0.405284734569351,  3.24227787655481 ,  0.810569469138702],\n      [-2.836993141985458,  3.242277876554809, 20.26423672846756 ]]\n\nSource:\n  - [0., 0.7071067811865475, 1.]\n  - [1., 0.7071067811865476, 0.]\n\nExpectedResult:\n  - [-0.0363482510397858,  0.7235793356729757,  0.9928055333486293]\n  - [ 0.9928055333486292,  0.7235793356729758, -0.0363482510397858]\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_DistributedGmresAlgorithm_Volume\"\n  ReductionFileName: \"Test_DistributedGmresAlgorithm_Reductions\"\n\nParallelGmres:\n  ConvergenceCriteria:\n    MaxIterations: 3\n    AbsoluteResidual: 1e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n\nConvergenceReason: AbsoluteResidual\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_DistributedGmresPreconditionedAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Richardson/Richardson.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\nnamespace helpers_distributed = DistributedLinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct ParallelGmres {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Preconditioner {\n  static constexpr Options::String help = \"Options for the preconditioner\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the preconditioned GMRES linear solver algorithm on multiple \"\n      \"elements\"};\n  static constexpr size_t volume_dim = 1;\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithoutBoundaryConditions<\n          volume_dim>;\n\n  using linear_solver =\n      LinearSolver::gmres::Gmres<Metavariables, helpers_distributed::fields_tag,\n                                 ParallelGmres, true>;\n  using preconditioner = LinearSolver::Richardson::Richardson<\n      typename linear_solver::operand_tag, Preconditioner,\n      typename linear_solver::preconditioner_source_tag>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<1>, tmpl::list<domain::creators::Interval>>>;\n  };\n\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n  using component_list = helpers_distributed::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_DistributedGmresPreconditionedAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  The test problem being solved here is a DG-discretized 1D Poisson equation\n  -u''(x) = f(x) on the interval [0, pi] with source f(x)=sin(x) and homogeneous\n  Dirichlet boundary conditions such that the solution is u(x)=sin(x) as well.\n\n  Details:\n  - Domain decomposition: 2 elements with 3 LGL grid-points each\n  - \"Primal\" DG formulation (no auxiliary variable)\n  - Multiplied by mass matrix and no mass-lumping\n  - Internal penalty flux with sigma = 1.5 * (N_points - 1)^2 / h\n  - The operator is symmetric, which helps the Richardson preconditioner\n    converge.\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[ 5.305164769729845,  0.848826363156775, -0.742723067762178],\n      [ 0.848826363156775,  3.395305452627101, -0.424413181578388],\n      [-0.742723067762178, -0.424413181578388,  3.395305452627101],\n      [ 0.318309886183791, -1.273239544735163, -1.909859317102744],\n      [ 0.               ,  0.               , -1.273239544735163],\n      [ 0.               ,  0.               ,  0.318309886183791]]\n  - [[ 0.318309886183791,  0.               ,  0.               ],\n      [-1.273239544735163,  0.               ,  0.               ],\n      [-1.909859317102744, -1.273239544735163,  0.318309886183791],\n      [ 3.395305452627101, -0.424413181578388, -0.742723067762178],\n      [-0.424413181578388,  3.395305452627101,  0.848826363156775],\n      [-0.742723067762178,  0.848826363156775,  5.305164769729845]]\n\nSource:\n  - [0.                , 0.740480489693061, 0.2617993877991494]\n  - [0.2617993877991494, 0.740480489693061, 0.                ]\n\nExpectedResult:\n  - [-0.0363482510397858,  0.7235793356729757,  0.9928055333486293]\n  - [ 0.9928055333486292,  0.7235793356729758, -0.0363482510397858]\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_DistributedGmresPreconditionedAlgorithm_Volume\"\n  ReductionFileName: \"Test_DistributedGmresPreconditionedAlgorithm_Reductions\"\n\nParallelGmres:\n  ConvergenceCriteria:\n    MaxIterations: 2\n    AbsoluteResidual: 1e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n\nPreconditioner:\n  RelaxationParameter: 0.2916330767929102\n  Iterations: 65\n  Verbosity: Verbose\n\nConvergenceReason: AbsoluteResidual\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_ElementActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataBox/TagName.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/SetData.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/ElementActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/InitializeElement.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Tags/InboxTags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct DummyOptionsGroup {};\n\nstruct VectorTag : db::SimpleTag {\n  using type = blaze::DynamicVector<double>;\n};\n\nusing fields_tag = VectorTag;\nusing operator_applied_to_fields_tag =\n    LinearSolver::Tags::OperatorAppliedTo<fields_tag>;\nusing initial_fields_tag = ::Tags::Initial<fields_tag>;\nusing operand_tag = LinearSolver::Tags::Operand<fields_tag>;\nusing preconditioned_operand_tag =\n    LinearSolver::Tags::Preconditioned<operand_tag>;\nusing operator_applied_to_operand_tag =\n    LinearSolver::Tags::OperatorAppliedTo<operand_tag>;\nusing operator_applied_to_preconditioned_operand_tag =\n    LinearSolver::Tags::OperatorAppliedTo<preconditioned_operand_tag>;\nusing orthogonalization_iteration_id_tag =\n    LinearSolver::Tags::Orthogonalization<\n        Convergence::Tags::IterationId<DummyOptionsGroup>>;\nusing basis_history_tag = LinearSolver::Tags::KrylovSubspaceBasis<operand_tag>;\nusing preconditioned_basis_history_tag =\n    LinearSolver::Tags::KrylovSubspaceBasis<preconditioned_operand_tag>;\n\ntemplate <typename Metavariables, bool Preconditioned>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<VectorTag>>,\n                     LinearSolver::gmres::detail::InitializeElement<\n                         fields_tag, DummyOptionsGroup, Preconditioned>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<\n              LinearSolver::gmres::detail::NormalizeInitialOperand<\n                  fields_tag, DummyOptionsGroup, Preconditioned,\n                  DummyOptionsGroup, void>,\n              LinearSolver::gmres::detail::PrepareStep<\n                  fields_tag, DummyOptionsGroup, Preconditioned,\n                  DummyOptionsGroup, void>,\n              LinearSolver::gmres::detail::NormalizeOperandAndUpdateField<\n                  fields_tag, DummyOptionsGroup, Preconditioned,\n                  DummyOptionsGroup, void>,\n              LinearSolver::gmres::detail::CompleteStep<\n                  fields_tag, DummyOptionsGroup, Preconditioned,\n                  DummyOptionsGroup, void>,\n              Parallel::Actions::TerminatePhase>>>;\n};\n\ntemplate <bool Preconditioned>\nstruct Metavariables {\n  using element_array = ElementArray<Metavariables, Preconditioned>;\n  using component_list = tmpl::list<element_array>;\n};\n\ntemplate <bool Preconditioned>\nvoid test_element_actions() {\n  CAPTURE(Preconditioned);\n  using metavariables = Metavariables<Preconditioned>;\n  using element_array = typename metavariables::element_array;\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{{}};\n\n  // Setup mock element array\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), 0, {blaze::DynamicVector<double>(3, 0.)});\n  ActionTesting::next_action<element_array>(make_not_null(&runner), 0);\n\n  // DataBox shortcuts\n  const auto get_tag = [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<element_array, tag>(runner, 0);\n  };\n  const auto tag_is_retrievable = [&runner](auto tag_v) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::tag_is_retrievable<element_array, tag>(runner, 0);\n  };\n  const auto set_tag = [&runner](auto tag_v, const auto& value) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    ActionTesting::simple_action<element_array,\n                                 ::Actions::SetData<tmpl::list<tag>>>(\n        make_not_null(&runner), 0, value);\n  };\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Can't test the other element actions because reductions are not yet\n  // supported. The full algorithm is tested in\n  // `Test_GmresAlgorithm.cpp` and\n  // `Test_DistributedGmresAlgorithm.cpp`.\n\n  {\n    INFO(\"InitializeElement\");\n    CHECK(get_tag(Convergence::Tags::IterationId<DummyOptionsGroup>{}) ==\n          std::numeric_limits<size_t>::max());\n    tmpl::for_each<tmpl::list<\n        initial_fields_tag, operator_applied_to_fields_tag, operand_tag,\n        std::conditional_t<Preconditioned,\n                           operator_applied_to_preconditioned_operand_tag,\n                           operator_applied_to_operand_tag>,\n        orthogonalization_iteration_id_tag, basis_history_tag>>(\n        [&tag_is_retrievable](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          CAPTURE(db::tag_name<tag>());\n          CHECK(tag_is_retrievable(tag{}));\n        });\n    CHECK(tag_is_retrievable(preconditioned_operand_tag{}) == Preconditioned);\n    CHECK(tag_is_retrievable(preconditioned_basis_history_tag{}) ==\n          Preconditioned);\n    CHECK_FALSE(get_tag(Convergence::Tags::HasConverged<DummyOptionsGroup>{}));\n  }\n\n  const auto test_normalize_initial_operand =\n      [&runner, &get_tag,\n       &set_tag](const Convergence::HasConverged& has_converged) {\n        const size_t iteration_id = 0;\n        set_tag(Convergence::Tags::IterationId<DummyOptionsGroup>{},\n                iteration_id);\n        set_tag(operand_tag{}, blaze::DynamicVector<double>(3, 2.));\n        set_tag(basis_history_tag{}, std::vector<blaze::DynamicVector<double>>{\n                                         blaze::DynamicVector<double>(3, 0.5),\n                                         blaze::DynamicVector<double>(3, 1.5)});\n        REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n            make_not_null(&runner), 0));\n        auto& inbox = ActionTesting::get_inbox_tag<\n            element_array, LinearSolver::gmres::detail::Tags::\n                               InitialOrthogonalization<DummyOptionsGroup>>(\n            make_not_null(&runner), 0);\n        const double residual_magnitude = 4.;\n        CAPTURE(has_converged);\n        inbox[iteration_id] =\n            std::make_tuple(residual_magnitude, has_converged);\n        ActionTesting::next_action<element_array>(make_not_null(&runner), 0);\n        CHECK_ITERABLE_APPROX(\n            get_tag(operand_tag{}),\n            blaze::DynamicVector<double>(3, has_converged ? 2. : 0.5));\n        CHECK(get_tag(basis_history_tag{}).size() == (has_converged ? 2 : 3));\n        if (not has_converged) {\n          CHECK(get_tag(basis_history_tag{})[2] == get_tag(operand_tag{}));\n        }\n        CHECK(get_tag(Convergence::Tags::HasConverged<DummyOptionsGroup>{}) ==\n              has_converged);\n        CHECK(ActionTesting::get_next_action_index<element_array>(runner, 0) ==\n              (has_converged ? 4 : 1));\n      };\n  SECTION(\"NormalizeInitialOperand (not yet converged: continue loop)\") {\n    test_normalize_initial_operand(Convergence::HasConverged{1, 0});\n  }\n  SECTION(\"NormalizeInitialOperand (has converged: terminate loop)\") {\n    test_normalize_initial_operand(Convergence::HasConverged{1, 1});\n  }\n\n  const auto test_normalize_operand_and_update_field =\n      [&runner, &get_tag,\n       &set_tag](const Convergence::HasConverged& has_converged) {\n        const size_t iteration_id = 2;\n        set_tag(Convergence::Tags::IterationId<DummyOptionsGroup>{},\n                iteration_id);\n        set_tag(initial_fields_tag{}, blaze::DynamicVector<double>(3, -1.));\n        set_tag(operand_tag{}, blaze::DynamicVector<double>(3, 2.));\n        set_tag(basis_history_tag{}, std::vector<blaze::DynamicVector<double>>{\n                                         blaze::DynamicVector<double>(3, 0.5),\n                                         blaze::DynamicVector<double>(3, 1.5)});\n        if constexpr (Preconditioned) {\n          set_tag(preconditioned_basis_history_tag{},\n                  get_tag(basis_history_tag{}));\n        }\n        runner.template force_next_action_to_be<\n            element_array,\n            LinearSolver::gmres::detail::NormalizeOperandAndUpdateField<\n                fields_tag, DummyOptionsGroup, Preconditioned,\n                DummyOptionsGroup, void>>(0);\n        REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n            make_not_null(&runner), 0));\n        auto& inbox = ActionTesting::get_inbox_tag<\n            element_array,\n            LinearSolver::gmres::detail::Tags::FinalOrthogonalization<\n                DummyOptionsGroup, double>>(make_not_null(&runner), 0);\n        const double normalization = 4.;\n        const blaze::DynamicVector<double> minres{2., 4.};\n        CAPTURE(has_converged);\n        inbox[iteration_id] =\n            std::make_tuple(normalization, minres, has_converged);\n        ActionTesting::next_action<element_array>(make_not_null(&runner), 0);\n        CHECK_ITERABLE_APPROX(get_tag(operand_tag{}),\n                              blaze::DynamicVector<double>(3, 0.5));\n        CHECK(get_tag(basis_history_tag{}).size() == 3);\n        CHECK(get_tag(basis_history_tag{})[2] == get_tag(operand_tag{}));\n        // minres * basis_history - initial = 2 * 0.5 + 4 * 1.5 - 1 = 6\n        CHECK_ITERABLE_APPROX(get_tag(VectorTag{}),\n                              blaze::DynamicVector<double>(3, 6.));\n        CHECK(get_tag(Convergence::Tags::IterationId<DummyOptionsGroup>{}) ==\n              2);\n        CHECK(get_tag(Convergence::Tags::HasConverged<DummyOptionsGroup>{}) ==\n              has_converged);\n        // CompleteStep action\n        ActionTesting::next_action<element_array>(make_not_null(&runner), 0);\n        CHECK(ActionTesting::get_next_action_index<element_array>(runner, 0) ==\n              (has_converged ? 4 : 1));\n      };\n  SECTION(\"NormalizeOperandAndUpdateField (not yet converged: continue loop)\") {\n    test_normalize_operand_and_update_field(Convergence::HasConverged{1, 0});\n  }\n  SECTION(\"NormalizeOperandAndUpdateField (has converged: terminate loop)\") {\n    test_normalize_operand_and_update_field(Convergence::HasConverged{1, 1});\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.LinearSolver.Gmres.ElementActions\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  test_element_actions<true>();\n  test_element_actions<false>();\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_GmresAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct SerialGmres {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the GMRES linear solver algorithm\"};\n\n  using linear_solver =\n      LinearSolver::gmres::Gmres<Metavariables, helpers::fields_tag<double>,\n                                 SerialGmres, false>;\n  using preconditioner = void;\n\n  using component_list = helpers::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_GmresAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nLinearOperator: [[4, 1], [3, 1]]\nSource: [1, 2]\nInitialGuess: [2, 1]\nExpectedResult: [-1., 5.]\n\nObservers:\n  VolumeFileName: \"Test_GmresAlgorithm_Volume\"\n  ReductionFileName: \"Test_GmresAlgorithm_Reductions\"\n\nSerialGmres:\n  ConvergenceCriteria:\n    MaxIterations: 2\n    AbsoluteResidual: 1e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n\nConvergenceReason: AbsoluteResidual\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_GmresPreconditionedAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Richardson/Richardson.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct SerialGmres {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Preconditioner {\n  static constexpr Options::String help = \"Options for the preconditioner\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the preconditioned GMRES linear solver algorithm\"};\n\n  using linear_solver =\n      LinearSolver::gmres::Gmres<Metavariables, helpers::fields_tag<double>,\n                                 SerialGmres, true>;\n  using preconditioner = LinearSolver::Richardson::Richardson<\n      typename linear_solver::operand_tag, Preconditioner,\n      typename linear_solver::preconditioner_source_tag>;\n\n  using component_list = helpers::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_GmresPreconditionedAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nLinearOperator: [[4, 1], [1, 3]]\nSource: [1, 2]\nInitialGuess: [2, 1]\nExpectedResult: [0.0909090909090909, 0.6363636363636364]\n\nObservers:\n  VolumeFileName: \"Test_GmresPreconditionedAlgorithm_Volume\"\n  ReductionFileName: \"Test_GmresPreconditionedAlgorithm_Reductions\"\n\nSerialGmres:\n  ConvergenceCriteria:\n    MaxIterations: 1\n    AbsoluteResidual: 1e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n\nPreconditioner:\n  RelaxationParameter: 0.2857142857142857\n  Iterations: 2\n  Verbosity: Verbose\n\nConvergenceReason: AbsoluteResidual\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/Test_ResidualMonitorActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/ResidualMonitorActionsTestHelpers.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"IO/Observer/ObservationId.hpp\"\n#include \"NumericalAlgorithms/Convergence/Criteria.hpp\"\n#include \"NumericalAlgorithms/Convergence/HasConverged.hpp\"\n#include \"NumericalAlgorithms/Convergence/Reason.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/ResidualMonitor.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/ResidualMonitorActions.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Observe.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace helpers = ResidualMonitorActionsTestHelpers;\n\nnamespace {\n\nstruct TestLinearSolver {};\n\nstruct VectorTag : db::SimpleTag {\n  using type = blaze::DynamicVector<double>;\n};\n\nusing fields_tag = VectorTag;\nusing initial_residual_magnitude_tag = ::Tags::Initial<\n    LinearSolver::Tags::Magnitude<LinearSolver::Tags::Residual<fields_tag>>>;\nusing orthogonalization_history_tag =\n    LinearSolver::Tags::OrthogonalizationHistory<fields_tag>;\n\ntemplate <typename Metavariables>\nstruct MockResidualMonitor {\n  using component_being_mocked =\n      LinearSolver::gmres::detail::ResidualMonitor<Metavariables, fields_tag,\n                                                   TestLinearSolver>;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockSingletonChare;\n  using array_index = int;\n  using const_global_cache_tags =\n      typename LinearSolver::gmres::detail::ResidualMonitor<\n          Metavariables, fields_tag, TestLinearSolver>::const_global_cache_tags;\n  using phase_dependent_action_list = tmpl::list<Parallel::PhaseActions<\n      Parallel::Phase::Initialization,\n      tmpl::list<LinearSolver::gmres::detail::InitializeResidualMonitor<\n          fields_tag, TestLinearSolver>>>>;\n};\n\n// This is used to receive action calls from the residual monitor\ntemplate <typename Metavariables>\nstruct MockElementArray {\n  using component_being_mocked = void;\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization, tmpl::list<>>>;\n  using inbox_tags =\n      tmpl::list<LinearSolver::gmres::detail::Tags::InitialOrthogonalization<\n                     TestLinearSolver>,\n                 LinearSolver::gmres::detail::Tags::Orthogonalization<\n                     TestLinearSolver, double>,\n                 LinearSolver::gmres::detail::Tags::FinalOrthogonalization<\n                     TestLinearSolver, double>>;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<MockResidualMonitor<Metavariables>,\n                                    MockElementArray<Metavariables>,\n                                    helpers::MockObserverWriter<Metavariables>>;\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.ParallelAlgorithms.LinearSolver.Gmres.ResidualMonitorActions\",\n    \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  using residual_monitor = MockResidualMonitor<Metavariables>;\n  using element_array = MockElementArray<Metavariables>;\n  using observer_writer = helpers::MockObserverWriter<Metavariables>;\n\n  const Convergence::Criteria convergence_criteria{2, 0., 0.5};\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {::Verbosity::Verbose, convergence_criteria}};\n\n  // Setup mock residual monitor\n  ActionTesting::emplace_component<residual_monitor>(make_not_null(&runner), 0);\n  for (size_t i = 0; i < 2; ++i) {\n    ActionTesting::next_action<residual_monitor>(make_not_null(&runner), 0);\n  }\n\n  // Setup mock element array\n  ActionTesting::emplace_component<element_array>(make_not_null(&runner), 0);\n\n  // Setup mock observer writer\n  ActionTesting::emplace_component_and_initialize<observer_writer>(\n      make_not_null(&runner), 0,\n      {observers::ObservationId{}, std::string{}, std::vector<std::string>{},\n       std::tuple<size_t, double, double>{\n           0, std::numeric_limits<double>::signaling_NaN(),\n           std::numeric_limits<double>::signaling_NaN()}});\n\n  // DataBox shortcuts\n  const auto get_residual_monitor_tag =\n      [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<residual_monitor, tag>(runner, 0);\n  };\n  const auto get_element_inbox_tag = [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_inbox_tag<element_array, tag>(runner, 0);\n  };\n  const auto get_observer_writer_tag = [&runner](auto tag_v) -> decltype(auto) {\n    using tag = std::decay_t<decltype(tag_v)>;\n    return ActionTesting::get_databox_tag<observer_writer, tag>(runner, 0);\n  };\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  SECTION(\"InitializeResidualMagnitude\") {\n    ActionTesting::simple_action<\n        residual_monitor,\n        LinearSolver::gmres::detail::InitializeResidualMagnitude<\n            fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 2.);\n    ActionTesting::invoke_queued_threaded_action<observer_writer>(\n        make_not_null(&runner), 0);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(initial_residual_magnitude_tag{}) == 2.);\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::gmres::detail::Tags::InitialOrthogonalization<\n                TestLinearSolver>{})\n            .at(0);\n    CHECK(get<0>(element_inbox) == 2.);\n    const auto& has_converged = get<1>(element_inbox);\n    CHECK_FALSE(has_converged);\n    // Test observer writer state\n    CHECK(get_observer_writer_tag(helpers::CheckSubfileNameTag{}) ==\n          \"/TestLinearSolverResiduals\");\n    CHECK(get_observer_writer_tag(helpers::CheckReductionNamesTag{}) ==\n          std::vector<std::string>{\"Iteration\", \"Walltime\", \"Residual\"});\n    CHECK(get<0>(get_observer_writer_tag(helpers::CheckReductionDataTag{})) ==\n          0);\n    CHECK(get<2>(get_observer_writer_tag(helpers::CheckReductionDataTag{})) ==\n          approx(2.));\n  }\n\n  SECTION(\"InitializeResidualMagnitudeAndConverge\") {\n    ActionTesting::simple_action<\n        residual_monitor,\n        LinearSolver::gmres::detail::InitializeResidualMagnitude<\n            fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 0.);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(initial_residual_magnitude_tag{}) == 0.);\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::gmres::detail::Tags::InitialOrthogonalization<\n                TestLinearSolver>{})\n            .at(0);\n    CHECK(get<0>(element_inbox) == 0.);\n    const auto& has_converged = get<1>(element_inbox);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n  }\n\n  SECTION(\"StoreOrthogonalization\") {\n    ActionTesting::simple_action<\n        residual_monitor,\n        LinearSolver::gmres::detail::InitializeResidualMagnitude<\n            fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 0_st, 2.);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(orthogonalization_history_tag{})(0, 0) ==\n          2.);\n    // Test element state\n    CHECK(get_element_inbox_tag(\n              LinearSolver::gmres::detail::Tags::Orthogonalization<\n                  TestLinearSolver, double>{})\n              .at(1) == 2.);\n  }\n\n  SECTION(\"StoreOrthogonalization (final)\") {\n    ActionTesting::simple_action<\n        residual_monitor,\n        LinearSolver::gmres::detail::InitializeResidualMagnitude<\n            fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 2.);\n    ActionTesting::invoke_queued_threaded_action<observer_writer>(\n        make_not_null(&runner), 0);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 0_st, 3.);\n    // Test intermediate residual monitor state\n    CHECK(get_residual_monitor_tag(orthogonalization_history_tag{})(0, 0) ==\n          3.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 1_st, 4.);\n    ActionTesting::invoke_queued_threaded_action<observer_writer>(\n        make_not_null(&runner), 0);\n    // Test residual monitor state\n    // H = [[3.], [2.]]\n    CHECK(get_residual_monitor_tag(orthogonalization_history_tag{}) ==\n          blaze::DynamicMatrix<double>({{3.}, {2.}}));\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::gmres::detail::Tags::FinalOrthogonalization<\n                TestLinearSolver, double>{})\n            .at(1);\n    // beta = [2., 0.]\n    // minres = inv(qr_R(H)) * trans(qr_Q(H)) * beta = [0.4615384615384615]\n    const auto& minres = get<1>(element_inbox);\n    CHECK(minres.size() == 1);\n    CHECK_ITERABLE_APPROX(minres,\n                          blaze::DynamicVector<double>({0.4615384615384615}));\n    // r = beta - H * minres = [0.6153846153846154, -0.923076923076923]\n    // |r| = 1.1094003924504583\n    const double residual_magnitude = 1.1094003924504583;\n    const auto& has_converged = get<2>(element_inbox);\n    CHECK_FALSE(has_converged);\n    CHECK(get<0>(element_inbox) == approx(2.));\n    // Test observer writer state\n    CHECK(get_observer_writer_tag(helpers::CheckSubfileNameTag{}) ==\n          \"/TestLinearSolverResiduals\");\n    CHECK(get_observer_writer_tag(helpers::CheckReductionNamesTag{}) ==\n          std::vector<std::string>{\"Iteration\", \"Walltime\", \"Residual\"});\n    CHECK(get<0>(get_observer_writer_tag(helpers::CheckReductionDataTag{})) ==\n          1);\n    CHECK(get<2>(get_observer_writer_tag(helpers::CheckReductionDataTag{})) ==\n          approx(residual_magnitude));\n  }\n\n  SECTION(\"ConvergeByAbsoluteResidual\") {\n    ActionTesting::simple_action<\n        residual_monitor,\n        LinearSolver::gmres::detail::InitializeResidualMagnitude<\n            fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 2.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 0_st, 1.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 1_st, 0.);\n    // Test residual monitor state\n    // H = [[1.], [0.]]\n    CHECK(get_residual_monitor_tag(orthogonalization_history_tag{}) ==\n          blaze::DynamicMatrix<double>({{1.}, {0.}}));\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::gmres::detail::Tags::FinalOrthogonalization<\n                TestLinearSolver, double>{})\n            .at(1);\n    // beta = [2., 0.]\n    // minres = inv(qr_R(H)) * trans(qr_Q(H)) * beta = [2.]\n    const auto& minres = get<1>(element_inbox);\n    CHECK(minres.size() == 1);\n    CHECK_ITERABLE_APPROX(minres, blaze::DynamicVector<double>({2.}));\n    // r = beta - H * minres = [0., 0.]\n    // |r| = 0.\n    const auto& has_converged = get<2>(element_inbox);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::AbsoluteResidual);\n    CHECK(get<0>(element_inbox) == 0.);\n  }\n\n  SECTION(\"ConvergeByMaxIterations\") {\n    ActionTesting::simple_action<\n        residual_monitor,\n        LinearSolver::gmres::detail::InitializeResidualMagnitude<\n            fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1.);\n    // Perform 2 mock iterations\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 0_st, 1.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 1_st, 4.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 2_st, 0_st, 3.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 2_st, 1_st, 4.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 2_st, 2_st, 25.);\n    // Test residual monitor state\n    CHECK(get_residual_monitor_tag(orthogonalization_history_tag{}) ==\n          blaze::DynamicMatrix<double>({{1., 3.}, {2., 4.}, {0., 5.}}));\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::gmres::detail::Tags::FinalOrthogonalization<\n                TestLinearSolver, double>{})\n            .at(2);\n    // beta = [1., 0., 0.]\n    // minres = inv(qr_R(H)) * trans(qr_Q(H)) * beta = [0.13178295, 0.03100775]\n    const auto& minres = get<1>(element_inbox);\n    CHECK(minres.size() == 2);\n    CHECK_ITERABLE_APPROX(\n        minres,\n        blaze::DynamicVector<double>({0.1317829457364342, 0.0310077519379845}));\n    // r = beta - H * minres = [0.77519, -0.38759, -0.15503]\n    // |r| = 0.8804509063256237\n    const auto& has_converged = get<2>(element_inbox);\n    CHECK(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::MaxIterations);\n  }\n\n  SECTION(\"ConvergeByRelativeResidual\") {\n    ActionTesting::simple_action<\n        residual_monitor,\n        LinearSolver::gmres::detail::InitializeResidualMagnitude<\n            fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 2.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 0_st, 3.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 1_st, 1.);\n    // Test residual monitor state\n    // H = [[3.], [1.]]\n    CHECK(get_residual_monitor_tag(orthogonalization_history_tag{}) ==\n          blaze::DynamicMatrix<double>({{3.}, {1.}}));\n    // Test element state\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::gmres::detail::Tags::FinalOrthogonalization<\n                TestLinearSolver, double>{})\n            .at(1);\n    // beta = [2., 0.]\n    // minres = inv(qr_R(H)) * trans(qr_Q(H)) * beta = [0.6]\n    const auto& minres = get<1>(element_inbox);\n    CHECK(minres.size() == 1);\n    CHECK_ITERABLE_APPROX(minres, blaze::DynamicVector<double>({0.6}));\n    // r = beta - H * minres = [0.2, -0.6]\n    // |r| = 0.6324555320336759\n    // |r| / |r_initial| = 0.31622776601683794\n    const auto& has_converged = get<2>(element_inbox);\n    REQUIRE(has_converged);\n    CHECK(has_converged.reason() == Convergence::Reason::RelativeResidual);\n  }\n\n  SECTION(\"ErrorOnDivergence\") {\n    ActionTesting::simple_action<\n        residual_monitor,\n        LinearSolver::gmres::detail::InitializeResidualMagnitude<\n            fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 0.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 0_st, 3.);\n    ActionTesting::simple_action<\n        residual_monitor, LinearSolver::gmres::detail::StoreOrthogonalization<\n                              fields_tag, TestLinearSolver, element_array>>(\n        make_not_null(&runner), 0, 1_st, 1_st, 1.);\n    // Test residual monitor state\n    // H = [[3.], [1.]]\n    CHECK(get_residual_monitor_tag(orthogonalization_history_tag{}) ==\n          blaze::DynamicMatrix<double>({{3.}, {1.}}));\n    // Test element state\n    // Initial residual is 0, so this should trigger an error\n    const auto& element_inbox =\n        get_element_inbox_tag(\n            LinearSolver::gmres::detail::Tags::FinalOrthogonalization<\n                TestLinearSolver, double>{})\n            .at(1);\n    const auto& minres = get<1>(element_inbox);\n    CHECK(minres.size() == 1);\n    CHECK_ITERABLE_APPROX(minres, blaze::DynamicVector<double>({0.}));\n    const auto& has_converged = get<2>(element_inbox);\n    REQUIRE(has_converged);\n    CHECK_THROWS_WITH(has_converged.check_for_error(),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Residual should decrease monotonically\"));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelMultigridActions\")\n\nset(LIBRARY_SOURCES\n  Test_RestrictFields.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  DataStructures\n  Domain\n  DomainStructure\n  Logging\n  Parallel\n  ParallelMultigrid\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Actions/Test_RestrictFields.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <optional>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/SegmentSize.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Actions/RestrictFields.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct DummyOptionsGroup {};\n\ntemplate <size_t N>\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct MassiveTag : db::SimpleTag {\n  using type = bool;\n};\n\nusing fields_tag =\n    ::Tags::Variables<tmpl::list<ScalarFieldTag<0>, ScalarFieldTag<1>>>;\n\ntemplate <size_t Dim, typename FieldsAreMassiveTag, typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<\n              amr::Tags::ParentId<Dim>, amr::Tags::ChildIds<Dim>,\n              domain::Tags::Mesh<Dim>, amr::Tags::ParentMesh<Dim>,\n              Convergence::Tags::IterationId<DummyOptionsGroup>, fields_tag>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<\n              LinearSolver::multigrid::Actions::SendFieldsToCoarserGrid<\n                  tmpl::list<fields_tag>, DummyOptionsGroup,\n                  FieldsAreMassiveTag>,\n              LinearSolver::multigrid::Actions::ReceiveFieldsFromFinerGrid<\n                  Dim, tmpl::list<fields_tag>, DummyOptionsGroup>,\n              Parallel::Actions::TerminatePhase>>>;\n};\n\ntemplate <size_t Dim, typename FieldsAreMassiveTag>\nstruct Metavariables {\n  using element_array = ElementArray<Dim, FieldsAreMassiveTag, Metavariables>;\n  using component_list = tmpl::list<element_array>;\n  using const_global_cache_tags =\n      tmpl::conditional_t<std::is_same_v<FieldsAreMassiveTag, void>,\n                          tmpl::list<>, tmpl::list<FieldsAreMassiveTag>>;\n};\n\ntemplate <typename FieldsAreMassiveTag>\nvoid test_restrict_fields(const Mesh<1>& fine_mesh, const Mesh<1>& coarse_mesh,\n                          const DataVector& fine_data_left,\n                          const DataVector& fine_data_right,\n                          const DataVector& expected_coarse_data,\n                          const bool fields_are_massive = false) {\n  constexpr size_t Dim = 1;\n  using metavariables = Metavariables<Dim, FieldsAreMassiveTag>;\n  using element_array = typename metavariables::element_array;\n\n  const auto global_cache = [&fields_are_massive]() {\n    if constexpr (std::is_same_v<FieldsAreMassiveTag, void>) {\n      (void)fields_are_massive;\n      return tuples::TaggedTuple<logging::Tags::Verbosity<DummyOptionsGroup>>{\n          Verbosity::Verbose};\n    } else {\n      return tuples::TaggedTuple<logging::Tags::Verbosity<DummyOptionsGroup>,\n                                 FieldsAreMassiveTag>{Verbosity::Verbose,\n                                                      fields_are_massive};\n    }\n  }();\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{\n      std::move(global_cache)};\n\n  // Setup element array\n  const auto add_element =\n      [&runner](const ElementId<Dim>& element_id,\n                const std::optional<ElementId<Dim>>& parent_id,\n                const std::unordered_set<ElementId<Dim>>& child_ids,\n                const Mesh<Dim>& mesh,\n                const std::optional<Mesh<Dim>>& parent_mesh,\n                const std::optional<DataVector>& data) {\n        typename fields_tag::type fields{};\n        if (data.has_value()) {\n          fields.initialize(mesh.number_of_grid_points());\n          get(get<ScalarFieldTag<0>>(fields)) = *data;\n          get(get<ScalarFieldTag<1>>(fields)) = *data;\n        }\n        ActionTesting::emplace_component_and_initialize<element_array>(\n            make_not_null(&runner), element_id,\n            {parent_id, child_ids, mesh, parent_mesh, size_t{0},\n             std::move(fields)});\n      };\n  const ElementId<Dim> left_element_id{0, {{{1, 0}}}, 0};\n  const ElementId<Dim> right_element_id{0, {{{1, 1}}}, 0};\n  const ElementId<Dim> coarse_element_id{0, {{{0, 0}}}, 1};\n  add_element(left_element_id, coarse_element_id, {}, fine_mesh, coarse_mesh,\n              fine_data_left);\n  add_element(right_element_id, coarse_element_id, {}, fine_mesh, coarse_mesh,\n              fine_data_right);\n  add_element(coarse_element_id, std::nullopt,\n              {left_element_id, right_element_id}, coarse_mesh, std::nullopt,\n              std::nullopt);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Skip over sending on coarse element\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            coarse_element_id);\n  REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n      make_not_null(&runner), coarse_element_id));\n  // Send from left element\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            left_element_id);\n  REQUIRE(ActionTesting::next_action_if_ready<element_array>(\n      make_not_null(&runner), left_element_id));\n  REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n      make_not_null(&runner), coarse_element_id));\n  // Send from right element\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            right_element_id);\n  REQUIRE(ActionTesting::next_action_if_ready<element_array>(\n      make_not_null(&runner), right_element_id));\n  // Receive on coarse element\n  REQUIRE(ActionTesting::next_action_if_ready<element_array>(\n      make_not_null(&runner), coarse_element_id));\n  const auto& coarse_data =\n      ActionTesting::get_databox_tag<element_array, fields_tag>(\n          runner, coarse_element_id);\n  CHECK_ITERABLE_APPROX(get(get<ScalarFieldTag<0>>(coarse_data)),\n                        expected_coarse_data);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelMultigrid.Action.RestrictFields\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  const Mesh<1> fine_mesh{4, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n  const Mesh<1> coarse_mesh{3, Spectral::Basis::Legendre,\n                            Spectral::Quadrature::GaussLobatto};\n  const DataVector fine_data_left{0., 1., 2., 3.};\n  const DataVector fine_data_right{4., 5., 6., 7.};\n  {\n    const DataVector coarse_data =\n        apply_matrices(\n            make_array<1>(Spectral::projection_matrix_child_to_parent(\n                fine_mesh, coarse_mesh, Spectral::SegmentSize::LowerHalf)),\n            fine_data_left, Index<1>{4}) +\n        apply_matrices(\n            make_array<1>(Spectral::projection_matrix_child_to_parent(\n                fine_mesh, coarse_mesh, Spectral::SegmentSize::UpperHalf)),\n            fine_data_right, Index<1>{4});\n    test_restrict_fields<void>(fine_mesh, coarse_mesh, fine_data_left,\n                               fine_data_right, coarse_data);\n    test_restrict_fields<MassiveTag>(fine_mesh, coarse_mesh, fine_data_left,\n                                     fine_data_right, coarse_data, false);\n  }\n  {\n    const DataVector coarse_data =\n        apply_matrices(\n            make_array<1>(Spectral::projection_matrix_child_to_parent(\n                fine_mesh, coarse_mesh, Spectral::SegmentSize::LowerHalf,\n                true)),\n            fine_data_left, Index<1>{4}) +\n        apply_matrices(\n            make_array<1>(Spectral::projection_matrix_child_to_parent(\n                fine_mesh, coarse_mesh, Spectral::SegmentSize::UpperHalf,\n                true)),\n            fine_data_right, Index<1>{4});\n    test_restrict_fields<MassiveTag>(fine_mesh, coarse_mesh, fine_data_left,\n                                     fine_data_right, coarse_data, true);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelMultigrid\")\n\nset(LIBRARY_SOURCES\n  Test_Hierarchy.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DomainStructure\n  LinearSolverHelpers\n  ParallelMultigrid\n  Utilities\n  )\n\nadd_standalone_test(\n  \"Integration.LinearSolver.MultigridAlgorithm\"\n  INPUT_FILE \"Test_MultigridAlgorithm.yaml\")\nadd_standalone_test(\n  \"Integration.LinearSolver.MultigridAlgorithmMassive\"\n  EXECUTABLE \"Test_MultigridAlgorithm\"\n  INPUT_FILE \"Test_MultigridAlgorithmMassive.yaml\")\ntarget_link_libraries(\n  \"Test_MultigridAlgorithm\"\n  PRIVATE\n  \"${DISTRIBUTED_INTEGRATION_TEST_LINK_LIBRARIES};ParallelMultigrid\")\n\nadd_standalone_test(\n  \"Integration.LinearSolver.MultigridPreconditionedGmresAlgorithm\"\n  INPUT_FILE \"Test_MultigridPreconditionedGmresAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_MultigridPreconditionedGmresAlgorithm\"\n  PRIVATE\n  \"${DISTRIBUTED_INTEGRATION_TEST_LINK_LIBRARIES};ParallelMultigrid;ParallelNonlinearSolver\")\n\nadd_subdirectory(Actions)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Test_Hierarchy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <ostream>\n#include <unordered_set>\n#include <vector>\n\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Hierarchy.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace LinearSolver::multigrid {\n\nSPECTRE_TEST_CASE(\"Unit.ParallelMultigrid.Hierarchy\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  {\n    INFO(\"Coarsening\");\n    CHECK(coarsen<1>({{{0}}, {{1}}, {{2}}}) ==\n          std::vector<std::array<size_t, 1>>{{{0}}, {{0}}, {{1}}});\n    CHECK(coarsen<2>({{{0, 1}}, {{1, 1}}, {{2, 0}}, {{3, 4}}}) ==\n          std::vector<std::array<size_t, 2>>{\n              {{0, 0}}, {{0, 0}}, {{1, 0}}, {{2, 3}}});\n    CHECK(coarsen<3>({{{0, 1, 2}}, {{1, 1, 1}}, {{2, 0, 1}}, {{3, 4, 5}}}) ==\n          std::vector<std::array<size_t, 3>>{\n              {{0, 0, 1}}, {{0, 0, 0}}, {{1, 0, 0}}, {{2, 3, 4}}});\n  }\n  {\n    INFO(\"Parent ID\");\n    const size_t block_id = 3;\n    CHECK(parent_id<1>({block_id, {{{0, 0}}}, 1}) ==\n          ElementId<1>{block_id, {{{0, 0}}}, 0});\n    CHECK(parent_id<1>({block_id, {{{1, 0}}}, 1}) ==\n          ElementId<1>{block_id, {{{0, 0}}}, 0});\n    CHECK(parent_id<1>({block_id, {{{1, 1}}}, 1}) ==\n          ElementId<1>{block_id, {{{0, 0}}}, 0});\n    CHECK(parent_id<1>({block_id, {{{2, 2}}}, 3}) ==\n          ElementId<1>{block_id, {{{1, 1}}}, 2});\n    CHECK(parent_id<1>({block_id, {{{2, 3}}}, 3}) ==\n          ElementId<1>{block_id, {{{1, 1}}}, 2});\n    CHECK(parent_id<2>({block_id, {{{0, 0}, {1, 0}}}, 1}) ==\n          ElementId<2>{block_id, {{{0, 0}, {0, 0}}}, 0});\n    CHECK(parent_id<2>({block_id, {{{2, 2}, {1, 1}}}, 3}) ==\n          ElementId<2>{block_id, {{{1, 1}, {0, 0}}}, 2});\n    CHECK(parent_id<3>({block_id, {{{0, 0}, {1, 0}, {2, 1}}}, 3}) ==\n          ElementId<3>{block_id, {{{0, 0}, {0, 0}, {1, 0}}}, 2});\n  }\n  {\n    INFO(\"Child IDs\");\n    const size_t block_id = 3;\n    CHECK(child_ids<1>({block_id, {{{0, 0}}}, 0}, {{0}}) ==\n          std::unordered_set<ElementId<1>>{{block_id, {{{0, 0}}}, 1}});\n    CHECK(child_ids<1>({block_id, {{{0, 0}}}, 0}, {{1}}) ==\n          std::unordered_set<ElementId<1>>{{block_id, {{{1, 0}}}, 1},\n                                           {block_id, {{{1, 1}}}, 1}});\n    CHECK(child_ids<1>({block_id, {{{2, 2}}}, 1}, {{3}}) ==\n          std::unordered_set<ElementId<1>>{{block_id, {{{3, 4}}}, 2},\n                                           {block_id, {{{3, 5}}}, 2}});\n    CHECK(child_ids<2>({block_id, {{{0, 0}, {1, 0}}}, 0}, {{0, 1}}) ==\n          std::unordered_set<ElementId<2>>{{block_id, {{{0, 0}, {1, 0}}}, 1}});\n    CHECK(child_ids<2>({block_id, {{{0, 0}, {1, 0}}}, 1}, {{1, 1}}) ==\n          std::unordered_set<ElementId<2>>{{block_id, {{{1, 0}, {1, 0}}}, 2},\n                                           {block_id, {{{1, 1}, {1, 0}}}, 2}});\n    CHECK(child_ids<2>({block_id, {{{0, 0}, {1, 0}}}, 1}, {{1, 1}}) ==\n          std::unordered_set<ElementId<2>>{{block_id, {{{1, 0}, {1, 0}}}, 2},\n                                           {block_id, {{{1, 1}, {1, 0}}}, 2}});\n    CHECK(child_ids<2>({block_id, {{{0, 0}, {1, 0}}}, 1}, {{1, 2}}) ==\n          std::unordered_set<ElementId<2>>{{block_id, {{{1, 0}, {2, 0}}}, 2},\n                                           {block_id, {{{1, 1}, {2, 0}}}, 2},\n                                           {block_id, {{{1, 0}, {2, 1}}}, 2},\n                                           {block_id, {{{1, 1}, {2, 1}}}, 2}});\n    CHECK(\n        child_ids<3>({block_id, {{{0, 0}, {1, 0}, {2, 0}}}, 0}, {{0, 1, 2}}) ==\n        std::unordered_set<ElementId<3>>{\n            {block_id, {{{0, 0}, {1, 0}, {2, 0}}}, 1}});\n    CHECK(\n        child_ids<3>({block_id, {{{0, 0}, {1, 1}, {2, 0}}}, 1}, {{1, 2, 2}}) ==\n        std::unordered_set<ElementId<3>>{\n            {block_id, {{{1, 0}, {2, 2}, {2, 0}}}, 2},\n            {block_id, {{{1, 1}, {2, 2}, {2, 0}}}, 2},\n            {block_id, {{{1, 0}, {2, 3}, {2, 0}}}, 2},\n            {block_id, {{{1, 1}, {2, 3}, {2, 0}}}, 2}});\n    CHECK(\n        child_ids<3>({block_id, {{{0, 0}, {1, 1}, {2, 0}}}, 1}, {{1, 2, 3}}) ==\n        std::unordered_set<ElementId<3>>{\n            {block_id, {{{1, 0}, {2, 2}, {3, 0}}}, 2},\n            {block_id, {{{1, 1}, {2, 2}, {3, 0}}}, 2},\n            {block_id, {{{1, 0}, {2, 3}, {3, 0}}}, 2},\n            {block_id, {{{1, 1}, {2, 3}, {3, 0}}}, 2},\n            {block_id, {{{1, 0}, {2, 2}, {3, 1}}}, 2},\n            {block_id, {{{1, 1}, {2, 2}, {3, 1}}}, 2},\n            {block_id, {{{1, 0}, {2, 3}, {3, 1}}}, 2},\n            {block_id, {{{1, 1}, {2, 3}, {3, 1}}}, 2}});\n  }\n}\n\n}  // namespace LinearSolver::multigrid\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Test_MultigridAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/Multigrid/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/ElementsRegistration.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Variables.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Multigrid.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Richardson/Richardson.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\nnamespace helpers_mg = TestHelpers::LinearSolver::multigrid;\n\nnamespace {\n\nstruct MultigridSolver {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct RichardsonSmoother {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the Multigrid linear solver algorithm on multiple elements\"};\n\n  static constexpr size_t volume_dim = 1;\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithoutBoundaryConditions<\n          volume_dim>;\n\n  // [setup_smoother]\n  using multigrid = LinearSolver::multigrid::Multigrid<\n      Metavariables, volume_dim, helpers_mg::fields_tag, MultigridSolver,\n      helpers_mg::OperatorIsMassive, helpers_mg::sources_tag>;\n\n  using smoother = LinearSolver::Richardson::Richardson<\n      typename multigrid::smooth_fields_tag, RichardsonSmoother,\n      typename multigrid::smooth_source_tag, ::amr::Tags::GridIndex>;\n  // [setup_smoother]\n\n  using bottom_solver_actions = tmpl::list<\n      helpers_mg::PrepareDirectSolve<typename multigrid::smooth_fields_tag,\n                                     typename multigrid::smooth_source_tag>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<1>, tmpl::list<domain::creators::Interval>>,\n        tmpl::pair<::amr::Criterion, tmpl::list<>>>;\n  };\n\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  using initialization_actions = tmpl::list<\n      helpers_mg::InitializeElement, typename multigrid::initialize_element,\n      typename smoother::initialize_element,\n      Initialization::Actions::InitializeItems<\n          ::amr::Initialization::Initialize<volume_dim, Metavariables>>,\n      Parallel::Actions::TerminatePhase>;\n\n  using register_actions = tmpl::list<\n      typename multigrid::register_element, typename smoother::register_element,\n      ::amr::Actions::RegisterElement, Parallel::Actions::TerminatePhase>;\n\n  template <typename OperandTag>\n  using compute_operator_action = helpers_mg::ComputeOperatorAction<\n      OperandTag,\n      db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, OperandTag>>;\n\n  // [action_list]\n  template <typename Label>\n  using smooth_actions = typename smoother::template solve<\n      compute_operator_action<typename smoother::operand_tag>, Label>;\n\n  using solve_actions =\n      tmpl::list<compute_operator_action<typename multigrid::fields_tag>,\n                 typename multigrid::template solve<\n                     compute_operator_action<typename smoother::fields_tag>,\n                     smooth_actions<LinearSolver::multigrid::VcycleDownLabel>,\n                     smooth_actions<LinearSolver::multigrid::VcycleUpLabel>,\n                     bottom_solver_actions>,\n                 Parallel::Actions::TerminatePhase>;\n  // [action_list]\n\n  using test_actions =\n      tmpl::list<helpers_mg::TestResult<typename multigrid::options_group>>;\n\n  using dg_element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<Parallel::Phase::Register, register_actions>,\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Solve, solve_actions>,\n          Parallel::PhaseActions<Parallel::Phase::Testing, test_actions>>,\n      LinearSolver::multigrid::ElementsAllocator<1, MultigridSolver>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = tmpl::flatten<tmpl::list<\n        helpers_mg::InitializeElement, typename multigrid::amr_projectors,\n        typename smoother::amr_projectors,\n        ::amr::projectors::DefaultInitialize<\n            tmpl::list<domain::Tags::InitialExtents<volume_dim>,\n                       domain::Tags::InitialRefinementLevels<volume_dim>>>,\n        ::amr::projectors::ProjectVariables<volume_dim, helpers_mg::fields_tag,\n                                            helpers_mg::sources_tag>>>;\n    static constexpr bool keep_coarse_grids = true;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  using component_list = tmpl::flatten<tmpl::list<\n      typename multigrid::component_list, typename smoother::component_list,\n      ::amr::Component<Metavariables>, dg_element_array,\n      observers::Observer<Metavariables>,\n      observers::ObserverWriter<Metavariables>,\n      helpers::OutputCleaner<Metavariables>>>;\n  using observed_reduction_data_tags =\n      observers::collect_reduction_data_tags<tmpl::list<multigrid, smoother>>;\n\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Test_MultigridAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  The test problem being solved here is a DG-discretized 1D Poisson equation\n  -u''(x) = f(x) on the interval [0, pi] with source f(x)=sin(x) and homogeneous\n  Dirichlet boundary conditions such that the solution is u(x)=sin(x) as well.\n\n  Details:\n  - Domain decomposition: 2 elements with 3 LGL grid-points each on the finest\n    mesh\n  - DG scheme: Strong compact flux formulation (no auxiliary variables)\n  - \"Massless\": Multiplied by inverse mass matrix with mass-lumping. Note that\n    whether or not the operator is DG-massive is relevant for the multigrid\n    restriction operation.\n  - Internal penalty flux with sigma = 1.5 * N_points^2 / h\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[[7.148074522300963, 0.8105694691387022, -1.0132118364233778],\n      [0.20264236728467566, 0.8105694691387024, 0.20264236728467566],\n      [-1.0132118364233778, 0.8105694691387022, 7.148074522300963]]]\n  - [[[17.133142186587385, 3.242277876554809, -2.8369931419854577],\n      [0.8105694691387026, 3.2422778765548097, -0.405284734569351],\n      [-2.8369931419854577, -1.6211389382774053, 11.403564235279148],\n      [1.2158542037080533, -4.863416814832214, -5.729577951308233],\n      [0.0, 0.0, -1.2158542037080537],\n      [0.0, 0.0, 1.2158542037080533]],\n     [[1.2158542037080533, 0.0, 0.0],\n      [-1.2158542037080537, 0.0, 0.0],\n      [-5.729577951308233, -4.863416814832214, 1.2158542037080533],\n      [11.403564235279148, -1.6211389382774053, -2.8369931419854577],\n      [-0.405284734569351, 3.2422778765548097, 0.8105694691387026],\n      [-2.836993141985458, 3.242277876554809, 17.133142186587385]]]\n\nSource:\n  - [0.0, 0.7071067811865475, 1.0]\n  - [1.0, 0.7071067811865475, 0.0]\n\nExpectedResult:\n  - [-0.04332079221988435, 0.7253224709680011, 0.9928055333486303]\n  - [0.9928055333486303, 0.7253224709680011, -0.04332079221988417]\n\nOperatorIsMassive: False\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_MultigridAlgorithm_Volume\"\n  ReductionFileName: \"Test_MultigridAlgorithm_Reductions\"\n\nMultigridSolver:\n  Iterations: 7\n  Verbosity: Verbose\n  InitialCoarseLevels: Auto\n  PreSmoothing: True\n  UseBottomSolver: True\n  PostSmoothingAtBottom: False\n  OutputVolumeData: True\n\nRichardsonSmoother:\n  Iterations: 20\n  RelaxationParameter: 0.09020991440370969  # 2. / (max_eigval + min_eigval)\n  Verbosity: Silent\n\nAmr:\n  Verbosity: Verbose\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: False\n  MaxCoarseLevels: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Test_MultigridAlgorithmMassive.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  This test problem is the same as detailed in `Test_MultigridAlgorithm.yaml`,\n  but the operator includes the mass matrix (with mass-lumping). This is\n  relevant for the multigrid restriction operation.\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[[3.7427230677621783, 0.42441318157838753, -0.5305164769729844],\n      [0.42441318157838775, 1.6976527263135506, 0.42441318157838775],\n      [-0.5305164769729844, 0.42441318157838753, 3.7427230677621783]]]\n  - [[[4.485446135524357, 0.8488263631567751, -0.7427230677621782],\n      [0.8488263631567755, 3.395305452627101, -0.4244131815783874],\n      [-0.7427230677621782, -0.4244131815783877, 2.9854461355243567],\n      [0.31830988618379064, -1.2732395447351628, -1.5],\n      [0.0, 0.0, -1.273239544735163],\n      [0.0, 0.0, 0.31830988618379064]],\n     [[0.31830988618379064, 0.0, 0.0],\n      [-1.273239544735163, 0.0, 0.0],\n      [-1.5, -1.2732395447351628, 0.31830988618379064],\n      [2.9854461355243567, -0.4244131815783877, -0.7427230677621782],\n      [-0.4244131815783874, 3.395305452627101, 0.8488263631567755],\n      [-0.7427230677621783, 0.8488263631567751, 4.485446135524357]]]\n\nSource:\n  - [0.0, 0.7404804896930609, 0.2617993877991494]\n  - [0.26179938779914935, 0.7404804896930614, 0.0]\n\nExpectedResult:\n  - [-0.04332079221988435, 0.7253224709680011, 0.9928055333486303]\n  - [0.9928055333486303, 0.7253224709680011, -0.04332079221988417]\n\nOperatorIsMassive: True\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_MultigridAlgorithmMassive_Volume\"\n  ReductionFileName: \"Test_MultigridAlgorithmMassive_Reductions\"\n\nMultigridSolver:\n  Iterations: 4\n  Verbosity: Verbose\n  InitialCoarseLevels: Auto\n  PreSmoothing: True\n  UseBottomSolver: False\n  PostSmoothingAtBottom: False\n  OutputVolumeData: True\n\nRichardsonSmoother:\n  Iterations: 20\n  RelaxationParameter: 0.33123384064827677  # 2. / (max_eigval + min_eigval)\n  Verbosity: Silent\n\nAmr:\n  Verbosity: Verbose\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: False\n  MaxCoarseLevels: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Test_MultigridPreconditionedGmresAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/Multigrid/Helpers.hpp\"\n#include \"IO/Observer/ObserverComponent.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"ParallelAlgorithms/Actions/Goto.hpp\"\n#include \"ParallelAlgorithms/Actions/InitializeItems.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Component.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/ElementsRegistration.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/Initialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Actions/SendAmrDiagnostics.hpp\"\n#include \"ParallelAlgorithms/Amr/Criteria/Criterion.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/DefaultInitialize.hpp\"\n#include \"ParallelAlgorithms/Amr/Projectors/Variables.hpp\"\n#include \"ParallelAlgorithms/Amr/Protocols/AmrMetavariables.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Actions/MakeIdentityIfSkipped.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/ElementsAllocator.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Multigrid.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Richardson/Richardson.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/NewtonRaphson.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\nnamespace helpers_mg = TestHelpers::LinearSolver::multigrid;\n\nnamespace {\n\nstruct NewtonRaphsonSolver {\n  static constexpr Options::String help =\n      \"Options for the iterative non-linear solver\";\n};\n\nstruct KrylovSolver {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct MultigridSolver {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct RichardsonSmoother {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the Multigrid solver used as a preconditioner for a \"\n      \"Krylov-subspace solver\"};\n\n  static constexpr size_t volume_dim = 1;\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithoutBoundaryConditions<\n          volume_dim>;\n\n  // The test problem is a linear operator, but we add a Newton-Raphson\n  // correction scheme to test it in a multigrid context as well.\n  using nonlinear_solver = NonlinearSolver::newton_raphson::NewtonRaphson<\n      Metavariables, helpers_mg::fields_tag, NewtonRaphsonSolver,\n      helpers_mg::sources_tag, ::amr::Tags::IsFinestGrid>;\n  using linear_solver = LinearSolver::gmres::Gmres<\n      Metavariables, typename nonlinear_solver::linear_solver_fields_tag,\n      KrylovSolver, true, typename nonlinear_solver::linear_solver_source_tag,\n      ::amr::Tags::IsFinestGrid>;\n  using multigrid = LinearSolver::multigrid::Multigrid<\n      Metavariables, volume_dim, typename linear_solver::operand_tag,\n      MultigridSolver, helpers_mg::OperatorIsMassive,\n      typename linear_solver::preconditioner_source_tag>;\n  using smoother = LinearSolver::Richardson::Richardson<\n      typename multigrid::smooth_fields_tag, RichardsonSmoother,\n      typename multigrid::smooth_source_tag, ::amr::Tags::GridIndex>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<1>, tmpl::list<domain::creators::Interval>>,\n        tmpl::pair<::amr::Criterion, tmpl::list<>>>;\n  };\n\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  using initialization_actions = tmpl::list<\n      helpers_mg::InitializeElement,\n      typename nonlinear_solver::initialize_element,\n      typename linear_solver::initialize_element,\n      typename multigrid::initialize_element,\n      typename smoother::initialize_element,\n      Initialization::Actions::InitializeItems<\n          ::amr::Initialization::Initialize<volume_dim, Metavariables>>,\n      Parallel::Actions::TerminatePhase>;\n\n  using register_actions = tmpl::list<\n      typename nonlinear_solver::register_element,\n      typename linear_solver::register_element,\n      typename multigrid::register_element, typename smoother::register_element,\n      ::amr::Actions::RegisterElement, Parallel::Actions::TerminatePhase>;\n\n  template <typename OperandTag, bool Linear>\n  using compute_operator_action = helpers_mg::ComputeOperatorAction<\n      OperandTag,\n      tmpl::conditional_t<\n          Linear,\n          db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, OperandTag>,\n          db::add_tag_prefix<NonlinearSolver::Tags::OperatorAppliedTo,\n                             OperandTag>>>;\n\n  template <typename Label>\n  using smooth_actions = typename smoother::template solve<\n      compute_operator_action<typename smoother::operand_tag, true>, Label>;\n\n  using solve_actions = tmpl::list<\n      typename nonlinear_solver::template solve<\n          compute_operator_action<typename nonlinear_solver::fields_tag, false>,\n          typename linear_solver::template solve<tmpl::list<\n              typename multigrid::template solve<\n                  compute_operator_action<typename smoother::fields_tag, true>,\n                  smooth_actions<LinearSolver::multigrid::VcycleDownLabel>,\n                  smooth_actions<LinearSolver::multigrid::VcycleUpLabel>>,\n              LinearSolver::Actions::make_identity_if_skipped<\n                  multigrid, compute_operator_action<\n                                 typename linear_solver::operand_tag, true>>>>>,\n      Parallel::Actions::TerminatePhase>;\n\n  using test_actions =\n      tmpl::list<helpers_mg::TestResult<typename multigrid::options_group>>;\n\n  using dg_element_array = elliptic::DgElementArray<\n      Metavariables,\n      tmpl::list<\n          Parallel::PhaseActions<Parallel::Phase::Initialization,\n                                 initialization_actions>,\n          Parallel::PhaseActions<Parallel::Phase::Register, register_actions>,\n          Parallel::PhaseActions<Parallel::Phase::CheckDomain,\n                                 tmpl::list<::amr::Actions::SendAmrDiagnostics,\n                                            Parallel::Actions::TerminatePhase>>,\n          Parallel::PhaseActions<Parallel::Phase::Solve, solve_actions>,\n          Parallel::PhaseActions<Parallel::Phase::Testing, test_actions>>,\n      LinearSolver::multigrid::ElementsAllocator<1, MultigridSolver>>;\n\n  struct amr : tt::ConformsTo<::amr::protocols::AmrMetavariables> {\n    using element_array = dg_element_array;\n    using projectors = tmpl::flatten<tmpl::list<\n        helpers_mg::InitializeElement,\n        typename nonlinear_solver::amr_projectors,\n        typename linear_solver::amr_projectors,\n        typename multigrid::amr_projectors, typename smoother::amr_projectors,\n        ::amr::projectors::DefaultInitialize<\n            tmpl::list<domain::Tags::InitialExtents<volume_dim>,\n                       domain::Tags::InitialRefinementLevels<volume_dim>>>,\n        ::amr::projectors::ProjectVariables<volume_dim, helpers_mg::fields_tag,\n                                            helpers_mg::sources_tag>>>;\n    static constexpr bool keep_coarse_grids = true;\n    static constexpr bool p_refine_only_in_event = false;\n  };\n\n  using component_list = tmpl::flatten<tmpl::list<\n      typename nonlinear_solver::component_list,\n      typename linear_solver::component_list,\n      typename multigrid::component_list, typename smoother::component_list,\n      ::amr::Component<Metavariables>, dg_element_array,\n      observers::Observer<Metavariables>,\n      observers::ObserverWriter<Metavariables>,\n      helpers::OutputCleaner<Metavariables>>>;\n  using observed_reduction_data_tags = observers::collect_reduction_data_tags<\n      tmpl::list<nonlinear_solver, linear_solver, multigrid, smoother>>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Test_MultigridPreconditionedGmresAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  This test problem is the same as detailed in\n  `Test_MultigridAlgorithmMassive.yaml`, but the multigrid solver is used to\n  precondition a GMRES solver.\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[[3.7427230677621783, 0.42441318157838753, -0.5305164769729844],\n      [0.42441318157838775, 1.6976527263135506, 0.42441318157838775],\n      [-0.5305164769729844, 0.42441318157838753, 3.7427230677621783]]]\n  - [[[4.485446135524357, 0.8488263631567751, -0.7427230677621782],\n      [0.8488263631567755, 3.395305452627101, -0.4244131815783874],\n      [-0.7427230677621782, -0.4244131815783877, 2.9854461355243567],\n      [0.31830988618379064, -1.2732395447351628, -1.5],\n      [0.0, 0.0, -1.273239544735163],\n      [0.0, 0.0, 0.31830988618379064]],\n     [[0.31830988618379064, 0.0, 0.0],\n      [-1.273239544735163, 0.0, 0.0],\n      [-1.5, -1.2732395447351628, 0.31830988618379064],\n      [2.9854461355243567, -0.4244131815783877, -0.7427230677621782],\n      [-0.4244131815783874, 3.395305452627101, 0.8488263631567755],\n      [-0.7427230677621783, 0.8488263631567751, 4.485446135524357]]]\n\nSource:\n  - [0.0, 0.7404804896930609, 0.2617993877991494]\n  - [0.26179938779914935, 0.7404804896930614, 0.0]\n\nExpectedResult:\n  - [-0.04332079221988435, 0.7253224709680011, 0.9928055333486303]\n  - [0.9928055333486303, 0.7253224709680011, -0.04332079221988417]\n\nOperatorIsMassive: True\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_MultigridPreconditionedGmresAlgorithm_Volume\"\n  ReductionFileName: \"Test_MultigridPreconditionedGmresAlgorithm_Reductions\"\n\nNewtonRaphsonSolver:\n  ConvergenceCriteria:\n    MaxIterations: 2\n    AbsoluteResidual: 1.e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n  SufficientDecrease: 1.e-4\n  MaxGlobalizationSteps: 40\n  DampingFactor: 1\n\nKrylovSolver:\n  ConvergenceCriteria:\n    MaxIterations: 1\n    AbsoluteResidual: 1.e-14\n    RelativeResidual: 1.e-8\n  Verbosity: Verbose\n\nMultigridSolver:\n  Iterations: 2\n  Verbosity: Verbose\n  InitialCoarseLevels: Auto\n  PreSmoothing: True\n  PostSmoothingAtBottom: False\n  OutputVolumeData: True\n\nRichardsonSmoother:\n  Iterations: 20\n  RelaxationParameter: 0.33123384064827677\n  Verbosity: Silent\n\nAmr:\n  Verbosity: Verbose\n  Criteria: []\n  EnableInBlocks: All\n  Policies:\n    EnforceTwoToOneBalanceInNormalDirection: true\n    Isotropy: Anisotropic\n    Limits:\n      NumGridPoints: Auto\n      RefinementLevel: Auto\n      ErrorBeyondLimits: False\n    AllowCoarsening: False\n  MaxCoarseLevels: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Multigrid/Tags.hpp\"\n\nnamespace LinearSolver::multigrid {\n\nnamespace {\nstruct Tag : db::SimpleTag {\n  using type = int;\n};\nstruct TestSolver {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.LinearSolver.Multigrid.Tags\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  TestHelpers::db::test_simple_tag<Tags::ChildrenRefinementLevels<1>>(\n      \"ChildrenRefinementLevels\");\n  TestHelpers::db::test_simple_tag<Tags::ParentRefinementLevels<1>>(\n      \"ParentRefinementLevels\");\n  TestHelpers::db::test_simple_tag<Tags::InitialCoarseLevels<TestSolver>>(\n      \"InitialCoarseLevels(TestSolver)\");\n  TestHelpers::db::test_prefix_tag<Tags::PreSmoothingInitial<Tag>>(\n      \"PreSmoothingInitial(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::PreSmoothingSource<Tag>>(\n      \"PreSmoothingSource(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::PreSmoothingResult<Tag>>(\n      \"PreSmoothingResult(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::PreSmoothingResidual<Tag>>(\n      \"PreSmoothingResidual(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::PostSmoothingInitial<Tag>>(\n      \"PostSmoothingInitial(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::PostSmoothingSource<Tag>>(\n      \"PostSmoothingSource(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::PostSmoothingResult<Tag>>(\n      \"PostSmoothingResult(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::PostSmoothingResidual<Tag>>(\n      \"PostSmoothingResidual(Tag)\");\n  TestHelpers::db::test_simple_tag<Tags::VolumeDataForOutput<\n      TestSolver, ::Tags::Variables<tmpl::list<::Tags::TempScalar<0>>>>>(\n      \"VolumeDataForOutput(TestSolver)\");\n}\n\n}  // namespace LinearSolver::multigrid\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Richardson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelRichardson\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ParallelLinearSolver\n  )\n\nadd_standalone_test(\n  \"Integration.LinearSolver.RichardsonAlgorithm\"\n  INPUT_FILE \"Test_RichardsonAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_RichardsonAlgorithm\"\n  PRIVATE\n  \"${INTEGRATION_TEST_LINK_LIBRARIES}\")\nadd_standalone_test(\n  \"Integration.LinearSolver.DistributedRichardsonAlgorithm\"\n  INPUT_FILE \"Test_DistributedRichardsonAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_DistributedRichardsonAlgorithm\"\n  PRIVATE\n  \"${DISTRIBUTED_INTEGRATION_TEST_LINK_LIBRARIES}\")\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Richardson/Test_DistributedRichardsonAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/Richardson/Richardson.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\nnamespace helpers_distributed = DistributedLinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct ParallelRichardson {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the Richardson linear solver algorithm on multiple elements\"};\n  static constexpr size_t volume_dim = 1;\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithoutBoundaryConditions<\n          volume_dim>;\n\n  using linear_solver = LinearSolver::Richardson::Richardson<\n      typename helpers_distributed::fields_tag, ParallelRichardson>;\n  using preconditioner = void;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<1>, tmpl::list<domain::creators::Interval>>>;\n  };\n\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n  using component_list = helpers_distributed::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &TestHelpers::domain::BoundaryConditions::register_derived_with_charm},\n      {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Richardson/Test_DistributedRichardsonAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  The test problem being solved here is a DG-discretized 1D Poisson equation\n  -u''(x) = f(x) on the interval [0, pi] with source f(x)=sin(x) and homogeneous\n  Dirichlet boundary conditions such that the solution is u(x)=sin(x) as well.\n\n  Details:\n  - Domain decomposition: 2 elements with 3 LGL grid-points each\n  - \"Primal\" DG formulation (no auxiliary variable)\n  - Multiplied by mass matrix and no mass-lumping\n  - Internal penalty flux with sigma = 1.5 * (N_points - 1)^2 / h\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[ 5.305164769729845,  0.848826363156775, -0.742723067762178],\n      [ 0.848826363156775,  3.395305452627101, -0.424413181578388],\n      [-0.742723067762178, -0.424413181578388,  3.395305452627101],\n      [ 0.318309886183791, -1.273239544735163, -1.909859317102744],\n      [ 0.               ,  0.               , -1.273239544735163],\n      [ 0.               ,  0.               ,  0.318309886183791]]\n  - [[ 0.318309886183791,  0.               ,  0.               ],\n      [-1.273239544735163,  0.               ,  0.               ],\n      [-1.909859317102744, -1.273239544735163,  0.318309886183791],\n      [ 3.395305452627101, -0.424413181578388, -0.742723067762178],\n      [-0.424413181578388,  3.395305452627101,  0.848826363156775],\n      [-0.742723067762178,  0.848826363156775,  5.305164769729845]]\n\nSource:\n  - [0.                , 0.740480489693061, 0.2617993877991494]\n  - [0.2617993877991494, 0.740480489693061, 0.                ]\n\nExpectedResult:\n  - [-0.0363482510397858,  0.7235793356729757,  0.9928055333486293]\n  - [ 0.9928055333486292,  0.7235793356729758, -0.0363482510397858]\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_DistributedRichardsonAlgorithm_Volume\"\n  ReductionFileName: \"Test_DistributedRichardsonAlgorithm_Reductions\"\n\nParallelRichardson:\n  Iterations: 199\n  RelaxationParameter: 0.2916330767929102\n  Verbosity: Verbose\n\nConvergenceReason: NumIterations\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Richardson/Test_RichardsonAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/Richardson/Richardson.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct SerialRichardson {\n  static constexpr Options::String help =\n      \"Options for the iterative linear solver\";\n};\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the Richardson linear solver algorithm\"};\n\n  using linear_solver =\n      LinearSolver::Richardson::Richardson<helpers::fields_tag<double>,\n                                           SerialRichardson>;\n  using preconditioner = void;\n\n  using component_list = helpers::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Richardson/Test_RichardsonAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\nLinearOperator: [[4, 1], [1, 3]]\nSource: [1, 2]\nInitialGuess: [2, 1]\nExpectedResult: [0.0909090909090909, 0.6363636363636364]\n\nObservers:\n  VolumeFileName: \"Test_RichardsonAlgorithm_Volume\"\n  ReductionFileName: \"Test_RichardsonAlgorithm_Reductions\"\n\nSerialRichardson:\n  Iterations: 29\n  RelaxationParameter: 0.2857142857142857\n  Verbosity: Verbose\n\nConvergenceReason: NumIterations\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Richardson/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Richardson/Tags.hpp\"\n\nnamespace {\nstruct TestSolver {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelLinearSolver.Richardson.Tags\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  TestHelpers::db::test_simple_tag<\n      LinearSolver::Richardson::Tags::RelaxationParameter<TestSolver>>(\n      \"RelaxationParameter(TestSolver)\");\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelSchwarzActions\")\n\nset(LIBRARY_SOURCES\n  Test_CommunicateOverlapFields.cpp\n  Test_ResetSubdomainSolver.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Convergence\n  DataStructures\n  DomainStructure\n  Logging\n  ParallelSchwarz\n  Spectral\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Actions/Test_CommunicateOverlapFields.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"NumericalAlgorithms/Convergence/Tags.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/TerminatePhase.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Actions/CommunicateOverlapFields.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct DummyOptionsGroup {};\n\ntemplate <size_t N>\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nusing fields_tag =\n    ::Tags::Variables<tmpl::list<ScalarFieldTag<0>, ScalarFieldTag<1>>>;\n\ntemplate <size_t Dim, bool RestrictToOverlap, typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<Dim>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<tmpl::list<\n              domain::Tags::Element<Dim>, domain::Tags::Mesh<Dim>,\n              LinearSolver::Schwarz::Tags::IntrudingExtents<Dim,\n                                                            DummyOptionsGroup>,\n              Convergence::Tags::IterationId<DummyOptionsGroup>, fields_tag,\n              LinearSolver::Schwarz::Tags::Overlaps<fields_tag, Dim,\n                                                    DummyOptionsGroup>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<\n              LinearSolver::Schwarz::Actions::SendOverlapFields<\n                  tmpl::list<fields_tag>, DummyOptionsGroup, RestrictToOverlap>,\n              LinearSolver::Schwarz::Actions::ReceiveOverlapFields<\n                  Dim, tmpl::list<fields_tag>, DummyOptionsGroup,\n                  RestrictToOverlap>,\n              Parallel::Actions::TerminatePhase>>>;\n};\n\ntemplate <size_t Dim, bool RestrictToOverlap>\nstruct Metavariables {\n  using element_array = ElementArray<Dim, RestrictToOverlap, Metavariables>;\n  using component_list = tmpl::list<element_array>;\n};\n\ntemplate <size_t Dim>\nElementId<Dim> make_element_id_2() {\n  const SegmentId root_segment{0, 0};\n  if constexpr (Dim == 1) {\n    return ElementId<1>{1, {{root_segment}}};\n  } else if constexpr (Dim == 2) {\n    return ElementId<2>{\n        1, {{root_segment, root_segment.id_of_child(Side::Lower)}}};\n  } else {\n    return ElementId<3>{\n        1,\n        {{root_segment, root_segment.id_of_child(Side::Lower), root_segment}}};\n  }\n}\n\ntemplate <size_t Dim>\nElementId<Dim> make_element_id_3() {\n  const SegmentId root_segment{0, 0};\n  if constexpr (Dim == 1) {\n    return ElementId<1>{2, {{root_segment}}};\n  } else if constexpr (Dim == 2) {\n    return ElementId<2>{\n        1, {{root_segment, root_segment.id_of_child(Side::Upper)}}};\n  } else {\n    return ElementId<3>{\n        1,\n        {{root_segment, root_segment.id_of_child(Side::Upper), root_segment}}};\n  }\n}\n\ntemplate <size_t Dim, bool RestrictToOverlap>\nvoid test_communicate_overlap_fields(const size_t num_points_per_dim,\n                                     const size_t overlap,\n                                     const DataVector& overlap_data,\n                                     const DataVector& expected_overlap_data) {\n  CAPTURE(Dim);\n  CAPTURE(RestrictToOverlap);\n  CAPTURE(overlap);\n\n  using metavariables = Metavariables<Dim, RestrictToOverlap>;\n  using element_array = typename metavariables::element_array;\n\n  ActionTesting::MockRuntimeSystem<metavariables> runner{tuples::TaggedTuple<\n      LinearSolver::Schwarz::Tags::MaxOverlap<DummyOptionsGroup>,\n      logging::Tags::Verbosity<DummyOptionsGroup>>{overlap,\n                                                   Verbosity::Verbose}};\n\n  // Setup element array\n  const auto add_element = [num_points_per_dim, overlap, &overlap_data,\n                            &runner](const Element<Dim>& element) {\n    Mesh<Dim> mesh{num_points_per_dim, Spectral::Basis::Legendre,\n                   Spectral::Quadrature::GaussLobatto};\n    auto intruding_extents = make_array<Dim>(overlap);\n    typename fields_tag::type fields{mesh.number_of_grid_points()};\n    get(get<ScalarFieldTag<0>>(fields)) = overlap_data;\n    get(get<ScalarFieldTag<1>>(fields)) = overlap_data;\n    ActionTesting::emplace_component_and_initialize<element_array>(\n        make_not_null(&runner), element.id(),\n        {element, std::move(mesh), std::move(intruding_extents), size_t{0},\n         std::move(fields),\n         LinearSolver::Schwarz::OverlapMap<Dim, typename fields_tag::type>{}});\n  };\n  const ElementId<Dim> first_element_id{0};\n  const ElementId<Dim> second_element_id = make_element_id_2<Dim>();\n  const ElementId<Dim> third_element_id = make_element_id_3<Dim>();\n  // We can only have multiple face-neighbors in >1 dimensions\n  if constexpr (Dim > 1) {\n    add_element({first_element_id,\n                 {{Direction<Dim>::upper_xi(),\n                   {{second_element_id, third_element_id},\n                    OrientationMap<Dim>::create_aligned()}}}});\n    add_element(\n        {second_element_id,\n         {{Direction<Dim>::lower_xi(),\n           {{first_element_id}, OrientationMap<Dim>::create_aligned()}}}});\n    add_element(\n        {third_element_id,\n         {{Direction<Dim>::lower_xi(),\n           {{first_element_id}, OrientationMap<Dim>::create_aligned()}}}});\n  } else {\n    add_element(\n        {first_element_id,\n         {{Direction<Dim>::upper_xi(),\n           {{second_element_id}, OrientationMap<Dim>::create_aligned()}}}});\n    add_element(\n        {second_element_id,\n         {{Direction<Dim>::lower_xi(),\n           {{first_element_id}, OrientationMap<Dim>::create_aligned()}}}});\n  }\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // Send from first element\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            first_element_id);\n  REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n      make_not_null(&runner), first_element_id));\n  // Send from second element\n  ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                            second_element_id);\n  // Send from third element\n  if constexpr (Dim > 1) {\n    REQUIRE_FALSE(ActionTesting::next_action_if_ready<element_array>(\n        make_not_null(&runner), first_element_id));\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              third_element_id);\n  }\n  // Receive on first element\n  {\n    ActionTesting::next_action<element_array>(make_not_null(&runner),\n                                              first_element_id);\n    const auto& all_overlap_fields =\n        ActionTesting::get_databox_tag<element_array,\n                                       LinearSolver::Schwarz::Tags::Overlaps<\n                                           fields_tag, Dim, DummyOptionsGroup>>(\n            runner, first_element_id);\n    CHECK(all_overlap_fields.size() == (Dim > 1 ? 2 : 1));\n    const auto& overlap_fields_on_second_element =\n        all_overlap_fields.at({Direction<Dim>::upper_xi(), second_element_id});\n    CHECK_ITERABLE_APPROX(\n        get(get<ScalarFieldTag<0>>(overlap_fields_on_second_element)),\n        expected_overlap_data);\n    CHECK_ITERABLE_APPROX(\n        get(get<ScalarFieldTag<1>>(overlap_fields_on_second_element)),\n        expected_overlap_data);\n    if constexpr (Dim > 1) {\n      const auto& overlap_fields_on_third_element =\n          all_overlap_fields.at({Direction<Dim>::upper_xi(), third_element_id});\n      CHECK_ITERABLE_APPROX(\n          get(get<ScalarFieldTag<0>>(overlap_fields_on_third_element)),\n          expected_overlap_data);\n      CHECK_ITERABLE_APPROX(\n          get(get<ScalarFieldTag<1>>(overlap_fields_on_third_element)),\n          expected_overlap_data);\n    }\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelSchwarz.Action.CommunicateOverlapFields\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  test_communicate_overlap_fields<1, false>(3, 2, {0., 1., 2.}, {0., 1., 2.});\n  test_communicate_overlap_fields<1, true>(3, 2, {0., 1., 2.}, {0., 1.});\n  test_communicate_overlap_fields<2, false>(\n      3, 2, {0., 1., 2., 3., 4., 5., 6., 7., 8.},\n      {0., 1., 2., 3., 4., 5., 6., 7., 8.});\n  test_communicate_overlap_fields<2, true>(\n      3, 2, {0., 1., 2., 3., 4., 5., 6., 7., 8.}, {0., 1., 3., 4., 6., 7.});\n  test_communicate_overlap_fields<3, false>(\n      2, 1, {0., 1., 2., 3., 4., 5., 6., 7.}, {0., 1., 2., 3., 4., 5., 6., 7.});\n  test_communicate_overlap_fields<3, true>(\n      2, 1, {0., 1., 2., 3., 4., 5., 6., 7.}, {0., 2., 4., 6.});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Actions/Test_ResetSubdomainSolver.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"IO/Logging/Tags.hpp\"\n#include \"IO/Logging/Verbosity.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Actions/ResetSubdomainSolver.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct DummyOptionsGroup {};\n\nstruct SubdomainSolver {\n  SubdomainSolver() = default;\n  explicit SubdomainSolver(const bool is_reset_in) : is_reset(is_reset_in) {}\n  void reset() { is_reset = true; }\n  bool is_reset = false;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | is_reset; }\n};\n\ntemplate <typename Metavariables>\nstruct ElementArray {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<\n          Parallel::Phase::Initialization,\n          tmpl::list<ActionTesting::InitializeDataBox<\n              tmpl::list<LinearSolver::Schwarz::Tags::SubdomainSolver<\n                  SubdomainSolver, DummyOptionsGroup>>>>>,\n      Parallel::PhaseActions<\n          Parallel::Phase::Testing,\n          tmpl::list<LinearSolver::Schwarz::Actions::ResetSubdomainSolver<\n              SubdomainSolver, DummyOptionsGroup>>>>;\n};\n\nstruct Metavariables {\n  using element_array = ElementArray<Metavariables>;\n  using component_list = tmpl::list<element_array>;\n};\n\nvoid test_reset_subdomain_solver(const bool skip_resets) {\n  CAPTURE(skip_resets);\n\n  using element_array = typename Metavariables::element_array;\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{tuples::TaggedTuple<\n      LinearSolver::Schwarz::Tags::SkipSubdomainSolverResets<DummyOptionsGroup>,\n      logging::Tags::Verbosity<DummyOptionsGroup>>{skip_resets,\n                                                   Verbosity::Verbose}};\n  const ElementId<1> element_id{0};\n  ActionTesting::emplace_component_and_initialize<element_array>(\n      make_not_null(&runner), element_id,\n      {std::make_unique<SubdomainSolver>()});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n  REQUIRE_FALSE(ActionTesting::get_databox_tag<\n                    element_array, LinearSolver::Schwarz::Tags::SubdomainSolver<\n                                       SubdomainSolver, DummyOptionsGroup>>(\n                    runner, element_id)\n                    .is_reset);\n  ActionTesting::next_action<element_array>(make_not_null(&runner), element_id);\n  CHECK(ActionTesting::get_databox_tag<\n            element_array, LinearSolver::Schwarz::Tags::SubdomainSolver<\n                               SubdomainSolver, DummyOptionsGroup>>(runner,\n                                                                    element_id)\n            .is_reset != skip_resets);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelSchwarz.Action.ResetSubdomainSolver\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver][Actions]\") {\n  test_reset_subdomain_solver(false);\n  test_reset_subdomain_solver(true);\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelSchwarz\")\n\nset(LIBRARY_SOURCES\n  Test_ComputeTags.cpp\n  Test_ElementCenteredSubdomainData.cpp\n  Test_OverlapHelpers.cpp\n  Test_Tags.cpp\n  Test_Weighting.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainStructure\n  LinearSolver\n  ParallelSchwarz\n  Spectral\n  )\n\nadd_standalone_test(\n  \"Integration.LinearSolver.SchwarzAlgorithm\"\n  INPUT_FILE \"Test_SchwarzAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_SchwarzAlgorithm\"\n  PRIVATE\n  \"${DISTRIBUTED_INTEGRATION_TEST_LINK_LIBRARIES};Domain;DomainStructure;ParallelSchwarz\")\n\nadd_subdirectory(Actions)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Test_ComputeTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/InterfaceComputeTags.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ComputeTags.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n\nnamespace {\nstruct DummyOptionsGroup {};\n}  // namespace\n\nnamespace LinearSolver::Schwarz {\n\nSPECTRE_TEST_CASE(\"Unit.ParallelSchwarz.ComputeTags\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  {\n    TestHelpers::db::test_compute_tag<\n        Tags::SummedIntrudingOverlapWeightsCompute<1, DummyOptionsGroup>>(\n        \"SummedIntrudingOverlapWeights(DummyOptionsGroup)\");\n    const auto box = db::create<\n        db::AddSimpleTags<\n            domain::Tags::Interface<domain::Tags::InternalDirections<1>,\n                                    Tags::Weight<DummyOptionsGroup>>,\n            domain::Tags::Mesh<1>,\n            Tags::IntrudingExtents<1, DummyOptionsGroup>>,\n        db::AddComputeTags<\n            Tags::SummedIntrudingOverlapWeightsCompute<1, DummyOptionsGroup>>>(\n        std::unordered_map<Direction<1>, Scalar<DataVector>>{\n            {Direction<1>::lower_xi(),\n             Scalar<DataVector>{DataVector{0.5, 0.1}}},\n            {Direction<1>::upper_xi(),\n             Scalar<DataVector>{DataVector{0.1, 0.5}}}},\n        Mesh<1>{3, Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto},\n        std::array<size_t, 1>{{2}});\n    const DataVector expected_weights{0.5, 0.2, 0.5};\n    CHECK_ITERABLE_APPROX(\n        get(get<Tags::SummedIntrudingOverlapWeights<DummyOptionsGroup>>(box)),\n        expected_weights);\n  }\n}\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Test_ElementCenteredSubdomainData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n#include <cstddef>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearSolver/InnerProduct.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\nusing namespace std::complex_literals;\n\nnamespace {\ntemplate <typename DataType>\nstruct ScalarField : db::SimpleTag {\n  using type = Scalar<DataType>;\n};\n}  // namespace\n\ntemplate <typename DataType>\nvoid test_subdomain_data() {\n  const DirectionalId<1> west_id{Direction<1>::lower_xi(),\n                                 ElementId<1>{0, {{{2, 0}}}}};\n  const DirectionalId<1> east_id{Direction<1>::upper_xi(),\n                                 ElementId<1>{0, {{{2, 2}}}}};\n  const auto make_subdomain_data = [&west_id, &east_id](\n                                       DataType element_data,\n                                       DataType east_overlap_data,\n                                       DataType west_overlap_data) {\n    ElementCenteredSubdomainData<1, tmpl::list<ScalarField<DataType>>>\n        subdomain_data{element_data.size()};\n    get(get<ScalarField<DataType>>(subdomain_data.element_data)) =\n        std::move(element_data);\n    subdomain_data.overlap_data.emplace(west_id, west_overlap_data.size());\n    get(get<ScalarField<DataType>>(subdomain_data.overlap_data.at(west_id))) =\n        std::move(west_overlap_data);\n    subdomain_data.overlap_data.emplace(east_id, east_overlap_data.size());\n    get(get<ScalarField<DataType>>(subdomain_data.overlap_data.at(east_id))) =\n        std::move(east_overlap_data);\n    return subdomain_data;\n  };\n  if constexpr (std::is_same_v<DataType, DataVector>) {\n    auto subdomain_data1 = make_subdomain_data({1., 2., 3.}, {4., 5.}, {6.});\n    const auto subdomain_data2 =\n        make_subdomain_data({2., 1., 0.}, {1., 2.}, {3.});\n    SECTION(\"Addition\") {\n      const auto subdomain_data_sum =\n          make_subdomain_data({3., 3., 3.}, {5., 7.}, {9.});\n      CHECK(subdomain_data1 + subdomain_data2 == subdomain_data_sum);\n      subdomain_data1 += subdomain_data2;\n      CHECK(subdomain_data1 == subdomain_data_sum);\n    }\n    SECTION(\"Subtraction\") {\n      const auto subdomain_data_diff =\n          make_subdomain_data({-1., 1., 3.}, {3., 3.}, {3.});\n      CHECK(subdomain_data1 - subdomain_data2 == subdomain_data_diff);\n      subdomain_data1 -= subdomain_data2;\n      CHECK(subdomain_data1 == subdomain_data_diff);\n    }\n    SECTION(\"Scalar multiplication\") {\n      const auto subdomain_data_double =\n          make_subdomain_data({2., 4., 6.}, {8., 10.}, {12.});\n      CHECK(2. * subdomain_data1 == subdomain_data_double);\n      CHECK(subdomain_data1 * 2. == subdomain_data_double);\n      subdomain_data1 *= 2.;\n      CHECK(subdomain_data1 == subdomain_data_double);\n    }\n    SECTION(\"Scalar division\") {\n      const auto subdomain_data_half =\n          make_subdomain_data({0.5, 1., 1.5}, {2., 2.5}, {3.});\n      CHECK(subdomain_data1 / 2. == subdomain_data_half);\n      subdomain_data1 /= 2.;\n      CHECK(subdomain_data1 == subdomain_data_half);\n    }\n    SECTION(\"Iterate raw data\") {\n      CAPTURE(subdomain_data1);\n      CAPTURE(subdomain_data2);\n      std::copy(subdomain_data2.begin(), subdomain_data2.end(),\n                subdomain_data1.begin());\n      CHECK(subdomain_data1 == subdomain_data2);\n    }\n    SECTION(\"Remaining tests\") {\n      test_serialization(subdomain_data1);\n      test_copy_semantics(subdomain_data1);\n      auto copied_subdomain_data = subdomain_data1;\n      test_move_semantics(std::move(copied_subdomain_data), subdomain_data1);\n      CHECK(inner_product(subdomain_data1, subdomain_data2) == 36.);\n      CHECK(make_with_value<ElementCenteredSubdomainData<\n                1, tmpl::list<ScalarField<DataType>>>>(subdomain_data1, 1.) ==\n            make_subdomain_data({1., 1., 1.}, {1., 1.}, {1.}));\n    }\n    SECTION(\"Resizing\") {\n      const DirectionalId<1> extra_id{Direction<1>::upper_xi(),\n                                      ElementId<1>{1, {{{2, 2}}}}};\n      subdomain_data1.overlap_data.erase(west_id);\n      subdomain_data1.overlap_data.emplace(extra_id, 3);\n      subdomain_data1.destructive_resize(subdomain_data2);\n      CHECK(subdomain_data1.overlap_data.size() == 2);\n      CHECK(subdomain_data1.overlap_data.count(extra_id) == 0);\n      CHECK(subdomain_data1.overlap_data.count(west_id) == 1);\n      CHECK(subdomain_data1.overlap_data.count(east_id) == 1);\n    }\n  } else {  // ComplexDataVector\n    auto subdomain_data1 = make_subdomain_data(\n        {1. + 2.i, 2. + 3.i, 3. + 4.i}, {4. + 5.i, 5. + 6.i}, {6. + 7.i});\n    const auto subdomain_data2 = make_subdomain_data(\n        {2. + 3.i, 1. + 2.i, 0. + 1.i}, {1. + 2.i, 2. + 3.i}, {3. + 4.i});\n    SECTION(\"Addition\") {\n      const auto subdomain_data_sum = make_subdomain_data(\n          {3. + 5.i, 3. + 5.i, 3. + 5.i}, {5. + 7.i, 7. + 9.i}, {9. + 11.i});\n      CHECK(subdomain_data1 + subdomain_data2 == subdomain_data_sum);\n      subdomain_data1 += subdomain_data2;\n      CHECK(subdomain_data1 == subdomain_data_sum);\n    }\n    SECTION(\"Subtraction\") {\n      const auto subdomain_data_diff = make_subdomain_data(\n          {-1. - 1.i, 1. + 1.i, 3. + 3.i}, {3. + 3.i, 3. + 3.i}, {3. + 3.i});\n      CHECK(subdomain_data1 - subdomain_data2 == subdomain_data_diff);\n      subdomain_data1 -= subdomain_data2;\n      CHECK(subdomain_data1 == subdomain_data_diff);\n    }\n    SECTION(\"Scalar multiplication\") {\n      const auto subdomain_data_double =\n          make_subdomain_data({2. + 4.i, 4. + 6.i, 6. + 8.i},\n                              {8. + 10.i, 10. + 12.i}, {12. + 14.i});\n      CHECK(2. * subdomain_data1 == subdomain_data_double);\n      CHECK(subdomain_data1 * 2. == subdomain_data_double);\n      subdomain_data1 *= 2.;\n      CHECK(subdomain_data1 == subdomain_data_double);\n    }\n    SECTION(\"Scalar division\") {\n      const auto subdomain_data_half =\n          make_subdomain_data({0.5 + 1.i, 1. + 1.5i, 1.5 + 2.i},\n                              {2. + 2.5i, 2.5 + 3.i}, {3. + 3.5i});\n      CHECK(subdomain_data1 / 2. == subdomain_data_half);\n      subdomain_data1 /= 2.;\n      CHECK(subdomain_data1 == subdomain_data_half);\n    }\n    SECTION(\"Iterate raw data\") {\n      CAPTURE(subdomain_data1);\n      CAPTURE(subdomain_data2);\n      std::copy(subdomain_data2.begin(), subdomain_data2.end(),\n                subdomain_data1.begin());\n      CHECK(subdomain_data1 == subdomain_data2);\n    }\n    SECTION(\"Remaining tests\") {\n      test_serialization(subdomain_data1);\n      test_copy_semantics(subdomain_data1);\n      auto copied_subdomain_data = subdomain_data1;\n      test_move_semantics(std::move(copied_subdomain_data), subdomain_data1);\n      CHECK(inner_product(subdomain_data1, subdomain_data2) ==\n            std::complex<double>(108., 12.));\n      CHECK(magnitude_square(subdomain_data1) == 230.);\n      CHECK(make_with_value<ElementCenteredSubdomainData<\n                1, tmpl::list<ScalarField<DataType>>>>(subdomain_data1, 1.) ==\n            make_subdomain_data({1. + 0.i, 1. + 0.i, 1. + 0.i},\n                                {1. + 0.i, 1. + 0.i}, {1. + 0.i}));\n    }\n    SECTION(\"Resizing\") {\n      const DirectionalId<1> extra_id{Direction<1>::upper_xi(),\n                                      ElementId<1>{1, {{{2, 2}}}}};\n      subdomain_data1.overlap_data.erase(west_id);\n      subdomain_data1.overlap_data.emplace(extra_id, 3);\n      subdomain_data1.destructive_resize(subdomain_data2);\n      CHECK(subdomain_data1.overlap_data.size() == 2);\n      CHECK(subdomain_data1.overlap_data.count(extra_id) == 0);\n      CHECK(subdomain_data1.overlap_data.count(west_id) == 1);\n      CHECK(subdomain_data1.overlap_data.count(east_id) == 1);\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.ParallelSchwarz.ElementCenteredSubdomainData\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  test_subdomain_data<DataVector>();\n  test_subdomain_data<ComplexDataVector>();\n}\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Test_OverlapHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\nusing namespace std::complex_literals;\n\ntemplate <typename DataType, size_t Dim>\nvoid test_data_on_overlap_consistency(const Index<Dim>& volume_extents,\n                                      const size_t overlap_extent,\n                                      const Direction<Dim>& direction) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const DataType used_for_size{volume_extents.product()};\n  using tags_list =\n      tmpl::list<::Tags::TempScalar<0, DataType>,\n                 ::Tags::TempI<1, Dim, Frame::Inertial, DataType>,\n                 ::Tags::Tempijj<2, Dim, Frame::Inertial, DataType>>;\n  const auto vars = make_with_random_values<Variables<tags_list>>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n  CAPTURE(vars);\n\n  const Variables<tags_list> vars_on_overlap =\n      data_on_overlap(vars, volume_extents, overlap_extent, direction);\n  CAPTURE(vars_on_overlap);\n\n  {\n    INFO(\"Test Variables and Tensor data on overlap is consistent\");\n    tmpl::for_each<tags_list>([&](auto tag_v) {\n      using tag = tmpl::type_from<decltype(tag_v)>;\n      CAPTURE(db::tag_name<tag>());\n      const auto tensor_on_overlap = data_on_overlap(\n          get<tag>(vars), volume_extents, overlap_extent, direction);\n      for (size_t i = 0; i < tensor_on_overlap.size(); i++) {\n        CAPTURE(i);\n        CHECK_ITERABLE_APPROX(tensor_on_overlap[i],\n                              get<tag>(vars_on_overlap)[i]);\n      }\n    });\n  }\n  {\n    INFO(\"Test extended_overlap_data and add_overlap_data are consistent\");\n    Variables<tags_list> extended_vars{used_for_size.size(), 0.};\n    add_overlap_data(make_not_null(&extended_vars), vars_on_overlap,\n                     volume_extents, overlap_extent, direction);\n    CHECK_VARIABLES_APPROX(\n        extended_vars, extended_overlap_data(vars_on_overlap, volume_extents,\n                                             overlap_extent, direction));\n  }\n}\n\ntemplate <typename DataType, size_t Dim>\nvoid test_data_on_overlap(const DataType& scalar_volume_data,\n                          const Index<Dim>& volume_extents,\n                          const size_t overlap_extent,\n                          const Direction<Dim>& direction,\n                          const DataType& expected_overlap_data,\n                          const DataType& expected_extended_data) {\n  CAPTURE(volume_extents);\n  CAPTURE(overlap_extent);\n  CAPTURE(direction);\n  {\n    INFO(\"Tensor data on overlap\");\n    const Scalar<DataType> scalar{scalar_volume_data};\n    CHECK_ITERABLE_APPROX(\n        get(data_on_overlap(scalar, volume_extents, overlap_extent, direction)),\n        expected_overlap_data);\n  }\n  using tag = ::Tags::TempScalar<0, DataType>;\n  Variables<tmpl::list<tag>> vars{scalar_volume_data.size()};\n  get(get<tag>(vars)) = scalar_volume_data;\n  const auto vars_on_overlap =\n      data_on_overlap(vars, volume_extents, overlap_extent, direction);\n  {\n    INFO(\"Variables data on overlap\");\n    CHECK_ITERABLE_APPROX(get(get<tag>(vars_on_overlap)),\n                          expected_overlap_data);\n  }\n  Variables<tmpl::list<tag>> extended_vars{scalar_volume_data.size(), 0.};\n  add_overlap_data(make_not_null(&extended_vars), vars_on_overlap,\n                   volume_extents, overlap_extent, direction);\n  {\n    INFO(\"Add overlap data\");\n    CHECK_ITERABLE_APPROX(get(get<tag>(extended_vars)), expected_extended_data);\n  }\n  {\n    INFO(\"Extended overlap data\");\n    CHECK_VARIABLES_APPROX(\n        extended_overlap_data(vars_on_overlap, volume_extents, overlap_extent,\n                              direction),\n        extended_vars);\n  }\n  {\n    INFO(\"Test consistency with non-scalars\");\n    test_data_on_overlap_consistency<DataType>(volume_extents, overlap_extent,\n                                               direction);\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_overlap_iterator(const Index<Dim>& volume_extents,\n                           const size_t overlap_extent,\n                           const Direction<Dim>& direction,\n                           const std::vector<size_t>& expected_volume_offsets) {\n  CAPTURE(Dim);\n  CAPTURE(volume_extents);\n  CAPTURE(overlap_extent);\n  CAPTURE(direction);\n  ASSERT(std::is_sorted(expected_volume_offsets.begin(),\n                        expected_volume_offsets.end()),\n         \"Volume indices must be sorted to optimize array access performance.\");\n\n  OverlapIterator overlap_iterator{volume_extents, overlap_extent, direction};\n  // Reset the iterator once to test resetting works\n  ++overlap_iterator;\n  overlap_iterator.reset();\n  for (size_t i = 0; i < expected_volume_offsets.size(); ++i) {\n    CHECK(overlap_iterator);\n    CHECK(overlap_iterator.volume_offset() == expected_volume_offsets[i]);\n    CHECK(overlap_iterator.overlap_offset() == i);\n    ++overlap_iterator;\n  }\n  CHECK_FALSE(overlap_iterator);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ParallelSchwarz.OverlapHelpers\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  {\n    INFO(\"Overlap extents\");\n    CHECK(overlap_extent(3, 0) == 0);\n    CHECK(overlap_extent(3, 1) == 1);\n    CHECK(overlap_extent(3, 2) == 2);\n    CHECK(overlap_extent(3, 3) == 3);\n    CHECK(overlap_extent(3, 4) == 3);\n    CHECK(overlap_extent(0, 0) == 0);\n  }\n  {\n    INFO(\"Overlap num_points\");\n    CHECK(overlap_num_points(Index<1>{{{3}}}, 0, 0) == 0);\n    CHECK(overlap_num_points(Index<1>{{{3}}}, 1, 0) == 1);\n    CHECK(overlap_num_points(Index<1>{{{3}}}, 2, 0) == 2);\n    CHECK(overlap_num_points(Index<1>{{{3}}}, 3, 0) == 3);\n    CHECK(overlap_num_points(Index<2>{{{2, 3}}}, 0, 0) == 0);\n    CHECK(overlap_num_points(Index<2>{{{2, 3}}}, 1, 0) == 3);\n    CHECK(overlap_num_points(Index<2>{{{2, 3}}}, 2, 0) == 6);\n    CHECK(overlap_num_points(Index<2>{{{2, 3}}}, 0, 1) == 0);\n    CHECK(overlap_num_points(Index<2>{{{2, 3}}}, 1, 1) == 2);\n    CHECK(overlap_num_points(Index<2>{{{2, 3}}}, 2, 1) == 4);\n    CHECK(overlap_num_points(Index<2>{{{2, 3}}}, 3, 1) == 6);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 0, 0) == 0);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 1, 0) == 12);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 2, 0) == 24);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 0, 1) == 0);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 1, 1) == 8);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 2, 1) == 16);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 3, 1) == 24);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 0, 2) == 0);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 1, 2) == 6);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 2, 2) == 12);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 3, 2) == 18);\n    CHECK(overlap_num_points(Index<3>{{{2, 3, 4}}}, 4, 2) == 24);\n  }\n  {\n    INFO(\"Overlap width\");\n    {\n      const DataVector coords{-1., -0.8, 0., 0.8, 1.};\n      CHECK(overlap_width(0, coords) == approx(0.));\n      CHECK(overlap_width(1, coords) == approx(0.2));\n      CHECK(overlap_width(2, coords) == approx(1.));\n      CHECK(overlap_width(3, coords) == approx(1.8));\n      CHECK(overlap_width(4, coords) == approx(2.));\n      CHECK(overlap_width(5, coords) == approx(2.));\n    }\n    {\n      const auto& coords =\n          Spectral::collocation_points<Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::GaussLobatto>(5);\n      CHECK(overlap_width(0, coords) == approx(0.));\n      CHECK(overlap_width(2, coords) == approx(1.));\n      CHECK(overlap_width(4, coords) == approx(2.));\n      CHECK(overlap_width(5, coords) == approx(2.));\n    }\n    {\n      const auto& coords =\n          Spectral::collocation_points<Spectral::Basis::Legendre,\n                                       Spectral::Quadrature::Gauss>(5);\n      CHECK(overlap_width(0, coords) > 0.);\n      CHECK(overlap_width(2, coords) == approx(1.));\n      CHECK(overlap_width(4, coords) < 2.);\n      CHECK(overlap_width(5, coords) == approx(2.));\n    }\n  }\n  {\n    INFO(\"Overlap iterator\");\n    {\n      // Give an example to include in the docs\n      // [overlap_iterator]\n      // Overlap region:\n      // + - - - + -xi->\n      // | X X O |\n      // | X X O |\n      // + - - - +\n      // v eta\n      const Index<2> volume_extents{{{3, 2}}};\n      const size_t overlap_extent = 2;\n      const auto direction = Direction<2>::lower_xi();\n      const std::array<size_t, 4> expected_volume_offsets{{0, 1, 3, 4}};\n      size_t i = 0;\n      for (OverlapIterator overlap_iterator{volume_extents, overlap_extent,\n                                            direction};\n           overlap_iterator; ++overlap_iterator) {\n        CHECK(overlap_iterator.volume_offset() ==\n              gsl::at(expected_volume_offsets, i));\n        CHECK(overlap_iterator.overlap_offset() == i);\n        ++i;\n      }\n      CHECK(i == expected_volume_offsets.size());\n      // [overlap_iterator]\n    }\n    // Overlap region: [X X O] -xi->\n    test_overlap_iterator(Index<1>{3}, 2, Direction<1>::lower_xi(), {{0, 1}});\n    // Overlap region: [O X X] -xi->\n    test_overlap_iterator(Index<1>{3}, 2, Direction<1>::upper_xi(), {{1, 2}});\n    // Overlap region:\n    // + - - - + -xi->\n    // | X X O |\n    // | X X O |\n    // + - - - +\n    // v eta\n    test_overlap_iterator(Index<2>{{{3, 2}}}, 2, Direction<2>::lower_xi(),\n                          {{0, 1, 3, 4}});\n    // Overlap region:\n    // + - - - + -xi->\n    // | O X X |\n    // | O X X |\n    // + - - - +\n    // v eta\n    test_overlap_iterator(Index<2>{{{3, 2}}}, 2, Direction<2>::upper_xi(),\n                          {{1, 2, 4, 5}});\n    // Overlap region:\n    // + - - - - + -xi->\n    // | X X X X |\n    // | X X X X |\n    // | O O O O |\n    // + - - - - +\n    // v eta\n    test_overlap_iterator(Index<2>{{{4, 3}}}, 2, Direction<2>::lower_eta(),\n                          {{0, 1, 2, 3, 4, 5, 6, 7}});\n    // Overlap region:\n    // + - - + -xi->\n    // | O O |\n    // | X X |\n    // | X X |\n    // + - - +\n    // v eta\n    test_overlap_iterator(Index<2>{{{2, 3}}}, 2, Direction<2>::upper_eta(),\n                          {{2, 3, 4, 5}});\n    test_overlap_iterator(Index<3>{{{3, 3, 2}}}, 2, Direction<3>::lower_xi(),\n                          {{0, 1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16}});\n    test_overlap_iterator(Index<3>{{{3, 3, 2}}}, 2, Direction<3>::upper_xi(),\n                          {{1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17}});\n    test_overlap_iterator(Index<3>{{{3, 3, 2}}}, 2, Direction<3>::lower_eta(),\n                          {{0, 1, 2, 3, 4, 5, 9, 10, 11, 12, 13, 14}});\n    test_overlap_iterator(Index<3>{{{3, 3, 2}}}, 2, Direction<3>::upper_eta(),\n                          {{3, 4, 5, 6, 7, 8, 12, 13, 14, 15, 16, 17}});\n    test_overlap_iterator(Index<3>{{{3, 2, 3}}}, 2, Direction<3>::lower_zeta(),\n                          {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}});\n    test_overlap_iterator(Index<3>{{{3, 2, 3}}}, 2, Direction<3>::upper_zeta(),\n                          {{6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}});\n  }\n  {\n    INFO(\"Data on overlap\");\n    {\n      INFO(\"1D\");\n      const Index<1> volume_extents{3};\n      const size_t overlap_extent = 2;\n      const DataVector scalar{1., 2., 3.};\n      // Overlap region: [X X O] -xi->\n      test_data_on_overlap(scalar, volume_extents, overlap_extent,\n                           Direction<1>::lower_xi(), {1., 2.}, {1., 2., 0.});\n      // Overlap region: [O X X] -xi->\n      test_data_on_overlap(scalar, volume_extents, overlap_extent,\n                           Direction<1>::upper_xi(), {2., 3.}, {0., 2., 3.});\n    }\n    {\n      INFO(\"1D complex\");\n      const Index<1> volume_extents{3};\n      const size_t overlap_extent = 2;\n      const ComplexDataVector scalar{1. + 2.i, 2. + 3.i, 3. + 4.i};\n      // Overlap region: [X X O] -xi->\n      test_data_on_overlap(scalar, volume_extents, overlap_extent,\n                           Direction<1>::lower_xi(), {1. + 2.i, 2. + 3.i},\n                           {1. + 2.i, 2. + 3.i, 0. + 0.i});\n      // Overlap region: [O X X] -xi->\n      test_data_on_overlap(scalar, volume_extents, overlap_extent,\n                           Direction<1>::upper_xi(), {2. + 3.i, 3. + 4.i},\n                           {0. + 0.i, 2. + 3.i, 3. + 4.i});\n    }\n    {\n      INFO(\"2D\");\n      const Index<2> volume_extents{{{3, 2}}};\n      const size_t overlap_extent_xi = 2;\n      const size_t overlap_extent_eta = 1;\n      const DataVector scalar{1., 2., 3., 4., 5., 6.};\n      // Overlap region:\n      // + - - - + -xi->\n      // | X X O |\n      // | X X O |\n      // + - - - +\n      // v eta\n      test_data_on_overlap(scalar, volume_extents, overlap_extent_xi,\n                           Direction<2>::lower_xi(), {1., 2., 4., 5.},\n                           {1., 2., 0., 4., 5., 0.});\n      // Overlap region:\n      // + - - - + -xi->\n      // | O X X |\n      // | O X X |\n      // + - - - +\n      // v eta\n      test_data_on_overlap(scalar, volume_extents, overlap_extent_xi,\n                           Direction<2>::upper_xi(), {2., 3., 5., 6.},\n                           {0., 2., 3., 0., 5., 6.});\n      // Overlap region:\n      // + - - - + -xi->\n      // | X X X |\n      // | O O O |\n      // + - - - +\n      // v eta\n      test_data_on_overlap(scalar, volume_extents, overlap_extent_eta,\n                           Direction<2>::lower_eta(), {1., 2., 3.},\n                           {1., 2., 3., 0., 0., 0.});\n      // Overlap region:\n      // + - - - + -xi->\n      // | O O O |\n      // | X X X |\n      // + - - - +\n      // v eta\n      test_data_on_overlap(scalar, volume_extents, overlap_extent_eta,\n                           Direction<2>::upper_eta(), {4., 5., 6.},\n                           {0., 0., 0., 4., 5., 6.});\n    }\n    {\n      INFO(\"3D\");\n      const Index<3> volume_extents{{{3, 2, 3}}};\n      const size_t overlap_extent_xi = 2;\n      const size_t overlap_extent_eta = 1;\n      const size_t overlap_extent_zeta = 2;\n      const DataVector scalar{1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.,\n                              10., 11., 12., 13., 14., 15., 16., 17., 18.};\n      test_data_on_overlap(\n          scalar, volume_extents, overlap_extent_xi, Direction<3>::lower_xi(),\n          {1., 2., 4., 5., 7., 8., 10., 11., 13., 14., 16., 17.},\n          {1., 2., 0., 4., 5., 0., 7., 8., 0., 10., 11., 0., 13., 14., 0., 16.,\n           17., 0.});\n      test_data_on_overlap(\n          scalar, volume_extents, overlap_extent_xi, Direction<3>::upper_xi(),\n          {2., 3., 5., 6., 8., 9., 11., 12., 14., 15., 17., 18.},\n          {0., 2., 3., 0., 5., 6., 0., 8., 9., 0., 11., 12., 0., 14., 15., 0.,\n           17., 18.});\n      test_data_on_overlap(scalar, volume_extents, overlap_extent_eta,\n                           Direction<3>::lower_eta(),\n                           {1., 2., 3., 7., 8., 9., 13., 14., 15.},\n                           {1., 2., 3., 0., 0., 0., 7., 8., 9., 0., 0., 0., 13.,\n                            14., 15., 0., 0., 0.});\n      test_data_on_overlap(scalar, volume_extents, overlap_extent_eta,\n                           Direction<3>::upper_eta(),\n                           {4., 5., 6., 10., 11., 12., 16., 17., 18.},\n                           {0., 0., 0., 4., 5., 6., 0., 0., 0., 10., 11., 12.,\n                            0., 0., 0., 16., 17., 18.});\n      test_data_on_overlap(scalar, volume_extents, overlap_extent_zeta,\n                           Direction<3>::lower_zeta(),\n                           {1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.},\n                           {1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.,\n                            0., 0., 0., 0., 0., 0.});\n      test_data_on_overlap(\n          scalar, volume_extents, overlap_extent_zeta,\n          Direction<3>::upper_zeta(),\n          {7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18.},\n          {0., 0., 0., 0., 0., 0., 7., 8., 9., 10., 11., 12., 13., 14., 15.,\n           16., 17., 18.});\n    }\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH((overlap_num_points(Index<1>{{{3}}}, 4, 0)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Overlap extent '4' exceeds volume extents\"));\n  CHECK_THROWS_WITH(\n      (overlap_num_points(Index<1>{{{3}}}, 0, 1)),\n      Catch::Matchers::ContainsSubstring(\"Invalid dimension '1' in 1D\"));\n#endif\n}\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Test_SchwarzAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/ApplyMatrices.hpp\"\n#include \"DataStructures/DynamicMatrix.hpp\"\n#include \"DataStructures/DynamicVector.hpp\"\n#include \"Domain/Creators/DomainCreator.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Elliptic/DiscontinuousGalerkin/DgElementArray.hpp\"\n#include \"Helpers/Domain/BoundaryConditions/BoundaryCondition.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/DistributedLinearSolverAlgorithmTestHelpers.hpp\"\n#include \"Helpers/ParallelAlgorithms/LinearSolver/LinearSolverAlgorithmTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"Parallel/GlobalCache.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/ElementCenteredSubdomainData.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Schwarz.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/SubdomainOperator.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = LinearSolverAlgorithmTestHelpers;\nnamespace helpers_distributed = DistributedLinearSolverAlgorithmTestHelpers;\n\nnamespace {\n\nstruct SchwarzSmoother {\n  static constexpr Options::String help =\n      \"Options for the iterative Schwarz smoother\";\n};\n\ntemplate <typename Tag>\nblaze::DynamicVector<double> extend_subdomain_data(\n    const LinearSolver::Schwarz::ElementCenteredSubdomainData<\n        1, tmpl::list<Tag>>& subdomain_data,\n    const size_t element_index, const size_t num_elements,\n    const size_t num_points_per_element, const size_t overlap_extent) {\n  blaze::DynamicVector<double> extended_subdomain_data(\n      num_elements * num_points_per_element, 0.);\n#if defined(__GNUC__) and not defined(__clang__) and __GNUC__ == 12\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wuse-after-free\"\n#endif\n  blaze::subvector(\n      extended_subdomain_data, element_index * num_points_per_element,\n      num_points_per_element) = get(get<Tag>(subdomain_data.element_data));\n#if defined(__GNUC__) and not defined(__clang__) and __GNUC__ == 12\n#pragma GCC diagnostic pop\n#endif\n  for (const auto& [overlap_id, overlap_data] : subdomain_data.overlap_data) {\n    const auto& direction = overlap_id.direction();\n    const auto direction_from_neighbor = direction.opposite();\n    const size_t overlapped_element_index = direction.side() == Side::Lower\n                                                ? (element_index - 1)\n                                                : (element_index + 1);\n    blaze::subvector(extended_subdomain_data,\n                     overlapped_element_index * num_points_per_element,\n                     num_points_per_element) =\n        get(get<Tag>(LinearSolver::Schwarz::extended_overlap_data(\n            overlap_data, Index<1>{num_points_per_element}, overlap_extent,\n            direction_from_neighbor)));\n  }\n  return extended_subdomain_data;\n}\n\ntemplate <typename Tag>\nvoid restrict_to_subdomain(\n    const gsl::not_null<LinearSolver::Schwarz::ElementCenteredSubdomainData<\n        1, tmpl::list<Tag>>*>\n        result,\n    const blaze::DynamicVector<double>& extended_result,\n    const size_t element_index, const size_t num_points_per_element,\n    const size_t overlap_extent) {\n  const blaze::DynamicVector<double> restricted_element_data =\n      blaze::subvector(extended_result, element_index * num_points_per_element,\n                       num_points_per_element);\n  std::copy(restricted_element_data.begin(), restricted_element_data.end(),\n            get(get<Tag>(result->element_data)).begin());\n  for (auto& [overlap_id, overlap_result] : result->overlap_data) {\n    const auto& direction = overlap_id.direction();\n    const auto direction_from_neighbor = direction.opposite();\n    const size_t overlapped_element_index = direction.side() == Side::Lower\n                                                ? (element_index - 1)\n                                                : (element_index + 1);\n    Scalar<DataVector> extended_result_on_element{num_points_per_element};\n    const blaze::DynamicVector<double> restricted_overlap_data =\n        blaze::subvector(extended_result,\n                         overlapped_element_index * num_points_per_element,\n                         num_points_per_element);\n    std::copy(restricted_overlap_data.begin(), restricted_overlap_data.end(),\n              get(extended_result_on_element).begin());\n    LinearSolver::Schwarz::data_on_overlap(\n        make_not_null(&get<Tag>(overlap_result)), extended_result_on_element,\n        Index<1>{num_points_per_element}, overlap_extent,\n        direction_from_neighbor);\n  }\n}\n\n// [subdomain_operator]\n// Applies the linear operator given explicitly in the input file to data on an\n// element-centered subdomain, using standard matrix multiplication.\n//\n// We assume all elements have the same extents so we can use the element's\n// intruding overlap extents instead of initializing extruding overlap extents.\nstruct SubdomainOperator : LinearSolver::Schwarz::SubdomainOperator<1> {\n  template <typename ResultTags, typename OperandTags, typename DbTagsList>\n  void operator()(\n      const gsl::not_null<\n          LinearSolver::Schwarz::ElementCenteredSubdomainData<1, ResultTags>*>\n          result,\n      const LinearSolver::Schwarz::ElementCenteredSubdomainData<1, OperandTags>&\n          operand,\n      const db::DataBox<DbTagsList>& box) const {\n    // Retrieve arguments from the DataBox\n    const auto& matrix_slices =\n        db::get<helpers_distributed::LinearOperator>(box);\n    const auto& element = db::get<domain::Tags::Element<1>>(box);\n    const auto& overlap_extents = db::get<\n        LinearSolver::Schwarz::Tags::IntrudingExtents<1, SchwarzSmoother>>(box);\n\n    const size_t element_index = helpers_distributed::get_index(element.id());\n    const size_t num_elements = matrix_slices.size();\n    const size_t num_points_per_element = matrix_slices.begin()->columns();\n\n    // Re-size the result buffer if necessary\n    result->destructive_resize(operand);\n\n    // Assemble full operator matrix\n    const auto operator_matrix =\n        helpers_distributed::combine_matrix_slices(matrix_slices);\n\n    // Extend subdomain data with zeros outside the subdomain\n    const auto extended_operand =\n        extend_subdomain_data(operand, element_index, num_elements,\n                              num_points_per_element, overlap_extents[0]);\n\n    // Apply matrix to extended subdomain data\n    const blaze::DynamicVector<double> extended_result =\n        operator_matrix * extended_operand;\n\n    // Restrict the result back to the subdomain\n    restrict_to_subdomain(result, extended_result, element_index,\n                          num_points_per_element, overlap_extents[0]);\n  }\n};\n// [subdomain_operator]\n\nstruct Metavariables {\n  static constexpr const char* const help{\n      \"Test the Schwarz linear solver algorithm\"};\n  static constexpr size_t volume_dim = 1;\n  using system =\n      TestHelpers::domain::BoundaryConditions::SystemWithoutBoundaryConditions<\n          volume_dim>;\n\n  using linear_solver =\n      LinearSolver::Schwarz::Schwarz<helpers_distributed::fields_tag,\n                                     SchwarzSmoother, SubdomainOperator>;\n  using preconditioner = void;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<DomainCreator<1>, tmpl::list<domain::creators::Interval>>>;\n  };\n\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  using component_list = helpers_distributed::component_list<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc(\n      {&domain::creators::register_derived_with_charm,\n       &register_derived_classes_with_charm<\n           Metavariables::linear_solver::subdomain_solver>,\n       &TestHelpers::domain::BoundaryConditions::register_derived_with_charm},\n      {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Test_SchwarzAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nDescription: |\n  The test problem being solved here is a DG-discretized 1D Poisson equation\n  -u''(x) = f(x) on the interval [0, pi] with source f(x)=sin(x) and homogeneous\n  Dirichlet boundary conditions such that the solution is u(x)=sin(x) as well.\n\n  Details:\n  - Domain decomposition: 2 elements with 3 LGL grid-points each\n  - \"Primal\" DG formulation (no auxiliary variable)\n  - Not multiplied by mass matrix so the operator is not symmetric\n  - Mass-lumping: inverse mass matrix is approximated by diagonal\n  - Internal penalty flux with sigma = 1.5 * (N_points - 1)^2 / h\n\n---\n\nParallelization:\n  ElementDistribution: NumGridPoints\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n\nDomainCreator:\n  Interval:\n    LowerBound: [0]\n    UpperBound: [3.141592653589793]\n    Distribution: [Linear]\n    IsPeriodicIn: [false]\n    InitialRefinement: [1]\n    InitialGridPoints: [3]\n    TimeDependence: None\n\nLinearOperator:\n  - [[20.26423672846756 ,  3.242277876554809, -2.836993141985458],\n      [ 0.810569469138702,  3.24227787655481 , -0.405284734569351],\n      [-2.836993141985458, -1.621138938277405, 12.969111506219237],\n      [ 1.215854203708053, -4.863416814832214, -7.295125222248322],\n      [ 0.               ,  0.               , -1.215854203708054],\n      [ 0.               ,  0.               ,  1.215854203708053]]\n  - [[ 1.215854203708053,  0.               ,  0.               ],\n      [-1.215854203708054,  0.               ,  0.               ],\n      [-7.295125222248322, -4.863416814832214,  1.215854203708053],\n      [12.969111506219237, -1.621138938277405, -2.836993141985458],\n      [-0.405284734569351,  3.24227787655481 ,  0.810569469138702],\n      [-2.836993141985458,  3.242277876554809, 20.26423672846756 ]]\n\nSource:\n  - [0., 0.7071067811865475, 1.]\n  - [1., 0.7071067811865476, 0.]\n\nExpectedResult:\n  - [-0.0363482510397858,  0.7235793356729757,  0.9928055333486293]\n  - [ 0.9928055333486292,  0.7235793356729758, -0.0363482510397858]\n\nSchwarzSmoother:\n  MaxOverlap: 2\n  Iterations: 9\n  Verbosity: Verbose\n  SubdomainSolver:\n    # Testing with a preconditioned Krylov-type subdomain solver\n    Gmres:\n      ConvergenceCriteria:\n        MaxIterations: 1\n        RelativeResidual: 1.e-14\n        AbsoluteResidual: 1.e-14\n      Verbosity: Verbose\n      Restart: None\n      Preconditioner:\n        # Preconditioning with the explicitly-built inverse matrix, so all\n        # subdomain solves should converge immediately\n        ExplicitInverse:\n          WriteMatrixToFile: None\n  ObservePerCoreReductions: False\n  OutputVolumeData: True\n\nConvergenceReason: NumIterations\n\nDiscretization:\n  DiscontinuousGalerkin:\n    Quadrature: GaussLobatto\n\nObservers:\n  VolumeFileName: \"Test_SchwarzAlgorithm_Volume\"\n  ReductionFileName: \"Test_SchwarzAlgorithm_Reductions\"\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Tags.hpp\"\n\nnamespace {\nstruct DummyOptionsGroup {};\nstruct DummySubdomainSolver {};\ntemplate <size_t N>\nstruct ScalarFieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace\n\nnamespace LinearSolver::Schwarz {\n\nSPECTRE_TEST_CASE(\"Unit.ParallelSchwarz.Tags\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  TestHelpers::db::test_simple_tag<Tags::MaxOverlap<DummyOptionsGroup>>(\n      \"MaxOverlap(DummyOptionsGroup)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::SubdomainSolver<DummySubdomainSolver, DummyOptionsGroup>>(\n      \"SubdomainSolver(DummyOptionsGroup)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::IntrudingExtents<1, DummyOptionsGroup>>(\n      \"IntrudingExtents(DummyOptionsGroup)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::IntrudingOverlapWidths<1, DummyOptionsGroup>>(\n      \"IntrudingOverlapWidths(DummyOptionsGroup)\");\n  TestHelpers::db::test_simple_tag<Tags::Weight<DummyOptionsGroup>>(\n      \"Weight(DummyOptionsGroup)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::SummedIntrudingOverlapWeights<DummyOptionsGroup>>(\n      \"SummedIntrudingOverlapWeights(DummyOptionsGroup)\");\n\n  {\n    TestHelpers::db::test_simple_tag<\n        Tags::Overlaps<ScalarFieldTag<0>, 1, DummyOptionsGroup>>(\n        \"Overlaps(ScalarFieldTag, DummyOptionsGroup)\");\n    // Test subitems on overlaps\n    const OverlapId<1> overlap_id{Direction<1>::lower_xi(), ElementId<1>{0}};\n    auto box = db::create<db::AddSimpleTags<Tags::Overlaps<\n        ::Tags::Variables<tmpl::list<ScalarFieldTag<0>, ScalarFieldTag<1>>>, 1,\n        DummyOptionsGroup>>>(\n        OverlapMap<1,\n                   Variables<tmpl::list<ScalarFieldTag<0>, ScalarFieldTag<1>>>>{\n            {overlap_id, {3, 0.}}});\n    // Test retrieving the individual tags\n    CHECK(get(get<Tags::Overlaps<ScalarFieldTag<0>, 1, DummyOptionsGroup>>(box)\n                  .at(overlap_id)) == DataVector(3, 0.));\n    CHECK(get(get<Tags::Overlaps<ScalarFieldTag<1>, 1, DummyOptionsGroup>>(box)\n                  .at(overlap_id)) == DataVector(3, 0.));\n    // Test mutating the individual tags\n    db::mutate<Tags::Overlaps<ScalarFieldTag<0>, 1, DummyOptionsGroup>>(\n        [&overlap_id](const auto scalar0_overlaps) {\n          get(scalar0_overlaps->at(overlap_id)) = 1.;\n        },\n        make_not_null(&box));\n    CHECK(get(get<Tags::Overlaps<ScalarFieldTag<0>, 1, DummyOptionsGroup>>(box)\n                  .at(overlap_id)) == DataVector(3, 1.));\n    CHECK(\n        get(get<ScalarFieldTag<0>>(\n            get<Tags::Overlaps<::Tags::Variables<tmpl::list<ScalarFieldTag<0>,\n                                                            ScalarFieldTag<1>>>,\n                               1, DummyOptionsGroup>>(box)\n                .at(overlap_id))) == DataVector(3, 1.));\n  }\n}\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/Test_Weighting.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/Neighbors.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/CollocationPoints.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/OverlapHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Schwarz/Weighting.hpp\"\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace LinearSolver::Schwarz {\n\nnamespace {\n\n// This function explicitly implements Eqn. (41) in\n// https://arxiv.org/abs/1907.01572\ntemplate <size_t Dim>\nvoid test_element_weight() {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist_coords(-2., 2.);\n  std::uniform_real_distribution<> dist_widths(0.1, 2.);\n  const auto logical_coords =\n      make_with_random_values<tnsr::I<DataVector, Dim, Frame::ElementLogical>>(\n          make_not_null(&generator), make_not_null(&dist_coords), size_t{5});\n  const auto widths = make_with_random_values<std::array<double, Dim>>(\n      make_not_null(&generator), make_not_null(&dist_widths));\n  CAPTURE(Dim);\n  CAPTURE(logical_coords);\n  CAPTURE(widths);\n  const auto phi = [](const DataVector& xi) {\n    DataVector result{xi.size()};\n    for (size_t i = 0; i < xi.size(); ++i) {\n      if (xi[i] < -1.) {\n        result[i] = -1.;\n      } else if (xi[i] > 1.) {\n        result[i] = 1.;\n      } else {\n        result[i] =\n            0.125 * (15. * xi[i] - 10. * cube(xi[i]) + 3. * pow<5>(xi[i]));\n      }\n    }\n    return result;\n  };\n  Scalar<DataVector> reference_element_weight{logical_coords.begin()->size(),\n                                              1.};\n  for (size_t d = 0; d < Dim; ++d) {\n    get(reference_element_weight) *=\n        0.5 * (phi((logical_coords.get(d) + 1.) / gsl::at(widths, d)) -\n               phi((logical_coords.get(d) - 1.) / gsl::at(widths, d)));\n  }\n  CHECK_ITERABLE_APPROX(\n      get(LinearSolver::Schwarz::element_weight(logical_coords, widths, {})),\n      get(reference_element_weight));\n}\n\ntemplate <size_t Dim>\nvoid test_weights_conservation(const Element<Dim>& element,\n                               const Mesh<Dim>& mesh,\n                               const std::optional<size_t> max_overlap) {\n  INFO(\"Weight conservation\");\n  CAPTURE(Dim);\n  CAPTURE(element);\n  CAPTURE(mesh);\n  CAPTURE(max_overlap);\n  const auto logical_coords = logical_coordinates(mesh);\n  CAPTURE(logical_coords);\n  const size_t num_points = mesh.number_of_grid_points();\n  std::array<double, Dim> overlap_widths{};\n  for (size_t d = 0; d < Dim; ++d) {\n    const auto overlap_extent =\n        LinearSolver::Schwarz::overlap_extent(mesh.extents(d), max_overlap);\n    const auto collocation_points =\n        Spectral::collocation_points(mesh.slice_through(d));\n    gsl::at(overlap_widths, d) = LinearSolver::Schwarz::overlap_width(\n        overlap_extent, collocation_points);\n  }\n  CAPTURE(overlap_widths);\n  // The weights for the element data and those for data on intruding overlaps\n  // should sum to one.\n  const auto element_weights = LinearSolver::Schwarz::element_weight(\n      logical_coords, overlap_widths, element.external_boundaries());\n  CAPTURE(element_weights);\n  auto summed_weights = element_weights;\n  for (const auto& direction_and_neighbors : element.neighbors()) {\n    const auto& direction = direction_and_neighbors.first;\n    const size_t num_neighbors = direction_and_neighbors.second.size();\n    get(summed_weights) +=\n        num_neighbors * get(LinearSolver::Schwarz::intruding_weight(\n                            logical_coords, direction, overlap_widths,\n                            num_neighbors, element.external_boundaries()));\n  }\n  CHECK_ITERABLE_APPROX(get(summed_weights), DataVector(num_points, 1.));\n}\n\ntemplate <size_t Dim>\nvoid test_weights(const Mesh<Dim>& mesh,\n                  const std::optional<size_t> max_overlap) {\n  typename Element<Dim>::Neighbors_t neighbors{};\n  std::unordered_map<size_t, OrientationMap<Dim>> orientations{};\n  for (size_t i = 0; i < two_to_the(Dim - 1); ++i) {\n    orientations.emplace(i + 1, OrientationMap<Dim>::create_aligned());\n  }\n  for (size_t d = 0; d < Dim; ++d) {\n    for (const auto& side : std::array<Side, 2>{{Side::Lower, Side::Upper}}) {\n      // This test as written uses Neighbors that are in different Blocks which\n      // are now by definition non-conforming.  LK has chosen to mark them as\n      // non-conforming instead of generating Elements that would be conforming\n      auto& direction_neighbors =\n          neighbors\n              .emplace(Direction<Dim>{d, side},\n                       Neighbors<Dim>{std::unordered_set<ElementId<Dim>>{},\n                                      orientations, false})\n              .first->second;\n      for (size_t i = 0; i < two_to_the(Dim - 1); ++i) {\n        direction_neighbors.add_ids({ElementId<Dim>{i + 1}});\n        const Element<Dim> element{ElementId<Dim>{0}, neighbors};\n        if (max_overlap.has_value()) {\n          for (size_t max_overlap_i = 1; max_overlap_i <= max_overlap;\n               ++max_overlap_i) {\n            test_weights_conservation(element, mesh, max_overlap_i);\n          }\n        } else {\n          test_weights_conservation(element, mesh, std::nullopt);\n        }\n      }\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelSchwarz.Weighting\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  {\n    INFO(\"Weighting function\");\n    // Extruding weight on upper side:\n    // ___\n    //    \\       (disabling backslash line continuation)\n    //     --\n    // ---+--> xi\n    //    1.\n    CHECK_ITERABLE_APPROX(\n        extruding_weight(DataVector({-1., 0., 1., 2., 3.}), 1., Side::Upper),\n        DataVector({1., 1., 0.5, 0., 0.}));\n    // Intruding weight on upper side:\n    //      __\n    //    /\n    // ---\n    // ---+---> xi\n    //    1.\n    CHECK_ITERABLE_APPROX(\n        intruding_weight(DataVector({-1., 0., 1., 2., 3.}), 1., Side::Upper),\n        DataVector({0., 0., 0.5, 1., 1.}));\n    // Extruding weight on lower side:\n    //     ___\n    //   /\n    // --\n    // --+----> xi\n    //  -1.\n    CHECK_ITERABLE_APPROX(\n        extruding_weight(DataVector({-3., -2., -1., 0., 1.}), 1., Side::Lower),\n        DataVector({0., 0., 0.5, 1., 1.}));\n    // Intruding weight on lower side:\n    // __\n    //   \\        (disabling backslash line continuation)\n    //    ---\n    // --+---> xi\n    //  -1.\n    CHECK_ITERABLE_APPROX(\n        intruding_weight(DataVector({-3., -2., -1., 0., 1.}), 1., Side::Lower),\n        DataVector({1., 1., 0.5, 0., 0.}));\n  }\n  {\n    const auto element_weight = LinearSolver::Schwarz::element_weight(\n        tnsr::I<DataVector, 1, Frame::ElementLogical>{\n            {{{-2., -1.5, -1., -0.5, -0.25, 0., 0.25, 0.5, 1., 1.5, 2.}}}},\n        {{1.}}, {});\n    CHECK_ITERABLE_APPROX(\n        get(element_weight),\n        DataVector({0., 0.103515625, 0.5, 0.896484375, 0.98394775390625, 1.,\n                    0.98394775390625, 0.896484375, 0.5, 0.103515625, 0.}));\n  }\n  // Test against an explicit reference implementation\n  {\n    test_element_weight<1>();\n    test_element_weight<2>();\n    test_element_weight<3>();\n  }\n  // Test weights conservation on all possible element-neighbor configurations\n  {\n    for (const auto& [quadrature, max_overlap] : cartesian_product(\n             make_array(Spectral::Quadrature::GaussLobatto,\n                        Spectral::Quadrature::Gauss),\n             std::array<std::optional<size_t>, 2>{{4, std::nullopt}})) {\n      test_weights(Mesh<1>{3, Spectral::Basis::Legendre, quadrature},\n                   max_overlap);\n      test_weights(Mesh<2>{{3, 4}, Spectral::Basis::Legendre, quadrature},\n                   max_overlap);\n      test_weights(Mesh<3>{{2, 3, 4}, Spectral::Basis::Legendre, quadrature},\n                   max_overlap);\n    }\n  }\n}\n\n}  // namespace LinearSolver::Schwarz\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/LinearSolver/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/LinearSolver/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Tag : db::SimpleTag {\n  using type = int;\n};\nstruct TestOptionsGroup {\n  static std::string name() { return \"TestLinearSolver\"; }\n};\nstruct TestSolver {};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.LinearSolver.Tags\",\n                  \"[Unit][ParallelAlgorithms][LinearSolver]\") {\n  TestHelpers::db::test_prefix_tag<LinearSolver::Tags::Operand<Tag>>(\n      \"LinearOperand(Tag)\");\n  TestHelpers::db::test_prefix_tag<LinearSolver::Tags::OperatorAppliedTo<Tag>>(\n      \"LinearOperatorAppliedTo(Tag)\");\n  TestHelpers::db::test_prefix_tag<LinearSolver::Tags::Residual<Tag>>(\n      \"LinearResidual(Tag)\");\n  TestHelpers::db::test_prefix_tag<LinearSolver::Tags::MagnitudeSquare<Tag>>(\n      \"LinearMagnitudeSquare(Tag)\");\n  TestHelpers::db::test_prefix_tag<LinearSolver::Tags::Magnitude<Tag>>(\n      \"LinearMagnitude(Tag)\");\n  TestHelpers::db::test_prefix_tag<LinearSolver::Tags::Orthogonalization<Tag>>(\n      \"LinearOrthogonalization(Tag)\");\n  TestHelpers::db::test_prefix_tag<\n      LinearSolver::Tags::OrthogonalizationHistory<Tag>>(\n      \"LinearOrthogonalizationHistory(Tag)\");\n  TestHelpers::db::test_prefix_tag<\n      LinearSolver::Tags::KrylovSubspaceBasis<Tag>>(\"KrylovSubspaceBasis(Tag)\");\n  TestHelpers::db::test_prefix_tag<LinearSolver::Tags::Preconditioned<Tag>>(\n      \"Preconditioned(Tag)\");\n  TestHelpers::db::test_simple_tag<\n      LinearSolver::Tags::OutputVolumeData<TestSolver>>(\n      \"OutputVolumeData(TestSolver)\");\n  TestHelpers::db::test_simple_tag<\n      LinearSolver::Tags::ObservationId<TestSolver>>(\n      \"ObservationId(TestSolver)\");\n\n  {\n    INFO(\"ResidualCompute\");\n    TestHelpers::db::test_compute_tag<\n        LinearSolver::Tags::ResidualCompute<Tag, ::Tags::Source<Tag>>>(\n        \"LinearResidual(Tag)\");\n    const auto box = db::create<\n        db::AddSimpleTags<::Tags::Source<Tag>,\n                          LinearSolver::Tags::OperatorAppliedTo<Tag>>,\n        db::AddComputeTags<\n            LinearSolver::Tags::ResidualCompute<Tag, ::Tags::Source<Tag>>>>(3,\n                                                                            2);\n    CHECK(db::get<LinearSolver::Tags::Residual<Tag>>(box) == 1);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/NonlinearSolver/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelNonlinearSolver\")\n\nset(LIBRARY_SOURCES\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Utilities\n  ParallelNonlinearSolver\n  )\n\nadd_subdirectory(NewtonRaphson)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ParallelNewtonRaphson\")\n\nset(LIBRARY_SOURCES\n  Test_LineSearch.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ParallelNonlinearSolver\n  )\n\nset(INTEGRATION_TEST_LINK_LIBRARIES\n  Charmxx::main\n  Convergence\n  DataStructures\n  ErrorHandling\n  Informer\n  NonlinearSolverHelpers\n  ParallelLinearSolver\n  ParallelNonlinearSolver\n  )\n\nadd_standalone_test(\n  \"Integration.LinearSolver.NewtonRaphsonAlgorithm\"\n  INPUT_FILE \"Test_NewtonRaphsonAlgorithm.yaml\")\ntarget_link_libraries(\n  \"Test_NewtonRaphsonAlgorithm\"\n  PRIVATE\n  \"${INTEGRATION_TEST_LINK_LIBRARIES}\")\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Test_LineSearch.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/LineSearch.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ParallelNewtonRaphson.LineSearch\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  {\n    // At the initial globalization step do a _quadratic_ polynomial\n    // interpolation and select the minimum\n    const double step_length = 0.5;\n    const double residual = 1.;\n    const double residual_slope = -2.;\n    const double next_residual = 1.5;\n    const double next_step_length =\n        NonlinearSolver::newton_raphson::next_step_length(\n            0, step_length, std::numeric_limits<double>::signaling_NaN(),\n            residual, residual_slope, next_residual,\n            std::numeric_limits<double>::signaling_NaN());\n    // The minimum of a quadratic function f(x) that passes through f(0) = 1 and\n    // f(0.5) = 1.5 with derivative f'(0) = -2 is at x = 1 / 6\n    CHECK(next_step_length == approx(1. / 6.));\n  }\n  {\n    // At subsequent initial globalization steps do a _cubic_ polynomial\n    // interpolation and select the minimum\n    const double step_length = 0.5;\n    const double residual = 1.;\n    const double residual_slope = -2.;\n    const double next_residual = 1.5;\n    const double prev_step_length = 1.;\n    const double prev_residual = 2.;\n    const double next_step_length =\n        NonlinearSolver::newton_raphson::next_step_length(\n            1, step_length, prev_step_length, residual, residual_slope,\n            next_residual, prev_residual);\n    // The minimum of a cubic function f(x) that passes through f(0) = 1,\n    // f(0.5) = 1.5 and f(1) = 2 with f'(0) = -2 is at x = 0.12732200375003505\n    CHECK(next_step_length == approx(0.12732200375003505));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Test_NewtonRaphsonAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <optional>\n#include <vector>\n\n#include \"Helpers/ParallelAlgorithms/NonlinearSolver/Algorithm.hpp\"\n#include \"Options/String.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/CharmMain.tpp\"\n#include \"ParallelAlgorithms/LinearSolver/Gmres/Gmres.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/NewtonRaphson/NewtonRaphson.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace PUP {\nclass er;\n}  // namespace PUP\n\nnamespace helpers = TestHelpers::NonlinearSolver;\n\nnamespace {\n\nstruct LinearSolverGroup {\n  static std::string name() { return \"LinearSolver\"; }\n  static constexpr Options::String help = \"Options for the linear solver\";\n};\n\nstruct NonlinearSolverGroup {\n  static std::string name() { return \"NewtonRaphson\"; }\n  static constexpr Options::String help = \"Options for the nonlinear solver\";\n};\n\ntemplate <typename OperandTag>\nstruct ApplyNonlinearOperator {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const int /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*component*/) {\n    db::mutate<::NonlinearSolver::Tags::OperatorAppliedTo<OperandTag>>(\n        [](const auto Ax, const auto& x) { *Ax = cube(x) - x; },\n        make_not_null(&box), get<OperandTag>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <typename OperandTag, typename FieldTag>\nstruct ApplyLinearizedOperator {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ActionList, typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const int /*array_index*/, const ActionList /*meta*/,\n      const ParallelComponent* const /*component*/) {\n    db::mutate<LinearSolver::Tags::OperatorAppliedTo<OperandTag>>(\n        [](const auto Ap, const auto& dx, const auto& x) {\n          *Ap = (3. * square(x) - 1) * dx;\n        },\n        make_not_null(&box), get<OperandTag>(box), get<FieldTag>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\nstruct Metavariables {\n  static constexpr Options::String help{\n      \"Test the Newton-Raphson nonlinear solver algorithm\"};\n\n  using nonlinear_solver = NonlinearSolver::newton_raphson::NewtonRaphson<\n      Metavariables, helpers::fields_tag, NonlinearSolverGroup>;\n  using linear_solver = LinearSolver::gmres::Gmres<\n      Metavariables, typename nonlinear_solver::linear_solver_fields_tag,\n      LinearSolverGroup, false,\n      typename nonlinear_solver::linear_solver_source_tag>;\n\n  template <typename OperandTag>\n  using apply_nonlinear_operator = ApplyNonlinearOperator<OperandTag>;\n  template <typename OperandTag, typename FieldTag>\n  using apply_linearized_operator =\n      ApplyLinearizedOperator<OperandTag, FieldTag>;\n\n  using component_list = helpers::component_list<Metavariables>;\n  using observed_reduction_data_tags =\n      helpers::observed_reduction_data_tags<Metavariables>;\n  static constexpr bool ignore_unrecognized_command_line_options = false;\n  static constexpr auto default_phase_order = helpers::default_phase_order;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& /*p*/) {}\n};\n\n}  // namespace\n\nextern \"C\" void CkRegisterMainModule() {\n  Parallel::charmxx::register_main_module<Metavariables>();\n  Parallel::charmxx::register_init_node_and_proc({}, {});\n}\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Test_NewtonRaphsonAlgorithm.yaml",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n---\n---\n\n# Finding the roots of x^3 - x - b, where b is the `Source`\nSource: [1, 2, 3]\nInitialGuess: [0.6, 0.7, 0.8]\nExpectedResult: [1.324717957244753, 1.521379706804575, 1.6716998816571695]\n\nNewtonRaphson:\n  ConvergenceCriteria:\n    MaxIterations: 8\n    AbsoluteResidual: 1.e-14\n    RelativeResidual: 0\n  Verbosity: Verbose\n  DampingFactor: 1.\n  SufficientDecrease: 1.e-4\n  MaxGlobalizationSteps: 40\n\nLinearSolver:\n  ConvergenceCriteria:\n    MaxIterations: 3\n    AbsoluteResidual: 1.e-14\n    RelativeResidual: 0\n  Verbosity: Quiet\n\nObservers:\n  VolumeFileName: \"Test_NewtonRaphsonAlgorithm_Volume\"\n  ReductionFileName: \"Test_NewtonRaphsonAlgorithm_Reductions\"\n\nResourceInfo:\n  AvoidGlobalProc0: false\n  Singletons: Auto\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/NonlinearSolver/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/NonlinearSolver/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Tag : db::SimpleTag {\n  using type = int;\n};\nstruct TestOptionsGroup {\n  static std::string name() { return \"TestNonlinearSolver\"; }\n};\n}  // namespace\n\nnamespace NonlinearSolver {\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.NonlinearSolver.Tags\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  TestHelpers::db::test_prefix_tag<Tags::Correction<Tag>>(\"Correction(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::OperatorAppliedTo<Tag>>(\n      \"NonlinearOperatorAppliedTo(Tag)\");\n  TestHelpers::db::test_prefix_tag<Tags::Residual<Tag>>(\n      \"NonlinearResidual(Tag)\");\n  TestHelpers::db::test_simple_tag<Tags::StepLength<TestOptionsGroup>>(\n      \"StepLength(TestNonlinearSolver)\");\n  TestHelpers::db::test_simple_tag<Tags::SufficientDecrease<TestOptionsGroup>>(\n      \"SufficientDecrease(TestNonlinearSolver)\");\n  TestHelpers::db::test_simple_tag<Tags::DampingFactor<TestOptionsGroup>>(\n      \"DampingFactor(TestNonlinearSolver)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::MaxGlobalizationSteps<TestOptionsGroup>>(\n      \"MaxGlobalizationSteps(TestNonlinearSolver)\");\n  TestHelpers::db::test_prefix_tag<Tags::Globalization<Tag>>(\n      \"Globalization(Tag)\");\n  {\n    INFO(\"ResidualCompute\");\n    TestHelpers::db::test_compute_tag<\n        Tags::ResidualCompute<Tag, ::Tags::Source<Tag>>>(\n        \"NonlinearResidual(Tag)\");\n    const auto box = db::create<\n        db::AddSimpleTags<::Tags::Source<Tag>, Tags::OperatorAppliedTo<Tag>>,\n        db::AddComputeTags<Tags::ResidualCompute<Tag, ::Tags::Source<Tag>>>>(3,\n                                                                             2);\n    CHECK(db::get<Tags::Residual<Tag>>(box) == 1);\n  }\n}\n\n}  // namespace NonlinearSolver\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_RayTracerBackgroundSpacetimes\")\n\nset(LIBRARY_SOURCES\n  Test_NumericData.cpp\n  Test_WrappedGr.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Domain\n  DomainCreators\n  DomainStructure\n  Framework\n  GeneralRelativity\n  GeneralRelativitySolutions\n  H5\n  RayTracer\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/Test_NumericData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"Domain/Creators/RegisterDerivedWithCharm.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/TensorData.hpp\"\n#include \"IO/H5/VolumeData.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"ParallelAlgorithms/RayTracer/BackgroundSpacetimes/NumericData.hpp\"\n#include \"ParallelAlgorithms/RayTracer/BackgroundSpacetimes/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace ray_tracing {\n\nnamespace {\n\ntemplate <typename DataType>\nusing DerivLapse =\n    ::Tags::deriv<gr::Tags::Lapse<DataType>, tmpl::size_t<3>, Frame::Inertial>;\ntemplate <typename DataType>\nusing DerivShift = ::Tags::deriv<gr::Tags::Shift<DataType, 3>, tmpl::size_t<3>,\n                                 Frame::Inertial>;\ntemplate <typename DataType>\nusing DerivInvSpatialMetric =\n    ::Tags::deriv<gr::Tags::InverseSpatialMetric<DataType, 3>, tmpl::size_t<3>,\n                  Frame::Inertial>;\ntemplate <typename DataType>\nusing DerivSpatialMetric = ::Tags::deriv<gr::Tags::SpatialMetric<DataType, 3>,\n                                         tmpl::size_t<3>, Frame::Inertial>;\ntemplate <typename DataType>\nusing solution_vars_list =\n    tmpl::list<gr::Tags::Lapse<DataType>, DerivLapse<DataType>,\n               gr::Tags::Shift<DataType, 3>, DerivShift<DataType>,\n               gr::Tags::SpatialMetric<DataType, 3>,\n               gr::Tags::InverseSpatialMetric<DataType, 3>,\n               DerivSpatialMetric<DataType>,\n               gr::Tags::ExtrinsicCurvature<DataType, 3>>;\n\nvoid make_test_volume_data_file(const std::string& volfile_name) {\n  // Create a simple volume data file with Schwarzschild spacetime in\n  // Kerr-Schild coordinates for testing.\n  const gr::Solutions::KerrSchild solution{/* mass */ 1.,\n                                           /* spin */ {{0., 0., 0.}},\n                                           /* center */ {{0., 0., 0.}}};\n  const size_t num_points_per_dim = 6;\n  const Mesh<3> mesh{num_points_per_dim, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const domain::creators::Sphere domain_creator{\n      3.0, 4.0, domain::creators::Sphere::Excision{}, 0_st, num_points_per_dim,\n      true};\n  const auto domain = domain_creator.create_domain();\n  const auto element_ids =\n      initial_element_ids(domain_creator.initial_refinement_levels());\n  h5::H5File<h5::AccessType::ReadWrite> h5file(volfile_name);\n  auto& volfile = h5file.insert<h5::VolumeData>(\"/VolumeData\", 0);\n  std::vector<ElementVolumeData> element_voldata;\n  element_voldata.reserve(element_ids.size());\n  for (const auto& element_id : element_ids) {\n    const auto logical_coords = logical_coordinates(mesh);\n    const ElementMap<3, Frame::Inertial> element_map{\n        element_id, domain.blocks()[element_id.block_id()]};\n    const auto inertial_coords = element_map(logical_coords);\n    std::vector<TensorComponent> tensor_components;\n    const auto solution_vars = solution.variables(\n        inertial_coords, 0.0, solution_vars_list<DataVector>{});\n    const auto vars = tuples::tagged_tuple_cat(\n        solution_vars,\n        tuples::TaggedTuple<DerivInvSpatialMetric<DataVector>>{\n            gr::deriv_inverse_spatial_metric(\n                get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(\n                    solution_vars),\n                get<DerivSpatialMetric<DataVector>>(solution_vars))});\n    tmpl::for_each<typename decltype(vars)::tags_list>(\n        [&tensor_components, &vars](auto tag_v) {\n          using Tag = tmpl::type_from<decltype(tag_v)>;\n          const auto& tensor = get<Tag>(vars);\n          const std::string tensor_name = db::tag_name<Tag>();\n          for (size_t i = 0; i < tensor.size(); ++i) {\n            tensor_components.emplace_back(\n                tensor_name + tensor.component_suffix(i), tensor[i]);\n          }\n        });\n    element_voldata.emplace_back(element_id, std::move(tensor_components),\n                                 mesh);\n  }\n  volfile.write_volume_data(0, 0.0, element_voldata, serialize(domain));\n}\n\nvoid test_numeric_data(const std::string& options_string) {\n  // Test factory-creation\n  auto created =\n      TestHelpers::test_factory_creation<BackgroundSpacetime, NumericData>(\n          options_string);\n  REQUIRE(dynamic_cast<const NumericData*>(created.get()) != nullptr);\n  auto& background_spacetime = dynamic_cast<NumericData&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(background_spacetime);\n    test_copy_semantics(background_spacetime);\n    auto move_background_spacetime = background_spacetime;\n    test_move_semantics(std::move(move_background_spacetime),\n                        background_spacetime);\n    const auto clone = background_spacetime.get_clone();\n    REQUIRE(dynamic_cast<const NumericData*>(clone.get()) != nullptr);\n    CHECK(dynamic_cast<const NumericData&>(*clone) == background_spacetime);\n  }\n  {\n    INFO(\"Variables\");\n    const tnsr::I<double, 3> x{{1.0, 2.0, 3.0}};\n    background_spacetime.initialize({0.0, 1.0});\n    const auto vars = background_spacetime.variables(x, 0.0);\n    const gr::Solutions::KerrSchild solution{/* mass */ 1.,\n                                             /* spin */ {{0., 0., 0.}},\n                                             /* center */ {{0., 0., 0.}}};\n    const auto solution_vars =\n        solution.variables(x, 0.0, solution_vars_list<double>{});\n    const auto expected_vars = tuples::tagged_tuple_cat(\n        solution_vars,\n        tuples::TaggedTuple<DerivInvSpatialMetric<double>>{\n            gr::deriv_inverse_spatial_metric(\n                get<gr::Tags::InverseSpatialMetric<double, 3>>(solution_vars),\n                get<DerivSpatialMetric<double>>(solution_vars))});\n    const Approx custom_approx = Approx::custom().epsilon(1e-4).scale(1.0);\n    tmpl::for_each<BackgroundSpacetime::tags>([&](auto tag_v) {\n      using Tag = tmpl::type_from<decltype(tag_v)>;\n      CHECK_ITERABLE_CUSTOM_APPROX(get<Tag>(vars), get<Tag>(expected_vars),\n                                   custom_approx);\n    });\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.RayTracer.BackgroundSpacetimes.NumericData\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  domain::creators::register_derived_with_charm();\n  const std::string volfile_name =\n      \"Unit.RayTracer.BackgroundSpacetimes.NumericData.h5\";\n  if (file_system::check_if_file_exists(volfile_name)) {\n    file_system::rm(volfile_name, true);\n  }\n  make_test_volume_data_file(volfile_name);\n  test_numeric_data(\n      \"NumericData:\\n\"\n      \"  FileGlob: \" +\n      volfile_name +\n      \"\\n\"\n      \"  SubfileName: VolumeData\\n\"\n      \"  ObservationStep: -1\\n\"\n      \"  Verbosity: Debug\\n\");\n  if (file_system::check_if_file_exists(volfile_name)) {\n    file_system::rm(volfile_name, true);\n  }\n}\n\n}  // namespace ray_tracing\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/RayTracer/BackgroundSpacetimes/Test_WrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"ParallelAlgorithms/RayTracer/BackgroundSpacetimes/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n\nnamespace ray_tracing {\nnamespace {\n\nusing DerivSpatialMetric =\n    ::Tags::deriv<gr::Tags::SpatialMetric<double, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>;\nusing DerivInvSpatialMetric =\n    ::Tags::deriv<gr::Tags::InverseSpatialMetric<double, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>;\n\ntemplate <typename SolutionType>\nvoid test_wrapped_gr(const std::string& options_string) {\n  CAPTURE(pretty_type::name<SolutionType>());\n  // Test factory-creation\n  const auto created = TestHelpers::test_factory_creation<\n      BackgroundSpacetime, WrappedGr<SolutionType>>(options_string);\n  REQUIRE(dynamic_cast<const WrappedGr<SolutionType>*>(created.get()) !=\n          nullptr);\n  const auto& background_spacetime =\n      dynamic_cast<const WrappedGr<SolutionType>&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(background_spacetime);\n    test_copy_semantics(background_spacetime);\n    auto move_background_spacetime = background_spacetime;\n    test_move_semantics(std::move(move_background_spacetime),\n                        background_spacetime);\n    const auto clone = background_spacetime.get_clone();\n    REQUIRE(dynamic_cast<const WrappedGr<SolutionType>*>(clone.get()) !=\n            nullptr);\n    CHECK(dynamic_cast<const WrappedGr<SolutionType>&>(*clone) ==\n          background_spacetime);\n  }\n  {\n    INFO(\"Variables\");\n    const tnsr::I<double, 3> x{{3.0, 4.0, 5.0}};\n    const auto vars = background_spacetime.variables(x, 0.0);\n    const auto solution_vars =\n        background_spacetime.wrapped_solution().variables(\n            x, 0.0,\n            tmpl::push_back<\n                tmpl::remove<BackgroundSpacetime::tags, DerivInvSpatialMetric>,\n                DerivSpatialMetric>{});\n    const auto expected_vars = tuples::tagged_tuple_cat(\n        solution_vars,\n        tuples::TaggedTuple<DerivInvSpatialMetric>{\n            gr::deriv_inverse_spatial_metric(\n                get<gr::Tags::InverseSpatialMetric<double, 3>>(solution_vars),\n                get<DerivSpatialMetric>(solution_vars))});\n    tmpl::for_each<BackgroundSpacetime::tags>([&](auto tag_v) {\n      using Tag = tmpl::type_from<decltype(tag_v)>;\n      CHECK_ITERABLE_APPROX(get<Tag>(vars), get<Tag>(expected_vars));\n    });\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.RayTracer.BackgroundSpacetimes.WrappedGr\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  test_wrapped_gr<gr::Solutions::KerrSchild>(\n      \"KerrSchild:\\n\"\n      \"  Mass: 1.0\\n\"\n      \"  Spin: [0.0, 0.0, 0.5]\\n\"\n      \"  Center: [1.0, 0.0, 0.0]\\n\"\n      \"  Velocity: [0.0, 0.0, 0.2]\\n\");\n  test_wrapped_gr<gr::Solutions::Minkowski<3>>(\"Minkowski\");\n}\n\n}  // namespace ray_tracing\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/RayTracer/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(BackgroundSpacetimes)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/SurfaceFinder/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\nset(LIBRARY \"Test_SurfaceFinder\")\n\nset(LIBRARY_SOURCES\n  Test_SurfaceFinder.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainCreators\n  MathFunctions\n  Spectral\n  SurfaceFinder\n  )\n\nspectre_add_python_bindings_test(\n  \"SurfaceFinder.FindRadialSurface\"\n  Test_FindRadialSurface.py\n  \"Python\"\n  None\n  TIMEOUT 20)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/SurfaceFinder/Test_FindRadialSurface.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Domain import ElementId, ElementMap, serialize_domain\nfrom spectre.Domain.Creators import Sphere\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.IO.H5.IterElements import Element\nfrom spectre.NumericalAlgorithms.LinearOperators import partial_derivative\nfrom spectre.PointwiseFunctions.AnalyticSolutions.GeneralRelativity import (\n    KerrSchild,\n)\nfrom spectre.Spectral import Basis, Mesh, Quadrature, logical_coordinates\nfrom spectre.SphericalHarmonics import Frame, Strahlkorper, cartesian_coords\nfrom spectre.support.Logging import configure_logging\nfrom spectre.SurfaceFinder.FindRadialSurface import (\n    find_radial_surface,\n    find_radial_surface_command,\n)\n\n\ndef _to_tensor_components(tensors):\n    \"\"\"Convert a dictionary of tensors to a list of tensor components\"\"\"\n    result = []\n    for name, tensor in tensors.items():\n        result.extend(\n            spectre_h5.TensorComponent(\n                name + tensor.component_suffix(i), tensor[i]\n            )\n            for i in range(len(tensor))\n        )\n    return result\n\n\nclass TestFindRadialSurface(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(unit_test_build_path(), \"SurfaceFinder\")\n        self.h5_filename = os.path.join(self.test_dir, \"TestData.h5\")\n        self.output_surfaces_filename = os.path.join(\n            self.test_dir, \"Surfaces.h5\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n\n        # Create a spherical domain\n        domain = Sphere(\n            inner_radius=1.0,\n            outer_radius=3.0,\n            excise=True,\n            initial_refinement=0,\n            initial_number_of_grid_points=8,\n            use_equiangular_map=True,\n        ).create_domain()\n        element_ids = [ElementId[3](block_id) for block_id in range(6)]\n        elements = [\n            Element(\n                id=element_id,\n                mesh=Mesh[3](10, Basis.Legendre, Quadrature.GaussLobatto),\n                map=ElementMap(element_id, domain),\n            )\n            for element_id in element_ids\n        ]\n\n        # Evaluate a Kerr solution on the domain and write to file\n        solution = KerrSchild(mass=1.0, dimensionless_spin=[0.0, 0.0, 0.0])\n        tensor_names = [\"Lapse\"]\n        with spectre_h5.H5File(self.h5_filename, \"w\") as h5file:\n            volfile = h5file.insert_vol(\"element_data\", version=0)\n            volfile.write_volume_data(\n                observation_id=0,\n                observation_value=0.0,\n                elements=[\n                    spectre_h5.ElementVolumeData(\n                        element.id,\n                        _to_tensor_components(\n                            solution.variables(\n                                element.inertial_coordinates,\n                                tensor_names,\n                            )\n                        ),\n                        element.mesh,\n                    )\n                    for element in elements\n                ],\n                serialized_domain=serialize_domain(domain),\n            )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_find_radial_surface(self):\n        surface = find_radial_surface(\n            [self.h5_filename],\n            subfile_name=\"element_data\",\n            obs_id=0,\n            obs_time=0.0,\n            var_name=\"Lapse\",\n            # Value of the lapse at the horizon\n            target=1 / np.sqrt(2),\n            initial_guess=Strahlkorper[Frame.Inertial](\n                l_max=12, radius=1.0, center=[0.0, 0.0, 0.0]\n            ),\n            output_surfaces_file=self.output_surfaces_filename,\n            output_coeffs_subfile=\"SurfaceCoeffs\",\n            output_coords_subfile=\"SurfaceCoords\",\n        )\n        # Horizon should be a sphere of coordinate radius 2.0\n        npt.assert_allclose(\n            np.linalg.norm(cartesian_coords(surface), axis=0), 2.0, atol=1e-3\n        )\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            find_radial_surface_command,\n            [\n                self.h5_filename,\n                \"-d\",\n                \"element_data\",\n                \"--step\",\n                \"0\",\n                \"-t\",\n                str(1 / np.sqrt(2)),\n                \"-y\",\n                \"Lapse\",\n                \"--l-max\",\n                \"12\",\n                \"--initial-radius\",\n                \"1.0\",\n                \"--center\",\n                \"0.0\",\n                \"0.0\",\n                \"0.0\",\n                \"--output-surfaces-file\",\n                self.output_surfaces_filename,\n                \"--output-coeffs-subfile\",\n                \"SurfaceCoeffs\",\n                \"--output-coords-subfile\",\n                \"SurfaceCoords\",\n            ],\n            catch_exceptions=True,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(self.output_surfaces_filename))\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/ParallelAlgorithms/SurfaceFinder/Test_SurfaceFinder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Index.hpp\"\n#include \"DataStructures/Matrix.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"Domain/BlockLogicalCoordinates.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Domain/ElementLogicalCoordinates.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/CreateInitialMesh.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"ParallelAlgorithms/SurfaceFinder/SurfaceFinder.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n\nnamespace {\n// Builds a Tensor out of the ray directions and outputted zeta coordinates.\n// Only used when we expect a root to be found along all rays.\ntnsr::I<DataVector, 3, Frame::ElementLogical> result_helper(\n    const tnsr::I<DataVector, 2, Frame::ElementLogical>& ray_directions,\n    const std::vector<std::optional<double>>& zeta_raw) {\n  DataVector zeta(zeta_raw.size());\n  for (size_t i = 0; i < zeta_raw.size(); i++) {\n    zeta[i] = zeta_raw[i].value();\n  }\n  tnsr::I<DataVector, 3, Frame::ElementLogical> result{\n      {{get<0>(ray_directions), get<1>(ray_directions), zeta}}};\n  return result;\n}\n\nvoid test_bulging_surface(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 2, Frame::ElementLogical>& ray_directions,\n    const ElementMap<3, Frame::Inertial>& element_map) {\n  INFO(\n      \"Find the level-surface of a Gaussian offset from the origin, such that \"\n      \"the surface bulges in and out of the element. This tests if the \"\n      \"function can find a surface partially in an element.\");\n\n  const MathFunctions::Gaussian<3, Frame::Inertial> offset_gaussian{\n      10., 5., std::array<double, 3>{0., 0., -1.}};\n  const auto data = offset_gaussian(inertial_coords);\n  const double target = 2.5;\n  const auto radius_zeta =\n      SurfaceFinder::find_radial_surface(data, target, mesh, ray_directions);\n\n  const tnsr::I<double, 3, Frame::ElementLogical> result{\n      {{get<0>(ray_directions)[1], get<1>(ray_directions)[1],\n        radius_zeta[1].value()}}};\n  const auto inertial_contour = element_map(result);\n  const auto radius_values = get(magnitude(inertial_contour));\n\n  Approx custom_approx = Approx::custom().epsilon(1.e-5).scale(1.0);\n  CHECK(radius_zeta[0].has_value() == false);\n  CHECK(radius_zeta[1].has_value() == true);\n  CHECK(radius_values == custom_approx(sqrt(-25. * log(0.25)) - 1.));\n  CHECK(radius_zeta[2].has_value() == false);\n}\n\nvoid test_radius_contour(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const Mesh<3>& mesh,\n    const tnsr::I<DataVector, 2, Frame::ElementLogical>& ray_directions,\n    const ElementMap<3, Frame::Inertial>& element_map) {\n  INFO(\"Find certain radius contour.\");\n\n  const auto data = magnitude(inertial_coords);\n  const double target = 4.0;\n\n  const auto radius_zeta =\n      SurfaceFinder::find_radial_surface(data, target, mesh, ray_directions);\n  const auto result = result_helper(ray_directions, radius_zeta);\n  const auto inertial_contour = element_map(result);\n  const auto radius_values = get(magnitude(inertial_contour));\n\n  Approx custom_approx = Approx::custom().epsilon(1.e-6).scale(1.0);\n  for (size_t i = 0; i < radius_zeta.size(); i++) {\n    CHECK(radius_zeta[i].has_value());\n    CHECK(radius_values[i] == custom_approx(target));\n  }\n}\n\nvoid test_strahlkorper_input(\n    const tnsr::I<DataVector, 3, Frame::Inertial>& inertial_coords,\n    const Mesh<3>& mesh, const Domain<3>& domain, const ElementId<3>& id,\n    const ElementMap<3, Frame::Inertial>& element_map) {\n  INFO(\n      \"Take the collocation points from a Strahlkorper, find a particular \"\n      \"radius, and compare to the physical radius from the Strahlkorper.\");\n\n  const double target = 4.3;\n  const auto data = magnitude(inertial_coords);\n\n  const ylm::Strahlkorper<Frame::Inertial> strahlkorper{\n      5, 5, 4.5, std::array<double, 3>{0., 0., 0.}};\n  const auto& ylm = strahlkorper.ylm_spherepack();\n  const auto& [theta, phi] = ylm.theta_phi_points();\n  const auto x = target * sin(theta) * cos(phi);\n  const auto y = target * sin(theta) * sin(phi);\n  const auto z = target * cos(theta);\n  const tnsr::I<DataVector, 3, Frame::Inertial> points{{{x, y, z}}};\n  const std::vector<ElementId<3>> id_vector{id};\n  const auto block_coords = block_logical_coordinates(domain, points);\n  const auto element_coords =\n      element_logical_coordinates(id_vector, block_coords);\n  const tnsr::I<DataVector, 2, Frame::ElementLogical> ray_directions{\n      {{get<0>(element_coords.at(id).element_logical_coords),\n        get<1>(element_coords.at(id).element_logical_coords)}}};\n\n  const auto radius_zeta =\n      SurfaceFinder::find_radial_surface(data, target, mesh, ray_directions);\n  const auto result = result_helper(ray_directions, radius_zeta);\n  const auto inertial_contour = element_map(result);\n  const auto radius_values = get(magnitude(inertial_contour));\n\n  Approx custom_approx = Approx::custom().epsilon(1.e-6).scale(1.0);\n  for (size_t i = 0; i < radius_zeta.size(); i++) {\n    CHECK(radius_zeta[i].has_value());\n    CHECK(radius_values[i] == custom_approx(target));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ParallelAlgorithms.SurfaceFinder.SurfaceFinder\",\n                  \"[Unit][ParallelAlgorithms]\") {\n  // Build wedge for tests.\n  static constexpr size_t dim = 3;\n  const domain::creators::Sphere sphere{\n      1., 5., domain::creators::Sphere::InnerCube{0.}, 0_st, 12_st, false};\n  const auto domain = sphere.create_domain();\n  const auto refinement_levels = sphere.initial_refinement_levels();\n  const auto extents = sphere.initial_extents();\n  const ElementId<dim> id{0};\n  const auto i1_basis = Spectral::Basis::Legendre;\n  const auto i1_quadrature = Spectral::Quadrature::GaussLobatto;\n  const auto& block = domain.blocks()[id.block_id()];\n  const auto mesh =\n      domain::create_initial_mesh(extents, block, id, i1_basis, i1_quadrature);\n  const ElementMap<dim, Frame::Inertial> element_map{\n      id, block.stationary_map().get_clone()};\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto inertial_coords = element_map(logical_coords);\n\n  // Ray directions used for some of the tests\n  const tnsr::I<DataVector, 2, Frame::ElementLogical> ray_directions{\n      {{{-1., 0., 1.}, {0., 0., 0.}}}};\n\n  test_bulging_surface(inertial_coords, mesh, ray_directions, element_map);\n  test_radius_contour(inertial_coords, mesh, ray_directions, element_map);\n  test_strahlkorper_input(inertial_coords, mesh, domain, id, element_map);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Burgers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_BurgersAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_Sinusoid.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  BurgersAnalyticData\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Burgers/Sinusoid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef u_variable(x_grid):\n    return np.sin(x_grid[0])\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Burgers/Test_Sinusoid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Burgers/Sinusoid.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct BurgersSinusoidProxy : Burgers::AnalyticData::Sinusoid {\n  tuples::TaggedTuple<Burgers::Tags::U> variables(\n      const tnsr::I<DataVector, 1, Frame::Inertial>& x) const {\n    return Burgers::AnalyticData::Sinusoid::variables(\n        x, tmpl::list<Burgers::Tags::U>{});\n  }\n};\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.Burgers.Sinusoid\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  test_serialization(Burgers::AnalyticData::Sinusoid{});\n  register_classes_with_charm<Burgers::AnalyticData::Sinusoid>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          Burgers::AnalyticData::Sinusoid>(\"Sinusoid:\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& sinusoid = dynamic_cast<const Burgers::AnalyticData::Sinusoid&>(\n      *deserialized_option_solution);\n  CHECK(sinusoid == Burgers::AnalyticData::Sinusoid{});\n  test_move_semantics(Burgers::AnalyticData::Sinusoid{},\n                      Burgers::AnalyticData::Sinusoid{});\n\n  pypp::check_with_random_values<1>(\n      &BurgersSinusoidProxy::variables, BurgersSinusoidProxy{},\n      \"PointwiseFunctions.AnalyticData.Burgers.Sinusoid\", {\"u_variable\"},\n      {{{0.0, M_PI}}}, {}, DataVector(5));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Burgers/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_AnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_AnalyticData.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  AnalyticData\n  AnalyticSolutions\n  DataStructures\n  DataStructuresHelpers\n  )\n\nadd_subdirectory(Burgers)\nadd_subdirectory(CurvedWaveEquation)\nadd_subdirectory(ForceFree)\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(GrMhd)\nadd_subdirectory(NewtonianEuler)\nadd_subdirectory(Punctures)\nadd_subdirectory(RadiationTransport)\nadd_subdirectory(ScalarTensor)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/CurvedWaveEquation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_CurvedWaveEquationAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_PureSphericalHarmonic.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CurvedWaveEquationAnalyticData\n  GeneralRelativitySolutions\n  GeneralRelativity\n  MathFunctions\n  Utilities\n  WaveEquationSolutions\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\ntry:\n    from scipy.special import sph_harm_y\nexcept ImportError:\n    # SciPy < 1.15\n    from scipy.special import sph_harm\n\n    def sph_harm_y(n, m, theta, phi):\n        return sph_harm(m, n, phi, theta)\n\n\ndef pi(x, radius, width, l, m):\n    radial = np.exp(-((np.linalg.norm(x) - radius) ** 2) / width**2)\n    theta = np.arctan2(np.sqrt(x[0] ** 2 + x[1] ** 2), x[2])\n    phi = np.arctan2(x[1], x[0])\n    angular = sph_harm_y(l, m, theta, phi)\n    return radial * np.real(angular)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/CurvedWaveEquation/Test_PureSphericalHarmonic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticData/CurvedWaveEquation/PureSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<tmpl::pair<\n        evolution::initial_data::InitialData,\n        tmpl::list<CurvedScalarWave::AnalyticData::PureSphericalHarmonic>>>;\n  };\n};\n\nvoid test_create_from_options() {\n  INFO(\"Testing option creation\");\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag<\n          evolution::initial_data::OptionTags::InitialData, Metavariables>(\n          \"PureSphericalHarmonic:\\n\"\n          \"  Radius: 0.8 \\n\"\n          \"  Width: 2. \\n\"\n          \"  Mode: [10, 8]\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& option_parsed = dynamic_cast<\n      const CurvedScalarWave::AnalyticData::PureSphericalHarmonic&>(\n      *deserialized_option_solution);\n  const CurvedScalarWave::AnalyticData::PureSphericalHarmonic constructed{\n      0.8, 2., {10, 8}};\n  CHECK(option_parsed == constructed);\n  CHECK_FALSE(option_parsed != constructed);\n\n  INFO(\"Testing serialization\");\n  test_serialization(constructed);\n}\n\n// `check_with_random_values` does not allow us to forward the l and m arguments\n// of type size_t / int to python, so we generate the random numbers and call\n// the python function manually here\nvoid test_variables() {\n  INFO(\"Testing random values against python function\");\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> x_dist{1., 10.};\n  std::uniform_real_distribution<double> radius_dist{1., 10.};\n  std::uniform_real_distribution<double> width_dist{2., 20.};\n  // size of the DataVector we are testing with\n  const size_t dv_size = 10;\n  for (size_t l = 0; l < 8; l++) {\n    for (int m = -l; m <= static_cast<int>(l); ++m) {\n      const auto x = make_with_random_values<tnsr::I<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&x_dist), DataVector(dv_size));\n      const double radius = radius_dist(gen);\n      const double width = width_dist(gen);\n      const auto py_res = pypp::call<Scalar<DataVector>>(\n          \"PureSphericalHarmonic\", \"pi\", x, radius, width, l, m);\n\n      const CurvedScalarWave::AnalyticData::PureSphericalHarmonic sh{\n          radius, width, {l, m}};\n      const auto sh_res = sh.variables(x, {});\n\n      CHECK_ITERABLE_APPROX(get<CurvedScalarWave::Tags::Pi>(sh_res), py_res);\n      CHECK(get<CurvedScalarWave::Tags::Phi<3>>(sh_res) ==\n            make_with_value<tnsr::i<DataVector, 3>>(dv_size, 0.));\n      CHECK(get<CurvedScalarWave::Tags::Psi>(sh_res) ==\n            make_with_value<Scalar<DataVector>>(dv_size, 0.));\n    }\n  }\n}\n\nvoid test_errors() {\n  INFO(\"Testing configuration errors\");\n  CHECK_THROWS_WITH(CurvedScalarWave::AnalyticData::PureSphericalHarmonic(\n                        -0.1, 1., {1, 1}, Options::Context{false, {}, 1, 1}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The radius must be greater than 0 but is -0.1\"));\n\n  CHECK_THROWS_WITH(CurvedScalarWave::AnalyticData::PureSphericalHarmonic(\n                        1., -0.1, {1, 1}, Options::Context{false, {}, 1, 1}),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The width must be greater than 0 but is -0.1\"));\n\n  CHECK_THROWS_WITH(\n      CurvedScalarWave::AnalyticData::PureSphericalHarmonic(\n          1., 1., {7, 8}, Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\n          \"The absolute value of the m_mode must be less than or equal to the \"\n          \"l-mode but the m-mode is 8 and the l-mode is 7\"));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.CurvedWaveEquation.SphericalHarmonic\",\n    \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/CurvedWaveEquation\"};\n  register_factory_classes_with_charm<Metavariables>();\n  test_create_from_options();\n  test_variables();\n  test_errors();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/CurvedWaveEquation/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ForceFree/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ForceFreeAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_FfeBreakdown.cpp\n  Test_MagnetosphericWald.cpp\n  Test_RotatingDipole.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ForceFreeAnalyticData\n  Options\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ForceFree/FfeBreakdown.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef initial_profile(x):\n    result = np.where(x > -0.1, -1.5 * x + 0.85, 1.0)\n    result = np.where(x > 0.1, 0.7, result)\n    return result\n\n\ndef TildeE(x):\n    electric_field = x * 0.0\n    electric_field[1] = 0.5\n    electric_field[2] = -0.5\n    return electric_field\n\n\ndef TildeB(x):\n    magnetic_field = x * 0.0\n    magnetic_field[0] = 1.0\n    magnetic_field[1] = np.where(x[0] < -0.1, 1.0, -10 * x[0])\n    magnetic_field[1] = np.where(x[0] > 0.1, -1.0, magnetic_field[1])\n    magnetic_field[2] = magnetic_field[1]\n    return magnetic_field\n\n\ndef TildePsi(x):\n    return x[0] * 0\n\n\ndef TildePhi(x):\n    return x[0] * 0\n\n\ndef TildeQ(x):\n    return x[0] * 0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ForceFree/MagnetosphericWald.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.tcoordst for details.\n\nimport numpy as np\n\na = 0.5\nB0 = 1.0\nM = 1.0\n\n\ndef TildeE(coords, spin):\n    return coords * 0.0\n\n\ndef TildeB(coords, spin):\n    x, y, z = coords\n    r = np.sqrt(np.dot(coords, coords))\n\n    tildeb = coords * 0.0\n\n    tildeb[0] = (\n        a\n        * B0\n        * z\n        * (\n            (a * x - r * y)\n            * (\n                1 / r**4\n                + 2\n                * M\n                * r\n                * (r**2 - a**2)\n                / (r**4 + a**2 * z**2) ** 2\n            )\n            + a\n            * M\n            * r\n            * x\n            * (\n                (r**2 - z**2) / (r**4 * (r**4 + a**2 * z**2))\n                - 4 * (r**2 + z**2) / (r**4 + a**2 * z**2) ** 2\n            )\n        )\n    )\n\n    tildeb[1] = (\n        a\n        * B0\n        * z\n        * (\n            (r * x + a * y)\n            * (\n                1 / r**4\n                + 2\n                * M\n                * r\n                * (r**2 - a**2)\n                / (r**4 + a**2 * z**2) ** 2\n            )\n            + a\n            * M\n            * r\n            * y\n            * (\n                (r**2 - z**2) / (r**4 * (r**4 + a**2 * z**2))\n                - 4 * (r**2 + z**2) / (r**4 + a**2 * z**2) ** 2\n            )\n        )\n    )\n\n    tildeb[2] = B0 * (\n        1\n        + a**2 * z**2 / r**4\n        + (M * a**2 / r**3)\n        * (\n            1\n            - z**2\n            * (a**2 + z**2)\n            * (5 * r**4 + a**2 * z**2)\n            / (r**4 + a**2 * z**2) ** 2\n        )\n    )\n\n    return tildeb\n\n\ndef TildePsi(coords, spin):\n    return 0.0\n\n\ndef TildePhi(coords, spin):\n    return 0.0\n\n\ndef TildeQ(coords, spin):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ForceFree/RotatingDipole.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom scipy.spatial.transform import Rotation\n\nA0 = 1.1\nvarpi0 = 0.3\ndelta = 0.07\nOmega = 0.123\nalpha = 0.456\n\n\ndef TildeE(\n    x,\n    vector_potential_amplitude,\n    varpi0,\n    delta,\n    angular_velocity,\n    tilt_angle,\n):\n    return x * 0.0\n\n\ndef TildeB(\n    x,\n    vector_potential_amplitude,\n    varpi0,\n    delta,\n    angular_velocity,\n    tilt_angle,\n):\n    tilde_b = x * 0.0\n\n    r = np.sqrt(np.einsum(\"a, a\", x, x))\n\n    tilt = Rotation.from_rotvec(alpha * np.array([0, 1, 0]))\n    x_prime = tilt.inv().apply(x)\n    xp, yp, zp = x_prime\n\n    tilde_b[0] = 3.0 * xp * zp\n    tilde_b[1] = 3.0 * yp * zp\n    tilde_b[2] = 3.0 * zp**2 - r**2 + 2.0 * delta**2\n    tilde_b = tilde_b / (r**2 + delta**2) ** (5 / 2)\n\n    return tilt.apply(tilde_b)\n\n\ndef TildePsi(\n    x,\n    vector_potential_amplitude,\n    varpi0,\n    delta,\n    angular_velocity,\n    tilt_angle,\n):\n    return 0.0\n\n\ndef TildePhi(\n    x,\n    vector_potential_amplitude,\n    varpi0,\n    delta,\n    angular_velocity,\n    tilt_angle,\n):\n    return 0.0\n\n\ndef TildeQ(\n    x,\n    vector_potential_amplitude,\n    varpi0,\n    delta,\n    angular_velocity,\n    tilt_angle,\n):\n    return 0.0\n\n\ndef InteriorMask(x):\n    r_squared = np.einsum(\"a, a\", x, x)\n\n    if r_squared < 1.0:\n        return -1.0\n    else:\n        return 1.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ForceFree/Test_FfeBreakdown.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/FfeBreakdown.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing FfeBreakdown = ForceFree::AnalyticData::FfeBreakdown;\n\nstruct FfeBreakdownProxy : FfeBreakdown {\n  using FfeBreakdown::FfeBreakdown;\n  using variables_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildePsi, ForceFree::Tags::TildePhi,\n                 ForceFree::Tags::TildeQ>;\n\n  tuples::tagged_tuple_from_typelist<variables_tags> return_variables(\n      const tnsr::I<DataVector, 3>& x) const {\n    return this->variables(x, variables_tags{});\n  }\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.ForceFree.FfeBreakdown\",\n    \"[Unit][PointwiseFunctions]\") {\n  // test creation\n  const auto solution = TestHelpers::test_creation<FfeBreakdown>(\"\");\n  CHECK(solution == FfeBreakdown());\n  // test serialize\n  test_serialization(solution);\n  // test move\n  test_move_semantics(FfeBreakdown{}, FfeBreakdown{});\n  // test derived\n  register_classes_with_charm<FfeBreakdown>();\n  const std::unique_ptr<InitialData> initial_data_ptr =\n      std::make_unique<FfeBreakdown>();\n  const std::unique_ptr<InitialData> deserialized_initial_data_ptr =\n      serialize_and_deserialize(initial_data_ptr)->get_clone();\n  CHECK(dynamic_cast<FfeBreakdown*>(deserialized_initial_data_ptr.get()) !=\n        nullptr);\n\n  // test solution\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/ForceFree\"};\n  const DataVector used_for_size{10};\n  pypp::check_with_random_values<1>(\n      &FfeBreakdownProxy::return_variables, FfeBreakdownProxy(), \"FfeBreakdown\",\n      {\"TildeE\", \"TildeB\", \"TildePsi\", \"TildePhi\", \"TildeQ\"}, {{{-10.0, 10.0}}},\n      {}, used_for_size);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ForceFree/Test_MagnetosphericWald.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/MagnetosphericWald.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing MagnetosphericWald = ForceFree::AnalyticData::MagnetosphericWald;\n\nstruct MagnetosphericWaldProxy : MagnetosphericWald {\n  using MagnetosphericWald::MagnetosphericWald;\n  using variables_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildePsi, ForceFree::Tags::TildePhi,\n                 ForceFree::Tags::TildeQ>;\n\n  tuples::tagged_tuple_from_typelist<variables_tags> return_variables(\n      const tnsr::I<DataVector, 3>& x) const {\n    return this->variables(x, variables_tags{});\n  }\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.ForceFree.MagnetosphericWald\",\n    \"[Unit][PointwiseFunctions]\") {\n  // test creation\n  const auto solution =\n      TestHelpers::test_creation<MagnetosphericWald>(\"Spin: 0.5\");\n  CHECK(solution == MagnetosphericWald(0.5));\n  CHECK(solution != MagnetosphericWald(0.1));\n\n  CHECK_THROWS_WITH(\n      []() { const MagnetosphericWald soln(2.0); }(),\n      Catch::Matchers::ContainsSubstring(\"Spin magnitude must be\"));\n\n  // test serialize\n  test_serialization(solution);\n\n  // test move\n  test_move_semantics(MagnetosphericWald{0.5}, MagnetosphericWald{0.5});\n\n  // test derived\n  register_classes_with_charm<MagnetosphericWald>();\n  const std::unique_ptr<InitialData> base_ptr =\n      std::make_unique<MagnetosphericWald>(0.5);\n  const std::unique_ptr<InitialData> deserialized_base_ptr =\n      serialize_and_deserialize(base_ptr)->get_clone();\n  CHECK(dynamic_cast<const MagnetosphericWald&>(*deserialized_base_ptr.get()) ==\n        dynamic_cast<const MagnetosphericWald&>(*base_ptr.get()));\n\n  // test solution\n  const DataVector used_for_size{10};\n\n  const double bh_spin = 0.5;\n  const auto member_variables = std::make_tuple(bh_spin);\n\n  MagnetosphericWaldProxy magnetospheric_wald(bh_spin);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/ForceFree\"};\n\n  // Check for the EM variables\n  pypp::check_with_random_values<1>(\n      &MagnetosphericWaldProxy::return_variables, magnetospheric_wald,\n      \"MagnetosphericWald\",\n      {\"TildeE\", \"TildeB\", \"TildePsi\", \"TildePhi\", \"TildeQ\"}, {{{-10.0, 10.0}}},\n      member_variables, used_for_size);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ForceFree/Test_RotatingDipole.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ForceFree/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ForceFree/RotatingDipole.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing RotatingDipole = ForceFree::AnalyticData::RotatingDipole;\n\nstruct RotatingDipoleProxy : RotatingDipole {\n  using RotatingDipole::RotatingDipole;\n  using variables_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildePsi, ForceFree::Tags::TildePhi,\n                 ForceFree::Tags::TildeQ>;\n\n  tuples::tagged_tuple_from_typelist<variables_tags> return_variables(\n      const tnsr::I<DataVector, 3>& x) const {\n    return this->variables(x, variables_tags{});\n  }\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.ForceFree.RotatingDipole\",\n    \"[Unit][PointwiseFunctions]\") {\n  // test creation\n  const auto solution = TestHelpers::test_creation<RotatingDipole>(\n      \"VectorPotentialAmplitude: 1.0\\n\"\n      \"Varpi0 : 0.5\\n\"\n      \"Delta : 0.1\\n\"\n      \"AngularVelocity : 0.3\\n\"\n      \"TiltAngle : 0.0\");\n  CHECK(solution == RotatingDipole(1.0, 0.5, 0.1, 0.3, 0.0));\n  CHECK(solution != RotatingDipole(2.0, 0.5, 0.1, 0.3, 0.0));\n  CHECK(solution != RotatingDipole(1.0, 1.0, 0.1, 0.3, 0.0));\n  CHECK(solution != RotatingDipole(1.0, 0.5, 1.0, 0.3, 0.0));\n  CHECK(solution != RotatingDipole(1.0, 0.5, 0.1, 0.5, 0.0));\n  CHECK(solution != RotatingDipole(1.0, 0.5, 0.1, 0.3, 1.0));\n\n  CHECK_THROWS_WITH(\n      []() { const RotatingDipole soln(1.0, -0.5, 0.1, 0.3, 0.0); }(),\n      Catch::Matchers::ContainsSubstring(\"The length constant varpi0\"));\n\n  CHECK_THROWS_WITH(\n      []() { const RotatingDipole soln(1.0, 0.5, -0.1, 0.3, 0.0); }(),\n      Catch::Matchers::ContainsSubstring(\"The small number delta\"));\n\n  CHECK_THROWS_WITH(\n      []() { const RotatingDipole soln(1.0, 0.5, 0.1, 2.0, 0.0); }(),\n      Catch::Matchers::ContainsSubstring(\"must be between -1.0 and 1.0\"));\n\n  CHECK_THROWS_WITH(\n      []() { const RotatingDipole soln(1.0, 0.5, 0.1, 0.3, 7.0); }(),\n      Catch::Matchers::ContainsSubstring(\"must be between 0 and Pi\"));\n\n  // test serialize\n  test_serialization(solution);\n\n  // test move\n  test_move_semantics(RotatingDipole{1.0, 0.5, 0.1, 0.3, 0.0},\n                      RotatingDipole{1.0, 0.5, 0.1, 0.3, 0.0});\n\n  // test derived\n  register_classes_with_charm<RotatingDipole>();\n  const std::unique_ptr<InitialData> base_ptr =\n      std::make_unique<RotatingDipole>(1.0, 0.5, 0.1, 0.3, 0.0);\n  const std::unique_ptr<InitialData> deserialized_base_ptr =\n      serialize_and_deserialize(base_ptr)->get_clone();\n  CHECK(dynamic_cast<const RotatingDipole&>(*deserialized_base_ptr.get()) ==\n        dynamic_cast<const RotatingDipole&>(*base_ptr.get()));\n\n  // test solution\n  const DataVector used_for_size{10};\n\n  const double vector_potential_amplitude = 1.1;\n  const double varpi0 = 0.3;\n  const double delta = 0.07;\n  const double angular_velocity = 0.123;\n  const double tilt_angle = 0.456;\n  const auto member_variables = std::make_tuple(\n      vector_potential_amplitude, varpi0, delta, angular_velocity, tilt_angle);\n\n  RotatingDipoleProxy rotating_dipole(vector_potential_amplitude, varpi0, delta,\n                                      angular_velocity, tilt_angle);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/ForceFree\"};\n\n  // Check for the EM variables\n  pypp::check_with_random_values<1>(\n      &RotatingDipoleProxy::return_variables, rotating_dipole, \"RotatingDipole\",\n      {\"TildeE\", \"TildeB\", \"TildePsi\", \"TildePhi\", \"TildeQ\"}, {{{-10.0, 10.0}}},\n      member_variables, used_for_size);\n\n  // Check the member function `angular_velocity()`\n  CHECK(solution.angular_velocity() == 0.3);\n\n  // Check the member function `interior_mask()`\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-1.0, 1.0);\n  const auto random_coords =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), dist, used_for_size);\n\n  const DataVector r_squared = get(dot_product(random_coords, random_coords));\n\n  if (min(r_squared) < 1.0) {\n    const Scalar<DataVector> mask_from_python{pypp::call<Scalar<DataVector>>(\n        \"RotatingDipole\", \"InteriorMask\", random_coords)};\n    CHECK(solution.interior_mask(random_coords) == mask_from_python);\n  } else {\n    CHECK(solution.interior_mask(random_coords) ==\n          std::optional<Scalar<DataVector>>{});\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ForceFree/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GeneralRelativity/BrillLindquist.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef x_minus_center_a(x, center_a):\n    return x - center_a\n\n\ndef r_a(x, center_a):\n    x_a = x_minus_center_a(x, center_a)\n    return np.sqrt(np.dot(x_a, x_a))\n\n\ndef x_minus_center_b(x, center_b):\n    return x - center_b\n\n\ndef r_b(x, center_b):\n    x_b = x_minus_center_b(x, center_b)\n    return np.sqrt(np.dot(x_b, x_b))\n\n\ndef conformal_factor(x, mass_a, mass_b, center_a, center_b):\n    return (\n        1.0 + 0.5 * mass_a / r_a(x, center_a) + 0.5 * mass_b / r_b(x, center_b)\n    )\n\n\ndef deriv_conformal_factor(x, mass_a, mass_b, center_a, center_b):\n    return -0.5 * mass_a * x_minus_center_a(x, center_a) / np.power(\n        r_a(x, center_a), 3\n    ) - 0.5 * mass_b * x_minus_center_b(x, center_b) / np.power(\n        r_b(x, center_b), 3\n    )\n\n\ndef spatial_metric(x, mass_a, mass_b, center_a, center_b):\n    spatial_metric = np.diag(np.ones_like(x))\n    return spatial_metric * np.power(\n        conformal_factor(x, mass_a, mass_b, center_a, center_b), 4\n    )\n\n\ndef d_spatial_metric(x, mass_a, mass_b, center_a, center_b):\n    d_spatial_metric = np.zeros((len(x), len(x), len(x)))\n    four_psi_cubed = 4.0 * np.power(\n        conformal_factor(x, mass_a, mass_b, center_a, center_b), 3\n    )\n    d_psi = deriv_conformal_factor(x, mass_a, mass_b, center_a, center_b)\n    d_spatial_metric[:, 0, 0] = four_psi_cubed * d_psi\n    d_spatial_metric[:, 1, 1] = four_psi_cubed * d_psi\n    d_spatial_metric[:, 2, 2] = four_psi_cubed * d_psi\n    return d_spatial_metric\n\n\ndef dt_spatial_metric(x, mass_a, mass_b, center_a, center_b):\n    return np.zeros((len(x), len(x)))\n\n\ndef lapse(x, mass_a, mass_b, center_a, center_b):\n    return 1.0\n\n\ndef dt_lapse(x, mass_a, mass_b, center_a, center_b):\n    return 0.0\n\n\ndef d_lapse(x, mass_a, mass_b, center_a, center_b):\n    return np.zeros_like(x)\n\n\ndef shift(x, mass_a, mass_b, center_a, center_b):\n    return np.zeros_like(x)\n\n\ndef dt_shift(x, mass_a, mass_b, center_a, center_b):\n    return np.zeros_like(x)\n\n\ndef d_shift(x, mass_a, mass_b, center_a, center_b):\n    return np.zeros((len(x), len(x)))\n\n\ndef sqrt_det_spatial_metric(x, mass_a, mass_b, center_a, center_b):\n    return np.sqrt(\n        np.linalg.det(spatial_metric(x, mass_a, mass_b, center_a, center_b))\n    )\n\n\ndef extrinsic_curvature(x, mass_a, mass_b, center_a, center_b):\n    return np.zeros((len(x), len(x)))\n\n\ndef inverse_spatial_metric(x, mass_a, mass_b, center_a, center_b):\n    return np.linalg.inv(spatial_metric(x, mass_a, mass_b, center_a, center_b))\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GeneralRelativityAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_BrillLindquist.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  GeneralRelativityAnalyticData\n  DataStructures\n  DataStructuresHelpers\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GeneralRelativity/Test_BrillLindquist.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <random>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GeneralRelativity/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GeneralRelativity/BrillLindquist.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n\nnamespace {\nstruct BrillLindquistProxy : gr::AnalyticData::BrillLindquist {\n  using gr::AnalyticData::BrillLindquist::BrillLindquist;\n\n  template <typename DataType>\n  using variables_tags =\n      typename gr::AnalyticDataBase<3>::template tags<DataType>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>> test_variables(\n      const tnsr::I<DataType, 3>& x) const {\n    return this->variables(x, variables_tags<DataType>{});\n  }\n};\n\ntemplate <typename DataType>\nvoid test_random(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  auto mass_distribution = std::uniform_real_distribution<>(0.5, 1.5);\n  auto center_distribution = std::uniform_real_distribution<>(0.5, 5.5);\n  const double mass_a = mass_distribution(generator);\n  const double mass_b = mass_distribution(generator);\n  const std::array<double, 3> center_a{{center_distribution(generator),\n                                        center_distribution(generator),\n                                        center_distribution(generator)}};\n  const std::array<double, 3> center_b{{center_distribution(generator),\n                                        center_distribution(generator),\n                                        center_distribution(generator)}};\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  pypp::check_with_random_values<1>(\n      &BrillLindquistProxy::test_variables<DataType>,\n      BrillLindquistProxy(mass_a, mass_b, center_a, center_b),\n      \"PointwiseFunctions.AnalyticData.GeneralRelativity.BrillLindquist\",\n      {\"lapse\", \"dt_lapse\", \"d_lapse\", \"shift\", \"dt_shift\", \"d_shift\",\n       \"spatial_metric\", \"dt_spatial_metric\", \"d_spatial_metric\",\n       \"sqrt_det_spatial_metric\", \"extrinsic_curvature\",\n       \"inverse_spatial_metric\"},\n      {{{1.0, 2.0}}}, std::make_tuple(mass_a, mass_b, center_a, center_b),\n      used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.Gr.BrillLindquist\",\n                  \"[PointwiseFunctions][Unit]\") {\n  test_random(std::numeric_limits<double>::signaling_NaN());\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GeneralRelativity/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/BlastWave.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef compute_piecewise(\n    x, inner_radius, outer_radius, inner_value, outer_value, is_cylindrical\n):\n    if is_cylindrical > 0.5:\n        radius = np.sqrt(np.square(x[0]) + np.square(x[1]))\n    else:\n        radius = np.sqrt(np.square(x[0]) + np.square(x[1]) + np.square(x[2]))\n    if radius > outer_radius:\n        return outer_value\n    elif radius < inner_radius:\n        return inner_value\n    else:\n        piecewise_scalar = (-1.0 * radius + inner_radius) * np.log(outer_value)\n        piecewise_scalar += (radius - outer_radius) * np.log(inner_value)\n        piecewise_scalar /= inner_radius - outer_radius\n        return np.exp(piecewise_scalar)\n\n\ndef rest_mass_density(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return compute_piecewise(\n        x,\n        inner_radius,\n        outer_radius,\n        inner_density,\n        outer_density,\n        is_cylindrical,\n    )\n\n\ndef electron_fraction(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return compute_piecewise(\n        x, inner_radius, outer_radius, 0.1, 0.4, is_cylindrical\n    )\n\n\ndef spatial_velocity(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return np.zeros(3)\n\n\ndef specific_internal_energy(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return (\n        1.0\n        / (adiabatic_index - 1.0)\n        * pressure(\n            x,\n            inner_radius,\n            outer_radius,\n            inner_density,\n            outer_density,\n            inner_pressure,\n            outer_pressure,\n            magnetic_field,\n            adiabatic_index,\n            is_cylindrical,\n        )\n        / rest_mass_density(\n            x,\n            inner_radius,\n            outer_radius,\n            inner_density,\n            outer_density,\n            inner_pressure,\n            outer_pressure,\n            magnetic_field,\n            adiabatic_index,\n            is_cylindrical,\n        )\n    )\n\n\ndef pressure(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return compute_piecewise(\n        x,\n        inner_radius,\n        outer_radius,\n        inner_pressure,\n        outer_pressure,\n        is_cylindrical,\n    )\n\n\ndef lorentz_factor(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return 1.0\n\n\ndef specific_enthalpy(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return 1.0 + adiabatic_index * specific_internal_energy(\n        x,\n        inner_radius,\n        outer_radius,\n        inner_density,\n        outer_density,\n        inner_pressure,\n        outer_pressure,\n        magnetic_field,\n        adiabatic_index,\n        is_cylindrical,\n    )\n\n\ndef magnetic_field(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return np.array(magnetic_field)\n\n\ndef divergence_cleaning_field(\n    x,\n    inner_radius,\n    outer_radius,\n    inner_density,\n    outer_density,\n    inner_pressure,\n    outer_pressure,\n    magnetic_field,\n    adiabatic_index,\n    is_cylindrical,\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nimport PointwiseFunctions.GeneralRelativity.KerrSchildCoords as ks_coords\n\n\ndef rest_mass_density(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    return rest_mass_density\n\n\ndef electron_fraction(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    return 0.1\n\n\ndef spatial_velocity(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    result = np.zeros(3)\n    spin_a = bh_mass * bh_dimless_spin\n    a_squared = spin_a**2\n    r_squared = ks_coords.r_coord_squared(x, bh_mass, bh_dimless_spin)\n    cos_theta = x[2] / np.sqrt(r_squared)\n    cos_theta_squared = cos_theta**2\n    sigma = r_squared + a_squared * cos_theta_squared\n\n    result[0] = (\n        flow_speed\n        * cos_theta\n        / np.sqrt(1.0 + 2.0 * bh_mass * np.sqrt(r_squared) / sigma)\n    )\n    result[1] = -flow_speed * np.sqrt(1.0 - cos_theta_squared) / np.sqrt(sigma)\n\n    return ks_coords.cartesian_from_spherical_ks(\n        result, x, bh_mass, bh_dimless_spin\n    )\n\n\ndef specific_internal_energy(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    return (\n        polytropic_constant\n        * pow(rest_mass_density, polytropic_exponent - 1.0)\n        / (polytropic_exponent - 1.0)\n    )\n\n\ndef pressure(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    return polytropic_constant * pow(rest_mass_density, polytropic_exponent)\n\n\ndef magnetic_field(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    result = np.zeros(3)\n    spin_a = bh_mass * bh_dimless_spin\n    a_squared = spin_a**2\n    r_squared = ks_coords.r_coord_squared(x, bh_mass, bh_dimless_spin)\n    cos_theta = x[2] / np.sqrt(r_squared)\n    cos_theta_squared = cos_theta**2\n    two_m_r = 2.0 * bh_mass * np.sqrt(r_squared)\n    sigma = r_squared + a_squared * cos_theta_squared\n\n    result[0:] = mag_field_strength / np.sqrt(sigma * (sigma + two_m_r))\n    result[0] *= (\n        r_squared\n        - two_m_r\n        + a_squared\n        + two_m_r * (r_squared**2 - a_squared**2) / sigma**2\n    ) * cos_theta\n    result[1] *= -(\n        (\n            np.sqrt(r_squared)\n            + bh_mass\n            * a_squared\n            * (r_squared - a_squared * cos_theta_squared)\n            * (1.0 + cos_theta_squared)\n            / sigma**2\n        )\n        * np.sqrt(1.0 - cos_theta_squared)\n    )\n    result[2] *= (\n        spin_a\n        * (1.0 + two_m_r * (r_squared - a_squared) / sigma**2)\n        * cos_theta\n    )\n    return ks_coords.cartesian_from_spherical_ks(\n        result, x, bh_mass, bh_dimless_spin\n    )\n\n\ndef divergence_cleaning_field(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    return 0.0\n\n\ndef lorentz_factor(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    return 1.0 / np.sqrt(1.0 - flow_speed**2)\n\n\ndef specific_enthalpy(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    rest_mass_density,\n    flow_speed,\n    mag_field_strength,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    return 1.0 + (\n        polytropic_constant\n        * polytropic_exponent\n        * pow(rest_mass_density, polytropic_exponent - 1.0)\n        / (polytropic_exponent - 1.0)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GrMhdAnalyticData\")\n\nset(LIBRARY_SOURCES\n  InitialMagneticFields/Test_Poloidal.cpp\n  InitialMagneticFields/Test_Toroidal.cpp\n  Test_BlastWave.cpp\n  Test_BondiHoyleAccretion.cpp\n  Test_CcsnCollapse.cpp\n  Test_KhInstability.cpp\n  Test_MagneticFieldLoop.cpp\n  Test_MagneticRotor.cpp\n  Test_MagnetizedFmDisk.cpp\n  Test_MagnetizedTovStar.cpp\n  Test_OrszagTangVortex.cpp\n  Test_PolarMagnetizedFmDisk.cpp\n  Test_RiemannProblem.cpp\n  Test_SlabJet.cpp\n  Test_SphericalTorus.cpp\n  )\n\nif (TARGET FUKA::Exporter)\n  list(APPEND LIBRARY_SOURCES\n    Test_FukaInitialData.cpp\n    )\nendif()\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ErrorHandling\n  GrMhdAnalyticData\n  Hydro\n  LinearOperators\n  Options\n  Spectral\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Poloidal.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef magnetic_field(\n    x,\n    pressure,\n    sqrt_det_spatial_metric,\n    dcoords_pressure,\n    pressure_exponent,\n    cutoff_pressure,\n    vector_potential_amplitude,\n):\n    magnetic_field = np.zeros(x.shape)\n\n    magnetic_field[0] = (\n        -(\n            vector_potential_amplitude\n            * pressure_exponent\n            / sqrt_det_spatial_metric\n        )\n        * (pressure - cutoff_pressure) ** (pressure_exponent - 1)\n        * x[0]\n        * dcoords_pressure[2]\n    )\n\n    magnetic_field[1] = (\n        -(\n            vector_potential_amplitude\n            * pressure_exponent\n            / sqrt_det_spatial_metric\n        )\n        * (pressure - cutoff_pressure) ** (pressure_exponent - 1)\n        * x[1]\n        * dcoords_pressure[2]\n    )\n\n    magnetic_field[2] = (\n        vector_potential_amplitude / sqrt_det_spatial_metric\n    ) * (\n        2 * (pressure - cutoff_pressure) ** pressure_exponent\n        + pressure_exponent\n        * (pressure - cutoff_pressure) ** (pressure_exponent - 1)\n        * (x[0] * dcoords_pressure[0] + x[1] * dcoords_pressure[1])\n    )\n\n    magnetic_field = np.where(pressure < cutoff_pressure, 0.0, magnetic_field)\n\n    return magnetic_field\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Test_Poloidal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Poloidal.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace grmhd::AnalyticData::InitialMagneticFields {\nnamespace {\n\nstruct PoloidalProxy : Poloidal {\n  using Poloidal::Poloidal;\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>> return_variables(\n      const tnsr::I<DataType, 3>& x, const Scalar<DataType>& pressure,\n      const Scalar<DataType>& sqrt_det_spatial_metric,\n      const tnsr::i<DataType, 3>& deriv_pressure) const {\n    auto mag_field = make_with_value<tnsr::I<DataType, 3>>(pressure, 0.0);\n    this->variables(make_not_null(&mag_field), x, pressure,\n                    sqrt_det_spatial_metric, deriv_pressure);\n    return {mag_field};\n  }\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.GrMhd.InitialMagneticFields.Poloidal\",\n    \"[Unit][PointwiseFunctions]\") {\n  register_classes_with_charm<Poloidal>();\n  // test creation\n  const auto factory_solution = serialize_and_deserialize(\n      TestHelpers::test_factory_creation<InitialMagneticField, Poloidal>(\n          \"Poloidal:\\n\"\n          \"  PressureExponent: 2\\n\"\n          \"  CutoffPressure: 1.0e-5\\n\"\n          \"  VectorPotentialAmplitude: 2500.0\\n\"\n          \"  Center: [0.0, 0.0, 0.0]\\n\"\n          \"  MaxDistanceFromCenter: 100.0\\n\"));\n  CHECK(factory_solution->is_equal(\n      Poloidal(2, 1.0e-5, 2500.0, {{0.0, 0.0, 0.0}}, 100.0)));\n\n  const auto& solution = dynamic_cast<const Poloidal&>(*factory_solution);\n\n  // test serialize\n  test_serialization(solution);\n\n  // test move\n  {\n    Poloidal poloidal_field{2, 1.0e-5, 2500.0, {{0.0, 0.0, 0.0}}, 100.0};\n    Poloidal poloidal_field_copy{2, 1.0e-5, 2500.0, {{0.0, 0.0, 0.0}}, 100.0};\n    test_move_semantics(std::move(poloidal_field), poloidal_field_copy);\n  }\n\n  // test derived\n  const std::unique_ptr<InitialMagneticField> base_ptr =\n      std::make_unique<Poloidal>();\n  const std::unique_ptr<InitialMagneticField> deserialized_base_ptr =\n      serialize_and_deserialize(base_ptr)->get_clone();\n  CHECK(dynamic_cast<Poloidal*>(deserialized_base_ptr.get()) != nullptr);\n\n  // test equality\n  const Poloidal field_original{2, 1.0e-5, 2500.0, {{0.0, 0.0, 0.0}}, 100.0};\n  const auto field = serialize_and_deserialize(field_original);\n  CHECK(static_cast<const InitialMagneticField&>(field).is_equal(\n      Poloidal(2, 1.0e-5, 2500.0, {{0.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Poloidal(3, 1.0e-5, 2500.0, {{0.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Poloidal(2, 2.0e-5, 2500.0, {{0.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Poloidal(2, 1.0e-5, 3500.0, {{0.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Poloidal(2, 1.0e-5, 2500.0, {{1.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Poloidal(2, 1.0e-5, 2500.0, {{0.0, 0.0, 0.0}}, 10.0)));\n\n  // test solution implementation\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields\"};\n  const DataVector used_for_size{10};\n\n  const size_t pressure_exponent = 2;\n  const double cutoff_pressure = 1.0e-5;\n  const double vector_potential_amplitude = 2500.0;\n\n  pypp::check_with_random_values<1>(\n      &PoloidalProxy::return_variables<double>,\n      PoloidalProxy(pressure_exponent, cutoff_pressure,\n                    vector_potential_amplitude, {{0.0, 0.0, 0.0}}, 100.0),\n      \"Poloidal\", {\"magnetic_field\"}, {{{-10.0, 10.0}}},\n      std::make_tuple(pressure_exponent, cutoff_pressure,\n                      vector_potential_amplitude),\n      used_for_size);\n\n  // test if the B field is divergence-free in the flat spacetime\n  const Mesh<3> mesh{5, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const size_t num_grid_pts = mesh.number_of_grid_points();\n  const auto log_coords = logical_coordinates(mesh);\n  const double scale = 1.0e-2;\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inv_jac{mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    inv_jac.get(i, i) = 1.0 / scale;\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{mesh.number_of_grid_points(),\n                                                   1.0};\n\n  const auto test_for_small_coords_patch =\n      [&inv_jac, &solution, &sqrt_det_spatial_metric, &mesh,\n       &num_grid_pts](const tnsr::I<DataVector, 3>& in_coords) {\n        const auto& x = in_coords.get(0);\n        const auto& y = in_coords.get(1);\n        const auto& z = in_coords.get(2);\n\n        // test with the pressure of a simple analytic form\n        Scalar<DataVector> pressure{num_grid_pts, 0.0};\n        get(pressure) = (x * x * x) + (y * y) + z +\n                        0.1;  // Add a small offset to set P > cutoff\n\n        tnsr::i<DataVector, 3, Frame::Inertial> d_pressure{num_grid_pts, 0.0};\n        d_pressure.get(0) = 3.0 * x * x;\n        d_pressure.get(1) = 2.0 * y;\n        d_pressure.get(2) = 1.0;\n\n        tnsr::I<DataVector, 3> b_field{num_grid_pts, 0.0};\n        solution.variables(&b_field, in_coords, pressure,\n                           sqrt_det_spatial_metric, d_pressure);\n\n        Scalar<DataVector> mag_b_field{num_grid_pts, 0.0};\n        for (size_t i = 0; i < 3; ++i) {\n          get(mag_b_field) += square(b_field.get(i));\n        }\n        get(mag_b_field) = sqrt(get(mag_b_field));\n\n        CHECK(get(mag_b_field) != approx(0.));\n        const auto div_b_field = divergence(b_field, mesh, inv_jac);\n\n        const Scalar<DataVector> div_b_over_mag_b{get(div_b_field) /\n                                                  get(mag_b_field)};\n\n        CHECK(max(abs(get(div_b_over_mag_b))) < 1.0e-12);\n      };\n\n  // check a small region around the origin\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coords{num_grid_pts, 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    inertial_coords.get(i) = scale * log_coords.get(i);\n  }\n  test_for_small_coords_patch(inertial_coords);\n\n  // check a small region off-origin\n  inertial_coords.get(0) += 0.5;\n  inertial_coords.get(1) += 1.0;\n  inertial_coords.get(2) += 2.0;\n  test_for_small_coords_patch(inertial_coords);\n}\n}  // namespace\n}  // namespace grmhd::AnalyticData::InitialMagneticFields\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Test_Toroidal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Toroidal.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace grmhd::AnalyticData::InitialMagneticFields {\nnamespace {\n\nstruct ToroidalProxy : Toroidal {\n  using Toroidal::Toroidal;\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::MagneticField<DataType, 3>> return_variables(\n      const tnsr::I<DataType, 3>& x, const Scalar<DataType>& pressure,\n      const Scalar<DataType>& sqrt_det_spatial_metric,\n      const tnsr::i<DataType, 3>& deriv_pressure) const {\n    auto mag_field = make_with_value<tnsr::I<DataType, 3>>(pressure, 0.0);\n    this->variables(make_not_null(&mag_field), x, pressure,\n                    sqrt_det_spatial_metric, deriv_pressure);\n    return {mag_field};\n  }\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.GrMhd.InitialMagneticFields.Toroidal\",\n    \"[Unit][PointwiseFunctions]\") {\n  register_classes_with_charm<Toroidal>();\n  // test creation\n  const auto factory_solution = serialize_and_deserialize(\n      TestHelpers::test_factory_creation<InitialMagneticField, Toroidal>(\n          \"Toroidal:\\n\"\n          \"  PressureExponent: 2\\n\"\n          \"  CutoffPressure: 1.0e-5\\n\"\n          \"  VectorPotentialAmplitude: 1000.0\\n\"\n          \"  Center: [0.0, 0.0, 0.0]\\n\"\n          \"  MaxDistanceFromCenter: 100.0\\n\"));\n  CHECK(factory_solution->is_equal(\n      Toroidal(2, 1.0e-5, 1000.0, {{0.0, 0.0, 0.0}}, 100.0)));\n\n  const auto& solution = dynamic_cast<const Toroidal&>(*factory_solution);\n\n  // test serialize\n  test_serialization(solution);\n\n  // test move\n  {\n    Toroidal toroidal_field{2, 1.0e-5, 1000.0, {{0.0, 0.0, 0.0}}, 100.0};\n    Toroidal toroidal_field_copy{2, 1.0e-5, 1000.0, {{0.0, 0.0, 0.0}}, 100.0};\n    test_move_semantics(std::move(toroidal_field), toroidal_field_copy);\n  }\n\n  // test derived\n  const std::unique_ptr<InitialMagneticField> base_ptr =\n      std::make_unique<Toroidal>();\n  const std::unique_ptr<InitialMagneticField> deserialized_base_ptr =\n      serialize_and_deserialize(base_ptr)->get_clone();\n  CHECK(dynamic_cast<Toroidal*>(deserialized_base_ptr.get()) != nullptr);\n\n  // test equality\n  const Toroidal field_original{2, 1.0e-5, 1000.0, {{0.0, 0.0, 0.0}}, 100.0};\n  const auto field = serialize_and_deserialize(field_original);\n  CHECK(static_cast<const InitialMagneticField&>(field).is_equal(\n      Toroidal(2, 1.0e-5, 1000.0, {{0.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Toroidal(3, 1.0e-5, 1000.0, {{0.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Toroidal(2, 2.0e-5, 1000.0, {{0.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Toroidal(2, 1.0e-5, 2000.0, {{0.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Toroidal(2, 1.0e-5, 1000.0, {{1.0, 0.0, 0.0}}, 100.0)));\n  CHECK_FALSE(static_cast<const InitialMagneticField&>(field).is_equal(\n      Toroidal(2, 1.0e-5, 1000.0, {{0.0, 0.0, 0.0}}, 10.0)));\n\n  // test solution implementation\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields\"};\n  const DataVector used_for_size{10};\n\n  const size_t pressure_exponent = 2;\n  const double cutoff_pressure = 1.0e-5;\n  const double vector_potential_amplitude = 1000.0;\n\n  pypp::check_with_random_values<1>(\n      &ToroidalProxy::return_variables<double>,\n      ToroidalProxy(pressure_exponent, cutoff_pressure,\n                    vector_potential_amplitude, {{0.0, 0.0, 0.0}}, 10000.0),\n      \"Toroidal\", {\"magnetic_field\"}, {{{-10.0, 10.0}}},\n      std::make_tuple(pressure_exponent, cutoff_pressure,\n                      vector_potential_amplitude),\n      used_for_size);\n\n  // test with very large `cutoff_pressure` to ensure that we hit the `if`\n  // branch for p < p_cutoff\n  pypp::check_with_random_values<1>(\n      &ToroidalProxy::return_variables<double>,\n      ToroidalProxy(pressure_exponent, 1e5, vector_potential_amplitude,\n                    {{0.0, 0.0, 0.0}}, 10000.0),\n      \"Toroidal\", {\"magnetic_field\"}, {{{-1.0, 1.0}}},\n      std::make_tuple(pressure_exponent, 1e5, vector_potential_amplitude),\n      used_for_size);\n\n  // test if the B field is divergence-free in the flat spacetime\n  const Mesh<3> mesh{5, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const size_t num_grid_pts = mesh.number_of_grid_points();\n  const auto log_coords = logical_coordinates(mesh);\n  const double scale = 1.0e-3;\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inv_jac{mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    inv_jac.get(i, i) = 1.0 / scale;\n  }\n  const Scalar<DataVector> sqrt_det_spatial_metric{mesh.number_of_grid_points(),\n                                                   1.0};\n\n  const auto test_for_small_coords_patch =\n      [&cutoff_pressure, &inv_jac, &solution, &sqrt_det_spatial_metric, &mesh,\n       &num_grid_pts](const tnsr::I<DataVector, 3>& in_coords) {\n        const auto& x = in_coords.get(0);\n        const auto& y = in_coords.get(1);\n        const auto& z = in_coords.get(2);\n\n        // test with the pressure of a simple analytic form\n        Scalar<DataVector> pressure{num_grid_pts, 0.0};\n        get(pressure) =\n            (x * x * x) + (y * y) + z +\n            0.5 * cutoff_pressure;  // Add a small offset to have both (P >\n                                    // cutoff) and (P < cutoff) regions\n\n        tnsr::i<DataVector, 3, Frame::Inertial> d_pressure{num_grid_pts, 0.0};\n        d_pressure.get(0) = 3.0 * x * x;\n        d_pressure.get(1) = 2.0 * y;\n        d_pressure.get(2) = 1.0;\n\n        tnsr::I<DataVector, 3> b_field{num_grid_pts, 0.0};\n        solution.variables(&b_field, in_coords, pressure,\n                           sqrt_det_spatial_metric, d_pressure);\n\n        Scalar<DataVector> mag_b_field{num_grid_pts, 0.0};\n        for (size_t i = 0; i < 3; ++i) {\n          get(mag_b_field) += square(b_field.get(i));\n        }\n\n        get(mag_b_field) = sqrt(get(mag_b_field));\n\n        CHECK(get(mag_b_field) != approx(0.));\n        const auto div_b_field = divergence(b_field, mesh, inv_jac);\n        const Scalar<DataVector> div_b_over_mag_b{get(div_b_field) /\n                                                  (get(mag_b_field))};\n\n        CHECK(max(abs(get(div_b_over_mag_b))) < 1.0e-10);\n      };\n\n  // Toroidal field goes to zero near origin, so just check a small region\n  // off-origin\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coords{num_grid_pts, 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    inertial_coords.get(i) = scale * log_coords.get(i);\n  }\n  inertial_coords.get(0) += 0.1;\n  inertial_coords.get(1) += 0.2;\n  inertial_coords.get(2) += 0.3;\n  test_for_small_coords_patch(inertial_coords);\n}\n\n}  // namespace\n}  // namespace grmhd::AnalyticData::InitialMagneticFields\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Toroidal.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef magnetic_field(\n    x,\n    pressure,\n    sqrt_det_spatial_metric,\n    deriv_pressure,\n    pressure_exponent,\n    cutoff_pressure,\n    vector_potential_amplitude,\n):\n    magnetic_field = np.zeros(x.shape)\n\n    varpi_squared = x[0] ** 2 + x[1] ** 2\n\n    magnetic_field[0] = (\n        vector_potential_amplitude / sqrt_det_spatial_metric\n    ) * (\n        2.0 * x[1] * (pressure - cutoff_pressure) ** pressure_exponent\n        + varpi_squared\n        * pressure_exponent\n        * (pressure - cutoff_pressure) ** (pressure_exponent - 1)\n        * deriv_pressure[1]\n    )\n\n    magnetic_field[1] = -(\n        vector_potential_amplitude / sqrt_det_spatial_metric\n    ) * (\n        2.0 * x[0] * (pressure - cutoff_pressure) ** pressure_exponent\n        + varpi_squared\n        * pressure_exponent\n        * (pressure - cutoff_pressure) ** (pressure_exponent - 1)\n        * deriv_pressure[0]\n    )\n\n    magnetic_field[2] = 0.0\n\n    magnetic_field = np.where(pressure < cutoff_pressure, 0.0, magnetic_field)\n\n    return magnetic_field\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/KhInstability.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef rest_mass_density(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    return (\n        strip_density\n        if np.absolute(x[1] - strip_bimedian_height) < 0.5 * strip_thickness\n        else background_density\n    )\n\n\ndef electron_fraction(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    return 0.1\n\n\ndef velocity(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    dim = x.size\n    result = np.zeros(dim)\n    result[0] = (\n        strip_velocity\n        if (np.absolute(x[1] - strip_bimedian_height) < 0.5 * strip_thickness)\n        else background_velocity\n    )\n    strip_lower_bound = strip_bimedian_height - 0.5 * strip_thickness\n    strip_upper_bound = strip_bimedian_height + 0.5 * strip_thickness\n    result[1] = np.exp(\n        -0.5 * ((x[1] - strip_lower_bound) / perturbation_width) ** 2\n    ) + np.exp(-0.5 * ((x[1] - strip_upper_bound) / perturbation_width) ** 2)\n    result[1] *= perturbation_amplitude * np.sin(4 * np.pi * x[0])\n    return result\n\n\ndef specific_internal_energy(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    return (\n        pressure / (adiabatic_index - 1.0) / strip_density\n        if np.absolute(x[1] - strip_bimedian_height) < 0.5 * strip_thickness\n        else pressure / (adiabatic_index - 1.0) / background_density\n    )\n\n\ndef pressure(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    return pressure\n\n\ndef specific_enthalpy(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    return 1.0 + adiabatic_index * specific_internal_energy(\n        x,\n        adiabatic_index,\n        strip_bimedian_height,\n        strip_thickness,\n        strip_density,\n        strip_velocity,\n        background_density,\n        background_velocity,\n        pressure,\n        perturbation_amplitude,\n        perturbation_width,\n        magnetic_field,\n    )\n\n\ndef lorentz_factor(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    v = velocity(\n        x,\n        adiabatic_index,\n        strip_bimedian_height,\n        strip_thickness,\n        strip_density,\n        strip_velocity,\n        background_density,\n        background_velocity,\n        pressure,\n        perturbation_amplitude,\n        perturbation_width,\n        magnetic_field,\n    )\n    return 1.0 / np.sqrt(1.0 - np.dot(v, v))\n\n\ndef magnetic_field(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    return np.array(magnetic_field)\n\n\ndef divergence_cleaning_field(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n    magnetic_field,\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/MagneticFieldLoop.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef compute_rest_mass_density(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    return rest_mass_density\n\n\ndef compute_pressure(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    return pressure\n\n\ndef electron_fraction(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    return 0.1\n\n\ndef specific_internal_energy(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    return (\n        1.0\n        / (adiabatic_index - 1.0)\n        * compute_pressure(\n            x,\n            pressure,\n            rest_mass_density,\n            adiabatic_index,\n            advection_velocity,\n            magnetic_field_strength,\n            inner_radius,\n            outer_radius,\n        )\n        / compute_rest_mass_density(\n            x,\n            pressure,\n            rest_mass_density,\n            adiabatic_index,\n            advection_velocity,\n            magnetic_field_strength,\n            inner_radius,\n            outer_radius,\n        )\n    )\n\n\ndef spatial_velocity(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    return np.array(advection_velocity)\n\n\ndef lorentz_factor(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    v = spatial_velocity(\n        x,\n        pressure,\n        rest_mass_density,\n        adiabatic_index,\n        advection_velocity,\n        magnetic_field_strength,\n        inner_radius,\n        outer_radius,\n    )\n    return 1.0 / np.sqrt(1.0 - np.dot(v, v))\n\n\ndef specific_enthalpy(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    return (\n        1.0\n        + specific_internal_energy(\n            x,\n            pressure,\n            rest_mass_density,\n            adiabatic_index,\n            advection_velocity,\n            magnetic_field_strength,\n            inner_radius,\n            outer_radius,\n        )\n        + compute_pressure(\n            x,\n            pressure,\n            rest_mass_density,\n            adiabatic_index,\n            advection_velocity,\n            magnetic_field_strength,\n            inner_radius,\n            outer_radius,\n        )\n        / compute_rest_mass_density(\n            x,\n            pressure,\n            rest_mass_density,\n            adiabatic_index,\n            advection_velocity,\n            magnetic_field_strength,\n            inner_radius,\n            outer_radius,\n        )\n    )\n\n\ndef magnetic_field(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    radius = np.sqrt(np.square(x[0]) + np.square(x[1]))\n    if radius > outer_radius:\n        return np.array([0.0, 0.0, 0.0])\n    if radius < inner_radius:\n        return np.array(\n            [\n                -magnetic_field_strength * x[1] / inner_radius,\n                magnetic_field_strength * x[0] / inner_radius,\n                0.0,\n            ]\n        )\n    return np.array(\n        [\n            -magnetic_field_strength * x[1] / radius,\n            magnetic_field_strength * x[0] / radius,\n            0.0,\n        ]\n    )\n\n\ndef divergence_cleaning_field(\n    x,\n    pressure,\n    rest_mass_density,\n    adiabatic_index,\n    advection_velocity,\n    magnetic_field_strength,\n    inner_radius,\n    outer_radius,\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef compute_piecewise(x, rotor_radius, inner_value, outer_value):\n    radius = np.sqrt(np.square(x[0]) + np.square(x[1]))\n    if radius > rotor_radius:\n        return outer_value\n    else:\n        return inner_value\n\n\ndef rest_mass_density(\n    x,\n    rotor_radius,\n    inner_density,\n    outer_density,\n    pressure,\n    angular_velocity,\n    magnetic_field,\n    adiabatic_index,\n):\n    return compute_piecewise(x, rotor_radius, inner_density, outer_density)\n\n\ndef spatial_velocity(\n    x,\n    rotor_radius,\n    inner_density,\n    outer_density,\n    pressure,\n    angular_velocity,\n    magnetic_field,\n    adiabatic_index,\n):\n    omega = compute_piecewise(x, rotor_radius, angular_velocity, 0.0)\n    return np.array([-x[1] * omega, x[0] * omega, 0.0])\n\n\ndef specific_internal_energy(\n    x,\n    rotor_radius,\n    inner_density,\n    outer_density,\n    pressure,\n    angular_velocity,\n    magnetic_field,\n    adiabatic_index,\n):\n    return (\n        1.0\n        / (adiabatic_index - 1.0)\n        * compute_pressure(\n            x,\n            rotor_radius,\n            inner_density,\n            outer_density,\n            pressure,\n            angular_velocity,\n            magnetic_field,\n            adiabatic_index,\n        )\n        / rest_mass_density(\n            x,\n            rotor_radius,\n            inner_density,\n            outer_density,\n            pressure,\n            angular_velocity,\n            magnetic_field,\n            adiabatic_index,\n        )\n    )\n\n\ndef compute_pressure(\n    x,\n    rotor_radius,\n    inner_density,\n    outer_density,\n    pressure,\n    angular_velocity,\n    magnetic_field,\n    adiabatic_index,\n):\n    return pressure\n\n\ndef lorentz_factor(\n    x,\n    rotor_radius,\n    inner_density,\n    outer_density,\n    pressure,\n    angular_velocity,\n    magnetic_field,\n    adiabatic_index,\n):\n    v = spatial_velocity(\n        x,\n        rotor_radius,\n        inner_density,\n        outer_density,\n        pressure,\n        angular_velocity,\n        magnetic_field,\n        adiabatic_index,\n    )\n    return 1.0 / np.sqrt(1.0 - np.dot(v, v))\n\n\ndef specific_enthalpy(\n    x,\n    rotor_radius,\n    inner_density,\n    outer_density,\n    pressure,\n    angular_velocity,\n    magnetic_field,\n    adiabatic_index,\n):\n    return 1.0 + adiabatic_index * specific_internal_energy(\n        x,\n        rotor_radius,\n        inner_density,\n        outer_density,\n        pressure,\n        angular_velocity,\n        magnetic_field,\n        adiabatic_index,\n    )\n\n\ndef magnetic_field(\n    x,\n    rotor_radius,\n    inner_density,\n    outer_density,\n    pressure,\n    angular_velocity,\n    magnetic_field,\n    adiabatic_index,\n):\n    return np.array(magnetic_field)\n\n\ndef divergence_cleaning_field(\n    x,\n    rotor_radius,\n    inner_density,\n    outer_density,\n    pressure,\n    angular_velocity,\n    magnetic_field,\n    adiabatic_index,\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nimport PointwiseFunctions.AnalyticSolutions.RelativisticEuler.FishboneMoncriefDisk as fm_disk\nimport PointwiseFunctions.GeneralRelativity.KerrSchildCoords as ks_coords\n\n\ndef rest_mass_density(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_density,\n    plasma_beta,\n):\n    dummy_time = 0.0\n    return fm_disk.rest_mass_density(\n        x,\n        dummy_time,\n        bh_mass,\n        bh_dimless_spin,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n        noise,\n    )\n\n\ndef spatial_velocity(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_density,\n    plasma_beta,\n):\n    dummy_time = 0.0\n    return fm_disk.spatial_velocity(\n        x,\n        dummy_time,\n        bh_mass,\n        bh_dimless_spin,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n        noise,\n    )\n\n\ndef specific_internal_energy(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_density,\n    plasma_beta,\n):\n    dummy_time = 0.0\n    return fm_disk.specific_internal_energy(\n        x,\n        dummy_time,\n        bh_mass,\n        bh_dimless_spin,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n        noise,\n    )\n\n\ndef pressure(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_density,\n    plasma_beta,\n):\n    dummy_time = 0.0\n    return fm_disk.pressure(\n        x,\n        dummy_time,\n        bh_mass,\n        bh_dimless_spin,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n        noise,\n    )\n\n\ndef magnetic_potential(\n    r,\n    sin_theta_sqrd,\n    m,\n    a,\n    rin,\n    rmax,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_rest_mass_density,\n):\n    l = fm_disk.angular_momentum(m, a, rmax)\n    Win = fm_disk.potential(l, rin**2, 1.0, m, a)\n    h = np.exp(Win - fm_disk.potential(l, r**2, sin_theta_sqrd, m, a))\n    return (\n        pow(\n            (h - 1.0)\n            * (polytropic_exponent - 1.0)\n            / (polytropic_constant * polytropic_exponent),\n            1.0 / (polytropic_exponent - 1.0),\n        )\n        - threshold_rest_mass_density\n    )\n\n\ndef magnetic_field(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_density,\n    plasma_beta,\n):\n    dummy_time = 0.0\n    result = np.zeros(3)\n    spin_a = bh_mass * bh_dimless_spin\n    r_in = bh_mass * dimless_r_in\n    r_max = bh_mass * dimless_r_max\n    rho = fm_disk.rest_mass_density(\n        x,\n        dummy_time,\n        bh_mass,\n        bh_dimless_spin,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n        noise,\n    )\n    x_max = np.array([r_max, 0.0, 0.0])\n    threshold_rho = threshold_density * (\n        fm_disk.rest_mass_density(\n            x_max,\n            dummy_time,\n            bh_mass,\n            bh_dimless_spin,\n            dimless_r_in,\n            dimless_r_max,\n            polytropic_constant,\n            polytropic_exponent,\n            noise,\n        )\n    )\n    x_ks = [0.0, 0.0, 0.0]\n\n    if rho > threshold_rho:\n        small = 0.0001 * bh_mass\n        a_squared = spin_a**2\n        sin_theta_squared = x[0] ** 2 + x[1] ** 2\n        r_squared = sin_theta_squared + x[2] ** 2\n        sin_theta_squared /= r_squared\n\n        r = np.sqrt(r_squared)\n        sin_theta = np.sqrt(sin_theta_squared)\n        sigma = r_squared + a_squared * x[2] ** 2 / r_squared\n        prefactor = np.sqrt(sigma * (sigma + 2.0 * bh_mass * r)) * sin_theta\n        prefactor = 1.0 / (2.0 * small * prefactor)\n        result[0] = (\n            (\n                magnetic_potential(\n                    r,\n                    sin_theta_squared + small,\n                    bh_mass,\n                    spin_a,\n                    r_in,\n                    r_max,\n                    polytropic_constant,\n                    polytropic_exponent,\n                    noise,\n                    threshold_rho,\n                )\n                - magnetic_potential(\n                    r,\n                    sin_theta_squared - small,\n                    bh_mass,\n                    spin_a,\n                    r_in,\n                    r_max,\n                    polytropic_constant,\n                    polytropic_exponent,\n                    noise,\n                    threshold_rho,\n                )\n            )\n            * 2.0\n            * prefactor\n            * sin_theta\n            * x[2]\n        ) / r\n        result[1] = (\n            magnetic_potential(\n                r - small,\n                sin_theta_squared,\n                bh_mass,\n                spin_a,\n                r_in,\n                r_max,\n                polytropic_constant,\n                polytropic_exponent,\n                noise,\n                threshold_rho,\n            )\n            - magnetic_potential(\n                r + small,\n                sin_theta_squared,\n                bh_mass,\n                spin_a,\n                r_in,\n                r_max,\n                polytropic_constant,\n                polytropic_exponent,\n                noise,\n                threshold_rho,\n            )\n        ) * prefactor\n        sks_to_ks_factor = np.sqrt(r_squared + a_squared) / np.sqrt(r_squared)\n        x_ks[0] = x[0] * sks_to_ks_factor\n        x_ks[1] = x[1] * sks_to_ks_factor\n        x_ks[2] = x[2]\n        # This normalization is specific for disk parameters used\n        # in test_variables function of Test_MagnetizedFmDisk.cpp.\n        # Note: we are using lower (than default)\n        # b_field_normalization for a faster testing time.\n        normalization = 8.49943510498341\n        result = normalization * ks_coords.cartesian_from_spherical_ks(\n            result, x_ks, bh_mass, bh_dimless_spin\n        )\n        inv_jac = fm_disk.inverse_jacobian_matrix(x, spin_a)\n        result = inv_jac @ result\n    return result\n\n\ndef divergence_cleaning_field(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_density,\n    plasma_beta,\n):\n    return 0.0\n\n\ndef lorentz_factor(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_density,\n    plasma_beta,\n):\n    dummy_time = 0.0\n    return fm_disk.lorentz_factor(\n        x,\n        dummy_time,\n        bh_mass,\n        bh_dimless_spin,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n        noise,\n    )\n\n\ndef specific_enthalpy(\n    x,\n    bh_mass,\n    bh_dimless_spin,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n    threshold_density,\n    plasma_beta,\n):\n    dummy_time = 0.0\n    return fm_disk.specific_enthalpy(\n        x,\n        dummy_time,\n        bh_mass,\n        bh_dimless_spin,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n        noise,\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/OrszagTangVortex.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef rest_mass_density(x):\n    return 25.0 / (36.0 * np.pi)\n\n\ndef spatial_velocity(x):\n    return np.array(\n        [\n            -1.0 / 2.0 * np.sin(2.0 * np.pi * x[1]),\n            1.0 / 2.0 * np.sin(2.0 * np.pi * x[0]),\n            0.0,\n        ]\n    )\n\n\ndef specific_internal_energy(x):\n    return 1.0 / (5.0 / 3.0 - 1.0) * pressure(x) / rest_mass_density(x)\n\n\ndef pressure(x):\n    return 5.0 / (12.0 * np.pi)\n\n\ndef lorentz_factor(x):\n    v = spatial_velocity(x)\n    return 1.0 / np.sqrt(1.0 - np.dot(v, v))\n\n\ndef specific_enthalpy(x):\n    return (\n        1.0 + specific_internal_energy(x) + pressure(x) / rest_mass_density(x)\n    )\n\n\ndef magnetic_field(x):\n    return np.array(\n        [\n            -1.0 / np.sqrt(4.0 * np.pi) * np.sin(2.0 * np.pi * x[1]),\n            1.0 / np.sqrt(4.0 * np.pi) * np.sin(4.0 * np.pi * x[0]),\n            0.0,\n        ]\n    )\n\n\ndef divergence_cleaning_field(x):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/RiemannProblem.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef rest_mass_density(\n    x,\n    adiabatic_index,\n    left_density,\n    right_density,\n    left_pressure,\n    right_pressure,\n    left_velocity,\n    right_velocity,\n    left_magnetic_field,\n    right_magnetic_field,\n    lapse,\n    shift,\n):\n    assert len(x) == 3\n    return left_density if x[0] <= 0.0 else right_density\n\n\ndef spatial_velocity(\n    x,\n    adiabatic_index,\n    left_density,\n    right_density,\n    left_pressure,\n    right_pressure,\n    left_velocity,\n    right_velocity,\n    left_magnetic_field,\n    right_magnetic_field,\n    lapse,\n    shift,\n):\n    return np.asarray(left_velocity if x[0] <= 0.0 else right_velocity)\n\n\ndef specific_internal_energy(\n    x,\n    adiabatic_index,\n    left_density,\n    right_density,\n    left_pressure,\n    right_pressure,\n    left_velocity,\n    right_velocity,\n    left_magnetic_field,\n    right_magnetic_field,\n    lapse,\n    shift,\n):\n    return (\n        1.0\n        / (adiabatic_index - 1.0)\n        * compute_pressure(\n            x,\n            adiabatic_index,\n            left_density,\n            right_density,\n            left_pressure,\n            right_pressure,\n            left_velocity,\n            right_velocity,\n            left_magnetic_field,\n            right_magnetic_field,\n            lapse,\n            shift,\n        )\n        / rest_mass_density(\n            x,\n            adiabatic_index,\n            left_density,\n            right_density,\n            left_pressure,\n            right_pressure,\n            left_velocity,\n            right_velocity,\n            left_magnetic_field,\n            right_magnetic_field,\n            lapse,\n            shift,\n        )\n    )\n\n\ndef compute_pressure(\n    x,\n    adiabatic_index,\n    left_density,\n    right_density,\n    left_pressure,\n    right_pressure,\n    left_velocity,\n    right_velocity,\n    left_magnetic_field,\n    right_magnetic_field,\n    lapse,\n    shift,\n):\n    return left_pressure if x[0] <= 0.0 else right_pressure\n\n\ndef lorentz_factor(\n    x,\n    adiabatic_index,\n    left_density,\n    right_density,\n    left_pressure,\n    right_pressure,\n    left_velocity,\n    right_velocity,\n    left_magnetic_field,\n    right_magnetic_field,\n    lapse,\n    shift,\n):\n    v = spatial_velocity(\n        x,\n        adiabatic_index,\n        left_density,\n        right_density,\n        left_pressure,\n        right_pressure,\n        left_velocity,\n        right_velocity,\n        left_magnetic_field,\n        right_magnetic_field,\n        lapse,\n        shift,\n    )\n    return 1.0 / np.sqrt(1.0 - np.dot(v, v))\n\n\ndef specific_enthalpy(\n    x,\n    adiabatic_index,\n    left_density,\n    right_density,\n    left_pressure,\n    right_pressure,\n    left_velocity,\n    right_velocity,\n    left_magnetic_field,\n    right_magnetic_field,\n    lapse,\n    shift,\n):\n    return 1.0 + adiabatic_index * specific_internal_energy(\n        x,\n        adiabatic_index,\n        left_density,\n        right_density,\n        left_pressure,\n        right_pressure,\n        left_velocity,\n        right_velocity,\n        left_magnetic_field,\n        right_magnetic_field,\n        lapse,\n        shift,\n    )\n\n\ndef magnetic_field(\n    x,\n    adiabatic_index,\n    left_density,\n    right_density,\n    left_pressure,\n    right_pressure,\n    left_velocity,\n    right_velocity,\n    left_magnetic_field,\n    right_magnetic_field,\n    lapse,\n    shift,\n):\n    return np.asarray(\n        left_magnetic_field if x[0] <= 0.0 else right_magnetic_field\n    )\n\n\ndef divergence_cleaning_field(\n    x,\n    adiabatic_index,\n    left_density,\n    right_density,\n    left_pressure,\n    right_pressure,\n    left_velocity,\n    right_velocity,\n    left_magnetic_field,\n    right_magnetic_field,\n    lapse,\n    shift,\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/SlabJet.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef piecewise(x, inlet_radius, ambient_value, jet_value):\n    return (\n        jet_value\n        if x[0] <= 0.0 and abs(x[1]) <= inlet_radius\n        else ambient_value\n    )\n\n\ndef piecewise_vector(x, inlet_radius, ambient_value, jet_value):\n    return np.array(\n        [\n            (\n                jet_value[d]\n                if x[0] <= 0.0 and abs(x[1]) <= inlet_radius\n                else ambient_value[d]\n            )\n            for d in range(3)\n        ]\n    )\n\n\ndef parse_vars(*args, **kwargs):\n    assert not kwargs, \"Found unexpected labeled arguments:\\n{}\".format(kwargs)\n    assert len(args) == 10, \"Expected 10 arguments, but got {}\".format(\n        len(args)\n    )\n    return dict(\n        zip(\n            [\n                \"adiabatic_index\",\n                \"ambient_density\",\n                \"ambient_pressure\",\n                \"ambient_electron_fraction\",\n                \"jet_density\",\n                \"jet_pressure\",\n                \"jet_electron_fraction\",\n                \"jet_velocity\",\n                \"inlet_radius\",\n                \"magnetic_field\",\n            ],\n            args,\n        )\n    )\n\n\ndef rest_mass_density(x, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return piecewise(\n        x, vars[\"inlet_radius\"], vars[\"ambient_density\"], vars[\"jet_density\"]\n    )\n\n\ndef electron_fraction(x, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return piecewise(\n        x,\n        vars[\"inlet_radius\"],\n        vars[\"ambient_electron_fraction\"],\n        vars[\"jet_electron_fraction\"],\n    )\n\n\ndef spatial_velocity(x, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return piecewise_vector(\n        x, vars[\"inlet_radius\"], [0.0, 0.0, 0.0], vars[\"jet_velocity\"]\n    )\n\n\ndef specific_internal_energy(x, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    p = pressure(x, *args, **kwargs)\n    rho = rest_mass_density(x, *args, **kwargs)\n    return p / ((vars[\"adiabatic_index\"] - 1.0) * rho)\n\n\ndef pressure(x, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return piecewise(\n        x, vars[\"inlet_radius\"], vars[\"ambient_pressure\"], vars[\"jet_pressure\"]\n    )\n\n\ndef specific_enthalpy(x, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    e = specific_internal_energy(x, *args, **kwargs)\n    return 1.0 + vars[\"adiabatic_index\"] * e\n\n\ndef lorentz_factor(x, *args, **kwargs):\n    v = spatial_velocity(x, *args, **kwargs)\n    return 1.0 / np.sqrt(1.0 - np.sum(v**2))\n\n\ndef magnetic_field(x, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return np.array(vars[\"magnetic_field\"])\n\n\ndef divergence_cleaning_field(x, *args, **kwargs):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_BlastWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <limits>\n#include <memory>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BlastWave.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct BlastWaveProxy : grmhd::AnalyticData::BlastWave {\n  using grmhd::AnalyticData::BlastWave::BlastWave;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  register_classes_with_charm<grmhd::AnalyticData::BlastWave>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::BlastWave>(\n          \"BlastWave:\\n\"\n          \"  InnerRadius: 0.8\\n\"\n          \"  OuterRadius: 1.0\\n\"\n          \"  InnerDensity: 1.0e-2\\n\"\n          \"  OuterDensity: 1.0e-4\\n\"\n          \"  InnerPressure: 1.0\\n\"\n          \"  OuterPressure: 5.0e-4\\n\"\n          \"  MagneticField: [0.1, 0.0, 0.0]\\n\"\n          \"  AdiabaticIndex: 1.3333333333333333333\\n\"\n          \"  Geometry: Cylindrical\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& cylindrical_blast_wave =\n      dynamic_cast<const grmhd::AnalyticData::BlastWave&>(\n          *deserialized_option_solution);\n\n  CHECK(cylindrical_blast_wave ==\n        grmhd::AnalyticData::BlastWave(\n            0.8, 1.0, 1.0e-2, 1.0e-4, 1.0, 5.0e-4,\n            std::array<double, 3>{{0.1, 0.0, 0.0}}, 1.3333333333333333333,\n            grmhd::AnalyticData::BlastWave::Geometry::Cylindrical));\n}\n\nvoid test_move() {\n  grmhd::AnalyticData::BlastWave cylindrical_blast_wave(\n      0.8, 1.0, 1.0e-2, 1.0e-4, 1.0, 5.0e-4,\n      std::array<double, 3>{{0.1, 0.0, 0.0}}, 1.3333333333333333333,\n      grmhd::AnalyticData::BlastWave::Geometry::Cylindrical);\n  grmhd::AnalyticData::BlastWave cylindrical_blast_wave_copy(\n      0.8, 1.0, 1.0e-2, 1.0e-4, 1.0, 5.0e-4,\n      std::array<double, 3>{{0.1, 0.0, 0.0}}, 1.3333333333333333333,\n      grmhd::AnalyticData::BlastWave::Geometry::Cylindrical);\n  test_move_semantics(std::move(cylindrical_blast_wave),\n                      cylindrical_blast_wave_copy);  //  NOLINT\n\n  grmhd::AnalyticData::BlastWave spherical_blast_wave(\n      0.8, 1.0, 1.0e-2, 1.0e-4, 1.0, 5.0e-4,\n      std::array<double, 3>{{0.1, 0.0, 0.0}}, 1.3333333333333333333,\n      grmhd::AnalyticData::BlastWave::Geometry::Spherical);\n  grmhd::AnalyticData::BlastWave spherical_blast_wave_copy(\n      0.8, 1.0, 1.0e-2, 1.0e-4, 1.0, 5.0e-4,\n      std::array<double, 3>{{0.1, 0.0, 0.0}}, 1.3333333333333333333,\n      grmhd::AnalyticData::BlastWave::Geometry::Spherical);\n  test_move_semantics(std::move(spherical_blast_wave),\n                      spherical_blast_wave_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  grmhd::AnalyticData::BlastWave cylindrical_blast_wave(\n      0.8, 1.0, 1.0e-2, 1.0e-4, 1.0, 5.0e-4,\n      std::array<double, 3>{{0.1, 0.0, 0.0}}, 1.3333333333333333333,\n      grmhd::AnalyticData::BlastWave::Geometry::Cylindrical);\n  test_serialization(cylindrical_blast_wave);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size,\n                    const grmhd::AnalyticData::BlastWave::Geometry geometry) {\n  const double inner_radius = 0.8;\n  const double outer_radius = 1.0;\n  const double inner_density = 1.0e-2;\n  const double outer_density = 1.0e-4;\n  const double inner_pressure = 1.0;\n  const double outer_pressure = 5.0e-4;\n  const std::array<double, 3> magnetic_field{{0.1, 0.0, 0.0}};\n  const double adiabatic_index = 1.3333333333333333333;\n\n  // last argument is 1.0 if Cylindrical and 0.0 if Spherical geometry\n  const auto member_variables = std::make_tuple(\n      inner_radius, outer_radius, inner_density, outer_density, inner_pressure,\n      outer_pressure, magnetic_field, adiabatic_index,\n      geometry == grmhd::AnalyticData::BlastWave::Geometry::Cylindrical ? 1.0\n                                                                        : 0.0);\n\n  BlastWaveProxy blast_wave(inner_radius, outer_radius, inner_density,\n                            outer_density, inner_pressure, outer_pressure,\n                            magnetic_field, adiabatic_index, geometry);\n\n  // Note: I select random numbers in the range {{-1.1, 1.1}} so that\n  // sometimes the random points are in the transition region and sometimes\n  // in the fixed region.\n  pypp::check_with_random_values<1>(\n      &BlastWaveProxy::hydro_variables<DataType>, blast_wave, \"BlastWave\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\"},\n      {{{-1.1, 1.1}}}, member_variables, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &BlastWaveProxy::grmhd_variables<DataType>, blast_wave, \"BlastWave\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\", \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{-1.1, 1.1}}}, member_variables, used_for_size);\n}\n\n// Check points on and near boundaries. Check that density = inner_density\n// at r = inner radius and at r = inner_radius - epsilon, and check that\n// density is approximately inner_density at r = inner_radius + epsilon.\n// Then do the analogous check for points on and near outer_radius.\nvoid test_density_on_and_near_boundaries(\n    const grmhd::AnalyticData::BlastWave::Geometry geometry) {\n  const double inner_radius = 0.8;\n  const double outer_radius = 1.0;\n  const double inner_density = 1.0e-2;\n  const double outer_density = 1.0e-4;\n  const double inner_pressure = 1.0;\n  const double outer_pressure = 5.0e-4;\n  const std::array<double, 3> magnetic_field{{0.1, 0.0, 0.0}};\n  const double adiabatic_index = 1.3333333333333333333;\n\n  BlastWaveProxy blast_wave(inner_radius, outer_radius, inner_density,\n                            outer_density, inner_pressure, outer_pressure,\n                            magnetic_field, adiabatic_index, geometry);\n\n  const double epsilon = 1.e-10;\n  Approx approx = Approx::custom().epsilon(epsilon * 100.0);\n  auto x =\n      make_with_value<tnsr::I<double, 3, Frame::Inertial>>(inner_radius, 0.0);\n\n  get<0>(x) = inner_radius;\n  CHECK(inner_density == get(get<hydro::Tags::RestMassDensity<double>>(\n                             blast_wave.grmhd_variables(x))));\n  get<0>(x) = inner_radius - epsilon;\n  CHECK(inner_density == get(get<hydro::Tags::RestMassDensity<double>>(\n                             blast_wave.grmhd_variables(x))));\n  get<0>(x) = inner_radius + epsilon;\n  CHECK_ITERABLE_CUSTOM_APPROX(inner_density,\n                               get(get<hydro::Tags::RestMassDensity<double>>(\n                                   blast_wave.grmhd_variables(x))),\n                               approx);\n\n  get<0>(x) = outer_radius;\n  CHECK(outer_density == get(get<hydro::Tags::RestMassDensity<double>>(\n                             blast_wave.grmhd_variables(x))));\n  get<0>(x) = outer_radius + epsilon;\n  CHECK(outer_density == get(get<hydro::Tags::RestMassDensity<double>>(\n                             blast_wave.grmhd_variables(x))));\n  get<0>(x) = outer_radius - epsilon;\n  CHECK_ITERABLE_CUSTOM_APPROX(outer_density,\n                               get(get<hydro::Tags::RestMassDensity<double>>(\n                                   blast_wave.grmhd_variables(x))),\n                               approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.BlastWave\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  for (const auto geometry :\n       {grmhd::AnalyticData::BlastWave::Geometry::Cylindrical,\n        grmhd::AnalyticData::BlastWave::Geometry::Spherical}) {\n    test_variables(std::numeric_limits<double>::signaling_NaN(), geometry);\n    test_variables(DataVector(5), geometry);\n    test_density_on_and_near_boundaries(geometry);\n  }\n\n  CHECK_THROWS_WITH(\n      (grmhd::AnalyticData::BlastWave(\n          1.2, 1.0, 1.0e-2, 1.0e-4, 1.0, 5.0e-4,\n          std::array<double, 3>{{0.1, 0.0, 0.0}}, 1.3333333333333333333,\n          grmhd::AnalyticData::BlastWave::Geometry::Cylindrical)),\n      Catch::Matchers::ContainsSubstring(\n          \"BlastWave expects InnerRadius < OuterRadius\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_BondiHoyleAccretion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <limits>\n#include <memory>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/BondiHoyleAccretion.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct BondiHoyleAccretionProxy : grmhd::AnalyticData::BondiHoyleAccretion {\n  using grmhd::AnalyticData::BondiHoyleAccretion::BondiHoyleAccretion;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  register_classes_with_charm<grmhd::AnalyticData::BondiHoyleAccretion>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::BondiHoyleAccretion>(\n          \"BondiHoyleAccretion:\\n\"\n          \"  BhMass: 1.0\\n\"\n          \"  BhDimlessSpin: 0.23\\n\"\n          \"  RestMassDensity: 2.7\\n\"\n          \"  FlowSpeed: 0.34\\n\"\n          \"  MagFieldStrength: 5.76\\n\"\n          \"  PolytropicConstant: 30.0\\n\"\n          \"  PolytropicExponent: 1.5\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& accretion =\n      dynamic_cast<const grmhd::AnalyticData::BondiHoyleAccretion&>(\n          *deserialized_option_solution);\n  CHECK(accretion == grmhd::AnalyticData::BondiHoyleAccretion(\n                         1.0, 0.23, 2.7, 0.34, 5.76, 30.0, 1.5));\n}\n\nvoid test_move() {\n  grmhd::AnalyticData::BondiHoyleAccretion accretion(0.2, 0.12, 1.1, 0.63, 3.1,\n                                                     251.4, 1.4);\n  grmhd::AnalyticData::BondiHoyleAccretion accretion_copy(0.2, 0.12, 1.1, 0.63,\n                                                          3.1, 251.4, 1.4);\n  test_move_semantics(std::move(accretion), accretion_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  grmhd::AnalyticData::BondiHoyleAccretion accretion(0.2, 0.12, 1.1, 0.63, 3.1,\n                                                     133.7, 1.65);\n  test_serialization(accretion);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double bh_mass = 1.6;\n  const double bh_dimless_spin = 0.24;\n  const double rest_mass_density = 1.42;\n  const double flow_speed = 0.87;\n  const double mag_field_strength = 2.3;\n  const double polytropic_constant = 25.0;\n  const double polytropic_exponent = 4.0 / 3.0;\n  const auto member_variables = std::make_tuple(\n      bh_mass, bh_dimless_spin, rest_mass_density, flow_speed,\n      mag_field_strength, polytropic_constant, polytropic_exponent);\n\n  BondiHoyleAccretionProxy accretion(\n      bh_mass, bh_dimless_spin, rest_mass_density, flow_speed,\n      mag_field_strength, polytropic_constant, polytropic_exponent);\n\n  pypp::check_with_random_values<1>(\n      &BondiHoyleAccretionProxy::hydro_variables<DataType>, accretion,\n      \"PointwiseFunctions.AnalyticData.GrMhd.BondiHoyleAccretion\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\"},\n      {{{-10., 10.}}}, member_variables, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &BondiHoyleAccretionProxy::grmhd_variables<DataType>, accretion,\n      \"PointwiseFunctions.AnalyticData.GrMhd.BondiHoyleAccretion\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\", \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{-10., 10.}}}, member_variables, used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.BondiHoyle\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::BondiHoyleAccretion>(\n          \"BhMass: -1.345\\n\"\n          \"BhDimlessSpin: 0.3\\n\"\n          \"RestMassDensity: 1.32\\n\"\n          \"FlowSpeed: 4.3\\n\"\n          \"MagFieldStrength: 0.52\\n\"\n          \"PolytropicConstant: 0.12\\n\"\n          \"PolytropicExponent: 1.5\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -1.345 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::BondiHoyleAccretion>(\n          \"BhMass: 4.4231\\n\"\n          \"BhDimlessSpin: -1.23\\n\"\n          \"RestMassDensity: 0.31\\n\"\n          \"FlowSpeed: 0.11\\n\"\n          \"MagFieldStrength: 2.4\\n\"\n          \"PolytropicConstant: 300.0\\n\"\n          \"PolytropicExponent: 1.4\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -1.23 is below the lower bound of -1\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::BondiHoyleAccretion>(\n          \"BhMass: 0.654\\n\"\n          \"BhDimlessSpin: 3.99\\n\"\n          \"RestMassDensity: 5.234\\n\"\n          \"FlowSpeed: 0.543\\n\"\n          \"MagFieldStrength: -0.352\\n\"\n          \"PolytropicConstant: 80.123\\n\"\n          \"PolytropicExponent: 1.66\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value 3.99 is above the upper bound of 1\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::BondiHoyleAccretion>(\n          \"BhMass: 12.34\\n\"\n          \"BhDimlessSpin: 0.99\\n\"\n          \"RestMassDensity: -4.21\\n\"\n          \"FlowSpeed: 1.3\\n\"\n          \"MagFieldStrength: 0.21\\n\"\n          \"PolytropicConstant: 54.16\\n\"\n          \"PolytropicExponent: 1.598\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -4.21 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::BondiHoyleAccretion>(\n          \"BhMass: 0.765\\n\"\n          \"BhDimlessSpin: -0.324\\n\"\n          \"RestMassDensity: 156.2\\n\"\n          \"FlowSpeed: 0.653\\n\"\n          \"MagFieldStrength: 1.454\\n\"\n          \"PolytropicConstant: -1.52\\n\"\n          \"PolytropicExponent: 2.0\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -1.52 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::BondiHoyleAccretion>(\n          \"BhMass: 4.21\\n\"\n          \"BhDimlessSpin: -0.11\\n\"\n          \"RestMassDensity: 0.43\\n\"\n          \"FlowSpeed: 0.435\\n\"\n          \"MagFieldStrength: 3.44\\n\"\n          \"PolytropicConstant: 0.653\\n\"\n          \"PolytropicExponent: 0.123\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value 0.123 is below the lower bound of 1\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_CcsnCollapse.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <memory>\n\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/CcsnCollapse.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace grmhd::AnalyticData {\nnamespace {\n\nstatic_assert(\n    not is_analytic_solution_v<CcsnCollapse>,\n    \"CcsnCollapse should be analytic_data and not an analytic_solution\");\nstatic_assert(\n    is_analytic_data_v<CcsnCollapse>,\n    \"CcsnCollapse should be analytic_data and not an analytic_solution\");\n\n// Make sure different parameters give different answers\nvoid test_equality(const std::string& progenitor_filename,\n                   double central_angular_velocity, double diff_rot_parameter,\n                   double max_dens_ratio, const std::string& eos_filename,\n                   const std::string& eos_subfilename) {\n  register_classes_with_charm<grmhd::AnalyticData::CcsnCollapse>();\n  // Base case for comparison\n  const CcsnCollapse ccsn_progenitor_original{\n      progenitor_filename, central_angular_velocity,\n      diff_rot_parameter,  max_dens_ratio,\n      eos_filename,        eos_subfilename};\n  // Different progenitor name\n  CHECK_THROWS_WITH(CcsnCollapse(progenitor_filename + \"bad_name\",\n                                 central_angular_velocity, diff_rot_parameter,\n                                 max_dens_ratio, eos_filename, eos_subfilename),\n                    Catch::Matchers::ContainsSubstring(\"Data file not found\"));\n\n  // Control case\n  const auto ccsn_progenitor =\n      serialize_and_deserialize(ccsn_progenitor_original);\n  CHECK(ccsn_progenitor == CcsnCollapse(progenitor_filename,\n                                        central_angular_velocity,\n                                        diff_rot_parameter, max_dens_ratio,\n                                        eos_filename, eos_subfilename));\n  // Different central angular velocity\n  CHECK(ccsn_progenitor != CcsnCollapse(progenitor_filename,\n                                        central_angular_velocity + 0.1,\n                                        diff_rot_parameter, max_dens_ratio,\n                                        eos_filename, eos_subfilename));\n  // Different differential rotation parameter\n  CHECK(ccsn_progenitor !=\n        CcsnCollapse(progenitor_filename, central_angular_velocity,\n                     diff_rot_parameter + 100, max_dens_ratio, eos_filename,\n                     eos_subfilename));\n}\n\nvoid test_ccsn_collapse(const std::string& progenitor_filename,\n                        const double central_angular_velocity,\n                        const double diff_rot_parameter,\n                        const double max_dens_ratio,\n                        const std::string& eos_filename,\n                        const std::string& eos_subfilename) {\n  register_classes_with_charm<grmhd::AnalyticData::CcsnCollapse>();\n\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::CcsnCollapse>(\n          \"CcsnCollapse:\\n\"\n          \"  ProgenitorFilename: \" +\n          progenitor_filename +\n          \"\\n\"\n          \"  CentralAngularVelocity: \" +\n          std::to_string(central_angular_velocity) +\n          \"\\n\"\n          \"  DifferentialRotationParameter: \" +\n          std::to_string(diff_rot_parameter) +\n          \"\\n\"\n          \"  MaxDensityRatioForLinearInterpolation: \" +\n          std::to_string(max_dens_ratio) +\n          \"\\n\"\n          \"  TableFilename: \" +\n          eos_filename +\n          \"\\n\"\n          \"  TableSubFilename: \" +\n          eos_subfilename + \"\\n\")\n          ->get_clone();\n\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& ccsn_progenitor =\n      dynamic_cast<const grmhd::AnalyticData::CcsnCollapse&>(\n          *deserialized_option_solution);\n\n  const Mesh<3> mesh{\n      {{5, 5, 5}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n        Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::GaussLobatto}}};\n  const auto log_coords = logical_coordinates(mesh);\n\n  // Coordinates where we check the data. Includes the origin.\n  tnsr::I<DataVector, 3, Frame::Inertial> in_coords{\n      mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    in_coords.get(i) = 1.0e-2 * (log_coords.get(i) + 1.);\n  }\n\n  const size_t num_radial_points = 1;\n  tnsr::I<DataVector, 3, Frame::Inertial> in_coords_large_radius{\n      num_radial_points, 1.0e30};\n\n  INFO(\"Check Physicality of Results\");\n\n  const auto vars = ccsn_progenitor.variables(\n      in_coords,\n      tmpl::list<hydro::Tags::RestMassDensity<DataVector>,\n                 hydro::Tags::ElectronFraction<DataVector>,\n                 hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::SpecificEnthalpy<DataVector>,\n                 hydro::Tags::Pressure<DataVector>,\n                 hydro::Tags::SpecificInternalEnergy<DataVector>,\n                 hydro::Tags::LorentzFactor<DataVector>,\n                 hydro::Tags::MagneticField<DataVector, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataVector>,\n                 gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n                 gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::SqrtDetSpatialMetric<DataVector>,\n                 gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                 ::Tags::deriv<gr::Tags::Lapse<DataVector>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                               Frame::Inertial>,\n                 ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                               tmpl::size_t<3>, Frame::Inertial>>{});\n\n// Check velocity > c check and provide small interpolation ratio test\n// coverage\n#ifdef SPECTRE_DEBUG\n  const std::unique_ptr<evolution::initial_data::InitialData>\n      v_grtr_than_c_solution =\n          TestHelpers::test_option_tag_factory_creation<\n              evolution::initial_data::OptionTags::InitialData,\n              grmhd::AnalyticData::CcsnCollapse>(\n              \"CcsnCollapse:\\n\"\n              \"  ProgenitorFilename: \" +\n              progenitor_filename +\n              \"\\n\"\n              \"  CentralAngularVelocity: 147669\\n\"\n              \"  DifferentialRotationParameter: 1.0e20\\n\"\n              \"  MaxDensityRatioForLinearInterpolation: 0.0\\n\"\n              \"  TableFilename: \" +\n              eos_filename +\n              \"\\n\"\n              \"  TableSubFilename: \" +\n              eos_subfilename + \"\\n\")\n              ->get_clone();\n\n  const auto deserialized_v_grtr_than_c_solution =\n      serialize_and_deserialize(v_grtr_than_c_solution);\n  const auto& ccsn_progenitor_v_grtr_than_c =\n      dynamic_cast<const grmhd::AnalyticData::CcsnCollapse&>(\n          *deserialized_v_grtr_than_c_solution);\n\n  CHECK_THROWS_WITH(\n      ccsn_progenitor_v_grtr_than_c.variables(\n          in_coords, tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3>>{}),\n      Catch::Matchers::ContainsSubstring(\"Spatial velocity\"));\n#endif\n\n  // Check radius too large check\n  CHECK_THROWS_WITH(ccsn_progenitor.variables(\n                        in_coords_large_radius,\n                        tmpl::list<hydro::Tags::RestMassDensity<DataVector>>{}),\n                    Catch::Matchers::ContainsSubstring(\"Requested radius\"));\n\n  // Ensure density is positive\n  const auto& rest_mass_density =\n      get<hydro::Tags::RestMassDensity<DataVector>>(vars);\n\n  CHECK(min(get(rest_mass_density)) > 0.0);\n\n  // Relevant metric variables\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& sqrt_det_spatial_metric_analytic =\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(vars);\n  const auto& det_spatial_metric_numeric =\n      determinant_and_inverse(spatial_metric).first;\n\n  // Parabolic interpolation is used for metric variables,\n  // possibly causing slight deviations below 1.0 (flat metric).\n  Approx custom_approx = Approx::custom().epsilon(1.e-11).scale(1.0);\n\n  // Metric must always be greater or equal to 1.0 (i.e. flat space)\n  CHECK(min(get(sqrt_det_spatial_metric_analytic)) == custom_approx(1.0));\n\n  // Same for numerically calculated determinant case\n  CHECK(min(sqrt(get(det_spatial_metric_numeric))) == custom_approx(1.0));\n\n  // Analytic and numeric determinant of the spatial metric\n  // should be equal (to machine precision)\n  CHECK(max(abs(get(sqrt_det_spatial_metric_analytic) -\n                sqrt(get(det_spatial_metric_numeric)))) == approx(0.0));\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.CcsnCollapse\",\n                  \"[Unit][PointwiseFunctions]\") {\n  // Load proper data at below file path\n  const std::string progenitor_filename =\n      unit_test_src_path() +\n      \"PointwiseFunctions/AnalyticData/GrMhd/\"\n      \"CcsnCollapseID.dat\";\n  const double central_angular_velocity = 1.0e-5;\n  const double diff_rot_parameter = 339.0;\n  const double max_dens_ratio = 100.0;\n  const std::string eos_filename{\n      unit_test_src_path() +\n      \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n  const std::string eos_subfilename = \"dd2\";\n\n  // Test if (de)serialized data are equal\n  test_equality(progenitor_filename, central_angular_velocity,\n                diff_rot_parameter, max_dens_ratio, eos_filename,\n                eos_subfilename);\n\n  // Check physicality of interpolated data\n  test_ccsn_collapse(progenitor_filename, central_angular_velocity,\n                     diff_rot_parameter, max_dens_ratio, eos_filename,\n                     eos_subfilename);\n}\n\n}  // namespace\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_FukaInitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdlib>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/FukaInitialData.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace grmhd::AnalyticData {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.FukaInitialData\",\n                  \"[Unit][PointwiseFunctions]\") {\n  register_classes_with_charm<grmhd::AnalyticData::FukaInitialData>();\n\n  // Get example data directory from environment variable. The example ID is\n  // in `FUKA_ROOT/codes/PythonTools/Example_id` unless installed elsewhere.\n  const char* example_id_dir_ptr = std::getenv(\"FUKA_EXAMPLE_ID_DIR\");\n  REQUIRE(example_id_dir_ptr != nullptr);\n  const std::string example_id_dir{example_id_dir_ptr};\n  REQUIRE_FALSE(example_id_dir.empty());\n  CAPTURE(example_id_dir);\n\n  const auto option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::FukaInitialData>(\n          \"FukaInitialData:\\n\"\n          \"  InfoFilename: \\\"\" +\n          example_id_dir +\n          \"/converged_BNS_TOTAL.togashi.30.6.0.0.2.8.q1.0.0.09.info\\\"\\n\"\n          \"  ElectronFraction: 0.15\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution =\n      dynamic_cast<const grmhd::AnalyticData::FukaInitialData&>(\n          *deserialized_option_solution);\n\n  const tnsr::I<DataVector, 3> coords{{{{15.3}, {0.0}, {0.0}}}};\n  const auto fuka_data =\n      solution.variables(coords, FukaInitialData::tags<DataVector>{});\n\n  CHECK_ITERABLE_APPROX(\n      get(get<hydro::Tags::RestMassDensity<DataVector>>(fuka_data)),\n      DataVector{0.00137492312500218});\n}\n\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_KhInstability.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/KhInstability.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nstruct KhInstabilityProxy : public ::grmhd::AnalyticData::KhInstability {\n  using grmhd::AnalyticData::KhInstability::KhInstability;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>,\n                 hydro::Tags::MagneticField<DataType, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return this->variables(x, variables_tags<DataType>{});\n  }\n};\n\ntemplate <typename DataType>\nvoid test(const DataType& used_for_size) {\n  const double adiabatic_index = 1.43;\n  const double strip_bimedian_height = 0.5;\n  const double strip_thickness = 0.4;\n  const double strip_density = 2.1;\n  const double strip_velocity = 0.3;\n  const double background_density = 2.0;\n  const double background_velocity = -0.2;\n  const double pressure = 1.1;\n  const double perturbation_amplitude = 0.1;\n  const double perturbation_width = 0.01;\n  const std::array<double, 3> magnetic_field{{1.0e-3, 0.0, 0.0}};\n  const auto members = std::make_tuple(\n      adiabatic_index, strip_bimedian_height, strip_thickness, strip_density,\n      strip_velocity, background_density, background_velocity, pressure,\n      perturbation_amplitude, perturbation_width, magnetic_field);\n\n  register_classes_with_charm<grmhd::AnalyticData::KhInstability>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::KhInstability>(\n          \"KhInstability:\\n\"\n          \"  AdiabaticIndex: 1.43\\n\"\n          \"  StripBimedianHeight: 0.5\\n\"\n          \"  StripThickness: 0.4\\n\"\n          \"  StripDensity: 2.1\\n\"\n          \"  StripVelocity: 0.3\\n\"\n          \"  BackgroundDensity: 2.0\\n\"\n          \"  BackgroundVelocity: -0.2\\n\"\n          \"  Pressure: 1.1\\n\"\n          \"  PerturbAmplitude: 0.1\\n\"\n          \"  PerturbWidth: 0.01\\n\"\n          \"  MagneticField: [1.0e-3, 0.0, 0.0]\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& kh_instability =\n      dynamic_cast<const grmhd::AnalyticData::KhInstability&>(\n          *deserialized_option_solution);\n\n  CHECK(kh_instability == grmhd::AnalyticData::KhInstability(\n                              adiabatic_index, strip_bimedian_height,\n                              strip_thickness, strip_density, strip_velocity,\n                              background_density, background_velocity, pressure,\n                              perturbation_amplitude, perturbation_width,\n                              magnetic_field));\n\n  KhInstabilityProxy kh_inst_to_move(\n      adiabatic_index, strip_bimedian_height, strip_thickness, strip_density,\n      strip_velocity, background_density, background_velocity, pressure,\n      perturbation_amplitude, perturbation_width, magnetic_field);\n  KhInstabilityProxy kh_inst(\n      adiabatic_index, strip_bimedian_height, strip_thickness, strip_density,\n      strip_velocity, background_density, background_velocity, pressure,\n      perturbation_amplitude, perturbation_width, magnetic_field);\n  test_move_semantics(std::move(kh_inst_to_move), kh_inst);  //  NOLINT\n\n  // run post-serialized state through checks with random numbers\n  pypp::check_with_random_values<1>(\n      &KhInstabilityProxy::template primitive_variables<DataType>,\n      serialize_and_deserialize(kh_inst), \"KhInstability\",\n      {\"rest_mass_density\", \"electron_fraction\", \"velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\", \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{0.0, 1.0}}}, members, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.KhInstability\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd\"};\n\n  test(std::numeric_limits<double>::signaling_NaN());\n  test(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_MagneticFieldLoop.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <limits>\n#include <memory>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticFieldLoop.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct MagneticFieldLoopProxy : grmhd::AnalyticData::MagneticFieldLoop {\n  using grmhd::AnalyticData::MagneticFieldLoop::MagneticFieldLoop;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  register_classes_with_charm<grmhd::AnalyticData::MagneticFieldLoop>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::MagneticFieldLoop>(\n          \"MagneticFieldLoop:\\n\"\n          \"  Pressure: 3.0\\n\"\n          \"  RestMassDensity: 1.0\\n\"\n          \"  AdiabaticIndex: 1.66666666666666667\\n\"\n          \"  AdvectionVelocity: [0.5, 0.04166666666666667, 0.0]\\n\"\n          \"  MagFieldStrength: 0.001\\n\"\n          \"  InnerRadius: 0.06\\n\"\n          \"  OuterRadius: 0.3\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& magnetic_field_loop =\n      dynamic_cast<const grmhd::AnalyticData::MagneticFieldLoop&>(\n          *deserialized_option_solution);\n\n  CHECK(magnetic_field_loop ==\n        grmhd::AnalyticData::MagneticFieldLoop(\n            3.0, 1.0, 1.66666666666666667,\n            std::array<double, 3>{{0.5, 0.04166666666666667, 0.0}}, 0.001, 0.06,\n            0.3));\n}\n\nvoid test_move() {\n  grmhd::AnalyticData::MagneticFieldLoop magnetic_field_loop(\n      3.0, 1.0, 1.66666666666666667,\n      std::array<double, 3>{{0.5, 0.04166666666666667, 0.0}}, 0.001, 0.06, 0.3);\n  grmhd::AnalyticData::MagneticFieldLoop magnetic_field_loop_copy(\n      3.0, 1.0, 1.66666666666666667,\n      std::array<double, 3>{{0.5, 0.04166666666666667, 0.0}}, 0.001, 0.06, 0.3);\n  test_move_semantics(std::move(magnetic_field_loop),\n                      magnetic_field_loop_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  grmhd::AnalyticData::MagneticFieldLoop magnetic_field_loop(\n      3.0, 1.0, 1.66666666666666667,\n      std::array<double, 3>{{0.5, 0.04166666666666667, 0.0}}, 0.001, 0.06, 0.3);\n  test_serialization(magnetic_field_loop);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double pressure = 3.0;\n  const double rest_mass_density = 1.0;\n  const double adiabatic_index = 1.6666666666666666;\n  const std::array<double, 3> advection_velocity{\n      {0.5, 0.04166666666666667, 0.0}};\n  const double magnetic_field_magnitude = 0.001;\n  const double inner_radius = 0.06;\n  const double outer_radius = 0.3;\n\n  const auto member_variables = std::make_tuple(\n      pressure, rest_mass_density, adiabatic_index, advection_velocity,\n      magnetic_field_magnitude, inner_radius, outer_radius);\n\n  MagneticFieldLoopProxy magnetic_field_loop(\n      pressure, rest_mass_density, adiabatic_index, advection_velocity,\n      magnetic_field_magnitude, inner_radius, outer_radius);\n\n  pypp::check_with_random_values<1>(\n      &MagneticFieldLoopProxy::hydro_variables<DataType>, magnetic_field_loop,\n      \"MagneticFieldLoop\",\n      {\"compute_rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"compute_pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\"},\n      {{{-1.0, 1.0}}}, member_variables, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &MagneticFieldLoopProxy::grmhd_variables<DataType>, magnetic_field_loop,\n      \"MagneticFieldLoop\",\n      {\"compute_rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"compute_pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\", \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{-1.0, 1.0}}}, member_variables, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.GrMhd.MagneticFieldLoop\",\n    \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n  CHECK_THROWS_WITH((grmhd::AnalyticData::MagneticFieldLoop(\n                        3.0, 1.0, 1.66666666666666667,\n                        std::array<double, 3>{{0.5, 0.04166666666666667, -0.9}},\n                        0.001, 0.06, 0.3)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"MagneticFieldLoop: superluminal AdvectionVelocity\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_MagneticRotor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <limits>\n#include <memory>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagneticRotor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct MagneticRotorProxy : grmhd::AnalyticData::MagneticRotor {\n  using grmhd::AnalyticData::MagneticRotor::MagneticRotor;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  register_classes_with_charm<grmhd::AnalyticData::MagneticRotor>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::MagneticRotor>(\n          \"MagneticRotor:\\n\"\n          \"  RotorRadius: 0.1\\n\"\n          \"  RotorDensity: 10.0\\n\"\n          \"  BackgroundDensity: 1.0\\n\"\n          \"  Pressure: 1.0\\n\"\n          \"  AngularVelocity: 9.95\\n\"\n          \"  MagneticField: [3.5449077018, 0.0, 0.0]\\n\"\n          \"  AdiabaticIndex: 1.6666666666666666\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& magnetic_rotor =\n      dynamic_cast<const grmhd::AnalyticData::MagneticRotor&>(\n          *deserialized_option_solution);\n\n  CHECK(magnetic_rotor == grmhd::AnalyticData::MagneticRotor(\n                              0.1, 10.0, 1.0, 1.0, 9.95,\n                              std::array<double, 3>{{3.5449077018, 0.0, 0.0}},\n                              1.6666666666666666));\n}\n\nvoid test_move() {\n  grmhd::AnalyticData::MagneticRotor magnetic_rotor(\n      0.1, 10.0, 1.0, 1.0, 9.95,\n      std::array<double, 3>{{3.5449077018, 0.0, 0.0}}, 1.6666666666666666);\n  grmhd::AnalyticData::MagneticRotor magnetic_rotor_copy(\n      0.1, 10.0, 1.0, 1.0, 9.95,\n      std::array<double, 3>{{3.5449077018, 0.0, 0.0}}, 1.6666666666666666);\n  test_move_semantics(std::move(magnetic_rotor),\n                      magnetic_rotor_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  grmhd::AnalyticData::MagneticRotor magnetic_rotor(\n      0.1, 10.0, 1.0, 1.0, 9.95,\n      std::array<double, 3>{{3.5449077018, 0.0, 0.0}}, 1.6666666666666666);\n  test_serialization(magnetic_rotor);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double rotor_radius = 0.1;\n  const double rotor_density = 10.0;\n  const double background_density = 1.0;\n  const double pressure = 1.0;\n  const double angular_velocity = 9.95;\n  const std::array<double, 3> magnetic_field{{3.5449077018, 0.0, 0.0}};\n  const double adiabatic_index = 1.6666666666666666;\n\n  const auto member_variables =\n      std::make_tuple(rotor_radius, rotor_density, background_density, pressure,\n                      angular_velocity, magnetic_field, adiabatic_index);\n\n  MagneticRotorProxy magnetic_rotor(\n      rotor_radius, rotor_density, background_density, pressure,\n      angular_velocity, magnetic_field, adiabatic_index);\n\n  pypp::check_with_random_values<1>(\n      &MagneticRotorProxy::hydro_variables<DataType>, magnetic_rotor,\n      \"MagneticRotor\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"compute_pressure\", \"lorentz_factor\", \"specific_enthalpy\"},\n      {{{-1.0, 1.0}}}, member_variables, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &MagneticRotorProxy::grmhd_variables<DataType>, magnetic_rotor,\n      \"MagneticRotor\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"compute_pressure\", \"lorentz_factor\", \"specific_enthalpy\",\n       \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{-1.0, 1.0}}}, member_variables, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.MagneticRotor\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n  CHECK_THROWS_WITH(\n      (grmhd::AnalyticData::MagneticRotor(\n          0.2, 10.0, 1.0, 1.0, -9.95,\n          std::array<double, 3>{{3.5449077018, 0.0, 0.0}}, 1.6666666666666666)),\n      Catch::Matchers::ContainsSubstring(\n          \"MagneticRotor expects RotorRadius * | AngularVelocity | < 1\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_MagnetizedFmDisk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <limits>\n#include <memory>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstatic_assert(\n    not is_analytic_solution_v<grmhd::AnalyticData::MagnetizedFmDisk>,\n    \"MagnetizedFmDisk should be analytic_data, and not an analytic_solution\");\nstatic_assert(\n    is_analytic_data_v<grmhd::AnalyticData::MagnetizedFmDisk>,\n    \"MagnetizedFmDisk should be analytic_data, and not an analytic_solution\");\nstruct MagnetizedFmDiskProxy : grmhd::AnalyticData::MagnetizedFmDisk {\n  using grmhd::AnalyticData::MagnetizedFmDisk::MagnetizedFmDisk;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3>& x) const {\n    return variables(x, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3>& x) const {\n    return variables(x, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  register_classes_with_charm<grmhd::AnalyticData::MagnetizedFmDisk>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::MagnetizedFmDisk>(\n          \"MagnetizedFmDisk:\\n\"\n          \"  BhMass: 1.3\\n\"\n          \"  BhDimlessSpin: 0.345\\n\"\n          \"  InnerEdgeRadius: 6.123\\n\"\n          \"  MaxPressureRadius: 14.2\\n\"\n          \"  PolytropicConstant: 0.065\\n\"\n          \"  PolytropicExponent: 1.654\\n\"\n          \"  Noise: 0.0\\n\"\n          \"  ThresholdDensity: 0.42\\n\"\n          \"  InversePlasmaBeta: 85.0\\n\"\n          \"  BFieldNormGridRes: 4\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& disk = dynamic_cast<const grmhd::AnalyticData::MagnetizedFmDisk&>(\n      *deserialized_option_solution);\n\n  CHECK(disk == grmhd::AnalyticData::MagnetizedFmDisk(\n                    1.3, 0.345, 6.123, 14.2, 0.065, 1.654, 0.0, 0.42, 85.0, 4));\n}\n\nvoid test_move() {\n  grmhd::AnalyticData::MagnetizedFmDisk disk(3.51, 0.87, 7.43, 15.3, 42.67,\n                                             1.87, 0.0, 0.13, 0.015, 4);\n  const grmhd::AnalyticData::MagnetizedFmDisk disk_copy(\n      3.51, 0.87, 7.43, 15.3, 42.67, 1.87, 0.0, 0.13, 0.015, 4);\n  test_move_semantics(std::move(disk), disk_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  const grmhd::AnalyticData::MagnetizedFmDisk disk(\n      3.51, 0.87, 7.43, 15.3, 42.67, 1.87, 0.0, 0.13, 0.015, 4);\n  test_serialization(disk);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double bh_mass = 1.23;\n  const double bh_dimless_spin = 0.97;\n  const double inner_edge_radius = 6.2;\n  const double max_pressure_radius = 11.6;\n  const double polytropic_constant = 0.034;\n  const double polytropic_exponent = 1.65;\n  const double noise = 0.0;\n  const double threshold_density = 0.14;\n  const double inverse_plasma_beta = 0.023;\n  const size_t b_field_normalization = 51;  // Using lower than default\n                                            //  resolution for faster testing.\n  const MagnetizedFmDiskProxy disk(\n      bh_mass, bh_dimless_spin, inner_edge_radius, max_pressure_radius,\n      polytropic_constant, polytropic_exponent, noise, threshold_density,\n      inverse_plasma_beta, b_field_normalization);\n  const auto member_variables = std::make_tuple(\n      bh_mass, bh_dimless_spin, inner_edge_radius, max_pressure_radius,\n      polytropic_constant, polytropic_exponent, noise, threshold_density,\n      inverse_plasma_beta);\n\n  pypp::check_with_random_values<1>(\n      &MagnetizedFmDiskProxy::grmhd_variables<DataType>, disk,\n      \"PointwiseFunctions.AnalyticData.GrMhd.MagnetizedFmDisk\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"pressure\", \"lorentz_factor\", \"specific_enthalpy\", \"magnetic_field\",\n       \"divergence_cleaning_field\"},\n      {{{-20., 20.}}}, member_variables, used_for_size, 1.0e-8);\n\n  // Test a few of the GR components to make sure that the implementation\n  // correctly forwards to the background solution of the base class.\n  const auto coords = make_with_value<tnsr::I<DataType, 3>>(used_for_size, 1.0);\n  const gr::Solutions::SphericalKerrSchild sks_soln{\n      bh_mass, {{0.0, 0.0, bh_dimless_spin}}, {{0.0, 0.0, 0.0}}};\n  const auto [expected_lapse, expected_sqrt_det_gamma] = sks_soln.variables(\n      coords, 0.0,\n      tmpl::list<gr::Tags::Lapse<DataType>,\n                 gr::Tags::SqrtDetSpatialMetric<DataType>>{});\n  const auto [lapse, sqrt_det_gamma] = disk.variables(\n      coords, tmpl::list<gr::Tags::Lapse<DataType>,\n                         gr::Tags::SqrtDetSpatialMetric<DataType>>{});\n  CHECK_ITERABLE_APPROX(expected_lapse, lapse);\n  CHECK_ITERABLE_APPROX(expected_sqrt_det_gamma, sqrt_det_gamma);\n  const auto expected_spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3>>(sks_soln.variables(\n          coords, 0.0, gr::Solutions::SphericalKerrSchild::tags<DataType>{}));\n  const auto spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3>>(disk.variables(\n          coords, tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{}));\n  CHECK_ITERABLE_APPROX(expected_spatial_metric, spatial_metric);\n\n  // Check that when InversePlasmaBeta = 0, magnetic field vanishes and\n  // we recover FishboneMoncriefDisk\n  const MagnetizedFmDiskProxy another_disk(\n      bh_mass, bh_dimless_spin, inner_edge_radius, max_pressure_radius,\n      polytropic_constant, polytropic_exponent, noise, threshold_density, 0.0,\n      4);\n\n  pypp::check_with_random_values<1>(\n      &MagnetizedFmDiskProxy::hydro_variables<DataType>, another_disk,\n      \"PointwiseFunctions.AnalyticData.GrMhd.MagnetizedFmDisk\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"pressure\", \"lorentz_factor\", \"specific_enthalpy\"},\n      {{{-20., 20.}}}, member_variables, used_for_size, 1.0e-8);\n  const auto [magnetic_field, div_clean_field] =\n      another_disk.variables(\n          coords, tmpl::list<hydro::Tags::SpatialVelocity<DataType, 3>,\n                             hydro::Tags::DivergenceCleaningField<DataType>>{});\n  const auto expected_magnetic_field =\n      make_with_value<tnsr::I<DataType, 3>>(used_for_size, 0.0);\n  const auto expected_div_clean_field =\n      make_with_value<Scalar<DataType>>(used_for_size, 0.0);\n  CHECK_ITERABLE_APPROX(magnetic_field, expected_magnetic_field);\n  CHECK_ITERABLE_APPROX(div_clean_field, expected_div_clean_field);\n}\n}  // namespace\n\n// [[TimeOut, 8]]\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.MagFmDisk\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      []() {\n        grmhd::AnalyticData::MagnetizedFmDisk disk(\n            0.7, 0.61, 5.4, 9.182, 11.123, 1.44, 0.0, -0.2, 0.023, 4);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The threshold density must be in the range (0, 1)\"));\n  CHECK_THROWS_WITH(\n      []() {\n        grmhd::AnalyticData::MagnetizedFmDisk disk(\n            0.7, 0.61, 5.4, 9.182, 11.123, 1.44, 0.0, 1.45, 0.023, 4);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The threshold density must be in the range (0, 1)\"));\n  CHECK_THROWS_WITH(\n      []() {\n        grmhd::AnalyticData::MagnetizedFmDisk disk(\n            0.7, 0.61, 5.4, 9.182, 11.123, 1.44, 0.0, 0.2, -0.153, 4);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The inverse plasma beta must be non-negative.\"));\n  CHECK_THROWS_WITH(\n      []() {\n        grmhd::AnalyticData::MagnetizedFmDisk disk(\n            0.7, 0.61, 5.4, 9.182, 11.123, 1.44, 0.0, 0.2, 0.153, 2);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"The grid resolution used in the magnetic field \"\n          \"normalization must be at least 4 points.\"));\n  CHECK_THROWS_WITH(\n      []() {\n        grmhd::AnalyticData::MagnetizedFmDisk disk(\n            0.7, 0.61, 5.4, 9.182, 11.123, 1.44, 0.0, 0.2, 0.023, 4);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Max b squared is zero.\"));\n#endif\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::MagnetizedFmDisk>(\n          \"BhMass: 13.45\\n\"\n          \"BhDimlessSpin: 0.45\\n\"\n          \"InnerEdgeRadius: 6.1\\n\"\n          \"MaxPressureRadius: 7.6\\n\"\n          \"PolytropicConstant: 2.42\\n\"\n          \"PolytropicExponent: 1.33\\n\"\n          \"Noise: 0.0\\n\"\n          \"ThresholdDensity: -0.01\\n\"\n          \"InversePlasmaBeta: 0.016\\n\"\n          \"BFieldNormGridRes: 4\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -0.01 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::MagnetizedFmDisk>(\n          \"BhMass: 1.5\\n\"\n          \"BhDimlessSpin: 0.94\\n\"\n          \"InnerEdgeRadius: 6.4\\n\"\n          \"MaxPressureRadius: 8.2\\n\"\n          \"PolytropicConstant: 41.1\\n\"\n          \"PolytropicExponent: 1.8\\n\"\n          \"Noise: 0.0\\n\"\n          \"ThresholdDensity: 4.1\\n\"\n          \"InversePlasmaBeta: 0.03\\n\"\n          \"BFieldNormGridRes: 4\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value 4.1 is above the upper bound of 1\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::MagnetizedFmDisk>(\n          \"BhMass: 1.4\\n\"\n          \"BhDimlessSpin: 0.91\\n\"\n          \"InnerEdgeRadius: 6.5\\n\"\n          \"MaxPressureRadius: 7.8\\n\"\n          \"PolytropicConstant: 13.5\\n\"\n          \"PolytropicExponent: 1.54\\n\"\n          \"Noise : 0.0\\n\"\n          \"ThresholdDensity: 0.22\\n\"\n          \"InversePlasmaBeta: -0.03\\n\"\n          \"BFieldNormGridRes: 4\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -0.03 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<grmhd::AnalyticData::MagnetizedFmDisk>(\n          \"BhMass: 1.4\\n\"\n          \"BhDimlessSpin: 0.91\\n\"\n          \"InnerEdgeRadius: 6.5\\n\"\n          \"MaxPressureRadius: 7.8\\n\"\n          \"PolytropicConstant: 13.5\\n\"\n          \"PolytropicExponent: 1.54\\n\"\n          \"Noise: 0.0\\n\"\n          \"ThresholdDensity: 0.22\\n\"\n          \"InversePlasmaBeta: 0.03\\n\"\n          \"BFieldNormGridRes: 2\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value 2 is below the lower bound of 4\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_MagnetizedTovStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/Divergence.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/InitialMagneticField.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/InitialMagneticFields/Poloidal.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/MagnetizedTovStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace grmhd::AnalyticData {\nnamespace {\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<evolution::initial_data::InitialData,\n                             tmpl::list<MagnetizedTovStar>>,\n                  tmpl::pair<InitialMagneticFields::InitialMagneticField,\n                             tmpl::list<InitialMagneticFields::Poloidal>>>;\n  };\n};\n\nstatic_assert(\n    not is_analytic_solution_v<MagnetizedTovStar>,\n    \"MagnetizedTovStar should be analytic_data, and not an analytic_solution\");\nstatic_assert(\n    is_analytic_data_v<MagnetizedTovStar>,\n    \"MagnetizedTovStar should be analytic_data, and not an analytic_solution\");\n\nusing TovCoordinates = RelativisticEuler::Solutions::TovCoordinates;\n\nvoid test_equality() {\n  register_classes_with_charm<grmhd::AnalyticData::MagnetizedTovStar>();\n  register_classes_with_charm<EquationsOfState::PolytropicFluid<true>>();\n  const MagnetizedTovStar mag_tov_original{\n      1.28e-3,\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n      TovCoordinates::Schwarzschild,\n      {}};\n  const auto mag_tov = serialize_and_deserialize(mag_tov_original);\n  CHECK(\n      mag_tov ==\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild, {}));\n  CHECK(\n      mag_tov !=\n      MagnetizedTovStar(\n          2.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild, {}));\n  CHECK(\n      mag_tov !=\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Isotropic, {}));\n  CHECK(\n      mag_tov !=\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild,\n          make_vector<\n              std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                  InitialMagneticField>>(\n              std::make_unique<\n                  grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n                  2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))));\n  CHECK(\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild,\n          make_vector<\n              std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                  InitialMagneticField>>(\n              std::make_unique<\n                  grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n                  2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))) ==\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild,\n          make_vector<\n              std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                  InitialMagneticField>>(\n              std::make_unique<\n                  grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n                  2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))));\n  CHECK(\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild,\n          make_vector<\n              std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                  InitialMagneticField>>(\n              std::make_unique<\n                  grmhd::AnalyticData::InitialMagneticFields::Toroidal>(\n                  2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))) !=\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild,\n          make_vector<\n              std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                  InitialMagneticField>>(\n              std::make_unique<\n                  grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n                  2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))));\n  CHECK(\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild,\n          make_vector<\n              std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                  InitialMagneticField>>(\n              std::make_unique<\n                  grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n                  2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))) !=\n      MagnetizedTovStar(\n          1.28e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n          TovCoordinates::Schwarzschild,\n          make_vector<\n              std::unique_ptr<grmhd::AnalyticData::InitialMagneticFields::\n                                  InitialMagneticField>>(\n              std::make_unique<\n                  grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n                  2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0),\n              std::make_unique<\n                  grmhd::AnalyticData::InitialMagneticFields::Toroidal>(\n                  2, 0.04, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0))));\n  // Check order of magnetic fields doesn't matter.\n  const MagnetizedTovStar toroidal_plus_poloidal(\n      1.28e-3,\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n      TovCoordinates::Schwarzschild,\n      make_vector<std::unique_ptr<\n          grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField>>(\n          std::make_unique<\n              grmhd::AnalyticData::InitialMagneticFields::Toroidal>(\n              2, 1.0e-6, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0),\n          std::make_unique<\n              grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n              2, 1.0e-6, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0)));\n  const MagnetizedTovStar poloidal_plus_toroidal(\n      1.28e-3,\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n      TovCoordinates::Schwarzschild,\n      make_vector<std::unique_ptr<\n          grmhd::AnalyticData::InitialMagneticFields::InitialMagneticField>>(\n          std::make_unique<\n              grmhd::AnalyticData::InitialMagneticFields::Poloidal>(\n              2, 1.0e-6, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0),\n          std::make_unique<\n              grmhd::AnalyticData::InitialMagneticFields::Toroidal>(\n              2, 1.0e-6, 2500.0, std::array{0.0, 0.0, 0.0}, 100.0)));\n  const DataVector coord{2.0};\n  const tnsr::I<DataVector, 3, Frame::Inertial> coords{{coord, coord, coord}};\n  CHECK_ITERABLE_APPROX(\n      (get<hydro::Tags::MagneticField<DataVector, 3>>(\n          poloidal_plus_toroidal.variables(\n              coords,\n              tmpl::list<hydro::Tags::MagneticField<DataVector, 3>>{}))),\n      (get<hydro::Tags::MagneticField<DataVector, 3>>(\n          toroidal_plus_poloidal.variables(\n              coords,\n              tmpl::list<hydro::Tags::MagneticField<DataVector, 3>>{}))));\n}\n\nvoid test_magnetized_tov_star(const TovCoordinates coord_system) {\n  register_factory_classes_with_charm<Metavariables>();\n  register_classes_with_charm<grmhd::AnalyticData::MagnetizedTovStar>();\n  register_classes_with_charm<EquationsOfState::PolytropicFluid<true>>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag<\n          evolution::initial_data::OptionTags::InitialData,\n          Metavariables>(\n          \"MagnetizedTovStar:\\n\"\n          \"  CentralDensity: 1.28e-3\\n\"\n          \"  EquationOfState:\\n\"\n          \"    PolytropicFluid:\\n\"\n          \"      PolytropicConstant: 100.0\\n\"\n          \"      PolytropicExponent: 2.0\\n\"\n          \"  Coordinates: \" +\n          get_output(coord_system) +\n          \"\\n\"\n          \"  MagneticFields:\\n\"\n          \"    - Poloidal:\\n\"\n          \"        PressureExponent: 2\\n\"\n          \"        VectorPotentialAmplitude: 2500\\n\"\n          \"        CutoffPressure: 6.5536e-06\\n\"  // 0.04 * 100 * (1.28e-3)**2\n          \"        Center: [0.0, 0.0, 0.0]\\n\"\n          \"        MaxDistanceFromCenter: 100.0\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& mag_tov =\n      dynamic_cast<const grmhd::AnalyticData::MagnetizedTovStar&>(\n          *deserialized_option_solution);\n\n  const RelativisticEuler::Solutions::TovStar tov{\n      1.28e-3,\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0),\n      coord_system};\n\n  std::unique_ptr<EquationsOfState::EquationOfState<true, 1>> eos =\n      std::make_unique<EquationsOfState::PolytropicFluid<true>>(100.0, 2.0);\n\n  const Mesh<3> mesh{\n      {{5, 5, 5}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n        Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::GaussLobatto}}};\n  const auto log_coords = logical_coordinates(mesh);\n\n  // Coordinates where we check the data.\n  tnsr::I<DataVector, 3, Frame::Inertial> inertial_coords{\n      mesh.number_of_grid_points(), 0.0};\n  const double scale = 1.0e-2;\n  InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>\n      inv_jac{mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    inv_jac.get(i, i) = 1.0 / scale;\n  }\n\n  const auto test_for_small_coords_patch = [&inv_jac, &mag_tov, &mesh,\n                                            &tov](const tnsr::I<DataVector, 3>&\n                                                      in_coords) {\n    // Check that the non-magnetic field tags match the unmagnetized solution.\n    using tov_tags = tmpl::remove<\n        tmpl::append<hydro::grmhd_tags<DataVector>,\n                     gr::tags_for_hydro<3, DataVector>>,\n        hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>;\n    tmpl::for_each<tov_tags>(\n        [tov_values = tov.variables(in_coords, 0.0, tov_tags{}),\n         mag_tov_values =\n             mag_tov.variables(in_coords, tov_tags{})](auto tag_v) {\n          using tag = tmpl::type_from<decltype(tag_v)>;\n          CHECK_ITERABLE_APPROX(get<tag>(mag_tov_values), get<tag>(tov_values));\n        });\n\n    // Verify that the resulting magnetic field has (approximately) vanishing\n    // covariant divergence, but is non-zero overall.\n    INFO(\"Check magnetic field\");\n    const auto vars = mag_tov.variables(\n        in_coords,\n        tmpl::list<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>,\n                   gr::Tags::SqrtDetSpatialMetric<DataVector>>{});\n    const auto& b_field =\n        get<hydro::Tags::MagneticField<DataVector, 3, Frame::Inertial>>(vars);\n    const auto& sqrt_det_spatial_metric =\n        get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(vars);\n    auto tilde_b = b_field;\n    double b_field_l2norm = 0.;\n    for (size_t i = 0; i < 3; ++i) {\n      tilde_b.get(i) *= get(sqrt_det_spatial_metric);\n      b_field_l2norm += sum(square(b_field.get(i)));\n    }\n\n    b_field_l2norm = sqrt(b_field_l2norm);\n    CHECK(b_field_l2norm != approx(0.));\n    const auto div_tilde_b = divergence(tilde_b, mesh, inv_jac);\n    CHECK(max(abs(get(div_tilde_b))) < 1.0e-6 * b_field_l2norm);\n  };\n\n  // check a small region around the origin\n  for (size_t i = 0; i < 3; ++i) {\n    inertial_coords.get(i) = scale * log_coords.get(i);\n  }\n  test_for_small_coords_patch(inertial_coords);\n\n  // check a small region off-origin\n  inertial_coords.get(0) += 0.5;\n  inertial_coords.get(1) += 1.0;\n  inertial_coords.get(2) += 2.0;\n  test_for_small_coords_patch(inertial_coords);\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.MagTovStar\",\n                  \"[Unit][PointwiseFunctions]\") {\n  test_equality();\n  test_magnetized_tov_star(TovCoordinates::Schwarzschild);\n  test_magnetized_tov_star(TovCoordinates::Isotropic);\n}\n\n}  // namespace\n}  // namespace grmhd::AnalyticData\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_OrszagTangVortex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/OrszagTangVortex.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing grmhd::AnalyticData::OrszagTangVortex;\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  using tags = tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                          hydro::Tags::SpatialVelocity<DataType, 3>,\n                          hydro::Tags::SpecificInternalEnergy<DataType>,\n                          hydro::Tags::Pressure<DataType>,\n                          hydro::Tags::LorentzFactor<DataType>,\n                          hydro::Tags::SpecificEnthalpy<DataType>,\n                          hydro::Tags::MagneticField<DataType, 3>,\n                          hydro::Tags::DivergenceCleaningField<DataType>>;\n  const auto names = make_array(\n      \"rest_mass_density\"s, \"spatial_velocity\"s, \"specific_internal_energy\"s,\n      \"pressure\"s, \"lorentz_factor\"s, \"specific_enthalpy\"s, \"magnetic_field\"s,\n      \"divergence_cleaning_field\"s);\n\n  tmpl::for_each<tags>([&used_for_size,\n                        name = names.begin()](\n                           auto tag) mutable {  // NOLINT(spectre-mutable)\n    using Tag = tmpl::type_from<decltype(tag)>;\n    pypp::check_with_random_values<1>(\n        +[](const tnsr::I<DataType, 3>& x) {\n          auto result =\n              get<Tag>(OrszagTangVortex{}.variables(x, tmpl::list<Tag>{}));\n          CHECK(result == get<Tag>(OrszagTangVortex{}.variables(x, tags{})));\n          return result;\n        },\n        \"OrszagTangVortex\", *name++, {{{-10., 10.}}}, used_for_size);\n  });\n\n  // Check that metric variables are being forwarded\n  using lapse_tag = gr::Tags::Lapse<DataType>;\n  using LapseType = typename lapse_tag::type;\n  CHECK(get<lapse_tag>(OrszagTangVortex{}.variables(\n            make_with_value<tnsr::I<DataType, 3>>(used_for_size, 3.),\n            tmpl::list<lapse_tag>{})) ==\n        make_with_value<LapseType>(used_for_size, 1.));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.OrszagTangVortex\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd\"};\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n  test_serialization(OrszagTangVortex{});\n  TestHelpers::test_creation<OrszagTangVortex>(\"\");\n\n  register_classes_with_charm<grmhd::AnalyticData::OrszagTangVortex>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::OrszagTangVortex>(\"OrszagTangVortex:\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution =\n      dynamic_cast<const grmhd::AnalyticData::OrszagTangVortex&>(\n          *deserialized_option_solution);\n  CHECK(solution == grmhd::AnalyticData::OrszagTangVortex{});\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_PolarMagnetizedFmDisk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticData/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/PolarMagnetizedFmDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SphericalTorus.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nstatic_assert(\n    not is_analytic_solution_v<grmhd::AnalyticData::PolarMagnetizedFmDisk>,\n    \"PolarMagnetizedFmDisk should be analytic_data, and not an \"\n    \"analytic_solution\");\nstatic_assert(is_analytic_data_v<grmhd::AnalyticData::PolarMagnetizedFmDisk>,\n              \"PolarMagnetizedFmDisk should be analytic_data, and not an \"\n              \"analytic_solution\");\n\nvoid test_create_from_options() {\n  register_classes_with_charm<grmhd::AnalyticData::PolarMagnetizedFmDisk>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::PolarMagnetizedFmDisk>(\n          \"PolarMagnetizedFmDisk:\\n\"\n          \"  DiskParameters:\\n\"\n          \"    BhMass: 1.3\\n\"\n          \"    BhDimlessSpin: 0.345\\n\"\n          \"    InnerEdgeRadius: 6.123\\n\"\n          \"    MaxPressureRadius: 14.2\\n\"\n          \"    PolytropicConstant: 0.065\\n\"\n          \"    PolytropicExponent: 1.654\\n\"\n          \"    Noise: 0.0\\n\"\n          \"    ThresholdDensity: 0.42\\n\"\n          \"    InversePlasmaBeta: 85.0\\n\"\n          \"    BFieldNormGridRes: 4\\n\"\n          \"  TorusParameters:\\n\"\n          \"    RadialRange: [2.5, 3.6]\\n\"\n          \"    MinPolarAngle: 0.9\\n\"\n          \"    FractionOfTorus: 0.7\\n\"\n          \"    CompressionLevel: 0.0\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& disk =\n      dynamic_cast<const grmhd::AnalyticData::PolarMagnetizedFmDisk&>(\n          *deserialized_option_solution);\n  CHECK(disk ==\n        grmhd::AnalyticData::PolarMagnetizedFmDisk(\n            grmhd::AnalyticData::MagnetizedFmDisk(\n                1.3, 0.345, 6.123, 14.2, 0.065, 1.654, 0.0, 0.42, 85.0, 4),\n            grmhd::AnalyticData::SphericalTorus(2.5, 3.6, 0.9, 0.7, 0.0)));\n}\n\nvoid test_move() {\n  grmhd::AnalyticData::PolarMagnetizedFmDisk disk(\n      grmhd::AnalyticData::MagnetizedFmDisk(1.3, 0.345, 6.123, 14.2, 0.065,\n                                            1.654, 0.0, 0.42, 85.0, 4),\n      grmhd::AnalyticData::SphericalTorus(2.5, 3.6, 0.9, 0.7, 0.0));\n  grmhd::AnalyticData::PolarMagnetizedFmDisk disk_copy(\n      grmhd::AnalyticData::MagnetizedFmDisk(1.3, 0.345, 6.123, 14.2, 0.065,\n                                            1.654, 0.0, 0.42, 85.0, 4),\n      grmhd::AnalyticData::SphericalTorus(2.5, 3.6, 0.9, 0.7, 0.0));\n  test_move_semantics(std::move(disk), disk_copy);\n}\n\nvoid test_serialize() {\n  grmhd::AnalyticData::PolarMagnetizedFmDisk disk(\n      grmhd::AnalyticData::MagnetizedFmDisk(1.3, 0.345, 6.123, 14.2, 0.065,\n                                            1.654, 0.0, 0.42, 85.0, 4),\n      grmhd::AnalyticData::SphericalTorus(2.5, 3.6, 0.9, 0.7, 0.0));\n  test_serialization(disk);\n}\n\nstruct Wrapper {\n  template <typename Tags>\n  auto variables(const tnsr::I<double, 3>& x, const double /*t*/,\n                 Tags tags) const {\n    return disk_->variables(x, tags);\n  }\n  const grmhd::AnalyticData::PolarMagnetizedFmDisk* disk_;\n};\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double bh_mass = 1.12;\n  const double bh_dimless_spin = 0.9375;\n  const double inner_edge_radius = 6.2;\n  const double max_pressure_radius = 11.6;\n  const double polytropic_constant = 0.034;\n  const double polytropic_exponent = 1.65;\n  const double noise = 0.0;\n  const double threshold_density = 0.14;\n  const double inverse_plasma_beta = 0.023;\n  const size_t b_field_normalization = 51;\n\n  const grmhd::AnalyticData::PolarMagnetizedFmDisk disk(\n      grmhd::AnalyticData::MagnetizedFmDisk(\n          bh_mass, bh_dimless_spin, inner_edge_radius, max_pressure_radius,\n          polytropic_constant, polytropic_exponent, noise, threshold_density,\n          inverse_plasma_beta, b_field_normalization),\n      grmhd::AnalyticData::SphericalTorus(3.0, 20.0, 1.0, 0.3, 0.0));\n\n  const auto coords = make_with_value<tnsr::I<DataType, 3>>(used_for_size, 0.5);\n\n  TestHelpers::AnalyticData::test_tag_retrieval(\n      disk, coords,\n      typename grmhd::AnalyticData::PolarMagnetizedFmDisk::tags<DataType>{});\n\n  if constexpr (std::is_same_v<DataType, double>) {\n    TestHelpers::VerifyGrSolution::verify_consistency(Wrapper{&disk}, 0.0,\n                                                      coords, 1.0e-2, 1.0e-10);\n  }\n}\n}  // namespace\n\n// [[TimeOut, 10]]\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.PolarMagFmDisk\",\n                  \"[Unit][PointwiseFunctions]\") {\n  test_create_from_options();\n  test_serialize();\n  test_move();\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_RiemannProblem.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <limits>\n#include <memory>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/RiemannProblem.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct RiemannProblemProxy : grmhd::AnalyticData::RiemannProblem {\n  using grmhd::AnalyticData::RiemannProblem::RiemannProblem;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return variables(x, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  register_classes_with_charm<grmhd::AnalyticData::RiemannProblem>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::RiemannProblem>(\n          \"RiemannProblem:\\n\"\n          \"  AdiabaticIndex: 2.0\\n\"\n          \"  LeftDensity: 1.0\\n\"\n          \"  LeftPressure: 1.0\\n\"\n          \"  LeftVelocity: [0.0, 0.0, 0.0]\\n\"\n          \"  LeftMagneticField: [0.5, 1.0, 0.0]\\n\"\n          \"  RightDensity: 0.125\\n\"\n          \"  RightPressure: 0.1\\n\"\n          \"  RightVelocity: [0.0, 0.0, 0.0]\\n\"\n          \"  RightMagneticField: [0.5, -1.0, 0.0]\\n\"\n          \"  Lapse: 2.0\\n\"\n          \"  ShiftX: 0.4\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& riemann_problem =\n      dynamic_cast<const grmhd::AnalyticData::RiemannProblem&>(\n          *deserialized_option_solution);\n\n  CHECK(riemann_problem ==\n        grmhd::AnalyticData::RiemannProblem(\n            2.0, 1.0, 0.125, 1.0, 0.1, std::array{0.0, 0.0, 0.0},\n            std::array{0.0, 0.0, 0.0}, std::array{0.5, 1.0, 0.0},\n            std::array{0.5, -1.0, 0.0}, 2.0, 0.4));\n}\n\nvoid test_move() {\n  grmhd::AnalyticData::RiemannProblem riemann_problem(\n      2.0, 1.0, 0.125, 1.0, 0.1, std::array{0.0, 0.0, 0.0},\n      std::array{0.0, 0.0, 0.0}, std::array{0.5, 1.0, 0.0},\n      std::array{0.5, -1.0, 0.0}, 2.0, 0.4);\n  grmhd::AnalyticData::RiemannProblem riemann_problem_copy(\n      2.0, 1.0, 0.125, 1.0, 0.1, std::array{0.0, 0.0, 0.0},\n      std::array{0.0, 0.0, 0.0}, std::array{0.5, 1.0, 0.0},\n      std::array{0.5, -1.0, 0.0}, 2.0, 0.4);\n  test_move_semantics(std::move(riemann_problem),\n                      riemann_problem_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  grmhd::AnalyticData::RiemannProblem riemann_problem(\n      2.0, 1.0, 0.125, 1.0, 0.1, std::array{0.0, 0.0, 0.0},\n      std::array{0.0, 0.0, 0.0}, std::array{0.5, 1.0, 0.0},\n      std::array{0.5, -1.0, 0.0}, 2.0, 0.4);\n  test_serialization(riemann_problem);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double adiabatic_index = 2.0;\n  const double left_density = 1.0;\n  const double right_density = 0.125;\n  const double left_pressure = 1.0;\n  const double right_pressure = 0.1;\n  const std::array left_velocity{0.0, 0.0, 0.0};\n  const std::array right_velocity{0.0, 0.0, 0.0};\n  const std::array left_magnetic_field{0.5, 1.0, 0.0};\n  const std::array right_magnetic_field{0.5, -1.0, 0.0};\n  const double lapse = 2.0;\n  const double shift = 0.4;\n\n  const auto member_variables = std::make_tuple(\n      adiabatic_index, left_density, right_density, left_pressure,\n      right_pressure, left_velocity, right_velocity, left_magnetic_field,\n      right_magnetic_field, lapse, shift);\n\n  RiemannProblemProxy riemann_problem(\n      adiabatic_index, left_density, right_density, left_pressure,\n      right_pressure, left_velocity, right_velocity, left_magnetic_field,\n      right_magnetic_field, lapse, shift);\n\n  pypp::check_with_random_values<1>(\n      &RiemannProblemProxy::hydro_variables<DataType>, riemann_problem,\n      \"RiemannProblem\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"compute_pressure\", \"lorentz_factor\", \"specific_enthalpy\"},\n      {{{-1.0, 1.0}}}, member_variables, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &RiemannProblemProxy::grmhd_variables<DataType>, riemann_problem,\n      \"RiemannProblem\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"compute_pressure\", \"lorentz_factor\", \"specific_enthalpy\",\n       \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{-1.0, 1.0}}}, member_variables, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.RiemannProblem\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_SlabJet.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SlabJet.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nstruct SlabJetProxy : public ::grmhd::AnalyticData::SlabJet {\n  using grmhd::AnalyticData::SlabJet::SlabJet;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>,\n                 hydro::Tags::MagneticField<DataType, 3>,\n                 hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x) const {\n    return this->variables(x, variables_tags<DataType>{});\n  }\n};\n\ntemplate <typename DataType>\nvoid test(const DataType& used_for_size) {\n  const double adiabatic_index = 1.33333333333333;\n  const double ambient_density = 10.;\n  const double ambient_pressure = 0.01;\n  const double ambient_electron_fraction = 0.;\n  const double jet_density = 0.1;\n  const double jet_pressure = 0.01;\n  const double jet_electron_fraction = 0.;\n  const std::array<double, 3> jet_velocity{{0.9987523388778445, 0., 0.}};\n  const double inlet_radius = 1.;\n  const std::array<double, 3> magnetic_field{{1., 0., 0.}};\n  const auto members = std::make_tuple(\n      adiabatic_index, ambient_density, ambient_pressure,\n      ambient_electron_fraction, jet_density, jet_pressure,\n      jet_electron_fraction, jet_velocity, inlet_radius, magnetic_field);\n\n  register_classes_with_charm<grmhd::AnalyticData::SlabJet>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::AnalyticData::SlabJet>(\n          \"SlabJet:\\n\"\n          \"  AdiabaticIndex: 1.33333333333333\\n\"\n          \"  AmbientDensity: 10.\\n\"\n          \"  AmbientPressure: 0.01\\n\"\n          \"  AmbientElectronFraction: 0.\\n\"\n          \"  JetDensity: 0.1\\n\"\n          \"  JetPressure: 0.01\\n\"\n          \"  JetElectronFraction: 0.\\n\"\n          \"  JetVelocity: [0.9987523388778445, 0., 0.]\\n\"\n          \"  InletRadius: 1.\\n\"\n          \"  MagneticField: [1., 0., 0.]\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& slab_jet = dynamic_cast<const grmhd::AnalyticData::SlabJet&>(\n      *deserialized_option_solution);\n\n  CHECK(slab_jet == grmhd::AnalyticData::SlabJet(\n                        adiabatic_index, ambient_density, ambient_pressure,\n                        ambient_electron_fraction, jet_density, jet_pressure,\n                        jet_electron_fraction, jet_velocity, inlet_radius,\n                        magnetic_field));\n\n  {\n    INFO(\"Semantics\");\n    auto to_move = slab_jet;\n    test_move_semantics(std::move(to_move), slab_jet);\n  }\n\n  const SlabJetProxy proxy{adiabatic_index,       ambient_density,\n                           ambient_pressure,      ambient_electron_fraction,\n                           jet_density,           jet_pressure,\n                           jet_electron_fraction, jet_velocity,\n                           inlet_radius,          magnetic_field};\n  pypp::check_with_random_values<1>(\n      &SlabJetProxy::template primitive_variables<DataType>, proxy, \"SlabJet\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\", \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{0.0, 1.0}}}, members, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.SlabJet\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/GrMhd\"};\n\n  test(std::numeric_limits<double>::signaling_NaN());\n  test(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/Test_SphericalTorus.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/Domain/CoordinateMaps/TestMapHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/GrMhd/SphericalTorus.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\nstd::array<double, 3> t2a(const tnsr::I<double, 3>& tens) {\n  std::array<double, 3> arr{};\n  for (size_t i = 0; i < 3; i++) {\n    gsl::at(arr,i) = tens.get(i);\n  }\n  return arr;\n}\n\ntnsr::I<double, 3> a2t(const std::array<double, 3>& arr) {\n  tnsr::I<double, 3> tens;\n  for (size_t i = 0; i < 3; i++) {\n    tens.get(i) = gsl::at(arr,i);\n  }\n  return tens;\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.GrMhd.SphericalTorus\",\n                  \"[Unit][PointwiseFunctions]\") {\n  // check parse errors first\n  CHECK_THROWS_WITH(\n      ([]() {\n        grmhd::AnalyticData::SphericalTorus(-1.0, 2.0, 0.1, 0.5, 0.0);\n      })(),\n      Catch::Matchers::ContainsSubstring(\"Minimum radius must be positive.\"));\n  CHECK_THROWS_WITH(([]() {\n                      grmhd::AnalyticData::SphericalTorus(3.0, 2.0, 0.1, 0.5,\n                                                          0.0);\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Maximum radius must be greater than minimum radius.\"));\n  CHECK_THROWS_WITH(([]() {\n                      grmhd::AnalyticData::SphericalTorus(1.0, 2.0, 10.0, 0.5,\n                                                          0.0);\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Minimum polar angle should be less than pi/2.\"));\n  CHECK_THROWS_WITH(([]() {\n                      grmhd::AnalyticData::SphericalTorus(1.0, 2.0, -0.1, 0.5,\n                                                          0.0);\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Minimum polar angle should be positive\"));\n  CHECK_THROWS_WITH(([]() {\n                      grmhd::AnalyticData::SphericalTorus(1.0, 2.0, 0.1, -0.5,\n                                                          0.0);\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Fraction of torus included must be positive.\"));\n  CHECK_THROWS_WITH(([]() {\n                      grmhd::AnalyticData::SphericalTorus(1.0, 2.0, 0.1, 2.0,\n                                                          0.0);\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Fraction of torus included must be at most 1.\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      grmhd::AnalyticData::SphericalTorus(1.0, 2.0, 0.1, 1.0,\n                                                          -0.3);\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Compression level must be non-negative.\"));\n\n  CHECK_THROWS_WITH(([]() {\n                      grmhd::AnalyticData::SphericalTorus(1.0, 2.0, 0.1, 1.0,\n                                                          1.0);\n                    })(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Compression level must be less than 1.\"));\n\n  // check constructor\n  CHECK(grmhd::AnalyticData::SphericalTorus(std::array<double, 2>{1.0, 2.0},\n                                            0.1, 0.5, 0.0) ==\n        grmhd::AnalyticData::SphericalTorus(1.0, 2.0, 0.1, 0.5, 0.0));\n\n  MAKE_GENERATOR(gen);\n  const double r_min = std::uniform_real_distribution<>(1.0, 2.0)(gen);\n  const double r_max = std::uniform_real_distribution<>(3.0, 4.0)(gen);\n  const double phi_max = std::uniform_real_distribution<>(0.5, 1.0)(gen);\n  const double fraction_of_torus =\n      std::uniform_real_distribution<>(0.1, 1.0)(gen);\n  const double compression_level =\n      std::uniform_real_distribution<>(0.1, 0.9)(gen);\n  const grmhd::AnalyticData::SphericalTorus full_torus(r_min, r_max, phi_max);\n  const grmhd::AnalyticData::SphericalTorus partial_torus(\n      r_min, r_max, phi_max, fraction_of_torus, compression_level);\n\n  {\n    const double x = std::uniform_real_distribution<>(-1.0, 1.0)(gen);\n    const double y = std::uniform_real_distribution<>(-1.0, 1.0)(gen);\n    const tnsr::I<double, 3> test_pt1{{{x, y, -1.0}}};\n    const tnsr::I<double, 3> test_pt2{{{x, y, 1.0}}};\n    CHECK_ITERABLE_APPROX(full_torus(test_pt1), full_torus(test_pt2));\n  }\n\n  {\n    const double x = std::uniform_real_distribution<>(-1.0, 1.0)(gen);\n    const double y1 = std::uniform_real_distribution<>(-1.0, 1.0)(gen);\n    const double y2 = std::uniform_real_distribution<>(-1.0, 1.0)(gen);\n    const double z1 = std::uniform_real_distribution<>(-1.0, 1.0)(gen);\n    const double z2 = std::uniform_real_distribution<>(-1.0, 1.0)(gen);\n\n    tnsr::I<double, 3> test_coords1{{{x, y1, z1}}};\n    tnsr::I<double, 3> test_coords2{{{x, y2, z2}}};\n    test_coords1 = partial_torus(test_coords1);\n    test_coords2 = partial_torus(test_coords2);\n\n    CHECK(magnitude(t2a(test_coords1)) == approx(magnitude(t2a(test_coords2))));\n  }\n\n  CHECK(full_torus == full_torus);\n  CHECK_FALSE(full_torus != full_torus);\n  CHECK(partial_torus == partial_torus);\n  CHECK_FALSE(partial_torus != partial_torus);\n  CHECK_FALSE(full_torus == partial_torus);\n  CHECK(full_torus != partial_torus);\n\n  CHECK(full_torus !=\n        grmhd::AnalyticData::SphericalTorus(r_min + 0.1, r_max, phi_max));\n  CHECK(full_torus !=\n        grmhd::AnalyticData::SphericalTorus(r_min, r_max + 0.1, phi_max));\n  CHECK(full_torus !=\n        grmhd::AnalyticData::SphericalTorus(r_min, r_max, phi_max + 0.1));\n\n  CHECK(not full_torus.is_identity());\n  CHECK(not partial_torus.is_identity());\n\n  {\n    auto deriv_approx = Approx::custom().epsilon(1.0e-6).scale(1.0);\n    std::uniform_real_distribution<> dist(-1.0, 1.0);\n    const auto test_point = make_with_random_values<std::array<double, 3>>(\n        make_not_null(&gen), make_not_null(&dist), double{});\n    const auto test_tnsr = a2t(test_point);\n    const tnsr::I<double, 3> mapped_tnsr = partial_torus(test_tnsr);\n    const auto mapped_point = t2a(mapped_tnsr);\n    CAPTURE(test_point);\n    const tnsr::I<double, 3> iden_tnsr = partial_torus.inverse(mapped_tnsr);\n\n    const auto jac = partial_torus.jacobian(test_tnsr);\n    const auto jac_inv = partial_torus.inv_jacobian(test_tnsr);\n    CAPTURE(jac_inv);\n\n    const auto analytic = partial_torus.hessian(test_tnsr);\n    const auto analytic_inverse =\n        partial_torus.derivative_of_inv_jacobian(test_tnsr);\n    for (size_t i = 0; i < 3; ++i) {\n      CAPTURE(i);\n      CHECK(test_tnsr.get(i) == deriv_approx(iden_tnsr.get(i)));\n      for (size_t j = 0; j < 3; ++j) {\n        CAPTURE(j);\n        const auto numerical_jacobian = numerical_derivative(\n            [&i, &partial_torus](const std::array<double, 3>& x) {\n              const auto x_tnsr = a2t(x);\n              return partial_torus(x_tnsr).get(i);\n            },\n            test_point, j, 1e-3);\n\n        const auto numerical_inverse_jacobian = numerical_derivative(\n            [&i, &partial_torus](const std::array<double, 3>& mapped_x) {\n              const auto mapped_x_tnsr = a2t(mapped_x);\n              return partial_torus.inverse(mapped_x_tnsr).get(i);\n            },\n            mapped_point, j, 1e-3);\n\n        CHECK(jac.get(i, j) == deriv_approx(numerical_jacobian));\n        CHECK(jac_inv.get(i, j) == deriv_approx(numerical_inverse_jacobian));\n        for (size_t k = 0; k < 3; ++k) {\n          CAPTURE(k);\n          const auto numerical_hessian = numerical_derivative(\n              [&i, &j, &partial_torus](const std::array<double, 3>& y) {\n                const auto y_tnsr = a2t(y);\n                return std::array{partial_torus.jacobian(y_tnsr).get(i, j),\n                                  partial_torus.inv_jacobian(y_tnsr).get(i, j)};\n              },\n              test_point, k, 1e-3);\n          CHECK(analytic.get(i, j, k) == deriv_approx(numerical_hessian[0]));\n          CHECK(analytic_inverse.get(i, j, k) ==\n                deriv_approx(numerical_hessian[1]));\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/GrMhd/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/NewtonianEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_NewtonianEulerAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_KhInstability.cpp\n  Test_ShuOsherTube.cpp\n  Test_SodExplosion.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  NewtonianEulerAnalyticData\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/NewtonianEuler/KhInstability.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef mass_density(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n):\n    return (\n        strip_density\n        if np.absolute(x[x.size - 1] - strip_bimedian_height)\n        < 0.5 * strip_thickness\n        else background_density\n    )\n\n\ndef velocity(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n):\n    dim = x.size\n    result = np.zeros(dim)\n    result[0] = (\n        strip_velocity\n        if (\n            np.absolute(x[x.size - 1] - strip_bimedian_height)\n            < 0.5 * strip_thickness\n        )\n        else background_velocity\n    )\n    strip_lower_bound = strip_bimedian_height - 0.5 * strip_thickness\n    strip_upper_bound = strip_bimedian_height + 0.5 * strip_thickness\n    result[dim - 1] = np.exp(\n        -0.5 * ((x[dim - 1] - strip_lower_bound) / perturbation_width) ** 2\n    ) + np.exp(\n        -0.5 * ((x[dim - 1] - strip_upper_bound) / perturbation_width) ** 2\n    )\n    result[dim - 1] *= perturbation_amplitude * np.sin(4 * np.pi * x[0])\n    return result\n\n\ndef specific_internal_energy(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n):\n    return (\n        pressure / (adiabatic_index - 1.0) / strip_density\n        if np.absolute(x[x.size - 1] - strip_bimedian_height)\n        < 0.5 * strip_thickness\n        else pressure / (adiabatic_index - 1.0) / background_density\n    )\n\n\ndef pressure(\n    x,\n    adiabatic_index,\n    strip_bimedian_height,\n    strip_thickness,\n    strip_density,\n    strip_velocity,\n    background_density,\n    background_velocity,\n    pressure,\n    perturbation_amplitude,\n    perturbation_width,\n):\n    return pressure\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/NewtonianEuler/ShuOsherTube.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef mass_density(\n    x,\n    mass_density_l,\n    velocity_l,\n    pressure_l,\n    jump_position,\n    epsilon,\n    wave_length,\n    velocity_r,\n    pressure_r,\n    adiabatic_index,\n):\n    if x < jump_position:\n        return mass_density_l\n    return 1.0 + epsilon * np.sin(wave_length * x[0])\n\n\ndef velocity(\n    x,\n    mass_density_l,\n    velocity_l,\n    pressure_l,\n    jump_position,\n    epsilon,\n    wave_length,\n    velocity_r,\n    pressure_r,\n    adiabatic_index,\n):\n    return np.asarray([velocity_l if x < jump_position else velocity_r])\n\n\ndef pressure(\n    x,\n    mass_density_l,\n    velocity_l,\n    pressure_l,\n    jump_position,\n    epsilon,\n    wave_length,\n    velocity_r,\n    pressure_r,\n    adiabatic_index,\n):\n    return pressure_l if x < jump_position else pressure_r\n\n\ndef specific_internal_energy(\n    x,\n    mass_density_l,\n    velocity_l,\n    pressure_l,\n    jump_position,\n    epsilon,\n    wave_length,\n    velocity_r,\n    pressure_r,\n    adiabatic_index,\n):\n    return (\n        pressure(\n            x,\n            mass_density_l,\n            velocity_l,\n            pressure_l,\n            jump_position,\n            epsilon,\n            wave_length,\n            velocity_r,\n            pressure_r,\n            adiabatic_index,\n        )\n        / mass_density(\n            x,\n            mass_density_l,\n            velocity_l,\n            pressure_l,\n            jump_position,\n            epsilon,\n            wave_length,\n            velocity_r,\n            pressure_r,\n            adiabatic_index,\n        )\n        / (adiabatic_index - 1.0)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/NewtonianEuler/SodExplosion.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef mass_density(\n    coords,\n    initial_radius,\n    inner_mass_density,\n    inner_pressure,\n    outer_mass_density,\n    outer_pressure,\n):\n    radius = np.sqrt(np.dot(coords, coords))\n    return (\n        inner_mass_density if radius <= initial_radius else outer_mass_density\n    )\n\n\ndef pressure(\n    coords,\n    initial_radius,\n    inner_mass_density,\n    inner_pressure,\n    outer_mass_density,\n    outer_pressure,\n):\n    radius = np.sqrt(np.dot(coords, coords))\n    return inner_pressure if radius <= initial_radius else outer_pressure\n\n\ndef velocity(\n    coords,\n    initial_radius,\n    inner_mass_density,\n    inner_pressure,\n    outer_mass_density,\n    outer_pressure,\n):\n    return np.zeros([len(coords)])\n\n\ndef specific_internal_energy(\n    coords,\n    initial_radius,\n    inner_mass_density,\n    inner_pressure,\n    outer_mass_density,\n    outer_pressure,\n):\n    adiabatic_index = 1.4\n    return (\n        pressure(\n            coords,\n            initial_radius,\n            inner_mass_density,\n            inner_pressure,\n            outer_mass_density,\n            outer_pressure,\n        )\n        / mass_density(\n            coords,\n            initial_radius,\n            inner_mass_density,\n            inner_pressure,\n            outer_mass_density,\n            outer_pressure,\n        )\n        / (adiabatic_index - 1.0)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/NewtonianEuler/Test_KhInstability.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/KhInstability.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct KhInstabilityProxy : NewtonianEuler::AnalyticData::KhInstability<Dim> {\n  using NewtonianEuler::AnalyticData::KhInstability<Dim>::KhInstability;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim, Frame::Inertial>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, Dim, Frame::Inertial>& x) const {\n    return this->variables(x, variables_tags<DataType>{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nvoid test_analytic_data(const DataType& used_for_size) {\n  const double adiabatic_index = 1.43;\n  const double strip_bimedian_height = 0.5;\n  const double strip_thickness = 0.4;\n  const double strip_density = 2.1;\n  const double strip_velocity = 0.3;\n  const double background_density = 2.0;\n  const double background_velocity = -0.2;\n  const double pressure = 1.1;\n  const double perturbation_amplitude = 0.1;\n  const double perturbation_width = 0.01;\n  const auto members = std::make_tuple(\n      adiabatic_index, strip_bimedian_height, strip_thickness, strip_density,\n      strip_velocity, background_density, background_velocity, pressure,\n      perturbation_amplitude, perturbation_width);\n\n  KhInstabilityProxy<Dim> kh_inst(\n      adiabatic_index, strip_bimedian_height, strip_thickness, strip_density,\n      strip_velocity, background_density, background_velocity, pressure,\n      perturbation_amplitude, perturbation_width);\n  pypp::check_with_random_values<1>(\n      &KhInstabilityProxy<Dim>::template primitive_variables<DataType>, kh_inst,\n      \"KhInstability\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      {{{0.0, 1.0}}}, members, used_for_size);\n\n  const auto kh_inst_from_options = TestHelpers::test_creation<\n      NewtonianEuler::AnalyticData::KhInstability<Dim>>(\n      \"  AdiabaticIndex: 1.43\\n\"\n      \"  StripBimedianHeight: 0.5\\n\"\n      \"  StripThickness: 0.4\\n\"\n      \"  StripDensity: 2.1\\n\"\n      \"  StripVelocity: 0.3\\n\"\n      \"  BackgroundDensity: 2.0\\n\"\n      \"  BackgroundVelocity: -0.2\\n\"\n      \"  Pressure: 1.1\\n\"\n      \"  PerturbAmplitude: 0.1\\n\"\n      \"  PerturbWidth: 0.01\");\n  CHECK(kh_inst_from_options == kh_inst);\n\n  KhInstabilityProxy<Dim> kh_inst_to_move(\n      adiabatic_index, strip_bimedian_height, strip_thickness, strip_density,\n      strip_velocity, background_density, background_velocity, pressure,\n      perturbation_amplitude, perturbation_width);\n  test_move_semantics(std::move(kh_inst_to_move), kh_inst);  //  NOLINT\n\n  // run post-serialized state through checks with random numbers\n  pypp::check_with_random_values<1>(\n      &KhInstabilityProxy<Dim>::template primitive_variables<DataType>,\n      serialize_and_deserialize(kh_inst), \"KhInstability\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      {{{0.0, 1.0}}}, members, used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.NewtEuler.KhInstability\",\n    \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/NewtonianEuler\"};\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_analytic_data, (2, 3));\n\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<2>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: 0.4\\n\"\n                        \"StripDensity: -2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: 2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: 1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: 0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -2.1 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<3>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: 0.4\\n\"\n                        \"StripDensity: -2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: 2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: 1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: 0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -2.1 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<2>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: 0.4\\n\"\n                        \"StripDensity: 2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: -2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: 1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: 0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -2 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<3>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: 0.4\\n\"\n                        \"StripDensity: 2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: -2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: 1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: 0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -2 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<2>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: 0.4\\n\"\n                        \"StripDensity: 2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: 2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: -1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: 0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -1.1 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<3>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: 0.4\\n\"\n                        \"StripDensity: 2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: 2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: -1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: 0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -1.1 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<2>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: 0.4\\n\"\n                        \"StripDensity: 2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: 2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: 1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: -0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.01 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<3>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: 0.4\\n\"\n                        \"StripDensity: 2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: 2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: 1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: -0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.01 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<2>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: -0.4\\n\"\n                        \"StripDensity: 2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: 2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: 1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: 0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.4 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::AnalyticData::KhInstability<3>>(\n                        \"AdiabaticIndex: 1.43\\n\"\n                        \"StripBimedianHeight: 0.5\\n\"\n                        \"StripThickness: -0.4\\n\"\n                        \"StripDensity: 2.1\\n\"\n                        \"StripVelocity: 0.3\\n\"\n                        \"BackgroundDensity: 2.0\\n\"\n                        \"BackgroundVelocity: -0.2\\n\"\n                        \"Pressure: 1.1\\n\"\n                        \"PerturbAmplitude: 0.1\\n\"\n                        \"PerturbWidth: 0.01\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.4 is below the lower bound of 0\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/NewtonianEuler/Test_ShuOsherTube.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/ShuOsherTube.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct ShuOsherTubeProxy : NewtonianEuler::AnalyticData::ShuOsherTube {\n  using NewtonianEuler::AnalyticData::ShuOsherTube::ShuOsherTube;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 1, Frame::Inertial>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, 1, Frame::Inertial>& x) const {\n    return this->variables(x, variables_tags<DataType>{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nvoid test_analytic_data(const DataType& used_for_size) {\n  const double mass_density_l = 3.857143;\n  const double velocity_l = 2.629369;\n  const double pressure_l = 10.33333;\n  const double jump_position = -4.0;\n  const double epsilon = 0.2;\n  const double lambda = 5.0;\n  const double velocity_r = 0.0;\n  const double pressure_r = 1.0;\n  const double adiabatic_index = 1.4;\n  const auto members =\n      std::make_tuple(mass_density_l, velocity_l, pressure_l, jump_position,\n                      epsilon, lambda, velocity_r, pressure_r, adiabatic_index);\n\n  ShuOsherTubeProxy shu_osher(jump_position, mass_density_l, velocity_l,\n                              pressure_l, velocity_r, pressure_r, epsilon,\n                              lambda);\n  pypp::check_with_random_values<1>(\n      &ShuOsherTubeProxy::template primitive_variables<DataType>, shu_osher,\n      \"ShuOsherTube\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      {{{0.0, 1.0}}}, members, used_for_size);\n\n  const auto shu_osher_from_options =\n      TestHelpers::test_creation<NewtonianEuler::AnalyticData::ShuOsherTube>(\n          \"  JumpPosition: -4.0\\n\"\n          \"  LeftMassDensity: 3.857143\\n\"\n          \"  LeftVelocity: 2.629369\\n\"\n          \"  LeftPressure: 10.33333\\n\"\n          \"  RightVelocity: 0.0\\n\"\n          \"  RightPressure: 1.0\\n\"\n          \"  Epsilon: 0.2\\n\"\n          \"  Lambda: 5.0\\n\");\n  CHECK(shu_osher_from_options == shu_osher);\n\n  // run post-serialized state through checks with random numbers\n  pypp::check_with_random_values<1>(\n      &ShuOsherTubeProxy::template primitive_variables<DataType>,\n      serialize_and_deserialize(shu_osher), \"ShuOsherTube\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      {{{0.0, 1.0}}}, members, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.NewtEuler.ShuOsherTube\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/NewtonianEuler\"};\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_analytic_data, (1));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/NewtonianEuler/Test_SodExplosion.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/NewtonianEuler/SodExplosion.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct SodExplosionProxy : NewtonianEuler::AnalyticData::SodExplosion<Dim> {\n  using NewtonianEuler::AnalyticData::SodExplosion<Dim>::SodExplosion;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim, Frame::Inertial>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>>;\n\n  tuples::tagged_tuple_from_typelist<variables_tags<DataVector>>\n  primitive_variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x) const {\n    return this->variables(x, variables_tags<DataVector>{});\n  }\n};\n\ntemplate <size_t Dim>\nvoid test_analytic_data(const DataVector& used_for_size) {\n  const double initial_radius = 0.5;\n  const double inner_mass_density = 1.0;\n  const double inner_pressure = 1.0;\n  const double outer_mass_density = 0.125;\n  const double outer_pressure = 0.1;\n\n  const auto members =\n      std::make_tuple(initial_radius, inner_mass_density, inner_pressure,\n                      outer_mass_density, outer_pressure);\n\n  const SodExplosionProxy<Dim> sod_explosion(initial_radius, inner_mass_density,\n                                             inner_pressure, outer_mass_density,\n                                             outer_pressure);\n  pypp::check_with_random_values<1>(\n      &SodExplosionProxy<Dim>::primitive_variables, sod_explosion,\n      \"SodExplosion\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      {{{0.0, 1.0}}}, members, used_for_size);\n\n  const auto sod_explosion_from_options = TestHelpers::test_creation<\n      NewtonianEuler::AnalyticData::SodExplosion<Dim>>(\n      \"InitialRadius: 0.5\\n\"\n      \"InnerMassDensity: 1.0\\n\"\n      \"InnerPressure: 1.0\\n\"\n      \"OuterMassDensity: 0.125\\n\"\n      \"OuterPressure: 0.1\\n\");\n  CHECK(sod_explosion_from_options == sod_explosion);\n\n  // run post-serialized state through checks with random numbers\n  pypp::check_with_random_values<1>(\n      &SodExplosionProxy<Dim>::primitive_variables,\n      serialize_and_deserialize(sod_explosion), \"SodExplosion\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      {{{0.0, 1.0}}}, members, used_for_size);\n\n  CHECK_THROWS_WITH(\n      NewtonianEuler::AnalyticData::SodExplosion<Dim>(\n          initial_radius, inner_mass_density, inner_pressure,\n          outer_mass_density + 100.0, outer_pressure,\n          Options::Context{false, {}, 1, 1}),\n      Catch::Matchers::ContainsSubstring(\"The inner mass density (\"));\n  CHECK_THROWS_WITH(NewtonianEuler::AnalyticData::SodExplosion<Dim>(\n                        initial_radius, inner_mass_density, inner_pressure,\n                        outer_mass_density, outer_pressure + 100.0,\n                        Options::Context{false, {}, 1, 1}),\n                    Catch::Matchers::ContainsSubstring(\"The inner pressure (\"));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.NewtEuler.SodExplosion\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/NewtonianEuler\"};\n\n  DataVector used_for_size{5};\n  test_analytic_data<2>(used_for_size);\n  test_analytic_data<3>(used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_PuncturesAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_MultiplePunctures.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Utilities\n  Punctures\n  PuncturesAnalyticData\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Punctures/MultiplePunctures.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\nmasses = [1.0, 0.5]\ncenters = np.array([[1.0, 2.0, 3.0], [-1.0, 2.0, 3.0]])\ndimensionless_momenta = np.array([[0.0, 0.0, 0.0], [0.1, -0.2, 0.3]])\ndimensionless_spins = np.array([[0.0, 0.0, 0.0], [0.3, -0.2, 0.1]])\n\n\ndef _traceless_conformal_extrinsic_curvature(r, n, P, S):\n    n_dot_P = np.dot(n, P)\n    S_cross_n = np.cross(S, n)\n    A = np.zeros((3, 3))\n    for i in range(3):\n        for j in range(3):\n            A[i, j] = (\n                3.0\n                / 2.0\n                / r**2\n                * (\n                    P[i] * n[j]\n                    + P[j] * n[i]\n                    - (np.eye(3)[i, j] - n[i] * n[j]) * n_dot_P\n                    + 2.0 / r * (n[i] * S_cross_n[j] + n[j] * S_cross_n[i])\n                )\n            )\n    return A\n\n\ndef traceless_conformal_extrinsic_curvature(x):\n    A_sum = np.zeros((3, 3))\n    for i in range(len(masses)):\n        r = np.linalg.norm(x - centers[i])\n        n = (x - centers[i]) / r\n        A_sum += _traceless_conformal_extrinsic_curvature(\n            r=r,\n            n=n,\n            P=masses[i] * dimensionless_momenta[i],\n            S=masses[i] ** 2 * dimensionless_spins[i],\n        )\n    return A_sum\n\n\ndef alpha(x):\n    one_over_alpha = 0.0\n    for i in range(len(masses)):\n        r = np.linalg.norm(x - centers[i])\n        one_over_alpha += 0.5 * masses[i] / r\n    return 1.0 / one_over_alpha\n\n\ndef beta(x):\n    A = traceless_conformal_extrinsic_curvature(x)\n    return 1.0 / 8.0 * alpha(x) ** 7 * np.einsum(\"ij,ij\", A, A)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Punctures/Test_MultiplePunctures.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Punctures/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Punctures/MultiplePunctures.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Punctures::AnalyticData {\nnamespace {\n\nusing test_tags =\n    tmpl::list<Punctures::Tags::TracelessConformalExtrinsicCurvature,\n               Punctures::Tags::Alpha, Punctures::Tags::Beta>;\n\nstruct MultiplePuncturesProxy {\n  tuples::tagged_tuple_from_typelist<test_tags> test_variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x) const {\n    return multiple_punctures.variables(x, test_tags{});\n  }\n\n  const MultiplePunctures& multiple_punctures;\n};\n\nvoid test_data(const std::vector<Puncture>& punctures,\n               const std::string& options_string) {\n  const auto created =\n      TestHelpers::test_factory_creation<elliptic::analytic_data::Background,\n                                         MultiplePunctures>(options_string);\n  REQUIRE(dynamic_cast<const MultiplePunctures*>(created.get()) != nullptr);\n  const auto& derived = dynamic_cast<const MultiplePunctures&>(*created);\n  const auto multiple_punctures = serialize_and_deserialize(derived);\n  {\n    INFO(\"Properties\");\n    CHECK(multiple_punctures.punctures() == punctures);\n  }\n  {\n    const MultiplePuncturesProxy proxy{multiple_punctures};\n    pypp::check_with_random_values<1>(\n        &MultiplePuncturesProxy::test_variables, proxy, \"MultiplePunctures\",\n        {\"traceless_conformal_extrinsic_curvature\", \"alpha\", \"beta\"},\n        {{{-1., 1.}}}, std::make_tuple(), DataVector(5));\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.AnalyticData.Punctures.MultiplePunctures\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/Punctures\"};\n  test_data({{{1., 2., 3.}, 1., {0., 0., 0.}, {0., 0., 0.}},\n             {{-1., 2., 3.}, 0.5, {0.1, -0.2, 0.3}, {0.3, -0.2, 0.1}}},\n            \"MultiplePunctures:\\n\"\n            \"  Punctures:\\n\"\n            \"   - Position: [1., 2., 3.]\\n\"\n            \"     Mass: 1.\\n\"\n            \"     Momentum: [0, 0, 0]\\n\"\n            \"     Spin: [0, 0, 0]\\n\"\n            \"   - Position: [-1., 2., 3.]\\n\"\n            \"     Mass: 0.5\\n\"\n            \"     Momentum: [0.1, -0.2, 0.3]\\n\"\n            \"     Spin: [0.3, -0.2, 0.1]\\n\");\n}\n\n}  // namespace Punctures::AnalyticData\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/RadiationTransport/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(M1Grey)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_M1GreyAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_HomogeneousSphere.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  M1GreyAnalyticData\n  DataStructures\n  DataStructuresHelpers\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/HomogeneousSphere.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef m1_tildeE(\n    x, radius, emissivity_and_opacity, outer_opacity, boundary_roundess\n):\n    radii = np.linalg.norm(np.asarray(x))\n    normalized_radii = (radii - radius) / -boundary_roundess\n\n    energy_difference = 1.0 - 1.0e-12\n    energy_sum = 1.0 + 1.0e-12\n\n    e_tilde = (\n        energy_difference / np.pi * np.arctan(normalized_radii)\n        + 0.5 * energy_sum\n    )\n\n    return e_tilde\n\n\ndef m1_tildeS(\n    x, radius, emissivity_and_opacity, outer_opacity, boundary_roundess\n):\n    return np.asarray(x) * 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/Test_HomogeneousSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <memory>\n\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/HomogeneousSphere.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace RadiationTransport::M1Grey::AnalyticData {\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing HomogeneousSphere =\n    RadiationTransport::M1Grey::AnalyticData::HomogeneousSphere;\nusing neutrino_species = neutrinos::ElectronNeutrinos<0>;\n\nstruct HomogeneousSphereProxy : HomogeneousSphere {\n  using HomogeneousSphere::HomogeneousSphere;\n  using m1_variables_tags =\n      tmpl::list<RadiationTransport::M1Grey::Tags::TildeE<Frame::Inertial,\n                                                          neutrino_species>,\n                 RadiationTransport::M1Grey::Tags::TildeS<Frame::Inertial,\n                                                          neutrino_species>>;\n\n  tuples::tagged_tuple_from_typelist<m1_variables_tags> m1_variables(\n      const tnsr::I<DataVector, 3>& x) const {\n    return variables(x, m1_variables_tags{});\n  }\n};\n\nvoid test_variables(const DataVector& used_for_size, const double sphere_radius,\n                    const double emissivity_and_opacity,\n                    const double outer_opacity,\n                    const double boundary_roundness) {\n  // Compare cpp & python calls for homogeneous sphere M1 variables\n  pypp::check_with_random_values<1>(\n      &HomogeneousSphereProxy::m1_variables,\n      HomogeneousSphereProxy(sphere_radius, emissivity_and_opacity,\n                             outer_opacity, boundary_roundness),\n      \"HomogeneousSphere\", {\"m1_tildeE\", \"m1_tildeS\"}, {{{-15., 15.}}},\n      std::make_tuple(sphere_radius, emissivity_and_opacity, outer_opacity,\n                      boundary_roundness),\n      used_for_size);\n}\n\nvoid test_equality(double sphere_radius, double emissivity_and_opacity,\n                   double outer_opacity, double boundary_roundness) {\n  register_classes_with_charm<\n      RadiationTransport::M1Grey::AnalyticData::HomogeneousSphere>();\n\n  const HomogeneousSphere homogeneous_original{\n      sphere_radius, emissivity_and_opacity, outer_opacity, boundary_roundness};\n\n  const auto homogeneous_sphere =\n      serialize_and_deserialize(homogeneous_original);\n\n  // serialize/deserialize should still be the same\n  CHECK(homogeneous_sphere ==\n        HomogeneousSphere(sphere_radius, emissivity_and_opacity, outer_opacity,\n                          boundary_roundness));\n\n  CHECK(homogeneous_sphere !=\n        HomogeneousSphere(sphere_radius + 0.1, emissivity_and_opacity,\n                          outer_opacity, boundary_roundness));\n  CHECK(homogeneous_sphere !=\n        HomogeneousSphere(sphere_radius, emissivity_and_opacity + 0.1,\n                          outer_opacity, boundary_roundness));\n\n  CHECK(homogeneous_sphere !=\n        HomogeneousSphere(sphere_radius, emissivity_and_opacity,\n                          outer_opacity + 0.1, boundary_roundness));\n\n  CHECK(homogeneous_sphere !=\n        HomogeneousSphere(sphere_radius, emissivity_and_opacity, outer_opacity,\n                          boundary_roundness + 0.1));\n}\n\nvoid test_consistency(double sphere_radius, double emissivity_and_opacity,\n                      double outer_opacity, double boundary_roundness) {\n  register_classes_with_charm<\n      RadiationTransport::M1Grey::AnalyticData::HomogeneousSphere>();\n\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          RadiationTransport::M1Grey::AnalyticData::HomogeneousSphere>(\n          \"HomogeneousSphere:\\n\"\n          \"  Radius: \" +\n          std::to_string(sphere_radius) +\n          \"\\n\"\n          \"  EmissivityAndOpacity: \" +\n          std::to_string(emissivity_and_opacity) +\n          \"\\n\"\n          \"  OuterOpacity: \" +\n          std::to_string(outer_opacity) +\n          \"\\n\"\n          \"  BoundaryRoundness: \" +\n          std::to_string(boundary_roundness) + \"\\n\")\n          ->get_clone();\n\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n\n  const auto& homogeneous_sphere = dynamic_cast<\n      const RadiationTransport::M1Grey::AnalyticData::HomogeneousSphere&>(\n      *deserialized_option_solution);\n\n  // establish sample grid\n  const Mesh<3> mesh{\n      {{7, 7, 7}},\n      {{Spectral::Basis::Legendre, Spectral::Basis::Legendre,\n        Spectral::Basis::Legendre}},\n      {{Spectral::Quadrature::GaussLobatto, Spectral::Quadrature::GaussLobatto,\n        Spectral::Quadrature::GaussLobatto}}};\n  const auto log_coords = logical_coordinates(mesh);\n\n  // Coordinates where we check the data. Includes inside and outside sphere\n  tnsr::I<DataVector, 3> in_coords{mesh.number_of_grid_points(), 0.0};\n  for (size_t i = 0; i < 3; ++i) {\n    in_coords.get(i) = (log_coords.get(i) + 1.);\n  }\n\n  using NeutrinoSpecies = neutrinos::ElectronNeutrinos<1>;\n\n  const auto vars = homogeneous_sphere.variables(\n      in_coords,\n      tmpl::list<\n          RadiationTransport::M1Grey::Tags::TildeE<Frame::Inertial,\n                                                   NeutrinoSpecies>,\n          RadiationTransport::M1Grey::Tags::TildeS<Frame::Inertial,\n                                                   NeutrinoSpecies>,\n          RadiationTransport::M1Grey::Tags::GreyEmissivity<NeutrinoSpecies>,\n          RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<\n              NeutrinoSpecies>,\n          RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<\n              NeutrinoSpecies>,\n          hydro::Tags::LorentzFactor<DataVector>,\n          hydro::Tags::SpatialVelocity<DataVector, 3>>{});\n\n  // Access the variables\n  const auto& tilde_e =\n      get<RadiationTransport::M1Grey::Tags::TildeE<Frame::Inertial,\n                                                   NeutrinoSpecies>>(vars);\n\n  const auto& tilde_s =\n      get<RadiationTransport::M1Grey::Tags::TildeS<Frame::Inertial,\n                                                   NeutrinoSpecies>>(vars);\n\n  const auto& grey_emissivity =\n      get<RadiationTransport::M1Grey::Tags::GreyEmissivity<NeutrinoSpecies>>(\n          vars);\n\n  const auto& grey_absorption_opacity = get<\n      RadiationTransport::M1Grey::Tags::GreyAbsorptionOpacity<NeutrinoSpecies>>(\n      vars);\n\n  const auto& grey_scattering_opacity = get<\n      RadiationTransport::M1Grey::Tags::GreyScatteringOpacity<NeutrinoSpecies>>(\n      vars);\n\n  const auto& lorentz_factor =\n      get<hydro::Tags::LorentzFactor<DataVector>>(vars);\n\n  const auto& velocity = get<hydro::Tags::SpatialVelocity<DataVector, 3>>(vars);\n\n  // Check points are not uniform for tildeE\n  CHECK(min(get(tilde_e)) < max(get(tilde_e)));\n\n  // All components of momentum density should be zero for this problem\n  CHECK(get<0>(tilde_s) == 0.0);\n  CHECK(get<1>(tilde_s) == 0.0);\n  CHECK(get<2>(tilde_s) == 0.0);\n\n  // Grey emissivity in sphere should be greater than outer region\n  CHECK(min(get(grey_emissivity)) < max(get(grey_emissivity)));\n\n  // Absorption opacity in sphere should be greater than outer region\n  CHECK(min(get(grey_absorption_opacity)) < max(get(grey_absorption_opacity)));\n\n  // Scattering opacity should be zero\n  CHECK(get(grey_scattering_opacity) == 0.0);\n\n  // Lorentz factor should be 1\n  CHECK(get(lorentz_factor) == 1.0);\n\n  // Velocity components should be zero\n  CHECK(get<0>(velocity) == 0.0);\n  CHECK(get<1>(velocity) == 0.0);\n  CHECK(get<2>(velocity) == 0.0);\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.RadiationTransport.M1Grey.\"\n    \"HomogeneousSphere\",\n    \"[Unit][PointwiseFunctions]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/RadiationTransport/M1Grey/\"};\n\n  const double sphere_radius = 1.0;\n  const double emissivity_and_opacity = 1.0;\n  const double outer_opacity = 0.5;\n  const double boundary_roundness = 0.03;\n\n  test_equality(sphere_radius, emissivity_and_opacity, outer_opacity,\n                boundary_roundness);\n\n  test_consistency(sphere_radius, emissivity_and_opacity, outer_opacity,\n                   boundary_roundness);\n  test_variables(DataVector(5), sphere_radius, emissivity_and_opacity,\n                 outer_opacity, boundary_roundness);\n}\n\n}  // namespace\n}  // namespace RadiationTransport::M1Grey::AnalyticData\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ScalarTensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarTensorAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_KerrSphericalHarmonic.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ErrorHandling\n  GeneralRelativitySolutions\n  GhScalarTensorAnalyticData\n  ScalarTensorAnalyticData\n  Options\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\ntry:\n    from scipy.special import sph_harm_y\nexcept ImportError:\n    # SciPy < 1.15\n    from scipy.special import sph_harm\n\n    def sph_harm_y(n, m, theta, phi):\n        return sph_harm(m, n, phi, theta)\n\n\ndef pi(x, amplitude, radius, width, l, m):\n    radial = amplitude * np.exp(\n        -((np.linalg.norm(x) - radius) ** 2) / width**2\n    )\n    theta = np.arctan2(np.sqrt(x[0] ** 2 + x[1] ** 2), x[2])\n    phi = np.arctan2(x[1], x[0])\n    angular = sph_harm_y(l, m, theta, phi)\n    return radial * np.real(angular)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/ScalarTensor/Test_KerrSphericalHarmonic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticData/ScalarTensor/KerrSphericalHarmonic.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename WrappedGrSolution, typename ArgumentSolution, size_t Dim>\nvoid check_wrapped_gr_analytic_data_consistency(\n    const WrappedGrSolution& wrapped_solution,\n    const ArgumentSolution& argument_solution,\n    const tnsr::I<DataVector, Dim, Frame::Inertial>& x) {\n  using argument_solution_tags =\n      typename ArgumentSolution::template tags<DataVector>;\n  const auto wrapped_vars =\n      wrapped_solution.variables(x, argument_solution_tags{});\n  const auto argument_vars =\n      argument_solution.variables(x, argument_solution_tags{});\n  tmpl::for_each<argument_solution_tags>(\n      [&wrapped_vars, &argument_vars](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        CHECK(get<tag>(wrapped_vars) == get<tag>(argument_vars));\n      });\n}\n\nvoid test_create_from_options() {\n  INFO(\"Testing option creation\");\n  const auto option_parsed = TestHelpers::test_creation<\n      ScalarTensor::AnalyticData::KerrSphericalHarmonic>(\n      \"Mass: 1.0 \\n\"\n      \"Spin: [0.0, 0.0, 0.1] \\n\"\n      \"Amplitude: 1.2 \\n\"\n      \"Radius: 0.8 \\n\"\n      \"Width: 2.0 \\n\"\n      \"Mode: [10, 8]\");\n  const ScalarTensor::AnalyticData::KerrSphericalHarmonic constructed{\n      1.0, std::array<double, 3>{{0.0, 0.0, 0.1}}, 1.2, 0.8, 2.0, {10, 8}};\n  CHECK(option_parsed == constructed);\n  CHECK_FALSE(option_parsed != constructed);\n\n  INFO(\"Testing serialization\");\n  test_serialization(constructed);\n}\n\n// `check_with_random_values` does not allow us to forward the l and m arguments\n// of type size_t / int to python, so we generate the random numbers and call\n// the python function manually here\nvoid test_variables() {\n  INFO(\"Testing random values against python function\");\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> x_dist{5., 6.};\n  std::uniform_real_distribution<double> amplitude_dist{1., 10.};\n  std::uniform_real_distribution<double> radius_dist{1., 2.};\n  std::uniform_real_distribution<double> width_dist{1., 2.};\n  // size of the DataVector we are testing with\n  const size_t dv_size = 10;\n  for (size_t l = 0; l < 8; l++) {\n    for (int m = -l; m <= static_cast<int>(l); ++m) {\n      const auto x = make_with_random_values<tnsr::I<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&x_dist), DataVector(dv_size));\n      const double amplitude = amplitude_dist(gen);\n      const double radius = radius_dist(gen);\n      const double width = width_dist(gen);\n      const auto py_res = pypp::call<Scalar<DataVector>>(\n          \"KerrSphericalHarmonic\", \"pi\", x, amplitude, radius, width, l, m);\n\n      const ScalarTensor::AnalyticData::KerrSphericalHarmonic sh{\n          // Fixed Black Hole parameters\n          1.0,\n          std::array<double, 3>{{0.0, 0.0, 0.0}},\n          // Scalar wave parameters\n          amplitude,\n          radius,\n          width,\n          {l, m}};\n      using all_scalar_tags =\n          typename tmpl::list<CurvedScalarWave::Tags::Psi,\n                              CurvedScalarWave::Tags::Phi<3>,\n                              CurvedScalarWave::Tags::Pi>;\n      const auto sh_res = sh.variables(x, all_scalar_tags{});\n\n      CHECK_ITERABLE_APPROX(get<CurvedScalarWave::Tags::Pi>(sh_res), py_res);\n      CHECK(get<CurvedScalarWave::Tags::Phi<3>>(sh_res) ==\n            make_with_value<tnsr::i<DataVector, 3>>(dv_size, 0.));\n      CHECK(get<CurvedScalarWave::Tags::Psi>(sh_res) ==\n            make_with_value<Scalar<DataVector>>(dv_size, 0.));\n    }\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.ScalarTensor.KerrSphericalHarmonic\",\n    \"[Unit][PointwiseFunctions]\") {\n  test_create_from_options();\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/ScalarTensor\"};\n  test_variables();\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticData.GhScalarTensor.WrappedGr\",\n    \"[Unit][PointwiseFunctions]\") {\n  const tnsr::I<DataVector, 3, Frame::Inertial> coords{DataVector{3.0, 4.0}};\n\n  check_wrapped_gr_analytic_data_consistency(\n      gh::Solutions::WrappedGr<\n          ScalarTensor::AnalyticData::KerrSphericalHarmonic>{\n          // Black Hole parameters\n          1.0, std::array<double, 3>{{0.1, -0.2, 0.3}},\n          // Scalar wave parameters\n          2.0, 1.0, 1.0, std::pair<size_t, int>{1, 0}},\n      ScalarTensor::AnalyticData::KerrSphericalHarmonic{\n          // Black Hole parameters\n          1.0, std::array<double, 3>{{0.1, -0.2, 0.3}},\n          // Scalar wave parameters\n          2.0, 1.0, 1.0, std::pair<size_t, int>{1, 0}},\n      coords);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Test_AnalyticData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n\nnamespace {\nstruct AnalyticData : public MarkAsAnalyticData {};\nstruct Solution : public MarkAsAnalyticSolution {};\nstruct SolutionDependentAnalyticData : public MarkAsAnalyticData,\n                                       private Solution {};\n\nstatic_assert(is_analytic_data_v<AnalyticData>,\n              \"Failed testing is_analytic_data_v\");\nstatic_assert(is_analytic_data_v<SolutionDependentAnalyticData>,\n              \"Failed testing is_analytic_data_v\");\n\nstatic_assert(is_analytic_data<AnalyticData>::value,\n              \"Failed testing is_analytic_data\");\nstatic_assert(is_analytic_data<SolutionDependentAnalyticData>::value,\n              \"Failed testing is_analytic_data\");\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Xcts/Binary.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\nx_coords = [-5.0, 6.0]\ny_offset = 0.1\nz_offset = 0.2\nmasses = [1.1, 0.43]\nangular_velocity = 0.02\nradial_velocity = 0.01\nlinear_velocity = [0.1, 0.2, 0.3]\nfalloff_widths = [7.0, 8.0]\n\n\ndef conformal_metric_bbh_isotropic(x):\n    return np.identity(3)\n\n\ndef inv_conformal_metric_bbh_isotropic(x):\n    return np.identity(3)\n\n\ndef deriv_conformal_metric_bbh_isotropic(x):\n    return np.zeros((3, 3, 3))\n\n\ndef extrinsic_curvature_trace_bbh_isotropic(x):\n    return 0.0\n\n\ndef shift_background(x):\n    return (\n        np.array([-angular_velocity * x[1], angular_velocity * x[0], 0.0])\n        + radial_velocity * x\n        + np.array(linear_velocity)\n    )\n\n\ndef longitudinal_shift_background_bbh_isotropic(x):\n    return np.zeros((3, 3))\n\n\ndef conformal_factor_minus_one_bbh_isotropic(x):\n    r1 = np.sqrt(\n        (x[0] - x_coords[0]) ** 2\n        + (x[1] - y_offset) ** 2\n        + (x[2] - z_offset) ** 2\n    )\n    r2 = np.sqrt(\n        (x[0] - x_coords[1]) ** 2\n        + (x[1] - y_offset) ** 2\n        + (x[2] - z_offset) ** 2\n    )\n    return 0.5 * (\n        np.exp(-(r1**2) / falloff_widths[0] ** 2) * masses[0] / r1\n        + np.exp(-(r2**2) / falloff_widths[1] ** 2) * masses[1] / r2\n    )\n\n\ndef energy_density_bbh_isotropic(x):\n    return 0.0\n\n\ndef stress_trace_bbh_isotropic(x):\n    return 0.0\n\n\ndef momentum_density_bbh_isotropic(x):\n    return np.zeros(3)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_XctsAnalyticData\")\n\nset(LIBRARY_SOURCES\n  Test_Binary.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Utilities\n  Xcts\n  XctsAnalyticData\n  XctsSolutions\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/Xcts/Test_Binary.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/Xcts/Binary.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::AnalyticData {\nnamespace {\n\nusing test_tags = tmpl::list<\n    Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n    Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n    ::Tags::deriv<Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    gr::Tags::TraceExtrinsicCurvature<DataVector>,\n    Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n    Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,\n                                                            Frame::Inertial>,\n    Tags::ConformalFactorMinusOne<DataVector>,\n    gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>, 0>,\n    gr::Tags::Conformal<gr::Tags::StressTrace<DataVector>, 0>,\n    gr::Tags::Conformal<gr::Tags::MomentumDensity<DataVector, 3>, 0>>;\n\ntemplate <typename IsolatedObjectBase, typename IsolatedObjectClasses>\nstruct BinaryProxy {\n  tuples::tagged_tuple_from_typelist<test_tags> test_variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x) const {\n    return binary.variables(x, test_tags{});\n  }\n\n  const Binary<IsolatedObjectBase, IsolatedObjectClasses>& binary;\n};\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<\n            elliptic::analytic_data::Background,\n            tmpl::list<Binary<elliptic::analytic_data::AnalyticSolution,\n                              tmpl::list<Xcts::Solutions::Schwarzschild>>>>,\n        tmpl::pair<elliptic::analytic_data::AnalyticSolution,\n                   tmpl::list<Xcts::Solutions::Schwarzschild>>>;\n  };\n};\n\nvoid test_data(const std::array<double, 2>& x_coords,\n               const std::array<double, 2>& center_of_mass_offset,\n               const double angular_velocity, const double expansion,\n               const std::array<double, 3> linear_velocity,\n               const std::optional<std::array<double, 2>>& falloff_widths,\n               const std::array<double, 2>& masses,\n               const std::string& py_functions_suffix,\n               const std::string& options_string) {\n  using IsolatedObjectBase = elliptic::analytic_data::AnalyticSolution;\n  using IsolatedObjectClasses = tmpl::list<Xcts::Solutions::Schwarzschild>;\n  register_classes_with_charm<Xcts::Solutions::Schwarzschild>();\n  const auto created = TestHelpers::test_creation<\n      std::unique_ptr<elliptic::analytic_data::Background>, Metavariables>(\n      options_string);\n  REQUIRE(\n      dynamic_cast<const Binary<IsolatedObjectBase, IsolatedObjectClasses>*>(\n          created.get()) != nullptr);\n  const auto& derived =\n      dynamic_cast<const Binary<IsolatedObjectBase, IsolatedObjectClasses>&>(\n          *created);\n  const auto binary = serialize_and_deserialize(derived);\n  {\n    INFO(\"Properties\");\n    CHECK(binary.x_coords() == x_coords);\n    CHECK(binary.y_offset() == center_of_mass_offset[0]);\n    CHECK(binary.z_offset() == center_of_mass_offset[1]);\n    CHECK(binary.angular_velocity() == angular_velocity);\n    CHECK(binary.expansion() == expansion);\n    CHECK(binary.linear_velocity() == linear_velocity);\n    CHECK(binary.falloff_widths() == falloff_widths);\n    const auto& superposed_objects = binary.superposed_objects();\n    CHECK(dynamic_cast<const Xcts::Solutions::Schwarzschild&>(\n              *superposed_objects[0])\n              .mass() == masses[0]);\n    CHECK(dynamic_cast<const Xcts::Solutions::Schwarzschild&>(\n              *superposed_objects[1])\n              .mass() == masses[1]);\n  }\n  {\n    const BinaryProxy<IsolatedObjectBase, IsolatedObjectClasses> proxy{binary};\n    pypp::check_with_random_values<1>(\n        &BinaryProxy<IsolatedObjectBase, IsolatedObjectClasses>::test_variables,\n        proxy, \"Binary\",\n        {\"conformal_metric_\" + py_functions_suffix,\n         \"inv_conformal_metric_\" + py_functions_suffix,\n         \"deriv_conformal_metric_\" + py_functions_suffix,\n         \"extrinsic_curvature_trace_\" + py_functions_suffix, \"shift_background\",\n         \"longitudinal_shift_background_\" + py_functions_suffix,\n         \"conformal_factor_minus_one_\" + py_functions_suffix,\n         \"energy_density_\" + py_functions_suffix,\n         \"stress_trace_\" + py_functions_suffix,\n         \"momentum_density_\" + py_functions_suffix},\n        {{{x_coords[0] * 2, x_coords[1] * 2}}}, std::make_tuple(),\n        DataVector(5));\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticData.Xcts.Binary\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticData/Xcts\"};\n  test_data({{-5., 6.}}, {{0.1, 0.2}}, 0.02, 0.01, {{0.1, 0.2, 0.3}},\n            {{7., 8.}}, {{1.1, 0.43}}, \"bbh_isotropic\",\n            \"Binary:\\n\"\n            \"  XCoords: [-5., 6.]\\n\"\n            \"  CenterOfMassOffset: [0.1, 0.2]\\n\"\n            \"  ObjectLeft:\\n\"\n            \"    Schwarzschild:\\n\"\n            \"      Mass: 1.1\\n\"\n            \"      Coordinates: Isotropic\\n\"\n            \"  ObjectRight:\\n\"\n            \"    Schwarzschild:\\n\"\n            \"      Mass: 0.43\\n\"\n            \"      Coordinates: Isotropic\\n\"\n            \"  AngularVelocity: 0.02\\n\"\n            \"  Expansion: 0.01\\n\"\n            \"  LinearVelocity: [0.1, 0.2, 0.3]\\n\"\n            \"  FalloffWidths: [7., 8.]\");\n}\n\n}  // namespace Xcts::AnalyticData\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticData/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Burgers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_BurgersSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_Bump.cpp\n  Test_Linear.cpp\n  Test_Step.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  BurgersSolutions\n  Burgers\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Burgers/Test_Bump.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Burgers/CheckBurgersSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Bump.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Burgers.Bump\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const double half_width = 5.;\n  const double height = 3.;\n  const double center = -8.;\n  const Burgers::Solutions::Bump solution(half_width, height, center);\n  const DataVector positions{-11., -9., -8., -6., -4.};\n  check_burgers_solution(solution, positions, {-0.5, 0., 0.1, 0.5});\n\n  // Check that the initial profile is correct.\n  CHECK_ITERABLE_APPROX(\n      get(solution.u(tnsr::I<DataVector, 1>{{{positions}}}, 0.)),\n      height * (1. - square((positions - center) / half_width)));\n  CHECK_ITERABLE_APPROX(\n      get(serialize_and_deserialize(solution).u(\n          tnsr::I<DataVector, 1>{{{positions}}}, 0.)),\n      height * (1. - square((positions - center) / half_width)));\n\n  register_classes_with_charm<Burgers::Solutions::Bump>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          Burgers::Solutions::Bump>(\n          \"Bump:\\n\"\n          \"  HalfWidth: 5.\\n\"\n          \"  Height: 3.\\n\"\n          \"  Center: -8.\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution = dynamic_cast<const Burgers::Solutions::Bump&>(\n      *deserialized_option_solution);\n\n  const auto x = tnsr::I<DataVector, 1>{{{positions}}};\n  const double t = 0.0;\n  CHECK(created_solution.variables(x, t, tmpl::list<Burgers::Tags::U>{}) ==\n        solution.variables(x, t, tmpl::list<Burgers::Tags::U>{}));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Burgers/Test_Linear.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Burgers/CheckBurgersSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Linear.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Burgers.Linear\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const double shock_time = 1.5;\n  const Burgers::Solutions::Linear solution(shock_time);\n  const DataVector positions{-1., 0., 1., 2.5, 3.5};\n  check_burgers_solution(solution, positions, {-1., 0., 1., 2.5});\n\n  // Check that the initial profile is correct.\n  CHECK_ITERABLE_APPROX(\n      get(solution.u(tnsr::I<DataVector, 1>{{{positions}}}, 0.)),\n      positions / -shock_time);\n  CHECK_ITERABLE_APPROX(get(serialize_and_deserialize(solution).u(\n                            tnsr::I<DataVector, 1>{{{positions}}}, 0.)),\n                        positions / -shock_time);\n\n  register_classes_with_charm<Burgers::Solutions::Linear>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          Burgers::Solutions::Linear>(\n          \"Linear:\\n\"\n          \"  ShockTime: 1.5\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution =\n      dynamic_cast<const Burgers::Solutions::Linear&>(\n          *deserialized_option_solution);\n\n  const auto x = tnsr::I<DataVector, 1>{{{positions}}};\n  const double t = 0.0;\n  CHECK(created_solution.variables(x, t, tmpl::list<Burgers::Tags::U>{}) ==\n        solution.variables(x, t, tmpl::list<Burgers::Tags::U>{}));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Burgers/Test_Step.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/Burgers/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Burgers/CheckBurgersSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Burgers/Step.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Burgers.Step\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const double left_value = 2.3;\n  const double right_value = 1.2;\n  const double initial_position = -0.5;\n  const Burgers::Solutions::Step solution(left_value, right_value,\n                                          initial_position);\n  const DataVector positions{-1., 0., 1., 2.5, 3.5};\n\n  // Check that the initial profile is correct.\n  const DataVector expected_initial_profile =\n      right_value +\n      (left_value - right_value) *\n          step_function(DataVector(-positions + initial_position));\n  CHECK_ITERABLE_APPROX(\n      get(solution.u(tnsr::I<DataVector, 1>{{{positions}}}, 0.)),\n      expected_initial_profile);\n\n  // Check serialization\n  CHECK_ITERABLE_APPROX(get(serialize_and_deserialize(solution).u(\n                            tnsr::I<DataVector, 1>{{{positions}}}, 0.)),\n                        expected_initial_profile);\n\n  // Check consistency\n  // Note that we avoid checking at any times where x_i == x0 + speed * t,\n  // because at these times the shock is near a grid point and the jump in the\n  // solution breaks the tests of check_burgers_solution\n  check_burgers_solution(solution, positions, {0., 0.7, 1.2, 10.0});\n\n  register_classes_with_charm<Burgers::Solutions::Step>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          Burgers::Solutions::Step>(\n          \"Step:\\n\"\n          \"  LeftValue: 2.3\\n\"\n          \"  RightValue: 1.2\\n\"\n          \"  InitialPosition: -0.5\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution = dynamic_cast<const Burgers::Solutions::Step&>(\n      *deserialized_option_solution);\n\n  const auto x = tnsr::I<DataVector, 1>{{{positions}}};\n  const double t = 0.0;\n  CHECK(created_solution.variables(x, t, tmpl::list<Burgers::Tags::U>{}) ==\n        solution.variables(x, t, tmpl::list<Burgers::Tags::U>{}));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_AnalyticSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_AnalyticSolution.cpp\n  Test_Tags.cpp\n  )\n\nadd_subdirectory(Burgers)\nadd_subdirectory(Elasticity)\nadd_subdirectory(ForceFree)\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(GhGrMhd)\nadd_subdirectory(GhRelativisticEuler)\nadd_subdirectory(GrMhd)\nadd_subdirectory(Hydro)\nadd_subdirectory(NewtonianEuler)\nadd_subdirectory(Poisson)\nadd_subdirectory(Punctures)\nadd_subdirectory(RadiationTransport)\nadd_subdirectory(RelativisticEuler)\nadd_subdirectory(ScalarAdvection)\nadd_subdirectory(WaveEquation)\nadd_subdirectory(Xcts)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  AnalyticSolutions\n  DataStructures\n  DataStructuresHelpers\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Elasticity/BentBeam.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom Elasticity.ConstitutiveRelations.IsotropicHomogeneous import (\n    poisson_ratio,\n    youngs_modulus,\n)\n\n\ndef displacement(\n    x, length, height, bending_moment, bulk_modulus, shear_modulus\n):\n    local_youngs_modulus = youngs_modulus(bulk_modulus, shear_modulus)\n    local_poisson_ratio = poisson_ratio(bulk_modulus, shear_modulus)\n    prefactor = 12.0 * bending_moment / (local_youngs_modulus * height**3)\n    return np.array(\n        [\n            -prefactor * x[0] * x[1],\n            prefactor\n            / 2.0\n            * (x[0] ** 2 + local_poisson_ratio * x[1] ** 2 - length**2 / 4.0),\n        ]\n    )\n\n\ndef strain(x, length, height, bending_moment, bulk_modulus, shear_modulus):\n    local_youngs_modulus = youngs_modulus(bulk_modulus, shear_modulus)\n    local_poisson_ratio = poisson_ratio(bulk_modulus, shear_modulus)\n    prefactor = 12.0 * bending_moment / (local_youngs_modulus * height**3)\n    result = np.zeros((2, 2))\n    result[0, 0] = -prefactor * x[1]\n    result[1, 1] = prefactor * local_poisson_ratio * x[1]\n    return result\n\n\ndef minus_stress(\n    x, length, height, bending_moment, bulk_modulus, shear_modulus\n):\n    return -np.array([[12.0 * bending_moment / height**3 * x[1], 0], [0, 0]])\n\n\ndef potential_energy_density(\n    x, length, height, bending_moment, bulk_modulus, shear_modulus\n):\n    local_strain = strain(\n        x, length, height, bending_moment, bulk_modulus, shear_modulus\n    )\n    local_minus_stress = minus_stress(\n        x, length, height, bending_moment, bulk_modulus, shear_modulus\n    )\n    return 0.5 * np.einsum(\"ij,ij\", local_strain, local_minus_stress)\n\n\ndef source(x):\n    return np.zeros(x.shape)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Elasticity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ElasticitySolutions\")\n\nset(LIBRARY_SOURCES\n  Test_BentBeam.cpp\n  Test_HalfSpaceMirror.cpp\n  Test_Zero.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstitutiveRelations\n  CoordinateMaps\n  DataStructures\n  Domain\n  Elasticity\n  ElasticityPointwiseFunctions\n  ElasticitySolutions\n  LinearOperators\n  Spectral\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Elasticity/HalfSpaceMirror.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nimport scipy.integrate as integrate\nfrom Elasticity import PotentialEnergy\nfrom Elasticity.ConstitutiveRelations import IsotropicHomogeneous\nfrom scipy.special import jv\n\n\ndef beam_profile(k, beam_width):\n    return 1.0 / (2.0 * np.pi) * np.exp(-((k * beam_width / 2.0) ** 2))\n\n\ndef integrand_xi_w(k, w, z, lame_parameter, shear_modulus, beam_width):\n    return (\n        1.0\n        / (2.0 * shear_modulus)\n        * jv(1, k * w)\n        * np.exp(-k * z)\n        * (\n            1.0\n            - (lame_parameter + 2.0 * shear_modulus)\n            / (lame_parameter + shear_modulus)\n            + k * z\n        )\n        * beam_profile(k, beam_width)\n    )\n\n\ndef integrand_xi_z(k, w, z, lame_parameter, shear_modulus, beam_width):\n    return (\n        1.0\n        / (2.0 * shear_modulus)\n        * jv(0, k * w)\n        * np.exp(-k * z)\n        * (1.0 + shear_modulus / (lame_parameter + shear_modulus) + k * z)\n        * beam_profile(k, beam_width)\n    )\n\n\ndef integrand_theta(k, w, z, lame_parameter, shear_modulus, beam_width):\n    return (\n        1.0\n        / (2.0 * shear_modulus)\n        * k\n        * jv(0, k * w)\n        * np.exp(-k * z)\n        * (-2.0 * shear_modulus / (lame_parameter + shear_modulus))\n        * beam_profile(k, beam_width)\n    )\n\n\ndef integrand_strain_rz(k, w, z, lame_parameter, shear_modulus, beam_width):\n    return (\n        -1.0\n        / (2.0 * shear_modulus)\n        * k\n        * jv(1, k * w)\n        * (k * z)\n        * np.exp(-k * z)\n        * beam_profile(k, beam_width)\n    )\n\n\ndef integrand_strain_zz(k, w, z, lame_parameter, shear_modulus, beam_width):\n    return (\n        1.0\n        / (2.0 * shear_modulus)\n        * k\n        * jv(0, k * w)\n        * np.exp(-k * z)\n        * (-shear_modulus / (lame_parameter + shear_modulus) - k * z)\n        * beam_profile(k, beam_width)\n    )\n\n\ndef displacement(r, beam_width, bulk_modulus, shear_modulus):\n    local_lame_parameter = IsotropicHomogeneous.lame_parameter(\n        bulk_modulus, shear_modulus\n    )\n    x = r[0]\n    y = r[1]\n    z = r[2]\n    radius = np.sqrt(x**2 + y**2)\n    xi_z = integrate.quad(\n        lambda k: integrand_xi_z(\n            k, radius, z, local_lame_parameter, shear_modulus, beam_width\n        ),\n        0,\n        np.inf,\n        limit=100,\n        epsabs=1e-13,\n    )[0]\n    if radius <= 1e-13:\n        return np.asarray(0.0, 0.0, xi_z)\n    xi_w = integrate.quad(\n        lambda k: integrand_xi_w(\n            k, radius, z, local_lame_parameter, shear_modulus, beam_width\n        ),\n        0,\n        np.inf,\n        limit=100,\n        epsabs=1e-13,\n    )[0]\n\n    cos_phi = x / radius\n    sin_phi = y / radius\n    return np.asarray([xi_w * cos_phi, xi_w * sin_phi, xi_z])\n\n\ndef strain(r, beam_width, bulk_modulus, shear_modulus):\n    local_lame_parameter = IsotropicHomogeneous.lame_parameter(\n        bulk_modulus, shear_modulus\n    )\n\n    x = r[0]\n    y = r[1]\n    z = r[2]\n    radius = np.sqrt(x**2 + y**2)\n\n    trace_term = integrate.quad(\n        lambda k: integrand_theta(\n            k, radius, z, local_lame_parameter, shear_modulus, beam_width\n        ),\n        0,\n        np.inf,\n        limit=100,\n        epsabs=1e-13,\n    )[0]\n\n    strain_zz = integrate.quad(\n        lambda k: integrand_strain_zz(\n            k, radius, z, local_lame_parameter, shear_modulus, beam_width\n        ),\n        0,\n        np.inf,\n        limit=100,\n        epsabs=1e-13,\n    )[0]\n\n    if radius <= 1e-13:\n        radius = 1.0\n        strain_rr = 0.5 * (trace_term - strain_zz)\n        return np.asarray(\n            [[strain_rr, 0, 0], [0, strain_rr, 0], [0, 0, strain_zz]]\n        )\n    else:\n        strain_rz = integrate.quad(\n            lambda k: integrand_strain_rz(\n                k, radius, z, local_lame_parameter, shear_modulus, beam_width\n            ),\n            0,\n            np.inf,\n            limit=100,\n            epsabs=1e-13,\n        )[0]\n        xi_w = integrate.quad(\n            lambda k: integrand_xi_w(\n                k, radius, z, local_lame_parameter, shear_modulus, beam_width\n            ),\n            0,\n            np.inf,\n            limit=100,\n            epsabs=1e-13,\n        )[0]\n        strain_pp = xi_w / radius\n        strain_rr = trace_term - strain_pp - strain_zz\n        return np.asarray(\n            [\n                [\n                    strain_pp + (x / radius) ** 2 * (strain_rr - strain_pp),\n                    x * y / radius**2 * (strain_rr - strain_pp),\n                    x / radius * strain_rz,\n                ],\n                [\n                    x * y / radius**2 * (strain_rr - strain_pp),\n                    strain_pp + (y / radius) ** 2 * (strain_rr - strain_pp),\n                    y / radius * strain_rz,\n                ],\n                [x / radius * strain_rz, y / radius * strain_rz, strain_zz],\n            ]\n        )\n\n\ndef minus_stress(r, beam_width, bulk_modulus, shear_modulus):\n    local_strain = strain(r, beam_width, bulk_modulus, shear_modulus)\n    return -IsotropicHomogeneous.stress(\n        local_strain, r, bulk_modulus, shear_modulus\n    )\n\n\ndef potential_energy_density(r, beam_width, bulk_modulus, shear_modulus):\n    local_strain = strain(r, beam_width, bulk_modulus, shear_modulus)\n    return PotentialEnergy.potential_energy_density(\n        local_strain, r, bulk_modulus, shear_modulus\n    )\n\n\ndef source(r):\n    return np.zeros(r.shape)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Elasticity/Test_BentBeam.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Elasticity/Equations.hpp\"\n#include \"Elliptic/Systems/Elasticity/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/BentBeam.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"PointwiseFunctions/Elasticity/PotentialEnergy.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct BentBeamProxy : Elasticity::Solutions::BentBeam {\n  using Elasticity::Solutions::BentBeam::BentBeam;\n\n  using field_tags =\n      tmpl::list<Elasticity::Tags::Displacement<2>, Elasticity::Tags::Strain<2>,\n                 Elasticity::Tags::MinusStress<2>,\n                 Elasticity::Tags::PotentialEnergyDensity<2>>;\n  using source_tags =\n      tmpl::list<Tags::FixedSource<Elasticity::Tags::Displacement<2>>>;\n\n  tuples::tagged_tuple_from_typelist<field_tags> field_variables(\n      const tnsr::I<DataVector, 2>& x) const {\n    return Elasticity::Solutions::BentBeam::variables(x, field_tags{});\n  }\n\n  // check_with_random_values() does not allow for arguments to be static\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  tuples::tagged_tuple_from_typelist<source_tags> source_variables(\n      const tnsr::I<DataVector, 2>& x) const {\n    return Elasticity::Solutions::BentBeam::variables(x, source_tags{});\n  }\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Elasticity.BentBeam\",\n    \"[PointwiseFunctions][Unit][Elasticity]\") {\n  const Elasticity::Solutions::BentBeam check_solution{\n      5., 1., 0.5,\n      // Iron: E=100, nu=0.29\n      Elasticity::ConstitutiveRelations::IsotropicHomogeneous<2>{\n          79.36507936507935, 38.75968992248062}};\n  const auto created_solution =\n      TestHelpers::test_creation<Elasticity::Solutions::BentBeam>(\n          \"Length: 5.\\n\"\n          \"Height: 1.\\n\"\n          \"BendingMoment: 0.5\\n\"\n          \"Material:\\n\"\n          \"  BulkModulus: 79.36507936507935\\n\"\n          \"  ShearModulus: 38.75968992248062\\n\");\n  CHECK(created_solution == check_solution);\n  CHECK(created_solution !=\n        Elasticity::Solutions::BentBeam{\n            6., 1., 0.5, {79.36507936507935, 38.75968992248062}});\n  CHECK(created_solution !=\n        Elasticity::Solutions::BentBeam{\n            5., 2., 0.5, {79.36507936507935, 38.75968992248062}});\n  CHECK(created_solution !=\n        Elasticity::Solutions::BentBeam{\n            5., 1., 0.6, {79.36507936507935, 38.75968992248062}});\n  CHECK(created_solution !=\n        Elasticity::Solutions::BentBeam{5., 1., 0.5, {80., 38.75968992248062}});\n  test_serialization(check_solution);\n  test_copy_semantics(check_solution);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\"PointwiseFunctions\"};\n  const Elasticity::ConstitutiveRelations::IsotropicHomogeneous<2>\n      constitutive_relation{79.36507936507935, 38.75968992248062};\n  const BentBeamProxy solution{5., 1., 0.5, constitutive_relation};\n\n  tnsr::I<DataVector, 2> x{{{{1., 2.}, {2., 1.}}}};\n  const auto solution_vars =\n      variables_from_tagged_tuple(solution.field_variables(x));\n  Variables<typename BentBeamProxy::field_tags> expected_vars{2};\n  auto& expected_displacement =\n      get<Elasticity::Tags::Displacement<2>>(expected_vars);\n  get<0>(expected_displacement) = DataVector{-0.12, -0.12};\n  get<1>(expected_displacement) = DataVector{-0.1227, -0.0588};\n  auto& expected_strain = get<Elasticity::Tags::Strain<2>>(expected_vars);\n  get<0, 0>(expected_strain) = DataVector{-0.12, -0.06};\n  get<1, 0>(expected_strain) = DataVector{0., 0.};\n  get<1, 1>(expected_strain) = DataVector{0.0348, 0.0174};\n  auto& expected_stress = get<Elasticity::Tags::MinusStress<2>>(expected_vars);\n  get<0, 0>(expected_stress) = DataVector{-12., -6.};\n  get<1, 0>(expected_stress) = DataVector{0., 0.};\n  get<1, 1>(expected_stress) = DataVector{0., 0.};\n  auto& expected_potential_energy_density =\n      get<Elasticity::Tags::PotentialEnergyDensity<2>>(expected_vars);\n  get(expected_potential_energy_density) = DataVector{0.72, 0.18};\n  CHECK_VARIABLES_APPROX(solution_vars, expected_vars);\n  CHECK(solution.potential_energy() == approx(0.075));\n\n  pypp::check_with_random_values<1>(\n      &BentBeamProxy::field_variables, solution,\n      \"AnalyticSolutions.Elasticity.BentBeam\",\n      {\"displacement\", \"strain\", \"minus_stress\", \"potential_energy_density\"},\n      {{{-5., 5.}}},\n      std::make_tuple(5., 1., 0.5, 79.36507936507935, 38.75968992248062),\n      DataVector(5));\n  pypp::check_with_random_values<1>(&BentBeamProxy::source_variables, solution,\n                                    \"AnalyticSolutions.Elasticity.BentBeam\",\n                                    {\"source\"}, {{{-5., 5.}}},\n                                    std::make_tuple(), DataVector(5));\n\n  using AffineMap = domain::CoordinateMaps::Affine;\n  using AffineMap2D =\n      domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n  // Since potential_energy() in BentBeam returns the energy stored in the\n  // entire beam, the coord_map has to match its dimensions.\n  const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                              AffineMap2D>\n      coord_map{{{-1., 1., -5. / 2., 5. / 2.}, {-1., 1., -1. / 2., 1. / 2.}}};\n  // Since the solution is a polynomial of degree 2, it should numerically\n  // solve the system equations to machine precision on 3 grid points per\n  // dimension.\n  const Mesh<2> mesh{3, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto inertial_coords = coord_map(logical_coords);\n  {\n    INFO(\"Test elasticity system with bent beam\");\n    using system = Elasticity::FirstOrderSystem<2>;\n    // Verify that the solution numerically solves the system\n    std::vector<std::unique_ptr<\n        Elasticity::ConstitutiveRelations::ConstitutiveRelation<2>>>\n        constitutive_relations{};\n    constitutive_relations.push_back(constitutive_relation.get_clone());\n    const ElementId<2> element_id{0};\n    FirstOrderEllipticSolutionsTestHelpers::verify_solution<system>(\n        solution, mesh, coord_map,\n        std::numeric_limits<double>::epsilon() * 100.,\n        std::make_tuple(std::move(constitutive_relations), inertial_coords,\n                        element_id));\n  };\n\n  {\n    INFO(\"Test elastic potential energy of bent beam\");\n    // Verify that the energy density integrated over the whole beam is correct\n    const auto jacobian = coord_map.jacobian(logical_coords);\n    const auto strain = get<Elasticity::Tags::Strain<2>>(solution.variables(\n        inertial_coords, tmpl::list<Elasticity::Tags::Strain<2>>{}));\n    const auto pointwise_energy = Elasticity::potential_energy_density(\n        strain, inertial_coords, constitutive_relation);\n    const auto det_jacobian = get(determinant(jacobian));\n    double potential_energy =\n        definite_integral(det_jacobian * get(pointwise_energy), mesh);\n    CHECK(potential_energy == approx(solution.potential_energy()));\n  };\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Elasticity/Test_HalfSpaceMirror.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Elasticity/Equations.hpp\"\n#include \"Elliptic/Systems/Elasticity/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/HalfSpaceMirror.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct HalfSpaceMirrorProxy : Elasticity::Solutions::HalfSpaceMirror {\n  using Elasticity::Solutions::HalfSpaceMirror::HalfSpaceMirror;\n\n  using field_tags =\n      tmpl::list<Elasticity::Tags::Displacement<3>, Elasticity::Tags::Strain<3>,\n                 Elasticity::Tags::MinusStress<3>,\n                 Elasticity::Tags::PotentialEnergyDensity<3>>;\n  using source_tags =\n      tmpl::list<Tags::FixedSource<Elasticity::Tags::Displacement<3>>>;\n\n  tuples::tagged_tuple_from_typelist<field_tags> field_variables(\n      const tnsr::I<DataVector, 3>& x) const {\n    return Elasticity::Solutions::HalfSpaceMirror::variables(x, field_tags{});\n  }\n\n  // NOLINTNEXTLINE(readability-convert-member-functions-to-static)\n  tuples::tagged_tuple_from_typelist<source_tags> source_variables(\n      const tnsr::I<DataVector, 3>& x) const {\n    return Elasticity::Solutions::HalfSpaceMirror::variables(x, source_tags{});\n  }\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Elasticity.HalfSpaceMirror\",\n    \"[PointwiseFunctions][Unit][Elasticity]\") {\n  // Fused silica: E = 72, nu = 0.17\n  // => K = E / (3 * (1 - 2 nu)), mu = E / (2 * (1 + nu))\n  const Elasticity::ConstitutiveRelations::IsotropicHomogeneous<3>\n      constitutive_relation{36.36363636363637, 30.76923076923077};\n  const Elasticity::Solutions::HalfSpaceMirror check_solution{\n      0.177, constitutive_relation, 350, 1.e-12, 1.e-10};\n  const auto created_solution =\n      TestHelpers::test_creation<Elasticity::Solutions::HalfSpaceMirror>(\n          \"BeamWidth: 0.177\\n\"\n          \"Material:\\n\"\n          \"  BulkModulus: 36.36363636363637\\n\"\n          \"  ShearModulus: 30.76923076923077\\n\"\n          \"IntegrationIntervals: 350\\n\"\n          \"AbsoluteTolerance: 1e-12\\n\"\n          \"RelativeTolerance: 1e-10\\n\");\n  CHECK(created_solution == check_solution);\n  test_serialization(check_solution);\n  test_copy_semantics(check_solution);\n\n  const HalfSpaceMirrorProxy solution{0.177, constitutive_relation, 350, 1e-11,\n                                      1e-11};\n  {\n    INFO(\"Random-value tests\");\n    pypp::SetupLocalPythonEnvironment local_python_env{\"PointwiseFunctions\"};\n    // The accuracy of the random-value tests is limited by the numerical\n    // integrations in both the HalfSpaceMirror solution and the Python\n    // implementation\n    const double eps = 1.e-8;\n    pypp::check_with_random_values<1>(\n        &HalfSpaceMirrorProxy::field_variables, solution,\n        \"AnalyticSolutions.Elasticity.HalfSpaceMirror\",\n        {\"displacement\", \"strain\", \"minus_stress\", \"potential_energy_density\"},\n        {{{0., 3.}}},\n        std::make_tuple(0.177, 36.36363636363637, 30.76923076923077),\n        DataVector(5), eps);\n    pypp::check_with_random_values<1>(\n        &HalfSpaceMirrorProxy::source_variables, solution,\n        \"AnalyticSolutions.Elasticity.HalfSpaceMirror\", {\"source\"},\n        {{{0., 3.}}}, std::make_tuple(), DataVector(5), eps);\n  }\n  {\n    INFO(\n        \"Test that functions behave expectedly at the origin and vanish far \"\n        \"away from the source\");\n    const tnsr::I<DataVector, 3> x{\n        {{{0., 20., 0.}, {0., 0., 0.}, {0., 0., 20.}}}};\n    const auto solution_vars = variables_from_tagged_tuple(\n        solution.variables(x, tmpl::list<Elasticity::Tags::Displacement<3>,\n                                         Elasticity::Tags::Strain<3>>{}));\n    Variables<tmpl::list<Elasticity::Tags::Displacement<3>,\n                         Elasticity::Tags::Strain<3>>>\n        expected_vars{3};\n    auto& expected_displacement =\n        get<Elasticity::Tags::Displacement<3>>(expected_vars);\n    get<0>(expected_displacement) = DataVector{0., 0., 0.};\n    get<1>(expected_displacement) = DataVector{0., 0., 0.};\n    get<2>(expected_displacement) = DataVector{4.30e-02, 0., 0.};\n    auto& expected_strain = get<Elasticity::Tags::Strain<3>>(expected_vars);\n    get<0, 0>(expected_strain) = DataVector{-5.45e-02, 0., 0.};\n    get<1, 0>(expected_strain) = DataVector{0., 0., 0.};\n    get<2, 0>(expected_strain) = DataVector{0., 0., 0.};\n    get<1, 1>(expected_strain) = DataVector{-5.45e-02, 0., 0.};\n    get<2, 1>(expected_strain) = DataVector{0., 0., 0.};\n    get<2, 2>(expected_strain) = DataVector{-10.90e-02, 0., 0.};\n    Approx custom_approx = Approx::custom().margin(5e-4);\n    CHECK_VARIABLES_CUSTOM_APPROX(solution_vars, expected_vars, custom_approx);\n  };\n\n  // Disabled for now because partial derivatives are not implemented\n  if constexpr (false) {  // NOLINT\n    INFO(\"Test elasticity system with half-space mirror\");\n    // Verify that the solution numerically solves the system\n    using system = Elasticity::FirstOrderSystem<3>;\n    using AffineMap = domain::CoordinateMaps::Affine;\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap3D>\n        coord_map{{{-1., 1., 0., 0.5}, {-1., 1., 0., 0.5}, {-1., 1., 0., 0.5}}};\n    const Mesh<3> mesh{12, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const auto logical_coords = logical_coordinates(mesh);\n    const auto inertial_coords = coord_map(logical_coords);\n    std::vector<std::unique_ptr<\n        Elasticity::ConstitutiveRelations::ConstitutiveRelation<3>>>\n        constitutive_relations{};\n    constitutive_relations.push_back(constitutive_relation.get_clone());\n    const ElementId<3> element_id{0};\n    FirstOrderEllipticSolutionsTestHelpers::verify_solution<system>(\n        solution, mesh, coord_map, 0.05,\n        std::make_tuple(std::move(constitutive_relations), inertial_coords,\n                        element_id));\n  };\n\n  CHECK_THROWS_WITH(\n      ([&constitutive_relation]() {\n        const HalfSpaceMirrorProxy bad_solution{0.177, constitutive_relation, 5,\n                                                1e-15, 1e-15};\n        const tnsr::I<DataVector, 3> x{{{{0.3}, {1.3}, {2.3}}}};\n        variables_from_tagged_tuple(bad_solution.variables(\n            x, tmpl::list<Elasticity::Tags::Displacement<3>,\n                          Elasticity::Tags::Strain<3>>{}));\n      }()),\n      Catch::Matchers::ContainsSubstring(\"The numerical integral failed\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Elasticity/Test_Zero.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Elasticity/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Elasticity/Zero.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nauto make_coord_map() {\n  using AffineMap = domain::CoordinateMaps::Affine;\n  if constexpr (Dim == 1) {\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap>{{-1., 1., 0., M_PI}};\n  } else if constexpr (Dim == 2) {\n    using AffineMap2D =\n        domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap2D>{\n        {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}}};\n  } else {\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap3D>{\n        {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}}};\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_solution() {\n  const Elasticity::Solutions::Zero<Dim> solution{};\n  const auto created_solution =\n      TestHelpers::test_creation<Elasticity::Solutions::Zero<Dim>>(\"\");\n  CHECK(created_solution == solution);\n  test_serialization(solution);\n  test_copy_semantics(solution);\n\n  using system = Elasticity::FirstOrderSystem<Dim>;\n  auto constitutive_relation = std::make_unique<\n      Elasticity::ConstitutiveRelations::IsotropicHomogeneous<Dim>>(1., 1.);\n  std::vector<std::unique_ptr<\n      Elasticity::ConstitutiveRelations::ConstitutiveRelation<Dim>>>\n      constitutive_relations{};\n  constitutive_relations.push_back(std::move(constitutive_relation));\n  const Mesh<Dim> mesh{12, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const ElementId<Dim> element_id{0};\n  const auto coord_map = make_coord_map<Dim>();\n  FirstOrderEllipticSolutionsTestHelpers::verify_solution<system>(\n      solution, mesh, coord_map, 1.e-14,\n      std::make_tuple(std::move(constitutive_relations),\n                      coord_map(logical_coordinates(mesh)), element_id));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Elasticity.Zero\",\n                  \"[PointwiseFunctions][Unit]\") {\n  test_solution<2>();\n  test_solution<3>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Elasticity/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ForceFree/AlfvenWave.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef solution_wave_frame(coords_prime):\n    electric_field = coords_prime * 0.0\n    magnetic_field = coords_prime * 0.0\n\n    x = coords_prime[0]\n\n    magnetic_field[0] = 1.0\n    magnetic_field[1] = 1.0\n    magnetic_field[2] = np.where(\n        x > -0.1, 1.0 + 0.15 * (1.0 + np.sin(5 * np.pi * x)), 1.0\n    )\n    magnetic_field[2] = np.where(x > 0.1, 1.3, magnetic_field[2])\n\n    electric_field[0] = -magnetic_field[2]\n    electric_field[2] = 1.0\n\n    return (electric_field, magnetic_field)\n\n\ndef lorentz_transform_em_field(v, em_fields_tuple):\n    # transforms electric and magnetic field to their values in the frame\n    # moving with the speed (v, 0, 0) with respect to the original frame\n    electric_field, magnetic_field = em_fields_tuple\n\n    electric_field_prime = electric_field * 1.0\n    magnetic_field_prime = magnetic_field * 1.0\n\n    lorentz_factor = 1.0 / np.sqrt(1 - v**2)\n\n    electric_field_prime[1] = lorentz_factor * (\n        electric_field[1] - v * magnetic_field[2]\n    )\n    electric_field_prime[2] = lorentz_factor * (\n        electric_field[2] + v * magnetic_field[1]\n    )\n\n    magnetic_field_prime[1] = lorentz_factor * (\n        magnetic_field[1] + v * electric_field[2]\n    )\n    magnetic_field_prime[2] = lorentz_factor * (\n        magnetic_field[2] - v * electric_field[1]\n    )\n\n    return (electric_field_prime, magnetic_field_prime)\n\n\ndef TildeE(x, t, wave_speed):\n    lorentz_factor = 1.0 / np.sqrt(1 - wave_speed**2)\n    x0 = x * 1.0\n    x0[0] -= wave_speed * t\n\n    return lorentz_transform_em_field(\n        -wave_speed, solution_wave_frame(lorentz_factor * x0)\n    )[0]\n\n\ndef TildeB(x, t, wave_speed):\n    lorentz_factor = 1.0 / np.sqrt(1 - wave_speed**2)\n    x0 = x * 1.0\n    x0[0] -= wave_speed * t\n    return lorentz_transform_em_field(\n        -wave_speed, solution_wave_frame(lorentz_factor * x0)\n    )[1]\n\n\ndef TildePsi(x, t, wave_speed):\n    return x[0] * 0\n\n\ndef TildePhi(x, t, wave_speed):\n    return x[0] * 0\n\n\ndef solution_charge_wave_frame(x):\n    q = np.where(x < -0.1, 0.0, -0.75 * np.pi * np.cos(5.0 * np.pi * x))\n    q = np.where(x > 0.1, 0.0, q)\n    return q\n\n\ndef TildeQ(x, t, wave_speed):\n    lorentz_factor = 1.0 / np.sqrt(1 - wave_speed**2)\n    return lorentz_factor * solution_charge_wave_frame(\n        lorentz_factor * (x[0] - wave_speed * t)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ForceFree/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ForceFreeSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_AlfvenWave.cpp\n  Test_ExactWald.cpp\n  Test_FastWave.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ForceFreeSolutions\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ForceFree/ExactWald.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef TildeE(x, t, B0):\n    result = x * 0.0\n    r_squared = np.sum(x * x)\n\n    result[0] = -2.0 * B0 * x[1] / r_squared\n    result[1] = 2.0 * B0 * x[0] / r_squared\n\n    return result\n\n\ndef TildeB(x, t, B0):\n    result = x * 0.0\n    result[2] = B0\n\n    return result\n\n\ndef TildePsi(x, t, B0):\n    return x[0] * 0.0\n\n\ndef TildePhi(x, t, B0):\n    return x[0] * 0.0\n\n\ndef TildeQ(x, t, B0):\n    return x[0] * 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ForceFree/FastWave.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef initial_profile(x):\n    result = np.where(x > -0.1, -1.5 * x + 0.85, 1.0)\n    result = np.where(x > 0.1, 0.7, result)\n    return result\n\n\ndef TildeE(x, t):\n    electric_field = x * 0.0\n    electric_field[2] = -initial_profile(x[0] - t)\n    return electric_field\n\n\ndef TildeB(x, t):\n    magnetic_field = x * 0.0\n    magnetic_field[0] = 1.0\n    magnetic_field[1] = initial_profile(x[0] - t)\n    return magnetic_field\n\n\ndef TildePsi(x, t):\n    return x[0] * 0\n\n\ndef TildePhi(x, t):\n    return x[0] * 0\n\n\ndef TildeQ(x, t):\n    return x[0] * 0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ForceFree/Test_AlfvenWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/AlfvenWave.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing AlfvenWave = ForceFree::Solutions::AlfvenWave;\n\nstruct AlfvenWaveProxy : AlfvenWave {\n  using AlfvenWave::AlfvenWave;\n  using variables_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildePsi, ForceFree::Tags::TildePhi,\n                 ForceFree::Tags::TildeQ>;\n\n  tuples::tagged_tuple_from_typelist<variables_tags> return_variables(\n      const tnsr::I<DataVector, 3>& x, double t) const {\n    return this->variables(x, t, variables_tags{});\n  }\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.ForceFree.AlfvenWave\",\n    \"[Unit][PointwiseFunctions]\") {\n  // test creation\n  const auto solution =\n      TestHelpers::test_creation<AlfvenWave>(\"WaveSpeed: 0.5\");\n  CHECK(solution == AlfvenWave(0.5));\n  CHECK(solution != AlfvenWave(-0.5));\n\n  // test serialize\n  test_serialization(solution);\n\n  // test move\n  test_move_semantics(AlfvenWave{0.5}, AlfvenWave{0.5});\n\n  // test derived\n  register_classes_with_charm<AlfvenWave>();\n  const std::unique_ptr<InitialData> base_ptr =\n      std::make_unique<AlfvenWave>(0.3);\n  const std::unique_ptr<InitialData> deserialized_base_ptr =\n      serialize_and_deserialize(base_ptr)->get_clone();\n  CHECK(dynamic_cast<const AlfvenWave&>(*deserialized_base_ptr.get()) ==\n        dynamic_cast<const AlfvenWave&>(*base_ptr.get()));\n\n  // test solution for a random wave speed\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist_mu(-0.99, 0.99);\n  const double wave_speed{make_with_random_values<double>(\n      make_not_null(&gen), make_not_null(&dist_mu))};\n\n  AlfvenWaveProxy alfven_wave(wave_speed);\n  const auto member_variables = std::make_tuple(wave_speed);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/ForceFree\"};\n  const DataVector used_for_size{10};\n\n  pypp::check_with_random_values<1>(\n      &AlfvenWaveProxy::return_variables, alfven_wave, \"AlfvenWave\",\n      {\"TildeE\", \"TildeB\", \"TildePsi\", \"TildePhi\", \"TildeQ\"}, {{{-0.2, 0.2}}},\n      member_variables, used_for_size);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ForceFree/Test_ExactWald.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/ExactWald.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing ExactWald = ForceFree::Solutions::ExactWald;\n\nstruct ExactWaldProxy : ExactWald {\n  using ExactWald::ExactWald;\n  using variables_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildePsi, ForceFree::Tags::TildePhi,\n                 ForceFree::Tags::TildeQ>;\n\n  tuples::tagged_tuple_from_typelist<variables_tags> return_variables(\n      const tnsr::I<DataVector, 3>& x, double t) const {\n    return this->variables(x, t, variables_tags{});\n  }\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.ForceFree.ExactWald\",\n    \"[Unit][PointwiseFunctions]\") {\n  // test creation\n  const auto solution =\n      TestHelpers::test_creation<ExactWald>(\"MagneticFieldAmplitude: 1.0\");\n  CHECK(solution == ExactWald(1.0));\n  CHECK(solution != ExactWald(-1.0));\n\n  // test serialize\n  test_serialization(solution);\n\n  // test move\n  test_move_semantics(ExactWald{1.0}, ExactWald{1.0});\n\n  // test derived\n  register_classes_with_charm<ExactWald>();\n  const std::unique_ptr<InitialData> base_ptr =\n      std::make_unique<ExactWald>(0.5);\n  const std::unique_ptr<InitialData> deserialized_base_ptr =\n      serialize_and_deserialize(base_ptr)->get_clone();\n  CHECK(dynamic_cast<const ExactWald&>(*deserialized_base_ptr) ==\n        dynamic_cast<const ExactWald&>(*base_ptr));\n\n  // test solution for a random value of B0\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist_mu(-10.0, 10.0);\n  const double magnetic_field_amplitude{make_with_random_values<double>(\n      make_not_null(&gen), make_not_null(&dist_mu))};\n\n  ExactWaldProxy exact_wald(magnetic_field_amplitude);\n  const auto member_variables = std::make_tuple(magnetic_field_amplitude);\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/ForceFree\"};\n  const DataVector used_for_size{10};\n\n  pypp::check_with_random_values<1>(\n      &ExactWaldProxy::return_variables, exact_wald, \"ExactWald\",\n      {\"TildeE\", \"TildeB\", \"TildePsi\", \"TildePhi\", \"TildeQ\"}, {{{-2.0, 2.0}}},\n      member_variables, used_for_size);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ForceFree/Test_FastWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <utility>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ForceFree/FastWave.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing FastWave = ForceFree::Solutions::FastWave;\n\nstruct FastWaveProxy : FastWave {\n  using FastWave::FastWave;\n  using variables_tags =\n      tmpl::list<ForceFree::Tags::TildeE, ForceFree::Tags::TildeB,\n                 ForceFree::Tags::TildePsi, ForceFree::Tags::TildePhi,\n                 ForceFree::Tags::TildeQ>;\n\n  tuples::tagged_tuple_from_typelist<variables_tags> return_variables(\n      const tnsr::I<DataVector, 3>& x, double t) const {\n    return this->variables(x, t, variables_tags{});\n  }\n};\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.ForceFree.FastWave\",\n    \"[Unit][PointwiseFunctions]\") {\n  // test creation\n  const auto solution = TestHelpers::test_creation<FastWave>(\"\");\n  CHECK(solution == FastWave());\n  // test serialize\n  test_serialization(solution);\n  // test move\n  test_move_semantics(FastWave{}, FastWave{});\n  // test derived\n  register_classes_with_charm<FastWave>();\n  const std::unique_ptr<InitialData> initial_data_ptr =\n      std::make_unique<FastWave>();\n  const std::unique_ptr<InitialData> deserialized_initial_data_ptr =\n      serialize_and_deserialize(initial_data_ptr)->get_clone();\n  CHECK(dynamic_cast<FastWave*>(deserialized_initial_data_ptr.get()) !=\n        nullptr);\n\n  // test solution\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/ForceFree\"};\n  const DataVector used_for_size{10};\n  pypp::check_with_random_values<1>(\n      &FastWaveProxy::return_variables, FastWaveProxy(), \"FastWave\",\n      {\"TildeE\", \"TildeB\", \"TildePsi\", \"TildePhi\", \"TildeQ\"}, {{{-10.0, 10.0}}},\n      {}, used_for_size);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ForceFree/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GeneralRelativitySolutions\")\n\nset(LIBRARY_SOURCES\n  Test_GaugeWave.cpp\n  Test_GaugePlaneWave.cpp\n  Test_HarmonicSchwarzschild.cpp\n  Test_KerrSchild.cpp\n  Test_Minkowski.cpp\n  Test_SphericalKerrSchild.cpp\n  Test_TeukolskyWave.cpp\n  Test_TrumpetSchwarzschild.cpp\n  Test_WrappedGr.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  GeneralRelativity\n  GeneralRelativitySolutions\n  GeneralizedHarmonic\n  GrSolutionsTestHelpers\n  Hydro\n  LinearOperators\n  Options\n  Spectral\n  Utilities\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef gauge_wave_h(x, t, amplitude, wavelength):\n    omega = 2.0 * np.pi / wavelength\n    return 1.0 - amplitude * np.sin(omega * (x[0] - t))\n\n\ndef gauge_wave_dh(x, t, amplitude, wavelength):\n    omega = 2.0 * np.pi / wavelength\n    return -1.0 * omega * amplitude * np.cos(omega * (x[0] - t))\n\n\ndef gauge_wave_sqrt_h(x, t, amplitude, wavelength):\n    return np.sqrt(gauge_wave_h(x, t, amplitude, wavelength))\n\n\ndef gauge_wave_dh_over_2_sqrt_h(x, t, amplitude, wavelength):\n    dh = gauge_wave_dh(x, t, amplitude, wavelength)\n    sqrt_h = gauge_wave_sqrt_h(x, t, amplitude, wavelength)\n    return 0.5 * dh / sqrt_h\n\n\ndef gauge_wave_lapse(x, t, amplitude, wavelength):\n    return gauge_wave_sqrt_h(x, t, amplitude, wavelength)\n\n\ndef gauge_wave_dt_lapse(x, t, amplitude, wavelength):\n    return -1.0 * gauge_wave_dh_over_2_sqrt_h(x, t, amplitude, wavelength)\n\n\ndef gauge_wave_d_lapse(x, t, amplitude, wavelength):\n    d_lapse = np.zeros_like(x)\n    d_lapse[0] = gauge_wave_dh_over_2_sqrt_h(x, t, amplitude, wavelength)\n    return d_lapse\n\n\ndef gauge_wave_shift(x, t, amplitude, wavelength):\n    return np.zeros_like(x)\n\n\ndef gauge_wave_dt_shift(x, t, amplitude, wavelength):\n    return np.zeros_like(x)\n\n\ndef gauge_wave_d_shift(x, t, amplitude, wavelength):\n    return np.zeros((len(x), len(x)))\n\n\ndef gauge_wave_spatial_metric(x, t, amplitude, wavelength):\n    spatial_metric = np.diag(np.ones_like(x))\n    spatial_metric[0, 0] = gauge_wave_h(x, t, amplitude, wavelength)\n    return spatial_metric\n\n\ndef gauge_wave_dt_spatial_metric(x, t, amplitude, wavelength):\n    dt_spatial_metric = np.zeros((len(x), len(x)))\n    dt_spatial_metric[0, 0] = -1.0 * gauge_wave_dh(x, t, amplitude, wavelength)\n    return dt_spatial_metric\n\n\ndef gauge_wave_d_spatial_metric(x, t, amplitude, wavelength):\n    d_spatial_metric = np.zeros((len(x), len(x), len(x)))\n    d_spatial_metric[0, 0, 0] = gauge_wave_dh(x, t, amplitude, wavelength)\n    return d_spatial_metric\n\n\ndef gauge_wave_sqrt_det_spatial_metric(x, t, amplitude, wavelength):\n    return gauge_wave_sqrt_h(x, t, amplitude, wavelength)\n\n\ndef gauge_wave_extrinsic_curvature(x, t, amplitude, wavelength):\n    extrinsic_curvature = np.zeros((len(x), len(x)))\n    extrinsic_curvature[0, 0] = gauge_wave_dh_over_2_sqrt_h(\n        x, t, amplitude, wavelength\n    )\n    return extrinsic_curvature\n\n\ndef gauge_wave_inverse_spatial_metric(x, t, amplitude, wavelength):\n    inverse_spatial_metric = np.diag(np.ones_like(x))\n    inverse_spatial_metric[0, 0] = 1.0 / gauge_wave_h(\n        x, t, amplitude, wavelength\n    )\n    return inverse_spatial_metric\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrHorizon.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef kerr_horizon_radius(\n    theta, phi, mass, dimless_spin_magnitude, spin_theta, spin_phi\n):\n    spin_a = (\n        mass\n        * dimless_spin_magnitude\n        * np.array(\n            (\n                np.sin(spin_theta) * np.cos(spin_phi),\n                np.sin(spin_theta) * np.sin(spin_phi),\n                np.cos(spin_theta),\n            )\n        )\n    )\n    a_squared = np.dot(spin_a, spin_a)\n    r_plus = mass + np.sqrt(mass**2 - a_squared)\n    n_hat = np.array(\n        (\n            np.sin(theta) * np.cos(phi),\n            np.sin(theta) * np.sin(phi),\n            np.cos(theta),\n        )\n    )\n    return np.sqrt(\n        r_plus**2\n        * (r_plus**2 + a_squared)\n        / (r_plus**2 + np.dot(spin_a, n_hat) ** 2)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef minkowski_lapse(x, t):\n    return 1.0\n\n\ndef minkowski_dt_lapse(x, t):\n    return 0.0\n\n\ndef minkowski_d_lapse(x, t):\n    return 0.0\n\n\ndef minkowski_shift(x, t):\n    return np.zeros_like(x)\n\n\ndef minkowski_dt_shift(x, t):\n    return np.zeros_like(x)\n\n\ndef minkowski_d_shift(x, t):\n    return np.zeros((len(x), len(x)))\n\n\ndef minkowski_spatial_metric(x, t):\n    spatial_metric = np.diag(np.ones_like(x))\n    return spatial_metric\n\n\ndef minkowski_dt_spatial_metric(x, t):\n    dt_spatial_metric = np.zeros((len(x), len(x)))\n    return dt_spatial_metric\n\n\ndef minkowski_d_spatial_metric(x, t):\n    d_spatial_metric = np.zeros((len(x), len(x), len(x)))\n    return d_spatial_metric\n\n\ndef minkowski_sqrt_det_spatial_metric(x, t):\n    return 1.0\n\n\ndef minkowski_extrinsic_curvature(x, t):\n    extrinsic_curvature = np.zeros((len(x), len(x)))\n    return extrinsic_curvature\n\n\ndef minkowski_inverse_spatial_metric(x, t):\n    inverse_spatial_metric = np.diag(np.ones_like(x))\n    return inverse_spatial_metric\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.AnalyticSolutions.GeneralRelativity.Python\"\n  \"Test_Bindings.py\"\n  \"Unit;PointwiseFunctions;Python\"\n  PyGrSolutions)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Python/Test_Bindings.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures.Tensor import DataVector, tnsr\nfrom spectre.PointwiseFunctions.AnalyticSolutions.GeneralRelativity import (\n    KerrSchild,\n)\n\n\nclass TestGrSolutions(unittest.TestCase):\n    def test_kerr_schild(self):\n        solution = KerrSchild(mass=1.0, dimensionless_spin=[0.0, 0.0, 0.0])\n        # Check some quantities at the horizon\n        (lapse, trace_K) = solution.variables(\n            np.array([[2.0], [0.0], [0.0]]),\n            [\"Lapse\", \"TraceExtrinsicCurvature\"],\n        ).values()\n        npt.assert_allclose(lapse, np.sqrt(0.5))\n        npt.assert_allclose(trace_K, 10.0 * (1.0 / 8.0) ** (3.0 / 2.0))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_GaugePlaneWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   tmpl::list<MathFunctions::PowX<1, Frame::Inertial>>>>;\n  };\n};\n\ntemplate <size_t Dim, typename DataType>\ntnsr::I<DataType, Dim, Frame::Inertial> spatial_coords(\n    const DataType& used_for_size) {\n  auto x = make_with_value<tnsr::I<DataType, Dim, Frame::Inertial>>(\n      used_for_size, 0.0);\n  get<0>(x) = 3.0;\n  if (Dim >= 2) {\n    x.get(1) = 5.0;\n  }\n  if (Dim >= 3) {\n    x.get(2) = 7.0;\n  }\n  return x;\n}\n\ntemplate <size_t Dim>\nstd::array<double, Dim> make_wave_vector() {\n  if constexpr (Dim == 1) {\n    return {{0.5}};\n  }\n  if constexpr (Dim == 2) {\n    return {{0.3, 0.4}};\n  }\n  if constexpr (Dim == 3) {\n    return {{0.3, 0.4, 1.2}};\n  }\n}\n\ntemplate <size_t Dim, typename DataType>\nDataType u(const std::array<double, Dim>& k,\n           const tnsr::I<DataType, Dim, Frame::Inertial>& x, const double t) {\n  const double omega = magnitude(k);\n  auto u = make_with_value<DataType>(x, -omega * t);\n  for (size_t i = 0; i < Dim; ++i) {\n    u += gsl::at(k, i) * x.get(i);\n  }\n  return u;\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test_gauge_wave(const gr::Solutions::GaugePlaneWave<Dim>& solution,\n                     const DataType& used_for_size) {\n  using GaugePlaneWave = gr::Solutions::GaugePlaneWave<Dim>;\n\n  // The spacetime metric for this solution is in Kerr-Schild form\n  // g_ab = \\eta_ab + H l_a l_b\n  //\n  // where \\eta_ab = diag(-1, 1, 1, 1) is the Minkowski metric\n  // and   l_a = (-\\omega, k_a) is a null vector\n  // with k_a a constant wave vector and \\omega = k^a k_a\n  // and   H = F[u] is a scalar function of u = k_a x^a - \\omega t\n  //\n  // Thus d_a H = F'[u] l_a\n  //\n  // Therefore d_c g_ab = F'[u] l_c l_a l_b\n\n  // Parameters for GaugePlaneWave solution\n  const auto wave_vector = make_wave_vector<Dim>();\n  const auto null_covector = prepend(wave_vector, -magnitude(wave_vector));\n\n  // Verify that the solution passed in was constructed with the correct\n  // parameters\n  REQUIRE(solution ==\n          GaugePlaneWave{\n              wave_vector,\n              std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2)});\n\n  const tnsr::I<DataType, Dim, Frame::Inertial> x =\n      spatial_coords<Dim>(used_for_size);\n  const double t = 11.0;\n\n  const auto expected_u = u(wave_vector, x, t);\n  const auto expected_h = square(expected_u);\n  const auto expected_d_h = 2.0 * expected_u;\n\n  const tuples::tagged_tuple_from_typelist<\n      typename gr::Solutions::GaugePlaneWave<Dim>::template tags<DataType>>\n      vars = solution.variables(\n          x, t, typename GaugePlaneWave::template tags<DataType>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars);\n  const auto& d_lapse =\n      get<typename GaugePlaneWave::template DerivLapse<DataType>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataType, Dim>>(vars);\n  const auto& d_shift =\n      get<typename GaugePlaneWave::template DerivShift<DataType>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataType, Dim>>>(vars);\n  const auto& gamma = get<gr::Tags::SpatialMetric<DataType, Dim>>(vars);\n  const auto& dt_gamma =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataType, Dim>>>(vars);\n  const auto& d_gamma =\n      get<typename GaugePlaneWave::template DerivSpatialMetric<DataType>>(vars);\n\n  // Rather than directly checking the 3+1 quantities, the test computes the\n  // spacetime metric and its derivatives from the 3+1 quantities and checks\n  // that they are the simple expressions listed above.\n\n  const auto g = gr::spacetime_metric(lapse, shift, gamma);\n\n  for (size_t a = 0; a < Dim + 1; ++a) {\n    for (size_t b = a; b < Dim + 1; ++b) {\n      DataType expected_g_ab =\n          expected_h * gsl::at(null_covector, a) * gsl::at(null_covector, b);\n      if (a == b) {\n        expected_g_ab += (a == 0 ? -1.0 : 1.0);\n      }\n      CHECK_ITERABLE_APPROX(expected_g_ab, g.get(a, b));\n    }\n  }\n\n  const auto d_g = gr::derivatives_of_spacetime_metric(\n      lapse, dt_lapse, d_lapse, shift, dt_shift, d_shift, gamma, dt_gamma,\n      d_gamma);\n\n  for (size_t c = 0; c < Dim + 1; ++c) {\n    CAPTURE(c);\n    for (size_t a = 0; a < Dim + 1; ++a) {\n      CAPTURE(a);\n      for (size_t b = a; b < Dim + 1; ++b) {\n        CAPTURE(b);\n        DataType expected_d_g_cab = expected_d_h * gsl::at(null_covector, c) *\n                                    gsl::at(null_covector, a) *\n                                    gsl::at(null_covector, b);\n        CHECK_ITERABLE_APPROX(expected_d_g_cab, d_g.get(c, a, b));\n      }\n    }\n  }\n\n  // Check that we can retrieve tags individually from the solution\n  tmpl::for_each<typename GaugePlaneWave::template tags<DataType>>(\n      [&solution, &vars, &x, &t](auto tag_v) {\n        using Tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<Tag>(vars), get<Tag>(solution.variables(\n                                                  x, t, tmpl::list<Tag>{})));\n      });\n\n  TestHelpers::AnalyticSolutions::test_tag_retrieval(\n      solution, x, t,\n      typename gr::Solutions::GaugePlaneWave<Dim>::template tags<DataType>{});\n}\n\nvoid test_consistency() {\n  const gr::Solutions::GaugePlaneWave<3> solution(\n      make_wave_vector<3>(),\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n  TestHelpers::VerifyGrSolution::verify_consistency(\n      solution, 1.234, tnsr::I<double, 3>{{{1.2, 2.3, 3.4}}}, 0.01, 1.0e-8);\n}\n\nvoid test_serialize() {\n  register_factory_classes_with_charm<Metavariables>();\n  const gr::Solutions::GaugePlaneWave<3> solution(\n      make_wave_vector<3>(),\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n  test_serialization(solution);\n  test_gauge_wave(serialize_and_deserialize(solution), DataVector{5});\n}\n\nvoid test_copy_and_move() {\n  gr::Solutions::GaugePlaneWave<3> solution(\n      make_wave_vector<3>(),\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n  test_copy_semantics(solution);\n  auto solution_copy = solution;\n  // clang-tidy: std::move of trivially copyable type\n  test_move_semantics(std::move(solution), solution_copy);  // NOLINT\n}\n\nvoid test_construct_from_options() {\n  const gr::Solutions::GaugePlaneWave<3> solution(\n      make_wave_vector<3>(),\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n  const auto created =\n      TestHelpers::test_creation<gr::Solutions::GaugePlaneWave<3>,\n                                 Metavariables>(\n          \"WaveVector: [0.3, 0.4, 1.2]\\n\"\n          \"Profile:\\n\"\n          \"    PowX:\\n\"\n          \"      Power: 2\");\n  CHECK(created == solution);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Gr.GaugePlaneWave\",\n                  \"[PointwiseFunctions][Unit]\") {\n  {\n    const gr::Solutions::GaugePlaneWave<1> solution(\n        make_wave_vector<1>(),\n        std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n    test_gauge_wave(\n        solution, DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n    test_gauge_wave(solution, std::numeric_limits<double>::signaling_NaN());\n  }\n  {\n    const gr::Solutions::GaugePlaneWave<2> solution(\n        make_wave_vector<2>(),\n        std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n    test_gauge_wave(\n        solution, DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n    test_gauge_wave(solution, std::numeric_limits<double>::signaling_NaN());\n  }\n  {\n    const gr::Solutions::GaugePlaneWave<3> solution(\n        make_wave_vector<3>(),\n        std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n    test_gauge_wave(\n        solution, DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n    test_gauge_wave(solution, std::numeric_limits<double>::signaling_NaN());\n  }\n  test_consistency();\n  test_copy_and_move();\n  test_serialize();\n  test_construct_from_options();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_GaugeWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstruct GaugeWaveProxy : gr::Solutions::GaugeWave<Dim> {\n  using gr::Solutions::GaugeWave<Dim>::GaugeWave;\n\n  template <typename DataType>\n  using variables_tags =\n      typename gr::Solutions::GaugeWave<Dim>::template tags<DataType>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>> test_variables(\n      const tnsr::I<DataType, Dim>& x, const double t) const {\n    return this->variables(x, t, variables_tags<DataType>{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\ntnsr::I<DataType, Dim, Frame::Inertial> spatial_coords(\n    const DataType& used_for_size) {\n  auto x = make_with_value<tnsr::I<DataType, Dim, Frame::Inertial>>(\n      used_for_size, 0.0);\n  get<0>(x) = 1.32;\n  if (Dim >= 2) {\n    x.get(1) = 0.82;\n  }\n  if (Dim >= 3) {\n    x.get(2) = 1.24;\n  }\n  return x;\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test_gauge_wave(const gr::Solutions::GaugeWave<Dim>& solution,\n                     const DataType& used_for_size) {\n  using GaugeWave = gr::Solutions::GaugeWave<Dim>;\n  // The solution is as follows, with H = 1 - A \\sin((2*\\pi/d)*(x-t))\n  // and \\partial_x H = - A (2*\\pi/d) \\cos((2*\\pi/d)*(x-t)):\n  //\n  // \\gamma_{ij} = 0, except \\gamma_{xx} = H and \\gamma_{yy} = \\gamma_{zz} = 1\n  // \\gamma^{ij} = 0, except \\gamma^{xx} = H and \\gamma^{yy} = \\gamma^{zz} = 1\n  // \\partial_t\\gamma_{ij} = 0, except \\partial_t\\gamma_{xx} = - \\partial_x H\n  // \\partial_x\\gamma_{ij} = 0, except \\partial_x\\gamma_{xx} = \\partial_x H\n  // \\sqrt{\\gamma} = \\sqrt{H}\n  // \\alpha = \\sqrt{H}\n  // \\partial_t \\alpha = - \\partial_x H / (2 \\sqrt{H})\n  // \\partial_x \\alpha = \\partial_x H / (2 \\sqrt{H})\n  // \\partial_y \\alpha = \\partial_z \\alpha = 0\n  // \\beta^i = 0\n  // \\partial_t \\beta^i = 0\n  // \\partial_i \\beta^j = 0\n  // K_{ij} = 0, except K_{xx} = \\partial_x H / (2 \\sqrt{H})\n\n  // Parameters for GaugeWave solution\n  const double amplitude = 0.24;\n  const double wavelength = 4.4;\n\n  // Verify that the solution passed in was constructed with the correct\n  // parameters\n  REQUIRE(solution == GaugeWave{amplitude, wavelength});\n\n  // Using `auto` triggers a gcc bug (known broken in 6.5.0, working in 9.2.0)\n  const tnsr::I<DataType, Dim, Frame::Inertial> x =\n      spatial_coords<Dim>(used_for_size);\n  const double t = 1.3;\n\n  // Using `auto` triggers a gcc bug (known broken in 6.5.0, working in 9.2.0)\n  const tuples::tagged_tuple_from_typelist<\n      typename gr::Solutions::GaugeWave<Dim>::template tags<DataType>>\n      vars = solution.variables(x, t,\n                                typename GaugeWave::template tags<DataType>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars);\n  const auto& d_lapse =\n      get<typename GaugeWave::template DerivLapse<DataType>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataType, Dim>>(vars);\n  const auto& d_shift =\n      get<typename GaugeWave::template DerivShift<DataType>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataType, Dim>>>(vars);\n  const auto& gamma = get<gr::Tags::SpatialMetric<DataType, Dim>>(vars);\n  const auto& dt_gamma =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataType, Dim>>>(vars);\n  const auto& d_gamma =\n      get<typename GaugeWave::template DerivSpatialMetric<DataType>>(vars);\n\n  // Check those quantities that should be zero or one.\n  const auto zero = make_with_value<DataType>(x, 0.);\n  const auto one = make_with_value<DataType>(x, 1.);\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK_ITERABLE_APPROX(shift.get(i), zero);\n    CHECK_ITERABLE_APPROX(dt_shift.get(i), zero);\n    if (i > 0) {\n      CHECK_ITERABLE_APPROX(d_lapse.get(i), zero);\n    }\n    for (size_t j = 0; j < Dim; ++j) {\n      CHECK_ITERABLE_APPROX(d_shift.get(i, j), zero);\n      if (i != j) {\n        CHECK_ITERABLE_APPROX(gamma.get(i, j), zero);\n      } else {\n        if (i > 0) {\n          CHECK_ITERABLE_APPROX(gamma.get(i, j), one);\n        }\n      }\n      if (i > 0 or j > 0) {\n        CHECK_ITERABLE_APPROX(dt_gamma.get(i, j), zero);\n        for (size_t k = 0; k < Dim; ++k) {\n          CHECK_ITERABLE_APPROX(d_gamma.get(k, i, j), zero);\n        }\n      } else {\n        for (size_t k = 1; k < Dim; ++k) {\n          CHECK_ITERABLE_APPROX(d_gamma.get(k, i, j), zero);\n        }\n      }\n    }\n  }\n\n  // Check quantities that depend on h\n  const auto expected_lapse =\n      make_with_value<DataType>(get(lapse), 0.9965673824732139);\n  const auto expected_dt_lapse =\n      make_with_value<DataType>(get(lapse), 0.17187971493515364);\n  const auto expected_d_lapse_0 =\n      make_with_value<DataType>(get(lapse), -0.17187971493515364);\n  CHECK_ITERABLE_APPROX(get(lapse), expected_lapse);\n  CHECK_ITERABLE_APPROX(get(dt_lapse), expected_dt_lapse);\n  CHECK_ITERABLE_APPROX(get<0>(d_lapse), expected_d_lapse_0);\n\n  const auto expected_gamma_0_0 =\n      make_with_value<DataType>(get(lapse), 0.993146547809513);\n  const auto expected_dt_gamma_0_0 =\n      make_with_value<DataType>(get(lapse), 0.34257943522633644);\n  const auto expected_d_gamma_0_0_0 =\n      make_with_value<DataType>(get(lapse), -0.34257943522633644);\n  // CHECK_ITERABLE_APPROX breaks if you try to pass get<0, 0>(...)\n  // in for argument a, becaue it doesn't like the comma. So\n  // define aliases for get<0, 0>(gamma), etc. first.\n  const auto& gamma_0_0 = get<0, 0>(gamma);\n  const auto& dt_gamma_0_0 = get<0, 0>(dt_gamma);\n  const auto& d_gamma_0_0_0 = get<0, 0, 0>(d_gamma);\n  CHECK_ITERABLE_APPROX(gamma_0_0, expected_gamma_0_0);\n  CHECK_ITERABLE_APPROX(dt_gamma_0_0, expected_dt_gamma_0_0);\n  CHECK_ITERABLE_APPROX(d_gamma_0_0_0, expected_d_gamma_0_0_0);\n\n  // Check quantities derivable from spatial metric, lapse, shift, and their\n  // derivatives\n  const auto& inv_gamma =\n      get<gr::Tags::InverseSpatialMetric<DataType, Dim>>(vars);\n  const auto& sqrt_det_gamma =\n      get<gr::Tags::SqrtDetSpatialMetric<DataType>>(vars);\n  const auto& det_and_inverse = determinant_and_inverse(gamma);\n  const auto& expected_inv_gamma = det_and_inverse.second;\n  const auto& expected_sqrt_det_gamma = sqrt(get(det_and_inverse.first));\n  CHECK_ITERABLE_APPROX(inv_gamma, expected_inv_gamma);\n  CHECK_ITERABLE_APPROX(get(sqrt_det_gamma), expected_sqrt_det_gamma);\n\n  const auto& K = get<gr::Tags::ExtrinsicCurvature<DataType, Dim>>(vars);\n  const auto& expected_K =\n      gr::extrinsic_curvature(lapse, shift, d_shift, gamma, dt_gamma, d_gamma);\n  CHECK_ITERABLE_APPROX(K, expected_K);\n\n  // Check that we can retrieve tags individually from the solution\n  tmpl::for_each<typename GaugeWave::template tags<DataType>>(\n      [&solution, &vars, &x, &t](auto tag_v) {\n        using Tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<Tag>(vars), get<Tag>(solution.variables(\n                                                  x, t, tmpl::list<Tag>{})));\n      });\n\n  // Check with random values\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/\"};\n  pypp::check_with_random_values<1>(\n      &GaugeWaveProxy<Dim>::template test_variables<DataType>,\n      GaugeWaveProxy<Dim>(amplitude, wavelength), \"GaugeWave\",\n      {\"gauge_wave_lapse\", \"gauge_wave_dt_lapse\", \"gauge_wave_d_lapse\",\n       \"gauge_wave_shift\", \"gauge_wave_dt_shift\", \"gauge_wave_d_shift\",\n       \"gauge_wave_spatial_metric\", \"gauge_wave_dt_spatial_metric\",\n       \"gauge_wave_d_spatial_metric\", \"gauge_wave_sqrt_det_spatial_metric\",\n       \"gauge_wave_extrinsic_curvature\", \"gauge_wave_inverse_spatial_metric\"},\n      {{{1.0, 20.0}}}, std::make_tuple(amplitude, wavelength), used_for_size);\n\n  TestHelpers::AnalyticSolutions::test_tag_retrieval(\n      solution, x, t,\n      typename gr::Solutions::GaugeWave<Dim>::template tags<DataType>{});\n}\n\nvoid test_consistency() {\n  const gr::Solutions::GaugeWave<3> solution(0.3, 2.4);\n  TestHelpers::VerifyGrSolution::verify_consistency(\n      solution, 1.234, tnsr::I<double, 3>{{{1.2, 2.3, 3.4}}}, 0.01, 1.0e-8);\n}\n\nvoid test_serialize() {\n  const gr::Solutions::GaugeWave<3> solution(0.24, 4.4);\n  test_serialization(solution);\n  test_gauge_wave(serialize_and_deserialize(solution), DataVector{5});\n}\n\nvoid test_copy_and_move() {\n  gr::Solutions::GaugeWave<3> solution(0.25, 4.0);\n  test_copy_semantics(solution);\n  auto solution_copy = solution;\n  // clang-tidy: std::move of trivially copyable type\n  test_move_semantics(std::move(solution), solution_copy);  // NOLINT\n}\n\nvoid test_construct_from_options() {\n  const auto created = TestHelpers::test_creation<gr::Solutions::GaugeWave<3>>(\n      \"Amplitude: 0.24\\n\"\n      \"Wavelength: 4.4\");\n  CHECK(created == gr::Solutions::GaugeWave<3>(0.24, 4.4));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Gr.GaugeWave\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const double amplitude = 0.24;\n  const double wavelength = 4.4;\n  {\n    const gr::Solutions::GaugeWave<1> solution(amplitude, wavelength);\n    test_gauge_wave(\n        solution, DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n    test_gauge_wave(solution, std::numeric_limits<double>::signaling_NaN());\n  }\n  {\n    const gr::Solutions::GaugeWave<2> solution(amplitude, wavelength);\n    test_gauge_wave(\n        solution, DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n    test_gauge_wave(solution, std::numeric_limits<double>::signaling_NaN());\n  }\n  {\n    const gr::Solutions::GaugeWave<3> solution(amplitude, wavelength);\n    test_gauge_wave(\n        solution, DataVector(5, std::numeric_limits<double>::signaling_NaN()));\n    test_gauge_wave(solution, std::numeric_limits<double>::signaling_NaN());\n  }\n  test_consistency();\n  test_copy_and_move();\n  test_serialize();\n  test_construct_from_options();\n\n  CHECK_THROWS_WITH(\n      []() { const gr::Solutions::GaugeWave<3> solution(-1.25, 1.0); }(),\n      Catch::Matchers::ContainsSubstring(\"Amplitude must be less than one\"));\n  CHECK_THROWS_WITH(\n      []() { const gr::Solutions::GaugeWave<3> solution(0.0, -0.25); }(),\n      Catch::Matchers::ContainsSubstring(\"Wavelength must be non-negative\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<gr::Solutions::GaugeWave<3>>(\n                        \"Amplitude: -1.25\\n\"\n                        \"Wavelength: 1.0\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -1.25 is below the lower bound of -1\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<gr::Solutions::GaugeWave<3>>(\n                        \"Amplitude: 1.0\\n\"\n                        \"Wavelength: -0.25\\n\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.25 is below the lower bound of 0\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_HarmonicSchwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n// SpEC implementation of HarmonicSchwarzschild\nnamespace spec {\n// Schwarzschild black hole of a t=const slice of time-harmonic\n// coordinates.\ntemplate <typename Frame, typename DataType>\nclass HarmonicSchwarzschild {\n public:\n  // Constructor\n  HarmonicSchwarzschild(const double mass,\n                        const std::array<double, 3>& center) {\n    mMass = mass;\n    mCenter = center;\n  }\n\n public:\n  bool IsTimeDependent() const { return false; }\n\n  void SetCoordinates(const tnsr::I<DataType, 3, Frame>& x) {\n    mCoords = x;\n    const Scalar<DataType> R(\n        sqrt(square(get<0>(mCoords) - gsl::at(mCenter, 0)) +\n             square(get<1>(mCoords) - gsl::at(mCenter, 1)) +\n             square(get<2>(mCoords) - gsl::at(mCenter, 2))));\n    get(mMoR) = mMass / get(R);\n    get(m2MoRpM) = 2.0 * mMass / (mMass + get(R));\n    get(mgrr) = 1.0 + get(m2MoRpM) + square(get(m2MoRpM)) + cube(get(m2MoRpM));\n    get(mdgrrdr) = (-0.5 / mMass) * square(get(m2MoRpM)) -\n                   (1.0 / mMass) * cube(get(m2MoRpM)) -\n                   (1.5 / mMass) * square(square(get(m2MoRpM)));\n    for (int i = 0; i < 3; ++i)\n      mXoR.get(i) = (mCoords.get(i) - gsl::at(mCenter, i)) / get(R);\n  }\n\n  // g(i,j) = g_{ij}\n  void LowerMetric(const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> g) const {\n    Scalar<DataType> gT(square(1.0 + get(mMoR)));\n    Scalar<DataType> f1(get(mgrr) - get(gT));\n\n    for (int i = 0; i < 3; ++i) {\n      for (int j = i; j < 3; ++j) {  // symmetry\n        g->get(i, j) = get(f1) * mXoR.get(i) * mXoR.get(j);\n        if (i == j)\n          g->get(i, j) += get(gT);\n      }\n    }\n  }\n  void DtLowerMetric(\n      const gsl::not_null<tnsr::ii<DataType, 3, Frame>*> dtg) const {\n    for (int i = 0; i < 3; ++i) {\n      for (int j = i; j < 3; ++j) {  // symmetry\n        dtg->get(i, j) = get(make_with_value<Scalar<DataType>>(get(mMoR), 0.0));\n      }\n    }\n  }\n  // dg(i,j)(k) = \\partial_k g_{ij}\n  void DerivLowerMetric(\n      const gsl::not_null<tnsr::ijj<DataType, 3, Frame>*> dg) const {\n    Scalar<DataType> gT(square(1.0 + get(mMoR)));\n    Scalar<DataType> dgTdr((-2.0 / mMass) *\n                           (square(get(mMoR)) + cube(get(mMoR))));\n\n    Scalar<DataType> f1((1.0 / mMass) * get(mMoR) * (get(mgrr) - get(gT)));\n    Scalar<DataType> f2(get(mdgrrdr) - get(dgTdr) - 2.0 * get(f1));\n\n    for (int i = 0; i < 3; ++i) {\n      for (int j = i; j < 3; ++j) {  // symmetry\n        for (int k = 0; k < 3; ++k) {\n          dg->get(k, i, j) = get(f2) * mXoR.get(i) * mXoR.get(j) * mXoR.get(k);\n          if (i == k)\n            dg->get(k, i, j) += get(f1) * mXoR.get(j);\n          if (j == k)\n            dg->get(k, i, j) += get(f1) * mXoR.get(i);\n          if (i == j)\n            dg->get(k, i, j) += get(dgTdr) * mXoR.get(k);\n        }\n      }\n    }\n  }\n\n  // N() is the 'N' that appears in \\partial_t g_{ij} = - 2 N K_{ij}+...\n  void PhysicalLapse(const gsl::not_null<Scalar<DataType>*> N) const {\n    get(*N) = 1.0 / sqrt(get(mgrr));\n  }\n  void DtPhysicalLapse(const gsl::not_null<Scalar<DataType>*> dtN) const {\n    get(*dtN) = get(make_with_value<Scalar<DataType>>(get(mMoR), 0.0));\n  }\n  void DerivPhysicalLapse(\n      const gsl::not_null<tnsr::i<DataType, 3, Frame>*> dN) const {\n    Scalar<DataType> f1(-0.5 * get(mdgrrdr) / pow(get(mgrr), 1.5));\n\n    for (int i = 0; i < 3; ++i)\n      dN->get(i) = get(f1) * mXoR.get(i);\n  }\n\n  // beta(i) = \\beta^i\n  void UpperShift(\n      const gsl::not_null<tnsr::I<DataType, 3, Frame>*> beta) const {\n    Scalar<DataType> f1(square(get(m2MoRpM)) / get(mgrr));\n\n    for (int i = 0; i < 3; ++i)\n      beta->get(i) = get(f1) * mXoR.get(i);\n  }\n  void DtUpperShift(\n      const gsl::not_null<tnsr::I<DataType, 3, Frame>*> dtbeta) const {\n    for (int i = 0; i < 3; ++i)\n      dtbeta->get(i) = get(make_with_value<Scalar<DataType>>(get(mMoR), 0.0));\n  }\n  // dbeta(i)(k) = \\partial_k beta^i\n  void DerivUpperShift(\n      const gsl::not_null<tnsr::iJ<DataType, 3, Frame>*> dbeta) const {\n    Scalar<DataType> f1((1.0 / mMass) * get(mMoR) * square(get(m2MoRpM)) /\n                        get(mgrr));\n\n    Scalar<DataType> f2(-get(f1) -\n                        (1.0 / mMass) * cube(get(m2MoRpM)) / get(mgrr) -\n                        get(mdgrrdr) * square(get(m2MoRpM) / get(mgrr)));\n\n    for (int i = 0; i < 3; ++i) {\n      for (int j = 0; j < 3; ++j) {\n        dbeta->get(j, i) = get(f2) * mXoR.get(i) * mXoR.get(j);\n        if (i == j)\n          dbeta->get(j, i) += get(f1);\n      }\n    }\n  }\n  int SpatialDim() const { return 3; }\n\n private:\n  tnsr::I<DataType, 3, Frame> mCoords;\n  Scalar<DataType> mMoR;             // M/R\n  Scalar<DataType> m2MoRpM;          // 2M/(R+M)\n  tnsr::I<DataType, 3, Frame> mXoR;  // x_i/R\n  Scalar<DataType> mgrr;\n  Scalar<DataType> mdgrrdr;\n  double mMass;\n  std::array<double, 3> mCenter;\n};\n}  // namespace spec\n\n// Get test coordinates\ntemplate <typename Frame, typename DataType>\ntnsr::I<DataType, 3, Frame> spatial_coords(const DataType& used_for_size) {\n  auto x = make_with_value<tnsr::I<DataType, 3, Frame>>(used_for_size, 0.0);\n  get<0>(x) = 1.32;\n  get<1>(x) = 0.82;\n  get<2>(x) = 1.24;\n  return x;\n}\n\ntemplate <typename Frame, typename DataType>\nvoid test_tag_retrieval(const DataType& used_for_size) {\n  // Parameters for HarmonicSchwarzschild solution\n  const double mass = 1.234;\n  const std::array<double, 3> center{{1.0, 2.0, 3.0}};\n  const auto x = spatial_coords<Frame>(used_for_size);\n  const double t = 1.3;\n\n  // Evaluate solution\n  const gr::Solutions::HarmonicSchwarzschild solution(mass, center);\n  TestHelpers::AnalyticSolutions::test_tag_retrieval(\n      solution, x, t,\n      typename gr::Solutions::HarmonicSchwarzschild::template tags<DataType,\n                                                                   Frame>{});\n}\n\nvoid test_serialize() {\n  gr::Solutions::HarmonicSchwarzschild solution(3.0, {{0.0, 3.0, 4.0}});\n  test_serialization(solution);\n}\n\nvoid test_copy_and_move() {\n  gr::Solutions::HarmonicSchwarzschild solution(3.0, {{0.0, 3.0, 4.0}});\n  test_copy_semantics(solution);\n  auto solution_copy = solution;\n  // clang-tidy: std::move of trivially copyable type\n  test_move_semantics(std::move(solution), solution_copy);  // NOLINT\n}\n\nvoid test_construct_from_options() {\n  const auto created =\n      TestHelpers::test_creation<gr::Solutions::HarmonicSchwarzschild>(\n          \"Mass: 0.5\\n\"\n          \"Center: [1.0,3.0,2.0]\");\n  CHECK(created ==\n        gr::Solutions::HarmonicSchwarzschild(0.5, {{1.0, 3.0, 2.0}}));\n}\n\n// Test that computed spacetime quantities are computed as expected. See\n// documentation for `gr::Solutions::HarmonicSchwarzschild` to see equations for\n// expected quantities.\ntemplate <typename Frame, typename DataType>\nvoid test_computed_quantities(const DataType& used_for_size) {\n  // Parameters for HarmonicSchwarzschild solution\n  const double mass = 1.03;\n  const std::array<double, 3> center{{0.2, -0.1, 0.4}};\n  const auto x = spatial_coords<Frame>(used_for_size);\n  const double t = 1.3;\n\n  // Evaluate solution\n  gr::Solutions::HarmonicSchwarzschild solution(mass, center);\n\n  // Get solution's spacetime quantities\n  const auto vars = solution.variables(\n      x, t,\n      typename gr::Solutions::HarmonicSchwarzschild::tags<DataType, Frame>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars);\n  const auto& d_lapse =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivLapse<DataType,\n                                                                    Frame>>(\n          vars);\n  const auto& shift = get<gr::Tags::Shift<DataType, 3, Frame>>(vars);\n  const auto& d_shift =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivShift<DataType,\n                                                                    Frame>>(\n          vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataType, 3, Frame>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3, Frame>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivSpatialMetric<\n          DataType, Frame>>(vars);\n  const auto& sqrt_det_spatial_metric =\n      get<typename gr::Tags::SqrtDetSpatialMetric<DataType>>(vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataType, 3, Frame>>(vars);\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataType, 3, Frame>>(vars);\n\n  // Check that metric * inverse metric = identity\n  auto identity =\n      make_with_value<tnsr::iJ<DataType, 3, Frame>>(used_for_size, 0.0);\n  get<0, 0>(identity) = 1.0;\n  get<1, 1>(identity) = 1.0;\n  get<2, 2>(identity) = 1.0;\n\n  tnsr::iJ<DataType, 3, Frame> metric_times_inverse_metric{};\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      metric_times_inverse_metric.get(i, j) =\n          spatial_metric.get(i, 0) * inverse_spatial_metric.get(0, j);\n      for (size_t k = 1; k < 3; k++) {\n        metric_times_inverse_metric.get(i, j) +=\n            spatial_metric.get(i, k) * inverse_spatial_metric.get(k, j);\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(metric_times_inverse_metric, identity);\n\n  // Check those quantities that should be zero\n  const auto zero = make_with_value<DataType>(x, 0.);\n  CHECK(dt_lapse.get() == zero);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(dt_shift.get(i) == zero);\n    for (size_t j = 0; j < 3; ++j) {\n      CHECK(dt_spatial_metric.get(i, j) == zero);\n    }\n  }\n\n  // Check remaining quantities\n\n  tnsr::I<DataType, 3, Frame> expected_x_minus_center{};\n  for (size_t i = 0; i < 3; ++i) {\n    expected_x_minus_center.get(i) = x.get(i) - gsl::at(center, i);\n  }\n\n  const DataType expected_r = get(magnitude(expected_x_minus_center));\n  const DataType expected_one_over_r_squared = 1.0 / square(expected_r);\n  const DataType expected_one_over_r_cubed = 1.0 / cube(expected_r);\n  const DataType expected_two_m_over_m_plus_r =\n      2.0 * mass / (mass + expected_r);\n  const DataType expected_spatial_metric_rr =\n      1.0 + expected_two_m_over_m_plus_r +\n      square(expected_two_m_over_m_plus_r) + cube(expected_two_m_over_m_plus_r);\n  const DataType expected_d_spatial_metric_rr =\n      -1.0 / (2.0 * mass) * square(expected_two_m_over_m_plus_r) -\n      (1.0 / mass) * cube(expected_two_m_over_m_plus_r) -\n      (3.0 / (2.0 * mass)) * pow<4>(expected_two_m_over_m_plus_r);\n  const DataType expected_f_0 = square(1 + mass / expected_r);\n  const DataType expected_d_f_0 =\n      2.0 * (1 + mass / expected_r) * (-mass * expected_one_over_r_squared);\n  const DataType expected_f_1 =\n      (expected_spatial_metric_rr - expected_f_0) / expected_r;\n  const DataType expected_f_2 =\n      expected_d_spatial_metric_rr - expected_d_f_0 - 2.0 * expected_f_1;\n  const DataType expected_f_3 = square(expected_two_m_over_m_plus_r) /\n                                (expected_r * expected_spatial_metric_rr);\n  const DataType expected_f_4 =\n      -expected_f_3 -\n      (1.0 / mass) * cube(expected_two_m_over_m_plus_r) /\n          expected_spatial_metric_rr -\n      expected_d_spatial_metric_rr *\n          square((expected_two_m_over_m_plus_r) / expected_spatial_metric_rr);\n\n  auto expected_lapse = make_with_value<Scalar<DataType>>(x, 0.0);\n  get(expected_lapse) = 1.0 / sqrt(expected_spatial_metric_rr);\n  CHECK_ITERABLE_APPROX(lapse, expected_lapse);\n\n  tnsr::i<DataType, 3, Frame> expected_d_lapse{};\n  for (size_t i = 0; i < 3; ++i) {\n    expected_d_lapse.get(i) = -0.5 * cube(get(expected_lapse)) *\n                              expected_d_spatial_metric_rr *\n                              expected_x_minus_center.get(i) / expected_r;\n  }\n  CHECK_ITERABLE_APPROX(d_lapse, expected_d_lapse);\n\n  tnsr::I<DataType, 3, Frame> expected_shift{};\n  for (size_t i = 0; i < 3; ++i) {\n    expected_shift.get(i) = square(expected_two_m_over_m_plus_r) *\n                            expected_x_minus_center.get(i) /\n                            (expected_r * expected_spatial_metric_rr);\n  }\n  CHECK_ITERABLE_APPROX(shift, expected_shift);\n\n  tnsr::iJ<DataType, 3, Frame> expected_d_shift{};\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t i = 0; i < 3; ++i) {\n      expected_d_shift.get(k, i) =\n          expected_f_4 * expected_x_minus_center.get(i) *\n          expected_x_minus_center.get(k) * expected_one_over_r_squared;\n      if (i == k) {\n        expected_d_shift.get(k, i) += expected_f_3;\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(d_shift, expected_d_shift);\n\n  tnsr::ii<DataType, 3, Frame> expected_spatial_metric{};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      expected_spatial_metric.get(i, j) =\n          (expected_spatial_metric_rr - expected_f_0) *\n          expected_x_minus_center.get(i) * expected_x_minus_center.get(j) *\n          expected_one_over_r_squared;\n      if (i == j) {\n        expected_spatial_metric.get(i, j) += expected_f_0;\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(spatial_metric, expected_spatial_metric);\n\n  tnsr::ijj<DataType, 3, Frame> expected_d_spatial_metric{};\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {\n        expected_d_spatial_metric.get(k, i, j) =\n            expected_f_2 * expected_x_minus_center.get(i) *\n            expected_x_minus_center.get(j) * expected_x_minus_center.get(k) *\n            expected_one_over_r_cubed;\n        if (i == k) {\n          expected_d_spatial_metric.get(k, i, j) +=\n              expected_f_1 * expected_x_minus_center.get(j) / expected_r;\n        }\n        if (j == k) {\n          expected_d_spatial_metric.get(k, i, j) +=\n              expected_f_1 * expected_x_minus_center.get(i) / expected_r;\n        }\n        if (i == j) {\n          expected_d_spatial_metric.get(k, i, j) +=\n              expected_d_f_0 * expected_x_minus_center.get(k) / expected_r;\n        }\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(d_spatial_metric, expected_d_spatial_metric);\n\n  const auto expected_det_and_inverse_spatial_metric =\n      determinant_and_inverse(expected_spatial_metric);\n  const auto expected_sqrt_det_spatial_metric =\n      sqrt(get(expected_det_and_inverse_spatial_metric.first));\n  CHECK_ITERABLE_APPROX(get(sqrt_det_spatial_metric),\n                        expected_sqrt_det_spatial_metric);\n\n  const auto& expected_inverse_spatial_metric =\n      expected_det_and_inverse_spatial_metric.second;\n  CHECK_ITERABLE_APPROX(inverse_spatial_metric,\n                        expected_inverse_spatial_metric);\n\n  const auto expected_extrinsic_curvature = gr::extrinsic_curvature(\n      expected_lapse, expected_shift, expected_d_shift, expected_spatial_metric,\n      tnsr::ii<DataType, 3, Frame>(zero), expected_d_spatial_metric);\n  CHECK_ITERABLE_APPROX(extrinsic_curvature, expected_extrinsic_curvature);\n}\n\n// Check that SpECTRE implementation matches SpEC implementation\ntemplate <typename Frame, typename DataType>\nvoid test_against_spec_impl(const DataType used_for_size) {\n  // Parameters for HarmonicSchwarzschild solution\n  const double mass = 1.03;\n  const std::array<double, 3> center{{0.2, -0.1, 0.4}};\n  const auto x = spatial_coords<Frame>(used_for_size);\n  const double t = 1.3;\n\n  // Evaluate solution\n  gr::Solutions::HarmonicSchwarzschild solution(mass, center);\n\n  // Get solution's spacetime quantities\n  const auto vars = solution.variables(\n      x, t,\n      typename gr::Solutions::HarmonicSchwarzschild::tags<DataType, Frame>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars);\n  const auto& d_lapse =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivLapse<DataType,\n                                                                    Frame>>(\n          vars);\n  const auto& shift = get<gr::Tags::Shift<DataType, 3, Frame>>(vars);\n  const auto& d_shift =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivShift<DataType,\n                                                                    Frame>>(\n          vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataType, 3, Frame>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3, Frame>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivSpatialMetric<\n          DataType, Frame>>(vars);\n\n  // Evaluate SpEC solution\n  spec::HarmonicSchwarzschild<Frame, DataType> spec_solution(mass, center);\n  spec_solution.SetCoordinates(x);\n\n  tnsr::ii<DataType, 3, Frame> spec_spatial_metric{};\n  spec_solution.LowerMetric(make_not_null(&spec_spatial_metric));\n  tnsr::ii<DataType, 3, Frame> spec_dt_spatial_metric{};\n  spec_solution.DtLowerMetric(make_not_null(&spec_dt_spatial_metric));\n  tnsr::ijj<DataType, 3, Frame> spec_d_spatial_metric{};\n  spec_solution.DerivLowerMetric(make_not_null(&spec_d_spatial_metric));\n  Scalar<DataType> spec_lapse{};\n  spec_solution.PhysicalLapse(make_not_null(&spec_lapse));\n  Scalar<DataType> spec_dt_lapse{};\n  spec_solution.DtPhysicalLapse(make_not_null(&spec_dt_lapse));\n  tnsr::i<DataType, 3, Frame> spec_d_lapse{};\n  spec_solution.DerivPhysicalLapse(make_not_null(&spec_d_lapse));\n  tnsr::I<DataType, 3, Frame> spec_shift{};\n  spec_solution.UpperShift(make_not_null(&spec_shift));\n  tnsr::I<DataType, 3, Frame> spec_dt_shift{};\n  spec_solution.DtUpperShift(make_not_null(&spec_dt_shift));\n  tnsr::iJ<DataType, 3, Frame> spec_d_shift{};\n  spec_solution.DerivUpperShift(make_not_null(&spec_d_shift));\n\n  // Check that SpECTRE implementation matches SpEC implementation\n  CHECK_ITERABLE_APPROX(spatial_metric, spec_spatial_metric);\n  CHECK_ITERABLE_APPROX(dt_spatial_metric, spec_dt_spatial_metric);\n  CHECK_ITERABLE_APPROX(d_spatial_metric, spec_d_spatial_metric);\n  CHECK_ITERABLE_APPROX(lapse, spec_lapse);\n  CHECK_ITERABLE_APPROX(dt_lapse, spec_dt_lapse);\n  CHECK_ITERABLE_APPROX(d_lapse, spec_d_lapse);\n  CHECK_ITERABLE_APPROX(shift, spec_shift);\n  CHECK_ITERABLE_APPROX(dt_shift, spec_dt_shift);\n  CHECK_ITERABLE_APPROX(d_shift, spec_d_shift);\n}\n\ntemplate <typename Frame>\nvoid test_einstein_solution() {\n  // Parameters for KerrSchild solution\n  const double mass = 1.7;\n  const std::array<double, 3> center{{0.3, 0.2, 0.4}};\n  // Setup grid\n  const std::array<double, 3> lower_bound{{0.82, 1.24, 1.32}};\n  const double time = -2.8;\n\n  gr::Solutions::HarmonicSchwarzschild solution(mass, center);\n  TestHelpers::VerifyGrSolution::verify_consistency(\n      solution, time, tnsr::I<double, 3, Frame>{lower_bound}, 0.01, 1.0e-10);\n  if constexpr (std::is_same_v<Frame, ::Frame::Inertial>) {\n    // Don't look at time-independent solution in other than the inertial\n    // frame.\n    const size_t grid_size = 8;\n    const std::array<double, 3> upper_bound{{0.8, 1.22, 1.30}};\n    TestHelpers::VerifyGrSolution::verify_time_independent_einstein_solution(\n        solution, grid_size, lower_bound, upper_bound,\n        std::numeric_limits<double>::epsilon() * 1.e5);\n  }\n}\n\n// Check that the solution satisfies the harmonic conditions:\n// eq 4.42, 4.44, and 4.45 of \\cite BaumgarteShapiro\ntemplate <typename Frame, typename DataType>\nvoid test_harmonic_conditions_satisfied(const DataType& used_for_size) {\n  // Parameters for HarmonicSchwarzschild solution\n  const double mass = 1.21;\n  const std::array<double, 3> center{{0.3, 0.1, -0.4}};\n  const auto x = spatial_coords<Frame>(used_for_size);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate solution\n  gr::Solutions::HarmonicSchwarzschild solution(mass, center);\n\n  // Get solution's spacetime quantities\n  const auto vars = solution.variables(\n      x, t,\n      typename gr::Solutions::HarmonicSchwarzschild::tags<DataType, Frame>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(vars);\n  const auto& d_lapse =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivLapse<DataType,\n                                                                    Frame>>(\n          vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataType, 3, Frame>>(vars);\n  const auto& d_shift =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivShift<DataType,\n                                                                    Frame>>(\n          vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataType, 3, Frame>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3, Frame>>(vars);\n  const auto& d_spatial_metric =\n      get<typename gr::Solutions::HarmonicSchwarzschild::DerivSpatialMetric<\n          DataType, Frame>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>>(vars);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataType, 3, Frame>>(vars);\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataType, 3, Frame>>(vars);\n\n  // Check that eq 4.42 of \\cite BaumgarteShapiro is satisfied:\n  //   \\Gamma^i = 0\n  const auto spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n  const auto da_spacetime_metric = gr::derivatives_of_spacetime_metric(\n      lapse, dt_lapse, d_lapse, shift, dt_shift, d_shift, spatial_metric,\n      dt_spatial_metric, d_spatial_metric);\n  const auto inverse_spacetime_metric =\n      determinant_and_inverse(spacetime_metric).second;\n  const auto spacetime_christoffel_second_kind = gr::christoffel_second_kind(\n      da_spacetime_metric, inverse_spacetime_metric);\n  const auto expected_contracted_spacetime_christoffel_second_kind =\n      make_with_value<tnsr::A<DataType, 3, Frame>>(used_for_size, 0.0);\n\n  CHECK_ITERABLE_APPROX(\n      tenex::evaluate<ti::A>(\n          inverse_spacetime_metric(ti::B, ti::C) *\n          spacetime_christoffel_second_kind(ti::A, ti::b, ti::c)),\n      expected_contracted_spacetime_christoffel_second_kind);\n\n  // Check that eq 4.44 of \\cite BaumgarteShapiro is satisfied:\n  //   (\\partial_t - \\beta^j \\partial_j)\\alpha = -\\alpha^2 K\n  CHECK_ITERABLE_APPROX(\n      tenex::evaluate(dt_lapse() - shift(ti::J) * d_lapse(ti::j)),\n      tenex::evaluate(-square(lapse()) * extrinsic_curvature(ti::i, ti::j) *\n                      inverse_spatial_metric(ti::I, ti::J)));\n\n  // Check that eq 4.45 of \\cite BaumgarteShapiro is satisfied:\n  //   (\\partial_t - \\beta^j \\partial_j)\\beta^i =\n  //     -\\alpha^2(\\gamma^{ij} \\partial_j ln \\alpha -\n  //     \\gamma^{jk} \\Gamma^i_{jk})\n  //\n  // Note: the textbook incorrectly has a + where there should be a - in front\n  // of the trace of the Christoffel symbols\n  const auto spatial_christoffel_second_kind =\n      gr::christoffel_second_kind(d_spatial_metric, inverse_spatial_metric);\n\n  CHECK_ITERABLE_APPROX(\n      tenex::evaluate<ti::I>(dt_shift(ti::I) -\n                             shift(ti::J) * d_shift(ti::j, ti::I)),\n      tenex::evaluate<ti::I>(\n          -square(lapse()) *\n          (inverse_spatial_metric(ti::I, ti::J) * d_lapse(ti::j) / lapse() -\n           inverse_spatial_metric(ti::J, ti::K) *\n               spatial_christoffel_second_kind(ti::I, ti::j, ti::k))));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Gr.HarmonicSchwarzschild\",\n    \"[PointwiseFunctions][Unit]\") {\n  test_copy_and_move();\n  test_serialize();\n  test_construct_from_options();\n\n  test_tag_retrieval<Frame::Inertial>(DataVector(5));\n  test_tag_retrieval<Frame::Inertial>(0.0);\n  test_tag_retrieval<Frame::Grid>(DataVector(5));\n  test_tag_retrieval<Frame::Grid>(0.0);\n\n  test_computed_quantities<Frame::Inertial>(DataVector(5));\n  test_computed_quantities<Frame::Inertial>(0.0);\n  test_computed_quantities<Frame::Grid>(DataVector(5));\n  test_computed_quantities<Frame::Grid>(0.0);\n\n  test_against_spec_impl<Frame::Inertial>(DataVector(5));\n  test_against_spec_impl<Frame::Inertial>(0.0);\n  test_against_spec_impl<Frame::Grid>(DataVector(5));\n  test_against_spec_impl<Frame::Grid>(0.0);\n\n  test_einstein_solution<Frame::Grid>();\n  test_einstein_solution<Frame::Inertial>();\n\n  test_harmonic_conditions_satisfied<Frame::Inertial>(DataVector(5));\n  test_harmonic_conditions_satisfied<Frame::Inertial>(0.0);\n  test_harmonic_conditions_satisfied<Frame::Grid>(DataVector(5));\n  test_harmonic_conditions_satisfied<Frame::Grid>(0.0);\n\n  CHECK_THROWS_WITH(\n      []() {\n        gr::Solutions::HarmonicSchwarzschild solution(-1.0, {{0.0, 0.0, 0.0}});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Mass must be non-negative\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<gr::Solutions::HarmonicSchwarzschild>(\n          \"Mass: -0.5\\n\"\n          \"Center: [1.0,3.0,2.0]\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -0.5 is below the lower bound of 0\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_KerrSchild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/SpecialRelativity/LorentzBoostMatrix.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <typename Frame, typename DataType>\ntnsr::I<DataType, 3, Frame> spatial_coords(const DataType& used_for_size) {\n  auto x = make_with_value<tnsr::I<DataType, 3, Frame>>(used_for_size, 0.0);\n  get<0>(x) = 1.32;\n  get<1>(x) = 0.82;\n  get<2>(x) = 1.24;\n  return x;\n}\n\ntemplate <typename Frame, typename DataType>\nvoid test_schwarzschild(const DataType& used_for_size) {\n  // Schwarzschild solution is (with x_i = delta_{ij} x^i):\n  // H                    = M/r\n  // l_mu                 = (1,x/r,y/r,z/r)\n  // lapse                = (1+2M/r)^{-1/2}\n  // d_i lapse            = (1+2M/r)^{-3/2}(Mx^i/r^3)\n  // shift^i              = (2Mx^i/r^2) * lapse^2\n  // g_{ij}               = delta_{ij} + 2 M x_i x_j/r^3\n  // d_i H                = -Mx_i/r^3\n  // d_i l_j              = delta_{ij}/r - x^i x^j/r^3\n  // d_k g_ij             = -6M x_i x_j x_k/r^5 + 2 M x_i delta_{kj}/r^3\n  //                                + 2 M x_j delta_{ki}/r^3\n  // Gamma_{ijk}          = M x_i(2r^2 delta_{jk} - 3 x_j x_k) / r^5\n  // Gamma^i_{jk}         = M x^i(2r^2 delta_{jk} - 3 x_j x_k) / (2Mr^4 + r^5)\n  // g^{ij} Gamma^k_{ij}  = M(8M + 3r) x^k / (r(2M+3r))^2\n  // g^{ij} K_{ij}        = 2M(3M + r) * ((2M+r)r)^(-3/2)\n\n  // Parameters for KerrSchild solution\n  const double mass = 1.01;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  const auto x = spatial_coords<Frame>(used_for_size);\n  const double t = 1.3;\n\n  // Evaluate solution\n  gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  const auto vars = solution.variables(\n      x, t, typename gr::Solutions::KerrSchild::tags<DataType, Frame>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars);\n  const auto& d_lapse =\n      get<typename gr::Solutions::KerrSchild::DerivLapse<DataType, Frame>>(\n          vars);\n  const auto& shift = get<gr::Tags::Shift<DataType, 3, Frame>>(vars);\n  const auto& d_shift =\n      get<typename gr::Solutions::KerrSchild::DerivShift<DataType, Frame>>(\n          vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataType, 3, Frame>>>(vars);\n  const auto& g = get<gr::Tags::SpatialMetric<DataType, 3, Frame>>(vars);\n  const auto& ig =\n      get<gr::Tags::InverseSpatialMetric<DataType, 3, Frame>>(vars);\n\n  const auto& dt_g =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>>(vars);\n  const auto& d_g = get<\n      typename gr::Solutions::KerrSchild::DerivSpatialMetric<DataType, Frame>>(\n      vars);\n  const auto& christoffel_first_kind =\n      get<typename gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>>(\n          vars);\n  const auto& christoffel_second_kind =\n      get<typename gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>>(\n          vars);\n  const auto& trace_christoffel = get<\n      typename gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3, Frame>>(\n      vars);\n  const auto& trace_extrinsic_curvature =\n      get<typename gr::Tags::TraceExtrinsicCurvature<DataType>>(vars);\n\n  // Check those quantities that should be zero.\n  const auto zero = make_with_value<DataType>(x, 0.);\n  CHECK(dt_lapse.get() == zero);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(dt_shift.get(i) == zero);\n    for (size_t j = 0; j < 3; ++j) {\n      CHECK(dt_g.get(i, j) == zero);\n    }\n  }\n\n  const DataType r = get(magnitude(x));\n  const DataType one_over_r_squared = 1.0 / square(r);\n  const DataType one_over_r_cubed = 1.0 / cube(r);\n  const DataType one_over_r_fifth = one_over_r_squared * one_over_r_cubed;\n  auto expected_lapse = make_with_value<Scalar<DataType>>(x, 0.0);\n  get(expected_lapse) = 1.0 / sqrt(1.0 + 2.0 * mass / r);\n  CHECK_ITERABLE_APPROX(lapse, expected_lapse);\n\n  auto expected_d_lapse = make_with_value<tnsr::i<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_d_lapse.get(i) =\n        mass * x.get(i) * one_over_r_cubed * cube(get(lapse));\n  }\n  CHECK_ITERABLE_APPROX(d_lapse, expected_d_lapse);\n\n  auto expected_shift = make_with_value<tnsr::I<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_shift.get(i) =\n        2.0 * mass * x.get(i) * one_over_r_squared * square(get(lapse));\n  }\n  CHECK_ITERABLE_APPROX(shift, expected_shift);\n\n  auto expected_d_shift = make_with_value<tnsr::iJ<DataType, 3, Frame>>(x, 0.0);\n  for (size_t j = 0; j < 3; ++j) {\n    expected_d_shift.get(j, j) =\n        2.0 * mass * one_over_r_squared * square(get(lapse));\n    for (size_t i = 0; i < 3; ++i) {\n      expected_d_shift.get(j, i) -=\n          4.0 * mass * x.get(j) * x.get(i) * square(one_over_r_squared) *\n          square(get(lapse)) * (1 - mass / r * square(get(lapse)));\n    }\n  }\n  CHECK_ITERABLE_APPROX(d_shift, expected_d_shift);\n\n  auto expected_g = make_with_value<tnsr::ii<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      expected_g.get(i, j) =\n          2.0 * mass * x.get(i) * x.get(j) * one_over_r_cubed;\n    }\n    expected_g.get(i, i) += 1.0;\n  }\n  CHECK_ITERABLE_APPROX(g, expected_g);\n  CHECK_ITERABLE_APPROX(ig, determinant_and_inverse(expected_g).second);\n\n  auto expected_d_g = make_with_value<tnsr::ijj<DataType, 3, Frame>>(x, 0.0);\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {  // Symmetry\n        expected_d_g.get(k, i, j) =\n            -6.0 * mass * x.get(i) * x.get(j) * x.get(k) * one_over_r_fifth;\n        if (k == j) {\n          expected_d_g.get(k, i, j) += 2.0 * mass * x.get(i) * one_over_r_cubed;\n        }\n        if (k == i) {\n          expected_d_g.get(k, i, j) += 2.0 * mass * x.get(j) * one_over_r_cubed;\n        }\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(d_g, expected_d_g);\n\n  auto expected_christoffel_first_kind =\n      make_with_value<tnsr::ijj<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = j; k < 3; ++k) {\n        const double delta_jk = j == k ? 1. : 0.;\n        expected_christoffel_first_kind.get(i, j, k) =\n            mass * x.get(i) *\n            (-3. * x.get(j) * x.get(k) + 2. * square(r) * delta_jk) / pow(r, 5);\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(christoffel_first_kind,\n                        expected_christoffel_first_kind);\n\n  auto expected_christoffel_second_kind =\n      make_with_value<tnsr::Ijj<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = j; k < 3; ++k) {\n        const double delta_jk = j == k ? 1. : 0.;\n        expected_christoffel_second_kind.get(i, j, k) =\n            x.get(i) * mass *\n            (-3. * x.get(j) * x.get(k) + 2. * square(r) * delta_jk) /\n            (2. * mass * pow(r, 4) + pow(r, 5));\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(christoffel_second_kind,\n                        expected_christoffel_second_kind);\n\n  auto expected_trace_christoffel =\n      make_with_value<tnsr::I<DataType, 3, Frame>>(x, 0.0);\n  DataType factor =\n      mass * (8. * mass + 3. * r) / (2. * mass + r) / (2. * mass + r) / r / r;\n  for (size_t i = 0; i < 3; ++i) {\n    expected_trace_christoffel.get(i) = factor * x.get(i);\n  }\n  CHECK_ITERABLE_APPROX(trace_christoffel, expected_trace_christoffel);\n\n  auto expected_trace_extrinsic_curvature =\n      make_with_value<Scalar<DataType>>(x, 0.0);\n  expected_trace_extrinsic_curvature.get() =\n      2. * mass * (3. * mass + r) * pow((2. * mass + r) * r, -1.5);\n  CHECK_ITERABLE_APPROX(trace_extrinsic_curvature,\n                        expected_trace_extrinsic_curvature);\n}\n\ntemplate <typename FrameType>\nvoid test_numerical_deriv_det_spatial_metric(const DataVector& used_for_size) {\n  // Parameters for KerrSchild solution\n  const double mass = 1.01;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  // Setup grid\n  const size_t num_points_1d = 8;\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n  const size_t SpatialDim = 3;\n  Mesh<SpatialDim> mesh{num_points_1d, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, FrameType>(Affine3D{\n          Affine{-1., 1., lower_bound[0], upper_bound[0]},\n          Affine{-1., 1., lower_bound[1], upper_bound[1]},\n          Affine{-1., 1., lower_bound[2], upper_bound[2]},\n      });\n  const size_t num_points_3d = num_points_1d * num_points_1d * num_points_1d;\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Compute actual analytical derivative of the determinant\n  const auto deriv_det_spatial_metric =\n      get<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          solution.variables(\n              x, t,\n              tmpl::list<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim,\n                                                         FrameType>>{}));\n\n  // Compute expected numerical derivative of the determinant\n  const double null_vector_0 = -1.0;\n  gr::Solutions::KerrSchild::IntermediateComputer<DataVector, FrameType>\n      ks_computer(solution, x);\n  gr::Solutions::KerrSchild::IntermediateVars<DataVector, FrameType> ks_cache(\n      num_points_3d);\n\n  auto H = make_with_value<Scalar<DataVector>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n  using H_tag = gr::Solutions::KerrSchild::internal_tags::H<DataVector>;\n  ks_computer(make_not_null(&H), make_not_null(&ks_cache), H_tag{});\n\n  const auto expected_deriv_H =\n      partial_derivative(H, mesh, coord_map.inv_jacobian(x_logical));\n\n  tnsr::i<DataVector, SpatialDim, FrameType>\n      expected_deriv_det_spatial_metric{};\n  for (size_t i = 0; i < SpatialDim; i++) {\n    expected_deriv_det_spatial_metric.get(i) =\n        2.0 * square(null_vector_0) * expected_deriv_H.get(i);\n  }\n\n  // A custom epsilon is used here because the Legendre polynomials don't fit\n  // the derivative of 1 / r well. This was looked at for various box sizes and\n  // number of 1D grid points.\n  Approx approx = Approx::custom().epsilon(1e-11).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(deriv_det_spatial_metric,\n                               expected_deriv_det_spatial_metric, approx);\n}\n\ntemplate <typename Frame, typename DataType>\nvoid test_tag_retrieval(const DataType& used_for_size) {\n  // Parameters for KerrSchild solution\n  const double mass = 1.234;\n  const std::array<double, 3> spin{{0.1, -0.2, 0.3}};\n  const std::array<double, 3> center{{1.0, 2.0, 3.0}};\n  const std::array<double, 3> boost_velocity{{0.2, -0.3, 0.1}};\n  const auto x = spatial_coords<Frame>(used_for_size);\n  const double t = 1.3;\n\n  using all_tags = typename gr::Solutions::KerrSchild::tags<DataType, Frame>;\n\n  // We test gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame>\n  // separately as it is not supported for the boosted solution.\n  using tags_for_zero_boost =\n      tmpl::list<gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame>>;\n  using regular_tags = tmpl::remove<\n      all_tags, gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame>>;\n\n  // Evaluate solution\n  const gr::Solutions::KerrSchild solution(mass, spin, center, boost_velocity);\n  TestHelpers::AnalyticSolutions::test_tag_retrieval(solution, x, t,\n                                                     regular_tags{});\n\n  const gr::Solutions::KerrSchild solution_zero_boost(mass, spin, center);\n  TestHelpers::AnalyticSolutions::test_tag_retrieval(solution_zero_boost, x, t,\n                                                     tags_for_zero_boost{});\n}\n\ntemplate <typename Frame>\nvoid test_einstein_solution(const bool use_non_zero_velocity) {\n  INFO(\"Verify KerrSchild solution satisfies Einstein equations\");\n  // Parameters\n  //   ...for KerrSchild solution\n  const double mass = 1.7;\n  const std::array<double, 3> spin{{0.1, 0.2, 0.3}};\n  const std::array<double, 3> center{{0.3, 0.2, 0.4}};\n   const std::array<double, 3> boost_velocity{{0.4, -0.51, -0.5}};\n  //   ...for grid\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n  const double time = -2.8;\n\n  if (use_non_zero_velocity) {\n    gr::Solutions::KerrSchild solution(mass, spin, center, boost_velocity);\n    // Time derivatives are non zero. However we do not code up the time\n    // dependence of the boosted solution for simplicity. Hence we only check\n    // consistency in the spatial derivatives.\n    TestHelpers::VerifyGrSolution::verify_spatial_consistency(\n        solution, time, tnsr::I<double, 3, Frame>{lower_bound}, 0.01, 1.0e-10);\n  } else {\n    gr::Solutions::KerrSchild solution(mass, spin, center);\n    TestHelpers::VerifyGrSolution::verify_consistency(\n        solution, time, tnsr::I<double, 3, Frame>{lower_bound}, 0.01, 1.0e-10);\n    if constexpr (std::is_same_v<Frame, ::Frame::Inertial>) {\n      // Don't look at time-independent solution in other than the inertial\n      // frame.\n      const size_t grid_size = 8;\n      const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n      TestHelpers::VerifyGrSolution::verify_time_independent_einstein_solution(\n          solution, grid_size, lower_bound, upper_bound,\n          std::numeric_limits<double>::epsilon() * 1.e5);\n    }\n  }\n\n}\n\ntemplate <typename Frame, typename DataType>\nvoid test_zero_spin_optimization(const DataType& used_for_size) {\n  // the optimizations are only taken when the spin is exactly zero, so we can\n  // test it against a numerical epsilon that should not affect results\n  gr::Solutions::KerrSchild solution_zero_spin(3.0, {{0., 0., 0.}},\n                                               {{0.2, 0.3, 0.2}});\n  gr::Solutions::KerrSchild solution_tiny_spin(3.0, {{0., 0., 1e-50}},\n                                               {{0.2, 0.3, 0.2}});\n  CHECK(solution_zero_spin.zero_spin());\n  CHECK(not solution_tiny_spin.zero_spin());\n  const auto x = spatial_coords<Frame>(used_for_size);\n  using all_tags = typename gr::Solutions::KerrSchild::tags<DataType, Frame>;\n  const auto all_tags_zero_spin =\n      solution_zero_spin.variables(x, 0., all_tags{});\n  const auto all_tags_tiny_spin =\n      solution_tiny_spin.variables(x, 0., all_tags{});\n  tmpl::for_each<all_tags>(\n      [&all_tags_zero_spin, &all_tags_tiny_spin](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<tag>(all_tags_zero_spin),\n                              get<tag>(all_tags_tiny_spin));\n      });\n}\n\ntemplate <typename Frame, typename DataType>\nvoid test_boosted_for_zero_velocity(const DataType& used_for_size) {\n  const double epsilon = std::numeric_limits<double>::epsilon();\n  const std::array<double, 3> boost_velocity{{0.0, 0.0, epsilon}};\n\n  gr::Solutions::KerrSchild solution_zero_velocity(\n      3.0, {{0., 0., 0.}}, {{0.2, 0.3, 0.2}});\n  gr::Solutions::KerrSchild solution_tiny_velocity(\n      3.0, {{0., 0., 1e-50}}, {{0.2, 0.3, 0.2}}, boost_velocity);\n  CHECK(solution_zero_velocity.zero_velocity());\n  CHECK(not solution_tiny_velocity.zero_velocity());\n  const auto x = spatial_coords<Frame>(used_for_size);\n\n  // We do not test gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame>\n  // as it is not supported for the boosted solution.\n  using all_tags = typename gr::Solutions::KerrSchild::tags<DataType, Frame>;\n  using tags_to_test = tmpl::remove<\n      all_tags, gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame>>;\n\n  const auto all_tags_zero_velocity =\n      solution_zero_velocity.variables(x, 0., tags_to_test{});\n  const auto all_tags_tiny_velocity =\n      solution_tiny_velocity.variables(x, 0., tags_to_test{});\n  tmpl::for_each<tags_to_test>(\n      [&all_tags_zero_velocity, &all_tags_tiny_velocity](auto tag_v) {\n        using tag = tmpl::type_from<decltype(tag_v)>;\n        CHECK_ITERABLE_APPROX(get<tag>(all_tags_zero_velocity),\n                              get<tag>(all_tags_tiny_velocity));\n      });\n}\n\ntemplate <typename Frame, typename DataType>\nvoid test_boosted_spacetime_metric(const DataType& used_for_size) {\n  // Test that the spacetime metric of the boosted solution indeed\n  // corresponds to the boosted spacetime metric tensor\n\n  const auto x = spatial_coords<Frame>(used_for_size);\n  const double t = 0.0;\n\n  // KerrSchild solution\n  const double mass = 1.0;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  // Boosted KerrSchild solution\n  const std::array<double, 3> velocity{{0.5, 0.0, 0.0}};\n  gr::Solutions::KerrSchild boosted_solution(mass, spin, center, velocity);\n\n  // Need to evaluate at the boosted coordinates\n  // Boost coordinates\n  tnsr::I<DataType, 3, Frame> x_boosted;\n  sr::lorentz_boost(make_not_null(&x_boosted), x, 0., -velocity);\n\n  // We do not test gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame>\n  // as it is not supported for the boosted solution.\n  using all_tags = typename gr::Solutions::KerrSchild::tags<DataType, Frame>;\n  using tags_to_test = tmpl::remove<\n      all_tags, gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame>>;\n\n  const auto vars = solution.variables(x_boosted, t, tags_to_test{});\n  const auto& lapse = get<typename gr::Tags::Lapse<DataType>>(vars);\n  const auto& shift = get<typename gr::Tags::Shift<DataType, 3, Frame>>(vars);\n  const auto& spatial_metric =\n      get<typename gr::Tags::SpatialMetric<DataType, 3, Frame>>(vars);\n\n  // Compute spacetime metric\n  tnsr::aa<DataType, 3, Frame> spacetime_metric;\n  gr::spacetime_metric(make_not_null(&spacetime_metric), lapse, shift,\n                       spatial_metric);\n\n  auto spacetime_metric_as_asymmetric_tensor =\n      make_with_value<tnsr::ab<DataType, 3, Frame>>(x, 0.0);\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b < 4; ++b) {\n      spacetime_metric_as_asymmetric_tensor.get(a, b) =\n          spacetime_metric.get(a, b);\n    }\n  }\n\n  tnsr::ab<DataType, 3, Frame> expected_boosted_spacetime_metric;\n  sr::lorentz_boost(make_not_null(&expected_boosted_spacetime_metric),\n                    spacetime_metric_as_asymmetric_tensor, -velocity);\n\n  // Boosted metric quantities\n  const auto boosted_vars = boosted_solution.variables(x, t, tags_to_test{});\n  const auto& lapse_from_boosted_solution =\n      get<typename gr::Tags::Lapse<DataType>>(boosted_vars);\n  const auto& shift_from_boosted_solution =\n      get<typename gr::Tags::Shift<DataType, 3, Frame>>(boosted_vars);\n  const auto& spatial_metric_from_boosted_solution =\n      get<typename gr::Tags::SpatialMetric<DataType, 3, Frame>>(boosted_vars);\n\n  tnsr::aa<DataType, 3, Frame> spacetime_metric_from_boosted_solution;\n  gr::spacetime_metric(make_not_null(&spacetime_metric_from_boosted_solution),\n                       lapse_from_boosted_solution, shift_from_boosted_solution,\n                       spatial_metric_from_boosted_solution);\n\n  auto spacetime_metric_from_boosted_solution_as_asymmetric_tensor =\n      make_with_value<tnsr::ab<DataType, 3, Frame>>(x, 0.0);\n  for (size_t a = 0; a < 4; ++a) {\n    for (size_t b = 0; b < 4; ++b) {\n      spacetime_metric_from_boosted_solution_as_asymmetric_tensor.get(a, b) =\n          spacetime_metric_from_boosted_solution.get(a, b);\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      expected_boosted_spacetime_metric,\n      spacetime_metric_from_boosted_solution_as_asymmetric_tensor);\n}\n\nvoid test_serialize() {\n  gr::Solutions::KerrSchild solution(3.0, {{0.2, 0.3, 0.2}}, {{0.0, 3.0, 4.0}},\n                                     {{0.1, 0.2, 0.3}});\n  test_serialization(solution);\n}\n\nvoid test_copy_and_move() {\n  gr::Solutions::KerrSchild solution(3.0, {{0.2, 0.3, 0.2}}, {{0.0, 3.0, 4.0}},\n                                     {{0.1, 0.2, 0.3}});\n  test_copy_semantics(solution);\n  auto solution_copy = solution;\n  // clang-tidy: std::move of trivially copyable type\n  test_move_semantics(std::move(solution), solution_copy);  // NOLINT\n}\n\nvoid test_construct_from_options() {\n  const auto created = TestHelpers::test_creation<gr::Solutions::KerrSchild>(\n      \"Mass: 0.5\\n\"\n      \"Spin: [0.1,0.2,0.3]\\n\"\n      \"Center: [1.0,3.0,2.0]\\n\"\n      \"Velocity: [0.5,0.4,0.3]\");\n  CHECK(created == gr::Solutions::KerrSchild(0.5, {{0.1, 0.2, 0.3}},\n                                             {{1.0, 3.0, 2.0}},\n                                             {{0.5, 0.4, 0.3}}));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Gr.KerrSchild\",\n                  \"[PointwiseFunctions][Unit]\") {\n  test_copy_and_move();\n  test_serialize();\n  test_construct_from_options();\n\n  test_schwarzschild<Frame::Inertial>(DataVector(5));\n  test_schwarzschild<Frame::Inertial>(0.0);\n  test_numerical_deriv_det_spatial_metric<Frame::Inertial>(DataVector(5));\n  test_tag_retrieval<Frame::Inertial>(DataVector(5));\n  test_tag_retrieval<Frame::Inertial>(0.0);\n  test_einstein_solution<Frame::Inertial>(true);\n  test_einstein_solution<Frame::Inertial>(false);\n  test_zero_spin_optimization<Frame::Inertial>(DataVector(5));\n  test_zero_spin_optimization<Frame::Inertial>(0.0);\n  test_boosted_for_zero_velocity<Frame::Inertial>(DataVector(5));\n  test_boosted_for_zero_velocity<Frame::Inertial>(0.0);\n  test_boosted_spacetime_metric<Frame::Inertial, double>(0.0);\n\n  test_schwarzschild<Frame::Grid>(DataVector(5));\n  test_schwarzschild<Frame::Grid>(0.0);\n  test_numerical_deriv_det_spatial_metric<Frame::Grid>(DataVector(5));\n  test_tag_retrieval<Frame::Grid>(DataVector(5));\n  test_tag_retrieval<Frame::Grid>(0.0);\n  test_einstein_solution<Frame::Grid>(true);\n  test_einstein_solution<Frame::Grid>(false);\n  test_zero_spin_optimization<Frame::Grid>(DataVector(5));\n  test_zero_spin_optimization<Frame::Grid>(0.0);\n  test_boosted_for_zero_velocity<Frame::Grid>(DataVector(5));\n  test_boosted_for_zero_velocity<Frame::Grid>(0.0);\n  test_boosted_spacetime_metric<Frame::Grid, double>(0.0);\n\n  CHECK_THROWS_WITH(\n      []() {\n        gr::Solutions::KerrSchild solution(1.0, {{1.0, 1.0, 1.0}},\n                                           {{0.0, 0.0, 0.0}});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Spin magnitude must be < 1\"));\n  CHECK_THROWS_WITH(\n      []() {\n        gr::Solutions::KerrSchild solution(-1.0, {{0.0, 0.0, 0.0}},\n                                           {{0.0, 0.0, 0.0}});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Mass must be non-negative\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<gr::Solutions::KerrSchild>(\n                        \"Mass: -0.5\\n\"\n                        \"Spin: [0.1,0.2,0.3]\\n\"\n                        \"Center: [1.0,3.0,2.0]\\n\"\n                        \"Velocity: [0,0,0]\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.5 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<gr::Solutions::KerrSchild>(\n          \"Mass: 0.5\\n\"\n          \"Spin: [1.1,0.9,0.3]\\n\"\n          \"Center: [1.0,3.0,2.0]\\n\"\n          \"Velocity: [0,0,0]\"),\n      Catch::Matchers::ContainsSubstring(\"Spin magnitude must be < 1\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_Minkowski.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename T>\nvoid test_minkowski(const T& value) {\n  gr::Solutions::Minkowski<Dim> minkowski{};\n\n  const tnsr::I<T, Dim> x{value};\n  const double t = 1.2;\n\n  const auto one = make_with_value<T>(value, 1.);\n  const auto zero = make_with_value<T>(value, 0.);\n\n  const auto lapse = get<gr::Tags::Lapse<T>>(\n      minkowski.variables(x, t, tmpl::list<gr::Tags::Lapse<T>>{}));\n  const auto dt_lapse = get<Tags::dt<gr::Tags::Lapse<T>>>(\n      minkowski.variables(x, t, tmpl::list<Tags::dt<gr::Tags::Lapse<T>>>{}));\n  const auto d_lapse =\n      get<Tags::deriv<gr::Tags::Lapse<T>, tmpl::size_t<Dim>, Frame::Inertial>>(\n          minkowski.variables(\n              x, t,\n              tmpl::list<Tags::deriv<gr::Tags::Lapse<T>, tmpl::size_t<Dim>,\n                                     Frame::Inertial>>{}));\n  const auto shift = get<gr::Tags::Shift<T, Dim>>(\n      minkowski.variables(x, t, tmpl::list<gr::Tags::Shift<T, Dim>>{}));\n  const auto dt_shift =\n      get<Tags::dt<gr::Tags::Shift<T, Dim>>>(minkowski.variables(\n          x, t, tmpl::list<Tags::dt<gr::Tags::Shift<T, Dim>>>{}));\n  const auto d_shift = get<\n      Tags::deriv<gr::Tags::Shift<T, Dim>, tmpl::size_t<Dim>, Frame::Inertial>>(\n      minkowski.variables(\n          x, t,\n          tmpl::list<Tags::deriv<gr::Tags::Shift<T, Dim>, tmpl::size_t<Dim>,\n                                 Frame::Inertial>>{}));\n  const auto g = get<gr::Tags::SpatialMetric<T, Dim>>(\n      minkowski.variables(x, t, tmpl::list<gr::Tags::SpatialMetric<T, Dim>>{}));\n  const auto dt_g =\n      get<Tags::dt<gr::Tags::SpatialMetric<T, Dim>>>(minkowski.variables(\n          x, t, tmpl::list<Tags::dt<gr::Tags::SpatialMetric<T, Dim>>>{}));\n  const auto d_g = get<Tags::deriv<gr::Tags::SpatialMetric<T, Dim>,\n                                   tmpl::size_t<Dim>, Frame::Inertial>>(\n      minkowski.variables(\n          x, t,\n          tmpl::list<Tags::deriv<gr::Tags::SpatialMetric<T, Dim>,\n                                 tmpl::size_t<Dim>, Frame::Inertial>>{}));\n  const auto inv_g =\n      get<gr::Tags::InverseSpatialMetric<T, Dim>>(minkowski.variables(\n          x, t, tmpl::list<gr::Tags::InverseSpatialMetric<T, Dim>>{}));\n  const auto extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<T, Dim>>(minkowski.variables(\n          x, t, tmpl::list<gr::Tags::ExtrinsicCurvature<T, Dim>>{}));\n  const auto det_g = get<gr::Tags::SqrtDetSpatialMetric<T>>(minkowski.variables(\n      x, t, tmpl::list<gr::Tags::SqrtDetSpatialMetric<T>>{}));\n  const auto dt_det_g =\n      get<Tags::dt<gr::Tags::SqrtDetSpatialMetric<T>>>(minkowski.variables(\n          x, t, tmpl::list<Tags::dt<gr::Tags::SqrtDetSpatialMetric<T>>>{}));\n  const auto d_det_g =\n      get<gr::Tags::DerivDetSpatialMetric<T, Dim>>(minkowski.variables(\n          x, t, tmpl::list<gr::Tags::DerivDetSpatialMetric<T, Dim>>{}));\n  const auto christoffel_first_kind =\n      get<gr::Tags::SpatialChristoffelFirstKind<T, Dim>>(minkowski.variables(\n          x, t, tmpl::list<gr::Tags::SpatialChristoffelFirstKind<T, Dim>>{}));\n  const auto christoffel_second_kind =\n      get<gr::Tags::SpatialChristoffelSecondKind<T, Dim>>(minkowski.variables(\n          x, t, tmpl::list<gr::Tags::SpatialChristoffelSecondKind<T, Dim>>{}));\n  const auto trace_christoffel = get<\n      gr::Tags::TraceSpatialChristoffelSecondKind<T, Dim>>(minkowski.variables(\n      x, t, tmpl::list<gr::Tags::TraceSpatialChristoffelSecondKind<T, Dim>>{}));\n  const auto trace_extrinsic_curvature =\n      get<gr::Tags::TraceExtrinsicCurvature<T>>(minkowski.variables(\n          x, t, tmpl::list<gr::Tags::TraceExtrinsicCurvature<T>>{}));\n\n  CHECK(lapse.get() == one);\n  CHECK(dt_lapse.get() == zero);\n  CHECK(det_g.get() == one);\n  CHECK(dt_det_g.get() == zero);\n  CHECK(trace_extrinsic_curvature.get() == zero);\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(shift.get(i) == zero);\n    CHECK(dt_shift.get(i) == zero);\n    CHECK(d_lapse.get(i) == zero);\n    CHECK(g.get(i, i) == one);\n    CHECK(inv_g.get(i, i) == one);\n    CHECK(d_det_g.get(i) == zero);\n    CHECK(trace_christoffel.get(i) == zero);\n    for (size_t j = 0; j < i; ++j) {\n      CHECK(g.get(i, j) == zero);\n      CHECK(inv_g.get(i, j) == zero);\n    }\n    for (size_t j = 0; j < Dim; ++j) {\n      CHECK(d_shift.get(i, j) == zero);\n      CHECK(dt_g.get(i, j) == zero);\n      CHECK(extrinsic_curvature.get(i, j) == zero);\n      for (size_t k = 0; k < Dim; ++k) {\n        CHECK(christoffel_first_kind.get(i, j, k) == zero);\n        CHECK(christoffel_second_kind.get(i, j, k) == zero);\n        CHECK(d_g.get(i, j, k) == zero);\n      }\n    }\n  }\n  test_serialization(minkowski);\n  // test operator !=\n  CHECK_FALSE(minkowski != minkowski);\n\n  TestHelpers::AnalyticSolutions::test_tag_retrieval(\n      minkowski, x, t,\n      typename gr::Solutions::Minkowski<Dim>::template tags<T>{});\n}\n\nvoid test_einstein_solution() {\n  gr::Solutions::Minkowski<3> solution{};\n  TestHelpers::VerifyGrSolution::verify_consistency(\n      solution, 1.234, tnsr::I<double, 3>{{{1.2, 2.3, 3.4}}}, 0.01, 1.0e-10);\n  TestHelpers::VerifyGrSolution::verify_time_independent_einstein_solution(\n      solution, 8, {{1.2, 2.3, 3.4}}, {{1.22, 2.32, 3.42}}, 1.0e-10);\n}\n\ntemplate <size_t Dim>\nvoid test_option_creation() {\n  TestHelpers::test_creation<gr::Solutions::Minkowski<Dim>>(\"\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Gr.Minkowski\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const double x = 1.2;\n  const DataVector x_dv{1., 2., 3., 4., 5.};\n\n  test_minkowski<1>(x);\n  test_minkowski<1>(x_dv);\n  test_minkowski<2>(x);\n  test_minkowski<2>(x_dv);\n  test_minkowski<3>(x);\n  test_minkowski<3>(x_dv);\n\n  test_einstein_solution();\n\n  test_option_creation<1>();\n  test_option_creation<2>();\n  test_option_creation<3>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_SphericalKerrSchild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <typename Frame, typename DataType>\ntnsr::I<DataType, 3, Frame> spatial_coords(const DataType& used_for_size) {\n  auto x = make_with_value<tnsr::I<DataType, 3, Frame>>(used_for_size, 0.0);\n  get<0>(x) = 1.32;\n  get<1>(x) = 0.82;\n  get<2>(x) = 1.24;\n  return x;\n}\n\ntemplate <typename Frame, typename DataType>\nvoid test_schwarzschild(const DataType& used_for_size) {\n  // Schwarzschild solution is (with x_i = delta_{ij} x^i):\n  // H                    = M/r\n  // l_mu                 = (1,x/r,y/r,z/r)\n  // lapse                = (1+2M/r)^{-1/2}\n  // d_i lapse            = (1+2M/r)^{-3/2}(Mx^i/r^3)\n  // shift^i              = (2Mx^i/r^2) / lapse^2\n  // g_{ij}               = delta_{ij} + 2 M x_i x_j/r^3\n  // d_i H                = -Mx_i/r^3\n  // d_i l_j              = delta_{ij}/r - x^i x^j/r^3\n  // d_k g_ij             = -6M x_i x_j x_k/r^5 + 2 M x_i delta_{kj}/r^3\n  //                                + 2 M x_j delta_{ki}/r^3\n  // Gamma_{ijk}          = M x_i(2r^2 delta_{jk} - 3 x_j x_k) / r^5\n  // Gamma^i_{jk}         = M x^i(2r^2 delta_{jk} - 3 x_j x_k) / (2Mr^4 + r^5)\n  // g^{ij} Gamma^k_{ij}  = M(8M + 3r) x^k / (r(2M+3r))^2\n  // g^{ij} K_{ij}        = 2M(3M + r) * ((2M+r)r)^(-3/2)\n\n  // Parameters for KerrSchild solution\n  const double mass = 1.01;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  const auto x = spatial_coords<Frame>(used_for_size);\n  const double t = 1.3;\n\n  // Evaluate solution\n  gr::Solutions::SphericalKerrSchild solution(mass, spin, center);\n\n  const auto vars = solution.variables(\n      x, t,\n      typename gr::Solutions::SphericalKerrSchild::tags<DataType, Frame>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars);\n  const auto& d_lapse = get<\n      typename gr::Solutions::SphericalKerrSchild::DerivLapse<DataType, Frame>>(\n      vars);\n  const auto& shift = get<gr::Tags::Shift<DataType, 3, Frame>>(vars);\n  const auto& d_shift = get<\n      typename gr::Solutions::SphericalKerrSchild::DerivShift<DataType, Frame>>(\n      vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataType, 3, Frame>>>(vars);\n  const auto& g = get<gr::Tags::SpatialMetric<DataType, 3, Frame>>(vars);\n  const auto& ig =\n      get<gr::Tags::InverseSpatialMetric<DataType, 3, Frame>>(vars);\n\n  const auto& dt_g =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame>>>(vars);\n  const auto& d_g =\n      get<typename gr::Solutions::SphericalKerrSchild::DerivSpatialMetric<\n          DataType, Frame>>(vars);\n  const auto& christoffel_first_kind =\n      get<typename gr::Tags::SpatialChristoffelFirstKind<DataType, 3, Frame>>(\n          vars);\n  const auto& christoffel_second_kind =\n      get<typename gr::Tags::SpatialChristoffelSecondKind<DataType, 3, Frame>>(\n          vars);\n  const auto& trace_christoffel = get<\n      typename gr::Tags::TraceSpatialChristoffelSecondKind<DataType, 3, Frame>>(\n      vars);\n  const auto& trace_extrinsic_curvature =\n      get<typename gr::Tags::TraceExtrinsicCurvature<DataType>>(vars);\n\n  // Check those quantities that should be zero.\n  const auto zero = make_with_value<DataType>(x, 0.);\n  CHECK(dt_lapse.get() == zero);\n  for (size_t i = 0; i < 3; ++i) {\n    CHECK(dt_shift.get(i) == zero);\n    for (size_t j = 0; j < 3; ++j) {\n      CHECK(dt_g.get(i, j) == zero);\n    }\n  }\n\n  const DataType r = get(magnitude(x));\n  const DataType one_over_r_squared = 1.0 / square(r);\n  const DataType one_over_r_cubed = 1.0 / cube(r);\n  const DataType one_over_r_fifth = one_over_r_squared * one_over_r_cubed;\n  auto expected_lapse = make_with_value<Scalar<DataType>>(x, 0.0);\n  get(expected_lapse) = 1.0 / sqrt(1.0 + 2.0 * mass / r);\n  CHECK_ITERABLE_APPROX(lapse, expected_lapse);\n\n  auto expected_d_lapse = make_with_value<tnsr::i<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_d_lapse.get(i) =\n        mass * x.get(i) * one_over_r_cubed * cube(get(lapse));\n  }\n  CHECK_ITERABLE_APPROX(d_lapse, expected_d_lapse);\n\n  auto expected_shift = make_with_value<tnsr::I<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_shift.get(i) =\n        2.0 * mass * x.get(i) * one_over_r_squared * square(get(lapse));\n  }\n  CHECK_ITERABLE_APPROX(shift, expected_shift);\n\n  auto expected_d_shift = make_with_value<tnsr::iJ<DataType, 3, Frame>>(x, 0.0);\n  for (size_t j = 0; j < 3; ++j) {\n    expected_d_shift.get(j, j) =\n        2.0 * mass * one_over_r_squared * square(get(lapse));\n    for (size_t i = 0; i < 3; ++i) {\n      expected_d_shift.get(j, i) -=\n          4.0 * mass * x.get(j) * x.get(i) * square(one_over_r_squared) *\n          square(get(lapse)) * (1 - mass / r * square(get(lapse)));\n    }\n  }\n  CHECK_ITERABLE_APPROX(d_shift, expected_d_shift);\n\n  auto expected_g = make_with_value<tnsr::ii<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      expected_g.get(i, j) =\n          2.0 * mass * x.get(i) * x.get(j) * one_over_r_cubed;\n    }\n    expected_g.get(i, i) += 1.0;\n  }\n  CHECK_ITERABLE_APPROX(g, expected_g);\n  CHECK_ITERABLE_APPROX(ig, determinant_and_inverse(expected_g).second);\n\n  auto expected_d_g = make_with_value<tnsr::ijj<DataType, 3, Frame>>(x, 0.0);\n  for (size_t k = 0; k < 3; ++k) {\n    for (size_t i = 0; i < 3; ++i) {\n      for (size_t j = i; j < 3; ++j) {  // Symmetry\n        expected_d_g.get(k, i, j) =\n            -6.0 * mass * x.get(i) * x.get(j) * x.get(k) * one_over_r_fifth;\n        if (k == j) {\n          expected_d_g.get(k, i, j) += 2.0 * mass * x.get(i) * one_over_r_cubed;\n        }\n        if (k == i) {\n          expected_d_g.get(k, i, j) += 2.0 * mass * x.get(j) * one_over_r_cubed;\n        }\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(d_g, expected_d_g);\n\n  auto expected_christoffel_first_kind =\n      make_with_value<tnsr::ijj<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = j; k < 3; ++k) {\n        const double delta_jk = j == k ? 1. : 0.;\n        expected_christoffel_first_kind.get(i, j, k) =\n            mass * x.get(i) *\n            (-3. * x.get(j) * x.get(k) + 2. * square(r) * delta_jk) / pow(r, 5);\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(christoffel_first_kind,\n                        expected_christoffel_first_kind);\n\n  auto expected_christoffel_second_kind =\n      make_with_value<tnsr::Ijj<DataType, 3, Frame>>(x, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = 0; j < 3; ++j) {\n      for (size_t k = j; k < 3; ++k) {\n        const double delta_jk = j == k ? 1. : 0.;\n        expected_christoffel_second_kind.get(i, j, k) =\n            x.get(i) * mass *\n            (-3. * x.get(j) * x.get(k) + 2. * square(r) * delta_jk) /\n            (2. * mass * pow(r, 4) + pow(r, 5));\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(christoffel_second_kind,\n                        expected_christoffel_second_kind);\n\n  auto expected_trace_christoffel =\n      make_with_value<tnsr::I<DataType, 3, Frame>>(x, 0.0);\n  DataType factor =\n      mass * (8. * mass + 3. * r) / (2. * mass + r) / (2. * mass + r) / r / r;\n  for (size_t i = 0; i < 3; ++i) {\n    expected_trace_christoffel.get(i) = factor * x.get(i);\n  }\n  CHECK_ITERABLE_APPROX(trace_christoffel, expected_trace_christoffel);\n\n  auto expected_trace_extrinsic_curvature =\n      make_with_value<Scalar<DataType>>(x, 0.0);\n  expected_trace_extrinsic_curvature.get() =\n      2. * mass * (3. * mass + r) * pow((2. * mass + r) * r, -1.5);\n  CHECK_ITERABLE_APPROX(trace_extrinsic_curvature,\n                        expected_trace_extrinsic_curvature);\n}\n\ntemplate <typename FrameType>\nvoid test_numerical_deriv_det_spatial_metric(const DataVector& used_for_size) {\n  // Parameters for SphericalKerrSchild solution\n  const double mass = 1.01;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  gr::Solutions::SphericalKerrSchild solution(mass, spin, center);\n\n  // Setup grid\n  const size_t num_points_1d = 8;\n  const std::array<double, 3> lower_bound{{0.82, 1.24, 1.32}};\n  const std::array<double, 3> upper_bound{{0.8, 1.22, 1.30}};\n  const size_t SpatialDim = 3;\n  Mesh<SpatialDim> mesh{num_points_1d, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, FrameType>(Affine3D{\n          Affine{-1., 1., lower_bound[0], upper_bound[0]},\n          Affine{-1., 1., lower_bound[1], upper_bound[1]},\n          Affine{-1., 1., lower_bound[2], upper_bound[2]},\n      });\n  const size_t num_points_3d = num_points_1d * num_points_1d * num_points_1d;\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Compute actual analytical derivative of the determinant\n  const auto deriv_det_spatial_metric =\n      get<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim, FrameType>>(\n          solution.variables(\n              x, t,\n              tmpl::list<gr::Tags::DerivDetSpatialMetric<DataVector, SpatialDim,\n                                                         FrameType>>{}));\n\n  // Compute expected numerical derivative of the determinant\n  const double null_vector_0 = -1.0;\n  gr::Solutions::SphericalKerrSchild::IntermediateComputer<DataVector,\n                                                           FrameType>\n      sks_computer(solution, x);\n  gr::Solutions::SphericalKerrSchild::IntermediateVars<DataVector, FrameType>\n      sks_cache(num_points_3d);\n\n  auto H = make_with_value<Scalar<DataVector>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n  using H_tag =\n      gr::Solutions::SphericalKerrSchild::internal_tags::H<DataVector>;\n  sks_computer(make_not_null(&H), make_not_null(&sks_cache), H_tag{});\n\n  const auto expected_deriv_H =\n      partial_derivative(H, mesh, coord_map.inv_jacobian(x_logical));\n\n  tnsr::i<DataVector, SpatialDim, FrameType>\n      expected_deriv_det_spatial_metric{};\n  for (size_t i = 0; i < SpatialDim; i++) {\n    expected_deriv_det_spatial_metric.get(i) =\n        2.0 * square(null_vector_0) * expected_deriv_H.get(i);\n  }\n\n  // A custom epsilon is used here because the Legendre polynomials don't fit\n  // the derivative of 1 / r well. This was looked at for various box sizes and\n  // number of 1D grid points.\n  Approx approx = Approx::custom().epsilon(1e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(deriv_det_spatial_metric,\n                               expected_deriv_det_spatial_metric, approx);\n}\n\ntemplate <typename Frame, typename DataType>\nvoid test_tag_retrieval(const DataType& used_for_size) {\n  // Parameters for SphericalKerrSchild solution\n  const double mass = 1.234;\n  const std::array<double, 3> spin{{0.1, -0.2, 0.3}};\n  const std::array<double, 3> center{{1.0, 2.0, 3.0}};\n  const auto x = spatial_coords<Frame>(used_for_size);\n  const double t = 1.3;\n\n  // Evaluate solution\n  const gr::Solutions::SphericalKerrSchild solution(mass, spin, center);\n  TestHelpers::AnalyticSolutions::test_tag_retrieval(\n      solution, x, t,\n      typename gr::Solutions::SphericalKerrSchild::template tags<DataType,\n                                                                 Frame>{});\n}\n\ntemplate <typename Frame>\nvoid test_einstein_solution() {\n  // Parameters\n  //   ...for SphericalKerrSchild solution\n  const double mass = 1.7;\n  const std::array<double, 3> spin{{0.1, 0.2, 0.3}};\n  const std::array<double, 3> center{{0.3, 0.2, 0.4}};\n  //   ...for grid\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n  const double time = -2.8;\n\n  gr::Solutions::SphericalKerrSchild solution(mass, spin, center);\n  TestHelpers::VerifyGrSolution::verify_consistency(\n      solution, time, tnsr::I<double, 3, Frame>{lower_bound}, 0.01, 1.0e-10);\n  if constexpr (std::is_same_v<Frame, ::Frame::Inertial>) {\n    // Don't look at time-independent solution in other than the inertial\n    // frame.\n    const size_t grid_size = 8;\n    const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n    TestHelpers::VerifyGrSolution::verify_time_independent_einstein_solution(\n        solution, grid_size, lower_bound, upper_bound,\n        std::numeric_limits<double>::epsilon() * 1.e5);\n  }\n}\n\nvoid test_serialize() {\n  gr::Solutions::SphericalKerrSchild solution(3.0, {{0.2, 0.3, 0.2}},\n                                              {{0.0, 3.0, 4.0}});\n  test_serialization(solution);\n}\n\nvoid test_copy_and_move() {\n  gr::Solutions::SphericalKerrSchild solution(3.0, {{0.2, 0.3, 0.2}},\n                                              {{0.0, 3.0, 4.0}});\n  test_copy_semantics(solution);\n  auto solution_copy = solution;\n  // clang-tidy: std::move of trivially copyable type\n  test_move_semantics(std::move(solution), solution_copy);  // NOLINT\n}\n\nvoid test_construct_from_options() {\n  const auto created =\n      TestHelpers::test_creation<gr::Solutions::SphericalKerrSchild>(\n          \"Mass: 0.5\\n\"\n          \"Spin: [0.1,0.2,0.3]\\n\"\n          \"Center: [1.0,3.0,2.0]\");\n  CHECK(created == gr::Solutions::SphericalKerrSchild(0.5, {{0.1, 0.2, 0.3}},\n                                                      {{1.0, 3.0, 2.0}}));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Gr.SphericalKerrSchild\",\n    \"[PointwiseFunctions][Unit]\") {\n  test_copy_and_move();\n  test_serialize();\n  test_construct_from_options();\n\n  test_schwarzschild<Frame::Inertial>(DataVector(5));\n  test_schwarzschild<Frame::Inertial>(0.0);\n  test_numerical_deriv_det_spatial_metric<Frame::Inertial>(DataVector(5));\n  test_tag_retrieval<Frame::Inertial>(DataVector(5));\n  test_tag_retrieval<Frame::Inertial>(0.0);\n  test_einstein_solution<Frame::Inertial>();\n\n  test_schwarzschild<Frame::Grid>(DataVector(5));\n  test_schwarzschild<Frame::Grid>(0.0);\n  test_numerical_deriv_det_spatial_metric<Frame::Grid>(DataVector(5));\n  test_tag_retrieval<Frame::Grid>(DataVector(5));\n  test_tag_retrieval<Frame::Grid>(0.0);\n  test_einstein_solution<Frame::Grid>();\n\n  CHECK_THROWS_WITH(\n      []() {\n        gr::Solutions::SphericalKerrSchild solution(1.0, {{1.0, 1.0, 1.0}},\n                                                    {{0.0, 0.0, 0.0}});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Spin magnitude must be < 1\"));\n  CHECK_THROWS_WITH(\n      []() {\n        gr::Solutions::SphericalKerrSchild solution(0.0, {{0.0, 0.0, 0.0}},\n                                                    {{0.0, 0.0, 0.0}});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Mass must be > 0\"));\n  CHECK_THROWS_WITH(\n      []() {\n        gr::Solutions::SphericalKerrSchild solution(-1.0, {{0.0, 0.0, 0.0}},\n                                                    {{0.0, 0.0, 0.0}});\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Mass must be > 0\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<gr::Solutions::SphericalKerrSchild>(\n          \"Mass: -0.5\\n\"\n          \"Spin: [0.1,0.2,0.3]\\n\"\n          \"Center: [1.0,3.0,2.0]\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -0.5 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<gr::Solutions::SphericalKerrSchild>(\n          \"Mass: 0.5\\n\"\n          \"Spin: [1.1,0.9,0.3]\\n\"\n          \"Center: [1.0,3.0,2.0]\"),\n      Catch::Matchers::ContainsSubstring(\"Spin magnitude must be < 1\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_TeukolskyWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TeukolskyWave.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing TeukolskyWave = gr::Solutions::TeukolskyWave;\n\ntemplate <typename DataType>\nusing WithoutBackgroundTags = tmpl::list<\n    gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,\n    gr::Tags::Shift<DataType, 3, Frame::Inertial>,\n    ::Tags::dt<gr::Tags::Shift<DataType, 3, Frame::Inertial>>,\n    gr::Tags::SpatialMetric<DataType, 3, Frame::Inertial>,\n    ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3, Frame::Inertial>>>;\n\ntemplate <typename DataType>\nusing WithBackgroundTags = tmpl::flatten<tmpl::list<\n    WithoutBackgroundTags<DataType>,\n    tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>,\n               gr::Tags::ExtrinsicCurvature<DataType, 3, Frame::Inertial>,\n               gr::Tags::InverseSpatialMetric<DataType, 3, Frame::Inertial>>>>;\n\ntemplate <typename DataType>\nusing WithoutBackgroundResultType =\n    tuples::tagged_tuple_from_typelist<WithoutBackgroundTags<DataType>>;\n\ntemplate <typename DataType>\nusing WithBackgroundResultType =\n    tuples::tagged_tuple_from_typelist<WithBackgroundTags<DataType>>;\n\nstruct SolutionParameters {\n  double amplitude{};\n  int mode{};\n  std::string parity{};\n  std::string direction{};\n  std::array<double, 3> center{};\n  double radius{};\n  double width{};\n  bool include_minkowski_background{};\n  double time{};\n};\n\nstruct TeukolskyWaveProxy : TeukolskyWave {\n  using TeukolskyWave::TeukolskyWave;\n\n  template <typename DataType>\n  WithBackgroundResultType<DataType> test_variables_with_background(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x, const double t) const {\n    return this->variables(x, t, WithBackgroundTags<DataType>{});\n  }\n\n  template <typename DataType>\n  WithoutBackgroundResultType<DataType> test_variables_without_background(\n      const tnsr::I<DataType, 3, Frame::Inertial>& x, const double t) const {\n    return this->variables(x, t, WithoutBackgroundTags<DataType>{});\n  }\n};\n\nstd::vector<std::string> with_background_python_functions() {\n  return {\"teukolsky_wave_lapse\",\n          \"teukolsky_wave_dt_lapse\",\n          \"teukolsky_wave_shift\",\n          \"teukolsky_wave_dt_shift\",\n          \"teukolsky_wave_spatial_metric\",\n          \"teukolsky_wave_dt_spatial_metric\",\n          \"teukolsky_wave_sqrt_det_spatial_metric\",\n          \"teukolsky_wave_extrinsic_curvature\",\n          \"teukolsky_wave_inverse_spatial_metric\"};\n}\n\nstd::vector<std::string> without_background_python_functions() {\n  return {\"teukolsky_wave_no_background_lapse\",\n          \"teukolsky_wave_no_background_dt_lapse\",\n          \"teukolsky_wave_no_background_shift\",\n          \"teukolsky_wave_no_background_dt_shift\",\n          \"teukolsky_wave_no_background_spatial_metric\",\n          \"teukolsky_wave_no_background_dt_spatial_metric\"};\n}\n\nSolutionParameters random_solution_parameters(\n    gsl::not_null<std::mt19937*> generator,\n    const bool include_minkowski_background) {\n  std::uniform_real_distribution<double> amplitude_magnitude(1.0e-5, 1.0e-3);\n  std::uniform_int_distribution<int> sign_distribution(0, 1);\n  std::uniform_int_distribution<int> mode_distribution(-2, 2);\n  std::uniform_int_distribution<int> bool_distribution(0, 1);\n  std::uniform_real_distribution<double> center_distribution(-2.0, 2.0);\n  std::uniform_real_distribution<double> radius_distribution(4.0, 9.0);\n  std::uniform_real_distribution<double> width_distribution(0.7, 2.2);\n  std::uniform_real_distribution<double> time_distribution(-1.5, 1.5);\n\n  const double sign = sign_distribution(*generator) == 0 ? -1.0 : 1.0;\n  return SolutionParameters{\n      sign * amplitude_magnitude(*generator),\n      mode_distribution(*generator),\n      bool_distribution(*generator) == 0 ? \"even\" : \"odd\",\n      bool_distribution(*generator) == 0 ? \"outgoing\" : \"ingoing\",\n      {center_distribution(*generator), center_distribution(*generator),\n       center_distribution(*generator)},\n      radius_distribution(*generator),\n      width_distribution(*generator),\n      include_minkowski_background,\n      time_distribution(*generator)};\n}\n\nTeukolskyWaveProxy make_solution(const SolutionParameters& parameters) {\n  return {parameters.amplitude, parameters.mode,\n          parameters.parity,    parameters.direction,\n          parameters.center,    parameters.radius,\n          parameters.width,     parameters.include_minkowski_background};\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3, Frame::Inertial> random_coords(\n    gsl::not_null<std::mt19937*> generator, const DataType& used_for_size,\n    const double lower = -9.0, const double upper = 9.0) {\n  const std::uniform_real_distribution<double> distribution(lower, upper);\n  return make_with_random_values<tnsr::I<DataType, 3, Frame::Inertial>>(\n      generator, distribution, used_for_size);\n}\n\ntemplate <typename DataType>\ntnsr::I<DataType, 3, Frame::Inertial> safe_random_coords(\n    gsl::not_null<std::mt19937*> generator, const DataType& used_for_size,\n    const std::array<double, 3>& center) {\n  auto x = random_coords(generator, used_for_size, -3.0, 3.0);\n  get<0>(x) += center[0] + 4.0;\n  get<1>(x) += center[1];\n  get<2>(x) += center[2];\n  return x;\n}\n\ntemplate <typename DataType>\nvoid test_background_true_basics(const TeukolskyWave& solution,\n                                 const double time,\n                                 const DataType& used_for_size,\n                                 gsl::not_null<std::mt19937*> generator) {\n  const auto x =\n      safe_random_coords(generator, used_for_size, solution.center());\n  const auto vars = solution.variables(x, time, WithBackgroundTags<DataType>{});\n\n  CHECK(solution.include_minkowski_background());\n  CHECK_ITERABLE_APPROX(get(get<gr::Tags::Lapse<DataType>>(vars)),\n                        make_with_value<DataType>(used_for_size, 1.0));\n  CHECK_ITERABLE_APPROX(get(get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars)),\n                        make_with_value<DataType>(used_for_size, 0.0));\n\n  const auto zero_shift =\n      make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(x, 0.0);\n  const auto& shift = get<gr::Tags::Shift<DataType, 3>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataType, 3>>>(vars);\n  CHECK_ITERABLE_APPROX(shift, zero_shift);\n  CHECK_ITERABLE_APPROX(dt_shift, zero_shift);\n\n  const auto& gamma = get<gr::Tags::SpatialMetric<DataType, 3>>(vars);\n  const auto& dt_gamma =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>>(vars);\n  const auto det_and_inverse = determinant_and_inverse(gamma);\n  const auto& inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataType, 3>>(vars);\n  CHECK_ITERABLE_APPROX(inverse_spatial_metric, det_and_inverse.second);\n  CHECK_ITERABLE_APPROX(\n      get(get<gr::Tags::SqrtDetSpatialMetric<DataType>>(vars)),\n      sqrt(get(det_and_inverse.first)));\n\n  const auto& extrinsic_curvature =\n      get<gr::Tags::ExtrinsicCurvature<DataType, 3>>(vars);\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      CHECK_ITERABLE_APPROX(extrinsic_curvature.get(i, j),\n                            -0.5 * dt_gamma.get(i, j));\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid test_background_false_basics(const TeukolskyWave& solution,\n                                  const double time,\n                                  const DataType& used_for_size,\n                                  gsl::not_null<std::mt19937*> generator) {\n  const auto x =\n      safe_random_coords(generator, used_for_size, solution.center());\n  const auto vars =\n      solution.variables(x, time, WithoutBackgroundTags<DataType>{});\n\n  CHECK_FALSE(solution.include_minkowski_background());\n  CHECK_ITERABLE_APPROX(get(get<gr::Tags::Lapse<DataType>>(vars)),\n                        make_with_value<DataType>(used_for_size, 0.0));\n  CHECK_ITERABLE_APPROX(get(get<Tags::dt<gr::Tags::Lapse<DataType>>>(vars)),\n                        make_with_value<DataType>(used_for_size, 0.0));\n\n  const auto zero_shift =\n      make_with_value<tnsr::I<DataType, 3, Frame::Inertial>>(x, 0.0);\n  const auto& shift = get<gr::Tags::Shift<DataType, 3>>(vars);\n  const auto& dt_shift = get<Tags::dt<gr::Tags::Shift<DataType, 3>>>(vars);\n  CHECK_ITERABLE_APPROX(shift, zero_shift);\n  CHECK_ITERABLE_APPROX(dt_shift, zero_shift);\n}\n\nvoid test_origin_errors(gsl::not_null<std::mt19937*> generator) {\n  const auto parameters = random_solution_parameters(generator, true);\n  const double time = parameters.time;\n  const TeukolskyWave with_background = make_solution(parameters);\n  const TeukolskyWave without_background(\n      parameters.amplitude, parameters.mode, parameters.parity,\n      parameters.direction, parameters.center, parameters.radius,\n      parameters.width, false);\n\n  auto check_origin_error = [&time](const TeukolskyWave& solution,\n                                    const auto& x) {\n    CHECK_THROWS_WITH(\n        (get<gr::Tags::SpatialMetric<typename std::decay_t<decltype(get<0>(x))>,\n                                     3>>(\n            solution.template variable<gr::Tags::SpatialMetric<\n                typename std::decay_t<decltype(get<0>(x))>, 3>>(x, time))),\n        Catch::Matchers::ContainsSubstring(\"radius <=\") and\n            Catch::Matchers::ContainsSubstring(\"Minimum radius was\"));\n  };\n\n  tnsr::I<double, 3, Frame::Inertial> x_at_center{};\n  get<0>(x_at_center) = parameters.center[0];\n  get<1>(x_at_center) = parameters.center[1];\n  get<2>(x_at_center) = parameters.center[2];\n  check_origin_error(with_background, x_at_center);\n  check_origin_error(without_background, x_at_center);\n\n  tnsr::I<double, 3, Frame::Inertial> x_inside_cutoff{};\n  get<0>(x_inside_cutoff) = parameters.center[0] + 0.05;\n  get<1>(x_inside_cutoff) = parameters.center[1];\n  get<2>(x_inside_cutoff) = parameters.center[2];\n  check_origin_error(with_background, x_inside_cutoff);\n\n  tnsr::I<double, 3, Frame::Inertial> x_near_cutoff{};\n  get<0>(x_near_cutoff) = parameters.center[0] + 0.09;\n  get<1>(x_near_cutoff) = parameters.center[1];\n  get<2>(x_near_cutoff) = parameters.center[2];\n  check_origin_error(with_background, x_near_cutoff);\n  check_origin_error(without_background, x_near_cutoff);\n\n  tnsr::I<DataVector, 3, Frame::Inertial> x_datavector{DataVector(3)};\n  get<0>(x_datavector) =\n      DataVector{parameters.center[0], parameters.center[0] + 0.2,\n                 parameters.center[0] + 1.0};\n  get<1>(x_datavector) = DataVector{parameters.center[1], parameters.center[1],\n                                    parameters.center[1]};\n  get<2>(x_datavector) = DataVector{parameters.center[2], parameters.center[2],\n                                    parameters.center[2]};\n  check_origin_error(with_background, x_datavector);\n}\n\nvoid test_far_field_background_recovery(\n    gsl::not_null<std::mt19937*> generator) {\n  // This checks Gaussian localization: far from the pulse center, the\n  // perturbation should be negligible and the metric should reduce to the\n  // requested background state.\n  std::uniform_real_distribution<double> direction_distribution(-1.0, 1.0);\n  for (const bool include_background : {true, false}) {\n    const auto parameters =\n        random_solution_parameters(generator, include_background);\n    const TeukolskyWave solution = make_solution(parameters);\n\n    const double propagation_shift =\n        parameters.direction == \"ingoing\" ? parameters.time : -parameters.time;\n    const double far_radius =\n        parameters.radius - propagation_shift + 12.0 * parameters.width;\n\n    tnsr::I<double, 3, Frame::Inertial> x{};\n    get<0>(x) = parameters.center[0] + far_radius;\n    get<1>(x) = parameters.center[1] + 0.2 * direction_distribution(*generator);\n    get<2>(x) = parameters.center[2] - 0.2 * direction_distribution(*generator);\n\n    const auto vars =\n        solution.variables(x, parameters.time, WithoutBackgroundTags<double>{});\n    const auto& gamma = get<gr::Tags::SpatialMetric<double, 3>>(vars);\n    const auto& dt_gamma =\n        get<Tags::dt<gr::Tags::SpatialMetric<double, 3>>>(vars);\n    const auto local_approx = approx.custom().margin(1.0e-12);\n\n    CHECK(get<0, 0>(gamma) == local_approx(include_background ? 1.0 : 0.0));\n    CHECK(get<1, 1>(gamma) == local_approx(include_background ? 1.0 : 0.0));\n    CHECK(get<2, 2>(gamma) == local_approx(include_background ? 1.0 : 0.0));\n    CHECK(get<0, 1>(gamma) == local_approx(0.0));\n    CHECK(get<0, 2>(gamma) == local_approx(0.0));\n    CHECK(get<1, 2>(gamma) == local_approx(0.0));\n    CHECK(get<0, 0>(dt_gamma) == local_approx(0.0));\n    CHECK(get<1, 1>(dt_gamma) == local_approx(0.0));\n    CHECK(get<2, 2>(dt_gamma) == local_approx(0.0));\n    CHECK(get<0, 1>(dt_gamma) == local_approx(0.0));\n    CHECK(get<0, 2>(dt_gamma) == local_approx(0.0));\n    CHECK(get<1, 2>(dt_gamma) == local_approx(0.0));\n  }\n}\n\nvoid test_axis_regularization(gsl::not_null<std::mt19937*> generator) {\n  const auto parameters = random_solution_parameters(generator, true);\n  std::uniform_real_distribution<double> offset_distribution(0.5, 2.0);\n  const std::array<double, 2> axis_signs{{-1.0, 1.0}};\n  // Use a different offset than the implementation's internal axis-limit\n  // stencil so this remains an independent limit check.\n  const double rho = 3.0e-8;\n\n  for (const std::string parity : {\"even\", \"odd\"}) {\n    for (int mode = -2; mode <= 2; ++mode) {\n      const TeukolskyWave solution(parameters.amplitude, mode, parity,\n                                   parameters.direction, parameters.center,\n                                   parameters.radius, parameters.width, true);\n      for (const double axis_sign : axis_signs) {\n        tnsr::I<double, 3, Frame::Inertial> axis_x{};\n        get<0>(axis_x) = parameters.center[0];\n        get<1>(axis_x) = parameters.center[1];\n        get<2>(axis_x) =\n            parameters.center[2] +\n            axis_sign * (parameters.radius + offset_distribution(*generator));\n\n        const auto axis_vars = solution.variables(axis_x, parameters.time,\n                                                  WithBackgroundTags<double>{});\n        const auto& axis_gamma =\n            get<gr::Tags::SpatialMetric<double, 3>>(axis_vars);\n        const auto& axis_dt_gamma =\n            get<Tags::dt<gr::Tags::SpatialMetric<double, 3>>>(axis_vars);\n        const auto det_and_inverse = determinant_and_inverse(axis_gamma);\n        const auto& inverse_spatial_metric =\n            get<gr::Tags::InverseSpatialMetric<double, 3>>(axis_vars);\n        CHECK_ITERABLE_APPROX(inverse_spatial_metric, det_and_inverse.second);\n        CHECK(get(get<gr::Tags::SqrtDetSpatialMetric<double>>(axis_vars)) >\n              0.0);\n\n        const auto near_axis_limit_approx =\n            approx.custom().epsilon(1.0e-10).scale(1.0);\n        CAPTURE(mode);\n        CAPTURE(parity);\n        CAPTURE(axis_sign);\n\n        tnsr::I<double, 3, Frame::Inertial> near_axis_x{};\n        get<0>(near_axis_x) = parameters.center[0] + rho;\n        get<1>(near_axis_x) = parameters.center[1];\n        get<2>(near_axis_x) = get<2>(axis_x);\n        tnsr::I<double, 3, Frame::Inertial> near_axis_y{};\n        get<0>(near_axis_y) = parameters.center[0];\n        get<1>(near_axis_y) = parameters.center[1] + rho;\n        get<2>(near_axis_y) = get<2>(axis_x);\n\n        const auto near_axis_x_vars = solution.variables(\n            near_axis_x, parameters.time, WithBackgroundTags<double>{});\n        const auto near_axis_y_vars = solution.variables(\n            near_axis_y, parameters.time, WithBackgroundTags<double>{});\n        const auto& near_axis_x_gamma =\n            get<gr::Tags::SpatialMetric<double, 3>>(near_axis_x_vars);\n        const auto& near_axis_x_dt_gamma =\n            get<Tags::dt<gr::Tags::SpatialMetric<double, 3>>>(near_axis_x_vars);\n        const auto& near_axis_y_gamma =\n            get<gr::Tags::SpatialMetric<double, 3>>(near_axis_y_vars);\n        const auto& near_axis_y_dt_gamma =\n            get<Tags::dt<gr::Tags::SpatialMetric<double, 3>>>(near_axis_y_vars);\n\n        for (size_t i = 0; i < 3; ++i) {\n          for (size_t j = i; j < 3; ++j) {\n            CHECK(std::isfinite(axis_gamma.get(i, j)));\n            CHECK(std::isfinite(axis_dt_gamma.get(i, j)));\n            const double near_axis_gamma_limit =\n                0.5 *\n                (near_axis_x_gamma.get(i, j) + near_axis_y_gamma.get(i, j));\n            const double near_axis_dt_gamma_limit =\n                0.5 * (near_axis_x_dt_gamma.get(i, j) +\n                       near_axis_y_dt_gamma.get(i, j));\n            CHECK(near_axis_x_gamma.get(i, j) ==\n                  near_axis_limit_approx(near_axis_y_gamma.get(i, j)));\n            CHECK(near_axis_x_dt_gamma.get(i, j) ==\n                  near_axis_limit_approx(near_axis_y_dt_gamma.get(i, j)));\n            CHECK(axis_gamma.get(i, j) ==\n                  near_axis_limit_approx(near_axis_gamma_limit));\n            CHECK(axis_dt_gamma.get(i, j) ==\n                  near_axis_limit_approx(near_axis_dt_gamma_limit));\n          }\n        }\n      }\n    }\n  }\n}\n\nvoid test_spec_pointwise_agreement() {\n  // This regression test checks agreement with SpEC's\n  // PointwiseFunctions/AnalyticSolutions/LinearizedGravity/TeukolskyWave.cpp\n  // at one representative point for the double-valued no-background case.\n  const TeukolskyWave solution{0.01, 2,   \"even\", \"outgoing\", {{0.0, 0.0, 0.0}},\n                               18.5, 1.2, false};\n  tnsr::I<double, 3, Frame::Inertial> x{};\n  get<0>(x) = 17.1;\n  get<1>(x) = -0.2;\n  get<2>(x) = 0.3;\n\n  const auto vars = solution.variables(x, 0.0, WithoutBackgroundTags<double>{});\n  const auto& gamma = get<gr::Tags::SpatialMetric<double, 3>>(vars);\n  const auto& dt_gamma =\n      get<Tags::dt<gr::Tags::SpatialMetric<double, 3>>>(vars);\n\n  const auto spec_approx = approx.custom().epsilon(1.0e-13).scale(1.0);\n  CHECK(get<0, 0>(gamma) == spec_approx(3.0726477174852849e-06));\n  CHECK(get<0, 1>(gamma) == spec_approx(4.6856056129806923e-06));\n  CHECK(get<0, 2>(gamma) == spec_approx(7.7441285036194232e-06));\n  CHECK(get<1, 1>(gamma) == spec_approx(4.2473114121358127e-04));\n  CHECK(get<1, 2>(gamma) == spec_approx(2.5498181687206425e-07));\n  CHECK(get<2, 2>(gamma) == spec_approx(-4.2780378893106667e-04));\n\n  CHECK(get<0, 0>(dt_gamma) == spec_approx(2.0523780729181779e-06));\n  CHECK(get<0, 1>(dt_gamma) == spec_approx(-4.9599082769854031e-06));\n  CHECK(get<0, 2>(dt_gamma) == spec_approx(-1.2565408165979533e-05));\n  CHECK(get<1, 1>(dt_gamma) == spec_approx(-6.2085896232436382e-04));\n  CHECK(get<1, 2>(dt_gamma) == spec_approx(-3.2099570756649984e-07));\n  CHECK(get<2, 2>(dt_gamma) == spec_approx(6.1880658425144612e-04));\n}\n\ntemplate <typename DataType>\nvoid test_python_comparison_with_background(const TeukolskyWaveProxy& solution,\n                                            const DataType& used_for_size) {\n  for (const std::string parity : {\"even\", \"odd\"}) {\n    for (int mode = -2; mode <= 2; ++mode) {\n      const TeukolskyWaveProxy branch_solution(\n          solution.amplitude(), mode, parity, solution.direction(),\n          {{20.0, 0.0, 0.0}}, solution.radius(), solution.width(), true);\n      CAPTURE(parity);\n      CAPTURE(mode);\n      pypp::check_with_random_values<2>(\n          &TeukolskyWaveProxy::template test_variables_with_background<\n              DataType>,\n          branch_solution, \"TeukolskyWave\", with_background_python_functions(),\n          {{{-9.0, 9.0}, {-2.0, 2.0}}},\n          std::make_tuple(branch_solution.amplitude(), branch_solution.mode(),\n                          branch_solution.parity(), branch_solution.direction(),\n                          branch_solution.center(), branch_solution.radius(),\n                          branch_solution.width()),\n          used_for_size);\n    }\n  }\n}\n\ntemplate <typename DataType>\nvoid test_python_comparison_without_background(\n    const TeukolskyWaveProxy& solution, const DataType& used_for_size) {\n  for (const std::string parity : {\"even\", \"odd\"}) {\n    for (int mode = -2; mode <= 2; ++mode) {\n      const TeukolskyWaveProxy branch_solution(\n          solution.amplitude(), mode, parity, solution.direction(),\n          {{20.0, 0.0, 0.0}}, solution.radius(), solution.width(), false);\n      CAPTURE(parity);\n      CAPTURE(mode);\n      pypp::check_with_random_values<2>(\n          &TeukolskyWaveProxy::template test_variables_without_background<\n              DataType>,\n          branch_solution, \"TeukolskyWave\",\n          without_background_python_functions(), {{{-9.0, 9.0}, {-2.0, 2.0}}},\n          std::make_tuple(branch_solution.amplitude(), branch_solution.mode(),\n                          branch_solution.parity(), branch_solution.direction(),\n                          branch_solution.center(), branch_solution.radius(),\n                          branch_solution.width()),\n          used_for_size);\n    }\n  }\n}\n\nvoid test_construct_from_options() {\n  const auto created_solution = TestHelpers::test_creation<TeukolskyWave>(\n      \"Amplitude: 1e-4\\n\"\n      \"Mode: 2\\n\"\n      \"Parity: even\\n\"\n      \"Direction: outgoing\\n\"\n      \"Center: [0.0, 0.0, 0.0]\\n\"\n      \"Radius: 8.0\\n\"\n      \"Width: 1.5\");\n  CHECK(created_solution == TeukolskyWave(1.0e-4, 2, \"even\", \"outgoing\",\n                                          {{0.0, 0.0, 0.0}}, 8.0, 1.5, true));\n}\n\nvoid test_background_false_errors() {\n  const TeukolskyWave solution{\n      1.0e-4, 2, \"even\", \"outgoing\", {{0.0, 0.0, 0.0}}, 8.0, 1.5, false};\n  tnsr::I<double, 3, Frame::Inertial> x{};\n  get<0>(x) = 6.2;\n  get<1>(x) = -1.4;\n  get<2>(x) = 3.6;\n  const double t = 1.3;\n  CHECK_THROWS_WITH(\n      (get<gr::Tags::InverseSpatialMetric<double, 3>>(\n          solution.template variable<gr::Tags::InverseSpatialMetric<double, 3>>(\n              x, t))),\n      Catch::Matchers::ContainsSubstring(\n          \"IncludeMinkowskiBackground must be true to compute inverse spatial \"\n          \"metric\"));\n  CHECK_THROWS_WITH(\n      (get<gr::Tags::SqrtDetSpatialMetric<double>>(\n          solution.template variable<gr::Tags::SqrtDetSpatialMetric<double>>(\n              x, t))),\n      Catch::Matchers::ContainsSubstring(\"IncludeMinkowskiBackground must be \"\n                                         \"true to compute sqrt(det(gamma))\"));\n  CHECK_THROWS_WITH(\n      (get<gr::Tags::ExtrinsicCurvature<double, 3>>(\n          solution.template variable<gr::Tags::ExtrinsicCurvature<double, 3>>(\n              x, t))),\n      Catch::Matchers::ContainsSubstring(\n          \"IncludeMinkowskiBackground must be true to compute extrinsic \"\n          \"curvature\"));\n}\n\nvoid test_invalid_construction_throws() {\n  CHECK_THROWS_WITH(\n      []() {\n        const TeukolskyWave bad_solution(1.0e-4, 3, \"even\", \"outgoing\",\n                                         {{0.0, 0.0, 0.0}}, 8.0, 1.5, true);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Mode must lie between -2 and 2\"));\n  CHECK_THROWS_WITH(\n      []() {\n        const TeukolskyWave bad_solution(1.0e-4, 2, \"even\", \"outgoing\",\n                                         {{0.0, 0.0, 0.0}}, 8.0, 0.0, true);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Width must be greater than 0\"));\n  CHECK_THROWS_WITH(\n      []() {\n        const TeukolskyWave bad_solution(1.0e-4, 2, \"even\", \"outgoing\",\n                                         {{0.0, 0.0, 0.0}}, 8.0, -1.0, true);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Width must be greater than 0\"));\n  CHECK_THROWS_WITH(\n      []() {\n        const TeukolskyWave bad_solution(1.0e-4, 2, \"bad\", \"outgoing\",\n                                         {{0.0, 0.0, 0.0}}, 8.0, 1.5, true);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Parity must be either 'even' or 'odd'\"));\n  CHECK_THROWS_WITH(\n      []() {\n        const TeukolskyWave bad_solution(1.0e-4, 2, \"even\", \"bad\",\n                                         {{0.0, 0.0, 0.0}}, 8.0, 1.5, true);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Direction must be either 'outgoing' or 'ingoing'\"));\n}\n\nvoid test_serialize_and_copy(gsl::not_null<std::mt19937*> generator) {\n  const auto parameters = random_solution_parameters(generator, true);\n  const TeukolskyWave solution = make_solution(parameters);\n  test_serialization(solution);\n  test_copy_semantics(solution);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Gr.TeukolskyWave\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/\"};\n\n  MAKE_GENERATOR(generator);\n  const auto with_background_parameters =\n      random_solution_parameters(make_not_null(&generator), true);\n  const TeukolskyWaveProxy with_background_solution =\n      make_solution(with_background_parameters);\n  test_background_true_basics(\n      with_background_solution, with_background_parameters.time,\n      DataVector(5, std::numeric_limits<double>::signaling_NaN()),\n      make_not_null(&generator));\n  test_background_true_basics(\n      with_background_solution, with_background_parameters.time,\n      std::numeric_limits<double>::signaling_NaN(), make_not_null(&generator));\n  test_python_comparison_with_background(\n      with_background_solution, std::numeric_limits<double>::signaling_NaN());\n\n  const auto without_background_parameters =\n      random_solution_parameters(make_not_null(&generator), false);\n  const TeukolskyWaveProxy without_background_solution =\n      make_solution(without_background_parameters);\n  test_background_false_basics(\n      without_background_solution, without_background_parameters.time,\n      DataVector(5, std::numeric_limits<double>::signaling_NaN()),\n      make_not_null(&generator));\n  test_background_false_basics(\n      without_background_solution, without_background_parameters.time,\n      std::numeric_limits<double>::signaling_NaN(), make_not_null(&generator));\n  test_python_comparison_without_background(\n      without_background_solution,\n      std::numeric_limits<double>::signaling_NaN());\n\n  test_origin_errors(make_not_null(&generator));\n  test_far_field_background_recovery(make_not_null(&generator));\n  test_axis_regularization(make_not_null(&generator));\n  test_spec_pointwise_agreement();\n  test_construct_from_options();\n  test_background_false_errors();\n  test_invalid_construction_throws();\n  test_serialize_and_copy(make_not_null(&generator));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_TrumpetSchwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/PyppFundamentals.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/VerifyGrSolution.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct TrumpetSchwarzschildProxy : gr::Solutions::TrumpetSchwarzschild {\n  using gr::Solutions::TrumpetSchwarzschild::TrumpetSchwarzschild;\n\n  template <typename DataType>\n  using variables_tags =\n      typename gr::Solutions::TrumpetSchwarzschild::template tags<DataType>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>> test_variables(\n      const tnsr::I<DataType, 3>& x, const double t) const {\n    return this->variables(x, t, variables_tags<DataType>{});\n  }\n};\n\nusing TrumpetSchwarzschild = gr::Solutions::TrumpetSchwarzschild;\n\ntemplate <typename DataType>\nusing ResultType = tuples::TaggedTuple<\n    gr::Tags::Lapse<DataType>, ::Tags::dt<gr::Tags::Lapse<DataType>>,\n    gr::Solutions::TrumpetSchwarzschild::DerivLapse<DataType>,\n    gr::Tags::Shift<DataType, 3>, ::Tags::dt<gr::Tags::Shift<DataType, 3>>,\n    gr::Solutions::TrumpetSchwarzschild::DerivShift<DataType>,\n    gr::Tags::SpatialMetric<DataType, 3>,\n    ::Tags::dt<gr::Tags::SpatialMetric<DataType, 3>>,\n    gr::Solutions::TrumpetSchwarzschild::DerivSpatialMetric<DataType>,\n    gr::Tags::SqrtDetSpatialMetric<DataType>,\n    gr::Tags::ExtrinsicCurvature<DataType, 3>,\n    gr::Tags::InverseSpatialMetric<DataType, 3>>;\n\ntemplate <typename DataType, typename Generator>\nvoid test_trumpet_schwarzschild_random(\n    const gr::Solutions::TrumpetSchwarzschild& solution,\n    const DataType& used_for_size, const gsl::not_null<Generator*> generator,\n    const double random_value_lower_bound,\n    const double random_value_upper_bound, const double mass, const double n) {\n  // check at random pts\n  const std::uniform_real_distribution<> distribution(random_value_lower_bound,\n                                                      random_value_upper_bound);\n\n  const auto x = make_with_random_values<tnsr::I<DataType, 3, Frame::Inertial>>(\n      generator, distribution, used_for_size);\n\n  const auto py_vars = pypp::call<ResultType<DataType>>(\n      \"TrumpetSchwarzschild\", \"trumpet_schwarzschild_variables\", x,\n      std::numeric_limits<double>::signaling_NaN(), mass, n);\n\n  const auto vars = solution.variables(\n      x, std::numeric_limits<double>::signaling_NaN(),\n      typename TrumpetSchwarzschild::template tags<DataType>{});\n\n  const Approx custom_approx = Approx::custom().epsilon(1.e-9).scale(1.);\n  tmpl::for_each<TrumpetSchwarzschild::tags<DataType>>(\n      [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        const std::string tag_name = db::tag_name<Tag>();\n        CAPTURE(tag_name);\n        CAPTURE(x);\n        CHECK_ITERABLE_CUSTOM_APPROX(tuples::get<Tag>(py_vars), get<Tag>(vars),\n                                     custom_approx);\n      });\n}\n\ntemplate <typename DataType>\nvoid test_trumpet_schwarzschild_fixed(\n    const gr::Solutions::TrumpetSchwarzschild& solution, const double mass,\n    const double n) {\n  // check at fixed pts\n  tnsr::I<DataType, 3, Frame::Inertial> x;\n  if constexpr (std::is_same<DataType, double>::value) {\n    x = tnsr::I<double, 3, Frame::Inertial>{\n        {{0.04879024622126530, 0.01279253964649560, 0.05082515227577830}}};\n  } else {\n    x = tnsr::I<DataVector, 3, Frame::Inertial>{\n        {{{51.5006439411830, 0.5680968170041150, 4.180590600246490},\n          {33.88201665342590, 0.4970452133901940, 3.029648991759770},\n          {13.96500204970360, 0.2830205552189050, 4.848632186557860}}}};\n  }\n  const auto py_vars = pypp::call<ResultType<DataType>>(\n      \"TrumpetSchwarzschild\", \"trumpet_schwarzschild_variables\", x,\n      std::numeric_limits<double>::signaling_NaN(), mass, n);\n\n  const auto vars = solution.variables(\n      x, std::numeric_limits<double>::signaling_NaN(),\n      typename TrumpetSchwarzschild::template tags<DataType>{});\n\n  const Approx custom_approx = Approx::custom().epsilon(1.e-9).scale(1.);\n  tmpl::for_each<TrumpetSchwarzschild::tags<DataType>>(\n      [&]<typename Tag>(tmpl::type_<Tag> /*meta*/) {\n        const std::string tag_name = db::tag_name<Tag>();\n        CAPTURE(tag_name);\n        CAPTURE(x);\n        CHECK_ITERABLE_CUSTOM_APPROX(tuples::get<Tag>(py_vars), get<Tag>(vars),\n                                     custom_approx);\n      });\n}\n\nvoid test_consistency(const TrumpetSchwarzschild& solution) {\n  TestHelpers::VerifyGrSolution::verify_consistency(\n      solution, 1.234, tnsr::I<double, 3>{{{1.2, 2.3, 3.4}}}, 0.01, 1.e-8);\n\n  const size_t grid_size = 8;\n  const std::array<double, 3> lower_bound{{0.8, 1.22, 1.30}};\n  const std::array<double, 3> upper_bound{{0.82, 1.24, 1.32}};\n  TestHelpers::VerifyGrSolution::verify_time_independent_einstein_solution(\n      solution, grid_size, lower_bound, upper_bound,\n      std::numeric_limits<double>::epsilon() * 1e5);\n}\n\nvoid test_serialize(const TrumpetSchwarzschild& solution, const double mass,\n                    const double n) {\n  test_serialization(solution);\n  test_trumpet_schwarzschild_fixed<double>(serialize_and_deserialize(solution),\n                                           mass, n);\n}\n\nTrumpetSchwarzschild test_construct_from_options(\n    const TrumpetSchwarzschild& solution, const double mass, const double n) {\n  auto created =\n      TestHelpers::test_creation<gr::Solutions::TrumpetSchwarzschild>(\n          \"Mass: \"s + std::to_string(mass) +\n          \"\\n\"\n          \"N: \" +\n          std::to_string(n));\n  CHECK(created == solution);\n  return created;\n}\n\nvoid test_copy_and_move(TrumpetSchwarzschild& solution,\n                        TrumpetSchwarzschild& solution_copy) {\n  test_copy_semantics(solution);\n  // clang-tidy: std::move of trivially copyable type\n  test_move_semantics(std::move(solution), solution_copy);  // NOLINT\n}\n}  // namespace\n\n// [[TimeOut, 40]]\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Gr.TrumpetSchwarzschild\",\n    \"[PointwiseFunctions][Unit]\") {\n  const double mass = 2.;\n  const double n = 2.;\n\n  {\n    gr::Solutions::TrumpetSchwarzschild solution(mass, n);\n    MAKE_GENERATOR(generator);\n\n    const pypp::SetupLocalPythonEnvironment local_python_env{\n        \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/\"};\n\n    // check if environment support Scipy attributes for random grid pts check\n    const bool can_import =\n        pypp::call<bool>(\"TrumpetSchwarzschild\", \"check_import\");\n    if (can_import) {\n      // random grid pts check\n      test_trumpet_schwarzschild_random(\n          solution, std::numeric_limits<double>::signaling_NaN(),\n          make_not_null(&generator), -1.e-1 * mass, -1.e-4 * mass, mass, n);\n      test_trumpet_schwarzschild_random(\n          solution, DataVector{1, std::numeric_limits<double>::signaling_NaN()},\n          make_not_null(&generator), 1.e-1 * mass, 1. * mass, mass, n);\n      test_trumpet_schwarzschild_random(\n          solution, DataVector{1, std::numeric_limits<double>::signaling_NaN()},\n          make_not_null(&generator), 1. * mass, 10. * mass, mass, n);\n      test_trumpet_schwarzschild_random(\n          solution, DataVector{1, std::numeric_limits<double>::signaling_NaN()},\n          make_not_null(&generator), 10. * mass, 100. * mass, mass, n);\n      test_trumpet_schwarzschild_random(\n          solution, DataVector{1, std::numeric_limits<double>::signaling_NaN()},\n          make_not_null(&generator), 100. * mass, 4999 / sqrt(3) * mass, mass,\n          n);\n    } else {\n      // fixed grid pts check\n      test_trumpet_schwarzschild_fixed<DataVector>(solution, mass, n);\n    }\n\n    test_consistency(solution);\n    test_serialize(solution, mass, n);\n    auto solution_copy = test_construct_from_options(solution, mass, n);\n    test_copy_and_move(solution, solution_copy);\n  }\n\n  CHECK_THROWS_WITH(\n      []() { const gr::Solutions::TrumpetSchwarzschild solution(0., 1.); }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Black hole mass must be positive, but given \"));\n  CHECK_THROWS_WITH(\n      []() {\n        const gr::Solutions::TrumpetSchwarzschild solution(0.1, -0.25);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"Parameter n must be non-negative, but given \"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<gr::Solutions::TrumpetSchwarzschild>(\n          \"Mass: 0.\\n\"\n          \"N: 1.\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value 0 is below the lower bound of 0.1\"));\n  CHECK_THROWS_WITH(\n      TestHelpers::test_creation<gr::Solutions::TrumpetSchwarzschild>(\n          \"Mass: 1.\\n\"\n          \"N: -0.25\\n\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Value -0.25 is below the lower bound of 0\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Test_WrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <limits>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugePlaneWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/GaugeWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   tmpl::list<MathFunctions::PowX<1, Frame::Inertial>>>>;\n  };\n};\n\ntemplate <typename SolutionType>\nvoid compare_different_wrapped_solutions(const double mass,\n                                         const std::array<double, 3>& spin,\n                                         const std::array<double, 3>& center,\n                                         const double mass2,\n                                         const std::array<double, 3>& spin2,\n                                         const std::array<double, 3>& center2) {\n  const SolutionType& solution{mass, spin, center};\n  const SolutionType& solution2{mass2, spin2, center2};\n  CHECK_FALSE(solution == solution2);\n  CHECK(solution != solution2);\n}\n\ntemplate <typename SolutionType>\nvoid test_copy_and_move(const SolutionType& solution) {\n  test_copy_semantics(solution);\n  auto solution_copy2 = solution;\n  // clang-tidy: std::move of trivially copyable type\n  test_move_semantics(std::move(solution_copy2), solution);  // NOLINT\n}\n\ntemplate <typename SolutionType, typename... Args>\nvoid test_generalized_harmonic_solution(Args&&... args) {\n  const SolutionType solution{std::forward<Args>(args)...};\n  const auto wrapped_solution =\n    gh::Solutions::WrappedGr<SolutionType>(solution);\n\n  const DataVector data_vector{3.0, 4.0};\n  const tnsr::I<DataVector, SolutionType::volume_dim, Frame::Inertial> x{\n      data_vector};\n  // Don't set time to signaling NaN, since not all solutions tested here\n  // are static\n  const double t = 44.44;\n\n  // Check that the wrapped solution returns the same variables as\n  // the solution\n  const auto vars = solution.variables(\n      x, t, typename SolutionType::template tags<DataVector>{});\n  const auto wrapped_vars = wrapped_solution.variables(\n      x, t, typename SolutionType::template tags<DataVector>{});\n\n  tmpl::for_each<typename SolutionType::template tags<DataVector>>(\n      [&vars, &wrapped_vars](auto tag_v) {\n        using tag = typename decltype(tag_v)::type;\n        CHECK(get<tag>(vars) == get<tag>(wrapped_vars));\n      });\n\n  // Check that the wrapped solution returns the correct psi, pi, phi\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<Tags::deriv<gr::Tags::Lapse<DataVector>,\n                      tmpl::size_t<SolutionType::volume_dim>, Frame::Inertial>>(\n          vars);\n  const auto& shift =\n      get<gr::Tags::Shift<DataVector, SolutionType::volume_dim>>(vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataVector, SolutionType::volume_dim>>>(\n          vars);\n  const auto& d_shift =\n      get<Tags::deriv<gr::Tags::Shift<DataVector, SolutionType::volume_dim>,\n                      tmpl::size_t<SolutionType::volume_dim>, Frame::Inertial>>(\n          vars);\n  const auto& g =\n      get<gr::Tags::SpatialMetric<DataVector, SolutionType::volume_dim>>(vars);\n  const auto& dt_g = get<\n      Tags::dt<gr::Tags::SpatialMetric<DataVector, SolutionType::volume_dim>>>(\n      vars);\n  const auto& d_g = get<\n      Tags::deriv<gr::Tags::SpatialMetric<DataVector, SolutionType::volume_dim>,\n                  tmpl::size_t<SolutionType::volume_dim>, Frame::Inertial>>(\n      vars);\n  const auto psi = gr::spacetime_metric(lapse, shift, g);\n  const auto phi = gh::phi(lapse, d_lapse, shift, d_shift, g, d_g);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, g, dt_g, phi);\n\n  const auto wrapped_gh_vars = wrapped_solution.variables(\n      x, t,\n      tmpl::list<\n          gr::Tags::SpacetimeMetric<DataVector, SolutionType::volume_dim>,\n          gh::Tags::Pi<DataVector, SolutionType::volume_dim>,\n          gh::Tags::Phi<DataVector, SolutionType::volume_dim>>{});\n  CHECK(psi ==\n        get<gr::Tags::SpacetimeMetric<DataVector, SolutionType::volume_dim>>(\n            wrapped_gh_vars));\n  CHECK(pi == get<gh::Tags::Pi<DataVector, SolutionType::volume_dim>>(\n                  wrapped_gh_vars));\n  CHECK(phi == get<gh::Tags::Phi<DataVector, SolutionType::volume_dim>>(\n                   wrapped_gh_vars));\n\n  // Weak test of operators == and !=\n  CHECK(wrapped_solution == wrapped_solution);\n  CHECK_FALSE(wrapped_solution != wrapped_solution);\n\n  if constexpr (std::is_same_v<SolutionType, gr::Solutions::GaugePlaneWave<1>>\n    or std::is_same_v<SolutionType, gr::Solutions::GaugePlaneWave<2>>\n    or std::is_same_v<SolutionType, gr::Solutions::GaugePlaneWave<3>>) {\n    register_factory_classes_with_charm<Metavariables>();\n  }\n  test_serialization(wrapped_solution);\n  test_copy_and_move(wrapped_solution);\n}\n\ntemplate <typename SolutionType>\nvoid test_construct_from_options() {\n  const auto created =\n      TestHelpers::test_creation<gh::Solutions::WrappedGr<SolutionType>>(\n          \"Mass: 0.5\\n\"\n          \"Spin: [0.1,0.2,0.3]\\n\"\n          \"Center: [1.0,3.0,2.0]\");\n  const double mass = 0.5;\n  const std::array<double, 3> spin{{0.1, 0.2, 0.3}};\n  const std::array<double, 3> center{{1.0, 3.0, 2.0}};\n\n  CHECK(created == gh::Solutions::WrappedGr<SolutionType>(mass, spin, center));\n}\n\ntemplate <>\nvoid test_construct_from_options<gr::Solutions::KerrSchild>() {\n  const auto created = TestHelpers::test_creation<\n      gh::Solutions::WrappedGr<gr::Solutions::KerrSchild>>(\n      \"Mass: 0.5\\n\"\n      \"Spin: [0.1,0.2,0.3]\\n\"\n      \"Center: [1.0,3.0,2.0]\\n\"\n      \"Velocity: [0.1,-0.3,0.5]\");\n  const double mass = 0.5;\n  const std::array<double, 3> spin{{0.1, 0.2, 0.3}};\n  const std::array<double, 3> center{{1.0, 3.0, 2.0}};\n    const std::array<double, 3> velocity{{0.1, -0.3, 0.5}};\n\n  CHECK(created == gh::Solutions::WrappedGr<gr::Solutions::KerrSchild>(\n                       mass, spin, center, velocity));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Gr.WrappedGr\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const double amplitude = 0.24;\n  const double wavelength = 4.4;\n  test_generalized_harmonic_solution<gr::Solutions::GaugeWave<1>>(amplitude,\n                                                                  wavelength);\n  test_generalized_harmonic_solution<gr::Solutions::GaugeWave<2>>(amplitude,\n                                                                  wavelength);\n  test_generalized_harmonic_solution<gr::Solutions::GaugeWave<3>>(amplitude,\n                                                                  wavelength);\n\n  test_generalized_harmonic_solution<gr::Solutions::GaugePlaneWave<1>>(\n    std::array{0.5},\n    std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n  test_generalized_harmonic_solution<gr::Solutions::GaugePlaneWave<2>>(\n    std::array{0.5, 0.1},\n    std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n  test_generalized_harmonic_solution<gr::Solutions::GaugePlaneWave<3>>(\n    std::array{0.5, 0.1, -0.2},\n    std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2));\n\n  test_generalized_harmonic_solution<gr::Solutions::Minkowski<1>>();\n  test_generalized_harmonic_solution<gr::Solutions::Minkowski<2>>();\n  test_generalized_harmonic_solution<gr::Solutions::Minkowski<3>>();\n\n  const double mass = 0.5;\n  const std::array<double, 3> spin{{0.1, 0.2, 0.3}};\n  const std::array<double, 3> center{{1.0, 3.0, 2.0}};\n  test_generalized_harmonic_solution<gr::Solutions::KerrSchild>(mass, spin,\n                                                                center);\n\n  test_generalized_harmonic_solution<gr::Solutions::SphericalKerrSchild>(\n      mass, spin, center);\n\n  const double mass2 = 0.4;\n  const std::array<double, 3> spin2{{0.4, 0.3, 0.2}};\n  const std::array<double, 3> center2{{4.0, 1.0, 3.0}};\n  test_generalized_harmonic_solution<gr::Solutions::KerrSchild>(mass2, spin2,\n                                                                center2);\n\n  test_generalized_harmonic_solution<gr::Solutions::SphericalKerrSchild>(\n      mass2, spin2, center2);\n\n  compare_different_wrapped_solutions<gr::Solutions::KerrSchild>(\n      mass, spin, center, mass2, spin2, center2);\n\n  compare_different_wrapped_solutions<gr::Solutions::SphericalKerrSchild>(\n      mass, spin, center, mass2, spin2, center2);\n\n  test_construct_from_options<gr::Solutions::KerrSchild>();\n\n  test_construct_from_options<gr::Solutions::SphericalKerrSchild>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TeukolskyWave.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\nORIGIN_EXCLUSION_RADIUS = 0.1\nAXIS_COORDINATE_TOLERANCE = 1.0e-14\nAXIS_LIMIT_OFFSET = 1.0e-8\n\n\ndef _perturbation_and_dt_perturbation(\n    centered_coords,\n    t,\n    amplitude,\n    mode,\n    parity,\n    direction,\n    radius,\n    width,\n):\n    radius_from_center = np.sqrt(np.sum(centered_coords**2))\n    if radius_from_center <= ORIGIN_EXCLUSION_RADIUS:\n        raise RuntimeError(\n            \"TeukolskyWave cannot be evaluated at points with radius <= 0.1 \"\n            \"from its center because the implementation uses spherical \"\n            \"coordinates, which are singular at the origin.\"\n        )\n\n    cylindrical_radius = np.sqrt(\n        centered_coords[0] ** 2 + centered_coords[1] ** 2\n    )\n    cos_theta = centered_coords[2] / radius_from_center\n    sin_theta = cylindrical_radius / radius_from_center\n    if cylindrical_radius > AXIS_COORDINATE_TOLERANCE:\n        cos_phi = centered_coords[0] / cylindrical_radius\n        sin_phi = centered_coords[1] / cylindrical_radius\n    else:\n        cos_phi = 1.0\n        sin_phi = 0.0\n    sin_2phi = 2.0 * sin_phi * cos_phi\n    cos_2phi = cos_phi**2 - sin_phi**2\n\n    y_profile = (\n        radius_from_center - radius + (t if direction == \"ingoing\" else -t)\n    )\n    minus_two_over_width_squared = -2.0 / width**2\n    profile = amplitude * np.exp(-(y_profile**2) / width**2)\n    profile_1 = minus_two_over_width_squared * y_profile * profile\n    profile_2 = minus_two_over_width_squared * (profile + y_profile * profile_1)\n    profile_3 = minus_two_over_width_squared * (\n        2.0 * profile_1 + y_profile * profile_2\n    )\n    profile_4 = minus_two_over_width_squared * (\n        3.0 * profile_2 + y_profile * profile_3\n    )\n    profile_5 = minus_two_over_width_squared * (\n        4.0 * profile_3 + y_profile * profile_4\n    )\n\n    spherical_metric = np.zeros((3, 3))\n    dt_spherical_metric = np.zeros((3, 3))\n\n    if parity == \"even\":\n        if mode == -2:\n            angular_rr = sin_theta**2 * sin_2phi\n            angular_rtheta = sin_theta * cos_theta * sin_2phi\n            angular_rphi = sin_theta * cos_2phi\n            angular_theta_theta_1 = (1.0 + cos_theta**2) * sin_2phi\n            angular_theta_theta_2 = -sin_2phi\n            angular_thetaphi = -cos_theta * cos_2phi\n            angular_phi_phi_1 = -angular_theta_theta_1\n            angular_phi_phi_2 = cos_theta**2 * sin_2phi\n        elif mode == -1:\n            angular_rr = 2.0 * sin_theta * cos_theta * sin_phi\n            angular_rtheta = (cos_theta**2 - sin_theta**2) * sin_phi\n            angular_rphi = cos_theta * cos_phi\n            angular_theta_theta_1 = -2.0 * sin_theta * cos_theta * sin_phi\n            angular_theta_theta_2 = 0.0\n            angular_thetaphi = sin_theta * cos_phi\n            angular_phi_phi_1 = -angular_theta_theta_1\n            angular_phi_phi_2 = -2.0 * sin_theta * cos_theta * sin_phi\n        elif mode == 0:\n            angular_rr = 2.0 - 3.0 * sin_theta**2\n            angular_rtheta = -3.0 * sin_theta * cos_theta\n            angular_rphi = 0.0\n            angular_theta_theta_1 = 3.0 * sin_theta**2\n            angular_theta_theta_2 = -1.0\n            angular_thetaphi = 0.0\n            angular_phi_phi_1 = -angular_theta_theta_1\n            angular_phi_phi_2 = 3.0 * sin_theta**2 - 1.0\n        elif mode == 1:\n            angular_rr = 2.0 * sin_theta * cos_theta * cos_phi\n            angular_rtheta = (cos_theta**2 - sin_theta**2) * cos_phi\n            angular_rphi = -cos_theta * sin_phi\n            angular_theta_theta_1 = -2.0 * sin_theta * cos_theta * cos_phi\n            angular_theta_theta_2 = 0.0\n            angular_thetaphi = -sin_theta * sin_phi\n            angular_phi_phi_1 = -angular_theta_theta_1\n            angular_phi_phi_2 = -2.0 * sin_theta * cos_theta * cos_phi\n        elif mode == 2:\n            angular_rr = sin_theta**2 * cos_2phi\n            angular_rtheta = sin_theta * cos_theta * cos_2phi\n            angular_rphi = -sin_theta * sin_2phi\n            angular_theta_theta_1 = (1.0 + cos_theta**2) * cos_2phi\n            angular_theta_theta_2 = -cos_2phi\n            angular_thetaphi = cos_theta * sin_2phi\n            angular_phi_phi_1 = -angular_theta_theta_1\n            angular_phi_phi_2 = cos_theta**2 * cos_2phi\n        else:\n            raise RuntimeError(\"Unsupported Teukolsky mode\")\n\n        radial_a = (\n            3.0\n            * (\n                profile_2\n                + (-3.0 * profile_1 + 3.0 * profile / radius_from_center)\n                / radius_from_center\n            )\n            / radius_from_center**3\n        )\n        radial_b = (\n            -(\n                -profile_3\n                + (\n                    3.0 * profile_2\n                    + (-6.0 * profile_1 + 6.0 * profile / radius_from_center)\n                    / radius_from_center\n                )\n                / radius_from_center\n            )\n            / radius_from_center**2\n        )\n        radial_c = (\n            0.25\n            * (\n                profile_4\n                + (\n                    -2.0 * profile_3\n                    + (\n                        9.0 * profile_2\n                        + (\n                            -21.0 * profile_1\n                            + 21.0 * profile / radius_from_center\n                        )\n                        / radius_from_center\n                    )\n                    / radius_from_center\n                )\n                / radius_from_center\n            )\n            / radius_from_center\n        )\n\n        spherical_metric[0, 0] += radial_a * angular_rr\n        spherical_metric[0, 1] += radius_from_center * radial_b * angular_rtheta\n        spherical_metric[0, 2] += (\n            radius_from_center * radial_b * angular_rphi * sin_theta\n        )\n        spherical_metric[1, 1] += radius_from_center**2 * (\n            radial_c * angular_theta_theta_1 + radial_a * angular_theta_theta_2\n        )\n        spherical_metric[1, 2] += (\n            radius_from_center**2\n            * (radial_a - 2.0 * radial_c)\n            * angular_thetaphi\n            * sin_theta\n        )\n        spherical_metric[2, 2] += (\n            radius_from_center**2\n            * (radial_c * angular_phi_phi_1 + radial_a * angular_phi_phi_2)\n            * sin_theta**2\n        )\n\n        propagation_sign = 1.0 if direction == \"ingoing\" else -1.0\n        dt_radial_a = (\n            propagation_sign\n            * 3.0\n            * (\n                profile_3\n                + (-3.0 * profile_2 + 3.0 * profile_1 / radius_from_center)\n                / radius_from_center\n            )\n            / radius_from_center**3\n        )\n        dt_radial_b = (\n            -propagation_sign\n            * (\n                -profile_4\n                + (\n                    3.0 * profile_3\n                    + (-6.0 * profile_2 + 6.0 * profile_1 / radius_from_center)\n                    / radius_from_center\n                )\n                / radius_from_center\n            )\n            / radius_from_center**2\n        )\n        dt_radial_c = (\n            propagation_sign\n            * 0.25\n            * (\n                profile_5\n                + (\n                    -2.0 * profile_4\n                    + (\n                        9.0 * profile_3\n                        + (\n                            -21.0 * profile_2\n                            + 21.0 * profile_1 / radius_from_center\n                        )\n                        / radius_from_center\n                    )\n                    / radius_from_center\n                )\n                / radius_from_center\n            )\n            / radius_from_center\n        )\n\n        dt_spherical_metric[0, 0] += dt_radial_a * angular_rr\n        dt_spherical_metric[0, 1] += (\n            radius_from_center * dt_radial_b * angular_rtheta\n        )\n        dt_spherical_metric[0, 2] += (\n            radius_from_center * dt_radial_b * angular_rphi * sin_theta\n        )\n        dt_spherical_metric[1, 1] += radius_from_center**2 * (\n            dt_radial_c * angular_theta_theta_1\n            + dt_radial_a * angular_theta_theta_2\n        )\n        dt_spherical_metric[1, 2] += (\n            radius_from_center**2\n            * (dt_radial_a - 2.0 * dt_radial_c)\n            * angular_thetaphi\n            * sin_theta\n        )\n        dt_spherical_metric[2, 2] += (\n            radius_from_center**2\n            * (\n                dt_radial_c * angular_phi_phi_1\n                + dt_radial_a * angular_phi_phi_2\n            )\n            * sin_theta**2\n        )\n    else:\n        if mode == -2:\n            angular_rtheta = 4.0 * sin_theta * sin_2phi\n            angular_rphi = 4.0 * sin_theta * cos_theta * cos_2phi\n            angular_theta_theta = -2.0 * cos_theta * sin_2phi\n            angular_thetaphi = -(2.0 - sin_theta**2) * cos_2phi\n            angular_phi_phi = 2.0 * cos_theta * sin_2phi\n        elif mode == -1:\n            angular_rtheta = -2.0 * cos_theta * sin_phi\n            angular_rphi = -2.0 * (cos_theta**2 - sin_theta**2) * cos_phi\n            angular_theta_theta = -sin_theta * sin_phi\n            angular_thetaphi = -cos_theta * sin_theta * cos_phi\n            angular_phi_phi = sin_theta * sin_phi\n        elif mode == 0:\n            angular_rtheta = 0.0\n            angular_rphi = -4.0 * cos_theta * sin_theta\n            angular_theta_theta = 0.0\n            angular_thetaphi = -(sin_theta**2)\n            angular_phi_phi = 0.0\n        elif mode == 1:\n            angular_rtheta = -2.0 * cos_theta * cos_phi\n            angular_rphi = 2.0 * (cos_theta**2 - sin_theta**2) * sin_phi\n            angular_theta_theta = -sin_theta * cos_phi\n            angular_thetaphi = cos_theta * sin_theta * sin_phi\n            angular_phi_phi = sin_theta * cos_phi\n        elif mode == 2:\n            angular_rtheta = 4.0 * sin_theta * cos_2phi\n            angular_rphi = -4.0 * sin_theta * cos_theta * sin_2phi\n            angular_theta_theta = -2.0 * cos_theta * cos_2phi\n            angular_thetaphi = (2.0 - sin_theta**2) * sin_2phi\n            angular_phi_phi = 2.0 * cos_theta * cos_2phi\n        else:\n            raise RuntimeError(\"Unsupported Teukolsky mode\")\n\n        radial_k = (\n            profile_2\n            + (-3.0 * profile_1 + 3.0 * profile / radius_from_center)\n            / radius_from_center\n        ) / radius_from_center**2\n        radial_l = (\n            -profile_3\n            + (\n                2.0 * profile_2\n                + (-3.0 * profile_1 + 3.0 * profile / radius_from_center)\n                / radius_from_center\n            )\n            / radius_from_center\n        ) / radius_from_center\n\n        spherical_metric[0, 1] += radius_from_center * radial_k * angular_rtheta\n        spherical_metric[0, 2] += (\n            radius_from_center * radial_k * angular_rphi * sin_theta\n        )\n        spherical_metric[1, 1] += (\n            radius_from_center**2 * radial_l * angular_theta_theta\n        )\n        spherical_metric[1, 2] += (\n            radius_from_center**2 * radial_l * angular_thetaphi * sin_theta\n        )\n        spherical_metric[2, 2] += (\n            radius_from_center**2\n            * radial_l\n            * angular_phi_phi\n            * sin_theta**2\n        )\n\n        propagation_sign = 1.0 if direction == \"ingoing\" else -1.0\n        dt_radial_k = (\n            propagation_sign\n            * (\n                profile_3\n                + (-3.0 * profile_2 + 3.0 * profile_1 / radius_from_center)\n                / radius_from_center\n            )\n            / radius_from_center**2\n        )\n        dt_radial_l = (\n            propagation_sign\n            * (\n                -profile_4\n                + (\n                    2.0 * profile_3\n                    + (-3.0 * profile_2 + 3.0 * profile_1 / radius_from_center)\n                    / radius_from_center\n                )\n                / radius_from_center\n            )\n            / radius_from_center\n        )\n\n        dt_spherical_metric[0, 1] += (\n            radius_from_center * dt_radial_k * angular_rtheta\n        )\n        dt_spherical_metric[0, 2] += (\n            radius_from_center * dt_radial_k * angular_rphi * sin_theta\n        )\n        dt_spherical_metric[1, 1] += (\n            radius_from_center**2 * dt_radial_l * angular_theta_theta\n        )\n        dt_spherical_metric[1, 2] += (\n            radius_from_center**2 * dt_radial_l * angular_thetaphi * sin_theta\n        )\n        dt_spherical_metric[2, 2] += (\n            radius_from_center**2\n            * dt_radial_l\n            * angular_phi_phi\n            * sin_theta**2\n        )\n\n    spherical_metric = spherical_metric + np.triu(spherical_metric, 1).T\n    dt_spherical_metric = (\n        dt_spherical_metric + np.triu(dt_spherical_metric, 1).T\n    )\n\n    sin_theta_for_phi = max(sin_theta, AXIS_COORDINATE_TOLERANCE)\n    inverse_jacobian = np.array(\n        [\n            [\n                sin_theta * cos_phi,\n                sin_theta * sin_phi,\n                cos_theta,\n            ],\n            [\n                cos_theta * cos_phi / radius_from_center,\n                cos_theta * sin_phi / radius_from_center,\n                -sin_theta / radius_from_center,\n            ],\n            [\n                -sin_phi / (radius_from_center * sin_theta_for_phi),\n                cos_phi / (radius_from_center * sin_theta_for_phi),\n                0.0,\n            ],\n        ]\n    )\n    perturbation = np.einsum(\n        \"ai,bj,ab->ij\", inverse_jacobian, inverse_jacobian, spherical_metric\n    )\n    dt_perturbation = np.einsum(\n        \"ai,bj,ab->ij\", inverse_jacobian, inverse_jacobian, dt_spherical_metric\n    )\n    return perturbation, dt_perturbation\n\n\ndef _pointwise_metric(\n    x,\n    t,\n    amplitude,\n    mode,\n    parity,\n    direction,\n    center,\n    radius,\n    width,\n    include_minkowski_background,\n):\n    spatial_metric = np.zeros((3, 3))\n    if include_minkowski_background:\n        spatial_metric += np.eye(3)\n    dt_spatial_metric = np.zeros((3, 3))\n\n    centered_coords = np.asarray(x) - np.asarray(center)\n    radius_from_center = np.sqrt(np.sum(centered_coords**2))\n    if radius_from_center <= ORIGIN_EXCLUSION_RADIUS:\n        raise RuntimeError(\n            \"TeukolskyWave cannot be evaluated at points with radius <= 0.1 \"\n            \"from its center because the implementation uses spherical \"\n            \"coordinates, which are singular at the origin.\"\n        )\n\n    cylindrical_radius = np.sqrt(\n        centered_coords[0] ** 2 + centered_coords[1] ** 2\n    )\n    if cylindrical_radius > AXIS_COORDINATE_TOLERANCE:\n        perturbation, dt_perturbation = _perturbation_and_dt_perturbation(\n            centered_coords,\n            t,\n            amplitude,\n            mode,\n            parity,\n            direction,\n            radius,\n            width,\n        )\n    else:\n        perturbation_x, dt_perturbation_x = _perturbation_and_dt_perturbation(\n            centered_coords + np.array([AXIS_LIMIT_OFFSET, 0.0, 0.0]),\n            t,\n            amplitude,\n            mode,\n            parity,\n            direction,\n            radius,\n            width,\n        )\n        perturbation_y, dt_perturbation_y = _perturbation_and_dt_perturbation(\n            centered_coords + np.array([0.0, AXIS_LIMIT_OFFSET, 0.0]),\n            t,\n            amplitude,\n            mode,\n            parity,\n            direction,\n            radius,\n            width,\n        )\n        perturbation = 0.5 * (perturbation_x + perturbation_y)\n        dt_perturbation = 0.5 * (dt_perturbation_x + dt_perturbation_y)\n\n    return spatial_metric + perturbation, dt_spatial_metric + dt_perturbation\n\n\ndef _variables(\n    x,\n    t,\n    amplitude,\n    mode,\n    parity,\n    direction,\n    center,\n    radius,\n    width,\n    include_minkowski_background,\n):\n    spatial_metric, dt_spatial_metric = _pointwise_metric(\n        np.asarray(x, dtype=float),\n        t,\n        amplitude,\n        mode,\n        parity,\n        direction,\n        center,\n        radius,\n        width,\n        include_minkowski_background,\n    )\n    lapse = 1.0 if include_minkowski_background else 0.0\n    dt_lapse = 0.0\n    shift = np.zeros(3)\n    dt_shift = np.zeros(3)\n\n    result = {\n        \"Lapse\": lapse,\n        \"dt(Lapse)\": dt_lapse,\n        \"Shift\": shift,\n        \"dt(Shift)\": dt_shift,\n        \"SpatialMetric\": spatial_metric,\n        \"dt(SpatialMetric)\": dt_spatial_metric,\n    }\n\n    if include_minkowski_background:\n        extrinsic_curvature = -0.5 * dt_spatial_metric\n        result[\"SqrtDetSpatialMetric\"] = np.sqrt(np.linalg.det(spatial_metric))\n        result[\"ExtrinsicCurvature\"] = extrinsic_curvature\n        result[\"InverseSpatialMetric\"] = np.linalg.inv(spatial_metric)\n\n    return result\n\n\ndef teukolsky_wave_variables(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return _variables(\n        x,\n        t,\n        amplitude,\n        mode,\n        parity,\n        direction,\n        center,\n        radius,\n        width,\n        True,\n    )\n\n\ndef teukolsky_wave_variables_no_background(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return _variables(\n        x,\n        t,\n        amplitude,\n        mode,\n        parity,\n        direction,\n        center,\n        radius,\n        width,\n        False,\n    )\n\n\ndef teukolsky_wave_lapse(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"Lapse\"]\n\n\ndef teukolsky_wave_dt_lapse(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"dt(Lapse)\"]\n\n\ndef teukolsky_wave_shift(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"Shift\"]\n\n\ndef teukolsky_wave_dt_shift(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"dt(Shift)\"]\n\n\ndef teukolsky_wave_spatial_metric(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"SpatialMetric\"]\n\n\ndef teukolsky_wave_dt_spatial_metric(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"dt(SpatialMetric)\"]\n\n\ndef teukolsky_wave_sqrt_det_spatial_metric(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"SqrtDetSpatialMetric\"]\n\n\ndef teukolsky_wave_extrinsic_curvature(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"ExtrinsicCurvature\"]\n\n\ndef teukolsky_wave_inverse_spatial_metric(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"InverseSpatialMetric\"]\n\n\ndef teukolsky_wave_no_background_lapse(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables_no_background(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"Lapse\"]\n\n\ndef teukolsky_wave_no_background_dt_lapse(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables_no_background(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"dt(Lapse)\"]\n\n\ndef teukolsky_wave_no_background_shift(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables_no_background(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"Shift\"]\n\n\ndef teukolsky_wave_no_background_dt_shift(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables_no_background(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"dt(Shift)\"]\n\n\ndef teukolsky_wave_no_background_spatial_metric(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables_no_background(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"SpatialMetric\"]\n\n\ndef teukolsky_wave_no_background_dt_spatial_metric(\n    x, t, amplitude, mode, parity, direction, center, radius, width\n):\n    return teukolsky_wave_variables_no_background(\n        x, t, amplitude, mode, parity, direction, center, radius, width\n    )[\"dt(SpatialMetric)\"]\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/TrumpetSchwarzschild.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport importlib.util\n\n\ndef check_import() -> bool:\n    has_integrate = importlib.util.find_spec(\"scipy.integrate\") is not None\n    has_optimize = importlib.util.find_spec(\"scipy.optimize\") is not None\n\n    if has_integrate and has_optimize:\n        try:\n            module = importlib.import_module(\"scipy.integrate\")\n            getattr(module, \"tanhsinh\")\n            module = importlib.import_module(\"scipy.optimize\")\n            getattr(module, \"root_scalar\")\n        except (ImportError, AttributeError):\n            return False\n\n    return has_integrate and has_optimize\n\n\ndef trumpet_schwarzschild_variables(x, t, mass, n):\n    import numpy as np\n\n    # for fixed pts check, we hardcode values obtained by the same python\n    # functions above\n    if np.array_equal(\n        x,\n        np.array([0.5680968170041150, 0.4970452133901940, 0.2830205552189050]),\n    ):\n        return {\n            \"Lapse\": 0.2102394708993822,\n            \"dt(Lapse)\": 0.0000000000000000,\n            \"deriv(Lapse)\": np.array(\n                [0.1629867952800197, 0.1426021129056149, 0.0811985068614934]\n            ),\n            \"Shift\": np.array(\n                [0.0918707439523423, 0.0803805129078502, 0.0457691509325556]\n            ),\n            \"dt(Shift)\": np.array(\n                [0.0000000000000000, 0.0000000000000000, 0.0000000000000000]\n            ),\n            \"deriv(Shift)\": np.array(\n                [\n                    [\n                        0.1185516082013154,\n                        -0.0377664561206673,\n                        -0.0215044488750177,\n                    ],\n                    [\n                        -0.0377664561206673,\n                        0.1286736794202534,\n                        -0.0188148974963263,\n                    ],\n                    [\n                        -0.0215044488750177,\n                        -0.0188148974963263,\n                        0.1510033859095480,\n                    ],\n                ]\n            ),\n            \"SpatialMetric\": np.array(\n                [\n                    [\n                        16.2297675079970318,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        16.2297675079970318,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        16.2297675079970318,\n                    ],\n                ]\n            ),\n            \"dt(SpatialMetric)\": np.array(\n                [\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                ]\n            ),\n            \"deriv(SpatialMetric)\": np.array(\n                [\n                    [\n                        [\n                            -22.4089328123191152,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -22.4089328123191152,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -22.4089328123191152,\n                        ],\n                    ],\n                    [\n                        [\n                            -19.6062580499636816,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -19.6062580499636816,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -19.6062580499636816,\n                        ],\n                    ],\n                    [\n                        [\n                            -11.1639220931592629,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -11.1639220931592629,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -11.1639220931592629,\n                        ],\n                    ],\n                ]\n            ),\n            \"SqrtDetSpatialMetric\": 65.3835426193290488,\n            \"ExtrinsicCurvature\": np.array(\n                [\n                    [\n                        -0.7075730434939116,\n                        -2.9154411387039083,\n                        -1.6600698438599941,\n                    ],\n                    [\n                        -2.9154411387039083,\n                        0.0738162069640711,\n                        -1.4524456837047197,\n                    ],\n                    [\n                        -1.6600698438599941,\n                        -1.4524456837047197,\n                        1.7975931138062093,\n                    ],\n                ]\n            ),\n            \"InverseSpatialMetric\": np.array(\n                [\n                    [\n                        0.0616151771433116,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0616151771433116,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0616151771433116,\n                    ],\n                ]\n            ),\n        }\n    elif np.array_equal(\n        x,\n        np.array([4.1805906002464903, 3.0296489917597702, 4.8486321865578601]),\n    ):\n        return {\n            \"Lapse\": 0.7570367089032295,\n            \"dt(Lapse)\": 0.0000000000000000,\n            \"deriv(Lapse)\": np.array(\n                [0.0169496724561394, 0.0122833262038080, 0.0196581620351931]\n            ),\n            \"Shift\": np.array(\n                [0.0389517377879063, 0.0282280912915661, 0.0451760690342816]\n            ),\n            \"dt(Shift)\": np.array(\n                [0.0000000000000000, 0.0000000000000000, 0.0000000000000000]\n            ),\n            \"deriv(Shift)\": np.array(\n                [\n                    [\n                        0.0022751608103024,\n                        -0.0051033824348369,\n                        -0.0081674228272535,\n                    ],\n                    [\n                        -0.0051033824348369,\n                        0.0056188903879402,\n                        -0.0059188824498637,\n                    ],\n                    [\n                        -0.0081674228272535,\n                        -0.0059188824498637,\n                        -0.0001552630897424,\n                    ],\n                ]\n            ),\n            \"SpatialMetric\": np.array(\n                [\n                    [\n                        1.6912774453505286,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        1.6912774453505286,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        1.6912774453505286,\n                    ],\n                ]\n            ),\n            \"dt(SpatialMetric)\": np.array(\n                [\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                ]\n            ),\n            \"deriv(SpatialMetric)\": np.array(\n                [\n                    [\n                        [\n                            -0.0684887677153134,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0684887677153134,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -0.0684887677153134,\n                        ],\n                    ],\n                    [\n                        [\n                            -0.0496334001333051,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0496334001333051,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -0.0496334001333051,\n                        ],\n                    ],\n                    [\n                        [\n                            -0.0794329977067292,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0794329977067292,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -0.0794329977067292,\n                        ],\n                    ],\n                ]\n            ),\n            \"SqrtDetSpatialMetric\": 2.1994914891050010,\n            \"ExtrinsicCurvature\": np.array(\n                [\n                    [\n                        0.0000254763514301,\n                        -0.0114013435617176,\n                        -0.0182466422723242,\n                    ],\n                    [\n                        -0.0114013435617176,\n                        0.0074956219223144,\n                        -0.0132232324686586,\n                    ],\n                    [\n                        -0.0182466422723242,\n                        -0.0132232324686586,\n                        -0.0054042750416586,\n                    ],\n                ]\n            ),\n            \"InverseSpatialMetric\": np.array(\n                [\n                    [\n                        0.5912690450340297,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.5912690450340297,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.5912690450340297,\n                    ],\n                ]\n            ),\n        }\n    elif np.array_equal(\n        x,\n        np.array(\n            [51.5006439411830002, 33.8820166534259002, 13.9650020497035996]\n        ),\n    ):\n        return {\n            \"Lapse\": 0.9688533839546105,\n            \"dt(Lapse)\": 0.0000000000000000,\n            \"deriv(Lapse)\": np.array(\n                [0.0003951648239540, 0.0002599769657511, 0.0001071535645805]\n            ),\n            \"Shift\": np.array(\n                [0.0015024323928771, 0.0009884427739242, 0.0004074021185062]\n            ),\n            \"dt(Shift)\": np.array(\n                [0.0000000000000000, 0.0000000000000000, 0.0000000000000000]\n            ),\n            \"deriv(Shift)\": np.array(\n                [\n                    [\n                        -0.0000268204747819,\n                        -0.0000368378801109,\n                        -0.0000151833073137,\n                    ],\n                    [\n                        -0.0000368378801109,\n                        0.0000049376222514,\n                        -0.0000099890221148,\n                    ],\n                    [\n                        -0.0000151833073137,\n                        -0.0000099890221148,\n                        0.0000250559483605,\n                    ],\n                ]\n            ),\n            \"SpatialMetric\": np.array(\n                [\n                    [\n                        1.0647991578520684,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        1.0647991578520684,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        1.0647991578520684,\n                    ],\n                ]\n            ),\n            \"dt(SpatialMetric)\": np.array(\n                [\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                ]\n            ),\n            \"deriv(SpatialMetric)\": np.array(\n                [\n                    [\n                        [\n                            -0.0008550051140581,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0008550051140581,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -0.0008550051140581,\n                        ],\n                    ],\n                    [\n                        [\n                            -0.0005625035979427,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0005625035979427,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -0.0005625035979427,\n                        ],\n                    ],\n                    [\n                        [\n                            -0.0002318446383693,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0002318446383693,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -0.0002318446383693,\n                        ],\n                    ],\n                ]\n            ),\n            \"SqrtDetSpatialMetric\": 1.0987567307255579,\n            \"ExtrinsicCurvature\": np.array(\n                [\n                    [\n                        -0.0000304751383919,\n                        -0.0000404859438680,\n                        -0.0000166869137361,\n                    ],\n                    [\n                        -0.0000404859438680,\n                        0.0000044279703096,\n                        -0.0000109782372769,\n                    ],\n                    [\n                        -0.0000166869137361,\n                        -0.0000109782372769,\n                        0.0000265386188899,\n                    ],\n                ]\n            ),\n            \"InverseSpatialMetric\": np.array(\n                [\n                    [\n                        0.9391442438940482,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.9391442438940482,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.9391442438940482,\n                    ],\n                ]\n            ),\n        }\n    elif np.array_equal(\n        x,\n        np.array([0.0487902462212653, 0.0127925396464956, 0.0508251522757783]),\n    ):\n        return {\n            \"Lapse\": 0.0180386580327798,\n            \"dt(Lapse)\": 0.0000000000000000,\n            \"deriv(Lapse)\": np.array(\n                [0.1844554327385223, 0.0483632205014491, 0.1921485580235478]\n            ),\n            \"Shift\": np.array(\n                [0.0129145602077450, 0.0033861280946483, 0.0134531907495737]\n            ),\n            \"dt(Shift)\": np.array(\n                [0.0000000000000000, 0.0000000000000000, 0.0000000000000000]\n            ),\n            \"deriv(Shift)\": np.array(\n                [\n                    [\n                        0.2592362314285327,\n                        -0.0014313993410471,\n                        -0.0056869934732700,\n                    ],\n                    [\n                        -0.0014313993410471,\n                        0.2643202273757710,\n                        -0.0014910990435719,\n                    ],\n                    [\n                        -0.0056869934732700,\n                        -0.0014910990435719,\n                        0.2587713503477229,\n                    ],\n                ]\n            ),\n            \"SpatialMetric\": np.array(\n                [\n                    [\n                        1389.2313517296529426,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        1389.2313517296529426,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        1389.2313517296529426,\n                    ],\n                ]\n            ),\n            \"dt(SpatialMetric)\": np.array(\n                [\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                ]\n            ),\n            \"deriv(SpatialMetric)\": np.array(\n                [\n                    [\n                        [\n                            -25962.1361093563755276,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -25962.1361093563755276,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -25962.1361093563755276,\n                        ],\n                    ],\n                    [\n                        [\n                            -6807.1321874555487739,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -6807.1321874555487739,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -6807.1321874555487739,\n                        ],\n                    ],\n                    [\n                        [\n                            -27044.9449092429713346,\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -27044.9449092429713346,\n                            -0.0000000000000000,\n                        ],\n                        [\n                            -0.0000000000000000,\n                            -0.0000000000000000,\n                            -27044.9449092429713346,\n                        ],\n                    ],\n                ]\n            ),\n            \"SqrtDetSpatialMetric\": 51779.9782473547966219,\n            \"ExtrinsicCurvature\": np.array(\n                [\n                    [\n                        -52.7199338403740967,\n                        -110.2379588223377738,\n                        -437.9787906501542807,\n                    ],\n                    [\n                        -110.2379588223377738,\n                        338.8195337372775953,\n                        -114.8356788016019578,\n                    ],\n                    [\n                        -437.9787906501542807,\n                        -114.8356788016019578,\n                        -88.5223405999799411,\n                    ],\n                ]\n            ),\n            \"InverseSpatialMetric\": np.array(\n                [\n                    [\n                        0.0007198225110274,\n                        0.0000000000000000,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0007198225110274,\n                        0.0000000000000000,\n                    ],\n                    [\n                        0.0000000000000000,\n                        0.0000000000000000,\n                        0.0007198225110274,\n                    ],\n                ]\n            ),\n        }\n\n    # for random pts check, we simply calculate the spacetime quantities\n    # using the python functions above\n    else:\n        integrate = importlib.import_module(\"scipy.integrate\")\n        tanhsinh = getattr(integrate, \"tanhsinh\")\n        optimize = importlib.import_module(\"scipy.optimize\")\n        root_scalar = getattr(optimize, \"root_scalar\")\n\n        threshold_lapse = 0.1\n        max_isotropic_r = 5000 * mass\n\n        def crit_lapse(n):\n            return np.sqrt(\n                (np.sqrt(4 + 9 * n * n) - 3 * n)\n                / (np.sqrt(4 + 9 * n * n) + 3 * n)\n            )\n\n        crit_lapse_ = crit_lapse(n)\n\n        def c_squared(n):\n            numerator = 3 * n + np.sqrt(4 + 9 * n**2)\n            return (\n                numerator**3\n                / (128 * n**3)\n                * np.exp(-2 * crit_lapse_ / n)\n                * mass**4\n            )\n\n        c_squared_ = c_squared(n)\n\n        def crit_schwarzschild_r(n, mass):\n            return (\n                3 * n * n * mass\n                + np.sqrt(4 * n * n * mass * mass + 9 * n**4 * mass * mass)\n            ) / (4 * n * n)\n\n        crit_schwarzschild_r_ = crit_schwarzschild_r(n, mass)\n\n        def func_of_lapse_and_schwarzschild_r(schwarzschild_r, n, mass, lapse):\n            return (\n                (1 - lapse * lapse) * schwarzschild_r**4\n                - 2 * mass * schwarzschild_r**3\n                + c_squared_ * np.exp(2 * lapse / n)\n            )\n\n        # This solver may fail when lapse is near crit_lapse,\n        # where two branches of solution cross.\n        # Not a problem in SpECTRE as we ensure the solver succeeds\n        # when initializing the source grid. Then we\n        # simply interpolate to the user requested grid pts.\n\n        def schwarzschild_r_from_lapse(n, mass, lapse):\n            lapse_arr = np.asarray(lapse)\n            sol = np.empty_like(lapse_arr, dtype=float)\n            for idx, l in np.ndenumerate(lapse_arr):\n                if l == crit_lapse_:\n                    return crit_schwarzschild_r_\n                elif l > crit_lapse_:\n                    lower_bound = crit_schwarzschild_r_\n                    upper_bound = max(max_isotropic_r, 4 * mass / (1 - l * l))\n                else:\n                    lower_bound = 0.0\n                    upper_bound = crit_schwarzschild_r_\n\n                sol[idx] = root_scalar(\n                    func_of_lapse_and_schwarzschild_r,\n                    args=(n, mass, l),\n                    bracket=[lower_bound, upper_bound],\n                    xtol=1e-15,\n                    rtol=1e-15,\n                    maxiter=100,\n                ).root\n            return sol\n\n        def first_integrand_above_threshold(lapse, mass, n):\n            return np.log(schwarzschild_r_from_lapse(n, mass, lapse)) / (\n                lapse * lapse\n            )\n\n        def first_integral_above_threshold(mass, lapse_upper_bound, n):\n            res = tanhsinh(\n                lambda lapse: first_integrand_above_threshold(lapse, mass, n),\n                threshold_lapse,\n                lapse_upper_bound,\n            )\n\n            return res.integral\n\n        def c_0(mass, n):\n            return first_integral_above_threshold(mass, 1.0, n)\n\n        c_0_ = c_0(mass, n)\n\n        def _raw_d_lapse_d_schwarzschild_r(n, mass, lapse, schwarzschild_r):\n            return (\n                -n\n                * (\n                    3 * mass\n                    - 2 * schwarzschild_r\n                    + 2 * schwarzschild_r * lapse * lapse\n                )\n                / (\n                    schwarzschild_r\n                    * (\n                        schwarzschild_r\n                        - 2 * mass\n                        + n * schwarzschild_r * lapse\n                        - schwarzschild_r * lapse * lapse\n                    )\n                )\n            )\n\n        # This function is undefined near the critical point corresponding to\n        # critical lapse or critical schwarzschild r, where two solution\n        # branches of func_of_lapse_and_schwarzschild_r cross. However, our\n        # chosen branch should be continuously differentiable. Here, we fix\n        # this issue by linearly interpolating from two neighboring pts.\n        # In SpECTRE, we demand no source grid point is nearly critical before\n        # interpolating values to user-requested grid pts (which can be nearly\n        # critical). The unit test may fail if one deliberately requests a\n        # nearly critical grid point, and it merely reflects that this function\n        # here no longer is exact.\n        # This subtlety should bear little practical significance.\n        def get_d_lapse_d_schwarzschild_r(\n            n, mass, lapse, schwarzschild_r, tol=1e-6\n        ):\n            too_close_to_crit = np.abs(lapse - crit_lapse_) < tol\n            too_close_to_crit &= (\n                np.abs(schwarzschild_r - crit_schwarzschild_r_) < tol\n            )\n\n            if not np.any(too_close_to_crit):\n                return _raw_d_lapse_d_schwarzschild_r(\n                    n, mass, lapse, schwarzschild_r\n                )\n            else:\n                temp_lapse = np.asarray(lapse, dtype=float)\n                temp_schwarzschild_r = np.asarray(schwarzschild_r, dtype=float)\n\n                # compute the full derivative array\n                deriv = _raw_d_lapse_d_schwarzschild_r(\n                    n, mass, temp_lapse, temp_schwarzschild_r\n                )\n\n                # average at the \"too close\" entries from neighbors\n                temp_lapse_p = temp_lapse[too_close_to_crit] + tol\n                temp_lapse_m = temp_lapse[too_close_to_crit] - tol\n                temp_schwarzschild_r_p = schwarzschild_r_from_lapse(\n                    n, mass, temp_lapse_p\n                )\n                temp_schwarzschild_r_m = schwarzschild_r_from_lapse(\n                    n, mass, temp_lapse_m\n                )\n\n                deriv_p = _raw_d_lapse_d_schwarzschild_r(\n                    n, mass, temp_lapse_p, temp_schwarzschild_r_p\n                )\n                deriv_m = _raw_d_lapse_d_schwarzschild_r(\n                    n, mass, temp_lapse_m, temp_schwarzschild_r_m\n                )\n\n                deriv[too_close_to_crit] = 0.5 * (deriv_p + deriv_m)\n\n                return deriv\n\n        def first_integrand_below_threshold(lapse, mass, n):\n            schwarzschild_r = schwarzschild_r_from_lapse(n, mass, lapse)\n            return (\n                -1.0\n                / get_d_lapse_d_schwarzschild_r(n, mass, lapse, schwarzschild_r)\n                / lapse\n                / schwarzschild_r\n            )\n\n        def first_integral_below_threshold(mass, lapse_lower_bound, n):\n            res = tanhsinh(\n                lambda lapse: first_integrand_below_threshold(lapse, mass, n),\n                lapse_lower_bound,\n                threshold_lapse,\n            )\n\n            return res.integral\n\n        def isotropic_r_from_lapse(lapse, mass, n):\n            if lapse == 0:\n                return 0.0\n            elif lapse > threshold_lapse:\n                r_val = schwarzschild_r_from_lapse(n, mass, lapse) ** (\n                    1.0 / lapse\n                )\n\n                integral = first_integral_above_threshold(mass, lapse, n)\n            else:\n                r_val = schwarzschild_r_from_lapse(\n                    n, mass, threshold_lapse\n                ) ** (1.0 / threshold_lapse)\n                integral = first_integral_below_threshold(mass, lapse, n)\n\n            return r_val * np.exp(integral - c_0_)\n\n        def isotropic_r_from_lapse_minus_target(\n            lapse, mass, n, target_isotropic_r\n        ):\n            return isotropic_r_from_lapse(lapse, mass, n) - target_isotropic_r\n\n        def lapse_from_isotropic_r(target_isotropic_r, mass, n):\n            max_lapse = np.sqrt(1.0 - mass / max_isotropic_r)\n\n            if target_isotropic_r == 0:\n                return 0.0\n            else:\n                # define the function whose root we seek\n                return root_scalar(\n                    isotropic_r_from_lapse_minus_target,\n                    method=\"toms748\",\n                    bracket=[0.0, max_lapse],\n                    args=(mass, n, target_isotropic_r),\n                    xtol=1e-15,\n                    rtol=1e-15,\n                    maxiter=100,\n                ).root\n\n        isotropic_r = np.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2])\n        lapse = lapse_from_isotropic_r(isotropic_r, mass, n)\n        schwarzschild_r = schwarzschild_r_from_lapse(n, mass, lapse)\n        d_lapse_d_schwarzschild_r = get_d_lapse_d_schwarzschild_r(\n            n, mass, lapse, schwarzschild_r\n        )\n        f = 1 - 2 * mass / schwarzschild_r\n        sqrt_of_lapse_squared_minus_f = np.sqrt(lapse * lapse - f)\n\n        def trumpet_schwarzschild_dt_lapse():\n            return 0.0\n\n        def trumpet_schwarzschild_d_lapse():\n            return (\n                lapse\n                * schwarzschild_r\n                * d_lapse_d_schwarzschild_r\n                / (isotropic_r * isotropic_r)\n                * x\n            )\n\n        def trumpet_schwarzschild_shift():\n            return np.sqrt(lapse * lapse - f) / schwarzschild_r * x\n\n        def trumpet_schwarzschild_dt_shift():\n            return np.zeros_like(x)\n\n        def trumpet_schwarzschild_d_shift():\n            isotropic_r_squared = isotropic_r * isotropic_r\n            common_factor = (\n                lapse\n                * lapse\n                * d_lapse_d_schwarzschild_r\n                / (isotropic_r_squared * sqrt_of_lapse_squared_minus_f)\n                - lapse\n                * sqrt_of_lapse_squared_minus_f\n                / (isotropic_r_squared * schwarzschild_r)\n                - lapse\n                * mass\n                / (\n                    isotropic_r_squared\n                    * schwarzschild_r\n                    * schwarzschild_r\n                    * sqrt_of_lapse_squared_minus_f\n                )\n            )\n            diagonal_terms = (\n                np.identity(3) * sqrt_of_lapse_squared_minus_f / schwarzschild_r\n            )\n\n            return common_factor * np.outer(x, x) + diagonal_terms\n\n        def trumpet_schwarzschild_spatial_metric():\n            return (schwarzschild_r / isotropic_r) ** 2 * np.identity(3)\n\n        def trumpet_schwarzschild_dt_spatial_metric():\n            return np.zeros((len(x), len(x)))\n\n        def trumpet_schwarzschild_d_spatial_metric():\n            d_spatial_metric = np.outer(\n                2.0\n                * schwarzschild_r\n                * schwarzschild_r\n                * (lapse - 1)\n                / isotropic_r**4\n                * x,\n                np.identity(3),\n            )\n\n            return d_spatial_metric.reshape(3, 3, 3)\n\n        def trumpet_schwarzschild_sqrt_det_spatial_metric():\n            return (schwarzschild_r / isotropic_r) ** 3\n\n        def trumpet_schwarzschild_extrinsic_curvature():\n            common_factor = (\n                schwarzschild_r\n                * (\n                    (\n                        schwarzschild_r * lapse * d_lapse_d_schwarzschild_r\n                        - mass / schwarzschild_r\n                    )\n                    - (\n                        sqrt_of_lapse_squared_minus_f\n                        * sqrt_of_lapse_squared_minus_f\n                    )\n                )\n                / sqrt_of_lapse_squared_minus_f\n                / isotropic_r**4\n            )\n\n            return schwarzschild_r * sqrt_of_lapse_squared_minus_f / (\n                isotropic_r * isotropic_r\n            ) * np.identity(3) + common_factor * np.outer(x, x)\n\n        def trumpet_schwarzschild_inverse_spatial_metric():\n            return (isotropic_r / schwarzschild_r) ** 2 * np.identity(3)\n\n        return {\n            \"Lapse\": lapse,\n            \"dt(Lapse)\": trumpet_schwarzschild_dt_lapse(),\n            \"deriv(Lapse)\": trumpet_schwarzschild_d_lapse(),\n            \"Shift\": trumpet_schwarzschild_shift(),\n            \"dt(Shift)\": trumpet_schwarzschild_dt_shift(),\n            \"deriv(Shift)\": trumpet_schwarzschild_d_shift(),\n            \"SpatialMetric\": trumpet_schwarzschild_spatial_metric(),\n            \"dt(SpatialMetric)\": trumpet_schwarzschild_dt_spatial_metric(),\n            \"deriv(SpatialMetric)\": trumpet_schwarzschild_d_spatial_metric(),\n            \"SqrtDetSpatialMetric\": (\n                trumpet_schwarzschild_sqrt_det_spatial_metric()\n            ),\n            \"ExtrinsicCurvature\": trumpet_schwarzschild_extrinsic_curvature(),\n            \"InverseSpatialMetric\": (\n                trumpet_schwarzschild_inverse_spatial_metric()\n            ),\n        }\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GhGrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GhGrMhdSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_InstantiateWrappedGr.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  GeneralRelativitySolutions\n  GhGrMhdSolutions\n  GrMhdSolutions\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GhGrMhd/Test_InstantiateWrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/CheckWrappedGrConsistency.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.GhGrMhd.WrappedGr\",\n                  \"[Unit][PointwiseFunctions]\") {\n  const double time = 2.0;\n  const tnsr::I<DataVector, 3, Frame::Inertial> coords{DataVector{3.0, 4.0}};\n\n  check_wrapped_gr_solution_consistency(\n      gh::Solutions::WrappedGr<grmhd::Solutions::BondiMichel>{1.1, 50.0, 1.3,\n                                                              1.5, 0.24},\n      grmhd::Solutions::BondiMichel{1.1, 50.0, 1.3, 1.5, 0.24}, coords, time);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GhRelativisticEulerSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_InstantiateWrappedGr.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  GeneralRelativitySolutions\n  GhRelativisticEulerSolutions\n  RelativisticEulerSolutions\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GhRelativisticEuler/Test_InstantiateWrappedGr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GeneralRelativity/CheckWrappedGrConsistency.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/WrappedGr.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.GhRelativisticEuler.WrappedGr\",\n    \"[Unit][PointwiseFunctions]\") {\n  const double t = 2.0;\n  const tnsr::I<DataVector, 3, Frame::Inertial> x_3d{DataVector{3.0, 4.0}};\n  const tnsr::I<DataVector, 2, Frame::Inertial> x_2d{DataVector{3.0, 4.0}};\n  const tnsr::I<DataVector, 1, Frame::Inertial> x_1d{DataVector{3.0, 4.0}};\n  check_wrapped_gr_solution_consistency(\n      gh::Solutions::WrappedGr<\n          RelativisticEuler::Solutions::FishboneMoncriefDisk>{\n          1.0, 0.23, 6.0, 12.0, 0.001, 1.4, 0.0},\n      RelativisticEuler::Solutions::FishboneMoncriefDisk{1.0, 0.23, 6.0, 12.0,\n                                                         0.001, 1.4, 0.0},\n      x_3d, t);\n\n  check_wrapped_gr_solution_consistency(\n      gh::Solutions::WrappedGr<RelativisticEuler::Solutions::TovStar>{\n          1.e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(8.0, 2.0)},\n      RelativisticEuler::Solutions::TovStar{\n          1.e-3,\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(8.0, 2.0)},\n      x_3d, t);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef rest_mass_density(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    return rest_mass_density\n\n\ndef spatial_velocity(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    magnitude_B0 = np.linalg.norm(bkgd_magnetic_field)\n    magnitude_B1 = np.linalg.norm(wave_magnetic_field)\n    unit_B0 = np.array(bkgd_magnetic_field) / magnitude_B0\n    unit_B1 = np.array(wave_magnetic_field) / magnitude_B1\n    unit_E = np.cross(unit_B1, unit_B0)\n    rho_zero_times_h = rest_mass_density + pressure * (adiabatic_index) / (\n        adiabatic_index - 1.0\n    )\n    aux_speed_b0 = magnitude_B0 / np.sqrt(\n        rho_zero_times_h + magnitude_B0**2 + magnitude_B1**2\n    )\n    aux_speed_b1 = magnitude_B1 * aux_speed_b0 / magnitude_B0\n    one_over_speed_denominator = 1.0 / np.sqrt(\n        0.5 * (1.0 + np.sqrt(1.0 - 4.0 * aux_speed_b0**2 * aux_speed_b1**2))\n    )\n    speed = aux_speed_b0 * one_over_speed_denominator\n    phase = wavenumber * (np.dot(unit_B0, x) - speed * t)\n    fluid_velocity = -aux_speed_b1 * one_over_speed_denominator\n    return fluid_velocity * (np.cos(phase) * unit_B1 - np.sin(phase) * unit_E)\n\n\ndef specific_internal_energy(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    return pressure / (rest_mass_density * (adiabatic_index - 1.0))\n\n\ndef pressure(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    return pressure\n\n\ndef specific_enthalpy(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    return 1.0 + adiabatic_index * specific_internal_energy(\n        x,\n        t,\n        wavenumber,\n        pressure,\n        rest_mass_density,\n        electron_fraction,\n        adiabatic_index,\n        bkgd_magnetic_field,\n        wave_magnetic_field,\n    )\n\n\ndef lorentz_factor(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    return 1.0 / np.sqrt(\n        1.0\n        - np.linalg.norm(\n            spatial_velocity(\n                x,\n                t,\n                wavenumber,\n                pressure,\n                rest_mass_density,\n                electron_fraction,\n                adiabatic_index,\n                bkgd_magnetic_field,\n                wave_magnetic_field,\n            )\n        )\n        ** 2\n    )\n\n\ndef magnetic_field(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    magnitude_B0 = np.linalg.norm(bkgd_magnetic_field)\n    magnitude_B1 = np.linalg.norm(wave_magnetic_field)\n    unit_B0 = np.array(bkgd_magnetic_field) / magnitude_B0\n    unit_B1 = np.array(wave_magnetic_field) / magnitude_B1\n    unit_E = np.cross(unit_B1, unit_B0)\n    rho_zero_times_h = rest_mass_density + pressure * (adiabatic_index) / (\n        adiabatic_index - 1.0\n    )\n    aux_speed_b0 = magnitude_B0 / np.sqrt(\n        rho_zero_times_h + magnitude_B0**2 + magnitude_B1**2\n    )\n    aux_speed_b1 = magnitude_B1 * aux_speed_b0 / magnitude_B0\n    one_over_speed_denominator = 1.0 / np.sqrt(\n        0.5 * (1.0 + np.sqrt(1.0 - 4.0 * aux_speed_b0**2 * aux_speed_b1**2))\n    )\n    speed = aux_speed_b0 * one_over_speed_denominator\n    phase = wavenumber * (np.dot(unit_B0, x) - speed * t)\n    return np.array(bkgd_magnetic_field) + magnitude_B1 * (\n        np.cos(phase) * unit_B1 - np.sin(phase) * unit_E\n    )\n\n\ndef divergence_cleaning_field(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    return 0.0\n\n\ndef electron_fraction(\n    x,\n    t,\n    wavenumber,\n    pressure,\n    rest_mass_density,\n    electron_fraction,\n    adiabatic_index,\n    bkgd_magnetic_field,\n    wave_magnetic_field,\n):\n    return electron_fraction\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nimport scipy.optimize as opt\n\n\ndef sonic_fluid_speed_squared(r_c, mass):\n    return 0.5 * mass / r_c\n\n\ndef sonic_sound_speed_squared(u_c_2):\n    return u_c_2 / (1.0 - 3.0 * u_c_2)\n\n\ndef sonic_newtonian_sound_speed_squared(g, c_s_c_2):\n    return (g - 1.0) * c_s_c_2 / (g - 1.0 - c_s_c_2)\n\n\ndef adiabatic_constant(n_s_c_2, g, rho_c):\n    return n_s_c_2 * pow(rho_c, 1.0 - g) / g\n\n\ndef mass_accretion_rate_over_four_pi(r_c, rho_c, u_c_2):\n    return r_c**2.0 * rho_c * np.sqrt(u_c_2)\n\n\ndef bernoulli_constant_squared_minus_one(n_s_c_2, u_c_2, g):\n    return -3.0 * u_c_2 * (1.0 + n_s_c_2 / (g - 1.0)) ** 2 + (\n        n_s_c_2 / (g - 1.0) * (2.0 + n_s_c_2 / (g - 1.0))\n    )\n\n\ndef bernoulli_equation_lhs_squared_minus_one(\n    rho, r, g, k, m_dot_over_four_pi, mass\n):\n    u = m_dot_over_four_pi / (r**2 * rho)\n    g_minus_one = g - 1.0\n    # polytropic_index times newtonian_sound_speed_squared:\n    ncns2 = k * g * pow(rho, g_minus_one) / g_minus_one\n    # specific enthalpy squared minus one:\n    h2_minus_one = ncns2 * (2.0 + ncns2)\n    h2 = h2_minus_one + 1.0\n    return h2_minus_one + h2 * (-2.0 * mass / r + u**2)\n\n\ndef bernoulli_root_function(\n    rho, r, g, k, m_dot_over_four_pi, n_s_c_2, u_c_2, mass\n):\n    return bernoulli_equation_lhs_squared_minus_one(\n        rho, r, g, k, m_dot_over_four_pi, mass\n    ) - bernoulli_constant_squared_minus_one(n_s_c_2, u_c_2, g)\n\n\ndef sound_speed_at_infinity_squared(g, c_s_c_2):\n    g_minus_one = g - 1.0\n    return g_minus_one + (c_s_c_2 - g_minus_one) * np.sqrt(1.0 + 3.0 * c_s_c_2)\n\n\ndef rest_mass_density_at_infinity(g, rho_c, c_s_inf_2, c_s_c_2):\n    return rho_c * pow(\n        c_s_inf_2 / (c_s_c_2 * np.sqrt(1.0 + 3.0 * c_s_c_2)), 1.0 / (g - 1.0)\n    )\n\n\ndef rest_mass_density_at_horizon(g, rho_c, u_c_2):\n    return 0.0625 * rho_c * pow(u_c_2, -1.5)\n\n\ndef one_over_lapse(mass, radius):\n    return np.sqrt(1.0 + 2.0 * mass / radius)\n\n\ndef shift(mass, radius):\n    return 2.0 * mass / (radius + 2.0 * mass)\n\n\ndef fluid_four_velocity_u_t(mass, radius, abs_u_r):\n    if radius == 2.0 * mass:\n        return abs_u_r + 0.5 / abs_u_r\n    return (\n        -2.0 * mass * abs_u_r\n        + radius * np.sqrt(abs_u_r**2 + 1.0 - 2.0 * mass / radius)\n    ) / (radius - 2.0 * mass)\n\n\ndef intermediate_variables(\n    mass, sonic_radius, sonic_density, adiabatic_exponent\n):\n    u_c_2 = sonic_fluid_speed_squared(sonic_radius, mass)\n    c_s_c_2 = sonic_sound_speed_squared(u_c_2)\n    n_s_c_2 = sonic_newtonian_sound_speed_squared(adiabatic_exponent, c_s_c_2)\n    k = adiabatic_constant(n_s_c_2, adiabatic_exponent, sonic_density)\n    m_dot_over_four_pi = mass_accretion_rate_over_four_pi(\n        sonic_radius, sonic_density, u_c_2\n    )\n    h_inf_2 = (\n        bernoulli_constant_squared_minus_one(n_s_c_2, u_c_2, adiabatic_exponent)\n        + 1.0\n    )\n    c_s_inf_2 = sound_speed_at_infinity_squared(adiabatic_exponent, c_s_c_2)\n    rho_inf = rest_mass_density_at_infinity(\n        adiabatic_exponent, sonic_density, c_s_inf_2, c_s_c_2\n    )\n    rho_horizon = rest_mass_density_at_horizon(\n        adiabatic_exponent, sonic_density, u_c_2\n    )\n    return {\n        \"sonic_fluid_speed_squared\": u_c_2,\n        \"sonic_sound_speed_squared\": c_s_c_2,\n        \"sonic_newtonian_sound_speed_squared\": n_s_c_2,\n        \"adiabatic_constant\": k,\n        \"mass_accretion_rate_over_four_pi\": m_dot_over_four_pi,\n        \"bernoulli_constant_squared\": h_inf_2,\n        \"sound_speed_at_infinity_squared\": c_s_inf_2,\n        \"rest_mass_density_at_infinity\": rho_inf,\n        \"rest_mass_density_at_horizon\": rho_horizon,\n    }\n\n\ndef rest_mass_density(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    variables = intermediate_variables(\n        mass, sonic_radius, sonic_density, adiabatic_exponent\n    )\n    radius = np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)\n    if radius < sonic_radius:\n        upper_bound = variables[\"mass_accretion_rate_over_four_pi\"] * np.sqrt(\n            2.0 / (mass * radius**3)\n        )\n        lower_bound = variables[\"rest_mass_density_at_infinity\"]\n    if radius >= sonic_radius:\n        upper_bound = sonic_density\n        lower_bound = variables[\"mass_accretion_rate_over_four_pi\"] * np.sqrt(\n            2.0 / (mass * radius**3)\n        )\n    return opt.brentq(\n        bernoulli_root_function,\n        lower_bound,\n        upper_bound,\n        xtol=1.0e-15,\n        rtol=1.0e-15,\n        args=(\n            radius,\n            adiabatic_exponent,\n            variables[\"adiabatic_constant\"],\n            variables[\"mass_accretion_rate_over_four_pi\"],\n            variables[\"sonic_newtonian_sound_speed_squared\"],\n            variables[\"sonic_fluid_speed_squared\"],\n            mass,\n        ),\n    )\n\n\ndef electron_fraction(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    return 0.4\n\n\ndef lorentz_factor(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    variables = intermediate_variables(\n        mass, sonic_radius, sonic_density, adiabatic_exponent\n    )\n    r = np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)\n    fluid_speed_u = variables[\"mass_accretion_rate_over_four_pi\"] / (\n        r**2\n        * rest_mass_density(\n            x,\n            mass,\n            sonic_radius,\n            sonic_density,\n            adiabatic_exponent,\n            magnetic_field,\n        )\n    )\n    return fluid_four_velocity_u_t(mass, r, fluid_speed_u) / one_over_lapse(\n        mass, r\n    )\n\n\ndef spatial_velocity(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    variables = intermediate_variables(\n        mass, sonic_radius, sonic_density, adiabatic_exponent\n    )\n    r = np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)\n    fluid_speed_u = variables[\"mass_accretion_rate_over_four_pi\"] / (\n        r**2\n        * rest_mass_density(\n            x,\n            mass,\n            sonic_radius,\n            sonic_density,\n            adiabatic_exponent,\n            magnetic_field,\n        )\n    )\n    # eulerian_radial_velocity_over_radius\n    e = (\n        one_over_lapse(mass, r)\n        * (\n            -fluid_speed_u / fluid_four_velocity_u_t(mass, r, fluid_speed_u)\n            + shift(mass, r)\n        )\n        / r\n    )\n    return np.array([e * x[0], e * x[1], e * x[2]])\n\n\ndef specific_internal_energy(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    rho = rest_mass_density(\n        x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n    )\n    pres = pressure(\n        x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n    )\n    return pres / (rho * (adiabatic_exponent - 1.0))\n\n\ndef specific_enthalpy(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    return 1.0 + adiabatic_exponent * specific_internal_energy(\n        x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n    )\n\n\ndef pressure(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    rho = rest_mass_density(\n        x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n    )\n    variables = intermediate_variables(\n        mass, sonic_radius, sonic_density, adiabatic_exponent\n    )\n    return variables[\"adiabatic_constant\"] * pow(rho, adiabatic_exponent)\n\n\ndef divergence_cleaning_field(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    return 0.0\n\n\ndef magnetic_field(\n    x, mass, sonic_radius, sonic_density, adiabatic_exponent, magnetic_field\n):\n    r = np.sqrt(x[0] ** 2 + x[1] ** 2 + x[2] ** 2)\n    m = magnetic_field * mass**2 / (r**3 * np.sqrt(1.0 + 2.0 * mass / r))\n    return np.array([m * x[0], m * x[1], m * x[2]])\n\n\n# End functions for testing BondiMichelMHD.cpp\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GrMhdSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_AlfvenWave.cpp\n  Test_BondiMichel.cpp\n  Test_KomissarovShock.cpp\n  Test_SmoothFlow.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  GrMhdSolutions\n  Options\n  Utilities\n  ValenciaDivClean\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Functions for Test_KomissarovShock.cpp\n\n\ndef parse_vars(*args, **kwargs):\n    assert not kwargs, \"Found unexpected labeled arguments:\\n{}\".format(kwargs)\n    assert len(args) is 12, \"Expected 12 arguments, but got {}\".format(\n        len(args)\n    )\n    return dict(\n        zip(\n            [\n                \"adiabatic_index\",\n                \"left_rest_mass_density\",\n                \"right_rest_mass_density\",\n                \"left_electron_fraction\",\n                \"right_electron_fraction\",\n                \"left_pressure\",\n                \"right_pressure\",\n                \"left_spatial_velocity\",\n                \"right_spatial_velocity\",\n                \"left_magnetic_field\",\n                \"right_magnetic_field\",\n                \"shock_speed\",\n            ],\n            args,\n        )\n    )\n\n\ndef piecewise(x, shock_position, left_value, right_value):\n    return left_value if x[0] < shock_position else right_value\n\n\ndef rest_mass_density(x, t, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return piecewise(\n        x,\n        t * vars[\"shock_speed\"],\n        vars[\"left_rest_mass_density\"],\n        vars[\"right_rest_mass_density\"],\n    )\n\n\ndef electron_fraction(x, t, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return piecewise(\n        x,\n        t * vars[\"shock_speed\"],\n        vars[\"left_electron_fraction\"],\n        vars[\"right_electron_fraction\"],\n    )\n\n\ndef spatial_velocity(x, t, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return np.asarray(\n        piecewise(\n            x,\n            t * vars[\"shock_speed\"],\n            vars[\"left_spatial_velocity\"],\n            vars[\"right_spatial_velocity\"],\n        )\n    )\n\n\ndef pressure(x, t, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return piecewise(\n        x,\n        t * vars[\"shock_speed\"],\n        vars[\"left_pressure\"],\n        vars[\"right_pressure\"],\n    )\n\n\ndef specific_internal_energy(x, t, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    p = pressure(x, t, *args, **kwargs)\n    rho = rest_mass_density(x, t, *args, **kwargs)\n    return p / ((vars[\"adiabatic_index\"] - 1.0) * rho)\n\n\ndef specific_enthalpy(x, t, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    e = specific_internal_energy(x, t, *args, **kwargs)\n    return 1.0 + vars[\"adiabatic_index\"] * e\n\n\ndef lorentz_factor(x, t, *args, **kwargs):\n    v = spatial_velocity(x, t, *args, **kwargs)\n    return 1.0 / np.sqrt(1.0 - np.sum(v**2))\n\n\ndef magnetic_field(x, t, *args, **kwargs):\n    vars = parse_vars(*args, **kwargs)\n    return np.asarray(\n        piecewise(\n            x,\n            t * vars[\"shock_speed\"],\n            vars[\"left_magnetic_field\"],\n            vars[\"right_magnetic_field\"],\n        )\n    )\n\n\ndef divergence_cleaning_field(x, t, *args, **kwargs):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom Hydro.SmoothFlow import *\n\n\ndef electron_fraction(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return 0.1\n\n\ndef magnetic_field(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return np.array([0.0, 0.0, 0.0])\n\n\ndef divergence_cleaning_field(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/Test_AlfvenWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GrMhd/VerifyGrMhdSolution.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/AlfvenWave.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct AlfvenWaveProxy : grmhd::Solutions::AlfvenWave {\n  using grmhd::Solutions::AlfvenWave::AlfvenWave;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3>& x, double t) const {\n    return variables(x, t, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3>& x, double t) const {\n    return variables(x, t, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  const auto wave = TestHelpers::test_creation<grmhd::Solutions::AlfvenWave>(\n      \"WaveNumber: 2.2\\n\"\n      \"Pressure: 1.23\\n\"\n      \"RestMassDensity: 0.2\\n\"\n      \"ElectronFraction: 0.1\\n\"\n      \"AdiabaticIndex: 1.4\\n\"\n      \"BkgdMagneticField: [0.0, 0.0, 2.0]\\n\"\n      \"WaveMagneticField: [0.75, 0.0, 0.0]\");\n  CHECK(wave == grmhd::Solutions::AlfvenWave(2.2, 1.23, 0.2, 0.1, 1.4,\n                                             {{0.0, 0.0, 2.0}},\n                                             {{0.75, 0.0, 0.0}}));\n}\n\nvoid test_move() {\n  grmhd::Solutions::AlfvenWave wave(3.0, 2.1, 1.3, 0.1, 1.5, {{0.0, 0.0, 0.24}},\n                                    {{0.01, 0.0, 0.0}});\n  const grmhd::Solutions::AlfvenWave wave_copy(\n      3.0, 2.1, 1.3, 0.1, 1.5, {{0.0, 0.0, 0.24}}, {{0.01, 0.0, 0.0}});\n  test_move_semantics(std::move(wave), wave_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  const grmhd::Solutions::AlfvenWave wave(\n      3.0, 2.1, 1.3, 0.1, 1.5, {{0.0, 0.0, 0.24}}, {{0.01, 0.0, 0.0}});\n  test_serialization(wave);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double wavenumber = 2.1;\n  const double pressure = 1.3;\n  const double rest_mass_density = 0.4;\n  const double electron_fraction = 0.1;\n  const double adiabatic_index = 4. / 3.;\n  const std::array<double, 3> bkgd_magnetic_field = {\n      {2.3 * cos(M_PI_4) * cos(0.5 * M_PI_4),\n       2.3 * cos(M_PI_4) * sin(0.5 * M_PI_4), 2.3 * sin(M_PI_4)}};\n  const std::array<double, 3> wave_magnetic_field = {\n      {0.7 * cos(M_PI_4 + M_PI_2) * cos(0.5 * M_PI_4),\n       0.7 * cos(M_PI_4 + M_PI_2) * sin(0.5 * M_PI_4),\n       0.7 * sin(M_PI_4 + M_PI_2)}};\n\n  pypp::check_with_random_values<1>(\n      &AlfvenWaveProxy::hydro_variables<DataType>,\n      AlfvenWaveProxy(wavenumber, pressure, rest_mass_density,\n                      electron_fraction, adiabatic_index, bkgd_magnetic_field,\n                      wave_magnetic_field),\n      \"AlfvenWave\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\"},\n      {{{-15., 15.}}},\n      std::make_tuple(wavenumber, pressure, rest_mass_density,\n                      electron_fraction, adiabatic_index, bkgd_magnetic_field,\n                      wave_magnetic_field),\n      used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &AlfvenWaveProxy::grmhd_variables<DataType>,\n      AlfvenWaveProxy(wavenumber, pressure, rest_mass_density,\n                      electron_fraction, adiabatic_index, bkgd_magnetic_field,\n                      wave_magnetic_field),\n      \"AlfvenWave\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\", \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{-15., 15.}}},\n      std::make_tuple(wavenumber, pressure, rest_mass_density,\n                      electron_fraction, adiabatic_index, bkgd_magnetic_field,\n                      wave_magnetic_field),\n      used_for_size);\n\n  // Test a few of the GR components to make sure that the implementation\n  // correctly forwards to the background solution. Not meant to be extensive.\n  const grmhd::Solutions::AlfvenWave soln(\n      wavenumber, pressure, rest_mass_density, electron_fraction,\n      adiabatic_index, bkgd_magnetic_field, wave_magnetic_field);\n  const auto coords = make_with_value<tnsr::I<DataType, 3>>(used_for_size, 1.0);\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataType>>(used_for_size, 1.0),\n      get<gr::Tags::Lapse<DataType>>(soln.variables(\n          coords, 0.0, tmpl::list<gr::Tags::Lapse<DataType>>{})));\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataType>>(used_for_size, 1.0),\n      get<gr::Tags::SqrtDetSpatialMetric<DataType>>(soln.variables(\n          coords, 0.0,\n          tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>>{})));\n  auto expected_spatial_metric =\n      make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(used_for_size,\n                                                              0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_spatial_metric.get(i, i) = 1.0;\n  }\n  const auto spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3>>(soln.variables(\n          coords, 0.0, tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{}));\n  CHECK_ITERABLE_APPROX(expected_spatial_metric, spatial_metric);\n}\n\nvoid test_solution() {\n  register_classes_with_charm<grmhd::Solutions::AlfvenWave>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::Solutions::AlfvenWave>(\n          \"AlfvenWave:\\n\"\n          \"  WaveNumber: 2.2\\n\"\n          \"  Pressure: 1.23\\n\"\n          \"  RestMassDensity: 0.2\\n\"\n          \"  ElectronFraction: 0.1\\n\"\n          \"  AdiabaticIndex: 1.4\\n\"\n          \"  BkgdMagneticField: [0.0, 0.0, 2.0]\\n\"\n          \"  WaveMagneticField: [0.75, 0.0, 0.0]\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution = dynamic_cast<const grmhd::Solutions::AlfvenWave&>(\n      *deserialized_option_solution);\n\n  const std::array<double, 3> x{{1.0, 2.3, -0.4}};\n  const std::array<double, 3> dx{{1.e-4, 1.e-4, 1.e-4}};\n\n  const domain::creators::Brick brick(x - dx, x + dx, {{0, 0, 0}}, {{5, 5, 5}},\n                                      {{false, false, false}});\n  const Mesh<3> mesh{brick.initial_extents()[0], Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto domain = brick.create_domain();\n  verify_grmhd_solution(solution, domain.blocks()[0], mesh, 1.e-10, 1.234,\n                        1.e-4);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.GrMhd.AlfvenWave\",\n                  \"[Unit][PointwiseFunctions]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/GrMhd\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n  test_solution();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/Test_BondiMichel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GrMhd/VerifyGrMhdSolution.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/BondiMichel.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct BondiMichelProxy : grmhd::Solutions::BondiMichel {\n  using grmhd::Solutions::BondiMichel::BondiMichel;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3>& x) const {\n    return variables(x, 0.0, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3>& x) const {\n    return variables(x, 0.0, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  const auto flow = TestHelpers::test_creation<grmhd::Solutions::BondiMichel>(\n      \"Mass: 1.2\\n\"\n      \"SonicRadius: 5.0\\n\"\n      \"SonicDensity: 0.05\\n\"\n      \"PolytropicExponent: 1.4\\n\"\n      \"MagFieldStrength: 2.0\");\n  CHECK(flow == grmhd::Solutions::BondiMichel(1.2, 5.0, 0.05, 1.4, 2.0));\n}\n\nvoid test_move() {\n  grmhd::Solutions::BondiMichel flow(2.0, 3000.0, 1.3, 1.5, 0.24);\n  const grmhd::Solutions::BondiMichel flow_copy(2.0, 3000.0, 1.3, 1.5, 0.24);\n  test_move_semantics(std::move(flow), flow_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  const grmhd::Solutions::BondiMichel flow(1.0, 3500.0, 1.3, 1.5, 0.24);\n  test_serialization(flow);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double mass = 1.6;\n  const double sonic_radius = 4.0;\n  const double sonic_density = 0.4;\n  const double polytropic_exponent = 4. / 3.;\n  const double mag_field_strength = 2.3;\n\n  pypp::check_with_random_values<1>(\n      &BondiMichelProxy::hydro_variables<DataType>,\n      BondiMichelProxy(mass, sonic_radius, sonic_density, polytropic_exponent,\n                       mag_field_strength),\n      \"BondiMichel\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\"},\n      {{{1.0, 20.0}}},\n      std::make_tuple(mass, sonic_radius, sonic_density, polytropic_exponent,\n                      mag_field_strength),\n      used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &BondiMichelProxy::grmhd_variables<DataType>,\n      BondiMichelProxy(mass, sonic_radius, sonic_density, polytropic_exponent,\n                       mag_field_strength),\n      \"BondiMichel\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\", \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{1.0, 20.0}}},\n      std::make_tuple(mass, sonic_radius, sonic_density, polytropic_exponent,\n                      mag_field_strength),\n      used_for_size);\n}\n\nvoid test_solution() {\n  register_classes_with_charm<grmhd::Solutions::BondiMichel>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::Solutions::BondiMichel>(\n          \"BondiMichel:\\n\"\n          \"  Mass: 1.1\\n\"\n          \"  SonicRadius: 50.0\\n\"\n          \"  SonicDensity: 1.3\\n\"\n          \"  PolytropicExponent: 1.5\\n\"\n          \"  MagFieldStrength: 0.24\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution = dynamic_cast<const grmhd::Solutions::BondiMichel&>(\n      *deserialized_option_solution);\n\n  const std::array<double, 3> x{{4.0, 4.0, 4.0}};\n  const std::array<double, 3> dx{{1.e-3, 1.e-3, 1.e-3}};\n\n  const domain::creators::Brick brick(x - dx, x + dx, {{0, 0, 0}}, {{4, 4, 4}},\n                                      {{false, false, false}});\n  const Mesh<3> mesh{brick.initial_extents()[0], Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const auto domain = brick.create_domain();\n  verify_grmhd_solution(solution, domain.blocks()[0], mesh, 1.e-10, 1.234,\n                        1.e-4);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.GrMhd.BondiMichel\",\n                  \"[Unit][PointwiseFunctions]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/GrMhd\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n  test_solution();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/Test_KomissarovShock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GrMhd/VerifyGrMhdSolution.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/KomissarovShock.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct KomissarovShockProxy : grmhd::Solutions::KomissarovShock {\n  using grmhd::Solutions::KomissarovShock::KomissarovShock;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x,\n                  const double t) const {\n    return variables(x, t, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x,\n                  const double t) const {\n    return variables(x, t, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  const auto komissarov_shock =\n      TestHelpers::test_creation<grmhd::Solutions::KomissarovShock>(\n          \"AdiabaticIndex: 1.33\\n\"\n          \"LeftDensity: 1.\\n\"\n          \"RightDensity: 3.323\\n\"\n          \"LeftElectronFraction: 0.1\\n\"\n          \"RightElectronFraction: 0.1\\n\"\n          \"LeftPressure: 10.\\n\"\n          \"RightPressure: 55.36\\n\"\n          \"LeftVelocity: [0.83, 0., 0.]\\n\"\n          \"RightVelocity: [0.62, -0.44, 0.]\\n\"\n          \"LeftMagneticField: [10., 18.28, 0.]\\n\"\n          \"RightMagneticField: [10., 14.49, 0.]\\n\"\n          \"ShockSpeed: 0.5\\n\");\n  CHECK(komissarov_shock == grmhd::Solutions::KomissarovShock(\n                                1.33, 1., 3.323, 0.1, 0.1, 10., 55.36,\n                                std::array<double, 3>{{0.83, 0., 0.}},\n                                std::array<double, 3>{{0.62, -0.44, 0.}},\n                                std::array<double, 3>{{10., 18.28, 0.}},\n                                std::array<double, 3>{{10., 14.49, 0.}}, 0.5));\n}\n\nvoid test_move() {\n  grmhd::Solutions::KomissarovShock komissarov_shock(\n      4. / 3., 1., 3.323, 0.1, 0.1, 10., 55.36,\n      std::array<double, 3>{{0.8370659816473115, 0., 0.}},\n      std::array<double, 3>{{0.6202085442748952, -0.44207111995019704, 0.}},\n      std::array<double, 3>{{10., 18.28, 0.}},\n      std::array<double, 3>{{10., 14.49, 0.}}, 0.5);\n  grmhd::Solutions::KomissarovShock komissarov_shock_copy(\n      4. / 3., 1., 3.323, 0.1, 0.1, 10., 55.36,\n      std::array<double, 3>{{0.8370659816473115, 0., 0.}},\n      std::array<double, 3>{{0.6202085442748952, -0.44207111995019704, 0.}},\n      std::array<double, 3>{{10., 18.28, 0.}},\n      std::array<double, 3>{{10., 14.49, 0.}}, 0.5);\n  test_move_semantics(std::move(komissarov_shock),\n                      komissarov_shock_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  grmhd::Solutions::KomissarovShock komissarov_shock(\n      4. / 3., 1., 3.323, 0.1, 0.1, 10., 55.36,\n      std::array<double, 3>{{0.8370659816473115, 0., 0.}},\n      std::array<double, 3>{{0.6202085442748952, -0.44207111995019704, 0.}},\n      std::array<double, 3>{{10., 18.28, 0.}},\n      std::array<double, 3>{{10., 14.49, 0.}}, 0.5);\n  test_serialization(komissarov_shock);\n}\n\nvoid test_left_and_right_variables() {\n  grmhd::Solutions::KomissarovShock komissarov_shock(\n      4. / 3., 1., 3.323, 0.1, 0.1, 10., 55.36,\n      std::array<double, 3>{{0.8370659816473115, 0., 0.}},\n      std::array<double, 3>{{0.6202085442748952, -0.44207111995019704, 0.}},\n      std::array<double, 3>{{10., 18.28, 0.}},\n      std::array<double, 3>{{10., 14.49, 0.}}, 0.5);\n\n  // Test that the fluid variables are set somewhere to the right and to the\n  // left of the shock interface\n  tnsr::I<double, 3, Frame::Inertial> left_x{0.};\n  get<0>(left_x) = -1.;\n  tnsr::I<double, 3, Frame::Inertial> right_x{0.};\n  get<0>(right_x) = 1.;\n  CHECK(\n      get(get<hydro::Tags::RestMassDensity<double>>(komissarov_shock.variables(\n          left_x, 0., tmpl::list<hydro::Tags::RestMassDensity<double>>{}))) ==\n      1.);\n  CHECK(\n      get(get<hydro::Tags::RestMassDensity<double>>(komissarov_shock.variables(\n          right_x, 0., tmpl::list<hydro::Tags::RestMassDensity<double>>{}))) ==\n      3.323);\n  CHECK(get(get<hydro::Tags::Pressure<double>>(komissarov_shock.variables(\n            left_x, 0., tmpl::list<hydro::Tags::Pressure<double>>{}))) == 10.);\n  CHECK(get(get<hydro::Tags::Pressure<double>>(komissarov_shock.variables(\n            right_x, 0., tmpl::list<hydro::Tags::Pressure<double>>{}))) ==\n        55.36);\n  CHECK(get<0>(get<hydro::Tags::SpatialVelocity<double, 3>>(\n            komissarov_shock.variables(\n                left_x, 0.,\n                tmpl::list<hydro::Tags::SpatialVelocity<double, 3>>{}))) ==\n        0.8370659816473115);\n  CHECK(get<0>(get<hydro::Tags::SpatialVelocity<double, 3>>(\n            komissarov_shock.variables(\n                right_x, 0.,\n                tmpl::list<hydro::Tags::SpatialVelocity<double, 3>>{}))) ==\n        0.6202085442748952);\n  CHECK(get(get<hydro::Tags::LorentzFactor<double>>(komissarov_shock.variables(\n            left_x, 0., tmpl::list<hydro::Tags::LorentzFactor<double>>{}))) ==\n        approx(1.827812900709479));\n  CHECK(get(get<hydro::Tags::LorentzFactor<double>>(komissarov_shock.variables(\n            right_x, 0., tmpl::list<hydro::Tags::LorentzFactor<double>>{}))) ==\n        approx(1.5431906071513006));\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  KomissarovShockProxy komissarov_shock(\n      4. / 3., 1., 3.323, 0.1, 0.1, 10., 55.36,\n      std::array<double, 3>{{0.8370659816473115, 0., 0.}},\n      std::array<double, 3>{{0.6202085442748952, -0.44207111995019704, 0.}},\n      std::array<double, 3>{{10., 18.28, 0.}},\n      std::array<double, 3>{{10., 14.49, 0.}}, 0.5);\n  const auto member_variables = std::make_tuple(\n      4. / 3., 1., 3.323, 0.1, 0.1, 10., 55.36,\n      std::array<double, 3>{{0.8370659816473115, 0., 0.}},\n      std::array<double, 3>{{0.6202085442748952, -0.44207111995019704, 0.}},\n      std::array<double, 3>{{10., 18.28, 0.}},\n      std::array<double, 3>{{10., 14.49, 0.}}, 0.5);\n\n  pypp::check_with_random_values<1>(\n      &KomissarovShockProxy::hydro_variables<DataType>, komissarov_shock,\n      \"KomissarovShock\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\"},\n      {{{-1., 1.}}}, member_variables, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &KomissarovShockProxy::grmhd_variables<DataType>, komissarov_shock,\n      \"KomissarovShock\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy\", \"magnetic_field\", \"divergence_cleaning_field\"},\n      {{{-1., 1.}}}, member_variables, used_for_size);\n}\n\nvoid test_solution() {\n  register_classes_with_charm<grmhd::Solutions::KomissarovShock>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::Solutions::KomissarovShock>(\n          \"KomissarovShock:\\n\"\n          \"  AdiabaticIndex: 1.33\\n\"\n          \"  LeftDensity: 1.\\n\"\n          \"  RightDensity: 3.323\\n\"\n          \"  LeftElectronFraction: 0.1\\n\"\n          \"  RightElectronFraction: 0.1\\n\"\n          \"  LeftPressure: 10.\\n\"\n          \"  RightPressure: 55.36\\n\"\n          \"  LeftVelocity: [0.83, 0., 0.]\\n\"\n          \"  RightVelocity: [0.62, -0.44, 0.]\\n\"\n          \"  LeftMagneticField: [10., 18.28, 0.]\\n\"\n          \"  RightMagneticField: [10., 14.49, 0.]\\n\"\n          \"  ShockSpeed: 0.5\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution = dynamic_cast<const grmhd::Solutions::KomissarovShock&>(\n      *deserialized_option_solution);\n\n  const std::array<double, 3> x{{1.0, 2.3, -0.4}};\n  const std::array<double, 3> dx{{1.e-1, 1.e-1, 1.e-1}};\n\n  domain::creators::Brick brick(x - dx, x + dx, {{0, 0, 0}}, {{6, 6, 6}},\n                                {{false, false, false}});\n  Mesh<3> mesh{brick.initial_extents()[0], Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto};\n  const auto domain = brick.create_domain();\n  verify_grmhd_solution(solution, domain.blocks()[0], mesh, 1.e-10, 1.234,\n                        1.e-1);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Solutions.GrMhd.KomissarovShock\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/GrMhd\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_left_and_right_variables();\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n  test_solution();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/Test_SmoothFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GrMhd/VerifyGrMhdSolution.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GrMhd/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nstruct SmoothFlowProxy : grmhd::Solutions::SmoothFlow {\n  using grmhd::Solutions::SmoothFlow::SmoothFlow;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::ElectronFraction<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3>& x, double t) const {\n    return variables(x, t, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3>& x, double t) const {\n    return variables(x, t, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  const auto flow = TestHelpers::test_creation<grmhd::Solutions::SmoothFlow>(\n      \"MeanVelocity: [0.1, -0.2, 0.3]\\n\"\n      \"WaveVector: [-0.13, -0.54, 0.04]\\n\"\n      \"Pressure: 1.23\\n\"\n      \"AdiabaticIndex: 1.4\\n\"\n      \"PerturbationSize: 0.75\");\n  CHECK(flow == grmhd::Solutions::SmoothFlow({{0.1, -0.2, 0.3}},\n                                             {{-0.13, -0.54, 0.04}}, 1.23, 1.4,\n                                             0.75));\n}\n\nvoid test_move() {\n  grmhd::Solutions::SmoothFlow flow({{0.24, 0.11, 0.04}}, {{0.14, 0.42, -0.03}},\n                                    1.3, 1.5, 0.24);\n  grmhd::Solutions::SmoothFlow flow_copy({{0.24, 0.11, 0.04}},\n                                         {{0.14, 0.42, -0.03}}, 1.3, 1.5, 0.24);\n  test_move_semantics(std::move(flow), flow_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  grmhd::Solutions::SmoothFlow flow({{0.24, 0.11, 0.04}}, {{0.14, 0.42, -0.03}},\n                                    1.3, 1.5, 0.24);\n  test_serialization(flow);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const std::array<double, 3> mean_velocity = {{0.23, 0.01, 0.31}};\n  const std::array<double, 3> wave_vector = {{0.11, 0.23, 0.32}};\n  const double pressure = 1.3;\n  const double adiabatic_index = 4. / 3.;\n  const double perturbation_size = 0.78;\n\n  pypp::check_with_random_values<1>(\n      &SmoothFlowProxy::hydro_variables<DataType>,\n      SmoothFlowProxy(mean_velocity, wave_vector, pressure, adiabatic_index,\n                      perturbation_size),\n      \"GrMhd.SmoothFlow\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy_relativistic\"},\n      {{{-15., 15.}}},\n      std::make_tuple(mean_velocity, wave_vector, pressure, adiabatic_index,\n                      perturbation_size),\n      used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &SmoothFlowProxy::grmhd_variables<DataType>,\n      SmoothFlowProxy(mean_velocity, wave_vector, pressure, adiabatic_index,\n                      perturbation_size),\n      \"GrMhd.SmoothFlow\",\n      {\"rest_mass_density\", \"electron_fraction\", \"spatial_velocity\",\n       \"specific_internal_energy\", \"pressure\", \"lorentz_factor\",\n       \"specific_enthalpy_relativistic\", \"magnetic_field\",\n       \"divergence_cleaning_field\"},\n      {{{-15., 15.}}},\n      std::make_tuple(mean_velocity, wave_vector, pressure, adiabatic_index,\n                      perturbation_size),\n      used_for_size);\n\n  // Test a few of the GR components to make sure that the implementation\n  // correctly forwards to the background solution. Not meant to be extensive.\n  grmhd::Solutions::SmoothFlow soln(mean_velocity, wave_vector, pressure,\n                                    adiabatic_index, perturbation_size);\n  const auto coords = make_with_value<tnsr::I<DataType, 3>>(used_for_size, 1.0);\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataType>>(used_for_size, 1.0),\n      get<gr::Tags::Lapse<DataType>>(soln.variables(\n          coords, 0.0, tmpl::list<gr::Tags::Lapse<DataType>>{})));\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataType>>(used_for_size, 1.0),\n      get<gr::Tags::SqrtDetSpatialMetric<DataType>>(soln.variables(\n          coords, 0.0,\n          tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>>{})));\n  auto expected_spatial_metric =\n      make_with_value<tnsr::ii<DataType, 3, Frame::Inertial>>(used_for_size,\n                                                              0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_spatial_metric.get(i, i) = 1.0;\n  }\n  const auto spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3>>(soln.variables(\n          coords, 0.0, tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{}));\n  CHECK_ITERABLE_APPROX(expected_spatial_metric, spatial_metric);\n}\n\nvoid test_solution() {\n  register_classes_with_charm<grmhd::Solutions::SmoothFlow>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          grmhd::Solutions::SmoothFlow>(\n          \"SmoothFlow:\\n\"\n          \"  MeanVelocity: [0.1, -0.2, 0.3]\\n\"\n          \"  WaveVector: [-0.13, -0.54, 0.04]\\n\"\n          \"  Pressure: 1.23\\n\"\n          \"  AdiabaticIndex: 1.4\\n\"\n          \"  PerturbationSize: 0.75\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution = dynamic_cast<const grmhd::Solutions::SmoothFlow&>(\n      *deserialized_option_solution);\n\n  const std::array<double, 3> x{{4.0, 4.0, 4.0}};\n  const std::array<double, 3> dx{{1.e-3, 1.e-3, 1.e-3}};\n\n  domain::creators::Brick brick(x - dx, x + dx, {{0, 0, 0}}, {{4, 4, 4}},\n                                {{false, false, false}});\n  Mesh<3> mesh{brick.initial_extents()[0], Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto};\n  const auto domain = brick.create_domain();\n  verify_grmhd_solution(solution, domain.blocks()[0], mesh, 1.e-10, 1.234,\n                        1.e-4);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.GrMhd.SmoothFlow\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n\n  test_solution();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/GrMhd/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Hydro/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_HydroSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_SmoothFlow.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  HydroSolutions\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Hydro/SmoothFlow.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef rest_mass_density(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return 1.0 + (\n        density_amplitude\n        * np.sin(\n            np.dot(\n                np.asarray(wave_vector),\n                np.asarray(x) - np.asarray(mean_velocity) * t,\n            )\n        )\n    )\n\n\ndef electron_fraction(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return 0.1\n\n\ndef spatial_velocity(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return np.asarray(mean_velocity)\n\n\ndef specific_internal_energy(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return pressure / (\n        (adiabatic_index - 1.0)\n        * rest_mass_density(\n            x,\n            t,\n            mean_velocity,\n            wave_vector,\n            pressure,\n            adiabatic_index,\n            density_amplitude,\n        )\n    )\n\n\ndef temperature(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return (adiabatic_index - 1.0) * specific_internal_energy(\n        x,\n        t,\n        mean_velocity,\n        wave_vector,\n        pressure,\n        adiabatic_index,\n        density_amplitude,\n    )\n\n\ndef pressure(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return pressure\n\n\ndef specific_enthalpy(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return adiabatic_index * specific_internal_energy(\n        x,\n        t,\n        mean_velocity,\n        wave_vector,\n        pressure,\n        adiabatic_index,\n        density_amplitude,\n    )\n\n\ndef specific_enthalpy_relativistic(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return 1.0 + adiabatic_index * specific_internal_energy(\n        x,\n        t,\n        mean_velocity,\n        wave_vector,\n        pressure,\n        adiabatic_index,\n        density_amplitude,\n    )\n\n\ndef lorentz_factor(\n    x,\n    t,\n    mean_velocity,\n    wave_vector,\n    pressure,\n    adiabatic_index,\n    density_amplitude,\n):\n    return 1.0 / np.sqrt(1.0 - np.linalg.norm(np.asarray(mean_velocity)) ** 2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Hydro/Test_SmoothFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Hydro/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim, bool IsRelativistic>\nstruct SmoothFlowProxy : hydro::Solutions::SmoothFlow<Dim, IsRelativistic> {\n public:\n  SmoothFlowProxy() = default;\n  SmoothFlowProxy(const SmoothFlowProxy& /*rhs*/) = default;\n  SmoothFlowProxy& operator=(const SmoothFlowProxy& /*rhs*/) = default;\n  SmoothFlowProxy(SmoothFlowProxy&& /*rhs*/) = default;\n  SmoothFlowProxy& operator=(SmoothFlowProxy&& /*rhs*/) = default;\n  ~SmoothFlowProxy() = default;\n\n  SmoothFlowProxy(const std::array<double, Dim>& mean_velocity,\n                  const std::array<double, Dim>& wavevector,\n                  const double pressure, const double adiabatic_index,\n                  const double perturbation_size)\n      : hydro::Solutions::SmoothFlow<Dim, IsRelativistic>(\n            mean_velocity, wavevector, pressure, adiabatic_index,\n            perturbation_size) {}\n\n  template <typename DataType>\n  using core_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::conditional_t<IsRelativistic,\n                          tmpl::push_back<core_variables_tags<DataType>,\n                                          hydro::Tags::LorentzFactor<DataType>>,\n                          core_variables_tags<DataType>>;\n\n  template <typename DataType, typename... Tags>\n  tuples::TaggedTuple<Tags...> variables(const tnsr::I<DataType, Dim>& x,\n                                         const double t,\n                                         tmpl::list<Tags...> /*meta*/) const {\n    static_assert(sizeof...(Tags) > 1,\n                  \"The generic template will recurse infinitely if only one \"\n                  \"tag is being retrieved.\");\n    return {\n        get<Tags>(hydro::Solutions::SmoothFlow<Dim, IsRelativistic>::variables(\n            x, t, tmpl::list<Tags>{}))...};\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, Dim>& x, const double t) const {\n    return this->variables(x, t, variables_tags<DataType>{});\n  }\n};\n\ntemplate <size_t Dim, bool IsRelativistic, typename DataType>\nvoid test_solution(const DataType& used_for_size,\n                   const std::array<double, Dim>& mean_velocity,\n                   const std::array<double, Dim>& wave_vector) {\n  CAPTURE(Dim);\n  CAPTURE(IsRelativistic);\n  const double pressure = 1.23;\n  const double adiabatic_index = 1.3334;\n  const double perturbation_size = 0.78;\n\n  SmoothFlowProxy<Dim, IsRelativistic> solution(\n      mean_velocity, wave_vector, pressure, adiabatic_index, perturbation_size);\n  if constexpr (IsRelativistic) {\n    pypp::check_with_random_values<1>(\n        &SmoothFlowProxy<Dim, IsRelativistic>::template primitive_variables<\n            DataType>,\n        solution, \"SmoothFlow\",\n        {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n         \"pressure\", \"specific_enthalpy_relativistic\", \"lorentz_factor\"},\n        {{{-15., 15.}}},\n        std::make_tuple(mean_velocity, wave_vector, pressure, adiabatic_index,\n                        perturbation_size),\n        used_for_size);\n  } else {\n    pypp::check_with_random_values<1>(\n        &SmoothFlowProxy<Dim, IsRelativistic>::template primitive_variables<\n            DataType>,\n        solution, \"SmoothFlow\",\n        {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n         \"pressure\", \"specific_enthalpy\"},\n        {{{-15., 15.}}},\n        std::make_tuple(mean_velocity, wave_vector, pressure, adiabatic_index,\n                        perturbation_size),\n        used_for_size);\n  }\n\n  test_serialization(solution);\n  test_copy_semantics(solution);\n}\n\ntemplate <bool IsRelativistic>\nvoid test() {\n  test_solution<1, IsRelativistic>(std::numeric_limits<double>::signaling_NaN(),\n                                   {{-0.3}}, {{0.4}});\n  test_solution<1, IsRelativistic>(DataVector(5), {{-0.3}}, {{0.4}});\n  test_solution<2, IsRelativistic>(std::numeric_limits<double>::signaling_NaN(),\n                                   {{-0.3, 0.1}}, {{0.4, -0.24}});\n  test_solution<2, IsRelativistic>(DataVector(5), {{-0.3, 0.1}},\n                                   {{0.4, -0.24}});\n  test_solution<3, IsRelativistic>(std::numeric_limits<double>::signaling_NaN(),\n                                   {{-0.3, 0.1, -0.002}},\n                                   {{0.4, -0.24, 0.054}});\n  test_solution<3, IsRelativistic>(DataVector(5), {{-0.3, 0.1, -0.002}},\n                                   {{0.4, -0.24, 0.054}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Hydro.SmoothFlow\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/Hydro\"};\n  test<true>();\n  test<false>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Hydro/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_NewtonianEulerSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_IsentropicVortex.cpp\n  Test_LaneEmdenStar.cpp\n  Test_RiemannProblem.cpp\n  Test_SmoothFlow.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  NewtonianEulerSolutions\n  NewtonianEulerSources\n  Options\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/IsentropicVortex.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef perturbation_profile(z):\n    return np.sin(z)\n\n\ndef deriv_of_perturbation_profile(z):\n    return np.cos(z)\n\n\ndef mass_density(\n    x,\n    t,\n    adiabatic_index,\n    center,\n    mean_velocity,\n    strength,\n    perturbation_amplitude,\n):\n    x_tilde = x - center - t * np.array(mean_velocity)\n    temp = 1.0 - strength * strength * (adiabatic_index - 1.0) * np.exp(\n        1.0 - np.dot(x_tilde[:2], x_tilde[:2])\n    ) / (8.0 * adiabatic_index * np.pi**2)\n    return np.power(temp, 1.0 / (adiabatic_index - 1.0))\n\n\ndef velocity(\n    x,\n    t,\n    adiabatic_index,\n    center,\n    mean_velocity,\n    strength,\n    perturbation_amplitude,\n):\n    x_tilde = x - center - t * np.array(mean_velocity)\n    temp = (\n        0.5\n        * strength\n        * np.exp(0.5 * (1.0 - np.dot(x_tilde[:2], x_tilde[:2])))\n        / np.pi\n    )\n    velocity = np.copy(mean_velocity)\n    velocity[0] -= x_tilde[1] * temp\n    velocity[1] += x_tilde[0] * temp\n    if velocity.size == 3:\n        velocity[2] += perturbation_amplitude * perturbation_profile(x[2])\n    return velocity\n\n\ndef specific_internal_energy(\n    x,\n    t,\n    adiabatic_index,\n    center,\n    mean_velocity,\n    strength,\n    perturbation_amplitude,\n):\n    return np.power(\n        mass_density(\n            x,\n            t,\n            adiabatic_index,\n            center,\n            mean_velocity,\n            strength,\n            perturbation_amplitude,\n        ),\n        adiabatic_index - 1.0,\n    ) / (adiabatic_index - 1.0)\n\n\ndef pressure(\n    x,\n    t,\n    adiabatic_index,\n    center,\n    mean_velocity,\n    strength,\n    perturbation_amplitude,\n):\n    return np.power(\n        mass_density(\n            x,\n            t,\n            adiabatic_index,\n            center,\n            mean_velocity,\n            strength,\n            perturbation_amplitude,\n        ),\n        adiabatic_index,\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef gravitational_field(x, central_mass_density, polytropic_constant):\n    alpha = np.sqrt(0.5 * polytropic_constant / np.pi)\n    outer_radius = alpha * np.pi\n    mass_scale = 4.0 * np.pi * alpha**3 * central_mass_density\n    radius = np.sqrt(np.sum(x**2)) + 1.0e-30 * outer_radius\n    if radius < outer_radius:\n        xi = radius / alpha\n        enclosed_mass = mass_scale * (np.sin(xi) - xi * np.cos(xi))\n        return -x * enclosed_mass / radius**3\n    else:\n        total_mass = mass_scale * np.pi\n        return -x * total_mass / radius**3\n\n\ndef mass_density(x, t, central_mass_density, polytropic_constant):\n    alpha = np.sqrt(0.5 * polytropic_constant / np.pi)\n    outer_radius = alpha * np.pi\n    radius = np.sqrt(np.sum(x**2)) + 1.0e-30 * outer_radius\n    if radius < outer_radius:\n        xi = radius / alpha\n        return central_mass_density * np.sin(xi) / xi\n    else:\n        return 0.0\n\n\ndef velocity(x, t, central_mass_density, polytropic_constant):\n    return np.zeros(3)\n\n\ndef specific_internal_energy(x, t, central_mass_density, polytropic_constant):\n    rho = mass_density(x, t, central_mass_density, polytropic_constant)\n    return polytropic_constant * rho\n\n\ndef pressure(x, t, central_mass_density, polytropic_constant):\n    rho = mass_density(x, t, central_mass_density, polytropic_constant)\n    return polytropic_constant * rho**2\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/RiemannProblem.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Note: Values for p_* and u_* correspond to those\n# values obtained for the initial data of the Sod tube test.\n\n\ndef sound_speed(adiabatic_index, mass_density, pressure):\n    return np.sqrt(adiabatic_index * pressure / mass_density)\n\n\ndef fan_density(\n    x_shifted, t, adiabatic_index, mass_density, velocity, pressure, direction\n):\n    s = (x_shifted / t) if t > 0.0 else 0.0\n    return mass_density * np.power(\n        (\n            2.0\n            - direction\n            * (adiabatic_index - 1.0)\n            * (velocity - s)\n            / sound_speed(adiabatic_index, mass_density, pressure)\n        )\n        / (adiabatic_index + 1.0),\n        2.0 / (adiabatic_index - 1.0),\n    )\n\n\ndef fan_velocity(\n    x_shifted, t, adiabatic_index, mass_density, velocity, pressure, direction\n):\n    s = (x_shifted / t) if t > 0.0 else 0.0\n    return (\n        2.0\n        * (\n            0.5 * (adiabatic_index - 1.0) * velocity\n            + s\n            - direction * sound_speed(adiabatic_index, mass_density, pressure)\n        )\n        / (adiabatic_index + 1.0)\n    )\n\n\ndef fan_pressure(\n    x_shifted, t, adiabatic_index, mass_density, velocity, pressure, direction\n):\n    s = (x_shifted / t) if t > 0.0 else 0.0\n    return pressure * np.power(\n        (\n            2.0\n            - direction\n            * (adiabatic_index - 1.0)\n            * (velocity - s)\n            / sound_speed(adiabatic_index, mass_density, pressure)\n        )\n        / (adiabatic_index + 1.0),\n        2.0 * adiabatic_index / (adiabatic_index - 1.0),\n    )\n\n\ndef rarefaction(\n    x_shifted,\n    t,\n    quantity,\n    quantity_star,\n    adiabatic_index,\n    velocity_star,\n    pressure_star,\n    fan,\n    mass_density,\n    velocity,\n    pressure,\n    direction,\n):\n    pressure_ratio = pressure_star / pressure\n    sound_speed_ = sound_speed(adiabatic_index, mass_density, pressure)\n    sound_speed_star = sound_speed_ * np.power(\n        pressure_ratio, 0.5 * (adiabatic_index - 1.0) / adiabatic_index\n    )\n    head_speed = velocity + direction * sound_speed_\n    tail_speed = velocity_star + direction * sound_speed_star\n    return (\n        quantity_star\n        if direction * (x_shifted - tail_speed * t) < 0.0\n        else (\n            fan(\n                x_shifted,\n                t,\n                adiabatic_index,\n                mass_density,\n                velocity,\n                pressure,\n                direction,\n            )\n            if (\n                direction * (x_shifted - tail_speed * t) >= 0.0\n                and direction * (x_shifted - head_speed * t) < 0.0\n            )\n            else quantity\n        )\n    )\n\n\ndef shock(\n    x_shifted,\n    t,\n    quantity,\n    quantity_star,\n    adiabatic_index,\n    pressure_star,\n    mass_density,\n    velocity,\n    pressure,\n    direction,\n):\n    shock_speed = velocity + direction * sound_speed(\n        adiabatic_index, mass_density, pressure\n    ) * np.sqrt(\n        0.5\n        * (\n            (adiabatic_index + 1.0) * (pressure_star / pressure)\n            + adiabatic_index\n            - 1.0\n        )\n        / adiabatic_index\n    )\n    return (\n        quantity_star\n        if direction * (x_shifted - shock_speed * t) < 0.0\n        else quantity\n    )\n\n\ndef mass_density(\n    x,\n    t,\n    adiabatic_index,\n    initial_pos,\n    l_mass_density,\n    l_velocity,\n    l_pressure,\n    r_mass_density,\n    r_velocity,\n    r_pressure,\n):\n    velocity_star = 0.927452620048950\n    pressure_star = 0.303130178050647\n\n    # density in star region for shock\n    gamma_mm_over_gamma_pp = (adiabatic_index - 1.0) / (adiabatic_index + 1.0)\n    r_pressure_ratio = pressure_star / r_pressure\n    r_density_star = (\n        r_mass_density\n        * (r_pressure_ratio + gamma_mm_over_gamma_pp)\n        / (r_pressure_ratio * gamma_mm_over_gamma_pp + 1.0)\n    )\n\n    # density in star region for rarefaction\n    l_density_star = l_mass_density * np.power(\n        pressure_star / l_pressure, 1.0 / adiabatic_index\n    )\n\n    x_shifted = x[0] - initial_pos\n    return (\n        rarefaction(\n            x_shifted,\n            t,\n            l_mass_density,\n            l_density_star,\n            adiabatic_index,\n            velocity_star,\n            pressure_star,\n            fan_density,\n            l_mass_density,\n            l_velocity[0],\n            l_pressure,\n            -1.0,\n        )\n        if x_shifted < velocity_star * t\n        else shock(\n            x_shifted,\n            t,\n            r_mass_density,\n            r_density_star,\n            adiabatic_index,\n            pressure_star,\n            r_mass_density,\n            r_velocity[0],\n            r_pressure,\n            1.0,\n        )\n    )\n\n\ndef velocity(\n    x,\n    t,\n    adiabatic_index,\n    initial_pos,\n    l_mass_density,\n    l_velocity,\n    l_pressure,\n    r_mass_density,\n    r_velocity,\n    r_pressure,\n):\n    velocity_star = 0.927452620048950\n    pressure_star = 0.303130178050647\n\n    velocity = np.zeros(x.size)\n    x_shifted = x[0] - initial_pos\n    velocity[0] = (\n        rarefaction(\n            x_shifted,\n            t,\n            l_velocity[0],\n            velocity_star,\n            adiabatic_index,\n            velocity_star,\n            pressure_star,\n            fan_velocity,\n            l_mass_density,\n            l_velocity[0],\n            l_pressure,\n            -1.0,\n        )\n        if x_shifted < velocity_star * t\n        else shock(\n            x_shifted,\n            t,\n            r_velocity[0],\n            velocity_star,\n            adiabatic_index,\n            pressure_star,\n            r_mass_density,\n            r_velocity[0],\n            r_pressure,\n            1.0,\n        )\n    )\n    if x.size > 1:\n        velocity[1] = (\n            l_velocity[1] if x_shifted < velocity_star * t else r_velocity[1]\n        )\n    if x.size > 2:\n        velocity[2] = (\n            l_velocity[2] if x_shifted < velocity_star * t else r_velocity[2]\n        )\n    return velocity\n\n\ndef pressure(\n    x,\n    t,\n    adiabatic_index,\n    initial_pos,\n    l_mass_density,\n    l_velocity,\n    l_pressure,\n    r_mass_density,\n    r_velocity,\n    r_pressure,\n):\n    velocity_star = 0.927452620048950\n    pressure_star = 0.303130178050647\n\n    x_shifted = x[0] - initial_pos\n    return (\n        rarefaction(\n            x_shifted,\n            t,\n            l_pressure,\n            pressure_star,\n            adiabatic_index,\n            velocity_star,\n            pressure_star,\n            fan_pressure,\n            l_mass_density,\n            l_velocity[0],\n            l_pressure,\n            -1.0,\n        )\n        if x_shifted < velocity_star * t\n        else shock(\n            x_shifted,\n            t,\n            r_pressure,\n            pressure_star,\n            adiabatic_index,\n            pressure_star,\n            r_mass_density,\n            r_velocity[0],\n            r_pressure,\n            1.0,\n        )\n    )\n\n\ndef specific_internal_energy(\n    x,\n    t,\n    adiabatic_index,\n    initial_pos,\n    l_mass_density,\n    l_velocity,\n    l_pressure,\n    r_mass_density,\n    r_velocity,\n    r_pressure,\n):\n    return (\n        pressure(\n            x,\n            t,\n            adiabatic_index,\n            initial_pos,\n            l_mass_density,\n            l_velocity,\n            l_pressure,\n            r_mass_density,\n            r_velocity,\n            r_pressure,\n        )\n        / mass_density(\n            x,\n            t,\n            adiabatic_index,\n            initial_pos,\n            l_mass_density,\n            l_velocity,\n            l_pressure,\n            r_mass_density,\n            r_velocity,\n            r_pressure,\n        )\n        / (adiabatic_index - 1.0)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/Test_IsentropicVortex.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/NoSource.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Sources/VortexPerturbation.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/IsentropicVortex.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct IsentropicVortexProxy\n    : NewtonianEuler::Solutions::IsentropicVortex<Dim> {\n  using NewtonianEuler::Solutions::IsentropicVortex<Dim>::IsentropicVortex;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n                      double t) const {\n    return this->variables(x, t, variables_tags<DataType>{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nvoid test_solution(const DataType& used_for_size,\n                   const std::array<double, Dim>& center,\n                   const std::string& center_option,\n                   const std::array<double, Dim>& mean_velocity,\n                   const std::string& mean_velocity_option,\n                   const double perturbation_amplitude = 0.0,\n                   const std::string& perturbation_amplitude_option = \"\") {\n  IsentropicVortexProxy<Dim> vortex(1.43, center, mean_velocity, 3.76,\n                                    perturbation_amplitude);\n  pypp::check_with_random_values<1>(\n      &IsentropicVortexProxy<Dim>::template primitive_variables<DataType>,\n      vortex, \"IsentropicVortex\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      {{{-1., 1.}}},\n      std::make_tuple(1.43, center, mean_velocity, 3.76,\n                      perturbation_amplitude),\n      used_for_size);\n\n  std::string input = \"  AdiabaticIndex: 1.43\\n  Center: \" + center_option +\n                      \"\\n  MeanVelocity: \" + mean_velocity_option +\n                      \"\\n  Strength: 3.76\";\n\n  if (Dim == 3) {\n    input += \"\\n  PerturbAmplitude: \" + perturbation_amplitude_option;\n  }\n\n  const auto vortex_from_options = TestHelpers::test_creation<\n      NewtonianEuler::Solutions::IsentropicVortex<Dim>>(input);\n  CHECK(vortex_from_options == vortex);\n\n  IsentropicVortexProxy<Dim> vortex_to_move(1.43, center, mean_velocity, 3.76,\n                                            perturbation_amplitude);\n  test_move_semantics(std::move(vortex_to_move), vortex);  //  NOLINT\n  test_copy_semantics(vortex);\n\n  test_serialization(vortex);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.NewtEuler.Vortex\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler\"};\n\n  const std::array<double, 2> center_2d = {{0.12, -0.04}};\n  const std::array<double, 2> mean_velocity_2d = {{0.54, -0.02}};\n  test_solution<2>(std::numeric_limits<double>::signaling_NaN(), center_2d,\n                   \"[0.12, -0.04]\", mean_velocity_2d, \"[0.54, -0.02]\");\n  test_solution<2>(DataVector(5), center_2d, \"[0.12, -0.04]\", mean_velocity_2d,\n                   \"[0.54, -0.02]\");\n\n  const std::array<double, 3> center_3d = {{-0.53, -0.1, 1.4}};\n  const std::array<double, 3> mean_velocity_3d = {{-0.04, 0.14, 0.3}};\n  const double perturbation_amplitude = 0.5;\n  test_solution<3>(std::numeric_limits<double>::signaling_NaN(), center_3d,\n                   \"[-0.53, -0.1, 1.4]\", mean_velocity_3d, \"[-0.04, 0.14, 0.3]\",\n                   perturbation_amplitude, \"0.5\");\n  test_solution<3>(DataVector(5), center_3d, \"[-0.53, -0.1, 1.4]\",\n                   mean_velocity_3d, \"[-0.04, 0.14, 0.3]\",\n                   perturbation_amplitude, \"0.5\");\n\n  NewtonianEuler::Solutions::IsentropicVortex<3> vortex(\n      1.3, {{3.21, -1.4}}, {{0.12, -0.53}}, 0.05, 1.7);\n\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> distribution(-3.0, 3.0);\n  const double random_z = distribution(gen);\n  const DataVector random_z_dv = {{random_z, random_z - 1.0, random_z + 2.0,\n                                   3.0 * random_z, exp(random_z)}};\n  CHECK(vortex.perturbation_profile(random_z) == sin(random_z));\n  CHECK(vortex.perturbation_profile(random_z_dv) == sin(random_z_dv));\n  CHECK(vortex.deriv_of_perturbation_profile(random_z) == cos(random_z));\n  CHECK(vortex.deriv_of_perturbation_profile(random_z_dv) == cos(random_z_dv));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      []() {\n        NewtonianEuler::Solutions::IsentropicVortex<2> test_vortex(\n            1.3, {{3.21, -1.4}}, {{0.12, -0.53}}, 1.7, 1.e-12);\n      }(),\n      Catch::Matchers::ContainsSubstring(\n          \"A nonzero perturbation amplitude only \"\n          \"makes sense in 3 dimensions.\"));\n  CHECK_THROWS_WITH(\n      []() {\n        NewtonianEuler::Solutions::IsentropicVortex<2> test_vortex(\n            1.3, {{3.21, -1.4}}, {{0.12, -0.53}}, -1.7);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"The strength must be non-negative.\"));\n  CHECK_THROWS_WITH(\n      []() {\n        NewtonianEuler::Solutions::IsentropicVortex<3> test_vortex(\n            1.65, {{-0.12, 1.542, 3.12}}, {{-0.04, -0.32, 0.003}}, -0.5, 4.2);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"The strength must be non-negative.\"));\n#endif\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::Solutions::IsentropicVortex<2>>(\n                        \"AdiabaticIndex: 1.4\\n\"\n                        \"Center: [-3.9, 1.1]\\n\"\n                        \"MeanVelocity: [0.1, -0.032]\\n\"\n                        \"Strength: -0.2\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.2 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        NewtonianEuler::Solutions::IsentropicVortex<3>>(\n                        \"AdiabaticIndex: 1.12\\n\"\n                        \"Center: [0.3, -0.12, 4.2]\\n\"\n                        \"MeanVelocity: [-0.03, -0.1, 0.09]\\n\"\n                        \"Strength: -0.3\\n\"\n                        \"PerturbAmplitude: 0.42\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.3 is below the lower bound of 0\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/Test_LaneEmdenStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <limits>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/LaneEmdenStar.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nvoid test_construction_and_serialization() {\n  const double central_mass_density = 0.7;\n  const double polytropic_constant = 2.0;\n  NewtonianEuler::Solutions::LaneEmdenStar star(central_mass_density,\n                                                polytropic_constant);\n\n  const std::string input =\n      MakeString{} << \"  CentralMassDensity: \" << central_mass_density\n                   << \"\\n  PolytropicConstant: \" << polytropic_constant;\n  const auto star_from_options =\n      TestHelpers::test_creation<NewtonianEuler::Solutions::LaneEmdenStar>(\n          input);\n\n  CHECK(star_from_options == star);\n\n  NewtonianEuler::Solutions::LaneEmdenStar star_to_move(central_mass_density,\n                                                        polytropic_constant);\n  test_move_semantics(std::move(star_to_move), star);  //  NOLINT\n  test_copy_semantics(star);\n\n  test_serialization(star);\n}\n\nstruct LaneEmdenStarProxy : NewtonianEuler::Solutions::LaneEmdenStar {\n  using NewtonianEuler::Solutions::LaneEmdenStar::LaneEmdenStar;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, 3, Frame::Inertial>& x,\n                      double t) const {\n    return this->variables(x, t, variables_tags<DataType>{});\n  }\n};\n\ntemplate <typename DataType>\nvoid test_solution(const DataType& used_for_size,\n                   const double central_mass_density,\n                   const double polytropic_constant) {\n  const LaneEmdenStarProxy star(central_mass_density, polytropic_constant);\n  pypp::check_with_random_values<1>(\n      &LaneEmdenStarProxy::template primitive_variables<DataType>, star,\n      \"LaneEmdenStar\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      // with polytropic_constant == 2, star has outer radius ~ 1.77\n      {{{-2.0, 2.0}}},\n      std::make_tuple(central_mass_density, polytropic_constant),\n      used_for_size);\n\n  const auto star_sd = serialize_and_deserialize(star);\n  pypp::check_with_random_values<1>(\n      &LaneEmdenStarProxy::template primitive_variables<DataType>, star_sd,\n      \"LaneEmdenStar\",\n      {\"mass_density\", \"velocity\", \"specific_internal_energy\", \"pressure\"},\n      // with polytropic_constant == 2, star has outer radius ~ 1.77\n      {{{-2.0, 2.0}}},\n      std::make_tuple(central_mass_density, polytropic_constant),\n      used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.NewtEuler.LaneEmdenStar\",\n    \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler\"};\n\n  test_construction_and_serialization();\n\n  // Nothing special about these values, we just want them to be non-unity and\n  // to be different from each other:\n  const double central_mass_density = 0.7;\n  const double polytropic_constant = 2.0;\n  test_solution(std::numeric_limits<double>::signaling_NaN(),\n                central_mass_density, polytropic_constant);\n  test_solution(DataVector(5), central_mass_density, polytropic_constant);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/Test_RiemannProblem.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/RiemannProblem.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct RiemannProblemProxy : NewtonianEuler::Solutions::RiemannProblem<Dim> {\n  using NewtonianEuler::Solutions::RiemannProblem<Dim>::RiemannProblem;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, Dim, Frame::Inertial>& x,\n                      double t) const {\n    return this->variables(x, t, variables_tags<DataType>{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nvoid test_solution(const std::array<double, Dim> left_velocity,\n                   const std::string& left_velocity_opt,\n                   const std::array<double, Dim> right_velocity,\n                   const std::string& right_velocity_opt,\n                   const DataType& used_for_size) {\n  // Member variables correspond to Sod tube test. For other test cases,\n  // new parameters in the star region must be given in the python modules.\n  RiemannProblemProxy<Dim> solution(1.4, 0.5, 1.0, left_velocity, 1.0, 0.125,\n                                    right_velocity, 0.1, 1.e-9);\n  pypp::check_with_random_values<1>(\n      &RiemannProblemProxy<Dim>::template primitive_variables<DataType>,\n      solution, \"RiemannProblem\",\n      {\"mass_density\", \"velocity\", \"pressure\", \"specific_internal_energy\"},\n      {{{0.0, 1.0}}},\n      std::make_tuple(1.4, 0.5, 1.0, left_velocity, 1.0, 0.125, right_velocity,\n                      0.1),\n      used_for_size, 1.e-9);\n\n  const auto solution_from_options = TestHelpers::test_creation<\n      NewtonianEuler::Solutions::RiemannProblem<Dim>>(\n      \"AdiabaticIndex: 1.4\\n\"\n      \"InitialPosition: 0.5\\n\"\n      \"LeftMassDensity: 1.0\\n\"\n      \"LeftVelocity: \" +\n      left_velocity_opt +\n      \"\\n\"\n      \"LeftPressure: 1.0\\n\"\n      \"RightMassDensity: 0.125\\n\"\n      \"RightVelocity: \" +\n      right_velocity_opt +\n      \"\\n\"\n      \"RightPressure: 0.1\\n\"\n      \"PressureStarTol: 1.e-9\");\n  CHECK(solution_from_options == solution);\n\n  RiemannProblemProxy<Dim> solution_to_move(1.4, 0.5, 1.0, left_velocity, 1.0,\n                                            0.125, right_velocity, 0.1, 1.e-9);\n  test_move_semantics(std::move(solution_to_move), solution);  //  NOLINT\n  test_serialization(solution);\n  test_copy_semantics(solution);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.NewtEuler.RiemannProblem\",\n    \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler\"};\n\n  test_solution<1>({{0.0}}, \"[0.0]\", {{0.0}}, \"[0.0]\",\n                   std::numeric_limits<double>::signaling_NaN());\n  test_solution<1>({{0.0}}, \"[0.0]\", {{0.0}}, \"[0.0]\", DataVector(5));\n  test_solution<2>({{0.0, 0.4}}, \"[0.0, 0.4]\", {{0.0, -0.3}}, \"[0.0, -0.3]\",\n                   std::numeric_limits<double>::signaling_NaN());\n  test_solution<2>({{0.0, 0.4}}, \"[0.0, 0.4]\", {{0.0, -0.3}}, \"[0.0, -0.3]\",\n                   DataVector(5));\n  test_solution<3>({{0.0, 0.4, -0.12}}, \"[0.0, 0.4, -0.12]\",\n                   {{0.0, -0.3, 0.53}}, \"[0.0, -0.3, 0.53]\",\n                   std::numeric_limits<double>::signaling_NaN());\n  test_solution<3>({{0.0, 0.4, -0.12}}, \"[0.0, 0.4, -0.12]\",\n                   {{0.0, -0.3, 0.53}}, \"[0.0, -0.3, 0.53]\", DataVector(5));\n}\n\n// Test correct evaluation of the variables in the star region for the five\n// initial setups listed in Table 4.1 of \\Toro2009. The numerical values to\n// compare with are listed in Table 4.2 of the same reference.\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.NewtEuler.RiemannProblem.StarRg\",\n    \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler\"};\n  // The accuracy of the numbers provided by Toro isn't too high so\n  // we need to adjust the precision of the comparison accordingly.\n  Approx larger_approx = Approx::custom().epsilon(1.e-5);\n\n  const double adiabatic_index = 1.4;\n  // Initial position doesn't affect the numbers tested below.\n  const double initial_position = 0.7;\n\n  // Sod or Shock-Tube problem: one shock wave and one rarefaction wave.\n  NewtonianEuler::Solutions::RiemannProblem<1> sod(\n      adiabatic_index, initial_position, 1.0, {{0.0}}, 1.0, 0.125, {{0.0}},\n      0.1);\n  CHECK_ITERABLE_CUSTOM_APPROX(sod.diagnostic_star_region_values(),\n                               make_array(0.30313, 0.92745), larger_approx);\n\n  // \"123\" problem: two strong rarefaction waves. Contact is stationary.\n  NewtonianEuler::Solutions::RiemannProblem<2> one_two_three(\n      adiabatic_index, initial_position, 1.0, {{-2.0}}, 0.4, 1.0, {{2.0}}, 0.4);\n  CHECK_ITERABLE_CUSTOM_APPROX(one_two_three.diagnostic_star_region_values(),\n                               make_array(0.00189, 0.),\n                               Approx::custom().epsilon(1.e-2));\n\n  // Left half of the blast wave problem of Woodward&Corella.\n  NewtonianEuler::Solutions::RiemannProblem<3> left_blast_wave(\n      adiabatic_index, initial_position, 1.0, {{0.0}}, 1000.0, 1.0, {{0.0}},\n      0.01);\n  CHECK_ITERABLE_CUSTOM_APPROX(left_blast_wave.diagnostic_star_region_values(),\n                               make_array(460.894, 19.5975), larger_approx);\n\n  // Right half of the blast wave problem of Woodward&Corella.\n  NewtonianEuler::Solutions::RiemannProblem<3> right_blast_wave(\n      adiabatic_index, initial_position, 1.0, {{0.0}}, 0.01, 1.0, {{0.0}},\n      100.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(right_blast_wave.diagnostic_star_region_values(),\n                               make_array(46.0950, -6.19633), larger_approx);\n\n  // Collision of the two previous shocks.\n  NewtonianEuler::Solutions::RiemannProblem<3> shock_collision(\n      adiabatic_index, initial_position, 5.99924, {{19.5975}}, 460.894, 5.99242,\n      {{-6.19633}}, 46.0950);\n  CHECK_ITERABLE_CUSTOM_APPROX(shock_collision.diagnostic_star_region_values(),\n                               make_array(1691.64, 8.68975), larger_approx);\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH((NewtonianEuler::Solutions::RiemannProblem<1>(\n                        1.4, 0.7, 1.0, {{0.0}}, 1.0, 0.125, {{30.0}}, 1.1)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The pressure positivity condition must be met.\"));\n  CHECK_THROWS_WITH(\n      (NewtonianEuler::Solutions::RiemannProblem<2>(1.4, 0.7, -1.0, {{0.0}},\n                                                    1.0, 0.125, {{30.0}}, 1.1)),\n      Catch::Matchers::ContainsSubstring(\"The mass density must be positive.\"));\n  CHECK_THROWS_WITH(\n      (NewtonianEuler::Solutions::RiemannProblem<3>(\n          1.4, 0.7, 1.0, {{0.0}}, -1.0, 0.125, {{30.0}}, 1.1)),\n      Catch::Matchers::ContainsSubstring(\"The pressure must be positive.\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/Test_SmoothFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/NewtonianEuler/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/NewtonianEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct SmoothFlowProxy : NewtonianEuler::Solutions::SmoothFlow<Dim> {\n  using NewtonianEuler::Solutions::SmoothFlow<Dim>::SmoothFlow;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, Dim>& x, const double t) const {\n    return this->variables(x, t, variables_tags<DataType>{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nvoid test_solution(const DataType& used_for_size,\n                   const std::array<double, Dim>& mean_velocity,\n                   const std::string& mean_velocity_opt,\n                   const std::array<double, Dim>& wave_vector,\n                   const std::string& wave_vector_opt) {\n  const double pressure = 1.23;\n  const double adiabatic_index = 1.3334;\n  const double perturbation_size = 0.78;\n\n  SmoothFlowProxy<Dim> solution(mean_velocity, wave_vector, pressure,\n                                adiabatic_index, perturbation_size);\n  pypp::check_with_random_values<1>(\n      &SmoothFlowProxy<Dim>::template primitive_variables<DataType>, solution,\n      \"Hydro.SmoothFlow\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"pressure\", \"specific_enthalpy\"},\n      {{{-15., 15.}}},\n      std::make_tuple(mean_velocity, wave_vector, pressure, adiabatic_index,\n                      perturbation_size),\n      used_for_size);\n\n  const auto solution_from_options =\n      TestHelpers::test_creation<NewtonianEuler::Solutions::SmoothFlow<Dim>>(\n          \"MeanVelocity: \" + mean_velocity_opt +\n          \"\\n\"\n          \"WaveVector: \" +\n          wave_vector_opt +\n          \"\\n\"\n          \"Pressure: 1.23\\n\"\n          \"AdiabaticIndex: 1.3334\\n\"\n          \"PerturbationSize: 0.78\");\n  CHECK(solution == solution_from_options);\n\n  NewtonianEuler::Solutions::SmoothFlow<Dim> solution_to_move(\n      mean_velocity, wave_vector, pressure, adiabatic_index, perturbation_size);\n\n  test_move_semantics(std::move(solution_to_move),\n                      solution_from_options);  //  NOLINT\n  test_serialization(solution);\n  test_copy_semantics(solution);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.NewtonianEuler.SmoothFlow\",\n    \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/\"};\n  test_solution<1>(std::numeric_limits<double>::signaling_NaN(), {{-0.3}},\n                   \"[-0.3]\", {{0.4}}, \"[0.4]\");\n  test_solution<1>(DataVector(5), {{-0.3}}, \"[-0.3]\", {{0.4}}, \"[0.4]\");\n  test_solution<2>(std::numeric_limits<double>::signaling_NaN(), {{-0.3, 0.1}},\n                   \"[-0.3, 0.1]\", {{0.4, -0.24}}, \"[0.4, -0.24]\");\n  test_solution<2>(DataVector(5), {{-0.3, 0.1}}, \"[-0.3, 0.1]\", {{0.4, -0.24}},\n                   \"[0.4, -0.24]\");\n  test_solution<3>(std::numeric_limits<double>::signaling_NaN(),\n                   {{-0.3, 0.1, -0.002}}, \"[-0.3, 0.1, -0.002]\",\n                   {{0.4, -0.24, 0.054}}, \"[0.4, -0.24, 0.054]\");\n  test_solution<3>(DataVector(5), {{-0.3, 0.1, -0.002}}, \"[-0.3, 0.1, -0.002]\",\n                   {{0.4, -0.24, 0.054}}, \"[0.4, -0.24, 0.054]\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/NewtonianEuler/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_PoissonSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_Lorentzian.cpp\n  Test_MathFunction.cpp\n  Test_Moustache.cpp\n  Test_ProductOfSinusoids.cpp\n  Test_Zero.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  Poisson\n  PoissonSolutions\n  Spectral\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/Gaussian.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef field(x, amplitude, width, center):\n    return amplitude * np.exp(-np.linalg.norm(x - center) ** 2 / width**2)\n\n\ndef field_gradient(x, amplitude, width, center):\n    return -2.0 * x / width**2 * field(x, amplitude, width, center)\n\n\ndef field_flux(x, amplitude, width, center):\n    return field_gradient(x, amplitude, width, center)\n\n\ndef source(x, amplitude, width, center):\n    return (\n        2.0 * len(x) / width**2 - 4.0 * np.linalg.norm(x) ** 2 / width**4\n    ) * field(x, amplitude, width, center)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/Lorentzian.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef field(x, constant, complex_phase):\n    result = 1.0 / np.sqrt(1.0 + np.dot(x, x)) + constant\n    if complex_phase != 0:\n        result *= np.exp(1j * complex_phase)\n    return result\n\n\ndef field_gradient(x, constant, complex_phase):\n    dtype = float if complex_phase == 0 else complex\n    result = -np.asarray(x, dtype) / np.sqrt(1.0 + np.dot(x, x)) ** 3\n    if complex_phase != 0:\n        result *= np.exp(1j * complex_phase)\n    return result\n\n\ndef field_flux(x, constant, complex_phase):\n    return field_gradient(x, constant, complex_phase)\n\n\ndef source(x, constant, complex_phase):\n    result = 3.0 / np.sqrt(1.0 + np.dot(x, x)) ** 5\n    if complex_phase != 0:\n        result *= np.exp(1j * complex_phase)\n    return result\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/Moustache.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef poly(x, coeffs):\n    return np.sum([c * x**p for p, c in enumerate(coeffs)])\n\n\ndef field(x):\n    return np.prod(x * (1.0 - x)) * np.sum((x - 0.5) ** 2) ** (3.0 / 2.0)\n\n\ndef field_gradient(x):\n    if len(x) == 1:\n        return np.abs(x - 0.5) * poly(x, [0.25, -3, 7.5, -5])\n    elif len(x) == 2:\n        return np.sqrt(np.sum((x - 0.5) ** 2)) * np.array(\n            [\n                x[d - 1]\n                * (1.0 - x[d - 1])\n                * (\n                    poly(x[d], [0.5, poly(x[d - 1], [-3.5, 2, -2]), 7.5, -5])\n                    + poly(x[d - 1], [0, -1, 1])\n                )\n                for d in range(len(x))\n            ]\n        )\n\n\ndef field_flux(x):\n    return field_gradient(x)\n\n\ndef source(x):\n    if len(x) == 1:\n        return np.abs(x[0] - 0.5) * (20.0 * (x[0] - 0.5) ** 2 - 1.5)\n    elif len(x) == 2:\n        r = np.linalg.norm(x - 0.5)\n        return r * (\n            -0.5625\n            + 6.25 * r**2\n            - 6.125 * r**4\n            + 4.125 * (x[0] - 0.5) ** 4\n            - 24.75 * (x[0] - 0.5) ** 2 * (x[1] - 0.5) ** 2\n            + 4.125 * (x[1] - 0.5) ** 4\n        )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/ProductOfSinusoids.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef field(x, wave_numbers, complex_phase):\n    x, wave_numbers = np.asarray(x), np.asarray(wave_numbers)\n    result = np.prod(np.sin(wave_numbers * x))\n    if complex_phase != 0.0:\n        result *= np.exp(1j * complex_phase)\n    return result\n\n\ndef field_gradient(x, wave_numbers, complex_phase):\n    x, wave_numbers = np.asarray(x), np.asarray(wave_numbers)\n    try:\n        dim = len(x)\n    except TypeError:\n        dim = 1\n    return (\n        wave_numbers\n        * np.cos(wave_numbers * x)\n        * np.array(\n            [\n                field(\n                    np.delete(x, d), np.delete(wave_numbers, d), complex_phase\n                )\n                for d in range(dim)\n            ]\n        )\n    )\n\n\ndef field_flux(x, wave_numbers, complex_phase):\n    return field_gradient(x, wave_numbers, complex_phase)\n\n\ndef source(x, wave_numbers, complex_phase):\n    x, wave_numbers = np.asarray(x), np.asarray(wave_numbers)\n    return np.sum(wave_numbers**2) * field(x, wave_numbers, complex_phase)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/Test_Lorentzian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Lorentzian.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim, typename DataType>\nstruct LorentzianProxy : Poisson::Solutions::Lorentzian<Dim, DataType> {\n  using Poisson::Solutions::Lorentzian<Dim, DataType>::Lorentzian;\n\n  using field_tags =\n      tmpl::list<Poisson::Tags::Field<DataType>,\n                 ::Tags::deriv<Poisson::Tags::Field<DataType>,\n                               tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::Tags::Flux<Poisson::Tags::Field<DataType>, tmpl::size_t<Dim>,\n                              Frame::Inertial>>;\n  using source_tags =\n      tmpl::list<Tags::FixedSource<Poisson::Tags::Field<DataType>>>;\n\n  tuples::tagged_tuple_from_typelist<field_tags> field_variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x) const {\n    return Poisson::Solutions::Lorentzian<Dim, DataType>::variables(\n        x, field_tags{});\n  }\n\n  tuples::tagged_tuple_from_typelist<source_tags> source_variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x) const {\n    return Poisson::Solutions::Lorentzian<Dim, DataType>::variables(\n        x, source_tags{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nvoid test_solution() {\n  double complex_phase = 0.0;  // NOLINT(misc-const-correctness)\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    complex_phase = 0.7;\n  }\n  const LorentzianProxy<Dim, DataType> solution{1.5, complex_phase};\n  pypp::check_with_random_values<1>(\n      &LorentzianProxy<Dim, DataType>::field_variables, solution, \"Lorentzian\",\n      {\"field\", \"field_gradient\", \"field_flux\"}, {{{-5., 5.}}},\n      std::make_tuple(1.5, complex_phase), DataVector(5));\n  pypp::check_with_random_values<1>(\n      &LorentzianProxy<Dim, DataType>::source_variables, solution, \"Lorentzian\",\n      {\"source\"}, {{{-5., 5.}}}, std::make_tuple(1.5, complex_phase),\n      DataVector(5));\n\n  const Poisson::Solutions::Lorentzian<Dim, DataType> check_solution{\n      1.2, complex_phase};\n  // NOLINTNEXTLINE(misc-const-correctness)\n  std::string option_string = \"PlusConstant: 1.2\";\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    option_string += \"\\nComplexPhase: 0.7\";\n  }\n  const auto created_solution =\n      TestHelpers::test_creation<Poisson::Solutions::Lorentzian<Dim, DataType>>(\n          option_string);\n  CHECK(created_solution == check_solution);\n  test_serialization(check_solution);\n  test_copy_semantics(check_solution);\n}\n\ntemplate <typename DataType>\nvoid test_lorentzian() {\n  // 1D and 2D solutions are not implemented yet.\n  test_solution<3, DataType>();\n  double complex_phase = 0.0;  // NOLINT(misc-const-correctness)\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    complex_phase = M_PI_4;\n  }\n\n  {\n    // Verify that the solution numerically solves the system and that the\n    // discretization error decreases exponentially with polynomial order\n    using system =\n        Poisson::FirstOrderSystem<3, Poisson::Geometry::FlatCartesian,\n                                  DataType>;\n    const Poisson::Solutions::Lorentzian<3, DataType> solution{1.0,\n                                                               complex_phase};\n    using AffineMap = domain::CoordinateMaps::Affine;\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap3D>\n        coord_map{\n            {{-1., 1., -0.5, 0.5}, {-1., 1., -0.5, 0.5}, {-1., 1., -0.5, 0.5}}};\n    FirstOrderEllipticSolutionsTestHelpers::verify_smooth_solution<system>(\n        solution, coord_map, 5.e1, 1.2,\n        [](const auto&... /*unused*/) { return std::tuple<>{}; });\n  }\n\n  {\n    // Verify that the solution also solves the non-euclidean system with a\n    // Euclidean metric. This is more a test of the system than of the solution.\n    using system =\n        Poisson::FirstOrderSystem<3, Poisson::Geometry::Curved, DataType>;\n    const Poisson::Solutions::Lorentzian<3, DataType> solution{1.0,\n                                                               complex_phase};\n    using AffineMap = domain::CoordinateMaps::Affine;\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap3D>\n        coord_map{\n            {{-1., 1., -0.5, 0.5}, {-1., 1., -0.5, 0.5}, {-1., 1., -0.5, 0.5}}};\n    Mesh<3> mesh{8, Spectral::Basis::Legendre,\n                 Spectral::Quadrature::GaussLobatto};\n    const DataVector used_for_size{mesh.number_of_grid_points()};\n    auto inv_spatial_metric =\n        make_with_value<tnsr::II<DataVector, 3>>(used_for_size, 0.);\n    get<0, 0>(inv_spatial_metric) = 1.;\n    get<1, 1>(inv_spatial_metric) = 1.;\n    get<2, 2>(inv_spatial_metric) = 1.;\n    const auto spatial_christoffel_contracted =\n        make_with_value<tnsr::i<DataVector, 3>>(used_for_size, 0.);\n    FirstOrderEllipticSolutionsTestHelpers::verify_solution<system>(\n        solution, mesh, coord_map, 0.1, std::make_tuple(inv_spatial_metric),\n        std::make_tuple(spatial_christoffel_contracted));\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Poisson.Lorentzian\",\n    \"[PointwiseFunctions][Unit]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/Poisson\"};\n  test_lorentzian<DataVector>();\n  test_lorentzian<ComplexDataVector>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/Test_MathFunction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Factory.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Poisson::Solutions {\nnamespace {\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<::MathFunction<Dim, Frame::Inertial>,\n                   MathFunctions::all_math_functions<Dim, Frame::Inertial>>>;\n  };\n  using component_list = tmpl::list<>;\n};\n\ntemplate <size_t Dim>\nstruct MathFunctionProxy : MathFunction<Dim> {\n  using MathFunction<Dim>::MathFunction;\n\n  using field_tags =\n      tmpl::list<Poisson::Tags::Field<DataVector>,\n                 ::Tags::deriv<Poisson::Tags::Field<DataVector>,\n                               tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                              tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::Tags::FixedSource<Poisson::Tags::Field<DataVector>>>;\n\n  tuples::tagged_tuple_from_typelist<field_tags> field_variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x) const {\n    return MathFunction<Dim>::variables(x, field_tags{});\n  }\n};\n\ntemplate <size_t Dim>\nvoid test_solution() {\n  CAPTURE(Dim);\n  const double amplitude = 0.5;\n  const double width = 1.;\n  const auto center = make_array<Dim>(0.);\n  const MathFunctions::Gaussian<Dim, Frame::Inertial> gaussian{amplitude, width,\n                                                               center};\n  const MathFunction<Dim> solution{\n      std::make_unique<MathFunctions::Gaussian<Dim, Frame::Inertial>>(\n          gaussian)};\n  register_factory_classes_with_charm<Metavariables<Dim>>();\n  {\n    INFO(\"Option-creation\");\n    const auto created =\n        TestHelpers::test_creation<MathFunction<Dim>, Metavariables<Dim>>(\n            \"Function:\\n\"\n            \"  Gaussian:\\n\"\n            \"    Amplitude: 0.5\\n\"\n            \"    Width: 1.\\n\"\n            \"    Center: \" +\n                []() -> std::string {\n              if constexpr (Dim == 1) {\n                return \"0.\";\n              } else if constexpr (Dim == 2) {\n                return \"[0., 0.]\";\n              } else {\n                return \"[0., 0., 0.]\";\n              }\n            }());\n    CHECK(solution == created);\n  }\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    MathFunction<Dim> moved{\n        std::make_unique<MathFunctions::Gaussian<Dim, Frame::Inertial>>(\n            gaussian)};\n    test_move_semantics(std::move(moved), solution);\n  }\n  {\n    INFO(\"Properties\");\n    CHECK(solution.math_function() == gaussian);\n  }\n  {\n    INFO(\"Random-value test\");\n    const MathFunctionProxy<Dim> proxy(\n        std::make_unique<MathFunctions::Gaussian<Dim, Frame::Inertial>>(\n            gaussian));\n    pypp::check_with_random_values<1>(\n        &MathFunctionProxy<Dim>::field_variables, proxy, \"Gaussian\",\n        {\"field\", \"field_gradient\", \"field_flux\", \"source\"}, {{{0., 1.}}},\n        std::make_tuple(amplitude, width, center), DataVector(5));\n  }\n  {\n    INFO(\"Verify solution solves the Poisson system\");\n    using system =\n        Poisson::FirstOrderSystem<Dim, Poisson::Geometry::FlatCartesian>;\n    const auto coord_map = []() {\n      using AffineMap = domain::CoordinateMaps::Affine;\n      if constexpr (Dim == 1) {\n        return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                     AffineMap>{{-1., 1., 0., 2.}};\n      } else if constexpr (Dim == 2) {\n        using AffineMap2D =\n            domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n        return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                     AffineMap2D>{\n            {{-1., 1., 0., 2.}, {-1., 1., 0., 2.}}};\n      } else {\n        using AffineMap3D =\n            domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap,\n                                                   AffineMap>;\n        return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                     AffineMap3D>{\n            {{-1., 1., 0., 2.}, {-1., 1., 0., 2.}, {-1., 1., 0., 2.}}};\n      }\n    }();\n    const Mesh<Dim> mesh{12, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto};\n    FirstOrderEllipticSolutionsTestHelpers::verify_solution<system>(\n        solution, mesh, coord_map, 2.e-4);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Poisson.MathFunction\",\n    \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/Poisson\"};\n  test_solution<1>();\n  test_solution<2>();\n  test_solution<3>();\n}\n\n}  // namespace Poisson::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/Test_Moustache.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Moustache.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct MoustacheProxy : Poisson::Solutions::Moustache<Dim> {\n  using Poisson::Solutions::Moustache<Dim>::Moustache;\n\n  using field_tags =\n      tmpl::list<Poisson::Tags::Field<DataVector>,\n                 ::Tags::deriv<Poisson::Tags::Field<DataVector>,\n                               tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::Tags::Flux<Poisson::Tags::Field<DataVector>,\n                              tmpl::size_t<Dim>, Frame::Inertial>>;\n  using source_tags =\n      tmpl::list<Tags::FixedSource<Poisson::Tags::Field<DataVector>>>;\n\n  tuples::tagged_tuple_from_typelist<field_tags> field_variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x) const {\n    return Poisson::Solutions::Moustache<Dim>::variables(x, field_tags{});\n  }\n\n  tuples::tagged_tuple_from_typelist<source_tags> source_variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x) const {\n    return Poisson::Solutions::Moustache<Dim>::variables(x, source_tags{});\n  }\n};\n\ntemplate <size_t Dim>\nvoid test_solution() {\n  const MoustacheProxy<Dim> solution{};\n  pypp::check_with_random_values<1>(\n      &MoustacheProxy<Dim>::field_variables, solution, \"Moustache\",\n      {\"field\", \"field_gradient\", \"field_flux\"}, {{{0., 1.}}},\n      std::make_tuple(), DataVector(5));\n  pypp::check_with_random_values<1>(\n      &MoustacheProxy<Dim>::source_variables, solution, \"Moustache\", {\"source\"},\n      {{{0., 1.}}}, std::make_tuple(), DataVector(5));\n\n  const auto created_solution =\n      TestHelpers::test_creation<Poisson::Solutions::Moustache<Dim>>(\"\");\n  CHECK(created_solution == solution);\n  test_serialization(solution);\n  test_copy_semantics(solution);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Poisson.Moustache\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/Poisson\"};\n\n  using AffineMap = domain::CoordinateMaps::Affine;\n  {\n    INFO(\"1D\");\n    test_solution<1>();\n\n    using system =\n        Poisson::FirstOrderSystem<1, Poisson::Geometry::FlatCartesian>;\n    const Poisson::Solutions::Moustache<1> solution{};\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap>\n        coord_map{{-1., 1., 0., 1.}};\n    FirstOrderEllipticSolutionsTestHelpers::\n        verify_solution_with_power_law_convergence<system>(solution, coord_map,\n                                                           3.e1, 2.5);\n  }\n  {\n    INFO(\"2D\");\n    test_solution<2>();\n\n    using system =\n        Poisson::FirstOrderSystem<2, Poisson::Geometry::FlatCartesian>;\n    const Poisson::Solutions::Moustache<2> solution{};\n    using AffineMap2D =\n        domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap2D>\n        coord_map{{{-1., 1., 0., 1.}, {-1., 1., 0., 1.}}};\n    FirstOrderEllipticSolutionsTestHelpers::\n        verify_solution_with_power_law_convergence<system>(solution, coord_map,\n                                                           5., 2.);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/Test_ProductOfSinusoids.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/ProductOfSinusoids.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim, typename DataType>\nstruct ProductOfSinusoidsProxy\n    : Poisson::Solutions::ProductOfSinusoids<Dim, DataType> {\n  using Poisson::Solutions::ProductOfSinusoids<Dim,\n                                               DataType>::ProductOfSinusoids;\n\n  using field_tags =\n      tmpl::list<Poisson::Tags::Field<DataType>,\n                 ::Tags::deriv<Poisson::Tags::Field<DataType>,\n                               tmpl::size_t<Dim>, Frame::Inertial>,\n                 ::Tags::Flux<Poisson::Tags::Field<DataType>, tmpl::size_t<Dim>,\n                              Frame::Inertial>>;\n  using source_tags =\n      tmpl::list<Tags::FixedSource<Poisson::Tags::Field<DataType>>>;\n\n  tuples::tagged_tuple_from_typelist<field_tags> field_variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x) const {\n    return Poisson::Solutions::ProductOfSinusoids<Dim, DataType>::variables(\n        x, field_tags{});\n  }\n\n  tuples::tagged_tuple_from_typelist<source_tags> source_variables(\n      const tnsr::I<DataVector, Dim, Frame::Inertial>& x) const {\n    return Poisson::Solutions::ProductOfSinusoids<Dim, DataType>::variables(\n        x, source_tags{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nvoid test_solution(const std::array<double, Dim>& wave_numbers,\n                   const double complex_phase, const std::string& options) {\n  const ProductOfSinusoidsProxy<Dim, DataType> solution(wave_numbers,\n                                                        complex_phase);\n  pypp::check_with_random_values<1>(\n      &ProductOfSinusoidsProxy<Dim, DataType>::field_variables, solution,\n      \"ProductOfSinusoids\", {\"field\", \"field_gradient\", \"field_flux\"},\n      {{{0., 2. * M_PI}}}, std::make_tuple(wave_numbers, complex_phase),\n      DataVector(5));\n  pypp::check_with_random_values<1>(\n      &ProductOfSinusoidsProxy<Dim, DataType>::source_variables, solution,\n      \"ProductOfSinusoids\", {\"source\"}, {{{0., 2. * M_PI}}},\n      std::make_tuple(wave_numbers, complex_phase), DataVector(5));\n\n  const auto created_solution = TestHelpers::test_creation<\n      Poisson::Solutions::ProductOfSinusoids<Dim, DataType>>(options);\n  CHECK(created_solution == solution);\n  test_serialization(solution);\n  test_copy_semantics(solution);\n}\n\ntemplate <typename DataType>\nvoid test_product_of_sinusoids() {\n  using AffineMap = domain::CoordinateMaps::Affine;\n  double complex_phase = 0.0;     // NOLINT(misc-const-correctness)\n  std::string complex_options{};  // NOLINT(misc-const-correctness)\n  if constexpr (std::is_same_v<DataType, ComplexDataVector>) {\n    complex_phase = 0.7;\n    complex_options = \"\\nComplexPhase: 0.7\";\n  }\n  {\n    INFO(\"1D\");\n    test_solution<1, DataType>({{0.5}}, complex_phase,\n                               \"WaveNumbers: [0.5]\" + complex_options);\n\n    using system =\n        Poisson::FirstOrderSystem<1, Poisson::Geometry::FlatCartesian,\n                                  DataType>;\n    const Poisson::Solutions::ProductOfSinusoids<1, DataType> solution{\n        {{0.5}}, complex_phase};\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap>\n        coord_map{{-1., 1., 0., M_PI}};\n    FirstOrderEllipticSolutionsTestHelpers::verify_smooth_solution<system>(\n        solution, coord_map, 1.e5, 3.,\n        [](const auto&... /*unused*/) { return std::tuple<>{}; });\n  }\n  {\n    INFO(\"2D\");\n    test_solution<2, DataType>({{0.5, 1.}}, complex_phase,\n                               \"WaveNumbers: [0.5, 1.]\" + complex_options);\n\n    using system =\n        Poisson::FirstOrderSystem<2, Poisson::Geometry::FlatCartesian,\n                                  DataType>;\n    const Poisson::Solutions::ProductOfSinusoids<2, DataType> solution{\n        {{0.5, 0.5}}, complex_phase};\n    using AffineMap2D =\n        domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap2D>\n        coord_map{{{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}}};\n    FirstOrderEllipticSolutionsTestHelpers::verify_smooth_solution<system>(\n        solution, coord_map, 1.e5, 3.,\n        [](const auto&... /*unused*/) { return std::tuple<>{}; });\n  }\n  {\n    INFO(\"3D\");\n    test_solution<3, DataType>({{1., 0.5, 1.5}}, complex_phase,\n                               \"WaveNumbers: [1., 0.5, 1.5]\" + complex_options);\n\n    using system =\n        Poisson::FirstOrderSystem<3, Poisson::Geometry::FlatCartesian,\n                                  DataType>;\n    const Poisson::Solutions::ProductOfSinusoids<3, DataType> solution{\n        {{0.5, 0.5, 0.5}}, complex_phase};\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap3D>\n        coord_map{\n            {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}}};\n    FirstOrderEllipticSolutionsTestHelpers::verify_smooth_solution<system>(\n        solution, coord_map, 1.e5, 3.,\n        [](const auto&... /*unused*/) { return std::tuple<>{}; });\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Poisson.ProductOfSinusoids\",\n    \"[PointwiseFunctions][Unit]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/Poisson\"};\n  test_product_of_sinusoids<DataVector>();\n  test_product_of_sinusoids<ComplexDataVector>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Poisson/Test_Zero.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Poisson/FirstOrderSystem.hpp\"\n#include \"Elliptic/Systems/Poisson/Geometry.hpp\"\n#include \"Elliptic/Systems/Poisson/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/FirstOrderEllipticSolutionsTestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Poisson/Zero.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nauto make_coord_map() {\n  using AffineMap = domain::CoordinateMaps::Affine;\n  if constexpr (Dim == 1) {\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap>{{-1., 1., 0., M_PI}};\n  } else if constexpr (Dim == 2) {\n    using AffineMap2D =\n        domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap2D>{\n        {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}}};\n  } else {\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap3D>{\n        {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}}};\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_solution() {\n  const Poisson::Solutions::Zero<Dim> solution{};\n  const auto created_solution =\n      TestHelpers::test_creation<Poisson::Solutions::Zero<Dim>>(\"\");\n  CHECK(created_solution == solution);\n  test_serialization(solution);\n  test_copy_semantics(solution);\n\n  using system =\n      Poisson::FirstOrderSystem<Dim, Poisson::Geometry::FlatCartesian>;\n  const Mesh<Dim> mesh{12, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto coord_map = make_coord_map<Dim>();\n  FirstOrderEllipticSolutionsTestHelpers::verify_solution<system>(\n      solution, mesh, coord_map, 1.e-14);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Poisson.Zero\",\n                  \"[PointwiseFunctions][Unit]\") {\n  test_solution<1>();\n  test_solution<2>();\n  test_solution<3>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_PuncturesSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_Flatness.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  DataStructuresHelpers\n  PuncturesSolutions\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Punctures/Test_Flatness.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <utility>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Punctures/Flatness.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n\nnamespace Punctures::Solutions {\n\nSPECTRE_TEST_CASE(\"Unit.AnalyticSolutions.Punctures.Flatness\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::analytic_data::AnalyticSolution, Flatness>(\"Flatness\");\n  REQUIRE(dynamic_cast<const Flatness*>(created.get()) != nullptr);\n  const auto& solution = dynamic_cast<const Flatness&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    test_copy_semantics(solution);\n    auto move_solution = solution;\n    test_move_semantics(std::move(move_solution), solution);\n  }\n  using test_tags = tmpl::list<\n      Tags::Field, ::Tags::deriv<Tags::Field, tmpl::size_t<3>, Frame::Inertial>,\n      Punctures::Tags::Alpha,\n      Punctures::Tags::TracelessConformalExtrinsicCurvature,\n      Punctures::Tags::Beta, ::Tags::FixedSource<Punctures::Tags::Field>>;\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  DataVector used_for_size{3};\n  const auto x = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&generator), make_not_null(&dist), used_for_size);\n  const auto vars =\n      variables_from_tagged_tuple(solution.variables(x, test_tags{}));\n  CHECK_VARIABLES_APPROX(vars, (Variables<test_tags>{3, 0.}));\n}\n\n}  // namespace Punctures::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RadiationTransport/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(M1Grey)\nadd_subdirectory(MonteCarlo)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_M1GreySolutions\")\n\nset(LIBRARY_SOURCES\n  Test_ConstantM1.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  M1Grey\n  M1GreySolutions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/ConstantM1.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef spatial_velocity(x, t, mean_velocity, comoving_energy_density):\n    return np.asarray(mean_velocity)\n\n\ndef lorentz_factor(x, t, mean_velocity, comoving_energy_density):\n    return 1.0 / np.sqrt(1.0 - np.linalg.norm(np.asarray(mean_velocity)) ** 2)\n\n\ndef tildeE(x, t, mean_velocity, comoving_energy_density):\n    w_sqr = 1.0 / (1.0 - np.linalg.norm(np.asarray(mean_velocity)) ** 2)\n    return comoving_energy_density / 3.0 * (4.0 * w_sqr - 1.0)\n\n\ndef tildeS(x, t, mean_velocity, comoving_energy_density):\n    w_sqr = 1.0 / (1.0 - np.linalg.norm(np.asarray(mean_velocity)) ** 2)\n    prefactor = 4.0 / 3.0 * comoving_energy_density * w_sqr\n    return np.asarray(mean_velocity) * prefactor\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/Test_ConstantM1.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/RadiationTransport/M1Grey/Tags.hpp\"\n#include \"Evolution/Systems/RadiationTransport/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/ConstantM1.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing InitialData = evolution::initial_data::InitialData;\nusing ConstantM1 = RadiationTransport::M1Grey::Solutions::ConstantM1;\n\nstruct ConstantM1Proxy : RadiationTransport::M1Grey::Solutions::ConstantM1 {\n  using ConstantM1::ConstantM1;\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3>,\n                 hydro::Tags::LorentzFactor<DataVector>>;\n\n  using m1_variables_tags =\n      tmpl::list<RadiationTransport::M1Grey::Tags::TildeE<\n                     Frame::Inertial, neutrinos::ElectronNeutrinos<0>>,\n                 RadiationTransport::M1Grey::Tags::TildeS<\n                     Frame::Inertial, neutrinos::ElectronNeutrinos<0>>>;\n\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags> hydro_variables(\n      const tnsr::I<DataVector, 3>& x, double t) const {\n    return variables(x, t, hydro_variables_tags{});\n  }\n\n  tuples::tagged_tuple_from_typelist<m1_variables_tags> m1_variables(\n      const tnsr::I<DataVector, 3>& x, double t) const {\n    return variables(x, t, m1_variables_tags{});\n  }\n};\n\nvoid test_create_from_options() {\n  register_classes_with_charm<ConstantM1>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData, ConstantM1>(\n          \"ConstantM1:\\n\"\n          \"  MeanVelocity: [0.0, 0.2, 0.1]\\n\"\n          \"  ComovingEnergyDensity: 1.3\");\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution =\n      dynamic_cast<const ConstantM1&>(*deserialized_option_solution);\n  CHECK(created_solution == ConstantM1({{0.0, 0.2, 0.1}}, 1.3));\n}\n\nvoid test_move() {\n  ConstantM1 flow({{0.24, 0.11, 0.04}}, 1.3);\n  const ConstantM1 flow_copy({{0.24, 0.11, 0.04}}, 1.3);\n  test_move_semantics(std::move(flow), flow_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  const ConstantM1 flow({{0.24, 0.11, 0.04}}, 1.3);\n  test_serialization(flow);\n}\n\nvoid test_derived() {\n  register_classes_with_charm<ConstantM1>();\n  const std::unique_ptr<InitialData> initial_data_ptr =\n      std::make_unique<ConstantM1>(std::array{0.24, 0.11, 0.04}, 1.3);\n  const std::unique_ptr<InitialData> deserialized_initial_data_ptr =\n      serialize_and_deserialize(initial_data_ptr)->get_clone();\n  CHECK(dynamic_cast<ConstantM1*>(deserialized_initial_data_ptr.get()) !=\n        nullptr);\n}\n\nvoid test_variables(const DataVector& used_for_size) {\n  const std::array<double, 3> mean_velocity = {{0.23, 0.01, 0.31}};\n  const double comoving_energy_density = 1.3;\n\n  // Test M1 variables\n  pypp::check_with_random_values<1>(\n      &ConstantM1Proxy::m1_variables,\n      ConstantM1Proxy(mean_velocity, comoving_energy_density), \"ConstantM1\",\n      {\"tildeE\", \"tildeS\"}, {{{-15., 15.}}},\n      std::make_tuple(mean_velocity, comoving_energy_density), used_for_size);\n\n  // Test hydro variables\n  pypp::check_with_random_values<1>(\n      &ConstantM1Proxy::hydro_variables,\n      ConstantM1Proxy(mean_velocity, comoving_energy_density), \"ConstantM1\",\n      {\"spatial_velocity\", \"lorentz_factor\"}, {{{-15., 15.}}},\n      std::make_tuple(mean_velocity, comoving_energy_density), used_for_size);\n\n  // Test a few of the GR components to make sure that the implementation\n  // correctly forwards to the background solution. Not meant to be extensive.\n  const RadiationTransport::M1Grey::Solutions::ConstantM1 soln(\n      mean_velocity, comoving_energy_density);\n  const auto coords =\n      make_with_value<tnsr::I<DataVector, 3>>(used_for_size, 1.0);\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0),\n      get<gr::Tags::Lapse<DataVector>>(soln.variables(\n          coords, 0.0, tmpl::list<gr::Tags::Lapse<DataVector>>{})));\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(soln.variables(\n          coords, 0.0,\n          tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataVector>>{})));\n  auto expected_spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(used_for_size,\n                                                                0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_spatial_metric.get(i, i) = 1.0;\n  }\n  const auto spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(soln.variables(\n          coords, 0.0, tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>>{}));\n  CHECK_ITERABLE_APPROX(expected_spatial_metric, spatial_metric);\n}\n\n}  // end namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.M1Grey.ConstantM1\",\n    \"[Unit][PointwiseFunctions]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n  test_derived();\n  test_variables(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RadiationTransport/M1Grey/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_MonteCarloSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_HomogeneousSphere.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  GeneralRelativity\n  GeneralRelativityHelpers\n  GeneralRelativitySolutions\n  H5\n  Hydro\n  MonteCarlo\n  MonteCarloSolutions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/Test_HomogeneousSphere.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Particles/MonteCarlo/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/Factory.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RadiationTransport/MonteCarlo/HomogeneousSphere.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing HomogeneousSphere =\n    RadiationTransport::MonteCarlo::Solutions::HomogeneousSphere;\nusing TabulatedEoS = EquationsOfState::Tabulated3D<true>;\n\nconst std::string h5_file_name_compose{\n    unit_test_src_path() +\n    \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n\nvoid test_create_from_options() {\n  register_classes_with_charm<HomogeneousSphere>();\n  const std::unique_ptr<InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData, HomogeneousSphere>(\n          \"HomogeneousSphere:\\n\"\n          \"  Radius: 1.0\\n\"\n          \"  Densities: [1.e-12, 1.e-3]\\n\"\n          \"  Temperatures: [0.01, 10.0]\\n\"\n          \"  ElectronFractions: [0.1,0.1]\\n\"\n          \"  EquationOfState:\\n\"\n          \"    Tabulated3D:\\n\"\n          \"       TableFilename: \" +\n          unit_test_src_path() +\n          \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\\n\"\n          \"       TableSubFilename: 'dd2'\");\n\n  register_derived_classes_with_charm<TabulatedEoS>();\n  Parallel::printf(\"%s\", h5_file_name_compose);\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution =\n      dynamic_cast<const HomogeneousSphere&>(*deserialized_option_solution);\n  CHECK(created_solution ==\n        HomogeneousSphere(\n            1.0, {{1.e-12, 1.e-3}}, {{0.01, 10.0}}, {{0.1, 0.1}},\n            std::make_unique<TabulatedEoS>(h5_file_name_compose, \"/dd2\")));\n  Parallel::printf(\"Testing HomogeneousSphere\\n\");\n}\n\nvoid test_move() {\n  HomogeneousSphere sphere(\n      1.0, {{1.e-12, 1.e-3}}, {{0.01, 10.0}}, {{0.1, 0.1}},\n      std::make_unique<TabulatedEoS>(h5_file_name_compose, \"/dd2\"));\n  const HomogeneousSphere sphere_copy(\n      1.0, {{1.e-12, 1.e-3}}, {{0.01, 10.0}}, {{0.1, 0.1}},\n      std::make_unique<TabulatedEoS>(h5_file_name_compose, \"/dd2\"));\n  test_move_semantics(std::move(sphere), sphere_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  const HomogeneousSphere sphere(\n      1.0, {{1.e-12, 1.e-3}}, {{0.01, 10.0}}, {{0.1, 0.1}},\n      std::make_unique<TabulatedEoS>(h5_file_name_compose, \"/dd2\"));\n  test_serialization(sphere);\n}\n\nvoid test_derived() {\n  register_classes_with_charm<HomogeneousSphere>();\n  register_derived_classes_with_charm<TabulatedEoS>();\n\n  const std::unique_ptr<InitialData> initial_data_ptr =\n      std::make_unique<HomogeneousSphere>(\n          1.0, std::array{1.e-12, 1.e-3}, std::array{0.01, 10.0},\n          std::array{0.1, 0.1},\n          std::make_unique<TabulatedEoS>(h5_file_name_compose, \"/dd2\"));\n  const std::unique_ptr<InitialData> deserialized_initial_data_ptr =\n      serialize_and_deserialize(initial_data_ptr)->get_clone();\n  CHECK(dynamic_cast<HomogeneousSphere*>(deserialized_initial_data_ptr.get()) !=\n        nullptr);\n}\n\nvoid test_variables(const DataVector& used_for_size) {\n  const double radius = 1.0;\n  const std::array<double, 2> densities = {{1.e-12, 1.e-3}};\n  const std::array<double, 2> temperatures = {{0.01, 10.0}};\n  const std::array<double, 2> electron_fractions = {{0.06, 0.08}};\n  const HomogeneousSphere soln(\n      radius, densities, temperatures, electron_fractions,\n      std::make_unique<TabulatedEoS>(h5_file_name_compose, \"/dd2\"));\n\n  // Test a few of the GR components to make sure that the implementation\n  // correctly forwards to the background solution. Not meant to be extensive.\n  auto coords = make_with_value<tnsr::I<DataVector, 3>>(used_for_size, 1.0);\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<double> rng_uniform(-1.5 * radius,\n                                                     1.5 * radius);\n  for (size_t p = 0; p < used_for_size.size(); p++) {\n    get<0>(coords)[p] = rng_uniform(generator);\n    get<1>(coords)[p] = rng_uniform(generator);\n    get<2>(coords)[p] = rng_uniform(generator);\n  }\n  auto coord_radius = get(magnitude(coords));\n  auto expected_temperature =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  auto expected_density =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  auto expected_electron_fraction =\n      make_with_value<Scalar<DataVector>>(used_for_size, 0.0);\n  for (size_t p = 0; p < used_for_size.size(); p++) {\n    get(expected_temperature)[p] =\n        (coord_radius[p] > radius) ? temperatures[1] : temperatures[0];\n    get(expected_density)[p] =\n        (coord_radius[p] > radius) ? densities[1] : densities[0];\n    get(expected_electron_fraction)[p] = (coord_radius[p] > radius)\n                                             ? electron_fractions[1]\n                                             : electron_fractions[0];\n  }\n  CHECK_ITERABLE_APPROX(\n      expected_temperature,\n      get<hydro::Tags::Temperature<DataVector>>(soln.variables(\n          coords, 0.0, tmpl::list<hydro::Tags::Temperature<DataVector>>{})));\n  CHECK_ITERABLE_APPROX(\n      expected_density,\n      get<hydro::Tags::RestMassDensity<DataVector>>(soln.variables(\n          coords, 0.0,\n          tmpl::list<hydro::Tags::RestMassDensity<DataVector>>{})));\n  CHECK_ITERABLE_APPROX(\n      expected_electron_fraction,\n      get<hydro::Tags::ElectronFraction<DataVector>>(soln.variables(\n          coords, 0.0,\n          tmpl::list<hydro::Tags::ElectronFraction<DataVector>>{})));\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0),\n      get<hydro::Tags::LorentzFactor<DataVector>>(soln.variables(\n          coords, 0.0, tmpl::list<hydro::Tags::LorentzFactor<DataVector>>{})));\n  const auto spatial_velocity =\n      get<hydro::Tags::SpatialVelocity<DataVector, 3>>(soln.variables(\n          coords, 0.0,\n          tmpl::list<hydro::Tags::SpatialVelocity<DataVector, 3>>{}));\n  const auto expected_spatial_velocity =\n      make_with_value<tnsr::I<DataVector, 3>>(used_for_size, 0.0);\n  CHECK_ITERABLE_APPROX(expected_spatial_velocity, spatial_velocity);\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0),\n      get<gr::Tags::Lapse<DataVector>>(soln.variables(\n          coords, 0.0, tmpl::list<gr::Tags::Lapse<DataVector>>{})));\n  CHECK_ITERABLE_APPROX(\n      make_with_value<Scalar<DataVector>>(used_for_size, 1.0),\n      get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(soln.variables(\n          coords, 0.0,\n          tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataVector>>{})));\n  auto expected_spatial_metric =\n      make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(used_for_size,\n                                                                0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    expected_spatial_metric.get(i, i) = 1.0;\n  }\n  const auto spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(soln.variables(\n          coords, 0.0, tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>>{}));\n  CHECK_ITERABLE_APPROX(expected_spatial_metric, spatial_metric);\n}\n\n}  // end namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.MonteCarlo.HomogeneousSphere\",\n    \"[Unit][PointwiseFunctions]\") {\n  test_create_from_options();\n  test_serialize();\n  test_move();\n  test_derived();\n  test_variables(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RadiationTransport/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_RelativisticEulerSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_FishboneMoncriefDisk.cpp\n  Test_RotatingStar.cpp\n  Test_SmoothFlow.cpp\n  Test_Tov.cpp\n  Test_TovStar.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Informer\n  RelativisticEulerSolutions\n  Options\n  Utilities\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Domain\n  DomainCreators\n  Hydro\n  RelativisticEulerSolutions\n  Options\n  Spectral\n  Utilities\n  ValenciaDivClean\n)\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\"\"\"\nBackground values (for density, temperature)\nFor more details, check the the comments on the background_*\nmember variables in FishboneMoncriefDisk.hpp.\n\"\"\"\nbackground_density_ = 1.0e-7\nbackground_temperature_ = 2.0e-5\n\n\ndef rho_max(\n    bh_mass,\n    bh_dimless_a,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n):\n    r_in = bh_mass * dimless_r_in\n    r_max = bh_mass * dimless_r_max\n    bh_spin_a = bh_mass * bh_dimless_a\n    l = angular_momentum(bh_mass, bh_spin_a, bh_mass * dimless_r_max)\n    Win = potential(l, r_in * r_in, 1.0, bh_mass, bh_spin_a)\n    Wmax = potential(l, r_max * r_max, 1.0, bh_mass, bh_spin_a)\n    h_max = np.exp(Win - Wmax)\n    return np.power(\n        (\n            (polytropic_exponent - 1.0)\n            * (h_max - 1.0)\n            / (polytropic_constant * polytropic_exponent)\n        ),\n        1.0 / (polytropic_exponent - 1.0),\n    )\n\n\ndef delta(r_sqrd, m, a):\n    return r_sqrd - 2.0 * m * np.sqrt(r_sqrd) + a**2\n\n\ndef transformation_matrix(x, a):\n    # Coordinate transformation matrix from SKS to KS.\n    # Returns the P^j_ibar from equation 10 of Spherical-KS documentation.\n    # Note, this assuemes the black hole spin is along z-direction.\n    r_sqrd = boyer_lindquist_r_sqrd(x)\n    r = np.sqrt(r_sqrd)\n    rho = np.sqrt(r_sqrd + a**2)\n    P_ji = np.diag([rho / r, rho / r, 1.0])\n    return P_ji\n\n\ndef jacobian_matrix(x, a):\n    # Jacbian matrix for coordinate transformation from SKS to KS.\n    # Returns T^i_jbar from equation 16 of Spherical-KS documentation.\n    # Note, this assuemes the black hole spin is along z-direction.\n    P_ij = transformation_matrix(x, a)\n\n    r_sqrd = boyer_lindquist_r_sqrd(x)\n    r = np.sqrt(r_sqrd)\n    rho = np.sqrt(r_sqrd + a**2)\n\n    F_ik = (-1.0 / (rho * r**3)) * np.diag([a**2, a**2, 0.0])\n    x_vec = np.array(x).T\n\n    T_ij = P_ij + np.outer(np.matmul(F_ik, x_vec), x_vec.T)\n\n    return T_ij.T\n\n\ndef inverse_jacobian_matrix(x, a):\n    # Inverse Jacbian matrix for coordinate transformation\n    # from KS to Spherical KS.\n    # Returns S^i_jbar from equation 17 of Spherical-KS documentation.\n    # Note, this assuemes the black hole spin is along z-direction.\n    T_ij = jacobian_matrix(x, a)\n    S_ij = np.linalg.inv(T_ij)\n    return S_ij\n\n\ndef sigma(r_sqrd, sin_theta_sqrd, a):\n    return r_sqrd + (1.0 - sin_theta_sqrd) * a**2\n\n\ndef ucase_a(r_sqrd, sin_theta_sqrd, m, a):\n    return (r_sqrd + a**2) ** 2 - delta(\n        r_sqrd, m, a\n    ) * sin_theta_sqrd * a**2\n\n\ndef boyer_lindquist_gtf(r_sqrd, sin_theta_sqrd, m, a):\n    return (\n        -2.0\n        * m\n        * np.sqrt(r_sqrd)\n        * a\n        * sin_theta_sqrd\n        / sigma(r_sqrd, sin_theta_sqrd, a)\n    )\n\n\ndef boyer_lindquist_gff(r_sqrd, sin_theta_sqrd, m, a):\n    return (\n        ucase_a(r_sqrd, sin_theta_sqrd, m, a)\n        * sin_theta_sqrd\n        / sigma(r_sqrd, sin_theta_sqrd, a)\n    )\n\n\ndef boyer_lindquist_r_sqrd(x):\n    # Here, we assume x is in Spherical-KS coordinates\n    return x[0] ** 2 + x[1] ** 2 + x[2] ** 2\n\n\ndef boyer_lindquist_sin_theta_sqrd(z_sqrd, r_sqrd):\n    return 1.0 - z_sqrd / r_sqrd\n\n\ndef kerr_schild_h(x, m, a):\n    r_sqrd = boyer_lindquist_r_sqrd(x)\n    return m * r_sqrd * np.sqrt(r_sqrd) / (r_sqrd**2 + a**2 * x[2] ** 2)\n\n\ndef kerr_schild_spatial_null_vec(x, m, a):\n    r_sqrd = boyer_lindquist_r_sqrd(x)\n    r = np.sqrt(r_sqrd)\n    rho = np.sqrt(r_sqrd + a**2)\n    denom = 1.0 / rho**2\n    # Again, we assume Spherical-KS coordinates\n    # xbar/r = x/rho, ybar/r = y/rho\n    # where rho^2 = r^2+a^2 and xbar and ybar are Spherical-KS\n    # Thus, we need to stick in the converseion factor of rho/r.\n    conv_fac = rho / r\n    l_vec = np.array(\n        [\n            (r * x[0] * conv_fac + a * x[1] * conv_fac) * denom,\n            (r * x[1] * conv_fac - a * x[0] * conv_fac) * denom,\n            x[2] / r,\n        ]\n    )\n    return l_vec\n\n\ndef sph_kerr_schild_spatial_null_form(x, m, a):\n    jac = jacobian_matrix(x, a)\n    l_vec = kerr_schild_spatial_null_vec(x, m, a)\n    return jac @ l_vec\n\n\ndef sph_kerr_schild_spatial_null_vec(x, m, a):\n    inv_jac = inverse_jacobian_matrix(x, a)\n    l_vec = kerr_schild_spatial_null_vec(x, m, a)\n    return inv_jac.T @ l_vec\n\n\ndef kerr_schild_lapse(x, m, a):\n    null_vector_0 = -1.0\n    return np.sqrt(\n        1.0\n        / (1.0 + 2.0 * kerr_schild_h(x, m, a) * null_vector_0 * null_vector_0)\n    )\n\n\ndef sph_kerr_schild_shift(x, m, a):\n    null_vector_0 = -1.0\n    return (\n        -2.0\n        * kerr_schild_h(x, m, a)\n        * null_vector_0\n        * kerr_schild_lapse(x, m, a) ** 2\n    ) * sph_kerr_schild_spatial_null_vec(x, m, a)\n\n\ndef sph_kerr_schild_spatial_metric(x, m, a):\n    prefactor = 2.0 * kerr_schild_h(x, m, a)\n    null_form = sph_kerr_schild_spatial_null_form(x, m, a)\n    T_ij = jacobian_matrix(x, a)\n    result = T_ij @ T_ij.T + prefactor * np.outer(null_form, null_form)\n    return result\n\n\ndef angular_momentum(m, a, rmax):\n    return np.sqrt(m) * (\n        (np.power(rmax, 1.5) + a * np.sqrt(m))\n        * (a**2 - 2.0 * a * np.sqrt(m) * np.sqrt(rmax) + rmax**2)\n        / (\n            2.0 * a * np.sqrt(m) * np.power(rmax, 1.5)\n            + (rmax - 3.0 * m) * rmax**2\n        )\n    )\n\n\ndef angular_velocity(angular_momentum, r_sqrd, sin_theta_sqrd, m, a):\n    prefactor = (\n        2.0\n        * angular_momentum\n        * delta(r_sqrd, m, a)\n        * sin_theta_sqrd\n        / np.power(boyer_lindquist_gff(r_sqrd, sin_theta_sqrd, m, a), 2.0)\n    )\n    return prefactor / (\n        1.0 + np.sqrt(1.0 + 2.0 * angular_momentum * prefactor)\n    ) - (\n        boyer_lindquist_gtf(r_sqrd, sin_theta_sqrd, m, a)\n        / boyer_lindquist_gff(r_sqrd, sin_theta_sqrd, m, a)\n    )\n\n\ndef u_t(angular_momentum, r_sqrd, sin_theta_sqrd, m, a):\n    return np.sqrt(\n        angular_momentum\n        / (\n            boyer_lindquist_gtf(r_sqrd, sin_theta_sqrd, m, a)\n            + angular_velocity(angular_momentum, r_sqrd, sin_theta_sqrd, m, a)\n            * boyer_lindquist_gff(r_sqrd, sin_theta_sqrd, m, a)\n        )\n    )\n\n\ndef potential(angular_momentum, r_sqrd, sin_theta_sqrd, m, a):\n    return angular_momentum * angular_velocity(\n        angular_momentum, r_sqrd, sin_theta_sqrd, m, a\n    ) - np.log(u_t(angular_momentum, r_sqrd, sin_theta_sqrd, m, a))\n\n\ndef specific_enthalpy(\n    x,\n    t,\n    bh_mass,\n    bh_dimless_a,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n):\n    r_in = bh_mass * dimless_r_in\n    bh_spin_a = bh_mass * bh_dimless_a\n    l = angular_momentum(bh_mass, bh_spin_a, bh_mass * dimless_r_max)\n    Win = potential(l, r_in * r_in, 1.0, bh_mass, bh_spin_a)\n    r_sqrd = boyer_lindquist_r_sqrd(x)\n    sin_theta_sqrd = boyer_lindquist_sin_theta_sqrd(x[2] * x[2], r_sqrd)\n    result = 1.0\n    if np.sqrt(r_sqrd * sin_theta_sqrd) >= r_in:\n        W = potential(l, r_sqrd, sin_theta_sqrd, bh_mass, bh_spin_a)\n        if W < Win:\n            result = np.exp(Win - W)\n\n    return result\n\n\ndef rest_mass_density(\n    x,\n    t,\n    bh_mass,\n    bh_dimless_a,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n):\n    rho_max_ = rho_max(\n        bh_mass,\n        bh_dimless_a,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n    )\n    return max(\n        (1 / rho_max_)\n        * np.power(\n            (polytropic_exponent - 1.0)\n            * (\n                specific_enthalpy(\n                    x,\n                    t,\n                    bh_mass,\n                    bh_dimless_a,\n                    dimless_r_in,\n                    dimless_r_max,\n                    polytropic_constant,\n                    polytropic_exponent,\n                    noise,\n                )\n                - 1.0\n            )\n            / (polytropic_exponent * polytropic_constant),\n            1.0 / (polytropic_exponent - 1.0),\n        ),\n        background_density_,\n    )\n\n\ndef specific_internal_energy(\n    x,\n    t,\n    bh_mass,\n    bh_dimless_a,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n):\n    rho_max_ = rho_max(\n        bh_mass,\n        bh_dimless_a,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n    )\n    return max(\n        polytropic_constant\n        * np.power(\n            rho_max_\n            * rest_mass_density(\n                x,\n                t,\n                bh_mass,\n                bh_dimless_a,\n                dimless_r_in,\n                dimless_r_max,\n                polytropic_constant,\n                polytropic_exponent,\n                noise,\n            ),\n            polytropic_exponent - 1.0,\n        )\n        / (polytropic_exponent - 1.0),\n        background_temperature_ / (polytropic_exponent - 1.0),\n    )\n\n\ndef pressure(\n    x,\n    t,\n    bh_mass,\n    bh_dimless_a,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n):\n    rho_max_ = rho_max(\n        bh_mass,\n        bh_dimless_a,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n    )\n    return max(\n        (1 / rho_max_)\n        * polytropic_constant\n        * np.power(\n            rho_max_\n            * rest_mass_density(\n                x,\n                t,\n                bh_mass,\n                bh_dimless_a,\n                dimless_r_in,\n                dimless_r_max,\n                polytropic_constant,\n                polytropic_exponent,\n                noise,\n            ),\n            polytropic_exponent,\n        ),\n        background_density_ * background_temperature_,\n    )\n\n\ndef spatial_velocity(\n    x,\n    t,\n    bh_mass,\n    bh_dimless_a,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n):\n    r_in = bh_mass * dimless_r_in\n    bh_spin_a = bh_mass * bh_dimless_a\n    l = angular_momentum(bh_mass, bh_spin_a, bh_mass * dimless_r_max)\n    Win = potential(l, r_in * r_in, 1.0, bh_mass, bh_spin_a)\n    r_sqrd = boyer_lindquist_r_sqrd(x)\n    sin_theta_sqrd = boyer_lindquist_sin_theta_sqrd(x[2] * x[2], r_sqrd)\n    sks_to_ks_factor = np.sqrt(r_sqrd + bh_spin_a**2) / np.sqrt(r_sqrd)\n\n    result = np.array([0.0, 0.0, 0.0])\n    if np.sqrt(r_sqrd * sin_theta_sqrd) >= r_in:\n        W = potential(l, r_sqrd, sin_theta_sqrd, bh_mass, bh_spin_a)\n        if W < Win:\n            transport_velocity_ks = (\n                sks_to_ks_factor\n                * np.array([-x[1], x[0], 0.0])\n                * angular_velocity(\n                    l, r_sqrd, sin_theta_sqrd, bh_mass, bh_spin_a\n                )\n            )\n            transport_velocity_sks = (\n                inverse_jacobian_matrix(x, bh_spin_a) @ transport_velocity_ks\n            )\n            result += (\n                transport_velocity_sks\n                + sph_kerr_schild_shift(x, bh_mass, bh_spin_a)\n            ) / kerr_schild_lapse(x, bh_mass, bh_spin_a)\n    return np.array(result)\n\n\ndef lorentz_factor(\n    x,\n    t,\n    bh_mass,\n    bh_dimless_a,\n    dimless_r_in,\n    dimless_r_max,\n    polytropic_constant,\n    polytropic_exponent,\n    noise,\n):\n    bh_spin_a = bh_mass * bh_dimless_a\n    spatial_metric = sph_kerr_schild_spatial_metric(x, bh_mass, bh_spin_a)\n    velocity = spatial_velocity(\n        x,\n        t,\n        bh_mass,\n        bh_dimless_a,\n        dimless_r_in,\n        dimless_r_max,\n        polytropic_constant,\n        polytropic_exponent,\n        noise,\n    )\n    return 1.0 / np.sqrt(\n        1.0 - np.einsum(\"i,j,ij\", velocity, velocity, spatial_metric)\n    )\n\n\ndef magnetic_field(*args):\n    return np.array([0.0, 0.0, 0.0])\n\n\ndef divergence_cleaning_field(*args):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.AnalyticSolutions.RelativisticEuler.Python.Tov\"\n  \"Test_Tov.py\"\n  \"Unit;PointwiseFunctions;Python\"\n  PyRelativisticEulerSolutions)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Python/Test_Tov.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.PointwiseFunctions.AnalyticSolutions.RelativisticEuler as spectre_re_solutions\nimport spectre.PointwiseFunctions.Hydro.EquationsOfState as spectre_eos\n\n\nclass TestTov(unittest.TestCase):\n    def test_creation(self):\n        eos = spectre_eos.RelativisticPolytropicFluid(\n            polytropic_constant=8, polytropic_exponent=2\n        )\n        tov = spectre_re_solutions.Tov(\n            equation_of_state=eos, central_mass_density=1e-3\n        )\n        # Just making sure we can call the member functions\n        outer_radius = tov.outer_radius()\n        self.assertAlmostEqual(outer_radius, 3.4685521362)\n        expected_mass = 0.0531036941\n        self.assertAlmostEqual(tov.total_mass(), expected_mass)\n        self.assertAlmostEqual(\n            tov.injection_energy(),\n            np.sqrt(1.0 - 2.0 * tov.total_mass() / outer_radius),\n        )\n        self.assertAlmostEqual(\n            tov.mass_over_radius(outer_radius) * outer_radius, expected_mass\n        )\n        self.assertAlmostEqual(tov.log_specific_enthalpy(outer_radius), 0.0)\n        # Test vectorization of member functions\n        radii = np.array([0.0, outer_radius])\n        npt.assert_allclose(\n            tov.mass_over_radius(radii) * radii, np.array([0.0, expected_mass])\n        )\n        # Testing `log_specific_enthalpy` only at outer radius because we\n        # haven't wrapped any EOS functions yet, so it's not trivial to compute\n        # the specific enthalpy at other points\n        npt.assert_allclose(\n            tov.log_specific_enthalpy(np.array([outer_radius, outer_radius])),\n            np.array([0.0, 0.0]),\n            atol=1e-14,\n            rtol=0.0,\n        )\n        # Test accessing interpolants\n        self.assertTrue(len(tov.mass_over_radius_interpolant.y_values()) > 0)\n        self.assertTrue(\n            len(tov.log_specific_enthalpy_interpolant.y_values()) > 0\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Test_FishboneMoncriefDisk.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GrMhd/VerifyGrMhdSolution.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/FishboneMoncriefDisk.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct FishboneMoncriefDiskProxy\n    : RelativisticEuler::Solutions::FishboneMoncriefDisk {\n  using RelativisticEuler::Solutions::FishboneMoncriefDisk::\n      FishboneMoncriefDisk;\n\n  template <typename DataType>\n  using hydro_variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, 3>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  using grmhd_variables_tags =\n      tmpl::push_back<hydro_variables_tags<DataType>,\n                      hydro::Tags::MagneticField<DataType, 3>,\n                      hydro::Tags::DivergenceCleaningField<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<hydro_variables_tags<DataType>>\n  hydro_variables(const tnsr::I<DataType, 3>& x, double t) const {\n    return variables(x, t, hydro_variables_tags<DataType>{});\n  }\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<grmhd_variables_tags<DataType>>\n  grmhd_variables(const tnsr::I<DataType, 3>& x, double t) const {\n    return variables(x, t, grmhd_variables_tags<DataType>{});\n  }\n};\n\nvoid test_create_from_options() {\n  const auto disk = TestHelpers::test_creation<\n      RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n      \"BhMass: 1.0\\n\"\n      \"BhDimlessSpin: 0.23\\n\"\n      \"InnerEdgeRadius: 6.0\\n\"\n      \"MaxPressureRadius: 12.0\\n\"\n      \"PolytropicConstant: 0.001\\n\"\n      \"PolytropicExponent: 1.4\\n\"\n      \"Noise: 0.0\");\n  CHECK(disk == RelativisticEuler::Solutions::FishboneMoncriefDisk(\n                    1.0, 0.23, 6.0, 12.0, 0.001, 1.4, 0.0));\n}\n\nvoid test_move() {\n  RelativisticEuler::Solutions::FishboneMoncriefDisk disk(3.45, 0.23, 4.8, 8.6,\n                                                          0.02, 1.5, 0.0);\n  const RelativisticEuler::Solutions::FishboneMoncriefDisk disk_copy(\n      3.45, 0.23, 4.8, 8.6, 0.02, 1.5, 0.0);\n  test_move_semantics(std::move(disk), disk_copy);  //  NOLINT\n}\n\nvoid test_serialize() {\n  RelativisticEuler::Solutions::FishboneMoncriefDisk disk(4.21, 0.65, 6.0, 12.0,\n                                                          0.001, 1.4, 0.0);\n  test_serialization(disk);\n}\n\ntemplate <typename DataType>\nvoid test_variables(const DataType& used_for_size) {\n  const double bh_mass = 1.23;\n  const double bh_dimless_spin = 0.94432;\n  const double inner_edge_radius = 6.0;\n  const double max_pressure_radius = 12.0;\n  const double polytropic_constant = 0.001;\n  const double polytropic_exponent = 4.0 / 3.0;\n  const double noise = 0.0;\n\n  const FishboneMoncriefDiskProxy disk(\n      bh_mass, bh_dimless_spin, inner_edge_radius, max_pressure_radius,\n      polytropic_constant, polytropic_exponent, noise);\n  const auto member_variables = std::make_tuple(\n      bh_mass, bh_dimless_spin, inner_edge_radius, max_pressure_radius,\n      polytropic_constant, polytropic_exponent, noise);\n\n  pypp::check_with_random_values<1>(\n      &FishboneMoncriefDiskProxy::hydro_variables<DataType>, disk,\n      \"FishboneMoncriefDisk\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"pressure\", \"lorentz_factor\", \"specific_enthalpy\"},\n      {{{-15., 15.}}}, member_variables, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &FishboneMoncriefDiskProxy::grmhd_variables<DataType>, disk,\n      \"FishboneMoncriefDisk\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"pressure\", \"lorentz_factor\", \"specific_enthalpy\", \"magnetic_field\",\n       \"divergence_cleaning_field\"},\n      {{{-15., 15.}}}, member_variables, used_for_size);\n\n  // Test a few of the GR components to make sure that the implementation\n  // correctly forwards to the background solution. Not meant to be extensive.\n  const auto coords = make_with_value<tnsr::I<DataType, 3>>(used_for_size, 1.0);\n  const gr::Solutions::SphericalKerrSchild sks_soln{\n      bh_mass, {{0.0, 0.0, bh_dimless_spin}}, {{0.0, 0.0, 0.0}}};\n  CHECK_ITERABLE_APPROX(\n      get<gr::Tags::Lapse<DataType>>(sks_soln.variables(\n          coords, 0.0, gr::Solutions::SphericalKerrSchild::tags<DataType>{})),\n      get<gr::Tags::Lapse<DataType>>(disk.variables(\n          coords, 0.0, tmpl::list<gr::Tags::Lapse<DataType>>{})));\n  CHECK_ITERABLE_APPROX(\n      get<gr::Tags::SqrtDetSpatialMetric<DataType>>(sks_soln.variables(\n          coords, 0.0, gr::Solutions::SphericalKerrSchild::tags<DataType>{})),\n      get<gr::Tags::SqrtDetSpatialMetric<DataType>>(disk.variables(\n          coords, 0.0,\n          tmpl::list<gr::Tags::SqrtDetSpatialMetric<DataType>>{})));\n  const auto expected_spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3>>(sks_soln.variables(\n          coords, 0.0, gr::Solutions::SphericalKerrSchild::tags<DataType>{}));\n  const auto spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, 3>>(disk.variables(\n          coords, 0.0, tmpl::list<gr::Tags::SpatialMetric<DataType, 3>>{}));\n  CHECK_ITERABLE_APPROX(expected_spatial_metric, spatial_metric);\n}\n\n//  A former implementation of the `IntermediateVariables` function in\n//  `FishboneMoncriefDisk.cpp` included an ill-defined calculation of\n//  sin_theta_squared, which resulted in negative numbers due to round-off\n//  errors. This would induce FPEs after taking the square root of\n//  sin_theta_squared. This test evaluates the most recent implementation at\n//  those points in order to ensure that the FPEs are no longer induced.\ntemplate <typename DataType>\nvoid test_sin_theta_squared(const DataType& used_for_size) {\n  // Numbers below reproduce the initial data the bug was spotted with,\n  // along with the points where the FPEs were found.\n  const FishboneMoncriefDiskProxy disk(1.0, 0.9375, 6.0, 12.0, 0.001,\n                                       1.3333333333333333333333, 0.0);\n  using variables_tags =\n      FishboneMoncriefDiskProxy::grmhd_variables_tags<DataType>;\n  auto coords = make_with_value<tnsr::I<DataType, 3>>(used_for_size, 0.0);\n  get<2>(coords) = -1.8427400003643177317514;\n  disk.variables(coords, 0.0, variables_tags{});\n  get<2>(coords) = 1.8427400003643177317514;\n  disk.variables(coords, 0.0, variables_tags{});\n  get<2>(coords) = -1.9588918957550884858421;\n  disk.variables(coords, 0.0, variables_tags{});\n  get<2>(coords) = 1.9588918957550884858421;\n  disk.variables(coords, 0.0, variables_tags{});\n  CHECK(true);\n}\n\nvoid test_solution() {\n  register_classes_with_charm<\n      RelativisticEuler::Solutions::FishboneMoncriefDisk>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n          \"FishboneMoncriefDisk:\\n\"\n          \"  BhMass: 1.0\\n\"\n          \"  BhDimlessSpin: 0.9375\\n\"\n          \"  InnerEdgeRadius: 6.0\\n\"\n          \"  MaxPressureRadius: 12.0\\n\"\n          \"  PolytropicConstant: 0.001\\n\"\n          \"  PolytropicExponent: 1.33333333333333333\\n\"\n          \"  Noise: 0.0\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution =\n      dynamic_cast<const RelativisticEuler::Solutions::FishboneMoncriefDisk&>(\n          *deserialized_option_solution);\n\n  const std::array<double, 3> x{{5.0, 5.0, 0.0}};\n  const std::array<double, 3> dx{{1.e-1, 1.e-1, 1.e-1}};\n\n  domain::creators::Brick brick(x - dx, x + dx, {{0, 0, 0}}, {{8, 8, 8}},\n                                {{false, false, false}});\n  Mesh<3> mesh{brick.initial_extents()[0], Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto};\n  const auto domain = brick.create_domain();\n  verify_grmhd_solution(solution, domain.blocks()[0], mesh, 1.e-9, 1.234,\n                        1.e-1);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.RelEuler.FMDisk\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler\"};\n\n  test_create_from_options();\n  test_serialize();\n  test_move();\n\n  test_variables(std::numeric_limits<double>::signaling_NaN());\n  test_variables(DataVector(5));\n  test_sin_theta_squared(std::numeric_limits<double>::signaling_NaN());\n  test_sin_theta_squared(DataVector(5));\n\n  test_solution();\n\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n                        \"BhMass: -1.5\\n\"\n                        \"BhDimlessSpin: 0.3\\n\"\n                        \"InnerEdgeRadius: 4.3\\n\"\n                        \"MaxPressureRadius: 6.7\\n\"\n                        \"PolytropicConstant: 0.12\\n\"\n                        \"PolytropicExponent: 1.5\\n\"\n                        \"Noise : 0.0\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -1.5 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n                        \"BhMass: 1.5\\n\"\n                        \"BhDimlessSpin: -0.24\\n\"\n                        \"InnerEdgeRadius: 5.76\\n\"\n                        \"MaxPressureRadius: 13.2\\n\"\n                        \"PolytropicConstant: 0.002\\n\"\n                        \"PolytropicExponent: 1.34\\n\"\n                        \"Noise: 0.0\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.24 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n                        \"BhMass: 1.5\\n\"\n                        \"BhDimlessSpin: 1.24\\n\"\n                        \"InnerEdgeRadius: 5.76\\n\"\n                        \"MaxPressureRadius: 13.2\\n\"\n                        \"PolytropicConstant: 0.002\\n\"\n                        \"PolytropicExponent: 1.34\\n\"\n                        \"Noise: 0.0\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value 1.24 is above the upper bound of 1\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n                        \"BhMass: 1.5\\n\"\n                        \"BhDimlessSpin: 0.3\\n\"\n                        \"InnerEdgeRadius: 4.3\\n\"\n                        \"MaxPressureRadius: 6.7\\n\"\n                        \"PolytropicConstant: -0.12\\n\"\n                        \"PolytropicExponent: 1.5\\n\"\n                        \"Noise: 0.0\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -0.12 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n                        \"BhMass: 1.5\\n\"\n                        \"BhDimlessSpin: 0.3\\n\"\n                        \"InnerEdgeRadius: 4.3\\n\"\n                        \"MaxPressureRadius: 6.7\\n\"\n                        \"PolytropicConstant: 0.123\\n\"\n                        \"PolytropicExponent: 0.25\\n\"\n                        \"Noise: 0.0\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value 0.25 is below the lower bound of 1\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n                        \"BhMass: 1.5\\n\"\n                        \"BhDimlessSpin: 0.3\\n\"\n                        \"InnerEdgeRadius: 4.3\\n\"\n                        \"MaxPressureRadius: 6.7\\n\"\n                        \"PolytropicConstant: 0.123\\n\"\n                        \"PolytropicExponent: 1.5\\n\"\n                        \"Noise: -1.25\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value -1.25 is below the lower bound of 0\"));\n  CHECK_THROWS_WITH(TestHelpers::test_creation<\n                        RelativisticEuler::Solutions::FishboneMoncriefDisk>(\n                        \"BhMass: 1.5\\n\"\n                        \"BhDimlessSpin: 0.3\\n\"\n                        \"InnerEdgeRadius: 4.3\\n\"\n                        \"MaxPressureRadius: 6.7\\n\"\n                        \"PolytropicConstant: 0.123\\n\"\n                        \"PolytropicExponent: 1.5\\n\"\n                        \"Noise: 1.5\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Value 1.5 is above the upper bound of 1\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Test_RotatingStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <pup.h>\n\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GrMhd/VerifyGrMhdSolution.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid verify_solution(const RelativisticEuler::Solutions::RotatingStar& solution,\n                     const std::array<double, 3>& x,\n                     const double error_tolerance) {\n  CAPTURE(x);\n  const std::array<double, 3> dx{{1.e-4, 1.e-4, 1.e-4}};\n  domain::creators::Brick brick(x - dx, x + dx, {{0, 0, 0}}, {{5, 5, 5}},\n                                {{false, false, false}});\n  Mesh<3> mesh{brick.initial_extents()[0], Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto};\n  const auto domain = brick.create_domain();\n\n  verify_grmhd_solution(solution, domain.blocks()[0], mesh, error_tolerance,\n                        1.234, 1.e-4);\n}\n\nvoid verify_solution_suite(\n    const RelativisticEuler::Solutions::RotatingStar& solution,\n    const double error_tolerance) {\n  // Near the center the finite difference derivatives cause \"large\" errors\n  // (1e-8) and so the check fails.\n  verify_solution(solution, {{1., 0., 0.}}, error_tolerance);\n  verify_solution(solution, {{-1., 0., 0.}}, error_tolerance);\n  verify_solution(solution, {{0., 1., 0.}}, error_tolerance);\n  verify_solution(solution, {{0., -1., 0.}}, error_tolerance);\n  verify_solution(solution, {{0., 0., 1.}}, error_tolerance);\n  verify_solution(solution, {{0., 0., -1.}}, error_tolerance);\n  verify_solution(solution, {{1., 0.528, 0.2}}, error_tolerance);\n  verify_solution(solution, {{1., 0.528, -0.2}}, error_tolerance);\n  verify_solution(solution, {{1., -0.528, 0.2}}, error_tolerance);\n  verify_solution(solution, {{-1., 0.528, 0.2}}, error_tolerance);\n  verify_solution(solution, {{1., -0.528, -0.2}}, error_tolerance);\n  verify_solution(solution, {{-1., 0.528, -0.2}}, error_tolerance);\n  verify_solution(solution, {{-1., -0.528, 0.2}}, error_tolerance);\n  verify_solution(solution, {{-1., -0.528, -0.2}}, error_tolerance);\n  // Test outside the star\n  verify_solution(solution, {{20., 20., 20.}}, error_tolerance);\n  verify_solution(solution, {{20., 20., -20.}}, error_tolerance);\n  verify_solution(solution, {{20., -20., 20.}}, error_tolerance);\n  verify_solution(solution, {{-20., 20., 20.}}, error_tolerance);\n  verify_solution(solution, {{20., -20., -20.}}, error_tolerance);\n  verify_solution(solution, {{-20., 20., -20.}}, error_tolerance);\n  verify_solution(solution, {{-20., -20., 20.}}, error_tolerance);\n  verify_solution(solution, {{-20., -20., -20.}}, error_tolerance);\n  verify_solution(solution, {{20., 0., 0.}}, error_tolerance);\n  verify_solution(solution, {{0., 20., 0.}}, error_tolerance);\n  verify_solution(solution, {{0., 0., 20.}}, error_tolerance);\n}\n}  // namespace\n\n// [[Timeout, 10]]\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.RelEuler.RotatingStar\",\n    \"[Unit][PointwiseFunctions]\") {\n  PUPable_reg(RelativisticEuler::Solutions::RotatingStar);\n  register_derived_classes_with_charm<\n      EquationsOfState::EquationOfState<true, 1>>();\n  // You can uncomment the cst_solution code below, include <iostream>,\n  // and update the path to the `dat` file in order to figure out what the\n  // central rest mass density is.\n  // RelativisticEuler::Solutions::detail::CstSolution cst_solution(\n  //     \"/path/to/InitialData.dat\", true, 100);\n  // std::cout << cst_solution.interpolate(0.0, 0.0, true) << \"\\n\\n\";\n\n  // The tolerance depends on the resolution that the RotNS file used. In order\n  // to avoid storing very large files in the repo, we accept a larger\n  // tolerance. However, Nils Deppe verified on Jan. 5, 2022 that increasing the\n  // RotNS code resolution allows for a stricter tolerance.\n\n  const RelativisticEuler::Solutions::detail::CstSolution cst_solution(\n      unit_test_src_path() +\n          \"/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/\"\n          \"RotatingStarId.dat\",\n      true, 100.);\n  // The following line would fail under the original stencil construction due\n  // to rounding errors.\n  cst_solution.interpolate(5.85, 0.64999999999, true);\n\n  register_classes_with_charm<RelativisticEuler::Solutions::RotatingStar>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          RelativisticEuler::Solutions::RotatingStar>(\n          \"RotatingStar:\\n\"\n          \"  RotNsFilename: \" +\n          unit_test_src_path() +\n          \"/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/\"\n          \"RotatingStarId.dat\\n\"\n          \"  PolytropicConstant: 100.0\\n\")\n          ->get_clone();\n\n  const double error_tolerance = 4.e-6;\n  const RelativisticEuler::Solutions::RotatingStar solution(\n      dynamic_cast<const RelativisticEuler::Solutions::RotatingStar&>(\n          *serialize_and_deserialize(\n              RelativisticEuler::Solutions::RotatingStar(\n                  unit_test_src_path() +\n                      \"/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/\"\n                      \"RotatingStarId.dat\",\n                  100)\n                  .get_clone())));\n\n  CHECK(solution.equatorial_radius() == approx(7.155891353887));\n\n  verify_solution_suite(solution, error_tolerance);\n\n  const RelativisticEuler::Solutions::RotatingStar hybrid_solution(\n      dynamic_cast<const RelativisticEuler::Solutions::RotatingStar&>(\n          *serialize_and_deserialize(\n              RelativisticEuler::Solutions::RotatingStar(\n                  unit_test_src_path() +\n                      \"/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/\"\n                      \"RotatingStarId_Hybrid.dat\",\n                  std::make_unique<EquationsOfState::PolytropicFluid<true>>(\n                      123.6, 2.0))\n                  .get_clone())));\n\n  CHECK(hybrid_solution.equatorial_radius() == approx(10.74552403267997));\n\n  verify_solution_suite(hybrid_solution, error_tolerance);\n\n  CHECK_THROWS_WITH(\n      RelativisticEuler::Solutions::RotatingStar(\n          unit_test_src_path() +\n              \"/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/\"\n              \"RotatingStarIdDoesNotExist.dat\",\n          100),\n      Catch::Matchers::ContainsSubstring(\"Cannot open file\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Test_SmoothFlow.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/ParseOptions.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/SmoothFlow.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nstruct SmoothFlowProxy : RelativisticEuler::Solutions::SmoothFlow<Dim> {\n  using RelativisticEuler::Solutions::SmoothFlow<Dim>::SmoothFlow;\n\n  template <typename DataType>\n  using variables_tags =\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>,\n                 hydro::Tags::SpatialVelocity<DataType, Dim>,\n                 hydro::Tags::SpecificInternalEnergy<DataType>,\n                 hydro::Tags::Pressure<DataType>,\n                 hydro::Tags::LorentzFactor<DataType>,\n                 hydro::Tags::SpecificEnthalpy<DataType>>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags<DataType>>\n  primitive_variables(const tnsr::I<DataType, Dim>& x, double t) const {\n    return this->variables(x, t, variables_tags<DataType>{});\n  }\n};\n\ntemplate <size_t Dim, typename DataType>\nvoid test_solution(const DataType& used_for_size,\n                   const std::array<double, Dim>& mean_velocity,\n                   const std::string& mean_velocity_opt,\n                   const std::array<double, Dim>& wave_vector,\n                   const std::string& wave_vector_opt) {\n  const double pressure = 1.23;\n  const double adiabatic_index = 1.3334;\n  const double perturbation_size = 0.78;\n\n  SmoothFlowProxy<Dim> solution(mean_velocity, wave_vector, pressure,\n                                adiabatic_index, perturbation_size);\n  pypp::check_with_random_values<1>(\n      &SmoothFlowProxy<Dim>::template primitive_variables<DataType>, solution,\n      \"Hydro.SmoothFlow\",\n      {\"rest_mass_density\", \"spatial_velocity\", \"specific_internal_energy\",\n       \"pressure\", \"lorentz_factor\", \"specific_enthalpy_relativistic\"},\n      {{{-15., 15.}}},\n      std::make_tuple(mean_velocity, wave_vector, pressure, adiabatic_index,\n                      perturbation_size),\n      used_for_size);\n\n  // Test a few of the GR components to make sure that the implementation\n  // correctly forwards to the background solution. Not meant to be extensive.\n  const auto coords =\n      make_with_value<tnsr::I<DataType, Dim>>(used_for_size, 1.0);\n  const double dummy_time = 1.1;\n  const gr::Solutions::Minkowski<Dim> minkowski{};\n  CHECK_ITERABLE_APPROX(\n      get<gr::Tags::Lapse<DataType>>(minkowski.variables(\n          coords, dummy_time, tmpl::list<gr::Tags::Lapse<DataType>>{})),\n      get<gr::Tags::Lapse<DataType>>(solution.variables(\n          coords, dummy_time, tmpl::list<gr::Tags::Lapse<DataType>>{})));\n  CHECK_ITERABLE_APPROX(\n      (get<gr::Tags::Shift<DataType, Dim>>(minkowski.variables(\n          coords, dummy_time, tmpl::list<gr::Tags::Shift<DataType, Dim>>{}))),\n      (get<gr::Tags::Shift<DataType, Dim>>(solution.variables(\n          coords, dummy_time, tmpl::list<gr::Tags::Shift<DataType, Dim>>{}))));\n  CHECK_ITERABLE_APPROX(\n      (get<gr::Tags::SpatialMetric<DataType, Dim>>(minkowski.variables(\n          coords, dummy_time,\n          tmpl::list<gr::Tags::SpatialMetric<DataType, Dim>>{}))),\n      (get<gr::Tags::SpatialMetric<DataType, Dim>>(solution.variables(\n          coords, dummy_time,\n          tmpl::list<gr::Tags::SpatialMetric<DataType, Dim>>{}))));\n\n  register_classes_with_charm<RelativisticEuler::Solutions::SmoothFlow<Dim>>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          RelativisticEuler::Solutions::SmoothFlow<Dim>>(\n          \"SmoothFlow:\\n\"\n          \"  MeanVelocity: \" +\n          mean_velocity_opt +\n          \"\\n\"\n          \"  WaveVector: \" +\n          wave_vector_opt +\n          \"\\n\"\n          \"  Pressure: 1.23\\n\"\n          \"  AdiabaticIndex: 1.3334\\n\"\n          \"  PerturbationSize: 0.78\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution_from_options =\n      dynamic_cast<const RelativisticEuler::Solutions::SmoothFlow<Dim>&>(\n          *deserialized_option_solution);\n\n  CHECK(solution == solution_from_options);\n\n  RelativisticEuler::Solutions::SmoothFlow<Dim> solution_to_move(\n      mean_velocity, wave_vector, pressure, adiabatic_index, perturbation_size);\n\n  test_move_semantics(std::move(solution_to_move),\n                      solution_from_options);  //  NOLINT\n  test_serialization(solution);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.RelEuler.SmoothFlow\",\n    \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/\"};\n  test_solution<1>(std::numeric_limits<double>::signaling_NaN(), {{-0.3}},\n                   \"[-0.3]\", {{0.4}}, \"[0.4]\");\n  test_solution<1>(DataVector(5), {{-0.3}}, \"[-0.3]\", {{0.4}}, \"[0.4]\");\n  test_solution<2>(std::numeric_limits<double>::signaling_NaN(), {{-0.3, 0.1}},\n                   \"[-0.3, 0.1]\", {{0.4, -0.24}}, \"[0.4, -0.24]\");\n  test_solution<2>(DataVector(5), {{-0.3, 0.1}}, \"[-0.3, 0.1]\", {{0.4, -0.24}},\n                   \"[0.4, -0.24]\");\n  test_solution<3>(std::numeric_limits<double>::signaling_NaN(),\n                   {{-0.3, 0.1, -0.002}}, \"[-0.3, 0.1, -0.002]\",\n                   {{0.4, -0.24, 0.054}}, \"[0.4, -0.24, 0.054]\");\n  test_solution<3>(DataVector(5), {{-0.3, 0.1, -0.002}}, \"[-0.3, 0.1, -0.002]\",\n                   {{0.4, -0.24, 0.054}}, \"[0.4, -0.24, 0.054]\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Test_Tov.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Tov.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nconstexpr double polytropic_constant = 8.0;\ndouble expected_newtonian_radius() {\n  /* Analytic solution in Newtonian limit for polytropic TOV integration has\n  $R = \\pi / \\alpha$ ; $M = 4\\pi^{2}\\rho_{0c} / \\alpha^{3}$\n   Here $\\rho_{0c}$ is the central mass density and $\\alpha = sqrt(2\\pi/K)$\n   with $K$ the polytropic constant\n  */\n  return M_PI / sqrt(2.0 * M_PI / polytropic_constant);\n}\n\ndouble expected_newtonian_mass(const double central_mass_density) {\n  return 4.0 * central_mass_density * square(M_PI) /\n         (cube(sqrt(2.0 * M_PI / polytropic_constant)));\n}\n\nvoid test_tov(\n    const EquationsOfState::EquationOfState<true, 1>& equation_of_state,\n    const double central_mass_density, const size_t num_pts,\n    const size_t current_iteration, const bool newtonian_limit) {\n  CAPTURE(central_mass_density);\n  CAPTURE(num_pts);\n  CAPTURE(current_iteration);\n  CAPTURE(newtonian_limit);\n  Approx custom_approx = Approx::custom().epsilon(1.0e-08).scale(1.0);\n  const double initial_log_enthalpy =\n      std::log(get(hydro::relativistic_specific_enthalpy(\n          Scalar<double>{central_mass_density},\n          equation_of_state.specific_internal_energy_from_density(\n              Scalar<double>{central_mass_density}),\n          equation_of_state.pressure_from_density(\n              Scalar<double>{central_mass_density}))));\n  const double surface_log_enthalpy = 0.0;\n  const double step = (surface_log_enthalpy - initial_log_enthalpy) / num_pts;\n  const double final_log_enthalpy =\n      num_pts == current_iteration + 1\n          ? surface_log_enthalpy\n          : initial_log_enthalpy + (current_iteration + 1.0) * step;\n  const RelativisticEuler::Solutions::TovSolution tov_out_full(\n      equation_of_state, central_mass_density,\n      RelativisticEuler::Solutions::TovCoordinates::Schwarzschild,\n      surface_log_enthalpy);\n\n  if (newtonian_limit) {\n    const double final_radius{tov_out_full.outer_radius()};\n    const double final_mass{tov_out_full.total_mass()};\n    CHECK(final_mass ==\n          approx(tov_out_full.mass_over_radius(final_radius) * final_radius));\n    CHECK(expected_newtonian_radius() == custom_approx(final_radius));\n    CHECK(expected_newtonian_mass(central_mass_density) ==\n          custom_approx(final_mass));\n  } else {\n    //  Values in relativistic limit obtained from SpEC for\n    //  polytropic_constant = 8.0 and polytropic_exponent = 2.0\n    constexpr double expected_relativistic_radius = 3.4685521362;\n    constexpr double expected_relativistic_mass = 0.0531036941;\n    const double final_radius{tov_out_full.outer_radius()};\n    const double final_mass{tov_out_full.total_mass()};\n    CHECK(final_mass ==\n          approx(tov_out_full.mass_over_radius(final_radius) * final_radius));\n    CHECK(expected_relativistic_radius == custom_approx(final_radius));\n    CHECK(expected_relativistic_mass == custom_approx(final_mass));\n  }\n  CHECK(tov_out_full.injection_energy() ==\n        approx(sqrt(1. - 2. * tov_out_full.total_mass() /\n                             tov_out_full.outer_radius())));\n\n  // Integrate only to some intermediate value, not the surface. Then compare to\n  // interpolated values.\n  const RelativisticEuler::Solutions::TovSolution tov_out_intermediate(\n      equation_of_state, central_mass_density,\n      RelativisticEuler::Solutions::TovCoordinates::Schwarzschild,\n      final_log_enthalpy);\n  const double intermediate_radius{tov_out_intermediate.outer_radius()};\n  const double intermediate_mass{tov_out_intermediate.total_mass()};\n  CHECK(intermediate_mass ==\n        approx(tov_out_intermediate.mass_over_radius(intermediate_radius) *\n               intermediate_radius));\n  const double intermediate_log_enthalpy{\n      tov_out_intermediate.log_specific_enthalpy(intermediate_radius)};\n  const double interpolated_mass{\n      tov_out_full.mass_over_radius(intermediate_radius) * intermediate_radius};\n  const double interpolated_log_enthalpy{\n      tov_out_full.log_specific_enthalpy(intermediate_radius)};\n  CHECK(intermediate_mass == custom_approx(interpolated_mass));\n  CHECK(intermediate_log_enthalpy == custom_approx(interpolated_log_enthalpy));\n\n  const auto deserialized_tov_out_full =\n      serialize_and_deserialize(tov_out_full);\n  const auto deserialized_tov_out_intermediate =\n      serialize_and_deserialize(tov_out_intermediate);\n  const double intermediate_radius_ds{\n      deserialized_tov_out_intermediate.outer_radius()};\n  const double intermediate_mass_ds{\n      deserialized_tov_out_intermediate.total_mass()};\n  const double intermediate_log_enthalpy_ds{\n      deserialized_tov_out_intermediate.log_specific_enthalpy(\n          intermediate_radius_ds)};\n  const double interpolated_mass_ds{\n      deserialized_tov_out_full.mass_over_radius(intermediate_radius_ds) *\n      intermediate_radius_ds};\n  const double interpolated_log_enthalpy_ds{\n      deserialized_tov_out_full.log_specific_enthalpy(intermediate_radius_ds)};\n  CHECK(intermediate_mass_ds == custom_approx(interpolated_mass_ds));\n  CHECK(intermediate_log_enthalpy_ds ==\n        custom_approx(interpolated_log_enthalpy_ds));\n}\n\nvoid test_tov_dp_dr(const EquationsOfState::EquationOfState<true, 1>& eos) {\n  INFO(\"dp/dr\");\n  const double central_rest_mass_density = 1.0e-3;\n  const RelativisticEuler::Solutions::TovSolution radial_tov_solution{\n      eos, central_rest_mass_density};\n\n  // Evaluate the radial solution at equally spaced points\n  const size_t num_radial_pts = 500;\n  const double dx = radial_tov_solution.outer_radius() / num_radial_pts;\n  DataVector radii{num_radial_pts};\n  Scalar<DataVector> density{num_radial_pts};\n  Scalar<DataVector> pressure{num_radial_pts};\n  DataVector dp_dr{num_radial_pts};\n  for (size_t i = 0; i < num_radial_pts; ++i) {\n    const double radius = i * dx;\n    radii[i] = radius;\n    const double mass_over_radius =\n        radial_tov_solution.mass_over_radius(radius);\n    const double specific_enthalpy =\n        exp(radial_tov_solution.log_specific_enthalpy(radius));\n    const Scalar<double> density_i =\n        eos.rest_mass_density_from_enthalpy(Scalar<double>(specific_enthalpy));\n    get(density)[i] = get(density_i);\n    get(pressure)[i] = get(eos.pressure_from_density(density_i));\n    // This is the TOV equation in its standard form\n    if (radius > 0.) {\n      const double specific_internal_energy =\n          get(eos.specific_internal_energy_from_density(density_i));\n      dp_dr[i] =\n          -(get(density)[i] * (1. + specific_internal_energy) +\n            get(pressure)[i]) *\n          (mass_over_radius / radius + 4. * M_PI * radius * get(pressure)[i]) /\n          (1. - 2 * mass_over_radius);\n    } else {\n      dp_dr[i] = 0.;\n      // Check the central density\n      CHECK(get(density)[i] == approx(central_rest_mass_density));\n    }\n  }\n\n  // Take a finite-difference numerical derivative of the pressure from the\n  // radial solution, and compare to the TOV equation dp/dr\n  auto custom_approx = Approx::custom().epsilon(2.e-5);\n  for (size_t i = 2; i < num_radial_pts - 2; ++i) {\n    CAPTURE(i);\n    CAPTURE(radii[i]);\n    CAPTURE(radial_tov_solution.outer_radius());\n    CAPTURE(get(pressure)[i]);\n    CAPTURE(get(density)[i]);\n    if (radii[i + 2] < radial_tov_solution.outer_radius()) {\n      CHECK((-get(pressure)[i + 2] / 12.0 + 2.0 / 3.0 * get(pressure)[i + 1] -\n             2.0 / 3.0 * get(pressure)[i - 1] + get(pressure)[i - 2] / 12.0) /\n                dx ==\n            custom_approx(dp_dr[i]));\n    } else if (radii[i] >= radial_tov_solution.outer_radius()) {\n      CHECK(dp_dr[i] == 0.0);\n    }\n  }\n}\n\nvoid test_baumgarte_shapiro() {\n  INFO(\"BaumgarteShapiro\");\n  // Reproduces Fig. 1.2 in BaumgarteShapiro, as suggested in footnote 25 on p.\n  // 18, and as listed in Table 14.1\n  // We use the polytropic scaling relations to prevent\n  // nonsensical behavior inside of the NS\n  const double K = 100.0;\n  const double Gamma = 2.0;\n  const EquationsOfState::PolytropicFluid<true> eos{K, Gamma};\n  const double central_energy_density = 0.42 / K;\n  const double K_over_Gamma_minus_1 = K / (Gamma - 1);\n  const double central_rest_mass_density =\n      (sqrt(1.0 + 4.0 * K_over_Gamma_minus_1 * central_energy_density) - 1.) /\n      (2 * K_over_Gamma_minus_1);\n  const RelativisticEuler::Solutions::TovSolution tov{\n      eos, central_rest_mass_density};\n  // The values in BaumgarteShapiro are given to this precision\n  // rescale macroscopic quantites to new units\n  const double target_total_mass = 0.164 * pow(K, 0.5);\n  const double target_outer_radius = 0.763 * pow(K, 0.5);\n  Approx custom_approx = Approx::custom().epsilon(1.e-03).scale(pow(K, 0.5));\n  CHECK(tov.total_mass() == custom_approx(target_total_mass));\n  CHECK(tov.outer_radius() == custom_approx(target_outer_radius));\n}\n\nvoid test_tov_isotropic(const EquationsOfState::EquationOfState<true, 1>& eos,\n                        const double central_mass_density) {\n  INFO(\"Isotropic\");\n  const RelativisticEuler::Solutions::TovSolution tov_areal{\n      eos, central_mass_density,\n      RelativisticEuler::Solutions::TovCoordinates::Schwarzschild};\n  const RelativisticEuler::Solutions::TovSolution tov_isotropic{\n      eos, central_mass_density,\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic};\n  const double outer_isotropic_radius = tov_isotropic.outer_radius();\n  CHECK(tov_areal.total_mass() == approx(tov_isotropic.total_mass()));\n  const double outer_areal_radius =\n      outer_isotropic_radius *\n      square(tov_isotropic.conformal_factor(outer_isotropic_radius));\n  CHECK(tov_areal.outer_radius() == approx(outer_areal_radius));\n  CHECK(tov_isotropic.mass_over_radius(0.) == approx(0.));\n  CHECK(tov_isotropic.mass_over_radius(outer_isotropic_radius) ==\n        approx(tov_isotropic.total_mass() / outer_areal_radius));\n  CHECK(\n      tov_isotropic.log_specific_enthalpy(0.) ==\n      approx(log(get(hydro::relativistic_specific_enthalpy(\n          Scalar<double>{central_mass_density},\n          eos.specific_internal_energy_from_density(\n              Scalar<double>{central_mass_density}),\n          eos.pressure_from_density(Scalar<double>{central_mass_density}))))));\n  CHECK(tov_isotropic.log_specific_enthalpy(outer_isotropic_radius) ==\n        approx(0.));\n  CHECK(tov_isotropic.conformal_factor(outer_isotropic_radius) ==\n        approx(1 + 0.5 * tov_isotropic.total_mass() / outer_isotropic_radius));\n}\n\nvoid test_rueter() {\n  INFO(\"Rueter\");\n  // Reproduces the values in Sec. V.C and Fig. 8 in\n  // https://arxiv.org/abs/1708.07358\n  const EquationsOfState::PolytropicFluid<true> eos{123.6489, 2};\n  RelativisticEuler::Solutions::TovSolution solution{\n      eos,\n      0.0008087415253997405,  // Central enthalpy h=1.2\n      RelativisticEuler::Solutions::TovCoordinates::Isotropic};\n  const double outer_radius = solution.outer_radius();\n  // The values in the paper are given to this precision\n  Approx custom_approx = Approx::custom().epsilon(1e-4).scale(1.);\n  CHECK(outer_radius == custom_approx(9.7098));\n  CHECK(solution.conformal_factor(0.) == custom_approx(1.16));\n  CHECK(solution.conformal_factor(outer_radius) == custom_approx(1.0727));\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Gr.Tov\",\n                  \"[Unit][PointwiseFunctions]\") {\n  std::unique_ptr<EquationsOfState::EquationOfState<true, 1>>\n      equation_of_state =\n          std::make_unique<EquationsOfState::PolytropicFluid<true>>(\n              polytropic_constant, 2.0);\n  /* Each iteration of the loop is for a different value of the final\n     log_enthalpy in the integration. This is done to test the interpolation:\n     the integration is stopped at some final log_enthalpy between the center\n     and surface, and values are compared to those obtained by interpolation of\n     the full integration up to the surface.\n  */\n  const size_t num_pts = 25;\n  for (size_t i = 0; i < num_pts; i++) {\n    test_tov(*equation_of_state, 1.0e-10, num_pts, i, true);\n    test_tov(*equation_of_state, 1.0e-03, num_pts, i, false);\n  }\n\n  test_tov_dp_dr(*equation_of_state);\n  test_baumgarte_shapiro();\n  test_tov_isotropic(*equation_of_state, 1.e-3);\n  test_rueter();\n}\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Test_TovStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Creators/Rectilinear.hpp\"\n#include \"Domain/Domain.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/GrMhd/VerifyGrMhdSolution.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/TovStar.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace RelativisticEuler::Solutions {\nnamespace {\n\nusing TovCoordinates = RelativisticEuler::Solutions::TovCoordinates;\n\nvoid verify_solution(const TovStar& solution, const std::array<double, 3>& x) {\n  const std::array<double, 3> dx{{1.e-4, 1.e-4, 1.e-4}};\n  domain::creators::Brick brick(x - dx, x + dx, {{0, 0, 0}}, {{5, 5, 5}},\n                                {{false, false, false}});\n  Mesh<3> mesh{brick.initial_extents()[0], Spectral::Basis::Legendre,\n               Spectral::Quadrature::GaussLobatto};\n  const auto domain = brick.create_domain();\n  verify_grmhd_solution(solution, domain.blocks()[0], mesh, 1.e-7, 1.234,\n                        1.e-4);\n\n  // Check that analytically computed dp/dx^i actually equals numerical\n  // derivative of pressure\n  using DerivPressure = ::Tags::deriv<hydro::Tags::Pressure<double>,\n                                      tmpl::size_t<3>, Frame::Inertial>;\n  const auto dpdx_from_solution = get<DerivPressure>(solution.variables(\n      tnsr::I<double, 3>(x), 0.0, tmpl::list<DerivPressure>{}));\n\n  for (size_t d = 0; d < 3; ++d) {\n    using Pressure = hydro::Tags::Pressure<double>;\n\n    const auto dpdx_numerical =\n        numerical_derivative(\n            [&solution](const std::array<double, 3>& local_x) {\n              return std::array<double, 1>{get(get<Pressure>(solution.variables(\n                  tnsr::I<double, 3>(local_x), 0., tmpl::list<Pressure>{})))};\n            },\n            x, d, 1e-4)\n            .at(0);\n\n    CHECK(dpdx_numerical - dpdx_from_solution.get(d) ==\n          Approx::custom().epsilon(1.0e-10).scale(1.0));\n  }\n}\n\nvoid test_tov_star(const TovCoordinates coord_system) {\n  register_classes_with_charm<RelativisticEuler::Solutions::TovStar>();\n  register_classes_with_charm<EquationsOfState::PolytropicFluid<true>>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          RelativisticEuler::Solutions::TovStar>(\n          \"TovStar:\\n\"\n          \"  CentralDensity: 1.0e-3\\n\"\n          \"  EquationOfState:\\n\"\n          \"    PolytropicFluid:\\n\"\n          \"      PolytropicConstant: 100.0\\n\"\n          \"      PolytropicExponent: 2.0\\n\"\n          \"  Coordinates: \" +\n          get_output(coord_system))\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& solution =\n      dynamic_cast<const RelativisticEuler::Solutions::TovStar&>(\n          *deserialized_option_solution);\n  {\n    INFO(\"Semantics\");\n    CHECK(solution ==\n          TovStar{1.0e-3,\n                  std::make_unique<EquationsOfState::PolytropicFluid<true>>(\n                      100.0, 2.0),\n                  coord_system});\n    // Add support for arbitrary EOSes would require custom copy\n    // semantics that we haven't needed yet.\n    test_copy_semantics(solution);\n    TovStar star_copy = solution;\n    test_move_semantics(std::move(star_copy), solution);\n    test_serialization(solution);\n  }\n  if (coord_system == TovCoordinates::Schwarzschild) {\n    INFO(\"Properties\");\n    Approx custom_approx = Approx::custom().epsilon(1.0e-08).scale(1.0);\n    CHECK(solution.radial_solution().outer_radius() ==\n          custom_approx(10.0473500683));\n    // Check a second solution\n    TovStar second_solution{\n        1.0e-3,\n        std::make_unique<EquationsOfState::PolytropicFluid<true>>(8.0, 2.0)};\n    CHECK(second_solution.radial_solution().outer_radius() ==\n          custom_approx(3.4685521362));\n  }\n\n  // Check the solution numerically solves the GRMHD equations\n  const double radius_of_star = solution.radial_solution().outer_radius();\n  verify_solution(solution, {{0.0, 0.0, 0.0}});\n  verify_solution(solution, {{0.0, 0.0, 0.5 * radius_of_star}});\n  verify_solution(solution, {{0.0, radius_of_star, 0.0}});\n  verify_solution(solution, {{1.5 * radius_of_star, 0.0, 0.0}});\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1.1 * radius_of_star,\n                                            1.1 * radius_of_star);\n  std::array<double, 3> random_point{};\n  for (size_t i = 0; i < 3; i++) {\n    gsl::at(random_point, i) = real_dis(gen);\n  }\n  verify_solution(solution, random_point);\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.RelEuler.Tov\",\n                  \"[Unit][PointwiseFunctions]\") {\n  test_tov_star(TovCoordinates::Schwarzschild);\n  test_tov_star(TovCoordinates::Isotropic);\n}\n\n}  // namespace\n}  // namespace RelativisticEuler::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/RelativisticEuler/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ScalarAdvectionSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_Krivodonova.cpp\n  Test_Kuzmin.cpp\n  Test_Sinusoid.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ScalarAdvectionSolutions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Krivodonova.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef func_F(x, alpha, a):\n    return np.sqrt(max(1.0 - alpha**2.0 * (x - a) ** 2.0, 0.0))\n\n\ndef func_G(x, beta, z):\n    return np.exp(-beta * (x - z) ** 2.0)\n\n\ndef u_initial(x_grid):\n    a = 0.5\n    z = -0.7\n    delta = 0.005\n    alpha = 10.0\n    beta = np.log(2.0) / (36.0 * delta**2)\n\n    x0 = x_grid[0]\n    if (-0.8 <= x0) and (x0 <= -0.6):\n        u = (\n            func_G(x0, beta, z - delta)\n            + func_G(x0, beta, z + delta)\n            + 4.0 * func_G(x0, beta, z)\n        ) / 6.0\n    elif (-0.4 <= x0) and (x0 <= -0.2):\n        u = 1.0\n    elif (0.0 <= x0) and (x0 <= 0.2):\n        u = 1.0 - np.abs(10.0 * x0 - 1.0)\n    elif (0.4 <= x0) and (x0 <= 0.6):\n        u = (\n            func_F(x0, alpha, a - delta)\n            + func_F(x0, alpha, a + delta)\n            + 4.0 * func_F(x0, alpha, a)\n        ) / 6.0\n    else:\n        u = 0.0\n\n    return u\n\n\ndef u(x, t):\n    xt = x - t\n    x0 = xt - 2.0 * np.floor((xt + 1.0) * 0.5)\n    return u_initial(x0)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Kuzmin.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\nr0 = 0.15\n\n\ndef r_xy(x, x0, y, y0):\n    return np.sqrt((x - x0) ** 2.0 + (y - y0) ** 2.0) / r0\n\n\ndef u_initial(x_grid):\n    x, y = x_grid\n\n    r_cylinder = r_xy(x, 0.5, y, 0.75)\n    r_cone = r_xy(x, 0.5, y, 0.25)\n    r_hump = r_xy(x, 0.25, y, 0.5)\n\n    if r_cylinder <= 1.0:\n        if (np.abs(x - 0.5) >= 0.025) or (y >= 0.85):\n            u = 1.0\n        else:\n            u = 0.0\n    elif r_cone <= 1.0:\n        u = 1.0 - r_cone\n    elif r_hump <= 1.0:\n        u = 0.25 * (1.0 + np.cos(np.pi * r_hump))\n    else:\n        u = 0.0\n\n    return u\n\n\ndef u(x, t):\n    x0 = (x[0] - 0.5) * np.cos(t) + (x[1] - 0.5) * np.sin(t) + 0.5\n    y0 = -(x[0] - 0.5) * np.sin(t) + (x[1] - 0.5) * np.cos(t) + 0.5\n    return u_initial([x0, y0])\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Sinusoid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef u(x, t):\n    return np.sin(np.pi * (x[0] - t))\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Test_Krivodonova.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Krivodonova.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing Krivodonova = ScalarAdvection::Solutions::Krivodonova;\n\nvoid test_create() {\n  const auto sol = TestHelpers::test_creation<Krivodonova>(\"\");\n  CHECK(sol == Krivodonova());\n}\n\nvoid test_serialize() {\n  Krivodonova sol;\n  test_serialization(sol);\n}\n\nvoid test_move() {\n  Krivodonova sol;\n  Krivodonova sol_copy;\n  test_move_semantics(std::move(sol), sol_copy);  //  NOLINT\n}\n\nvoid test_derived() {\n  register_classes_with_charm<Krivodonova>();\n  const std::unique_ptr<InitialData> initial_data_ptr =\n      std::make_unique<Krivodonova>();\n  const std::unique_ptr<InitialData> deserialized_initial_data_ptr =\n      serialize_and_deserialize(initial_data_ptr)->get_clone();\n  CHECK(dynamic_cast<Krivodonova*>(deserialized_initial_data_ptr.get()) !=\n        nullptr);\n}\n\nstruct KrivodonovaProxy : Krivodonova {\n  using Krivodonova::Krivodonova;\n  using variables_tags = tmpl::list<ScalarAdvection::Tags::U>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags> retrieve_variables(\n      const tnsr::I<DataType, 1>& x, double t) const {\n    return this->variables(x, t, variables_tags{});\n  }\n};\n\nvoid verify_solution(const gsl::not_null<std::mt19937*> generator,\n                     const size_t number_of_pts_per_subinterval) {\n  // DataVectors to store values\n  Scalar<DataVector> u_sol{9 * number_of_pts_per_subinterval};\n  Scalar<DataVector> u_test{9 * number_of_pts_per_subinterval};\n\n  // generate random 1D grid points within each of 9 subintervals.\n  // - u = 0        : [-1.0, -0.8], [-0.6, -0.4], [-0.2, 0.0], [0.2, 0.4],\n  //                  [0.6, 1.0]\n  // - Gaussian     : [-0.8, -0.6]\n  // - square pulse : [-0.4, -0.2]\n  // - triangle     : [0.0, 0.2]\n  // - half-ellipse : [0.4, 0.6]\n  // Rather than generating random points for [-1.0, 1.0] without any rules,\n  // patching the random grid points of these subintervals together allows for\n  // this test to always 'hit' every on one of the shapes on the initial data,\n  // which also helps for code coverage checks.\n  std::uniform_real_distribution<> distribution_coords(-1.0, 1.0);\n  auto x = make_with_random_values<tnsr::I<DataVector, 1>>(\n      generator, make_not_null(&distribution_coords),\n      9 * number_of_pts_per_subinterval);\n\n  for (size_t i = 0; i < 9; ++i) {\n    const auto x_sub = make_with_random_values<Scalar<DataVector>>(\n        generator, make_not_null(&distribution_coords),\n        number_of_pts_per_subinterval);\n    if (i < 8) {\n      // first 8 subintervals\n      for (size_t j = 0; j < number_of_pts_per_subinterval; ++j) {\n        get<0>(x)[i * number_of_pts_per_subinterval + j] =\n            0.1 * get(x_sub)[j] - 0.9 + 0.2 * i;\n      }\n    } else {\n      // handling the last subinterval [0.6, 1.0]\n      for (size_t j = 0; j < number_of_pts_per_subinterval; ++j) {\n        get<0>(x)[i * number_of_pts_per_subinterval + j] =\n            0.2 * get(x_sub)[j] + 0.8;\n      }\n    }\n  }\n\n  // generate random time and shift coordinates\n  std::uniform_real_distribution<> distribution_time(0.0, 10.0);\n  double t{make_with_random_values<double>(generator,\n                                           make_not_null(&distribution_time))};\n  get<0>(x) = get<0>(x) + t;\n\n  // test the solution\n  KrivodonovaProxy sol;\n  u_sol = get<ScalarAdvection::Tags::U>(sol.retrieve_variables(x, t));\n  u_test = pypp::call<Scalar<DataVector>>(\"Krivodonova\", \"u\", x, t);\n  CHECK_ITERABLE_APPROX(u_sol, u_test);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.ScalarAdvection.Krivodonova\",\n    \"[Unit][PointwiseFunctions]\") {\n  test_create();\n  test_serialize();\n  test_move();\n  test_derived();\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection\"};\n  MAKE_GENERATOR(gen);\n  verify_solution(make_not_null(&gen), 5);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Test_Kuzmin.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Kuzmin.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing Kuzmin = ScalarAdvection::Solutions::Kuzmin;\n\nvoid test_create() {\n  const auto sol = TestHelpers::test_creation<Kuzmin>(\"\");\n  CHECK(sol == Kuzmin());\n}\n\nvoid test_serialize() {\n  Kuzmin sol;\n  test_serialization(sol);\n}\n\nvoid test_move() {\n  Kuzmin sol;\n  Kuzmin sol_copy;\n  test_move_semantics(std::move(sol), sol_copy);  //  NOLINT\n}\n\nvoid test_derived() {\n  register_classes_with_charm<Kuzmin>();\n  const std::unique_ptr<InitialData> initial_data_ptr =\n      std::make_unique<Kuzmin>();\n  const std::unique_ptr<InitialData> deserialized_initial_data_ptr =\n      serialize_and_deserialize(initial_data_ptr)->get_clone();\n  CHECK(dynamic_cast<Kuzmin*>(deserialized_initial_data_ptr.get()) != nullptr);\n}\n\nstruct KuzminProxy : Kuzmin {\n  using Kuzmin::Kuzmin;\n  using variables_tags = tmpl::list<ScalarAdvection::Tags::U>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags> retrieve_variables(\n      const tnsr::I<DataType, 2>& x, double t) const {\n    return this->variables(x, t, variables_tags{});\n  }\n};\n\nvoid verify_solution(const gsl::not_null<std::mt19937*> generator,\n                     const size_t number_of_pts_per_region) {\n  // we need to distribute random points for 5 different regions (see the for\n  // loop below)\n  const size_t num_pts = 5 * number_of_pts_per_region;\n\n  // DataVectors to store values\n  Scalar<DataVector> u_sol{num_pts};\n  Scalar<DataVector> u_test{num_pts};\n  auto coords_init = make_with_value<tnsr::I<DataVector, 2>>(\n      5 * number_of_pts_per_region, 0.0);\n  auto& x0 = get<0>(coords_init);\n  auto& y0 = get<1>(coords_init);\n\n  // random displacements within a circle of radius 0.15\n  std::uniform_real_distribution<> distribution_radius(0.0, 0.15);\n  std::uniform_real_distribution<> distribution_angle(0.0, 2 * M_PI);\n  const auto radius = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&distribution_radius), number_of_pts_per_region);\n  const auto angle = make_with_random_values<Scalar<DataVector>>(\n      generator, make_not_null(&distribution_angle), number_of_pts_per_region);\n  auto x_circle =\n      make_with_value<tnsr::I<DataVector, 2>>(number_of_pts_per_region, 0.0);\n  auto& xc = get<0>(x_circle);\n  auto& yc = get<1>(x_circle);\n  xc = get(radius) * cos(get(angle));\n  yc = get(radius) * sin(get(angle));\n\n  // random displacements within a square [0, 1] x [0, 1]\n  std::uniform_real_distribution<> distribution_xy(0.0, 1.0);\n  const auto x_square = make_with_random_values<tnsr::I<DataVector, 2>>(\n      generator, make_not_null(&distribution_xy), number_of_pts_per_region);\n\n  for (size_t i = 0; i < number_of_pts_per_region; ++i) {\n    // a cylinder centered at (0.5, 0.75)\n    x0[i] = xc[i] + 0.5;\n    y0[i] = yc[i] + 0.75;\n    // and the slot inside the cylinder\n    x0[number_of_pts_per_region + i] = 0.05 * get<0>(x_square)[i] + 0.475;\n    y0[number_of_pts_per_region + i] = 0.3 * get<1>(x_square)[i] + 0.6;\n    // cone centered at (0.5, 0.25)\n    x0[2 * number_of_pts_per_region + i] = xc[i] + 0.5;\n    y0[2 * number_of_pts_per_region + i] = yc[i] + 0.25;\n    // hump centered at (0.25, 0.5)\n    x0[3 * number_of_pts_per_region + i] = xc[i] + 0.25;\n    y0[3 * number_of_pts_per_region + i] = yc[i] + 0.5;\n    // other regions with u=0; some of pts may overlap with previous ones\n    x0[4 * number_of_pts_per_region + i] = get<0>(x_square)[i];\n    y0[4 * number_of_pts_per_region + i] = get<1>(x_square)[i];\n  }\n\n  // generate random time and shift coordinates\n  std::uniform_real_distribution<> distribution_time(0.0, 10.0);\n  double t{make_with_random_values<double>(generator,\n                                           make_not_null(&distribution_time))};\n  auto x = make_with_value<tnsr::I<DataVector, 2>>(num_pts, 0.0);\n  get<0>(x) = (x0 - 0.5) * cos(t) - (y0 - 0.5) * sin(t) + 0.5;\n  get<1>(x) = (x0 - 0.5) * sin(t) + (y0 - 0.5) * cos(t) + 0.5;\n\n  // test the solution\n  KuzminProxy sol;\n  u_sol = get<ScalarAdvection::Tags::U>(sol.retrieve_variables(x, t));\n  u_test = pypp::call<Scalar<DataVector>>(\"Kuzmin\", \"u\", x, t);\n  CHECK_ITERABLE_APPROX(u_sol, u_test);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.ScalarAdvection.Kuzmin\",\n    \"[Unit][PointwiseFunctions]\") {\n  test_create();\n  test_serialize();\n  test_move();\n  test_derived();\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection\"};\n  MAKE_GENERATOR(gen);\n  verify_solution(make_not_null(&gen), 10);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Test_Sinusoid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarAdvection/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection/Sinusoid.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n\nusing InitialData = evolution::initial_data::InitialData;\nusing Sinusoid = ScalarAdvection::Solutions::Sinusoid;\n\nvoid test_create() {\n  const auto sine_wave = TestHelpers::test_creation<Sinusoid>(\"\");\n  CHECK(sine_wave == Sinusoid());\n}\n\nvoid test_serialize() {\n  Sinusoid sine_wave;\n  test_serialization(sine_wave);\n}\n\nvoid test_move() {\n  Sinusoid sine_wave;\n  Sinusoid sine_wave_copy;\n  test_move_semantics(std::move(sine_wave), sine_wave_copy);  //  NOLINT\n}\n\nvoid test_derived() {\n  register_classes_with_charm<Sinusoid>();\n  const std::unique_ptr<InitialData> initial_data_ptr =\n      std::make_unique<Sinusoid>();\n  const std::unique_ptr<InitialData> deserialized_initial_data_ptr =\n      serialize_and_deserialize(initial_data_ptr)->get_clone();\n  CHECK(dynamic_cast<Sinusoid*>(deserialized_initial_data_ptr.get()) !=\n        nullptr);\n}\n\nstruct SinusoidProxy : Sinusoid {\n  using Sinusoid::Sinusoid;\n  using variables_tags = tmpl::list<ScalarAdvection::Tags::U>;\n\n  template <typename DataType>\n  tuples::tagged_tuple_from_typelist<variables_tags> retrieve_variables(\n      const tnsr::I<DataType, 1>& x, double t) const {\n    return this->variables(x, t, variables_tags{});\n  }\n};\n\nvoid verify_solution(const gsl::not_null<std::mt19937*> generator,\n                     const size_t number_of_pts) {\n  // DataVectors to store values\n  Scalar<DataVector> u_sol{number_of_pts};\n  Scalar<DataVector> u_test{number_of_pts};\n\n  // generate random 1D grid points\n  std::uniform_real_distribution<> distribution_coords(-1.0, 1.0);\n  const auto x = make_with_random_values<tnsr::I<DataVector, 1>>(\n      generator, make_not_null(&distribution_coords), u_sol);\n\n  // test for random time\n  std::uniform_real_distribution<> distribution_time(0.0, 10.0);\n  double t{make_with_random_values<double>(generator,\n                                           make_not_null(&distribution_time))};\n  SinusoidProxy sine_wave;\n  u_sol = get<ScalarAdvection::Tags::U>(sine_wave.retrieve_variables(x, t));\n  u_test = pypp::call<Scalar<DataVector>>(\"Sinusoid\", \"u\", x, t);\n  CHECK_ITERABLE_APPROX(u_sol, u_test);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.ScalarAdvection.Sinusoid\",\n    \"[Unit][PointwiseFunctions]\") {\n  test_create();\n  test_serialize();\n  test_move();\n  test_derived();\n\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/ScalarAdvection\"};\n  MAKE_GENERATOR(gen);\n  verify_solution(make_not_null(&gen), 10);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/ScalarAdvection/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Test_AnalyticSolution.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n\nnamespace {\nstruct Solution : public MarkAsAnalyticSolution {};\nstruct SolutionDependentAnalyticData : public MarkAsAnalyticData,\n                                       private Solution {};\n\nstatic_assert(is_analytic_solution_v<Solution>,\n              \"Failed testing is_analytic_solution_v\");\nstatic_assert(is_analytic_solution<Solution>::value,\n              \"Failed testing is_analytic_solution\");\nstatic_assert(not is_analytic_solution_v<SolutionDependentAnalyticData>,\n              \"Failed testing is_solution_data_v\");\nstatic_assert(not is_analytic_solution<SolutionDependentAnalyticData>::value,\n              \"Failed testing is_solution_data\");\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticData/AnalyticData.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/AnalyticSolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Tags.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct DummyType {};\nstruct DummyTag : db::SimpleTag {\n  using type = DummyType;\n};\n\nstruct FieldTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.AnalyticSolutions.Tags\", \"[Unit][PointwiseFunctions]\") {\n  // [analytic_name]\n  TestHelpers::db::test_prefix_tag<Tags::Analytic<DummyTag>>(\n      \"Analytic(DummyTag)\");\n  // [analytic_name]\n  TestHelpers::db::test_prefix_tag<Tags::Error<DummyTag>>(\"Error(DummyTag)\");\n  TestHelpers::db::test_simple_tag<\n      Tags::AnalyticSolutions<tmpl::list<DummyTag>>>(\"AnalyticSolutions\");\n  TestHelpers::db::test_compute_tag<Tags::ErrorsCompute<tmpl::list<FieldTag>>>(\n      \"Errors\");\n\n  tnsr::I<DataVector, 1, Frame::Inertial> inertial_coords{{{{1., 2., 3., 4.}}}};\n  const double current_time = 2.;\n  const Variables<tmpl::list<FieldTag>> vars{4, 3.};\n  using solutions_tag = ::Tags::AnalyticSolutions<tmpl::list<FieldTag>>;\n  {\n    INFO(\"Test analytic solution\");\n    auto solution =\n        std::make_optional(typename solutions_tag::type::value_type{4});\n    get<Tags::detail::AnalyticImpl<FieldTag>>(solution.value()) =\n        Scalar<DataVector>{current_time * get<0>(inertial_coords)};\n\n    auto box = db::create<\n        db::AddSimpleTags<domain::Tags::Coordinates<1, Frame::Inertial>,\n                          Tags::Time, ::Tags::Variables<tmpl::list<FieldTag>>,\n                          solutions_tag>,\n        db::AddComputeTags<Tags::ErrorsCompute<tmpl::list<FieldTag>>>>(\n        inertial_coords, current_time, vars, solution);\n    const DataVector expected{2., 4., 6., 8.};\n    const DataVector expected_error{1., -1., -3., -5.};\n    CHECK_ITERABLE_APPROX(get(get<Tags::Analytic<FieldTag>>(box).value()),\n                          expected);\n    CHECK_ITERABLE_APPROX(get(get<Tags::Error<FieldTag>>(box).value()),\n                          expected_error);\n    db::mutate<::Tags::Variables<tmpl::list<FieldTag>>>(\n        [](const auto vars_ptr) {\n          *vars_ptr = Variables<tmpl::list<FieldTag>>{4, 4.};\n        },\n        make_not_null(&box));\n    const DataVector new_expected_error{2., 0., -2., -4.};\n    CHECK_ITERABLE_APPROX(get(get<Tags::Analytic<FieldTag>>(box).value()),\n                          expected);\n    CHECK_ITERABLE_APPROX(get(get<Tags::Error<FieldTag>>(box).value()),\n                          new_expected_error);\n  }\n  {\n    INFO(\"Test analytic data\");\n    const auto box = db::create<\n        db::AddSimpleTags<domain::Tags::Coordinates<1, Frame::Inertial>,\n                          Tags::Time, ::Tags::Variables<tmpl::list<FieldTag>>,\n                          solutions_tag>,\n        db::AddComputeTags<Tags::ErrorsCompute<tmpl::list<FieldTag>>>>(\n        inertial_coords, current_time, vars,\n        typename solutions_tag::type{std::nullopt});\n    const DataVector expected{2., 4., 6., 8.};\n    CHECK_FALSE(get<Tags::Analytic<FieldTag>>(box).has_value());\n    CHECK_FALSE(get<Tags::Error<FieldTag>>(box).has_value());\n  }\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/WaveEquation/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\nset(LIBRARY \"Test_WaveEquation\")\n\n# Nils Deppe: The SemidiscretizedDg test is commented out because switching\n# it over to the ComputeTimeDerivate and ApplyBoundaryCorrections actions\n# is too time consuming at the moment (Mar. 25, 2021). We should do one of\n# the following:\n# - switch it over later\n# - switch to a pypp test that we use as a regression test\nset(LIBRARY_SOURCES\n  Test_PlaneWave.cpp\n  Test_RegularSphericalWave.cpp\n  # Test_SemidiscretizedDg.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  WaveEquationSolutions\n  MathFunctions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/WaveEquation/Test_PlaneWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/PlaneWave.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim>\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   tmpl::list<MathFunctions::PowX<1, Frame::Inertial>>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   tmpl::list<ScalarWave::Solutions::PlaneWave<Dim>>>>;\n  };\n};\n\ninline tnsr::I<double, 1, Frame::Inertial> extract_point_from_coords(\n    const size_t offset, const tnsr::I<DataVector, 1>& x) {\n  return tnsr::I<double, 1, Frame::Inertial>{\n      std::array<double, 1>{{x.get(0)[offset]}}};\n}\n\ninline tnsr::I<double, 2, Frame::Inertial> extract_point_from_coords(\n    const size_t offset, const tnsr::I<DataVector, 2>& x) {\n  return tnsr::I<double, 2, Frame::Inertial>{\n      std::array<double, 2>{{x.get(0)[offset], x.get(1)[offset]}}};\n}\n\ninline tnsr::I<double, 3, Frame::Inertial> extract_point_from_coords(\n    const size_t offset, const tnsr::I<DataVector, 3>& x) {\n  return tnsr::I<double, 3, Frame::Inertial>{std::array<double, 3>{\n      {x.get(0)[offset], x.get(1)[offset], x.get(2)[offset]}}};\n}\n\ntemplate <size_t Dim, size_t DimSolution>\nvoid check_solution(\n    const DataVector& expected_psi, const DataVector& expected_dpsi_dt,\n    const DataVector& expected_d2psi_dt2, const DataVector& expected_dpsi_dlast,\n    const std::array<DataVector, Dim + 1>& expected_second_derivs,\n    const ScalarWave::Solutions::PlaneWave<DimSolution>& pw,\n    const tnsr::I<DataVector, DimSolution>& x, const double t) {\n  CHECK_FALSE(pw != pw);\n  test_copy_semantics(pw);\n  auto pw_for_move = pw;\n  test_move_semantics(std::move(pw_for_move), pw);\n  // expected_second_derivs is:\n  // 0 -> d^2 psi / dt dx^(Dim-1)\n  // 1-3 -> d^2 psi / dx^(Dim - 1) dx^i where i is in [0, 2]\n  CHECK_ITERABLE_APPROX(expected_psi, pw.psi(x, t).get());\n  CHECK_ITERABLE_APPROX(expected_dpsi_dt, pw.dpsi_dt(x, t).get());\n  CHECK_ITERABLE_APPROX(expected_d2psi_dt2, pw.d2psi_dt2(x, t).get());\n  CHECK_ITERABLE_APPROX(expected_dpsi_dlast, pw.dpsi_dx(x, t).get(Dim - 1));\n  CHECK_ITERABLE_APPROX(expected_second_derivs[0],\n                        pw.d2psi_dtdx(x, t).get(Dim - 1));\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK_ITERABLE_APPROX(gsl::at(expected_second_derivs, i + 1),\n                          pw.d2psi_dxdx(x, t).get(i, Dim - 1));\n    if (i < Dim - 1) {\n      CHECK_ITERABLE_APPROX(gsl::at(expected_second_derivs, i + 1),\n                            pw.d2psi_dxdx(x, t).get(Dim - 1, i));\n    }\n  }\n\n  for (size_t s = 0; s < x.get(0).size(); ++s) {\n    const auto p = extract_point_from_coords(s, x);\n    CHECK_ITERABLE_APPROX(expected_psi[s], pw.psi(p, t).get());\n    CHECK_ITERABLE_APPROX(expected_dpsi_dt[s], pw.dpsi_dt(p, t).get());\n    CHECK_ITERABLE_APPROX(expected_d2psi_dt2[s], pw.d2psi_dt2(p, t).get());\n    CHECK_ITERABLE_APPROX(expected_dpsi_dlast[s],\n                          pw.dpsi_dx(p, t).get(Dim - 1));\n    CHECK_ITERABLE_APPROX(expected_second_derivs[0][s],\n                          pw.d2psi_dtdx(p, t).get(Dim - 1));\n    for (size_t i = 0; i < Dim; ++i) {\n      CHECK_ITERABLE_APPROX(gsl::at(expected_second_derivs, i + 1)[s],\n                            pw.d2psi_dxdx(p, t).get(i, Dim - 1));\n      if (i < Dim - 1) {\n        CHECK_ITERABLE_APPROX(gsl::at(expected_second_derivs, i + 1)[s],\n                              pw.d2psi_dxdx(p, t).get(Dim - 1, i));\n      }\n    }\n  }\n\n  CHECK_ITERABLE_APPROX(\n      get<ScalarWave::Tags::Psi>(\n          pw.variables(x, t,\n                       tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                  ScalarWave::Tags::Phi<DimSolution>>{})),\n      pw.psi(x, t));\n  CHECK_ITERABLE_APPROX(\n      get<ScalarWave::Tags::Phi<DimSolution>>(\n          pw.variables(x, t,\n                       tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                  ScalarWave::Tags::Phi<DimSolution>>{})),\n      pw.dpsi_dx(x, t));\n  CHECK_ITERABLE_APPROX(\n      get<ScalarWave::Tags::Pi>(\n          pw.variables(x, t,\n                       tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                  ScalarWave::Tags::Phi<DimSolution>>{})),\n      Scalar<DataVector>(-1.0 * pw.dpsi_dt(x, t).get()));\n\n  CHECK_ITERABLE_APPROX(\n      get<Tags::dt<ScalarWave::Tags::Psi>>(pw.variables(\n          x, t,\n          tmpl::list<Tags::dt<ScalarWave::Tags::Psi>,\n                     Tags::dt<ScalarWave::Tags::Pi>,\n                     Tags::dt<ScalarWave::Tags::Phi<DimSolution>>>{})),\n      pw.dpsi_dt(x, t));\n  CHECK_ITERABLE_APPROX(\n      get<Tags::dt<ScalarWave::Tags::Phi<DimSolution>>>(pw.variables(\n          x, t,\n          tmpl::list<Tags::dt<ScalarWave::Tags::Psi>,\n                     Tags::dt<ScalarWave::Tags::Pi>,\n                     Tags::dt<ScalarWave::Tags::Phi<DimSolution>>>{})),\n      pw.d2psi_dtdx(x, t));\n  CHECK_ITERABLE_APPROX(\n      get<Tags::dt<ScalarWave::Tags::Pi>>(pw.variables(\n          x, t,\n          tmpl::list<Tags::dt<ScalarWave::Tags::Psi>,\n                     Tags::dt<ScalarWave::Tags::Pi>,\n                     Tags::dt<ScalarWave::Tags::Phi<DimSolution>>>{})),\n      Scalar<DataVector>(-1.0 * pw.d2psi_dt2(x, t).get()));\n}\n\nvoid test_1d() {\n  const double kx = -1.5;\n  const double center_x = 2.4;\n  const double omega = std::abs(kx);\n  const double t = 3.1;\n  const double x1 = -0.2;\n  const double x2 = 8.7;\n  const tnsr::I<DataVector, 1> x(DataVector({x1, x2}));\n  const DataVector u(\n      {kx * (x1 - center_x) - omega * t, kx * (x2 - center_x) - omega * t});\n  const ScalarWave::Solutions::PlaneWave<1> pw(\n      {{kx}}, {{center_x}},\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(3));\n  check_solution<1>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * kx * square(u),\n      std::array<DataVector, 2>{{-6.0 * omega * kx * u, 6.0 * square(kx) * u}},\n      pw, x, t);\n\n  register_factory_classes_with_charm<Metavariables<1>>();\n  const auto deserialized_pw = serialize_and_deserialize(pw);\n  check_solution<1>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * kx * square(u),\n      std::array<DataVector, 2>{{-6.0 * omega * kx * u, 6.0 * square(kx) * u}},\n      deserialized_pw, x, t);\n\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag<\n          evolution::initial_data::OptionTags::InitialData, Metavariables<1>>(\n          \"PlaneWave:\\n\"\n          \"  WaveVector: [-1.5]\\n\"\n          \"  Center: [2.4]\\n\"\n          \"  Profile:\\n\"\n          \"    PowX:\\n\"\n          \"      Power: 3\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution =\n      dynamic_cast<const ScalarWave::Solutions::PlaneWave<1>&>(\n          *deserialized_option_solution);\n\n  CHECK(created_solution.variables(\n            x, t,\n            tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                       ScalarWave::Tags::Phi<1>>{}) ==\n        pw.variables(x, t,\n                     tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                ScalarWave::Tags::Phi<1>>{}));\n}\n\nvoid test_2d() {\n  const double kx = 1.5;\n  const double ky = -7.2;\n  const double center_x = 2.4;\n  const double center_y = -4.8;\n  const double omega = std::sqrt(square(kx) + square(ky));\n  const double t = 3.1;\n  const double x1 = -10.2;\n  const double x2 = 8.7;\n  const double y1 = -1.98;\n  const double y2 = 48.27;\n  const tnsr::I<DataVector, 2> x{\n      std::array<DataVector, 2>{{DataVector({x1, x2}), DataVector({y1, y2})}}};\n  const DataVector u({kx * (x1 - center_x) + ky * (y1 - center_y) - omega * t,\n                      kx * (x2 - center_x) + ky * (y2 - center_y) - omega * t});\n  const ScalarWave::Solutions::PlaneWave<2> pw(\n      {{kx, ky}}, {{center_x, center_y}},\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(3));\n  check_solution<1>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * kx * square(u),\n      std::array<DataVector, 2>{{-6.0 * omega * kx * u, 6.0 * square(kx) * u}},\n      pw, x, t);\n  check_solution<2>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * ky * square(u),\n      std::array<DataVector, 3>{\n          {-6.0 * omega * ky * u, 6.0 * kx * ky * u, 6.0 * square(ky) * u}},\n      pw, x, t);\n\n  register_factory_classes_with_charm<Metavariables<2>>();\n  const auto deserialized_pw = serialize_and_deserialize(pw);\n  check_solution<1>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * kx * square(u),\n      std::array<DataVector, 2>{{-6.0 * omega * kx * u, 6.0 * square(kx) * u}},\n      deserialized_pw, x, t);\n  check_solution<2>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * ky * square(u),\n      std::array<DataVector, 3>{\n          {-6.0 * omega * ky * u, 6.0 * kx * ky * u, 6.0 * square(ky) * u}},\n      deserialized_pw, x, t);\n\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag<\n          evolution::initial_data::OptionTags::InitialData, Metavariables<2>>(\n          \"PlaneWave:\\n\"\n          \"  WaveVector: [1.5, -7.2]\\n\"\n          \"  Center: [2.4, -4.8]\\n\"\n          \"  Profile:\\n\"\n          \"    PowX:\\n\"\n          \"      Power: 3\");\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution =\n      dynamic_cast<const ScalarWave::Solutions::PlaneWave<2>&>(\n          *deserialized_option_solution);\n\n  CHECK(created_solution.variables(\n            x, t,\n            tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                       ScalarWave::Tags::Phi<2>>{}) ==\n        pw.variables(x, t,\n                     tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                ScalarWave::Tags::Phi<2>>{}));\n}\n\nvoid test_3d() {\n  const double kx = 1.5;\n  const double ky = -7.2;\n  const double kz = 2.7;\n  const double center_x = 2.4;\n  const double center_y = -4.8;\n  const double center_z = 8.4;\n  const double omega = std::sqrt(square(kx) + square(ky) + square(kz));\n  const double t = 3.1;\n  const double x1 = -10.2;\n  const double x2 = 8.7;\n  const double y1 = -1.98;\n  const double y2 = 48.27;\n  const double z1 = 2.2;\n  const double z2 = 1.1;\n  const tnsr::I<DataVector, 3> x{std::array<DataVector, 3>{\n      {DataVector({x1, x2}), DataVector({y1, y2}), DataVector({z1, z2})}}};\n  const DataVector u({kx * (x1 - center_x) + ky * (y1 - center_y) +\n                          kz * (z1 - center_z) - omega * t,\n                      kx * (x2 - center_x) + ky * (y2 - center_y) +\n                          kz * (z2 - center_z) - omega * t});\n  const ScalarWave::Solutions::PlaneWave<3> pw(\n      {{kx, ky, kz}}, {{center_x, center_y, center_z}},\n      std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(3));\n  check_solution<1>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * kx * square(u),\n      std::array<DataVector, 2>{{-6.0 * omega * kx * u, 6.0 * square(kx) * u}},\n      pw, x, t);\n  check_solution<2>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * ky * square(u),\n      std::array<DataVector, 3>{\n          {-6.0 * omega * ky * u, 6.0 * kx * ky * u, 6.0 * square(ky) * u}},\n      pw, x, t);\n  check_solution<3>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * kz * square(u),\n      std::array<DataVector, 4>{{-6.0 * omega * kz * u, 6.0 * kx * kz * u,\n                                 6.0 * ky * kz * u, 6.0 * square(kz) * u}},\n      pw, x, t);\n\n  register_factory_classes_with_charm<Metavariables<3>>();\n  const auto deserialized_pw = serialize_and_deserialize(pw);\n  check_solution<1>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * kx * square(u),\n      std::array<DataVector, 2>{{-6.0 * omega * kx * u, 6.0 * square(kx) * u}},\n      deserialized_pw, x, t);\n  check_solution<2>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * ky * square(u),\n      std::array<DataVector, 3>{\n          {-6.0 * omega * ky * u, 6.0 * kx * ky * u, 6.0 * square(ky) * u}},\n      deserialized_pw, x, t);\n  check_solution<3>(\n      cube(u), -3.0 * omega * square(u), 6.0 * square(omega) * u,\n      3.0 * kz * square(u),\n      std::array<DataVector, 4>{{-6.0 * omega * kz * u, 6.0 * kx * kz * u,\n                                 6.0 * ky * kz * u, 6.0 * square(kz) * u}},\n      deserialized_pw, x, t);\n\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag<\n          evolution::initial_data::OptionTags::InitialData, Metavariables<3>>(\n          \"PlaneWave:\\n\"\n          \"  WaveVector: [1.5, -7.2, 2.7]\\n\"\n          \"  Center: [2.4, -4.8, 8.4]\\n\"\n          \"  Profile:\\n\"\n          \"    PowX:\\n\"\n          \"      Power: 3\");\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution =\n      dynamic_cast<const ScalarWave::Solutions::PlaneWave<3>&>(\n          *deserialized_option_solution);\n\n  CHECK(created_solution.variables(\n            x, t,\n            tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                       ScalarWave::Tags::Phi<3>>{}) ==\n        pw.variables(x, t,\n                     tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                ScalarWave::Tags::Phi<3>>{}));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.WaveEquation.PlaneWave\",\n    \"[PointwiseFunctions][Unit]\") {\n  test_1d();\n  test_2d();\n  test_3d();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/WaveEquation/Test_RegularSphericalWave.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <memory>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/RegularSphericalWave.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<MathFunction<1, Frame::Inertial>,\n                   tmpl::list<MathFunctions::Gaussian<1, Frame::Inertial>>>,\n        tmpl::pair<evolution::initial_data::InitialData,\n                   tmpl::list<ScalarWave::Solutions::RegularSphericalWave>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.AnalyticSolutions.WaveEquation.RegularSphericalWave\",\n                  \"[PointwiseFunctions][Unit]\") {\n  register_factory_classes_with_charm<Metavariables>();\n  const ScalarWave::Solutions::RegularSphericalWave solution{\n      std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(1., 1.,\n                                                                    0.)};\n  CHECK_FALSE(solution != solution);\n  test_copy_semantics(solution);\n  auto solution_for_move = solution;\n  test_move_semantics(std::move(solution_for_move), solution);\n\n  const tnsr::I<DataVector, 3> x{std::array<DataVector, 3>{\n      {DataVector({0., 1., 2., 3.}), DataVector({0., 0., 0., 0.}),\n       DataVector({0., 0., 0., 0.})}}};\n  auto vars =\n      solution.variables(x, 1.,\n                         tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                                    ScalarWave::Tags::Phi<3>>{});\n  CHECK_ITERABLE_APPROX(\n      get<ScalarWave::Tags::Psi>(vars).get(),\n      DataVector({{1.471517764685769, 0.9816843611112658, 0.1838780156836778,\n                   0.006105175451186487}}));\n  CHECK_ITERABLE_APPROX(\n      get<ScalarWave::Tags::Pi>(vars).get(),\n      DataVector({{1.471517764685769, -0.07326255555493672, -0.3682496705837024,\n                   -0.02442115194544483}}));\n  CHECK_ITERABLE_APPROX(\n      get<ScalarWave::Tags::Phi<3>>(vars).get(0),\n      DataVector({{0., -0.9084218055563291, -0.4594482196010212,\n                   -0.02645561024157515}}));\n  CHECK_ITERABLE_APPROX(get<ScalarWave::Tags::Phi<3>>(vars).get(1),\n                        DataVector({{0., 0., 0., 0.}}));\n  CHECK_ITERABLE_APPROX(get<ScalarWave::Tags::Phi<3>>(vars).get(2),\n                        DataVector({{0., 0., 0., 0.}}));\n  auto dt_vars =\n      solution.variables(x, 1.,\n                         tmpl::list<Tags::dt<ScalarWave::Tags::Psi>,\n                                    Tags::dt<ScalarWave::Tags::Pi>,\n                                    Tags::dt<ScalarWave::Tags::Phi<3>>>{});\n  CHECK_ITERABLE_APPROX(\n      get<Tags::dt<ScalarWave::Tags::Psi>>(dt_vars).get(),\n      DataVector({{-1.471517764685769, 0.07326255555493672, 0.3682496705837024,\n                   0.02442115194544483}}));\n  CHECK_ITERABLE_APPROX(\n      get<Tags::dt<ScalarWave::Tags::Pi>>(dt_vars).get(),\n      DataVector({{2.943035529371539, 2.256418944442279, -0.3657814745019688,\n                   -0.08547065575381531}}));\n  CHECK_ITERABLE_APPROX(get<Tags::dt<ScalarWave::Tags::Phi<3>>>(dt_vars).get(0),\n                        DataVector({{0., 1.670318500002785, -0.5541022431327671,\n                                     -0.09361569118951865}}));\n  CHECK_ITERABLE_APPROX(get<Tags::dt<ScalarWave::Tags::Phi<3>>>(dt_vars).get(1),\n                        DataVector({{0., 0., 0., 0.}}));\n  CHECK_ITERABLE_APPROX(get<Tags::dt<ScalarWave::Tags::Phi<3>>>(dt_vars).get(2),\n                        DataVector({{0., 0., 0., 0.}}));\n\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag<\n          evolution::initial_data::OptionTags::InitialData, Metavariables>(\n          \"RegularSphericalWave:\\n\"\n          \"  Profile:\\n\"\n          \"    Gaussian:\\n\"\n          \"      Amplitude: 1.\\n\"\n          \"      Width: 1.\\n\"\n          \"      Center: 0.\\n\")\n          ->get_clone();\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution);\n  const auto& created_solution =\n      dynamic_cast<const ScalarWave::Solutions::RegularSphericalWave&>(\n          *deserialized_option_solution);\n\n  CHECK(created_solution.variables(\n            x, 1.,\n            tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Pi,\n                       ScalarWave::Tags::Phi<3>>{}) == vars);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/WaveEquation/Test_SemidiscretizedDg.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <pup.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceComputeTags.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Domain/TagsTimeDependent.hpp\"\n#include \"Evolution/DiscontinuousGalerkin/Actions/ComputeTimeDerivative.hpp\"\n#include \"Evolution/Systems/ScalarWave/Characteristics.hpp\"\n#include \"Evolution/Systems/ScalarWave/Constraints.hpp\"\n#include \"Evolution/Systems/ScalarWave/Equations.hpp\"\n#include \"Evolution/Systems/ScalarWave/System.hpp\"\n#include \"Evolution/Systems/ScalarWave/TimeDerivative.hpp\"\n#include \"Evolution/Systems/ScalarWave/UpwindPenaltyCorrection.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Actions/ComputeNonconservativeBoundaryFluxes.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/BoundarySchemes/FirstOrder/FirstOrderScheme.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Formulation.hpp\"\n#include \"NumericalAlgorithms/DiscontinuousGalerkin/Tags.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/DiscontinuousGalerkin/CollectDataForFluxes.hpp\"\n#include \"ParallelAlgorithms/DiscontinuousGalerkin/FluxCommunication.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/WaveEquation/SemidiscretizedDg.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/InitialData.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = ElementId<1>;\n\n  using variables_tag = typename metavariables::system::variables_tag;\n  using boundary_scheme = typename metavariables::boundary_scheme;\n  using normal_dot_fluxes_tag = domain::Tags::Interface<\n      domain::Tags::InternalDirections<1>,\n      db::add_tag_prefix<Tags::NormalDotFlux, variables_tag>>;\n  using history_tag = ::Tags::HistoryEvolvedVariables<variables_tag>;\n  using mortar_data_tag =\n      Tags::Mortars<typename boundary_scheme::mortar_data_tag, 1>;\n\n  using const_global_cache_tags =\n      tmpl::list<typename boundary_scheme::numerical_flux_computer_tag>;\n\n  using simple_tags = db::AddSimpleTags<\n      Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, domain::Tags::Mesh<1>,\n      domain::Tags::Element<1>, domain::Tags::MeshVelocity<1>,\n      domain::Tags::DivMeshVelocity, domain::Tags::ElementMap<1>, variables_tag,\n      db::add_tag_prefix<Tags::dt, variables_tag>,\n      ScalarWave::Tags::ConstraintGamma2, normal_dot_fluxes_tag, history_tag,\n      mortar_data_tag, Tags::Mortars<Tags::Next<Tags::TimeStepId>, 1>,\n      Tags::Mortars<domain::Tags::Mesh<0>, 1>,\n      Tags::Mortars<Tags::MortarSize<0>, 1>,\n      // Need this only because the DG scheme doesn't know at compile-time that\n      // the element has no external boundaries\n      domain::Tags::Interface<\n          domain::Tags::BoundaryDirectionsInterior<1>,\n          Tags::Magnitude<domain::Tags::UnnormalizedFaceNormal<1>>>>;\n\n  using inverse_jacobian = domain::Tags::InverseJacobianCompute<\n      domain::Tags::ElementMap<1>,\n      domain::Tags::Coordinates<1, Frame::ElementLogical>>;\n\n  template <typename Tag>\n  using interface_compute_tag =\n      domain::Tags::InterfaceCompute<domain::Tags::InternalDirections<1>, Tag>;\n\n  using compute_tags = db::AddComputeTags<\n      domain::Tags::LogicalCoordinates<1>,\n      domain::Tags::MappedCoordinates<\n          domain::Tags::ElementMap<1>,\n          domain::Tags::Coordinates<1, Frame::ElementLogical>>,\n      inverse_jacobian,\n      Tags::DerivCompute<variables_tag, domain::Tags::Mesh<Dim>,\n                         inverse_jacobian,\n                         typename metavariables::system::gradients_tags>,\n      domain::Tags::InternalDirectionsCompute<1>,\n      domain::Tags::Slice<domain::Tags::InternalDirections<1>,\n                          typename metavariables::system::variables_tag>,\n      domain::Tags::Slice<domain::Tags::InternalDirections<1>,\n                          ScalarWave::Tags::ConstraintGamma2>,\n      interface_compute_tag<domain::Tags::Direction<1>>,\n      interface_compute_tag<domain::Tags::InterfaceMesh<1>>,\n      interface_compute_tag<domain::Tags::UnnormalizedFaceNormalCompute<1>>,\n      interface_compute_tag<\n          Tags::EuclideanMagnitude<domain::Tags::UnnormalizedFaceNormal<1>>>,\n      interface_compute_tag<\n          Tags::NormalizedCompute<domain::Tags::UnnormalizedFaceNormal<1>>>,\n      interface_compute_tag<ScalarWave::Tags::CharacteristicFieldsCompute<1>>,\n      interface_compute_tag<ScalarWave::Tags::CharacteristicSpeedsCompute<1>>>;\n\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase, Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n\n      Parallel::PhaseActions<\n          Parallel::Phase, Parallel::Phase::Testing,\n          tmpl::list<evolution::dg::Actions::ComputeTimeDerivative<\n                         volume_dim, system, AllStepChoosers>,\n                     dg::Actions::ReceiveDataForFluxes<boundary_scheme>,\n                     Actions::MutateApply<boundary_scheme>>>>;\n};\n\nstruct Metavariables {\n  static constexpr size_t volume_dim = 1;\n  using system = ScalarWave::System<1>;\n  using boundary_scheme = dg::FirstOrderScheme::FirstOrderScheme<\n      1, typename system::variables_tag,\n      db::add_tag_prefix<::Tags::dt, typename system::variables_tag>,\n      Tags::NumericalFlux<ScalarWave::UpwindPenaltyCorrection<1>>,\n      Tags::TimeStepId>;\n  static constexpr bool local_time_stepping = false;\n  using step_choosers = tmpl::list<>;\n\n  using component_list = tmpl::list<Component<Metavariables>>;\n  using temporal_id = Tags::TimeStepId;\n  enum class Phase { Initialization, Testing, Exit };\n};\n\nusing system = Metavariables::system;\nusing EvolvedVariables = typename system::variables_tag::type;\n\nstd::pair<tnsr::I<DataVector, 1>, EvolvedVariables> evaluate_rhs(\n    const double time,\n    const ScalarWave::Solutions::SemidiscretizedDg& solution) {\n  using component = Component<Metavariables>;\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {ScalarWave::UpwindPenaltyCorrection<1>{},\n       ::dg::Formulation::StrongInertial}};\n\n  const Slab slab(time, time + 1.0);\n  const TimeStepId current_time(true, 0, slab.start());\n  const TimeStepId next_time(true, 0, slab.end());\n  const Mesh<1> mesh{2, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n\n  PUPable_reg(\n      SINGLE_ARG(domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                       domain::CoordinateMaps::Affine>));\n  const auto block_map =\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Affine(-1.0, 1.0, 0.0, 2.0 * M_PI));\n\n  const auto emplace_element =\n      [&block_map, &current_time, &mesh, &next_time, &runner, &solution, &time](\n          const ElementId<1>& id,\n          const std::vector<std::pair<Direction<1>, ElementId<1>>>& mortars) {\n        Element<1>::Neighbors_t neighbors;\n        for (const auto& mortar_id : mortars) {\n          neighbors.insert({mortar_id.first, {{mortar_id.second}, {}}});\n        }\n        const Element<1> element(id, std::move(neighbors));\n\n        auto map = ElementMap<1, Frame::Inertial>(id, block_map->get_clone());\n\n        typename system::variables_tag::type variables(2);\n        typename db::add_tag_prefix<Tags::dt, system::variables_tag>::type\n            dt_variables(2);\n\n        typename component::normal_dot_fluxes_tag::type normal_dot_fluxes;\n        typename component::history_tag::type history{4};\n        typename component::mortar_data_tag::type mortar_history{};\n        typename Tags::Mortars<Tags::Next<Tags::TimeStepId>, 1>::type\n            mortar_next_temporal_ids{};\n        typename Tags::Mortars<domain::Tags::Mesh<0>, 1>::type mortar_meshes{};\n        typename Tags::Mortars<Tags::MortarSize<0>, 1>::type mortar_sizes{};\n        for (const auto& mortar_id : mortars) {\n          normal_dot_fluxes[mortar_id.first].initialize(1, 0.0);\n          mortar_history.insert({mortar_id, {}});\n          mortar_next_temporal_ids.insert({mortar_id, current_time});\n          mortar_meshes.insert({mortar_id, mesh.slice_away(0)});\n          mortar_sizes.insert({mortar_id, {}});\n        }\n        Scalar<DataVector> gamma_2{mesh.number_of_grid_points(), 0.};\n\n        ActionTesting::emplace_component_and_initialize<component>(\n            &runner, id,\n            {current_time, next_time, mesh, element,\n             std::optional<tnsr::I<DataVector, 1, Frame::Inertial>>{},\n             std::optional<Scalar<DataVector>>{}, std::move(map),\n             std::move(variables), std::move(dt_variables), std::move(gamma_2),\n             std::move(normal_dot_fluxes), std::move(history),\n             std::move(mortar_history), std::move(mortar_next_temporal_ids),\n             std::move(mortar_meshes), std::move(mortar_sizes),\n             std::unordered_map<Direction<1>, Scalar<DataVector>>{}});\n\n        auto& box = ActionTesting::get_databox<\n            component,\n            tmpl::append<component::simple_tags, component::compute_tags>>(\n            make_not_null(&runner), id);\n        db::mutate<system::variables_tag>(\n            [&solution, &time](\n                const gsl::not_null<EvolvedVariables*> vars,\n                const tnsr::I<DataVector, 1, Frame::Inertial>& coords) {\n              vars->assign_subset(solution.variables(\n                  coords, time, system::variables_tag::tags_list{}));\n            },\n            make_not_null(&box),\n            db::get<domain::Tags::Coordinates<1, Frame::Inertial>>(box));\n      };\n\n  const ElementId<1> self_id(0, {{{4, 1}}});\n  const ElementId<1> left_id(0, {{{4, 0}}});\n  const ElementId<1> right_id(0, {{{4, 2}}});\n\n  emplace_element(self_id, {{Direction<1>::lower_xi(), left_id},\n                            {Direction<1>::upper_xi(), right_id}});\n  emplace_element(left_id, {{Direction<1>::upper_xi(), self_id}});\n  emplace_element(right_id, {{Direction<1>::lower_xi(), self_id}});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  // The neighbors only have to get as far as sending data\n  for (size_t i = 0; i < 1; ++i) {\n    runner.next_action<component>(left_id);\n    runner.next_action<component>(right_id);\n  }\n\n  for (size_t i = 0; i < 3; ++i) {\n    runner.next_action<component>(self_id);\n  }\n\n  return {\n      ActionTesting::get_databox_tag<\n          component, domain::Tags::Coordinates<1, Frame::Inertial>>(runner,\n                                                                    self_id),\n      EvolvedVariables(\n          ActionTesting::get_databox_tag<\n              component, db::add_tag_prefix<Tags::dt, system::variables_tag>>(\n              runner, self_id))};\n}\n\nvoid check_solution(const ScalarWave::Solutions::SemidiscretizedDg& solution) {\n  const double time = 1.23;\n\n  const auto coords_and_deriv_from_system = evaluate_rhs(time, solution);\n  const auto& coords = coords_and_deriv_from_system.first;\n  const auto& deriv_from_system = coords_and_deriv_from_system.second;\n  const auto numerical_deriv = numerical_derivative(\n      [&coords, &solution](const std::array<double, 1>& t) {\n        EvolvedVariables result(2);\n        result.assign_subset(solution.variables(\n            coords, t[0], system::variables_tag::tags_list{}));\n        return result;\n      },\n      std::array<double, 1>{{time}}, 0, 1e-3);\n\n  CHECK_VARIABLES_CUSTOM_APPROX(numerical_deriv, deriv_from_system,\n                                approx.epsilon(1.0e-12));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.WaveEquation.SemidiscretizedDg\",\n    \"[Unit][PointwiseFunctions]\") {\n  // We use 16 elements, so there are 16 independent harmonics.\n  for (int harmonic = 0; harmonic < 16; ++harmonic) {\n    check_solution({harmonic, {{1.2, 2.3, 3.4, 4.5}}});\n  }\n\n  register_classes_with_charm<ScalarWave::Solutions::SemidiscretizedDg>();\n  const std::unique_ptr<evolution::initial_data::InitialData> option_solution =\n      TestHelpers::test_option_tag_factory_creation<\n          evolution::initial_data::OptionTags::InitialData,\n          ScalarWave::Solutions::SemidiscretizedDg>(\n          \"SemidiscretizedDg:\\n\"\n          \"  Harmonic: 1\\n\"\n          \"  Amplitudes: [1.2, 2.3, 3.4, 4.5]\\n\");\n  const auto deserialized_option_solution =\n      serialize_and_deserialize(option_solution->get_clone());\n  const auto& solution =\n      dynamic_cast<const ScalarWave::Solutions::SemidiscretizedDg&>(\n          *deserialized_option_solution);\n  check_solution(solution);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_XctsSolutions\")\n\nset(LIBRARY_SOURCES\n  Test_ConstantDensityStar.cpp\n  Test_Flatness.cpp\n  Test_HarmonicSchwarzschild.cpp\n  Test_Kerr.cpp\n  Test_RotatingStar.cpp\n  Test_Schwarzschild.cpp\n  Test_SphericalKerr.cpp\n  Test_TovStar.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  RelativisticEulerSolutions\n  Spectral\n  Utilities\n  Xcts\n  XctsSolutions\n  XctsSolutionsTestHelpers\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/ConstantDensityStar.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom scipy.optimize import newton\n\n\ndef compute_alpha(density, radius):\n    def f(a):\n        return (\n            density * radius**2\n            - 3.0 / (2.0 * np.pi) * a**10 / (1.0 + a**2) ** 6\n        )\n\n    def fprime(a):\n        return 3.0 * a**9 * (a**2 - 5.0) / (1.0 + a**2) ** 7 / np.pi\n\n    return newton(func=f, fprime=fprime, x0=2.0 * np.sqrt(5.0))\n\n\ndef sobolov(r, alpha, radius):\n    return np.sqrt(alpha * radius / (r**2 + (alpha * radius) ** 2))\n\n\ndef conformal_factor(x, density, radius):\n    alpha = compute_alpha(density, radius)\n    C = (3.0 / (2.0 * np.pi * density)) ** (1.0 / 4.0)\n    r = np.linalg.norm(x)\n    if r <= radius:\n        return C * sobolov(r, alpha, radius)\n    else:\n        beta = radius * (C * sobolov(radius, alpha, radius) - 1.0)\n        return beta / r + 1.0\n\n\ndef initial_conformal_factor(x, density, radius):\n    return 1.0\n\n\ndef initial_conformal_factor_gradient(x, density, radius):\n    return np.zeros(len(x))\n\n\ndef conformal_factor_source(x, density, radius):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/SchwarzschildIsotropic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef conformal_metric(x, mass):\n    return np.identity(3)\n\n\ndef inv_conformal_metric(x, mass):\n    return np.identity(3)\n\n\ndef deriv_conformal_metric(x, mass):\n    return np.zeros((3, 3, 3))\n\n\ndef extrinsic_curvature_trace(x, mass):\n    return 0.0\n\n\ndef extrinsic_curvature_trace_gradient(x, mass):\n    return np.zeros(3)\n\n\ndef conformal_factor(x, mass):\n    r = np.linalg.norm(x)\n    return 1.0 + 0.5 * mass / r\n\n\ndef conformal_factor_gradient(x, mass):\n    r = np.linalg.norm(x)\n    return -0.5 * mass * x / r**3\n\n\ndef lapse_times_conformal_factor(x, mass):\n    r = np.linalg.norm(x)\n    return 1.0 - 0.5 * mass / r\n\n\ndef lapse_times_conformal_factor_gradient(x, mass):\n    r = np.linalg.norm(x)\n    return 0.5 * mass * x / r**3\n\n\ndef lapse(x, mass):\n    return lapse_times_conformal_factor(x, mass) / conformal_factor(x, mass)\n\n\ndef shift_background(x, mass):\n    return np.zeros(3)\n\n\ndef longitudinal_shift_background_minus_dt_conformal_metric(x, mass):\n    return np.zeros((3, 3))\n\n\ndef shift(x, mass):\n    return np.zeros(3)\n\n\ndef shift_strain(x, mass):\n    return np.zeros((3, 3))\n\n\ndef longitudinal_shift(x, mass):\n    return np.zeros((3, 3))\n\n\ndef shift_dot_extrinsic_curvature_trace_gradient(x, mass):\n    return 0.0\n\n\ndef longitudinal_shift_minus_dt_conformal_metric_square(x, mass):\n    return 0.0\n\n\ndef longitudinal_shift_minus_dt_conformal_metric_over_lapse_square(x, mass):\n    return 0.0\n\n\n# Matter sources\n\n\ndef energy_density(x, mass):\n    return 0.0\n\n\ndef stress_trace(x, mass):\n    return 0.0\n\n\ndef momentum_density(x, mass):\n    return np.zeros(3)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/SchwarzschildKerrSchildIsotropic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom numpy import exp, sqrt\nfrom scipy.optimize import newton\n\n# Areal Kerr-Schild coordinates\n\n\ndef lapse_areal(r, mass):\n    return 1.0 / sqrt(1.0 + 2.0 * mass / r)\n\n\ndef lapse_areal_deriv(r, mass):\n    return lapse_areal(r, mass) ** 3 * mass / r**2\n\n\ndef shift_magnitude_areal(r, mass):\n    return 2.0 * mass * lapse_areal(r, mass) ** 2 / r\n\n\ndef shift_areal(x, mass):\n    r = np.linalg.norm(x)\n    return shift_magnitude_areal(r, mass) * x / r\n\n\ndef spatial_metric_areal(x, mass):\n    r = np.linalg.norm(x)\n    return np.identity(3) + 2.0 * mass * np.outer(x, x) / r**3\n\n\ndef extrinsic_curvature_trace_areal(r, mass):\n    lapse = lapse_areal(r, mass)\n    return 2.0 * mass * lapse**3 / r**2 * (1.0 + 3.0 * mass / r)\n\n\ndef extrinsic_curvature_trace_areal_deriv(r, mass):\n    lapse = lapse_areal(r, mass)\n    K = extrinsic_curvature_trace_areal(r, mass)\n    return (\n        K\n        / r\n        * (3.0 * mass * lapse**2 / r - 3.0 * mass / (r + 3.0 * mass) - 2.0)\n    )\n\n\ndef extrinsic_curvature_areal(x, mass):\n    r = np.linalg.norm(x)\n    lapse = lapse_areal(r, mass)\n    return (\n        2.0\n        * mass\n        * lapse\n        / r**2\n        * (np.identity(3) - (2.0 + mass / r) * np.outer(x, x) / r**2)\n    )\n\n\n# Isotropic Kerr-Schild coordinates\n# See e.g. Sec. 7.4.1 in https://arxiv.org/abs/gr-qc/0510016\n\n\ndef isotropic_radius_from_areal(r_areal, mass):\n    one_over_lapse = 1.0 / lapse_areal(r_areal, mass)\n    return (\n        r_areal\n        / 4.0\n        * (1.0 + one_over_lapse) ** 2\n        * exp(2.0 - 2.0 * one_over_lapse)\n    )\n\n\ndef isotropic_radius_from_areal_deriv(r_areal, mass):\n    r_isotropic = isotropic_radius_from_areal(r_areal, mass)\n    return r_isotropic / r_areal / lapse_areal(r_areal, mass)\n\n\ndef areal_radius_from_isotropic(r_isotropic, mass):\n    def f(r_areal):\n        return isotropic_radius_from_areal(r_areal, mass) - r_isotropic\n\n    def fprime(r_areal):\n        return isotropic_radius_from_areal_deriv(r_areal, mass)\n\n    return newton(func=f, fprime=fprime, x0=r_isotropic, tol=1.0e-12)\n\n\ndef conformal_metric(x, mass):\n    return np.identity(3)\n\n\ndef inv_conformal_metric(x, mass):\n    return np.identity(3)\n\n\ndef deriv_conformal_metric(x, mass):\n    return np.zeros((3, 3, 3))\n\n\ndef extrinsic_curvature_trace(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return extrinsic_curvature_trace_areal(r_areal, mass)\n\n\ndef extrinsic_curvature_trace_gradient(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return (\n        extrinsic_curvature_trace_areal_deriv(r_areal, mass)\n        / isotropic_radius_from_areal_deriv(r_areal, mass)\n        * x\n        / r_isotropic\n    )\n\n\ndef conformal_factor_from_areal(r_areal, mass):\n    one_over_lapse = 1.0 / lapse_areal(r_areal, mass)\n    return 2.0 * exp(one_over_lapse - 1.0) / (1.0 + one_over_lapse)\n\n\ndef conformal_factor_from_areal_deriv(r_areal, mass):\n    one_over_lapse = 1.0 / lapse_areal(r_areal, mass)\n    return (\n        -2.0\n        * mass\n        * exp(one_over_lapse - 1.0)\n        / (1.0 + one_over_lapse) ** 2\n        / r_areal**2\n    )\n\n\ndef conformal_factor(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return conformal_factor_from_areal(r_areal, mass)\n\n\ndef conformal_factor_gradient(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return (\n        conformal_factor_from_areal_deriv(r_areal, mass)\n        / isotropic_radius_from_areal_deriv(r_areal, mass)\n        * x\n        / r_isotropic\n    )\n\n\ndef lapse_times_conformal_factor(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return lapse_areal(r_areal, mass) * conformal_factor_from_areal(\n        r_areal, mass\n    )\n\n\ndef lapse_times_conformal_factor_gradient(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return (\n        (\n            lapse_areal_deriv(r_areal, mass)\n            * conformal_factor_from_areal(r_areal, mass)\n            + lapse_areal(r_areal, mass)\n            * conformal_factor_from_areal_deriv(r_areal, mass)\n        )\n        / isotropic_radius_from_areal_deriv(r_areal, mass)\n        * x\n        / r_isotropic\n    )\n\n\ndef lapse(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return lapse_areal(r_areal, mass)\n\n\ndef shift_background(x, mass):\n    return np.zeros(3)\n\n\ndef longitudinal_shift_background_minus_dt_conformal_metric(x, mass):\n    return np.zeros((3, 3))\n\n\ndef shift_magnitude_from_areal(r_areal, mass):\n    return (\n        shift_magnitude_areal(r_areal, mass)\n        / lapse_areal(r_areal, mass)\n        / conformal_factor_from_areal(r_areal, mass) ** 2\n    )\n\n\ndef shift(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return shift_magnitude_from_areal(r_areal, mass) * x / r_isotropic\n\n\ndef shift_strain(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    lapse = lapse_areal(r_areal, mass)\n    return (\n        2.0\n        * mass\n        * lapse\n        / r_areal**2\n        * (\n            np.identity(3)\n            + (mass * lapse**3 / r_areal - 2.0 * lapse)\n            * np.outer(x, x)\n            / r_isotropic**2\n        )\n    )\n\n\ndef longitudinal_shift(x, mass):\n    B = shift_strain(x, mass)\n    return 2 * (B - 1.0 / 3.0 * np.identity(3) * np.trace(B))\n\n\ndef shift_dot_extrinsic_curvature_trace_gradient(x, mass):\n    beta = shift(x, mass)\n    dK = extrinsic_curvature_trace_gradient(x, mass)\n    return np.dot(beta, dK)\n\n\ndef longitudinal_shift_minus_dt_conformal_metric_square(x, mass):\n    LB = longitudinal_shift(x, mass)\n    return np.einsum(\"ij,ij\", LB, LB)\n\n\ndef longitudinal_shift_minus_dt_conformal_metric_over_lapse_square(x, mass):\n    LB = longitudinal_shift(x, mass)\n    return np.einsum(\"ij,ij\", LB, LB) / lapse(x, mass) ** 2\n\n\n# Matter sources\n\n\ndef energy_density(x, mass):\n    return 0.0\n\n\ndef stress_trace(x, mass):\n    return 0.0\n\n\ndef momentum_density(x, mass):\n    return np.zeros(3)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/SchwarzschildMaximalIsotropic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom numpy import exp, sqrt\nfrom scipy.optimize import newton\n\n# Schwarzschild Maximal Isotropic coordinates\n\n\ndef isotropic_radius_from_areal(r_areal, mass):\n    # First part of the equation\n    part1 = (\n        2 * r_areal\n        + mass\n        + sqrt(4 * r_areal**2 + 4 * mass * r_areal + 3 * mass**2)\n    ) / 4.0\n\n    # Second part of the equation\n    part2_numerator = (4 + 3 * sqrt(2)) * (2 * r_areal - 3 * mass)\n    part2_denominator = (\n        8 * r_areal\n        + 6 * mass\n        + 3 * sqrt(8 * r_areal**2 + 8 * mass * r_areal + 6 * mass**2)\n    )\n    part2 = (part2_numerator / part2_denominator) ** (1 / sqrt(2))\n\n    return part1 * part2\n\n\ndef isotropic_radius_from_areal_deriv(r_areal, mass):\n    h = 1e-6\n    term1 = -isotropic_radius_from_areal(r_areal + 2 * h, mass)\n    term2 = 8 * isotropic_radius_from_areal(r_areal + h, mass)\n    term3 = -8 * isotropic_radius_from_areal(r_areal - h, mass)\n    term4 = isotropic_radius_from_areal(r_areal - 2 * h, mass)\n\n    return (term1 + term2 + term3 + term4) / (12 * h)\n\n\ndef areal_radius_from_isotropic(r_isotropic, mass):\n    def f(r_areal):\n        return isotropic_radius_from_areal(r_areal, mass) - r_isotropic\n\n    def fprime(r_areal):\n        return isotropic_radius_from_areal_deriv(r_areal, mass)\n\n    return newton(\n        func=f,\n        fprime=fprime,\n        # Initial guess for r_areal can't be smaller than 1.5 * mass because\n        # isotropic radius is not defined for smaller values.\n        x0=max(r_isotropic, 2 * mass),\n        tol=1.0e-12,\n    )\n\n\ndef conformal_metric(x, mass):\n    return np.identity(3)\n\n\ndef inv_conformal_metric(x, mass):\n    return np.identity(3)\n\n\ndef deriv_conformal_metric(x, mass):\n    return np.zeros((3, 3, 3))\n\n\ndef extrinsic_curvature_trace(x, mass):\n    return 0.0\n\n\ndef extrinsic_curvature_trace_gradient(x, mass):\n    return np.zeros(3)\n\n\ndef conformal_factor(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return sqrt(r_areal / r_isotropic)\n\n\ndef conformal_factor_gradient(x, mass):\n    h = 1e-6\n\n    x_forward_2h = np.array([x[0] + 2 * h, x[1], x[2]])\n    x_forward_h = np.array([x[0] + h, x[1], x[2]])\n    x_backward_h = np.array([x[0] - h, x[1], x[2]])\n    x_backward_2h = np.array([x[0] - 2 * h, x[1], x[2]])\n    dx = (\n        1 / 12 * conformal_factor(x_backward_2h, mass)\n        - 2 / 3 * conformal_factor(x_backward_h, mass)\n        + 2 / 3 * conformal_factor(x_forward_h, mass)\n        - 1 / 12 * conformal_factor(x_forward_2h, mass)\n    ) / h\n\n    y_forward_2h = np.array([x[0], x[1] + 2 * h, x[2]])\n    y_forward_h = np.array([x[0], x[1] + h, x[2]])\n    y_backward_h = np.array([x[0], x[1] - h, x[2]])\n    y_backward_2h = np.array([x[0], x[1] - 2 * h, x[2]])\n    dy = (\n        1 / 12 * conformal_factor(y_backward_2h, mass)\n        - 2 / 3 * conformal_factor(y_backward_h, mass)\n        + 2 / 3 * conformal_factor(y_forward_h, mass)\n        - 1 / 12 * conformal_factor(y_forward_2h, mass)\n    ) / h\n\n    z_forward_2h = np.array([x[0], x[1], x[2] + 2 * h])\n    z_forward_h = np.array([x[0], x[1], x[2] + h])\n    z_backward_h = np.array([x[0], x[1], x[2] - h])\n    z_backward_2h = np.array([x[0], x[1], x[2] - 2 * h])\n    dz = (\n        1 / 12 * conformal_factor(z_backward_2h, mass)\n        - 2 / 3 * conformal_factor(z_backward_h, mass)\n        + 2 / 3 * conformal_factor(z_forward_h, mass)\n        - 1 / 12 * conformal_factor(z_forward_2h, mass)\n    ) / h\n\n    return np.array([dx, dy, dz])\n\n\ndef lapse(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return sqrt(1 - 2 * mass / r_areal + (27 / 16) * mass**4 / r_areal**4)\n\n\ndef lapse_times_conformal_factor(x, mass):\n    lapse_val = lapse(x, mass)\n    conformal_factor_val = conformal_factor(x, mass)\n    return lapse_val * conformal_factor_val\n\n\ndef lapse_times_conformal_factor_gradient(x, mass):\n    h = 1e-6\n\n    x_forward_2h = np.array([x[0] + 2 * h, x[1], x[2]])\n    x_forward_h = np.array([x[0] + h, x[1], x[2]])\n    x_backward_h = np.array([x[0] - h, x[1], x[2]])\n    x_backward_2h = np.array([x[0] - 2 * h, x[1], x[2]])\n    dx = (\n        1 / 12 * lapse_times_conformal_factor(x_backward_2h, mass)\n        - 2 / 3 * lapse_times_conformal_factor(x_backward_h, mass)\n        + 2 / 3 * lapse_times_conformal_factor(x_forward_h, mass)\n        - 1 / 12 * lapse_times_conformal_factor(x_forward_2h, mass)\n    ) / h\n\n    y_forward_2h = np.array([x[0], x[1] + 2 * h, x[2]])\n    y_forward_h = np.array([x[0], x[1] + h, x[2]])\n    y_backward_h = np.array([x[0], x[1] - h, x[2]])\n    y_backward_2h = np.array([x[0], x[1] - 2 * h, x[2]])\n    dy = (\n        1 / 12 * lapse_times_conformal_factor(y_backward_2h, mass)\n        - 2 / 3 * lapse_times_conformal_factor(y_backward_h, mass)\n        + 2 / 3 * lapse_times_conformal_factor(y_forward_h, mass)\n        - 1 / 12 * lapse_times_conformal_factor(y_forward_2h, mass)\n    ) / h\n\n    z_forward_2h = np.array([x[0], x[1], x[2] + 2 * h])\n    z_forward_h = np.array([x[0], x[1], x[2] + h])\n    z_backward_h = np.array([x[0], x[1], x[2] - h])\n    z_backward_2h = np.array([x[0], x[1], x[2] - 2 * h])\n    dz = (\n        1 / 12 * lapse_times_conformal_factor(z_backward_2h, mass)\n        - 2 / 3 * lapse_times_conformal_factor(z_backward_h, mass)\n        + 2 / 3 * lapse_times_conformal_factor(z_forward_h, mass)\n        - 1 / 12 * lapse_times_conformal_factor(z_forward_2h, mass)\n    ) / h\n\n    return np.array([dx, dy, dz])\n\n\ndef shift_background(x, mass):\n    return np.zeros(3)\n\n\ndef longitudinal_shift_background_minus_dt_conformal_metric(x, mass):\n    return np.zeros((3, 3))\n\n\ndef shift(x, mass):\n    r_isotropic = np.linalg.norm(x)\n    r_areal = areal_radius_from_isotropic(r_isotropic, mass)\n    return (\n        (0.75 * sqrt(3.0) * mass**2 * (r_isotropic / r_areal**3))\n        * x\n        / r_isotropic\n    )\n\n\ndef shift_strain(x, mass):\n    h = 1e-6\n    n = len(x)\n    strain_tensor = np.zeros((n, n))\n\n    for i in range(n):\n        for j in range(n):\n            x_forward_2h = np.copy(x)\n            x_forward_h = np.copy(x)\n            x_backward_h = np.copy(x)\n            x_backward_2h = np.copy(x)\n\n            x_forward_2h[j] += 2 * h\n            x_forward_h[j] += h\n            x_backward_h[j] -= h\n            x_backward_2h[j] -= 2 * h\n\n            beta_forward_2h = shift(x_forward_2h, mass)[i]\n            beta_forward_h = shift(x_forward_h, mass)[i]\n            beta_backward_h = shift(x_backward_h, mass)[i]\n            beta_backward_2h = shift(x_backward_2h, mass)[i]\n\n            strain_tensor[i, j] = (\n                1 / 12 * beta_backward_2h\n                - 2 / 3 * beta_backward_h\n                + 2 / 3 * beta_forward_h\n                - 1 / 12 * beta_forward_2h\n            ) / h\n\n    return strain_tensor\n\n\ndef longitudinal_shift(x, mass):\n    B = shift_strain(x, mass)\n    return 2 * (B - 1.0 / 3.0 * np.identity(3) * np.trace(B))\n\n\ndef shift_dot_extrinsic_curvature_trace_gradient(x, mass):\n    beta = shift(x, mass)\n    dK = extrinsic_curvature_trace_gradient(x, mass)\n    return np.dot(beta, dK)\n\n\ndef longitudinal_shift_minus_dt_conformal_metric_square(x, mass):\n    LB = longitudinal_shift(x, mass)\n    return np.einsum(\"ij,ij\", LB, LB)\n\n\ndef longitudinal_shift_minus_dt_conformal_metric_over_lapse_square(x, mass):\n    LB = longitudinal_shift(x, mass)\n    return np.einsum(\"ij,ij\", LB, LB) / lapse(x, mass) ** 2\n\n\n# Matter sources\n\n\ndef energy_density(x, mass):\n    return 0.0\n\n\ndef stress_trace(x, mass):\n    return 0.0\n\n\ndef momentum_density(x, mass):\n    return np.zeros(3)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/SchwarzschildPainleveGullstrand.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom numpy import sqrt\n\n\ndef conformal_metric(x, mass):\n    return np.identity(3)\n\n\ndef inv_conformal_metric(x, mass):\n    return np.identity(3)\n\n\ndef deriv_conformal_metric(x, mass):\n    return np.zeros((3, 3, 3))\n\n\ndef extrinsic_curvature_trace(x, mass):\n    r = np.linalg.norm(x)\n    return 3.0 / 2.0 * sqrt(2.0 * mass / r**3)\n\n\ndef extrinsic_curvature_trace_gradient(x, mass):\n    r = np.linalg.norm(x)\n    return -9.0 / 4.0 * sqrt(2.0 * mass / r**5) * x / r\n\n\ndef conformal_factor(x, mass):\n    return 1.0\n\n\ndef conformal_factor_gradient(x, mass):\n    return np.zeros(3)\n\n\ndef lapse_times_conformal_factor(x, mass):\n    return 1.0\n\n\ndef lapse_times_conformal_factor_gradient(x, mass):\n    return np.zeros(3)\n\n\ndef lapse(x, mass):\n    return 1.0\n\n\ndef shift_background(x, mass):\n    return np.zeros(3)\n\n\ndef longitudinal_shift_background_minus_dt_conformal_metric(x, mass):\n    return np.zeros((3, 3))\n\n\ndef shift(x, mass):\n    r = np.linalg.norm(x)\n    return sqrt(2.0 * mass / r) * x / r\n\n\ndef shift_strain(x, mass):\n    r = np.linalg.norm(x)\n    return sqrt(2.0 * mass / r**3) * (\n        np.identity(3) - 3.0 / 2.0 * np.outer(x, x) / r**2\n    )\n\n\ndef longitudinal_shift(x, mass):\n    B = shift_strain(x, mass)\n    return 2 * (B - 1.0 / 3.0 * np.identity(3) * np.trace(B))\n\n\ndef shift_dot_extrinsic_curvature_trace_gradient(x, mass):\n    beta = shift(x, mass)\n    dK = extrinsic_curvature_trace_gradient(x, mass)\n    return np.dot(beta, dK)\n\n\ndef longitudinal_shift_minus_dt_conformal_metric_square(x, mass):\n    LB = longitudinal_shift(x, mass)\n    return np.einsum(\"ij,ij\", LB, LB)\n\n\ndef longitudinal_shift_minus_dt_conformal_metric_over_lapse_square(x, mass):\n    LB = longitudinal_shift(x, mass)\n    return np.einsum(\"ij,ij\", LB, LB) / lapse(x, mass) ** 2\n\n\n# Matter sources\n\n\ndef energy_density(x, mass):\n    return 0.0\n\n\ndef stress_trace(x, mass):\n    return 0.0\n\n\ndef momentum_density(x, mass):\n    return np.zeros(3)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/Test_ConstantDensityStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/ConstantDensityStar.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\nusing field_tags = tmpl::list<Xcts::Tags::ConformalFactor<DataVector>>;\nusing auxiliary_field_tags =\n    tmpl::list<::Tags::deriv<Xcts::Tags::ConformalFactor<DataVector>,\n                             tmpl::size_t<3>, Frame::Inertial>>;\nusing initial_tags =\n    db::wrap_tags_in<Tags::Initial,\n                     tmpl::append<field_tags, auxiliary_field_tags>>;\nusing source_tags = db::wrap_tags_in<Tags::FixedSource, field_tags>;\n\nstruct ConstantDensityStarProxy : Xcts::Solutions::ConstantDensityStar {\n  using Xcts::Solutions::ConstantDensityStar::ConstantDensityStar;\n  tuples::tagged_tuple_from_typelist<field_tags> field_variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x) const {\n    return Xcts::Solutions::ConstantDensityStar::variables(x, field_tags{});\n  }\n  tuples::tagged_tuple_from_typelist<initial_tags> initial_variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x) const {\n    return Xcts::Solutions::ConstantDensityStar::variables(x, initial_tags{});\n  }\n  tuples::tagged_tuple_from_typelist<source_tags> source_variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x) const {\n    return Xcts::Solutions::ConstantDensityStar::variables(x, source_tags{});\n  }\n};\n\nvoid test_solution(const double density, const double radius,\n                   const std::string& options) {\n  const ConstantDensityStarProxy solution(density, radius);\n  const double test_radius = 2. * radius;\n  pypp::check_with_random_values<1>(\n      &ConstantDensityStarProxy::field_variables, solution,\n      \"ConstantDensityStar\", {\"conformal_factor\", \"conformal_factor_gradient\"},\n      {{{-test_radius, test_radius}}}, std::make_tuple(density, radius),\n      DataVector(5));\n  pypp::check_with_random_values<1>(\n      &ConstantDensityStarProxy::initial_variables, solution,\n      \"ConstantDensityStar\",\n      {\"initial_conformal_factor\", \"initial_conformal_factor_gradient\"},\n      {{{-test_radius, test_radius}}}, std::make_tuple(density, radius),\n      DataVector(5));\n  pypp::check_with_random_values<1>(\n      &ConstantDensityStarProxy::source_variables, solution,\n      \"ConstantDensityStar\", {\"conformal_factor_source\"},\n      {{{-test_radius, test_radius}}}, std::make_tuple(density, radius),\n      DataVector(5));\n\n  // Test that we selected the weak-field solution of the two possible branches\n  const tnsr::I<DataVector, 3> far_away_coords{{{{1e16 * radius, 0., 0.},\n                                                 {0., 1e16 * radius, 0.},\n                                                 {0., 0., 1e16 * radius}}}};\n  const auto far_away_solution = solution.field_variables(far_away_coords);\n  CHECK_ITERABLE_APPROX(\n      get<Xcts::Tags::ConformalFactor<DataVector>>(far_away_solution),\n      make_with_value<Scalar<DataVector>>(far_away_coords, 1.));\n\n  const auto created_solution =\n      TestHelpers::test_creation<Xcts::Solutions::ConstantDensityStar>(options);\n  CHECK(created_solution == solution);\n  test_serialization(solution);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Xcts.ConstantDensityStar\",\n    \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/Xcts\"};\n  test_solution(0.01, 1.,\n                \"Density: 0.01\\n\"\n                \"Radius: 1.\\n\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/Test_Flatness.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <utility>\n\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Flatness.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n\nnamespace Xcts::Solutions {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Xcts.Flatness\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::analytic_data::AnalyticSolution, Flatness>(\"Flatness\");\n  REQUIRE(dynamic_cast<const Flatness*>(created.get()) != nullptr);\n  const auto& solution = dynamic_cast<const Flatness&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    test_copy_semantics(solution);\n    auto move_solution = solution;\n    test_move_semantics(std::move(move_solution), solution);\n  }\n  {\n    INFO(\"Verify the solution solves the XCTS equations\");\n    TestHelpers::Xcts::Solutions::verify_solution<Xcts::Geometry::FlatCartesian,\n                                                  0>(solution, {{0., 0., 0.}},\n                                                     0., 1., 1.e-13);\n  }\n}\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/Test_HarmonicSchwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n#include <utility>\n\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n\nnamespace Xcts::Solutions {\nnamespace {\n\nusing HarmonicSchwarzschild = WrappedGr<gr::Solutions::HarmonicSchwarzschild>;\n\nvoid test_solution(const double mass, const std::array<double, 3>& center,\n                   const std::string& options_string) {\n  CAPTURE(mass);\n  CAPTURE(center);\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::analytic_data::AnalyticSolution, HarmonicSchwarzschild>(\n      options_string);\n  REQUIRE(dynamic_cast<const HarmonicSchwarzschild*>(created.get()) != nullptr);\n  const auto& solution = dynamic_cast<const HarmonicSchwarzschild&>(*created);\n  {\n    INFO(\"Properties\");\n    CHECK(solution.gr_solution().mass() == mass);\n    CHECK(solution.gr_solution().center() == center);\n  }\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    test_copy_semantics(solution);\n    auto move_solution = solution;\n    test_move_semantics(std::move(move_solution), solution);\n  }\n  {\n    INFO(\"Verify the solution solves the XCTS system\");\n    TestHelpers::Xcts::Solutions::verify_solution<Xcts::Geometry::Curved, 0>(\n        solution, center, 2. * mass, 6. * mass, 1.e-5);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Xcts.HarmonicSchwarzschild\",\n    \"[PointwiseFunctions][Unit]\") {\n  test_solution(0.43, {{1., 2., 3.}},\n                \"HarmonicSchwarzschild:\\n\"\n                \"  Mass: 0.43\\n\"\n                \"  Center: [1., 2., 3.]\");\n}\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/Test_Kerr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n#include <utility>\n\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::Solutions {\nnamespace {\n\nusing Kerr = WrappedGr<gr::Solutions::KerrSchild>;\n\nvoid test_solution(const double mass, const std::array<double, 3> spin,\n                   const std::array<double, 3>& center,\n                   const std::string& options_string) {\n  CAPTURE(mass);\n  CAPTURE(spin);\n  CAPTURE(center);\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::analytic_data::AnalyticSolution, Kerr>(options_string);\n  REQUIRE(dynamic_cast<const Kerr*>(created.get()) != nullptr);\n  const auto& solution = dynamic_cast<const Kerr&>(*created);\n  {\n    INFO(\"Properties\");\n    CHECK(solution.gr_solution().mass() == mass);\n    CHECK(solution.gr_solution().dimensionless_spin() == spin);\n    CHECK(solution.gr_solution().center() == center);\n  }\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    test_copy_semantics(solution);\n    auto move_solution = solution;\n    test_move_semantics(std::move(move_solution), solution);\n  }\n  {\n    INFO(\"Verify the solution solves the XCTS system\");\n    TestHelpers::Xcts::Solutions::verify_solution<Xcts::Geometry::Curved, 0>(\n        solution, center, 2. * mass, 6. * mass, 1.e-5);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Xcts.Kerr\",\n                  \"[PointwiseFunctions][Unit]\") {\n  test_solution(0.43, {{0.1, 0.2, 0.3}}, {{1., 2., 3.}},\n                \"KerrSchild:\\n\"\n                \"  Mass: 0.43\\n\"\n                \"  Spin: [0.1, 0.2, 0.3]\\n\"\n                \"  Center: [1., 2., 3.]\\n\"\n                \"  Velocity: [0., 0., 0.]\");\n}\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/Test_RotatingStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/RotatingStar.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n\nnamespace Xcts::Solutions {\n\n// [[Timeout, 20]]\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Xcts.RotatingStar\",\n                  \"[PointwiseFunctions][Unit]\") {\n  register_derived_classes_with_charm<\n      EquationsOfState::EquationOfState<true, 1>>();\n  using RotatingStar = WrappedGrMhd<RelativisticEuler::Solutions::RotatingStar>;\n  const std::string rotns_filename = unit_test_src_path() +\n                                     \"/PointwiseFunctions/AnalyticSolutions/\"\n                                     \"RelativisticEuler/RotatingStarId.dat\";\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::analytic_data::AnalyticSolution, RotatingStar>(\n      \"RotatingStar:\\n\"\n      \"  RotNsFilename: \" +\n      rotns_filename +\n      \"\\n\"\n      \"  PolytropicConstant: 100.\\n\");\n  REQUIRE(dynamic_cast<const RotatingStar*>(created.get()) != nullptr);\n  const auto& solution = dynamic_cast<const RotatingStar&>(*created);\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    test_copy_semantics(solution);\n    auto move_solution = solution;\n    test_move_semantics(std::move(move_solution), solution);\n  }\n  {\n    INFO(\"Verify the solution solves the XCTS equations\");\n    const auto& equatorial_radius = solution.gr_solution().equatorial_radius();\n    CAPTURE(equatorial_radius);\n    for (const double fraction_of_star_radius : {0.2, 1., 1.5}) {\n      CAPTURE(fraction_of_star_radius);\n      const double inner_radius = fraction_of_star_radius * equatorial_radius;\n      const double outer_radius =\n          (fraction_of_star_radius + 0.01) * equatorial_radius;\n      CAPTURE(inner_radius);\n      CAPTURE(outer_radius);\n      TestHelpers::Xcts::Solutions::verify_solution<Xcts::Geometry::Curved, 0>(\n          solution, {{0., 0., 0.}}, inner_radius, outer_radius, 2.e-3);\n    }\n  }\n}\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/Test_Schwarzschild.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::Solutions {\nnamespace {\n\nusing py_test_tags = tmpl::list<\n    Tags::ConformalFactor<DataVector>,\n    Tags::LapseTimesConformalFactor<DataVector>,\n    Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n    ::Tags::deriv<Tags::ConformalFactorMinusOne<DataVector>, tmpl::size_t<3>,\n                  Frame::Inertial>,\n    ::Tags::deriv<Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    Tags::ShiftStrain<DataVector, 3, Frame::Inertial>,\n    ::Tags::Flux<Tags::ConformalFactorMinusOne<DataVector>, tmpl::size_t<3>,\n                 Frame::Inertial>,\n    ::Tags::Flux<Tags::LapseTimesConformalFactorMinusOne<DataVector>,\n                 tmpl::size_t<3>, Frame::Inertial>,\n    Tags::LongitudinalShiftExcess<DataVector, 3, Frame::Inertial>,\n    Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n    Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n    ::Tags::deriv<Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    gr::Tags::TraceExtrinsicCurvature<DataVector>,\n    ::Tags::deriv<gr::Tags::TraceExtrinsicCurvature<DataVector>,\n                  tmpl::size_t<3>, Frame::Inertial>,\n    Tags::ShiftBackground<DataVector, 3, Frame::Inertial>,\n    Tags::LongitudinalShiftBackgroundMinusDtConformalMetric<DataVector, 3,\n                                                            Frame::Inertial>,\n    gr::Tags::Conformal<gr::Tags::EnergyDensity<DataVector>, 0>,\n    gr::Tags::Conformal<gr::Tags::StressTrace<DataVector>, 0>,\n    gr::Tags::Conformal<gr::Tags::MomentumDensity<DataVector, 3>, 0>,\n    gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n    Tags::ShiftDotDerivExtrinsicCurvatureTrace<DataVector>,\n    Tags::LongitudinalShiftMinusDtConformalMetricSquare<DataVector>,\n    Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<DataVector>>;\n\nstruct SchwarzschildProxy : Xcts::Solutions::Schwarzschild {\n  using Xcts::Solutions::Schwarzschild::Schwarzschild;\n  tuples::tagged_tuple_from_typelist<py_test_tags> py_test_variables(\n      const tnsr::I<DataVector, 3, Frame::Inertial>& x) const {\n    return Xcts::Solutions::Schwarzschild::variables(x, py_test_tags{});\n  }\n};\n\nvoid test_solution(const double mass,\n                   const Xcts::Solutions::SchwarzschildCoordinates coords,\n                   const double expected_radius_at_horizon,\n                   const double expected_conformal_factor_at_horizon,\n                   const std::string& py_module,\n                   const std::string& options_string) {\n  CAPTURE(mass);\n  CAPTURE(coords);\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::analytic_data::AnalyticSolution, Schwarzschild>(options_string);\n  REQUIRE(dynamic_cast<const Schwarzschild*>(created.get()) != nullptr);\n  const auto& solution = dynamic_cast<const Schwarzschild&>(*created);\n  {\n    INFO(\"Properties\");\n    CHECK(solution.mass() == mass);\n    CHECK(solution.radius_at_horizon() == approx(expected_radius_at_horizon));\n  }\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    test_copy_semantics(solution);\n    auto move_solution = solution;\n    test_move_semantics(std::move(move_solution), solution);\n  }\n  {\n    INFO(\"Conformal factor at horizon\");\n    // Also test retrieving doubles, instead of DataVectors\n    const auto conformal_factor_at_horizon =\n        get<Xcts::Tags::ConformalFactor<double>>(solution.variables(\n            tnsr::I<double, 3>{{{solution.radius_at_horizon(), 0., 0.}}},\n            tmpl::list<Xcts::Tags::ConformalFactor<double>>{}));\n    CHECK(get(conformal_factor_at_horizon) ==\n          approx(expected_conformal_factor_at_horizon));\n  }\n  {\n    INFO(\"Random-value tests\");\n    const SchwarzschildProxy solution_proxy{mass, coords};\n    const double inner_radius = expected_radius_at_horizon;\n    const double outer_radius = 3. * expected_radius_at_horizon;\n    pypp::check_with_random_values<1>(\n        &SchwarzschildProxy::py_test_variables, solution_proxy, py_module,\n        {\"conformal_factor\",\n         \"lapse_times_conformal_factor\",\n         \"shift\",\n         \"conformal_factor_gradient\",\n         \"lapse_times_conformal_factor_gradient\",\n         \"shift_strain\",\n         \"conformal_factor_gradient\",\n         \"lapse_times_conformal_factor_gradient\",\n         \"longitudinal_shift\",\n         \"conformal_metric\",\n         \"inv_conformal_metric\",\n         \"deriv_conformal_metric\",\n         \"extrinsic_curvature_trace\",\n         \"extrinsic_curvature_trace_gradient\",\n         \"shift_background\",\n         \"longitudinal_shift_background_minus_dt_conformal_metric\",\n         \"energy_density\",\n         \"stress_trace\",\n         \"momentum_density\",\n         \"lapse\",\n         \"shift\",\n         \"shift_dot_extrinsic_curvature_trace_gradient\",\n         \"longitudinal_shift_minus_dt_conformal_metric_square\",\n         \"longitudinal_shift_minus_dt_conformal_metric_over_lapse_square\"},\n        {{{inner_radius, outer_radius}}}, std::make_tuple(mass), DataVector(3),\n        1e-8);\n  }\n  {\n    INFO(\"Verify the solution solves the XCTS equations\");\n    const double inner_radius = expected_radius_at_horizon;\n    const double outer_radius = 3. * expected_radius_at_horizon;\n    TestHelpers::Xcts::Solutions::verify_solution<Xcts::Geometry::FlatCartesian,\n                                                  0>(\n        solution, {{0., 0., 0.}}, inner_radius, outer_radius, 1.e-5);\n  }\n}\n\n}  // namespace\n\n// [[TimeOut, 15]]\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Xcts.Schwarzschild\",\n    \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/AnalyticSolutions/Xcts\"};\n  test_solution(1., SchwarzschildCoordinates::Isotropic, 0.5, 2.,\n                \"SchwarzschildIsotropic\",\n                \"Schwarzschild:\\n\"\n                \"  Mass: 1.\\n\"\n                \"  Coordinates: Isotropic\");\n  test_solution(0.8, SchwarzschildCoordinates::Isotropic, 0.4, 2.,\n                \"SchwarzschildIsotropic\",\n                \"Schwarzschild:\\n\"\n                \"  Mass: 0.8\\n\"\n                \"  Coordinates: Isotropic\");\n  test_solution(1., SchwarzschildCoordinates::PainleveGullstrand, 2., 1.,\n                \"SchwarzschildPainleveGullstrand\",\n                \"Schwarzschild:\\n\"\n                \"  Mass: 1.\\n\"\n                \"  Coordinates: PainleveGullstrand\");\n  test_solution(0.8, SchwarzschildCoordinates::PainleveGullstrand, 1.6, 1.,\n                \"SchwarzschildPainleveGullstrand\",\n                \"Schwarzschild:\\n\"\n                \"  Mass: 0.8\\n\"\n                \"  Coordinates: PainleveGullstrand\");\n  // For radius of horizon see Eq. (7.37) in https://arxiv.org/abs/gr-qc/0510016\n  test_solution(1., SchwarzschildCoordinates::KerrSchildIsotropic,\n                1.2727410334221052, 1.2535595643473059,\n                \"SchwarzschildKerrSchildIsotropic\",\n                \"Schwarzschild:\\n\"\n                \"  Mass: 1.\\n\"\n                \"  Coordinates: KerrSchildIsotropic\");\n  test_solution(0.8, SchwarzschildCoordinates::KerrSchildIsotropic,\n                1.2727410334221052 * 0.8, 1.2535595643473059,\n                \"SchwarzschildKerrSchildIsotropic\",\n                \"Schwarzschild:\\n\"\n                \"  Mass: 0.8\\n\"\n                \"  Coordinates: KerrSchildIsotropic\");\n  test_solution(1., SchwarzschildCoordinates::MaximalIsotropic,\n                0.7793271080557972, 1.601972683625847,\n                \"SchwarzschildMaximalIsotropic\",\n                \"Schwarzschild:\\n\"\n                \"  Mass: 1.\\n\"\n                \"  Coordinates: MaximalIsotropic\");\n  test_solution(0.8, SchwarzschildCoordinates::MaximalIsotropic,\n                0.7793271080557972 * 0.8, 1.601972683625847,\n                \"SchwarzschildMaximalIsotropic\",\n                \"Schwarzschild:\\n\"\n                \"  Mass: 0.8\\n\"\n                \"  Coordinates: MaximalIsotropic\");\n}\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/Test_SphericalKerr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n#include <utility>\n\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/SphericalKerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/AnalyticSolution.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::Solutions {\nnamespace {\n\nusing SphericalKerr = WrappedGr<gr::Solutions::SphericalKerrSchild>;\n\nvoid test_solution(const double mass, const std::array<double, 3> spin,\n                   const std::array<double, 3>& center,\n                   const std::string& options_string) {\n  CAPTURE(mass);\n  CAPTURE(spin);\n  CAPTURE(center);\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::analytic_data::AnalyticSolution, SphericalKerr>(options_string);\n  REQUIRE(dynamic_cast<const SphericalKerr*>(created.get()) != nullptr);\n  const auto& solution = dynamic_cast<const SphericalKerr&>(*created);\n  {\n    INFO(\"Properties\");\n    CHECK(solution.gr_solution().mass() == mass);\n    CHECK(solution.gr_solution().dimensionless_spin() == spin);\n    CHECK(solution.gr_solution().center() == center);\n  }\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    test_copy_semantics(solution);\n    auto move_solution = solution;\n    test_move_semantics(std::move(move_solution), solution);\n  }\n  {\n    INFO(\"Verify the solution solves the XCTS system\");\n    TestHelpers::Xcts::Solutions::verify_solution<Xcts::Geometry::Curved, 0>(\n        solution, center, 2. * mass, 6. * mass, 1.e-5);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.AnalyticSolutions.Xcts.SphericalKerr\",\n    \"[PointwiseFunctions][Unit]\") {\n  test_solution(0.43, {{0.1, 0.2, 0.3}}, {{1., 2., 3.}},\n                \"SphericalKerrSchild:\\n\"\n                \"  Mass: 0.43\\n\"\n                \"  Spin: [0.1, 0.2, 0.3]\\n\"\n                \"  Center: [1., 2., 3.]\");\n}\n\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/Xcts/Test_TovStar.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Xcts/Geometry.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/RelativisticEuler/Tov.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/TovStar.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/RegisterDerivedWithCharm.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Xcts::Solutions {\nnamespace {\n\nusing TovCoordinates = RelativisticEuler::Solutions::TovCoordinates;\n\nvoid test_solution(const TovCoordinates coord_system) {\n  CAPTURE(coord_system);\n  EquationsOfState::register_derived_with_charm();\n  const auto created = TestHelpers::test_factory_creation<\n      elliptic::analytic_data::AnalyticSolution, TovStar>(\n      \"TovStar:\\n\"\n      \"  CentralDensity: 1.e-3\\n\"\n      \"  EquationOfState:\\n\"\n      \"    PolytropicFluid:\\n\"\n      \"      PolytropicExponent: 2\\n\"\n      \"      PolytropicConstant: 1.\\n\"\n      \"  Coordinates: \" +\n      get_output(coord_system));\n  REQUIRE(dynamic_cast<const TovStar*>(created.get()) != nullptr);\n  const auto& solution = dynamic_cast<const TovStar&>(*created);\n  {\n    INFO(\"Properties\");\n    const auto& radial_solution = solution.radial_solution();\n    CHECK(radial_solution.coordinate_system() == coord_system);\n  }\n  {\n    INFO(\"Semantics\");\n    test_serialization(solution);\n    test_copy_semantics(solution);\n    auto move_solution = solution;\n    test_move_semantics(std::move(move_solution), solution);\n  }\n  if (coord_system == TovCoordinates::Isotropic) {\n    INFO(\"Test exterior solution is Schwarzschild\");\n    const auto& radial_solution = solution.radial_solution();\n    const double star_radius = radial_solution.outer_radius();\n    const double total_mass = radial_solution.total_mass();\n    CAPTURE(star_radius);\n    CAPTURE(total_mass);\n    const Schwarzschild schwarzschild{total_mass,\n                                      SchwarzschildCoordinates::Isotropic};\n    // Look at a cube just outside the star\n    const Mesh<3> mesh{5, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n    const auto logical_coords = logical_coordinates(mesh);\n    const double dx = 0.1 * star_radius;\n    using AffineMap = domain::CoordinateMaps::Affine;\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    const domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                AffineMap3D>\n        coord_map{{{-1., 1., star_radius, star_radius + dx},\n                   {-1., 1., 0., dx},\n                   {-1., 1., 0., dx}}};\n    const auto x = coord_map(logical_coords);\n    const auto inv_jacobian = coord_map.inv_jacobian(logical_coords);\n    // Check the TOV solution is identical to the Schwarzschild solution\n    using all_fields = typename TovStar::template tags<DataVector>;\n    const auto tov_vars = variables_from_tagged_tuple(\n        solution.variables(x, mesh, inv_jacobian, all_fields{}));\n    const auto schwarzschild_vars = variables_from_tagged_tuple(\n        schwarzschild.variables(x, mesh, inv_jacobian, all_fields{}));\n    CHECK_VARIABLES_APPROX(tov_vars, schwarzschild_vars);\n  }\n  {\n    INFO(\"Verify the solution solves the XCTS equations\");\n    const auto& radial_solution = solution.radial_solution();\n    const double star_radius = radial_solution.outer_radius();\n    CAPTURE(star_radius);\n    for (const double fraction_of_star_radius : {0., 0.5, 1., 1.5}) {\n      CAPTURE(fraction_of_star_radius);\n      const double inner_radius = fraction_of_star_radius * star_radius;\n      const double outer_radius =\n          (fraction_of_star_radius + 0.01) * star_radius;\n      CAPTURE(inner_radius);\n      CAPTURE(outer_radius);\n      if (coord_system == TovCoordinates::Isotropic) {\n        TestHelpers::Xcts::Solutions::verify_solution<\n            Xcts::Geometry::FlatCartesian, 0>(\n            solution, {{0., 0., 0.}}, inner_radius, outer_radius, 1.e-4);\n      } else {\n        TestHelpers::Xcts::Solutions::verify_solution<Xcts::Geometry::Curved,\n                                                      0>(\n            solution, {{0., 0., 0.}}, inner_radius, outer_radius, 1.e-4);\n      }\n    }\n  }\n}\n\n// [[Timeout, 20]]\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Xcts.TovStar\",\n                  \"[PointwiseFunctions][Unit]\") {\n  test_solution(TovCoordinates::Schwarzschild);\n  test_solution(TovCoordinates::Isotropic);\n}\n\n}  // namespace\n}  // namespace Xcts::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/AnalyticSolutions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(AnalyticData)\nadd_subdirectory(AnalyticSolutions)\nadd_subdirectory(ConstraintDamping)\nadd_subdirectory(Elasticity)\nadd_subdirectory(GeneralRelativity)\nadd_subdirectory(Hydro)\nadd_subdirectory(InitialDataUtilities)\nadd_subdirectory(MathFunctions)\nadd_subdirectory(Punctures)\nadd_subdirectory(ScalarTensor)\nadd_subdirectory(SpecialRelativity)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ConstraintDamping\")\n\nset(LIBRARY_SOURCES\n  Test_Constant.cpp\n  Test_GaussianPlusConstant.cpp\n  Test_TimeDependentTripleGaussian.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstraintDampingHelpers\n  DataStructures\n  GeneralizedHarmonic\n  Parallel\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/Python/Constant.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef call_operator(coords, time, constant):\n    return constant\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/Python/ConstraintDampingHelpers.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef centered_coordinates(coords, center):\n    return coords - center\n\n\ndef squared_distance_from_center(centered_coords, center):\n    return np.einsum(\"i,i\", centered_coords, centered_coords)\n\n\ndef function_of_time(time):\n    a = [1.0, 0.2, 0.03, 0.004]\n    return (\n        a[0]\n        + a[1] * (time + 1.0)\n        + a[2] * np.square(time + 1.0)\n        + a[3] * (time + 1.0) * (time + 1.0) * (time + 1.0)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/Python/GaussianPlusConstant.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom ConstraintDampingHelpers import (\n    centered_coordinates,\n    squared_distance_from_center,\n)\n\n\ndef call_operator(coords, time, constant, amplitude, width, center):\n    one_over_width = 1.0 / width\n    distance_squared = squared_distance_from_center(\n        centered_coordinates(coords, center), center\n    )\n    return (\n        amplitude * np.exp(-1.0 * distance_squared * np.square(one_over_width))\n        + constant\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/Python/TimeDependentTripleGaussian.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom ConstraintDampingHelpers import function_of_time\nfrom GaussianPlusConstant import (\n    call_operator as GaussianPlusConstant_call_operator,\n)\n\n\ndef call_operator(\n    coords,\n    time,\n    constant,\n    amplitude_1,\n    width_1,\n    center_1,\n    amplitude_2,\n    width_2,\n    center_2,\n    amplitude_3,\n    width_3,\n    center_3,\n):\n    factor_scaling_widths = 1.0 / function_of_time(time)\n    return (\n        GaussianPlusConstant_call_operator(\n            coords,\n            time,\n            constant,\n            amplitude_1,\n            width_1 * factor_scaling_widths,\n            center_1,\n        )\n        + GaussianPlusConstant_call_operator(\n            coords,\n            time,\n            0.0,\n            amplitude_2,\n            width_2 * factor_scaling_widths,\n            center_2,\n        )\n        + GaussianPlusConstant_call_operator(\n            coords,\n            time,\n            0.0,\n            amplitude_3,\n            width_3 * factor_scaling_widths,\n            center_3,\n        )\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/Python/TimeDependentTripleGaussianObjectCenters.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom GaussianPlusConstant import (\n    call_operator as GaussianPlusConstant_call_operator,\n)\n\n\ndef call_operator(\n    coords,\n    time,\n    constant,\n    amplitude_1,\n    width_1,\n    unused_center_1,\n    amplitude_2,\n    width_2,\n    unused_center_2,\n    amplitude_3,\n    width_3,\n    center_3,\n):\n    center_1 = np.asarray([16.0 + time * -1.0e-3, 0.0, 0.0])\n    center_2 = np.asarray([-16.0 + time * 2.0e-3, 0.0, 0.0])\n    return (\n        GaussianPlusConstant_call_operator(\n            coords,\n            time,\n            constant,\n            amplitude_1,\n            width_1,\n            center_1,\n        )\n        + GaussianPlusConstant_call_operator(\n            coords,\n            time,\n            0.0,\n            amplitude_2,\n            width_2,\n            center_2,\n        )\n        + GaussianPlusConstant_call_operator(\n            coords,\n            time,\n            0.0,\n            amplitude_3,\n            width_3,\n            center_3,\n        )\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/Test_Constant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/ConstraintDamping/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/Constant.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim, typename DataType, typename Fr>\nvoid test_constant_random(const DataType& used_for_size) {\n  register_derived_classes_with_charm<\n      ConstraintDamping::Constant<VolumeDim, Fr>>();\n\n  // Generate the amplitude and width\n  MAKE_GENERATOR(gen);\n\n  const double value = std::uniform_real_distribution(-1.0, 1.0)(gen);\n\n  ConstraintDamping::Constant<VolumeDim, Fr> val{value};\n\n  TestHelpers::ConstraintDamping::check(std::move(val), \"Constant\",\n                                        used_for_size, {{{-1.0, 1.0}}},\n                                        {\"IgnoredFunctionOfTime\"}, value);\n\n  std::unique_ptr<ConstraintDamping::Constant<VolumeDim, Fr>> val_unique_ptr =\n      std::make_unique<ConstraintDamping::Constant<VolumeDim, Fr>>(value);\n\n  TestHelpers::ConstraintDamping::check(val_unique_ptr->get_clone(), \"Constant\",\n                                        used_for_size, {{{-1.0, 1.0}}},\n                                        {\"IgnoredFunctionOfTime\"}, value);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ConstraintDamp.Const\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const DataVector dv{5};\n\n  pypp::SetupLocalPythonEnvironment{\n      \"PointwiseFunctions/ConstraintDamping/Python\"};\n\n  using VolumeDims = tmpl::integral_list<size_t, 1, 2, 3>;\n  using Frames = tmpl::list<Frame::Grid, Frame::Inertial>;\n\n  tmpl::for_each<VolumeDims>([&dv](auto dim_v) {\n    using VolumeDim = typename decltype(dim_v)::type;\n    tmpl::for_each<Frames>([&dv](auto frame_v) {\n      using Fr = typename decltype(frame_v)::type;\n      test_constant_random<VolumeDim::value, DataVector, Fr>(dv);\n      test_constant_random<VolumeDim::value, double, Fr>(\n          std::numeric_limits<double>::signaling_NaN());\n    });\n  });\n\n  TestHelpers::test_creation<\n      std::unique_ptr<ConstraintDamping::DampingFunction<1, Frame::Inertial>>>(\n      \"Constant:\\n\"\n      \"  Value: 4.0\\n\");\n\n  const double value_3d{5.0};\n  const ConstraintDamping::Constant<3, Frame::Inertial> val_3d{value_3d};\n  const auto created_val = TestHelpers::test_creation<\n      ConstraintDamping::Constant<3, Frame::Inertial>>(\"Value: 5.0\\n\");\n  CHECK(created_val == val_3d);\n  const auto created_gh_damping_function = TestHelpers::test_creation<\n      std::unique_ptr<ConstraintDamping::DampingFunction<3, Frame::Inertial>>>(\n      \"Constant:\\n\"\n      \"  Value: 5.0\\n\");\n\n  test_serialization(val_3d);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/Test_GaussianPlusConstant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/ConstraintDamping/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/GaussianPlusConstant.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\ntemplate <size_t VolumeDim, typename DataType, typename Fr>\nvoid test_gaussian_plus_constant_random(const DataType& used_for_size) {\n  register_derived_classes_with_charm<\n      ConstraintDamping::GaussianPlusConstant<VolumeDim, Fr>>();\n\n  // Generate the amplitude and width\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1, 1);\n  std::uniform_real_distribution<> positive_dis(0, 1);\n\n  const double constant = real_dis(gen);\n  const double amplitude = positive_dis(gen);\n  // If the width is too small then the terms in the second derivative\n  // can become very large and fail the test due to rounding errors.\n  const double width = positive_dis(gen) + 0.5;\n\n  // Generate the center\n  std::array<double, VolumeDim> center{};\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    gsl::at(center, i) = real_dis(gen);\n  }\n\n  ConstraintDamping::GaussianPlusConstant<VolumeDim, Fr> gauss_plus_const{\n      constant, amplitude, width, center};\n\n  TestHelpers::ConstraintDamping::check(\n      std::move(gauss_plus_const), \"GaussianPlusConstant\", used_for_size,\n      {{{-1.0, 1.0}}}, {\"IgnoredFunctionOfTime\"}, constant, amplitude, width,\n      center);\n\n  std::unique_ptr<ConstraintDamping::GaussianPlusConstant<VolumeDim, Fr>>\n      gauss_plus_const_unique_ptr = std::make_unique<\n          ConstraintDamping::GaussianPlusConstant<VolumeDim, Fr>>(\n          constant, amplitude, width, center);\n\n  TestHelpers::ConstraintDamping::check(\n      gauss_plus_const_unique_ptr->get_clone(), \"GaussianPlusConstant\",\n      used_for_size, {{{-1.0, 1.0}}}, {\"IgnoredFunctionOfTime\"}, constant,\n      amplitude, width, center);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ConstraintDamp.GaussPlusConst\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const DataVector dv{5};\n\n  pypp::SetupLocalPythonEnvironment{\n      \"PointwiseFunctions/ConstraintDamping/Python\"};\n\n  using VolumeDims = tmpl::integral_list<size_t, 1, 2, 3>;\n  using Frames = tmpl::list<Frame::Grid, Frame::Inertial>;\n\n  tmpl::for_each<VolumeDims>([&dv](auto dim_v) {\n    using VolumeDim = typename decltype(dim_v)::type;\n    tmpl::for_each<Frames>([&dv](auto frame_v) {\n      using Fr = typename decltype(frame_v)::type;\n      test_gaussian_plus_constant_random<VolumeDim::value, DataVector, Fr>(dv);\n      test_gaussian_plus_constant_random<VolumeDim::value, double, Fr>(\n          std::numeric_limits<double>::signaling_NaN());\n    });\n  });\n\n  TestHelpers::test_creation<\n      std::unique_ptr<ConstraintDamping::DampingFunction<1, Frame::Inertial>>>(\n      \"GaussianPlusConstant:\\n\"\n      \"  Constant: 4.0\\n\"\n      \"  Amplitude: 3.0\\n\"\n      \"  Width: 2.0\\n\"\n      \"  Center: [-9.0]\");\n\n  const double constant_3d{5.0};\n  const double amplitude_3d{4.0};\n  const double width_3d{1.5};\n  const std::array<double, 3> center_3d{{1.1, -2.2, 3.3}};\n  const ConstraintDamping::GaussianPlusConstant<3, Frame::Inertial>\n      gauss_plus_const_3d{constant_3d, amplitude_3d, width_3d, center_3d};\n  const auto created_gauss_plus_const = TestHelpers::test_creation<\n      ConstraintDamping::GaussianPlusConstant<3, Frame::Inertial>>(\n      \"Constant: 5.0\\n\"\n      \"Amplitude: 4.0\\n\"\n      \"Width: 1.5\\n\"\n      \"Center: [1.1, -2.2, 3.3]\");\n  CHECK(created_gauss_plus_const == gauss_plus_const_3d);\n  const auto created_gauss_gh_damping_function = TestHelpers::test_creation<\n      std::unique_ptr<ConstraintDamping::DampingFunction<3, Frame::Inertial>>>(\n      \"GaussianPlusConstant:\\n\"\n      \"  Constant: 5.0\\n\"\n      \"  Amplitude: 4.0\\n\"\n      \"  Width: 1.5\\n\"\n      \"  Center: [1.1, -2.2, 3.3]\");\n\n  test_serialization(gauss_plus_const_3d);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ConstraintDamping/Test_TimeDependentTripleGaussian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/ConstraintDamping/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/TimeDependentTripleGaussian.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nvoid test_triple_gaussian_random(const DataType& used_for_size) {\n  register_derived_classes_with_charm<\n      ConstraintDamping::TimeDependentTripleGaussian>();\n\n  // Generate the amplitude and width\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1.0, 1.0);\n  std::uniform_real_distribution<> positive_dis(0.0, 1.0);\n\n  const double constant = real_dis(gen);\n\n  const double amplitude_1{positive_dis(gen)};\n  const double amplitude_2{positive_dis(gen)};\n  const double amplitude_3{positive_dis(gen)};\n\n  const double width_1{positive_dis(gen) + 0.5};\n  const double width_2{positive_dis(gen) + 0.5};\n  const double width_3{positive_dis(gen) + 0.5};\n\n  // Generate the center\n  std::array<double, 3> center_1{};\n  std::array<double, 3> center_2{};\n  std::array<double, 3> center_3{};\n  for (size_t i = 0; i < 3; ++i) {\n    gsl::at(center_1, i) = real_dis(gen);\n    gsl::at(center_2, i) = real_dis(gen);\n    gsl::at(center_3, i) = real_dis(gen);\n  }\n\n  // Name of FunctionOfTime to read. This must match up with the hard coded name\n  // in the TimeDependentTripleGaussian\n  const std::string function_of_time_for_scaling{\"Expansion\"s};\n\n  const ConstraintDamping::TimeDependentTripleGaussian triple_gauss{\n      constant, amplitude_1, width_1, center_1, amplitude_2,      width_2,\n      center_2, amplitude_3, width_3, center_3, \"ExpansionFactor\"};\n\n  TestHelpers::ConstraintDamping::check(\n      triple_gauss, \"TimeDependentTripleGaussian\", used_for_size,\n      {{{-1.0, 1.0}}}, {function_of_time_for_scaling}, constant, amplitude_1,\n      width_1, center_1, amplitude_2, width_2, center_2, amplitude_3, width_3,\n      center_3);\n\n  const std::unique_ptr<ConstraintDamping::TimeDependentTripleGaussian>\n      triple_gauss_unique_ptr =\n          std::make_unique<ConstraintDamping::TimeDependentTripleGaussian>(\n              constant, amplitude_1, width_1, center_1, amplitude_2, width_2,\n              center_2, amplitude_3, width_3, center_3, \"ExpansionFactor\");\n\n  TestHelpers::ConstraintDamping::check(\n      triple_gauss_unique_ptr->get_clone(), \"TimeDependentTripleGaussian\",\n      used_for_size, {{{-1.0, 1.0}}}, {function_of_time_for_scaling}, constant,\n      amplitude_1, width_1, center_1, amplitude_2, width_2, center_2,\n      amplitude_3, width_3, center_3);\n\n  const std::unique_ptr<ConstraintDamping::TimeDependentTripleGaussian>\n      triple_gauss_object_centers_unique_ptr =\n          std::make_unique<ConstraintDamping::TimeDependentTripleGaussian>(\n              constant, amplitude_1, width_1, std::nullopt, amplitude_2,\n              width_2, std::nullopt, amplitude_3, width_3, center_3,\n              \"ObjectCenters\");\n\n  REQUIRE(dynamic_cast<const ConstraintDamping::TimeDependentTripleGaussian&>(\n              *triple_gauss_object_centers_unique_ptr->get_clone()) ==\n          *triple_gauss_object_centers_unique_ptr);\n  TestHelpers::ConstraintDamping::check(\n      triple_gauss_object_centers_unique_ptr->get_clone(),\n      \"TimeDependentTripleGaussianObjectCenters\", used_for_size,\n      {{{-1.0, 1.0}}}, {\"GridCenters\"}, constant, amplitude_1, width_1,\n      center_1, amplitude_2, width_2, center_2, amplitude_3, width_3, center_3);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ConstraintDamp.TimeDep3Gauss\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const DataVector dv{5};\n\n  pypp::SetupLocalPythonEnvironment{\n      \"PointwiseFunctions/ConstraintDamping/Python\"};\n\n  test_triple_gaussian_random<DataVector>(dv);\n  test_triple_gaussian_random<double>(\n      std::numeric_limits<double>::signaling_NaN());\n\n  const double constant_3d{5.0};\n  const double amplitude_1_3d{4.0};\n  const double width_1_3d{1.5};\n  const std::array<double, 3> center_1_3d{{1.1, -2.2, 3.3}};\n  const double amplitude_2_3d{3.0};\n  const double width_2_3d{2.0};\n  const std::array<double, 3> center_2_3d{{4.4, -5.5, 6.6}};\n  const double amplitude_3_3d{5.0};\n  const double width_3_3d{1.0};\n  const std::array<double, 3> center_3_3d{{7.7, -8.8, 9.9}};\n\n  const ConstraintDamping::TimeDependentTripleGaussian triple_gauss_3d{\n      constant_3d,    amplitude_1_3d, width_1_3d,       center_1_3d,\n      amplitude_2_3d, width_2_3d,     center_2_3d,      amplitude_3_3d,\n      width_3_3d,     center_3_3d,    \"ExpansionFactor\"};\n  const auto created_triple_gauss = TestHelpers::test_creation<\n      ConstraintDamping::TimeDependentTripleGaussian>(\n      \"Constant: 5.0\\n\"\n      \"Gaussian1:\\n\"\n      \"  Amplitude: 4.0\\n\"\n      \"  Width: 1.5\\n\"\n      \"  Center: [1.1, -2.2, 3.3]\\n\"\n      \"Gaussian2:\\n\"\n      \"  Amplitude: 3.0\\n\"\n      \"  Width: 2.0\\n\"\n      \"  Center: [4.4, -5.5, 6.6]\\n\"\n      \"Gaussian3:\\n\"\n      \"  Amplitude: 5.0\\n\"\n      \"  Width: 1.0\\n\"\n      \"  Center: [7.7, -8.8, 9.9]\\n\"\n      \"MovementMethod: ExpansionFactor\\n\");\n  CHECK(created_triple_gauss == triple_gauss_3d);\n  CHECK_FALSE(created_triple_gauss != triple_gauss_3d);\n  const auto created_triple_gauss_gh_damping_function =\n      TestHelpers::test_creation<\n          std::unique_ptr<ConstraintDamping::DampingFunction<3, Frame::Grid>>>(\n          \"TimeDependentTripleGaussian:\\n\"\n          \"  Constant: 5.0\\n\"\n          \"  Gaussian1:\\n\"\n          \"    Amplitude: 4.0\\n\"\n          \"    Width: 1.5\\n\"\n          \"    Center: [1.1, -2.2, 3.3]\\n\"\n          \"  Gaussian2:\\n\"\n          \"    Amplitude: 3.0\\n\"\n          \"    Width: 2.0\\n\"\n          \"    Center: [4.4, -5.5, 6.6]\\n\"\n          \"  Gaussian3:\\n\"\n          \"    Amplitude: 5.0\\n\"\n          \"    Width: 1.0\\n\"\n          \"    Center: [7.7, -8.8, 9.9]\\n\"\n          \"  MovementMethod: ExpansionFactor\\n\");\n\n  test_serialization(triple_gauss_3d);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(ConstitutiveRelations)\n\nset(LIBRARY Test_ElasticityPointwiseFunctions)\n\nset(LIBRARY_SOURCES\n  Test_PotentialEnergy.cpp\n  Test_Strain.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstitutiveRelations\n  CoordinateMaps\n  DataStructures\n  Elasticity\n  ElasticityPointwiseFunctions\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/ConstitutiveRelations/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ConstitutiveRelations\")\n\nset(LIBRARY_SOURCES\n  Test_CubicCrystal.cpp\n  Test_IsotropicHomogeneous.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstitutiveRelations\n  DataStructures\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/ConstitutiveRelations/CubicCrystal.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef stress(strain, x, c_11, c_12, c_44):\n    return (\n        -(c_11 - c_12 - 2 * c_44) * np.diag(np.diag(strain))\n        - c_12 * np.identity(3) * np.trace(strain)\n        - 2.0 * c_44 * strain\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef stress(strain, x, bulk_modulus, shear_modulus):\n    dim = len(x)\n    krond = np.eye(dim)\n    strain_trace = np.trace(strain)\n    traceless_strain = strain - strain_trace / dim * krond\n    if dim == 3:\n        trace_factor = bulk_modulus\n    elif dim == 2:\n        trace_factor = (\n            9\n            * bulk_modulus\n            * shear_modulus\n            / (3 * bulk_modulus + 4 * shear_modulus)\n        )\n    else:\n        raise NotImplementedError\n    return (\n        -trace_factor * strain_trace * krond\n        - 2 * shear_modulus * traceless_strain\n    )\n\n\ndef youngs_modulus(bulk_modulus, shear_modulus):\n    return (\n        9.0\n        * bulk_modulus\n        * shear_modulus\n        / (3.0 * bulk_modulus + shear_modulus)\n    )\n\n\ndef poisson_ratio(bulk_modulus, shear_modulus):\n    return (3.0 * bulk_modulus - 2.0 * shear_modulus) / (\n        6.0 * bulk_modulus + 2.0 * shear_modulus\n    )\n\n\ndef lame_parameter(bulk_modulus, shear_modulus):\n    return bulk_modulus - 2 / 3.0 * shear_modulus\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/ConstitutiveRelations/Test_CubicCrystal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <random>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/CubicCrystal.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Elasticity::ConstitutiveRelations {\nnamespace {\n\nvoid test_semantics() {\n  INFO(\"Test type traits\");\n  const CubicCrystal relation{3., 2., 1.};\n  CHECK(relation == CubicCrystal{3., 2., 1.});\n  CHECK(relation != CubicCrystal{2., 2., 2.});\n  CHECK(relation != CubicCrystal{3., 0.2, 1.});\n  test_serialization(relation);\n  test_copy_semantics(relation);\n  const auto created_ptr =\n      TestHelpers::test_factory_creation<ConstitutiveRelation<3>, CubicCrystal>(\n          \"CubicCrystal:\\n\"\n          \"  C_11: 3.\\n\"\n          \"  C_12: 2.\\n\"\n          \"  C_44: 1.\\n\")\n          ->get_clone();\n  REQUIRE(dynamic_cast<const CubicCrystal*>(created_ptr.get()) != nullptr);\n  const auto& created_relation =\n      dynamic_cast<const CubicCrystal&>(*created_ptr);\n  CHECK(created_relation == relation);\n  CubicCrystal moved_relation{3., 2., 1.};\n  test_move_semantics(std::move(moved_relation), relation);\n}\n\nvoid test_consistency(const tnsr::ii<DataVector, 3>& random_strain,\n                      const tnsr::I<DataVector, 3>& random_inertial_coords) {\n  INFO(\"Consistency between CubicCrystal and IsotropicHomogeneous\");\n  const double youngs_modulus = 1.;\n  const double poisson_ratio = 1. / 3.;\n  const double isotropic_bulk_modulus =\n      youngs_modulus / (1. - 2. * poisson_ratio) / 3.;\n  const double isotropic_shear_modulus =\n      youngs_modulus / (1. + poisson_ratio) / 2.;\n  const IsotropicHomogeneous<3> isotropic_homogeneous_relation{\n      isotropic_bulk_modulus, isotropic_shear_modulus};\n  const double c_11 = youngs_modulus * (1. - poisson_ratio) /\n                      ((1. + poisson_ratio) * (1. - 2. * poisson_ratio));\n  const double c_12 = youngs_modulus * poisson_ratio /\n                      ((1. + poisson_ratio) * (1. - 2. * poisson_ratio));\n  const double c_44 = isotropic_shear_modulus;\n  // this should be isotropic homogeneous\n  const CubicCrystal cubic_crystalline_relation{c_11, c_12, c_44};\n  tnsr::II<DataVector, 3> cubic_crystalline_stress{\n      random_strain.begin()->size()};\n  cubic_crystalline_relation.stress(make_not_null(&cubic_crystalline_stress),\n                                    random_strain, random_inertial_coords);\n  tnsr::II<DataVector, 3> isotropic_homogeneous_stress{\n      random_strain.begin()->size()};\n  isotropic_homogeneous_relation.stress(\n      make_not_null(&isotropic_homogeneous_stress), random_strain,\n      random_inertial_coords);\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      CHECK_ITERABLE_APPROX(cubic_crystalline_stress.get(i, j),\n                            isotropic_homogeneous_stress.get(i, j));\n    }\n  }\n  // This relation should be the negative identity\n  const CubicCrystal relation{1., 0., 0.5};\n  relation.stress(make_not_null(&cubic_crystalline_stress), random_strain,\n                  random_inertial_coords);\n  for (size_t i = 0; i < 3; i++) {\n    for (size_t j = 0; j < 3; j++) {\n      CHECK_ITERABLE_APPROX(cubic_crystalline_stress.get(i, j),\n                            -random_strain.get(i, j));\n    }\n  }\n}\n\nvoid test_implementation(const double youngs_modulus,\n                         const double poisson_ratio, double shear_modulus) {\n  const CubicCrystal relation{youngs_modulus, poisson_ratio, shear_modulus};\n  pypp::check_with_random_values<1>(\n      static_cast<void (CubicCrystal::*)(\n          gsl::not_null<tnsr::II<DataVector, 3>*>,\n          const tnsr::ii<DataVector, 3>&, const tnsr::I<DataVector, 3>&) const>(\n          &CubicCrystal::stress),\n      relation, \"CubicCrystal\", {\"stress\"}, {{{-1., 1.}}},\n      std::tuple<double, double, double>{youngs_modulus, poisson_ratio,\n                                         shear_modulus},\n      DataVector(5));\n}\n\nvoid test_implementation_suite() {\n  INFO(\"Comparison to an independent Python implementation\");\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Elasticity/ConstitutiveRelations\");\n  test_implementation(3., 2., 1.);\n  // Values taken from:\n  // R. E. Newnham: Properties of materials. Oxford University Press, 2005, ISBN\n  // 978-0-19-852075-7 Diamond: c_11=1020, c_12=250, c_44=492\n  test_implementation(1020., 250., 492.);\n  // Silicon: c_11=166, c_12=64, c_44=80\n  test_implementation(166., 64., 80.);\n  // Germanium: c_11=130, c_12=49, c_44=67\n  test_implementation(130., 49., 67.);\n  // Lithium: c_11=13.5, c_12=11.4, c_44=8.8\n  test_implementation(13.5, 11.4, 8.8);\n  // Sodium: c_11=7.4, c_12=6.2, c_44=4.2\n  test_implementation(7.4, 6.2, 4.2);\n  // Potassium: c_11=3.7, c_12=3.1, c_44=1.9\n  test_implementation(3.7, 3.1, 1.9);\n  // NaCl: c_11=48.5, c_12=12.5, c_44=12.7\n  test_implementation(48.5, 12.5, 12.7);\n  // KCl: c_11=40.5, c_12=6.6, c_44=6.3\n  test_implementation(40.5, 6.6, 6.3);\n  // RbCl: c_11=36.3, c_12=6.2, c_44=4.7\n  test_implementation(36.3, 6.2, 4.7);\n}\n\nvoid test_analytically() {\n  INFO(\"Comparison to analytic expressions\");\n  // Generate random strain data\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_dist = make_not_null(&dist);\n  const DataVector used_for_size{10};\n  const auto random_strain = make_with_random_values<tnsr::ii<DataVector, 3>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto random_inertial_coords =\n      make_with_random_values<tnsr::I<DataVector, 3>>(nn_generator, nn_dist,\n                                                      used_for_size);\n\n  test_consistency(random_strain, random_inertial_coords);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elasticity.ConstitutiveRelations.CubicCrystal\",\n                  \"[PointwiseFunctions][Unit][Elasticity]\") {\n  {\n    INFO(\"CubicyCrystal\");\n    test_semantics();\n    test_analytically();\n    test_implementation_suite();\n  }\n}\n\n}  // namespace Elasticity::ConstitutiveRelations\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/ConstitutiveRelations/Test_IsotropicHomogeneous.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <pup.h>\n#include <random>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Elasticity::ConstitutiveRelations {\nnamespace {\n\ntemplate <size_t Dim>\nvoid test_type_traits() {\n  INFO(\"Test type traits\");\n  const IsotropicHomogeneous<Dim> relation{1., 2.};\n  CHECK(relation == IsotropicHomogeneous<Dim>{1., 2.});\n  CHECK(relation != IsotropicHomogeneous<Dim>{1., 1.});\n  CHECK(relation != IsotropicHomogeneous<Dim>{2., 2.});\n  test_serialization(relation);\n  test_copy_semantics(relation);\n  const auto created_ptr =\n      TestHelpers::test_factory_creation<ConstitutiveRelation<Dim>,\n                                         IsotropicHomogeneous<Dim>>(\n          \"IsotropicHomogeneous:\\n\"\n          \"  BulkModulus: 1.\\n\"\n          \"  ShearModulus: 2.\\n\")\n          ->get_clone();\n  REQUIRE(dynamic_cast<const IsotropicHomogeneous<Dim>*>(created_ptr.get()) !=\n          nullptr);\n  const auto& created_relation =\n      dynamic_cast<const IsotropicHomogeneous<Dim>&>(*created_ptr);\n  CHECK(created_relation == relation);\n  IsotropicHomogeneous<Dim> moved_relation{1., 2.};\n  test_move_semantics(std::move(moved_relation), relation);\n}\n\ntemplate <size_t Dim>\nvoid test_implementation(const double incompressibility,\n                         const double rigidity) {\n  const IsotropicHomogeneous<Dim> relation{incompressibility, rigidity};\n  pypp::check_with_random_values<1>(\n      static_cast<void (IsotropicHomogeneous<Dim>::*)(\n          gsl::not_null<tnsr::II<DataVector, Dim>*>,\n          const tnsr::ii<DataVector, Dim>&, const tnsr::I<DataVector, Dim>&)\n                      const>(&IsotropicHomogeneous<Dim>::stress),\n      relation, \"IsotropicHomogeneous\", {\"stress\"}, {{{-10.0, 10.0}}},\n      std::tuple<double, double>{incompressibility, rigidity}, DataVector(5));\n}\n\ntemplate <size_t Dim>\nvoid test_implementation_suite() {\n  INFO(\"Comparison to an independent Python implementation\");\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Elasticity/ConstitutiveRelations\");\n  test_implementation<Dim>(1., 1.);\n  // Values taken from:\n  // http://homepages.engineering.auckland.ac.nz/~pkel015/SolidMechanicsBooks/Part_I/BookSM_Part_I/06_LinearElasticity/06_Linear_Elasticity_Complete.pdf\n  // Iron: E=100, nu=0.29\n  test_implementation<Dim>(79.3651, 38.7597);\n  // Rubber: E=0.001, nu=0.4\n  test_implementation<Dim>(0.00166667, 0.000357143);\n  // Wood (fibre direction): E=17, nu=0.45\n  test_implementation<Dim>(56.6667, 5.86207);\n  // Wood (transverse direction): E=1, nu=0.79\n  test_implementation<Dim>(-0.574713, 0.27933);\n  // Parameters used for solving a Bowen-York momentum constraint:\n  test_implementation<Dim>(0., 1.);\n}\n\ntemplate <size_t Dim>\nvoid test_identity(const tnsr::ii<DataVector, Dim>& random_strain,\n                   const tnsr::I<DataVector, Dim>& random_inertial_coords) {\n  INFO(\"Identity\");\n  // This relation should be the negative identity\n  const IsotropicHomogeneous<Dim> relation{1. / 3., 1. / 2.};\n  tnsr::II<DataVector, Dim> stress{random_strain.begin()->size()};\n  relation.stress(make_not_null(&stress), random_strain,\n                  random_inertial_coords);\n  for (size_t i = 0; i < Dim; i++) {\n    for (size_t j = 0; j < Dim; j++) {\n      CHECK_ITERABLE_APPROX(stress.get(i, j), -random_strain.get(i, j));\n    }\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_trace(const tnsr::ii<DataVector, Dim>& random_strain,\n                const tnsr::I<DataVector, Dim>& random_inertial_coords);\n\ntemplate <>\nvoid test_trace<3>(const tnsr::ii<DataVector, 3>& random_strain,\n                   const tnsr::I<DataVector, 3>& random_inertial_coords) {\n  INFO(\"Trace\");\n  // This relation should result in a stress trace that is equal the negative\n  // trace of the strain. The shear modulus should be irrelevant here.\n  const IsotropicHomogeneous<3> relation{1. / 3., 10.};\n  tnsr::II<DataVector, 3> stress{random_strain.begin()->size()};\n  relation.stress(make_not_null(&stress), random_strain,\n                  random_inertial_coords);\n  auto strain_trace = make_with_value<DataVector>(random_strain, 0.);\n  auto stress_trace = make_with_value<DataVector>(random_strain, 0.);\n  for (size_t i = 0; i < 3; i++) {\n    strain_trace += random_strain.get(i, i);\n    stress_trace += stress.get(i, i);\n  }\n  CHECK_ITERABLE_APPROX(stress_trace, -strain_trace);\n}\n\ntemplate <>\nvoid test_trace<2>(const tnsr::ii<DataVector, 2>& random_strain,\n                   const tnsr::I<DataVector, 2>& random_inertial_coords) {\n  INFO(\"Trace\");\n  const IsotropicHomogeneous<2> relation{1. / 3., 2.};\n  tnsr::II<DataVector, 2> stress{random_strain.begin()->size()};\n  relation.stress(make_not_null(&stress), random_strain,\n                  random_inertial_coords);\n  auto strain_trace = make_with_value<DataVector>(random_strain, 0.);\n  auto stress_trace = make_with_value<DataVector>(random_strain, 0.);\n  for (size_t i = 0; i < 2; i++) {\n    strain_trace += random_strain.get(i, i);\n    stress_trace += stress.get(i, i);\n  }\n  // 2 (trace of delta_ij) * 9 * K * mu / (3 * K + 4 * mu) = 4 / 3\n  CHECK_ITERABLE_APPROX(stress_trace, -4. / 3. * strain_trace);\n}\n\ntemplate <size_t Dim>\nvoid test_traceless(const tnsr::ii<DataVector, Dim>& random_strain,\n                    const tnsr::I<DataVector, Dim>& random_inertial_coords) {\n  INFO(\"Traceless\");\n  // This relation should result in a traceless stress.\n  // The shear modulus should be irrelevant here.\n  const IsotropicHomogeneous<Dim> relation{0., 10.};\n  tnsr::II<DataVector, Dim> stress{random_strain.begin()->size()};\n  relation.stress(make_not_null(&stress), random_strain,\n                  random_inertial_coords);\n  auto trace = make_with_value<DataVector>(random_strain, 0.);\n  for (size_t i = 0; i < Dim; i++) {\n    trace += stress.get(i, i);\n  }\n  CHECK_ITERABLE_APPROX(trace, make_with_value<DataVector>(trace, 0.));\n}\n\ntemplate <size_t Dim>\nvoid test_analytically() {\n  INFO(\"Comparison to analytic expressions\");\n  // Generate random strain data\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_dist = make_not_null(&dist);\n  const DataVector used_for_size{10};\n  const auto random_strain = make_with_random_values<tnsr::ii<DataVector, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  auto random_strain_trace = make_with_value<DataVector>(used_for_size, 0.);\n  for (size_t i = 0; i < Dim; i++) {\n    random_strain_trace += random_strain.get(i, i);\n  }\n  const auto random_inertial_coords =\n      make_with_random_values<tnsr::I<DataVector, Dim>>(nn_generator, nn_dist,\n                                                        used_for_size);\n\n  test_identity<Dim>(random_strain, random_inertial_coords);\n  test_trace<Dim>(random_strain, random_inertial_coords);\n  test_traceless<Dim>(random_strain, random_inertial_coords);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Elasticity.ConstitutiveRelations.IsotropicHomogeneous\",\n                  \"[PointwiseFunctions][Unit][Elasticity]\") {\n  {\n    INFO(\"3D\");\n    test_type_traits<3>();\n    test_implementation_suite<3>();\n    test_analytically<3>();\n  }\n  {\n    INFO(\"2D\");\n    test_type_traits<2>();\n    test_implementation_suite<2>();\n    test_analytically<2>();\n  }\n}\n\n}  // namespace Elasticity::ConstitutiveRelations\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/ConstitutiveRelations/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp\"\n\nnamespace Elasticity::ConstitutiveRelations {\n\nSPECTRE_TEST_CASE(\"Unit.Elasticity.ConstitutiveRelations.Tags\",\n                  \"[Unit][Elasticity]\") {\n  TestHelpers::db::test_simple_tag<Tags::ConstitutiveRelation<3>>(\n      \"ConstitutiveRelation\");\n}\n\n}  // namespace Elasticity::ConstitutiveRelations\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/ConstitutiveRelations/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/PotentialEnergy.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom Elasticity.ConstitutiveRelations.IsotropicHomogeneous import stress\n\n\ndef potential_energy_density(strain, coordinates, bulk_modulus, shear_modulus):\n    local_stress = stress(strain, coordinates, bulk_modulus, shear_modulus)\n    return -0.5 * np.einsum(\"ij, ij\", strain, local_stress)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/Strain.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef strain_curved(\n    deriv_displacement,\n    metric,\n    deriv_metric,\n    christoffel_first_kind,\n    displacement,\n):\n    deriv_displacement_lo = np.einsum(\n        \"jk,ik->ij\", metric, deriv_displacement\n    ) + np.einsum(\"k,ijk->ij\", displacement, deriv_metric)\n    return 0.5 * (\n        deriv_displacement_lo + np.transpose(deriv_displacement_lo)\n    ) - np.einsum(\"kij,k\", christoffel_first_kind, displacement)\n\n\ndef strain_flat(deriv_displacement):\n    dim = len(deriv_displacement)\n    return strain_curved(\n        deriv_displacement,\n        metric=np.identity(dim),\n        deriv_metric=np.zeros((dim, dim, dim)),\n        christoffel_first_kind=np.zeros((dim, dim, dim)),\n        displacement=np.zeros((dim)),\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/Test_PotentialEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/IsotropicHomogeneous.hpp\"\n#include \"PointwiseFunctions/Elasticity/ConstitutiveRelations/Tags.hpp\"\n#include \"PointwiseFunctions/Elasticity/PotentialEnergy.hpp\"\n#include \"PointwiseFunctions/Elasticity/Stress.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <size_t Dim>\nScalar<DataVector> potential_energy_density(\n    // The wrapper constructs a constitutive relation for use in\n    // check_with_random_values.\n    const tnsr::ii<DataVector, Dim, Frame::Inertial>& strain,\n    const tnsr::I<DataVector, Dim>& coordinates, const double bulk_modulus,\n    const double shear_modulus) {\n  Elasticity::ConstitutiveRelations::IsotropicHomogeneous<Dim>\n      constitutive_relation{bulk_modulus, shear_modulus};\n  return Elasticity::potential_energy_density<Dim>(\n      strain, coordinates, std::move(constitutive_relation));\n}\n\ntemplate <size_t Dim>\nvoid test_elastic_potential_energy(const DataVector& used_for_size) {\n  pypp::check_with_random_values<4>(\n      &potential_energy_density<Dim>, \"Elasticity.PotentialEnergy\",\n      \"potential_energy_density\", {{{-1., 1.}, {0., 1.}, {0., 1.}, {0., 1.}}},\n      used_for_size);\n}\n\ntemplate <size_t Dim>\nvoid test_compute_tags(const DataVector& used_for_size) {\n  CAPTURE(Dim);\n  using strain_tag = Elasticity::Tags::Strain<Dim>;\n  using energy_tag = Elasticity::Tags::PotentialEnergyDensity<Dim>;\n  using constitutive_relation_tag = Elasticity::Tags::ConstitutiveRelation<Dim>;\n  using coordinates_tag = domain::Tags::Coordinates<Dim, Frame::Inertial>;\n  TestHelpers::db::test_compute_tag<Elasticity::Tags::StressCompute<Dim>>(\n      \"Stress\");\n  TestHelpers::db::test_compute_tag<\n      Elasticity::Tags::PotentialEnergyDensityCompute<Dim>>(\n      \"PotentialEnergyDensity\");\n  const size_t num_points = used_for_size.size();\n  {\n    INFO(\"Energy\");\n    std::unique_ptr<\n        Elasticity::ConstitutiveRelations::ConstitutiveRelation<Dim>>\n        constitutive_relation = std::make_unique<\n            Elasticity::ConstitutiveRelations::IsotropicHomogeneous<Dim>>(3.,\n                                                                          4.);\n    const auto box =\n        db::create<db::AddSimpleTags<strain_tag, constitutive_relation_tag,\n                                     coordinates_tag>,\n                   db::AddComputeTags<\n                       Elasticity::Tags::StressCompute<Dim>,\n                       Elasticity::Tags::PotentialEnergyDensityCompute<Dim>>>(\n            tnsr::ii<DataVector, Dim>{num_points, 1.},\n            std::move(constitutive_relation),\n            tnsr::I<DataVector, Dim>{num_points, 2.});\n\n    const auto expected_energy = Elasticity::potential_energy_density<Dim>(\n        get<strain_tag>(box), get<coordinates_tag>(box),\n        get<constitutive_relation_tag>(box));\n    CHECK(get<energy_tag>(box) == expected_energy);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Elasticity.PotentialEnergy\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"PointwiseFunctions\"};\n  GENERATE_UNINITIALIZED_DATAVECTOR;\n  CHECK_FOR_DATAVECTORS(test_elastic_potential_energy, (2, 3));\n  CHECK_FOR_DATAVECTORS(test_compute_tags, (2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/Test_Strain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Elliptic/Systems/Elasticity/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"PointwiseFunctions/Elasticity/Strain.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\n// Polynomial functions of max. degree 2, so the differentiation is exact on 3\n// grid points\ntemplate <size_t Dim>\ntnsr::I<DataVector, Dim> polynomial_displacement(\n    const tnsr::I<DataVector, Dim>& x) {\n  tnsr::I<DataVector, Dim> displacement{x.begin()->size()};\n  get<0>(displacement) = square(get<0>(x)) + 2. * get<1>(x);\n  get<1>(displacement) = square(get<1>(x)) + 3. * get<0>(x);\n  if constexpr (Dim == 3) {\n    get<2>(displacement) = square(get<2>(x)) + get<0>(x) + 4. * get<1>(x);\n  }\n  return displacement;\n}\n\n// This is the symmetrized gradient of `polynomial_displacement`\ntemplate <size_t Dim>\ntnsr::ii<DataVector, Dim> polynomial_strain(const tnsr::I<DataVector, Dim>& x) {\n  tnsr::ii<DataVector, Dim> strain{x.begin()->size()};\n  get<0, 0>(strain) = 2. * get<0>(x);\n  get<1, 1>(strain) = 2. * get<1>(x);\n  get<0, 1>(strain) = 2.5;\n  if constexpr (Dim == 3) {\n    get<2, 2>(strain) = 2. * get<2>(x);\n    get<0, 2>(strain) = 0.5;\n    get<1, 2>(strain) = 2.;\n  }\n  return strain;\n}\n\ntemplate <size_t Dim>\nauto make_coord_map() {\n  using AffineMap = domain::CoordinateMaps::Affine;\n  if constexpr (Dim == 1) {\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap>{{-1., 1., 0., M_PI}};\n  } else if constexpr (Dim == 2) {\n    using AffineMap2D =\n        domain::CoordinateMaps::ProductOf2Maps<AffineMap, AffineMap>;\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap2D>{\n        {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}}};\n  } else {\n    using AffineMap3D =\n        domain::CoordinateMaps::ProductOf3Maps<AffineMap, AffineMap, AffineMap>;\n    return domain::CoordinateMap<Frame::ElementLogical, Frame::Inertial,\n                                 AffineMap3D>{\n        {{-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}, {-1., 1., 0., M_PI}}};\n  }\n}\n\ntemplate <size_t Dim>\nvoid test_strain() {\n  CAPTURE(Dim);\n  {\n    INFO(\"Random-value tests\");\n    DataVector used_for_size{5};\n    pypp::check_with_random_values<1>(\n        static_cast<void (*)(gsl::not_null<tnsr::ii<DataVector, Dim>*>,\n                             const tnsr::iJ<DataVector, Dim>&)>(\n            &Elasticity::strain<DataVector, Dim>),\n        \"Strain\", {\"strain_flat\"}, {{{-1., 1.}}}, used_for_size);\n    pypp::check_with_random_values<1>(\n        static_cast<void (*)(gsl::not_null<tnsr::ii<DataVector, Dim>*>,\n                             const tnsr::iJ<DataVector, Dim>&,\n                             const tnsr::ii<DataVector, Dim>&,\n                             const tnsr::ijj<DataVector, Dim>&,\n                             const tnsr::ijj<DataVector, Dim>&,\n                             const tnsr::I<DataVector, Dim>&)>(\n            &Elasticity::strain<DataVector, Dim>),\n        \"Strain\", {\"strain_curved\"}, {{{-1., 1.}}}, used_for_size);\n  }\n  const Mesh<Dim> mesh{3, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto logical_coords = logical_coordinates(mesh);\n  const auto coord_map = make_coord_map<Dim>();\n  const auto inertial_coords = coord_map(logical_coords);\n  const auto inv_jacobian = coord_map.inv_jacobian(logical_coords);\n  const auto displacement = polynomial_displacement(inertial_coords);\n  const auto expected_strain = polynomial_strain(inertial_coords);\n  tnsr::ii<DataVector, Dim> strain{mesh.number_of_grid_points()};\n  Elasticity::strain(make_not_null(&strain), displacement, mesh, inv_jacobian);\n  for (size_t i = 0; i < strain.size(); ++i) {\n    const auto component_name =\n        strain.component_name(strain.get_tensor_index(i));\n    CAPTURE(component_name);\n    CHECK_ITERABLE_APPROX(strain[i], expected_strain[i]);\n  }\n  {\n    INFO(\"Test the compute tag\");\n    TestHelpers::db::test_compute_tag<Elasticity::Tags::StrainCompute<Dim>>(\n        \"Strain\");\n    const auto box = db::create<\n        db::AddSimpleTags<Elasticity::Tags::Displacement<Dim>,\n                          domain::Tags::Mesh<Dim>,\n                          domain::Tags::InverseJacobian<\n                              Dim, Frame::ElementLogical, Frame::Inertial>>,\n        db::AddComputeTags<Elasticity::Tags::StrainCompute<Dim>>>(\n        displacement, mesh, inv_jacobian);\n    CHECK(get<Elasticity::Tags::Strain<Dim>>(box) == strain);\n  }\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Elasticity.Strain\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Elasticity\"};\n  test_strain<2>();\n  test_strain<3>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Elasticity/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GeneralRelativity\")\n\nset(LIBRARY_SOURCES\n  Test_Christoffel.cpp\n  Test_ComputeGhQuantities.cpp\n  Test_ComputeSpacetimeQuantities.cpp\n  Test_CurvatureScalarComputeTags.cpp\n  Test_CurvatureScalars.cpp\n  Test_GeodesicAcceleration.cpp\n  Test_GeodesicEquation.cpp\n  Test_InterfaceNullNormal.cpp\n  Test_KerrHorizon.cpp\n  Test_KerrSchildCoords.cpp\n  Test_ProjectionOperators.cpp\n  Test_Psi4.cpp\n  Test_Ricci.cpp\n  Test_SpacetimeDerivativeOfGothG.cpp\n  Test_Tags.cpp\n  Test_TortoiseCoordinates.cpp\n  Test_WeylElectric.cpp\n  Test_WeylMagnetic.cpp\n  Test_WeylPropagating.cpp\n  Test_WeylTypeD1.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataBoxTestHelpers\n  DataStructures\n  DataStructuresHelpers\n  Domain\n  DomainStructure\n  GeneralizedHarmonic\n  GeneralRelativity\n  GeneralRelativityHelpers\n  LinearOperators\n  Spectral\n  Utilities\n)\n\nadd_subdirectory(Python)\nadd_subdirectory(Surfaces)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Christoffel.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef christoffel_first_kind(d_metric):\n    dim = d_metric.shape[0]\n    return 0.5 * np.array(\n        [\n            [\n                [\n                    d_metric[b, c, a] + d_metric[a, c, b] - d_metric[c, a, b]\n                    for b in range(dim)\n                ]\n                for a in range(dim)\n            ]\n            for c in range(dim)\n        ]\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/ComputeGhQuantities.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\nfrom .ComputeSpacetimeQuantities import (\n    dt_spacetime_metric,\n    spatial_deriv_spacetime_metric,\n)\n\n\ndef phi(\n    lapse, deriv_lapse, shift, deriv_shift, spatial_metric, deriv_spatial_metric\n):\n    return spatial_deriv_spacetime_metric(\n        lapse,\n        deriv_lapse,\n        shift,\n        deriv_shift,\n        spatial_metric,\n        deriv_spatial_metric,\n    )\n\n\ndef pi(\n    lapse, dt_lapse, shift, dt_shift, spatial_metric, dt_spatial_metric, phi\n):\n    return (\n        np.einsum(\"iab,i\", phi, shift)\n        - dt_spacetime_metric(\n            lapse, dt_lapse, shift, dt_shift, spatial_metric, dt_spatial_metric\n        )\n    ) / lapse\n\n\ndef gauge_source(\n    lapse,\n    dt_lapse,\n    deriv_lapse,\n    shift,\n    dt_shift,\n    deriv_shift,\n    spatial_metric,\n    tr_extrinsic_curvature,\n    trace_christoffel_last_indices,\n):\n    dim = shift.size\n    source = np.zeros(dim + 1)\n    shift_dot_d_shift = np.einsum(\"k,ki\", shift, deriv_shift)\n    inv_lapse = 1.0 / lapse\n    source[1:] = (\n        inv_lapse**2\n        * np.einsum(\"ij,j\", spatial_metric, dt_shift - shift_dot_d_shift)\n        + deriv_lapse / lapse\n        - trace_christoffel_last_indices\n    )\n    source[0] = (\n        -dt_lapse * inv_lapse\n        + inv_lapse * np.dot(shift, deriv_lapse)\n        + np.dot(shift, source[1:])\n        - lapse * tr_extrinsic_curvature\n    )\n    return source\n\n\ndef deriv_lapse(lapse, spacetime_unit_normal, phi):\n    t1 = np.einsum(\"iab,b->ia\", phi, spacetime_unit_normal)\n    t1 = np.einsum(\"ia,a->i\", t1, spacetime_unit_normal)\n    return -0.5 * lapse * t1\n\n\ndef dt_lapse(lapse, shift, spacetime_unit_normal, phi, pi):\n    t1 = np.einsum(\"ab,b->a\", pi, spacetime_unit_normal)\n    t1 = np.einsum(\"a,a\", t1, spacetime_unit_normal)\n    t1 *= lapse\n    t2 = np.einsum(\"iab,b->ia\", phi, spacetime_unit_normal)\n    t2 = np.einsum(\"ia,a->i\", t2, spacetime_unit_normal)\n    t2 = np.einsum(\"i,i\", t2, shift)\n    return 0.5 * lapse * (t1 - t2)\n\n\ndef deriv_shift(lapse, inverse_spacetime_metric, spacetime_unit_normal, phi):\n    t1 = np.einsum(\"b,iab->ia\", spacetime_unit_normal, phi)\n    t1 = np.einsum(\"ja,ia->ij\", inverse_spacetime_metric[1:, :], t1) * lapse\n    t2 = np.einsum(\"a,iab->ib\", spacetime_unit_normal, phi)\n    t2 = np.einsum(\"b,ib->i\", spacetime_unit_normal, t2)\n    t2 = np.einsum(\"i,j->ij\", t2, spacetime_unit_normal[1:]) * lapse\n    return t1 + t2\n\n\ndef dt_shift(\n    lapse, shift, inverse_spatial_metric, spacetime_unit_normal, phi, pi\n):\n    t1 = np.einsum(\"a,ja->j\", spacetime_unit_normal, pi[1:, :])\n    t1 = np.einsum(\"j,ij->i\", t1, inverse_spatial_metric)\n    t2 = np.einsum(\"jka,a->jk\", phi[:, 1:, :], spacetime_unit_normal)\n    t2 = np.einsum(\"jk,ik->ji\", t2, inverse_spatial_metric)\n    t2 = np.einsum(\"ji,j->i\", t2, shift)\n    return lapse * (t2 - lapse * t1)\n\n\ndef dt_lower_shift(\n    lapse, shift, spatial_metric, spacetime_unit_normal, phi, pi\n):\n    inverse_spatial_metric = np.linalg.inv(spatial_metric)\n    dt_shift_ = dt_shift(\n        lapse, shift, inverse_spatial_metric, spacetime_unit_normal, phi, pi\n    )\n    t1 = np.einsum(\"ij,j->i\", spatial_metric, dt_shift_)\n    dt_spatial_metric = (-lapse * pi + np.einsum(\"k,kab->ab\", shift, phi))[\n        1:, 1:\n    ]\n    t2 = np.einsum(\"j,ij->i\", shift, dt_spatial_metric)\n    return t1 + t2\n\n\ndef spacetime_deriv_norm_shift(\n    lapse,\n    shift,\n    spatial_metric,\n    inverse_spatial_metric,\n    inverse_spacetime_metric,\n    spacetime_unit_normal,\n    phi,\n    pi,\n):\n    lower_shift = np.einsum(\"ij,j->i\", spatial_metric, shift)\n    deriv_shift_ = deriv_shift(\n        lapse, inverse_spacetime_metric, spacetime_unit_normal, phi\n    )\n    dt_lower_shift_ = dt_lower_shift(\n        lapse, shift, spatial_metric, spacetime_unit_normal, phi, pi\n    )\n    dt_shift_ = dt_shift(\n        lapse, shift, inverse_spatial_metric, spacetime_unit_normal, phi, pi\n    )\n    t0 = np.einsum(\"i,i\", lower_shift, dt_shift_) + np.einsum(\n        \"i,i\", shift, dt_lower_shift_\n    )\n    ti = np.einsum(\"i,ji->j\", lower_shift, deriv_shift_) + np.einsum(\n        \"i,ji->j\", shift, phi[:, 0, 1:]\n    )\n    ta = np.zeros(1 + len(ti))\n    ta[0] = t0\n    ta[1:] = ti\n    return ta\n\n\ndef deriv_spatial_metric(phi):\n    return phi[:, 1:, 1:]\n\n\ndef dt_spatial_metric(lapse, shift, phi, pi):\n    return (-lapse * pi + np.einsum(\"k,kab->ab\", shift, phi))[1:, 1:]\n\n\ndef gh_dt_spacetime_metric(lapse, shift, pi, phi):\n    return -lapse * pi + np.einsum(\"k,kab\", shift, phi)\n\n\ndef gh_d2t2_spacetime_metric(\n    lapse, dt_lapse, shift, dt_shift, phi, dt_phi, pi, dt_pi\n):\n    return (\n        -dt_lapse * pi\n        - lapse * dt_pi\n        + np.einsum(\"k,kab\", dt_shift, phi)\n        + np.einsum(\"k,kab\", shift, dt_phi)\n    )\n\n\ndef spacetime_deriv_detg(\n    sqrt_det_spatial_metric, inverse_spatial_metric, dt_spatial_metric, phi\n):\n    det_spatial_metric = sqrt_det_spatial_metric**2\n    deriv_of_g = deriv_spatial_metric(phi)\n    dtg = np.einsum(\"jk,jk\", inverse_spatial_metric, dt_spatial_metric)\n    dtg *= det_spatial_metric\n    dxg = np.einsum(\"jk,ijk->i\", inverse_spatial_metric, deriv_of_g)\n    dxg *= det_spatial_metric\n    dg = np.zeros(1 + len(dxg))\n    dg[0] = dtg\n    dg[1:] = dxg\n    return dg\n\n\ndef extrinsic_curvature(spacetime_normal_vector, pi, phi):\n    return (\n        0.5 * pi[1:, 1:]\n        + 0.5 * np.einsum(\"ija,a->ij\", phi[:, 1:, :], spacetime_normal_vector)\n        + 0.5 * np.einsum(\"jia,a->ij\", phi[:, 1:, :], spacetime_normal_vector)\n    )\n\n\ndef covariant_deriv_extrinsic_curvture(\n    extrinsic_curvature,\n    spacetime_unit_nomal_vector,\n    spatial_christoffel_second_kind,\n    inverse_spacetime_metric,\n    phi,\n    d_pi,\n    d_phi,\n):\n    term4 = np.einsum(\n        \"ija,b,ca,kcb->kij\",\n        phi[:, 1:, :],\n        spacetime_unit_nomal_vector,\n        inverse_spacetime_metric,\n        phi,\n    )\n    term5 = np.einsum(\n        \"jia,b,ca,kcb->kij\",\n        phi[:, 1:, :],\n        spacetime_unit_nomal_vector,\n        inverse_spacetime_metric,\n        phi,\n    )\n    term6 = np.einsum(\n        \"ija,b,c,a,kcb->kij\",\n        phi[:, 1:, :],\n        spacetime_unit_nomal_vector,\n        0.5 * spacetime_unit_nomal_vector,\n        spacetime_unit_nomal_vector,\n        phi,\n    )\n    term7 = np.einsum(\n        \"jia,b,c,a,kcb->kij\",\n        phi[:, 1:, :],\n        spacetime_unit_nomal_vector,\n        0.5 * spacetime_unit_nomal_vector,\n        spacetime_unit_nomal_vector,\n        phi,\n    )\n\n    cdk = (\n        d_pi[:, 1:, 1:]\n        + np.einsum(\n            \"kija,a->kij\", d_phi[:, :, 1:, :], spacetime_unit_nomal_vector\n        )\n        + np.einsum(\n            \"kjia,a->kij\", d_phi[:, :, 1:, :], spacetime_unit_nomal_vector\n        )\n        - term4\n        - term5\n        - term6\n        - term7\n    )\n\n    return (\n        0.5 * cdk\n        - np.einsum(\n            \"lik,lj->kij\", spatial_christoffel_second_kind, extrinsic_curvature\n        )\n        - np.einsum(\n            \"ljk,li->kij\", spatial_christoffel_second_kind, extrinsic_curvature\n        )\n    )\n\n\ndef gh_spatial_ricci_tensor(phi, deriv_phi, inverse_spatial_metric):\n    ricci_deriv_terms = 0.25 * (\n        np.einsum(\n            \"kl,jlki->ij\", inverse_spatial_metric, deriv_phi[:, :, 1:, 1:]\n        )\n        + np.einsum(\n            \"kl,ilkj->ij\", inverse_spatial_metric, deriv_phi[:, :, 1:, 1:]\n        )\n        - np.einsum(\n            \"kl,jikl->ij\", inverse_spatial_metric, deriv_phi[:, :, 1:, 1:]\n        )\n        - np.einsum(\n            \"kl,ijkl->ij\", inverse_spatial_metric, deriv_phi[:, :, 1:, 1:]\n        )\n        + np.einsum(\n            \"kl,kijl->ij\", inverse_spatial_metric, deriv_phi[:, :, 1:, 1:]\n        )\n        + np.einsum(\n            \"kl,kjil->ij\", inverse_spatial_metric, deriv_phi[:, :, 1:, 1:]\n        )\n        - 2\n        * np.einsum(\n            \"kl,lkij->ij\", inverse_spatial_metric, deriv_phi[:, :, 1:, 1:]\n        )\n    )\n\n    spatial_phi_Ijj = 0.5 * np.einsum(\n        \"kl,lij->kij\", inverse_spatial_metric, phi[:, 1:, 1:]\n    )\n    spatial_phi_ijK = 0.5 * np.einsum(\n        \"kl,ijl->ijk\", inverse_spatial_metric, phi[:, 1:, 1:]\n    )\n    d_minus_two_b = np.einsum(\n        \"kl,lii->k\", inverse_spatial_metric, spatial_phi_ijK\n    ) - 2 * np.einsum(\"kl,iil->k\", inverse_spatial_metric, spatial_phi_Ijj)\n\n    return (\n        ricci_deriv_terms\n        + 0.5\n        * (\n            np.einsum(\"ijk,k->ij\", phi[:, 1:, 1:], d_minus_two_b)\n            + np.einsum(\"jik,k->ij\", phi[:, 1:, 1:], d_minus_two_b)\n            - np.einsum(\"kij,k->ij\", phi[:, 1:, 1:], d_minus_two_b)\n        )\n        + np.einsum(\"ikl,jlk->ij\", spatial_phi_ijK, spatial_phi_ijK)\n        + 2 * np.einsum(\"kil,kjl->ij\", spatial_phi_Ijj, spatial_phi_ijK)\n        - 2 * np.einsum(\"kli,lkj->ij\", spatial_phi_Ijj, spatial_phi_Ijj)\n    )\n\n\ndef trace_christoffel(\n    spacetime_normal_one_form,\n    spacetime_normal_vector,\n    inverse_spatial_metric,\n    inverse_spacetime_metric,\n    pi,\n    phi,\n):\n    spatial_normal_vector = spacetime_normal_vector[1:]\n    # everything except delta^i_a term\n    result = (\n        np.einsum(\"ij,ija->a\", inverse_spatial_metric, phi[:, 1:, :])\n        + np.einsum(\"b,ba->a\", spacetime_normal_vector, pi)\n        - 0.5\n        * np.einsum(\n            \"a,bc,bc->a\",\n            spacetime_normal_one_form,\n            inverse_spacetime_metric,\n            np.einsum(\"i,ibc->bc\", spatial_normal_vector, phi) + pi,\n        )\n    )\n    result[1:] -= 0.5 * np.einsum(\"bc,ibc->i\", inverse_spacetime_metric, phi)\n    return result\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/ComputeSpacetimeQuantities.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef shift(spacetime_metric, inverse_spatial_metric):\n    return np.einsum(\"ij,j->i\", inverse_spatial_metric, spacetime_metric[1:, 0])\n\n\ndef lapse(shift, spacetime_metric):\n    return np.sqrt(\n        np.einsum(\"i,i->\", shift, spacetime_metric[1:, 0])\n        - spacetime_metric[0, 0]\n    )\n\n\ndef spacetime_metric(lapse, shift, spatial_metric):\n    dim = shift.size\n    psi = np.zeros([dim + 1, dim + 1])\n    psi[0, 0] = -(lapse**2) + np.einsum(\n        \"m,n,mn\", shift, shift, spatial_metric\n    )\n    psi[1:, 0] = np.einsum(\"mi,m->i\", spatial_metric, shift)\n    psi[0, 1:] = psi[1:, 0]\n    psi[1:, 1:] = spatial_metric\n    return psi\n\n\ndef inverse_spacetime_metric(lapse, shift, inverse_spatial_metric):\n    dim = shift.size\n    inv_psi = np.zeros([dim + 1, dim + 1])\n    inv_psi[0, 0] = -1.0 / lapse**2\n    inv_psi[1:, 0] = shift / lapse**2\n    inv_psi[0, 1:] = inv_psi[1:, 0]\n    inv_psi[1:, 1:] = (\n        inverse_spatial_metric - np.outer(shift, shift) / lapse**2\n    )\n    return inv_psi\n\n\ndef dt_spacetime_metric(\n    lapse, dt_lapse, shift, dt_shift, spatial_metric, dt_spatial_metric\n):\n    dim = shift.size\n    dt_psi = np.zeros([dim + 1, dim + 1])\n    dt_psi[0, 0] = (\n        -2 * lapse * dt_lapse\n        + 2 * np.einsum(\"mn,m,n\", spatial_metric, shift, dt_shift)\n        + np.einsum(\"m,n,mn\", shift, shift, dt_spatial_metric)\n    )\n    dt_psi[1:, 0] = np.einsum(\"mi,m\", spatial_metric, dt_shift) + np.einsum(\n        \"m,mi\", shift, dt_spatial_metric\n    )\n    dt_psi[1:, 1:] = dt_spatial_metric\n    dt_psi[0, 1:] = dt_psi[1:, 0]  # Symmetrise\n    return dt_psi\n\n\ndef dt_spatial_metric(\n    lapse,\n    shift,\n    deriv_shift,\n    spatial_metric,\n    deriv_spatial_metric,\n    extrinsic_curvature,\n):\n    return (\n        -2.0 * lapse * extrinsic_curvature\n        + np.einsum(\"k,kij\", shift, deriv_spatial_metric)\n        + np.einsum(\"ik,jk\", spatial_metric, deriv_shift)\n        + np.einsum(\"jk,ik\", spatial_metric, deriv_shift)\n    )\n\n\ndef spatial_deriv_spacetime_metric(\n    lapse, deriv_lapse, shift, deriv_shift, spatial_metric, deriv_spatial_metric\n):\n    dim = shift.size\n    deriv_psi = np.zeros([dim, dim + 1, dim + 1])\n    deriv_psi[:, 0, 0] = (\n        -2.0 * lapse * deriv_lapse\n        + 2.0 * np.einsum(\"mn,m,kn->k\", spatial_metric, shift, deriv_shift)\n        + np.einsum(\"m,n,kmn->k\", shift, shift, deriv_spatial_metric)\n    )\n    deriv_psi[:, 1:, 0] = np.einsum(\n        \"mi,km->ki\", spatial_metric, deriv_shift\n    ) + np.einsum(\"m,kmi->ki\", shift, deriv_spatial_metric)\n    deriv_psi[:, 1:, 1:] = deriv_spatial_metric\n    deriv_psi[:, 0, 1:] = deriv_psi[:, 1:, 0]  # Symmetrise\n    return deriv_psi\n\n\ndef derivatives_of_spacetime_metric(\n    lapse,\n    dt_lapse,\n    deriv_lapse,\n    shift,\n    dt_shift,\n    deriv_shift,\n    spatial_metric,\n    dt_spatial_metric,\n    deriv_spatial_metric,\n):\n    dim = shift.size\n    d4_psi = np.zeros([dim + 1, dim + 1, dim + 1])\n    # Spatial derivatives\n    d4_psi[0, :, :] = dt_spacetime_metric(\n        lapse, dt_lapse, shift, dt_shift, spatial_metric, dt_spatial_metric\n    )\n    d4_psi[1:, :, :] = spatial_deriv_spacetime_metric(\n        lapse,\n        deriv_lapse,\n        shift,\n        deriv_shift,\n        spatial_metric,\n        deriv_spatial_metric,\n    )\n    return d4_psi\n\n\ndef spacetime_normal_vector(lapse, shift):\n    dim = shift.size\n    vector = np.zeros([dim + 1])\n    vector[0] = 1.0 / lapse\n    vector[1:] = -shift / lapse\n    return vector\n\n\ndef spacetime_normal_one_form(lapse, shift):\n    dim = shift.size\n    one_form = np.zeros([dim + 1])\n    one_form[0] = -lapse\n    one_form[1:] = 0.0\n    return one_form\n\n\ndef extrinsic_curvature(\n    lapse,\n    shift,\n    deriv_shift,\n    spatial_metric,\n    dt_spatial_metric,\n    deriv_spatial_metric,\n):\n    ext_curve = (\n        np.einsum(\"k,kij\", shift, deriv_spatial_metric)\n        + np.einsum(\"ki,jk\", spatial_metric, deriv_shift)\n        + np.einsum(\"kj,ik\", spatial_metric, deriv_shift)\n        - dt_spatial_metric\n    )\n    ext_curve *= 0.5 / lapse\n    return ext_curve\n\n\ndef covariant_deriv_extrinsic_curvture_adm(\n    d_ex_curv,\n    ex_curv,\n    spatial_christoffel_second_kind,\n):\n    grad_ex_curv = d_ex_curv\n    grad_ex_curv += -np.einsum(\n        \"lki,lj->kij\", spatial_christoffel_second_kind, ex_curv\n    )\n    grad_ex_curv += -np.einsum(\n        \"lkj,il->kij\", spatial_christoffel_second_kind, ex_curv\n    )\n\n    return grad_ex_curv\n\n\ndef deriv_inverse_spatial_metric(inverse_spatial_metric, d_spatial_metric):\n    return -np.einsum(\n        \"in,mj,knm->kij\",\n        inverse_spatial_metric,\n        inverse_spatial_metric,\n        d_spatial_metric,\n    )\n\n\ndef pontryagin_scalar_in_vacuum(\n    weyl_electric, weyl_magnetic, inverse_spatial_metric\n):\n    pontryagin_scalar = -16.0 * np.trace(\n        weyl_electric\n        @ inverse_spatial_metric\n        @ weyl_magnetic\n        @ inverse_spatial_metric\n    )\n\n    return pontryagin_scalar\n\n\ndef gauss_bonnet_scalar_in_vacuum(\n    weyl_electric_scalar,\n    weyl_magnetic_scalar,\n):\n    return 8 * (weyl_electric_scalar - weyl_magnetic_scalar)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/GeodesicEquation.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef geodesic_equation(\n    x,\n    pi,\n    lnp0,\n    lapse,\n    deriv_lapse,\n    shift,\n    deriv_shift,\n    inv_spatial_metric,\n    deriv_inv_spatial_metric,\n    extrinsic_curvature,\n):\n    pi_upper = np.einsum(\"ij,j\", inv_spatial_metric, pi)\n    dt_lnp0 = -np.einsum(\"i,i\", deriv_lapse, pi_upper) + lapse * np.einsum(\n        \"ij,i,j\", extrinsic_curvature, pi_upper, pi_upper\n    )\n    dt_x = lapse * pi_upper - shift\n    dt_pi = (\n        -deriv_lapse\n        - dt_lnp0 * pi\n        + np.einsum(\"ik,k\", deriv_shift, pi)\n        - 0.5 * lapse * np.einsum(\"ijk,j,k\", deriv_inv_spatial_metric, pi, pi)\n    )\n    return [dt_x, dt_pi, dt_lnp0]\n\n\ndef dt_x(*args):\n    return geodesic_equation(*args)[0]\n\n\ndef dt_pi(*args):\n    return geodesic_equation(*args)[1]\n\n\ndef dt_lnp0(*args):\n    return geodesic_equation(*args)[2]\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/InterfaceNullNormal.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef interface_outgoing_null_normal(\n    spacetime_normal_vector_or_one_form,\n    interface_normal_vector_or_one_form,\n    shift=None,\n):\n    result = (2.0**-0.5) * spacetime_normal_vector_or_one_form\n    if shift is not None:\n        result[0] = result[0] + (2.0**-0.5) * np.einsum(\n            \"i...,i...->...\",\n            interface_normal_vector_or_one_form,\n            shift,\n        )\n    result[1:] = (\n        result[1:] + (2.0**-0.5) * interface_normal_vector_or_one_form\n    )\n    return result\n\n\ndef interface_incoming_null_normal(\n    spacetime_normal_vector_or_one_form,\n    interface_normal_vector_or_one_form,\n    shift=None,\n):\n    result = (2.0**-0.5) * spacetime_normal_vector_or_one_form\n    if shift is not None:\n        result[0] = result[0] - (2.0**-0.5) * np.einsum(\n            \"i...,i...->...\",\n            interface_normal_vector_or_one_form,\n            shift,\n        )\n    result[1:] = (\n        result[1:] - (2.0**-0.5) * interface_normal_vector_or_one_form\n    )\n    return result\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/KerrSchildCoords.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef r_coord_squared(coords, bh_mass, bh_dimless_spin):\n    a_squared = (bh_mass * bh_dimless_spin) ** 2\n    temp = 0.5 * (\n        coords[0] * coords[0]\n        + coords[1] * coords[1]\n        + coords[2] * coords[2]\n        - a_squared\n    )\n    return temp + np.sqrt(temp * temp + a_squared * coords[2] * coords[2])\n\n\ndef jacobian(coords, bh_mass, bh_dimless_spin):\n    result = np.zeros((3, 3))\n    spin_a = bh_mass * bh_dimless_spin\n    a_squared = spin_a**2\n    r_squared = r_coord_squared(coords, bh_mass, bh_dimless_spin)\n    r = np.sqrt(r_squared)\n    sin_theta = np.sqrt(\n        (coords[0] ** 2 + coords[1] ** 2) / (r_squared + a_squared)\n    )\n    cos_theta = coords[2] / r\n    inv_denom = 1.0 / np.sqrt(\n        (coords[0] ** 2 + coords[1] ** 2) * (r_squared + a_squared)\n    )\n    sin_phi = (coords[1] * r - spin_a * coords[0]) * inv_denom\n    cos_phi = (coords[0] * r + spin_a * coords[1]) * inv_denom\n\n    result[0, 0] = sin_theta * cos_phi\n    result[0, 1] = (r * cos_phi - spin_a * sin_phi) * cos_theta\n    result[0, 2] = -(r * sin_phi + spin_a * cos_phi) * sin_theta\n    result[1, 0] = sin_theta * sin_phi\n    result[1, 1] = (r * sin_phi + spin_a * cos_phi) * cos_theta\n    result[1, 2] = (r * cos_phi - spin_a * sin_phi) * sin_theta\n    result[2, 0] = cos_theta\n    result[2, 1] = -r * sin_theta\n    result[2, 2] = 0.0\n    return result\n\n\ndef cartesian_from_spherical_ks(\n    vector, cartesian_coords, bh_mass, bh_dimless_spin\n):\n    return np.einsum(\n        \"ij,j\", jacobian(cartesian_coords, bh_mass, bh_dimless_spin), vector\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/ProjectionOperators.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef transverse_projection_operator(\n    spatial_metric_or_its_inverse, normal_vector_or_one_form\n):\n    dim_offset = (\n        np.shape(normal_vector_or_one_form)[0]\n        - np.shape(spatial_metric_or_its_inverse)[1]\n    )\n    if dim_offset != 0 and dim_offset != 1:\n        raise RuntimeError(\"Incompatible inputs passed\")\n    return spatial_metric_or_its_inverse - np.einsum(\n        \"i,j->ij\",\n        normal_vector_or_one_form[dim_offset:],\n        normal_vector_or_one_form[dim_offset:],\n    )\n\n\ndef transverse_projection_operator_mixed_from_spatial_input(\n    normal_vector, normal_one_form\n):\n    return np.eye(np.shape(normal_vector)[0]) - np.einsum(\n        \"i,j->ij\", normal_vector, normal_one_form\n    )\n\n\ndef projection_operator_transverse_to_interface(\n    spacetime_metric_or_its_inverse,\n    normal_vector_or_one_form,\n    interface_normal_vector_or_one_form,\n    shift=None,\n):\n    interface_normal = np.zeros(1 + len(interface_normal_vector_or_one_form))\n    if shift is not None:\n        interface_normal[0] = np.einsum(\n            \"i,i->\", interface_normal_vector_or_one_form, shift\n        )\n    interface_normal[1:] = interface_normal_vector_or_one_form\n    return (\n        spacetime_metric_or_its_inverse\n        + np.einsum(\n            \"i,j->ij\", normal_vector_or_one_form, normal_vector_or_one_form\n        )\n        - np.einsum(\"i,j->ij\", interface_normal, interface_normal)\n    )\n\n\ndef projection_operator_transverse_to_interface_mixed(\n    normal_vector,\n    normal_one_form,\n    interface_normal_vector,\n    interface_normal_one_form,\n    shift,\n):\n    interface_normal_vec = np.zeros(1 + len(interface_normal_vector))\n    interface_normal_1form = np.zeros(1 + len(interface_normal_one_form))\n    interface_normal_vec[1:] = interface_normal_vector\n    interface_normal_1form[0] = np.einsum(\n        \"i,i->\", interface_normal_one_form, shift\n    )\n    interface_normal_1form[1:] = interface_normal_one_form\n    return (\n        np.eye(np.shape(normal_vector)[0])\n        + np.einsum(\"i,j->ij\", normal_vector, normal_one_form)\n        - np.einsum(\"i,j->ij\", interface_normal_vec, interface_normal_1form)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Psi4.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport cmath\nimport math\n\nimport numpy as np\n\nfrom .ProjectionOperators import transverse_projection_operator\nfrom .WeylPropagating import weyl_propagating_modes\n\n\ndef psi_4(\n    spatial_ricci,\n    extrinsic_curvature,\n    cov_deriv_extrinsic_curvature,\n    spatial_metric,\n    inv_spatial_metric,\n    inertial_coords,\n):\n    magnitude_inertial = math.sqrt(\n        np.einsum(\"a,b,ab\", inertial_coords, inertial_coords, spatial_metric)\n    )\n    if magnitude_inertial != 0.0:\n        r_hat = np.einsum(\"a\", inertial_coords / magnitude_inertial)\n    else:\n        r_hat = np.einsum(\"a\", inertial_coords * 0.0)\n\n    lower_r_hat = np.einsum(\"a,ab\", r_hat, spatial_metric)\n\n    inv_projection_tensor = transverse_projection_operator(\n        inv_spatial_metric, r_hat\n    )\n    projection_tensor = transverse_projection_operator(\n        spatial_metric, lower_r_hat\n    )\n    projection_up_lo = np.einsum(\"ab,ac\", inv_projection_tensor, spatial_metric)\n\n    u8_plus = weyl_propagating_modes(\n        spatial_ricci,\n        extrinsic_curvature,\n        inv_spatial_metric,\n        cov_deriv_extrinsic_curvature,\n        r_hat,\n        inv_projection_tensor,\n        projection_tensor,\n        projection_up_lo,\n        1,\n    )\n\n    x_coord = np.zeros((3))\n    x_coord[0] = 1\n    x_component = np.einsum(\"a,b,ab\", x_coord, r_hat, spatial_metric)\n    x_hat = x_coord - (x_component * r_hat)\n    magnitude_x = math.sqrt(np.einsum(\"a,b,ab\", x_hat, x_hat, spatial_metric))\n    if magnitude_x != 0.0:\n        x_hat = np.einsum(\"a\", x_hat / magnitude_x)\n    else:\n        x_hat = np.einsum(\"a\", x_hat * 0.0)\n    y_coord = np.zeros((3))\n    y_coord[1] = 1\n    y_component = np.einsum(\"a,b,ab\", y_coord, r_hat, spatial_metric)\n    y_hat = y_coord - (y_component * r_hat)\n    y_component = np.einsum(\"a,b,ab\", y_coord, x_hat, spatial_metric)\n    y_hat = y_hat - (y_component * x_hat)\n    magnitude_y = math.sqrt(np.einsum(\"a,b,ab\", y_hat, y_hat, spatial_metric))\n    if magnitude_y != 0.0:\n        y_hat = np.einsum(\"a\", y_hat / magnitude_y)\n    else:\n        y_hat = np.einsum(\"a\", y_hat * 0.0)\n    m_bar = x_hat - (y_hat * complex(0.0, 1.0))\n\n    return -0.5 * np.einsum(\"ab,a,b\", u8_plus, m_bar, m_bar)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.GeneralRelativity.Python\"\n  Test_Bindings.py\n  \"Unit;GeneralRelativity;Python\"\n  PyGeneralRelativity)\n\nspectre_add_python_bindings_test(\n  \"Unit.GeneralizedHarmonic.Python\"\n  Test_GhBindings.py\n  \"Unit;GeneralizedHarmonic;Python\"\n  PyGeneralizedHarmonic)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Python/Test_Bindings.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar, tnsr\nfrom spectre.PointwiseFunctions.GeneralRelativity import *\n\n\nclass TestBindings(unittest.TestCase):\n    def test_derivative_inverse_spatial_metric(self):\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        d_spatial_metric = tnsr.ijj[DataVector, 3](num_points=1, fill=0.0)\n        deri_inverse_spatial_metric_tensor = deriv_inverse_spatial_metric(\n            inverse_spatial_metric,\n            d_spatial_metric,\n        )\n\n    def test_extrinsic_curvature(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        deriv_shift = tnsr.iJ[DataVector, 3](num_points=1, fill=0.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        spatial_metric[0] = spatial_metric[3] = spatial_metric[5] = DataVector(\n            1, 1.0\n        )\n        dt_spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        deriv_spatial_metric = tnsr.ijj[DataVector, 3](num_points=1, fill=0.0)\n        extrinsic_curv = extrinsic_curvature(\n            lapse,\n            shift,\n            deriv_shift,\n            spatial_metric,\n            dt_spatial_metric,\n            deriv_spatial_metric,\n        )\n        npt.assert_allclose(extrinsic_curv, 0)\n\n    def test_inverse_spacetime_metric(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=-1.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=0.0)\n        inverse_spacetime_metric_test = inverse_spacetime_metric(\n            lapse, shift, inverse_spatial_metric\n        )\n        npt.assert_allclose(inverse_spacetime_metric_test, -1)\n\n    def test_interface_null_normal(self):\n        spacetime_normal_one_form = tnsr.a[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        interface_unit_normal_one_form = tnsr.i[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        shift_ = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        inter_null_norm = interface_null_normal(\n            spacetime_normal_one_form,\n            interface_unit_normal_one_form,\n            shift_,\n            sign=1.0,\n        )\n\n    def test_lapse_shift_normals(self):\n        spacetime_metric = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        shift_ = shift(spacetime_metric, inverse_spatial_metric)\n        lapse_ = lapse(shift_, spacetime_metric)\n        spacetime_normal_one_form(lapse_)\n        spacetime_normal_vector(lapse_, shift_)\n\n    def test_projections(self):\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        normal_vector = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        # tnsr.II\n        transverse_projection_operator(inverse_spatial_metric, normal_vector)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        normal_one_form = tnsr.i[DataVector, 3](num_points=1, fill=1.0)\n        # tnsr.ii\n        transverse_projection_operator(spatial_metric, normal_one_form)\n        # tnsr.Ij\n        transverse_projection_operator(normal_vector, normal_one_form)\n        spacetime_metric = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        spacetime_normal_one_form = tnsr.a[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        interface_unit_normal_one_form = tnsr.i[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        # tnsr.aa\n        transverse_projection_operator(\n            spacetime_metric,\n            spacetime_normal_one_form,\n            interface_unit_normal_one_form,\n            shift,\n        )\n        inverse_spacetime_metric = tnsr.AA[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        spacetime_normal_vector = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        interface_unit_normal_vector = tnsr.I[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        # tnsr.AA\n        transverse_projection_operator(\n            inverse_spacetime_metric,\n            spacetime_normal_vector,\n            interface_unit_normal_vector,\n        )\n        # tnsr.Ab\n        transverse_projection_operator(\n            spacetime_normal_vector,\n            spacetime_normal_one_form,\n            interface_unit_normal_vector,\n            interface_unit_normal_one_form,\n            shift,\n        )\n\n    def test_psi4(self):\n        spatial_ricci = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        extrinsic_curvature = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        cov_deriv_extrinsic_curvature = tnsr.ijj[DataVector, 3](\n            num_points=1, fill=0.0\n        )\n        flat_metric = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        flat_metric[0] = flat_metric[3] = flat_metric[5] = DataVector(1, 1.0)\n        inverse_metric = tnsr.II[DataVector, 3](num_points=1, fill=0.0)\n        inverse_metric[0] = inverse_metric[3] = inverse_metric[5] = DataVector(\n            1, 1.0\n        )\n        inertial_coords = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        psi_4_real = psi4real(\n            spatial_ricci,\n            extrinsic_curvature,\n            cov_deriv_extrinsic_curvature,\n            flat_metric,\n            inverse_metric,\n            inertial_coords,\n        )\n        npt.assert_allclose(psi_4_real, 0)\n\n    def test_ricci(self):\n        christoffel_second_kind = tnsr.Abb[DataVector, 3](\n            num_points=1, fill=0.0\n        )\n        d_christoffel_second_kind = tnsr.aBcc[DataVector, 3](\n            num_points=1, fill=0.0\n        )\n        spatial_ricci = ricci_tensor(\n            christoffel_second_kind, d_christoffel_second_kind\n        )\n        inverse_metric = tnsr.AA[DataVector, 3](num_points=1, fill=0.0)\n        ricci_scalar(spatial_ricci, inverse_metric)\n\n    def test_derivatives_of_spacetime_metric(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        dt_lapse = Scalar[DataVector](num_points=1, fill=0.0)\n        deriv_lapse = tnsr.i[DataVector, 3](num_points=1, fill=0.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        dt_shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        deriv_shift = tnsr.iJ[DataVector, 3](num_points=1, fill=0.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        dt_spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        deriv_spatial_metric = tnsr.ijj[DataVector, 3](num_points=1, fill=0.0)\n        derivatives_of_spacetime_metric_test = derivatives_of_spacetime_metric(\n            lapse,\n            dt_lapse,\n            deriv_lapse,\n            shift,\n            dt_shift,\n            deriv_shift,\n            spatial_metric,\n            dt_spatial_metric,\n            deriv_spatial_metric,\n        )\n        npt.assert_allclose(derivatives_of_spacetime_metric_test, 0)\n\n    def test_spacetime_metric(self):\n        lapse = Scalar[DataVector](num_points=1, fill=0.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        spacetime_metric_test = spacetime_metric(lapse, shift, spatial_metric)\n        npt.assert_allclose(spacetime_metric_test, 0)\n\n    def test_spatial_metric(self):\n        spacetime_metric = tnsr.aa[DataVector, 3](num_points=1, fill=0.0)\n        spatial_metric_test = spatial_metric(spacetime_metric)\n        npt.assert_allclose(spatial_metric_test, 0)\n\n    def test_time_derivative_of_spacetime_metric(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        dt_lapse = Scalar[DataVector](num_points=1, fill=0.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        dt_shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        dt_spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        t_deriv_of_spacetime_metric_test = time_derivative_of_spacetime_metric(\n            lapse, dt_lapse, shift, dt_shift, spatial_metric, dt_spatial_metric\n        )\n        npt.assert_allclose(t_deriv_of_spacetime_metric_test, 0)\n\n    def test_time_derivative_of_spatial_metric(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        deriv_shift = tnsr.iJ[DataVector, 3](num_points=1, fill=0.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        deriv_spatial_metric = tnsr.ijj[DataVector, 3](num_points=1, fill=0.0)\n        extrinsic_curvature = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        t_deriv_of_spatial_metric_test = time_derivative_of_spatial_metric(\n            lapse,\n            shift,\n            deriv_shift,\n            spatial_metric,\n            deriv_spatial_metric,\n            extrinsic_curvature,\n        )\n        npt.assert_allclose(t_deriv_of_spatial_metric_test, 0)\n\n    def test_tortoise_coordinates(self):\n        npt.assert_allclose(\n            tortoise_radius_from_boyer_lindquist_minus_r_plus(\n                0.55692908552214748, 1.0, 0.0\n            ),\n            0.0,\n            atol=1e-14,\n        )\n        npt.assert_allclose(\n            boyer_lindquist_radius_minus_r_plus_from_tortoise(0.0, 1.0, 0.0),\n            0.55692908552214748,\n            atol=1e-14,\n        )\n\n    def test_weyl_electric(self):\n        spatial_ricci = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        extrinsic_curvature = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        weyl_electric_tensor = weyl_electric(\n            spatial_ricci, extrinsic_curvature, inverse_spatial_metric\n        )\n        weyl_electric_scalar(weyl_electric_tensor, inverse_spatial_metric)\n\n    def test_weyl_propagating(self):\n        ricci = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        extrinsic_curvature = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        cov_deriv_extrinsic_curvature = tnsr.ijj[DataVector, 3](\n            num_points=1, fill=0.0\n        )\n        unit_interface_normal_vector = tnsr.I[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        projection_IJ = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        projection_ij = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        projection_Ij = tnsr.Ij[DataVector, 3](num_points=1, fill=1.0)\n        weyl_prop = weyl_propagating(\n            ricci,\n            extrinsic_curvature,\n            inverse_spatial_metric,\n            cov_deriv_extrinsic_curvature,\n            unit_interface_normal_vector,\n            projection_IJ,\n            projection_ij,\n            projection_Ij,\n            sign=1.0,\n        )\n        npt.assert_allclose(weyl_prop, 0)\n\n    def test_weyl_magnetic(self):\n        grad_extrinsic_curvature = tnsr.ijj[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        sqrt_det_spatial_metric = Scalar[DataVector](num_points=1, fill=1.0)\n        weyl_mag = weyl_magnetic(\n            grad_extrinsic_curvature, spatial_metric, sqrt_det_spatial_metric\n        )\n        npt.assert_allclose(weyl_mag, 0)\n\n    def test_weyl_magnetic_scalar(self):\n        weyl_magnetic = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=0.0)\n        weyl_mag_scalar = weyl_magnetic_scalar(\n            weyl_magnetic, inverse_spatial_metric\n        )\n        npt.assert_allclose(weyl_mag_scalar, 0)\n\n    def test_weyl_type_D1(self):\n        weyl_electric = tnsr.ii[DataVector, 3](num_points=1, fill=-1.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=-1.0)\n        weyl_D1 = weyl_type_D1(\n            weyl_electric, spatial_metric, inverse_spatial_metric\n        )\n        npt.assert_allclose(weyl_D1, 0)\n\n    def test_weyl_type_D1_scalar(self):\n        weyl_type_D1 = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=0.0)\n        weyl_D1_scalar = weyl_type_D1_scalar(\n            weyl_type_D1, inverse_spatial_metric\n        )\n        npt.assert_allclose(weyl_D1_scalar, 0)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Python/Test_GhBindings.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.PointwiseFunctions.GeneralRelativity.GeneralizedHarmonic as gh\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar, tnsr\n\n\nclass TestBindings(unittest.TestCase):\n    def test_christoffel_second_kind(self):\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        inv_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        gh.christoffel_second_kind(\n            phi,\n            inv_metric,\n        )\n\n    def test_covariant_deriv_of_extrinsic_curvature(self):\n        extrinsic_curvature = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        spacetime_unit_normal_vector = tnsr.A[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        spatial_christoffel_second_kind = tnsr.Ijj[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        inverse_spacetime_metric = tnsr.AA[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        d_pi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        d_phi = tnsr.ijaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.covariant_deriv_of_extrinsic_curvature(\n            extrinsic_curvature,\n            spacetime_unit_normal_vector,\n            spatial_christoffel_second_kind,\n            inverse_spacetime_metric,\n            phi,\n            d_pi,\n            d_phi,\n        )\n\n    def test_deriv_spatial_metric(self):\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.deriv_spatial_metric(\n            phi,\n        )\n\n    def test_extrinsic_curvature(self):\n        spacetime_normal_vector = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        pi = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.extrinsic_curvature(spacetime_normal_vector, pi, phi)\n\n    def test_spatial_deriv_of_shift(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        inverse_spacetime_metric = tnsr.AA[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        spacetime_unit_normal = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.spatial_deriv_of_shift(\n            lapse,\n            inverse_spacetime_metric,\n            spacetime_unit_normal,\n            phi,\n        )\n\n    def test_spatial_deriv_of_lapse(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        spacetime_unit_normal = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.spatial_deriv_of_lapse(\n            lapse,\n            spacetime_unit_normal,\n            phi,\n        )\n\n    def test_gauge_source(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        dt_lapse = Scalar[DataVector](num_points=1, fill=0.0)\n        deriv_lapse = tnsr.i[DataVector, 3](num_points=1, fill=0.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        dt_shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        deriv_shift = tnsr.iJ[DataVector, 3](num_points=1, fill=0.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        trace_extrinsic_curvature = Scalar[DataVector](num_points=1, fill=1.0)\n        trace_christoffel_last_indices = tnsr.i[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        gh.gauge_source(\n            lapse,\n            dt_lapse,\n            deriv_lapse,\n            shift,\n            dt_shift,\n            deriv_shift,\n            spatial_metric,\n            trace_extrinsic_curvature,\n            trace_christoffel_last_indices,\n        )\n\n    def test_phi(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        deriv_lapse = tnsr.i[DataVector, 3](num_points=1, fill=0.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        deriv_shift = tnsr.iJ[DataVector, 3](num_points=1, fill=0.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        deriv_spatial_metric = tnsr.ijj[DataVector, 3](num_points=1, fill=0.0)\n        gh.phi(\n            lapse,\n            deriv_lapse,\n            shift,\n            deriv_shift,\n            spatial_metric,\n            deriv_spatial_metric,\n        )\n\n    def test_pi(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        dt_lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        dt_shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        dt_spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=0.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.pi(\n            lapse,\n            dt_lapse,\n            shift,\n            dt_shift,\n            spatial_metric,\n            dt_spatial_metric,\n            phi,\n        )\n\n    def test_spacetime_deriv_of_det_spatial_metric(self):\n        sqrt_det_spatial_metric = Scalar[DataVector](num_points=1, fill=1.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        dt_spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.spacetime_deriv_of_det_spatial_metric(\n            sqrt_det_spatial_metric,\n            inverse_spatial_metric,\n            dt_spatial_metric,\n            phi,\n        )\n\n    def test_spacetime_deriv_of_norm_of_shift(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        inverse_spacetime_metric = tnsr.AA[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        spacetime_unit_normal = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        pi = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        gh.spacetime_deriv_of_norm_of_shift(\n            lapse,\n            shift,\n            spatial_metric,\n            inverse_spatial_metric,\n            inverse_spacetime_metric,\n            spacetime_unit_normal,\n            phi,\n            pi,\n        )\n\n    def test_spatial_ricci_tensor(self):\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=0.0)\n        deriv_phi = tnsr.ijaa[DataVector, 3](num_points=1, fill=0.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=0.0)\n        gh.spatial_ricci_tensor(\n            phi,\n            deriv_phi,\n            inverse_spatial_metric,\n        )\n\n    def test_time_deriv_of_laspe(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        spacetime_unit_normal = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        pi = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        gh.time_deriv_of_lapse(\n            lapse,\n            shift,\n            spacetime_unit_normal,\n            phi,\n            pi,\n        )\n\n    def test_time_deriv_of_lower_shift(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        spatial_metric = tnsr.ii[DataVector, 3](num_points=1, fill=1.0)\n        spacetime_unit_normal = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        pi = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        gh.time_deriv_of_lower_shift(\n            lapse, shift, spatial_metric, spacetime_unit_normal, phi, pi\n        )\n\n    def test_time_deriv_of_shift(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        spacetime_unit_normal = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        pi = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        gh.time_deriv_of_shift(\n            lapse,\n            shift,\n            inverse_spatial_metric,\n            spacetime_unit_normal,\n            phi,\n            pi,\n        )\n\n    def test_time_derive_of_spatial_metric(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=0.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        pi = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        gh.time_deriv_of_spatial_metric(\n            lapse,\n            shift,\n            phi,\n            pi,\n        )\n\n    def test_time_derivative_of_spacetime_metric(self):\n        lapse = Scalar[DataVector](num_points=1, fill=1.0)\n        shift = tnsr.I[DataVector, 3](num_points=1, fill=1.0)\n        pi = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.time_derivative_of_spacetime_metric(lapse, shift, pi, phi)\n\n    def test_trace_christoffel(self):\n        spacetime_normal_one_form = tnsr.a[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        spacetime_normal_vector = tnsr.A[DataVector, 3](num_points=1, fill=1.0)\n        inverse_spatial_metric = tnsr.II[DataVector, 3](num_points=1, fill=1.0)\n        inverse_spacetime_metric = tnsr.AA[DataVector, 3](\n            num_points=1, fill=1.0\n        )\n        pi = tnsr.aa[DataVector, 3](num_points=1, fill=1.0)\n        phi = tnsr.iaa[DataVector, 3](num_points=1, fill=1.0)\n        gh.trace_christoffel(\n            spacetime_normal_one_form,\n            spacetime_normal_vector,\n            inverse_spatial_metric,\n            inverse_spacetime_metric,\n            pi,\n            phi,\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Ricci.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef ricci_tensor(christoffel, deriv_christoffel):\n    return (\n        np.einsum(\"ccab\", deriv_christoffel)\n        - 0.5\n        * (\n            np.einsum(\"bcac\", deriv_christoffel)\n            + np.einsum(\"acbc\", deriv_christoffel)\n        )\n        + np.einsum(\"dab,ccd\", christoffel, christoffel)\n        - np.einsum(\"dac,cbd\", christoffel, christoffel)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/RicciScalar.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\nimport numpy as np\n\n\ndef ricci_scalar(ricci_tensor, inverse_metric):\n    ricci_up_down = np.einsum(\"cb,ac\", ricci_tensor, inverse_metric)\n    return np.einsum(\"aa\", ricci_up_down)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/SpacetimeDerivativeOfGothG.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Functions testing SpacetimeDerivativeOfGothG\n\n\ndef spacetime_deriv_of_goth_g(\n    inverse_spacetime_metric,\n    da_spacetime_metric,\n    lapse,\n    da_lapse,\n    sqrt_det_spatial_metric,\n    da_det_spatial_metric,\n):\n    return np.tensordot(\n        da_lapse * sqrt_det_spatial_metric\n        + 0.5 * lapse * da_det_spatial_metric / sqrt_det_spatial_metric,\n        inverse_spacetime_metric,\n        axes=0,\n    ) - lapse * sqrt_det_spatial_metric * np.einsum(\n        \"im,jn,hmn\",\n        inverse_spacetime_metric,\n        inverse_spacetime_metric,\n        da_spacetime_metric,\n    )\n\n\n# End functions for testing SpacetimeDerivativeOfGothG\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Surfaces/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_GrSurfaces\")\n\nset(LIBRARY_SOURCES\n  Test_ComputeItems.cpp\n  Test_GrSurfaces.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ApparentHorizonFinder\n  DataStructures\n  GeneralRelativity\n  GrSurfaces\n  GrSurfacesHelpers\n  SphericalHarmonics\n  SphericalHarmonicsHelpers\n  XctsSolutions\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Surfaces/Test_ComputeItems.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/ComputeItems.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename Frame, typename T>\nvoid test_strahlkorper_compute_items(const T& used_for_size) {\n  // Set up random values for lapse, shift, spatial_metric,\n  // and their derivatives.\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  std::uniform_real_distribution<> dist_positive(1., 2.);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_dist = make_not_null(&dist);\n  const auto nn_dist_positive = make_not_null(&dist_positive);\n\n  const auto lapse = make_with_random_values<Scalar<T>>(\n      nn_generator, nn_dist_positive, used_for_size);\n  const auto shift = make_with_random_values<tnsr::I<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto spatial_metric = [&]() {\n    auto spatial_metric_l = make_with_random_values<tnsr::ii<T, Dim>>(\n        nn_generator, nn_dist, used_for_size);\n    // Make sure spatial_metric isn't singular by adding\n    // large enough positive diagonal values.\n    for (size_t i = 0; i < Dim; ++i) {\n      spatial_metric_l.get(i, i) += 4.0;\n    }\n    return spatial_metric_l;\n  }();\n  const auto dt_lapse = make_with_random_values<Scalar<T>>(\n      nn_generator, nn_dist_positive, used_for_size);\n  const auto deriv_lapse = make_with_random_values<tnsr::i<T, Dim>>(\n      nn_generator, nn_dist_positive, used_for_size);\n  const auto dt_shift = make_with_random_values<tnsr::I<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto deriv_shift = make_with_random_values<tnsr::iJ<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto deriv_spatial_metric = make_with_random_values<tnsr::ijj<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto dt_spatial_metric = make_with_random_values<tnsr::ii<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n\n  // Make spacetime metric, extrinsic curvature, inverse spatial\n  // metric, spatial christoffel of the second kind, and generalized\n  // harmonic pi, psi variables in a way that is already independently\n  // tested.\n  const auto spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n  const auto expected_extrinsic_curvature =\n      gr::extrinsic_curvature(lapse, shift, deriv_shift, spatial_metric,\n                              dt_spatial_metric, deriv_spatial_metric);\n  const auto expected_inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto expected_spatial_christoffel_second_kind =\n      raise_or_lower_first_index(\n          gr::christoffel_first_kind(deriv_spatial_metric),\n          expected_inverse_spatial_metric);\n  const auto phi = gh::phi(lapse, deriv_lapse, shift, deriv_shift,\n                           spatial_metric, deriv_spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n                         dt_spatial_metric, phi);\n\n  // Now test the ComputeItems.\n  const auto box = db::create<\n      db::AddSimpleTags<\n          tmpl::list<gr::Tags::SpacetimeMetric<DataVector, Dim, Frame>,\n                     gh::Tags::Pi<DataVector, Dim, Frame>,\n                     gh::Tags::Phi<DataVector, Dim, Frame>>>,\n      db::AddComputeTags<tmpl::list<\n          gr::surfaces::Tags::InverseSpatialMetricCompute<Dim, Frame>,\n          gr::surfaces::Tags::ExtrinsicCurvatureCompute<Dim, Frame>,\n          gr::surfaces::Tags::SpatialChristoffelSecondKindCompute<Dim,\n                                                                  Frame>>>>(\n      spacetime_metric, pi, phi);\n\n  const auto& inverse_spatial_metric =\n      db::get<gr::Tags::InverseSpatialMetric<DataVector, Dim, Frame>>(box);\n  const auto& extrinsic_curvature =\n      db::get<gr::Tags::ExtrinsicCurvature<DataVector, Dim, Frame>>(box);\n  const auto& spatial_christoffel_second_kind =\n      db::get<gr::Tags::SpatialChristoffelSecondKind<DataVector, Dim, Frame>>(\n          box);\n  CHECK_ITERABLE_APPROX(inverse_spatial_metric,\n                        expected_inverse_spatial_metric);\n  CHECK_ITERABLE_APPROX(extrinsic_curvature, expected_extrinsic_curvature);\n  CHECK_ITERABLE_APPROX(spatial_christoffel_second_kind,\n                        expected_spatial_christoffel_second_kind);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.GeneralRelativity.Surfaces.ComputeItems\",\n    \"[PointwiseFunctions][Unit]\") {\n  const DataVector used_for_size(20);\n  // Need only Dim=3 and DataVectors for apparent horizons.\n  test_strahlkorper_compute_items<3, Frame::Inertial>(used_for_size);\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::InverseSpatialMetricCompute<3, Frame::Inertial>>(\n      \"InverseSpatialMetric\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::ExtrinsicCurvatureCompute<3, Frame::Inertial>>(\n      \"ExtrinsicCurvature\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::SpatialChristoffelSecondKindCompute<3,\n                                                              Frame::Inertial>>(\n      \"SpatialChristoffelSecondKind\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Surfaces/Test_GrSurfaces.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/NumericalAlgorithms/SphericalHarmonics/StrahlkorperTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/Surfaces/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/StrahlkorperFunctions.hpp\"\n#include \"NumericalAlgorithms/SphericalHarmonics/Tags.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/HarmonicSchwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/Minkowski.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Expansion1D.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/AreaElement.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Expansion.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/GradUnitNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/InverseSurfaceMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Mass.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/RadialDistance.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/RicciScalar.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Spin.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfScalar.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/SurfaceIntegralOfVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/UnitNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename Solution, typename Fr, typename ExpectedLambda>\nvoid test_expansion(const Solution& solution,\n                    const ylm::Strahlkorper<Fr>& strahlkorper,\n                    const ExpectedLambda& expected) {\n  // Make databox from surface\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& deriv_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(vars);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const DataVector one_over_one_form_magnitude =\n      1.0 /\n      get(magnitude(db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n                    inverse_spatial_metric));\n  const auto unit_normal_one_form = gr::surfaces::unit_normal_one_form(\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n      one_over_one_form_magnitude);\n  const auto grad_unit_normal_one_form =\n      gr::surfaces::grad_unit_normal_one_form(\n          db::get<ylm::Tags::Rhat<Frame::Inertial>>(box),\n          db::get<ylm::Tags::Radius<Frame::Inertial>>(box),\n          unit_normal_one_form,\n          db::get<ylm::Tags::D2xRadius<Frame::Inertial>>(box),\n          one_over_one_form_magnitude,\n          raise_or_lower_first_index(\n              gr::christoffel_first_kind(deriv_spatial_metric),\n              inverse_spatial_metric));\n  const auto inverse_surface_metric = gr::surfaces::inverse_surface_metric(\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric),\n      inverse_spatial_metric);\n\n  const auto residual = gr::surfaces::expansion(\n      grad_unit_normal_one_form, inverse_surface_metric,\n      gr::extrinsic_curvature(\n          get<gr::Tags::Lapse<DataVector>>(vars),\n          get<gr::Tags::Shift<DataVector, 3>>(vars),\n          get<Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                          Frame::Inertial>>(vars),\n          spatial_metric,\n          get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars),\n          deriv_spatial_metric));\n\n  Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(get(residual), expected(get(residual).size()),\n                               custom_approx);\n}\n\nnamespace TestExtrinsicCurvature {\nvoid test_minkowski() {\n  // Make surface of radius 2.\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      ylm::Strahlkorper<Frame::Inertial>(8, 8, 2.0, {{0.0, 0.0, 0.0}}));\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  gr::Solutions::Minkowski<3> solution{};\n\n  const auto deriv_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(\n          solution.variables(\n              cart_coords, t,\n              tmpl::list<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                     tmpl::size_t<3>, Frame::Inertial>>{}));\n  const auto inverse_spatial_metric =\n      get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(solution.variables(\n          cart_coords, t,\n          tmpl::list<gr::Tags::InverseSpatialMetric<DataVector, 3>>{}));\n\n  const DataVector one_over_one_form_magnitude =\n      1.0 /\n      get(magnitude(db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n                    inverse_spatial_metric));\n  const auto unit_normal_one_form = gr::surfaces::unit_normal_one_form(\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n      one_over_one_form_magnitude);\n  const auto grad_unit_normal_one_form =\n      gr::surfaces::grad_unit_normal_one_form(\n          db::get<ylm::Tags::Rhat<Frame::Inertial>>(box),\n          db::get<ylm::Tags::Radius<Frame::Inertial>>(box),\n          unit_normal_one_form,\n          db::get<ylm::Tags::D2xRadius<Frame::Inertial>>(box),\n          one_over_one_form_magnitude,\n          raise_or_lower_first_index(\n              gr::christoffel_first_kind(deriv_spatial_metric),\n              inverse_spatial_metric));\n\n  const auto unit_normal_vector =\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric);\n  const auto extrinsic_curvature = gr::surfaces::extrinsic_curvature(\n      grad_unit_normal_one_form, unit_normal_one_form, unit_normal_vector);\n  const auto extrinsic_curvature_minkowski =\n      TestHelpers::Minkowski::extrinsic_curvature_sphere(cart_coords);\n\n  CHECK_ITERABLE_APPROX(extrinsic_curvature, extrinsic_curvature_minkowski);\n}\n}  // namespace TestExtrinsicCurvature\n\ntemplate <typename Solution, typename SpatialRicciScalar,\n          typename ExpectedLambda>\nvoid test_ricci_scalar(const Solution& solution,\n                       const SpatialRicciScalar& spatial_ricci_scalar,\n                       const ExpectedLambda& expected) {\n  // Make surface of radius 2.\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      ylm::Strahlkorper<Frame::Inertial>(8, 8, 2.0, {{0.0, 0.0, 0.0}}));\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto& deriv_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(vars);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const DataVector one_over_one_form_magnitude =\n      1.0 /\n      get(magnitude(db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n                    inverse_spatial_metric));\n  const auto unit_normal_one_form = gr::surfaces::unit_normal_one_form(\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n      one_over_one_form_magnitude);\n  const auto grad_unit_normal_one_form =\n      gr::surfaces::grad_unit_normal_one_form(\n          db::get<ylm::Tags::Rhat<Frame::Inertial>>(box),\n          db::get<ylm::Tags::Radius<Frame::Inertial>>(box),\n          unit_normal_one_form,\n          db::get<ylm::Tags::D2xRadius<Frame::Inertial>>(box),\n          one_over_one_form_magnitude,\n          raise_or_lower_first_index(\n              gr::christoffel_first_kind(deriv_spatial_metric),\n              inverse_spatial_metric));\n\n  const auto unit_normal_vector =\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric);\n  const auto ricci_scalar = gr::surfaces::ricci_scalar(\n      spatial_ricci_scalar(cart_coords), unit_normal_vector,\n      gr::surfaces::extrinsic_curvature(\n          grad_unit_normal_one_form, unit_normal_one_form, unit_normal_vector),\n      inverse_spatial_metric);\n\n  CHECK_ITERABLE_APPROX(get(ricci_scalar), expected(get(ricci_scalar).size()));\n}\n\ntemplate <typename Solution, typename ExpectedLambda>\nvoid test_area_element(const Solution& solution, const double surface_radius,\n                       const ExpectedLambda& expected) {\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      ylm::Strahlkorper<Frame::Inertial>(8, 8, surface_radius,\n                                         {{0.0, 0.0, 0.0}}));\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame::Inertial>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame::Inertial>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame::Inertial>>(box);\n\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n\n  CHECK_ITERABLE_APPROX(get(area_element), expected(get(area_element).size()));\n}\n\nvoid test_euclidean_surface_integral_of_vector(\n    const ylm::Strahlkorper<Frame::Inertial>& strahlkorper) {\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame::Inertial>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame::Inertial>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame::Inertial>>(box);\n\n  const auto euclidean_area_element = gr::surfaces::euclidean_area_element(\n      jacobian, normal_one_form, radius, r_hat);\n\n  // Create arbitrary vector\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const auto test_vector = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&generator), make_not_null(&dist), radius);\n\n  // Test against integrating this scalar.\n  const auto scalar = Scalar<DataVector>(\n      get(dot_product(test_vector, normal_one_form)) /\n      sqrt(get(dot_product(normal_one_form, normal_one_form))));\n\n  const auto integral_1 = gr::surfaces::surface_integral_of_scalar(\n      euclidean_area_element, scalar, strahlkorper);\n  const auto integral_2 = gr::surfaces::euclidean_surface_integral_of_vector(\n      euclidean_area_element, test_vector, normal_one_form, strahlkorper);\n\n  Approx custom_approx = Approx::custom().epsilon(1.e-13).scale(1.0);\n  CHECK(integral_1 == custom_approx(integral_2));\n}\n\ntemplate <typename Solution, typename Frame>\nvoid test_euclidean_surface_integral_of_vector_2(\n    const Solution& solution, const ylm::Strahlkorper<Frame>& strahlkorper,\n    double expected_area) {\n  // Another test:  Integrate (assuming Euclidean metric) the vector\n  // V^i = s_j \\delta^{ij} (s_k s_l \\delta^{kl})^{-1/2} A A_euclid^{-1}\n  // where s_j is the unnormalized Strahlkorper normal one-form,\n  // A is the correct (curved) area element in Kerr,\n  // and A_euclid is the euclidean area element.\n  // This integral should give the area of the horizon, which is\n  // 16 pi M_irr^2 = 8 pi M^2 (1 + sqrt(1-chi^2))\n\n  const auto box =\n      db::create<db::AddSimpleTags<ylm::Tags::items_tags<Frame>>,\n                 db::AddComputeTags<ylm::Tags::compute_items_tags<Frame>>>(\n          strahlkorper);\n\n  // Get spatial metric\n  const double t = 0.0;\n  const auto& cart_coords = db::get<ylm::Tags::CartesianCoords<Frame>>(box);\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, Frame>>(vars);\n\n  // Get everything we need for the integral\n  const auto& normal_one_form = db::get<ylm::Tags::NormalOneForm<Frame>>(box);\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame>>(box);\n  const auto euclidean_area_element = gr::surfaces::euclidean_area_element(\n      jacobian, normal_one_form, radius, r_hat);\n\n  // Make the test vector\n  // V^i = s_j \\delta^{ij} (s_k s_l \\delta^{kl})^{-1/2} A A_euclid^{-1}\n  // where A is the area element and A_euclid is the euclidean area element.\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n  const auto test_vector_factor = Scalar<DataVector>(\n      get(area_element) / get(euclidean_area_element) /\n      sqrt(get(dot_product(normal_one_form, normal_one_form))));\n  auto test_vector = make_with_value<tnsr::I<DataVector, 3, Frame>>(r_hat, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    test_vector.get(i) = normal_one_form.get(i) * get(test_vector_factor);\n  }\n\n  const auto area_integral = gr::surfaces::euclidean_surface_integral_of_vector(\n      euclidean_area_element, test_vector, normal_one_form, strahlkorper);\n\n  // approx here because we are integrating over a Strahlkorper\n  // at finite resolution.\n  CHECK(area_integral == approx(expected_area));\n}\n\nvoid test_euclidean_area_element(\n    const ylm::Strahlkorper<Frame::Inertial>& strahlkorper) {\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  // Create a Minkowski metric.\n  const gr::Solutions::Minkowski<3> solution{};\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n  const auto vars = solution.variables(\n      cart_coords, t, gr::Solutions::Minkowski<3>::tags<DataVector>{});\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame::Inertial>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame::Inertial>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame::Inertial>>(box);\n\n  // We are using a flat metric, so area_element and euclidean_area_element\n  // should be the same, and this is what we test.\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n  const auto euclidean_area_element = gr::surfaces::euclidean_area_element(\n      jacobian, normal_one_form, radius, r_hat);\n\n  CHECK_ITERABLE_APPROX(get(euclidean_area_element), get(area_element));\n}\n\ntemplate <typename Solution, typename Fr>\nvoid test_area(const Solution& solution,\n               const ylm::Strahlkorper<Fr>& strahlkorper, const double expected,\n               const double expected_irreducible_mass,\n               const double dimensionful_spin_magnitude,\n               const double expected_christodoulou_mass) {\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame::Inertial>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame::Inertial>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame::Inertial>>(box);\n\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n\n  const double area =\n      strahlkorper.ylm_spherepack().definite_integral(get(area_element).data());\n\n  CHECK_ITERABLE_APPROX(area, expected);\n\n  const double irreducible_mass = gr::surfaces::irreducible_mass(area);\n  CHECK(irreducible_mass == approx(expected_irreducible_mass));\n\n  const double christodoulou_mass = gr::surfaces::christodoulou_mass(\n      dimensionful_spin_magnitude, irreducible_mass);\n  CHECK(christodoulou_mass == approx(expected_christodoulou_mass));\n}\n\n// Let I_1 = surface_integral_of_scalar(J^i \\tilde{s}_i \\sqrt(-g))\n// where J^i is some arbitrary vector (representing a flux through the\n// surface), g is the determinant of the spacetime metric,\n// \\tilde{s}_i is the spatial unit one-form to the Strahlkorper,\n// normalized with the flat metric \\tilde{s}_i \\tilde{s}_j \\delta^{ij} = 1,\n// and where euclidean_area_element is passed into surface_integral_of_scalar.\n//\n// Let I_2 = surface_integral_of_scalar(J^i s_i \\alpha), where \\alpha is the\n// lapse, s_i is the spatial unit one-form to the Strahlkorper,\n// normalized with the spatial metric s_i s_j \\gamma^{ij} = 1, and where\n// the area_element computed with $\\gamma_{ij}$ is passed into\n// surface_integral_of_scalar.\n//\n// This tests that I_1==I_2 for an arbitrary 3-vector J^i.\ntemplate <typename Solution, typename Frame>\nvoid test_integral_correspondence(\n    const Solution& solution, const ylm::Strahlkorper<Frame>& strahlkorper) {\n  const auto box =\n      db::create<db::AddSimpleTags<ylm::Tags::items_tags<Frame>>,\n                 db::AddComputeTags<ylm::Tags::compute_items_tags<Frame>>>(\n          strahlkorper);\n\n  const double t = 0.0;\n  const auto& cart_coords = db::get<ylm::Tags::CartesianCoords<Frame>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, Frame>>(vars);\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto det_and_inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric);\n  const auto& det_spatial_metric = det_and_inverse_spatial_metric.first;\n  const auto& inverse_spatial_metric = det_and_inverse_spatial_metric.second;\n\n  const auto& normal_one_form = db::get<ylm::Tags::NormalOneForm<Frame>>(box);\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame>>(box);\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n  const auto euclidean_area_element = gr::surfaces::euclidean_area_element(\n      jacobian, normal_one_form, radius, r_hat);\n\n  const auto normal_one_form_euclidean_magnitude =\n      dot_product(normal_one_form, normal_one_form);\n  const auto normal_one_form_magnitude =\n      dot_product(normal_one_form, normal_one_form, inverse_spatial_metric);\n\n  auto unit_normal_one_form_flat = normal_one_form;\n  auto unit_normal_one_form_curved = normal_one_form;\n  for (size_t i = 0; i < 3; ++i) {\n    unit_normal_one_form_flat.get(i) /=\n        sqrt(get(normal_one_form_euclidean_magnitude));\n    unit_normal_one_form_curved.get(i) /= sqrt(get(normal_one_form_magnitude));\n  }\n\n  // Set up random values for test_vector\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  const auto test_vector = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&generator), make_not_null(&dist), lapse);\n\n  const auto scalar_1 = Scalar<DataVector>(\n      get(dot_product(test_vector, unit_normal_one_form_flat)) * get(lapse) *\n      sqrt(get(det_spatial_metric)));\n  const auto scalar_2 = Scalar<DataVector>(\n      get(dot_product(test_vector, unit_normal_one_form_curved)) * get(lapse));\n\n  const double integral_1 = gr::surfaces::surface_integral_of_scalar(\n      euclidean_area_element, scalar_1, strahlkorper);\n  const double integral_2 = gr::surfaces::surface_integral_of_scalar(\n      area_element, scalar_2, strahlkorper);\n  Approx custom_approx = Approx::custom().epsilon(1.e-13).scale(1.0);\n  CHECK(integral_1 == custom_approx(integral_2));\n}\n\ntemplate <typename Solution, typename Fr>\nvoid test_surface_integral_of_scalar(const Solution& solution,\n                                     const ylm::Strahlkorper<Fr>& strahlkorper,\n                                     const double expected) {\n  const auto box =\n      db::create<db::AddSimpleTags<ylm::Tags::items_tags<Fr>>,\n                 db::AddComputeTags<ylm::Tags::compute_items_tags<Fr>>>(\n          strahlkorper);\n\n  const double t = 0.0;\n  const auto& cart_coords = db::get<ylm::Tags::CartesianCoords<Fr>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3, Fr>>(vars);\n\n  const auto& normal_one_form = db::get<ylm::Tags::NormalOneForm<Fr>>(box);\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Fr>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Fr>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Fr>>(box);\n\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n\n  auto scalar = make_with_value<Scalar<DataVector>>(radius, 0.0);\n  get(scalar) = square(get<0>(cart_coords));\n\n  const double integral = gr::surfaces::surface_integral_of_scalar(\n      area_element, scalar, strahlkorper);\n\n  CHECK_ITERABLE_APPROX(integral, expected);\n}\n\ntemplate <typename Solution, typename Fr>\nvoid test_spin_function(const Solution& solution,\n                        const ylm::Strahlkorper<Fr>& strahlkorper,\n                        const double expected) {\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const DataVector one_over_one_form_magnitude =\n      1.0 /\n      get(magnitude(db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n                    inverse_spatial_metric));\n  const auto unit_normal_one_form = gr::surfaces::unit_normal_one_form(\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n      one_over_one_form_magnitude);\n  const auto unit_normal_vector =\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric);\n\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame::Inertial>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame::Inertial>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame::Inertial>>(box);\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n\n  const auto& ylm = strahlkorper.ylm_spherepack();\n\n  const auto& deriv_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(vars);\n  const auto extrinsic_curvature = gr::extrinsic_curvature(\n      get<gr::Tags::Lapse<DataVector>>(vars),\n      get<gr::Tags::Shift<DataVector, 3>>(vars),\n      get<Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(vars),\n      spatial_metric,\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars),\n      deriv_spatial_metric);\n\n  const auto& tangents = db::get<ylm::Tags::Tangents<Frame::Inertial>>(box);\n\n  const auto spin_function =\n      gr::surfaces::spin_function(tangents, strahlkorper, unit_normal_vector,\n                                  area_element, extrinsic_curvature);\n\n  auto integrand = spin_function;\n  get(integrand) *= get(area_element) * get(spin_function);\n\n  const double integral = ylm.definite_integral(get(integrand).data());\n\n  Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(integral, expected, custom_approx);\n}\n\ntemplate <typename Solution, typename Fr>\nvoid test_dimensionful_spin_magnitude(\n    const Solution& solution, const ylm::Strahlkorper<Fr>& strahlkorper,\n    const double mass, const std::array<double, 3> dimensionless_spin,\n    const Scalar<DataVector>& horizon_radius_with_spin_on_z_axis,\n    const ylm::Spherepack& ylm_with_spin_on_z_axis, const double expected,\n    const double tolerance) {\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const DataVector one_over_one_form_magnitude =\n      1.0 /\n      get(magnitude(db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n                    inverse_spatial_metric));\n  const auto unit_normal_one_form = gr::surfaces::unit_normal_one_form(\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n      one_over_one_form_magnitude);\n  const auto unit_normal_vector =\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric);\n\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame::Inertial>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame::Inertial>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame::Inertial>>(box);\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n\n  const auto& ylm = strahlkorper.ylm_spherepack();\n\n  const auto& deriv_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(vars);\n  const auto extrinsic_curvature = gr::extrinsic_curvature(\n      get<gr::Tags::Lapse<DataVector>>(vars),\n      get<gr::Tags::Shift<DataVector, 3>>(vars),\n      get<Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(vars),\n      spatial_metric,\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars),\n      deriv_spatial_metric);\n\n  const auto& tangents = db::get<ylm::Tags::Tangents<Frame::Inertial>>(box);\n\n  const auto spin_function =\n      gr::surfaces::spin_function(tangents, strahlkorper, unit_normal_vector,\n                                  area_element, extrinsic_curvature);\n\n  const auto grad_unit_normal_one_form =\n      gr::surfaces::grad_unit_normal_one_form(\n          db::get<ylm::Tags::Rhat<Frame::Inertial>>(box),\n          db::get<ylm::Tags::Radius<Frame::Inertial>>(box),\n          unit_normal_one_form,\n          db::get<ylm::Tags::D2xRadius<Frame::Inertial>>(box),\n          one_over_one_form_magnitude,\n          raise_or_lower_first_index(\n              gr::christoffel_first_kind(deriv_spatial_metric),\n              inverse_spatial_metric));\n\n  const auto ricci_scalar = TestHelpers::Kerr::horizon_ricci_scalar(\n      horizon_radius_with_spin_on_z_axis, ylm_with_spin_on_z_axis, ylm, mass,\n      dimensionless_spin);\n\n  const double spin_magnitude = gr::surfaces::dimensionful_spin_magnitude(\n      ricci_scalar, spin_function, spatial_metric, tangents, strahlkorper,\n      area_element);\n\n  double spin_magnitude_void = std::numeric_limits<double>::signaling_NaN();\n  gr::surfaces::dimensionful_spin_magnitude(\n      make_not_null(&spin_magnitude_void), ricci_scalar, spin_function,\n      spatial_metric, tangents, strahlkorper, area_element);\n\n  Approx custom_approx = Approx::custom().epsilon(tolerance).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(spin_magnitude, expected, custom_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(spin_magnitude_void, expected, custom_approx);\n}\n\ntemplate <typename Solution, typename Fr>\nvoid test_spin_vector(\n    const Solution& solution, const ylm::Strahlkorper<Fr>& strahlkorper,\n    const double mass, const std::array<double, 3> dimensionless_spin,\n    const Scalar<DataVector>& horizon_radius_with_spin_on_z_axis,\n    const ylm::Spherepack& ylm_with_spin_on_z_axis) {\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const DataVector one_over_one_form_magnitude =\n      1.0 /\n      get(magnitude(db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n                    inverse_spatial_metric));\n  const auto unit_normal_one_form = gr::surfaces::unit_normal_one_form(\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box),\n      one_over_one_form_magnitude);\n  const auto unit_normal_vector =\n      raise_or_lower_index(unit_normal_one_form, inverse_spatial_metric);\n\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame::Inertial>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame::Inertial>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame::Inertial>>(box);\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n\n  const auto& ylm = strahlkorper.ylm_spherepack();\n\n  const auto& deriv_spatial_metric =\n      get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(vars);\n  const auto extrinsic_curvature = gr::extrinsic_curvature(\n      get<gr::Tags::Lapse<DataVector>>(vars),\n      get<gr::Tags::Shift<DataVector, 3>>(vars),\n      get<Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                      Frame::Inertial>>(vars),\n      spatial_metric,\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(vars),\n      deriv_spatial_metric);\n\n  const auto& tangents = db::get<ylm::Tags::Tangents<Frame::Inertial>>(box);\n\n  const auto spin_function =\n      gr::surfaces::spin_function(tangents, strahlkorper, unit_normal_vector,\n                                  area_element, extrinsic_curvature);\n\n  const auto ricci_scalar = TestHelpers::Kerr::horizon_ricci_scalar(\n      horizon_radius_with_spin_on_z_axis, ylm_with_spin_on_z_axis, ylm, mass,\n      dimensionless_spin);\n\n  const auto spin_vector_taking_coords = gr::surfaces::spin_vector(\n      magnitude(dimensionless_spin), area_element, ricci_scalar, spin_function,\n      strahlkorper, cart_coords);\n  CHECK_ITERABLE_APPROX(spin_vector_taking_coords, dimensionless_spin);\n}\n\ntemplate <typename Solution, typename Fr>\nvoid test_dimensionless_spin_magnitude(\n    const Solution& solution, const ylm::Strahlkorper<Fr>& strahlkorper,\n    const double dimensionful_spin_magnitude, const double expected) {\n  const auto box = db::create<\n      db::AddSimpleTags<ylm::Tags::items_tags<Frame::Inertial>>,\n      db::AddComputeTags<ylm::Tags::compute_items_tags<Frame::Inertial>>>(\n      strahlkorper);\n\n  const double t = 0.0;\n  const auto& cart_coords =\n      db::get<ylm::Tags::CartesianCoords<Frame::Inertial>>(box);\n\n  const auto vars = solution.variables(\n      cart_coords, t, typename Solution::template tags<DataVector>{});\n\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n\n  const auto& normal_one_form =\n      db::get<ylm::Tags::NormalOneForm<Frame::Inertial>>(box);\n  const auto& r_hat = db::get<ylm::Tags::Rhat<Frame::Inertial>>(box);\n  const auto& radius = db::get<ylm::Tags::Radius<Frame::Inertial>>(box);\n  const auto& jacobian = db::get<ylm::Tags::Jacobian<Frame::Inertial>>(box);\n\n  const auto area_element = gr::surfaces::area_element(\n      spatial_metric, jacobian, normal_one_form, radius, r_hat);\n\n  const double area =\n      strahlkorper.ylm_spherepack().definite_integral(get(area_element).data());\n\n  const double irreducible_mass = gr::surfaces::irreducible_mass(area);\n\n  const double christodoulou_mass = gr::surfaces::christodoulou_mass(\n      dimensionful_spin_magnitude, irreducible_mass);\n\n  const double dimensionless_spin = gr::surfaces::dimensionless_spin_magnitude(\n      dimensionful_spin_magnitude, christodoulou_mass);\n\n  double dimensionless_spin_void = std::numeric_limits<double>::signaling_NaN();\n  gr::surfaces::dimensionless_spin_magnitude(\n      make_not_null(&dimensionless_spin_void), dimensionful_spin_magnitude,\n      christodoulou_mass);\n\n  CHECK_ITERABLE_APPROX(dimensionless_spin, expected);\n  CHECK_ITERABLE_APPROX(dimensionless_spin_void, expected);\n}\n\n// Testing 1D Expansion calculations for finding horizons\nvoid test_expansion_1d() {\n  using DerivSpatialMetric =\n      ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3, Frame::Inertial>,\n                    tmpl::size_t<3>, Frame::Inertial>;\n\n  // create line of points along x axis\n  const size_t num_points = 10;\n  auto x = make_with_value<tnsr::I<DataVector, 3, Frame::Inertial>>(\n      DataVector(num_points), 0.0);\n  for (size_t i = 0; i < num_points; ++i) {\n    get<0>(x)[i] = 0.8 + static_cast<double>(i) * 0.2;\n  }\n\n  // Harmonic coords for Schwarzschild BH\n  const double mass_sc = 1.0;\n  const auto solution_sc =\n      gr::Solutions::HarmonicSchwarzschild{mass_sc, {{0.0, 0.0, 0.0}}};\n\n  const auto vars_sc = solution_sc.variables(\n      x, 0.0,\n      tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                 gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                 DerivSpatialMetric>{});\n\n  {\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(vars_sc);\n    const auto& extrinsic_curvature =\n        get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(vars_sc);\n\n    const auto& deriv_spatial_metric = get<DerivSpatialMetric>(vars_sc);\n\n    const auto result_sc = gh::expansion1D(spatial_metric, deriv_spatial_metric,\n                                           extrinsic_curvature, x);\n    CAPTURE(result_sc);\n    CAPTURE(mass_sc);\n    CAPTURE(x);\n    // To have a horizon, the sign of the expansion should change\n    CHECK(min(result_sc.get()) < 0.0);\n    CHECK(max(result_sc.get()) > 0.0);\n    // In harmonic coordinates, the expansion should be zero at x = M\n    CHECK((result_sc.get()[1] == 0.0 && get<0>(x)[1] == mass_sc));\n  }\n\n  // BH Isotropic coords\n  {\n    const double mass = 2.0;\n    const auto solution = Xcts::Solutions::Schwarzschild(\n        mass, Xcts::Solutions::SchwarzschildCoordinates::Isotropic);\n\n    const auto vars = solution.variables(\n        x, tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>,\n                      gr::Tags::ExtrinsicCurvature<DataVector, 3>,\n                      DerivSpatialMetric>{});\n\n    const auto& spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(vars);\n    const auto& extrinsic_curvature =\n        get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(vars);\n\n    const auto& deriv_spatial_metric = get<DerivSpatialMetric>(vars);\n\n    const auto result = gh::expansion1D(spatial_metric, deriv_spatial_metric,\n                                        extrinsic_curvature, x);\n    CAPTURE(spatial_metric);\n    CAPTURE(extrinsic_curvature);\n    CAPTURE(x);\n    CAPTURE(result);\n\n    // Need opposite signs for horizon to form\n    CHECK(min(result.get()) < 0.0);\n    CHECK(max(result.get()) > 0.0);\n    // In isotropic coordinates, the expansion should be zero at x = M / 2\n    CHECK((result.get()[1] == 0.0 && get<0>(x)[1] == mass / 2.0));\n  }\n\n  {\n    // Minkowski\n    const double t = 0.0;\n    const gr::Solutions::Minkowski<3> minkowski{};\n    const auto spatial_metric =\n        get<gr::Tags::SpatialMetric<DataVector, 3>>(minkowski.variables(\n            x, t, tmpl::list<gr::Tags::SpatialMetric<DataVector, 3>>{}));\n\n    const auto deriv_spatial_metric =\n        get<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(\n            minkowski.variables(\n                x, t,\n                tmpl::list<Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                       tmpl::size_t<3>, Frame::Inertial>>{}));\n\n    const auto extrinsic_curvature =\n        get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(minkowski.variables(\n            x, t, tmpl::list<gr::Tags::ExtrinsicCurvature<DataVector, 3>>{}));\n\n    const auto result = gh::expansion1D(spatial_metric, deriv_spatial_metric,\n                                        extrinsic_curvature, x);\n    CAPTURE(spatial_metric);\n    CAPTURE(extrinsic_curvature);\n    CAPTURE(x);\n    CAPTURE(result);\n\n    // One of these has to be false b/c flat space should not have a horizon\n    CHECK_FALSE((min(result.get()) <= 0.0 && max(result.get()) > 0.0));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.Expansion\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  const auto sphere =\n      ylm::Strahlkorper<Frame::Inertial>(8, 8, 2.0, {{0.0, 0.0, 0.0}});\n\n  test_expansion(\n      gr::Solutions::KerrSchild{1.0, {{0.0, 0.0, 0.0}}, {{0.0, 0.0, 0.0}}},\n      sphere, [](const size_t size) { return DataVector(size, 0.0); });\n  test_expansion(gr::Solutions::Minkowski<3>{}, sphere,\n                 [](const size_t size) { return DataVector(size, 1.0); });\n\n  constexpr int l_max = 20;\n  const double mass = 4.444;\n  const std::array<double, 3> spin{{0.3, 0.4, 0.5}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n\n  const auto horizon_radius = gr::Solutions::kerr_horizon_radius(\n      ylm::Strahlkorper<Frame::Inertial>(l_max, l_max, 2.0, center)\n          .ylm_spherepack()\n          .theta_phi_points(),\n      mass, spin);\n\n  const auto kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      l_max, l_max, get(horizon_radius), center);\n\n  test_expansion(gr::Solutions::KerrSchild{mass, spin, center}, kerr_horizon,\n                 [](const size_t size) { return DataVector(size, 0.0); });\n\n  // 1D expansion test below\n  test_expansion_1d();\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.ExtrinsicCurvature\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  // N.B.: test_minkowski() fully tests the extrinsic curvature function.\n  // All components of extrinsic curvature of a sphere in flat space\n  // are nontrivial; cf. extrinsic_curvature_sphere()\n  // in GeneralRelativity/Surfaces/TestHelpers.cpp).\n  TestExtrinsicCurvature::test_minkowski();\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.RicciScalar\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  const double mass = 1.0;\n  test_ricci_scalar(\n      gr::Solutions::KerrSchild(mass, {{0.0, 0.0, 0.0}}, {{0.0, 0.0, 0.0}}),\n      [&mass](const auto& cartesian_coords) {\n        return TestHelpers::Schwarzschild::spatial_ricci(cartesian_coords,\n                                                         mass);\n      },\n      [&mass](const size_t size) {\n        return DataVector(size, 0.5 / square(mass));\n      });\n  test_ricci_scalar(\n      gr::Solutions::Minkowski<3>{},\n      [](const auto& cartesian_coords) {\n        return make_with_value<tnsr::ii<DataVector, 3, Frame::Inertial>>(\n            cartesian_coords, 0.0);\n      },\n      [](const size_t size) { return DataVector(size, 0.5); });\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.AreaElement\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  // Check value of dA for a Schwarzschild horizon and a sphere in flat space\n  test_area_element(\n      gr::Solutions::KerrSchild{4.0, {{0.0, 0.0, 0.0}}, {{0.0, 0.0, 0.0}}}, 8.0,\n      [](const size_t size) { return DataVector(size, 64.0); });\n  test_area_element(gr::Solutions::Minkowski<3>{}, 2.0,\n                    [](const size_t size) { return DataVector(size, 4.0); });\n\n  // Check the area of a Kerr horizon\n  constexpr int l_max = 22;\n  const double mass = 4.444;\n  const std::array<double, 3> spin{{0.4, 0.33, 0.22}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n\n  const double kerr_horizon_radius =\n      mass * (1.0 + sqrt(1.0 - square(magnitude(spin))));\n  // Eq. (26.84a) of Thorne and Blandford\n  const double expected_area = 8.0 * M_PI * mass * kerr_horizon_radius;\n  const double expected_irreducible_mass =\n      sqrt(0.5 * mass * kerr_horizon_radius);\n\n  const auto horizon_radius = gr::Solutions::kerr_horizon_radius(\n      ylm::Strahlkorper<Frame::Inertial>(l_max, l_max, 2.0, center)\n          .ylm_spherepack()\n          .theta_phi_points(),\n      mass, spin);\n\n  const auto kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      l_max, l_max, get(horizon_radius), center);\n\n  test_area(gr::Solutions::KerrSchild{mass, spin, center}, kerr_horizon,\n            expected_area, expected_irreducible_mass,\n            square(mass) * magnitude(spin), mass);\n\n  test_euclidean_area_element(kerr_horizon);\n\n  test_integral_correspondence(gr::Solutions::Minkowski<3>{}, kerr_horizon);\n  test_integral_correspondence(gr::Solutions::KerrSchild{mass, spin, center},\n                               kerr_horizon);\n\n  // Check that the two methods of computing the surface integral are\n  // still equal for a surface inside and outside the horizon\n  // (that is, for spacelike and timelike Strahlkorpers).\n  const auto inside_kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      l_max, l_max, 0.9 * get(horizon_radius), center);\n  const auto outside_kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      l_max, l_max, 2.0 * get(horizon_radius), center);\n  test_integral_correspondence(gr::Solutions::KerrSchild{mass, spin, center},\n                               inside_kerr_horizon);\n  test_integral_correspondence(gr::Solutions::KerrSchild{mass, spin, center},\n                               outside_kerr_horizon);\n\n  test_euclidean_surface_integral_of_vector(kerr_horizon);\n  test_euclidean_surface_integral_of_vector_2(\n      gr::Solutions::KerrSchild{mass, spin, center}, kerr_horizon,\n      expected_area);\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.SurfaceIntegralOfScalar\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  // Check the surface integral of a Schwarzschild horizon, using the radius\n  // as the scalar\n  constexpr int l_max = 20;\n  const double mass = 4.444;\n  const std::array<double, 3> spin{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n\n  const double radius = 2.0 * mass;\n\n  // The test will integrate x^2 on a Schwarzschild horizon.\n  // This is the analytic result.\n  const double expected_integral = 4.0 * M_PI * square(square(radius)) / 3.0;\n\n  const auto horizon =\n      ylm::Strahlkorper<Frame::Inertial>(l_max, l_max, radius, center);\n\n  test_surface_integral_of_scalar(gr::Solutions::KerrSchild{mass, spin, center},\n                                  horizon, expected_integral);\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.SpinFunction\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  // Set up Kerr horizon\n  constexpr int l_max = 24;\n  const double mass = 4.444;\n  const std::array<double, 3> spin{{0.4, 0.33, 0.22}};\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n\n  const auto horizon_radius = gr::Solutions::kerr_horizon_radius(\n      ylm::Strahlkorper<Frame::Inertial>(l_max, l_max, 2.0, center)\n          .ylm_spherepack()\n          .theta_phi_points(),\n      mass, spin);\n\n  const auto kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      l_max, l_max, get(horizon_radius), center);\n\n  // Check value of SpinFunction^2 integrated over the surface for\n  // Schwarzschild. Expected result is zero.\n  test_spin_function(\n      gr::Solutions::KerrSchild{mass, {{0.0, 0.0, 0.0}}, center},\n      ylm::Strahlkorper<Frame::Inertial>(l_max, l_max, 8.888, center), 0.0);\n\n  // Check value of SpinFunction^2 integrated over the surface for\n  // Kerr. Derive this by integrating the square of the imaginary\n  // part of Newman-Penrose Psi^2 on the Kerr horizon using the Kinnersley\n  // null tetrad. For example, this integral is set up in the section\n  // ``Spin function for Kerr'' of Geoffrey Lovelace's overleaf note:\n  // https://v2.overleaf.com/read/twdtxchyrtyv\n  // The integral does not have a simple, closed form, so just\n  // enter the expected numerical value for this test.\n  const double expected_spin_function_sq_integral = 4.0 * 0.0125109627941394;\n\n  test_spin_function(gr::Solutions::KerrSchild{mass, spin, center},\n                     kerr_horizon, expected_spin_function_sq_integral);\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.DimensionfulSpinMagnitude\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  const double mass = 2.0;\n  const std::array<double, 3> generic_dimensionless_spin = {{0.12, 0.08, 0.04}};\n  const double expected_dimensionless_spin_magnitude =\n      magnitude(generic_dimensionless_spin);\n  const double expected_spin_magnitude =\n      expected_dimensionless_spin_magnitude * square(mass);\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n\n  const double aligned_tolerance = 1.e-13;\n  const double aligned_l_max = 12;\n  const std::array<double, 3> aligned_dimensionless_spin = {\n      {0.0, 0.0, expected_dimensionless_spin_magnitude}};\n  const auto aligned_horizon_radius = gr::Solutions::kerr_horizon_radius(\n      ylm::Strahlkorper<Frame::Inertial>(aligned_l_max, aligned_l_max, 2.0,\n                                         center)\n          .ylm_spherepack()\n          .theta_phi_points(),\n      mass, aligned_dimensionless_spin);\n  const auto aligned_kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      aligned_l_max, aligned_l_max, get(aligned_horizon_radius), center);\n  test_dimensionful_spin_magnitude(\n      gr::Solutions::KerrSchild{mass, aligned_dimensionless_spin, center},\n      aligned_kerr_horizon, mass, aligned_dimensionless_spin,\n      aligned_horizon_radius, aligned_kerr_horizon.ylm_spherepack(),\n      expected_spin_magnitude, aligned_tolerance);\n\n  const double generic_tolerance = 1.e-13;\n  const int generic_l_max = 12;\n  const double expected_generic_dimensionless_spin_magnitude =\n      magnitude(generic_dimensionless_spin);\n  const double expected_generic_spin_magnitude =\n      expected_generic_dimensionless_spin_magnitude * square(mass);\n  const auto generic_horizon_radius = gr::Solutions::kerr_horizon_radius(\n      ylm::Strahlkorper<Frame::Inertial>(generic_l_max, generic_l_max, 2.0,\n                                         center)\n          .ylm_spherepack()\n          .theta_phi_points(),\n      mass, generic_dimensionless_spin);\n  const auto generic_kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      generic_l_max, generic_l_max, get(generic_horizon_radius), center);\n\n  // Create rotated horizon radius, Strahlkorper, with same spin magnitude\n  // but with spin on the z axis\n  const auto rotated_horizon_radius = gr::Solutions::kerr_horizon_radius(\n      ylm::Strahlkorper<Frame::Inertial>(generic_l_max, generic_l_max, 2.0,\n                                         center)\n          .ylm_spherepack()\n          .theta_phi_points(),\n      mass, aligned_dimensionless_spin);\n  const auto rotated_kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      generic_l_max, generic_l_max, get(rotated_horizon_radius), center);\n  test_dimensionful_spin_magnitude(\n      gr::Solutions::KerrSchild{mass, generic_dimensionless_spin, center},\n      generic_kerr_horizon, mass, generic_dimensionless_spin,\n      rotated_horizon_radius, rotated_kerr_horizon.ylm_spherepack(),\n      expected_generic_spin_magnitude, generic_tolerance);\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.SpinVector\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  // Set up Kerr horizon\n  constexpr int l_max = 20;\n  const double mass = 2.0;\n  const std::array<double, 3> spin{{0.04, 0.08, 0.12}};\n  const auto spin_magnitude = magnitude(spin);\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n\n  const auto horizon_radius = gr::Solutions::kerr_horizon_radius(\n      ylm::Strahlkorper<Frame::Inertial>(l_max, l_max, 2.0, center)\n          .ylm_spherepack()\n          .theta_phi_points(),\n      mass, spin);\n  const auto kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      l_max, l_max, get(horizon_radius), center);\n\n  const auto horizon_radius_with_spin_on_z_axis =\n      gr::Solutions::kerr_horizon_radius(\n          ylm::Strahlkorper<Frame::Inertial>(l_max, l_max, 2.0, center)\n              .ylm_spherepack()\n              .theta_phi_points(),\n          mass, {{0.0, 0.0, spin_magnitude}});\n  const auto kerr_horizon_with_spin_on_z_axis =\n      ylm::Strahlkorper<Frame::Inertial>(\n          l_max, l_max, get(horizon_radius_with_spin_on_z_axis), center);\n\n  // Check that the gr::surfaces::spin_vector() correctly recovers the\n  // chosen dimensionless spin\n  test_spin_vector(gr::Solutions::KerrSchild{mass, spin, center}, kerr_horizon,\n                   mass, spin, horizon_radius_with_spin_on_z_axis,\n                   kerr_horizon_with_spin_on_z_axis.ylm_spherepack());\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.DimensionlessSpinMagnitude\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  // Set up Kerr solution\n  const double mass = 4.444;\n  const std::array<double, 3> center{{0.0, 0.0, 0.0}};\n  const std::array<double, 3> spin{{0.12, 0.08, 0.04}};\n\n  // Set up Kerr horizon\n  const double l_max = 20;\n  const auto horizon_radius = gr::Solutions::kerr_horizon_radius(\n      ylm::Strahlkorper<Frame::Inertial>(l_max, l_max, 2.0, center)\n          .ylm_spherepack()\n          .theta_phi_points(),\n      mass, spin);\n\n  const auto kerr_horizon = ylm::Strahlkorper<Frame::Inertial>(\n      l_max, l_max, get(horizon_radius), center);\n\n  // Set up dimensionful spin magnitude\n  const double spin_magnitude = magnitude(spin);\n  const double dimful_spin_magnitude = spin_magnitude * square(mass);\n\n  // Expected dimensionless spin magnitude\n  const double kerr_radius = mass * (1.0 + sqrt(1.0 - square(spin_magnitude)));\n  const double irreducible_mass = sqrt(0.5 * mass * kerr_radius);\n  const double christodoulou_mass =\n      sqrt(square(irreducible_mass) +\n           square(dimful_spin_magnitude) / (4.0 * square(irreducible_mass)));\n\n  const double expected_dimless_spin_magnitude =\n      dimful_spin_magnitude / square(christodoulou_mass);\n\n  // Test function  for dimensionless spin magnitude\n  test_dimensionless_spin_magnitude(\n      gr::Solutions::KerrSchild{mass, spin, center}, kerr_horizon,\n      dimful_spin_magnitude, expected_dimless_spin_magnitude);\n}\n\nSPECTRE_TEST_CASE(\"Unit.GrSurfaces.RadialDistance\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  const double y11_amplitude = 1.0;\n  const double radius = 2.0;\n  const std::array<double, 3> center = {{0.1, 0.2, 0.3}};\n  const auto strahlkorper_a =\n      ylm::TestHelpers::create_strahlkorper_y11(y11_amplitude, radius, center);\n  const auto strahlkorper_b = ylm::TestHelpers::create_strahlkorper_y11(\n      4.0 * y11_amplitude, radius, center);\n  const Scalar<DataVector> expected_radial_dist_a_minus_b{\n      get(ylm::radius(strahlkorper_a)) - get(ylm::radius(strahlkorper_b))};\n  const Scalar<DataVector> expected_radial_dist_b_minus_a{\n      -get(expected_radial_dist_a_minus_b)};\n\n  Scalar<DataVector> radial_dist{\n      strahlkorper_a.ylm_spherepack().physical_size()};\n\n  gr::surfaces::radial_distance(make_not_null(&radial_dist), strahlkorper_a,\n                                strahlkorper_b);\n  CHECK_ITERABLE_APPROX(radial_dist, expected_radial_dist_a_minus_b);\n\n  // Check cases where one has more resolution than the other\n  gr::surfaces::radial_distance(\n      make_not_null(&radial_dist), strahlkorper_a,\n      ylm::Strahlkorper<Frame::Inertial>(strahlkorper_b.l_max() - 1,\n                                         strahlkorper_b.m_max() - 1,\n                                         strahlkorper_b));\n  CHECK_ITERABLE_APPROX(radial_dist, expected_radial_dist_a_minus_b);\n  gr::surfaces::radial_distance(make_not_null(&radial_dist),\n                                ylm::Strahlkorper<Frame::Inertial>(\n                                    strahlkorper_b.l_max() - 1,\n                                    strahlkorper_b.m_max() - 1, strahlkorper_b),\n                                strahlkorper_a);\n  CHECK_ITERABLE_APPROX(radial_dist, expected_radial_dist_b_minus_a);\n  gr::surfaces::radial_distance(\n      make_not_null(&radial_dist), strahlkorper_a,\n      ylm::Strahlkorper<Frame::Inertial>(\n          strahlkorper_b.l_max(), strahlkorper_b.m_max() - 1, strahlkorper_b));\n  CHECK_ITERABLE_APPROX(radial_dist, expected_radial_dist_a_minus_b);\n  gr::surfaces::radial_distance(\n      make_not_null(&radial_dist),\n      ylm::Strahlkorper<Frame::Inertial>(\n          strahlkorper_b.l_max(), strahlkorper_b.m_max() - 1, strahlkorper_b),\n      strahlkorper_a);\n  CHECK_ITERABLE_APPROX(radial_dist, expected_radial_dist_b_minus_a);\n\n  Scalar<DataVector> wrong_size_radial_dist{\n      DataVector{strahlkorper_a.ylm_spherepack().physical_size() + 1}};\n  CHECK_THROWS_WITH(\n      (gr::surfaces::radial_distance(make_not_null(&wrong_size_radial_dist),\n                                     strahlkorper_a, strahlkorper_b)),\n      Catch::Matchers::ContainsSubstring(\n          \"both Strahlkorpers have physical_size\"));\n\n  Scalar<DataVector> too_small_for_a{\n      DataVector{strahlkorper_b.ylm_spherepack().physical_size() / 2}};\n  const ylm::Strahlkorper<Frame::Inertial> lower_res_b(\n      strahlkorper_b.l_max() - 1, strahlkorper_b.m_max() - 1, strahlkorper_b);\n  CHECK_THROWS_WITH(\n      (gr::surfaces::radial_distance(make_not_null(&too_small_for_a),\n                                     strahlkorper_a, lower_res_b)),\n      Catch::Matchers::ContainsSubstring(\n          \"Strahlkorper a has higher resolution with physical_size\"));\n\n  Scalar<DataVector> too_small_for_b{\n      DataVector{strahlkorper_a.ylm_spherepack().physical_size()}};\n  const ylm::Strahlkorper<Frame::Inertial> higher_res_b(\n      strahlkorper_b.l_max() + 1, strahlkorper_b.m_max() + 1, strahlkorper_b);\n  CHECK_THROWS_WITH(\n      (gr::surfaces::radial_distance(make_not_null(&too_small_for_b),\n                                     strahlkorper_a, higher_res_b)),\n      Catch::Matchers::ContainsSubstring(\n          \"Strahlkorper b has higher resolution with physical_size\"));\n\n  CHECK_THROWS_WITH(\n      ([&strahlkorper_a, &y11_amplitude, &radius, &radial_dist]() {\n        const std::array<double, 3> different_center{{0.1, 0.4, 0.3}};\n        const auto strahlkorper_off_center =\n            ylm::TestHelpers::create_strahlkorper_y11(4.0 * y11_amplitude,\n                                                      radius, different_center);\n        gr::surfaces::radial_distance(make_not_null(&radial_dist),\n                                      strahlkorper_a, strahlkorper_off_center);\n      }()),\n      Catch::Matchers::ContainsSubstring(\n          \"Currently computing the radial distance between\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Surfaces/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp\"\n\nnamespace {\nstruct SomeType {};\nstruct SomeTag : db::SimpleTag {\n  using type = SomeType;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.Surfaces.Tags\",\n                  \"[ApparentHorizonFinder][Unit]\") {\n  TestHelpers::db::test_simple_tag<gr::surfaces::Tags::Area>(\"Area\");\n  TestHelpers::db::test_simple_tag<gr::surfaces::Tags::IrreducibleMass>(\n      \"IrreducibleMass\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::OneOverOneFormMagnitude>(\n      \"OneOverOneFormMagnitude\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::RicciScalar>(\"RicciScalar\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::MaxRicciScalar>(\"MaxRicciScalar\");\n  TestHelpers::db::test_simple_tag<ylm::Tags::MinRicciScalar>(\"MinRicciScalar\");\n  TestHelpers::db::test_simple_tag<gr::surfaces::Tags::SpinFunction>(\n      \"SpinFunction\");\n  TestHelpers::db::test_simple_tag<\n      gr::surfaces::Tags::DimensionfulSpinMagnitude>(\n      \"DimensionfulSpinMagnitude\");\n  TestHelpers::db::test_simple_tag<gr::surfaces::Tags::ChristodoulouMass>(\n      \"ChristodoulouMass\");\n  TestHelpers::db::test_simple_tag<\n      ylm::Tags::UnitNormalOneForm<Frame::Inertial>>(\"UnitNormalOneForm\");\n  TestHelpers::db::test_simple_tag<\n      ylm::Tags::GradUnitNormalOneForm<Frame::Inertial>>(\n      \"GradUnitNormalOneForm\");\n  TestHelpers::db::test_simple_tag<\n      ylm::Tags::ExtrinsicCurvature<Frame::Inertial>>(\"ExtrinsicCurvature\");\n  TestHelpers::db::test_simple_tag<\n      ylm::Tags::UnitNormalVector<Frame::Inertial>>(\"UnitNormalVector\");\n  TestHelpers::db::test_simple_tag<\n      ylm::Tags::EuclideanAreaElement<Frame::Inertial>>(\"EuclideanAreaElement\");\n  TestHelpers::db::test_simple_tag<\n      ylm::Tags::EuclideanSurfaceIntegral<SomeTag, Frame::Inertial>>(\n      \"EuclideanSurfaceIntegral(SomeTag)\");\n  TestHelpers::db::test_simple_tag<\n      ylm::Tags::EuclideanSurfaceIntegralVector<SomeTag, Frame::Inertial>>(\n      \"EuclideanSurfaceIntegralVector(SomeTag)\");\n  TestHelpers::db::test_simple_tag<\n      gr::surfaces::Tags::AreaElement<Frame::Inertial>>(\"AreaElement\");\n  TestHelpers::db::test_simple_tag<\n      gr::surfaces::Tags::SurfaceIntegral<SomeTag, Frame::Inertial>>(\n      \"SurfaceIntegral(SomeTag)\");\n  TestHelpers::db::test_simple_tag<\n      gr::surfaces::Tags::DimensionfulSpinVector<Frame::Inertial>>(\n      \"DimensionfulSpinVector\");\n  TestHelpers::db::test_simple_tag<\n      gr::surfaces::Tags::DimensionlessSpinVector<Frame::Inertial>>(\n      \"DimensionlessSpinVector\");\n  TestHelpers::db::test_simple_tag<\n      gr::surfaces::Tags::DimensionlessSpinMagnitude<Frame::Inertial>>(\n      \"DimensionlessSpinMagnitude\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::EuclideanAreaElementCompute<Frame::Inertial>>(\n      \"EuclideanAreaElement\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::EuclideanSurfaceIntegralCompute<SomeTag, Frame::Inertial>>(\n      \"EuclideanSurfaceIntegral(SomeTag)\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::EuclideanSurfaceIntegralVectorCompute<SomeTag,\n                                                       Frame::Inertial>>(\n      \"EuclideanSurfaceIntegralVector(SomeTag)\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::AreaElementCompute<Frame::Inertial>>(\"AreaElement\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::SurfaceIntegralCompute<SomeTag, Frame::Inertial>>(\n      \"SurfaceIntegral(SomeTag)\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::AreaCompute<Frame::Inertial>>(\"Area\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::IrreducibleMassCompute<Frame::Inertial>>(\n      \"IrreducibleMass\");\n  TestHelpers::db::test_compute_tag<ylm::Tags::OneOverOneFormMagnitudeCompute<\n      DataVector, 1, Frame::Inertial>>(\"OneOverOneFormMagnitude\");\n  TestHelpers::db::test_compute_tag<ylm::Tags::OneOverOneFormMagnitudeCompute<\n      DataVector, 2, Frame::Inertial>>(\"OneOverOneFormMagnitude\");\n  TestHelpers::db::test_compute_tag<ylm::Tags::OneOverOneFormMagnitudeCompute<\n      DataVector, 3, Frame::Inertial>>(\"OneOverOneFormMagnitude\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::UnitNormalOneFormCompute<Frame::Inertial>>(\n      \"UnitNormalOneForm\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::GradUnitNormalOneFormCompute<Frame::Inertial>>(\n      \"GradUnitNormalOneForm\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::ExtrinsicCurvatureCompute<Frame::Inertial>>(\n      \"ExtrinsicCurvature\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::UnitNormalVectorCompute<Frame::Inertial>>(\"UnitNormalVector\");\n  TestHelpers::db::test_compute_tag<\n      ylm::Tags::RicciScalarCompute<Frame::Inertial>>(\"RicciScalar\");\n  TestHelpers::db::test_compute_tag<ylm::Tags::MaxRicciScalarCompute>(\n      \"MaxRicciScalar\");\n  TestHelpers::db::test_compute_tag<ylm::Tags::MinRicciScalarCompute>(\n      \"MinRicciScalar\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::SpinFunctionCompute<Frame::Inertial>>(\"SpinFunction\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::DimensionfulSpinMagnitudeCompute<Frame::Inertial>>(\n      \"DimensionfulSpinMagnitude\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::ChristodoulouMassCompute<Frame::Inertial>>(\n      \"ChristodoulouMass\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::DimensionfulSpinVectorCompute<Frame::Inertial,\n                                                        Frame::Inertial>>(\n      \"DimensionfulSpinVector\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::DimensionlessSpinVectorCompute<Frame::Inertial,\n                                                         Frame::Inertial>>(\n      \"DimensionlessSpinVector\");\n  TestHelpers::db::test_compute_tag<\n      gr::surfaces::Tags::DimensionlessSpinMagnitudeCompute<Frame::Inertial>>(\n      \"DimensionlessSpinMagnitude\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_Christoffel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\ntemplate <typename Tag, typename Dim, typename Frame>\nstruct deriv;\n}  // namespace Tags\ntemplate <typename X, typename Symm, typename IndexList>\nclass Tensor;\n\nnamespace {\ntemplate <size_t Dim, IndexType Index, typename DataType>\nvoid test_christoffel(const DataType& used_for_size) {\n  tnsr::abb<DataType, Dim, Frame::Inertial, Index> (*f)(\n      const tnsr::abb<DataType, Dim, Frame::Inertial, Index>&) =\n      &gr::christoffel_first_kind<Dim, Frame::Inertial, Index, DataType>;\n  pypp::check_with_random_values<1>(f, \"Christoffel\", \"christoffel_first_kind\",\n                                    {{{-10., 10.}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.Christoffel\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n  const DataVector dv(5);\n  test_christoffel<1, IndexType::Spatial>(dv);\n  test_christoffel<2, IndexType::Spatial>(dv);\n  test_christoffel<3, IndexType::Spatial>(dv);\n  test_christoffel<1, IndexType::Spacetime>(dv);\n  test_christoffel<2, IndexType::Spacetime>(dv);\n  test_christoffel<3, IndexType::Spacetime>(dv);\n  test_christoffel<1, IndexType::Spatial>(0.);\n  test_christoffel<2, IndexType::Spatial>(0.);\n  test_christoffel<3, IndexType::Spatial>(0.);\n  test_christoffel<1, IndexType::Spacetime>(0.);\n  test_christoffel<2, IndexType::Spacetime>(0.);\n  test_christoffel<3, IndexType::Spacetime>(0.);\n\n  // Check that compute items work correctly in the DataBox\n  // First, check that the names are correct\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpatialChristoffelFirstKindCompute<DataVector, 3,\n                                                   Frame::Inertial>>(\n      \"SpatialChristoffelFirstKind\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::TraceSpatialChristoffelFirstKindCompute<DataVector, 3,\n                                                        Frame::Inertial>>(\n      \"TraceSpatialChristoffelFirstKind\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpatialChristoffelSecondKindCompute<DataVector, 3,\n                                                    Frame::Inertial>>(\n      \"SpatialChristoffelSecondKind\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::TraceSpatialChristoffelSecondKindCompute<DataVector, 3,\n                                                         Frame::Inertial>>(\n      \"TraceSpatialChristoffelSecondKind\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpacetimeChristoffelFirstKindCompute<DataVector, 3,\n                                                     Frame::Inertial>>(\n      \"SpacetimeChristoffelFirstKind\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::TraceSpacetimeChristoffelFirstKindCompute<DataVector, 3,\n                                                          Frame::Inertial>>(\n      \"TraceSpacetimeChristoffelFirstKind\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpacetimeChristoffelSecondKindCompute<DataVector, 3,\n                                                      Frame::Inertial>>(\n      \"SpacetimeChristoffelSecondKind\");\n\n  // Check that the compute items return correct values\n  const DataVector used_for_size{3., 4., 5.};\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-0.2, 0.2);\n\n  const auto spacetime_metric = [&]() {\n    auto spacetime_metric_l =\n        make_with_random_values<tnsr::aa<DataVector, 3, Frame::Inertial>>(\n            make_not_null(&generator), make_not_null(&distribution),\n            used_for_size);\n    // Make sure spacetime_metric isn't singular\n    get<0, 0>(spacetime_metric_l) += -1.;\n    for (size_t i = 1; i <= 3; ++i) {\n      spacetime_metric_l.get(i, i) += 1.;\n    }\n    return spacetime_metric_l;\n  }();\n\n  const auto deriv_spatial_metric =\n      make_with_random_values<tnsr::ijj<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto deriv_shift =\n      make_with_random_values<tnsr::iJ<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto deriv_lapse =\n      make_with_random_values<tnsr::i<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n\n  const auto dt_spatial_metric =\n      make_with_random_values<tnsr::ii<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto dt_shift =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto dt_lapse = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  const auto det_and_inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric);\n  const auto& inverse_spatial_metric = det_and_inverse_spatial_metric.second;\n  const auto shift = gr::shift(spacetime_metric, inverse_spatial_metric);\n  const auto lapse = gr::lapse(shift, spacetime_metric);\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n\n  const auto derivatives_of_spacetime_metric =\n      gr::derivatives_of_spacetime_metric(\n          lapse, dt_lapse, deriv_lapse, shift, dt_shift, deriv_shift,\n          spatial_metric, dt_spatial_metric, deriv_spatial_metric);\n\n  const auto expected_spatial_christoffel_first_kind =\n      gr::christoffel_first_kind(deriv_spatial_metric);\n  const auto expected_trace_spatial_christoffel_first_kind = trace_last_indices(\n      expected_spatial_christoffel_first_kind, inverse_spatial_metric);\n  const auto expected_spatial_christoffel_second_kind =\n      raise_or_lower_first_index(expected_spatial_christoffel_first_kind,\n                                 inverse_spatial_metric);\n  const auto expected_trace_spatial_christoffel_second_kind =\n      trace_last_indices(expected_spatial_christoffel_second_kind,\n                         inverse_spatial_metric);\n\n  const auto expected_spacetime_christoffel_first_kind =\n      gr::christoffel_first_kind(derivatives_of_spacetime_metric);\n  const auto expected_trace_spacetime_christoffel_first_kind =\n      trace_last_indices(expected_spacetime_christoffel_first_kind,\n                         inverse_spacetime_metric);\n  const auto expected_spacetime_christoffel_second_kind =\n      raise_or_lower_first_index(expected_spacetime_christoffel_first_kind,\n                                 inverse_spacetime_metric);\n\n  // Compute Christoffel symbol of the 2nd kind in two different ways\n  // (the direct one, which we are testing here, and the one already\n  // tested independently, which uses christoffel_first_kind) and make\n  // sure we get the same result.\n  const auto spatial_christoffel_second_kind_test =\n      gr::christoffel_second_kind(deriv_spatial_metric, inverse_spatial_metric);\n  CHECK_ITERABLE_APPROX(expected_spatial_christoffel_second_kind,\n                        spatial_christoffel_second_kind_test);\n  const auto spacetime_christoffel_second_kind_test =\n      gr::christoffel_second_kind(derivatives_of_spacetime_metric,\n                                  inverse_spacetime_metric);\n  CHECK_ITERABLE_APPROX(expected_spacetime_christoffel_second_kind,\n                        spacetime_christoffel_second_kind_test);\n\n  const auto box = db::create<\n      db::AddSimpleTags<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                        ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        gr::Tags::InverseSpacetimeMetric<DataVector, 3>,\n                        gr::Tags::DerivativesOfSpacetimeMetric<DataVector, 3>>,\n      db::AddComputeTags<\n          gr::Tags::SpatialChristoffelFirstKindCompute<DataVector, 3,\n                                                       Frame::Inertial>,\n          gr::Tags::TraceSpatialChristoffelFirstKindCompute<DataVector, 3,\n                                                            Frame::Inertial>,\n          gr::Tags::SpatialChristoffelSecondKindCompute<DataVector, 3,\n                                                        Frame::Inertial>,\n          gr::Tags::TraceSpatialChristoffelSecondKindCompute<DataVector, 3,\n                                                             Frame::Inertial>,\n          gr::Tags::SpacetimeChristoffelFirstKindCompute<DataVector, 3,\n                                                         Frame::Inertial>,\n          gr::Tags::TraceSpacetimeChristoffelFirstKindCompute<DataVector, 3,\n                                                              Frame::Inertial>,\n          gr::Tags::SpacetimeChristoffelSecondKindCompute<DataVector, 3,\n                                                          Frame::Inertial>>>(\n      inverse_spatial_metric, deriv_spatial_metric, inverse_spacetime_metric,\n      derivatives_of_spacetime_metric);\n\n  CHECK(db::get<gr::Tags::SpatialChristoffelFirstKind<DataVector, 3>>(box) ==\n        expected_spatial_christoffel_first_kind);\n  CHECK(db::get<gr::Tags::TraceSpatialChristoffelFirstKind<DataVector, 3>>(\n            box) == expected_trace_spatial_christoffel_first_kind);\n  CHECK(db::get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>>(box) ==\n        expected_spatial_christoffel_second_kind);\n  CHECK(db::get<gr::Tags::TraceSpatialChristoffelSecondKind<DataVector, 3>>(\n            box) == expected_trace_spatial_christoffel_second_kind);\n\n  CHECK(db::get<gr::Tags::SpacetimeChristoffelFirstKind<DataVector, 3>>(box) ==\n        expected_spacetime_christoffel_first_kind);\n  CHECK(db::get<gr::Tags::TraceSpacetimeChristoffelFirstKind<DataVector, 3>>(\n            box) == expected_trace_spacetime_christoffel_first_kind);\n  CHECK(db::get<gr::Tags::SpacetimeChristoffelSecondKind<DataVector, 3>>(box) ==\n        expected_spacetime_christoffel_second_kind);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_ComputeGhQuantities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <pup.h>\n#include <random>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Trace.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/GeneralizedHarmonic/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Christoffel.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ConstraintGammas.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/CovariantDerivOfExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/DerivSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/GaugeSource.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Phi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Pi.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SecondTimeDerivOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfDetSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivOfNormOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpacetimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/SpatialDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfLowerShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfShift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivOfSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeneralizedHarmonic/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\ntemplate <typename Tag, typename Dim, typename Frame>\nstruct deriv;\n}  // namespace Tags\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_phi(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iaa<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::i<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJ<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&)>(\n          &::gh::phi<DataType, Dim, Frame::Inertial>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"phi\", {{{-1., 1.}}},\n      used_for_size);\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_pi(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::aa<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&, const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iaa<DataType, Dim, Frame::Inertial>&)>(\n          &::gh::pi<DataType, Dim, Frame::Inertial>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"pi\", {{{-1., 1.}}},\n      used_for_size);\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_gauge_source(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::a<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&, const Scalar<DataType>&,\n          const tnsr::i<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJ<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const Scalar<DataType>&,\n          const tnsr::i<DataType, Dim, Frame::Inertial>&)>(\n          &::gh::gauge_source<DataType, Dim, Frame::Inertial>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"gauge_source\", {{{-1., 1.}}},\n      used_for_size, 1.e-11);\n}\n\ntemplate <size_t Dim, typename T>\nvoid test_compute_extrinsic_curvature_and_deriv_metric(const T& used_for_size) {\n  // Set up random values for lapse, shift, spatial_metric,\n  // and their derivatives.\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  std::uniform_real_distribution<> dist_positive(1., 2.);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_dist = make_not_null(&dist);\n  const auto nn_dist_positive = make_not_null(&dist_positive);\n\n  const auto lapse = make_with_random_values<Scalar<T>>(\n      nn_generator, nn_dist_positive, used_for_size);\n  const auto shift = make_with_random_values<tnsr::I<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto spatial_metric = [&]() {\n    auto spatial_metric_l = make_with_random_values<tnsr::ii<T, Dim>>(\n        nn_generator, nn_dist, used_for_size);\n    // Make sure spatial_metric isn't singular by adding\n    // large enough positive diagonal values.\n    for (size_t i = 0; i < Dim; ++i) {\n      spatial_metric_l.get(i, i) += 4.;\n    }\n    return spatial_metric_l;\n  }();\n  const auto dt_lapse = make_with_random_values<Scalar<T>>(\n      nn_generator, nn_dist_positive, used_for_size);\n  const auto deriv_lapse = make_with_random_values<tnsr::i<T, Dim>>(\n      nn_generator, nn_dist_positive, used_for_size);\n  const auto dt_shift = make_with_random_values<tnsr::I<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto deriv_shift = make_with_random_values<tnsr::iJ<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto deriv_spatial_metric = make_with_random_values<tnsr::ijj<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto dt_spatial_metric = make_with_random_values<tnsr::ii<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n\n  // Make extrinsic curvature, spacetime_normal_vector, and generalized\n  // harmonic pi,psi variables in a way that is already independently tested.\n  const auto extrinsic_curvature =\n      gr::extrinsic_curvature(lapse, shift, deriv_shift, spatial_metric,\n                              dt_spatial_metric, deriv_spatial_metric);\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<T, Dim, Frame::Inertial>(lapse);\n  const auto phi = gh::phi(lapse, deriv_lapse, shift, deriv_shift,\n                           spatial_metric, deriv_spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n                         dt_spatial_metric, phi);\n\n  // Compute extrinsic curvature and deriv_spatial_metric from generalized\n  // harmonic variables and make sure we get the same result.\n  const auto extrinsic_curvature_test =\n      gh::extrinsic_curvature(spacetime_normal_vector, pi, phi);\n  const auto deriv_spatial_metric_test = gh::deriv_spatial_metric(phi);\n\n  CHECK_ITERABLE_APPROX(extrinsic_curvature, extrinsic_curvature_test);\n  CHECK_ITERABLE_APPROX(deriv_spatial_metric, deriv_spatial_metric_test);\n\n  // Compute Christoffel symbol of the 2nd kind in two different ways\n  // (the gr one already tested independently) and make sure we get\n  // the same result.\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto christoffel_second_kind =\n      gh::christoffel_second_kind(phi, inverse_spatial_metric);\n  const auto christoffel_second_kind_test = raise_or_lower_first_index(\n      gr::christoffel_first_kind(deriv_spatial_metric), inverse_spatial_metric);\n\n  CHECK_ITERABLE_APPROX(christoffel_second_kind, christoffel_second_kind_test);\n\n  // Test Christoffel trace\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto trace_christoffel = gh::trace_christoffel(\n      spacetime_normal_one_form, spacetime_normal_vector,\n      inverse_spatial_metric, inverse_spacetime_metric, pi, phi);\n  tnsr::abb<T, Dim, Frame::Inertial> d4_spacetime_metric;\n  ::gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&d4_spacetime_metric), lapse, shift, pi, phi);\n  const auto expected_trace_christoffel =\n      trace_last_indices(gr::christoffel_first_kind(d4_spacetime_metric),\n                         inverse_spacetime_metric);\n  CHECK_ITERABLE_APPROX(trace_christoffel, expected_trace_christoffel);\n  tnsr::a<T, Dim, Frame::Inertial> (*f_trace_christoffel)(\n      const tnsr::a<T, Dim, Frame::Inertial>&,\n      const tnsr::A<T, Dim, Frame::Inertial>&,\n      const tnsr::II<T, Dim, Frame::Inertial>&,\n      const tnsr::AA<T, Dim, Frame::Inertial>&,\n      const tnsr::aa<T, Dim, Frame::Inertial>&,\n      const tnsr::iaa<T, Dim, Frame::Inertial>&) =\n      &gh::trace_christoffel<T, Dim, Frame::Inertial>;\n  pypp::check_with_random_values<1>(\n      f_trace_christoffel, \"GeneralRelativity.ComputeGhQuantities\",\n      \"trace_christoffel\", {{{-1., 1.}}}, used_for_size, 1.e-11);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_lapse_deriv_functions(const DataVector& used_for_size) {\n  // spatial_deriv_of_lapse\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::i<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataType>&, const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&)>(\n          &::gh::spatial_deriv_of_lapse<DataType, SpatialDim, Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"deriv_lapse\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size);\n  // time_deriv_of_lapse\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataType> (*)(\n          const Scalar<DataType>&, const tnsr::I<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&)>(\n          &::gh::time_deriv_of_lapse<DataType, SpatialDim, Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"dt_lapse\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size,\n      1.e-11);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_gij_deriv_functions(const DataVector& used_for_size) {\n  // time_deriv_of_spatial_metric\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ii<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataType>&, const tnsr::I<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&)>(\n          &::gh::time_deriv_of_spatial_metric<DataType, SpatialDim, Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"dt_spatial_metric\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size);\n  // spacetime_deriv_of_det_spatial_metric\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::a<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataType>&, const tnsr::II<DataType, SpatialDim, Frame>&,\n          const tnsr::ii<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&)>(\n          &::gh::spacetime_deriv_of_det_spatial_metric<DataType, SpatialDim,\n                                                       Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"spacetime_deriv_detg\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_spacetime_metric_deriv_functions(const DataVector& used_for_size) {\n  // time_derivative_of_spacetime_metric\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::aa<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataType>&, const tnsr::I<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&)>(\n          &::gh::time_derivative_of_spacetime_metric<DataType, SpatialDim,\n                                                     Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"gh_dt_spacetime_metric\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size);\n  // second_time_deriv_of_spacetime_metric\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::aa<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataVector>&, const Scalar<DataVector>&,\n          const tnsr::I<DataVector, SpatialDim, Frame>&,\n          const tnsr::I<DataVector, SpatialDim, Frame>&,\n          const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n          const tnsr::iaa<DataVector, SpatialDim, Frame>&,\n          const tnsr::aa<DataVector, SpatialDim, Frame>&,\n          const tnsr::aa<DataVector, SpatialDim, Frame>&)>(\n          &::gh::second_time_deriv_of_spacetime_metric<DataType, SpatialDim,\n                                                       Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"gh_d2t2_spacetime_metric\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size);\n}\n\n// Test computation of derivs of spacetime metric by comparing to Kerr-Schild\ntemplate <typename Solution>\nvoid test_gab_deriv_functions_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Setup grid\n  const size_t SpatialDim = 3;\n  Mesh<SpatialDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, SpatialDim>>(vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataVector, SpatialDim>>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  const auto phi =\n      gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric, d_spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n                         dt_spatial_metric, phi);\n  const auto dt_phi = make_with_value<decltype(phi)>(phi, 0.);\n  const auto dt_pi = make_with_value<decltype(pi)>(pi, 0.);\n\n  const auto d2t2_spacetime_metric = gh::second_time_deriv_of_spacetime_metric(\n      lapse, dt_lapse, shift, dt_shift, phi, dt_phi, pi, dt_pi);\n\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = a; b < SpatialDim + 1; ++b) {\n      CHECK(d2t2_spacetime_metric.get(a, b) == approx(0.0));\n    };\n  };\n  // CHECK_ITERABLE_APPROX(d2t2_spacetime_metric, 0.);\n}\n\n// Test computation of derivs of lapse by comparing to Kerr-Schild\ntemplate <typename Solution>\nvoid test_lapse_deriv_functions_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Setup grid\n  const size_t spatial_dim = 3;\n  Mesh<spatial_dim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                         Spectral::Quadrature::GaussLobatto};\n\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse_expected =\n      get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse_expected =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, spatial_dim>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataVector, spatial_dim>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, spatial_dim>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, spatial_dim>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Get ingredients\n  const auto phi = gh::phi(lapse, d_lapse_expected, shift, d_shift,\n                           spatial_metric, d_spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse_expected, shift, dt_shift,\n                         spatial_metric, dt_spatial_metric, phi);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n\n  // Check that locally computed derivs match returned ones\n  const auto dt_lapse =\n      gh::time_deriv_of_lapse(lapse, shift, normal_vector, phi, pi);\n\n  const auto d_lapse = gh::spatial_deriv_of_lapse(lapse, normal_vector, phi);\n\n  CHECK_ITERABLE_APPROX(dt_lapse_expected, dt_lapse);\n  CHECK_ITERABLE_APPROX(d_lapse_expected, d_lapse);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_shift_deriv_functions(const DataVector& used_for_size) {\n  // spatial_deriv_of_shift\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iJ<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataType>&, const tnsr::AA<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&)>(\n          &::gh::spatial_deriv_of_shift<DataType, SpatialDim, Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"deriv_shift\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size);\n  // time_deriv_of_shift\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataType>& lapse,\n          const tnsr::I<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&)>(\n          &::gh::time_deriv_of_shift<DataType, SpatialDim, Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"dt_shift\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size,\n      1.e-10);\n  // time_deriv_of_lower_shift\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::i<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataType>&, const tnsr::I<DataType, SpatialDim, Frame>&,\n          const tnsr::ii<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&)>(\n          &::gh::time_deriv_of_lower_shift<DataType, SpatialDim, Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"dt_lower_shift\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size,\n      5.e-9);\n  // spacetime_deriv_of_norm_of_shift\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::a<DataType, SpatialDim, Frame> (*)(\n          const Scalar<DataType>&, const tnsr::I<DataType, SpatialDim, Frame>&,\n          const tnsr::ii<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&,\n          const tnsr::AA<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::aa<DataType, SpatialDim, Frame>&)>(\n          &::gh::spacetime_deriv_of_norm_of_shift<DataType, SpatialDim, Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"spacetime_deriv_norm_shift\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size,\n      1.e-10);\n}\n\n// Test computation of derivs of shift by comparing to Kerr-Schild\ntemplate <typename Solution>\nvoid test_shift_deriv_functions_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Setup grid\n  const size_t SpatialDim = 3;\n  Mesh<SpatialDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, SpatialDim>>(vars);\n  const auto& d_shift_expected =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift_expected =\n      get<Tags::dt<gr::Tags::Shift<DataVector, SpatialDim>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim>>(vars);\n  const auto& dt_spatial_metric =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim>>>(vars);\n  const auto& d_spatial_metric =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n\n  // Get ingredients\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto phi = gh::phi(lapse, d_lapse, shift, d_shift_expected,\n                           spatial_metric, d_spatial_metric);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift_expected,\n                         spatial_metric, dt_spatial_metric, phi);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n  const auto lower_shift = raise_or_lower_index(shift, spatial_metric);\n\n  // Get d4_spacetime_metric for d4_norm_shift\n  const auto d4_spacetime_metric = gr::derivatives_of_spacetime_metric(\n      lapse, dt_lapse, d_lapse, shift, dt_shift_expected, d_shift_expected,\n      spatial_metric, dt_spatial_metric, d_spatial_metric);\n\n  // Compute d4_norm_shift = d4_spacetime_metric_00 + 2 lapse d4_lapse\n  auto d4_norm_of_shift_expected =\n      make_with_value<tnsr::a<DataVector, SpatialDim, Frame::Inertial>>(x, 0.);\n  get<0>(d4_norm_of_shift_expected) =\n      d4_spacetime_metric.get(0, 0, 0) + 2. * get(lapse) * get(dt_lapse);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    d4_norm_of_shift_expected.get(1 + i) =\n        d4_spacetime_metric.get(i + 1, 0, 0) + 2. * get(lapse) * d_lapse.get(i);\n  }\n\n  // Compute dt_norm_of_shift - lower_shift_dot_dt_shift =\n  // shift_dot_dt_lower_shift\n  auto shift_dot_dt_lower_shift_expected =\n      make_with_value<Scalar<DataVector>>(x, 0.);\n  auto lower_shift_dot_dt_shift = make_with_value<Scalar<DataVector>>(x, 0.);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    get(lower_shift_dot_dt_shift) +=\n        lower_shift.get(i) * dt_shift_expected.get(i);\n  }\n  get(shift_dot_dt_lower_shift_expected) =\n      get<0>(d4_norm_of_shift_expected) - get(lower_shift_dot_dt_shift);\n\n  // Check that locally computed derivs match returned ones\n  const auto dt_shift = gh::time_deriv_of_shift(\n      lapse, shift, inverse_spatial_metric, normal_vector, phi, pi);\n\n  const auto d_shift = gh::spatial_deriv_of_shift(\n      lapse, inverse_spacetime_metric, normal_vector, phi);\n\n  const auto d4_norm_shift = gh::spacetime_deriv_of_norm_of_shift(\n      lapse, shift, spatial_metric, inverse_spatial_metric,\n      inverse_spacetime_metric, normal_vector, phi, pi);\n\n  const auto dt_lower_shift = gh::time_deriv_of_lower_shift(\n      lapse, shift, spatial_metric, normal_vector, phi, pi);\n\n  auto shift_dot_dt_lower_shift = make_with_value<Scalar<DataVector>>(x, 0.);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    get(shift_dot_dt_lower_shift) += shift.get(i) * dt_lower_shift.get(i);\n  }\n\n  CHECK_ITERABLE_APPROX(dt_shift_expected, dt_shift);\n  CHECK_ITERABLE_APPROX(d_shift_expected, d_shift);\n  CHECK_ITERABLE_APPROX(d4_norm_of_shift_expected, d4_norm_shift);\n  CHECK_ITERABLE_APPROX(get(shift_dot_dt_lower_shift_expected),\n                        get(shift_dot_dt_lower_shift));\n}\n\n// Test computation of derivs of spatial metric by comparing to Kerr-Schild\ntemplate <typename Solution>\nvoid test_gij_deriv_functions_analytic(\n    const Solution& solution, const size_t grid_size_each_dimension,\n    const std::array<double, 3>& lower_bound,\n    const std::array<double, 3>& upper_bound) {\n  // Setup grid\n  const size_t SpatialDim = 3;\n  Mesh<SpatialDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  // Arbitrary time for time-independent solution.\n  const double t = std::numeric_limits<double>::signaling_NaN();\n  // Evaluate analytic solution\n  const auto vars =\n      solution.variables(x, t, typename Solution::template tags<DataVector>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataVector>>(vars);\n  const auto& dt_lapse = get<Tags::dt<gr::Tags::Lapse<DataVector>>>(vars);\n  const auto& d_lapse =\n      get<typename Solution::template DerivLapse<DataVector>>(vars);\n  const auto& shift = get<gr::Tags::Shift<DataVector, SpatialDim>>(vars);\n  const auto& d_shift =\n      get<typename Solution::template DerivShift<DataVector>>(vars);\n  const auto& dt_shift =\n      get<Tags::dt<gr::Tags::Shift<DataVector, SpatialDim>>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataVector, SpatialDim>>(vars);\n  const auto& dt_spatial_metric_expected =\n      get<Tags::dt<gr::Tags::SpatialMetric<DataVector, SpatialDim>>>(vars);\n  const auto& d_spatial_metric_expected =\n      get<typename Solution::template DerivSpatialMetric<DataVector>>(vars);\n  // Get ingredients\n  const auto det_and_inv = determinant_and_inverse(spatial_metric);\n  const auto inverse_spatial_metric = det_and_inv.second;\n  const auto det_spatial_metric = det_and_inv.first;\n  const Scalar<DataVector> sqrt_det_spatial_metric{\n      sqrt(get(det_spatial_metric))};\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto d4_spacetime_metric = gr::derivatives_of_spacetime_metric(\n      lapse, dt_lapse, d_lapse, shift, dt_shift, d_shift, spatial_metric,\n      dt_spatial_metric_expected, d_spatial_metric_expected);\n  const auto christoffel_first =\n      gr::christoffel_first_kind(d4_spacetime_metric);\n  const auto phi = gh::phi(lapse, d_lapse, shift, d_shift, spatial_metric,\n                           d_spatial_metric_expected);\n  const auto pi = gh::pi(lapse, dt_lapse, shift, dt_shift, spatial_metric,\n                         dt_spatial_metric_expected, phi);\n  const auto normal_vector = gr::spacetime_normal_vector(lapse, shift);\n\n  // Get spacetime deriv of Det[g]:\n  // \\partial_a g = -2 g \\partial_a \\alpha / \\alpha + 2 g \\Gamma^b_{a b}\n  auto d4_g_expected =\n      make_with_value<tnsr::a<DataVector, SpatialDim, Frame::Inertial>>(x, 0.);\n  // \\Gamma^b_{a b} = \\psi^{bc} \\Gamma_{bac}\n  auto inv_psi_dot_christoffel =\n      make_with_value<tnsr::a<DataVector, SpatialDim, Frame::Inertial>>(x, 0.);\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        inv_psi_dot_christoffel.get(a) +=\n            inverse_spacetime_metric.get(b, c) * christoffel_first.get(b, a, c);\n      }\n    }\n  }\n  const auto pre_factor1 = -2. * get(det_spatial_metric) / get(lapse);\n  get<0>(d4_g_expected) =\n      pre_factor1 * get(dt_lapse) +\n      2. * get(det_spatial_metric) * get<0>(inv_psi_dot_christoffel);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    d4_g_expected.get(1 + i) =\n        pre_factor1 * d_lapse.get(i) +\n        2. * get(det_spatial_metric) * inv_psi_dot_christoffel.get(i + 1);\n  }\n\n  // Check that locally computed derivs match returned ones\n  const auto dt_gij = gh::time_deriv_of_spatial_metric(lapse, shift, phi, pi);\n  const auto d_gij = gh::deriv_spatial_metric(phi);\n  const auto d4_g = gh::spacetime_deriv_of_det_spatial_metric(\n      sqrt_det_spatial_metric, inverse_spatial_metric,\n      dt_spatial_metric_expected, phi);\n\n  CHECK_ITERABLE_APPROX(dt_spatial_metric_expected, dt_gij);\n  CHECK_ITERABLE_APPROX(d_spatial_metric_expected, d_gij);\n  CHECK_ITERABLE_APPROX(d4_g_expected, d4_g);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_spacetime_derivative_of_spacetime_metric(const size_t num_pts) {\n  CAPTURE(SpatialDim);\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.1, 1.0);\n\n  const auto lapse = make_with_random_values<Scalar<DataType>>(\n      make_not_null(&generator), make_not_null(&distribution), num_pts);\n  const auto shift =\n      make_with_random_values<tnsr::I<DataType, SpatialDim, Frame>>(\n          make_not_null(&generator), make_not_null(&distribution), num_pts);\n  const auto pi =\n      make_with_random_values<tnsr::aa<DataType, SpatialDim, Frame>>(\n          make_not_null(&generator), make_not_null(&distribution), num_pts);\n  const auto phi =\n      make_with_random_values<tnsr::iaa<DataType, SpatialDim, Frame>>(\n          make_not_null(&generator), make_not_null(&distribution), num_pts);\n\n  const auto expected_dt_spacetime_metric =\n      ::gh::time_derivative_of_spacetime_metric(lapse, shift, pi, phi);\n  tnsr::abb<DataType, SpatialDim, Frame> d4_spacetime_metric;\n  ::gh::spacetime_derivative_of_spacetime_metric(\n      make_not_null(&d4_spacetime_metric), lapse, shift, pi, phi);\n  for (size_t a = 0; a < SpatialDim + 1; ++a) {\n    for (size_t b = 0; b < SpatialDim + 1; ++b) {\n      for (size_t c = 0; c < SpatialDim + 1; ++c) {\n        if (a == 0) {\n          CHECK(d4_spacetime_metric.get(a, b, c) ==\n                expected_dt_spacetime_metric.get(b, c));\n        } else {\n          CHECK(d4_spacetime_metric.get(a, b, c) == phi.get(a - 1, b, c));\n        }\n      }\n    }\n  }\n}\n\ntemplate <size_t SpatialDim, typename Frame, typename DataType>\nvoid test_cov_deriv_extrinsic_curvature(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ijj<DataType, SpatialDim, Frame> (*)(\n          const tnsr::ii<DataType, SpatialDim, Frame>&,\n          const tnsr::A<DataType, SpatialDim, Frame>&,\n          const tnsr::Ijj<DataType, SpatialDim, Frame>&,\n          const tnsr::AA<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::ijaa<DataType, SpatialDim, Frame>&)>(\n          &::gh::covariant_deriv_of_extrinsic_curvature<DataType, SpatialDim,\n                                                        Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\",\n      \"covariant_deriv_extrinsic_curvture\", {{{-1., 1.}}}, used_for_size);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_spatial_ricci_tensor(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ii<DataType, SpatialDim, Frame> (*)(\n          const tnsr::iaa<DataType, SpatialDim, Frame>&,\n          const tnsr::ijaa<DataType, SpatialDim, Frame>&,\n          const tnsr::II<DataType, SpatialDim, Frame>&)>(\n          &::gh::spatial_ricci_tensor<DataType, SpatialDim, Frame>),\n      \"GeneralRelativity.ComputeGhQuantities\", \"gh_spatial_ricci_tensor\",\n      {{{std::numeric_limits<double>::denorm_min(), 1.}}}, used_for_size);\n}\n\n// Test gh::ricci_tensor by comparing to specific values\n// c.f. SpEC\nvoid test_spatial_ricci_tensor_spec(const size_t grid_size_each_dimension,\n                                    const std::array<double, 3>& lower_bound,\n                                    const std::array<double, 3>& upper_bound) {\n  using frame = Frame::Inertial;\n  constexpr size_t VolumeDim = 3;\n  // Setup grid\n  Mesh<VolumeDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                       Spectral::Quadrature::GaussLobatto};\n  const auto coord_map =\n      domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n          Affine3D{\n              Affine{-1., 1., lower_bound[0], upper_bound[0]},\n              Affine{-1., 1., lower_bound[1], upper_bound[1]},\n              Affine{-1., 1., lower_bound[2], upper_bound[2]},\n          });\n\n  // Setup coordinates\n  const auto x_logical = logical_coordinates(mesh);\n  const auto x = coord_map(x_logical);\n  const Direction<VolumeDim> direction(1, Side::Upper);  // +y direction\n  const size_t slice_grid_points =\n      mesh.extents().slice_away(direction.dimension()).product();\n  const auto inertial_coords = [&slice_grid_points, &lower_bound]() {\n    tnsr::I<DataVector, VolumeDim, frame> tmp(slice_grid_points, 0.);\n    // +y direction\n    get<1>(tmp) = 0.5;\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      for (size_t j = 0; j < VolumeDim; ++j) {\n        get<0>(tmp)[i * VolumeDim + j] =\n            lower_bound[0] + 0.5 * static_cast<double>(i);\n        get<2>(tmp)[i * VolumeDim + j] =\n            lower_bound[2] + 0.5 * static_cast<double>(j);\n      }\n    }\n    return tmp;\n  }();\n\n  auto local_inverse_spatial_metric =\n      make_with_value<tnsr::II<DataVector, VolumeDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  auto local_phi =\n      make_with_value<tnsr::iaa<DataVector, VolumeDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  auto local_d_pi =\n      make_with_value<tnsr::iaa<DataVector, VolumeDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  auto local_d_phi =\n      make_with_value<tnsr::ijaa<DataVector, VolumeDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n\n  // Setting inverse_spatial_metric with values chosen to reproduce a result\n  // from SpEC\n  for (size_t i = 0; i < get<0>(inertial_coords).size(); ++i) {\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      local_inverse_spatial_metric.get(0, j)[i] = 41.;\n      local_inverse_spatial_metric.get(1, j)[i] = 43.;\n      local_inverse_spatial_metric.get(2, j)[i] = 47.;\n    }\n  }\n  // Setting pi AND phi with values chosen to reproduce a result from SpEC\n  for (size_t i = 0; i < get<0>(inertial_coords).size(); ++i) {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = 0; b <= VolumeDim; ++b) {\n        // local_pi.get(a, b)[i] = 1.;\n        local_phi.get(0, a, b)[i] = 3.;\n        local_phi.get(1, a, b)[i] = 5.;\n        local_phi.get(2, a, b)[i] = 7.;\n      }\n    }\n  }\n  // Setting local_d_phi with values chosen to reproduce a result from SpEC\n  for (size_t i = 0; i < get<0>(inertial_coords).size(); ++i) {\n    for (size_t a = 0; a <= VolumeDim; ++a) {\n      for (size_t b = 0; b <= VolumeDim; ++b) {\n        local_d_pi.get(0, a, b)[i] = 1.;\n        local_d_phi.get(0, 0, a, b)[i] = 3.;\n        local_d_phi.get(0, 1, a, b)[i] = 5.;\n        local_d_phi.get(0, 2, a, b)[i] = 7.;\n        local_d_pi.get(1, a, b)[i] = 53.;\n        local_d_phi.get(1, 0, a, b)[i] = 59.;\n        local_d_phi.get(1, 1, a, b)[i] = 61.;\n        local_d_phi.get(1, 2, a, b)[i] = 67.;\n        local_d_pi.get(2, a, b)[i] = 71.;\n        local_d_phi.get(2, 0, a, b)[i] = 73.;\n        local_d_phi.get(2, 1, a, b)[i] = 79.;\n        local_d_phi.get(2, 2, a, b)[i] = 83.;\n      }\n    }\n  }\n\n  // Call tested function\n  auto local_ricci_3 = gh::spatial_ricci_tensor(local_phi, local_d_phi,\n                                                local_inverse_spatial_metric);\n\n  // Initialize with values from SpEC\n  auto spec_ricci_3 = local_ricci_3;\n  get<0, 0>(spec_ricci_3) = 153230.;\n  get<0, 1>(spec_ricci_3) = 5283.;\n  get<0, 2>(spec_ricci_3) = -142541.;\n  get<1, 0>(spec_ricci_3) = 5283.;\n  get<1, 1>(spec_ricci_3) = 2497.;\n  get<1, 2>(spec_ricci_3) = -928.;\n  get<2, 2>(spec_ricci_3) = 141189.;\n\n  // Compare with values from SpEC\n  CHECK_ITERABLE_APPROX(local_ricci_3, spec_ricci_3);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.GhQuantities\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"PointwiseFunctions/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_phi, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_pi, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_gauge_source, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_compute_extrinsic_curvature_and_deriv_metric, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_cov_deriv_extrinsic_curvature,\n                                    (1, 2, 3), (Frame::Grid, Frame::Inertial));\n\n  const size_t num_pts = 5;\n  const DataVector used_for_size(num_pts);\n  test_lapse_deriv_functions<DataVector, 1, Frame::Grid>(used_for_size);\n  test_lapse_deriv_functions<DataVector, 2, Frame::Grid>(used_for_size);\n  test_lapse_deriv_functions<DataVector, 3, Frame::Grid>(used_for_size);\n  test_lapse_deriv_functions<DataVector, 1, Frame::Inertial>(used_for_size);\n  test_lapse_deriv_functions<DataVector, 2, Frame::Inertial>(used_for_size);\n  test_lapse_deriv_functions<DataVector, 3, Frame::Inertial>(used_for_size);\n\n  test_gij_deriv_functions<DataVector, 1, Frame::Grid>(used_for_size);\n  test_gij_deriv_functions<DataVector, 2, Frame::Grid>(used_for_size);\n  test_gij_deriv_functions<DataVector, 3, Frame::Grid>(used_for_size);\n  test_gij_deriv_functions<DataVector, 1, Frame::Inertial>(used_for_size);\n  test_gij_deriv_functions<DataVector, 2, Frame::Inertial>(used_for_size);\n  test_gij_deriv_functions<DataVector, 3, Frame::Inertial>(used_for_size);\n\n  test_spacetime_metric_deriv_functions<DataVector, 1, Frame::Grid>(\n      used_for_size);\n  test_spacetime_metric_deriv_functions<DataVector, 2, Frame::Grid>(\n      used_for_size);\n  test_spacetime_metric_deriv_functions<DataVector, 3, Frame::Grid>(\n      used_for_size);\n  test_spacetime_metric_deriv_functions<DataVector, 1, Frame::Inertial>(\n      used_for_size);\n  test_spacetime_metric_deriv_functions<DataVector, 2, Frame::Inertial>(\n      used_for_size);\n  test_spacetime_metric_deriv_functions<DataVector, 3, Frame::Inertial>(\n      used_for_size);\n\n  test_spacetime_derivative_of_spacetime_metric<DataVector, 1, Frame::Grid>(\n      num_pts);\n  test_spacetime_derivative_of_spacetime_metric<DataVector, 2, Frame::Grid>(\n      num_pts);\n  test_spacetime_derivative_of_spacetime_metric<DataVector, 3, Frame::Grid>(\n      num_pts);\n  test_spacetime_derivative_of_spacetime_metric<DataVector, 1, Frame::Inertial>(\n      num_pts);\n  test_spacetime_derivative_of_spacetime_metric<DataVector, 2, Frame::Inertial>(\n      num_pts);\n  test_spacetime_derivative_of_spacetime_metric<DataVector, 3, Frame::Inertial>(\n      num_pts);\n\n  test_shift_deriv_functions<DataVector, 1, Frame::Grid>(used_for_size);\n  test_shift_deriv_functions<DataVector, 2, Frame::Grid>(used_for_size);\n  test_shift_deriv_functions<DataVector, 3, Frame::Grid>(used_for_size);\n  test_shift_deriv_functions<DataVector, 1, Frame::Inertial>(used_for_size);\n  test_shift_deriv_functions<DataVector, 2, Frame::Inertial>(used_for_size);\n  test_shift_deriv_functions<DataVector, 3, Frame::Inertial>(used_for_size);\n\n  test_spatial_ricci_tensor<DataVector, 1, Frame::Grid>(used_for_size);\n  test_spatial_ricci_tensor<DataVector, 2, Frame::Grid>(used_for_size);\n  test_spatial_ricci_tensor<DataVector, 3, Frame::Grid>(used_for_size);\n  test_spatial_ricci_tensor<DataVector, 1, Frame::Inertial>(used_for_size);\n  test_spatial_ricci_tensor<DataVector, 2, Frame::Inertial>(used_for_size);\n  test_spatial_ricci_tensor<DataVector, 3, Frame::Inertial>(used_for_size);\n\n  const double mass = 2.;\n  const std::array<double, 3> spin{{0.3, 0.5, 0.2}};\n  const std::array<double, 3> center{{0.2, 0.3, 0.4}};\n  const gr::Solutions::KerrSchild solution(mass, spin, center);\n\n  const size_t grid_size = 8;\n  const std::array<double, 3> lower_bound{{0.82, 1.24, 1.32}};\n  const std::array<double, 3> upper_bound{{0.8, 1.22, 1.30}};\n\n  test_lapse_deriv_functions_analytic(solution, grid_size, lower_bound,\n                                      upper_bound);\n  test_gab_deriv_functions_analytic(solution, grid_size, lower_bound,\n                                    upper_bound);\n  test_gij_deriv_functions_analytic(solution, grid_size, lower_bound,\n                                    upper_bound);\n  test_shift_deriv_functions_analytic(solution, grid_size, lower_bound,\n                                      upper_bound);\n  test_spatial_ricci_tensor_spec(grid_size, lower_bound, upper_bound);\n\n  // Check that compute items work correctly in the DataBox\n  // First, check that the names are correct\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::SecondTimeDerivOfSpacetimeMetricCompute<3, Frame::Inertial>>(\n      \"dt(dt(SpacetimeMetric))\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::TimeDerivSpatialMetricCompute<3, Frame::Inertial>>(\n      \"dt(SpatialMetric)\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::TimeDerivLapseCompute<3, Frame::Inertial>>(\"dt(Lapse)\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::TimeDerivShiftCompute<3, Frame::Inertial>>(\"dt(Shift)\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::DerivSpatialMetricCompute<3, Frame::Inertial>>(\n      \"deriv(SpatialMetric)\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::DerivInverseSpatialMetricCompute<3, Frame::Inertial>>(\n      \"deriv(InverseSpatialMetric)\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::DerivLapseCompute<3, Frame::Inertial>>(\"deriv(Lapse)\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::DerivShiftCompute<3, Frame::Inertial>>(\"deriv(Shift)\");\n  TestHelpers::db::test_compute_tag<gh::Tags::PhiCompute<3, Frame::Inertial>>(\n      \"Phi\");\n  TestHelpers::db::test_compute_tag<gh::Tags::PiCompute<3, Frame::Inertial>>(\n      \"Pi\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ExtrinsicCurvatureCompute<3, Frame::Inertial>>(\n      \"ExtrinsicCurvature\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::TraceExtrinsicCurvatureCompute<3, Frame::Inertial>>(\n      \"TraceExtrinsicCurvature\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ConstraintGamma0Compute<3, Frame::Inertial>>(\n      \"ConstraintGamma0\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ConstraintGamma1Compute<3, Frame::Inertial>>(\n      \"ConstraintGamma1\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::ConstraintGamma2Compute<3, Frame::Inertial>>(\n      \"ConstraintGamma2\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::SpacetimeDerivGaugeHCompute<3, Frame::Inertial>>(\n      \"SpacetimeDerivGaugeH\");\n  TestHelpers::db::test_compute_tag<\n      gh::Tags::GaugeHImplicitFrom3p1QuantitiesCompute<3, Frame::Inertial>>(\n      \"GaugeH\");\n\n  // Check that the compute items return the correct values\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-0.1, 0.1);\n  std::uniform_real_distribution<> dist_positive(1., 2.);\n\n  const auto spacetime_metric = [&]() {\n    auto spacetime_metric_l =\n        make_with_random_values<tnsr::aa<DataVector, 3, Frame::Inertial>>(\n            make_not_null(&generator), make_not_null(&distribution),\n            used_for_size);\n    // Make sure spacetime_metric isn't singular.\n    get<0, 0>(spacetime_metric_l) += -1.;\n    for (size_t i = 1; i <= 3; ++i) {\n      spacetime_metric_l.get(i, i) += 1.;\n    }\n    return spacetime_metric_l;\n  }();\n\n  const auto spatial_metric = gr::spatial_metric(spacetime_metric);\n  const auto det_and_inv_spatial_metric =\n      determinant_and_inverse(spatial_metric);\n  const auto shift =\n      gr::shift(spacetime_metric, det_and_inv_spatial_metric.second);\n  const auto lapse = gr::lapse(shift, spacetime_metric);\n\n  const auto expected_dt_spatial_metric =\n      make_with_random_values<tnsr::ii<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto expected_dt_shift =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto expected_dt_lapse = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n\n  const auto expected_deriv_lapse =\n      make_with_random_values<tnsr::i<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&dist_positive),\n          used_for_size);\n  const auto expected_deriv_shift =\n      make_with_random_values<tnsr::iJ<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto expected_deriv_spatial_metric =\n      make_with_random_values<tnsr::ijj<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  const auto& inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n\n  const auto expected_phi =\n      gh::phi(lapse, expected_deriv_lapse, shift, expected_deriv_shift,\n              spatial_metric, expected_deriv_spatial_metric);\n  const auto expected_pi =\n      gh::pi(lapse, expected_dt_lapse, shift, expected_dt_shift, spatial_metric,\n             expected_dt_spatial_metric, expected_phi);\n  const auto expected_extrinsic_curvature = gh::extrinsic_curvature(\n      spacetime_normal_vector, expected_pi, expected_phi);\n  const auto expected_trace_extrinsic_curvature =\n      trace(expected_extrinsic_curvature, inverse_spatial_metric);\n  const auto expected_deriv_inv_spatial_metric =\n      gr::deriv_inverse_spatial_metric(inverse_spatial_metric,\n                                       expected_deriv_spatial_metric);\n\n  const auto box = db::create<\n      db::AddSimpleTags<gr::Tags::Lapse<DataVector>,\n                        gr::Tags::SpacetimeNormalVector<DataVector, 3>,\n                        gr::Tags::InverseSpacetimeMetric<DataVector, 3>,\n                        gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                        gh::Tags::Phi<DataVector, 3>>,\n      db::AddComputeTags<\n          gh::Tags::DerivSpatialMetricCompute<3, Frame::Inertial>,\n          gr::Tags::DerivInverseSpatialMetricCompute<3, Frame::Inertial>,\n          gh::Tags::DerivLapseCompute<3, Frame::Inertial>,\n          gh::Tags::DerivShiftCompute<3, Frame::Inertial>>>(\n      lapse, spacetime_normal_vector, inverse_spacetime_metric,\n      inverse_spatial_metric, expected_phi);\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(db::get<::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                       tmpl::size_t<3>, Frame::Inertial>>(box)),\n      expected_deriv_spatial_metric);\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(\n          db::get<::Tags::deriv<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                                tmpl::size_t<3>, Frame::Inertial>>(box)),\n      expected_deriv_inv_spatial_metric);\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(db::get<::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                       tmpl::size_t<3>, Frame::Inertial>>(box)),\n      expected_deriv_lapse);\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(db::get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>,\n                                       tmpl::size_t<3>, Frame::Inertial>>(box)),\n      expected_deriv_shift);\n\n  const auto other_box = db::create<\n      db::AddSimpleTags<\n          gr::Tags::Lapse<DataVector>, gr::Tags::Shift<DataVector, 3>,\n          gr::Tags::SpacetimeNormalVector<DataVector, 3>,\n          gr::Tags::InverseSpacetimeMetric<DataVector, 3>,\n          gr::Tags::InverseSpatialMetric<DataVector, 3>,\n          gh::Tags::Phi<DataVector, 3>, gh::Tags::Pi<DataVector, 3>>,\n      db::AddComputeTags<\n          gh::Tags::TimeDerivSpatialMetricCompute<3, Frame::Inertial>,\n          gh::Tags::TimeDerivLapseCompute<3, Frame::Inertial>,\n          gh::Tags::TimeDerivShiftCompute<3, Frame::Inertial>>>(\n      lapse, shift, spacetime_normal_vector, inverse_spacetime_metric,\n      inverse_spatial_metric, expected_phi, expected_pi);\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(db::get<::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>>(\n          other_box)),\n      expected_dt_spatial_metric);\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(db::get<::Tags::dt<gr::Tags::Lapse<DataVector>>>(other_box)),\n      expected_dt_lapse);\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(\n          db::get<::Tags::dt<gr::Tags::Shift<DataVector, 3>>>(other_box)),\n      expected_dt_shift);\n\n  const auto ghvars_box = db::create<\n      db::AddSimpleTags<gr::Tags::SpatialMetric<DataVector, 3>,\n                        gr::Tags::Lapse<DataVector>,\n                        gr::Tags::Shift<DataVector, 3>,\n                        gr::Tags::SpacetimeNormalVector<DataVector, 3>,\n                        gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                        ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::deriv<gr::Tags::Shift<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>,\n                        ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n                        ::Tags::dt<gr::Tags::Shift<DataVector, 3>>>,\n      db::AddComputeTags<\n          gh::Tags::PhiCompute<3, Frame::Inertial>,\n          gh::Tags::PiCompute<3, Frame::Inertial>,\n          gh::Tags::ExtrinsicCurvatureCompute<3, Frame::Inertial>,\n          gh::Tags::TraceExtrinsicCurvatureCompute<3, Frame::Inertial>>>(\n      spatial_metric, lapse, shift, spacetime_normal_vector,\n      inverse_spatial_metric, expected_deriv_spatial_metric,\n      expected_deriv_lapse, expected_deriv_shift, expected_dt_spatial_metric,\n      expected_dt_lapse, expected_dt_shift);\n\n  CHECK(db::get<gh::Tags::Phi<DataVector, 3>>(ghvars_box) == expected_phi);\n  CHECK(db::get<gh::Tags::Pi<DataVector, 3>>(ghvars_box) == expected_pi);\n  CHECK(db::get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(ghvars_box) ==\n        expected_extrinsic_curvature);\n  CHECK(db::get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(ghvars_box) ==\n        expected_trace_extrinsic_curvature);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_ComputeSpacetimeQuantities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <random>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativeSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DerivativesOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/DetAndInverseSpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ExtrinsicCurvature.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Lapse.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/QuadraticCurvatureScalars.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Shift.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpatialMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TimeDerivativeOfSpatialMetric.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\ntemplate <typename Tag, typename Dim, typename Frame>\nstruct deriv;\n}  // namespace Tags\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_spacetime_metric(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::aa<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&)>(\n          &gr::spacetime_metric<DataType, Dim, Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"spacetime_metric\", {{{-10., 10.}}},\n      used_for_size);\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_inverse_spacetime_metric(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::AA<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::II<DataType, Dim, Frame::Inertial>&)>(\n          &gr::inverse_spacetime_metric<DataType, Dim, Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"inverse_spacetime_metric\", {{{-10., 10.}}},\n      used_for_size);\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_time_derivative_of_spacetime_metric(\n    const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::aa<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&, const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&)>(\n          &gr::time_derivative_of_spacetime_metric<DataType, Dim,\n                                                   Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"dt_spacetime_metric\", {{{-10., 10.}}},\n      used_for_size);\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_time_derivative_of_spatial_metric(\n    const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ii<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJ<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&)>(\n          &gr::time_derivative_of_spatial_metric<DataType, Dim,\n                                                 Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"dt_spatial_metric\", {{{-10., 10.}}},\n      used_for_size);\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_derivatives_of_spacetime_metric(\n    const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::abb<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&, const Scalar<DataType>&,\n          const tnsr::i<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJ<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&)>(\n          &gr::derivatives_of_spacetime_metric<DataType, Dim, Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"derivatives_of_spacetime_metric\",\n      {{{-10., 10.}}}, used_for_size);\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_spacetime_normal_vector(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::A<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&)>(\n          &gr::spacetime_normal_vector<DataType, Dim, Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"spacetime_normal_vector\", {{{-10., 10.}}},\n      used_for_size);\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_spacetime_normal_one_form(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-1., 1.);\n  const auto lapse = make_with_random_values<Scalar<DataType>>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<DataType, Dim, Frame::Inertial>(lapse);\n  CHECK_ITERABLE_APPROX(spacetime_normal_one_form.get(0), -lapse.get());\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK_ITERABLE_APPROX(spacetime_normal_one_form.get(i + 1),\n                          make_with_value<DataType>(used_for_size, 0.));\n  }\n}\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_extrinsic_curvature(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ii<DataType, Dim, Frame::Inertial> (*)(\n          const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame::Inertial>&,\n          const tnsr::iJ<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ii<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&)>(\n          &gr::extrinsic_curvature<DataType, Dim, Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"extrinsic_curvature\", {{{-10., 10.}}},\n      used_for_size);\n}\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_cov_deriv_extrinsic_curvature_adm(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::ijj<DataType, SpatialDim, Frame::Inertial> (*)(\n          const tnsr::ijj<DataType, SpatialDim, Frame::Inertial>&,\n          const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n          const tnsr::Ijj<DataType, SpatialDim, Frame::Inertial>&)>(\n          &::gr::covariant_derivative_of_extrinsic_curvature<\n              DataType, SpatialDim, Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"covariant_deriv_extrinsic_curvture_adm\",\n      {{{-1., 1.}}}, used_for_size);\n}\ntemplate <size_t Dim, typename T>\nvoid test_compute_spatial_metric_lapse_shift(const T& used_for_size) {\n  // Set up random values for lapse, shift, and spatial_metric.\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> dist(-1., 1.);\n  std::uniform_real_distribution<> dist_positive(1., 2.);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_dist = make_not_null(&dist);\n  const auto nn_dist_positive = make_not_null(&dist_positive);\n\n  const auto lapse = make_with_random_values<Scalar<T>>(\n      nn_generator, nn_dist_positive, used_for_size);\n  const auto shift = make_with_random_values<tnsr::I<T, Dim>>(\n      nn_generator, nn_dist, used_for_size);\n  const auto spatial_metric = [&]() {\n    auto spatial_metric_l = make_with_random_values<tnsr::ii<T, Dim>>(\n        nn_generator, nn_dist, used_for_size);\n    // Make sure spatial_metric isn't singular by adding\n    // large enough positive diagonal values.\n    for (size_t i = 0; i < Dim; ++i) {\n      spatial_metric_l.get(i, i) += 4.;\n    }\n    return spatial_metric_l;\n  }();\n\n  // Make spacetime metric from spatial metric, lapse, and shift.\n  // Then go backwards and compute the spatial metric, lapse, and shift\n  // and make sure we get back the original values.\n  const auto psi = gr::spacetime_metric(lapse, shift, spatial_metric);\n\n  // Here are the functions we are testing.\n  const auto spatial_metric_test = gr::spatial_metric(psi);\n  const auto shift_test =\n      gr::shift(psi, determinant_and_inverse(spatial_metric).second);\n  const auto lapse_test = gr::lapse(shift, psi);\n\n  CHECK_ITERABLE_APPROX(spatial_metric, spatial_metric_test);\n  CHECK_ITERABLE_APPROX(shift, shift_test);\n  CHECK_ITERABLE_APPROX(lapse, lapse_test);\n}\n\ntemplate <size_t Dim, typename DataType>\nvoid test_compute_deriv_inverse_spatial_metric(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iJJ<DataType, Dim, Frame::Inertial> (*)(\n          const tnsr::II<DataType, Dim, Frame::Inertial>&,\n          const tnsr::ijj<DataType, Dim, Frame::Inertial>&)>(\n          &gr::deriv_inverse_spatial_metric<DataType, Dim, Frame::Inertial>),\n      \"ComputeSpacetimeQuantities\", \"deriv_inverse_spatial_metric\",\n      {{{-10., 10.}}}, used_for_size);\n}\n\nvoid test_compute_pontryagin_scalar_in_vacuum(const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataVector> (*)(\n          const tnsr::ii<DataVector, 3, Frame::Inertial>&,\n          const tnsr::ii<DataVector, 3, Frame::Inertial>&,\n          const tnsr::II<DataVector, 3, Frame::Inertial>&)>(\n          &gr::pontryagin_scalar_in_vacuum),\n      \"ComputeSpacetimeQuantities\", \"pontryagin_scalar_in_vacuum\",\n      // The C++ Tenex contraction and the Python matrix-product reference\n      // accumulate roundoff in a different order for this fully algebraic\n      // expression, so allow a slightly looser comparison here.\n      {{{-10., 10.}}}, used_for_size, 2.0e-12);\n}\n\nvoid test_compute_gauss_bonnet_scalar_in_vacuum(\n    const DataVector& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataVector> (*)(const Scalar<DataVector>&,\n                                         const Scalar<DataVector>&)>(\n          &gr::gauss_bonnet_scalar_in_vacuum),\n      \"ComputeSpacetimeQuantities\", \"gauss_bonnet_scalar_in_vacuum\",\n      {{{-10., 10.}}}, used_for_size);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.SpacetimeDecomp\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_spacetime_metric, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_inverse_spacetime_metric,\n                                    (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_compute_derivatives_of_spacetime_metric, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_compute_time_derivative_of_spacetime_metric, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_compute_time_derivative_of_spatial_metric, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_spacetime_normal_vector,\n                                    (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_spacetime_normal_one_form,\n                                    (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_spatial_metric_lapse_shift,\n                                    (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_extrinsic_curvature,\n                                    (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_compute_deriv_inverse_spatial_metric,\n                                    (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_cov_deriv_extrinsic_curvature_adm,\n                                    (1, 2, 3));\n  test_compute_pontryagin_scalar_in_vacuum(DataVector{5});\n  test_compute_gauss_bonnet_scalar_in_vacuum(DataVector{5});\n\n  // Check that compute items work correctly in the DataBox\n  // First, check that the names are correct\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpacetimeNormalOneFormCompute<DataVector, 3, Frame::Inertial>>(\n      \"SpacetimeNormalOneForm\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpacetimeNormalVectorCompute<DataVector, 3, Frame::Inertial>>(\n      \"SpacetimeNormalVector\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpacetimeMetricCompute<DataVector, 3, Frame::Inertial>>(\n      \"SpacetimeMetric\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::InverseSpacetimeMetricCompute<DataVector, 3, Frame::Inertial>>(\n      \"InverseSpacetimeMetric\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpatialMetricCompute<DataVector, 3, Frame::Inertial>>(\n      \"SpatialMetric\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::ShiftCompute<DataVector, 3, Frame::Inertial>>(\"Shift\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::LapseCompute<DataVector, 3, Frame::Inertial>>(\"Lapse\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SqrtDetSpatialMetricCompute<DataVector, 3, Frame::Inertial>>(\n      \"SqrtDetSpatialMetric\");\n  TestHelpers::db::test_compute_tag<gr::Tags::DetAndInverseSpatialMetricCompute<\n      DataVector, 3, Frame::Inertial>>(\n      \"Variables(DetSpatialMetric,InverseSpatialMetric)\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::DerivativesOfSpacetimeMetricCompute<3, Frame::Inertial>>(\n      \"DerivativesOfSpacetimeMetric\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::CovariantDerivativeOfExtrinsicCurvatureCompute<\n          3, Frame::Inertial>>(\"CovariantDerivativeOfExtrinsicCurvature\");\n\n  // Second, put the compute items into a data box and check that they\n  // put the correct results\n  // Let's start with a known spacetime metric, and then test the\n  // compute items that depend on the spacetime metric\n  DataVector used_for_size{5., 4.};\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-0.1, 0.1);\n\n  const auto expected_spacetime_metric = [&generator, &distribution,\n                                          &used_for_size]() {\n    auto spacetime_metric_l =\n        make_with_random_values<tnsr::aa<DataVector, 3, Frame::Inertial>>(\n            make_not_null(&generator), make_not_null(&distribution),\n            used_for_size);\n    // Make sure spacetime_metric isn't singular.\n    get<0, 0>(spacetime_metric_l) += -1.;\n    for (size_t i = 1; i <= 3; ++i) {\n      spacetime_metric_l.get(i, i) += 1.;\n    }\n    return spacetime_metric_l;\n  }();\n\n  const auto expected_spatial_metric =\n      gr::spatial_metric(expected_spacetime_metric);\n  const auto expected_det_and_inverse_spatial_metric =\n      determinant_and_inverse(expected_spatial_metric);\n  const auto expected_shift =\n      gr::shift(expected_spacetime_metric,\n                expected_det_and_inverse_spatial_metric.second);\n  const auto expected_lapse =\n      gr::lapse(expected_shift, expected_spacetime_metric);\n  const auto expected_inverse_spacetime_metric = gr::inverse_spacetime_metric(\n      expected_lapse, expected_shift,\n      expected_det_and_inverse_spatial_metric.second);\n\n  const auto box = db::create<\n      db::AddSimpleTags<gr::Tags::SpacetimeMetric<DataVector, 3>>,\n      db::AddComputeTags<\n          gr::Tags::SpatialMetricCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::DetAndInverseSpatialMetricCompute<DataVector, 3,\n                                                      Frame::Inertial>,\n          gr::Tags::SqrtDetSpatialMetricCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::ShiftCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::LapseCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::InverseSpacetimeMetricCompute<DataVector, 3,\n                                                  Frame::Inertial>,\n          gr::Tags::SpacetimeNormalOneFormCompute<DataVector, 3,\n                                                  Frame::Inertial>,\n          gr::Tags::SpacetimeNormalVectorCompute<DataVector, 3,\n                                                 Frame::Inertial>>>(\n      expected_spacetime_metric);\n  CHECK(db::get<gr::Tags::SpatialMetric<DataVector, 3>>(box) ==\n        expected_spatial_metric);\n  CHECK(db::get<gr::Tags::DetSpatialMetric<DataVector>>(box) ==\n        expected_det_and_inverse_spatial_metric.first);\n  CHECK(db::get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(box) ==\n        expected_det_and_inverse_spatial_metric.second);\n  CHECK(get(db::get<gr::Tags::SqrtDetSpatialMetric<DataVector>>(box)) ==\n        sqrt(get(expected_det_and_inverse_spatial_metric.first)));\n  CHECK(db::get<gr::Tags::Shift<DataVector, 3>>(box) == expected_shift);\n  CHECK(db::get<gr::Tags::Lapse<DataVector>>(box) == expected_lapse);\n  CHECK(db::get<gr::Tags::InverseSpacetimeMetric<DataVector, 3>>(box) ==\n        expected_inverse_spacetime_metric);\n  CHECK(db::get<gr::Tags::SpacetimeNormalOneForm<DataVector, 3>>(box) ==\n        gr::spacetime_normal_one_form<DataVector, 3, Frame::Inertial>(\n            expected_lapse));\n  CHECK(db::get<gr::Tags::SpacetimeNormalVector<DataVector, 3>>(box) ==\n        gr::spacetime_normal_vector(expected_lapse, expected_shift));\n\n  // Now let's put the lapse, shift, and spatial metric into the databox\n  // and test that we can compute the correct spacetime metric\n  const auto second_box =\n      db::create<db::AddSimpleTags<gr::Tags::SpatialMetric<DataVector, 3>,\n                                   gr::Tags::Lapse<DataVector>,\n                                   gr::Tags::Shift<DataVector, 3>>,\n                 db::AddComputeTags<gr::Tags::SpacetimeMetricCompute<\n                     DataVector, 3, Frame::Inertial>>>(\n          expected_spatial_metric, expected_lapse, expected_shift);\n  CHECK_ITERABLE_APPROX(\n      SINGLE_ARG(db::get<gr::Tags::SpacetimeMetric<DataVector, 3>>(second_box)),\n      expected_spacetime_metric);\n\n  // Now let's put the temporal and spatial derivatives of lapse, shift, and\n  // spatial metric into the databox and test that we can assemple the\n  // correct spatial and spacetime derivatives of the spacetime metric\n  const auto deriv_spatial_metric =\n      make_with_random_values<tnsr::ijj<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto deriv_shift =\n      make_with_random_values<tnsr::iJ<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto deriv_lapse =\n      make_with_random_values<tnsr::i<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto dt_spatial_metric =\n      make_with_random_values<tnsr::ii<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto dt_shift =\n      make_with_random_values<tnsr::I<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  const auto dt_lapse = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n\n  const auto expected_derivatives_of_spacetime_metric =\n      gr::derivatives_of_spacetime_metric(\n          expected_lapse, dt_lapse, deriv_lapse, expected_shift, dt_shift,\n          deriv_shift, expected_spatial_metric, dt_spatial_metric,\n          deriv_spatial_metric);\n\n  const auto third_box = db::create<\n      db::AddSimpleTags<gr::Tags::SpatialMetric<DataVector, 3>,\n                        gr::Tags::Lapse<DataVector>,\n                        gr::Tags::Shift<DataVector, 3>,\n                        ::Tags::deriv<gr::Tags::SpatialMetric<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::deriv<gr::Tags::Shift<DataVector, 3>,\n                                      tmpl::size_t<3>, Frame::Inertial>,\n                        ::Tags::dt<gr::Tags::SpatialMetric<DataVector, 3>>,\n                        ::Tags::dt<gr::Tags::Lapse<DataVector>>,\n                        ::Tags::dt<gr::Tags::Shift<DataVector, 3>>>,\n      db::AddComputeTags<\n          gr::Tags::DerivativesOfSpacetimeMetricCompute<3, Frame::Inertial>>>(\n      expected_spatial_metric, expected_lapse, expected_shift,\n      deriv_spatial_metric, deriv_lapse, deriv_shift, dt_spatial_metric,\n      dt_lapse, dt_shift);\n  CHECK(db::get<gr::Tags::DerivativesOfSpacetimeMetric<DataVector, 3>>(\n            third_box) == expected_derivatives_of_spacetime_metric);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_CurvatureScalarComputeTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/CubicCurvatureScalars.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/QuadraticCurvatureScalars.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylMagnetic.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.GeneralRelativity.CurvatureScalars.ComputeTags\",\n    \"[PointwiseFunctions][Unit]\") {\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::PontryaginScalarCompute<DataVector, 3, Frame::Inertial>>(\n      \"PontryaginScalar\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::GaussBonnetScalarCompute<DataVector>>(\"GaussBonnetScalar\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::CubicInvariantRealCompute<DataVector, 3, Frame::Inertial>>(\n      \"CubicInvariantReal\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::CubicInvariantImagCompute<DataVector, 3, Frame::Inertial>>(\n      \"CubicInvariantImag\");\n\n  const DataVector used_for_size(7);\n  // Randomize inputs inline\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> dist(-0.2, 0.2);\n  const auto E =\n      make_with_random_values<tnsr::ii<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), used_for_size);\n  const auto B =\n      make_with_random_values<tnsr::ii<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), used_for_size);\n  auto inv_metric =\n      make_with_random_values<tnsr::II<DataVector, 3, Frame::Inertial>>(\n          make_not_null(&gen), make_not_null(&dist), used_for_size);\n  for (size_t i = 0; i < 3; ++i) {\n    inv_metric.get(i, i) += 1.0;\n  }\n\n  const auto E_scalar = gr::weyl_electric_scalar(E, inv_metric);\n  const auto B_scalar =\n      gr::weyl_magnetic_scalar<Frame::Inertial, DataVector>(B, inv_metric);\n\n  const auto box = db::create<\n      db::AddSimpleTags<\n          gr::Tags::WeylElectric<DataVector, 3, Frame::Inertial>,\n          gr::Tags::WeylMagnetic<DataVector, 3, Frame::Inertial>,\n          gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>,\n          gr::Tags::WeylElectricScalar<DataVector>,\n          gr::Tags::WeylMagneticScalar<DataVector>>,\n      db::AddComputeTags<\n          gr::Tags::PontryaginScalarCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::GaussBonnetScalarCompute<DataVector>,\n          gr::Tags::CubicInvariantRealCompute<DataVector, 3, Frame::Inertial>,\n          gr::Tags::CubicInvariantImagCompute<DataVector, 3, Frame::Inertial>>>(\n      E, B, inv_metric, E_scalar, B_scalar);\n\n  const auto expected_pontryagin =\n      gr::pontryagin_scalar_in_vacuum<Frame::Inertial>(E, B, inv_metric);\n  const auto expected_gb =\n      gr::gauss_bonnet_scalar_in_vacuum(E_scalar, B_scalar);\n  const auto expected_cubic_real = gr::cubic_invariant_real(E, B, inv_metric);\n  const auto expected_cubic_imag = gr::cubic_invariant_imag(E, B, inv_metric);\n\n  CHECK_ITERABLE_APPROX((db::get<gr::Tags::PontryaginScalar<DataVector>>(box)),\n                        expected_pontryagin);\n  CHECK_ITERABLE_APPROX((db::get<gr::Tags::GaussBonnetScalar<DataVector>>(box)),\n                        expected_gb);\n  CHECK_ITERABLE_APPROX(\n      (db::get<gr::Tags::CubicInvariantReal<DataVector>>(box)),\n      expected_cubic_real);\n  CHECK_ITERABLE_APPROX(\n      (db::get<gr::Tags::CubicInvariantImag<DataVector>>(box)),\n      expected_cubic_imag);\n}\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_CurvatureScalars.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/CubicCurvatureScalars.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/QuadraticCurvatureScalars.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylMagnetic.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\ntemplate <typename DataType>\nvoid build_schwarzschild_spatial_metrics(\n    gsl::not_null<tnsr::ii<DataType, 3, Frame::Inertial>*> spatial_metric,\n    gsl::not_null<tnsr::II<DataType, 3, Frame::Inertial>*>\n        inverse_spatial_metric,\n    const DataType& r, const DataType& theta, const double mass) {\n  const auto one = make_with_value<DataType>(r, 1.0);\n  const auto sin_th = sin(theta);\n  const auto g_rr = one / (one - 2.0 * mass / r);\n  const auto g_tt = r * r;\n  const auto g_pp = g_tt * sin_th * sin_th;\n\n  spatial_metric->get(0, 0) = g_rr;\n  spatial_metric->get(1, 1) = g_tt;\n  spatial_metric->get(2, 2) = g_pp;\n\n  inverse_spatial_metric->get(0, 0) = one - 2.0 * mass / r;\n  inverse_spatial_metric->get(1, 1) = one / (r * r);\n  inverse_spatial_metric->get(2, 2) = one / g_pp;\n}\n\nvoid test_curvature_scalars_schwarzschild() {\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> mass_dist(0.5, 2.0);\n  std::uniform_real_distribution<> rfac_dist(2.5, 6.0);\n  std::uniform_real_distribution<> theta_dist(0.3, 2.5);\n  const double mass = mass_dist(gen);\n  const size_t num_points = 1000;\n  const auto r = make_with_random_values<DataVector>(\n      make_not_null(&gen), make_not_null(&rfac_dist), num_points);\n  const auto theta = make_with_random_values<DataVector>(\n      make_not_null(&gen), make_not_null(&theta_dist), num_points);\n  tnsr::ii<DataVector, 3, Frame::Inertial> spatial_metric(num_points, 0.0);\n  tnsr::II<DataVector, 3, Frame::Inertial> inverse_spatial_metric(num_points,\n                                                                  0.0);\n  build_schwarzschild_spatial_metrics(make_not_null(&spatial_metric),\n                                      make_not_null(&inverse_spatial_metric), r,\n                                      theta, mass);\n\n  const DataVector a = mass / (r * r * r);\n  tnsr::ii<DataVector, 3, Frame::Inertial> electric_weyl(num_points, 0.0);\n  const tnsr::ii<DataVector, 3, Frame::Inertial> magnetic_weyl(num_points, 0.0);\n  electric_weyl.get(0, 0) = -2.0 * a * spatial_metric.get(0, 0);\n  electric_weyl.get(1, 1) = a * spatial_metric.get(1, 1);\n  electric_weyl.get(2, 2) = a * spatial_metric.get(2, 2);\n\n  const auto electric_weyl_scalar =\n      gr::weyl_electric_scalar(electric_weyl, inverse_spatial_metric);\n  const auto magnetic_weyl_scalar =\n      gr::weyl_magnetic_scalar<Frame::Inertial, DataVector>(\n          magnetic_weyl, inverse_spatial_metric);\n\n  const auto gauss_bonnet = gr::gauss_bonnet_scalar_in_vacuum(\n      electric_weyl_scalar, magnetic_weyl_scalar);\n  CHECK_ITERABLE_APPROX(gauss_bonnet.get(), 48.0 * a * a);\n\n  const auto pontryagin = gr::pontryagin_scalar_in_vacuum<Frame::Inertial>(\n      electric_weyl, magnetic_weyl, inverse_spatial_metric);\n  CHECK_ITERABLE_APPROX(pontryagin.get(), DataVector(num_points, 0.0));\n\n  const auto cubic_invariant_real = gr::cubic_invariant_real(\n      electric_weyl, magnetic_weyl, inverse_spatial_metric);\n  const auto cubic_invariant_imag = gr::cubic_invariant_imag(\n      electric_weyl, magnetic_weyl, inverse_spatial_metric);\n  CHECK_ITERABLE_APPROX(cubic_invariant_real.get(), a * a * a);\n  CHECK_ITERABLE_APPROX(cubic_invariant_imag.get(),\n                        DataVector(num_points, 0.0));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.GeneralRelativity.CurvatureScalars.Schwarzschild\",\n    \"[PointwiseFunctions][Unit]\") {\n  test_curvature_scalars_schwarzschild();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_GeodesicAcceleration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/BoostMultiArray.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/OdeIntegration/OdeIntegration.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicAcceleration.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n\nnamespace {\n\ntemplate <typename DataType>\nvoid test_circular_orbit_kerr_schild() {\n  MAKE_GENERATOR(gen);\n  const double mass = 3.2;\n  const std::array<double, 3> spin{{0., 0., 0.}};\n  const std::array<double, 3> center{{0., 0., 0.}};\n  gr::Solutions::KerrSchild kerr_schild(mass, spin, center);\n  const auto used_for_size = Scalar<DataType>(static_cast<size_t>(100));\n  std::uniform_real_distribution<> radius_dist(7., 100.);\n  std::uniform_real_distribution<> angle_dist(0., 2. * M_PI);\n  const auto radius = make_with_random_values<DataType>(\n      make_not_null(&gen), radius_dist, used_for_size);\n  const auto angle = make_with_random_values<DataType>(\n      make_not_null(&gen), angle_dist, used_for_size);\n  const auto angular_velocity = sqrt(mass) / (radius * sqrt(radius));\n\n  const tnsr::I<DataType, 3> position{\n      {radius * cos(angle), radius * sin(angle),\n       make_with_value<DataType>(used_for_size, 0.)}};\n  const tnsr::I<DataType, 3> velocity{\n      {-radius * angular_velocity * sin(angle),\n       radius * angular_velocity * cos(angle),\n       make_with_value<DataType>(used_for_size, 0.)}};\n  const tnsr::I<DataType, 3> expected_acceleration{\n      {-radius * square(angular_velocity) * cos(angle),\n       -radius * square(angular_velocity) * sin(angle),\n       make_with_value<DataType>(used_for_size, 0.)}};\n\n  const auto christoffel = get<\n      gr::Tags::SpacetimeChristoffelSecondKind<DataType, 3, Frame::Inertial>>(\n      kerr_schild.variables(position, 0.,\n                            tmpl::list<gr::Tags::SpacetimeChristoffelSecondKind<\n                                DataType, 3, Frame::Inertial>>{}));\n\n  const auto geodesic_acc = gr::geodesic_acceleration(velocity, christoffel);\n  CHECK_ITERABLE_APPROX(geodesic_acc, expected_acceleration);\n}\n\nstd::array<tnsr::I<double, 3>, 2> state_to_tnsr(\n    const std::array<double, 6>& state) {\n  tnsr::I<double, 3> pos_tensor{{state[0], state[1], state[2]}};\n  tnsr::I<double, 3> vel_tensor{{state[3], state[4], state[5]}};\n  return {std::move(pos_tensor), std::move(vel_tensor)};\n}\n\n// struct with interface conforming to a `System` as used in\n// boost::numeric::odeint\nstruct BoostGeodesicIntegrator {\n  gr::Solutions::KerrSchild kerr_schild;\n  void operator()(const std::array<double, 6>& state,\n                  std::array<double, 6>& derivative_of_state,\n                  const double /*time*/) const {\n    const auto [position, velocity] = state_to_tnsr(state);\n    const auto christoffel = get<\n        gr::Tags::SpacetimeChristoffelSecondKind<double, 3, Frame::Inertial>>(\n        kerr_schild.variables(\n            position, 0.,\n            tmpl::list<gr::Tags::SpacetimeChristoffelSecondKind<\n                double, 3, Frame::Inertial>>{}));\n    const auto geodesic_acc = gr::geodesic_acceleration(velocity, christoffel);\n    for (size_t i = 0; i < 3; ++i) {\n      gsl::at(derivative_of_state, i) = state.at(i + 3);\n      gsl::at(derivative_of_state, i + 3) = geodesic_acc.get(i);\n    }\n  }\n};\n\n// struct with interface conforming to an `Observer` as used in\n// boost::numeric::odeint\nstruct BoostObserver {\n  std::vector<std::array<double, 6>>& states;\n  std::vector<double>& times;\n  void operator()(const std::array<double, 6>& state, double t) {\n    states.push_back(state);\n    times.push_back(t);\n  }\n};\n\ntnsr::a<double, 3> lower_four_velocity(\n    const std::array<double, 6>& state,\n    const gr::Solutions::KerrSchild& kerr_schild) {\n  const auto [pos, vel] = state_to_tnsr(state);\n  const auto spacetime_vars = kerr_schild.variables(\n      pos, 0.,\n      tmpl::list<gr::Tags::Lapse<double>,\n                 gr::Tags::Shift<double, 3, Frame::Inertial>,\n                 gr::Tags::SpatialMetric<double, 3, Frame::Inertial>>{});\n  const auto spacetime_metric = gr::spacetime_metric(\n      get<gr::Tags::Lapse<double>>(spacetime_vars),\n      get<gr::Tags::Shift<double, 3, Frame::Inertial>>(spacetime_vars),\n      get<gr::Tags::SpatialMetric<double, 3, Frame::Inertial>>(spacetime_vars));\n  double temp = spacetime_metric.get(0, 0);\n  for (size_t i = 0; i < 3; ++i) {\n    temp += 2. * spacetime_metric.get(i + 1, 0) * vel.get(i);\n    for (size_t j = 0; j < 3; ++j) {\n      temp += spacetime_metric.get(i + 1, j + 1) * vel.get(i) * vel.get(j);\n    }\n  }\n  // the expression for u0 follows directly from u^i = u^0 * v^i and the\n  // normalization of the four velocity\n  const double u0 = sqrt(-1. / temp);\n  const tnsr::A<double, 3> u{\n      {u0, vel.get(0) * u0, vel.get(1) * u0, vel.get(2) * u0}};\n  return tenex::evaluate<ti::a>(spacetime_metric(ti::a, ti::b) * u(ti::B));\n}\n\n// checks that the conserved quantities due to the spacetime symmetries i.e.\n// Killing vectors are conserved during geodesic evolution\nvoid test_conserved_quantities_kerr_schild() {\n  const double mass = 1.3;\n  const std::array<double, 3> spin{{0., 0., 0.3}};\n  const std::array<double, 3> center{{0., 0., 0.}};\n  gr::Solutions::KerrSchild kerr_schild(mass, spin, center);\n\n  const double t_max = 1000.0;\n\n  // corresponds to a strongly perturbed circular orbit\n  std::array<double, 6> initial_state = {\n      10., 1.0, 0.8, 0.1, 1. / sqrt(10.) * 1.2, 0.3};\n\n  std::vector<std::array<double, 6>> states{};\n  std::vector<double> times{};\n  BoostObserver observer{states, times};\n#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 13\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wuninitialized\"\n#endif\n  boost::numeric::odeint::integrate_adaptive(\n      boost::numeric::odeint::make_controlled(\n          1e-15, 1e-15,\n          boost::numeric::odeint::runge_kutta_cash_karp54<\n              std::array<double, 6>>()),\n      BoostGeodesicIntegrator{kerr_schild}, initial_state, 0.0, t_max, 1e-5,\n      observer);\n#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 13\n#pragma GCC diagnostic pop\n#endif\n\n  const auto initial_four_velocity =\n      lower_four_velocity(initial_state, kerr_schild);\n\n  // the two Killing vectors in Kerr-Schild coordinates\n  const tnsr::A<double, 3> timelike_killing{{1., 0., 0., 0.}};\n  const tnsr::A<double, 3> azimuthal_killing_initial{\n      {0., initial_state[1], -initial_state[0], 0.}};\n\n  const double initial_energy =\n      get(dot_product(initial_four_velocity, timelike_killing));\n  const double initial_angular_momentum =\n      get(dot_product(initial_four_velocity, azimuthal_killing_initial));\n\n  for (const auto& state : states) {\n    const auto four_velocity = lower_four_velocity(state, kerr_schild);\n    const double energy = get(dot_product(four_velocity, timelike_killing));\n    const tnsr::A<double, 3> azimuthal_killing{{0., state[1], -state[0], 0.}};\n    const double angular_momentum =\n        get(dot_product(four_velocity, azimuthal_killing));\n    CHECK(initial_energy == approx(energy));\n    CHECK(initial_angular_momentum == approx(angular_momentum));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.GeneralRelativity.GeodesicAcceleration\",\n    \"[Unit][PointwiseFunctions]\") {\n  test_circular_orbit_kerr_schild<double>();\n  test_circular_orbit_kerr_schild<DataVector>();\n  test_conserved_quantities_kerr_schild();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_GeodesicEquation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/GeodesicEquation.hpp\"\n\nnamespace gr {\n\nSPECTRE_TEST_CASE(\"Unit.GeneralRelativity.GeodesicEquation\",\n                  \"[Unit][PointwiseFunctions]\") {\n  MAKE_GENERATOR(generator);\n  const pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity\");\n  pypp::check_with_random_values<1>(\n      &geodesic_equation<double, 3, Frame::Inertial>, \"GeodesicEquation\",\n      {\"dt_x\", \"dt_pi\", \"dt_lnp0\"}, {{{-1., 1.}}}, DataVector(5));\n}\n\n}  // namespace gr\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_InterfaceNullNormal.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <numbers>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/Evaluate.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeRandomVectorInMagnitudeRange.hpp\"\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InterfaceNullNormal.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nconst Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n\ntemplate <size_t SpatialDim, typename DataType>\ntnsr::a<DataType, SpatialDim, Frame::Inertial>\ninterface_outgoing_null_normal_one_form(\n    const tnsr::a<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_one_form,\n    const tnsr::i<DataType, SpatialDim, Frame::Inertial>&\n        interface_normal_one_form,\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>& shift) {\n  return gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n      spacetime_normal_one_form, interface_normal_one_form, shift, 1.);\n}\ntemplate <size_t SpatialDim, typename DataType>\ntnsr::a<DataType, SpatialDim, Frame::Inertial>\ninterface_incoming_null_normal_one_form(\n    const tnsr::a<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_one_form,\n    const tnsr::i<DataType, SpatialDim, Frame::Inertial>&\n        interface_normal_one_form,\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>& shift) {\n  return gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n      spacetime_normal_one_form, interface_normal_one_form, shift, -1.);\n}\ntemplate <size_t SpatialDim, typename DataType>\ntnsr::A<DataType, SpatialDim, Frame::Inertial>\ninterface_outgoing_null_normal_vector(\n    const tnsr::A<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_vector,\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>&\n        interface_normal_vector) {\n  return gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n      spacetime_normal_vector, interface_normal_vector, 1.);\n}\ntemplate <size_t SpatialDim, typename DataType>\ntnsr::A<DataType, SpatialDim, Frame::Inertial>\ninterface_incoming_null_normal_vector(\n    const tnsr::A<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_vector,\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>&\n        interface_normal_vector) {\n  return gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n      spacetime_normal_vector, interface_normal_vector, -1.);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\ntnsr::A<DataType, SpatialDim, Frame::Inertial>\nextend_spatial_vector_to_spacetime(\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>& spatial_vector,\n    const DataType& used_for_size) {\n  auto spacetime_vector =\n      make_with_value<tnsr::A<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    spacetime_vector.get(i + 1) = spatial_vector.get(i);\n  }\n  return spacetime_vector;\n}\n\ntemplate <size_t SpatialDim, typename DataType>\ntnsr::a<DataType, SpatialDim, Frame::Inertial>\nextend_spatial_one_form_to_spacetime(\n    const tnsr::i<DataType, SpatialDim, Frame::Inertial>& spatial_one_form,\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>& shift,\n    const DataType& used_for_size) {\n  auto spacetime_one_form =\n      make_with_value<tnsr::a<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    spacetime_one_form.get(i + 1) = spatial_one_form.get(i);\n  }\n  spacetime_one_form.get(0) = get(dot_product(shift, spatial_one_form));\n  return spacetime_one_form;\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_interface_null_normals(const DataType& used_for_size) {\n  {\n    auto* f = &interface_outgoing_null_normal_one_form<SpatialDim, DataType>;\n    pypp::check_with_random_values<1>(f, \"InterfaceNullNormal\",\n                                      \"interface_outgoing_null_normal\",\n                                      {{{-1., 1.}}}, used_for_size);\n  }\n  {\n    auto* f = &interface_outgoing_null_normal_vector<SpatialDim, DataType>;\n    pypp::check_with_random_values<1>(f, \"InterfaceNullNormal\",\n                                      \"interface_outgoing_null_normal\",\n                                      {{{-1., 1.}}}, used_for_size);\n  }\n  {\n    auto* f = &interface_incoming_null_normal_one_form<SpatialDim, DataType>;\n    pypp::check_with_random_values<1>(f, \"InterfaceNullNormal\",\n                                      \"interface_incoming_null_normal\",\n                                      {{{-1., 1.}}}, used_for_size);\n  }\n  {\n    auto* f = &interface_incoming_null_normal_vector<SpatialDim, DataType>;\n    pypp::check_with_random_values<1>(f, \"InterfaceNullNormal\",\n                                      \"interface_incoming_null_normal\",\n                                      {{{-1., 1.}}}, used_for_size);\n  }\n}\n\ntemplate <typename DataType>\nvoid test_one_form_time_component_difference(const DataType& used_for_size) {\n  constexpr size_t spatial_dim = 3;\n  MAKE_GENERATOR(generator);\n  const auto lapse =\n      TestHelpers::gr::random_lapse(make_not_null(&generator), used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<spatial_dim>(\n      make_not_null(&generator), used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<spatial_dim>(\n          make_not_null(&generator), used_for_size);\n  const auto interface_normal_vector =\n      random_unit_normal(make_not_null(&generator), spatial_metric);\n  auto interface_normal_one_form =\n      make_with_value<tnsr::i<DataType, spatial_dim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  raise_or_lower_index(make_not_null(&interface_normal_one_form),\n                       interface_normal_vector, spatial_metric);\n\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<DataType, spatial_dim, Frame::Inertial>(\n          lapse);\n\n  auto incoming =\n      gr::interface_null_normal<DataType, spatial_dim, Frame::Inertial>(\n          spacetime_normal_one_form, interface_normal_one_form, shift, -1.0);\n  auto outgoing =\n      gr::interface_null_normal<DataType, spatial_dim, Frame::Inertial>(\n          spacetime_normal_one_form, interface_normal_one_form, shift, 1.0);\n\n  const auto shift_dot_interface_normal =\n      dot_product(shift, interface_normal_one_form);\n  const DataType expected_time_component_difference =\n      std::numbers::sqrt2 * get(shift_dot_interface_normal);\n  const DataType computed_time_component_difference =\n      outgoing.get(0) - incoming.get(0);\n  CHECK_ITERABLE_CUSTOM_APPROX(computed_time_component_difference,\n                               expected_time_component_difference,\n                               custom_approx);\n\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  const auto incoming_outgoing_difference =\n      tenex::evaluate<ti::a>(outgoing(ti::a) - incoming(ti::a));\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(spacetime_normal_vector, incoming_outgoing_difference)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid check_null_normal_contractions(\n    const tnsr::aa<DataType, SpatialDim, Frame::Inertial>& spacetime_metric,\n    const tnsr::AA<DataType, SpatialDim, Frame::Inertial>&\n        inverse_spacetime_metric,\n    const tnsr::a<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_vector,\n    const tnsr::i<DataType, SpatialDim, Frame::Inertial>&\n        interface_unit_normal_one_form,\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>&\n        interface_unit_normal_vector,\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>& shift,\n    const DataType& used_for_size) {\n  const auto outgoing_one_form =\n      gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n          spacetime_normal_one_form, interface_unit_normal_one_form, shift, 1.);\n  const auto incoming_one_form =\n      gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n          spacetime_normal_one_form, interface_unit_normal_one_form, shift,\n          -1.);\n  const auto outgoing_vector =\n      gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n          spacetime_normal_vector, interface_unit_normal_vector, 1.);\n  const auto incoming_vector =\n      gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n          spacetime_normal_vector, interface_unit_normal_vector, -1.);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(outgoing_one_form, outgoing_one_form,\n                      inverse_spacetime_metric)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(incoming_one_form, incoming_one_form,\n                      inverse_spacetime_metric)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(outgoing_vector, outgoing_vector, spacetime_metric)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(incoming_vector, incoming_vector, spacetime_metric)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(outgoing_one_form, incoming_one_form,\n                      inverse_spacetime_metric)),\n      make_with_value<DataType>(used_for_size, -1.0), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(outgoing_vector, incoming_vector, spacetime_metric)),\n      make_with_value<DataType>(used_for_size, -1.0), custom_approx);\n\n  const auto extended_interface_vector = extend_spatial_vector_to_spacetime(\n      interface_unit_normal_vector, used_for_size);\n  const auto extended_interface_one_form = extend_spatial_one_form_to_spacetime(\n      interface_unit_normal_one_form, shift, used_for_size);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(extended_interface_vector, outgoing_one_form)),\n      make_with_value<DataType>(used_for_size, 1.0 / std::numbers::sqrt2),\n      custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(extended_interface_vector, incoming_one_form)),\n      make_with_value<DataType>(used_for_size, -1.0 / std::numbers::sqrt2),\n      custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(extended_interface_one_form, outgoing_vector)),\n      make_with_value<DataType>(used_for_size, 1.0 / std::numbers::sqrt2),\n      custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(extended_interface_one_form, incoming_vector)),\n      make_with_value<DataType>(used_for_size, -1.0 / std::numbers::sqrt2),\n      custom_approx);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_null_normal_contractions_random_background(\n    const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  const auto lapse =\n      TestHelpers::gr::random_lapse(make_not_null(&generator), used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<SpatialDim>(\n      make_not_null(&generator), used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<SpatialDim>(\n          make_not_null(&generator), used_for_size);\n\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<DataType, SpatialDim, Frame::Inertial>(\n          lapse);\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n\n  const auto interface_unit_normal_vector =\n      random_unit_normal(make_not_null(&generator), spatial_metric);\n  auto interface_unit_normal_one_form =\n      make_with_value<tnsr::i<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  raise_or_lower_index(make_not_null(&interface_unit_normal_one_form),\n                       interface_unit_normal_vector, spatial_metric);\n\n  check_null_normal_contractions(\n      spacetime_metric, inverse_spacetime_metric, spacetime_normal_one_form,\n      spacetime_normal_vector, interface_unit_normal_one_form,\n      interface_unit_normal_vector, shift, used_for_size);\n}\n\ntemplate <typename DataType>\nvoid test_null_normal_contractions_kerr(const DataType& used_for_size) {\n  constexpr size_t spatial_dim = 3;\n  const gr::Solutions::KerrSchild kerr_solution{\n      1.7, {{0.2, -0.4, 0.3}}, {{0.1, -0.2, 0.3}}};\n  MAKE_GENERATOR(generator);\n  const auto coords =\n      make_random_vector_in_magnitude_range_flat<DataType, spatial_dim,\n                                                 UpLo::Up, Frame::Inertial>(\n          make_not_null(&generator), used_for_size, 4.0, 6.0);\n\n  const auto vars = kerr_solution.variables(\n      coords, 0.0,\n      tmpl::list<\n          gr::Tags::Lapse<DataType>,\n          gr::Tags::Shift<DataType, spatial_dim, Frame::Inertial>,\n          gr::Tags::SpatialMetric<DataType, spatial_dim, Frame::Inertial>,\n          gr::Tags::InverseSpatialMetric<DataType, spatial_dim,\n                                         Frame::Inertial>>{});\n  const auto& lapse = get<gr::Tags::Lapse<DataType>>(vars);\n  const auto& shift =\n      get<gr::Tags::Shift<DataType, spatial_dim, Frame::Inertial>>(vars);\n  const auto& spatial_metric =\n      get<gr::Tags::SpatialMetric<DataType, spatial_dim, Frame::Inertial>>(\n          vars);\n  const auto& inverse_spatial_metric = get<\n      gr::Tags::InverseSpatialMetric<DataType, spatial_dim, Frame::Inertial>>(\n      vars);\n  const auto spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n\n  const auto interface_unit_normal_vector =\n      random_unit_normal(make_not_null(&generator), spatial_metric);\n  auto interface_unit_normal_one_form =\n      make_with_value<tnsr::i<DataType, spatial_dim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  raise_or_lower_index(make_not_null(&interface_unit_normal_one_form),\n                       interface_unit_normal_vector, spatial_metric);\n\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<DataType, spatial_dim, Frame::Inertial>(\n          lapse);\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n  check_null_normal_contractions(\n      spacetime_metric, inverse_spacetime_metric, spacetime_normal_one_form,\n      spacetime_normal_vector, interface_unit_normal_one_form,\n      interface_unit_normal_vector, shift, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.IntfcNullNormals\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_interface_null_normals, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_one_form_time_component_difference,\n                                    ());\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(\n      test_null_normal_contractions_random_background, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_null_normal_contractions_kerr, ());\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_KerrHorizon.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <limits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrHorizon.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace gr::Solutions {\nnamespace {\n\n// This wraps the kerr_horizon_radius function with\n// types that CheckWithRandomValues can understand.\n// We use magnitude and angle of dimensionless spin so that\n// we know their bounds.\ntemplate <typename DataType>\nScalar<DataType> wrap_kerr_horizon_radius(const Scalar<DataType>& theta,\n                                          const Scalar<DataType>& phi,\n                                          const double mass,\n                                          const double dimless_spin_magnitude,\n                                          const double dimless_spin_theta,\n                                          const double dimless_spin_phi) {\n  return kerr_horizon_radius<DataType>(\n      {{get(theta), get(phi)}}, mass,\n      {{dimless_spin_magnitude * sin(dimless_spin_theta) *\n            cos(dimless_spin_phi),\n        dimless_spin_magnitude * sin(dimless_spin_theta) *\n            sin(dimless_spin_phi),\n        dimless_spin_magnitude * cos(dimless_spin_theta)}});\n}\n\ntemplate <typename DataType>\nvoid test_kerr_horizon(const DataType& used_for_size) {\n  pypp::check_with_random_values<6>(&wrap_kerr_horizon_radius<DataType>,\n                                    \"KerrHorizon\", \"kerr_horizon_radius\",\n                                    {{{0.0, M_PI},\n                                      {0.0, 2.0 * M_PI},\n                                      {1.0, 2.0},\n                                      {0.0, 1.0},\n                                      {0.0, M_PI},\n                                      {0.0, 2.0 * M_PI}}},\n                                    used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.AnalyticSolutions.Gr.KerrHorizon\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/\");\n  const DataVector dv(5);\n  test_kerr_horizon(dv);\n  test_kerr_horizon(0.0);\n\n  // Test for Schwarzschild (mass=2) at randomly-chosen point\n  CHECK(4.0 == get(kerr_horizon_radius<double>({{1.12345, 2.2222}}, 2.0,\n                                               {{0.0, 0.0, 0.0}})));\n\n  // Test for Kerr (mass=2) along pole, for spin not in z direction.\n  CHECK(approx(2.0 * (1.0 + sqrt(0.86))) ==\n        get(kerr_horizon_radius<double>(\n            {{acos(0.3 / sqrt(0.14)), atan2(0.2, 0.1)}}, 2.0,\n            {{0.1, 0.2, 0.3}})));\n\n  // Test for Kerr (mass=2) along equator, for spin not in z direction.\n  // Angles and radius worked out by hand.\n  CHECK(approx(sqrt(square(2.0 * (1.0 + sqrt(0.86))) + (4.0 * 0.14))) ==\n        get(kerr_horizon_radius<double>(\n            {{acos(0.3 / sqrt(0.14)) + M_PI_2, atan2(0.2, 0.1)}}, 2.0,\n            {{0.1, 0.2, 0.3}})));\n\n  // Test for Kerr (mass=2) along pole, for extremal spin not in z direction.\n  // one_minus_eps to make sure we don't get FPE in sqrt(M^2-a^2).\n  const double one_minus_eps = 1.0 - std::numeric_limits<double>::min();\n  const Approx numerical_approx = Approx::custom().epsilon(1.e-8).scale(1.0);\n  CHECK(\n      numerical_approx(2.0) ==\n      get(kerr_horizon_radius<double>(\n          {{acos(0.3 / sqrt(0.14)), atan2(0.2, 0.1)}}, 2.0,\n          {{one_minus_eps * 0.1 / sqrt(0.14), one_minus_eps * 0.2 / sqrt(0.14),\n            one_minus_eps * 0.3 / sqrt(0.14)}})));\n\n  // Test for Kerr (mass=2) along equator,\n  // for extremal spin not in z direction.\n  CHECK(\n      numerical_approx(sqrt(8.0)) ==\n      get(kerr_horizon_radius<double>(\n          {{acos(0.3 / sqrt(0.14)) + M_PI_2, atan2(0.2, 0.1)}}, 2.0,\n          {{one_minus_eps * 0.1 / sqrt(0.14), one_minus_eps * 0.2 / sqrt(0.14),\n            one_minus_eps * 0.3 / sqrt(0.14)}})));\n\n  {\n    const double mass = 0.45;\n    const std::array<double, 3> dimless_spin{{0., 0.3, 0.5}};\n    const double r_plus =\n        mass * (1. + sqrt(1. - dot(dimless_spin, dimless_spin)));\n    const DataVector theta{0., M_PI_4 * 0.34, M_PI_4, M_PI_2 * 0.67, M_PI_2};\n    const DataVector phi{M_PI_2 * 0.4, M_PI * 0.55, M_PI_4, M_PI_4 * 1.2, M_PI};\n    const std::array<DataVector, 2> theta_phi{{theta, phi}};\n    CHECK_ITERABLE_APPROX(kerr_schild_radius_from_boyer_lindquist(\n                              r_plus, theta_phi, mass, dimless_spin),\n                          kerr_horizon_radius(theta_phi, mass, dimless_spin));\n  }\n}\n}  // namespace gr::Solutions\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_KerrSchildCoords.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n#include <tuple>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/KerrSchildCoords.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\n\ntemplate <typename DataType>\nvoid test_kerr_schild_coords(const DataType& used_for_size) {\n  const double bh_mass = 2.54;\n  const double bh_dimless_spin = 0.9375;\n  const auto member_variables = std::make_tuple(bh_mass, bh_dimless_spin);\n  gr::KerrSchildCoords kerr_schild_coords(bh_mass, bh_dimless_spin);\n\n  pypp::check_with_random_values<1>(\n      &gr::KerrSchildCoords::r_coord_squared<DataType>, kerr_schild_coords,\n      \"KerrSchildCoords\", \"r_coord_squared\", {{{-10.0, 10.0}}},\n      member_variables, used_for_size);\n\n  pypp::check_with_random_values<1>(\n      &gr::KerrSchildCoords::cartesian_from_spherical_ks<DataType>,\n      kerr_schild_coords, \"KerrSchildCoords\", \"cartesian_from_spherical_ks\",\n      {{{-10.0, 10.0}}}, member_variables, used_for_size);\n}\n\ntemplate <typename DataType>\nvoid test_coord_transformation_on_xy_plane(const DataType& used_for_size) {\n  const double bh_mass = 0.34;\n  const double bh_dimless_spin = 0.6332;\n  const double spin_a = bh_mass * bh_dimless_spin;\n\n  MAKE_GENERATOR(generator);\n\n  // get random point outside of the ring singularity x^2 + y^2 = a^2\n  // to avoid possible FPE when computing the Jacobian.\n  std::uniform_real_distribution<> distribution_mod(spin_a, 10);\n  const double random_mod = distribution_mod(generator);\n  std::uniform_real_distribution<> distribution_angle(0, 2.0 * M_PI);\n  const double random_angle = distribution_angle(generator);\n  auto random_point_on_xy_plane =\n      make_with_value<tnsr::I<DataType, 3>>(used_for_size, 0.0);\n  get<0>(random_point_on_xy_plane) = random_mod * cos(random_angle);\n  get<1>(random_point_on_xy_plane) = random_mod * sin(random_angle);\n\n  std::uniform_real_distribution<> distribution_vector(-3.0, 3.0);\n  auto random_vector_to_transform =\n      make_with_value<tnsr::I<DataType, 3, Frame::NoFrame>>(used_for_size, 0.0);\n  for (size_t i = 0; i < 3; ++i) {\n    random_vector_to_transform.get(i) = distribution_vector(generator);\n  }\n\n  CHECK_ITERABLE_APPROX(\n      (gr::KerrSchildCoords{bh_mass, bh_dimless_spin}\n           .cartesian_from_spherical_ks(random_vector_to_transform,\n                                        random_point_on_xy_plane)),\n      (pypp::call<tnsr::I<DataType, 3>>(\n          \"KerrSchildCoords\", \"cartesian_from_spherical_ks\",\n          random_vector_to_transform, random_point_on_xy_plane, bh_mass,\n          bh_dimless_spin)));\n}\n\ntemplate <typename DataType>\nvoid test_coord_transformation_along_z_axis(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-13.0, 13.0);\n\n  auto random_radial_vector_to_transform =\n      make_with_value<tnsr::I<DataType, 3, Frame::NoFrame>>(used_for_size, 0.0);\n  get<0>(random_radial_vector_to_transform) = distribution(generator);\n\n  const double z_coordinate = distribution(generator);\n  auto random_point_along_z_axis =\n      make_with_value<tnsr::I<DataType, 3>>(used_for_size, 0.0);\n  get<2>(random_point_along_z_axis) = z_coordinate;\n\n  gr::KerrSchildCoords ks_coords{1.532, 0.9375};\n\n  // New vector should point along z-axis, with v^z = sign(z) v^r\n  const auto transformed_vector = ks_coords.cartesian_from_spherical_ks(\n      random_radial_vector_to_transform, random_point_along_z_axis);\n  CHECK(get<0>(transformed_vector) == 0.0);\n  CHECK(get<1>(transformed_vector) == 0.0);\n  CHECK(get<2>(transformed_vector) ==\n        (z_coordinate > 0.0 ? 1.0 : -1.0) *\n            get<0>(random_radial_vector_to_transform));\n\n  // Since we transform a uniform vector, the transformation should not modify\n  // the new vector upon flipping the sign of the z coordinate\n  get<2>(random_point_along_z_axis) *= -1.0;\n  // v^r flips sign in order to represent the same uniform vector at z < 0\n  get<0>(random_radial_vector_to_transform) *= -1.0;\n  CHECK_ITERABLE_APPROX(\n      transformed_vector,\n      ks_coords.cartesian_from_spherical_ks(random_radial_vector_to_transform,\n                                            random_point_along_z_axis));\n}\n\ntemplate <typename DataType>\nvoid test_theta_component(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-13.0, 13.0);\n\n  auto random_point_along_z_axis =\n      make_with_value<tnsr::I<DataType, 3>>(used_for_size, 0.0);\n  get<2>(random_point_along_z_axis) = distribution(generator);\n\n  // input vector has a nonvanishing theta component along the z-axis.\n  gr::KerrSchildCoords{3.123, 0.854}.cartesian_from_spherical_ks(\n      make_with_value<tnsr::I<DataType, 3, Frame::NoFrame>>(\n          used_for_size, distribution(generator)),\n      random_point_along_z_axis);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.KerrSchildCoords\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n\n  const double d(std::numeric_limits<double>::signaling_NaN());\n  const DataVector dv(5);\n  test_kerr_schild_coords(d);\n  test_kerr_schild_coords(dv);\n  test_coord_transformation_along_z_axis(d);\n  test_coord_transformation_along_z_axis(dv);\n  test_coord_transformation_on_xy_plane(d);\n  test_coord_transformation_on_xy_plane(dv);\n\n  gr::KerrSchildCoords kerr_schild_coords(0.8624, 0.151);\n  test_serialization(kerr_schild_coords);\n\n  gr::KerrSchildCoords kerr_schild_coords_copy(0.8624, 0.151);\n  test_move_semantics(std::move(kerr_schild_coords),\n                      kerr_schild_coords_copy);  // NOLINT\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      (test_theta_component(std::numeric_limits<double>::signaling_NaN())),\n      Catch::Matchers::ContainsSubstring(\n          \"The input vector must have a vanishing theta component\"));\n  CHECK_THROWS_WITH(\n      (test_theta_component(DataVector(5))),\n      Catch::Matchers::ContainsSubstring(\n          \"The input vector must have a vanishing theta component\"));\n  CHECK_THROWS_WITH(\n      (gr::KerrSchildCoords(-4.21, 0.999)),\n      Catch::Matchers::ContainsSubstring(\"The mass must be positive\"));\n  CHECK_THROWS_WITH((gr::KerrSchildCoords(0.15, -1.3)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The dimensionless spin must be in the range (-1, 1)\"));\n  CHECK_THROWS_WITH((gr::KerrSchildCoords(1.532, 4.2)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"The dimensionless spin must be in the range (-1, 1)\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_ProjectionOperators.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/EagerMath/RaiseOrLowerIndex.hpp\"\n#include \"DataStructures/Tensor/Expressions/Evaluate.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/Structure/Direction.hpp\"\n#include \"Domain/Structure/Side.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/RandomUnitNormal.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InterfaceNullNormal.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/ProjectionOperators.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalOneForm.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeNormalVector.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\nconst Approx custom_approx = Approx::custom().epsilon(1.e-12).scale(1.0);\n\ntemplate <size_t SpatialDim, typename DataType>\ntnsr::A<DataType, SpatialDim, Frame::Inertial>\nextend_spatial_vector_to_spacetime(\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>& spatial_vector,\n    const DataType& used_for_size) {\n  auto spacetime_vector =\n      make_with_value<tnsr::A<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    spacetime_vector.get(i + 1) = spatial_vector.get(i);\n  }\n  return spacetime_vector;\n}\n\ntemplate <size_t SpatialDim, typename DataType>\ntnsr::a<DataType, SpatialDim, Frame::Inertial>\nextend_spatial_one_form_to_spacetime(\n    const tnsr::i<DataType, SpatialDim, Frame::Inertial>& spatial_one_form,\n    const tnsr::I<DataType, SpatialDim, Frame::Inertial>& shift,\n    const DataType& used_for_size) {\n  auto spacetime_one_form =\n      make_with_value<tnsr::a<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    spacetime_one_form.get(i + 1) = spatial_one_form.get(i);\n  }\n  spacetime_one_form.get(0) = get(dot_product(shift, spatial_one_form));\n  return spacetime_one_form;\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid check_aa_orthogonality(\n    const tnsr::aa<DataType, SpatialDim, Frame::Inertial>& projection_aa,\n    const tnsr::A<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_vector) {\n  const auto right_contraction = tenex::evaluate<ti::a>(\n      projection_aa(ti::a, ti::b) * spacetime_normal_vector(ti::B));\n  const auto left_contraction = tenex::evaluate<ti::b>(\n      spacetime_normal_vector(ti::A) * projection_aa(ti::a, ti::b));\n  const auto zero =\n      make_with_value<tnsr::a<DataType, SpatialDim, Frame::Inertial>>(\n          get_size(get<0, 0>(projection_aa)), 0.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(right_contraction, zero, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(left_contraction, zero, custom_approx);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid check_ab_orthogonality(\n    const tnsr::Ab<DataType, SpatialDim, Frame::Inertial>& projection_ab,\n    const tnsr::a<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_one_form,\n    const tnsr::A<DataType, SpatialDim, Frame::Inertial>&\n        spacetime_normal_vector) {\n  const auto lower_contraction = tenex::evaluate<ti::b>(\n      spacetime_normal_one_form(ti::a) * projection_ab(ti::A, ti::b));\n  const auto upper_contraction = tenex::evaluate<ti::A>(\n      projection_ab(ti::A, ti::b) * spacetime_normal_vector(ti::B));\n  const auto zero_one_form =\n      make_with_value<tnsr::a<DataType, SpatialDim, Frame::Inertial>>(\n          get_size(get<0, 0>(projection_ab)), 0.0);\n  const auto zero_vector =\n      make_with_value<tnsr::A<DataType, SpatialDim, Frame::Inertial>>(\n          get_size(get<0, 0>(projection_ab)), 0.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(lower_contraction, zero_one_form, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(upper_contraction, zero_vector, custom_approx);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_projection_operator(const DataType& used_for_size) {\n  {\n    tnsr::II<DataType, SpatialDim, Frame::Inertial> (*f)(\n        const tnsr::II<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::I<DataType, SpatialDim, Frame::Inertial>&) =\n        &gr::transverse_projection_operator<DataType, SpatialDim,\n                                            Frame::Inertial>;\n    pypp::check_with_random_values<1>(f, \"ProjectionOperators\",\n                                      \"transverse_projection_operator\",\n                                      {{{-1., 1.}}}, used_for_size);\n  }\n\n  {\n    tnsr::ii<DataType, SpatialDim, Frame::Inertial> (*f)(\n        const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::i<DataType, SpatialDim, Frame::Inertial>&) =\n        &gr::transverse_projection_operator<DataType, SpatialDim,\n                                            Frame::Inertial>;\n    pypp::check_with_random_values<1>(f, \"ProjectionOperators\",\n                                      \"transverse_projection_operator\",\n                                      {{{-1., 1.}}}, used_for_size);\n  }\n\n  {\n    tnsr::Ij<DataType, SpatialDim, Frame::Inertial> (*f)(\n        const tnsr::I<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::i<DataType, SpatialDim, Frame::Inertial>&) =\n        &gr::transverse_projection_operator<DataType, SpatialDim,\n                                            Frame::Inertial>;\n    pypp::check_with_random_values<1>(\n        f, \"ProjectionOperators\",\n        \"transverse_projection_operator_mixed_from_spatial_input\",\n        {{{-1., 1.}}}, used_for_size);\n  }\n\n  {\n    tnsr::AA<DataType, SpatialDim, Frame::Inertial> (*f)(\n        const tnsr::AA<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::A<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::I<DataType, SpatialDim, Frame::Inertial>&) =\n        &gr::transverse_projection_operator<DataType, SpatialDim,\n                                            Frame::Inertial>;\n    pypp::check_with_random_values<1>(\n        f, \"ProjectionOperators\", \"projection_operator_transverse_to_interface\",\n        {{{-1., 1.}}}, used_for_size);\n  }\n\n  {\n    tnsr::aa<DataType, SpatialDim, Frame::Inertial> (*f)(\n        const tnsr::aa<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::a<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::i<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::I<DataType, SpatialDim, Frame::Inertial>&) =\n        &gr::transverse_projection_operator<DataType, SpatialDim,\n                                            Frame::Inertial>;\n    pypp::check_with_random_values<1>(\n        f, \"ProjectionOperators\", \"projection_operator_transverse_to_interface\",\n        {{{-1., 1.}}}, used_for_size);\n  }\n\n  {\n    tnsr::Ab<DataType, SpatialDim, Frame::Inertial> (*f)(\n        const tnsr::A<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::a<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::I<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::i<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::I<DataType, SpatialDim, Frame::Inertial>&) =\n        &gr::transverse_projection_operator<DataType, SpatialDim,\n                                            Frame::Inertial>;\n    pypp::check_with_random_values<1>(\n        f, \"ProjectionOperators\",\n        \"projection_operator_transverse_to_interface_mixed\", {{{-1., 1.}}},\n        used_for_size);\n  }\n\n  const auto data_size = get_size(used_for_size);\n  MAKE_GENERATOR(generator);\n  const auto lapse =\n      TestHelpers::gr::random_lapse(make_not_null(&generator), used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<SpatialDim>(\n      make_not_null(&generator), used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<SpatialDim>(\n          make_not_null(&generator), used_for_size);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const auto spacetime_metric =\n      gr::spacetime_metric(lapse, shift, spatial_metric);\n  const auto inverse_spacetime_metric =\n      gr::inverse_spacetime_metric(lapse, shift, inverse_spatial_metric);\n  const auto spacetime_normal_one_form =\n      gr::spacetime_normal_one_form<DataType, SpatialDim, Frame::Inertial>(\n          lapse);\n  const auto spacetime_normal_vector =\n      gr::spacetime_normal_vector(lapse, shift);\n\n  const auto interface_unit_normal_vector =\n      random_unit_normal(make_not_null(&generator), spatial_metric);\n  auto interface_unit_normal_one_form =\n      make_with_value<tnsr::i<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  raise_or_lower_index(make_not_null(&interface_unit_normal_one_form),\n                       interface_unit_normal_vector, spatial_metric);\n\n  const auto projection_aa = gr::transverse_projection_operator(\n      spacetime_metric, spacetime_normal_one_form,\n      interface_unit_normal_one_form, shift);\n  check_aa_orthogonality(projection_aa, spacetime_normal_vector);\n\n  tnsr::aa<DataType, SpatialDim, Frame::Inertial> projection_aa_not_null(\n      data_size);\n  gr::transverse_projection_operator(\n      make_not_null(&projection_aa_not_null), spacetime_metric,\n      spacetime_normal_one_form, interface_unit_normal_one_form, shift);\n  check_aa_orthogonality(projection_aa_not_null, spacetime_normal_vector);\n\n  const auto projection_ab = gr::transverse_projection_operator(\n      spacetime_normal_vector, spacetime_normal_one_form,\n      interface_unit_normal_vector, interface_unit_normal_one_form, shift);\n  check_ab_orthogonality(projection_ab, spacetime_normal_one_form,\n                         spacetime_normal_vector);\n\n  tnsr::Ab<DataType, SpatialDim, Frame::Inertial> projection_ab_not_null(\n      data_size);\n  gr::transverse_projection_operator(\n      make_not_null(&projection_ab_not_null), spacetime_normal_vector,\n      spacetime_normal_one_form, interface_unit_normal_vector,\n      interface_unit_normal_one_form, shift);\n  check_ab_orthogonality(projection_ab_not_null, spacetime_normal_one_form,\n                         spacetime_normal_vector);\n\n  const auto outgoing_null_one_form =\n      gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n          spacetime_normal_one_form, interface_unit_normal_one_form, shift,\n          1.0);\n  const auto incoming_null_one_form =\n      gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n          spacetime_normal_one_form, interface_unit_normal_one_form, shift,\n          -1.0);\n  const auto outgoing_null_vector =\n      gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n          spacetime_normal_vector, interface_unit_normal_vector, 1.0);\n  const auto incoming_null_vector =\n      gr::interface_null_normal<DataType, SpatialDim, Frame::Inertial>(\n          spacetime_normal_vector, interface_unit_normal_vector, -1.0);\n\n  const auto contract_aa_right = [&](const auto& vector) {\n    return tenex::evaluate<ti::a>(projection_aa(ti::a, ti::b) * vector(ti::B));\n  };\n  const auto contract_aa_left = [&](const auto& vector) {\n    return tenex::evaluate<ti::b>(vector(ti::A) * projection_aa(ti::a, ti::b));\n  };\n  const auto contract_ab_left = [&](const auto& one_form) {\n    return tenex::evaluate<ti::b>(one_form(ti::a) *\n                                  projection_ab(ti::A, ti::b));\n  };\n  const auto zero_spacetime_one_form =\n      make_with_value<tnsr::a<DataType, SpatialDim, Frame::Inertial>>(data_size,\n                                                                      0.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_aa_right(outgoing_null_vector),\n                               zero_spacetime_one_form, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_aa_right(incoming_null_vector),\n                               zero_spacetime_one_form, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_aa_left(outgoing_null_vector),\n                               zero_spacetime_one_form, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_aa_left(incoming_null_vector),\n                               zero_spacetime_one_form, custom_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_ab_left(outgoing_null_one_form),\n                               zero_spacetime_one_form, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_ab_left(incoming_null_one_form),\n                               zero_spacetime_one_form, custom_approx);\n\n  const auto interface_normal_one_form = extend_spatial_one_form_to_spacetime(\n      interface_unit_normal_one_form, shift, used_for_size);\n  const auto interface_normal_vector = extend_spatial_vector_to_spacetime(\n      interface_unit_normal_vector, used_for_size);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_aa_right(interface_normal_vector),\n                               zero_spacetime_one_form, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_aa_left(interface_normal_vector),\n                               zero_spacetime_one_form, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(contract_ab_left(interface_normal_one_form),\n                               zero_spacetime_one_form, custom_approx);\n\n  const auto trace_aa = tenex::evaluate(inverse_spacetime_metric(ti::A, ti::B) *\n                                        projection_aa(ti::a, ti::b));\n  const auto trace_ab = tenex::evaluate(projection_ab(ti::A, ti::a));\n  const auto expected_trace =\n      make_with_value<Scalar<DataType>>(used_for_size, SpatialDim - 1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(trace_aa, expected_trace, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(trace_ab, expected_trace, custom_approx);\n\n  const auto raised_projection_aa = tenex::evaluate<ti::A, ti::b>(\n      inverse_spacetime_metric(ti::A, ti::C) * projection_aa(ti::c, ti::b));\n  CHECK_ITERABLE_CUSTOM_APPROX(raised_projection_aa, projection_ab,\n                               custom_approx);\n\n  const auto idempotent_projection_ab = tenex::evaluate<ti::A, ti::b>(\n      projection_ab(ti::A, ti::c) * projection_ab(ti::C, ti::b));\n  CHECK_ITERABLE_CUSTOM_APPROX(idempotent_projection_ab, projection_ab,\n                               custom_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(outgoing_null_one_form, outgoing_null_one_form,\n                      inverse_spacetime_metric)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(incoming_null_one_form, incoming_null_one_form,\n                      inverse_spacetime_metric)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(outgoing_null_vector, outgoing_null_vector,\n                      spacetime_metric)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      get(dot_product(incoming_null_vector, incoming_null_vector,\n                      spacetime_metric)),\n      make_with_value<DataType>(used_for_size, 0.0), custom_approx);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_projection_operator_spatial(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<SpatialDim>(\n          make_not_null(&generator), used_for_size);\n  const auto inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const auto interface_unit_normal_vector =\n      random_unit_normal(make_not_null(&generator), spatial_metric);\n  auto interface_unit_normal_one_form =\n      make_with_value<tnsr::i<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  raise_or_lower_index(make_not_null(&interface_unit_normal_one_form),\n                       interface_unit_normal_vector, spatial_metric);\n\n  const auto projection_II = gr::transverse_projection_operator(\n      inverse_spatial_metric, interface_unit_normal_vector);\n  const auto projection_ii = gr::transverse_projection_operator(\n      spatial_metric, interface_unit_normal_one_form);\n  const auto projection_Ij = gr::transverse_projection_operator(\n      interface_unit_normal_vector, interface_unit_normal_one_form);\n\n  auto projection_II_not_null =\n      make_with_value<tnsr::II<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  gr::transverse_projection_operator(make_not_null(&projection_II_not_null),\n                                     inverse_spatial_metric,\n                                     interface_unit_normal_vector);\n  CHECK_ITERABLE_CUSTOM_APPROX(projection_II_not_null, projection_II,\n                               custom_approx);\n\n  auto projection_ii_not_null =\n      make_with_value<tnsr::ii<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  gr::transverse_projection_operator(make_not_null(&projection_ii_not_null),\n                                     spatial_metric,\n                                     interface_unit_normal_one_form);\n  CHECK_ITERABLE_CUSTOM_APPROX(projection_ii_not_null, projection_ii,\n                               custom_approx);\n\n  auto projection_Ij_not_null =\n      make_with_value<tnsr::Ij<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  gr::transverse_projection_operator(make_not_null(&projection_Ij_not_null),\n                                     interface_unit_normal_vector,\n                                     interface_unit_normal_one_form);\n  CHECK_ITERABLE_CUSTOM_APPROX(projection_Ij_not_null, projection_Ij,\n                               custom_approx);\n\n  const auto zero_vector =\n      make_with_value<tnsr::I<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n  const auto zero_one_form =\n      make_with_value<tnsr::i<DataType, SpatialDim, Frame::Inertial>>(\n          used_for_size, 0.0);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate<ti::I>(projection_II(ti::I, ti::J) *\n                             interface_unit_normal_one_form(ti::j)),\n      zero_vector, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate<ti::J>(interface_unit_normal_one_form(ti::i) *\n                             projection_II(ti::I, ti::J)),\n      zero_vector, custom_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate<ti::i>(projection_ii(ti::i, ti::j) *\n                             interface_unit_normal_vector(ti::J)),\n      zero_one_form, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate<ti::j>(interface_unit_normal_vector(ti::I) *\n                             projection_ii(ti::i, ti::j)),\n      zero_one_form, custom_approx);\n\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate<ti::I>(projection_Ij(ti::I, ti::j) *\n                             interface_unit_normal_vector(ti::J)),\n      zero_vector, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(\n      tenex::evaluate<ti::j>(interface_unit_normal_one_form(ti::i) *\n                             projection_Ij(ti::I, ti::j)),\n      zero_one_form, custom_approx);\n\n  const auto trace_II = tenex::evaluate(spatial_metric(ti::i, ti::j) *\n                                        projection_II(ti::I, ti::J));\n  const auto trace_ii = tenex::evaluate(inverse_spatial_metric(ti::I, ti::J) *\n                                        projection_ii(ti::i, ti::j));\n  const auto trace_Ij = tenex::evaluate(projection_Ij(ti::I, ti::i));\n  const auto expected_trace =\n      make_with_value<Scalar<DataType>>(used_for_size, SpatialDim - 1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(trace_II, expected_trace, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(trace_ii, expected_trace, custom_approx);\n  CHECK_ITERABLE_CUSTOM_APPROX(trace_Ij, expected_trace, custom_approx);\n\n  const auto idempotent_projection_Ij = tenex::evaluate<ti::I, ti::j>(\n      projection_Ij(ti::I, ti::k) * projection_Ij(ti::K, ti::j));\n  CHECK_ITERABLE_CUSTOM_APPROX(idempotent_projection_Ij, projection_Ij,\n                               custom_approx);\n\n  const auto raised_projection_ii = tenex::evaluate<ti::I, ti::j>(\n      inverse_spatial_metric(ti::I, ti::K) * projection_ii(ti::k, ti::j));\n  CHECK_ITERABLE_CUSTOM_APPROX(raised_projection_ii, projection_Ij,\n                               custom_approx);\n}\n}  // namespace\n\nnamespace {\nusing frame = Frame::Inertial;\nconstexpr size_t SpatialDim = 3;\n\n// Compare with fixed reference values from SpEC on a\n// 3D mesh with 3x3x3 grid points.\nvoid compare_spatial_projection_tensors_with_spec() {\n  constexpr size_t grid_size_each_dimension = 3;\n  const std::array<double, 3> lower_bound{{299., -0.5, -0.5}};\n  // Setup grid\n  Mesh<SpatialDim> mesh{grid_size_each_dimension, Spectral::Basis::Legendre,\n                        Spectral::Quadrature::GaussLobatto};\n  // Setup coordinates\n  const Direction<SpatialDim> direction(1, Side::Upper);  // +y direction\n  const size_t slice_grid_points =\n      mesh.extents().slice_away(direction.dimension()).product();\n  const auto inertial_coords = [&slice_grid_points, &lower_bound]() {\n    tnsr::I<DataVector, SpatialDim, frame> tmp(slice_grid_points, 0.);\n    // +y direction\n    get<1>(tmp) = 0.5;\n    for (size_t i = 0; i < SpatialDim; ++i) {\n      for (size_t j = 0; j < SpatialDim; ++j) {\n        get<0>(tmp)[i * SpatialDim + j] =\n            lower_bound[0] + 0.5 * static_cast<double>(i);\n        get<2>(tmp)[i * SpatialDim + j] =\n            lower_bound[2] + 0.5 * static_cast<double>(j);\n      }\n    }\n    return tmp;\n  }();\n\n  // 1. Projection IJ\n  auto local_inverse_spatial_metric =\n      make_with_value<tnsr::II<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  auto local_unit_interface_normal_vector =\n      make_with_value<tnsr::I<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  auto local_spatial_projection_IJ =\n      make_with_value<tnsr::II<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n\n  // Setting inverse_spatial_metric to compare with values from SpEC\n  for (size_t i = 0; i < get<0>(inertial_coords).size(); ++i) {\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      local_inverse_spatial_metric.get(0, j)[i] = 41.;\n      local_inverse_spatial_metric.get(1, j)[i] = 43.;\n      local_inverse_spatial_metric.get(2, j)[i] = 47.;\n    }\n  }\n  // Setting unit_interface_normal_vector to compare with values from SpEC\n  get<0>(local_unit_interface_normal_vector) = -1.;\n  get<1>(local_unit_interface_normal_vector) = 1.;\n  get<2>(local_unit_interface_normal_vector) = 1.;\n\n  // Call tested function\n  gr::transverse_projection_operator(\n      make_not_null(&local_spatial_projection_IJ), local_inverse_spatial_metric,\n      local_unit_interface_normal_vector);\n\n  // Initialize with values from SpEC\n  auto spec_spatial_projection_IJ =\n      make_with_value<tnsr::II<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  {\n    const std::array<double, 9> spec_vals = {\n        {40., 42., 42., 42., 42., 42., 42., 42., 46.}};\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t k = j; k < SpatialDim; ++k) {\n        spec_spatial_projection_IJ.get(j, k) =\n            gsl::at(spec_vals, j * SpatialDim + k);\n      }\n    }\n  }\n\n  // Compare values returned to those from SpEC\n  CHECK_ITERABLE_CUSTOM_APPROX(local_spatial_projection_IJ,\n                               spec_spatial_projection_IJ, custom_approx);\n\n  // 2. Projection ij\n  auto local_spatial_metric =\n      make_with_value<tnsr::ii<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  auto local_unit_interface_normal_one_form =\n      make_with_value<tnsr::i<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  auto local_spatial_projection_ij =\n      make_with_value<tnsr::ii<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n\n  // Setting inverse_spatial_metric to compare with values from SpEC\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    local_spatial_metric.get(0, i) = 263.;\n    local_spatial_metric.get(1, i) = 269.;\n    local_spatial_metric.get(2, i) = 271.;\n  }\n  // Setting unit_interface_normal_vector to compare with values from SpEC\n  get<0>(local_unit_interface_normal_one_form) = -1.;\n  get<1>(local_unit_interface_normal_one_form) = 1.;\n  get<2>(local_unit_interface_normal_one_form) = 1.;\n\n  // Call tested function\n  gr::transverse_projection_operator(\n      make_not_null(&local_spatial_projection_ij), local_spatial_metric,\n      local_unit_interface_normal_one_form);\n\n  // Initialize with values from SpEC\n  auto spec_spatial_projection_ij =\n      make_with_value<tnsr::ii<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  {\n    const std::array<double, 9> spec_vals = {\n        {262., 264., 264., 264., 268., 268., 264., 268., 270.}};\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t k = j; k < SpatialDim; ++k) {\n        spec_spatial_projection_ij.get(j, k) =\n            gsl::at(spec_vals, j * SpatialDim + k);\n      }\n    }\n  }\n\n  // Compare values returned to those from SpEC\n  CHECK_ITERABLE_CUSTOM_APPROX(local_spatial_projection_ij,\n                               spec_spatial_projection_ij, custom_approx);\n\n  // 3. Projection Ij\n  auto local_spatial_projection_Ij =\n      make_with_value<tnsr::Ij<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n\n  // Call tested function\n  gr::transverse_projection_operator(\n      make_not_null(&local_spatial_projection_Ij),\n      local_unit_interface_normal_vector, local_unit_interface_normal_one_form);\n\n  // Initialize with values from SpEC\n  auto spec_spatial_projection_Ij =\n      make_with_value<tnsr::Ij<DataVector, SpatialDim, Frame::Inertial>>(\n          inertial_coords, 0.);\n  {\n    const std::array<double, 9> spec_vals = {\n        {0., 1., 1., 1., 0., -1., 1., -1., 0.}};\n    for (size_t j = 0; j < SpatialDim; ++j) {\n      for (size_t k = 0; k < SpatialDim; ++k) {\n        spec_spatial_projection_Ij.get(j, k) =\n            gsl::at(spec_vals, j * SpatialDim + k);\n      }\n    }\n  }\n\n  // Compare values returned to those from SpEC\n  CHECK_ITERABLE_CUSTOM_APPROX(local_spatial_projection_Ij,\n                               spec_spatial_projection_Ij, custom_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.ProjectionOps\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_projection_operator, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_projection_operator_spatial,\n                                    (1, 2, 3));\n\n  compare_spatial_projection_tensors_with_spec();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_Psi4.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Expressions/Evaluate.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Psi4Real.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylPropagating.hpp\"\n\nnamespace {\ntemplate <typename RealDataType>\nvoid test_compute_item_in_databox(const RealDataType& used_for_size_real) {\n  TestHelpers::db::test_compute_tag<gr::Tags::Psi4RealCompute<Frame::Inertial>>(\n      \"Psi4Real\");\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.1, 3.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  const auto spatial_ricci =\n      make_with_random_values<tnsr::ii<RealDataType, 3, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size_real);\n  const auto extrinsic_curvature =\n      make_with_random_values<tnsr::ii<RealDataType, 3, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size_real);\n  const auto cov_deriv_extrinsic_curvature =\n      make_with_random_values<tnsr::ijj<RealDataType, 3, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size_real);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<3, RealDataType, Frame::Inertial>(\n          nn_generator, used_for_size_real);\n  const auto inv_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  const auto inertial_coords =\n      make_with_random_values<tnsr::I<RealDataType, 3, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size_real);\n\n  const auto box = db::create<\n      db::AddSimpleTags<\n          gr::Tags::SpatialRicci<RealDataType, 3>,\n          gr::Tags::ExtrinsicCurvature<RealDataType, 3>,\n          ::Tags::deriv<gr::Tags::ExtrinsicCurvature<RealDataType, 3>,\n                        tmpl::size_t<3>, Frame::Inertial>,\n          gr::Tags::SpatialMetric<RealDataType, 3>,\n          gr::Tags::InverseSpatialMetric<RealDataType, 3>,\n          domain::Tags::Coordinates<3, Frame::Inertial>>,\n      db::AddComputeTags<gr::Tags::Psi4RealCompute<Frame::Inertial>>>(\n      spatial_ricci, extrinsic_curvature, cov_deriv_extrinsic_curvature,\n      spatial_metric, inv_spatial_metric, inertial_coords);\n  const auto expected = gr::psi_4_real(\n      spatial_ricci, extrinsic_curvature, cov_deriv_extrinsic_curvature,\n      spatial_metric, inv_spatial_metric, inertial_coords);\n  CHECK_ITERABLE_APPROX((db::get<gr::Tags::Psi4Real<RealDataType>>(box)),\n                        expected);\n}\n\ntemplate <typename RealDataType, typename ComplexDataType>\nvoid test_psi_4(const RealDataType& used_for_size_real,\n                const ComplexDataType& /*used_for_size_complex*/) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(0.1, 3.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  const auto spatial_ricci =\n      make_with_random_values<tnsr::ii<RealDataType, 3, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size_real);\n  const auto extrinsic_curvature =\n      make_with_random_values<tnsr::ii<RealDataType, 3, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size_real);\n  const auto cov_deriv_extrinsic_curvature =\n      make_with_random_values<tnsr::ijj<RealDataType, 3, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size_real);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<3, RealDataType, Frame::Inertial>(\n          nn_generator, used_for_size_real);\n  const auto inv_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n  auto inertial_coords =\n      make_with_random_values<tnsr::I<RealDataType, 3, Frame::Inertial>>(\n          nn_generator, nn_distribution, used_for_size_real);\n\n  const auto python_psi_4 = pypp::call<Scalar<ComplexDataType>>(\n      \"GeneralRelativity.Psi4\", \"psi_4\", spatial_ricci, extrinsic_curvature,\n      cov_deriv_extrinsic_curvature, spatial_metric, inv_spatial_metric,\n      inertial_coords);\n  const auto expected = gr::psi_4(spatial_ricci, extrinsic_curvature,\n                                  cov_deriv_extrinsic_curvature, spatial_metric,\n                                  inv_spatial_metric, inertial_coords);\n  Approx local_approx = Approx::custom().epsilon(1e-13).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(expected, python_psi_4, local_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.Psi4\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"PointwiseFunctions/\");\n\n  const size_t size = 5;\n  const DataVector used_for_size_real_dv =\n      DataVector(size, std::numeric_limits<double>::signaling_NaN());\n  const ComplexDataVector used_for_size_complex_dv =\n      ComplexDataVector(size, std::numeric_limits<double>::signaling_NaN());\n  test_psi_4(used_for_size_real_dv, used_for_size_complex_dv);\n  test_compute_item_in_databox(used_for_size_real_dv);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_Ricci.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Ricci.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n\nnamespace {\ntemplate <size_t Dim, IndexType TypeOfIndex, typename DataType>\nvoid test_compute_item_in_databox(const DataType& used_for_size) {\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpatialRicciCompute<DataType, Dim, Frame::Inertial>>(\n      \"SpatialRicci\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::SpatialRicciScalarCompute<DataType, Dim, Frame::Inertial>>(\n      \"SpatialRicciScalar\");\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-3.0, 3.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  const auto christoffel_2nd_kind = make_with_random_values<\n      tnsr::Abb<DataType, Dim, Frame::Inertial, TypeOfIndex>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto d_christoffel_2nd_kind = make_with_random_values<\n      tnsr::aBcc<DataType, Dim, Frame::Inertial, TypeOfIndex>>(\n      nn_generator, nn_distribution, used_for_size);\n  const auto spatial_metric =\n      TestHelpers::gr::random_spatial_metric<Dim, DataType, Frame::Inertial>(\n          nn_generator, used_for_size);\n  const auto inv_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  const auto box = db::create<\n      db::AddSimpleTags<\n          gr::Tags::SpatialChristoffelSecondKind<DataType, Dim>,\n          ::Tags::deriv<gr::Tags::SpatialChristoffelSecondKind<DataType, Dim>,\n                        tmpl::size_t<Dim>, Frame::Inertial>,\n          gr::Tags::InverseSpatialMetric<DataType, Dim>>,\n      db::AddComputeTags<\n          gr::Tags::SpatialRicciCompute<DataType, Dim, Frame::Inertial>,\n          gr::Tags::SpatialRicciScalarCompute<DataType, Dim, Frame::Inertial>>>(\n      christoffel_2nd_kind, d_christoffel_2nd_kind, inv_spatial_metric);\n\n  const auto expected =\n      gr::ricci_tensor(christoffel_2nd_kind, d_christoffel_2nd_kind);\n  const auto expected_spatial_scalar =\n      gr::ricci_scalar(expected, inv_spatial_metric);\n  CHECK_ITERABLE_APPROX((db::get<gr::Tags::SpatialRicci<DataType, Dim>>(box)),\n                        expected);\n  CHECK_ITERABLE_APPROX((db::get<gr::Tags::SpatialRicciScalar<DataType>>(box)),\n                        expected_spatial_scalar);\n}\n\ntemplate <size_t Dim, IndexType TypeOfIndex, typename DataType>\nvoid test_ricci(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::aa<DataType, Dim, Frame::Inertial, TypeOfIndex> (*)(\n          const tnsr::Abb<DataType, Dim, Frame::Inertial, TypeOfIndex>&,\n          const tnsr::aBcc<DataType, Dim, Frame::Inertial, TypeOfIndex>&)>(\n          &gr::ricci_tensor<Dim, Frame::Inertial, TypeOfIndex, DataType>),\n      \"Ricci\", \"ricci_tensor\", {{{-1., 1.}}}, used_for_size);\n}\n\ntemplate <size_t Dim, IndexType TypeOfIndex, typename DataType>\nvoid test_ricci_scalar(const DataType& used_for_size) {\n  Scalar<DataType> (*f)(\n      const tnsr::aa<DataType, Dim, Frame::Inertial, TypeOfIndex>&,\n      const tnsr::AA<DataType, Dim, Frame::Inertial, TypeOfIndex>&) =\n      &gr::ricci_scalar<Dim, Frame::Inertial, TypeOfIndex, DataType>;\n  pypp::check_with_random_values<1>(f, \"RicciScalar\", \"ricci_scalar\",\n                                    {{{-1., 1.}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.Ricci\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_ricci, (1, 2, 3),\n                                    (IndexType::Spatial, IndexType::Spacetime));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_ricci_scalar, (1, 2, 3),\n                                    (IndexType::Spatial, IndexType::Spacetime));\n  test_compute_item_in_databox<3, IndexType::Spatial>(d);\n  test_compute_item_in_databox<3, IndexType::Spatial>(dv);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_SpacetimeDerivativeOfGothG.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeDerivativeOfGothG.hpp\"\n\nnamespace gr {\n\ntemplate <typename DataType, size_t SpatialDim>\nvoid test_compute_spacetime_deriv_of_goth_g(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::aBB<DataVector, SpatialDim, Frame::Inertial> (*)(\n          const tnsr::AA<DataType, SpatialDim, Frame::Inertial>&,\n          const tnsr::abb<DataType, SpatialDim, Frame::Inertial>&,\n          const Scalar<DataType>&,\n          const tnsr::a<DataType, SpatialDim, Frame::Inertial>&,\n          const Scalar<DataType>&,\n          const tnsr::a<DataType, SpatialDim, Frame::Inertial>&)>(\n          &spacetime_deriv_of_goth_g<DataType, SpatialDim, Frame::Inertial>),\n      \"SpacetimeDerivativeOfGothG\", \"spacetime_deriv_of_goth_g\",\n      {{{0.0, 1.0}}}, used_for_size);\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.GeneralRelativity.SpacetimeDerivativeOfGothG\",\n    \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n  const DataVector used_for_size(5);\n  test_compute_spacetime_deriv_of_goth_g<DataVector, 1>(used_for_size);\n  test_compute_spacetime_deriv_of_goth_g<DataVector, 3>(used_for_size);\n}\n\n}  // namespace gr\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags/Conformal.hpp\"\n\nnamespace {\nstruct ArbitraryFrame;\nstruct ArbitraryType;\n\nstruct Tag : db::SimpleTag {\n  using type = int;\n};\n}  // namespace\n\ntemplate <size_t Dim, typename Frame, typename Type>\nvoid test_simple_tags() {\n  TestHelpers::db::test_simple_tag<gr::Tags::SpacetimeMetric<Type, Dim, Frame>>(\n      \"SpacetimeMetric\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::InverseSpacetimeMetric<Type, Dim, Frame>>(\n      \"InverseSpacetimeMetric\");\n  TestHelpers::db::test_simple_tag<gr::Tags::SpatialMetric<Type, Dim, Frame>>(\n      \"SpatialMetric\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::InverseSpatialMetric<Type, Dim, Frame>>(\"InverseSpatialMetric\");\n  TestHelpers::db::test_simple_tag<gr::Tags::DetSpatialMetric<Type>>(\n      \"DetSpatialMetric\");\n  TestHelpers::db::test_simple_tag<gr::Tags::SqrtDetSpatialMetric<Type>>(\n      \"SqrtDetSpatialMetric\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::DerivDetSpatialMetric<Type, Dim, Frame>>(\n      \"DerivDetSpatialMetric\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::DerivInverseSpatialMetric<Type, Dim, Frame>>(\n      \"DerivInverseSpatialMetric\");\n  TestHelpers::db::test_simple_tag<gr::Tags::Shift<Type, Dim, Frame>>(\"Shift\");\n  TestHelpers::db::test_simple_tag<gr::Tags::Lapse<Type>>(\"Lapse\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::DerivativesOfSpacetimeMetric<Type, Dim, Frame>>(\n      \"DerivativesOfSpacetimeMetric\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::SpacetimeChristoffelFirstKind<Type, Dim, Frame>>(\n      \"SpacetimeChristoffelFirstKind\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::SpacetimeChristoffelSecondKind<Type, Dim, Frame>>(\n      \"SpacetimeChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::SpatialChristoffelFirstKind<Type, Dim, Frame>>(\n      \"SpatialChristoffelFirstKind\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::SpatialChristoffelSecondKind<Type, Dim, Frame>>(\n      \"SpatialChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::SpacetimeNormalOneForm<Type, Dim, Frame>>(\n      \"SpacetimeNormalOneForm\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::SpacetimeNormalVector<Type, Dim, Frame>>(\n      \"SpacetimeNormalVector\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::TraceSpacetimeChristoffelFirstKind<Type, Dim, Frame>>(\n      \"TraceSpacetimeChristoffelFirstKind\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::TraceSpacetimeChristoffelSecondKind<Type, Dim, Frame>>(\n      \"TraceSpacetimeChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::TraceSpatialChristoffelFirstKind<Type, Dim, Frame>>(\n      \"TraceSpatialChristoffelFirstKind\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::TraceSpatialChristoffelSecondKind<Type, Dim, Frame>>(\n      \"TraceSpatialChristoffelSecondKind\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::SpatialChristoffelSecondKindContracted<Type, Dim, Frame>>(\n      \"SpatialChristoffelSecondKindContracted\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::ExtrinsicCurvature<Type, Dim, Frame>>(\"ExtrinsicCurvature\");\n  TestHelpers::db::test_simple_tag<gr::Tags::TraceExtrinsicCurvature<Type>>(\n      \"TraceExtrinsicCurvature\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::CovariantDerivativeOfExtrinsicCurvature<Type, Dim, Frame>>(\n      \"CovariantDerivativeOfExtrinsicCurvature\");\n  TestHelpers::db::test_simple_tag<gr::Tags::SpatialRicci<Type, Dim, Frame>>(\n      \"SpatialRicci\");\n  TestHelpers::db::test_simple_tag<gr::Tags::EnergyDensity<Type>>(\n      \"EnergyDensity\");\n  TestHelpers::db::test_simple_tag<gr::Tags::MomentumDensity<Type, Dim, Frame>>(\n      \"MomentumDensity\");\n  TestHelpers::db::test_simple_tag<gr::Tags::StressTrace<Type>>(\"StressTrace\");\n  TestHelpers::db::test_simple_tag<gr::Tags::HamiltonianConstraint<Type>>(\n      \"HamiltonianConstraint\");\n  TestHelpers::db::test_simple_tag<\n      gr::Tags::MomentumConstraint<Type, Dim, Frame>>(\"MomentumConstraint\");\n  TestHelpers::db::test_simple_tag<gr::Tags::WeylElectric<Type, Dim, Frame>>(\n      \"WeylElectric\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.Tags\",\n                  \"[Unit][PointwiseFunctions]\") {\n  test_simple_tags<1, ArbitraryFrame, ArbitraryType>();\n  test_simple_tags<2, ArbitraryFrame, ArbitraryType>();\n  test_simple_tags<3, ArbitraryFrame, ArbitraryType>();\n\n  TestHelpers::db::test_prefix_tag<gr::Tags::Conformal<Tag, -3>>(\n      \"Conformal(Tag)\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_TortoiseCoordinates.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TortoiseCoordinates.hpp\"\n\nnamespace gr {\n\nSPECTRE_TEST_CASE(\"Unit.GeneralRelativity.TortoiseCoordinates\",\n                  \"[Unit][PointwiseFunctions]\") {\n  MAKE_GENERATOR(generator);\n  {\n    INFO(\"Random values\");\n    const pypp::SetupLocalPythonEnvironment local_python_env(\n        \"PointwiseFunctions/GeneralRelativity\");\n    pypp::check_with_random_values<3>(\n        tortoise_radius_from_boyer_lindquist_minus_r_plus<DataVector>,\n        \"TortoiseCoordinates\",\n        \"tortoise_radius_from_boyer_lindquist_minus_r_plus\",\n        {{{0.3, 10.0}, {0.3, 1.0}, {0.3, 1.0}}}, DataVector(5));\n  }\n  {\n    INFO(\"Inverse\");\n    const size_t num_samples = 100;\n    std::uniform_real_distribution<> dist_mass(0.1, 2.0);\n    std::uniform_real_distribution<> dist_spin(0.0, 1.0);\n    std::uniform_real_distribution<> dist_tortoise(-50.0, 100.0);\n    const double mass = dist_mass(generator);\n    const double dimensionless_spin = dist_spin(generator);\n    const DataVector r_star = make_with_random_values<DataVector>(\n                                  make_not_null(&generator),\n                                  make_not_null(&dist_tortoise), num_samples) *\n                              mass;\n    CAPTURE(mass);\n    CAPTURE(dimensionless_spin);\n    CAPTURE(r_star);\n    const auto r_minus_r_plus =\n        boyer_lindquist_radius_minus_r_plus_from_tortoise(r_star, mass,\n                                                          dimensionless_spin);\n    CHECK_ITERABLE_APPROX(tortoise_radius_from_boyer_lindquist_minus_r_plus(\n                              r_minus_r_plus, mass, dimensionless_spin),\n                          r_star);\n  }\n  {\n    INFO(\"Specific values\");\n    CHECK(boyer_lindquist_radius_minus_r_plus_from_tortoise(0.0, 1.0, 0.0) ==\n          approx(0.55692908552214748));\n  }\n}\n\n}  // namespace gr\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_WeylElectric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n\nnamespace {\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_compute_item_in_databox(const DataType& used_for_size) {\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::WeylElectricCompute<DataType, SpatialDim, Frame::Inertial>>(\n      \"WeylElectric\");\n  TestHelpers::db::test_compute_tag<gr::Tags::WeylElectricScalarCompute<\n      DataType, SpatialDim, Frame::Inertial>>(\"WeylElectricScalar\");\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-3.0, 3.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  const auto spatial_ricci =\n      make_with_random_values<tnsr::ii<DataType, SpatialDim>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto extrinsic_curvature =\n      make_with_random_values<tnsr::ii<DataType, SpatialDim>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto inv_spatial_metric =\n      make_with_random_values<tnsr::II<DataType, SpatialDim>>(\n          nn_generator, nn_distribution, used_for_size);\n\n  const auto box = db::create<\n      db::AddSimpleTags<gr::Tags::SpatialRicci<DataType, SpatialDim>,\n                        gr::Tags::ExtrinsicCurvature<DataType, SpatialDim>,\n                        gr::Tags::InverseSpatialMetric<DataType, SpatialDim>>,\n      db::AddComputeTags<\n          gr::Tags::WeylElectricCompute<DataType, SpatialDim, Frame::Inertial>,\n          gr::Tags::WeylElectricScalarCompute<DataType, SpatialDim,\n                                              Frame::Inertial>>>(\n      spatial_ricci, extrinsic_curvature, inv_spatial_metric);\n\n  const auto expected =\n      gr::weyl_electric(spatial_ricci, extrinsic_curvature, inv_spatial_metric);\n  const auto expected_scalar =\n      gr::weyl_electric_scalar(expected, inv_spatial_metric);\n  CHECK_ITERABLE_APPROX(\n      (db::get<gr::Tags::WeylElectric<DataType, SpatialDim>>(box)), expected);\n  CHECK_ITERABLE_APPROX((db::get<gr::Tags::WeylElectricScalar<DataType>>(box)),\n                        expected_scalar);\n}\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_weyl_electric(const DataType& used_for_size) {\n  tnsr::ii<DataType, SpatialDim, Frame::Inertial> (*f)(\n      const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n      const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n      const tnsr::II<DataType, SpatialDim, Frame::Inertial>&) =\n      &gr::weyl_electric<DataType, SpatialDim, Frame::Inertial>;\n  pypp::check_with_random_values<1>(f, \"WeylElectric\", \"weyl_electric_tensor\",\n                                    {{{-1., 1.}}}, used_for_size);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_weyl_electric_scalar(const DataType& used_for_size) {\n  Scalar<DataType> (*f)(\n      const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n      const tnsr::II<DataType, SpatialDim, Frame::Inertial>&) =\n      &gr::weyl_electric_scalar<DataType, SpatialDim, Frame::Inertial>;\n  pypp::check_with_random_values<1>(f, \"WeylElectricScalar\",\n                                    \"weyl_electric_scalar\", {{{-1., 1.}}},\n                                    used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.WeylElectric\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_weyl_electric, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_weyl_electric_scalar, (1, 2, 3));\n  test_compute_item_in_databox<3>(d);\n  test_compute_item_in_databox<3>(dv);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_WeylMagnetic.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/LeviCivitaIterator.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/VectorImpl.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylMagnetic.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n#include \"Utilities/GenerateInstantiations.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nvoid make_random_tensors(\n    const gsl::not_null<tnsr::ijj<DataType, 3>*> grad_extrinsic_curvature,\n    const gsl::not_null<tnsr::ii<DataType, 3>*> spatial_metric,\n    const gsl::not_null<Scalar<DataType>*> sqrt_det_spatial_metric,\n    const gsl::not_null<tnsr::II<DataType, 3>*> inverse_spatial_metric,\n    const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-3.0, 3.0);\n  const auto nn_distribution = make_not_null(&distribution);\n  *grad_extrinsic_curvature = make_with_random_values<tnsr::ijj<DataType, 3>>(\n      make_not_null(&generator), nn_distribution, used_for_size);\n\n  std::uniform_real_distribution<> metric_distribution(-0.03, 0.03);\n  const auto nn_metric_distribution = make_not_null(&metric_distribution);\n  *spatial_metric = make_with_random_values<tnsr::ii<DataType, 3>>(\n      make_not_null(&generator), nn_metric_distribution, used_for_size);\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_metric->get(i, i) += 1.0;\n  }\n\n  std::uniform_real_distribution<> positive_distribution(0.1, 1.0);\n  *sqrt_det_spatial_metric = make_with_random_values<Scalar<DataType>>(\n      make_not_null(&generator), positive_distribution, used_for_size);\n\n  *inverse_spatial_metric = make_with_random_values<tnsr::II<DataType, 3>>(\n      make_not_null(&generator), nn_metric_distribution, used_for_size);\n}\n\ntemplate <typename DataType>\nvoid test_compute_item_in_databox(const DataType& used_for_size) {\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::WeylMagneticCompute<DataType, 3, Frame::Inertial>>(\n      \"WeylMagnetic\");\n\n  auto grad_extrinsic_curvature = make_with_value<tnsr::ijj<DataType, 3>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n  auto spatial_metric = make_with_value<tnsr::ii<DataType, 3>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n  auto sqrt_det_spatial_metric = make_with_value<Scalar<DataType>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n  auto inverse_spatial_metric = make_with_value<tnsr::II<DataType, 3>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n  make_random_tensors(make_not_null(&grad_extrinsic_curvature),\n                      make_not_null(&spatial_metric),\n                      make_not_null(&sqrt_det_spatial_metric),\n                      make_not_null(&inverse_spatial_metric), used_for_size);\n\n  const auto box = db::create<\n      db::AddSimpleTags<gr::Tags::CovariantDerivativeOfExtrinsicCurvature<\n                            DataType, 3, Frame::Inertial>,\n                        gr::Tags::SpatialMetric<DataType, 3>,\n                        gr::Tags::SqrtDetSpatialMetric<DataType>,\n                        gr::Tags::InverseSpatialMetric<DataType, 3>>,\n      db::AddComputeTags<\n          gr::Tags::WeylMagneticCompute<DataType, 3, Frame::Inertial>,\n          gr::Tags::WeylMagneticScalarCompute<DataType, 3, Frame::Inertial>>>(\n      grad_extrinsic_curvature, spatial_metric, sqrt_det_spatial_metric,\n      inverse_spatial_metric);\n\n  const auto expected_weyl_magnetic = gr::weyl_magnetic(\n      grad_extrinsic_curvature, spatial_metric, sqrt_det_spatial_metric);\n  CHECK_ITERABLE_APPROX(\n      (db::get<gr::Tags::WeylMagnetic<DataType, 3, Frame::Inertial>>(box)),\n      expected_weyl_magnetic);\n\n  const auto expected_weyl_scalar =\n      gr::weyl_magnetic_scalar(expected_weyl_magnetic, inverse_spatial_metric);\n  CHECK_ITERABLE_APPROX((db::get<gr::Tags::WeylMagneticScalar<DataType>>(box)),\n                        expected_weyl_scalar);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_weyl_magnetic(const DataType& used_for_size) {\n  // Initialize input Tensor objects\n  auto grad_extrinsic_curvature =\n      make_with_value<tnsr::ijj<DataType, SpatialDim>>(\n          used_for_size, std::numeric_limits<double>::signaling_NaN());\n  auto spatial_metric = make_with_value<tnsr::ii<DataType, SpatialDim>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n  auto sqrt_det_spatial_metric = make_with_value<Scalar<DataType>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n  auto inverse_spatial_metric = make_with_value<tnsr::II<DataType, 3>>(\n      used_for_size, std::numeric_limits<double>::signaling_NaN());\n\n  // Populating inputs with random values\n  make_random_tensors(make_not_null(&grad_extrinsic_curvature),\n                      make_not_null(&spatial_metric),\n                      make_not_null(&sqrt_det_spatial_metric),\n                      make_not_null(&inverse_spatial_metric), used_for_size);\n\n  const auto cpp_weyl_magnetic = gr::weyl_magnetic(\n      grad_extrinsic_curvature, spatial_metric, sqrt_det_spatial_metric);\n  const auto scalar_weyl_magnetic =\n      gr::weyl_magnetic_scalar(cpp_weyl_magnetic, inverse_spatial_metric);\n  const auto python_weyl_magnetic =\n      pypp::call<tnsr::ii<DataType, SpatialDim, Frame::Inertial>>(\n          \"WeylMagnetic\", \"weyl_magnetic_tensor\", grad_extrinsic_curvature,\n          spatial_metric, sqrt_det_spatial_metric);\n  const auto python_weyl_scalar =\n      pypp::call<Scalar<DataType>>(\"WeylMagneticScalar\", \"weyl_magnetic_scalar\",\n                                   cpp_weyl_magnetic, inverse_spatial_metric);\n  CHECK_ITERABLE_APPROX(cpp_weyl_magnetic, python_weyl_magnetic);\n  CHECK_ITERABLE_APPROX(scalar_weyl_magnetic, python_weyl_scalar);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.WeylMagnetic\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_weyl_magnetic, (3));\n  test_compute_item_in_databox(d);\n  test_compute_item_in_databox(dv);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_WeylPropagating.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylPropagating.hpp\"\n\nnamespace {\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> weyl_propagating_plus_wrapper(\n    const tnsr::ii<DataType, SpatialDim, Frame>& ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::I<DataType, SpatialDim, Frame>& unit_interface_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& projection_IJ,\n    const tnsr::ii<DataType, SpatialDim, Frame>& projection_ij,\n    const tnsr::Ij<DataType, SpatialDim, Frame>& projection_Ij) {\n  return gr::weyl_propagating<DataType, SpatialDim, Frame>(\n      ricci, extrinsic_curvature, inverse_spatial_metric,\n      cov_deriv_extrinsic_curvature, unit_interface_normal_vector,\n      projection_IJ, projection_ij, projection_Ij, 1.);\n}\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\ntnsr::ii<DataType, SpatialDim, Frame> weyl_propagating_minus_wrapper(\n    const tnsr::ii<DataType, SpatialDim, Frame>& ricci,\n    const tnsr::ii<DataType, SpatialDim, Frame>& extrinsic_curvature,\n    const tnsr::II<DataType, SpatialDim, Frame>& inverse_spatial_metric,\n    const tnsr::ijj<DataType, SpatialDim, Frame>& cov_deriv_extrinsic_curvature,\n    const tnsr::I<DataType, SpatialDim, Frame>& unit_interface_normal_vector,\n    const tnsr::II<DataType, SpatialDim, Frame>& projection_IJ,\n    const tnsr::ii<DataType, SpatialDim, Frame>& projection_ij,\n    const tnsr::Ij<DataType, SpatialDim, Frame>& projection_Ij) {\n  return gr::weyl_propagating<DataType, SpatialDim, Frame>(\n      ricci, extrinsic_curvature, inverse_spatial_metric,\n      cov_deriv_extrinsic_curvature, unit_interface_normal_vector,\n      projection_IJ, projection_ij, projection_Ij, -1.);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_weyl_propagating(const DataType& used_for_size) {\n  {\n    tnsr::ii<DataType, SpatialDim, Frame::Inertial> (*f)(\n        const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::II<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::ijj<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::I<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::II<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::Ij<DataType, SpatialDim, Frame::Inertial>&) =\n        &weyl_propagating_plus_wrapper<DataType, SpatialDim, Frame::Inertial>;\n    pypp::check_with_random_values<1>(f, \"GeneralRelativity.WeylPropagating\",\n                                      \"weyl_propagating_mode_plus\",\n                                      {{{-1., 1.}}}, used_for_size);\n  }\n  {\n    tnsr::ii<DataType, SpatialDim, Frame::Inertial> (*f)(\n        const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::II<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::ijj<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::I<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::II<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n        const tnsr::Ij<DataType, SpatialDim, Frame::Inertial>&) =\n        &weyl_propagating_minus_wrapper<DataType, SpatialDim, Frame::Inertial>;\n    pypp::check_with_random_values<1>(f, \"GeneralRelativity.WeylPropagating\",\n                                      \"weyl_propagating_mode_minus\",\n                                      {{{-1., 1.}}}, used_for_size);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.WeylPropagating\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\"PointwiseFunctions/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_weyl_propagating, (1, 2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/Test_WeylTypeD1.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/TagsDeclarations.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylElectric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/WeylTypeD1.hpp\"\n\nnamespace {\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_compute_item_in_databox(const DataType& used_for_size) {\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::WeylTypeD1Compute<DataType, SpatialDim, Frame::Inertial>>(\n      \"WeylTypeD1\");\n  TestHelpers::db::test_compute_tag<\n      gr::Tags::WeylTypeD1ScalarCompute<DataType, SpatialDim, Frame::Inertial>>(\n      \"WeylTypeD1Scalar\");\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-3.0, 3.0);\n  const auto nn_generator = make_not_null(&generator);\n  const auto nn_distribution = make_not_null(&distribution);\n\n  const auto weyl_electric =\n      make_with_random_values<tnsr::ii<DataType, SpatialDim>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto spatial_metric =\n      make_with_random_values<tnsr::ii<DataType, SpatialDim>>(\n          nn_generator, nn_distribution, used_for_size);\n  const auto inverse_spatial_metric =\n      make_with_random_values<tnsr::II<DataType, SpatialDim>>(\n          nn_generator, nn_distribution, used_for_size);\n\n  const auto box = db::create<\n      db::AddSimpleTags<\n          gr::Tags::WeylElectric<DataType, SpatialDim, Frame::Inertial>,\n          gr::Tags::SpatialMetric<DataType, SpatialDim, Frame::Inertial>,\n          gr::Tags::InverseSpatialMetric<DataType, SpatialDim,\n                                         Frame::Inertial>>,\n      db::AddComputeTags<\n          gr::Tags::WeylTypeD1Compute<DataType, SpatialDim, Frame::Inertial>,\n          gr::Tags::WeylTypeD1ScalarCompute<DataType, SpatialDim,\n                                            Frame::Inertial>>>(\n      weyl_electric, spatial_metric, inverse_spatial_metric);\n\n  const auto expected =\n      gr::weyl_type_D1(weyl_electric, spatial_metric, inverse_spatial_metric);\n  const auto expected_scalar =\n      gr::weyl_type_D1_scalar(expected, inverse_spatial_metric);\n  CHECK_ITERABLE_APPROX(\n      (db::get<gr::Tags::WeylTypeD1<DataType, SpatialDim, Frame::Inertial>>(\n          box)),\n      expected);\n  CHECK_ITERABLE_APPROX((db::get<gr::Tags::WeylTypeD1Scalar<DataType>>(box)),\n                        expected_scalar);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_weyl_type_D1(const DataType& used_for_size) {\n  tnsr::ii<DataType, SpatialDim, Frame::Inertial> (*f)(\n      const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n      const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n      const tnsr::II<DataType, SpatialDim, Frame::Inertial>&) =\n      &gr::weyl_type_D1<DataType, SpatialDim, Frame::Inertial>;\n  pypp::check_with_random_values<1>(f, \"WeylTypeD1\", \"weyl_type_D1\",\n                                    {{{-1., 1.}}}, used_for_size);\n}\n\ntemplate <size_t SpatialDim, typename DataType>\nvoid test_weyl_type_D1_scalar(const DataType& used_for_size) {\n  Scalar<DataType> (*f)(\n      const tnsr::ii<DataType, SpatialDim, Frame::Inertial>&,\n      const tnsr::II<DataType, SpatialDim, Frame::Inertial>&) =\n      &gr::weyl_type_D1_scalar<DataType, SpatialDim, Frame::Inertial>;\n  pypp::check_with_random_values<1>(f, \"WeylTypeD1Scalar\",\n                                    \"weyl_type_D1_scalar\", {{{-1., 1.}}},\n                                    used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.GeneralRelativity.WeylTypeD1\",\n                  \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/GeneralRelativity/\");\n\n  GENERATE_UNINITIALIZED_DOUBLE_AND_DATAVECTOR;\n\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_weyl_type_D1, (1, 2, 3));\n  CHECK_FOR_DOUBLES_AND_DATAVECTORS(test_weyl_type_D1_scalar, (1, 2, 3));\n  test_compute_item_in_databox<3>(d);\n  test_compute_item_in_databox<3>(dv);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/TortoiseCoordinates.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef tortoise_radius_from_boyer_lindquist_minus_r_plus(\n    r_minus_r_plus, mass, dimensionless_spin\n):\n    r_plus = mass * (1.0 + np.sqrt(1.0 - dimensionless_spin**2))\n    r_minus = mass * (1.0 - np.sqrt(1.0 - dimensionless_spin**2))\n    r = r_minus_r_plus + r_plus\n    return r + 2 * mass / (r_plus - r_minus) * (\n        r_plus * np.log(r_minus_r_plus / (2 * mass))\n        - r_minus * np.log((r - r_minus) / (2 * mass))\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/WeylElectric.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef weyl_electric_tensor(\n    spatial_ricci, extrinsic_curvature, inverse_spatial_metric\n):\n    return (\n        np.einsum(\"ij\", spatial_ricci)\n        + np.einsum(\n            \"kl,kl,ij\",\n            extrinsic_curvature,\n            inverse_spatial_metric,\n            extrinsic_curvature,\n        )\n        - np.einsum(\n            \"il,kl,kj\",\n            extrinsic_curvature,\n            inverse_spatial_metric,\n            extrinsic_curvature,\n        )\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/WeylElectricScalar.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\nimport numpy as np\n\n\ndef weyl_electric_scalar(weyl_electric, inverse_spatial_metric):\n    return np.einsum(\n        \"ik,jl,ij,kl\",\n        weyl_electric,\n        weyl_electric,\n        inverse_spatial_metric,\n        inverse_spatial_metric,\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/WeylMagnetic.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef weyl_magnetic_tensor(\n    grad_extrinsic_curvature, spatial_metric, sqrt_det_spatial_metric\n):\n    levi_civita = np.zeros((3, 3, 3))\n    levi_civita[0, 1, 2] = levi_civita[1, 2, 0] = levi_civita[2, 0, 1] = 1.0\n    levi_civita[0, 2, 1] = levi_civita[2, 1, 0] = levi_civita[1, 0, 2] = -1.0\n\n    det_spatial_metric = np.linalg.det(spatial_metric)\n    result = (0.5 / sqrt_det_spatial_metric) * (\n        np.einsum(\n            \"kli,mlk,jm\", grad_extrinsic_curvature, levi_civita, spatial_metric\n        )\n        + np.einsum(\n            \"klj,mlk,im\", grad_extrinsic_curvature, levi_civita, spatial_metric\n        )\n    )\n    return result\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/WeylMagneticScalar.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef weyl_magnetic_scalar(weyl_magnetic, inverse_spatial_metric):\n    return np.einsum(\n        \"ij,kl,jk,il\",\n        weyl_magnetic,\n        weyl_magnetic,\n        inverse_spatial_metric,\n        inverse_spatial_metric,\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/WeylPropagating.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\nfrom .WeylElectric import weyl_electric_tensor\n\n\ndef weyl_propagating_modes(\n    ricci,\n    extrinsic_curvature,\n    inverse_spatial_metric,\n    cov_deriv_ex_curv,\n    unit_normal_vector,\n    projection_IJ,\n    projection_ij,\n    projection_Ij,\n    sign,\n):\n    tmp = weyl_electric_tensor(\n        ricci, extrinsic_curvature, inverse_spatial_metric\n    )\n    tmp = tmp - sign * np.einsum(\n        \"k,kij->ij\", unit_normal_vector, cov_deriv_ex_curv\n    )\n    tmp = tmp + sign * 0.5 * np.einsum(\n        \"k,jik->ij\", unit_normal_vector, cov_deriv_ex_curv\n    )\n    tmp = tmp + sign * 0.5 * np.einsum(\n        \"k,ijk->ij\", unit_normal_vector, cov_deriv_ex_curv\n    )\n\n    return np.einsum(\n        \"ki,lj,kl->ij\", projection_Ij, projection_Ij, tmp\n    ) - 0.5 * np.einsum(\"kl,ij,kl->ij\", projection_IJ, projection_ij, tmp)\n\n\ndef weyl_propagating_mode_plus(\n    ricci,\n    extrinsic_curvature,\n    inverse_spatial_metric,\n    cov_deriv_ex_curv,\n    unit_normal_vector,\n    projection_IJ,\n    projection_ij,\n    projection_Ij,\n):\n    return weyl_propagating_modes(\n        ricci,\n        extrinsic_curvature,\n        inverse_spatial_metric,\n        cov_deriv_ex_curv,\n        unit_normal_vector,\n        projection_IJ,\n        projection_ij,\n        projection_Ij,\n        1,\n    )\n\n\ndef weyl_propagating_mode_minus(\n    ricci,\n    extrinsic_curvature,\n    inverse_spatial_metric,\n    cov_deriv_ex_curv,\n    unit_normal_vector,\n    projection_IJ,\n    projection_ij,\n    projection_Ij,\n):\n    return weyl_propagating_modes(\n        ricci,\n        extrinsic_curvature,\n        inverse_spatial_metric,\n        cov_deriv_ex_curv,\n        unit_normal_vector,\n        projection_IJ,\n        projection_ij,\n        projection_Ij,\n        -1,\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/WeylTypeD1.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef weyl_type_D1(weyl_electric, spatial_metric, inverse_spatial_metric):\n    inverse_weyl_electric = np.einsum(\n        \"lk,ik,lj\",\n        weyl_electric,\n        inverse_spatial_metric,\n        inverse_spatial_metric,\n    )\n    a = 16 * (np.einsum(\"ij,ij\", weyl_electric, inverse_weyl_electric))\n    b = -64 * (\n        np.einsum(\n            \"il,lk,ij,jk\",\n            weyl_electric,\n            inverse_spatial_metric,\n            inverse_weyl_electric,\n            weyl_electric,\n        )\n    )\n    return (\n        (a / 12) * (np.einsum(\"ij\", spatial_metric))\n        - (np.einsum(\"ij\", weyl_electric) * (b / a))\n        - 4\n        * np.einsum(\n            \"im,km,jk\", weyl_electric, inverse_spatial_metric, weyl_electric\n        )\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/WeylTypeD1Scalar.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef weyl_type_D1_scalar(weyl_type_D1_tensor, inverse_spatial_metric):\n    return np.einsum(\n        \"ij,kl,lj,ki\",\n        weyl_type_D1_tensor,\n        weyl_type_D1_tensor,\n        inverse_spatial_metric,\n        inverse_spatial_metric,\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/GeneralRelativity/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(EquationsOfState)\nadd_subdirectory(InitialData)\nadd_subdirectory(Python)\n\nset(LIBRARY \"Test_Hydro\")\n\nset(LIBRARY_SOURCES\n  Test_ComovingMagneticField.cpp\n  Test_InversePlasmaBeta.cpp\n  Test_LorentzFactor.cpp\n  Test_MagneticFieldTreatment.cpp\n  Test_MassFlux.cpp\n  Test_MassWeightedFluidItems.cpp\n  Test_QuadrupoleFormula.cpp\n  Test_SoundSpeedSquared.cpp\n  Test_SpecificEnthalpy.cpp\n  Test_StressEnergy.cpp\n  Test_Tags.cpp\n  Test_Temperature.cpp\n  Test_TestHelpers.cpp\n  Test_TransportVelocity.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  DataStructuresHelpers\n  Domain\n  GeneralRelativity\n  GeneralRelativityHelpers\n  GhValenciaDivClean\n  Hydro\n  HydroHelpers\n  LinearOperators\n  Spectral\n  IO\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/ComovingMagneticField.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef comoving_magnetic_field(\n    spatial_velocity,\n    magnetic_field,\n    magnetic_field_dot_spatial_velocity,\n    lorentz_factor,\n    shift,\n    lapse,\n):\n    b_0 = lorentz_factor * magnetic_field_dot_spatial_velocity / lapse\n    b_i = magnetic_field / lorentz_factor + (\n        lapse * b_0 * (spatial_velocity - shift / lapse)\n    )\n\n    return np.concatenate([[b_0], b_i])\n\n\ndef comoving_magnetic_field_one_form(\n    spatial_velocity_one_form,\n    magnetic_field_one_form,\n    magnetic_field_dot_spatial_velocity,\n    lorentz_factor,\n    shift,\n    lapse,\n):\n    b_i = (\n        magnetic_field_one_form / lorentz_factor\n        + magnetic_field_dot_spatial_velocity\n        * lorentz_factor\n        * spatial_velocity_one_form\n    )\n    b_0 = (\n        -lapse * lorentz_factor * magnetic_field_dot_spatial_velocity\n        + np.einsum(\"i...,i...\", shift, b_i)\n    )\n\n    return np.concatenate([[b_0], b_i])\n\n\ndef comoving_magnetic_field_squared(\n    magnetic_field_squared, magnetic_field_dot_spatial_velocity, lorentz_factor\n):\n    return (\n        magnetic_field_squared / lorentz_factor**2\n        + magnetic_field_dot_spatial_velocity**2\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_EquationsOfState\")\n\nset(LIBRARY_SOURCES\n  Test_Barotropic2D.cpp\n  Test_Barotropic3D.cpp\n  Test_DarkEnergyFluid.cpp\n  Test_Enthalpy.cpp\n  Test_Equilibrium3D.cpp\n  Test_HybridEos.cpp\n  Test_IdealFluid.cpp\n  Test_PiecewisePolytropicFluid.cpp\n  Test_PolytropicFluid.cpp\n  Test_SpectralEoS.cpp\n  Test_Tabulated3D.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Hydro\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/DarkEnergyFluid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef dark_energy_fluid_pressure_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, parameter_w\n):\n    return parameter_w * rest_mass_density * (1.0 + specific_internal_energy)\n\n\ndef dark_energy_fluid_rel_pressure_from_density_and_enthalpy(\n    rest_mass_density, specific_enthalpy, parameter_w\n):\n    return (\n        parameter_w\n        * rest_mass_density\n        * specific_enthalpy\n        / (parameter_w + 1.0)\n    )\n\n\ndef dark_energy_fluid_rel_specific_enthalpy_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, parameter_w\n):\n    return (parameter_w + 1.0) * (1.0 + specific_internal_energy)\n\n\ndef dark_energy_fluid_temperature_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, parameter_w\n):\n    return parameter_w * specific_internal_energy\n\n\ndef dark_energy_fluid_specific_internal_energy_from_density_and_pressure(\n    rest_mass_density, pressure, parameter_w\n):\n    return pressure / (parameter_w * rest_mass_density) - 1.0\n\n\ndef dark_energy_fluid_chi_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, parameter_w\n):\n    return parameter_w * (1.0 + specific_internal_energy)\n\n\ndef dark_energy_fluid_kappa_times_p_over_rho_squared_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, parameter_w\n):\n    return parameter_w**2 * (1.0 + specific_internal_energy)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/HybridEos.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom PolytropicFluid import (\n    polytropic_pressure_from_density,\n    polytropic_specific_internal_energy_from_density,\n)\n\n\ndef hybrid_polytrope_pressure_from_density_and_energy(\n    rest_mass_density,\n    specific_internal_energy,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    p_c = polytropic_pressure_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return p_c + rest_mass_density * max(\n        (specific_internal_energy - eps_c), 0.0\n    ) * (thermal_adiabatic_index - 1.0)\n\n\ndef hybrid_polytrope_rel_pressure_from_density_and_enthalpy(\n    rest_mass_density,\n    specific_enthalpy,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    p_c = polytropic_pressure_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return p_c / thermal_adiabatic_index + (\n        rest_mass_density\n        * max((specific_enthalpy - 1.0 - eps_c), 0.0)\n        * (thermal_adiabatic_index - 1.0)\n        / thermal_adiabatic_index\n    )\n\n\ndef hybrid_polytrope_temperature_from_density_and_energy(\n    rest_mass_density,\n    specific_internal_energy,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return (thermal_adiabatic_index - 1.0) * max(\n        (specific_internal_energy - eps_c), 0.0\n    )\n\n\ndef hybrid_polytrope_newt_pressure_from_density_and_enthalpy(\n    rest_mass_density,\n    specific_enthalpy,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    p_c = polytropic_pressure_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return p_c / thermal_adiabatic_index + (\n        rest_mass_density\n        * max((specific_enthalpy - eps_c), 0.0)\n        * (thermal_adiabatic_index - 1.0)\n        / thermal_adiabatic_index\n    )\n\n\ndef hybrid_polytrope_rel_specific_enthalpy_from_density_and_energy(\n    rest_mass_density,\n    specific_internal_energy,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    p_c = polytropic_pressure_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return (\n        1.0\n        + eps_c\n        + p_c / rest_mass_density\n        + thermal_adiabatic_index * max((specific_internal_energy - eps_c), 0.0)\n    )\n\n\ndef hybrid_polytrope_newt_specific_enthalpy_from_density_and_energy(\n    rest_mass_density,\n    specific_internal_energy,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    p_c = polytropic_pressure_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return (\n        eps_c\n        + p_c / rest_mass_density\n        + thermal_adiabatic_index * max((specific_internal_energy - eps_c), 0.0)\n    )\n\n\ndef hybrid_polytrope_specific_internal_energy_from_density_and_pressure(\n    rest_mass_density,\n    pressure,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    p_c = polytropic_pressure_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return (\n        eps_c\n        + max((pressure - p_c), 0.0)\n        / (thermal_adiabatic_index - 1.0)\n        / rest_mass_density\n    )\n\n\ndef hybrid_polytrope_chi_from_density_and_energy(\n    rest_mass_density,\n    specific_internal_energy,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    p_c = polytropic_pressure_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return polytropic_exponent * p_c / rest_mass_density + (\n        specific_internal_energy - eps_c - p_c / rest_mass_density\n    ) * (thermal_adiabatic_index - 1.0)\n\n\ndef hybrid_polytrope_kappa_times_p_over_rho_squared_from_density_and_energy(\n    rest_mass_density,\n    specific_internal_energy,\n    polytropic_constant,\n    polytropic_exponent,\n    thermal_adiabatic_index,\n):\n    p_c = polytropic_pressure_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    eps_c = polytropic_specific_internal_energy_from_density(\n        rest_mass_density, polytropic_constant, polytropic_exponent\n    )\n    return (thermal_adiabatic_index - 1.0) * p_c / rest_mass_density + max(\n        (specific_internal_energy - eps_c), 0.0\n    ) * (thermal_adiabatic_index - 1.0) ** 2\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef ideal_fluid_pressure_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, adiabatic_index\n):\n    return (\n        rest_mass_density * specific_internal_energy * (adiabatic_index - 1.0)\n    )\n\n\ndef ideal_fluid_temperature_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, adiabatic_index\n):\n    return (adiabatic_index - 1.0) * specific_internal_energy\n\n\ndef ideal_fluid_rel_pressure_from_density_and_enthalpy(\n    rest_mass_density, specific_enthalpy, adiabatic_index\n):\n    return (\n        rest_mass_density\n        * (specific_enthalpy - 1.0)\n        * (adiabatic_index - 1.0)\n        / adiabatic_index\n    )\n\n\ndef ideal_fluid_newt_pressure_from_density_and_enthalpy(\n    rest_mass_density, specific_enthalpy, adiabatic_index\n):\n    return (\n        rest_mass_density\n        * specific_enthalpy\n        * (adiabatic_index - 1.0)\n        / adiabatic_index\n    )\n\n\ndef ideal_fluid_rel_specific_enthalpy_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, adiabatic_index\n):\n    return 1.0 + adiabatic_index * specific_internal_energy\n\n\ndef ideal_fluid_newt_specific_enthalpy_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, adiabatic_index\n):\n    return adiabatic_index * specific_internal_energy\n\n\ndef ideal_fluid_specific_internal_energy_from_density_and_pressure(\n    rest_mass_density, pressure, adiabatic_index\n):\n    return pressure / (adiabatic_index - 1.0) / rest_mass_density\n\n\ndef ideal_fluid_chi_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, adiabatic_index\n):\n    return specific_internal_energy * (adiabatic_index - 1.0)\n\n\ndef ideal_fluid_kappa_times_p_over_rho_squared_from_density_and_energy(\n    rest_mass_density, specific_internal_energy, adiabatic_index\n):\n    return specific_internal_energy * (adiabatic_index - 1.0) ** 2\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef calc_eint_constant(\n    polytropic_exponent,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    eint_constant = (\n        (polytropic_exponent - poly_exponent_lo)\n        / ((poly_exponent_hi - 1.0) * (poly_exponent_lo - 1.0))\n        * poly_constant_lo\n        * transition_density ** (poly_exponent_lo - 1.0)\n    )\n    return eint_constant\n\n\ndef piecewisepolytropic_pressure_from_density(\n    rest_mass_density,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    poly_constant_hi = poly_constant_lo * transition_density ** (\n        poly_exponent_lo - poly_exponent_hi\n    )\n    if rest_mass_density < transition_density:\n        polytropic_constant = poly_constant_lo\n        polytropic_exponent = poly_exponent_lo\n    else:\n        polytropic_constant = poly_constant_hi\n        polytropic_exponent = poly_exponent_hi\n    return polytropic_constant * rest_mass_density**polytropic_exponent\n\n\ndef piecewisepolytropic_rel_rest_mass_density_from_enthalpy(\n    specific_enthalpy,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    poly_constant_hi = poly_constant_lo * transition_density ** (\n        poly_exponent_lo - poly_exponent_hi\n    )\n    transition_pressure = poly_constant_lo * transition_density ** (\n        poly_exponent_lo\n    )\n    transition_spec_eint = transition_pressure / (\n        (poly_exponent_lo - 1.0) * transition_density\n    )\n    transition_spec_enthalpy = (\n        1.0 + transition_spec_eint + transition_pressure / transition_density\n    )\n\n    if specific_enthalpy < transition_spec_enthalpy:\n        polytropic_constant = poly_constant_lo\n        polytropic_exponent = poly_exponent_lo\n    else:\n        polytropic_constant = poly_constant_hi\n        polytropic_exponent = poly_exponent_hi\n\n    eint_constant = calc_eint_constant(\n        polytropic_exponent,\n        transition_density,\n        poly_constant_lo,\n        poly_exponent_lo,\n        poly_exponent_hi,\n    )\n\n    return (\n        (polytropic_exponent - 1.0)\n        / (polytropic_constant * polytropic_exponent)\n        * (specific_enthalpy - 1.0 - eint_constant)\n    ) ** (1.0 / (polytropic_exponent - 1.0))\n\n\ndef piecewisepolytropic_newt_rest_mass_density_from_enthalpy(\n    specific_enthalpy,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    poly_constant_hi = poly_constant_lo * transition_density ** (\n        poly_exponent_lo - poly_exponent_hi\n    )\n    transition_pressure = poly_constant_lo * transition_density ** (\n        poly_exponent_lo\n    )\n    transition_spec_eint = transition_pressure / (\n        (poly_exponent_lo - 1.0) * transition_density\n    )\n    transition_spec_enthalpy = (\n        transition_spec_eint + transition_pressure / transition_density\n    )\n\n    if specific_enthalpy < transition_spec_enthalpy:\n        polytropic_constant = poly_constant_lo\n        polytropic_exponent = poly_exponent_lo\n    else:\n        polytropic_constant = poly_constant_hi\n        polytropic_exponent = poly_exponent_hi\n\n    eint_constant = calc_eint_constant(\n        polytropic_exponent,\n        transition_density,\n        poly_constant_lo,\n        poly_exponent_lo,\n        poly_exponent_hi,\n    )\n\n    return (\n        (polytropic_exponent - 1.0)\n        / (polytropic_constant * polytropic_exponent)\n        * (specific_enthalpy - eint_constant)\n    ) ** (1.0 / (polytropic_exponent - 1.0))\n\n\ndef piecewisepolytropic_rel_specific_enthalpy_from_density(\n    rest_mass_density,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    poly_constant_hi = poly_constant_lo * transition_density ** (\n        poly_exponent_lo - poly_exponent_hi\n    )\n    if rest_mass_density < transition_density:\n        polytropic_constant = poly_constant_lo\n        polytropic_exponent = poly_exponent_lo\n    else:\n        polytropic_constant = poly_constant_hi\n        polytropic_exponent = poly_exponent_hi\n\n    eint_constant = calc_eint_constant(\n        polytropic_exponent,\n        transition_density,\n        poly_constant_lo,\n        poly_exponent_lo,\n        poly_exponent_hi,\n    )\n\n    return (\n        1.0\n        + polytropic_constant\n        * polytropic_exponent\n        / (polytropic_exponent - 1.0)\n        * rest_mass_density ** (polytropic_exponent - 1.0)\n        + eint_constant\n    )\n\n\ndef piecewisepolytropic_newt_specific_enthalpy_from_density(\n    rest_mass_density,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    poly_constant_hi = poly_constant_lo * transition_density ** (\n        poly_exponent_lo - poly_exponent_hi\n    )\n    if rest_mass_density < transition_density:\n        polytropic_constant = poly_constant_lo\n        polytropic_exponent = poly_exponent_lo\n    else:\n        polytropic_constant = poly_constant_hi\n        polytropic_exponent = poly_exponent_hi\n\n    eint_constant = calc_eint_constant(\n        polytropic_exponent,\n        transition_density,\n        poly_constant_lo,\n        poly_exponent_lo,\n        poly_exponent_hi,\n    )\n\n    return (\n        polytropic_constant\n        * polytropic_exponent\n        / (polytropic_exponent - 1.0)\n        * rest_mass_density ** (polytropic_exponent - 1.0)\n        + eint_constant\n    )\n\n\ndef piecewisepolytropic_specific_internal_energy_from_density(\n    rest_mass_density,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    poly_constant_hi = poly_constant_lo * transition_density ** (\n        poly_exponent_lo - poly_exponent_hi\n    )\n    if rest_mass_density < transition_density:\n        polytropic_constant = poly_constant_lo\n        polytropic_exponent = poly_exponent_lo\n    else:\n        polytropic_constant = poly_constant_hi\n        polytropic_exponent = poly_exponent_hi\n\n    eint_constant = calc_eint_constant(\n        polytropic_exponent,\n        transition_density,\n        poly_constant_lo,\n        poly_exponent_lo,\n        poly_exponent_hi,\n    )\n\n    return (\n        polytropic_constant\n        * rest_mass_density ** (polytropic_exponent - 1.0)\n        / (polytropic_exponent - 1.0)\n        + eint_constant\n    )\n\n\ndef piecewisepolytropic_chi_from_density(\n    rest_mass_density,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    poly_constant_hi = poly_constant_lo * transition_density ** (\n        poly_exponent_lo - poly_exponent_hi\n    )\n    if rest_mass_density < transition_density:\n        polytropic_constant = poly_constant_lo\n        polytropic_exponent = poly_exponent_lo\n    else:\n        polytropic_constant = poly_constant_hi\n        polytropic_exponent = poly_exponent_hi\n    return (\n        polytropic_constant\n        * polytropic_exponent\n        * rest_mass_density ** (polytropic_exponent - 1.0)\n    )\n\n\ndef piecewisepolytropic_kappa_times_p_over_rho_squared_from_density(\n    rest_mass_density,\n    transition_density,\n    poly_constant_lo,\n    poly_exponent_lo,\n    poly_exponent_hi,\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef polytropic_pressure_from_density(\n    rest_mass_density, polytropic_constant, polytropic_exponent\n):\n    return polytropic_constant * rest_mass_density**polytropic_exponent\n\n\ndef polytropic_rel_rest_mass_density_from_enthalpy(\n    specific_enthalpy, polytropic_constant, polytropic_exponent\n):\n    return (\n        (polytropic_exponent - 1.0)\n        / (polytropic_constant * polytropic_exponent)\n        * (specific_enthalpy - 1.0)\n    ) ** (1.0 / (polytropic_exponent - 1.0))\n\n\ndef polytropic_newt_rest_mass_density_from_enthalpy(\n    specific_enthalpy, polytropic_constant, polytropic_exponent\n):\n    return (\n        (polytropic_exponent - 1.0)\n        / (polytropic_constant * polytropic_exponent)\n        * (specific_enthalpy)\n    ) ** (1.0 / (polytropic_exponent - 1.0))\n\n\ndef polytropic_rel_specific_enthalpy_from_density(\n    rest_mass_density, polytropic_constant, polytropic_exponent\n):\n    return 1.0 + polytropic_constant * polytropic_exponent / (\n        polytropic_exponent - 1.0\n    ) * rest_mass_density ** (polytropic_exponent - 1.0)\n\n\ndef polytropic_newt_specific_enthalpy_from_density(\n    rest_mass_density, polytropic_constant, polytropic_exponent\n):\n    return (\n        polytropic_constant\n        * polytropic_exponent\n        / (polytropic_exponent - 1.0)\n        * rest_mass_density ** (polytropic_exponent - 1.0)\n    )\n\n\ndef polytropic_specific_internal_energy_from_density(\n    rest_mass_density, polytropic_constant, polytropic_exponent\n):\n    return (\n        polytropic_constant\n        * rest_mass_density ** (polytropic_exponent - 1.0)\n        / (polytropic_exponent - 1.0)\n    )\n\n\ndef polytropic_chi_from_density(\n    rest_mass_density, polytropic_constant, polytropic_exponent\n):\n    return (\n        polytropic_constant\n        * polytropic_exponent\n        * rest_mass_density ** (polytropic_exponent - 1.0)\n    )\n\n\ndef polytropic_kappa_times_p_over_rho_squared_from_density(\n    rest_mass_density, polytropic_constant, polytropic_exponent\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.EquationsOfState.Python.Enthalpy\"\n  \"Test_Enthalpy.py\"\n  \"Unit;EquationsOfState;Python\"\n  PyEquationsOfState)\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.EquationsOfState.Python.PolytropicFluid\"\n  \"Test_PolytropicFluid.py\"\n  \"Unit;EquationsOfState;Python\"\n  PyEquationsOfState)\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.EquationsOfState.Python.PiecewisePolytropicFluid\"\n  \"Test_PiecewisePolytropicFluid.py\"\n  \"Unit;EquationsOfState;Python\"\n  PyEquationsOfState)\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.EquationsOfState.Python.Spectral\"\n  \"Test_Spectral.py\"\n  \"Unit;EquationsOfState;Python\"\n  PyEquationsOfState)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Python/Test_Enthalpy.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre\nimport spectre.PointwiseFunctions.Hydro.EquationsOfState as EoS\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar\n\n\nclass TestEnthalpy(unittest.TestCase):\n    def test_enthalpy(self):\n        # Parameters are DBHF parameters from https://doi.org/10.48550/arXiv.2301.13818\n\n        low_eos_spectre = EoS.Spectral(\n            reference_density=4.533804669935759e-05,\n            reference_pressure=9.970647727158039e-08,\n            spectral_coefficients=[\n                1.2,\n                0.0,\n                1.34440187653529,\n                -0.46098357752567365,\n            ],\n            upper_density=0.0004533804669935759,\n        )\n\n        eos_spectre = EoS.EnthalpySpectral(\n            reference_density=0.00022669023349678794,\n            max_density=0.0031736632689550316,\n            min_density=0.0004533804669935759,\n            trig_scale=1.26426988871305,\n            polynomial_coefficients=[\n                1.0,\n                0.08063293075870805,\n                4.406887319408924e-26,\n                8.177895241388924e-22,\n                0.013558242085066733,\n                0.004117320982626606,\n                9.757362504479485e-26,\n                1.5646573325075753e-30,\n                0.00016253964205058317,\n            ],\n            sin_coefficients=[\n                0.0003763514388305583,\n                0.017968749910748837,\n                0.008140052979970034,\n                -0.003067418379116628,\n                -0.0008236601907322793,\n            ],\n            cos_coefficients=[\n                -0.01080996024705052,\n                -0.003421193490191067,\n                0.012325774692378716,\n                0.004367136076912163,\n                -0.00020374276952538073,\n            ],\n            low_density_eos=low_eos_spectre,\n            transition_delta_epsilon=0.0,\n        )\n        self.assertTrue(\n            isinstance(eos_spectre, EoS.RelativisticEquationOfState1D)\n        )\n        # Only testing p(rho) because the class is tested thoroughly in C++\n        density = np.geomspace(4.6e-4, 5e-3, 10)\n        pressure = eos_spectre.pressure_from_density(\n            Scalar[DataVector](density)\n        )\n        # These are the pressures calculated in python implementation\n        # testing the bindings. The python implementation will be added\n        # in a second merge request.\n\n        pressures_pyth = [\n            1.5302849088016283e-5,\n            3.1441124536164583e-5,\n            6.5907292284151015e-5,\n            1.4033564857885284e-4,\n            3.0098826697494873e-4,\n            6.4603643776028635e-4,\n            1.3821660234437354e-3,\n            2.9420820328679361e-3,\n            6.2206944563790453e-3,\n            1.3007001237896111e-2,\n        ]\n\n        npt.assert_allclose(\n            pressure[0],\n            pressures_pyth,\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Python/Test_PiecewisePolytropicFluid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport spectre.PointwiseFunctions.Hydro.EquationsOfState as spectre_eos\n\n\nclass TestPiecewisePolytropicFluid(unittest.TestCase):\n    def test_creation(self):\n        # This class currently exposes no functionality, so we can't test\n        # anything but its base class and that it can be instantiated.\n        eos = spectre_eos.RelativisticPiecewisePolytropicFluid(\n            transition_density=10.0,\n            polytropic_constant_lo=5.0,\n            polytropic_exponent_lo=1.5,\n            polytropic_exponent_hi=2.1,\n        )\n        self.assertTrue(\n            isinstance(eos, spectre_eos.RelativisticEquationOfState1D)\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Python/Test_PolytropicFluid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.PointwiseFunctions.Hydro.EquationsOfState as spectre_eos\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar\n\n\nclass TestPolytropicFluid(unittest.TestCase):\n    def test_polytropic_fluid(self):\n        K = 100.0\n        Gamma = 2.0\n        eos = spectre_eos.RelativisticPolytropicFluid(\n            polytropic_constant=K, polytropic_exponent=Gamma\n        )\n        self.assertTrue(\n            isinstance(eos, spectre_eos.RelativisticEquationOfState1D)\n        )\n        # Only testing p(rho) because the class is tested thoroughly in C++\n        density = np.random.random_sample((1, 5))\n        pressure = eos.pressure_from_density(Scalar[DataVector](density))\n        npt.assert_allclose(pressure, K * density**Gamma)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Python/Test_Spectral.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.PointwiseFunctions.Hydro.EquationsOfState as spectre_eos\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar\n\n\nclass TestSpectral(unittest.TestCase):\n    def test_spectral(self):\n        # Parameters are sLy$\\Gamma$2 from https://doi.org/10.48550/arXiv.1908.05277\n\n        eos = spectre_eos.Spectral(\n            reference_density=1.0118e-4,\n            reference_pressure=3.33625e-7,\n            spectral_coefficients=[2, 0, 0.4029, -0.1008],\n            upper_density=6e-3,\n        )\n\n        # These are the pressures calculated in python implementation\n        # testing the bindings. The python implementation will be added\n        # in a second merge request.\n\n        pressures_pyth = [\n            3.258886510983e-7,\n            7.846581912375e-7,\n            1.990840482194e-6,\n            5.508910104189e-6,\n            1.683916561373e-5,\n            5.636080617205e-5,\n            2.003712243313e-4,\n            7.183209159534e-4,\n            2.412536276978e-3,\n            6.901956906998e-3,\n        ]\n        self.assertTrue(\n            isinstance(eos, spectre_eos.RelativisticEquationOfState1D)\n        )\n        # Only testing p(rho) because the class is tested thoroughly in C++\n        density = np.geomspace(1e-4, 5e-3, 10)\n        pressure = eos.pressure_from_density(Scalar[DataVector](density))\n\n        npt.assert_allclose(\n            pressure,\n            [pressures_pyth],\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_Barotropic2D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.Barotropic2D\",\n                  \"[Unit][EquationsOfState]\") {\n  namespace EoS = EquationsOfState;\n  const EoS::PolytropicFluid<true> underlying_eos{100.0, 2.0};\n  EoS::Barotropic2D<EoS::PolytropicFluid<true>> eos{underlying_eos};\n  CHECK(eos.rest_mass_density_lower_bound() ==\n        underlying_eos.rest_mass_density_lower_bound());\n  CHECK(eos.rest_mass_density_upper_bound() ==\n        underlying_eos.rest_mass_density_upper_bound());\n  CHECK(eos.electron_fraction_lower_bound() == 0.0);\n  CHECK(eos.electron_fraction_upper_bound() == 1.0);\n  CHECK(eos.temperature_lower_bound() == 0.0);\n  CHECK(eos.temperature_upper_bound() == std::numeric_limits<double>::max());\n  CHECK(eos.is_barotropic());\n  CHECK(eos.is_equilibrium());\n  {\n    // DataVector functions\n    const Scalar<DataVector> rest_mass_density{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-2, 1e-2, 1.0}};\n    // temperature and electron fraction should be irrelevant\n    const Scalar<DataVector> temperature{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-2, 1e-2, 1.0}};\n    const Scalar<DataVector> specific_internal_energy =\n        underlying_eos.specific_internal_energy_from_density(rest_mass_density);\n    const Scalar<DataVector> pressure =\n        underlying_eos.pressure_from_density(rest_mass_density);\n    CHECK(eos.pressure_from_density_and_enthalpy(\n              rest_mass_density,\n              hydro::relativistic_specific_enthalpy(\n                  rest_mass_density, specific_internal_energy, pressure)) ==\n          pressure);\n    CHECK(eos.pressure_from_density_and_energy(rest_mass_density,\n                                               specific_internal_energy) ==\n          underlying_eos.pressure_from_density(rest_mass_density));\n    CHECK(eos.specific_internal_energy_from_density_and_pressure(\n              rest_mass_density, specific_internal_energy) ==\n          underlying_eos.specific_internal_energy_from_density(\n              rest_mass_density));\n    CHECK(eos.chi_from_density_and_energy(rest_mass_density,\n                                          specific_internal_energy) ==\n          underlying_eos.chi_from_density(rest_mass_density));\n    CHECK(eos.kappa_times_p_over_rho_squared_from_density_and_energy(\n              rest_mass_density, specific_internal_energy) ==\n          underlying_eos.kappa_times_p_over_rho_squared_from_density(\n              rest_mass_density));\n    // The energy is independent of energy, so this is a garabage value, but it\n    // is taken to be zero\n    CHECK(eos.temperature_from_density_and_energy(rest_mass_density,\n                                                  specific_internal_energy) ==\n          make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0));\n  }\n\n  {\n    // double functions\n    const Scalar<double> rest_mass_density{{1e-10}};\n    const Scalar<double> specific_internal_energy =\n        underlying_eos.specific_internal_energy_from_density(rest_mass_density);\n    const Scalar<double> pressure =\n        underlying_eos.pressure_from_density(rest_mass_density);\n    CHECK(eos.pressure_from_density_and_enthalpy(\n              rest_mass_density,\n              hydro::relativistic_specific_enthalpy(\n                  rest_mass_density, specific_internal_energy, pressure)) ==\n          pressure);\n    CHECK(eos.pressure_from_density_and_energy(rest_mass_density,\n                                               specific_internal_energy) ==\n          underlying_eos.pressure_from_density(rest_mass_density));\n    CHECK(eos.specific_internal_energy_from_density_and_pressure(\n              rest_mass_density, specific_internal_energy) ==\n          underlying_eos.specific_internal_energy_from_density(\n              rest_mass_density));\n    CHECK(eos.chi_from_density_and_energy(rest_mass_density,\n                                          specific_internal_energy) ==\n          underlying_eos.chi_from_density(rest_mass_density));\n    CHECK(eos.kappa_times_p_over_rho_squared_from_density_and_energy(\n              rest_mass_density, specific_internal_energy) ==\n          underlying_eos.kappa_times_p_over_rho_squared_from_density(\n              rest_mass_density));\n    // The energy is independent of energy, so this is a garabage value, but it\n    // is taken to be zero\n    CHECK(eos.temperature_from_density_and_energy(rest_mass_density,\n                                                  specific_internal_energy) ==\n          make_with_value<Scalar<double>>(rest_mass_density, 0.0));\n  }\n  {\n    register_derived_classes_with_charm<EoS::EquationOfState<true, 2>>();\n    register_derived_classes_with_charm<EoS::EquationOfState<true, 1>>();\n    const auto eos_pointer = serialize_and_deserialize(\n        TestHelpers::test_creation<\n            std::unique_ptr<EoS::EquationOfState<true, 2>>>(\n            {\"Barotropic2D(PolytropicFluid):\\n\"\n             \"  PolytropicFluid:\\n\"\n             \"    PolytropicConstant: 100.0\\n\"\n             \"    PolytropicExponent: 2.0\"}));\n    const auto& deserialized_eos =\n        dynamic_cast<const EoS::Barotropic2D<EoS::PolytropicFluid<true>>&>(\n            *eos_pointer);\n    TestHelpers::EquationsOfState::test_get_clone(deserialized_eos);\n\n    CHECK(eos == deserialized_eos);\n    CHECK(eos != EoS::Barotropic2D<EoS::PolytropicFluid<true>>{\n                     EoS::PolytropicFluid<true>(10.0, 2.0)});\n    CHECK(eos != EoS::Barotropic2D<EoS::PolytropicFluid<true>>{\n                     EoS::PolytropicFluid<true>(100.0, 1.0)});\n    CHECK(not eos.is_equal(EoS::Barotropic2D<EoS::Spectral>{\n        EoS::Spectral(1e-5, 1e-4, {2.0, 0.0, 0.0}, 1e-2)}));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_Barotropic3D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.Barotropic3D\",\n                  \"[Unit][EquationsOfState]\") {\n  namespace EoS = EquationsOfState;\n  const EoS::PolytropicFluid<true> underlying_eos{100.0, 2.0};\n  EoS::Barotropic3D<EoS::PolytropicFluid<true>> eos{underlying_eos};\n  CHECK(eos.rest_mass_density_lower_bound() ==\n        underlying_eos.rest_mass_density_lower_bound());\n  CHECK(eos.rest_mass_density_upper_bound() ==\n        underlying_eos.rest_mass_density_upper_bound());\n  CHECK(eos.electron_fraction_lower_bound() == 0.0);\n  CHECK(eos.electron_fraction_upper_bound() == 1.0);\n  CHECK(eos.temperature_lower_bound() == 0.0);\n  CHECK(eos.temperature_upper_bound() == std::numeric_limits<double>::max());\n  CHECK(eos.is_barotropic());\n  CHECK(not eos.is_equilibrium());\n  {\n    // DataVector functions\n    const Scalar<DataVector> rest_mass_density{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    // temperature and electron fraction should be irrelevant\n    const Scalar<DataVector> electron_fraction{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    const Scalar<DataVector> temperature{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    const Scalar<DataVector> specific_internal_energy =\n        underlying_eos.specific_internal_energy_from_density(rest_mass_density);\n    const Scalar<DataVector> pressure =\n        underlying_eos.pressure_from_density(rest_mass_density);\n    CHECK(eos.pressure_from_density_and_temperature(\n              rest_mass_density, temperature, electron_fraction) == pressure);\n    CHECK(eos.pressure_from_density_and_energy(\n              rest_mass_density, specific_internal_energy, electron_fraction) ==\n          underlying_eos.pressure_from_density(rest_mass_density));\n    // The energy is independent of energy, so this is a garabage value, but it\n    // is taken to be zero\n    CHECK(eos.temperature_from_density_and_energy(\n              rest_mass_density, specific_internal_energy, electron_fraction) ==\n          make_with_value<Scalar<DataVector>>(rest_mass_density, 0.0));\n    DataVector enthalpy_density =\n        get(rest_mass_density) * (1.0 + get(specific_internal_energy)) +\n        get(pressure);\n    CHECK(eos.sound_speed_squared_from_density_and_temperature(\n              rest_mass_density, temperature, electron_fraction) ==\n          Scalar<DataVector>{\n              get(rest_mass_density) *\n              get(underlying_eos.chi_from_density(rest_mass_density)) /\n              enthalpy_density});\n  }\n\n  {\n    // double functions\n    const Scalar<double> rest_mass_density{{1e-10}};\n    // temperature and electron fraction should be irrelevant\n    const Scalar<double> electron_fraction{{1e-10}};\n    const Scalar<double> temperature{{1e-10}};\n    const Scalar<double> specific_internal_energy =\n        underlying_eos.specific_internal_energy_from_density(rest_mass_density);\n    const Scalar<double> pressure =\n        underlying_eos.pressure_from_density(rest_mass_density);\n    CHECK(eos.pressure_from_density_and_temperature(\n              rest_mass_density, temperature, electron_fraction) == pressure);\n    CHECK(eos.pressure_from_density_and_energy(\n              rest_mass_density, specific_internal_energy, electron_fraction) ==\n          underlying_eos.pressure_from_density(rest_mass_density));\n    // The energy is independent of energy, so this is a garabage value, but it\n    // is taken to be zero\n    CHECK(eos.temperature_from_density_and_energy(\n              rest_mass_density, specific_internal_energy, electron_fraction) ==\n          make_with_value<Scalar<double>>(rest_mass_density, 0.0));\n    double enthalpy_density =\n        get(rest_mass_density) * (1.0 + get(specific_internal_energy)) +\n        get(pressure);\n    CHECK(\n        eos.sound_speed_squared_from_density_and_temperature(\n            rest_mass_density, temperature, electron_fraction) ==\n        Scalar<double>{get(rest_mass_density) *\n                       get(underlying_eos.chi_from_density(rest_mass_density)) /\n                       enthalpy_density});\n  }\n  {\n    register_derived_classes_with_charm<EoS::EquationOfState<true, 3>>();\n    register_derived_classes_with_charm<EoS::EquationOfState<true, 1>>();\n    const auto eos_pointer = serialize_and_deserialize(\n        TestHelpers::test_creation<\n            std::unique_ptr<EoS::EquationOfState<true, 3>>>(\n            {\"Barotropic3D(PolytropicFluid):\\n\"\n             \"  PolytropicFluid:\\n\"\n             \"    PolytropicConstant: 100.0\\n\"\n             \"    PolytropicExponent: 2.0\"}));\n    const EoS::Barotropic3D<EoS::PolytropicFluid<true>>& deserialized_eos =\n        dynamic_cast<const EoS::Barotropic3D<EoS::PolytropicFluid<true>>&>(\n            *eos_pointer);\n    TestHelpers::EquationsOfState::test_get_clone(deserialized_eos);\n\n    CHECK(eos == deserialized_eos);\n    CHECK(eos != EoS::Barotropic3D<EoS::PolytropicFluid<true>>{\n                     EoS::PolytropicFluid<true>(10.0, 2.0)});\n    CHECK(eos != EoS::Barotropic3D<EoS::PolytropicFluid<true>>{\n                     EoS::PolytropicFluid<true>(100.0, 1.0)});\n    CHECK(not eos.is_equal(EoS::Barotropic3D<EoS::Spectral>{\n        EoS::Spectral(1e-5, 1e-4, {2.0, 0.0, 0.0}, 1e-2)}));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_DarkEnergyFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/DarkEnergyFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\nvoid check_bounds() {\n  const auto eos = EquationsOfState::DarkEnergyFluid<true>{1.0};\n  CHECK(0.0 == eos.rest_mass_density_lower_bound());\n  CHECK(-1.0 == eos.specific_internal_energy_lower_bound(1.0));\n  CHECK(0.0 == eos.specific_enthalpy_lower_bound());\n  const double max_double = std::numeric_limits<double>::max();\n  CHECK(max_double == eos.rest_mass_density_upper_bound());\n  CHECK(max_double == eos.specific_internal_energy_upper_bound(1.0));\n  CHECK(eos.baryon_mass() ==\n        approx(hydro::units::geometric::default_baryon_mass));\n  CHECK(not eos.is_barotropic());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.DarkEnergyFluid\",\n                  \"[Unit][EquationsOfState]\") {\n  namespace EoS = EquationsOfState;\n  register_derived_classes_with_charm<EoS::EquationOfState<true, 2>>();\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Hydro/EquationsOfState/\"};\n  const double d_for_size = std::numeric_limits<double>::signaling_NaN();\n  const DataVector dv_for_size(5);\n  const auto eos = EoS::DarkEnergyFluid<true>{1.0};\n  const auto other_eos = EoS::DarkEnergyFluid<true>{0.5};\n  const auto other_type_eos = EoS::PolytropicFluid<true>{100.0, 2.0};\n  CHECK(eos == eos);\n  CHECK(eos != other_eos);\n  CHECK(eos != other_type_eos);\n  CHECK(*eos.promote_to_3d_eos() ==\n        EoS::Equilibrium3D<EoS::DarkEnergyFluid<true>>(eos));\n  TestHelpers::EquationsOfState::test_get_clone(\n      EoS::DarkEnergyFluid<true>(1.0));\n\n  TestHelpers::EquationsOfState::check(EoS::DarkEnergyFluid<true>(1.0),\n                                       \"DarkEnergyFluid\", \"dark_energy_fluid\",\n                                       d_for_size, 1.0);\n  TestHelpers::EquationsOfState::check(EoS::DarkEnergyFluid<true>(1.0 / 3.0),\n                                       \"DarkEnergyFluid\", \"dark_energy_fluid\",\n                                       dv_for_size, 1.0 / 3.0);\n  TestHelpers::EquationsOfState::check(EoS::DarkEnergyFluid<true>(1.0),\n                                       \"DarkEnergyFluid\", \"dark_energy_fluid\",\n                                       dv_for_size, 1.0);\n  TestHelpers::EquationsOfState::check(EoS::DarkEnergyFluid<true>(1.0 / 3.0),\n                                       \"DarkEnergyFluid\", \"dark_energy_fluid\",\n                                       d_for_size, 1.0 / 3.0);\n\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 2>>>(\n          {\"DarkEnergyFluid:\\n\"\n           \"  ParameterW: 1.0\\n\"}),\n      \"DarkEnergyFluid\", \"dark_energy_fluid\", d_for_size, 1.0);\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 2>>>(\n          {\"DarkEnergyFluid:\\n\"\n           \"  ParameterW: 0.3333333333333333\\n\"}),\n      \"DarkEnergyFluid\", \"dark_energy_fluid\", d_for_size, 1.0 / 3.0);\n\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 2>>>(\n          {\"DarkEnergyFluid:\\n\"\n           \"  ParameterW: 1.0\\n\"}),\n      \"DarkEnergyFluid\", \"dark_energy_fluid\", dv_for_size, 1.0);\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 2>>>(\n          {\"DarkEnergyFluid:\\n\"\n           \"  ParameterW: 0.3333333333333333\\n\"}),\n      \"DarkEnergyFluid\", \"dark_energy_fluid\", dv_for_size, 1.0 / 3.0);\n\n  check_bounds();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_Enthalpy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <pup.h>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Enthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\nnamespace EquationsOfState {\nnamespace {\n\nvoid check_exact() {\n  // Test creation\n  register_derived_classes_with_charm<EquationOfState<true, 1>>();\n  const auto eos_pointer = serialize_and_deserialize(\n      TestHelpers::test_creation<std::unique_ptr<EquationOfState<true, 1>>>(\n          {\"Enthalpy(Spectral):\\n\"\n           \"  ReferenceDensity: 2.0\\n\"\n           \"  MinimumDensity: 4.0\\n\"\n           \"  MaximumDensity: 100.0\\n\"\n           \"  TrigScaling: 1.5\\n\"\n           \"  PolynomialCoefficients: [1.0,0.2,0.0,0.0,0.0001]\\n\"\n           \"  SinCoefficients: [0.01,0.003,-0.0001,0.0001]\\n\"\n           \"  CosCoefficients: [0.01,0.003,0.0001,0.00001]\\n\"\n           \"  TransitionDeltaEpsilon: 0.0\\n\"\n           \"  Spectral:\\n\"\n           \"    ReferenceDensity: 1.054388552462907080\\n\"\n           \"    ReferencePressure: 0.02168181441607176\\n\"\n           \"    Coefficients: [1.4, 0, -0.022880893142188646, \"\n           \"0.7099134558804311]\\n\"\n           \"    UpperDensity: 4.0\\n\"}));\n\n  const Enthalpy<Spectral>& eos =\n      dynamic_cast<const Enthalpy<Spectral>&>(*eos_pointer);\n  TestHelpers::EquationsOfState::test_get_clone(eos);\n  // Test comparison operators\n  {\n    const auto other_eos = Enthalpy<Spectral>{\n        1.0,\n        2.0,\n        1.5,\n        1.0,\n        std::vector<double>{1.0, 1.0},\n        std::vector<double>{0.1},\n        std::vector<double>{0.1},\n        Spectral{.5, .3, std::vector<double>{1.4, 0.0, 0.0}, 1.0},\n        0.0};\n    const auto other_low_eos =\n        Enthalpy<PolytropicFluid<true>>{1.0,\n                                        2.0,\n                                        1.5,\n                                        1.0,\n                                        std::vector<double>{1.0, 1.0},\n                                        std::vector<double>{0.1},\n                                        std::vector<double>{0.1},\n                                        PolytropicFluid<true>{100.0, 2.0},\n                                        0.0};\n    const auto other_hi_eos = Enthalpy<Spectral>{\n        1.0,\n        2.0,\n        1.5,\n        1.0,\n        std::vector<double>{1.0, .95},\n        std::vector<double>{0.05},\n        std::vector<double>{0.05},\n        Spectral{.5, .3, std::vector<double>{1.4, 0.0, 0.0}, 1.0},\n        0.0};\n    const auto other_type_eos = PolytropicFluid<true>{100.0, 2.0};\n\n    CHECK(eos == eos);\n    CHECK(other_eos != other_low_eos);\n    CHECK(other_eos != other_hi_eos);\n    CHECK(eos != other_eos);\n    CHECK(eos != other_type_eos);\n    CHECK(other_low_eos != other_type_eos);\n    CHECK(*eos.promote_to_3d_eos() == Barotropic3D<Enthalpy<Spectral>>(eos));\n    CHECK(*eos.promote_to_2d_eos() == Barotropic2D<Enthalpy<Spectral>>(eos));\n    CHECK(eos.baryon_mass() ==\n          approx(hydro::units::geometric::default_baryon_mass));\n    CHECK(eos.is_barotropic());\n  }\n  // Test DataVector functions\n  {\n    const Scalar<DataVector> rho{DataVector{1.5 * exp(1.0), 1.5 * exp(2.0),\n                                            1.5 * exp(3.0), 1.5 * exp(4.0)}};\n    const Scalar<DataVector> p = eos.pressure_from_density(rho);\n    CAPTURE(rho);\n    CAPTURE(p);\n    const Scalar<DataVector> p_expected{\n        DataVector{0.25505331617202415, 1.53718607768077375,\n                   5.29831173857097415, 17.13581872708278553}};\n    CHECK_ITERABLE_APPROX(p, p_expected);\n    const auto eps_c = eos.specific_internal_energy_from_density(rho);\n    const Scalar<DataVector> eps_expected{\n        DataVector{0.09425055282366418, 0.19998963900481931,\n                   0.36012641619526214, 0.55066416955025821}};\n    CHECK_ITERABLE_APPROX(eps_c, eps_expected);\n    const auto chi_c = eos.chi_from_density(rho);\n    const Scalar<DataVector> chi_expected{\n        DataVector{0.18207356789438428, 0.1923165022214991, 0.19908149093638267,\n                   0.25186554704031844}};\n    CHECK_ITERABLE_APPROX(get(chi_expected), get(chi_c));\n    const Scalar<DataVector> p_c_kappa_c_over_rho_sq_expected{\n        DataVector{0.0, 0.0, 0.0, 0.0}};\n    const auto p_c_kappa_c_over_rho_sq =\n        eos.kappa_times_p_over_rho_squared_from_density(rho);\n    CHECK_ITERABLE_APPROX(p_c_kappa_c_over_rho_sq_expected,\n                          p_c_kappa_c_over_rho_sq);\n    const auto rho_from_enthalpy = eos.rest_mass_density_from_enthalpy(\n        hydro::relativistic_specific_enthalpy(rho, eps_c, p));\n    CHECK_ITERABLE_APPROX(rho, rho_from_enthalpy);\n  }\n  // Test low density stitched EoS\n  {\n    const Scalar<DataVector> rho{DataVector{1.5 * exp(-1.0), 1.5}};\n    const Scalar<DataVector> p = eos.pressure_from_density(rho);\n    const Scalar<DataVector> p_expected{\n        DataVector{0.00875810516598451, 0.03560143071808177}};\n    CHECK_ITERABLE_APPROX(p, p_expected);\n    const auto eps_c = eos.specific_internal_energy_from_density(rho);\n    const Scalar<DataVector> eps_expected{\n        DataVector{0.03967833020738167, 0.05919690661251007}};\n    CHECK_ITERABLE_APPROX(eps_c, eps_expected);\n    const auto chi_c = eos.chi_from_density(rho);\n    const Scalar<DataVector> chi_expected{\n        DataVector{0.02221986491613373, 0.03389855168318545}};\n    CHECK_ITERABLE_APPROX(get(chi_expected), get(chi_c));\n    const Scalar<DataVector> p_c_kappa_c_over_rho_sq_expected{\n        DataVector{0.0, 0.0}};\n    const auto p_c_kappa_c_over_rho_sq =\n        eos.kappa_times_p_over_rho_squared_from_density(rho);\n    CHECK_ITERABLE_APPROX(p_c_kappa_c_over_rho_sq_expected,\n                          p_c_kappa_c_over_rho_sq);\n    const auto rho_from_enthalpy = eos.rest_mass_density_from_enthalpy(\n        hydro::relativistic_specific_enthalpy(rho, eps_c, p));\n    CHECK_ITERABLE_APPROX(rho, rho_from_enthalpy);\n  }\n  // Test double functions\n  {\n    const Scalar<double> rho{1.5 * exp(1.0)};\n    const auto p = eos.pressure_from_density(rho);\n    const double p_expected = 0.25505331617202415;\n    CHECK(get(p) == approx(p_expected));\n    const auto eps = eos.specific_internal_energy_from_density(rho);\n    const double eps_expected = 0.09425055282366418;\n    CHECK(get(eps) == approx(eps_expected));\n    const auto chi = eos.chi_from_density(rho);\n    const double chi_expected = 0.18207356789438428;\n    CHECK(get(chi) == approx(chi_expected));\n    const auto p_c_kappa_c_over_rho_sq =\n        eos.kappa_times_p_over_rho_squared_from_density(rho);\n    const double p_c_kappa_c_over_rho_sq_expected = 0.0;\n    CHECK(get(p_c_kappa_c_over_rho_sq) ==\n          approx(p_c_kappa_c_over_rho_sq_expected));\n    const auto rho_from_enthalpy = eos.rest_mass_density_from_enthalpy(\n        hydro::relativistic_specific_enthalpy(rho, eps, p));\n    CHECK(get(rho) == approx(get(rho_from_enthalpy)));\n  }\n  {\n    // Guarantee that the root find has the correct bracket:\n    // the risk being that if it does not have the correct bracket,\n    // a call to rho(h) evaluates the enthalpy EoS in the low-density region\n    // where it generically does not effectively represent the EoS\n    // We therefore make the EoS nonsensical in the low density region,\n    // such that the root finder would fail if the wrong bracket were used\n    const auto oscillating_eos =\n        Enthalpy<Spectral>{0.5,\n                           2.0,\n                           1.0,\n                           M_PI / log(2.0),\n                           std::vector<double>{3.5},\n                           std::vector<double>{0.0},\n                           std::vector<double>{0.5},\n                           Spectral{.5, .25, std::vector<double>{2.0}, 1.0},\n                           0.0};\n    // h(z) = 3.5 + .5 * cos(pi/log(2) * z)\n    // cos(pi)) = -1, so the enthalpy is 3 at z=log(2.0),\n    // which is rho = 1.0\n    const Scalar<double> target_enthalpy{3.25};\n    // If the rootfinder had the wrong bracket e.g. z in [0, log(4)]\n    // then because h(z=0) = 3.5 + .5 = 4.0, by IVT there's some root between\n    // z=0 and z = log(2).  Also h(z=log(4) = 2log(2)) = 4.0, so there's a\n    // second root between log(2) and log(4).  This will cause a bracketing to\n    // rootfinder to fail (because the function is not monotonic)\n    const Scalar<double> target_rho{pow(2.0, 1.0 / 3.0)};\n    const auto rho_from_enthalpy =\n        oscillating_eos.rest_mass_density_from_enthalpy(target_enthalpy);\n    // We check to make sure the root find also found the correct value\n    CHECK(get(target_rho) == approx(get(rho_from_enthalpy)));\n  }\n  // Test bounds\n  CHECK(0.0 == eos.rest_mass_density_lower_bound());\n  CHECK(0.0 == eos.specific_internal_energy_lower_bound());\n  CHECK(1.0 == eos.specific_enthalpy_lower_bound());\n  const double max_double = std::numeric_limits<double>::max();\n  CHECK(max_double == eos.rest_mass_density_upper_bound());\n  CHECK(max_double == eos.specific_internal_energy_upper_bound());\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.Enthalpy\",\n                  \"[Unit][EquationsOfState]\") {\n  check_exact();\n}\n}  // namespace EquationsOfState\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_Equilibrium3D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.Equilibrium3D\",\n                  \"[Unit][EquationsOfState]\") {\n  namespace EoS = EquationsOfState;\n\n  const double ideal_adiabatic_index = 2.0;\n  const double cold_polytropic_index = 2.0;\n  const EoS::PolytropicFluid<true> cold_eos{100.0, cold_polytropic_index};\n  const EoS::IdealFluid<true> eos_ideal_fluid{ideal_adiabatic_index, 0.0};\n\n  EoS::Equilibrium3D<EoS::IdealFluid<true>> eos_ideal_fluid_3d{eos_ideal_fluid};\n  const EoS::HybridEos<EoS::PolytropicFluid<true>> underlying_eos{\n      cold_eos, ideal_adiabatic_index};\n  EoS::Equilibrium3D<EoS::HybridEos<EoS::PolytropicFluid<true>>> eos{\n      underlying_eos};\n  CHECK(eos.rest_mass_density_lower_bound() ==\n        underlying_eos.rest_mass_density_lower_bound());\n  CHECK(eos.rest_mass_density_upper_bound() ==\n        underlying_eos.rest_mass_density_upper_bound());\n  CHECK(eos.electron_fraction_lower_bound() == 0.0);\n  CHECK(eos.electron_fraction_upper_bound() == 1.0);\n  CHECK(eos.specific_enthalpy_lower_bound() ==\n        underlying_eos.specific_enthalpy_lower_bound());\n  CHECK(not eos.is_barotropic());\n  CHECK(eos.is_equilibrium());\n\n  {\n    const double rest_mass_density = 1e-3;\n    const double electron_fraction = 0.1;\n    const double underlying_specific_energy_lower_bound =\n        underlying_eos.specific_internal_energy_lower_bound(rest_mass_density);\n    const double underlying_specific_energy_upper_bound =\n        underlying_eos.specific_internal_energy_upper_bound(rest_mass_density);\n    CHECK(eos.specific_internal_energy_lower_bound(rest_mass_density,\n                                                   electron_fraction) ==\n          underlying_specific_energy_lower_bound);\n    CHECK(eos.specific_internal_energy_upper_bound(rest_mass_density,\n                                                   electron_fraction) ==\n          underlying_specific_energy_upper_bound);\n    CHECK(eos.temperature_lower_bound() ==\n          underlying_eos.temperature_lower_bound());\n    CHECK(eos.temperature_upper_bound() ==\n          underlying_eos.temperature_upper_bound());\n  }\n  {\n    // DataVector functions\n    const Scalar<DataVector> rest_mass_density{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    // electron fraction should be irrelevant\n    const Scalar<DataVector> electron_fraction{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    const Scalar<DataVector> temperature{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    const Scalar<DataVector> specific_internal_energy =\n        underlying_eos.specific_internal_energy_from_density_and_temperature(\n            rest_mass_density, temperature);\n    const Scalar<DataVector> pressure =\n        underlying_eos.pressure_from_density_and_energy(\n            rest_mass_density, specific_internal_energy);\n    CHECK(eos.pressure_from_density_and_temperature(\n              rest_mass_density, temperature, electron_fraction) == pressure);\n    CHECK(eos.pressure_from_density_and_energy(\n              rest_mass_density, specific_internal_energy, electron_fraction) ==\n          underlying_eos.pressure_from_density_and_energy(\n              rest_mass_density, specific_internal_energy));\n    CHECK_ITERABLE_APPROX(\n        eos.pressure_from_density_and_energy(\n            rest_mass_density, specific_internal_energy, electron_fraction),\n        pressure);\n    CHECK_ITERABLE_APPROX(\n        eos.temperature_from_density_and_energy(\n            rest_mass_density, specific_internal_energy, electron_fraction),\n        temperature);\n  }\n  {\n    const Scalar<DataVector> rest_mass_density{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    // electron fraction should be irrelevant\n    const Scalar<DataVector> electron_fraction{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    const Scalar<DataVector> temperature{\n        DataVector{1e-10, 1e-6, 1e-4, 1e-3, 1e-2, 1.0}};\n    // For the ideal fluid the sound speed can be computed analytically\n    const Scalar<DataVector> pressure =\n        eos_ideal_fluid_3d.pressure_from_density_and_temperature(\n            rest_mass_density, temperature, electron_fraction);\n    const Scalar<DataVector> specific_internal_energy =\n        eos_ideal_fluid_3d\n            .specific_internal_energy_from_density_and_temperature(\n                rest_mass_density, temperature, electron_fraction);\n    const DataVector enthalpy_density =\n        get(rest_mass_density) * (1.0 + get(specific_internal_energy)) +\n        get(pressure);\n\n    CHECK_ITERABLE_APPROX(\n        get(eos_ideal_fluid_3d.sound_speed_squared_from_density_and_temperature(\n            rest_mass_density, temperature, electron_fraction)),\n        (ideal_adiabatic_index)*get(pressure) / enthalpy_density);\n  }\n  {\n    // double functions\n    const Scalar<double> rest_mass_density{{1e-3}};\n    // temperature and electron fraction should be irrelevant\n    const Scalar<double> electron_fraction{{1e-1}};\n    const Scalar<double> temperature{{1e-1}};\n    const Scalar<double> specific_internal_energy =\n        underlying_eos.specific_internal_energy_from_density_and_temperature(\n            rest_mass_density, temperature);\n    const Scalar<double> pressure =\n        underlying_eos.pressure_from_density_and_energy(\n            rest_mass_density, specific_internal_energy);\n    CHECK(eos.pressure_from_density_and_temperature(\n              rest_mass_density, temperature, electron_fraction) == pressure);\n    CHECK(eos.pressure_from_density_and_energy(\n              rest_mass_density, specific_internal_energy, electron_fraction) ==\n          underlying_eos.pressure_from_density_and_energy(\n              rest_mass_density, specific_internal_energy));\n    CHECK(eos.temperature_from_density_and_energy(\n              rest_mass_density, specific_internal_energy, electron_fraction) ==\n          underlying_eos.temperature_from_density_and_energy(\n              rest_mass_density, specific_internal_energy));\n  }\n  {\n    // Using the ideal fluid for sound speed computations\n    const Scalar<double> rest_mass_density{{1e-3}};\n    // temperature and electron fraction should be irrelevant\n    const Scalar<double> electron_fraction{{1e-1}};\n    const Scalar<double> temperature{{1e-1}};\n    const Scalar<double> pressure =\n        eos_ideal_fluid_3d.pressure_from_density_and_temperature(\n            rest_mass_density, temperature, electron_fraction);\n    const Scalar<double> specific_internal_energy =\n        eos_ideal_fluid_3d\n            .specific_internal_energy_from_density_and_temperature(\n                rest_mass_density, temperature, electron_fraction);\n\n    const double enthalpy_density =\n        get(rest_mass_density) * (1.0 + get(specific_internal_energy)) +\n        get(pressure);\n    // IdealFluid value is known analytically\n    CHECK_ITERABLE_APPROX(\n        get(eos_ideal_fluid_3d.sound_speed_squared_from_density_and_temperature(\n            rest_mass_density, temperature, electron_fraction)),\n        (ideal_adiabatic_index)*get(pressure) / enthalpy_density);\n    // Check that the sound speed calculation works at zero temperature\n    CHECK_ITERABLE_APPROX(\n        get(eos.sound_speed_squared_from_density_and_temperature(\n            rest_mass_density, Scalar<double>{0.0}, electron_fraction)),\n        get(rest_mass_density) /\n            (get(cold_eos.pressure_from_density(rest_mass_density)) +\n             get(rest_mass_density) *\n                 (1.0 + get(cold_eos.specific_internal_energy_from_density(\n                            rest_mass_density)))) *\n            get(cold_eos.chi_from_density(rest_mass_density)));\n  }\n  {\n    register_derived_classes_with_charm<EoS::EquationOfState<true, 3>>();\n    register_derived_classes_with_charm<EoS::EquationOfState<true, 2>>();\n    register_derived_classes_with_charm<EoS::EquationOfState<true, 1>>();\n    const auto eos_pointer = serialize_and_deserialize(\n        TestHelpers::test_creation<\n            std::unique_ptr<EoS::EquationOfState<true, 3>>>(\n            {\"Equilibrium3D(HybridEos(PolytropicFluid)):\\n\"\n             \"  HybridEos:\\n\"\n             \"    ThermalAdiabaticIndex: 2.0\\n\"\n             \"    PolytropicFluid:\\n\"\n             \"      PolytropicConstant: 100.0\\n\"\n             \"      PolytropicExponent: 2.0\"}));\n    const EoS::Equilibrium3D<EoS::HybridEos<EoS::PolytropicFluid<true>>>&\n        deserialized_eos = dynamic_cast<const EoS::Equilibrium3D<\n            EoS::HybridEos<EoS::PolytropicFluid<true>>>&>(*eos_pointer);\n    TestHelpers::EquationsOfState::test_get_clone(deserialized_eos);\n\n    CHECK(eos == deserialized_eos);\n    CHECK(eos !=\n          EoS::Equilibrium3D<EoS::HybridEos<EoS::PolytropicFluid<true>>>{\n              EoS::HybridEos<EoS::PolytropicFluid<true>>({10.0, 2.0}, 2.0)});\n    CHECK(eos !=\n          EoS::Equilibrium3D<EoS::HybridEos<EoS::PolytropicFluid<true>>>{\n              EoS::HybridEos<EoS::PolytropicFluid<true>>({100.0, 1.0}, 2.0)});\n    CHECK(eos !=\n          EoS::Equilibrium3D<EoS::HybridEos<EoS::PolytropicFluid<true>>>{\n              EoS::HybridEos<EoS::PolytropicFluid<true>>({100.0, 2.0}, 1.5)});\n    EoS::Equilibrium3D<EoS::HybridEos<EoS::Spectral>> eq_spectral{\n        EoS::HybridEos<EoS::Spectral>{{1e-5, 1e-4, {2.0, 0.0, 0.0, 0.0}, 1e-2},\n                                      2.0}};\n    CHECK(eq_spectral.is_equal(eq_spectral));\n    CHECK(not(eos.is_equal(eq_spectral)));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_HybridEos.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/HybridEos.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\ntemplate <bool IsRelativistic>\nvoid check_random_polytrope() {\n  register_derived_classes_with_charm<\n      EquationsOfState::EquationOfState<true, 2>>();\n  register_derived_classes_with_charm<\n      EquationsOfState::EquationOfState<false, 2>>();\n  const double d_for_size = std::numeric_limits<double>::signaling_NaN();\n  const DataVector dv_for_size(5);\n  TestHelpers::EquationsOfState::check(\n      EquationsOfState::HybridEos<\n          EquationsOfState::PolytropicFluid<IsRelativistic>>{\n          EquationsOfState::PolytropicFluid<IsRelativistic>{100.0, 4.0 / 3.0},\n          5.0 / 3.0},\n      \"HybridEos\", \"hybrid_polytrope\", d_for_size, 100.0, 4.0 / 3.0, 5.0 / 3.0);\n  TestHelpers::EquationsOfState::check(\n      EquationsOfState::HybridEos<\n          EquationsOfState::PolytropicFluid<IsRelativistic>>{\n          EquationsOfState::PolytropicFluid<IsRelativistic>{100.0, 4.0 / 3.0},\n          5.0 / 3.0},\n      \"HybridEos\", \"hybrid_polytrope\", dv_for_size, 100.0, 4.0 / 3.0,\n      5.0 / 3.0);\n}\n\ntemplate <bool IsRelativistic>\nvoid check_exact_polytrope() {\n  EquationsOfState::PolytropicFluid<IsRelativistic> cold_eos{3.0, 2.0};\n  const Scalar<double> rho{4.0};\n  const auto p_c = cold_eos.pressure_from_density(rho);\n  CHECK(get(p_c) == 48.0);\n  const auto eps_c = cold_eos.specific_internal_energy_from_density(rho);\n  CHECK(get(eps_c) == 12.0);\n  const auto h_c = IsRelativistic\n                       ? hydro::relativistic_specific_enthalpy(rho, eps_c, p_c)\n                       : Scalar<double>{get(eps_c) + get(p_c) / get(rho)};\n  CHECK(get(h_c) == (IsRelativistic ? 25.0 : 24.0));\n  const auto chi_c = cold_eos.chi_from_density(rho);\n  CHECK(get(chi_c) == 24.0);\n  const auto p_c_kappa_c_over_rho_sq =\n      cold_eos.kappa_times_p_over_rho_squared_from_density(rho);\n  CHECK(get(p_c_kappa_c_over_rho_sq) == 0.0);\n  const auto c_s_sq = (get(chi_c) + get(p_c_kappa_c_over_rho_sq)) / get(h_c);\n  CHECK(c_s_sq == (IsRelativistic ? 0.96 : 1.0));\n  EquationsOfState::HybridEos<EquationsOfState::PolytropicFluid<IsRelativistic>>\n      eos{cold_eos, 1.5};\n  TestHelpers::EquationsOfState::test_get_clone(eos);\n\n  const EquationsOfState::HybridEos<EquationsOfState::PolytropicFluid<true>>\n      other_eos{{100.0, 2.0}, 1.4};\n  const auto other_type_eos =\n      EquationsOfState::PolytropicFluid<true>{100.0, 2.0};\n  CHECK(eos == eos);\n  CHECK(eos != other_eos);\n  CHECK(eos != other_type_eos);\n  CHECK(*eos.promote_to_3d_eos() ==\n        EquationsOfState::Equilibrium3D<EquationsOfState::HybridEos<\n            EquationsOfState::PolytropicFluid<IsRelativistic>>>(eos));\n  const Scalar<double> eps{18.0};\n  const auto p = eos.pressure_from_density_and_energy(rho, eps);\n  CHECK(get(p) == 60.0);\n  const auto h = IsRelativistic\n                     ? hydro::relativistic_specific_enthalpy(rho, eps, p)\n                     : Scalar<double>{get(eps) + get(p) / get(rho)};\n  CHECK(get(h) == (IsRelativistic ? 34.0 : 33.0));\n  CHECK(get(eos.pressure_from_density_and_enthalpy(rho, h)) == 60.0);\n  CHECK(get(eos.specific_internal_energy_from_density_and_pressure(rho, p)) ==\n        get(eps));\n  const auto chi = eos.chi_from_density_and_energy(rho, eps);\n  CHECK(get(chi) == 21.0);\n  const auto p_kappa_over_rho_sq =\n      eos.kappa_times_p_over_rho_squared_from_density_and_energy(rho, eps);\n  CHECK(get(p_kappa_over_rho_sq) == 7.5);\n  CHECK(not eos.is_barotropic());\n}\n\ntemplate <bool IsRelativistic>\nvoid check_bounds() {\n  const auto cold_eos =\n      EquationsOfState::PolytropicFluid<IsRelativistic>{100.0, 1.5};\n  const EquationsOfState::HybridEos<\n      EquationsOfState::PolytropicFluid<IsRelativistic>>\n      eos{cold_eos, 1.5};\n  CHECK(0.0 == eos.rest_mass_density_lower_bound());\n  CHECK(200.0 == eos.specific_internal_energy_lower_bound(1.0));\n  if constexpr (IsRelativistic) {\n    CHECK(1.0 == eos.specific_enthalpy_lower_bound());\n  } else {\n    CHECK(0.0 == eos.specific_enthalpy_lower_bound());\n  }\n  const double max_double = std::numeric_limits<double>::max();\n  CHECK(max_double == eos.rest_mass_density_upper_bound());\n  CHECK(max_double == eos.specific_internal_energy_upper_bound(1.0));\n  CHECK(eos.baryon_mass() ==\n        approx(hydro::units::geometric::default_baryon_mass));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.HybridEos\",\n                  \"[Unit][EquationsOfState]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Hydro/EquationsOfState/\"};\n  check_random_polytrope<true>();\n  check_random_polytrope<false>();\n  check_exact_polytrope<true>();\n  check_exact_polytrope<false>();\n  check_bounds<true>();\n  check_bounds<false>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_IdealFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Equilibrium3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\n// check that pressure equals energy density at upper bound of specific\n// internal energy for adiabatic index > 2\nvoid check_dominant_energy_condition_at_bound() {\n  const auto seed = std::random_device{}();\n  MAKE_GENERATOR(generator, seed);\n  CAPTURE(seed);\n  auto distribution = std::uniform_real_distribution<>{2.0, 3.0};\n  const double adiabatic_index = distribution(generator);\n  const double rest_mass_density = distribution(generator);\n  const auto eos = EquationsOfState::IdealFluid<true>{adiabatic_index};\n  const double specific_internal_energy =\n      eos.specific_internal_energy_upper_bound(rest_mass_density);\n  const double pressure = get(eos.pressure_from_density_and_energy(\n      Scalar<double>{rest_mass_density},\n      Scalar<double>{specific_internal_energy}));\n  const double energy_density =\n      rest_mass_density * (1.0 + specific_internal_energy);\n  CAPTURE(rest_mass_density);\n  CAPTURE(specific_internal_energy);\n  CHECK(approx(pressure) == energy_density);\n}\n\ntemplate <bool IsRelativistic, bool NonZeroMinTemp>\nvoid check_bounds() {\n  auto min_temperature = 0.0;\n  if constexpr (NonZeroMinTemp) {\n    const auto seed = std::random_device{}();\n    MAKE_GENERATOR(generator, seed);\n    CAPTURE(seed);\n    auto distribution = std::uniform_real_distribution<>{1.e-15, 1.e-5};\n    min_temperature = distribution(generator);\n  }\n\n  const auto adiabatic_index = 1.5;\n  const auto eos = EquationsOfState::IdealFluid<IsRelativistic>{\n      adiabatic_index, min_temperature};\n  CAPTURE(adiabatic_index);\n  CAPTURE(min_temperature);\n\n  CHECK(0.0 == eos.rest_mass_density_lower_bound());\n  CHECK(min_temperature == eos.temperature_lower_bound());\n  CHECK(min_temperature / (adiabatic_index - 1) ==\n        eos.specific_internal_energy_lower_bound(1.0));\n  if constexpr (IsRelativistic) {\n    CHECK(1.0 + (adiabatic_index * min_temperature) / (adiabatic_index - 1.0) ==\n          eos.specific_enthalpy_lower_bound());\n  } else {\n    CHECK((adiabatic_index * min_temperature) / (adiabatic_index - 1.0) ==\n          eos.specific_enthalpy_lower_bound());\n  }\n  const double max_double = std::numeric_limits<double>::max();\n  CHECK(max_double == eos.rest_mass_density_upper_bound());\n  CHECK(max_double == eos.specific_internal_energy_upper_bound(1.0));\n  CHECK(eos.baryon_mass() ==\n        approx(hydro::units::geometric::default_baryon_mass));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.IdealFluid\",\n                  \"[Unit][EquationsOfState]\") {\n  namespace EoS = EquationsOfState;\n  register_derived_classes_with_charm<EoS::EquationOfState<true, 2>>();\n  register_derived_classes_with_charm<EoS::EquationOfState<false, 2>>();\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Hydro/EquationsOfState/\"};\n  const double d_for_size = std::numeric_limits<double>::signaling_NaN();\n  const DataVector dv_for_size(5);\n  TestHelpers::EquationsOfState::test_get_clone(\n      EoS::IdealFluid<true>(5.0 / 3.0));\n  const auto eos = EoS::IdealFluid<true>{5.0 / 3.0};\n  const auto other_eos = EoS::IdealFluid<true>{4.0 / 3.0};\n  const auto other_type_eos = EoS::PolytropicFluid<true>{100.0, 2.0};\n  CHECK(eos == eos);\n  CHECK(eos != other_eos);\n  CHECK(eos != other_type_eos);\n  CHECK(*eos.promote_to_3d_eos() ==\n        EoS::Equilibrium3D<EoS::IdealFluid<true>>(eos));\n  CHECK(not eos.is_barotropic());\n  CHECK(not other_eos.is_barotropic());\n\n  TestHelpers::EquationsOfState::check(EoS::IdealFluid<true>{5.0 / 3.0},\n                                       \"IdealFluid\", \"ideal_fluid\", d_for_size,\n                                       5.0 / 3.0);\n  TestHelpers::EquationsOfState::check(EoS::IdealFluid<true>{4.0 / 3.0},\n                                       \"IdealFluid\", \"ideal_fluid\", dv_for_size,\n                                       4.0 / 3.0);\n  TestHelpers::EquationsOfState::check(EoS::IdealFluid<false>{5.0 / 3.0},\n                                       \"IdealFluid\", \"ideal_fluid\", d_for_size,\n                                       5.0 / 3.0);\n  TestHelpers::EquationsOfState::check(EoS::IdealFluid<false>{4.0 / 3.0},\n                                       \"IdealFluid\", \"ideal_fluid\", dv_for_size,\n                                       4.0 / 3.0);\n\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 2>>>(\n          {\"IdealFluid:\\n\"\n           \"  AdiabaticIndex: 1.6666666666666667\\n\"\n           \"  MinTemperature: 0.0\\n\"}),\n      \"IdealFluid\", \"ideal_fluid\", d_for_size, 5.0 / 3.0);\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 2>>>(\n          {\"IdealFluid:\\n\"\n           \"  AdiabaticIndex: 1.3333333333333333\\n\"\n           \"  MinTemperature: 0.0\\n\"}),\n      \"IdealFluid\", \"ideal_fluid\", dv_for_size, 4.0 / 3.0);\n\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<false, 2>>>(\n          {\"IdealFluid:\\n\"\n           \"  AdiabaticIndex: 1.6666666666666667\\n\"\n           \"  MinTemperature: 0.0\\n\"}),\n      \"IdealFluid\", \"ideal_fluid\", d_for_size, 5.0 / 3.0);\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<false, 2>>>(\n          {\"IdealFluid:\\n\"\n           \"  AdiabaticIndex: 1.3333333333333333\\n\"\n           \"  MinTemperature: 0.0\\n\"}),\n      \"IdealFluid\", \"ideal_fluid\", dv_for_size, 4.0 / 3.0);\n\n  check_bounds<true, true>();\n  check_bounds<true, false>();\n  check_bounds<false, true>();\n  check_bounds<false, false>();\n  check_dominant_energy_condition_at_bound();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_PiecewisePolytropicFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PiecewisePolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\n// parts of PiecewisePolytropicFluid\n// choose high or low constants/exponents based on transition\n// equate transition density, hi/lo constant/exponent\n// pressure_from_density_impl\n\nnamespace {\n\n// Calculate high density polytropic constant, based on low density constant\n// and exponents.  This is derived from enforcing pressure continuity at\n// transition density.\ndouble calc_Khi(const double transition_density, const double poly_constant_lo,\n                const double poly_exponent_lo, const double poly_exponent_hi) {\n  return poly_constant_lo *\n         pow(transition_density, poly_exponent_lo - poly_exponent_hi);\n}\n\n// Check solutions for datavector inputs\ntemplate <bool IsRelativistic>\nvoid check_exact() {\n  const double transition_density = 10.0;\n  const double polytropic_constant_lo = 0.5;\n  const double polytropic_exponent_lo = 1.5;\n  const double polytropic_exponent_hi = 2.0;\n\n  const auto eos = EquationsOfState::PiecewisePolytropicFluid<IsRelativistic>{\n      transition_density, polytropic_constant_lo, polytropic_exponent_lo,\n      polytropic_exponent_hi};\n\n  // Khi = Klo * pow (rho_transition,gamma_lo - gamma_hi)\n  const double polytropic_constant_hi =\n      polytropic_constant_lo *\n      pow(transition_density, polytropic_exponent_lo - polytropic_exponent_hi);\n\n  // tests of DataVector type functions\n  {\n    const Scalar<DataVector> rho{DataVector{0.5 * transition_density,\n                                            transition_density,\n                                            2.0 * transition_density}};\n    // pressure\n    const Scalar<DataVector> p = eos.pressure_from_density(rho);\n    const Scalar<DataVector> p_expected{\n        DataVector{polytropic_constant_lo *\n                       pow(0.5 * transition_density, polytropic_exponent_lo),\n                   polytropic_constant_lo *\n                       pow(transition_density, polytropic_exponent_lo),\n                   polytropic_constant_hi *\n                       pow(2.0 * transition_density, polytropic_exponent_hi)}};\n    CHECK(p == p_expected);\n\n    // eint\n    const Scalar<DataVector> eint =\n        eos.specific_internal_energy_from_density(rho);\n    const double eint_hi_constant =\n        (polytropic_exponent_hi - polytropic_exponent_lo) /\n        ((polytropic_exponent_hi - 1.0) * (polytropic_exponent_lo - 1.0)) *\n        polytropic_constant_lo *\n        pow(transition_density, polytropic_exponent_lo - 1.0);\n    const Scalar<DataVector> eint_expected{DataVector{\n        polytropic_constant_lo / (polytropic_exponent_lo - 1.0) *\n            pow(0.5 * transition_density, polytropic_exponent_lo - 1.0),\n        polytropic_constant_lo / (polytropic_exponent_lo - 1.0) *\n            pow(transition_density, polytropic_exponent_lo - 1.0),\n        polytropic_constant_hi / (polytropic_exponent_hi - 1.0) *\n                pow(2.0 * transition_density, polytropic_exponent_hi - 1.0) +\n            eint_hi_constant}};\n    CHECK_ITERABLE_APPROX(eint, eint_expected);\n\n    // chi\n    // Note the sound speeds at the transition density are different depending\n    // on which pair of constant/exponent is picked.  Here the hi constant &\n    // exponent must be chosen to match choose_polytropic_properties() in\n    // PiecewisePolytropicFluid.cpp\n    const Scalar<DataVector> chi = eos.chi_from_density(rho);\n    const Scalar<DataVector> chi_expected{DataVector{\n        polytropic_constant_lo * polytropic_exponent_lo *\n            pow(0.5 * transition_density, polytropic_exponent_lo - 1.0),\n        polytropic_constant_hi * polytropic_exponent_hi *\n            pow(transition_density, polytropic_exponent_hi - 1.0),\n        polytropic_constant_hi * polytropic_exponent_hi *\n            pow(2.0 * transition_density, polytropic_exponent_hi - 1.0)}};\n    CHECK(chi == chi_expected);\n\n    // kappa\n    const Scalar<DataVector> kappa_x_p_over_rho_sq_expected{\n        DataVector{0.0, 0.0, 0.0}};\n    const auto kappa_x_p_over_rho_sq =\n        eos.kappa_times_p_over_rho_squared_from_density(rho);\n    CHECK(kappa_x_p_over_rho_sq == kappa_x_p_over_rho_sq_expected);\n\n    // rho from h\n    if (IsRelativistic) {\n      const Scalar<DataVector> spec_enthalpy{\n          DataVector{1.0 + get_element(get(eint), 0) +\n                         get_element(get(p), 0) / get_element(get(rho), 0),\n                     1.0 + get_element(get(eint), 1) +\n                         get_element(get(p), 1) / get_element(get(rho), 1),\n                     1.0 + get_element(get(eint), 2) +\n                         get_element(get(p), 2) / get_element(get(rho), 2)}};\n      const auto rho_from_enthalpy =\n          eos.rest_mass_density_from_enthalpy(spec_enthalpy);\n      CHECK_ITERABLE_APPROX(rho, rho_from_enthalpy);\n    } else {\n      const Scalar<DataVector> spec_enthalpy{\n          DataVector{get_element(get(eint), 0) +\n                         get_element(get(p), 0) / get_element(get(rho), 0),\n                     get_element(get(eint), 1) +\n                         get_element(get(p), 1) / get_element(get(rho), 1),\n                     get_element(get(eint), 2) +\n                         get_element(get(p), 2) / get_element(get(rho), 2)}};\n      const auto rho_from_enthalpy =\n          eos.rest_mass_density_from_enthalpy(spec_enthalpy);\n      CHECK_ITERABLE_APPROX(rho, rho_from_enthalpy);\n    }\n  }\n\n  // tests of double type functions\n  {\n    // pressure\n    const Scalar<double> rho{1.2 * transition_density};\n    const auto p = eos.pressure_from_density(rho);\n    const double p_expected =\n        polytropic_constant_hi * pow(get(rho), polytropic_exponent_hi);\n    CHECK(get(p) == p_expected);\n\n    // eint\n    const auto eint = eos.specific_internal_energy_from_density(rho);\n    const double eint_hi_constant =\n        (polytropic_exponent_hi - polytropic_exponent_lo) /\n        ((polytropic_exponent_hi - 1.0) * (polytropic_exponent_lo - 1.0)) *\n        polytropic_constant_lo *\n        pow(transition_density, polytropic_exponent_lo - 1.0);\n    const double eint_expected =\n        polytropic_constant_hi / (polytropic_exponent_hi - 1.0) *\n            pow(get(rho), polytropic_exponent_hi - 1.0) +\n        eint_hi_constant;\n    CHECK(get(eint) == approx(eint_expected));\n\n    // chi\n    const auto chi = eos.chi_from_density(rho);\n    const double chi_expected = polytropic_constant_hi *\n                                polytropic_exponent_hi *\n                                pow(get(rho), polytropic_exponent_hi - 1.0);\n    CHECK(get(chi) == chi_expected);\n\n    // kappa\n    const auto kappa_x_p_over_rho_sq =\n        eos.kappa_times_p_over_rho_squared_from_density(rho);\n    const double kappa_x_p_over_rho_sq_expected = 0.0;\n    CHECK(get(kappa_x_p_over_rho_sq) == kappa_x_p_over_rho_sq_expected);\n\n    // rho from h\n    double spec_enthalpy_constant = 0.0;\n    if (IsRelativistic) {\n      spec_enthalpy_constant = 1.0;\n    }\n\n    const Scalar<double> spec_enthalpy{spec_enthalpy_constant + get(eint) +\n                                       get(p) / get(rho)};\n\n    const auto rho_from_enthalpy =\n        eos.rest_mass_density_from_enthalpy(spec_enthalpy);\n    CHECK_ITERABLE_APPROX(get(rho), get(rho_from_enthalpy));\n  }\n}\n\ntemplate <bool IsRelativistic>\nvoid check_edge_cases() {\n  // Two edge cases checked are:\n  // * The piecewise polytrope (with identical exponents) matches the single\n  // polytrope case\n  //\n  // * The rest mass density at the transition density matches using both the\n  //  high and low density constants/exponents\n\n  const double transition_density = 10.0;\n  const double polytropic_constant = 0.5;\n  const double polytropic_exponent = 1.5;\n\n  const Scalar<double> rest_mass_density_high{1.1 * transition_density};\n  const Scalar<double> rest_mass_density_transition{transition_density};\n  const Scalar<double> rest_mass_density_low{0.9 * transition_density};\n\n  const auto eos = EquationsOfState::PiecewisePolytropicFluid<IsRelativistic>{\n      transition_density, polytropic_constant, 0.9 * polytropic_exponent,\n      1.1 * polytropic_exponent};\n\n  // At the transition density, this EoS defaults to using the high density\n  // constants and exponents to calculate remaining thermodynamic quantities.\n  // Here check that eint matches at the transition density using low density\n  // constants and exponents as well.\n  const double transition_pressure =\n      polytropic_constant * pow(transition_density, 0.9 * polytropic_exponent);\n\n  const double transition_spec_eint_low_params =\n      transition_pressure /\n      ((0.9 * polytropic_exponent - 1.0) * transition_density);\n\n  CHECK(get(eos.specific_internal_energy_from_density(\n            rest_mass_density_transition)) ==\n        approx(transition_spec_eint_low_params));\n\n  // Compare single and piecewise polytrope with same parameters\n  const auto eos_piecewise_polytrope =\n      EquationsOfState::PiecewisePolytropicFluid<IsRelativistic>{\n          transition_density, polytropic_constant, polytropic_exponent,\n          polytropic_exponent};\n  const auto eos_single_polytrope =\n      EquationsOfState::PolytropicFluid<IsRelativistic>{polytropic_constant,\n                                                        polytropic_exponent};\n  // Compare single and identical piecewise polytrope\n  CHECK(get(eos_piecewise_polytrope.specific_internal_energy_from_density(\n            rest_mass_density_low)) ==\n        get(eos_single_polytrope.specific_internal_energy_from_density(\n            rest_mass_density_low)));\n  CHECK(get(eos_piecewise_polytrope.specific_internal_energy_from_density(\n            rest_mass_density_high)) ==\n        get(eos_single_polytrope.specific_internal_energy_from_density(\n            rest_mass_density_high)));\n  CHECK(get(eos_piecewise_polytrope.chi_from_density(rest_mass_density_low)) ==\n        get(eos_single_polytrope.chi_from_density(rest_mass_density_low)));\n  CHECK(get(eos_piecewise_polytrope.chi_from_density(rest_mass_density_high)) ==\n        get(eos_single_polytrope.chi_from_density(rest_mass_density_high)));\n}\n\nvoid check_dominant_energy_condition_at_bound() {\n  MAKE_GENERATOR(generator);\n  auto distribution = std::uniform_real_distribution<>{2.0, 3.0};  //[a,b)\n  const double polytropic_exponent = distribution(generator);\n  const double transition_density = 1.0;\n  const double polytropic_constant = 0.5;\n  const auto eos = EquationsOfState::PiecewisePolytropicFluid<true>{\n      transition_density, 0.5 * polytropic_constant, 0.5 * polytropic_exponent,\n      polytropic_exponent};\n  const Scalar<double> rest_mass_density{eos.rest_mass_density_upper_bound()};\n  const double specific_internal_energy =\n      get(eos.specific_internal_energy_from_density(rest_mass_density));\n  const double pressure =\n      get(eos.pressure_from_density(Scalar<double>{rest_mass_density}));\n  const double energy_density =\n      get(rest_mass_density) * (1.0 + specific_internal_energy);\n  CAPTURE(rest_mass_density);\n  CAPTURE(specific_internal_energy);\n  CHECK(approx(pressure) == energy_density);\n}\n\ntemplate <bool IsRelativistic>\nvoid check_bounds() {\n  const double transition_density = 10.0;\n  const double polytropic_constant = 0.5;\n  const double polytropic_exponent = 1.5;\n  const auto eos = EquationsOfState::PiecewisePolytropicFluid<IsRelativistic>{\n      transition_density, 0.5 * polytropic_constant, 0.5 * polytropic_exponent,\n      1.0 * polytropic_exponent};\n  CHECK(0.0 == eos.rest_mass_density_lower_bound());\n  CHECK(0.0 == eos.specific_internal_energy_lower_bound());\n  if constexpr (IsRelativistic) {\n    CHECK(1.0 == eos.specific_enthalpy_lower_bound());\n  } else {\n    CHECK(0.0 == eos.specific_enthalpy_lower_bound());\n  }\n  const double max_double = std::numeric_limits<double>::max();\n  CHECK(max_double == eos.rest_mass_density_upper_bound());\n  CHECK(max_double == eos.specific_internal_energy_upper_bound());\n\n  const auto eos_high_gamma =\n      EquationsOfState::PiecewisePolytropicFluid<IsRelativistic>{\n          transition_density, 0.5 * polytropic_constant,\n          0.5 * polytropic_exponent, 2.0 * polytropic_exponent};\n  double density_upper_limit = max_double;\n\n  if constexpr (IsRelativistic) {\n    const double polytropic_constant_hi =\n        calc_Khi(transition_density, 0.5 * polytropic_constant,\n                 0.5 * polytropic_exponent, 2.0 * polytropic_exponent);\n\n    const double boundary_constant =\n        (2.0 * polytropic_exponent - 0.5 * polytropic_exponent) * 0.5 *\n        polytropic_constant /\n        ((2.0 * polytropic_exponent - 1.0) *\n         (0.5 * polytropic_exponent - 1.0)) *\n        pow(transition_density, 0.5 * polytropic_exponent - 1.0);\n\n    density_upper_limit =\n        pow((2.0 * polytropic_exponent - 1.0) /\n                (polytropic_constant_hi * (2.0 * polytropic_exponent - 2.0)) *\n                (1.0 + boundary_constant),\n            1.0 / (2.0 * polytropic_exponent - 1.0));\n  }\n  CHECK(density_upper_limit == eos_high_gamma.rest_mass_density_upper_bound());\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.EquationsOfState.PiecewisePolytropicFluid\",\n    \"[Unit][EquationsOfState]\") {\n  namespace EoS = EquationsOfState;\n  register_derived_classes_with_charm<EoS::EquationOfState<true, 1>>();\n  register_derived_classes_with_charm<EoS::EquationOfState<false, 1>>();\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Hydro/EquationsOfState/\"};\n  TestHelpers::EquationsOfState::test_get_clone(\n      EoS::PiecewisePolytropicFluid<true>(10.0, 0.5, 1.5, 2.0));\n\n  const auto eos = EoS::PiecewisePolytropicFluid<true>{10.0, 0.5, 1.5, 2.0};\n  const auto other_eos =\n      EoS::PiecewisePolytropicFluid<true>{10.0, 0.7, 1.5, 2.0};\n  const auto other_type_eos = EoS::DarkEnergyFluid<true>{0.5};\n  // Same parameters should match\n  CHECK(eos == eos);\n  // Different parameters should NOT match\n  CHECK(eos != other_eos);\n  // Different eos types should NOT match\n  CHECK(eos != other_type_eos);\n  // Check the correct 3D EoS is obtained\n  CHECK(*eos.promote_to_3d_eos() ==\n        EoS::Barotropic3D<EoS::PiecewisePolytropicFluid<true>>(eos));\n  // Check the correct 2D EoS is obtained\n  CHECK(*eos.promote_to_2d_eos() ==\n        EoS::Barotropic2D<EoS::PiecewisePolytropicFluid<true>>(eos));\n  // Check baryon density\n  CHECK(eos.baryon_mass() ==\n        approx(hydro::units::geometric::default_baryon_mass));\n  const double d_for_size = std::numeric_limits<double>::signaling_NaN();\n  const DataVector dv_for_size(5);\n  const double transition_density = 10.0;\n  const double poly_exponent_lo = 1.5;\n  const double poly_constant_lo = 10.0;\n  const double poly_exponent_hi = 2.0;\n  //Check if barotropic\n  CHECK(eos.is_barotropic());\n\n  // Relativistic checks\n  TestHelpers::EquationsOfState::check(\n      EoS::PiecewisePolytropicFluid<true>{transition_density, poly_constant_lo,\n                                          poly_exponent_lo, poly_exponent_hi},\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, poly_exponent_lo, poly_exponent_hi);\n  TestHelpers::EquationsOfState::check(\n      EoS::PiecewisePolytropicFluid<true>{transition_density,\n                                          1.1 * poly_constant_lo,\n                                          poly_exponent_lo, poly_exponent_hi},\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, 1.1 * poly_constant_lo, poly_exponent_lo,\n      poly_exponent_hi);\n\n  TestHelpers::EquationsOfState::check(\n      EoS::PiecewisePolytropicFluid<true>{transition_density, poly_constant_lo,\n                                          1.2 * poly_exponent_lo,\n                                          poly_exponent_hi},\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, 1.2 * poly_exponent_lo,\n      poly_exponent_hi);\n  TestHelpers::EquationsOfState::check(\n      EoS::PiecewisePolytropicFluid<true>{transition_density, poly_constant_lo,\n                                          poly_exponent_lo,\n                                          1.4 * poly_exponent_hi},\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, poly_exponent_lo,\n      1.4 * poly_exponent_hi);\n\n  // Non relativistic checks\n  TestHelpers::EquationsOfState::check(\n      EoS::PiecewisePolytropicFluid<false>{transition_density, poly_constant_lo,\n                                           poly_exponent_lo, poly_exponent_hi},\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, poly_exponent_lo, poly_exponent_hi);\n  TestHelpers::EquationsOfState::check(\n      EoS::PiecewisePolytropicFluid<false>{transition_density,\n                                           1.1 * poly_constant_lo,\n                                           poly_exponent_lo, poly_exponent_hi},\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, 1.1 * poly_constant_lo, poly_exponent_lo,\n      poly_exponent_hi);\n  TestHelpers::EquationsOfState::check(\n      EoS::PiecewisePolytropicFluid<false>{transition_density, poly_constant_lo,\n                                           1.2 * poly_exponent_lo,\n                                           poly_exponent_hi},\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, 1.2 * poly_exponent_lo,\n      poly_exponent_hi);\n  TestHelpers::EquationsOfState::check(\n      EoS::PiecewisePolytropicFluid<false>{transition_density, poly_constant_lo,\n                                           poly_exponent_lo,\n                                           1.4 * poly_exponent_hi},\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, poly_exponent_lo,\n      1.4 * poly_exponent_hi);\n\n  // Relativistic\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 1>>>(\n          {\"PiecewisePolytropicFluid:\\n\"\n           \"  PiecewisePolytropicTransitionDensity: \" +\n           std::to_string(transition_density) +\n           \"\\n\"\n           \"  PolytropicConstantLow: \" +\n           std::to_string(poly_constant_lo) +\n           \"\\n\"\n           \"  PolytropicExponentLow: \" +\n           std::to_string(poly_exponent_lo) +\n           \"\\n\"\n           \"  PolytropicExponentHigh: \" +\n           std::to_string(poly_exponent_hi) + \"\\n\"}),\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, poly_exponent_lo, poly_exponent_hi);\n\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 1>>>(\n          {\"PiecewisePolytropicFluid:\\n\"\n           \"  PiecewisePolytropicTransitionDensity: \" +\n           std::to_string(transition_density) +\n           \"\\n\"\n           \"  PolytropicConstantLow: \" +\n           std::to_string(poly_constant_lo) +\n           \"\\n\"\n           \"  PolytropicExponentLow: \" +\n           std::to_string(poly_exponent_lo) +\n           \"\\n\"\n           \"  PolytropicExponentHigh: \" +\n           std::to_string(1.5 * poly_exponent_hi) + \"\\n\"}),\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, poly_exponent_lo,\n      1.5 * poly_exponent_hi);\n\n  // Nonrelativistic\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<false, 1>>>(\n          {\"PiecewisePolytropicFluid:\\n\"\n           \"  PiecewisePolytropicTransitionDensity: \" +\n           std::to_string(transition_density) +\n           \"\\n\"\n           \"  PolytropicConstantLow: \" +\n           std::to_string(poly_constant_lo) +\n           \"\\n\"\n           \"  PolytropicExponentLow: \" +\n           std::to_string(poly_exponent_lo) +\n           \"\\n\"\n           \"  PolytropicExponentHigh: \" +\n           std::to_string(poly_exponent_hi) + \"\\n\"}),\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, poly_exponent_lo, poly_exponent_hi);\n\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<false, 1>>>(\n          {\"PiecewisePolytropicFluid:\\n\"\n           \"  PiecewisePolytropicTransitionDensity: \" +\n           std::to_string(transition_density) +\n           \"\\n\"\n           \"  PolytropicConstantLow: \" +\n           std::to_string(poly_constant_lo) +\n           \"\\n\"\n           \"  PolytropicExponentLow: \" +\n           std::to_string(poly_exponent_lo) +\n           \"\\n\"\n           \"  PolytropicExponentHigh: \" +\n           std::to_string(1.2 * poly_exponent_hi) + \"\\n\"}),\n      \"PiecewisePolytropicFluid\", \"piecewisepolytropic\", d_for_size,\n      transition_density, poly_constant_lo, poly_exponent_lo,\n      1.2 * poly_exponent_hi);\n\n  check_bounds<true>();\n  check_bounds<false>();\n  check_dominant_energy_condition_at_bound();\n  check_edge_cases<true>();\n  check_edge_cases<false>();\n  check_exact<true>();\n  check_exact<false>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_PolytropicFluid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\n// check that pressure equals energy density at upper bound of specific\n// internal energy for adiabatic index > 2\nvoid check_dominant_energy_condition_at_bound() {\n  const auto seed = std::random_device{}();\n  MAKE_GENERATOR(generator, seed);\n  CAPTURE(seed);\n  auto distribution = std::uniform_real_distribution<>{2.0, 3.0};\n  const double polytropic_exponent = distribution(generator);\n  const auto eos =\n      EquationsOfState::PolytropicFluid<true>{100.0, polytropic_exponent};\n  const Scalar<double> rest_mass_density{eos.rest_mass_density_upper_bound()};\n  const double specific_internal_energy =\n      get(eos.specific_internal_energy_from_density(rest_mass_density));\n  const double pressure =\n      get(eos.pressure_from_density(Scalar<double>{rest_mass_density}));\n  const double energy_density =\n      get(rest_mass_density) * (1.0 + specific_internal_energy);\n  CAPTURE(rest_mass_density);\n  CAPTURE(specific_internal_energy);\n  CHECK(approx(pressure) == energy_density);\n}\n\ntemplate <bool IsRelativistic>\nvoid check_bounds() {\n  const auto eos =\n      EquationsOfState::PolytropicFluid<IsRelativistic>{100.0, 1.5};\n  CHECK(0.0 == eos.rest_mass_density_lower_bound());\n  CHECK(0.0 == eos.specific_internal_energy_lower_bound());\n  if constexpr (IsRelativistic) {\n    CHECK(1.0 == eos.specific_enthalpy_lower_bound());\n  } else {\n    CHECK(0.0 == eos.specific_enthalpy_lower_bound());\n  }\n  const double max_double = std::numeric_limits<double>::max();\n  CHECK(max_double == eos.rest_mass_density_upper_bound());\n  CHECK(max_double == eos.specific_internal_energy_upper_bound());\n  CHECK(eos.baryon_mass() ==\n        approx(hydro::units::geometric::default_baryon_mass));\n}\n\n}  // namespace\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.PolytropicFluid\",\n                  \"[Unit][EquationsOfState]\") {\n  namespace EoS = EquationsOfState;\n  register_derived_classes_with_charm<EoS::EquationOfState<true, 1>>();\n  register_derived_classes_with_charm<EoS::EquationOfState<false, 1>>();\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Hydro/EquationsOfState/\"};\n  TestHelpers::EquationsOfState::test_get_clone(\n      EoS::PolytropicFluid<true>(100.0, 2.0));\n\n  const auto eos = EoS::PolytropicFluid<true>{100.0, 2.0};\n  const auto other_eos = EoS::PolytropicFluid<true>{4.0, 2.0};\n  const auto other_type_eos = EoS::DarkEnergyFluid<true>{0.5};\n  CHECK(eos == eos);\n  CHECK(eos != other_eos);\n  CHECK(eos != other_type_eos);\n  CHECK(*eos.promote_to_3d_eos() ==\n        EoS::Barotropic3D<EoS::PolytropicFluid<true>>(eos));\n  CHECK(*eos.promote_to_2d_eos() ==\n        EoS::Barotropic2D<EoS::PolytropicFluid<true>>(eos));\n  CHECK(eos.is_barotropic());\n  const double d_for_size = std::numeric_limits<double>::signaling_NaN();\n  const DataVector dv_for_size(5);\n  TestHelpers::EquationsOfState::check(EoS::PolytropicFluid<true>{100.0, 2.0},\n                                       \"PolytropicFluid\", \"polytropic\",\n                                       d_for_size, 100.0, 2.0);\n  TestHelpers::EquationsOfState::check(EoS::PolytropicFluid<true>{134.0, 1.5},\n                                       \"PolytropicFluid\", \"polytropic\",\n                                       dv_for_size, 134.0, 1.5);\n  TestHelpers::EquationsOfState::check(EoS::PolytropicFluid<false>{121.0, 1.2},\n                                       \"PolytropicFluid\", \"polytropic\",\n                                       d_for_size, 121.0, 1.2);\n  TestHelpers::EquationsOfState::check(EoS::PolytropicFluid<false>{117.0, 1.12},\n                                       \"PolytropicFluid\", \"polytropic\",\n                                       dv_for_size, 117.0, 1.12);\n\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 1>>>(\n          {\"PolytropicFluid:\\n\"\n           \"  PolytropicConstant: 100.0\\n\"\n           \"  PolytropicExponent: 2.0\\n\"}),\n      \"PolytropicFluid\", \"polytropic\", d_for_size, 100.0, 2.0);\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<true, 1>>>(\n          {\"PolytropicFluid:\\n\"\n           \"  PolytropicConstant: 134.0\\n\"\n           \"  PolytropicExponent: 1.5\\n\"}),\n      \"PolytropicFluid\", \"polytropic\", dv_for_size, 134.0, 1.5);\n\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<false, 1>>>(\n          {\"PolytropicFluid:\\n\"\n           \"  PolytropicConstant: 121.0\\n\"\n           \"  PolytropicExponent: 1.2\\n\"}),\n      \"PolytropicFluid\", \"polytropic\", d_for_size, 121.0, 1.2);\n  TestHelpers::EquationsOfState::check(\n      TestHelpers::test_creation<\n          std::unique_ptr<EoS::EquationOfState<false, 1>>>(\n          {\"PolytropicFluid:\\n\"\n           \"  PolytropicConstant: 117.0\\n\"\n           \"  PolytropicExponent: 1.12\\n\"}),\n      \"PolytropicFluid\", \"polytropic\", dv_for_size, 117.0, 1.12);\n\n  check_bounds<true>();\n  check_bounds<false>();\n  check_dominant_energy_condition_at_bound();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_SpectralEoS.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic2D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Barotropic3D.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Spectral.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\n\nvoid check_exact() {\n  // Test creation\n  namespace EoS = EquationsOfState;\n  const std::vector coefs = {3.0, 0.25, 0.375, 0.5};\n  TestHelpers::test_creation<std::unique_ptr<EoS::EquationOfState<true, 1>>>(\n      {\"Spectral:\\n\"\n       \"  ReferenceDensity: 2.0\\n\"\n       \"  ReferencePressure: 4.0\\n\"\n       \"  Coefficients: [3.0,0.25,0.375,0.5]\\n\"\n       \"  UpperDensity: 15.0\\n\"});\n\n  EquationsOfState::Spectral eos(2.0, 4.0, {3.0, 0.25, 0.375, 0.5},\n                                 2.0 * exp(2.0));\n  TestHelpers::EquationsOfState::test_get_clone(eos);\n\n  EquationsOfState::Spectral other_eos(1.0, 4.0, {3.0, 0.25, 0.375, 0.5},\n                                       2.0 * exp(2.0));\n  const auto other_type_eos = EoS::PolytropicFluid<true>{100.0, 2.0};\n  CHECK(eos == eos);\n  CHECK(eos != other_eos);\n  CHECK(eos != other_type_eos);\n  CHECK(*eos.promote_to_3d_eos() ==\n        EquationsOfState::Barotropic3D<EquationsOfState::Spectral>(eos));\n  CHECK(*eos.promote_to_2d_eos() ==\n        EquationsOfState::Barotropic2D<EquationsOfState::Spectral>(eos));\n  CHECK(eos.is_barotropic());\n  // Test DataVector functions\n  {\n    const Scalar<DataVector> rho{DataVector{\n        2.0 * exp(-1.0), 2.0, 2.0 * exp(1.0), 2.0 * exp(2.0), 2.0 * exp(3.0)}};\n    const Scalar<DataVector> p = eos.pressure_from_density(rho);\n    INFO(rho);\n    INFO(p);\n    const Scalar<DataVector> p_expected{\n        DataVector{4.0 * exp(-3.0), 4.0, 4.0 * exp(3.375), 4.0 * exp(9.5),\n                   4.0 * exp(18.5)}};\n    CHECK(p == p_expected);\n    const auto eps_c = eos.specific_internal_energy_from_density(rho);\n    const Scalar<DataVector> eps_expected{\n        DataVector{exp(-2.0), 1.0, 8.519830698147826, 528.9617452597413,\n                   1347501.570212399}};\n    CHECK_ITERABLE_APPROX(eps_c, eps_expected);\n    const auto chi_c = eos.chi_from_density(rho);\n    const Scalar<DataVector> gamma{DataVector{3.0, 3.0, 4.125, 9.0, 9.0}};\n    const auto chi_expected = get(p_expected) * get(gamma) / get(rho);\n    CHECK_ITERABLE_APPROX(chi_expected, get(chi_c));\n    const Scalar<DataVector> p_c_kappa_c_over_rho_sq_expected{\n        DataVector{0.0, 0.0, 0.0, 0.0, 0.0}};\n    const auto p_c_kappa_c_over_rho_sq =\n        eos.kappa_times_p_over_rho_squared_from_density(rho);\n    CHECK_ITERABLE_APPROX(p_c_kappa_c_over_rho_sq_expected,\n                          p_c_kappa_c_over_rho_sq);\n    const auto rho_from_enthalpy = eos.rest_mass_density_from_enthalpy(\n        hydro::relativistic_specific_enthalpy(rho, eps_c, p));\n    CHECK_ITERABLE_APPROX(rho, rho_from_enthalpy);\n  }\n  // Test double functions\n  {\n    const Scalar<double> rho{2.0 * exp(1.0)};\n    const auto p = eos.pressure_from_density(rho);\n    const double p_expected = 4.0 * exp(3.375);\n    CHECK(get(p) == p_expected);\n    const auto eps = eos.specific_internal_energy_from_density(rho);\n    const double eps_expected = 8.519830698147826;\n    CHECK_ITERABLE_APPROX(get(eps), eps_expected);\n    const auto chi = eos.chi_from_density(rho);\n    const double chi_expected = p_expected * 4.125 / get(rho);\n    CHECK_ITERABLE_APPROX(chi_expected, get(chi));\n    const auto p_c_kappa_c_over_rho_sq =\n        eos.kappa_times_p_over_rho_squared_from_density(rho);\n    const double p_c_kappa_c_over_rho_sq_expected = 0.0;\n    CHECK_ITERABLE_APPROX(p_c_kappa_c_over_rho_sq_expected,\n                          get(p_c_kappa_c_over_rho_sq));\n    const auto rho_from_enthalpy = eos.rest_mass_density_from_enthalpy(\n        hydro::relativistic_specific_enthalpy(rho, eps, p));\n    CHECK_ITERABLE_APPROX(get(rho), get(rho_from_enthalpy));\n  }\n  // Test bounds\n  CHECK(0.0 == eos.rest_mass_density_lower_bound());\n  CHECK(0.0 == eos.specific_internal_energy_lower_bound());\n  CHECK(1.0 == eos.specific_enthalpy_lower_bound());\n  const double max_double = std::numeric_limits<double>::max();\n  CHECK(max_double == eos.rest_mass_density_upper_bound());\n  CHECK(max_double == eos.specific_internal_energy_upper_bound());\n  CHECK(eos.baryon_mass() ==\n        approx(hydro::units::geometric::default_baryon_mass));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.Spectral\",\n                  \"[Unit][EquationsOfState]\") {\n  check_exact();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/EquationsOfState/Test_Tabulated3D.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <pup.h>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/EquationsOfState/TestHelpers.hpp\"\n#include \"IO/Connectivity.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/EosTable.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.EquationsOfState.Tabulated3D\",\n                  \"[Unit][EquationsOfState]\") {\n  namespace EoS = EquationsOfState;\n\n  using TEoS = EoS::Tabulated3D<true>;\n\n  // We do not test proper registration, yet.\n  // Will do this once we have the EoS reader setup.\n  // For now, we will construct the internal state of\n  // the EoS manually.\n\n  // register_derived_classes_with_charm<\n  //     EoS::EquationOfState<true, 2>>();\n  // register_derived_classes_with_charm<\n  //     EoS::EquationOfState<false, 2>>();\n  // pypp::SetupLocalPythonEnvironment local_python_env{\n  //     \"PointwiseFunctions/Hydro/EquationsOfState/\"};\n  //\n  MAKE_GENERATOR(gen);\n\n  std::uniform_int_distribution<size_t> dist_int(3, 5);\n\n  constexpr int Dim = 3;\n  constexpr int NumVar = TEoS::NumberOfVars;\n\n  std::array<double, Dim> lower_bounds{{\n      -3 * std::log(10),  // TEMP\n      -5 * std::log(10),  // RHO\n      0.01                // YE\n  }};\n\n  std::array<double, Dim> upper_bounds{{1 * std::log(10),   // TEMP\n                                        -1 * std::log(10),  // RHO\n                                        0.5}};\n\n  // Create data data structures\n  std::array<std::vector<double>, Dim> X_data;\n  Index<Dim> num_x_points;\n\n  size_t total_num_points = 1;\n  for (size_t n = 0; n < Dim; ++n) {\n    num_x_points[n] = dist_int(gen);\n    total_num_points *= num_x_points[n];\n\n    X_data[n].resize(num_x_points[n]);\n\n    for (size_t m = 0; m < num_x_points[n]; m++) {\n      X_data[n][m] =\n          lower_bounds[n] + m * (upper_bounds[n] - lower_bounds[n]) /\n                                static_cast<double>(num_x_points[n] - 1);\n    }\n  }\n  CAPTURE(num_x_points);\n  CAPTURE(X_data);\n\n  // Correct for NumVars\n  total_num_points *= TEoS::NumberOfVars;\n  CAPTURE(total_num_points);\n\n  // Allocate storage for main table\n  std::vector<double> dependent_variables;\n  dependent_variables.resize(total_num_points);\n\n  // Fill dependent_variables\n  //\n  double energy_shift = 0;  // Will test this later\n\n  double eps_min = std::exp(lower_bounds[0]);\n  CAPTURE(eps_min);\n\n  if (eps_min < 0) {\n    energy_shift = 2. * eps_min;\n  }\n  CAPTURE(energy_shift);\n\n  auto test_eos = [&](auto state) {\n    enum TableIndex { Temp = 0, Rho = 1, Ye = 2 };\n\n    // We use a simple ideal fluid like EOS with a Ye variable Gamma:\n    // p = (rho*eps)*Ye = rho T\n\n    std::array<double, TEoS::NumberOfVars> vars;\n\n    // This is not consistent, but better keep this simple\n    vars[TEoS::Epsilon] = state[TableIndex::Temp];\n    vars[TEoS::Pressure] = state[TableIndex::Temp] + state[TableIndex::Rho];\n    vars[TEoS::CsSquared] = state[TableIndex::Ye];\n\n    return vars;\n  };\n\n  for (size_t ijk = 0; ijk < total_num_points / NumVar; ++ijk) {\n    Index<Dim> index;\n    auto tmp = std::array<double, Dim>{};\n\n    // Uncompress index\n    size_t myind = ijk;\n    for (size_t nn = 0; nn < Dim - 1; ++nn) {\n      index[nn] = myind % num_x_points[nn];\n      myind = (myind - index[nn]) / num_x_points[nn];\n      tmp[nn] = X_data[nn][index[nn]];\n    }\n    index[Dim - 1] = myind;\n    tmp[Dim - 1] = X_data[Dim - 1][index[Dim - 1]];\n\n    for (size_t nv = 0; nv < NumVar; ++nv) {\n      std::array<double, NumVar> Fx = test_eos(tmp);\n      dependent_variables[nv + NumVar * ijk] = Fx[nv];\n    }\n  }\n\n  // Construct EOS\n\n  double enthalpy_minimum = 1.;\n\n  TEoS eos = TEoS(X_data[2], X_data[1], X_data[0], dependent_variables,\n                  energy_shift, enthalpy_minimum);\n\n  CHECK(std::abs(std::exp(lower_bounds[0]) - eos.temperature_lower_bound()) <\n        1.e-12);\n  CHECK(std::abs(std::exp(lower_bounds[1]) -\n                 eos.rest_mass_density_lower_bound()) < 1.e-12);\n  CHECK(std::abs((lower_bounds[2]) - eos.electron_fraction_lower_bound()) <\n        1.e-12);\n\n  CHECK(std::abs(std::exp(upper_bounds[0]) - eos.temperature_upper_bound()) <\n        1.e-12);\n  CHECK(std::abs(std::exp(upper_bounds[1]) -\n                 eos.rest_mass_density_upper_bound()) < 1.e-12);\n  CHECK(std::abs((upper_bounds[2]) - eos.electron_fraction_upper_bound()) <\n        1.e-12);\n\n  // Construct a test state\n  std::array<double, 3> pure_state{{1., 1.e-3, 0.3}};\n\n  std::array<Scalar<double>, 3> state{};\n  std::array<Scalar<DataVector>, 3> vector_state{};\n\n  constexpr size_t data_vector_length = 5;\n\n  for (size_t n = 0; n < 3; ++n) {\n    get(gsl::at(state, n)) = gsl::at(pure_state, n);\n    get(gsl::at(vector_state, n)) =\n        DataVector{data_vector_length, gsl::at(pure_state, n)};\n  }\n\n  pure_state[0] = std::log(pure_state[0]);\n  pure_state[1] = std::log(pure_state[1]);\n  CAPTURE(pure_state);\n  CAPTURE(vector_state);\n\n  const auto output = test_eos(pure_state);\n  CAPTURE(output);\n\n  CHECK(std::abs((std::exp(output[TEoS::Epsilon]) + energy_shift) -\n                 get(eos.specific_internal_energy_from_density_and_temperature(\n                     state[1], state[0], state[2]))) < 1.e-12);\n  CHECK(std::abs((std::exp(output[TEoS::Pressure])) -\n                 get(eos.pressure_from_density_and_temperature(\n                     state[1], state[0], state[2]))) < 1.e-12);\n  CHECK(std::abs(output[TEoS::CsSquared]) -\n            get(eos.sound_speed_squared_from_density_and_temperature(\n                state[1], state[0], state[2])) <\n        1.e-12);\n  CHECK(not eos.is_barotropic());\n  CHECK(not eos.is_equilibrium());\n\n  const auto eps_interp =\n      eos.specific_internal_energy_from_density_and_temperature(\n          vector_state[1], vector_state[0], vector_state[2]);\n  CAPTURE(eps_interp);\n\n  // Ensure the tabulated EoS is applied to all elements in the datavector, not\n  // just the first element\n  for (size_t vector_index = 0; vector_index < data_vector_length;\n       vector_index++) {\n    CHECK(std::abs(std::exp(pure_state[0]) -\n                   get(eos.temperature_from_density_and_energy(\n                       vector_state[1], eps_interp,\n                       vector_state[2]))[vector_index]) < 1.e-12);\n  }\n\n  CHECK(std::abs((std::exp(output[TEoS::Epsilon]) + energy_shift) -\n                 get(eos.specific_internal_energy_from_density_and_temperature(\n                     vector_state[1], vector_state[0], vector_state[2]))[0]) <\n        1.e-12);\n  CHECK(std::abs((std::exp(output[TEoS::Pressure])) -\n                 get(eos.pressure_from_density_and_temperature(\n                     vector_state[1], vector_state[0], vector_state[2]))[0]) <\n        1.e-12);\n  CHECK(std::abs(output[TEoS::CsSquared] -\n                 get(eos.sound_speed_squared_from_density_and_temperature(\n                     vector_state[1], vector_state[0], vector_state[2]))[0]) <\n        1.e-12);\n\n  const auto eps_interp_vector =\n      eos.specific_internal_energy_from_density_and_temperature(\n          vector_state[1], vector_state[0], vector_state[2]);\n  CAPTURE(eps_interp_vector);\n\n  CHECK(std::abs(std::exp(pure_state[0]) -\n                 get(eos.temperature_from_density_and_energy(\n                     vector_state[1], eps_interp_vector, vector_state[2]))[0]) <\n        1.e-12);\n\n  auto test_against_reference_values = [&](auto& this_eos) {\n    get(state[1]) = 1.e-4;\n\n    CHECK_ITERABLE_APPROX(\n        get(this_eos.specific_internal_energy_from_density_and_temperature(\n            state[1], state[0], state[2])),\n        0.30204636358732767);\n    CHECK_ITERABLE_APPROX(get(this_eos.pressure_from_density_and_temperature(\n                              state[1], state[0], state[2])),\n                          0.00001103280164124);\n    CHECK_ITERABLE_APPROX(\n        get(this_eos.sound_speed_squared_from_density_and_temperature(\n            state[1], state[0], state[2])),\n        0.41669901507784435);\n  };\n\n  // Test against reference values\n\n  std::string h5_file_name{\n      unit_test_src_path() +\n      \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n\n  h5::H5File<h5::AccessType::ReadOnly> eos_file{h5_file_name};\n  const auto& compose_eos = eos_file.get<h5::EosTable>(\"/dd2\");\n\n  eos.initialize(compose_eos);\n\n  test_against_reference_values(eos);\n\n  // Test serialization\n\n  register_derived_classes_with_charm<EoS::EquationOfState<true, 3>>();\n  const auto eos_pointer =\n      serialize_and_deserialize(TestHelpers::test_creation<\n                                std::unique_ptr<EoS::EquationOfState<true, 3>>>(\n          {\"Tabulated3D:\\n\"\n           \"  TableFilename: \" +\n           unit_test_src_path() +\n           \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\\n\"\n           \"  TableSubFilename: 'dd2'\"}));\n  const EoS::Tabulated3D<true>& deserialized_eos =\n      dynamic_cast<const EoS::Tabulated3D<true>&>(*eos_pointer);\n  TestHelpers::EquationsOfState::test_get_clone(deserialized_eos);\n\n  CHECK(deserialized_eos == eos);\n\n  test_against_reference_values(deserialized_eos);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/InitialData/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_HydroInitialData\")\n\nset(LIBRARY_SOURCES\n  Test_IrrotationalBns.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Hydro\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/InitialData/IrrotationalBns.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\nimport numpy as np\n\n\ndef rotational_shift(shift, spatial_rotational_killing_vector):\n    return shift + spatial_rotational_killing_vector\n\n\ndef rotational_shift_stress(rotational_shift, lapse):\n    return np.outer(rotational_shift, rotational_shift) / lapse**2\n\n\ndef derivative_rotational_shift_over_lapse_squared(\n    rotational_shift,\n    deriv_of_shift,\n    lapse,\n    deriv_of_lapse,\n    deriv_of_spatial_rotational_killing_vector,\n):\n    return (\n        deriv_of_shift + deriv_of_spatial_rotational_killing_vector\n    ) / lapse**2 - 2 * np.outer(deriv_of_lapse / lapse**3, rotational_shift)\n\n\ndef specific_enthalpy_squared(\n    rotational_shift,\n    lapse,\n    velocity_potential_gradient,\n    inverse_spatial_metric,\n    euler_enthalpy_constant,\n):\n    return 1 / lapse**2 * (\n        euler_enthalpy_constant\n        + np.einsum(\"i, i\", rotational_shift, velocity_potential_gradient)\n    ) ** 2 - np.einsum(\n        \"i, i\",\n        velocity_potential_gradient,\n        np.einsum(\"ij, j\", inverse_spatial_metric, velocity_potential_gradient),\n    )\n\n\ndef spatial_rotational_killing_vector(\n    x, local_angular_velocity_around_z, sqrt_det_spatial_metric\n):\n    return sqrt_det_spatial_metric * np.cross(\n        local_angular_velocity_around_z\n        * np.array(\n            [np.zeros_like(x[0]), np.zeros_like(x[0]), np.ones_like(x[0])]\n        ),\n        x,\n    )\n\n\ndef divergence_spatial_rotational_killing_vector(\n    x, local_angular_velocity_around_z, sqrt_det_spatial_metric\n):\n    return 0.0\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/InitialData/Test_IrrotationalBns.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"PointwiseFunctions/Hydro/InitialData/IrrotationalBns.hpp\"\n\nnamespace hydro::initial_data::irrotational_bns {\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.InitialData.IrrotationalBns\",\n                  \"[Unit][Hydro][Elliptic]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Hydro/InitialData\");\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataVector, 3> (*)(const tnsr::I<DataVector, 3>&,\n                                             const tnsr::I<DataVector, 3>&)>(\n          &rotational_shift),\n      \"IrrotationalBns\", \"rotational_shift\", {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::II<DataVector, 3> (*)(const tnsr::I<DataVector, 3>&,\n                                              const Scalar<DataVector>&)>(\n          &rotational_shift_stress),\n      \"IrrotationalBns\", \"rotational_shift_stress\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::iJ<DataVector, 3> (*)(\n          const tnsr::I<DataVector, 3>&, const tnsr::iJ<DataVector, 3>&,\n          const Scalar<DataVector>&, const tnsr::i<DataVector, 3>&,\n          const tnsr::iJ<DataVector, 3>&)>(\n          &derivative_rotational_shift_over_lapse_squared),\n      \"IrrotationalBns\", \"derivative_rotational_shift_over_lapse_squared\",\n      {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataVector> (*)(\n          const tnsr::I<DataVector, 3>&, const Scalar<DataVector>&,\n          const tnsr::i<DataVector, 3>&, const tnsr::II<DataVector, 3>&,\n          double)>(&specific_enthalpy_squared),\n      \"IrrotationalBns\", \"specific_enthalpy_squared\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataVector, 3> (*)(\n          const tnsr::I<DataVector, 3>&, double, const Scalar<DataVector>&)>(\n          &spatial_rotational_killing_vector),\n      \"IrrotationalBns\", \"spatial_rotational_killing_vector\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataVector> (*)(const tnsr::I<DataVector, 3>&, double,\n                                         const Scalar<DataVector>&)>(\n          &divergence_spatial_rotational_killing_vector),\n      \"IrrotationalBns\", \"divergence_spatial_rotational_killing_vector\",\n      {{{0.0, 1.0}}}, used_for_size);\n}\n}  // namespace hydro::initial_data::irrotational_bns\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/InversePlasmaBeta.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Functions testing InversePlasmaBeta\n\n\ndef inverse_plasma_beta(comoving_magnetic_field_magnitude, fluid_pressure):\n    return 0.5 * (comoving_magnetic_field_magnitude**2) / fluid_pressure\n\n\n# End function for testing InversePlasmaBeta\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/LorentzFactor.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef lorentz_factor(spatial_velocity, spatial_velocity_one_form):\n    return 1.0 / np.sqrt(\n        1.0 - np.dot(spatial_velocity.T, spatial_velocity_one_form)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/MassFlux.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef mass_flux(\n    rest_mass_density,\n    spatial_velocity,\n    lorentz_factor,\n    lapse,\n    shift,\n    sqrt_det_spatial_metric,\n):\n    return (\n        rest_mass_density\n        * lorentz_factor\n        * sqrt_det_spatial_metric\n        * (lapse * spatial_velocity - shift)\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/MassWeightedFluidItems.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef mass_weighted_internal_energy(tilde_d, specific_internal_energy):\n    return tilde_d * specific_internal_energy\n\n\ndef mass_weighted_kinetic_energy(tilde_d, lorentz_factor):\n    return tilde_d * (lorentz_factor - 1.0)\n\n\ndef tilde_d_unbound_ut_criterion(\n    tilde_d, lorentz_factor, spatial_velocity, spatial_metric, lapse, shift\n):\n    shift_dot_velocity = np.einsum(\n        \"ab, ab\", spatial_metric, np.outer(spatial_velocity, shift)\n    )\n    u_t = lorentz_factor * (-lapse + shift_dot_velocity)\n    return tilde_d * (u_t < -1.0)\n\n\ndef mass_weighted_coords_none(tilde_d, grid_coords, compute_coords):\n    return tilde_d * compute_coords\n\n\ndef mass_weighted_coords_a(tilde_d, grid_coords, compute_coords):\n    return tilde_d * compute_coords * np.heaviside(grid_coords, 0)\n\n\ndef mass_weighted_coords_b(tilde_d, grid_coords, compute_coords):\n    return tilde_d * compute_coords * np.heaviside(grid_coords * (-1.0), 0)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.ComovingMagneticField.Python\"\n  ComovingMagneticField.py\n  \"unit;hydro;python\"\n  PyHydro)\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.LorentzFactor.Python\"\n  LorentzFactor.py\n  \"unit;hydro;python\"\n  PyHydro)\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.MassFlux.Python\"\n  MassFlux.py\n  \"unit;hydro;python\"\n  PyHydro)\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.MassWeightedFluidItems.Python\"\n  MassWeightedFluidItems.py\n  \"unit;hydro;python\"\n  PyHydro)\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.SoundSpeedSquared.Python\"\n  SoundSpeedSquared.py\n  \"unit;hydro;python\"\n  PyHydro)\n\nspectre_add_python_bindings_test(\n  \"Unit.Hydro.SpecificEnthalpy.Python\"\n  SpecificEnthalpy.py\n  \"unit;hydro;python\"\n  PyHydro)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Python/ComovingMagneticField.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport sys\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.PointwiseFunctions.Hydro as hydro\nfrom spectre import Informer\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar, tnsr\n\nsys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))\nfrom ComovingMagneticField import (\n    comoving_magnetic_field,\n    comoving_magnetic_field_one_form,\n    comoving_magnetic_field_squared,\n)\n\n\nclass TestCoMovingMF(unittest.TestCase):\n    def test_comoving_magnetic_field(self):\n        spatial_velocity = tnsr.I[DataVector, 3](num_points=5, fill=1.2)\n        magnetic_field = tnsr.I[DataVector, 3](num_points=5, fill=1.1)\n        magnetic_field_dot_spatial_velocity = Scalar[DataVector](\n            num_points=5, fill=1.7\n        )\n        lorentz_factor = Scalar[DataVector](num_points=5, fill=2.1)\n        shift = tnsr.I[DataVector, 3](num_points=5, fill=1.5)\n        lapse = Scalar[DataVector](num_points=5, fill=1.1)\n\n        bindings = hydro.comoving_magnetic_field(\n            spatial_velocity,\n            magnetic_field,\n            magnetic_field_dot_spatial_velocity,\n            lorentz_factor,\n            shift,\n            lapse,\n        )\n        alternative = comoving_magnetic_field(\n            np.array(spatial_velocity),\n            np.array(magnetic_field),\n            np.array(magnetic_field_dot_spatial_velocity)[0],\n            np.array(lorentz_factor)[0],\n            np.array(shift),\n            np.array(lapse)[0],\n        )\n\n        assert type(bindings) == tnsr.A[DataVector, 3]\n        np.testing.assert_allclose(bindings, alternative)\n\n    def test_comoving_magnetic_field_one_form(self):\n        spatial_velocity_one_form = tnsr.i[DataVector, 3](\n            num_points=5, fill=1.2\n        )\n        magnetic_field_one_form = tnsr.i[DataVector, 3](num_points=5, fill=1.1)\n        magnetic_field_dot_spatial_velocity = Scalar[DataVector](\n            num_points=5, fill=1.7\n        )\n        lorentz_factor = Scalar[DataVector](num_points=5, fill=2.1)\n        shift = tnsr.I[DataVector, 3](num_points=5, fill=1.5)\n        lapse = Scalar[DataVector](num_points=5, fill=1.1)\n\n        bindings = hydro.comoving_magnetic_field_one_form(\n            spatial_velocity_one_form,\n            magnetic_field_one_form,\n            magnetic_field_dot_spatial_velocity,\n            lorentz_factor,\n            shift,\n            lapse,\n        )\n        alternative = comoving_magnetic_field_one_form(\n            np.array(spatial_velocity_one_form),\n            np.array(magnetic_field_one_form),\n            np.array(magnetic_field_dot_spatial_velocity)[0],\n            np.array(lorentz_factor)[0],\n            np.array(shift),\n            np.array(lapse)[0],\n        )\n\n        assert type(bindings) == tnsr.a[DataVector, 3]\n        np.testing.assert_allclose(bindings, alternative)\n\n    def test_comoving_magnetic_field_squared(self):\n        magnetic_field_squared = Scalar[DataVector](num_points=5, fill=1.5)\n        magnetic_field_dot_spatial_velocity = Scalar[DataVector](\n            num_points=5, fill=1.7\n        )\n        lorentz_factor = Scalar[DataVector](num_points=5, fill=2.1)\n\n        bindings = hydro.comoving_magnetic_field_squared(\n            magnetic_field_squared,\n            magnetic_field_dot_spatial_velocity,\n            lorentz_factor,\n        )\n\n        alternative = comoving_magnetic_field_squared(\n            np.array(magnetic_field_squared),\n            np.array(magnetic_field_dot_spatial_velocity),\n            np.array(lorentz_factor)[0],\n        )\n\n        assert type(bindings) == Scalar[DataVector]\n        np.testing.assert_allclose(bindings, alternative)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Python/LorentzFactor.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport sys\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.DataStructures.Tensor.Frame as fr\nimport spectre.PointwiseFunctions.Hydro as hydro\nfrom spectre import Informer\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar, tnsr\n\nsys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))\nfrom LorentzFactor import lorentz_factor\n\n\nclass TestLorentzFactor(unittest.TestCase):\n    def test_lorentz_factor(self):\n        spatial_velocity = tnsr.I[DataVector, 3, fr.Inertial](\n            num_points=5, fill=-1.0\n        )\n\n        spatial_velocity_form = tnsr.i[DataVector, 3, fr.Inertial](\n            num_points=5, fill=1 / np.sqrt(3)\n        )\n\n        bindings = hydro.lorentz_factor(spatial_velocity, spatial_velocity_form)\n        alternative = [\n            lorentz_factor(\n                np.array(spatial_velocity), np.array(spatial_velocity_form)\n            )[0]\n        ]\n        assert type(bindings) == Scalar[DataVector]\n        np.testing.assert_allclose(bindings, alternative)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Python/MassFlux.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport sys\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.DataStructures.Tensor.Frame as fr\nimport spectre.PointwiseFunctions.Hydro as hydro\nfrom spectre import Informer\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar, tnsr\n\nsys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))\nfrom MassFlux import mass_flux\n\n\nclass TestMassFlux(unittest.TestCase):\n    def test_mass_flux(self):\n        rest_mass_density = Scalar[DataVector](num_points=5, fill=2.1)\n        spatial_velocity = tnsr.I[DataVector, 3, fr.Grid](\n            num_points=5, fill=-1.0\n        )\n\n        lorentz_factor = Scalar[DataVector](num_points=5, fill=2.1)\n        shift = tnsr.I[DataVector, 3, fr.Grid](num_points=5, fill=1.5)\n        lapse = Scalar[DataVector](num_points=5, fill=1.1)\n        sqrt_det_spatial_metric = Scalar[DataVector](num_points=5, fill=1.8)\n\n        bindings = hydro.mass_flux(\n            rest_mass_density,\n            spatial_velocity,\n            lorentz_factor,\n            lapse,\n            shift,\n            sqrt_det_spatial_metric,\n        )\n\n        alternative = mass_flux(\n            np.array(rest_mass_density)[0],\n            np.array(spatial_velocity),\n            np.array(lorentz_factor)[0],\n            np.array(shift),\n            np.array(lapse)[0],\n            np.array(sqrt_det_spatial_metric)[0],\n        )\n\n        assert type(bindings) == tnsr.I[DataVector, 3, fr.Grid]\n        np.testing.assert_allclose(bindings, alternative)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Python/MassWeightedFluidItems.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Python/SoundSpeedSquared.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.PointwiseFunctions.Hydro as hydro\nimport spectre.PointwiseFunctions.Hydro.EquationsOfState as eos\nfrom spectre import Informer\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar, tnsr\n\n\nclass TestSoundSpeedSquared(unittest.TestCase):\n    def test_sound_speed_squared_1d(self):\n        rest_mass_density = Scalar[DataVector](num_points=5, fill=2.1)\n        specific_internal_energy = Scalar[DataVector](num_points=5, fill=1.1)\n        specific_enthalpy = Scalar[DataVector](num_points=5, fill=1.8)\n        equation_of_state = eos.RelativisticPolytropicFluid(0.003, 4.0 / 3.0)\n\n        bindings = hydro.sound_speed_squared(\n            rest_mass_density,\n            specific_internal_energy,\n            specific_enthalpy,\n            equation_of_state,\n        )\n\n        alternative = np.array(\n            (\n                equation_of_state.chi_from_density(np.array(rest_mass_density))[\n                    0\n                ]\n                + equation_of_state.kappa_times_p_over_rho_squared_from_density(\n                    np.array(rest_mass_density)\n                )[0]\n            )\n            / np.array(specific_enthalpy)[0]\n        )[0]\n\n        assert type(bindings) == Scalar[DataVector]\n        np.testing.assert_allclose(bindings, alternative)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Python/SpecificEnthalpy.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport sys\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nimport spectre.DataStructures.Tensor.Frame as fr\nimport spectre.PointwiseFunctions.Hydro as hydro\nfrom spectre import Informer\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar, tnsr\n\nsys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))\nfrom SpecificEnthalpy import relativistic_specific_enthalpy\n\n\nclass TestSpecificEnthalpy(unittest.TestCase):\n    def test_relativistic_specific_enthalpy(self):\n        rest_mass_density = Scalar[DataVector](num_points=5, fill=2.1)\n        specific_internal_energy = Scalar[DataVector](num_points=5, fill=1.1)\n        pressure = Scalar[DataVector](num_points=5, fill=3.1)\n\n        bindings = hydro.relativistic_specific_enthalpy(\n            rest_mass_density, specific_internal_energy, pressure\n        )\n        alternative = relativistic_specific_enthalpy(\n            np.array(rest_mass_density),\n            np.array(specific_internal_energy),\n            np.array(pressure),\n        )\n        assert type(bindings) == Scalar[DataVector]\n        np.testing.assert_allclose(bindings, alternative)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Python/Test_StressEnergy.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef inverse_spacetime_metric(lapse, shift, inverse_spatial_metric):\n    result = np.zeros([4, 4])\n    result[0, 0] = -1.0 / (lapse * lapse)\n    for i in range(3):\n        result[0, i + 1] = -shift[i] * result[0, 0]\n        result[i + 1, 0] = result[0, i + 1]\n    for i in range(3):\n        for j in range(i, 3):\n            result[i + 1, j + 1] = inverse_spatial_metric[i, j] + (\n                shift[i] * shift[j] * result[0, 0]\n            )\n            if i != j:\n                result[j + 1, i + 1] = result[i + 1, j + 1]\n    return result\n\n\ndef four_velocity(spatial_velocity, shift, lorentz_factor, lapse):\n    result = np.zeros(4)\n    result[0] = lorentz_factor / lapse\n    result[1:] = lorentz_factor * (spatial_velocity - shift / lapse)\n    return result\n\n\ndef comoving_magnetic_field(\n    spatial_velocity,\n    magnetic_field,\n    magnetic_field_dot_spatial_velocity,\n    lorentz_factor,\n    shift,\n    lapse,\n):\n    result = np.zeros(4)\n    result[0] = lorentz_factor * magnetic_field_dot_spatial_velocity / lapse\n    result[1:] = (\n        magnetic_field\n        + lapse\n        * result[0]\n        * (lorentz_factor * (spatial_velocity - shift / lapse))\n    ) / lorentz_factor\n    return result\n\n\ndef energy_density(\n    rest_mass_density,\n    specific_enthalpy,\n    pressure,\n    lorentz_factor,\n    magnetic_field_dot_spatial_velocity,\n    comoving_magnetic_field_squared,\n):\n    return (\n        rest_mass_density * specific_enthalpy * lorentz_factor**2\n        - pressure\n        + comoving_magnetic_field_squared * (lorentz_factor**2 - 0.5)\n        - (lorentz_factor * magnetic_field_dot_spatial_velocity) ** 2\n    )\n\n\ndef momentum_density(\n    rest_mass_density,\n    specific_enthalpy,\n    spatial_velocity,\n    lorentz_factor,\n    magnetic_field,\n    magnetic_field_dot_spatial_velocity,\n    comoving_magnetic_field_squared,\n):\n    return (\n        rest_mass_density\n        * specific_enthalpy\n        * lorentz_factor**2\n        * spatial_velocity\n        + comoving_magnetic_field_squared\n        * lorentz_factor**2\n        * spatial_velocity\n        - magnetic_field_dot_spatial_velocity * magnetic_field\n        - magnetic_field_dot_spatial_velocity**2\n        * lorentz_factor**2\n        * spatial_velocity\n    )\n\n\ndef stress_trace(\n    rest_mass_density,\n    specific_enthalpy,\n    pressure,\n    spatial_velocity_squared,\n    lorentz_factor,\n    magnetic_field_dot_spatial_velocity,\n    comoving_magnetic_field_squared,\n):\n    return (\n        3.0 * pressure\n        + rest_mass_density * specific_enthalpy * (lorentz_factor**2 - 1.0)\n        + comoving_magnetic_field_squared\n        * (lorentz_factor**2 * spatial_velocity_squared + 0.5)\n        - magnetic_field_dot_spatial_velocity**2\n        * (lorentz_factor**2 * spatial_velocity_squared + 1.0)\n    )\n\n\ndef stress_energy_tensor(\n    rest_mass_density,\n    specific_internal_energy,\n    pressure,\n    lorentz_factor,\n    lapse,\n    comoving_magnetic_field_magnitude,\n    spatial_velocity,\n    shift,\n    magnetic_field,\n    spatial_metric,\n    inverse_spatial_metric,\n):\n    inverse_spacetime_metric_v = inverse_spacetime_metric(\n        lapse, shift, inverse_spatial_metric\n    )\n\n    magnetic_field_dot_spatial_velocity = np.dot(\n        np.dot(spatial_metric, magnetic_field), spatial_velocity\n    )\n    comoving_magnetic_field_v = comoving_magnetic_field(\n        spatial_velocity,\n        magnetic_field,\n        magnetic_field_dot_spatial_velocity,\n        lorentz_factor,\n        shift,\n        lapse,\n    )\n    four_velocity_v = four_velocity(\n        spatial_velocity, shift, lorentz_factor, lapse\n    )\n    rho_h_star = (\n        (rest_mass_density + rest_mass_density * specific_internal_energy)\n        + pressure\n        + comoving_magnetic_field_magnitude**2\n    )\n    p_star = pressure + 0.5 * (comoving_magnetic_field_magnitude**2)\n\n    result = np.zeros([4, 4])\n\n    for i in range(4):\n        result[i, i] += (\n            rho_h_star * (four_velocity_v[i]) ** 2\n            + p_star * inverse_spacetime_metric_v[i, i]\n            - (comoving_magnetic_field_v[i]) ** 2\n        )\n\n        for j in range(i + 1, 4):\n            value = (\n                rho_h_star * four_velocity_v[i] * four_velocity_v[j]\n                + p_star * inverse_spacetime_metric_v[i, j]\n                - comoving_magnetic_field_v[i] * comoving_magnetic_field_v[j]\n            )\n            result[i, j] += value\n            result[j, i] += value\n\n    return result\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/QuadrupoleFormula.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Functions testing QuadrupoleFormula\n\n\ndef quadrupole_moment(tilde_d, compute_coords):\n    xi, xj = np.meshgrid(compute_coords, compute_coords, indexing=\"ij\")\n    return tilde_d * xi * xj\n\n\ndef quadrupole_moment_derivative(tilde_d, compute_coords, spatial_velocity):\n    xi, xj = np.meshgrid(compute_coords, compute_coords, indexing=\"ij\")\n    vi, vj = np.meshgrid(spatial_velocity, spatial_velocity, indexing=\"ij\")\n    return tilde_d * (vi * xj + xi * vj)\n\n\n# End functions for testing QuadrupoleFormula\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/SpecificEnthalpy.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\ndef relativistic_specific_enthalpy(\n    rest_mass_density, specific_internal_energy, pressure\n):\n    return pressure / rest_mass_density + 1.0 + specific_internal_energy\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_ComovingMagneticField.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/ComovingMagneticField.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n\nnamespace {\n// Verifies that the squared comoving magnetic field, computed as the dot\n// product of the comoving magnetic field and its one-form, matches the value\n// returned by the function \"comoving_magnetic_field_squared\".\ntemplate <typename DataType>\nvoid consistency_check(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n  const auto lapse =\n      TestHelpers::gr::random_lapse(make_not_null(&generator), used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<3>(make_not_null(&generator),\n                                                      used_for_size);\n  const auto spatial_metric = TestHelpers::gr::random_spatial_metric<3>(\n      make_not_null(&generator), used_for_size);\n\n  std::uniform_real_distribution<> distribution(-1.0, 1.0);\n  auto spatial_velocity = make_with_random_values<tnsr::I<DataType, 3>>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n\n  const auto magnetic_field = make_with_random_values<tnsr::I<DataType, 3>>(\n      make_not_null(&generator), make_not_null(&distribution), used_for_size);\n\n  TempBuffer<tmpl::list<\n      ::Tags::TempScalar<0, DataType>, ::Tags::TempScalar<1, DataType>,\n      ::Tags::TempScalar<2, DataType>, ::Tags::TempScalar<3, DataType>,\n      ::Tags::Tempi<4, 3, Frame::Inertial, DataType>,\n      ::Tags::Tempi<5, 3, Frame::Inertial, DataType>>>\n      buffer(get_size(used_for_size));\n\n  auto& spatial_velocity_squared = get<::Tags::TempScalar<0, DataType>>(buffer);\n  auto& magnetic_field_squared = get<::Tags::TempScalar<1, DataType>>(buffer);\n  auto& magnetic_field_dot_spatial_velocity =\n      get<::Tags::TempScalar<2, DataType>>(buffer);\n  auto& lorentz_factor_v = get<::Tags::TempScalar<3, DataType>>(buffer);\n  auto& spatial_velocity_one_form =\n      get<::Tags::Tempi<4, 3, Frame::Inertial, DataType>>(buffer);\n  auto& magnetic_field_one_form =\n      get<::Tags::Tempi<5, 3, Frame::Inertial, DataType>>(buffer);\n\n  dot_product(make_not_null(&spatial_velocity_squared), spatial_velocity,\n              spatial_velocity, spatial_metric);\n\n  // To avoid speeds exceeding the speed of light,\n  // normalize the spatial velocity so that its magnitude is 0.6c.\n\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_velocity.get(i) /= (5. / 3.) * sqrt(get(spatial_velocity_squared));\n  }\n  get(spatial_velocity_squared) = 9. / 25.;\n\n  dot_product(make_not_null(&magnetic_field_squared), magnetic_field,\n              magnetic_field, spatial_metric);\n\n  dot_product(make_not_null(&magnetic_field_dot_spatial_velocity),\n              magnetic_field, spatial_velocity, spatial_metric);\n\n  hydro::lorentz_factor(make_not_null(&lorentz_factor_v),\n                        spatial_velocity_squared);\n\n  tenex::evaluate<ti::i>(\n      make_not_null(&spatial_velocity_one_form),\n      spatial_metric(ti::i, ti::j) * spatial_velocity(ti::J));\n\n  tenex::evaluate<ti::i>(make_not_null(&magnetic_field_one_form),\n                         spatial_metric(ti::i, ti::j) * magnetic_field(ti::J));\n\n  const tnsr::A<DataType, 3> comoving_magnetic_field_v =\n      hydro::comoving_magnetic_field(spatial_velocity, magnetic_field,\n                                     magnetic_field_dot_spatial_velocity,\n                                     lorentz_factor_v, shift, lapse);\n\n  const tnsr::a<DataType, 3> comoving_magnetic_field_one_form_v =\n      hydro::comoving_magnetic_field_one_form(\n          spatial_velocity_one_form, magnetic_field_one_form,\n          magnetic_field_dot_spatial_velocity, lorentz_factor_v, shift, lapse);\n\n  const Scalar<DataType> comoving_magnetic_field_squared_v =\n      hydro::comoving_magnetic_field_squared(\n          magnetic_field_squared, magnetic_field_dot_spatial_velocity,\n          lorentz_factor_v);\n\n  auto comoving_magnetic_field_squared_calc =\n      make_with_value<Scalar<DataType>>(used_for_size, 0.0);\n  for (size_t i = 0; i < 4; ++i) {\n    get(comoving_magnetic_field_squared_calc) +=\n        comoving_magnetic_field_v.get(i) *\n        comoving_magnetic_field_one_form_v.get(i);\n  }\n  CHECK_ITERABLE_APPROX(comoving_magnetic_field_squared_calc,\n                        comoving_magnetic_field_squared_v);\n}\n}  // namespace\n\nnamespace hydro {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.ComovingMagneticField\",\n                  \"[Unit][Hydro]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Hydro/\");\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::A<DataVector, 3> (*)(\n          const tnsr::I<DataVector, 3>&, const tnsr::I<DataVector, 3>&,\n          const Scalar<DataVector>&, const Scalar<DataVector>&,\n          const tnsr::I<DataVector, 3>&, const Scalar<DataVector>&)>(\n          &comoving_magnetic_field<DataVector>),\n      \"ComovingMagneticField\", \"comoving_magnetic_field\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::a<DataVector, 3> (*)(\n          const tnsr::i<DataVector, 3>&, const tnsr::i<DataVector, 3>&,\n          const Scalar<DataVector>&, const Scalar<DataVector>&,\n          const tnsr::I<DataVector, 3>&, const Scalar<DataVector>&)>(\n          &comoving_magnetic_field_one_form<DataVector>),\n      \"ComovingMagneticField\", \"comoving_magnetic_field_one_form\",\n      {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataVector> (*)(const Scalar<DataVector>&,\n                                         const Scalar<DataVector>&,\n                                         const Scalar<DataVector>&)>(\n          &comoving_magnetic_field_squared<DataVector>),\n      \"ComovingMagneticField\", \"comoving_magnetic_field_squared\",\n      {{{0.0, 1.0}}}, used_for_size);\n\n  consistency_check(used_for_size);\n}\n\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_InversePlasmaBeta.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/InversePlasmaBeta.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename DataType>\nvoid test_inverse_plasma_beta(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataType> (*)(const Scalar<DataType>&,\n                                       const Scalar<DataType>&)>(\n          &hydro::inverse_plasma_beta<DataType>),\n      \"InversePlasmaBeta\", \"inverse_plasma_beta\", {{{0.01, 1.0}}},\n      used_for_size);\n}\n}  // namespace\n\nnamespace hydro {\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.InvserePlasmaBeta\",\n                  \"[Unit][Hydro]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Hydro\"};\n\n  test_inverse_plasma_beta(\n      std::numeric_limits<double>::signaling_NaN());\n  test_inverse_plasma_beta(DataVector(5));\n\n  // Check compute item works correctly in DataBox\n  TestHelpers::db::test_compute_tag<Tags::InversePlasmaBetaCompute<DataVector>>(\n      \"InversePlasmaBeta\");\n  const Scalar<DataVector> comoving_magnetic_field_magnitude{\n      {{DataVector{5, 0.11}}}};\n  const Scalar<DataVector> fluid_pressure{{{DataVector{5, 0.05}}}};\n\n  const auto box =\n      db::create<db::AddSimpleTags<\n        Tags::ComovingMagneticFieldMagnitude<DataVector>,\n        Tags::Pressure<DataVector>>,\n                 db::AddComputeTags<\n        Tags::InversePlasmaBetaCompute<DataVector>>>(\n          comoving_magnetic_field_magnitude, fluid_pressure);\n  CHECK(db::get<Tags::InversePlasmaBeta<DataVector>>(box) ==\n        inverse_plasma_beta(comoving_magnetic_field_magnitude,\n                            fluid_pressure));\n}\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_LorentzFactor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace hydro {\nnamespace {\ntemplate <size_t Dim, typename Frame, typename DataType>\nvoid test_lorentz_factor(const DataType& used_for_size) {\n  constexpr auto function =\n      static_cast<Scalar<DataType> (*)(const tnsr::I<DataType, Dim, Frame>&,\n                                       const tnsr::i<DataType, Dim, Frame>&)>(\n          &lorentz_factor<DataType, Dim, Frame>);\n  pypp::check_with_random_values<1>(function, \"LorentzFactor\", \"lorentz_factor\",\n                                    {{{0.0, 1.0 / sqrt(Dim)}}}, used_for_size);\n  pypp::check_with_random_values<1>(function, \"LorentzFactor\", \"lorentz_factor\",\n                                    {{{-1.0 / sqrt(Dim), 0.0}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.LorentzFactor\",\n                  \"[Unit][Hydro]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Hydro/\");\n  const DataVector dv(5);\n  test_lorentz_factor<1, Frame::Inertial>(dv);\n  test_lorentz_factor<1, Frame::Grid>(dv);\n  test_lorentz_factor<2, Frame::Inertial>(dv);\n  test_lorentz_factor<2, Frame::Grid>(dv);\n  test_lorentz_factor<3, Frame::Inertial>(dv);\n  test_lorentz_factor<3, Frame::Grid>(dv);\n\n  test_lorentz_factor<1, Frame::Inertial>(0.0);\n  test_lorentz_factor<1, Frame::Grid>(0.0);\n  test_lorentz_factor<2, Frame::Inertial>(0.0);\n  test_lorentz_factor<2, Frame::Grid>(0.0);\n  test_lorentz_factor<3, Frame::Inertial>(0.0);\n  test_lorentz_factor<3, Frame::Grid>(0.0);\n\n  // Check compute item works correctly in DataBox\n  TestHelpers::db::test_compute_tag<\n      Tags::LorentzFactorCompute<DataVector, 2, Frame::Inertial>>(\n      \"LorentzFactor\");\n  tnsr::i<DataVector, 2> velocity_one_form{\n      {{DataVector{5, 0.2}, DataVector{5, 0.3}}}};\n  tnsr::I<DataVector, 2> velocity{{{DataVector{5, 0.25}, DataVector{5, 0.35}}}};\n  const auto box =\n      db::create<db::AddSimpleTags<Tags::SpatialVelocity<DataVector, 2>,\n                                   Tags::SpatialVelocityOneForm<DataVector, 2>>,\n                 db::AddComputeTags<Tags::LorentzFactorCompute<\n                     DataVector, 2, Frame::Inertial>>>(velocity,\n                                                       velocity_one_form);\n  CHECK(db::get<Tags::LorentzFactor<DataVector>>(box) ==\n        lorentz_factor(velocity, velocity_one_form));\n}\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_MagneticFieldTreatment.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"PointwiseFunctions/Hydro/MagneticFieldTreatment.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <hydro::MagneticFieldTreatment MagFieldTreatment>\nvoid test_construct_from_options() {\n  const auto created =\n      TestHelpers::test_creation<hydro::MagneticFieldTreatment>(\n          get_output(MagFieldTreatment));\n  CHECK(created == MagFieldTreatment);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.MagneticFieldTreatment\",\n                  \"[Hydro][Unit]\") {\n  using hydro::MagneticFieldTreatment;\n  CHECK(get_output(MagneticFieldTreatment::AssumeZero) == \"AssumeZero\");\n  CHECK(get_output(MagneticFieldTreatment::AssumeNonZero) == \"AssumeNonZero\");\n  CHECK(get_output(MagneticFieldTreatment::CheckIfZero) == \"CheckIfZero\");\n\n  test_construct_from_options<MagneticFieldTreatment::AssumeZero>();\n  test_construct_from_options<MagneticFieldTreatment::AssumeNonZero>();\n  test_construct_from_options<MagneticFieldTreatment::CheckIfZero>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_MassFlux.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/MassFlux.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace hydro {\nnamespace {\ntemplate <size_t Dim, typename Frame, typename DataType>\nvoid test_mass_flux(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataType, Dim, Frame> (*)(\n          const Scalar<DataType>&, const tnsr::I<DataType, Dim, Frame>&,\n          const Scalar<DataType>&, const Scalar<DataType>&,\n          const tnsr::I<DataType, Dim, Frame>&, const Scalar<DataType>&)>(\n          &mass_flux<DataType, Dim, Frame>),\n      \"MassFlux\", \"mass_flux\", {{{-10.0, 10.0}}}, used_for_size);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.MassFlux\",\n                  \"[Unit][Hydro]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Hydro/\");\n  const DataVector dv(5);\n  test_mass_flux<1, Frame::Inertial>(dv);\n  test_mass_flux<1, Frame::Grid>(dv);\n  test_mass_flux<2, Frame::Inertial>(dv);\n  test_mass_flux<2, Frame::Grid>(dv);\n  test_mass_flux<3, Frame::Inertial>(dv);\n  test_mass_flux<3, Frame::Grid>(dv);\n\n  test_mass_flux<1, Frame::Inertial>(0.0);\n  test_mass_flux<1, Frame::Grid>(0.0);\n  test_mass_flux<2, Frame::Inertial>(0.0);\n  test_mass_flux<2, Frame::Grid>(0.0);\n  test_mass_flux<3, Frame::Inertial>(0.0);\n  test_mass_flux<3, Frame::Grid>(0.0);\n\n  // Check compute item works correctly in DataBox\n  TestHelpers::db::test_compute_tag<\n      Tags::MassFluxCompute<DataVector, 2, Frame::Inertial>>(\"MassFlux\");\n  Scalar<DataVector> rho{{{DataVector{5, 1.0}}}};\n  tnsr::I<DataVector, 3> velocity{\n      {{DataVector{5, 0.25}, DataVector{5, 0.1}, DataVector{5, 0.35}}}};\n  Scalar<DataVector> lorentz{{{DataVector{5, 0.2}}}};\n  Scalar<DataVector> lapse{{{DataVector{5, 0.3}}}};\n  tnsr::I<DataVector, 3> shift{\n      {{DataVector{5, 0.1}, DataVector{5, 0.2}, DataVector{5, 0.3}}}};\n  Scalar<DataVector> sqrt_det_g{{{DataVector{5, 0.25}}}};\n  const auto box = db::create<\n      db::AddSimpleTags<\n          Tags::RestMassDensity<DataVector>,\n          Tags::SpatialVelocity<DataVector, 3>, Tags::LorentzFactor<DataVector>,\n          ::gr::Tags::Lapse<DataVector>, ::gr::Tags::Shift<DataVector, 3>,\n          ::gr::Tags::SqrtDetSpatialMetric<DataVector>>,\n      db::AddComputeTags<\n          Tags::MassFluxCompute<DataVector, 3, Frame::Inertial>>>(\n      rho, velocity, lorentz, lapse, shift, sqrt_det_g);\n  CHECK(db::get<Tags::MassFlux<DataVector, 3, Frame::Inertial>>(box) ==\n        mass_flux(rho, velocity, lorentz, lapse, shift, sqrt_det_g));\n}\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_MassWeightedFluidItems.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/MassWeightedFluidItems.hpp\"\n\nnamespace hydro {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.MassWeightedFluidItems\",\n                  \"[Unit][Hydro]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Hydro/\");\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(gsl::not_null<Scalar<DataVector>*>,\n                           const Scalar<DataVector>&,\n                           const Scalar<DataVector>&)>(\n          &mass_weighted_internal_energy<DataVector>),\n      \"MassWeightedFluidItems\", {\"mass_weighted_internal_energy\"},\n      {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataVector> (*)(const Scalar<DataVector>&,\n                                         const Scalar<DataVector>&)>(\n          &mass_weighted_internal_energy<DataVector>),\n      \"MassWeightedFluidItems\", \"mass_weighted_internal_energy\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(gsl::not_null<Scalar<DataVector>*>,\n                           const Scalar<DataVector>&,\n                           const Scalar<DataVector>&)>(\n          &mass_weighted_kinetic_energy<DataVector>),\n      \"MassWeightedFluidItems\", {\"mass_weighted_kinetic_energy\"},\n      {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataVector> (*)(const Scalar<DataVector>&,\n                                         const Scalar<DataVector>&)>(\n          &mass_weighted_kinetic_energy<DataVector>),\n      \"MassWeightedFluidItems\", \"mass_weighted_kinetic_energy\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(gsl::not_null<Scalar<DataVector>*>,\n                           const Scalar<DataVector>&, const Scalar<DataVector>&,\n                           const tnsr::I<DataVector, 1, Frame::Inertial>&,\n                           const tnsr::ii<DataVector, 1, Frame::Inertial>&,\n                           const Scalar<DataVector>& lapse,\n                           const tnsr::I<DataVector, 1, Frame::Inertial>&)>(\n          &tilde_d_unbound_ut_criterion<DataVector, 1, Frame::Inertial>),\n      \"MassWeightedFluidItems\", {\"tilde_d_unbound_ut_criterion\"},\n      {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataVector> (*)(\n          const Scalar<DataVector>&, const Scalar<DataVector>&,\n          const tnsr::I<DataVector, 1, Frame::Inertial>&,\n          const tnsr::ii<DataVector, 1, Frame::Inertial>&,\n          const Scalar<DataVector>& lapse,\n          const tnsr::I<DataVector, 1, Frame::Inertial>&)>(\n          &tilde_d_unbound_ut_criterion<DataVector, 1, Frame::Inertial>),\n      \"MassWeightedFluidItems\", \"tilde_d_unbound_ut_criterion\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataVector, 1, Frame::Inertial> (*)(\n          const Scalar<DataVector>& tilde_d,\n          const tnsr::I<DataVector, 1, Frame::Grid>& grid_coords,\n          const tnsr::I<DataVector, 1, Frame::Inertial>& compute_coords)>(\n          &mass_weighted_coords<::hydro::HalfPlaneIntegralMask::None,\n                                DataVector, 1, Frame::Inertial>),\n      \"MassWeightedFluidItems\", \"mass_weighted_coords_none\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<3>(\n      static_cast<tnsr::I<DataVector, 3, Frame::Inertial> (*)(\n          const Scalar<DataVector>& tilde_d,\n          const tnsr::I<DataVector, 3, Frame::Grid>& grid_coords,\n          const tnsr::I<DataVector, 3, Frame::Inertial>& compute_coords)>(\n          &mass_weighted_coords<::hydro::HalfPlaneIntegralMask::None,\n                                DataVector, 3, Frame::Inertial>),\n      \"MassWeightedFluidItems\", \"mass_weighted_coords_none\", {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataVector, 1, Frame::Inertial> (*)(\n          const Scalar<DataVector>& tilde_d,\n          const tnsr::I<DataVector, 1, Frame::Grid>& grid_coords,\n          const tnsr::I<DataVector, 1, Frame::Inertial>& compute_coords)>(\n          &mass_weighted_coords<::hydro::HalfPlaneIntegralMask::PositiveXOnly,\n                                DataVector, 1, Frame::Inertial>),\n      \"MassWeightedFluidItems\", \"mass_weighted_coords_a\", {{{-1.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<3>(\n      static_cast<tnsr::I<DataVector, 3, Frame::Inertial> (*)(\n          const Scalar<DataVector>& tilde_d,\n          const tnsr::I<DataVector, 3, Frame::Grid>& grid_coords,\n          const tnsr::I<DataVector, 3, Frame::Inertial>& compute_coords)>(\n          &mass_weighted_coords<::hydro::HalfPlaneIntegralMask::PositiveXOnly,\n                                DataVector, 3, Frame::Inertial>),\n      \"MassWeightedFluidItems\", \"mass_weighted_coords_a\", {{{-1.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<tnsr::I<DataVector, 1, Frame::Inertial> (*)(\n          const Scalar<DataVector>& tilde_d,\n          const tnsr::I<DataVector, 1, Frame::Grid>& grid_coords,\n          const tnsr::I<DataVector, 1, Frame::Inertial>& compute_coords)>(\n          &mass_weighted_coords<::hydro::HalfPlaneIntegralMask::NegativeXOnly,\n                                DataVector, 1, Frame::Inertial>),\n      \"MassWeightedFluidItems\", \"mass_weighted_coords_b\", {{{-1.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<3>(\n      static_cast<tnsr::I<DataVector, 3, Frame::Inertial> (*)(\n          const Scalar<DataVector>& tilde_d,\n          const tnsr::I<DataVector, 3, Frame::Grid>& grid_coords,\n          const tnsr::I<DataVector, 3, Frame::Inertial>& compute_coords)>(\n          &mass_weighted_coords<::hydro::HalfPlaneIntegralMask::NegativeXOnly,\n                                DataVector, 3, Frame::Inertial>),\n      \"MassWeightedFluidItems\", \"mass_weighted_coords_b\", {{{-1.0, 1.0}}},\n      used_for_size);\n}\n\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_QuadrupoleFormula.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/QuadrupoleFormula.hpp\"\n\nnamespace hydro {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.QuadrupoleFormula\",\n                  \"[Unit][Hydro]\") {\n  TestHelpers::db::test_simple_tag<\n      Tags::QuadrupoleMoment<\n                 DataVector, 3, Frame::Inertial>>(\"QuadrupoleMoment\");\n  TestHelpers::db::test_simple_tag<\n      Tags::QuadrupoleMomentDerivative<\n                 DataVector, 3, Frame::Inertial>>(\"QuadrupoleMomentDerivative\");\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Hydro/\");\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(\n      &quadrupole_moment<DataVector, 1, Frame::Inertial>,\n      \"QuadrupoleFormula\", {\"quadrupole_moment\"}, {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      &quadrupole_moment<DataVector, 3, Frame::Inertial>,\n      \"QuadrupoleFormula\", {\"quadrupole_moment\"}, {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      &quadrupole_moment_derivative<DataVector, 1, Frame::Inertial>,\n      \"QuadrupoleFormula\", {\"quadrupole_moment_derivative\"}, {{{0.0, 1.0}}},\n      used_for_size);\n  pypp::check_with_random_values<1>(\n      &quadrupole_moment_derivative<DataVector, 3, Frame::Inertial>,\n      \"QuadrupoleFormula\", {\"quadrupole_moment_derivative\"}, {{{0.0, 1.0}}},\n      used_for_size);\n\n  const Mesh<3> mesh{12, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Scalar<DataVector> rho_flat{mesh.number_of_grid_points(), 1.28e-3};\n  const Scalar<DataVector> velocity_squared{mesh.number_of_grid_points(), 0.04};\n  const Scalar<DataVector> W = lorentz_factor(velocity_squared);\n  const Scalar<DataVector> tildeD{get(W) * get(rho_flat)};\n\n  const auto logical_coords = logical_coordinates(mesh);\n  const size_t Dim=3;\n  ElementMap<Dim, Frame::Inertial> logical_to_inertial_map{\n      ElementId<Dim>{0},\n      domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Inertial>(\n          domain::CoordinateMaps::Identity<Dim>{})};\n  const auto inertial_coords = logical_to_inertial_map(logical_coords);\n\n  tnsr::I<DataVector, 3> velocity_x{mesh.number_of_grid_points(), 0.};\n  get<0>(velocity_x) = 0.2;\n  tnsr::I<DataVector, 3> velocity_y{mesh.number_of_grid_points(), 0.};\n  get<1>(velocity_y) = 0.2;\n  tnsr::I<DataVector, 3> velocity_z{mesh.number_of_grid_points(), 0.};\n  get<2>(velocity_z) = 0.2;\n\n  tnsr::ii<DataVector, 3, Frame::Inertial> q{};\n  quadrupole_moment(make_not_null(&q), tildeD, inertial_coords);\n  tnsr::ii<DataVector, 3, Frame::Inertial> qd_x{};\n  quadrupole_moment_derivative(make_not_null(&qd_x), tildeD, inertial_coords,\n                                                                    velocity_x);\n  tnsr::ii<DataVector, 3, Frame::Inertial> qd_y{};\n  quadrupole_moment_derivative(make_not_null(&qd_y), tildeD, inertial_coords,\n                                                                    velocity_y);\n  tnsr::ii<DataVector, 3, Frame::Inertial> qd_z{};\n  quadrupole_moment_derivative(make_not_null(&qd_z), tildeD, inertial_coords,\n                                                                    velocity_z);\n\n  tnsr::ii<double, 3> q_integral{};\n  tnsr::ii<double, 3> qd_x_integral{};\n  tnsr::ii<double, 3> qd_y_integral{};\n  tnsr::ii<double, 3> qd_z_integral{};\n  for (size_t i = 0; i < 3; ++i) {\n    for (size_t j = i; j < 3; ++j) {\n      q_integral.get(i,j) = definite_integral(q.get(i,j), mesh);\n      qd_x_integral.get(i,j) = definite_integral(qd_x.get(i,j), mesh);\n      qd_y_integral.get(i,j) = definite_integral(qd_y.get(i,j), mesh);\n      qd_z_integral.get(i,j) = definite_integral(qd_z.get(i,j), mesh);\n      CHECK(qd_x_integral.get(i, j) == approx(0.0));\n      CHECK(qd_y_integral.get(i, j) == approx(0.0));\n      CHECK(qd_z_integral.get(i, j) == approx(0.0));\n      if (i == j) {\n        CHECK(q_integral.get(i, j) == approx(3.483718745291631e-3));\n      }\n      else {\n        CHECK(q_integral.get(i, j) == approx(0.0));\n      }\n    }\n  }\n}\n\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_SoundSpeedSquared.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/SoundSpeedSquared.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\n\ntemplate <typename DataType, typename EquationOfStateType>\nvoid test_compute_item_in_databox(\n    const Scalar<DataType>& rest_mass_density,\n    const Scalar<DataType>& specific_internal_energy,\n    const Scalar<DataType>& specific_enthalpy,\n    const EquationOfStateType& equation_of_state) {\n  TestHelpers::db::test_compute_tag<hydro::Tags::SoundSpeedSquaredCompute<\n      DataType, EquationOfStateType::is_relativistic,\n      EquationOfStateType::thermodynamic_dim>>(\"SoundSpeedSquared\");\n  const auto box = db::create<\n      db::AddSimpleTags<\n          hydro::Tags::RestMassDensity<DataType>,\n          hydro::Tags::SpecificInternalEnergy<DataType>,\n          hydro::Tags::SpecificEnthalpy<DataType>,\n          hydro::Tags::EquationOfState<EquationOfStateType::is_relativistic,\n                                       EquationOfStateType::thermodynamic_dim>>,\n      db::AddComputeTags<hydro::Tags::SoundSpeedSquaredCompute<\n          DataType, EquationOfStateType::is_relativistic,\n          EquationOfStateType::thermodynamic_dim>>>(\n      rest_mass_density, specific_internal_energy, specific_enthalpy,\n      equation_of_state.get_clone());\n\n  const auto expected_sound_speed_squared =\n      hydro::sound_speed_squared(rest_mass_density, specific_internal_energy,\n                                 specific_enthalpy, equation_of_state);\n  CHECK(db::get<hydro::Tags::SoundSpeedSquared<DataType>>(box) ==\n        expected_sound_speed_squared);\n}\n\ntemplate <typename DataType>\nvoid test_sound_speed_squared(const DataType& used_for_size) {\n  MAKE_GENERATOR(generator);\n\n  const auto rest_mass_density = TestHelpers::hydro::random_density(\n      make_not_null(&generator), used_for_size);\n  Scalar<DataType> specific_internal_energy{};\n  Scalar<DataType> specific_enthalpy{};\n\n  // check with representative equation of state of one independent variable\n  const EquationsOfState::PolytropicFluid<true> eos_1d(0.003, 4.0 / 3.0);\n  specific_internal_energy =\n      eos_1d.specific_internal_energy_from_density(rest_mass_density);\n  specific_enthalpy = hydro::relativistic_specific_enthalpy(\n      rest_mass_density, specific_internal_energy,\n      eos_1d.pressure_from_density(rest_mass_density));\n  CHECK(\n      Scalar<DataType>{(get(eos_1d.chi_from_density(rest_mass_density)) +\n                        get(eos_1d.kappa_times_p_over_rho_squared_from_density(\n                            rest_mass_density))) /\n                       get(specific_enthalpy)} ==\n      hydro::sound_speed_squared(rest_mass_density, specific_internal_energy,\n                                 specific_enthalpy, eos_1d));\n  test_compute_item_in_databox(rest_mass_density, specific_internal_energy,\n                               specific_enthalpy, eos_1d);\n\n  // check with representative equation of state of two independent variables\n  const EquationsOfState::IdealFluid<true> eos_2d(5.0 / 3.0);\n  specific_internal_energy =\n      TestHelpers::hydro::random_specific_internal_energy(\n          make_not_null(&generator), used_for_size);\n  specific_enthalpy = hydro::relativistic_specific_enthalpy(\n      rest_mass_density, specific_internal_energy,\n      eos_2d.pressure_from_density_and_energy(rest_mass_density,\n                                              specific_internal_energy));\n  CHECK(Scalar<DataType>{\n            (get(eos_2d.chi_from_density_and_energy(rest_mass_density,\n                                                    specific_internal_energy)) +\n             get(eos_2d.kappa_times_p_over_rho_squared_from_density_and_energy(\n                 rest_mass_density, specific_internal_energy))) /\n            get(specific_enthalpy)} ==\n        hydro::sound_speed_squared(rest_mass_density, specific_internal_energy,\n                                   specific_enthalpy, eos_2d));\n  test_compute_item_in_databox(rest_mass_density, specific_internal_energy,\n                               specific_enthalpy, eos_2d);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.SoundSpeedSquared\",\n                  \"[Unit][Evolution]\") {\n  test_sound_speed_squared(std::numeric_limits<double>::signaling_NaN());\n  test_sound_speed_squared(DataVector(5));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_SpecificEnthalpy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/SpecificEnthalpy.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n\ntemplate <typename DataType>\nvoid test_relativistic_specific_enthalpy(const DataType& used_for_size) {\n  pypp::check_with_random_values<1>(\n      static_cast<Scalar<DataType> (*)(const Scalar<DataType>&,\n                                       const Scalar<DataType>&,\n                                       const Scalar<DataType>&)>(\n          &hydro::relativistic_specific_enthalpy<DataType>),\n      \"SpecificEnthalpy\", \"relativistic_specific_enthalpy\", {{{0.01, 1.0}}},\n      used_for_size);\n}\n}  // namespace\n\nnamespace hydro {\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.SpecificEnthalpy\",\n                  \"[Unit][Hydro]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Hydro\"};\n\n  test_relativistic_specific_enthalpy(\n      std::numeric_limits<double>::signaling_NaN());\n  test_relativistic_specific_enthalpy(DataVector(5));\n\n  // Check compute item works correctly in DataBox\n  TestHelpers::db::test_compute_tag<Tags::SpecificEnthalpyCompute<DataVector>>(\n      \"SpecificEnthalpy\");\n  Scalar<DataVector> rest_mass_density{{{DataVector{5, 0.2}}}};\n  Scalar<DataVector> specific_internal_energy{{{DataVector{5, 0.23}}}};\n  Scalar<DataVector> pressure{{{DataVector{5, 0.234}}}};\n\n  const auto box =\n      db::create<db::AddSimpleTags<Tags::RestMassDensity<DataVector>,\n                                   Tags::SpecificInternalEnergy<DataVector>,\n                                   Tags::Pressure<DataVector>>,\n                 db::AddComputeTags<Tags::SpecificEnthalpyCompute<DataVector>>>(\n          rest_mass_density, specific_internal_energy, pressure);\n  CHECK(db::get<Tags::SpecificEnthalpy<DataVector>>(box) ==\n        relativistic_specific_enthalpy(rest_mass_density,\n                                       specific_internal_energy, pressure));\n}\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_StressEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tags/TempTensor.hpp\"\n#include \"DataStructures/TempBuffer.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/InverseSpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/SpacetimeMetric.hpp\"\n#include \"PointwiseFunctions/Hydro/ComovingMagneticField.hpp\"\n#include \"PointwiseFunctions/Hydro/LorentzFactor.hpp\"\n#include \"PointwiseFunctions/Hydro/StressEnergy.hpp\"\n\nnamespace {\n// Verifies that the trace-reversed stress-energy tensor, computed from the\n// upper-index stress-energy tensor, matches the result of the function\n// \"trace_reversed_stress_energy\" from\n// Evolution/Systems/GrMhd/GhValenciaDivClean/StressEnergy.cpp.\n// Note: The function \"trace_reversed_stress_energy\" only supports DataVector,\n// so this test is restricted to DataVector.\nvoid consistency_check(const DataVector& used_for_size) {\n  MAKE_GENERATOR(generator);\n\n  // Generate random lapse, shift, spatial metric.\n  const auto lapse =\n      TestHelpers::gr::random_lapse(make_not_null(&generator), used_for_size);\n  const auto shift = TestHelpers::gr::random_shift<3>(make_not_null(&generator),\n                                                      used_for_size);\n  const auto spatial_metric = TestHelpers::gr::random_spatial_metric<3>(\n      make_not_null(&generator), used_for_size);\n  const tnsr::II<DataVector, 3, Frame::Inertial> inverse_spatial_metric =\n      determinant_and_inverse(spatial_metric).second;\n\n  std::uniform_real_distribution<> distribution1(-1.0, 1.0);\n  std::uniform_real_distribution<> distribution2(0.01, 1.0);\n\n  // Generate random hydro variables.\n  auto spatial_velocity = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&generator), make_not_null(&distribution1), used_for_size);\n  const auto magnetic_field = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&generator), make_not_null(&distribution1), used_for_size);\n\n  const auto rest_mass_density = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), make_not_null(&distribution2), used_for_size);\n  const auto pressure = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&generator), make_not_null(&distribution2), used_for_size);\n  const auto specific_internal_energy =\n      make_with_random_values<Scalar<DataVector>>(make_not_null(&generator),\n                                                  make_not_null(&distribution2),\n                                                  used_for_size);\n\n  TempBuffer<tmpl::list<\n      ::Tags::TempScalar<0, DataVector>, ::Tags::TempScalar<1, DataVector>,\n      ::Tags::TempScalar<2, DataVector>, ::Tags::TempScalar<3, DataVector>,\n      ::Tags::TempScalar<4, DataVector>, ::Tags::TempScalar<5, DataVector>,\n      ::Tags::Tempi<6, 3, Frame::Inertial, DataVector>,\n      ::Tags::Tempi<7, 3, Frame::Inertial, DataVector>,\n      ::Tags::Tempaa<8, 3, Frame::Inertial, DataVector>,\n      ::Tags::Tempaa<9, 3, Frame::Inertial, DataVector>,\n      ::Tags::Tempaa<10, 3, Frame::Inertial, DataVector>,\n      ::Tags::Tempaa<11, 3, Frame::Inertial, DataVector>,\n      ::Tags::TempAA<12, 3, Frame::Inertial, DataVector>,\n      ::Tags::TempAA<13, 3, Frame::Inertial, DataVector>>>\n      buffer(get_size(used_for_size));\n\n  auto& spatial_velocity_squared =\n      get<::Tags::TempScalar<0, DataVector>>(buffer);\n  auto& magnetic_field_squared = get<::Tags::TempScalar<1, DataVector>>(buffer);\n  auto& magnetic_field_dot_spatial_velocity =\n      get<::Tags::TempScalar<2, DataVector>>(buffer);\n  auto& lorentz_factor_v = get<::Tags::TempScalar<3, DataVector>>(buffer);\n  auto& one_over_w_squared = get<::Tags::TempScalar<4, DataVector>>(buffer);\n  auto& comoving_magnetic_field_magnitude_v =\n      get<::Tags::TempScalar<5, DataVector>>(buffer);\n\n  auto& spatial_velocity_one_form =\n      get<::Tags::Tempi<6, 3, Frame::Inertial, DataVector>>(buffer);\n  auto& magnetic_field_one_form =\n      get<::Tags::Tempi<7, 3, Frame::Inertial, DataVector>>(buffer);\n\n  auto& spacetime_metric_v =\n      get<::Tags::Tempaa<8, 3, Frame::Inertial, DataVector>>(buffer);\n  auto& stress_energy_tensor_lowered =\n      get<::Tags::Tempaa<9, 3, Frame::Inertial, DataVector>>(buffer);\n  auto& trace_reversed_stress_energy_tensor_v =\n      get<::Tags::Tempaa<10, 3, Frame::Inertial, DataVector>>(buffer);\n  auto& trace_reversed_stress_energy_tensor_calc =\n      get<::Tags::Tempaa<11, 3, Frame::Inertial, DataVector>>(buffer);\n  auto& inverse_spacetime_metric_v =\n      get<::Tags::TempAA<12, 3, Frame::Inertial, DataVector>>(buffer);\n  auto& stress_energy_tensor_v =\n      get<::Tags::TempAA<13, 3, Frame::Inertial, DataVector>>(buffer);\n\n  dot_product(make_not_null(&spatial_velocity_squared), spatial_velocity,\n              spatial_velocity, spatial_metric);\n\n  // To avoid speeds exceeding the speed of light,\n  // normalize the spatial velocity so that its magnitude is 0.6c.\n  for (size_t i = 0; i < 3; ++i) {\n    spatial_velocity.get(i) /= (5. / 3.) * sqrt(get(spatial_velocity_squared));\n  }\n  get(spatial_velocity_squared) = 9. / 25.;\n\n  dot_product(make_not_null(&magnetic_field_squared), magnetic_field,\n              magnetic_field, spatial_metric);\n  dot_product(make_not_null(&magnetic_field_dot_spatial_velocity),\n              magnetic_field, spatial_velocity, spatial_metric);\n  hydro::lorentz_factor(make_not_null(&lorentz_factor_v),\n                        spatial_velocity_squared);\n  get(one_over_w_squared) = 1. / square(get(lorentz_factor_v));\n\n  tenex::evaluate<ti::i>(\n      make_not_null(&spatial_velocity_one_form),\n      spatial_velocity(ti::J) * spatial_metric(ti::j, ti::i));\n\n  tenex::evaluate<ti::i>(make_not_null(&magnetic_field_one_form),\n                         magnetic_field(ti::J) * spatial_metric(ti::j, ti::i));\n\n  hydro::comoving_magnetic_field_squared(\n      make_not_null(&comoving_magnetic_field_magnitude_v),\n      magnetic_field_squared, magnetic_field_dot_spatial_velocity,\n      lorentz_factor_v);\n  get(comoving_magnetic_field_magnitude_v) =\n      sqrt(get(comoving_magnetic_field_magnitude_v));\n\n  hydro::stress_energy_tensor(\n      make_not_null(&stress_energy_tensor_v), rest_mass_density,\n      specific_internal_energy, pressure, lorentz_factor_v, lapse,\n      comoving_magnetic_field_magnitude_v, spatial_velocity, shift,\n      magnetic_field, spatial_metric, inverse_spatial_metric);\n\n  gr::spacetime_metric(make_not_null(&spacetime_metric_v), lapse, shift,\n                       spatial_metric);\n\n  gr::inverse_spacetime_metric(make_not_null(&inverse_spacetime_metric_v),\n                               lapse, shift, inverse_spatial_metric);\n\n  tnsr::a<DataVector, 3> four_velocity_one_form_buffer;\n  tnsr::a<DataVector, 3> comoving_magnetic_field_one_form_buffer;\n  ::grmhd::GhValenciaDivClean::trace_reversed_stress_energy(\n      make_not_null(&trace_reversed_stress_energy_tensor_v),\n      make_not_null(&four_velocity_one_form_buffer),\n      make_not_null(&comoving_magnetic_field_one_form_buffer),\n      rest_mass_density, spatial_velocity_one_form, magnetic_field_one_form,\n      magnetic_field_squared, magnetic_field_dot_spatial_velocity,\n      lorentz_factor_v, one_over_w_squared, pressure, specific_internal_energy,\n      spacetime_metric_v, shift, lapse);\n\n  tenex::evaluate<ti::a, ti::b>(make_not_null(&stress_energy_tensor_lowered),\n                                stress_energy_tensor_v(ti::C, ti::D) *\n                                    spacetime_metric_v(ti::c, ti::a) *\n                                    spacetime_metric_v(ti::d, ti::b));\n  tenex::evaluate<ti::a, ti::b>(\n      make_not_null(&trace_reversed_stress_energy_tensor_calc),\n      stress_energy_tensor_lowered(ti::a, ti::b) -\n          0.5 * spacetime_metric_v(ti::a, ti::b) *\n              inverse_spacetime_metric_v(ti::C, ti::D) *\n              stress_energy_tensor_lowered(ti::d, ti::c));\n\n  // Test cases sometimes fail with the default scale/epsilon value due to\n  // catastrophic cancellation in the computation for the test.\n  // Therefore, we use a custom scale and epsilon values here.\n  Approx approx = Approx::custom().epsilon(1.e-11).scale(1.0);\n  CHECK_ITERABLE_CUSTOM_APPROX(trace_reversed_stress_energy_tensor_calc,\n                               trace_reversed_stress_energy_tensor_v, approx);\n}\n}  // namespace\n\nnamespace hydro {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.StressEnergy\",\n                  \"[Unit][Hydro]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Hydro/Python/\");\n  const DataVector used_for_size(5);\n  const double tolerance(1.e-11);\n  pypp::check_with_random_values<1>(&energy_density<DataVector>,\n                                    \"Test_StressEnergy\", {\"energy_density\"},\n                                    {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(&momentum_density<DataVector>,\n                                    \"Test_StressEnergy\", {\"momentum_density\"},\n                                    {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(&stress_trace<DataVector>,\n                                    \"Test_StressEnergy\", {\"stress_trace\"},\n                                    {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      &stress_energy_tensor<DataVector>, \"Test_StressEnergy\",\n      {\"stress_energy_tensor\"}, {{{0.0, 1.0}}}, used_for_size, tolerance);\n\n  consistency_check(used_for_size);\n}\n\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n\nclass DataVector;\ntemplate <bool IsRelativistic>\nclass IdealFluid;\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.Tags\", \"[Unit][Hydro]\") {\n  TestHelpers::db::test_simple_tag<hydro::Tags::AlfvenSpeedSquared<DataVector>>(\n      \"AlfvenSpeedSquared\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::ComovingMagneticField<DataVector, 3, Frame::ElementLogical>>(\n      \"ElementLogical_ComovingMagneticField\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::ComovingMagneticFieldSquared<DataVector>>(\n      \"ComovingMagneticFieldSquared\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::DivergenceCleaningField<DataVector>>(\n      \"DivergenceCleaningField\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::EquationOfState<false, 2>>(\n      \"EquationOfState\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::EquationOfState<true, 3>>(\n      \"EquationOfState\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::LorentzFactor<DataVector>>(\n      \"LorentzFactor\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::InversePlasmaBeta<DataVector>>(\"InversePlasmaBeta\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::LorentzFactorSquared<DataVector>>(\"LorentzFactorSquared\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::MagneticField<DataVector, 3, Frame::Distorted>>(\n      \"Distorted_MagneticField\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::MagneticFieldDotSpatialVelocity<DataVector>>(\n      \"MagneticFieldDotSpatialVelocity\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::MagneticFieldOneForm<DataVector, 3, Frame::ElementLogical>>(\n      \"ElementLogical_MagneticFieldOneForm\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::MagneticFieldSquared<DataVector>>(\"MagneticFieldSquared\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::MagneticPressure<DataVector>>(\n      \"MagneticPressure\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::Pressure<DataVector>>(\n      \"Pressure\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::RestMassDensity<DataVector>>(\n      \"RestMassDensity\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::SoundSpeedSquared<DataVector>>(\n      \"SoundSpeedSquared\");\n  // [prefix_example]\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::SpatialVelocity<DataVector, 3, Frame::Grid>>(\n      \"Grid_SpatialVelocity\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::SpatialVelocityOneForm<\n      DataVector, 3, Frame::ElementLogical>>(\n      \"ElementLogical_SpatialVelocityOneForm\");\n  // [prefix_example]\n  TestHelpers::db::test_simple_tag<hydro::Tags::SpatialVelocitySquared<double>>(\n      \"SpatialVelocitySquared\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::SpatialVelocitySquared<DataVector>>(\n      \"SpatialVelocitySquared\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::SpecificEnthalpy<double>>(\n      \"SpecificEnthalpy\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::SpecificEnthalpy<DataVector>>(\n      \"SpecificEnthalpy\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::SpecificInternalEnergy<double>>(\n      \"SpecificInternalEnergy\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::SpecificInternalEnergy<DataVector>>(\n      \"SpecificInternalEnergy\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::Temperature<DataVector>>(\n      \"Temperature\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::TransportVelocity<DataVector, 3, Frame::Inertial>>(\n      \"TransportVelocity\");\n  TestHelpers::db::test_simple_tag<hydro::Tags::LowerSpatialFourVelocity<\n      DataVector, 3, Frame::ElementLogical>>(\"LowerSpatialFourVelocity\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::LorentzFactorTimesSpatialVelocity<DataVector, 3,\n                                                     Frame::ElementLogical>>(\n      \"LorentzFactorTimesSpatialVelocity\");\n  TestHelpers::db::test_simple_tag<\n      hydro::Tags::MassFlux<DataVector, 3, Frame::ElementLogical>>(\n      \"ElementLogical_MassFlux\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_Temperature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"IO/Connectivity.hpp\"\n#include \"IO/H5/AccessType.hpp\"\n#include \"IO/H5/CheckH5.hpp\"\n#include \"IO/H5/EosTable.hpp\"\n#include \"IO/H5/File.hpp\"\n#include \"IO/H5/Header.hpp\"\n#include \"IO/H5/Helpers.hpp\"\n#include \"IO/H5/OpenGroup.hpp\"\n#include \"IO/H5/SourceArchive.hpp\"\n#include \"IO/H5/Version.hpp\"\n#include \"IO/H5/Wrappers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/EquationOfState.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Factory.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/IdealFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/PolytropicFluid.hpp\"\n#include \"PointwiseFunctions/Hydro/EquationsOfState/Tabulated3d.hpp\"\n#include \"PointwiseFunctions/Hydro/Tags.hpp\"\n#include \"PointwiseFunctions/Hydro/Temperature.hpp\"\n#include \"PointwiseFunctions/Hydro/Units.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace hydro {\nnamespace {\ntemplate <typename EosType, size_t Dim>\nclass DummySolution\n    : public TemperatureInitialization<DummySolution<EosType, Dim>> {\n public:\n  DummySolution(EosType eos) : eos_(std::move(eos)) {}\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::RestMassDensity<DataType>> variables(\n      const tnsr::I<DataType, Dim>& coords,\n      tmpl::list<hydro::Tags::RestMassDensity<DataType>> /*meta*/) const {\n    if constexpr (std::is_same_v<DataType, double>) {\n      return {Scalar<DataType>(1.28e-3)};\n    } else {\n      return {Scalar<DataType>(get<0>(coords).size(), 1.28e-3)};\n    }\n  }\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::SpecificInternalEnergy<DataType>> variables(\n      const tnsr::I<DataType, Dim>& coords,\n      tmpl::list<hydro::Tags::SpecificInternalEnergy<DataType>> /*meta*/)\n      const {\n    if constexpr (std::is_same_v<DataType, double>) {\n      return {Scalar<DataType>(1.)};\n    } else {\n      return {Scalar<DataType>(get<0>(coords).size(), 1.)};\n    }\n  }\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::ElectronFraction<DataType>> variables(\n      const tnsr::I<DataType, Dim>& coords,\n      tmpl::list<hydro::Tags::ElectronFraction<DataType>> /*meta*/) const {\n    if constexpr (std::is_same_v<DataType, double>) {\n      return {Scalar<DataType>(0.1)};\n    } else {\n      return {Scalar<DataType>(get<0>(coords).size(), 0.1)};\n    }\n  }\n\n  template <typename DataType>\n  tuples::TaggedTuple<hydro::Tags::Temperature<DataType>> variables(\n      const tnsr::I<DataType, Dim>& coords,\n      tmpl::list<hydro::Tags::Temperature<DataType>> tvar) const {\n    return TemperatureInitialization<DummySolution<EosType, Dim>>::variables(\n        coords, tvar);\n  }\n\n  EosType const equation_of_state() const { return eos_; };\n\n private:\n  EosType eos_{};\n};\n}  // namespace\n\ntemplate <size_t Dim>\nvoid test() {\n  const tnsr::I<DataVector, Dim> coords_dv{10u, 3.0};\n  const tnsr::I<double, Dim> coords_double{13.0};\n\n  {\n    EquationsOfState::PolytropicFluid<true> polytrope{};\n    DummySolution<EquationsOfState::PolytropicFluid<true>, Dim> solution{\n        polytrope};\n\n    CHECK(get<hydro::Tags::Temperature<DataVector>>(solution.variables(\n              coords_dv, tmpl::list<hydro::Tags::Temperature<DataVector>>{})) ==\n          polytrope.temperature_from_density(Scalar<DataVector>(10u, 1.28e-3)));\n\n    CHECK(get<hydro::Tags::Temperature<double>>(solution.variables(\n              coords_double, tmpl::list<hydro::Tags::Temperature<double>>{})) ==\n          polytrope.temperature_from_density(Scalar<double>(1.28e-3)));\n  }\n\n  {\n    const auto ideal_fluid = EquationsOfState::IdealFluid<true>{1.5};\n    DummySolution<EquationsOfState::IdealFluid<true>, Dim> solution{\n        ideal_fluid};\n\n    CHECK(get<hydro::Tags::Temperature<DataVector>>(solution.variables(\n              coords_dv, tmpl::list<hydro::Tags::Temperature<DataVector>>{})) ==\n          ideal_fluid.temperature_from_density_and_energy(\n              Scalar<DataVector>(10u, 1.28e-3), Scalar<DataVector>(10u, 1.)));\n\n    CHECK(get<hydro::Tags::Temperature<double>>(solution.variables(\n              coords_double, tmpl::list<hydro::Tags::Temperature<double>>{})) ==\n          ideal_fluid.temperature_from_density_and_energy(\n              Scalar<double>(1.28e-3), Scalar<double>(1.)));\n  }\n\n  {\n    std::string h5_file_name{\n        unit_test_src_path() +\n        \"PointwiseFunctions/Hydro/EquationsOfState/dd2_unit_test.h5\"};\n\n    h5::H5File<h5::AccessType::ReadOnly> eos_file{h5_file_name};\n    const auto& compose_eos = eos_file.get<h5::EosTable>(\"/dd2\");\n\n    EquationsOfState::Tabulated3D<true> eos;\n    eos.initialize(compose_eos);\n\n    DummySolution<EquationsOfState::Tabulated3D<true>, Dim> solution{eos};\n\n    CHECK(get<hydro::Tags::Temperature<DataVector>>(solution.variables(\n              coords_dv, tmpl::list<hydro::Tags::Temperature<DataVector>>{})) ==\n          eos.temperature_from_density_and_energy(\n              Scalar<DataVector>(10u, 1.28e-3), Scalar<DataVector>(10u, 1.),\n              Scalar<DataVector>(10u, 0.1)));\n\n    CHECK(get<hydro::Tags::Temperature<double>>(solution.variables(\n              coords_double, tmpl::list<hydro::Tags::Temperature<double>>{})) ==\n          eos.temperature_from_density_and_energy(Scalar<double>(1.28e-3),\n                                                  Scalar<double>(1.),\n                                                  Scalar<double>(0.1)));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.Temperature\",\n                  \"[Unit][Hydro]\") {\n  test<1>();\n  test<2>();\n  test<3>();\n}\n\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_TestHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DotProduct.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/Hydro/TestHelpers.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <size_t Dim, typename DataType>\nvoid test_spatial_velocity(const gsl::not_null<std::mt19937*> generator,\n                           const DataType& used_for_size) {\n  const auto metric =\n      TestHelpers::gr::random_spatial_metric<Dim>(generator, used_for_size);\n  const auto lorentz_factor =\n      TestHelpers::hydro::random_lorentz_factor(generator, used_for_size);\n  const auto spatial_velocity =\n      TestHelpers::hydro::random_velocity(generator, lorentz_factor, metric);\n  CHECK_ITERABLE_APPROX(\n      get(dot_product(spatial_velocity, spatial_velocity, metric)),\n      1.0 - 1.0 / square(get(lorentz_factor)));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.TestHelpers\",\n                  \"[Unit][Hydro]\") {\n  MAKE_GENERATOR(generator);\n  const double d = std::numeric_limits<double>::signaling_NaN();\n  test_spatial_velocity<1>(&generator, d);\n  test_spatial_velocity<2>(&generator, d);\n  test_spatial_velocity<3>(&generator, d);\n  const DataVector dv(5);\n  test_spatial_velocity<1>(&generator, dv);\n  test_spatial_velocity<2>(&generator, dv);\n  test_spatial_velocity<3>(&generator, dv);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/Test_TransportVelocity.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Hydro/TransportVelocity.hpp\"\n\nnamespace hydro {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Hydro.TransportVelocity\",\n                  \"[Unit][Hydro]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/Hydro/\");\n  const DataVector used_for_size(5);\n  pypp::check_with_random_values<1>(\n      &transport_velocity<DataVector, 1, Frame::Inertial>, \"TransportVelocity\",\n      {\"transport_velocity\"}, {{{0.0, 1.0}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      &transport_velocity<DataVector, 3, Frame::Inertial>, \"TransportVelocity\",\n      {\"transport_velocity\"}, {{{0.0, 1.0}}}, used_for_size);\n  TestHelpers::db::test_compute_tag<\n      Tags::TransportVelocityCompute<DataVector, 3, Frame::Inertial>>(\n      \"TransportVelocity\");\n}\n\n}  // namespace hydro\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Hydro/TransportVelocity.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n# Functions testing TransportVelocity\n\n\ndef transport_velocity(spatial_velocity, lapse, shift):\n    return spatial_velocity * lapse - shift\n\n\n# End functions for testing TransportVelocity\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/InitialDataUtilities/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Test_InitialDataUtilities)\n\nset(LIBRARY_SOURCES\n  Tags/Test_InitialData.cpp\n  Test_NumericData.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PUBLIC\n  InitialDataUtilities\n  Parallel\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/InitialDataUtilities/Tags/Test_InitialData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/Tags/InitialData.hpp\"\n\nnamespace {\nvoid test_simpletag() {\n  TestHelpers::db::test_simple_tag<evolution::initial_data::Tags::InitialData>(\n      \"InitialData\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.InitialDataUtilities.Tags.InitialData\",\n    \"[Unit][PointwiseFunctions]\") {\n  test_simpletag();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/InitialDataUtilities/Test_NumericData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Evolution/Systems/ScalarWave/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"PointwiseFunctions/InitialDataUtilities/NumericData.hpp\"\n\ntemplate <typename Subclass>\nvoid test_numeric_data() {\n  const std::string file_name =\n      unit_test_src_path() + \"/Visualization/Python/VolTestData*.h5\";\n  const std::string option_string = \"FileGlob: \" + file_name +\n                                    \"\\nSubgroup: element_data\\n\"\n                                    \"ObservationStep: -1\\n\"\n                                    \"ExtrapolateIntoExcisions: False\\n\";\n  const auto created = TestHelpers::test_creation<Subclass>(option_string);\n  const elliptic::analytic_data::NumericData numeric_data{\n      file_name, \"element_data\", -1, false};\n  CHECK(created == numeric_data);\n  test_serialization(numeric_data);\n  test_copy_semantics(numeric_data);\n\n  const auto interpolated_data = numeric_data.variables(\n      tnsr::I<DataVector, 3>{\n          {{{0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 0.0, 0.0}}}},\n      tmpl::list<ScalarWave::Tags::Psi, ScalarWave::Tags::Phi<3>>{});\n  const auto& psi = get(get<ScalarWave::Tags::Psi>(interpolated_data));\n  CHECK(psi[0] == approx(-0.07059806932542323));\n  CHECK(psi[1] == approx(0.7869554122196492));\n  CHECK(psi[2] == approx(0.9876185584100299));\n  const auto& phi_y = get<1>(get<ScalarWave::Tags::Phi<3>>(interpolated_data));\n  CHECK(phi_y[0] == approx(1.0569673471948728));\n  CHECK(phi_y[1] == approx(0.6741524090220188));\n  CHECK(phi_y[2] == approx(0.2629752479142838));\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.InitialDataUtilities.NumericData\",\n                  \"[Unit][PointwiseFunctions]\") {\n  test_numeric_data<elliptic::analytic_data::NumericData>();\n  test_numeric_data<evolution::initial_data::NumericData>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/MathFunctions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_MathFunctions\")\n\nset(LIBRARY_SOURCES\n  Test_Gaussian.cpp\n  Test_PowX.cpp\n  Test_Sinusoid.cpp\n  Test_TensorProduct.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  LinearOperators\n  MathFunctions\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/MathFunctions/Python/Gaussian.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef centered_coordinates(coords, center):\n    return coords - center\n\n\ndef squared_distance_from_center(centered_coords, center):\n    return np.einsum(\"i,i\", centered_coords, centered_coords)\n\n\ndef call_operator(coords, amplitude, width, center):\n    one_over_width = 1.0 / width\n    distance = squared_distance_from_center(\n        centered_coordinates(coords, center), center\n    )\n    return amplitude * np.exp(-1.0 * distance * np.square(one_over_width))\n\n\ndef first_deriv(coords, amplitude, width, center):\n    one_over_width = 1.0 / width\n    result = (\n        -2.0\n        * np.square(one_over_width)\n        * call_operator(coords, amplitude, width, center)\n        * centered_coordinates(coords, center)\n    )\n    return result\n\n\ndef second_deriv(coords, amplitude, width, center):\n    one_over_width = 1.0 / width\n    result = np.einsum(\n        \"i,j\",\n        centered_coordinates(coords, center),\n        first_deriv(coords, amplitude, width, center),\n    )\n    result += np.eye(len(center)) * call_operator(\n        coords, amplitude, width, center\n    )\n    return result * -2.0 * np.square(one_over_width)\n\n\ndef third_deriv(coords, amplitude, width, center):\n    one_over_width = 1.0 / width\n    centered_coords = centered_coordinates(coords, center)\n    df = first_deriv(coords, amplitude, width, center)\n    d2f = second_deriv(coords, amplitude, width, center)\n    kronecker_delta = np.eye(len(center))\n    result = np.einsum(\"j,ik\", centered_coords, d2f)\n    result += np.einsum(\"ij,k\", kronecker_delta, df)\n    result += np.einsum(\"jk,i\", kronecker_delta, df)\n    return result * -2.0 * np.square(one_over_width)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/MathFunctions/Python/PowX.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef call_operator(coords, power):\n    return np.power(coords, power)[0]\n\n\ndef first_deriv(coords, power):\n    if power == 0.0:\n        return np.array([0.0])\n    else:\n        return power * np.power(coords, power - 1.0)\n\n\ndef second_deriv(coords, power):\n    if power == 0.0 or power == 1.0:\n        return np.array([[0.0]])\n    else:\n        return np.array([(power - 1.0) * power * np.power(coords, power - 2.0)])\n\n\ndef third_deriv(coords, power):\n    if power == 0.0 or power == 1.0 or power == 2.0:\n        return np.array([[[0.0]]])\n    else:\n        return np.array(\n            [\n                [\n                    (power - 2.0)\n                    * (power - 1.0)\n                    * power\n                    * np.power(coords, power - 3.0)\n                ]\n            ]\n        )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/MathFunctions/Python/Sinusoid.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef call_operator(coords, amplitude, wavenumber, phase):\n    return amplitude * np.sin(wavenumber * coords + phase)[0]\n\n\ndef first_deriv(coords, amplitude, wavenumber, phase):\n    return amplitude * wavenumber * np.cos(wavenumber * coords + phase)\n\n\ndef second_deriv(coords, amplitude, wavenumber, phase):\n    return np.array(\n        [\n            -amplitude\n            * np.square(wavenumber)\n            * np.sin(wavenumber * coords + phase)\n        ]\n    )\n\n\ndef third_deriv(coords, amplitude, wavenumber, phase):\n    return np.array(\n        [\n            [\n                -amplitude\n                * wavenumber\n                * np.square(wavenumber)\n                * np.cos(wavenumber * coords + phase)\n            ]\n        ]\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/MathFunctions/Test_Gaussian.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/MathFunctions/TestHelpers.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction;\n\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace {\ntemplate <size_t VolumeDim, typename DataType, typename Fr>\nvoid test_gaussian_random(const DataType& used_for_size) {\n  register_classes_with_charm<MathFunctions::Gaussian<VolumeDim, Fr>>();\n\n  // Generate the amplitude and width\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1, 1);\n  std::uniform_real_distribution<> positive_dis(0, 1);\n\n  const double amplitude = positive_dis(gen);\n  // If the width is too small then the terms in the second derivative\n  // can become very large and fail the test due to rounding errors.\n  const double width = positive_dis(gen) + 0.5;\n\n  // Generate the center\n  std::array<double, VolumeDim> center{};\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    gsl::at(center, i) = real_dis(gen);\n  }\n\n  MathFunctions::Gaussian<VolumeDim, Fr> gauss{amplitude, width, center};\n  CHECK(gauss == *(gauss.get_clone()));\n  CHECK_FALSE(gauss != gauss);\n  test_copy_semantics(gauss);\n  auto gauss_for_move = gauss;\n  test_move_semantics(std::move(gauss_for_move), gauss);\n\n  TestHelpers::MathFunctions::check(std::move(gauss), \"Gaussian\", used_for_size,\n                                    {{{-1.0, 1.0}}}, amplitude, width, center);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.MathFunctions.Gaussian\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const DataVector dv{5};\n\n  pypp::SetupLocalPythonEnvironment{\"PointwiseFunctions/MathFunctions/Python\"};\n\n  using VolumeDims = tmpl::integral_list<size_t, 1, 2, 3>;\n  using Frames = tmpl::list<Frame::Grid, Frame::Inertial>;\n\n  tmpl::for_each<VolumeDims>([&dv](auto dim_v) {\n    using VolumeDim = typename decltype(dim_v)::type;\n    tmpl::for_each<Frames>([&dv](auto frame_v) {\n      using Fr = typename decltype(frame_v)::type;\n      test_gaussian_random<VolumeDim::value, DataVector, Fr>(dv);\n      test_gaussian_random<VolumeDim::value, double, Fr>(\n          std::numeric_limits<double>::signaling_NaN());\n    });\n  });\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.MathFunctions.Gaussian.Factory\",\n                  \"[PointwiseFunctions][Unit]\") {\n  TestHelpers::test_factory_creation<\n      MathFunction<1, Frame::Inertial>,\n      MathFunctions::Gaussian<1, Frame::Inertial>>(\n      \"Gaussian:\\n\"\n      \"  Amplitude: 3\\n\"\n      \"  Width: 2\\n\"\n      \"  Center: -9\");\n\n  const double amplitude_3d{4.0};\n  const double width_3d{1.5};\n  const std::array<double, 3> center_3d{{1.1, -2.2, 3.3}};\n  const MathFunctions::Gaussian<3, Frame::Inertial> gauss_3d{\n      amplitude_3d, width_3d, center_3d};\n  const auto created_gauss =\n      TestHelpers::test_creation<MathFunctions::Gaussian<3, Frame::Inertial>>(\n          \"Amplitude: 4.0\\n\"\n          \"Width: 1.5\\n\"\n          \"Center: [1.1, -2.2, 3.3]\");\n  CHECK(created_gauss == gauss_3d);\n  const auto created_gauss_mathfunction = TestHelpers::test_factory_creation<\n      MathFunction<3, Frame::Inertial>,\n      MathFunctions::Gaussian<3, Frame::Inertial>>(\n      \"Gaussian:\\n\"\n      \"  Amplitude: 4.0\\n\"\n      \"  Width: 1.5\\n\"\n      \"  Center: [1.1, -2.2, 3.3]\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/MathFunctions/Test_PowX.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/MathFunctions/TestHelpers.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction;\n\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace {\ntemplate <size_t VolumeDim, typename DataType, typename Fr>\nvoid test_pow_x_random(const DataType& used_for_size) {\n  register_classes_with_charm<MathFunctions::PowX<VolumeDim, Fr>>();\n\n  const MathFunctions::Sinusoid<VolumeDim, Fr> sinusoid{};\n  for (int power = -5; power < 6; ++power) {\n    MathFunctions::PowX<VolumeDim, Fr> pow_x{power};\n\n    CHECK(pow_x == *(pow_x.get_clone()));\n    CHECK(pow_x != *(sinusoid.get_clone()));\n    CHECK_FALSE(pow_x != pow_x);\n    test_copy_semantics(pow_x);\n    auto pow_for_move = pow_x;\n    test_move_semantics(std::move(pow_for_move), pow_x);\n\n    TestHelpers::MathFunctions::check(std::move(pow_x), \"PowX\", used_for_size,\n                                      {{{-5.0, 5.0}}},\n                                      static_cast<double>(power));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.MathFunctions.PowX\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const DataVector dv{5};\n\n  pypp::SetupLocalPythonEnvironment{\"PointwiseFunctions/MathFunctions/Python\"};\n\n  using Frames = tmpl::list<Frame::Grid, Frame::Inertial>;\n  tmpl::for_each<Frames>([&dv](auto frame_v) {\n    using Fr = typename decltype(frame_v)::type;\n    test_pow_x_random<1, DataVector, Fr>(dv);\n    test_pow_x_random<1, double, Fr>(\n        std::numeric_limits<double>::signaling_NaN());\n  });\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.MathFunctions.PowX.Factory\",\n                  \"[PointwiseFunctions][Unit]\") {\n  TestHelpers::test_factory_creation<MathFunction<1, Frame::Inertial>,\n                                     MathFunctions::PowX<1, Frame::Inertial>>(\n      \"PowX:\\n    Power: 3\");\n  TestHelpers::test_factory_creation<MathFunction<1, Frame::Inertial>,\n                                     MathFunctions::PowX<1, Frame::Inertial>>(\n      \"PowX:\\n    Power: 3\");\n  // Catch requires us to have at least one CHECK in each test\n  // The Unit.PointwiseFunctions.MathFunctions.PowX.Factory does not need to\n  // check anything\n  CHECK(true);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/MathFunctions/Test_Sinusoid.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <limits>\n#include <random>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/MathFunctions/TestHelpers.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction;\n\nnamespace Frame {\nstruct Grid;\nstruct Inertial;\n}  // namespace Frame\n\nnamespace {\ntemplate <size_t VolumeDim, typename DataType, typename Fr>\nvoid test_sinusoid_random(const DataType& used_for_size) {\n  register_classes_with_charm<MathFunctions::Sinusoid<VolumeDim, Fr>>();\n\n  // Generate the amplitude and width\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<> real_dis(-1, 1);\n\n  const double amplitude = real_dis(gen);\n  // If the width is too small then the terms in the second derivative\n  // can become very large and fail the test due to rounding errors.\n  const double wavenumber = real_dis(gen);\n\n  // Generate the center\n  const double phase = real_dis(gen);\n\n  MathFunctions::Sinusoid<VolumeDim, Fr> sinusoid{amplitude, wavenumber, phase};\n  const MathFunctions::Gaussian<VolumeDim, Fr> gaussian{};\n  CHECK_FALSE(sinusoid == *(gaussian.get_clone()));\n  CHECK(sinusoid == *(sinusoid.get_clone()));\n  CHECK_FALSE(sinusoid != sinusoid);\n  test_copy_semantics(sinusoid);\n  auto sinusoid_for_move = sinusoid;\n  test_move_semantics(std::move(sinusoid_for_move), sinusoid);\n\n  TestHelpers::MathFunctions::check(std::move(sinusoid), \"Sinusoid\",\n                                    used_for_size, {{{-1.0, 1.0}}}, amplitude,\n                                    wavenumber, phase);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.MathFunctions.Sinusoid\",\n                  \"[PointwiseFunctions][Unit]\") {\n  const DataVector dv{5};\n\n  pypp::SetupLocalPythonEnvironment{\"PointwiseFunctions/MathFunctions/Python\"};\n\n  using Frames = tmpl::list<Frame::Grid, Frame::Inertial>;\n  tmpl::for_each<Frames>([&dv](auto frame_v) {\n    using Fr = typename decltype(frame_v)::type;\n    test_sinusoid_random<1, DataVector, Fr>(dv);\n    test_sinusoid_random<1, double, Fr>(\n        std::numeric_limits<double>::signaling_NaN());\n  });\n}\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.MathFunctions.Sinusoid.Factory\",\n                  \"[PointwiseFunctions][Unit]\") {\n  TestHelpers::test_factory_creation<\n      MathFunction<1, Frame::Inertial>,\n      MathFunctions::Sinusoid<1, Frame::Inertial>>(\n      \"Sinusoid:\\n\"\n      \"  Amplitude: 3\\n\"\n      \"  Wavenumber: 2\\n\"\n      \"  Phase: -9\");\n  TestHelpers::test_factory_creation<\n      MathFunction<1, Frame::Inertial>,\n      MathFunctions::Sinusoid<1, Frame::Inertial>>(\n      \"Sinusoid:\\n\"\n      \"  Amplitude: 3\\n\"\n      \"  Wavenumber: 2\\n\"\n      \"  Phase: -9\");\n  // Catch requires us to have at least one CHECK in each test\n  // The Unit.PointwiseFunctions.MathFunctions.Sinusoid.Factory does not need to\n  // check anything\n  CHECK(true);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/MathFunctions/Test_TensorProduct.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <pup.h>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/PartialDerivatives.tpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Gaussian.hpp\"\n#include \"PointwiseFunctions/MathFunctions/MathFunction.hpp\"\n#include \"PointwiseFunctions/MathFunctions/PowX.hpp\"\n#include \"PointwiseFunctions/MathFunctions/Sinusoid.hpp\"\n#include \"PointwiseFunctions/MathFunctions/TensorProduct.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass DataVector;\ntemplate <size_t VolumeDim, typename Fr>\nclass MathFunction;\n\nnamespace {\nusing Affine = domain::CoordinateMaps::Affine;\nusing Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\nusing Affine3D = domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n\ntemplate <size_t VolumeDim>\nauto make_affine_map();\n\ntemplate <>\nauto make_affine_map<1>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine{-1.0, 1.0, -0.3, 0.7});\n}\n\ntemplate <>\nauto make_affine_map<2>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine2D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55}});\n}\n\ntemplate <>\nauto make_affine_map<3>() {\n  return domain::make_coordinate_map<Frame::ElementLogical, Frame::Inertial>(\n      Affine3D{Affine{-1.0, 1.0, -0.3, 0.7}, Affine{-1.0, 1.0, 0.3, 0.55},\n               Affine{-1.0, 1.0, 2.3, 2.8}});\n}\n\ntemplate <typename T, size_t VolumeDim>\nScalar<T> expected_value(const tnsr::I<T, VolumeDim>& x,\n                         const std::array<size_t, VolumeDim>& powers,\n                         const double scale) {\n  auto result = make_with_value<Scalar<T>>(x, scale);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    result.get() *= pow(x.get(d), gsl::at(powers, d));\n  }\n  return result;\n}\n\ntemplate <typename T, size_t VolumeDim>\ntnsr::i<T, VolumeDim> expected_first_derivs(\n    const tnsr::I<T, VolumeDim>& x, const std::array<size_t, VolumeDim>& powers,\n    const double scale) {\n  auto result = make_with_value<tnsr::i<T, VolumeDim>>(x, scale);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const size_t p = gsl::at(powers, d);\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      if (d == i) {\n        if (0 == p) {\n          result.get(i) = 0.0;\n        } else {\n          result.get(i) *= p * pow(x.get(d), p - 1);\n        }\n      } else {\n        result.get(i) *= pow(x.get(d), p);\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <typename T, size_t VolumeDim>\ntnsr::ii<T, VolumeDim> expected_second_derivs(\n    const tnsr::I<T, VolumeDim>& x, const std::array<size_t, VolumeDim>& powers,\n    const double scale) {\n  auto result = make_with_value<tnsr::ii<T, VolumeDim>>(x, scale);\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    const size_t p = gsl::at(powers, d);\n    for (size_t i = 0; i < VolumeDim; ++i) {\n      if (d == i) {\n        if (2 > p) {\n          result.get(i, i) = 0.0;\n        } else {\n          result.get(i, i) *= p * (p - 1) * pow(x.get(d), p - 2);\n        }\n      } else {\n        result.get(i, i) *= pow(x.get(d), p);\n      }\n      for (size_t j = i + 1; j < VolumeDim; ++j) {\n        if (d == j or d == i) {\n          if (0 == p) {\n            result.get(i, j) = 0.0;\n          } else {\n            result.get(i, j) *= p * pow(x.get(d), p - 1);\n          }\n        } else {\n          result.get(i, j) *= pow(x.get(d), p);\n        }\n      }\n    }\n  }\n  return result;\n}\n\ntemplate <size_t VolumeDim>\nvoid test_tensor_product(\n    const Mesh<VolumeDim>& mesh, const double scale,\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, VolumeDim>&&\n        functions,\n    const std::array<size_t, VolumeDim>& powers) {\n  const auto coordinate_map = make_affine_map<VolumeDim>();\n  const auto x = coordinate_map(logical_coordinates(mesh));\n  MathFunctions::TensorProduct<VolumeDim> f(scale, std::move(functions));\n\n  CHECK_FALSE(f != f);\n  test_copy_semantics(f);\n  auto f_for_move = f;\n  test_move_semantics(std::move(f_for_move), f);\n\n  CHECK_ITERABLE_APPROX(f(x), expected_value(x, powers, scale));\n  CHECK_ITERABLE_APPROX(f.first_derivatives(x),\n                        expected_first_derivs(x, powers, scale));\n  CHECK_ITERABLE_APPROX(f.second_derivatives(x),\n                        expected_second_derivs(x, powers, scale));\n  tnsr::I<double, VolumeDim> point{2.2};\n  for (size_t d = 0; d < VolumeDim; ++d) {\n    point.get(d) += d * 1.7;\n  };\n  CHECK_ITERABLE_APPROX(f(point), expected_value(point, powers, scale));\n  CHECK_ITERABLE_APPROX(f.first_derivatives(point),\n                        expected_first_derivs(point, powers, scale));\n  CHECK_ITERABLE_APPROX(f.second_derivatives(point),\n                        expected_second_derivs(point, powers, scale));\n}\n\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n  static std::string name() { return \"Var1\"; }\n};\n\ntemplate <size_t VolumeDim>\nstruct Var2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, VolumeDim, Frame::Inertial>;\n  static std::string name() { return \"Var2\"; }\n};\n\ntemplate <size_t VolumeDim>\nusing TwoVars = tmpl::list<Var1, Var2<VolumeDim>>;\n\ntemplate <size_t VolumeDim>\nvoid test_with_numerical_derivatives(\n    const Mesh<VolumeDim>& mesh, const double scale,\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, VolumeDim>&&\n        functions) {\n  const auto coordinate_map = make_affine_map<VolumeDim>();\n  Variables<TwoVars<VolumeDim>> u(mesh.number_of_grid_points());\n  const auto xi = logical_coordinates(mesh);\n  const auto x = coordinate_map(xi);\n  const auto inv_jacobian = coordinate_map.inv_jacobian(xi);\n  MathFunctions::TensorProduct<VolumeDim> f(scale, std::move(functions));\n  get<Var1>(u) = f(x);\n  get<Var2<VolumeDim>>(u) = f.first_derivatives(x);\n  const auto du =\n      partial_derivatives<TwoVars<VolumeDim>>(u, mesh, inv_jacobian);\n  const auto& dVar1 =\n      get<Tags::deriv<Var1, tmpl::size_t<VolumeDim>, Frame::Inertial>>(du);\n  Approx custom_approx = Approx::custom().epsilon(1.e-6);\n  CHECK_ITERABLE_CUSTOM_APPROX(f.first_derivatives(x), dVar1, custom_approx);\n  const auto& dVar2 = get<\n      Tags::deriv<Var2<VolumeDim>, tmpl::size_t<VolumeDim>, Frame::Inertial>>(\n      du);\n  const auto d2f = f.second_derivatives(x);\n  for (size_t i = 0; i < VolumeDim; ++i) {\n    for (size_t j = 0; j < VolumeDim; ++j) {\n      const auto& d2f_ij = d2f.get(i, j);\n      const auto& dVar2_ij = dVar2.get(i, j);\n      CHECK_ITERABLE_CUSTOM_APPROX(d2f_ij, dVar2_ij, custom_approx);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.MathFunctions.TensorProduct\",\n                  \"[PointwiseFunctions][Unit]\") {\n  for (size_t a = 0; a < 5; ++a) {\n    std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 1> functions{\n        {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a)}};\n    test_tensor_product(Mesh<1>{4, Spectral::Basis::Legendre,\n                                Spectral::Quadrature::GaussLobatto},\n                        1.5, std::move(functions), {{a}});\n    for (size_t b = 0; b < 4; ++b) {\n      std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 2>\n          functions_2d{\n              {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a),\n               std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(b)}};\n      test_tensor_product(Mesh<2>{{{4, 3}},\n                                  Spectral::Basis::Legendre,\n                                  Spectral::Quadrature::GaussLobatto},\n                          2.5, std::move(functions_2d), {{a, b}});\n      for (size_t c = 0; c < 3; ++c) {\n        std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 3>\n            functions_3d{\n                {std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(a),\n                 std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(b),\n                 std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(c)}};\n        test_tensor_product(Mesh<3>{{{4, 3, 5}},\n                                    Spectral::Basis::Legendre,\n                                    Spectral::Quadrature::GaussLobatto},\n                            3.5, std::move(functions_3d), {{a, b, c}});\n      }\n    }\n  }\n\n  std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 1> sinusoid{\n      {std::make_unique<MathFunctions::Sinusoid<1, Frame::Inertial>>(1.0, 1.0,\n                                                                     -1.0)}};\n\n  test_with_numerical_derivatives(\n      Mesh<1>{8, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto},\n      1.5, std::move(sinusoid));\n\n  std::array<std::unique_ptr<MathFunction<1, Frame::Inertial>>, 3> generic_3d{\n      {std::make_unique<MathFunctions::Sinusoid<1, Frame::Inertial>>(1.0, 1.0,\n                                                                     -1.0),\n       std::make_unique<MathFunctions::Gaussian<1, Frame::Inertial>>(1.0, 1.0,\n                                                                     0.4),\n       std::make_unique<MathFunctions::PowX<1, Frame::Inertial>>(2)}};\n\n  test_with_numerical_derivatives(\n      Mesh<3>{8, Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto},\n      1.5, std::move(generic_3d));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Punctures/AdmIntegrals.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef adm_mass_integrand(field, alpha, beta):\n    return 1.0 / (2.0 * np.pi) * beta * (alpha * (1.0 + field) + 1.0) ** (-7)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Punctures/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Test_PuncturesPointwiseFunctions)\n\nset(LIBRARY_SOURCES\n  Test_AdmIntegrals.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  Spectral\n  PuncturesPointwiseFunctions\n  Utilities\n  )\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Punctures/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.PointwiseFunctions.Punctures.Python\"\n  Test_Bindings.py\n  \"Unit;PointwiseFunctions\"\n  PyPuncturesPointwiseFunctions)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Punctures/Python/Test_Bindings.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\n\nfrom spectre.DataStructures import DataVector\nfrom spectre.DataStructures.Tensor import Scalar\nfrom spectre.PointwiseFunctions.Punctures import adm_mass_integrand\n\n\nclass TestBindings(unittest.TestCase):\n    def test_adm_mass_integrand(self):\n        field = np.random.rand(1, 5)\n        alpha = np.random.rand(1, 5)\n        beta = np.random.rand(1, 5)\n        result = adm_mass_integrand(field, alpha, beta)\n        npt.assert_allclose(\n            np.array(result),\n            1.0 / (2.0 * np.pi) * beta * (alpha * (1.0 + field) + 1.0) ** (-7),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Punctures/Test_AdmIntegrals.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Punctures/AdmIntegrals.hpp\"\n\nnamespace Punctures {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Punctures.AdmIntegrals\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Punctures\"};\n  const DataVector used_for_size{5};\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(gsl::not_null<Scalar<DataVector>*>,\n                           const Scalar<DataVector>&, const Scalar<DataVector>&,\n                           const Scalar<DataVector>&)>(&adm_mass_integrand),\n      \"AdmIntegrals\", {\"adm_mass_integrand\"}, {{{-1., 1.}}}, used_for_size);\n\n  TestHelpers::db::test_simple_tag<Tags::AdmMassIntegrand>(\"AdmMassIntegrand\");\n  TestHelpers::db::test_compute_tag<Tags::AdmMassIntegrandCompute>(\n      \"AdmMassIntegrand\");\n}\n\n}  // namespace Punctures\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\nset(LIBRARY \"Test_ScalarTensorPointwise\")\n\nset(LIBRARY_SOURCES\n  Test_ConstraintDampingTags.cpp\n  Test_ConstraintGammas.cpp\n  Test_RampUpFunction.cpp\n  Test_ScalarCharge.cpp\n  Test_ScalarSource.cpp\n  Test_SourceTags.cpp\n  Test_StressEnergy.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstraintDamping\n  Framework\n  ScalarGaussBonnetPointwise\n  ScalarTensor\n  ScalarTensorPointwise\n  )\n\nadd_subdirectory(ScalarGaussBonnet)\nadd_subdirectory(Xcts)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/RampUpFunction.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef nonic_ramp_function(time, t_start, t_ramp):\n    if time < t_start:\n        ramp_factor = 0.0\n    elif time < t_ramp + t_start:\n        t_star = (time - t_start) / t_ramp\n        ramp_factor = np.power(t_star, 5) * (\n            126\n            + t_star * (-420 + t_star * (540 + t_star * (-315 + 70 * t_star)))\n        )\n    else:\n        ramp_factor = 1.0\n    return ramp_factor\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/ScalarCharge.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef scalar_charge_integrand(phi, unit_normal_vector):\n    result = np.dot(phi, unit_normal_vector)\n    result /= -4.0 * np.pi\n    return result\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\nset(LIBRARY \"Test_ScalarGaussBonnetPointwise\")\n\nset(LIBRARY_SOURCES\n  Test_CouplingParameters.cpp\n  Test_ScalarSource.cpp\n  Test_Tags.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ConstraintDamping\n  Framework\n  ScalarGaussBonnetPointwise\n  ScalarTensor\n  ScalarTensorPointwise\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/Test_CouplingParameters.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/CouplingParameters.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.Sgb.CouplingParamters\",\n                  \"[Unit][PointwiseFunctions]\") {\n  const ScalarTensor::CouplingParameterOptions params{-2.0, 3.1, -40.2};\n  CHECK(params == ScalarTensor::CouplingParameterOptions{-2.0, 3.1, -40.2});\n  CHECK(params != ScalarTensor::CouplingParameterOptions{5.2, 3.1, -40.2});\n  CHECK(params != ScalarTensor::CouplingParameterOptions{-2.0, 31.0, -40.2});\n  CHECK(params != ScalarTensor::CouplingParameterOptions{-2.0, 3.1, 4.2});\n  test_serialization(params);\n  test_copy_semantics(params);\n  const auto created_params =\n      TestHelpers::test_creation<ScalarTensor::CouplingParameterOptions>(\n          \"Linear: -2.0\\n\"\n          \"Quadratic: 3.1\\n\"\n          \"Quartic: -40.2\\n\");\n  CHECK(created_params == params);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/Test_ScalarSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/ScalarSource.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nnamespace detail {\nScalar<DataVector> test_gauss_bonnet_scalar_source(\n    const Scalar<DataVector>& weyl_electric_scalar,\n    const Scalar<DataVector>& weyl_magnetic_scalar,\n    const Scalar<DataVector>& psi,\n    const std::array<double, 3>& coupling_parameters, const double mass_psi,\n    const double start_time, const double ramp_time, const double time) {\n  const ScalarTensor::CouplingParameterOptions coupling_parameters_opts{\n      gsl::at(coupling_parameters, 0), gsl::at(coupling_parameters, 1),\n      gsl::at(coupling_parameters, 2)};\n  return ::ScalarTensor::gauss_bonnet_scalar_source(\n      weyl_electric_scalar, weyl_magnetic_scalar, psi, coupling_parameters_opts,\n      mass_psi, std::pair<double, double>{start_time, ramp_time}, time);\n}\n\nScalar<DataVector> test_multiply_by_negative_deriv_of_coupling_func(\n    const Scalar<DataVector>& psi,\n    const std::array<double, 3>& coupling_parameters, const double start_time,\n    const double ramp_time, const double time) {\n  Scalar<DataVector> result = make_with_value<Scalar<DataVector>>(psi, 1.0);\n  const ScalarTensor::CouplingParameterOptions coupling_parameters_opts{\n      gsl::at(coupling_parameters, 0), gsl::at(coupling_parameters, 1),\n      gsl::at(coupling_parameters, 2)};\n  ::ScalarTensor::multiply_by_negative_deriv_of_coupling_func(\n      make_not_null(&result), psi, coupling_parameters_opts,\n      std::pair<double, double>{start_time, ramp_time}, time);\n  return result;\n}\n\nScalar<DataVector> test_multiply_by_negative_second_deriv_of_coupling_func(\n    const Scalar<DataVector>& psi,\n    const std::array<double, 3>& coupling_parameters, const double start_time,\n    const double ramp_time, const double time) {\n  Scalar<DataVector> result = make_with_value<Scalar<DataVector>>(psi, 1.0);\n  const ScalarTensor::CouplingParameterOptions coupling_parameters_opts{\n      gsl::at(coupling_parameters, 0), gsl::at(coupling_parameters, 1),\n      gsl::at(coupling_parameters, 2)};\n  ::ScalarTensor::multiply_by_negative_second_deriv_of_coupling_func(\n      make_not_null(&result), psi, coupling_parameters_opts,\n      std::pair<double, double>{start_time, ramp_time}, time);\n  return result;\n}\n\n}  // namespace detail\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.Sgb.ScalarSource\",\n                  \"[Unit][PointwiseFunctions]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/ScalarTensor\"};\n\n  TestHelpers::db::test_compute_tag<ScalarTensor::Tags::ScalarSourceCompute>(\n      \"ScalarSource\");\n\n  pypp::check_with_random_values<\n      1,\n      Scalar<DataVector> (*)(\n          const Scalar<DataVector>&, const Scalar<DataVector>&,\n          const Scalar<DataVector>&, const std::array<double, 3>&, const double,\n          const double, const double, const double),\n      DataVector, nullptr>(&detail::test_gauss_bonnet_scalar_source, \"Sources\",\n                           {\"gauss_bonnet_scalar_source\"}, {{{1.0e-2, 0.5}}},\n                           DataVector{5});\n\n  pypp::check_with_random_values<1,\n                                 Scalar<DataVector> (*)(\n                                     const Scalar<DataVector>&,\n                                     const std::array<double, 3>&, const double,\n                                     const double, const double),\n                                 DataVector, nullptr>(\n      &detail::test_multiply_by_negative_deriv_of_coupling_func, \"Sources\",\n      {\"negative_deriv_of_coupling_func\"}, {{{1.0e-2, 0.5}}}, DataVector{5});\n\n  pypp::check_with_random_values<1,\n                                 Scalar<DataVector> (*)(\n                                     const Scalar<DataVector>&,\n                                     const std::array<double, 3>&, const double,\n                                     const double, const double),\n                                 DataVector, nullptr>(\n      &detail::test_multiply_by_negative_second_deriv_of_coupling_func,\n      \"Sources\", {\"negative_second_deriv_of_coupling_func\"}, {{{1.0e-2, 0.5}}},\n      DataVector{5});\n\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/RampUpFunction.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarGaussBonnet/Tags.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.Sgb.Tags\",\n                  \"[Unit][PointwiseFunctions]\") {\n  TestHelpers::db::test_simple_tag<ScalarTensor::Tags::CouplingParameters>(\n      \"CouplingParameters\");\n  TestHelpers::test_option_tag<ScalarTensor::OptionTags::CouplingParameters>(\n      \"Linear: 2.0\\n\"\n      \"Quadratic: 0.1\\n\"\n      \"Quartic: 0.5\\n\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Sources.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom RampUpFunction import nonic_ramp_function\n\n\ndef mass_source(psi, mass_psi):\n    return mass_psi * mass_psi * psi\n\n\ndef vacuum_gb_scalar(\n    weyl_electric_scalar,\n    weyl_magnetic_scalar,\n):\n    return 8 * (weyl_electric_scalar - weyl_magnetic_scalar)\n\n\ndef vacuum_pontryagin_scalar(\n    weyl_electric, weyl_magnetic, inverse_spatial_metric\n):\n    pontryagin_scalar = -16.0 * np.trace(\n        weyl_electric\n        @ inverse_spatial_metric\n        @ weyl_magnetic\n        @ inverse_spatial_metric\n    )\n\n    return pontryagin_scalar\n\n\ndef first_derivative_of_coupling_function(\n    psi,\n    coupling_parameters,\n):\n    linear_coupling = coupling_parameters[0]\n    quadratic_coupling = coupling_parameters[1]\n    quartic_coupling = coupling_parameters[2]\n\n    df = 0.25 * (\n        linear_coupling\n        + quadratic_coupling * psi\n        + quartic_coupling * np.power(psi, 3)\n    )\n\n    return df\n\n\ndef second_derivative_of_coupling_function(\n    psi,\n    coupling_parameters,\n):\n    quadratic_coupling = coupling_parameters[1]\n    quartic_coupling = coupling_parameters[2]\n\n    ddf = 0.25 * (\n        quadratic_coupling + 3.0 * quartic_coupling * np.power(psi, 2)\n    )\n\n    return ddf\n\n\ndef negative_deriv_of_coupling_func(\n    psi, coupling_parameters, start_time, ramp_time, time\n):\n    ramp_up_factor = nonic_ramp_function(\n        time=time, t_start=start_time, t_ramp=ramp_time\n    )\n    ramped_up_coupling_parameters = ramp_up_factor * np.array(\n        coupling_parameters\n    )\n    df = first_derivative_of_coupling_function(\n        psi=psi,\n        coupling_parameters=ramped_up_coupling_parameters,\n    )\n    result = -df\n    return result\n\n\ndef negative_second_deriv_of_coupling_func(\n    psi, coupling_parameters, start_time, ramp_time, time\n):\n    ramp_up_factor = nonic_ramp_function(\n        time=time, t_start=start_time, t_ramp=ramp_time\n    )\n    ramped_up_coupling_parameters = ramp_up_factor * np.array(\n        coupling_parameters\n    )\n    ddf = second_derivative_of_coupling_function(\n        psi=psi,\n        coupling_parameters=ramped_up_coupling_parameters,\n    )\n    result = -ddf\n    return result\n\n\ndef gauss_bonnet_scalar_source(\n    weyl_electric_scalar,\n    weyl_magnetic_scalar,\n    psi,\n    coupling_parameters,\n    mass_psi,\n    t_start,\n    t_ramp,\n    time,\n):\n    ramp_up_factor = nonic_ramp_function(\n        time=time, t_start=t_start, t_ramp=t_ramp\n    )\n    ramped_up_coupling_parameters = ramp_up_factor * np.array(\n        coupling_parameters\n    )\n    scalar_source = vacuum_gb_scalar(\n        weyl_electric_scalar=weyl_electric_scalar,\n        weyl_magnetic_scalar=weyl_magnetic_scalar,\n    )\n    # The source function has a minus sign factor in the curvature coupling\n    # function\n    scalar_source *= -first_derivative_of_coupling_function(\n        psi=psi, coupling_parameters=ramped_up_coupling_parameters\n    )\n    scalar_source += mass_source(psi=psi, mass_psi=mass_psi)\n\n    return scalar_source\n\n\ndef add_scalar_source_to_dt_pi_scalar(scalar_source, lapse):\n    return 0.1234 + lapse * scalar_source\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/StressEnergy.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef trace_reversed_stress_energy(pi_scalar, phi_scalar, lapse, shift):\n    # Define the different components\n    rho = pi_scalar * pi_scalar\n    j_vec = -pi_scalar * phi_scalar\n    S = np.outer(phi_scalar, phi_scalar)\n\n    # Construct the trace-reversed stress energy tensor\n    stress_energy = np.zeros((4, 4))\n\n    # 00-component\n    stress_energy[0, 0] = (\n        np.power(lapse, 2) * rho\n        + 2.0 * lapse * np.dot(shift, j_vec)\n        + shift @ S @ shift\n    )\n\n    # 0i-component\n    stress_energy[0, 1:] = lapse * j_vec + shift @ S\n    stress_energy[1:, 0] = np.transpose(stress_energy[0, 1:])\n\n    # ij-component\n    stress_energy[1:, 1:] = S\n\n    return stress_energy\n\n\ndef add_stress_energy_term_to_dt_pi(trace_reversed_stress_energy, lapse):\n    return 0.1234 - 16.0 * np.pi * lapse * trace_reversed_stress_energy\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Test_ConstraintDampingTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ConstraintDampingTags.hpp\"\n\nnamespace {\nstruct ArbitraryFrame;\n\ntemplate <size_t Dim, typename Fr>\nvoid test_tags() {\n  TestHelpers::db::test_simple_tag<\n      ScalarTensor::Tags::DampingFunctionGamma1<Dim, Fr>>(\n      \"DampingFunctionGamma1\");\n  TestHelpers::db::test_simple_tag<\n      ScalarTensor::Tags::DampingFunctionGamma2<Dim, Fr>>(\n      \"DampingFunctionGamma2\");\n}\n\ntemplate <size_t Dim, typename Fr>\nvoid test_option_tags() {\n  TestHelpers::test_option_tag<\n      ScalarTensor::OptionTags::DampingFunctionGamma1<Dim, Fr>>(\n      \"Constant:\\n\"\n      \"  Value: 5.0\\n\");\n  TestHelpers::test_option_tag<\n      ScalarTensor::OptionTags::DampingFunctionGamma2<Dim, Fr>>(\n      \"Constant:\\n\"\n      \"  Value: 5.0\\n\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.ConstraintDampTags\",\n                  \"[Unit][PointwiseFunctions]\") {\n  test_tags<1, ArbitraryFrame>();\n  test_tags<2, ArbitraryFrame>();\n  test_tags<3, ArbitraryFrame>();\n  test_option_tags<1, Frame::Grid>();\n  test_option_tags<2, Frame::Grid>();\n  test_option_tags<3, Frame::Grid>();\n  test_option_tags<1, Frame::Inertial>();\n  test_option_tags<2, Frame::Inertial>();\n  test_option_tags<3, Frame::Inertial>();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Test_ConstraintGammas.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/FunctionOfTime.hpp\"\n#include \"Domain/FunctionsOfTime/PiecewisePolynomial.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/Constant.hpp\"\n#include \"PointwiseFunctions/ConstraintDamping/DampingFunction.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ConstraintDampingTags.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ConstraintGammas.hpp\"\n#include \"Time/Tags/Time.hpp\"\n\nnamespace {\nstruct ArbitraryFrame;\n\ntemplate <size_t Dim, typename Fr>\nvoid test_tags() {\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::ConstraintGamma1Compute<Dim, Fr>>(\"ConstraintGamma1\");\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::ConstraintGamma2Compute<Dim, Fr>>(\"ConstraintGamma2\");\n}\n\nvoid test_tag_retrieval() {\n  const size_t num_points = 4;\n  const size_t volume_dim = 3;\n  const double initial_time = 0.0;\n  const double expiration_time = 2.0;\n  const double value1 = 1.0;\n  const double value2 = 2.0;\n\n  using simple_tags = db::AddSimpleTags<\n      domain::Tags::Coordinates<volume_dim, Frame::Grid>, ::Tags::Time,\n      ::domain::Tags::FunctionsOfTimeInitialize,\n      ScalarTensor::Tags::DampingFunctionGamma1<volume_dim, Frame::Grid>,\n      ScalarTensor::Tags::DampingFunctionGamma2<volume_dim, Frame::Grid>>;\n  using compute_tags = db::AddComputeTags<\n      ScalarTensor::Tags::ConstraintGamma1Compute<volume_dim, Frame::Grid>,\n      ScalarTensor::Tags::ConstraintGamma2Compute<volume_dim, Frame::Grid>>;\n\n  const tnsr::I<DataVector, volume_dim, Frame::Grid> grid_coords =\n      make_with_value<tnsr::I<DataVector, volume_dim, Frame::Grid>>(num_points,\n                                                                    1.0);\n  const std::array<DataVector, 3> init_func{{{0.0}, {0.0}, {2.0}}};\n  std::unordered_map<std::string,\n                     std::unique_ptr<domain::FunctionsOfTime::FunctionOfTime>>\n      functions_of_time{};\n  functions_of_time[\"translation\"] =\n      std::make_unique<domain::FunctionsOfTime::PiecewisePolynomial<2>>(\n          initial_time, init_func, expiration_time);\n\n  auto box = db::create<simple_tags, compute_tags>(\n      grid_coords, 1.0, std::move(functions_of_time),\n      std::unique_ptr<\n          ConstraintDamping::DampingFunction<volume_dim, Frame::Grid>>{\n          std::make_unique<\n              ConstraintDamping::Constant<volume_dim, Frame::Grid>>(value1)},\n      std::unique_ptr<\n          ConstraintDamping::DampingFunction<volume_dim, Frame::Grid>>{\n          std::make_unique<\n              ConstraintDamping::Constant<volume_dim, Frame::Grid>>(value2)});\n\n  const auto& retrieved_gamma1 =\n      get<CurvedScalarWave::Tags::ConstraintGamma1>(box);\n  const auto& retrieved_gamma2 =\n      get<CurvedScalarWave::Tags::ConstraintGamma2>(box);\n\n  const Scalar<DataVector> expected_gamma1 =\n      make_with_value<Scalar<DataVector>>(num_points, value1);\n  const Scalar<DataVector> expected_gamma2 =\n      make_with_value<Scalar<DataVector>>(num_points, value2);\n\n  CHECK_ITERABLE_APPROX(retrieved_gamma1, expected_gamma1);\n  CHECK_ITERABLE_APPROX(retrieved_gamma2, expected_gamma2);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.ConstraintGammas\",\n                  \"[Unit][PointwiseFunctions]\") {\n  test_tags<1, ArbitraryFrame>();\n  test_tags<2, ArbitraryFrame>();\n  test_tags<3, ArbitraryFrame>();\n  test_tag_retrieval();\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Test_RampUpFunction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/RampUpFunction.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.RampUpFunction\",\n                  \"[Unit][PointwiseFunctions]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/ScalarTensor\"};\n\n  // Specify explicitly the template parameters to avoid ambiguous calls\n  pypp::check_with_random_values<3, double (*)(double, double, double),\n                                 DataVector, nullptr>(\n      &ScalarTensor::nonic_ramp_function, \"RampUpFunction\",\n      {\"nonic_ramp_function\"}, {{{-1.0, 1.0}, {-1.0, 1.0}, {0.1, 1.0}}},\n      DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Test_ScalarCharge.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarCharge.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.ScalarCharge\",\n                  \"[Unit][PointwiseFunctions]\") {\n  TestHelpers::db::test_simple_tag<\n      ScalarTensor::StrahlkorperScalar::Tags::ScalarChargeIntegrand>(\n      \"ScalarChargeIntegrand\");\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::StrahlkorperScalar::Tags::ScalarChargeIntegrandCompute>(\n      \"ScalarChargeIntegrand\");\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/ScalarTensor\"};\n\n  pypp::check_with_random_values<1>(&ScalarTensor::scalar_charge_integrand,\n                                    \"ScalarCharge\", {\"scalar_charge_integrand\"},\n                                    {{{1.0e-2, 0.5}}}, DataVector{5});\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Test_ScalarSource.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarSource.hpp\"\n\nnamespace {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.ScalarSource\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/ScalarTensor\"};\n\n  pypp::check_with_random_values<1>(&ScalarTensor::mass_source, \"Sources\",\n                                    {\"mass_source\"}, {{{1.0e-2, 0.5}}},\n                                    DataVector{5});\n\n  pypp::check_with_random_values<1>(\n      &ScalarTensor::add_scalar_source_to_dt_pi_scalar, \"Sources\",\n      {\"add_scalar_source_to_dt_pi_scalar\"}, {{{1.0e-2, 0.5}}}, DataVector{5},\n      1.0e-12, std::random_device{}(), 0.1234);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Test_SourceTags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/RampUpFunction.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/ScalarSource.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/SourceTags.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.SourceTags\",\n                  \"[Unit][PointwiseFunctions]\") {\n  TestHelpers::db::test_simple_tag<ScalarTensor::Tags::RampUpParameters>(\n      \"RampUpParameters\");\n  TestHelpers::test_option_tag<ScalarTensor::OptionTags::RampUpStart>(\"0.0\");\n  TestHelpers::test_option_tag<ScalarTensor::OptionTags::RampUpDuration>(\n      \"100.0\");\n  CHECK_THROWS_WITH(\n      ScalarTensor::Tags::RampUpParameters::create_from_options(0.0, 0.0),\n      Catch::Matchers::ContainsSubstring(\n          \"Ramp up duration time must be greater than zero\"));\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Test_StressEnergy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Evolution/Systems/ScalarTensor/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/Pypp.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/Domain/DomainTestHelpers.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/StressEnergy.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.StressEnergy\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/ScalarTensor\"};\n\n  pypp::check_with_random_values<1>(\n      &ScalarTensor::trace_reversed_stress_energy, \"StressEnergy\",\n      {\"trace_reversed_stress_energy\"}, {{{1.0e-2, 0.5}}}, DataVector{5});\n\n  pypp::check_with_random_values<1>(\n      &ScalarTensor::add_stress_energy_term_to_dt_pi, \"StressEnergy\",\n      {\"add_stress_energy_term_to_dt_pi\"}, {{{1.0e-2, 0.5}}}, DataVector{5},\n      1.0e-12, std::random_device{}(), 0.1234);\n\n  TestHelpers::db::test_compute_tag<\n      ScalarTensor::Tags::TraceReversedStressEnergyCompute>(\n      \"TraceReversedStressEnergy\");\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Test_ScalarTensorXcts)\n\nset(LIBRARY_SOURCES\n  Test_ScalarMomentum.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  DataStructures\n  ScalarTensorXcts\n  Spectral\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Xcts/ScalarMomentum.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef compute_pi(deriv, shift, lapse):\n    return np.einsum(\"i,i\", shift, deriv) / lapse\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/ScalarTensor/Xcts/Test_ScalarMomentum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Elliptic/Systems/ScalarGaussBonnet/Tags.hpp\"\n#include \"Elliptic/Systems/Xcts/Tags.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"PointwiseFunctions/ScalarTensor/Xcts/ScalarMomentum.hpp\"\n\nnamespace sgb {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.ScalarTensor.Xcts.ScalarMomentum\",\n                  \"[Unit][PointwiseFunctions]\") {\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/ScalarTensor/Xcts\"};\n  const DataVector used_for_size{5};\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(gsl::not_null<Scalar<DataVector>*>,\n                           const tnsr::i<DataVector, 3, Frame::Inertial>&,\n                           const tnsr::I<DataVector, 3>&,\n                           const Scalar<DataVector>&)>(&scalar_momentum),\n      \"ScalarMomentum\", {\"compute_pi\"}, {{{0., 1.}}}, used_for_size);\n\n  TestHelpers::db::test_compute_tag<Tags::PiCompute<\n      gr::Tags::Shift<DataVector, 3>, CurvedScalarWave::Tags::Pi>>(\"Pi\");\n  TestHelpers::db::test_compute_tag<Tags::PiCompute<\n      sgb::Tags::RolledOffShift, sgb::Tags::PiWithRolledOffShift>>(\n      \"PiWithRolledOffShift\");\n}\n\n}  // namespace sgb\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/SpecialRelativity/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_SpecialRelativity\")\n\nset(LIBRARY_SOURCES\n  Test_LorentzBoostMatrix.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  SpecialRelativity\n)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/SpecialRelativity/LorentzBoostMatrix.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef lorentz_boost_matrix(velocity):\n    boost_matrix = np.zeros((velocity.size + 1, velocity.size + 1))\n    velocity_squared = np.einsum(\"i,i->\", velocity, velocity)\n    lorentz_factor = 1.0 / np.sqrt(1.0 - velocity_squared)\n\n    prefactor = lorentz_factor / (1 + np.sqrt(1.0 - velocity_squared))\n\n    boost_matrix[0, 0] = lorentz_factor\n\n    for i in np.arange(0, velocity.size, 1):\n        boost_matrix[0, i + 1] = lorentz_factor * velocity[i]\n        boost_matrix[i + 1, 0] = lorentz_factor * velocity[i]\n        for j in np.arange(0, velocity.size, 1):\n            if i == j:\n                boost_matrix[i + 1, j + 1] = (\n                    velocity[i] * velocity[j] * prefactor\n                ) + 1.0\n            else:\n                boost_matrix[i + 1, j + 1] = (\n                    velocity[i] * velocity[j] * prefactor\n                )\n\n    return boost_matrix\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/SpecialRelativity/Test_LorentzBoostMatrix.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"PointwiseFunctions/SpecialRelativity/LorentzBoostMatrix.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\ntemplate <size_t SpatialDim>\nvoid test_lorentz_boost_matrix_random(const double& used_for_size) {\n  tnsr::Ab<double, SpatialDim, Frame::NoFrame> (*f)(\n      const tnsr::I<double, SpatialDim, Frame::NoFrame>&) =\n      &sr::lorentz_boost_matrix<SpatialDim>;\n  // The boost matrix is actually singular if the boost velocity is unity,\n  // so let's ensure the speed never exceeds 0.999.\n  constexpr double max_speed = 0.999;\n  constexpr double upper = max_speed / static_cast<double>(SpatialDim);\n  constexpr double lower = -upper;\n  pypp::check_with_random_values<1>(f, \"LorentzBoostMatrix\",\n                                    \"lorentz_boost_matrix\", {{{lower, upper}}},\n                                    used_for_size);\n}\n\ntemplate <size_t SpatialDim>\nvoid test_lorentz_boost_matrix_analytic(const double& velocity_squared) {\n  // Check that zero velocity returns an identity matrix\n  const tnsr::I<double, SpatialDim, Frame::NoFrame> velocity_zero{0.0};\n  const auto boost_matrix_zero = sr::lorentz_boost_matrix(velocity_zero);\n\n  // Do not use DataStructures/Tensor/Identity.hpp, because identity returns a\n  // spatial tensor, not a spacetime tensor\n  tnsr::Ab<double, SpatialDim, Frame::NoFrame> identity_matrix{0.0};\n  for (size_t i = 0; i < SpatialDim + 1; ++i) {\n    identity_matrix.get(i, i) = 1.0;\n  }\n  CHECK_ITERABLE_APPROX(boost_matrix_zero, identity_matrix);\n\n  // Check that the boost matrix inverse is the boost matrix with v->-v\n  const tnsr::I<double, SpatialDim, Frame::NoFrame> velocity{\n      sqrt(velocity_squared / static_cast<double>(SpatialDim))};\n  const tnsr::I<double, SpatialDim, Frame::NoFrame> minus_velocity{\n      -sqrt(velocity_squared / static_cast<double>(SpatialDim))};\n  const auto boost_matrix = sr::lorentz_boost_matrix(velocity);\n  const auto boost_matrix_minus = sr::lorentz_boost_matrix(minus_velocity);\n  tnsr::Ab<double, SpatialDim, Frame::NoFrame> inverse_check{0.0};\n  for (size_t i = 0; i < SpatialDim + 1; ++i) {\n    for (size_t j = 0; j < SpatialDim + 1; ++j) {\n      for (size_t k = 0; k < SpatialDim + 1; ++k) {\n        inverse_check.get(i, j) +=\n            boost_matrix.get(i, k) * boost_matrix_minus.get(k, j);\n      }\n    }\n  }\n  CHECK_ITERABLE_APPROX(inverse_check, identity_matrix);\n}\n\ntemplate <typename DataType, size_t SpatialDim, typename Frame>\nvoid test_lorentz_boost(const std::array<double, SpatialDim> velocity) {\n  const DataVector used_for_size{3., 4., 5.};\n\n  MAKE_GENERATOR(generator);\n  std::uniform_real_distribution<> distribution(-0.2, 0.2);\n\n  auto covariant_vector =\n      make_with_random_values<tnsr::a<DataType, SpatialDim, Frame>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n\n  tnsr::a<DataType, SpatialDim, Frame> boosted_covariant_vector;\n  sr::lorentz_boost<DataType, SpatialDim, Frame>(\n      make_not_null(&boosted_covariant_vector), covariant_vector, velocity);\n\n  // Check that applying the inverse boost returns the original vector\n  tnsr::a<DataType, SpatialDim, Frame> unboosted_covariant_vector;\n  sr::lorentz_boost<DataType, SpatialDim, Frame>(\n      make_not_null(&unboosted_covariant_vector), boosted_covariant_vector,\n      -velocity);\n  CHECK_ITERABLE_APPROX(unboosted_covariant_vector, covariant_vector);\n\n  // Check boost of the spatial vector\n  auto contravariant_vector =\n      make_with_random_values<tnsr::a<DataType, SpatialDim, Frame>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  tnsr::I<DataType, SpatialDim, Frame> spatial_vector =\n      make_with_value<tnsr::I<DataType, SpatialDim, Frame>>(used_for_size, 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    spatial_vector.get(i) = contravariant_vector.get(i + 1);\n  }\n  // Lower index, i.e. v_0\n  const double vector_component_0 = get<0>(contravariant_vector);\n\n  tnsr::I<DataType, SpatialDim, Frame> boosted_spatial_vector =\n      make_with_value<tnsr::I<DataType, SpatialDim, Frame>>(used_for_size, 0.0);\n  sr::lorentz_boost<DataType, SpatialDim, Frame>(\n      make_not_null(&boosted_spatial_vector), spatial_vector,\n      vector_component_0, velocity);\n\n  // We don't have an overload of the Lorentz boost for 4d vectors (just for one\n  // forms). But we can just change the sign of the velocity to boost it with\n  // the inverse Lorentz matrix for testing\n  tnsr::a<DataType, SpatialDim, Frame> boosted_contravariant_vector;\n  sr::lorentz_boost<DataType, SpatialDim, Frame>(\n      make_not_null(&boosted_contravariant_vector), contravariant_vector,\n      -velocity);\n  tnsr::I<DataType, SpatialDim, Frame> expected_spatial_vector =\n      make_with_value<tnsr::I<DataType, SpatialDim, Frame>>(used_for_size, 0.0);\n  for (size_t i = 0; i < SpatialDim; ++i) {\n    expected_spatial_vector.get(i) = boosted_contravariant_vector.get(i + 1);\n  }\n  CHECK_ITERABLE_APPROX(expected_spatial_vector, boosted_spatial_vector);\n\n  // Check boost on rank-2 matrix by constructing one with an outer tensor\n  // product, i.e. T_{ab} = v_{a} u_{b}\n  tnsr::ab<DataType, SpatialDim, Frame> tensor =\n      make_with_value<tnsr::ab<DataType, SpatialDim, Frame>>(used_for_size,\n                                                             0.0);\n  tnsr::ab<DataType, SpatialDim, Frame> boosted_tensor =\n      make_with_value<tnsr::ab<DataType, SpatialDim, Frame>>(used_for_size,\n                                                             0.0);\n  tnsr::ab<DataType, SpatialDim, Frame> expected_tensor =\n      make_with_value<tnsr::ab<DataType, SpatialDim, Frame>>(used_for_size,\n                                                             0.0);\n  // We need a second covariant vector\n  auto covariant_vector_2 =\n      make_with_random_values<tnsr::a<DataType, SpatialDim, Frame>>(\n          make_not_null(&generator), make_not_null(&distribution),\n          used_for_size);\n  // We boost it as well\n  tnsr::a<DataType, SpatialDim, Frame> boosted_covariant_vector_2;\n  sr::lorentz_boost<DataType, SpatialDim, Frame>(\n      make_not_null(&boosted_covariant_vector_2), covariant_vector_2, velocity);\n\n  for (size_t i = 0; i < SpatialDim + 1; ++i) {\n    for (size_t j = 0; j < SpatialDim + 1; ++j) {\n      tensor.get(i, j) = covariant_vector.get(i) * covariant_vector_2.get(j);\n      expected_tensor.get(i, j) =\n          boosted_covariant_vector.get(i) * boosted_covariant_vector_2.get(j);\n    }\n  }\n\n  sr::lorentz_boost<DataType, SpatialDim, Frame>(make_not_null(&boosted_tensor),\n                                                 tensor, velocity);\n  CHECK_ITERABLE_APPROX(expected_tensor, boosted_tensor);\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\n    \"Unit.PointwiseFunctions.SpecialRelativity.LorentzBoostMatrix\",\n    \"[PointwiseFunctions][Unit]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env(\n      \"PointwiseFunctions/SpecialRelativity/\");\n\n  // CHECK_FOR_DOUBLES passes a double named d to the test function.\n  // For test_lorentz_boost_matrix_random(), this double is only used for\n  // size, so here set it to a signaling NaN.\n  double d(std::numeric_limits<double>::signaling_NaN());\n  CHECK_FOR_DOUBLES(test_lorentz_boost_matrix_random, (1, 2, 3));\n\n  const double small_velocity_squared = 5.0e-6;\n  const double large_velocity_squared = 0.99;\n\n  // The analytic test function uses the double that CHECK_FOR_DOUBLES passes to\n  // set the magnitude of the velocity. Here, we test both for a small and for\n  // a large velocity.\n  d = small_velocity_squared;\n  CHECK_FOR_DOUBLES(test_lorentz_boost_matrix_analytic, (1, 2, 3));\n\n  d = large_velocity_squared;\n  CHECK_FOR_DOUBLES(test_lorentz_boost_matrix_analytic, (1, 2, 3));\n\n  const std::array<double, 3> velocity{{0.1, -0.4, 0.3}};\n  test_lorentz_boost<double, 3, Frame::Inertial>(velocity);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/AdmLinearMomentum.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef adm_linear_momentum_surface_integrand(\n    conformal_factor,\n    inv_spatial_metric,\n    inv_extrinsic_curvature,\n    trace_extrinsic_curvature,\n):\n    return (\n        1.0\n        / (8.0 * np.pi)\n        * conformal_factor**10\n        * (\n            inv_extrinsic_curvature\n            - trace_extrinsic_curvature * inv_spatial_metric\n        )\n    )\n\n\ndef adm_linear_momentum_volume_integrand(\n    surface_integrand,\n    conformal_factor,\n    conformal_factor_deriv,\n    conformal_metric,\n    inv_conformal_metric,\n    conformal_christoffel_second_kind,\n    conformal_christoffel_contracted,\n):\n    first_term = np.einsum(\n        \"ijk,jk->i\",\n        conformal_christoffel_second_kind,\n        surface_integrand,\n    )\n\n    second_term = np.einsum(\n        \"k,ik->i\",\n        conformal_christoffel_contracted,\n        surface_integrand,\n    )\n\n    third_term = 2.0 * np.einsum(\n        \"jk,jk,il,l->i\",\n        conformal_metric,\n        surface_integrand,\n        inv_conformal_metric,\n        conformal_factor_deriv / conformal_factor,\n    )\n\n    return -(first_term + second_term - third_term)\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY Test_XctsPointwiseFunctions)\n\nset(LIBRARY_SOURCES\n  Test_AdmMass.cpp\n  Test_AdmMomentum.cpp\n  Test_CenterOfMass.cpp\n  Test_ExtrinsicCurvature.cpp\n  Test_LongitudinalOperator.cpp\n  Test_SpacetimeQuantities.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  Domain\n  DomainCreators\n  DomainStructure\n  GeneralRelativitySolutions\n  LinearOperators\n  ElasticityPointwiseFunctions\n  GeneralRelativityHelpers\n  Spectral\n  Utilities\n  XctsPointwiseFunctions\n  XctsSolutions\n  )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/ExtrinsicCurvature.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef extrinsic_curvature(\n    conformal_factor,\n    lapse,\n    conformal_metric,\n    longitudinal_shift_minus_dt_conformal_metric,\n    trace_extrinsic_curvature,\n):\n    longitudinal_shift_minus_dt_conformal_metric_lower = np.einsum(\n        \"ij,ik,jl\",\n        longitudinal_shift_minus_dt_conformal_metric,\n        conformal_metric,\n        conformal_metric,\n    )\n    return conformal_factor**4 * (\n        longitudinal_shift_minus_dt_conformal_metric_lower / 2.0 / lapse\n        + conformal_metric * trace_extrinsic_curvature / 3.0\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/LongitudinalOperator.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\n\n\ndef longitudinal_operator(strain, inv_metric):\n    projection = np.einsum(\n        \"ij,kl->ikjl\", inv_metric, inv_metric\n    ) - 1.0 / 3.0 * np.einsum(\"ij,kl->ijkl\", inv_metric, inv_metric)\n    return 2.0 * np.einsum(\"ijkl,kl\", projection, strain)\n\n\ndef longitudinal_operator_flat_cartesian(strain):\n    return longitudinal_operator(strain, np.identity(3))\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/SpacetimeQuantities.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport numpy as np\nfrom PointwiseFunctions.Xcts.ExtrinsicCurvature import extrinsic_curvature\n\n\ndef spatial_metric(conformal_factor, conformal_metric):\n    return conformal_factor**4 * conformal_metric\n\n\ndef inv_spatial_metric(conformal_factor, inv_conformal_metric):\n    return conformal_factor ** (-4) * inv_conformal_metric\n\n\ndef deriv_inv_spatial_metric(\n    conformal_factor,\n    deriv_conformal_factor,\n    inv_conformal_metric,\n    deriv_conformal_metric,\n):\n    deriv_inv_conformal_metric = -np.einsum(\n        \"in,mj,knm->kij\",\n        inv_conformal_metric,\n        inv_conformal_metric,\n        deriv_conformal_metric,\n    )\n    return conformal_factor ** (-4) * deriv_inv_conformal_metric - 4.0 * (\n        conformal_factor ** (-5)\n        * np.einsum(\"k,ij->kij\", deriv_conformal_factor, inv_conformal_metric)\n    )\n\n\ndef spatial_christoffel_second_kind(\n    conformal_factor,\n    deriv_conformal_factor,\n    conformal_metric,\n    inv_conformal_metric,\n    conformal_christoffel_second_kind,\n):\n    # Eq. (3.7) in Baumgarte/Shapiro\n    krond = np.eye(3)\n    return conformal_christoffel_second_kind + 2.0 * (\n        np.einsum(\"ij,k->ijk\", krond, deriv_conformal_factor / conformal_factor)\n        + np.einsum(\n            \"ik,j->ijk\", krond, deriv_conformal_factor / conformal_factor\n        )\n        - np.einsum(\n            \"jk,il,l->ijk\",\n            conformal_metric,\n            inv_conformal_metric,\n            deriv_conformal_factor / conformal_factor,\n        )\n    )\n\n\ndef lapse(conformal_factor, lapse_times_conformal_factor):\n    return lapse_times_conformal_factor / conformal_factor\n\n\ndef deriv_lapse(\n    conformal_factor,\n    deriv_conformal_factor,\n    lapse_times_conformal_factor,\n    deriv_lapse_times_conformal_factor,\n):\n    return (\n        deriv_lapse_times_conformal_factor / conformal_factor\n        - lapse_times_conformal_factor\n        * deriv_conformal_factor\n        / conformal_factor**2\n    )\n\n\ndef shift(shift_excess, shift_background):\n    return shift_excess + shift_background\n\n\ndef deriv_shift(deriv_shift_excess, deriv_shift_background):\n    return deriv_shift_excess + deriv_shift_background\n\n\ndef hamiltonian_constraint(\n    spatial_ricci_tensor, extrinsic_curvature, local_inv_spatial_metric\n):\n    return (\n        np.einsum(\"ij,ij\", local_inv_spatial_metric, spatial_ricci_tensor)\n        + np.einsum(\"ij,ij\", local_inv_spatial_metric, extrinsic_curvature) ** 2\n        - np.einsum(\n            \"ij,kl,ik,jl\",\n            local_inv_spatial_metric,\n            local_inv_spatial_metric,\n            extrinsic_curvature,\n            extrinsic_curvature,\n        )\n    )\n\n\ndef momentum_constraint(\n    cov_deriv_extrinsic_curvature, local_inv_spatial_metric\n):\n    return np.einsum(\n        \"jk,jki\", local_inv_spatial_metric, cov_deriv_extrinsic_curvature\n    ) - np.einsum(\n        \"jk,ijk\", local_inv_spatial_metric, cov_deriv_extrinsic_curvature\n    )\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/Test_AdmMass.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n#include \"PointwiseFunctions/Xcts/AdmMass.hpp\"\n#include \"PointwiseFunctions/Xcts/AdmMomentum.hpp\"\n\nnamespace {\n\ntemplate <typename Solution>\nvoid test_infinite_surface_integral(const double distance, const double mass,\n                                    const double horizon_radius,\n                                    const double boost_speed,\n                                    const Solution& solution) {\n  // Set up domain\n  const size_t h_refinement = 1;\n  const size_t p_refinement = 6;\n  const domain::creators::Sphere shell{\n      /* inner_radius */ 1.1 * horizon_radius,\n      /* outer_radius */ distance,\n      /* interior */ domain::creators::Sphere::Excision{},\n      /* initial_refinement */ h_refinement,\n      /* initial_number_of_grid_points */ p_refinement + 1,\n      /* use_equiangular_map */ true,\n      /* equatorial_compression */ {},\n      /* radial_partitioning */ {},\n      /* radial_distribution */ domain::CoordinateMaps::Distribution::Inverse};\n  const auto shell_domain = shell.create_domain();\n  const auto& blocks = shell_domain.blocks();\n  const auto& initial_ref_levels = shell.initial_refinement_levels();\n  const auto element_ids = initial_element_ids(initial_ref_levels);\n  const Mesh<2> face_mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  // Initialize surface integral\n  Scalar<double> surface_integral(0.);\n\n  // Compute integrals by summing over each element\n  for (const auto& element_id : element_ids) {\n    // Skip elements not at the outer boundary\n    const size_t radial_dimension = 2;\n    const auto radial_segment_id = element_id.segment_id(radial_dimension);\n    if (radial_segment_id.index() !=\n        two_to_the(radial_segment_id.refinement_level()) - 1) {\n      continue;\n    }\n\n    // Get element information\n    const auto current_element =\n        domain::create_initial_element(element_id, blocks, initial_ref_levels);\n    const auto& current_block = blocks.at(element_id.block_id());\n    const ElementMap<3, Frame::Inertial> logical_to_inertial_map(\n        element_id, current_block.stationary_map().get_clone());\n\n    // Loop over external boundaries\n    for (auto boundary_direction : current_element.external_boundaries()) {\n      // Skip interfaces not at the outer boundary\n      if (boundary_direction != Direction<3>::upper_zeta()) {\n        continue;\n      }\n\n      // Get interface coordinates\n      const auto logical_coords =\n          interface_logical_coordinates(face_mesh, boundary_direction);\n      const auto inertial_coords = logical_to_inertial_map(logical_coords);\n      const auto inv_jacobian =\n          logical_to_inertial_map.inv_jacobian(logical_coords);\n\n      // Get required fields on the interface\n      const auto background_fields = solution.variables(\n          inertial_coords,\n          tmpl::list<\n              ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n                            tmpl::size_t<3>, Frame::Inertial>,\n              Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n              Xcts::Tags::InverseConformalMetric<DataVector, 3,\n                                                 Frame::Inertial>,\n              Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                         Frame::Inertial>,\n              Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                         Frame::Inertial>>{});\n      const auto& deriv_conformal_factor =\n          get<::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n                            tmpl::size_t<3>, Frame::Inertial>>(\n              background_fields);\n      const auto& conformal_metric =\n          get<Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>>(\n              background_fields);\n      const auto& inv_conformal_metric = get<\n          Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>(\n          background_fields);\n      const auto& conformal_christoffel_second_kind =\n          get<Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                         Frame::Inertial>>(\n              background_fields);\n      const auto& conformal_christoffel_contracted =\n          get<Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                         Frame::Inertial>>(\n              background_fields);\n\n      // Compute conformal area element\n      const auto sqrt_det_conformal_metric =\n          Scalar<DataVector>(sqrt(get(determinant(conformal_metric))));\n      const auto conformal_area_element =\n          area_element(inv_jacobian, boundary_direction, inv_conformal_metric,\n                       sqrt_det_conformal_metric);\n\n      // Compute conformal face normal\n      auto conformal_face_normal = unnormalized_face_normal(\n          face_mesh, logical_to_inertial_map, boundary_direction);\n      const auto face_normal_magnitude =\n          magnitude(conformal_face_normal, inv_conformal_metric);\n      for (size_t d = 0; d < 3; ++d) {\n        conformal_face_normal.get(d) /= get(face_normal_magnitude);\n      }\n\n      // Compute and contract surface integrand\n      const auto surface_integrand = Xcts::adm_mass_surface_integrand(\n          deriv_conformal_factor, inv_conformal_metric,\n          conformal_christoffel_second_kind, conformal_christoffel_contracted);\n      const auto contracted_integrand = tenex::evaluate(\n          surface_integrand(ti::I) * conformal_face_normal(ti::i));\n\n      // Compute contribution to surface integral\n      surface_integral.get() += definite_integral(\n          get(contracted_integrand) * get(conformal_area_element), face_mesh);\n    }\n  }\n\n  // Check result\n  auto custom_approx = Approx::custom().epsilon(10. / distance).scale(1.0);\n  const double lorentz_factor = 1. / sqrt(1. - square(boost_speed));\n  CHECK(get(surface_integral) == custom_approx(lorentz_factor * mass));\n}\n\ntemplate <typename Solution>\nvoid test_infinite_volume_integral(const double distance, const double mass,\n                                   const double horizon_radius,\n                                   const double boost_speed,\n                                   const Solution& solution) {\n  // Set up domain\n  const size_t h_refinement = 1;\n  const size_t p_refinement = 6;\n  const domain::creators::Sphere shell{\n      /* inner_radius */ 1.1 * horizon_radius,\n      /* outer_radius */ distance,\n      /* interior */ domain::creators::Sphere::Excision{},\n      /* initial_refinement */ h_refinement,\n      /* initial_number_of_grid_points */ p_refinement + 1,\n      /* use_equiangular_map */ true,\n      /* equatorial_compression */ {},\n      /* radial_partitioning */ {},\n      /* radial_distribution */ domain::CoordinateMaps::Distribution::Inverse};\n  const auto shell_domain = shell.create_domain();\n  const auto& blocks = shell_domain.blocks();\n  const auto& initial_ref_levels = shell.initial_refinement_levels();\n  const auto element_ids = initial_element_ids(initial_ref_levels);\n  const Mesh<3> mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> face_mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  // Initialize \"reduced\" integral.\n  Scalar<double> total_integral(0.);\n\n  // Compute integrals by summing over each element\n  for (const auto& element_id : element_ids) {\n    // Get element information\n    const auto current_element =\n        domain::create_initial_element(element_id, blocks, initial_ref_levels);\n    const auto& current_block = blocks.at(element_id.block_id());\n    const ElementMap<3, Frame::Inertial> logical_to_inertial_map(\n        element_id, current_block.stationary_map().get_clone());\n\n    // Get 3D coordinates\n    const auto logical_coords = logical_coordinates(mesh);\n    const auto inertial_coords = logical_to_inertial_map(logical_coords);\n    const auto jacobian = logical_to_inertial_map.jacobian(logical_coords);\n    const auto det_jacobian = determinant(jacobian);\n    const auto inv_jacobian =\n        logical_to_inertial_map.inv_jacobian(logical_coords);\n\n    // Get required fields\n    const auto background_fields = solution.variables(\n        inertial_coords, mesh, inv_jacobian,\n        tmpl::list<\n            Xcts::Tags::ConformalFactor<DataVector>,\n            ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n                          tmpl::size_t<3>, Frame::Inertial>,\n            Xcts::Tags::ConformalRicciScalar<DataVector>,\n            gr::Tags::TraceExtrinsicCurvature<DataVector>,\n            Xcts::Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<\n                DataVector>,\n            Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n            ::Tags::deriv<\n                Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n                tmpl::size_t<3>, Frame::Inertial>,\n            Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n            Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                       Frame::Inertial>,\n            ::Tags::deriv<Xcts::Tags::ConformalChristoffelSecondKind<\n                              DataVector, 3, Frame::Inertial>,\n                          tmpl::size_t<3>, Frame::Inertial>,\n            Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                       Frame::Inertial>>{});\n    const auto& conformal_factor =\n        get<Xcts::Tags::ConformalFactor<DataVector>>(background_fields);\n    const auto& deriv_conformal_factor =\n        get<::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n                          tmpl::size_t<3>, Frame::Inertial>>(background_fields);\n    const auto& conformal_ricci_scalar =\n        get<Xcts::Tags::ConformalRicciScalar<DataVector>>(background_fields);\n    const auto& trace_extrinsic_curvature =\n        get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(background_fields);\n    const auto& longitudinal_shift_minus_dt_conformal_metric_over_lapse_square =\n        get<Xcts::Tags::LongitudinalShiftMinusDtConformalMetricOverLapseSquare<\n            DataVector>>(background_fields);\n    const auto& conformal_metric =\n        get<Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>>(\n            background_fields);\n    const auto& deriv_conformal_metric = get<::Tags::deriv<\n        Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n        tmpl::size_t<3>, Frame::Inertial>>(background_fields);\n    const auto& inv_conformal_metric =\n        get<Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>(\n            background_fields);\n    const auto& conformal_christoffel_second_kind =\n        get<Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                       Frame::Inertial>>(\n            background_fields);\n    const auto& deriv_conformal_christoffel_second_kind =\n        get<::Tags::deriv<Xcts::Tags::ConformalChristoffelSecondKind<\n                              DataVector, 3, Frame::Inertial>,\n                          tmpl::size_t<3>, Frame::Inertial>>(background_fields);\n    const auto& conformal_christoffel_contracted =\n        get<Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                       Frame::Inertial>>(\n            background_fields);\n\n    const auto deriv_inv_conformal_metric =\n        tenex::evaluate<ti::i, ti::J, ti::K>(\n            inv_conformal_metric(ti::J, ti::L) *\n                inv_conformal_metric(ti::K, ti::M) *\n                (deriv_conformal_metric(ti::i, ti::l, ti::m) -\n                 conformal_christoffel_second_kind(ti::N, ti::i, ti::l) *\n                     conformal_metric(ti::n, ti::m) -\n                 conformal_christoffel_second_kind(ti::N, ti::i, ti::m) *\n                     conformal_metric(ti::l, ti::n)) -\n            conformal_christoffel_second_kind(ti::J, ti::i, ti::l) *\n                inv_conformal_metric(ti::L, ti::K) -\n            conformal_christoffel_second_kind(ti::K, ti::i, ti::l) *\n                inv_conformal_metric(ti::J, ti::L));\n\n    const auto energy_density =\n        make_with_value<Scalar<DataVector>>(inertial_coords, 0.0);\n\n    const auto sqrt_det_conformal_metric =\n        Scalar<DataVector>(sqrt(get(determinant(conformal_metric))));\n\n    // Evaluate volume integral.\n    const auto volume_integrand = Xcts::adm_mass_volume_integrand(\n        conformal_factor, conformal_ricci_scalar, trace_extrinsic_curvature,\n        longitudinal_shift_minus_dt_conformal_metric_over_lapse_square,\n        energy_density, inv_conformal_metric, deriv_inv_conformal_metric,\n        conformal_christoffel_second_kind, conformal_christoffel_contracted,\n        deriv_conformal_christoffel_second_kind);\n    total_integral.get() += definite_integral(\n        get(volume_integrand) * get(sqrt_det_conformal_metric) *\n            get(det_jacobian),\n        mesh);\n\n    // Loop over external boundaries.\n    for (auto boundary_direction : current_element.external_boundaries()) {\n      // Skip interfaces not at the inner boundary.\n      if (boundary_direction != Direction<3>::lower_zeta()) {\n        continue;\n      }\n\n      // Get interface coordinates.\n      const auto face_logical_coords =\n          interface_logical_coordinates(face_mesh, boundary_direction);\n      const auto face_inv_jacobian =\n          logical_to_inertial_map.inv_jacobian(face_logical_coords);\n\n      // Slice required fields to the interface\n      const size_t slice_index =\n          index_to_slice_at(mesh.extents(), boundary_direction);\n      const auto& face_deriv_conformal_factor =\n          data_on_slice(deriv_conformal_factor, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n      const auto& face_inv_conformal_metric =\n          data_on_slice(inv_conformal_metric, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n      const auto& face_conformal_christoffel_second_kind =\n          data_on_slice(conformal_christoffel_second_kind, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n      const auto& face_conformal_christoffel_contracted =\n          data_on_slice(conformal_christoffel_contracted, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n      const auto& face_sqrt_det_conformal_metric =\n          data_on_slice(sqrt_det_conformal_metric, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n\n      // Compute conformal area element\n      const auto conformal_area_element = area_element(\n          face_inv_jacobian, boundary_direction, face_inv_conformal_metric,\n          face_sqrt_det_conformal_metric);\n\n      // Compute conformal face normal\n      auto conformal_face_normal = unnormalized_face_normal(\n          face_mesh, logical_to_inertial_map, boundary_direction);\n      const auto face_normal_magnitude =\n          magnitude(conformal_face_normal, face_inv_conformal_metric);\n      for (size_t d = 0; d < 3; ++d) {\n        conformal_face_normal.get(d) /= get(face_normal_magnitude);\n      }\n\n      // Evaluate surface integral.\n      const auto surface_integrand = Xcts::adm_mass_surface_integrand(\n          face_deriv_conformal_factor, face_inv_conformal_metric,\n          face_conformal_christoffel_second_kind,\n          face_conformal_christoffel_contracted);\n      const auto contracted_integrand = tenex::evaluate(\n          -surface_integrand(ti::I) * conformal_face_normal(ti::i));\n\n      // Compute contribution to surface integral\n      total_integral.get() += definite_integral(\n          get(contracted_integrand) * get(conformal_area_element), face_mesh);\n    }\n  }\n\n  // Check result\n  const double lorentz_factor = 1. / sqrt(1. - square(boost_speed));\n  auto custom_approx = Approx::custom().epsilon(10. / distance).scale(1.0);\n  CHECK(get(total_integral) == custom_approx(lorentz_factor * mass));\n}\n\n}  // namespace\n\n// [[TimeOut, 60]]\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Xcts.AdmMass\",\n                  \"[Unit][PointwiseFunctions]\") {\n  {\n    INFO(\"Schwarzschild in Kerr-Schild coordinates\");\n    const double mass = 1.;\n    const double horizon_radius = 2. * mass;\n    const double boost_speed = 0.;\n    const Xcts::Solutions::WrappedGr<gr::Solutions::KerrSchild> solution(\n        mass, std::array<double, 3>{{0., 0., 0.}},\n        std::array<double, 3>{{0., 0., 0.}},\n        std::array<double, 3>{{0., 0., boost_speed}});\n    for (const double distance : std::array<double, 3>({1.e4, 1.e5, 1.e6})) {\n      test_infinite_surface_integral(distance, mass, horizon_radius,\n                                     boost_speed, solution);\n      test_infinite_volume_integral(distance, mass, horizon_radius, boost_speed,\n                                    solution);\n    }\n  }\n  {\n    INFO(\"Boosted Schwarzschild in Kerr-Schild coordinates\");\n    const double mass = 1.;\n    const double horizon_radius = 2. * mass;\n    const double boost_speed = 0.5;\n    const Xcts::Solutions::WrappedGr<gr::Solutions::KerrSchild> solution(\n        mass, std::array<double, 3>{{0., 0., 0.}},\n        std::array<double, 3>{{0., 0., 0.}},\n        std::array<double, 3>{{0., 0., boost_speed}});\n    for (const double distance : std::array<double, 3>({1.e4, 1.e5, 1.e6})) {\n      test_infinite_surface_integral(distance, mass, horizon_radius,\n                                     boost_speed, solution);\n      // Note: the volume integral currently doesn't work with this test case.\n    }\n  }\n  {\n    INFO(\"Schwarzschild in isotropic coordinates\");\n    const double mass = 1.;\n    const double horizon_radius = 0.5 * mass;\n    const double boost_speed = 0.;\n    const Xcts::Solutions::Schwarzschild solution(\n        mass, Xcts::Solutions::SchwarzschildCoordinates::Isotropic);\n    for (const double distance : std::array<double, 3>({1.e4, 1.e5, 1.e6})) {\n      test_infinite_surface_integral(distance, mass, horizon_radius,\n                                     boost_speed, solution);\n      test_infinite_volume_integral(distance, mass, horizon_radius, boost_speed,\n                                    solution);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/Test_AdmMomentum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/GeneralRelativity/KerrSchild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/WrappedGr.hpp\"\n#include \"PointwiseFunctions/Xcts/AdmMomentum.hpp\"\n\nnamespace {\n\nusing Schwarzschild = Xcts::Solutions::Schwarzschild;\nusing KerrSchild = Xcts::Solutions::WrappedGr<gr::Solutions::KerrSchild>;\n\ntemplate <typename Solution>\nvoid test_infinite_surface_integrals(const double distance, const double mass,\n                                     const double horizon_radius,\n                                     const double boost_speed,\n                                     const Solution& solution) {\n  // Define x-shift in the domain\n  const double x_shift = 1.;\n\n  // Set up domain\n  const size_t h_refinement = 1;\n  const size_t p_refinement = 6;\n  const domain::creators::Sphere shell{\n      /* inner_radius */ horizon_radius + x_shift,\n      /* outer_radius */ distance,\n      /* interior */ domain::creators::Sphere::Excision{},\n      /* initial_refinement */ h_refinement,\n      /* initial_number_of_grid_points */ p_refinement + 1,\n      /* use_equiangular_map */ true,\n      /* equatorial_compression */ {},\n      /* radial_partitioning */ {},\n      /* radial_distribution */ domain::CoordinateMaps::Distribution::Inverse};\n  const auto shell_domain = shell.create_domain();\n  const auto& blocks = shell_domain.blocks();\n  const auto& initial_ref_levels = shell.initial_refinement_levels();\n  const auto element_ids = initial_element_ids(initial_ref_levels);\n  const Mesh<2> face_mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  // Initialize surface integrals\n  tnsr::I<double, 3> linear_momentum({0., 0., 0.});\n  Scalar<double> angular_momentum_z(0.);\n\n  // Compute integrals by summing over each element\n  for (const auto& element_id : element_ids) {\n    // Skip elements not at the outer boundary\n    const size_t radial_dimension = 2;\n    const auto radial_segment_id = element_id.segment_id(radial_dimension);\n    if (radial_segment_id.index() !=\n        two_to_the(radial_segment_id.refinement_level()) - 1) {\n      continue;\n    }\n\n    // Get element information\n    const auto current_element =\n        domain::create_initial_element(element_id, blocks, initial_ref_levels);\n    const auto& current_block = blocks.at(element_id.block_id());\n    const ElementMap<3, Frame::Inertial> logical_to_inertial_map(\n        element_id, current_block.stationary_map().get_clone());\n\n    // Loop over external boundaries\n    for (auto boundary_direction : current_element.external_boundaries()) {\n      // Skip interfaces not at the outer boundary\n      if (boundary_direction != Direction<3>::upper_zeta()) {\n        continue;\n      }\n\n      // Get coordinates\n      const auto logical_coords =\n          interface_logical_coordinates(face_mesh, boundary_direction);\n      const auto inertial_coords = logical_to_inertial_map(logical_coords);\n      const auto inv_jacobian =\n          logical_to_inertial_map.inv_jacobian(logical_coords);\n\n      // Shift x-coordinate\n      auto shifted_coords = inertial_coords;\n      shifted_coords.get(0) -= x_shift;\n\n      // Get required fields\n      const auto background_fields = solution.variables(\n          shifted_coords,\n          tmpl::list<\n              Xcts::Tags::ConformalFactor<DataVector>,\n              gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>,\n              gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame::Inertial>,\n              gr::Tags::TraceExtrinsicCurvature<DataVector>>{});\n      const auto& conformal_factor =\n          get<Xcts::Tags::ConformalFactor<DataVector>>(background_fields);\n      const auto& inv_spatial_metric =\n          get<gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>>(\n              background_fields);\n      const auto& extrinsic_curvature =\n          get<gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame::Inertial>>(\n              background_fields);\n      const auto& trace_extrinsic_curvature =\n          get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(background_fields);\n\n      // Compute the inverse extrinsic curvature\n      tnsr::II<DataVector, 3> inv_extrinsic_curvature;\n      tenex::evaluate<ti::I, ti::J>(make_not_null(&inv_extrinsic_curvature),\n                                    inv_spatial_metric(ti::I, ti::K) *\n                                        inv_spatial_metric(ti::J, ti::L) *\n                                        extrinsic_curvature(ti::k, ti::l));\n\n      // Compute Euclidean area element\n      const auto flat_area_element =\n          euclidean_area_element(inv_jacobian, boundary_direction);\n\n      // Compute Euclidean face normal\n      auto flat_face_normal = unnormalized_face_normal(\n          face_mesh, logical_to_inertial_map, boundary_direction);\n      const auto face_normal_magnitude = magnitude(flat_face_normal);\n      for (size_t d = 0; d < 3; ++d) {\n        flat_face_normal.get(d) /= get(face_normal_magnitude);\n      }\n\n      // Evaluate linear momentum surface integral\n      const auto linear_momentum_surface_integrand =\n          Xcts::adm_linear_momentum_surface_integrand(\n              conformal_factor, inv_spatial_metric, inv_extrinsic_curvature,\n              trace_extrinsic_curvature);\n      const auto linear_momentum_contracted_integrand = tenex::evaluate<ti::I>(\n          linear_momentum_surface_integrand(ti::I, ti::J) *\n          flat_face_normal(ti::j));\n      for (int I = 0; I < 3; I++) {\n        linear_momentum.get(I) +=\n            definite_integral(linear_momentum_contracted_integrand.get(I) *\n                                  get(flat_area_element),\n                              face_mesh);\n      }\n\n      // Evaluate angular momentum surface integral\n      const auto angular_momentum_surface_integrand =\n          Xcts::adm_angular_momentum_z_surface_integrand(\n              linear_momentum_surface_integrand, inertial_coords);\n      const auto angular_momentum_contracted_integrand = tenex::evaluate(\n          angular_momentum_surface_integrand(ti::I) * flat_face_normal(ti::i));\n      angular_momentum_z.get() += definite_integral(\n          get(angular_momentum_contracted_integrand) * get(flat_area_element),\n          face_mesh);\n    }\n  }\n\n  // Check results\n  auto custom_approx = Approx::custom().epsilon(10. / distance).scale(1.0);\n  const double lorentz_factor = 1. / sqrt(1. - square(boost_speed));\n  CHECK(get<0>(linear_momentum) == custom_approx(0.));\n  CHECK(get<1>(linear_momentum) ==\n        custom_approx(lorentz_factor * mass * boost_speed));\n  CHECK(get<2>(linear_momentum) == custom_approx(0.));\n  CHECK(get(angular_momentum_z) ==\n        custom_approx(x_shift * lorentz_factor * mass * boost_speed));\n}\n\ntemplate <typename Solution>\nvoid test_infinite_volume_integral(const double distance, const double mass,\n                                   const double horizon_radius,\n                                   const double boost_speed,\n                                   const Solution& solution) {\n  // Define x-shift in the domain\n  const double x_shift = 1.;\n\n  // Set up domain\n  const size_t h_refinement = 1;\n  const size_t p_refinement = 6;\n  const domain::creators::Sphere shell{\n      /* inner_radius */ horizon_radius + x_shift,\n      /* outer_radius */ distance,\n      /* interior */ domain::creators::Sphere::Excision{},\n      /* initial_refinement */ h_refinement,\n      /* initial_number_of_grid_points */ p_refinement + 1,\n      /* use_equiangular_map */ true,\n      /* equatorial_compression */ {},\n      /* radial_partitioning */ {},\n      /* radial_distribution */ domain::CoordinateMaps::Distribution::Inverse};\n  const auto shell_domain = shell.create_domain();\n  const auto& blocks = shell_domain.blocks();\n  const auto& initial_ref_levels = shell.initial_refinement_levels();\n  const auto element_ids = initial_element_ids(initial_ref_levels);\n  const Mesh<3> mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> face_mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  // Initialize integrals\n  tnsr::I<double, 3> linear_momentum({0., 0., 0.});\n  Scalar<double> angular_momentum_z(0.);\n\n  // Compute integrals by summing over each element\n  for (const auto& element_id : element_ids) {\n    // Get element information\n    const auto current_element =\n        domain::create_initial_element(element_id, blocks, initial_ref_levels);\n    const auto& current_block = blocks.at(element_id.block_id());\n    const ElementMap<3, Frame::Inertial> logical_to_inertial_map(\n        element_id, current_block.stationary_map().get_clone());\n\n    // Get 3D coordinates\n    const auto logical_coords = logical_coordinates(mesh);\n    const auto inertial_coords = logical_to_inertial_map(logical_coords);\n    const auto jacobian = logical_to_inertial_map.jacobian(logical_coords);\n    const auto det_jacobian = determinant(jacobian);\n    const auto inv_jacobian =\n        logical_to_inertial_map.inv_jacobian(logical_coords);\n\n    // Shift x-coordinate\n    auto shifted_coords = inertial_coords;\n    shifted_coords.get(0) -= x_shift;\n\n    // Get required fields\n    const auto solution_fields = solution.variables(\n        shifted_coords,\n        tmpl::list<\n            Xcts::Tags::ConformalFactor<DataVector>,\n            ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n                          tmpl::size_t<3>, Frame::Inertial>,\n            Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>,\n            Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>,\n            gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>,\n            gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame::Inertial>,\n            gr::Tags::TraceExtrinsicCurvature<DataVector>,\n            Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                       Frame::Inertial>,\n            Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                       Frame::Inertial>>{});\n    const auto& conformal_factor =\n        get<Xcts::Tags::ConformalFactor<DataVector>>(solution_fields);\n    const auto& deriv_conformal_factor =\n        get<::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n                          tmpl::size_t<3>, Frame::Inertial>>(solution_fields);\n    const auto& conformal_metric =\n        get<Xcts::Tags::ConformalMetric<DataVector, 3, Frame::Inertial>>(\n            solution_fields);\n    const auto& inv_conformal_metric =\n        get<Xcts::Tags::InverseConformalMetric<DataVector, 3, Frame::Inertial>>(\n            solution_fields);\n    const auto& inv_spatial_metric =\n        get<gr::Tags::InverseSpatialMetric<DataVector, 3, Frame::Inertial>>(\n            solution_fields);\n    const auto& extrinsic_curvature =\n        get<gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame::Inertial>>(\n            solution_fields);\n    const auto& trace_extrinsic_curvature =\n        get<gr::Tags::TraceExtrinsicCurvature<DataVector>>(solution_fields);\n    const auto& conformal_christoffel_second_kind =\n        get<Xcts::Tags::ConformalChristoffelSecondKind<DataVector, 3,\n                                                       Frame::Inertial>>(\n            solution_fields);\n    const auto& conformal_christoffel_contracted =\n        get<Xcts::Tags::ConformalChristoffelContracted<DataVector, 3,\n                                                       Frame::Inertial>>(\n            solution_fields);\n\n    // Compute the inverse extrinsic curvature\n    tnsr::II<DataVector, 3> inv_extrinsic_curvature;\n    tenex::evaluate<ti::I, ti::J>(make_not_null(&inv_extrinsic_curvature),\n                                  inv_spatial_metric(ti::I, ti::K) *\n                                      inv_spatial_metric(ti::J, ti::L) *\n                                      extrinsic_curvature(ti::k, ti::l));\n\n    // Evaluate linear momentum volume integral\n    const auto linear_momentum_surface_integrand =\n        Xcts::adm_linear_momentum_surface_integrand(\n            conformal_factor, inv_spatial_metric, inv_extrinsic_curvature,\n            trace_extrinsic_curvature);\n    const auto linear_momentum_volume_integrand =\n        Xcts::adm_linear_momentum_volume_integrand(\n            linear_momentum_surface_integrand, conformal_factor,\n            deriv_conformal_factor, conformal_metric, inv_conformal_metric,\n            conformal_christoffel_second_kind,\n            conformal_christoffel_contracted);\n    for (int I = 0; I < 3; I++) {\n      linear_momentum.get(I) += definite_integral(\n          linear_momentum_volume_integrand.get(I) * get(det_jacobian), mesh);\n    }\n\n    // Evaluate angular momentum volume integral\n    const auto angular_momentum_surface_integrand =\n        Xcts::adm_angular_momentum_z_surface_integrand(\n            linear_momentum_surface_integrand, inertial_coords);\n    const auto angular_momentum_volume_integrand =\n        Xcts::adm_angular_momentum_z_volume_integrand(\n            linear_momentum_volume_integrand, inertial_coords);\n    angular_momentum_z.get() += definite_integral(\n        get(angular_momentum_volume_integrand) * get(det_jacobian), mesh);\n\n    // Loop over external boundaries\n    for (auto boundary_direction : current_element.external_boundaries()) {\n      // Skip interfaces not at the inner boundary\n      if (boundary_direction != Direction<3>::lower_zeta()) {\n        continue;\n      }\n\n      // Get interface coordinates.\n      auto face_logical_coords =\n          interface_logical_coordinates(face_mesh, boundary_direction);\n      const auto face_inv_jacobian =\n          logical_to_inertial_map.inv_jacobian(face_logical_coords);\n\n      // Slice required fields to the interface\n      const size_t slice_index =\n          index_to_slice_at(mesh.extents(), boundary_direction);\n      const auto& face_linear_momentum_surface_integrand =\n          data_on_slice(linear_momentum_surface_integrand, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n      const auto& face_angular_momentum_surface_integrand =\n          data_on_slice(angular_momentum_surface_integrand, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n\n      // Compute Euclidean area element\n      const auto flat_area_element =\n          euclidean_area_element(face_inv_jacobian, boundary_direction);\n\n      // Compute Euclidean face normal\n      auto flat_face_normal = unnormalized_face_normal(\n          face_mesh, logical_to_inertial_map, boundary_direction);\n      const auto face_normal_magnitude = magnitude(flat_face_normal);\n      for (size_t d = 0; d < 3; ++d) {\n        flat_face_normal.get(d) /= get(face_normal_magnitude);\n      }\n\n      // Evaluate linear momentum surface integral\n      const auto linear_momentum_contracted_integrand = tenex::evaluate<ti::I>(\n          -face_linear_momentum_surface_integrand(ti::I, ti::J) *\n          flat_face_normal(ti::j));\n      for (int I = 0; I < 3; I++) {\n        linear_momentum.get(I) +=\n            definite_integral(linear_momentum_contracted_integrand.get(I) *\n                                  get(flat_area_element),\n                              face_mesh);\n      }\n\n      // Evaluate angular momentum surface integral\n      const auto angular_momentum_contracted_integrand =\n          tenex::evaluate(-face_angular_momentum_surface_integrand(ti::I) *\n                          flat_face_normal(ti::i));\n      angular_momentum_z.get() += definite_integral(\n          get(angular_momentum_contracted_integrand) * get(flat_area_element),\n          face_mesh);\n    }\n  }\n\n  // Check result\n  const double lorentz_factor = 1. / sqrt(1. - square(boost_speed));\n  auto custom_approx = Approx::custom().epsilon(10. / distance).scale(1.0);\n  CHECK(get<0>(linear_momentum) == custom_approx(0.));\n  CHECK(get<1>(linear_momentum) ==\n        custom_approx(lorentz_factor * mass * boost_speed));\n  CHECK(get<2>(linear_momentum) == custom_approx(0.));\n  CHECK(get(angular_momentum_z) ==\n        custom_approx(x_shift * lorentz_factor * mass * boost_speed));\n}\n\n}  // namespace\n\n// [[Timeout, 10]]\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Xcts.AdmLinearMomentum\",\n                  \"[Unit][PointwiseFunctions]\") {\n  {\n    INFO(\"Schwarzschild in Kerr-Schild coordinates\");\n    const double mass = 1.;\n    const double horizon_radius = 2. * mass;\n    const double boost_speed = 0.;\n    const Xcts::Solutions::WrappedGr<gr::Solutions::KerrSchild> solution(\n        mass, std::array<double, 3>{{0., 0., 0.}},\n        std::array<double, 3>{{0., 0., 0.}},\n        std::array<double, 3>{{0., boost_speed, 0.}});\n    for (const double distance : std::array<double, 3>({1.e4, 1.e5, 1.e6})) {\n      test_infinite_surface_integrals(distance, mass, horizon_radius,\n                                      boost_speed, solution);\n      test_infinite_volume_integral(distance, mass, horizon_radius, boost_speed,\n                                    solution);\n    }\n  }\n  {\n    INFO(\"Boosted Schwarzschild in Kerr-Schild coordinates\");\n    const double mass = 1.;\n    const double horizon_radius = 2. * mass;\n    const double boost_speed = 0.5;\n    const Xcts::Solutions::WrappedGr<gr::Solutions::KerrSchild> solution(\n        mass, std::array<double, 3>{{0., 0., 0.}},\n        std::array<double, 3>{{0., 0., 0.}},\n        std::array<double, 3>{{0., boost_speed, 0.}});\n    for (const double distance : std::array<double, 3>({1.e4, 1.e5, 1.e6})) {\n      test_infinite_surface_integrals(distance, mass, horizon_radius,\n                                      boost_speed, solution);\n      // Note: the volume integral currently doesn't work with this test case.\n    }\n  }\n  {\n    INFO(\"Schwarzschild in isotropic coordinates\");\n    const double mass = 1.;\n    const double horizon_radius = 0.5 * mass;\n    const double boost_speed = 0.;\n    const Xcts::Solutions::Schwarzschild solution(\n        mass, Xcts::Solutions::SchwarzschildCoordinates::Isotropic);\n    for (const double distance : std::array<double, 3>({1.e4, 1.e5, 1.e6})) {\n      test_infinite_surface_integrals(distance, mass, horizon_radius,\n                                      boost_speed, solution);\n      test_infinite_volume_integral(distance, mass, horizon_radius, boost_speed,\n                                    solution);\n    }\n  }\n\n  // Test integrands against Python implementation with random values.\n  const pypp::SetupLocalPythonEnvironment local_python_env{\n      \"PointwiseFunctions/Xcts\"};\n  const DataVector used_for_size{5};\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(\n          gsl::not_null<tnsr::II<DataVector, 3>*>, const Scalar<DataVector>&,\n          const tnsr::II<DataVector, 3>&, const tnsr::II<DataVector, 3>&,\n          const Scalar<DataVector>&)>(\n          &Xcts::adm_linear_momentum_surface_integrand),\n      \"AdmLinearMomentum\", {\"adm_linear_momentum_surface_integrand\"},\n      {{{-1., 1.}}}, used_for_size);\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(\n          gsl::not_null<tnsr::I<DataVector, 3>*>,\n          const tnsr::II<DataVector, 3>&, const Scalar<DataVector>&,\n          const tnsr::i<DataVector, 3>&, const tnsr::ii<DataVector, 3>&,\n          const tnsr::II<DataVector, 3>&, const tnsr::Ijj<DataVector, 3>&,\n          const tnsr::i<DataVector, 3>&)>(\n          &Xcts::adm_linear_momentum_volume_integrand),\n      \"AdmLinearMomentum\", {\"adm_linear_momentum_volume_integrand\"},\n      {{{-1, 1.}}}, used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/Test_CenterOfMass.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Determinant.hpp\"\n#include \"DataStructures/Tensor/EagerMath/Magnitude.hpp\"\n#include \"DataStructures/Tensor/Slice.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/AreaElement.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CreateInitialElement.hpp\"\n#include \"Domain/Creators/Sphere.hpp\"\n#include \"Domain/ElementMap.hpp\"\n#include \"Domain/ElementToBlockLogicalMap.hpp\"\n#include \"Domain/FaceNormal.hpp\"\n#include \"Domain/InterfaceLogicalCoordinates.hpp\"\n#include \"Domain/Structure/InitialElementIds.hpp\"\n#include \"NumericalAlgorithms/LinearOperators/DefiniteIntegral.hpp\"\n#include \"NumericalAlgorithms/Spectral/LogicalCoordinates.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/AnalyticSolutions/Xcts/Schwarzschild.hpp\"\n#include \"PointwiseFunctions/Xcts/CenterOfMass.hpp\"\n\nnamespace {\n\n/*\n  The tests below shift the isotropic Schwarzschild solution and check that the\n  center of mass corresponds to the coordinate shift.\n*/\n\nvoid test_infinite_surface_integral(const double distance,\n                                    const double z_shift) {\n  // Get Schwarzschild solution in isotropic coordinates.\n  const double mass = 1;\n  const Xcts::Solutions::Schwarzschild solution(\n      mass, Xcts::Solutions::SchwarzschildCoordinates::Isotropic);\n  const double horizon_radius = 0.5 * mass;\n\n  // Set up domain\n  const size_t h_refinement = 1;\n  const size_t p_refinement = 6;\n  const domain::creators::Sphere shell{\n      /* inner_radius */ z_shift + 1.1 * horizon_radius,\n      /* outer_radius */ distance,\n      /* interior */ domain::creators::Sphere::Excision{},\n      /* initial_refinement */ h_refinement,\n      /* initial_number_of_grid_points */ p_refinement + 1,\n      /* use_equiangular_map */ true,\n      /* equatorial_compression */ {},\n      /* radial_partitioning */ {},\n      /* radial_distribution */ domain::CoordinateMaps::Distribution::Inverse};\n  const auto shell_domain = shell.create_domain();\n  const auto& blocks = shell_domain.blocks();\n  const auto& initial_ref_levels = shell.initial_refinement_levels();\n  const auto element_ids = initial_element_ids(initial_ref_levels);\n  const Mesh<2> face_mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  // Initialize surface integral\n  tnsr::I<double, 3> surface_integral({0., 0., 0.});\n\n  // Compute integrals by summing over each element\n  for (const auto& element_id : element_ids) {\n    // Skip elements not at the outer boundary\n    const size_t radial_dimension = 2;\n    const auto radial_segment_id = element_id.segment_id(radial_dimension);\n    if (radial_segment_id.index() !=\n        two_to_the(radial_segment_id.refinement_level()) - 1) {\n      continue;\n    }\n\n    // Get element information\n    const auto current_element =\n        domain::create_initial_element(element_id, blocks, initial_ref_levels);\n    const auto& current_block = blocks.at(element_id.block_id());\n    const ElementMap<3, Frame::Inertial> logical_to_inertial_map(\n        element_id, current_block.stationary_map().get_clone());\n\n    // Loop over external boundaries\n    for (auto boundary_direction : current_element.external_boundaries()) {\n      // Skip interfaces not at the outer boundary\n      if (boundary_direction != Direction<3>::upper_zeta()) {\n        continue;\n      }\n\n      // Get interface coordinates\n      const auto logical_coords =\n          interface_logical_coordinates(face_mesh, boundary_direction);\n      const auto inertial_coords = logical_to_inertial_map(logical_coords);\n      const auto inv_jacobian =\n          logical_to_inertial_map.inv_jacobian(logical_coords);\n\n      // Shift coordinates used to get analytic solution\n      auto shifted_coords = inertial_coords;\n      shifted_coords.get(2) -= z_shift;\n\n      // Get required fields on the interface\n      const auto shifted_fields = solution.variables(\n          shifted_coords,\n          tmpl::list<Xcts::Tags::ConformalFactorMinusOne<DataVector>>{});\n      const auto& conformal_factor_minus_one =\n          get<Xcts::Tags::ConformalFactorMinusOne<DataVector>>(shifted_fields);\n\n      // Compute area element\n      const auto flat_area_element =\n          euclidean_area_element(inv_jacobian, boundary_direction);\n\n      // Evaluate surface integral\n      const auto surface_integrand = Xcts::center_of_mass_surface_integrand(\n          conformal_factor_minus_one, inertial_coords);\n      for (int I = 0; I < 3; I++) {\n        surface_integral.get(I) += definite_integral(\n            surface_integrand.get(I) * get(flat_area_element), face_mesh);\n      }\n    }\n  }\n\n  // Check result\n  auto custom_approx = Approx::custom().epsilon(1. / distance).scale(1.0);\n  CHECK(get<0>(surface_integral) == custom_approx(0.));\n  CHECK(get<1>(surface_integral) == custom_approx(0.));\n  CHECK(get<2>(surface_integral) / mass == custom_approx(z_shift));\n}\n\nvoid test_infinite_volume_integral(const double distance,\n                                   const double z_shift) {\n  // Get Schwarzschild solution in isotropic coordinates.\n  const double mass = 1;\n  const Xcts::Solutions::Schwarzschild solution(\n      mass, Xcts::Solutions::SchwarzschildCoordinates::Isotropic);\n  const double horizon_radius = 0.5 * mass;\n\n  // Set up domain\n  const size_t h_refinement = 1;\n  const size_t p_refinement = 11;\n  const domain::creators::Sphere shell{\n      /* inner_radius */ z_shift + 1.1 * horizon_radius,\n      /* outer_radius */ distance,\n      /* interior */ domain::creators::Sphere::Excision{},\n      /* initial_refinement */ h_refinement,\n      /* initial_number_of_grid_points */ p_refinement + 1,\n      /* use_equiangular_map */ true,\n      /* equatorial_compression */ {},\n      /* radial_partitioning */ {},\n      /* radial_distribution */ domain::CoordinateMaps::Distribution::Inverse};\n  const auto shell_domain = shell.create_domain();\n  const auto& blocks = shell_domain.blocks();\n  const auto& initial_ref_levels = shell.initial_refinement_levels();\n  const auto element_ids = initial_element_ids(initial_ref_levels);\n  const Mesh<3> mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const Mesh<2> face_mesh{p_refinement + 1, Spectral::Basis::Legendre,\n                          Spectral::Quadrature::GaussLobatto};\n\n  // Initialize \"reduced\" integral\n  tnsr::I<double, 3> total_integral({0., 0., 0.});\n\n  // Compute integrals by summing over each element\n  for (const auto& element_id : element_ids) {\n    // Get element information\n    const auto current_element =\n        domain::create_initial_element(element_id, blocks, initial_ref_levels);\n    const auto& current_block = blocks.at(element_id.block_id());\n    const ElementMap<3, Frame::Inertial> logical_to_inertial_map(\n        element_id, current_block.stationary_map().get_clone());\n\n    // Get 3D coordinates\n    const auto logical_coords = logical_coordinates(mesh);\n    const auto inertial_coords = logical_to_inertial_map(logical_coords);\n    const auto jacobian = logical_to_inertial_map.jacobian(logical_coords);\n    const auto det_jacobian = determinant(jacobian);\n\n    // Shift coordinates used to get analytic solution\n    auto shifted_coords = inertial_coords;\n    shifted_coords.get(2) -= z_shift;\n\n    // Get required fields\n    const auto shifted_fields = solution.variables(\n        shifted_coords,\n        tmpl::list<\n            Xcts::Tags::ConformalFactor<DataVector>,\n            Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n            ::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n                          tmpl::size_t<3>, Frame::Inertial>>{});\n    const auto& conformal_factor =\n        get<Xcts::Tags::ConformalFactor<DataVector>>(shifted_fields);\n    const auto& conformal_factor_minus_one =\n        get<Xcts::Tags::ConformalFactorMinusOne<DataVector>>(shifted_fields);\n    const auto& deriv_conformal_factor =\n        get<::Tags::deriv<Xcts::Tags::ConformalFactorMinusOne<DataVector>,\n                          tmpl::size_t<3>, Frame::Inertial>>(shifted_fields);\n\n    // Evaluate volume integral.\n    const auto volume_integrand = Xcts::center_of_mass_volume_integrand(\n        conformal_factor, deriv_conformal_factor, inertial_coords);\n    for (int I = 0; I < 3; I++) {\n      total_integral.get(I) +=\n          definite_integral(volume_integrand.get(I) * get(det_jacobian), mesh);\n    }\n\n    // Loop over external boundaries\n    for (auto boundary_direction : current_element.external_boundaries()) {\n      // Skip interfaces not at the inner boundary\n      if (boundary_direction != Direction<3>::lower_zeta()) {\n        continue;\n      }\n\n      // Get interface coordinates.\n      const auto face_logical_coords =\n          interface_logical_coordinates(face_mesh, boundary_direction);\n      const auto face_inv_jacobian =\n          logical_to_inertial_map.inv_jacobian(face_logical_coords);\n\n      // Slice required fields to the interface\n      const size_t slice_index =\n          index_to_slice_at(mesh.extents(), boundary_direction);\n      const auto& face_conformal_factor_minus_one =\n          data_on_slice(conformal_factor_minus_one, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n      const auto& face_inertial_coords =\n          data_on_slice(inertial_coords, mesh.extents(),\n                        boundary_direction.dimension(), slice_index);\n\n      // Compute Euclidean area element\n      const auto flat_area_element =\n          euclidean_area_element(face_inv_jacobian, boundary_direction);\n\n      // Evaluate surface integral.\n      const auto surface_integrand = Xcts::center_of_mass_surface_integrand(\n          face_conformal_factor_minus_one, face_inertial_coords);\n      for (int I = 0; I < 3; I++) {\n        total_integral.get(I) += definite_integral(\n            surface_integrand.get(I) * get(flat_area_element), face_mesh);\n      }\n    }\n  }\n\n  // Check result\n  auto custom_approx = Approx::custom().epsilon(1.e-3).scale(1.0);\n  CHECK(get<0>(total_integral) == custom_approx(0.));\n  CHECK(get<1>(total_integral) == custom_approx(0.));\n  CHECK(get<2>(total_integral) / mass == custom_approx(z_shift));\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Xcts.CenterOfMass\",\n                  \"[Unit][PointwiseFunctions]\") {\n  // The surface integral converges with distance up to R = 10^7, after which a\n  // residual asymptote is reached around ~ 10^-8.\n  for (const double distance :\n       std::array<double, 5>({1.e3, 1.e4, 1.e5, 1.e6, 1.e7})) {\n    test_infinite_surface_integral(distance, 0.);\n    test_infinite_surface_integral(distance, 0.1);\n  }\n\n  // The volume integral currently suffers more from round-off errors than the\n  // surface integral, starting to dominate at R > 10^5. Here, we simply test\n  // that its error is below a fixed tolerance of 10^-3. This is not a\n  // convergence test, but rather a check that the error does not grow too\n  // large.\n  for (const double distance : std::array<double, 3>({1.e3, 1.e4, 1.e5})) {\n    test_infinite_volume_integral(distance, 0.);\n    test_infinite_volume_integral(distance, 0.1);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/Test_ExtrinsicCurvature.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"PointwiseFunctions/Xcts/ExtrinsicCurvature.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Xcts.ExtrinsicCurvature\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"PointwiseFunctions/Xcts\"};\n  const DataVector used_for_size{5};\n  pypp::check_with_random_values<1>(\n      static_cast<void (*)(\n          gsl::not_null<tnsr::ii<DataVector, 3>*>, const Scalar<DataVector>&,\n          const Scalar<DataVector>&, const tnsr::ii<DataVector, 3>&,\n          const tnsr::II<DataVector, 3>&, const Scalar<DataVector>&)>(\n          &Xcts::extrinsic_curvature<DataVector>),\n      \"ExtrinsicCurvature\", {\"extrinsic_curvature\"}, {{{-1., 1.}}},\n      used_for_size);\n}\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/Test_LongitudinalOperator.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/EagerMath/DeterminantAndInverse.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Helpers/PointwiseFunctions/GeneralRelativity/TestHelpers.hpp\"\n#include \"PointwiseFunctions/Elasticity/Strain.hpp\"\n#include \"PointwiseFunctions/GeneralRelativity/Christoffel.hpp\"\n#include \"PointwiseFunctions/Xcts/LongitudinalOperator.hpp\"\n\nnamespace Xcts {\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Xcts.LongitudinalOperator\",\n                  \"[Unit][PointwiseFunctions]\") {\n  {\n    INFO(\"Regression test with Python\");\n    pypp::SetupLocalPythonEnvironment local_python_env{\n        \"PointwiseFunctions/Xcts\"};\n    const DataVector used_for_size{5};\n    pypp::check_with_random_values<1>(\n        static_cast<void (*)(gsl::not_null<tnsr::II<DataVector, 3>*>,\n                             const tnsr::ii<DataVector, 3>&,\n                             const tnsr::II<DataVector, 3>&)>(\n            &Xcts::longitudinal_operator<DataVector>),\n        \"LongitudinalOperator\", {\"longitudinal_operator\"}, {{{-1., 1.}}},\n        used_for_size);\n    pypp::check_with_random_values<1>(\n        static_cast<void (*)(gsl::not_null<tnsr::II<DataVector, 3>*>,\n                             const tnsr::ii<DataVector, 3>&)>(\n            &Xcts::longitudinal_operator_flat_cartesian<DataVector>),\n        \"LongitudinalOperator\", {\"longitudinal_operator_flat_cartesian\"},\n        {{{-1., 1.}}}, used_for_size);\n  }\n  {\n    INFO(\"Test equivalence of overloads\");\n    MAKE_GENERATOR(generator);\n    const DataVector used_for_size(5);\n    std::uniform_real_distribution<> dist{0., 1.};\n    const auto spatial_metric =\n        TestHelpers::gr::random_spatial_metric<3, DataVector, Frame::Inertial>(\n            make_not_null(&generator), used_for_size);\n    const auto inv_spatial_metric =\n        determinant_and_inverse(spatial_metric).second;\n    const auto shift = make_with_random_values<tnsr::I<DataVector, 3>>(\n        make_not_null(&generator), make_not_null(&dist), used_for_size);\n\n    const auto deriv_spatial_metric =\n        make_with_random_values<tnsr::ijj<DataVector, 3>>(\n            make_not_null(&generator), make_not_null(&dist),\n            used_for_size);\n    const auto deriv_shift = make_with_random_values<tnsr::iJ<DataVector, 3>>(\n        make_not_null(&generator), make_not_null(&dist), used_for_size);\n\n    const auto christoffel_first_kind =\n        ::gr::christoffel_first_kind(deriv_spatial_metric);\n    const auto christoffel_second_kind =\n        ::gr::christoffel_second_kind(deriv_spatial_metric, inv_spatial_metric);\n\n    tnsr::ii<DataVector, 3> strain{used_for_size.size()};\n    Elasticity::strain(make_not_null(&strain), deriv_shift, spatial_metric,\n                       deriv_spatial_metric, christoffel_first_kind, shift);\n    tnsr::II<DataVector, 3> result1{used_for_size.size()};\n    longitudinal_operator(make_not_null(&result1), strain, inv_spatial_metric);\n    tnsr::II<DataVector, 3> result2{used_for_size.size()};\n    longitudinal_operator(make_not_null(&result2), shift, deriv_shift,\n                          inv_spatial_metric, christoffel_second_kind);\n    CHECK_ITERABLE_APPROX(result1, result2);\n  }\n}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/Test_SpacetimeQuantities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/CheckWithRandomValues.hpp\"\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"PointwiseFunctions/Xcts/SpacetimeQuantities.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace Xcts {\n\nnamespace {\ntemplate <typename Computed, typename... Args>\nvoid check_with_python(const Computed& computed,\n                       const std::string& function_name, const Args&... args) {\n  CAPTURE(function_name);\n  const auto expected = pypp::call<Computed>(\n      \"PointwiseFunctions.Xcts.SpacetimeQuantities\", function_name, args...);\n  Approx custom_approx = Approx::custom().epsilon(1.e-10).scale(1.);\n  CHECK_ITERABLE_CUSTOM_APPROX(computed, expected, custom_approx);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.PointwiseFunctions.Xcts.SpacetimeQuantities\",\n                  \"[Unit][PointwiseFunctions]\") {\n  pypp::SetupLocalPythonEnvironment local_python_env{\"\"};\n  // Set up a mesh for numerical derivatives\n  const Mesh<3> mesh{8, Spectral::Basis::Legendre,\n                     Spectral::Quadrature::GaussLobatto};\n  const size_t num_points = mesh.number_of_grid_points();\n  auto inv_jacobian = make_with_value<\n      InverseJacobian<DataVector, 3, Frame::ElementLogical, Frame::Inertial>>(\n      num_points, 0.);\n  get<0, 0>(inv_jacobian) = 1.;\n  get<1, 1>(inv_jacobian) = 1.;\n  get<2, 2>(inv_jacobian) = 1.;\n  // Generate random input vars\n  MAKE_GENERATOR(gen);\n  std::uniform_real_distribution<double> dist_positive{0.5, 2.};\n  std::uniform_real_distribution<double> dist_factor_minus_one{-0.5, 0.5};\n  std::uniform_real_distribution<double> dist_isotropic{-1., 1.};\n  const auto conformal_factor_minus_one =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist_factor_minus_one),\n          num_points);\n  const Scalar<DataVector> conformal_factor{get(conformal_factor_minus_one) +\n                                            1.};\n  const auto lapse_times_conformal_factor_minus_one =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist_factor_minus_one),\n          num_points);\n  const Scalar<DataVector> lapse_times_conformal_factor{\n      get(lapse_times_conformal_factor_minus_one) + 1.};\n  const auto shift_excess = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto conformal_metric =\n      make_with_random_values<tnsr::ii<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto inv_conformal_metric =\n      make_with_random_values<tnsr::II<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto deriv_conformal_metric =\n      make_with_random_values<tnsr::ijj<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto conformal_christoffel_first_kind =\n      make_with_random_values<tnsr::ijj<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto conformal_christoffel_second_kind =\n      make_with_random_values<tnsr::Ijj<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto conformal_christoffel_contracted =\n      make_with_random_values<tnsr::i<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto conformal_ricci_tensor =\n      make_with_random_values<tnsr::ii<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto conformal_ricci_scalar =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto trace_extrinsic_curvature =\n      make_with_random_values<Scalar<DataVector>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto deriv_trace_extrinsic_curvature =\n      make_with_random_values<tnsr::i<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto shift_background = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto deriv_shift_background =\n      make_with_random_values<tnsr::iJ<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto longitudinal_shift_background_minus_dt_conformal_metric =\n      make_with_random_values<tnsr::II<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto div_longitudinal_shift_background_minus_dt_conformal_metric =\n      make_with_random_values<tnsr::I<DataVector, 3>>(\n          make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  const auto energy_density = make_with_random_values<Scalar<DataVector>>(\n      make_not_null(&gen), make_not_null(&dist_positive), num_points);\n  const auto momentum_density = make_with_random_values<tnsr::I<DataVector, 3>>(\n      make_not_null(&gen), make_not_null(&dist_isotropic), num_points);\n  // Check output vars\n  const auto box = db::create<\n      tmpl::remove_duplicates<typename Tags::SpacetimeQuantitiesCompute<\n          typename SpacetimeQuantities::tags_list>::argument_tags>,\n      db::AddComputeTags<Tags::SpacetimeQuantitiesCompute<\n          typename SpacetimeQuantities::tags_list>>>(\n      mesh, conformal_factor_minus_one, lapse_times_conformal_factor_minus_one,\n      shift_excess, conformal_metric, inv_conformal_metric,\n      deriv_conformal_metric, conformal_christoffel_first_kind,\n      conformal_christoffel_second_kind, conformal_christoffel_contracted,\n      conformal_ricci_tensor, conformal_ricci_scalar, trace_extrinsic_curvature,\n      deriv_trace_extrinsic_curvature, shift_background, deriv_shift_background,\n      longitudinal_shift_background_minus_dt_conformal_metric,\n      div_longitudinal_shift_background_minus_dt_conformal_metric,\n      energy_density, momentum_density, std::move(inv_jacobian));\n  const auto& vars =\n      get<::Tags::Variables<typename SpacetimeQuantities::tags_list>>(box);\n  const auto& deriv_conformal_factor =\n      get<::Tags::deriv<Tags::ConformalFactor<DataVector>, tmpl::size_t<3>,\n                        Frame::Inertial>>(box);\n  const auto& deriv_lapse_times_conformal_factor =\n      get<::Tags::deriv<Tags::LapseTimesConformalFactor<DataVector>,\n                        tmpl::size_t<3>, Frame::Inertial>>(box);\n  check_with_python(get<gr::Tags::SpatialMetric<DataVector, 3>>(vars),\n                    \"spatial_metric\", conformal_factor, conformal_metric);\n  check_with_python(get<gr::Tags::InverseSpatialMetric<DataVector, 3>>(vars),\n                    \"inv_spatial_metric\", conformal_factor,\n                    inv_conformal_metric);\n  check_with_python(\n      get<::Tags::deriv<gr::Tags::InverseSpatialMetric<DataVector, 3>,\n                        tmpl::size_t<3>, Frame::Inertial>>(vars),\n      \"deriv_inv_spatial_metric\", conformal_factor, deriv_conformal_factor,\n      inv_conformal_metric, deriv_conformal_metric);\n  check_with_python(\n      get<gr::Tags::SpatialChristoffelSecondKind<DataVector, 3>>(vars),\n      \"spatial_christoffel_second_kind\", conformal_factor,\n      deriv_conformal_factor, conformal_metric, inv_conformal_metric,\n      conformal_christoffel_second_kind);\n  check_with_python(get<gr::Tags::Lapse<DataVector>>(vars), \"lapse\",\n                    conformal_factor, lapse_times_conformal_factor);\n  check_with_python(get<::Tags::deriv<gr::Tags::Lapse<DataVector>,\n                                      tmpl::size_t<3>, Frame::Inertial>>(vars),\n                    \"deriv_lapse\", conformal_factor, deriv_conformal_factor,\n                    lapse_times_conformal_factor,\n                    deriv_lapse_times_conformal_factor);\n  check_with_python(get<gr::Tags::Shift<DataVector, 3>>(vars), \"shift\",\n                    shift_excess, shift_background);\n  check_with_python(\n      get<::Tags::deriv<gr::Tags::Shift<DataVector, 3>, tmpl::size_t<3>,\n                        Frame::Inertial>>(vars),\n      \"deriv_shift\",\n      get<::Tags::deriv<Tags::ShiftExcess<DataVector, 3, Frame::Inertial>,\n                        tmpl::size_t<3>, Frame::Inertial>>(vars),\n      deriv_shift_background);\n  check_with_python(\n      get<gr::Tags::ExtrinsicCurvature<DataVector, 3>>(vars),\n      \"extrinsic_curvature\", conformal_factor,\n      get<gr::Tags::Lapse<DataVector>>(vars), conformal_metric,\n      get<detail::LongitudinalShiftMinusDtConformalMetric<DataVector>>(vars),\n      trace_extrinsic_curvature);\n  // Constraints and other quantities are tested for various analytic solutions\n  // in: Helpers/PointwiseFunctions/AnalyticSolutions/Xcts/VerifySolution.hpp\n}\n\n}  // namespace Xcts\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/Xcts/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/PointwiseFunctions/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/Unit/TestMain.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <catch2/catch_session.hpp>\n#include <catch2/catch_tostring.hpp>\n#include <catch2/reporters/catch_reporter_event_listener.hpp>\n#include <catch2/reporters/catch_reporter_registrars.hpp>\n#include <cstddef>\n#include <memory>\n\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Parallel/InitializationFunctions.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n#include \"Utilities/Kokkos/KokkosCore.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n\nclass TestRunListener : public Catch::EventListenerBase {\n public:\n  using Catch::EventListenerBase::EventListenerBase;\n\n  void fatalErrorEncountered(Catch::StringRef error) { ERROR(error); }\n};\n\nCATCH_REGISTER_LISTENER(TestRunListener)\n\n// Charm looks for this function but since we build without a main function or\n// main module we just have it be empty\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmissing-declarations\"\nextern \"C\" void CkRegisterMainModule(void) {}\n#pragma GCC diagnostic pop\n\nint main(int argc, char* argv[]) {\n#ifdef SPECTRE_KOKKOS\n  Kokkos::initialize(argc, argv);\n#endif\n  setup_error_handling();\n  setup_memory_allocation_failure_reporting();\n  Parallel::printf(\"%s\", info_from_build().c_str());\n  enable_floating_point_exceptions();\n  enable_segfault_handler();\n  Catch::StringMaker<double>::precision =\n      std::numeric_limits<double>::max_digits10;\n  Catch::StringMaker<float>::precision =\n      std::numeric_limits<float>::max_digits10;\n\n  const int result = Catch::Session().run(argc, argv);\n\n  // Clean up the Python environment only after all tests have finished running,\n  // since there could be multiple tests run in a single executable launch.\n  pypp::SetupLocalPythonEnvironment::finalize_env();\n\n#ifdef SPECTRE_KOKKOS\n  Kokkos::finalize();\n#endif\n  return result;\n}\n"
  },
  {
    "path": "tests/Unit/TestMainCharm.ci",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\nmainmodule TestMainCharm {\n\n  mainchare TestMainCharm {\n    entry TestMainCharm(CkArgMsg * msg);\n  };\n\n};\n"
  },
  {
    "path": "tests/Unit/TestMainCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"TestMainCharm.hpp\"\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <charm++.h>\n#include <cstddef>\n#include <exception>\n#include <limits>\n#include <memory>\n#include <string>\n\n#include \"Framework/SetupLocalPythonEnvironment.hpp\"\n#include \"Informer/InfoFromBuild.hpp\"\n#include \"Parallel/InitializationFunctions.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n#include \"Utilities/MemoryHelpers.hpp\"\n#include \"Utilities/System/Abort.hpp\"\n#include \"Utilities/System/Exit.hpp\"\n\nTestMainCharm::TestMainCharm(CkArgMsg* msg) {\n  setup_error_handling();\n  setup_memory_allocation_failure_reporting();\n  Parallel::printf(\"%s\", info_from_build().c_str());\n  enable_floating_point_exceptions();\n  enable_segfault_handler();\n  Catch::StringMaker<double>::precision =\n      std::numeric_limits<double>::max_digits10;\n  Catch::StringMaker<float>::precision =\n      std::numeric_limits<float>::max_digits10;\n  const int result = Catch::Session().run(msg->argc, msg->argv);\n  // Clean up the Python environment only after all tests have finished running,\n  // since there could be multiple tests run in a single executable launch.\n  pypp::SetupLocalPythonEnvironment::finalize_env();\n  if (0 == result) {\n    sys::exit();\n  }\n  sys::abort(\"A catch test has failed.\");\n}\n\n#include \"TestMainCharm.def.h\"\n"
  },
  {
    "path": "tests/Unit/TestMainCharm.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"TestMainCharm.decl.h\"\n\n/// \\cond\nclass CkArgMsg;\n/// \\endcond\n\n/// Main executable for running unit tests in a Charm++ environment\nclass TestMainCharm : public CBase_TestMainCharm {\n public:\n  [[noreturn]] explicit TestMainCharm(CkArgMsg* msg);\n};\n"
  },
  {
    "path": "tests/Unit/Time/Actions/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Actions/Test_SelfStartActions.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Time/Actions/Test_SelfStartActions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <cstdint>\n#include <deque>\n#include <initializer_list>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <tuple>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Evolution/Conservative/UpdatePrimitives.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Parallel/AlgorithmExecution.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"ParallelAlgorithms/Actions/MutateApply.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Time/Actions/SelfStartActions.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/StepNumberWithinSlab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nclass TimeStepper;\n\nnamespace {\nstruct TemporalId {\n  template <typename Tag>\n  using step_prefix = Tags::dt<Tag>;\n};\n\nstruct Var : db::SimpleTag {\n  using type = double;\n};\n\nstruct ComplexVar : db::SimpleTag {\n  using type = std::complex<double>;\n};\n\nstruct PrimitiveVar : db::SimpleTag {\n  using type = double;\n};\n\nstruct ComputeTimeDerivative {\n  template <typename DbTagsList, typename... InboxTags, typename Metavariables,\n            typename ArrayIndex, typename ActionList,\n            typename ParallelComponent>\n  static Parallel::iterable_action_return_t apply(\n      db::DataBox<DbTagsList>& box,\n      tuples::TaggedTuple<InboxTags...>& /*inboxes*/,\n      const Parallel::GlobalCache<Metavariables>& /*cache*/,\n      const ArrayIndex& /*array_index*/, ActionList /*meta*/,\n      const ParallelComponent* const /*meta*/) {\n    using argument_tag = tmpl::conditional_t<\n        Metavariables::system::has_primitive_and_conservative_vars,\n        PrimitiveVar, Var>;\n    db::mutate<Tags::dt<Var>>([](const gsl::not_null<double*> dt_var,\n                                 const double var) { *dt_var = exp(var); },\n                              make_not_null(&box), db::get<argument_tag>(box));\n    return {Parallel::AlgorithmExecution::Continue, std::nullopt};\n  }\n};\n\ntemplate <bool HasPrimitives = false>\nstruct System {\n  static constexpr bool has_primitive_and_conservative_vars = false;\n  using variables_tag = Var;\n  // Do not define primitive_variables_tag here.  Actions must work without it.\n\n  // Only used by the test\n  using test_primitive_variables_tags = tmpl::list<>;\n};\n\ntemplate <>\nstruct System<true> {\n  static constexpr bool has_primitive_and_conservative_vars = true;\n  using variables_tag = Var;\n  using primitive_variables_tag = PrimitiveVar;\n  // Only used by the test\n  using test_primitive_variables_tags = tmpl::list<primitive_variables_tag>;\n\n  template <typename>\n  struct primitive_from_conservative {\n    using return_tags = tmpl::list<PrimitiveVar>;\n    using argument_tags = tmpl::list<Var>;\n    static void apply(const gsl::not_null<double*> prim, const double cons) {\n      *prim = cons;\n    }\n  };\n};\n\nusing history_tag = Tags::HistoryEvolvedVariables<Var>;\nusing additional_history_tag = Tags::HistoryEvolvedVariables<ComplexVar>;\n\ntemplate <typename Metavariables>\nstruct Component;\n\ntemplate <bool HasPrimitives = false, bool MultipleHistories = false>\nstruct Metavariables {\n  static constexpr bool has_primitives = HasPrimitives;\n  static constexpr bool multiple_histories = MultipleHistories;\n  using system = System<HasPrimitives>;\n  using component_list = tmpl::list<Component<Metavariables>>;\n  using ordered_list_of_primitive_recovery_schemes = tmpl::list<>;\n  using temporal_id = TemporalId;\n};\n\ntemplate <typename Metavariables>\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n  using const_global_cache_tags =\n      tmpl::list<Tags::ConcreteTimeStepper<TimeStepper>,\n                 Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>;\n  using simple_tags = tmpl::flatten<db::AddSimpleTags<\n      typename metavariables::system::variables_tag,\n      typename metavariables::system::test_primitive_variables_tags,\n      db::add_tag_prefix<Tags::dt,\n                         typename metavariables::system::variables_tag>,\n      history_tag,\n      tmpl::conditional_t<Metavariables::multiple_histories,\n                          additional_history_tag, tmpl::list<>>,\n      Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n      Tags::Time, Tags::StepNumberWithinSlab,\n      Tags::AdaptiveSteppingDiagnostics>>;\n  using compute_tags = time_stepper_ref_tags<TimeStepper>;\n\n  static constexpr bool has_primitives = Metavariables::has_primitives;\n\n  using step_actions = tmpl::list<\n      ComputeTimeDerivative,\n      Actions::MutateApply<\n          RecordTimeStepperData<typename metavariables::system>>,\n      Actions::MutateApply<UpdateU<typename metavariables::system, false>>,\n      Actions::MutateApply<CleanHistory<typename metavariables::system>>,\n      tmpl::conditional_t<has_primitives, Actions::UpdatePrimitives,\n                          tmpl::list<>>>;\n  using action_list = tmpl::flatten<\n      tmpl::list<SelfStart::self_start_procedure<\n                     step_actions, typename metavariables::system>,\n                 step_actions>>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing, action_list>>;\n};\n\ntemplate <bool HasPrimitives = false, bool MultipleHistories = false>\nusing MockRuntimeSystem = ActionTesting::MockRuntimeSystem<\n    Metavariables<HasPrimitives, MultipleHistories>>;\n\ntemplate <bool HasPrimitives = false, bool MultipleHistories = false>\nvoid emplace_component_and_initialize(\n    const gsl::not_null<MockRuntimeSystem<HasPrimitives, MultipleHistories>*>\n        runner,\n    const bool forward_in_time, const Time& initial_time,\n    const TimeDelta& initial_time_step, const size_t order,\n    const double initial_value) {\n  ActionTesting::emplace_component_and_initialize<\n      Component<Metavariables<HasPrimitives, MultipleHistories>>>(\n      runner, 0,\n      {initial_value, 0., typename history_tag::type{1}, TimeStepId{},\n       TimeStepId(forward_in_time, 1 - static_cast<int64_t>(order),\n                  initial_time),\n       initial_time_step, std::numeric_limits<double>::signaling_NaN(),\n       uint64_t{0}, Tags::AdaptiveSteppingDiagnostics::type{}});\n}\n\ntemplate <>\nvoid emplace_component_and_initialize<true, false>(\n    const gsl::not_null<MockRuntimeSystem<true, false>*> runner,\n    const bool forward_in_time, const Time& initial_time,\n    const TimeDelta& initial_time_step, const size_t order,\n    const double initial_value) {\n  ActionTesting::emplace_component_and_initialize<\n      Component<Metavariables<true, false>>>(\n      runner, 0,\n      {initial_value, initial_value, 0., typename history_tag::type{1},\n       TimeStepId{},\n       TimeStepId(forward_in_time, 1 - static_cast<int64_t>(order),\n                  initial_time),\n       initial_time_step, std::numeric_limits<double>::signaling_NaN(),\n       uint64_t{0}, Tags::AdaptiveSteppingDiagnostics::type{}});\n}\n\ntemplate <>\nvoid emplace_component_and_initialize<false, true>(\n    const gsl::not_null<MockRuntimeSystem<false, true>*> runner,\n    const bool forward_in_time, const Time& initial_time,\n    const TimeDelta& initial_time_step, const size_t order,\n    const double initial_value) {\n  ActionTesting::emplace_component_and_initialize<\n      Component<Metavariables<false, true>>>(\n      runner, 0,\n      {initial_value, 0., typename history_tag::type{1},\n       typename additional_history_tag::type{1}, TimeStepId{},\n       TimeStepId(forward_in_time, 1 - static_cast<int64_t>(order),\n                  initial_time),\n       initial_time_step, std::numeric_limits<double>::signaling_NaN(),\n       uint64_t{0}, Tags::AdaptiveSteppingDiagnostics::type{}});\n}\n\ntemplate <>\nvoid emplace_component_and_initialize<true, true>(\n    const gsl::not_null<MockRuntimeSystem<true, true>*> runner,\n    const bool forward_in_time, const Time& initial_time,\n    const TimeDelta& initial_time_step, const size_t order,\n    const double initial_value) {\n  ActionTesting::emplace_component_and_initialize<\n      Component<Metavariables<true, true>>>(\n      runner, 0,\n      {initial_value, initial_value, 0., typename history_tag::type{1},\n       typename additional_history_tag::type{1}, TimeStepId{},\n       TimeStepId(forward_in_time, 1 - static_cast<int64_t>(order),\n                  initial_time),\n       initial_time_step, std::numeric_limits<double>::signaling_NaN(),\n       uint64_t{0}, Tags::AdaptiveSteppingDiagnostics::type{}});\n}\n\ntemplate <typename T>\nstruct is_initialize : std::false_type {};\n\ntemplate <typename System, template <typename> typename CacheTagPrefix>\nstruct is_initialize<SelfStart::Actions::Initialize<System, CacheTagPrefix>>\n    : std::true_type {};\n\nusing not_self_start_action = std::negation<std::disjunction<\n    is_initialize<tmpl::_1>,\n    tt::is_a<SelfStart::Actions::CheckForCompletion, tmpl::_1>,\n    std::is_same<SelfStart::Actions::CheckForOrderIncrease, tmpl::_1>,\n    std::is_same<SelfStart::Actions::Cleanup, tmpl::_1>>>;\n\n// Run until an action satisfying the Stop metalambda is executed.\n// Fail a REQUIRE if any action not passing the Whitelist metalambda\n// is run first (as that would often lead to an infinite loop).\n// Returns true if the last action jumped.\ntemplate <typename Stop, typename Whitelist, bool MultipleHistories,\n          bool HasPrimitives>\nbool run_past(\n    const gsl::not_null<MockRuntimeSystem<HasPrimitives, MultipleHistories>*>\n        runner) {\n  for (;;) {\n    bool done = false;\n    const size_t current_action = ActionTesting::get_next_action_index<\n        Component<Metavariables<HasPrimitives, MultipleHistories>>>(*runner, 0);\n    size_t action_to_check = current_action;\n    tmpl::for_each<typename Component<\n        Metavariables<HasPrimitives, MultipleHistories>>::action_list>(\n        [&action_to_check, &done](const auto action) {\n          using Action = tmpl::type_from<decltype(action)>;\n          if (action_to_check-- == 0) {\n            INFO(pretty_type::get_name<Action>());\n            done = tmpl::apply<Stop, Action>::value;\n            REQUIRE((done or tmpl::apply<Whitelist, Action>::value));\n          }\n        });\n    ActionTesting::next_action<\n        Component<Metavariables<HasPrimitives, MultipleHistories>>>(runner, 0);\n    // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Branch) false positive\n    if (done) {\n      // Self-start does not use the automatic algorithm looping, so\n      // we don't have to check for the end.\n      return current_action + 1 !=\n             ActionTesting::get_next_action_index<\n                 Component<Metavariables<HasPrimitives, MultipleHistories>>>(\n                 *runner, 0);\n    }\n  }\n}\n\nvoid test_actions(const size_t order, const int step_denominator) {\n  const bool forward_in_time = step_denominator > 0;\n  const Slab slab(1., 3.);\n  const TimeDelta initial_time_step = slab.duration() / step_denominator;\n  const Time initial_time = forward_in_time ? slab.start() : slab.end();\n  const double initial_value = -1.;\n\n  MockRuntimeSystem<> runner{\n      {std::make_unique<TimeSteppers::AdamsBashforth>(order),\n       EventsAndTriggers{}}};\n  emplace_component_and_initialize(make_not_null(&runner), forward_in_time,\n                                   initial_time, initial_time_step, order,\n                                   initial_value);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  {\n    INFO(\"Initialize\");\n    const bool jumped =\n        run_past<is_initialize<tmpl::_1>, not_self_start_action>(\n            make_not_null(&runner));\n    CHECK(not jumped);\n    CHECK(ActionTesting::get_databox_tag<Component<Metavariables<>>,\n                                         Tags::StepNumberWithinSlab>(runner,\n                                                                     0) == 0);\n    CHECK(\n        get<0>(\n            ActionTesting::get_databox_tag<Component<Metavariables<>>,\n                                           SelfStart::Tags::InitialValue<Var>>(\n                runner, 0)) == initial_value);\n    CHECK(get<0>(ActionTesting::get_databox_tag<\n                 Component<Metavariables<>>,\n                 SelfStart::Tags::InitialValue<Tags::TimeStep>>(runner, 0)) ==\n          initial_time_step);\n    CHECK(ActionTesting::get_databox_tag<Component<Metavariables<>>, Var>(\n              runner, 0) == initial_value);\n    CHECK(\n        ActionTesting::get_databox_tag<Component<Metavariables<>>, history_tag>(\n            runner, 0)\n            .size() == 0);\n  }\n\n  for (size_t current_order = 1; current_order < order; ++current_order) {\n    CAPTURE(current_order);\n    for (size_t points = 0; points <= current_order; ++points) {\n      CAPTURE(points);\n      {\n        INFO(\"CheckForCompletion\");\n        const bool jumped = run_past<\n            tt::is_a<SelfStart::Actions::CheckForCompletion, tmpl::_1>,\n            not_self_start_action>(make_not_null(&runner));\n        CHECK(not jumped);\n        CHECK(ActionTesting::get_databox_tag<Component<Metavariables<>>,\n                                             history_tag>(runner, 0)\n                  .integration_order() == current_order);\n      }\n      {\n        INFO(\"CheckForOrderIncrease\");\n        const bool jumped = run_past<\n            std::is_same<SelfStart::Actions::CheckForOrderIncrease, tmpl::_1>,\n            not_self_start_action>(make_not_null(&runner));\n        CHECK(not jumped);\n        const auto next_time =\n            ActionTesting::get_databox_tag<Component<Metavariables<>>,\n                                           Tags::Next<Tags::TimeStepId>>(runner,\n                                                                         0)\n                .step_time();\n        CHECK((next_time == initial_time) == (points == current_order));\n      }\n    }\n  }\n\n  {\n    INFO(\"CheckForCompletion\");\n    const bool jumped = run_past<\n        tt::is_a<SelfStart::Actions::CheckForCompletion, tmpl::_1>,\n        not_self_start_action>(make_not_null(&runner));\n    CHECK(jumped);\n  }\n  {\n    INFO(\"Cleanup\");\n    // Make sure we reach Cleanup to check the flow is sane...\n    run_past<std::is_same<SelfStart::Actions::Cleanup, tmpl::_1>,\n             not_self_start_action>(make_not_null(&runner));\n    // ...and then finish the procedure.\n    while (not ActionTesting::get_terminate<Component<Metavariables<>>>(runner,\n                                                                        0)) {\n      ActionTesting::next_action<Component<Metavariables<>>>(\n          make_not_null(&runner), 0);\n    }\n    CHECK(ActionTesting::get_databox_tag<Component<Metavariables<>>,\n                                         Tags::StepNumberWithinSlab>(runner,\n                                                                     0) == 0);\n    CHECK(ActionTesting::get_databox_tag<Component<Metavariables<>>, Var>(\n              runner, 0) == initial_value);\n    CHECK(ActionTesting::get_databox_tag<Component<Metavariables<>>,\n                                         Tags::TimeStep>(runner, 0) ==\n          initial_time_step);\n    CHECK(ActionTesting::get_databox_tag<Component<Metavariables<>>,\n                                         Tags::TimeStepId>(runner, 0) ==\n          TimeStepId(forward_in_time, 0, initial_time));\n    // This test only uses Adams-Bashforth.\n    CHECK(ActionTesting::get_databox_tag<Component<Metavariables<>>,\n                                         Tags::Next<Tags::TimeStepId>>(runner,\n                                                                       0) ==\n          TimeStepId(forward_in_time, 0, initial_time + initial_time_step));\n    CHECK(\n        ActionTesting::get_databox_tag<Component<Metavariables<>>, history_tag>(\n            runner, 0)\n            .integration_order() == order);\n  }\n}\n\ntemplate <bool TestPrimitives, bool MultipleHistories>\ndouble error_in_step(const size_t order, const double step) {\n  const bool forward_in_time = step > 0.;\n  const auto slab = forward_in_time ? Slab::with_duration_from_start(1., step)\n                                    : Slab::with_duration_to_end(1., -step);\n  const TimeDelta initial_time_step =\n      (forward_in_time ? 1 : -1) * slab.duration();\n  const Time initial_time = forward_in_time ? slab.start() : slab.end();\n  const double initial_value = -1.;\n\n  using component = Component<Metavariables<TestPrimitives, MultipleHistories>>;\n  MockRuntimeSystem<TestPrimitives, MultipleHistories> runner{\n      {std::make_unique<TimeSteppers::AdamsBashforth>(order),\n       EventsAndTriggers{}}};\n  emplace_component_and_initialize<TestPrimitives, MultipleHistories>(\n      make_not_null(&runner), forward_in_time, initial_time, initial_time_step,\n      order, initial_value);\n\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  run_past<std::is_same<SelfStart::Actions::Cleanup, tmpl::_1>,\n           tmpl::bool_<true>, MultipleHistories>(make_not_null(&runner));\n  run_past<std::is_same<tmpl::pin<Actions::MutateApply<\n                            UpdateU<System<TestPrimitives>, false>>>,\n                        tmpl::_1>,\n           tmpl::bool_<true>, MultipleHistories>(make_not_null(&runner));\n\n  const double exact = -log(exp(-initial_value) - step);\n  return ActionTesting::get_databox_tag<component, Var>(runner, 0) - exact;\n}\n\ntemplate <bool TestPrimitives, bool MultipleHistories>\nvoid test_convergence(const size_t order, const bool forward_in_time) {\n  const double step = forward_in_time ? 0.1 : -0.1;\n  const double convergence_rate =\n      (log(abs(error_in_step<TestPrimitives, MultipleHistories>(order, step))) -\n       log(abs(error_in_step<TestPrimitives, MultipleHistories>(order,\n                                                                0.5 * step)))) /\n      log(2.);\n  // This measures the local truncation error, so order + 1.  It\n  // should be converging to an integer, so just check that it looks\n  // like the right one and don't worry too much about how close it\n  // is.\n  CHECK(convergence_rate == approx(order + 1).margin(0.1));\n}\n\nstruct DummyType {};\nstruct DummyTag : db::SimpleTag {\n  using type = DummyType;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Actions.SelfStart\", \"[Unit][Time][Actions]\") {\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  for (size_t order = 1; order < 5; ++order) {\n    CAPTURE(order);\n    for (const int step_denominator : {1, -1, 2, -2, 20, -20}) {\n      CAPTURE(step_denominator);\n      test_actions(order, step_denominator);\n    }\n    for (const bool forward_in_time : {true, false}) {\n      CAPTURE(forward_in_time);\n      test_convergence<false, false>(order, forward_in_time);\n      test_convergence<true, false>(order, forward_in_time);\n      test_convergence<false, true>(order, forward_in_time);\n      test_convergence<true, true>(order, forward_in_time);\n    }\n  }\n\n  TestHelpers::db::test_prefix_tag<SelfStart::Tags::InitialValue<DummyTag>>(\n      \"InitialValue(DummyTag)\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Time\")\n\nset(LIBRARY_SOURCES\n  Test_AdaptiveSteppingDiagnostics.cpp\n  Test_AdvanceTime.cpp\n  Test_ApproximateTime.cpp\n  Test_BoundaryHistory.cpp\n  Test_ChangeStepSize.cpp\n  Test_ChangeTimeStepperOrder.cpp\n  Test_ChooseLtsStepSize.cpp\n  Test_CleanHistory.cpp\n  Test_EvolutionOrdering.cpp\n  Test_History.cpp\n  Test_LargestStepperError.cpp\n  Test_RecordTimeStepperData.cpp\n  Test_SelfStart.cpp\n  Test_Slab.cpp\n  Test_SlabRoundingError.cpp\n  Test_StepperErrorTolerances.cpp\n  Test_Time.cpp\n  Test_TimeSequence.cpp\n  Test_TimeStepId.cpp\n  Test_TimeStepRequest.cpp\n  Test_TimeStepRequestProcessor.cpp\n  Test_UpdateU.cpp\n  )\n\nadd_subdirectory(Actions)\nadd_subdirectory(ChangeSlabSize)\nadd_subdirectory(OptionTags)\nadd_subdirectory(StepChoosers)\nadd_subdirectory(Tags)\nadd_subdirectory(TimeSteppers)\nadd_subdirectory(Triggers)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  CoordinateMaps\n  DataStructures\n  DataStructuresHelpers\n  Domain\n  DomainStructure\n  Evolution\n  Imex\n  Time\n  TimeStepperHelpers\n  Utilities\n  )\n"
  },
  {
    "path": "tests/Unit/Time/ChangeSlabSize/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  ChangeSlabSize/Test_Action.cpp\n  ChangeSlabSize/Test_ChangeSlabSize.cpp\n  ChangeSlabSize/Test_Tags.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Time/ChangeSlabSize/Test_Action.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdint>\n#include <initializer_list>\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Framework/ActionTesting.hpp\"\n#include \"Parallel/Phase.hpp\"\n#include \"Parallel/PhaseDependentActionList.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/ChangeSlabSize/Action.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Time/TimeStepRequestProcessor.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Tags {\ntemplate <typename Tag>\nstruct Next;\ntemplate <typename Tag>\nstruct dt;\n}  // namespace Tags\n\nnamespace {\nstruct Var : db::SimpleTag {\n  using type = double;\n};\n\nstruct Component;\n\nstruct Metavariables {\n  using component_list = tmpl::list<Component>;\n};\n\nstruct Component {\n  using metavariables = Metavariables;\n  using chare_type = ActionTesting::MockArrayChare;\n  using array_index = int;\n\n  using const_global_cache_tags =\n      tmpl::list<Tags::ConcreteTimeStepper<TimeStepper>>;\n\n  using simple_tags =\n      tmpl::list<Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n                 Tags::HistoryEvolvedVariables<Var>,\n                 Tags::AdaptiveSteppingDiagnostics,\n                 ::Tags::ChangeSlabSize::SlabSizeGoal>;\n  using compute_tags = time_stepper_ref_tags<TimeStepper>;\n  using phase_dependent_action_list = tmpl::list<\n      Parallel::PhaseActions<Parallel::Phase::Initialization,\n                             tmpl::list<ActionTesting::InitializeDataBox<\n                                 simple_tags, compute_tags>>>,\n      Parallel::PhaseActions<Parallel::Phase::Testing,\n                             tmpl::list<Actions::ChangeSlabSize>>>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Actions.ChangeSlabSize\", \"[Unit][Time][Actions]\") {\n  register_classes_with_charm<TimeSteppers::Rk3HesthavenSsp>();\n\n  ActionTesting::MockRuntimeSystem<Metavariables> runner{\n      {std::make_unique<TimeSteppers::Rk3HesthavenSsp>(), 1e-8}};\n\n  ActionTesting::emplace_component_and_initialize<Component>(&runner, 0, {});\n  ActionTesting::set_phase(make_not_null(&runner), Parallel::Phase::Testing);\n\n  auto& box = ActionTesting::get_databox<Component>(make_not_null(&runner), 0);\n\n  for (const bool time_runs_forward : {true, false}) {\n    const double unit_step = time_runs_forward ? 1.0 : -1.0;\n    Slab slab(1.5, 2.0);\n    Time start_time;\n\n    const auto resize_slab = [&slab, &start_time,\n                              &time_runs_forward](const double length) {\n      if (time_runs_forward) {\n        slab = slab.with_duration_from_start(length);\n        start_time = slab.start();\n      } else {\n        slab = slab.with_duration_to_end(length);\n        start_time = slab.end();\n      }\n    };\n\n    resize_slab(slab.duration().value());\n\n    const auto get_step = [](const TimeStepId& id) {\n      return (id.time_runs_forward() ? 1 : -1) *\n             id.step_time().slab().duration() / 2;\n    };\n\n    db::mutate<Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n               Tags::AdaptiveSteppingDiagnostics,\n               ::Tags::ChangeSlabSize::SlabSizeGoal>(\n        [&get_step, &start_time, &time_runs_forward](\n            const gsl::not_null<TimeStepId*> id,\n            const gsl::not_null<TimeStepId*> next_id,\n            const gsl::not_null<TimeDelta*> step,\n            const gsl::not_null<AdaptiveSteppingDiagnostics*> diags,\n            const gsl::not_null<double*> slab_size_goal,\n            const TimeStepper& stepper) {\n          *id = TimeStepId(time_runs_forward, 3, start_time);\n          *step = get_step(*id);\n          *next_id = stepper.next_time_id(*id, *step);\n          *diags = AdaptiveSteppingDiagnostics{1, 2, 3, 4, 5};\n          // Step is slab/2 for test\n          *slab_size_goal = (2 * *step).value();\n        },\n        make_not_null(&box), db::get<Tags::TimeStepper<TimeStepper>>(box));\n\n    using ExpectedMessages = ::Tags::ChangeSlabSize::NumberOfExpectedMessages;\n    using NewSize = ::Tags::ChangeSlabSize::NewSlabSize;\n\n    const auto check_box = [&box, &get_step](const TimeStepId& id,\n                                             const uint64_t changes,\n                                             const double expected_goal) {\n      CHECK(db::get<Tags::TimeStepId>(box) == id);\n      CHECK(db::get<Tags::TimeStep>(box) == get_step(id));\n      CHECK(db::get<Tags::Next<Tags::TimeStepId>>(box) ==\n            db::get<Tags::TimeStepper<TimeStepper>>(box).next_time_id(\n                db::get<Tags::TimeStepId>(box), db::get<Tags::TimeStep>(box)));\n      CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) ==\n            AdaptiveSteppingDiagnostics{1, 2 + changes, 3, 4, 5});\n      CHECK(db::get<::Tags::ChangeSlabSize::SlabSizeGoal>(box) ==\n            expected_goal);\n    };\n\n    // Nothing to do\n    {\n      runner.next_action<Component>(0);\n      check_box(TimeStepId(time_runs_forward, 3, start_time), 0,\n                ((time_runs_forward ? 1 : -1) * slab.duration()).value());\n    }\n\n    // Nothing to do, but not at the goal.\n    {\n      db::mutate<::Tags::ChangeSlabSize::SlabSizeGoal>(\n          [&unit_step](const gsl::not_null<double*> slab_size_goal) {\n            *slab_size_goal = 2.0 * unit_step;\n          },\n          make_not_null(&box));\n      runner.next_action<Component>(0);\n      resize_slab(2.0);\n      check_box(TimeStepId(time_runs_forward, 3, start_time), 1,\n                2.0 * unit_step);\n    }\n\n    // Simple case\n    {\n      db::mutate<ExpectedMessages>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected) {\n            ++(*expected)[3];\n          },\n          make_not_null(&box));\n      REQUIRE_FALSE(ActionTesting::next_action_if_ready<Component>(\n          make_not_null(&runner), 0));\n      db::mutate<NewSize>(\n          [&](const gsl::not_null<\n              std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            (*sizes)[3]\n                .emplace_back(time_runs_forward)\n                .process(TimeStepRequest{.size = unit_step});\n          },\n          make_not_null(&box));\n\n      runner.next_action<Component>(0);\n      resize_slab(1.0);\n      check_box(TimeStepId(time_runs_forward, 3, start_time), 2,\n                2.0 * unit_step);\n      CHECK(db::get<ExpectedMessages>(box).empty());\n      CHECK(db::get<NewSize>(box).empty());\n      db::mutate<ExpectedMessages, NewSize>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected,\n              const gsl::not_null<\n                  std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            expected->clear();\n            sizes->clear();\n          },\n          make_not_null(&box));\n    }\n\n    // Multiple messages at multiple times\n    {\n      db::mutate<ExpectedMessages>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected) {\n            (*expected)[3] += 2;\n            (*expected)[4] += 1;\n          },\n          make_not_null(&box));\n      REQUIRE_FALSE(ActionTesting::next_action_if_ready<Component>(\n          make_not_null(&runner), 0));\n      db::mutate<NewSize>(\n          [&](const gsl::not_null<\n              std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            (*sizes)[3]\n                .emplace_back(time_runs_forward)\n                .process(TimeStepRequest{.size_goal = 2.0 * unit_step});\n          },\n          make_not_null(&box));\n      REQUIRE_FALSE(ActionTesting::next_action_if_ready<Component>(\n          make_not_null(&runner), 0));\n      db::mutate<NewSize>(\n          [&](const gsl::not_null<\n              std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            (*sizes)[4]\n                .emplace_back(time_runs_forward)\n                .process(TimeStepRequest{.size_goal = 0.5 * unit_step});\n          },\n          make_not_null(&box));\n      REQUIRE_FALSE(ActionTesting::next_action_if_ready<Component>(\n          make_not_null(&runner), 0));\n      db::mutate<ExpectedMessages>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected) {\n            ++(*expected)[4];\n          },\n          make_not_null(&box));\n      db::mutate<NewSize>(\n          [&](const gsl::not_null<\n              std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            (*sizes)[3]\n                .emplace_back(time_runs_forward)\n                .process(TimeStepRequest{.size_goal = 3.0 * unit_step});\n          },\n          make_not_null(&box));\n      runner.next_action<Component>(0);\n      resize_slab(2.0);\n      check_box(TimeStepId(time_runs_forward, 3, start_time), 3,\n                2.0 * unit_step);\n      CHECK(db::get<ExpectedMessages>(box).size() == 1);\n      CHECK(db::get<ExpectedMessages>(box).count(4) == 1);\n      CHECK(db::get<NewSize>(box).size() == 1);\n      CHECK(db::get<NewSize>(box).count(4) == 1);\n      db::mutate<ExpectedMessages, NewSize>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected,\n              const gsl::not_null<\n                  std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            expected->clear();\n            sizes->clear();\n          },\n          make_not_null(&box));\n    }\n\n    // Check interior of slab\n    {\n      db::mutate<Tags::TimeStepId, Tags::Next<Tags::TimeStepId>>(\n          [&start_time, &time_runs_forward](\n              const gsl::not_null<TimeStepId*> id,\n              const gsl::not_null<TimeStepId*> next_id, const TimeDelta& step,\n              const TimeStepper& stepper) {\n            *id = TimeStepId(time_runs_forward, 3, start_time + step);\n            *next_id = stepper.next_time_id(*id, step);\n          },\n          make_not_null(&box), db::get<Tags::TimeStep>(box),\n          db::get<Tags::TimeStepper<TimeStepper>>(box));\n      const TimeStepId initial_id = db::get<Tags::TimeStepId>(box);\n      db::mutate<ExpectedMessages>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected) {\n            ++(*expected)[3];\n            ++(*expected)[4];\n          },\n          make_not_null(&box));\n      runner.next_action<Component>(0);\n      check_box(initial_id, 3, 2.0 * unit_step);\n      CHECK(db::get<ExpectedMessages>(box).size() == 2);\n      CHECK(db::get<ExpectedMessages>(box).count(3) == 1);\n      CHECK(db::get<ExpectedMessages>(box).count(4) == 1);\n      CHECK(db::get<NewSize>(box).empty());\n      db::mutate<NewSize>(\n          [&](const gsl::not_null<\n              std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            (*sizes)[3]\n                .emplace_back(time_runs_forward)\n                .process(TimeStepRequest{.size_goal = 0.1 * unit_step});\n            (*sizes)[4]\n                .emplace_back(time_runs_forward)\n                .process(TimeStepRequest{.size_goal = 0.1 * unit_step});\n          },\n          make_not_null(&box));\n      runner.next_action<Component>(0);\n      check_box(initial_id, 3, 2.0 * unit_step);\n      db::mutate<ExpectedMessages, NewSize>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected,\n              const gsl::not_null<\n                  std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            expected->clear();\n            sizes->clear();\n          },\n          make_not_null(&box));\n    }\n\n    // Check at a substep\n    {\n      db::mutate<Tags::TimeStepId, Tags::Next<Tags::TimeStepId>>(\n          [](const gsl::not_null<TimeStepId*> id,\n             const gsl::not_null<TimeStepId*> next_id, const TimeDelta& step,\n             const TimeStepper& stepper) {\n            const auto local_slab = id->step_time().slab();\n            while (id->substep_time() != local_slab.start().value() and\n                   id->substep_time() != local_slab.end().value()) {\n              *id = *next_id;\n              REQUIRE(id->substep() != 0);\n              *next_id = stepper.next_time_id(*id, step);\n            }\n          },\n          make_not_null(&box), db::get<Tags::TimeStep>(box),\n          db::get<Tags::TimeStepper<TimeStepper>>(box));\n      const TimeStepId initial_id = db::get<Tags::TimeStepId>(box);\n      db::mutate<ExpectedMessages>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected) {\n            ++(*expected)[3];\n            ++(*expected)[4];\n          },\n          make_not_null(&box));\n      runner.next_action<Component>(0);\n      check_box(initial_id, 3, 2.0 * unit_step);\n      CHECK(db::get<ExpectedMessages>(box).size() == 2);\n      CHECK(db::get<ExpectedMessages>(box).count(3) == 1);\n      CHECK(db::get<ExpectedMessages>(box).count(4) == 1);\n      CHECK(db::get<NewSize>(box).empty());\n      db::mutate<NewSize>(\n          [&](const gsl::not_null<\n              std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            (*sizes)[3]\n                .emplace_back(time_runs_forward)\n                .process(TimeStepRequest{.size_goal = 0.1 * unit_step});\n            (*sizes)[4]\n                .emplace_back(time_runs_forward)\n                .process(TimeStepRequest{.size_goal = 0.1 * unit_step});\n          },\n          make_not_null(&box));\n      runner.next_action<Component>(0);\n      check_box(initial_id, 3, 2.0 * unit_step);\n      db::mutate<ExpectedMessages, NewSize>(\n          [&](const gsl::not_null<std::map<int64_t, size_t>*> expected,\n              const gsl::not_null<\n                  std::map<int64_t, std::vector<TimeStepRequestProcessor>>*>\n                  sizes) {\n            expected->clear();\n            sizes->clear();\n          },\n          make_not_null(&box));\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/ChangeSlabSize/Test_ChangeSlabSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/ChangeSlabSize/ChangeSlabSize.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/MinimumTimeStep.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\nstruct Vars1 : db::SimpleTag {\n  using type = double;\n};\n\nstruct Vars2 : db::SimpleTag {\n  using type = double;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.ChangeSlabSize\", \"[Unit][Time]\") {\n  // Forward in time, no substeps\n  {\n    const TimeSteppers::AdamsBashforth time_stepper(1);\n    const Slab initial_slab(2.0, 3.0);\n    const TimeStepId initial_id(true, 5, initial_slab.start());\n    const TimeDelta initial_step = initial_slab.duration();\n    const TimeStepId next_id =\n        time_stepper.next_time_id(initial_id, initial_step);\n    const AdaptiveSteppingDiagnostics diagnostics{20, 30, 40, 50, 60};\n    TimeSteppers::History<Vars1::type> history1{};\n    TimeSteppers::History<Vars2::type> history2{};\n    history2.insert(initial_id, 1.23, 4.56);\n\n    auto box = db::create<\n        db::AddSimpleTags<\n            Tags::ConcreteTimeStepper<TimeStepper>, Tags::TimeStepId,\n            Tags::TimeStep, Tags::Next<Tags::TimeStepId>,\n            Tags::AdaptiveSteppingDiagnostics,\n            Tags::HistoryEvolvedVariables<Vars1>,\n            Tags::HistoryEvolvedVariables<Vars2>, ::Tags::MinimumTimeStep>,\n        time_stepper_ref_tags<TimeStepper>>(\n        static_cast<std::unique_ptr<TimeStepper>>(\n            std::make_unique<TimeSteppers::AdamsBashforth>(time_stepper)),\n        initial_id, initial_step, next_id, diagnostics, std::move(history1),\n        std::move(history2), 1e-8);\n\n    change_slab_size(make_not_null(&box), 5.0);\n\n    const Slab new_slab(2.0, 5.0);\n    const TimeStepId expected_id(true, 5, new_slab.start());\n    const TimeDelta expected_step = new_slab.duration();\n    const TimeStepId expected_next_id =\n        time_stepper.next_time_id(expected_id, expected_step);\n    auto expected_diagnostics = diagnostics;\n    ++expected_diagnostics.number_of_slab_size_changes;\n    TimeSteppers::History<Vars1::type> expected_history1{};\n    TimeSteppers::History<Vars2::type> expected_history2{};\n    expected_history2.insert(expected_id, 1.23, 4.56);\n\n    CHECK(db::get<Tags::TimeStepId>(box) == expected_id);\n    CHECK(db::get<Tags::TimeStep>(box) == expected_step);\n    CHECK(db::get<Tags::Next<Tags::TimeStepId>>(box) == expected_next_id);\n    CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) ==\n          expected_diagnostics);\n    CHECK(db::get<Tags::HistoryEvolvedVariables<Vars1>>(box) ==\n          expected_history1);\n    CHECK(db::get<Tags::HistoryEvolvedVariables<Vars2>>(box) ==\n          expected_history2);\n  }\n\n  // Backward in time, substep method\n  {\n    const TimeSteppers::Rk3HesthavenSsp time_stepper{};\n    const Slab initial_slab(2.0, 3.0);\n    const TimeStepId initial_id(false, 5, initial_slab.end());\n    const TimeDelta initial_step = -initial_slab.duration();\n    const TimeStepId next_id =\n        time_stepper.next_time_id(initial_id, initial_step);\n    const AdaptiveSteppingDiagnostics diagnostics{20, 30, 40, 50, 60};\n    TimeSteppers::History<Vars1::type> history1{};\n    TimeSteppers::History<Vars2::type> history2{};\n    history2.insert(initial_id, 1.23, 4.56);\n\n    auto box = db::create<\n        db::AddSimpleTags<\n            Tags::ConcreteTimeStepper<TimeStepper>, Tags::TimeStepId,\n            Tags::TimeStep, Tags::Next<Tags::TimeStepId>,\n            Tags::AdaptiveSteppingDiagnostics,\n            Tags::HistoryEvolvedVariables<Vars1>,\n            Tags::HistoryEvolvedVariables<Vars2>, ::Tags::MinimumTimeStep>,\n        time_stepper_ref_tags<TimeStepper>>(\n        static_cast<std::unique_ptr<TimeStepper>>(\n            std::make_unique<TimeSteppers::Rk3HesthavenSsp>(time_stepper)),\n        initial_id, initial_step, next_id, diagnostics, std::move(history1),\n        std::move(history2), 1e-8);\n\n    change_slab_size(make_not_null(&box), -1.0);\n\n    const Slab new_slab(-1.0, 3.0);\n    const TimeStepId expected_id(false, 5, new_slab.end());\n    const TimeDelta expected_step = -new_slab.duration();\n    const TimeStepId expected_next_id =\n        time_stepper.next_time_id(expected_id, expected_step);\n    auto expected_diagnostics = diagnostics;\n    ++expected_diagnostics.number_of_slab_size_changes;\n    TimeSteppers::History<Vars1::type> expected_history1{};\n    TimeSteppers::History<Vars2::type> expected_history2{};\n    expected_history2.insert(expected_id, 1.23, 4.56);\n\n    CHECK(db::get<Tags::TimeStepId>(box) == expected_id);\n    CHECK(db::get<Tags::TimeStep>(box) == expected_step);\n    CHECK(db::get<Tags::Next<Tags::TimeStepId>>(box) == expected_next_id);\n    CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) ==\n          expected_diagnostics);\n    CHECK(db::get<Tags::HistoryEvolvedVariables<Vars1>>(box) ==\n          expected_history1);\n    CHECK(db::get<Tags::HistoryEvolvedVariables<Vars2>>(box) ==\n          expected_history2);\n  }\n\n  // No change\n  {\n    const TimeSteppers::AdamsBashforth time_stepper(1);\n    const Slab initial_slab(2.0, 3.0);\n    const TimeStepId initial_id(true, 5, initial_slab.start());\n    const TimeDelta initial_step = initial_slab.duration();\n    const TimeStepId next_id =\n        time_stepper.next_time_id(initial_id, initial_step);\n    const AdaptiveSteppingDiagnostics diagnostics{20, 30, 40, 50, 60};\n    TimeSteppers::History<Vars1::type> history1{};\n    TimeSteppers::History<Vars2::type> history2{};\n    history2.insert(initial_id, 1.23, 4.56);\n\n    auto box = db::create<\n        db::AddSimpleTags<\n            Tags::ConcreteTimeStepper<TimeStepper>, Tags::TimeStepId,\n            Tags::TimeStep, Tags::Next<Tags::TimeStepId>,\n            Tags::AdaptiveSteppingDiagnostics,\n            Tags::HistoryEvolvedVariables<Vars1>,\n            Tags::HistoryEvolvedVariables<Vars2>, ::Tags::MinimumTimeStep>,\n        time_stepper_ref_tags<TimeStepper>>(\n        static_cast<std::unique_ptr<TimeStepper>>(\n            std::make_unique<TimeSteppers::AdamsBashforth>(time_stepper)),\n        initial_id, initial_step, next_id, diagnostics, history1, history2,\n        1e-8);\n\n    change_slab_size(make_not_null(&box), 3.0);\n\n    CHECK(db::get<Tags::TimeStepId>(box) == initial_id);\n    CHECK(db::get<Tags::TimeStep>(box) == initial_step);\n    CHECK(db::get<Tags::Next<Tags::TimeStepId>>(box) == next_id);\n    CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) == diagnostics);\n    CHECK(db::get<Tags::HistoryEvolvedVariables<Vars1>>(box) == history1);\n    CHECK(db::get<Tags::HistoryEvolvedVariables<Vars2>>(box) == history2);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/ChangeSlabSize/Test_Tags.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/ChangeSlabSize/Tags.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.ChangeSlabSize.Tags\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::ChangeSlabSize::NewSlabSize>(\n      \"NewSlabSize\");\n  TestHelpers::db::test_simple_tag<\n      Tags::ChangeSlabSize::NumberOfExpectedMessages>(\n      \"NumberOfExpectedMessages\");\n  TestHelpers::db::test_simple_tag<Tags::ChangeSlabSize::SlabSizeGoal>(\n      \"SlabSizeGoal\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/OptionTags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  OptionTags/Test_InitialSlabSize.cpp\n  OptionTags/Test_InitialTime.cpp\n  OptionTags/Test_InitialTimeStep.cpp\n  OptionTags/Test_LtsStepChoosers.cpp\n  OptionTags/Test_MinimumTimeStep.cpp\n  OptionTags/Test_TimeStepper.cpp\n  OptionTags/Test_VariableOrderAlgorithm.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Time/OptionTags/Test_InitialSlabSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Time/OptionTags/InitialSlabSize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.OptionTags.InitialSlabSize\", \"[Unit][Time]\") {\n  CHECK(TestHelpers::test_option_tag<OptionTags::InitialSlabSize>(\"0.01\") ==\n        0.01);\n}\n"
  },
  {
    "path": "tests/Unit/Time/OptionTags/Test_InitialTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Time/OptionTags/InitialTime.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.OptionTags.InitialTime\", \"[Unit][Time]\") {\n  CHECK(TestHelpers::test_option_tag<OptionTags::InitialTime>(\"0.01\") == 0.01);\n}\n"
  },
  {
    "path": "tests/Unit/Time/OptionTags/Test_InitialTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Time/OptionTags/InitialTimeStep.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.OptionTags.InitialTimeStep\", \"[Unit][Time]\") {\n  CHECK(TestHelpers::test_option_tag<OptionTags::InitialTimeStep>(\"0.01\") ==\n        0.01);\n}\n"
  },
  {
    "path": "tests/Unit/Time/OptionTags/Test_LtsStepChoosers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <typeinfo>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/OptionTags/LtsStepChoosers.hpp\"\n#include \"Time/StepChoosers/LimitIncrease.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<StepChoosers::LimitIncrease>>>;\n  };\n};\n\nSPECTRE_TEST_CASE(\"Unit.Time.OptionTags.LtsStepChoosers\", \"[Unit][Time]\") {\n  const auto choosers =\n      TestHelpers::test_option_tag<OptionTags::LtsStepChoosers, Metavariables>(\n          \"- LimitIncrease:\\n\"\n          \"    Factor: 3.0\\n\");\n  CHECK(choosers.size() == 1);\n  const auto& quiet_compiler = *choosers[0];\n  CHECK(typeid(quiet_compiler) == typeid(StepChoosers::LimitIncrease));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/OptionTags/Test_MinimumTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Time/OptionTags/MinimumTimeStep.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.OptionTags.MinimumTimeStep\", \"[Unit][Time]\") {\n  CHECK(TestHelpers::test_option_tag<OptionTags::MinimumTimeStep>(\"0.01\") ==\n        0.01);\n}\n"
  },
  {
    "path": "tests/Unit/Time/OptionTags/Test_TimeStepper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/OptionTags/TimeStepper.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<TimeStepper, tmpl::list<TimeSteppers::AdamsBashforth>>>;\n  };\n};\n\nSPECTRE_TEST_CASE(\"Unit.Time.OptionTags.TimeStepper\", \"[Unit][Time]\") {\n  const auto stepper =\n      TestHelpers::test_option_tag<OptionTags::TimeStepper<TimeStepper>,\n                                   Metavariables>(\n          \"AdamsBashforth:\\n\"\n          \"  Order: 7\\n\");\n  CHECK(stepper->order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(7));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/OptionTags/Test_VariableOrderAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Time/OptionTags/VariableOrderAlgorithm.hpp\"\n#include \"Time/VariableOrderAlgorithm.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.OptionTags.VariableOrderAlgorithm\",\n                  \"[Unit][Time]\") {\n  CHECK(TestHelpers::test_option_tag<OptionTags::VariableOrderAlgorithm>(\n            \"GoalOrder: 4\") == VariableOrderAlgorithm(4_st));\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  StepChoosers/Test_ByBlock.cpp\n  StepChoosers/Test_Cfl.cpp\n  StepChoosers/Test_Constant.cpp\n  StepChoosers/Test_ElementSizeCfl.cpp\n  StepChoosers/Test_ErrorControl.cpp\n  StepChoosers/Test_FixedLtsRatio.cpp\n  StepChoosers/Test_LimitIncrease.cpp\n  StepChoosers/Test_Maximum.cpp\n  StepChoosers/Test_PreventRapidIncrease.cpp\n  StepChoosers/Test_Random.cpp\n  StepChoosers/Test_StepToTimes.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_ByBlock.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/StepChoosers/ByBlock.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nconstexpr size_t volume_dim = 2;\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<StepChoosers::ByBlock<volume_dim>>>,\n                  tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                             tmpl::list<StepChoosers::ByBlock<volume_dim>>>>;\n  };\n  using component_list = tmpl::list<>;\n};\n\ntemplate <typename Use>\nvoid test_by_block() {\n  using ByBlock = StepChoosers::ByBlock<volume_dim>;\n\n  const ByBlock by_block({2.5, 3.0, 3.5});\n  const std::unique_ptr<StepChooser<Use>> by_block_base =\n      std::make_unique<ByBlock>(by_block);\n\n  const double current_step = std::numeric_limits<double>::infinity();\n  for (size_t block = 0; block < 3; ++block) {\n    const Element<volume_dim> element(ElementId<volume_dim>(block), {});\n    auto box = db::create<\n        db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                          domain::Tags::Element<volume_dim>>>(Metavariables{},\n                                                              element);\n    const TimeStepRequest expected{.size_goal =\n                                       0.5 * static_cast<double>(block + 5)};\n\n    CHECK(by_block(element, current_step) == expected);\n    CHECK(serialize_and_deserialize(by_block)(element, current_step) ==\n          expected);\n\n    CHECK(by_block_base->desired_step(current_step, box) == expected);\n    CHECK(serialize_and_deserialize(by_block_base)\n              ->desired_step(current_step, box) == expected);\n  }\n\n  TestHelpers::test_factory_creation<StepChooser<Use>, ByBlock>(\n      \"ByBlock:\\n\"\n      \"  Sizes: [1.0, 2.0]\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.ByBlock\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  test_by_block<StepChooserUse::LtsStep>();\n  test_by_block<StepChooserUse::Slab>();\n\n  CHECK(StepChoosers::ByBlock<1>{}.uses_local_data());\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_Cfl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/MinimumGridSpacing.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/StepChoosers/Cfl.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nconstexpr size_t dim = 1;\nusing frame = Frame::Grid;\n\nstruct CharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  using const_global_cache_tags = tmpl::list<>;\n  struct system {\n    static constexpr size_t volume_dim = dim;\n    struct largest_characteristic_speed : db::SimpleTag {\n      using type = double;\n    };\n    struct compute_largest_characteristic_speed : db::ComputeTag,\n                                                  largest_characteristic_speed {\n      using base = largest_characteristic_speed;\n      using argument_tags = tmpl::list<CharacteristicSpeed>;\n      using return_type = double;\n      static void function(const gsl::not_null<double*> return_speed,\n                           const double& speed) {\n        *return_speed = speed;\n      }\n    };\n  };\n\n  using Cfl = StepChoosers::Cfl<frame, system>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>, tmpl::list<Cfl>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>, tmpl::list<Cfl>>>;\n  };\n};\n\ntemplate <typename Use>\ndouble get_suggestion(const size_t stepper_order, const double safety_factor,\n                      const double characteristic_speed,\n                      const DataVector& coordinates) {\n  using Cfl = Metavariables::Cfl;\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Parallel::Tags::MetavariablesImpl<Metavariables>, CharacteristicSpeed,\n          domain::Tags::Coordinates<dim, frame>, domain::Tags::Mesh<dim>,\n          Tags::ConcreteTimeStepper<TimeStepper>>,\n      tmpl::push_back<time_stepper_ref_tags<TimeStepper>,\n                      domain::Tags::MinimumGridSpacingCompute<dim, frame>,\n                      typename Metavariables::system::\n                          compute_largest_characteristic_speed>>(\n      Metavariables{}, characteristic_speed,\n      tnsr::I<DataVector, dim, frame>{{{coordinates}}},\n      Mesh<dim>(coordinates.size(), Spectral::Basis::Legendre,\n                Spectral::Quadrature::GaussLobatto),\n      std::unique_ptr<TimeStepper>{\n          std::make_unique<TimeSteppers::AdamsBashforth>(stepper_order)});\n\n  const double grid_spacing =\n      get<domain::Tags::MinimumGridSpacing<dim, frame>>(box);\n  const double speed =\n      get<typename Metavariables::system::largest_characteristic_speed>(box);\n  const auto& time_stepper = get<Tags::TimeStepper<TimeStepper>>(box);\n\n  const Cfl cfl{safety_factor};\n  const std::unique_ptr<StepChooser<Use>> cfl_base = std::make_unique<Cfl>(cfl);\n\n  const double current_step = std::numeric_limits<double>::infinity();\n  const auto result = cfl(grid_spacing, time_stepper, speed, current_step);\n  REQUIRE(result.size_goal.has_value());\n  CHECK(result == TimeStepRequest{.size_goal = result.size_goal});\n  CHECK(serialize_and_deserialize(cfl)(grid_spacing, time_stepper, speed,\n                                       current_step) == result);\n  CHECK(cfl_base->desired_step(current_step, box) == result);\n  CHECK(serialize_and_deserialize(cfl_base)->desired_step(current_step, box) ==\n        result);\n  return *result.size_goal;\n}\n\ntemplate <typename Use>\nvoid test_use() {\n  CHECK(get_suggestion<Use>(1, 1., 1., {0., 2., 3., 5.}) == approx(1.));\n  CHECK(get_suggestion<Use>(2, 1., 1., {0., 2., 3., 5.}) < 1.);\n  CHECK(get_suggestion<Use>(1, 2., 1., {0., 2., 3., 5.}) == approx(2.));\n  CHECK(get_suggestion<Use>(1, 1., 2., {0., 2., 3., 5.}) == approx(0.5));\n  CHECK(get_suggestion<Use>(1, 1., 1., {0., 2., 2.5, 5.}) == approx(0.5));\n\n  TestHelpers::test_creation<std::unique_ptr<StepChooser<Use>>, Metavariables>(\n      \"Cfl:\\n\"\n      \"  SafetyFactor: 5.0\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.Cfl\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  test_use<StepChooserUse::LtsStep>();\n  test_use<StepChooserUse::Slab>();\n\n  CHECK(StepChoosers::Cfl<frame, Metavariables::system>{}.uses_local_data());\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_Constant.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/StepChoosers/Constant.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<StepChoosers::Constant>>,\n                  tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                             tmpl::list<StepChoosers::Constant>>>;\n  };\n  using component_list = tmpl::list<>;\n};\n\ntemplate <typename Use>\nvoid test_use() {\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>>>(\n      Metavariables{});\n\n  // Sign of argument should be ignored.\n  const StepChoosers::Constant constant{-5.4};\n  const std::unique_ptr<StepChooser<Use>> constant_base =\n      std::make_unique<StepChoosers::Constant>(constant);\n\n  const double current_step = std::numeric_limits<double>::infinity();\n  const TimeStepRequest expected{.size_goal = 5.4};\n  CHECK(constant(current_step) == expected);\n  CHECK(serialize_and_deserialize(constant)(current_step) == expected);\n  CHECK(constant_base->desired_step(current_step, box) == expected);\n  CHECK(serialize_and_deserialize(constant_base)\n            ->desired_step(current_step, box) == expected);\n\n  TestHelpers::test_creation<std::unique_ptr<StepChooser<Use>>, Metavariables>(\n      \"Constant: -5.4\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.Constant\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  test_use<StepChooserUse::LtsStep>();\n  test_use<StepChooserUse::Slab>();\n\n  CHECK(not StepChoosers::Constant{}.uses_local_data());\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_ElementSizeCfl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.tpp\"\n#include \"Domain/CoordinateMaps/Identity.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.tpp\"\n#include \"Domain/Creators/Tags/FunctionsOfTime.hpp\"\n#include \"Domain/SizeOfElement.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"NumericalAlgorithms/Spectral/Basis.hpp\"\n#include \"NumericalAlgorithms/Spectral/Mesh.hpp\"\n#include \"NumericalAlgorithms/Spectral/Quadrature.hpp\"\n#include \"Time/StepChoosers/ElementSizeCfl.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct CharacteristicSpeed : db::SimpleTag {\n  using type = double;\n};\n\ntemplate <size_t Dim>\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct system {\n    struct largest_characteristic_speed : db::SimpleTag {\n      using type = double;\n    };\n    struct compute_largest_characteristic_speed : db::ComputeTag,\n                                                  largest_characteristic_speed {\n      using base = largest_characteristic_speed;\n      using argument_tags = tmpl::list<CharacteristicSpeed>;\n      using return_type = double;\n      static void function(const gsl::not_null<double*> return_speed,\n                           const double& speed) {\n        *return_speed = speed;\n      }\n    };\n  };\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   tmpl::list<StepChoosers::ElementSizeCfl<Dim, system>>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::list<StepChoosers::ElementSizeCfl<Dim, system>>>>;\n  };\n};\n\ntemplate <size_t Dim>\ndouble get_suggestion(const double safety_factor,\n                      const double characteristic_speed,\n                      ElementMap<Dim, Frame::Grid>&& element_map) {\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables<Dim>>,\n                        CharacteristicSpeed,\n                        Tags::ConcreteTimeStepper<TimeStepper>,\n                        domain::Tags::ElementMap<Dim, Frame::Grid>,\n                        domain::CoordinateMaps::Tags::CoordinateMap<\n                            Dim, Frame::Grid, Frame::Inertial>,\n                        ::Tags::Time, domain::Tags::FunctionsOfTimeInitialize>,\n      tmpl::push_back<time_stepper_ref_tags<TimeStepper>,\n                      domain::Tags::SizeOfElementCompute<Dim>,\n                      typename Metavariables<Dim>::system::\n                          compute_largest_characteristic_speed>>(\n      Metavariables<Dim>{}, characteristic_speed,\n      std::unique_ptr<TimeStepper>{\n          std::make_unique<TimeSteppers::AdamsBashforth>(2)},\n      std::move(element_map),\n      ::domain::make_coordinate_map_base<Frame::Grid, Frame::Inertial>(\n          ::domain::CoordinateMaps::Identity<Dim>{}),\n      0.0, typename domain::Tags::FunctionsOfTimeInitialize::type{});\n  const StepChoosers::ElementSizeCfl<Dim, typename Metavariables<Dim>::system>\n      element_size_cfl{safety_factor};\n  const std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>\n      element_size_base = std::make_unique<StepChoosers::ElementSizeCfl<\n          Dim, typename Metavariables<Dim>::system>>(element_size_cfl);\n\n  const double speed =\n      get<typename Metavariables<Dim>::system::largest_characteristic_speed>(\n          box);\n  const std::array<double, Dim> element_size =\n      db::get<domain::Tags::SizeOfElement<Dim>>(box);\n  const auto& time_stepper = get<Tags::TimeStepper<TimeStepper>>(box);\n\n  const double current_step = std::numeric_limits<double>::infinity();\n  const auto result =\n      element_size_cfl(time_stepper, element_size, speed, current_step);\n  REQUIRE(result.size_goal.has_value());\n  CHECK(result == TimeStepRequest{.size_goal = result.size_goal});\n  CHECK(element_size_base->desired_step(current_step, box) == result);\n  CHECK(serialize_and_deserialize(element_size_cfl)(\n            time_stepper, element_size, speed, current_step) == result);\n  CHECK(serialize_and_deserialize(element_size_base)\n            ->desired_step(current_step, box) == result);\n  return *result.size_goal;\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.ElementSizeCfl\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables<1>>();\n  register_factory_classes_with_charm<Metavariables<2>>();\n  register_factory_classes_with_charm<Metavariables<3>>();\n\n  {\n    INFO(\"Test 1D element size CFL step chooser\");\n    auto map =\n        domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n            domain::CoordinateMaps::Affine(-1.0, 1.0, 0.3, 1.1));\n    const ElementId<1> element_id(0, {{{2, 3}}});\n    ElementMap<1, Frame::Grid> logical_to_grid_map(element_id, std::move(map));\n    // NOLINTNEXTLINE(bugprone-use-after-move) - confused by CHECK\n    CHECK(approx(get_suggestion(0.8, 2.0, std::move(logical_to_grid_map))) ==\n          0.04);\n  }\n  {\n    INFO(\"Test 2D element size CFL step chooser\");\n    using Affine = domain::CoordinateMaps::Affine;\n    using Affine2D = domain::CoordinateMaps::ProductOf2Maps<Affine, Affine>;\n    auto map =\n        domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n            Affine2D{Affine(-1.0, 1.0, 0.3, 0.4),\n                     Affine(-1.0, 1.0, -0.5, 1.1)});\n    const ElementId<2> element_id(0, {{{1, 0}, {2, 3}}});\n    ElementMap<2, Frame::Grid> logical_to_grid_map(element_id, std::move(map));\n    // NOLINTNEXTLINE(bugprone-use-after-move) - confused by CHECK\n    CHECK(approx(get_suggestion(0.8, 2.0, std::move(logical_to_grid_map))) ==\n          0.005);\n  }\n  {\n    INFO(\"Test 3D element size CFL step chooser\");\n    using Affine = domain::CoordinateMaps::Affine;\n    using Affine3D =\n        domain::CoordinateMaps::ProductOf3Maps<Affine, Affine, Affine>;\n    auto map =\n        domain::make_coordinate_map_base<Frame::BlockLogical, Frame::Grid>(\n            Affine3D{Affine(-1.0, 1.0, 0.3, 0.4), Affine(-1.0, 1.0, -0.5, 1.1),\n                     Affine(-1.0, 1.0, 12.0, 12.4)});\n    const ElementId<3> element_id(0, {{{2, 3}, {1, 0}, {3, 4}}});\n    ElementMap<3, Frame::Grid> logical_to_grid_map(element_id, std::move(map));\n    // NOLINTNEXTLINE(bugprone-use-after-move) - confused by CHECK\n    CHECK(approx(get_suggestion(0.8, 2.0, std::move(logical_to_grid_map))) ==\n          0.005 / 3.0);\n  }\n\n  TestHelpers::test_creation<\n      std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>, Metavariables<1>>(\n      \"ElementSizeCfl:\\n\"\n      \"  SafetyFactor: 5.0\");\n  TestHelpers::test_creation<\n      std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>, Metavariables<2>>(\n      \"ElementSizeCfl:\\n\"\n      \"  SafetyFactor: 5.0\");\n  TestHelpers::test_creation<\n      std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>, Metavariables<3>>(\n      \"ElementSizeCfl:\\n\"\n      \"  SafetyFactor: 5.0\");\n\n  CHECK(StepChoosers::ElementSizeCfl<1, Metavariables<1>::system>{}\n            .uses_local_data());\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_ErrorControl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n#include <type_traits>\n#include <typeinfo>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/ErrorControl.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass DataVector;\n\nnamespace {\nstruct EvolvedVar1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct EvolvedVar2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, 2>;\n};\n\nusing EvolvedVariablesTag =\n    Tags::Variables<tmpl::list<EvolvedVar1, EvolvedVar2>>;\n\nstruct ErrorControlSelecter {\n  static std::string name() { return \"SelectorLabel\"; }\n};\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   tmpl::list<StepChoosers::ErrorControl<\n                       StepChooserUse::LtsStep, EvolvedVariablesTag>>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::list<StepChoosers::ErrorControl<\n                       StepChooserUse::Slab, EvolvedVariablesTag>>>>;\n  };\n};\n\ntemplate <typename StepChooserUse>\nstd::optional<double> get_suggestion(\n    const StepChoosers::ErrorControl<StepChooserUse, EvolvedVariablesTag>&\n        error_control,\n    const std::optional<StepperErrorEstimate>& error,\n    const std::optional<StepperErrorEstimate>& previous_error,\n    const double previous_step) {\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::StepperErrors<EvolvedVariablesTag>>>(\n      Metavariables{}, std::array{previous_error, error});\n\n  const std::unique_ptr<StepChooser<StepChooserUse>> error_control_base =\n      std::make_unique<\n          StepChoosers::ErrorControl<StepChooserUse, EvolvedVariablesTag>>(\n          error_control);\n\n  const auto result =\n      error_control(std::array{previous_error, error}, previous_step);\n  CHECK(result == TimeStepRequest{.size_goal = result.size_goal});\n  CHECK(error_control_base->desired_step(previous_step, box) == result);\n  CHECK(serialize_and_deserialize(error_control)(\n            std::array{previous_error, error}, previous_step) == result);\n  CHECK(serialize_and_deserialize(error_control_base)\n            ->desired_step(previous_step, box) == result);\n  return result.size_goal;\n}\n\ntemplate <typename StepChooserUse>\nvoid test_chooser() {\n  using ErrorControl =\n      StepChoosers::ErrorControl<StepChooserUse, EvolvedVariablesTag>;\n\n  const std::vector<size_t> stepper_orders{2_st, 5_st};\n  for (const bool time_runs_forward : {true, false}) {\n    const double unit_step = time_runs_forward ? 1.0 : -1.0;\n    for (size_t stepper_order : stepper_orders) {\n      CAPTURE(stepper_order);\n      const auto step_errors = [&](const double error_time, const double error,\n                                   const double step_size = 1.0) {\n        const auto error_slab = time_runs_forward\n                                    ? Slab(error_time, error_time + step_size)\n                                    : Slab(error_time - step_size, error_time);\n        return StepperErrorEstimate{\n            time_runs_forward ? error_slab.start() : error_slab.end(),\n            (time_runs_forward ? 1 : -1) * error_slab.duration(),\n            stepper_order - 1, error};\n      };\n\n      {\n        INFO(\"No data available\");\n        const ErrorControl error_control{5.0e-4, 0.0, 2.0, 0.5, 0.95};\n        const auto result = get_suggestion(error_control, {}, {}, unit_step);\n        CHECK(not result.has_value());\n      }\n      {\n        INFO(\"Test successful step\");\n        const ErrorControl error_control{5.0e-4, 1.0e-3, 2.0, 0.5, 0.95};\n        CHECK(error_control.tolerances().at(typeid(EvolvedVariablesTag)) ==\n              StepperErrorTolerances{\n                  .estimates = StepperErrorTolerances::Estimates::StepperOrder,\n                  .absolute = 5.0e-4,\n                  .relative = 1.0e-3});\n        const auto first_result = get_suggestion(\n            error_control, {step_errors(0.0, 0.3)}, {}, unit_step);\n        REQUIRE(first_result.has_value());\n        CHECK(approx(*first_result) ==\n              0.95 * unit_step / pow(0.3, 1.0 / stepper_order));\n        if constexpr (std::is_same_v<StepChooserUse, ::StepChooserUse::Slab>) {\n          const auto second_result = get_suggestion(\n              error_control, {step_errors(0.0, 0.31, abs(*first_result))},\n              {step_errors(-1.0, 0.3)}, *first_result);\n          REQUIRE(second_result.has_value());\n          CHECK(approx(*second_result) == 0.95 * *first_result /\n                                              (pow(0.3, -0.4 / stepper_order) *\n                                               pow(0.31, 0.7 / stepper_order)));\n          // Check that the suggested step size is smaller if the error in\n          // increasing faster.\n          const auto adjusted_second_result = get_suggestion(\n              error_control, {step_errors(0.0, 0.31, abs(*first_result))},\n              {step_errors(-1.0, 0.1)}, *first_result);\n          REQUIRE(adjusted_second_result.has_value());\n          CHECK(abs(*adjusted_second_result) < abs(*second_result));\n        } else {\n          // Check that the result is independent of the old error\n          const auto second_result =\n              get_suggestion(error_control, {step_errors(0.0, 0.3)},\n                             {step_errors(-1.0, 0.7)}, unit_step);\n          CHECK(first_result == second_result);\n        }\n      }\n      {\n        INFO(\"Test error control step failure\");\n        const ErrorControl error_control{4.0e-5, 4.0e-5, 2.0, 0.5, 0.95};\n        const auto result_start = get_suggestion(\n            error_control, {step_errors(0.0, 1.2)}, {}, unit_step);\n        REQUIRE(result_start.has_value());\n        const auto result_end = get_suggestion(\n            error_control, {step_errors(-1.0, 1.2)}, {}, unit_step);\n        REQUIRE(result_end.has_value());\n        const auto result_end2 = get_suggestion(\n            error_control, {step_errors(-1.0, 1.2)}, {}, *result_end);\n        REQUIRE(result_end2.has_value());\n        CHECK(approx(*result_start) ==\n              0.95 * unit_step / pow(1.2, 1.0 / stepper_order));\n        CHECK(result_end == result_start);\n        CHECK(result_end2 == result_start);\n      }\n      {\n        INFO(\"Test error control clamped minimum\");\n        const ErrorControl error_control{4.0e-5, 4.0e-5, 2.0, 0.9, 0.95};\n        const auto first_result = get_suggestion(\n            error_control, {step_errors(0.0, 10.0)}, {}, unit_step);\n        CHECK(first_result == std::optional(0.9 * unit_step));\n      }\n      {\n        INFO(\"Test error control clamped maximum\");\n        const ErrorControl error_control{1.0e-1, 1.0e-1, 2.0, 0.5, 0.95};\n        const auto first_result = get_suggestion(\n            error_control, {step_errors(0.0, 0.01)}, {}, unit_step);\n        CHECK(first_result == std::optional(2.0 * unit_step));\n      }\n    }\n  }\n  // test option creation\n  TestHelpers::test_factory_creation<\n      StepChooser<StepChooserUse>,\n      StepChoosers::ErrorControl<StepChooserUse, EvolvedVariablesTag>>(\n      \"ErrorControl:\\n\"\n      \"  SafetyFactor: 0.95\\n\"\n      \"  AbsoluteTolerance: 1.0e-5\\n\"\n      \"  RelativeTolerance: 1.0e-4\\n\"\n      \"  MaxFactor: 2.1\\n\"\n      \"  MinFactor: 0.5\");\n  TestHelpers::test_factory_creation<\n      StepChooser<StepChooserUse>,\n      StepChoosers::ErrorControl<StepChooserUse, EvolvedVariablesTag,\n                                 ErrorControlSelecter>>(\n      \"ErrorControl(SelectorLabel):\\n\"\n      \"  SafetyFactor: 0.95\\n\"\n      \"  AbsoluteTolerance: 1.0e-5\\n\"\n      \"  RelativeTolerance: 1.0e-4\\n\"\n      \"  MaxFactor: 2.1\\n\"\n      \"  MinFactor: 0.5\");\n\n  CHECK(StepChoosers::ErrorControl<StepChooserUse, EvolvedVariablesTag,\n                                   ErrorControlSelecter>{}\n            .uses_local_data());\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.ErrorControl\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  test_chooser<StepChooserUse::Slab>();\n  test_chooser<StepChooserUse::LtsStep>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_FixedLtsRatio.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/Constant.hpp\"\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n#include \"Time/StepChoosers/LimitIncrease.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/FixedLtsRatio.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nclass ErrorChooser : public StepChooser<StepChooserUse::LtsStep> {\n public:\n  ErrorChooser() = default;\n  explicit ErrorChooser(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(ErrorChooser);  // NOLINT\n#pragma GCC diagnostic pop\n\n  static constexpr Options::String help{\"\"};\n  using options = tmpl::list<>;\n\n  using argument_tags = tmpl::list<>;\n\n  TimeStepRequest operator()(double /*last_step*/) const {\n    ERROR(\"StepChooser should not be called in fixed LTS region\");\n  }\n\n  bool uses_local_data() const override { return false; }\n  bool can_be_delayed() const override { return true; }\n};\n\nPUP::able::PUP_ID ErrorChooser::my_PUP_ID = 0;  // NOLINT\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   tmpl::list<StepChoosers::Constant,\n                              StepChoosers::LimitIncrease, ErrorChooser>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::list<StepChoosers::FixedLtsRatio>>>;\n  };\n};\n\nvoid test(const std::optional<double>& expected_goal,\n          const std::optional<double>& expected_size,\n          const std::optional<size_t>& fixed_ratio,\n          const std::string& lts_choosers) {\n  CAPTURE(lts_choosers);\n  const Slab slab(2.0, 6.0);\n  const auto time_step = slab.duration() / 2;\n\n  for (const auto& time_sign : {1, -1}) {\n    CAPTURE(time_sign);\n    auto box = db::create<\n        db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                          Tags::TimeStep, Tags::FixedLtsRatio>>(\n        Metavariables{}, time_sign * time_step, fixed_ratio);\n\n    const auto chooser = TestHelpers::test_creation<\n        std::unique_ptr<StepChooser<StepChooserUse::Slab>>, Metavariables>(\n        \"FixedLtsRatio:\\n\"\n        \"  StepChoosers:\\n\" +\n        lts_choosers);\n\n    const auto set_sign = [&](const std::optional<double>& opt) {\n      if (opt.has_value()) {\n        return std::optional<double>(time_sign * *opt);\n      } else {\n        return std::optional<double>{};\n      }\n    };\n\n    const double current_step = time_sign * slab.duration().value();\n    const TimeStepRequest expected{.size_goal = set_sign(expected_goal),\n                                   .size = set_sign(expected_size)};\n    CHECK(chooser->desired_step(current_step, box) == expected);\n    CHECK(serialize_and_deserialize(chooser)->desired_step(current_step, box) ==\n          expected);\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.FixedLtsRatio\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  test({}, {}, {}, \"    - ErrorChooser\");\n  test({}, {}, {8}, \"\");\n  test({40.0}, {}, {8},\n       \"    - Constant: 5.0\\n\"\n       \"    - Constant: 7.0\");\n  // Initial step size used in test is 2.0\n  test({}, {64.0}, {8},\n       \"    - LimitIncrease:\\n\"\n       \"        Factor: 4.0\\n\"\n       \"    - LimitIncrease:\\n\"\n       \"        Factor: 9.0\");\n  test({40.0}, {32.0}, {8},\n       \"    - Constant: 5.0\\n\"\n       \"    - LimitIncrease:\\n\"\n       \"        Factor: 2.0\");\n  // Should never give a limit larger than the goal.\n  test({40.0}, {}, {8},\n       \"    - Constant: 5.0\\n\"\n       \"    - LimitIncrease:\\n\"\n       \"        Factor: 4.0\");\n\n  CHECK(StepChoosers::FixedLtsRatio{}.uses_local_data());\n  CHECK(StepChoosers::FixedLtsRatio{}.can_be_delayed());\n\n  StepChoosers::FixedLtsRatio{}.for_each_step_chooser(\n      [](const auto& /*unused*/) { ERROR(\"Should not be called\"); });\n  {\n    const StepChoosers::FixedLtsRatio two_choosers{\n        make_vector<std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>>(\n            std::make_unique<StepChoosers::Constant>(1.0),\n            std::make_unique<StepChoosers::Constant>(2.0))};\n    int count = 0;\n    two_choosers.for_each_step_chooser(\n        [&](const auto& /*unused*/) { ++count; });\n    CHECK(count == 2);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_LimitIncrease.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <initializer_list>\n#include <limits>\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/StepChoosers/LimitIncrease.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<StepChoosers::LimitIncrease>>,\n                  tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                             tmpl::list<StepChoosers::LimitIncrease>>>;\n  };\n  using component_list = tmpl::list<>;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.LimitIncrease\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>>>(\n      Metavariables{});\n  const auto check = [&box](auto use, const double step,\n                            const double expected_size) {\n    using Use = tmpl::type_from<decltype(use)>;\n    const StepChoosers::LimitIncrease increase{5.};\n    const std::unique_ptr<StepChooser<Use>> increase_base =\n        std::make_unique<StepChoosers::LimitIncrease>(increase);\n\n    const TimeStepRequest expected{.size = expected_size};\n    CHECK(increase(step) == expected);\n    CHECK(serialize_and_deserialize(increase)(step) == expected);\n    CHECK(increase_base->desired_step(step, box) == expected);\n    CHECK(serialize_and_deserialize(increase_base)->desired_step(step, box) ==\n          expected);\n  };\n  check(tmpl::type_<StepChooserUse::LtsStep>{}, 0.25, 1.25);\n  check(tmpl::type_<StepChooserUse::Slab>{}, 0.25, 1.25);\n  check(tmpl::type_<StepChooserUse::LtsStep>{},\n        std::numeric_limits<double>::infinity(),\n        std::numeric_limits<double>::infinity());\n  check(tmpl::type_<StepChooserUse::Slab>{},\n        std::numeric_limits<double>::infinity(),\n        std::numeric_limits<double>::infinity());\n\n  TestHelpers::test_creation<\n      std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>, Metavariables>(\n      \"LimitIncrease:\\n\"\n      \"  Factor: 5.0\");\n  TestHelpers::test_creation<std::unique_ptr<StepChooser<StepChooserUse::Slab>>,\n                             Metavariables>(\n      \"LimitIncrease:\\n\"\n      \"  Factor: 5.0\");\n\n  CHECK(not StepChoosers::LimitIncrease{}.uses_local_data());\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_Maximum.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <type_traits>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/StepChoosers/Maximum.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<StepChoosers::Maximum>>,\n                  tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                             tmpl::list<StepChoosers::Maximum>>>;\n  };\n  using component_list = tmpl::list<>;\n};\n\ntemplate <typename Use>\nvoid test_use() {\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>>>(\n      Metavariables{});\n\n  // Sign of argument should be ignored.\n  const StepChoosers::Maximum maximum{-5.4};\n  const std::unique_ptr<StepChooser<Use>> maximum_base =\n      std::make_unique<StepChoosers::Maximum>(maximum);\n\n  const double current_step = std::numeric_limits<double>::infinity();\n  const TimeStepRequest expected{.size = 5.4};\n  CHECK(maximum(current_step) == expected);\n  CHECK(serialize_and_deserialize(maximum)(current_step) == expected);\n  CHECK(maximum_base->desired_step(current_step, box) == expected);\n  CHECK(serialize_and_deserialize(maximum_base)\n            ->desired_step(current_step, box) == expected);\n\n  TestHelpers::test_creation<std::unique_ptr<StepChooser<Use>>, Metavariables>(\n      \"Maximum: 5.4\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.Maximum\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  test_use<StepChooserUse::LtsStep>();\n  test_use<StepChooserUse::Slab>();\n\n  CHECK(not StepChoosers::Maximum{}.uses_local_data());\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_PreventRapidIncrease.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <cstdint>\n#include <initializer_list>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/PreventRapidIncrease.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Frac = Time::rational_t;\n\nstruct Tag : db::SimpleTag {\n  using type = double;\n};\nusing history_tag = Tags::HistoryEvolvedVariables<Tag>;\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   tmpl::list<StepChoosers::PreventRapidIncrease<Tag>>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::list<StepChoosers::PreventRapidIncrease<Tag>>>>;\n  };\n  using component_list = tmpl::list<>;\n};\n\nvoid check_case(const Frac& expected_frac, const std::vector<Frac>& times) {\n  CAPTURE(times);\n  CAPTURE(expected_frac);\n\n  const Slab slab(0.25, 1.5);\n  for (const auto& direction : {1, -1}) {\n    CAPTURE(direction);\n\n    const std::optional<double> expected_size =\n        expected_frac == -1\n            ? std::nullopt\n            : std::optional(\n                  (direction * expected_frac * slab.duration()).value());\n\n    const auto make_time_id = [&direction, &slab, &times](const size_t i) {\n      Frac frac = -direction * times[i];\n      int64_t slab_number = 0;\n      Slab time_slab = slab;\n      while (frac > 1) {\n        time_slab = time_slab.advance();\n        frac -= 1;\n        slab_number += direction;\n      }\n      while (frac < 0) {\n        time_slab = time_slab.retreat();\n        frac += 1;\n        slab_number -= direction;\n      }\n      return TimeStepId(direction > 0, slab_number, Time(time_slab, frac));\n    };\n\n    typename history_tag::type lts_history{};\n    typename history_tag::type gts_history{};\n\n    const auto make_gts_time_id = [&direction, &make_time_id](const size_t i) {\n      const double time = make_time_id(i).substep_time();\n      const double next_time =\n          i > 0 ? make_time_id(i - 1).substep_time() : time + direction;\n      const Slab gts_slab(std::min(time, next_time), std::max(time, next_time));\n      return TimeStepId(direction > 0, -static_cast<int64_t>(i),\n                        direction > 0 ? gts_slab.start() : gts_slab.end());\n    };\n\n    for (size_t i = 1; i < times.size(); ++i) {\n      lts_history.insert_initial(make_time_id(i), 0.0, 0.0);\n      gts_history.insert_initial(make_gts_time_id(i), 0.0, 0.0);\n    }\n\n    const auto check = [&direction, &expected_size](auto use, const auto& box,\n                                                    const Time& current_time) {\n      using Use = tmpl::type_from<decltype(use)>;\n      const auto& history = db::get<history_tag>(box);\n      const double current_step =\n          history.size() > 0\n              ? (current_time - history.back().time_step_id.step_time()).value()\n              : direction * std::numeric_limits<double>::infinity();\n\n      const StepChoosers::PreventRapidIncrease<Tag> relax{};\n      const std::unique_ptr<StepChooser<Use>> relax_base =\n          std::make_unique<StepChoosers::PreventRapidIncrease<Tag>>(relax);\n\n      const TimeStepRequest expected{.size = expected_size};\n      CHECK(relax(history, current_step) == expected);\n      CHECK(serialize_and_deserialize(relax)(history, current_step) ==\n            expected);\n      CHECK(relax_base->desired_step(current_step, box) == expected);\n      CHECK(serialize_and_deserialize(relax_base)\n                ->desired_step(current_step, box) == expected);\n    };\n\n    {\n      CAPTURE(lts_history);\n      const auto box = db::create<db::AddSimpleTags<\n          Parallel::Tags::MetavariablesImpl<Metavariables>, history_tag>>(\n          Metavariables{}, std::move(lts_history));\n      check(tmpl::type_<StepChooserUse::LtsStep>{}, box,\n            make_time_id(0).step_time());\n      check(tmpl::type_<StepChooserUse::Slab>{}, box,\n            make_time_id(0).step_time());\n    }\n\n    {\n      CAPTURE(gts_history);\n      const auto box = db::create<db::AddSimpleTags<\n          Parallel::Tags::MetavariablesImpl<Metavariables>, history_tag>>(\n          Metavariables{}, std::move(gts_history));\n      check(tmpl::type_<StepChooserUse::LtsStep>{}, box,\n            make_gts_time_id(0).step_time());\n      check(tmpl::type_<StepChooserUse::Slab>{}, box,\n            make_gts_time_id(0).step_time());\n    }\n  }\n}\n\nvoid check_substep_methods() {\n  const Slab slab(0.25, 1.5);\n\n  typename history_tag::type history{};\n\n  history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n  history.insert(TimeStepId(true, 0, slab.start(), 1, slab.duration(),\n                            (slab.start() + slab.duration() / 3).value()),\n                 0.0, 0.0);\n  history.insert(TimeStepId(true, 0, slab.start(), 2, slab.duration(),\n                            (slab.start() + slab.duration() / 2).value()),\n                 0.0, 0.0);\n  const StepChoosers::PreventRapidIncrease<Tag> relax{};\n  CHECK(relax(history, 3.14) == TimeStepRequest{});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.PreventRapidIncrease\",\n                  \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  // -1 indicates no expected restriction\n  check_case(-1, {0});\n  check_case(-1, {{5, 8}});\n  check_case(-1, {0, {5, 8}});\n  check_case(-1, {{4, 8}, {5, 8}, {6, 8}});\n  check_case(-1, {{7, 8}, {8, 8}, {9, 8}});\n  check_case(-1, {{7, 8}, {8, 8}, {10, 8}});\n  check_case({1, 8}, {{7, 8}, {8, 8}, {17, 16}});\n  check_case({2, 8}, {{6, 8}, {8, 8}, {9, 8}});\n  check_case(-1, {{4, 8}, {5, 8}, {6, 8}, {7, 8}});\n  check_case({2, 8}, {{3, 8}, {5, 8}, {6, 8}, {7, 8}});\n  check_case({1, 8}, {{4, 8}, {5, 8}, {7, 8}, {8, 8}});\n\n  // Cause roundoff errors\n  check_case(-1, {{1, 3}, {2, 3}, {3, 3}});\n\n  check_substep_methods();\n\n  TestHelpers::test_creation<\n      std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>, Metavariables>(\n      \"PreventRapidIncrease\");\n  TestHelpers::test_creation<std::unique_ptr<StepChooser<StepChooserUse::Slab>>,\n                             Metavariables>(\"PreventRapidIncrease\");\n\n  CHECK(not StepChoosers::PreventRapidIncrease<Tag>{}.uses_local_data());\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_Random.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <iomanip>\n#include <memory>\n#include <random>\n#include <unordered_set>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Domain/Structure/Element.hpp\"\n#include \"Domain/Structure/ElementId.hpp\"\n#include \"Domain/Tags.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/Random.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nconstexpr size_t volume_dim = 2;\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<StepChoosers::Random<volume_dim>>>,\n                  tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                             tmpl::list<StepChoosers::Random<volume_dim>>>>;\n  };\n  using component_list = tmpl::list<>;\n};\n\ntemplate <typename Use>\ndouble get_suggestion(const double min, const double max, const size_t seed,\n                      const Element<volume_dim>& element,\n                      const TimeStepId& time_step_id) {\n  using Random = StepChoosers::Random<volume_dim>;\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::TimeStepId, domain::Tags::Element<volume_dim>>>(\n      Metavariables{}, time_step_id, element);\n\n  // Not used.\n  const double current_step = 1.23;\n\n  const Random random(min, max, seed);\n  const std::unique_ptr<StepChooser<Use>> random_base =\n      TestHelpers::test_factory_creation<StepChooser<Use>, Random>(\n          MakeString{} << std::setprecision(19) << \"Random:\\n\"\n                       << \"  Minimum: \" << min << \"\\n\"\n                       << \"  Maximum: \" << max << \"\\n\"\n                       << \"  Seed: \" << seed);\n\n  CHECK(random.uses_local_data());\n\n  const auto result = random(element, time_step_id, current_step);\n  REQUIRE(result.size_goal.has_value());\n  CHECK(result == TimeStepRequest{.size_goal = result.size_goal});\n  // Should be deterministic\n  CHECK(result == random(element, time_step_id, current_step));\n\n  CHECK(*result.size_goal >= min);\n  CHECK(*result.size_goal <= max);\n\n  CHECK(serialize_and_deserialize(random)(element, time_step_id,\n                                          current_step) == result);\n  CHECK(random_base->desired_step(current_step, box) == result);\n  CHECK(\n      serialize_and_deserialize(random_base)->desired_step(current_step, box) ==\n      result);\n\n  return *result.size_goal;\n}\n\ntemplate <typename Use>\nvoid test_random() {\n  const Element<volume_dim> element1(ElementId<volume_dim>(1), {});\n  const Element<volume_dim> element2(ElementId<volume_dim>(2), {});\n  const TimeStepId time1(true, 0, Slab(0.0, 1.0).start());\n  const TimeStepId time2(true, 0, Slab(0.0, 1.0).end());\n\n  MAKE_GENERATOR(gen);\n  const double min = std::uniform_real_distribution(1.0e-6, 1.0)(gen);\n  const double max = min * std::uniform_real_distribution(1.1, 10.0)(gen);\n  const size_t seed = gen();\n\n  std::unordered_set<double> results{};\n  for (const auto& element : {element1, element2}) {\n    for (const auto& time : {time1, time2}) {\n      CHECK(results.insert(get_suggestion<Use>(min, max, seed, element, time))\n                .second);\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.Random\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  test_random<StepChooserUse::LtsStep>();\n  test_random<StepChooserUse::Slab>();\n}\n"
  },
  {
    "path": "tests/Unit/Time/StepChoosers/Test_StepToTimes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/StepChoosers/StepToTimes.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                             tmpl::list<StepChoosers::StepToTimes>>,\n                  tmpl::pair<TimeSequence<double>,\n                             TimeSequences::all_time_sequences<double>>>;\n  };\n  using component_list = tmpl::list<>;\n};\n\nvoid check_case(const double now, std::vector<double> times,\n                const std::optional<double>& expected_end) {\n  const auto impl = [](const double local_now, const double last_step,\n                       const std::vector<double>& impl_times) {\n    CAPTURE(local_now);\n    CAPTURE(impl_times);\n\n    using Specified = TimeSequences::Specified<double>;\n    const StepChoosers::StepToTimes step_to_times(\n        std::make_unique<Specified>(impl_times));\n    const std::unique_ptr<StepChooser<StepChooserUse::Slab>>\n        step_to_times_base = std::make_unique<StepChoosers::StepToTimes>(\n            std::make_unique<Specified>(impl_times));\n\n    auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::Time>>(\n        Metavariables{}, local_now);\n\n    const auto request = step_to_times(local_now, last_step);\n\n    CHECK(request.end == request.end_hard_limit);\n    REQUIRE(request.size.has_value() == request.end.has_value());\n    CHECK(not request.size_goal.has_value());\n    CHECK(not request.size_hard_limit.has_value());\n\n    CHECK(step_to_times_base->desired_step(last_step, box) == request);\n    CHECK(serialize_and_deserialize(step_to_times)(local_now, last_step) ==\n          request);\n    CHECK(serialize_and_deserialize(step_to_times_base)\n              ->desired_step(last_step, box) == request);\n    return request.end.has_value()\n               ? std::optional(std::pair(*request.end, *request.size))\n               : std::nullopt;\n  };\n  const auto result = impl(now, 1.0, times);\n  alg::for_each(times, [](double& x) { return x = -x; });\n  const auto backwards_result = impl(-now, -1.0, times);\n  CHECK(backwards_result.has_value() == result.has_value());\n  if (result.has_value()) {\n    CHECK(backwards_result ==\n          std::optional(std::pair(-result->first, -result->second)));\n  }\n\n  CHECK(result.has_value() == expected_end.has_value());\n  if (expected_end.has_value()) {\n    CHECK(result == std::optional(std::pair(\n                        *expected_end, 2.0 / 3.0 * (*expected_end - now))));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepChoosers.StepToTimes\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  check_case(3.0, {}, {});\n  check_case(3.0, {1.0}, {});\n  check_case(3.0, {3.0}, {});\n  check_case(3.0, {5.0}, {5.0});\n\n  const auto one_five_check = [](const std::vector<double>& times) {\n    check_case(0.0, times, {1.0});\n    check_case(1.0, times, {5.0});\n    check_case(2.0, times, {5.0});\n    check_case(5.0, times, {});\n    check_case(7.0, times, {});\n  };\n\n  one_five_check({1.0, 5.0});\n  one_five_check({5.0, 1.0});\n  one_five_check({1.0, 5.0, 1.0, 5.0, 1.0, 5.0});\n\n  const auto check_rounding = [](const double start, const double step) {\n    CAPTURE(start);\n    CAPTURE(step);\n    static constexpr double infinity = std::numeric_limits<double>::infinity();\n    check_case(std::nextafter(start, -infinity), {start, start + step},\n               {start});\n    check_case(start, {start, start + step}, {start + step});\n    check_case(std::nextafter(start, +infinity), {start, start + step},\n               {start + step});\n  };\n\n  check_rounding(0.0, 1.0);\n  check_rounding(1.0, 1.0);\n  check_rounding(-1.0, 1.0);\n  check_rounding(1.0e5, 1.0);\n  check_rounding(-1.0e5, 1.0);\n  check_rounding(1.0, 1.0e5);\n  check_rounding(-1.0, 1.0e5);\n  check_rounding(1.0e5, 1.0e5);\n  check_rounding(-1.0e5, 1.0e5);\n\n  TestHelpers::test_creation<std::unique_ptr<StepChooser<StepChooserUse::Slab>>,\n                             Metavariables>(\n      \"StepToTimes:\\n\"\n      \"  Times:\\n\"\n      \"    Specified:\\n\"\n      \"      Values: [5.0, 3.0, 6.0]\");\n\n  CHECK(not StepChoosers::StepToTimes{}.uses_local_data());\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Tags/Test_AdaptiveSteppingDiagnostics.cpp\n  Tags/Test_FixedLtsRatio.cpp\n  Tags/Test_HistoryEvolvedVariables.cpp\n  Tags/Test_LtsStepChoosers.cpp\n  Tags/Test_MinimumTimeStep.cpp\n  Tags/Test_StepNumberWithinSlab.cpp\n  Tags/Test_StepperErrorEstimatesEnabled.cpp\n  Tags/Test_StepperErrorTolerances.cpp\n  Tags/Test_StepperErrorTolerancesCompute.cpp\n  Tags/Test_StepperErrors.cpp\n  Tags/Test_Time.cpp\n  Tags/Test_TimeAndPrevious.cpp\n  Tags/Test_TimeStepId.cpp\n  Tags/Test_TimeStepper.cpp\n  Tags/Test_VariableOrderAlgorithm.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_AdaptiveSteppingDiagnostics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.AdaptiveSteppingDiagnostics\",\n                  \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::AdaptiveSteppingDiagnostics>(\n      \"AdaptiveSteppingDiagnostics\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_FixedLtsRatio.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/FixedLtsRatio.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.FixedLtsRatio\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::FixedLtsRatio>(\"FixedLtsRatio\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_HistoryEvolvedVariables.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing DummyVariablesTag =\n    Tags::Variables<tmpl::list<TestHelpers::Tags::Scalar<>>>;\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.HistoryEvolvedVariables\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<\n      Tags::HistoryEvolvedVariables<DummyVariablesTag>>(\n      \"HistoryEvolvedVariables\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_LtsStepChoosers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <pup_stl.h>\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/LtsStepChoosers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.LtsStepChoosers\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::LtsStepChoosers>(\"LtsStepChoosers\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_MinimumTimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/MinimumTimeStep.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.MinimumTimeStep\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::MinimumTimeStep>(\"MinimumTimeStep\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_StepNumberWithinSlab.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/StepNumberWithinSlab.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.StepNumberWithinSlab\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::StepNumberWithinSlab>(\n      \"StepNumberWithinSlab\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_StepperErrorEstimatesEnabled.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.StepperErrorEstimatesEnabled\",\n                  \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::StepperErrorEstimatesEnabled>(\n      \"StepperErrorEstimatesEnabled\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_StepperErrorTolerances.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/StepperErrorTolerances.hpp\"\n\nnamespace {\nstruct Vars {};\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.StepperErrorTolerances\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::StepperErrorTolerances<Vars>>(\n      \"StepperErrorTolerances(Vars)\");\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_StepperErrorTolerancesCompute.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Event.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/LogicalTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Time/ChangeSlabSize/Event.hpp\"\n#include \"Time/StepChoosers/Constant.hpp\"\n#include \"Time/StepChoosers/ErrorControl.hpp\"\n#include \"Time/StepChoosers/FixedLtsRatio.hpp\"\n#include \"Time/StepChoosers/LimitIncrease.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Tags/LtsStepChoosers.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/Tags/StepperErrorTolerances.hpp\"\n#include \"Time/Tags/StepperErrorTolerancesCompute.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Tags/VariableOrderAlgorithm.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/VariableOrderAlgorithm.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nclass DataVector;\nnamespace Parallel {\ntemplate <typename Metavariables>\nclass GlobalCache;\n}  // namespace Parallel\n\nnamespace {\nclass OtherEvent : public Event {\n public:\n  explicit OtherEvent(CkMigrateMessage* /*unused*/) {}\n  using PUP::able::register_constructor;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\n  WRAPPED_PUPable_decl_template(OtherEvent);  // NOLINT\n#pragma GCC diagnostic pop\n\n  using compute_tags_for_observation_box = tmpl::list<>;\n  using options = tmpl::list<>;\n  static constexpr Options::String help = {\"\"};\n\n  OtherEvent() = default;\n\n  using return_tags = tmpl::list<>;\n  using argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  void operator()(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                  const ArrayIndex& /*array_index*/,\n                  const Component* const /*meta*/,\n                  const ObservationValue& /*observation_value*/) const {}\n\n  using is_ready_argument_tags = tmpl::list<>;\n\n  template <typename Metavariables, typename ArrayIndex, typename Component>\n  bool is_ready(Parallel::GlobalCache<Metavariables>& /*cache*/,\n                const ArrayIndex& /*array_index*/,\n                const Component* const /*meta*/) const {\n    return true;\n  }\n\n  bool needs_evolved_variables() const override { return false; }\n};\n\nPUP::able::PUP_ID OtherEvent::my_PUP_ID = 0;  // NOLINT\n\nstruct EvolvedVar1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct EvolvedVar2 : db::SimpleTag {\n  using type = tnsr::i<DataVector, 2>;\n};\n\nstruct EvolvedVar3 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nusing EvolvedVariablesTag =\n    Tags::Variables<tmpl::list<EvolvedVar1, EvolvedVar2>>;\n\nusing AltEvolvedVariablesTag = Tags::Variables<tmpl::list<EvolvedVar3>>;\n\nstruct ErrorControlSelecter {\n  static std::string name() { return \"SelectorLabel\"; }\n};\n\nstruct Metavariables {\n  template <typename Use>\n  using step_choosers =\n      tmpl::list<StepChoosers::LimitIncrease, StepChoosers::Constant,\n                 StepChoosers::ErrorControl<Use, EvolvedVariablesTag>,\n                 StepChoosers::ErrorControl<Use, AltEvolvedVariablesTag,\n                                            ErrorControlSelecter>>;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes = tmpl::map<\n        tmpl::pair<Event, tmpl::list<Events::ChangeSlabSize, OtherEvent>>,\n        tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                   step_choosers<StepChooserUse::LtsStep>>,\n        tmpl::pair<StepChooser<StepChooserUse::Slab>,\n                   tmpl::push_back<step_choosers<StepChooserUse::Slab>,\n                                   StepChoosers::FixedLtsRatio>>,\n        tmpl::pair<Trigger, tmpl::list<Triggers::Always>>>;\n  };\n};\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.StepperErrorTolerancesCompute\",\n                  \"[Unit][Time]\") {\n  TestHelpers::db::test_compute_tag<\n      Tags::StepperErrorEstimatesEnabledCompute<true>>(\n      \"StepperErrorEstimatesEnabled\");\n  TestHelpers::db::test_compute_tag<\n      Tags::StepperErrorTolerancesCompute<EvolvedVariablesTag, true>>(\n      \"StepperErrorTolerances(Variables(EvolvedVar1,EvolvedVar2))\");\n\n  {\n    INFO(\"Compute tag LTS test\");\n    const auto test_lts_tags = [](const auto box, const bool all_orders) {\n      const auto expected_estimates =\n          all_orders ? StepperErrorTolerances::Estimates::AllOrders\n                     : StepperErrorTolerances::Estimates::StepperOrder;\n\n      db::mutate<Tags::LtsStepChoosers,\n                 Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n          [](const gsl::not_null<Tags::LtsStepChoosers::type*> choosers,\n             const gsl::not_null<EventsAndTriggers*> events) {\n            *choosers = TestHelpers::test_creation<Tags::LtsStepChoosers::type,\n                                                   Metavariables>(\n                \"- ErrorControl:\\n\"\n                \"    SafetyFactor: 0.95\\n\"\n                \"    AbsoluteTolerance: 1.0e-5\\n\"\n                \"    RelativeTolerance: 1.0e-4\\n\"\n                \"    MaxFactor: 2.1\\n\"\n                \"    MinFactor: 0.5\\n\"\n                \"- LimitIncrease:\\n\"\n                \"    Factor: 2\\n\"\n                \"- Constant: 0.5\");\n            *events = EventsAndTriggers{};\n          },\n          box);\n      CHECK(db::get<Tags::StepperErrorEstimatesEnabled>(*box));\n      CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(*box) ==\n            StepperErrorTolerances{.estimates = expected_estimates,\n                                   .absolute = 1.0e-5,\n                                   .relative = 1.0e-4});\n      CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n\n      db::mutate<Tags::LtsStepChoosers>(\n          [](const gsl::not_null<Tags::LtsStepChoosers::type*> choosers) {\n            *choosers = TestHelpers::test_creation<Tags::LtsStepChoosers::type,\n                                                   Metavariables>(\n                \"- LimitIncrease:\\n\"\n                \"    Factor: 2\\n\"\n                \"- Constant: 0.5\");\n          },\n          box);\n      CHECK_FALSE(db::get<Tags::StepperErrorEstimatesEnabled>(*box));\n      CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n      CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n\n      db::mutate<Tags::LtsStepChoosers>(\n          [](const gsl::not_null<Tags::LtsStepChoosers::type*> choosers) {\n            *choosers = TestHelpers::test_creation<Tags::LtsStepChoosers::type,\n                                                   Metavariables>(\"\");\n          },\n          box);\n      CHECK_FALSE(db::get<Tags::StepperErrorEstimatesEnabled>(*box));\n      CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n      CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n\n      db::mutate<Tags::LtsStepChoosers>(\n          [](const gsl::not_null<Tags::LtsStepChoosers::type*> choosers) {\n            *choosers = TestHelpers::test_creation<Tags::LtsStepChoosers::type,\n                                                   Metavariables>(\n                \"- ErrorControl:\\n\"\n                \"    SafetyFactor: 0.95\\n\"\n                \"    AbsoluteTolerance: 1.0e-5\\n\"\n                \"    RelativeTolerance: 1.0e-4\\n\"\n                \"    MaxFactor: 2.1\\n\"\n                \"    MinFactor: 0.5\\n\"\n                \"- ErrorControl:\\n\"\n                \"    SafetyFactor: 0.8\\n\"\n                \"    AbsoluteTolerance: 1.0e-5\\n\"\n                \"    RelativeTolerance: 1.0e-4\\n\"\n                \"    MaxFactor: 1.1\\n\"\n                \"    MinFactor: 0.1\\n\"\n                \"- LimitIncrease:\\n\"\n                \"    Factor: 2\\n\"\n                \"- Constant: 0.5\");\n          },\n          box);\n      CHECK(db::get<Tags::StepperErrorEstimatesEnabled>(*box));\n      CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(*box) ==\n            StepperErrorTolerances{.estimates = expected_estimates,\n                                   .absolute = 1.0e-5,\n                                   .relative = 1.0e-4});\n      CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n\n      db::mutate<Tags::LtsStepChoosers>(\n          [](const gsl::not_null<Tags::LtsStepChoosers::type*> choosers) {\n            *choosers = TestHelpers::test_creation<Tags::LtsStepChoosers::type,\n                                                   Metavariables>(\n                \"- ErrorControl:\\n\"\n                \"    SafetyFactor: 0.95\\n\"\n                \"    AbsoluteTolerance: 1.0e-5\\n\"\n                \"    RelativeTolerance: 1.0e-4\\n\"\n                \"    MaxFactor: 2.1\\n\"\n                \"    MinFactor: 0.5\\n\"\n                \"- ErrorControl(SelectorLabel):\\n\"\n                \"    SafetyFactor: 0.8\\n\"\n                \"    AbsoluteTolerance: 1.0e-5\\n\"\n                \"    RelativeTolerance: 1.0e-8\\n\"\n                \"    MaxFactor: 1.1\\n\"\n                \"    MinFactor: 0.1\\n\"\n                \"- LimitIncrease:\\n\"\n                \"    Factor: 2\\n\"\n                \"- Constant: 0.5\");\n          },\n          box);\n      CHECK(db::get<Tags::StepperErrorEstimatesEnabled>(*box));\n      CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(*box) ==\n            StepperErrorTolerances{.estimates = expected_estimates,\n                                   .absolute = 1.0e-5,\n                                   .relative = 1.0e-4});\n      CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(\n                *box) == StepperErrorTolerances{.estimates = expected_estimates,\n                                                .absolute = 1.0e-5,\n                                                .relative = 1.0e-8});\n\n      db::mutate<Tags::LtsStepChoosers>(\n          [](const gsl::not_null<Tags::LtsStepChoosers::type*> choosers) {\n            *choosers = TestHelpers::test_creation<Tags::LtsStepChoosers::type,\n                                                   Metavariables>(\n                \"- ErrorControl:\\n\"\n                \"    SafetyFactor: 0.95\\n\"\n                \"    AbsoluteTolerance: 1.0e-5\\n\"\n                \"    RelativeTolerance: 1.0e-4\\n\"\n                \"    MaxFactor: 2.1\\n\"\n                \"    MinFactor: 0.5\\n\"\n                \"- ErrorControl:\\n\"\n                \"    SafetyFactor: 0.8\\n\"\n                \"    AbsoluteTolerance: 1.0e-5\\n\"\n                \"    RelativeTolerance: 1.0e-8\\n\"\n                \"    MaxFactor: 1.1\\n\"\n                \"    MinFactor: 0.1\\n\"\n                \"- LimitIncrease:\\n\"\n                \"    Factor: 2\\n\"\n                \"- Constant: 0.5\");\n          },\n          box);\n      CHECK_THROWS_WITH(\n          db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(*box),\n          Catch::Matchers::ContainsSubstring(\"All ErrorControl events for \") and\n              Catch::Matchers::ContainsSubstring(\"EvolvedVar1\") and\n              Catch::Matchers::ContainsSubstring(\n                  \" must use the same tolerances.\"));\n\n      db::mutate<Tags::LtsStepChoosers,\n                 Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n          [](const gsl::not_null<Tags::LtsStepChoosers::type*> choosers,\n             const gsl::not_null<EventsAndTriggers*> events) {\n            *choosers = TestHelpers::test_creation<Tags::LtsStepChoosers::type,\n                                                   Metavariables>(\"\");\n            *events =\n                TestHelpers::test_creation<EventsAndTriggers, Metavariables>(\n                    \"- Trigger: Always\\n\"\n                    \"  Events:\\n\"\n                    \"    - OtherEvent\\n\"\n                    \"- Trigger: Always\\n\"\n                    \"  Events:\\n\"\n                    \"    - OtherEvent\\n\"\n                    \"    - ChangeSlabSize:\\n\"\n                    \"        DelayChange: 0\\n\"\n                    \"        StepChoosers:\\n\"\n                    \"          - LimitIncrease:\\n\"\n                    \"              Factor: 2\\n\"\n                    \"          - FixedLtsRatio:\\n\"\n                    \"              StepChoosers:\\n\"\n                    \"                - Constant: 0.5\");\n          },\n          box);\n      CHECK_FALSE(db::get<Tags::StepperErrorEstimatesEnabled>(*box));\n      CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n      CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n\n      db::mutate<Tags::LtsStepChoosers,\n                 Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n          [](const gsl::not_null<Tags::LtsStepChoosers::type*> choosers,\n             const gsl::not_null<EventsAndTriggers*> events) {\n            *choosers = TestHelpers::test_creation<Tags::LtsStepChoosers::type,\n                                                   Metavariables>(\"\");\n            *events =\n                TestHelpers::test_creation<EventsAndTriggers, Metavariables>(\n                    \"- Trigger: Always\\n\"\n                    \"  Events:\\n\"\n                    \"    - OtherEvent\\n\"\n                    \"- Trigger: Always\\n\"\n                    \"  Events:\\n\"\n                    \"    - OtherEvent\\n\"\n                    \"    - ChangeSlabSize:\\n\"\n                    \"        DelayChange: 0\\n\"\n                    \"        StepChoosers:\\n\"\n                    \"          - LimitIncrease:\\n\"\n                    \"              Factor: 2\\n\"\n                    \"          - FixedLtsRatio:\\n\"\n                    \"              StepChoosers:\\n\"\n                    \"                - ErrorControl:\\n\"\n                    \"                    SafetyFactor: 0.95\\n\"\n                    \"                    AbsoluteTolerance: 1.0e-5\\n\"\n                    \"                    RelativeTolerance: 1.0e-4\\n\"\n                    \"                    MaxFactor: 2.1\\n\"\n                    \"                    MinFactor: 0.5\\n\"\n                    \"                - Constant: 0.5\");\n          },\n          box);\n      CHECK(db::get<Tags::StepperErrorEstimatesEnabled>(*box));\n      CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(*box) ==\n            StepperErrorTolerances{.estimates = expected_estimates,\n                                   .absolute = 1.0e-5,\n                                   .relative = 1.0e-4});\n      CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(*box)\n                .estimates == StepperErrorTolerances::Estimates::None);\n    };\n\n    auto box = db::create<\n        db::AddSimpleTags<\n            Tags::LtsStepChoosers,\n            Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n            Tags::ConcreteTimeStepper<LtsTimeStepper>,\n            Tags::VariableOrderAlgorithm>,\n        tmpl::push_back<\n            time_stepper_ref_tags<LtsTimeStepper>,\n            Tags::StepperErrorEstimatesEnabledCompute<true>,\n            Tags::StepperErrorTolerancesCompute<EvolvedVariablesTag, true>,\n            Tags::StepperErrorTolerancesCompute<AltEvolvedVariablesTag,\n                                                true>>>();\n\n    db::mutate<Tags::ConcreteTimeStepper<LtsTimeStepper>,\n               Tags::VariableOrderAlgorithm>(\n        [](const gsl::not_null<std::unique_ptr<LtsTimeStepper>*> time_stepper,\n           const gsl::not_null<VariableOrderAlgorithm*> vo_algorithm) {\n          *time_stepper = std::make_unique<TimeSteppers::AdamsBashforth>(4);\n          *vo_algorithm = VariableOrderAlgorithm(0.1);\n        },\n        make_not_null(&box));\n    test_lts_tags(make_not_null(&box), false);\n\n    db::mutate<Tags::VariableOrderAlgorithm>(\n        [](const gsl::not_null<VariableOrderAlgorithm*> vo_algorithm) {\n          *vo_algorithm = VariableOrderAlgorithm(4_st);\n        },\n        make_not_null(&box));\n    test_lts_tags(make_not_null(&box), false);\n\n    db::mutate<Tags::ConcreteTimeStepper<LtsTimeStepper>>(\n        [](const gsl::not_null<std::unique_ptr<LtsTimeStepper>*> time_stepper) {\n          *time_stepper =\n              std::make_unique<TimeSteppers::AdamsBashforth>(std::nullopt);\n        },\n        make_not_null(&box));\n    test_lts_tags(make_not_null(&box), false);\n\n    db::mutate<Tags::VariableOrderAlgorithm>(\n        [](const gsl::not_null<VariableOrderAlgorithm*> vo_algorithm) {\n          *vo_algorithm = VariableOrderAlgorithm(0.1);\n        },\n        make_not_null(&box));\n    test_lts_tags(make_not_null(&box), true);\n  }\n\n  {\n    INFO(\"Compute tag GTS test\");\n    auto box = db::create<\n        db::AddSimpleTags<\n            Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>,\n        db::AddComputeTags<\n            Tags::StepperErrorEstimatesEnabledCompute<false>,\n            Tags::StepperErrorTolerancesCompute<EvolvedVariablesTag, false>,\n            Tags::StepperErrorTolerancesCompute<AltEvolvedVariablesTag,\n                                                false>>>();\n    db::mutate<Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n        [](const gsl::not_null<EventsAndTriggers*> events) {\n          *events =\n              TestHelpers::test_creation<EventsAndTriggers, Metavariables>(\n                  \"- Trigger: Always\\n\"\n                  \"  Events:\\n\"\n                  \"    - OtherEvent\\n\"\n                  \"- Trigger: Always\\n\"\n                  \"  Events:\\n\"\n                  \"    - OtherEvent\\n\"\n                  \"    - ChangeSlabSize:\\n\"\n                  \"        DelayChange: 0\\n\"\n                  \"        StepChoosers:\\n\"\n                  \"          - LimitIncrease:\\n\"\n                  \"              Factor: 2\\n\"\n                  \"          - ErrorControl:\\n\"\n                  \"              SafetyFactor: 0.95\\n\"\n                  \"              AbsoluteTolerance: 1.0e-5\\n\"\n                  \"              RelativeTolerance: 1.0e-4\\n\"\n                  \"              MaxFactor: 2.1\\n\"\n                  \"              MinFactor: 0.5\\n\"\n                  \"          - Constant: 0.5\");\n        },\n        make_not_null(&box));\n    CHECK(db::get<Tags::StepperErrorEstimatesEnabled>(box));\n    CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(box) ==\n          StepperErrorTolerances{\n              .estimates = StepperErrorTolerances::Estimates::StepperOrder,\n              .absolute = 1.0e-5,\n              .relative = 1.0e-4});\n    CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(box)\n              .estimates == StepperErrorTolerances::Estimates::None);\n\n    db::mutate<Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n        [](const gsl::not_null<EventsAndTriggers*> events) {\n          *events =\n              TestHelpers::test_creation<EventsAndTriggers, Metavariables>(\n                  \"- Trigger: Always\\n\"\n                  \"  Events:\\n\"\n                  \"    - OtherEvent\\n\"\n                  \"- Trigger: Always\\n\"\n                  \"  Events:\\n\"\n                  \"    - OtherEvent\\n\"\n                  \"    - ChangeSlabSize:\\n\"\n                  \"        DelayChange: 0\\n\"\n                  \"        StepChoosers:\\n\"\n                  \"          - LimitIncrease:\\n\"\n                  \"              Factor: 2\\n\"\n                  \"          - Constant: 0.5\");\n        },\n        make_not_null(&box));\n    CHECK_FALSE(db::get<Tags::StepperErrorEstimatesEnabled>(box));\n    CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(box)\n              .estimates == StepperErrorTolerances::Estimates::None);\n    CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(box)\n              .estimates == StepperErrorTolerances::Estimates::None);\n\n    db::mutate<Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n        [](const gsl::not_null<EventsAndTriggers*> events) {\n          *events =\n              TestHelpers::test_creation<EventsAndTriggers, Metavariables>(\n                  \"- Trigger: Always\\n\"\n                  \"  Events:\\n\"\n                  \"    - OtherEvent\\n\"\n                  \"- Trigger: Always\\n\"\n                  \"  Events:\\n\"\n                  \"    - OtherEvent\");\n        },\n        make_not_null(&box));\n    CHECK_FALSE(db::get<Tags::StepperErrorEstimatesEnabled>(box));\n    CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(box)\n              .estimates == StepperErrorTolerances::Estimates::None);\n    CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(box)\n              .estimates == StepperErrorTolerances::Estimates::None);\n\n    db::mutate<Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>>(\n        [](const gsl::not_null<EventsAndTriggers*> events) {\n          *events =\n              TestHelpers::test_creation<EventsAndTriggers, Metavariables>(\"\");\n        },\n        make_not_null(&box));\n    CHECK_FALSE(db::get<Tags::StepperErrorEstimatesEnabled>(box));\n    CHECK(db::get<Tags::StepperErrorTolerances<EvolvedVariablesTag>>(box)\n              .estimates == StepperErrorTolerances::Estimates::None);\n    CHECK(db::get<Tags::StepperErrorTolerances<AltEvolvedVariablesTag>>(box)\n              .estimates == StepperErrorTolerances::Estimates::None);\n  }\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_StepperErrors.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing DummyVariablesTag =\n    Tags::Variables<tmpl::list<TestHelpers::Tags::Scalar<>>>;\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.StepperErrors\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::StepperErrors<DummyVariablesTag>>(\n      \"StepperErrors(Variables(Scalar))\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_Time.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/Time.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.Time\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::Time>(\"Time\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_TimeAndPrevious.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/TimeAndPrevious.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.TimeAndPrevious\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::TimeAndPrevious<0>>(\n      \"TimeAndPrevious0\");\n  TestHelpers::db::test_simple_tag<Tags::TimeAndPrevious<1>>(\n      \"TimeAndPrevious1\");\n  TestHelpers::db::test_compute_tag<Tags::TimeAndPreviousCompute<0>>(\n      \"TimeAndPrevious0\");\n  TestHelpers::db::test_compute_tag<Tags::TimeAndPreviousCompute<1>>(\n      \"TimeAndPrevious1\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_TimeStep.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.TimeStep\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::TimeStep>(\"TimeStep\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_TimeStepId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.TimeStepId\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::TimeStepId>(\"TimeStepId\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_TimeStepper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <string>\n#include <type_traits>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct DummyType {};\n\nclass FakeTimeStepper {\n public:\n  using provided_time_stepper_interfaces = tmpl::list<FakeTimeStepper>;\n};\n\nclass MoreSpecificFakeTimeStepper : public FakeTimeStepper {\n public:\n  using provided_time_stepper_interfaces =\n      tmpl::list<MoreSpecificFakeTimeStepper, FakeTimeStepper>;\n};\n\nstatic_assert(\n    std::is_same_v<\n        time_stepper_ref_tags<FakeTimeStepper>,\n        tmpl::list<Tags::TimeStepperRef<FakeTimeStepper, FakeTimeStepper>>>);\nstatic_assert(std::is_same_v<\n              time_stepper_ref_tags<MoreSpecificFakeTimeStepper>,\n              tmpl::list<Tags::TimeStepperRef<MoreSpecificFakeTimeStepper,\n                                              MoreSpecificFakeTimeStepper>,\n                         Tags::TimeStepperRef<FakeTimeStepper,\n                                              MoreSpecificFakeTimeStepper>>>);\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.TimeStepper\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::ConcreteTimeStepper<DummyType>>(\n      \"ConcreteTimeStepper\");\n  TestHelpers::db::test_simple_tag<Tags::TimeStepper<DummyType>>(\"TimeStepper\");\n  TestHelpers::db::test_reference_tag<\n      Tags::TimeStepperRef<DummyType, DummyType>>(\"TimeStepper\");\n\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  // Check that these are allowed...\n  Tags::ConcreteTimeStepper<TimeStepper>::create_from_options(\n      std::make_unique<TimeSteppers::AdamsBashforth>(3));\n  Tags::ConcreteTimeStepper<LtsTimeStepper>::create_from_options(\n      std::make_unique<TimeSteppers::AdamsBashforth>(3));\n  Tags::ConcreteTimeStepper<LtsTimeStepper>::create_from_options(\n      std::make_unique<TimeSteppers::AdamsBashforth>(std::nullopt));\n  // ...but this isn't.\n  CHECK_THROWS_WITH(\n      Tags::ConcreteTimeStepper<TimeStepper>::create_from_options(\n          std::make_unique<TimeSteppers::AdamsBashforth>(std::nullopt)),\n      Catch::Matchers::ContainsSubstring(\n          \"Variable-order TimeSteppers are only supported in evolutions with \"\n          \"local time-stepping.\"));\n}\n"
  },
  {
    "path": "tests/Unit/Time/Tags/Test_VariableOrderAlgorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Helpers/DataStructures/DataBox/TestHelpers.hpp\"\n#include \"Time/Tags/VariableOrderAlgorithm.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Tags.VariableOrderAlgorithm\", \"[Unit][Time]\") {\n  TestHelpers::db::test_simple_tag<Tags::VariableOrderAlgorithm>(\n      \"VariableOrderAlgorithm\");\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_AdaptiveSteppingDiagnostics.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.AdaptiveSteppingDiagnostics\", \"[Unit][Time]\") {\n  AdaptiveSteppingDiagnostics diags{1, 2, 3, 4, 5};\n  CHECK(diags.number_of_slabs == 1);\n  CHECK(diags.number_of_slab_size_changes == 2);\n  CHECK(diags.number_of_steps == 3);\n  CHECK(diags.number_of_step_fraction_changes == 4);\n  CHECK(diags.number_of_step_rejections == 5);\n\n  CHECK(diags == AdaptiveSteppingDiagnostics{1, 2, 3, 4, 5});\n  CHECK(diags != AdaptiveSteppingDiagnostics{2, 2, 3, 4, 5});\n  CHECK(diags != AdaptiveSteppingDiagnostics{1, 3, 3, 4, 5});\n  CHECK(diags != AdaptiveSteppingDiagnostics{1, 2, 4, 4, 5});\n  CHECK(diags != AdaptiveSteppingDiagnostics{1, 2, 3, 5, 5});\n  CHECK(diags != AdaptiveSteppingDiagnostics{1, 2, 3, 4, 6});\n  CHECK_FALSE(diags != AdaptiveSteppingDiagnostics{1, 2, 3, 4, 5});\n  CHECK_FALSE(diags == AdaptiveSteppingDiagnostics{2, 2, 3, 4, 5});\n  CHECK_FALSE(diags == AdaptiveSteppingDiagnostics{1, 3, 3, 4, 5});\n  CHECK_FALSE(diags == AdaptiveSteppingDiagnostics{1, 2, 4, 4, 5});\n  CHECK_FALSE(diags == AdaptiveSteppingDiagnostics{1, 2, 3, 5, 5});\n  CHECK_FALSE(diags == AdaptiveSteppingDiagnostics{1, 2, 3, 4, 6});\n\n  CHECK(diags == serialize_and_deserialize(diags));\n  diags += diags;\n  CHECK(diags == AdaptiveSteppingDiagnostics{1, 2, 6, 8, 10});\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_AdvanceTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <cstdint>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/AdvanceTime.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/StepNumberWithinSlab.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/ClassicalRungeKutta4.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nclass TimeStepper;\n\nnamespace {\nvoid check(std::unique_ptr<TimeStepper> time_stepper,\n           const std::vector<Rational>& substeps, const Time& start,\n           const TimeDelta& time_step, const bool using_error_control) {\n  std::vector<TimeDelta> substep_offsets{};\n  substep_offsets.reserve(substeps.size());\n  for (const auto& substep : substeps) {\n    substep_offsets.push_back(substep * time_step);\n  }\n\n  auto box = db::create<\n      db::AddSimpleTags<Tags::ConcreteTimeStepper<TimeStepper>,\n                        Tags::TimeStepId, Tags::Next<Tags::TimeStepId>,\n                        Tags::TimeStep, Tags::Time, Tags::StepNumberWithinSlab,\n                        Tags::StepperErrorEstimatesEnabled,\n                        Tags::AdaptiveSteppingDiagnostics>,\n      time_stepper_ref_tags<TimeStepper>>(\n      std::move(time_stepper), TimeStepId(time_step.is_positive(), 8, start),\n      substeps.size() == 1\n          ? TimeStepId(time_step.is_positive(), 8, start + time_step)\n          : TimeStepId(time_step.is_positive(), 8, start, 1, time_step,\n                       (start + substep_offsets[1]).value()),\n      time_step, start.value(), uint64_t{0}, using_error_control,\n      AdaptiveSteppingDiagnostics{1, 2, 3, 4, 5});\n\n  uint64_t step_number_within_slab = 0;\n  auto current_slab_number =\n      db::get<Tags::AdaptiveSteppingDiagnostics>(box).number_of_slabs;\n  for (const auto& step_start : {start, start + time_step}) {\n    for (size_t substep = 0; substep < substep_offsets.size(); ++substep) {\n      const double substep_time =\n          (step_start + gsl::at(substep_offsets, substep)).value();\n      CHECK(db::get<Tags::TimeStepId>(box) ==\n            TimeStepId(time_step.is_positive(), 8, step_start, substep,\n                       time_step, substep_time));\n      CHECK(db::get<Tags::TimeStep>(box) == time_step);\n      CHECK(db::get<Tags::Time>(box) ==\n            db::get<Tags::TimeStepId>(box).substep_time());\n      CHECK(db::get<Tags::StepNumberWithinSlab>(box) ==\n            step_number_within_slab);\n      db::mutate_apply<AdvanceTime<>>(make_not_null(&box));\n    }\n    if (current_slab_number ==\n        db::get<Tags::AdaptiveSteppingDiagnostics>(box).number_of_slabs) {\n      ++step_number_within_slab;\n    } else {\n      step_number_within_slab = 0;\n      ++current_slab_number;\n      ASSERT(\n          current_slab_number ==\n              db::get<Tags::AdaptiveSteppingDiagnostics>(box).number_of_slabs,\n          \"Current slab number is not what I expected\");\n    }\n  }\n\n  const auto& final_time_id = db::get<Tags::TimeStepId>(box);\n  const auto expected_slab = start.slab().advance_towards(time_step);\n  CHECK(final_time_id.step_time().slab() == expected_slab);\n  CHECK(final_time_id ==\n        TimeStepId(time_step.is_positive(), 8, start + 2 * time_step));\n  CHECK(db::get<Tags::Time>(box) == final_time_id.substep_time());\n  CHECK(db::get<Tags::TimeStep>(box) == time_step.with_slab(expected_slab));\n  CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) ==\n        AdaptiveSteppingDiagnostics{\n            1 + static_cast<uint64_t>(final_time_id.slab_number() - 8), 2, 5, 4,\n            5});\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.AdvanceTime\", \"[Unit][Time]\") {\n  const Slab slab(0., 1.);\n  check(std::make_unique<TimeSteppers::ClassicalRungeKutta4>(),\n        {0, {1, 2}, {1, 2}, 1}, slab.start(), slab.duration() / 2, false);\n  check(std::make_unique<TimeSteppers::ClassicalRungeKutta4>(),\n        {0, {1, 2}, {1, 2}, 1}, slab.end(), -slab.duration() / 2, false);\n  check(std::make_unique<TimeSteppers::AdamsBashforth>(1), {0}, slab.start(),\n        slab.duration() / 2, false);\n  check(std::make_unique<TimeSteppers::AdamsBashforth>(1), {0}, slab.end(),\n        -slab.duration() / 2, false);\n  check(std::make_unique<TimeSteppers::ClassicalRungeKutta4>(),\n        {0, {1, 2}, {1, 2}, 1, {3, 4}}, slab.start(), slab.duration() / 2,\n        true);\n  check(std::make_unique<TimeSteppers::ClassicalRungeKutta4>(),\n        {0, {1, 2}, {1, 2}, 1, {3, 4}}, slab.end(), -slab.duration() / 2, true);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/Test_ApproximateTime.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Time/ApproximateTime.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.ApproximateTime\", \"[Unit][Time]\") {\n  const double val1 = 1.25;\n  const double val2 = 3.5;\n  const Slab slab(val1, val2);\n\n  const Time time1 = slab.start();\n  const Time time2 = slab.end();\n  const ApproximateTime approx1{val1};\n  const ApproximateTime approx2{val2};\n  const TimeDelta diff = time2 - time1;\n  const ApproximateTimeDelta approx_diff{val2 - val1};\n  const TimeDelta neg_diff = time1 - time2;\n  const ApproximateTimeDelta approx_neg_diff{val1 - val2};\n\n  CHECK(approx1.value() == time1.value());\n  CHECK(approx2.value() == time2.value());\n  CHECK(approx_diff.value() == diff.value());\n  CHECK(approx_neg_diff.value() == neg_diff.value());\n\n  CHECK(approx1 == approx1);\n  CHECK(approx1 == time1);\n  CHECK(time1 == approx1);\n  CHECK_FALSE(approx1 == approx2);\n  CHECK_FALSE(approx1 == time2);\n  CHECK_FALSE(time2 == approx1);\n  CHECK_FALSE(approx1 != approx1);\n  CHECK_FALSE(approx1 != time1);\n  CHECK_FALSE(time1 != approx1);\n  CHECK(approx1 != approx2);\n  CHECK(approx1 != time2);\n  CHECK(time2 != approx1);\n\n  CHECK(approx_diff == approx_diff);\n  CHECK(approx_diff == diff);\n  CHECK(diff == approx_diff);\n  CHECK_FALSE(approx_diff == approx_neg_diff);\n  CHECK_FALSE(approx_diff == neg_diff);\n  CHECK_FALSE(neg_diff == approx_diff);\n  CHECK_FALSE(approx_diff != approx_diff);\n  CHECK_FALSE(approx_diff != diff);\n  CHECK_FALSE(diff != approx_diff);\n  CHECK(approx_diff != approx_neg_diff);\n  CHECK(approx_diff != neg_diff);\n  CHECK(neg_diff != approx_diff);\n\n  CHECK(approx1 < approx2);\n  CHECK(approx1 < time2);\n  CHECK(time1 < approx2);\n  CHECK_FALSE(approx2 < approx1);\n  CHECK_FALSE(approx2 < time1);\n  CHECK_FALSE(time2 < approx1);\n  CHECK_FALSE(approx1 < approx1);\n  CHECK_FALSE(approx1 < time1);\n  CHECK_FALSE(time1 < approx1);\n\n  CHECK_FALSE(approx1 > approx2);\n  CHECK_FALSE(approx1 > time2);\n  CHECK_FALSE(time1 > approx2);\n  CHECK(approx2 > approx1);\n  CHECK(approx2 > time1);\n  CHECK(time2 > approx1);\n  CHECK_FALSE(approx1 > approx1);\n  CHECK_FALSE(approx1 > time1);\n  CHECK_FALSE(time1 > approx1);\n\n  CHECK(approx1 <= approx2);\n  CHECK(approx1 <= time2);\n  CHECK(time1 <= approx2);\n  CHECK_FALSE(approx2 <= approx1);\n  CHECK_FALSE(approx2 <= time1);\n  CHECK_FALSE(time2 <= approx1);\n  CHECK(approx1 <= approx1);\n  CHECK(approx1 <= time1);\n  CHECK(time1 <= approx1);\n\n  CHECK_FALSE(approx1 >= approx2);\n  CHECK_FALSE(approx1 >= time2);\n  CHECK_FALSE(time1 >= approx2);\n  CHECK(approx2 >= approx1);\n  CHECK(approx2 >= time1);\n  CHECK(time2 >= approx1);\n  CHECK(approx1 >= approx1);\n  CHECK(approx1 >= time1);\n  CHECK(time1 >= approx1);\n\n  CHECK(+approx_diff == approx_diff);\n  CHECK(-approx_diff == approx_neg_diff);\n  CHECK(approx_diff.is_positive());\n  CHECK(not approx_neg_diff.is_positive());\n  CHECK(abs(approx_diff) == approx_diff);\n  CHECK(abs(approx_neg_diff) == approx_diff);\n\n  CHECK(approx1 - approx2 == time1 - time2);\n  CHECK(approx1 - time2 == time1 - time2);\n  CHECK(time1 - approx2 == time1 - time2);\n\n  CHECK(approx1 + approx_diff == time1 + diff);\n  CHECK(approx1 + diff == time1 + diff);\n  CHECK(time1 + approx_diff == time1 + diff);\n\n  CHECK(approx_diff + approx1 == diff + time1);\n  CHECK(approx_diff + approx1 == diff + time1);\n  CHECK(approx_diff + time1 == diff + time1);\n\n  CHECK(approx2 - approx_diff == time2 - diff);\n  CHECK(approx2 - diff == time2 - diff);\n  CHECK(time2 - approx_diff == time2 - diff);\n\n  CHECK(approx_diff + approx_diff == diff + diff);\n  CHECK(approx_diff + diff == diff + diff);\n  CHECK(diff + approx_diff == diff + diff);\n  CHECK(approx_diff + approx_neg_diff == diff + neg_diff);\n  CHECK(approx_diff + neg_diff == diff + neg_diff);\n  CHECK(diff + approx_neg_diff == diff + neg_diff);\n\n  CHECK(approx_diff - approx_diff == diff - diff);\n  CHECK(approx_diff - diff == diff - diff);\n  CHECK(diff - approx_diff == diff - diff);\n  CHECK(approx_diff - approx_neg_diff == diff - neg_diff);\n  CHECK(approx_diff - neg_diff == diff - neg_diff);\n  CHECK(diff - approx_neg_diff == diff - neg_diff);\n\n  CHECK(approx_diff / approx_diff == diff / diff);\n  CHECK(approx_diff / diff == diff / diff);\n  CHECK(diff / approx_diff == diff / diff);\n  CHECK(approx_diff / approx_neg_diff == diff / neg_diff);\n  CHECK(approx_diff / neg_diff == diff / neg_diff);\n  CHECK(diff / approx_neg_diff == diff / neg_diff);\n\n  const TimeDelta::rational_t third{1, 3};\n  const TimeDelta::rational_t quarter{1, 4};\n  CHECK(approx_diff * 3 == diff * 3);\n  CHECK(approx_diff * quarter == diff * quarter);\n  CHECK(3 * approx_diff == 3 * diff);\n  CHECK(quarter * approx_diff == quarter * diff);\n  CHECK(approx_diff / third == diff / third);\n  CHECK(approx_diff / 4 == diff / 4);\n\n  CHECK(get_output(approx1) == get_output(approx1.value()));\n  CHECK(get_output(approx_diff) == get_output(approx_diff.value()));\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_BoundaryHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <boost/functional/hash.hpp>\n#include <cctype>\n#include <cstddef>\n#include <limits>\n#include <map>\n#include <string>\n#include <type_traits>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/BoundaryHistory.tpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nnamespace {\nusing BoundaryHistoryType =\n    TimeSteppers::BoundaryHistory<std::string, std::vector<int>, double>;\n\nTimeStepId make_time_id(const double t, const uint64_t substep = 0) {\n  const Slab slab(t, t + 0.5);\n  if (substep == 0) {\n    return {true, 0, slab.start()};\n  } else {\n    return {true, 0, slab.start(), substep, slab.duration(), t + 0.25};\n  }\n}\n\n// Check the expected state at one particular point in the test.\ntemplate <bool IsConcrete, typename T>\nvoid check_local(const T& local_times) {\n  CHECK(local_times.size() == 4);\n\n  CHECK(local_times[0] == make_time_id(-1.));\n  CHECK(local_times[1] == make_time_id(0.));\n  CHECK(local_times[2] == make_time_id(1.));\n  CHECK(local_times[3] == make_time_id(2.));\n\n  if constexpr (IsConcrete) {\n    CHECK(local_times.data(make_time_id(-1.)) == get_output(-1));\n    CHECK(local_times.data(make_time_id(0.)) == get_output(0));\n    CHECK(local_times.data(make_time_id(1.)) == get_output(1));\n    CHECK(local_times.data(make_time_id(2.)) == get_output(2));\n    CHECK(local_times.data(0) == get_output(-1));\n    CHECK(local_times.data(1) == get_output(0));\n    CHECK(local_times.data(2) == get_output(1));\n    CHECK(local_times.data(3) == get_output(2));\n  }\n\n  CHECK(local_times.integration_order(make_time_id(-1.)) == 9);\n  CHECK(local_times.integration_order(make_time_id(0.)) == 10);\n  CHECK(local_times.integration_order(make_time_id(1.)) == 11);\n  CHECK(local_times.integration_order(make_time_id(2.)) == 12);\n  CHECK(local_times.integration_order(0) == 9);\n  CHECK(local_times.integration_order(1) == 10);\n  CHECK(local_times.integration_order(2) == 11);\n  CHECK(local_times.integration_order(3) == 12);\n}\n\n// Check the expected state at one particular point in the test.\ntemplate <bool IsConcrete, typename T>\nvoid check_remote(const T& remote_times) {\n  CHECK(remote_times[0] == make_time_id(-2.));\n  CHECK(remote_times[1] == make_time_id(-1.));\n  CHECK(remote_times[2] == make_time_id(0.));\n  CHECK(remote_times[3] == make_time_id(1.));\n  CHECK(remote_times[4] == make_time_id(2.));\n  CHECK(remote_times[5] == make_time_id(3.));\n\n  if constexpr (IsConcrete) {\n    CHECK(remote_times.data(make_time_id(-2.)) == std::vector{-2});\n    CHECK(remote_times.data(make_time_id(-1.)) == std::vector{-1});\n    CHECK(remote_times.data(make_time_id(0.)) == std::vector{0});\n    CHECK(remote_times.data(make_time_id(1.)) == std::vector{1});\n    CHECK(remote_times.data(make_time_id(2.)) == std::vector{2});\n    CHECK(remote_times.data(make_time_id(3.)) == std::vector{3});\n    CHECK(remote_times.data(0) == std::vector{-2});\n    CHECK(remote_times.data(1) == std::vector{-1});\n    CHECK(remote_times.data(2) == std::vector{0});\n    CHECK(remote_times.data(3) == std::vector{1});\n    CHECK(remote_times.data(4) == std::vector{2});\n    CHECK(remote_times.data(5) == std::vector{3});\n  }\n\n  CHECK(remote_times.integration_order(make_time_id(-2.)) == 18);\n  CHECK(remote_times.integration_order(make_time_id(-1.)) == 19);\n  CHECK(remote_times.integration_order(make_time_id(0.)) == 20);\n  CHECK(remote_times.integration_order(make_time_id(1.)) == 21);\n  CHECK(remote_times.integration_order(make_time_id(2.)) == 22);\n  CHECK(remote_times.integration_order(make_time_id(3.)) == 23);\n  CHECK(remote_times.integration_order(0) == 18);\n  CHECK(remote_times.integration_order(1) == 19);\n  CHECK(remote_times.integration_order(2) == 20);\n  CHECK(remote_times.integration_order(3) == 21);\n  CHECK(remote_times.integration_order(4) == 22);\n  CHECK(remote_times.integration_order(5) == 23);\n}\n\ntemplate <bool EvaluatorOwnsCoupling>\nsize_t check_coupling_evaluation(const BoundaryHistoryType& hist) {\n  std::string local_arg;\n  std::vector<int> remote_arg;\n  double coupling_return = std::numeric_limits<double>::signaling_NaN();\n\n  struct NonCopyableCoupling {\n    NonCopyableCoupling(const NonCopyableCoupling&) = delete;\n    NonCopyableCoupling& operator=(const NonCopyableCoupling&) = delete;\n    ~NonCopyableCoupling() = default;\n    NonCopyableCoupling(gsl::not_null<std::string*> local_arg,\n                        gsl::not_null<std::vector<int>*> remote_arg,\n                        gsl::not_null<const double*> coupling_return)\n        : local_arg_(local_arg),\n          remote_arg_(remote_arg),\n          coupling_return_(coupling_return) {}\n\n    double operator()(const std::string& local,\n                      const std::vector<int>& remote) const {\n      *local_arg_ = local;\n      *remote_arg_ = remote;\n      return *coupling_return_;\n    }\n\n    gsl::not_null<std::string*> local_arg_;\n    gsl::not_null<std::vector<int>*> remote_arg_;\n    gsl::not_null<const double*> coupling_return_;\n  };\n  const NonCopyableCoupling unowned_coupling{&local_arg, &remote_arg,\n                                             &coupling_return};\n\n  // Test correct handling of lvalues and rvalues.\n  struct CouplingChooser {  // NOLINT(cppcoreguidelines-pro-type-member-init)\n    // clang-tidy complaint is bogus.  It's not possible to fail to\n    // initialize a gsl::not_null.\n    auto operator()(std::true_type /*unused*/) const {\n      // Capture pointers by value to try to ensure that the lambda\n      // object has actual member variables that will go out of scope\n      // if the lambda is destroyed.  I'm not sure that references\n      // could not be resolved at compile time, but these are harder\n      // to optimize out.\n      return [local_arg_capture = local_arg_, remote_arg_capture = remote_arg_,\n              coupling_return_capture = coupling_return_](\n                 const std::string& local, const std::vector<int>& remote) {\n        *local_arg_capture = local;\n        *remote_arg_capture = remote;\n        return *coupling_return_capture;\n      };\n    };\n    const auto& operator()(std::false_type /*unused*/) const {\n      return *unowned_coupling_;\n    }\n\n    gsl::not_null<std::string*> local_arg_;\n    gsl::not_null<std::vector<int>*> remote_arg_;\n    gsl::not_null<const double*> coupling_return_;\n    gsl::not_null<const NonCopyableCoupling*> unowned_coupling_;\n  };\n  const CouplingChooser coupling_chooser{&local_arg, &remote_arg,\n                                         &coupling_return, &unowned_coupling};\n\n  const auto evaluator = hist.evaluator(\n      coupling_chooser(std::bool_constant<EvaluatorOwnsCoupling>{}));\n\n  size_t coupling_calls = 0;\n  coupling_return = 3.5;\n  const double result = evaluator(hist.local().front(), hist.remote().front());\n  if (local_arg.empty()) {\n    CHECK(remote_arg.empty());\n    // In one case this should use a cached result generated from the\n    // call below this one in a previous call to this function.\n    CHECK((result == 3.5 or result == 6.5));\n  } else {\n    ++coupling_calls;\n    CHECK(local_arg == hist.local().data(0));\n    CHECK(remote_arg == hist.remote().data(0));\n    CHECK(result == 3.5);\n  }\n  local_arg.clear();\n  remote_arg.clear();\n\n  coupling_return = 6.5;\n  CHECK(6.5 == evaluator(hist.local()[3], hist.remote()[2]));\n  if (local_arg.empty()) {\n    CHECK(remote_arg.empty());\n  } else {\n    ++coupling_calls;\n    CHECK(local_arg == hist.local().data(3));\n    CHECK(remote_arg == hist.remote().data(2));\n  }\n\n  return coupling_calls;\n}\n\ntemplate <bool EvaluatorOwnsCoupling>\nvoid test_boundary_history() {\n  BoundaryHistoryType history{};\n  const BoundaryHistoryType& const_history = history;\n\n  CHECK(const_history.local().empty());\n  CHECK(const_history.remote().empty());\n\n  {\n    INFO(\"Local state\");\n    history.local().insert(make_time_id(0.), 10, get_output(0));\n    history.local().insert(make_time_id(1.), 11, get_output(1));\n    history.local().insert_initial(make_time_id(-1.), 9, get_output(-1));\n    history.local().insert(make_time_id(2.), 12, get_output(2));\n\n    check_local<true>(const_history.local());\n    check_local<false, TimeSteppers::ConstBoundaryHistoryTimes>(\n        const_history.local());\n    check_local<true>(history.local());\n    check_local<false, TimeSteppers::MutableBoundaryHistoryTimes>(\n        history.local());\n    check_local<false, TimeSteppers::ConstBoundaryHistoryTimes>(\n        history.local());\n  }\n\n  {\n    INFO(\"Remote state\");\n    history.remote().insert(make_time_id(1.), 21, std::vector{1});\n    history.remote().insert_initial(make_time_id(0.), 20, std::vector{0});\n    history.remote().insert_initial(make_time_id(-1.), 19, std::vector{-1});\n    history.remote().insert(make_time_id(2.), 22, std::vector{2});\n    history.remote().insert_initial(make_time_id(-2.), 18, std::vector{-2});\n    history.remote().insert(make_time_id(3.), 23, std::vector{3});\n\n    check_remote<true>(const_history.remote());\n    check_remote<false, TimeSteppers::ConstBoundaryHistoryTimes>(\n        const_history.remote());\n    check_remote<true>(history.remote());\n    check_remote<false, TimeSteppers::MutableBoundaryHistoryTimes>(\n        history.remote());\n    check_remote<false, TimeSteppers::ConstBoundaryHistoryTimes>(\n        history.remote());\n  }\n\n  {\n    INFO(\"Caching\");\n    CHECK(check_coupling_evaluation<EvaluatorOwnsCoupling>(const_history) == 2);\n    CHECK(check_coupling_evaluation<EvaluatorOwnsCoupling>(const_history) == 0);\n    history.clear_coupling_cache();\n    CHECK(check_coupling_evaluation<EvaluatorOwnsCoupling>(const_history) == 2);\n  }\n\n  {\n    INFO(\"Serialization\");\n    const auto copy = serialize_and_deserialize(history);\n    check_local<true>(copy.local());\n    check_remote<true>(copy.remote());\n    CHECK(check_coupling_evaluation<EvaluatorOwnsCoupling>(copy) == 0);\n  }\n\n  {\n    INFO(\"Streaming\");\n    const std::string expected_output_without_data =\n        \"Local Data:\\n\"\n        \" Time: 0:Slab[-1,-0.5]:0/1:0:-1 (order 9)\\n\"\n        \" Time: 0:Slab[0,0.5]:0/1:0:0 (order 10)\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:0:1 (order 11)\\n\"\n        \" Time: 0:Slab[2,2.5]:0/1:0:2 (order 12)\\n\"\n        \"Remote Data:\\n\"\n        \" Time: 0:Slab[-2,-1.5]:0/1:0:-2 (order 18)\\n\"\n        \" Time: 0:Slab[-1,-0.5]:0/1:0:-1 (order 19)\\n\"\n        \" Time: 0:Slab[0,0.5]:0/1:0:0 (order 20)\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:0:1 (order 21)\\n\"\n        \" Time: 0:Slab[2,2.5]:0/1:0:2 (order 22)\\n\"\n        \" Time: 0:Slab[3,3.5]:0/1:0:3 (order 23)\\n\";\n    const std::string expected_output_with_data =\n        \"Local Data:\\n\"\n        \" Time: 0:Slab[-1,-0.5]:0/1:0:-1 (order 9)\\n\"\n        \"  Data: -1\\n\"\n        \" Time: 0:Slab[0,0.5]:0/1:0:0 (order 10)\\n\"\n        \"  Data: 0\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:0:1 (order 11)\\n\"\n        \"  Data: 1\\n\"\n        \" Time: 0:Slab[2,2.5]:0/1:0:2 (order 12)\\n\"\n        \"  Data: 2\\n\"\n        \"Remote Data:\\n\"\n        \" Time: 0:Slab[-2,-1.5]:0/1:0:-2 (order 18)\\n\"\n        \"  Data: (-2)\\n\"\n        \" Time: 0:Slab[-1,-0.5]:0/1:0:-1 (order 19)\\n\"\n        \"  Data: (-1)\\n\"\n        \" Time: 0:Slab[0,0.5]:0/1:0:0 (order 20)\\n\"\n        \"  Data: (0)\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:0:1 (order 21)\\n\"\n        \"  Data: (1)\\n\"\n        \" Time: 0:Slab[2,2.5]:0/1:0:2 (order 22)\\n\"\n        \"  Data: (2)\\n\"\n        \" Time: 0:Slab[3,3.5]:0/1:0:3 (order 23)\\n\"\n        \"  Data: (3)\\n\";\n    CHECK(get_output(history) == expected_output_with_data);\n    {\n      std::ostringstream ss{};\n      CHECK(&history.print<true>(ss) == &ss);\n      CHECK(ss.str() == expected_output_with_data);\n    }\n    {\n      std::ostringstream ss{};\n      CHECK(&history.print<false>(ss) == &ss);\n      CHECK(ss.str() == expected_output_without_data);\n    }\n  }\n\n  {\n    INFO(\"pop_front()\");\n    history.local().pop_front();\n    CHECK(history.local().size() == 3);\n    CHECK(history.remote().size() == 6);\n    static_cast<const TimeSteppers::MutableBoundaryHistoryTimes&>(\n        history.local())\n        .pop_front();\n    CHECK(history.local().size() == 2);\n    CHECK(history.remote().size() == 6);\n    history.remote().pop_front();\n    CHECK(history.local().size() == 2);\n    CHECK(history.remote().size() == 5);\n    static_cast<const TimeSteppers::MutableBoundaryHistoryTimes&>(\n        history.remote())\n        .pop_front();\n    CHECK(history.local().size() == 2);\n    CHECK(history.remote().size() == 4);\n  }\n\n  history.local().pop_front();\n  history.local().insert(make_time_id(3.), 13, get_output(3));\n  history.local().insert(make_time_id(4.), 14, get_output(4));\n  history.local().insert(make_time_id(5.), 15, get_output(5));\n  REQUIRE(history.local().size() == 4);\n  REQUIRE(history.remote().size() == 4);\n\n  {\n    INFO(\"Caching across steps\");\n    // check_coupling_evaluation tests (0, 0) and (3, 2).  We have\n    // cleverly adjusted the old (3, 2) to be (0, 0).\n    CHECK(check_coupling_evaluation<EvaluatorOwnsCoupling>(const_history) == 1);\n  }\n\n  {\n    INFO(\"Data mutation\");\n    history.local().data(1) = \"changed local 1\";\n    history.local().data(history.local()[2]) = \"changed local 2\";\n    history.remote().data(1) = std::vector{1000};\n    history.remote().data(history.remote()[2]) = std::vector{2000};\n    CHECK(const_history.local().data(0) == get_output(2));\n    CHECK(const_history.local().data(1) == \"changed local 1\");\n    CHECK(const_history.local().data(2) == \"changed local 2\");\n    CHECK(const_history.local().data(3) == get_output(5));\n    CHECK(const_history.remote().data(0) == std::vector{0});\n    CHECK(const_history.remote().data(1) == std::vector{1000});\n    CHECK(const_history.remote().data(2) == std::vector{2000});\n    CHECK(const_history.remote().data(3) == std::vector{3});\n  }\n\n  {\n    INFO(\"clear()\");\n    static_cast<const TimeSteppers::MutableBoundaryHistoryTimes&>(\n        history.local())\n        .clear();\n    CHECK(const_history.local().empty());\n    CHECK(const_history.remote().size() == 4);\n\n    history.local().insert(make_time_id(5.), 15, get_output(5));\n\n    static_cast<const TimeSteppers::MutableBoundaryHistoryTimes&>(\n        history.remote())\n        .clear();\n    CHECK(const_history.local().size() == 1);\n    CHECK(const_history.remote().empty());\n  }\n}\n\n// The template parameter has no particular meaning, it just swaps the\n// fairly arbitrary choices of which side to operator on in each\n// place.\ntemplate <bool SwapSides>\nvoid test_substeps() {\n  struct Coupling {\n    double operator()(double a, double b) const {\n      if (SwapSides) {\n        std::swap(a, b);\n      }\n      call.emplace(a, b);\n      return a;\n    }\n\n    // NOLINTNEXTLINE(spectre-mutable)\n    mutable std::optional<std::pair<double, double>> call{};\n  };\n\n  Coupling coupling{};\n  TimeSteppers::BoundaryHistory<double, double, double> history{};\n  auto [side1, side2] = [&history]() {\n    if constexpr (SwapSides) {\n      return std::pair{history.remote(), history.local()};\n    } else {\n      return std::pair{history.local(), history.remote()};\n    }\n  }();\n\n  const auto real_evaluator = history.evaluator(std::as_const(coupling));\n  const auto evaluator = [&real_evaluator](const TimeStepId& a,\n                                           const TimeStepId& b) {\n    if (SwapSides) {\n      return real_evaluator(b, a);\n    } else {\n      return real_evaluator(a, b);\n    }\n  };\n\n  side1.insert(make_time_id(0.0), 5, 0.0);\n  side1.insert(make_time_id(0.0, 1), 5, 0.5);\n  side1.insert(make_time_id(1.0), 5, 1.0);\n\n  CHECK(side1.size() == 2);\n  CHECK(side1[0] == make_time_id(0.0));\n  CHECK(side1[{0, 0}] == make_time_id(0.0));\n  CHECK(side1[{0, 1}] == make_time_id(0.0, 1));\n  CHECK(side1.number_of_substeps(0) == 2);\n  CHECK(side1.number_of_substeps(1) == 1);\n  CHECK(side1.number_of_substeps(make_time_id(0.0)) == 2);\n  CHECK(side1.number_of_substeps(make_time_id(0.0, 1)) == 2);\n  CHECK(side1.integration_order(make_time_id(0.0)) == 5);\n  CHECK(side1.integration_order(make_time_id(0.0, 1)) == 5);\n  CHECK(side1.data(make_time_id(0.0, 1)) == 0.5);\n\n  side1.insert(make_time_id(1.0, 1), 5, 1.5);\n\n  side2.insert(make_time_id(0.0), 5, 0.0);\n  side2.insert(make_time_id(0.0, 1), 5, 0.5);\n  side2.insert(make_time_id(1.0), 5, 1.0);\n  side2.insert(make_time_id(1.0, 1), 5, 1.5);\n\n  evaluator(make_time_id(0.0), make_time_id(0.0));\n  CHECK(coupling.call == std::pair{0.0, 0.0});\n  coupling.call.reset();\n  evaluator(make_time_id(0.0), make_time_id(1.0, 1));\n  CHECK(coupling.call == std::pair{0.0, 1.5});\n  coupling.call.reset();\n  evaluator(make_time_id(0.0, 1), make_time_id(1.0, 1));\n  CHECK(coupling.call == std::pair{0.5, 1.5});\n  coupling.call.reset();\n  evaluator(make_time_id(1.0, 0), make_time_id(1.0, 1));\n  CHECK(coupling.call == std::pair{1.0, 1.5});\n  coupling.call.reset();\n  // Repeat\n  evaluator(make_time_id(1.0, 0), make_time_id(1.0, 1));\n  CHECK(not coupling.call.has_value());\n  coupling.call.reset();\n\n  side1.pop_front();\n\n  {\n    const std::string expected_1_without_data =\n        \" Time: 0:Slab[1,1.5]:0/1:0:1 (order 5)\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:1:1.25\\n\";\n    const std::string expected_2_without_data =\n        \" Time: 0:Slab[0,0.5]:0/1:0:0 (order 5)\\n\"\n        \" Time: 0:Slab[0,0.5]:0/1:1:0.25\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:0:1 (order 5)\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:1:1.25\\n\";\n    const std::string expected_1_with_data =\n        \" Time: 0:Slab[1,1.5]:0/1:0:1 (order 5)\\n\"\n        \"  Data: 1\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:1:1.25\\n\"\n        \"  Data: 1.5\\n\";\n    const std::string expected_2_with_data =\n        \" Time: 0:Slab[0,0.5]:0/1:0:0 (order 5)\\n\"\n        \"  Data: 0\\n\"\n        \" Time: 0:Slab[0,0.5]:0/1:1:0.25\\n\"\n        \"  Data: 0.5\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:0:1 (order 5)\\n\"\n        \"  Data: 1\\n\"\n        \" Time: 0:Slab[1,1.5]:0/1:1:1.25\\n\"\n        \"  Data: 1.5\\n\";\n    const std::string expected_without_data =\n        \"Local Data:\\n\" +\n        (SwapSides ? expected_2_without_data : expected_1_without_data) +\n        \"Remote Data:\\n\" +\n        (SwapSides ? expected_1_without_data : expected_2_without_data);\n    const std::string expected_with_data =\n        \"Local Data:\\n\" +\n        (SwapSides ? expected_2_with_data : expected_1_with_data) +\n        \"Remote Data:\\n\" +\n        (SwapSides ? expected_1_with_data : expected_2_with_data);\n\n    CHECK(get_output(history) == expected_with_data);\n    {\n      std::ostringstream ss{};\n      CHECK(&history.print<true>(ss) == &ss);\n      CHECK(ss.str() == expected_with_data);\n    }\n    {\n      std::ostringstream ss{};\n      CHECK(&history.print<false>(ss) == &ss);\n      CHECK(ss.str() == expected_without_data);\n    }\n  }\n\n  side1.insert(make_time_id(2.0), 5, 2.0);\n  side1.insert(make_time_id(2.0, 1), 5, 2.5);\n\n  evaluator(make_time_id(2.0, 0), make_time_id(1.0));\n  CHECK(coupling.call == std::pair{2.0, 1.0});\n  coupling.call.reset();\n  evaluator(make_time_id(2.0, 0), make_time_id(1.0, 1));\n  CHECK(coupling.call == std::pair{2.0, 1.5});\n  coupling.call.reset();\n  evaluator(make_time_id(2.0, 1), make_time_id(1.0, 1));\n  CHECK(coupling.call == std::pair{2.5, 1.5});\n  coupling.call.reset();\n\n  side2.clear_substeps(1);\n}\n\ntemplate <bool Local, bool Modified>\nstruct ReferenceFunctor {\n  using ExpectedData =\n      tmpl::conditional_t<Local, std::string, std::vector<int>>;\n  std::unordered_set<TimeStepId> ids{};\n  std::unordered_set<ExpectedData, boost::hash<ExpectedData>> entries{};\n\n  // Templated to test that the correct types are passed.\n  template <typename Id, typename Data>\n  void operator()(Id& id, Data& data) {\n    static_assert(std::is_same_v<Id, const TimeStepId>);\n    static_assert(std::is_same_v<Data, const ExpectedData>);\n\n    CHECK(ids.insert(id).second);\n    CHECK(entries.insert(data).second);\n    if constexpr (Local) {\n      CHECK(static_cast<bool>(std::islower(data[0])) == Modified);\n    } else {\n      CHECK((data[0] < 0) == Modified);\n    }\n  }\n};\n\ntemplate <bool Local, bool Modified, bool Modify>\nstruct NotNullFunctor {\n  using ExpectedData =\n      tmpl::conditional_t<Local, std::string, std::vector<int>>;\n  std::unordered_set<TimeStepId> ids{};\n  std::unordered_set<ExpectedData, boost::hash<ExpectedData>> entries{};\n\n  // Templated to test that the correct types are passed.\n  template <typename Id, typename Data>\n  bool operator()(Id& id, const gsl::not_null<Data*> data) {\n    static_assert(std::is_same_v<Id, const TimeStepId>);\n    static_assert(std::is_same_v<Data, ExpectedData>);\n\n    CHECK(ids.insert(id).second);\n    CHECK(entries.insert(*data).second);\n    if constexpr (Local) {\n      CHECK(static_cast<bool>(std::islower((*data)[0])) == Modified);\n      if constexpr (Modify) {\n        (*data)[0] =\n            Modified ? std::toupper((*data)[0]) : std::tolower((*data)[0]);\n      }\n    } else {\n      CHECK(((*data)[0] < 0) == Modified);\n      if constexpr (Modify) {\n        (*data)[0] *= -1;\n      }\n    }\n    return Modify;\n  }\n};\n\ntemplate <bool Local, bool Modified, typename Times>\nvoid check_reference(const Times& times, const size_t expected_size) {\n  ReferenceFunctor<Local, Modified> func{};\n  times.for_each(func);\n  CHECK(func.ids.size() == expected_size);\n}\n\ntemplate <bool Local, bool Modified, bool Modify, typename Times>\nvoid check_not_null(const Times& times, const size_t expected_size) {\n  NotNullFunctor<Local, Modified, Modify> func{};\n  times.for_each(func);\n  CHECK(func.ids.size() == expected_size);\n}\n\ntemplate <typename LocalData, typename RemoteData, typename CouplingResult>\nbool cache_filled(\n    const TimeSteppers::BoundaryHistory<LocalData, RemoteData, CouplingResult>&\n        history) {\n  bool called = false;\n  history.evaluator(\n      [&called](const LocalData& /*unused*/, const RemoteData& /*unused*/) {\n        called = true;\n        return CouplingResult{};\n      })(history.local().front(), history.remote().front());\n  return not called;\n}\n\nvoid test_for_each() {\n  INFO(\"for_each\");\n\n  BoundaryHistoryType history{};\n  const BoundaryHistoryType& const_history = history;\n\n  history.local().insert(make_time_id(0.), 1, \"A\");\n  history.local().insert(make_time_id(1.), 1, \"B\");\n  history.local().insert(make_time_id(2.), 1, \"C\");\n  history.local().insert(make_time_id(2., 1), 1, \"D\");\n  const size_t local_size = 4;\n\n  history.remote().insert(make_time_id(0.), 1, std::vector{1});\n  history.remote().insert(make_time_id(1.), 1, std::vector{2});\n  history.remote().insert(make_time_id(2.), 1, std::vector{3});\n  history.remote().insert(make_time_id(3.), 1, std::vector{4});\n  history.remote().insert(make_time_id(3., 1), 1, std::vector{5});\n  const size_t remote_size = 5;\n\n  // The second template parameter indicates whether the data is\n  // expected to have been modified from its original state at that\n  // point.  Modification happens with each call to\n  // `check_not_null<..., ..., true>`.  Modifying twice gives the\n  // original data.\n  CHECK(not cache_filled(history));\n  check_reference<true, false>(const_history.local(), local_size);\n  CHECK(cache_filled(history));\n  check_reference<true, false>(history.local(), local_size);\n  CHECK(cache_filled(history));\n  check_not_null<true, false, false>(history.local(), local_size);\n  CHECK(cache_filled(history));\n  check_not_null<true, false, true>(history.local(), local_size);\n  CHECK(not cache_filled(history));\n  check_reference<true, true>(const_history.local(), local_size);\n  CHECK(cache_filled(history));\n  check_reference<true, true>(history.local(), local_size);\n  CHECK(cache_filled(history));\n  check_not_null<true, true, false>(history.local(), local_size);\n  CHECK(cache_filled(history));\n  check_not_null<true, true, true>(history.local(), local_size);\n  CHECK(not cache_filled(history));\n  check_reference<true, false>(const_history.local(), local_size);\n  CHECK(cache_filled(history));\n\n  check_reference<false, false>(const_history.remote(), remote_size);\n  CHECK(cache_filled(history));\n  check_reference<false, false>(history.remote(), remote_size);\n  CHECK(cache_filled(history));\n  check_not_null<false, false, false>(history.remote(), remote_size);\n  CHECK(cache_filled(history));\n  check_not_null<false, false, true>(history.remote(), remote_size);\n  CHECK(not cache_filled(history));\n  check_reference<false, true>(const_history.remote(), remote_size);\n  CHECK(cache_filled(history));\n  check_reference<false, true>(history.remote(), remote_size);\n  CHECK(cache_filled(history));\n  check_not_null<false, true, false>(history.remote(), remote_size);\n  CHECK(cache_filled(history));\n  check_not_null<false, true, true>(history.remote(), remote_size);\n  CHECK(not cache_filled(history));\n  check_reference<false, false>(const_history.remote(), remote_size);\n  CHECK(cache_filled(history));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.BoundaryHistory\", \"[Unit][Time]\") {\n  test_boundary_history<true>();\n  test_boundary_history<false>();\n  test_substeps<false>();\n  test_substeps<true>();\n  test_for_each();\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_ChangeStepSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/Constant.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/FixedLtsRatio.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/LtsStepChoosers.hpp\"\n#include \"Time/Tags/MinimumTimeStep.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var : db::SimpleTag {\n  using type = double;\n};\n\nstruct Metavariables {\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<StepChoosers::Constant>>>;\n  };\n};\n\ntemplate <typename StepChoosersToUse = AllStepChoosers>\nvoid check(const bool time_runs_forward,\n           std::unique_ptr<LtsTimeStepper> time_stepper,\n           TimeSteppers::History<double> history, const Time& time,\n           const double request, const TimeDelta& expected_step) {\n  CAPTURE(time);\n  CAPTURE(request);\n\n  const TimeDelta initial_step_size = (time_runs_forward ? 1 : -1) *\n                                      time.slab().duration() /\n                                      time.fraction().denominator();\n\n  auto choosers =\n      make_vector<std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>>(\n          std::make_unique<StepChoosers::Constant>(2. * request),\n          std::make_unique<StepChoosers::Constant>(request),\n          std::make_unique<StepChoosers::Constant>(2. * request));\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Parallel::Tags::MetavariablesImpl<Metavariables>,\n          Tags::ConcreteTimeStepper<LtsTimeStepper>, Tags::MinimumTimeStep,\n          Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n          Tags::LtsStepChoosers, Tags::HistoryEvolvedVariables<Var>,\n          Tags::AdaptiveSteppingDiagnostics>,\n      db::AddComputeTags<time_stepper_ref_tags<LtsTimeStepper>>>(\n      Metavariables{}, std::move(time_stepper), 1e-8,\n      TimeStepId(time_runs_forward, 0, time, 1, initial_step_size,\n                 time.value()),\n      TimeStepId(time_runs_forward, 0, time + initial_step_size),\n      initial_step_size, std::move(choosers), std::move(history),\n      AdaptiveSteppingDiagnostics{1, 2, 3, 4, 5});\n\n  // Nothing should happen on a substep\n  db::mutate_apply<ChangeStepSize<StepChoosersToUse>>(make_not_null(&box));\n  CHECK(db::get<Tags::TimeStep>(box) == initial_step_size);\n  CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) ==\n        AdaptiveSteppingDiagnostics{1, 2, 3, 4, 5});\n\n  db::mutate<Tags::TimeStepId>(\n      [&](const gsl::not_null<TimeStepId*> id) {\n        *id = TimeStepId(time_runs_forward, 0, time);\n      },\n      make_not_null(&box));\n\n  db::mutate_apply<ChangeStepSize<StepChoosersToUse>>(make_not_null(&box));\n\n  CHECK(db::get<Tags::TimeStep>(box) == expected_step);\n  CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) ==\n        AdaptiveSteppingDiagnostics{\n            1, 2, 3,\n            db::get<Tags::TimeStep>(box) == initial_step_size ? 4_st : 5_st,\n            5});\n}\n\nvoid test_fixed_lts_ratio() {\n  std::unique_ptr<LtsTimeStepper> time_stepper =\n      std::make_unique<TimeSteppers::AdamsBashforth>(3);\n  const Slab slab(2.3, 4.5);\n  const TimeStepId initial_id(true, 0, slab.start());\n  const auto initial_step = slab.duration() / 4;\n  const auto next_id = time_stepper->next_time_id(initial_id, initial_step);\n  TimeSteppers::History<double> history(3);\n  history.insert(TimeStepId(true, -1, slab.start() + initial_step), 0.0, 0.0);\n  history.insert(TimeStepId(true, -1, slab.start() + 2 * initial_step), 0.0,\n                 0.0);\n  history.insert(initial_id, 0.0, 0.0);\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::ConcreteTimeStepper<LtsTimeStepper>,\n                        Tags::LtsStepChoosers, Tags::MinimumTimeStep,\n                        Tags::FixedLtsRatio, Tags::TimeStepId, Tags::TimeStep,\n                        Tags::Next<Tags::TimeStepId>,\n                        Tags::HistoryEvolvedVariables<Var>,\n                        Tags::AdaptiveSteppingDiagnostics>,\n      db::AddComputeTags<time_stepper_ref_tags<LtsTimeStepper>>>(\n      Metavariables{}, std::move(time_stepper), Tags::LtsStepChoosers::type{},\n      1e-10, std::optional<size_t>(8), initial_id, initial_step, next_id,\n      std::move(history), AdaptiveSteppingDiagnostics{1, 2, 3, 4, 5});\n\n  db::mutate_apply<ChangeStepSize<>>(make_not_null(&box));\n  // Step size change forbidden after self-start\n  CHECK(db::get<Tags::TimeStep>(box) == initial_step);\n  CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) ==\n        AdaptiveSteppingDiagnostics{1, 2, 3, 4, 5});\n\n  db::mutate<Tags::HistoryEvolvedVariables<Var>>(\n      [&](const gsl::not_null<TimeSteppers::History<double>*> local_history) {\n        const auto old_step = initial_step.with_slab(slab.retreat());\n        local_history->clear();\n        local_history->insert(TimeStepId(true, -1, slab.start() - 2 * old_step),\n                              0.0, 0.0);\n        local_history->insert(TimeStepId(true, -1, slab.start() - old_step),\n                              0.0, 0.0);\n        local_history->insert(initial_id, 0.0, 0.0);\n      },\n      make_not_null(&box));\n\n  db::mutate_apply<ChangeStepSize<>>(make_not_null(&box));\n  CHECK(db::get<Tags::TimeStep>(box).fraction() == Rational(1, 8));\n  CHECK(db::get<Tags::AdaptiveSteppingDiagnostics>(box) ==\n        AdaptiveSteppingDiagnostics{1, 2, 3, 5, 5});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Actions.ChangeStepSize\", \"[Unit][Time][Actions]\") {\n  register_classes_with_charm<TimeSteppers::AdamsBashforth>();\n  register_factory_classes_with_charm<Metavariables>();\n  const Slab slab(-5., -2.);\n  const double slab_length = slab.duration().value();\n  check(true, std::make_unique<TimeSteppers::AdamsBashforth>(1), {},\n        slab.start() + slab.duration() / 4, slab_length / 5.,\n        slab.duration() / 8);\n  check(true, std::make_unique<TimeSteppers::AdamsBashforth>(1), {},\n        slab.start() + slab.duration() / 4, slab_length, slab.duration() / 4);\n  check(false, std::make_unique<TimeSteppers::AdamsBashforth>(1), {},\n        slab.end() - slab.duration() / 4, slab_length / 5.,\n        -slab.duration() / 8);\n  check(false, std::make_unique<TimeSteppers::AdamsBashforth>(1), {},\n        slab.end() - slab.duration() / 4, slab_length, -slab.duration() / 4);\n\n  // Check for roundoff issues\n  check(true, std::make_unique<TimeSteppers::AdamsBashforth>(1), {},\n        slab.start() + slab.duration() / 4,\n        slab_length / 16. / (1.0 + std::numeric_limits<double>::epsilon()),\n        slab.duration() / 32);\n  check(false, std::make_unique<TimeSteppers::AdamsBashforth>(1), {},\n        slab.end() - slab.duration() / 4,\n        slab_length / 16. / (1.0 + std::numeric_limits<double>::epsilon()),\n        -slab.duration() / 32);\n\n  {\n    // History out of order, as if just after self-start.\n    TimeSteppers::History<double> history{};\n    history.insert(TimeStepId(true, -1, slab.start() + slab.duration() / 8),\n                   0.0, 0.0);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    check(true, std::make_unique<TimeSteppers::AdamsBashforth>(3),\n          std::move(history), slab.start(), 1.0e-3, slab.duration());\n  }\n\n  CHECK_THROWS_WITH(\n      check(true, std::make_unique<TimeSteppers::AdamsBashforth>(1), {},\n            slab.start() + slab.duration() / 4, 1e-9, slab.duration() / 4),\n      Catch::Matchers::ContainsSubstring(\"smaller than the MinimumTimeStep\"));\n\n  test_fixed_lts_ratio();\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_ChangeTimeStepperOrder.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <iomanip>\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"DataStructures/DataBox/PrefixHelpers.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"DataStructures/VariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Helpers/DataStructures/TestTags.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"Parallel/Printf/Printf.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/EventsAndTriggers.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Tags.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/WhenToCheck.hpp\"\n#include \"Time/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/ChangeStepSize.hpp\"\n#include \"Time/ChangeTimeStepperOrder.hpp\"\n#include \"Time/ChangeTimeStepperOrder.tpp\"\n#include \"Time/ChooseLtsStepSize.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepChoosers/ErrorControl.hpp\"\n#include \"Time/StepChoosers/StepChooser.hpp\"\n#include \"Time/StepperErrorEstimate.hpp\"\n#include \"Time/Tags/AdaptiveSteppingDiagnostics.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/LtsStepChoosers.hpp\"\n#include \"Time/Tags/MinimumTimeStep.hpp\"\n#include \"Time/Tags/StepperErrorTolerancesCompute.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Tags/VariableOrderAlgorithm.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/AdamsMoultonPc.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Time/VariableOrderAlgorithm.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nusing Vars1 = Tags::Variables<tmpl::list<TestHelpers::Tags::Vector<>>>;\nusing Vars2 = Tags::Variables<tmpl::list<TestHelpers::Tags::Scalar<>>>;\n\nstruct System1 {\n  using variables_tag = Vars1;\n  static constexpr bool two_vars = false;\n};\n\nstruct System2 {\n  using variables_tag = tmpl::list<Vars1, Vars2>;\n  static constexpr bool two_vars = true;\n};\n\ntemplate <typename System>\nsize_t test_system(VariableOrderAlgorithm algorithm, const size_t initial_order,\n                   std::optional<StepperErrorEstimate> errors1,\n                   std::optional<StepperErrorEstimate> errors2) {\n  const Slab slab(0.0, 1.0);\n\n  // First entry is for the previous step, which is only used for step\n  // size control, not order.\n  std::array<std::optional<StepperErrorEstimate>, 2> stepper_errors1{};\n  std::array<std::optional<StepperErrorEstimate>, 2> stepper_errors2{};\n  stepper_errors1[1] = std::move(errors1);\n  stepper_errors2[1] = std::move(errors2);\n\n  using history_tag1 = Tags::HistoryEvolvedVariables<Vars1>;\n  using history_tag2 = Tags::HistoryEvolvedVariables<Vars2>;\n\n  const TimeStepId next_time_step_id(true, 0, slab.end());\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Tags::ConcreteTimeStepper<TimeStepper>, Tags::VariableOrderAlgorithm,\n          history_tag1, history_tag2, Tags::StepperErrors<Vars1>,\n          Tags::StepperErrors<Vars2>, Tags::Next<Tags::TimeStepId>>,\n      time_stepper_ref_tags<TimeStepper>>(\n      static_cast<std::unique_ptr<TimeStepper>>(\n          std::make_unique<TimeSteppers::AdamsBashforth>(initial_order)),\n      std::move(algorithm), history_tag1::type{initial_order},\n      history_tag2::type{initial_order}, stepper_errors1, stepper_errors2,\n      next_time_step_id);\n\n  // Does nothing for fixed-order\n  db::mutate_apply<ChangeTimeStepperOrder<System>>(make_not_null(&box));\n  CHECK(db::get<history_tag1>(box).integration_order() == initial_order);\n  CHECK(db::get<history_tag2>(box).integration_order() == initial_order);\n\n  db::mutate<Tags::ConcreteTimeStepper<TimeStepper>,\n             Tags::Next<Tags::TimeStepId>>(\n      [&](const gsl::not_null<std::unique_ptr<TimeStepper>*> stepper,\n          const gsl::not_null<TimeStepId*> id) {\n        *stepper = make_unique<TimeSteppers::AdamsBashforth>(std::nullopt);\n        *id = TimeStepId(true, 0, slab.start(), 1, slab.duration(), 1.0);\n      },\n      make_not_null(&box));\n\n  // Does nothing on a substep\n  db::mutate_apply<ChangeTimeStepperOrder<System>>(make_not_null(&box));\n  CHECK(db::get<history_tag1>(box).integration_order() == initial_order);\n  CHECK(db::get<history_tag2>(box).integration_order() == initial_order);\n\n  db::mutate<Tags::Next<Tags::TimeStepId>>(\n      [&](const gsl::not_null<TimeStepId*> id) {\n        *id = TimeStepId(true, 0, slab.end());\n      },\n      make_not_null(&box));\n  db::mutate_apply<ChangeTimeStepperOrder<System>>(make_not_null(&box));\n\n  if constexpr (System::two_vars) {\n    CHECK(db::get<history_tag2>(box).integration_order() ==\n          db::get<history_tag1>(box).integration_order());\n  } else {\n    CHECK(db::get<history_tag2>(box).integration_order() == initial_order);\n  }\n  return db::get<history_tag1>(box).integration_order();\n}\n\nsize_t goal_order(const size_t goal, const size_t initial_order) {\n  const VariableOrderAlgorithm goal_algorithm{goal};\n  const auto algorithm_from_options =\n      TestHelpers::test_creation<VariableOrderAlgorithm>(\n          MakeString{} << \"GoalOrder: \" << goal);\n  CHECK(algorithm_from_options == goal_algorithm);\n  CHECK(goal_algorithm.required_estimates() ==\n        StepperErrorTolerances::Estimates::None);\n\n  const size_t result = test_system<System1>(goal_algorithm, initial_order,\n                                             std::nullopt, std::nullopt);\n  CHECK(test_system<System2>(goal_algorithm, initial_order, std::nullopt,\n                             std::nullopt) == result);\n  CHECK(test_system<System1>(algorithm_from_options, initial_order,\n                             std::nullopt, std::nullopt) == result);\n  CHECK(test_system<System2>(algorithm_from_options, initial_order,\n                             std::nullopt, std::nullopt) == result);\n  return result;\n}\n\nvoid test_goal() {\n  CHECK(goal_order(4, 1) == 2);\n  CHECK(goal_order(4, 2) == 3);\n  CHECK(goal_order(4, 3) == 4);\n  CHECK(goal_order(4, 4) == 4);\n  CHECK(goal_order(4, 5) == 4);\n  CHECK(goal_order(4, 6) == 5);\n  CHECK(goal_order(1, 1) == 1);\n}\n\nsize_t falloff_order(const double falloff, const std::vector<double>& errors) {\n  const VariableOrderAlgorithm falloff_algorithm{falloff};\n  const auto algorithm_from_options =\n      TestHelpers::test_creation<VariableOrderAlgorithm>(\n          MakeString{} << std::setprecision(18) << \"OrderFalloff: \" << falloff);\n  CHECK(algorithm_from_options == falloff_algorithm);\n  CHECK(falloff_algorithm.required_estimates() ==\n        StepperErrorTolerances::Estimates::AllOrders);\n\n  const Slab slab(0.0, 1.0);\n  // Time and step size are unused.\n  StepperErrorEstimate falloff_errors(slab.start(), slab.duration(),\n                                      errors.size() - 1, errors.back());\n  for (size_t i = 0; i < errors.size() - 1; ++i) {\n    gsl::at(falloff_errors.errors, i).emplace(errors[i]);\n  }\n\n  const double min_error = *alg::min_element(errors);\n  StepperErrorEstimate constant_errors(slab.start(), slab.duration(),\n                                       errors.size() - 1, min_error);\n  for (size_t i = 0; i < errors.size() - 1; ++i) {\n    gsl::at(constant_errors.errors, i).emplace(min_error);\n  }\n\n  const size_t result = test_system<System1>(falloff_algorithm, errors.size(),\n                                             falloff_errors, std::nullopt);\n  CHECK(test_system<System2>(falloff_algorithm, errors.size(), falloff_errors,\n                             falloff_errors) == result);\n  CHECK(test_system<System2>(falloff_algorithm, errors.size(), falloff_errors,\n                             constant_errors) == result);\n  CHECK(test_system<System2>(falloff_algorithm, errors.size(), constant_errors,\n                             falloff_errors) == result);\n  CHECK(test_system<System2>(falloff_algorithm, errors.size(), falloff_errors,\n                             std::nullopt) == result);\n  CHECK(test_system<System2>(falloff_algorithm, errors.size(), std::nullopt,\n                             falloff_errors) == result);\n  return result;\n}\n\nvoid test_falloff_without_errors() {\n  const size_t initial_order = 4;\n  const Slab slab(0.0, 1.0);\n\n  std::array<std::optional<StepperErrorEstimate>, 2> stepper_errors{};\n\n  using history_tag = Tags::HistoryEvolvedVariables<Vars1>;\n\n  const TimeStepId next_time_step_id(true, 0, slab.end());\n\n  auto box =\n      db::create<db::AddSimpleTags<Tags::ConcreteTimeStepper<TimeStepper>,\n                                   Tags::VariableOrderAlgorithm, history_tag,\n                                   Tags::StepperErrors<Vars1>,\n                                   Tags::Next<Tags::TimeStepId>>,\n                 time_stepper_ref_tags<TimeStepper>>(\n          static_cast<std::unique_ptr<TimeStepper>>(\n              std::make_unique<TimeSteppers::AdamsBashforth>(initial_order)),\n          VariableOrderAlgorithm{0.1}, history_tag::type{initial_order},\n          stepper_errors, next_time_step_id);\n\n  // Does nothing for fixed-order\n  db::mutate_apply<ChangeTimeStepperOrder<System1>>(make_not_null(&box));\n  CHECK(db::get<history_tag>(box).integration_order() == initial_order);\n\n  db::mutate<Tags::ConcreteTimeStepper<TimeStepper>>(\n      [&](const gsl::not_null<std::unique_ptr<TimeStepper>*> stepper) {\n        *stepper = make_unique<TimeSteppers::AdamsBashforth>(std::nullopt);\n      },\n      make_not_null(&box));\n\n  CHECK_THROWS_WITH(\n      db::mutate_apply<ChangeTimeStepperOrder<System1>>(make_not_null(&box)),\n      Catch::Matchers::ContainsSubstring(\n          \"OrderFalloff only implemented with error-based adaptive time \"\n          \"stepping.\"));\n}\n\nvoid test_falloff() {\n  // Euler's method should always increase to second order\n  CHECK(falloff_order(0.1, {3.0}) == 2);\n\n  // The algorithm can't decrease from second to first order, and will\n  // only stay the same if second is actually worse.\n  CHECK(falloff_order(0.1, {0.9, 0.899}) == 3);\n  CHECK(falloff_order(0.1, {0.9, 0.901}) == 2);\n\n  // Max order can't increase\n  CHECK(falloff_order(0.1, {1.0e-1, 1.0e-2, 1.0e-3, 1.0e-4, 1.0e-5, 1.0e-6,\n                            1.0e-7, 1.0e-8}) == 8);\n\n  // Typical cases\n  // Make the largest drop a factor of 4, falloff 0.5 then requires\n  // factor of 2 decrease.\n  CHECK(falloff_order(0.5, {0.9, 0.8, 0.2, 0.15, 1.0e-5}) == 4);\n  CHECK(falloff_order(0.5, {0.9, 0.8, 0.2, 0.08, 0.041}) == 5);\n  CHECK(falloff_order(0.5, {0.9, 0.8, 0.2, 0.08, 0.039}) == 6);\n  CHECK(falloff_order(0.51, {0.9, 0.8, 0.2, 0.08, 0.04}) == 5);\n  CHECK(falloff_order(0.49, {0.9, 0.8, 0.2, 0.08, 0.04}) == 6);\n\n  test_falloff_without_errors();\n}\n\n// See Hairer II.4\n//\n// Evolves the system\n// y1' = 1 + y1^2 y2 - 4 y1\n// y2' = 3 y1 - y1^2 y2\n//\n// This is a popular test for various sorts of adaptive stepping\n// because the solution has a sawtooth-like shape, with gradual\n// changes alternating with rapid ones.\nnamespace brusselator {\nstruct Var1 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct Var2 : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nstruct System {\n  using variables_tag = ::Tags::Variables<tmpl::list<Var1, Var2>>;\n\n  struct compute_time_derivative {\n    using return_tags = tmpl::list<::Tags::dt<Var1>, ::Tags::dt<Var2>>;\n    using argument_tags = tmpl::list<Var1, Var2>;\n    static void apply(const gsl::not_null<Scalar<DataVector>*> dt_var1,\n                      const gsl::not_null<Scalar<DataVector>*> dt_var2,\n                      const Scalar<DataVector>& var1,\n                      const Scalar<DataVector>& var2) {\n      tenex::evaluate(dt_var1, 1.0 + square(var1()) * var2() - 4.0 * var1());\n      tenex::evaluate(dt_var2, 3.0 * var1() - square(var1()) * var2());\n    }\n  };\n};\n\nusing ErrorControl =\n    StepChoosers::ErrorControl<StepChooserUse::LtsStep, System::variables_tag>;\n\nstruct Metavariables {\n  using system = System;\n\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<StepChooser<StepChooserUse::LtsStep>,\n                             tmpl::list<ErrorControl>>>;\n  };\n};\n\ndouble run(std::unique_ptr<LtsTimeStepper> time_stepper, const double tolerance,\n           const double initial_step_size,\n           const std::optional<std::string>& output_file = {}) {\n  // Prevent people from forgetting to turn this off after testing.\n  CHECK(not output_file.has_value());\n\n  const Slab slab(0.0, 20.0);\n\n  const TimeStepId initial_time_step_id(true, 0, slab.start());\n  const TimeDelta initial_time_step =\n      choose_lts_step_size(slab.start(), initial_step_size);\n\n  std::vector<std::unique_ptr<StepChooser<StepChooserUse::LtsStep>>>\n      step_choosers{};\n  step_choosers.push_back(\n      std::make_unique<ErrorControl>(tolerance, tolerance, 2.0, 0.25, 0.95));\n\n  const double minimum_time_step = 1.0e-7;\n\n  System::variables_tag::type initial_vars{1};\n  get(get<Var1>(initial_vars)) = 1.5;\n  get(get<Var2>(initial_vars)) = 3.0;\n\n  const auto stepper_order = time_stepper->order();\n  REQUIRE(holds_alternative<TimeSteppers::Tags::VariableOrder>(stepper_order));\n  const size_t initial_order =\n      get<TimeSteppers::Tags::VariableOrder>(stepper_order).minimum;\n\n  using dt_variables_tag =\n      db::add_tag_prefix<::Tags::dt, System::variables_tag>;\n  using history_tag = ::Tags::HistoryEvolvedVariables<System::variables_tag>;\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          ::Parallel::Tags::MetavariablesImpl<Metavariables>,\n          ::Tags::ConcreteTimeStepper<LtsTimeStepper>,\n          ::Tags::EventsAndTriggers<Triggers::WhenToCheck::AtSlabs>,\n          Tags::VariableOrderAlgorithm, ::Tags::TimeStepId,\n          ::Tags::Next<::Tags::TimeStepId>, ::Tags::TimeStep, ::Tags::Time,\n          ::Tags::AdaptiveSteppingDiagnostics, ::Tags::LtsStepChoosers,\n          ::Tags::MinimumTimeStep, System::variables_tag, dt_variables_tag,\n          history_tag, ::Tags::StepperErrors<System::variables_tag>>,\n      tmpl::push_back<\n          time_stepper_ref_tags<LtsTimeStepper>,\n          ::Tags::StepperErrorEstimatesEnabledCompute<true>,\n          ::Tags::StepperErrorTolerancesCompute<System::variables_tag, true>>>(\n      Metavariables{}, std::move(time_stepper), EventsAndTriggers{},\n      VariableOrderAlgorithm(0.1), initial_time_step_id,\n      time_stepper->next_time_id(initial_time_step_id, initial_time_step),\n      initial_time_step, slab.start().value(), ::AdaptiveSteppingDiagnostics{},\n      std::move(step_choosers), minimum_time_step, std::move(initial_vars),\n      dt_variables_tag::type{1}, history_tag::type{initial_order},\n      ::Tags::StepperErrors<System::variables_tag>::type{});\n\n  if (output_file.has_value()) {\n    ::file_system::rm(*output_file, false);\n  }\n\n  size_t order_sum = 0;\n  size_t num_steps = 0;\n\n  for (;;) {\n    if (output_file.has_value() and\n        db::get<::Tags::TimeStepId>(box).substep() == 0) {\n      Parallel::fprintf(\n          *output_file, \"%zu\\t%.16g\\t%.16g\\t%.16g\\t%.16g\\t%zu\\n\",\n          static_cast<size_t>(db::get<::Tags::AdaptiveSteppingDiagnostics>(box)\n                                  .number_of_steps),\n          db::get<::Tags::Time>(box), get(db::get<Var1>(box))[0],\n          get(db::get<Var2>(box))[0], db::get<::Tags::TimeStep>(box).value(),\n          static_cast<size_t>(db::get<history_tag>(box).integration_order()));\n    }\n    if (db::get<::Tags::TimeStepId>(box).slab_number() != 0) {\n      break;\n    }\n\n    order_sum += db::get<history_tag>(box).integration_order();\n    ++num_steps;\n\n    db::mutate_apply<System::compute_time_derivative>(make_not_null(&box));\n    db::mutate_apply<ChangeStepSize<AllStepChoosers>>(make_not_null(&box));\n    db::mutate_apply<RecordTimeStepperData<System>>(make_not_null(&box));\n    db::mutate_apply<UpdateU<System, true>>(make_not_null(&box));\n    db::mutate_apply<ChangeTimeStepperOrder<System>>(make_not_null(&box));\n\n    db::mutate<::Tags::TimeStepId, ::Tags::Next<::Tags::TimeStepId>,\n               ::Tags::Time, ::Tags::AdaptiveSteppingDiagnostics, history_tag>(\n        [](const gsl::not_null<TimeStepId*> time_step_id,\n           const gsl::not_null<TimeStepId*> next_time_step_id,\n           const gsl::not_null<double*> time,\n           const gsl::not_null<AdaptiveSteppingDiagnostics*> diags,\n           const gsl::not_null<history_tag::type*> history,\n           const TimeStepper& local_time_stepper, const TimeDelta& time_step) {\n          if (next_time_step_id->substep() == 0) {\n            ++diags->number_of_steps;\n          }\n          *time_step_id = *next_time_step_id;\n          *next_time_step_id = local_time_stepper.next_time_id(\n              *next_time_step_id,\n              time_step.with_slab(time_step_id->step_time().slab()));\n          *time = time_step_id->substep_time();\n          local_time_stepper.clean_history(history);\n        },\n        make_not_null(&box), db::get<::Tags::TimeStepper<TimeStepper>>(box),\n        db::get<::Tags::TimeStep>(box));\n  }\n\n  return static_cast<double>(order_sum) / static_cast<double>(num_steps);\n}\n\nvoid test() {\n  const auto average_order_ab_3 =\n      run(std::make_unique<TimeSteppers::AdamsBashforth>(std::nullopt), 1.0e-3,\n          1.0e-3);\n  const auto average_order_ab_8 =\n      run(std::make_unique<TimeSteppers::AdamsBashforth>(std::nullopt), 1.0e-8,\n          1.0e-6);\n  const auto average_order_am_3 =\n      run(std::make_unique<TimeSteppers::AdamsMoultonPc<true>>(std::nullopt),\n          1.0e-3, 1.0e-3);\n  const auto average_order_am_8 =\n      run(std::make_unique<TimeSteppers::AdamsMoultonPc<true>>(std::nullopt),\n          1.0e-8, 1.0e-6);\n\n  CHECK(average_order_ab_3 < average_order_ab_8);\n  CHECK(average_order_am_3 < average_order_am_8);\n}\n}  // namespace brusselator\n\nSPECTRE_TEST_CASE(\"Unit.Time.ChangeTimeStepperOrder\", \"[Unit][Time]\") {\n  test_goal();\n  test_falloff();\n  brusselator::test();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/Test_ChooseLtsStepSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <limits>\n\n#include \"Time/ChooseLtsStepSize.hpp\"\n#include \"Time/Slab.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.ChooseLtsStepSize\", \"[Unit][Time]\") {\n  const Slab slab(1., 4.);\n  CHECK(choose_lts_step_size(slab.start(), 4.) == slab.duration());\n  CHECK(choose_lts_step_size(slab.start(), 10.) == slab.duration());\n  CHECK(choose_lts_step_size(slab.start(), 2.) == slab.duration() / 2);\n  CHECK(choose_lts_step_size(slab.start(), 1.4) == slab.duration() / 4);\n  CHECK(choose_lts_step_size(slab.start() + slab.duration() / 4, 2.) ==\n        slab.duration() / 4);\n\n  CHECK(choose_lts_step_size(slab.end(), -2.) == -slab.duration() / 2);\n\n  CHECK(choose_lts_step_size(slab.start(),\n                             std::numeric_limits<double>::infinity()) ==\n        slab.duration());\n\n  CHECK(choose_lts_step_size(slab.start(),\n                             1.1 * slab.duration().value() / (1 << 30)) ==\n        slab.duration() / (1 << 30));\n  CHECK_THROWS_WITH(\n      choose_lts_step_size(slab.start(),\n                           0.9 * slab.duration().value() / (1 << 30)),\n      Catch::Matchers::ContainsSubstring(\"integer overflow\"));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      choose_lts_step_size(slab.start() + slab.duration() / 3, 1.),\n      Catch::Matchers::ContainsSubstring(\n          \"Not at a binary-fraction time within slab\"));\n#endif  // SPECTRE_DEBUG\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_CleanHistory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/CleanHistory.hpp\"\n#include \"Time/CleanHistory.tpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var : db::SimpleTag {\n  using type = double;\n};\n\nstruct AlternativeVar : db::SimpleTag {\n  using type = double;\n};\n\nstruct SingleVariableSystem {\n  using variables_tag = Var;\n};\n\nstruct TwoVariableSystem {\n  using variables_tag = tmpl::list<Var, AlternativeVar>;\n};\n\ntemplate <bool TwoVars>\nvoid test() {\n  using system =\n      tmpl::conditional_t<TwoVars, TwoVariableSystem, SingleVariableSystem>;\n\n  const Slab slab(1., 3.);\n  TimeSteppers::History<double> history{2};\n  history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n  history.insert(TimeStepId(true, 0, slab.end()), 0.0, 0.0);\n\n  auto box = db::create<\n      db::AddSimpleTags<Tags::ConcreteTimeStepper<TimeStepper>,\n                        Tags::HistoryEvolvedVariables<Var>,\n                        Tags::HistoryEvolvedVariables<AlternativeVar>>,\n      time_stepper_ref_tags<TimeStepper>>(\n      static_cast<std::unique_ptr<TimeStepper>>(\n          std::make_unique<TimeSteppers::AdamsBashforth>(2)),\n      history, history);\n\n  db::mutate_apply<CleanHistory<system>>(make_not_null(&box));\n\n  CHECK(db::get<Tags::HistoryEvolvedVariables<Var>>(box).size() == 1);\n  CHECK(db::get<Tags::HistoryEvolvedVariables<AlternativeVar>>(box).size() ==\n        (TwoVars ? 1 : 2));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.CleanHistory\", \"[Unit][Time]\") {\n  test<false>();\n  test<true>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/Test_EvolutionOrdering.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <functional>\n#include <limits>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/EvolutionOrdering.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\ntemplate <int>\nstruct StrangeComparison {\n  explicit StrangeComparison(const int v) : value(v) {}\n  StrangeComparison(const StrangeComparison&) = delete;\n  StrangeComparison(StrangeComparison&&) = default;\n  StrangeComparison& operator=(const StrangeComparison&) = delete;\n  StrangeComparison& operator=(StrangeComparison&&) = default;\n  ~StrangeComparison() = default;\n  int value;\n};\n\nbool operator==(const StrangeComparison<1>& x, const StrangeComparison<1>& y) {\n  return x.value == y.value;\n}\n\n// These are for testing types and forwarding, so it isn't important\n// to do something meaningful with the values.  (And we never call\n// this with I1 == I2, so we don't have to handle that.)\ntemplate <int I1, int I2>\nconst StrangeComparison<std::min(I1, I2)>& operator<(\n    const StrangeComparison<I1>& x, const StrangeComparison<I2>& y) {\n  if constexpr (I1 < I2) {\n    return x;\n  } else {\n    return y;\n  }\n}\n\ntemplate <int I1, int I2>\ndecltype(auto) operator>(const StrangeComparison<I1>& x,\n                         const StrangeComparison<I2>& y) {\n  return x < y;\n}\n\ntemplate <int I1, int I2>\ndecltype(auto) operator<=(const StrangeComparison<I1>& x,\n                          const StrangeComparison<I2>& y) {\n  return x < y;\n}\n\ntemplate <int I1, int I2>\ndecltype(auto) operator>=(const StrangeComparison<I1>& x,\n                          const StrangeComparison<I2>& y) {\n  return x < y;\n}\n\ntemplate <typename EvOp, typename StdOp, typename Arg1, typename Arg2>\nvoid check_helper(const EvOp& ev_forward, const EvOp& ev_backward) {\n  CHECK(ev_forward(Arg1{1}, Arg2{2}) == StdOp{}(Arg1{1}, Arg2{2}));\n  CHECK(ev_forward(Arg1{2}, Arg2{1}) == StdOp{}(Arg1{2}, Arg2{1}));\n  CHECK(ev_forward(Arg1{1}, Arg2{1}) == StdOp{}(Arg1{1}, Arg2{1}));\n\n  CHECK(ev_backward(Arg1{1}, Arg2{2}) == StdOp{}(Arg2{2}, Arg1{1}));\n  CHECK(ev_backward(Arg1{2}, Arg2{1}) == StdOp{}(Arg2{1}, Arg1{2}));\n  CHECK(ev_backward(Arg1{1}, Arg2{1}) == StdOp{}(Arg2{1}, Arg1{1}));\n}\n\ntemplate <template <typename...> class EvOp, template <typename...> class StdOp>\nvoid check_op() {\n  {\n    INFO(\"Concrete\");\n    check_helper<EvOp<int>, StdOp<int>, int, int>({true}, {false});\n\n    CHECK(EvOp<double>{true}.infinity() ==\n          std::numeric_limits<double>::infinity());\n    CHECK(EvOp<int>{true}.template infinity<double>() ==\n          std::numeric_limits<double>::infinity());\n    CHECK(EvOp<double>{false}.infinity() ==\n          -std::numeric_limits<double>::infinity());\n    CHECK(EvOp<int>{false}.template infinity<double>() ==\n          -std::numeric_limits<double>::infinity());\n\n    INFO(\"Serialization\");\n    check_helper<EvOp<int>, StdOp<int>, int, int>(\n        serialize_and_deserialize(EvOp<int>{true}),\n        serialize_and_deserialize(EvOp<int>{false}));\n  }\n  {\n    INFO(\"Generic\");\n    check_helper<EvOp<>, StdOp<>, StrangeComparison<1>, StrangeComparison<2>>(\n        {true}, {false});\n\n    CHECK(EvOp<>{true}.template infinity<double>() ==\n          std::numeric_limits<double>::infinity());\n    CHECK(EvOp<>{false}.template infinity<double>() ==\n          -std::numeric_limits<double>::infinity());\n\n    INFO(\"Serialization\");\n    check_helper<EvOp<>, StdOp<>, StrangeComparison<1>, StrangeComparison<2>>(\n        serialize_and_deserialize(EvOp<>{true}),\n        serialize_and_deserialize(EvOp<>{false}));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.EvolutionOrdering\", \"[Unit][Time]\") {\n  {\n    INFO(\"less\");\n    check_op<evolution_less, std::less>();\n  }\n  {\n    INFO(\"greater\");\n    check_op<evolution_greater, std::greater>();\n  }\n  {\n    INFO(\"less_equal\");\n    check_op<evolution_less_equal, std::less_equal>();\n  }\n  {\n    INFO(\"greater_equal\");\n    check_op<evolution_greater_equal, std::greater_equal>();\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_History.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <type_traits>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Variables.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nconstexpr size_t num_points = 3;\n\ntemplate <typename T>\nconst T& as_const(const T& t) {\n  return t;\n}\n\nstruct VarTag : db::SimpleTag {\n  using type = Scalar<DataVector>;\n};\n\nvoid test_untyped_step_record() {\n  const Slab slab(0.0, 1.0);\n  const TimeStepId time_a(true, 0, slab.start());\n  const TimeStepId time_b(true, 0, slab.start(), 1, slab.duration(),\n                          slab.start().value());\n  const TimeSteppers::UntypedStepRecord<double> a{time_a, std::optional{5.0},\n                                                  6.0};\n  auto b = a;\n  CHECK(a == b);\n  CHECK_FALSE(a != b);\n\n  b.time_step_id = time_b;\n  CHECK(a != b);\n  CHECK_FALSE(a == b);\n\n  b = a;\n  b.value = std::nullopt;\n  CHECK(a != b);\n  CHECK_FALSE(a == b);\n\n  b = a;\n  b.derivative = -10.0;\n  CHECK(a != b);\n  CHECK_FALSE(a == b);\n}\n\ntemplate <typename Vars, typename Derivs>\nvoid test_step_record() {\n  using Record = TimeSteppers::StepRecord<Vars>;\n  static_assert(std::is_same_v<typename Record::DerivVars, Derivs>);\n\n  const Slab slab(0.0, 1.0);\n  const TimeStepId time_a(true, 0, slab.start());\n  const TimeStepId time_b(true, 0, slab.start(), 1, slab.duration(),\n                          slab.start().value());\n\n  const Record a{time_a, std::optional{make_with_value<Vars>(num_points, 2.0)},\n                 make_with_value<Derivs>(num_points, 3.0)};\n  auto b = a;\n  CHECK(a == b);\n  CHECK_FALSE(a != b);\n\n  b.time_step_id = time_b;\n  CHECK(a != b);\n  CHECK_FALSE(a == b);\n\n  b = a;\n  b.value = std::nullopt;\n  CHECK(a != b);\n  CHECK_FALSE(a == b);\n\n  b = a;\n  b.derivative *= 2.0;\n  CHECK(a != b);\n  CHECK_FALSE(a == b);\n\n  test_serialization(a);\n}\n\ntemplate <typename Vars, typename Derivs>\nvoid test_history() {\n  const auto make_value = [](const double v) {\n    return make_with_value<Vars>(num_points, v);\n  };\n  const auto make_deriv = [](const double v) {\n    return make_with_value<Derivs>(num_points, v);\n  };\n\n  using History = TimeSteppers::History<Vars>;\n  static_assert(std::is_same_v<typename History::DerivVars, Derivs>);\n  using UntypedVars = typename History::UntypedVars;\n  using ConstUntyped = TimeSteppers::ConstUntypedHistory<UntypedVars>;\n  using MutableUntyped = TimeSteppers::MutableUntypedHistory<UntypedVars>;\n\n  const Slab slab(0.0, 1.0);\n\n  History history(5);\n  const History& const_history = history;\n  CHECK(const_history.integration_order() == 5);\n  history.integration_order(4);\n  CHECK(const_history.integration_order() == 4);\n\n  // Initial state: [steps] [step_time: substeps] = [] []\n  CHECK(const_history.empty());\n  history.insert_initial(TimeStepId(true, 0, slab.start()), make_value(-1.0),\n                         make_deriv(-10.0));\n  // [(0, -1, -10)] []\n  CHECK(const_history.size() == 1);\n  CHECK(&const_history.step_start(0.0) == &const_history[0]);\n  CHECK(&const_history.step_start(0.1) == &const_history[0]);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      const_history.step_start(-0.1),\n      Catch::Matchers::ContainsSubstring(\"before start of history\"));\n#endif  // SPECTRE_DEBUG\n  history.insert_initial(\n      TimeStepId(true, -1, slab.start() - Slab(-1.0, 0.0).duration() / 4),\n      History::no_value, make_deriv(-20.0));\n  // [(-1/4, X, -20), (0, -1, -10)] []\n  CHECK(const_history.size() == 2);\n  history.insert_initial(\n      TimeStepId(true, -1, slab.start() - Slab(-1.0, 0.0).duration() / 2),\n      make_value(-3.0), make_deriv(-30.0));\n  // [(-1/2, -3, -30), (-1/4, X, -20), (0, -1, -10)] []\n  CHECK(const_history.size() == 3);\n\n  CHECK(const_history.max_size() < 20);\n  CHECK(const_history.max_size() >= const_history.size());\n\n  CHECK(const_history[0].time_step_id ==\n        TimeStepId(true, -1, slab.start() - Slab(-1.0, 0.0).duration() / 2));\n  CHECK(const_history[0].value == std::optional{make_value(-3.0)});\n  CHECK(const_history[0].derivative == make_deriv(-30.0));\n  CHECK(not const_history[1].value.has_value());\n  CHECK(const_history[1].derivative == make_deriv(-20.0));\n  CHECK(const_history[2].value == std::optional{make_value(-1.0)});\n  CHECK(const_history[2].derivative == make_deriv(-10.0));\n  CHECK(const_history.latest_value() == *const_history.back().value);\n  CHECK(&const_history.latest_value() == &*const_history.back().value);\n  CHECK(&const_history.step_start(0.0) == &const_history[2]);\n  CHECK(&const_history.step_start(0.1) == &const_history[2]);\n  CHECK(&const_history.step_start(-0.1) == &const_history[1]);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      const_history.step_start(-1.0),\n      Catch::Matchers::ContainsSubstring(\"before start of history\"));\n#endif  // SPECTRE_DEBUG\n\n  history[0].derivative = make_deriv(-300.0);\n  // [(-1/2, -3, -300), (-1/4, X, -20), (0, -1, -10)] []\n  CHECK(const_history[0].derivative == make_deriv(-300.0));\n\n  history.insert(TimeStepId(true, 0, slab.start() + slab.duration() / 4),\n                 make_value(1.0), make_deriv(10.0));\n  // [(-1/2, -3, -300), (-1/4, X, -20), (0, -1, -10), (1/4, 1, 10)] []\n  CHECK(const_history.size() == 4);\n  CHECK(const_history[0].derivative == make_deriv(-300.0));\n  CHECK(const_history[3].derivative == make_deriv(10.0));\n\n  CHECK(&const_history[2] == &const_history[const_history[2].time_step_id]);\n  history[const_history[2].time_step_id].derivative = make_deriv(-100.0);\n  // [(-1/2, -3, -300), (-1/4, X, -20), (0, -1, -100), (1/4, 1, 10)] []\n  CHECK(const_history[2].derivative == make_deriv(-100.0));\n\n  [[maybe_unused]] const double* cached_value = nullptr;\n  if constexpr (tt::is_a_v<Variables, Vars>) {\n    cached_value = const_history[2].value->data();\n  }\n  history.discard_value(const_history[2].time_step_id);\n  // [(-1/2, -3, -300), (-1/4, X, -20), (0, X, -100), (1/4, 1, 10)] []\n  CHECK(not const_history[2].value.has_value());\n  CHECK(const_history[0].value.has_value());\n  CHECK(const_history[3].value.has_value());\n  history.insert_in_place(\n      TimeStepId(true, 0, slab.start() + slab.duration() / 2),\n      [&](const auto v) {\n        if constexpr (tt::is_a_v<Variables, Vars>) {\n          CHECK(v->data() == cached_value);\n        }\n        // Avoid overwriting the cached allocation for testing.\n        *v = as_const(make_value(2.0));\n      },\n      [&](const auto d) { *d = as_const(make_deriv(20.0)); });\n  // [(-1/2, -3, -300), (-1/4, X, -20), (0, X, -100), (1/4, 1, 10),\n  //  (1/2, 2, 20)] []\n  CHECK(*const_history.back().value == make_value(2.0));\n  CHECK(const_history.back().derivative == make_deriv(20.0));\n\n  {\n    const auto check_const_untyped = [&const_history](const auto& untyped) {\n      CHECK(untyped.integration_order() == const_history.integration_order());\n      CHECK(untyped.size() == const_history.size());\n      CHECK(untyped.max_size() == const_history.max_size());\n      CHECK(untyped[0].time_step_id == const_history[0].time_step_id);\n      CHECK(untyped[0].value.has_value());\n      CHECK(*untyped[0].value == *const_history[0].value);\n      CHECK(untyped[0].derivative == const_history[0].derivative);\n      // Note: we use Overloader instead of `if constexpr` because nvcc 12.3\n      // doesn't correctly compile the `if constexpr` version.\n      const Overloader overloader{\n          []<typename U>(\n              const U& /*untyped_local*/,\n              const TimeSteppers::History<double>& /*const_history_local*/) {},\n          []<typename U, typename T>(\n              const U& untyped_local,\n              const TimeSteppers::History<Variables<T>>& const_history_local) {\n            CHECK((untyped_local[0].value->data()) ==\n                  const_history_local[0].value->data());\n            CHECK(untyped_local[0].derivative.data() ==\n                  const_history_local[0].derivative.data());\n          },\n      };\n      overloader(untyped, const_history);\n      CHECK(untyped[1].time_step_id == const_history[1].time_step_id);\n      CHECK(not untyped[1].value.has_value());\n      CHECK(untyped[1].derivative == const_history[1].derivative);\n      CHECK(&untyped[1] == &untyped[untyped[1].time_step_id]);\n    };\n\n    // Capture rvalue temporaries by const reference to the base class\n    // to verify all functionality is correctly declared virtual.\n    const ConstUntyped& const_untyped = const_history.untyped();\n    check_const_untyped(const_untyped);\n    const MutableUntyped& mutable_untyped = history.untyped();\n    check_const_untyped(mutable_untyped);\n    const auto implicitly_convert =\n        [](const ConstUntyped& x) -> const ConstUntyped& { return x; };\n    check_const_untyped(implicitly_convert(mutable_untyped));\n    mutable_untyped.discard_value(mutable_untyped[3].time_step_id);\n    // [(-1/2, -3, -300), (-1/4, X, -20), (0, X, -100), (1/4, X, 10),\n    //  (1/2, 2, 20)] []\n    CHECK(not const_history[3].value.has_value());\n    CHECK(not mutable_untyped[3].value.has_value());\n  }\n\n  [[maybe_unused]] const double* cached_deriv = nullptr;\n  if constexpr (tt::is_a_v<Variables, Vars>) {\n    cached_value = const_history.front().value->data();\n    cached_deriv = const_history.front().derivative.data();\n  }\n  history.pop_front();\n  // [(-1/4, X, -20), (0, X, -100), (1/4, X, 10), (1/2, 2, 20)] []\n  CHECK(const_history.size() == 4);\n  CHECK(const_history.front().derivative == make_deriv(-20.0));\n  history.insert_in_place(\n      TimeStepId(true, 0, slab.start() + slab.duration() * 3 / 4),\n      [&](const auto v) {\n        if constexpr (tt::is_a_v<Variables, Vars>) {\n          CHECK(v->data() == cached_value);\n        }\n        *v = as_const(make_value(3.0));\n      },\n      [&](const auto d) {\n        if constexpr (tt::is_a_v<Variables, Vars>) {\n          CHECK(d->data() == cached_deriv);\n        }\n        *d = as_const(make_deriv(30.0));\n      });\n  // [(-1/4, X, -20), (0, X, -100), (1/4, X, 10), (1/2, 2, 20), (3/4, 3, 30)]\n  // []\n\n  history.pop_front();\n  // [(0, X, -100), (1/4, X, 10), (1/2, 2, 20), (3/4, 3, 30)] []\n\n  {\n    const MutableUntyped& untyped = history.untyped();\n    untyped.pop_front();\n    // [(1/4, X, 10), (1/2, 2, 20), (3/4, 3, 30)] []\n    CHECK(const_history.size() == 3);\n    CHECK(const_history.front().derivative == make_deriv(10.0));\n    // Verify that `untyped` reflects the new state.\n    CHECK(untyped.size() == 3);\n    CHECK(untyped.front().derivative == make_deriv(10.0));\n  }\n\n  CHECK(const_history.at_step_start());\n  CHECK(static_cast<const ConstUntyped&>(const_history.untyped())\n            .at_step_start());\n  history.undo_latest();\n  // [(1/4, X, 10), (1/2, 2, 20)] []\n  CHECK(const_history.size() == 2);\n  CHECK(const_history.front().derivative == make_deriv(10.0));\n  CHECK(const_history.at_step_start());\n  CHECK(static_cast<const ConstUntyped&>(const_history.untyped())\n            .at_step_start());\n\n  // Now test substeps\n\n  CHECK(as_const(const_history.substeps()).empty());\n  CHECK(as_const(history.substeps()).empty());\n\n  const auto step_time = const_history.back().time_step_id.step_time();\n  const auto step_size = slab.duration() / 4;\n  history.insert(TimeStepId(true, 0, step_time, 1, step_size,\n                            (step_time + slab.duration() / 4).value()),\n                 make_value(4.0), make_deriv(40.0));\n  // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, 4, 40)]\n  CHECK(const_history.back().derivative == make_deriv(20.0));\n  CHECK(as_const(const_history.substeps()).size() == 1);\n  CHECK(as_const(const_history.substeps())[0].derivative == make_deriv(40.0));\n  CHECK(as_const(history.substeps()).size() == 1);\n  CHECK(&const_history.step_start(0.5) == &const_history[1]);\n  CHECK(&const_history.step_start(0.7) == &const_history[1]);\n  CHECK(&const_history.step_start(0.3) == &const_history[0]);\n\n  as_const(history.substeps())[0].derivative = make_deriv(400.0);\n  // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, 4, 400)]\n  CHECK(as_const(const_history.substeps())[0].derivative == make_deriv(400.0));\n\n  CHECK(not const_history.at_step_start());\n  CHECK(not static_cast<const ConstUntyped&>(const_history.untyped())\n                .at_step_start());\n  history.undo_latest();\n  // [(1/4, X, 10), (1/2, 2, 20)] []\n  CHECK(as_const(const_history.substeps()).empty());\n  CHECK(const_history.at_step_start());\n  CHECK(static_cast<const ConstUntyped&>(const_history.untyped())\n            .at_step_start());\n\n  history.insert(TimeStepId(true, 0, step_time, 1, step_size,\n                            (step_time + slab.duration() / 4).value()),\n                 make_value(4.0), make_deriv(40.0));\n  // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, 4, 40)]\n  history.insert(TimeStepId(true, 0, step_time, 2, step_size,\n                            (step_time + slab.duration() / 4).value()),\n                 make_value(5.0), make_deriv(50.0));\n  // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, 4, 40), (2, 5, 50)]\n\n  {\n    const auto expected_latest = *const_history.substeps().back().value;\n    CHECK(const_history.latest_value() == expected_latest);\n    CHECK(&const_history.latest_value() ==\n          &*const_history.substeps().back().value);\n    history.discard_value(const_history.substeps().back().time_step_id);\n    // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, 4, 40), (2, X, 50)]\n    CHECK(not const_history.substeps().back().value.has_value());\n    CHECK(const_history.latest_value() == expected_latest);\n    CHECK(serialize_and_deserialize(const_history).latest_value() ==\n          expected_latest);\n  }\n\n  {\n    const auto typed = const_history.substeps();\n    const ConstUntyped& untyped_interface = const_history.untyped();\n    const auto untyped = untyped_interface.substeps();\n    CHECK(untyped.size() == typed.size());\n    CHECK(untyped.max_size() == typed.max_size());\n    CHECK(typed.max_size() < 20);\n    CHECK(typed.max_size() >= typed.size());\n\n    CHECK(untyped[0].time_step_id == typed[0].time_step_id);\n    CHECK(typed[0].value.has_value());\n    CHECK(untyped[0].value.has_value());\n    CHECK(*untyped[0].value == *typed[0].value);\n    CHECK(untyped[0].derivative == typed[0].derivative);\n    CHECK(untyped[1].time_step_id == typed[1].time_step_id);\n    CHECK(not typed[1].value.has_value());\n    CHECK(not untyped[1].value.has_value());\n    CHECK(untyped[1].derivative == typed[1].derivative);\n  }\n\n  static_cast<const MutableUntyped&>(history.untyped())\n      .discard_value(const_history.substeps().front().time_step_id);\n  // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, X, 40), (2, X, 50)]\n  CHECK(not const_history.substeps().front().value.has_value());\n\n  CHECK(not const_history.at_step_start());\n  CHECK(not static_cast<const ConstUntyped&>(const_history.untyped())\n                .at_step_start());\n  history.undo_latest();\n  // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, X, 40)]\n  CHECK(const_history.substeps().size() == 1);\n  CHECK(const_history.substeps().front().derivative == make_deriv(40.0));\n  CHECK(not const_history.at_step_start());\n  CHECK(not static_cast<const ConstUntyped&>(const_history.untyped())\n                .at_step_start());\n\n  history.clear_substeps();\n  // [(1/4, X, 10), (1/2, 2, 20)] []\n  CHECK(const_history.substeps().empty());\n  CHECK(const_history.size() == 2);\n  CHECK(const_history.at_step_start());\n  CHECK(static_cast<const ConstUntyped&>(const_history.untyped())\n            .at_step_start());\n\n  history.insert(TimeStepId(true, 0, step_time, 1, step_size,\n                            (step_time + slab.duration() / 4).value()),\n                 make_value(4.0), make_deriv(40.0));\n  // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, 4, 40)]\n  history.insert(TimeStepId(true, 0, step_time, 2, step_size,\n                            (step_time + slab.duration() / 4).value()),\n                 make_value(5.0), make_deriv(50.0));\n  // [(1/4, X, 10), (1/2, 2, 20)] [(1, 4, 40), (2, 5, 50)]\n  history.insert(TimeStepId(true, 1, slab.end()), make_value(6.0),\n                 make_deriv(60.0));\n  // [(1/4, X, 10), (1/2, 2, 20), (1, 6, 60)] [1/2: (1, 4, 40), (2, 5, 50)]\n\n  CHECK(const_history.size() == 3);\n  CHECK(as_const(const_history.substeps()).size() == 2);\n  CHECK(const_history.at_step_start());\n  CHECK(static_cast<const ConstUntyped&>(const_history.untyped())\n            .at_step_start());\n  CHECK(&const_history.step_start(0.5) == &const_history[1]);\n  CHECK(&const_history.step_start(0.7) == &const_history[1]);\n  CHECK(&const_history.step_start(1.1) == &const_history[2]);\n  CHECK(&const_history.step_start(1.0) == &const_history[2]);\n\n  // Test transform when the substeps are not associated with the last\n  // step.  This causes errors in a naive implementation.\n  {\n    const auto return_transformer = []() {\n      if constexpr (tt::is_a_v<Variables, Vars>) {\n        return [](const auto& input) { return input.data()[0] + 1.0; };\n      } else {\n        return [](const double& input) { return input + 1.0; };\n      }\n    }();\n    const auto return_transformer2 = []() {\n      if constexpr (tt::is_a_v<Variables, Vars>) {\n        return [](const auto& input) { return input.data()[0] + 2.0; };\n      } else {\n        return [](const double& input) { return input + 2.0; };\n      }\n    }();\n    const auto mutate_transformer = []() {\n      if constexpr (tt::is_a_v<Variables, Vars>) {\n        return [](const gsl::not_null<double*> result, const auto& input) {\n          *result = input.data()[0] + 1.0;\n        };\n      } else {\n        return [](const gsl::not_null<double*> result, const double& input) {\n          *result = input + 1.0;\n        };\n      }\n    }();\n\n    const auto modify_transformer = []() {\n      if constexpr (tt::is_a_v<Variables, Vars>) {\n        return [](const gsl::not_null<double*> result, const auto& input) {\n          *result *= input.data()[0];\n        };\n      } else {\n        return [](const gsl::not_null<double*> result, const double& input) {\n          *result *= input;\n        };\n      }\n    }();\n    const auto modify_transformer2 = []() {\n      if constexpr (tt::is_a_v<Variables, Vars>) {\n        return [](const gsl::not_null<double*> result, const auto& input) {\n          *result *= (input.data()[0] - 1.0);\n        };\n      } else {\n        return [](const gsl::not_null<double*> result, const double& input) {\n          *result *= (input - 1.0);\n        };\n      }\n    }();\n\n    TimeSteppers::History<double> transformed_history{};\n    transform(make_not_null(&transformed_history), history, return_transformer);\n\n    CHECK(transformed_history.integration_order() ==\n          history.integration_order());\n\n    CHECK(transformed_history.size() == 3);\n    CHECK(transformed_history[0].time_step_id == history[0].time_step_id);\n    CHECK(not transformed_history[0].value.has_value());\n    CHECK(transformed_history[0].derivative == 11.0);\n    CHECK(transformed_history[1].time_step_id == history[1].time_step_id);\n    CHECK(transformed_history[1].value == std::optional{3.0});\n    CHECK(transformed_history[1].derivative == 21.0);\n    CHECK(transformed_history.substeps().size() == 2);\n    CHECK(transformed_history.substeps()[0].time_step_id ==\n          history.substeps()[0].time_step_id);\n    CHECK(transformed_history.substeps()[0].value == 5.0);\n    CHECK(transformed_history.substeps()[0].derivative == 41.0);\n\n    transform_mutate(make_not_null(&transformed_history), history,\n                     modify_transformer);\n\n    CHECK(transformed_history.size() == 3);\n    CHECK(transformed_history[0].time_step_id == history[0].time_step_id);\n    CHECK(not transformed_history[0].value.has_value());\n    CHECK(transformed_history[0].derivative == 110.0);\n    CHECK(transformed_history[1].time_step_id == history[1].time_step_id);\n    CHECK(transformed_history[1].value == std::optional{6.0});\n    CHECK(transformed_history[1].derivative == 420.0);\n    CHECK(transformed_history.substeps().size() == 2);\n    CHECK(transformed_history.substeps()[0].time_step_id ==\n          history.substeps()[0].time_step_id);\n    CHECK(transformed_history.substeps()[0].value == 20.0);\n    CHECK(transformed_history.substeps()[0].derivative == 1640.0);\n\n    transform(make_not_null(&transformed_history), history, return_transformer,\n              return_transformer2);\n\n    CHECK(transformed_history.size() == 3);\n    CHECK(transformed_history[0].time_step_id == history[0].time_step_id);\n    CHECK(not transformed_history[0].value.has_value());\n    CHECK(transformed_history[0].derivative == 12.0);\n    CHECK(transformed_history[1].time_step_id == history[1].time_step_id);\n    CHECK(transformed_history[1].value == std::optional{3.0});\n    CHECK(transformed_history[1].derivative == 22.0);\n    CHECK(transformed_history.substeps().size() == 2);\n    CHECK(transformed_history.substeps()[0].time_step_id ==\n          history.substeps()[0].time_step_id);\n    CHECK(transformed_history.substeps()[0].value == 5.0);\n    CHECK(transformed_history.substeps()[0].derivative == 42.0);\n\n    transform_mutate(make_not_null(&transformed_history), history,\n                     modify_transformer, modify_transformer2);\n\n    CHECK(transformed_history.size() == 3);\n    CHECK(transformed_history[0].time_step_id == history[0].time_step_id);\n    CHECK(not transformed_history[0].value.has_value());\n    CHECK(transformed_history[0].derivative == 108.0);\n    CHECK(transformed_history[1].time_step_id == history[1].time_step_id);\n    CHECK(transformed_history[1].value == std::optional{6.0});\n    CHECK(transformed_history[1].derivative == 418.0);\n    CHECK(transformed_history.substeps().size() == 2);\n    CHECK(transformed_history.substeps()[0].time_step_id ==\n          history.substeps()[0].time_step_id);\n    CHECK(transformed_history.substeps()[0].value == 20.0);\n    CHECK(transformed_history.substeps()[0].derivative == 1638.0);\n\n    transform(make_not_null(&transformed_history), history, mutate_transformer);\n\n    CHECK(transformed_history.size() == 3);\n    CHECK(transformed_history[0].time_step_id == history[0].time_step_id);\n    CHECK(not transformed_history[0].value.has_value());\n    CHECK(transformed_history[0].derivative == 11.0);\n    CHECK(transformed_history[1].time_step_id == history[1].time_step_id);\n    CHECK(transformed_history[1].value == std::optional{3.0});\n    CHECK(transformed_history[1].derivative == 21.0);\n    CHECK(transformed_history.substeps().size() == 2);\n    CHECK(transformed_history.substeps()[0].time_step_id ==\n          history.substeps()[0].time_step_id);\n    CHECK(transformed_history.substeps()[0].value == 5.0);\n    CHECK(transformed_history.substeps()[0].derivative == 41.0);\n\n#ifdef SPECTRE_DEBUG\n    transformed_history.integration_order(1);\n    CHECK_THROWS_WITH(\n        transform_mutate(make_not_null(&transformed_history), history,\n                         modify_transformer),\n        Catch::Matchers::ContainsSubstring(\"integration orders\"));\n\n    transform(make_not_null(&transformed_history), history, mutate_transformer);\n    transformed_history.discard_value(transformed_history[1].time_step_id);\n    CHECK_THROWS_WITH(\n        transform_mutate(make_not_null(&transformed_history), history,\n                         modify_transformer),\n        Catch::Matchers::ContainsSubstring(\n            \"Only one of the entries to combine has a value\"));\n\n    transform(make_not_null(&transformed_history), history, mutate_transformer);\n    transformed_history[0].time_step_id = TimeStepId(true, 10, slab.start());\n    CHECK_THROWS_WITH(\n        transform_mutate(make_not_null(&transformed_history), history,\n                         modify_transformer),\n        Catch::Matchers::ContainsSubstring(\"Entries to combine do not match\"));\n\n    transform(make_not_null(&transformed_history), history, mutate_transformer);\n    transformed_history.pop_front();\n    CHECK_THROWS_WITH(\n        transform_mutate(make_not_null(&transformed_history), history,\n                         modify_transformer),\n        Catch::Matchers::ContainsSubstring(\"with sizes\"));\n\n    transform(make_not_null(&transformed_history), history, mutate_transformer);\n    transformed_history.clear_substeps();\n    CHECK_THROWS_WITH(\n        transform_mutate(make_not_null(&transformed_history), history,\n                         modify_transformer),\n        Catch::Matchers::ContainsSubstring(\"with substep sizes\"));\n#endif\n  }\n\n  history.undo_latest();\n  // [(1/4, X, 10), (1/2, 2, 20)] [1/2: (1, 4, 40), (2, 5, 50)]\n\n  CHECK(const_history.size() == 2);\n  CHECK(as_const(const_history.substeps()).size() == 2);\n  CHECK(not const_history.at_step_start());\n  CHECK(not static_cast<const ConstUntyped&>(const_history.untyped())\n                .at_step_start());\n\n  const auto step_time2 = slab.start() + slab.duration() * 3 / 4;\n  ASSERT(step_time2 - step_time == step_size, \"Test bug\");\n  const auto step_size2 = slab.duration() / 4;\n  history.insert(TimeStepId(true, 1, step_time2), make_value(6.0),\n                 make_deriv(60.0));\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)] [1/2: (1, 4, 40), (2, 5, 50)]\n\n  static_cast<const MutableUntyped&>(history.untyped()).clear_substeps();\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)] []\n\n  CHECK(const_history.size() == 3);\n  CHECK(as_const(const_history.substeps()).empty());\n  CHECK(const_history.at_step_start());\n  CHECK(static_cast<const ConstUntyped&>(const_history.untyped())\n            .at_step_start());\n\n  history.insert(\n      TimeStepId(true, 1, step_time2, 1, step_size2, slab.end().value()),\n      make_value(7.0), make_deriv(70.0));\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)] [3/4: (1, 7, 70)]\n\n  history.shrink_to_fit();\n  CHECK(const_history.size() == 3);\n  CHECK(as_const(const_history.substeps()).size() == 1);\n\n  if constexpr (tt::is_a_v<Variables, Vars>) {\n    cached_value = const_history.substeps().back().value->data();\n    cached_deriv = const_history.substeps().back().derivative.data();\n  }\n  history.undo_latest();\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)] []\n  CHECK(const_history.size() == 3);\n  CHECK(as_const(const_history.substeps()).empty());\n  history.insert_in_place(\n      TimeStepId(true, 1, step_time2, 1, step_size2, slab.end().value()),\n      History::no_value, [&](const auto d) {\n        if constexpr (tt::is_a_v<Variables, Vars>) {\n          CHECK(d->data() == cached_deriv);\n        }\n        *d = as_const(make_deriv(70.0));\n      });\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)] [3/4: (1, X, 70)]\n  history.insert(\n      TimeStepId(true, 1, step_time2, 2, step_size2, slab.end().value()),\n      History::no_value, make_deriv(80.0));\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)] [3/4: (1, X, 70), (1, X, 80)]\n  history.insert_in_place(\n      TimeStepId(true, 1, step_time2, 3, step_size2, slab.end().value()),\n      [&](const auto v) {\n        if constexpr (tt::is_a_v<Variables, Vars>) {\n          CHECK(v->data() == cached_value);\n        }\n        *v = as_const(make_value(9.0));\n      },\n      [&](const auto d) { *d = as_const(make_deriv(90.0)); });\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)]\n  //     [3/4: (1, X, 70), (1, X, 80), (1, 9, 90)]\n\n  history.undo_latest();\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)] [3/4: (1, X, 70), (1, X, 80)]\n  history.shrink_to_fit();\n\n  history.insert_in_place(\n      TimeStepId(true, 1, step_time2, 3, step_size2, slab.end().value()),\n      [&](const auto v) {\n        if constexpr (tt::is_a_v<Variables, Vars>) {\n          CHECK(v->size() == 0);\n        }\n        *v = as_const(make_value(9.0));\n      },\n      [&](const auto d) {\n        if constexpr (tt::is_a_v<Variables, Vars>) {\n          CHECK(d->size() == 0);\n        }\n        *d = as_const(make_deriv(90.0));\n      });\n  // [(1/4, X, 10), (1/2, 2, 20), (3/4, 6, 60)]\n  //     [3/4: (1, X, 70), (1, X, 80), (1, 8, 80)]\n\n  const auto check_copy = [&history](const auto& copy) {\n    CHECK(copy.integration_order() == history.integration_order());\n    CHECK(copy.size() == history.size());\n    CHECK(copy.front() == history.front());\n    CHECK(copy.substeps().size() == history.substeps().size());\n    CHECK(copy.substeps().front() == history.substeps().front());\n  };\n  {\n    const auto copy_constructed = history;\n    check_copy(copy_constructed);\n    History copy_assigned{};\n    copy_assigned = history;\n    check_copy(copy_assigned);\n  }\n  const auto copy = serialize_and_deserialize(history);\n  check_copy(copy);\n\n  history.map_entries([](const auto entry) { *entry *= 10.0; });\n  // [(1/4, X, 100), (1/2, 20, 200), (3/4, 60, 600)]\n  // [3/4: (1, X, 700), (1, X, 800), (1, 9, 900)]\n  CHECK(history.size() == 3);\n  for (size_t i = 0; i < history.size(); ++i) {\n    CHECK(history[i].time_step_id == copy[i].time_step_id);\n    CHECK(history[i].value.has_value() == copy[i].value.has_value());\n    if (history[i].value.has_value()) {\n      CHECK(*history[i].value == Vars(10.0 * *copy[i].value));\n    }\n    CHECK(history[i].derivative == Derivs(10.0 * copy[i].derivative));\n  }\n  CHECK(history.substeps().size() == 3);\n  for (size_t i = 0; i < history.substeps().size(); ++i) {\n    CHECK(history.substeps()[i].time_step_id ==\n          copy.substeps()[i].time_step_id);\n    CHECK(history.substeps()[i].value.has_value() ==\n          copy.substeps()[i].value.has_value());\n    if (history.substeps()[i].value.has_value()) {\n      CHECK(*history.substeps()[i].value ==\n            Vars(10.0 * *copy.substeps()[i].value));\n    }\n    CHECK(history.substeps()[i].derivative ==\n          Derivs(10.0 * copy.substeps()[i].derivative));\n  }\n\n  history.clear();\n  // [] []\n  CHECK(history.integration_order() == copy.integration_order());\n  CHECK(history.empty());\n  CHECK(history.substeps().empty());\n\n  {\n    History a{2};\n    History b{3};\n    History c = b;\n    c.insert(TimeStepId(true, 0, slab.start()), make_value(1.0),\n             make_deriv(10.0));\n    History d = c;\n    d.insert(TimeStepId(true, 0, slab.start(), 1, slab.duration(),\n                        slab.start().value()),\n             make_value(2.0), make_deriv(20.0));\n\n    CHECK(a == a);\n    CHECK_FALSE(a != a);\n    CHECK(b == b);\n    CHECK_FALSE(b != b);\n    CHECK(c == c);\n    CHECK_FALSE(c != c);\n    CHECK(d == d);\n    CHECK_FALSE(d != d);\n    CHECK(a != b);\n    CHECK_FALSE(a == b);\n    CHECK(b != c);\n    CHECK_FALSE(b == c);\n    CHECK(c != d);\n    CHECK_FALSE(c == d);\n  }\n}\n\nvoid test_history_assertions() {\n#ifdef SPECTRE_DEBUG\n  const Slab slab(0.0, 1.0);\n  const auto slab_half = slab.start() + slab.duration() / 2;\n  const auto step = slab_half - slab.start();\n\n  // Insertion errors\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(\n        history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0),\n        Catch::Matchers::ContainsSubstring(\"must be later\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert_initial(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(\n        history.insert_initial(TimeStepId(true, 0, slab.start()), 0.0, 0.0),\n        Catch::Matchers::ContainsSubstring(\"must be earlier\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    CHECK_THROWS_WITH(history.insert(TimeStepId(true, 0, slab.start(), 1, step,\n                                                slab.start().value()),\n                                     0.0, 0.0),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Cannot insert substep into empty history\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(history.insert(TimeStepId(true, 0, slab.start(), 2, step,\n                                                slab.start().value()),\n                                     0.0, 0.0),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Cannot insert substep 2 following 0\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(\n        history.insert(\n            TimeStepId(true, 0, slab_half, 1, step, slab.end().value()), 0.0,\n            0.0),\n        Catch::Matchers::ContainsSubstring(\"Cannot insert substep \") and\n            Catch::Matchers::ContainsSubstring(\" of different step \"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert_initial(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(\n        history.insert_initial(\n            TimeStepId(true, 0, slab.start(), 2, step, slab.start().value()),\n            0.0, 0.0),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot use insert_initial for substeps\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    // +1 because 0 isn't a substep.\n    for (size_t i = 0; i < history.substeps().max_size() + 1; ++i) {\n      history.insert(\n          TimeStepId(true, 0, slab.start(), i, step, slab.start().value()), 0.0,\n          0.0);\n    }\n    CHECK_THROWS_WITH(\n        history.insert(\n            TimeStepId(true, 0, slab.start(), history.substeps().max_size() + 1,\n                       step, slab.start().value()),\n            0.0, 0.0),\n        Catch::Matchers::ContainsSubstring(\n            \"Cannot insert new substep because the History is full\"));\n  }\n\n  // Indexing errors\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(std::as_const(history)[TimeStepId(true, 1, slab.start())],\n                      Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(history[TimeStepId(true, 1, slab.start())],\n                      Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(\n        std::as_const(history)[TimeStepId(true, 0, slab.start(), 1, step,\n                                          slab.start().value())],\n        Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(history[TimeStepId(true, 0, slab.start(), 1, step,\n                                         slab.start().value())],\n                      Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    history.insert(\n        TimeStepId(true, 0, slab.start(), 1, step, slab.end().value()), 0.0,\n        0.0);\n    CHECK_THROWS_WITH(\n        history[TimeStepId(true, 0, slab_half, 1, step, slab.end().value())],\n        Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    const TimeSteppers::History<double> history(1);\n    CHECK_THROWS_WITH(history.substeps()[0],\n                      Catch::Matchers::ContainsSubstring(\n                          \"Requested substep 0 but only have 0\"));\n  }\n  {\n    const TimeSteppers::History<double> history(1);\n    CHECK_THROWS_WITH(history.substeps()[1],\n                      Catch::Matchers::ContainsSubstring(\n                          \"Requested substep 1 but only have 0\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    CHECK_THROWS_WITH(history.substeps()[0],\n                      Catch::Matchers::ContainsSubstring(\n                          \"Requested substep 0 but only have 0\"));\n  }\n\n  // Untyped indexing errors\n  {\n    const TimeSteppers::History<double> history(1);\n    CHECK_THROWS_WITH(\n        history.untyped()[0],\n        Catch::Matchers::ContainsSubstring(\"Requested step 0 but only have 0\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(history.untyped()[TimeStepId(true, 1, slab.start())],\n                      Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(history.untyped()[TimeStepId(true, 0, slab.start(), 1,\n                                                   step, slab.start().value())],\n                      Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    history.insert(\n        TimeStepId(true, 0, slab.start(), 1, step, slab.end().value()), 0.0,\n        0.0);\n    CHECK_THROWS_WITH(history.untyped()[TimeStepId(true, 0, slab_half, 1, step,\n                                                   slab.end().value())],\n                      Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    const TimeSteppers::History<double> history(1);\n    CHECK_THROWS_WITH(history.untyped().substeps()[0],\n                      Catch::Matchers::ContainsSubstring(\n                          \"Requested substep 0 but only have 0\"));\n  }\n\n  // Record removal errors\n  {\n    TimeSteppers::History<double> history(1);\n    CHECK_THROWS_WITH(history.pop_front(),\n                      Catch::Matchers::ContainsSubstring(\"History is empty\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    CHECK_THROWS_WITH(history.undo_latest(),\n                      Catch::Matchers::ContainsSubstring(\"History is empty\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    history.insert(\n        TimeStepId(true, 0, slab.start(), 1, step, slab.start().value()), 0.0,\n        0.0);\n    CHECK_THROWS_WITH(history.pop_front(),\n                      Catch::Matchers::ContainsSubstring(\n                          \"Cannot remove a step with substeps.  \"\n                          \"Call clear_substeps() first\"));\n  }\n\n  // Value discarding errors\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(history.discard_value(TimeStepId(true, 1, slab.start())),\n                      Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(\n        history.discard_value(\n            TimeStepId(true, 0, slab.start(), 1, step, slab.start().value())),\n        Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n\n  // Untyped value discarding errors\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(\n        history.untyped().discard_value(TimeStepId(true, 1, slab.start())),\n        Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    CHECK_THROWS_WITH(\n        history.untyped().discard_value(\n            TimeStepId(true, 0, slab.start(), 1, step, slab.start().value())),\n        Catch::Matchers::ContainsSubstring(\"not present\"));\n  }\n\n  // latest_value errors\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    history.discard_value(TimeStepId(true, 0, slab.start()));\n    history.insert(TimeStepId(true, 1, slab.start()), 0.0, 0.0);\n    history.undo_latest();\n    CHECK_THROWS_WITH(\n        history.latest_value(),\n        Catch::Matchers::ContainsSubstring(\"Latest value unavailable\"));\n  }\n  {\n    TimeSteppers::History<double> history(1);\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    history.discard_value(TimeStepId(true, 0, slab.start()));\n    history.insert(TimeStepId(true, 1, slab.start()), 0.0, 0.0);\n    history.discard_value(TimeStepId(true, 1, slab.start()));\n    history.undo_latest();\n    CHECK_THROWS_WITH(\n        history.latest_value(),\n        Catch::Matchers::ContainsSubstring(\"Latest value unavailable\"));\n  }\n\n#endif  // SPECTRE_DEBUG\n}\n\nvoid test_history_output() {\n  TimeSteppers::History<double> history(2);\n  const Slab slab(0.0, 1.0);\n  history.insert(TimeStepId(true, 0, slab.start()), 0.0, 1.0);\n  const auto step_time = history.back().time_step_id.step_time();\n  const auto step_size = slab.duration() / 4;\n  history.insert(TimeStepId(true, 0, step_time, 1, step_size,\n                            (step_time + slab.duration() / 4).value()),\n                 4.0, 40.0);\n  const std::string expected_output =\n      \"Integration order: 2\\n\"\n      \"Step values:\\n\"\n      \"TimeStepId: 0:Slab[0,1]:0/1:0:0\\n\"\n      \"Value: 0\\n\"\n      \"Derivative: 1\\n\"\n      \"Substep values:\\n\"\n      \"TimeStepId: 0:Slab[0,1]:0/1:1:0.25\\n\"\n      \"Value: 4\\n\"\n      \"Derivative: 40\\n\"\n      \"Latest value if discarded: --\\n\";\n  CHECK(get_output(history) == expected_output);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.History\", \"[Unit][Time]\") {\n  test_untyped_step_record();\n\n  test_step_record<double, double>();\n  test_step_record<Variables<tmpl::list<VarTag>>,\n                   Variables<tmpl::list<Tags::dt<VarTag>>>>();\n\n  test_history<double, double>();\n  test_history<Variables<tmpl::list<VarTag>>,\n               Variables<tmpl::list<Tags::dt<VarTag>>>>();\n\n  test_history_assertions();\n\n  test_history_output();\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_LargestStepperError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <complex>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"Time/LargestStepperError.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.LargestStepperError\", \"[Unit][Time]\") {\n  CHECK(largest_stepper_error(20.0, 10.0, {.absolute = 2.0, .relative = 0.0}) ==\n        5.0);\n  CHECK(largest_stepper_error(20.0, 10.0, {.absolute = 0.0, .relative = 2.0}) ==\n        1.0 / 6.0);\n  CHECK(largest_stepper_error(0.0, 10.0, {.absolute = 0.0, .relative = 2.0}) ==\n        0.5);\n\n  CHECK(largest_stepper_error(20.0, 10.0, {.absolute = 2.0, .relative = 3.0}) ==\n        10.0 / (2.0 + 3.0 * 30.0));\n\n  CHECK(largest_stepper_error(std::complex<double>(12.0, 5.0),\n                              std::complex<double>(-3.0, -4.0),\n                              {.absolute = 2.0, .relative = 3.0}) ==\n        approx(5.0 / (2.0 + 3.0 * 13.0)));\n\n  {\n    const DataVector values{10.0, 0.0};\n    const DataVector errors{5.0, 2.0};\n    const StepperErrorTolerances mostly_abs{.absolute = 2.0, .relative = 0.1};\n    const StepperErrorTolerances mostly_rel{.absolute = 0.1, .relative = 2.0};\n    CHECK(largest_stepper_error(values, errors, mostly_abs) ==\n          largest_stepper_error(10.0, 5.0, mostly_abs));\n    CHECK(largest_stepper_error(values, errors, mostly_rel) ==\n          largest_stepper_error(0.0, 2.0, mostly_rel));\n  }\n\n  {\n    const ComplexDataVector values{std::complex<double>{10.0, 3.0},\n                                   std::complex<double>{0.0, 0.0}};\n    const ComplexDataVector errors{std::complex<double>{5.0, 1.0},\n                                   std::complex<double>{1.0, 3.0}};\n    const StepperErrorTolerances mostly_abs{.absolute = 2.0, .relative = 0.1};\n    const StepperErrorTolerances mostly_rel{.absolute = 0.1, .relative = 2.0};\n    // Vectorized complex math may not be identical to std::complex operations.\n    CHECK(largest_stepper_error(values, errors, mostly_abs) ==\n          approx(largest_stepper_error(values[0], errors[0], mostly_abs)));\n    CHECK(largest_stepper_error(values, errors, mostly_rel) ==\n          approx(largest_stepper_error(values[1], errors[1], mostly_rel)));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_RecordTimeStepperData.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/RecordTimeStepperData.hpp\"\n#include \"Time/RecordTimeStepperData.tpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var : db::SimpleTag {\n  using type = double;\n};\n\nstruct AlternativeVar : db::SimpleTag {\n  using type = double;\n};\n\nstruct SingleVariableSystem {\n  using variables_tag = Var;\n};\n\nstruct TwoVariableSystem {\n  using variables_tag = tmpl::list<Var, AlternativeVar>;\n};\n\ntemplate <typename System, bool AlternativeUpdates>\nvoid run_test() {\n  using history_tag = Tags::HistoryEvolvedVariables<Var>;\n  using alternative_history_tag = Tags::HistoryEvolvedVariables<AlternativeVar>;\n\n  const Slab slab(1., 3.);\n  const TimeStepId slab_start_id(true, 0, slab.start());\n  const TimeStepId slab_end_id(true, 0, slab.end());\n\n  typename history_tag::type history{};\n  history.insert(slab_start_id, -3., 3.);\n  typename alternative_history_tag::type alternative_history{};\n  alternative_history.insert(slab_start_id, -3., 3.);\n\n  const double initial_value = 4.;\n  auto box = db::create<\n      db::AddSimpleTags<Tags::TimeStepId, Var, ::Tags::dt<Var>,\n                        Tags::HistoryEvolvedVariables<Var>, AlternativeVar,\n                        ::Tags::dt<AlternativeVar>,\n                        Tags::HistoryEvolvedVariables<AlternativeVar>>>(\n      slab_end_id, initial_value, 5., std::move(history), initial_value, 5.,\n      std::move(alternative_history));\n\n  db::mutate_apply<RecordTimeStepperData<System>>(make_not_null(&box));\n\n  const auto check_history = [&initial_value, &slab_end_id,\n                              &slab_start_id](const auto& updated_history) {\n    CHECK(updated_history.size() == 2);\n    CHECK(updated_history[0].time_step_id == slab_start_id);\n    CHECK(updated_history[0].value == std::optional{-3.});\n    CHECK(updated_history[0].derivative == 3.);\n    CHECK(updated_history[1].time_step_id == slab_end_id);\n    CHECK(updated_history[1].value == std::optional{initial_value});\n    CHECK(updated_history[1].derivative == 5.);\n  };\n  check_history(db::get<history_tag>(box));\n  if (AlternativeUpdates) {\n    check_history(db::get<alternative_history_tag>(box));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.RecordTimeStepperData\", \"[Unit][Time]\") {\n  run_test<SingleVariableSystem, false>();\n  run_test<TwoVariableSystem, true>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/Test_SelfStart.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Time/SelfStart.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.SelfStart\", \"[Unit][Time][Actions]\") {\n  CHECK(SelfStart::is_self_starting(\n      TimeStepId{true, -1, Time{{0.0, 1.0}, {1, 5}}}));\n  CHECK(SelfStart::is_self_starting(\n      TimeStepId{true, -5, Time{{0.5, 0.6}, {4, 9}}}));\n  CHECK_FALSE(SelfStart::is_self_starting(\n      TimeStepId{true, 0, Time{{0.0, 1.0}, {1, 5}}}));\n  CHECK_FALSE(SelfStart::is_self_starting(\n      TimeStepId{true, 5, Time{{0.5, 0.6}, {4, 9}}}));\n\n  CHECK_FALSE(\n      SelfStart::step_unused(TimeStepId{true, 0, Time{{0.0, 1.0}, 0}},\n                             TimeStepId{true, 0, Time{{0.0, 1.0}, {1, 2}}}));\n  CHECK_FALSE(SelfStart::step_unused(TimeStepId{true, 0, Time{{0.0, 1.0}, 0}},\n                                     TimeStepId{true, 1, Time{{1.0, 2.0}, 0}}));\n  CHECK_FALSE(\n      SelfStart::step_unused(TimeStepId{true, -1, Time{{0.0, 1.0}, 0}},\n                             TimeStepId{true, -1, Time{{0.0, 1.0}, {1, 2}}}));\n  CHECK(SelfStart::step_unused(TimeStepId{true, -1, Time{{0.0, 1.0}, 0}},\n                               TimeStepId{true, 0, Time{{0.0, 1.0}, 0}}));\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_Slab.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <functional>\n#include <string>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.Slab\", \"[Unit][Time]\") {\n  const double tstart_d = 0.68138945475734402635;\n  const double tend_d = 76.34481744714527451379;\n  // Make sure we're using values that will trigger rounding errors.\n  CHECK_FALSE(tstart_d + (tend_d - tstart_d) == tend_d);\n  const Slab slab(tstart_d, tend_d);\n\n  // Arbitrary\n  const double tend2_d = tend_d + 1.234;\n  // Arbitrary duration, not related to above slabs.\n  const double duration_d = tend2_d;\n\n  {\n    CHECK(slab.start().value() == tstart_d);\n    CHECK(slab.end().value() == tend_d);\n    CHECK(slab.duration().value() == approx(tend_d - tstart_d));\n    CHECK(slab.end() - slab.start() == slab.duration());\n  }\n\n  {\n    const Slab slab2 = Slab::with_duration_from_start(tstart_d, duration_d);\n    CHECK(slab2.start().value() == tstart_d);\n    CHECK(slab2.duration().value() == approx(duration_d));\n  }\n\n  {\n    const Slab slab2 = Slab::with_duration_to_end(tend_d, duration_d);\n    CHECK(slab2.end().value() == tend_d);\n    CHECK(slab2.duration().value() == approx(duration_d));\n  }\n\n  {\n    const Slab next = slab.advance();\n    CHECK(next.start() == slab.end());\n    CHECK(next.duration().value() == approx(slab.duration().value()));\n    const Slab prev = slab.retreat();\n    CHECK(prev.end() == slab.start());\n    CHECK(prev.duration().value() == approx(slab.duration().value()));\n\n    CHECK(slab.advance_towards(slab.duration()) == next);\n    CHECK(slab.advance_towards(-slab.duration()) == prev);\n  }\n\n  {\n    const Slab front = slab.with_duration_from_start(duration_d);\n    CHECK(front.start().value() == slab.start().value());\n    CHECK(front.duration().value() == approx(duration_d));\n    const Slab back = slab.with_duration_to_end(duration_d);\n    CHECK(back.end().value() == slab.end().value());\n    CHECK(back.duration().value() == approx(duration_d));\n  }\n\n  {\n    CHECK(slab.is_followed_by(slab.advance().with_duration_from_start(3)));\n    CHECK(slab.is_preceeded_by(slab.retreat().with_duration_to_end(3)));\n  }\n\n  CHECK(slab == slab);\n  CHECK_FALSE(slab == Slab(tstart_d, tend2_d));\n  CHECK(slab != Slab(tstart_d, tend2_d));\n  CHECK(slab != Slab(tend2_d / 2., tend_d));\n  CHECK(slab != Slab(tend_d, tend2_d));\n\n  {\n    const auto check_overlaps = [](const Slab& a, const Slab& b,\n                                   const bool expected) {\n      CAPTURE(a);\n      CAPTURE(b);\n      CHECK(a.overlaps(b) == expected);\n      CHECK(b.overlaps(a) == expected);\n    };\n    check_overlaps(slab, slab, true);\n    check_overlaps(slab, slab.advance(), false);\n    check_overlaps(slab, slab.advance().advance(), false);\n    check_overlaps(slab, Slab(tstart_d, tend_d + 1.0), true);\n    check_overlaps(slab, Slab(tstart_d - 1.0, tend_d), true);\n    check_overlaps(slab, Slab(tstart_d - 1.0, tend_d + 1.0), true);\n  }\n\n  check_cmp(Slab(1, 2), Slab(3, 4));\n  check_cmp(Slab(1, 2), Slab(2, 4));\n\n  // Hashing\n  std::hash<Slab> h;\n  CHECK(h(Slab(0, 1)) != h(Slab(0, 2)));\n  CHECK(h(Slab(0, 1)) != h(Slab(-1, 0)));\n  CHECK(h(Slab(0, 1)) != h(Slab(-1, 1)));\n\n  // Output\n  CHECK(get_output(Slab(0.5, 1.5)) == \"Slab[0.5,1.5]\");\n\n  // Serialization\n  test_serialization(slab);\n\n  // Failure tests\n#ifdef __clang__\n#pragma GCC diagnostic ignored \"-Wunused-comparison\"\n#endif\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(Slab(1., 0.),\n                    Catch::Matchers::ContainsSubstring(\"Backwards Slab\"));\n  CHECK_THROWS_WITH(Slab::with_duration_from_start(0., -1.),\n                    Catch::Matchers::ContainsSubstring(\"Backwards Slab\"));\n  CHECK_THROWS_WITH(Slab::with_duration_to_end(0., -1.),\n                    Catch::Matchers::ContainsSubstring(\"Backwards Slab\"));\n\n  CHECK_THROWS_WITH(slab.advance_towards(0 * slab.duration()),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't advance along a zero time vector\"));\n\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) < Slab(0.1, 0.9),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) < Slab(0.1, 1.1),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) < Slab(-0.1, 0.9),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) < Slab(-0.1, 1.1),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) > Slab(0.1, 0.9),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) > Slab(0.1, 1.1),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) > Slab(-0.1, 0.9),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) > Slab(-0.1, 1.1),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) <= Slab(0.1, 0.9),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) <= Slab(0.1, 1.1),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) <= Slab(-0.1, 0.9),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) <= Slab(-0.1, 1.1),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) >= Slab(0.1, 0.9),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) >= Slab(0.1, 1.1),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) >= Slab(-0.1, 0.9),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n  CHECK_THROWS_WITH(\n      Slab(0., 1.) >= Slab(-0.1, 1.1),\n      Catch::Matchers::ContainsSubstring(\"Cannot compare overlapping slabs\"));\n#endif /*SPECTRE_DEBUG*/\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_SlabRoundingError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <initializer_list>\n\n#include \"Time/Slab.hpp\"\n#include \"Time/SlabRoundingError.hpp\"\n\nnamespace {\nvoid check_slab(const double start, const double end) {\n  const double duration = end - start;\n  {\n    const Slab slab = Slab::with_duration_from_start(start, duration);\n    CHECK(std::abs(slab.end().value() - end) <\n          slab_rounding_error(slab.start()));\n    CHECK(std::abs(slab.end().value() - end) <\n          slab_rounding_error(slab.end()));\n  }\n  {\n    const Slab slab = Slab::with_duration_to_end(end, duration);\n    CHECK(std::abs(slab.start().value() - start) <\n          slab_rounding_error(slab.start()));\n    CHECK(std::abs(slab.start().value() - start) <\n          slab_rounding_error(slab.end()));\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.slab_rounding_error\", \"[Unit][Time]\") {\n  const std::initializer_list<double> test_times{\n      -1.0e5 - 1.0,  -1.0e5,  -1.0e5 + 1.0, -2.0,   -1.0 - 1.0e-5, -1.0,\n      -1.0 + 1.0e-5, -1.0e-5, 0.0,          1.0e-5, 1.0 - 1.0e-5,  1.0,\n      1.0 + 1.0e-5,  2.0,     1.0e5 - 1.0,  1.0e5,  1.0e5 + 1.0};\n\n  for (auto start = test_times.begin(); start != test_times.end(); ++start) {\n    for (auto end = start + 1; end != test_times.end(); ++end) {\n      check_slab(*start, *end);\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_StepperErrorTolerances.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.StepperErrorTolerances\", \"[Unit][Time]\") {\n  const StepperErrorTolerances tols0{};\n  const StepperErrorTolerances tols1{\n      .estimates = StepperErrorTolerances::Estimates::StepperOrder,\n      .absolute = 0.1,\n      .relative = 0.3};\n  const StepperErrorTolerances tols2{\n      .estimates = StepperErrorTolerances::Estimates::StepperOrder,\n      .absolute = 0.1,\n      .relative = 0.5};\n  const StepperErrorTolerances tols3{\n      .estimates = StepperErrorTolerances::Estimates::StepperOrder,\n      .absolute = 0.5,\n      .relative = 0.3};\n  const StepperErrorTolerances tols4{\n      .estimates = StepperErrorTolerances::Estimates::AllOrders,\n      .absolute = 0.1,\n      .relative = 0.3};\n  CHECK(tols0 == tols0);\n  CHECK_FALSE(tols0 != tols0);\n  CHECK(tols1 == tols1);\n  CHECK_FALSE(tols1 != tols1);\n  CHECK(tols1 != tols0);\n  CHECK_FALSE(tols1 == tols0);\n  CHECK(tols1 != tols2);\n  CHECK_FALSE(tols1 == tols2);\n  CHECK(tols1 != tols3);\n  CHECK_FALSE(tols1 == tols3);\n  CHECK(tols1 != tols4);\n  CHECK_FALSE(tols1 == tols4);\n  test_serialization(tols1);\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_Time.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <functional>\n#include <initializer_list>\n#include <string>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test_time() {\n  using rational_t = Time::rational_t;\n\n  const double tstart_d = 0.68138945475734402635;\n  const double tend_d = 76.34481744714527451379;\n  // Make sure we're using values that will trigger rounding errors.\n  CHECK_FALSE(tstart_d + (tend_d - tstart_d) == tend_d);\n\n  const Slab slab(tstart_d, tend_d);\n  CHECK(Time(slab, 0).slab() == slab);\n  CHECK(Time(slab, rational_t(3, 5)).fraction() == rational_t(3, 5));\n\n  CHECK(Time(slab, 0).value() == tstart_d);\n  CHECK(Time(slab, 1).value() == tend_d);\n  CHECK(Time(slab, rational_t(3, 5)).value() ==\n        approx(2. / 5. * tstart_d + 3. / 5. * tend_d));\n\n  CHECK(Time(slab, 0).is_at_slab_start());\n  CHECK_FALSE(Time(slab, rational_t(1, 2)).is_at_slab_start());\n  CHECK_FALSE(Time(slab, 1).is_at_slab_start());\n  CHECK_FALSE(Time(slab, 0).is_at_slab_end());\n  CHECK_FALSE(Time(slab, rational_t(1, 2)).is_at_slab_end());\n  CHECK(Time(slab, 1).is_at_slab_end());\n  CHECK(Time(slab, 0).is_at_slab_boundary());\n  CHECK_FALSE(Time(slab, rational_t(1, 2)).is_at_slab_boundary());\n  CHECK(Time(slab, 1).is_at_slab_boundary());\n\n  check_cmp(Time(slab, 0), Time(slab, 1));\n  check_cmp(Time(slab, 0), Time(slab, rational_t(3, 5)));\n  check_cmp(Time(slab, rational_t(3, 5)), Time(slab, 1));\n  check_cmp(Time(slab, rational_t(2, 5)), Time(slab, rational_t(3, 5)));\n  check_cmp(Time(slab, 1), Time(slab.advance(), 1));\n  check_cmp(Time(slab, 0), Time(slab.advance(), 0));\n  check_cmp(Time(slab, rational_t(3, 5)),\n            Time(slab.advance(), rational_t(2, 5)));\n\n  CHECK(Time(slab, rational_t(2, 3)).with_slab(slab) ==\n        Time(slab, rational_t(2, 3)));\n\n  {\n    // Slab boundary stuff\n    const double other_duration = 2. * slab.duration().value();\n\n    const Time a2(slab, 1);\n    const Time b2 = a2.with_slab(slab.advance());\n    CHECK(b2.slab() == slab.advance());\n    CHECK(b2.fraction() == 0);\n    const Time c2 = a2.with_slab(slab.with_duration_to_end(other_duration));\n    CHECK(c2.slab() == slab.with_duration_to_end(other_duration));\n    CHECK(c2.fraction() == 1);\n  }\n\n  CHECK(Time(slab, 0).value() == Time(slab.retreat(), 1).value());\n  CHECK(Time(slab, 1).value() == Time(slab.advance(), 0).value());\n\n  CHECK(Time(slab, rational_t(3, 5)) - Time(slab, rational_t(1, 5)) ==\n        TimeDelta(slab, rational_t(2, 5)));\n\n  CHECK_OP(Time(slab, rational_t(1, 5)), +, TimeDelta(slab, rational_t(2, 5)),\n           Time(slab, rational_t(3, 5)));\n  CHECK_OP(Time(slab, rational_t(3, 5)), -, TimeDelta(slab, rational_t(2, 5)),\n           Time(slab, rational_t(1, 5)));\n\n  // Slab boundary arithmetic\n  CHECK(Time(slab.advance(), 0) - Time(slab, rational_t(2, 3)) ==\n        TimeDelta(slab, rational_t(1, 3)));\n  CHECK(Time(slab, rational_t(2, 3)) - Time(slab.advance(), 0) ==\n        TimeDelta(slab, -rational_t(1, 3)));\n  CHECK(Time(slab, 1) - Time(slab.advance(), rational_t(2, 3)) ==\n        TimeDelta(slab.advance(), -rational_t(2, 3)));\n  CHECK(Time(slab.advance(), rational_t(2, 3)) - Time(slab, 1) ==\n        TimeDelta(slab.advance(), rational_t(2, 3)));\n\n  CHECK_OP(Time(slab, 1), +, TimeDelta(slab.advance(), rational_t(1, 3)),\n           Time(slab.advance(), rational_t(1, 3)));\n  CHECK_OP(Time(slab, 1), -, TimeDelta(slab.advance(), -rational_t(1, 3)),\n           Time(slab.advance(), rational_t(1, 3)));\n  CHECK_OP(Time(slab, 0), +, TimeDelta(slab.retreat(), -rational_t(1, 3)),\n           Time(slab.retreat(), rational_t(2, 3)));\n  CHECK_OP(Time(slab, 0), -, TimeDelta(slab.retreat(), rational_t(1, 3)),\n           Time(slab.retreat(), rational_t(2, 3)));\n\n  // Hashing\n  const std::hash<Time> h{};\n  CHECK(h(Slab(0, 1).start()) == h(Slab(0, 2).start()));\n  CHECK(h(Slab(0, 1).start()) != h(Slab(0, 1).end()));\n  CHECK(h(Slab(0, 1).start()) == h(Slab(-2, 0).end()));\n  CHECK(h(Slab(-1, 0).end()) == h(Slab(-2, 0).end()));\n  CHECK(h(Time(Slab(0, 1), rational_t(1, 2))) !=\n        h(Time(Slab(0, 2), rational_t(1, 3))));\n\n  // Output\n  const std::string slab_str = get_output(slab);\n  CHECK(get_output(Time(slab, 0)) == slab_str + \":0/1\");\n  CHECK(get_output(Time(slab, 1)) == slab_str + \":1/1\");\n  CHECK(get_output(Time(slab, rational_t(3, 5))) == slab_str + \":3/5\");\n\n  const auto check_structural_compare = [](const Time& a, const Time& b) {\n    const Time::StructuralCompare sc{};\n    CHECK_FALSE(sc(a, a));\n    CHECK_FALSE(sc(b, b));\n    CHECK(sc(a, b) != sc(b, a));\n  };\n  check_structural_compare(Time(slab, rational_t(1, 3)),\n                           Time(slab, rational_t(2, 3)));\n  check_structural_compare(Time(slab, rational_t(1, 3)),\n                           Time(slab, rational_t(1, 2)));\n  check_structural_compare(Time(slab, rational_t(0, 1)),\n                           Time(slab.advance(), rational_t(0, 1)));\n\n  {\n    const Time time = slab.start() + slab.duration() * 3 / 5;\n    test_serialization(time);\n    CHECK(serialize_and_deserialize(time).value() == time.value());\n    // Check that this doesn't crash.\n    serialize_and_deserialize(Time{});\n  }\n}\n\nvoid test_time_roundoff_comparison() {\n  const double tstart_d = 0.68138945475734402635;\n  const double tend_d = 76.34481744714527451379;\n  // Make sure we're using values that will trigger rounding errors.\n  CHECK_FALSE(tstart_d + (tend_d - tstart_d) == tend_d);\n\n  const Slab slab(tstart_d, tend_d);\n\n  const double other_duration = 2. * slab.duration().value();\n\n  const Time a(slab, 0);\n  const Time b = a.with_slab(slab.retreat());\n  CHECK(b.slab() == slab.retreat());\n  CHECK(b.fraction() == 1);\n  const Time c = a.with_slab(slab.with_duration_from_start(other_duration));\n  CHECK(c.slab() == slab.with_duration_from_start(other_duration));\n  CHECK(c.fraction() == 0);\n  const Time d =\n      a.with_slab(slab.retreat().with_duration_to_end(other_duration));\n  CHECK(d.slab() == slab.retreat().with_duration_to_end(other_duration));\n  CHECK(d.fraction() == 1);\n\n  const std::array<Time, 4> equal_times{{a, b, c, d}};\n  for (const auto& t1 : equal_times) {\n    for (const auto& t2 : equal_times) {\n      CHECK(t1 == t2);\n      CHECK_FALSE(t1 != t2);\n      CHECK_FALSE(t1 < t2);\n      CHECK_FALSE(t1 > t2);\n      CHECK(t1 <= t2);\n      CHECK(t1 >= t2);\n    }\n\n    for (const auto& t2 : {b.slab().start(), d.slab().start()}) {\n      CHECK_FALSE(t1 == t2);\n      CHECK_FALSE(t2 == t1);\n      CHECK(t1 != t2);\n      CHECK(t2 != t1);\n      CHECK_FALSE(t1 < t2);\n      CHECK_FALSE(t2 > t1);\n      CHECK(t1 > t2);\n      CHECK(t2 < t1);\n      CHECK_FALSE(t1 <= t2);\n      CHECK_FALSE(t2 >= t1);\n      CHECK(t1 >= t2);\n      CHECK(t2 <= t1);\n    }\n\n    for (const auto& t2 : {a.slab().end(), c.slab().end()}) {\n      CHECK_FALSE(t1 == t2);\n      CHECK_FALSE(t2 == t1);\n      CHECK(t1 != t2);\n      CHECK(t2 != t1);\n      CHECK(t1 < t2);\n      CHECK(t2 > t1);\n      CHECK_FALSE(t1 > t2);\n      CHECK_FALSE(t2 < t1);\n      CHECK(t1 <= t2);\n      CHECK(t2 >= t1);\n      CHECK_FALSE(t1 >= t2);\n      CHECK_FALSE(t2 <= t1);\n    }\n  }\n}\n\nvoid test_time_delta() {\n  using rational_t = TimeDelta::rational_t;\n\n  const double tstart_d = 0.68138945475734402635;\n  const double tend_d = 76.34481744714527451379;\n  const double length_d = tend_d - tstart_d;\n  // Make sure we're using values that will trigger rounding errors.\n  CHECK_FALSE(tstart_d + length_d == tend_d);\n\n  const Slab slab(tstart_d, tend_d);\n  CHECK(TimeDelta(slab, 0).slab() == slab);\n  CHECK(TimeDelta(slab, rational_t(3, 5)).fraction() == rational_t(3, 5));\n\n  CHECK(TimeDelta(slab, 0).value() == 0);\n  CHECK(TimeDelta(slab, 1).value() == approx(length_d));\n  CHECK(TimeDelta(slab, rational_t(1, 5)).value() == approx(length_d / 5));\n  // TimeDeltas can be \"out of range\".\n  CHECK(TimeDelta(slab, -rational_t(1, 5)).value() == approx(-length_d / 5));\n  CHECK(TimeDelta(slab, 2).value() == approx(2 * length_d));\n\n  CHECK(TimeDelta(slab, rational_t(1, 2)).is_positive());\n  CHECK_FALSE(TimeDelta(slab, -rational_t(1, 2)).is_positive());\n  CHECK_FALSE(TimeDelta(slab, 0).is_positive());\n\n  {\n    const Slab slab2(10., 14.);\n    const TimeDelta delta(slab, rational_t(1, 3));\n    const TimeDelta delta2 = delta.with_slab(slab2);\n    CHECK(delta2.slab() == slab2);\n    CHECK(delta2.fraction() == delta.fraction());\n  }\n\n  check_cmp(TimeDelta(slab, rational_t(2, 5)),\n            TimeDelta(slab, rational_t(3, 5)));\n\n  CHECK(+TimeDelta(slab, rational_t(3, 5)) ==\n        TimeDelta(slab, rational_t(3, 5)));\n\n  CHECK(-TimeDelta(slab, rational_t(3, 5)) ==\n        TimeDelta(slab, -rational_t(3, 5)));\n\n  CHECK(TimeDelta(slab, rational_t(3, 5)) / TimeDelta(slab, rational_t(3, 5)) ==\n        1.);\n  CHECK(TimeDelta(slab, rational_t(3, 5)) / TimeDelta(slab, rational_t(2, 5)) ==\n        approx(1.5));\n  // This is the only operation that is valid arbitrarily cross-slab.\n  CHECK(TimeDelta(slab.advance().with_duration_from_start(2.345),\n                  rational_t(2, 3)) /\n            TimeDelta(slab, rational_t(4, 5)) ==\n        approx(2.345 * 2. / 3. / (length_d * 4. / 5.)));\n\n  CHECK(TimeDelta(slab, rational_t(2, 5)) + Time(slab, rational_t(1, 5)) ==\n        Time(slab, rational_t(3, 5)));\n\n  CHECK_OP(TimeDelta(slab, rational_t(1, 5)), +,\n           TimeDelta(slab, rational_t(2, 5)),\n           TimeDelta(slab, rational_t(3, 5)));\n  CHECK_OP(TimeDelta(slab, rational_t(1, 5)), -,\n           TimeDelta(slab, rational_t(2, 5)),\n           TimeDelta(slab, -rational_t(1, 5)));\n  CHECK_OP(TimeDelta(slab, rational_t(1, 5)), *, rational_t(2, 5),\n           TimeDelta(slab, rational_t(2, 25)));\n  CHECK_OP(TimeDelta(slab, rational_t(1, 5)), /, rational_t(2, 5),\n           TimeDelta(slab, rational_t(1, 2)));\n\n  CHECK(rational_t(2, 5) * TimeDelta(slab, rational_t(1, 5)) ==\n        TimeDelta(slab, rational_t(2, 25)));\n\n  CHECK(abs(TimeDelta(slab, rational_t(1, 5))) ==\n        TimeDelta(slab, rational_t(1, 5)));\n  CHECK(abs(TimeDelta(slab, -rational_t(1, 5))) ==\n        TimeDelta(slab, rational_t(1, 5)));\n\n  // Slab boundary arithmetic\n  CHECK(TimeDelta(slab.advance(), rational_t(1, 3)) + Time(slab, 1) ==\n        Time(slab.advance(), rational_t(1, 3)));\n  CHECK(TimeDelta(slab.retreat(), -rational_t(1, 3)) + Time(slab, 0) ==\n        Time(slab.retreat(), rational_t(2, 3)));\n\n  // Hashing\n  const std::hash<TimeDelta> h{};\n  CHECK(h(Slab(0.0, 1.0).duration()) != h(Slab(0.0, 2.0).duration()));\n  CHECK(h(Slab(0.0, 1.0).duration()) != h(Slab(0.0, 1.0).duration() / 2));\n\n  // Output\n  const std::string slab_str = get_output(slab);\n  CHECK(get_output(TimeDelta(slab, 0)) == slab_str + \":0/1\");\n  CHECK(get_output(TimeDelta(slab, 1)) == slab_str + \":1/1\");\n  CHECK(get_output(TimeDelta(slab, rational_t(3, 5))) == slab_str + \":3/5\");\n  CHECK(get_output(TimeDelta(slab, rational_t(3, -5))) == slab_str + \":-3/5\");\n\n  {\n    const TimeDelta dt = slab.duration() * 3 / 5;\n    test_serialization(dt);\n    CHECK(serialize_and_deserialize(dt).value() == dt.value());\n    // Check that this doesn't crash.\n    serialize_and_deserialize(TimeDelta{});\n  }\n}\n\n// Failure tests\n#ifdef __clang__\n#pragma GCC diagnostic ignored \"-Wunused-comparison\"\n#endif\n\nvoid test_assertions() {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(Time(Slab(0., 1.), -1), Catch::Matchers::ContainsSubstring(\n                                                \"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(Time(Slab(0., 1.), 2), Catch::Matchers::ContainsSubstring(\n                                               \"Out of range slab fraction\"));\n\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), Time::rational_t(1, 2)).with_slab(Slab(1., 2.)),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), Time::rational_t(1, 2)).with_slab(Slab(-1., 0.)),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), Time::rational_t(1, 2)).with_slab(Slab(0., 2.)),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0).with_slab(Slab(1., 2.)),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0).with_slab(Slab(-1., 1.)),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 1).with_slab(Slab(-1., 0.)),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 1).with_slab(Slab(0., 2.)),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 1) < Time(Slab(0., 2.), 1),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n\n  CHECK_THROWS_WITH(Time(Slab(0., 1.), 0) - Time(Slab(2., 3.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't subtract times from different slabs\"));\n  CHECK_THROWS_WITH(Time(Slab(0., 1.), 1) - Time(Slab(-1., 0.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't subtract times from different slabs\"));\n  CHECK_THROWS_WITH(Time(Slab(-1., 0.), 0) - Time(Slab(0., 1.), 1),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't subtract times from different slabs\"));\n\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) + TimeDelta(Slab(0., 1.), 2),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) += TimeDelta(Slab(0., 1.), 2),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) + TimeDelta(Slab(0., 1.), -2),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) += TimeDelta(Slab(0., 1.), -2),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) - TimeDelta(Slab(0., 1.), 2),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) -= TimeDelta(Slab(0., 1.), 2),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) - TimeDelta(Slab(0., 1.), -2),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) -= TimeDelta(Slab(0., 1.), -2),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) + TimeDelta(Slab(1., 2.), 0),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) += TimeDelta(Slab(1., 2.), 0),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) - TimeDelta(Slab(1., 2.), 0),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n  CHECK_THROWS_WITH(\n      Time(Slab(0., 1.), 0) -= TimeDelta(Slab(1., 2.), 0),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n\n  CHECK_THROWS_WITH(TimeDelta(Slab(0., 1.), 0) < TimeDelta(Slab(1., 2.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't check cross-slab TimeDelta inequalities\"));\n  CHECK_THROWS_WITH(TimeDelta(Slab(0., 1.), 0) > TimeDelta(Slab(1., 2.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't check cross-slab TimeDelta inequalities\"));\n  CHECK_THROWS_WITH(TimeDelta(Slab(0., 1.), 0) <= TimeDelta(Slab(1., 2.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't check cross-slab TimeDelta inequalities\"));\n  CHECK_THROWS_WITH(TimeDelta(Slab(0., 1.), 0) >= TimeDelta(Slab(1., 2.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't check cross-slab TimeDelta inequalities\"));\n\n  CHECK_THROWS_WITH(\n      TimeDelta(Slab(0., 1.), 2) + Time(Slab(0., 1.), 0),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      TimeDelta(Slab(0., 1.), -2) + Time(Slab(0., 1.), 0),\n      Catch::Matchers::ContainsSubstring(\"Out of range slab fraction\"));\n  CHECK_THROWS_WITH(\n      TimeDelta(Slab(1., 2.), 0) + Time(Slab(0., 1.), 0),\n      Catch::Matchers::Matches(\"(.|\\\\n)*Can't move .* to slab(.|\\\\n)*\"));\n\n  CHECK_THROWS_WITH(TimeDelta(Slab(0., 1.), 0) += TimeDelta(Slab(1., 2.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't add TimeDeltas from different slabs\"));\n  CHECK_THROWS_WITH(TimeDelta(Slab(0., 1.), 0) + TimeDelta(Slab(1., 2.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't add TimeDeltas from different slabs\"));\n  CHECK_THROWS_WITH(TimeDelta(Slab(0., 1.), 0) -= TimeDelta(Slab(1., 2.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't subtract TimeDeltas from different slabs\"));\n  CHECK_THROWS_WITH(TimeDelta(Slab(0., 1.), 0) - TimeDelta(Slab(1., 2.), 0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Can't subtract TimeDeltas from different slabs\"));\n#endif  // SPECTRE_DEBUG\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Time\", \"[Unit][Time]\") {\n  test_time();\n  test_time_roundoff_comparison();\n  test_time_delta();\n  test_assertions();\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_TimeSequence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstdint>\n#include <memory>\n#include <optional>\n#include <pup.h>\n#include <vector>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test_evenly_spaced() {\n  {\n    const TimeSequences::EvenlySpaced<std::uint64_t> constructed(3, 5);\n    const auto factory = TestHelpers::test_factory_creation<\n        TimeSequence<std::uint64_t>,\n        TimeSequences::EvenlySpaced<std::uint64_t>>(\n        \"EvenlySpaced:\\n\"\n        \"  Interval: 3\\n\"\n        \"  Offset: 5\\n\");\n    const auto check = [&constructed, &factory](const std::uint64_t arg,\n                                                const std::uint64_t result) {\n      using Result = std::array<std::optional<std::uint64_t>, 3>;\n      const auto expected = result >= 3\n                                ? Result{{result - 3, result, result + 3}}\n                                : Result{{{}, result, result + 3}};\n      CHECK(constructed.times_near(arg) == expected);\n      CHECK(factory->times_near(arg) == expected);\n      CHECK(serialize_and_deserialize(factory)->times_near(arg) == expected);\n    };\n    check(0, 2);\n    check(1, 2);\n    check(2, 2);\n    check(3, 2);\n    check(4, 5);\n    check(5, 5);\n    check(6, 5);\n    check(7, 8);\n    check(8, 8);\n    check(9, 8);\n  }\n  {\n    const TimeSequences::EvenlySpaced<double> constructed(3.75, 4.0625);\n    const auto factory =\n        TestHelpers::test_factory_creation<TimeSequence<double>,\n                                           TimeSequences::EvenlySpaced<double>>(\n            \"EvenlySpaced:\\n\"\n            \"  Interval: 3.75\\n\"\n            \"  Offset: 4.0625\\n\");\n    const auto check = [&constructed, &factory](const double arg,\n                                                const double result) {\n      const std::array<std::optional<double>, 3> expected{\n          {result - 3.75, result, result + 3.75}};\n      CHECK(constructed.times_near(arg) == expected);\n      CHECK(factory->times_near(arg) == expected);\n      CHECK(serialize_and_deserialize(factory)->times_near(arg) == expected);\n    };\n    check(4.0625, 4.0625);\n    check(0.0, 4.0625 - 3.75);\n    check(19.0, 4.0625 + 4.0 * 3.75);\n    check(-11.0, 4.0625 - 4.0 * 3.75);\n  }\n}\n\nvoid test_specified() {\n  {\n    using Result = std::array<std::optional<std::uint64_t>, 3>;\n    const TimeSequences::Specified<std::uint64_t> constructed(\n        {4, 8, 0, 4, 4, 3, 4});\n    const auto factory = TestHelpers::test_factory_creation<\n        TimeSequence<std::uint64_t>, TimeSequences::Specified<std::uint64_t>>(\n        \"Specified:\\n\"\n        \"  Values: [4, 8, 0, 4, 4, 3, 4]\\n\");\n    const auto check = [&constructed, &factory](const std::uint64_t arg,\n                                                const Result& expected) {\n      CHECK(constructed.times_near(arg) == expected);\n      CHECK(factory->times_near(arg) == expected);\n      CHECK(serialize_and_deserialize(factory)->times_near(arg) == expected);\n    };\n    check(0, {{{}, 0, 3}});\n    check(1, {{{}, 0, 3}});\n    check(2, {{0, 3, 4}});\n    check(3, {{0, 3, 4}});\n    check(4, {{3, 4, 8}});\n    check(5, {{3, 4, 8}});\n    // 6 is a halfway point, and we choose not to specify which side\n    // it prefers.\n    check(7, {{4, 8, {}}});\n    check(8, {{4, 8, {}}});\n    check(9, {{4, 8, {}}});\n\n    const TimeSequences::Specified<std::uint64_t> zero_elements({});\n    CHECK(zero_elements.times_near(5) == Result{});\n    const TimeSequences::Specified<std::uint64_t> one_element({3});\n    CHECK(one_element.times_near(5) == Result{{{}, 3, {}}});\n    const TimeSequences::Specified<std::uint64_t> two_elements({3, 6});\n    CHECK(two_elements.times_near(2) == Result{{{}, 3, 6}});\n    CHECK(two_elements.times_near(3) == Result{{{}, 3, 6}});\n    CHECK(two_elements.times_near(4) == Result{{{}, 3, 6}});\n    CHECK(two_elements.times_near(5) == Result{{3, 6, {}}});\n    CHECK(two_elements.times_near(6) == Result{{3, 6, {}}});\n    CHECK(two_elements.times_near(7) == Result{{3, 6, {}}});\n  }\n  {\n    using Result = std::array<std::optional<double>, 3>;\n    const TimeSequences::Specified<double> constructed(\n        {-5.1, 7.2, -2.3, 0.0, -5.1, -5.1, 3.4, 4.5});\n    const auto factory =\n        TestHelpers::test_factory_creation<TimeSequence<double>,\n                                           TimeSequences::Specified<double>>(\n            \"Specified:\\n\"\n            \"  Values: [-5.1, 7.2, -2.3, 0.0, -5.1, -5.1, 3.4, 4.5]\\n\");\n    const auto check = [&constructed, &factory](const double arg,\n                                                const Result& expected) {\n      CHECK(constructed.times_near(arg) == expected);\n      CHECK(factory->times_near(arg) == expected);\n      CHECK(serialize_and_deserialize(factory)->times_near(arg) == expected);\n    };\n    check(-8.9, {{{}, -5.1, -2.3}});\n    check(-5.1, {{{}, -5.1, -2.3}});\n    check(-4.7, {{{}, -5.1, -2.3}});\n    check(-2.8, {{-5.1, -2.3, 0.0}});\n    check(-2.3, {{-5.1, -2.3, 0.0}});\n    check(4.5, {{3.4, 4.5, 7.2}});\n    check(4.6, {{3.4, 4.5, 7.2}});\n    check(7.0, {{4.5, 7.2, {}}});\n    check(7.2, {{4.5, 7.2, {}}});\n    check(8.1, {{4.5, 7.2, {}}});\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSequence\", \"[Unit][Time]\") {\n  register_classes_with_charm(TimeSequences::all_time_sequences<double>{});\n  register_classes_with_charm(\n      TimeSequences::all_time_sequences<std::uint64_t>{});\n\n  test_evenly_spaced();\n  test_specified();\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_TimeStepId.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdint>\n#include <functional>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nnamespace {\nvoid check(const bool time_runs_forward) {\n  using Hash = std::hash<TimeStepId>;\n\n  const Slab slab(1.25, 3.5);\n  const Time start = time_runs_forward ? slab.start() : slab.end();\n  const Time end = time_runs_forward ? slab.end() : slab.start();\n  const TimeDelta step = end - start;\n\n  CHECK(TimeStepId(time_runs_forward, 4, start + step / 3) ==\n        TimeStepId(time_runs_forward, 4, start + step / 3, 0, step,\n                   (start + step / 3).value()));\n\n  CHECK_FALSE(TimeStepId(time_runs_forward, 4, start + step / 3, 2, step,\n                         (start + step / 2).value())\n                  .is_at_slab_boundary());\n  CHECK_FALSE(TimeStepId(time_runs_forward, 4, start, 2, step, start.value())\n                  .is_at_slab_boundary());\n  CHECK_FALSE(TimeStepId(time_runs_forward, 4, start, 2, step, end.value())\n                  .is_at_slab_boundary());\n  CHECK_FALSE(\n      TimeStepId(time_runs_forward, 4, start + step / 3).is_at_slab_boundary());\n  CHECK_FALSE(TimeStepId(time_runs_forward, 4, start, 1, step, start.value())\n                  .is_at_slab_boundary());\n  CHECK_FALSE(TimeStepId(time_runs_forward, 4, start, 1, step, end.value())\n                  .is_at_slab_boundary());\n  CHECK(TimeStepId(time_runs_forward, 4, start).is_at_slab_boundary());\n  CHECK(TimeStepId(time_runs_forward, 4, end).is_at_slab_boundary());\n\n  CHECK(TimeStepId(time_runs_forward, 5, start).slab_number() == 5);\n  CHECK(TimeStepId(time_runs_forward, 5, start).step_time().slab() == slab);\n  CHECK(TimeStepId(time_runs_forward, 5, end).slab_number() == 6);\n  CHECK(TimeStepId(time_runs_forward, 5, end).step_time().slab() ==\n        slab.advance_towards(step));\n\n  CHECK(\n      TimeStepId(time_runs_forward, 4, start + step / 2).next_step(step / 4) ==\n      TimeStepId(time_runs_forward, 4, start + step * 3 / 4));\n  CHECK(TimeStepId(time_runs_forward, 4, start + step / 2, 1, step / 4,\n                   end.value())\n            .next_step(step / 4) ==\n        TimeStepId(time_runs_forward, 4, start + step * 3 / 4));\n  CHECK(TimeStepId(time_runs_forward, 4, start + step / 2, 1, step / 4,\n                   end.value())\n            .next_substep(step / 4, 1.0 / 8.0) ==\n        TimeStepId(time_runs_forward, 4, start + step / 2, 2, step / 4,\n                   (start + step * 17 / 32).value()));\n\n  const TimeStepId id(time_runs_forward, 4, start + step / 3, 2, step / 2,\n                      (start + step / 2).value());\n  CHECK(id.step_size() == step / 2);\n\n  CHECK(id == id);\n  CHECK_FALSE(id != id);\n  CHECK(id == TimeStepId(id));\n\n  const auto check_comparisons = [&id](const int64_t slab_delta,\n                                       const TimeDelta& step_time_delta,\n                                       const int64_t substep_delta,\n                                       const TimeDelta& substep_time_delta) {\n    const TimeStepId id2(id.time_runs_forward(),\n                         id.slab_number() + slab_delta,\n                         id.step_time() + step_time_delta,\n                         id.substep() + static_cast<uint64_t>(substep_delta),\n                         id.step_size(),\n                         id.substep_time() + substep_time_delta.value());\n    check_cmp(id, id2);\n    CHECK(Hash{}(id) != Hash{}(id2));\n  };\n\n  check_comparisons(1, 0 * step, 0, 0 * step);\n  check_comparisons(0, step / 8, 0, 0 * step);\n  check_comparisons(0, 0 * step, 1, 0 * step);\n\n  check_comparisons(1, -step / 4, 0, 0 * step);\n  check_comparisons(1, 0 * step, -1, 0 * step);\n  check_comparisons(1, 0 * step, 0, -step / 8);\n  check_comparisons(0, step / 8, -1, 0 * step);\n  check_comparisons(0, step / 16, 0, -step / 16);\n  check_comparisons(0, 0 * step, 1, -step / 8);\n\n  test_serialization(id);\n\n  {\n    const TimeStepId id2(id.time_runs_forward(), id.slab_number() + 1,\n                         id.step_time());\n    check_cmp(id, id2);\n    CHECK(Hash{}(id) != Hash{}(id2));\n    test_serialization(id2);\n  }\n\n  CHECK(get_output(id) == \"4:\" + get_output(id.step_time()) +\n                              \":2:\" + get_output(id.substep_time()));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeStepId\", \"[Unit][Time]\") {\n  check(true);\n  check(false);\n}\n"
  },
  {
    "path": "tests/Unit/Time/Test_TimeStepRequest.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n\nnamespace {\nvoid check_eq(std::optional<double> TimeStepRequest::* const field) {\n  const TimeStepRequest request{{1.0}, {2.0}, {3.0}, {4.0}, {5.0}};\n  auto request2 = request;\n  (request2.*field).reset();\n  CHECK(request != request2);\n  CHECK_FALSE(request == request2);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeStepRequest\", \"[Unit][Time]\") {\n  CHECK(TimeStepRequest{{1.0}, {2.0}, {3.0}, {4.0}, {5.0}} ==\n        TimeStepRequest{{1.0}, {2.0}, {3.0}, {4.0}, {5.0}});\n  CHECK_FALSE(TimeStepRequest{{1.0}, {2.0}, {3.0}, {4.0}, {5.0}} !=\n              TimeStepRequest{{1.0}, {2.0}, {3.0}, {4.0}, {5.0}});\n\n  check_eq(&TimeStepRequest::size_goal);\n  check_eq(&TimeStepRequest::size);\n  check_eq(&TimeStepRequest::end);\n  check_eq(&TimeStepRequest::size_hard_limit);\n  check_eq(&TimeStepRequest::end_hard_limit);\n\n  test_serialization(TimeStepRequest{{1.0}, {2.0}, {3.0}, {4.0}, {5.0}});\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/Test_TimeStepRequestProcessor.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <optional>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Time/TimeStepRequest.hpp\"\n#include \"Time/TimeStepRequestProcessor.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n// Test processing requests in all orders, with different subsets\n// combined first.\nvoid test_permutations_roundoff(const std::vector<TimeStepRequest>& requests,\n                                const double start, const double goal,\n                                const std::optional<double>& expected_goal,\n                                const double expected_step,\n                                const double expected_end) {\n  std::vector<size_t> ordering(requests.size());\n  alg::iota(ordering, 0_st);\n  std::vector<size_t> partition{};\n  partition.reserve(requests.size());\n  do {\n    partition.clear();\n    partition.push_back(requests.size());\n    for (;;) {\n      {\n        TimeStepRequestProcessor processor(goal > 0.0);\n        TimeStepRequestProcessor processor2(goal > 0.0);\n        size_t index = 0;\n        for (const size_t partition_element : partition) {\n          TimeStepRequestProcessor element_processor(goal > 0.0);\n          for (size_t i = 0; i < partition_element; ++i) {\n            element_processor.process(requests[ordering[index]]);\n            ++index;\n          }\n          processor2 = processor + element_processor;\n          processor += serialize_and_deserialize(element_processor);\n          CHECK(processor == processor2);\n          CHECK_FALSE(processor != processor2);\n        }\n        CHECK(processor.new_step_size_goal() == expected_goal);\n        CHECK(processor.step_size(start, goal) == expected_step);\n        CHECK(processor.step_end(start, goal) == expected_end);\n        CHECK(processor2.new_step_size_goal() == expected_goal);\n        CHECK(processor2.step_size(start, goal) == expected_step);\n        CHECK(processor2.step_end(start, goal) == expected_end);\n      }\n\n      if (requests.empty() or partition.size() == requests.size()) {\n        break;\n      }\n      size_t trailing_ones = 0;\n      while (partition.back() == 1) {\n        ++trailing_ones;\n        partition.pop_back();\n      }\n      --partition.back();\n      partition.push_back(trailing_ones + 1);\n    }\n  } while (std::next_permutation(ordering.begin(), ordering.end()));\n}\n\n// Test processing requests in all orders, with different subsets\n// combined first.  This version computes the step end from the step\n// size, so only works for the non-roundoff-sensitive tests, but it\n// removes a lot of redundancy in those.\nvoid test_permutations(const std::vector<TimeStepRequest>& requests,\n                       const double start, const double goal,\n                       const std::optional<double>& expected_goal,\n                       const double expected_step) {\n  test_permutations_roundoff(requests, start, goal, expected_goal,\n                             expected_step, start + expected_step);\n}\n\nvoid test_with_roundoff(const double start, const double step,\n                        const double end) {\n  CAPTURE(start);\n  CAPTURE(step);\n  CAPTURE(end);\n  test_permutations_roundoff({}, start, step, {}, step, start + step);\n\n  test_permutations_roundoff({{.size_goal = step}}, start, 0.5 * step, {step},\n                             step, start + step);\n  test_permutations_roundoff({{.size = step}}, start, 2.0 * step, {}, step,\n                             start + step);\n  test_permutations_roundoff({{.end = end}}, start, 2.0 * step, {}, end - start,\n                             end);\n\n  test_permutations_roundoff({{.size_goal = 0.5 * step}}, start, step,\n                             {0.5 * step}, 0.5 * step, start + 0.5 * step);\n  test_permutations_roundoff({{.size = 0.5 * step}}, start, step, {},\n                             0.5 * step, start + 0.5 * step);\n  test_permutations_roundoff({{.size_goal = 2.0 * step}}, start, step,\n                             {2.0 * step}, 2.0 * step, start + 2.0 * step);\n  test_permutations_roundoff({{.size = 2.0 * step}}, start, step, {}, step,\n                             start + step);\n\n  const double far_end = end + 0.5 * step;\n  const double close_end = start + 0.5 * step;\n  test_permutations_roundoff({{.end = close_end}}, start, step, {},\n                             close_end - start, close_end);\n  test_permutations_roundoff({{.end = far_end}}, start, step, {}, step,\n                             start + step);\n\n  // Test using the looser limit when two are given.\n  test_permutations_roundoff({{.size = step, .end = start + 0.5 * step}}, start,\n                             2.0 * step, {}, step, start + step);\n  test_permutations_roundoff({{.size = 0.5 * step, .end = end}}, start,\n                             2.0 * step, {}, end - start, end);\n}\n\nvoid test_processor(const bool time_runs_forward) {\n  const double time_sign = time_runs_forward ? 1.0 : -1.0;\n\n  {\n    const double start = 2.0 * time_sign;\n    const double end = 5.0 * time_sign;\n    const double step = end - start;\n    // Ignore roundoff subtleties for this part of the test.\n    CHECK(start + step == end);\n\n    {\n      TimeStepRequestProcessor processor(time_runs_forward);\n      CHECK(processor == TimeStepRequestProcessor(time_runs_forward));\n      CHECK_FALSE(processor != TimeStepRequestProcessor(time_runs_forward));\n      CHECK(processor != TimeStepRequestProcessor(not time_runs_forward));\n      CHECK_FALSE(processor == TimeStepRequestProcessor(not time_runs_forward));\n      processor.process({.end = end});\n      CHECK(processor != TimeStepRequestProcessor(time_runs_forward));\n      CHECK_FALSE(processor == TimeStepRequestProcessor(time_runs_forward));\n    }\n\n    // No requests.\n    test_permutations({}, start, step, {}, step);\n    test_permutations({{}}, start, step, {}, step);\n    test_permutations({{}, {}}, start, step, {}, step);\n\n    // Simple goal setting requests.\n    test_permutations({{.size_goal = 0.5 * step}}, start, step, {0.5 * step},\n                      0.5 * step);\n    test_permutations({{.size_goal = 2.0 * step}}, start, step, {2.0 * step},\n                      2.0 * step);\n\n    // Simple step size requests.\n    test_permutations({{.size = 0.5 * step}}, start, step, {}, 0.5 * step);\n    test_permutations({{.size = 2.0 * step}}, start, step, {}, step);\n\n    // Simple step end requests.\n    test_permutations({{.end = start + 0.5 * step}}, start, step, {},\n                      0.5 * step);\n    test_permutations({{.end = start + 2.0 * step}}, start, step, {}, step);\n\n    // Multiple requests of the same type.\n    test_permutations({{.size_goal = 0.5 * step}, {.size_goal = 0.25 * step}},\n                      start, step, {0.25 * step}, 0.25 * step);\n    test_permutations({{.size = 0.5 * step}, {.size = 0.25 * step}}, start,\n                      step, {}, 0.25 * step);\n    test_permutations(\n        {{.end = start + 0.5 * step}, {.end = start + 0.25 * step}}, start,\n        step, {}, 0.25 * step);\n\n    // Multiple requests of different types.\n    // size_goal wins\n    test_permutations({{.size_goal = 0.5 * step}, {.size = 0.6 * step}}, start,\n                      step, {0.5 * step}, 0.5 * step);\n    test_permutations({{.size_goal = 0.5 * step}, {.end = start + 0.6 * step}},\n                      start, step, {0.5 * step}, 0.5 * step);\n    test_permutations({{.size_goal = 0.5 * step},\n                       {.size = 0.6 * step},\n                       {.end = start + 0.6 * step}},\n                      start, step, {0.5 * step}, 0.5 * step);\n    // size wins\n    test_permutations({{.size_goal = 0.6 * step}, {.size = 0.5 * step}}, start,\n                      step, {0.6 * step}, 0.5 * step);\n    test_permutations({{.size = 0.5 * step}, {.end = start + 0.6 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size_goal = 0.6 * step},\n                       {.size = 0.5 * step},\n                       {.end = start + 0.6 * step}},\n                      start, step, {0.6 * step}, 0.5 * step);\n    // end wins\n    test_permutations({{.size_goal = 0.6 * step}, {.end = start + 0.5 * step}},\n                      start, step, {0.6 * step}, 0.5 * step);\n    test_permutations({{.size = 0.6 * step}, {.end = start + 0.5 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size_goal = 0.6 * step},\n                       {.size = 0.6 * step},\n                       {.end = start + 0.5 * step}},\n                      start, step, {0.6 * step}, 0.5 * step);\n\n    // Requests with both .size and .end are fulfilled if either is\n    // used without adjustment.\n    test_permutations({{.size_goal = 0.6 * step,\n                        .size = 0.4 * step,\n                        .end = start + 0.5 * step}},\n                      start, step, {0.6 * step}, 0.5 * step);\n    test_permutations({{.size_goal = 0.6 * step,\n                        .size = 0.5 * step,\n                        .end = start + 0.4 * step}},\n                      start, step, {0.6 * step}, 0.5 * step);\n    test_permutations({{.size = 0.4 * step, .end = start + 0.5 * step}}, start,\n                      step, {}, 0.5 * step);\n    test_permutations({{.size = 0.5 * step, .end = start + 0.4 * step}}, start,\n                      step, {}, 0.5 * step);\n    // .size_goal doesn't participate in that.\n    test_permutations({{.size_goal = 0.5 * step, .end = start + 0.6 * step}},\n                      start, step, {0.5 * step}, 0.5 * step);\n    test_permutations({{.size_goal = 0.6 * step, .end = start + 0.5 * step}},\n                      start, step, {0.6 * step}, 0.5 * step);\n\n    // Repeats don't affect the result.\n    test_permutations({{.size = 0.4 * step, .end = start + 0.5 * step},\n                       {.size = 0.4 * step, .end = start + 0.5 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.5 * step, .end = start + 0.4 * step},\n                       {.size = 0.5 * step, .end = start + 0.4 * step}},\n                      start, step, {}, 0.5 * step);\n\n    // Less stringent requirements don't affect that, either.\n    test_permutations(\n        {{.size = 0.4 * step, .end = start + 0.5 * step}, {.size = 0.6 * step}},\n        start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.4 * step, .end = start + 0.5 * step},\n                       {.end = start + 0.6 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations(\n        {{.size = 0.5 * step, .end = start + 0.4 * step}, {.size = 0.6 * step}},\n        start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.5 * step, .end = start + 0.4 * step},\n                       {.end = start + 0.6 * step}},\n                      start, step, {}, 0.5 * step);\n\n    // Requests with both .size and .end usually must satisfy both in\n    // other cases.\n    test_permutations({{.size = 0.5 * step, .end = start + 0.6 * step},\n                       {.size = 0.55 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.6 * step, .end = start + 0.5 * step},\n                       {.size = 0.55 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.5 * step, .end = start + 0.6 * step},\n                       {.end = start + 0.55 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.6 * step, .end = start + 0.5 * step},\n                       {.end = start + 0.55 * step}},\n                      start, step, {}, 0.5 * step);\n\n    // Including when other requests partially duplicate them.\n    test_permutations(\n        {{.size = 0.5 * step, .end = start + 0.6 * step}, {.size = 0.5 * step}},\n        start, step, {}, 0.5 * step);\n    test_permutations(\n        {{.size = 0.6 * step, .end = start + 0.5 * step}, {.size = 0.5 * step}},\n        start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.5 * step, .end = start + 0.6 * step},\n                       {.end = start + 0.5 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.6 * step, .end = start + 0.5 * step},\n                       {.end = start + 0.5 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.5 * step, .end = start + 0.6 * step},\n                       {.size = 0.5 * step},\n                       {.end = start + 0.6 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.6 * step, .end = start + 0.5 * step},\n                       {.size = 0.6 * step},\n                       {.end = start + 0.5 * step}},\n                      start, step, {}, 0.5 * step);\n\n    // And when multiple different ones occur.\n    test_permutations({{.size = 0.7 * step, .end = start + 0.6 * step},\n                       {.size = 0.65 * step, .end = start + 0.5 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.7 * step, .end = start + 0.6 * step},\n                       {.size = 0.5 * step, .end = start + 0.65 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.6 * step, .end = start + 0.7 * step},\n                       {.size = 0.65 * step, .end = start + 0.5 * step}},\n                      start, step, {}, 0.5 * step);\n    test_permutations({{.size = 0.6 * step, .end = start + 0.7 * step},\n                       {.size = 0.5 * step, .end = start + 0.65 * step}},\n                      start, step, {}, 0.5 * step);\n\n    // Test with several entries.\n    test_permutations({{.size_goal = 0.6 * step, .size = 0.5 * step},\n                       {.size_goal = 0.7 * step},\n                       {.size = 0.6 * step},\n                       {.end = start + 0.6 * step},\n                       {.end = start + 0.7 * step}},\n                      start, step, {0.6 * step}, 0.5 * step);\n\n    // Hard limits don't affect the suggestion\n    test_permutations({{.size_hard_limit = 0.5 * step}}, start, step, {}, step);\n    test_permutations({{.end_hard_limit = start + 0.5 * step}}, start, step, {},\n                      step);\n\n    {\n      TimeStepRequestProcessor processor(step > 0.0);\n      processor.process({.size_hard_limit = step, .end_hard_limit = end});\n      processor.error_on_hard_limit(0.9 * step, start);\n      processor.error_on_hard_limit(step, end);\n      CHECK_THROWS_WITH(\n          processor.error_on_hard_limit(1.1 * step, end),\n          Catch::Matchers::ContainsSubstring(\"Could not adjust step below \") and\n              Catch::Matchers::ContainsSubstring(\n                  \" to meet maximum step size \"));\n      CHECK_THROWS_WITH(\n          processor.error_on_hard_limit(step, start + 1.1 * step),\n          Catch::Matchers::ContainsSubstring(\n              \"Could not adjust step to before \") and\n              Catch::Matchers::ContainsSubstring(\" to avoid exceeding time \"));\n    }\n  }\n\n  {\n    const double start = -1.0e20 * time_sign;\n    const double end = 1.0 * time_sign;\n    const double step = 1.0e20 * time_sign;\n    CHECK(end - start == step);\n    CHECK(start + step != end);\n    test_with_roundoff(start, step, end);\n  }\n\n  {\n    const double start = 1.0 * time_sign;\n    const double end = (1.0 + 1.0e-10) * time_sign;\n    const double step = (1.0e-10 + 1.0e-20) * time_sign;\n    CHECK(end - start != step);\n    CHECK(start + step == end);\n    test_with_roundoff(start, step, end);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeStepRequestProcessor\", \"[Unit][Time]\") {\n  test_processor(true);\n  test_processor(false);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/Test_UpdateU.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <memory>\n#include <optional>\n#include <utility>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Prefixes.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Tags/HistoryEvolvedVariables.hpp\"\n#include \"Time/Tags/StepperErrorEstimatesEnabled.hpp\"\n#include \"Time/Tags/StepperErrorTolerances.hpp\"\n#include \"Time/Tags/StepperErrors.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/Tags/TimeStepper.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Time/UpdateU.hpp\"\n#include \"Time/UpdateU.tpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Rational.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Var : db::SimpleTag {\n  using type = double;\n};\n\nstruct AlternativeVar : db::SimpleTag {\n  using type = double;\n};\n\nstruct SingleVariableSystem {\n  using variables_tag = Var;\n};\n\nstruct TwoVariableSystem {\n  using variables_tag = tmpl::list<Var, AlternativeVar>;\n};\n\ntemplate <typename System, bool LocalTimeStepping, bool AlternativeUpdates>\nvoid test_integration() {\n  using history_tag = Tags::HistoryEvolvedVariables<Var>;\n  using alternative_history_tag = Tags::HistoryEvolvedVariables<AlternativeVar>;\n\n  const Slab slab(1., 3.);\n  const TimeStepId initial_id(true, 0, slab.start());\n  const TimeDelta time_step = slab.duration() / 2;\n  std::unique_ptr<TimeStepper> time_stepper =\n      std::make_unique<TimeSteppers::Rk3HesthavenSsp>();\n\n  const auto rhs = [](const auto t, const auto y) {\n    return 2. * t - 2. * (y - t * t);\n  };\n\n  auto box = db::create<\n      db::AddSimpleTags<\n          Tags::ConcreteTimeStepper<TimeStepper>, Tags::TimeStepId,\n          Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n          ::Tags::StepperErrorEstimatesEnabled, Var, history_tag,\n          AlternativeVar, alternative_history_tag,\n          Tags::StepperErrorTolerances<Var>, Tags::StepperErrors<Var>,\n          Tags::StepperErrorTolerances<AlternativeVar>,\n          Tags::StepperErrors<AlternativeVar>>,\n      time_stepper_ref_tags<TimeStepper>>(\n      std::move(time_stepper), initial_id,\n      time_stepper->next_time_id(initial_id, time_step), time_step, false, 1.,\n      typename history_tag::type{3}, 1.,\n      typename alternative_history_tag::type{3},\n      Tags::StepperErrorTolerances<Var>::type{},\n      Tags::StepperErrors<Var>::type{},\n      Tags::StepperErrorTolerances<AlternativeVar>::type{},\n      Tags::StepperErrors<AlternativeVar>::type{});\n\n  // The exact answer is y = x^2, but the integrator would need a\n  // smaller step size to get that accurately.\n  const std::array<double, 3> expected_values{{3., 3., 10. / 3.}};\n\n  for (size_t substep = 0; substep < 3; ++substep) {\n    db::mutate<history_tag, alternative_history_tag>(\n        [&rhs](const gsl::not_null<typename history_tag::type*> history,\n               const gsl::not_null<typename alternative_history_tag::type*>\n                   alternative_history,\n               const TimeStepId& time_step_id, const double vars) {\n          history->insert(time_step_id, vars,\n                          rhs(time_step_id.substep_time(), vars));\n          *alternative_history = *history;\n        },\n        make_not_null(&box), db::get<Tags::TimeStepId>(box), db::get<Var>(box));\n\n    db::mutate_apply<UpdateU<System, LocalTimeStepping>>(make_not_null(&box));\n    CHECK(db::get<Var>(box) == approx(gsl::at(expected_values, substep)));\n    if (AlternativeUpdates) {\n      CHECK(db::get<AlternativeVar>(box) ==\n            approx(gsl::at(expected_values, substep)));\n    } else {\n      CHECK(db::get<AlternativeVar>(box) == 1.0);\n    }\n\n    db::mutate<Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, history_tag>(\n        [&time_step](const gsl::not_null<TimeStepId*> time_step_id,\n                     const gsl::not_null<TimeStepId*> next_time_step_id,\n                     const gsl::not_null<typename history_tag::type*> history,\n                     const TimeStepper& stepper) {\n          *time_step_id = *next_time_step_id;\n          *next_time_step_id = stepper.next_time_id(*time_step_id, time_step);\n          stepper.clean_history(history);\n        },\n        make_not_null(&box), db::get<Tags::TimeStepper<TimeStepper>>(box));\n  }\n}\n\ntemplate <bool LocalTimeStepping>\nvoid test_stepper_error() {\n  using variables_tag = Var;\n  using history_tag = Tags::HistoryEvolvedVariables<variables_tag>;\n\n  const Slab slab(1., 3.);\n  const TimeStepId initial_id(true, 0, slab.start());\n  const TimeDelta initial_time_step = slab.duration() / 2;\n  std::unique_ptr<TimeStepper> time_stepper =\n      std::make_unique<TimeSteppers::Rk3HesthavenSsp>();\n\n  auto box = db::create<\n      db::AddSimpleTags<Tags::ConcreteTimeStepper<TimeStepper>,\n                        Tags::TimeStepId, Tags::Next<Tags::TimeStepId>,\n                        Tags::TimeStep, ::Tags::StepperErrorEstimatesEnabled,\n                        ::Tags::StepperErrorTolerances<variables_tag>,\n                        variables_tag, history_tag,\n                        Tags::StepperErrors<variables_tag>>,\n      time_stepper_ref_tags<TimeStepper>>(\n      std::move(time_stepper), initial_id,\n      time_stepper->next_time_id(initial_id, initial_time_step),\n      initial_time_step, true,\n      StepperErrorTolerances{\n          .estimates = StepperErrorTolerances::Estimates::StepperOrder,\n          .absolute = 1.0,\n          .relative = 0.0},\n      1., history_tag::type{3}, Tags::StepperErrors<variables_tag>::type{});\n\n  const auto do_substep = [&box](const bool repeat_substep = false) {\n    db::mutate<history_tag>(\n        [](const gsl::not_null<typename history_tag::type*> history,\n           const TimeStepId& time_step_id,\n           const double vars) { history->insert(time_step_id, vars, vars); },\n        make_not_null(&box), db::get<Tags::TimeStepId>(box),\n        db::get<variables_tag>(box));\n\n    if (repeat_substep) {\n      db::mutate_apply<UpdateU<SingleVariableSystem, LocalTimeStepping>>(\n          make_not_null(&box));\n    }\n\n    db::mutate_apply<UpdateU<SingleVariableSystem, LocalTimeStepping>>(\n        make_not_null(&box));\n\n    db::mutate<Tags::TimeStepId, Tags::Next<Tags::TimeStepId>, Tags::TimeStep,\n               history_tag>(\n        [](const gsl::not_null<TimeStepId*> time_step_id,\n           const gsl::not_null<TimeStepId*> next_time_step_id,\n           const gsl::not_null<TimeDelta*> time_step,\n           const gsl::not_null<typename history_tag::type*> history,\n           const TimeStepper& stepper) {\n          *time_step_id = *next_time_step_id;\n          *time_step = time_step->with_slab(time_step_id->step_time().slab());\n          *next_time_step_id = stepper.next_time_id(*time_step_id, *time_step);\n          stepper.clean_history(history);\n        },\n        make_not_null(&box), db::get<Tags::TimeStepper<TimeStepper>>(box));\n  };\n\n  using error_tag = Tags::StepperErrors<variables_tag>;\n  do_substep();\n  CHECK(not db::get<error_tag>(box)[0].has_value());\n  CHECK(not db::get<error_tag>(box)[1].has_value());\n  do_substep();\n  CHECK(not db::get<error_tag>(box)[0].has_value());\n  CHECK(not db::get<error_tag>(box)[1].has_value());\n  do_substep();\n  CHECK(not db::get<error_tag>(box)[0].has_value());\n  REQUIRE(db::get<error_tag>(box)[1].has_value());\n  CHECK(db::get<error_tag>(box)[1]->step_time == slab.start());\n\n  const auto first_step_errors = db::get<error_tag>(box)[1]->errors;\n  const auto second_step = slab.start() + initial_time_step;\n  do_substep();\n  CHECK(not db::get<error_tag>(box)[0].has_value());\n  REQUIRE(db::get<error_tag>(box)[1].has_value());\n  CHECK(db::get<error_tag>(box)[1]->step_time == slab.start());\n  do_substep();\n  CHECK(not db::get<error_tag>(box)[0].has_value());\n  REQUIRE(db::get<error_tag>(box)[1].has_value());\n  CHECK(db::get<error_tag>(box)[1]->step_time == slab.start());\n  do_substep(true);\n  REQUIRE(db::get<error_tag>(box)[0].has_value());\n  REQUIRE(db::get<error_tag>(box)[1].has_value());\n  CHECK(db::get<error_tag>(box)[0]->step_time == slab.start());\n  CHECK(db::get<error_tag>(box)[1]->step_time == second_step);\n  CHECK(db::get<error_tag>(box)[0]->errors == first_step_errors);\n  CHECK(db::get<error_tag>(box)[1]->errors != first_step_errors);\n}\n\ntemplate <bool LocalTimeStepping>\nvoid test_errors_for_restart() {\n  // We should get low-order errors if we have all of\n  //\n  // 1. variable-order\n  // 2. error-based stepping\n  // 3. stepping to the end of a slab.\n  //\n  // Otherwise, we should get whatever the error-based settings want.\n\n  using Estimates = StepperErrorTolerances::Estimates;\n\n  const auto which_errors = [](const std::optional<size_t>& stepper_order,\n                               const StepperErrorTolerances& tolerances,\n                               const Rational& step_fraction) {\n    using variables_tag = Var;\n    using history_tag = Tags::HistoryEvolvedVariables<variables_tag>;\n\n    const Slab slab(1., 3.);\n    const TimeStepId initial_id(true, 0, slab.start() + slab.duration() / 2);\n\n    history_tag::type history{3};\n    history.insert(TimeStepId(true, 0, slab.start()), 0.0, 0.0);\n    history.insert(TimeStepId(true, 0, slab.start() + slab.duration() / 4), 0.0,\n                   0.0);\n    history.insert(TimeStepId(true, 0, slab.start() + slab.duration() / 2), 0.0,\n                   0.0);\n\n    std::unique_ptr<TimeStepper> time_stepper =\n        std::make_unique<TimeSteppers::AdamsBashforth>(stepper_order);\n    const auto time_step = slab.duration() * step_fraction;\n\n    auto box = db::create<\n        db::AddSimpleTags<Tags::ConcreteTimeStepper<TimeStepper>,\n                          Tags::TimeStepId, Tags::Next<Tags::TimeStepId>,\n                          Tags::TimeStep, ::Tags::StepperErrorEstimatesEnabled,\n                          ::Tags::StepperErrorTolerances<variables_tag>,\n                          variables_tag, history_tag,\n                          Tags::StepperErrors<variables_tag>>,\n        time_stepper_ref_tags<TimeStepper>>(\n        std::move(time_stepper), initial_id,\n        time_stepper->next_time_id(initial_id, time_step), time_step, true,\n        tolerances, 1., std::move(history),\n        Tags::StepperErrors<variables_tag>::type{});\n    db::mutate_apply<UpdateU<SingleVariableSystem, LocalTimeStepping>>(\n        make_not_null(&box));\n    const auto& errors = db::get<Tags::StepperErrors<variables_tag>>(box)[1];\n    if (not errors.has_value()) {\n      return Estimates::None;\n    }\n    return errors->errors[0].has_value() ? Estimates::AllOrders\n                                         : Estimates::StepperOrder;\n  };\n\n  const StepperErrorTolerances none{};\n  const StepperErrorTolerances order{\n      .estimates = Estimates::StepperOrder, .absolute = 1.0, .relative = 0.0};\n  const StepperErrorTolerances all{\n      .estimates = Estimates::AllOrders, .absolute = 1.0, .relative = 0.0};\n\n  CHECK(which_errors(3, none, {1, 4}) == Estimates::None);\n  CHECK(which_errors(3, order, {1, 4}) == Estimates::StepperOrder);\n  CHECK(which_errors(3, all, {1, 4}) == Estimates::AllOrders);\n  CHECK(which_errors(3, none, {1, 2}) == Estimates::None);\n  CHECK(which_errors(3, order, {1, 2}) == Estimates::StepperOrder);\n  CHECK(which_errors(3, all, {1, 2}) == Estimates::AllOrders);\n  CHECK(which_errors(std::nullopt, none, {1, 4}) == Estimates::None);\n  CHECK(which_errors(std::nullopt, order, {1, 4}) == Estimates::StepperOrder);\n  CHECK(which_errors(std::nullopt, all, {1, 4}) == Estimates::AllOrders);\n  CHECK(which_errors(std::nullopt, none, {1, 2}) == Estimates::None);\n  // Interesting case:\n  CHECK(which_errors(std::nullopt, order, {1, 2}) == Estimates::AllOrders);\n  CHECK(which_errors(std::nullopt, all, {1, 2}) == Estimates::AllOrders);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.UpdateU\", \"[Unit][Time]\") {\n  test_integration<SingleVariableSystem, false, false>();\n  test_integration<TwoVariableSystem, false, true>();\n  test_stepper_error<false>();\n  test_errors_for_restart<false>();\n\n  // The mutator's behavior is the same for LTS and GTS.  The flag\n  // only affects the compute tags put into the box.  But the test is\n  // quick, so run it both ways.\n  test_integration<SingleVariableSystem, true, false>();\n  test_integration<TwoVariableSystem, true, true>();\n  test_stepper_error<true>();\n  test_errors_for_restart<true>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  TimeSteppers/Test_AdamsBashforth.cpp\n  TimeSteppers/Test_AdamsCoefficients.cpp\n  TimeSteppers/Test_AdamsLts.cpp\n  TimeSteppers/Test_AdamsMoultonPc.cpp\n  TimeSteppers/Test_ClassicalRungeKutta4.cpp\n  TimeSteppers/Test_DormandPrince5.cpp\n  TimeSteppers/Test_Factory.cpp\n  TimeSteppers/Test_Heun2.cpp\n  TimeSteppers/Test_Rk3HesthavenSsp.cpp\n  TimeSteppers/Test_Rk3Kennedy.cpp\n  TimeSteppers/Test_Rk3Owren.cpp\n  TimeSteppers/Test_Rk3Pareschi.cpp\n  TimeSteppers/Test_Rk4Kennedy.cpp\n  TimeSteppers/Test_Rk4Owren.cpp\n  TimeSteppers/Test_Rk5Owren.cpp\n  TimeSteppers/Test_Rk5Tsitouras.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_AdamsBashforth.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <limits>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/LtsHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/BoundaryHistory.tpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\nvoid test_variable_order() {\n  for (size_t order = 1; order <= 8; ++order) {\n    TimeStepperTestUtils::lts::test_variable_order_consistency(\n        TimeSteppers::AdamsBashforth(std::nullopt),\n        TimeSteppers::AdamsBashforth(order));\n  }\n\n  TimeStepperTestUtils::lts::test_variable_order_boundary_consistency(\n      TimeSteppers::AdamsBashforth(std::nullopt));\n\n  // Check that the variable-order errors look reasonable\n  {\n    const double step = 1.0e-2;\n    const Slab slab(0.0, step);\n    const TimeSteppers::AdamsBashforth variable_stepper(std::nullopt);\n    TimeSteppers::History<double> history{4};\n    TimeStepperTestUtils::initialize_history(\n        slab.end(), make_not_null(&history),\n        [](const double t) { return exp(t); },\n        [](const double y, const double /*t*/) { return y; }, slab.duration(),\n        history.integration_order());\n    double y = std::numeric_limits<double>::signaling_NaN();\n    const auto errors = variable_stepper.update_u(\n        make_not_null(&y), history, slab.duration(),\n        StepperErrorTolerances{StepperErrorTolerances::Estimates::AllOrders,\n                               1.0, 0.0});\n    REQUIRE(errors.has_value());\n    // Truncation error for AB is |final_ab_coef| step^{k+1} f^(k).\n    // As with many time-stepper things, the quoted order is the\n    // equivalent global order even though this is a purely local\n    // quantity, hence the +1 in the exponent.\n    const std::array expected_coefs{1.0, 1.0 / 2.0, 5.0 / 12.0, 3.0 / 8.0};\n    // Error is dominated by finite-difference error in f^(k)\n    auto truncation_approx = Approx::custom().epsilon(2.0 * step);\n    for (size_t order = 0; order < expected_coefs.size(); ++order) {\n      REQUIRE(gsl::at(errors->errors, order).has_value());\n      CHECK(*gsl::at(errors->errors, order) ==\n            truncation_approx(gsl::at(expected_coefs, order) *\n                              std::pow(step, order + 1)));\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsBashforth\", \"[Unit][Time]\") {\n  for (size_t order = 1; order < 9; ++order) {\n    CAPTURE(order);\n    const TimeSteppers::AdamsBashforth stepper(order);\n    TimeStepperTestUtils::check_multistep_properties(stepper);\n    CHECK(stepper.monotonic());\n    for (size_t start_points = 0; start_points < order; ++start_points) {\n      CAPTURE(start_points);\n      const double epsilon = std::max(std::pow(1e-3, start_points + 1), 1e-14);\n      TimeStepperTestUtils::integrate_test(stepper, start_points + 1,\n                                           start_points, 1., epsilon);\n      TimeStepperTestUtils::integrate_test_explicit_time_dependence(\n          stepper, start_points + 1, start_points, 1., epsilon);\n\n      const double large_step_epsilon =\n          std::clamp(1.0e3 * std::pow(2.0e-2, start_points + 1), 1e-14, 1.0);\n      TimeStepperTestUtils::integrate_error_test(\n          stepper, start_points + 1, start_points, 1.0, large_step_epsilon, 20,\n          1.0e-4);\n      TimeStepperTestUtils::integrate_error_test(\n          stepper, start_points + 1, start_points, -1.0, large_step_epsilon, 20,\n          1.0e-4);\n    }\n    TimeStepperTestUtils::check_convergence_order(stepper, {10, 30});\n    TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n    CHECK(stepper.order() ==\n          variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(order));\n\n    TimeStepperTestUtils::stability_test(stepper);\n  }\n\n  const Slab slab(0., 1.);\n  const Time start = slab.start();\n  const Time mid = slab.start() + slab.duration() / 2;\n  const Time end = slab.end();\n  const auto can_change = [](const Time& first, const Time& second,\n                             const Time& now) {\n    const TimeSteppers::AdamsBashforth stepper(2);\n    TimeSteppers::History<double> history(2);\n    history.insert(TimeStepId(true, 0, first), 0., 0.);\n    history.insert(TimeStepId(true, 2, second), 0., 0.);\n    return stepper.can_change_step_size(TimeStepId(true, 4, now), history);\n  };\n  CHECK(can_change(start, mid, end));\n  CHECK_FALSE(can_change(start, end, mid));\n  CHECK(can_change(mid, start, end));\n  CHECK_FALSE(can_change(mid, end, start));\n  CHECK_FALSE(can_change(end, start, mid));\n  CHECK_FALSE(can_change(end, mid, start));\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::AdamsBashforth>(\n      \"AdamsBashforth:\\n\"\n      \"  Order: 3\");\n  TestHelpers::test_factory_creation<LtsTimeStepper,\n                                     TimeSteppers::AdamsBashforth>(\n      \"AdamsBashforth:\\n\"\n      \"  Order: 3\");\n\n  TimeSteppers::AdamsBashforth ab4(4);\n  test_serialization(ab4);\n  test_serialization_via_base<TimeStepper, TimeSteppers::AdamsBashforth>(4_st);\n  test_serialization_via_base<LtsTimeStepper, TimeSteppers::AdamsBashforth>(\n      4_st);\n  // test operator !=\n  TimeSteppers::AdamsBashforth ab2(2);\n  CHECK(ab4 != ab2);\n\n  TimeStepperTestUtils::check_strong_stability_preservation(\n      TimeSteppers::AdamsBashforth(1), 1.0);\n\n  test_variable_order();\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsBashforth.Variable\",\n                  \"[Unit][Time]\") {\n  for (size_t order = 1; order < 9; ++order) {\n    INFO(order);\n    for (size_t start_points = 0; start_points < order; ++start_points) {\n      INFO(start_points);\n      const double epsilon = std::max(std::pow(1e-3, start_points + 1), 1e-14);\n      TimeStepperTestUtils::integrate_variable_test(\n          TimeSteppers::AdamsBashforth(order), start_points + 1, start_points,\n          epsilon);\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsBashforth.Backwards\",\n                  \"[Unit][Time]\") {\n  for (size_t order = 1; order < 9; ++order) {\n    INFO(order);\n    for (size_t start_points = 0; start_points < order; ++start_points) {\n      INFO(start_points);\n      const double epsilon = std::max(std::pow(1e-3, start_points + 1), 1e-14);\n      TimeStepperTestUtils::integrate_test(\n          TimeSteppers::AdamsBashforth(order), start_points + 1, start_points,\n          -1., epsilon);\n      TimeStepperTestUtils::integrate_test_explicit_time_dependence(\n          TimeSteppers::AdamsBashforth(order), start_points + 1, start_points,\n          -1., epsilon);\n    }\n  }\n\n  const Slab slab(0., 1.);\n  const Time start = slab.start();\n  const Time mid = slab.start() + slab.duration() / 2;\n  const Time end = slab.end();\n  const auto can_change = [](const Time& first, const Time& second,\n                             const Time& now) {\n    const TimeSteppers::AdamsBashforth stepper(2);\n    TimeSteppers::History<double> history(2);\n    history.insert(TimeStepId(false, 0, first), 0., 0.);\n    history.insert(TimeStepId(false, 2, second), 0., 0.);\n    return stepper.can_change_step_size(TimeStepId(false, 4, now), history);\n  };\n  CHECK_FALSE(can_change(start, mid, end));\n  CHECK_FALSE(can_change(start, end, mid));\n  CHECK_FALSE(can_change(mid, start, end));\n  CHECK(can_change(mid, end, start));\n  CHECK_FALSE(can_change(end, start, mid));\n  CHECK(can_change(end, mid, start));\n}\n\nnamespace {\nvoid test_neighbor_data_required() {\n  // Test is order-independent\n  const TimeSteppers::AdamsBashforth stepper(4);\n  const Slab slab(0.0, 1.0);\n  CHECK(not stepper.neighbor_data_required(TimeStepId(true, 0, slab.start()),\n                                           TimeStepId(true, 0, slab.start())));\n  CHECK(not stepper.neighbor_data_required(TimeStepId(true, 0, slab.start()),\n                                           TimeStepId(true, 0, slab.end())));\n  CHECK(stepper.neighbor_data_required(TimeStepId(true, 0, slab.end()),\n                                       TimeStepId(true, 0, slab.start())));\n\n  CHECK(not stepper.neighbor_data_required(TimeStepId(false, 0, slab.end()),\n                                           TimeStepId(false, 0, slab.end())));\n  CHECK(not stepper.neighbor_data_required(TimeStepId(false, 0, slab.end()),\n                                           TimeStepId(false, 0, slab.start())));\n  CHECK(stepper.neighbor_data_required(TimeStepId(false, 0, slab.start()),\n                                       TimeStepId(false, 0, slab.end())));\n}\n}  // namespace\n\n// [[Timeout, 30]]\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsBashforth.Boundary\",\n                  \"[Unit][Time]\") {\n  test_neighbor_data_required();\n\n  for (size_t order = 1; order < 9; ++order) {\n    CAPTURE(order);\n    const TimeSteppers::AdamsBashforth stepper(order);\n    TimeStepperTestUtils::lts::test_equal_rate(stepper);\n    TimeStepperTestUtils::lts::test_uncoupled(stepper, 1e-12);\n    TimeStepperTestUtils::lts::test_conservation(stepper);\n    // Only test convergence for low-order methods, since it's hard to\n    // find parameters where the high-order ones are in the convergent\n    // limit but not roundoff-dominated.\n    if (order < 5) {\n      TimeStepperTestUtils::lts::test_convergence(stepper, {20, 100}, 20);\n      TimeStepperTestUtils::lts::test_dense_convergence(stepper, {40, 200}, 40);\n    }\n  }\n\n  const TimeSteppers::AdamsBashforth variable_order_stepper(std::nullopt);\n  for (size_t local_order = 1; local_order < 9; ++local_order) {\n    for (size_t remote_order = 1; remote_order < 9; ++remote_order) {\n      const TimeStepperTestUtils::lts::VariableOrderChoice variable_order{\n          local_order, local_order - 1, remote_order, remote_order - 1};\n      TimeStepperTestUtils::lts::test_uncoupled(variable_order_stepper, 1e-12,\n                                                {variable_order});\n      TimeStepperTestUtils::lts::test_conservation(variable_order_stepper,\n                                                   {variable_order});\n    }\n  }\n\n  // These are slow, so just test a few of them.\n  TimeStepperTestUtils::lts::test_convergence(variable_order_stepper, {40, 100},\n                                              20, {{4, 3, 4, 3}});\n  TimeStepperTestUtils::lts::test_convergence(variable_order_stepper, {40, 100},\n                                              20, {{6, 5, 3, 2}});\n  TimeStepperTestUtils::lts::test_convergence(variable_order_stepper, {40, 100},\n                                              20, {{3, 2, 6, 5}});\n  TimeStepperTestUtils::lts::test_dense_convergence(\n      variable_order_stepper, {40, 200}, 40, {{4, 3, 4, 3}});\n  TimeStepperTestUtils::lts::test_dense_convergence(\n      variable_order_stepper, {40, 200}, 40, {{6, 5, 3, 2}});\n  TimeStepperTestUtils::lts::test_dense_convergence(\n      variable_order_stepper, {40, 200}, 40, {{3, 2, 6, 5}});\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsBashforth.Reversal\",\n                  \"[Unit][Time]\") {\n  const TimeSteppers::AdamsBashforth ab3(3);\n\n  const auto f = [](const double t) {\n    return 1. + t * (2. + t * (3. + t * 4.));\n  };\n  const auto df = [](const double t) { return 2. + t * (6. + t * 12.); };\n\n  TimeSteppers::History<double> history{3};\n  const auto add_history = [&df, &f, &history](const int64_t slab,\n                                               const Time& time) {\n    history.insert(TimeStepId(true, slab, time), f(time.value()),\n                   df(time.value()));\n  };\n  const Slab slab(0., 1.);\n  add_history(0, slab.start());\n  add_history(0, slab.start() + slab.duration() * 3 / 4);\n  add_history(1, slab.start() + slab.duration() / 3);\n  double y = f(1. / 3.);\n  ab3.update_u(make_not_null(&y), history, slab.duration() / 3);\n  CHECK(y == approx(f(2. / 3.)));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsBashforth.Boundary.Reversal\",\n                  \"[Unit][Time]\") {\n  const size_t order = 3;\n  const TimeSteppers::AdamsBashforth ab3(order);\n\n  const auto f = [](const double t) {\n    return 1. + t * (2. + t * (3. + t * 4.));\n  };\n  const auto df = [](const double t) { return 2. + t * (6. + t * 12.); };\n\n  const Slab slab(0., 1.);\n  TimeSteppers::BoundaryHistory<double, double, double> history{};\n  const auto add_history = [&df, &history](const TimeStepId& time_id) {\n    history.local().insert(time_id, order, df(time_id.step_time().value()));\n    history.remote().insert(time_id, order, 0.);\n  };\n  add_history(TimeStepId(true, 0, slab.start()));\n  add_history(TimeStepId(true, 0, slab.start() + slab.duration() * 3 / 4));\n  add_history(TimeStepId(true, 1, slab.start() + slab.duration() / 3));\n  double y = f(1. / 3.);\n  ab3.add_boundary_delta(\n      &y, history, slab.duration() / 3,\n      [](const double local, const double /*remote*/) { return local; });\n  CHECK(y == approx(f(2. / 3.)));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_AdamsCoefficients.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n\n#include \"Time/ApproximateTime.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeSteppers/AdamsCoefficients.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nnamespace {\nnamespace ac = TimeSteppers::adams_coefficients;\n\nvoid check_consistency(const ac::OrderVector<Time>& control_times,\n                       const ac::OrderVector<double>& standard_coefficients) {\n  // Need step_start to be compatible with input.\n  ASSERT(control_times.front().slab().duration().value() == 1.0 and\n             (control_times.front().slab().start().fraction() == 0 or\n              control_times.front().slab().start().fraction() == 1),\n         \"Unexpected slabs in input\");\n  const Time step_start = Slab(-1.0, 0.0).start();\n\n  ac::OrderVector<double> control_times_for_variable{};\n  for (const Time& t : control_times) {\n    control_times_for_variable.push_back(t.value());\n  }\n\n  CHECK_ITERABLE_APPROX(\n      ac::variable_coefficients(control_times_for_variable, -1.0, 0.0),\n      standard_coefficients);\n  // This should be exact, because the function should detect the\n  // constant step case and forward to that function.  (And then\n  // multiply by 1, but that's also exact.)\n  CHECK(ac::coefficients(control_times.begin(), control_times.end(), step_start,\n                         ApproximateTime{0.0}) == standard_coefficients);\n\n  // Test offset times\n  {\n    const double offset = 3.3;\n    ac::OrderVector<Time> offset_control_times{};\n    ac::OrderVector<double> offset_control_times_for_variable{};\n    for (const Time& t : control_times) {\n      offset_control_times.emplace_back(Slab(t.slab().start().value() + offset,\n                                             t.slab().end().value() + offset),\n                                        t.fraction());\n      offset_control_times_for_variable.push_back(\n          offset_control_times.back().value());\n    }\n    const Time offset_start(Slab(step_start.slab().start().value() + offset,\n                                 step_start.slab().end().value() + offset),\n                            step_start.fraction());\n\n    CHECK(ac::coefficients(offset_control_times.begin(),\n                           offset_control_times.end(), offset_start,\n                           ApproximateTime{offset}) ==\n          standard_coefficients);\n    CHECK_ITERABLE_APPROX(\n        ac::variable_coefficients(offset_control_times_for_variable,\n                                  offset - 1.0, offset),\n        standard_coefficients);\n  }\n\n  // Test scaling with time step\n  {\n    const double time_step = 2.1;\n    ac::OrderVector<Time> scaled_control_times{};\n    ac::OrderVector<double> scaled_control_times_for_variable{};\n    for (const Time& t : control_times) {\n      scaled_control_times.emplace_back(\n          Slab(t.slab().start().value() * time_step,\n               t.slab().end().value() * time_step),\n          t.fraction());\n      scaled_control_times_for_variable.push_back(\n          scaled_control_times.back().value());\n    }\n    const Time scaled_start(Slab(step_start.slab().start().value() * time_step,\n                                 step_start.slab().end().value() * time_step),\n                            step_start.fraction());\n    const auto scaled_coefficients = ac::coefficients(\n        scaled_control_times.begin(), scaled_control_times.end(), scaled_start,\n        ApproximateTime{0.0});\n    const auto scaled_variable_coefficients = ac::variable_coefficients(\n        scaled_control_times_for_variable, -time_step, 0.0);\n    for (size_t i = 0; i < standard_coefficients.size(); ++i) {\n      CHECK(scaled_coefficients[i] == time_step * standard_coefficients[i]);\n      CHECK(scaled_variable_coefficients[i] ==\n            approx(time_step * standard_coefficients[i]));\n    }\n  }\n}\n\nvoid check_adams_bashforth_consistency() {\n  ac::OrderVector<Time> standard_ab_control_times{};\n  for (size_t order = 1; order <= ac::maximum_order; ++order) {\n    standard_ab_control_times.insert(\n        standard_ab_control_times.begin(),\n        Slab::with_duration_from_start(-static_cast<double>(order), 1.0)\n            .start());\n    check_consistency(standard_ab_control_times,\n                      ac::constant_adams_bashforth_coefficients(order));\n  }\n}\n\nvoid check_adams_moulton_consistency() {\n  ac::OrderVector<Time> standard_am_control_times{};\n  for (size_t order = 1; order <= ac::maximum_order; ++order) {\n    standard_am_control_times.insert(\n        standard_am_control_times.begin(),\n        Slab::with_duration_from_start(-static_cast<double>(order), 1.0)\n            .end());\n    check_consistency(standard_am_control_times,\n                      ac::constant_adams_moulton_coefficients(order));\n  }\n}\n\nvoid test_rational_computation() {\n  // Check a few known cases\n\n  // Euler's method\n  CHECK(ac::variable_coefficients<Rational>({0}, 0, 1) ==\n        ac::OrderVector<Rational>{1});\n  CHECK(ac::variable_coefficients<Rational>({0}, 0, 2) ==\n        ac::OrderVector<Rational>{2});\n  // Backwards Euler method\n  CHECK(ac::variable_coefficients<Rational>({0}, -1, 0) ==\n        ac::OrderVector<Rational>{1});\n  CHECK(ac::variable_coefficients<Rational>({0}, -2, 0) ==\n        ac::OrderVector<Rational>{2});\n  // AB3\n  CHECK(ac::variable_coefficients<Rational>({-3, -2, -1}, -1, 0) ==\n        ac::OrderVector<Rational>{{5, 12}, {-4, 3}, {23, 12}});\n  // AB3 backwards\n  CHECK(ac::variable_coefficients<Rational>({3, 2, 1}, 1, 0) ==\n        ac::OrderVector<Rational>{{-5, 12}, {4, 3}, {-23, 12}});\n  // AM3\n  CHECK(ac::variable_coefficients<Rational>({-2, -1, 0}, -1, 0) ==\n        ac::OrderVector<Rational>{{-1, 12}, {2, 3}, {5, 12}});\n  // AM3 backwards\n  CHECK(ac::variable_coefficients<Rational>({2, 1, 0}, 1, 0) ==\n        ac::OrderVector<Rational>{{1, 12}, {-2, 3}, {-5, 12}});\n  // Variable step case\n  CHECK(ac::variable_coefficients<Rational>({{-5, 2}, {-2, 3}}, {-2, 3}, 0) ==\n        ac::OrderVector<Rational>{{-4, 33}, {26, 33}});\n  // Step not aligned with control points\n  CHECK(ac::variable_coefficients<Rational>({{1, 2}, {3, 4}}, {1, 3}, 1) ==\n        ac::OrderVector<Rational>{{2, 9}, {4, 9}});\n}\n\nvoid test_unaligned_step() {\n  // Check ac::coefficients with the step not aligned with the control\n  // times.  This is done for the exact math in\n  // test_rational_computation.\n  const Slab slab(0.0, 1.0);\n  const std::array control_times{Time(slab, {1, 2}), Time(slab, {3, 4})};\n  CHECK_ITERABLE_APPROX(\n      ac::coefficients(control_times.begin(), control_times.end(),\n                       Time(slab, {1, 3}), Time{slab, 1}),\n      (ac::OrderVector<double>{2.0 / 9.0, 4.0 / 9.0}));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsCoefficients\", \"[Unit][Time]\") {\n  // These tests just do consistency checks in the various functions.\n  // The actual values are tested by the time stepper tests.\n  check_adams_bashforth_consistency();\n  check_adams_moulton_consistency();\n\n  test_rational_computation();\n  test_unaligned_step();\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_AdamsLts.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/StdHelpers.hpp\"\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <cstdint>\n#include <map>\n#include <optional>\n#include <ostream>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Time/ApproximateTime.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/BoundaryHistory.tpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsLts.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nnamespace {\nnamespace adams_lts = TimeSteppers::adams_lts;\n\nvoid test_exact_substep_time() {\n  const Slab slab(1.2, 3.4);\n  const Time quarter = slab.start() + slab.duration() / 4;\n  const Time middle = slab.start() + slab.duration() / 2;\n  CHECK(adams_lts::exact_substep_time(TimeStepId(true, 1, middle)) == middle);\n  CHECK(adams_lts::exact_substep_time(TimeStepId(false, 1, middle)) == middle);\n  CHECK(adams_lts::exact_substep_time(TimeStepId(\n            true, 1, quarter, 1, slab.duration() / 4, middle.value())) ==\n        middle);\n  CHECK(adams_lts::exact_substep_time(TimeStepId(\n            false, 1, middle, 1, -slab.duration() / 4, quarter.value())) ==\n        quarter);\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      adams_lts::exact_substep_time(TimeStepId(\n          true, 1, slab.start(), 1, slab.duration(), middle.value())),\n      Catch::Matchers::ContainsSubstring(\"Substep not at expected time\"));\n#endif  // SPECTRE_DEBUG\n}\n\nvoid test_lts_coefficients_struct() {\n  const Slab slab(0.0, 1.0);\n  const TimeStepId id1(true, 0, slab.start());\n  const TimeStepId id2 = id1.next_substep(slab.duration() / 2, 1.0);\n  const TimeStepId id3 = id2.next_step(slab.duration() / 2);\n\n  const adams_lts::LtsCoefficients coefs1{\n      {id1, id1, 2.0}, {id1, id2, 3.0}, {id1, id3, 4.0}, {id2, id2, 11.0}};\n  const adams_lts::LtsCoefficients coefs2{\n      {id1, id1, 5.0}, {id1, id3, -4.0}, {id2, id1, 9.0}, {id2, id2, 11.0}};\n\n  const adams_lts::LtsCoefficients sum{\n      {id1, id1, 7.0}, {id1, id2, 3.0}, {id2, id1, 9.0}, {id2, id2, 22.0}};\n  const adams_lts::LtsCoefficients difference{\n      {id1, id1, -3.0}, {id1, id2, 3.0}, {id1, id3, 8.0}, {id2, id1, -9.0}};\n\n  {\n    auto s = coefs1;\n    CHECK(&(s += coefs2) == &s);\n    CHECK(s == sum);\n  }\n  {\n    auto d = coefs1;\n    CHECK(&(d -= coefs2) == &d);\n    CHECK(d == difference);\n  }\n\n  CHECK(coefs1 + coefs2 == sum);\n  CHECK(coefs1 - coefs2 == difference);\n  CHECK(adams_lts::LtsCoefficients(coefs1) + coefs2 == sum);\n  CHECK(adams_lts::LtsCoefficients(coefs1) - coefs2 == difference);\n  CHECK(coefs1 + adams_lts::LtsCoefficients(coefs2) == sum);\n  CHECK(coefs1 - adams_lts::LtsCoefficients(coefs2) == difference);\n  CHECK(adams_lts::LtsCoefficients(coefs1) +\n            adams_lts::LtsCoefficients(coefs2) ==\n        sum);\n  CHECK(adams_lts::LtsCoefficients(coefs1) -\n            adams_lts::LtsCoefficients(coefs2) ==\n        difference);\n\n  adams_lts::LtsCoefficients allocated_coefs{};\n  for (size_t i = 0; i < adams_lts::lts_coefficients_static_size + 1; ++i) {\n    allocated_coefs.emplace_back(\n        id1,\n        TimeStepId(\n            true, static_cast<int64_t>(i),\n            Slab(static_cast<double>(i), static_cast<double>(i + 1)).start()),\n        i);\n  }\n\n  {\n    auto a = allocated_coefs;\n    const auto* const data = a.data();\n    const auto res = std::move(a) + allocated_coefs;\n    CHECK(res.data() == data);\n    CHECK(res == allocated_coefs + allocated_coefs);\n  }\n  {\n    auto a = allocated_coefs;\n    const auto* const data = a.data();\n    const auto res = std::move(a) - allocated_coefs;\n    CHECK(res.data() == data);\n    CHECK(res == allocated_coefs - allocated_coefs);\n  }\n  {\n    auto a = allocated_coefs;\n    const auto* const data = a.data();\n    const auto res = allocated_coefs + std::move(a);\n    CHECK(res.data() == data);\n    CHECK(res == allocated_coefs + allocated_coefs);\n  }\n  {\n    auto a = allocated_coefs;\n    const auto* const data = a.data();\n    const auto res = allocated_coefs - std::move(a);\n    CHECK(res.data() == data);\n    CHECK(res == allocated_coefs - allocated_coefs);\n  }\n  {\n    auto a = allocated_coefs;\n    auto b = allocated_coefs;\n    const auto* const a_data = a.data();\n    const auto* const b_data = a.data();\n    const auto res = std::move(a) + std::move(b);\n    CHECK((res.data() == a_data or res.data() == b_data));\n    CHECK(res == allocated_coefs + allocated_coefs);\n  }\n  {\n    auto a = allocated_coefs;\n    auto b = allocated_coefs;\n    const auto* const a_data = a.data();\n    const auto* const b_data = a.data();\n    const auto res = std::move(a) - std::move(b);\n    CHECK((res.data() == a_data or res.data() == b_data));\n    CHECK(res == allocated_coefs - allocated_coefs);\n  }\n\n  // Check roundoff elimination\n  {\n    const double eps = 10.0 * std::numeric_limits<double>::epsilon();\n    CHECK((adams_lts::LtsCoefficients{{id1, id1, 1.0 + eps}} +\n           adams_lts::LtsCoefficients{{id1, id1, -1.0}})\n              .empty());\n    CHECK(not(adams_lts::LtsCoefficients{{id1, id1, 1.0 + 100.0 * eps}} +\n              adams_lts::LtsCoefficients{{id1, id1, -1.0}})\n                 .empty());\n    CHECK((adams_lts::LtsCoefficients{{id1, id1, 1.0 + eps}} -\n           adams_lts::LtsCoefficients{{id1, id1, 1.0}})\n              .empty());\n    CHECK(not(adams_lts::LtsCoefficients{{id1, id1, 1.0 + 100.0 * eps}} -\n              adams_lts::LtsCoefficients{{id1, id1, 1.0}})\n                 .empty());\n    CHECK((adams_lts::LtsCoefficients{{id1, id1, 1.0e-5 * (1.0 + eps)}} +\n           adams_lts::LtsCoefficients{{id1, id1, -1.0e-5}})\n              .empty());\n    CHECK(not(adams_lts::LtsCoefficients{{id1, id1, 1.0e-5 + eps}} +\n              adams_lts::LtsCoefficients{{id1, id1, -1.0e-5}})\n                 .empty());\n    CHECK((adams_lts::LtsCoefficients{{id1, id1, 1.0e-5 * (1.0 + eps)}} -\n           adams_lts::LtsCoefficients{{id1, id1, 1.0e-5}})\n              .empty());\n    CHECK(not(adams_lts::LtsCoefficients{{id1, id1, 1.0e-5 + eps}} -\n              adams_lts::LtsCoefficients{{id1, id1, 1.0e-5}})\n                 .empty());\n  }\n\n  {\n    auto twice_coefs1 = coefs1;\n    twice_coefs1 += coefs1;\n    auto a = coefs1;\n    a += a;\n    CHECK(a == twice_coefs1);\n  }\n  {\n    auto a = coefs1;\n#if defined(__clang__) and __clang__ < 17\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wself-assign-overloaded\"\n#endif\n    a -= a;\n#if defined(__clang__) and __clang__ < 17\n#pragma GCC diagnostic pop\n#endif\n    CHECK(a.empty());\n  }\n}\n\ntemplate <typename T>\nvoid test_apply_coefficients(const T& used_for_size) {\n  const Slab slab(1.2, 3.4);\n\n  TimeSteppers::BoundaryHistory<double, double, T> history{};\n  history.local().insert(TimeStepId(true, 0, slab.start()), 1, 1.0);\n  history.local().insert(TimeStepId(true, 0, slab.end()), 1, 10.0);\n  history.remote().insert(TimeStepId(true, 0, slab.start()), 1, 100.0);\n  history.remote().insert(TimeStepId(true, 0, slab.end()), 1, 10000.0);\n\n  const adams_lts::LtsCoefficients coefficients{\n      {history.local()[0], history.remote()[0], 1.0},\n      {history.local()[0], history.remote()[1], 2.0},\n      {history.local()[1], history.remote()[1], 3.0}};\n\n  const auto coupling = [&used_for_size](const double local,\n                                         const double remote) {\n    return make_with_value<T>(used_for_size, local * remote);\n  };\n\n  auto result = make_with_value<T>(used_for_size, 2.0);\n  adams_lts::apply_coefficients(make_not_null(&result), coefficients,\n                                history.evaluator(coupling));\n\n  CHECK(result == make_with_value<T>(used_for_size, 320102.0));\n}\n\nstruct FakeId {\n  int step_time;\n  std::optional<int> step_size_if_substep{};\n  int64_t slab = 0;\n};\n\nbool operator<(const FakeId& a, const FakeId& b) {\n  return a.slab < b.slab or\n         (a.slab == b.slab and (a.step_time < b.step_time or\n                                (a.step_time == b.step_time and\n                                 not a.step_size_if_substep.has_value() and\n                                 b.step_size_if_substep.has_value())));\n}\n\nstd::ostream& operator<<(std::ostream& s, const FakeId& id) {\n  using ::operator<<;\n  return s << \"[\" << id.step_time << \" \" << id.step_size_if_substep << \" \"\n           << id.slab << \"]\";\n}\n\nusing ExpectedCoefficients = std::map<std::pair<FakeId, FakeId>, double>;\n\nExpectedCoefficients operator+(ExpectedCoefficients a,\n                               const ExpectedCoefficients& b) {\n  for (const auto& term : b) {\n    const auto inserted = a.insert(term);\n    if (not inserted.second) {\n      inserted.first->second += term.second;\n      if (inserted.first->second == 0.0) {\n        a.erase(inserted.first);\n      }\n    }\n  }\n  return a;\n}\n\nExpectedCoefficients operator-(ExpectedCoefficients a,\n                               const ExpectedCoefficients& b) {\n  for (const auto& term : b) {\n    const auto inserted = a.insert(term);\n    if (inserted.second) {\n      inserted.first->second *= -1.0;\n    } else {\n      inserted.first->second -= term.second;\n      if (inserted.first->second == 0.0) {\n        a.erase(inserted.first);\n      }\n    }\n  }\n  return a;\n}\n\nstruct IdOrder {\n  FakeId id;\n  size_t order;\n};\n\nExpectedCoefficients flip_sides(const ExpectedCoefficients& coefs) {\n  ExpectedCoefficients flipped{};\n  for (const auto& coef : coefs) {\n    flipped.insert({{coef.first.second, coef.first.first}, coef.second});\n  }\n  return flipped;\n}\n\nnamespace step_coefficients_detail {\nconstexpr int max_time = 16;\n\nTime make_time(const int time) {\n  return {Slab(-max_time, max_time), Rational(time + max_time, 2 * max_time)};\n}\n\nApproximateTime make_time(const double time) { return {time}; }\n\nTimeStepId make_id(const FakeId& fake_id) {\n  if (fake_id.step_size_if_substep.has_value()) {\n    ASSERT(*fake_id.step_size_if_substep > 0, \"Zero step size\");\n    const auto unit_step =\n        Slab(-max_time, max_time).duration() / (2 * max_time);\n    return {\n        true,\n        fake_id.slab,\n        make_time(fake_id.step_time),\n        1,\n        *fake_id.step_size_if_substep * unit_step,\n        static_cast<double>(fake_id.step_time + *fake_id.step_size_if_substep)};\n  } else {\n    return {true, fake_id.slab, make_time(fake_id.step_time)};\n  }\n}\n\nFakeId fake_from_id(const TimeStepId& id) {\n  return {static_cast<int>(id.step_time().value()),\n          id.substep() == 1\n              ? std::optional(static_cast<int>(id.step_size().value()))\n              : std::nullopt,\n          id.slab_number()};\n}\n\n// Map t -> -t/2.  As our test slab is symmetrical about 0, this maps\n// the fraction as [0, 1] -> [3/4, 1/4].\nTime alternate_time(const Time& time) {\n  return {time.slab(), Rational(3, 4) - time.fraction() / 2};\n}\n\nApproximateTime alternate_time(const ApproximateTime& time) {\n  return {-0.5 * time.value()};\n}\n\nTimeStepId alternate_id(const TimeStepId& id) {\n  if (id.substep() == 0) {\n    return {not id.time_runs_forward(), id.slab_number(),\n            alternate_time(id.step_time())};\n  } else {\n    return {not id.time_runs_forward(),\n            id.slab_number(),\n            alternate_time(id.step_time()),\n            id.substep(),\n            id.step_size() / -2,\n            id.substep_time() / -2.0};\n  }\n}\n\ntemplate <typename T>\nExpectedCoefficients step_coefficients_impl(\n    const std::vector<IdOrder>& local_steps_and_orders,\n    const std::vector<IdOrder>& remote_steps_and_orders,\n    const adams_lts::SchemeType local_scheme,\n    const adams_lts::SchemeType remote_scheme, const int local_order_offset,\n    const int remote_order_offset, const int step_start, const T step_end) {\n  TimeSteppers::BoundaryHistory<double, double, double> history{};\n  TimeSteppers::BoundaryHistory<double, double, double> alt_history{};\n\n  for (const auto& step : local_steps_and_orders) {\n    const auto id = make_id(step.id);\n    history.local().insert(id, step.order, 0.0);\n    alt_history.local().insert(alternate_id(id), step.order, 0.0);\n  }\n  for (const auto& step : remote_steps_and_orders) {\n    const auto id = make_id(step.id);\n    history.remote().insert(id, step.order, 0.0);\n    alt_history.remote().insert(alternate_id(id), step.order, 0.0);\n  }\n\n  const auto coefficients = adams_lts::lts_coefficients(\n      history.local(), history.remote(), make_time(step_start),\n      make_time(step_end), local_scheme, remote_scheme, local_order_offset,\n      remote_order_offset);\n\n  // Compare with step scaled by -1/2.\n  const auto alt_coefficients = adams_lts::lts_coefficients(\n      alt_history.local(), alt_history.remote(),\n      alternate_time(make_time(step_start)),\n      alternate_time(make_time(step_end)), local_scheme, remote_scheme,\n      local_order_offset, remote_order_offset);\n  REQUIRE(coefficients.size() == alt_coefficients.size());\n  for (size_t i = 0; i < coefficients.size(); ++i) {\n    const auto& coef = coefficients[i];\n    const auto& alt_coef = alt_coefficients[i];\n    CHECK(get<0>(alt_coef) == alternate_id(get<0>(coef)));\n    CHECK(get<1>(alt_coef) == alternate_id(get<1>(coef)));\n    CHECK(get<2>(alt_coef) == approx(get<2>(coef) / -2.0));\n  }\n\n  ExpectedCoefficients coefficients_map{};\n  for (const auto& entry : coefficients) {\n    const auto insert_success = coefficients_map.insert(\n        {{fake_from_id(get<0>(entry)), fake_from_id(get<1>(entry))},\n         get<2>(entry)});\n    if (not insert_success.second) {\n      // Duplicate entry.\n      CAPTURE(entry);\n      CHECK(false);\n      insert_success.first->second += get<2>(entry);\n    }\n  }\n  return coefficients_map;\n}\n}  // namespace step_coefficients_detail\n\nExpectedCoefficients dense_coefficients(\n    const std::vector<IdOrder>& local_steps_and_orders,\n    const std::vector<IdOrder>& remote_steps_and_orders,\n    const adams_lts::SchemeType local_scheme,\n    const adams_lts::SchemeType remote_scheme, const int local_order_offset,\n    const int remote_order_offset, const int step_start,\n    const double step_end) {\n  auto result = step_coefficients_detail::step_coefficients_impl(\n      local_steps_and_orders, remote_steps_and_orders, local_scheme,\n      remote_scheme, local_order_offset, remote_order_offset, step_start,\n      step_end);\n  // NOLINTNEXTLINE(readability-suspicious-call-argument)\n  const auto reversed = step_coefficients_detail::step_coefficients_impl(\n      remote_steps_and_orders, local_steps_and_orders, remote_scheme,\n      local_scheme, remote_order_offset, local_order_offset, step_start,\n      step_end);\n  CHECK_ITERABLE_APPROX(result, flip_sides(reversed));\n  return result;\n}\n\nExpectedCoefficients step_coefficients(\n    const std::vector<IdOrder>& local_steps_and_orders,\n    const std::vector<IdOrder>& remote_steps_and_orders,\n    const adams_lts::SchemeType local_scheme,\n    const adams_lts::SchemeType remote_scheme, const int local_order_offset,\n    const int remote_order_offset, const int step_start, const int step_end,\n    const bool also_check_dense = true) {\n  auto result = step_coefficients_detail::step_coefficients_impl(\n      local_steps_and_orders, remote_steps_and_orders, local_scheme,\n      remote_scheme, local_order_offset, remote_order_offset, step_start,\n      step_end);\n  // NOLINTNEXTLINE(readability-suspicious-call-argument)\n  const auto reversed = step_coefficients_detail::step_coefficients_impl(\n      remote_steps_and_orders, local_steps_and_orders, remote_scheme,\n      local_scheme, remote_order_offset, local_order_offset, step_start,\n      step_end);\n  CHECK_ITERABLE_APPROX(result, flip_sides(reversed));\n  if (also_check_dense) {\n    const auto dense = dense_coefficients(\n        local_steps_and_orders, remote_steps_and_orders, local_scheme,\n        remote_scheme, local_order_offset, remote_order_offset, step_start,\n        static_cast<double>(step_end));\n    CHECK_ITERABLE_APPROX(result, dense);\n  }\n  return result;\n}\n\nvoid test_lts_coefficients() {\n  // using enum adams_lts::SchemeType; // Not supported until gcc 11\n  const auto Explicit = adams_lts::SchemeType::Explicit;\n  const auto Implicit = adams_lts::SchemeType::Implicit;\n\n  // AB2 [0 1] step to 2\n  const std::array coefs_ab2{-1.0 / 2.0, 3.0 / 2.0};\n  // AB2 [0 1] step to 3/2\n  const std::array coefs_ab2_32{-1.0 / 8.0, 5.0 / 8.0};\n  // AB3 [0 1 2] step to 3\n  const std::array coefs_ab3{5.0 / 12.0, -4.0 / 3.0, 23.0 / 12.0};\n  // AB3 [0 1 2] step to 5/2\n  const std::array coefs_ab3_52{1.0 / 12.0, -7.0 / 24.0, 17.0 / 24.0};\n  // AM3 [0 1 (2)] step to 2\n  const std::array coefs_am3{-1.0 / 12.0, 2.0 / 3.0, 5.0 / 12.0};\n  // AM3 [0 1 (2)] dense to 3/2\n  const std::array coefs_am3_32{-1.0 / 24.0, 11.0 / 24.0, 1.0 / 12.0};\n\n  {\n    INFO(\"AB GTS order 1\");\n    const std::vector<IdOrder> steps{{{0}, 1}};\n    // clang-format off\n    const ExpectedCoefficients expected{\n        {{{0}, {0}}, 1.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps, steps, Explicit, Explicit, 0, 0, 0, 1),\n        expected);\n    const ExpectedCoefficients expected_dense_12{\n        {{{0}, {0}}, 0.5}};\n    CHECK_ITERABLE_APPROX(\n        dense_coefficients(steps, steps, Explicit, Explicit, 0, 0, 0, 0.5),\n        expected_dense_12);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AB GTS order 3\");\n    const std::vector<IdOrder> steps{{{0}, 3}, {{1}, 3}, {{2}, 3}};\n    // clang-format off\n    const ExpectedCoefficients expected{\n        {{{0}, {0}}, coefs_ab3[0]},\n        {{{1}, {1}}, coefs_ab3[1]},\n        {{{2}, {2}}, coefs_ab3[2]}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps, steps, Explicit, Explicit, 0, 0, 2, 3),\n        expected);\n    const ExpectedCoefficients expected_dense_52{\n        {{{0}, {0}}, coefs_ab3_52[0]},\n        {{{1}, {1}}, coefs_ab3_52[1]},\n        {{{2}, {2}}, coefs_ab3_52[2]}};\n    CHECK_ITERABLE_APPROX(\n        dense_coefficients(steps, steps, Explicit, Explicit, 0, 0, 2, 2.5),\n        expected_dense_52);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AM GTS order 3\");\n    const std::vector<IdOrder> steps{{{0}, 3}, {{1}, 3}, {{1, 1}, 3}};\n    // clang-format off\n    const ExpectedCoefficients expected{\n        {{{0}, {0}}, coefs_am3[0]},\n        {{{1}, {1}}, coefs_am3[1]},\n        {{{1, 1}, {1, 1}}, coefs_am3[2]}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps, steps, Implicit, Implicit, 0, 0, 1, 2),\n        expected);\n    const ExpectedCoefficients expected_predictor{\n        {{{0}, {0}}, coefs_ab2[0]},\n        {{{1}, {1}}, coefs_ab2[1]}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps, steps, Explicit, Explicit, -1, -1, 1, 2),\n        expected_predictor);\n    const ExpectedCoefficients expected_dense_32_nonmonotonic{\n        {{{0}, {0}}, coefs_am3_32[0]},\n        {{{1}, {1}}, coefs_am3_32[1]},\n        {{{1, 1}, {1, 1}}, coefs_am3_32[2]}};\n    CHECK_ITERABLE_APPROX(\n        dense_coefficients(steps, steps, Implicit, Implicit, 0, 0, 1, 1.5),\n        expected_dense_32_nonmonotonic);\n    const ExpectedCoefficients expected_dense_32_monotonic{\n        {{{0}, {0}}, coefs_ab2_32[0]},\n        {{{1}, {1}}, coefs_ab2_32[1]}};\n    CHECK_ITERABLE_APPROX(\n        dense_coefficients(steps, steps, Explicit, Explicit, -1, -1, 1, 1.5),\n        expected_dense_32_monotonic);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AB single-side order 3\");\n    const std::vector<IdOrder> steps{{{0}, 3}, {{1}, 3}, {{2}, 3}};\n    const std::vector<IdOrder> single_step{{{0}, 1}};\n    // clang-format off\n    const ExpectedCoefficients expected{\n        {{{0}, {0}}, coefs_ab3[0]},\n        {{{1}, {0}}, coefs_ab3[1]},\n        {{{2}, {0}}, coefs_ab3[2]}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps, single_step, Explicit, Explicit, 0, 0, 2, 3),\n        expected);\n    const ExpectedCoefficients expected_dense_52{\n        {{{0}, {0}}, coefs_ab3_52[0]},\n        {{{1}, {0}}, coefs_ab3_52[1]},\n        {{{2}, {0}}, coefs_ab3_52[2]}};\n    CHECK_ITERABLE_APPROX(\n        dense_coefficients(\n            steps, single_step, Explicit, Explicit, 0, 0, 2, 2.5),\n        expected_dense_52);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AB 2:1 order 3\");\n    // -8          -4           0           4\n    //             -4    -2     0     2     4\n    const std::vector<IdOrder> steps_large{{{-8}, 3}, {{-4}, 3}, {{0}, 3}};\n    const std::vector<IdOrder> steps_small{\n        {{-4}, 3}, {{-2}, 3}, {{0}, 3}, {{2}, 3}};\n    // clang-format off\n    const ExpectedCoefficients expected_large{\n        {{{0}, {2}}, 115.0 / 16.0},\n        {{{0}, {0}}, 7.0 / 6.0},\n        {{{0}, {-2}}, -11.0 / 16.0},\n        {{{-4}, {2}}, -115.0 / 24.0},\n        {{{-4}, {-2}}, -11.0 / 8.0},\n        {{{-4}, {-4}}, 5.0 / 6.0},\n        {{{-8}, {2}}, 23.0 / 16.0},\n        {{{-8}, {-2}}, 11.0 / 48.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Explicit, Explicit, 0, 0, 0, 4, false),\n        expected_large);\n    const ExpectedCoefficients expected_small_1{\n        {{{0}, {0}}, 23.0 / 6.0},\n        {{{-2}, {0}}, -1.0},\n        {{{-2}, {-4}}, -2.0},\n        {{{-4}, {-4}}, 5.0 / 6.0},\n        {{{-2}, {-8}}, 1.0 / 3.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, 0, 0, 0, 2),\n        expected_small_1);\n    const ExpectedCoefficients expected_small_2{\n        {{{2}, {0}}, 115.0 / 16.0},\n        {{{0}, {0}}, -8.0 / 3.0},\n        {{{-2}, {0}}, 5.0 / 16.0},\n        {{{2}, {-4}}, -115.0 / 24.0},\n        {{{-2}, {-4}}, 5.0 / 8.0},\n        {{{2}, {-8}}, 23.0 / 16.0},\n        {{{-2}, {-8}}, -5.0 / 48.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, 0, 0, 2, 4),\n        expected_small_2);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AB LTS -> GTS order 2\");\n    // -2     0  1\n    //    -1  0  1\n    const std::vector<IdOrder> steps_large{{{-2}, 2}, {{0}, 2}};\n    const std::vector<IdOrder> steps_small{{{-1}, 2}, {{0}, 2}};\n    // clang-format off\n    const ExpectedCoefficients expected{\n        {{{0}, {0}}, 3.0 / 2.0},\n        {{{0}, {-1}}, -1.0 / 4.0},\n        {{{-2}, {-1}}, -1.0 / 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Explicit, Explicit, 0, 0, 0, 1),\n        expected);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AB 3:1 order 2\");\n    // -3        0        3\n    //       -1  0  1  2  3\n    const std::vector<IdOrder> steps_large{{{-3}, 2}, {{0}, 2}};\n    const std::vector<IdOrder> steps_small{\n        {{-1}, 2}, {{0}, 2}, {{1}, 2}, {{2}, 2}};\n    // clang-format off\n    const ExpectedCoefficients expected_large{\n        {{{-3}, {-1}}, -1.0 / 6.0},\n        {{{-3}, {1}}, -1.0 / 3.0},\n        {{{-3}, {2}}, -1.0},\n        {{{0}, {-1}}, -1.0 / 3.0},\n        {{{0}, {0}}, 1.0},\n        {{{0}, {1}}, 4.0 / 3.0},\n        {{{0}, {2}}, 5.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Explicit, Explicit, 0, 0, 0, 3, false),\n        expected_large);\n    const ExpectedCoefficients expected_small_1{\n        {{{-1}, {-3}}, -1.0 / 6.0},\n        {{{-1}, {0}}, -1.0 / 3.0},\n        {{{0}, {0}}, 3.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, 0, 0, 0, 1),\n        expected_small_1);\n    const ExpectedCoefficients expected_small_2{\n        {{{0}, {0}}, -1.0 / 2.0},\n        {{{1}, {-3}}, -1.0 / 2.0},\n        {{{1}, {0}}, 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, 0, 0, 1, 2),\n        expected_small_2);\n    const ExpectedCoefficients expected_small_3{\n        {{{1}, {-3}}, 1.0 / 6.0},\n        {{{1}, {0}}, -2.0 / 3.0},\n        {{{2}, {-3}}, -1.0},\n        {{{2}, {0}}, 5.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, 0, 0, 2, 3),\n        expected_small_3);\n\n    const ExpectedCoefficients expected_dense_12{\n        {{{-3}, {-1}}, -1.0 / 24.0},\n        {{{0}, {-1}}, -1.0 / 12.0},\n        {{{0}, {0}}, 5.0 / 8.0}};\n    CHECK_ITERABLE_APPROX(\n        dense_coefficients(\n            steps_large, steps_small, Explicit, Explicit, 0, 0, 0, 0.5),\n        expected_dense_12);\n    const ExpectedCoefficients expected_dense_32{\n        {{{0}, {0}}, -1.0 / 8.0},\n        {{{-3}, {1}}, -5.0 / 24.0},\n        {{{0}, {1}}, 5.0 / 6.0}};\n    CHECK_ITERABLE_APPROX(\n        dense_coefficients(\n            steps_large, steps_small, Explicit, Explicit, 0, 0, 1, 1.5),\n        expected_dense_32);\n    const ExpectedCoefficients expected_dense_52{\n        {{{-3}, {1}}, 1.0 / 24.0},\n        {{{0}, {1}}, -1.0 / 6.0},\n        {{{-3}, {2}}, -5.0 / 12.0},\n        {{{0}, {2}}, 25.0 / 24.0}};\n    CHECK_ITERABLE_APPROX(\n        dense_coefficients(\n            steps_large, steps_small, Explicit, Explicit, 0, 0, 2, 2.5),\n        expected_dense_52);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AB unaligned order 2\");\n    // 0  1     3  4     6\n    // 0     2  3     5  6\n    const std::vector<IdOrder> steps_a{{{1}, 2}, {{3}, 2}, {{4}, 2}};\n    const std::vector<IdOrder> steps_b{{{2}, 2}, {{3}, 2}, {{5}, 2}};\n    // clang-format off\n    const ExpectedCoefficients expected_a_1{\n        {{{1}, {2}}, -1.0 / 4.0},\n        {{{3}, {2}}, -1.0 / 4.0},\n        {{{3}, {3}}, 3.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps_a, steps_b, Explicit, Explicit, 0, 0, 3, 4),\n        expected_a_1);\n    const ExpectedCoefficients expected_a_2{\n        {{{3}, {3}}, -1.0 / 2.0},\n        {{{3}, {5}}, -3.0 / 2.0},\n        {{{4}, {2}}, -3.0 / 2.0},\n        {{{4}, {3}}, 11.0 / 4.0},\n        {{{4}, {5}}, 11.0 / 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_a, steps_b, Explicit, Explicit, 0, 0, 4, 6, false),\n        expected_a_2);\n    const ExpectedCoefficients expected_b_1{\n        {{{2}, {1}}, -1.0 / 4.0},\n        {{{2}, {3}}, -1.0 / 4.0},\n        {{{2}, {4}}, -3.0 / 2.0},\n        {{{3}, {3}}, 1.0},\n        {{{3}, {4}}, 3.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_b, steps_a, Explicit, Explicit, 0, 0, 3, 5, false),\n        expected_b_1);\n    const ExpectedCoefficients expected_b_2{\n        {{{3}, {4}}, -1.0 / 4.0},\n        {{{5}, {3}}, -3.0 / 2.0},\n        {{{5}, {4}}, 11.0 / 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps_b, steps_a, Explicit, Explicit, 0, 0, 5, 6),\n        expected_b_2);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AM 2:1 order 2\");\n    // 0     2\n    // 0  1  2\n    const std::vector<IdOrder> steps_large{{{0}, 2}, {{0, 2}, 2}};\n    const std::vector<IdOrder> steps_small{\n        {{0}, 2}, {{0, 1}, 2}, {{1}, 2}, {{1, 1}, 2}};\n    const std::vector<IdOrder> steps_small_for_nonmonotonic_predictor{{{0}, 2}};\n    // clang-format off\n    // Monotonic predictor requires two calls, both of which are\n    // small-side tests below.\n    const ExpectedCoefficients expected_large_predictor_nonmonotonic{\n        {{{0}, {0}}, 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps_large, steps_small_for_nonmonotonic_predictor,\n                          Explicit, Explicit, -1, -1, 0, 2),\n        expected_large_predictor_nonmonotonic);\n    const ExpectedCoefficients expected_large_corrector{\n        {{{0}, {0}}, 1.0 / 2.0},\n        {{{0}, {0, 1}}, 1.0 / 4.0},\n        {{{0, 2}, {0, 1}}, 1.0 / 4.0},\n        {{{0}, {1}}, 1.0 / 4.0},\n        {{{0, 2}, {1}}, 1.0 / 4.0},\n        {{{0, 2}, {1, 1}}, 1.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Implicit, Implicit, 0, 0, 0, 2, false),\n        expected_large_corrector);\n    const ExpectedCoefficients expected_small_predictor_1{\n        {{{0}, {0}}, 1.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 0, 1),\n        expected_small_predictor_1);\n    const ExpectedCoefficients expected_small_corrector_1{\n        {{{0}, {0}}, 1.0 / 2.0},\n        {{{0, 1}, {0}}, 1.0 / 4.0},\n        {{{0, 1}, {0, 2}}, 1.0 / 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 0, 1),\n        expected_small_corrector_1);\n    const ExpectedCoefficients expected_small_predictor_2{\n        {{{1}, {0}}, 1.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 1, 2),\n        expected_small_predictor_2);\n    const ExpectedCoefficients expected_small_corrector_2{\n        {{{1}, {0}}, 1.0 / 4.0},\n        {{{1}, {0, 2}}, 1.0 / 4.0},\n        {{{1, 1}, {0, 2}}, 1.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 1, 2),\n        expected_small_corrector_2);\n    // clang-format on\n  }\n\n  {\n    INFO(\"AB self-start order 4\");\n    // 0 1 2 3 -- 0 1\n    // 0 1 2 3 -- 0 1\n    const std::vector<IdOrder> steps{\n        {{2, {}, -1}, 3}, {{3, {}, -1}, 3}, {{0}, 4}, {{1}, 4}};\n    // clang-format off\n    const ExpectedCoefficients expected{\n        {{{2, {}, -1}, {2, {}, -1}}, 13.0 / 24.0},\n        {{{3, {}, -1}, {3, {}, -1}}, -1.0 / 24.0},\n        {{{0}, {0}}, -1.0 / 24.0},\n        {{{1}, {1}}, 13.0 / 24.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(steps, steps, Explicit, Explicit, 0, 0, 1, 2),\n        expected);\n    // clang-format on\n  }\n\n  {\n    INFO(\"Paper example - non-monotonic\");\n    //   1 2       6\n    // 0   2   4 5 6\n    std::vector<IdOrder> steps_large{{{1}, 3}, {{2}, 3}};\n    std::vector<IdOrder> steps_small{{{0}, 3}, {{2}, 3}};\n    // clang-format off\n    const ExpectedCoefficients expected_large_predictor{\n       {{{1}, {0}}, -4.0},\n       {{{1}, {2}}, -4.0},\n       {{{2}, {2}}, 12.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Explicit, Explicit, -1, -1, 2, 6, false),\n        expected_large_predictor);\n    const ExpectedCoefficients expected_small_predictor_0{\n       {{{0}, {1}}, -1.0},\n       {{{2}, {1}}, -1.0},\n       {{{2}, {2}}, 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 2, 4),\n        expected_small_predictor_0);\n    steps_large.push_back({{2, 4}, 3});\n    steps_small.push_back({{2, 2}, 3});\n    const ExpectedCoefficients expected_small_corrector_0{\n        {{{0}, {1}}, -1.0 / 6.0},\n        {{{2}, {1}}, -1.0 / 3.0},\n        {{{2}, {2}}, 5.0 / 3.0},\n        {{{2, 2}, {1}}, -17.0 / 30.0},\n        {{{2, 2}, {2}}, 7.0 / 6.0},\n        {{{2, 2}, {2, 4}}, 7.0 / 30.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 2, 4),\n        expected_small_corrector_0);\n    steps_small.push_back({{4}, 3});\n    const ExpectedCoefficients expected_small_predictor_1{\n        {{{2}, {2}}, -1.0 / 4.0},\n        {{{4}, {1}}, -5.0 / 2.0},\n        {{{4}, {2}}, 15.0 / 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 4, 5),\n        expected_small_predictor_1);\n    steps_small.push_back({{4, 1}, 3});\n    const ExpectedCoefficients expected_small_corrector_1{\n        {{{2}, {2}}, -1.0 / 36.0},\n        {{{4}, {1}}, -7.0 / 15.0},\n        {{{4}, {2}}, 7.0 / 8.0},\n        {{{4}, {2, 4}}, 7.0 / 40.0},\n        {{{4, 1}, {1}}, -4.0 / 15.0},\n        {{{4, 1}, {2}}, 4.0 / 9.0},\n        {{{4, 1}, {2, 4}}, 4.0 / 15.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 4, 5),\n        expected_small_corrector_1);\n    steps_small.push_back({{5}, 3});\n    const ExpectedCoefficients expected_small_predictor_2{\n        {{{4}, {1}}, 1.0},\n        {{{4}, {2}}, -3.0 / 2.0},\n        {{{5}, {1}}, -9.0 / 2.0},\n        {{{5}, {2}}, 6.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 5, 6),\n        expected_small_predictor_2);\n    steps_small.push_back({{5, 1}, 3});\n    const ExpectedCoefficients expected_small_corrector_2{\n        {{{4}, {1}}, 1.0 / 15.0},\n        {{{4}, {2}}, -1.0 / 8.0},\n        {{{4}, {2, 4}}, -1.0 / 40.0},\n        {{{5}, {1}}, -2.0 / 5.0},\n        {{{5}, {2}}, 2.0 / 3.0},\n        {{{5}, {2, 4}}, 2.0 / 5.0},\n        {{{5, 1}, {2, 4}}, 5.0 / 12.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 5, 6),\n        expected_small_corrector_2);\n    const ExpectedCoefficients expected_large_corrector{\n        {{{1}, {0}}, -1.0 / 6.0},\n        {{{1}, {2}}, -1.0 / 3.0},\n        {{{1}, {2, 2}}, -17.0 / 30.0},\n        {{{1}, {4}}, -2.0 / 5.0},\n        {{{1}, {4, 1}}, -4.0 / 15.0},\n        {{{1}, {5}}, -2.0 / 5.0},\n        {{{2}, {2}}, 59.0 / 36.0},\n        {{{2}, {2, 2}}, 7.0 / 6.0},\n        {{{2}, {4}}, 3.0 / 4.0},\n        {{{2}, {4, 1}}, 4.0 / 9.0},\n        {{{2}, {5}}, 2.0 / 3.0},\n        {{{2, 4}, {2, 2}}, 7.0 / 30.0},\n        {{{2, 4}, {4}}, 3.0 / 20.0},\n        {{{2, 4}, {4, 1}}, 4.0 / 15.0},\n        {{{2, 4}, {5}}, 2.0 / 5.0},\n        {{{2, 4}, {5, 1}}, 5.0 / 12.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Implicit, Implicit, 0, 0, 2, 6, false),\n        expected_large_corrector);\n    // clang-format on\n  }\n\n  {\n    INFO(\"Paper example - monotonic\");\n    //   1 2       6\n    // 0   2   4 5 6\n    std::vector<IdOrder> steps_large{{{1}, 3}, {{2}, 3}};\n    std::vector<IdOrder> steps_small{{{0}, 3}, {{2}, 3}};\n    // clang-format off\n    const ExpectedCoefficients expected_small_predictor_0{\n        {{{0}, {1}}, -1.0},\n        {{{2}, {1}}, -1.0},\n        {{{2}, {2}}, 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 2, 4),\n        expected_small_predictor_0);\n    steps_small.push_back({{2, 2}, 3});\n    const ExpectedCoefficients expected_small_corrector_0{\n        {{{0}, {1}}, -1.0 / 6.0},\n        {{{2}, {1}}, -1.0 / 3.0},\n        {{{2}, {2}}, 5.0 / 3.0},\n        {{{2, 2}, {1}}, -3.0 / 2.0},\n        {{{2, 2}, {2}}, 7.0 / 3.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Explicit, 0, -1, 2, 4),\n        expected_small_corrector_0);\n    steps_small.push_back({{4}, 3});\n    const ExpectedCoefficients expected_small_predictor_1{\n        {{{2}, {2}}, -1.0 / 4.0},\n        {{{4}, {1}}, -5.0 / 2.0},\n        {{{4}, {2}}, 15.0 / 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 4, 5),\n        expected_small_predictor_1);\n    steps_small.push_back({{4, 1}, 3});\n    const ExpectedCoefficients expected_small_corrector_1{\n        {{{2}, {2}}, -1.0 / 36.0},\n        {{{4}, {1}}, -7.0 / 6.0},\n        {{{4}, {2}}, 7.0 / 4.0},\n        {{{4, 1}, {1}}, -4.0 / 3.0},\n        {{{4, 1}, {2}}, 16.0 / 9.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Explicit, 0, -1, 4, 5),\n        expected_small_corrector_1);\n    steps_small.push_back({{5}, 3});\n    const ExpectedCoefficients expected_small_predictor_2{\n        {{{4}, {1}}, 1.0},\n        {{{4}, {2}}, -3.0 / 2.0},\n        {{{5}, {1}}, -9.0 / 2.0},\n        {{{5}, {2}}, 6.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 5, 6),\n        expected_small_predictor_2);\n    const ExpectedCoefficients expected_large_predictor{\n        {{{1}, {0}}, -1.0 / 6.0},\n        {{{1}, {2}}, -1.0 / 3.0},\n        {{{1}, {2, 2}}, -3.0 / 2.0},\n        {{{1}, {4}}, -1.0 / 6.0},\n        {{{1}, {4, 1}}, -4.0 / 3.0},\n        {{{1}, {5}}, -9.0 / 2.0},\n        {{{2}, {2}}, 59.0 / 36.0},\n        {{{2}, {2, 2}}, 7.0 / 3.0},\n        {{{2}, {4}}, 1.0 / 4.0},\n        {{{2}, {4, 1}}, 16.0 / 9.0},\n        {{{2}, {5}}, 6.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Explicit, Explicit, -1, -1, 5, 6) +\n        step_coefficients(\n            steps_large, steps_small, Explicit, Implicit, -1, 0, 2, 5, false),\n        expected_large_predictor);\n    steps_small.push_back({{5, 1}, 3});\n    steps_large.push_back({{2, 4}, 3});\n    const ExpectedCoefficients expected_large_corrector{\n        {{{1}, {0}}, -1.0 / 6.0},\n        {{{1}, {2}}, -1.0 / 3.0},\n        {{{1}, {2, 2}}, -17.0 / 30.0},\n        {{{1}, {4}}, -2.0 / 5.0},\n        {{{1}, {4, 1}}, -4.0 / 15.0},\n        {{{1}, {5}}, -2.0 / 5.0},\n        {{{2}, {2}}, 59.0 / 36.0},\n        {{{2}, {2, 2}}, 7.0 / 6.0},\n        {{{2}, {4}}, 3.0 / 4.0},\n        {{{2}, {4, 1}}, 4.0 / 9.0},\n        {{{2}, {5}}, 2.0 / 3.0},\n        {{{2, 4}, {2, 2}}, 7.0 / 30.0},\n        {{{2, 4}, {4}}, 3.0 / 20.0},\n        {{{2, 4}, {4, 1}}, 4.0 / 15.0},\n        {{{2, 4}, {5}}, 2.0 / 5.0},\n        {{{2, 4}, {5, 1}}, 5.0 / 12.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Implicit, Implicit, 0, 0, 2, 6, false),\n        expected_large_corrector);\n    const ExpectedCoefficients expected_small_corrector_2{\n        {{{2, 2}, {1}}, 14.0 / 15.0},\n        {{{2, 2}, {2}}, -7.0 / 6.0},\n        {{{2, 2}, {2, 4}}, 7.0 / 30.0},\n        {{{4}, {1}}, 23.0 / 30.0},\n        {{{4}, {2}}, -1.0},\n        {{{4}, {2, 4}}, 3.0 / 20.0},\n        {{{4, 1}, {1}}, 16.0 / 15.0},\n        {{{4, 1}, {2}}, -4.0 / 3.0},\n        {{{4, 1}, {2, 4}}, 4.0 / 15.0},\n        {{{5}, {1}}, -2.0 / 5.0},\n        {{{5}, {2}}, 2.0 / 3.0},\n        {{{5}, {2, 4}}, 2.0 / 5.0},\n        {{{5, 1}, {2, 4}}, 5.0 / 12.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 2, 6, false) -\n        step_coefficients(\n            steps_small, steps_large, Implicit, Explicit, 0, -1, 2, 5, false),\n        expected_small_corrector_2);\n    // clang-format on\n  }\n\n  {\n    INFO(\"Paper example - non-monotonic variable order\");\n    //   1 2       6\n    // 0   2   4 5 6\n    std::vector<IdOrder> steps_large{{{1}, 3}, {{2}, 3}};\n    std::vector<IdOrder> steps_small{{{0}, 3}, {{2}, 3}};\n    // clang-format off\n    const ExpectedCoefficients expected_large_predictor{\n        {{{1}, {0}}, -4.0},\n        {{{1}, {2}}, -4.0},\n        {{{2}, {2}}, 12.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Explicit, Explicit, -1, -1, 2, 6, false),\n        expected_large_predictor);\n    const ExpectedCoefficients expected_small_predictor_0{\n        {{{0}, {1}}, -1.0},\n        {{{2}, {1}}, -1.0},\n        {{{2}, {2}}, 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 2, 4),\n        expected_small_predictor_0);\n    steps_large.push_back({{2, 4}, 3});\n    steps_small.push_back({{2, 2}, 3});\n    const ExpectedCoefficients expected_small_corrector_0{\n        {{{0}, {1}}, -1.0 / 6.0},\n        {{{2}, {1}}, -1.0 / 3.0},\n        {{{2}, {2}}, 5.0 / 3.0},\n        {{{2, 2}, {1}}, -17.0 / 30.0},\n        {{{2, 2}, {2}}, 7.0 / 6.0},\n        {{{2, 2}, {2, 4}}, 7.0 / 30.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 2, 4),\n        expected_small_corrector_0);\n    steps_small.push_back({{4}, 2});\n    const ExpectedCoefficients expected_small_predictor_1{\n        {{{4}, {1}}, -5.0 / 2.0},\n        {{{4}, {2}}, 7.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 4, 5),\n        expected_small_predictor_1);\n    steps_small.push_back({{4, 1}, 2});\n    const ExpectedCoefficients expected_small_corrector_1{\n        {{{4}, {1}}, -7.0 / 15.0},\n        {{{4}, {2}}, 19.0 / 24.0},\n        {{{4}, {2, 4}}, 7.0 / 40.0},\n        {{{4, 1}, {1}}, -4.0 / 15.0},\n        {{{4, 1}, {2}}, 1.0 / 2.0},\n        {{{4, 1}, {2, 4}}, 4.0 / 15.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 4, 5),\n        expected_small_corrector_1);\n    steps_small.push_back({{5}, 2});\n    const ExpectedCoefficients expected_small_predictor_2{\n        {{{5}, {1}}, -7.0 / 2.0},\n        {{{5}, {2}}, 9.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 5, 6),\n        expected_small_predictor_2);\n    steps_small.push_back({{5, 1}, 2});\n    const ExpectedCoefficients expected_small_corrector_2{\n        {{{5}, {1}}, -1.0 / 3.0},\n        {{{5}, {2}}, 1.0 / 2.0},\n        {{{5}, {2, 4}}, 1.0 / 3.0},\n        {{{5, 1}, {2}}, 1.0 / 24.0},\n        {{{5, 1}, {2, 4}}, 11.0 / 24.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 5, 6),\n        expected_small_corrector_2);\n    const ExpectedCoefficients expected_large_corrector{\n        {{{1}, {0}}, -1.0 / 6.0},\n        {{{1}, {2}}, -1.0 / 3.0},\n        {{{1}, {2, 2}}, -17.0 / 30.0},\n        {{{1}, {4}}, -7.0 / 15.0},\n        {{{1}, {4, 1}}, -4.0 / 15.0},\n        {{{1}, {5}}, -1.0 / 3.0},\n        {{{2}, {2}}, 5.0 / 3.0},\n        {{{2}, {2, 2}}, 7.0 / 6.0},\n        {{{2}, {4}}, 19.0 / 24.0},\n        {{{2}, {4, 1}}, 1.0 / 2.0},\n        {{{2}, {5}}, 1.0 / 2.0},\n        {{{2}, {5, 1}}, 1.0 / 24.0},\n        {{{2, 4}, {2, 2}}, 7.0 / 30.0},\n        {{{2, 4}, {4}}, 7.0 / 40.0},\n        {{{2, 4}, {4, 1}}, 4.0 / 15.0},\n        {{{2, 4}, {5}}, 1.0 / 3.0},\n        {{{2, 4}, {5, 1}}, 11.0 / 24.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Implicit, Implicit, 0, 0, 2, 6, false),\n        expected_large_corrector);\n    // clang-format on\n  }\n\n  {\n    INFO(\"Paper example - monotonic variable order\");\n    //   1 2       6\n    // 0   2   4 5 6\n    std::vector<IdOrder> steps_large{{{1}, 3}, {{2}, 3}};\n    std::vector<IdOrder> steps_small{{{0}, 3}, {{2}, 3}};\n    // clang-format off\n    const ExpectedCoefficients expected_small_predictor_0{\n        {{{0}, {1}}, -1.0},\n        {{{2}, {1}}, -1.0},\n        {{{2}, {2}}, 4.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 2, 4),\n        expected_small_predictor_0);\n    steps_small.push_back({{2, 2}, 3});\n    const ExpectedCoefficients expected_small_corrector_0{\n        {{{0}, {1}}, -1.0 / 6.0},\n        {{{2}, {1}}, -1.0 / 3.0},\n        {{{2}, {2}}, 5.0 / 3.0},\n        {{{2, 2}, {1}}, -3.0 / 2.0},\n        {{{2, 2}, {2}}, 7.0 / 3.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Explicit, 0, -1, 2, 4),\n        expected_small_corrector_0);\n    steps_small.push_back({{4}, 2});\n    const ExpectedCoefficients expected_small_predictor_1{\n        {{{4}, {1}}, -5.0 / 2.0},\n        {{{4}, {2}}, 7.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 4, 5),\n        expected_small_predictor_1);\n    steps_small.push_back({{4, 1}, 2});\n    const ExpectedCoefficients expected_small_corrector_1{\n        {{{4}, {1}}, -7.0 / 6.0},\n        {{{4}, {2}}, 5.0 / 3.0},\n        {{{4, 1}, {1}}, -4.0 / 3.0},\n        {{{4, 1}, {2}}, 11.0 / 6.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Explicit, 0, -1, 4, 5),\n        expected_small_corrector_1);\n    steps_small.push_back({{5}, 2});\n    const ExpectedCoefficients expected_small_predictor_2{\n        {{{5}, {1}}, -7.0 / 2.0},\n        {{{5}, {2}}, 9.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Explicit, Explicit, -1, -1, 5, 6),\n        expected_small_predictor_2);\n    const ExpectedCoefficients expected_large_predictor{\n        {{{1}, {0}}, -1.0 / 6.0},\n        {{{1}, {2}}, -1.0 / 3.0},\n        {{{1}, {2, 2}}, -3.0 / 2.0},\n        {{{1}, {4}}, -7.0 / 6.0},\n        {{{1}, {4, 1}}, -4.0 / 3.0},\n        {{{1}, {5}}, -7.0 / 2.0},\n        {{{2}, {2}}, 5.0 / 3.0},\n        {{{2}, {2, 2}}, 7.0 / 3.0},\n        {{{2}, {4}}, 5.0 / 3.0},\n        {{{2}, {4, 1}}, 11.0 / 6.0},\n        {{{2}, {5}}, 9.0 / 2.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Explicit, Explicit, -1, -1, 5, 6) +\n        step_coefficients(\n            steps_large, steps_small, Explicit, Implicit, -1, 0, 2, 5, false),\n        expected_large_predictor);\n    steps_small.push_back({{5, 1}, 2});\n    steps_large.push_back({{2, 4}, 3});\n    const ExpectedCoefficients expected_large_corrector{\n        {{{1}, {0}}, -1.0 / 6.0},\n        {{{1}, {2}}, -1.0 / 3.0},\n        {{{1}, {2, 2}}, -17.0 / 30.0},\n        {{{1}, {4}}, -7.0 / 15.0},\n        {{{1}, {4, 1}}, -4.0 / 15.0},\n        {{{1}, {5}}, -1.0 / 3.0},\n        {{{2}, {2}}, 5.0 / 3.0},\n        {{{2}, {2, 2}}, 7.0 / 6.0},\n        {{{2}, {4}}, 19.0 / 24.0},\n        {{{2}, {4, 1}}, 1.0 / 2.0},\n        {{{2}, {5}}, 1.0 / 2.0},\n        {{{2}, {5, 1}}, 1.0 / 24.0},\n        {{{2, 4}, {2, 2}}, 7.0 / 30.0},\n        {{{2, 4}, {4}}, 7.0 / 40.0},\n        {{{2, 4}, {4, 1}}, 4.0 / 15.0},\n        {{{2, 4}, {5}}, 1.0 / 3.0},\n        {{{2, 4}, {5, 1}}, 11.0 / 24.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_large, steps_small, Implicit, Implicit, 0, 0, 2, 6, false),\n        expected_large_corrector);\n    const ExpectedCoefficients expected_small_corrector_2{\n        {{{2, 2}, {1}}, 14.0 / 15.0},\n        {{{2, 2}, {2}}, -7.0 / 6.0},\n        {{{2, 2}, {2, 4}}, 7.0 / 30.0},\n        {{{4}, {1}}, 7.0 / 10.0},\n        {{{4}, {2}}, -7.0 / 8.0},\n        {{{4}, {2, 4}}, 7.0 / 40.0},\n        {{{4, 1}, {1}}, 16.0 / 15.0},\n        {{{4, 1}, {2}}, -4.0 / 3.0},\n        {{{4, 1}, {2, 4}}, 4.0 / 15.0},\n        {{{5}, {1}}, -1.0 / 3.0},\n        {{{5}, {2}}, 1.0 / 2.0},\n        {{{5}, {2, 4}}, 1.0 / 3.0},\n        {{{5, 1}, {2}}, 1.0 / 24.0},\n        {{{5, 1}, {2, 4}}, 11.0 / 24.0}};\n    CHECK_ITERABLE_APPROX(\n        step_coefficients(\n            steps_small, steps_large, Implicit, Implicit, 0, 0, 2, 6, false) -\n        step_coefficients(\n            steps_small, steps_large, Implicit, Explicit, 0, -1, 2, 5, false),\n        expected_small_corrector_2);\n    // clang-format on\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsLts\", \"[Unit][Time]\") {\n  test_exact_substep_time();\n  test_lts_coefficients_struct();\n  test_apply_coefficients(0.0);\n  test_apply_coefficients(DataVector(5, 0.0));\n  test_lts_coefficients();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_AdamsMoultonPc.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <utility>\n#include <vector>\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/LtsHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/BoundaryHistory.hpp\"\n#include \"Time/BoundaryHistory.tpp\"\n#include \"Time/History.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/StepperErrorTolerances.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsMoultonPc.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nnamespace {\ntemplate <bool Monotonic>\nvoid test_am() {\n  for (size_t order = 2; order < 9; ++order) {\n    CAPTURE(order);\n    const TimeSteppers::AdamsMoultonPc<Monotonic> stepper(order);\n    CHECK(stepper.order() ==\n          variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(order));\n    CHECK(stepper.number_of_past_steps() == order - 2);\n    CHECK(stepper.number_of_substeps() == 2);\n    CHECK(stepper.number_of_substeps_for_error() == 2);\n\n    for (size_t start_points = 0;\n         start_points <= stepper.number_of_past_steps();\n         ++start_points) {\n      CAPTURE(start_points);\n      const double epsilon = std::max(std::pow(1e-3, start_points + 2), 1e-14);\n      TimeStepperTestUtils::integrate_test(stepper, start_points + 2,\n                                           start_points, 1., epsilon);\n      TimeStepperTestUtils::integrate_test_explicit_time_dependence(\n          stepper, start_points + 2, start_points, 1., epsilon);\n\n      const double large_step_epsilon =\n          std::clamp(1.0e2 * std::pow(2.0e-2, start_points + 2), 1e-14, 1.0);\n      TimeStepperTestUtils::integrate_error_test(\n          stepper, start_points + 2, start_points, 1.0, large_step_epsilon, 20,\n          1.0e-4);\n      TimeStepperTestUtils::integrate_error_test(\n          stepper, start_points + 2, start_points, -1.0, large_step_epsilon, 20,\n          1.0e-4);\n\n      TimeStepperTestUtils::integrate_variable_test(stepper, start_points + 2,\n                                                    start_points, epsilon);\n    }\n    TimeStepperTestUtils::check_convergence_order(stepper, {10, 30});\n    {\n      std::pair<int32_t, int32_t> convergence_step_range{10, 30};\n      int32_t stride = 1;\n      if (Monotonic) {\n        // Monotonic dense output is much noisier.\n        convergence_step_range.second = 110 - 10 * static_cast<int32_t>(order);\n        stride = 3 - static_cast<int32_t>(order) / 3;\n      }\n      TimeStepperTestUtils::check_dense_output(stepper, convergence_step_range,\n                                               stride, not Monotonic);\n    }\n  }\n\n  const Slab slab(0., 1.);\n  const Time start = slab.start();\n  const Time mid = slab.start() + slab.duration() / 2;\n  const Time end = slab.end();\n  const auto can_change = [](const bool time_runs_forward, const Time& first,\n                             const Time& second, const Time& now) {\n    const TimeSteppers::AdamsMoultonPc<Monotonic> stepper(2);\n    TimeSteppers::History<double> history(2);\n    history.insert(TimeStepId(time_runs_forward, 0, first), 0., 0.);\n    history.insert(TimeStepId(time_runs_forward, 2, second), 0., 0.);\n    return stepper.can_change_step_size(TimeStepId(time_runs_forward, 4, now),\n                                        history);\n  };\n  CHECK(can_change(true, start, mid, end));\n  CHECK_FALSE(can_change(true, start, end, mid));\n  CHECK(can_change(true, mid, start, end));\n  CHECK_FALSE(can_change(true, mid, end, start));\n  CHECK_FALSE(can_change(true, end, start, mid));\n  CHECK_FALSE(can_change(true, end, mid, start));\n  CHECK(can_change(true, start, mid, mid));\n  CHECK_FALSE(can_change(true, start, mid, start));\n\n  CHECK(can_change(false, end, mid, start));\n  CHECK_FALSE(can_change(false, end, start, mid));\n  CHECK(can_change(false, mid, end, start));\n  CHECK_FALSE(can_change(false, mid, start, end));\n  CHECK_FALSE(can_change(false, start, end, mid));\n  CHECK_FALSE(can_change(false, start, mid, end));\n  CHECK(can_change(false, end, mid, mid));\n  CHECK_FALSE(can_change(false, end, mid, end));\n\n  {\n    TimeSteppers::AdamsMoultonPc<Monotonic> am4(4);\n    TimeSteppers::AdamsMoultonPc<Monotonic> am2(2);\n    CHECK(am4 == am4);\n    CHECK_FALSE(am4 != am4);\n    CHECK(am4 != am2);\n    CHECK_FALSE(am4 == am2);\n\n    test_serialization(am4);\n    test_serialization_via_base<TimeStepper,\n                                TimeSteppers::AdamsMoultonPc<Monotonic>>(4_st);\n  }\n\n  {\n    const std::string name =\n        Monotonic ? \"AdamsMoultonPcMonotonic\" : \"AdamsMoultonPc\";\n    const auto created = TestHelpers::test_factory_creation<\n        TimeStepper, TimeSteppers::AdamsMoultonPc<Monotonic>>(name +\n                                                              \":\\n\"\n                                                              \"  Order: 3\");\n    CHECK(created->order() ==\n          variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(3));\n  }\n\n  {\n    const auto check_order = [](const size_t order, const double phase) {\n      CAPTURE(order);\n      TimeStepperTestUtils::stability_test(\n          TimeSteppers::AdamsMoultonPc<Monotonic>(order), phase);\n    };\n\n    check_order(2, M_PI);\n    check_order(3, 2.504);\n    check_order(4, 2.347);\n    check_order(5, 2.339);\n    check_order(6, 2.368);\n    check_order(7, 2.369);\n    check_order(8, 2.364);\n  }\n}\n\ntemplate <bool Monotonic>\nvoid test_neighbor_data_required(const bool time_runs_forward) {\n  // Test is order-independent\n  const TimeSteppers::AdamsMoultonPc<Monotonic> stepper(4);\n  const Slab slab(0.0, 1.0);\n  const auto long_step = (time_runs_forward ? 1 : -1) * slab.duration() / 2;\n  const auto short_step = long_step / 2;\n\n  const TimeStepId start(time_runs_forward, 1,\n                         time_runs_forward ? slab.start() : slab.end());\n  const TimeStepId long_sub = start.next_substep(long_step, 1.0);\n  const TimeStepId short_sub1 = start.next_substep(short_step, 1.0);\n  const TimeStepId middle(time_runs_forward, 1, start.step_time() + short_step);\n  const TimeStepId short_sub2 = middle.next_substep(short_step, 1.0);\n  const TimeStepId end(time_runs_forward, 1, start.step_time() + long_step);\n\n  const double middle_time = middle.step_time().value();\n\n  if constexpr (Monotonic) {\n    CHECK(not stepper.neighbor_data_required(start, start));\n\n    CHECK(stepper.neighbor_data_required(long_sub, start));\n    CHECK(not stepper.neighbor_data_required(long_sub, long_sub));\n    CHECK(stepper.neighbor_data_required(long_sub, short_sub1));\n    CHECK(stepper.neighbor_data_required(long_sub, middle));\n    CHECK(not stepper.neighbor_data_required(long_sub, short_sub2));\n\n    CHECK(stepper.neighbor_data_required(short_sub1, start));\n    CHECK(not stepper.neighbor_data_required(short_sub1, long_sub));\n    CHECK(not stepper.neighbor_data_required(short_sub1, short_sub1));\n    CHECK(not stepper.neighbor_data_required(short_sub1, middle));\n\n    CHECK(stepper.neighbor_data_required(middle, start));\n    CHECK(not stepper.neighbor_data_required(middle, long_sub));\n    CHECK(stepper.neighbor_data_required(middle, short_sub1));\n    CHECK(not stepper.neighbor_data_required(middle, middle));\n\n    CHECK(stepper.neighbor_data_required(middle_time, start));\n    CHECK(not stepper.neighbor_data_required(middle_time, long_sub));\n    CHECK(stepper.neighbor_data_required(middle_time, short_sub1));\n    CHECK(stepper.neighbor_data_required(middle_time, middle));\n    CHECK(not stepper.neighbor_data_required(middle_time, short_sub2));\n\n    CHECK(stepper.neighbor_data_required(short_sub2, start));\n    CHECK(not stepper.neighbor_data_required(short_sub2, long_sub));\n    CHECK(stepper.neighbor_data_required(short_sub2, short_sub1));\n    CHECK(stepper.neighbor_data_required(short_sub2, middle));\n    CHECK(not stepper.neighbor_data_required(short_sub2, short_sub2));\n\n    CHECK(stepper.neighbor_data_required(end, start));\n    CHECK(stepper.neighbor_data_required(end, long_sub));\n    CHECK(stepper.neighbor_data_required(end, short_sub1));\n    CHECK(stepper.neighbor_data_required(end, middle));\n    CHECK(stepper.neighbor_data_required(end, short_sub2));\n    CHECK(not stepper.neighbor_data_required(end, end));\n  } else {\n    CHECK(not stepper.neighbor_data_required(start, start));\n\n    CHECK(stepper.neighbor_data_required(long_sub, start));\n    CHECK(not stepper.neighbor_data_required(long_sub, long_sub));\n    CHECK(not stepper.neighbor_data_required(long_sub, short_sub1));\n    CHECK(not stepper.neighbor_data_required(long_sub, middle));\n    CHECK(not stepper.neighbor_data_required(long_sub, short_sub2));\n\n    CHECK(stepper.neighbor_data_required(short_sub1, start));\n    CHECK(not stepper.neighbor_data_required(short_sub1, long_sub));\n    CHECK(not stepper.neighbor_data_required(short_sub1, short_sub1));\n    CHECK(not stepper.neighbor_data_required(short_sub1, middle));\n\n    CHECK(stepper.neighbor_data_required(middle, start));\n    CHECK(stepper.neighbor_data_required(middle, long_sub));\n    CHECK(stepper.neighbor_data_required(middle, short_sub1));\n    CHECK(not stepper.neighbor_data_required(middle, middle));\n\n    CHECK(stepper.neighbor_data_required(middle_time, start));\n    CHECK(stepper.neighbor_data_required(middle_time, long_sub));\n    CHECK(stepper.neighbor_data_required(middle_time, short_sub1));\n    CHECK(not stepper.neighbor_data_required(middle_time, middle));\n\n    CHECK(stepper.neighbor_data_required(short_sub2, start));\n    CHECK(stepper.neighbor_data_required(short_sub2, long_sub));\n    CHECK(stepper.neighbor_data_required(short_sub2, short_sub1));\n    CHECK(stepper.neighbor_data_required(short_sub2, middle));\n    CHECK(not stepper.neighbor_data_required(short_sub2, short_sub2));\n\n    CHECK(stepper.neighbor_data_required(end, start));\n    CHECK(stepper.neighbor_data_required(end, long_sub));\n    CHECK(stepper.neighbor_data_required(end, short_sub1));\n    CHECK(stepper.neighbor_data_required(end, middle));\n    CHECK(stepper.neighbor_data_required(end, short_sub2));\n    CHECK(not stepper.neighbor_data_required(end, end));\n  }\n}\n\ntemplate <bool Monotonic>\nvoid test_boundary() {\n  test_neighbor_data_required<Monotonic>(true);\n  test_neighbor_data_required<Monotonic>(false);\n\n  for (size_t order = 2; order < 9; ++order) {\n    CAPTURE(order);\n    const TimeSteppers::AdamsMoultonPc<Monotonic> stepper(order);\n    TimeStepperTestUtils::lts::test_equal_rate(stepper);\n    TimeStepperTestUtils::lts::test_uncoupled(stepper, 1e-12);\n    TimeStepperTestUtils::lts::test_conservation(stepper);\n    // Only test convergence for low-order methods, since it's hard to\n    // find parameters where the high-order ones are in the convergent\n    // limit but not roundoff-dominated.\n    if (order < 5) {\n      TimeStepperTestUtils::lts::test_convergence(stepper, {20, 100}, 20);\n      TimeStepperTestUtils::lts::test_dense_convergence(stepper, {50, 170}, 15);\n    }\n  }\n\n  const TimeSteppers::AdamsMoultonPc<Monotonic> variable_order_stepper(\n      std::nullopt);\n  for (size_t local_order = 2; local_order < 9; ++local_order) {\n    for (size_t remote_order = 2; remote_order < 9; ++remote_order) {\n      const TimeStepperTestUtils::lts::VariableOrderChoice variable_order{\n          local_order, local_order - 2, remote_order, remote_order - 2};\n      TimeStepperTestUtils::lts::test_uncoupled(variable_order_stepper, 1e-12,\n                                                {variable_order});\n      TimeStepperTestUtils::lts::test_conservation(variable_order_stepper,\n                                                   {variable_order});\n    }\n  }\n\n  // These are slow, so just test a few of them.\n  TimeStepperTestUtils::lts::test_convergence(variable_order_stepper, {40, 100},\n                                              20, {{4, 2, 4, 2}});\n  TimeStepperTestUtils::lts::test_convergence(variable_order_stepper, {40, 100},\n                                              20, {{6, 4, 3, 1}});\n  TimeStepperTestUtils::lts::test_convergence(variable_order_stepper, {40, 100},\n                                              20, {{3, 1, 6, 4}});\n  TimeStepperTestUtils::lts::test_dense_convergence(\n      variable_order_stepper, {60, 180}, 20, {{4, 2, 4, 2}});\n  TimeStepperTestUtils::lts::test_dense_convergence(\n      variable_order_stepper, {60, 180}, 20, {{6, 4, 3, 1}});\n  TimeStepperTestUtils::lts::test_dense_convergence(\n      variable_order_stepper, {60, 180}, 20, {{3, 1, 6, 4}});\n}\n\nTimeStepId make_id(const Rational& time_fraction, const uint64_t substep,\n                   const Rational& step_fraction = 0) {\n  ASSERT(substep == 0 or step_fraction != 0, \"Need step size for a substep.\");\n  const Slab slab(0.0, 1.0);\n  const TimeStepId step_id(true, 0, Time(slab, time_fraction));\n  if (substep == 0) {\n    return step_id;\n  } else {\n    return step_id.next_substep(TimeDelta(slab, step_fraction), 1.0);\n  }\n}\n\nusing Counts = std::vector<std::pair<Rational, size_t>>;\n\nCounts substep_counts(const TimeSteppers::ConstBoundaryHistoryTimes& times) {\n  std::vector<std::pair<Rational, size_t>> counts{};\n  counts.reserve(times.size());\n  for (const auto& step : times) {\n    counts.emplace_back(step.step_time().fraction(),\n                        times.number_of_substeps(step));\n  }\n  return counts;\n}\n\nvoid test_non_monotonic_boundary_cleaning() {\n  const TimeSteppers::AdamsMoultonPc<false> am3(3);\n  TimeSteppers::BoundaryHistory<double, double, double> history{};\n\n  // Equal rate\n  history.local().insert(make_id({0, 8}, 0), 3, 0.0);\n  history.local().insert(make_id({1, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({0, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({1, 8}, 0), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{0, 8}, 1}, {{1, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{0, 8}, 1}, {{1, 8}, 1}});\n  history.local().insert(make_id({1, 8}, 1, {1, 8}), 3, 0.0);\n  history.remote().insert(make_id({1, 8}, 1, {1, 8}), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{1, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{1, 8}, 1}});\n\n  // Local finer\n  history.local().insert(make_id({2, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({2, 8}, 0), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{1, 8}, 1}, {{2, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{1, 8}, 1}, {{2, 8}, 1}});\n  history.local().insert(make_id({2, 8}, 1, {1, 8}), 3, 0.0);\n  history.remote().insert(make_id({2, 8}, 1, {2, 8}), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{2, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{1, 8}, 1}, {{2, 8}, 2}});\n  history.local().insert(make_id({3, 8}, 0), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{2, 8}, 1}, {{3, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{1, 8}, 1}, {{2, 8}, 2}});\n  history.local().insert(make_id({3, 8}, 1, {1, 8}), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{3, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{2, 8}, 1}});\n\n  // Local coarser\n  history.local().insert(make_id({4, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({4, 8}, 0), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{3, 8}, 1}, {{4, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{2, 8}, 1}, {{4, 8}, 1}});\n  history.local().insert(make_id({4, 8}, 1, {2, 8}), 3, 0.0);\n  history.remote().insert(make_id({4, 8}, 1, {1, 8}), 3, 0.0);\n  history.remote().insert(make_id({5, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({5, 8}, 1, {1, 8}), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{4, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{5, 8}, 1}});\n}\n\nvoid test_monotonic_boundary_cleaning() {\n  const TimeSteppers::AdamsMoultonPc<true> am3(3);\n  TimeSteppers::BoundaryHistory<double, double, double> history{};\n\n  // Equal rate\n  history.local().insert(make_id({0, 8}, 0), 3, 0.0);\n  history.local().insert(make_id({1, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({0, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({1, 8}, 0), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{0, 8}, 1}, {{1, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{0, 8}, 1}, {{1, 8}, 1}});\n  history.local().insert(make_id({1, 8}, 1, {1, 8}), 3, 0.0);\n  history.remote().insert(make_id({1, 8}, 1, {1, 8}), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{1, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{1, 8}, 1}});\n\n  // Local finer\n  history.local().insert(make_id({2, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({2, 8}, 0), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{1, 8}, 1}, {{2, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{1, 8}, 1}, {{2, 8}, 1}});\n  history.local().insert(make_id({2, 8}, 1, {1, 8}), 3, 0.0);\n  history.remote().insert(make_id({2, 8}, 1, {2, 8}), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{1, 8}, 1}, {{2, 8}, 2}});\n  CHECK(substep_counts(history.remote()) == Counts{{{1, 8}, 1}, {{2, 8}, 2}});\n  history.local().insert(make_id({3, 8}, 0), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) ==\n        Counts{{{1, 8}, 1}, {{2, 8}, 2}, {{3, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{1, 8}, 1}, {{2, 8}, 2}});\n  history.local().insert(make_id({3, 8}, 1, {1, 8}), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{3, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{2, 8}, 1}});\n\n  // Local coarser\n  history.local().insert(make_id({4, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({4, 8}, 0), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{3, 8}, 1}, {{4, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{2, 8}, 1}, {{4, 8}, 1}});\n  history.local().insert(make_id({4, 8}, 1, {2, 8}), 3, 0.0);\n  history.remote().insert(make_id({4, 8}, 1, {1, 8}), 3, 0.0);\n  history.remote().insert(make_id({5, 8}, 0), 3, 0.0);\n  history.remote().insert(make_id({5, 8}, 1, {1, 8}), 3, 0.0);\n  am3.clean_boundary_history(make_not_null(&history));\n  CHECK(substep_counts(history.local()) == Counts{{{4, 8}, 1}});\n  CHECK(substep_counts(history.remote()) == Counts{{{5, 8}, 1}});\n}\n\ntemplate <bool Monotonic>\nvoid test_variable_order() {\n  for (size_t order = 2; order <= 8; ++order) {\n    TimeStepperTestUtils::lts::test_variable_order_consistency(\n        TimeSteppers::AdamsMoultonPc<Monotonic>(std::nullopt),\n        TimeSteppers::AdamsMoultonPc<Monotonic>(order));\n  }\n\n  TimeStepperTestUtils::lts::test_variable_order_boundary_consistency(\n      TimeSteppers::AdamsMoultonPc<Monotonic>(std::nullopt));\n\n  // Check that the variable-order errors look reasonable\n  {\n    const double step = 1.0e-2;\n    const Slab slab(0.0, step);\n    const TimeSteppers::AdamsMoultonPc<Monotonic> variable_stepper(\n        std::nullopt);\n    TimeSteppers::History<double> history{4};\n    TimeStepperTestUtils::initialize_history(\n        slab.end(), make_not_null(&history),\n        [](const double t) { return exp(t); },\n        [](const double y, const double /*t*/) { return y; }, slab.duration(),\n        history.integration_order() - 1);\n    history.insert(TimeStepId(true, 0, slab.start(), 1, slab.duration(), step),\n                   exp(step), exp(step));\n    double y = std::numeric_limits<double>::signaling_NaN();\n    const auto errors = variable_stepper.update_u(\n        make_not_null(&y), history, slab.duration(),\n        StepperErrorTolerances{StepperErrorTolerances::Estimates::AllOrders,\n                               1.0, 0.0});\n    REQUIRE(errors.has_value());\n    // Truncation error for AM is |implicit_corrector_coef| step^{k+1} f^(k).\n    // As with many time-stepper things, the quoted order is the\n    // equivalent global order even though this is a purely local\n    // quantity, hence the +1 in the exponent.\n    const std::array expected_coefs{1.0, 1.0 / 2.0, 5.0 / 12.0, 3.0 / 8.0};\n    // Error is dominated by finite-difference error in f^(k)\n    auto truncation_approx = Approx::custom().epsilon(2.0 * step);\n    for (size_t order = 0; order < expected_coefs.size(); ++order) {\n      REQUIRE(gsl::at(errors->errors, order).has_value());\n      CHECK(*gsl::at(errors->errors, order) ==\n            truncation_approx(gsl::at(expected_coefs, order) *\n                              std::pow(step, order + 1)));\n    }\n  }\n}\n\n// [[Timeout, 60]]\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsMoultonPc\", \"[Unit][Time]\") {\n  test_am<false>();\n  test_boundary<false>();\n  test_non_monotonic_boundary_cleaning();\n  test_variable_order<false>();\n}\n\n// [[Timeout, 60]]\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.AdamsMoultonPcMonotonic\",\n                  \"[Unit][Time]\") {\n  test_am<true>();\n  test_boundary<true>();\n  test_monotonic_boundary_cleaning();\n  test_variable_order<true>();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_ClassicalRungeKutta4.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/ClassicalRungeKutta4.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.ClassicalRungeKutta4\",\n                  \"[Unit][Time]\") {\n  const TimeSteppers::ClassicalRungeKutta4 stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(4));\n  CHECK(stepper.number_of_substeps() == 4);\n  CHECK(stepper.number_of_substeps_for_error() == 5);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 4, 0, 1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 4, 0, -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 4, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 4, 0, 1.0, 1.0e-8, 50,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_error_test(stepper, 4, 0, -1.0, 1.0e-8, 50,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 4, 0, 1.0e-9);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TestHelpers::test_factory_creation<TimeStepper,\n                                     TimeSteppers::ClassicalRungeKutta4>(\n      \"ClassicalRungeKutta4\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper,\n                              TimeSteppers::ClassicalRungeKutta4>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_DormandPrince5.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/DormandPrince5.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.DormandPrince5\", \"[Unit][Time]\") {\n  const TimeSteppers::DormandPrince5 stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(5));\n  CHECK(stepper.number_of_substeps() == 6);\n  CHECK(stepper.number_of_substeps_for_error() == 7);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 5, 0, 1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 5, 0, -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 5, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 5, 0, 1.0, 1.0e-8, 8,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_error_test(stepper, 5, 0, -1.0, 1.0e-8, 8,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 5, 0, 1.0e-9);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::DormandPrince5>(\n      \"DormandPrince5\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::DormandPrince5>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Factory.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <type_traits>\n\n#include \"Time/Slab.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/TimeSteppers/AdamsBashforth.hpp\"\n#include \"Time/TimeSteppers/AdamsMoultonPc.hpp\"\n#include \"Time/TimeSteppers/Factory.hpp\"\n#include \"Time/TimeSteppers/ImexTimeStepper.hpp\"\n#include \"Time/TimeSteppers/LtsTimeStepper.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename StepperList, typename InclusionPredicate>\nvoid check_list_contents(const std::string& description,\n                         InclusionPredicate&& inclusion_predicate) {\n  INFO(description);\n  static_assert(std::is_same_v<\n                tmpl::list_difference<StepperList, TimeSteppers::time_steppers>,\n                tmpl::list<>>);\n  tmpl::for_each<StepperList>(\n      [&]<typename Stepper>(tmpl::type_<Stepper> /*meta*/) {\n        INFO(pretty_type::get_name<Stepper>());\n        CHECK(inclusion_predicate(tmpl::type_<Stepper>{}));\n      });\n  tmpl::for_each<\n      tmpl::list_difference<TimeSteppers::time_steppers, StepperList>>(\n      [&]<typename Stepper>(tmpl::type_<Stepper> /*meta*/) {\n        INFO(pretty_type::get_name<Stepper>());\n        CHECK(not inclusion_predicate(tmpl::type_<Stepper>{}));\n      });\n}\n\ntemplate <typename Stepper>\nStepper create_example(tmpl::type_<Stepper> /*meta*/) {\n  return Stepper{};\n}\n\nTimeSteppers::AdamsBashforth create_example(\n    tmpl::type_<TimeSteppers::AdamsBashforth> /*meta*/) {\n  return TimeSteppers::AdamsBashforth{4};\n}\n\ntemplate <bool Monotonic>\nTimeSteppers::AdamsMoultonPc<Monotonic> create_example(\n    tmpl::type_<TimeSteppers::AdamsMoultonPc<Monotonic>> /*meta*/) {\n  return TimeSteppers::AdamsMoultonPc<Monotonic>{4};\n}\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Factory\", \"[Unit][Time]\") {\n  check_list_contents<TimeSteppers::time_steppers>(\n      \"time_steppers\", []<typename Stepper>(tmpl::type_<Stepper> /*meta*/) {\n        return std::is_convertible_v<Stepper*, TimeStepper*>;\n      });\n  check_list_contents<TimeSteppers::lts_time_steppers>(\n      \"lts_time_steppers\", []<typename Stepper>(tmpl::type_<Stepper> /*meta*/) {\n        return std::is_convertible_v<Stepper*, LtsTimeStepper*>;\n      });\n  check_list_contents<TimeSteppers::imex_time_steppers>(\n      \"imex_time_steppers\",\n      []<typename Stepper>(tmpl::type_<Stepper> /*meta*/) {\n        return std::is_convertible_v<Stepper*, ImexTimeStepper*>;\n      });\n  check_list_contents<TimeSteppers::increasing_substep_time_steppers>(\n      \"increasing_substep_time_steppers\", [](auto stepper_type) {\n        const auto stepper = create_example(stepper_type);\n        const Slab slab(0.0, 1.0);\n        const auto step = slab.duration();\n        TimeStepId id(true, 0, slab.start());\n        while (id.slab_number() == 0) {\n          auto next_id = stepper.next_time_id_for_error(id, step);\n          if (next_id.substep_time() <= id.substep_time()) {\n            return false;\n          }\n          id = next_id;\n        }\n        return true;\n      });\n  check_list_contents<TimeSteppers::monotonic_lts_time_steppers>(\n      \"monotonic_lts_time_steppers\",\n      []<typename Stepper>(tmpl::type_<Stepper> /*meta*/) {\n        return std::is_convertible_v<Stepper*, LtsTimeStepper*> and\n               create_example(tmpl::type_<Stepper>{}).monotonic();\n      });\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Heun2.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/ImexHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Heun2.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Heun2\", \"[Unit][Time]\") {\n  const TimeSteppers::Heun2 stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(2));\n  CHECK(stepper.number_of_substeps() == 2);\n  CHECK(stepper.number_of_substeps_for_error() == 2);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n  TestHelpers::RungeKutta::check_implicit_tableau(stepper, true);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 2, 0, 1.0, 1.0e-6);\n  TimeStepperTestUtils::integrate_test(stepper, 2, 0, -1.0, 1.0e-6);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 2, 0,\n                                                                -1.0, 1.0e-6);\n  TimeStepperTestUtils::integrate_error_test(stepper, 2, 0, 1.0, 1.0e-6, 600,\n                                             1.0e-4);\n  TimeStepperTestUtils::integrate_error_test(stepper, 2, 0, -1.0, 1.0e-6, 600,\n                                             1.0e-4);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 2, 0, 1.0e-6);\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 100}, 1, true);\n\n  TimeStepperTestUtils::imex::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::imex::check_bounded_dense_output(stepper);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::Heun2>(\"Heun2\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Heun2>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Rk3HesthavenSsp.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Rk3HesthavenSsp.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Rk3HesthavenSsp\", \"[Unit][Time]\") {\n  const TimeSteppers::Rk3HesthavenSsp stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(3));\n  CHECK(stepper.number_of_substeps() == 3);\n  CHECK(stepper.number_of_substeps_for_error() == 3);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 3, 0, 1., 1e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 3, 0, -1., 1e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 3, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 3, 0, 1.0, 1.0e-8, 100,\n                                             1.0e-4);\n  TimeStepperTestUtils::integrate_error_test(stepper, 3, 0, -1.0, 1.0e-8, 100,\n                                             1.0e-4);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 3, 0, 1e-9);\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TimeStepperTestUtils::check_strong_stability_preservation(stepper, 1.0);\n\n  TestHelpers::test_factory_creation<TimeStepper,\n                                     TimeSteppers::Rk3HesthavenSsp>(\n      \"Rk3HesthavenSsp\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Rk3HesthavenSsp>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Rk3Kennedy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/ImexHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Rk3Kennedy.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Rk3Kennedy\", \"[Unit][Time]\") {\n  const TimeSteppers::Rk3Kennedy stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(3));\n  CHECK(stepper.number_of_substeps() == 4);\n  CHECK(stepper.number_of_substeps_for_error() == 4);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n  TestHelpers::RungeKutta::check_implicit_tableau(stepper, true);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 3, 0, 1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 3, 0, -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 3, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 3, 0, 1.0, 1.0e-8, 100,\n                                             1.0e-3);\n  TimeStepperTestUtils::integrate_error_test(stepper, 3, 0, -1.0, 1.0e-8, 100,\n                                             1.0e-3);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 3, 0, 1.0e-9);\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TimeStepperTestUtils::imex::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::imex::check_bounded_dense_output(stepper);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::Rk3Kennedy>(\n      \"Rk3Kennedy\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Rk3Kennedy>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Rk3Owren.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Rk3Owren.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Rk3Owren\", \"[Unit][Time]\") {\n  const TimeSteppers::Rk3Owren stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(3));\n  CHECK(stepper.number_of_substeps() == 3);\n  CHECK(stepper.number_of_substeps_for_error() == 3);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 3, 0, 1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 3, 0, -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 3, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 3, 0, 1.0, 1.0e-6, 100,\n                                             1.0e-3);\n  TimeStepperTestUtils::integrate_error_test(stepper, 3, 0, -1.0, 1.0e-6, 100,\n                                             1.0e-3);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 3, 0, 1.0e-9);\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::Rk3Owren>(\n      \"Rk3Owren\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Rk3Owren>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Rk3Pareschi.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/ImexHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Rk3Pareschi.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Rk3Pareschi\", \"[Unit][Time]\") {\n  const TimeSteppers::Rk3Pareschi stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(3));\n  CHECK(stepper.number_of_substeps() == 5);\n  CHECK(stepper.number_of_substeps_for_error() == 5);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n  TestHelpers::RungeKutta::check_implicit_tableau(stepper, false);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 3, 0, 1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 3, 0, -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 3, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 3, 0, 1.0, 1.0e-8, 100,\n                                             1.0e-4);\n  TimeStepperTestUtils::integrate_error_test(stepper, 3, 0, -1.0, 1.0e-8, 100,\n                                             1.0e-4);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 3, 0, 1.0e-9);\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TimeStepperTestUtils::imex::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::imex::check_bounded_dense_output(stepper);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::Rk3Pareschi>(\n      \"Rk3Pareschi\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Rk3Pareschi>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Rk4Kennedy.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/ImexHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Rk4Kennedy.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Rk4Kennedy\", \"[Unit][Time]\") {\n  const TimeSteppers::Rk4Kennedy stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(4));\n  CHECK(stepper.number_of_substeps() == 6);\n  CHECK(stepper.number_of_substeps_for_error() == 6);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n  TestHelpers::RungeKutta::check_implicit_tableau(stepper, true);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 4, 0, 1.0, 1.0e-14);\n  TimeStepperTestUtils::integrate_test(stepper, 4, 0, -1.0, 1.0e-14);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 4, 0,\n                                                                -1.0, 1.0e-14);\n  TimeStepperTestUtils::integrate_error_test(stepper, 4, 0, 1.0, 1.0e-8, 50,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_error_test(stepper, 4, 0, -1.0, 1.0e-8, 50,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 4, 0, 1.0e-14);\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TimeStepperTestUtils::imex::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::imex::check_bounded_dense_output(stepper);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::Rk4Kennedy>(\n      \"Rk4Kennedy\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Rk4Kennedy>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Rk4Owren.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Rk4Owren.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Rk4Owren\", \"[Unit][Time]\") {\n  const TimeSteppers::Rk4Owren stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(4));\n  CHECK(stepper.number_of_substeps() == 5);\n  CHECK(stepper.number_of_substeps_for_error() == 5);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 4, 0, 1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 4, 0, -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 4, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 4, 0, 1.0, 1.0e-8, 20,\n                                             1.0e-3);\n  TimeStepperTestUtils::integrate_error_test(stepper, 4, 0, -1.0, 1.0e-8, 20,\n                                             1.0e-3);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 4, 0, 1.0e-9);\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::Rk4Owren>(\n      \"Rk4Owren\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Rk4Owren>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Rk5Owren.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Rk5Owren.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Rk5Owren\", \"[Unit][Time]\") {\n  const TimeSteppers::Rk5Owren stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(5));\n  CHECK(stepper.number_of_substeps() == 7);\n  CHECK(stepper.number_of_substeps_for_error() == 7);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 5, 0, 1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 5, 0, -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 5, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 5, 0, 1.0, 1.0e-8, 10,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_error_test(stepper, 5, 0, -1.0, 1.0e-8, 10,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 5, 0, 1.0e-9);\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_convergence_order(stepper, {10, 50});\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::Rk5Owren>(\n      \"Rk5Owren\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Rk5Owren>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/TimeSteppers/Test_Rk5Tsitouras.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/TaggedVariant.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/Time/TimeSteppers/RungeKutta.hpp\"\n#include \"Helpers/Time/TimeSteppers/TimeStepperTestUtils.hpp\"\n#include \"Time/TimeSteppers/Rk5Tsitouras.hpp\"\n#include \"Time/TimeSteppers/TimeStepper.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Time.TimeSteppers.Rk5Tsitouras\", \"[Unit][Time]\") {\n  const TimeSteppers::Rk5Tsitouras stepper{};\n\n  CHECK(stepper.order() ==\n        variants::TaggedVariant<TimeSteppers::Tags::FixedOrder>(5));\n  CHECK(stepper.number_of_substeps() == 6);\n  CHECK(stepper.number_of_substeps_for_error() == 7);\n  TestHelpers::RungeKutta::check_tableau(stepper);\n\n  TimeStepperTestUtils::check_substep_properties(stepper);\n  TimeStepperTestUtils::integrate_test(stepper, 5, 0, 1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test(stepper, 5, 0, -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_test_explicit_time_dependence(stepper, 5, 0,\n                                                                -1.0, 1.0e-9);\n  TimeStepperTestUtils::integrate_error_test(stepper, 5, 0, 1.0, 1.0e-8, 8,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_error_test(stepper, 5, 0, -1.0, 1.0e-8, 8,\n                                             1.0e-2);\n  TimeStepperTestUtils::integrate_variable_test(stepper, 5, 0, 1.0e-9);\n  TimeStepperTestUtils::check_convergence_order(stepper, {30, 70});\n  TimeStepperTestUtils::stability_test(stepper);\n  TimeStepperTestUtils::check_dense_output(stepper, {10, 30}, 1, true);\n\n  TestHelpers::test_factory_creation<TimeStepper, TimeSteppers::Rk5Tsitouras>(\n      \"Rk5Tsitouras\");\n  test_serialization(stepper);\n  test_serialization_via_base<TimeStepper, TimeSteppers::Rk5Tsitouras>();\n  // test operator !=\n  CHECK_FALSE(stepper != stepper);\n}\n"
  },
  {
    "path": "tests/Unit/Time/Triggers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Triggers/Test_NearTimes.cpp\n  Triggers/Test_SlabCompares.cpp\n  Triggers/Test_Slabs.cpp\n  Triggers/Test_StepsWithinSlab.cpp\n  Triggers/Test_TimeCompares.cpp\n  Triggers/Test_Times.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Time/Triggers/Test_NearTimes.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Tags/TimeStep.hpp\"\n#include \"Time/Time.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/Triggers/NearTimes.hpp\"\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<TimeSequence<double>,\n                             TimeSequences::all_time_sequences<double>>,\n                  tmpl::pair<Trigger, tmpl::list<Triggers::NearTimes>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Triggers.NearTimes\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  using Direction = Triggers::NearTimes::Direction;\n  using Unit = Triggers::NearTimes::Unit;\n\n  const auto check = [](const double time_in, const double range_in,\n                        const std::vector<double>& trigger_times_in,\n                        const Direction direction, const bool expected) {\n    const auto check_signs = [&direction, &expected, &trigger_times_in,\n                              &time_in](const TimeDelta time_step_in,\n                                        const double range, const Unit unit) {\n      const auto check_calls = [&direction, &expected, &range, &unit](\n                                   std::vector<double> trigger_times,\n                                   const double time,\n                                   const TimeDelta& time_step) {\n        CAPTURE(trigger_times);\n        CAPTURE(range);\n        CAPTURE(static_cast<int>(unit));\n        CAPTURE(static_cast<int>(direction));\n        CAPTURE(time);\n        CAPTURE(time_step);\n        const std::unique_ptr<Trigger> trigger =\n            std::make_unique<Triggers::NearTimes>(\n                std::make_unique<TimeSequences::Specified<double>>(\n                    std::move(trigger_times)),\n                range, unit, direction);\n        const auto sent_trigger = serialize_and_deserialize(trigger);\n\n        const auto box = db::create<\n            db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                              Tags::Time, Tags::TimeStep>>(\n            Metavariables{}, time, time_step);\n\n        CHECK(trigger->is_triggered(box) == expected);\n        CHECK(sent_trigger->is_triggered(box) == expected);\n      };\n\n      check_calls(trigger_times_in, time_in, time_step_in);\n      std::vector<double> negated_trigger_times = trigger_times_in;\n      alg::for_each(negated_trigger_times, [](double& x) { x = -x; });\n      check_calls(negated_trigger_times, -time_in, -time_step_in);\n    };\n\n    // Absolute position of slab should not matter.\n    // Slab size 100, step size 10\n    const TimeDelta large_step = Slab(10000.0, 10100.0).duration() / 10;\n    // Slab size 0.1, step_size 0.1\n    const TimeDelta small_step = Slab(10000.0, 10000.1).duration();\n\n    check_signs(large_step, range_in, Unit::Time);\n    check_signs(large_step, range_in / 10.0, Unit::Step);\n    check_signs(large_step, range_in / 100.0, Unit::Slab);\n    check_signs(small_step, range_in, Unit::Time);\n    check_signs(small_step, range_in / 0.1, Unit::Step);\n    check_signs(small_step, range_in / 0.1, Unit::Slab);\n  };\n\n  check(5.0, 3.0, {}, Direction::Both, false);\n  check(5.0, 3.0, {}, Direction::Before, false);\n  check(5.0, 3.0, {}, Direction::After, false);\n  check(5.0, 3.0, {6.0}, Direction::Both, true);\n  check(5.0, 3.0, {6.0}, Direction::Before, true);\n  check(5.0, 3.0, {6.0}, Direction::After, false);\n  check(5.0, 3.0, {4.0}, Direction::Both, true);\n  check(5.0, 3.0, {4.0}, Direction::Before, false);\n  check(5.0, 3.0, {4.0}, Direction::After, true);\n  check(5.0, 3.0, {4.0, 6.0}, Direction::Both, true);\n  check(5.0, 3.0, {4.0, 6.0}, Direction::Before, true);\n  check(5.0, 3.0, {4.0, 6.0}, Direction::After, true);\n  check(5.0, 3.0, {9.0}, Direction::Both, false);\n  check(5.0, 3.0, {9.0}, Direction::Before, false);\n  check(5.0, 3.0, {9.0}, Direction::After, false);\n  check(5.0, 3.0, {1.0}, Direction::Both, false);\n  check(5.0, 3.0, {1.0}, Direction::Before, false);\n  check(5.0, 3.0, {1.0}, Direction::After, false);\n  check(5.0, 3.0, {1.0, 9.0}, Direction::Both, false);\n  check(5.0, 3.0, {1.0, 9.0}, Direction::Before, false);\n  check(5.0, 3.0, {1.0, 9.0}, Direction::After, false);\n\n  TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n      \"NearTimes:\\n\"\n      \"  Times:\\n\"\n      \"    Specified:\\n\"\n      \"      Values: [2.0, 1.0, 3.0, 2.0]\\n\"\n      \"  Range: 0.3\\n\"\n      \"  Direction: Before\\n\"\n      \"  Unit: Time\");\n  CHECK(TestHelpers::test_creation<Unit>(\"Time\") == Unit::Time);\n  CHECK(TestHelpers::test_creation<Unit>(\"Step\") == Unit::Step);\n  CHECK(TestHelpers::test_creation<Unit>(\"Slab\") == Unit::Slab);\n  CHECK(TestHelpers::test_creation<Direction>(\"Before\") == Direction::Before);\n  CHECK(TestHelpers::test_creation<Direction>(\"After\") == Direction::After);\n  CHECK(TestHelpers::test_creation<Direction>(\"Both\") == Direction::Both);\n}\n"
  },
  {
    "path": "tests/Unit/Time/Triggers/Test_SlabCompares.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdint>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/Triggers/SlabCompares.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Trigger, tmpl::list<Triggers::SlabCompares>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Triggers.SlabCompares\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto trigger =\n      TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n          \"SlabCompares:\\n\"\n          \"  Comparison: GreaterThan\\n\"\n          \"  Value: 3\");\n\n  const auto sent_trigger = serialize_and_deserialize(trigger);\n\n  const Slab slab(0., 1.);\n  {\n    const auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::TimeStepId>>(\n        Metavariables{}, TimeStepId(true, 2, slab.start()));\n    CHECK(not trigger->is_triggered(box));\n    CHECK(not sent_trigger->is_triggered(box));\n  }\n  {\n    const auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::TimeStepId>>(\n        Metavariables{}, TimeStepId(true, 3, slab.start()));\n    CHECK(not trigger->is_triggered(box));\n    CHECK(not sent_trigger->is_triggered(box));\n  }\n  {\n    const auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::TimeStepId>>(\n        Metavariables{}, TimeStepId(true, 4, slab.start()));\n    CHECK(trigger->is_triggered(box));\n    CHECK(sent_trigger->is_triggered(box));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/Triggers/Test_Slabs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdint>\n#include <initializer_list>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Slab.hpp\"\n#include \"Time/Tags/TimeStepId.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/TimeStepId.hpp\"\n#include \"Time/Triggers/Slabs.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<TimeSequence<std::uint64_t>,\n                             TimeSequences::all_time_sequences<std::uint64_t>>,\n                  tmpl::pair<Trigger, tmpl::list<Triggers::Slabs>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Triggers.Slabs\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto trigger =\n      TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n          \"Slabs:\\n\"\n          \"  Specified:\\n\"\n          \"    Values: [3, 6, 8]\");\n\n  const auto sent_trigger = serialize_and_deserialize(trigger);\n\n  const Slab slab(0., 1.);\n  auto box = db::create<db::AddSimpleTags<\n      Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::TimeStepId>>(\n      Metavariables{}, TimeStepId(true, 0, slab.start()));\n  for (const bool expected :\n       {false, false, false, true, false, false, true, false, true, false}) {\n    CHECK(sent_trigger->is_triggered(box) == expected);\n    db::mutate<Tags::TimeStepId>(\n        [&slab](const gsl::not_null<TimeStepId*> time_id) {\n          *time_id = TimeStepId(true, time_id->slab_number(),\n                                time_id->step_time(), 1, slab.duration(),\n                                time_id->step_time().value());\n        },\n        make_not_null(&box));\n    CHECK(sent_trigger->is_triggered(box) == expected);\n    db::mutate<Tags::TimeStepId>(\n        [](const gsl::not_null<TimeStepId*> time_id) {\n          *time_id = TimeStepId(true, time_id->slab_number() + 1,\n                                time_id->step_time());\n        },\n        make_not_null(&box));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/Triggers/Test_StepsWithinSlab.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdint>\n#include <initializer_list>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Tags/StepNumberWithinSlab.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/Triggers/StepsWithinSlab.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<TimeSequence<std::uint64_t>,\n                             TimeSequences::all_time_sequences<std::uint64_t>>,\n                  tmpl::pair<Trigger, tmpl::list<Triggers::StepsWithinSlab>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Triggers.StepsWithSlab\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto trigger =\n      TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n          \"StepsWithinSlab:\\n\"\n          \"  Specified:\\n\"\n          \"    Values: [3, 6, 8]\");\n\n  const auto sent_trigger = serialize_and_deserialize(trigger);\n\n  auto box = db::create<\n      db::AddSimpleTags<Parallel::Tags::MetavariablesImpl<Metavariables>,\n                        Tags::StepNumberWithinSlab>>(Metavariables{},\n                                                     uint64_t{0});\n  for (const bool expected :\n       {false, false, false, true, false, false, true, false, true, false}) {\n    CHECK(sent_trigger->is_triggered(box) == expected);\n    db::mutate<Tags::StepNumberWithinSlab>(\n        [](const gsl::not_null<uint64_t*> step_within_slab) {\n          ++(*step_within_slab);\n        },\n        make_not_null(&box));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/Triggers/Test_TimeCompares.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdint>\n#include <memory>\n#include <pup.h>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/Triggers/TimeCompares.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<Trigger, tmpl::list<Triggers::TimeCompares>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Triggers.TimeCompares\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto trigger =\n      TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n          \"TimeCompares:\\n\"\n          \"  Comparison: GreaterThan\\n\"\n          \"  Value: 3.5\");\n\n  const auto sent_trigger = serialize_and_deserialize(trigger);\n\n  {\n    const auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::Time>>(\n        Metavariables{}, 3.4);\n    CHECK(not trigger->is_triggered(box));\n    CHECK(not sent_trigger->is_triggered(box));\n  }\n  {\n    const auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::Time>>(\n        Metavariables{}, 3.5);\n    CHECK(not trigger->is_triggered(box));\n    CHECK(not sent_trigger->is_triggered(box));\n  }\n  {\n    const auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::Time>>(\n        Metavariables{}, 3.6);\n    CHECK(trigger->is_triggered(box));\n    CHECK(sent_trigger->is_triggered(box));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Time/Triggers/Test_Times.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <vector>\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/MetavariablesTag.hpp\"\n#include \"Framework/TestCreation.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Options/Protocols/FactoryCreation.hpp\"\n#include \"ParallelAlgorithms/EventsAndTriggers/Trigger.hpp\"\n#include \"Time/Tags/Time.hpp\"\n#include \"Time/TimeSequence.hpp\"\n#include \"Time/Triggers/Times.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct Metavariables {\n  using component_list = tmpl::list<>;\n  struct factory_creation\n      : tt::ConformsTo<Options::protocols::FactoryCreation> {\n    using factory_classes =\n        tmpl::map<tmpl::pair<TimeSequence<double>,\n                             TimeSequences::all_time_sequences<double>>,\n                  tmpl::pair<Trigger, tmpl::list<Triggers::Times>>>;\n  };\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Time.Triggers.Times\", \"[Unit][Time]\") {\n  register_factory_classes_with_charm<Metavariables>();\n\n  const auto check = [](const double time,\n                        const std::vector<double>& trigger_times,\n                        const bool expected) {\n    CAPTURE(time);\n    CAPTURE(trigger_times);\n    const std::unique_ptr<Trigger> trigger = std::make_unique<Triggers::Times>(\n        std::make_unique<TimeSequences::Specified<double>>(trigger_times));\n    const auto sent_trigger = serialize_and_deserialize(trigger);\n\n    const auto box = db::create<db::AddSimpleTags<\n        Parallel::Tags::MetavariablesImpl<Metavariables>, Tags::Time>>(\n        Metavariables{}, time);\n    CHECK(trigger->is_triggered(box) == expected);\n    CHECK(sent_trigger->is_triggered(box) == expected);\n  };\n\n  check(1.0, {}, false);\n  check(1.0, {1.0}, true);\n  check(1.0, {2.0}, false);\n  check(1.0, {0.0, 1.0, 2.0}, true);\n  check(1.0, {2.0, 1.0, 0.0}, true);\n\n  TestHelpers::test_creation<std::unique_ptr<Trigger>, Metavariables>(\n      \"Times:\\n\"\n      \"  Specified:\\n\"\n      \"    Values: [2.0, 1.0, 3.0, 2.0]\");\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Autodiff/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (NOT SPECTRE_AUTODIFF)\n  return()\nendif()\n\nset(LIBRARY \"Test_Autodiff\")\n\nset(LIBRARY_SOURCES\n  Test_Autodiff.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Autodiff\n  )\n"
  },
  {
    "path": "tests/Unit/Utilities/Autodiff/Test_Autodiff.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Utilities/Autodiff/Autodiff.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n\nnamespace {\nusing Dual = autodiff::dual;\nusing Var = autodiff::var;\n\ntemplate <typename DataType>\nDataType f1(const DataType& x, const double param) {\n  return square(x) * param;\n}\n\ntemplate <typename DataType>\nstd::array<DataType, 2> f2(const std::array<DataType, 2>& x) {\n  return {{square(x[0]) * x[1], cube(x[1])}};\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.Autodiff\", \"[Unit][DataStructures]\") {\n  {\n    INFO(\"Single number forward mode\");\n    Dual x = 2.0;\n    const double param = 3.0;\n    const auto df_dx = autodiff::derivative(&f1<Dual>, wrt(x), at(x, param));\n    CHECK(df_dx == approx(12.0));\n  }\n  {\n    INFO(\"Single number reverse mode\");\n    const Var x = 2.0;\n    const double param = 3.0;\n    const Var u = f1<Var>(x, param);\n    const auto [df_dx] =\n        autodiff::derivatives(u, wrt(x));\n    CHECK(df_dx == approx(12.0));\n  }\n  {\n    INFO(\"SIMD number forward mode\");\n    using BatchType = simd::batch<double>;\n    using BatchDual = autodiff::HigherOrderDual<2, BatchType>;\n    BatchDual x = BatchType(2.0);\n    const double param = 3.0;\n    autodiff::seed<1>(x, 1.0);\n    autodiff::seed<2>(x, 1.0);\n    const auto fx = f1(x, param);\n    const auto df_dx = autodiff::derivative<1>(fx);\n    std::array<double, BatchType::size> lanes{};\n    df_dx.store_unaligned(lanes.data());\n    for (const double lane : lanes) {\n      CHECK(lane == approx(12.0));\n    }\n    const auto df_dxx = autodiff::derivative<2>(fx);\n    df_dxx.store_unaligned(lanes.data());\n    for (const double lane : lanes) {\n      CHECK(lane == approx(6.0));\n    }\n  }\n  {\n    INFO(\"SIMD number reverse mode\");\n    using BatchType = simd::batch<double>;\n    using BatchVar = autodiff::Variable<BatchType>;\n    BatchVar x = BatchType(2.0);\n    const double param = 3.0;\n    const auto u = f1(x, param);\n    const auto [u_x] = derivativesx(u, wrt(x));\n    const auto [u_xx] = derivativesx(u_x, wrt(x));\n    std::array<double, BatchType::size> lanes{};\n    autodiff::val(u_x).store_unaligned(lanes.data());\n    for (const double lane : lanes) {\n      CHECK(lane == approx(12.0));\n    }\n    autodiff::val(u_xx).store_unaligned(lanes.data());\n    for (const double lane : lanes) {\n      CHECK(lane == approx(6.0));\n    }\n  }\n  {\n    INFO(\"Jacobian forward mode\");\n    std::array<Dual, 2> x = {2.0, 3.0};\n    std::array<std::array<double, 2>, 2> expected_df_dx{\n        {{12.0, 4.0}, {0.0, 27.0}}};\n\n    for (size_t j = 0; j < 2; ++j) {\n      for (size_t i = 0; i < 2; ++i) {\n        autodiff::seed<1>(x.at(i), 1.0);\n        autodiff::seed<1>(x.at((i + 1) % 2), 0.0);\n        const std::array<Dual, 2> fx = f2(x);\n        const auto dfj_dxi = autodiff::derivative(gsl::at(fx, j));\n        CHECK(dfj_dxi == approx(expected_df_dx.at(j).at(i)));\n      }\n    }\n  }\n  {\n    INFO(\"Jacobian reverse mode\");\n    std::array<Var, 2> x = {2.0, 3.0};\n    std::array<std::array<double, 2>, 2> expected_df_dx{\n        {{12.0, 4.0}, {0.0, 27.0}}};\n    std::array<Var, 2> fx = f2(x);\n\n    for (size_t j = 0; j < 2; ++j) {\n      const auto dfj_dxi = autodiff::derivatives(\n          gsl::at(fx, j), wrt(x[0], x[1]));\n      for (size_t i = 0; i < 2; ++i) {\n        CHECK(dfj_dxi.at(i) == approx(expected_df_dx.at(j).at(i)));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_Utilities\")\n\nset(LIBRARY_SOURCES\n  Test_Algorithm.cpp\n  Test_Array.cpp\n  Test_Base64.cpp\n  Test_Blas.cpp\n  Test_CachedFunction.cpp\n  Test_CallWithDynamicType.cpp\n  Test_CartesianProduct.cpp\n  Test_CleanupRoutine.cpp\n  Test_CloneUniquePtrs.cpp\n  Test_ConstantExpressions.cpp\n  Test_ContainerHelpers.cpp\n  Test_DereferenceWrapper.cpp\n  Test_EqualWithinRoundoff.cpp\n  Test_FileSystem.cpp\n  Test_Formaline.cpp\n  Test_FractionUtilities.cpp\n  Test_Functional.cpp\n  Test_GetOutput.cpp\n  Test_Gsl.cpp\n  Test_MakeArray.cpp\n  Test_MakeSignalingNan.cpp\n  Test_MakeString.cpp\n  Test_MakeVector.cpp\n  Test_MakeWithValue.cpp\n  Test_Math.cpp\n  Test_Numeric.cpp\n  Test_OptionalHelpers.cpp\n  Test_Overloader.cpp\n  Test_ParallelInfo.cpp\n  Test_PrettyType.cpp\n  Test_PrintHelpers.cpp\n  Test_ProtocolHelpers.cpp\n  Test_Rational.cpp\n  Test_Registration.cpp\n  Test_Requires.cpp\n  Test_SetNumberOfGridPoints.cpp\n  Test_SnakeCase.cpp\n  Test_SplitTuple.cpp\n  Test_StaticCache.cpp\n  Test_StdArrayHelpers.cpp\n  Test_StdHelpers.cpp\n  Test_StlBoilerplate.cpp\n  Test_TMPL.cpp\n  Test_TMPLDocumentation.cpp\n  Test_Tuple.cpp\n  Test_TupleSlice.cpp\n  Test_VectorAlgebra.cpp\n  Test_WrapText.cpp\n  )\n\nadd_subdirectory(Autodiff)\nadd_subdirectory(ErrorHandling)\nadd_subdirectory(Kokkos)\nadd_subdirectory(Protocols)\nadd_subdirectory(Serialization)\nadd_subdirectory(StdHelpers)\nadd_subdirectory(System)\nadd_subdirectory(TypeTraits)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  Boost::boost\n  CoordinateMaps\n  DataStructures\n  Options\n  Serialization\n  Utilities\n)\n"
  },
  {
    "path": "tests/Unit/Utilities/ErrorHandling/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_ErrorHandling\")\n\nset(LIBRARY_SOURCES\n  Test_AbortWithErrorMessage.cpp\n  Test_AssertAndError.cpp\n  Test_CaptureForError.cpp\n  Test_Error.cpp\n  Test_FloatingPointExceptions.cpp\n  Test_SegfaultHandler.cpp\n  Test_Strerror.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\"\n  # Run in Charm++ environment to avoid issues with the segfault handler test\n  # on some systems (macOS).\n  WITH_CHARM\n  )\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  ErrorHandling\n  )\n"
  },
  {
    "path": "tests/Unit/Utilities/ErrorHandling/Test_AbortWithErrorMessage.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Utilities/ErrorHandling/AbortWithErrorMessage.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.AbortWithErrorMessage\",\n                  \"[Unit][ErrorHandling]\") {\n  CHECK_THROWS_WITH(\n      abort_with_error_message(\"a == b\", __FILE__, __LINE__,\n                               static_cast<const char*>(__PRETTY_FUNCTION__),\n                               \"Test Abort\"),\n      Catch::Matchers::ContainsSubstring(\"ASSERT FAILED\") &&\n          Catch::Matchers::ContainsSubstring(\"a == b\") &&\n          Catch::Matchers::ContainsSubstring(\"Test Abort\") &&\n          Catch::Matchers::ContainsSubstring(\"Test_AbortWithErrorMessage\") &&\n          Catch::Matchers::ContainsSubstring(\"Stack trace:\"));\n  CHECK_THROWS_WITH(\n      abort_with_error_message<SpectreError>(\n          __FILE__, __LINE__, static_cast<const char*>(__PRETTY_FUNCTION__),\n          \"Test Error\"),\n      Catch::Matchers::ContainsSubstring(\"ERROR\") &&\n          Catch::Matchers::ContainsSubstring(\"Test Error\") &&\n          Catch::Matchers::ContainsSubstring(\"Test_AbortWithErrorMessage\") &&\n          Catch::Matchers::ContainsSubstring(\"Stack trace:\"));\n  CHECK_THROWS_WITH(\n      abort_with_error_message_no_trace(\n          __FILE__, __LINE__, static_cast<const char*>(__PRETTY_FUNCTION__),\n          \"Test no trace\"),\n      Catch::Matchers::ContainsSubstring(\"ERROR\") &&\n          Catch::Matchers::ContainsSubstring(\"Test no trace\") &&\n          Catch::Matchers::ContainsSubstring(\"Test_AbortWithErrorMessage\") &&\n          not Catch::Matchers::ContainsSubstring(\"Stack trace:\"));\n  CHECK_THROWS_AS(\n      abort_with_error_message_no_trace(\n          __FILE__, __LINE__, static_cast<const char*>(__PRETTY_FUNCTION__),\n          \"Test no trace\"),\n      std::runtime_error);\n  {\n    INFO(\"Demangling\");\n    CHECK_THROWS_WITH(\n        abort_with_error_message<SpectreError>(\n            __FILE__, __LINE__, static_cast<const char*>(__PRETTY_FUNCTION__),\n            \"Test demangling\"),\n        Catch::Matchers::ContainsSubstring(\"Catch::Session::run()\"));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/ErrorHandling/Test_AssertAndError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n\nnamespace {\n#ifdef SPECTRE_DEBUG\n[[noreturn]] void trigger_assert() { ASSERT(false, \"Testing assert\"); }\n#endif\n\n[[noreturn]] void trigger_error() { ERROR(\"Testing error\"); }\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.AssertAndError\",\n                  \"[Unit][ErrorHandling]\") {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(trigger_assert(),\n                    Catch::Matchers::ContainsSubstring(\"Testing assert\") &&\n                        Catch::Matchers::ContainsSubstring(\"false\"));\n#endif\n  CHECK_THROWS_WITH(trigger_error(),\n                    Catch::Matchers::ContainsSubstring(\"Testing error\"));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/ErrorHandling/Test_CaptureForError.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/ErrorHandling/CaptureForError.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.CaptureForError\",\n                  \"[Unit][ErrorHandling]\") {\n  CHECK_THROWS_WITH(\n      []() { ERROR(\"Boom\"); }(),\n      not Catch::Matchers::ContainsSubstring(\"Captured variables:\"));\n\n  std::vector<int> vector_var{1, 2, 3};\n  const int int_var = 7;\n  CAPTURE_FOR_ERROR(vector_var);\n  CAPTURE_FOR_ERROR(int_var);\n  vector_var.push_back(4);\n  CHECK_THROWS_WITH(\n      []() { ERROR(\"Boom\"); }(),\n      Catch::Matchers::ContainsSubstring(\"Captured variables:\\n\"\n                                         \"vector_var = (1,2,3,4)\\n\"\n                                         \"int_var = 7\"));\n  // Check that the list is left in the same state.\n  CHECK_THROWS_WITH(\n      []() { ERROR(\"Boom\"); }(),\n      Catch::Matchers::ContainsSubstring(\"Captured variables:\\n\"\n                                         \"vector_var = (1,2,3,4)\\n\"\n                                         \"int_var = 7\"));\n  // Test an additional capture in a nested scope\n  CHECK_THROWS_WITH(\n      []() {\n        const int another_int = 12;\n        CAPTURE_FOR_ERROR(another_int);\n        ERROR(\"Boom\");\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Captured variables:\\n\"\n                                         \"vector_var = (1,2,3,4)\\n\"\n                                         \"int_var = 7\\n\"\n                                         \"another_int = 12\"));\n  // And make sure it's gone again.\n  CHECK_THROWS_WITH([]() { ERROR(\"Boom\"); }(),\n                    not Catch::Matchers::ContainsSubstring(\"another_int\"));\n\n  // Check precision\n  const double high_precision = 1.2345678901234;\n  CAPTURE_FOR_ERROR(high_precision);\n  CHECK_THROWS_WITH([]() { ERROR(\"Boom\"); }(),\n                    Catch::Matchers::ContainsSubstring(\"234567890123\"));\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      []() { ASSERT(false, \"Boom\"); }(),\n      Catch::Matchers::ContainsSubstring(\"Captured variables:\\n\"\n                                         \"vector_var = (1,2,3,4)\\n\"\n                                         \"int_var = 7\"));\n#endif  // SPECTRE_DEBUG\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/ErrorHandling/Test_Error.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <exception>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\ntemplate <typename ExceptionTypeToThrow>\nvoid test() {\n  CHECK_THROWS_MATCHES([]() { ERROR_AS(\"Test error\", ExceptionTypeToThrow); }(),\n                       ExceptionTypeToThrow,\n                       Catch::Matchers::MessageMatches(\n                           Catch::Matchers::ContainsSubstring(\"ERROR\") &&\n                           Catch::Matchers::ContainsSubstring(\"Test error\")));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.Error\", \"[Unit][ErrorHandling]\") {\n  CHECK_THROWS_MATCHES([]() { ERROR(\"Test error\"); }(), SpectreError,\n                       Catch::Matchers::MessageMatches(\n                           Catch::Matchers::ContainsSubstring(\"ERROR\") &&\n                           Catch::Matchers::ContainsSubstring(\"Test error\")));\n\n  using exceptions_list =\n      tmpl::list<std::logic_error, std::invalid_argument, std::domain_error,\n                 std::length_error, std::out_of_range,\n\n                 std::runtime_error, std::range_error, std::overflow_error,\n                 std::underflow_error,\n\n                 std::ios_base::failure,\n\n                 SpectreError, SpectreAssert, convergence_error>;\n  tmpl::for_each<exceptions_list>([](auto exception_type_v) {\n    test<tmpl::type_from<decltype(exception_type_v)>>();\n  });\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/ErrorHandling/Test_FloatingPointExceptions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <catch2/matchers/catch_matchers.hpp>\n#include <cmath>\n#include <limits>\n#include <stdexcept>\n\n#include \"Utilities/ErrorHandling/Exceptions.hpp\"\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n\n#if SPECTRE_FPE_CSR\n#include <xmmintrin.h>\n#elif SPECTRE_FPE_FENV\n#include <cfenv>\n#endif\n\n// Trapping floating-point exceptions is apparently unsupported on\n// the arm64 architecture, so when building on Apple Silicon or Linux aarch64,\n// directly call the fpe_signal_handler in these tests so that they pass.\n\n#if not((defined(__APPLE__) and defined(__arm64__)) or defined(__aarch64__))\nnamespace {\n// Compilers (both gcc and clang) seem prone to deciding that these\n// can't actually throw exceptions and then optimizing away the\n// try-catch logic (despite explicit requests to treat floating-point\n// instructions as potentially throwing).  All this nonsense is to\n// convince them not to do that.  In real code nothing is likely to be\n// simple enough that the compiler can \"prove\" to itself that no\n// exception can be thrown, so hopefully this won't be a real issue.\n[[noreturn]] __attribute__((noinline)) void throw_invalid() {\n  volatile double x = -1.0;\n  volatile double invalid = sqrt(x);\n  static_cast<void>(invalid);\n  asm(\"\");\n  throw std::runtime_error(\"wrong\");\n}\n\n[[noreturn]] __attribute__((noinline)) void throw_overflow() {\n  volatile double overflow = std::numeric_limits<double>::max();\n  overflow = overflow * 1.0e300;\n  (void)overflow;\n  asm(\"\");\n  throw std::runtime_error(\"wrong\");\n}\n\n[[noreturn]] __attribute__((noinline)) void throw_div_by_zero() {\n  volatile double div_by_zero = 1.0;\n  div_by_zero = div_by_zero / 0.0;\n  (void)div_by_zero;\n  asm(\"\");\n  throw std::runtime_error(\"wrong\");\n}\n}  // namespace\n#endif\n\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.FloatingPointExceptions\",\n                  \"[ErrorHandling][Unit]\") {\n#if (defined(__APPLE__) and defined(__arm64__)) or defined(__aarch64__)\n  CHECK(true);\n#else\n  enable_floating_point_exceptions();\n  CHECK_THROWS_MATCHES(\n      throw_invalid(), SpectreFpe,\n      Catch::Matchers::MessageMatches(\n          Catch::Matchers::ContainsSubstring(\"Floating point exception!\")));\n\n  CHECK_THROWS_MATCHES(\n      throw_overflow(), SpectreFpe,\n      Catch::Matchers::MessageMatches(\n          Catch::Matchers::ContainsSubstring(\"Floating point exception!\")));\n\n  CHECK_THROWS_MATCHES(\n      throw_div_by_zero(), SpectreFpe,\n      Catch::Matchers::MessageMatches(\n          Catch::Matchers::ContainsSubstring(\"Floating point exception!\")));\n#endif\n}\n\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.FloatingPointExceptions.Disable\",\n                  \"[ErrorHandling][Unit]\") {\n  enable_floating_point_exceptions();\n  disable_floating_point_exceptions();\n  double x = -1.0;\n  double invalid = sqrt(x);\n  static_cast<void>(invalid);\n  volatile double overflow = std::numeric_limits<double>::max();\n  overflow = overflow * 1.0e300;\n  (void)overflow;\n  volatile double div_by_zero = 1.0;\n  div_by_zero = div_by_zero / 0.0;\n  (void)div_by_zero;\n  CHECK(true);\n}\n\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.FloatingPointExceptions.ScopedFpeState\",\n                  \"[ErrorHandling][Unit]\") {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      []() {\n        ScopedFpeState s{ScopedFpeState::DoNotSave{}};\n        s.set_exceptions(true);\n      }(),\n      Catch::Matchers::ContainsSubstring(\"FPE state not saved\"));\n  CHECK_THROWS_WITH(\n      []() {\n        ScopedFpeState s{true};\n        s.save_exceptions();\n      }(),\n      Catch::Matchers::ContainsSubstring(\"FPE state already saved\"));\n#endif  // SPECTRE_DEBUG\n\n#if (defined(__APPLE__) and defined(__arm64__)) or defined(__aarch64__)\n  CHECK(true);\n  return;\n  // Don't stick the rest of the function in an #else or something\n  // because it should still compile.\n#pragma GCC diagnostic ignored \"-Wunreachable-code\"\n#endif\n\n  // Stack unwinding from a signal handler doesn't work on all\n  // compilers, so we can't actually test causing FPEs.\n  const auto exceptions_enabled = []() {\n#if SPECTRE_FPE_CSR\n    return (_mm_getcsr() & _MM_MASK_OVERFLOW) == 0;\n#elif SPECTRE_FPE_FENV\n    return (fegetexcept() & FE_DIVBYZERO) != 0;\n#else\n    return false;\n#endif\n  };\n\n  enable_floating_point_exceptions();\n  CHECK(exceptions_enabled());\n  {\n    const ScopedFpeState s{};\n    CHECK(exceptions_enabled());\n  }\n  CHECK(exceptions_enabled());\n\n  {\n    const ScopedFpeState s(true);\n    CHECK(exceptions_enabled());\n  }\n  CHECK(exceptions_enabled());\n\n  {\n    const ScopedFpeState s(false);\n    CHECK(not exceptions_enabled());\n  }\n  CHECK(exceptions_enabled());\n\n  CHECK(exceptions_enabled());\n  {\n    const ScopedFpeState s(ScopedFpeState::DoNotSave{});\n    CHECK(exceptions_enabled());\n    disable_floating_point_exceptions();\n  }\n  CHECK(not exceptions_enabled());\n  enable_floating_point_exceptions();\n\n  {\n    const ScopedFpeState s(false);\n    CHECK(not exceptions_enabled());\n    s.set_exceptions(false);\n    CHECK(not exceptions_enabled());\n    s.set_exceptions(true);\n    CHECK(exceptions_enabled());\n  }\n  CHECK(exceptions_enabled());\n\n  {\n    const ScopedFpeState s{};\n    // Other methods of modifying the state should also be scoped\n    disable_floating_point_exceptions();\n  }\n  CHECK(exceptions_enabled());\n\n  {\n    ScopedFpeState s(false);\n    CHECK(not exceptions_enabled());\n    // Restoring the state explicitly should suppress the implicit one\n    // in the destructor.\n    s.restore_exceptions();\n    CHECK(exceptions_enabled());\n    disable_floating_point_exceptions();\n  }\n  CHECK(not exceptions_enabled());\n  enable_floating_point_exceptions();\n\n  {\n    ScopedFpeState s(false);\n    CHECK(not exceptions_enabled());\n    s.restore_exceptions();\n    CHECK(exceptions_enabled());\n    // Saving the state again should return the restoring behavior.\n    s.save_exceptions();\n    disable_floating_point_exceptions();\n  }\n  CHECK(exceptions_enabled());\n\n  {\n    const ScopedFpeState s1(false);\n    {\n      const ScopedFpeState s2(false);\n      {\n        const ScopedFpeState s3(true);\n        CHECK(exceptions_enabled());\n      }\n      CHECK(not exceptions_enabled());\n    }\n    CHECK(not exceptions_enabled());\n  }\n  CHECK(exceptions_enabled());\n\n  // Test that ERROR preserves the state.  This primarily matters for\n  // tests.\n  enable_floating_point_exceptions();\n  CHECK_THROWS_WITH([]() { ERROR(\"BOOM\"); }(),\n                    Catch::Matchers::ContainsSubstring(\"BOOM\"));\n  CHECK(exceptions_enabled());\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/ErrorHandling/Test_SegfaultHandler.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <csignal>\n#include <string>\n\n#ifdef __APPLE__\n#include \"Parallel/Printf/Printf.hpp\"\n#endif\n#include \"Utilities/ErrorHandling/SegfaultHandler.hpp\"\n\n// [[OutputRegex, Segmentation fault!]]\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.SegfaultHandler\",\n                  \"[ErrorHandling][Unit]\") {\n  // Tried to make this not an OUTPUT_TEST, but it fails on all CI compilers\n  // despite passing on my desktop...\n  OUTPUT_TEST();\n  enable_segfault_handler();\n  CHECK_THROWS_WITH(std::raise(SIGSEGV),\n                    Catch::Matchers::ContainsSubstring(\"Segmentation fault!\"));\n\n// For some reason on the newer macOS-13 CI runners, this test never prints out\n// \"Segmentation fault!\". Somehow, the CHECK_THROWS_WITH must catch the signal\n// and exit cleanly if the test is working. Because of this, the test fails\n// because this is an output test. So we manually print out the message so the\n// output test is happy.\n#ifdef __APPLE__\n  Parallel::printf(\"Workaround for Apple: Segmentation fault!\\n\");\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/ErrorHandling/Test_Strerror.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cerrno>\n#include <cstring>\n\n#include \"Utilities/ErrorHandling/Strerror.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.ErrorHandling.Strerror\", \"[Unit][ErrorHandling]\") {\n  // NOLINTNEXTLINE(concurrency-mt-unsafe)\n  CHECK(strerror_threadsafe(EINVAL) == strerror(EINVAL));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Kokkos/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (NOT TARGET Kokkos::kokkos)\n  return()\nendif()\n\n# Add a library with device code to test linking it into an executable\nadd_spectre_library(\n  TestKernel\n  TestKernel.cpp\n)\ntarget_link_libraries(TestKernel PUBLIC Kokkos::kokkos)\n\n# Add a test executable that links against the TestKernel library\nset(LIBRARY \"Test_Kokkos\")\n\nset(LIBRARY_SOURCES\n  Test_Kokkos.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  TestKernel\n  SpectreKokkos\n  )\n"
  },
  {
    "path": "tests/Unit/Utilities/Kokkos/TestKernel.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/Kokkos/TestKernel.hpp\"\n\nKOKKOS_FUNCTION\ndouble kernel_square(double x) { return x * x; }\n"
  },
  {
    "path": "tests/Unit/Utilities/Kokkos/TestKernel.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#pragma once\n\n#include \"Utilities/Kokkos/KokkosCore.hpp\"\n\nKOKKOS_FUNCTION\ndouble kernel_square(double x);\n"
  },
  {
    "path": "tests/Unit/Utilities/Kokkos/Test_Kokkos.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Utilities/Kokkos/KokkosCore.hpp\"\n#include \"Utilities/Kokkos/TestKernel.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Kokkos\",\n                   \"[Utilities][Unit]\") {\n    Kokkos::View<double*> view(\"view\", 10);\n    Kokkos::parallel_for(\n        \"TestKernel\", 10, KOKKOS_LAMBDA(const int i) {\n          // Implementation of `kernel_square` is in a separate library to test\n          // linking it in.\n          view(i) = kernel_square(static_cast<double>(i));\n        });\n    const auto host_view = Kokkos::create_mirror_view(view);\n    Kokkos::deep_copy(host_view, view);\n    for (int i = 0; i < 10; ++i) {\n        CHECK(host_view(i) == static_cast<double>(i * i));\n    }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Protocols/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Protocols/Test_StaticReturnApplyable.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Utilities/Protocols/Test_StaticReturnApplyable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/DataBox/DataBox.hpp\"\n#include \"DataStructures/DataBox/Tag.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/Protocols/StaticReturnApplyable.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nstruct T1 {};\nstruct T2 {};\nstruct T3 {};\nstruct T4 {};\n\nstruct Tag1 : db::SimpleTag {\n  using type = T1;\n};\n\nstruct Tag2 : db::SimpleTag {\n  using type = T2;\n};\n\ntemplate <typename T>\nstruct TemplatedTag : db::SimpleTag {\n  using type = T;\n};\n\n// [StaticReturnApplyable]\nstruct Applyable : tt::ConformsTo<protocols::StaticReturnApplyable> {\n  using return_tags = tmpl::list<Tag1, TemplatedTag<T3>>;\n  using argument_tags = tmpl::list<TemplatedTag<T4>, Tag2>;\n  static void apply(const gsl::not_null<T1*> /*return1*/,\n                    const gsl::not_null<T3*> /*return2*/,\n                    const T4& /*argument1*/, const T2& /*argument2*/) {}\n};\n// [StaticReturnApplyable]\n\nstatic_assert(\n    tt::assert_conforms_to_v<Applyable, protocols::StaticReturnApplyable>);\n\n// new struct where apply is a template function\nstruct ApplyableTemplatedApply\n    : tt::ConformsTo<protocols::StaticReturnApplyable> {\n  using return_tags = tmpl::list<Tag1, TemplatedTag<T3>>;\n  using argument_tags = tmpl::list<TemplatedTag<T4>, Tag2>;\n\n  template <typename T>\n  static void apply(const gsl::not_null<T1*> /*return1*/,\n                    const gsl::not_null<T3*> /*return2*/,\n                    const T4& /*argument1*/, const T& /*argument2*/) {}\n};\n\nstatic_assert(tt::assert_conforms_to_v<ApplyableTemplatedApply,\n                                       protocols::StaticReturnApplyable>);\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Protocols.StaticReturnApplyable\",\n                  \"[Unit][Utilities]\") {\n  // Make sure the protocol is consistent with db::mutate_apply.  Not\n  // all valid arguments to db::mutate_apply satisfy this protocol,\n  // but the reverse should be true.\n  auto box = db::create<\n      db::AddSimpleTags<Tag1, Tag2, TemplatedTag<T3>, TemplatedTag<T4>>>(\n      T1{}, T2{}, T3{}, T4{});\n  db::mutate_apply<Applyable>(make_not_null(&box));\n  db::mutate_apply<ApplyableTemplatedApply>(make_not_null(&box));\n  CHECK(true);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Serialization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY_SOURCES\n  ${LIBRARY_SOURCES}\n  Serialization/Test_PupBoost.cpp\n  Serialization/Test_PupStlCpp11.cpp\n  Serialization/Test_PupStlCpp17.cpp\n  Serialization/Test_RegisterDerivedClassesWithCharm.cpp\n  Serialization/Test_Serialize.cpp\n  PARENT_SCOPE)\n"
  },
  {
    "path": "tests/Unit/Utilities/Serialization/Test_PupBoost.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <boost/container/small_vector.hpp>\n#include <boost/container/static_vector.hpp>\n#include <boost/math/quaternion.hpp>\n#include <boost/rational.hpp>\n#include <cstddef>\n#include <functional>\n#include <type_traits>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupBoost.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nvoid test_rational() {\n  boost::rational<size_t> r1(3_st, 4_st);\n  test_serialization(r1);\n  boost::rational<int> r2(-5, 2);\n  test_serialization(r2);\n}\n\nstruct NotTriviallyCopyable {\n  NotTriviallyCopyable() = default;\n  NotTriviallyCopyable(const NotTriviallyCopyable& other) : data(other.data) {}\n  NotTriviallyCopyable& operator=(const NotTriviallyCopyable& other) {\n    data = other.data;\n    return *this;\n  }\n  explicit NotTriviallyCopyable(const int d) : data(d) {}\n\n  int data;\n\n  void pup(PUP::er& p) { p | data; }\n};\n\nstatic_assert(not std::is_trivially_copyable_v<NotTriviallyCopyable>);\n\nbool operator==(const NotTriviallyCopyable& a, const NotTriviallyCopyable& b) {\n  return a.data == b.data;\n}\n\nvoid test_static_vector() {\n  boost::container::static_vector<int, 5> vector_int{1, 2, 3};\n  test_serialization(vector_int);\n  boost::container::static_vector<NotTriviallyCopyable, 5> vector_ntc{};\n  vector_ntc.emplace_back(1);\n  vector_ntc.emplace_back(2);\n  vector_ntc.emplace_back(3);\n  test_serialization(vector_ntc);\n}\n\n// Boost 1.86 added an undocumented `is_small()` method, but we have\n// to support older versions.\ntemplate <typename T, size_t N>\nbool small_vector_is_small(const boost::container::small_vector<T, N>& v) {\n  // The pointer comparison rules are weird and std::less is not\n  // equivalent to operator<.\n  // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  return std::less_equal<const char*>{}(\n             reinterpret_cast<const char*>(&v),\n             reinterpret_cast<const char*>(v.data())) and\n         std::less<const char*>{}(reinterpret_cast<const char*>(v.data()),\n                                  reinterpret_cast<const char*>(&v + 1));\n  // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast, cppcoreguidelines-pro-bounds-pointer-arithmetic)\n}\n\ntemplate <typename T>\nvoid test_small_vector() {\n  boost::container::small_vector<T, 5> small_vector{};\n  for (int i = 0; i < 3; ++i) {\n    small_vector.emplace_back(i);\n  }\n  boost::container::small_vector<T, 5> large_vector{};\n  for (int i = 0; i < 6; ++i) {\n    large_vector.emplace_back(i);\n  }\n\n  // Test that the function works\n  REQUIRE(small_vector_is_small(small_vector));\n  REQUIRE(not small_vector_is_small(large_vector));\n\n  boost::container::small_vector<T, 5> copied_small_vector{};\n  boost::container::small_vector<T, 5> copied_large_vector{};\n  serialize_and_deserialize(make_not_null(&copied_small_vector), small_vector);\n  serialize_and_deserialize(make_not_null(&copied_large_vector), large_vector);\n\n  CHECK(copied_small_vector == small_vector);\n  CHECK(copied_large_vector == large_vector);\n  CHECK(small_vector_is_small(copied_small_vector));\n  CHECK(not small_vector_is_small(copied_large_vector));\n}\n\nvoid test_quaternion() {\n  boost::math::quaternion<double> quat(2.0, 3.0, 4.0, 5.0);\n  test_serialization(quat);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Serialization.PupBoost\", \"[Unit][Serialization]\") {\n  test_rational();\n  test_static_vector();\n  test_small_vector<int>();\n  test_small_vector<NotTriviallyCopyable>();\n  test_quaternion();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Serialization/Test_PupStlCpp11.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <deque>\n#include <memory>\n#include <pup.h>\n#include <pup_stl.h>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/CharmPupable.hpp\"\n#include \"Utilities/Serialization/PupStlCpp11.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace Test_Classes {\nstruct DerivedInPupStlCpp11;\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wunused-function\"\nstruct Base : public PUP::able {\n  using creatable_classes = tmpl::list<Test_Classes::DerivedInPupStlCpp11>;\n  // clang-tidy: internal charm++ warnings\n  WRAPPED_PUPable_abstract(Base);  // NOLINT\n};\n#pragma GCC diagnostic pop\n\nstruct DerivedInPupStlCpp11 : public Base {\n  explicit DerivedInPupStlCpp11(std::vector<double> vec)\n      : vec_(std::move(vec)) {}\n  // clang-tidy: internal charm++ warnings\n  WRAPPED_PUPable_decl_base_template(Base,  // NOLINT\n                                     DerivedInPupStlCpp11);\n  explicit DerivedInPupStlCpp11(CkMigrateMessage* /* m */) {}\n  void pup(PUP::er& p) override {\n    Base::pup(p);\n    p | vec_;\n  }\n\n  const auto& get() const { return vec_; }\n\n  friend bool operator==(const DerivedInPupStlCpp11& lhs,\n                         const DerivedInPupStlCpp11& rhs) {\n    return lhs.vec_ == rhs.vec_;\n  }\n\n private:\n  std::vector<double> vec_;\n};\n\n}  // namespace Test_Classes\n\nstruct PairComparator {\n  bool operator()(std::pair<int, double> lhs,\n                  std::pair<int, double> rhs) const {\n    return lhs.first < rhs.first or\n           (lhs.first == rhs.first and lhs.second < rhs.second);\n  }\n};\n\nSPECTRE_TEST_CASE(\"Unit.Serialization.PupStlCpp11\", \"[Serialization][Unit]\") {\n  // [example_serialize_comparable]\n  {\n    INFO(\"tuple\");\n    std::unordered_map<std::string, double> um;\n    um[\"aaa\"] = 1.589;\n    um[\"bbb\"] = -10.7392;\n    auto test_tuple = std::make_tuple<int, double, std::string,\n                                      std::unordered_map<std::string, double>>(\n        2, 0.57, \"blah\", std::move(um));\n    test_serialization(test_tuple);\n  }\n  // [example_serialize_comparable]\n  // [example_serialize_derived]\n  {\n    INFO(\"unique_ptr.abstract_base\");\n    test_serialization_via_base<Test_Classes::Base,\n                                Test_Classes::DerivedInPupStlCpp11>(\n        std::vector<double>{-1, 12.3, -7, 8});\n  }\n  // [example_serialize_derived]\n  {\n    // note the `serialize_and_deserialize` utilities don't work here because\n    // atomic specifically has no copy constructor -- it must be serialized\n    // directly into the target object.\n    INFO(\"atomic int\");\n    const std::atomic<int> to_serialize{3};\n    std::atomic<int> serialization_target{234};\n    const auto serialized_data = serialize<std::atomic<int>>(to_serialize);\n    CHECK(to_serialize != serialization_target);\n    PUP::fromMem reader(serialized_data.data());\n    reader | serialization_target;\n    CHECK(serialization_target == to_serialize);\n  }\n  {\n    INFO(\"atomic double\");\n    const std::atomic<double> to_serialize{3.4};\n    std::atomic<double> serialization_target{23.5};\n    const auto serialized_data = serialize<std::atomic<double>>(to_serialize);\n    CHECK(to_serialize != serialization_target);\n    PUP::fromMem reader(serialized_data.data());\n    reader | serialization_target;\n    CHECK(serialization_target == to_serialize);\n  }\n  {\n    INFO(\"map with custom comparator\");\n    std::map<std::pair<int, double>, double, PairComparator> map_to_serialize;\n    map_to_serialize.insert({std::make_pair(1, 2.0), 3.0});\n    map_to_serialize.insert({std::make_pair(3, 1.0), 1.5});\n    map_to_serialize.insert({std::make_pair(2, 6.0), 10.2});\n    std::map<std::pair<int, double>, double, PairComparator>\n        serialization_target;\n    CHECK(map_to_serialize != serialization_target);\n    PUP::sizer sizer;\n    pup_override(sizer, map_to_serialize);\n    std::vector<char> data(sizer.size());\n    PUP::toMem writer(data.data());\n    pup_override(writer, map_to_serialize);\n    PUP::fromMem reader(data.data());\n    pup_override(reader, serialization_target);\n    CHECK(serialization_target == map_to_serialize);\n  }\n}\n\n// clang-tidy: possibly throwing constructor static storage\n// clang-tidy: false positive: redundant declaration\nPUP::able::PUP_ID Test_Classes::DerivedInPupStlCpp11::my_PUP_ID = 0;  // NOLINT\n"
  },
  {
    "path": "tests/Unit/Utilities/Serialization/Test_PupStlCpp17.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <optional>\n#include <variant>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Serialization/PupStlCpp17.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\nstruct LocalNoCopyMove {\n  LocalNoCopyMove() = default;\n  explicit LocalNoCopyMove(const size_t in_t) : t(in_t) {}\n  LocalNoCopyMove(const LocalNoCopyMove&) = delete;\n  LocalNoCopyMove& operator=(const LocalNoCopyMove&) = delete;\n  LocalNoCopyMove(LocalNoCopyMove&&) = delete;\n  LocalNoCopyMove& operator=(LocalNoCopyMove&&) = delete;\n  ~LocalNoCopyMove() = default;\n\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | t; }\n\n  size_t t;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Serialization.PupStlCpp17\", \"[Serialization][Unit]\") {\n  {\n    INFO(\"Optional\");\n    const std::optional<int> t_not_set{};\n    CHECK_FALSE(t_not_set.has_value());\n    const auto t_not_set_deserialized = serialize_and_deserialize(t_not_set);\n    CHECK_FALSE(t_not_set_deserialized.has_value());\n\n    const std::optional<int> t_set{10};\n    REQUIRE(t_set.value() == 10);\n    const auto t_set_deserialized = serialize_and_deserialize(t_set);\n    REQUIRE(t_set_deserialized.value());\n    CHECK(*t_set_deserialized == 10);\n\n    // Test that we can handle a non-copyable and non-movable class. This occurs\n    // when you have a hashtable where the mapped type is a\n    // std::optional<NonCopyable>\n    const std::optional<LocalNoCopyMove> t_nc{10};\n    REQUIRE(t_nc.value().t == 10);\n    std::optional<LocalNoCopyMove> t_nc_deserialized{};\n    serialize_and_deserialize(make_not_null(&t_nc_deserialized), t_nc);\n    REQUIRE(t_nc_deserialized.value().t == 10);\n    CHECK(t_nc_deserialized->t == 10);\n  }\n  {\n    INFO(\"Variant\");\n    std::variant<int, char, size_t> t{-1};\n    CHECK(std::get<int>(serialize_and_deserialize(t)) == -1);\n    t = 'a';\n    CHECK(std::get<char>(serialize_and_deserialize(t)) == 'a');\n    t = 10;\n    CHECK(std::get<int>(serialize_and_deserialize(t)) == 10);\n    t = static_cast<size_t>(100);\n    CHECK(std::get<size_t>(serialize_and_deserialize(t)) == 100);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Serialization/Test_RegisterDerivedClassesWithCharm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"DataStructures/Tensor/IndexType.hpp\"\n#include \"Domain/CoordinateMaps/Affine.hpp\"\n#include \"Domain/CoordinateMaps/CoordinateMap.hpp\"\n#include \"Domain/CoordinateMaps/ProductMaps.hpp\"\n#include \"Utilities/Serialization/RegisterDerivedClassesWithCharm.hpp\"\n\nnamespace {\n\nvoid test_registration_name() {\n  CHECK(registration_name<\n            domain::CoordinateMap<Frame::BlockLogical, Frame::Inertial,\n                                  domain::CoordinateMaps::ProductOf2Maps<\n                                      domain::CoordinateMaps::Affine,\n                                      domain::CoordinateMaps::Affine>>>() ==\n        \"domain::CoordinateMap<Frame::BlockLogical,Frame::Inertial,\"\n        \"domain::CoordinateMaps::ProductOf2Maps<domain::CoordinateMaps::\"\n        \"Affine,domain::CoordinateMaps::Affine>>\");\n}\n\n}\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.RegisterDerivedClassesWithCharm\",\n                  \"[Unit][Parallel]\") {\n  test_registration_name();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Serialization/Test_Serialize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Serialization/Serialize.hpp\"\n\nnamespace {\n// A type without a default constructor, to test the not_null overload.\nstruct NoDefault {\n  explicit NoDefault(int v) : value(v) {}\n  int value;\n  // NOLINTNEXTLINE(google-runtime-references)\n  void pup(PUP::er& p) { p | value; }\n  bool operator==(const NoDefault& rhs) const { return value == rhs.value; }\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Parallel.Serialize\", \"[Unit][Parallel]\") {\n  CHECK(size_of_object_in_bytes(std::array<double, 4>{}) == 4 * sizeof(double));\n  CHECK(\n      size_of_object_in_bytes(std::vector<double>(10)) ==\n      (10 * sizeof(double) + sizeof(decltype(std::vector<double>(10).size()))));\n\n  // Test value-returning overload\n  {\n    const int original = 42;\n    const int result = serialize_and_deserialize(original);\n    CHECK(result == original);\n  }\n  {\n    const std::vector<double> original{1.0, 2.0, 3.0};\n    const auto result = serialize_and_deserialize(original);\n    CHECK(result == original);\n  }\n\n  // Test not_null overload\n  {\n    const int original = 7;\n    int result = 0;\n    serialize_and_deserialize(make_not_null(&result), original);\n    CHECK(result == original);\n  }\n  {\n    const std::vector<double> original{4.0, 5.0, 6.0};\n    std::vector<double> result{};\n    serialize_and_deserialize(make_not_null(&result), original);\n    CHECK(result == original);\n  }\n  // Test not_null overload with a non-default-constructible type\n  {\n    const NoDefault original{42};\n    NoDefault result{0};\n    serialize_and_deserialize(make_not_null(&result), original);\n    CHECK(result == original);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/StdHelpers/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_StdHelpers\")\n\nset(LIBRARY_SOURCES\n  Test_RetrieveUniquePtr.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  StdHelpers\n)\n"
  },
  {
    "path": "tests/Unit/Utilities/StdHelpers/Test_RetrieveUniquePtr.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for detai\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"Utilities/StdHelpers/RetrieveUniquePtr.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.StdHelpers.RetrieveUniquePtr\", \"[Utilities][Unit]\") {\n  const int value = 7;\n  CHECK(StdHelpers::retrieve(value) == 7);\n  CHECK(&StdHelpers::retrieve(value) == &value);\n  const auto pointer = std::make_unique<int>(10);\n  CHECK(StdHelpers::retrieve(pointer) == 10);\n  CHECK(&StdHelpers::retrieve(pointer) == pointer.get());\n\n  int mutable_value = 1;\n  CHECK(StdHelpers::retrieve(mutable_value) == 1);\n  CHECK(&StdHelpers::retrieve(mutable_value) == &mutable_value);\n  auto mutable_pointer = std::make_unique<int>(10);\n  CHECK(StdHelpers::retrieve(mutable_pointer) == 10);\n  CHECK(&StdHelpers::retrieve(mutable_pointer) == mutable_pointer.get());\n  static_assert(\n      std::is_same_v<decltype(StdHelpers::retrieve(mutable_pointer)), int&>);\n  static_assert(\n      std::is_same_v<decltype(StdHelpers::retrieve(mutable_value)), int&>);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/System/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_SystemUtilities\")\n\nset(LIBRARY_SOURCES\n  Test_Prefetch.cpp\n)\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\ntarget_link_libraries(\n  ${LIBRARY}\n  PRIVATE\n  SystemUtilities\n)\n"
  },
  {
    "path": "tests/Unit/Utilities/System/Test_Prefetch.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/System/Prefetch.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.System.Prefetch\", \"[Unit][Utilities]\") {\n  // The goal of this test is just to make sure the code compiles and doesn't\n  // segfault.\n  std::vector<double> vector(20);\n  sys::prefetch<sys::PrefetchTo::L1Cache>(vector.data());\n  sys::prefetch<sys::PrefetchTo::L2Cache>(vector.data());\n  sys::prefetch<sys::PrefetchTo::L3Cache>(vector.data());\n\n  sys::prefetch<sys::PrefetchTo::NonTemporal>(vector.data());\n\n  sys::prefetch<sys::PrefetchTo::WriteL1Cache>(vector.data());\n  sys::prefetch<sys::PrefetchTo::WriteL2Cache>(vector.data());\n\n  CHECK(get_output(sys::PrefetchTo::L1Cache) == \"L1Cache\");\n  CHECK(get_output(sys::PrefetchTo::L2Cache) == \"L2Cache\");\n  CHECK(get_output(sys::PrefetchTo::L3Cache) == \"L3Cache\");\n  CHECK(get_output(sys::PrefetchTo::NonTemporal) == \"NonTemporal\");\n  CHECK(get_output(sys::PrefetchTo::WriteL1Cache) == \"WriteL1Cache\");\n  CHECK(get_output(sys::PrefetchTo::WriteL2Cache) == \"WriteL2Cache\");\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Algorithm.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cctype>\n#include <cstddef>\n#include <iterator>\n#include <random>\n#include <string>\n#include <vector>\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace {\nconstexpr bool check_swap() {\n  int x = 4;\n  int y = 444;\n  cpp20::swap(x, y);\n  return x == 444 and y == 4;\n}\n\nconstexpr bool check_iter_swap() {\n  const size_t size = 6;\n  cpp20::array<size_t, size> a{};\n  cpp2b::iota(a.begin(), a.end(), size_t(1));\n  cpp20::iter_swap(a.begin() + 1, a.begin() + 4);\n\n  cpp20::array<size_t, size> expected{{1, 5, 3, 4, 2, 6}};\n  return a == expected;\n}\n\nconstexpr bool check_reverse() {\n  const size_t size = 6;\n  cpp20::array<size_t, size> a{};\n  cpp2b::iota(a.begin(), a.end(), size_t(1));\n  cpp20::detail::reverse(a.begin(), a.end(), std::bidirectional_iterator_tag());\n\n  cpp20::array<size_t, size> expected{{6, 5, 4, 3, 2, 1}};\n  return a == expected;\n}\n\nconstexpr bool check_reverse_random_access() {\n  const size_t size = 6;\n  cpp20::array<size_t, size> a{};\n  cpp2b::iota(a.begin(), a.end(), size_t(1));\n  // a is a random_access_iterator, so the next line tests both the generic\n  // cpp20::reverse() and the one for random access iterators\n  cpp20::reverse(a.begin(), a.end());\n\n  cpp20::array<size_t, size> expected{{6, 5, 4, 3, 2, 1}};\n  return a == expected;\n}\n\nconstexpr bool check_next_permutation() {\n  const size_t size = 6;\n  cpp20::array<size_t, size> a{{1, 2, 4, 6, 3, 5}};\n  cpp20::next_permutation(a.begin(), a.end());\n\n  cpp20::array<size_t, size> expected{{1, 2, 4, 6, 5, 3}};\n  return a == expected;\n}\n\nvoid test_constexpr_swap_iter_swap_reverse_etc() {\n  // Check the functions at runtime\n  CHECK(check_swap());\n  CHECK(check_iter_swap());\n  CHECK(check_reverse());\n  CHECK(check_reverse_random_access());\n  CHECK(check_next_permutation());\n\n  // Check the functions at compile time\n  static_assert(check_swap(), \"Failed test Unit.Utilities.Algorithm\");\n  static_assert(check_iter_swap(), \"Failed test Unit.Utilities.Algorithm\");\n  static_assert(check_reverse(), \"Failed test Unit.Utilities.Algorithm\");\n  static_assert(check_reverse_random_access(),\n                \"Failed test Unit.Utilities.Algorithm\");\n  static_assert(check_next_permutation(),\n                \"Failed test Unit.Utilities.Algorithm\");\n}\n\nvoid test_all_of_and_any_of_and_none_of() {\n  // Test wrappers around STL algorithms\n  CHECK(alg::all_of(std::vector<int>{1, 1, 1, 1},\n                    [](const int t) { return t == 1; }));\n  CHECK_FALSE(alg::all_of(std::vector<int>{1, 2, 1, 1},\n                          [](const int t) { return t == 1; }));\n  CHECK_FALSE(alg::all_of(std::vector<int>{2, 1, 1, 1},\n                          [](const int t) { return t == 1; }));\n  CHECK_FALSE(alg::all_of(std::vector<int>{1, 1, 1, 2},\n                          [](const int t) { return t == 1; }));\n  CHECK(alg::any_of(std::vector<int>{4, 2, 1, 3},\n                    [](const int t) { return t == 1; }));\n  CHECK_FALSE(alg::any_of(std::vector<int>{4, 2, -1, 3},\n                          [](const int t) { return t == 1; }));\n  CHECK(alg::none_of(std::vector<int>{4, 2, -1, 3},\n                     [](const int t) { return t == 1; }));\n  CHECK_FALSE(alg::none_of(std::vector<int>{4, 2, 1, 3},\n                           [](const int t) { return t == 1; }));\n  CHECK_FALSE(alg::none_of(std::vector<int>{1, 2, -1, 3},\n                           [](const int t) { return t == 1; }));\n  CHECK_FALSE(alg::none_of(std::vector<int>{4, 2, -1, 1},\n                           [](const int t) { return t == 1; }));\n}\n\nvoid test_count_related() {\n  std::vector<int> a{1, 2, 3, 4, 3, 5};\n  CHECK(alg::count(a, 0) == 0);\n  CHECK(alg::count(a, -1) == 0);\n  CHECK(alg::count(a, -2) == 0);\n  for (int i = 1; i < 5; ++i) {\n    CHECK(alg::count(a, i) == (i == 3 ? 2 : 1));\n  }\n\n  CHECK(alg::count_if(a, [](const int t) { return t == 0; }) == 0);\n  CHECK(alg::count_if(a, [](const int t) { return t == -1; }) == 0);\n  CHECK(alg::count_if(a, [](const int t) { return t == -2; }) == 0);\n  CHECK(alg::count_if(a, [](const int t) { return t % 2 == 0; }) == 2);\n  CHECK(alg::count_if(a, [](const int t) { return t % 3 == 0; }) == 2);\n  CHECK(alg::count_if(a, [](const int t) { return t % 5 == 0; }) == 1);\n  CHECK(alg::count_if(a, [](const int t) { return t % 7 == 0; }) == 0);\n}\n\nvoid test_equal() {\n  // Test alg::equal\n  CHECK(alg::equal(std::vector<int>{1, -7, 8, 9},\n                   std::array<int, 4>{{1, -7, 8, 9}}));\n  CHECK_FALSE(alg::equal(std::vector<int>{1, 7, 8, 9},\n                         std::array<int, 4>{{1, -7, 8, 9}}));\n\n  CHECK(alg::equal(std::vector<int>{1, -7, 8, 9},\n                   std::array<int, 4>{{-1, 7, -8, -9}},\n                   [](const int lhs, const int rhs) { return lhs == -rhs; }));\n  CHECK_FALSE(alg::equal(\n      std::vector<int>{1, -7, 8, 9}, std::array<int, 4>{{-1, -7, -8, -9}},\n      [](const int lhs, const int rhs) { return lhs == -rhs; }));\n}\n\nvoid test_min_max_element() {\n  std::vector<int> t{3, 5, 8, -1};\n  CHECK(*alg::min_element(t) == -1);\n  CHECK(*alg::max_element(t) == 8);\n\n  {\n    const auto [min, max] = alg::minmax_element(t);\n    CHECK(*min == -1);\n    CHECK(*max == 8);\n  }\n\n  CHECK(*alg::min_element(t, [](const int a, const int b) { return a > b; }) ==\n        8);\n  CHECK(*alg::max_element(t, [](const int a, const int b) { return a > b; }) ==\n        -1);\n\n  {\n    const auto [min, max] =\n        alg::minmax_element(t, [](const int a, const int b) { return a > b; });\n    CHECK(*min == 8);\n    CHECK(*max == -1);\n  }\n}\n\nvoid test_find_related() {\n  const std::vector<int> a{1, 2, 3, 4};\n  CHECK(alg::find(a, 3) == a.begin() + 2);\n  CHECK(alg::find(a, 5) == a.end());\n  CHECK(alg::found(a, 3));\n  CHECK_FALSE(alg::found(a, 5));\n  std::vector<int> a_copy = a;\n  CHECK(alg::find(a_copy, 3) == a_copy.begin() + 2);\n  *alg::find(a_copy, 3) = 4;\n  CHECK(alg::find(a_copy, 3) == a_copy.end());\n\n  const auto greater_than_three = [](const int t) { return t > 3; };\n  CHECK(alg::find_if(a, greater_than_three) == a.end() - 1);\n  CHECK(alg::find_if(a, [](const int t) { return t < 0; }) == a.end());\n  CHECK(alg::found_if(a, greater_than_three));\n  CHECK_FALSE(alg::found_if(a, [](const int t) { return t < 0; }));\n  a_copy = a;\n  CHECK(alg::find_if(a_copy, greater_than_three) == a_copy.begin() + 3);\n  *alg::find_if(a_copy, greater_than_three) = 1;\n  CHECK(alg::find_if(a_copy, greater_than_three) == a_copy.end());\n\n  const auto less_than_three = [](const int t) { return t < 3; };\n  CHECK(alg::find_if_not(a, less_than_three) == a.end() - 2);\n  CHECK(alg::find_if_not(a, [](const int t) { return t < 8; }) == a.end());\n  CHECK(alg::found_if_not(a, less_than_three));\n  CHECK_FALSE(alg::found_if_not(a, [](const int t) { return t < 8; }));\n  a_copy = a;\n  CHECK(alg::find_if_not(a_copy, less_than_three) == a_copy.begin() + 2);\n  *alg::find_if_not(a_copy, less_than_three) = 2;\n  CHECK(alg::find_if_not(a_copy, less_than_three) == a_copy.begin() + 3);\n}\n\nvoid test_for_each() {\n  int count = 0;\n  const std::vector<size_t> a{1, 7, 234, 987, 32};\n  alg::for_each(a, [&count](const size_t value) {\n    if (value > 100) {\n      count++;\n    }\n  });\n  CHECK(count == 2);\n\n  std::vector<size_t> b{1, 7, 234, 987, 32};\n  alg::for_each(b, [](size_t& value) {\n    if (value > 100) {\n      value *= 2;\n    }\n  });\n  CHECK(b == std::vector<size_t>{1, 7, 2 * 234, 2 * 987, 32});\n}\n\nvoid test_remove() {\n  std::string str1 = \"Test with some   spaces\";\n  str1.erase(alg::remove(str1, ' '), str1.end());\n  CHECK(str1 == \"Testwithsomespaces\");\n\n  std::string str2 = \"Text\\n with\\tsome \\t  whitespaces\\n\\n\";\n  str2.erase(\n      alg::remove_if(str2, [](unsigned char x) { return std::isspace(x); }),\n      str2.end());\n  CHECK(str2 == \"Textwithsomewhitespaces\");\n}\n\nvoid test_sample() {\n  std::vector<int> v{-1, 3, 5, 9, -24, 8};\n  std::mt19937 gen;\n  for (size_t s = 0; s < 8; ++s) {\n    std::vector<int> alg_s{};\n    gen.seed(1);\n    alg::sample(v, std::back_inserter(alg_s), s, gen);\n    std::vector<int> std_s{};\n    gen.seed(1);\n    std::sample(std::begin(v), std::end(v), std::back_inserter(std_s), s, gen);\n    CHECK(alg_s == std_s);\n  }\n}\n\nvoid test_sort() {\n  std::vector<int> t_orig{3, 5, 8, -1};\n  std::vector<int> t0 = t_orig;\n  std::vector<int> t0_copy = t0;\n  alg::sort(t0);\n  std::sort(t0_copy.begin(), t0_copy.end());\n  CHECK(t0 == t0_copy);\n\n  auto t1 = t_orig;\n  auto t1_copy = t1;\n  alg::sort(t1, std::greater<>{});\n  std::sort(t1_copy.begin(), t1_copy.end(), std::greater<>{});\n  CHECK(t1 == t1_copy);\n}\n\nvoid test_transform() {\n  std::vector<int> t0{3, 5, 8, -1};\n  std::vector<int> t1{8, 2, 7, -5};\n  std::vector<int> result(t0.size());\n  alg::transform(t0, result.begin(), [](const size_t t) { return t + 5; });\n  CHECK(result == std::vector<int>{8, 10, 13, 4});\n\n  alg::transform(t0, t1, result.begin(),\n                 [](const size_t a, const size_t b) { return a + b; });\n  CHECK(result == std::vector<int>{11, 7, 15, -6});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Algorithm\", \"[Unit][Utilities]\") {\n  test_constexpr_swap_iter_swap_reverse_etc();\n  test_all_of_and_any_of_and_none_of();\n  test_count_related();\n  test_equal();\n  test_find_related();\n  test_for_each();\n  test_min_max_element();\n  test_remove();\n  test_sample();\n  test_sort();\n  test_transform();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Array.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <initializer_list>\n#include <string>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nstatic_assert(convert_to_cpp20_array(make_array<4>(7.8)) ==\n                  cpp20::array<double, 4>{{7.8, 7.8, 7.8, 7.8}},\n              \"Failed testing cpp20::array conversion from std::array\");\nstatic_assert(convert_to_cpp20_array(make_array(3.2, 4.3, 5.4, 6.5, 7.8)) ==\n                  cpp20::array<double, 5>{{3.2, 4.3, 5.4, 6.5, 7.8}},\n              \"Failed testing cpp20::array conversion from std::array\");\n\nnamespace {\nconstexpr auto array_n = convert_to_cpp20_array(make_array<6>(5));\nstatic_assert(\n    std::is_same<typename std::decay<decltype(array_n)>::type::value_type,\n                 int>::value,\n    \"Failed testing cpp20::array type from make_array\");\nstatic_assert(array_n.size() == 6,\n              \"Failed testing cpp20::array size from make_array\");\nconstexpr bool test_constexpr_iter() {\n  bool result = true;\n  for (const auto& p : array_n) {\n    result = result && 5 == p;\n  }\n  return result;\n}\nstatic_assert(test_constexpr_iter(), \"Failed testing cpp20::array iterator\");\n\nconstexpr auto array_empty = convert_to_cpp20_array(make_array<0>(2));\nstatic_assert(array_empty.empty(),\n              \"Failed testing cpp20::array size of empty array\");\n\nconstexpr auto array_non_copyable_empty =\n    convert_to_cpp20_array(make_array<0>(NonCopyable{}));\nstatic_assert(array_non_copyable_empty.empty(),\n              \"Failed testing cpp20::array size for empty array of move-only\");\n\nconstexpr auto my_array = convert_to_cpp20_array(make_array(1, 3, 4, 8, 9));\nstatic_assert(my_array.size() == 5, \"Failed testing cpp20::array size\");\nstatic_assert(my_array.max_size() == 5, \"Failed testing cpp20::array size\");\nstatic_assert(my_array[0] == 1 and my_array[1] == 3 and my_array[2] == 4 and\n                  my_array[3] == 8 and my_array[4] == 9,\n              \"Failed testing cpp20::array values\");\n\nconstexpr auto array_from_sequence = convert_to_cpp20_array(\n    make_array<int, 3>(std::initializer_list<int>{2, 8, 6}));\nstatic_assert(cpp20::array<int, 3>{{2, 8, 6}} == array_from_sequence,\n              \"Failed testing cpp20::array from sequence\");\nconstexpr const auto array_from_truncated_sequence = convert_to_cpp20_array(\n    make_array<int, 3>(std::initializer_list<int>{2, 8, 6, 9, 7}));\nstatic_assert(cpp20::array<int, 3>{{2, 8, 6}} == array_from_truncated_sequence,\n              \"Failed testing cpp20::array from sequence\");\n\nconstexpr cpp20::array<int, 3> create_an_array() {\n  cpp20::array<int, 3> a{};\n  a[0] = 8;\n  a[1] = -9;\n  a[2] = 10;\n  return a;\n}\nstatic_assert(create_an_array() == cpp20::array<int, 3>{{8, -9, 10}},\n              \"Failed testing cpp20::array\");\nstatic_assert(create_an_array().back() == 10, \"Failed testing cpp20::array\");\nstatic_assert(create_an_array().front() == 8, \"Failed testing cpp20::array\");\n\nconstexpr cpp20::array<int, 3> create_an_array_with_at() {\n  cpp20::array<int, 3> a{};\n  a.at(0) = 10;\n  a.at(1) = -9;\n  a.at(2) = -50;\n  a.front() = 8;\n  a.back() = 10;\n  return a;\n}\nstatic_assert(create_an_array_with_at() == cpp20::array<int, 3>{{8, -9, 10}},\n              \"Failed testing cpp20::array\");\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Cpp20Array\", \"[Unit][Utilities]\") {\n  cpp20::array<int, 0> a0{};\n  CHECK(get_output(a0) == \"()\");\n  cpp20::array<int, 1> a1{{1}};\n  CHECK(get_output(a1) == \"(1)\");\n  cpp20::array<int, 5> a5{{1, 2, 3, 4, 5}};\n  CHECK(get_output(a5) == \"(1,2,3,4,5)\");\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Base64.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <string>\n#include <vector>\n\n#include \"Utilities/Base64.hpp\"\n\nnamespace {\nvoid check_encoding(const std::string& data, const std::string& encoded) {\n  std::vector<std::byte> binary_data(data.size());\n  std::transform(data.begin(), data.end(), binary_data.begin(),\n                 [](const char c) { return static_cast<std::byte>(c); });\n  CHECK(base64_encode(binary_data) == encoded);\n  CHECK(base64_decode(encoded) == binary_data);\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(\n      base64_decode(\"x\"),\n      Catch::Matchers::ContainsSubstring(\n          \"base64 encoded data must have a multiple of 4 characters, not 1\"));\n\n  CHECK_THROWS_WITH(base64_decode(\"xx*x\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Invalid character in base64 encoded string: '*'\"));\n\n  CHECK_THROWS_WITH(base64_decode(\"====\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Misplaced padding character at position 0 of base64 \"\n                        \"encoded string.\"));\n  CHECK_THROWS_WITH(base64_decode(\"x===\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Misplaced padding character at position 1 of base64 \"\n                        \"encoded string.\"));\n  CHECK_THROWS_WITH(base64_decode(\"x=xx\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Misplaced padding character at position 1 of base64 \"\n                        \"encoded string.\"));\n  CHECK_THROWS_WITH(base64_decode(\"xx=x\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Misplaced padding character at position 2 of base64 \"\n                        \"encoded string.\"));\n  CHECK_THROWS_WITH(base64_decode(\"xxx=xxxx\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Misplaced padding character at position 3 of base64 \"\n                        \"encoded string.\"));\n  CHECK_THROWS_WITH(base64_decode(\"xxx=====\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Misplaced padding character at position 3 of base64 \"\n                        \"encoded string.\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Base64\", \"[Unit][Utilities]\") {\n  check_encoding(\"\", \"\");\n  check_encoding(\"f\", \"Zg==\");\n  check_encoding(\"fo\", \"Zm8=\");\n  check_encoding(\"foo\", \"Zm9v\");\n  check_encoding(\"foob\", \"Zm9vYg==\");\n  check_encoding(\"fooba\", \"Zm9vYmE=\");\n  check_encoding(\"foobar\", \"Zm9vYmFy\");\n\n  check_encoding({0}, \"AA==\");\n\n  test_errors();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Blas.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Utilities/Test_Blas.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Blas\", \"[Unit][Utilities]\") {\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      test_blas_asserts_for_bad_char::dgemm_error_transa_false(),\n      Catch::Matchers::ContainsSubstring(\n          \"TRANSA must be upper or lower case N, T, or C. See the \"\n          \"BLAS documentation for help.\"));\n  CHECK_THROWS_WITH(\n      test_blas_asserts_for_bad_char::dgemm_error_transb_false(),\n      Catch::Matchers::ContainsSubstring(\n          \"TRANSB must be upper or lower case N, T, or C. See the \"\n          \"BLAS documentation for help.\"));\n  CHECK_THROWS_WITH(\n      test_blas_asserts_for_bad_char::dgemm_error_transa_true(),\n      Catch::Matchers::ContainsSubstring(\n          \"TRANSA must be upper or lower case N, T, or C. See the \"\n          \"BLAS documentation for help.\"));\n  CHECK_THROWS_WITH(\n      test_blas_asserts_for_bad_char::dgemm_error_transb_true(),\n      Catch::Matchers::ContainsSubstring(\n          \"TRANSB must be upper or lower case N, T, or C. See the \"\n          \"BLAS documentation for help.\"));\n  CHECK_THROWS_WITH(test_blas_asserts_for_bad_char::dgemv_error_trans(),\n                    Catch::Matchers::ContainsSubstring(\n                        \"TRANS must be upper or lower case N, T, or C. See the \"\n                        \"BLAS documentation for help.\"));\n#endif\n  // Verify that zero-sized matrix multiplications are treated as no-ops.\n  const size_t zero = 0;\n  const size_t one = 1;\n  const double alpha = 1.23;\n  const double beta = 4.56;\n  const std::array<double, 1> a{{7.89}};\n  const std::array<double, 1> b{{0.12}};\n  std::array<double, 1> c{{3.45}};\n  const double original_c = c[0];\n  dgemm_('N', 'N', zero, zero, zero, alpha, a.data(), zero, b.data(), one, beta,\n         c.data(), one);\n  CHECK(c[0] == original_c);\n\n  dgemv_('N', zero, zero, alpha, a.data(), zero, b.data(), one, beta, c.data(),\n         one);\n  CHECK(c[0] == original_c);\n\n  const std::complex<double> alpha_complex = std::complex<double>{1.23, 3.21};\n  const std::complex<double> beta_complex = std::complex<double>{4.56, 6.54};\n  const std::array<std::complex<double>, 1> a_complex{\n      {std::complex<double>{7.89, 9.87}}};\n  const std::array<std::complex<double>, 1> b_complex{\n      {std::complex<double>{0.12, 2.10}}};\n  std::array<std::complex<double>, 1> c_complex{\n      {std::complex<double>{3.45, 5.43}}};\n  const std::complex<double> original_c_complex = c_complex[0];\n  zgemm_('N', 'N', zero, zero, zero, alpha_complex, a_complex.data(), zero,\n         b_complex.data(), one, beta_complex, c_complex.data(), one);\n  CHECK(c_complex[0] == original_c_complex);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Blas.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n/// \\file\n/// Defines functions that have warnings that aren't always ignored even with\n/// a warning pop. This header must only be included in the Test_Blas.cpp\n// file.\n\n#pragma once\n\n#ifdef __GNUC__\n#pragma GCC system_header\n#endif\n\n#include <cstddef>\n\n#include \"Utilities/Blas.hpp\"\n\nnamespace test_blas_asserts_for_bad_char {\ninline void dgemm_error_transa_false() {\n  size_t size = 0;\n  double value = 10;\n  dgemm_('this_bad', 'N', size, size, size, value, &value, size, &value, size,\n         value, &value, size);\n}\n\ninline void dgemm_error_transb_false() {\n  size_t size = 0;\n  double value = 10;\n  dgemm_('n', 'this_bad', size, size, size, value, &value, size, &value, size,\n         value, &value, size);\n}\n\ninline void dgemm_error_transa_true() {\n  size_t size = 0;\n  double value = 10;\n  dgemm_<true>('this_bad', 'N', size, size, size, value, &value, size, &value,\n               size, value, &value, size);\n}\n\ninline void dgemm_error_transb_true() {\n  size_t size = 0;\n  double value = 10;\n  dgemm_<true>('n', 'this_bad', size, size, size, value, &value, size, &value,\n               size, value, &value, size);\n}\n\ninline void dgemv_error_trans() {\n  size_t size = 0;\n  double value = 10;\n  dgemv_('this_bad', size, size, value, &value, size, &value, size, value,\n         &value, size);\n}\n}  // namespace test_blas_asserts_for_bad_char\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_CachedFunction.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <map>\n#include <string>\n\n#include \"Utilities/CachedFunction.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.CachedFunction\", \"[Unit][Utilities]\") {\n  size_t call_count = 0;\n  // Use types that cannot be implicitly converted\n  const auto func = [&call_count](const std::string& s) {\n    ++call_count;\n    return s.size();\n  };\n\n  bool comparator_used = false;\n  // Unused capture to silence clang-tidy warning in the example below.\n  auto comparator = [&comparator_used, not_trivially_copyable = std::string{}](\n                        const std::string& a, const std::string& b) {\n    comparator_used = true;\n    return a < b;\n  };\n\n  // [make_cached_function_example]\n  auto cached = make_cached_function<const std::string&>(func);\n\n  // Specifying all the arguments\n  auto cached2 =\n      make_cached_function<const std::string&, std::map, decltype(comparator)>(\n          func, std::move(comparator));\n  // [make_cached_function_example]\n\n  static_assert(std::is_same_v<decltype(cached)::input, std::string>,\n                \"Wrong input type\");\n  static_assert(std::is_same_v<decltype(cached)::output, size_t>,\n                \"Wrong output type\");\n\n  static_assert(std::is_same_v<decltype(cached2)::input, std::string>,\n                \"Wrong input type\");\n  static_assert(std::is_same_v<decltype(cached2)::output, size_t>,\n                \"Wrong output type\");\n\n  CHECK(call_count == 0);\n  CHECK(cached(\"x\") == 1);\n  CHECK(call_count == 1);\n  CHECK(cached(\"xx\") == 2);\n  CHECK(call_count == 2);\n  CHECK(cached(\"x\") == 1);\n  CHECK(call_count == 2);\n  cached.clear();\n  CHECK(cached(\"x\") == 1);\n  CHECK(call_count == 3);\n\n  CHECK(not comparator_used);\n  CHECK(cached2(\"x\") == 1);\n  CHECK(cached2(\"xx\") == 2);\n  CHECK(comparator_used);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_CallWithDynamicType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <utility>\n\n#include \"Utilities/CallWithDynamicType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\nvoid test_static_cast() {\n  struct B {\n    B() = default;\n    B(const B&) = delete;\n    B(B&&) = delete;\n    B& operator=(const B&) = delete;\n    B& operator=(B&&) = delete;\n    virtual ~B() = default;\n  };\n  struct D1 : B {\n    int mutable_func() { return 1; }\n    int const_func() const { return 11; }\n  };\n  struct D2 : B {\n    int mutable_func() { return 2; }\n    int const_func() const { return 12; }\n  };\n  std::unique_ptr<B> d1 = std::make_unique<D1>();\n  std::unique_ptr<B> d2 = std::make_unique<D2>();\n\n  CHECK(1 == (call_with_dynamic_type<int, tmpl::list<D1, D2>>(\n                 d1.get(),\n                 [](auto* const p, const int extra_arg) {\n                   CHECK(extra_arg == 5);\n                   return p->mutable_func();\n                 },\n                 5)));\n  CHECK(2 == (call_with_dynamic_type<int, tmpl::list<D1, D2>>(\n                 d2.get(),\n                 [](auto* const p, const int extra_arg) {\n                   CHECK(extra_arg == 6);\n                   return p->mutable_func();\n                 },\n                 6)));\n\n  CHECK(11 == (call_with_dynamic_type<int, tmpl::list<D1, D2>>(\n                  &std::as_const(*d1),\n                  [](const auto* const p, const int extra_arg) {\n                    CHECK(extra_arg == 7);\n                    return p->const_func();\n                  },\n                  7)));\n  CHECK(12 == (call_with_dynamic_type<int, tmpl::list<D1, D2>>(\n                  &std::as_const(*d2),\n                  [](const auto* const p, const int extra_arg) {\n                    CHECK(extra_arg == 8);\n                    return p->const_func();\n                  },\n                  8)));\n\n  // Test void return type\n  int result = 0;\n  call_with_dynamic_type<void, tmpl::list<D1, D2>>(\n      d2.get(), [&result](auto* const p) { result = p->mutable_func(); });\n  CHECK(2 == result);\n}\n\nvoid test_dynamic_cast() {\n  struct B {\n    B() = default;\n    B(const B&) = delete;\n    B(B&&) = delete;\n    B& operator=(const B&) = delete;\n    B& operator=(B&&) = delete;\n    virtual ~B() = default;\n  };\n  struct D1 : virtual B {\n    int mutable_func() { return 1; }\n    int const_func() const { return 11; }\n  };\n  struct D2 : virtual B {\n    int mutable_func() { return 2; }\n    int const_func() const { return 12; }\n  };\n  struct C : D1, D2 {\n    int mutable_func() { return 3; }\n    int const_func() const { return 13; }\n  };\n  std::unique_ptr<B> d1 = std::make_unique<D1>();\n  std::unique_ptr<B> d2 = std::make_unique<D2>();\n  std::unique_ptr<B> c = std::make_unique<C>();\n\n  CHECK(1 == (call_with_dynamic_type<int, tmpl::list<D1, D2, C>>(\n                 d1.get(),\n                 [](auto* const p, const int extra_arg) {\n                   CHECK(extra_arg == 5);\n                   return p->mutable_func();\n                 },\n                 5)));\n  CHECK(2 == (call_with_dynamic_type<int, tmpl::list<D1, D2, C>>(\n                 d2.get(),\n                 [](auto* const p, const int extra_arg) {\n                   CHECK(extra_arg == 6);\n                   return p->mutable_func();\n                 },\n                 6)));\n  CHECK(3 == (call_with_dynamic_type<int, tmpl::list<D1, D2, C>>(\n                 c.get(),\n                 [](auto* const p, const int extra_arg) {\n                   CHECK(extra_arg == 7);\n                   return p->mutable_func();\n                 },\n                 7)));\n\n  CHECK(11 == (call_with_dynamic_type<int, tmpl::list<D1, D2, C>>(\n                  &std::as_const(*d1),\n                  [](const auto* const p, const int extra_arg) {\n                    CHECK(extra_arg == 8);\n                    return p->const_func();\n                  },\n                  8)));\n  CHECK(12 == (call_with_dynamic_type<int, tmpl::list<D1, D2, C>>(\n                  &std::as_const(*d2),\n                  [](const auto* const p, const int extra_arg) {\n                    CHECK(extra_arg == 9);\n                    return p->const_func();\n                  },\n                  9)));\n  CHECK(13 == (call_with_dynamic_type<int, tmpl::list<D1, D2, C>>(\n                  &std::as_const(*c),\n                  [](const auto* const p, const int extra_arg) {\n                    CHECK(extra_arg == 10);\n                    return p->const_func();\n                  },\n                  10)));\n\n  // Test void return type\n  int result = 0;\n  call_with_dynamic_type<void, tmpl::list<D1, D2, C>>(\n      d2.get(), [&result](auto* const p) { result = p->mutable_func(); });\n  CHECK(2 == result);\n  call_with_dynamic_type<void, tmpl::list<D1, D2, C>>(\n      c.get(), [&result](auto* const p) { result = p->mutable_func(); });\n  CHECK(3 == result);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.call_with_dynamic_type\",\n                  \"[Unit][Utilities]\") {\n  test_static_cast();\n  test_dynamic_cast();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_CartesianProduct.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <tuple>\n\n#include \"Utilities/CartesianProduct.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\nvoid test_product_of_arrays() {\n  const auto result =\n      cartesian_product(make_array(true, false), make_array(1, 2, 3));\n  const std::array<std::tuple<bool, int>, 6> expected{\n      {{true, 1}, {true, 2}, {true, 3}, {false, 1}, {false, 2}, {false, 3}}};\n  CHECK(result == expected);\n}\n\nvoid test_product_of_containers() {\n  std::array bools{true, false};\n  std::vector ints{1, 2, 3};\n  std::set doubles{-1., 0.5, 3.0, 7.25};\n  const auto result = cartesian_product(bools, ints, doubles);\n  const std::vector<std::tuple<bool, int, double>> expected{\n      {true, 1, -1.},  {true, 1, 0.5},  {true, 1, 3.0},  {true, 1, 7.25},\n      {true, 2, -1.},  {true, 2, 0.5},  {true, 2, 3.0},  {true, 2, 7.25},\n      {true, 3, -1.},  {true, 3, 0.5},  {true, 3, 3.0},  {true, 3, 7.25},\n      {false, 1, -1.}, {false, 1, 0.5}, {false, 1, 3.0}, {false, 1, 7.25},\n      {false, 2, -1.}, {false, 2, 0.5}, {false, 2, 3.0}, {false, 2, 7.25},\n      {false, 3, -1.}, {false, 3, 0.5}, {false, 3, 3.0}, {false, 3, 7.25}};\n  CHECK(result == expected);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.CartesianProduct\", \"[Unit][Utilities]\") {\n  test_product_of_arrays();\n  test_product_of_containers();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_CleanupRoutine.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <exception>\n\n#include \"Utilities/CleanupRoutine.hpp\"\n\nnamespace {\nvoid test(const bool do_throw) {\n  // [cleanup_routine]\n  int x = 1;\n  try {\n    const CleanupRoutine cleanup = [&x]() { x = 3; };\n    x = 2;\n    if (do_throw) {\n      throw std::exception{};\n    }\n  } catch (const std::exception&) {\n    CHECK(x == 3);\n  }\n  CHECK(x == 3);\n  // [cleanup_routine]\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.CleanupRoutine\", \"[Utilities][Unit]\") {\n  test(false);\n  test(true);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_CloneUniquePtrs.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <unordered_map>\n\n#include \"Utilities/CloneUniquePtrs.hpp\"\n\nnamespace {\nstruct NonCopyableValue {\n  explicit constexpr NonCopyableValue(const int value_in) : value(value_in) {}\n  constexpr NonCopyableValue(const NonCopyableValue&) = delete;\n  constexpr NonCopyableValue& operator=(const NonCopyableValue&) = delete;\n  constexpr NonCopyableValue(NonCopyableValue&&) = default;\n  NonCopyableValue& operator=(NonCopyableValue&&) = default;\n  ~NonCopyableValue() = default;\n\n  std::unique_ptr<NonCopyableValue> get_clone() {\n    return std::make_unique<NonCopyableValue>(value);\n  }\n\n  int value;\n};\n\nvoid test_unordered_map() {\n  using Map = std::unordered_map<int, std::unique_ptr<NonCopyableValue>>;\n  const auto check_cloning = [](const Map& map) {\n    const auto map_copy = clone_unique_ptrs(map);\n    REQUIRE(map.size() == map_copy.size());\n    for (const auto& kv : map) {\n      CHECK(map_copy.at(kv.first)->value == kv.second->value);\n    }\n  };\n  check_cloning({});\n\n  Map t0{};\n  t0[10] = std::make_unique<NonCopyableValue>(150);\n  check_cloning(t0);\n\n  Map t1{};\n  t1[10] = std::make_unique<NonCopyableValue>(150);\n  t1[32] = std::make_unique<NonCopyableValue>(1);\n  check_cloning(t1);\n\n  Map t2{};\n  t2[10] = std::make_unique<NonCopyableValue>(150);\n  t2[32] = std::make_unique<NonCopyableValue>(1);\n  t2[-1] = std::make_unique<NonCopyableValue>(100);\n  check_cloning(t2);\n}\n\nvoid test_vector() {\n  using Vector = std::vector<std::unique_ptr<NonCopyableValue>>;\n  const auto check_cloning = [](const Vector& vector) {\n    const auto vector_copy = clone_unique_ptrs(vector);\n    REQUIRE(vector.size() == vector_copy.size());\n    for (size_t i = 0; i < vector.size(); ++i) {\n      CHECK(vector_copy[i]->value == vector[i]->value);\n    }\n  };\n\n  check_cloning({});\n\n  Vector t0{1};\n  t0[0] = std::make_unique<NonCopyableValue>(10);\n  check_cloning(t0);\n\n  Vector t1{2};\n  t1[0] = std::make_unique<NonCopyableValue>(10);\n  t1[1] = std::make_unique<NonCopyableValue>(100);\n  check_cloning(t1);\n\n  Vector t2{3};\n  t2[0] = std::make_unique<NonCopyableValue>(10);\n  t2[1] = std::make_unique<NonCopyableValue>(100);\n  t2[2] = std::make_unique<NonCopyableValue>(200);\n  check_cloning(t2);\n}\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.CloneUniquePtrs\", \"[Unit][Utilities]\") {\n  test_unordered_map();\n  test_vector();\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_ConstantExpressions.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cmath>\n#include <cstddef>\n#include <functional>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\ntemplate <typename T>\nstruct TestTwoToThe {\n  static constexpr T zero = 0;\n  static constexpr T one = 1;\n  static constexpr T two = 2;\n  static constexpr T three = 3;\n  static constexpr T four = 4;\n  static constexpr T eight = 8;\n  static constexpr T sixteen = 16;\n\n  static_assert(one == two_to_the(zero),\n                \"Failed test Unit.Utilities.ConstantExpressions\");\n  static_assert(two == two_to_the(one),\n                \"Failed test Unit.Utilities.ConstantExpressions\");\n  static_assert(four == two_to_the(two),\n                \"Failed test Unit.Utilities.ConstantExpressions\");\n  static_assert(eight == two_to_the(three),\n                \"Failed test Unit.Utilities.ConstantExpressions\");\n  static_assert(sixteen == two_to_the(four),\n                \"Failed test Unit.Utilities.ConstantExpressions\");\n};\n\ntemplate struct TestTwoToThe<unsigned int>;\ntemplate struct TestTwoToThe<unsigned short>;\ntemplate struct TestTwoToThe<unsigned long>;\ntemplate struct TestTwoToThe<unsigned long long>;\n\nstatic_assert(square(2) == 4, \"Failed test Unit.Utilities.ConstantExpressions\");\n\nstatic_assert(cube(2) == 8, \"Failed test Unit.Utilities.ConstantExpressions\");\n\nstatic_assert(get_nth_bit(0, 0) == 0,\n              \"Failed test Unit.Utilities.ConstantExpressions\");\nstatic_assert(get_nth_bit(1, 0) == 1,\n              \"Failed test Unit.Utilities.ConstantExpressions\");\nstatic_assert(get_nth_bit(2, 0) == 0,\n              \"Failed test Unit.Utilities.ConstantExpressions\");\nstatic_assert(get_nth_bit(2, 1) == 1,\n              \"Failed test Unit.Utilities.ConstantExpressions\");\nstatic_assert(get_nth_bit(4, 2) == 1,\n              \"Failed test Unit.Utilities.ConstantExpressions\");\n\n// Test falling_factorial and factorial\nstatic_assert(falling_factorial(3, 3) == 6, \"Failed testing falling factorial\");\nstatic_assert(falling_factorial(3, 2) == 6, \"Failed testing falling factorial\");\nstatic_assert(falling_factorial(3, 1) == 3, \"Failed testing falling factorial\");\nstatic_assert(falling_factorial(3, 0) == 1, \"Failed testing falling factorial\");\nstatic_assert(falling_factorial(8, 8) == 40320,\n              \"Failed testing falling factorial\");\nstatic_assert(falling_factorial(20, 20) == 2432902008176640000,\n              \"Failed testing falling factorial\");\nstatic_assert(falling_factorial(21, 1) == 21,\n              \"Failed testing falling factorial\");\nstatic_assert(falling_factorial(55, 2) == 55 * 54,\n              \"Failed testing falling factorial\");\nstatic_assert(falling_factorial(55, 5) == 417451320,\n              \"Failed testing falling factorial\");\n\nstatic_assert(factorial(3) == 6, \"Failed testing factorial\");\nstatic_assert(factorial(8) == 40320, \"Failed testing factorial\");\nstatic_assert(factorial(20) == 2432902008176640000, \"Failed testing factorial\");\n\n// Test binomial coefficient\nstatic_assert(binomial(0, 0) == 1);\nstatic_assert(binomial(1, 0) == 1);\nstatic_assert(binomial(1, 1) == 1);\nstatic_assert(binomial(2, 0) == 1);\nstatic_assert(binomial(2, 1) == 2);\nstatic_assert(binomial(2, 2) == 1);\nstatic_assert(binomial(3, 0) == 1);\nstatic_assert(binomial(3, 1) == 3);\nstatic_assert(binomial(3, 2) == 3);\nstatic_assert(binomial(3, 3) == 1);\nstatic_assert(binomial(4, 0) == 1);\nstatic_assert(binomial(4, 1) == 4);\nstatic_assert(binomial(4, 2) == 6);\nstatic_assert(binomial(4, 3) == 4);\nstatic_assert(binomial(4, 4) == 1);\n\n// Test pow<>\nstatic_assert(pow<4>(2.0) == 16.0, \"Failed testing pow\");\nstatic_assert(pow<0>(2.0) == 1.0, \"Failed testing pow\");\nstatic_assert(pow<-4>(2.0) == 1.0 / 16.0, \"Failed testing pow\");\n\n// Test abs\nstatic_assert(ce_abs(0) == 0, \"Failed testing abs\");\nstatic_assert(ce_abs(1.4) == 1.4, \"Failed testing abs\");\nstatic_assert(ce_abs(-1.4) == 1.4, \"Failed testing abs\");\n\n// Test fabs\nstatic_assert(ce_fabs(-1.4) == 1.4, \"Failed testing fabs\");\nstatic_assert(ce_fabs(-1.4f) == 1.4f, \"Failed testing fabs\");\n\n// Test max_by_magnitude\nstatic_assert(max_by_magnitude(1, 2) == 2, \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(2, 1) == 2, \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(-1, 2) == 2, \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(2, -1) == 2, \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(1, -2) == -2, \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(-2, 1) == -2, \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(-1, -2) == -2,\n              \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(-2, -1) == -2,\n              \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude({-2, -1, -3}) == -3,\n              \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(1, -1) == 1, \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude(-1, 1) == -1, \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude({1, -1}) == 1,\n              \"Failed testing max_by_magnitude\");\nstatic_assert(max_by_magnitude({-1, 1}) == -1,\n              \"Failed testing max_by_magnitude\");\n\n// Test min_by_magnitude\nstatic_assert(min_by_magnitude(1, 2) == 1, \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(2, 1) == 1, \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(-1, 2) == -1, \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(2, -1) == -1, \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(1, -2) == 1, \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(-2, 1) == 1, \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(-1, -2) == -1,\n              \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(-2, -1) == -1,\n              \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude({-2, -1, -3}) == -1,\n              \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(1, -1) == 1, \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude(-1, 1) == -1, \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude({1, -1}) == 1,\n              \"Failed testing min_by_magnitude\");\nstatic_assert(min_by_magnitude({-1, 1}) == -1,\n              \"Failed testing min_by_magnitude\");\n\nstruct TwoN {\n  template <typename T>\n  constexpr size_t operator()(T n) {\n    return 2 * n;\n  }\n};\nstatic_assert(constexpr_sum<5>(TwoN{}) == 20, \"Failed testing constexpr_sum\");\n\n// Test string manipulation\nconstexpr const char* const dummy_string1 = \"test 1\";\nconstexpr const char* const dummy_string2 = \"test 1\";\nconstexpr const char* const dummy_string3 = \"test blah\";\nstatic_assert(6 == cstring_length(dummy_string1),\n              \"Failed testing cstring_length\");\nstatic_assert(cstring_hash(dummy_string1) == cstring_hash(dummy_string2),\n              \"Failed testing cstring_hash\");\nstatic_assert(cstring_hash(dummy_string1) != cstring_hash(dummy_string3),\n              \"Failed testing cstring_hash\");\n\n// Test array_equal and replace_at\nconstexpr std::array<double, 3> array1{{1.2, 3, 4}};\nconstexpr std::array<double, 3> array1_copy{{1.2, 3, 4}};\nstatic_assert(array_equal(array1, array1_copy), \"Failed testing array_equal\");\nstatic_assert(not array_equal(array1, std::array<double, 3>{{2.2, 3, 4}}),\n              \"Failed testing array_equal\");\nstatic_assert(array_equal(replace_at<1>(array1, 5.0),\n                          std::array<double, 3>{{1.2, 5, 4}}),\n              \"Failed testing array_equal\");\n\n// Test make_array_from_list\nstatic_assert(\n    array_equal(std::array<size_t, 3>{{1, 2, 5}},\n                make_array_from_list<tmpl::integral_list<size_t, 1, 2, 5>>()),\n    \"Failed testing make_array_from_list\");\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.ConstantExpressions\", \"[Unit][Utilities]\") {\n  CHECK((std::array<std::array<size_t, 3>, 3>{\n            {std::array<size_t, 3>{{1, 2, 5}}, std::array<size_t, 3>{{8, 7, 2}},\n             std::array<size_t, 3>{{3, 9, 0}}}}) ==\n        (make_array_from_list<\n            tmpl::list<tmpl::integral_list<size_t, 1, 2, 5>,\n                       tmpl::integral_list<size_t, 8, 7, 2>,\n                       tmpl::integral_list<size_t, 3, 9, 0>>>()));\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_ContainerHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/Tensor/Tensor.hpp\"\n#include \"DataStructures/Tensor/TypeAliases.hpp\"\n#include \"Utilities/ContainerHelpers.hpp\"\n\n// [get_element_example_indexing_callable]\nstruct ArrayOfArraysIndexFunctor {\n  template <size_t OuterArraySize, size_t InnerArraySize, typename T>\n  T& operator()(std::array<std::array<T, InnerArraySize>, OuterArraySize>&\n                    array_of_arrays,\n                size_t index) {\n    return array_of_arrays.at(index % OuterArraySize)\n        .at(index / OuterArraySize);\n  }\n};\n// [get_element_example_indexing_callable]\n\n// [get_size_example_size_callable]\nstruct ArrayOfArraysSizeFunctor {\n  template <size_t OuterArraySize, size_t InnerArraySize, typename T>\n  size_t operator()(\n      const std::array<std::array<T, InnerArraySize>, OuterArraySize>&\n      /*array_of_arrays*/) {\n    return OuterArraySize * InnerArraySize;\n  }\n};\n// [get_size_example_size_callable]\n\nnamespace {\nstruct HalfDataVectorSize {\n  size_t operator()(const DataVector& x) { return x.size() / 2; }\n};\n\nstruct ReverseIndexStlVector {\n  template <typename T>\n  decltype(auto) operator()(T& x, size_t i) {\n    return x.at(x.size() - i - 1);\n  }\n};\n\n}  // namespace\nSPECTRE_TEST_CASE(\"Unit.Utilities.ContainerHelpers\", \"[Unit][Utilities]\") {\n  const std::vector<double> constant_stl_vector{0.0, 1.0, 2.0, 3.0, 4.0,\n                                                5.0, 6.0, 7.0, 8.0, 9.0};\n  const double constant_fundamental = 10.0;\n  const std::complex<double> constant_complex_of_fundamental =\n      std::complex<double>(1.0, 5.0);\n  const DataVector constant_spectre_vector(10, 2.0);\n  std::vector<double> stl_vector{0.0, 1.0, 2.0, 3.0, 4.0,\n                                 5.0, 6.0, 7.0, 8.0, 9.0};\n  double fundamental = 10.0;\n  std::complex<double> complex_of_fundamental = std::complex<double>(1.0, 5.0);\n  std::array<std::array<double, 5>, 2> array_of_arrays = {\n      {{{0.0, 2.0, 4.0, 6.0, 8.0}}, {{1.0, 3.0, 5.0, 7.0, 9.0}}}};\n\n  DataVector spectre_vector(10, 2.0);\n  REQUIRE(get_size(constant_stl_vector) == 10);\n  REQUIRE(get_size(constant_fundamental) == 1);\n  REQUIRE(get_size(constant_complex_of_fundamental) == 1);\n  REQUIRE(get_size(constant_spectre_vector) == 10);\n  REQUIRE(get_size(stl_vector) == 10);\n  REQUIRE(get_size(fundamental) == 1);\n  REQUIRE(get_size(complex_of_fundamental) == 1);\n  REQUIRE(get_size(spectre_vector) == 10);\n  REQUIRE(get_size(array_of_arrays, ArrayOfArraysSizeFunctor{}) == 10);\n\n  CHECK(get_size(constant_spectre_vector, HalfDataVectorSize{}) == 5);\n  CHECK(get_size(spectre_vector, HalfDataVectorSize{}) == 5);\n  CHECK(get_size(constant_fundamental, HalfDataVectorSize{}) == 1);\n  CHECK(get_size(fundamental, HalfDataVectorSize{}) == 1);\n\n  std::vector<double> stl_vector_to_reverse{0.0, 1.0, 2.0, 3.0, 4.0,\n                                            5.0, 6.0, 7.0, 8.0, 9.0};\n\n  for (size_t i = 0; i < constant_stl_vector.size(); ++i) {\n    get_element(stl_vector, i) = get_element(constant_stl_vector, i) * 2.0;\n    get_element(stl_vector_to_reverse, i, ReverseIndexStlVector{}) =\n        get_element(constant_stl_vector, i) * 2.0;\n    get_element(fundamental, i) = get_element(constant_fundamental, i) * 2.0;\n    get_element(complex_of_fundamental, i) =\n        get_element(constant_complex_of_fundamental, i) * 2.0;\n    get_element(spectre_vector, i) =\n        get_element(constant_spectre_vector, i) * 2.0;\n  }\n\n  for (size_t i = 0; i < constant_stl_vector.size(); ++i) {\n    CHECK(get_element(constant_stl_vector, i) == static_cast<double>(i));\n    CHECK(get_element(constant_stl_vector, i, ReverseIndexStlVector{}) ==\n          (9.0 - static_cast<double>(i)));\n    CHECK(get_element(constant_fundamental, i) == 10.0);\n    CHECK(get_element(constant_fundamental, i, ReverseIndexStlVector{}) ==\n          10.0);\n    CHECK(get_element(constant_complex_of_fundamental, i) ==\n          std::complex<double>(1.0, 5.0));\n    CHECK(get_element(constant_spectre_vector, i) == 2.0);\n    CAPTURE(stl_vector);\n    CAPTURE(stl_vector_to_reverse);\n    CHECK(get_element(stl_vector, i) == 2.0 * static_cast<double>(i));\n    CHECK(get_element(stl_vector, i, ReverseIndexStlVector{}) ==\n          (9.0 - static_cast<double>(i)) * 2.0);\n    CHECK(get_element(stl_vector_to_reverse, i) ==\n          (9.0 - static_cast<double>(i)) * 2.0);\n    CHECK(get_element(fundamental, i) == 20.0);\n    CHECK(get_element(fundamental, i, ReverseIndexStlVector{}) == 20.0);\n    CHECK(get_element(complex_of_fundamental, i) ==\n          std::complex<double>(2.0, 10.0));\n    CHECK(get_element(spectre_vector, i) == 4.0);\n    CHECK(get_element(array_of_arrays, i, ArrayOfArraysIndexFunctor{}) ==\n          static_cast<double>(i));\n  }\n  CHECK(min(constant_spectre_vector) == min(2.0));\n  CHECK(max(constant_spectre_vector) == max(2.0));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_DereferenceWrapper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <functional>\n#include <type_traits>\n\n#include \"Utilities/DereferenceWrapper.hpp\"\n\nstatic_assert(\n    std::is_same<decltype(dereference_wrapper(std::declval<double>())),\n                 double&&>::value,\n    \"Failed testing dereference_wrapper\");\nstatic_assert(\n    std::is_same<decltype(dereference_wrapper(std::declval<const double>())),\n                 const double&&>::value,\n    \"Failed testing dereference_wrapper\");\nstatic_assert(\n    std::is_same<decltype(dereference_wrapper(std::declval<double&>())),\n                 double&>::value,\n    \"Failed testing dereference_wrapper\");\nstatic_assert(\n    std::is_same<decltype(dereference_wrapper(std::declval<const double&>())),\n                 const double&>::value,\n    \"Failed testing dereference_wrapper\");\n\nstatic_assert(std::is_same<decltype(dereference_wrapper(\n                               std::declval<std::reference_wrapper<double>>())),\n                           double&&>::value,\n              \"Failed testing dereference_wrapper\");\nstatic_assert(\n    std::is_same<decltype(dereference_wrapper(\n                     std::declval<const std::reference_wrapper<double>>())),\n                 double&&>::value,\n    \"Failed testing dereference_wrapper\");\nstatic_assert(\n    std::is_same<decltype(dereference_wrapper(\n                     std::declval<std::reference_wrapper<double>&>())),\n                 double&>::value,\n    \"Failed testing dereference_wrapper\");\nstatic_assert(\n    std::is_same<decltype(dereference_wrapper(\n                     std::declval<const std::reference_wrapper<double>&>())),\n                 double&>::value,\n    \"Failed testing dereference_wrapper\");\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.DereferenceWrapper\", \"[Unit][Utilities]\") {\n  double a = 1.478;\n  std::reference_wrapper<double> ref_a(a);\n  CHECK(dereference_wrapper(ref_a) == 1.478);\n  std::reference_wrapper<const double> ref_ca(a);\n  CHECK(dereference_wrapper(ref_ca) == 1.478);\n  const std::reference_wrapper<double> cref_a(a);\n  CHECK(dereference_wrapper(cref_a) == 1.478);\n  const std::reference_wrapper<const double> cref_ca(a);\n  CHECK(dereference_wrapper(cref_ca) == 1.478);\n\n  CHECK(dereference_wrapper(a) == 1.478);\n  CHECK(dereference_wrapper(static_cast<const double&>(a)) == 1.478);\n  CHECK(dereference_wrapper(static_cast<double&>(a)) == 1.478);\n  CHECK(dereference_wrapper(static_cast<const double&&>(a)) == 1.478);\n  CHECK(dereference_wrapper(static_cast<double&&>(a)) == 1.478);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_EqualWithinRoundoff.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <vector>\n\n#include \"Utilities/EqualWithinRoundoff.hpp\"\n\nstatic_assert(equal_within_roundoff(1.0, 1.0, 0.0),\n              \"Failed testing EqualWithinRoundoff\");\n\nstatic_assert(equal_within_roundoff(1.0, 1.0 - 4.0e-16, 1.0e-15),\n              \"Failed testing EqualWithinRoundoff\");\nstatic_assert(not equal_within_roundoff(1.0, 1.0 - 4.0e-15, 1.0e-15),\n              \"Failed testing EqualWithinRoundoff\");\n\nstatic_assert(equal_within_roundoff(1.0e16, 1.0e16 - 1.0e1, 1.0e-15, 1.0e16),\n              \"Failed testing EqualWithinRoundoff\");\nstatic_assert(not equal_within_roundoff(1.0e16, 1.0e16 - 2.0e1, 1.0e-15, 1.0),\n              \"Failed testing EqualWithinRoundoff\");\n\nstatic_assert(not equal_within_roundoff(1.0, 1.0 - 1.0e-8, 1.0e-8, 0.0),\n              \"Failed testing EqualWithinRoundoff\");\nstatic_assert(equal_within_roundoff(1.0, 1.0 - 1.0e-8, 1.0e-8, 1.0),\n              \"Failed testing EqualWithinRoundoff\");\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.EqualWithinRoundoff\", \"[Unit][Utilities]\") {\n  CHECK(equal_within_roundoff(\n      std::vector<double>{1.0, 1.0 - 4.0e-16, 1.0 + 5.0e-15}, 1.0));\n  CHECK(equal_within_roundoff(\n      1.0, std::vector<double>{1.0, 1.0 - 4.0e-16, 1.0 + 5.0e-15}));\n  CHECK(equal_within_roundoff(\n      std::array<double, 3>{{1.0, 1.0 - 4.0e-16, 1.0 + 5.0e-15}}, 1.0));\n  CHECK(equal_within_roundoff(\n      1.0, std::array<double, 3>{{1.0, 1.0 - 4.0e-16, 1.0 + 5.0e-15}}));\n  CHECK(equal_within_roundoff(\n      std::vector<double>{1.0, 1.0 - 4.0e-16, 1.0 + 5.0e-15},\n      std::array<double, 3>{{1.0 - 4.0e-16, 1.0 + 5.0e-15, 1.0}}));\n  CHECK(equal_within_roundoff(\n      std::vector<double>{1.0, 1.0 - 4.0e-10, 1.0 + 5.0e-10},\n      std::array<double, 3>{{1.0 - 4.0e-10, 1.0 + 5.0e-10, 1.0}}, 1.e-9));\n  CHECK_FALSE(equal_within_roundoff(\n      std::vector<double>{1.0, 1.0 - 4.0e-10, 1.0 + 5.0e-10},\n      std::array<double, 3>{{1.0 - 4.0e-10, 1.0 + 5.0e-10, 1.0}}, 1.e-11));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_FileSystem.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstdlib>\n#include <cstring>\n#include <fcntl.h>\n#include <fstream>\n#include <string>\n#include <unistd.h>\n#include <vector>\n\n#include \"Utilities/Algorithm.hpp\"\n#include \"Utilities/FileSystem.hpp\"\n\nnamespace {\nvoid create_file(const char* const file) { std::fstream(file, std::ios::out); }\n\nvoid test() {\n  {\n    INFO(\"get_parent_path\");\n    // [get_parent_path]\n    CHECK(\"/test/path/to/dir\"s ==\n          file_system::get_parent_path(\"/test/path/to/dir/dummy.txt\"));\n    CHECK(\"/test/path/to\"s ==\n          file_system::get_parent_path(\"/test/path/to/dir/\"));\n    CHECK(\"/\"s == file_system::get_parent_path(\"/\"));\n    CHECK(\"path/to/dir\"s ==\n          file_system::get_parent_path(\"path/to/dir/dummy.txt\"));\n    CHECK(\"/usr\"s == file_system::get_parent_path(\"/usr/lib/\"));\n    CHECK(\"/\"s == file_system::get_parent_path(\"/usr\"));\n    CHECK(\".\"s == file_system::get_parent_path(\"usr\"));\n    CHECK(\".\"s == file_system::get_parent_path(\"..\"));\n    CHECK(\".\"s == file_system::get_parent_path(\"\"));\n    // [get_parent_path]\n  }\n  {\n    INFO(\"get_file_name\");\n    // [get_file_name]\n    CHECK(\"dummy.txt\"s ==\n          file_system::get_file_name(\"/test/path/to/dir/dummy.txt\"));\n    CHECK(\".dummy.txt\"s ==\n          file_system::get_file_name(\"/test/path/to/dir/.dummy.txt\"));\n    CHECK(\"dummy.txt\"s == file_system::get_file_name(\"./dummy.txt\"));\n    CHECK(\"dummy.txt\"s == file_system::get_file_name(\"../dummy.txt\"));\n    CHECK(\".dummy.txt\"s == file_system::get_file_name(\".dummy.txt\"));\n    CHECK(\"dummy.txt\"s == file_system::get_file_name(\"dummy.txt\"));\n    CHECK(\".dummy\"s == file_system::get_file_name(\".dummy\"));\n    // [get_file_name]\n  }\n  {\n    INFO(\"get_absolute_path\");\n    CHECK(file_system::cwd() == file_system::get_absolute_path(\"./\"));\n  }\n  {\n    INFO(\"check_if_exists\");\n    CHECK(file_system::check_if_dir_exists(\"./\"));\n    create_file(\"check_if_exists.txt\");\n    CHECK(file_system::check_if_file_exists(\"./check_if_exists.txt\"));\n    CHECK(0 == file_system::file_size(\"./check_if_exists.txt\"));\n\n    {\n      std::fstream file(\"check_if_exists.txt\", file.out);\n      file << \"Write something\";\n    }\n    CHECK(0 < file_system::file_size(\"./check_if_exists.txt\"));\n\n    file_system::rm(\"./check_if_exists.txt\", false);\n    CHECK_FALSE(file_system::check_if_file_exists(\"./check_if_exists.txt\"));\n  }\n  {\n    INFO(\"create_and_rm_directory\");\n    const std::string dir_one(\n        \"./create_and_rm_directory/nested///nested2/nested3///\");\n    file_system::create_directory(dir_one);\n    CHECK(file_system::check_if_dir_exists(dir_one));\n    const std::string dir_two(\n        \"./create_and_rm_directory/nested/nested2/nested4\");\n    file_system::create_directory(dir_two);\n    CHECK(file_system::check_if_dir_exists(dir_two));\n    create_file(\"./create_and_rm_directory//nested/nested2/nested4/\"\n                \"check_if_exists.txt\");\n    // Check that creating an existing directory does nothing\n    file_system::create_directory(dir_two);\n    CHECK(file_system::check_if_dir_exists(dir_two));\n    CHECK(file_system::check_if_file_exists(dir_two + \"/check_if_exists.txt\"s));\n    file_system::rm(\"./create_and_rm_directory\"s, true);\n    CHECK_FALSE(file_system::check_if_dir_exists(\"./create_and_rm_directory\"s));\n  }\n  {\n    INFO(\"create_and_rm_empty_directory\");\n    const std::string dir_name(\"./create_and_rm_empty_directory\");\n    file_system::create_directory(dir_name);\n    CHECK(file_system::check_if_dir_exists(dir_name));\n    file_system::rm(dir_name, false);\n    CHECK_FALSE(file_system::check_if_dir_exists(dir_name));\n  }\n  {\n    INFO(\"create_dir_root\");\n    file_system::create_directory(\"/\"s);\n    CHECK(file_system::check_if_dir_exists(\"/\"s));\n  }\n  {\n    INFO(\"glob\");\n    create_file(\"glob1.txt\");\n    create_file(\"glob2.txt\");\n    CHECK(file_system::glob(\"glob*.txt\") ==\n          std::vector<std::string>{\"glob1.txt\", \"glob2.txt\"});\n    file_system::rm(\"glob1.txt\", false);\n    file_system::rm(\"glob2.txt\", false);\n  }\n  {\n    INFO(\"ls\");\n    file_system::create_directory(\"ls_test\"s);\n    create_file(\"ls_test/file1\");\n    create_file(\"ls_test/file2\");\n    create_file(\"ls_test/file3\");\n    file_system::create_directory(\"ls_test/dir1\"s);\n    create_file(\"ls_test/dir1/nested_file\");\n    std::vector<std::string> expected_list{\"file1\", \"file2\", \"file3\", \"dir1\"};\n    auto list = file_system::ls(\"ls_test\");\n    alg::sort(list);\n    alg::sort(expected_list);\n    CHECK(list == expected_list);\n  }\n}\n\nvoid trigger_nonexistent_absolute_path() {\n  static_cast<void>(\n      file_system::get_absolute_path(\"./get_absolute_path_nonexistent/\"));\n}\n\nvoid trigger_rm_error_not_empty() {\n  const std::string dir_name(\"./rm_error_not_empty\");\n  file_system::create_directory(dir_name);\n  CHECK(file_system::check_if_dir_exists(dir_name));\n  std::fstream file(\"./rm_error_not_empty/cause_error_in_rm.txt\", file.out);\n  file.close();\n  file_system::rm(\"./rm_error_not_empty\"s, false);\n}\n\nvoid test_errors() {\n  CHECK_THROWS_WITH(file_system::get_file_name(\"/\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Failed to find a file in the given path: '/'\"));\n  CHECK_THROWS_WITH(\n      file_system::get_file_name(\"\"),\n      Catch::Matchers::ContainsSubstring(\"Received an empty path\"));\n  CHECK_THROWS_WITH(\n      trigger_nonexistent_absolute_path(),\n      Catch::Matchers::ContainsSubstring(\"No such file or directory\"));\n  CHECK_THROWS_WITH(\n      file_system::file_size(\"./file_size_error.txt\"),\n      Catch::Matchers::ContainsSubstring(\n          \"Cannot get size of file './file_size_error.txt' because \"\n          \"it cannot be accessed. Either it does not exist or you \"\n          \"do not have the appropriate permissions.\"));\n  CHECK_THROWS_WITH(\n      trigger_rm_error_not_empty(),\n      Catch::Matchers::ContainsSubstring(\"remove: Directory not empty\"));\n  CHECK_THROWS_WITH(file_system::is_file(\"./is_file_error\"),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Failed to check if path points to a file \"\n                        \"because the path is invalid\"));\n  CHECK_THROWS_WITH(file_system::create_directory(\"\"s),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot create a directory that has no name\"));\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.FileSystem\", \"[Unit][Utilities]\") {\n  // This is a macro to report locations better and to ensure that\n  // creating the argument can't modify errno before we save it.\n#define SYS_ERROR(description)                                 \\\n  do {                                                         \\\n    const int errno_save = errno;                              \\\n    ERROR(description << \" failed: \" << strerror(errno_save)); \\\n  } while (false)\n\n  // Run the test in a temporary directory so we have a known starting\n  // state for the filesystem.  This also simplifies cleanup.\n  const int original_directory = open(\".\", O_RDONLY);\n  if (original_directory == -1) {\n    SYS_ERROR(\"opendir(.)\");\n  }\n  char scratch_directory[] = \"Test_FileSystem_scratch-XXXXXX\";\n  if (mkdtemp(scratch_directory) == nullptr) {\n    // scratch_directory may have been modified, so reporting it in\n    // the error would be misleading.\n    SYS_ERROR(\"mkdtemp\");\n  }\n  const auto cleanup = [&]() {\n    if (fchdir(original_directory) != 0) {\n      SYS_ERROR(\"fchdir\");\n    }\n    close(original_directory);\n    file_system::rm(scratch_directory, true);\n  };\n\n  try {\n    if (chdir(scratch_directory) != 0) {\n      SYS_ERROR(\"chrdir(\" << scratch_directory << \")\");\n    }\n    test();\n    test_errors();\n    cleanup();\n  } catch (...) {\n    cleanup();\n    throw;\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Formaline.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <vector>\n\n#include \"Utilities/FileSystem.hpp\"\n#include \"Utilities/Formaline.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Formaline\", \"[Unit][Utilities]\") {\n#ifndef __APPLE__\n  SECTION(\"check archive size\") {\n    const auto archive = formaline::get_archive();\n    // The archive should be at least 1.7MB since it is ~1.73MB as of\n    // 02/14/2019.\n    CHECK(archive.size() > 1740 * 1024);\n  }\n  SECTION(\"check paths\") {\n    const auto paths = formaline::get_paths();\n    CHECK(paths.find(\"PATH=\") != std::string::npos);\n    CHECK(paths.find(\"CPATH=\") != std::string::npos);\n    CHECK(paths.find(\"LD_LIBRARY_PATH=\") != std::string::npos);\n    CHECK(paths.find(\"LIBRARY_PATH=\") != std::string::npos);\n    CHECK(paths.find(\"CMAKE_PREFIX_PATH=\") != std::string::npos);\n  }\n  SECTION(\"check writing archive to file\") {\n    const std::string basename = \"./FormalineTestArchive\";\n    const std::string filename = basename + \".tar.gz\";\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, false);\n    }\n    formaline::write_to_file(basename);\n    CHECK(file_system::check_if_file_exists(filename));\n    if (file_system::check_if_file_exists(filename)) {\n      file_system::rm(filename, false);\n    }\n  }\n#else\n  // Need to do at least 1 check otherwise Catch will fail the test.\n  CHECK(true);\n#endif  // defined(__APPLE__)\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_FractionUtilities.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <cstddef>\n#include <cstdint>\n#include <limits>\n#include <random>\n#include <set>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Assert.hpp\"\n#include \"Utilities/FractionUtilities.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nnamespace {\ntemplate <typename Source>\nstd::vector<typename Source::value_type> collect(\n    Source source, const size_t limit = std::numeric_limits<size_t>::max()) {\n  std::vector<typename Source::value_type> result;\n  for (size_t count = 0; count < limit and source; ++count, ++source) {\n    result.push_back(*source);\n  }\n  return result;\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.FractionUtilities.ContinuedFraction\",\n                  \"[Utilities][Unit]\") {\n  CHECK((std::vector<int32_t>{1, 2, 4, 2}) ==\n        collect(ContinuedFraction<Rational>(Rational(29, 20))));\n  CHECK((std::vector<int64_t>{0, 8}) ==\n        collect(ContinuedFraction<double>(0.125)));\n  CHECK((std::vector<int64_t>{-1, 1, 7}) ==\n        collect(ContinuedFraction<double>(-0.125)));\n  CHECK(std::vector<int64_t>(20, 1) ==\n        collect(ContinuedFraction<double>(0.5 * (1. + sqrt(5.))), 20));\n  CHECK((std::vector<int32_t>{0}) ==\n        collect(ContinuedFraction<Rational>(Rational(0))));\n  CHECK((std::vector<int64_t>{0}) == collect(ContinuedFraction<double>(0.)));\n\n  // Check that the iterator terminates because of precision loss.\n  CHECK(collect(ContinuedFraction<double>(0.5 * (1. + sqrt(5.))), 100).size() <\n        100);\n\n  // Check that the iterator doesn't terminate prematurely\n  // because of precision loss.\n  const int64_t two_to_the_fourty = 1099511627776;\n  CHECK(std::vector<int64_t>{0, two_to_the_fourty} ==\n        collect(ContinuedFraction<double>(1. / two_to_the_fourty)));\n  CHECK(std::vector<int64_t>{1, two_to_the_fourty} ==\n        collect(ContinuedFraction<double>(1 + 1. / two_to_the_fourty)));\n  CHECK(std::vector<int64_t>{0, two_to_the_fourty, 2} ==\n        collect(ContinuedFraction<double>(1. / (two_to_the_fourty + 0.5))));\n\n  MAKE_GENERATOR(gen);\n  {\n    std::uniform_real_distribution<> dist(-10., 10.);\n    const double value = dist(gen);\n    // Set the scale because the fractional part of a negative number\n    // (defined as `x - floor(x)`) can be larger than the number.\n    auto local_approx = approx;\n    local_approx.scale(std::max(value, std::abs(std::floor(value))));\n    ContinuedFractionSummer<Rational> summer;\n    bool should_be_smaller = true;\n    std::vector<int64_t> terms{};  // Only for output\n    std::vector<double> convergents{};  // Only for output\n    for (ContinuedFraction<double> source(value); source; ++source) {\n      summer.insert(*source);\n      terms.push_back(*source);\n      convergents.push_back(summer.value().value());\n      CAPTURE(terms);\n      CAPTURE(convergents);\n\n      // Convergents to a continued fraction always alternate between\n      // over- and underestimates.\n      if (convergents.back() != local_approx(value)) {\n        if (should_be_smaller) {\n          CHECK(convergents.back() <= value);\n        } else {\n          CHECK(convergents.back() >= value);\n        }\n      }\n      should_be_smaller = !should_be_smaller;\n    }\n    CAPTURE(terms);\n    CAPTURE(convergents);\n    CHECK(convergents.back() == local_approx(value));\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.FractionUtilities.ContinuedFractionSummer\",\n                  \"[Utilities][Unit]\") {\n  const auto check = [](const int num, const int denom) {\n    const Rational value(num, denom);\n    ContinuedFractionSummer<Rational> summer;\n    for (ContinuedFraction<Rational> source(value); source; ++source) {\n      summer.insert(*source);\n    }\n    CHECK(summer.value() == value);\n  };\n\n  check(0, 1);\n  check(1, 1);\n  check(2, 1);\n  check(1, 2);\n  check(2, 3);\n  check(29, 20);\n\n  // Check overflow is handled correctly\n  {\n    ContinuedFractionSummer<Rational> summer;\n    summer.insert(1000);\n    const auto first_value = summer.value();\n    summer.insert(std::numeric_limits<std::int32_t>::max() / 10);\n    CHECK(summer.value() == first_value);\n    summer.insert(1);\n    CHECK(summer.value() == first_value);\n  }\n\n  // Check a case that returns a semiconvergent\n  {\n    ContinuedFractionSummer<Rational> summer;\n    summer.insert(0);\n    summer.insert(2);\n    summer.insert(std::numeric_limits<std::int32_t>::max() / 2 + 10);\n    // Remember that max() is odd, so this isn't 1/2.\n    CHECK(summer.value() ==\n          Rational(std::numeric_limits<std::int32_t>::max() / 2,\n                   std::numeric_limits<std::int32_t>::max()));\n  }\n}\n\nSPECTRE_TEST_CASE(\n    \"Unit.Utilities.FractionUtilities.simplest_fraction_in_interval\",\n    \"[Utilities][Unit]\") {\n  const int denom_max = 20;\n\n  std::set<Rational> fractions;\n  for (int d = 1; d <= denom_max; ++d) {\n    for (int n = 0; n <= d; ++n) {\n      fractions.emplace(n, d);\n    }\n  }\n\n  for (auto end1 = fractions.begin(); end1 != fractions.end(); ++end1) {\n    // Worse than any considered value so will be immediately replaced.\n    Rational simplest(1, denom_max + 1);\n    for (auto end2 = end1; end2 != fractions.end(); ++end2) {\n      if (*end1 == 0 and *end2 == 1) {\n        // Correct answer not clear for the entire interval\n        continue;\n      }\n      ASSERT(end2->denominator() != simplest.denominator(),\n             \"Answer is not unique\");\n      if (end2->denominator() < simplest.denominator()) {\n        simplest = *end2;\n      }\n      CHECK(simplest_fraction_in_interval<Rational>(*end1, *end2) == simplest);\n      CHECK(simplest_fraction_in_interval<Rational>(*end2, *end1) == simplest);\n    }\n  }\n\n  // Quick check of non-exact input\n  CHECK(simplest_fraction_in_interval<Rational>(0.6, 0.9) == Rational(2, 3));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Functional.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cmath>\n#include <complex>\n#include <cstddef>\n#include <map>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/Functional.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/Tuple.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\nnamespace funcl {\nusing std::imag;\nusing std::real;\nusing std::max;\nusing std::min;\n\n// clang-tidy : suppress warning from no-paren on macro parameter, macro doesn't\n// work when it's in parens. Same for all subsequent macros.\n#define MAKE_UNARY_TEST(STRUCTNAME, FUNC)                                \\\n  struct TestFuncEval##STRUCTNAME {                                      \\\n    template <typename T, typename DistT, typename UniformGen>           \\\n    void operator()(const gsl::not_null<UniformGen*> gen,                \\\n                    UniformCustomDistribution<DistT> dist, T /*meta*/) { \\\n      auto val = make_with_random_values<typename T::type>(              \\\n          gen, make_not_null(&dist));                                    \\\n      CHECK(STRUCTNAME<>{}(val) == FUNC(val)); /*NOLINT*/                \\\n    }                                                                    \\\n  }\n\n#define MAKE_BINARY_TEST(STRUCTNAME, FUNC)                                \\\n  struct TestFuncEval##STRUCTNAME {                                       \\\n    template <typename T1, typename T2, typename DistT1, typename DistT2, \\\n              typename UniformGen>                                        \\\n    void operator()(const gsl::not_null<UniformGen*> gen,                 \\\n                    UniformCustomDistribution<DistT1> dist1,              \\\n                    UniformCustomDistribution<DistT2> dist2, T1 /*meta*/, \\\n                    T2 /*meta*/) {                                        \\\n      auto val1 = make_with_random_values<typename T1::type>(             \\\n          gen, make_not_null(&dist1));                                    \\\n      auto val2 = make_with_random_values<typename T2::type>(             \\\n          gen, make_not_null(&dist2));                                    \\\n      CHECK(STRUCTNAME<>{}(val1, val2) == FUNC(val1, val2)); /*NOLINT*/   \\\n    }                                                                     \\\n  }\n\n#define MAKE_BINARY_OP_TEST(STRUCTNAME, OP)                               \\\n  struct TestFuncEval##STRUCTNAME {                                       \\\n    template <typename T1, typename T2, typename DistT1, typename DistT2, \\\n              typename UniformGen>                                        \\\n    void operator()(const gsl::not_null<UniformGen*> gen,                 \\\n                    UniformCustomDistribution<DistT1> dist1,              \\\n                    UniformCustomDistribution<DistT2> dist2, T1 /*meta*/, \\\n                    T2 /*meta*/) {                                        \\\n      auto val1 = make_with_random_values<typename T1::type>(             \\\n          gen, make_not_null(&dist1));                                    \\\n      auto val2 = make_with_random_values<typename T2::type>(             \\\n          gen, make_not_null(&dist2));                                    \\\n      CHECK(STRUCTNAME<>{}(val1, val2) == (val1 OP val2)); /*NOLINT*/     \\\n    }                                                                     \\\n  }\n\n#define MAKE_BINARY_INPLACE_TEST(STRUCTNAME, OP, TESTOP)                       \\\n  struct TestFuncEval##STRUCTNAME {                                            \\\n    template <typename T1, typename T2, typename DistT1, typename DistT2,      \\\n              typename UniformGen,                                             \\\n              Requires<std::is_same_v<                                         \\\n                  typename T1::type,                                           \\\n                  decltype(std::declval<typename T1::type>() TESTOP            \\\n                               std::declval<typename T2::type>())>> = nullptr> \\\n    void operator()(const gsl::not_null<UniformGen*> gen,                      \\\n                    UniformCustomDistribution<DistT1> dist1,                   \\\n                    UniformCustomDistribution<DistT2> dist2, T1 /*meta*/,      \\\n                    T2 /*meta*/) {                                             \\\n      auto val1 = make_with_random_values<typename T1::type>(                  \\\n          gen, make_not_null(&dist1));                                         \\\n      auto val2 = make_with_random_values<typename T2::type>(                  \\\n          gen, make_not_null(&dist2));                                         \\\n      auto val1_copy = val1;                                                   \\\n      auto result = val1_copy TESTOP val2;                                     \\\n      STRUCTNAME<>{}(val1, val2); /*NOLINT*/                                   \\\n      CHECK(val1 == result);                                                   \\\n    }                                                                          \\\n    /* SFINAE to avoid testing illegal combinations like */                    \\\n    /* double += complex<double> in addition to vise-versa. */                 \\\n    /* Below no-op happens when inplace cannot be called. */                   \\\n    template <typename T1, typename T2, typename DistT1, typename DistT2,      \\\n              typename UniformGen,                                             \\\n              Requires<not std::is_same_v<                                     \\\n                  typename T1::type,                                           \\\n                  decltype(std::declval<typename T1::type>() TESTOP            \\\n                               std::declval<typename T2::type>())>> = nullptr> \\\n    void operator()(const gsl::not_null<UniformGen*> /*gen*/,                  \\\n                    UniformCustomDistribution<DistT1> /*dist1*/,               \\\n                    UniformCustomDistribution<DistT2> /*dist2*/, T1 /*meta*/,  \\\n                    T2 /*meta*/) {}                                            \\\n  }\n\nnamespace {\n\nMAKE_UNARY_TEST(Abs, abs);\nMAKE_UNARY_TEST(Acos, acos);\nMAKE_UNARY_TEST(Acosh, acosh);\nMAKE_UNARY_TEST(Asin, asin);\nMAKE_UNARY_TEST(Asinh, asinh);\nMAKE_UNARY_TEST(Atan, atan);\nMAKE_UNARY_TEST(Atanh, atanh);\nMAKE_UNARY_TEST(Cbrt, cbrt);\nMAKE_UNARY_TEST(Cos, cos);\nMAKE_UNARY_TEST(Cosh, cosh);\nMAKE_UNARY_TEST(Erf, erf);\nMAKE_UNARY_TEST(Exp, exp);\nMAKE_UNARY_TEST(Exp2, exp2);\nMAKE_UNARY_TEST(Fabs, fabs);\nMAKE_UNARY_TEST(Imag, imag);\nMAKE_UNARY_TEST(InvCbrt, invcbrt);\nMAKE_UNARY_TEST(InvSqrt, invsqrt);\nMAKE_UNARY_TEST(Log, log);\nMAKE_UNARY_TEST(Log10, log10);\nMAKE_UNARY_TEST(Log2, log2);\nMAKE_UNARY_TEST(Real, real);\nMAKE_UNARY_TEST(Sin, sin);\nMAKE_UNARY_TEST(Sinh, sinh);\nMAKE_UNARY_TEST(StepFunction, step_function);\nMAKE_UNARY_TEST(Square, square);\nMAKE_UNARY_TEST(Sqrt, sqrt);\nMAKE_UNARY_TEST(Tan, tan);\nMAKE_UNARY_TEST(Tanh, tanh);\n\n// test for UnaryPow needs to be handled separately due to the compile-time\n// exponent parameter\ntemplate <int N>\nstruct TestFuncEvalUnaryPow {\n  template <typename T, typename DistT, typename UniformGen>\n  void operator()(const gsl::not_null<UniformGen*> gen,\n                  UniformCustomDistribution<DistT> dist, T /*meta*/) {\n    auto val =\n        make_with_random_values<typename T::type>(gen, make_not_null(&dist));\n    CHECK(UnaryPow<N>{}(val) == pow<N>(val)); /*NOLINT*/\n  }\n};\n\nMAKE_BINARY_TEST(Atan2, atan2);\nMAKE_BINARY_TEST(Hypot, hypot);\nMAKE_BINARY_TEST(Max, max);\nMAKE_BINARY_TEST(Min, min);\nMAKE_BINARY_TEST(Pow, pow);\n\nMAKE_BINARY_OP_TEST(Divides, /);\nMAKE_BINARY_OP_TEST(Minus, -);\nMAKE_BINARY_OP_TEST(Multiplies, *);\nMAKE_BINARY_OP_TEST(Plus, +);\nMAKE_BINARY_OP_TEST(And, and);\nMAKE_BINARY_OP_TEST(Or, or);\n\nMAKE_BINARY_INPLACE_TEST(DivAssign, /=, /);\nMAKE_BINARY_INPLACE_TEST(MinusAssign, -=, -);\nMAKE_BINARY_INPLACE_TEST(MultAssign, *=, *);\nMAKE_BINARY_INPLACE_TEST(PlusAssign, +=, +);\n\nusing RealTypeList = tmpl::list<float, double>;\nusing AllTypeList = tmpl::list<float, double, std::complex<double>>;\nusing DoubleSet = tmpl::list<double, std::complex<double>>;\n\nusing Bound = std::array<double, 2>;\n\ntemplate <typename C, typename ValType, typename Func, typename Gen,\n          size_t... Is>\nvoid test_functional_against_function(\n    const Func func, const gsl::not_null<Gen*> gen, const Bound& bounds,\n    const std::index_sequence<Is...> /*meta*/) {\n  static_assert(sizeof...(Is) == C::arity,\n                \"test was passed incorrect number of arguments\");\n  UniformCustomDistribution<typename tt::get_fundamental_type_t<ValType>> dist{\n      bounds};\n  const auto args = make_with_random_values<std::array<ValType, sizeof...(Is)>>(\n      gen, make_not_null(&dist));\n  if (std::is_same_v<ValType, typename tt::get_fundamental_type_t<ValType>>) {\n    CHECK_ITERABLE_APPROX(C{}(args[Is]...), func(args[Is]...));\n  } else {\n    CHECK(C{}(args[Is]...) == func(args[Is]...));\n  }\n}\n\n// [using_sinusoid]\nusing Sinusoid = funcl::Multiplies<\n    funcl::Identity,\n    funcl::Sin<funcl::Plus<funcl::Identity, funcl::Multiplies<>>>>;\n// [using_sinusoid]\n\n// [using_gaussian]\nusing GaussianExp =\n    funcl::Negate<funcl::Square<funcl::Minus<Identity, Literal<5, double>>>>;\nusing Gaussian = funcl::Exp<GaussianExp>;\n// [using_gaussian]\n\nvoid test_get_argument() {\n  CHECK(GetArgument<1>{}(-2) == -2);\n  CHECK(GetArgument<1, 0>{}(-2) == -2);\n  CHECK(GetArgument<2, 0>{}(-2, 1) == -2);\n  CHECK(GetArgument<2, 1>{}(-2, 8) == 8);\n  CHECK(GetArgument<3, 0>{}(-2, 8, -10) == -2);\n  CHECK(GetArgument<3, 1>{}(-2, 8, -10) == 8);\n  CHECK(GetArgument<3, 2>{}(-2, 8, -10) == -10);\n}\n\nvoid test_assert_equal() { CHECK(AssertEqual<>{}(7, 7) == 7); }\n\ntemplate <typename Gen>\nvoid test_functional_combinations(const gsl::not_null<Gen*> gen) {\n  const Bound generic{{-50.0, 50.0}};\n  const Bound small{{-5.0, 5.0}};\n\n  test_functional_against_function<Plus<Minus<>, Identity>, double>(\n      [](const auto& x, const auto& y, const auto& z) { return (x - y) + z; },\n      gen, generic, std::make_index_sequence<3>());\n  test_functional_against_function<Sinusoid, double>(\n      [](const auto& w, const auto& x, const auto& y, const auto& z) {\n        return w * sin(x + y * z);\n      },\n      gen, small, std::make_index_sequence<4>());\n  // This function needs the distribution to be `small` to avoid rare\n  // accumulation of error\n  test_functional_against_function<Multiplies<Plus<Plus<>, Plus<>>, Identity>,\n                                   double>(\n      [](const auto& x1, const auto& x2, const auto& x3, const auto& x4,\n         const auto& x5) { return x5 * (x1 + x2 + x3 + x4); },\n      gen, small, std::make_index_sequence<5>());\n  test_functional_against_function<\n      Minus<Plus<Identity, Multiplies<>>, Identity>, double>(\n      [](const auto& x1, const auto& x2, const auto& x3, const auto& x4) {\n        return (x1 + (x2 * x3)) - x4;\n      },\n      gen, generic, std::make_index_sequence<4>());\n  test_functional_against_function<Plus<Negate<>, Identity>, double>(\n      [](const auto& x, const auto& y) { return (y - x); }, gen, generic,\n      std::make_index_sequence<2>());\n  test_functional_against_function<Negate<Plus<>>, double>(\n      [](const auto& x, const auto& y) { return -(y + x); }, gen, generic,\n      std::make_index_sequence<2>());\n  test_functional_against_function<Negate<Plus<Identity, Negate<>>>, double>(\n      [](const auto& x, const auto& y) { return -(x - y); }, gen, generic,\n      std::make_index_sequence<2>());\n  test_functional_against_function<Gaussian, double>(\n      [](const auto& x) { return exp(-(x - 5.0) * (x - 5.0)); }, gen, generic,\n      std::make_index_sequence<1>());\n  test_functional_against_function<Negate<Plus<Literal<3>, Negate<>>>, double>(\n      [](const auto& y) { return -(3.0 - y); }, gen, generic,\n      std::make_index_sequence<1>());\n}\n\ntemplate <typename Gen>\nvoid test_generic_unaries(const gsl::not_null<Gen*> gen) {\n  const Bound generic{{-50.0, 50.0}};\n  const Bound gt_one{{1.0, 100.0}};\n  const Bound mone_one{{-1.0, 1.0}};\n  const Bound positive{{.001, 100.0}};\n\n  auto generic_unaries =\n      std::make_tuple(std::make_tuple(TestFuncEvalAbs{}, generic),\n                      std::make_tuple(TestFuncEvalAcos{}, mone_one),\n                      std::make_tuple(TestFuncEvalAcosh{}, gt_one),\n                      std::make_tuple(TestFuncEvalAsin{}, mone_one),\n                      std::make_tuple(TestFuncEvalAsinh{}, generic),\n                      std::make_tuple(TestFuncEvalAtan{}, generic),\n                      std::make_tuple(TestFuncEvalAtanh{}, mone_one),\n                      std::make_tuple(TestFuncEvalCos{}, generic),\n                      std::make_tuple(TestFuncEvalCosh{}, generic),\n                      std::make_tuple(TestFuncEvalExp{}, generic),\n                      std::make_tuple(TestFuncEvalImag{}, generic),\n                      std::make_tuple(TestFuncEvalInvSqrt{}, positive),\n                      std::make_tuple(TestFuncEvalLog{}, positive),\n                      std::make_tuple(TestFuncEvalReal{}, generic),\n                      std::make_tuple(TestFuncEvalSin{}, generic),\n                      std::make_tuple(TestFuncEvalSinh{}, generic),\n                      std::make_tuple(TestFuncEvalSqrt{}, positive),\n                      std::make_tuple(TestFuncEvalSquare{}, generic),\n                      std::make_tuple(TestFuncEvalTan{}, generic),\n                      std::make_tuple(TestFuncEvalTanh{}, generic),\n                      std::make_tuple(TestFuncEvalUnaryPow<1>{}, generic),\n                      std::make_tuple(TestFuncEvalUnaryPow<-2>{}, generic),\n                      std::make_tuple(TestFuncEvalUnaryPow<3>{}, generic));\n\n  tmpl::for_each<AllTypeList>([&gen, &generic_unaries](auto x) {\n    using DistType =\n        typename tt::get_fundamental_type_t<typename decltype(x)::type>;\n    tmpl::for_each<\n        tmpl::make_sequence<std::integral_constant<size_t, 0>,\n                            std::tuple_size<decltype(generic_unaries)>::value,\n                            tmpl::next<tmpl::_1>>>([&gen, &x, &generic_unaries](\n                                                       auto index) {\n      auto& tup = std::get<decltype(index)::type::value>(generic_unaries);\n      std::get<0>(tup)(\n          gen, UniformCustomDistribution<DistType>{std::get<Bound>(tup)}, x);\n    });\n  });\n}\n\ntemplate <typename Gen>\nvoid test_floating_point_functions(const gsl::not_null<Gen*> gen) {\n  const Bound generic{{-50.0, 50.0}};\n  const Bound gt_one{{1.0, 100.0}};\n  const Bound positive{{.001, 100.0}};\n  const Bound small{{0.1, 5.0}};\n\n  auto floating_unaries =\n      std::make_tuple(std::make_tuple(TestFuncEvalLog10{}, positive));\n\n  auto just_floating_binaries =\n      std::make_tuple(std::make_tuple(TestFuncEvalPow{}, small));\n\n  auto generic_binaries =\n      std::make_tuple(std::make_tuple(TestFuncEvalDivides{}, gt_one),\n                      std::make_tuple(TestFuncEvalMinus{}, generic),\n                      std::make_tuple(TestFuncEvalMultiplies{}, generic),\n                      std::make_tuple(TestFuncEvalPlus{}, generic),\n                      std::make_tuple(TestFuncEvalDivAssign{}, gt_one),\n                      std::make_tuple(TestFuncEvalMultAssign{}, generic),\n                      std::make_tuple(TestFuncEvalMinusAssign{}, generic),\n                      std::make_tuple(TestFuncEvalPlusAssign{}, generic));\n\n  auto floating_binaries =\n      std::tuple_cat(generic_binaries, just_floating_binaries);\n\n  tmpl::for_each<DoubleSet>([&gen, &floating_unaries,\n                             &floating_binaries](auto x) {\n    using DistType1 =\n        typename tt::get_fundamental_type_t<typename decltype(x)::type>;\n    tmpl::for_each<\n        tmpl::make_sequence<std::integral_constant<size_t, 0>,\n                            std::tuple_size<decltype(floating_unaries)>::value,\n                            tmpl::next<tmpl::_1>>>([&gen, &x,\n                                                    &floating_unaries](\n                                                       auto index) {\n      auto& tup = std::get<decltype(index)::type::value>(floating_unaries);\n      std::get<0>(tup)(\n          gen, UniformCustomDistribution<DistType1>{std::get<Bound>(tup)}, x);\n    });\n    tmpl::for_each<DoubleSet>([&gen, &floating_binaries, &x](auto y) {\n      using DistType2 =\n          typename tt::get_fundamental_type_t<typename decltype(y)::type>;\n      tmpl::for_each<tmpl::make_sequence<\n          std::integral_constant<size_t, 0>,\n          std::tuple_size<decltype(floating_binaries)>::value,\n          tmpl::next<tmpl::_1>>>([&gen, &x, &y,\n                                  &floating_binaries](auto index) {\n        auto& tup = std::get<decltype(index)::type::value>(floating_binaries);\n        std::get<0>(tup)(\n            gen, UniformCustomDistribution<DistType1>{std::get<Bound>(tup)},\n            UniformCustomDistribution<DistType2>{std::get<Bound>(tup)}, x, y);\n      });\n    });\n  });\n}\n\ntemplate <typename Gen>\nvoid test_boolean_functions(const gsl::not_null<Gen*> gen) {\n  auto boolean_binaries = std::make_tuple(TestFuncEvalAnd{}, TestFuncEvalOr{});\n  tuple_fold(boolean_binaries, [&gen](auto binary_test_func) {\n    binary_test_func(gen, UniformCustomDistribution<bool>{},\n                     UniformCustomDistribution<bool>{}, tmpl::type_<bool>{},\n                     tmpl::type_<bool>{});\n  });\n}\n\ntemplate <typename Gen>\nvoid test_real_functions(const gsl::not_null<Gen*> gen) {\n  const Bound generic{{-50.0, 50.0}};\n  const Bound gt_one{{1.0, 100.0}};\n  const Bound positive{{.001, 100.0}};\n  const Bound small{{0.1, 5.0}};\n\n  auto generic_binaries =\n      std::make_tuple(std::make_tuple(TestFuncEvalDivides{}, gt_one),\n                      std::make_tuple(TestFuncEvalMinus{}, generic),\n                      std::make_tuple(TestFuncEvalMultiplies{}, generic),\n                      std::make_tuple(TestFuncEvalPlus{}, generic),\n                      std::make_tuple(TestFuncEvalDivAssign{}, gt_one),\n                      std::make_tuple(TestFuncEvalMultAssign{}, generic),\n                      std::make_tuple(TestFuncEvalMinusAssign{}, generic),\n                      std::make_tuple(TestFuncEvalPlusAssign{}, generic));\n\n  auto just_real_binaries =\n      std::make_tuple(std::make_tuple(TestFuncEvalAtan2{}, generic),\n                      std::make_tuple(TestFuncEvalHypot{}, generic));\n\n  auto same_just_real_binaries =\n      std::make_tuple(std::make_tuple(TestFuncEvalMax{}, generic),\n                      std::make_tuple(TestFuncEvalMin{}, generic));\n\n  auto real_unaries =\n      std::make_tuple(std::make_tuple(TestFuncEvalCbrt{}, positive),\n                      std::make_tuple(TestFuncEvalErf{}, generic),\n                      std::make_tuple(TestFuncEvalExp2{}, small),\n                      std::make_tuple(TestFuncEvalFabs{}, generic),\n                      std::make_tuple(TestFuncEvalInvCbrt{}, gt_one),\n                      std::make_tuple(TestFuncEvalLog2{}, gt_one),\n                      std::make_tuple(TestFuncEvalStepFunction{}, generic));\n\n  auto real_binaries = std::tuple_cat(just_real_binaries, generic_binaries);\n\n  tmpl::for_each<RealTypeList>([&gen, &real_binaries, &same_just_real_binaries,\n                                &real_unaries](auto x) {\n    using DistType1 =\n        typename tt::get_fundamental_type_t<typename decltype(x)::type>;\n    tmpl::for_each<tmpl::make_sequence<\n        std::integral_constant<size_t, 0>,\n        std::tuple_size<decltype(real_unaries)>::value, tmpl::next<tmpl::_1>>>(\n        [&gen, &x, &real_unaries](auto index) {\n          auto& tup = std::get<decltype(index)::type::value>(real_unaries);\n          std::get<0>(tup)(\n              gen, UniformCustomDistribution<DistType1>{std::get<Bound>(tup)},\n              x);\n        });\n\n    tmpl::for_each<tmpl::make_sequence<\n        std::integral_constant<size_t, 0>,\n        std::tuple_size<decltype(same_just_real_binaries)>::value,\n        tmpl::next<tmpl::_1>>>(\n        [&gen, &x, &same_just_real_binaries](auto index) {\n          auto& tup =\n              std::get<decltype(index)::type::value>(same_just_real_binaries);\n          std::get<0>(tup)(\n              gen, UniformCustomDistribution<DistType1>{std::get<Bound>(tup)},\n              UniformCustomDistribution<DistType1>{std::get<Bound>(tup)}, x, x);\n        });\n\n    tmpl::for_each<RealTypeList>([&gen, &real_binaries, &x](auto y) {\n      using DistType2 =\n          typename tt::get_fundamental_type_t<typename decltype(y)::type>;\n      tmpl::for_each<\n          tmpl::make_sequence<std::integral_constant<size_t, 0>,\n                              std::tuple_size<decltype(real_binaries)>::value,\n                              tmpl::next<tmpl::_1>>>([&gen, &x, &y,\n                                                      &real_binaries](\n                                                         auto index) {\n        auto& tup = std::get<decltype(index)::type::value>(real_binaries);\n        std::get<0>(tup)(\n            gen, UniformCustomDistribution<DistType1>(std::get<Bound>(tup)),\n            UniformCustomDistribution<DistType2>(std::get<Bound>(tup)), x, y);\n      });\n    });\n  });\n}\n\nvoid test_vector_plus() {\n  CHECK_ITERABLE_APPROX(funcl::ElementWise<funcl::Abs<>>{}(\n                            std::vector<double>{0.12, -20.87, 3.2}),\n                        (std::vector<double>{0.12, 20.87, 3.2}));\n  CHECK_ITERABLE_APPROX(funcl::ElementWise<funcl::Plus<>>{}(\n                            std::vector<double>{0.12, -20.87, 3.2},\n                            std::vector<double>{-11.04, 7.5, 6.18}),\n                        (std::vector<double>{-10.92, -13.37, 9.38}));\n  CHECK_ITERABLE_APPROX(funcl::ElementWise<funcl::Max<>>{}(\n                            std::vector<double>{0.12, -20.87, 3.2},\n                            std::vector<double>{-11.04, 7.5, 6.18}),\n                        (std::vector<double>{0.12, 7.5, 6.18}));\n  CHECK_ITERABLE_APPROX(funcl::ElementWise<funcl::Min<>>{}(\n                            std::vector<double>{0.12, -20.87, 3.2},\n                            std::vector<double>{-11.04, 7.5, 6.18}),\n                        (std::vector<double>{-11.04, -20.87, 3.2}));\n  CHECK_ITERABLE_APPROX(funcl::ElementWise<funcl::Divides<>>{}(\n                            std::vector<double>{0.12, -20.87, 3.2}, 0.5),\n                        (std::vector<double>{0.24, -41.74, 6.4}));\n}\n\nvoid test_map_merge() {\n  using MapType = std::map<int, std::string>;\n  CHECK(funcl::Merge<>{}(MapType{std::make_pair(0, \"0\")},\n                         MapType{std::make_pair(1, \"1\")}) ==\n        MapType{std::make_pair(0, \"0\"), std::make_pair(1, \"1\")});\n}\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Functional\", \"[Utilities][Unit]\") {\n  MAKE_GENERATOR(generator);\n  test_generic_unaries(make_not_null(&generator));\n  test_floating_point_functions(make_not_null(&generator));\n  test_boolean_functions(make_not_null(&generator));\n  test_real_functions(make_not_null(&generator));\n  test_functional_combinations(make_not_null(&generator));\n  test_assert_equal();\n  test_get_argument();\n  test_vector_plus();\n  test_map_merge();\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH((AssertEqual<>{}(7, 8)),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Values are not equal in funcl::AssertEqual 7 and 8\"));\n  CHECK_THROWS_WITH(\n      (funcl::ElementWise<funcl::Plus<>>{}(std::vector<double>{2.0},\n                                           std::vector<double>{0.4, -19.90})),\n      Catch::Matchers::ContainsSubstring(\"Sizes must be the same but got\"));\n#endif\n}\n\n#undef MAKE_UNARY_TEST\n#undef MAKE_BINARY_TEST\n#undef MAKE_BINARY_OP_TEST\n#undef MAKE_BINARY_INPLACE_TEST\n}  // namespace\n}  // namespace funcl\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_GetOutput.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <type_traits>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.GetOutput\", \"[Utilities][Unit]\") {\n  CHECK(get_output(3) == \"3\");\n  CHECK(get_output(std::string(\"abc\")) == \"abc\");\n  CHECK(get_output(std::add_const_t<NonCopyable>{}) == \"NC\");\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Gsl.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <numeric>\n#include <vector>\n\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/Gsl.hpp\"\n\nnamespace {\ntemplate <typename T>\nvoid func(const gsl::not_null<T*> t) { *t += 2; }\n\nvoid test_make_not_null() {\n  int x = 5;\n  func(make_not_null(&x));\n  CHECK(x == 7);\n}\n\nvoid test_span_stream() {\n  std::vector<double> a(4);\n  std::iota(a.begin(), a.end(), 1.3);\n  gsl::span<const double> span_a(a.data(), a.size());\n  CHECK(get_output(span_a) == \"(1.3,2.3,3.3,4.3)\");\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Gsl\", \"[Unit][Utilities]\") {\n  test_make_not_null();\n  test_span_stream();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_MakeArray.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <initializer_list>\n#include <type_traits>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n\nnamespace {\ntemplate <typename T>\nstruct MyNonCopyable {\n  MyNonCopyable() = default;\n  ~MyNonCopyable() = default;\n  explicit MyNonCopyable(T t) : t_(std::move(t)) {}\n  MyNonCopyable(const MyNonCopyable&) = delete;\n  MyNonCopyable& operator=(const MyNonCopyable&) = delete;\n  MyNonCopyable(MyNonCopyable&&) = default;\n  MyNonCopyable& operator=(MyNonCopyable&&) = default;\n\n  const T& get() const { return t_; }\n\n private:\n  T t_;\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.MakeArray\", \"[Unit][Utilities]\") {\n  constexpr auto same_array = make_array<4>(7.8);\n  CHECK((same_array == std::array<double, 4>{{7.8, 7.8, 7.8, 7.8}}));\n  constexpr auto varying_array = make_array(3.2, 4.3, 5.4, 6.5, 7.8);\n  CHECK((varying_array == std::array<double, 5>{{3.2, 4.3, 5.4, 6.5, 7.8}}));\n\n  auto array_n = make_array<6>(5);\n  static_assert(\n      std::is_same<typename std::decay<decltype(array_n)>::type::value_type,\n                   int>::value,\n      \"Unit Test Failure: Incorrect type from make_array.\");\n  static_assert(array_n.size() == 6,\n                \"Unit Test Failure: Incorrect size from make_array.\");\n  for (const auto& p : array_n) {\n    CHECK(5 == p);\n  }\n\n  auto array_non_copyable = make_array<1>(NonCopyable{});\n  static_assert(array_non_copyable.size() == 1,\n                \"Unit Test Failure: Incorrect array size should be 1 for \"\n                \"move-only types.\");\n\n  auto array_empty = make_array<0>(2);\n  static_assert(array_empty.empty(),\n                \"Unit Test Failure: Incorrect array size for empty array.\");\n\n  auto array_non_copyable_empty = make_array<0>(NonCopyable{});\n  static_assert(\n      array_non_copyable_empty.empty(),\n      \"Unit Test Failure: Incorrect array size for empty array of move-only.\");\n\n  auto my_array = make_array(1, 3, 4, 8, 9);\n  static_assert(my_array.size() == 5,\n                \"Unit Test Failure: Incorrect array size.\");\n  CHECK((my_array[0] == 1 and my_array[1] == 3 and my_array[2] == 4 and\n         my_array[3] == 8 and my_array[4] == 9));\n\n  constexpr const auto array_from_sequence =\n      make_array<int, 3>(std::initializer_list<int>{2, 8, 6});\n  CHECK((std::array<int, 3>{{2, 8, 6}}) == array_from_sequence);\n  constexpr const auto array_from_truncated_sequence =\n      make_array<int, 3>(std::initializer_list<int>{2, 8, 6, 9, 7});\n  CHECK((std::array<int, 3>{{2, 8, 6}}) == array_from_truncated_sequence);\n\n  // Check make_array with a non-copyable type\n  const auto noncopyable_vectors =\n      make_array<3, MyNonCopyable<std::vector<double>>>(\n          std::vector<double>{2.3, 8.6, 9.7});\n  for (size_t i = 0; i < 3; i++) {\n    CHECK(gsl::at(noncopyable_vectors, i).get() ==\n          (std::vector<double>{2.3, 8.6, 9.7}));\n  }\n\n  // Check make_array with a multi-arg constructor\n  const auto vector_array1 = make_array<3, std::vector<int>>(3_st, 9);\n  for (size_t i = 0; i < 3; i++) {\n    CHECK(gsl::at(vector_array1, i) == (std::vector<int>(3, 9)));\n  }\n\n  // Check make_array with an initializer list constructor\n  const auto vector_array2 = make_array<3, std::vector<int>>({3, 9, 12, -10});\n  for (size_t i = 0; i < 3; i++) {\n    CHECK(gsl::at(vector_array2, i) == (std::vector<int>{3, 9, 12, -10}));\n  }\n\n  // Check make_array with an rvalue sequence\n  {\n    std::vector<MyNonCopyable<int>> vector;\n    vector.reserve(3);\n    for (int i = 0; i < 3; ++i) {\n      vector.emplace_back(i);\n    }\n    const auto array = make_array<MyNonCopyable<int>, 3>(std::move(vector));\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(gsl::at(array, i).get() == static_cast<int>(i));\n    }\n  }\n\n  // Check that make_array does not move from an lvalue sequence\n  {\n    std::vector<std::vector<int>> vector(3, std::vector<int>{1, 2, 3});\n    make_array<std::vector<int>, 3>(vector);\n    CHECK(vector.size() == 3);\n    for (size_t i = 0; i < 3; ++i) {\n      CHECK(vector[i] == (std::vector<int>{1, 2, 3}));\n    }\n  }\n\n  // Check that make_array works with non-default-constructible types\n  {\n    struct NonDefaultConstructible {\n      NonDefaultConstructible() = delete;\n      explicit NonDefaultConstructible(int /*unused*/) {}\n    };\n    const NonDefaultConstructible ndc{1};\n    // We just check that these compile, since there's not any\n    // realistic way they could give a wrong answer.\n    make_array<0>(ndc);\n    make_array<1>(ndc);\n    make_array<2>(ndc);\n    make_array(ndc, ndc);\n    make_array(ndc, ndc, ndc);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_MakeSignalingNan.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <complex>\n\n#include \"Utilities/ErrorHandling/FloatingPointExceptions.hpp\"\n#include \"Utilities/MakeSignalingNan.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.MakeSignalingNaN\", \"[Unit][Utilities]\") {\n  const ScopedFpeState disable_fpes(false);\n  const auto double_snan = make_signaling_NaN<double>();\n  CHECK(std::isnan(double_snan));\n  const auto float_snan = make_signaling_NaN<float>();\n  CHECK(std::isnan(float_snan));\n  const auto complex_snan = make_signaling_NaN<std::complex<double>>();\n  CHECK(std::isnan(complex_snan.real()));\n  CHECK(std::isnan(complex_snan.imag()));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_MakeString.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <string>\n\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.MakeString\", \"[Unit][Utilities]\") {\n  // [make_string]\n  std::array<int, 3> arr{{2, 3, 4}};\n  const std::string t = MakeString{} << \"Test\" << 2 << arr << \"Done\";\n  // [make_string]\n  const std::string expected = \"Test2\" + get_output(arr) + \"Done\";\n  CHECK(t == expected);\n  CHECK(get_output(MakeString{} << \"Test\" << 2 << arr << \"Done\") == expected);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_MakeVector.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <type_traits>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/MakeVector.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.MakeVector\", \"[Unit][Utilities]\") {\n  static_assert(\n      std::is_same<\n          typename std::decay<decltype(make_vector(0., 0.))>::type::value_type,\n          double>::value,\n      \"Unit Test Failure: Incorrect type from make_vector.\");\n\n  // [make_vector_example]\n  auto vector = make_vector(3.2, 4.3, 5.4, 6.5, 7.8);\n  CHECK(vector == (std::vector<double>{{3.2, 4.3, 5.4, 6.5, 7.8}}));\n\n  auto vector_non_copyable = make_vector(NonCopyable{}, NonCopyable{});\n  CHECK(vector_non_copyable.size() == 2);\n\n  auto vector_empty = make_vector<int>();\n  CHECK(vector_empty.empty());\n\n  auto vector_size_one = make_vector(3);\n  CHECK(vector_size_one == std::vector<int>{3});\n\n  auto vector_explicit_type = make_vector<double>(1, 2, 3);\n  CHECK(vector_explicit_type == (std::vector<double>{1.,2.,3.}));\n  // [make_vector_example]\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_MakeWithValue.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <functional>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeArray.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n\nnamespace {\nstruct Makeable {\n  size_t size = 0;\n  double value = 0.0;\n};\n\nbool operator==(const Makeable& a, const Makeable& b) {\n  return a.size == b.size and a.value == b.value;\n}\n}  // namespace\n\nnamespace MakeWithValueImpls {\ntemplate <>\nstruct MakeWithSize<Makeable> {\n  static Makeable apply(const size_t size, const double value) {\n    return Makeable{size, value};\n  }\n};\n\ntemplate <>\nstruct NumberOfPoints<Makeable> {\n  static size_t apply(const Makeable& input) { return input.size; }\n};\n}  // namespace MakeWithValueImpls\n\nnamespace {\nnamespace Tags {\nstruct Makeable {\n  using type = ::Makeable;\n};\n\nstruct Makeable2 {\n  using type = ::Makeable;\n};\n\nstruct Double {\n  using type = double;\n};\n}  // namespace Tags\n\ntemplate <typename R, typename T, typename ValueType>\nvoid check_make_with_value(const R& expected, const T& input,\n                           const ValueType value) {\n  const auto computed = make_with_value<R>(input, value);\n  CHECK(expected == computed);\n}\n\nvoid test_make_tagged_tuple() {\n  check_make_with_value(tuples::TaggedTuple<Tags::Double>(-5.7), 0.0, -5.7);\n  for (size_t n_pts = 1; n_pts < 4; ++n_pts) {\n    check_make_with_value(\n        tuples::TaggedTuple<Tags::Makeable>(Makeable{n_pts, -5.7}),\n        Makeable{n_pts, 0.0}, -5.7);\n\n    check_make_with_value(\n        tuples::TaggedTuple<Tags::Makeable, Tags::Makeable2, Tags::Double>(\n            Makeable{n_pts, 3.8}, Makeable{n_pts, 3.8}, 3.8),\n        Makeable{n_pts, 0.0}, 3.8);\n    check_make_with_value(Makeable{n_pts, 3.8},\n                          tuples::TaggedTuple<Tags::Makeable, Tags::Makeable2>(\n                              Makeable{n_pts, 0.0}, Makeable{n_pts, 1.0}),\n                          3.8);\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(make_with_value<Makeable>(\n                        tuples::TaggedTuple<Tags::Makeable, Tags::Makeable2>(\n                            Makeable{1, 0.0}, Makeable{2, 0.0}),\n                        0.0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Inconsistent number of points in tuple entries\"));\n#endif  // SPECTRE_DEBUG\n}\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.MakeWithValue\",\n                  \"[DataStructures][Unit]\") {\n  check_make_with_value(Makeable{2, 1.2}, 2_st, 1.2);\n  check_make_with_value(Makeable{2, 1.2}, Makeable{2, 3.4}, 1.2);\n\n  check_make_with_value(8.3, 1.3, 8.3);\n  check_make_with_value(std::complex<double>(8.3, 2.5), 1.3,\n                        std::complex<double>(8.3, 2.5));\n  check_make_with_value(8.3, Makeable{8, 2.3}, 8.3);\n  check_make_with_value(std::complex<double>(8.3, 2.5), Makeable{8, 2.3},\n                        std::complex<double>(8.3, 2.5));\n\n  check_make_with_value(make_array<4>(8.3), 1.3, 8.3);\n  check_make_with_value(make_array<3>(Makeable{5, 8.3}), Makeable{5, 4.5}, 8.3);\n  check_make_with_value(1.3, make_array<4>(8.3), 1.3);\n  check_make_with_value(make_array<3>(Makeable{5, 8.3}),\n                        make_array<4>(Makeable{5, 4.5}), 8.3);\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(make_with_value<Makeable>(\n                        make_array(Makeable{1, 0.0}, Makeable{2, 0.0}), 0.0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Inconsistent number of points in array entries\"));\n#endif  // SPECTRE_DEBUG\n\n  // std::vector is only usable as input because the number of entries\n  // in a result vector cannot be inferred.\n  check_make_with_value(1.3, std::vector{8.3, 4.5}, 1.3);\n  check_make_with_value(Makeable{5, 8.3},\n                        std::vector{Makeable{5, 4.5}, Makeable{5, 1.2}}, 8.3);\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(make_with_value<Makeable>(\n                        std::vector{Makeable{1, 0.0}, Makeable{2, 0.0}}, 0.0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Inconsistent number of points in vector entries\"));\n  CHECK_THROWS_WITH(make_with_value<Makeable>(std::vector<Makeable>{}, 0.0),\n                    Catch::Matchers::ContainsSubstring(\n                        \"Cannot get number of points from empty std::vector\"));\n#endif  // SPECTRE_DEBUG\n\n  // std::reference_wrapper is only usable as input.\n  {\n    Makeable used_for_size{3, 1.1};\n    check_make_with_value(1.3, std::ref(used_for_size), 1.3);\n    check_make_with_value(Makeable{3, 8.3}, std::ref(used_for_size), 8.3);\n    check_make_with_value(1.3, std::cref(used_for_size), 1.3);\n    check_make_with_value(Makeable{3, 8.3}, std::cref(used_for_size), 8.3);\n  }\n\n  test_make_tagged_tuple();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Math.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cmath>\n#include <vector>\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/Math.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\ntemplate <size_t N>\nvoid test_smoothstep() {\n  CAPTURE(N);\n  CHECK(smoothstep<N>(0., 1., 0.) == approx(0.));\n  CHECK(smoothstep<N>(0., 1., 0.5) == approx(0.5));\n  CHECK(smoothstep<N>(0., 1., 1.) == approx(1.));\n  CHECK_ITERABLE_APPROX(\n      smoothstep<N>(0., 1., DataVector({-1., 0., 0.5, 1., 2.})),\n      DataVector({0., 0., 0.5, 1., 1.}));\n  CHECK_ITERABLE_APPROX(\n      smoothstep<N>(-1., 1., DataVector({-2., -1., 0., 1., 2.})),\n      DataVector({0., 0., 0.5, 1., 1.}));\n  if constexpr (N > 0) {\n    INFO(\"Test smoothstep_deriv\");\n    CHECK(smoothstep_deriv<N>(0., 2., 0.) == approx(0.));\n    CHECK(smoothstep_deriv<N>(0., 2., 2.) == approx(0.));\n    const Approx custom_approx = Approx::custom().epsilon(1e-6).scale(1.0);\n    for (const double x : {-1., 0., 1., 2., 3.}) {\n      CAPTURE(x);\n      CHECK(numerical_derivative(\n                [](const std::array<double, 1>& x0) {\n                  return smoothstep<N>(0., 2., x0[0]);\n                },\n                std::array<double, 1>{{x}}, 0,\n                1e-6) == custom_approx(smoothstep_deriv<N>(0., 2., x)));\n    }\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Math\", \"[Unit][Utilities]\") {\n  {\n    INFO(\"Test number_of_digits\");\n    CHECK(2 == number_of_digits(10));\n    CHECK(1 == number_of_digits(0));\n    CHECK(1 == number_of_digits(-1));\n    CHECK(1 == number_of_digits(9));\n    CHECK(2 == number_of_digits(-99));\n  }\n\n  {\n    INFO(\"Test evaluate_polynomial\");\n    const std::vector<double> poly_coeffs{1., 2.5, 0.3, 1.5};\n    CHECK_ITERABLE_APPROX(evaluate_polynomial(poly_coeffs, 0.5), 2.5125);\n    CHECK_ITERABLE_APPROX(\n        evaluate_polynomial(std::array<double, 4>{1., 2.5, 0.3, 1.5}, 0.5),\n        2.5125);\n    CHECK_ITERABLE_APPROX(\n        evaluate_polynomial(poly_coeffs,\n                            DataVector({-0.5, -0.1, 0., 0.8, 1., 12.})),\n        DataVector({-0.3625, 0.7515, 1., 3.96, 5.3, 2666.2}));\n    const std::vector<DataVector> poly_variable_coeffs{DataVector{1., 0., 2.},\n                                                       DataVector{0., 2., 1.}};\n    CHECK_ITERABLE_APPROX(\n        evaluate_polynomial(poly_variable_coeffs, DataVector({0., 0.5, 1.})),\n        DataVector({1., 1., 3.}));\n  }\n\n  {\n    INFO(\"Test smoothstep\");\n    test_smoothstep<0>();\n    test_smoothstep<1>();\n    test_smoothstep<2>();\n    test_smoothstep<3>();\n    test_smoothstep<4>();\n    test_smoothstep<5>();\n    test_smoothstep<6>();\n    // Test a case that failed in Release builds when a static_cast was missing\n    CHECK_ITERABLE_APPROX(\n        smoothstep<2>(0., 2.,\n                      DataVector({-2., -1.5, -1., -0.5, -0.25, 0., 0.25, 0.5,\n                                  1., 1.5, 2.})),\n        DataVector({0., 0., 0., 0., 0., 0., 1.605224609375e-02, 1.03515625e-01,\n                    0.5, 8.96484375e-01, 1.}));\n  }\n  {\n    INFO(\"Test smoothstep with equal edges\");\n    const double edge = 1.5;\n    CHECK(smoothstep<2>(edge, edge, 1.0) == approx(0.0));\n    CHECK(smoothstep<2>(edge, edge, 1.5) == approx(1.0));\n    CHECK(smoothstep<2>(edge, edge, 2.0) == approx(1.0));\n\n    INFO(\"Test smoothstep_deriv with equal edges\");\n    CHECK(smoothstep_deriv<2>(edge, edge, 1.0) == approx(0.0));\n    CHECK(smoothstep_deriv<2>(edge, edge, 1.5) == approx(0.0));\n    CHECK(smoothstep_deriv<2>(edge, edge, 2.0) == approx(0.0));\n\n    CHECK_ITERABLE_APPROX(\n        smoothstep_deriv<2>(edge, edge, DataVector({1.0, 1.5, 2.0})),\n        DataVector({0.0, 0.0, 0.0}));\n  }\n  {\n    INFO(\"Test inverse roots and step_function\");\n    CHECK(step_function(1.0) == 1.0);\n    CHECK(step_function(0.5) == 1.0);\n    CHECK(step_function(-10) == 0);\n    CHECK(step_function(0.0) == 1.0);\n    CHECK(step_function(0) == 1);\n    CHECK(invsqrt(4.0) == 0.5);\n    CHECK(invsqrt(10) == 1.0 / sqrt(10));\n    CHECK(approx(invcbrt(27.0)) == (1 / 3.0));\n    CHECK(approx(invcbrt(1.0 / 64.0)) == 4.0);\n  }\n\n  {\n    INFO(\"Test sign function\");\n    CHECK(sgn(2) == 1);\n    CHECK(sgn(0) == 0);\n    CHECK(sgn(-2) == -1);\n    static_assert(std::is_same_v<decltype(sgn(-2)), int>,\n                  \"Failed testing type of sgn\");\n\n    CHECK(sgn(2.14) == 1.0);\n    CHECK(sgn(0.0) == 0.0);\n    CHECK(sgn(-3.87) == -1.0);\n    static_assert(std::is_same_v<decltype(sgn(2.14)), double>,\n                  \"Failed testing type of sgn\");\n\n    CHECK(sgn(static_cast<unsigned>(2)) == 1);\n    CHECK(sgn(static_cast<unsigned>(0)) == 0);\n    CHECK(sgn(static_cast<unsigned>(-1)) == 1);\n    static_assert(\n        std::is_same_v<decltype(sgn(static_cast<unsigned>(2))), unsigned>,\n        \"Failed testing type of sgn\");\n  }\n\n  {\n    INFO(\"Test integer pow\");\n\n    CHECK(integer_pow(1., 5) == approx(pow(1., 5.)));\n    CHECK(integer_pow(11., 12) == approx(pow(11., 12)));\n    CHECK(integer_pow(29375925., 0) == 1.);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_MemoryHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"tests/Unit/TestingFramework.hpp\"\n\n#include <vector>\n\n#include \"Utilities/MemoryHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.allocation_failure\", \"[Unit][Time]\") {\n  CHECK_THROWS_WITH(\n      (std::vector<int>(1000000000000000)),\n      Catch::Matchers::ContainsSubstring(\"Failed to allocate memory\"));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Numeric.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Utilities/Array.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/Numeric.hpp\"\n\nnamespace {\nconstexpr bool check_iota() {\n  const size_t size = 6;\n  cpp20::array<size_t, size> a{};\n  cpp2b::iota(a.begin(), a.end(), size_t(1));\n  bool check{true};\n  for (size_t i = 0; i < size; ++i) {\n    check = check and a[i] == i + 1;\n  }\n\n  const cpp20::array<size_t, 4> inline_iota =\n      alg::iota(cpp20::array<size_t, 4>{{}}, 7_st);\n  for (size_t i = 0; i < inline_iota.size(); ++i) {\n    check = check and inline_iota[i] == i + 7;\n  }\n  return check;\n}\n\nconstexpr bool check_accumulate() {\n  cpp20::array<size_t, 6> a{};\n  cpp2b::iota(a.begin(), a.end(), size_t(1));\n  size_t sum = cpp2b::accumulate(a.begin(), a.end(), size_t(23));\n  return sum == 44;\n}\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Numeric\", \"[Unit][Utilities]\") {\n  // Check the functions at runtime\n  CHECK(check_iota());\n  CHECK(check_accumulate());\n\n  // Check the functions at compile time\n  static_assert(check_iota(), \"Failed test Unit.Utilities.Numeric\");\n  static_assert(check_accumulate(), \"Failed test Unit.Utilities.Numeric\");\n\n  // Check STL wrappers\n  CHECK(alg::accumulate(std::array<int, 3>{{1, -3, 7}}, 4) == 9);\n  CHECK(alg::accumulate(std::array<int, 3>{{1, -3, 7}}, 4,\n                        std::multiplies<>{}) == -84);\n  CHECK(alg::accumulate(std::array<int, 3>{{1, -3, 7}}, 4,\n                        [](const int state, const int element) {\n                          return state * element;\n                        }) == -84);\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_OptionalHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <optional>\n\n#include \"Utilities/OptionalHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.OptionalHelpers\", \"[Unit][Utilities]\") {\n  const int t = 10;\n  std::optional<int> t_opt{12};\n  std::optional<int> t_no_opt{std::nullopt};\n\n  CHECK(has_value(t));\n  CHECK(has_value(t_opt));\n  CHECK(not has_value(t_no_opt));\n\n  CHECK(value(t) == 10);\n  int t_mutable = 0;\n  value(t_mutable) = value(t) + 10;\n  CHECK(value(t_mutable) == 20);\n\n  CHECK(value(t_opt) == 12);\n  value(t_opt) = 15;\n  CHECK(value(t_opt) == 15);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Overloader.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include \"Utilities/Overloader.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\n// [overloader_example]\nstruct my_type1 {\n  int func(int a) { return 2 * a; }\n};\n\nstruct my_type2 {};\n\ntemplate <class T, class = std::void_t<>>\nstruct has_func : std::false_type {};\n\ntemplate <class T>\nstruct has_func<\n    T, std::void_t<decltype(std::declval<T>().func(std::declval<int>()))>>\n    : std::true_type {};\n\ntemplate <class T>\nvoid func(T t) {\n  auto my_lambdas =\n      Overloader{[](auto& /*f*/, std::integral_constant<bool, false>,\n                    std::integral_constant<bool, false>) { return 0; },\n                 [](auto& /*f*/, std::integral_constant<bool, true>,\n                    std::integral_constant<bool, false>) { return 1; },\n                 [](auto& /*f*/, std::integral_constant<bool, false>,\n                    std::integral_constant<bool, true>) { return 2; },\n                 [](auto& /*f*/, std::integral_constant<bool, true>,\n                    std::integral_constant<bool, true>) { return 3; }};\n  CHECK(static_cast<int>(has_func<T>::value) ==\n        (my_lambdas(t, std::integral_constant<bool, has_func<T>::value>{},\n                    std::integral_constant<bool, false>{})));\n  CHECK(2 + static_cast<int>(has_func<T>::value) ==\n        (my_lambdas(t, std::integral_constant<bool, has_func<T>::value>{},\n                    std::integral_constant<bool, true>{})));\n}\n\nvoid caller() {\n  func(my_type1{});\n  func(my_type2{});\n}\n// [overloader_example]\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Overloader\", \"[Unit][Utilities]\") {\n  auto my_lambdas =\n      Overloader{[](std::integral_constant<int, 0>) { return 0; },\n                 [](std::integral_constant<int, 1>) { return 1; },\n                 [](std::integral_constant<int, 2>) { return 2; },\n                 [](std::integral_constant<int, 3>) { return 3; },\n                 [](std::integral_constant<int, 4>) { return 4; },\n                 [](std::integral_constant<int, 5>) { return 5; },\n                 [](std::integral_constant<int, 6>) { return 6; },\n                 [](std::integral_constant<int, 7>) { return 7; },\n                 [](std::integral_constant<int, 8>) { return 8; },\n                 [](std::integral_constant<int, 9>) { return 9; },\n                 [](std::integral_constant<int, 10>) { return 10; }};\n  CHECK((0 == my_lambdas(std::integral_constant<int, 0>{})));\n  CHECK((1 == my_lambdas(std::integral_constant<int, 1>{})));\n  CHECK((2 == my_lambdas(std::integral_constant<int, 2>{})));\n  CHECK((3 == my_lambdas(std::integral_constant<int, 3>{})));\n  CHECK((4 == my_lambdas(std::integral_constant<int, 4>{})));\n  CHECK((5 == my_lambdas(std::integral_constant<int, 5>{})));\n  CHECK((6 == my_lambdas(std::integral_constant<int, 6>{})));\n  CHECK((7 == my_lambdas(std::integral_constant<int, 7>{})));\n  CHECK((8 == my_lambdas(std::integral_constant<int, 8>{})));\n  CHECK((9 == my_lambdas(std::integral_constant<int, 9>{})));\n  CHECK((10 == my_lambdas(std::integral_constant<int, 10>{})));\n\n  caller();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_ParallelInfo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Utilities/System/ParallelInfo.hpp\"\n\nnamespace sys {\nnamespace {\n\nvoid test_time(const double seconds, const std::string& expected) {\n  const std::string result = sys::pretty_wall_time(seconds);\n  CHECK(result == expected);\n}\n\nvoid test() {\n  test_time(43.0, \"00:00:43\");\n  test_time(43.9, \"00:00:43\");\n  test_time(2589.3, \"00:43:09\");\n  test_time(10001.5, \"02:46:41\");\n  test_time(123456.7, \"01-10:17:36\");\n}\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.ParallelInfo\", \"[Unit][Utilities]\") {\n  test();\n}\n}  // namespace\n}  // namespace sys\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_PrettyType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <deque>\n#include <forward_list>\n#include <istream>\n#include <list>\n#include <map>\n#include <memory>\n#include <queue>\n#include <set>\n#include <stack>\n#include <string>\n#include <typeinfo>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Utilities/PrettyType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.PrettyType.Fundamental\",\n                  \"[Utilities][Unit]\") {\n  CHECK(\"char\" == pretty_type::get_name<char>());\n  CHECK(\"signed char\" == pretty_type::get_name<signed char>());\n  CHECK(\"unsigned char\" == pretty_type::get_name<unsigned char>());\n  CHECK(\"wchar_t\" == pretty_type::get_name<wchar_t>());\n  CHECK(\"char16_t\" == pretty_type::get_name<char16_t>());\n  CHECK(\"char32_t\" == pretty_type::get_name<char32_t>());\n  CHECK(\"int\" == pretty_type::get_name<int>());\n  CHECK(\"unsigned int\" == pretty_type::get_name<unsigned int>());\n  CHECK(\"long\" == pretty_type::get_name<long>());\n  CHECK(\"unsigned long\" == pretty_type::get_name<unsigned long>());\n  CHECK(\"long long\" == pretty_type::get_name<long long>());\n  CHECK(\"unsigned long long\" == pretty_type::get_name<unsigned long long>());\n  CHECK(\"short\" == pretty_type::get_name<short>());\n  CHECK(\"unsigned short\" == pretty_type::get_name<unsigned short>());\n  CHECK(\"float\" == pretty_type::get_name<float>());\n  CHECK(\"double\" == pretty_type::get_name<double>());\n  CHECK(\"long double\" == pretty_type::get_name<long double>());\n  CHECK(\"bool\" == pretty_type::get_name<bool>());\n\n  // References and pointers\n  CHECK(\"double&\" == pretty_type::get_name<double&>());\n  CHECK(\"double const&\" == pretty_type::get_name<const double&>());\n  CHECK(\"double volatile&\" == pretty_type::get_name<volatile double&>());\n  CHECK(\"double const volatile&\" ==\n        pretty_type::get_name<const volatile double&>());\n  CHECK(\"double*\" == pretty_type::get_name<double*>());\n  CHECK(\"double* const\" == pretty_type::get_name<double* const>());\n  CHECK(\"double* volatile\" == pretty_type::get_name<double* volatile>());\n  CHECK(\"double* const volatile\" ==\n        pretty_type::get_name<double* volatile const>());\n  CHECK(\"double const* const\" == pretty_type::get_name<const double* const>());\n  CHECK(\"double volatile* volatile\" ==\n        pretty_type::get_name<volatile double* volatile>());\n  CHECK(\"double volatile* const\" ==\n        pretty_type::get_name<volatile double* const>());\n  CHECK(\"double const* volatile\" ==\n        pretty_type::get_name<const double* volatile>());\n  CHECK(\"double volatile* const volatile\" ==\n        pretty_type::get_name<volatile double* const volatile>());\n  CHECK(\"double const volatile* volatile\" ==\n        pretty_type::get_name<const volatile double* volatile>());\n  CHECK(\"double const volatile* const volatile\" ==\n        pretty_type::get_name<const volatile double* const volatile>());\n  CHECK(\"double const*\" == pretty_type::get_name<const double*>());\n  CHECK(\"double volatile*\" == pretty_type::get_name<volatile double*>());\n  CHECK(\"double const volatile*\" ==\n        pretty_type::get_name<const volatile double*>());\n\n  // Test get_runtime_type_name\n  CHECK(\"char\" == pretty_type::get_runtime_type_name('a'));\n}\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.PrettyType.Stl\", \"[Utilities][Unit]\") {\n  CHECK(\"std::string\" == pretty_type::get_name<std::string>());\n  CHECK(\"std::array<double, 4>\" ==\n        (pretty_type::get_name<std::array<double, 4>>()));\n  CHECK(\"std::vector<double>\" == pretty_type::get_name<std::vector<double>>());\n  CHECK(\"std::deque<double>\" == pretty_type::get_name<std::deque<double>>());\n  CHECK(\"std::forward_list<double>\" ==\n        pretty_type::get_name<std::forward_list<double>>());\n  CHECK(\"std::list<double>\" == pretty_type::get_name<std::list<double>>());\n  CHECK(\"std::map<std::string, double>\" ==\n        (pretty_type::get_name<std::map<std::string, double>>()));\n  CHECK(\"std::set<std::string>\" ==\n        (pretty_type::get_name<std::set<std::string>>()));\n  CHECK(\"std::multiset<std::string>\" ==\n        (pretty_type::get_name<std::multiset<std::string>>()));\n  CHECK(\"std::multimap<std::string, double>\" ==\n        (pretty_type::get_name<std::multimap<std::string, double>>()));\n  CHECK(\"std::unordered_map<std::string, double>\" ==\n        (pretty_type::get_name<std::unordered_map<std::string, double>>()));\n  CHECK(\n      \"std::unordered_multimap<std::string, double>\" ==\n      (pretty_type::get_name<std::unordered_multimap<std::string, double>>()));\n  CHECK(\"std::unordered_set<std::string>\" ==\n        (pretty_type::get_name<std::unordered_set<std::string>>()));\n  CHECK(\"std::unordered_multiset<std::string>\" ==\n        (pretty_type::get_name<std::unordered_multiset<std::string>>()));\n\n  CHECK(\"std::priority_queue<double, std::vector<double>>\" ==\n        pretty_type::get_name<std::priority_queue<double>>());\n  CHECK(\"std::queue<double, std::deque<double>>\" ==\n        pretty_type::get_name<std::queue<double>>());\n  CHECK(\"std::stack<double, std::deque<double>>\" ==\n        pretty_type::get_name<std::stack<double>>());\n\n  CHECK(\"std::unique_ptr<double>\" ==\n        pretty_type::get_name<std::unique_ptr<double>>());\n  CHECK(\"std::shared_ptr<double>\" ==\n        pretty_type::get_name<std::shared_ptr<double>>());\n  CHECK(\"std::weak_ptr<double>\" ==\n        pretty_type::get_name<std::weak_ptr<double>>());\n}\n\n// NOTE: Do not put these in a namespace, including an anonymous\n// namespace.  These are for testing demangling things that are not in\n// a namespace.\nstruct Test_PrettyType_struct {};\ntemplate <typename>\nstruct Test_PrettyType_templated_struct {};\nenum class Test_PrettyType_Enum { Zero, One, Two };\n\nnamespace Test_PrettyType_namespace {\nstruct NamedNamespace {};\n}  // namespace Test_PrettyType_namespace\n\nnamespace {\nstruct Type1Containing2Digits3 {};\n\nstruct TestType {};\n\ntemplate <typename>\nstruct Template {};\n\ntemplate <typename, typename>\nstruct Template2 {};\n\ntemplate <typename...>\nstruct Pack {\n  struct Inner {};\n};\n\ntemplate <typename, typename...>\nstruct Pack2 {};\n\nstruct Outer {\n  struct Inner {};\n\n  template <typename>\n  struct InnerTemplate {};\n};\n\ntemplate <typename>\nstruct OuterTemplate {\n  struct Inner {};\n\n  template <typename>\n  struct InnerTemplate {};\n};\n\ntemplate <int>\nstruct NonType {};\n\ntemplate <int, int>\nstruct NonTypeNonType {};\n\ntemplate <typename, int>\nstruct TypeNonType {};\n\ntemplate <int, typename>\nstruct NonTypeType {};\n\ntemplate <std::nullptr_t>\nstruct NullptrTemplate {};\n\nenum class LocalEnum { Zero, One, Two, Negative = -1 };\n\ntemplate <Test_PrettyType_Enum>\nstruct GlobalEnumTemplate {};\n\ntemplate <LocalEnum>\nstruct LocalEnumTemplate {};\n\ntemplate <typename>\nstruct TemplateWithName {\n  static std::string name() { return \"UniqueTemplateWithName\"; }\n};\n\nstruct NonTemplateWithName {\n  static std::string name() { return \"UniqueNonTemplateWithName\"; }\n};\n\nstruct ShortName {\n  template <typename TestType>\n  static std::string name() {\n    return pretty_type::short_name<TestType>();\n  }\n};\n\nstruct Name {\n  template <typename TestType>\n  static std::string name() {\n    if constexpr (std::is_constructible_v<TestType>) {\n      CHECK(pretty_type::name<TestType>() == pretty_type::name(TestType{}));\n    }\n    return pretty_type::name<TestType>();\n  }\n};\n\ntemplate <typename NameFunc>\nvoid test_name_func() {\n  // Fundamentals\n  CHECK(NameFunc::template name<bool>() == \"bool\");\n  CHECK(NameFunc::template name<char>() == \"char\");\n  CHECK(NameFunc::template name<signed char>() == \"signed char\");\n  CHECK(NameFunc::template name<unsigned char>() == \"unsigned char\");\n  CHECK(NameFunc::template name<short>() == \"short\");\n  CHECK(NameFunc::template name<unsigned short>() == \"unsigned short\");\n  CHECK(NameFunc::template name<int>() == \"int\");\n  CHECK(NameFunc::template name<unsigned int>() == \"unsigned int\");\n  CHECK(NameFunc::template name<long>() == \"long\");\n  CHECK(NameFunc::template name<unsigned long>() == \"unsigned long\");\n  CHECK(NameFunc::template name<long long>() == \"long long\");\n  CHECK(NameFunc::template name<unsigned long long>() == \"unsigned long long\");\n  CHECK(NameFunc::template name<void>() == \"void\");\n  CHECK(NameFunc::template name<float>() == \"float\");\n  CHECK(NameFunc::template name<double>() == \"double\");\n  CHECK(NameFunc::template name<long double>() == \"long double\");\n\n  // Standard library\n  // Untemplated\n  CHECK(NameFunc::template name<std::type_info>() == \"type_info\");\n  // Templated\n  CHECK(NameFunc::template name<std::vector<int>>() == \"vector\");\n  // (Probably) special cased in mangling\n  CHECK(NameFunc::template name<std::ostream>() == \"ostream\");\n  // Possibly special cased in mangling and a case we particularly care about\n  CHECK(NameFunc::template name<std::string>() == \"string\");\n\n  // Types and templates with no namespaces\n  CHECK(NameFunc::template name<Test_PrettyType_struct>() ==\n        \"Test_PrettyType_struct\");\n  CHECK(NameFunc::template name<Test_PrettyType_templated_struct<int>>() ==\n        \"Test_PrettyType_templated_struct\");\n  CHECK(NameFunc::template name<\n            Test_PrettyType_templated_struct<Test_PrettyType_struct>>() ==\n        \"Test_PrettyType_templated_struct\");\n  CHECK(NameFunc::template name<Test_PrettyType_templated_struct<\n            Test_PrettyType_templated_struct<int>>>() ==\n        \"Test_PrettyType_templated_struct\");\n  CHECK(NameFunc::template name<Test_PrettyType_templated_struct<\n            Test_PrettyType_templated_struct<Test_PrettyType_struct>>>() ==\n        \"Test_PrettyType_templated_struct\");\n\n  // Named namespaces\n  CHECK(NameFunc::template name<Test_PrettyType_namespace::NamedNamespace>() ==\n        \"NamedNamespace\");\n\n  // Anonymous namespaces\n  CHECK(NameFunc::template name<TestType>() == \"TestType\");\n\n  // Digits (special meaning in mangled names)\n  CHECK(NameFunc::template name<Type1Containing2Digits3>() ==\n        \"Type1Containing2Digits3\");\n\n  using Global = Test_PrettyType_struct;\n  using Std = std::type_info;\n  using Special = std::ostream;\n  using Templated = Test_PrettyType_templated_struct<Test_PrettyType_struct>;\n\n  // const\n  CHECK(NameFunc::template name<const int>() == \"int\");\n  CHECK(NameFunc::template name<const Global>() == \"Test_PrettyType_struct\");\n  CHECK(NameFunc::template name<const TestType>() == \"TestType\");\n  CHECK(NameFunc::template name<const Std>() == \"type_info\");\n  CHECK(NameFunc::template name<const Special>() == \"ostream\");\n  CHECK(NameFunc::template name<const Templated>() ==\n        \"Test_PrettyType_templated_struct\");\n\n  // Template stuff\n  CHECK(NameFunc::template name<Template<int>>() == \"Template\");\n  CHECK(NameFunc::template name<Template<Global>>() == \"Template\");\n  CHECK(NameFunc::template name<Template<TestType>>() == \"Template\");\n  CHECK(NameFunc::template name<Template<Std>>() == \"Template\");\n  CHECK(NameFunc::template name<Template<Special>>() == \"Template\");\n  CHECK(NameFunc::template name<Template<Templated>>() == \"Template\");\n  CHECK(NameFunc::template name<Template<const Global>>() == \"Template\");\n  CHECK(NameFunc::template name<Template<const TestType>>() == \"Template\");\n\n  CHECK(NameFunc::template name<Template2<int, int>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<int, Global>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<int, TestType>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<int, Std>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<int, Special>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Global, int>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Global, Global>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Global, TestType>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Global, Std>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Global, Special>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<TestType, int>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<TestType, Global>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<TestType, TestType>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<TestType, Std>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<TestType, Special>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Std, int>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Std, Global>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Std, TestType>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Std, Std>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Std, Special>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Special, int>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Special, Global>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Special, TestType>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Special, Std>>() == \"Template2\");\n  CHECK(NameFunc::template name<Template2<Special, Special>>() == \"Template2\");\n\n  CHECK(NameFunc::template name<Template2<Global, const Global>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<Global, const TestType>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<const Global, Global>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<const TestType, Global>>() ==\n        \"Template2\");\n\n  CHECK(NameFunc::template name<Template<Template<int>>>() == \"Template\");\n  CHECK(NameFunc::template name<Template2<Template<int>, Template<double>>>() ==\n        \"Template2\");\n\n  CHECK(NameFunc::template name<Pack<>>() == \"Pack\");\n  CHECK(NameFunc::template name<Pack<int>>() == \"Pack\");\n  CHECK(NameFunc::template name<Pack<Global>>() == \"Pack\");\n  CHECK(NameFunc::template name<Pack<TestType>>() == \"Pack\");\n  CHECK(NameFunc::template name<Pack<Std>>() == \"Pack\");\n  CHECK(NameFunc::template name<Pack<Special>>() == \"Pack\");\n  CHECK(NameFunc::template name<Pack<Templated>>() == \"Pack\");\n\n  CHECK(NameFunc::template name<Pack2<int>>() == \"Pack2\");\n  CHECK(NameFunc::template name<Pack2<Global>>() == \"Pack2\");\n  CHECK(NameFunc::template name<Pack2<TestType>>() == \"Pack2\");\n  CHECK(NameFunc::template name<Pack2<Std>>() == \"Pack2\");\n  CHECK(NameFunc::template name<Pack2<Special>>() == \"Pack2\");\n  CHECK(NameFunc::template name<Pack2<Templated>>() == \"Pack2\");\n  CHECK(NameFunc::template name<Pack2<TestType, TestType>>() == \"Pack2\");\n  CHECK(NameFunc::template name<Pack2<Special, TestType>>() == \"Pack2\");\n  CHECK(NameFunc::template name<Pack2<TestType, Special>>() == \"Pack2\");\n\n  // Nested types\n  CHECK(NameFunc::template name<Outer::Inner>() == \"Inner\");\n  CHECK(NameFunc::template name<Outer::InnerTemplate<int>>() ==\n        \"InnerTemplate\");\n  CHECK(NameFunc::template name<Outer::InnerTemplate<Global>>() ==\n        \"InnerTemplate\");\n  CHECK(NameFunc::template name<Outer::InnerTemplate<TestType>>() ==\n        \"InnerTemplate\");\n  CHECK(NameFunc::template name<Outer::InnerTemplate<Special>>() ==\n        \"InnerTemplate\");\n  CHECK(NameFunc::template name<OuterTemplate<int>::Inner>() == \"Inner\");\n  CHECK(NameFunc::template name<OuterTemplate<Global>::Inner>() == \"Inner\");\n  CHECK(NameFunc::template name<OuterTemplate<TestType>::Inner>() == \"Inner\");\n  CHECK(NameFunc::template name<OuterTemplate<Special>::Inner>() == \"Inner\");\n\n  CHECK(NameFunc::template name<Pack<>::Inner>() == \"Inner\");\n  CHECK(NameFunc::template name<Pack<TestType>::Inner>() == \"Inner\");\n\n  // Non-type template parameters\n  CHECK(NameFunc::template name<NonType<3>>() == \"NonType\");\n  CHECK(NameFunc::template name<NonType<0>>() == \"NonType\");\n  CHECK(NameFunc::template name<NonType<-3>>() == \"NonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<3, 3>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<3, 0>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<3, -3>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<0, 3>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<0, 0>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<0, -3>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<-3, 3>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<-3, 0>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<NonTypeNonType<-3, -3>>() == \"NonTypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<int, 3>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<Global, 3>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<TestType, 3>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<Special, 3>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<int, 0>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<Global, 0>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<TestType, 0>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<Special, 0>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<int, -3>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<Global, -3>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<TestType, -3>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<TypeNonType<Special, -3>>() == \"TypeNonType\");\n  CHECK(NameFunc::template name<NonTypeType<3, int>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<3, Global>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<3, TestType>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<3, Special>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<0, int>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<0, Global>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<0, TestType>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<0, Special>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<-3, int>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<-3, Global>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<-3, TestType>>() == \"NonTypeType\");\n  CHECK(NameFunc::template name<NonTypeType<-3, Special>>() == \"NonTypeType\");\n\n  // nullptr-related things\n  CHECK(NameFunc::template name<Template<std::nullptr_t>>() == \"Template\");\n  CHECK(NameFunc::template name<Template2<std::nullptr_t, std::nullptr_t>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<int, std::nullptr_t>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<Global, std::nullptr_t>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<TestType, std::nullptr_t>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<Special, std::nullptr_t>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<std::nullptr_t, int>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<std::nullptr_t, Global>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<std::nullptr_t, TestType>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<Template2<std::nullptr_t, Special>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<NullptrTemplate<nullptr>>() ==\n        \"NullptrTemplate\");\n\n  // Enum non-type template parameters\n  CHECK(NameFunc::template name<\n            GlobalEnumTemplate<Test_PrettyType_Enum::Zero>>() ==\n        \"GlobalEnumTemplate\");\n  CHECK(NameFunc::template name<\n            GlobalEnumTemplate<Test_PrettyType_Enum::One>>() ==\n        \"GlobalEnumTemplate\");\n  CHECK(NameFunc::template name<\n            Template2<GlobalEnumTemplate<Test_PrettyType_Enum::One>,\n                      GlobalEnumTemplate<Test_PrettyType_Enum::Two>>>() ==\n        \"Template2\");\n  CHECK(NameFunc::template name<LocalEnumTemplate<LocalEnum::Zero>>() ==\n        \"LocalEnumTemplate\");\n  CHECK(NameFunc::template name<LocalEnumTemplate<LocalEnum::One>>() ==\n        \"LocalEnumTemplate\");\n  CHECK(\n      NameFunc::template name<Template2<LocalEnumTemplate<LocalEnum::One>,\n                                        LocalEnumTemplate<LocalEnum::Two>>>() ==\n      \"Template2\");\n  CHECK(NameFunc::template name<LocalEnumTemplate<LocalEnum::Negative>>() ==\n        \"LocalEnumTemplate\");\n\n  // Complicated Test\n  CHECK(NameFunc::template name<OuterTemplate<\n            Template2<OuterTemplate<Global>::InnerTemplate<TestType>,\n                      OuterTemplate<Global>::InnerTemplate<TestType>>>::\n                                    InnerTemplate<NonTypeType<1, Global>>>() ==\n        \"InnerTemplate\");\n\n  // Long test\n  CHECK(NameFunc::template name<tmpl::range<int, 0, 1000>>() == \"list\");\n}\n\nvoid test_list_of_names() {\n  using empty_list = tmpl::list<>;\n  using one_element_list = tmpl::list<Type1Containing2Digits3>;\n  using three_element_list =\n      tmpl::list<TestType, Type1Containing2Digits3, NonTemplateWithName>;\n\n  CHECK(pretty_type::list_of_names<empty_list>() == \"\");\n  CHECK(pretty_type::list_of_names<one_element_list>() ==\n        \"Type1Containing2Digits3\");\n  CHECK(pretty_type::list_of_names<three_element_list>() ==\n        \"TestType, Type1Containing2Digits3, UniqueNonTemplateWithName\");\n\n  CHECK(pretty_type::vector_of_get_names(empty_list{}).empty());\n  {\n    const auto vector_of_names =\n        pretty_type::vector_of_get_names(one_element_list{});\n    CAPTURE(vector_of_names);\n    REQUIRE(vector_of_names.size() == 1);\n    CHECK(vector_of_names[0].find(std::string{\"Type1Containing2Digits3\"}) !=\n          std::string::npos);\n  }\n  {\n    const auto vector_of_names =\n        pretty_type::vector_of_get_names(three_element_list{});\n    CAPTURE(vector_of_names);\n    REQUIRE(vector_of_names.size() == 3);\n    CHECK(vector_of_names[0].find(std::string{\"TestType\"}) !=\n          std::string::npos);\n    CHECK(vector_of_names[1].find(std::string{\"Type1Containing2Digits3\"}) !=\n          std::string::npos);\n    CHECK(vector_of_names[2].find(std::string{\"NonTemplateWithName\"}) !=\n          std::string::npos);\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.PrettyType.name_and_short_name\",\n                  \"[Utilities][Unit]\") {\n  test_name_func<ShortName>();\n  // For all these types without a name() member, the results should be\n  // identical to that of pretty_type::short_name()\n  test_name_func<Name>();\n  test_list_of_names();\n\n  CHECK(NonTemplateWithName::name() == \"UniqueNonTemplateWithName\");\n  CHECK(TemplateWithName<int>::name() == \"UniqueTemplateWithName\");\n}\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_PrintHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <list>\n#include <ostream>\n#include <sstream>\n#include <utility>\n#include <vector>\n\n#include \"Utilities/PrintHelpers.hpp\"\n\nnamespace {\ntemplate <typename F, typename Arg>\nvoid check_value_categories(const F& test, const Arg& arg) {\n  auto copy = arg;\n  test(std::as_const(copy));\n  test(copy);\n  REQUIRE(copy == arg);\n  test(std::move(copy));\n}\n\ntemplate <typename F, typename It>\nvoid check_all_categories(const F& test, const It& begin, const It& end) {\n  check_value_categories(\n      [&end, &test](auto&& begin_cat) {\n        check_value_categories(\n            [&begin_cat, &test](auto&& end_cat) {\n              // Need a copy for each call as it may be moved out of.\n              auto begin_copy = begin_cat;\n              // Having begin_cat not match begin_copy is intentional.\n              test(std::forward<decltype(begin_cat)>(begin_copy),\n                   std::forward<decltype(end_cat)>(end_cat));\n            },\n            end);\n      },\n      begin);\n}\n\ntemplate <typename F>\nvoid test_vector_and_list(const F& test) {\n  const std::vector<int> vec{1, 2, 9, 8, 11};\n  const std::list<int> list(vec.begin(), vec.end());\n\n  // Pointers are valid iterators for contiguous containers.\n  const int* const vec_begin = vec.data();\n  // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)\n  const int* const vec_end = vec.data() + vec.size();\n  // std::list is guaranteed to have a class as an iterator.\n  const auto list_begin = list.begin();\n  const auto list_end = list.end();\n\n  check_all_categories(test, vec_begin, vec_end);\n  check_all_categories(test, list_begin, list_end);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.PrintHelpers\", \"[Utilities][Unit]\") {\n  const auto stars = [](std::ostream& os, const int value) {\n    os << \"*\" << value << \"*\";\n  };\n\n  test_vector_and_list([](auto&& begin, auto&& end) {\n    std::ostringstream os{};\n    sequence_print_helper(os, std::forward<decltype(begin)>(begin),\n                          std::forward<decltype(end)>(end));\n    CHECK(os.str() == \"(1,2,9,8,11)\");\n  });\n\n  test_vector_and_list([&stars](auto&& begin, auto&& end) {\n    std::ostringstream os{};\n    sequence_print_helper(os, std::forward<decltype(begin)>(begin),\n                          std::forward<decltype(end)>(end), stars);\n    CHECK(os.str() == \"(*1*,*2*,*9*,*8*,*11*)\");\n  });\n\n  test_vector_and_list([](auto&& begin, auto&& end) {\n    std::ostringstream os{};\n    unordered_print_helper(os, std::forward<decltype(begin)>(begin),\n                           std::forward<decltype(end)>(end));\n    CHECK(os.str() == \"(1,11,2,8,9)\");\n  });\n\n  test_vector_and_list([&stars](auto&& begin, auto&& end) {\n    std::ostringstream os{};\n    unordered_print_helper(os, std::forward<decltype(begin)>(begin),\n                           std::forward<decltype(end)>(end), stars);\n    CHECK(os.str() == \"(*1*,*11*,*2*,*8*,*9*)\");\n  });\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_ProtocolHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Utilities/ProtocolHelpers.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace {\n\n// [named_protocol]\nnamespace protocols {\n/*\n * \\brief Has a name.\n *\n * Requires the class has these member functions:\n * - `name`: Returns the name of the object as a `std::string`.\n */\nstruct Named {\n  template <typename ConformingType>\n  struct test {\n    // Try calling the `ConformingType::name` member function\n    using name_return_type = decltype(std::declval<ConformingType>().name());\n    // Check the return type of the `name` member function\n    static_assert(std::is_same_v<name_return_type, std::string>,\n                  \"The 'name' function must return a 'std::string'.\");\n  };\n};\n}  // namespace protocols\n// [named_protocol]\n\n// [named_conformance]\nclass Person : public tt::ConformsTo<protocols::Named> {\n public:\n  // Function required to conform to the protocol\n  std::string name() const { return first_name_ + \" \" + last_name_; }\n\n private:\n  // Implementation details of the class that are irrelevant to the protocol\n  std::string first_name_;\n  std::string last_name_;\n\n public:\n  Person(std::string first_name, std::string last_name)\n      : first_name_(std::move(first_name)), last_name_(std::move(last_name)) {}\n};\n// [named_conformance]\n\n// [using_named_protocol]\ntemplate <typename NamedThing>\nstd::string greet(const NamedThing& named_thing) {\n  // Make sure the template parameter conforms to the protocol\n  static_assert(tt::assert_conforms_to_v<NamedThing, protocols::Named>);\n  // Now we can rely on the interface that the protocol defines\n  return \"Hello, \" + named_thing.name() + \"!\";\n}\n// [using_named_protocol]\n\n// [protocol_sfinae]\ntemplate <typename Thing,\n          Requires<not tt::conforms_to_v<Thing, protocols::Named>> = nullptr>\nstd::string greet_anything(const Thing& /*anything*/) {\n  return \"Hello!\";\n}\ntemplate <typename NamedThing,\n          Requires<tt::conforms_to_v<NamedThing, protocols::Named>> = nullptr>\nstd::string greet_anything(const NamedThing& named_thing) {\n  return greet(named_thing);\n}\n// [protocol_sfinae]\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.ProtocolHelpers\", \"[Utilities][Unit]\") {\n  Person person{\"Alice\", \"Bob\"};\n  CHECK(greet(person) == \"Hello, Alice Bob!\");\n  CHECK(greet_anything<int>(0) == \"Hello!\");\n  CHECK(greet_anything(person) == \"Hello, Alice Bob!\");\n}\n\n// Test tt::conforms_to metafunction\n// [conforms_to]\n// SFINAE-friendly version:\nconstexpr bool person_class_is_named =\n    tt::conforms_to_v<Person, protocols::Named>;\n// Assert-friendly version with more diagnostics:\nstatic_assert(tt::assert_conforms_to_v<Person, protocols::Named>);\n// [conforms_to]\nstatic_assert(person_class_is_named,\n              \"The 'Person' class does not conform to the 'Named' protocol.\");\n\nnamespace {\nstruct NotNamed {};\nclass DerivedNonConformingClass : private Person {};\nclass DerivedConformingClass : public Person {};\n}  // namespace\n\nstatic_assert(not tmpl::apply<tt::conforms_to<tmpl::_1, tmpl::_2>, NotNamed,\n                              protocols::Named>::value);\nstatic_assert(tmpl::apply<tt::conforms_to<tmpl::_1, tmpl::_2>, Person,\n                          protocols::Named>::value);\n\nstatic_assert(not tt::conforms_to_v<NotNamed, protocols::Named>,\n              \"Failed testing tt::conforms_to_v\");\nstatic_assert(\n    not tt::conforms_to_v<DerivedNonConformingClass, protocols::Named>,\n    \"Failed testing tt::conforms_to_v\");\nstatic_assert(tt::conforms_to_v<DerivedConformingClass, protocols::Named>,\n              \"Failed testing tt::conforms_to_v\");\n\n// Give examples about protocol antipatterns\nnamespace {\nnamespace protocols {\n// [named_antipattern]\n// Don't do this. Protocols should not have template parameters.\ntemplate <typename NameType>\nstruct NamedAntipattern {\n  template <typename ConformingType>\n  struct test {\n    // Check that the `name` function exists _and_ its return type\n    using name_return_type = decltype(std::declval<ConformingType>().name());\n    static_assert(std::is_same_v<name_return_type, NameType>,\n                  \"The 'name' function must return a 'NameType'.\");\n  };\n};\n// [named_antipattern]\n// [named_with_type]\n// Instead, do this.\nstruct NamedWithType {\n  template <typename ConformingType>\n  struct test {\n    // Use the `ConformingType::NameType` to check the return type of the `name`\n    // function.\n    using name_type = typename ConformingType::NameType;\n    using name_return_type = decltype(std::declval<ConformingType>().name());\n    static_assert(std::is_same_v<name_return_type, name_type>,\n                  \"The 'name' function must return a 'NameType'.\");\n  };\n};\n// [named_with_type]\n}  // namespace protocols\n// [person_with_name_type]\nstruct PersonWithNameType : tt::ConformsTo<protocols::NamedWithType> {\n  using NameType = std::string;\n  std::string name() const;\n};\n// [person_with_name_type]\n// [example_check_name_type]\nstatic_assert(\n    tt::assert_conforms_to_v<PersonWithNameType, protocols::NamedWithType>);\nstatic_assert(\n    std::is_same_v<typename PersonWithNameType::NameType, std::string>,\n    \"The `NameType` isn't a `std::string`!\");\n// [example_check_name_type]\n}  // namespace\n\n// Give an example how protocol consumers should test protocol conformance\n// [test_protocol_conformance]\nstatic_assert(tt::assert_conforms_to_v<Person, protocols::Named>);\n// [test_protocol_conformance]\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Rational.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <functional>\n#include <string>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/Rational.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Rational\", \"[Unit][Utilities]\") {\n  CHECK(Rational(6, 8).numerator() == 3);\n  CHECK(Rational(6, 8).denominator() == 4);\n  CHECK(Rational(-6, 8).numerator() == -3);\n  CHECK(Rational(-6, 8).denominator() == 4);\n  CHECK(Rational(6, -8).numerator() == -3);\n  CHECK(Rational(6, -8).denominator() == 4);\n  CHECK(Rational(-6, -8).numerator() == 3);\n  CHECK(Rational(-6, -8).denominator() == 4);\n\n  CHECK(Rational(0, 2).numerator() == 0);\n  CHECK(Rational(0, 2).denominator() == 1);\n  CHECK(Rational(0, -2).numerator() == 0);\n  CHECK(Rational(0, -2).denominator() == 1);\n\n  CHECK(Rational(3, 4).value() == 0.75);\n  CHECK(Rational(-3, 2).value() == -1.5);\n  CHECK(Rational(1, 3).value() == approx(1./3.));\n\n  check_cmp(Rational(3, 4), Rational(5, 4));\n  check_cmp(Rational(3, 5), Rational(3, 4));\n\n  CHECK(-Rational(3, 4) == Rational(-3, 4));\n  CHECK(-Rational(-3, 4) == Rational(3, 4));\n\n  CHECK(Rational(3, 4).inverse() == Rational(4, 3));\n  CHECK(Rational(-3, 4).inverse() == Rational(-4, 3));\n\n  CHECK_OP(Rational(3, 4), +, Rational(2, 5), Rational(23, 20));\n  CHECK_OP(Rational(3, 4), +, Rational(7, 4), Rational(5, 2));\n  CHECK_OP(Rational(3, 4), -, Rational(2, 5), Rational(7, 20));\n  CHECK_OP(Rational(3, 4), -, Rational(7, 4), Rational(-1));\n  CHECK_OP(Rational(3, 4), *, Rational(5, 2), Rational(15, 8));\n  CHECK_OP(Rational(3, 4), *, Rational(2, 3), Rational(1, 2));\n  CHECK_OP(Rational(3, 4), /, Rational(2, 5), Rational(15, 8));\n  CHECK_OP(Rational(3, 4), /, Rational(3, 2), Rational(1, 2));\n\n  CHECK(abs(Rational(3, 4)) == Rational(3, 4));\n  CHECK(abs(Rational(-3, 4)) == Rational(3, 4));\n  CHECK(abs(Rational(0)) == Rational(0));\n\n  const auto hash = std::hash<Rational>{};\n  CHECK(hash(Rational(1, 2)) != hash(Rational(1, 3)));\n  CHECK(hash(Rational(1, 2)) != hash(Rational(3, 2)));\n  CHECK(hash(Rational(1, 2)) != hash(Rational(-1, 2)));\n\n  CHECK(get_output(Rational(3, 4)) == \"3/4\");\n  CHECK(get_output(Rational(-3, 4)) == \"-3/4\");\n\n  test_serialization(Rational(3, 4));\n\n  struct ArbitraryType {};\n  CHECK(make_with_value<Rational>(ArbitraryType{}, 0.0) == Rational(0));\n  CHECK(make_with_value<Rational>(ArbitraryType{}, 1.0) == Rational(1));\n  CHECK(make_with_value<Rational>(ArbitraryType{}, -1.0) == Rational(-1));\n  CHECK(make_with_value<Rational>(ArbitraryType{}, 1234.0) == Rational(1234));\n\n  // Check that the internal implementation does not cause overflow\n  // when the results can be correctly represented.\n\n  CHECK(Rational(6000000, 8000000) == Rational(3, 4));\n\n  check_cmp(Rational(5, 1 << 16), Rational(1 << 16));\n\n  CHECK_OP(Rational((1 << 21) + 1, 1 << 17), +,\n           Rational((1 << 22) - 1, 1 << 18), Rational((1 << 23) + 1, 1 << 18));\n  CHECK_OP(Rational((1 << 21) + 1, 1 << 17), -,\n           Rational((1 << 22) - 1, 1 << 18), Rational(3, 1 << 18));\n  CHECK_OP(Rational((1 << 20) - 1, 1 << 20), *,\n           Rational(1 << 17, (1 << 10) + 1), Rational((1 << 10) - 1, 1 << 3));\n  CHECK_OP(Rational((1 << 20) - 1, 1 << 20), /,\n           Rational((1 << 10) + 1, 1 << 17), Rational((1 << 10) - 1, 1 << 3));\n\n  // Check assertions\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(Rational(1, 0),\n                    Catch::Matchers::ContainsSubstring(\"Division by zero\"));\n  CHECK_THROWS_WITH(Rational(0).inverse(),\n                    Catch::Matchers::ContainsSubstring(\"Division by zero\"));\n  CHECK_THROWS_WITH(Rational(1) / 0,\n                    Catch::Matchers::ContainsSubstring(\"Division by zero\"));\n  CHECK_THROWS_WITH(\n      []() {\n        Rational r(1);\n        r /= 0;\n      }(),\n      Catch::Matchers::ContainsSubstring(\"Division by zero\"));\n  CHECK_THROWS_WITH(\n      Rational(1000000) * Rational(1000000),\n      Catch::Matchers::ContainsSubstring(\"Rational overflow: 1000000000000/1\"));\n  CHECK_THROWS_WITH(\n      Rational(1, 1000000) * Rational(1, 1000000),\n      Catch::Matchers::ContainsSubstring(\"Rational overflow: 1/1000000000000\"));\n  CHECK_THROWS_WITH(\n      make_with_value<Rational>(1, 1.5),\n      Catch::Matchers::ContainsSubstring(\n          \"Only integer-valued Rationals can be created with MakeWithValue\"));\n#endif\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Registration.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"Framework/TestCreation.hpp\"\n#include \"Options/String.hpp\"\n#include \"Utilities/Registration.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// [registrar_structure]\ntemplate <typename Registrars>\nclass Base {\n public:\n  Base() = default;\n  Base(const Base&) = default;\n  Base(Base&&) = default;\n  Base& operator=(const Base&) = default;\n  Base& operator=(Base&&) = default;\n  virtual ~Base() = default;\n  using creatable_classes = Registration::registrants<Registrars>;\n\n  virtual int func() const = 0;\n};\n\ntemplate <typename Registrars>\nclass Derived1;\n\nnamespace Registrars {\nusing Derived1 = Registration::Registrar<::Derived1>;\n}  // namespace Registrars\n\ntemplate <typename Registrars = tmpl::list<Registrars::Derived1>>\nclass Derived1 : public Base<Registrars> {\n public:\n  static constexpr Options::String help = \"help\";\n  using options = tmpl::list<>;\n  int func() const override { return 1; }\n};\n// [registrar_structure]\n\n// [registrar]\ntemplate <typename SomeArg, typename Registrars>\nclass Derived2;\n\nnamespace Registrars {\ntemplate <typename SomeArg>\nusing Derived2 = Registration::Registrar<::Derived2, SomeArg>;\n}  // namespace Registrars\n\ntemplate <typename SomeArg,\n          typename Registrars = tmpl::list<Registrars::Derived2<SomeArg>>>\nclass Derived2 : public Base<Registrars> {\n public:\n  static constexpr Options::String help = \"help\";\n  using options = tmpl::list<>;\n  int func() const override { return 2; }\n};\n// [registrar]\n\n// [custom_registrar]\ntemplate <int N, typename Registrars>\nclass Derived3;\n\nnamespace Registrars {\ntemplate <int N>\nstruct Derived3 {\n  template <typename RegistrarList>\n  using f = ::Derived3<N, RegistrarList>;\n};\n}  // namespace Registrars\n\ntemplate <int N, typename Registrars = tmpl::list<Registrars::Derived3<N>>>\nclass Derived3 : public Base<Registrars> {\n public:\n  static constexpr Options::String help = \"help\";\n  using options = tmpl::list<>;\n  int func() const override { return N; }\n};\n// [custom_registrar]\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Registration\", \"[Unit][Utilities]\") {\n  // [registrar_use]\n  using ConcreteBase =\n      Base<tmpl::list<Registrars::Derived1, Registrars::Derived2<double>,\n                      Registrars::Derived3<4>>>;\n  // [registrar_use]\n  CHECK(TestHelpers::test_creation<std::unique_ptr<ConcreteBase>>(\"Derived1\")\n            ->func() == 1);\n  CHECK(TestHelpers::test_creation<std::unique_ptr<ConcreteBase>>(\"Derived2\")\n            ->func() == 2);\n  CHECK(TestHelpers::test_creation<std::unique_ptr<ConcreteBase>>(\"Derived3\")\n            ->func() == 4);\n\n  // Test standalone derived classes\n  CHECK(Derived1<>{}.func() == 1);\n  CHECK(Derived2<double>{}.func() == 2);\n  CHECK(Derived3<4>{}.func() == 4);\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Requires.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <list>\n#include <string>\n#include <vector>\n\n#include \"Utilities/Requires.hpp\"\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace {\n// [function_definitions]\n// [foo_definition]\ntemplate <typename T, Requires<tt::is_a_v<std::vector, T>> = nullptr>\nstd::string foo(const T& /*unused*/) {\n  return \"vector\";\n}\n// [foo_definition]\n\ntemplate <typename T, Requires<tt::is_a_v<std::list, T>> = nullptr>\nstd::string foo(const T& /*unused*/) {\n  return \"list\";\n}\n// [function_definitions]\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.Requires\", \"[Unit][Utilities]\") {\n  CHECK(\"vector\" == foo(std::vector<double>{}));\n  CHECK(\"list\" == foo(std::list<double>{}));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_SetNumberOfGridPoints.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <complex>\n#include <cstddef>\n#include <vector>\n\n#include \"DataStructures/TaggedTuple.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeWithValue.hpp\"\n#include \"Utilities/SetNumberOfGridPoints.hpp\"\n\nnamespace {\nstruct TriviallyResizable {};\n\nstruct Resizable {\n  size_t size = 0;\n  bool resized = false;\n};\n\nstruct NoSize {};\n\ntemplate <typename T, int Label>\nstruct Tag {\n  using type = T;\n};\n}  // namespace\n\n// [SetNumberOfGridPointsImpl]\ntemplate <>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<\n    TriviallyResizable> {\n  static constexpr bool is_trivial = true;\n};\n\ntemplate <>\nstruct SetNumberOfGridPointsImpls::SetNumberOfGridPointsImpl<Resizable> {\n  static constexpr bool is_trivial = false;\n  static void apply(const gsl::not_null<Resizable*> result, const size_t size) {\n    result->size = size;\n    result->resized = true;\n  }\n};\n// [SetNumberOfGridPointsImpl]\n\nnamespace MakeWithValueImpls {\ntemplate <>\nstruct NumberOfPoints<Resizable> {\n  static size_t apply(const Resizable& input) { return input.size; }\n};\n}  // namespace MakeWithValueImpls\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.SetNumberOfGridPoints\", \"[Utilities][Unit]\") {\n  Resizable pattern3;\n  pattern3.size = 3;\n\n  {\n    TriviallyResizable x;\n    set_number_of_grid_points(make_not_null(&x), 3_st);\n    set_number_of_grid_points(make_not_null(&x), pattern3);\n    set_number_of_grid_points(make_not_null(&x), TriviallyResizable{});\n  }\n\n  {\n    Resizable r;\n    set_number_of_grid_points(make_not_null(&r), 3_st);\n    CHECK(r.size == 3);\n    CHECK(r.resized);\n  }\n  {\n    Resizable r;\n    r.size = 3;\n    set_number_of_grid_points(make_not_null(&r), 3_st);\n    CHECK(r.size == 3);\n    CHECK(not r.resized);\n  }\n  {\n    Resizable r;\n    set_number_of_grid_points(make_not_null(&r), pattern3);\n    CHECK(r.size == 3);\n    CHECK(r.resized);\n  }\n  {\n    Resizable r;\n    r.size = 3;\n    set_number_of_grid_points(make_not_null(&r), pattern3);\n    CHECK(r.size == 3);\n    CHECK(not r.resized);\n  }\n\n  {\n    double x = 2.0;\n    set_number_of_grid_points(make_not_null(&x), 3_st);\n    CHECK(x == 2.0);\n  }\n  {\n    double x = 2.0;\n    set_number_of_grid_points(make_not_null(&x), NoSize{});\n    CHECK(x == 2.0);\n  }\n\n  {\n    std::complex<double> x(2.0, 4.0);\n    set_number_of_grid_points(make_not_null(&x), 3_st);\n    CHECK(x == std::complex<double>(2.0, 4.0));\n  }\n  {\n    std::complex<double> x(2.0, 4.0);\n    set_number_of_grid_points(make_not_null(&x), NoSize{});\n    CHECK(x == std::complex<double>(2.0, 4.0));\n  }\n\n  {\n    std::array<Resizable, 3> x;\n    x[0].size = 2;\n    x[1].size = 2;\n    x[2].size = 2;\n    set_number_of_grid_points(make_not_null(&x), pattern3);\n    CHECK(x[0].size == 3);\n    CHECK(x[1].size == 3);\n    CHECK(x[2].size == 3);\n    CHECK(x[0].resized);\n    CHECK(x[1].resized);\n    CHECK(x[2].resized);\n  }\n  {\n    std::array<Resizable, 3> x;\n    x[0].size = 3;\n    x[1].size = 3;\n    x[2].size = 3;\n    set_number_of_grid_points(make_not_null(&x), pattern3);\n    CHECK(x[0].size == 3);\n    CHECK(x[1].size == 3);\n    CHECK(x[2].size == 3);\n    CHECK(not x[0].resized);\n    CHECK(not x[1].resized);\n    CHECK(not x[2].resized);\n  }\n  {\n    std::array<double, 3> x{1.0, 2.0, 3.0};\n    set_number_of_grid_points(make_not_null(&x), NoSize{});\n    CHECK(x == std::array{1.0, 2.0, 3.0});\n  }\n  {\n    std::array<double, 3> x{1.0, 2.0, 3.0};\n    set_number_of_grid_points(make_not_null(&x), 1_st);\n    CHECK(x == std::array{1.0, 2.0, 3.0});\n  }\n  {\n    std::array<Resizable, 0> x{};\n    set_number_of_grid_points(make_not_null(&x), 1_st);\n  }\n\n  {\n    std::vector<Resizable> x{{2, false}, {2, false}, {2, false}};\n    set_number_of_grid_points(make_not_null(&x), pattern3);\n    CHECK(x[0].size == 3);\n    CHECK(x[1].size == 3);\n    CHECK(x[2].size == 3);\n    CHECK(x[0].resized);\n    CHECK(x[1].resized);\n    CHECK(x[2].resized);\n  }\n  {\n    std::vector<Resizable> x{{3, false}, {3, false}, {3, false}};\n    set_number_of_grid_points(make_not_null(&x), pattern3);\n    CHECK(x[0].size == 3);\n    CHECK(x[1].size == 3);\n    CHECK(x[2].size == 3);\n    CHECK(not x[0].resized);\n    CHECK(not x[1].resized);\n    CHECK(not x[2].resized);\n  }\n  {\n    std::vector<double> x{1.0, 2.0, 3.0};\n    set_number_of_grid_points(make_not_null(&x), NoSize{});\n    CHECK(x == std::vector{1.0, 2.0, 3.0});\n  }\n  {\n    std::vector<double> x{1.0, 2.0, 3.0};\n    set_number_of_grid_points(make_not_null(&x), 1_st);\n    CHECK(x == std::vector{1.0, 2.0, 3.0});\n  }\n\n  {\n    tuples::TaggedTuple<Tag<Resizable, 0>, Tag<Resizable, 1>> x;\n    set_number_of_grid_points(make_not_null(&x), pattern3);\n    CHECK(tuples::get<Tag<Resizable, 0>>(x).size == 3);\n    CHECK(tuples::get<Tag<Resizable, 1>>(x).size == 3);\n    CHECK(tuples::get<Tag<Resizable, 0>>(x).resized);\n    CHECK(tuples::get<Tag<Resizable, 1>>(x).resized);\n  }\n  {\n    tuples::TaggedTuple<Tag<Resizable, 0>, Tag<Resizable, 1>> x;\n    tuples::get<Tag<Resizable, 0>>(x).size = 3;\n    tuples::get<Tag<Resizable, 1>>(x).size = 3;\n    set_number_of_grid_points(make_not_null(&x), pattern3);\n    CHECK(tuples::get<Tag<Resizable, 0>>(x).size == 3);\n    CHECK(tuples::get<Tag<Resizable, 1>>(x).size == 3);\n    CHECK(not tuples::get<Tag<Resizable, 0>>(x).resized);\n    CHECK(not tuples::get<Tag<Resizable, 1>>(x).resized);\n  }\n  {\n    tuples::TaggedTuple<Tag<Resizable, 0>, Tag<Resizable, 1>> x;\n    set_number_of_grid_points(make_not_null(&x), 3_st);\n    CHECK(tuples::get<Tag<Resizable, 0>>(x).size == 3);\n    CHECK(tuples::get<Tag<Resizable, 1>>(x).size == 3);\n    CHECK(tuples::get<Tag<Resizable, 0>>(x).resized);\n    CHECK(tuples::get<Tag<Resizable, 1>>(x).resized);\n  }\n  {\n    // We've checked resizing double and complex<double> above.  This\n    // just checks that the correct overloads are called so these\n    // compile.\n    tuples::TaggedTuple<Tag<double, 0>, Tag<std::complex<double>, 0>> x;\n    set_number_of_grid_points(make_not_null(&x), 3_st);\n    set_number_of_grid_points(make_not_null(&x), pattern3);\n    set_number_of_grid_points(make_not_null(&x), NoSize{});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_SnakeCase.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n\n#include \"Utilities/SnakeCase.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.SnakeCase\", \"[Unit][Utilities]\") {\n  CHECK(camel_case_to_snake_case(\"CamelCaseString\") == \"camel_case_string\");\n  CHECK(snake_case_to_camel_case(\"snake_case_string\") == \"SnakeCaseString\");\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_SplitTuple.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n#include <tuple>\n#include <type_traits>\n#include <utility>\n\n#include \"Utilities/SplitTuple.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Test constexpr\n// [split_tuple]\nstatic_assert(split_tuple<2, 1>(std::tuple{1, 2, 3}) ==\n              std::tuple{std::tuple{1, 2}, std::tuple{3}});\nstatic_assert(split_tuple<tmpl::integral_list<size_t, 2, 1>>(std::tuple{\n                  1, 2, 3}) == std::tuple{std::tuple{1, 2}, std::tuple{3}});\n// [split_tuple]\n\n// Needed for type-deduction of the nested tuples.\ntemplate <typename... T>\nstd::tuple<T...> construct_tuple(T... args) {\n  return {std::move(args)...};\n}\n\ntemplate <size_t... Sizes, typename... T, typename Expected>\nvoid check(std::tuple<T...> tuple, const Expected& expected) {\n  auto split = split_tuple<Sizes...>(tuple);\n  static_assert(std::is_same_v<decltype(split), Expected>);\n  auto split_typelist =\n      split_tuple<tmpl::integral_list<size_t, Sizes...>>(tuple);\n  static_assert(std::is_same_v<decltype(split_typelist), Expected>);\n  CHECK(split == expected);\n  CHECK(split_typelist == expected);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.SplitTuple\", \"[Unit][Utilities]\") {\n  // Normal case\n  check<2, 1>(construct_tuple<int, double, float>(1, 2.0, 3.0),\n              construct_tuple<std::tuple<int, double>, std::tuple<float>>(\n                  {1, 2.0}, {3.0}));\n  // Empty piece\n  check<2, 0, 1>(\n      construct_tuple<int, double, float>(1, 2.0, 3.0),\n      construct_tuple<std::tuple<int, double>, std::tuple<>, std::tuple<float>>(\n          {1, 2.0}, {}, {3.0}));\n  // No output\n  check<>(construct_tuple<>(), construct_tuple<>());\n  // Single output\n  check<2>(construct_tuple<int, double>(1, 2.0),\n           construct_tuple<std::tuple<int, double>>({1, 2.0}));\n  // Non-copyable\n  {\n    using Expected =\n        std::tuple<std::tuple<int>, std::tuple<std::unique_ptr<double>>>;\n    auto split =\n        split_tuple<1, 1>(construct_tuple<int, std::unique_ptr<double>>(\n            1, std::make_unique<double>(2.0)));\n    static_assert(std::is_same_v<decltype(split), Expected>);\n    auto split_typelist = split_tuple<tmpl::integral_list<size_t, 1, 1>>(\n        construct_tuple<int, std::unique_ptr<double>>(\n            1, std::make_unique<double>(2.0)));\n    static_assert(std::is_same_v<decltype(split_typelist), Expected>);\n    CHECK(std::get<0>(std::get<0>(split)) == 1);\n    CHECK(*std::get<0>(std::get<1>(split)) == 2.0);\n    CHECK(std::get<0>(std::get<0>(split_typelist)) == 1);\n    CHECK(*std::get<0>(std::get<1>(split_typelist)) == 2.0);\n  }\n  // References\n  {\n    // NOLINTBEGIN(bugprone-use-after-move)\n    const int a = 0;\n    double b = 0.0;\n    using Expected = std::tuple<std::tuple<const int&>, std::tuple<double&&>>;\n    auto split = split_tuple<1, 1>(\n        construct_tuple<const int&, double&&>(a, std::move(b)));\n    static_assert(std::is_same_v<decltype(split), Expected>);\n    auto split_typelist = split_tuple<tmpl::integral_list<size_t, 1, 1>>(\n        construct_tuple<const int&, double&&>(a, std::move(b)));\n    static_assert(std::is_same_v<decltype(split_typelist), Expected>);\n    CHECK(&std::get<0>(std::get<0>(split)) == &a);\n    CHECK(&std::get<0>(std::get<1>(split)) == &b);\n    CHECK(&std::get<0>(std::get<0>(split_typelist)) == &a);\n    CHECK(&std::get<0>(std::get<1>(split_typelist)) == &b);\n    // NOLINTEND(bugprone-use-after-move)\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_StaticCache.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <utility>\n#include <vector>\n\n#include \"Utilities/ConstantExpressions.hpp\"\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Literals.hpp\"\n#include \"Utilities/MakeString.hpp\"\n#include \"Utilities/StaticCache.hpp\"\n\nnamespace {\nenum class Color { Red, Green, Purple };\n\nstd::ostream& operator<<(std::ostream& os, Color t) {\n  switch (t) {\n    case Color::Red:\n      return os << \"Red\";\n    case Color::Green:\n      return os << \"Green\";\n    case Color::Purple:\n      return os << \"Purple\";\n    default:\n      ERROR(\"Unknown color\");\n  }\n}\n\nenum class Animal { Goldendoodle, Labradoodle, Poodle };\n\nstd::ostream& operator<<(std::ostream& os, Animal t) {\n  switch (t) {\n    case Animal::Goldendoodle:\n      return os << \"Goldendoodle\";\n    case Animal::Labradoodle:\n      return os << \"Labradoodle\";\n    case Animal::Poodle:\n      return os << \"Poodle\";\n    default:\n      ERROR(\"Unknown Animal\");\n  }\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.StaticCache\", \"[Utilities][Unit]\") {\n  // [static_cache]\n  const static auto cache =\n      make_static_cache<CacheRange<0_st, 3_st>, CacheRange<3_st, 5_st>>(\n          [](const size_t a, const size_t b) { return a + b; });\n  CHECK(cache(0, 3) == 3);  // smallest entry\n  CHECK(cache(2, 4) == 6);  // largest entry\n  // [static_cache]\n\n  std::vector<std::pair<size_t, size_t>> calls;\n  const auto cache2 =\n      make_static_cache<CacheRange<0_st, 3_st>, CacheRange<3_st, 5_st>>(\n          [&calls](const size_t a, const size_t b) {\n            calls.emplace_back(a, b);\n            return a + b;\n          });\n  // cache is lazy, shouldn't have called at all before retrieving\n  CHECK(calls.empty());\n\n  // explicitly call the cache creation to check its contents\n  cache2(0, 3);\n  cache2(0, 4);\n  cache2(1, 3);\n  CHECK(calls.size() == 3);\n\n  cache2(0, 4);\n  cache2(1, 3);\n  cache2(1, 3);\n  CHECK(calls.size() == 3);\n\n  cache2(1, 4);\n  cache2(2, 3);\n  cache2(2, 4);\n  CHECK(calls.size() == 6);\n\n  const decltype(calls) expected_calls{{0, 3}, {0, 4}, {1, 3},\n                                       {1, 4}, {2, 3}, {2, 4}};\n  CHECK(calls == expected_calls);\n  for (const auto& call : expected_calls) {\n    CHECK(cache2(call.first, call.second) == call.first + call.second);\n  }\n  CHECK(calls == expected_calls);\n\n  size_t small_calls = 0;\n  const auto small_cache = make_static_cache([&small_calls]() {\n    ++small_calls;\n    return size_t{5};\n  });\n  CHECK(small_calls == 0);\n  CHECK(small_cache() == 5);\n  CHECK(small_calls == 1);\n\n  // [static_cache_no_args]\n  const auto simple_small_cache =\n      make_static_cache([]() { return size_t{10}; });\n  CHECK(simple_small_cache() == 10);\n  // [static_cache_no_args]\n\n  // check enum caching functionality\n  const auto enum_generator_tuple = [](const Color& color,\n                                       const size_t value = 5,\n                                       const Animal animal =\n                                           Animal::Goldendoodle) {\n    size_t offset_animal = 0;\n    switch (animal) {\n      case Animal::Goldendoodle:\n        offset_animal = 3;\n        break;\n      case Animal::Labradoodle:\n        offset_animal = 2;\n        break;\n      case Animal::Poodle:\n        offset_animal = 1;\n        break;\n      default:\n        offset_animal = std::numeric_limits<size_t>::max();\n        break;\n    };\n\n    switch (color) {\n      case Color::Red:\n        return std::make_tuple(offset_animal, 1, value);\n      case Color::Green:\n        return std::make_tuple(offset_animal, 2, value);\n      case Color::Purple:\n        return std::make_tuple(offset_animal, 3, value);\n      default:\n        return std::make_tuple(offset_animal, std::numeric_limits<int>::max(),\n                               value);\n    };\n  };\n  // [static_cache_with_enum]\n  const auto simple_enum_cache = make_static_cache<\n      CacheEnumeration<Color, Color::Red, Color::Green, Color::Purple>>(\n      [](const Color color) { return std::string{MakeString{} << color}; });\n  CHECK(simple_enum_cache(Color::Red) == \"Red\");\n  // [static_cache_with_enum]\n\n  const auto int_cache = make_static_cache<CacheRange<-5, 10>>(\n      [](const int val) { return pow<3>(val); });\n  CHECK(int_cache(-3) == -27);\n  CHECK(int_cache(2) == 8);\n\n  const auto enum_cache = make_static_cache<\n      CacheEnumeration<Color, Color::Red, Color::Green, Color::Purple>>(\n      enum_generator_tuple);\n  for (const auto color : {Color::Red, Color::Green, Color::Purple}) {\n    CHECK(enum_cache(color) ==\n          std::make_tuple(3, static_cast<size_t>(color) + 1, 5));\n  }\n\n  const auto enum_size_t_cache = make_static_cache<\n      CacheEnumeration<Color, Color::Red, Color::Green, Color::Purple>,\n      CacheRange<3_st, 5_st>>(enum_generator_tuple);\n  for (const auto color : {Color::Red, Color::Green, Color::Purple}) {\n    CHECK(enum_size_t_cache(color, 3) ==\n          std::make_tuple(3, static_cast<size_t>(color) + 1, 3));\n    CHECK(enum_size_t_cache(color, 4) ==\n          std::make_tuple(3, static_cast<size_t>(color) + 1, 4));\n  }\n\n  // [static_cache_with_enum_and_numeric]\n  const auto simple_enum_size_t_enum_cache = make_static_cache<\n      CacheEnumeration<Color, Color::Red, Color::Green, Color::Purple>,\n      CacheRange<3_st, 5_st>,\n      CacheEnumeration<Animal, Animal::Goldendoodle, Animal::Labradoodle,\n                       Animal::Poodle>>(\n      [](const Color color, const size_t value, const Animal animal) {\n        return std::string{MakeString{} << color << value << animal};\n      });\n  CHECK(simple_enum_size_t_enum_cache(Color::Red, 3, Animal::Labradoodle) ==\n        \"Red3Labradoodle\");\n  CHECK(simple_enum_size_t_enum_cache(Color::Purple, 4, Animal::Poodle) ==\n        \"Purple4Poodle\");\n  // [static_cache_with_enum_and_numeric]\n  const auto enum_size_t_enum_cache = make_static_cache<\n      CacheEnumeration<Color, Color::Red, Color::Green, Color::Purple>,\n      CacheRange<3_st, 5_st>,\n      CacheEnumeration<Animal, Animal::Goldendoodle, Animal::Labradoodle,\n                       Animal::Poodle>>(enum_generator_tuple);\n  for (const auto color : {Color::Red, Color::Green, Color::Purple}) {\n    for (const auto animal :\n         {Animal::Goldendoodle, Animal::Labradoodle, Animal::Poodle}) {\n      CHECK(enum_size_t_enum_cache(color, 3, animal) ==\n            std::make_tuple(3 - static_cast<size_t>(animal),\n                            static_cast<size_t>(color) + 1, 3));\n      CHECK(enum_size_t_enum_cache(color, 4, animal) ==\n            std::make_tuple(3 - static_cast<size_t>(animal),\n                            static_cast<size_t>(color) + 1, 4));\n    }\n  }\n\n#ifdef SPECTRE_DEBUG\n  CHECK_THROWS_WITH(\n      (make_static_cache<CacheRange<3, 5>>([](const size_t x) { return x; })(\n          2)),\n      Catch::Matchers::ContainsSubstring(\"Index out of range: 3 <= 2 < 5\"));\n  CHECK_THROWS_WITH(\n      (make_static_cache<CacheRange<3, 5>>([](const size_t x) { return x; })(\n          5)),\n      Catch::Matchers::ContainsSubstring(\"Index out of range: 3 <= 5 < 5\"));\n#endif\n\n  // Test that the passed callable is stored, so we don't get a\n  // dangling reference in the usual case that the cache outlives its\n  // calling scope.\n  {\n    struct StoreTest {\n      int value{};\n      int operator()() const { return value; }\n    };\n\n    StoreTest callable{5};\n    const auto cache_from_lvalue = make_static_cache(callable);\n    callable.value = 8;\n    CHECK(cache_from_lvalue() == 5);\n  }\n\n  // Test that the cache caches values, even if the callable returns\n  // by reference\n  {\n    int value = 5;\n    const auto cache_returned_ref =\n        make_static_cache([&value]() -> const int& { return value; });\n    CHECK(cache_returned_ref() == 5);\n    value = 8;\n    CHECK(cache_returned_ref() == 5);\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_StdArrayHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <cstddef>\n#include <functional>\n#include <memory>\n\n// We wish to explicitly test implicit type conversion when adding std::arrays\n// of different fundamentals, so we supress -Wsign-conversion.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wsign-conversion\"\n#include \"Utilities/StdArrayHelpers.hpp\"\n#pragma GCC diagnostic pop\n\n#include \"DataStructures/DataVector.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Literals.hpp\"\n\nnamespace {\nvoid test_arithmetic() {\n  const size_t Dim = 3;\n\n  std::array<double, Dim> p1{{2.3, -1.4, 0.2}};\n  std::array<double, Dim> p2{{-12.4, 4.5, 2.6}};\n\n  const std::array<double, Dim> expected_plus{{-10.1, 3.1, 2.8}};\n  const std::array<double, Dim> expected_minus{{14.7, -5.9, -2.4}};\n\n  const auto plus = p1 + p2;\n  const auto minus = p1 - p2;\n\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(gsl::at(plus, i) == approx(gsl::at(expected_plus, i)));\n    CHECK(gsl::at(minus, i) == approx(gsl::at(expected_minus, i)));\n  }\n\n  p1 += expected_plus;\n  p2 -= expected_minus;\n\n  const std::array<double, Dim> expected_plus_equal{{-7.8, 1.7, 3.}};\n  const std::array<double, Dim> expected_minus_equal{{-27.1, 10.4, 5.}};\n\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(gsl::at(p1, i) == approx(gsl::at(expected_plus_equal, i)));\n    CHECK(gsl::at(p2, i) == approx(gsl::at(expected_minus_equal, i)));\n  }\n\n  const double scale = -1.8;\n  const auto left_scaled_array = scale * p1;\n  const std::array<double, Dim> expected_left_scaled_array{\n      {14.04, -3.06, -5.4}};\n  const auto right_scaled_array = p1 * scale;\n  const auto array_divided_by_double = p1 / (1 / scale);\n  auto in_place_scaled_array = p1;\n  in_place_scaled_array *= scale;\n  auto array_divided_in_place_by_double = p1;\n  array_divided_in_place_by_double /= 1.0 / scale;\n\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(gsl::at(left_scaled_array, i) ==\n          approx(gsl::at(expected_left_scaled_array, i)));\n    CHECK(gsl::at(left_scaled_array, i) ==\n          approx(gsl::at(right_scaled_array, i)));\n    CHECK(gsl::at(left_scaled_array, i) ==\n          approx(gsl::at(array_divided_by_double, i)));\n    CHECK(gsl::at(left_scaled_array, i) ==\n          approx(gsl::at(in_place_scaled_array, i)));\n    CHECK(gsl::at(left_scaled_array, i) ==\n          approx(gsl::at(array_divided_in_place_by_double, i)));\n  }\n\n  const auto neg_p1 = -p1;\n  const auto expected_neg_p1 = -1. * p1;\n  for (size_t i = 0; i < Dim; ++i) {\n    CHECK(gsl::at(neg_p1, i) == approx(gsl::at(expected_neg_p1, i)));\n  }\n\n  const std::array<double, 2> double_array{{2.2, -1.0}};\n  const std::array<int, 2> int_array{{1, 3}};\n  const std::array<size_t, 2> size_array{{2, 10}};\n  const std::array<double, 2> double_plus_int{{3.2, 2.0}};\n  const std::array<double, 2> double_plus_size{{4.2, 9.0}};\n  const std::array<size_t, 2> int_plus_size{{3, 13}};\n  const std::array<size_t, 2> size_minus_int{{1, 7}};\n  CHECK(double_array + int_array == double_plus_int);\n  CHECK(double_array + size_array == double_plus_size);\n  CHECK(int_array + size_array == int_plus_size);\n  CHECK(int_array + double_array == double_plus_int);\n  CHECK(size_array + double_array == double_plus_size);\n  CHECK(size_array + int_array == int_plus_size);\n  CHECK(size_array - -int_array == int_plus_size);\n  CHECK(size_array - -double_array == double_plus_size);\n  CHECK(size_array - int_array == size_minus_int);\n}\n\nvoid test_magnitude() {\n  std::array<double, 1> p1{{-2.5}};\n  CHECK(2.5 == magnitude(p1));\n  const std::array<double, 2> p2{{3., -4.}};\n  CHECK(magnitude(p2) == approx(5.));\n  const std::array<double, 3> p3{{-2., 10., 11.}};\n  CHECK(magnitude(p3) == approx(15.));\n}\n\nvoid test_dot() {\n  {\n    INFO(\"Testing 1D\");\n    const std::array<double, 1> double_1{{-2.}};\n    const std::array<double, 1> double_2{{-3.}};\n    const std::array<DataVector, 1> dv_1{{{1., 2., 3.}}};\n    const std::array<DataVector, 1> dv_2{{{2., 4., 6.}}};\n\n    CHECK(dot(double_1, double_2) == approx(6.));\n    CHECK_ITERABLE_APPROX(dot(dv_1, dv_2), (DataVector{2., 8., 18.}));\n    CHECK_ITERABLE_APPROX(dot(dv_1, double_1), (DataVector{-2., -4., -6.}));\n    CHECK(dot(double_1, dv_1) == dot(dv_1, double_1));\n  }\n\n  {\n    INFO(\"Testing 2D\");\n    const std::array<double, 2> double_1{{-2., -3.}};\n    const std::array<double, 2> double_2{{-3., -1.}};\n    const std::array<DataVector, 2> dv_1{{{1., 2., 3.}, {-1., -2., -3.}}};\n    const std::array<DataVector, 2> dv_2{{{2., 4., 6.}, {3., 6., 9.}}};\n\n    CHECK(dot(double_1, double_2) == approx(9.));\n    CHECK_ITERABLE_APPROX(dot(dv_1, dv_2), (DataVector{-1., -4., -9.}));\n    CHECK_ITERABLE_APPROX(dot(dv_1, double_1), (DataVector{1., 2., 3.}));\n    CHECK(dot(double_1, dv_1) == dot(dv_1, double_1));\n  }\n\n  {\n    INFO(\"Testing 3D\");\n    const std::array<double, 3> double_1{{-2., 1., 4.}};\n    const std::array<double, 3> double_2{{-3., 2., 0.}};\n    const std::array<DataVector, 3> dv_1{\n        {{1., 2., 3.}, {-1., 2., 0.}, {-2., 1., 1.}}};\n    const std::array<DataVector, 3> dv_2{\n        {{1., 2., 3.}, {-3., -1., 2.}, {-4., 3., 1.}}};\n\n    CHECK(dot(double_1, double_2) == approx(8.));\n    CHECK_ITERABLE_APPROX(dot(dv_1, dv_2), (DataVector{12., 5., 10.}));\n    CHECK_ITERABLE_APPROX(dot(dv_1, double_1), (DataVector{-11., 2., -2.}));\n    CHECK(dot(double_1, dv_1) == dot(dv_1, double_1));\n  }\n}\n\nvoid test_all_but_specified_element_of() {\n  const std::array<size_t, 3> a3{{5, 2, 3}};\n  const std::array<size_t, 2> a2{{2, 3}};\n  const std::array<size_t, 1> a1{{3}};\n  const std::array<size_t, 0> a0{{}};\n  CHECK(a2 == all_but_specified_element_of(a3, 0));\n  CHECK(a1 == all_but_specified_element_of(a2, 0));\n  CHECK(a0 == all_but_specified_element_of(a1, 0));\n  const std::array<size_t, 2> b2{{5, 3}};\n  const std::array<size_t, 1> b1{{5}};\n  auto c2 = all_but_specified_element_of(a3, 1);\n  CHECK(b2 == c2);\n  auto c1 = all_but_specified_element_of(b2, 1);\n  CHECK(b1 == c1);\n  CHECK(a0 == all_but_specified_element_of(b1, 0));\n}\n\nvoid test_insert_element() {\n  const std::array<int, 0> a0{{}};\n  const std::array<int, 1> a1{{3}};\n  const std::array<int, 2> a2{{2, 3}};\n  const std::array<int, 3> a3{{2, 4, 3}};\n  CHECK(insert_element(a0, 0, 3) == a1);\n  CHECK(insert_element(a1, 0, 2) == a2);\n  CHECK(insert_element(a2, 1, 4) == a3);\n}\n\nvoid test_prepend() {\n  const std::array<size_t, 3> a3{{5, 2, 3}};\n  const std::array<size_t, 2> a2{{2, 3}};\n  const std::array<size_t, 1> a1{{3}};\n  const std::array<size_t, 0> a0{{}};\n  const auto p1 = prepend(a0, 3_st);\n  CHECK(p1 == a1);\n  const auto p2 = prepend(a1, 2_st);\n  CHECK(p2 == a2);\n  const auto p3 = prepend(a2, 5_st);\n  CHECK(p3 == a3);\n}\n\nvoid test_concatenate() {\n  static_assert(concatenate(std::array{1, 2}, std::array{3, 4, 5})[3] == 4);\n  auto two_empty = concatenate(std::array<std::unique_ptr<int>, 0>{},\n                               std::array<std::unique_ptr<int>, 0>{});\n  CHECK(two_empty.empty());\n  std::array<std::unique_ptr<size_t>, 2> two{};\n  two[0] = std::make_unique<size_t>(1);\n  two[1] = std::make_unique<size_t>(2);\n  std::array<std::unique_ptr<size_t>, 3> three{};\n  three[0] = std::make_unique<size_t>(3);\n  three[1] = std::make_unique<size_t>(4);\n  three[2] = std::make_unique<size_t>(5);\n  auto concatenated = concatenate(std::move(two), std::move(three));\n  REQUIRE(concatenated.size() == 5);\n  for (size_t i = 0; i < concatenated.size(); ++i) {\n    CHECK(*gsl::at(concatenated, i) == i + 1);\n  }\n}\n\nvoid test_map_array() {\n  const auto func = [](const int x) { return 2. * x; };\n  CHECK(map_array(std::array<int, 3>{{4, 3, 2}}, func) ==\n        std::array<double, 3>{{8, 6, 4}});\n  CHECK(map_array(std::array<int, 1>{{4}}, func) == std::array<double, 1>{{8}});\n  CHECK(map_array(std::array<int, 0>{}, func) == std::array<double, 0>{});\n\n  // Check function returning a reference\n  CHECK(map_array(std::array<int, 1>{{4}}, [](const int& x) -> const int& {\n          return x;\n        }) == std::array<int, 1>{{4}});\n}\n\nDEFINE_STD_ARRAY_BINOP(int, int, int, operator+, std::plus<>())\nDEFINE_STD_ARRAY_BINOP(double, double, double, f, std::multiplies<>())\n\nDEFINE_STD_ARRAY_INPLACE_BINOP(int, int, operator+=, std::plus<>())\nDEFINE_STD_ARRAY_INPLACE_BINOP(double, double, g, std::multiplies<>())\n\nvoid test_define_std_array_binop() {\n  // tests DEFINE_STD_ARRAY_BINOP\n  CHECK(std::array<int, 2>{{1, 2}} + std::array<int, 2>{{2, 3}} ==\n        std::array<int, 2>{{3, 5}});\n  CHECK(\n      f(std::array<double, 2>{{1.0, 2.0}}, std::array<double, 2>{{2.0, 3.0}}) ==\n      std::array<double, 2>{{2.0, 6.0}});\n\n  // tests DEFINE_STD_ARRAY_INPLACE_BINOP\n  std::array<int, 2> a{{1, 2}};\n  CHECK((a += std::array<int, 2>{{2, 3}}) == std::array<int, 2>{{3, 5}});\n  CHECK(a == std::array<int, 2>{{3, 5}});\n  std::array<double, 2> b{{1.0, 2.0}};\n  CHECK(g(b, std::array<double, 2>{{2.0, 3.0}}) ==\n        std::array<double, 2>{{2.0, 6.0}});\n  CHECK(b == std::array<double, 2>{{2.0, 6.0}});\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.StdArrayHelpers\", \"[DataStructures][Unit]\") {\n  test_arithmetic();\n  test_magnitude();\n  test_dot();\n  test_all_but_specified_element_of();\n  test_insert_element();\n  test_prepend();\n  test_concatenate();\n  test_map_array();\n  test_define_std_array_binop();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_StdHelpers.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <array>\n#include <boost/functional/hash.hpp>\n#include <deque>\n#include <functional>\n#include <list>\n#include <map>\n#include <memory>\n#include <optional>\n#include <set>\n#include <sstream>\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"Framework/TestHelpers.hpp\"\n#include \"Utilities/GetOutput.hpp\"\n#include \"Utilities/StdHelpers.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.StdHelpers.Output\", \"[Utilities][Unit]\") {\n  NonStreamable ns{2};\n  NonStreamable another_ns{4};\n\n  std::list<int> my_list;\n  CHECK(get_output(my_list) == \"()\");\n  my_list = {1};\n  CHECK(get_output(my_list) == \"(1)\");\n  my_list = {{1, 2, 3, 4, 5}};\n  CHECK(get_output(my_list) == \"(1,2,3,4,5)\");\n  std::list<NonStreamable> ns_list{};\n  CHECK(get_output(ns_list) == \"()\");\n  ns_list = {ns};\n  CHECK(get_output(ns_list) == \"(UNSTREAMABLE)\");\n  ns_list = {{ns, ns}};\n  CHECK(get_output(ns_list) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  std::vector<int> my_vector;\n  CHECK(get_output(my_vector) == \"()\");\n  my_vector = {1};\n  CHECK(get_output(my_vector) == \"(1)\");\n  my_vector = {{1, 2, 3, 4, 5}};\n  CHECK(get_output(my_vector) == \"(1,2,3,4,5)\");\n  std::vector<NonStreamable> ns_vector{};\n  CHECK(get_output(ns_vector) == \"()\");\n  ns_vector = {ns};\n  CHECK(get_output(ns_vector) == \"(UNSTREAMABLE)\");\n  ns_vector = {{ns, ns}};\n  CHECK(get_output(ns_vector) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  std::deque<int> my_deque;\n  CHECK(get_output(my_deque) == \"()\");\n  my_deque = {1};\n  CHECK(get_output(my_deque) == \"(1)\");\n  my_deque = {{1, 2, 3, 4, 5}};\n  CHECK(get_output(my_deque) == \"(1,2,3,4,5)\");\n  std::deque<NonStreamable> ns_deque;\n  CHECK(get_output(ns_deque) == \"()\");\n  ns_deque = {ns};\n  CHECK(get_output(ns_deque) == \"(UNSTREAMABLE)\");\n  ns_deque = {{ns, ns}};\n  CHECK(get_output(ns_deque) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  std::array<int, 0> a0{};\n  CHECK(get_output(a0) == \"()\");\n  std::array<int, 1> a1{{1}};\n  CHECK(get_output(a1) == \"(1)\");\n  std::array<int, 5> a5{{1, 2, 3, 4, 5}};\n  CHECK(get_output(a5) == \"(1,2,3,4,5)\");\n  std::array<NonStreamable, 0> ns_array0{};\n  CHECK(get_output(ns_array0) == \"()\");\n  std::array<NonStreamable, 1> ns_array1{{ns}};\n  CHECK(get_output(ns_array1) == \"(UNSTREAMABLE)\");\n  std::array<NonStreamable, 2> ns_array2 = {{ns, ns}};\n  CHECK(get_output(ns_array2) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  auto tuple1 = std::make_tuple<int, double, std::string>(1, 1.87, \"test\");\n  CHECK(get_output(tuple1) == \"(1,1.87,test)\");\n  std::tuple<> tuple0{};\n  CHECK(get_output(tuple0) == \"()\");\n  auto tuple_ns = std::make_tuple<int, double, NonStreamable>(1, 1.87, {});\n  CHECK(get_output(tuple_ns) == \"(1,1.87,UNSTREAMABLE)\");\n\n  std::optional<int> opt{};\n  CHECK(get_output(opt) == \"--\");\n  opt = -42;\n  CHECK(get_output(opt) == \"-42\");\n  opt = std::nullopt;\n  CHECK(get_output(opt) == \"--\");\n  std::optional<NonStreamable> opt_ns{};\n  CHECK(get_output(opt_ns) == \"--\");\n  opt_ns = ns;\n  CHECK(get_output(opt_ns) == \"UNSTREAMABLE\");\n  opt_ns = std::nullopt;\n  CHECK(get_output(opt_ns) == \"--\");\n\n  std::unordered_map<std::string, int, boost::hash<std::string>>\n      my_unordered_map;\n  CHECK(get_output(my_unordered_map) == \"()\");\n  CHECK(keys_of(my_unordered_map) == \"()\");\n  my_unordered_map[\"aaa\"] = 1;\n  CHECK(get_output(my_unordered_map) == \"([aaa,1])\");\n  CHECK(keys_of(my_unordered_map) == \"(aaa)\");\n  my_unordered_map[\"bbb\"] = 2;\n  my_unordered_map[\"ccc\"] = 3;\n  my_unordered_map[\"ddd\"] = 4;\n  my_unordered_map[\"eee\"] = 5;\n  CHECK(get_output(my_unordered_map) ==\n        \"([aaa,1],[bbb,2],[ccc,3],[ddd,4],[eee,5])\");\n  CHECK(keys_of(my_unordered_map) == \"(aaa,bbb,ccc,ddd,eee)\");\n  std::unordered_map<int, NonStreamable> ns_value_unordered_map;\n  CHECK(get_output(ns_value_unordered_map) == \"()\");\n  CHECK(keys_of(ns_value_unordered_map) == \"()\");\n  ns_value_unordered_map[1] = ns;\n  CHECK(get_output(ns_value_unordered_map) == \"([1,UNSTREAMABLE])\");\n  CHECK(keys_of(ns_value_unordered_map) == \"(1)\");\n  ns_value_unordered_map[3] = ns;\n  CHECK(get_output(ns_value_unordered_map) ==\n        \"([1,UNSTREAMABLE],[3,UNSTREAMABLE])\");\n  CHECK(keys_of(ns_value_unordered_map) == \"(1,3)\");\n  std::unordered_map<NonStreamable, int> ns_key_unordered_map;\n  CHECK(get_output(ns_key_unordered_map) == \"()\");\n  CHECK(keys_of(ns_key_unordered_map) == \"()\");\n  ns_key_unordered_map[ns] = 1;\n  CHECK(get_output(ns_key_unordered_map) == \"([UNSTREAMABLE,1])\");\n  CHECK(keys_of(ns_key_unordered_map) == \"(UNSTREAMABLE)\");\n  ns_key_unordered_map[NonStreamable{4}] = 3;\n  CHECK(get_output(ns_key_unordered_map) ==\n        \"([UNSTREAMABLE,1],[UNSTREAMABLE,3])\");\n  CHECK(keys_of(ns_key_unordered_map) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  // check map with some other comparison op\n  std::map<std::string, int, std::greater<>> my_map;\n  CHECK(get_output(my_map) == \"()\");\n  CHECK(keys_of(my_map) == \"()\");\n  my_map[\"aaa\"] = 1;\n  CHECK(get_output(my_map) == \"([aaa,1])\");\n  CHECK(keys_of(my_map) == \"(aaa)\");\n  my_map[\"bbb\"] = 2;\n  my_map[\"ccc\"] = 3;\n  my_map[\"ddd\"] = 4;\n  my_map[\"eee\"] = 5;\n  CHECK(get_output(my_map) == \"([eee,5],[ddd,4],[ccc,3],[bbb,2],[aaa,1])\");\n  CHECK(keys_of(my_map) == \"(eee,ddd,ccc,bbb,aaa)\");\n\n  std::map<int, NonStreamable> ns_value_map;\n  CHECK(get_output(ns_value_map) == \"()\");\n  CHECK(keys_of(ns_value_map) == \"()\");\n  ns_value_map[1] = ns;\n  CHECK(get_output(ns_value_map) == \"([1,UNSTREAMABLE])\");\n  CHECK(keys_of(ns_value_map) == \"(1)\");\n  ns_value_map[3] = ns;\n  CHECK(get_output(ns_value_map) == \"([1,UNSTREAMABLE],[3,UNSTREAMABLE])\");\n  CHECK(keys_of(ns_value_map) == \"(1,3)\");\n  std::map<NonStreamable, int> ns_key_map;\n  CHECK(get_output(ns_key_map) == \"()\");\n  CHECK(keys_of(ns_key_map) == \"()\");\n  ns_key_map[ns] = 1;\n  CHECK(get_output(ns_key_map) == \"([UNSTREAMABLE,1])\");\n  CHECK(keys_of(ns_key_map) == \"(UNSTREAMABLE)\");\n  ns_key_map[NonStreamable{4}] = 3;\n  CHECK(get_output(ns_key_map) == \"([UNSTREAMABLE,1],[UNSTREAMABLE,3])\");\n  CHECK(keys_of(ns_key_map) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  std::unordered_set<int> my_unordered_set{1, 3, 4, 5};\n  CHECK(get_output(my_unordered_set) == \"(1,3,4,5)\");\n  std::unordered_set<int, boost::hash<int>> my_boost_unordered_set{1, 3, 4, 5};\n  CHECK(get_output(my_boost_unordered_set) == \"(1,3,4,5)\");\n  std::unordered_set<NonStreamable> ns_unordered_set{ns, another_ns};\n  CHECK(get_output(ns_unordered_set) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  std::unordered_multiset<int> my_unordered_multiset{1, 3, 1, 5};\n  CHECK(get_output(my_unordered_multiset) == \"(1,1,3,5)\");\n  std::unordered_multiset<int, boost::hash<int>> my_boost_unordered_multiset{\n      1, 3, 1, 5};\n  CHECK(get_output(my_boost_unordered_multiset) == \"(1,1,3,5)\");\n  std::unordered_multiset<NonStreamable> ns_unordered_multiset{ns, ns};\n  CHECK(get_output(ns_unordered_multiset) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  std::set<int> my_set{1, 3, 4, 5};\n  CHECK(get_output(my_set) == \"(1,3,4,5)\");\n  std::unordered_set<NonStreamable> ns_set{ns, another_ns};\n  CHECK(get_output(ns_set) == \"(UNSTREAMABLE,UNSTREAMABLE)\");\n\n  auto my_unique = std::make_unique<double>(6.7);\n  CHECK(\"6.7\" == get_output(my_unique));\n  auto my_shared = std::make_shared<double>(6.7);\n  CHECK(\"6.7\" == get_output(my_shared));\n\n  auto my_pair = std::make_pair(7.8, \"test\"s);\n  CHECK(\"(7.8, test)\" == get_output(my_pair));\n  auto ns_pair = std::make_pair(7.8, ns);\n  CHECK(\"(7.8, UNSTREAMABLE)\" == get_output(ns_pair));\n\n  CHECK(\"1.19e+01 10 test\" ==\n        formatted_string(\"%1.2e %d %s\", 11.87, 10, \"test\"));\n\n  {\n    std::ostringstream ss{};\n    print_stl(ss, 5);\n    CHECK(ss.str() == get_output(5));\n  }\n  {\n    std::ostringstream ss{};\n    print_stl(ss, std::vector{5});\n    CHECK(ss.str() == get_output(std::vector{5}));\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_StlBoilerplate.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <iterator>\n#include <type_traits>\n#include <vector>\n\n#include \"Utilities/ErrorHandling/Error.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/StlBoilerplate.hpp\"\n#include \"Utilities/TMPL.hpp\"\n\nnamespace {\n// Iterate over two std::vectors.\ntemplate <typename T>\nclass TwoVectorIterator\n    : public stl_boilerplate::RandomAccessIterator<TwoVectorIterator<T>, T> {\n  // In a real use would set constness of Vector correctly.\n  using Vector = std::vector<std::remove_const_t<T>>;\n\n public:\n  TwoVectorIterator() = default;\n  TwoVectorIterator(const gsl::not_null<Vector*> v1,\n                    const gsl::not_null<Vector*> v2)\n      : v1_(v1), v2_(v2) {}\n\n  T& operator*() const {\n    if (offset_ < v1_->size()) {\n      return (*v1_)[offset_];\n    } else {\n      return (*v2_)[offset_ - v1_->size()];\n    }\n  }\n\n  TwoVectorIterator& operator+=(const std::ptrdiff_t n) {\n    offset_ += static_cast<size_t>(n);\n    return *this;\n  }\n\n  friend inline bool operator==(const TwoVectorIterator& a,\n                                const TwoVectorIterator& b) {\n    return a.v1_ == b.v1_ and a.v2_ == b.v2_ and a.offset_ == b.offset_;\n  }\n\n  friend inline std::ptrdiff_t operator-(const TwoVectorIterator& a,\n                                         const TwoVectorIterator& b) {\n    return static_cast<std::ptrdiff_t>(a.offset_) -\n           static_cast<std::ptrdiff_t>(b.offset_);\n  }\n\n private:\n  Vector* v1_{nullptr};\n  Vector* v2_{nullptr};\n  size_t offset_{0};\n};\n\n// [RandomAccessIterator]\ntemplate <typename T>\nclass EmptyIterator\n    : public stl_boilerplate::RandomAccessIterator<EmptyIterator<T>, T> {\n public:\n  T& operator*() const { ERROR(\"Not dereferenceable.\"); }\n\n  EmptyIterator& operator+=(const std::ptrdiff_t /*n*/) { return *this; }\n};\n\n// Often these will be friend functions.\ntemplate <typename T>\nbool operator==(const EmptyIterator<T>& /*a*/, const EmptyIterator<T>& /*b*/) {\n  return true;\n}\n\ntemplate <typename T>\nstd::ptrdiff_t operator-(const EmptyIterator<T>& /*a*/,\n                         const EmptyIterator<T>& /*b*/) {\n  return 0;\n}\n// [RandomAccessIterator]\n\nusing empty_traits = std::iterator_traits<EmptyIterator<char>>;\nstatic_assert(std::is_same_v<empty_traits::iterator_category,\n                             std::random_access_iterator_tag>);\nstatic_assert(std::is_same_v<empty_traits::value_type, char>);\nstatic_assert(std::is_same_v<empty_traits::difference_type, std::ptrdiff_t>);\nstatic_assert(std::is_same_v<empty_traits::reference, char&>);\nstatic_assert(std::is_same_v<empty_traits::pointer, char*>);\n\ntemplate <bool CheckConst>\nvoid test_two_vector_iterator() {\n  using Iterator =\n      TwoVectorIterator<tmpl::conditional_t<CheckConst, const int, int>>;\n  using traits = std::iterator_traits<Iterator>;\n  static_assert(std::is_same_v<typename traits::iterator_category,\n                               std::random_access_iterator_tag>);\n  static_assert(std::is_same_v<typename traits::value_type, int>);\n  static_assert(\n      std::is_same_v<typename traits::difference_type, std::ptrdiff_t>);\n  if constexpr (CheckConst) {\n    static_assert(std::is_same_v<typename traits::reference, const int&>);\n    static_assert(std::is_same_v<typename traits::pointer, const int*>);\n  } else {\n    static_assert(std::is_same_v<typename traits::reference, int&>);\n    static_assert(std::is_same_v<typename traits::pointer, int*>);\n  }\n\n  std::vector<int> v1{1, 2};\n  std::vector<int> v2{11, 12};\n  Iterator it(&v1, &v2);\n  Iterator it2 = it;\n  CHECK(*it == 1);\n  {\n    Iterator& incremented = ++it;\n    CHECK(&incremented == &it);\n  }\n  CHECK(*it == 2);\n\n  // input\n  CHECK(it.operator->() == &v1[1]);\n  CHECK_FALSE(it == it2);\n  CHECK(it != it2);\n  ++it2;\n  CHECK(it == it2);\n  CHECK_FALSE(it != it2);\n  CHECK(*it++ == 2);\n  CHECK(*it == 11);\n\n  // output\n  if constexpr (not CheckConst) {\n    *it = 21;\n    CHECK(*it == 21);\n  }\n\n  // forward\n  CHECK(Iterator{} == Iterator{});\n\n  // bidirectional\n  {\n    Iterator& decremented = --it;\n    CHECK(&decremented == &it);\n  }\n  CHECK(*it == 2);\n  CHECK(it == it2);\n  CHECK(*it-- == 2);\n  CHECK(*it == 1);\n\n  // random access\n  {\n    decltype(auto) ret = it += 3;\n    CHECK(&ret == &it);\n  }\n  CHECK(*it == 12);\n  {\n    decltype(auto) ret = it2 + 2;\n    CHECK(&ret != &it2);\n    CHECK(ret != it2);\n    CHECK(ret == it);\n  }\n  CHECK(*it2 == 2);\n  {\n    decltype(auto) ret = 2 + it2;\n    CHECK(&ret != &it2);\n    CHECK(ret != it2);\n    CHECK(ret == it);\n  }\n  CHECK(*it2 == 2);\n  {\n    decltype(auto) ret = it + (-2);\n    CHECK(&ret != &it);\n    CHECK(ret != it);\n    CHECK(ret == it2);\n  }\n  CHECK(*it == 12);\n  {\n    decltype(auto) ret = (-2) + it;\n    CHECK(&ret != &it);\n    CHECK(ret != it);\n    CHECK(ret == it2);\n  }\n  CHECK(*it == 12);\n  {\n    decltype(auto) ret = it - 2;\n    CHECK(&ret != &it);\n    CHECK(ret != it);\n    CHECK(ret == it2);\n  }\n  CHECK(*it == 12);\n  {\n    decltype(auto) ret = it2 - (-2);\n    CHECK(&ret != &it2);\n    CHECK(ret != it2);\n    CHECK(ret == it);\n  }\n  CHECK(*it2 == 2);\n  it -= 2;\n  CHECK(it == it2);\n  it -= -2;\n  CHECK(*it == 12);\n  it += -2;\n  CHECK(it == it2);\n\n  CHECK(it - it2 == 0);\n  it += 2;\n  CHECK(it - it2 == 2);\n  CHECK(it2 - it == -2);\n\n  CHECK(it2[0] == 2);\n  CHECK(&it2[0] == &v1[1]);\n  CHECK(it2[2] == 12);\n  CHECK(it2[-1] == 1);\n  if constexpr (not CheckConst) {\n    it2[2] = 30;\n    CHECK(*it == 30);\n  }\n\n  CHECK(it > it2);\n  CHECK(it >= it2);\n  CHECK_FALSE(it < it2);\n  CHECK_FALSE(it <= it2);\n  CHECK_FALSE(it2 > it);\n  CHECK_FALSE(it2 >= it);\n  CHECK(it2 < it);\n  CHECK(it2 <= it);\n  CHECK_FALSE(it < it);\n  CHECK_FALSE(it > it);\n  CHECK(it <= it);\n  CHECK(it >= it);\n}\n\nvoid test_random_access_iterator() {\n  test_two_vector_iterator<false>();\n  test_two_vector_iterator<true>();\n}\n\n// [RandomAccessSequence]\ntemplate <typename T>\nclass SequenceView\n    : public stl_boilerplate::RandomAccessSequence<SequenceView<T>, T, true> {\n public:\n  SequenceView(const gsl::not_null<T*> data, const size_t size)\n      : data_(data), size_(size) {}\n\n  size_t size() const { return size_; }\n\n  T& operator[](const size_t n) { return *(data_ + n); }\n  const T& operator[](const size_t n) const { return *(data_ + n); }\n\n private:\n  T* data_{nullptr};\n  size_t size_{0};\n};\n// [RandomAccessSequence]\n\ntemplate <bool CheckConst>\nvoid test_sequence_view() {\n  using AccessType = tmpl::conditional_t<CheckConst, const int, int>;\n  using View = SequenceView<AccessType>;\n\n  static_assert(std::is_same_v<typename View::value_type, AccessType>);\n  static_assert(std::is_same_v<typename View::reference, AccessType&>);\n  static_assert(std::is_same_v<typename View::const_reference, const int&>);\n  static_assert(std::is_same_v<typename View::pointer, AccessType*>);\n  static_assert(std::is_same_v<typename View::const_pointer, const int*>);\n  static_assert(std::is_same_v<typename View::difference_type, std::ptrdiff_t>);\n  static_assert(std::is_same_v<typename View::size_type, size_t>);\n  static_assert(std::is_convertible_v<typename View::iterator,\n                                      typename View::const_iterator>);\n  static_assert(\n      std::is_convertible_v<typename std::iterator_traits<\n                                typename View::iterator>::iterator_category,\n                            std::random_access_iterator_tag>);\n  static_assert(std::is_convertible_v<\n                typename std::iterator_traits<\n                    typename View::const_iterator>::iterator_category,\n                std::random_access_iterator_tag>);\n\n  std::array data{1, 2, 3, 4, 5};\n  View view(&data[1], 3);\n  View view_same(&data[1], 3);\n  View view2(&data[1], 2);\n  View view3(&data[0], 3);\n  View view_empty(&data[1], 0);\n\n  const View& cview = view;\n\n  static_assert(\n      std::is_same_v<decltype(view.begin()), typename View::iterator>);\n  static_assert(std::is_same_v<decltype(view.end()), typename View::iterator>);\n  static_assert(\n      std::is_same_v<decltype(view.cbegin()), typename View::const_iterator>);\n  static_assert(\n      std::is_same_v<decltype(view.cend()), typename View::const_iterator>);\n  static_assert(\n      std::is_same_v<decltype(cview.begin()), typename View::const_iterator>);\n  static_assert(\n      std::is_same_v<decltype(cview.end()), typename View::const_iterator>);\n  CHECK(*view.begin() == 2);\n  CHECK(*(view.end() - 1) == 4);\n  CHECK(*cview.begin() == 2);\n  CHECK(*(cview.end() - 1) == 4);\n  {\n    typename View::iterator mi{};\n    typename View::const_iterator ci{};\n    mi = view.begin();\n    ci = view.begin();\n  }\n\n  CHECK(cview == view_same);\n  CHECK_FALSE(cview != view_same);\n  CHECK_FALSE(cview == view2);\n  CHECK(cview != view2);\n  CHECK_FALSE(cview == view3);\n  CHECK(cview != view3);\n  CHECK_FALSE(cview == view_empty);\n  CHECK(cview != view_empty);\n\n  CHECK(cview.size() == 3);\n  CHECK(view_empty.size() == 0);\n  CHECK(not cview.empty());\n  CHECK(view_empty.empty());\n  CHECK(view.end() - view.begin() == static_cast<std::ptrdiff_t>(view.size()));\n  CHECK(cview.end() - cview.begin() ==\n        static_cast<std::ptrdiff_t>(view.size()));\n  CHECK(view_empty.begin() == view_empty.end());\n  CHECK(cview.max_size() >= view.size());\n\n  static_assert(std::is_same_v<typename View::reverse_iterator,\n                               std::reverse_iterator<typename View::iterator>>);\n  static_assert(\n      std::is_same_v<typename View::const_reverse_iterator,\n                     std::reverse_iterator<typename View::const_iterator>>);\n  static_assert(\n      std::is_same_v<decltype(view.rbegin()), typename View::reverse_iterator>);\n  static_assert(\n      std::is_same_v<decltype(view.rend()), typename View::reverse_iterator>);\n  static_assert(std::is_same_v<decltype(view.crbegin()),\n                               typename View::const_reverse_iterator>);\n  static_assert(std::is_same_v<decltype(view.crend()),\n                               typename View::const_reverse_iterator>);\n  static_assert(std::is_same_v<decltype(cview.rbegin()),\n                               typename View::const_reverse_iterator>);\n  static_assert(std::is_same_v<decltype(cview.rend()),\n                               typename View::const_reverse_iterator>);\n  CHECK(*view.rbegin() == 4);\n  CHECK(*(view.rend() - 1) == 2);\n  CHECK(view.rend() - view.rbegin() ==\n        static_cast<std::ptrdiff_t>(view.size()));\n  CHECK(*cview.rbegin() == 4);\n  CHECK(*(cview.rend() - 1) == 2);\n  CHECK(cview.rend() - cview.rbegin() ==\n        static_cast<std::ptrdiff_t>(view.size()));\n\n  CHECK_FALSE(cview < view_same);\n  CHECK_FALSE(cview > view_same);\n  CHECK(cview <= view_same);\n  CHECK(cview >= view_same);\n  CHECK_FALSE(cview < view2);\n  CHECK_FALSE(view2 > cview);\n  CHECK(cview > view2);\n  CHECK(view2 < cview);\n  CHECK_FALSE(cview <= view2);\n  CHECK_FALSE(view2 >= cview);\n  CHECK(cview >= view2);\n  CHECK(view2 <= cview);\n  CHECK_FALSE(cview < view3);\n  CHECK_FALSE(view3 > cview);\n  CHECK(cview > view3);\n  CHECK(view3 < cview);\n  CHECK_FALSE(cview <= view3);\n  CHECK_FALSE(view3 >= cview);\n  CHECK(cview >= view3);\n  CHECK(view3 <= cview);\n  CHECK_FALSE(cview < view_empty);\n  CHECK_FALSE(view_empty > view3);\n  CHECK(cview > view_empty);\n  CHECK(view_empty < cview);\n  CHECK_FALSE(cview <= view_empty);\n  CHECK_FALSE(view_empty >= cview);\n  CHECK(cview >= view_empty);\n  CHECK(view_empty <= cview);\n\n  static_assert(\n      std::is_same_v<decltype(view.front()), decltype(*view.begin())>);\n  CHECK(&view.front() == &*view.begin());\n  static_assert(\n      std::is_same_v<decltype(cview.front()), decltype(*view.cbegin())>);\n  CHECK(&cview.front() == &*view.cbegin());\n  static_assert(\n      std::is_same_v<decltype(view.back()), decltype(*view.rbegin())>);\n  CHECK(&view.back() == &*view.rbegin());\n  static_assert(\n      std::is_same_v<decltype(cview.back()), decltype(*view.crbegin())>);\n  CHECK(&cview.back() == &*view.crbegin());\n\n  CHECK(view[1] == 3);\n  CHECK(&view[1] == &data[2]);\n  CHECK(view.at(1) == 3);\n  CHECK(&view.at(1) == &data[2]);\n  CHECK_THROWS_AS(view.at(view.size()), std::out_of_range);\n  CHECK(cview.at(1) == 3);\n  CHECK(&cview.at(1) == &data[2]);\n  CHECK_THROWS_AS(cview.at(view.size()), std::out_of_range);\n\n  if constexpr (not CheckConst) {\n    CHECK(&(view[1] = 12) == &data[2]);\n    CHECK(&(view.at(1) = 12) == &data[2]);\n    CHECK(&(view.front() = 12) == &data[1]);\n    CHECK(&(view.back() = 12) == &data[3]);\n  }\n}\n\nvoid test_random_access_sequence() {\n  test_sequence_view<false>();\n  test_sequence_view<true>();\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.StlBoilerplate\", \"[Unit][Utilities]\") {\n  test_random_access_iterator();\n  test_random_access_sequence();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_TMPL.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <tuple>\n#include <utility>\n\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits.hpp\"\n\nnamespace {\n// [expand_pack_example]\ntemplate <typename... Elements, size_t... Is>\nvoid transform(const std::tuple<Elements...>& tupull,\n               std::tuple<Elements...>& out_tupull,\n               std::index_sequence<Is...> /*meta*/) {\n  const auto func = [](const auto& in, auto& out) {\n    out = in * static_cast<decltype(in)>(2);\n    return 0;\n  };\n  expand_pack(func(std::get<Is>(tupull), std::get<Is>(out_tupull))...);\n}\n\nvoid test_expand_pack() {\n  std::tuple<int, double, float> my_tupull = std::make_tuple(3, 2.7, 8.2);\n  std::tuple<int, double, float> my_tupull_output;\n  transform(my_tupull, my_tupull_output, std::make_index_sequence<3>{});\n  CHECK(std::get<0>(my_tupull_output) == 6);\n  CHECK(std::get<1>(my_tupull_output) == 5.4);\n  CHECK(std::get<2>(my_tupull_output) == 16.4f);\n}\n// [expand_pack_example]\n\ntemplate <typename>\nstruct Templated {};\n\nstatic_assert(tmpl::list_contains_v<tmpl::list<Templated<int>,\n                                               Templated<double>>,\n                                    Templated<double>>,\n              \"Failed testing list_contains\");\n\nstatic_assert(not tmpl::list_contains_v<tmpl::list<Templated<int>,\n                                                   Templated<double>>,\n                                        double>,\n              \"Failed testing list_contains\");\n\nstatic_assert(\n    std::is_same_v<\n        tmpl::list_difference<tmpl::list<Templated<int>, Templated<double>>,\n                              tmpl::list<double>>,\n        tmpl::list<Templated<int>, Templated<double>>>,\n    \"Failed testing list_difference\");\n\nstatic_assert(\n    std::is_same_v<\n        tmpl::list_difference<tmpl::list<Templated<int>, Templated<double>>,\n                              tmpl::list<Templated<double>>>,\n        tmpl::list<Templated<int>>>,\n    \"Failed testing list_difference\");\n\nstatic_assert(\n    std::is_same_v<\n        tmpl::list_difference<tmpl::list<>, tmpl::list<Templated<double>>>,\n        tmpl::list<>>,\n    \"Failed testing list_difference\");\n\nvoid test_get_first_argument() {\n  const long a0 = 5;\n  const long a1 = 6;\n  const int a2 = -5;\n  const char a3 = '7';\n  CHECK(5 == get_first_argument(a0, a1, a2, a3));\n  CHECK(5 == get_first_argument(a0));\n  CHECK(6 == get_first_argument(a1, a0, a2, a3));\n  CHECK('7' == get_first_argument(a3, a1, a2, a0));\n}\n\n// [expand_pack_left_to_right]\ntemplate <typename... Ts>\nvoid test_expand_pack_left_to_right(const size_t expected,\n                                    tmpl::list<Ts...> /*meta*/) {\n  size_t sum = 0;\n  const auto lambda = [&sum](auto tag) { sum += decltype(tag)::value; };\n  EXPAND_PACK_LEFT_TO_RIGHT(lambda(Ts{}));\n  CHECK(sum == expected);\n}\n// [expand_pack_left_to_right]\n\nstatic_assert(tmpl::as_pack<tmpl::list<tmpl::size_t<1>, tmpl::size_t<2>,\n                                       tmpl::size_t<3>>>([](auto... args) {\n                return (... + tmpl::type_from<decltype(args)>::value);\n              }) == 6);\n\nvoid test_as_pack() {\n  // [as_pack]\n  using List = tmpl::list<tmpl::size_t<1>, tmpl::size_t<2>, tmpl::size_t<3>>;\n  const size_t result = tmpl::as_pack<List>([](auto... args) {\n    return (... + tmpl::type_from<decltype(args)>::value);\n  });\n  CHECK(result == 6);\n  // [as_pack]\n}\n\nstruct Type1;\n\ntemplate <typename... T>\nstruct make_list {\n  using type = tmpl::list<T...>;\n};\n\nvoid test_transform() {\n  using new_list = tmpl::transform<tmpl::list<Type1>, make_list<tmpl::_1>>;\n  static_assert(std::is_same_v<new_list, tmpl::list<tmpl::list<Type1>>>);\n  using new_list_2 = tmpl::transform<tmpl::list<Type1>, tmpl::list<Type1>,\n                                     make_list<tmpl::_1, tmpl::_2>>;\n  static_assert(\n      std::is_same_v<new_list_2, tmpl::list<tmpl::list<Type1, Type1>>>);\n  // using bad_list_1 = tmpl::transform<double, make_list<tmpl::_1>>;\n  // using bad_list_2 =\n  //     tmpl::transform<int, tmpl::list<Type1>, make_list<tmpl::_1, tmpl::_2>>;\n  // using bad_list_3 =\n  //     tmpl::transform<tmpl::list<Type1>, int, make_list<tmpl::_1, tmpl::_2>>;\n  // using bad_list_4 =\n  //     tmpl::transform<double, double, make_list<tmpl::_1, tmpl::_2>>;\n}\n\nvoid test_make_std_variant_over() {\n  static_assert(std::is_same_v<\n                std::variant<char, bool, double>,\n                tmpl::make_std_variant_over<tmpl::list<char, bool, double>>>);\n  static_assert(std::is_same_v<std::variant<char>,\n                               tmpl::make_std_variant_over<tmpl::list<char>>>);\n}\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.TMPL\", \"[Unit][Utilities]\") {\n  test_expand_pack();\n  test_get_first_argument();\n  test_expand_pack_left_to_right(\n      10, tmpl::list<std::integral_constant<size_t, 2>,\n                     std::integral_constant<size_t, 4>,\n                     std::integral_constant<size_t, 1>,\n                     std::integral_constant<size_t, 3>>{});\n  test_as_pack();\n  test_transform();\n  test_make_std_variant_over();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_TMPLDocumentation.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <algorithm>\n#include <cstddef>\n#include <cstdint>\n#include <numeric>\n#include <string>\n#include <type_traits>\n#include <typeindex>\n#include <typeinfo>\n#include <utility>\n#include <vector>\n\n// [include]\n#include \"Utilities/TMPL.hpp\"\n// [include]\n\n// We want the code to be nicely formatted for the documentation, not here.\n// clang-format off\nnamespace {\n\ntemplate <typename T, typename U>\nvoid assert_same() {\n  static_assert(std::is_same_v<T, U>);\n}\n\ntemplate <typename Map, typename Supermap>\nusing is_submap =\n    tmpl::all<tmpl::keys_as_sequence<Map, tmpl::list>,\n              std::is_same<tmpl::lazy::lookup<tmpl::pin<Map>, tmpl::_1>,\n                           tmpl::lazy::lookup<tmpl::pin<Supermap>, tmpl::_1>>>;\n\ntemplate <typename T, typename U>\nvoid assert_maps_same() {\n  static_assert(is_submap<T, U>::value and is_submap<U, T>::value);\n}\n\ntemplate <typename Set, typename Superset>\nusing is_subset =\n    tmpl::all<Set, tmpl::bind<tmpl::contains, tmpl::pin<Superset>, tmpl::_1>>;\n\ntemplate <typename T, typename U>\nvoid assert_sets_same() {\n  static_assert(is_subset<T, U>::value and is_subset<U, T>::value);\n}\n\n#define HAS_LAZY_VERSION(name) \\\n  assert_same<tmpl::wrap<lazy_test_arguments, tmpl::lazy::name>::type, \\\n              tmpl::wrap<lazy_test_arguments, tmpl::name>>()\n\n// [example_declarations]\nstruct Type1;\nstruct Type2;\nstruct Type3;\n\n// Sequence containers.  Practical applications will usually use\n// tmpl::list in place of these.\ntemplate <typename...>\nstruct List1;\ntemplate <typename...>\nstruct List2;\ntemplate <typename...>\nstruct List3;\n\ntemplate <typename>\nstruct Wrapper;\n\ntemplate <typename... T>\nstruct lazy_make_list1 {\n  using type = List1<T...>;\n};\n\n// A comparator on the above types defining Type1 < Type2 < Type3\nusing CompareType123 = tmpl::and_<tmpl::or_<std::is_same<tmpl::_1, Type1>,\n                                            std::is_same<tmpl::_2, Type3>>,\n                                  tmpl::not_<std::is_same<tmpl::_1, tmpl::_2>>>;\n// [example_declarations]\n\nnamespace metafunctions {\n// [metafunctions:lazy]\ntemplate <typename T>\nstruct lazy_make_list_of_T_and_int {\n  using type = List1<T, int>;\n};\n// [metafunctions:lazy]\n\n// [metafunctions:eager]\ntemplate <typename T>\nusing eager_make_list_of_T_and_int = List1<T, int>;\n// [metafunctions:eager]\n\n// [metafunctions:eager_from_lazy]\ntemplate <typename T>\nusing eager_make_list_of_T_and_int2 =\n    typename lazy_make_list_of_T_and_int<T>::type;\n// [metafunctions:eager_from_lazy]\n\n// [metafunctions:call_lazy_metafunction]\ntemplate <typename Func, typename... Args>\nstruct apply_lazy_metafunction;\ntemplate <template <typename...> typename Func, typename... DummyArgs,\n          typename... Args>\nstruct apply_lazy_metafunction<Func<DummyArgs...>, Args...> {\n  using type = typename Func<Args...>::type;\n};\n\ntemplate <typename Func>\nusing call_lazy_metafunction_twice =\n    List1<typename apply_lazy_metafunction<Func, double>::type,\n          typename apply_lazy_metafunction<Func, char>::type>;\n// [metafunctions:call_lazy_metafunction]\n\n// [metafunctions:call_eager_metafunction]\ntemplate <typename Func, typename... Args>\nstruct apply_eager_metafunction;\ntemplate <template <typename...> typename Func, typename... DummyArgs,\n          typename... Args>\nstruct apply_eager_metafunction<Func<DummyArgs...>, Args...> {\n  using type = Func<Args...>;\n};\n\ntemplate <typename Func>\nusing call_eager_metafunction_twice =\n    List1<typename apply_eager_metafunction<Func, double>::type,\n          typename apply_eager_metafunction<Func, char>::type>;\n// [metafunctions:call_eager_metafunction]\n\nnamespace pin_protect_eager {\n// [tmpl::pin:protect_eager]\ntemplate <typename T>\nusing is_int = tmpl::apply<std::is_same<tmpl::pin<T>, int>>;\nstatic_assert(is_int<int>::value);\n// Breaks without tmpl::pin: tmpl::list<> has no ::type\nstatic_assert(not is_int<tmpl::list<>>::value);\n// [tmpl::pin:protect_eager]\n}  // namespace pin_protect_eager\n\n// [metafunctions:evens]\ntemplate <typename L>\nusing evens = tmpl::filter<\n  L, tmpl::equal_to<tmpl::modulo<tmpl::_1, tmpl::integral_constant<int, 2>>,\n                    tmpl::integral_constant<int, 0>>>;\n// [metafunctions:evens]\n\n// [metafunctions:maybe_first]\ntemplate <typename L>\nusing maybe_first = tmpl::apply<tmpl::apply<\n  tmpl::if_<std::bool_constant<(tmpl::size<L>::value != 0)>,\n            tmpl::defer<tmpl::bind<tmpl::front, tmpl::pin<L>>>,\n            tmpl::no_such_type_>>>;\n// [metafunctions:maybe_first]\n\n// [metafunctions:factorial]\ntemplate <typename N>\nusing factorial =\n  tmpl::fold<tmpl::range<typename N::value_type, 1, N::value + 1>,\n             tmpl::integral_constant<typename N::value_type, 1>,\n             tmpl::times<tmpl::_state, tmpl::_element>>;\n// [metafunctions:factorial]\n\n// [metafunctions:factorial_cpp]\nsize_t factorial_cpp(const size_t n) {\n  std::vector<size_t> range(n);\n  std::iota(range.begin(), range.end(), 1);\n  size_t state = 1;\n  for (size_t i : range) {\n    state = state * i;\n  }\n  return state;\n}\n// [metafunctions:factorial_cpp]\n\n// [metafunctions:make_subtracter]\ntemplate <typename N>\nusing make_subtracter =\n    tmpl::apply<tmpl::defer<tmpl::minus<tmpl::_1, tmpl::parent<tmpl::_1>>>, N>;\n// [metafunctions:make_subtracter]\n\n// [metafunctions:make_subtracter_simple]\ntemplate <typename N>\nusing make_subtracter_simple = tmpl::minus<tmpl::_1, tmpl::pin<N>>;\n// [metafunctions:make_subtracter_simple]\n\n// [metafunctions:multiplication_table]\ntemplate <typename N>\nusing multiplication_table =\n  tmpl::transform<\n    tmpl::range<typename N::value_type, 1, N::value + 1>,\n    tmpl::lazy::transform<\n      tmpl::pin<tmpl::range<typename N::value_type, 1, N::value + 1>>,\n      tmpl::defer<tmpl::times<tmpl::_1, tmpl::parent<tmpl::_1>>>>>;\n// [metafunctions:multiplication_table]\n\n// [metafunctions:column_with_zeros]\ntemplate <typename Lists, typename Column>\nusing column_with_zeros =\n  tmpl::transform<\n    Lists,\n    tmpl::bind<\n      tmpl::apply,\n      tmpl::if_<\n        tmpl::greater<tmpl::bind<tmpl::size, tmpl::_1>, Column>,\n        tmpl::defer<  // avoid out-of-range call to `at`\n          tmpl::parent<\n            tmpl::bind<tmpl::at, tmpl::_1, Column>>>,\n        tmpl::size_t<0>>>>;\n// [metafunctions:column_with_zeros]\n\n// [metafunctions:factorial_recursion]\ntemplate <typename N>\nusing factorial_recursion =\n  tmpl::apply<\n    tmpl::bind<tmpl::apply, tmpl::_1, tmpl::_1, N>,\n    tmpl::bind<  // recursive metalambda starts here\n      tmpl::apply,\n      tmpl::if_<\n        tmpl::not_equal_to<tmpl::_2, tmpl::size_t<0>>,\n        tmpl::defer<  // prevent speculative recursion\n          tmpl::parent<\n            tmpl::times<\n              tmpl::_2,\n              tmpl::bind<tmpl::apply, tmpl::_1,\n                         tmpl::_1, tmpl::prev<tmpl::_2>>>>>,\n        tmpl::integral_constant<typename N::value_type, 1>>>>;\n// [metafunctions:factorial_recursion]\n\n// [metafunctions:primes]\ntemplate <typename N>\nusing zero = tmpl::integral_constant<typename N::value_type, 0>;\n\n// Return Sequence with the Nth element replaced by NewType.\ntemplate <typename Sequence, typename N, typename NewType>\nusing replace_at =\n  tmpl::append<tmpl::front<tmpl::split_at<Sequence, N>>,\n               tmpl::list<NewType>,\n               tmpl::pop_front<tmpl::back<tmpl::split_at<Sequence, N>>>>;\n\ntemplate <typename Start, typename End>\nusing range_from_types =\n  tmpl::range<typename Start::value_type, Start::value, End::value>;\n\ntemplate <typename N>\nusing primes =\n  tmpl::remove<\n    tmpl::fold<\n      tmpl::range<typename N::value_type, 2, N::value>,\n      tmpl::push_front<\n        tmpl::range<typename N::value_type, 2, N::value>, zero<N>, zero<N>>,\n      tmpl::bind<\n        tmpl::apply,\n        tmpl::if_<  // Skip work for known-composite entries\n          tmpl::or_<\n            std::is_same<\n              tmpl::bind<tmpl::at, tmpl::_state, tmpl::_element>, zero<N>>,\n            // Only iteration up to sqrt(N) is necessary\n            tmpl::greater_equal<\n              tmpl::times<tmpl::_element, tmpl::_element>, N>>,\n          tmpl::defer<  // Match other branch (don't execute the state)\n            tmpl::parent<tmpl::_state>>,\n          tmpl::defer<\n            tmpl::parent<\n              tmpl::lazy::fold<\n                tmpl::bind<\n                  range_from_types,\n                  tmpl::_element,\n                  tmpl::next<tmpl::divides<tmpl::prev<N>, tmpl::_element>>>,\n                tmpl::_state,\n                tmpl::defer<  // Passed as a closure to the inner fold\n                  tmpl::bind<\n                    replace_at,\n                    tmpl::_state,\n                    tmpl::times<tmpl::parent<tmpl::_element>, tmpl::_element>,\n                    zero<N>>>>>>>>>,\n    zero<N>>;\n// [metafunctions:primes]\n\n// [metafunctions:primes_cpp]\nstd::vector<size_t> primes_cpp(const size_t n) {\n  std::vector<size_t> sieve(n, 0);\n  std::iota(sieve.begin() + 2, sieve.end(), 2);\n  for (size_t i = 2; i < n; ++i) {\n    if (not (sieve[i] == 0 or i * i >= n)) {\n      for (size_t j = i; j < (n - 1) / i + 1; ++j) {\n        sieve[i * j] = 0;\n      }\n    }\n  }\n  std::vector<size_t> result;\n  std::copy_if(sieve.begin(), sieve.end(),\n               std::insert_iterator(result, result.begin()),\n               [](const size_t x) { return x != 0; });\n  return result;\n}\n// [metafunctions:primes_cpp]\n\nvoid run() {\n// [metafunctions:agreement]\nassert_same<eager_make_list_of_T_and_int<double>,\n            eager_make_list_of_T_and_int2<double>>();\n// [metafunctions:agreement]\n\n// [metafunctions:call_lazy_metafunction_assert]\nstruct Dummy;\n\nassert_same<call_lazy_metafunction_twice<lazy_make_list_of_T_and_int<Dummy>>,\n            List1<List1<double, int>, List1<char, int>>>();\n// [metafunctions:call_lazy_metafunction_assert]\n\n// [metafunctions:call_eager_metafunction_assert]\nassert_same<call_eager_metafunction_twice<eager_make_list_of_T_and_int<Dummy>>,\n            List1<List1<double>, List1<char>>>();\n// [metafunctions:call_eager_metafunction_assert]\n\n// [tmpl::args]\nstatic_assert(not std::is_same_v<tmpl::_1, tmpl::args<0>>);\nstatic_assert(not std::is_same_v<tmpl::_2, tmpl::args<1>>);\nstatic_assert(std::is_same_v<tmpl::_3, tmpl::args<2>>);\nstatic_assert(std::is_same_v<tmpl::_4, tmpl::args<3>>);\n// [tmpl::args]\n\n// [tmpl::args:eval]\nassert_same<tmpl::apply<tmpl::_1, Type1, Type2>, Type1>();\nassert_same<tmpl::apply<tmpl::_2, Type1, Type2>, Type2>();\nassert_same<tmpl::apply<tmpl::args<0>, Type1, Type2>, Type1>();\n// [tmpl::args:eval]\n\n// [metalambda_lazy]\nassert_same<tmpl::apply<lazy_make_list1<tmpl::_1, tmpl::_2>, Type1, Type2>,\n            List1<Type1, Type2>>();\n// [metalambda_lazy]\n\n// [tmpl::bind]\nassert_same<tmpl::apply<tmpl::bind<List1, tmpl::_1, tmpl::_2>, Type1, Type2>,\n            List1<Type1, Type2>>();\n// [tmpl::bind]\n\n// [tmpl::pin]\nassert_same<tmpl::apply<tmpl::pin<List1<Type1>>>, List1<Type1>>();\n// Error: List1 is not a lazy metafunction\n// assert_same<tmpl::apply<List1<Type1>>, List1<Type1>>();\nassert_same<tmpl::apply<tmpl::pin<tmpl::_1>, Type1>, tmpl::_1>();\n// [tmpl::pin]\n\n// [tmpl::defer]\nassert_same<\n  tmpl::apply<tmpl::apply<tmpl::defer<tmpl::_1>,\n                          Type1>,\n              Type2>,\n  Type2>();\n// [tmpl::defer]\n\n// [tmpl::parent]\nassert_same<\n  tmpl::apply<tmpl::apply<tmpl::defer<tmpl::parent<tmpl::_1>>,\n                          Type1>,\n              Type2>,\n  Type1>();\n// [tmpl::parent]\n\n// [metalambda_constant]\nassert_same<tmpl::apply<Type1>, Type1>();\n// [metalambda_constant]\n\n// [metafunctions:evens:asserts]\nassert_same<evens<tmpl::integral_list<int, 1, 1, 2, 3, 5, 8, 13>>,\n            tmpl::integral_list<int, 2, 8>>();\n// [metafunctions:evens:asserts]\n\n// [metafunctions:maybe_first:asserts]\nassert_same<maybe_first<tmpl::list<Type1>>, Type1>();\nassert_same<maybe_first<tmpl::list<Type1, Type2>>, Type1>();\nassert_same<maybe_first<tmpl::list<>>, tmpl::no_such_type_>();\n// [metafunctions:maybe_first:asserts]\n\n// [metafunctions:factorial:asserts]\nassert_same<factorial<tmpl::size_t<5>>, tmpl::size_t<120>>();\n// [metafunctions:factorial:asserts]\nCHECK(factorial_cpp(5) == 120);\n\n{\n// [metafunctions:make_subtracter:asserts]\nusing subtract_three = make_subtracter<tmpl::size_t<3>>;\nassert_same<tmpl::apply<subtract_three, tmpl::size_t<5>>, tmpl::size_t<2>>();\n// [metafunctions:make_subtracter:asserts]\nassert_same<tmpl::apply<make_subtracter_simple<tmpl::size_t<3>>,\n                        tmpl::size_t<5>>,\n            tmpl::size_t<2>>();\n}\n\n// [metafunctions:multiplication_table:asserts]\nassert_same<multiplication_table<tmpl::size_t<5>>,\n            tmpl::list<tmpl::integral_list<size_t, 1, 2, 3, 4, 5>,\n                       tmpl::integral_list<size_t, 2, 4, 6, 8, 10>,\n                       tmpl::integral_list<size_t, 3, 6, 9, 12, 15>,\n                       tmpl::integral_list<size_t, 4, 8, 12, 16, 20>,\n                       tmpl::integral_list<size_t, 5, 10, 15, 20, 25>>>();\n// [metafunctions:multiplication_table:asserts]\n\n// [metafunctions:factorial_recursion:asserts]\nassert_same<factorial_recursion<tmpl::size_t<5>>, tmpl::size_t<120>>();\n// [metafunctions:factorial_recursion:asserts]\n\n// [metafunctions:column_with_zeros:asserts]\nassert_same<\n  column_with_zeros<\n    tmpl::list<tmpl::integral_list<size_t, 11, 12, 13>,\n               tmpl::integral_list<size_t, 21, 22, 23, 24, 25>,\n               tmpl::integral_list<size_t, 31, 32, 33, 34>>,\n    tmpl::size_t<3>>,\n  tmpl::integral_list<size_t, 0, 24, 34>>();\n// [metafunctions:column_with_zeros:asserts]\n\n// [metafunctions:primes:asserts]\nassert_same<\n  primes<tmpl::size_t<100>>,\n  tmpl::integral_list<size_t, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,\n                      43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97>>();\n// [metafunctions:primes:asserts]\n// Check edge cases:\n// Prime\nassert_same<\n  primes<tmpl::size_t<11>>,\n  tmpl::integral_list<size_t, 2, 3, 5, 7>>();\n// Prime + 1\nassert_same<\n  primes<tmpl::size_t<12>>,\n  tmpl::integral_list<size_t, 2, 3, 5, 7, 11>>();\n// Prime^2\nassert_same<\n  primes<tmpl::size_t<25>>,\n  tmpl::integral_list<size_t, 2, 3, 5, 7, 11, 13, 17, 19, 23>>();\n\nCHECK(primes_cpp(100) ==\n      std::vector<size_t>{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,\n                          43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97});\nCHECK(primes_cpp(11) == std::vector<size_t>{2, 3, 5, 7});\nCHECK(primes_cpp(12) == std::vector<size_t>{2, 3, 5, 7, 11});\nCHECK(primes_cpp(25) == std::vector<size_t>{2, 3, 5, 7, 11, 13, 17, 19, 23});\n}\n}  // namespace metafunctions\n\nnamespace guidelines {\nnamespace lazy_eager {\n// [guidelines:lazy_eager]\ntemplate <typename T>\nstruct always_true : std::true_type {};\ntemplate <typename T>\nusing always_true_t = typename always_true<T>::type;\ntemplate <typename T>\nconstexpr bool always_true_v = always_true<T>::value;\n// [guidelines:lazy_eager]\nstatic_assert(std::is_same_v<always_true_t<int>, std::true_type>);\nstatic_assert(always_true_v<int>);\n}  // namespace lazy_eager\n}  // namespace guidelines\n\nnamespace containers {\nvoid run() {\n// [tmpl::integral_constant]\nusing T = tmpl::integral_constant<int, 3>;\nassert_same<T::value_type, int>();\nassert_same<T::type, T>();\nstatic_assert(T::value == 3);\n\n// At runtime only\nCHECK(T{} == 3);\nCHECK(T{}() == 3);\n// [tmpl::integral_constant]\n\n// [tmpl::integral_constant::abbreviations]\nassert_same<tmpl::int8_t<3>, tmpl::integral_constant<int8_t, 3>>();\nassert_same<tmpl::int16_t<3>, tmpl::integral_constant<int16_t, 3>>();\nassert_same<tmpl::int32_t<3>, tmpl::integral_constant<int32_t, 3>>();\nassert_same<tmpl::int64_t<3>, tmpl::integral_constant<int64_t, 3>>();\n\nassert_same<tmpl::uint8_t<3>, tmpl::integral_constant<uint8_t, 3>>();\nassert_same<tmpl::uint16_t<3>, tmpl::integral_constant<uint16_t, 3>>();\nassert_same<tmpl::uint32_t<3>, tmpl::integral_constant<uint32_t, 3>>();\nassert_same<tmpl::uint64_t<3>, tmpl::integral_constant<uint64_t, 3>>();\n\nassert_same<tmpl::size_t<3>, tmpl::integral_constant<size_t, 3>>();\nassert_same<tmpl::ptrdiff_t<3>, tmpl::integral_constant<ptrdiff_t, 3>>();\nassert_same<tmpl::bool_<true>, tmpl::integral_constant<bool, true>>();\n// [tmpl::integral_constant::abbreviations]\n\n// [tmpl::list]\nstatic_assert(not std::is_same_v<tmpl::list<Type1, Type2>,\n                                 tmpl::list<Type2, Type1>>);\n// [tmpl::list]\n\n// [tmpl::map]\nassert_same<tmpl::lookup<tmpl::map<tmpl::pair<Type1, int>,\n                                   tmpl::pair<Type2, double>>,\n                         Type1>,\n            int>();\n// [tmpl::map]\n\n// [tmpl::pair]\nassert_same<tmpl::pair<Type1, Type2>::first_type, Type1>();\nassert_same<tmpl::pair<Type1, Type2>::second_type, Type2>();\n// [tmpl::pair]\n\n// [tmpl::set]\nassert_same<tmpl::contains<tmpl::set<Type1, Type2>, Type1>, tmpl::true_type>();\n// [tmpl::set]\n\n// [tmpl::type_]\nassert_same<tmpl::type_<Type1>::type, Type1>();\n// [tmpl::type_]\n}\n}  // namespace containers\n\nnamespace constants {\nvoid run() {\n// [tmpl::empty_base]\nassert_same<tmpl::inherit_linearly<List1<>, List2<>>, tmpl::empty_base>();\n// [tmpl::empty_base]\n\n// [tmpl::empty_sequence]\nassert_same<tmpl::empty_sequence, tmpl::list<>>();\n// [tmpl::empty_sequence]\n\n// [tmpl::false_type]\nassert_same<tmpl::false_type, tmpl::bool_<false>>();\n// [tmpl::false_type]\n\n// [tmpl::no_such_type_]\nassert_same<tmpl::index_of<List1<>, Type1>, tmpl::no_such_type_>();\n// [tmpl::no_such_type_]\n\n// [tmpl::true_type]\nassert_same<tmpl::true_type, tmpl::bool_<true>>();\n// [tmpl::true_type]\n}\n}  // namespace constants\n\nnamespace list_constructors {\nvoid run() {\n// [tmpl::filled_list]\nassert_same<tmpl::filled_list<Type1, 3, List1>, List1<Type1, Type1, Type1>>();\nassert_same<tmpl::filled_list<Type1, 3>, tmpl::list<Type1, Type1, Type1>>();\n// [tmpl::filled_list]\n\n// [tmpl::integral_list]\nassert_same<tmpl::integral_list<int, 3, 2, 1>,\n            tmpl::list<tmpl::integral_constant<int, 3>,\n                       tmpl::integral_constant<int, 2>,\n                       tmpl::integral_constant<int, 1>>>();\n// [tmpl::integral_list]\n\n// [tmpl::make_sequence]\nassert_same<tmpl::make_sequence<tmpl::size_t<5>, 3>,\n            tmpl::list<tmpl::size_t<5>, tmpl::size_t<6>, tmpl::size_t<7>>>();\nassert_same<tmpl::make_sequence<Type1, 3, lazy_make_list1<tmpl::_1>, List2>,\n            List2<Type1, List1<Type1>, List1<List1<Type1>>>>();\n// [tmpl::make_sequence]\n\n// [tmpl::range]\nassert_same<tmpl::range<size_t, 4, 7>,\n            tmpl::list<tmpl::size_t<4>, tmpl::size_t<5>, tmpl::size_t<6>>>();\nassert_same<tmpl::range<size_t, 4, 4>, tmpl::list<>>();\n// [tmpl::range]\n\n// [tmpl::reverse_range]\nassert_same<tmpl::reverse_range<size_t, 7, 4>,\n            tmpl::list<tmpl::size_t<7>, tmpl::size_t<6>, tmpl::size_t<5>>>();\nassert_same<tmpl::reverse_range<size_t, 7, 7>, tmpl::list<>>();\n// [tmpl::reverse_range]\n}\n}  // namespace list_constructors\n\nnamespace list_query {\nvoid run() {\n// [tmpl::all]\nstatic_assert(not tmpl::all<\n  List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>>::value);\nstatic_assert(tmpl::all<\n  List1<tmpl::size_t<1>, tmpl::size_t<1>, tmpl::size_t<2>>>::value);\nstatic_assert(not tmpl::all<\n  List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>,\n  tmpl::less<tmpl::_1, tmpl::size_t<2>>>::value);\nstatic_assert(tmpl::all<\n  List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<0>>,\n  tmpl::less<tmpl::_1, tmpl::size_t<2>>>::value);\nstatic_assert(tmpl::all<List1<>>::value);\n// [tmpl::all]\n\n// [tmpl::all:inhomogeneous]\n#ifndef __CUDACC__\nstatic_assert(not tmpl::all<\n  List1<std::true_type, tmpl::true_type>, tmpl::_1>::value);\n#endif\n// [tmpl::all:inhomogeneous]\n\n// [tmpl::any]\nstatic_assert(tmpl::any<\n  List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>>::value);\nstatic_assert(not tmpl::any<\n  List1<tmpl::size_t<0>, tmpl::size_t<0>, tmpl::size_t<0>>>::value);\nstatic_assert(tmpl::any<\n  List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>,\n  tmpl::less<tmpl::_1, tmpl::size_t<2>>>::value);\nstatic_assert(not tmpl::any<\n  List1<tmpl::size_t<4>, tmpl::size_t<3>, tmpl::size_t<2>>,\n  tmpl::less<tmpl::_1, tmpl::size_t<2>>>::value);\nstatic_assert(not tmpl::any<List1<>>::value);\n// [tmpl::any]\n\n// [tmpl::any:inhomogeneous]\n#ifndef __CUDACC__\nstatic_assert(tmpl::any<\n  List1<std::false_type, tmpl::false_type>, tmpl::_1>::value);\n#endif\n// [tmpl::any:inhomogeneous]\n\n// [tmpl::at]\nassert_same<tmpl::at<List1<Type1, Type2, Type3>, tmpl::size_t<0>>, Type1>();\n// [tmpl::at]\n\n// [tmpl::at_c]\nassert_same<tmpl::at_c<List1<Type1, Type2, Type3>, 0>, Type1>();\n// [tmpl::at_c]\n\n// [tmpl::back]\nassert_same<tmpl::back<List1<Type1, Type2, Type3>>, Type3>();\n// [tmpl::back]\n\n// [tmpl::count_if]\nassert_same<tmpl::count_if<List1<Type1, Type2, Type1>,\n                           std::is_same<tmpl::_1, Type1>>,\n            tmpl::integral_constant<size_t, 2>>();\n// [tmpl::count_if]\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List2<Type1, Type2>, Type3,\n             lazy_make_list1<tmpl::_state, tmpl::_element>>;\n// [tmpl::fold]\nassert_same<tmpl::fold<List2<Type1, Type2>, Type3,\n                       lazy_make_list1<tmpl::_state, tmpl::_element>>,\n            List1<List1<Type3, Type1>, Type2>>();\nHAS_LAZY_VERSION(fold);\n// [tmpl::fold]\n}\n\n// [tmpl::found]\nassert_same<\n  tmpl::found<List1<Type1, Type2, Type2, Type3>, std::is_same<tmpl::_1, Type2>>,\n  tmpl::true_type>();\nassert_same<\n  tmpl::found<List1<Type1, Type1, Type1, Type3>, std::is_same<tmpl::_1, Type2>>,\n  tmpl::false_type>();\nassert_same<\n  tmpl::found<List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>>,\n  tmpl::true_type>();\n// [tmpl::found]\n\n// [tmpl::front]\nassert_same<tmpl::front<List1<Type1, Type2, Type3>>, Type1>();\n// [tmpl::front]\n\n// [tmpl::index_if]\nassert_same<tmpl::index_if<List1<Type1, Type2, Type3>,\n                           std::is_same<Type3, tmpl::_1>>,\n            tmpl::size_t<2>>();\nassert_same<tmpl::index_if<List1<Type1, Type3, Type3>,\n                           std::is_same<Type3, tmpl::_1>>,\n            tmpl::size_t<1>>();\nassert_same<tmpl::index_if<List1<Type1>, std::is_same<Type3, tmpl::_1>>,\n            tmpl::no_such_type_>();\nassert_same<tmpl::index_if<List1<Type1>, std::is_same<Type3, tmpl::_1>, Type2>,\n            Type2>();\n// [tmpl::index_if]\n\n// [tmpl::index_of]\nassert_same<tmpl::index_of<List1<Type1, Type2, Type3>, Type3>,\n            tmpl::size_t<2>>();\nassert_same<tmpl::index_of<List1<Type1, Type3, Type3>, Type3>,\n            tmpl::size_t<1>>();\nassert_same<tmpl::index_of<List1<Type1>, Type2>,\n            tmpl::no_such_type_>();\n// [tmpl::index_of]\n\n{\nusing lazy_test_arguments = tmpl::list<List1<Type1, Type2>, Type1>;\n// [tmpl::list_contains]\nassert_same<tmpl::list_contains<List1<Type1, Type2>, Type1>, tmpl::true_type>();\nstatic_assert(tmpl::list_contains_v<List1<Type1, Type2>, Type1>);\nstatic_assert(not tmpl::list_contains_v<List1<Type2, Type2>, Type1>);\nHAS_LAZY_VERSION(list_contains);\n// [tmpl::list_contains]\n}\n\n// [tmpl::none]\nstatic_assert(not tmpl::none<\n  List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>>::value);\nstatic_assert(tmpl::none<\n  List1<tmpl::size_t<0>, tmpl::size_t<0>, tmpl::size_t<0>>>::value);\nstatic_assert(not tmpl::none<\n  List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>,\n  tmpl::less<tmpl::_1, tmpl::size_t<2>>>::value);\nstatic_assert(tmpl::none<\n  List1<tmpl::size_t<4>, tmpl::size_t<3>, tmpl::size_t<2>>,\n  tmpl::less<tmpl::_1, tmpl::size_t<2>>>::value);\nstatic_assert(tmpl::none<List1<>>::value);\n// [tmpl::none]\n\n// [tmpl::none:inhomogeneous]\n#ifndef __CUDACC__\nstatic_assert(not tmpl::none<\n  List1<std::false_type, tmpl::false_type>, tmpl::_1>::value);\n#endif\n// [tmpl::none:inhomogeneous]\n\n// [tmpl::not_found]\nassert_same<\n  tmpl::not_found<List1<Type1, Type2, Type2, Type3>,\n                  std::is_same<tmpl::_1, Type2>>,\n  tmpl::false_type>();\nassert_same<\n  tmpl::not_found<List1<Type1, Type1, Type1, Type3>,\n                  std::is_same<tmpl::_1, Type2>>,\n  tmpl::true_type>();\nassert_same<\n  tmpl::not_found<List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>>,\n  tmpl::false_type>();\n// [tmpl::not_found]\n\n// [tmpl::size]\nassert_same<tmpl::size<List1<Type1, Type1>>,\n            tmpl::integral_constant<unsigned int, 2>>();\n// [tmpl::size]\n}\n}  // namespace list_query\n\nnamespace list_from_list {\nvoid run() {\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<Type1, Type2>, List2<>, List2<Type2>>;\n// [tmpl::append]\nassert_same<tmpl::append<List1<Type1, Type2>, List2<>, List2<Type2>>,\n            List1<Type1, Type2, Type2>>();\nassert_same<tmpl::append<>, tmpl::list<>>();\nHAS_LAZY_VERSION(append);\n// [tmpl::append]\n}\n\n// [tmpl::clear]\nassert_same<tmpl::clear<List1<Type1>>, List1<>>();\n// [tmpl::clear]\n\n// [tmpl::erase]\nassert_same<tmpl::erase<List1<Type1, Type2, Type3>, tmpl::size_t<1>>,\n            List1<Type1, Type3>>();\n// [tmpl::erase]\n\n// [tmpl::erase_c]\nassert_same<tmpl::erase_c<List1<Type1, Type2, Type3>, 1>,\n            List1<Type1, Type3>>();\n// [tmpl::erase_c]\n\n{\nusing lazy_test_arguments = tmpl::list<List1<Type1, Type2, Type1, Type3>,\n                                       std::is_same<Type1, tmpl::_1>>;\n// [tmpl::filter]\nassert_same<tmpl::filter<List1<Type1, Type2, Type1, Type3>,\n                         std::is_same<Type1, tmpl::_1>>,\n            List1<Type1, Type1>>();\nHAS_LAZY_VERSION(filter);\n// [tmpl::filter]\n}\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<Type1, Type2, Type2, Type3>, std::is_same<tmpl::_1, Type2>>;\n// [tmpl::find]\nassert_same<\n  tmpl::find<List1<Type1, Type2, Type2, Type3>, std::is_same<tmpl::_1, Type2>>,\n  List1<Type2, Type2, Type3>>();\nassert_same<\n  tmpl::find<List1<Type1, Type1, Type1, Type3>, std::is_same<tmpl::_1, Type2>>,\n  List1<>>();\nassert_same<\n  tmpl::find<List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>>,\n  List1<tmpl::size_t<1>, tmpl::size_t<2>>>();\nHAS_LAZY_VERSION(find);\n// [tmpl::find]\n}\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<List1<Type1, List1<Type2>>, List2<List1<Type3>>>>;\n// [tmpl::flatten]\nassert_same<\n  tmpl::flatten<List1<List1<Type1, List1<Type2>>, List2<List1<Type3>>>>,\n  List1<Type1, Type2, List2<List1<Type3>>>>();\nHAS_LAZY_VERSION(flatten);\n// [tmpl::flatten]\n}\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List3<List1<Type1, Type2>, List2<>, List2<Type2>>>;\n// [tmpl::join]\nassert_same<tmpl::join<List3<List1<Type1, Type2>, List2<>, List2<Type2>>>,\n            List1<Type1, Type2, Type2>>();\nassert_same<tmpl::join<List1<>>, tmpl::list<>>();\nHAS_LAZY_VERSION(join);\n// [tmpl::join]\n}\n\n// [tmpl::list_difference]\nassert_same<tmpl::list_difference<List1<Type1, Type2, Type1, Type2>,\n                                  List2<Type3, Type2>>,\n            List1<Type1, Type1>>();\n// [tmpl::list_difference]\n\n{\n// [tmpl::merge]\nassert_same<\n  tmpl::merge<List1<tmpl::size_t<1>, tmpl::size_t<2>, tmpl::size_t<5>>,\n              List2<tmpl::size_t<1>, tmpl::size_t<3>, tmpl::size_t<6>>>,\n  List1<tmpl::size_t<1>, tmpl::size_t<1>, tmpl::size_t<2>, tmpl::size_t<3>,\n        tmpl::size_t<5>, tmpl::size_t<6>>>();\nassert_same<tmpl::merge<List1<Type1, Type2>, List2<Type1, Type3>,\n                        CompareType123>,\n            List1<Type1, Type1, Type2, Type3>>();\n// [tmpl::merge]\n\n// [tmpl::merge:equiv]\nassert_same<tmpl::merge<List1<Type1, Type1>, List2<Type2, Type2>,\n                        std::false_type>,\n            List1<Type2, Type2, Type1, Type1>>();\n// [tmpl::merge:equiv]\n}\n\n// [tmpl::partition]\nassert_same<tmpl::partition<List1<Type1, Type2, Type1, Type3>,\n                            std::is_same<Type1, tmpl::_1>>,\n            tmpl::pair<List1<Type1, Type1>, List1<Type2, Type3>>>();\n// [tmpl::partition]\n\n// [tmpl::pop_back]\nassert_same<tmpl::pop_back<List1<Type1, Type2, Type3>>, List1<Type1, Type2>>();\nassert_same<tmpl::pop_back<List1<Type1, Type2, Type3>,\n                           tmpl::integral_constant<unsigned int, 2>>,\n            List1<Type1>>();\n// [tmpl::pop_back]\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<Type1, Type2, Type3>,\n             tmpl::integral_constant<unsigned int, 2>>;\n// [tmpl::pop_front]\nassert_same<tmpl::pop_front<List1<Type1, Type2, Type3>>,\n            List1<Type2, Type3>>();\nassert_same<tmpl::pop_front<List1<Type1, Type2, Type3>,\n                            tmpl::integral_constant<unsigned int, 2>>,\n            List1<Type3>>();\nHAS_LAZY_VERSION(pop_front);\n// [tmpl::pop_front]\n}\n\n// [tmpl::push_back]\nassert_same<tmpl::push_back<List1<Type1>, Type2, Type3>,\n            List1<Type1, Type2, Type3>>();\n// [tmpl::push_back]\n\n{\nusing lazy_test_arguments = tmpl::list<List1<Type1>, Type2, Type3>;\n// [tmpl::push_front]\nassert_same<tmpl::push_front<List1<Type1>, Type2, Type3>,\n            List1<Type2, Type3, Type1>>();\nHAS_LAZY_VERSION(push_front);\n// [tmpl::push_front]\n}\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<Type1, Type2, Type1, Type3>, Type1>;\n// [tmpl::remove]\nassert_same<tmpl::remove<List1<Type1, Type2, Type1, Type3>, Type1>,\n            List1<Type2, Type3>>();\nHAS_LAZY_VERSION(remove);\n// [tmpl::remove]\n}\n\n// [tmpl::remove_duplicates]\nassert_same<tmpl::remove_duplicates<List1<Type1, Type2, Type1, Type3, Type2>>,\n            List1<Type1, Type2, Type3>>();\n// [tmpl::remove_duplicates]\n\n{\nusing lazy_test_arguments = tmpl::list<List1<Type1, Type2, Type1, Type3>,\n                                       std::is_same<Type1, tmpl::_1>>;\n// [tmpl::remove_if]\nassert_same<tmpl::remove_if<List1<Type1, Type2, Type1, Type3>,\n                            std::is_same<Type1, tmpl::_1>>,\n            List1<Type2, Type3>>();\nHAS_LAZY_VERSION(remove_if);\n// [tmpl::remove_if]\n}\n\n{\nusing lazy_test_arguments =\n    tmpl::list<List1<Type1, Type2, Type1>, Type1, Type3>;\n// [tmpl::replace]\nassert_same<tmpl::replace<List1<Type1, Type2, Type1>, Type1, Type3>,\n            List1<Type3, Type2, Type3>>();\nHAS_LAZY_VERSION(replace);\n// [tmpl::replace]\n}\n\n{\nusing lazy_test_arguments = tmpl::list<List1<Type1, Type2, Type1>,\n                                       std::is_same<Type1, tmpl::_1>, Type3>;\n// [tmpl::replace_if]\nassert_same<tmpl::replace_if<List1<Type1, Type2, Type1>,\n                             std::is_same<Type1, tmpl::_1>, Type3>,\n            List1<Type3, Type2, Type3>>();\nHAS_LAZY_VERSION(replace_if);\n// [tmpl::replace_if]\n}\n\n{\nusing lazy_test_arguments = tmpl::list<List1<Type1, Type2, Type3, Type1>>;\n// [tmpl::reverse]\nassert_same<tmpl::reverse<List1<Type1, Type2, Type3, Type1>>,\n            List1<Type1, Type3, Type2, Type1>>();\nHAS_LAZY_VERSION(reverse);\n// [tmpl::reverse]\n}\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<Type1, Type2, Type2, Type3>, std::is_same<tmpl::_1, Type2>>;\n// [tmpl::reverse_find]\nassert_same<\n  tmpl::reverse_find<List1<Type1, Type2, Type2, Type3>,\n                     std::is_same<tmpl::_1, Type2>>,\n  List1<Type1, Type2, Type2>>();\nassert_same<\n  tmpl::reverse_find<List1<Type1, Type1, Type1, Type3>,\n                     std::is_same<tmpl::_1, Type2>>,\n  List1<>>();\nassert_same<\n  tmpl::reverse_find<List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>>,\n  List1<tmpl::size_t<0>, tmpl::size_t<1>, tmpl::size_t<2>>>();\nHAS_LAZY_VERSION(reverse_find);\n// [tmpl::reverse_find]\n}\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List2<Type1, Type2>, Type3,\n             lazy_make_list1<tmpl::_state, tmpl::_element>>;\n// [tmpl::reverse_fold]\nassert_same<tmpl::reverse_fold<List2<Type1, Type2>, Type3,\n                               lazy_make_list1<tmpl::_state, tmpl::_element>>,\n            List1<List1<Type3, Type2>, Type1>>();\nHAS_LAZY_VERSION(reverse_fold);\n// [tmpl::reverse_fold]\n}\n\n// [tmpl::sort]\nassert_same<tmpl::sort<List1<tmpl::size_t<9>, tmpl::size_t<6>,\n                             tmpl::size_t<7>, tmpl::size_t<0>>>,\n            List1<tmpl::size_t<0>, tmpl::size_t<6>, tmpl::size_t<7>,\n                  tmpl::size_t<9>>>();\nassert_same<tmpl::sort<List1<Type2, Type3, Type3, Type1, Type2, Type3, Type2>,\n                       CompareType123>,\n            List1<Type1, Type2, Type2, Type2, Type3, Type3, Type3>>();\n// [tmpl::sort]\n\n// [tmpl::sort:equiv]\nassert_same<tmpl::sort<List1<Type1, Type2, Type3>, std::false_type>,\n            List1<Type3, Type2, Type1>>();\n// [tmpl::sort:equiv]\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<Type1, Type2, Type3, Type2, Type3, Type3, Type1>, Type3>;\n// [tmpl::split]\nassert_same<\n  tmpl::split<List1<Type1, Type2, Type3, Type2, Type3, Type3, Type1>, Type3>,\n  List1<List1<Type1, Type2>, List1<Type2>, List1<Type1>>>();\nHAS_LAZY_VERSION(split);\n// [tmpl::split]\n}\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<Type1, Type2, Type3>,\n             tmpl::integral_constant<unsigned int, 2>>;\n// [tmpl::split_at]\nassert_same<tmpl::split_at<List1<Type1, Type2, Type3>,\n                           tmpl::integral_constant<unsigned int, 2>>,\n            List1<List1<Type1, Type2>, List1<Type3>>>();\nHAS_LAZY_VERSION(split_at);\n// [tmpl::split_at]\n}\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List2<Type1, Type2, Type3>, List3<Type3, Type2, Type1>,\n             lazy_make_list1<tmpl::_1, tmpl::_2>>;\n// [tmpl::transform]\nassert_same<\n  tmpl::transform<List2<Type1, Type2, Type3>, List3<Type3, Type2, Type1>,\n                  lazy_make_list1<tmpl::_1, tmpl::_2>>,\n  List2<List1<Type1, Type3>, List1<Type2, Type2>, List1<Type3, Type1>>>();\nHAS_LAZY_VERSION(transform);\n// [tmpl::transform]\n}\n}\n}  // namespace list_from_list\n\nnamespace map_functions {\n// [example_map]\nusing example_map =\n  tmpl::map<tmpl::pair<Type1, int>, tmpl::pair<Type2, double>>;\n// [example_map]\n\nvoid run() {\n// [tmpl::at:map]\nassert_same<tmpl::at<example_map, Type1>, int>();\nassert_same<tmpl::at<example_map, Type3>, tmpl::no_such_type_>();\n// [tmpl::at:map]\n\n// [tmpl::erase:map]\nassert_maps_same<tmpl::erase<example_map, Type1>,\n                 tmpl::map<tmpl::pair<Type2, double>>>();\nassert_maps_same<tmpl::erase<example_map, Type3>, example_map>();\n// [tmpl::erase:map]\n\n// [tmpl::has_key:map]\nassert_same<tmpl::has_key<example_map, Type2>, tmpl::true_type>();\nassert_same<tmpl::has_key<example_map, Type3>, tmpl::false_type>();\n// [tmpl::has_key:map]\n\n// [tmpl::insert:map]\nassert_maps_same<tmpl::insert<example_map, tmpl::pair<Type3, int>>,\n                 tmpl::map<tmpl::pair<Type1, int>, tmpl::pair<Type2, double>,\n                           tmpl::pair<Type3, int>>>();\nassert_maps_same<tmpl::insert<example_map, tmpl::pair<Type1, float>>,\n                 example_map>();\n// [tmpl::insert:map]\n\n// [tmpl::keys_as_sequence]\nassert_sets_same<tmpl::keys_as_sequence<example_map>,\n                 tmpl::set<Type1, Type2>>();\nstatic_assert(std::is_same_v<tmpl::keys_as_sequence<example_map, List1>,\n                             List1<Type1, Type2>> or\n              std::is_same_v<tmpl::keys_as_sequence<example_map, List1>,\n                             List1<Type2, Type1>>);\n// [tmpl::keys_as_sequence]\n\n{\nusing lazy_test_arguments = tmpl::list<example_map, Type1>;\n// [tmpl::lookup]\nassert_same<tmpl::lookup<example_map, Type1>, int>();\nassert_same<tmpl::lookup<example_map, Type3>, tmpl::no_such_type_>();\nHAS_LAZY_VERSION(lookup);\n// [tmpl::lookup]\n}\n\n// [tmpl::lookup_at]\nassert_same<tmpl::lazy::lookup_at<example_map, Type1>::type,\n            tmpl::type_<int>>();\nassert_same<tmpl::lazy::lookup_at<example_map, Type3>::type,\n            tmpl::type_<tmpl::no_such_type_>>();\n// [tmpl::lookup_at]\n\n// [tmpl::values_as_sequence]\nstatic_assert(std::is_same_v<tmpl::values_as_sequence<example_map>,\n                             tmpl::list<int, double>> or\n              std::is_same_v<tmpl::values_as_sequence<example_map>,\n                             tmpl::list<double, int>>);\nstatic_assert(std::is_same_v<tmpl::values_as_sequence<example_map, List1>,\n                             List1<int, double>> or\n              std::is_same_v<tmpl::values_as_sequence<example_map, List1>,\n                             List1<double, int>>);\n// [tmpl::values_as_sequence]\n}\n}  // namespace map_functions\n\nnamespace set_functions {\nvoid run() {\n// [tmpl::contains]\nassert_same<tmpl::contains<tmpl::set<Type1, Type2>, Type1>, tmpl::true_type>();\nassert_same<tmpl::contains<tmpl::set<Type1, Type2>, Type3>, tmpl::false_type>();\n// [tmpl::contains]\n\n// [tmpl::erase:set]\nassert_sets_same<tmpl::erase<tmpl::set<Type1, Type2>, Type1>,\n                 tmpl::set<Type2>>();\nassert_sets_same<tmpl::erase<tmpl::set<Type1, Type2>, Type3>,\n                 tmpl::set<Type1, Type2>>();\n// [tmpl::erase:set]\n\n// [tmpl::has_key:set]\nassert_same<tmpl::has_key<tmpl::set<Type1, Type2>, Type2>, tmpl::true_type>();\nassert_same<tmpl::has_key<tmpl::set<Type1, Type2>, Type3>, tmpl::false_type>();\n// [tmpl::has_key:set]\n\n// [tmpl::insert:set]\nassert_sets_same<tmpl::insert<tmpl::set<Type1, Type2>, Type3>,\n                 tmpl::set<Type1, Type2, Type3>>();\nassert_sets_same<tmpl::insert<tmpl::set<Type1, Type2>, Type1>,\n                 tmpl::set<Type1, Type2>>();\n// [tmpl::insert:set]\n}\n}  // namespace set_functions\n\nnamespace math_functions {\nvoid run() {\n// [math_arithmetic]\nassert_same<tmpl::plus<tmpl::size_t<10>, tmpl::size_t<3>>::type,\n            tmpl::size_t<13>>();\nassert_same<tmpl::minus<tmpl::size_t<10>, tmpl::size_t<3>>::type,\n            tmpl::size_t<7>>();\nassert_same<tmpl::times<tmpl::size_t<10>, tmpl::size_t<3>>::type,\n            tmpl::size_t<30>>();\nassert_same<tmpl::divides<tmpl::size_t<10>, tmpl::size_t<3>>::type,\n            tmpl::size_t<3>>();\nassert_same<tmpl::modulo<tmpl::size_t<10>, tmpl::size_t<3>>::type,\n            tmpl::size_t<1>>();\nassert_same<tmpl::negate<tmpl::int64_t<10>>::type, tmpl::int64_t<-10>>();\n// [math_arithmetic]\n\n// [math_bitwise]\nassert_same<tmpl::complement<tmpl::uint8_t<0b10001111>>::type,\n                             tmpl::uint8_t<0b01110000>>();\nassert_same<tmpl::bitand_<tmpl::uint8_t<0b00111011>,\n                          tmpl::uint8_t<0b01010110>>::type,\n                          tmpl::uint8_t<0b00010010>>();\nassert_same<tmpl::bitor_<tmpl::uint8_t<0b01100011>,\n                         tmpl::uint8_t<0b10100111>>::type,\n                         tmpl::uint8_t<0b11100111>>();\nassert_same<tmpl::bitxor_<tmpl::uint8_t<0b11000011>,\n                          tmpl::uint8_t<0b00000110>>::type,\n                          tmpl::uint8_t<0b11000101>>();\nassert_same<tmpl::shift_left<tmpl::uint8_t<0b00001110>, tmpl::size_t<3>>::type,\n                             tmpl::uint8_t<0b01110000>>();\nassert_same<tmpl::shift_right<tmpl::uint8_t<0b10110011>, tmpl::size_t<4>>::type,\n                              tmpl::uint8_t<0b00001011>>();\n// [math_bitwise]\n\n// [math_comparison]\nassert_same<tmpl::equal_to<tmpl::size_t<1>, tmpl::size_t<2>>::type,\n            tmpl::false_type>();\nassert_same<tmpl::not_equal_to<tmpl::size_t<1>, tmpl::size_t<2>>::type,\n            tmpl::true_type>();\nassert_same<tmpl::greater<tmpl::size_t<1>, tmpl::size_t<2>>::type,\n            tmpl::false_type>();\nassert_same<tmpl::greater_equal<tmpl::size_t<1>, tmpl::size_t<2>>::type,\n            tmpl::false_type>();\nassert_same<tmpl::less<tmpl::size_t<1>, tmpl::size_t<2>>::type,\n            tmpl::true_type>();\nassert_same<tmpl::less_equal<tmpl::size_t<1>, tmpl::size_t<2>>::type,\n            tmpl::true_type>();\n// [math_comparison]\n\n// [math_logical]\nassert_same<tmpl::and_<>::type, tmpl::true_type>();\nassert_same<tmpl::and_<std::true_type, std::false_type>::type,\n            tmpl::false_type>();\nassert_same<tmpl::or_<>::type, tmpl::false_type>();\nassert_same<tmpl::or_<std::true_type, std::false_type, std::false_type>::type,\n            tmpl::true_type>();\nassert_same<tmpl::xor_<std::true_type, std::false_type>::type,\n            tmpl::true_type>();\nassert_same<tmpl::not_<std::true_type>::type, tmpl::false_type>();\n// [math_logical]\n\n// [tmpl::identity]\nassert_same<tmpl::identity<tmpl::size_t<10>>::type, tmpl::size_t<10>>();\n// [tmpl::identity]\n\n// [tmpl::max]\nassert_same<tmpl::max<tmpl::size_t<10>, tmpl::int32_t<3>>::type,\n            tmpl::size_t<10>>();\n// [tmpl::max]\n\n// [tmpl::min]\nassert_same<tmpl::min<tmpl::size_t<10>, tmpl::int32_t<3>>::type,\n            tmpl::size_t<3>>();\n// [tmpl::min]\n\n// [tmpl::next]\nassert_same<tmpl::next<tmpl::size_t<10>>::type, tmpl::size_t<11>>();\n// [tmpl::next]\n\n// [tmpl::prev]\nassert_same<tmpl::prev<tmpl::size_t<10>>::type, tmpl::size_t<9>>();\n// [tmpl::prev]\n}\n}  // namespace math_functions\n\nnamespace miscellaneous {\n// [tmpl::has_type:pack_expansion]\ntemplate <typename... T>\nbool check_sizes(const T&... containers,\n                 const typename tmpl::has_type<T, size_t>::type... sizes) {\n  return (... and (containers.size() == sizes));\n}\n// [tmpl::has_type:pack_expansion]\n\n// [tmpl::inherit:pack:definitions]\ntemplate <typename... T>\nstruct inherit_pack {\n  struct type : T... {};\n};\n// [tmpl::inherit:pack:definitions]\n\nvoid run() {\n// [tmpl::always]\nassert_same<tmpl::always<Type1>::type, Type1>();\n// [tmpl::always]\n\n// [tmpl::apply]\nassert_same<tmpl::apply<std::is_convertible<tmpl::_1, tmpl::_2>,\n                        const char*, std::string>,\n            std::true_type>();\nassert_same<tmpl::apply<std::is_convertible<tmpl::_2, tmpl::_1>,\n                        const char*, std::string>,\n            std::false_type>();\n// [tmpl::apply]\n\n// [tmpl::count]\nassert_same<tmpl::count<Type1, Type2, Type1>,\n            tmpl::integral_constant<unsigned int, 3>>();\n// [tmpl::count]\n\n// [tmpl::conditional_t]\nassert_same<tmpl::conditional_t<true, Type1, Type2>, Type1>();\nassert_same<tmpl::conditional_t<false, Type1, Type2>, Type2>();\n// [tmpl::conditional_t]\n\n// [tmpl::eval_if]\nassert_same<tmpl::eval_if<std::true_type,\n                          tmpl::plus<tmpl::size_t<1>, tmpl::size_t<2>>,\n                          tmpl::plus<Type1, Type2>  // Invalid expression\n                          >::type,\n            tmpl::size_t<3>>();\n// [tmpl::eval_if]\n\n// [tmpl::eval_if_c]\nassert_same<tmpl::eval_if_c<true,\n                            tmpl::plus<tmpl::size_t<1>, tmpl::size_t<2>>,\n                            tmpl::plus<Type1, Type2>  // Invalid expression\n                            >::type,\n            tmpl::size_t<3>>();\n// [tmpl::eval_if_c]\n\n// [tmpl::has_type]\nassert_same<tmpl::has_type<Type1, Type2>::type, Type2>();\nassert_same<tmpl::has_type<Type1>::type, void>();\n// [tmpl::has_type]\n\n// [tmpl::has_type:pack_expansion:asserts]\nCHECK(check_sizes<std::string, std::vector<int>>(\"Hello\", {1, 2, 3}, 5, 3));\n// [tmpl::has_type:pack_expansion:asserts]\n\n// [tmpl::if_]\nassert_same<tmpl::if_<std::true_type, Type1, Type2>::type, Type1>();\n// [tmpl::if_]\n\n// [tmpl::if_c]\nassert_same<tmpl::if_c<true, Type1, Type2>::type, Type1>();\n// [tmpl::if_c]\n\n// [tmpl::inherit]\n// tmpl::type_ is used in this example because base classes must be\n// complete types\nstatic_assert(\n    std::is_base_of_v<tmpl::type_<Type2>,\n                      tmpl::inherit<tmpl::type_<Type1>, tmpl::type_<Type2>,\n                                    tmpl::type_<Type3>>::type>);\n// [tmpl::inherit]\n\n// [tmpl::inherit:pack:asserts]\nstatic_assert(\n    std::is_base_of_v<tmpl::type_<Type2>,\n                      inherit_pack<tmpl::type_<Type1>, tmpl::type_<Type2>,\n                                   tmpl::type_<Type3>>::type>);\n// [tmpl::inherit:pack:asserts]\n\n{\nusing lazy_test_arguments =\n  tmpl::list<List1<Type1, Type2>, List2<tmpl::_1, tmpl::_2, Type3>>;\n// [tmpl::inherit_linearly]\nassert_same<tmpl::inherit_linearly<List1<Type1, Type2>,\n                                   List2<tmpl::_1, tmpl::_2, Type3>>,\n            List2<List2<tmpl::empty_base, Type1, Type3>, Type2, Type3>>();\nassert_same<tmpl::inherit_linearly<List1<Type1, Type2>,\n                                   List2<tmpl::_1, tmpl::_2, Type3>, Type3>,\n            List2<List2<Type3, Type1, Type3>, Type2, Type3>>();\nHAS_LAZY_VERSION(inherit_linearly);\n// [tmpl::inherit_linearly]\n}\n\n// [tmpl::is_set]\nassert_same<tmpl::is_set<Type1, Type2, Type3>, tmpl::true_type>();\nassert_same<tmpl::is_set<Type1, Type2, Type1>, tmpl::false_type>();\nassert_same<tmpl::is_set<>, tmpl::true_type>();\n// [tmpl::is_set]\n\n{\n// [tmpl::real_]\nusing three_eighths = tmpl::single_<0x3EC00000>;\nusing minus_one_hundred_thousand_three = tmpl::double_<0xC0F86A3000000000>;\nCHECK(static_cast<float>(three_eighths{}) == 0.375f);\nCHECK(static_cast<double>(minus_one_hundred_thousand_three{}) == -100003.0);\nassert_same<three_eighths::value_type, float>();\nassert_same<minus_one_hundred_thousand_three::value_type, double>();\n// [tmpl::real_]\n}\n\n// [tmpl::repeat]\nassert_same<tmpl::repeat<Wrapper, tmpl::size_t<3>, Type1>,\n            Wrapper<Wrapper<Wrapper<Type1>>>>();\nassert_same<tmpl::repeat<Wrapper, tmpl::size_t<0>, Type1>, Type1>();\n// [tmpl::repeat]\n\n// [tmpl::repeat:lazy]\nassert_same<tmpl::lazy::repeat<Wrapper, tmpl::size_t<3>, Type1>::type,\n            Wrapper<Wrapper<Wrapper<Type1>>>>();\n// [tmpl::repeat:lazy]\n\n// [tmpl::sizeof_]\nassert_same<tmpl::sizeof_<double>::type,\n            tmpl::integral_constant<unsigned int, sizeof(double)>>();\n// [tmpl::sizeof_]\n\n// [tmpl::substitute]\nassert_same<tmpl::substitute<List1<List2<tmpl::_1, tmpl::_2, tmpl::_3>,\n                                   tmpl::args<0>, tmpl::args<1>, tmpl::args<2>>,\n                             List3<Type1, Type2, Type3>>,\n            List1<List2<tmpl::_1, tmpl::_2, Type3>, Type1, Type2, Type3>>();\n// [tmpl::substitute]\n\n// [tmpl::type_from]\nassert_same<tmpl::type_from<tmpl::type_<Type1>>, Type1>();\n// [tmpl::type_from]\n\n// [tmpl::wrap]\nassert_same<tmpl::wrap<List1<Type1, Type2>, List2>, List2<Type1, Type2>>();\n// [tmpl::wrap]\n\n// [tmpl::wrap:lazy]\nassert_same<tmpl::lazy::wrap<List1<Type1, Type2>, List2>::type,\n            List2<Type1, Type2>>();\n// [tmpl::wrap:lazy]\n}\n}  // namespace miscellaneous\n\nnamespace runtime {\n// [runtime_declarations]\ntemplate <typename T>\nstruct NonCopyable {\n  NonCopyable(T t = T{}) : value(std::move(t)) {}\n  NonCopyable(const NonCopyable&) = delete;\n  NonCopyable(NonCopyable&&) = default;\n  NonCopyable& operator=(const NonCopyable&) = delete;\n  NonCopyable& operator=(NonCopyable&&) = default;\n\n  T value;\n\n  template <typename... Args>\n  decltype(auto) operator()(Args&&... args) {\n    return value(std::forward<Args>(args)...);\n  }\n};\n// [runtime_declarations]\n\nnamespace example_for_each_args {\n// [tmpl::for_each_args:defs]\nstruct Functor {\n  Functor() = default;\n  Functor(const Functor&) = delete;\n  Functor(Functor&&) = default;\n  Functor& operator=(const Functor&) = delete;\n  Functor& operator=(Functor&&) = default;\n\n  std::vector<double> record;\n\n  void operator()(NonCopyable<int> x) {\n    record.push_back(x.value);\n  }\n\n  void operator()(const NonCopyable<double>& x) {\n    record.push_back(x.value);\n  }\n};\n// [tmpl::for_each_args:defs]\n}  // namespace example_for_each_args\n\nnamespace example_for_each {\n// [tmpl::for_each:defs]\nstruct Functor {\n  Functor() = default;\n  Functor(const Functor&) = delete;\n  Functor(Functor&&) = default;\n  Functor& operator=(const Functor&) = delete;\n  Functor& operator=(Functor&&) = default;\n\n  std::vector<std::string> record;\n\n  template <typename T>\n  void operator()(T /*t*/) {\n    using type = tmpl::type_from<T>;\n    if (std::is_same_v<type, int>) {\n      record.push_back(\"int\");\n    } else if (std::is_same_v<type, double>) {\n      record.push_back(\"double\");\n    }\n  }\n};\n// [tmpl::for_each:defs]\n}  // namespace example_for_each\n\nvoid run() {\n{\nusing example_for_each_args::Functor;\n// [tmpl::for_each_args]\nconst NonCopyable<double> three_point_five{3.5};\nCHECK(tmpl::for_each_args(Functor{}, NonCopyable<int>{2}, three_point_five)\n          .record == std::vector<double>{2.0, 3.5});\n// [tmpl::for_each_args]\n}\n\n{\nusing example_for_each::Functor;\n// [tmpl::for_each]\nCHECK(tmpl::for_each<List1<int, double, int>>(Functor{}).record ==\n        std::vector<std::string>{\"int\", \"double\", \"int\"});\n// [tmpl::for_each]\n}\n\n{\n// [tmpl::select]\nconst NonCopyable<std::string> hi{\"Hi\"};\nCHECK(tmpl::select<std::true_type>(NonCopyable<int>{3}, hi).value == 3);\nCHECK(tmpl::select<std::false_type>(NonCopyable<int>{3}, hi).value == \"Hi\");\n// [tmpl::select]\n}\n}\n}  // namespace runtime\n\nnamespace external {\nvoid run() {\n// [stl_integration]\nassert_same<tmpl::as_pair<List1<Type1, Type2>>, std::pair<Type1, Type2>>();\nassert_same<tmpl::as_tuple<List1<Type1, Type2, Type3>>,\n            std::tuple<Type1, Type2, Type3>>();\n\nassert_same<tmpl::pair_wrapper<Type1, Type2>, std::pair<Type1, Type2>>();\nassert_same<tmpl::tuple_wrapper<Type1, Type2, Type3>,\n            std::tuple<Type1, Type2, Type3>>();\n\nassert_same<tmpl::pair_wrapper_<Type1, Type2>::type, std::pair<Type1, Type2>>();\n// [stl_integration]\n\n// [tmpl::make_integral]\nassert_same<tmpl::make_integral<std::integral_constant<char, 3>>::type,\n            tmpl::integral_constant<char, 3>>();\nassert_same<tmpl::as_integral_list<List1<std::true_type, std::true_type,\n                                         std::false_type>>,\n            List1<tmpl::true_type, tmpl::true_type, tmpl::false_type>>();\n// [tmpl::make_integral]\n\n// [tmpl::as_list]\nassert_same<tmpl::as_sequence<std::pair<Type1, Type2>, List1>,\n            List1<Type1, Type2>>();\nassert_same<tmpl::as_list<std::pair<Type1, Type2>>, tmpl::list<Type1, Type2>>();\n// [tmpl::as_list]\n\n// [tmpl::as_set]\nassert_same<tmpl::as_set<std::tuple<Type1, Type2, Type3>>,\n            tmpl::set<Type1, Type2, Type3>>();\nassert_same<tmpl::set_wrapper<Type1, Type2, Type3>,\n            tmpl::set<Type1, Type2, Type3>>();\n// [tmpl::as_set]\n}\n}  // namespace external\n}  // namespace\n// clang-format on\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.TMPL.Documentation\", \"[Unit][Utilities]\") {\n  metafunctions::run();\n  containers::run();\n  constants::run();\n  list_constructors::run();\n  list_query::run();\n  list_from_list::run();\n  map_functions::run();\n  set_functions::run();\n  math_functions::run();\n  miscellaneous::run();\n  runtime::run();\n  external::run();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_Tuple.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n#include <tuple>\n\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/Tuple.hpp\"\n\nnamespace {\n// [tuple_fold_struct_defn]\ntemplate <typename T>\nstruct tuple_fold_plus {\n  T value = 0.0;\n  template <typename S>\n  void operator()(const S& element) {\n    value += element;\n  }\n};\n// [tuple_fold_struct_defn]\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.tuple_fold\", \"[Utilities][Unit]\") {\n  {\n    // [tuple_fold_lambda]\n    const auto my_tupull = std::make_tuple(2, 7, -3.8, 20.9);\n    double sum_value = 0.0;\n    tuple_fold(my_tupull,\n               [](const auto& element, double& state) { state += element; },\n               sum_value);\n    CHECK(sum_value == approx(26.1));\n    // [tuple_fold_lambda]\n\n    // [tuple_counted_fold_lambda]\n    sum_value = 0.0;\n    tuple_counted_fold(my_tupull,\n                       [](const auto& element, size_t index, double& state) {\n                         if (index != 1) {\n                           state += element;\n                         }\n                       },\n                       sum_value);\n    CHECK(sum_value == approx(19.1));\n    // [tuple_counted_fold_lambda]\n  }\n  {\n    // [tuple_fold_struct]\n    const auto my_tupull = std::make_tuple(2, 7, -3.8, 20.9);\n    tuple_fold_plus<double> sum_value{};\n    tuple_fold(my_tupull, sum_value);\n    CHECK(sum_value.value == approx(26.1));\n    // [tuple_fold_struct]\n  }\n  {\n    // Check passing rvalue to tuple_fold and tuple_counted_fold works\n    const auto my_tupull = std::make_tuple(2, 7, -3.8, 20.9);\n    double sum_value = 0.0;\n    tuple_fold(my_tupull,\n               [](const auto& element, double& state, const std::string& word) {\n                 state += element;\n                 CHECK(word == \"test sentence\");\n               },\n               sum_value, std::string(\"test sentence\"));\n    CHECK(sum_value == approx(26.1));\n    sum_value = 0.0;\n    tuple_counted_fold(my_tupull,\n                       [](const auto& element, const size_t /*index*/,\n                          double& state, const std::string& word) {\n                         state += element;\n                         CHECK(word == \"test sentence\");\n                       },\n                       sum_value, std::string(\"test sentence\"));\n    CHECK(sum_value == approx(26.1));\n  }\n  {\n    // Check passing const lvalue to tuple_fold and tuple_counted_fold works\n    const auto my_tupull = std::make_tuple(2, 7, -3.8, 20.9);\n    const std::string test_sentence(\"test sentence\");\n    double sum_value = 0.0;\n    tuple_fold(my_tupull,\n               [](const auto& element, double& state, const std::string& word) {\n                 state += element;\n                 CHECK(word == \"test sentence\");\n               },\n               sum_value, test_sentence);\n    CHECK(sum_value == approx(26.1));\n    sum_value = 0.0;\n    tuple_counted_fold(my_tupull,\n                       [](const auto& element, const size_t /*index*/,\n                          double& state, const std::string& word) {\n                         state += element;\n                         CHECK(word == \"test sentence\");\n                       },\n                       sum_value, test_sentence);\n    CHECK(sum_value == approx(26.1));\n  }\n  {\n    // Check left and right fold work as expected\n    const auto my_tupull = std::make_tuple(2, 7, -3.8, 20.9);\n    double sum_value = 0.0;\n    tuple_fold(my_tupull,\n               [](const auto& element, double& state) {\n                 if (state < 8.0) {\n                   state += element;\n                 }\n               },\n               sum_value);\n    CHECK(sum_value == approx(9.0));\n    sum_value = 0.0;\n    tuple_counted_fold(\n        my_tupull,\n        [](const auto& element, const size_t /*index*/, double& state) {\n          if (state < 8.0) {\n            state += element;\n          }\n        },\n        sum_value);\n    CHECK(sum_value == approx(9.0));\n\n    sum_value = 0.0;\n    tuple_fold<true>(my_tupull,\n                     [](const auto& element, double& state) {\n                       if (state < 8.0) {\n                         state += element;\n                       }\n                     },\n                     sum_value);\n    CHECK(sum_value == approx(20.9));\n    sum_value = 0.0;\n    tuple_counted_fold<true>(\n        my_tupull,\n        [](const auto& element, const size_t /*index*/, double& state) {\n          if (state < 8.0) {\n            state += element;\n          }\n        },\n        sum_value);\n    CHECK(sum_value == approx(20.9));\n  }\n}\n\nnamespace {\n// [tuple_transform_negate]\nstruct negate {\n  template <typename T, typename Index, typename S>\n  void operator()(const T& element, Index /*index*/,\n                  S& second_tuple_element) const {\n    std::get<Index::value>(second_tuple_element) = -element;\n  }\n};\n// [tuple_transform_negate]\nstruct negate_if_sum_less {\n  template <typename T, typename Index, typename S>\n  void operator()(const T& element, Index /*index*/, S& second_tuple_element,\n                  const gsl::not_null<double*> state,\n                  const std::string& test_sentence,\n                  const std::string& test_sentence2) const {\n    CHECK(test_sentence == \"test sentence\");\n    CHECK(test_sentence2 == \"test sentence2\");\n    if (*state < 8.0) {\n      *state += element;\n      std::get<Index::value>(second_tuple_element) = -element;\n    } else {\n      std::get<Index::value>(second_tuple_element) = element;\n    }\n  }\n};\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.tuple_transform\", \"[Utilities][Unit]\") {\n  // [tuple_transform]\n  const auto my_tupull = std::make_tuple(2, 7, -3.8, 20.9);\n  std::decay_t<decltype(my_tupull)> out_tupull;\n  tuple_transform(my_tupull,\n                  [](const auto& element, auto index, auto& out_tuple) {\n                    constexpr size_t index_v = decltype(index)::value;\n                    std::get<index_v>(out_tuple) = -element;\n                  },\n                  out_tupull);\n\n  CHECK(std::get<0>(out_tupull) == -2);\n  CHECK(std::get<1>(out_tupull) == -7);\n  CHECK(std::get<2>(out_tupull) == 3.8);\n  CHECK(std::get<3>(out_tupull) == -20.9);\n  // [tuple_transform]\n\n  // Check iterating left-to-right and right-to-left, and also passing in rvalue\n  // and const lvalue references\n  const std::string test_sentence2(\"test sentence2\");\n  double sum_value = 0.0;\n  tuple_transform(my_tupull, negate_if_sum_less{}, out_tupull, &sum_value,\n                  std::string(\"test sentence\"), test_sentence2);\n  CHECK(std::get<0>(out_tupull) == -2);\n  CHECK(std::get<1>(out_tupull) == -7);\n  CHECK(std::get<2>(out_tupull) == -3.8);\n  CHECK(std::get<3>(out_tupull) == 20.9);\n  CHECK(sum_value == approx(9.0));\n\n  sum_value = 0.0;\n  tuple_transform<true>(my_tupull, negate_if_sum_less{}, out_tupull, &sum_value,\n                        std::string(\"test sentence\"), test_sentence2);\n  CHECK(std::get<0>(out_tupull) == 2);\n  CHECK(std::get<1>(out_tupull) == 7);\n  CHECK(std::get<2>(out_tupull) == -3.8);\n  CHECK(std::get<3>(out_tupull) == -20.9);\n  CHECK(sum_value == approx(20.9));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_TupleSlice.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <string>\n#include <tuple>\n\n#include \"Utilities/TupleSlice.hpp\"\n\nnamespace {\n\nstruct ConstructionObserver {\n  ConstructionObserver() = default;\n  ConstructionObserver(const ConstructionObserver& /*rhs*/) {\n    status = \"copy-constructed\";\n  }\n  ConstructionObserver& operator=(const ConstructionObserver&) = delete;\n  ConstructionObserver(ConstructionObserver&& rhs) {\n    status = \"move-constructed\";\n    rhs.status = \"move-constructed-away\";\n  }\n  ConstructionObserver& operator=(ConstructionObserver&& rhs) {\n    status = \"moved\";\n    rhs.status = \"moved-away\";\n    return *this;\n  };\n  ~ConstructionObserver() = default;\n\n  std::string status = \"initial\";\n};\n\n}  // namespace\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.TupleSlice\", \"[Utilities][Unit]\") {\n  CHECK(tuple_slice<1, 3>(std::tuple<int, float, double, std::string>{\n            1, 2., 3., \"\"}) == std::tuple<float, double>{2., 3.});\n  CHECK(tuple_head<2>(std::tuple<int, float, double, std::string>{\n            1, 2., 3., \"\"}) == std::tuple<int, float>{1, 2.});\n  CHECK(tuple_tail<2>(std::tuple<int, float, double, std::string>{\n            1, 2., 3., \"\"}) == std::tuple<double, std::string>{3., \"\"});\n  CHECK(tuple_slice<0, 1>(std::tuple<int>{1}) == std::tuple<int>{1});\n  CHECK(tuple_head<1>(std::tuple<int>{1}) == std::tuple<int>{1});\n  CHECK(tuple_tail<1>(std::tuple<int>{1}) == std::tuple<int>{1});\n  CHECK(tuple_slice<0, 0>(std::tuple<int>{1}) == std::tuple<>{});\n  CHECK(tuple_head<0>(std::tuple<int>{1}) == std::tuple<>{});\n  CHECK(tuple_tail<0>(std::tuple<int>{1}) == std::tuple<>{});\n  CHECK(tuple_slice<0, 0>(std::tuple<>{}) == std::tuple<>{});\n  CHECK(tuple_head<0>(std::tuple<>{}) == std::tuple<>{});\n  CHECK(tuple_tail<0>(std::tuple<>{}) == std::tuple<>{});\n  {\n    INFO(\"Copy tuple of reference types\");\n    ConstructionObserver a{};\n    ConstructionObserver& b = a;\n    const std::tuple<ConstructionObserver, ConstructionObserver&> tuple{a, b};\n    // Assert that tuple_slice behaves like std::tuple_cat\n    static_assert(std::is_same_v<decltype(tuple_slice<0, 2>(tuple)),\n                                 decltype(std::tuple_cat(tuple))>);\n    const auto sliced_tuple = tuple_slice<0, 2>(tuple);\n    CHECK(b.status == \"initial\");\n    CHECK(std::get<0>(sliced_tuple).status == \"copy-constructed\");\n    CHECK(std::get<1>(sliced_tuple).status == \"initial\");\n  }\n  {\n    INFO(\"Move tuple of reference types\");\n    ConstructionObserver a{};\n    ConstructionObserver& b = a;\n    ConstructionObserver c{};\n    const ConstructionObserver& d = a;\n    ConstructionObserver e{};\n    std::tuple<ConstructionObserver, ConstructionObserver&,\n               ConstructionObserver&&, const ConstructionObserver&,\n               const ConstructionObserver&&>\n        tuple{a, b, std::move(c), d, std::move(e)};\n    // Assert that tuple_slice behaves like std::tuple_cat\n    static_assert(std::is_same_v<decltype(tuple_slice<0, 5>(std::move(tuple))),\n                                 decltype(std::tuple_cat(std::move(tuple)))>);\n    const auto moved_tuple = tuple_slice<0, 5>(std::move(tuple));\n    CHECK(b.status == \"initial\");\n    CHECK(std::get<0>(moved_tuple).status == \"move-constructed\");\n    CHECK(std::get<1>(moved_tuple).status == \"initial\");\n    CHECK(std::get<2>(moved_tuple).status == \"initial\");\n    CHECK(std::get<3>(moved_tuple).status == \"initial\");\n    CHECK(std::get<4>(moved_tuple).status == \"initial\");\n  }\n  {\n    INFO(\"Works with non-tuple containers\");\n    CHECK(tuple_head<1>(std::array<double, 2>{{1., 2.}}) ==\n          std::tuple<double>{{1.}});\n    CHECK(tuple_head<1>(std::pair<int, float>{1, 2.}) == std::tuple<int>{1});\n  }\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_VectorAlgebra.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <random>\n\n#include \"DataStructures/ComplexDataVector.hpp\"\n#include \"DataStructures/DataVector.hpp\"\n#include \"DataStructures/ModalVector.hpp\"\n#include \"Framework/TestHelpers.hpp\"\n#include \"Helpers/DataStructures/MakeWithRandomValues.hpp\"\n#include \"Utilities/Gsl.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n#include \"Utilities/VectorAlgebra.hpp\"\n\ntemplate <typename RhsVectorType, typename LhsVectorType>\nvoid test_outer_product() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> size_distribution{5, 10};\n  UniformCustomDistribution<\n      tt::get_fundamental_type_t<get_vector_element_type_t<LhsVectorType>>>\n      lhs_distribution{-2.0, 2.0};\n  UniformCustomDistribution<\n      tt::get_fundamental_type_t<get_vector_element_type_t<RhsVectorType>>>\n      rhs_distribution{-2.0, 2.0};\n  const LhsVectorType lhs = make_with_random_values<LhsVectorType>(\n      make_not_null(&gen), make_not_null(&lhs_distribution),\n      size_distribution(gen));\n  const RhsVectorType rhs = make_with_random_values<RhsVectorType>(\n      make_not_null(&gen), make_not_null(&rhs_distribution),\n      size_distribution(gen));\n  const auto outer_test = outer_product(lhs, rhs);\n  size_t product_index = 0;\n  for (const auto& b : rhs) {\n    for (const auto& a : lhs) {\n      // Iterable approx has overloads for both real and complex types\n      CHECK_ITERABLE_APPROX(outer_test[product_index], a * b);\n      ++product_index;\n    }\n  }\n}\n\ntemplate <typename VectorType>\nvoid test_n_copies() {\n  MAKE_GENERATOR(gen);\n  UniformCustomDistribution<size_t> size_distribution{5, 10};\n  UniformCustomDistribution<\n      tt::get_fundamental_type_t<get_vector_element_type_t<VectorType>>>\n      element_distribution{-2.0, 2.0};\n  const VectorType to_repeat = make_with_random_values<VectorType>(\n      make_not_null(&gen), make_not_null(&element_distribution),\n      size_distribution(gen));\n  size_t repeats = size_distribution(gen);\n  const auto repeated = create_vector_of_n_copies(to_repeat, repeats);\n  for (size_t i = 0; i < repeats; ++i) {\n    for (size_t j = 0; j < to_repeat.size(); ++j) {\n      CHECK(to_repeat[j] == repeated[j + i * to_repeat.size()]);\n    }\n  }\n}\n\nSPECTRE_TEST_CASE(\"Unit.DataStructures.VectorAlgebra\",\n                  \"[Unit][DataStructures]\") {\n  test_outer_product<ComplexDataVector, ComplexDataVector>();\n  test_outer_product<DataVector, ComplexDataVector>();\n  test_outer_product<ComplexDataVector, DataVector>();\n  test_outer_product<DataVector, DataVector>();\n\n  test_n_copies<DataVector>();\n  test_n_copies<ComplexDataVector>();\n  test_n_copies<ModalVector>();\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/Test_WrapText.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <cstddef>\n#include <string>\n\n#include \"Utilities/WrapText.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.WrapText\", \"[Utilities][Unit]\") {\n  CHECK(\n      wrap_text(\n          \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n          \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n          \"aa\",\n          50, \"         \") ==\n      std::string(\"         aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-\\n\"\n                  \"         aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-\\n\"\n                  \"         aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-\\n\"\n                  \"         aaaaaaaaaaaaaaaaaa\"));\n  CHECK(wrap_text(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n                  \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n                  \"aaaaaaaaaaaaaaaaaa\",\n                  50) ==\n        std::string(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-\\n\"\n                    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-\\n\"\n                    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"));\n\n  CHECK(wrap_text(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n                  \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\n                  50) ==\n        std::string(\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-\\n\"\n                    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"));\n\n  CHECK(wrap_text(\"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                  \"I want to wrap \\n\\nthis text  \"\n                  \"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                  \"after about 50 characters please.\",\n                  50, \"   \") ==\n        std::string(\"   thisisareallylongwordthatwouldcauseproblemssow-\\n\"\n                    \"   eneedtoerror I want to wrap \\n   \\n   \"\n                    \"this text \\n\"\n                    \"   thisisareallylongwordthatwouldcauseproblemssow-\\n\"\n                    \"   eneedtoerror after about 50 characters please.\"));\n  CHECK(wrap_text(\"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                  \"I want to wrap \\n\\nthis text  \"\n                  \"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                  \"after about 50 characters please.\",\n                  50) ==\n        std::string(\"thisisareallylongwordthatwouldcauseproblemssowene-\\n\"\n                    \"edtoerror I want to wrap \\n\\n\"\n                    \"this text \\n\"\n                    \"thisisareallylongwordthatwouldcauseproblemssowene-\\n\"\n                    \"edtoerror after about 50 characters please.\"));\n  CHECK(\n      wrap_text(\"   thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                \"I want to wrap \\n\\nthis text  \"\n                \"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                \"after about 50 characters please.\",\n                50) ==\n      std::string(\"   thisisareallylongwordthatwouldcauseproblemssow-\\n\"\n                  \"eneedtoerror I want to wrap \\n\\n\"\n                  \"this text \\n\"\n                  \"thisisareallylongwordthatwouldcauseproblemssowene-\\n\"\n                  \"edtoerror after about 50 characters please.\"));\n  CHECK(wrap_text(\"I want to wrap \\n\\nthis text  \"\n                  \"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                  \"after about 50 characters please.\",\n                  50) ==\n        std::string(\"I want to wrap \\n\\n\"\n                    \"this text \\n\"\n                    \"thisisareallylongwordthatwouldcauseproblemssowene-\\n\"\n                    \"edtoerror after about 50 characters please.\"));\n  CHECK(wrap_text(\"I want to wrap \\n\\nthis text  \"\n                  \"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                  \"after about 20 characters please.\",\n                  20) ==\n        std::string(\"I want to wrap \\n\\nthis text \\n\"\n                    \"thisisareallylongwo-\\nrdthatwouldcausepro-\\n\"\n                    \"blemssoweneedtoerror\\nafter about 20\\n\"\n                    \"characters please.\"));\n  CHECK(\n      wrap_text(\"I want to wrap \\n\\nthis text\\n\"\n                \"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                \"after about 20 characters please.\",\n                20) ==\n      std::string(\"I want to wrap \\n\\nthis text\\nthisisareallylongwo-\\n\"\n                  \"rdthatwouldcausepro-\\nblemssoweneedtoerror\\nafter about 20\\n\"\n                  \"characters please.\"));\n  CHECK(wrap_text(\"I want to wrap \\n\\nthis text\\nand this text\\n\"\n                  \"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                  \"after about 50 characters please.\",\n                  50, \"    \") ==\n        std::string(\n            \"    I want to wrap \\n    \\n    this text\\n    and this text\\n\"\n            \"    thisisareallylongwordthatwouldcauseproblemsso-\\n\"\n            \"    weneedtoerror after about 50 characters\\n\"\n            \"    please.\"));\n  CHECK(wrap_text(\"I want to wrap \\n\\na\\nand this text\\n\"\n                  \"thisisareallylongwordthatwouldcauseproblemssoweneedtoerror \"\n                  \"after about 50 characters please.\",\n                  50, \"    \") ==\n        std::string(\n            \"    I want to wrap \\n    \\n    a\\n    and this text\\n\"\n            \"    thisisareallylongwordthatwouldcauseproblemsso-\\n\"\n            \"    weneedtoerror after about 50 characters\\n\"\n            \"    please.\"));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nset(LIBRARY \"Test_TypeTraits\")\n\nset(LIBRARY_SOURCES\n  Test_ArraySize.cpp\n  Test_CreateGetStaticMemberVariableOrDefault.cpp\n  Test_CreateGetTypeAliasOrDefault.cpp\n  Test_CreateHasStaticMemberVariable.cpp\n  Test_CreateHasTypeAlias.cpp\n  Test_CreateIsCallable.cpp\n  Test_FastPointerCast.cpp\n  Test_FunctionInfo.cpp\n  Test_GetFundamentalType.cpp\n  Test_HasEquivalence.cpp\n  Test_HasInequivalence.cpp\n  Test_IsA.cpp\n  Test_IsCallable.cpp\n  Test_IsComplexOfFundamental.cpp\n  Test_IsInteger.cpp\n  Test_IsIterable.cpp\n  Test_IsMaplike.cpp\n  Test_IsStdArrayOfSize.cpp\n  Test_IsStreamable.cpp\n  Test_RemoveReferenceWrapper.cpp\n  )\n\nadd_test_library(${LIBRARY} \"${LIBRARY_SOURCES}\")\n\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_ArraySize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n\n#include \"Utilities/TypeTraits/ArraySize.hpp\"\n\nnamespace {\nclass A {};\n}  // namespace\n\n// [array_size_example]\nstatic_assert(tt::array_size<std::array<double, 3>>::value == 3,\n              \"Failed testing type trait array_size\");\nstatic_assert(tt::array_size<std::array<A, 4>>::value == 4,\n              \"Failed testing type trait array_size\");\n// [array_size_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_CanBeCopyConstructed.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <unordered_map>\n#include <vector>\n\n#include \"Utilities/NonCopyable.hpp\"\n#include \"Utilities/TypeTraits/CanBeCopyConstructed.hpp\"\n\nstatic_assert(tt::can_be_copy_constructed_v<int>,\n              \"Failed testing type trait is_copy_constructible\");\nstatic_assert(tt::can_be_copy_constructed_v<std::vector<int>>,\n              \"Failed testing type trait is_copy_constructible\");\nstatic_assert(tt::can_be_copy_constructed_v<std::unordered_map<int, int>>,\n              \"Failed testing type trait is_copy_constructible\");\nstatic_assert(\n    not tt::can_be_copy_constructed_v<std::unordered_map<int, NonCopyable>>,\n    \"Failed testing type trait is_copy_constructible\");\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_CreateGetStaticMemberVariableOrDefault.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/TypeTraits/CreateGetStaticMemberVariableOrDefault.hpp\"\n\nnamespace {\nCREATE_GET_STATIC_MEMBER_VARIABLE_OR_DEFAULT(foo)\n\nstruct WithFoo {\n  static constexpr int foo = 3;\n};\n\nstruct WithoutFoo {};\n\nstatic_assert(get_foo_or_default_v<WithFoo, 7> == 3);\nstatic_assert(get_foo_or_default_v<WithoutFoo, 7> == 7);\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_CreateGetTypeAliasOrDefault.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <type_traits>\n\n#include \"Utilities/NoSuchType.hpp\"\n#include \"Utilities/TMPL.hpp\"\n#include \"Utilities/TypeTraits/CreateGetTypeAliasOrDefault.hpp\"\n\nnamespace {\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(foo)\nCREATE_GET_TYPE_ALIAS_OR_DEFAULT(foobar)\nstruct testing_create_has_type_alias {\n  using foo = int;\n};\n\nstatic_assert(\n    std::is_same_v<\n        get_foo_or_default_t<testing_create_has_type_alias, NoSuchType>, int>);\nstatic_assert(\n    std::is_same_v<\n        get_foobar_or_default_t<testing_create_has_type_alias, NoSuchType>,\n        NoSuchType>);\nstatic_assert(std::is_same_v<tmpl::apply<get_foo_or_default<tmpl::_1, double>,\n                                         testing_create_has_type_alias>,\n                             int>);\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_CreateHasStaticMemberVariable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n\n#include \"Utilities/TypeTraits/CreateHasStaticMemberVariable.hpp\"\n\nnamespace {\n// [CREATE_HAS_EXAMPLE]\nCREATE_HAS_STATIC_MEMBER_VARIABLE(foo)\nCREATE_HAS_STATIC_MEMBER_VARIABLE_V(foo)\nCREATE_HAS_STATIC_MEMBER_VARIABLE(foobar)\nCREATE_HAS_STATIC_MEMBER_VARIABLE_V(foobar)\nstruct testing_create_has_static_member_variable {\n  static constexpr size_t foo = 1;\n};\n\nstatic_assert(has_foo_v<testing_create_has_static_member_variable>,\n              \"Failed testing CREATE_HAS_STATIC_MEMBER_VARIABLE\");\nstatic_assert(has_foo_v<testing_create_has_static_member_variable, size_t>,\n              \"Failed testing CREATE_HAS_STATIC_MEMBER_VARIABLE\");\nstatic_assert(not has_foo_v<testing_create_has_static_member_variable, int>,\n              \"Failed testing CREATE_HAS_STATIC_MEMBER_VARIABLE\");\nstatic_assert(not has_foobar_v<testing_create_has_static_member_variable>,\n              \"Failed testing CREATE_HAS_STATIC_MEMBER_VARIABLE\");\nstatic_assert(\n    not has_foobar_v<testing_create_has_static_member_variable, size_t>,\n    \"Failed testing CREATE_HAS_STATIC_MEMBER_VARIABLE\");\n// [CREATE_HAS_EXAMPLE]\n\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_CreateHasTypeAlias.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/TypeTraits/CreateHasTypeAlias.hpp\"\n\nnamespace {\n// [CREATE_HAS_TYPE_ALIAS]\nCREATE_HAS_TYPE_ALIAS(foo_alias)\nCREATE_HAS_TYPE_ALIAS_V(foo_alias)\nCREATE_HAS_TYPE_ALIAS(foobar_alias)\nCREATE_HAS_TYPE_ALIAS_V(foobar_alias)\nstruct testing_create_has_type_alias {\n  using foo_alias = int;\n};\n\nstatic_assert(has_foo_alias_v<testing_create_has_type_alias>,\n              \"Failed testing CREATE_HAS_TYPE_ALIAS\");\nstatic_assert(has_foo_alias_v<testing_create_has_type_alias, int>,\n              \"Failed testing CREATE_HAS_TYPE_ALIAS\");\nstatic_assert(not has_foo_alias_v<testing_create_has_type_alias, double>,\n              \"Failed testing CREATE_HAS_TYPE_ALIAS\");\nstatic_assert(not has_foobar_alias_v<testing_create_has_type_alias>,\n              \"Failed testing CREATE_HAS_TYPE_ALIAS\");\nstatic_assert(not has_foobar_alias_v<testing_create_has_type_alias, int>,\n              \"Failed testing CREATE_HAS_TYPE_ALIAS\");\n// [CREATE_HAS_TYPE_ALIAS]\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_CreateIsCallable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <cstddef>\n\n#include \"Utilities/TypeTraits/CreateIsCallable.hpp\"\n\nnamespace {\n// [CREATE_IS_CALLABLE_EXAMPLE]\nCREATE_IS_CALLABLE(foo)\nCREATE_IS_CALLABLE_V(foo)\nCREATE_IS_CALLABLE_R_V(foo)\nCREATE_IS_CALLABLE(foobar)\nCREATE_IS_CALLABLE_V(foobar)\nCREATE_IS_CALLABLE_R_V(foobar)\nstruct bar {\n  size_t foo(int /*unused*/, double /*unused*/) { return size_t{0}; }\n};\n\nstatic_assert(is_foo_callable_v<bar, int, double>,\n              \"Failed testing CREATE_IS_CALLABLE\");\nstatic_assert(is_foo_callable_r_v<size_t, bar, int, double>,\n              \"Failed testing CREATE_IS_CALLABLE\");\nstatic_assert(not is_foo_callable_v<bar, int>,\n              \"Failed testing CREATE_IS_CALLABLE\");\nstatic_assert(not is_foo_callable_v<bar>, \"Failed testing CREATE_IS_CALLABLE\");\nstatic_assert(not is_foobar_callable_r_v<size_t, bar, int, double>,\n              \"Failed testing CREATE_IS_CALLABLE\");\nstatic_assert(not is_foo_callable_r_v<int, bar, int, double>,\n              \"Failed testing CREATE_IS_CALLABLE\");\nstatic_assert(not is_foobar_callable_v<bar, int, double>,\n              \"Failed testing CREATE_IS_CALLABLE\");\n// [CREATE_IS_CALLABLE_EXAMPLE]\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_FastPointerCast.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Framework/TestingFramework.hpp\"\n\n#include <memory>\n\n#include \"Utilities/TypeTraits/FastPointerCast.hpp\"\n\nSPECTRE_TEST_CASE(\"Unit.Utilities.TypeTraits.FastPointerCast\",\n                  \"[Unit][Utilities]\") {\n  struct B {\n    B() = default;\n    B(const B&) = delete;\n    B(B&&) = delete;\n    B& operator=(const B&) = delete;\n    B& operator=(B&&) = delete;\n    virtual ~B() = default;\n  };\n  struct D1 : virtual B {\n    int mutable_func() { return 1; }\n    int const_func() const { return 11; }\n  };\n  struct D2 : virtual B {\n    int mutable_func() { return 2; }\n    int const_func() const { return 12; }\n  };\n  struct C : D1, D2 {\n    int mutable_func() { return 3; }\n    int const_func() const { return 13; }\n  };\n  std::unique_ptr<B> d1 = std::make_unique<D1>();\n  std::unique_ptr<B> d2 = std::make_unique<D2>();\n  std::unique_ptr<B> c = std::make_unique<C>();\n\n  CHECK(1 == (tt::fast_pointer_cast<D1* const>(d1.get())->mutable_func()));\n  CHECK(2 == (tt::fast_pointer_cast<D2* const>(d2.get())->mutable_func()));\n  CHECK(3 == (tt::fast_pointer_cast<C* const>(c.get())->mutable_func()));\n\n  CHECK(11 == (tt::fast_pointer_cast<const D1* const>(&std::as_const(*d1))\n                   ->const_func()));\n  CHECK(12 == (tt::fast_pointer_cast<const D2* const>(&std::as_const(*d2))\n                   ->const_func()));\n  CHECK(13 == (tt::fast_pointer_cast<const C* const>(&std::as_const(*c))\n                   ->const_func()));\n}\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_FunctionInfo.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/FunctionInfo.hpp\"\n\nnamespace {\n// The name syntax is ReturnType_NumberOfArgs_Counter\n// clang-tidy: no non-const references (we need them for testing)\nvoid void_none() {}\ndouble double_none() { return 1.0; }\nvoid void_one_0(double /*unused*/) {}\nvoid void_one_1(const double& /*unused*/) {}\nvoid void_one_2(double& /*unused*/) {}  // NOLINT\nvoid void_one_3(double* /*unused*/) {}\nvoid void_one_4(const double* /*unused*/) {}\nvoid void_one_5(const double* const /*unused*/) {}\n\nint int_one_0(double /*unused*/) { return 0; }\nint int_one_1(const double& /*unused*/) { return 0; }  // NOLINT\nint int_one_2(double& /*unused*/) { return 0; }        // NOLINT\nint int_one_3(double* /*unused*/) { return 0; }\nint int_one_4(const double* /*unused*/) { return 0; }\nint int_one_5(const double* const /*unused*/) { return 0; }\n\nint int_two_0(double /*unused*/, char* /*unused*/) { return 0; }\nint int_two_1(const double& /*unused*/, const char* /*unused*/) { return 0; }\nint int_two_2(double& /*unused*/, const char* const /*unused*/) {  // NOLINT\n  return 0;\n}\nint int_two_3(double* /*unused*/, char& /*unused*/) { return 0; }  // NOLINT\nint int_two_4(const double* /*unused*/, const char& /*unused*/) { return 0; }\nint int_two_5(const double* const /*unused*/, char /*unused*/) { return 0; }\n\n// We have to NOLINT these for 2 reasons:\n// - no non-const references\n// - ClangTidy wants a () after the macros, which expands to not what we want\n#define FUNCTION_INFO_TEST(NAME, PREFIX, POSTFIX)                              \\\n  struct FunctionInfoTest##NAME {                                              \\\n    PREFIX void void_none() POSTFIX {}                            /* NOLINT */ \\\n    PREFIX double double_none() POSTFIX { return 1.0; }           /* NOLINT */ \\\n    PREFIX void void_one_0(double /*unused*/) POSTFIX {}          /* NOLINT */ \\\n    PREFIX void void_one_1(const double& /*unused*/) POSTFIX {}   /* NOLINT */ \\\n    PREFIX void void_one_2(double& /*unused*/) POSTFIX {}         /* NOLINT */ \\\n    PREFIX void void_one_3(double* /*unused*/) POSTFIX {}         /* NOLINT */ \\\n    PREFIX void void_one_4(const double* /*unused*/) POSTFIX {}   /* NOLINT */ \\\n    PREFIX void void_one_5(                                       /* NOLINT */ \\\n                           const double* const /*unused*/)        /* NOLINT */ \\\n        POSTFIX {}                                                /* NOLINT */ \\\n    PREFIX int int_one_0(double /*unused*/) POSTFIX { return 0; } /* NOLINT */ \\\n    PREFIX int int_one_1(const double& /*unused*/) POSTFIX {      /* NOLINT */ \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_one_2(double& /*unused*/) POSTFIX { /* NOLINT */            \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_one_3(double* /*unused*/) POSTFIX { /* NOLINT */            \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_one_4(const double* /*unused*/) POSTFIX { /* NOLINT */      \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_one_5(const double* const /*unused*/) /* NOLINT */          \\\n        POSTFIX {                                        /* NOLINT */          \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_two_0(double /*unused*/,          /* NOLINT */              \\\n                         char* /*unused*/) POSTFIX { /* NOLINT */              \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_two_1(const double& /*unused*/,         /* NOLINT */        \\\n                         const char* /*unused*/) POSTFIX { /* NOLINT */        \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_two_2(double& /*unused*/,                     /* NOLINT */  \\\n                         const char* const /*unused*/) POSTFIX { /* NOLINT */  \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_two_3(double* /*unused*/,         /* NOLINT */              \\\n                         char& /*unused*/) POSTFIX { /* NOLINT */              \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_two_4(const double* /*unused*/,         /* NOLINT */        \\\n                         const char& /*unused*/) POSTFIX { /* NOLINT */        \\\n      return 0;                                                                \\\n    }                                                                          \\\n    PREFIX int int_two_5(const double* const /*unused*/, /* NOLINT */          \\\n                         char /*unused*/) POSTFIX {      /* NOLINT */          \\\n      return 0;                                                                \\\n    }                                                                          \\\n  };\n\nFUNCTION_INFO_TEST(, , )\nFUNCTION_INFO_TEST(Const, , const)\nFUNCTION_INFO_TEST(Static, static, )\nFUNCTION_INFO_TEST(Noexcept, , )\nFUNCTION_INFO_TEST(ConstNoexcept, , const)\nFUNCTION_INFO_TEST(StaticNoexcept, static, )\n\n#undef FUNCTION_INFO_TEST\n\nstruct FunctionInfoTestVirtual {\n  FunctionInfoTestVirtual(const FunctionInfoTestVirtual&) = default;\n  FunctionInfoTestVirtual& operator=(const FunctionInfoTestVirtual&) = default;\n  FunctionInfoTestVirtual(FunctionInfoTestVirtual&&) = default;\n  FunctionInfoTestVirtual& operator=(FunctionInfoTestVirtual&&) = default;\n  virtual ~FunctionInfoTestVirtual() = default;\n  virtual void void_none() {}\n  virtual double double_none() { return 1.0; }\n  virtual void void_one_0(double /*unused*/) {}\n  virtual void void_one_1(const double& /*unused*/) {}\n  virtual void void_one_2(double& /*unused*/) {}  // NOLINT\n  virtual void void_one_3(double* /*unused*/) {}\n  virtual void void_one_4(const double* /*unused*/) {}\n  virtual void void_one_5(const double* const /*unused*/) {}\n\n  virtual int int_one_0(double /*unused*/) { return 0; }\n  virtual int int_one_1(const double& /*unused*/) { return 0; }\n  virtual int int_one_2(double& /*unused*/) { return 0; }  // NOLINT\n  virtual int int_one_3(double* /*unused*/) { return 0; }\n  virtual int int_one_4(const double* /*unused*/) { return 0; }\n  virtual int int_one_5(const double* const /*unused*/) { return 0; }\n\n  virtual int int_two_0(double /*unused*/, char* /*unused*/) { return 0; }\n  virtual int int_two_1(const double& /*unused*/,  // NOLINT\n                        const char* /*unused*/) {\n    return 0;\n  }\n  virtual int int_two_2(double& /*unused*/,  // NOLINT\n                        const char* const /*unused*/) {\n    return 0;\n  }\n  virtual int int_two_3(double* /*unused*/, char& /*unused*/) {  // NOLINT\n    return 0;\n  }\n  virtual int int_two_4(const double* /*unused*/, const char& /*unused*/) {\n    return 0;\n  }\n  virtual int int_two_5(const double* const /*unused*/, char /*unused*/) {\n    return 0;\n  }\n};\n\nstruct FunctionInfoTestVirtualBase {\n  // Some of these are redundant because we don't need to const things in\n  // forward decls. However, so that we can use the generic interface we keep\n  // the functions.\n  FunctionInfoTestVirtualBase(const FunctionInfoTestVirtualBase&) = default;\n  FunctionInfoTestVirtualBase& operator=(const FunctionInfoTestVirtualBase&) =\n      default;\n  FunctionInfoTestVirtualBase(FunctionInfoTestVirtualBase&&) = default;\n  FunctionInfoTestVirtualBase& operator=(FunctionInfoTestVirtualBase&&) =\n      default;\n  virtual ~FunctionInfoTestVirtualBase() = default;\n  virtual void void_none() = 0;\n  virtual double double_none() = 0;\n  virtual void void_one_0(double /*unused*/) = 0;\n  virtual void void_one_1(const double& /*unused*/) = 0;\n  virtual void void_one_2(double& /*unused*/) = 0;  // NOLINT\n  virtual void void_one_3(double* /*unused*/) = 0;\n  virtual void void_one_4(const double* /*unused*/) = 0;\n  virtual void void_one_5(const double* /*unused*/) = 0;\n\n  virtual int int_one_0(double /*unused*/) = 0;\n  virtual int int_one_1(const double& /*unused*/) = 0;\n  virtual int int_one_2(double& /*unused*/) = 0;  // NOLINT\n  virtual int int_one_3(double* /*unused*/) = 0;\n  virtual int int_one_4(const double* /*unused*/) = 0;\n  virtual int int_one_5(const double* /*unused*/) = 0;\n\n  virtual int int_two_0(double /*unused*/, char* /*unused*/) = 0;\n  virtual int int_two_1(const double& /*unused*/,                   // NOLINT\n                        const char* /*unused*/) = 0;                // NOLINT\n  virtual int int_two_2(double& /*unused*/,                         // NOLINT\n                        const char* /*unused*/) = 0;                // NOLINT\n  virtual int int_two_3(double* /*unused*/, char& /*unused*/) = 0;  // NOLINT\n  virtual int int_two_4(const double* /*unused*/, const char& /*unused*/) = 0;\n  virtual int int_two_5(const double* /*unused*/, char /*unused*/) = 0;\n};\n\n// Check that function_info works after LazyF is applied to a variety of\n// different functions. LazyF is identity, add_pointer, add_const<add_pointer>,\n// add_volatile<add_pointer>, and add_cv<add_pointer>. This allows us to check\n// all the different combinations with a single class.\ntemplate <template <class> class LazyF>\nstruct check_function_info {\n  template <class T>\n  using F = typename LazyF<T>::type;\n\n  template <class Function, class ReturnType, class ClassType, class... Args>\n  struct Check {\n    static_assert(\n        std::is_same_v<ReturnType,\n                       typename tt::function_info<Function>::return_type>,\n        \"Failed testing function_info\");\n    static_assert(\n        std::is_same_v<tmpl::list<Args...>,\n                       typename tt::function_info<Function>::argument_types>,\n        \"Failed testing function_info\");\n    static_assert(\n        std::is_same_v<ClassType,\n                       typename tt::function_info<Function>::class_type>,\n        \"Failed testing function_info\");\n    static constexpr bool t = true;\n  };\n\n  static constexpr Check<F<decltype(void_none)>, void, void> t_void_none{};\n  static constexpr Check<F<decltype(double_none)>, double, void>\n      t_double_none{};\n\n  static constexpr Check<F<decltype(void_one_0)>, void, void, double>\n      t_void_one_0{};\n  static constexpr Check<F<decltype(void_one_1)>, void, void, const double&>\n      t_void_one_1{};\n  static constexpr Check<F<decltype(void_one_2)>, void, void, double&>\n      t_void_one_2{};\n  static constexpr Check<F<decltype(void_one_3)>, void, void, double*>\n      t_void_one_3{};\n  static constexpr Check<F<decltype(void_one_4)>, void, void, const double*>\n      t_void_one_4{};\n  static constexpr Check<F<decltype(void_one_5)>, void, void, const double*>\n      t_void_one_5{};\n\n  static constexpr Check<F<decltype(int_one_0)>, int, void, double>\n      t_int_one_0{};\n  static constexpr Check<F<decltype(int_one_1)>, int, void, const double&>\n      t_int_one_1{};\n  static constexpr Check<F<decltype(int_one_2)>, int, void, double&>\n      t_int_one_2{};\n  static constexpr Check<F<decltype(int_one_3)>, int, void, double*>\n      t_int_one_3{};\n  static constexpr Check<F<decltype(int_one_4)>, int, void, const double*>\n      t_int_one_4{};\n  static constexpr Check<F<decltype(int_one_5)>, int, void, const double*>\n      t_int_one_5{};\n\n  static constexpr Check<F<decltype(int_two_0)>, int, void, double, char*>\n      t_int_two_0{};\n  static constexpr Check<F<decltype(int_two_1)>, int, void, const double&,\n                         const char*>\n      t_int_two_1{};\n  static constexpr Check<F<decltype(int_two_2)>, int, void, double&,\n                         const char*>\n      t_int_two_2{};\n  static constexpr Check<F<decltype(int_two_3)>, int, void, double*, char&>\n      t_int_two_3{};\n  static constexpr Check<F<decltype(int_two_4)>, int, void, const double*,\n                         const char&>\n      t_int_two_4{};\n  static constexpr Check<F<decltype(int_two_5)>, int, void, const double*, char>\n      t_int_two_5{};\n\n  template <class Scope, class Class = Scope>\n  struct CheckClass {\n    // We have to remove the pointer since we are already adding a pointer\n    // sometimes using F\n    static constexpr bool t_void_none =\n        Check<std::remove_pointer_t<F<decltype(&Scope::void_none)>>, void,\n              Class>::t;\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::double_none)>>, double, Class>\n        t_double_none{};\n\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::void_one_0)>>, void, Class,\n        double>\n        t_void_one_0{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::void_one_1)>>, void, Class,\n        const double&>\n        t_void_one_1{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::void_one_2)>>, void, Class,\n        double&>\n        t_void_one_2{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::void_one_3)>>, void, Class,\n        double*>\n        t_void_one_3{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::void_one_4)>>, void, Class,\n        const double*>\n        t_void_one_4{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::void_one_5)>>, void, Class,\n        const double*>\n        t_void_one_5{};\n\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_one_0)>>, int, Class,\n        double>\n        t_int_one_0{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_one_1)>>, int, Class,\n        const double&>\n        t_int_one_1{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_one_2)>>, int, Class,\n        double&>\n        t_int_one_2{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_one_3)>>, int, Class,\n        double*>\n        t_int_one_3{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_one_4)>>, int, Class,\n        const double*>\n        t_int_one_4{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_one_5)>>, int, Class,\n        const double*>\n        t_int_one_5{};\n\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_two_0)>>, int, Class,\n        double, char*>\n        t_int_two_0{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_two_1)>>, int, Class,\n        const double&, const char*>\n        t_int_two_1{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_two_2)>>, int, Class,\n        double&, const char*>\n        t_int_two_2{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_two_3)>>, int, Class,\n        double*, char&>\n        t_int_two_3{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_two_4)>>, int, Class,\n        const double*, const char&>\n        t_int_two_4{};\n    static constexpr Check<\n        std::remove_pointer_t<F<decltype(&Scope::int_two_5)>>, int, Class,\n        const double*, char>\n        t_int_two_5{};\n  };\n\n  static constexpr CheckClass<FunctionInfoTest> non_const_members{};\n  static constexpr CheckClass<FunctionInfoTestConst> const_members{};\n  static constexpr CheckClass<FunctionInfoTestConstNoexcept>\n      const_noexcept_members{};\n  static constexpr CheckClass<FunctionInfoTestNoexcept> _members{};\n  static constexpr CheckClass<FunctionInfoTestVirtual> virtual_members{};\n  static constexpr CheckClass<FunctionInfoTestVirtualBase>\n      virtual_base_members{};\n  static constexpr CheckClass<FunctionInfoTestStatic, void> static_members{};\n  static constexpr CheckClass<FunctionInfoTestStaticNoexcept, void>\n      static_noexcept_members{};\n};\n\ntemplate <template <class> class F>\nstruct add_pointer_helper {\n  template <class T>\n  struct impl {\n    using type = typename F<std::add_pointer_t<T>>::type;\n  };\n};\n\ntemplate <class T>\nstruct identity {\n  using type = T;\n};\n\n// Scope function calls to avoid warnings\nstruct TestFunctions {\n  TestFunctions() {\n    (void)check_function_info<identity>{};\n    (void)check_function_info<std::add_pointer>{};\n    (void)check_function_info<add_pointer_helper<std::add_const>::impl>{};\n    (void)check_function_info<add_pointer_helper<std::add_volatile>::impl>{};\n    (void)check_function_info<add_pointer_helper<std::add_cv>::impl>{};\n    // Use these to avoid warnings\n    void_none();\n    (void)double_none();\n\n    double t_double = 1.0;\n    void_one_0(t_double);\n    void_one_1(t_double);\n    void_one_2(t_double);\n    void_one_3(&t_double);\n    void_one_4(&t_double);\n    void_one_5(&t_double);\n\n    (void)int_one_0(t_double);\n    (void)int_one_1(t_double);\n    (void)int_one_2(t_double);\n    (void)int_one_3(&t_double);\n    (void)int_one_4(&t_double);\n    (void)int_one_5(&t_double);\n\n    char t_char = 'a';\n    (void)int_two_0(t_double, &t_char);\n    (void)int_two_1(t_double, &t_char);\n    (void)int_two_2(t_double, &t_char);\n    (void)int_two_3(&t_double, t_char);\n    (void)int_two_4(&t_double, t_char);\n    (void)int_two_5(&t_double, t_char);\n  }\n};\nTestFunctions test_functions{};\n}  // namespace\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_GetFundamentalType.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n#include <complex>\n#include <vector>\n\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/GetFundamentalType.hpp\"\n\n// [get_fundamental_type]\nstatic_assert(\n    std::is_same_v<\n        typename tt::get_fundamental_type<std::array<double, 2>>::type, double>,\n    \"Failed testing get_fundamental_type\");\nstatic_assert(\n    std::is_same_v<\n        typename tt::get_fundamental_type_t<std::vector<std::complex<double>>>,\n        double>,\n    \"Failed testing get_fundamental_type\");\nstatic_assert(std::is_same_v<typename tt::get_fundamental_type_t<int>, int>,\n              \"Failed testing get_fundamental_type\");\n// [get_fundamental_type]\n\nstatic_assert(\n    std::is_same_v<\n        typename tt::get_complex_or_fundamental_type_t<std::array<double, 2>>,\n        double>);\nstatic_assert(std::is_same_v<typename tt::get_complex_or_fundamental_type_t<\n                                 std::vector<std::complex<double>>>,\n                             std::complex<double>>);\nstatic_assert(\n    std::is_same_v<typename tt::get_complex_or_fundamental_type_t<int>, int>);\nstatic_assert(\n    std::is_same_v<\n        typename tt::get_complex_or_fundamental_type_t<std::complex<double>>,\n        std::complex<double>>);\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_HasEquivalence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/TypeTraits/HasEquivalence.hpp\"\n\nnamespace {\nclass A {};\n}  // namespace\n\nnamespace TestHelpers {\nclass C {};\n\nbool operator==(const C&, const C&);\n}  // namespace TestHelpers\n\n// [has_equivalence_example]\nstatic_assert(not tt::has_equivalence<A>::value,\n              \"Failed testing type trait has_equivalence\");\nstatic_assert(tt::has_equivalence<TestHelpers::C>::value,\n              \"Failed testing type trait has_equivalence\");\nstatic_assert(tt::has_equivalence_t<TestHelpers::C>::value,\n              \"Failed testing type trait has_equivalence\");\nstatic_assert(tt::has_equivalence_v<TestHelpers::C>,\n              \"Failed testing type trait has_equivalence\");\n// [has_equivalence_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_HasInequivalence.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/TypeTraits/HasInequivalence.hpp\"\n\nnamespace {\nclass A {};\n}  // namespace\n\nnamespace TestHelpers {\nclass C {};\n\nbool operator!=(const C&, const C&);\n}  // namespace TestHelpers\n\n// [has_inequivalence_example]\nstatic_assert(not tt::has_inequivalence<A>::value,\n              \"Failed testing type trait has_inequivalence\");\nstatic_assert(tt::has_inequivalence<TestHelpers::C>::value,\n              \"Failed testing type trait has_inequivalence\");\nstatic_assert(tt::has_inequivalence_t<TestHelpers::C>::value,\n              \"Failed testing type trait has_inequivalence\");\nstatic_assert(tt::has_inequivalence_v<TestHelpers::C>,\n              \"Failed testing type trait has_inequivalence\");\n// [has_inequivalence_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsA.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <deque>\n#include <forward_list>\n#include <future>\n#include <list>\n#include <map>\n#include <memory>\n#include <queue>\n#include <set>\n#include <stack>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"Utilities/TypeTraits/IsA.hpp\"\n\nnamespace {\nclass A;\nclass C;\nclass D;\n} // namespace\n\n// [is_a_example]\nstatic_assert(tt::is_a<std::vector, std::vector<double>>::value,\n              \"Failed testing type trait is_a<vector>\");\nstatic_assert(not tt::is_a_t<std::vector, double>::value,\n              \"Failed testing type trait is_a<vector>\");\nstatic_assert(tt::is_a_v<std::vector, std::vector<D>>,\n              \"Failed testing type trait is_a<vector>\");\n\nstatic_assert(tt::is_a<std::deque, std::deque<double>>::value,\n              \"Failed testing type trait is_a<deque>\");\nstatic_assert(not tt::is_a<std::deque, double>::value,\n              \"Failed testing type trait is_a<deque>\");\nstatic_assert(tt::is_a<std::deque, std::deque<D>>::value,\n              \"Failed testing type trait is_a<deque>\");\n\nstatic_assert(tt::is_a<std::forward_list, std::forward_list<double>>::value,\n              \"Failed testing type trait is_a<forward_list>\");\nstatic_assert(not tt::is_a<std::forward_list, double>::value,\n              \"Failed testing type trait is_a<forward_list>\");\nstatic_assert(tt::is_a<std::forward_list, std::forward_list<D>>::value,\n              \"Failed testing type trait is_a<forward_list>\");\n\nstatic_assert(tt::is_a<std::list, std::list<double>>::value,\n              \"Failed testing type trait is_a<list>\");\nstatic_assert(not tt::is_a<std::list, double>::value,\n              \"Failed testing type trait is_a<list>\");\nstatic_assert(tt::is_a<std::list, std::list<D>>::value,\n              \"Failed testing type trait is_a<list>\");\n\nstatic_assert(tt::is_a<std::map, std::map<std::string, double>>::value,\n              \"Failed testing type trait is_a<map>\");\nstatic_assert(not tt::is_a<std::map, double>::value,\n              \"Failed testing type trait is_a<map>\");\nstatic_assert(tt::is_a<std::map, std::map<std::string, D>>::value,\n              \"Failed testing type trait is_a<map>\");\n\nstatic_assert(tt::is_a<std::unordered_map,\n                       std::unordered_map<std::string, double>>::value,\n              \"Failed testing type trait is_a<unordered_map>\");\nstatic_assert(not tt::is_a<std::unordered_map, double>::value,\n              \"Failed testing type trait is_a<unordered_map>\");\nstatic_assert(\n    tt::is_a<std::unordered_map, std::unordered_map<std::string, D>>::value,\n    \"Failed testing type trait is_a<unordered_map>\");\n\nstatic_assert(tt::is_a<std::set, std::set<double>>::value,\n              \"Failed testing type trait is_a<set>\");\nstatic_assert(not tt::is_a<std::set, double>::value,\n              \"Failed testing type trait is_a<set>\");\nstatic_assert(tt::is_a<std::set, std::set<D>>::value,\n              \"Failed testing type trait is_a<set>\");\n\nstatic_assert(tt::is_a<std::unordered_set, std::unordered_set<double>>::value,\n              \"Failed testing type trait is_a<unordered_set>\");\nstatic_assert(not tt::is_a<std::unordered_set, double>::value,\n              \"Failed testing type trait is_a<unordered_set>\");\nstatic_assert(tt::is_a<std::unordered_set, std::unordered_set<D>>::value,\n              \"Failed testing type trait is_a<unordered_set>\");\n\nstatic_assert(tt::is_a<std::multiset, std::multiset<double>>::value,\n              \"Failed testing type trait is_a<multiset>\");\nstatic_assert(not tt::is_a<std::multiset, double>::value,\n              \"Failed testing type trait is_a<multiset>\");\nstatic_assert(tt::is_a<std::multiset, std::multiset<D>>::value,\n              \"Failed testing type trait is_a<multiset>\");\n\nstatic_assert(\n    tt::is_a<std::unordered_multiset, std::unordered_multiset<double>>::value,\n    \"Failed testing type trait is_a<unordered_multiset>\");\nstatic_assert(not tt::is_a<std::unordered_multiset, double>::value,\n              \"Failed testing type trait is_a<unordered_multiset>\");\nstatic_assert(\n    tt::is_a<std::unordered_multiset, std::unordered_multiset<D>>::value,\n    \"Failed testing type trait is_a<unordered_multiset>\");\n\nstatic_assert(\n    tt::is_a<std::multimap, std::multimap<std::string, double>>::value,\n    \"Failed testing type trait is_a<multimap>\");\nstatic_assert(not tt::is_a<std::multimap, double>::value,\n              \"Failed testing type trait is_a<multimap>\");\nstatic_assert(tt::is_a<std::multimap, std::multimap<std::string, D>>::value,\n              \"Failed testing type trait is_a<multimap>\");\n\nstatic_assert(tt::is_a<std::unordered_multimap,\n                       std::unordered_multimap<std::string, double>>::value,\n              \"Failed testing type trait is_a<unordered_multimap>\");\nstatic_assert(not tt::is_a<std::unordered_multimap, double>::value,\n              \"Failed testing type trait is_a<unordered_multimap>\");\nstatic_assert(tt::is_a<std::unordered_multimap,\n                       std::unordered_multimap<std::string, D>>::value,\n              \"Failed testing type trait is_a<unordered_multimap>\");\n\nstatic_assert(tt::is_a<std::priority_queue, std::priority_queue<double>>::value,\n              \"Failed testing type trait is_a<priority_queue>\");\nstatic_assert(not tt::is_a<std::priority_queue, double>::value,\n              \"Failed testing type trait is_a<priority_queue>\");\nstatic_assert(tt::is_a<std::priority_queue, std::priority_queue<D>>::value,\n              \"Failed testing type trait is_a<priority_queue>\");\n\nstatic_assert(tt::is_a<std::queue, std::queue<double>>::value,\n              \"Failed testing type trait is_a<queue>\");\nstatic_assert(not tt::is_a<std::queue, double>::value,\n              \"Failed testing type trait is_a<queue>\");\nstatic_assert(tt::is_a<std::queue, std::queue<D>>::value,\n              \"Failed testing type trait is_a<queue>\");\n\nstatic_assert(tt::is_a<std::stack, std::stack<double>>::value,\n              \"Failed testing type trait is_a<stack>\");\nstatic_assert(not tt::is_a<std::stack, double>::value,\n              \"Failed testing type trait is_a<stack>\");\nstatic_assert(tt::is_a<std::stack, std::stack<D>>::value,\n              \"Failed testing type trait is_a<stack>\");\n\nstatic_assert(tt::is_a<std::unique_ptr, std::unique_ptr<double>>::value,\n              \"Failed testing type trait is_a<unique_ptr>\");\nstatic_assert(tt::is_a<std::unique_ptr, std::unique_ptr<C>>::value,\n              \"Failed testing type trait is_a<unique_ptr>\");\nstatic_assert(not tt::is_a<std::unique_ptr, std::shared_ptr<double>>::value,\n              \"Failed testing type trait is_a<unique_ptr>\");\nstatic_assert(not tt::is_a<std::unique_ptr, C>::value,\n              \"Failed testing type trait is_a<unique_ptr>\");\n\nstatic_assert(tt::is_a<std::shared_ptr, std::shared_ptr<double>>::value,\n              \"Failed testing type trait is_a<shared_ptr>\");\nstatic_assert(tt::is_a<std::shared_ptr, std::shared_ptr<C>>::value,\n              \"Failed testing type trait is_a<shared_ptr>\");\nstatic_assert(not tt::is_a<std::shared_ptr, std::unique_ptr<double>>::value,\n              \"Failed testing type trait is_a<shared_ptr>\");\nstatic_assert(not tt::is_a<std::shared_future, C>::value,\n              \"Failed testing type trait is_a<shared_ptr>\");\n\nstatic_assert(tt::is_a<std::weak_ptr, std::weak_ptr<double>>::value,\n              \"Failed testing type trait is_a<weak_ptr>\");\nstatic_assert(tt::is_a<std::weak_ptr, std::weak_ptr<C>>::value,\n              \"Failed testing type trait is_a<weak_ptr>\");\nstatic_assert(not tt::is_a<std::weak_ptr, std::unique_ptr<double>>::value,\n              \"Failed testing type trait is_a<weak_ptr>\");\nstatic_assert(not tt::is_a<std::weak_ptr, C>::value,\n              \"Failed testing type trait is_a<weak_ptr>\");\n\nstatic_assert(tt::is_a<std::tuple, std::tuple<int, double, A>>::value,\n              \"Failed testing type trait is_a\");\nstatic_assert(tt::is_a<std::vector, std::vector<A>>::value,\n              \"Failed testing type trait is_a\");\n\nstatic_assert(tt::is_a<std::future, std::future<double>>::value,\n              \"Failed testing type trait is_a<future>\");\nstatic_assert(tt::is_a<std::future, std::future<std::vector<double>>>::value,\n              \"Failed testing type trait is_a<future>\");\nstatic_assert(not tt::is_a<std::future, std::shared_future<double>>::value,\n              \"Failed testing type trait is_a<future>\");\n\nstatic_assert(tt::is_a<std::shared_future, std::shared_future<double>>::value,\n              \"Failed testing type trait is_a<shared_future>\");\nstatic_assert(tt::is_a<std::shared_future,\n                       std::shared_future<std::vector<double>>>::value,\n              \"Failed testing type trait is_a<shared_future>\");\nstatic_assert(not tt::is_a<std::shared_future, std::future<double>>::value,\n              \"Failed testing type trait is_a<shared_future>\");\n// [is_a_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsCallable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/TypeTraits/IsCallable.hpp\"\n\nnamespace {\nclass A {\n public:\n  int operator()() { return 1; }\n};\n\nclass B {\n public:\n  double operator()(const int /* unused */, const double /* unused */) {\n    return 0.;\n  }\n};\n}  // namespace\n\n// [is_callable_example]\nstatic_assert(not tt::is_callable<A, int, double>::value,\n              \"Failed testing type trait is_callable\");\nstatic_assert(not tt::is_callable<A, int>::value,\n              \"Failed testing type trait is_callable\");\nstatic_assert(tt::is_callable<A>::value,\n              \"Failed testing type trait is_callable\");\nstatic_assert(tt::is_callable<B, int, double>::value,\n              \"Failed testing type trait is_callable\");\nstatic_assert(tt::is_callable_t<B, int, double>::value,\n              \"Failed testing type trait is_callable\");\nstatic_assert(tt::is_callable_v<B, int, double>,\n              \"Failed testing type trait is_callable\");\nstatic_assert(not tt::is_callable<B>::value,\n              \"Failed testing type trait is_callable\");\n// [is_callable_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsComplexOfFundamental.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <complex>\n\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/IsComplexOfFundamental.hpp\"\n\nclass DataVector;\n\n// [is_complex_of_fundamental]\nstatic_assert(tt::is_complex_of_fundamental_v<std::complex<double>>,\n              \"Failed testing is_complex_of_fundamental\");\nstatic_assert(tt::is_complex_of_fundamental_v<std::complex<int>>,\n              \"Failed testing is_complex_of_fundamental\");\nstatic_assert(not tt::is_complex_of_fundamental_v<double>,\n              \"Failed testing is_complex_of_fundamental\");\nstatic_assert(not tt::is_complex_of_fundamental_v<std::complex<DataVector>>,\n              \"Failed testing is_complex_of_fundamental\");\nstatic_assert(not tt::is_complex_of_fundamental_v<DataVector>,\n              \"Failed testing is_complex_of_fundamental\");\n// [is_complex_of_fundamental]\n\nstatic_assert(tt::is_complex_or_fundamental_v<std::complex<double>>,\n              \"Failed testing is_complex_of_fundamental\");\nstatic_assert(tt::is_complex_or_fundamental_v<std::complex<int>>,\n              \"Failed testing is_complex_of_fundamental\");\nstatic_assert(tt::is_complex_or_fundamental_v<double>,\n              \"Failed testing is_complex_of_fundamental\");\nstatic_assert(not tt::is_complex_or_fundamental_v<std::complex<DataVector>>,\n              \"Failed testing is_complex_of_fundamental\");\nstatic_assert(not tt::is_complex_or_fundamental_v<DataVector>,\n              \"Failed testing is_complex_of_fundamental\");\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsInteger.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include \"Utilities/TypeTraits/IsInteger.hpp\"\n\n// [is_integer_example]\nstatic_assert(tt::is_integer<short>::value,\n              \"Failed testing type trait is_integer\");\nstatic_assert(tt::is_integer_v<unsigned short>,\n              \"Failed testing type trait is_integer\");\nstatic_assert(tt::is_integer_v<int>, \"Failed testing type trait is_integer\");\nstatic_assert(tt::is_integer_v<unsigned int>,\n              \"Failed testing type trait is_integer\");\nstatic_assert(tt::is_integer_v<long>, \"Failed testing type trait is_integer\");\nstatic_assert(tt::is_integer_v<unsigned long>,\n              \"Failed testing type trait is_integer\");\nstatic_assert(tt::is_integer_v<long long>,\n              \"Failed testing type trait is_integer\");\nstatic_assert(tt::is_integer_v<unsigned long long>,\n              \"Failed testing type trait is_integer\");\nstatic_assert(not tt::is_integer_v<bool>,\n              \"Failed testing type trait is_integer\");\nstatic_assert(not tt::is_integer_v<char>,\n              \"Failed testing type trait is_integer\");\n// [is_integer_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsIterable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <vector>\n\n#include \"Utilities/TypeTraits/IsIterable.hpp\"\n\n// [is_iterable_example]\nstatic_assert(tt::is_iterable<std::vector<double>>::value,\n              \"Failed testing type trait is_iterable\");\nstatic_assert(tt::is_iterable_t<std::vector<double>>::value,\n              \"Failed testing type trait is_iterable\");\nstatic_assert(tt::is_iterable_v<std::vector<double>>,\n              \"Failed testing type trait is_iterable\");\nstatic_assert(not tt::is_iterable<double>::value,\n              \"Failed testing type trait is_iterable\");\n// [is_iterable_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsMaplike.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <map>\n#include <unordered_map>\n#include <vector>\n\n#include \"Utilities/TypeTraits/IsMaplike.hpp\"\n\nnamespace {\nclass C {};\nclass D {};\n}  // namespace\n\n// [is_maplike_example]\nstatic_assert(tt::is_maplike<std::unordered_map<int, double>>::value,\n              \"Failed testing type trait is_maplike\");\nstatic_assert(tt::is_maplike_t<std::unordered_map<int, double>>::value,\n              \"Failed testing type trait is_maplike\");\nstatic_assert(tt::is_maplike_v<std::unordered_map<int, double>>,\n              \"Failed testing type trait is_maplike\");\nstatic_assert(tt::is_maplike<std::map<int, C>>::value,\n              \"Failed testing type trait is_maplike\");\nstatic_assert(not tt::is_maplike<std::vector<C>>::value,\n              \"Failed testing type trait is_maplike\");\nstatic_assert(not tt::is_maplike<D>::value,\n              \"Failed testing type trait is_maplike\");\n// [is_maplike_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsStdArray.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n\n#include \"Utilities/TypeTraits/IsStdArrayOfSize.hpp\"\n\nnamespace {\nclass D;\n}  // namespace\n\n// [is_std_array_example]\nstatic_assert(tt::is_std_array<std::array<double, 3>>::value,\n              \"Failed testing type trait is_std_array\");\nstatic_assert(tt::is_std_array_t<std::array<double, 3>>::value,\n              \"Failed testing type trait is_std_array\");\nstatic_assert(tt::is_std_array_v<std::array<double, 3>>,\n              \"Failed testing type trait is_std_array\");\nstatic_assert(not tt::is_std_array<double>::value,\n              \"Failed testing type trait is_std_array\");\nstatic_assert(tt::is_std_array<std::array<D, 10>>::value,\n              \"Failed testing type trait is_std_array\");\n// [is_std_array_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsStdArrayOfSize.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <array>\n\n#include \"Utilities/TypeTraits/IsStdArrayOfSize.hpp\"\n\nnamespace {\nclass D;\n}  // namespace\n\n// [is_std_array_of_size_example]\nstatic_assert(tt::is_std_array_of_size<3, std::array<double, 3>>::value,\n              \"Failed testing type trait is_std_array_of_size\");\nstatic_assert(tt::is_std_array_of_size_t<3, std::array<double, 3>>::value,\n              \"Failed testing type trait is_std_array_of_size\");\nstatic_assert(tt::is_std_array_of_size_v<3, std::array<double, 3>>,\n              \"Failed testing type trait is_std_array_of_size\");\nstatic_assert(not tt::is_std_array_of_size<3, double>::value,\n              \"Failed testing type trait is_std_array_of_size\");\nstatic_assert(not tt::is_std_array_of_size<2, std::array<double, 3>>::value,\n              \"Failed testing type trait is_std_array_of_size\");\nstatic_assert(tt::is_std_array_of_size<10, std::array<D, 10>>::value,\n              \"Failed testing type trait is_std_array_of_size\");\n// [is_std_array_of_size_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_IsStreamable.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <iosfwd>\n#include <vector>\n\n#include \"Utilities/TypeTraits/IsStreamable.hpp\"\n\nnamespace {\nclass A {};\n\nclass C {};\n}  // namespace\n\nnamespace TestHelpers {\nclass D {};\n\nstd::ostream& operator<<(std::ostream& os, const D&);\n}  // namespace TestHelpers\n\n// [is_streamable_example]\nstatic_assert(not tt::is_streamable<std::ostream, C>::value,\n              \"Failed testing type trait is_streamable\");\nstatic_assert(not tt::is_streamable_t<std::ostream, C>::value,\n              \"Failed testing type trait is_streamable\");\nstatic_assert(not tt::is_streamable_v<std::ostream, C>,\n              \"Failed testing type trait is_streamable\");\nstatic_assert(not tt::is_streamable<std::ostream, A>::value,\n              \"Failed testing type trait is_streamable\");\nstatic_assert(tt::is_streamable<std::ostream, TestHelpers::D>::value,\n              \"Failed testing type trait is_streamable\");\nstatic_assert(tt::is_streamable_v<std::ostream, std::vector<TestHelpers::D>>,\n              \"Failed testing type trait is_streamable\");\n// [is_streamable_example]\n"
  },
  {
    "path": "tests/Unit/Utilities/TypeTraits/Test_RemoveReferenceWrapper.cpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#include <functional>\n\n#include \"Utilities/TypeTraits.hpp\"\n#include \"Utilities/TypeTraits/RemoveReferenceWrapper.hpp\"\n\nnamespace {\nclass A;\n}  // namespace\n\n// [remove_reference_wrapper_example]\nstatic_assert(\n    std::is_same_v<const double, tt::remove_reference_wrapper_t<\n                                     std::reference_wrapper<const double>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<const double, tt::remove_reference_wrapper_t<const double>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<\n        double, tt::remove_reference_wrapper_t<std::reference_wrapper<double>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<double, tt::remove_reference_wrapper_t<double>>,\n              \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<const A, tt::remove_reference_wrapper_t<\n                                          std::reference_wrapper<const A>>>,\n              \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<const A, tt::remove_reference_wrapper_t<const A>>,\n              \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<\n                  A, tt::remove_reference_wrapper_t<std::reference_wrapper<A>>>,\n              \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<A, tt::remove_reference_wrapper_t<A>>,\n              \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<double, tt::remove_reference_wrapper_t<\n                                         const std::reference_wrapper<double>>>,\n              \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<double, tt::remove_reference_wrapper_t<\n                               volatile std::reference_wrapper<double>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<double, tt::remove_reference_wrapper_t<\n                               const volatile std::reference_wrapper<double>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<const double,\n                             tt::remove_reference_wrapper_t<\n                                 const std::reference_wrapper<const double>>>,\n              \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<const double,\n                   tt::remove_reference_wrapper_t<\n                       volatile std::reference_wrapper<const double>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<const double,\n                   tt::remove_reference_wrapper_t<\n                       const volatile std::reference_wrapper<const double>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<\n        A, tt::remove_reference_wrapper_t<const std::reference_wrapper<A>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<\n        A, tt::remove_reference_wrapper_t<volatile std::reference_wrapper<A>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<A, tt::remove_reference_wrapper_t<\n                                    const volatile std::reference_wrapper<A>>>,\n              \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<const A, tt::remove_reference_wrapper_t<\n                                const std::reference_wrapper<const A>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(\n    std::is_same_v<const A, tt::remove_reference_wrapper_t<\n                                volatile std::reference_wrapper<const A>>>,\n    \"Failed testing remove_reference_wrapper\");\nstatic_assert(std::is_same_v<\n                  const A, tt::remove_reference_wrapper_t<\n                               const volatile std::reference_wrapper<const A>>>,\n              \"Failed testing remove_reference_wrapper\");\n// [remove_reference_wrapper_example]\n\n// [remove_cvref_wrap]\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<int>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<int&>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<const int&>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<int&&>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<const int&&>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<const int>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<volatile int>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<const volatile int>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<volatile int&>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<const volatile int&>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(std::is_same_v<tt::remove_cvref_wrap_t<volatile int&&>, int>,\n              \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<tt::remove_cvref_wrap_t<const volatile int&&>, int>,\n    \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<tt::remove_cvref_wrap_t<std::reference_wrapper<const int>>,\n                   int>,\n    \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<tt::remove_cvref_wrap_t<std::reference_wrapper<int>>, int>,\n    \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<tt::remove_cvref_wrap_t<std::reference_wrapper<int*>>, int*>,\n    \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<tt::remove_cvref_wrap_t<std::reference_wrapper<const int*>>,\n                   const int*>,\n    \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<tt::remove_cvref_wrap_t<std::reference_wrapper<int* const>>,\n                   int*>,\n    \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<tt::remove_cvref_wrap_t<const std::reference_wrapper<int>>,\n                   int>,\n    \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<\n        tt::remove_cvref_wrap_t<volatile std::reference_wrapper<int>>, int>,\n    \"Failed testing remove_cvref_wrap\");\nstatic_assert(\n    std::is_same_v<\n        tt::remove_cvref_wrap_t<const volatile std::reference_wrapper<int>>,\n        int>,\n    \"Failed testing remove_cvref_wrap\");\n// [remove_cvref_wrap]\n"
  },
  {
    "path": "tests/Unit/Visualization/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.ElementIdToParaview\"\n  Test_ElementIdToParaview.py\n  \"unit;visualization;python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.GenerateTetrahedralConnectivity\"\n  Test_GenerateTetrahedralConnectivity.py\n  \"unit;visualization;python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.GenerateXdmf\"\n  Test_GenerateXdmf.py\n  \"unit;visualization;python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotAlongLine\"\n  Test_PlotAlongLine.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotCce\"\n  Test_PlotCce.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotControlSystem\"\n  Test_PlotControlSystem.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotDatFile\"\n  Test_PlotDatFile.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotEccentricityControl\"\n  Test_PlotEccentricityControl.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotEllipticConvergence\"\n  Test_PlotEllipticConvergence.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotMemoryMonitors\"\n  Test_PlotMemoryMonitors.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotPowerMonitors\"\n  Test_PlotPowerMonitors.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotSizeControl\"\n  Test_PlotSizeControl.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotSlice\"\n  Test_PlotSlice.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.PlotTrajectories\"\n  Test_PlotTrajectories.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.ReadInputFile\"\n  Test_ReadInputFile.py\n  \"unit;visualization;python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.Render1D\"\n  Test_Render1D.py\n  \"unit;visualization;python\"\n  None\n  TIMEOUT 10)\n\nadd_subdirectory(Render3D)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Render3D/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfind_python_module(paraview)\n\n# Skip tests if ParaView is not found\nif (NOT PY_paraview_FOUND)\n  message(STATUS \"ParaView not found, skipping 'Render3D' tests\")\n  return()\nendif()\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.Render3D.Clip\"\n  Test_Clip.py\n  \"unit;visualization;python;paraview\"\n  None\n  TIMEOUT 30)\n\nspectre_add_python_bindings_test(\n  \"Unit.Visualization.Python.Render3D.Domain\"\n  Test_Domain.py\n  \"unit;visualization;python;paraview\"\n  None\n  TIMEOUT 30)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Render3D/Test_Clip.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.Visualization.Render3D.Clip import render_clip_command\n\n\nclass TestClip(unittest.TestCase):\n    def setUp(self):\n        self.vol_test_data = os.path.join(\n            unit_test_src_path(), \"Visualization/Python/VolTestData.xmf\"\n        )\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization/Render3D/Clip\"\n        )\n        self.output_file = os.path.join(self.test_dir, \"test_clip.png\")\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_render_clip(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            render_clip_command,\n            [\n                self.vol_test_data,\n                \"-y\",\n                \"Psi\",\n                \"--clip-origin\",\n                \"0\",\n                \"0\",\n                \"1\",\n                \"-o\",\n                self.output_file,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, msg=result.output)\n        self.assertTrue(os.path.exists(self.output_file), msg=result.output)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Render3D/Test_Domain.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.Visualization.Render3D.Domain import render_domain_command\n\n\nclass TestDomain(unittest.TestCase):\n    def setUp(self):\n        self.vol_test_data = os.path.join(\n            unit_test_src_path(), \"Visualization/Python/VolTestData.xmf\"\n        )\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization/Render3D/Domain\"\n        )\n        self.output_file = os.path.join(self.test_dir, \"test_domain.png\")\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_render_domain(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            render_domain_command,\n            [\n                self.vol_test_data,\n                self.vol_test_data,\n                \"--clip-origin\",\n                \"0\",\n                \"0\",\n                \"1\",\n                \"-o\",\n                self.output_file,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, msg=result.output)\n        self.assertTrue(os.path.exists(self.output_file), msg=result.output)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_ElementIdToParaview.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom click.testing import CliRunner\n\nfrom spectre.Domain import ElementId\nfrom spectre.Visualization.ElementIdToParaview import (\n    element_id_to_paraview_command,\n    element_id_to_short_id,\n)\n\n\nclass TestElementIdToParaview(unittest.TestCase):\n    def test_element_id_to_short_id(self):\n        eid_str = \"[B2,(L2I3,L1I0,L1I1)]\"\n        expected = ElementId[3](eid_str).to_short_id()\n        self.assertEqual(element_id_to_short_id(eid_str), expected)\n\n    def test_auto_detects_dimension(self):\n        self.assertEqual(\n            element_id_to_short_id(\"[B0,(L1I0)]\"),\n            ElementId[1](\"[B0,(L1I0)]\").to_short_id(),\n        )\n        self.assertEqual(\n            element_id_to_short_id(\"[B0,(L1I0,L2I3)]\"),\n            ElementId[2](\"[B0,(L1I0,L2I3)]\").to_short_id(),\n        )\n\n    def test_cli_single(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            element_id_to_paraview_command,\n            [\"[B2,(L2I3,L1I0,L1I1)]\"],\n        )\n        self.assertEqual(result.exit_code, 0)\n        expected = str(ElementId[3](\"[B2,(L2I3,L1I0,L1I1)]\").to_short_id())\n        self.assertEqual(result.output.strip(), expected)\n\n    def test_cli_multiple(self):\n        runner = CliRunner()\n        ids = [\n            \"[B0,(L2I3,L1I0,L1I1)]\",\n            \"[B2,(L2I3,L1I0,L1I1)]\",\n        ]\n        result = runner.invoke(element_id_to_paraview_command, ids)\n        self.assertEqual(result.exit_code, 0)\n        lines = result.output.strip().split(\"\\n\")\n        self.assertEqual(len(lines), 2)\n        self.assertEqual(\n            int(lines[0]),\n            ElementId[3](\"[B0,(L2I3,L1I0,L1I1)]\").to_short_id(),\n        )\n        self.assertEqual(\n            int(lines[1]),\n            ElementId[3](\"[B2,(L2I3,L1I0,L1I1)]\").to_short_id(),\n        )\n\n    def test_cli_mixed_dimensions(self):\n        runner = CliRunner()\n        ids = [\"[B0,(L1I0)]\", \"[B2,(L2I3,L1I0,L1I1)]\"]\n        result = runner.invoke(element_id_to_paraview_command, ids)\n        self.assertNotEqual(result.exit_code, 0)\n        self.assertIn(\"same dimension\", result.output)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_GenerateTetrahedralConnectivity.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport glob\nimport logging\nimport os\nimport shutil\nimport sys\nimport unittest\n\nimport h5py\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.Informer as spectre_informer\nfrom spectre.support.Logging import configure_logging\nfrom spectre.Visualization.GenerateTetrahedralConnectivity import (\n    generate_tetrahedral_connectivity,\n    generate_tetrahedral_connectivity_command,\n)\n\n\nclass TestGenerateTetrahedralConnectivity(unittest.TestCase):\n    def setUp(self):\n        self.data_dir = os.path.join(\n            spectre_informer.unit_test_src_path(), \"Visualization/Python\"\n        )\n        self.test_dir = os.path.join(\n            spectre_informer.unit_test_build_path(),\n            \"Visualization/TetrahedralConnectivity\",\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n        self.maxDiff = None\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_generate_tetrahedral_connectivity(self):\n        original_file = os.path.join(self.data_dir, \"VolTestData0.h5\")\n        data_file = os.path.join(self.test_dir, \"VolTestData0.h5\")\n        shutil.copy2(original_file, data_file)\n\n        self.assertTrue(\n            \"tetrahedral_connectivity\"\n            in h5py.File(data_file, \"r\")[\"element_data.vol\"][\n                \"ObservationId1090594013349131584\"\n            ]\n        )\n\n        generate_tetrahedral_connectivity(\n            h5file=data_file,\n            subfile_name=\"element_data\",\n            start_time=0.0,\n            stop_time=1.0,\n            stride=1,\n            coordinates=\"InertialCoordinates\",\n            force=True,\n        )\n\n        self.assertTrue(\n            \"tetrahedral_connectivity\"\n            in h5py.File(data_file, \"r\")[\"element_data.vol\"][\n                \"ObservationId1090594013349131584\"\n            ]\n        )\n\n        # Wild card because some HDF5 configurations add \"synchronously\"\n        with self.assertRaisesRegex(ValueError, \"Unable to.*create dataset\"):\n            generate_tetrahedral_connectivity(\n                h5file=data_file,\n                subfile_name=\"element_data\",\n                start_time=0.0,\n                stop_time=1.0,\n                stride=1,\n                coordinates=\"InertialCoordinates\",\n            )\n\n        self.assertTrue(\n            \"tetrahedral_connectivity\"\n            in h5py.File(data_file, \"r\")[\"element_data.vol\"][\n                \"ObservationId1090594013349131584\"\n            ]\n        )\n\n        original_data = np.asarray(\n            h5py.File(original_file, \"r\")[\"element_data.vol\"][\n                \"ObservationId1090594013349131584\"\n            ][\"tetrahedral_connectivity\"]\n        )\n        new_data = np.asarray(\n            h5py.File(data_file, \"r\")[\"element_data.vol\"][\n                \"ObservationId1090594013349131584\"\n            ][\"tetrahedral_connectivity\"]\n        )\n\n    def test_cli(self):\n        original_file = os.path.join(self.data_dir, \"VolTestData0.h5\")\n        data_file = os.path.join(self.test_dir, \"VolTestData0.h5\")\n        shutil.copy2(original_file, data_file)\n        data_files = glob.glob(os.path.join(self.test_dir, \"VolTestData0.h5\"))\n\n        self.assertTrue(\n            \"tetrahedral_connectivity\"\n            in h5py.File(data_file, \"r\")[\"element_data.vol\"][\n                \"ObservationId1090594013349131584\"\n            ]\n        )\n\n        runner = CliRunner()\n        result = runner.invoke(\n            generate_tetrahedral_connectivity_command,\n            [\n                *data_files,\n                \"-d\",\n                \"element_data\",\n                \"--force\",\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n\n        self.assertTrue(\n            \"tetrahedral_connectivity\"\n            in h5py.File(data_file, \"r\")[\"element_data.vol\"][\n                \"ObservationId1090594013349131584\"\n            ]\n        )\n\n        result = runner.invoke(\n            generate_tetrahedral_connectivity_command,\n            [\n                *data_files,\n                \"-d\",\n                \"element_data\",\n            ],\n            catch_exceptions=True,\n        )\n        self.assertEqual(result.exit_code, 1)\n\n        self.assertTrue(\n            \"tetrahedral_connectivity\"\n            in h5py.File(data_file, \"r\")[\"element_data.vol\"][\n                \"ObservationId1090594013349131584\"\n            ]\n        )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_GenerateXdmf.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport glob\nimport logging\nimport os\nimport shutil\nimport sys\nimport unittest\nimport xml.etree.ElementTree as ET\n\nimport h5py\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.Informer as spectre_informer\nfrom spectre.support.Logging import configure_logging\nfrom spectre.Visualization.GenerateXdmf import (\n    _count_cells_in_mixed_connectivity,\n    generate_xdmf,\n    generate_xdmf_command,\n)\n\n\nclass TestGenerateXdmf(unittest.TestCase):\n    def setUp(self):\n        self.data_dir = os.path.join(\n            spectre_informer.unit_test_src_path(), \"Visualization/Python\"\n        )\n        self.test_dir = os.path.join(\n            spectre_informer.unit_test_build_path(),\n            \"Visualization/GenerateXdmf\",\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n        self.maxDiff = None\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def write_test_volume_file(self, filename, observation_values):\n        with h5py.File(filename, \"w\") as open_h5file:\n            volfile = open_h5file.create_group(\"VolumeData.vol\")\n            volfile.attrs[\"dimension\"] = 3\n            for observation_id, observation_value in enumerate(\n                observation_values\n            ):\n                observation = volfile.create_group(\n                    f\"ObservationId{observation_id}\"\n                )\n                observation.attrs[\"observation_value\"] = observation_value\n                # New mixed-topology format: type tag 9 (Hexahedron) followed\n                # by 8 vertex indices for a single 2x2x2 hex cell.\n                observation.create_dataset(\n                    \"connectivity\",\n                    data=np.array([9, 0, 1, 3, 2, 4, 5, 7, 6], dtype=np.int32),\n                )\n                observation.create_dataset(\n                    \"total_extents\", data=np.array([2, 2, 2], dtype=np.int32)\n                )\n                observation.create_dataset(\n                    \"grid_names\", data=np.array([b\"[B0,(L0I0,L0I0,L0I0)]\"])\n                )\n                observation.create_dataset(\n                    \"bases\", data=np.array([0, 0, 0], dtype=np.int32)\n                )\n                observation.create_dataset(\n                    \"quadratures\", data=np.array([0, 0, 0], dtype=np.int32)\n                )\n                observation.create_dataset(\n                    \"InertialCoordinates_x\",\n                    data=np.array([0, 1, 0, 1, 0, 1, 0, 1], dtype=float),\n                )\n                observation.create_dataset(\n                    \"InertialCoordinates_y\",\n                    data=np.array([0, 0, 1, 1, 0, 0, 1, 1], dtype=float),\n                )\n                observation.create_dataset(\n                    \"InertialCoordinates_z\",\n                    data=np.array([0, 0, 0, 0, 1, 1, 1, 1], dtype=float),\n                )\n                # Cell-centered datasets for new mixed-topology format\n                observation.create_dataset(\n                    \"ElementId\", data=np.array([42], dtype=np.uint64)\n                )\n                observation.create_dataset(\n                    \"BlockId\", data=np.array([0], dtype=np.uint64)\n                )\n                observation.create_dataset(\n                    \"Psi\",\n                    data=np.array(\n                        [\n                            observation_value,\n                            observation_value + 1.0,\n                            observation_value + 2.0,\n                            observation_value + 3.0,\n                            observation_value + 4.0,\n                            observation_value + 5.0,\n                            observation_value + 6.0,\n                            observation_value + 7.0,\n                        ],\n                        dtype=float,\n                    ),\n                )\n\n    def test_generate_xdmf(self):\n        for use_tetrahedral_connectivity in [False, True]:\n            data_files = glob.glob(\n                os.path.join(self.data_dir, \"VolTestData*.h5\")\n            )\n            output_filename = os.path.join(\n                self.test_dir, \"Test_GenerateXdmf_output\"\n            )\n            generate_xdmf(\n                h5files=data_files,\n                output=output_filename,\n                subfile_name=\"element_data\",\n                start_time=0.0,\n                stop_time=1.0,\n                stride=1,\n                coordinates=\"InertialCoordinates\",\n                use_tetrahedral_connectivity=use_tetrahedral_connectivity,\n            )\n\n            # The script is quite opaque right now, so we only test that we can\n            # run it and it produces output without raising an error. To test\n            # more details, we should refactor the script into smaller units.\n            self.assertTrue(os.path.isfile(output_filename + \".xmf\"))\n\n            # Also make sure that the output doesn't change. This has caught\n            # many bugs.\n            # - Compare canonicalized XML with stripped whitespace. Pretty\n            #   indentation was only added in Python 3.9.\n            self.assertEqual(\n                ET.canonicalize(\n                    from_file=output_filename + \".xmf\", strip_text=True\n                ),\n                ET.canonicalize(\n                    from_file=os.path.join(\n                        self.data_dir,\n                        (\n                            \"VolTestDataTetrahedral.xmf\"\n                            if use_tetrahedral_connectivity\n                            else \"VolTestData.xmf\"\n                        ),\n                    ),\n                    strip_text=True,\n                ).replace(\n                    \"VolTestData0.h5\",\n                    os.path.relpath(data_files[0], self.test_dir),\n                ),\n            )\n            os.remove(\n                os.path.join(self.test_dir, \"Test_GenerateXdmf_output.xmf\")\n            )\n\n    def test_surface_generate_xdmf(self):\n        data_files = [os.path.join(self.data_dir, \"SurfaceTestData.h5\")]\n        output_filename = os.path.join(\n            self.test_dir, \"Test_SurfaceGenerateXdmf_output\"\n        )\n        generate_xdmf(\n            h5files=data_files,\n            output=output_filename,\n            subfile_name=\"AhA\",\n            start_time=0.0,\n            stop_time=0.03,\n            stride=1,\n            coordinates=\"InertialCoordinates\",\n        )\n\n        # The script is quite opaque right now, so we only test that we can run\n        # it and it produces output without raising an error. To test more\n        # details, we should refactor the script into smaller units.\n        self.assertTrue(os.path.isfile(output_filename + \".xmf\"))\n\n        # Also make sure that the output doesn't change. This has caught\n        # many bugs.\n        # - Compare canonicalized XML with stripped whitespace. Pretty\n        #   indentation was only added in Python 3.9.\n        self.assertEqual(\n            ET.canonicalize(\n                from_file=output_filename + \".xmf\", strip_text=True\n            ),\n            ET.canonicalize(\n                from_file=os.path.join(self.data_dir, \"SurfaceTestData.xmf\"),\n                strip_text=True,\n            ).replace(\n                \"SurfaceTestData.h5\",\n                os.path.relpath(data_files[0], self.test_dir),\n            ),\n        )\n\n    def write_test_surface_file(self, filename):\n        \"\"\"Create a minimal new-format 2D surface H5 file for testing.\n\n        One spectral element with extents [3, 4] (12 nodes). Mixed\n        connectivity: 3 quads (type 5) + 2 triangles (type 4) = 5 cells.\n        element_id and block_id mark this as the new mixed-topology format.\n        \"\"\"\n        with h5py.File(filename, \"w\") as f:\n            vol = f.create_group(\"SurfaceData.vol\")\n            vol.attrs[\"dimension\"] = 2\n            obs = vol.create_group(\"ObservationId0\")\n            obs.attrs[\"observation_value\"] = 1.0\n            # total_extents for 1 element in 2D: [3, 4] → num_points = 12\n            obs.create_dataset(\n                \"total_extents\", data=np.array([3, 4], dtype=np.int32)\n            )\n            obs.create_dataset(\n                \"grid_names\", data=np.array([b\"[B0,(L0I0,L0I0)]\"])\n            )\n            obs.create_dataset(\"bases\", data=np.array([0, 0], dtype=np.int32))\n            obs.create_dataset(\n                \"quadratures\", data=np.array([0, 0], dtype=np.int32)\n            )\n            # Mixed connectivity: 3 quads + 2 triangles = 5 cells\n            # tag 5=Quad (4 verts), tag 4=Triangle (3 verts)\n            obs.create_dataset(\n                \"connectivity\",\n                data=np.array(\n                    [\n                        5,\n                        0,\n                        1,\n                        4,\n                        3,\n                        5,\n                        1,\n                        2,\n                        5,\n                        4,\n                        5,\n                        3,\n                        4,\n                        7,\n                        6,\n                        4,\n                        4,\n                        5,\n                        8,\n                        4,\n                        6,\n                        7,\n                        9,\n                    ],\n                    dtype=np.int32,\n                ),\n            )\n            coords = np.linspace(0.0, 1.0, 12)\n            obs.create_dataset(\"InertialCoordinates_x\", data=coords)\n            obs.create_dataset(\"InertialCoordinates_y\", data=coords)\n            obs.create_dataset(\"InertialCoordinates_z\", data=coords)\n            obs.create_dataset(\n                \"ElementId\",\n                data=np.array([10, 11, 12, 13, 14], dtype=np.uint64),\n            )\n            obs.create_dataset(\n                \"BlockId\",\n                data=np.array([0, 0, 0, 0, 0], dtype=np.uint64),\n            )\n            obs.create_dataset(\"Phi\", data=coords)\n\n    def test_new_format_surface_generate_xdmf(self):\n        h5_file = os.path.join(self.test_dir, \"NewSurface.h5\")\n        self.write_test_surface_file(h5_file)\n        output_filename = os.path.join(self.test_dir, \"Test_NewSurface_output\")\n        generate_xdmf(\n            h5files=[h5_file],\n            output=output_filename,\n            subfile_name=\"SurfaceData\",\n            start_time=0.0,\n            stop_time=2.0,\n            stride=1,\n            coordinates=\"InertialCoordinates\",\n        )\n        self.assertTrue(os.path.isfile(output_filename + \".xmf\"))\n        xmf_root = ET.parse(output_filename + \".xmf\").getroot()\n\n        # Should be exactly one Uniform grid (no separate pole grid)\n        uniform_grids = xmf_root.findall(\".//Grid[@GridType='Uniform']\")\n        self.assertEqual(len(uniform_grids), 1)\n\n        grid = uniform_grids[0]\n        topo = grid.find(\"Topology\")\n        self.assertIsNotNone(topo)\n        self.assertEqual(topo.attrib.get(\"TopologyType\"), \"Mixed\")\n        self.assertEqual(topo.attrib.get(\"NumberOfElements\"), \"5\")\n\n        attr_names = {a.attrib[\"Name\"] for a in grid.findall(\"Attribute\")}\n        self.assertIn(\"ElementId\", attr_names)\n        self.assertIn(\"BlockId\", attr_names)\n        self.assertIn(\"Phi\", attr_names)\n\n        # ElementId and BlockId should be Cell centered\n        for attr in grid.findall(\"Attribute\"):\n            if attr.attrib[\"Name\"] in (\"ElementId\", \"BlockId\"):\n                self.assertEqual(attr.attrib.get(\"Center\"), \"Cell\")\n        # Phi should be Node centered\n        for attr in grid.findall(\"Attribute\"):\n            if attr.attrib[\"Name\"] == \"Phi\":\n                self.assertEqual(attr.attrib.get(\"Center\"), \"Node\")\n\n    def write_disk_h5_file(self, filename):\n        \"\"\"Create a minimal new-format 2D disk H5 file for testing.\n\n        One disk element with extents [2, 3] (6 nodes). Mixed connectivity:\n        2 standard quads + 1 wrapping quad + 1 center triangle = 4 cells.\n        \"\"\"\n        with h5py.File(filename, \"w\") as f:\n            vol = f.create_group(\"DiskData.vol\")\n            vol.attrs[\"dimension\"] = 2\n            obs = vol.create_group(\"ObservationId0\")\n            obs.attrs[\"observation_value\"] = 0.5\n            # total_extents: n_r=2, n_phi=3\n            obs.create_dataset(\n                \"total_extents\", data=np.array([2, 3], dtype=np.int32)\n            )\n            obs.create_dataset(\"grid_names\", data=np.array([b\"DiskMin\"]))\n            obs.create_dataset(\n                \"bases\",\n                data=np.array([8, 8], dtype=np.int32),  # ZernikeB2 = 8\n            )\n            obs.create_dataset(\n                \"quadratures\",\n                data=np.array(\n                    [3, 4], dtype=np.int32\n                ),  # GaussRadauUpper, Equiangular\n            )\n            # Mixed connectivity: 2 quads + 1 wrapping quad + 1 triangle\n            # type 5 = Quad (4 verts), type 4 = Triangle (3 verts)\n            obs.create_dataset(\n                \"connectivity\",\n                data=np.array(\n                    [\n                        5,\n                        0,\n                        1,\n                        3,\n                        2,  # Quad phi=0→1\n                        5,\n                        2,\n                        3,\n                        5,\n                        4,  # Quad phi=1→2\n                        5,\n                        0,\n                        1,\n                        5,\n                        4,  # wrapping Quad\n                        4,\n                        0,\n                        2,\n                        4,\n                    ],  # center Triangle\n                    dtype=np.int32,\n                ),\n            )\n            coords = np.linspace(0.0, 1.0, 6)\n            obs.create_dataset(\"InertialCoordinates_x\", data=coords)\n            obs.create_dataset(\"InertialCoordinates_y\", data=coords)\n            obs.create_dataset(\"InertialCoordinates_z\", data=coords)\n            obs.create_dataset(\n                \"ElementId\",\n                data=np.array([42, 42, 42, 42], dtype=np.uint64),\n            )\n            obs.create_dataset(\n                \"BlockId\",\n                data=np.array([0, 0, 0, 0], dtype=np.uint64),\n            )\n            obs.create_dataset(\"Phi\", data=coords)\n\n    def test_disk_generate_xdmf(self):\n        h5_file = os.path.join(self.test_dir, \"DiskData.h5\")\n        self.write_disk_h5_file(h5_file)\n        output_filename = os.path.join(self.test_dir, \"Test_Disk_output\")\n        generate_xdmf(\n            h5files=[h5_file],\n            output=output_filename,\n            subfile_name=\"DiskData\",\n            start_time=0.0,\n            stop_time=1.0,\n            stride=1,\n            coordinates=\"InertialCoordinates\",\n        )\n        self.assertTrue(os.path.isfile(output_filename + \".xmf\"))\n        xmf_root = ET.parse(output_filename + \".xmf\").getroot()\n\n        # Should be exactly one Uniform grid\n        uniform_grids = xmf_root.findall(\".//Grid[@GridType='Uniform']\")\n        self.assertEqual(len(uniform_grids), 1)\n\n        grid = uniform_grids[0]\n        topo = grid.find(\"Topology\")\n        self.assertIsNotNone(topo)\n        self.assertEqual(topo.attrib.get(\"TopologyType\"), \"Mixed\")\n        self.assertEqual(topo.attrib.get(\"NumberOfElements\"), \"4\")\n\n        attr_names = {a.attrib[\"Name\"] for a in grid.findall(\"Attribute\")}\n        self.assertIn(\"ElementId\", attr_names)\n        self.assertIn(\"BlockId\", attr_names)\n        self.assertIn(\"Phi\", attr_names)\n\n        # ElementId and BlockId should be Cell-centered\n        for attr in grid.findall(\"Attribute\"):\n            if attr.attrib[\"Name\"] in (\"ElementId\", \"BlockId\"):\n                self.assertEqual(attr.attrib.get(\"Center\"), \"Cell\")\n        # Phi should be Node-centered\n        for attr in grid.findall(\"Attribute\"):\n            if attr.attrib[\"Name\"] == \"Phi\":\n                self.assertEqual(attr.attrib.get(\"Center\"), \"Node\")\n\n    def test_count_cells_empty(self):\n        self.assertEqual(\n            _count_cells_in_mixed_connectivity(np.array([], dtype=np.int32)), 0\n        )\n\n    def test_count_cells_single_wedge(self):\n        # Single Wedge: tag 8 + 6 vertex indices = 7 ints → 1 cell\n        conn = np.array([8, 0, 1, 2, 3, 4, 5], dtype=np.int32)\n        self.assertEqual(_count_cells_in_mixed_connectivity(conn), 1)\n\n    def test_count_cells_mixed_hex_wedge_triangle(self):\n        # 2 Hex (tag 9, 8 verts each) + 3 Wedge (tag 8, 6 verts each)\n        # + 1 Triangle (tag 4, 3 verts) = 6 cells\n        conn = np.array(\n            [\n                9,\n                0,\n                1,\n                2,\n                3,\n                4,\n                5,\n                6,\n                7,  # Hex 1\n                9,\n                8,\n                9,\n                10,\n                11,\n                12,\n                13,\n                14,\n                15,  # Hex 2\n                8,\n                0,\n                1,\n                2,\n                4,\n                5,\n                6,  # Wedge 1\n                8,\n                1,\n                2,\n                3,\n                5,\n                6,\n                7,  # Wedge 2\n                8,\n                2,\n                3,\n                4,\n                6,\n                7,\n                8,  # Wedge 3\n                4,\n                0,\n                1,\n                2,  # Triangle\n            ],\n            dtype=np.int32,\n        )\n        self.assertEqual(_count_cells_in_mixed_connectivity(conn), 6)\n\n    def test_count_cells_invalid_type_tag(self):\n        conn = np.array([99, 0, 1, 2], dtype=np.int32)\n        with self.assertRaises(ValueError):\n            _count_cells_in_mixed_connectivity(conn)\n\n    def write_wedge_h5_file(self, filename):\n        \"\"\"Create an H5 file with mixed Hex+Wedge connectivity.\"\"\"\n        with h5py.File(filename, \"w\") as f:\n            vol = f.create_group(\"WedgeData.vol\")\n            vol.attrs[\"dimension\"] = 3\n            obs = vol.create_group(\"ObservationId0\")\n            obs.attrs[\"observation_value\"] = 0.0\n            # 6 nodes for a Wedge + 8 for a Hex = 14 nodes\n            # Mixed: 1 Hex (tag 9, 8 verts) + 1 Wedge (tag 8, 6 verts)\n            obs.create_dataset(\n                \"connectivity\",\n                data=np.array(\n                    [\n                        9,\n                        0,\n                        1,\n                        2,\n                        3,\n                        4,\n                        5,\n                        6,\n                        7,  # Hex (tag 9, 8 verts)\n                        8,\n                        8,\n                        9,\n                        10,\n                        11,\n                        12,\n                        13,  # Wedge (tag 8, 6 verts)\n                    ],\n                    dtype=np.int32,\n                ),\n            )\n            obs.create_dataset(\n                \"total_extents\", data=np.array([2, 2, 2], dtype=np.int32)\n            )\n            obs.create_dataset(\n                \"grid_names\", data=np.array([b\"[B0,(L0I0,L0I0,L0I0)]\"])\n            )\n            obs.create_dataset(\n                \"bases\", data=np.array([0, 0, 0], dtype=np.int32)\n            )\n            obs.create_dataset(\n                \"quadratures\", data=np.array([0, 0, 0], dtype=np.int32)\n            )\n            coords = np.linspace(0.0, 1.0, 14)\n            obs.create_dataset(\"InertialCoordinates_x\", data=coords)\n            obs.create_dataset(\"InertialCoordinates_y\", data=coords)\n            obs.create_dataset(\"InertialCoordinates_z\", data=coords)\n            # 2 cells total\n            obs.create_dataset(\n                \"ElementId\", data=np.array([1, 2], dtype=np.uint64)\n            )\n            obs.create_dataset(\n                \"BlockId\", data=np.array([0, 0], dtype=np.uint64)\n            )\n            obs.create_dataset(\"Psi\", data=coords)\n\n    def test_wedge_generate_xdmf(self):\n        h5_file = os.path.join(self.test_dir, \"WedgeData.h5\")\n        self.write_wedge_h5_file(h5_file)\n        output_filename = os.path.join(self.test_dir, \"Test_Wedge_output\")\n        generate_xdmf(\n            h5files=[h5_file],\n            output=output_filename,\n            subfile_name=\"WedgeData\",\n            start_time=0.0,\n            stop_time=1.0,\n            stride=1,\n            coordinates=\"InertialCoordinates\",\n        )\n        self.assertTrue(os.path.isfile(output_filename + \".xmf\"))\n        xmf_root = ET.parse(output_filename + \".xmf\").getroot()\n\n        uniform_grids = xmf_root.findall(\".//Grid[@GridType='Uniform']\")\n        self.assertEqual(len(uniform_grids), 1)\n\n        grid = uniform_grids[0]\n        topo = grid.find(\"Topology\")\n        self.assertIsNotNone(topo)\n        self.assertEqual(topo.attrib.get(\"TopologyType\"), \"Mixed\")\n        # 1 Hex + 1 Wedge = 2 cells\n        self.assertEqual(topo.attrib.get(\"NumberOfElements\"), \"2\")\n        # Connectivity array length: (1+8) + (1+6) = 16 ints\n        data_item = topo.find(\"DataItem\")\n        self.assertIsNotNone(data_item)\n        self.assertEqual(data_item.attrib.get(\"Dimensions\"), \"16\")\n\n        attr_names = {a.attrib[\"Name\"] for a in grid.findall(\"Attribute\")}\n        self.assertIn(\"ElementId\", attr_names)\n        self.assertIn(\"BlockId\", attr_names)\n        self.assertIn(\"Psi\", attr_names)\n\n        # ElementId and BlockId should be Cell-centered with 2 entries\n        for attr in grid.findall(\"Attribute\"):\n            if attr.attrib[\"Name\"] in (\"ElementId\", \"BlockId\"):\n                self.assertEqual(attr.attrib.get(\"Center\"), \"Cell\")\n                di = attr.find(\"DataItem\")\n                self.assertIsNotNone(di)\n                self.assertEqual(di.attrib.get(\"Dimensions\"), \"2\")\n        # Psi should be Node-centered\n        for attr in grid.findall(\"Attribute\"):\n            if attr.attrib[\"Name\"] == \"Psi\":\n                self.assertEqual(attr.attrib.get(\"Center\"), \"Node\")\n\n    def test_subfile_not_found(self):\n        data_files = glob.glob(os.path.join(self.data_dir, \"VolTestData*.h5\"))\n        output_filename = os.path.join(\n            self.test_dir, \"Test_GenerateXdmf_subfile_not_found\"\n        )\n        with self.assertRaisesRegex(ValueError, \"Could not open subfile\"):\n            generate_xdmf(\n                h5files=data_files,\n                output=output_filename,\n                subfile_name=\"unknown_subfile\",\n                start_time=0.0,\n                stop_time=1.0,\n                stride=1,\n                coordinates=\"InertialCoordinates\",\n            )\n\n    def test_cli(self):\n        data_files = glob.glob(os.path.join(self.data_dir, \"VolTestData*.h5\"))\n        output_filename = os.path.join(\n            self.test_dir, \"Test_GenerateXdmf_output\"\n        )\n        runner = CliRunner()\n        result = runner.invoke(\n            generate_xdmf_command,\n            [\n                *data_files,\n                \"-o\",\n                output_filename,\n                \"-d\",\n                \"element_data\",\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n\n        # List available subfiles\n        result = runner.invoke(\n            generate_xdmf_command,\n            [\n                *data_files,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n        self.assertIn(\"element_data\", result.output)\n\n    def test_stride_across_multiple_files(self):\n        data_files = [\n            os.path.join(self.test_dir, \"VolumeSegment0.h5\"),\n            os.path.join(self.test_dir, \"VolumeSegment1.h5\"),\n            os.path.join(self.test_dir, \"VolumeSegment2.h5\"),\n        ]\n        self.write_test_volume_file(data_files[0], [0.0, 1000.0, 2000.0])\n        self.write_test_volume_file(\n            data_files[1], [2500.0, 3000.0, 3500.0, 4000.0, 4500.0]\n        )\n        self.write_test_volume_file(data_files[2], [4800.0])\n\n        output_filename = os.path.join(\n            self.test_dir, \"Test_GenerateXdmf_stride_output\"\n        )\n        generate_xdmf(\n            h5files=data_files,\n            output=output_filename,\n            subfile_name=\"VolumeData\",\n            stride=2,\n        )\n\n        xmf_root = ET.parse(output_filename + \".xmf\").getroot()\n        found_times = [\n            float(time_element.attrib[\"Value\"])\n            for time_element in xmf_root.findall(\".//Time\")\n        ]\n        self.assertEqual(found_times, [0.0, 2000.0, 3000.0, 4000.0, 4800.0])\n\n        # All grids should use Mixed topology\n        uniform_grids = xmf_root.findall(\".//Grid[@GridType='Uniform']\")\n        self.assertEqual(len(uniform_grids), 5)\n        for grid in uniform_grids:\n            topo = grid.find(\"Topology\")\n            self.assertIsNotNone(topo)\n            self.assertEqual(topo.attrib.get(\"TopologyType\"), \"Mixed\")\n            self.assertEqual(topo.attrib.get(\"NumberOfElements\"), \"1\")\n            attr_names = {a.attrib[\"Name\"] for a in grid.findall(\"Attribute\")}\n            self.assertIn(\"ElementId\", attr_names)\n            self.assertIn(\"BlockId\", attr_names)\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotAlongLine.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.Visualization.PlotAlongLine import plot_along_line_command\n\n\nclass TestPlotAlongLine(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization\", \"PlotAlongLine\"\n        )\n        self.h5_filename = os.path.join(\n            unit_test_src_path(), \"Visualization/Python\", \"VolTestData0.h5\"\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_cli(self):\n        runner = CliRunner()\n        common_args = [\n            self.h5_filename,\n            \"-d\",\n            \"element_data\",\n            \"-y\",\n            \"Psi\",\n            \"-A\",\n            \"0,0,0\",\n            \"-B\",\n            \"1,1,1\",\n            \"--x-logscale\",\n            \"--y-logscale\",\n        ]\n        # Single plot\n        output_filename = os.path.join(self.test_dir, \"output.pdf\")\n        result = runner.invoke(\n            plot_along_line_command,\n            common_args\n            + [\n                \"--step\",\n                \"0\",\n                \"-o\",\n                output_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_filename))\n        # Animation\n        output_filename = os.path.join(self.test_dir, \"output.gif\")\n        result = runner.invoke(\n            plot_along_line_command,\n            common_args\n            + [\n                \"--animate\",\n                \"-o\",\n                output_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_filename))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotCce.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport h5py\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Visualization.PlotCce import plot_cce_command\n\n\nclass TestPlotCce(unittest.TestCase):\n    def setUp(self):\n        self.work_dir = os.path.join(\n            unit_test_build_path(), \"Visualization/PlotCce\"\n        )\n        self.reduction_file_name_one_subfile = \"TestCceReductions1.h5\"\n        self.reduction_file_name_two_subfiles = \"TestCceReductions2.h5\"\n        plot_quantities = [\n            \"EthInertialRetardedTime\",\n            \"Strain\",\n            \"News\",\n            \"Psi0\",\n            \"Psi1\",\n            \"Psi2\",\n            \"Psi3\",\n            \"Psi4\",\n        ]\n        self.l_max = 2\n        self.extraction_radius_1 = 150\n        self.extraction_radius_2 = 250\n\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n        os.makedirs(self.work_dir, exist_ok=True)\n\n        legend = [\n            \"time\",\n            \"Real Y_0,0\",\n            \"Imag Y_0,0\",\n            \"Real Y_1,-1\",\n            \"Imag Y_1,-1\",\n            \"Real Y_1,0\",\n            \"Imag Y_1,0\",\n            \"Real Y_1,1\",\n            \"Imag Y_1,1\",\n            \"Real Y_2,-2\",\n            \"Imag Y_2,-2\",\n            \"Real Y_2,-1\",\n            \"Imag Y_2,-1\",\n            \"Real Y_2,0\",\n            \"Imag Y_2,0\",\n            \"Real Y_2,1\",\n            \"Imag Y_2,1\",\n            \"Real Y_2,2\",\n            \"Imag Y_2,2\",\n        ]\n\n        self.cce_data = {}\n        for quantity in plot_quantities:\n            self.cce_data[quantity] = [np.random.rand(len(legend))]\n            self.cce_data[quantity].append(np.random.rand(len(legend)))\n            for i in range(2):\n                self.cce_data[quantity][i][0] = float(i)\n\n        with spectre_h5.H5File(\n            os.path.join(self.work_dir, self.reduction_file_name_one_subfile),\n            \"w\",\n        ) as open_h5_file:\n            cce_subfile = open_h5_file.insert_cce(\n                path=f\"SpectreR{self.extraction_radius_1:04}\",\n                l_max=self.l_max,\n                version=0,\n            )\n            # Doesn't matter that we are repeating data. Just testing that\n            # things get plotted\n            cce_subfile.append(\n                {\n                    quantity: self.cce_data[quantity][0]\n                    for quantity in plot_quantities\n                }\n            )\n            cce_subfile.append(\n                {\n                    quantity: self.cce_data[quantity][1]\n                    for quantity in plot_quantities\n                }\n            )\n            open_h5_file.close_current_object()\n            for quantity in plot_quantities:\n                dat_subfile = open_h5_file.insert_dat(\n                    path=f\"Cce/{quantity}\", legend=legend, version=0\n                )\n                dat_subfile.append(self.cce_data[quantity][0])\n                dat_subfile.append(self.cce_data[quantity][1])\n                open_h5_file.close_current_object()\n        with spectre_h5.H5File(\n            os.path.join(self.work_dir, self.reduction_file_name_two_subfiles),\n            \"w\",\n        ) as open_h5_file:\n            cce_subfile = open_h5_file.insert_cce(\n                path=f\"SpectreR{self.extraction_radius_1:04}\",\n                l_max=self.l_max,\n                version=0,\n            )\n            # Doesn't matter that we are repeating data. Just testing that\n            # things get plotted\n            cce_subfile.append(\n                {\n                    quantity: self.cce_data[quantity][0]\n                    for quantity in plot_quantities\n                }\n            )\n            cce_subfile.append(\n                {\n                    quantity: self.cce_data[quantity][1]\n                    for quantity in plot_quantities\n                }\n            )\n            open_h5_file.close_current_object()\n            cce_subfile = open_h5_file.insert_cce(\n                path=f\"SpectreR{self.extraction_radius_2:04}\",\n                l_max=self.l_max,\n                version=0,\n            )\n            # Doesn't matter that we are repeating data. Just testing that\n            # things get plotted\n            cce_subfile.append(\n                {\n                    quantity: self.cce_data[quantity][0]\n                    for quantity in plot_quantities\n                }\n            )\n            cce_subfile.append(\n                {\n                    quantity: self.cce_data[quantity][1]\n                    for quantity in plot_quantities\n                }\n            )\n\n        self.runner = CliRunner()\n\n    def tearDown(self):\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n\n    def test_plot_cce(self):\n        output_file_name = os.path.join(self.work_dir, \"PlotCceTest1.pdf\")\n        result = self.runner.invoke(\n            plot_cce_command,\n            [\n                os.path.join(\n                    self.work_dir, self.reduction_file_name_one_subfile\n                ),\n                \"-o\",\n                output_file_name,\n                \"-m\",\n                \"2,2\",\n                \"-m\",\n                \"2,-2\",\n                \"-m\",\n                \"2,0\",\n                \"-m\",\n                \"1,0\",\n                \"--imag\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Retarded Time (M)\",\n                \"--title\",\n                \"Cce at Scri Test1\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        print(result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n        output_file_name = os.path.join(self.work_dir, \"PlotCceTest2.pdf\")\n        result = self.runner.invoke(\n            plot_cce_command,\n            [\n                os.path.join(\n                    self.work_dir, self.reduction_file_name_two_subfiles\n                ),\n                \"-o\",\n                output_file_name,\n                \"-m\",\n                \"2,2\",\n                \"--real\",\n                \"--extraction-radius\",\n                \"250\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Retarded Time (M)\",\n                \"--title\",\n                \"Cce at Scri Test2\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n        output_file_name = os.path.join(self.work_dir, \"PlotCceTest3.pdf\")\n        result = self.runner.invoke(\n            plot_cce_command,\n            [\n                os.path.join(\n                    self.work_dir, self.reduction_file_name_one_subfile\n                ),\n                \"-o\",\n                output_file_name,\n                \"-m\",\n                \"2,2\",\n                \"--cce-group\",\n                \"Cce\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Retarded Time (M)\",\n                \"--title\",\n                \"Cce at Scri Test3\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n        result = self.runner.invoke(\n            plot_cce_command,\n            [\n                os.path.join(\n                    self.work_dir, self.reduction_file_name_two_subfiles\n                ),\n                \"-l\",\n                \"-m\",\n                \"2,2\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Retarded Time (M)\",\n                \"--title\",\n                \"Cce at Scri List\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertIn(\n            f\"SpectreR{self.extraction_radius_1:04}.cce\", result.output\n        )\n        self.assertIn(\n            f\"SpectreR{self.extraction_radius_2:04}.cce\", result.output\n        )\n\n    def test_plot_cce_errors(self):\n        error_filename = os.path.join(self.work_dir, \"PlotCceTestError.pdf\")\n        result = self.runner.invoke(\n            plot_cce_command,\n            [\n                os.path.join(\n                    self.work_dir, self.reduction_file_name_one_subfile\n                ),\n                \"-o\",\n                error_filename,\n                \"-m\",\n                \"2,2\",\n                \"--imag\",\n                \"--real\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Retarded Time (M)\",\n                \"--title\",\n                \"Cce at Scri Error1\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertNotEqual(result.exit_code, 0, result.output)\n        self.assertIn(\"Only specify one of '--real'/'--imag'.\", result.output)\n\n        result = self.runner.invoke(\n            plot_cce_command,\n            [\n                os.path.join(\n                    self.work_dir, self.reduction_file_name_one_subfile\n                ),\n                \"-o\",\n                error_filename,\n                \"-m\",\n                \"2,2\",\n                \"-r\",\n                \"1000\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Retarded Time (M)\",\n                \"--title\",\n                \"Cce at Scri Error2\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertNotEqual(result.exit_code, 0, result.output)\n        self.assertIn(\n            (\n                \"Either specify the correct extraction radius, or remove the\"\n                \" option altogether.\"\n            ),\n            result.output,\n        )\n\n        result = self.runner.invoke(\n            plot_cce_command,\n            [\n                os.path.join(\n                    self.work_dir, self.reduction_file_name_two_subfiles\n                ),\n                \"-o\",\n                error_filename,\n                \"-m\",\n                \"2,2\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Retarded Time (M)\",\n                \"--title\",\n                \"Cce at Scri Error2\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertNotEqual(result.exit_code, 0, result.output)\n        self.assertIn(\"you did not specify an extraction radius\", result.output)\n\n        bad_h5_file_name = \"NoCceSubfile.h5\"\n        shutil.copy(\n            os.path.join(self.work_dir, self.reduction_file_name_one_subfile),\n            os.path.join(self.work_dir, bad_h5_file_name),\n        )\n        with h5py.File(\n            os.path.join(self.work_dir, bad_h5_file_name), \"a\"\n        ) as h5_file:\n            del h5_file[f\"SpectreR{self.extraction_radius_1:04}.cce\"]\n\n        result = self.runner.invoke(\n            plot_cce_command,\n            [\n                os.path.join(self.work_dir, bad_h5_file_name),\n                \"-o\",\n                error_filename,\n                \"-m\",\n                \"2,2\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Retarded Time (M)\",\n                \"--title\",\n                \"Cce at Scri Error3\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertNotEqual(result.exit_code, 0, result.output)\n        self.assertIn(\n            \"Could not find any Cce subfiles in H5 file\", result.output\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotControlSystem.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Visualization.PlotControlSystem import plot_control_system_command\n\n\nclass TestPlotControlSystem(unittest.TestCase):\n    def setUp(self):\n        self.work_dir = os.path.join(\n            unit_test_build_path(), \"Visualization/PlotControlSystem\"\n        )\n        self.reductions_file_names = [\n            f\"TestControlSystemReductions{i}.h5\" for i in range(2)\n        ]\n        system_and_components = {\n            \"Expansion\": [\"Expansion\"],\n            \"Translation\": [\"x\", \"y\", \"z\"],\n            \"Rotation\": [\"x\", \"y\", \"z\"],\n            # These should be ignored\n            \"SizeA\": [\"Size\"],\n            \"SizeB\": [\"Size\"],\n        }\n        for obj in [\"A\", \"B\"]:\n            system_and_components[f\"Shape{obj}\"] = []\n            for l in range(4):\n                for m in range(-l, l + 1):\n                    system_and_components[f\"Shape{obj}\"].append(f\"l{l}m{m}\")\n\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n        os.makedirs(self.work_dir, exist_ok=True)\n\n        # Taken from src/ControlSystem/WriteData.hpp\n        legend = [\n            \"Time\",\n            \"FunctionOfTime\",\n            \"dtFunctionOfTime\",\n            \"d2tFunctionOfTime\",\n            \"ControlError\",\n            \"dtControlError\",\n            \"DampingTimescale\",\n        ]\n        for reduction_file_name in self.reductions_file_names:\n            with spectre_h5.H5File(\n                os.path.join(self.work_dir, reduction_file_name), \"w\"\n            ) as open_h5_file:\n                for system in system_and_components:\n                    for component in system_and_components[system]:\n                        subfile = open_h5_file.insert_dat(\n                            f\"ControlSystems/{system}/{component}\",\n                            legend=legend,\n                            version=0,\n                        )\n                        # The numbers here don't have to be meaningful. We're\n                        # just testing the plotting\n                        for i in range(4):\n                            data = np.random.rand(len(legend))\n                            data[0] = float(i)\n                            subfile.append(data)\n                        open_h5_file.close_current_object()\n\n        self.runner = CliRunner()\n\n    def tearDown(self):\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n\n    def test_plot_size(self):\n        # Just one h5 file\n        output_file_name = os.path.join(\n            self.work_dir, \"SingleFileWithShape.pdf\"\n        )\n        result = self.runner.invoke(\n            plot_control_system_command,\n            [\n                os.path.join(self.work_dir, self.reductions_file_names[0]),\n                \"-o\",\n                output_file_name,\n                \"--with-shape\",\n                \"--shape-l_max\",\n                \"3\",\n                \"--show-all-m\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Time (M)\",\n                \"--title\",\n                \"Control Systems First File\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n        # Just one h5 file again, but l2 norm of shape coefs\n        output_file_name = os.path.join(\n            self.work_dir, \"SingleFileWithShapeL2Norm.pdf\"\n        )\n        result = self.runner.invoke(\n            plot_control_system_command,\n            [\n                os.path.join(self.work_dir, self.reductions_file_names[0]),\n                \"-o\",\n                output_file_name,\n                \"--with-shape\",\n                \"--shape-l_max\",\n                \"3\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Time (M)\",\n                \"--title\",\n                \"Control Systems First File\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n        # Multiple h5 files\n        output_file_name = os.path.join(\n            self.work_dir, \"MultiFileWithoutShape.pdf\"\n        )\n        result = self.runner.invoke(\n            plot_control_system_command,\n            [\n                os.path.join(self.work_dir, self.reductions_file_names[0]),\n                os.path.join(self.work_dir, self.reductions_file_names[1]),\n                \"-o\",\n                output_file_name,\n                \"--without-shape\",\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Time (M)\",\n                \"--title\",\n                \"Control Systems Both Files\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotDatFile.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nimport click\nimport h5py\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.Informer as spectre_informer\nfrom spectre.Visualization.PlotDatFile import plot_dat_command\n\n\nclass TestPlotDatFile(unittest.TestCase):\n    def setUp(self):\n        self.data_dir = os.path.join(\n            spectre_informer.unit_test_src_path(), \"Visualization/Python\"\n        )\n        self.filename = os.path.join(self.data_dir, \"DatTestData.h5\")\n        self.stylesheet_filename = os.path.join(\n            self.data_dir, \"teststyle.mplstyle\"\n        )\n        self.test_dir = os.path.join(\n            spectre_informer.unit_test_build_path(),\n            \"Visualization/Python/PlotDatFile\",\n        )\n        self.runner = CliRunner()\n\n    def test_list_subfiles(self):\n        result = self.runner.invoke(\n            plot_dat_command, [self.filename], catch_exceptions=False\n        )\n        self.assertEqual(result.exit_code, 2)\n        self.assertIn(\"TimeSteps2.dat\", result.output)\n\n    def test_nonexistent_subfile(self):\n        result = self.runner.invoke(\n            plot_dat_command,\n            [self.filename, \"-d\", \"TimeSteps\"],\n            catch_exceptions=False,\n        )\n        self.assertNotEqual(result.exit_code, 0)\n        self.assertIn(\n            \"Unable to open dat subfile 'TimeSteps.dat'.\", result.output\n        )\n\n    def test_legend_only(self):\n        # Invoke with the subfile name but no functions to plot\n        result = self.runner.invoke(\n            plot_dat_command,\n            [self.filename, \"-d\", \"TimeSteps2\"],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n        self.assertIn(\"Time\", result.output)\n        self.assertIn(\"NumberOfPoints\", result.output)\n        self.assertIn(\"Slab size\", result.output)\n        # Invoke with the \"-l\" option and a couple of functions\n        result = self.runner.invoke(\n            plot_dat_command,\n            [self.filename, \"-d\", \"TimeSteps2\", \"-l\", \"-y\", \"NumberOfPoints\"],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n        self.assertIn(\"Time\", result.output)\n        self.assertIn(\"NumberOfPoints\", result.output)\n        self.assertIn(\"Slab size\", result.output)\n\n    def test_write_plot(self):\n        output_filename = os.path.join(self.test_dir, \"output.pdf\")\n        os.makedirs(self.test_dir, exist_ok=True)\n\n        # Run with minimal options\n        result = self.runner.invoke(\n            plot_dat_command,\n            [\n                self.filename,\n                \"-d\",\n                \"TimeSteps2\",\n                \"-y\",\n                \"Slab size\",\n                \"-o\",\n                output_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n        # We can't test that the plot is correct, so just make sure it at least\n        # gets written out.\n        self.assertTrue(os.path.exists(output_filename))\n\n        # Run with all options\n        result = self.runner.invoke(\n            plot_dat_command,\n            [\n                self.filename,\n                \"-d\",\n                \"Group0/MemoryData\",\n                \"-x\",\n                \"Time\",\n                \"-y\",\n                \"Usage in MB\",\n                \"-o\",\n                output_filename,\n                \"--x-label\",\n                \"The time\",\n                \"--y-label\",\n                \"The usage\",\n                \"--x-logscale\",\n                \"--y-logscale\",\n                \"--x-bounds\",\n                \"0.1\",\n                \"1\",\n                \"--y-bounds\",\n                \"0.1\",\n                \"0.2\",\n                \"-t\",\n                \"Memory Data\",\n                \"-s\",\n                self.stylesheet_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n\n        os.remove(output_filename)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotEccentricityControl.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport yaml\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Visualization.PlotEccentricityControl import (\n    plot_eccentricity_control_command,\n)\n\n\nclass TestPlotEccentricityControl(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization\", \"PlotEccentricityControl\"\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n        self.ecc_filenames = []\n        for i in range(3):\n            ecc_filename = os.path.join(\n                self.test_dir,\n                f\"EccentricityParams{i}.yaml\",\n            )\n            with open(ecc_filename, \"w\") as open_file:\n                yaml.safe_dump(\n                    {\n                        \"Eccentricity\": 10 ** (-i),\n                        \"Omega0\": 0.015 + np.random.rand() * 1e-4,\n                        \"Adot0\": 1e-4 + np.random.rand() * 1e-4,\n                        \"NewOmega0\": 0.015 + np.random.rand() * 1e-4,\n                        \"NewAdot0\": 1e-4 + np.random.rand() * 1e-4,\n                    },\n                    open_file,\n                )\n            self.ecc_filenames.append(ecc_filename)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_cli(self):\n        runner = CliRunner()\n        output_filename = os.path.join(self.test_dir, \"output.pdf\")\n        result = runner.invoke(\n            plot_eccentricity_control_command,\n            self.ecc_filenames + [\"-o\", output_filename],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_filename))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotEllipticConvergence.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport click\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.Informer as spectre_informer\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Visualization.PlotEllipticConvergence import (\n    plot_elliptic_convergence_command,\n)\n\n\nclass TestPlotEllipticConvergence(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            spectre_informer.unit_test_build_path(),\n            \"Visualization/PlotEllipticConvergence\",\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        self.h5_filename = os.path.join(self.test_dir, \"Residuals.h5\")\n        os.makedirs(self.test_dir, exist_ok=True)\n        with spectre_h5.H5File(self.h5_filename, \"w\") as h5_file:\n            linear_residuals = h5_file.insert_dat(\n                \"/GmresResiduals\",\n                legend=[\"Iteration\", \"Walltime\", \"Residual\"],\n                version=0,\n            )\n            linear_residuals.append([0, 0.1, 1.0])\n            linear_residuals.append([1, 0.2, 0.5])\n            linear_residuals.append([0, 0.3, 0.6])\n            linear_residuals.append([1, 0.5, 0.4])\n            linear_residuals.append([2, 0.8, 0.2])\n            h5_file.close_current_object()\n            nonlinear_residuals = h5_file.insert_dat(\n                \"/NewtonRaphsonResiduals\",\n                legend=[\"Iteration\", \"Walltime\", \"Residual\"],\n                version=0,\n            )\n            nonlinear_residuals.append([0, 0.1, 1.0])\n            nonlinear_residuals.append([1, 0.3, 0.5])\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_cli(self):\n        output_filename = os.path.join(self.test_dir, \"output.pdf\")\n        runner = CliRunner()\n        result = runner.invoke(\n            plot_elliptic_convergence_command,\n            [\n                self.h5_filename,\n                \"-o\",\n                output_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n        # We can't test that the plot is correct, so just make sure it at least\n        # gets written out.\n        self.assertTrue(os.path.exists(output_filename))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotMemoryMonitors.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Visualization.PlotMemoryMonitors import (\n    plot_memory_monitors_command,\n)\n\n\nclass TestPlotMemoryMonitors(unittest.TestCase):\n    def setUp(self):\n        self.work_dir = os.path.join(\n            unit_test_build_path(), \"Visualization/PlotMemory\"\n        )\n        self.reductions_file_names = [\n            f\"TestMemoryReductions{i}.h5\" for i in range(2)\n        ]\n        self.memory_monitor_dirs = \"/MemoryMonitors\"\n\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n        os.makedirs(self.work_dir, exist_ok=True)\n\n        singleton_legend = [\"Time\", \"Proc\", \"Size (MB)\"]\n        nodegroup_array_legend = [\n            \"Time\",\n            \"Size on node 0 (MB)\",\n            \"Size on node 1 (MB)\",\n            \"Average size per node (MB)\",\n        ]\n        group_legend = [\n            \"Time\",\n            \"Size on node 0 (MB)\",\n            \"Size on node 1 (MB)\",\n            \"Proc of max size\",\n            \"Size on proc of max size (MB)\",\n            \"Average size per node (MB)\",\n        ]\n        filenames = [\"Singleton.dat\", \"Nodegroup.dat\", \"Array.dat\", \"Group.dat\"]\n        legends = [\n            singleton_legend,\n            nodegroup_array_legend,\n            nodegroup_array_legend,\n            group_legend,\n        ]\n\n        for reduction_file_name in self.reductions_file_names:\n            with spectre_h5.H5File(\n                os.path.join(self.work_dir, reduction_file_name), \"w\"\n            ) as open_h5_file:\n                for filename, legend in zip(filenames, legends):\n                    subfile = open_h5_file.insert_dat(\n                        f\"{self.memory_monitor_dirs}/{filename}\",\n                        legend=legend,\n                        version=0,\n                    )\n                    # The numbers here don't have to be meaningful. We're just\n                    # testing the plotting\n                    for i in range(4):\n                        data = np.random.rand(len(legend))\n                        data[0] = float(i)\n                        subfile.append(data)\n                    open_h5_file.close_current_object()\n\n        self.runner = CliRunner()\n\n    def tearDown(self):\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n\n    def test_plot_size(self):\n        # Just one h5 file\n        output_file_name = os.path.join(self.work_dir, \"SingleFile.pdf\")\n        result = self.runner.invoke(\n            plot_memory_monitors_command,\n            [\n                os.path.join(self.work_dir, self.reductions_file_names[0]),\n                \"-o\",\n                output_file_name,\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--use-mb\",\n                \"--x-label\",\n                \"Time (M)\",\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n        # Multiple h5 files\n        output_file_name = os.path.join(self.work_dir, \"MultiFile.pdf\")\n        result = self.runner.invoke(\n            plot_memory_monitors_command,\n            [\n                os.path.join(self.work_dir, self.reductions_file_names[0]),\n                os.path.join(self.work_dir, self.reductions_file_names[1]),\n                \"-o\",\n                output_file_name,\n                \"--x-label\",\n                \"Time (M)\",\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotPowerMonitors.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nfrom click.testing import CliRunner\n\nfrom spectre.Domain.Creators import Cylinder\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.Visualization.PlotPowerMonitors import (\n    find_block_or_group,\n    plot_power_monitors_command,\n)\n\n\nclass TestPlotPowerMonitors(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization\", \"PlotPowerMonitors\"\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n        self.h5_filename = os.path.join(\n            unit_test_src_path(), \"Visualization/Python\", \"VolTestData0.h5\"\n        )\n        self.plot_filename = os.path.join(self.test_dir, \"plot.pdf\")\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_find_block_or_group(self):\n        domain = Cylinder(\n            inner_radius=1.0,\n            outer_radius=3.0,\n            lower_bound=0.0,\n            upper_bound=2.0,\n            is_periodic_in_z=False,\n            initial_refinement=1,\n            initial_number_of_grid_points=[3, 4, 5],\n            use_equiangular_map=True,\n        ).create_domain()\n        self.assertEqual(\n            find_block_or_group(0, [\"BlockyBlock\", \"InnerCube\"], domain), 1\n        )\n        self.assertEqual(\n            find_block_or_group(1, [\"BlockyBlock\", \"InnerCube\"], domain), None\n        )\n        self.assertEqual(\n            find_block_or_group(1, [\"InnerCube\", \"Wedges\"], domain), 1\n        )\n\n    def test_cli(self):\n        runner = CliRunner()\n        # Test plotting a single step\n        result = runner.invoke(\n            plot_power_monitors_command,\n            [\n                self.h5_filename,\n                \"-d\",\n                \"element_data\",\n                \"--step\",\n                \"-1\",\n                \"-b\",\n                \"Brick\",\n                \"-e\",\n                \"B*\",\n                \"-y\",\n                \"Psi\",\n                \"--figsize\",\n                \"12\",\n                \"4\",\n                \"-o\",\n                self.plot_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        # Can't easily test the plot itself, so just check that it was created\n        self.assertTrue(os.path.exists(self.plot_filename))\n        os.remove(self.plot_filename)\n\n        # Test plotting over time\n        result = runner.invoke(\n            plot_power_monitors_command,\n            [\n                self.h5_filename,\n                \"-d\",\n                \"element_data\",\n                \"-b\",\n                \"Brick\",\n                \"-e\",\n                \"B*\",\n                \"-y\",\n                \"Psi\",\n                \"--over-time\",\n                \"-o\",\n                self.plot_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        # Can't easily test the plot itself, so just check that it was created\n        self.assertTrue(os.path.exists(self.plot_filename))\n        os.remove(self.plot_filename)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotSizeControl.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Visualization.PlotSizeControl import plot_size_control_command\n\n\nclass TestPlotSizeControl(unittest.TestCase):\n    def setUp(self):\n        self.work_dir = os.path.join(\n            unit_test_build_path(), \"Visualization/PlotSize\"\n        )\n        self.reductions_file_names = [\n            f\"TestSizeReductions{i}.h5\" for i in range(2)\n        ]\n        self.diagnostic_subfile_name = \"/ControlSystems/SizeB/Diagnostics.dat\"\n\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n        os.makedirs(self.work_dir, exist_ok=True)\n\n        # Taken from src/ControlSystem/ControlErrors/Size.cpp\n        legend = [\n            \"Time\",\n            \"ControlError\",\n            \"StateNumber\",\n            \"DiscontinuousChangeHasOccurred\",\n            \"FunctionOfTime\",\n            \"DtFunctionOfTime\",\n            \"HorizonCoef00\",\n            \"AveragedDtHorizonCoef00\",\n            \"RawDtHorizonCoef00\",\n            \"SmootherTimescale\",\n            \"MinDeltaR\",\n            \"MinRelativeDeltaR\",\n            \"AvgDeltaR\",\n            \"AvgRelativeDeltaR\",\n            \"ControlErrorDeltaR\",\n            \"TargetCharSpeed\",\n            \"MinCharSpeed\",\n            \"MinComovingCharSpeed\",\n            \"CharSpeedCrossingTime\",\n            \"ComovingCharSpeedCrossingTime\",\n            \"DeltaRCrossingTime\",\n            \"SuggestedTimescale\",\n            \"DampingTime\",\n        ]\n        for reduction_file_name in self.reductions_file_names:\n            with spectre_h5.H5File(\n                os.path.join(self.work_dir, reduction_file_name), \"w\"\n            ) as open_h5_file:\n                diagnostic_subfile = open_h5_file.insert_dat(\n                    self.diagnostic_subfile_name,\n                    legend=legend,\n                    version=0,\n                )\n                # The numbers here don't have to be meaningful. We're just\n                # testing the plotting\n                for i in range(4):\n                    data = np.random.rand(len(legend))\n                    data[0] = float(i)\n                    diagnostic_subfile.append(data)\n\n        self.runner = CliRunner()\n\n    def tearDown(self):\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n\n    def test_plot_size(self):\n        # Just one h5 file\n        output_file_name = os.path.join(self.work_dir, \"SingleFileB.pdf\")\n        result = self.runner.invoke(\n            plot_size_control_command,\n            [\n                os.path.join(self.work_dir, self.reductions_file_names[0]),\n                \"-d\",\n                \"B\",\n                \"-o\",\n                output_file_name,\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Time (M)\",\n                \"--title\",\n                \"Size Control First File\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n        # Multiple h5 files\n        output_file_name = os.path.join(self.work_dir, \"MultiFileB.pdf\")\n        result = self.runner.invoke(\n            plot_size_control_command,\n            [\n                os.path.join(self.work_dir, self.reductions_file_names[0]),\n                os.path.join(self.work_dir, self.reductions_file_names[1]),\n                \"-d\",\n                \"B\",\n                \"-o\",\n                output_file_name,\n                \"--x-bounds\",\n                \"0.0\",\n                \"3.0\",\n                \"--x-label\",\n                \"Time (M)\",\n                \"--title\",\n                \"Size Control Both Files\",\n            ],\n            catch_exceptions=False,\n        )\n\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_file_name))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotSlice.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.Visualization.PlotSlice import plot_slice_command\n\n\nclass TestPlotSlice(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization\", \"PlotSlice\"\n        )\n        self.h5_filename = os.path.join(\n            unit_test_src_path(), \"Visualization/Python\", \"VolTestData0.h5\"\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_cli(self):\n        runner = CliRunner()\n        common_args = [\n            self.h5_filename,\n            \"-d\",\n            \"element_data\",\n            \"-y\",\n            \"Psi\",\n            \"-C\",\n            \"1,1,1\",\n            \"-X\",\n            \"2\",\n            \"2\",\n            \"-n\",\n            \"0,0,1\",\n            \"-u\",\n            \"0,1,0\",\n        ]\n        # Single plot\n        output_filename = os.path.join(self.test_dir, \"output.pdf\")\n        result = runner.invoke(\n            plot_slice_command,\n            common_args\n            + [\n                \"--step\",\n                \"0\",\n                \"-o\",\n                output_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_filename))\n        # Animation\n        output_filename = os.path.join(self.test_dir, \"output.gif\")\n        result = runner.invoke(\n            plot_slice_command,\n            common_args\n            + [\n                \"--animate\",\n                \"-o\",\n                output_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_filename))\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_PlotTrajectories.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n# unit test for plot trajectories\n\nimport logging\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.support.Logging import configure_logging\nfrom spectre.testing.PostNewtonian import BinaryTrajectories\nfrom spectre.Visualization.PlotTrajectories import plot_trajectories_command\n\n\nclass TestPlotTrajectories(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization\", \"PlotTrajectories\"\n        )\n        self.h5_filename = os.path.join(\n            self.test_dir, \"TestPlotTrajectoriesReductions.h5\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n        self.create_h5_file()\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def create_h5_file(self):\n        binary_trajectories = BinaryTrajectories(initial_separation=16)\n        times = np.linspace(0, 200, 100)\n        positions = np.array(binary_trajectories.positions(times))\n        with spectre_h5.H5File(self.h5_filename, \"w\") as h5_file:\n            for i, ab in enumerate(\"AB\"):\n                dataset = h5_file.insert_dat(\n                    f\"ApparentHorizons/ControlSystemAh{ab}_Centers.dat\",\n                    legend=[\n                        \"Time\",\n                        \"GridCenter_x\",\n                        \"GridCenter_y\",\n                        \"GridCenter_z\",\n                        \"InertialCenter_x\",\n                        \"InertialCenter_y\",\n                        \"InertialCenter_z\",\n                    ],\n                    version=0,\n                )\n                for t, coords in zip(times, positions[i].T):\n                    dataset.append([t, *coords, *coords])\n                h5_file.close_current_object()\n\n    def test_cli(self):\n        output_filename = os.path.join(self.test_dir, \"output.pdf\")\n        runner = CliRunner()\n        result = runner.invoke(\n            plot_trajectories_command,\n            [\n                self.h5_filename,\n                \"-o\",\n                output_filename,\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(output_filename))\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_ReadInputFile.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\nfrom pathlib import Path\n\nimport yaml\n\nfrom spectre.Informer import unit_test_src_path\nfrom spectre.Visualization.ReadInputFile import find_event, find_phase_change\n\n\nclass TestReadInputFile(unittest.TestCase):\n    def setUp(self):\n        self.input_files_dir = Path(unit_test_src_path(), \"..\", \"InputFiles\")\n        self.input_file = Path(\n            self.input_files_dir, \"ExportCoordinates/Input1D.yaml\"\n        )\n\n    def test_find_event(self):\n        with self.input_file.open() as open_input_file:\n            _, input_file = yaml.safe_load_all(open_input_file)\n        self.assertEqual(\n            find_event(\"Completion\", \"EventsAndTriggersAtSlabs\", input_file), {}\n        )\n        self.assertIsNone(\n            find_event(\n                \"NonexistentEvent\", \"EventsAndTriggersAtSlabs\", input_file\n            )\n        )\n\n    def test_find_phase_change(self):\n        with self.input_file.open() as open_input_file:\n            _, input_file = yaml.safe_load_all(open_input_file)\n        self.assertEqual(\n            find_phase_change(\n                \"VisitAndReturn(EvaluateAmrCriteria)\", input_file\n            ),\n            {},\n        )\n        self.assertIsNone(\n            find_phase_change(\"NonexistentPhaseChange\", input_file)\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/Test_Render1D.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Domain import ElementId, serialize_domain\nfrom spectre.Domain.Creators import Interval\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\nfrom spectre.Spectral import Basis, Mesh, Quadrature\nfrom spectre.Visualization.Render1D import render_1d_command\n\n\nclass TestRender1D(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Visualization/Render1D\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n\n        # Generate 1D volume data\n        domain = Interval(\n            lower_bounds=[0.0],\n            upper_bounds=[1.0],\n            initial_refinement_levels=[1],\n            initial_num_points=[4],\n            is_periodic=[False],\n        ).create_domain()\n        serialized_domain = serialize_domain(domain)\n        mesh = Mesh[1](4, Basis.Legendre, Quadrature.GaussLobatto)\n        self.h5file = os.path.join(self.test_dir, \"voldata.h5\")\n        with spectre_h5.H5File(self.h5file, \"w\") as open_h5file:\n            volfile = open_h5file.insert_vol(\"/VolumeData\", version=0)\n            volfile.write_volume_data(\n                observation_id=0,\n                observation_value=1.0,\n                elements=[\n                    ElementVolumeData(\n                        ElementId[1](\"[B0,(L1I0)]\"),\n                        [TensorComponent(\"U\", np.random.rand(4))],\n                        mesh,\n                    ),\n                    ElementVolumeData(\n                        ElementId[1](\"[B0,(L1I1)]\"),\n                        [TensorComponent(\"U\", np.random.rand(4))],\n                        mesh,\n                    ),\n                ],\n                serialized_domain=serialized_domain,\n            )\n            volfile.write_volume_data(\n                observation_id=1,\n                observation_value=2.0,\n                elements=[\n                    ElementVolumeData(\n                        ElementId[1](\"[B0,(L1I0)]\"),\n                        [TensorComponent(\"U\", np.random.rand(4))],\n                        mesh,\n                    ),\n                    ElementVolumeData(\n                        ElementId[1](\"[B0,(L1I1)]\"),\n                        [TensorComponent(\"U\", np.random.rand(4))],\n                        mesh,\n                    ),\n                ],\n                serialized_domain=serialized_domain,\n            )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_render_1d(self):\n        # Can't easily test the layout of the plot, so we just test that the\n        # script runs without error and produces output.\n        # We also don't have ffmpeg installed in the CI container, so we can't\n        # test an animation.\n        runner = CliRunner()\n        plot_file = os.path.join(self.test_dir, \"plot.pdf\")\n        result = runner.invoke(\n            render_1d_command,\n            [self.h5file, \"-d\", \"VolumeData\", \"--step\", \"0\", \"-o\", plot_file],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, msg=result.output)\n        self.assertTrue(os.path.exists(plot_file), msg=result.output)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/Unit/Visualization/Python/teststyle.mplstyle",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nlines.linewidth: 1\nfont.size: 9\nlegend.fontsize: 8\naxes.prop_cycle: cycler('linestyle', ['-', '--', '-.', 'dotted'])\n"
  },
  {
    "path": "tests/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tests/support/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Pipelines)\nadd_subdirectory(Python)\n"
  },
  {
    "path": "tests/support/Pipelines/Bbh/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (SpEC_FOUND)\n  spectre_add_python_bindings_test(\n    \"support.Pipelines.Bbh.EccentricityControl\"\n    Test_EccentricityControl.py\n    \"Python\"\n    None\n    TIMEOUT 20)\nendif()\n\nspectre_add_python_bindings_test(\n  \"support.Pipelines.Bbh.FindHorizon\"\n  Test_FindHorizon.py\n  \"Python\"\n  None\n  TIMEOUT 20)\n\nspectre_add_python_bindings_test(\n  \"support.Pipelines.Bbh.InitialData\"\n  Test_InitialData.py\n  \"Python\"\n  None\n  TIMEOUT 60)\n\nspectre_add_python_bindings_test(\n  \"support.Pipelines.Bbh.Inspiral\"\n  Test_Inspiral.py\n  \"Python\"\n  None\n  TIMEOUT 60)\n\nspectre_add_python_bindings_test(\n  \"support.Pipelines.Bbh.PostprocessId\"\n  Test_PostprocessId.py\n  \"Python\"\n  None\n  TIMEOUT 60)\n\nspectre_add_python_bindings_test(\n  \"support.Pipelines.Bbh.Ringdown\"\n  Test_Ringdown.py\n  \"Python\"\n  None\n  TIMEOUT 60)\n"
  },
  {
    "path": "tests/support/Pipelines/Bbh/Test_EccentricityControl.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport numpy as np\nimport yaml\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.Pipelines.Bbh.EccentricityControl import (\n    eccentricity_control,\n    eccentricity_control_command,\n)\nfrom spectre.support.Logging import configure_logging\nfrom spectre.testing.PostNewtonian import BinaryTrajectories\n\n\nclass TestEccentricityControl(unittest.TestCase):\n    # Set up and prepare test directory and file paths\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Pipelines/Bbh/EccentricityControl\"\n        )\n        self.h5_filename = os.path.join(\n            self.test_dir, \"TestEccentricityControlData.h5\"\n        )\n        self.id_input_file_path = os.path.join(\n            self.test_dir, \"InitialData.yaml\"\n        )\n        # Clean up any existing test directory and create new one\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n        # Create HDF5 and YAML files for the test\n        self.create_h5_file()\n        self.create_yaml_file()\n\n    # Clean up and remove test directory after tests are done\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def create_h5_file(self):\n        binary_trajectories = BinaryTrajectories(initial_separation=16)\n        self.angular_velocity = binary_trajectories.angular_velocity(0)\n        self.initial_separation = binary_trajectories.separation(0)\n        times = np.arange(0, 1500, 1.0)\n        positions = np.array(binary_trajectories.positions(times))\n        with spectre_h5.H5File(self.h5_filename, \"w\") as h5_file:\n            for i, ab in enumerate(\"AB\"):\n                dataset = h5_file.insert_dat(\n                    f\"ApparentHorizons/ControlSystemAh{ab}_Centers.dat\",\n                    legend=[\n                        \"Time\",\n                        \"GridCenter_x\",\n                        \"GridCenter_y\",\n                        \"GridCenter_z\",\n                        \"InertialCenter_x\",\n                        \"InertialCenter_y\",\n                        \"InertialCenter_z\",\n                    ],\n                    version=0,\n                )\n                for t, coords in zip(times, positions[i].T):\n                    dataset.append([t, *coords, *coords])\n                h5_file.close_current_object()\n\n    def create_yaml_file(self):\n        # Define YAML data and write it to the file\n        metadata = {\n            \"TargetParams\": {\n                \"MassRatio\": 1.0,\n                \"MassA\": 0.5,\n                \"MassB\": 0.5,\n                \"DimensionlessSpinA\": [0.0, 0.0, 0.0],\n                \"DimensionlessSpinB\": [0.0, 0.0, 0.0],\n                \"Eccentricity\": 0.0,\n            },\n            \"Next\": {\n                \"With\": {\n                    \"control\": True,\n                    \"control_refinement_level\": 1,\n                    \"control_polynomial_order\": 10,\n                }\n            },\n        }\n\n        data1 = {\n            \"Background\": {\n                \"Binary\": {\n                    \"AngularVelocity\": self.angular_velocity,\n                    \"Expansion\": -1e-6,\n                    \"XCoords\": [\n                        -self.initial_separation / 2.0,\n                        self.initial_separation / 2.0,\n                    ],\n                    \"ObjectLeft\": {\n                        \"KerrSchild\": {\"Mass\": 0.5, \"Spin\": [0.0, 0.0, 0.0]}\n                    },\n                    \"ObjectRight\": {\n                        \"KerrSchild\": {\"Mass\": 0.5, \"Spin\": [0.0, 0.0, 0.0]}\n                    },\n                    \"CenterOfMassOffset\": [0.0, 0.0],\n                },\n            },\n            \"DomainCreator\": {\n                \"BinaryCompactObject\": {\n                    \"ObjectA\": {\n                        \"Interior\": {\n                            \"ExciseWithBoundaryCondition\": {\n                                \"ApparentHorizon\": {\"Rotation\": [0.0, 0.0, 0.0]}\n                            }\n                        },\n                    },\n                    \"ObjectB\": {\n                        \"Interior\": {\n                            \"ExciseWithBoundaryCondition\": {\n                                \"ApparentHorizon\": {\"Rotation\": [0.0, 0.0, 0.0]}\n                            }\n                        },\n                    },\n                }\n            },\n        }\n\n        # Pass both metadata and data1 to the YAML file\n        with open(self.id_input_file_path, \"w\") as yaml_file:\n            yaml.dump_all([metadata, data1], yaml_file)\n\n    # Test the eccentricity control function with the created files\n    def test_eccentricity_control(self):\n        eccentricity_control(\n            h5_files=self.h5_filename,\n            id_input_file_path=self.id_input_file_path,\n            pipeline_dir=self.test_dir,\n            plot_output_dir=self.test_dir,\n            scheduler=None,\n            submit=False,\n        )\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            eccentricity_control_command,\n            [\n                self.h5_filename,\n                \"-i\",\n                self.id_input_file_path,\n                \"-d\",\n                self.test_dir,\n                \"--plot-output-dir\",\n                self.test_dir,\n                \"--no-schedule\",\n                \"--no-submit\",\n            ],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue((Path(self.test_dir) / \"FigureEccRemoval.pdf\").exists())\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Pipelines/Bbh/Test_FindHorizon.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport numpy.testing as npt\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Domain import ElementId, ElementMap, serialize_domain\nfrom spectre.Domain.Creators import Sphere\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.IO.H5.IterElements import Element\nfrom spectre.NumericalAlgorithms.LinearOperators import partial_derivative\nfrom spectre.Pipelines.Bbh.FindHorizon import (\n    find_horizon,\n    find_horizon_command,\n    use_excision_as_horizon,\n)\nfrom spectre.PointwiseFunctions.AnalyticSolutions.GeneralRelativity import (\n    KerrSchild,\n)\nfrom spectre.PointwiseFunctions.GeneralRelativity import ricci_tensor\nfrom spectre.Spectral import Basis, Mesh, Quadrature, logical_coordinates\nfrom spectre.SphericalHarmonics import Frame, Strahlkorper, cartesian_coords\nfrom spectre.support.Logging import configure_logging\n\n\ndef _to_tensor_components(tensors):\n    \"\"\"Convert a dictionary of tensors to a list of tensor components\"\"\"\n    result = []\n    for name, tensor in tensors.items():\n        result.extend(\n            spectre_h5.TensorComponent(\n                name + tensor.component_suffix(i), tensor[i]\n            )\n            for i in range(len(tensor))\n        )\n    return result\n\n\ndef _compute_derived(tensors, mesh, inv_jacobian):\n    \"\"\"Compute the spatial Ricci tensor from the spatial Christoffel symbols\"\"\"\n    christoffels = tensors[\"SpatialChristoffelSecondKind\"]\n    deriv_christoffels = partial_derivative(christoffels, mesh, inv_jacobian)\n    tensors[\"SpatialRicci\"] = ricci_tensor(christoffels, deriv_christoffels)\n    return tensors\n\n\nclass TestFindHorizon(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Pipelines/Bbh/FindHorizon\"\n        )\n        self.h5_filename = os.path.join(self.test_dir, \"TestData.h5\")\n        self.output_surfaces_filename = os.path.join(\n            self.test_dir, \"Surfaces.h5\"\n        )\n        self.output_reductions_filename = os.path.join(\n            self.test_dir, \"Reductions.h5\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n\n        # Create a spherical domain\n        domain = Sphere(\n            inner_radius=1.0,\n            outer_radius=3.0,\n            excise=True,\n            initial_refinement=0,\n            initial_number_of_grid_points=8,\n            use_equiangular_map=True,\n        ).create_domain()\n        element_ids = [ElementId[3](block_id) for block_id in range(6)]\n        elements = [\n            Element(\n                id=element_id,\n                mesh=Mesh[3](10, Basis.Legendre, Quadrature.GaussLobatto),\n                map=ElementMap(element_id, domain),\n            )\n            for element_id in element_ids\n        ]\n\n        # Evaluate a Kerr solution on the domain and write to file\n        solution = KerrSchild(mass=1.0, dimensionless_spin=[0.0, 0.0, 0.0])\n        tensor_names = [\n            \"SpatialMetric\",\n            \"InverseSpatialMetric\",\n            \"ExtrinsicCurvature\",\n            \"SpatialChristoffelSecondKind\",\n            # \"SpatialRicci\", < have to compute this from numeric derivative\n        ]\n        with spectre_h5.H5File(self.h5_filename, \"w\") as h5file:\n            volfile = h5file.insert_vol(\"element_data\", version=0)\n            volfile.write_volume_data(\n                observation_id=0,\n                observation_value=0.0,\n                elements=[\n                    spectre_h5.ElementVolumeData(\n                        element.id,\n                        _to_tensor_components(\n                            _compute_derived(\n                                solution.variables(\n                                    element.inertial_coordinates,\n                                    tensor_names,\n                                ),\n                                element.mesh,\n                                element.inv_jacobian,\n                            )\n                        ),\n                        element.mesh,\n                    )\n                    for element in elements\n                ],\n                serialized_domain=serialize_domain(domain),\n            )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_find_horizon(self):\n        horizon, quantities = find_horizon(\n            self.h5_filename,\n            subfile_name=\"element_data\",\n            obs_id=0,\n            obs_time=0.0,\n            initial_guess=Strahlkorper[Frame.Inertial](\n                l_max=12, radius=2.5, center=[0.0, 0.0, 0.0]\n            ),\n        )\n        # Horizon should be a sphere of coordinate radius 2.0\n        npt.assert_allclose(\n            np.linalg.norm(cartesian_coords(horizon), axis=0), 2.0, atol=1e-3\n        )\n        # Mass and spin should be 1.0 and 0.0\n        npt.assert_allclose(quantities[\"ChristodoulouMass\"], 1.0, atol=1e-3)\n        npt.assert_allclose(\n            quantities[\"DimensionlessSpinMagnitude\"], 0.0, atol=1e-3\n        )\n\n    def test_use_excision_as_horizon(self):\n        horizon, quantities = use_excision_as_horizon(\n            self.h5_filename,\n            subfile_name=\"element_data\",\n            obs_id=0,\n            obs_time=0.0,\n            l_max=12,\n            radius=1.0,\n            center=[0.0, 0.0, 0.0],\n        )\n        # Horizon should be a sphere of coordinate radius 1.0\n        npt.assert_allclose(\n            np.linalg.norm(cartesian_coords(horizon), axis=0), 1.0, atol=1e-3\n        )\n        # Mass should be 0.5 because r=2M and we're at r=1.0\n        npt.assert_allclose(quantities[\"ChristodoulouMass\"], 0.5, atol=1e-3)\n        # Spin should be 0.0\n        npt.assert_allclose(\n            quantities[\"DimensionlessSpinMagnitude\"], 0.0, atol=1e-3\n        )\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            find_horizon_command,\n            [\n                self.h5_filename,\n                \"-d\",\n                \"element_data\",\n                \"--step\",\n                \"0\",\n                \"--l-max\",\n                \"12\",\n                \"--initial-radius\",\n                \"2.5\",\n                \"--center\",\n                \"0.0\",\n                \"0.0\",\n                \"0.0\",\n                \"--output-surfaces-file\",\n                self.output_surfaces_filename,\n                \"--output-coeffs-subfile\",\n                \"HorizonCoeffs\",\n                \"--output-coords-subfile\",\n                \"HorizonCoords\",\n                \"--output-reductions-file\",\n                self.output_reductions_filename,\n                \"--output-quantities-subfile\",\n                \"HorizonQuantities\",\n            ],\n            catch_exceptions=True,\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n        self.assertTrue(os.path.exists(self.output_surfaces_filename))\n        self.assertTrue(os.path.exists(self.output_reductions_filename))\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Pipelines/Bbh/Test_InitialData.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport numpy.testing as npt\nimport yaml\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Pipelines.Bbh.InitialData import generate_id_command, id_parameters\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestInitialData(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(\n            unit_test_build_path(), \"support/Pipelines/Bbh/InitialData\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.bin_dir = Path(unit_test_build_path(), \"../../bin\").resolve()\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_generate_id(self):\n        params = id_parameters(\n            conformal_mass_a=0.6,\n            conformal_mass_b=0.4,\n            horizon_rotation_a=[-0.04, -0.08, -0.1],\n            horizon_rotation_b=[-0.3, -0.4, -0.4],\n            center_of_mass_offset=[0.1, 0.2, 0.3],\n            linear_velocity=[0.1, 0.2, 0.3],\n            separation=20.0,\n            orbital_angular_velocity=0.01,\n            radial_expansion_velocity=-1.0e-5,\n            refinement_level=1,\n            polynomial_order=5,\n            negative_expansion_bc=True,\n            target_params={\n                \"MassA\": 0.6,\n                \"MassB\": 0.4,\n                \"DimensionlessSpinA\": [0.1, 0.2, 0.3],\n                \"DimensionlessSpinB\": [0.4, 0.5, 0.6],\n            },\n        )\n        self.assertEqual(params[\"ConformalMassRight\"], 0.6)\n        self.assertEqual(params[\"ConformalMassLeft\"], 0.4)\n        self.assertEqual(params[\"XRight\"], 8.0 + 0.1)\n        self.assertEqual(params[\"XLeft\"], -12.0 + 0.1)\n        self.assertEqual(\n            [params[f\"CenterOfMassOffset_{yz}\"] for yz in \"yz\"],\n            [0.2, 0.3],\n        )\n        self.assertEqual(\n            [params[f\"LinearVelocity_{xyz}\"] for xyz in \"xyz\"],\n            [0.1, 0.2, 0.3],\n        )\n        self.assertAlmostEqual(params[\"ExcisionRadiusRight\"], 1.07546791205)\n        self.assertAlmostEqual(params[\"ExcisionRadiusLeft\"], 0.5504049327)\n        self.assertEqual(params[\"OrbitalAngularVelocity\"], 0.01)\n        self.assertEqual(params[\"RadialExpansionVelocity\"], -1.0e-5)\n        self.assertEqual(\n            [params[f\"ConformalSpinRight_{xyz}\"] for xyz in \"xyz\"],\n            [0.1, 0.2, 0.3],\n        )\n        self.assertEqual(\n            [params[f\"ConformalSpinLeft_{xyz}\"] for xyz in \"xyz\"],\n            [0.4, 0.5, 0.6],\n        )\n        npt.assert_allclose(\n            [params[f\"HorizonRotationRight_{xyz}\"] for xyz in \"xyz\"],\n            [-0.04, -0.08, -0.1 + 0.01],\n        )\n        npt.assert_allclose(\n            [params[f\"HorizonRotationLeft_{xyz}\"] for xyz in \"xyz\"],\n            [-0.3, -0.4, -0.4 + 0.01],\n        )\n        self.assertAlmostEqual(params[\"FalloffWidthRight\"], 6.479672589667676)\n        self.assertAlmostEqual(params[\"FalloffWidthLeft\"], 5.520327410332324)\n        self.assertEqual(params[\"L\"], 1)\n        self.assertEqual(params[\"P\"], 5)\n        # Newtonian center of mass (without offset) is zero\n        self.assertAlmostEqual(\n            params[\"ConformalMassRight\"] * (params[\"XRight\"] - 0.1)\n            + params[\"ConformalMassLeft\"] * (params[\"XLeft\"] - 0.1),\n            0.0,\n        )\n\n    def test_cli(self):\n        common_args = [\n            \"--mass-ratio\",\n            \"1.5\",\n            \"--chi-A\",\n            \"0.1\",\n            \"0.2\",\n            \"0.3\",\n            \"--chi-B\",\n            \"0.4\",\n            \"0.5\",\n            \"0.6\",\n            \"--separation\",\n            \"20\",\n            \"--orbital-angular-velocity\",\n            \"0.01\",\n            \"--radial-expansion-velocity\",\n            \"-1.0e-5\",\n            \"--refinement-level\",\n            \"1\",\n            \"--polynomial-order\",\n            \"5\",\n            \"-E\",\n            str(self.bin_dir / \"SolveXcts\"),\n            \"--no-schedule\",\n        ]\n        # Not using `CliRunner.invoke()` because it runs in an isolated\n        # environment and doesn't work with MPI in the container.\n        try:\n            generate_id_command(\n                common_args\n                + [\n                    \"-o\",\n                    str(self.test_dir),\n                    \"--no-submit\",\n                ]\n            )\n        except SystemExit as e:\n            self.assertEqual(e.code, 0)\n        self.assertTrue(\n            (self.test_dir / \"ControlParams_000/InitialData.yaml\").exists()\n        )\n        # Test with pipeline directory\n        try:\n            generate_id_command(\n                common_args\n                + [\n                    \"-d\",\n                    str(self.test_dir / \"Pipeline\"),\n                    \"--evolve\",\n                    \"--eccentricity-control\",\n                    \"--no-submit\",\n                ]\n            )\n        except SystemExit as e:\n            self.assertEqual(e.code, 0)\n        with open(\n            self.test_dir\n            / \"Pipeline/000_InitialData/ControlParams_000/InitialData.yaml\",\n            \"r\",\n        ) as open_input_file:\n            metadata = next(yaml.safe_load_all(open_input_file))\n        self.assertEqual(\n            metadata[\"TargetParams\"],\n            {\n                \"MassRatio\": 1.5,\n                \"MassA\": 0.6,\n                \"MassB\": 0.4,\n                \"DimensionlessSpinA\": [0.1, 0.2, 0.3],\n                \"DimensionlessSpinB\": [0.4, 0.5, 0.6],\n                \"CenterOfMass\": [0.0, 0.0, 0.0],\n                \"AdmLinearMomentum\": [0.0, 0.0, 0.0],\n                \"Eccentricity\": 0.0,\n                \"EccentricityAbsoluteTolerance\": 1e-3,\n                \"MeanAnomalyFraction\": None,\n                \"NumOrbits\": None,\n                \"TimeToMerger\": None,\n                \"EvolutionLev\": 1,\n            },\n        )\n        self.assertEqual(\n            metadata[\"Next\"],\n            {\n                \"Run\": \"spectre.Pipelines.Bbh.PostprocessId:postprocess_id\",\n                \"With\": {\n                    \"id_input_file_path\": \"__file__\",\n                    \"id_run_dir\": \"./\",\n                    \"pipeline_dir\": str(self.test_dir.resolve() / \"Pipeline\"),\n                    \"horizon_l_max\": 20,\n                    \"control\": True,\n                    \"control_refinement_level\": 1,\n                    \"control_polynomial_order\": 5,\n                    \"control_params\": [\n                        \"MassA\",\n                        \"MassB\",\n                        \"DimensionlessSpinA\",\n                        \"DimensionlessSpinB\",\n                        \"CenterOfMass\",\n                        \"AdmLinearMomentum\",\n                    ],\n                    \"evolve\": True,\n                    \"eccentricity_control\": True,\n                    \"negative_expansion_bc\": True,\n                    \"scheduler\": \"None\",\n                    \"copy_executable\": \"None\",\n                    \"submit_script_template\": \"None\",\n                    \"submit\": True,\n                },\n            },\n        )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Pipelines/Bbh/Test_Inspiral.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport yaml\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Pipelines.Bbh.InitialData import generate_id\nfrom spectre.Pipelines.Bbh.Inspiral import (\n    INSPIRAL_INPUT_FILE_TEMPLATE,\n    inspiral_parameters,\n    start_inspiral_command,\n)\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestInspiral(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(\n            unit_test_build_path(), \"support/Pipelines/Bbh/Inspiral\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.bin_dir = Path(unit_test_build_path(), \"../../bin\").resolve()\n        generate_id(\n            {\n                \"MassRatio\": 1.5,\n                \"MassA\": 0.6,\n                \"MassB\": 0.4,\n                \"DimensionlessSpinA\": [0.0, 0.0, 0.0],\n                \"DimensionlessSpinB\": [0.0, 0.0, 0.0],\n            },\n            separation=20.0,\n            orbital_angular_velocity=0.01,\n            radial_expansion_velocity=-1.0e-5,\n            refinement_level=1,\n            polynomial_order=5,\n            run_dir=self.test_dir / \"ID\",\n            scheduler=None,\n            submit=False,\n            executable=str(self.bin_dir / \"SolveXcts\"),\n        )\n        self.id_dir = self.test_dir / \"ID\"\n        # Purposefully not in the ID directory\n        self.horizons_filename = self.test_dir / \"Horizons.h5\"\n        with spectre_h5.H5File(\n            str(self.horizons_filename.resolve()), \"a\"\n        ) as horizons_file:\n            legend = [\"Time\", \"ChristodoulouMass\", \"DimensionlessSpinMagnitude\"]\n            for subfile_name in [\"AhA\", \"AhB\"]:\n                horizons_file.close_current_object()\n                dat_file = horizons_file.try_insert_dat(subfile_name, legend, 0)\n                dat_file.append([[0.0, 1.0, 0.3]])\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_inspiral_parameters(self):\n        with open(self.id_dir / \"InitialData.yaml\") as open_input_file:\n            id_metadata, id_input_file = yaml.safe_load_all(open_input_file)\n        params = inspiral_parameters(\n            id_input_file=id_input_file,\n            id_metadata=id_metadata,\n            id_run_dir=self.id_dir,\n            id_subfile_name=\"VolumeData\",\n            id_horizons_path=self.horizons_filename,\n        )\n        self.assertEqual(\n            params[\"IdFileGlob\"],\n            str((self.id_dir).resolve() / \"BbhVolume*.h5\"),\n        )\n        self.assertEqual(params[\"IdSubfile\"], \"VolumeData\")\n        self.assertAlmostEqual(params[\"ExcisionRadiusA\"], 1.116 * 1.0385 * 0.82)\n        self.assertAlmostEqual(params[\"ExcisionRadiusB\"], 0.744 * 1.0385 * 0.82)\n        self.assertEqual(params[\"XCoordA\"], 8.0)\n        self.assertEqual(params[\"XCoordB\"], -12.0)\n        self.assertEqual(params[\"InitialAngularVelocity\"], 0.01)\n        self.assertEqual(params[\"RadialExpansionVelocity\"], -1.0e-5)\n        self.assertEqual(\n            params[\"HorizonsFile\"], str(self.horizons_filename.resolve())\n        )\n        self.assertEqual(params[\"AhASubfileName\"], \"AhA/Coefficients\")\n        self.assertEqual(params[\"AhBSubfileName\"], \"AhB/Coefficients\")\n        self.assertEqual(params[\"ExcisionAShapeMass\"], 0.6 * 0.82)\n        self.assertEqual(params[\"ExcisionAShapeSpin_x\"], 0.0)\n        self.assertEqual(params[\"ExcisionAShapeSpin_y\"], 0.0)\n        self.assertEqual(params[\"ExcisionAShapeSpin_z\"], 0.0)\n        self.assertEqual(params[\"ExcisionBShapeMass\"], 0.4 * 0.82)\n        self.assertEqual(params[\"ExcisionBShapeSpin_x\"], 0.0)\n        self.assertEqual(params[\"ExcisionBShapeSpin_y\"], 0.0)\n        self.assertEqual(params[\"ExcisionBShapeSpin_z\"], 0.0)\n        # Control system\n        self.assertEqual(params[\"MaxDampingTimescale\"], 20.0)\n        self.assertEqual(params[\"KinematicTimescale\"], 0.2)\n        self.assertAlmostEqual(params[\"SizeATimescale\"], 0.024)\n        self.assertAlmostEqual(params[\"SizeBTimescale\"], 0.016)\n        self.assertAlmostEqual(params[\"ShapeATimescale\"], 1.0)\n        self.assertAlmostEqual(params[\"ShapeBTimescale\"], 1.0)\n        self.assertEqual(params[\"SizeIncreaseThreshold\"], 1e-3)\n        self.assertEqual(params[\"DecreaseThreshold\"], 6 / 130 * 2e-3)\n        self.assertEqual(params[\"IncreaseThreshold\"], 1.5 / 130 * 2e-3)\n        self.assertEqual(params[\"SizeAMaxTimescale\"], 20)\n        self.assertEqual(params[\"SizeBMaxTimescale\"], 20)\n        # Constraint damping\n        self.assertEqual(params[\"Gamma0Constant\"], 0.01)\n        self.assertEqual(params[\"Gamma0LeftAmplitude\"], 4.0 / 0.4)\n        self.assertEqual(params[\"Gamma0LeftWidth\"], 7.0 * 0.4)\n        self.assertEqual(params[\"Gamma0RightAmplitude\"], 4.0 / 0.6)\n        self.assertEqual(params[\"Gamma0RightWidth\"], 7.0 * 0.6)\n        self.assertEqual(params[\"Gamma0OriginAmplitude\"], 0.75)\n        self.assertEqual(params[\"Gamma0OriginWidth\"], 50.0)\n        self.assertEqual(params[\"Gamma1Width\"], 200.0)\n\n    def test_cli(self):\n        common_args = [\n            str(self.id_dir / \"InitialData.yaml\"),\n            \"--id-subfile-name\",\n            \"VolumeData\",\n            \"--id-horizons-path\",\n            str(self.horizons_filename),\n            \"-E\",\n            str(self.bin_dir / \"EvolveGhBinaryBlackHole\"),\n            \"--no-schedule\",\n            \"--num-nodes\",\n            \"1\",\n        ]\n        # Not using `CliRunner.invoke()` because it runs in an isolated\n        # environment and doesn't work with MPI in the container.\n        try:\n            start_inspiral_command(\n                common_args\n                + [\n                    \"--refinement-level\",\n                    \"1\",\n                    \"--polynomial-order\",\n                    \"5\",\n                    \"-O\",\n                    str(self.test_dir / \"Inspiral\"),\n                    \"--no-submit\",\n                ]\n            )\n        except SystemExit as e:\n            self.assertEqual(e.code, 0)\n        self.assertTrue(\n            (self.test_dir / \"Inspiral/Segment_0000/Inspiral.yaml\").exists()\n        )\n        # Test with pipeline directory and lev specified\n        try:\n            start_inspiral_command(\n                common_args\n                + [\n                    \"--lev\",\n                    \"-2\",\n                    \"-d\",\n                    str(self.test_dir / \"Pipeline\"),\n                    \"--continue-with-ringdown\",\n                    \"--no-submit\",\n                ]\n            )\n        except SystemExit as e:\n            self.assertEqual(e.code, 0)\n        with open(\n            self.test_dir / \"Pipeline/000_Inspiral/Segment_0000/Inspiral.yaml\",\n            \"r\",\n        ) as open_input_file:\n            metadata = next(yaml.safe_load_all(open_input_file))\n        self.assertEqual(\n            metadata[\"Next\"],\n            {\n                \"Run\": \"spectre.Pipelines.Bbh.Ringdown:start_ringdown\",\n                \"With\": {\n                    \"inspiral_input_file_path\": \"__file__\",\n                    \"inspiral_run_dir\": \"./\",\n                    \"pipeline_dir\": str(self.test_dir.resolve() / \"Pipeline\"),\n                    \"lev\": -2,\n                    \"scheduler\": \"None\",\n                    \"copy_executable\": \"None\",\n                    \"submit_script_template\": \"None\",\n                    \"submit\": True,\n                },\n            },\n        )\n        # Test with eccentricity control\n        try:\n            start_inspiral_command(\n                common_args\n                + [\n                    \"--lev\",\n                    \"-2\",\n                    \"-d\",\n                    str(self.test_dir / \"Pipeline\"),\n                    \"--eccentricity-control\",\n                    \"--no-submit\",\n                ]\n            )\n        except SystemExit as e:\n            self.assertEqual(e.code, 0)\n        with open(\n            self.test_dir / \"Pipeline/001_Inspiral/Segment_0000/Inspiral.yaml\",\n            \"r\",\n        ) as open_input_file:\n            metadata = next(yaml.safe_load_all(open_input_file))\n        self.maxDiff = None\n        modulename = \"spectre.Pipelines.Bbh.EccentricityControl\"\n        self.assertEqual(\n            metadata[\"Next\"],\n            {\n                \"Run\": modulename + \":eccentricity_control\",\n                \"With\": {\n                    \"h5_files\": \"../Segment_*/BbhReductions.h5\",\n                    \"id_input_file_path\": str(\n                        self.id_dir.resolve() / \"InitialData.yaml\"\n                    ),\n                    \"plot_output_dir\": \"./\",\n                    \"ecc_params_output_file\": \"../EccentricityParams.yaml\",\n                    \"pipeline_dir\": str(self.test_dir.resolve() / \"Pipeline\"),\n                    \"branch_levs_when_complete\": [-2],\n                    \"inspiral_input_file_path\": \"__file__\",\n                    \"inspiral_run_dir\": \"./\",\n                    \"inspiral_input_file_template\": str(\n                        INSPIRAL_INPUT_FILE_TEMPLATE\n                    ),\n                    \"scheduler\": \"None\",\n                    \"copy_executable\": \"None\",\n                    \"submit_script_template\": \"None\",\n                    \"submit\": True,\n                },\n            },\n        )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Pipelines/Bbh/Test_PostprocessId.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport numpy.testing as npt\nimport yaml\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Pipelines.Bbh.InitialData import generate_id\nfrom spectre.Pipelines.Bbh.PostprocessId import postprocess_id_command\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestPostprocessId(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(\n            unit_test_build_path(), \"support/Pipelines/Bbh/PostprocessId\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.bin_dir = Path(unit_test_build_path(), \"../../bin\").resolve()\n        generate_id(\n            {\n                \"MassRatio\": 1.5,\n                \"MassA\": 0.6,\n                \"MassB\": 0.4,\n                \"DimensionlessSpinA\": [0.0, 0.0, 0.0],\n                \"DimensionlessSpinB\": [0.0, 0.0, 0.0],\n            },\n            separation=20.0,\n            orbital_angular_velocity=0.01,\n            radial_expansion_velocity=-1.0e-5,\n            refinement_level=1,\n            polynomial_order=5,\n            run_dir=self.test_dir / \"ID\",\n            scheduler=None,\n            submit=False,\n            executable=str(self.bin_dir / \"SolveXcts\"),\n        )\n        self.id_dir = self.test_dir / \"ID\"\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_cli(self):\n        # Not using `CliRunner.invoke()` because it runs in an isolated\n        # environment and doesn't work with MPI in the container.\n        # Can only test that the command runs until this error until we have\n        # some BBH initial data to postprocess.\n        with self.assertRaisesRegex(ValueError, \"Number of observations\"):\n            postprocess_id_command(\n                [\n                    str(self.id_dir / \"InitialData.yaml\"),\n                ]\n            )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Pipelines/Bbh/Test_Ringdown.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport math\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport numpy as np\nimport yaml\nfrom click.testing import CliRunner\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre import Spectral\nfrom spectre.DataStructures import DataVector\nfrom spectre.Domain import (\n    PiecewisePolynomial2,\n    PiecewisePolynomial3,\n    QuaternionFunctionOfTime,\n    serialize_domain,\n    serialize_functions_of_time,\n)\nfrom spectre.Domain.Creators import BinaryCompactObject\nfrom spectre.Domain.Creators.TimeDependentOptions import (\n    BinaryCompactObjectTimeDependentOptions,\n    ExpansionMapOptions,\n    RotationMapOptions,\n    TranslationMapOptions,\n)\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.IO.H5 import ElementVolumeData, TensorComponent\nfrom spectre.Pipelines.Bbh.InitialData import generate_id\nfrom spectre.Pipelines.Bbh.Inspiral import start_inspiral\nfrom spectre.Pipelines.Bbh.Ringdown import (\n    ringdown_parameters,\n    start_ringdown_command,\n)\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestInitialData(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(\n            unit_test_build_path(), \"support/Pipelines/Bbh/Ringdown\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.bin_dir = Path(unit_test_build_path(), \"../../bin\").resolve()\n        generate_id(\n            {\n                \"MassRatio\": 1.5,\n                \"MassA\": 0.6,\n                \"MassB\": 0.4,\n                \"DimensionlessSpinA\": [0.0, 0.0, 0.0],\n                \"DimensionlessSpinB\": [0.0, 0.0, 0.0],\n            },\n            separation=20.0,\n            orbital_angular_velocity=0.01,\n            radial_expansion_velocity=-1.0e-5,\n            refinement_level=1,\n            polynomial_order=5,\n            run_dir=self.test_dir / \"ID\",\n            scheduler=None,\n            submit=False,\n            executable=str(self.bin_dir / \"SolveXcts\"),\n        )\n        self.id_dir = self.test_dir / \"ID\"\n        self.horizons_filename = self.id_dir / \"Horizons.h5\"\n        with spectre_h5.H5File(\n            str(self.horizons_filename.resolve()), \"a\"\n        ) as horizons_file:\n            legend = [\"Time\", \"ChristodoulouMass\", \"DimensionlessSpinMagnitude\"]\n            for subfile_name in [\"AhA\", \"AhB\"]:\n                horizons_file.close_current_object()\n                dat_file = horizons_file.try_insert_dat(subfile_name, legend, 0)\n                dat_file.append([[0.0, 1.0, 0.3]])\n        start_inspiral(\n            id_input_file_path=self.test_dir / \"ID\" / \"InitialData.yaml\",\n            id_subfile_name=\"VolumeData\",\n            refinement_level=1,\n            polynomial_order=5,\n            segments_dir=self.test_dir / \"Inspiral\",\n            scheduler=None,\n            submit=False,\n            executable=str(self.bin_dir / \"EvolveGhBinaryBlackHole\"),\n        )\n        self.inspiral_dir = self.test_dir / \"Inspiral\" / \"Segment_0000\"\n        # Making fake reduction data with simple derivative\n        self.inspiral_reduction_data = self.inspiral_dir / \"BbhReductions.h5\"\n        with spectre_h5.H5File(\n            str(self.inspiral_reduction_data.resolve()), \"a\"\n        ) as reduction_file:\n            legend = [\n                \"Time\",\n                \"InertialExpansionCenter_x\",\n                \"InertialExpansionCenter_y\",\n                \"InertialExpansionCenter_z\",\n                \"Lmax\",\n                \"coef(0,0)\",\n                \"coef(1,-1)\",\n                \"coef(1,0)\",\n                \"coef(1,1)\",\n                \"coef(2,-2)\",\n                \"coef(2,-1)\",\n                \"coef(2,0)\",\n                \"coef(2,1)\",\n                \"coef(2,2)\",\n            ]\n            shape_coefs = [5.0, 6.0, 7.0, 8.0, 9.0, 10.0]\n            times = [4990, 4992, 4994, 4996, 4998, 5000]\n            reduction_dat = reduction_file.try_insert_dat(\n                \"ObservationAhC_Ylm.dat\", legend, 0\n            )\n            for x in range(0, 5):\n                reduction_dat.append(\n                    [\n                        [\n                            times[x],\n                            0.0,\n                            0.0,\n                            0.0,\n                            2,\n                            shape_coefs[x],\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                            0.0,\n                        ]\n                    ]\n                )\n            reduction_file.close_current_object()\n\n        # Making volume data for functions of time to be extracted\n        rotation_fot = QuaternionFunctionOfTime(\n            times[0],\n            [DataVector(size=4, fill=1.0)],\n            4 * [DataVector(size=3, fill=0.0)],\n            math.inf,\n        )\n        expansion_fot = PiecewisePolynomial3(\n            times[0], 4 * [DataVector(size=1, fill=1.0)], math.inf\n        )\n        expansion_outer_fot = PiecewisePolynomial3(\n            times[0], 4 * [DataVector(size=1, fill=1.0)], math.inf\n        )\n        translation_fot = PiecewisePolynomial2(\n            times[0],\n            [\n                DataVector([1.0, -1.0, 0.5]),\n                DataVector([0.1, -0.4, -0.3]),\n                DataVector([0.0, 0.0, 0.0]),\n            ],\n            math.inf,\n        )\n        serialized_fots = serialize_functions_of_time(\n            {\n                \"Expansion\": expansion_fot,\n                \"ExpansionOuterBoundary\": expansion_outer_fot,\n                \"Rotation\": rotation_fot,\n                \"Translation\": translation_fot,\n            }\n        )\n\n        expansion_map = ExpansionMapOptions([1.0, 1e-4, 0.0], 100.0, 1e-6)\n        rotation_map = RotationMapOptions([[0.0, 0.0, 0.0, 1.0]], 100.0)\n        translation_map = TranslationMapOptions(\n            [[1.0, -1.0, 0.5], [0.1, -0.4, -0.3], [0.0, 0.0, 0.0]]\n        )\n        bco_time_dependent_options = BinaryCompactObjectTimeDependentOptions(\n            4990.0,\n            expansion_map,\n            rotation_map,\n            translation_map,\n            None,\n            None,\n            None,\n            None,\n        )\n\n        bco_domain = BinaryCompactObject(\n            inner_radius_a=0.5,\n            outer_radius_a=1.0,\n            x_coord_a=2.0,\n            excise_a=True,\n            use_logarithmic_map_a=True,\n            inner_radius_b=0.5,\n            outer_radius_b=1.0,\n            x_coord_b=-2.0,\n            excise_b=True,\n            use_logarithmic_map_b=True,\n            center_of_mass_offset=[0.1, 0.2],\n            envelope_radius=50.0,\n            outer_radius=600.0,\n            cube_scale=1.2,\n            initial_refinement=1,\n            initial_number_of_grid_points=5,\n            use_equiangular_map=True,\n            radial_partitioning_outer_shell=[],\n            opening_angle_in_degrees=120.0,\n            time_dependent_options=bco_time_dependent_options,\n        ).create_domain()\n        serialized_binary_domain = serialize_domain(bco_domain)\n\n        self.inspiral_volume_data = self.inspiral_dir / \"BbhVolume0.h5\"\n        obs_values = [4990.0, 4992.0, 4994.0, 4996.0, 4998.0, 5000.0]\n        with spectre_h5.H5File(self.inspiral_volume_data, \"w\") as volume_file:\n            volfile = volume_file.insert_vol(\"ForContinuation\", version=0)\n            for x in range(0, 5):\n                volfile.write_volume_data(\n                    observation_id=x,\n                    observation_value=obs_values[x],\n                    elements=[\n                        ElementVolumeData(\n                            element_name=\"foo\",\n                            components=[\n                                TensorComponent(\n                                    \"bar\",\n                                    np.random.rand(3),\n                                ),\n                            ],\n                            extents=[3],\n                            basis=[Spectral.Basis.Legendre],\n                            quadrature=[Spectral.Quadrature.GaussLobatto],\n                        )\n                    ],\n                    serialized_domain=serialized_binary_domain,\n                    serialized_observation_functions_of_time=serialized_fots,\n                )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_ringdown_parameters(self):\n        with open(self.inspiral_dir / \"Inspiral.yaml\") as open_input_file:\n            inspiral_metadata, inspiral_input_file = yaml.safe_load_all(\n                open_input_file\n            )\n        params = ringdown_parameters(\n            inspiral_input_file,\n            inspiral_metadata,\n            self.inspiral_dir,\n            fot_vol_subfile=\"ForContinuation\",\n        )\n        self.assertEqual(\n            params[\"IdFileGlob\"],\n            str((self.inspiral_dir).resolve() / \"BbhVolume*.h5\"),\n        )\n\n    def test_cli(self):\n        # Not using `CliRunner.invoke()` because it runs in an isolated\n        # environment and doesn't work with MPI in the container.\n        try:\n            start_ringdown_command(\n                [\n                    str(self.inspiral_dir),\n                    \"--refinement-level\",\n                    \"1\",\n                    \"--polynomial-order\",\n                    \"5\",\n                    \"-O\",\n                    str(self.test_dir / \"Ringdown\"),\n                    \"--match-time\",\n                    \"5000.0\",\n                    \"--number-of-ahc-finds-for-fit\",\n                    \"5\",\n                    \"--settling-timescale\",\n                    \"10.0\",\n                    \"--path-to-output-h5\",\n                    str(self.test_dir / \"RingdownCoefs.h5\"),\n                    \"-E\",\n                    str(self.bin_dir / \"EvolveGhSingleBlackHole\"),\n                    \"--no-submit\",\n                ]\n            )\n        except SystemExit as e:\n            self.assertEqual(e.code, 0)\n        self.assertTrue(\n            (self.test_dir / \"Ringdown/Segment_0000/Ringdown.yaml\").exists()\n        )\n        self.assertTrue(\n            (self.test_dir / \"RingdownCoefs.h5\").exists(),\n        )\n        try:\n            start_ringdown_command(\n                [\n                    str(self.inspiral_dir),\n                    \"--lev\",\n                    \"1\",\n                    \"-d\",\n                    str(self.test_dir / \"Pipeline\"),\n                    \"--number-of-ahc-finds-for-fit\",\n                    \"5\",\n                    \"-E\",\n                    str(self.bin_dir / \"EvolveGhSingleBlackHole\"),\n                    \"--no-submit\",\n                ]\n            )\n        except SystemExit as e:\n            self.assertEqual(e.code, 0)\n        self.assertTrue(\n            (\n                self.test_dir\n                / \"Pipeline/000_Ringdown/Segment_0000/Ringdown.yaml\"\n            ).exists()\n        )\n        self.assertTrue(\n            (\n                self.test_dir / \"Pipeline/000_Ringdown/RingdownShapeCoefs.h5\"\n            ).exists(),\n        )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Pipelines/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nadd_subdirectory(Bbh)\nadd_subdirectory(EccentricityControl)\n"
  },
  {
    "path": "tests/support/Pipelines/EccentricityControl/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif (SpEC_FOUND)\n  spectre_add_python_bindings_test(\n    \"support.Pipelines.EccentricityControl.EccentricityControlParams\"\n    Test_EccentricityControlParams.py\n    \"Python\"\n    None\n    TIMEOUT 60)\n  spectre_add_python_bindings_test(\n    \"support.Pipelines.EccentricityControl.InitialOrbitalParameters\"\n    Test_InitialOrbitalParameters.py\n    \"Python\"\n    None\n    TIMEOUT 60)\nendif()\n"
  },
  {
    "path": "tests/support/Pipelines/EccentricityControl/Test_EccentricityControlParams.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport numpy as np\nimport numpy.testing as npt\nimport yaml\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.Pipelines.EccentricityControl.EccentricityControlParams import (\n    eccentricity_control_params,\n)\nfrom spectre.support.Logging import configure_logging\nfrom spectre.testing.PostNewtonian import BinaryTrajectories\n\n\nclass TestEccentricityControlParams(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"Pipelines/EccentricityControl\"\n        )\n        self.h5_filename = os.path.join(\n            self.test_dir, \"TestEccentricityControlData.h5\"\n        )\n        self.out_filename = os.path.join(\n            self.test_dir, \"TestEccentricityParams.yaml\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        os.makedirs(self.test_dir, exist_ok=True)\n\n        # Write mock trajectory data to an H5 file\n        binary_trajectories = BinaryTrajectories(initial_separation=16)\n        self.angular_velocity = binary_trajectories.angular_velocity(0)\n        self.initial_separation = binary_trajectories.separation(0)\n        times = np.arange(0, 1500, 1.0)\n        positions = np.array(binary_trajectories.positions(times))\n        with spectre_h5.H5File(self.h5_filename, \"w\") as h5_file:\n            for i, ab in enumerate(\"AB\"):\n                dataset = h5_file.insert_dat(\n                    f\"ApparentHorizons/ControlSystemAh{ab}_Centers.dat\",\n                    legend=[\n                        \"Time\",\n                        \"GridCenter_x\",\n                        \"GridCenter_y\",\n                        \"GridCenter_z\",\n                        \"InertialCenter_x\",\n                        \"InertialCenter_y\",\n                        \"InertialCenter_z\",\n                    ],\n                    version=0,\n                )\n                for t, coords in zip(times, positions[i].T):\n                    dataset.append([t, *coords, *coords])\n                h5_file.close_current_object()\n\n        # Write a mock initial data input file\n        self.id_input_file_path = os.path.join(\n            self.test_dir, \"InitialData.yaml\"\n        )\n        with open(self.id_input_file_path, \"w\") as open_input_file:\n            yaml.safe_dump_all(\n                [\n                    {\n                        \"TargetParams\": {\n                            \"MassRatio\": 1.0,\n                            \"MassA\": 0.5,\n                            \"MassB\": 0.5,\n                            \"DimensionlessSpinA\": [0.0, 0.0, 0.0],\n                            \"DimensionlessSpinB\": [0.0, 0.0, 0.0],\n                            \"Eccentricity\": 0.0,\n                        }\n                    },\n                    {\n                        \"Background\": {\n                            \"Binary\": {\n                                \"AngularVelocity\": self.angular_velocity,\n                                \"Expansion\": -1e-6,\n                                \"XCoords\": [\n                                    -self.initial_separation / 2.0,\n                                    self.initial_separation / 2.0,\n                                ],\n                                \"ObjectLeft\": {\"KerrSchild\": {\"Mass\": 0.5}},\n                                \"ObjectRight\": {\"KerrSchild\": {\"Mass\": 0.5}},\n                            },\n                        }\n                    },\n                ],\n                open_input_file,\n            )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_ecc_control_params(self):\n        ecc_params = eccentricity_control_params(\n            h5_files=self.test_dir + \"/TestEccentricity*.h5\",\n            id_input_file_path=self.id_input_file_path,\n            tmin=0.0,\n            tmax=1200.0,\n            plot_output_dir=self.test_dir,\n            ecc_params_output_file=self.out_filename,\n        )\n        self.assertAlmostEqual(ecc_params[\"Eccentricity\"], 0.0, delta=1e-5)\n        with open(self.out_filename, \"r\") as open_output_file:\n            self.assertEqual(yaml.safe_load(open_output_file), ecc_params)\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Pipelines/EccentricityControl/Test_InitialOrbitalParameters.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport unittest\n\nimport numpy.testing as npt\n\nfrom spectre.Pipelines.EccentricityControl.InitialOrbitalParameters import (\n    initial_orbital_parameters,\n)\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestInitialOrbitalParameters(unittest.TestCase):\n    def test_initial_orbital_parameters(self):\n        target_params = {\n            \"MassRatio\": 1.0,\n            \"MassA\": 0.5,\n            \"MassB\": 0.5,\n            \"DimensionlessSpinA\": [0.0, 0.0, 0.0],\n            \"DimensionlessSpinB\": [0.0, 0.0, 0.0],\n            \"Eccentricity\": 0.0,\n        }\n        # Expected results are computed from SpEC's ZeroEccParamsFromPN.py\n        npt.assert_allclose(\n            initial_orbital_parameters(\n                target_params,\n                separation=20.0,\n                orbital_angular_velocity=0.01,\n                radial_expansion_velocity=-1.0e-5,\n            ),\n            [20.0, 0.01, -1.0e-5],\n        )\n        npt.assert_allclose(\n            initial_orbital_parameters(\n                target_params,\n                separation=16.0,\n            ),\n            [16.0, 0.014474280975952748, -4.117670632867514e-05],\n        )\n        npt.assert_allclose(\n            initial_orbital_parameters(\n                target_params,\n                orbital_angular_velocity=0.015,\n            ),\n            [15.6060791015625, 0.015, -4.541705362753467e-05],\n        )\n        npt.assert_allclose(\n            initial_orbital_parameters(\n                target_params,\n                orbital_angular_velocity=0.015,\n            ),\n            [15.6060791015625, 0.015, -4.541705362753467e-05],\n        )\n        npt.assert_allclose(\n            initial_orbital_parameters(\n                {**target_params, \"NumOrbits\": 20},\n            ),\n            [16.0421142578125, 0.014419921875000002, -4.0753460821644916e-05],\n        )\n        npt.assert_allclose(\n            initial_orbital_parameters(\n                {**target_params, \"TimeToMerger\": 6000},\n            ),\n            [16.1357421875, 0.01430025219917298, -3.9831982447244026e-05],\n        )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Python/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# The SPECTRE_VERSION is inserted in __init__.py by cmake, so these tests don't\n# work in PY_DEV_MODE\nif (BUILD_PYTHON_BINDINGS AND NOT PY_DEV_MODE)\n  add_test(NAME \"support.Python.Cli\"\n    COMMAND ${CMAKE_BINARY_DIR}/bin/spectre --version)\n  set_tests_properties(\n    \"support.Python.Cli\" PROPERTIES\n    PASS_REGULAR_EXPRESSION \"${SPECTRE_VERSION}\"\n    LABELS \"python\")\n\n  add_test(NAME \"support.Python.python-spectre\"\n    COMMAND ${CMAKE_BINARY_DIR}/bin/python-spectre -c\n      \"import spectre; print(spectre.__version__)\")\n  set_tests_properties(\n    \"support.Python.python-spectre\" PROPERTIES\n    PASS_REGULAR_EXPRESSION \"${SPECTRE_VERSION}\"\n    LABELS \"python\")\nendif()\n\nspectre_add_python_bindings_test(\n  \"support.DirectoryStructure\"\n  Test_DirectoryStructure.py\n  \"Python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"support.Machines\"\n  Test_Machines.py\n  \"Python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"support.Python.Main\"\n  Test_Main.py\n  \"Python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"support.Python.RunNext\"\n  Test_RunNext.py\n  \"Python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"support.Python.Schedule\"\n  Test_Schedule.py\n  \"Python\"\n  None)\n"
  },
  {
    "path": "tests/support/Python/Test_DirectoryStructure.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.support.DirectoryStructure import (\n    Checkpoint,\n    PipelineStep,\n    Segment,\n    list_checkpoints,\n    list_pipeline_steps,\n    list_segments,\n)\nfrom spectre.support.Logging import configure_logging\n\n\nclass TestDirectoryStructure(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(\n            unit_test_build_path(), \"support/DirectoryStructure\"\n        )\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_checkpoints(self):\n        checkpoint = Checkpoint.match(self.test_dir / \"Checkpoint_0002\")\n        self.assertEqual(\n            checkpoint, Checkpoint(path=self.test_dir / \"Checkpoint_0002\", id=2)\n        )\n\n        self.assertEqual(list_checkpoints(self.test_dir), [])\n        checkpoint.path.mkdir()\n        self.assertEqual(list_checkpoints(self.test_dir), [checkpoint])\n\n    def test_segments(self):\n        first_segment = Segment.first(self.test_dir)\n        self.assertEqual(\n            first_segment, Segment(path=self.test_dir / \"Segment_0000\", id=0)\n        )\n        self.assertEqual(Segment.match(first_segment.path), first_segment)\n\n        segment = Segment.match(self.test_dir / \"Segment_0003\")\n        self.assertEqual(\n            segment, Segment(path=self.test_dir / \"Segment_0003\", id=3)\n        )\n\n        next_segment = segment.next\n        self.assertEqual(next_segment.id, 4)\n        self.assertEqual(next_segment.path.name, \"Segment_0004\")\n        self.assertEqual(\n            next_segment.path.resolve().parent, segment.path.resolve().parent\n        )\n\n        self.assertEqual(list_segments(self.test_dir), [])\n        first_segment.path.mkdir()\n        segment.path.mkdir()\n        next_segment.path.mkdir()\n        self.assertEqual(\n            list_segments(self.test_dir), [first_segment, segment, next_segment]\n        )\n\n    def test_pipeline_steps(self):\n        first_step = PipelineStep.first(self.test_dir, \"InitialData\")\n        self.assertEqual(\n            first_step,\n            PipelineStep(\n                path=self.test_dir / \"000_InitialData\",\n                id=0,\n                label=\"InitialData\",\n            ),\n        )\n        self.assertEqual(PipelineStep.match(first_step.path), first_step)\n\n        next_step = first_step.next(\"Processing\")\n        self.assertEqual(next_step.id, 1)\n        self.assertEqual(next_step.path.name, \"001_Processing\")\n        self.assertEqual(\n            next_step.path.resolve().parent, first_step.path.resolve().parent\n        )\n\n        self.assertEqual(list_pipeline_steps(self.test_dir), [])\n        first_step.path.mkdir()\n        next_step.path.mkdir()\n        self.assertEqual(\n            list_pipeline_steps(self.test_dir), [first_step, next_step]\n        )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Python/Test_Machines.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport unittest\n\nimport yaml\n\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.support.Machines import Machine, UnknownMachineError, this_machine\n\n\nclass TestMachines(unittest.TestCase):\n    def setUp(self):\n        self.machinefile_path = os.path.join(\n            unit_test_build_path(), \"TestMachine.yaml\"\n        )\n        with open(self.machinefile_path, \"w\") as open_machinefile:\n            yaml.safe_dump(\n                dict(\n                    Machine=dict(\n                        Name=\"TestMachine\",\n                        Description=\"Just for testing\",\n                        DefaultTasksPerNode=2,\n                        DefaultProcsPerTask=15,\n                        DefaultQueue=\"production\",\n                        DefaultTimeLimit=\"1-00:00:00\",\n                        LaunchCommandSingleNode=[\"mpirun\", \"-n\", \"2\"],\n                        LaunchCommandLoginNode=[\"mpirun\", \"-n\", \"1\"],\n                    )\n                ),\n                open_machinefile,\n            )\n\n    def test_this_machine(self):\n        with self.assertRaises(UnknownMachineError):\n            this_machine(\"NonexistentMachinefile.yaml\")\n        self.assertIsNone(\n            this_machine(\"NonexistentMachinefile.yaml\", raise_exception=False)\n        )\n        machine = this_machine(self.machinefile_path)\n        self.assertIsInstance(machine, Machine)\n        self.assertEqual(machine.Name, \"TestMachine\")\n        self.assertEqual(machine.Description, \"Just for testing\")\n        self.assertEqual(machine.DefaultTasksPerNode, 2)\n        self.assertEqual(machine.DefaultProcsPerTask, 15)\n        self.assertEqual(machine.DefaultQueue, \"production\")\n        self.assertEqual(machine.DefaultTimeLimit, \"1-00:00:00\")\n        self.assertEqual(machine.LaunchCommandSingleNode, [\"mpirun\", \"-n\", \"2\"])\n        self.assertEqual(machine.LaunchCommandLoginNode, [\"mpirun\", \"-n\", \"1\"])\n        self.assertEqual(\n            machine.launch_command,\n            (\n                [\"mpirun\", \"-n\", \"2\"]\n                if machine.on_compute_node()\n                else [\"mpirun\", \"-n\", \"1\"]\n            ),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Python/Test_Main.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport yaml\nfrom click.testing import CliRunner\n\nfrom spectre.__main__ import cli\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.tools.CleanOutput import MissingExpectedOutputError\n\n\nclass TestMain(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            unit_test_build_path(), \"support/Python/Main\"\n        )\n        os.makedirs(self.test_dir, exist_ok=True)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_config_file(self):\n        # Collect some test data\n        input_file = os.path.join(\n            unit_test_src_path(),\n            \"../InputFiles/Poisson/ProductOfSinusoids1D.yaml\",\n        )\n        # Create a config file\n        config_file_path = os.path.join(self.test_dir, \"config.yaml\")\n        with open(config_file_path, \"w\") as open_config_file:\n            yaml.safe_dump({\"clean-output\": {\"force\": True}}, open_config_file)\n        # Run. The 'clean-output' command should fail because there's no data to\n        # clean up in the test dir, but with the 'force' option from the config\n        # file it should pass.\n        runner = CliRunner()\n        with self.assertRaises(MissingExpectedOutputError):\n            runner.invoke(\n                cli,\n                [\"clean-output\", \"-o\", self.test_dir, input_file],\n                catch_exceptions=False,\n            )\n        result = runner.invoke(\n            cli,\n            [\"clean-output\", \"-o\", self.test_dir, input_file],\n            catch_exceptions=False,\n            env={\"SPECTRE_CONFIG_FILE\": config_file_path},\n        )\n        self.assertEqual(result.exit_code, 0, result.output)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Python/Test_RunNext.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport yaml\nfrom click.testing import CliRunner\n\nimport spectre\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.support.Logging import configure_logging\nfrom spectre.support.RunNext import run_next, run_next_command\n\n\nclass TestRunNext(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(unit_test_build_path(), \"RunNext\").resolve()\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.input_file_path = self.test_dir / \"Input.yaml\"\n        self.next_entrypoint = {\n            \"Run\": \"spectre.__main__:cli\",\n            \"With\": {\n                \"args\": [\"--version\"],\n                \"standalone_mode\": False,\n            },\n        }\n        with open(self.input_file_path, \"w\") as open_input_file:\n            yaml.dump_all(\n                [\n                    {\"Next\": self.next_entrypoint},\n                    {},\n                ],\n                open_input_file,\n            )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_run_next(self):\n        with open(self.input_file_path, \"r\") as open_input_file:\n            metadata = next(yaml.safe_load_all(open_input_file))\n        result = run_next(\n            metadata[\"Next\"],\n            input_file_path=self.input_file_path,\n            cwd=self.test_dir,\n        )\n        self.assertEqual(result, 0)\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            run_next_command,\n            [str(self.input_file_path)],\n            catch_exceptions=False,\n        )\n        self.assertEqual(result.exit_code, 0)\n        self.assertIn(spectre.__version__, result.stdout)\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/support/Python/Test_Schedule.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport shutil\nimport stat\nimport unittest\nfrom pathlib import Path\n\nimport yaml\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.support.Logging import configure_logging\nfrom spectre.support.Resubmit import resubmit, resubmit_command\nfrom spectre.support.Schedule import (\n    Checkpoint,\n    Segment,\n    list_checkpoints,\n    list_segments,\n    schedule,\n    schedule_command,\n)\n\n\nclass TestSchedule(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(unit_test_build_path(), \"Schedule\").resolve()\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.spectre_cli = (\n            Path(unit_test_build_path()).parent.parent / \"bin/spectre\"\n        )\n\n        # Create an executable that just outputs all arguments passed to it\n        self.executable = self.test_dir / \"TestExec\"\n        self.executable.write_text(\"\"\"#!/bin/bash\necho $@ > out.txt\n\"\"\")\n        self.executable.chmod(self.executable.stat().st_mode | stat.S_IEXEC)\n\n        # Create an input file template\n        self.input_file_template = self.test_dir / \"InputFile.yaml\"\n        self.input_file_template.write_text(\"\"\"\nExecutable: TestExec\nMetadataOption: {{ metadata_option }}\n---\nOption: {{ extra_option }}\n\"\"\")\n\n        # Create a submit script template\n        (self.test_dir / \"SubmitTemplateBase.sh\").write_text(\"\"\"\\\nSPECTRE_EXECUTABLE={{ executable }}\nSPECTRE_CLI={{ spectre_cli }}\n{% block derived %}\n{% endblock %}\n\"\"\")\n        self.submit_script_template = self.test_dir / \"SubmitTemplate.sh\"\n        self.submit_script_template.write_text(\"\"\"\\\n{% extends \"SubmitTemplateBase.sh\" %}\n{% block derived %}\nNUM_NODES={{ num_nodes | default(1) }}\nNUM_TASKS_PER_NODE={{ num_slurm_tasks | default (5) }}\n{% endblock %}\n\"\"\")\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_run(self):\n        # Run executable once\n        proc = schedule(\n            input_file_template=self.input_file_template,\n            scheduler=None,\n            executable=self.executable,\n            run_dir=self.test_dir / \"Run\",\n            # Passing one extra param in a dict and another as a keyword\n            # argument to test that both work\n            extra_params=dict(extra_option=\"TestOpt\"),\n            metadata_option=\"MetaOpt\",\n        )\n        self.assertEqual(proc.returncode, 0)\n        with open(self.test_dir / \"Run/InputFile.yaml\", \"r\") as open_input_file:\n            rendered_metadata, rendered_input_file = yaml.safe_load_all(\n                open_input_file\n            )\n            self.assertEqual(rendered_input_file[\"Option\"], \"TestOpt\")\n            self.assertEqual(rendered_metadata[\"MetadataOption\"], \"MetaOpt\")\n        args = (self.test_dir / \"Run/out.txt\").read_text().split()\n        self.assertEqual(\n            args,\n            [\n                \"--input-file\",\n                str(self.test_dir / \"Run/InputFile.yaml\"),\n                \"+auto-provision\",\n            ],\n        )\n\n        # Run executable multiple times (like a convergence test)\n        schedule(\n            input_file_template=self.input_file_template,\n            scheduler=None,\n            executable=self.executable,\n            run_dir=self.test_dir / \"Lev{{ lev }}\",\n            lev=range(1, 3),\n            extra_option=\"TestOpt\",\n            metadata_option=\"MetaOpt\",\n        )\n        self.assertEqual(\n            sorted(self.test_dir.glob(\"Lev*\")),\n            [self.test_dir / \"Lev1\", self.test_dir / \"Lev2\"],\n        )\n        self.assertEqual(\n            sorted(self.test_dir.glob(\"Lev*/InputFile.yaml\")),\n            [\n                self.test_dir / \"Lev1/InputFile.yaml\",\n                self.test_dir / \"Lev2/InputFile.yaml\",\n            ],\n        )\n\n        # Create first of a series of segments\n        schedule(\n            input_file_template=self.input_file_template,\n            scheduler=None,\n            executable=self.executable,\n            segments_dir=self.test_dir,\n            extra_option=\"TestOpt\",\n            metadata_option=\"MetaOpt\",\n        )\n        self.assertEqual(\n            sorted(self.test_dir.glob(\"Segment*\")),\n            [self.test_dir / \"Segment_0000\"],\n        )\n        self.assertEqual(\n            sorted(self.test_dir.glob(\"Segment*/InputFile.yaml\")),\n            [self.test_dir / \"Segment_0000/InputFile.yaml\"],\n        )\n\n        # Create next segment\n        # - Can't continue without a previous checkpoint\n        with self.assertRaisesRegex(AssertionError, \"continue from the last\"):\n            schedule(\n                input_file_template=self.input_file_template,\n                scheduler=None,\n                executable=self.executable,\n                segments_dir=self.test_dir,\n                extra_option=\"TestOpt\",\n                metadata_option=\"MetaOpt\",\n            )\n        # - Can't continue from an earlier checkpoint than the last\n        earlier_checkpoint = Checkpoint.match(\n            self.test_dir / \"Segment_0000/Checkpoints/Checkpoint_0000\"\n        )\n        earlier_checkpoint.path.mkdir(parents=True)\n        last_checkpoint = Checkpoint.match(\n            self.test_dir / \"Segment_0000/Checkpoints/Checkpoint_0001\"\n        )\n        last_checkpoint.path.mkdir(parents=True)\n        with self.assertRaisesRegex(AssertionError, \"continue from the last\"):\n            schedule(\n                input_file_template=self.input_file_template,\n                scheduler=None,\n                executable=self.executable,\n                segments_dir=self.test_dir,\n                from_checkpoint=earlier_checkpoint,\n                extra_option=\"TestOpt\",\n                metadata_option=\"MetaOpt\",\n            )\n        # - Continue from last checkpoint\n        schedule(\n            input_file_template=self.input_file_template,\n            scheduler=None,\n            executable=self.executable,\n            segments_dir=self.test_dir,\n            from_checkpoint=last_checkpoint,\n            extra_option=\"TestOpt\",\n            metadata_option=\"MetaOpt\",\n        )\n\n    def test_schedule(self):\n        # Submit a batch job to create the first segment\n        schedule(\n            input_file_template=self.input_file_template,\n            scheduler=[\"echo\", \"Submitted batch job 000\"],\n            submit_script_template=self.submit_script_template,\n            executable=self.executable,\n            segments_dir=self.test_dir / \"Segments\",\n            extra_option=\"TestOpt\",\n            metadata_option=\"MetaOpt\",\n            submit=True,\n        )\n        self.assertEqual(\n            sorted(self.test_dir.glob(\"Segments/**/*\")),\n            [\n                self.test_dir / \"Segments/Segment_0000\",\n                self.test_dir / \"Segments/Segment_0000/InputFile.yaml\",\n                self.test_dir / \"Segments/Segment_0000/SchedulerContext.yaml\",\n                self.test_dir / \"Segments/Segment_0000/Submit.sh\",\n                self.test_dir / \"Segments/Segment_0000/jobid.txt\",\n                self.test_dir / \"Segments/Segment_0000/out.txt\",\n                self.test_dir / \"Segments/SubmitTemplate.sh\",\n                self.test_dir / \"Segments/SubmitTemplateBase.sh\",\n                self.test_dir / \"Segments/TestExec\",\n            ],\n        )\n        self.assertEqual(\n            (self.test_dir / \"Segments/Segment_0000/jobid.txt\").read_text(),\n            \"000\",\n        )\n        self.assertEqual(\n            (self.test_dir / \"Segments/Segment_0000/out.txt\")\n            .read_text()\n            .split(),\n            [\n                \"--input-file\",\n                str(self.test_dir / \"Segments/Segment_0000/InputFile.yaml\"),\n                \"--check-options\",\n            ],\n        )\n        with open(\n            self.test_dir / \"Segments/Segment_0000/SchedulerContext.yaml\", \"r\"\n        ) as open_context_file:\n            context = yaml.safe_load(open_context_file)\n        self.assertDictEqual(\n            context,\n            dict(\n                clean_output=False,\n                context_file_name=\"SchedulerContext.yaml\",\n                copy_executable=None,\n                executable=str(self.test_dir / \"Segments/TestExec\"),\n                executable_name=\"TestExec\",\n                extra_option=\"TestOpt\",\n                metadata_option=\"MetaOpt\",\n                force=False,\n                validate=True,\n                input_file=\"InputFile.yaml\",\n                input_file_name=\"InputFile.yaml\",\n                input_file_template=str(self.test_dir / \"InputFile.yaml\"),\n                job_name=\"TestExec\",\n                out_file=str(\n                    self.test_dir / \"Segments/Segment_0000/spectre.out\"\n                ),\n                out_file_name=\"spectre.out\",\n                run_dir=str(self.test_dir / \"Segments/Segment_0000\"),\n                scheduler=[\"echo\", \"Submitted batch job 000\"],\n                segments_dir=str(self.test_dir / \"Segments\"),\n                spectre_cli=str(self.spectre_cli),\n                submit=True,\n                submit_script_name=\"Submit.sh\",\n                submit_script_template=str(\n                    self.test_dir / \"Segments/SubmitTemplate.sh\"\n                ),\n            ),\n        )\n        self.assertEqual(\n            (self.test_dir / \"Segments/Segment_0000/Submit.sh\").read_text(),\n            \"\"\"\\\nSPECTRE_EXECUTABLE={executable}\nSPECTRE_CLI={spectre_cli}\nNUM_NODES=1\nNUM_TASKS_PER_NODE=5\n\"\"\".format(\n                executable=self.test_dir / \"Segments/TestExec\",\n                spectre_cli=self.spectre_cli,\n            ),\n        )\n\n        # Resubmit from the first segment using `schedule`\n        checkpoint = Checkpoint.match(\n            self.test_dir / \"Segments/Segment_0000/Checkpoints/Checkpoint_0000\"\n        )\n        checkpoint.path.mkdir(parents=True)\n        schedule(\n            input_file_template=self.test_dir\n            / \"Segments/Segment_0000/InputFile.yaml\",\n            scheduler=[\"echo\", \"Submitted batch job 001\"],\n            submit_script_template=self.test_dir / \"Segments/SubmitTemplate.sh\",\n            executable=self.test_dir / \"Segments/TestExec\",\n            segments_dir=self.test_dir / \"Segments\",\n            from_checkpoint=checkpoint,\n            extra_option=\"TestOpt\",\n            metadata_option=\"MetaOpt\",\n            submit=True,\n        )\n        self.assertEqual(\n            (self.test_dir / \"Segments/Segment_0001/jobid.txt\").read_text(),\n            \"001\",\n        )\n\n        # Resubmit from the second segment using `resubmit`\n        checkpoint = Checkpoint.match(\n            self.test_dir / \"Segments/Segment_0001/Checkpoints/Checkpoint_0000\"\n        )\n        checkpoint.path.mkdir(parents=True)\n        resubmit(self.test_dir / \"Segments\", submit=True)\n        self.assertEqual(\n            (self.test_dir / \"Segments/Segment_0002/jobid.txt\").read_text(),\n            \"001\",\n        )\n\n    def test_cli(self):\n        runner = CliRunner()\n        runner.invoke(\n            schedule_command,\n            [\n                str(self.input_file_template),\n                \"-E\",\n                str(self.executable),\n                \"-o\",\n                str(self.test_dir / \"Run\"),\n                \"-p\",\n                \"extra_option=TestOpt\",\n                \"-p\",\n                \"metadata_option=MetaOpt\",\n            ],\n            catch_exceptions=False,\n        )\n        runner.invoke(\n            schedule_command,\n            [\n                str(self.input_file_template),\n                \"-E\",\n                str(self.executable),\n                \"-O\",\n                str(self.test_dir / \"Segments\"),\n                \"-p\",\n                \"extra_option=TestOpt\",\n                \"-p\",\n                \"metadata_option=MetaOpt\",\n                \"--scheduler\",\n                \"cat\",\n                \"--submit-script-template\",\n                str(self.submit_script_template),\n                \"--submit\",\n            ],\n            catch_exceptions=False,\n        )\n        checkpoint = Checkpoint.match(\n            self.test_dir / \"Segments/Segment_0000/Checkpoints/Checkpoint_0000\"\n        )\n        checkpoint.path.mkdir(parents=True)\n        runner.invoke(\n            resubmit_command,\n            [\n                str(self.test_dir / \"Segments\"),\n            ],\n            catch_exceptions=False,\n        )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/tools/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_add_python_bindings_test(\n  \"tools.CharmSimplifyTraces\"\n  Test_CharmSimplifyTraces.py\n  \"Python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"tools.CleanOutput\"\n  Test_CleanOutput.py\n  \"Python\"\n  None)\n\nspectre_add_python_bindings_test(\n  \"tools.Status\"\n  Test_Status.py\n  \"Python\"\n  None\n  TIMEOUT 30)\n\nspectre_add_python_bindings_test(\n  \"tools.ValidateInputFile\"\n  Test_ValidateInputFile.py\n  \"Python\"\n  None\n  TIMEOUT 30)\n"
  },
  {
    "path": "tests/tools/Test_CharmSimplifyTraces.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport unittest\n\nfrom spectre.tools.CharmSimplifyTraces import extract_first_template_parameter\n\n\nclass TestCharmSimplifyTraces(unittest.TestCase):\n    def test_extract_first_template_parameter(self):\n        # Test the example in the docs\n        self.assertEqual(\n            extract_first_template_parameter(\n                \"A<1, B<2, 4>, 3>, C, D, E<7, 8, F<9>>\"\n            ),\n            \"A<1, B<2, 4>, 3>\",\n        )\n        # This test should be expanded further!\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/tools/Test_CleanOutput.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport shutil\nimport unittest\n\nimport yaml\nfrom click.testing import CliRunner\n\nfrom spectre import Informer\nfrom spectre.tools.CleanOutput import (\n    MissingExpectedOutputError,\n    clean_output,\n    clean_output_command,\n)\n\n\nclass TestCleanOutput(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = os.path.join(\n            Informer.unit_test_build_path(), \"tools\", \"CleanOutput\"\n        )\n        self.input_file_path = os.path.join(self.test_dir, \"Input.yaml\")\n        self.reduction_file_path = os.path.join(self.test_dir, \"Reduction.h5\")\n        self.volume_file_path = os.path.join(self.test_dir, \"Volume0.h5\")\n        os.makedirs(self.test_dir, exist_ok=True)\n        with open(self.input_file_path, \"w\") as open_file:\n            yaml.safe_dump_all(\n                [{\"ExpectedOutput\": [\"Reduction.h5\", \"Volume*.h5\"]}, {}],\n                open_file,\n            )\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir)\n\n    def test_clean_output(self):\n        with self.assertRaises(MissingExpectedOutputError):\n            clean_output(\n                input_file=self.input_file_path,\n                output_dir=self.test_dir,\n                force=False,\n            )\n        clean_output(\n            input_file=self.input_file_path,\n            output_dir=self.test_dir,\n            force=True,\n        )\n        with open(self.reduction_file_path, \"w\"):\n            pass\n        with self.assertRaisesRegex(MissingExpectedOutputError, \"Volume\\*.h5\"):\n            clean_output(\n                input_file=self.input_file_path,\n                output_dir=self.test_dir,\n                force=False,\n            )\n        self.assertFalse(os.path.exists(self.reduction_file_path))\n        with open(self.reduction_file_path, \"w\"):\n            pass\n        with open(self.volume_file_path, \"w\"):\n            pass\n        clean_output(\n            input_file=self.input_file_path,\n            output_dir=self.test_dir,\n            force=False,\n        )\n        self.assertFalse(os.path.exists(self.reduction_file_path))\n        self.assertFalse(os.path.exists(self.volume_file_path))\n\n    def test_cli(self):\n        runner = CliRunner()\n        with open(self.reduction_file_path, \"w\"):\n            pass\n        with open(self.volume_file_path, \"w\"):\n            pass\n        result = runner.invoke(\n            clean_output_command, [\"-o\", self.test_dir, self.input_file_path]\n        )\n        self.assertEqual(result.exit_code, 0)\n        result = runner.invoke(\n            clean_output_command,\n            [\"-o\", self.test_dir, \"-f\", self.input_file_path],\n        )\n        self.assertEqual(result.exit_code, 0)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/tools/Test_CompileReleaseNotes.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport textwrap\nimport unittest\n\nimport git\nimport yaml\n\nfrom tools.CompileReleaseNotes import (\n    PullRequest,\n    compile_release_notes,\n    get_last_release,\n    get_merged_pull_requests,\n    get_upgrade_instructions,\n)\n\n\nclass TestCompileReleaseNotes(unittest.TestCase):\n    def test_get_last_release(self):\n        repo = git.Repo(path=__file__, search_parent_directories=True)\n        with open(\n            os.path.join(repo.working_dir, \"Metadata.yaml\"), \"r\"\n        ) as open_metadata_file:\n            metadata = yaml.safe_load(open_metadata_file)\n        expected_tag_name = \"v\" + metadata[\"Version\"]\n        last_release = get_last_release(repo, head_rev=\"HEAD\")\n        self.assertEqual(\n            str(last_release),\n            expected_tag_name,\n            (\n                f\"The last release '{last_release}' doesn't match the expected \"\n                f\"tag name '{expected_tag_name}' listed in the repo metadata.\"\n            ),\n        )\n        also_last_release = get_last_release(repo, head_rev=last_release)\n        self.assertEqual(\n            last_release,\n            also_last_release,\n            (\n                f\"Should find the last release '{last_release}' when HEAD is\"\n                f\" the release tag, but found '{also_last_release}'.\"\n            ),\n        )\n        earlier_release = get_last_release(\n            repo, head_rev=str(last_release) + \"~1\"\n        )\n        self.assertNotEqual(\n            earlier_release,\n            last_release,\n            (\n                \"Should find an earlier release when HEAD is before \"\n                f\"'{last_release}', not the same release.\"\n            ),\n        )\n\n    def test_get_merged_pull_requests(self):\n        repo = git.Repo(path=__file__, search_parent_directories=True)\n        last_release = get_last_release(repo)\n        last_merge_commit = next(\n            repo.iter_commits(rev=last_release, min_parents=2)\n        )\n        merged_prs = get_merged_pull_requests(\n            repo,\n            from_rev=(str(last_merge_commit) + \"~1\"),\n            to_rev=last_merge_commit,\n        )\n        self.assertTrue(\n            len(merged_prs) > 0,\n            (\n                \"Failed to parse pull request corresponding to last merge \"\n                f\"commit before release {last_release}: '{last_merge_commit}' \"\n            ),\n        )\n\n    def test_get_upgrade_instructions(self):\n        upgrade_instructions = get_upgrade_instructions(textwrap.dedent(\"\"\"\\\n            ### Upgrade instructions\n            Here's what you should do when rebasing on this PR:\n            <!-- UPGRADE INSTRUCTIONS -->\n            - Add the option `Evolution.InitialTime` to evolution input files.\n              Set it to the value `0.` to keep the behavior the same as before.\n            <!-- UPGRADE INSTRUCTIONS -->\n            \"\"\"))\n        self.assertEqual(\n            upgrade_instructions,\n            (\n                \"- Add the option `Evolution.InitialTime` to evolution input\"\n                \" files.\\n  Set it to the value `0.` to keep the behavior the\"\n                \" same as before.\"\n            ),\n        )\n        self.assertIsNone(\n            get_upgrade_instructions(\n                \"<!-- UPGRADE INSTRUCTIONS --> <!-- UPGRADE INSTRUCTIONS -->\"\n            ),\n            \"Not all whitespace is stripped\",\n        )\n        self.assertIsNone(\n            get_upgrade_instructions(\n                \"<!-- UPGRADE INSTRUCTIONS -->\\n<!-- UPGRADE INSTRUCTIONS -->\"\n            ),\n            \"Not all whitespace is stripped\",\n        )\n        self.assertEqual(\n            get_upgrade_instructions(\n                \"<!-- UPGRADE INSTRUCTIONS -->\\n\\n\\n   Do this and that.\"\n                \"  \\n\\n<!-- UPGRADE INSTRUCTIONS -->\"\n            ),\n            \"Do this and that.\",\n            \"Not all whitespace is stripped\",\n        )\n        self.assertIsNone(get_upgrade_instructions(\"\"))\n        self.assertIsNone(get_upgrade_instructions(None))\n\n    def test_compile_release_notes(self):\n        self.assertEqual(\n            compile_release_notes([]),\n            textwrap.dedent(\"\"\"\\\n                ## Merged pull-requests (0)\n\n                _None_\n                \"\"\"),\n        )\n        pr1 = PullRequest(id=1, title=\"Add this\", author=\"author1\")\n        pr2 = PullRequest(\n            id=2,\n            title=\"Also add this new feature\",\n            author=\"author2\",\n            url=\"https://github.com/2\",\n        )\n        self.assertEqual(\n            compile_release_notes([pr1, pr2]),\n            textwrap.dedent(\"\"\"\\\n                ## Merged pull-requests (2)\n\n                **General changes (2):**\n\n                - Add this (#1)\n                - Also add this new feature ([#2](https://github.com/2))\n\n                Contributors (2): @author1, @author2\n                \"\"\"),\n        )\n        major_pr1 = PullRequest(\n            id=3,\n            title=\"This is big\",\n            author=\"author2\",\n            group=\"new feature\",\n            upgrade_instructions=\"- Do this.\\n- And that.\",\n        )\n        major_pr2 = PullRequest(\n            id=4, title=\"Another feature\", author=\"author3\", group=\"new feature\"\n        )\n        bugfix_pr1 = PullRequest(\n            id=5,\n            title=\"Fixed this bug\",\n            author=\"author3\",\n            url=\"https://github.com/5\",\n            group=\"bugfix\",\n            upgrade_instructions=\"You'll have to rerun your simulation.\",\n        )\n        bugfix_pr2 = PullRequest(\n            id=6, title=\"Fixed another bug\", author=\"author1\", group=\"bugfix\"\n        )\n        self.assertEqual(\n            compile_release_notes(\n                [pr1, major_pr1, pr2, bugfix_pr1, bugfix_pr2, major_pr2]\n            ),\n            textwrap.dedent(\"\"\"\\\n                ## Upgrade instructions\n\n                **From #3 (This is big):**\n\n                - Do this.\n                - And that.\n\n                **From [#5](https://github.com/5) (Fixed this bug):**\n\n                You'll have to rerun your simulation.\n\n                ## Merged pull-requests (6)\n\n                **New features (2):**\n\n                - This is big (#3)\n                - Another feature (#4)\n\n                **General changes (2):**\n\n                - Add this (#1)\n                - Also add this new feature ([#2](https://github.com/2))\n\n                **Bugfixes (2):**\n\n                - Fixed this bug ([#5](https://github.com/5))\n                - Fixed another bug (#6)\n\n                Contributors (3): @author1, @author2, @author3\n                \"\"\"),\n        )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/tools/Test_Status.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nimport shutil\nimport unittest\n\nimport numpy as np\nimport yaml\n\nimport spectre.IO.H5 as spectre_h5\nfrom spectre.Informer import unit_test_build_path\nfrom spectre.support.Logging import configure_logging\nfrom spectre.tools.Status.ExecutableStatus import match_executable_status\nfrom spectre.tools.Status.Status import get_executable_name, get_input_file\n\n\nclass TestExecutableStatus(unittest.TestCase):\n    def setUp(self):\n        self.input_file = {\n            \"Observers\": {\"ReductionFileName\": \"Reductions\"},\n            \"EventsAndTriggersAtSlabs\": [\n                {\n                    \"Trigger\": \"Always\",\n                    \"Events\": [\n                        {\"ObserveTimeStep\": {\"SubfileName\": \"TimeSteps\"}}\n                    ],\n                }\n            ],\n        }\n        self.work_dir = os.path.join(\n            unit_test_build_path(), \"tools/ExecutableStatus\"\n        )\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n        os.makedirs(self.work_dir, exist_ok=True)\n        with spectre_h5.H5File(\n            os.path.join(self.work_dir, \"Reductions.h5\"), \"w\"\n        ) as open_h5_file:\n            # Time data\n            time_subfile = open_h5_file.insert_dat(\n                \"/TimeSteps\",\n                legend=[\n                    \"Time\",\n                    \"Slab size\",\n                    \"Minimum Walltime\",\n                    \"Maximum Walltime\",\n                ],\n                version=0,\n            )\n            time_subfile.append([0.0, 0.5, 0.5, 1.0])\n            time_subfile.append([1.0, 0.5, 2.0, 3.0])\n            time_subfile.append([2.0, 0.5, 3.0, 4.0])\n            open_h5_file.close_current_object()\n            # Control systems data\n            rotation_subfile = open_h5_file.insert_dat(\n                \"/ControlSystems/Rotation/z\",\n                legend=[\"FunctionOfTime\"],\n                version=0,\n            )\n            rotation_subfile.append([0.0])\n            rotation_subfile.append([np.pi])\n            open_h5_file.close_current_object()\n            # AH data\n            aha_subfile = open_h5_file.insert_dat(\n                \"/ApparentHorizons/ControlSystemAhA_Centers\",\n                legend=[\n                    \"InertialCenter_x\",\n                    \"InertialCenter_y\",\n                    \"InertialCenter_z\",\n                ],\n                version=0,\n            )\n            aha_subfile.append([1.0, 0.0, 0.0])\n            open_h5_file.close_current_object()\n            ahb_subfile = open_h5_file.insert_dat(\n                \"/ApparentHorizons/ControlSystemAhB_Centers\",\n                legend=[\n                    \"InertialCenter_x\",\n                    \"InertialCenter_y\",\n                    \"InertialCenter_z\",\n                ],\n                version=0,\n            )\n            ahb_subfile.append([-1.0, 0.0, 0.0])\n            open_h5_file.close_current_object()\n            # Constraints\n            constraints_subfile = open_h5_file.insert_dat(\n                \"/Norms\",\n                legend=[\n                    \"L2Norm(ConstraintEnergy)\",\n                    \"L2Norm(PointwiseL2Norm(ThreeIndexConstraint))\",\n                ],\n                version=0,\n            )\n            constraints_subfile.append([1.0e-3, 1.0e-4])\n            open_h5_file.close_current_object()\n            # Elliptic solver residuals\n            residuals_subfile = open_h5_file.insert_dat(\n                \"/NewtonRaphsonResiduals\",\n                legend=[\"Iteration\", \"Residual\"],\n                version=0,\n            )\n            residuals_subfile.append([0.0, 1.0e-1])\n            residuals_subfile.append([1.0, 1.0e-4])\n            open_h5_file.close_current_object()\n            residuals_subfile = open_h5_file.insert_dat(\n                \"/GmresResiduals\",\n                legend=[\"Iteration\", \"Residual\"],\n                version=0,\n            )\n            residuals_subfile.append([0.0, 1.0e-1])\n            residuals_subfile.append([1.0, 1.0e-4])\n            residuals_subfile.append([0.0, 1.0e-2])\n            residuals_subfile.append([1.0, 1.0e-5])\n\n    def tearDown(self):\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n\n    def test_evolution_status(self):\n        executable_status = match_executable_status(\"EvolveSomething\")\n        status = executable_status.status(self.input_file, self.work_dir)\n        self.assertEqual(status, {\"Time\": 2.0, \"Speed\": 2400.0})\n        self.assertEqual(executable_status.format(\"Time\", 1.5), \"1.5\")\n        self.assertEqual(executable_status.format(\"Speed\", 1.2), \"1.2\")\n\n    def test_evolve_bbh_status(self):\n        executable_status = match_executable_status(\"EvolveGhBinaryBlackHole\")\n        status = executable_status.status(self.input_file, self.work_dir)\n        self.assertEqual(status[\"Time\"], 2.0)\n        self.assertEqual(status[\"Speed\"], 2400.0)\n        self.assertEqual(status[\"Orbits\"], 0.5)\n        self.assertEqual(status[\"Separation\"], 2.0)\n        self.assertEqual(status[\"3-Index Constraint\"], 1.0e-4)\n\n    def test_evolve_single_bh_status(self):\n        executable_status = match_executable_status(\"EvolveGhSingleBlackHole\")\n        status = executable_status.status(self.input_file, self.work_dir)\n        self.assertEqual(status[\"Time\"], 2.0)\n        self.assertEqual(status[\"Speed\"], 2400.0)\n        self.assertEqual(status[\"Constraint Energy\"], 1.0e-3)\n\n    def test_elliptic_status(self):\n        executable_status = match_executable_status(\"SolveSomething\")\n        status = executable_status.status(self.input_file, self.work_dir)\n        self.assertEqual(status, {\"Iteration\": 1.0, \"Residual\": 1.0e-4})\n        self.assertEqual(executable_status.format(\"Iteration\", 1.0), \"1\")\n        self.assertEqual(\n            executable_status.format(\"Residual\", 1.0e-4), \"1.0e-04\"\n        )\n\n    def test_xcts_status(self):\n        executable_status = match_executable_status(\"SolveXcts\")\n        status = executable_status.status(self.input_file, self.work_dir)\n        self.assertEqual(\n            status,\n            {\n                \"Nonlinear iteration\": 1,\n                \"Nonlinear residual\": 1.0e-4,\n                \"Linear iteration\": 2,\n                \"Linear residual\": 1.0e-5,\n            },\n        )\n        self.assertEqual(\n            executable_status.format(\"Nonlinear iteration\", 1.0), \"1\"\n        )\n        self.assertEqual(executable_status.format(\"Linear iteration\", 1.0), \"1\")\n        self.assertEqual(\n            executable_status.format(\"Nonlinear residual\", 1.0e-4), \"1.0e-04\"\n        )\n        self.assertEqual(\n            executable_status.format(\"Linear residual\", 1.0e-4), \"1.0e-04\"\n        )\n\n\nclass TestStatus(unittest.TestCase):\n    def setUp(self):\n        self.slurm_comment = (\n            \"SPECTRE_INPUT_FILE=path/to/input/file\\n\"\n            \"SPECTRE_EXECUTABLE=path/to/executable\\n\"\n        )\n        self.work_dir = os.path.join(unit_test_build_path(), \"tools/Status\")\n        self.input_file_path = os.path.join(self.work_dir, \"InputFile.yaml\")\n        os.makedirs(self.work_dir, exist_ok=True)\n        with open(self.input_file_path, \"w\") as open_input_file:\n            yaml.safe_dump_all([{\"Executable\": \"MyExec\"}, {}], open_input_file)\n\n    def tearDown(self):\n        shutil.rmtree(self.work_dir, ignore_errors=True)\n\n    def test_get_input_file(self):\n        self.assertEqual(\n            get_input_file(self.slurm_comment, self.work_dir),\n            os.path.join(self.work_dir, \"path/to/input/file\"),\n        )\n        self.assertIsNone(\n            get_input_file(\"\", os.path.join(self.work_dir, \"nonexistent\"))\n        )\n        self.assertEqual(\n            get_input_file(\"\", self.work_dir), self.input_file_path\n        )\n\n    def test_get_executable_name(self):\n        self.assertEqual(\n            get_executable_name(self.slurm_comment, self.input_file_path),\n            \"executable\",\n        )\n        self.assertIsNone(get_executable_name(\"\", None))\n        self.assertEqual(\n            get_executable_name(\"\", self.input_file_path), \"MyExec\"\n        )\n\n\nif __name__ == \"__main__\":\n    configure_logging(log_level=logging.DEBUG)\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/tools/Test_ValidateInputFile.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport shutil\nimport unittest\nfrom pathlib import Path\n\nimport yaml\nfrom click.testing import CliRunner\n\nfrom spectre.Informer import unit_test_build_path, unit_test_src_path\nfrom spectre.tools.ValidateInputFile import (\n    InvalidInputFileError,\n    validate_input_file,\n    validate_input_file_command,\n)\n\n\nclass TestValidateInputFile(unittest.TestCase):\n    def setUp(self):\n        self.test_dir = Path(unit_test_build_path(), \"tools/ValidateInputFile\")\n        self.test_dir.mkdir(parents=True, exist_ok=True)\n        self.executable = Path(\n            unit_test_build_path(), \"../../bin/SolvePoisson3D\"\n        )\n        self.valid_input_file_path = Path(\n            unit_test_src_path(),\n            \"../InputFiles/Poisson/ProductOfSinusoids3D.yaml\",\n        )\n        self.invalid_input_file_path = self.test_dir / \"InvalidInputFile.yaml\"\n        with open(self.valid_input_file_path, \"r\") as open_input_file:\n            metadata, input_file = yaml.safe_load_all(open_input_file)\n        del input_file[\"DomainCreator\"][\"Brick\"][\"LowerBound\"]\n        with open(self.invalid_input_file_path, \"w\") as open_input_file:\n            yaml.safe_dump_all([metadata, input_file], open_input_file)\n\n    def tearDown(self):\n        shutil.rmtree(self.test_dir, ignore_errors=True)\n\n    def test_validate_input_file(self):\n        validate_input_file(\n            self.valid_input_file_path, executable=self.executable\n        )\n        with self.assertRaisesRegex(InvalidInputFileError, \"LowerBound\"):\n            validate_input_file(\n                self.invalid_input_file_path, executable=self.executable\n            )\n\n    def test_cli(self):\n        runner = CliRunner()\n        result = runner.invoke(\n            validate_input_file_command,\n            [str(self.valid_input_file_path), \"-E\", str(self.executable)],\n        )\n        self.assertEqual(result.exit_code, 0)\n        with self.assertRaisesRegex(InvalidInputFileError, \"LowerBound\"):\n            result = runner.invoke(\n                validate_input_file_command,\n                [str(self.invalid_input_file_path), \"-E\", str(self.executable)],\n                catch_exceptions=False,\n            )\n            self.assertEqual(result.exit_code, 1)\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tests/tools/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tools/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  tools\n  PYTHON_FILES\n  CharmSimplifyTraces.py\n  CleanOutput.py\n  ValidateInputFile.py\n)\n\nadd_subdirectory(Status)\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage_v7.0.0.decl.h.patch",
    "content": "diff --git a/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h b/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\nindex 7444066..317d095 100644\n--- a/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\n+++ b/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\n@@ -38,7 +38,7 @@ template <size_t Dim> class CMessage_BoundaryMessage:public CkMessage{\n     void operator delete(void*p, size_t){dealloc(p);}\n     static void* alloc(int,size_t, int*, int, GroupDepNum);\n     static void dealloc(void *p);\n-    CMessage_BoundaryMessage <Dim> ();\n+    CMessage_BoundaryMessage ();\n     static void *pack(BoundaryMessage <Dim>  *p);\n     static BoundaryMessage <Dim> * unpack(void* p);\n     void *operator new(size_t, const int);\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage_v7.0.1.decl.h.patch",
    "content": "diff --git a/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h b/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\nindex 7444066..317d095 100644\n--- a/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\n+++ b/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\n@@ -38,7 +38,7 @@ template <size_t Dim> class CMessage_BoundaryMessage:public CkMessage{\n     void operator delete(void*p, size_t){dealloc(p);}\n     static void* alloc(int,size_t, int*, int, GroupDepNum);\n     static void dealloc(void *p);\n-    CMessage_BoundaryMessage <Dim> ();\n+    CMessage_BoundaryMessage ();\n     static void *pack(BoundaryMessage <Dim>  *p);\n     static BoundaryMessage <Dim> * unpack(void* p);\n     void *operator new(size_t, const int);\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage_v8.0.0.decl.h.patch",
    "content": "diff --git a/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h~ b/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\nindex 7444066..bcdf184 100644\n--- a/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h~\n+++ b/src/Evolution/DiscontinuousGalerkin/Messages/BoundaryMessage.decl.h\n@@ -38,7 +38,7 @@ template <size_t Dim> class CMessage_BoundaryMessage:public CkMessage{\n     void operator delete(void*p, size_t){dealloc(p);}\n     static void* alloc(int,size_t, int*, int, GroupDepNum);\n     static void dealloc(void *p);\n-    CMessage_BoundaryMessage <Dim> ();\n+    CMessage_BoundaryMessage();\n     static void *pack(BoundaryMessage <Dim>  *p);\n     static BoundaryMessage <Dim> * unpack(void* p);\n     void *operator new(size_t, const int);\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmArray_v7.0.0.decl.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmArray.decl.h b/build_main/src/Parallel/Algorithms/AlgorithmArray.decl.h\nindex 7bc3d5c52..f10f24c9a 100644\n--- a/src/Parallel/Algorithms/AlgorithmArray.decl.h\n+++ b/src/Parallel/Algorithms/AlgorithmArray.decl.h\n@@ -498,7 +498,7 @@ template <class ParallelComponent, class SpectreArrayIndex>  class CProxyElement\n \n /* DECLS: void receive_data(const typename ReceiveTag::temporal_id &impl_noname_7, const ReceiveData_t &impl_noname_8, bool enable_if_disabled);\n  */\n-    template <class ReceiveTag, class ReceiveData_t, typename Fwd1 = typename ReceiveTag::temporal_id, typename Fwd2 = ReceiveData_t>\n+    template <typename ReceiveTag, typename Fwd2, typename Fwd1 = typename ReceiveTag::temporal_id>\n     void receive_data(Fwd1 &&impl_noname_7, Fwd2 &&impl_noname_8, bool enable_if_disabled = false, const CkEntryOptions *impl_e_opts=NULL) ;\n \n /* DECLS: void set_terminate(const bool &impl_noname_9);\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmArray_v7.0.0.def.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmArray.def.h b/src/Parallel/Algorithms/AlgorithmArray.def.h\nindex 79de7a10b..ac9571c89 100644\n--- a/src/Parallel/Algorithms/AlgorithmArray.def.h\n+++ b/src/Parallel/Algorithms/AlgorithmArray.def.h\n@@ -555,16 +555,17 @@ void CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::recei\n /* DEFS: void receive_data(const typename ReceiveTag::temporal_id &impl_noname_7, const ReceiveData_t &impl_noname_8, bool enable_if_disabled);\n  */\n template <class ParallelComponent, class SpectreArrayIndex> \n-template <class ReceiveTag, class ReceiveData_t, typename Fwd1, typename Fwd2>\n+template <typename ReceiveTag, typename Fwd2, typename Fwd1>\n void CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::receive_data(Fwd1 &&impl_noname_7, Fwd2 &&impl_noname_8, bool enable_if_disabled, const CkEntryOptions *impl_e_opts) \n {\n+  using ReceiveData_t = Fwd2;\n   ckCheck();\n   AlgorithmArray <ParallelComponent, SpectreArrayIndex>  *obj = ckLocal();\n-  if (obj) {\n+  if (obj != nullptr and not Parallel::detail::max_inline_entry_methods_reached()) {\n   envelope env;\n   env.setMsgtype(ForArrayEltMsg);\n   env.setTotalsize(0);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>()));\n   _TRACE_CREATION_DONE(1);\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>()),CkMyPe(), 0, ((CkArrayIndex&)ckGetIndex()).getProjectionID(), obj);\n #if CMK_LBDB_ON\n@@ -669,7 +670,7 @@ bool CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::invok\n   envelope env;\n   env.setMsgtype(ForArrayEltMsg);\n   env.setTotalsize(0);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>()));\n   _TRACE_CREATION_DONE(1);\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>()),CkMyPe(), 0, ((CkArrayIndex&)ckGetIndex()).getProjectionID(), obj);\n #if CMK_LBDB_ON\n@@ -1399,7 +1400,7 @@ template <class ParallelComponent, class SpectreArrayIndex>\n template <class ReceiveTag, class MessageType>\n int CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::reg_receive_data_MessageType() {\n   int epidx = CkRegisterEp<ReceiveTag, MessageType>(\"receive_data(MessageType* impl_msg)\",\n-      reinterpret_cast<CkCallFnPtr>(_call_receive_data_MessageType<ReceiveTag, MessageType>), CMessage_MessageType::__idx, __idx, 0);\n+      reinterpret_cast<CkCallFnPtr>(_call_receive_data_MessageType<ReceiveTag, MessageType>), MessageType::base::__idx, __idx, 0);\n   CkRegisterMessagePupFn(epidx, (CkMessagePupFn)MessageType::ckDebugPup);\n   return epidx;\n }\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmArray_v7.0.1.decl.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmArray.decl.h b/build_main/src/Parallel/Algorithms/AlgorithmArray.decl.h\nindex 7bc3d5c52..f10f24c9a 100644\n--- a/src/Parallel/Algorithms/AlgorithmArray.decl.h\n+++ b/src/Parallel/Algorithms/AlgorithmArray.decl.h\n@@ -498,7 +498,7 @@ template <class ParallelComponent, class SpectreArrayIndex>  class CProxyElement\n \n /* DECLS: void receive_data(const typename ReceiveTag::temporal_id &impl_noname_7, const ReceiveData_t &impl_noname_8, bool enable_if_disabled);\n  */\n-    template <class ReceiveTag, class ReceiveData_t, typename Fwd1 = typename ReceiveTag::temporal_id, typename Fwd2 = ReceiveData_t>\n+    template <typename ReceiveTag, typename Fwd2, typename Fwd1 = typename ReceiveTag::temporal_id>\n     void receive_data(Fwd1 &&impl_noname_7, Fwd2 &&impl_noname_8, bool enable_if_disabled = false, const CkEntryOptions *impl_e_opts=NULL) ;\n \n /* DECLS: void set_terminate(const bool &impl_noname_9);\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmArray_v7.0.1.def.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmArray.def.h b/src/Parallel/Algorithms/AlgorithmArray.def.h\nindex 79de7a10b..ac9571c89 100644\n--- a/src/Parallel/Algorithms/AlgorithmArray.def.h\n+++ b/src/Parallel/Algorithms/AlgorithmArray.def.h\n@@ -555,16 +555,17 @@ void CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::recei\n /* DEFS: void receive_data(const typename ReceiveTag::temporal_id &impl_noname_7, const ReceiveData_t &impl_noname_8, bool enable_if_disabled);\n  */\n template <class ParallelComponent, class SpectreArrayIndex> \n-template <class ReceiveTag, class ReceiveData_t, typename Fwd1, typename Fwd2>\n+template <typename ReceiveTag, typename Fwd2, typename Fwd1>\n void CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::receive_data(Fwd1 &&impl_noname_7, Fwd2 &&impl_noname_8, bool enable_if_disabled, const CkEntryOptions *impl_e_opts) \n {\n+  using ReceiveData_t = Fwd2;\n   ckCheck();\n   AlgorithmArray <ParallelComponent, SpectreArrayIndex>  *obj = ckLocal();\n-  if (obj) {\n+  if (obj != nullptr and not Parallel::detail::max_inline_entry_methods_reached()) {\n   envelope env;\n   env.setMsgtype(ForArrayEltMsg);\n   env.setTotalsize(0);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>()));\n   _TRACE_CREATION_DONE(1);\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>()),CkMyPe(), 0, ((CkArrayIndex&)ckGetIndex()).getProjectionID(), obj);\n #if CMK_LBDB_ON\n@@ -669,7 +670,7 @@ bool CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::invok\n   envelope env;\n   env.setMsgtype(ForArrayEltMsg);\n   env.setTotalsize(0);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>()));\n   _TRACE_CREATION_DONE(1);\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>()),CkMyPe(), 0, ((CkArrayIndex&)ckGetIndex()).getProjectionID(), obj);\n #if CMK_LBDB_ON\n@@ -1399,7 +1400,7 @@ template <class ParallelComponent, class SpectreArrayIndex>\n template <class ReceiveTag, class MessageType>\n int CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::reg_receive_data_MessageType() {\n   int epidx = CkRegisterEp<ReceiveTag, MessageType>(\"receive_data(MessageType* impl_msg)\",\n-      reinterpret_cast<CkCallFnPtr>(_call_receive_data_MessageType<ReceiveTag, MessageType>), CMessage_MessageType::__idx, __idx, 0);\n+      reinterpret_cast<CkCallFnPtr>(_call_receive_data_MessageType<ReceiveTag, MessageType>), MessageType::base::__idx, __idx, 0);\n   CkRegisterMessagePupFn(epidx, (CkMessagePupFn)MessageType::ckDebugPup);\n   return epidx;\n }\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmArray_v8.0.0.decl.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmArray.decl.h~ b/src/Parallel/Algorithms/AlgorithmArray.decl.h\nindex 8fc4551..9a0f67b 100644\n--- a/src/Parallel/Algorithms/AlgorithmArray.decl.h~\n+++ b/src/Parallel/Algorithms/AlgorithmArray.decl.h\n@@ -553,7 +553,7 @@ template <class ParallelComponent, class SpectreArrayIndex>  class CProxyElement\n \n /* DECLS: void receive_data(const typename ReceiveTag::temporal_id &impl_noname_7, const ReceiveData_t &impl_noname_8, bool enable_if_disabled);\n  */\n-    template <class ReceiveTag, class ReceiveData_t, typename Fwd1 = typename ReceiveTag::temporal_id, typename Fwd2 = ReceiveData_t>\n+    template <class ReceiveTag, typename Fwd2, typename Fwd1 = typename ReceiveTag::temporal_id>\n     void receive_data(Fwd1 &&impl_noname_7, Fwd2 &&impl_noname_8, bool enable_if_disabled = false, const CkEntryOptions *impl_e_opts=NULL) ;\n \n /* DECLS: void set_terminate(const bool &impl_noname_9);\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmArray_v8.0.0.def.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmArray.def.h~ b/src/Parallel/Algorithms/AlgorithmArray.def.h\nindex a2eea60..b353f76 100644\n--- a/src/Parallel/Algorithms/AlgorithmArray.def.h~\n+++ b/src/Parallel/Algorithms/AlgorithmArray.def.h\n@@ -564,16 +564,17 @@ void CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::recei\n /* DEFS: void receive_data(const typename ReceiveTag::temporal_id &impl_noname_7, const ReceiveData_t &impl_noname_8, bool enable_if_disabled);\n  */\n template <class ParallelComponent, class SpectreArrayIndex> \n-template <class ReceiveTag, class ReceiveData_t, typename Fwd1, typename Fwd2>\n+template <class ReceiveTag, typename Fwd2, typename Fwd1>\n void CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::receive_data(Fwd1 &&impl_noname_7, Fwd2 &&impl_noname_8, bool enable_if_disabled, const CkEntryOptions *impl_e_opts) \n {\n+  using ReceiveData_t = Fwd2;\n   ckCheck();\n   AlgorithmArray <ParallelComponent, SpectreArrayIndex>  *obj = ckLocal();\n-  if (obj) {\n+  if (obj != nullptr and not Parallel::detail::max_inline_entry_methods_reached()) {\n   envelope env;\n   env.setTotalsize(0);\n   env.setMsgtype(ForArrayEltMsg);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>()));\n   _TRACE_CREATION_DONE(1);\n   CmiObjId projID = ((CkArrayIndex&)ckGetIndex()).getProjectionID();\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall10<ReceiveTag, ReceiveData_t>()),CkMyPe(), 0, &projID, obj);\n@@ -674,7 +675,7 @@ bool CProxyElement_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::invok\n   envelope env;\n   env.setTotalsize(0);\n   env.setMsgtype(ForArrayEltMsg);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>()));\n   _TRACE_CREATION_DONE(1);\n   CmiObjId projID = ((CkArrayIndex&)ckGetIndex()).getProjectionID();\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::template idx_invoke_iterable_action_void<ThisAction, PhaseIndex, DataBoxIndex>()),CkMyPe(), 0, &projID, obj);\n@@ -1427,7 +1428,7 @@ template <class ParallelComponent, class SpectreArrayIndex>\n template <class ReceiveTag, class MessageType>\n int CkIndex_AlgorithmArray <ParallelComponent, SpectreArrayIndex> ::reg_receive_data_MessageType() {\n   int epidx = CkRegisterEp<ReceiveTag, MessageType>(\"receive_data(MessageType* impl_msg)\",\n-      reinterpret_cast<CkCallFnPtr>(_call_receive_data_MessageType<ReceiveTag, MessageType>), CMessage_MessageType::__idx, __idx, 0);\n+      reinterpret_cast<CkCallFnPtr>(_call_receive_data_MessageType<ReceiveTag, MessageType>), MessageType::base::__idx, __idx, 0);\n   CkRegisterMessagePupFn(epidx, (CkMessagePupFn)MessageType::ckDebugPup);\n   return epidx;\n }\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmSingleton_v7.0.0.def.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmSingleton.def.h b/build_main/src/Parallel/Algorithms/AlgorithmSingleton.def.h\nindex 136029936..4e3875fbf 100644\n--- a/src/Parallel/Algorithms/AlgorithmSingleton.def.h\n+++ b/src/Parallel/Algorithms/AlgorithmSingleton.def.h\n@@ -474,11 +474,11 @@ void CProxyElement_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::receive\n {\n   ckCheck();\n   AlgorithmSingleton <ParallelComponent, SpectreArrayIndex>  *obj = ckLocal();\n-  if (obj) {\n+  if (obj != nullptr and not Parallel::detail::max_inline_entry_methods_reached()) {\n   envelope env;\n   env.setMsgtype(ForArrayEltMsg);\n   env.setTotalsize(0);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>()));\n   _TRACE_CREATION_DONE(1);\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>()),CkMyPe(), 0, ((CkArrayIndex&)ckGetIndex()).getProjectionID(), obj);\n #if CMK_LBDB_ON\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmSingleton_v7.0.1.def.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmSingleton.def.h b/build_main/src/Parallel/Algorithms/AlgorithmSingleton.def.h\nindex 136029936..4e3875fbf 100644\n--- a/src/Parallel/Algorithms/AlgorithmSingleton.def.h\n+++ b/src/Parallel/Algorithms/AlgorithmSingleton.def.h\n@@ -474,11 +474,11 @@ void CProxyElement_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::receive\n {\n   ckCheck();\n   AlgorithmSingleton <ParallelComponent, SpectreArrayIndex>  *obj = ckLocal();\n-  if (obj) {\n+  if (obj != nullptr and not Parallel::detail::max_inline_entry_methods_reached()) {\n   envelope env;\n   env.setMsgtype(ForArrayEltMsg);\n   env.setTotalsize(0);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>()));\n   _TRACE_CREATION_DONE(1);\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>()),CkMyPe(), 0, ((CkArrayIndex&)ckGetIndex()).getProjectionID(), obj);\n #if CMK_LBDB_ON\n"
  },
  {
    "path": "tools/CharmModulePatches/src/Parallel/Algorithms/AlgorithmSingleton_v8.0.0.def.h.patch",
    "content": "diff --git a/src/Parallel/Algorithms/AlgorithmSingleton.def.h~ b/src/Parallel/Algorithms/AlgorithmSingleton.def.h\nindex bea0f8a..f6fb8c2 100644\n--- a/src/Parallel/Algorithms/AlgorithmSingleton.def.h~\n+++ b/src/Parallel/Algorithms/AlgorithmSingleton.def.h\n@@ -496,11 +496,11 @@ void CProxyElement_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::r\n {\n   ckCheck();\n   AlgorithmSingleton <ParallelComponent, SpectreArrayIndex>  *obj = ckLocal();\n-  if (obj) {\n+  if (obj != nullptr and not Parallel::detail::max_inline_entry_methods_reached()) {\n   envelope env;\n   env.setTotalsize(0);\n   env.setMsgtype(ForArrayEltMsg);\n-  _TRACE_CREATION_DETAILED(&env, CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>());\n+  _TRACE_CREATION_DETAILED(&env, (CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>()));\n   _TRACE_CREATION_DONE(1);\n   CmiObjId projID = ((CkArrayIndex&)ckGetIndex()).getProjectionID();\n   _TRACE_BEGIN_EXECUTE_DETAILED(CpvAccess(curPeEvent),ForArrayEltMsg,(CkIndex_AlgorithmSingleton <ParallelComponent, SpectreArrayIndex> ::template idx_receive_data_marshall8<ReceiveTag, ReceiveData_t>()),CkMyPe(), 0, &projID, obj);\n"
  },
  {
    "path": "tools/CharmSimplifyTraces.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport json\nimport re\n\nimport click\n\n\ndef extract_first_template_parameter(string):\n    \"\"\"\n    Extract the first template parameter from a string\n\n    Assume you have the string passed in is:\n      A<1, B<2, 4>, 3>, C, D, E<7, 8, F<9>>\n    We want to extract:\n      A<1, B<2, 4>, 3>\n    To do this we count the depth of the angle brackets and stop when we get\n    to the first comma where we have zero angle brackets.\n    \"\"\"\n    if not (\",\" in string):\n        return string\n\n    depth = 0\n    for i in range(0, len(string)):\n        if depth == 0 and string[i] == \",\":\n            break\n        if string[i] == \"<\":\n            depth = depth + 1\n        if string[i] == \">\":\n            assert depth > 0, (\n                \"In the process of extracting first template parameter. \"\n                f\"Depth is {depth} which is not > 0. Parsing string: \"\n                f\"{string}\"\n            )\n            depth = depth - 1\n    assert depth == 0, (\n        \"At end of extracting first template parameter. Depth should be 0 \"\n        f\"but is {depth} instead. Parsing string: {string}\"\n    )\n    return string[:i]\n\n\ndef generic_replacements(text):\n    \"\"\"\n    Apply generic naming replacements for Chares and entry methods.\n    \"\"\"\n    for i in range(len(text)):\n        if (\n            text[i].startswith(\"ENTRY CHARE\")\n            and \"invoke_iterable_action<\" in text[i]\n        ):\n            matches = re.findall(\n                \", std::integral_constant<unsigned long, [0-9]+>\", text[i]\n            )\n            if len(matches) == 0:\n                matches = re.findall(\n                    \", std::integral_constant<long unsigned int, [0-9]+>\",\n                    text[i],\n                )\n            text[i] = text[i].replace(\"invoke_iterable_action<\", \"\")\n            text[i] = text[i].replace(str(matches[-1]), \"\")\n            text[i] = text[i].replace(str(matches[-2]), \"\")\n            text[i] = text[i].replace(\">()\", \"()\")\n        if (\n            text[i].startswith(\"ENTRY CHARE\")\n            and \"simple_action<\" in text[i]\n            and \">()\" in text[i]\n        ):\n            text[i] = text[i].replace(\"simple_action<\", \"\").replace(\">()\", \"()\")\n        elif text[i].startswith(\"ENTRY CHARE\") and \"simple_action<\" in text[i]:\n            # The case where simple_action is receiving arguments\n            text[i] = text[i].replace(\"simple_action<\", \"\").replace(\">(\", \"(\")\n            matches = re.findall('\"(.*)\\(', text[i])\n            # Replace tuple arg with nothing since the argument is irrelevant\n            text[i] = re.sub(\n                '\".*\\(.*\\)',\n                '\"' + extract_first_template_parameter(matches[-1]) + \"()\",\n                text[i],\n            )\n        if (\n            text[i].startswith(\"ENTRY CHARE\")\n            and \"threaded_action<\" in text[i]\n            and \">()\" in text[i]\n        ):\n            text[i] = (\n                text[i].replace(\"threaded_action<\", \"\").replace(\">()\", \"()\")\n            )\n        elif (\n            text[i].startswith(\"ENTRY CHARE\") and \"threaded_action<\" in text[i]\n        ):\n            # The case where threaded_action is receiving arguments\n            text[i] = text[i].replace(\"threaded_action<\", \"\").replace(\">(\", \"(\")\n            matches = re.findall('\"(.*)\\(', text[i])\n            # Replace tuple arg with nothing since the argument is irrelevant\n            text[i] = re.sub(\n                '\".*\\(.*\\)',\n                '\"' + extract_first_template_parameter(matches[-1]) + \"()\",\n                text[i],\n            )\n\n        # Simplify general parallel components\n        if text[i].startswith(\"CHARE\") and \"AlgorithmGroup<\" in text[i]:\n            text[i] = (\n                text[i].replace(\"AlgorithmGroup<\", \"\").replace(\",int>\", \"\")\n            )\n        if text[i].startswith(\"CHARE\") and \"AlgorithmNodegroup<\" in text[i]:\n            text[i] = (\n                text[i].replace(\"AlgorithmNodegroup<\", \"\").replace(\",int>\", \"\")\n            )\n        if text[i].startswith(\"CHARE\") and \"Parallel::GlobalCache<\" in text[i]:\n            text[i] = re.sub(\n                'Parallel::GlobalCache<.*\"', 'GlobalCache\"', text[i]\n            )\n        if text[i].startswith(\"CHARE\") and \"Parallel::Main<\" in text[i]:\n            text[i] = re.sub('Parallel::Main<.*\"', 'Main\"', text[i])\n        if (\n            text[i].startswith(\"CHARE\")\n            and \"Parallel::detail::AtSyncIndicator<\" in text[i]\n        ):\n            text[i] = re.sub(\n                'Parallel::detail::AtSyncIndicator<.*\"',\n                'AtSyncIndicator\"',\n                text[i],\n            )\n\n        # This will need to be updated when we rename to\n        # evolution::DgElementArray\n        if (\n            text[i].startswith(\"CHARE\")\n            and \"AlgorithmArray<DgElementArray\" in text[i]\n        ):\n            text[i] = re.sub('\".*\"', '\"DgElementArray\"', text[i])\n        if (\n            text[i].startswith(\"CHARE\")\n            and \"AlgorithmArray<elliptic::DgElementArray\" in text[i]\n        ):\n            text[i] = re.sub('\".*\"', '\"elliptic::DgElementArray\"', text[i])\n\n        # Simplify observers and interpolator\n        if text[i].startswith(\"CHARE\") and (\n            (\"intrp::Interpolator<\" in text[i])\n            or (\"observers::Observer<\" in text[i])\n            or (\"observers::ObserverWriter<\" in text[i])\n        ):\n            text[i] = re.sub('<.*\"', '\"', text[i])\n\n    return text\n\n\ndef user_replacements(text, json_replacements):\n    \"\"\"\n    Apply user replacements from a JSON file.\n\n    First basic textual replacements are performed, followed by regular\n    expression replacements.\n    \"\"\"\n    # if 'RegexReplace' in json_replacements:\n    #     raise ValueError(\"Regular expression replacement not yet added\")\n    if len(json_replacements) != 2:\n        raise ValueError(\n            \"Expected only 2 entry in the JSON root dictionary but got \"\n            + str(len(json_replacements))\n        )\n    basic_replacements = json_replacements[\"BasicReplace\"]\n    regex_replacements = json_replacements[\"RegexReplace\"]\n\n    for i in range(len(text)):\n        for replacement_entry in basic_replacements:\n            if len(basic_replacements[replacement_entry]) == 0:\n                raise ValueError(\n                    \"Expected at least one replacement for \" + replacement_entry\n                )\n\n            if replacement_entry in text[i]:\n                for replacement in basic_replacements[replacement_entry]:\n                    if len(replacement) != 2:\n                        raise ValueError(\n                            \"Basic replacement must be exactly length 2, \"\n                            \"[to_replace, replace_with], but got \"\n                            + str(len(replacement))\n                            + \" (\"\n                            + str(replacement)\n                            + \") for entry \"\n                            + replacement_entry\n                        )\n                    text[i] = text[i].replace(replacement[0], replacement[1])\n\n        for replacement_entry in regex_replacements:\n            if len(regex_replacements[replacement_entry]) == 0:\n                raise ValueError(\n                    \"Expected at least one replacement for \" + replacement_entry\n                )\n\n            if replacement_entry in text[i]:\n                for replacement in regex_replacements[replacement_entry]:\n                    if len(replacement) != 2:\n                        raise ValueError(\n                            \"Regex replacement must be exactly length 2, \"\n                            \"[to_replace, replace_with], but got \"\n                            + str(len(replacement))\n                            + \" (\"\n                            + str(replacement)\n                            + \") for entry \"\n                            + replacement_entry\n                        )\n                    text[i] = re.sub(replacement[0], replacement[1], text[i])\n    return text\n\n\n@click.command(name=\"simplify-traces\")\n@click.argument(\n    \"projections_file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.argument(\"output_file\", required=False, type=click.Path(writable=True))\n@click.option(\n    \"--in-place\",\n    \"-i\",\n    is_flag=True,\n    help=(\n        \"Edit the 'PROJECTIONS_FILE' in place. A backup of the file is \"\n        \"written to the 'OUTPUT_FILE' if specified.\"\n    ),\n)\n@click.option(\n    \"--replacements-json-file\",\n    \"-r\",\n    type=click.File(\"r\"),\n    help=\"\"\"\nA JSON file listing textual and regular expression replacements. The file must\nspecify \"BasicReplace\" and \"RegexReplace\" dictionaries. Each dictionary has keys\nthat are the name of the replacement (unused in any searches). For BasicReplace\nthe value is a list of two-element lists, the first entry in the nested\ntwo-element list is the string to replace and the second what to replace it\nwith. An example entry is:\n\n  \"Actions::MutateApply\": [[\"Actions::MutateApply<\", \"], [\">()\", \"()\"]]\n\nwhere if the line contains \"Actions::MutateApply<\" it and \">()\" are replaced.\nThe regular expression is structured similarly but the entire regex match is\nreplaced.\n\"\"\",\n)\ndef simplify_traces_command(\n    projections_file, output_file, in_place, replacements_json_file\n):\n    \"\"\"Process Charm++ Projections trace files\n\n    Process Charm++ Projections '.sts' (not '.sum.sts') files to make the entry\n    method and Chare names easier to read in the GUI. Long template names are\n    not rendered fully making it impossible to figure out what Action and Chare\n    was being measured. The standard entry methods like 'invoke_iterable_action'\n    and 'simple_action' are simplified by default, but further textual and\n    regular expression replacements can be specified in a JSON file.\n\n    The output of this command will be written to the 'OUTPUT_FILE' if\n    specified, to stdout if unspecified, edited in-place if the '-i' flag is\n    specified. Note that you will need to replace Charm++'s .sts file with the\n    output file and the names must match.\n    \"\"\"\n    _rich_traceback_guard = True  # Hide traceback until here\n\n    with open(projections_file, \"r\") as open_file:\n        original_text = open_file.readlines()\n\n    text = generic_replacements(original_text)\n    if replacements_json_file:\n        text = user_replacements(text, json.load(replacements_json_file))\n\n    if in_place:\n        backup_file = output_file\n        output_file = projections_file\n    else:\n        backup_file = None\n\n    if backup_file:\n        with open(backup_file, \"w\") as open_file:\n            open_file.writelines(original_text)\n    if output_file:\n        with open(output_file, \"w\") as open_file:\n            open_file.writelines(text)\n    else:\n        print(\"\\n\".join(text))\n\n\nif __name__ == \"__main__\":\n    simplify_traces_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "tools/CharmTraceReplacements.json",
    "content": "{\n    \"BasicReplace\": {\n        \"Actions::MutateApply\": [[\"Actions::MutateApply<\", \"\"], [\">()\", \"()\"]],\n        \"EvolutionMetavars<grmhd::AnalyticData::KhInstability>\": [\n            [\"EvolutionMetavars<grmhd::AnalyticData::KhInstability>\",\n             \"Metavars\"]],\n        \"evolution::dg::Actions\": [[\"evolution::dg::Actions\", \"dg\"]],\n        \"grmhd::ValenciaDivClean::subcell\": [\n            [\"grmhd::ValenciaDivClean::subcell\", \"subcell\"]]\n    },\n    \"RegexReplace\": {\n        \"subcell::FixConservativesAndComputePrims\": [\n            [\"subcell::FixConservativesAndComputePrims.*\\\"\",\n             \"subcell::FixConservativesAndComputePrims\\\"\"]],\n        \"subcell::PrimsAfterRollback\": [\n            [\"subcell::PrimsAfterRollback.*\\\"\",\n             \"subcell::PrimsAfterRollback\\\"\"]],\n        \"subcell::ResizeAndComputePrims\": [\n            [\"subcell::ResizeAndComputePrims.*\\\"\",\n             \"subcell::ResizeAndComputePrims\\\"\"]],\n        \"subcell::Actions::TciAndRollback\": [\n            [\"evolution::dg::subcell::Actions::TciAndRollback.*\\\"\",\n             \"TciAndRollback\\\"\"]],\n        \"mem_monitor::ProcessArray\" : [\n            [\"mem_monitor::ProcessArray<DgElementArray.*\\\"\",\n             \"mem_monitor::ProcessArray<DgElementArray>\\\"\"]]\n    }\n}\n"
  },
  {
    "path": "tools/CheckCommits.sh",
    "content": "#!/usr/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Set \\n as the only separator for lists since commit messages might\n# have spaces in them\nIFS=$'\\n'\n\n# We get the list of commit messages to check. We only check commits that are\n# not on the develop branch. For non-Travis runs, from a user fork we assume\n# that the branch 'develop' exists and only check the commits since the HEAD\n# of the develop branch\n# On TravisCI we clone the upstream repository and use the UPSTREAM_BRANCH\n# (typically 'develop') to check for each upstream commit if it is on the\n# pull request branch, once we find a match we save that hash and\n# exit. This allows us to check only files currently being committed.\n# Note that the search is in reverse chronological order.\nCOMMIT_LINES=''\nif [ -z $TRAVIS ];\nthen\n    COMMIT_LINES=`git log develop..HEAD --oneline`\nelse\n    WORK_DIR=`pwd`\n    git clone ${UPSTREAM_REPO} ${HOME}/spectre_upstream\n    cd ${HOME}/spectre_upstream\n    git checkout ${UPSTREAM_BRANCH}\n    COMMITS_ON_UPSTREAM=`git rev-list HEAD`\n    cd $WORK_DIR\n    # For each upstream commit we check if the commit is on this\n    # branch, once we find a match we save that hash and exit. This\n    # allows us to check only files currently being committed.\n    HEAD_COMMIT_HASH=''\n\n    for hash in ${COMMITS_ON_UPSTREAM}\n    do\n        if git cat-file -e $hash^{commit} 2> /dev/null\n        then\n            HEAD_COMMIT_HASH=$hash\n            break\n        fi\n    done\n\n    if [ -z $HEAD_COMMIT_HASH ];\n    then\n        echo \"The branch is not branched from\" \\\n             \"${UPSTREAM_REPO}/${UPSTREAM_BRANCH}\"\n        exit 1\n    fi\n    COMMIT_LINES=`git log ${HEAD_COMMIT_HASH}..HEAD --oneline`\nfi\n\n# For all commit messages, check if they start with one of the key words\nfor commit_msg in $COMMIT_LINES\ndo\n    if grep \"^[0-9a-f]\\{6,40\\} [Ff][Ii][Xx][Uu][Pp]\" <<< $commit_msg\\\n    > /dev/null; then\n        printf \"\\n\\n\\nError: Fixup commit found: %s\\n\\n\\n\" \"$commit_msg\" 2>&1\n        exit 1\n    elif grep \"^[0-9a-f]\\{6,40\\} [Ww][Ii][Pp]\" <<< $commit_msg > /dev/null; then\n        printf \"\\n\\n\\nError: WIP commit found: %s\\n\\n\\n\" \"$commit_msg\" 2>&1\n        exit 1\n    elif grep \"^[0-9a-f]\\{6,40\\} [Ff][Ii][Xx][Mm][Ee]\" <<< $commit_msg\\\n    > /dev/null; then\n        printf \"\\n\\n\\nError: FixMe commit found: %s\\n\\n\\n\" \"$commit_msg\" 2>&1\n        exit 1\n    elif grep \"^[0-9a-f]\\{6,40\\} [Dd][Ee][Ll][Ee][Tt][Ee][Mm][Ee]\" <<< \\\n    $commit_msg > /dev/null; then\n        printf \"\\n\\n\\nError: DeleteMe commit found: %s\\n\\n\\n\" \"$commit_msg\" 2>&1\n        exit 1\n    elif grep \"^[0-9a-f]\\{6,40\\} [Rr][Ee][Bb][Aa][Ss][Ee][Mm][Ee]\" <<< \\\n    $commit_msg > /dev/null; then\n        printf \"\\n\\n\\nError: RebaseMe commit found: %s\\n\\n\\n\" \"$commit_msg\" 2>&1\n        exit 1\n    elif grep \"^[0-9a-f]\\{6,40\\} [Tt][Ee][Ss][Tt][Ii][Nn][Gg]\" <<< $commit_msg\\\n     > /dev/null; then\n        printf \"\\n\\n\\nError: Testing commit found: %s\\n\\n\\n\" \"$commit_msg\" 2>&1\n        exit 1\n    elif grep \"^[0-9a-f]\\{6,40\\} [Rr][Ee][Bb][Aa][Ss][Ee]\" <<< $commit_msg\\\n     > /dev/null; then\n        printf \"\\n\\n\\nError: Rebase commit found: %s\\n\\n\\n\" \"$commit_msg\" 2>&1\n        exit 1\n    fi\ndone\n\nexit 0\n"
  },
  {
    "path": "tools/CheckFiles.sh",
    "content": "#!/bin/bash -e\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This script can be invoked with the --test argument to run its unit\n# tests instead of checking the repository.\n\n# In addition to sourcing various helper functions, source the standard file\n# checks, i.e., the file checks that are performed in the git commit hook AND\n# by CI testing.\ntop_level=$(git rev-parse --show-cdup) || exit 1\n. \"${top_level}tools/FileTestDefs.sh\"\n\n# redefine grep functions to run on the working directory\nstaged_grep() {\n    grep \"$@\";\n}\npretty_grep() {\n    GREP_COLOR='1;37;41' GREP_COLORS='mt=1;37;41' \\\n        grep --with-filename -n $color_option \"$@\"\n}\n\n##### CI checks #####\nci_checks=()\n\n# Check for iostream header\n# (Checked in CI only to allow local debugging-related commits)\niostream() {\n    is_c++ \"$1\" \\\n     && whitelist \"$1\" \\\n                  'tests/Unit/IO/Exporter/BundledExporter/Test_BundledExporter.cpp$' \\\n                  'src/Utilities/System/AttachDebugger.cpp' \\\n     && grep -q '#include <iostream>' \"$1\"\n}\niostream_report() {\n    echo \"Found iostream header:\"\n    pretty_grep '#include <iostream>' \"$@\"\n}\niostream_test() {\n    test_check pass foo.cpp '#include <vector>'$'\\n'\n    test_check fail foo.cpp '#include <iostream>'$'\\n'\n}\nci_checks+=(iostream)\n\n# Check for TmplDebugging header\n# (Checked in CI only to allow local debugging-related commits)\ntmpl_debugging() {\n    is_c++ \"$1\" && grep -q '#include \"Utilities/TmplDebugging.hpp\"' \"$1\"\n}\ntmpl_debugging_report() {\n    echo \"Found Utilities/TmplDebugging header:\"\n    pretty_grep '#include \"Utilities/TmplDebugging.hpp\"' \"$@\"\n}\ntmpl_debugging_test() {\n    test_check pass foo.cpp '#include <vector>'$'\\n'\n    test_check fail foo.cpp '#include \"Utilities/TmplDebugging.hpp\"'$'\\n'\n}\nci_checks+=(tmpl_debugging)\n\n# Check that every C++ file is listed in the directory's CMakeLists.txt\n# (Checked in CI because this involves comparisons between files and may\n#  be too slow for a fluid git-hook user experience)\n#\n# We check for C++ files in the src directory only, and even there we omit any\n# directories named Executables or Python. This is because we currently only\n# expect the main SpECTRE libraries from src to list all their C++ files.\n# The CMakeLists for Python bindings, executables, and tests will be updated\n# in the future.\n# We also omit special C++ files we know shouldn't be in their CMakeLists.txt.\ncheck_cmakelists_for_missing_cxx() {\n    local dir base cmakelists\n    dir=$(dirname $1)\n    base=$(basename $1)\n    cmakelists=\"${dir}/CMakeLists.txt\"\n    is_c++ \"$1\" \\\n      && whitelist \"$dir\" \\\n                   'docs' \\\n                   'tests/*' \\\n                   'tools' \\\n                   'Executables' \\\n                   'Python' \\\n      && whitelist \"$1\" \\\n                   'src/Informer/InfoAtCompile.cpp$' \\\n                   'src/Informer/InfoAtLink.cpp$' \\\n      && [ -f $cmakelists ] \\\n      && [ $(grep -L \"^[ \\t]*$base\" $cmakelists) ]\n}\ncheck_cmakelists_for_missing_cxx_report() {\n    local file dir base cmakelists\n    echo \"Found C++ files not in CMakeLists.txt:\"\n    for file in \"$@\"; do\n        dir=$(dirname $file)\n        base=$(basename $file)\n        cmakelists=\"${dir}/CMakeLists.txt\"\n        echo \"$base should be added to $cmakelists\"\n    done\n}\ncheck_cmakelists_for_missing_cxx_test() {\n    # This check relies on comparisons between different files, which makes\n    # it cumbersome to test. We omit the test.\n    :\n}\nci_checks+=(check_cmakelists_for_missing_cxx)\n\n# Check CMakeLists.txt contain no spurious C++ files\n# (Checked in CI because this involves comparisons between files and may\n#  be too slow for a fluid git-hook user experience)\n#\n# Check for CMakeLists lines that are [two spaces][anything][.?pp]\n# Then we check that the filename has no slashes (this would indicate a file\n# in a subdirectory), and that the corresponding file exists.\ncheck_cmakelists_for_extra_cxx() {\n    local dir match matches\n    dir=$(dirname $1)\n    if [[ $1 =~ CMakeLists\\.txt$ ]] && whitelist \"$dir\" 'docs' \\\n                                                        'tests' \\\n                                                        'tools' \\\n                                                        'Executables' \\\n                                                        'Exporter' \\\n                                                        'Python'; then\n        matches=$(grep -E \"^  .*\\.[cht]pp\" $1)\n        for match in $matches; do\n            # Special case: the Informer CMakeLists includes a special C++ file\n            [[ $match == '${CMAKE_BINARY_DIR}/Informer/InfoAtCompile.cpp' ]] \\\n              && continue\n            [[ $match =~ '/' ]] || [[ ! -f \"${dir}/$match\" ]] && return 0\n        done\n    fi\n    return 1\n}\ncheck_cmakelists_for_extra_cxx_report() {\n    local file dir match matches\n    echo \"Found spurious C++ files in CMakeLists.txt:\"\n    for file in \"$@\"; do\n        dir=$(dirname $file)\n        matches=$(grep -E \"^  .*\\.[cht]pp\" $file)\n        for match in $matches; do\n            [[ $match == '${CMAKE_BINARY_DIR}/Informer/InfoAtCompile.cpp' ]] \\\n              && continue\n            [[ $match =~ '/' ]] || [[ ! -f \"${dir}/$match\" ]] \\\n              && echo \"$match should be removed from $file\"\n        done\n    done\n}\ncheck_cmakelists_for_extra_cxx_test() {\n    # This check relies on comparisons between different files, which makes\n    # it cumbersome to test. We omit the test.\n    :\n}\nci_checks+=(check_cmakelists_for_extra_cxx)\n\n# Check for \"to do\" comments because these should be issues instead\nprevent_todo_comments() {\n    regex=\"//[[:space:]]*TODO.*\\|.*\\*[[:space:]]*TODO.*\\|.*#[[:space:]]*TODO.*\"\n    is_c++_or_python \"$1\" \\\n        && staged_grep -q -i \"${regex}\" \"$1\"\n}\nprevent_todo_comments_report() {\n    regex=\"//[[:space:]]*TODO.*\\|.*\\*[[:space:]]*TODO.*\\|.*#[[:space:]]*TODO.*\"\n    echo \"Found TODO comments. Please file issues instead.\"\n    pretty_grep -i \"${regex}\" \"$@\"\n}\nprevent_todo_comments_test() {\n    test_check pass foo.hpp ''\n    test_check pass foo.cpp ''\n    test_check pass foo.tpp ''\n    test_check pass foo.tpp 'christodoulou'\n    test_check fail foo.hpp '// TODO blah'\n    test_check fail foo.cpp '// TODO blah'\n    test_check fail foo.tpp '// TODO blah'\n    test_check fail foo.hpp '// TODO: blah'\n    test_check fail foo.hpp '//TODO blah'\n    test_check fail foo.hpp '//TODO'\n    test_check fail foo.hpp ' * TODO blah'\n    test_check fail foo.hpp ' * TODO: blah'\n    test_check fail foo.hpp ' *TODO blah'\n    test_check fail foo.hpp ' *TODO'\n    test_check fail foo.hpp '// todo blah'\n    test_check fail foo.hpp '// todo: blah'\n    test_check fail foo.hpp '//todo blah'\n    test_check fail foo.hpp '//todo'\n    test_check fail foo.hpp ' * todo blah'\n    test_check fail foo.hpp ' * todo: blah'\n    test_check fail foo.hpp ' *todo blah'\n    test_check fail foo.hpp ' *todo'\n    test_check fail foo.hpp '// Todo blah'\n    test_check fail foo.hpp '// Todo: blah'\n    test_check fail foo.hpp '//Todo blah'\n    test_check fail foo.hpp '//Todo'\n    test_check fail foo.hpp ' * Todo blah'\n    test_check fail foo.hpp ' * Todo: blah'\n    test_check fail foo.hpp ' *Todo blah'\n    test_check fail foo.hpp ' *Todo'\n    test_check pass foo.py 'christodoulou'\n    test_check fail foo.py '#TODO: blah'\n    test_check fail foo.py '  #TODO: blah'\n    test_check fail foo.py '# TODO: blah'\n    test_check fail foo.py '  # TODO: blah'\n    test_check fail foo.py '#TODO blah'\n    test_check fail foo.py '  #TODO blah'\n    test_check fail foo.py '# TODO blah'\n    test_check fail foo.py '  # TODO blah'\n}\nci_checks+=(prevent_todo_comments)\n\nif [ \"$1\" = --test ] ; then\n    run_tests \"${ci_checks[@]}\"\n    exit 0\nfi\n\n# Exclude files that are generated, out of our control, etc.\nfind . \\\n     -type f \\\n     ! -path './.git/*' \\\n     ! -path './build*' \\\n     ! -path './docs/*' \\\n     ! -path './external/*' \\\n     ! -path '*.idea/*' \\\n     ! -name '*.patch' \\\n     ! -name '*.pyc' \\\n     ! -path '*/__pycache__/*' \\\n     ! -path './tools/charm_module_patchs/*' \\\n     ! -name 'CircularOrbitCoeffs.cpp' \\\n     ! -name 'CircularOrbitConvertEffsource.cpp' \\\n     ! -name '*~' \\\n     ! -name deploy_key.enc \\\n     -print0 \\\n        | run_checks \"${standard_checks[@]}\" \"${ci_checks[@]}\"\n"
  },
  {
    "path": "tools/CheckMetadata.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport datetime\nimport os\nimport re\nimport unicodedata\nimport unittest\nimport urllib\n\nimport git\nimport pybtex\nimport pybtex.database\nimport yaml\n\nVERSION_PATTERN = r\"(\\d{4})\\.(\\d{2})\\.(\\d{2})(\\.\\d+)?\"\nDOI_PATTERN = r\"10\\.\\d{4,9}/zenodo\\.(\\d+)\"\nORCID_PATTERN = r\"\\d{4}-\\d{4}-\\d{4}-\\d{3}[0-9X]\"\n\n\ndef simplify_name(name):\n    \"\"\"Converts the `name` to lower-case ASCII for fuzzy comparisons.\"\"\"\n    return unicodedata.normalize(\"NFKD\", name.lower()).encode(\"ascii\", \"ignore\")\n\n\nclass TestMetadata(unittest.TestCase):\n    @classmethod\n    def setUpClass(cls):\n        # Always work with the repository that contains this file\n        try:\n            cls.repo = git.Repo(__file__, search_parent_directories=True)\n        except git.exc.InvalidGitRepositoryError as err:\n            raise git.exc.InvalidGitRepositoryError(\n                f\"The file '{__file__}' needs to be in a git repository to \"\n                \"find the corresponding Metadata.yaml file.\"\n            ) from err\n        with open(\n            os.path.join(cls.repo.working_dir, \"Metadata.yaml\"), \"r\"\n        ) as open_metadata_file:\n            cls.metadata = yaml.safe_load(open_metadata_file)\n\n    def test_name(self):\n        self.assertIn(\"Name\", self.metadata)\n        self.assertTrue(isinstance(self.metadata[\"Name\"], str))\n\n    def test_license(self):\n        self.assertIn(\"License\", self.metadata)\n        self.assertTrue(isinstance(self.metadata[\"License\"], str))\n\n    def test_homepage(self):\n        self.assertIn(\"Homepage\", self.metadata)\n        homepage = self.metadata[\"Homepage\"]\n        homepage_components = urllib.parse.urlparse(homepage)\n        self.assertTrue(homepage_components.scheme)\n        self.assertTrue(homepage_components.netloc)\n\n    def test_github(self):\n        self.assertIn(\"GitHub\", self.metadata)\n        github = self.metadata[\"GitHub\"]\n        gh_user, gh_repo = github.split(\"/\")\n        self.assertTrue(gh_user)\n        self.assertTrue(gh_repo)\n\n    def test_version(self):\n        self.assertIn(\"Version\", self.metadata)\n        version = self.metadata[\"Version\"]\n        self.assertRegex(version, \"^\" + VERSION_PATTERN + \"$\")\n\n    def test_publication_date(self):\n        self.assertIn(\"PublicationDate\", self.metadata)\n        publication_date = self.metadata[\"PublicationDate\"]\n        self.assertTrue(isinstance(publication_date, datetime.date))\n        version_year, version_month, version_day = re.match(\n            VERSION_PATTERN, self.metadata[\"Version\"]\n        ).groups()[:3]\n        self.assertEqual(publication_date.year, int(version_year))\n        self.assertEqual(publication_date.month, int(version_month))\n        self.assertEqual(publication_date.day, int(version_day))\n\n    def test_doi(self):\n        self.assertIn(\"Doi\", self.metadata)\n        doi = self.metadata[\"Doi\"]\n        self.assertRegex(doi, \"^\" + DOI_PATTERN + \"$\")\n\n    def test_zenodo_id(self):\n        self.assertIn(\"ZenodoId\", self.metadata)\n        zenodo_id = self.metadata[\"ZenodoId\"]\n        self.assertTrue(isinstance(zenodo_id, int))\n        zenodo_id_from_doi = re.match(DOI_PATTERN, self.metadata[\"Doi\"]).group(\n            1\n        )\n        self.assertEqual(\n            zenodo_id, int(zenodo_id_from_doi), \"Zenodo ID should match DOI\"\n        )\n\n    def test_description(self):\n        self.assertIn(\"Description\", self.metadata)\n        self.assertTrue(isinstance(self.metadata[\"Description\"], str))\n\n    def test_keywords(self):\n        self.assertIn(\"Keywords\", self.metadata)\n        keywords = self.metadata[\"Keywords\"]\n        self.assertTrue(isinstance(keywords, list))\n        self.assertTrue([isinstance(keyword, str) for keyword in keywords])\n\n    def test_affiliations(self):\n        self.assertIn(\"Affiliations\", self.metadata)\n        affiliations = self.metadata[\"Affiliations\"]\n        self.assertTrue(isinstance(affiliations, list))\n        self.assertTrue(\n            [isinstance(affiliation, str) for affiliation in affiliations]\n        )\n        # Check for duplicates\n        simplified_affiliations = [\n            simplify_name(affiliation) for affiliation in affiliations\n        ]\n        for affiliation in affiliations:\n            self.assertEqual(\n                simplified_affiliations.count(simplify_name(affiliation)),\n                1,\n                f\"Duplicate affiliation: {affiliation}\",\n            )\n\n    def test_authors(self):\n        self.assertIn(\"Authors\", self.metadata)\n        authors = self.metadata[\"Authors\"]\n        self.assertTrue(isinstance(authors[\"Description\"], str))\n        authors_core = authors[\"Core\"]\n        self.assertTrue(isinstance(authors_core[\"Description\"], str))\n        authors_core_list = authors_core[\"List\"]\n        self.assertTrue(isinstance(authors_core_list, list))\n        authors_devs = authors[\"Developers\"]\n        self.assertTrue(isinstance(authors_devs[\"Description\"], str))\n        authors_devs_list = authors_devs[\"List\"]\n        self.assertTrue(isinstance(authors_devs_list, list))\n        authors_contribs = authors[\"Contributors\"]\n        self.assertTrue(isinstance(authors_contribs[\"Description\"], str))\n        authors_contribs_list = authors_contribs[\"List\"]\n        self.assertTrue(isinstance(authors_contribs_list, list))\n        all_authors = (\n            authors_core_list + authors_devs_list + authors_contribs_list\n        )\n\n        # Check alphabetical ordering\n        self.assertEqual(\n            sorted(authors_devs_list, key=lambda a: a[\"Name\"]),\n            authors_devs_list,\n            (\n                \"'Developers' author list should be ordered alphabetically \"\n                \"by last name\"\n            ),\n        )\n        self.assertEqual(\n            sorted(authors_contribs_list, key=lambda a: a[\"Name\"]),\n            authors_contribs_list,\n            (\n                \"'Contributors' author list should be ordered alphabetically \"\n                \"by last name\"\n            ),\n        )\n\n        # Check all authors\n        def check_name(name):\n            split_name = name.split(\", \")\n            self.assertTrue(\n                len(split_name) == 2 or len(split_name) == 3,\n                (\n                    f\"Name '{name}' should be formatted \"\n                    \"'Last name[, Jr.], First name'\"\n                ),\n            )\n            if len(split_name) == 3:\n                last_name, jr, first_name = split_name\n                self.assertTrue(jr, f\"Empty 'Jr.' component in name: {name}\")\n            else:\n                last_name, first_name = split_name\n            self.assertTrue(last_name, f\"Missing last name: {name}\")\n            self.assertTrue(first_name, f\"Missing first name: {name}\")\n\n        all_author_names_simplified = [\n            simplify_name(author[\"Name\"]) for author in all_authors\n        ]\n        for author in all_authors:\n            self.assertIn(\"Name\", author)\n            author_name = author[\"Name\"]\n            check_name(author_name)\n            self.assertEqual(\n                all_author_names_simplified.count(simplify_name(author_name)),\n                1,\n                f\"Duplicate author: {author_name}\",\n            )\n            if \"Orcid\" in author:\n                self.assertRegex(\n                    author[\"Orcid\"],\n                    \"^\" + ORCID_PATTERN + \"$\",\n                    f\"Invalid ORCID for {author_name}\",\n                )\n            self.assertIn(\"Affiliations\", author)\n            for affiliation in author[\"Affiliations\"]:\n                self.assertIn(\n                    affiliation,\n                    self.metadata[\"Affiliations\"],\n                    (\n                        \"Please use an existing affiliation or add a new one to\"\n                        \" the main 'Affiliations' list in this file.\"\n                    ),\n                )\n            self.assertIn(\"GitHub\", author)\n\n        # Check for unused affiliations\n        all_authors_affiliations = set(\n            sum([author[\"Affiliations\"] for author in all_authors], [])\n        )\n        for affiliation in self.metadata[\"Affiliations\"]:\n            self.assertIn(\n                affiliation,\n                all_authors_affiliations,\n                f\"No author is affiliated with: {affiliation}\",\n            )\n\n    def test_references(self):\n        # Import the BibTeX to CFF conversion functions from the relase script.\n        # We should probably move the release script into the spectre Python\n        # package so we can share code between them better.\n        import sys\n\n        sys.path.append(os.path.join(self.repo.working_dir, \".github/scripts\"))\n        from Release import to_cff_reference, to_plaintext_reference\n\n        # Validate references in the metadata\n        reference_keys = self.metadata[\"References\"][\"List\"]\n        all_references = pybtex.database.parse_file(\n            os.path.join(\n                self.repo.working_dir,\n                self.metadata[\"References\"][\"BibliographyFile\"],\n            )\n        )\n        charmpp_ref = (\n            \"Laxmikant Kale, Bilge Acun, Seonmyeong Bak, Aaron Becker, Milind \"\n            \"Bhandarkar, Nitin Bhat, Abhinav Bhatele, Eric Bohm, Cyril \"\n            \"Bordage, Robert Brunner, Ronak Buch, Sayantan Chakravorty, \"\n            \"Kavitha Chandrasekar, Jaemin Choi, Michael Denardo, Jayant \"\n            \"DeSouza, Matthias Diener, Harshit Dokania, Isaac Dooley, Wayne \"\n            \"Fenton, Juan Galvez, Fillipo Gioachin, Abhishek Gupta, Gagan \"\n            \"Gupta, Manish Gupta, Attila Gursoy, Vipul Harsh, Fang Hu, Chao \"\n            \"Huang, Narain Jagathesan, Nikhil Jain, Pritish Jetley, Prateek \"\n            \"Jindal, Raghavendra Kanakagiri, Greg Koenig, Sanjeev Krishnan, \"\n            \"Sameer Kumar, David Kunzman, Michael Lang, Akhil Langer, Orion \"\n            \"Lawlor, Chee Wai Lee, Jonathan Lifflander, Karthik Mahesh, Celso \"\n            \"Mendes, Harshitha Menon, Chao Mei, Esteban Meneses, Eric Mikida, \"\n            \"Phil Miller, Ryan Mokos, Venkatasubrahmanian Narayanan, Xiang \"\n            \"Ni, Kevin Nomura, Sameer Paranjpye, Parthasarathy Ramachandran, \"\n            \"Balkrishna Ramkumar, Evan Ramos, Michael Robson, Neelam Saboo, \"\n            \"Vikram Saletore, Osman Sarood, Karthik Senthil, Nimish Shah, \"\n            \"Wennie Shu, Amitabh B. Sinha, Yanhua Sun, Zehra Sura, Ehsan \"\n            \"Totoni, Krishnan Varadarajan, Ramprasad Venkataraman, Jackie \"\n            \"Wang, Lukasz Wesolowski, Sam White, Terry Wilmarth, Jeff Wright, \"\n            \"Joshua Yelon, and Gengbin Zheng. The Charm++ Parallel \"\n            \"Programming System. Aug 2019. URL: \"\n            \"https://charm.cs.illinois.edu, doi:10.5281/zenodo.3370873.\"\n        )\n        for key in reference_keys:\n            # Check the reference exists in the bib file\n            entry = all_references.entries[key]\n            # Check the entry converts to CFF without error\n            to_cff_reference(entry)\n            # Check the entry formats to plaintext without error\n            ref_text = to_plaintext_reference(entry)\n            if key == \"charmpp\":\n                self.assertEqual(ref_text, charmpp_ref)\n\n    def test_documentation_bibliography_journal_names(self):\n        for bibliography_file in [\n            \"docs/References.bib\",\n            \"docs/Dependencies.bib\",\n        ]:\n            references = pybtex.database.parse_file(\n                os.path.join(self.repo.working_dir, bibliography_file)\n            )\n            journal_macros = [\n                f\"{key}: {entry.fields['journal']}\"\n                for key, entry in references.entries.items()\n                if \"journal\" in entry.fields\n                and entry.fields[\"journal\"].startswith(\"\\\\\")\n            ]\n            self.assertEqual(\n                journal_macros,\n                [],\n                (\n                    f\"Journal names in '{bibliography_file}' should be written\"\n                    \" out directly because Doxygen does not expand LaTeX\"\n                    f\" macros: {journal_macros}\"\n                ),\n            )\n\n\nif __name__ == \"__main__\":\n    unittest.main(verbosity=2)\n"
  },
  {
    "path": "tools/CheckOutputFiles.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport argparse\nimport glob\nimport logging\nimport os\nimport re\nimport subprocess\nimport unittest\n\nimport h5py\nimport numpy as np\nimport numpy.testing as npt\nimport yaml\n\n\ndef enumerate_h5_datasets(obj):\n    \"\"\"\n    Iterate through structure of an h5 file and yield all paths to dataset\n    \"\"\"\n    if type(obj) in [h5py.Group, h5py.File]:\n        for key in obj.keys():\n            yield from enumerate_h5_datasets(obj[key])\n    elif type(obj) == h5py.Dataset:\n        yield obj.name\n\n\nclass H5Check:\n    \"\"\"Describes a particular comparison between H5 datasets or groups\n\n    When passed a `unittest.testcase` object to the `perform_checks`,\n    the described checks are performed on the H5 files in the appointed run\n    directory\n\n    Attributes:\n        unit_test: The `unittest.testcase` object, used to invoke asserts\n        label: An identifier string for the test\n        file_glob: The shell glob matching the h5 files to test\n        subfile: The h5 path for the group or dataset to check. This is allowed\n          to be a python regex but only the first matching dataset is checked.\n        absolute_tolerance: The absolute tolerance for approximation checks\n        relative_tolerance: The relative tolerance for approximation checks\n        expected_data_subfile: The h5 path for the expected group or dataset\n        expected_data: The expected data to compare with directly. Mutually\n          exclusive with `expected_data_subfile`.\n        skip_columns: Which \"columns\" of the data to skip when checking. Here\n          column means the last index of a dataset of any shape. If the dataset\n          is rank 1, these are the indices to skip; if the dataset is rank 2,\n          with indices (row, column), then these are the columns to skip; etc.\n    \"\"\"\n\n    def __init__(\n        self,\n        unit_test,\n        label,\n        file_glob,\n        subfile,\n        absolute_tolerance,\n        relative_tolerance=None,\n        expected_data_subfile=None,\n        expected_data=None,\n        skip_columns=None,\n    ):\n        \"\"\"Initializer for H5Check\n\n        Note: the `unit_test` argument must be the unit test object -- this\n        class is to be constructed within a test case, and must have access\n        to the calling unit test object so that it can access the assert\n        and subTest member functions\n        \"\"\"\n        self.unit_test = unit_test\n        self.h5_glob = file_glob\n        self.test_h5_label = label\n        self.test_h5_entity = subfile\n        # The `expected` entity, if specified, is what the `test` entity is\n        # compared to. For example, one may wish to compare the L2Norm of the\n        # scalar field to an expected value from an analytic solution. If an\n        # `expected` entity is not specified then the `test` entity is compared\n        # to 0.0. This is what would be typically done for error measures, for\n        # example.\n        self.expected_h5_entity = expected_data_subfile\n        if expected_data:\n            assert (\n                self.expected_h5_entity is None\n            ), \"Set expected subfile or expected data, not both.\"\n        self.expected_data = expected_data\n        self.absolute_tolerance = float(absolute_tolerance)\n        self.relative_tolerance = (\n            0.0 if relative_tolerance is None else float(relative_tolerance)\n        )\n        self.skip_columns = [] if skip_columns is None else skip_columns\n\n    def check_h5_file(self, h5_file, test_entity, expected_entity):\n        \"\"\"Perform the unit test comparisons between the dataset or group\n\n        If `expected_entity` is `None`, then the `test_entity` is compared to\n        the `expected_data` or 0.0 instead.\n\n        Args:\n            h5_file: An h5 File object on which to perform checks\n            test_entity: The h5 path string to the test dataset or group\n            expected_entity: The h5 path to the expected dataset or group\n        \"\"\"\n\n        if isinstance(h5_file[test_entity], h5py.Dataset):\n            logging.info(\"Checking dataset : \" + test_entity)\n            with self.unit_test.subTest(\n                test_entity=test_entity, expected_entity=expected_entity\n            ):\n                test_data = h5_file[test_entity][()]\n                column_mask = [\n                    x not in self.skip_columns\n                    for x in range(test_data.shape[-1])\n                ]\n                if self.expected_h5_entity is not None:\n                    expected_data = h5_file[expected_entity][()]\n                    self.unit_test.assertEqual(\n                        test_data.shape,\n                        expected_data.shape,\n                        (\n                            \"test and expected h5 datasets must have identical\"\n                            \" sizes.\"\n                        ),\n                    )\n                    self.unit_test.assertEqual(\n                        test_data.dtype,\n                        expected_data.dtype,\n                        (\n                            \"test and expected h5 datasets must have identical\"\n                            \" types.\"\n                        ),\n                    )\n                    if test_data.dtype == float or test_data.dtype == complex:\n                        # numpy testing doesn't print the full array nor does it\n                        # print to full precision during a test, so we capture\n                        # the error and print the arrays to full precision\n                        try:\n                            npt.assert_allclose(\n                                test_data[..., column_mask],\n                                expected_data[..., column_mask],\n                                rtol=self.relative_tolerance,\n                                atol=self.absolute_tolerance,\n                            )\n                        except AssertionError as e:\n                            np.set_printoptions(precision=16)\n                            print(\n                                \"DESIRED:\"\n                                f\" {np.array(expected_data[..., column_mask])}\"\n                            )\n                            print(\n                                \"ACTUAL:\"\n                                f\" {np.array(test_data[..., column_mask])}\"\n                            )\n                            raise AssertionError(\n                                \"Test data is not equal to the expected data\"\n                            ) from e\n                    else:\n                        self.unit_test.assertEqual(test_data, expected_data)\n                else:\n                    if test_data.dtype == float or test_data.dtype == complex:\n                        # numpy testing doesn't print the full array nor does it\n                        # print to full precision during a test, so we capture\n                        # the error and print the arrays to full precision\n                        try:\n                            npt.assert_allclose(\n                                test_data[..., column_mask],\n                                self.expected_data or 0.0,\n                                rtol=self.relative_tolerance,\n                                atol=self.absolute_tolerance,\n                            )\n                        except AssertionError as e:\n                            np.set_printoptions(precision=16)\n                            print(f\"DESIRED: {np.array(self.expected_data)}\")\n                            if self.expected_data:\n                                print(\n                                    \"ACTUAL:\"\n                                    f\" {np.array(test_data[..., column_mask])}\"\n                                )\n                            else:\n                                print(\"ACTUAL: 0.0\")\n                            raise AssertionError(\n                                \"Test data is not equal to the expected data\"\n                            ) from e\n                    else:\n                        self.assertTrue(\n                            False,\n                            msg=(\n                                \"cannot test non-numeric data without an\"\n                                \" expected data set to compare against\"\n                            ),\n                        )\n        elif isinstance(h5_file[test_entity], h5py.Group):\n            test_keys = set(h5_file[self.test_entity].keys())\n            checks_passed = True\n            if self.expected_h5_entity is not None:\n                expected_keys = set(h5_file[self.expected_entity].keys())\n                keys_difference = test_keys ^ expected_keys\n                self.unit_test.assertEqual(\n                    keys_difference,\n                    {},\n                    \"test and expected h5 groups must have identical\"\n                    + \"subgroups and data members. Found differences: \"\n                    + str(keys_difference)\n                    + \"\\nin comparing group \"\n                    + test_entity\n                    + \" to group \"\n                    + expected_entity,\n                )\n                for key in test_keys:\n                    checks_passed = checks_passed and self.check_h5_file(\n                        h5_file,\n                        test_entity + \"/\" + key,\n                        expected_entity + \"/\" + key,\n                    )\n            else:\n                for key in test_keys:\n                    checks_passed = checks_passed and self.check_h5_file(\n                        h5_file, test_entity + \"/\" + key, None\n                    )\n            return checks_passed\n\n    def perform_check(self, run_directory):\n        \"\"\"Apply the h5 check to every h5 file within the `run_directory`\n        that matches the glob `self.h5_glob`.\n\n        If neither the expected nor test objects are present in an h5 file,\n        that file is skipped. However, if none of the matching h5 files have\n        the requested datasets, the test fails, under the assumption that\n        failure to produce any of the anticipated data should be regarded\n        as an error in the executable.\n        \"\"\"\n        found_test_entity = False\n        found_expected_entity = False\n        files_and_entities = \"\"\n        logging.info(\n            \"Performing checks: \" + os.path.join(run_directory, self.h5_glob)\n        )\n        test_h5_entity_pat = re.compile(self.test_h5_entity)\n        for filename in glob.glob(os.path.join(run_directory, self.h5_glob)):\n            logging.info(\"Checking file: \" + filename)\n            with self.unit_test.subTest(filename=filename):\n                with h5py.File(filename, \"r\") as check_h5:\n                    dataset_paths = enumerate_h5_datasets(check_h5)\n                    matched_paths = [\n                        path\n                        for path in dataset_paths\n                        if test_h5_entity_pat.match(path)\n                    ]\n\n                    if len(matched_paths) > 1:\n                        logging.warn(\n                            \"Found more than 1 subfile path matching pattern '\"\n                            + self.test_h5_entity\n                            + \"'. Going to use first match. The matches were:\\n\"\n                            + \"\\n\".join(matched_paths)\n                        )\n                        test_h5_entity = matched_paths[0]\n                    elif len(matched_paths) == 1:\n                        test_h5_entity = matched_paths[0]\n                    else:\n                        test_h5_entity = self.test_h5_entity\n\n                    files_and_entities = (\n                        files_and_entities\n                        + filename\n                        + \": \"\n                        + str(list(check_h5.keys()))\n                        + \"\\n\"\n                    )\n                    found_test_entity = found_test_entity or (\n                        test_h5_entity in check_h5\n                    )\n                    found_expected_entity = found_expected_entity or (\n                        self.expected_h5_entity is not None\n                        and self.expected_h5_entity in check_h5\n                    )\n\n                    if test_h5_entity in check_h5 or (\n                        self.expected_h5_entity is not None\n                        and self.expected_h5_entity in check_h5\n                    ):\n                        self.unit_test.assertTrue(test_h5_entity in check_h5)\n                        self.unit_test.assertTrue(\n                            self.expected_h5_entity is None\n                            or self.expected_h5_entity in check_h5\n                        )\n                        self.check_h5_file(\n                            check_h5,\n                            test_h5_entity,\n                            self.expected_h5_entity,\n                        )\n        self.unit_test.assertTrue(\n            found_test_entity,\n            \"Failed to find the subfile '\"\n            + self.test_h5_entity\n            + \"' using glob:\\n\"\n            + os.path.join(run_directory, self.h5_glob)\n            + \"\\nFiles and entities:\\n\"\n            + files_and_entities,\n        )\n        if self.expected_h5_entity is not None:\n            self.unit_test.assertTrue(\n                found_expected_entity,\n                \"Failed to find the expected entity/subfile (i.e. the data set \"\n                \"to which the test data is compared to) '\"\n                + self.expected_h5_entity\n                + \"' using glob:\\n\"\n                + os.path.join(run_directory, self.h5_glob)\n                + \"\\nFiles and entities:\\n\"\n                + files_and_entities,\n            )\n\n\nclass OutputCheckTestCase(unittest.TestCase):\n    \"\"\"The unit test object for performing output checks for a given input file\n\n    The parameters for the H5 output checks are determined by the\n    command-line arguments. The `--input-filename` flag specifies the yaml\n    input file. The `--run-directory` specifies the directory in which to find\n    the run's .h5 files.\n\n    The H5 checks and arguments are parsed from the `OutputFileChecks` in the\n    input file metadata:\n\n    ```yaml\n    OutputFileChecks:\n      - Label: \"label\"\n        Subfile: \"/h5_name.dat\"\n        FileGlob: \"VolumeData*.h5\"\n        AbsoluteTolerance: 1e-12\n      - Label: \"another_label\"\n        Subfile: \"/h5_group_name\"\n        FileGlob: \"ReductionData*.h5\"\n        ExpectedDataSubfile: \"/expected_h5_group_name\"\n        AbsoluteTolerance: 1e-11\n        RelativeTolerance: 1e-6\n        SkipColumns: [0, 1]\n    ```\n\n    Extra checking executables can be invoked from the `ExtraFileCheckExecs`\n    section in the input file metadata:\n\n    ```yaml\n    ExtraFileCheckExecs:\n      - Label: \"label1\"\n        Executable: \"check1.py\"\n        Arguments: [\"--additional\", \"--arguments\"]\n      - Label: \"label2\"\n        Executable: \"check2.py\"\n        Arguments:\n          - \"--other\"\n          - \"--arguments\"\n    ```\n    Each `Executable` will be invoked with `subprocess.run`, so it must be\n    executable (with a shebang if a it's e.g. a python script). It will receive\n    command line arguments corresponding to --input-filename, --run-directory,\n    --cmake-source-directory, --cmake-bin-directory, and the contents of\n    `Arguments`.\n    \"\"\"\n\n    def test_h5_output(self):\n        h5_check_list = []\n        with open(self.input_filename, \"r\") as open_input_file:\n            parsed_yaml = next(yaml.safe_load_all(open_input_file))\n        to_snake_case = re.compile(r\"(?!^)(?=[A-Z])\")\n        for check_block in parsed_yaml[\"OutputFileChecks\"]:\n            logging.info(\"Parsed File check : \" + check_block.get(\"Label\"))\n            h5_check_list.append(\n                H5Check(\n                    self,\n                    **{\n                        re.sub(to_snake_case, \"_\", key).lower(): value\n                        for key, value in check_block.items()\n                    },\n                )\n            )\n        for h5_check in h5_check_list:\n            with self.subTest(test=h5_check.test_h5_label):\n                h5_check.perform_check(self.run_directory)\n\n    def test_extra_execs(self):\n        extra_check_list = []\n        with open(self.input_filename, \"r\") as open_input_file:\n            parsed_yaml = next(yaml.safe_load_all(open_input_file))\n        if \"ExtraFileCheckExecs\" in parsed_yaml:\n            for check_block in parsed_yaml[\"ExtraFileCheckExecs\"]:\n                logging.info(\"Parsed extra check : \" + check_block[\"Label\"])\n                extra_check_list.append(check_block)\n        for extra_check in extra_check_list:\n            with self.subTest(test=extra_check[\"Label\"]):\n                dirname = os.path.dirname(self.input_filename)\n                cmdline = [\n                    os.path.join(dirname, extra_check[\"Executable\"]),\n                    \"--input-filename\",\n                    self.input_filename,\n                    \"--run-directory\",\n                    self.run_directory,\n                    \"--cmake-source-directory\",\n                    self.cmake_source_directory,\n                    \"--cmake-bin-directory\",\n                    self.cmake_bin_directory,\n                ]\n                if \"Arguments\" in extra_check:\n                    cmdline = cmdline + extra_check[\"Arguments\"]\n                run_result = subprocess.run(cmdline)\n                self.assertEqual(run_result.returncode, 0)\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\"--input-filename\")\n    parser.add_argument(\"--run-directory\")\n    parser.add_argument(\"--cmake-source-directory\")\n    parser.add_argument(\"--cmake-bin-directory\")\n    logging.basicConfig(level=logging.INFO)\n    duplicate_test_case, remaining_args = parser.parse_known_args(\n        namespace=OutputCheckTestCase\n    )\n    del duplicate_test_case\n    # Use of full command-line arguments breaks the unit-test framework\n    # (which needs to take its own command-line arguments), so we only pass\n    # on the remaining args after we've retrieved the ones used by\n    # `H5CheckTestCase`.\n    unittest.main(argv=[parser.prog] + remaining_args, verbosity=2)\n"
  },
  {
    "path": "tools/ClangTidyAll.sh",
    "content": "#!/usr/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nALL_SOURCE_FILES=`find \\\n                  @CMAKE_SOURCE_DIR@/src \\\n                  @CMAKE_SOURCE_DIR@/tests \\\n                  -name \"*.cpp\"`\n\nif [ ${#ALL_SOURCE_FILES[@]} == 0 ]; then\n   echo \"No C++ files found in repository\"\n   exit 0\nfi\n\nBUILD_DIR=@CMAKE_BINARY_DIR@\n\nif [ $# != 0 ] && [ $# != 1 ]; then\n    echo \"Wrong number of arguments passed to ClangTidyAll.sh, $#\"\n    echo \"Expecting none or just the number of threads to run on.\"\n    exit 1\nfi\n\nTHREADING_FLAG=\"-j 1\"\nif [ $# == 1 ]; then\n    THREADING_FLAG=\"-j $1\"\nfi\n\n# Try to find run-clang-tidy-LLVM_VERSION in the path.\nRUN_CLANG_TIDY_BIN=\nfor dir in $(echo ${PATH} | tr ':' '\\n');\ndo\n    if [ -d $dir ]; then\n        RUN_CLANG_TIDY_BIN=`find $dir -name run-clang-tidy* | head -n 1`\n        if [ ! -z \"${RUN_CLANG_TIDY_BIN}\" ]; then\n            break\n        fi\n    fi\ndone\n\nEXIT_CODE=0\n\n# Check for run-clang-tidy, then run-clang-tidy-LLVM_VERSION, and\n# if we can't find those, then just loop over the files one at a time.\nRAN_CLANG_TIDY=no\nfor run_clang_tidy in run-clang-tidy run-clang-tidy.py \"${RUN_CLANG_TIDY_BIN}\" ;\ndo\n    if command -v \"${run_clang_tidy}\" > /dev/null 2>&1; then\n        ${run_clang_tidy} -quiet ${THREADING_FLAG} -p ${BUILD_DIR} \\\n                          ${ALL_SOURCE_FILES} || EXIT_CODE=1\n        RAN_CLANG_TIDY=yes\n        break\n    fi\ndone\nif [ ${RAN_CLANG_TIDY} == no ]; then\n    for FILENAME in ${ALL_SOURCE_FILES};\n    do\n        printf \"\\nChecking file $FILENAME...\\n\"\n        make clang-tidy FILE=${FILENAME} || EXIT_CODE=1\n    done\nfi\n\nexit $EXIT_CODE\n"
  },
  {
    "path": "tools/ClangTidyHash.sh",
    "content": "#!/usr/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nif [ $# != 3 ] && [ $# != 4 ]; then\n    echo \"Wrong number of arguments passed to ClangTidyHash.sh, $#\"\n    echo \"Expecting BUILD_DIR SOURCE_DIR HASH\"\n    echo \"or BUILD_DIR SOURCE_DIR HASH NUM_THREADS_TO_RUN_ON\"\n    exit 1\nfi\n\nBUILD_DIR=$(realpath $1)\nSOURCE_DIR=$(realpath $2)\nHASH=$3\n\nTHREADING_FLAG=\"-j 1\"\nif [ $# == 4 ]; then\n    THREADING_FLAG=\"-j $4\"\nfi\n\n###############################################################################\ncd $SOURCE_DIR\n# Get list of non-deleted files\nMODIFIED_FILES=()\n\nfor FILENAME in `git diff --name-only ${HASH} HEAD`\ndo\n    if [ -f $FILENAME ] \\\n           && [ ${FILENAME: -4} == \".cpp\" ] \\\n           && ! grep -q \"FILE_IS_COMPILATION_TEST\" $FILENAME; then\n        MODIFIED_FILES+=(\"$FILENAME\")\n    fi\ndone\n\nif [ ${#MODIFIED_FILES[@]} == 0 ]; then\n   echo \"No C++ files to check with clang-tidy\"\n   exit 0\nfi\n\n# Go to build directory and run clang-tidy\ncd ${BUILD_DIR}\n\n# Try to find run-clang-tidy-LLVM_VERSION in the path.\nRUN_CLANG_TIDY_BIN=\nfor dir in $(echo ${PATH} | tr ':' '\\n');\ndo\n    if [ -d $dir ]; then\n        RUN_CLANG_TIDY_BIN=`find $dir -name run-clang-tidy* | head -n 1`\n        if [ ! -z \"${RUN_CLANG_TIDY_BIN}\" ]; then\n            break\n        fi\n    fi\ndone\n\nEXIT_CODE=0\n\n# Check for run-clang-tidy, then run-clang-tidy-LLVM_VERSION, and\n# if we can't find those, then just loop over the files one at a time.\nRAN_CLANG_TIDY=no\nfor run_clang_tidy in run-clang-tidy run-clang-tidy.py \"${RUN_CLANG_TIDY_BIN}\" ;\ndo\n    if command -v \"${run_clang_tidy}\" > /dev/null 2>&1; then\n        ${run_clang_tidy} -quiet ${THREADING_FLAG} -p ${BUILD_DIR} \\\n                          ${MODIFIED_FILES[@]} || EXIT_CODE=1\n        RAN_CLANG_TIDY=yes\n        break\n    fi\ndone\nif [ ${RAN_CLANG_TIDY} == no ]; then\n    for FILENAME in ${MODIFIED_FILES[@]};\n    do\n        printf \"\\nChecking file $FILENAME...\\n\"\n        make clang-tidy FILE=${SOURCE_DIR}/${FILENAME} || EXIT_CODE=1\n    done\nfi\n\nexit $EXIT_CODE\n"
  },
  {
    "path": "tools/CleanOutput.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport glob\nimport logging\nimport os\nimport shutil\n\nimport click\nimport yaml\n\n\nclass MissingExpectedOutputError(Exception):\n    def __init__(self, missing_files):\n        self.missing_files = missing_files\n\n    def __str__(self):\n        return \"Expected output files are missing: {}\".format(\n            self.missing_files\n        )\n\n\ndef clean_output(input_file, output_dir, force):\n    \"\"\"\n    Deletes output files specified in the `input_file` from the `output_dir`,\n    raising an error if the expected output files were not found.\n\n    The `input_file` must list its expected output files in the metadata.\n    They may contain glob patterns:\n\n    \\b\n    ```yaml\n    ExpectedOutput:\n      - Reduction.h5\n      - Volume*.h5\n    ```\n    \"\"\"\n    with open(input_file, \"r\") as open_input_file:\n        metadata = next(yaml.safe_load_all(open_input_file))\n\n    if not metadata or \"ExpectedOutput\" not in metadata:\n        logging.warning(\n            f\"Input file {input_file} does not list 'ExpectedOutput' files.\"\n        )\n        return\n\n    # Validate the user input. We have to be careful that we don't iterate over\n    # a string, which would yield each character in turn.\n    expected_output = metadata[\"ExpectedOutput\"]\n    assert not isinstance(expected_output, str), (\n        f\"'ExpectedOutput' in file '{input_file}' should be a list of files, \"\n        \"not a string.\"\n    )\n\n    missing_files = []\n    for expected_output_file in expected_output:\n        found_output_files = glob.glob(\n            os.path.join(output_dir, expected_output_file)\n        )\n        logging.debug(f\"Attempting to remove file {expected_output_file}...\")\n        if len(found_output_files) > 0:\n            for expected_output_file in found_output_files:\n                if os.path.isfile(expected_output_file):\n                    os.remove(expected_output_file)\n                else:\n                    shutil.rmtree(expected_output_file)\n                logging.info(f\"Removed file {expected_output_file}.\")\n        elif not force:\n            missing_files.append(expected_output_file)\n            logging.error(\n                f\"Expected file {expected_output_file} was not found.\"\n            )\n    # Raise an error if expected files were not found\n    if len(missing_files) > 0:\n        raise MissingExpectedOutputError(missing_files)\n\n\n@click.command(name=\"clean-output\", help=clean_output.__doc__)\n@click.argument(\n    \"input_file\",\n    type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True),\n)\n@click.option(\n    \"--output-dir\",\n    \"-o\",\n    type=click.Path(exists=True, file_okay=False, dir_okay=True, readable=True),\n    required=True,\n    help=\"Output directory of the run to clean up\",\n)\n@click.option(\"--force\", \"-f\", is_flag=True, help=\"Suppress all errors\")\ndef clean_output_command(**kwargs):\n    _rich_traceback_guard = True  # Hide traceback until here\n    clean_output(**kwargs)\n\n\nif __name__ == \"__main__\":\n    clean_output_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "tools/CompileReleaseNotes.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport itertools\nimport logging\nimport re\nfrom dataclasses import dataclass\nfrom typing import List, Optional, Union\n\nimport git\n\nlogger = logging.getLogger(__name__)\n\nRevision = Union[str, git.TagReference]\n\n\ndef get_last_release(\n    repo: git.Repo, head_rev: Revision = \"HEAD\"\n) -> Optional[git.TagReference]:\n    \"\"\"Retrieve the release closest to the `head_rev` in its git history\n\n    Returns:\n      The tag representing the latest release, as measured by number of commits\n      to the `head_rev`, or `None` if no release is in the history of\n      `head_rev`.\n    \"\"\"\n\n    def is_version_tag(tag):\n        return str(tag).startswith(\"v\")\n\n    def distance_from_head(tag):\n        return sum(\n            1\n            for _ in repo.iter_commits(\n                rev=f\"{tag}..{head_rev}\", first_parent=True\n            )\n        )\n\n    def is_in_history(tag):\n        return repo.is_ancestor(ancestor_rev=tag, rev=head_rev)\n\n    try:\n        return min(\n            filter(is_in_history, filter(is_version_tag, repo.tags)),\n            key=distance_from_head,\n        )\n    except ValueError:\n        return None\n\n\n@dataclass\nclass PullRequest:\n    id: int\n    title: str\n    author: str\n    url: Optional[str] = None\n    group: Optional[str] = None\n    upgrade_instructions: Optional[str] = None\n\n\n# The groups into which pull-requests are sorted.\n# - The keys in this mapping should correspond to labels on GitHub. The values\n#   are the group headers.\n# - The order of the groups is the order in which they will appear in the\n#   release notes (dicts are ordered in Python 3.7+).\nPULL_REQUEST_GROUPS = {\n    \"new feature\": \"New features\",\n    \"cli/pybindings\": \"CLI & Python bindings\",\n    None: \"General changes\",\n    \"documentation\": \"Documentation\",\n    \"bugfix\": \"Bugfixes\",\n    \"build system\": \"Build system\",\n    \"ci/cd\": \"Continuous integration & deployment\",\n}\n\n\ndef get_merged_pull_requests(\n    repo: git.Repo, from_rev: Revision, to_rev: Revision\n) -> List[PullRequest]:\n    \"\"\"Parses list of merged PRs from merge commits.\n\n    Parses merge commits in the repository between the revisions `from_rev` and\n    `to_rev`. This is faster than querying GitHub and we can filter by commit\n    SHAs instead of date.\n\n    Returns:\n      Merged pull-requests, ordered from least-recently merged to most-recently\n      merged.\n    \"\"\"\n    merge_commit_msg_pattern = \"^Merge pull request #([0-9]+) from (.+)/\"\n    merged_prs = []\n    for commit in repo.iter_commits(\n        rev=f\"{from_rev}..{to_rev}\", first_parent=True\n    ):\n        merge_commit_match = re.match(\n            merge_commit_msg_pattern, commit.message, flags=re.MULTILINE\n        )\n        if not merge_commit_match:\n            continue\n        merged_prs.append(\n            PullRequest(\n                id=int(merge_commit_match.group(1)),\n                title=\" \".join(commit.message.splitlines(False)[2:]),\n                author=merge_commit_match.group(2),\n            )\n        )\n    return merged_prs[::-1]\n\n\ndef get_upgrade_instructions(pr_description: str) -> Optional[str]:\n    \"\"\"Parse a section labeled \"Upgrade instructions\" from the PR description.\n\n    This function looks for a section in the PR description that is enclosed in\n    the HTML-comments `<!-- UPGRADE INSTRUCTIONS -->`. For example:\n\n    ```md\n    ### Upgrade instructions\n\n    <!-- UPGRADE INSTRUCTIONS -->\n    - Add the option `Evolution.InitialTime` to evolution input files. Set it\n      to the value `0.` to keep the behavior the same as before.\n    <!-- UPGRADE INSTRUCTIONS -->\n    ```\n    \"\"\"\n    if not pr_description:\n        return None\n    FENCE_PATTERN = \"<!-- UPGRADE INSTRUCTIONS -->\"\n    match = re.search(\n        FENCE_PATTERN + \"(.*)\" + FENCE_PATTERN, pr_description, flags=re.DOTALL\n    )\n    if match is None:\n        return None\n    match = match.group(1).strip()\n    if not match or match.isspace():\n        return None\n    return match\n\n\ndef compile_release_notes(merged_prs: List[PullRequest]) -> str:\n    \"\"\"Compile nicely-formatted release-notes.\n\n    Args:\n      merged_prs: The list of merged PRs since the last release, ordered from\n        least-recently merged to most-recently merged. The format of the\n        release notes depends on what information is available for the PRs.\n\n    Returns:\n      The Markdown-formatted release notes, ready to write to a file or print\n      to screen.\n    \"\"\"\n    # Sort PRs into their groups, ordered first by group then by the order they\n    # were merged\n    ordered_groups = list(PULL_REQUEST_GROUPS.keys())\n    grouped_prs = itertools.groupby(\n        sorted(\n            merged_prs,\n            key=lambda pr: (\n                ordered_groups.index(pr.group),\n                merged_prs.index(pr),\n            ),\n        ),\n        key=lambda pr: pr.group,\n    )\n\n    # Get set of distinct PR authors. Maintain the order by using a dict, which\n    # is guaranteed to maintain the insertion order since Py 3.7\n    pr_authors = list(dict.fromkeys([pr.author for pr in merged_prs]))\n\n    def format_pr_link(pr):\n        return f\"#{pr.id}\" if pr.url is None else f\"[#{pr.id}]({pr.url})\"\n\n    def format_list_of_prs(prs):\n        return [f\"- {pr.title} ({format_pr_link(pr)})\" for pr in prs]\n\n    release_notes_content = []\n\n    prs_with_upgrade_instructions = list(\n        filter(lambda pr: pr.upgrade_instructions, merged_prs)\n    )\n    if len(prs_with_upgrade_instructions) > 0:\n        release_notes_content += [\"## Upgrade instructions\", \"\"]\n        for pr in prs_with_upgrade_instructions:\n            release_notes_content += [\n                f\"**From {format_pr_link(pr)} ({pr.title}):**\",\n                \"\",\n                pr.upgrade_instructions,\n                \"\",\n            ]\n\n    release_notes_content += [\n        f\"## Merged pull-requests ({len(merged_prs)})\",\n        \"\",\n    ]\n    if len(merged_prs) > 0:\n        for group, prs_iterator in grouped_prs:\n            group_header = PULL_REQUEST_GROUPS[group]\n            prs = list(prs_iterator)\n            release_notes_content += (\n                [f\"**{group_header} ({len(prs)}):**\", \"\"]\n                + format_list_of_prs(prs)\n                + [\"\"]\n            )\n        release_notes_content += [\n            f\"Contributors ({len(pr_authors)}): \"\n            + \", \".join([f\"@{pr_author}\" for pr_author in pr_authors])\n        ] + [\"\"]\n    else:\n        release_notes_content += [\"_None_\", \"\"]\n\n    return \"\\n\".join(release_notes_content)\n\n\nif __name__ == \"__main__\":\n    # The release notes always refer to the repository that contains this file\n    repo = git.Repo(__file__, search_parent_directories=True)\n\n    import argparse\n\n    parser = argparse.ArgumentParser(\n        description=(\n            \"Compile release notes based on merged pull-requests. \"\n            f\"Repository: {repo.working_dir}.\"\n        ),\n        formatter_class=argparse.ArgumentDefaultsHelpFormatter,\n    )\n    parser.add_argument(\n        \"--output\",\n        \"-o\",\n        required=False,\n        help=(\n            \"Name of the output file, e.g. 'release_notes.md'. If you omit \"\n            \"this argument the Markdown-formatted output will be printed \"\n            \"to stdout. Hint: Pipe the output to a CLI Markdown-renderer to \"\n            \"improve readability, e.g. https://github.com/charmbracelet/glow.\"\n        ),\n    )\n    parser.add_argument(\n        \"--from\",\n        required=False,\n        dest=\"from_rev\",\n        help=(\n            \"Git revision that marks the last release, used as starting \"\n            \"point for the release notes. Can be any commit SHA or tag. \"\n            \"Defaults to the last release in the git history of \"\n            \"the '--to' revision.\"\n        ),\n    )\n    parser.add_argument(\n        \"--to\",\n        required=False,\n        dest=\"to_rev\",\n        default=\"HEAD\",\n        help=(\n            \"Git revision that marks this release. Can be any commit SHA \"\n            \"or tag. Defaults to 'HEAD'.\"\n        ),\n    )\n    parser.add_argument(\n        \"--github-repository\",\n        required=False,\n        default=\"sxs-collaboration/spectre\",\n        help=(\n            \"GitHub repository associated with pull-request IDs in merge \"\n            \"commits.\"\n        ),\n    )\n    parser_github_auth = parser.add_mutually_exclusive_group(required=False)\n    parser_github_auth.add_argument(\n        \"--github-token\",\n        required=False,\n        help=(\n            \"Access token for GitHub queries. Refer to the GitHub documentation\"\n            \" for instructions on creating a personal access token.\"\n        ),\n    )\n    parser_github_auth.add_argument(\n        \"--no-github\",\n        action=\"store_true\",\n        help=\"Disable GitHub queries, working only with the local repository.\",\n    )\n    parser_logging = parser.add_mutually_exclusive_group(required=False)\n    parser_logging.add_argument(\n        \"-v\",\n        \"--verbose\",\n        action=\"count\",\n        default=0,\n        help=\"Verbosity (-v, -vv, ...)\",\n    )\n    parser_logging.add_argument(\n        \"--silent\", action=\"store_true\", help=\"Disable any logging\"\n    )\n    args = parser.parse_args()\n\n    # Set the log level\n    logging.basicConfig(\n        level=(\n            logging.CRITICAL\n            if args.silent\n            else (logging.WARNING - args.verbose * 10)\n        )\n    )\n\n    # Default `from_rev` to last release\n    if args.from_rev is None:\n        args.from_rev = get_last_release(repo=repo, head_rev=args.to_rev)\n        logging.info(f\"Last release is: {args.from_rev}\")\n\n    # Retrieve list of merged PRs since last release\n    merged_prs = get_merged_pull_requests(\n        repo=repo, from_rev=args.from_rev, to_rev=args.to_rev\n    )\n    logger.info(\n        \"Merged PRs since last release:\\n{}\".format(\n            \"\\n\".join(map(str, merged_prs))\n        )\n    )\n\n    # Try to query GitHub for further information on the merged PRs\n    if not args.no_github:\n        import github\n\n        gh = github.Github(args.github_token)\n        gh_repo = gh.get_repo(args.github_repository)\n        if args.silent:\n            prs_iterator = iter(merged_prs)\n        else:\n            import tqdm\n\n            prs_iterator = tqdm.tqdm(\n                merged_prs, desc=\"Downloading PR data\", unit=\"PR\"\n            )\n        for pr in prs_iterator:\n            # First, download data\n            pr_gh = gh_repo.get_pull(pr.id)\n            # Update the title (it may have changed since the PR was merged)\n            pr.title = pr_gh.title\n            # Update the author (the GitHub username may have changed)\n            pr.author = pr_gh.user.login\n            # Add missing metadata to PR\n            pr.url = pr_gh.html_url\n            # Add group information to PR. Iterate over groups in reverse order\n            # so that PRs with both, say, \"bugfix\" and \"cli/pybindings\" labels\n            # are listed under \"bugfix\" rather than \"cli/pybindings\".\n            labels = [label.name for label in pr_gh.labels]\n            for group in reversed(PULL_REQUEST_GROUPS.keys()):\n                if group is None:\n                    continue\n                if group in labels:\n                    pr.group = group\n                    break\n            # Add upgrade instructions to PR\n            pr.upgrade_instructions = get_upgrade_instructions(pr_gh.body)\n\n    # Assemble everything into a nicely formatted string\n    release_notes_content = compile_release_notes(merged_prs=merged_prs)\n\n    # Output\n    if args.output:\n        with open(args.output, \"w\") as output_file:\n            output_file.write(release_notes_content)\n        logging.info(f\"Release notes written to file: '{args.output}'\")\n    else:\n        logging.info(\"Compiled release notes:\")\n        print(release_notes_content)\n"
  },
  {
    "path": "tools/FileTestDefs.sh",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# This file defines the framework for running simple textual checks on\n# files in the repo and defines a collection of standard checks.\n\n# A check C consists of three functions:\n# - `C`, which takes one filename as argument and should return true\n#   if there is a problem with the file, and\n# - `C_report`, which takes the list of bad files and should print a\n#   message about them, and\n# - `C_test`, which is used to test the check if the global $1 =\n#   --test.\n# The report function will not be called if there are no bad files.\n# `C_test` should consist of several calls to the test_check function.\n\n# Exit with a failure message\ndie() {\n    [ -n \"$1\" ] && echo \"$1\" >&2 || echo \"died\" >&2\n    exit 1\n}\n\n# Option to enable color in grep or the empty string if grep does not\n# support color\ncolor_option=''\nif grep --help 2>&1 | grep -q -e --color ; then\n  color_option='--color=always'\nfi\n\n# Utility that uses grep on the staged version of the file specified by the last\n# argument does not work with multiple files as argument\nstaged_grep() {\n    # git show \":./path/to/file\" shows the content of the file as it\n    # appears in the staging area\n    git show \":./${@: -1}\" | grep \"${@:1:$(($#-1))}\"\n}\n\n# Utility function for reporters that enables lots of decorators in\n# grep.  Works like staged_grep, except that it can take multiple file\n# arguments like real grep.  Accepts the same arguments as grep except\n# that any option taking an argument must use the --opt=arg form\n# instead of the short form.  Additionally, the order of options is\n# restricted as compared to real grep: only filename arguments are\n# allowed to follow the pattern.\npretty_grep() {\n    local -a non_file_args\n    local color file file_prefix\n    color=${color_option}\n    # This loop extracts all the flags and the pattern into\n    # non_file_args, leaving the filenames in $@.\n    while [ $# -gt 0 ] ; do\n        case \"$1\" in\n            # \"--\" indicates the end of command line switches, so the\n            # following argument is the pattern and the remainder are\n            # file names.\n            --)\n                non_file_args+=(\"$1\" \"$2\")\n                shift 2\n                break\n                ;;\n            --color=*)\n                color=$1\n                shift\n                ;;\n            -*)\n                non_file_args+=(\"$1\")\n                shift\n                ;;\n            *)\n                # This is the pattern\n                non_file_args+=(\"$1\")\n                shift\n                break\n                ;;\n        esac\n    done\n\n    for file in \"$@\" ; do\n        if [ -n \"${color}\" ] && [ \"${color}\" != \"--color=no\" ] ; then\n            printf -v file_prefix \"\\033[0;35m%s\\033[0m:\" \"${file}\"\n        else\n            file_prefix=${file}:\n        fi\n        git show \":./${file}\" | \\\n            GREP_COLOR='1;37;41' GREP_COLORS='mt=1;37;41' \\\n            grep -n $color \"${non_file_args[@]}\" | \\\n            sed \"s|^|${file_prefix}|\"\n    done\n}\n\n# Utility functions for checks classifying a file based on its name\nis_includible() { [[ $1 =~ \\.hpp$ ]] || [[ $1 =~ \\.tpp$ ]] ; }\nis_c++() { [[ $1 =~ \\.cpp$ ]] || [[ $1 =~ \\.hpp$ ]] || [[ $1 =~ \\.tpp$ ]] ; }\nis_c++_or_python() { [[ $1 =~ \\.cpp$ ]] || [[ $1 =~ \\.hpp$ ]] \\\n                         || [[ $1 =~ \\.tpp$ ]] || [[ $1 =~ \\.py$ ]] ; }\n\n# Utility function for checks that returns false if the first argument\n# matches any of the shell regexes passed as subsequent arguments.\nwhitelist() {\n    local check pattern\n    check=$1\n    shift\n    for pattern in \"$@\" ; do\n        [[ ${check} =~ ${pattern} ]] && return 1\n    done\n    return 0\n}\n\n# check_nolint [-q] lint file...\n#\n# Check for NOLINT comments in the supplied files matching the\n# supplied `lint` string escaping the lines from standard input.\n# Echos any non-escaped lines unless '-q' is passed as the first\n# argument.  Returns 1 if everything was NOLINTED, 0 otherwise.\n#\n# The lines to match must not contain color sequences, because they\n# are too hard to parse.  Pass ${color_option:+--color=no} to pretty_grep.\ncheck_nolint() {\n    local quiet=no\n    if [ \"$1\" = \"-q\" ] ; then\n        quiet=yes\n        shift\n    fi\n    [ $# -ge 2 ] || die \"Wrong number of arguments\"\n    perl -e '\nuse strict;\nmy ($quiet, $lint, @files) = @ARGV;\nmy $result = 1;\nwhile (my $match = <STDIN>) {\n  my $found = 0;\n  my ($match_file, $match_lineno, $match_text) =\n    $match =~ m#^([\\w/.]+):(\\d+):(.*)#;\n  $match_text //= $match;\n  chomp $match_text;\n  foreach my $file (@files) {\n    next if defined $match_file and $match_file ne $file;\n    open F, $file or die;\n    my $escaped = 0;\n    while(<F>) {\n      chomp;\n      m# NOLINT(NEXTLINE)?\\((?:|.*,)\\Q$lint\\E(?:|,.*)\\)# and\n        $escaped = $1 eq \"NEXTLINE\" ? 2 : 1;\n      if ((not defined $match_lineno or $match_lineno == $.) and\n           $_ eq $match_text) {\n        $found = 1;\n        if ($escaped != 1) {\n          $result = 0;\n          print $match unless $quiet eq \"yes\";\n        }\n      }\n      --$escaped if $escaped;\n    }\n    die \"Did not find line: $match\" unless $found;\n    close F;\n  }\n}\nexit $result;\n' \"${quiet}\" \"$@\"\n}\n\n# Main driver.  Takes a list of checks as arguments and a list of\n# filenames as null separated strings on its standard input.  Returns\n# true if all the checks passed.\nrun_checks() {\n    local check failures file files ret\n\n    ret=0\n\n    files=()\n    while IFS= read -d '' -r file ; do\n        files+=(\"${file}\")\n    done\n\n    for check in \"$@\" ; do\n        failures=()\n        for file in \"${files[@]}\" ; do\n            ${check} \"${file}\" && failures+=(\"${file}\")\n        done\n        if [ ${#failures[@]} -ne 0 ] ; then\n            ret=1\n            ${check}_report \"${failures[@]}\"\n            echo\n        fi\n    done\n\n    return \"${ret}\"\n}\n\n# test_check pass|fail filename contents\ntest_check() {\n    # check and failed are global variables\n    local contents expected file tempdir\n    [ $# -eq 3 ] || die \"Wrong number of arguments\"\n    expected=$1\n    file=$2\n    contents=$3\n    [ \"${expected}\" = pass ] || [ \"${expected}\" = fail ] || \\\n        die \"Expected pass or fail, got '${expected}'\"\n    [[ \"${file}\" =~ ^/ ]] && die \"Can't test with absolute path\"\n\n    # Delete the temporary directory if the script exits.  (This is\n    # reset before leaving this function.)\n    trap 'rm -rf \"${tempdir}\"' EXIT\n    tempdir=$(mktemp -d)\n    pushd \"${tempdir}\" >/dev/null\n    mkdir -p \"$(dirname \"${file}\")\"\n    printf '%s' \"${contents}\" >\"${file}\"\n    if ${check} \"${file}\" ; then\n        if [ \"${expected}\" != fail ] ; then\n            echo \"${check} unexpectedly failed on ${file}:\"\n            cat \"${file}\"\n            echo\n            failed=yes\n        fi\n    else\n        if [ \"${expected}\" != pass ] ; then\n            echo \"${check} unexpectedly passed on ${file}:\"\n            cat \"${file}\"\n            echo\n            failed=yes\n        fi\n    fi\n    rm -rf \"${tempdir}\"\n    popd >/dev/null\n    trap - EXIT\n    return 0\n}\n\n# Run the specified tests.  Automatically run on the standard_checks\n# if $1 is --test.\nrun_tests() {\n    local check failed\n    failed=no\n    for check in \"$@\" ; do\n        ${check}_test\n    done\n    [ \"${failed}\" != no ] && die \"Tests failed\"\n    return 0\n}\n\n\n###### Standard checks ######\nstandard_checks=()\n\n# Check for lines longer than 80 characters\n# Some patterns are allowed to exceed the line limit. Notes:\n# - Black Python formatting prefers to end single-line docstrings on the same\n#   line even if the '\"\"\"' exceeds the line limit.\n# - Black also prefers to avoid backslashes even if that means a long import\n#   statements exceeds the line limit (in case breaking the line with\n#   parentheseses is not possible).\nlong_lines_exclude() {\n    grep -Ev 'https?://' | \\\n        grep -v 'mailto:' | \\\n        grep -v '// NOLINT' | \\\n        grep -v '\\\\snippet' | \\\n        grep -v '\\\\image' | \\\n        grep -v 'a href=' | \\\n        grep -v '\"\"\"' | \\\n        grep -v 'import' | \\\n        grep -v '\\\\link' | \\\n        grep -v '\\\\endlink' | \\\n        grep -v 'CopyFiles:' | \\\n        grep -v '^allowed-tools:'\n}\nlong_lines() {\n    whitelist \"$1\" \\\n              '.claude/skills/*' \\\n              '.cmake$' \\\n              '.css$' \\\n              '.github/workflows/*' \\\n              '.h5$' \\\n              '.html$' \\\n              '.ipynb$' \\\n              '.js$' \\\n              '.json$' \\\n              '.min.js$' \\\n              '.mplstyle$' \\\n              '.patch' \\\n              '.serializations' \\\n              '.svg' \\\n              '.travis.yml$' \\\n              '.xml$' \\\n              '.xmf$' \\\n              'CITATION.cff' \\\n              'CMakeLists.txt$' \\\n              'Doxyfile.in$' \\\n              'containers/Dockerfile.buildenv$' \\\n              'containers/Dockerfile.travis$' \\\n              'docs/DevGuide/Travis.md' \\\n              'docs/MainSite/Main.md' \\\n              'docs/Tutorials/CCE.md' \\\n              'docs/Tutorials/ParallelExecutable/Tutorials.md' \\\n              'docs/config/MathJax.js' \\\n              'CircularOrbitCoeffs.cpp$' \\\n              'CircularOrbitConvertEffsource.cpp$' \\\n              'eos.*' \\\n              'RotatingStarId.dat$' \\\n              'RotatingStarId_Hybrid.dat$' \\\n              'external/*' \\\n              'support/Environments/setup/mbot_install.sh' \\\n              'tools/CheckFiles.sh$' \\\n              'tools/FileTestDefs.sh' && \\\n        staged_grep '^[^#].\\{80,\\}' \"$1\" | long_lines_exclude >/dev/null\n}\nlong_lines_report() {\n    echo \"Found lines over 80 characters:\"\n    pretty_grep '^[^#].\\{80,\\}' \"$@\" | long_lines_exclude\n}\nlong_lines_test() {\n    local ten=xxxxxxxxxx\n    local eighty=${ten}${ten}${ten}${ten}${ten}${ten}${ten}${ten}\n    test_check pass foo.cpp \"${eighty}\"$'\\n'\n    test_check fail foo.cpp \"${eighty}x\"$'\\n'\n    test_check fail foo.hpp \"${eighty}x\"$'\\n'\n    test_check fail foo.tpp \"${eighty}x\"$'\\n'\n    test_check fail foo.yaml \"${eighty}x\"$'\\n'\n    test_check pass foo.cmake \"${eighty}x\"$'\\n'\n    test_check pass foo.cpp \"#include ${eighty}x\"$'\\n'\n    test_check pass foo.cpp \"xxx http://${eighty}x\"$'\\n'\n    test_check pass foo.cpp \"xxx https://${eighty}x\"$'\\n'\n    test_check pass foo.cpp \"linted;  // NOLINT(${eighty})\"$'\\n'\n    test_check pass foo.cpp \"// NOLINTNEXTLINE(${eighty})\"$'\\n'\n    test_check pass foo.cpp \"// \\\\snippet ${eighty}\"$'\\n'\n    test_check pass foo.cpp \"// \\\\image ${eighty}\"$'\\n'\n    test_check pass foo.cpp \"// \\\\link ${eighty}\"$'\\n'\n    test_check pass foo.cpp \"// \\\\endlink ${eighty}\"$'\\n'\n}\nstandard_checks+=(long_lines)\n\n# Check for lrtslock header\nlrtslock() {\n    is_c++ \"$1\" && staged_grep -q '#include <lrtslock.h>' \"$1\"\n}\nlrtslock_report() {\n    echo \"Found lrtslock header (include converse.h instead):\"\n    pretty_grep '#include <lrtslock.h>' \"$@\"\n}\nlrtslock_test() {\n    test_check pass foo.cpp '#include <vector>'$'\\n'\n    test_check fail foo.cpp '#include <lrtslock.h>'$'\\n'\n}\nstandard_checks+=(lrtslock)\n\n# Check for boost::optional headers (we favor std::optional)\n# (see related check for boost/none header below)\nboost_optional() {\n    is_c++ \"$1\" && staged_grep -q '#include <boost/optional.*>' \"$1\"\n}\nboost_optional_report() {\n    echo \"Found boost::optional header (use std::optional instead):\"\n    pretty_grep '#include <boost/optional.*>' \"$@\"\n}\nboost_optional_test() {\n    test_check pass foo.cpp '#include <optional>'$'\\n'\n    test_check pass foo.cpp '#include <boost/none.hpp>'$'\\n'\n    test_check fail foo.cpp '#include <boost/optional.hpp>'$'\\n'\n    test_check fail foo.cpp '#include <boost/optional/optional_io.hpp>'$'\\n'\n}\nstandard_checks+=(boost_optional)\n\n# Check for boost/none header\n# (see related check for boost/optional* headers above)\nboost_none() {\n    is_c++ \"$1\" && staged_grep -q '#include <boost/none.hpp>' \"$1\"\n}\nboost_none_report() {\n    echo \"Found boost/none.hpp header:\"\n    pretty_grep '#include <boost/none.hpp>' \"$@\"\n}\nboost_none_test() {\n    test_check pass foo.cpp '#include <optional>'$'\\n'\n    test_check pass foo.cpp '#include <boost/optional.hpp>'$'\\n'\n    test_check fail foo.cpp '#include <boost/none.hpp>'$'\\n'\n}\nstandard_checks+=(boost_none)\n\n# Check for files containing tabs\ntabs() {\n    whitelist \"$1\" '.h5' '.ico' '.jpg' '.png' '.patch' 'external/*' &&\n    staged_grep -q -F $'\\t' \"$1\"\n}\ntabs_report() {\n    echo \"Found tabs in the following files:\"\n    pretty_grep -F $'\\t' \"$@\"\n}\ntabs_test() {\n    test_check pass foo.cpp \"x x\"$'\\n'\n    test_check fail foo.cpp x$'\\t'x$'\\n'\n}\nstandard_checks+=(tabs)\n\n# Check for end-of-line spaces\ntrailing_space() {\n    whitelist \"$1\" '.h5' '.ico' '.jpg' '.png' '.patch' 'eos.*' 'external/*' &&\n    staged_grep -q -E ' +$' \"$1\"\n}\ntrailing_space_report() {\n    echo \"Found white space at end of line in the following files:\"\n    pretty_grep -E ' +$' \"$@\"\n}\ntrailing_space_test() {\n    test_check pass foo.cpp ' x'$'\\n'\n    test_check fail foo.cpp 'x '$'\\n'\n}\nstandard_checks+=(trailing_space)\n\n# Check for carriage returns\ncarriage_returns() {\n    whitelist \"$1\" '.h5' '.ico' '.jpg' '.png' &&\n    staged_grep -q -F $'\\r' \"$1\"\n}\ncarriage_returns_report() {\n    echo \"Found carriage returns in the following files:\"\n    # Skip highlighting because trying to highlight a carriage return\n    # confuses some terminals.\n    pretty_grep ${color_option:+--color=no} -F $'\\r' \"$@\"\n}\ncarriage_returns_test() {\n    test_check pass foo.cpp 'x'\n    test_check fail foo.cpp $'\\r'\n}\nstandard_checks+=(carriage_returns)\n\n# Check for license file.\nlicense() {\n    whitelist \"$1\" \\\n              '.clang-format$' \\\n              '.clang-tidy$' \\\n              '.github/ISSUE_TEMPLATE.md' \\\n              '.github/PULL_REQUEST_TEMPLATE.md' \\\n              '.h5' \\\n              '.ico' \\\n              '.jpg' \\\n              '.json' \\\n              '.nojekyll' \\\n              '.png' \\\n              '.serializations' \\\n              '.svg' \\\n              '.patch' \\\n              '.xmf' \\\n              'LICENSE' \\\n              'citation.bib' \\\n              'cmake/CodeCoverage.cmake$' \\\n              'cmake/CodeCoverageDetection.cmake$' \\\n              'cmake/FindCatch.cmake$' \\\n              'cmake/FindLIBCXX.cmake$' \\\n              'cmake/FindPAPI.cmake$' \\\n              'cmake/FindPythonModule.cmake$' \\\n              'docs/config/doxygen-awesome-sidebar-only.css' \\\n              'docs/config/doxygen-awesome.css' \\\n              'docs/config/doxygen-awesome-fragment-copy-button.js' \\\n              'docs/config/doxygen-awesome-interactive-toc.js' \\\n              'docs/config/doxygen-awesome-paragraph-link.js' \\\n              'docs/config/footer.html' \\\n              'docs/config/header.html' \\\n              'docs/config/DoxygenLayout_1_8_10.xml' \\\n              'docs/config/DoxygenLayout_1_9_8.xml' \\\n              'docs/config/MathJax.js$' \\\n              'external/*' \\\n              'eos.*' \\\n              'RotatingStarId.dat$' \\\n              'RotatingStarId_Hybrid.dat$' \\\n              'support/Profiling/ScoreP/spectre.flt' \\\n              'support/TeXLive/texlive.profile' \\\n              'tests/InputFiles/GrMhd/GhValenciaDivClean/EvolutionParameters.perl' \\\n              '\\.skills/.*\\.md$' \\\n              '\\.codex/.*\\.md$' \\\n              '\\.agents/.*\\.md$' \\\n              '\\.claude/.*\\.md$' && \\\n        ! staged_grep -q \"Distributed under the MIT License\" \"$1\"\n}\nlicense_report() {\n    echo \"Did not find a license in these files:\"\n    printf '%s\\n' \"$@\"\n}\nlicense_test() {\n    test_check pass foo.cpp 'XXDistributed under the MIT LicenseXX'\n    test_check fail foo.cpp ''\n    test_check pass LICENSE ''\n}\nstandard_checks+=(license)\n\n# Check for tests using Catch's TEST_CASE instead of SPECTRE_TEST_CASE\ntest_case() {\n    is_c++ \"$1\" && staged_grep -q \"^TEST_CASE\" \"$1\"\n}\ntest_case_report() {\n    echo \"Found occurrences of TEST_CASE, must use SPECTRE_TEST_CASE:\"\n    pretty_grep \"^TEST_CASE\" \"$@\"\n}\ntest_case_test() {\n    test_check pass foo.cpp ''\n    test_check pass foo.cpp 'SPECTRE_TEST_CASE()'\n    test_check fail foo.cpp 'TEST_CASE()'\n    test_check pass foo.yaml 'TEST_CASE()'\n}\nstandard_checks+=(test_case)\n\n# Check for tests using Catch's Approx, which has a very loose tolerance\ncatch_approx() {\n    is_c++ \"$1\" && staged_grep -q \"Approx(\" \"$1\"\n}\ncatch_approx_report() {\n    echo \"Found occurrences of Approx, must use approx from\"\n    echo \"tests/Unit/Framework/TestingFramework.hpp instead:\"\n    pretty_grep \"Approx(\" \"$@\"\n}\ncatch_approx_test() {\n    test_check pass foo.cpp ''\n    test_check pass foo.cpp 'a == approx(b)'\n    test_check fail foo.cpp 'a == Approx(b)'\n    test_check pass foo.yaml 'a == Approx(b)'\n}\nstandard_checks+=(catch_approx)\n\n# Check for Doxygen comments on the same line as a /*!\ndoxygen_start_line() {\n    is_c++ \"$1\" && staged_grep -q '/\\*![^\\n]' \"$1\"\n}\ndoxygen_start_line_report() {\n    echo \"Found occurrences of bad Doxygen syntax: /*! STUFF:\"\n    pretty_grep -E '/\\*!.*' \"$@\"\n}\ndoxygen_start_line_test() {\n    test_check pass foo.cpp ''\n    test_check pass foo.cpp '  /*!'$'\\n'\n    test_check fail foo.cpp '  /*! '$'\\n'\n    test_check pass foo.yaml '  /*! '$'\\n'\n}\nstandard_checks+=(doxygen_start_line)\n\n# Check for Ls because of a preference not to use it as short form for List\nls_list() {\n    is_c++ \"$1\" && staged_grep -q Ls \"$1\"\n}\nls_list_report() {\n    echo \"Found occurrences of 'Ls', which is usually short for List:\"\n    pretty_grep Ls \"$@\"\n}\nls_list_test() {\n    test_check pass foo.cpp ''\n    test_check pass foo.cpp ' FooList '\n    test_check fail foo.cpp ' FooLs '\n    test_check pass foo.yaml ' FooLs '\n}\nstandard_checks+=(ls_list)\n\n# Check for pragma once in all header files\npragma_once() {\n    is_includible \"$1\" && \\\n        whitelist \"$1\" \\\n                  'tools/SpectrePch.hpp$' \\\n                  'tools/BlazeExceptions.hpp$' && \\\n        ! staged_grep -q -x '#pragma once' \"$1\"\n}\npragma_once_report() {\n    echo \"Did not find '#pragma once' in these header files:\"\n    printf '%s\\n' \"$@\"\n}\npragma_once_test() {\n    test_check pass foo.cpp ''\n    test_check fail foo.hpp ''\n    test_check fail foo.tpp ''\n    test_check pass foo.hpp '#pragma once'$'\\n'\n    test_check fail foo.hpp '//#pragma once'$'\\n'\n    test_check pass foo.hpp $'\\n''#pragma once'$'\\n\\n'\n}\nstandard_checks+=(pragma_once)\n\n# Check for 'return Py_None;' in all C++ files\npy_return_none() {\n    is_c++ \"$1\" && \\\n        whitelist \"$1\" && \\\n        staged_grep -q -x '.*return Py_None;.*' \"$1\"\n}\npy_return_none_report() {\n    echo \"Found 'return Py_None;' in files. Use Py_RETURN_NONE instead.\"\n    pretty_grep \".*return Py_None;.*\" \"$@\"\n}\npy_return_none_test() {\n    test_check pass foo.cpp ''\n    test_check pass foo.hpp ''\n    test_check pass foo.tpp ''\n    test_check fail foo.hpp '  return Py_None;'$'\\n'\n    test_check fail foo.cpp '  return Py_None;'$'\\n'\n    test_check fail foo.tpp '  return Py_None;'$'\\n'\n    test_check fail foo.hpp '//return Py_None;'$'\\n'\n    test_check fail foo.cpp '//return Py_None;'$'\\n'\n    test_check fail foo.tpp '//return Py_None;'$'\\n'\n    test_check fail foo.hpp '  return Py_None; '$'\\n'\n    test_check fail foo.cpp '  return Py_None; '$'\\n'\n    test_check fail foo.tpp '  return Py_None; '$'\\n'\n    test_check pass foo.hpp '//return Py_None'$'\\n'\n    test_check pass foo.cpp '//return Py_None'$'\\n'\n    test_check pass foo.tpp '//return Py_None'$'\\n'\n}\nstandard_checks+=(py_return_none)\n\n# Check for a newline at end of file\nfinal_newline() {\n    whitelist \"$1\" '.h5' '.ico' '.jpg' '.nojekyll' '.png' '.svg' &&\n    # Bash strips trailing newlines from $() output\n    [ \"$(tail -c 1 \"$1\" ; echo x)\" != $'\\n'x ]\n}\nfinal_newline_report() {\n    echo \"No newline at end of file in:\"\n    printf '%s\\n' \"$@\"\n}\nfinal_newline_test() {\n    test_check pass foo.cpp $'\\n'\n    test_check fail foo.cpp ''\n    test_check fail foo.cpp $'\\n'x\n}\nstandard_checks+=(final_newline)\n\n# Check for enable_if and request replacing it with Requires\nenable_if() {\n    is_c++ \"$1\" && \\\n        whitelist \"$1\" \\\n                  'src/DataStructures/Tensor/Structure.hpp$' \\\n                  'src/Evolution/Systems/Cce/WorldtubeBufferUpdater.hpp$'\\\n                  'src/Evolution/Systems/Cce/WorldtubeBufferUpdater.cpp$'\\\n                  'src/IO/H5/File.hpp$' \\\n                  'src/Utilities/Requires.hpp$' \\\n                  'src/Utilities/TMPL.hpp$' \\\n                  'src/DataStructures/TaggedTuple.hpp$' \\\n                  'tests/Unit/Utilities/Test_TypeTraits.cpp$' && \\\n        staged_grep -q std::enable_if \"$1\"\n}\nenable_if_report() {\n    echo \"Found occurrences of 'std::enable_if', prefer 'Requires':\"\n    pretty_grep std::enable_if \"$@\"\n}\nenable_if_test() {\n    test_check pass foo.cpp 'enable'\n    test_check pass foo.cpp 'enable if'\n    test_check pass foo.cpp 'enable_if'\n    test_check fail foo.cpp 'std::enable_if'\n}\nstandard_checks+=(enable_if)\n\n# Check for mutable\nmutable() {\n    # We talk about mutable stuff a lot, so try to avoid flagging\n    # occurrences in comments and strings.  Checking for end-of-line\n    # comments and balanced quotes isn't worth it.  \"mutable\" is also\n    # fine for mutable lambdas, but those are rare and hard to detect\n    # with regex, so they can just be NOLINTed.\n    is_c++ \"$1\" && \\\n        staged_grep ' mutable ' \"$1\" | \\\n            check_nolint spectre-mutable \"$1\" | \\\n            grep -v -E -q '^ *(//|\\* )|\".* mutable '\n}\nmutable_report() {\n    echo \"Found occurrences of 'mutable'.  Please discuss with the core devs.\"\n    pretty_grep ${color_option:+--color=no} ' mutable ' \"$@\" | \\\n        check_nolint spectre-mutable \"$@\" | \\\n        grep -v -E '^([^:]*:){2} *(//|\\* )|\".* mutable '\n}\nmutable_test() {\n    test_check pass foo.cpp ''\n    test_check pass foo.hpp ''\n    test_check pass foo.tpp ''\n    test_check fail foo.hpp ' mutable '\n    test_check fail foo.cpp ' mutable '\n    test_check fail foo.tpp ' mutable '\n    test_check pass foo.hpp ' mutable {  // NOLINT(spectre-mutable)'\n    test_check pass foo.cpp ' mutable {  // NOLINT(spectre-mutable)'\n    test_check pass foo.tpp ' mutable {  // NOLINT(spectre-mutable)'\n    test_check fail foo.cpp ' mutable {  // NOLINT(notspectre-mutable)'\n    test_check pass foo.cpp \\\n               '// NOLINTNEXTLINE(spectre-mutable)'$'\\n'' mutable {'\n    test_check pass foo.cpp '// mutable '\n    test_check pass foo.cpp ' * mutable ' # Probably a multiline comment\n    test_check pass foo.cpp '  // mutable '\n    test_check pass foo.cpp ' \"something mutable something\" '\n    test_check fail foo.cpp ' mutable // mutable '\n}\nstandard_checks+=(mutable)\n\n# Check for struct TD and class TD asking to remove it\nstruct_td() {\n    is_c++ \"$1\" && staged_grep -q \"\\(struct TD;\\|class TD;\\)\" \"$1\"\n}\nstruct_td_report() {\n    echo \"Found 'struct TD;' or 'class TD;' which should be removed\"\n    pretty_grep \"\\(struct TD;\\|class TD;\\)\" \"$@\"\n}\nstruct_td_test() {\n    test_check pass foo.cpp ''\n    test_check fail foo.cpp 'struct TD;'\n    test_check fail foo.cpp 'class TD;'\n}\nstandard_checks+=(struct_td)\n\n# Check for _details and details namespaces, request replacement with detail\nnamespace_details() {\n    is_c++ \"$1\" && \\\n        staged_grep -q \"\\(_details\\|namespace[[:space:]]\\+details\\)\" \"$1\"\n}\nnamespace_details_report() {\n    echo \"Found '_details' namespace, please replace with '_detail'\"\n    pretty_grep \"\\(_details\\|namespace details\\)\" \"$@\"\n}\nnamespace_details_test() {\n    test_check pass foo.cpp ''\n    test_check fail foo.cpp 'namespace details'\n    test_check fail foo.cpp 'namespace    details'\n    test_check fail foo.cpp 'namespace Test_details'\n    test_check pass foo.cpp 'namespace Test_detail'\n    test_check pass foo.cpp 'namespace detail'\n    test_check pass foo.cpp 'details'\n}\nstandard_checks+=(namespace_details)\n\n# Check for .cpp includes in cpp, hpp, and tpp files\nprevent_cpp_includes() {\n    is_c++ \"$1\" && staged_grep -q \"#include .*\\.cpp\" \"$1\"\n}\nprevent_cpp_includes_report() {\n    echo \"Found cpp files included in cpp, hpp. or tpp files.\"\n    pretty_grep \"#include .*\\.cpp\" \"$@\"\n}\nprevent_cpp_includes_test() {\n    test_check pass foo.hpp ''\n    test_check pass foo.tpp ''\n    test_check fail foo.hpp '#include \"blah/blue/bla.cpp\"'\n    test_check fail foo.tpp '#include \"blah/blue/bla.cpp\"'\n    test_check fail foo.cpp '#include \"blah/blue/bla.cpp\"'\n    test_check pass foo.hpp '#include \"blah/blue/bla.hpp\"'\n    test_check pass foo.tpp '#include \"blah/blue/bla.hpp\"'\n    test_check pass foo.cpp '#include \"blah/blue/bla.hpp\"'\n    test_check pass foo.hpp '#include \"blah/blue/bla.tpp\"'\n    test_check pass foo.tpp '#include \"blah/blue/bla.tpp\"'\n    test_check pass foo.cpp '#include \"blah/blue/bla.tpp\"'\n    test_check pass foo.hpp 'include \"blah/blue/bla.cpp\"'\n}\nstandard_checks+=(prevent_cpp_includes)\n\n# Check for Doxygen-comments in cpp files. We don't parse cpp files with\n# Doxygen, so they shouldn't contain Doxygen-formatting.\nprevent_cpp_doxygen() {\n    [[ $1 =~ \\.cpp$ ]] && staged_grep -q '/// \\|/\\*!' \"$1\"\n}\nprevent_cpp_doxygen_report() {\n    echo \"Found Doxygen-formatting in cpp file:\"\n    pretty_grep '/// \\|/\\*!' \"$@\"\n    echo \"Doxygen-formatting only has an effect in header files.\"\n    echo \"Use standard C++ comments in cpp files.\"\n}\nprevent_cpp_doxygen_test() {\n    test_check fail foo.cpp '/// Dox.'$'\\n'\n    test_check fail foo.cpp '/*! Dox.'$'\\n'\n    test_check fail foo.cpp '/*!'$'\\n'' * Dox.'$'\\n'\n    test_check pass foo.cpp '// Dox.'$'\\n'\n    test_check pass foo.cpp '/* Dox.'$'\\n'\n    test_check pass foo.cpp '/*'$'\\n'' * Dox.'$'\\n'\n}\nstandard_checks+=(prevent_cpp_doxygen)\n\n# Check for CMakeLists.txt hardcoding library names\ncmakelists_hardcoded_libraries_command_list() {\n    printf '%s' add_dependencies\n    printf '|%s' add_test_library spectre_python_add_dependencies \\\n           spectre_python_link_libraries spectre_target_headers \\\n           spectre_target_sources target_link_libraries target_link_options\n}\ncmakelists_hardcoded_libraries() {\n    local CHECKED_COMMANDS=$(cmakelists_hardcoded_libraries_command_list)\n    [[ $1 =~ CMakeLists\\.txt$ ]] && \\\n        whitelist \"$1\" \\\n                  \"tests/Unit/Executables/CMakeLists.txt$\" \\\n                  \"tests/Unit/Parallel/CMakeLists.txt$\" \\\n                  \"tests/Unit/ParallelAlgorithms/LinearSolver/\\\nActions/CMakeLists.txt$\" \\\n                  \"tests/Unit/ParallelAlgorithms/LinearSolver/\\\nConjugateGradient/CMakeLists.txt$\" \\\n                  \"tests/Unit/ParallelAlgorithms/LinearSolver/Gmres/\\\nCMakeLists.txt$\" \\\n                  \"tests/Unit/ParallelAlgorithms/LinearSolver/Multigrid/\\\nCMakeLists.txt$\" \\\n                  \"tests/Unit/ParallelAlgorithms/LinearSolver/Richardson/\\\nCMakeLists.txt$\" \\\n                  \"tests/Unit/ParallelAlgorithms/LinearSolver/Schwarz/\\\nCMakeLists.txt$\" \\\n                  \"tests/Unit/ParallelAlgorithms/NonlinearSolver/NewtonRaphson/\\\nCMakeLists.txt$\" && \\\n        staged_grep -E -A1 \"(${CHECKED_COMMANDS})\\(\\$\" \"$1\" | \\\n            grep -Ev -- \"--|${CHECKED_COMMANDS}\" | \\\n            grep -qvF '${'\n}\ncmakelists_hardcoded_libraries_report() {\n    local CHECKED_COMMANDS=$(cmakelists_hardcoded_libraries_command_list)\n    echo \"Found CMakeLists.txt files using hardcoded library names.\"\n    echo \"Use a CMake variable to ensure the library name is consistent\" \\\n         \"within the file.\"\n    pretty_grep -E -A1 \"(${CHECKED_COMMANDS})\\(\\$\" \"$@\" | \\\n        grep -Ev -- \"--|${CHECKED_COMMANDS}\" | \\\n        grep -vF '${'\n}\ncmakelists_hardcoded_libraries_test() {\n    test_check pass foo.hpp 'add_dependencies('$'\\n''  Name'$'\\n'\n    test_check fail CMakeLists.txt 'add_dependencies('$'\\n''  Name'$'\\n'\n    test_check pass CMakeLists.txt 'add_dependencies('$'\\n''  ${LIBRARY}'$'\\n'\n    test_check pass foo.hpp 'spectre_target_sources('$'\\n''  Name'$'\\n'\n    test_check fail CMakeLists.txt 'spectre_target_sources('$'\\n''  Name'$'\\n'\n    test_check pass CMakeLists.txt \\\n               'spectre_target_sources('$'\\n''  ${LIBRARY}'$'\\n'\n    test_check pass foo.hpp 'target_link_options('$'\\n''  Name'$'\\n'\n    test_check fail CMakeLists.txt 'target_link_options('$'\\n''  Name'$'\\n'\n    test_check pass CMakeLists.txt \\\n               'target_link_options('$'\\n''  ${LIBRARY}'$'\\n'\n}\nstandard_checks+=(cmakelists_hardcoded_libraries)\n\n# Check for {CHECK,REQUIRE}_THROWS\ncheck_throws() {\n    is_c++ \"$1\" && staged_grep -q -E \"(CHECK|REQUIRE)_THROWS[^_]\" \"$1\"\n}\ncheck_throws_report() {\n    echo \"Found use of {CHECK,REQUIRE}_THROWS.\"\n    echo \"Use one of the more specific macros, such as CHECK_THROWS_WITH.\"\n    pretty_grep -E \"(CHECK|REQUIRE)_THROWS[^_]\" \"$@\"\n}\ncheck_throws_test() {\n    test_check pass foo.hpp 'CHECK'\n    test_check fail foo.hpp 'CHECK_THROWS(foo)'\n    test_check pass foo.hpp 'CHECK_THROWS_WITH(foo, bar)'\n    test_check pass foo.txt 'CHECK_THROWS(foo)'\n    test_check pass foo.hpp 'REQUIRE'\n    test_check fail foo.hpp 'REQUIRE_THROWS(foo)'\n    test_check pass foo.hpp 'REQUIRE_THROWS_WITH(foo, bar)'\n    test_check pass foo.txt 'REQUIRE_THROWS(foo)'\n}\nstandard_checks+=(check_throws)\n\n# Check for typos similar to Doxygen groups\ncheck_dox_groups() {\n    is_c++ \"$1\" && staged_grep -E \"^\\s*//+\\s*[\\{\\}\\(\\)]*@[\\{\\}\\(\\)]*\" \"$1\" \\\n            | grep -v -E -q \"(^\\s*/// @\\{|^\\s*/// @\\})\"\n}\ncheck_dox_groups_report() {\n    echo \"Found a likely typo similar to a valid doxygen grouping\"\n    echo \"Use balanced /// @{ and /// @} (on their own lines) to\"\\\n         \" create doxygen groups\"\n    pretty_grep -E \"^\\s*//+\\s*[\\{\\}\\(\\)]*@[\\{\\}\\(\\)]*\" \"$1\" | \\\n        grep -v -E \"(/// @{|/// @})\"\n}\ncheck_dox_groups_test() {\n    test_check pass foo.hpp '/// @{'\n    test_check pass foo.hpp '/// @}'\n    test_check pass foo.txt '// @('\n    test_check fail foo.hpp '// @{'\n    test_check fail foo.hpp '// @}'\n    test_check fail foo.hpp '//@{'\n    test_check fail foo.hpp '//@}'\n    test_check fail foo.hpp '// {@'\n    test_check fail foo.hpp '// }@'\n    test_check fail foo.hpp '///@('\n    test_check fail foo.hpp '///@)'\n}\nstandard_checks+=(check_dox_groups)\n\n# Check for Doxygen conditionals that are not in Doxygen comments.  We\n# assume these only occur in one-line comments.\ncheck_dox_conditionals() {\n    is_c++ \"$1\" && staged_grep -E '\\\\(end)?cond' \"$1\" \\\n            | grep -v -E -q '(///|/\\*\\*)\\s*\\\\(end)?cond'\n}\ncheck_dox_conditionals_report() {\n    echo \"Found a Doxygen conditional that is not in a single-line /// or /**\"\n    echo \"comment, which is the style preferred in SpECTRE.\"\n    if [[ $1 =~ \\.cpp$ ]]; then\n        echo \"cpp files are not processed with Doxygen, so this conditional\"\n        echo \"can be removed.\"\n    fi\n    pretty_grep -E '.*\\\\(end)?cond' \"$1\" \\\n        | grep -v -E '(///|/\\*\\*)\\s*\\\\(end)?cond'\n}\ncheck_dox_conditionals_test() {\n    test_check pass foo.hpp '/// \\cond'\n    test_check pass foo.hpp '/// \\endcond'\n    test_check pass foo.hpp '/// \\cond NAME'\n    test_check pass foo.hpp '///   \\cond'\n    # Form appearing in macros\n    test_check pass foo.hpp '  /** \\cond */ \\'\n    test_check pass foo.hpp '  /** \\endcond */ \\'\n    test_check pass foo.hpp '// condition'\n    test_check pass foo.md '\\cond'\n    test_check pass foo.md '\\endcond'\n    test_check pass foo.md '   \\cond NAME'\n    test_check fail foo.hpp '// \\cond'\n    test_check fail foo.hpp '// \\endcond'\n    test_check fail foo.hpp '// \\cond NAME'\n    test_check fail foo.hpp '  /* \\cond */'\n}\nstandard_checks+=(check_dox_conditionals)\n\n# Check for uses of `proxy.ckLocal()`, prefer `Parallel::local(proxy)`\nprevent_cklocal() {\n    is_c++ \"$1\" && \\\n        whitelist \"$1\" 'src/Parallel/Local.hpp$' && \\\n        staged_grep -q \"\\\\.ckLocal()\" \"$1\"\n}\nprevent_cklocal_report() {\n    echo \"Found occurrences of ckLocal, must use Parallel::local:\"\n    pretty_grep \"\\\\.ckLocal()\" \"$@\"\n}\nprevent_cklocal_test() {\n    test_check fail foo.cpp 'auto a = some_proxy.ckLocal();'\n    test_check pass foo.cpp 'auto a = some_proxy.ckLocalBranch();'\n    test_check pass foo.cpp 'auto a = Parallel::local(some_proxy);'\n    test_check pass foo.cpp '// a comment talking about ckLocal'\n    test_check pass foo.txt '// a comment talking about ckLocal'\n}\nstandard_checks+=(prevent_cklocal)\n\n# Check for uses of `proxy.ckLocalBranch()`,\n# prefer `Parallel::local_branch(proxy)`\nprevent_cklocalbranch() {\n    is_c++ \"$1\" && \\\n        whitelist \"$1\" 'src/Parallel/Local.hpp$' && \\\n        staged_grep -q \"\\\\.ckLocalBranch()\" \"$1\"\n}\nprevent_cklocalbranch_report() {\n    echo \"Found occurrences of ckLocalBranch, must use Parallel::local_branch:\"\n    pretty_grep \"\\\\.ckLocalBranch()\" \"$@\"\n}\nprevent_cklocalbranch_test() {\n    test_check fail foo.cpp 'auto a = some_proxy.ckLocalBranch();'\n    test_check pass foo.cpp 'auto a = some_proxy.ckLocal();'\n    test_check pass foo.cpp 'auto a = Parallel::local_branch(some_proxy);'\n    test_check pass foo.cpp '// a comment talking about ckLocalBranch'\n    test_check pass foo.txt '// a comment talking about ckLocalBranch'\n}\nstandard_checks+=(prevent_cklocalbranch)\n\n# if test is enabled: redefines staged_grep to run tests on files that\n# are not in git\n[ \"$1\" = --test ] && staged_grep() { grep \"$@\"; } && \\\n    run_tests \"${standard_checks[@]}\"\n\n# True result for sourcing\n:\n"
  },
  {
    "path": "tools/Formaline.sh",
    "content": "#!/bin/bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Generate an archive of the source tree, then generate an object file to\n# be linked into the final executable.\nformaline_archive_name=spectre_$1\nformaline_dir=@CMAKE_BINARY_DIR@/tmp/\npushd @CMAKE_SOURCE_DIR@ >/dev/null\nif [ -d \"./.git\" ]; then\n    git ls-tree --full-tree --name-only HEAD \\\n        | xargs tar -czf ${formaline_dir}/${formaline_archive_name}.tar.gz\nelse\n    # Since we can't record all the files in the repo (because there is\n    # no repo), we have to manually save a list of files to avoid trying\n    # to archive build dirs created inside the source dir.\n    #\n    # When running in a git repo we explicitly call into git to get the list\n    # of files just in case files or directories were added to the repo\n    # since the last time CMake ran. This should be very, very rare.\n    tar -czf ${formaline_dir}/${formaline_archive_name}.tar.gz \\\n        @SPECTRE_FORMALINE_LOCATIONS_SHELL@\nfi\npopd >/dev/null\n\npushd @CMAKE_BINARY_DIR@/tmp >/dev/null\nld -r -z noexecstack -b binary -o ${formaline_archive_name}.o \\\n   ${formaline_archive_name}.tar.gz\nrm ${formaline_dir}/${formaline_archive_name}.tar.gz\npopd >/dev/null\n# Set the formaline object file output name\nformaline_object_output=${formaline_dir}/${formaline_archive_name}.o\n# formaline takes the archive name and the name of the output file\nformaline_output=@CMAKE_BINARY_DIR@/tmp/${formaline_archive_name}.cpp\nrm -f ${formaline_output}\ncat >${formaline_output} <<EOF\n#include \"Utilities/Formaline.hpp\"\n\n#include <string>\n#include <vector>\n\nextern char _binary_${formaline_archive_name}_tar_gz_start[];\nextern char _binary_${formaline_archive_name}_tar_gz_end[];\n\nnamespace formaline {\nstd::vector<char> get_archive() {\n  return std::vector<char>{_binary_${formaline_archive_name}_tar_gz_start,\n                           _binary_${formaline_archive_name}_tar_gz_end};\n}\n\nstd::string get_environment_variables() {\n  // Use a delimiter that's unlikely to appear in the printenv\n  // output.\n  return R\"AZBYCXDWEVFU(\n`printenv`\n)AZBYCXDWEVFU\";\n}\n\nstd::string get_build_info() {\n  return R\"AZBYCXDWEVFU(\n`cat @CMAKE_BINARY_DIR@/BuildInfo.txt`\n)AZBYCXDWEVFU\";\n}\n\nstd::string get_paths() {\n  return R\"AZBYCXDWEVFU(PATH=${PATH}\nCPATH=${CPATH}\nLD_LIBRARY_PATH=${LD_LIBRARY_PATH}\nLIBRARY_PATH=${LIBRARY_PATH}\nCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}\n)AZBYCXDWEVFU\";\n}\n}  // namespace formaline\nEOF\n"
  },
  {
    "path": "tools/Hooks/CheckFileSize.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport os\nimport subprocess\nimport sys\n\n# The maximum file-size for a file to be committed in kB\nmax_file_size = 200.0\n\n# Files allowed to exceed the file-size limit\nallowed_large_files = [\n    \"docs/config/cppreference-doxygen-web.tag.xml\",\n]\n\n# The path to the git-binary:\ngit_executable = \"@GIT_EXECUTABLE@\"\n\n\ndef sizeof_fmt(num):\n    \"\"\"\n    This function will return a human-readable filesize-string\n    like \"3.5 MB\" for its given 'num'-parameter.\n    From http://stackoverflow.com/questions/1094841\n    \"\"\"\n    for x in [\"bytes\", \"KB\", \"MB\", \"GB\", \"TB\"]:\n        if num < 1024.0:\n            return \"%3.1f %s\" % (num, x)\n        num /= 1024.0\n\n\n# Check all files in the staging-area:\ntext = subprocess.check_output(\n    [git_executable, \"status\", \"--porcelain\", \"-uno\"], stderr=subprocess.STDOUT\n).decode(\"utf-8\")\nfile_list = text.splitlines()\n\n# Check all files:\nfor file_s in file_list:\n    if not os.path.isfile(file_s[3:]):\n        continue\n    elif file_s[3:] in allowed_large_files:\n        continue\n    stat = os.stat(file_s[3:])\n    if stat.st_size > (max_file_size * 1024):\n        print(\n            \"File '\"\n            + file_s[3:]\n            + \"' is too large to be committed. The file \"\n            \"is %s and the limit is %s\"\n            % (sizeof_fmt(stat.st_size), sizeof_fmt(max_file_size * 1024))\n        )\n        sys.exit(1)\n\nsys.exit(0)\n"
  },
  {
    "path": "tools/Hooks/pre-commit.sh",
    "content": "#!/usr/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n. @CMAKE_SOURCE_DIR@/tools/FileTestDefs.sh\n\n###############################################################################\n# Get list of non-deleted file names, which we need below.\ncommit_files=()\nwhile IFS= read -r -d '' file ; do\n    if [ -f \"${file}\" ] && [[ ${file} != external/*/* ]] ; then\n        commit_files+=(\"${file}\")\n    fi\ndone < <(@GIT_EXECUTABLE@ diff --cached --name-only -z)\n\n# If no files were added or modified we do not run any of the hooks. If we did\n# then rewording commits, and making empty commits would result in the hooks\n# searching the entire repository, which is not what we want in general.\nif [ ${#commit_files[@]} -eq 0 ]; then\n    exit 0\nfi\n\nfound_error=0\n\n###############################################################################\n# Check the file size\n@Python_EXECUTABLE@ @CMAKE_SOURCE_DIR@/.git/hooks/CheckFileSize.py\n[ \"$?\" -ne 0 ] && found_error=1\n\nprintf '%s\\0' \"${commit_files[@]}\" | run_checks \"${standard_checks[@]}\"\n[ $? -ne 0 ] && found_error=1\n\n###############################################################################\n# Use git-clang-format to check for any suspicious formatting of code.\n@GIT_CLANG_FORMAT_BIN@ --help > /dev/null\nif [ $? -eq 0 ]; then\n    clang_format_diff=$(\n        @GIT_CLANG_FORMAT_BIN@ --binary @CLANG_FORMAT_BIN@ --diff --quiet)\n    # Clang-format didn't always return the right exit code before version 15,\n    # so we check the diff output instead (see issue:\n    # https://github.com/llvm/llvm-project/issues/54758)\n    if [ -n \"$clang_format_diff\" ] \\\n        && [[ \"$clang_format_diff\" != *\"no modified files to format\"* ]] \\\n        && [[ \"$clang_format_diff\" != *\"did not modify any files\"* ]]; then\n        echo \"$clang_format_diff\" > @CMAKE_SOURCE_DIR@/.clang_format_diff.patch\n        echo \"Found C++ formatting errors.\"\n        echo \"Please run 'git clang-format' in the repository.\"\n        echo \"You can also apply the patch directly:\"\n        echo \"git apply @CMAKE_SOURCE_DIR@/.clang_format_diff.patch\"\n        echo \"\"\n    fi\nfi\n\n# Filter Python files\npython_files=()\nfor commit_file in ${commit_files[@]}\ndo\n    if [[ $commit_file =~ .*\\.py$ ]]; then\n        python_files+=(\"${commit_file}\")\n    fi\ndone\n\n# Python file checks\nif [ ${#python_files[@]} -ne 0 ]; then\n    # Use black to check Python file formatting\n    @Python_EXECUTABLE@ -m black --version > /dev/null\n    if [ $? -eq 0 ]; then\n        @Python_EXECUTABLE@ -m black --check --quiet ${python_files[@]}\n        if [ $? -ne 0 ]; then\n            found_error=1\n            printf \"Found Python formatting errors.\\n\"\n            printf \"Please run 'black .' in the repository.\\n\"\n        fi\n    else\n        printf \"Could not find 'black' Python formatter. Install with:\\n\"\n        printf \"pip3 install -r support/Python/dev_requirements.txt\\n\"\n        exit 1\n    fi\n\n    # Use isort to check python import order, only if it is installed.\n    @Python_EXECUTABLE@ -m isort --version > /dev/null\n    if [ $? -eq 0 ]; then\n        @Python_EXECUTABLE@ -m isort --check-only --quiet ${python_files[@]}\n        if [ $? -ne 0 ]; then\n            found_error=1\n            printf \"Found unsorted Python imports.\\n\"\n            printf \"Please run '@Python_EXECUTABLE@ -m isort .' in the \"\n            printf \"repository.\\n\"\n        fi\n    else\n        printf \"Could not find 'isort' Python formatter. Install with:\\n\"\n        printf \"pip3 install -r support/Python/dev_requirements.txt\\n\"\n        exit 1\n    fi\nfi\n\n###############################################################################\nif [ \"$found_error\" -eq \"1\" ]; then\n    exit 1\nfi\n"
  },
  {
    "path": "tools/ResignCommits.sh",
    "content": "#!/usr/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Re-attributes a range of commits from the given starting commit hash to HEAD,\n# resetting the author and committer to the current git identity. Signing is\n# controlled by the git config (commit.gpgsign). Intended to be run outside the\n# container after a Claude Code session.\n#\n# Usage: ResignCommits.sh <first-commit-hash>\n\nset -e\n\nif [ \"$#\" -ne 1 ]; then\n    echo \"Usage: $0 <first-commit-hash>\"\n    echo \"\"\n    echo \"Re-attributes all commits from <first-commit-hash> to HEAD\"\n    echo \"using the current git user.name and user.email.\"\n    exit 1\nfi\n\nFIRST_COMMIT=\"$1\"\n\n# Verify the commit exists\nif ! git cat-file -e \"${FIRST_COMMIT}^{commit}\" 2>/dev/null; then\n    echo \"Error: '${FIRST_COMMIT}' is not a valid commit in this repository.\"\n    exit 2\nfi\n\n# Abort if any merge commits exist in the range\nMERGE_COMMITS=$(git rev-list --merges \"${FIRST_COMMIT}^..HEAD\")\nif [ -n \"${MERGE_COMMITS}\" ]; then\n    echo \"Error: Merge commits found in the range ${FIRST_COMMIT}..HEAD.\"\n    echo \"ResignCommits.sh is not designed to handle merge commits.\"\n    echo \"\"\n    echo \"Merge commits in range:\"\n    git log --oneline --merges \"${FIRST_COMMIT}^..HEAD\"\n    exit 3\nfi\n\necho \"Re-attributing commits from ${FIRST_COMMIT} to HEAD...\"\nAMEND_CMD='git commit --amend --no-edit --reset-author'\ngit rebase --exec \"${AMEND_CMD}\" \"${FIRST_COMMIT}^\"\n\necho \"\"\necho \"Done. Verify with: git log --show-signature\"\n"
  },
  {
    "path": "tools/SpectrePch.hpp",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n#ifndef SPECTRE_PCH_HPP\n#define SPECTRE_PCH_HPP\n\n// Include STL headers\n#include <algorithm>\n#include <array>\n#include <cstddef>\n#include <limits>\n#include <memory>\n#include <ostream>\n#include <utility>\n#include <vector>\n\n// For Blaze error handling (see SetupBlaze.cmake)\n#include <Utilities/BlazeExceptions.hpp>\n\n#include <Utilities/ErrorHandling/Assert.hpp>\n#include <blaze/math/CustomVector.h>\n#include <blaze/math/DenseVector.h>\n#include <blaze/math/GroupTag.h>\n#include <blaze/math/typetraits/IsVector.h>\n#include <blaze/system/Optimizations.h>\n#include <blaze/system/Version.h>\n#include <blaze/util/typetraits/RemoveConst.h>\n\n// Include Brigand related headers\n#include <Utilities/TMPL.hpp>\n\n#endif  // SPECTRE_PCH_HPP\n"
  },
  {
    "path": "tools/SpectrePrettyPrinters.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport itertools\nimport re\nimport sys\n\nimport gdb\n\nif sys.version_info[0] > 2:\n    Iterator = object\n    imap = map\n    izip = zip\n    long = int\nelse:\n    ### Python 2 stuff\n    class Iterator:\n        \"\"\"Compatibility mixin for iterators\n\n        Instead of writing next() methods for iterators, write\n        __next__() methods and use this mixin to make them work in\n        Python 2 as well as Python 3.\n\n        Idea stolen from the \"six\" documentation:\n        <http://pythonhosted.org/six/#six.Iterator>\n        \"\"\"\n\n        def next(self):\n            return self.__next__()\n\n    from itertools import imap, izip\n\n\nclass VectorImplPrinter:\n    \"\"\"Print a VectorImpl, including DataVector, ComplexDataVector and their\n    modal counterparts\n    \"\"\"\n\n    class _iterator(Iterator):\n        def __init__(self, start, num_entries):\n            self.item = start\n            self.finish = start + num_entries\n            self.count = 0\n\n        def __iter__(self):\n            return self\n\n        def __next__(self):\n            count = self.count\n            self.count = self.count + 1\n            if self.item == self.finish:\n                raise StopIteration\n            elt = self.item.dereference()\n            self.item = self.item + 1\n            return (\"[%d]\" % count, elt)\n\n    def __init__(self, val):\n        self.val = val\n\n    def children(self):\n        return self._iterator(self.val[\"v_\"], self.val[\"size_\"])\n\n    def to_string(self):\n        return (\n            str(self.val.type.tag)\n            + \" owning: \"\n            + str(self.val[\"owning_\"])\n            + \" size: \"\n            + str(self.val[\"size_\"])\n            + \" values \"\n        )\n\n    def display_hint(self):\n        return \"array\"\n\n\nclass GslSpanPrinter:\n    \"Print a gsl::span\"\n\n    class _iterator(Iterator):\n        def __init__(self, start, num_entries):\n            self.item = start\n            self.finish = start + num_entries\n            self.count = 0\n\n        def __iter__(self):\n            return self\n\n        def __next__(self):\n            count = self.count\n            self.count = self.count + 1\n            if self.item == self.finish:\n                raise StopIteration\n            elt = self.item.dereference()\n            self.item = self.item + 1\n            return (\"[%d]\" % count, elt)\n\n    def __init__(self, val):\n        self.val = val\n\n    def children(self):\n        return self._iterator(\n            self.val[\"storage_\"][\"data_\"], self.val[\"storage_\"][\"size_\"]\n        )\n\n    def to_string(self):\n        return (\n            str(self.val.type.strip_typedefs().name).replace(\", -1l\", \"\")\n            + \" size: \"\n            + str(self.val[\"storage_\"][\"size_\"])\n            + \" values \"\n        )\n\n    def display_hint(self):\n        return \"array\"\n\n\nclass TensorPrinter:\n    \"Print a Tensor\"\n\n    class _iterator(Iterator):\n        def __init__(self, component_suffix_eval_string, start, num_entries):\n            self.component_suffix_eval_string = component_suffix_eval_string\n            self.item = start\n            self.num_entries = int(num_entries)\n            self.count = 0\n\n        def __iter__(self):\n            return self\n\n        def __next__(self):\n            count = self.count\n            self.count = self.count + 1\n            if self.count == self.num_entries + 1:\n                raise StopIteration\n            elt = self.item[count]\n            index = str(count)\n            # ND: It seems like some versions (or something else) causes GDB to\n            # sometimes fail to evaluate expressions and causes GDB to hard\n            # crash. Instead, we return the less helpful storage index since\n            # I don't know how to detect if a hard crash will happen. A\n            # workaround would be implementing the storage->suffix map in\n            # python.\n            #\n            # gdb.parse_and_eval(\n            #         self.component_suffix_eval_string + str(count) + \")\")\n            index = \"\\n[\" + str(index) + \"]\"\n            return (index, str(elt))\n\n    def children(self):\n        array_name = str(\n            self.val[\"data_\"].type.strip_typedefs().fields()[0].name\n        )\n        suffix_eval_string = (\n            str(self.val.type.strip_typedefs())\n            .replace(\"&\", \"\")\n            .replace(\"const \", \"\")\n            .replace(\" \", \"\")\n            + \"::component_suffix(\"\n        )\n        num_entries = (\n            int(\n                self.val[\"data_\"]\n                .type.strip_typedefs()\n                .fields()[0]\n                .type.strip_typedefs()\n                .range()[1]\n            )\n            + 1\n        )\n        return self._iterator(\n            suffix_eval_string,\n            self.val[\"data_\"][array_name][0].address,\n            num_entries,\n        )\n\n    def __init__(self, val):\n        if val.type.code == gdb.TYPE_CODE_TYPEDEF:\n            self.typename = val.type.name\n        else:\n            self.typename = \"Tensor\"\n        self.val = val\n\n    def to_string(self):\n        return self.typename\n\n\nclass VariablesPrinter:\n    \"Print a Variables\"\n\n    class _iterator(Iterator):\n        def __init__(self, head, empty):\n            self.head = head\n            self.count = 0\n            self.empty = empty\n\n        def __iter__(self):\n            return self\n\n        def __next__(self):\n            if self.empty or self.count == len(self.head.type.fields()):\n                raise StopIteration\n            count = self.count\n            self.count = self.count + 1\n\n            elet = self.head.cast(\n                self.head.type.fields()[count].type.strip_typedefs()\n            )[\"value_\"]\n            return (\n                \"[%s]\"\n                % str(self.head.type.fields()[count].type.strip_typedefs().name)\n                .replace(\"tuples::tuples_detail::TaggedTupleLeaf<\", \"\")\n                .replace(\", false>\", \"\")\n                .replace(\", true>\", \"\"),\n                elet.cast(elet.type.strip_typedefs()),\n            )\n\n    def children(self):\n        if self.is_empty:\n            return self._iterator(\"\", True)\n        else:\n            return self._iterator(\n                self.val[\"reference_variable_data_\"].cast(\n                    self.val[\"reference_variable_data_\"].type.strip_typedefs()\n                ),\n                False,\n            )\n\n    def __init__(self, val):\n        self.typename = \"Variables\"\n        self.val = val\n        self.is_empty = False\n        try:\n            str(self.val[\"owning_\"])\n        except:\n            self.is_empty = True\n\n    def to_string(self):\n        if self.is_empty:\n            return \"Variables<list<>>\"\n        else:\n            return (\n                \"Variables: (owning:\"\n                + str(self.val[\"owning_\"])\n                + \",Tags:\"\n                + str(self.val[\"number_of_variables\"])\n                + \",\"\n                + str(self.val[\"number_of_independent_components\"])\n                + \"x\"\n                + str(self.val[\"number_of_grid_points_\"])\n                + \") \"\n            )\n\n\ndef spectre_build_pretty_printer():\n    pp = gdb.printing.RegexpCollectionPrettyPrinter(\"spectre\")\n    pp.add_printer(\"DataVector\", \"^DataVector$\", VectorImplPrinter)\n    pp.add_printer(\n        \"ComplexDataVector\", \"^ComplexDataVector$\", VectorImplPrinter\n    )\n    pp.add_printer(\"ModalVector\", \"^ModalVector$\", VectorImplPrinter)\n    pp.add_printer(\n        \"ComplexModalVector\", \"^ComplexModalVector$\", VectorImplPrinter\n    )\n    pp.add_printer(\"gsl::span\", \"^gsl::span<.*-1.*>$\", GslSpanPrinter)\n    pp.add_printer(\"Scalar\", \"^Scalar<.*>$\", TensorPrinter)\n    pp.add_printer(\"tnsr\", \"^tnsr::.*>$\", TensorPrinter)\n    pp.add_printer(\"Tensor\", \"^Tensor<.*>$\", TensorPrinter)\n    pp.add_printer(\"Variables\", \"^Variables<.*>$\", VariablesPrinter)\n    return pp\n\n\nif __name__ == \"__main__\":\n    gdb.printing.register_pretty_printer(\n        gdb.current_objfile(), spectre_build_pretty_printer()\n    )\n"
  },
  {
    "path": "tools/Status/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  Status\n  MODULE_PATH \"tools\"\n  PYTHON_FILES\n  __init__.py\n  Status.py\n)\n\nadd_subdirectory(ExecutableStatus)\n"
  },
  {
    "path": "tools/Status/Dashboard.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\"\"\"Experimental dashboard for monitoring simulations.\n\nThis script provides a [Streamlit](https://streamlit.io) dashboard for\nmonitoring simulations. It uses the 'spectre.tools.Status' module to fetch\ninformation about the jobs and then displays lots of plots and metrics that are\nupdated in real time.\n\nTo use this experimental feature, first make sure you have the following Python\npackages installed in your environment in addition to the regular Python\ndependencies:\n\n```sh\npip install streamlit streamlit-autorefresh plotly\n```\n\nTo spin up the dashboard, run the following command:\n\n```sh\n$BUILD_DIR/bin/python-spectre -m streamlit \\\n    run $SPECTRE_HOME/tools/Status/Dashboard.py\n```\n\nThe dashboard will be available at the following URL by default:\n\n- Dashboard: http://localhost:8501\n\nYou can forward the port through your SSH connection to open the dashboard in\nyour local browser. Note that VSCode forwards ports automatically and also\nprovides a you with a simple browser to view the dashboard within VSCode if you\nprefer.\n\nSee the [Streamlit docs](https://docs.streamlit.io/develop/api-reference/cli/run)\nfor details.\n\"\"\"\n\nimport logging\nfrom functools import partial\nfrom pathlib import Path\n\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport streamlit as st\nimport yaml\nfrom streamlit_autorefresh import st_autorefresh\n\nfrom spectre.tools.Status.Status import (\n    DEFAULT_COLUMNS,\n    fetch_status,\n    match_executable_status,\n)\nfrom spectre.Visualization.Plot import DEFAULT_MPL_STYLESHEET\n\nlogger = logging.getLogger(__name__)\n\nplt.style.use(DEFAULT_MPL_STYLESHEET)\n\nSTATE_ICONS = {\n    \"RUNNING\": \":material/arrow_forward:\",\n    \"COMPLETED\": \":material/check_circle:\",\n    \"PENDING\": \":material/pending:\",\n    \"FAILED\": \":material/block:\",\n    \"TIMEOUT\": \":material/hourglass_disabled:\",\n    \"CANCELLED\": \":material/cancel:\",\n}\n\n\ndef _render_page(job):\n    st.header(job[\"JobName\"])\n\n    # Print run directory\n    run_dir = Path(job[\"WorkDir\"])\n    st.write(run_dir)\n\n    # Print README\n    readme_files = [\n        run_dir / \"README.md\",\n    ]\n    if job[\"SegmentsDir\"]:\n        readme_files += [\n            Path(job[\"SegmentsDir\"]) / \"README.md\",\n            Path(job[\"SegmentsDir\"]) / \"../README.md\",\n        ]\n    for readme_file in readme_files[::-1]:\n        if readme_file.exists():\n            st.markdown(readme_file.read_text())\n\n    # Organize into tabs\n    status_tab, input_file_tab, submit_script_tab, outfile_tab = st.tabs(\n        [\"Status\", Path(job[\"InputFile\"]).name, \"Submit.sh\", \"spectre.out\"]\n    )\n\n    # Print input file\n    with input_file_tab:\n        st.code(\n            Path(job[\"InputFile\"]).read_text(),\n            language=\"yaml\",\n            line_numbers=True,\n        )\n\n    # Print submit script\n    with submit_script_tab:\n        st.code(\n            (run_dir / \"Submit.sh\").read_text(),\n            language=\"bash\",\n            line_numbers=True,\n        )\n\n    # Print outfile\n    with outfile_tab:\n        outfile = run_dir / \"spectre.out\"\n        if outfile.exists():\n            # Show only the last 50 lines because showing the full file can be\n            # slow. We could show a button to load more lines.\n            with open(outfile, \"r\") as open_outfile:\n                st.code(\"\".join([\"...\\n\"] + open_outfile.readlines()[-50:]))\n        else:\n            st.write(\"No spectre.out file found.\")\n\n    # Render job status\n    with status_tab:\n        columns = list(DEFAULT_COLUMNS)\n        columns.remove(\"User\")\n        columns.remove(\"JobName\")\n        if pd.notna(job[\"JobID\"]):\n            st.table(pd.DataFrame([job[columns]]).set_index(\"JobID\"))\n\n        # Render status metrics\n        executable_status = match_executable_status(job[\"ExecutableName\"])\n        with open(job[\"InputFile\"], \"r\") as open_input_file:\n            metadata, input_file = yaml.safe_load_all(open_input_file)\n        status = executable_status.status(input_file, job[\"WorkDir\"])\n        for (field, unit), col in zip(\n            executable_status.fields.items(),\n            st.columns(len(executable_status.fields)),\n        ):\n            col.metric(\n                (field + f\" [{unit}]\") if unit else field,\n                (\n                    executable_status.format(field, status[field])\n                    if field in status\n                    else \"-\"\n                ),\n            )\n\n        if job[\"ErrorMessage\"]:\n            st.error(job[\"ErrorMessage\"])\n\n        # Executable-specific dashboard\n        executable_status.render_dashboard(job, input_file, metadata)\n\n\n# Add runs at specific paths\nextra_run_dirs = st.session_state.get(\"extra_run_dirs\", [])\nif \"extra_run_dirs\" not in st.session_state:\n    st.session_state[\"extra_run_dirs\"] = extra_run_dirs\nadd_run_dir = st.sidebar.text_input(\"Add extra run directory:\", \"\")\nwith st.sidebar.container():\n    if st.button(\"Add\"):\n        if add_run_dir and add_run_dir not in extra_run_dirs:\n            extra_run_dirs.append(add_run_dir)\n            st.session_state[\"extra_run_dirs\"] = extra_run_dirs\n    if st.button(\"Clear\"):\n        extra_run_dirs.clear()\n        st.session_state[\"extra_run_dirs\"] = extra_run_dirs\n\n# Fetch the job data\njob_data = fetch_status(\n    user=None,\n    allusers=st.sidebar.toggle(\"Show all users\", False),\n    starttime=st.sidebar.text_input(\"Start time\", \"now-1day\"),\n    extra_run_dirs=extra_run_dirs,\n)\nif len(job_data) > 0:\n    job_data.sort_values(\"JobID\", inplace=True, ascending=False)\n    job_data.dropna(subset=[\"WorkDir\", \"ExecutableName\"], inplace=True)\n\n# Refresh the page regularly\nif st.sidebar.toggle(\"Auto-refresh\", True):\n    refresh_interval = st.sidebar.number_input(\n        \"Refresh interval [s]\", min_value=1, value=30, format=\"%d\"\n    )\n    count = st_autorefresh(interval=refresh_interval * 1000)\n\n# Each job gets its own page. Jobs are grouped by user.\npages = {}\nfor username, user_data in job_data.groupby(\"User\", dropna=False):\n    pages_user = []\n    for _, job in user_data.iterrows():\n        # Get a somewhat descriptive title for the page. This can be improved.\n        # We probably want to make the job name more descriptive and show that\n        # here.\n        display_dir = (\n            Path(job[\"SegmentsDir\"])\n            if job[\"SegmentsDir\"]\n            else Path(job[\"WorkDir\"])\n        )\n        if pd.isna(job[\"JobName\"]):\n            job[\"JobName\"] = job[\"ExecutableName\"]\n        title = (\n            job[\"JobName\"]\n            if job[\"JobName\"] != job[\"ExecutableName\"]\n            else (display_dir.resolve().parent.name + \" / \" + display_dir.name)\n        )\n        if pd.notna(job[\"JobID\"]):\n            title += f\" ({job['JobID']})\"\n        pages_user.append(\n            st.Page(\n                partial(_render_page, job=job),\n                title=title,\n                icon=STATE_ICONS.get(job[\"State\"]),\n                url_path=str(\n                    job[\"JobID\"]\n                    if pd.notna(job[\"JobID\"])\n                    else hash(job[\"WorkDir\"])\n                ),\n            )\n        )\n    pages[username if pd.notna(username) else \"\"] = pages_user\n\n# Set up navigation\nif len(pages) > 0:\n    page = st.navigation(pages)\n    page.run()\nelse:\n    st.write(\"No jobs found.\")\n"
  },
  {
    "path": "tools/Status/ExecutableStatus/CMakeLists.txt",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nspectre_python_add_module(\n  ExecutableStatus\n  MODULE_PATH tools/Status\n  PYTHON_FILES\n  __init__.py\n  CharacteristicExtract.py\n  EvolveGhBinaryBlackHole.py\n  EvolveGhSingleBlackHole.py\n  ExecutableStatus.py\n  SolveXcts.py\n)\n"
  },
  {
    "path": "tools/Status/ExecutableStatus/CharacteristicExtract.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nfrom pathlib import Path\n\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom spectre.IO.H5 import to_dataframe\n\nfrom .ExecutableStatus import ExecutableStatus\n\nlogger = logging.getLogger(__name__)\n\n\nclass CharacteristicExtract(ExecutableStatus):\n    executable_name_patterns = [r\"^CharacteristicExtract\"]\n    fields = {\n        \"Time\": \"M\",\n    }\n\n    def status(self, input_file, work_dir):\n        try:\n            open_reductions_file = h5py.File(\n                Path(work_dir)\n                / (input_file[\"Observers\"][\"ReductionFileName\"] + \".h5\"),\n                \"r\",\n            )\n        except:\n            logger.debug(\"Unable to open reductions file.\", exc_info=True)\n            return {}\n        with open_reductions_file:\n            try:\n                time_steps = to_dataframe(\n                    open_reductions_file[\"Cce/CceTimeStep.dat\"]\n                )\n            except:\n                logger.debug(\"Unable to read CCE time steps.\", exc_info=True)\n                return {}\n        return {\n            \"Time\": time_steps.iloc[-1][\"Time\"],\n        }\n\n    def format(self, field, value):\n        if field in [\"Time\", \"Speed\"]:\n            return f\"{value:g}\"\n        raise ValueError\n\n    def render_dashboard(self, job: dict, input_file: dict, metadata: dict):\n        import plotly.express as px\n        import streamlit as st\n\n        from spectre.Visualization.PlotCce import plot_cce\n\n        fig = plot_cce(\n            Path(job[\"WorkDir\"])\n            / (input_file[\"Observers\"][\"ReductionFileName\"] + \".h5\"),\n            modes=[\"Real Y_2,2\", \"Imag Y_2,2\"],\n            x_label=\"Time [M]\",\n        )\n        st.pyplot(fig)\n"
  },
  {
    "path": "tools/Status/ExecutableStatus/EvolveGhBinaryBlackHole.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport glob\nimport logging\nimport os\nfrom pathlib import Path\n\nimport h5py\nimport numpy as np\nimport pandas as pd\n\nfrom spectre.IO.H5 import to_dataframe\n\nfrom .ExecutableStatus import EvolutionStatus, list_reduction_files\n\nlogger = logging.getLogger(__name__)\n\n\nclass EvolveGhBinaryBlackHole(EvolutionStatus):\n    executable_name_patterns = [r\"^EvolveGhBinaryBlackHole\"]\n    fields = {\n        \"Time\": \"M\",\n        \"Speed\": \"M/h\",\n        \"Orbits\": None,\n        \"Separation\": \"M\",\n        \"3-Index Constraint\": None,\n    }\n\n    def status(self, input_file, work_dir):\n        try:\n            reductions_file = input_file[\"Observers\"][\"ReductionFileName\"]\n            open_reductions_file = h5py.File(\n                os.path.join(work_dir, reductions_file + \".h5\"), \"r\"\n            )\n        except:\n            logger.debug(\"Unable to open reductions file.\", exc_info=True)\n            return {}\n        with open_reductions_file:\n            result = self.time_status(input_file, open_reductions_file)\n            # Number of orbits. We use the rotation control system for this.\n            try:\n                rotation_z = to_dataframe(\n                    open_reductions_file[\"ControlSystems/Rotation/z.dat\"],\n                    slice=np.s_[-1:],\n                )\n                # Assume the initial rotation angle is 0 for now. We can update\n                # this to read the initial rotation angle once we can read\n                # previous segments / checkpoints of a simulation.\n                covered_angle = rotation_z[\"FunctionOfTime\"].iloc[-1]\n                result[\"Orbits\"] = covered_angle / (2.0 * np.pi)\n            except:\n                logger.debug(\"Unable to extract orbits.\", exc_info=True)\n            # Euclidean separation between horizons\n            try:\n                ah_centers = [\n                    to_dataframe(\n                        open_reductions_file[\n                            f\"ApparentHorizons/ControlSystemAh{ab}_Centers.dat\"\n                        ],\n                        slice=np.s_[-1:],\n                    ).iloc[-1]\n                    for ab in \"AB\"\n                ]\n                ah_separation = np.sqrt(\n                    sum(\n                        (\n                            ah_centers[0][\"InertialCenter\" + xyz]\n                            - ah_centers[1][\"InertialCenter\" + xyz]\n                        )\n                        ** 2\n                        for xyz in [\"_x\", \"_y\", \"_z\"]\n                    )\n                )\n                result[\"Separation\"] = ah_separation\n            except:\n                logger.debug(\"Unable to extract separation.\", exc_info=True)\n            # Norms\n            try:\n                norms = to_dataframe(\n                    open_reductions_file[\"Norms.dat\"], slice=np.s_[-1:]\n                )\n                result[\"3-Index Constraint\"] = norms.iloc[-1][\n                    \"L2Norm(PointwiseL2Norm(ThreeIndexConstraint))\"\n                ]\n            except:\n                logger.debug(\n                    \"Unable to extract three index constraint.\", exc_info=True\n                )\n        return result\n\n    def format(self, field, value):\n        if field == \"Separation\":\n            return f\"{value:g}\"\n        elif field == \"Orbits\":\n            return f\"{value:g}\"\n        elif field == \"3-Index Constraint\":\n            return f\"{value:.2e}\"\n        return super().format(field, value)\n\n    def render_dashboard(self, job: dict, input_file: dict, metadata: dict):\n        import matplotlib.pyplot as plt\n        import plotly.express as px\n        import streamlit as st\n\n        from spectre.Pipelines.EccentricityControl.EccentricityControlParams import (\n            eccentricity_control_params,\n        )\n        from spectre.Visualization.GenerateXdmf import generate_xdmf\n        from spectre.Visualization.PlotControlSystem import plot_control_system\n        from spectre.Visualization.PlotSizeControl import plot_size_control\n        from spectre.Visualization.PlotTrajectories import (\n            import_A_and_B,\n            plot_trajectory,\n        )\n\n        run_dir = Path(job[\"WorkDir\"])\n        reduction_files = list_reduction_files(job=job, input_file=input_file)\n        if not reduction_files:\n            st.warning(\"No data yet.\")\n            return\n\n        # Common horizon\n        with h5py.File(run_dir / reduction_files[-1], \"r\") as open_h5file:\n            ahc_subfile = open_h5file.get(\"ObservationAhC.dat\")\n            if ahc_subfile is not None:\n                ahc_data = to_dataframe(ahc_subfile).iloc[0]\n                st.success(\n                    \"Found a common horizon with mass\"\n                    f\" {ahc_data['ChristodoulouMass']:g} and spin\"\n                    f\" {ahc_data['DimensionlessSpinMagnitude']:g} at time\"\n                    f\" {ahc_data['Time']:g} M.\"\n                )\n\n        # Plot style\n        legend_layout = dict(\n            title=None,\n            orientation=\"h\",\n            yanchor=\"bottom\",\n            y=1,\n            xanchor=\"left\",\n            x=0,\n        )\n\n        # Constraints\n        st.subheader(\"Constraints\")\n\n        def get_constraints_data(reductions_file):\n            with h5py.File(reductions_file, \"r\") as open_h5file:\n                return to_dataframe(open_h5file[\"Norms.dat\"]).set_index(\"Time\")\n\n        constraints = pd.concat(map(get_constraints_data, reduction_files))\n        constraints.sort_index(inplace=True)\n        constraints = constraints[\n            [col for col in constraints.columns if \"Constraint\" in col]\n        ]\n        fig = px.line(constraints.iloc[1:], log_y=True)\n        fig.update_layout(xaxis_title=\"Time [M]\", legend=legend_layout)\n        fig.update_yaxes(exponentformat=\"e\", title=None)\n        st.plotly_chart(fig)\n\n        # Horizon parameters\n        st.subheader(\"Horizon parameters\")\n\n        def get_horizons_data(reductions_file):\n            with h5py.File(reductions_file, \"r\") as open_h5file:\n                horizons_data = []\n                for ab in \"AB\":\n                    ah_subfile = open_h5file.get(f\"ObservationAh{ab}.dat\")\n                    if ah_subfile is not None:\n                        horizons_data.append(\n                            to_dataframe(ah_subfile)\n                            .set_index(\"Time\")\n                            .add_prefix(f\"Ah{ab} \")\n                        )\n                if not horizons_data:\n                    return None\n                return pd.concat(horizons_data, axis=1)\n\n        horizon_params = pd.concat(map(get_horizons_data, reduction_files))\n        for label, name, col in zip(\n            [\"AhA mass\", \"AhB mass\", \"AhA spin\", \"AhB spin\"],\n            [\n                \"AhA ChristodoulouMass\",\n                \"AhB ChristodoulouMass\",\n                \"AhA DimensionlessSpinMagnitude\",\n                \"AhB DimensionlessSpinMagnitude\",\n            ],\n            st.columns(4),\n        ):\n            col.metric(label, f\"{horizon_params.iloc[-1][name]:.4g}\")\n        fig = px.line(\n            np.abs(horizon_params.iloc[1:] - horizon_params.iloc[0]),\n            log_y=True,\n        )\n        fig.update_layout(xaxis_title=\"Time [M]\", legend=legend_layout)\n        fig.update_yaxes(\n            exponentformat=\"e\", title=\"Difference to initial values\"\n        )\n        st.plotly_chart(fig)\n\n        # Time steps\n        super().render_time_steps(input_file, reduction_files)\n\n        # Trajectories\n        st.subheader(\"Trajectories\")\n        traj_A, traj_B = import_A_and_B(reduction_files)\n        st.pyplot(plot_trajectory(traj_A, traj_B))\n        coord_separation = np.linalg.norm(traj_A[:, 1:] - traj_B[:, 1:], axis=1)\n        fig = px.line(x=traj_A[:, 0], y=coord_separation)\n        fig.update_layout(xaxis_title=\"Time [M]\", legend=legend_layout)\n        fig.update_yaxes(title=\"Coordinate separation [M]\")\n        st.plotly_chart(fig)\n\n        # Grid\n        st.subheader(\"Grid\")\n\n        @st.fragment\n        def render_grid():\n            if st.button(\"Render grid\"):\n                from spectre.Visualization.Render3D.Domain import render_domain\n\n                volume_files = glob.glob(str(run_dir / \"BbhVolume*.h5\"))\n                generate_xdmf(\n                    volume_files,\n                    output=str(run_dir / \"Bbh.xmf\"),\n                    subfile_name=\"VolumeData\",\n                )\n                render_domain(\n                    str(run_dir / \"Bbh.xmf\"),\n                    output=str(run_dir / \"domain.png\"),\n                    slice=True,\n                    zoom_factor=50.0,\n                    time_step=-1,\n                )\n            if (run_dir / \"domain.png\").exists():\n                st.image(str(run_dir / \"domain.png\"))\n\n        render_grid()\n\n        # Control systems\n        st.subheader(\"Control systems\")\n\n        @st.fragment\n        def render_control_systems():\n            if st.checkbox(\"Show control systems\"):\n                st.pyplot(\n                    plot_control_system(\n                        reduction_files,\n                        with_shape=st.checkbox(\"With shape\", True),\n                        show_all_m=st.checkbox(\"Show all m\", False),\n                        shape_l_max=st.number_input(\n                            \"Shape l max\", value=2, min_value=0\n                        ),\n                    )\n                )\n                with st.expander(\"Size control A\", expanded=False):\n                    st.pyplot(plot_size_control(reduction_files, \"A\"))\n                with st.expander(\"Size control B\", expanded=False):\n                    st.pyplot(plot_size_control(reduction_files, \"B\"))\n\n        render_control_systems()\n\n        # Eccentricity\n        st.subheader(\"Eccentricity\")\n\n        @st.fragment\n        def render_eccentricity():\n            if st.checkbox(\"Show eccentricity\"):\n                if st.checkbox(\"Set time bounds\"):\n                    col_tmin, col_tmax = st.columns(2)\n                    tmin = col_tmin.number_input(\"tmin\", value=500, min_value=0)\n                    tmax = col_tmax.number_input(\n                        \"tmax\", value=1500, min_value=0\n                    )\n                else:\n                    tmin, tmax = None, None\n                ecc_params = eccentricity_control_params(\n                    reduction_files,\n                    metadata[\"Next\"][\"With\"][\"id_input_file_path\"],\n                    tmin=tmin,\n                    tmax=tmax,\n                )\n                st.metric(\"Eccentricity\", f\"{ecc_params['Eccentricity']:e}\")\n\n        render_eccentricity()\n"
  },
  {
    "path": "tools/Status/ExecutableStatus/EvolveGhSingleBlackHole.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\n\nimport h5py\nimport numpy as np\n\nfrom spectre.IO.H5 import to_dataframe\n\nfrom .ExecutableStatus import EvolutionStatus\n\nlogger = logging.getLogger(__name__)\n\n\nclass EvolveGhSingleBlackHole(EvolutionStatus):\n    executable_name_patterns = [r\"^EvolveGhSingleBlackHole\"]\n    fields = {\n        \"Time\": \"M\",\n        \"Speed\": \"M/h\",\n        \"Constraint Energy\": None,\n    }\n\n    def status(self, input_file, work_dir):\n        try:\n            reductions_file = input_file[\"Observers\"][\"ReductionFileName\"]\n            open_reductions_file = h5py.File(\n                os.path.join(work_dir, reductions_file + \".h5\"), \"r\"\n            )\n        except:\n            logger.debug(\"Unable to open reductions file.\", exc_info=True)\n            return {}\n        with open_reductions_file:\n            result = self.time_status(input_file, open_reductions_file)\n            # Norms\n            try:\n                norms = to_dataframe(\n                    open_reductions_file[\"Norms.dat\"], slice=np.s_[-1:]\n                )\n                result[\"Constraint Energy\"] = norms.iloc[-1][\n                    \"L2Norm(ConstraintEnergy)\"\n                ]\n            except:\n                logger.debug(\n                    \"Unable to extract constraint energy.\", exc_info=True\n                )\n        return result\n\n    def format(self, field, value):\n        if field in [\"Constraint Energy\"]:\n            return f\"{value:.2e}\"\n        return super().format(field, value)\n"
  },
  {
    "path": "tools/Status/ExecutableStatus/ExecutableStatus.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport datetime\nimport logging\nimport os\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional\n\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom spectre.IO.H5 import to_dataframe\nfrom spectre.support.DirectoryStructure import list_segments\nfrom spectre.Visualization.ReadInputFile import find_event\n\nlogger = logging.getLogger(__name__)\n\n\ndef list_reduction_files(job: dict, input_file: dict):\n    reductions_file_name = input_file[\"Observers\"][\"ReductionFileName\"] + \".h5\"\n    if job[\"SegmentsDir\"]:\n        reduction_files = [\n            segment_dir.path / reductions_file_name\n            for segment_dir in list_segments(job[\"SegmentsDir\"])\n        ]\n    else:\n        reduction_files = [Path(job[\"WorkDir\"]) / reductions_file_name]\n    return list(\n        filter(\n            lambda reduction_file: reduction_file.exists(),\n            reduction_files,\n        )\n    )\n\n\ndef get_start_time(spectre_out: Path):\n    with open(spectre_out, \"r\") as f:\n        for line in f:\n            if (\n                \"Date and time at startup:\" in line\n                or \"Restarting from checkpoint. Date and time:\" in line\n            ):\n                dt = pd.to_datetime(line.split(\":\", 1)[-1].strip())\n                if dt.tzinfo:\n                    # Convert to local time zone\n                    dt = dt.tz_convert(\n                        datetime.datetime.now().astimezone().tzinfo\n                    )\n                return dt\n\n    return None\n\n\nclass ExecutableStatus:\n    \"\"\"Base class for providing status information for SpECTRE executables.\n\n    Subclasses read output from executables and provide status information.\n    For example, a subclass for a binary black hole evolution executable can\n    provide the current separation and number of orbits.\n\n    Properties:\n      executable_name_patterns: List of regex patterns that match the executable\n        names the subclass applies to. For example, r\"^Evolve\" matches all\n        executables that start with \"Evolve\".\n      fields: Dictionary of all field names the subclass provides and their\n        units. For example: {\"Time\": \"M\", \"Orbits\": None} if the subclass\n        provides time in units of mass and orbits with no unit.\n    \"\"\"\n\n    executable_name_patterns: List[str] = []\n    fields: Dict[str, Optional[str]] = {}\n\n    def status(\n        self, input_file: Optional[dict], work_dir: Optional[str]\n    ) -> dict:\n        \"\"\"Provide status information of an executable run.\n\n        Arguments:\n          input_file: The input file read in as a dictionary.\n          work_dir: The working directory of the executable run. Paths in the\n            input file are relative to this directory.\n\n        Returns: Dictionary of 'fields' and their values. For example:\n          {\"Time\": 10.5, \"Orbits\": 3}. Not all fields must be provided if they\n          can't be determined.\n\n        Note: Avoid raising exceptions in this function. It is better to provide\n          only a subset of fields than to raise an exception. For example, if\n          there's an error reading the number of orbits, intercept and log the\n          exception and just skip providing the number of orbits.\n        \"\"\"\n        return {}\n\n    def format(self, field: str, value: Any) -> str:\n        \"\"\"Transform status values to human-readable strings.\n\n        Arguments:\n          field: The name of the field, as listed in 'fields' (e.g. \"Time\").\n          value: The status value of the field, as returned by 'status()'.\n\n        Returns: Human-readable representation of the value.\n        \"\"\"\n        raise NotImplementedError\n\n    def render_dashboard(self, job: dict, input_file: dict, metadata: dict):\n        \"\"\"Render a dashboard for the job (experimental).\n\n        Arguments:\n          job: A dictionary of job information, including the input file and\n            working directory. See the 'spectre.tools.Status.fetch_status'\n            function for details.\n          input_file: The input file read in as a dictionary.\n          metadata: The input file metadata read in as a dictionary.\n\n        This method can be overridden by subclasses to provide a custom\n        dashboard for the job. The default implementation does nothing.\n        \"\"\"\n        import streamlit as st\n\n        st.warning(\n            \"No dashboard available for this executable. Add an implementation\"\n            \" to the 'ExecutableStatus' subclass.\"\n        )\n\n\nclass EvolutionStatus(ExecutableStatus):\n    \"\"\"An 'ExecutableStatus' subclass that matches all evolution executables.\n\n    This is a fallback if no more specialized subclass is implemented. It just\n    determines the current time and run speed. This class can be subclassed\n    further to use the 'time_status' function in subclasses.\n    \"\"\"\n\n    executable_name_patterns = [r\"^Evolve\"]\n    fields = {\n        \"Time\": None,\n        \"Speed\": \"1/h\",\n    }\n\n    def time_status(\n        self, input_file: dict, open_reductions_file, avg_num_slabs: int = 50\n    ) -> dict:\n        \"\"\"Report the simulation time and speed.\n\n        Uses the 'ObserveTimeStep' event, so the status information is only as\n        current as the output frequency of the event. The reported time is the\n        most recent output of the 'ObserveTimeStep' event, and the reported\n        speed is determined from the two most recent outputs. Since the walltime\n        at which different elements in the computational domain reach the\n        timestep varies because of the lack of a global synchronization point in\n        the task-based parallel evolution, this function returns the average\n        between the minimum and maximum walltime.\n\n        Arguments:\n          input_file: The input file read in as a dictionary.\n          open_reductions_file: The open h5py reductions data file.\n          avg_num_slabs: Number of slabs to average over for estimating the\n            simulation speed (at least 2).\n\n        Returns: Status fields \"Time\" in code units and \"Speed\" in simulation\n          time per hour.\n        \"\"\"\n        assert (\n            avg_num_slabs >= 2\n        ), \"Need at least 'avg_num_slabs >= 2' to estimate simulation speed.\"\n        try:\n            observe_time_event = find_event(\n                \"ObserveTimeStep\", \"EventsAndTriggersAtSlabs\", input_file\n            )\n        except KeyError:\n            # Fallback to old name for events and triggers\n            observe_time_event = find_event(\n                \"ObserveTimeStep\", \"EventsAndTriggers\", input_file\n            )\n        if observe_time_event is None:\n            return {}\n        subfile_name = observe_time_event[\"SubfileName\"] + \".dat\"\n        logger.debug(f\"Reading time steps from subfile: '{subfile_name}'\")\n        try:\n            # Time steps are sampled at most once per slab, so we load data\n            # going back `avg_num_slabs`\n            time_steps = to_dataframe(\n                open_reductions_file[subfile_name], slice=np.s_[-avg_num_slabs:]\n            )\n        except:\n            logger.debug(\"Unable to read time steps.\", exc_info=True)\n            return {}\n        result = {\n            \"Time\": time_steps.iloc[-1][\"Time\"],\n        }\n        # Estimate simulation speed\n        try:\n            # Average over the last `avg_num_slabs` slabs, but at least the last\n            # two observations\n            start_time = min(\n                time_steps.iloc[-1][\"Time\"]\n                - time_steps.iloc[-1][\"Slab size\"] * (avg_num_slabs - 1),\n                time_steps.iloc[-2][\"Time\"],\n            )\n            time_window = time_steps[time_steps[\"Time\"] >= start_time]\n            dt_sim = time_window.iloc[-1][\"Time\"] - time_window.iloc[0][\"Time\"]\n            dt_wall = (\n                time_window.iloc[-1][\"Maximum Walltime\"]\n                - time_window.iloc[0][\"Maximum Walltime\"]\n            )\n            speed = dt_sim / dt_wall * 3600.0\n            result[\"Speed\"] = speed\n        except:\n            logger.debug(\"Unable to estimate simulation speed.\", exc_info=True)\n        return result\n\n    def status(self, input_file, work_dir):\n        try:\n            reductions_file = input_file[\"Observers\"][\"ReductionFileName\"]\n            open_reductions_file = h5py.File(\n                os.path.join(work_dir, reductions_file + \".h5\"), \"r\"\n            )\n        except:\n            logger.debug(\"Unable to open reductions file.\", exc_info=True)\n            return {}\n        with open_reductions_file:\n            return self.time_status(input_file, open_reductions_file)\n\n    def format(self, field, value):\n        if field in [\"Time\", \"Speed\"]:\n            return f\"{value:g}\"\n        raise ValueError\n\n    def render_time_steps(self, input_file: dict, reduction_files: List[Path]):\n        import plotly.colors as pc\n        import plotly.express as px\n        import plotly.graph_objects as go\n        import streamlit as st\n\n        if not reduction_files:\n            st.warning(\"No data yet.\")\n            return\n\n        try:\n            observe_time_event = find_event(\n                \"ObserveTimeStep\", \"EventsAndTriggersAtSlabs\", input_file\n            )\n        except KeyError:\n            # Fallback to old name for events and triggers\n            observe_time_event = find_event(\n                \"ObserveTimeStep\", \"EventsAndTriggers\", input_file\n            )\n        if observe_time_event is None:\n            st.warning(\"No 'ObserveTimeStep' event found in input file.\")\n            return\n        subfile_name = observe_time_event[\"SubfileName\"] + \".dat\"\n        logger.debug(f\"Reading time steps from subfile: '{subfile_name}'\")\n\n        def get_time_steps(reductions_file):\n            with h5py.File(reductions_file, \"r\") as open_h5file:\n                return to_dataframe(open_h5file[subfile_name]).set_index(\"Time\")\n\n        # Plot style\n        legend_layout = dict(\n            title=None,\n            orientation=\"h\",\n            yanchor=\"bottom\",\n            y=1,\n            xanchor=\"left\",\n            x=0,\n        )\n\n        st.subheader(\"Time steps\")\n        all_time_steps = list(map(get_time_steps, reduction_files))\n        time_steps = pd.concat(all_time_steps)\n        fig = px.line(\n            time_steps,\n            y=[\n                \"Effective time step\",\n                \"Minimum time step\",\n                \"Maximum time step\",\n                \"Slab size\",\n            ],\n            log_y=True,\n        )\n        fig.update_layout(\n            legend=legend_layout,\n            xaxis=dict(title=\"Time [M]\"),\n            yaxis=dict(exponentformat=\"e\", title=None),\n        )\n        st.plotly_chart(fig)\n        run_speed = (\n            time_steps.index.to_series().diff()\n            / time_steps[\"Maximum Walltime\"].diff()\n        ) * 3600\n        avg_run_speed = run_speed.rolling(30, min_periods=1).mean()\n\n        # Plot simulation speed\n        fig = px.line(avg_run_speed)\n        fig.add_scatter(\n            x=run_speed.index,\n            y=run_speed,\n            mode=\"lines\",\n            opacity=0.5,\n            name=\"Instantaneous speed\",\n        )\n        fig.update_layout(\n            xaxis_title=\"Time [M]\",\n            yaxis_title=\"Simulation speed [M/h]\",\n            showlegend=False,\n        )\n        st.plotly_chart(fig)\n\n        # Plot simulation speed over calendar time\n        fig = go.Figure()\n        for reduction_file, segment_time_steps in zip(\n            reduction_files, all_time_steps\n        ):\n            spectre_out = reduction_file.parent / \"spectre.out\"\n            start_time = get_start_time(spectre_out)\n            if not start_time:\n                continue\n            calendar_time = start_time + pd.to_timedelta(\n                segment_time_steps[\"Maximum Walltime\"], unit=\"s\"\n            )\n            fig.add_trace(\n                go.Scatter(\n                    x=calendar_time,\n                    y=segment_time_steps.index,\n                    mode=\"lines\",\n                    name=reduction_file.parent.name,\n                )\n            )\n        fig.update_layout(\n            xaxis_title=\"Calendar day\",\n            yaxis_title=\"Simulation time [M]\",\n            showlegend=False,\n        )\n        st.plotly_chart(fig)\n\n        # Plot AMR stats if available\n        def get_amr_observations(reduction_files):\n            for reductions_file in reduction_files:\n                with h5py.File(reductions_file, \"r\") as open_h5file:\n                    if \"Amr.dat\" in open_h5file:\n                        yield to_dataframe(open_h5file[\"Amr.dat\"]).set_index(\n                            \"Time\"\n                        )\n\n        all_amr_observations = list(get_amr_observations(reduction_files))\n        if len(all_amr_observations) > 0:\n            amr_stats = pd.concat(all_amr_observations)\n            st.subheader(\"AMR\")\n\n            # Plot total num points over time\n            fig = px.line(time_steps[\"NumberOfPoints\"])\n            fig.add_scatter(\n                x=amr_stats.index,\n                y=amr_stats[\"TotalNumPoints\"],\n                mode=\"lines+markers\",\n            )\n            fig.update_layout(\n                xaxis_title=\"Time [M]\",\n                yaxis_title=\"Number of grid points\",\n                showlegend=False,\n            )\n            st.plotly_chart(fig)\n\n            # Plot points per element per dimension over time\n            fig = go.Figure()\n            colors = pc.qualitative.Plotly\n            for dim in range((len(amr_stats.columns) - 3) // 3):\n                avg_p = (\n                    amr_stats[f\"NumPointsPerDim_{dim}\"]\n                    / amr_stats[\"NumElements\"]\n                )\n                min_p = amr_stats[f\"MinPointsPerDim_{dim}\"]\n                max_p = amr_stats[f\"MaxPointsPerDim_{dim}\"]\n                color = colors[dim]\n                fig.add_scatter(\n                    x=amr_stats.index,\n                    y=avg_p,\n                    mode=\"lines\",\n                    name=f\"Avg points per element (d={dim})\",\n                    line=dict(color=color),\n                )\n                fig.add_scatter(\n                    x=amr_stats.index,\n                    y=min_p,\n                    mode=\"lines\",\n                    opacity=0.5,\n                    name=f\"Min points per element (d={dim})\",\n                    line=dict(color=color),\n                )\n                fig.add_scatter(\n                    x=amr_stats.index,\n                    y=max_p,\n                    mode=\"lines\",\n                    opacity=0.5,\n                    name=f\"Max points per element (d={dim})\",\n                    line=dict(color=color),\n                    fill=\"tonexty\",\n                )\n            fig.update_layout(\n                xaxis_title=\"Time [M]\",\n                yaxis_title=\"Points per element\",\n                showlegend=False,\n            )\n            st.plotly_chart(fig)\n        else:\n            fig = px.line(time_steps[\"NumberOfPoints\"])\n            fig.update_layout(\n                xaxis_title=\"Time [M]\",\n                yaxis_title=\"Number of grid points\",\n                showlegend=False,\n            )\n            st.plotly_chart(fig)\n\n    def render_dashboard(self, job: dict, input_file: dict, metadata: dict):\n        return self.render_time_steps(\n            input_file, list_reduction_files(job, input_file)\n        )\n\n\nclass EllipticStatus(ExecutableStatus):\n    \"\"\"An 'ExecutableStatus' subclass that matches all elliptic executables.\n\n    This is a fallback if no more specialized subclass is implemented.\n    \"\"\"\n\n    executable_name_patterns = [r\"^Solve\"]\n    fields = {\n        \"Iteration\": None,\n        \"Residual\": None,\n    }\n\n    def solver_status(\n        self, input_file: dict, open_reductions_file, solver_name: str\n    ) -> dict:\n        \"\"\"Report the residual of the iterative solver\n\n        Arguments:\n          input_file: The input file read in as a dictionary.\n          open_reductions_file: The open h5py reductions data file.\n          solver_name: Name of the solver. Will read the subfile named\n            <SOLVER_NAME>Residuals.dat from the reductions file to extract\n            number of iterations and residual.\n\n        Returns: Status fields \"Iteration\" and \"Residual\".\n        \"\"\"\n        subfile_name = solver_name + \"Residuals.dat\"\n        logger.debug(f\"Reading residuals from subfile: '{subfile_name}'\")\n        try:\n            residuals = to_dataframe(open_reductions_file[subfile_name])\n        except:\n            logger.debug(\"Unable to read residuals.\", exc_info=True)\n            return {}\n        result = {\n            \"Iteration\": len(residuals[residuals[\"Iteration\"] != 0]),\n            \"Residual\": residuals[\"Residual\"].iloc[-1],\n        }\n        return result\n\n    def status(self, input_file, work_dir):\n        try:\n            reductions_file = input_file[\"Observers\"][\"ReductionFileName\"]\n            open_reductions_file = h5py.File(\n                os.path.join(work_dir, reductions_file + \".h5\"), \"r\"\n            )\n        except:\n            logger.debug(\"Unable to open reductions file.\", exc_info=True)\n            return {}\n        with open_reductions_file:\n            # Try these solver names in turn and report the status of the first\n            # that exists. Create a subclass to customize this behavior for\n            # a specific executable (e.g. if it has multiple solvers).\n            for solver_name in [\"NewtonRaphson\", \"Gmres\"]:\n                solver_status = self.solver_status(\n                    input_file, open_reductions_file, solver_name=solver_name\n                )\n                if solver_status:\n                    return solver_status\n        return {}\n\n    def format(self, field, value):\n        if field in [\"Iteration\"]:\n            return str(int(value))\n        elif field in [\"Residual\"]:\n            return f\"{value:.1e}\"\n        raise ValueError\n\n    def render_solver_convergence(self, job: dict, input_file: dict):\n        import plotly.express as px\n        import streamlit as st\n\n        from spectre.Visualization.PlotEllipticConvergence import (\n            plot_elliptic_convergence,\n        )\n\n        st.subheader(\"Elliptic solver convergence\")\n        fig = plt.figure(figsize=(8, 6), layout=\"tight\")\n        plot_elliptic_convergence(\n            Path(job[\"WorkDir\"])\n            / (input_file[\"Observers\"][\"ReductionFileName\"] + \".h5\"),\n            fig=fig,\n        )\n        st.pyplot(fig)\n\n    def render_dashboard(self, job: dict, input_file: dict, metadata: dict):\n        return self.render_solver_convergence(job, input_file)\n"
  },
  {
    "path": "tools/Status/ExecutableStatus/SolveXcts.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport os\nfrom pathlib import Path\n\nimport h5py\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\n\nfrom .ExecutableStatus import EllipticStatus\n\nlogger = logging.getLogger(__name__)\n\n\nclass SolveXcts(EllipticStatus):\n    executable_name_patterns = [r\"^SolveXcts\"]\n    fields = {\n        \"Nonlinear iteration\": None,\n        \"Nonlinear residual\": None,\n        \"Linear iteration\": None,\n        \"Linear residual\": None,\n    }\n\n    def status(self, input_file, work_dir):\n        try:\n            reductions_file = input_file[\"Observers\"][\"ReductionFileName\"]\n            open_reductions_file = h5py.File(\n                os.path.join(work_dir, reductions_file + \".h5\"), \"r\"\n            )\n        except:\n            logger.debug(\"Unable to open reductions file.\", exc_info=True)\n            return {}\n        with open_reductions_file:\n            nonlinear_status = self.solver_status(\n                input_file, open_reductions_file, solver_name=\"NewtonRaphson\"\n            )\n            linear_status = self.solver_status(\n                input_file, open_reductions_file, solver_name=\"Gmres\"\n            )\n        result = {\n            \"Nonlinear iteration\": nonlinear_status[\"Iteration\"],\n            \"Nonlinear residual\": nonlinear_status[\"Residual\"],\n            \"Linear iteration\": linear_status[\"Iteration\"],\n            \"Linear residual\": linear_status[\"Residual\"],\n        }\n        return result\n\n    def format(self, field, value):\n        if \"iteration\" in field:\n            return super().format(\"Iteration\", value)\n        elif \"residual\" in field:\n            return super().format(\"Residual\", value)\n\n    def render_dashboard(self, job: dict, input_file: dict, metadata: dict):\n        import plotly.express as px\n        import streamlit as st\n\n        super().render_solver_convergence(job, input_file)\n\n        # Parameter control\n        control_outfile = Path(job[\"WorkDir\"]) / \"ControlParamsData.txt\"\n        if control_outfile.exists():\n            st.subheader(\"Parameter control\")\n            control_data = np.loadtxt(control_outfile, delimiter=\",\")\n            df = pd.DataFrame(\n                control_data,\n                columns=[\n                    \"Iteration\",\n                    \"Mass A\",\n                    \"Mass B\",\n                    \"Spin A x\",\n                    \"Spin A y\",\n                    \"Spin A z\",\n                    \"Spin B x\",\n                    \"Spin B y\",\n                    \"Spin B z\",\n                    \"Residual Mass A\",\n                    \"Residual Mass B\",\n                    \"Residual Spin A x\",\n                    \"Residual Spin A y\",\n                    \"Residual Spin A z\",\n                    \"Residual Spin B x\",\n                    \"Residual Spin B y\",\n                    \"Residual Spin B z\",\n                ],\n            ).set_index(\"Iteration\")\n            # Print current params\n            params = df.iloc[-1]\n            mass_A, mass_B = params[\"Mass A\"], params[\"Mass B\"]\n            spin_A, spin_B = [\n                np.linalg.norm(params[[f\"Spin {AB} {xyz}\" for xyz in \"xyz\"]])\n                for AB in \"AB\"\n            ]\n            for label, val, col in zip(\n                [\"Mass A\", \"Mass B\", \"Spin A\", \"Spin B\"],\n                [mass_A, mass_B, spin_A, spin_B],\n                st.columns(4),\n            ):\n                col.metric(label, f\"{val:.4g}\")\n            # Plot convergence\n            fig = px.line(\n                np.abs(df[[col for col in df.columns if \"Residual\" in col]]),\n                log_y=True,\n                markers=True,\n            )\n            fig.update_yaxes(exponentformat=\"e\", title=None)\n            st.plotly_chart(fig)\n"
  },
  {
    "path": "tools/Status/ExecutableStatus/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport re\n\nfrom .CharacteristicExtract import CharacteristicExtract\nfrom .EvolveGhBinaryBlackHole import EvolveGhBinaryBlackHole\nfrom .EvolveGhSingleBlackHole import EvolveGhSingleBlackHole\nfrom .ExecutableStatus import EllipticStatus, EvolutionStatus, ExecutableStatus\nfrom .SolveXcts import SolveXcts\n\nlogger = logging.getLogger(__name__)\n\n# Subclasses are matched in this order. Add new subclasses here.\nexecutable_status_subclasses = [\n    CharacteristicExtract,\n    EvolveGhBinaryBlackHole,\n    EvolveGhSingleBlackHole,\n    EvolutionStatus,\n    SolveXcts,\n    EllipticStatus,\n]\n\n\ndef match_executable_status(executable_name: str) -> ExecutableStatus:\n    \"\"\"Get the 'ExecutableStatus' subclass that matches the 'executable_name'.\n\n    The 'executable_name' is matched against all 'executable_name_patterns' in\n    the 'executable_status_subclasses' list. If no pattern matches, a plain\n    'ExecutableStatus' object is returned.\n    \"\"\"\n    for cls in executable_status_subclasses:\n        for pattern in cls.executable_name_patterns:\n            if re.match(pattern, executable_name):\n                return cls()\n    logger.warning(\n        \"No 'ExecutableStatus' subclass matches executable name \"\n        f\"'{executable_name}'. Implement one and add it to \"\n        \"'executable_status_classes'.\"\n    )\n    return ExecutableStatus()\n"
  },
  {
    "path": "tools/Status/Status.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport datetime\nimport glob\nimport logging\nimport os\nimport re\nimport subprocess\nimport time\nfrom io import StringIO\nfrom pathlib import Path\nfrom typing import Any, Optional, Sequence\n\nimport click\nimport humanize\nimport pandas as pd\nimport rich.console\nimport rich.live\nimport rich.rule\nimport rich.table\nimport yaml\n\nfrom spectre.support.DirectoryStructure import Segment\n\nfrom .ExecutableStatus import match_executable_status\n\nlogger = logging.getLogger(__name__)\n\n\ndef fetch_job_data(\n    fields: Sequence[str],\n    user: Optional[str] = None,\n    allusers: bool = False,\n    state: Optional[str] = None,\n    starttime: Optional[str] = None,\n    jobid: Optional[str] = None,\n) -> pd.DataFrame:\n    \"\"\"Query Slurm 'sacct' to get metadata of recent jobs on the machine.\n\n    Arguments:\n      fields: List of Slurm fields that 'sacct --format' accepts.\n        Run 'sacct --helpformat' to print all available fields.\n      user: Slurm user IDs or names, or None for the current user.\n        See documentation for 'sacct -u' for details.\n      allusers: Fetch data for all users.\n        See documentation for 'sacct -a' for details.\n      state: Fetch only jobs with this state.\n        See documentation for 'sacct -s' for details.\n      starttime: Fetch only jobs after this time.\n        See documentation for 'sacct -S' for details.\n      jobid: Fetch only this job ID.\n        See documentation for 'sacct -j' for details.\n\n    Returns: Pandas DataFrame with the job data.\n    \"\"\"\n    completed_process = subprocess.run(\n        [\"sacct\", \"-PX\", \"--format\", \",\".join(fields)]\n        + ([\"-u\", user] if user else [])\n        + ([\"-a\"] if allusers else [])\n        + ([\"-s\", state] if state else [])\n        + ([\"-S\", starttime] if starttime else [])\n        + ([\"-j\", jobid] if jobid else []),\n        capture_output=True,\n        text=True,\n    )\n    try:\n        completed_process.check_returncode()\n    except subprocess.CalledProcessError as err:\n        # Remove invalid fields and retry\n        match = re.search(\n            r'Invalid field requested: \"(.*)\"',\n            completed_process.stderr,\n        )\n        if match:\n            invalid_field = match.group(1)\n            logger.warning(\n                f\"Field '{invalid_field}' is not valid for 'sacct --format'.\"\n                \" Removing it and retrying. Some functionality may be\"\n                \" unavailable. Consider configuring Slurm to support this\"\n                \" field.\"\n            )\n            fields.remove(invalid_field)\n            job_data = fetch_job_data(\n                fields,\n                user=user,\n                allusers=allusers,\n                state=state,\n                starttime=starttime,\n                jobid=jobid,\n            )\n            job_data[invalid_field] = None\n            return job_data\n        else:\n            raise ValueError(completed_process.stderr) from err\n    job_data = pd.read_table(\n        StringIO(completed_process.stdout),\n        sep=\"|\",\n        keep_default_na=False,\n        on_bad_lines=\"warn\",\n    )\n    # Parse dates and times. Do this in postprocessing because\n    # `pd.read_table(parse_dates=...)` doesn't handle NaN values well.\n    date_cols = set(fields).intersection({\"Start\", \"End\"})\n    for date_col in date_cols:\n        job_data[date_col] = job_data[date_col].apply(\n            lambda v: v.replace(\"Unknown\", \"NaN\")\n        )\n        # infer_datetime_format is deprecated in version 2.0.0\n        # so just use the actual SLURM format\n        job_data[date_col] = pd.to_datetime(\n            job_data[date_col], format=\"%Y-%m-%dT%H:%M:%S\"\n        )\n    # We could parse the elapsed time as a timedelta, but the string\n    # representation is fine so we don't right now. Here's the code for it:\n    # if \"Elapsed\" in fields:\n    #     job_data[\"Elapsed\"] = pd.to_timedelta(\n    #         job_data[\"Elapsed\"].apply(lambda v: v.replace(\"-\", \" days \")))\n    return job_data\n\n\ndef get_input_file(comment: Optional[str], work_dir: str) -> Optional[str]:\n    \"\"\"Find the input file corresponding to a job.\n\n    Arguments:\n      comment: The Slurm comment field. The input file is extracted from it if\n        it includes a line like \"SPECTRE_INPUT_FILE=path/to/input/file\".\n      work_dir: The working directory of the job. If no input file was found\n        in the Slurm comment, we see if there's a single YAML file in the\n        work dir and assume that's the input file.\n\n    Returns: Path to the input file, or None.\n    \"\"\"\n    if comment:\n        # Get the input file from the Slurm comment if the submission\n        # specified it\n        match = re.search(\n            r\"^SPECTRE_INPUT_FILE=(.*)\", comment, flags=re.MULTILINE\n        )\n        if match:\n            return os.path.join(work_dir, match.group(1))\n        else:\n            logger.debug(\n                \"Could not find 'SPECTRE_INPUT_FILE' in Slurm comment:\\n\"\n                + comment\n            )\n    # Fallback: Get the input file from the scheduler context file if present\n    context_file = Path(work_dir) / \"SchedulerContext.yaml\"\n    try:\n        with open(context_file, \"r\") as open_context_file:\n            input_file = yaml.safe_load(open_context_file)[\"input_file\"]\n            return os.path.join(work_dir, input_file)\n    except:\n        logger.debug(f\"Failed to read file '{context_file}'.\", exc_info=True)\n    # Fallback: Check if there's a single YAML file in the work dir\n    yaml_files = glob.glob(os.path.join(work_dir, \"*.yaml\"))\n    if len(yaml_files) == 1:\n        try:\n            open(yaml_files[0], \"r\").close()\n        except PermissionError:\n            logger.debug(\n                f\"Unable to open potential input file: {yaml_files[0]}.\",\n                exc_info=True,\n            )\n            return None\n        return yaml_files[0]\n    else:\n        logger.debug(\n            \"No input file found. \"\n            f\"Didn't find a single YAML file in '{work_dir}'. \"\n            f\"YAML files found: {yaml_files}\"\n        )\n        return None\n\n\ndef get_executable_name(\n    comment: Optional[str], input_file_path: Optional[str]\n) -> Optional[str]:\n    \"\"\"Determine the executable name of a job.\n\n    Arguments:\n      comment: The Slurm comment field. The executable name is extracted from it\n        if it includes a line like \"SPECTRE_EXECUTABLE=path/to/executable\".\n      input_file_path: Path to input file. If no executable name was found in\n        the Slurm comment, we try to extract it from the input file.\n\n    Returns: Executable name, or None.\n    \"\"\"\n    if comment:\n        # Get the executable from the Slurm comment if the submission\n        # specified it\n        match = re.search(\n            r\"^SPECTRE_EXECUTABLE=(.*)\", comment, flags=re.MULTILINE\n        )\n        if match:\n            return os.path.basename(match.group(1))\n        else:\n            logger.debug(\n                \"Could not find 'SPECTRE_EXECUTABLE' in Slurm comment:\\n\"\n                + comment\n            )\n    # Fallback: See if the executable is specified in the input file metadata\n    if not input_file_path:\n        return None\n    try:\n        with open(input_file_path, \"r\") as open_input_file:\n            metadata = next(yaml.safe_load_all(open_input_file))\n    except yaml.YAMLError:\n        logger.debug(\n            f\"Failed loading YAML file '{input_file_path}'.\", exc_info=True\n        )\n        metadata = None\n    if metadata and \"Executable\" in metadata:\n        return os.path.basename(metadata[\"Executable\"])\n    # Backwards compatibility for input files without metadata (can be removed\n    # as soon as most people have rebased)\n    match = re.search(\n        r\"#\\s+Executable:\\s+(.+)\", Path(input_file_path).read_text()\n    )\n    if match:\n        return match.group(1)\n    return None\n\n\ndef get_error_message(std_err: Optional[str], work_dir: str) -> Optional[str]:\n    \"\"\"Determine the error message of a job.\n\n    Arguments:\n      std_err: The standard error output file path, can be relative to work_dir.\n      work_dir: The working directory of the job. If no std_err was given, we\n        look for a file named \"spectre.out\" in the work dir.\n\n    Returns: Error message, or None.\n    \"\"\"\n    if std_err:\n        outfile = Path(work_dir) / std_err\n    else:\n        outfile = Path(work_dir) / \"spectre.out\"\n    if not outfile.exists():\n        logger.debug(f\"No error output file found at '{outfile}'.\")\n        return None\n    with open(outfile, \"r\") as open_outfile:\n        # Go through the file line by line and find the first error message\n        error_message = []\n        found_error = False\n        found_walltime = False\n        found_message = False\n        for line in open_outfile:\n            if \"# ERROR #\" in line:\n                if found_error:\n                    # Stop before the next error message\n                    break\n                else:\n                    found_error = True\n                    continue\n            if found_error and \"Wall time:\" in line:\n                found_walltime = True\n                continue\n            if found_walltime and line.strip() == \"\":\n                found_message = True\n                continue\n            if found_message and \"Captured variables:\" in line:\n                break\n            if found_message:\n                error_message.append(line)\n    return \"\\n\".join(error_message).strip() if found_message else None\n\n\ndef _state_order(state):\n    order = [\n        \"RUNNING\",\n        \"PENDING\",\n        \"COMPLETED\",\n        \"TIMEOUT\",\n        \"FAILED\",\n        \"CANCELLED\",\n    ]\n    try:\n        return order.index(state)\n    except ValueError:\n        return None\n\n\ndef _format(field: str, value: Any, state_styles: dict = {}) -> str:\n    # The SLURM field \"NodeList\" returns \"None assigned\" if a job is pending\n    if pd.isnull(value) or value == \"None assigned\":\n        return \"-\"\n    elif field == \"State\":\n        style = {\n            \"RUNNING\": \"[blue]\",\n            \"COMPLETED\": \"[green]\",\n            \"PENDING\": \"[magenta]\",\n            \"FAILED\": \"[red]\",\n            \"TIMEOUT\": \"[red]\",\n            \"CANCELLED\": \"[red]\",\n        }\n        style.update(state_styles)\n        return style.get(value, \"\") + str(value)\n    elif field in [\"Start\", \"End\"]:\n        return humanize.naturaldate(value) + \" \" + value.strftime(\"%X\")\n    elif field == \"Segment\":\n        return str(int(value)).zfill(Segment.NUM_DIGITS)\n    else:\n        return str(value)\n\n\n# Dictionary of available columns to print. Key is the SLURM name, and the value\n# is the human-readable name. These are sometimes the same\nAVAILABLE_COLUMNS = {\n    \"State\": \"State\",\n    \"Partition\": \"Queue\",\n    \"End\": \"End\",\n    \"User\": \"User\",\n    \"JobID\": \"JobID\",\n    \"JobName\": \"JobName\",\n    \"SegmentId\": \"Segment\",\n    \"Elapsed\": \"Elapsed\",\n    \"NCPUS\": \"Cores\",\n    \"NNodes\": \"Nodes\",\n    \"NodeList\": \"NodeList\",\n    \"WorkDir\": \"WorkDir\",\n    \"Comment\": \"Comment\",\n    \"StdOut\": \"StdOut\",\n    \"StdErr\": \"StdErr\",\n}\n\nDEFAULT_COLUMNS = [\n    \"State\",\n    \"End\",\n    \"User\",\n    \"JobID\",\n    \"JobName\",\n    \"Elapsed\",\n    \"Cores\",\n    \"Nodes\",\n]\n\n\ndef fetch_job_data_from_run_dir(run_dir: str, fields: Sequence[str]) -> dict:\n    jobid_path = Path(run_dir) / \"jobid.txt\"\n    if jobid_path.exists():\n        jobid = jobid_path.read_text().strip()\n        return fetch_job_data(fields, jobid=jobid)\n    else:\n        return pd.DataFrame([dict(WorkDir=run_dir)])\n\n\ndef input_column_callback(ctx, param, value):\n    result = value.split(\",\") if value else []\n    result = [x.strip() for x in result]\n\n    for input_key in result:\n        if input_key not in AVAILABLE_COLUMNS.values():\n            raise click.BadParameter(\n                f\"Input column '{input_key}' not in available columns (case\"\n                f\" sensitive) {list(AVAILABLE_COLUMNS.values())}\"\n            )\n\n    return result\n\n\ndef fetch_status(\n    show_deleted=False,\n    show_all_segments=False,\n    extra_run_dirs: Sequence[str] = [],\n    **kwargs,\n):\n    \"\"\"Fetches job data and processes it for display.\n\n    See 'fetch_job_data' for how jobs are fetched. This function processes the\n    data to make it more human-readable and filters it, e.g. related to deleted\n    jobs and segments.\n\n    Arguments:\n      show_deleted: Show jobs that ran in directories that are now deleted.\n        (default: False)\n      show_all_segments: Show all segments as individual jobs instead of just\n        the latest. (default: False)\n      kwargs: Arguments to pass to 'fetch_job_data'.\n\n    Returns: Pandas DataFrame with the job data sorted by JobID (most recently\n      submitted job first).\n    \"\"\"\n    columns_to_fetch = list(AVAILABLE_COLUMNS)\n    columns_to_fetch.remove(\"SegmentId\")\n\n    job_data = fetch_job_data(\n        columns_to_fetch,\n        **kwargs,\n    ).convert_dtypes()\n\n    # Add extra run directories\n    for run_dir in extra_run_dirs:\n        extra_job_data = fetch_job_data_from_run_dir(run_dir, columns_to_fetch)\n        job_data = pd.concat([job_data, extra_job_data], ignore_index=True)\n    job_data.fillna({\"Comment\": \"\", \"State\": \"N/A\"}, inplace=True)\n\n    # Do nothing if job list is empty\n    if len(job_data) == 0:\n        return job_data\n\n    # Remove deleted jobs\n    if not show_deleted:\n        deleted_jobs = job_data[~job_data[\"WorkDir\"].map(os.path.exists)]\n        job_data.drop(deleted_jobs.index, inplace=True)\n        if len(job_data) == 0:\n            return\n\n    # Keep only latest in a series of segments\n    job_data[[\"SegmentsDir\", \"SegmentId\"]] = [\n        (\n            (str(segment.path.resolve().parent), segment.id)\n            if segment\n            else (None, None)\n        )\n        for segment in job_data[\"WorkDir\"].map(Segment.match)\n    ]\n    if not show_all_segments:\n        for segments_dir in pd.unique(job_data[\"SegmentsDir\"]):\n            if not segments_dir:\n                continue\n            segments_jobs = job_data[job_data[\"SegmentsDir\"] == segments_dir]\n            if len(segments_jobs) == 1:\n                continue\n            latest_segment_id = sorted(segments_jobs[\"SegmentId\"])[-1]\n            drop_segment_jobs = job_data[\n                (job_data[\"SegmentsDir\"] == segments_dir)\n                & (job_data[\"SegmentId\"] != latest_segment_id)\n            ]\n            job_data.drop(drop_segment_jobs.index, inplace=True)\n        if len(job_data) == 0:\n            return\n\n    # Rename columns from SLURM name to user name\n    job_data.rename(columns=AVAILABLE_COLUMNS, inplace=True)\n\n    # List most recent jobs first\n    job_data.sort_values(\"JobID\", inplace=True, ascending=False)\n\n    # Get the input file corresponding to each job\n    job_data[\"InputFile\"] = [\n        get_input_file(comment, work_dir)\n        for comment, work_dir in zip(job_data[\"Comment\"], job_data[\"WorkDir\"])\n    ]\n\n    # Get the executable name corresponding to each job.\n    job_data[\"ExecutableName\"] = [\n        get_executable_name(comment, input_file)\n        for comment, input_file in zip(\n            job_data[\"Comment\"], job_data[\"InputFile\"]\n        )\n    ]\n\n    # Get the error message corresponding to each job.\n    job_data[\"ErrorMessage\"] = [\n        get_error_message(std_err, work_dir)\n        for std_err, work_dir in zip(job_data[\"StdErr\"], job_data[\"WorkDir\"])\n    ]\n\n    # Invalidate older jobs that ran in the same work dir because they would\n    # report wrong information (that of the newer job).\n    for work_dir in pd.unique(job_data[\"WorkDir\"]):\n        duplicate_jobs = job_data[job_data[\"WorkDir\"] == work_dir]\n        if len(duplicate_jobs) == 1:\n            continue\n        # Jobs are already sorted by JobID, so just keep the latest job\n        # associated and invalidate the others.\n        latest_job_id = duplicate_jobs.iloc[0][\"JobID\"]\n        job_data.loc[\n            (job_data[\"WorkDir\"] == work_dir)\n            & (job_data[\"JobID\"] != latest_job_id),\n            [\"WorkDir\", \"NewJobID\"],\n        ] = [None, latest_job_id]\n\n    # Normalize \"cancelled\" job state (remove \"by X\")\n    job_data.loc[job_data[\"State\"].str.contains(\"CANCELLED\"), \"State\"] = (\n        \"CANCELLED\"\n    )\n\n    # Add metadata so jobs can be grouped by state\n    job_data[\"StateOrder\"] = job_data[\"State\"].apply(_state_order)\n\n    return job_data\n\n\n@rich.console.group()\ndef render_status(\n    show_paths,\n    show_unidentified,\n    state_styles,\n    columns,\n    **kwargs,\n):\n    job_data = fetch_status(**kwargs)\n\n    # Do nothing if job list is empty\n    if len(job_data) == 0:\n        return\n\n    # Remove the user column unless we're specifying all users\n    if not kwargs[\"allusers\"]:\n        if \"User\" in columns:\n            columns.remove(\"User\")\n\n    # Group output by executable\n    first_section = True\n    for executable_name, exec_data in job_data.groupby(\"ExecutableName\"):\n        if first_section:\n            first_section = False\n        else:\n            yield \"\"\n        yield rich.rule.Rule(f\"[bold]{executable_name}\", align=\"left\")\n        yield \"\"\n        executable_status = match_executable_status(executable_name)\n\n        extra_columns = [\n            (field + f\" [{unit}]\") if unit else field\n            for field, unit in executable_status.fields.items()\n        ]\n        table_headers = columns + extra_columns\n        table = rich.table.Table(*table_headers, box=None)\n\n        # Group by job state\n        for state_index, data in exec_data.groupby(\"StateOrder\"):\n            for _, row in data.iterrows():\n                # Extract job status and format row for output\n                row_formatted = [\n                    _format(field, row[field], state_styles)\n                    for field in columns\n                ]\n                try:\n                    with open(row[\"InputFile\"], \"r\") as open_input_file:\n                        # Backwards compatibility for input files without\n                        # metadata. Once people have rebased this can be changed\n                        # to: _, input_file = yaml.safe_load_all(...)\n                        for doc in yaml.safe_load_all(open_input_file):\n                            input_file = doc\n                except:\n                    logger.debug(\"Unable to load input file.\", exc_info=True)\n                    input_file = None\n                try:\n                    status = executable_status.status(\n                        input_file, row[\"WorkDir\"]\n                    )\n                except:\n                    logger.debug(\n                        \"Unable to extract executable status.\", exc_info=True\n                    )\n                    status = {}\n                row_formatted += [\n                    (\n                        executable_status.format(field, status[field])\n                        if field in status\n                        else \"-\"\n                    )\n                    for field in executable_status.fields.keys()\n                ]\n                table.add_row(*row_formatted)\n\n                # Print paths if requested\n                if show_paths:\n                    yield table\n                    # Print WorkDir in its own line so it wraps nicely in the\n                    # terminal and can be copied\n                    yield (\n                        \" [bold]WorkDir:[/bold] \"\n                        + (\n                            row[\"WorkDir\"]\n                            if row[\"WorkDir\"]\n                            else (\n                                \"[italic]Same as job\"\n                                f\" {row['NewJobID']}[/italic]\"\n                            )\n                        )\n                    )\n                    yield \" [bold]InputFile:[/bold] \" + str(row[\"InputFile\"])\n                    table = rich.table.Table(*table_headers, box=None)\n        if not show_paths:\n            yield table\n\n    # Output jobs that couldn't be parsed\n    unidentified_jobs = job_data[job_data[\"ExecutableName\"].isnull()]\n    if len(unidentified_jobs) > 0 and show_unidentified:\n        yield \"\"\n        yield rich.rule.Rule(\"[bold]Unidentified Jobs\", align=\"left\")\n        yield \"\"\n        table = rich.table.Table(*columns, box=None)\n        for i, row in unidentified_jobs.iterrows():\n            row_formatted = [\n                _format(field, row[field], state_styles) for field in columns\n            ]\n            table.add_row(*row_formatted)\n        yield table\n\n\n@click.command(name=\"status\")\n@click.option(\n    \"-u\",\n    \"--uid\",\n    \"--user\",\n    \"user\",\n    show_default=\"you\",\n    help=\"User name or user ID. See documentation for 'sacct -u' for details.\",\n)\n@click.option(\n    \"-a\",\n    \"--allusers\",\n    is_flag=True,\n    help=(\n        \"Show jobs for all users. See documentation for 'sacct -a' for details.\"\n    ),\n)\n@click.option(\n    \"-p\",\n    \"--show-paths\",\n    is_flag=True,\n    help=\"Show job working directory and input file paths.\",\n)\n@click.option(\n    \"-U\",\n    \"--show-unidentified\",\n    is_flag=True,\n    help=\"Also show jobs that were not identified as SpECTRE executables.\",\n)\n@click.option(\n    \"-D\",\n    \"--show-deleted\",\n    is_flag=True,\n    help=\"Also show jobs that ran in directories that are now deleted.\",\n)\n@click.option(\n    \"-A\",\n    \"--show-all-segments\",\n    is_flag=True,\n    help=\"Show all segments instead of just the latest.\",\n)\n@click.option(\n    \"-s\",\n    \"--state\",\n    help=(\n        \"Show only jobs with this state, \"\n        \"e.g. running (r) or completed (cd). \"\n        \"See documentation for 'sacct -s' for details.\"\n    ),\n)\n@click.option(\n    \"-S\",\n    \"--starttime\",\n    show_default=\"start of today\",\n    help=(\n        \"Show jobs eligible after this time, e.g. 'now-2days'. \"\n        \"See documentation for 'sacct -S' for details.\"\n    ),\n)\n@click.option(\n    \"-w\",\n    \"--watch\",\n    \"refresh_rate\",\n    type=float,\n    default=None,\n    help=(\n        \"On a new screen, refresh jobs every 'watch' number \"\n        \"of seconds. Exit out with Ctl+C.\"\n    ),\n)\n@click.option(\n    \"--state-styles\",\n    type=dict,\n    default={},\n    help=(\n        \"Dictionary between sacct states and rich modifiers for \"\n        \"how the state will be printed. Rather than always having \"\n        \"to specify a dict on the command line, you can add this \"\n        \"to the spectre config file. \\n\\n\"\n        \"An example for the config file would be\\n\\n\"\n        \"\\b\\n\"\n        \"status:\\n\"\n        \"  state_styles:\\n\"\n        \"    RUNNING: '[green]'\\n\"\n        \"    COMPLETED: '[bold][red]'\\n\\n\"\n        \"See `spectre -h` for its path.\"\n    ),\n)\n@click.option(\n    \"--columns\",\n    \"-c\",\n    callback=input_column_callback,\n    default=\",\".join(DEFAULT_COLUMNS),\n    show_default=True,\n    help=(\n        \"List of columns that will be printed for all jobs (executable specific\"\n        \" columns will be added in addition to this list). This can also be\"\n        \" specified in the config file with the name 'columns'. Note\"\n        \" that if the '--allusers'  option is not specified, then the \\\"User\\\"\"\n        \" column will be omitted. Specify the columns as a comma separated\"\n        \" list: State,JobId,Nodes . If you want to have spaces in the list,\"\n        \" wrap it in single quotes: 'State, JobId, Nodes'\"\n    ),\n)\n@click.option(\n    \"--available-columns\",\n    \"-e\",\n    is_flag=True,\n    help=\"Print a list of all available columns to use.\",\n)\ndef status_command(refresh_rate, available_columns, **kwargs):\n    \"\"\"Gives an overview of simulations running on this machine.\"\"\"\n\n    if available_columns:\n        rich.print(rich.columns.Columns(AVAILABLE_COLUMNS.values()))\n        return\n\n    # Start printing things\n    console = rich.console.Console()\n\n    if not refresh_rate:\n        console.print(render_status(**kwargs))\n        return\n\n    try:\n        logging.disable()\n        with rich.live.Live(\n            render_status(**kwargs),\n            console=console,\n            auto_refresh=False,\n            screen=True,\n        ) as live:\n            while True:\n                time.sleep(refresh_rate)\n                live.update(render_status(**kwargs), refresh=True)\n    except KeyboardInterrupt:\n        pass\n"
  },
  {
    "path": "tools/Status/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nfrom .Status import status_command\n"
  },
  {
    "path": "tools/SuppressionsCppCheck.txt",
    "content": "// Distributed under the MIT License.\n// See LICENSE.txt for details.\n\n// Ignore non-C++ files and some compilation testing infrastructure\n// files that can't be compiled\n*:*.f\n*:*tests/Unit/TestCompilationFramework.cpp\n\n// Suppress unmatched suppressions\nunmatchedSuppression:*\n"
  },
  {
    "path": "tools/ValidateInputFile.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport logging\nimport re\nimport subprocess\nfrom pathlib import Path\nfrom typing import Optional, Sequence, Union\n\nimport click\nimport rich\nimport rich.console\nimport rich.syntax\nimport yaml\n\nfrom spectre.support.Machines import this_machine\n\nlogger = logging.getLogger(__name__)\n\n\nclass InvalidInputFileError(Exception):\n    \"\"\"Indicates that the input file cannot be parsed by the executable\n\n    The exception prints the 'message' by default. To print additional context,\n    catch the exception and print the 'render_context()' as well.\n\n    Attributes:\n      input_file_path: Path to the input file on disk.\n      line_number: Line number in the input file where the parse error occurred.\n      yaml_path: Sequence of YAML keys in the input file that lead to the\n        parse error, such as '[\"DomainCreator\", \"Interval\", \"LowerBound\"]'.\n      message: Error message emitted by the option parsing.\n    \"\"\"\n\n    def __init__(\n        self,\n        input_file_path: Union[str, Path],\n        line_number: Optional[int],\n        yaml_path: Sequence[str],\n        message: Optional[str],\n    ):\n        self.input_file_path = Path(input_file_path)\n        self.line_number = line_number\n        self.yaml_path = yaml_path\n        self.message = message\n        super().__init__(message)\n\n    @rich.console.group()\n    def render_context(self) -> rich.console.RenderResult:\n        if self.line_number is not None:\n            yield f\"{self.input_file_path.resolve()}:{self.line_number}\"\n            yield rich.syntax.Syntax(\n                self.input_file_path.read_text(),\n                theme=\"ansi_dark\",\n                lexer=\"yaml\",\n                line_range=(self.line_number - 2, self.line_number + 2),\n                highlight_lines={self.line_number},\n                line_numbers=True,\n            )\n            yield \"\"\n        if self.yaml_path:\n            yield \"In [bold]\" + \".\".join(self.yaml_path) + \":\"\n            yield \"\"\n\n\ndef validate_input_file(\n    input_file_path: Union[str, Path],\n    executable: Optional[Union[str, Path]] = None,\n    work_dir: Optional[Union[str, Path]] = None,\n    print_context: bool = True,\n    raise_exception: bool = True,\n):\n    \"\"\"Check an input file for parse errors\n\n    Invokes the executable with the '--check-options' flag to check for parse\n    errors.\n\n    Arguments:\n      input_file_path: Path to the input file on disk.\n      executable: Name or path of the executable. If unspecified, use the\n        'Executable:' in the input file metadata.\n      work_dir: Working directory for invoking the executable with the input\n        file. Relative paths in the input file are resolved from here.\n      print_context: Print additional context where the parse error occurred\n        before raising an exception (default: True).\n      raise_exception: Raise an 'InvalidInputFileError' if a parse error\n        occurred (default: True).\n    \"\"\"\n    input_file_path = Path(input_file_path)\n\n    # Resolve the executable\n    if not executable:\n        with open(input_file_path, \"r\") as open_input_file:\n            executable = next(yaml.safe_load_all(open_input_file))[\"Executable\"]\n\n    # Use the executable to validate the input file\n    machine = this_machine(raise_exception=False)\n    process = subprocess.run(\n        (machine.launch_command if machine else [])\n        + [executable, \"--input-file\", input_file_path, \"--check-options\"],\n        capture_output=True,\n        text=True,\n        cwd=work_dir,\n    )\n\n    if process.returncode == 0:\n        return\n\n    # Parse the validation error\n    logger.debug(process.stderr)\n    found_hints = False\n    path = []\n    line_number = None\n    msg = []\n    for line in process.stderr.split(\"\\n\"):\n        if str(input_file_path) in line:\n            found_hints = True\n            continue\n        if found_hints:\n            if line_number is None:\n                if line.startswith(\"In group\"):\n                    path.append(line[9:-1])\n                    continue\n                elif line.startswith(\"While parsing option\"):\n                    path.append(line[21:-1])\n                    continue\n                elif line.startswith(\"While creating a\"):\n                    path.append(line[17:-1])\n                    continue\n                elif line.startswith(\"At line\"):\n                    match = re.match(r\"At line ([0-9]+) column ([0-9]+)\", line)\n                    line_number, _ = map(int, match.groups())\n                    continue\n                elif line.startswith(\"While operating factory for\"):\n                    # remove \"unique_ptr\" entry from path\n                    path.pop()\n                    continue\n            if \"# ERROR #\" in line:\n                break\n            else:\n                msg.append(line)\n\n    # Print context and raise exception\n    error = InvalidInputFileError(\n        input_file_path=input_file_path,\n        line_number=line_number,\n        yaml_path=path,\n        message=(\n            \"\\n\".join(msg).strip() if found_hints else process.stderr.strip()\n        ),\n    )\n    if print_context:\n        rich.print(error.render_context())\n    if raise_exception:\n        _rich_traceback_guard = True\n        raise error\n\n\n@click.command(name=\"validate\")\n@click.argument(\n    \"input_file_path\",\n    type=click.Path(\n        exists=True,\n        file_okay=True,\n        dir_okay=False,\n        readable=True,\n        path_type=Path,\n    ),\n)\n@click.option(\n    \"--executable\",\n    \"-E\",\n    help=(\n        \"Name or path of the executable. \"\n        \"If unspecified, the 'Executable:' in the input file \"\n        \"metadata is used.\"\n    ),\n)\ndef validate_input_file_command(**kwargs):\n    \"\"\"Check an input file for parse errors\"\"\"\n    validate_input_file(**kwargs)\n    rich.print(f\"Input file '{kwargs['input_file_path']}' parsed successfully.\")\n\n\nif __name__ == \"__main__\":\n    validate_input_file_command(help_option_names=[\"-h\", \"--help\"])\n"
  },
  {
    "path": "tools/WrapExecutableLinker.sh",
    "content": "#!/bin/bash -e\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\ntemp_files=()\ntrap 'rm -rf \"${temp_files[@]}\"' EXIT\n\npushd @CMAKE_SOURCE_DIR@ >/dev/null\ngit_description=\"@GIT_DESCRIPTION_COMMAND@\"\ngit_branch=\"@GIT_BRANCH_COMMAND@\"\nif [ ! -z \"${git_description}\" ]; then\n    git_description=`${git_description}`\nfi\nif [ ! -z \"${git_branch}\" ]; then\n    git_branch=`${git_branch}`\nfi\npopd >/dev/null\n\n# Find the index of \"-o\" in the argument list\noindex=\nfor (( i=1 ; i<$# ; ++i )) ; do\n    [ \"${!i}\" = -o ] && { oindex=$i ; break ; }\ndone\nif [ -z \"${oindex}\" ] ; then\n    echo \"$0: Can't find -o option\" >&2\n    exit 1\nfi\n# Construct a filename from the next argument\nlet ++oindex\nexecutable_name=$(basename \"${!oindex}\")\n\n# We compile the InfoAtLink.cpp file into the executable at link time\nInfoAtLink_file=@CMAKE_SOURCE_DIR@/src/Informer/InfoAtLink.cpp\n# Read the appropriate flags for compiling InfoAtLink.cpp from the generated\n# file\nInfoAtLink_flags=`cat @CMAKE_BINARY_DIR@/Informer/InfoAtLink_flags.txt`\n# Compile into an object file\nInfoAtLink_ofile=@CMAKE_BINARY_DIR@/tmp/${executable_name}_InfoAtLink.cpp.o\n@CMAKE_CXX_COMPILER@ -c ${InfoAtLink_file} \\\n    -DGIT_DESCRIPTION=$git_description -DGIT_BRANCH=$git_branch \\\n    -DLINK_TIME=\"$(date -u -Iseconds)\" ${InfoAtLink_flags} \\\n    -DEXECUTABLE_NAME=\"${executable_name}\" \\\n    -o ${InfoAtLink_ofile}\ntemp_files+=(\"${InfoAtLink_ofile}\")\n\n# We inject the source code archive into the executable at link time\n# by compiling it into an object file (formaline).\n# - Formaline through the linker doesn't work on macOS and since we won't\n#   be doing production runs on macOS we disable it.\nif [ -f @CMAKE_BINARY_DIR@/tmp/Formaline.sh ]; then\n    . @CMAKE_BINARY_DIR@/tmp/Formaline.sh ${executable_name}\n    formaline_ofile=@CMAKE_BINARY_DIR@/tmp/${executable_name}_Formaline.cpp.o\n    @CMAKE_CXX_COMPILER@ -c ${formaline_output} ${InfoAtLink_flags} \\\n        -o ${formaline_ofile}\n    temp_files+=(\"${formaline_output}\" \"${formaline_object_output}\" \\\n        \"${formaline_ofile}\")\n    \"$@\" ${InfoAtLink_ofile} ${formaline_ofile} ${formaline_object_output}\nelse\n    \"$@\" ${InfoAtLink_ofile}\nfi\n\nif @WRAP_EXECUTABLE_LINKER_USE_STUB_OBJECT_FILES@; then\n    # When linking, mv the object files to a temporary file (this\n    # preserves times), then use touch to create a new empty\n    # stub object file with the same times (atime and mtime) as\n    # the original object file, except that it is 0kB\n    for file in \"$@\"\n    do\n        if [[ $file =~ .*\\.cpp\\.o ]]; then\n            temp_file=\"${file}.tmp_to_eliminate_size\"\n            mv \"${file}\" \"${temp_file}\"\n            touch -r \"${temp_file}\" \"${file}\"\n            rm \"${temp_file}\"\n        fi\n    done\nfi\n"
  },
  {
    "path": "tools/WrapLibraryLinker.sh",
    "content": "#!/bin/bash -e\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n\"$@\"\n\nif @WRAP_LIBRARY_LINKER_USE_STUB_OBJECT_FILES@; then\n    # When linking, mv the object files to a temporary file (this\n    # preserves times), then use touch to create a new empty\n    # stub object file with the same times (atime and mtime) as\n    # the original object file, except that it is 0kB\n    for file in \"$@\"\n    do\n        if [[ $file =~ .*\\.cpp\\.o ]]; then\n            temp_file=\"${file}.tmp_to_eliminate_size\"\n            mv \"${file}\" \"${temp_file}\"\n            touch -r \"${temp_file}\" \"${file}\"\n            rm \"${temp_file}\"\n        fi\n    done\nfi\n"
  },
  {
    "path": "tools/__init__.py",
    "content": "# Distributed under the MIT License.\n# See LICENSE.txt for details.\n"
  },
  {
    "path": "tools/latex2dox.py",
    "content": "#!/usr/bin/env python\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nimport re\n\n\ndef parse_file(input_file_name, output_file_name):\n    \"\"\"\n    Parses a LaTeX file and replaces most tex commands with ones\n    compatible with Doxygen and MathJAX\n    \"\"\"\n    tex_string = open(input_file_name, \"r\").read()\n    # Replace equations, etc.\n    out_string = re.sub(\n        \"\\\\\\\\begin{(align\\*?|equation\\*?|eqnarray\\*?)}\", r\"\\\\f{\\1}{\", tex_string\n    )\n    out_string = re.sub(\n        \"\\\\\\\\end{(align\\*?|equation\\*?|eqnarray\\*?)}\", r\"\\\\f}\", out_string\n    )\n    # Replace section and subsection\n    out_string = re.sub(\"\\\\\\\\section{([^}]+)}\", r\"## \\1\", out_string)\n    out_string = re.sub(\"\\\\\\\\subsection{([^}]+)}\", r\"### \\1\", out_string)\n    # Get inline math to work\n    out_string = re.sub(\"([^\\\\\\\\])\\$\", r\"\\1\\\\f$\", out_string)\n    # Wrap \\ref in math to get references to work\n    out_string = re.sub(\"(\\\\\\\\(eq)?ref{[^}]+})\", r\"\\\\f$\\1\\\\f$\", out_string)\n    # Remove all comment lines\n    out_string = re.sub(\"([^\\\\\\\\])%.*\", r\"\\1\\n\", out_string)\n    # Get newcommand from tex file and add them to the output\n    command_string = \"\"\n    for newcommand in re.findall(\"(\\\\\\\\newcommand.*)\\n\", tex_string):\n        command_string = \"%s\\\\f$%s\\\\f$\\n\" % (command_string, newcommand)\n\n    # Extract contents inside document\n    is_doc = re.search(\n        re.compile(\"\\\\\\\\begin{document}(.*)\\\\\\\\end{document}\", re.DOTALL),\n        out_string,\n    )\n    if is_doc:\n        out_string = is_doc.group(1)\n\n    out_string = \"%s\\n%s\" % (command_string, out_string)\n    open(output_file_name, \"w\").write(out_string)\n\n\ndef parse_args():\n    \"\"\"\n    Parse the command line arguments\n    \"\"\"\n    import argparse as ap\n\n    parser = ap.ArgumentParser(\n        description=(\n            \"Do a simple conversion from LaTeX to Doxygen comment \"\n            \"using MathJAX. The result will likely need some additional \"\n            \"tweaking but the goal of this script is to automate the majority\"\n            \" of the work.\"\n        ),\n        formatter_class=ap.ArgumentDefaultsHelpFormatter,\n    )\n    parser.add_argument(\n        \"--tex-file\", required=True, help=\"The name LaTeX file to process\"\n    )\n    parser.add_argument(\n        \"--output-file\",\n        required=True,\n        help=\"The name of the file to write the output to\",\n    )\n    return vars(parser.parse_args())\n\n\nif __name__ == \"__main__\":\n    input_args = parse_args()\n    parse_file(input_args[\"tex_file\"], input_args[\"output_file\"])\n"
  },
  {
    "path": "tools/llvm-gcov.sh",
    "content": "#!/bin/bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nexec @LLVM_COV_BIN@ gcov \"$@\"\n"
  },
  {
    "path": "tools/patch_charm_modules.sh",
    "content": "#!/usr/bin/env bash\n\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\n# Applies any patches that are present in $3/tools/CharmModulePatches/ to\n# their corresponding .def.h and decl.h files in the charm module outputs.\n# This is used to override default charm behavior with custom versions or to\n# patch bugs\n#\n# You can create a patch by running:\n#\n# git diff file_original file_update > file.patch\n#\n# This works even outside a git repository.\n\nset -e\n\nif [ \"$#\" -ne 5 ]; then\n    echo \"Usage: patch_charm_modules.sh module_name path_to_current_source_dir\\\n path_to_source_root_dir path_to_current_build_dir version_suffix\"\n    exit 1\nfi\n\nPATCH_PREFIX=$3/tools/CharmModulePatches${2#\"$3\"}\nDEF_PATCH_FILENAME=\"$PATCH_PREFIX\"/$1$5.def.h.patch\nDECL_PATCH_FILENAME=\"$PATCH_PREFIX\"/$1$5.decl.h.patch\n\nif [ -f \"$DEF_PATCH_FILENAME\" ]; then\n    patch -u $4/$1.def.h -i $DEF_PATCH_FILENAME --quiet\nfi\n\nif [ -f \"$DECL_PATCH_FILENAME\" ]; then\n    patch -u $4/$1.decl.h -i $DECL_PATCH_FILENAME --quiet\nfi\n"
  },
  {
    "path": "tools/pr-docs",
    "content": "#!/bin/bash -e\n# Distributed under the MIT License.\n# See LICENSE.txt for details.\n\nusage() {\n    cat <<EOF\nUsage: $0 pr-number output/location\n\nRender documentation from a pull request and write the result to the\nspecified location.  This script must be run from within a SpECTRE repo.\nEOF\n}\n\nif [ \"$1\" = -h ] ; then\n    usage\n    exit 0\nfi\n\nif [ \"$#\" -ne 2 ] ; then\n    usage >&2\n    exit 1\nfi\n\npr=$1\noutdir=$2\n\n# Linux || OSX\ntmpdir=$(mktemp -d --tmpdir 2>/dev/null || mktemp -d -t spectre-doc)\ntrap 'rm -rf \"${tmpdir}\"' EXIT\n\nspectre=$(git rev-parse --show-toplevel 2>/dev/null) || {\n    echo \"Must run from a SpECTRE git working tree\" >&2\n    exit 1\n}\n\npushd \"${tmpdir}\" >/dev/null\n# Clone from the local SpECTRE repo to avoid having to redownload\n# everything.\ngit clone --shared --quiet --no-checkout -- \"${spectre}\" source\npushd source >/dev/null\ngit fetch https://github.com/sxs-collaboration/spectre pull/\"${pr}\"/head\ngit checkout --quiet FETCH_HEAD\npopd >/dev/null\ncmake -D DOCS_ONLY=TRUE source\n# Ignore doxygen failures.  We want them to be displayed, but we still\n# usually get usable documentation.\nmake doc-check || :\nrm -rf \"${outdir}\"\npopd >/dev/null\nmv \"${tmpdir}/docs/html\" \"${outdir}\"\n"
  }
]